69800 lines
3.1 MiB

"use strict";
var __create = Object.create;
var __defProp = Object.defineProperty;
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
var __getOwnPropNames = Object.getOwnPropertyNames;
var __getProtoOf = Object.getPrototypeOf;
var __hasOwnProp = Object.prototype.hasOwnProperty;
var __esm = (fn, res) => function __init() {
return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
};
var __commonJS = (cb, mod) => function __require() {
return mod || (0, cb[__getOwnPropNames(cb)[0]])((mod = { exports: {} }).exports, mod), mod.exports;
};
var __export = (target, all) => {
for (var name in all)
__defProp(target, name, { get: all[name], enumerable: true });
};
var __copyProps = (to, from, except, desc) => {
if (from && typeof from === "object" || typeof from === "function") {
for (let key of __getOwnPropNames(from))
if (!__hasOwnProp.call(to, key) && key !== except)
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
}
return to;
};
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
// If the importer is in node compatibility mode or this is not an ESM
// file that has been converted to a CommonJS file using a Babel-
// compatible transform (i.e. "__esModule" has not been set), then set
// "default" to the CommonJS "module.exports" for node compatibility.
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
mod
));
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
// packages/isomorphic/ariaSnapshot.ts
function ariaNodesEqual(a, b) {
if (a.role !== b.role || a.name !== b.name)
return false;
if (!ariaPropsEqual(a, b) || hasPointerCursor(a) !== hasPointerCursor(b))
return false;
const aKeys = Object.keys(a.props);
const bKeys = Object.keys(b.props);
return aKeys.length === bKeys.length && aKeys.every((k) => a.props[k] === b.props[k]);
}
function hasPointerCursor(ariaNode) {
return ariaNode.box.cursor === "pointer";
}
function ariaPropsEqual(a, b) {
return a.active === b.active && a.checked === b.checked && a.disabled === b.disabled && a.expanded === b.expanded && a.selected === b.selected && a.level === b.level && a.pressed === b.pressed;
}
function parseAriaSnapshotUnsafe(yaml3, text2, options2 = {}) {
const result2 = parseAriaSnapshot(yaml3, text2, options2);
if (result2.errors.length)
throw new Error(result2.errors[0].message);
return result2.fragment;
}
function parseAriaSnapshot(yaml3, text2, options2 = {}) {
const lineCounter = new yaml3.LineCounter();
const parseOptions = {
keepSourceTokens: true,
lineCounter,
...options2
};
const yamlDoc = yaml3.parseDocument(text2, parseOptions);
const errors = [];
const convertRange = (range) => {
return [lineCounter.linePos(range[0]), lineCounter.linePos(range[1])];
};
const addError = (error) => {
errors.push({
message: error.message,
range: [lineCounter.linePos(error.pos[0]), lineCounter.linePos(error.pos[1])]
});
};
const convertSeq = (container, seq) => {
for (const item of seq.items) {
const itemIsString = item instanceof yaml3.Scalar && typeof item.value === "string";
if (itemIsString) {
const childNode = KeyParser.parse(item, parseOptions, errors);
if (childNode) {
container.children = container.children || [];
container.children.push(childNode);
}
continue;
}
const itemIsMap = item instanceof yaml3.YAMLMap;
if (itemIsMap) {
convertMap(container, item);
continue;
}
errors.push({
message: "Sequence items should be strings or maps",
range: convertRange(item.range || seq.range)
});
}
};
const convertMap = (container, map) => {
for (const entry of map.items) {
container.children = container.children || [];
const keyIsString = entry.key instanceof yaml3.Scalar && typeof entry.key.value === "string";
if (!keyIsString) {
errors.push({
message: "Only string keys are supported",
range: convertRange(entry.key.range || map.range)
});
continue;
}
const key = entry.key;
const value2 = entry.value;
if (key.value === "text") {
const valueIsString = value2 instanceof yaml3.Scalar && typeof value2.value === "string";
if (!valueIsString) {
errors.push({
message: "Text value should be a string",
range: convertRange(entry.value.range || map.range)
});
continue;
}
container.children.push({
kind: "text",
text: textValue(value2.value)
});
continue;
}
if (key.value === "/children") {
const valueIsString = value2 instanceof yaml3.Scalar && typeof value2.value === "string";
if (!valueIsString || value2.value !== "contain" && value2.value !== "equal" && value2.value !== "deep-equal") {
errors.push({
message: 'Strict value should be "contain", "equal" or "deep-equal"',
range: convertRange(entry.value.range || map.range)
});
continue;
}
container.containerMode = value2.value;
continue;
}
if (key.value.startsWith("/")) {
const valueIsString = value2 instanceof yaml3.Scalar && typeof value2.value === "string";
if (!valueIsString) {
errors.push({
message: "Property value should be a string",
range: convertRange(entry.value.range || map.range)
});
continue;
}
container.props = container.props ?? {};
container.props[key.value.slice(1)] = textValue(value2.value);
continue;
}
const childNode = KeyParser.parse(key, parseOptions, errors);
if (!childNode)
continue;
const valueIsScalar = value2 instanceof yaml3.Scalar;
if (valueIsScalar) {
const type3 = typeof value2.value;
if (type3 !== "string" && type3 !== "number" && type3 !== "boolean") {
errors.push({
message: "Node value should be a string or a sequence",
range: convertRange(entry.value.range || map.range)
});
continue;
}
container.children.push({
...childNode,
children: [{
kind: "text",
text: textValue(String(value2.value))
}]
});
continue;
}
const valueIsSequence = value2 instanceof yaml3.YAMLSeq;
if (valueIsSequence) {
container.children.push(childNode);
convertSeq(childNode, value2);
continue;
}
errors.push({
message: "Map values should be strings or sequences",
range: convertRange(entry.value.range || map.range)
});
}
};
const fragment = { kind: "role", role: "fragment" };
yamlDoc.errors.forEach(addError);
if (errors.length)
return { errors, fragment };
if (!(yamlDoc.contents instanceof yaml3.YAMLSeq)) {
errors.push({
message: 'Aria snapshot must be a YAML sequence, elements starting with " -"',
range: yamlDoc.contents ? convertRange(yamlDoc.contents.range) : [{ line: 0, col: 0 }, { line: 0, col: 0 }]
});
}
if (errors.length)
return { errors, fragment };
convertSeq(fragment, yamlDoc.contents);
if (errors.length)
return { errors, fragment: emptyFragment };
if (fragment.children?.length === 1 && (!fragment.containerMode || fragment.containerMode === "contain"))
return { fragment: fragment.children[0], errors: [] };
return { fragment, errors: [] };
}
function normalizeWhitespace(text2) {
return text2.replace(/[\u200b\u00ad]/g, "").replace(/[\r\n\s\t]+/g, " ").trim();
}
function textValue(value2) {
return {
raw: value2,
normalized: normalizeWhitespace(value2)
};
}
function findNewNode(from, to) {
function fillMap(root, map, position) {
let size = 1;
let childPosition = position + size;
for (const child of root.children || []) {
if (typeof child === "string") {
size++;
childPosition++;
} else {
size += fillMap(child, map, childPosition);
childPosition += size;
}
}
if (!["none", "presentation", "fragment", "iframe", "generic"].includes(root.role) && root.name) {
let byRole = map.get(root.role);
if (!byRole) {
byRole = /* @__PURE__ */ new Map();
map.set(root.role, byRole);
}
const existing = byRole.get(root.name);
const sizeAndPosition = size * 100 - position;
if (!existing || existing.sizeAndPosition < sizeAndPosition)
byRole.set(root.name, { node: root, sizeAndPosition });
}
return size;
}
const fromMap = /* @__PURE__ */ new Map();
if (from)
fillMap(from, fromMap, 0);
const toMap = /* @__PURE__ */ new Map();
fillMap(to, toMap, 0);
const result2 = [];
for (const [role, byRole] of toMap) {
for (const [name, byName] of byRole) {
const inFrom = fromMap.get(role)?.get(name);
if (!inFrom)
result2.push(byName);
}
}
result2.sort((a, b) => b.sizeAndPosition - a.sizeAndPosition);
return result2[0]?.node;
}
var emptyFragment, KeyParser, ParserError;
var init_ariaSnapshot = __esm({
"packages/isomorphic/ariaSnapshot.ts"() {
"use strict";
emptyFragment = { kind: "role", role: "fragment" };
KeyParser = class _KeyParser {
static parse(text2, options2, errors) {
try {
return new _KeyParser(text2.value)._parse();
} catch (e) {
if (e instanceof ParserError) {
const message = options2.prettyErrors === false ? e.message : e.message + ":\n\n" + text2.value + "\n" + " ".repeat(e.pos) + "^\n";
errors.push({
message,
range: [options2.lineCounter.linePos(text2.range[0]), options2.lineCounter.linePos(text2.range[0] + e.pos)]
});
return null;
}
throw e;
}
}
constructor(input) {
this._input = input;
this._pos = 0;
this._length = input.length;
}
_peek() {
return this._input[this._pos] || "";
}
_next() {
if (this._pos < this._length)
return this._input[this._pos++];
return null;
}
_eof() {
return this._pos >= this._length;
}
_isWhitespace() {
return !this._eof() && /\s/.test(this._peek());
}
_skipWhitespace() {
while (this._isWhitespace())
this._pos++;
}
_readIdentifier(type3) {
if (this._eof())
this._throwError(`Unexpected end of input when expecting ${type3}`);
const start3 = this._pos;
while (!this._eof() && /[a-zA-Z]/.test(this._peek()))
this._pos++;
return this._input.slice(start3, this._pos);
}
_readString() {
let result2 = "";
let escaped2 = false;
while (!this._eof()) {
const ch = this._next();
if (escaped2) {
result2 += ch;
escaped2 = false;
} else if (ch === "\\") {
escaped2 = true;
} else if (ch === '"') {
return result2;
} else {
result2 += ch;
}
}
this._throwError("Unterminated string");
}
_throwError(message, offset = 0) {
throw new ParserError(message, offset || this._pos);
}
_readRegex() {
let result2 = "";
let escaped2 = false;
let insideClass = false;
while (!this._eof()) {
const ch = this._next();
if (escaped2) {
result2 += ch;
escaped2 = false;
} else if (ch === "\\") {
escaped2 = true;
result2 += ch;
} else if (ch === "/" && !insideClass) {
return { pattern: result2 };
} else if (ch === "[") {
insideClass = true;
result2 += ch;
} else if (ch === "]" && insideClass) {
result2 += ch;
insideClass = false;
} else {
result2 += ch;
}
}
this._throwError("Unterminated regex");
}
_readStringOrRegex() {
const ch = this._peek();
if (ch === '"') {
this._next();
return normalizeWhitespace(this._readString());
}
if (ch === "/") {
this._next();
return this._readRegex();
}
return null;
}
_readAttributes(result2) {
let errorPos = this._pos;
while (true) {
this._skipWhitespace();
if (this._peek() === "[") {
this._next();
this._skipWhitespace();
errorPos = this._pos;
const flagName = this._readIdentifier("attribute");
this._skipWhitespace();
let flagValue = "";
if (this._peek() === "=") {
this._next();
this._skipWhitespace();
errorPos = this._pos;
while (this._peek() !== "]" && !this._isWhitespace() && !this._eof())
flagValue += this._next();
}
this._skipWhitespace();
if (this._peek() !== "]")
this._throwError("Expected ]");
this._next();
this._applyAttribute(result2, flagName, flagValue || "true", errorPos);
} else {
break;
}
}
}
_parse() {
this._skipWhitespace();
const role = this._readIdentifier("role");
this._skipWhitespace();
const name = this._readStringOrRegex() || "";
const result2 = { kind: "role", role, name };
this._readAttributes(result2);
this._skipWhitespace();
if (!this._eof())
this._throwError("Unexpected input");
return result2;
}
_applyAttribute(node, key, value2, errorPos) {
if (key === "checked") {
this._assert(value2 === "true" || value2 === "false" || value2 === "mixed", 'Value of "checked" attribute must be a boolean or "mixed"', errorPos);
node.checked = value2 === "true" ? true : value2 === "false" ? false : "mixed";
return;
}
if (key === "disabled") {
this._assert(value2 === "true" || value2 === "false", 'Value of "disabled" attribute must be a boolean', errorPos);
node.disabled = value2 === "true";
return;
}
if (key === "expanded") {
this._assert(value2 === "true" || value2 === "false", 'Value of "expanded" attribute must be a boolean', errorPos);
node.expanded = value2 === "true";
return;
}
if (key === "active") {
this._assert(value2 === "true" || value2 === "false", 'Value of "active" attribute must be a boolean', errorPos);
node.active = value2 === "true";
return;
}
if (key === "level") {
this._assert(!isNaN(Number(value2)), 'Value of "level" attribute must be a number', errorPos);
node.level = Number(value2);
return;
}
if (key === "pressed") {
this._assert(value2 === "true" || value2 === "false" || value2 === "mixed", 'Value of "pressed" attribute must be a boolean or "mixed"', errorPos);
node.pressed = value2 === "true" ? true : value2 === "false" ? false : "mixed";
return;
}
if (key === "selected") {
this._assert(value2 === "true" || value2 === "false", 'Value of "selected" attribute must be a boolean', errorPos);
node.selected = value2 === "true";
return;
}
this._assert(false, `Unsupported attribute [${key}]`, errorPos);
}
_assert(value2, message, valuePos) {
if (!value2)
this._throwError(message || "Assertion error", valuePos);
}
};
ParserError = class extends Error {
constructor(message, pos) {
super(message);
this.pos = pos;
}
};
}
});
// packages/isomorphic/stringUtils.ts
function escapeWithQuotes(text2, char = "'") {
const stringified = JSON.stringify(text2);
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 escapeTemplateString(text2) {
return text2.replace(/\\/g, "\\\\").replace(/`/g, "\\`").replace(/\$\{/g, "\\${");
}
function isString(obj) {
return typeof obj === "string" || obj instanceof String;
}
function toTitleCase(name) {
return name.charAt(0).toUpperCase() + name.substring(1);
}
function toSnakeCase(name) {
return name.replace(/([a-z0-9])([A-Z])/g, "$1_$2").replace(/([A-Z])([A-Z][a-z])/g, "$1_$2").toLowerCase();
}
function formatObject(value2, indent = " ", mode = "multiline") {
if (typeof value2 === "string")
return escapeWithQuotes(value2, "'");
if (Array.isArray(value2))
return `[${value2.map((o) => formatObject(o)).join(", ")}]`;
if (typeof value2 === "object") {
const keys = Object.keys(value2).filter((key) => key !== "timeout" && value2[key] !== void 0).sort();
if (!keys.length)
return "{}";
const tokens = [];
for (const key of keys)
tokens.push(`${key}: ${formatObject(value2[key])}`);
if (mode === "multiline")
return `{
${tokens.map((t) => indent + t).join(`,
`)}
}`;
return `{ ${tokens.join(", ")} }`;
}
return String(value2);
}
function formatObjectOrVoid(value2, indent = " ") {
const result2 = formatObject(value2, indent);
return result2 === "{}" ? "" : result2;
}
function quoteCSSAttributeValue(text2) {
return `"${text2.replace(/["\\]/g, (char) => "\\" + char)}"`;
}
function cacheNormalizedWhitespaces() {
normalizedWhitespaceCache = /* @__PURE__ */ new Map();
}
function normalizeWhiteSpace(text2) {
let result2 = normalizedWhitespaceCache?.get(text2);
if (result2 === void 0) {
result2 = text2.replace(/[\u200b\u00ad]/g, "").trim().replace(/\s+/g, " ");
normalizedWhitespaceCache?.set(text2, result2);
}
return result2;
}
function normalizeEscapedRegexQuotes(source8) {
return source8.replace(/(^|[^\\])(\\\\)*\\(['"`])/g, "$1$2$3");
}
function escapeRegexForSelector(re2) {
if (re2.unicode || re2.unicodeSets)
return String(re2);
return String(re2).replace(/(^|[^\\])(\\\\)*(["'`])/g, "$1$2\\$3").replace(/>>/g, "\\>\\>");
}
function escapeForTextSelector(text2, exact) {
if (typeof text2 !== "string")
return escapeRegexForSelector(text2);
return `${JSON.stringify(text2)}${exact ? "s" : "i"}`;
}
function escapeForAttributeSelector(value2, exact) {
if (typeof value2 !== "string")
return escapeRegexForSelector(value2);
return `"${value2.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, "\u2026");
}
function escapeRegExp(s) {
return s.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
}
function escapeHTMLAttribute(s) {
return s.replace(/[&<>"']/ug, (char) => escaped[char]);
}
function escapeHTML(s) {
return s.replace(/[&<]/ug, (char) => escaped[char]);
}
function longestCommonSubstring(s1, s2) {
const n = s1.length;
const m = s2.length;
let maxLen = 0;
let endingIndex = 0;
const dp = Array(n + 1).fill(null).map(() => Array(m + 1).fill(0));
for (let i = 1; i <= n; i++) {
for (let j = 1; j <= m; j++) {
if (s1[i - 1] === s2[j - 1]) {
dp[i][j] = dp[i - 1][j - 1] + 1;
if (dp[i][j] > maxLen) {
maxLen = dp[i][j];
endingIndex = i;
}
}
}
}
return s1.slice(endingIndex - maxLen, endingIndex);
}
function parseRegex(regex) {
if (regex[0] !== "/")
throw new Error(`Invalid regex, must start with '/': ${regex}`);
const lastSlash = regex.lastIndexOf("/");
if (lastSlash <= 0)
throw new Error(`Invalid regex, must end with '/' followed by optional flags: ${regex}`);
const source8 = regex.slice(1, lastSlash);
const flags = regex.slice(lastSlash + 1);
return new RegExp(source8, flags);
}
function stripAnsiEscapes(str) {
return str.replace(ansiRegex, "");
}
var normalizedWhitespaceCache, escaped, ansiRegex;
var init_stringUtils = __esm({
"packages/isomorphic/stringUtils.ts"() {
"use strict";
escaped = { "&": "&amp;", "<": "&lt;", ">": "&gt;", '"': "&quot;", "'": "&#39;" };
ansiRegex = new RegExp("([\\u001B\\u009B][[\\]()#;?]*(?:(?:(?:[a-zA-Z\\d]*(?:;[-a-zA-Z\\d\\/#&.:=?%@~_]*)*)?\\u0007)|(?:(?:\\d{1,4}(?:;\\d{0,4})*)?[\\dA-PR-TZcf-ntqry=><~])))", "g");
}
});
// packages/isomorphic/rtti.ts
function isRegExp(obj) {
return obj instanceof RegExp || Object.prototype.toString.call(obj) === "[object RegExp]";
}
function isObject(obj) {
return typeof obj === "object" && obj !== null;
}
function isError(obj) {
return obj instanceof Error || obj && Object.getPrototypeOf(obj)?.name === "Error";
}
var init_rtti = __esm({
"packages/isomorphic/rtti.ts"() {
"use strict";
init_stringUtils();
}
});
// packages/isomorphic/expectUtils.ts
function serializeExpectedTextValues(items, options2 = {}) {
return items.map((i) => ({
string: isString(i) ? i : void 0,
regexSource: isRegExp(i) ? i.source : void 0,
regexFlags: isRegExp(i) ? i.flags : void 0,
matchSubstring: options2.matchSubstring,
ignoreCase: options2.ignoreCase,
normalizeWhiteSpace: options2.normalizeWhiteSpace
}));
}
var init_expectUtils = __esm({
"packages/isomorphic/expectUtils.ts"() {
"use strict";
init_rtti();
}
});
// packages/isomorphic/assert.ts
function assert(value2, message) {
if (!value2)
throw new Error(message || "Assertion error");
}
var init_assert = __esm({
"packages/isomorphic/assert.ts"() {
"use strict";
}
});
// packages/isomorphic/colors.ts
var webColors, noColors, applyStyle;
var init_colors = __esm({
"packages/isomorphic/colors.ts"() {
"use strict";
webColors = {
enabled: true,
reset: (text2) => applyStyle(0, 0, text2),
bold: (text2) => applyStyle(1, 22, text2),
dim: (text2) => applyStyle(2, 22, text2),
italic: (text2) => applyStyle(3, 23, text2),
underline: (text2) => applyStyle(4, 24, text2),
inverse: (text2) => applyStyle(7, 27, text2),
hidden: (text2) => applyStyle(8, 28, text2),
strikethrough: (text2) => applyStyle(9, 29, text2),
black: (text2) => applyStyle(30, 39, text2),
red: (text2) => applyStyle(31, 39, text2),
green: (text2) => applyStyle(32, 39, text2),
yellow: (text2) => applyStyle(33, 39, text2),
blue: (text2) => applyStyle(34, 39, text2),
magenta: (text2) => applyStyle(35, 39, text2),
cyan: (text2) => applyStyle(36, 39, text2),
white: (text2) => applyStyle(37, 39, text2),
gray: (text2) => applyStyle(90, 39, text2),
grey: (text2) => applyStyle(90, 39, text2)
};
noColors = {
enabled: false,
reset: (t) => t,
bold: (t) => t,
dim: (t) => t,
italic: (t) => t,
underline: (t) => t,
inverse: (t) => t,
hidden: (t) => t,
strikethrough: (t) => t,
black: (t) => t,
red: (t) => t,
green: (t) => t,
yellow: (t) => t,
blue: (t) => t,
magenta: (t) => t,
cyan: (t) => t,
white: (t) => t,
gray: (t) => t,
grey: (t) => t
};
applyStyle = (open7, close3, text2) => `\x1B[${open7}m${text2}\x1B[${close3}m`;
}
});
// packages/isomorphic/headers.ts
function headersObjectToArray(headers, separator, setCookieSeparator) {
if (!setCookieSeparator)
setCookieSeparator = separator;
const result2 = [];
for (const name in headers) {
const values = headers[name];
if (values === void 0)
continue;
if (separator) {
const sep = name.toLowerCase() === "set-cookie" ? setCookieSeparator : separator;
for (const value2 of values.split(sep))
result2.push({ name, value: value2.trim() });
} else {
result2.push({ name, value: values });
}
}
return result2;
}
function headersArrayToObject(headers, lowerCase) {
const result2 = {};
for (const { name, value: value2 } of headers)
result2[lowerCase ? name.toLowerCase() : name] = value2;
return result2;
}
var init_headers = __esm({
"packages/isomorphic/headers.ts"() {
"use strict";
}
});
// packages/isomorphic/imageUtils.ts
function padImageToSize(image, size) {
if (image.width === size.width && image.height === size.height)
return image;
const buffer = new Uint8Array(size.width * size.height * 4);
for (let y = 0; y < size.height; y++) {
for (let x = 0; x < size.width; x++) {
const to = (y * size.width + x) * 4;
if (y < image.height && x < image.width) {
const from = (y * image.width + x) * 4;
buffer[to] = image.data[from];
buffer[to + 1] = image.data[from + 1];
buffer[to + 2] = image.data[from + 2];
buffer[to + 3] = image.data[from + 3];
} else {
buffer[to] = 0;
buffer[to + 1] = 0;
buffer[to + 2] = 0;
buffer[to + 3] = 0;
}
}
}
return { data: Buffer.from(buffer), width: size.width, height: size.height };
}
function scaleImageToSize(image, size) {
const { data: src, width: w1, height: h1 } = image;
const w2 = Math.max(1, Math.floor(size.width));
const h2 = Math.max(1, Math.floor(size.height));
if (w1 === w2 && h1 === h2)
return image;
if (w1 <= 0 || h1 <= 0)
throw new Error("Invalid input image");
if (size.width <= 0 || size.height <= 0 || !isFinite(size.width) || !isFinite(size.height))
throw new Error("Invalid output dimensions");
const clamp = (v, lo, hi) => v < lo ? lo : v > hi ? hi : v;
const weights = (t, o) => {
const t2 = t * t;
const t3 = t2 * t;
o[0] = -0.5 * t + 1 * t2 - 0.5 * t3;
o[1] = 1 - 2.5 * t2 + 1.5 * t3;
o[2] = 0.5 * t + 2 * t2 - 1.5 * t3;
o[3] = -0.5 * t2 + 0.5 * t3;
};
const srcRowStride = w1 * 4;
const dstRowStride = w2 * 4;
const xOff = new Int32Array(w2 * 4);
const xW = new Float32Array(w2 * 4);
const wx = new Float32Array(4);
const xScale = w1 / w2;
for (let x = 0; x < w2; x++) {
const sx = (x + 0.5) * xScale - 0.5;
const sxi = Math.floor(sx);
const t = sx - sxi;
weights(t, wx);
const b = x * 4;
const i0 = clamp(sxi - 1, 0, w1 - 1);
const i1 = clamp(sxi + 0, 0, w1 - 1);
const i2 = clamp(sxi + 1, 0, w1 - 1);
const i3 = clamp(sxi + 2, 0, w1 - 1);
xOff[b + 0] = i0 << 2;
xOff[b + 1] = i1 << 2;
xOff[b + 2] = i2 << 2;
xOff[b + 3] = i3 << 2;
xW[b + 0] = wx[0];
xW[b + 1] = wx[1];
xW[b + 2] = wx[2];
xW[b + 3] = wx[3];
}
const yRow = new Int32Array(h2 * 4);
const yW = new Float32Array(h2 * 4);
const wy = new Float32Array(4);
const yScale = h1 / h2;
for (let y = 0; y < h2; y++) {
const sy = (y + 0.5) * yScale - 0.5;
const syi = Math.floor(sy);
const t = sy - syi;
weights(t, wy);
const b = y * 4;
const j0 = clamp(syi - 1, 0, h1 - 1);
const j1 = clamp(syi + 0, 0, h1 - 1);
const j2 = clamp(syi + 1, 0, h1 - 1);
const j3 = clamp(syi + 2, 0, h1 - 1);
yRow[b + 0] = j0 * srcRowStride;
yRow[b + 1] = j1 * srcRowStride;
yRow[b + 2] = j2 * srcRowStride;
yRow[b + 3] = j3 * srcRowStride;
yW[b + 0] = wy[0];
yW[b + 1] = wy[1];
yW[b + 2] = wy[2];
yW[b + 3] = wy[3];
}
const dst = new Uint8Array(w2 * h2 * 4);
for (let y = 0; y < h2; y++) {
const yb = y * 4;
const rb0 = yRow[yb + 0];
const rb1 = yRow[yb + 1];
const rb2 = yRow[yb + 2];
const rb3 = yRow[yb + 3];
const wy0 = yW[yb + 0];
const wy1 = yW[yb + 1];
const wy2 = yW[yb + 2];
const wy3 = yW[yb + 3];
const dstBase = y * dstRowStride;
for (let x = 0; x < w2; x++) {
const xb = x * 4;
const xo0 = xOff[xb + 0];
const xo1 = xOff[xb + 1];
const xo2 = xOff[xb + 2];
const xo3 = xOff[xb + 3];
const wx0 = xW[xb + 0];
const wx1 = xW[xb + 1];
const wx2 = xW[xb + 2];
const wx3 = xW[xb + 3];
const di = dstBase + (x << 2);
for (let c = 0; c < 4; c++) {
const r0 = src[rb0 + xo0 + c] * wx0 + src[rb0 + xo1 + c] * wx1 + src[rb0 + xo2 + c] * wx2 + src[rb0 + xo3 + c] * wx3;
const r1 = src[rb1 + xo0 + c] * wx0 + src[rb1 + xo1 + c] * wx1 + src[rb1 + xo2 + c] * wx2 + src[rb1 + xo3 + c] * wx3;
const r2 = src[rb2 + xo0 + c] * wx0 + src[rb2 + xo1 + c] * wx1 + src[rb2 + xo2 + c] * wx2 + src[rb2 + xo3 + c] * wx3;
const r3 = src[rb3 + xo0 + c] * wx0 + src[rb3 + xo1 + c] * wx1 + src[rb3 + xo2 + c] * wx2 + src[rb3 + xo3 + c] * wx3;
const v = r0 * wy0 + r1 * wy1 + r2 * wy2 + r3 * wy3;
dst[di + c] = v < 0 ? 0 : v > 255 ? 255 : v | 0;
}
}
}
return { data: Buffer.from(dst.buffer), width: w2, height: h2 };
}
var init_imageUtils = __esm({
"packages/isomorphic/imageUtils.ts"() {
"use strict";
}
});
// packages/isomorphic/jsonSchema.ts
function validate(value2, schema, path59) {
const errors = [];
if (schema.oneOf) {
let bestErrors;
for (const variant of schema.oneOf) {
const variantErrors = validate(value2, variant, path59);
if (variantErrors.length === 0)
return [];
if (!bestErrors || variantErrors.length < bestErrors.length)
bestErrors = variantErrors;
}
if (bestErrors.length === 1 && bestErrors[0].startsWith(`${path59}: expected `))
return [`${path59}: does not match any of the expected types`];
return bestErrors;
}
if (schema.type === "string") {
if (typeof value2 !== "string") {
errors.push(`${path59}: expected string, got ${typeof value2}`);
return errors;
}
if (schema.pattern && !cachedRegex(schema.pattern).test(value2))
errors.push(schema.patternError || `${path59}: must match pattern "${schema.pattern}"`);
return errors;
}
if (schema.type === "array") {
if (!Array.isArray(value2)) {
errors.push(`${path59}: expected array, got ${typeof value2}`);
return errors;
}
if (schema.items) {
for (let i = 0; i < value2.length; i++)
errors.push(...validate(value2[i], schema.items, `${path59}[${i}]`));
}
return errors;
}
if (schema.type === "object") {
if (!value2 || typeof value2 !== "object" || Array.isArray(value2)) {
errors.push(`${path59}: expected object, got ${Array.isArray(value2) ? "array" : typeof value2}`);
return errors;
}
const obj = value2;
for (const key of schema.required || []) {
if (obj[key] === void 0)
errors.push(`${path59}.${key}: required`);
}
for (const [key, propSchema] of Object.entries(schema.properties || {})) {
if (obj[key] !== void 0)
errors.push(...validate(obj[key], propSchema, `${path59}.${key}`));
}
return errors;
}
return errors;
}
function cachedRegex(pattern) {
let regex = regexCache.get(pattern);
if (!regex) {
regex = new RegExp(pattern);
regexCache.set(pattern, regex);
}
return regex;
}
var regexCache;
var init_jsonSchema = __esm({
"packages/isomorphic/jsonSchema.ts"() {
"use strict";
regexCache = /* @__PURE__ */ new Map();
}
});
// packages/isomorphic/cssTokenizer.ts
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;
}
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 = Math.pow(2, 16) + lead * Math.pow(2, 10) + trail;
i++;
}
codepoints.push(code);
}
return codepoints;
}
function stringFromCode(code) {
if (code <= 65535)
return String.fromCharCode(code);
code -= Math.pow(2, 16);
const lead = Math.floor(code / Math.pow(2, 10)) + 55296;
const trail = code % Math.pow(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(i2) {
if (i2 >= str.length)
return -1;
return str[i2];
};
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(codepoint2) {
if (codepoint2 === void 0)
codepoint2 = code;
return codepoint2 === -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 str2 = consumeAName();
if (str2.toLowerCase() === "url" && next() === 40) {
consume();
while (whitespace(next(1)) && whitespace(next(2)))
consume();
if (next() === 34 || next() === 39)
return new FunctionToken(str2);
else if (whitespace(next()) && (next(2) === 34 || next(2) === 39))
return new FunctionToken(str2);
else
return consumeAURLToken();
} else if (next() === 40) {
consume();
return new FunctionToken(str2);
} else {
return new IdentToken(str2);
}
};
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 value2 = parseInt(digits.map(function(x) {
return String.fromCharCode(x);
}).join(""), 16);
if (value2 > maximumallowedcodepoint)
value2 = 65533;
return value2;
} 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 result2 = "";
while (consume()) {
if (namechar(code)) {
result2 += stringFromCode(code);
} else if (startsWithAValidEscape()) {
result2 += stringFromCode(consumeEscape());
} else {
reconsume();
return result2;
}
}
throw new Error("Internal parse error");
};
const consumeANumber = function() {
let repr = "";
let type3 = "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);
type3 = "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);
type3 = "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);
type3 = "number";
while (digit(next())) {
consume();
repr += stringFromCode(code);
}
}
const value2 = convertAStringToANumber(repr);
return { type: type3, value: value2, 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;
}
function escapeIdent(string) {
string = "" + string;
let result2 = "";
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)
result2 += "\\" + code.toString(16) + " ";
else if (code >= 128 || code === 45 || code === 95 || between(code, 48, 57) || between(code, 65, 90) || between(code, 97, 122))
result2 += string[i];
else
result2 += "\\" + string[i];
}
return result2;
}
function escapeHash(string) {
string = "" + string;
let result2 = "";
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))
result2 += string[i];
else
result2 += "\\" + code.toString(16) + " ";
}
return result2;
}
function escapeString(string) {
string = "" + string;
let result2 = "";
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)
result2 += "\\" + code.toString(16) + " ";
else if (code === 34 || code === 92)
result2 += "\\" + string[i];
else
result2 += string[i];
}
return result2;
}
var between, maximumallowedcodepoint, InvalidCharacterError, CSSParserToken, BadStringToken, BadURLToken, WhitespaceToken, CDOToken, CDCToken, ColonToken, SemicolonToken, CommaToken, GroupingToken, OpenCurlyToken, CloseCurlyToken, OpenSquareToken, CloseSquareToken, OpenParenToken, CloseParenToken, IncludeMatchToken, DashMatchToken, PrefixMatchToken, SuffixMatchToken, SubstringMatchToken, ColumnToken, EOFToken, DelimToken, StringValuedToken, IdentToken, FunctionToken, AtKeywordToken, HashToken, StringToken, URLToken, NumberToken, PercentageToken, DimensionToken;
var init_cssTokenizer = __esm({
"packages/isomorphic/cssTokenizer.ts"() {
"use strict";
between = function(num, first, last) {
return num >= first && num <= last;
};
maximumallowedcodepoint = 1114111;
InvalidCharacterError = class extends Error {
constructor(message) {
super(message);
this.name = "InvalidCharacterError";
}
};
CSSParserToken = class {
constructor() {
this.tokenType = "";
}
toJSON() {
return { token: this.tokenType };
}
toString() {
return this.tokenType;
}
toSource() {
return "" + this;
}
};
BadStringToken = class extends CSSParserToken {
constructor() {
super(...arguments);
this.tokenType = "BADSTRING";
}
};
BadURLToken = class extends CSSParserToken {
constructor() {
super(...arguments);
this.tokenType = "BADURL";
}
};
WhitespaceToken = class extends CSSParserToken {
constructor() {
super(...arguments);
this.tokenType = "WHITESPACE";
}
toString() {
return "WS";
}
toSource() {
return " ";
}
};
CDOToken = class extends CSSParserToken {
constructor() {
super(...arguments);
this.tokenType = "CDO";
}
toSource() {
return "<!--";
}
};
CDCToken = class extends CSSParserToken {
constructor() {
super(...arguments);
this.tokenType = "CDC";
}
toSource() {
return "-->";
}
};
ColonToken = class extends CSSParserToken {
constructor() {
super(...arguments);
this.tokenType = ":";
}
};
SemicolonToken = class extends CSSParserToken {
constructor() {
super(...arguments);
this.tokenType = ";";
}
};
CommaToken = class extends CSSParserToken {
constructor() {
super(...arguments);
this.tokenType = ",";
}
};
GroupingToken = class extends CSSParserToken {
constructor() {
super(...arguments);
this.value = "";
this.mirror = "";
}
};
OpenCurlyToken = class extends GroupingToken {
constructor() {
super();
this.tokenType = "{";
this.value = "{";
this.mirror = "}";
}
};
CloseCurlyToken = class extends GroupingToken {
constructor() {
super();
this.tokenType = "}";
this.value = "}";
this.mirror = "{";
}
};
OpenSquareToken = class extends GroupingToken {
constructor() {
super();
this.tokenType = "[";
this.value = "[";
this.mirror = "]";
}
};
CloseSquareToken = class extends GroupingToken {
constructor() {
super();
this.tokenType = "]";
this.value = "]";
this.mirror = "[";
}
};
OpenParenToken = class extends GroupingToken {
constructor() {
super();
this.tokenType = "(";
this.value = "(";
this.mirror = ")";
}
};
CloseParenToken = class extends GroupingToken {
constructor() {
super();
this.tokenType = ")";
this.value = ")";
this.mirror = "(";
}
};
IncludeMatchToken = class extends CSSParserToken {
constructor() {
super(...arguments);
this.tokenType = "~=";
}
};
DashMatchToken = class extends CSSParserToken {
constructor() {
super(...arguments);
this.tokenType = "|=";
}
};
PrefixMatchToken = class extends CSSParserToken {
constructor() {
super(...arguments);
this.tokenType = "^=";
}
};
SuffixMatchToken = class extends CSSParserToken {
constructor() {
super(...arguments);
this.tokenType = "$=";
}
};
SubstringMatchToken = class extends CSSParserToken {
constructor() {
super(...arguments);
this.tokenType = "*=";
}
};
ColumnToken = class extends CSSParserToken {
constructor() {
super(...arguments);
this.tokenType = "||";
}
};
EOFToken = class extends CSSParserToken {
constructor() {
super(...arguments);
this.tokenType = "EOF";
}
toSource() {
return "";
}
};
DelimToken = class extends CSSParserToken {
constructor(code) {
super();
this.tokenType = "DELIM";
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;
}
};
StringValuedToken = class extends CSSParserToken {
constructor() {
super(...arguments);
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;
}
};
IdentToken = class extends StringValuedToken {
constructor(val) {
super();
this.tokenType = "IDENT";
this.value = val;
}
toString() {
return "IDENT(" + this.value + ")";
}
toSource() {
return escapeIdent(this.value);
}
};
FunctionToken = class extends StringValuedToken {
constructor(val) {
super();
this.tokenType = "FUNCTION";
this.value = val;
this.mirror = ")";
}
toString() {
return "FUNCTION(" + this.value + ")";
}
toSource() {
return escapeIdent(this.value) + "(";
}
};
AtKeywordToken = class extends StringValuedToken {
constructor(val) {
super();
this.tokenType = "AT-KEYWORD";
this.value = val;
}
toString() {
return "AT(" + this.value + ")";
}
toSource() {
return "@" + escapeIdent(this.value);
}
};
HashToken = class extends StringValuedToken {
constructor(val) {
super();
this.tokenType = "HASH";
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);
}
};
StringToken = class extends StringValuedToken {
constructor(val) {
super();
this.tokenType = "STRING";
this.value = val;
}
toString() {
return '"' + escapeString(this.value) + '"';
}
};
URLToken = class extends StringValuedToken {
constructor(val) {
super();
this.tokenType = "URL";
this.value = val;
}
toString() {
return "URL(" + this.value + ")";
}
toSource() {
return 'url("' + escapeString(this.value) + '")';
}
};
NumberToken = class extends CSSParserToken {
constructor() {
super();
this.tokenType = "NUMBER";
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;
}
};
PercentageToken = class extends CSSParserToken {
constructor() {
super();
this.tokenType = "PERCENTAGE";
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 + "%";
}
};
DimensionToken = class extends CSSParserToken {
constructor() {
super();
this.tokenType = "DIMENSION";
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 source8 = 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 source8 + unit;
}
};
}
});
// packages/isomorphic/cssParser.ts
function isInvalidSelectorError(error) {
return error instanceof InvalidSelectorError;
}
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 css selector "${selector}". Did you mean to CSS.escape it?`;
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 || // TODO: Consider using these for something, e.g. to escape complex strings.
// For example :xpath{ (//div/bar[@attr="foo"])[2]/baz }
// Or this way :xpath( {complex-xpath-goes-here("hello")} )
token instanceof OpenCurlyToken || token instanceof CloseCurlyToken || // TODO: Consider treating these as strings?
token instanceof URLToken || token instanceof PercentageToken;
});
if (unsupportedToken)
throw new InvalidSelectorError(`Unsupported token "${unsupportedToken.toSource()}" while parsing css selector "${selector}". Did you mean to CSS.escape it?`);
let pos = 0;
const names = /* @__PURE__ */ new Set();
function unexpected() {
return new InvalidSelectorError(`Unexpected token "${tokens[pos].toSource()}" while parsing css selector "${selector}". Did you mean to CSS.escape it?`);
}
function skipWhitespace() {
while (tokens[pos] instanceof WhitespaceToken)
pos++;
}
function isIdent(p = pos) {
return tokens[p] instanceof IdentToken;
}
function isString2(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 isFunction2(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 result3 = [consumeArgument()];
while (true) {
skipWhitespace();
if (!isComma())
break;
pos++;
result3.push(consumeArgument());
}
return result3;
}
function consumeArgument() {
skipWhitespace();
if (isNumber())
return tokens[pos++].value;
if (isString2())
return tokens[pos++].value;
return consumeComplexSelector();
}
function consumeComplexSelector() {
const result3 = { simples: [] };
skipWhitespace();
if (isClauseCombinator()) {
result3.simples.push({ selector: { functions: [{ name: "scope", args: [] }] }, combinator: "" });
} else {
result3.simples.push({ selector: consumeSimpleSelector(), combinator: "" });
}
while (true) {
skipWhitespace();
if (isClauseCombinator()) {
result3.simples[result3.simples.length - 1].combinator = tokens[pos++].value;
skipWhitespace();
} else if (isSelectorClauseEnd()) {
break;
}
result3.simples.push({ combinator: "", selector: consumeSimpleSelector() });
}
return result3;
}
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 (isFunction2()) {
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() || isFunction2())
balance++;
if (isCloseParen())
balance--;
if (!balance)
break;
s += tokens[pos++].toSource();
}
return s;
}
const result2 = consumeFunctionArguments();
if (!isEOF())
throw unexpected();
if (result2.some((arg) => typeof arg !== "object" || !("simples" in arg)))
throw new InvalidSelectorError(`Error while parsing css selector "${selector}". Did you mean to CSS.escape it?`);
return { selector: result2, names: Array.from(names) };
}
function serializeSelector(args) {
return args.map((arg) => {
if (typeof arg === "string")
return `"${arg}"`;
if (typeof arg === "number")
return String(arg);
return arg.simples.map(({ selector, combinator }) => {
let s = selector.css || "";
s = s + selector.functions.map((func) => `:${func.name}(${serializeSelector(func.args)})`).join("");
if (combinator)
s += " " + combinator;
return s;
}).join(" ");
}).join(", ");
}
var InvalidSelectorError;
var init_cssParser = __esm({
"packages/isomorphic/cssParser.ts"() {
"use strict";
init_cssTokenizer();
InvalidSelectorError = class extends Error {
};
}
});
// packages/isomorphic/selectorParser.ts
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((part2) => part2.name === "internal:control" && part2.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 splitSelectorByFrame(selectorText) {
const selector = parseSelector(selectorText);
const result2 = [];
let chunk = {
parts: []
};
let chunkStartIndex = 0;
for (let i = 0; i < selector.parts.length; ++i) {
const part = selector.parts[i];
if (part.name === "internal:control" && part.body === "enter-frame") {
if (!chunk.parts.length)
throw new InvalidSelectorError("Selector cannot start with entering frame, select the iframe first");
result2.push(chunk);
chunk = { parts: [] };
chunkStartIndex = i + 1;
continue;
}
if (selector.capture === i)
chunk.capture = i - chunkStartIndex;
chunk.parts.push(part);
}
if (!chunk.parts.length)
throw new InvalidSelectorError(`Selector cannot end with entering frame, while parsing selector ${selectorText}`);
result2.push(chunk);
if (typeof selector.capture === "number" && typeof result2[result2.length - 1].capture !== "number")
throw new InvalidSelectorError(`Can not capture the selector before diving into the frame. Only use * after the last frame has been selected`);
return result2;
}
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 = (selector2, nested) => {
for (const part of selector2.parts) {
visitor(part, nested);
if (kNestedSelectorNames.has(part.name))
visit(part.body.parsed, true);
}
};
visit(selector, false);
}
function parseSelectorString(selector) {
let index = 0;
let quote5;
let start3 = 0;
const result2 = { parts: [] };
const append = () => {
const part = selector.substring(start3, index).trim();
const eqIndex = part.indexOf("=");
let name;
let body;
if (eqIndex !== -1 && part.substring(0, eqIndex).trim().match(/^[a-zA-Z_0-9-+:*]+$/)) {
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);
}
result2.parts.push({ name, body });
if (capture) {
if (result2.capture !== void 0)
throw new InvalidSelectorError(`Only one of the selectors can capture using * modifier`);
result2.capture = result2.parts.length - 1;
}
};
if (!selector.includes(">>")) {
index = selector.length;
append();
return result2;
}
const shouldIgnoreTextSelectorQuote = () => {
const prefix = selector.substring(start3, index);
const match = prefix.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 === quote5) {
quote5 = void 0;
index++;
} else if (!quote5 && (c === '"' || c === "'" || c === "`") && !shouldIgnoreTextSelectorQuote()) {
quote5 = c;
index++;
} else if (!quote5 && c === ">" && selector[index + 1] === ">") {
append();
index += 2;
start3 = index;
} else {
index++;
}
}
append();
return result2;
}
function parseAttributeSelector(selector, allowUnquotedStrings) {
let wp = 0;
let EOL = selector.length === 0;
const next = () => selector[wp] || "";
const eat1 = () => {
const result3 = next();
++wp;
EOL = wp >= selector.length;
return result3;
};
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 >= "\x80" || char >= "0" && char <= "9" || char >= "A" && char <= "Z" || char >= "a" && char <= "z" || char >= "0" && char <= "9" || char === "_" || char === "-";
}
function readIdentifier() {
let result3 = "";
skipSpaces();
while (!EOL && isCSSNameChar(next()))
result3 += eat1();
return result3;
}
function readQuotedString(quote5) {
let result3 = eat1();
if (result3 !== quote5)
syntaxError("parsing quoted string");
while (!EOL && next() !== quote5) {
if (next() === "\\")
eat1();
result3 += eat1();
}
if (next() !== quote5)
syntaxError("parsing quoted string");
result3 += eat1();
return result3;
}
function readRegularExpression() {
if (eat1() !== "/")
syntaxError("parsing regular expression");
let source8 = "";
let inClass = false;
while (!EOL) {
if (next() === "\\") {
source8 += eat1();
if (EOL)
syntaxError("parsing regular expression");
} else if (inClass && next() === "]") {
inClass = false;
} else if (!inClass && next() === "[") {
inClass = true;
} else if (!inClass && next() === "/") {
break;
}
source8 += eat1();
}
if (eat1() !== "/")
syntaxError("parsing regular expression");
let flags = "";
while (!EOL && next().match(/[dgimsuy]/))
flags += eat1();
try {
return new RegExp(source8, 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 value2 = void 0;
let caseSensitive = true;
skipSpaces();
if (next() === "/") {
if (operator !== "=")
throw new InvalidSelectorError(`Error while parsing selector \`${selector}\` - cannot use ${operator} in attribute with regular expression`);
value2 = readRegularExpression();
} else if (next() === `'` || next() === `"`) {
value2 = readQuotedString(next()).slice(1, -1);
skipSpaces();
if (next() === "i" || next() === "I") {
caseSensitive = false;
eat1();
} else if (next() === "s" || next() === "S") {
caseSensitive = true;
eat1();
}
} else {
value2 = "";
while (!EOL && (isCSSNameChar(next()) || next() === "+" || next() === "."))
value2 += eat1();
if (value2 === "true") {
value2 = true;
} else if (value2 === "false") {
value2 = false;
} else {
if (!allowUnquotedStrings) {
value2 = +value2;
if (Number.isNaN(value2))
syntaxError("parsing attribute value");
}
}
}
skipSpaces();
if (next() !== "]")
syntaxError("parsing attribute value");
eat1();
if (operator !== "=" && typeof value2 !== "string")
throw new InvalidSelectorError(`Error while parsing selector \`${selector}\` - cannot use ${operator} in attribute with non-string matching value - ${value2}`);
return { name: jsonPath.join("."), jsonPath, op: operator, value: value2, caseSensitive };
}
const result2 = {
name: "",
attributes: []
};
result2.name = readIdentifier();
skipSpaces();
while (next() === "[") {
result2.attributes.push(readAttribute());
skipSpaces();
}
if (!EOL)
syntaxError(void 0);
if (!result2.name && !result2.attributes.length)
throw new InvalidSelectorError(`Error while parsing selector \`${selector}\` - selector cannot be empty`);
return result2;
}
var kNestedSelectorNames, kNestedSelectorNamesWithDistance, customCSSNames;
var init_selectorParser = __esm({
"packages/isomorphic/selectorParser.ts"() {
"use strict";
init_cssParser();
init_cssParser();
kNestedSelectorNames = /* @__PURE__ */ new Set(["internal:has", "internal:has-not", "internal:and", "internal:or", "internal:chain", "left-of", "right-of", "above", "below", "near"]);
kNestedSelectorNamesWithDistance = /* @__PURE__ */ new Set(["left-of", "right-of", "above", "below", "near"]);
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"]);
}
});
// packages/isomorphic/locatorGenerators.ts
function asLocatorDescription(lang, selector) {
try {
const parsed = parseSelector(selector);
const customDescription = parseCustomDescription(parsed);
if (customDescription)
return customDescription;
return innerAsLocators(new generators[lang](), parsed, false, 1)[0];
} catch (e) {
return selector;
}
}
function locatorCustomDescription(selector) {
try {
const parsed = parseSelector(selector);
return parseCustomDescription(parsed);
} catch (e) {
return void 0;
}
}
function parseCustomDescription(parsed) {
const lastPart = parsed.parts[parsed.parts.length - 1];
if (lastPart?.name === "internal:describe") {
const description = JSON.parse(lastPart.body);
if (typeof description === "string")
return description;
}
return void 0;
}
function asLocator(lang, selector, isFrameLocator = false) {
return asLocators(lang, selector, isFrameLocator, 1)[0];
}
function asLocators(lang, selector, isFrameLocator = false, maxOutputSize = 20, preferredQuote) {
try {
return innerAsLocators(new generators[lang](preferredQuote), parseSelector(selector), isFrameLocator, maxOutputSize);
} catch (e) {
return [selector];
}
}
function innerAsLocators(factory, parsed, isFrameLocator = false, maxOutputSize = 20) {
const parts = [...parsed.parts];
const tokens = [];
let nextBase = isFrameLocator ? "frame-locator" : "page";
for (let index = 0; index < parts.length; index++) {
const part = parts[index];
const base = nextBase;
nextBase = "locator";
if (part.name === "internal:describe")
continue;
if (part.name === "nth") {
if (part.body === "0")
tokens.push([factory.generateLocator(base, "first", ""), factory.generateLocator(base, "nth", "0")]);
else if (part.body === "-1")
tokens.push([factory.generateLocator(base, "last", ""), factory.generateLocator(base, "nth", "-1")]);
else
tokens.push([factory.generateLocator(base, "nth", part.body)]);
continue;
}
if (part.name === "visible") {
tokens.push([factory.generateLocator(base, "visible", part.body), factory.generateLocator(base, "default", `visible=${part.body}`)]);
continue;
}
if (part.name === "internal:text") {
const { exact, text: text2 } = detectExact(part.body);
tokens.push([factory.generateLocator(base, "text", text2, { exact })]);
continue;
}
if (part.name === "internal:has-text") {
const { exact, text: text2 } = detectExact(part.body);
if (!exact) {
tokens.push([factory.generateLocator(base, "has-text", text2, { exact })]);
continue;
}
}
if (part.name === "internal:has-not-text") {
const { exact, text: text2 } = detectExact(part.body);
if (!exact) {
tokens.push([factory.generateLocator(base, "has-not-text", text2, { exact })]);
continue;
}
}
if (part.name === "internal:has") {
const inners = innerAsLocators(factory, part.body.parsed, false, maxOutputSize);
tokens.push(inners.map((inner) => factory.generateLocator(base, "has", inner)));
continue;
}
if (part.name === "internal:has-not") {
const inners = innerAsLocators(factory, part.body.parsed, false, maxOutputSize);
tokens.push(inners.map((inner) => factory.generateLocator(base, "hasNot", inner)));
continue;
}
if (part.name === "internal:and") {
const inners = innerAsLocators(factory, part.body.parsed, false, maxOutputSize);
tokens.push(inners.map((inner) => factory.generateLocator(base, "and", inner)));
continue;
}
if (part.name === "internal:or") {
const inners = innerAsLocators(factory, part.body.parsed, false, maxOutputSize);
tokens.push(inners.map((inner) => factory.generateLocator(base, "or", inner)));
continue;
}
if (part.name === "internal:chain") {
const inners = innerAsLocators(factory, part.body.parsed, false, maxOutputSize);
tokens.push(inners.map((inner) => factory.generateLocator(base, "chain", inner)));
continue;
}
if (part.name === "internal:label") {
const { exact, text: text2 } = detectExact(part.body);
tokens.push([factory.generateLocator(base, "label", text2, { exact })]);
continue;
}
if (part.name === "internal:role") {
const attrSelector = parseAttributeSelector(part.body, true);
const options2 = { attrs: [] };
for (const attr of attrSelector.attributes) {
if (attr.name === "name") {
options2.exact = attr.caseSensitive;
options2.name = attr.value;
} else if (attr.name === "description") {
options2.exact = attr.caseSensitive;
options2.description = attr.value;
} else {
if (attr.name === "level" && typeof attr.value === "string")
attr.value = +attr.value;
options2.attrs.push({ name: attr.name === "include-hidden" ? "includeHidden" : attr.name, value: attr.value });
}
}
tokens.push([factory.generateLocator(base, "role", attrSelector.name, options2)]);
continue;
}
if (part.name === "internal:testid") {
const attrSelector = parseAttributeSelector(part.body, true);
const { value: value2 } = attrSelector.attributes[0];
tokens.push([factory.generateLocator(base, "test-id", value2)]);
continue;
}
if (part.name === "internal:attr") {
const attrSelector = parseAttributeSelector(part.body, true);
const { name, value: value2, caseSensitive } = attrSelector.attributes[0];
const text2 = value2;
const exact = !!caseSensitive;
if (name === "placeholder") {
tokens.push([factory.generateLocator(base, "placeholder", text2, { exact })]);
continue;
}
if (name === "alt") {
tokens.push([factory.generateLocator(base, "alt", text2, { exact })]);
continue;
}
if (name === "title") {
tokens.push([factory.generateLocator(base, "title", text2, { exact })]);
continue;
}
}
if (part.name === "internal:control" && part.body === "enter-frame") {
const lastTokens = tokens[tokens.length - 1];
const lastPart = parts[index - 1];
const transformed = lastTokens.map((token) => factory.chainLocators([token, factory.generateLocator(base, "frame", "")]));
if (["xpath", "css"].includes(lastPart.name)) {
transformed.push(
factory.generateLocator(base, "frame-locator", stringifySelector({ parts: [lastPart] })),
factory.generateLocator(base, "frame-locator", stringifySelector({ parts: [lastPart] }, true))
);
}
lastTokens.splice(0, lastTokens.length, ...transformed);
nextBase = "frame-locator";
continue;
}
const nextPart = parts[index + 1];
const selectorPart = stringifySelector({ parts: [part] });
const locatorPart = factory.generateLocator(base, "default", selectorPart);
if (nextPart && ["internal:has-text", "internal:has-not-text"].includes(nextPart.name)) {
const { exact, text: text2 } = detectExact(nextPart.body);
if (!exact) {
const nextLocatorPart = factory.generateLocator("locator", nextPart.name === "internal:has-text" ? "has-text" : "has-not-text", text2, { exact });
const options2 = {};
if (nextPart.name === "internal:has-text")
options2.hasText = text2;
else
options2.hasNotText = text2;
const combinedPart = factory.generateLocator(base, "default", selectorPart, options2);
tokens.push([factory.chainLocators([locatorPart, nextLocatorPart]), combinedPart]);
index++;
continue;
}
}
let locatorPartWithEngine;
if (["xpath", "css"].includes(part.name)) {
const selectorPart2 = stringifySelector(
{ parts: [part] },
/* forceEngineName */
true
);
locatorPartWithEngine = factory.generateLocator(base, "default", selectorPart2);
}
tokens.push([locatorPart, locatorPartWithEngine].filter(Boolean));
}
return combineTokens(factory, tokens, maxOutputSize);
}
function combineTokens(factory, tokens, maxOutputSize) {
const currentTokens = tokens.map(() => "");
const result2 = [];
const visit = (index) => {
if (index === tokens.length) {
result2.push(factory.chainLocators(currentTokens));
return result2.length < maxOutputSize;
}
for (const taken of tokens[index]) {
currentTokens[index] = taken;
if (!visit(index + 1))
return false;
}
return true;
};
visit(0);
return result2;
}
function detectExact(text2) {
let exact = false;
const match = text2.match(/^\/(.*)\/([igm]*)$/);
if (match)
return { text: new RegExp(match[1], match[2]) };
if (text2.endsWith('"')) {
text2 = JSON.parse(text2);
exact = true;
} else if (text2.endsWith('"s')) {
text2 = JSON.parse(text2.substring(0, text2.length - 1));
exact = true;
} else if (text2.endsWith('"i')) {
text2 = JSON.parse(text2.substring(0, text2.length - 1));
exact = false;
}
return { exact, text: text2 };
}
function isRegExp2(obj) {
return obj instanceof RegExp;
}
var JavaScriptLocatorFactory, PythonLocatorFactory, JavaLocatorFactory, CSharpLocatorFactory, JsonlLocatorFactory, generators;
var init_locatorGenerators = __esm({
"packages/isomorphic/locatorGenerators.ts"() {
"use strict";
init_selectorParser();
init_stringUtils();
JavaScriptLocatorFactory = class {
constructor(preferredQuote) {
this.preferredQuote = preferredQuote;
}
generateLocator(base, kind, body, options2 = {}) {
switch (kind) {
case "default":
if (options2.hasText !== void 0)
return `locator(${this.quote(body)}, { hasText: ${this.toHasText(options2.hasText)} })`;
if (options2.hasNotText !== void 0)
return `locator(${this.quote(body)}, { hasNotText: ${this.toHasText(options2.hasNotText)} })`;
return `locator(${this.quote(body)})`;
case "frame-locator":
return `frameLocator(${this.quote(body)})`;
case "frame":
return `contentFrame()`;
case "nth":
return `nth(${body})`;
case "first":
return `first()`;
case "last":
return `last()`;
case "visible":
return `filter({ visible: ${body === "true" ? "true" : "false"} })`;
case "role":
const attrs = [];
if (isRegExp2(options2.name))
attrs.push(`name: ${this.regexToSourceString(options2.name)}`);
else if (typeof options2.name === "string")
attrs.push(`name: ${this.quote(options2.name)}`);
if (isRegExp2(options2.description))
attrs.push(`description: ${this.regexToSourceString(options2.description)}`);
else if (typeof options2.description === "string")
attrs.push(`description: ${this.quote(options2.description)}`);
if (options2.exact && (typeof options2.name === "string" || typeof options2.description === "string"))
attrs.push(`exact: true`);
for (const { name, value: value2 } of options2.attrs)
attrs.push(`${name}: ${typeof value2 === "string" ? this.quote(value2) : value2}`);
const attrString = attrs.length ? `, { ${attrs.join(", ")} }` : "";
return `getByRole(${this.quote(body)}${attrString})`;
case "has-text":
return `filter({ hasText: ${this.toHasText(body)} })`;
case "has-not-text":
return `filter({ hasNotText: ${this.toHasText(body)} })`;
case "has":
return `filter({ has: ${body} })`;
case "hasNot":
return `filter({ hasNot: ${body} })`;
case "and":
return `and(${body})`;
case "or":
return `or(${body})`;
case "chain":
return `locator(${body})`;
case "test-id":
return `getByTestId(${this.toTestIdValue(body)})`;
case "text":
return this.toCallWithExact("getByText", body, !!options2.exact);
case "alt":
return this.toCallWithExact("getByAltText", body, !!options2.exact);
case "placeholder":
return this.toCallWithExact("getByPlaceholder", body, !!options2.exact);
case "label":
return this.toCallWithExact("getByLabel", body, !!options2.exact);
case "title":
return this.toCallWithExact("getByTitle", body, !!options2.exact);
default:
throw new Error("Unknown selector kind " + kind);
}
}
chainLocators(locators) {
return locators.join(".");
}
regexToSourceString(re2) {
return normalizeEscapedRegexQuotes(String(re2));
}
toCallWithExact(method, body, exact) {
if (isRegExp2(body))
return `${method}(${this.regexToSourceString(body)})`;
return exact ? `${method}(${this.quote(body)}, { exact: true })` : `${method}(${this.quote(body)})`;
}
toHasText(body) {
if (isRegExp2(body))
return this.regexToSourceString(body);
return this.quote(body);
}
toTestIdValue(value2) {
if (isRegExp2(value2))
return this.regexToSourceString(value2);
return this.quote(value2);
}
quote(text2) {
return escapeWithQuotes(text2, this.preferredQuote ?? "'");
}
};
PythonLocatorFactory = class {
generateLocator(base, kind, body, options2 = {}) {
switch (kind) {
case "default":
if (options2.hasText !== void 0)
return `locator(${this.quote(body)}, has_text=${this.toHasText(options2.hasText)})`;
if (options2.hasNotText !== void 0)
return `locator(${this.quote(body)}, has_not_text=${this.toHasText(options2.hasNotText)})`;
return `locator(${this.quote(body)})`;
case "frame-locator":
return `frame_locator(${this.quote(body)})`;
case "frame":
return `content_frame`;
case "nth":
return `nth(${body})`;
case "first":
return `first`;
case "last":
return `last`;
case "visible":
return `filter(visible=${body === "true" ? "True" : "False"})`;
case "role":
const attrs = [];
if (isRegExp2(options2.name))
attrs.push(`name=${this.regexToString(options2.name)}`);
else if (typeof options2.name === "string")
attrs.push(`name=${this.quote(options2.name)}`);
if (isRegExp2(options2.description))
attrs.push(`description=${this.regexToString(options2.description)}`);
else if (typeof options2.description === "string")
attrs.push(`description=${this.quote(options2.description)}`);
if (options2.exact && (typeof options2.name === "string" || typeof options2.description === "string"))
attrs.push(`exact=True`);
for (const { name, value: value2 } of options2.attrs) {
let valueString = typeof value2 === "string" ? this.quote(value2) : value2;
if (typeof value2 === "boolean")
valueString = value2 ? "True" : "False";
attrs.push(`${toSnakeCase(name)}=${valueString}`);
}
const attrString = attrs.length ? `, ${attrs.join(", ")}` : "";
return `get_by_role(${this.quote(body)}${attrString})`;
case "has-text":
return `filter(has_text=${this.toHasText(body)})`;
case "has-not-text":
return `filter(has_not_text=${this.toHasText(body)})`;
case "has":
return `filter(has=${body})`;
case "hasNot":
return `filter(has_not=${body})`;
case "and":
return `and_(${body})`;
case "or":
return `or_(${body})`;
case "chain":
return `locator(${body})`;
case "test-id":
return `get_by_test_id(${this.toTestIdValue(body)})`;
case "text":
return this.toCallWithExact("get_by_text", body, !!options2.exact);
case "alt":
return this.toCallWithExact("get_by_alt_text", body, !!options2.exact);
case "placeholder":
return this.toCallWithExact("get_by_placeholder", body, !!options2.exact);
case "label":
return this.toCallWithExact("get_by_label", body, !!options2.exact);
case "title":
return this.toCallWithExact("get_by_title", body, !!options2.exact);
default:
throw new Error("Unknown selector kind " + kind);
}
}
chainLocators(locators) {
return locators.join(".");
}
regexToString(body) {
const suffix = body.flags.includes("i") ? ", re.IGNORECASE" : "";
return `re.compile(r"${normalizeEscapedRegexQuotes(body.source).replace(/\\\//, "/").replace(/"/g, '\\"')}"${suffix})`;
}
toCallWithExact(method, body, exact) {
if (isRegExp2(body))
return `${method}(${this.regexToString(body)})`;
if (exact)
return `${method}(${this.quote(body)}, exact=True)`;
return `${method}(${this.quote(body)})`;
}
toHasText(body) {
if (isRegExp2(body))
return this.regexToString(body);
return `${this.quote(body)}`;
}
toTestIdValue(value2) {
if (isRegExp2(value2))
return this.regexToString(value2);
return this.quote(value2);
}
quote(text2) {
return escapeWithQuotes(text2, '"');
}
};
JavaLocatorFactory = class {
generateLocator(base, kind, body, options2 = {}) {
let clazz;
switch (base) {
case "page":
clazz = "Page";
break;
case "frame-locator":
clazz = "FrameLocator";
break;
case "locator":
clazz = "Locator";
break;
}
switch (kind) {
case "default":
if (options2.hasText !== void 0)
return `locator(${this.quote(body)}, new ${clazz}.LocatorOptions().setHasText(${this.toHasText(options2.hasText)}))`;
if (options2.hasNotText !== void 0)
return `locator(${this.quote(body)}, new ${clazz}.LocatorOptions().setHasNotText(${this.toHasText(options2.hasNotText)}))`;
return `locator(${this.quote(body)})`;
case "frame-locator":
return `frameLocator(${this.quote(body)})`;
case "frame":
return `contentFrame()`;
case "nth":
return `nth(${body})`;
case "first":
return `first()`;
case "last":
return `last()`;
case "visible":
return `filter(new ${clazz}.FilterOptions().setVisible(${body === "true" ? "true" : "false"}))`;
case "role":
const attrs = [];
if (isRegExp2(options2.name))
attrs.push(`.setName(${this.regexToString(options2.name)})`);
else if (typeof options2.name === "string")
attrs.push(`.setName(${this.quote(options2.name)})`);
if (isRegExp2(options2.description))
attrs.push(`.setDescription(${this.regexToString(options2.description)})`);
else if (typeof options2.description === "string")
attrs.push(`.setDescription(${this.quote(options2.description)})`);
if (options2.exact && (typeof options2.name === "string" || typeof options2.description === "string"))
attrs.push(`.setExact(true)`);
for (const { name, value: value2 } of options2.attrs)
attrs.push(`.set${toTitleCase(name)}(${typeof value2 === "string" ? this.quote(value2) : value2})`);
const attrString = attrs.length ? `, new ${clazz}.GetByRoleOptions()${attrs.join("")}` : "";
return `getByRole(AriaRole.${toSnakeCase(body).toUpperCase()}${attrString})`;
case "has-text":
return `filter(new ${clazz}.FilterOptions().setHasText(${this.toHasText(body)}))`;
case "has-not-text":
return `filter(new ${clazz}.FilterOptions().setHasNotText(${this.toHasText(body)}))`;
case "has":
return `filter(new ${clazz}.FilterOptions().setHas(${body}))`;
case "hasNot":
return `filter(new ${clazz}.FilterOptions().setHasNot(${body}))`;
case "and":
return `and(${body})`;
case "or":
return `or(${body})`;
case "chain":
return `locator(${body})`;
case "test-id":
return `getByTestId(${this.toTestIdValue(body)})`;
case "text":
return this.toCallWithExact(clazz, "getByText", body, !!options2.exact);
case "alt":
return this.toCallWithExact(clazz, "getByAltText", body, !!options2.exact);
case "placeholder":
return this.toCallWithExact(clazz, "getByPlaceholder", body, !!options2.exact);
case "label":
return this.toCallWithExact(clazz, "getByLabel", body, !!options2.exact);
case "title":
return this.toCallWithExact(clazz, "getByTitle", body, !!options2.exact);
default:
throw new Error("Unknown selector kind " + kind);
}
}
chainLocators(locators) {
return locators.join(".");
}
regexToString(body) {
const suffix = body.flags.includes("i") ? ", Pattern.CASE_INSENSITIVE" : "";
return `Pattern.compile(${this.quote(normalizeEscapedRegexQuotes(body.source))}${suffix})`;
}
toCallWithExact(clazz, method, body, exact) {
if (isRegExp2(body))
return `${method}(${this.regexToString(body)})`;
if (exact)
return `${method}(${this.quote(body)}, new ${clazz}.${toTitleCase(method)}Options().setExact(true))`;
return `${method}(${this.quote(body)})`;
}
toHasText(body) {
if (isRegExp2(body))
return this.regexToString(body);
return this.quote(body);
}
toTestIdValue(value2) {
if (isRegExp2(value2))
return this.regexToString(value2);
return this.quote(value2);
}
quote(text2) {
return escapeWithQuotes(text2, '"');
}
};
CSharpLocatorFactory = class {
generateLocator(base, kind, body, options2 = {}) {
switch (kind) {
case "default":
if (options2.hasText !== void 0)
return `Locator(${this.quote(body)}, new() { ${this.toHasText(options2.hasText)} })`;
if (options2.hasNotText !== void 0)
return `Locator(${this.quote(body)}, new() { ${this.toHasNotText(options2.hasNotText)} })`;
return `Locator(${this.quote(body)})`;
case "frame-locator":
return `FrameLocator(${this.quote(body)})`;
case "frame":
return `ContentFrame`;
case "nth":
return `Nth(${body})`;
case "first":
return `First`;
case "last":
return `Last`;
case "visible":
return `Filter(new() { Visible = ${body === "true" ? "true" : "false"} })`;
case "role":
const attrs = [];
if (isRegExp2(options2.name))
attrs.push(`NameRegex = ${this.regexToString(options2.name)}`);
else if (typeof options2.name === "string")
attrs.push(`Name = ${this.quote(options2.name)}`);
if (isRegExp2(options2.description))
attrs.push(`DescriptionRegex = ${this.regexToString(options2.description)}`);
else if (typeof options2.description === "string")
attrs.push(`Description = ${this.quote(options2.description)}`);
if (options2.exact && (typeof options2.name === "string" || typeof options2.description === "string"))
attrs.push(`Exact = true`);
for (const { name, value: value2 } of options2.attrs)
attrs.push(`${toTitleCase(name)} = ${typeof value2 === "string" ? this.quote(value2) : value2}`);
const attrString = attrs.length ? `, new() { ${attrs.join(", ")} }` : "";
return `GetByRole(AriaRole.${toTitleCase(body)}${attrString})`;
case "has-text":
return `Filter(new() { ${this.toHasText(body)} })`;
case "has-not-text":
return `Filter(new() { ${this.toHasNotText(body)} })`;
case "has":
return `Filter(new() { Has = ${body} })`;
case "hasNot":
return `Filter(new() { HasNot = ${body} })`;
case "and":
return `And(${body})`;
case "or":
return `Or(${body})`;
case "chain":
return `Locator(${body})`;
case "test-id":
return `GetByTestId(${this.toTestIdValue(body)})`;
case "text":
return this.toCallWithExact("GetByText", body, !!options2.exact);
case "alt":
return this.toCallWithExact("GetByAltText", body, !!options2.exact);
case "placeholder":
return this.toCallWithExact("GetByPlaceholder", body, !!options2.exact);
case "label":
return this.toCallWithExact("GetByLabel", body, !!options2.exact);
case "title":
return this.toCallWithExact("GetByTitle", body, !!options2.exact);
default:
throw new Error("Unknown selector kind " + kind);
}
}
chainLocators(locators) {
return locators.join(".");
}
regexToString(body) {
const suffix = body.flags.includes("i") ? ", RegexOptions.IgnoreCase" : "";
return `new Regex(${this.quote(normalizeEscapedRegexQuotes(body.source))}${suffix})`;
}
toCallWithExact(method, body, exact) {
if (isRegExp2(body))
return `${method}(${this.regexToString(body)})`;
if (exact)
return `${method}(${this.quote(body)}, new() { Exact = true })`;
return `${method}(${this.quote(body)})`;
}
toHasText(body) {
if (isRegExp2(body))
return `HasTextRegex = ${this.regexToString(body)}`;
return `HasText = ${this.quote(body)}`;
}
toTestIdValue(value2) {
if (isRegExp2(value2))
return this.regexToString(value2);
return this.quote(value2);
}
toHasNotText(body) {
if (isRegExp2(body))
return `HasNotTextRegex = ${this.regexToString(body)}`;
return `HasNotText = ${this.quote(body)}`;
}
quote(text2) {
return escapeWithQuotes(text2, '"');
}
};
JsonlLocatorFactory = class {
generateLocator(base, kind, body, options2 = {}) {
return JSON.stringify({
kind,
body,
options: options2
});
}
chainLocators(locators) {
const objects = locators.map((l) => JSON.parse(l));
for (let i = 0; i < objects.length - 1; ++i)
objects[i].next = objects[i + 1];
return JSON.stringify(objects[0]);
}
};
generators = {
javascript: JavaScriptLocatorFactory,
python: PythonLocatorFactory,
java: JavaLocatorFactory,
csharp: CSharpLocatorFactory,
jsonl: JsonlLocatorFactory
};
}
});
// packages/isomorphic/stackTrace.ts
function captureRawStack() {
const stackTraceLimit = Error.stackTraceLimit;
Error.stackTraceLimit = 50;
const error = new Error();
const stack = error.stack || "";
Error.stackTraceLimit = stackTraceLimit;
return stack.split("\n");
}
function parseStackFrame(text2, pathSeparator, showInternalStackFrames) {
const match = text2 && text2.match(re);
if (!match)
return null;
let fname = match[2];
let file = match[7];
if (!file)
return null;
if (!showInternalStackFrames && (file.startsWith("internal") || file.startsWith("node:")))
return null;
const line = match[8];
const column = match[9];
const closeParen = match[11] === ")";
const frame = {
file: "",
line: 0,
column: 0
};
if (line)
frame.line = Number(line);
if (column)
frame.column = Number(column);
if (closeParen && file) {
let closes = 0;
for (let i = file.length - 1; i > 0; i--) {
if (file.charAt(i) === ")") {
closes++;
} else if (file.charAt(i) === "(" && file.charAt(i - 1) === " ") {
closes--;
if (closes === -1 && file.charAt(i - 1) === " ") {
const before = file.slice(0, i - 1);
const after = file.slice(i + 1);
file = after;
fname += ` (${before}`;
break;
}
}
}
}
if (fname) {
const methodMatch = fname.match(methodRe);
if (methodMatch)
fname = methodMatch[1];
}
if (file) {
if (file.startsWith("file://"))
file = fileURLToPath(file, pathSeparator);
frame.file = file;
}
if (fname)
frame.function = fname;
return frame;
}
function rewriteErrorMessage(e, newMessage) {
const lines = (e.stack?.split("\n") || []).filter((l) => l.startsWith(" at "));
e.message = newMessage;
const errorTitle = `${e.name}: ${e.message}`;
if (lines.length)
e.stack = `${errorTitle}
${lines.join("\n")}`;
return e;
}
function stringifyStackFrames(frames) {
const stackLines = [];
for (const frame of frames) {
if (frame.function)
stackLines.push(` at ${frame.function} (${frame.file}:${frame.line}:${frame.column})`);
else
stackLines.push(` at ${frame.file}:${frame.line}:${frame.column}`);
}
return stackLines;
}
function splitErrorMessage(message) {
const separationIdx = message.indexOf(":");
return {
name: separationIdx !== -1 ? message.slice(0, separationIdx) : "",
message: separationIdx !== -1 && separationIdx + 2 <= message.length ? message.substring(separationIdx + 2) : message
};
}
function parseErrorStack(stack, pathSeparator, showInternalStackFrames = false) {
const lines = stack.split("\n");
let firstStackLine = lines.findIndex((line) => line.startsWith(" at "));
if (firstStackLine === -1)
firstStackLine = lines.length;
const message = lines.slice(0, firstStackLine).join("\n");
const stackLines = lines.slice(firstStackLine);
let location2;
for (const line of stackLines) {
const frame = parseStackFrame(line, pathSeparator, showInternalStackFrames);
if (!frame || !frame.file)
continue;
if (belongsToNodeModules(frame.file, pathSeparator))
continue;
location2 = { file: frame.file, column: frame.column || 0, line: frame.line || 0 };
break;
}
return { message, stackLines, location: location2 };
}
function belongsToNodeModules(file, pathSeparator) {
return file.includes(`${pathSeparator}node_modules${pathSeparator}`);
}
function fileURLToPath(fileUrl, pathSeparator) {
if (!fileUrl.startsWith("file://"))
return fileUrl;
let path59 = decodeURIComponent(fileUrl.slice(7));
if (path59.startsWith("/") && /^[a-zA-Z]:/.test(path59.slice(1)))
path59 = path59.slice(1);
return path59.replace(/\//g, pathSeparator);
}
var re, methodRe;
var init_stackTrace = __esm({
"packages/isomorphic/stackTrace.ts"() {
"use strict";
re = new RegExp(
"^(?:\\s*at )?(?:(new) )?(?:(.*?) \\()?(?:eval at ([^ ]+) \\((.+?):(\\d+):(\\d+)\\), )?(?:(.+?):(\\d+):(\\d+)|(native))(\\)?)$"
);
methodRe = /^(.*?) \[as (.*?)\]$/;
}
});
// packages/isomorphic/manualPromise.ts
function signalToPromise(signal) {
const promise = new Promise((resolve) => {
if (signal.aborted)
resolve();
else
signal.addEventListener("abort", () => resolve(), { once: true });
});
return { promise, dispose: () => {
} };
}
function cloneError(error, frames) {
const clone = new Error();
clone.name = error.name;
clone.message = error.message;
clone.stack = [error.name + ":" + error.message, ...frames].join("\n");
return clone;
}
var ManualPromise, LongStandingScope;
var init_manualPromise = __esm({
"packages/isomorphic/manualPromise.ts"() {
"use strict";
init_stackTrace();
ManualPromise = class extends Promise {
constructor() {
let resolve;
let reject;
super((f, r) => {
resolve = f;
reject = r;
});
this._isDone = false;
this._resolve = resolve;
this._reject = reject;
}
isDone() {
return this._isDone;
}
resolve(t) {
this._isDone = true;
this._resolve(t);
}
reject(e) {
this._isDone = true;
this._reject(e);
}
static get [Symbol.species]() {
return Promise;
}
get [Symbol.toStringTag]() {
return "ManualPromise";
}
};
LongStandingScope = class {
constructor() {
this._terminatePromises = /* @__PURE__ */ new Map();
this._isClosed = false;
}
reject(error) {
this._isClosed = true;
this._terminateError = error;
for (const p of this._terminatePromises.keys())
p.resolve(error);
}
close(error) {
this._isClosed = true;
this._closeError = error;
for (const [p, frames] of this._terminatePromises)
p.resolve(cloneError(error, frames));
}
isClosed() {
return this._isClosed;
}
static async raceMultiple(scopes, promise) {
return Promise.race(scopes.map((s) => s.race(promise)));
}
async race(promise) {
return this._race(Array.isArray(promise) ? promise : [promise], false);
}
async safeRace(promise, defaultValue) {
return this._race([promise], true, defaultValue);
}
async _race(promises, safe, defaultValue) {
const terminatePromise = new ManualPromise();
const frames = captureRawStack();
if (this._terminateError)
terminatePromise.resolve(this._terminateError);
if (this._closeError)
terminatePromise.resolve(cloneError(this._closeError, frames));
this._terminatePromises.set(terminatePromise, frames);
try {
return await Promise.race([
terminatePromise.then((e) => safe ? defaultValue : Promise.reject(e)),
...promises
]);
} finally {
this._terminatePromises.delete(terminatePromise);
}
}
};
}
});
// packages/isomorphic/mimeType.ts
function isJsonMimeType(mimeType) {
return !!mimeType.match(/^(application\/json|application\/.*?\+json|text\/(x-)?json)(;\s*charset=.*)?$/);
}
function isXmlMimeType(mimeType) {
return !!mimeType.match(/^(application\/xml|application\/.*?\+xml|text\/xml)(;\s*charset=.*)?$/);
}
function isTextualMimeType(mimeType) {
return !!mimeType.match(/^(text\/.*?|application\/(json|(x-)?javascript|xml.*?|ecmascript|graphql|x-www-form-urlencoded)|image\/svg(\+xml)?|application\/.*?(\+json|\+xml))(;\s*charset=.*)?$/);
}
function getMimeTypeForPath(path59) {
const dotIndex = path59.lastIndexOf(".");
if (dotIndex === -1)
return null;
const extension = path59.substring(dotIndex + 1);
return types.get(extension) || null;
}
function getExtensionForMimeType(contentType) {
const subtype = (contentType ?? "").split(";")[0].split("/")[1]?.trim().toLowerCase() ?? "";
if (!subtype)
return "bin";
const tail = subtype.includes("+") ? subtype.split("+").pop() : subtype;
if (tail === "plain")
return "txt";
if (tail === "javascript" || tail === "ecmascript")
return "js";
if (tail === "jpeg")
return "jpg";
return tail.replace(/[^a-z0-9]/g, "") || "bin";
}
var types;
var init_mimeType = __esm({
"packages/isomorphic/mimeType.ts"() {
"use strict";
types = /* @__PURE__ */ new Map([
["ez", "application/andrew-inset"],
["aw", "application/applixware"],
["atom", "application/atom+xml"],
["atomcat", "application/atomcat+xml"],
["atomdeleted", "application/atomdeleted+xml"],
["atomsvc", "application/atomsvc+xml"],
["dwd", "application/atsc-dwd+xml"],
["held", "application/atsc-held+xml"],
["rsat", "application/atsc-rsat+xml"],
["bdoc", "application/bdoc"],
["xcs", "application/calendar+xml"],
["ccxml", "application/ccxml+xml"],
["cdfx", "application/cdfx+xml"],
["cdmia", "application/cdmi-capability"],
["cdmic", "application/cdmi-container"],
["cdmid", "application/cdmi-domain"],
["cdmio", "application/cdmi-object"],
["cdmiq", "application/cdmi-queue"],
["cu", "application/cu-seeme"],
["mpd", "application/dash+xml"],
["davmount", "application/davmount+xml"],
["dbk", "application/docbook+xml"],
["dssc", "application/dssc+der"],
["xdssc", "application/dssc+xml"],
["ecma", "application/ecmascript"],
["es", "application/ecmascript"],
["emma", "application/emma+xml"],
["emotionml", "application/emotionml+xml"],
["epub", "application/epub+zip"],
["exi", "application/exi"],
["exp", "application/express"],
["fdt", "application/fdt+xml"],
["pfr", "application/font-tdpfr"],
["geojson", "application/geo+json"],
["gml", "application/gml+xml"],
["gpx", "application/gpx+xml"],
["gxf", "application/gxf"],
["gz", "application/gzip"],
["hjson", "application/hjson"],
["stk", "application/hyperstudio"],
["ink", "application/inkml+xml"],
["inkml", "application/inkml+xml"],
["ipfix", "application/ipfix"],
["its", "application/its+xml"],
["ear", "application/java-archive"],
["jar", "application/java-archive"],
["war", "application/java-archive"],
["ser", "application/java-serialized-object"],
["class", "application/java-vm"],
["js", "application/javascript"],
["mjs", "application/javascript"],
["json", "application/json"],
["map", "application/json"],
["json5", "application/json5"],
["jsonml", "application/jsonml+json"],
["jsonld", "application/ld+json"],
["lgr", "application/lgr+xml"],
["lostxml", "application/lost+xml"],
["hqx", "application/mac-binhex40"],
["cpt", "application/mac-compactpro"],
["mads", "application/mads+xml"],
["webmanifest", "application/manifest+json"],
["mrc", "application/marc"],
["mrcx", "application/marcxml+xml"],
["ma", "application/mathematica"],
["mb", "application/mathematica"],
["nb", "application/mathematica"],
["mathml", "application/mathml+xml"],
["mbox", "application/mbox"],
["mscml", "application/mediaservercontrol+xml"],
["metalink", "application/metalink+xml"],
["meta4", "application/metalink4+xml"],
["mets", "application/mets+xml"],
["maei", "application/mmt-aei+xml"],
["musd", "application/mmt-usd+xml"],
["mods", "application/mods+xml"],
["m21", "application/mp21"],
["mp21", "application/mp21"],
["m4p", "application/mp4"],
["mp4s", "application/mp4"],
["doc", "application/msword"],
["dot", "application/msword"],
["mxf", "application/mxf"],
["nq", "application/n-quads"],
["nt", "application/n-triples"],
["cjs", "application/node"],
["bin", "application/octet-stream"],
["bpk", "application/octet-stream"],
["buffer", "application/octet-stream"],
["deb", "application/octet-stream"],
["deploy", "application/octet-stream"],
["dist", "application/octet-stream"],
["distz", "application/octet-stream"],
["dll", "application/octet-stream"],
["dmg", "application/octet-stream"],
["dms", "application/octet-stream"],
["dump", "application/octet-stream"],
["elc", "application/octet-stream"],
["exe", "application/octet-stream"],
["img", "application/octet-stream"],
["iso", "application/octet-stream"],
["lrf", "application/octet-stream"],
["mar", "application/octet-stream"],
["msi", "application/octet-stream"],
["msm", "application/octet-stream"],
["msp", "application/octet-stream"],
["pkg", "application/octet-stream"],
["so", "application/octet-stream"],
["oda", "application/oda"],
["opf", "application/oebps-package+xml"],
["ogx", "application/ogg"],
["omdoc", "application/omdoc+xml"],
["onepkg", "application/onenote"],
["onetmp", "application/onenote"],
["onetoc", "application/onenote"],
["onetoc2", "application/onenote"],
["oxps", "application/oxps"],
["relo", "application/p2p-overlay+xml"],
["xer", "application/patch-ops-error+xml"],
["pdf", "application/pdf"],
["pgp", "application/pgp-encrypted"],
["asc", "application/pgp-signature"],
["sig", "application/pgp-signature"],
["prf", "application/pics-rules"],
["p10", "application/pkcs10"],
["p7c", "application/pkcs7-mime"],
["p7m", "application/pkcs7-mime"],
["p7s", "application/pkcs7-signature"],
["p8", "application/pkcs8"],
["ac", "application/pkix-attr-cert"],
["cer", "application/pkix-cert"],
["crl", "application/pkix-crl"],
["pkipath", "application/pkix-pkipath"],
["pki", "application/pkixcmp"],
["pls", "application/pls+xml"],
["ai", "application/postscript"],
["eps", "application/postscript"],
["ps", "application/postscript"],
["provx", "application/provenance+xml"],
["pskcxml", "application/pskc+xml"],
["raml", "application/raml+yaml"],
["owl", "application/rdf+xml"],
["rdf", "application/rdf+xml"],
["rif", "application/reginfo+xml"],
["rnc", "application/relax-ng-compact-syntax"],
["rl", "application/resource-lists+xml"],
["rld", "application/resource-lists-diff+xml"],
["rs", "application/rls-services+xml"],
["rapd", "application/route-apd+xml"],
["sls", "application/route-s-tsid+xml"],
["rusd", "application/route-usd+xml"],
["gbr", "application/rpki-ghostbusters"],
["mft", "application/rpki-manifest"],
["roa", "application/rpki-roa"],
["rsd", "application/rsd+xml"],
["rss", "application/rss+xml"],
["rtf", "application/rtf"],
["sbml", "application/sbml+xml"],
["scq", "application/scvp-cv-request"],
["scs", "application/scvp-cv-response"],
["spq", "application/scvp-vp-request"],
["spp", "application/scvp-vp-response"],
["sdp", "application/sdp"],
["senmlx", "application/senml+xml"],
["sensmlx", "application/sensml+xml"],
["setpay", "application/set-payment-initiation"],
["setreg", "application/set-registration-initiation"],
["shf", "application/shf+xml"],
["sieve", "application/sieve"],
["siv", "application/sieve"],
["smi", "application/smil+xml"],
["smil", "application/smil+xml"],
["rq", "application/sparql-query"],
["srx", "application/sparql-results+xml"],
["gram", "application/srgs"],
["grxml", "application/srgs+xml"],
["sru", "application/sru+xml"],
["ssdl", "application/ssdl+xml"],
["ssml", "application/ssml+xml"],
["swidtag", "application/swid+xml"],
["tei", "application/tei+xml"],
["teicorpus", "application/tei+xml"],
["tfi", "application/thraud+xml"],
["tsd", "application/timestamped-data"],
["toml", "application/toml"],
["trig", "application/trig"],
["ttml", "application/ttml+xml"],
["ubj", "application/ubjson"],
["rsheet", "application/urc-ressheet+xml"],
["td", "application/urc-targetdesc+xml"],
["vxml", "application/voicexml+xml"],
["wasm", "application/wasm"],
["wgt", "application/widget"],
["hlp", "application/winhlp"],
["wsdl", "application/wsdl+xml"],
["wspolicy", "application/wspolicy+xml"],
["xaml", "application/xaml+xml"],
["xav", "application/xcap-att+xml"],
["xca", "application/xcap-caps+xml"],
["xdf", "application/xcap-diff+xml"],
["xel", "application/xcap-el+xml"],
["xns", "application/xcap-ns+xml"],
["xenc", "application/xenc+xml"],
["xht", "application/xhtml+xml"],
["xhtml", "application/xhtml+xml"],
["xlf", "application/xliff+xml"],
["rng", "application/xml"],
["xml", "application/xml"],
["xsd", "application/xml"],
["xsl", "application/xml"],
["dtd", "application/xml-dtd"],
["xop", "application/xop+xml"],
["xpl", "application/xproc+xml"],
["*xsl", "application/xslt+xml"],
["xslt", "application/xslt+xml"],
["xspf", "application/xspf+xml"],
["mxml", "application/xv+xml"],
["xhvml", "application/xv+xml"],
["xvm", "application/xv+xml"],
["xvml", "application/xv+xml"],
["yang", "application/yang"],
["yin", "application/yin+xml"],
["zip", "application/zip"],
["*3gpp", "audio/3gpp"],
["adp", "audio/adpcm"],
["amr", "audio/amr"],
["au", "audio/basic"],
["snd", "audio/basic"],
["kar", "audio/midi"],
["mid", "audio/midi"],
["midi", "audio/midi"],
["rmi", "audio/midi"],
["mxmf", "audio/mobile-xmf"],
["*mp3", "audio/mp3"],
["m4a", "audio/mp4"],
["mp4a", "audio/mp4"],
["m2a", "audio/mpeg"],
["m3a", "audio/mpeg"],
["mp2", "audio/mpeg"],
["mp2a", "audio/mpeg"],
["mp3", "audio/mpeg"],
["mpga", "audio/mpeg"],
["oga", "audio/ogg"],
["ogg", "audio/ogg"],
["opus", "audio/ogg"],
["spx", "audio/ogg"],
["s3m", "audio/s3m"],
["sil", "audio/silk"],
["wav", "audio/wav"],
["*wav", "audio/wave"],
["weba", "audio/webm"],
["xm", "audio/xm"],
["ttc", "font/collection"],
["otf", "font/otf"],
["ttf", "font/ttf"],
["woff", "font/woff"],
["woff2", "font/woff2"],
["exr", "image/aces"],
["apng", "image/apng"],
["avif", "image/avif"],
["bmp", "image/bmp"],
["cgm", "image/cgm"],
["drle", "image/dicom-rle"],
["emf", "image/emf"],
["fits", "image/fits"],
["g3", "image/g3fax"],
["gif", "image/gif"],
["heic", "image/heic"],
["heics", "image/heic-sequence"],
["heif", "image/heif"],
["heifs", "image/heif-sequence"],
["hej2", "image/hej2k"],
["hsj2", "image/hsj2"],
["ief", "image/ief"],
["jls", "image/jls"],
["jp2", "image/jp2"],
["jpg2", "image/jp2"],
["jpe", "image/jpeg"],
["jpeg", "image/jpeg"],
["jpg", "image/jpeg"],
["jph", "image/jph"],
["jhc", "image/jphc"],
["jpm", "image/jpm"],
["jpf", "image/jpx"],
["jpx", "image/jpx"],
["jxr", "image/jxr"],
["jxra", "image/jxra"],
["jxrs", "image/jxrs"],
["jxs", "image/jxs"],
["jxsc", "image/jxsc"],
["jxsi", "image/jxsi"],
["jxss", "image/jxss"],
["ktx", "image/ktx"],
["ktx2", "image/ktx2"],
["png", "image/png"],
["sgi", "image/sgi"],
["svg", "image/svg+xml"],
["svgz", "image/svg+xml"],
["t38", "image/t38"],
["tif", "image/tiff"],
["tiff", "image/tiff"],
["tfx", "image/tiff-fx"],
["webp", "image/webp"],
["wmf", "image/wmf"],
["disposition-notification", "message/disposition-notification"],
["u8msg", "message/global"],
["u8dsn", "message/global-delivery-status"],
["u8mdn", "message/global-disposition-notification"],
["u8hdr", "message/global-headers"],
["eml", "message/rfc822"],
["mime", "message/rfc822"],
["3mf", "model/3mf"],
["gltf", "model/gltf+json"],
["glb", "model/gltf-binary"],
["iges", "model/iges"],
["igs", "model/iges"],
["mesh", "model/mesh"],
["msh", "model/mesh"],
["silo", "model/mesh"],
["mtl", "model/mtl"],
["obj", "model/obj"],
["stpx", "model/step+xml"],
["stpz", "model/step+zip"],
["stpxz", "model/step-xml+zip"],
["stl", "model/stl"],
["vrml", "model/vrml"],
["wrl", "model/vrml"],
["*x3db", "model/x3d+binary"],
["x3dbz", "model/x3d+binary"],
["x3db", "model/x3d+fastinfoset"],
["*x3dv", "model/x3d+vrml"],
["x3dvz", "model/x3d+vrml"],
["x3d", "model/x3d+xml"],
["x3dz", "model/x3d+xml"],
["x3dv", "model/x3d-vrml"],
["appcache", "text/cache-manifest"],
["manifest", "text/cache-manifest"],
["ics", "text/calendar"],
["ifb", "text/calendar"],
["coffee", "text/coffeescript"],
["litcoffee", "text/coffeescript"],
["css", "text/css"],
["csv", "text/csv"],
["htm", "text/html"],
["html", "text/html"],
["shtml", "text/html"],
["jade", "text/jade"],
["jsx", "text/jsx"],
["less", "text/less"],
["markdown", "text/markdown"],
["md", "text/markdown"],
["mml", "text/mathml"],
["mdx", "text/mdx"],
["n3", "text/n3"],
["conf", "text/plain"],
["def", "text/plain"],
["in", "text/plain"],
["ini", "text/plain"],
["list", "text/plain"],
["log", "text/plain"],
["text", "text/plain"],
["txt", "text/plain"],
["rtx", "text/richtext"],
["*rtf", "text/rtf"],
["sgm", "text/sgml"],
["sgml", "text/sgml"],
["shex", "text/shex"],
["slim", "text/slim"],
["slm", "text/slim"],
["spdx", "text/spdx"],
["styl", "text/stylus"],
["stylus", "text/stylus"],
["tsv", "text/tab-separated-values"],
["man", "text/troff"],
["me", "text/troff"],
["ms", "text/troff"],
["roff", "text/troff"],
["t", "text/troff"],
["tr", "text/troff"],
["ttl", "text/turtle"],
["uri", "text/uri-list"],
["uris", "text/uri-list"],
["urls", "text/uri-list"],
["vcard", "text/vcard"],
["vtt", "text/vtt"],
["*xml", "text/xml"],
["yaml", "text/yaml"],
["yml", "text/yaml"],
["3gp", "video/3gpp"],
["3gpp", "video/3gpp"],
["3g2", "video/3gpp2"],
["h261", "video/h261"],
["h263", "video/h263"],
["h264", "video/h264"],
["m4s", "video/iso.segment"],
["jpgv", "video/jpeg"],
["jpm", "video/jpm"],
["jpgm", "video/jpm"],
["mj2", "video/mj2"],
["mjp2", "video/mj2"],
["ts", "application/typescript"],
["mp4", "video/mp4"],
["mp4v", "video/mp4"],
["mpg4", "video/mp4"],
["m1v", "video/mpeg"],
["m2v", "video/mpeg"],
["mpe", "video/mpeg"],
["mpeg", "video/mpeg"],
["mpg", "video/mpeg"],
["ogv", "video/ogg"],
["mov", "video/quicktime"],
["qt", "video/quicktime"],
["webm", "video/webm"]
]);
}
});
// packages/isomorphic/multimap.ts
var MultiMap;
var init_multimap = __esm({
"packages/isomorphic/multimap.ts"() {
"use strict";
MultiMap = class {
constructor() {
this._map = /* @__PURE__ */ new Map();
}
set(key, value2) {
let values = this._map.get(key);
if (!values) {
values = [];
this._map.set(key, values);
}
values.push(value2);
}
get(key) {
return this._map.get(key) || [];
}
has(key) {
return this._map.has(key);
}
delete(key, value2) {
const values = this._map.get(key);
if (!values)
return;
if (values.includes(value2))
this._map.set(key, values.filter((v) => value2 !== v));
}
deleteAll(key) {
this._map.delete(key);
}
hasValue(key, value2) {
const values = this._map.get(key);
if (!values)
return false;
return values.includes(value2);
}
get size() {
return this._map.size;
}
[Symbol.iterator]() {
return this._map[Symbol.iterator]();
}
keys() {
return this._map.keys();
}
values() {
const result2 = [];
for (const key of this.keys())
result2.push(...this.get(key));
return result2;
}
clear() {
this._map.clear();
}
};
}
});
// packages/isomorphic/protocolMetainfo.ts
function getMetainfo(metadata) {
return methodMetainfo.get(metadata.type + "." + metadata.method);
}
var methodMetainfo;
var init_protocolMetainfo = __esm({
"packages/isomorphic/protocolMetainfo.ts"() {
"use strict";
methodMetainfo = /* @__PURE__ */ new Map([
["Android.devices", { internal: true }],
["AndroidSocket.write", { internal: true }],
["AndroidSocket.close", { internal: true }],
["AndroidDevice.wait", { title: "Wait" }],
["AndroidDevice.fill", { title: 'Fill "{text}"' }],
["AndroidDevice.tap", { title: "Tap" }],
["AndroidDevice.drag", { title: "Drag" }],
["AndroidDevice.fling", { title: "Fling" }],
["AndroidDevice.longTap", { title: "Long tap" }],
["AndroidDevice.pinchClose", { title: "Pinch close" }],
["AndroidDevice.pinchOpen", { title: "Pinch open" }],
["AndroidDevice.scroll", { title: "Scroll" }],
["AndroidDevice.swipe", { title: "Swipe" }],
["AndroidDevice.info", { internal: true }],
["AndroidDevice.screenshot", { title: "Screenshot" }],
["AndroidDevice.inputType", { title: "Type" }],
["AndroidDevice.inputPress", { title: "Press" }],
["AndroidDevice.inputTap", { title: "Tap" }],
["AndroidDevice.inputSwipe", { title: "Swipe" }],
["AndroidDevice.inputDrag", { title: "Drag" }],
["AndroidDevice.launchBrowser", { title: "Launch browser" }],
["AndroidDevice.open", { title: "Open app" }],
["AndroidDevice.shell", { title: "Execute shell command", group: "configuration" }],
["AndroidDevice.installApk", { title: "Install apk" }],
["AndroidDevice.push", { title: "Push" }],
["AndroidDevice.connectToWebView", { title: "Connect to Web View" }],
["AndroidDevice.close", { internal: true }],
["APIRequestContext.fetch", { title: '{method} "{url}"' }],
["APIRequestContext.fetchResponseBody", { title: "Get response body", group: "getter" }],
["APIRequestContext.fetchLog", { internal: true }],
["APIRequestContext.storageState", { title: "Get storage state", group: "configuration" }],
["APIRequestContext.disposeAPIResponse", { internal: true }],
["APIRequestContext.dispose", { internal: true }],
["Artifact.pathAfterFinished", { internal: true }],
["Artifact.saveAs", { internal: true }],
["Artifact.saveAsStream", { internal: true }],
["Artifact.failure", { internal: true }],
["Artifact.stream", { internal: true }],
["Artifact.cancel", { internal: true }],
["Artifact.delete", { internal: true }],
["Stream.read", { internal: true }],
["Stream.close", { internal: true }],
["WritableStream.write", { internal: true }],
["WritableStream.close", { internal: true }],
["Browser.startServer", { title: "Start server" }],
["Browser.stopServer", { title: "Stop server" }],
["Browser.close", { title: "Close browser", pause: true }],
["Browser.killForTests", { internal: true }],
["Browser.defaultUserAgentForTest", { internal: true }],
["Browser.newContext", { title: "Create context" }],
["Browser.newContextForReuse", { internal: true }],
["Browser.disconnectFromReusedContext", { internal: true }],
["Browser.newBrowserCDPSession", { title: "Create CDP session", group: "configuration" }],
["Browser.startTracing", { title: "Start browser tracing", group: "configuration" }],
["Browser.stopTracing", { title: "Stop browser tracing", group: "configuration" }],
["BrowserContext.addCookies", { title: "Add cookies", group: "configuration" }],
["BrowserContext.addInitScript", { title: "Add init script", group: "configuration" }],
["BrowserContext.clearCookies", { title: "Clear cookies", group: "configuration" }],
["BrowserContext.clearPermissions", { title: "Clear permissions", group: "configuration" }],
["BrowserContext.close", { title: "Close context", pause: true }],
["BrowserContext.cookies", { title: "Get cookies", group: "getter" }],
["BrowserContext.exposeBinding", { title: "Expose binding", group: "configuration" }],
["BrowserContext.grantPermissions", { title: "Grant permissions", group: "configuration" }],
["BrowserContext.newPage", { title: "Create page" }],
["BrowserContext.registerSelectorEngine", { internal: true }],
["BrowserContext.setTestIdAttributeName", { internal: true }],
["BrowserContext.setExtraHTTPHeaders", { title: "Set extra HTTP headers", group: "configuration" }],
["BrowserContext.setGeolocation", { title: "Set geolocation", group: "configuration" }],
["BrowserContext.setHTTPCredentials", { title: "Set HTTP credentials", group: "configuration" }],
["BrowserContext.setNetworkInterceptionPatterns", { title: "Route requests", group: "route" }],
["BrowserContext.setWebSocketInterceptionPatterns", { title: "Route WebSockets", group: "route" }],
["BrowserContext.setOffline", { title: "Set offline mode" }],
["BrowserContext.storageState", { title: "Get storage state", group: "configuration" }],
["BrowserContext.setStorageState", { title: "Set storage state", group: "configuration" }],
["BrowserContext.pause", { title: "Pause" }],
["BrowserContext.enableRecorder", { internal: true }],
["BrowserContext.disableRecorder", { internal: true }],
["BrowserContext.exposeConsoleApi", { internal: true }],
["BrowserContext.newCDPSession", { title: "Create CDP session", group: "configuration" }],
["BrowserContext.createTempFiles", { internal: true }],
["BrowserContext.updateSubscription", { internal: true }],
["BrowserContext.clockFastForward", { title: 'Fast forward clock "{ticksNumber|ticksString}"' }],
["BrowserContext.clockInstall", { title: 'Install clock "{timeNumber|timeString}"' }],
["BrowserContext.clockPauseAt", { title: 'Pause clock "{timeNumber|timeString}"' }],
["BrowserContext.clockResume", { title: "Resume clock" }],
["BrowserContext.clockRunFor", { title: 'Run clock "{ticksNumber|ticksString}"' }],
["BrowserContext.clockSetFixedTime", { title: 'Set fixed time "{timeNumber|timeString}"' }],
["BrowserContext.clockSetSystemTime", { title: 'Set system time "{timeNumber|timeString}"' }],
["BrowserType.launch", { title: "Launch browser" }],
["BrowserType.launchPersistentContext", { title: "Launch persistent context" }],
["BrowserType.connectOverCDP", { title: "Connect over CDP" }],
["BrowserType.connectToWorker", { title: "Connect to worker" }],
["Disposable.dispose", { internal: true }],
["EventTarget.waitForEventInfo", { title: 'Wait for event "{info.event}"', snapshot: true }],
["AndroidDevice.waitForEventInfo", { title: 'Wait for event "{info.event}"', snapshot: true }],
["BrowserContext.waitForEventInfo", { title: 'Wait for event "{info.event}"', snapshot: true }],
["ElectronApplication.waitForEventInfo", { title: 'Wait for event "{info.event}"', snapshot: true }],
["WebSocket.waitForEventInfo", { title: 'Wait for event "{info.event}"', snapshot: true }],
["Page.waitForEventInfo", { title: 'Wait for event "{info.event}"', snapshot: true }],
["Debugger.waitForEventInfo", { title: 'Wait for event "{info.event}"', snapshot: true }],
["Worker.waitForEventInfo", { title: 'Wait for event "{info.event}"', snapshot: true }],
["Electron.launch", { title: "Launch electron" }],
["ElectronApplication.browserWindow", { internal: true }],
["ElectronApplication.evaluateExpression", { title: "Evaluate" }],
["ElectronApplication.evaluateExpressionHandle", { title: "Evaluate" }],
["ElectronApplication.updateSubscription", { internal: true }],
["Frame.evalOnSelector", { title: "Evaluate", snapshot: true, pause: true }],
["Frame.evalOnSelectorAll", { title: "Evaluate", snapshot: true, pause: true }],
["Frame.addScriptTag", { title: "Add script tag", snapshot: true, pause: true }],
["Frame.addStyleTag", { title: "Add style tag", snapshot: true, pause: true }],
["Frame.ariaSnapshot", { title: "Aria snapshot", group: "getter" }],
["Frame.blur", { title: "Blur", slowMo: true, snapshot: true, pause: true }],
["Frame.check", { title: "Check", slowMo: true, snapshot: true, pause: true, input: true, isAutoWaiting: true }],
["Frame.click", { title: "Click", slowMo: true, snapshot: true, pause: true, input: true, isAutoWaiting: true }],
["Frame.content", { title: "Get content", snapshot: true, pause: true }],
["Frame.dragAndDrop", { title: "Drag and drop", slowMo: true, snapshot: true, pause: true, input: true, isAutoWaiting: true }],
["Frame.drop", { title: "Drop files or data onto an element", slowMo: true, snapshot: true, pause: true, input: true, isAutoWaiting: true }],
["Frame.dblclick", { title: "Double click", slowMo: true, snapshot: true, pause: true, input: true, isAutoWaiting: true }],
["Frame.dispatchEvent", { title: 'Dispatch "{type}"', slowMo: true, snapshot: true, pause: true }],
["Frame.evaluateExpression", { title: "Evaluate", snapshot: true, pause: true }],
["Frame.evaluateExpressionHandle", { title: "Evaluate", snapshot: true, pause: true }],
["Frame.fill", { title: 'Fill "{value}"', slowMo: true, snapshot: true, pause: true, input: true, isAutoWaiting: true }],
["Frame.focus", { title: "Focus", slowMo: true, snapshot: true, pause: true }],
["Frame.frameElement", { title: "Get frame element", group: "getter" }],
["Frame.resolveSelector", { internal: true }],
["Frame.highlight", { internal: true }],
["Frame.hideHighlight", { internal: true }],
["Frame.getAttribute", { title: 'Get attribute "{name}"', snapshot: true, pause: true, group: "getter" }],
["Frame.goto", { title: 'Navigate to "{url}"', slowMo: true, snapshot: true, pause: true }],
["Frame.hover", { title: "Hover", slowMo: true, snapshot: true, pause: true, input: true, isAutoWaiting: true }],
["Frame.innerHTML", { title: "Get HTML", snapshot: true, pause: true, group: "getter" }],
["Frame.innerText", { title: "Get inner text", snapshot: true, pause: true, group: "getter" }],
["Frame.inputValue", { title: "Get input value", snapshot: true, pause: true, group: "getter" }],
["Frame.isChecked", { title: "Is checked", snapshot: true, pause: true, group: "getter" }],
["Frame.isDisabled", { title: "Is disabled", snapshot: true, pause: true, group: "getter" }],
["Frame.isEnabled", { title: "Is enabled", snapshot: true, pause: true, group: "getter" }],
["Frame.isHidden", { title: "Is hidden", snapshot: true, pause: true, group: "getter" }],
["Frame.isVisible", { title: "Is visible", snapshot: true, pause: true, group: "getter" }],
["Frame.isEditable", { title: "Is editable", snapshot: true, pause: true, group: "getter" }],
["Frame.press", { title: 'Press "{key}"', slowMo: true, snapshot: true, pause: true, input: true, isAutoWaiting: true }],
["Frame.querySelector", { title: "Query selector", snapshot: true }],
["Frame.querySelectorAll", { title: "Query selector all", snapshot: true }],
["Frame.queryCount", { title: "Query count", snapshot: true, pause: true }],
["Frame.selectOption", { title: "Select option", slowMo: true, snapshot: true, pause: true, input: true, isAutoWaiting: true }],
["Frame.setContent", { title: "Set content", snapshot: true, pause: true }],
["Frame.setInputFiles", { title: "Set input files", slowMo: true, snapshot: true, pause: true, input: true, isAutoWaiting: true }],
["Frame.tap", { title: "Tap", slowMo: true, snapshot: true, pause: true, input: true, isAutoWaiting: true }],
["Frame.textContent", { title: "Get text content", snapshot: true, pause: true, group: "getter" }],
["Frame.title", { title: "Get page title", group: "getter" }],
["Frame.type", { title: 'Type "{text}"', slowMo: true, snapshot: true, pause: true, input: true, isAutoWaiting: true }],
["Frame.uncheck", { title: "Uncheck", slowMo: true, snapshot: true, pause: true, input: true, isAutoWaiting: true }],
["Frame.waitForTimeout", { title: "Wait for timeout", snapshot: true }],
["Frame.waitForFunction", { title: "Wait for function", snapshot: true, pause: true }],
["Frame.waitForSelector", { title: "Wait for selector", snapshot: true }],
["Frame.expect", { title: 'Expect "{expression}"', snapshot: true, pause: true }],
["JSHandle.dispose", { internal: true }],
["ElementHandle.dispose", { internal: true }],
["JSHandle.evaluateExpression", { title: "Evaluate", snapshot: true, pause: true }],
["ElementHandle.evaluateExpression", { title: "Evaluate", snapshot: true, pause: true }],
["JSHandle.evaluateExpressionHandle", { title: "Evaluate", snapshot: true, pause: true }],
["ElementHandle.evaluateExpressionHandle", { title: "Evaluate", snapshot: true, pause: true }],
["JSHandle.getPropertyList", { title: "Get property list", group: "getter" }],
["ElementHandle.getPropertyList", { title: "Get property list", group: "getter" }],
["JSHandle.getProperty", { title: "Get JS property", group: "getter" }],
["ElementHandle.getProperty", { title: "Get JS property", group: "getter" }],
["JSHandle.jsonValue", { title: "Get JSON value", group: "getter" }],
["ElementHandle.jsonValue", { title: "Get JSON value", group: "getter" }],
["ElementHandle.evalOnSelector", { title: "Evaluate", snapshot: true, pause: true }],
["ElementHandle.evalOnSelectorAll", { title: "Evaluate", snapshot: true, pause: true }],
["ElementHandle.boundingBox", { title: "Get bounding box", snapshot: true, pause: true }],
["ElementHandle.check", { title: "Check", slowMo: true, snapshot: true, pause: true, input: true, isAutoWaiting: true }],
["ElementHandle.click", { title: "Click", slowMo: true, snapshot: true, pause: true, input: true, isAutoWaiting: true }],
["ElementHandle.contentFrame", { title: "Get content frame", group: "getter" }],
["ElementHandle.dblclick", { title: "Double click", slowMo: true, snapshot: true, pause: true, input: true, isAutoWaiting: true }],
["ElementHandle.dispatchEvent", { title: "Dispatch event", slowMo: true, snapshot: true, pause: true }],
["ElementHandle.fill", { title: 'Fill "{value}"', slowMo: true, snapshot: true, pause: true, input: true, isAutoWaiting: true }],
["ElementHandle.focus", { title: "Focus", slowMo: true, snapshot: true, pause: true }],
["ElementHandle.getAttribute", { title: "Get attribute", snapshot: true, pause: true, group: "getter" }],
["ElementHandle.hover", { title: "Hover", slowMo: true, snapshot: true, pause: true, input: true, isAutoWaiting: true }],
["ElementHandle.innerHTML", { title: "Get HTML", snapshot: true, pause: true, group: "getter" }],
["ElementHandle.innerText", { title: "Get inner text", snapshot: true, pause: true, group: "getter" }],
["ElementHandle.inputValue", { title: "Get input value", snapshot: true, pause: true, group: "getter" }],
["ElementHandle.isChecked", { title: "Is checked", snapshot: true, pause: true, group: "getter" }],
["ElementHandle.isDisabled", { title: "Is disabled", snapshot: true, pause: true, group: "getter" }],
["ElementHandle.isEditable", { title: "Is editable", snapshot: true, pause: true, group: "getter" }],
["ElementHandle.isEnabled", { title: "Is enabled", snapshot: true, pause: true, group: "getter" }],
["ElementHandle.isHidden", { title: "Is hidden", snapshot: true, pause: true, group: "getter" }],
["ElementHandle.isVisible", { title: "Is visible", snapshot: true, pause: true, group: "getter" }],
["ElementHandle.ownerFrame", { title: "Get owner frame", group: "getter" }],
["ElementHandle.press", { title: 'Press "{key}"', slowMo: true, snapshot: true, pause: true, input: true, isAutoWaiting: true }],
["ElementHandle.querySelector", { title: "Query selector", snapshot: true }],
["ElementHandle.querySelectorAll", { title: "Query selector all", snapshot: true }],
["ElementHandle.screenshot", { title: "Screenshot", snapshot: true, pause: true }],
["ElementHandle.scrollIntoViewIfNeeded", { title: "Scroll into view", slowMo: true, snapshot: true, pause: true }],
["ElementHandle.selectOption", { title: "Select option", slowMo: true, snapshot: true, pause: true, input: true, isAutoWaiting: true }],
["ElementHandle.selectText", { title: "Select text", slowMo: true, snapshot: true, pause: true }],
["ElementHandle.setInputFiles", { title: "Set input files", slowMo: true, snapshot: true, pause: true, input: true, isAutoWaiting: true }],
["ElementHandle.tap", { title: "Tap", slowMo: true, snapshot: true, pause: true, input: true, isAutoWaiting: true }],
["ElementHandle.textContent", { title: "Get text content", snapshot: true, pause: true, group: "getter" }],
["ElementHandle.type", { title: "Type", slowMo: true, snapshot: true, pause: true, input: true, isAutoWaiting: true }],
["ElementHandle.uncheck", { title: "Uncheck", slowMo: true, snapshot: true, pause: true, input: true, isAutoWaiting: true }],
["ElementHandle.waitForElementState", { title: "Wait for state", snapshot: true, pause: true }],
["ElementHandle.waitForSelector", { title: "Wait for selector", snapshot: true }],
["LocalUtils.zip", { internal: true }],
["LocalUtils.harOpen", { internal: true }],
["LocalUtils.harLookup", { internal: true }],
["LocalUtils.harClose", { internal: true }],
["LocalUtils.harUnzip", { internal: true }],
["LocalUtils.connect", { internal: true }],
["LocalUtils.tracingStarted", { internal: true }],
["LocalUtils.addStackToTracingNoReply", { internal: true }],
["LocalUtils.traceDiscarded", { internal: true }],
["LocalUtils.globToRegex", { internal: true }],
["Request.response", { internal: true }],
["Request.rawRequestHeaders", { internal: true }],
["Route.redirectNavigationRequest", { internal: true }],
["Route.abort", { title: "Abort request", group: "route" }],
["Route.continue", { title: "Continue request", group: "route" }],
["Route.fulfill", { title: "Fulfill request", group: "route" }],
["WebSocketRoute.connect", { title: "Connect WebSocket to server", group: "route" }],
["WebSocketRoute.ensureOpened", { internal: true }],
["WebSocketRoute.sendToPage", { title: "Send WebSocket message", group: "route" }],
["WebSocketRoute.sendToServer", { title: "Send WebSocket message", group: "route" }],
["WebSocketRoute.closePage", { internal: true }],
["WebSocketRoute.closeServer", { internal: true }],
["Response.body", { title: "Get response body", group: "getter" }],
["Response.securityDetails", { internal: true }],
["Response.serverAddr", { internal: true }],
["Response.rawResponseHeaders", { internal: true }],
["Response.httpVersion", { internal: true }],
["Response.sizes", { internal: true }],
["Page.addInitScript", { title: "Add init script", group: "configuration" }],
["Page.close", { title: "Close page", pause: true }],
["Page.clearConsoleMessages", { title: "Clear console messages" }],
["Page.consoleMessages", { title: "Get console messages", group: "getter" }],
["Page.emulateMedia", { title: "Emulate media", snapshot: true, pause: true }],
["Page.exposeBinding", { title: "Expose binding", group: "configuration" }],
["Page.goBack", { title: "Go back", slowMo: true, snapshot: true, pause: true }],
["Page.goForward", { title: "Go forward", slowMo: true, snapshot: true, pause: true }],
["Page.requestGC", { title: "Request garbage collection", group: "configuration" }],
["Page.registerLocatorHandler", { title: "Register locator handler" }],
["Page.resolveLocatorHandlerNoReply", { internal: true }],
["Page.unregisterLocatorHandler", { title: "Unregister locator handler" }],
["Page.reload", { title: "Reload", slowMo: true, snapshot: true, pause: true }],
["Page.expectScreenshot", { title: "Expect screenshot", snapshot: true, pause: true }],
["Page.screenshot", { title: "Screenshot", snapshot: true, pause: true }],
["Page.setExtraHTTPHeaders", { title: "Set extra HTTP headers", group: "configuration" }],
["Page.setNetworkInterceptionPatterns", { title: "Route requests", group: "route" }],
["Page.setWebSocketInterceptionPatterns", { title: "Route WebSockets", group: "route" }],
["Page.setViewportSize", { title: "Set viewport size", snapshot: true, pause: true }],
["Page.keyboardDown", { title: 'Key down "{key}"', slowMo: true, snapshot: true, pause: true, input: true }],
["Page.keyboardUp", { title: 'Key up "{key}"', slowMo: true, snapshot: true, pause: true, input: true }],
["Page.keyboardInsertText", { title: 'Insert "{text}"', slowMo: true, snapshot: true, pause: true, input: true }],
["Page.keyboardType", { title: 'Type "{text}"', slowMo: true, snapshot: true, pause: true, input: true }],
["Page.keyboardPress", { title: 'Press "{key}"', slowMo: true, snapshot: true, pause: true, input: true }],
["Page.mouseMove", { title: "Mouse move", slowMo: true, snapshot: true, pause: true, input: true }],
["Page.mouseDown", { title: "Mouse down", slowMo: true, snapshot: true, pause: true, input: true }],
["Page.mouseUp", { title: "Mouse up", slowMo: true, snapshot: true, pause: true, input: true }],
["Page.mouseClick", { title: "Click", slowMo: true, snapshot: true, pause: true, input: true }],
["Page.mouseWheel", { title: "Mouse wheel", slowMo: true, snapshot: true, pause: true, input: true }],
["Page.touchscreenTap", { title: "Tap", slowMo: true, snapshot: true, pause: true, input: true }],
["Page.clearPageErrors", { title: "Clear page errors" }],
["Page.pageErrors", { title: "Get page errors", group: "getter" }],
["Page.pdf", { title: "PDF" }],
["Page.requests", { title: "Get network requests", group: "getter" }],
["Page.startJSCoverage", { title: "Start JS coverage", group: "configuration" }],
["Page.stopJSCoverage", { title: "Stop JS coverage", group: "configuration" }],
["Page.startCSSCoverage", { title: "Start CSS coverage", group: "configuration" }],
["Page.stopCSSCoverage", { title: "Stop CSS coverage", group: "configuration" }],
["Page.bringToFront", { title: "Bring to front" }],
["Page.pickLocator", { title: "Pick locator", group: "configuration" }],
["Page.cancelPickLocator", { title: "Cancel pick locator", group: "configuration" }],
["Page.hideHighlight", { title: "Hide all element highlights", group: "configuration" }],
["Page.screencastShowOverlay", { title: "Show overlay", group: "configuration" }],
["Page.screencastRemoveOverlay", { title: "Remove overlay", group: "configuration" }],
["Page.screencastChapter", { title: "Show chapter overlay", group: "configuration" }],
["Page.screencastSetOverlayVisible", { title: "Set overlay visibility", group: "configuration" }],
["Page.screencastShowActions", { title: "Show actions", group: "configuration" }],
["Page.screencastHideActions", { title: "Remove actions", group: "configuration" }],
["Page.screencastStart", { title: "Start screencast", group: "configuration" }],
["Page.screencastStop", { title: "Stop screencast", group: "configuration" }],
["Page.updateSubscription", { internal: true }],
["Page.setDockTile", { internal: true }],
["Root.initialize", { internal: true }],
["Playwright.newRequest", { title: "Create request context" }],
["DebugController.initialize", { internal: true }],
["DebugController.setReportStateChanged", { internal: true }],
["DebugController.setRecorderMode", { internal: true }],
["DebugController.highlight", { internal: true }],
["DebugController.hideHighlight", { internal: true }],
["DebugController.resume", { internal: true }],
["DebugController.kill", { internal: true }],
["SocksSupport.socksConnected", { internal: true }],
["SocksSupport.socksFailed", { internal: true }],
["SocksSupport.socksData", { internal: true }],
["SocksSupport.socksError", { internal: true }],
["SocksSupport.socksEnd", { internal: true }],
["JsonPipe.send", { internal: true }],
["JsonPipe.close", { internal: true }],
["CDPSession.send", { title: "Send CDP command", group: "configuration" }],
["CDPSession.detach", { title: "Detach CDP session", group: "configuration" }],
["BindingCall.reject", { internal: true }],
["BindingCall.resolve", { internal: true }],
["Debugger.requestPause", { title: "Pause on next call", group: "configuration" }],
["Debugger.resume", { title: "Resume", group: "configuration" }],
["Debugger.next", { title: "Step to next call", group: "configuration" }],
["Debugger.runTo", { title: "Run to location", group: "configuration" }],
["Dialog.accept", { title: "Accept dialog" }],
["Dialog.dismiss", { title: "Dismiss dialog" }],
["Tracing.tracingStart", { title: "Start tracing", group: "configuration" }],
["Tracing.tracingStartChunk", { title: "Start tracing", group: "configuration" }],
["Tracing.tracingGroup", { title: 'Trace "{name}"' }],
["Tracing.tracingGroupEnd", { title: "Group end" }],
["Tracing.tracingStopChunk", { title: "Stop tracing", group: "configuration" }],
["Tracing.tracingStop", { title: "Stop tracing", group: "configuration" }],
["Tracing.harStart", { internal: true }],
["Tracing.harExport", { internal: true }],
["Worker.disconnect", { title: "Disconnect from worker" }],
["Worker.evaluateExpression", { title: "Evaluate" }],
["Worker.evaluateExpressionHandle", { title: "Evaluate" }],
["Worker.updateSubscription", { internal: true }]
]);
}
});
// packages/isomorphic/protocolFormatter.ts
function formatProtocolParam(params2, alternatives) {
return _formatProtocolParam(params2, alternatives)?.replaceAll("\n", "\\n");
}
function _formatProtocolParam(params2, alternatives) {
if (!params2)
return void 0;
for (const name of alternatives.split("|")) {
if (name === "url") {
try {
const urlObject = new URL(params2[name]);
if (urlObject.protocol === "data:")
return urlObject.protocol;
if (["about:", "chrome:", "edge:"].includes(urlObject.protocol))
return params2[name];
return urlObject.pathname + urlObject.search;
} catch (error) {
if (params2[name] !== void 0)
return params2[name];
}
}
if (name === "timeNumber" && params2[name] !== void 0) {
return new Date(params2[name]).toString();
}
const value2 = deepParam(params2, name);
if (value2 !== void 0)
return value2;
}
}
function deepParam(params2, name) {
const tokens = name.split(".");
let current = params2;
for (const token of tokens) {
if (typeof current !== "object" || current === null)
return void 0;
current = current[token];
}
if (current === void 0)
return void 0;
return String(current);
}
function renderTitleForCall(metadata) {
const titleFormat = metadata.title ?? getMetainfo(metadata)?.title ?? metadata.method;
return titleFormat.replace(/\{([^}]+)\}/g, (fullMatch, p1) => {
return formatProtocolParam(metadata.params, p1) ?? fullMatch;
});
}
function getActionGroup(metadata) {
return getMetainfo(metadata)?.group;
}
var init_protocolFormatter = __esm({
"packages/isomorphic/protocolFormatter.ts"() {
"use strict";
init_protocolMetainfo();
}
});
// packages/isomorphic/semaphore.ts
var Semaphore;
var init_semaphore = __esm({
"packages/isomorphic/semaphore.ts"() {
"use strict";
init_manualPromise();
Semaphore = class {
constructor(max) {
this._acquired = 0;
this._queue = [];
this._max = max;
}
setMax(max) {
this._max = max;
}
acquire() {
const lock2 = new ManualPromise();
this._queue.push(lock2);
this._flush();
return lock2;
}
release() {
--this._acquired;
this._flush();
}
_flush() {
while (this._acquired < this._max && this._queue.length) {
++this._acquired;
this._queue.shift().resolve();
}
}
};
}
});
// packages/isomorphic/formatUtils.ts
function msToString(ms) {
if (ms < 0 || !isFinite(ms))
return "-";
if (ms === 0)
return "0ms";
if (ms < 1e3)
return ms.toFixed(0) + "ms";
const seconds = ms / 1e3;
if (seconds < 60)
return seconds.toFixed(1) + "s";
const minutes = seconds / 60;
if (minutes < 60)
return minutes.toFixed(1) + "m";
const hours = minutes / 60;
if (hours < 24)
return hours.toFixed(1) + "h";
const days = hours / 24;
return days.toFixed(1) + "d";
}
function bytesToString(bytes) {
if (bytes < 0 || !isFinite(bytes))
return "-";
if (bytes === 0)
return "0";
if (bytes < 1e3)
return bytes.toFixed(0);
const kb = bytes / 1024;
if (kb < 1e3)
return kb.toFixed(1) + "K";
const mb = kb / 1024;
if (mb < 1e3)
return mb.toFixed(1) + "M";
const gb = mb / 1024;
return gb.toFixed(1) + "G";
}
var init_formatUtils = __esm({
"packages/isomorphic/formatUtils.ts"() {
"use strict";
}
});
// packages/isomorphic/time.ts
function setTimeOrigin(origin) {
_timeOrigin = origin;
_timeShift = performance.timeOrigin - origin;
}
function timeOrigin() {
return _timeOrigin;
}
function monotonicTime() {
return Math.floor((performance.now() + _timeShift) * 1e3) / 1e3;
}
var _timeOrigin, _timeShift, DEFAULT_PLAYWRIGHT_TIMEOUT, DEFAULT_PLAYWRIGHT_LAUNCH_TIMEOUT;
var init_time = __esm({
"packages/isomorphic/time.ts"() {
"use strict";
_timeOrigin = performance.timeOrigin;
_timeShift = 0;
DEFAULT_PLAYWRIGHT_TIMEOUT = 3e4;
DEFAULT_PLAYWRIGHT_LAUNCH_TIMEOUT = 3 * 60 * 1e3;
}
});
// packages/isomorphic/timeoutRunner.ts
async function raceAgainstDeadline(cb, deadline) {
let timer;
return await Promise.race([
cb().then((result2) => {
return { result: result2, timedOut: false };
}),
new Promise((resolve) => {
if (!deadline)
return;
timer = setTimeout(() => resolve({ timedOut: true }), deadline - monotonicTime());
})
]).finally(() => {
clearTimeout(timer);
});
}
async function pollAgainstDeadline(callback, deadline, pollIntervals = [100, 250, 500, 1e3]) {
const lastPollInterval = pollIntervals.pop() ?? 1e3;
let lastResult;
const wrappedCallback = () => Promise.resolve().then(callback);
while (true) {
const time = monotonicTime();
if (deadline && time >= deadline)
break;
const received = await raceAgainstDeadline(wrappedCallback, deadline);
if (received.timedOut)
break;
lastResult = received.result.result;
if (!received.result.continuePolling)
return { result: lastResult, timedOut: false };
const interval = pollIntervals.shift() ?? lastPollInterval;
if (deadline && deadline <= monotonicTime() + interval)
break;
await new Promise((x) => setTimeout(x, interval));
}
return { timedOut: true, result: lastResult };
}
var init_timeoutRunner = __esm({
"packages/isomorphic/timeoutRunner.ts"() {
"use strict";
init_time();
}
});
// packages/isomorphic/trace/snapshotServer.ts
function removeHash(url2) {
try {
const u = new URL(url2);
u.hash = "";
return u.toString();
} catch (e) {
return url2;
}
}
var SnapshotServer;
var init_snapshotServer = __esm({
"packages/isomorphic/trace/snapshotServer.ts"() {
"use strict";
SnapshotServer = class {
constructor(snapshotStorage, resourceLoader) {
this._snapshotIds = /* @__PURE__ */ new Map();
this._snapshotStorage = snapshotStorage;
this._resourceLoader = resourceLoader;
}
serveSnapshot(pageOrFrameId, searchParams, snapshotUrl) {
const snapshot3 = this._snapshot(pageOrFrameId, searchParams);
if (!snapshot3)
return new Response(null, { status: 404 });
const renderedSnapshot = snapshot3.render();
this._snapshotIds.set(snapshotUrl, snapshot3);
return new Response(renderedSnapshot.html, { status: 200, headers: { "Content-Type": "text/html; charset=utf-8" } });
}
async serveClosestScreenshot(pageOrFrameId, searchParams) {
const snapshot3 = this._snapshot(pageOrFrameId, searchParams);
const sha1 = snapshot3?.closestScreenshot();
if (!sha1)
return new Response(null, { status: 404 });
return new Response(await this._resourceLoader(sha1));
}
serveSnapshotInfo(pageOrFrameId, searchParams) {
const snapshot3 = this._snapshot(pageOrFrameId, searchParams);
return this._respondWithJson(snapshot3 ? {
viewport: snapshot3.viewport(),
url: snapshot3.snapshot().frameUrl,
timestamp: snapshot3.snapshot().timestamp,
wallTime: snapshot3.snapshot().wallTime
} : {
error: "No snapshot found"
});
}
_snapshot(pageOrFrameId, params2) {
const name = params2.get("name");
return this._snapshotStorage.snapshotByName(pageOrFrameId, name);
}
_respondWithJson(object) {
return new Response(JSON.stringify(object), {
status: 200,
headers: {
"Cache-Control": "public, max-age=31536000",
"Content-Type": "application/json"
}
});
}
async serveResource(requestUrlAlternatives, method, snapshotUrl) {
let resource;
const snapshot3 = this._snapshotIds.get(snapshotUrl);
for (const requestUrl of requestUrlAlternatives) {
resource = snapshot3?.resourceByUrl(removeHash(requestUrl), method);
if (resource)
break;
}
if (!resource)
return new Response(null, { status: 404 });
const sha1 = resource.response.content._sha1;
const content = sha1 ? await this._resourceLoader(sha1) || new Blob([]) : new Blob([]);
let contentType = resource.response.content.mimeType;
const isTextEncoding = /^text\/|^application\/(javascript|json)/.test(contentType);
if (isTextEncoding && !contentType.includes("charset"))
contentType = `${contentType}; charset=utf-8`;
const headers = new Headers();
if (contentType !== "x-unknown")
headers.set("Content-Type", contentType);
for (const { name, value: value2 } of resource.response.headers)
headers.set(name, value2);
headers.delete("Content-Encoding");
headers.delete("Access-Control-Allow-Origin");
headers.set("Access-Control-Allow-Origin", "*");
headers.delete("Content-Length");
headers.set("Content-Length", String(content.size));
if (this._snapshotStorage.hasResourceOverride(resource.request.url))
headers.set("Cache-Control", "no-store, no-cache, max-age=0");
else
headers.set("Cache-Control", "public, max-age=31536000");
const { status } = resource.response;
const isNullBodyStatus = status === 101 || status === 204 || status === 205 || status === 304;
return new Response(isNullBodyStatus ? null : content, {
headers,
status: resource.response.status,
statusText: resource.response.statusText
});
}
};
}
});
// packages/isomorphic/urlMatch.ts
function isHttpUrl(url2, base) {
try {
return ["http:", "https:"].includes(new URL(url2, base).protocol);
} catch {
return false;
}
}
function globToRegexPattern(glob) {
const tokens = ["^"];
let inGroup = false;
for (let i = 0; i < glob.length; ++i) {
const c = glob[i];
if (c === "\\" && i + 1 < glob.length) {
const char = glob[++i];
tokens.push(escapedChars.has(char) ? "\\" + char : char);
continue;
}
if (c === "*") {
const charBefore = glob[i - 1];
let starCount = 1;
while (glob[i + 1] === "*") {
starCount++;
i++;
}
if (starCount > 1) {
const charAfter = glob[i + 1];
if (charAfter === "/") {
if (charBefore === "/")
tokens.push("((.+/)|)");
else
tokens.push("(.*/)");
++i;
} else {
tokens.push("(.*)");
}
} else {
tokens.push("([^/]*)");
}
continue;
}
switch (c) {
case "{":
if (inGroup)
throw new Error(`Invalid glob pattern ${JSON.stringify(glob)}: nested '{' is not supported`);
inGroup = true;
tokens.push("(");
break;
case "}":
if (!inGroup)
throw new Error(`Invalid glob pattern ${JSON.stringify(glob)}: unmatched '}'`);
inGroup = false;
tokens.push(")");
break;
case ",":
if (inGroup) {
tokens.push("|");
break;
}
tokens.push("\\" + c);
break;
default:
tokens.push(escapedChars.has(c) ? "\\" + c : c);
}
}
if (inGroup)
throw new Error(`Invalid glob pattern ${JSON.stringify(glob)}: unmatched '{'`);
tokens.push("$");
return tokens.join("");
}
function isRegExp3(obj) {
return obj instanceof RegExp || Object.prototype.toString.call(obj) === "[object RegExp]";
}
function serializeURLPattern(v) {
return {
hash: v.hash,
hostname: v.hostname,
password: v.password,
pathname: v.pathname,
port: v.port,
protocol: v.protocol,
search: v.search,
username: v.username
};
}
function serializeURLMatch(match) {
if (isString(match))
return { glob: match };
if (isRegExp3(match))
return { regexSource: match.source, regexFlags: match.flags };
if (isURLPattern(match))
return { urlPattern: serializeURLPattern(match) };
return void 0;
}
function deserializeURLPattern(v) {
if (typeof globalThis.URLPattern !== "function")
return () => true;
return new globalThis.URLPattern({
hash: v.hash,
hostname: v.hostname,
password: v.password,
pathname: v.pathname,
port: v.port,
protocol: v.protocol,
search: v.search,
username: v.username
});
}
function deserializeURLMatch(match) {
if (match.regexSource)
return new RegExp(match.regexSource, match.regexFlags);
if (match.urlPattern)
return deserializeURLPattern(match.urlPattern);
return match.glob;
}
function urlMatchesEqual(match1, match2) {
if (isRegExp3(match1) && isRegExp3(match2))
return match1.source === match2.source && match1.flags === match2.flags;
return match1 === match2;
}
function urlMatches(baseURL, urlString, match, webSocketUrl) {
if (match === void 0 || match === "")
return true;
if (isString(match))
match = new RegExp(resolveGlobToRegexPattern(baseURL, match, webSocketUrl));
if (isRegExp3(match)) {
const r = match.test(urlString);
return r;
}
const url2 = parseURL(urlString);
if (!url2)
return false;
if (isURLPattern(match))
return match.test(url2.href);
if (typeof match !== "function")
throw new Error("url parameter should be string, RegExp, URLPattern or function");
return match(url2);
}
function resolveGlobToRegexPattern(baseURL, glob, webSocketUrl) {
if (webSocketUrl)
baseURL = toWebSocketBaseUrl(baseURL);
glob = resolveGlobBase(baseURL, glob);
return globToRegexPattern(glob);
}
function toWebSocketBaseUrl(baseURL) {
if (baseURL && /^https?:\/\//.test(baseURL))
baseURL = baseURL.replace(/^http/, "ws");
return baseURL;
}
function resolveGlobBase(baseURL, match) {
if (!match.startsWith("*")) {
let mapToken2 = function(original, replacement) {
if (original.length === 0)
return "";
tokenMap.set(replacement, original);
return replacement;
};
var mapToken = mapToken2;
const tokenMap = /* @__PURE__ */ new Map();
match = match.replaceAll(/\\\\\?/g, "?");
if (match.startsWith("about:") || match.startsWith("data:") || match.startsWith("chrome:") || match.startsWith("edge:") || match.startsWith("file:"))
return match;
const relativePath = match.split("/").map((token, index) => {
if (token === "." || token === ".." || token === "")
return token;
if (index === 0 && token.endsWith(":")) {
if (token.indexOf("*") !== -1 || token.indexOf("{") !== -1)
return mapToken2(token, "http:");
return token;
}
const questionIndex = token.indexOf("?");
if (questionIndex === -1)
return mapToken2(token, `$_${index}_$`);
const newPrefix = mapToken2(token.substring(0, questionIndex), `$_${index}_$`);
const newSuffix = mapToken2(token.substring(questionIndex), `?$_${index}_$`);
return newPrefix + newSuffix;
}).join("/");
const result2 = resolveBaseURL(baseURL, relativePath);
let resolved = result2.resolved;
for (const [token, original] of tokenMap) {
const normalize = result2.caseInsensitivePart?.includes(token);
resolved = resolved.replace(token, normalize ? original.toLowerCase() : original);
}
match = resolved;
}
return match;
}
function parseURL(url2) {
try {
return new URL(url2);
} catch (e) {
return null;
}
}
function constructURLBasedOnBaseURL(baseURL, givenURL) {
try {
return resolveBaseURL(baseURL, givenURL).resolved;
} catch (e) {
return givenURL;
}
}
function resolveBaseURL(baseURL, givenURL) {
try {
const url2 = new URL(givenURL, baseURL);
const resolved = url2.toString();
const caseInsensitivePrefix = url2.origin;
return { resolved, caseInsensitivePart: caseInsensitivePrefix };
} catch (e) {
return { resolved: givenURL };
}
}
var escapedChars, isURLPattern;
var init_urlMatch = __esm({
"packages/isomorphic/urlMatch.ts"() {
"use strict";
init_stringUtils();
escapedChars = /* @__PURE__ */ new Set(["$", "^", "+", ".", "*", "(", ")", "|", "\\", "?", "{", "}", "[", "]"]);
isURLPattern = (v) => typeof globalThis.URLPattern === "function" && v instanceof globalThis.URLPattern;
}
});
// packages/isomorphic/locatorParser.ts
function parseLocator(locator2, testIdAttributeName2) {
locator2 = locator2.replace(/AriaRole\s*\.\s*([\w]+)/g, (_, group) => group.toLowerCase()).replace(/(get_by_role|getByRole)\s*\(\s*(?:["'`])([^'"`]+)['"`]/g, (_, group1, group2) => `${group1}(${group2.toLowerCase()}`);
const params2 = [];
let template = "";
for (let i = 0; i < locator2.length; ++i) {
const quote5 = locator2[i];
if (quote5 !== '"' && quote5 !== "'" && quote5 !== "`" && quote5 !== "/") {
template += quote5;
continue;
}
const isRegexEscaping = locator2[i - 1] === "r" || locator2[i] === "/";
++i;
let text2 = "";
while (i < locator2.length) {
if (locator2[i] === "\\") {
if (isRegexEscaping) {
if (locator2[i + 1] !== quote5)
text2 += locator2[i];
++i;
text2 += locator2[i];
} else {
++i;
if (locator2[i] === "n")
text2 += "\n";
else if (locator2[i] === "r")
text2 += "\r";
else if (locator2[i] === "t")
text2 += " ";
else
text2 += locator2[i];
}
++i;
continue;
}
if (locator2[i] !== quote5) {
text2 += locator2[i++];
continue;
}
break;
}
params2.push({ quote: quote5, text: text2 });
template += (quote5 === "/" ? "r" : "") + "$" + params2.length;
}
template = template.toLowerCase().replace(/get_by_alt_text/g, "getbyalttext").replace(/get_by_test_id/g, "getbytestid").replace(/get_by_([\w]+)/g, "getby$1").replace(/has_not_text/g, "hasnottext").replace(/has_text/g, "hastext").replace(/has_not/g, "hasnot").replace(/frame_locator/g, "framelocator").replace(/content_frame/g, "contentframe").replace(/[{}\s]/g, "").replace(/new\(\)/g, "").replace(/new[\w]+\.[\w]+options\(\)/g, "").replace(/\.set/g, ",set").replace(/\.or_\(/g, "or(").replace(/\.and_\(/g, "and(").replace(/:/g, "=").replace(/,re\.ignorecase/g, "i").replace(/,pattern.case_insensitive/g, "i").replace(/,regexoptions.ignorecase/g, "i").replace(/re.compile\(([^)]+)\)/g, "$1").replace(/pattern.compile\(([^)]+)\)/g, "r$1").replace(/newregex\(([^)]+)\)/g, "r$1").replace(/string=/g, "=").replace(/regex=/g, "=").replace(/,,/g, ",").replace(/,\)/g, ")");
const preferredQuote = params2.map((p) => p.quote).filter((quote5) => "'\"`".includes(quote5))[0];
return { selector: transform(template, params2, testIdAttributeName2), preferredQuote };
}
function countParams(template) {
return [...template.matchAll(/\$\d+/g)].length;
}
function shiftParams(template, sub) {
return template.replace(/\$(\d+)/g, (_, ordinal) => `$${ordinal - sub}`);
}
function transform(template, params2, testIdAttributeName2) {
while (true) {
const hasMatch = template.match(/filter\(,?(has=|hasnot=|sethas\(|sethasnot\()/);
if (!hasMatch)
break;
const start3 = hasMatch.index + hasMatch[0].length;
let balance = 0;
let end = start3;
for (; end < template.length; end++) {
if (template[end] === "(")
balance++;
else if (template[end] === ")")
balance--;
if (balance < 0)
break;
}
let prefix = template.substring(0, start3);
let extraSymbol = 0;
if (["sethas(", "sethasnot("].includes(hasMatch[1])) {
extraSymbol = 1;
prefix = prefix.replace(/sethas\($/, "has=").replace(/sethasnot\($/, "hasnot=");
}
const paramsCountBeforeHas = countParams(template.substring(0, start3));
const hasTemplate = shiftParams(template.substring(start3, end), paramsCountBeforeHas);
const paramsCountInHas = countParams(hasTemplate);
const hasParams = params2.slice(paramsCountBeforeHas, paramsCountBeforeHas + paramsCountInHas);
const hasSelector = JSON.stringify(transform(hasTemplate, hasParams, testIdAttributeName2));
template = prefix.replace(/=$/, "2=") + `$${paramsCountBeforeHas + 1}` + shiftParams(template.substring(end + extraSymbol), paramsCountInHas - 1);
const paramsBeforeHas = params2.slice(0, paramsCountBeforeHas);
const paramsAfterHas = params2.slice(paramsCountBeforeHas + paramsCountInHas);
params2 = paramsBeforeHas.concat([{ quote: '"', text: hasSelector }]).concat(paramsAfterHas);
}
template = template.replace(/\,set([\w]+)\(([^)]+)\)/g, (_, group1, group2) => "," + group1.toLowerCase() + "=" + group2.toLowerCase()).replace(/framelocator\(([^)]+)\)/g, "$1.internal:control=enter-frame").replace(/contentframe(\(\))?/g, "internal:control=enter-frame").replace(/locator\(([^)]+),hastext=([^),]+)\)/g, "locator($1).internal:has-text=$2").replace(/locator\(([^)]+),hasnottext=([^),]+)\)/g, "locator($1).internal:has-not-text=$2").replace(/locator\(([^)]+),hastext=([^),]+)\)/g, "locator($1).internal:has-text=$2").replace(/locator\(([^)]+)\)/g, "$1").replace(/getbyrole\(([^)]+)\)/g, "internal:role=$1").replace(/getbytext\(([^)]+)\)/g, "internal:text=$1").replace(/getbylabel\(([^)]+)\)/g, "internal:label=$1").replace(/getbytestid\(([^)]+)\)/g, `internal:testid=[${testIdAttributeName2}=$1]`).replace(/getby(placeholder|alt|title)(?:text)?\(([^)]+)\)/g, "internal:attr=[$1=$2]").replace(/first(\(\))?/g, "nth=0").replace(/last(\(\))?/g, "nth=-1").replace(/nth\(([^)]+)\)/g, "nth=$1").replace(/filter\(,?visible=true\)/g, "visible=true").replace(/filter\(,?visible=false\)/g, "visible=false").replace(/filter\(,?hastext=([^)]+)\)/g, "internal:has-text=$1").replace(/filter\(,?hasnottext=([^)]+)\)/g, "internal:has-not-text=$1").replace(/filter\(,?has2=([^)]+)\)/g, "internal:has=$1").replace(/filter\(,?hasnot2=([^)]+)\)/g, "internal:has-not=$1").replace(/,exact=false/g, "").replace(/(,name=\$\d+)(,description=\$\d+),exact=true/g, "$1s$2s").replace(/,exact=true/g, "s").replace(/,includehidden=/g, ",include-hidden=").replace(/\,/g, "][");
const parts = template.split(".");
for (let index = 0; index < parts.length - 1; index++) {
if (parts[index] === "internal:control=enter-frame" && parts[index + 1].startsWith("nth=")) {
const [nth] = parts.splice(index, 1);
parts.splice(index + 1, 0, nth);
}
}
return parts.map((t) => {
if (!t.startsWith("internal:") || t === "internal:control")
return t.replace(/\$(\d+)/g, (_, ordinal) => {
const param = params2[+ordinal - 1];
return param.text;
});
t = t.includes("[") ? t.replace(/\]/, "") + "]" : t;
t = t.replace(/(?:r)\$(\d+)(i)?/g, (_, ordinal, suffix) => {
const param = params2[+ordinal - 1];
if (t.startsWith("internal:attr") || t.startsWith("internal:testid") || t.startsWith("internal:role"))
return escapeForAttributeSelector(new RegExp(param.text), false) + (suffix || "");
return escapeForTextSelector(new RegExp(param.text, suffix), false);
}).replace(/\$(\d+)(i|s)?/g, (_, ordinal, suffix) => {
const param = params2[+ordinal - 1];
if (t.startsWith("internal:has=") || t.startsWith("internal:has-not="))
return param.text;
if (t.startsWith("internal:testid"))
return escapeForAttributeSelector(param.text, true);
if (t.startsWith("internal:attr") || t.startsWith("internal:role"))
return escapeForAttributeSelector(param.text, suffix === "s");
return escapeForTextSelector(param.text, suffix === "s");
});
return t;
}).join(" >> ");
}
function locatorOrSelectorAsSelector(language, locator2, testIdAttributeName2) {
try {
return unsafeLocatorOrSelectorAsSelector(language, locator2, testIdAttributeName2);
} catch (e) {
return "";
}
}
function unsafeLocatorOrSelectorAsSelector(language, locator2, testIdAttributeName2) {
try {
parseSelector(locator2);
return locator2;
} catch (e) {
}
const { selector, preferredQuote } = parseLocator(locator2, testIdAttributeName2);
const locators = asLocators(language, selector, void 0, void 0, preferredQuote);
const digest = digestForComparison(language, locator2);
if (locators.some((candidate) => digestForComparison(language, candidate) === digest))
return selector;
return "";
}
function digestForComparison(language, locator2) {
locator2 = locator2.replace(/\s/g, "");
if (language === "javascript")
locator2 = locator2.replace(/\\?["`]/g, "'").replace(/,{}/g, "");
return locator2;
}
var init_locatorParser = __esm({
"packages/isomorphic/locatorParser.ts"() {
"use strict";
init_locatorGenerators();
init_selectorParser();
init_stringUtils();
}
});
// packages/isomorphic/trace/snapshotRenderer.ts
function findClosest(items, metric, target) {
return items.find((item, index) => {
if (index === items.length - 1)
return true;
const next = items[index + 1];
return Math.abs(metric(item) - target) < Math.abs(metric(next) - target);
});
}
function isNodeNameAttributesChildNodesSnapshot(n) {
return Array.isArray(n) && typeof n[0] === "string";
}
function isSubtreeReferenceSnapshot(n) {
return Array.isArray(n) && Array.isArray(n[0]);
}
function snapshotNodes(snapshot3) {
if (!snapshot3._nodes) {
const nodes = [];
const visit = (n) => {
if (typeof n === "string") {
nodes.push(n);
} else if (isNodeNameAttributesChildNodesSnapshot(n)) {
const [, , ...children] = n;
for (const child of children)
visit(child);
nodes.push(n);
}
};
visit(snapshot3.html);
snapshot3._nodes = nodes;
}
return snapshot3._nodes;
}
function snapshotScript(viewport, ...targetIds) {
function applyPlaywrightAttributes(blankSnapshotUrl2, viewport2, ...targetIds2) {
const win = window;
const searchParams = new URLSearchParams(win.location.search);
const shouldPopulateCanvasFromScreenshot = searchParams.has("shouldPopulateCanvasFromScreenshot");
const isUnderTest2 = searchParams.has("isUnderTest");
const frameBoundingRectsInfo = {
viewport: viewport2,
frames: /* @__PURE__ */ new WeakMap()
};
win["__playwright_frame_bounding_rects__"] = frameBoundingRectsInfo;
const kPointerWarningTitle = "Recorded click position in absolute coordinates did not match the center of the clicked element. This is either due to the use of provided offset, or due to a difference between the test runner and the trace viewer operating systems.";
const scrollTops = [];
const scrollLefts = [];
const targetElements = [];
const canvasElements = [];
let topSnapshotWindow = win;
while (topSnapshotWindow !== topSnapshotWindow.parent && !topSnapshotWindow.location.pathname.match(/\/page@[a-z0-9]+$/))
topSnapshotWindow = topSnapshotWindow.parent;
const visit = (root) => {
for (const e of root.querySelectorAll(`[__playwright_scroll_top_]`))
scrollTops.push(e);
for (const e of root.querySelectorAll(`[__playwright_scroll_left_]`))
scrollLefts.push(e);
for (const element2 of root.querySelectorAll(`[__playwright_value_]`)) {
const inputElement = element2;
if (inputElement.type !== "file")
inputElement.value = inputElement.getAttribute("__playwright_value_");
element2.removeAttribute("__playwright_value_");
}
for (const element2 of root.querySelectorAll(`[__playwright_checked_]`)) {
element2.checked = element2.getAttribute("__playwright_checked_") === "true";
element2.removeAttribute("__playwright_checked_");
}
for (const element2 of root.querySelectorAll(`[__playwright_selected_]`)) {
element2.selected = element2.getAttribute("__playwright_selected_") === "true";
element2.removeAttribute("__playwright_selected_");
}
for (const element2 of root.querySelectorAll(`[__playwright_popover_open_]`)) {
try {
element2.showPopover();
} catch {
}
element2.removeAttribute("__playwright_popover_open_");
}
for (const element2 of root.querySelectorAll(`[__playwright_dialog_open_]`)) {
try {
if (element2.getAttribute("__playwright_dialog_open_") === "modal")
element2.showModal();
else
element2.show();
} catch {
}
element2.removeAttribute("__playwright_dialog_open_");
}
for (const targetId of targetIds2) {
for (const target of root.querySelectorAll(`[__playwright_target__="${targetId}"]`)) {
const style = target.style;
style.outline = "2px solid #006ab1";
style.backgroundColor = "#6fa8dc7f";
targetElements.push(target);
}
}
for (const iframe of root.querySelectorAll("iframe, frame")) {
const boundingRectJson = iframe.getAttribute("__playwright_bounding_rect__");
iframe.removeAttribute("__playwright_bounding_rect__");
const boundingRect = boundingRectJson ? JSON.parse(boundingRectJson) : void 0;
if (boundingRect)
frameBoundingRectsInfo.frames.set(iframe, { boundingRect, scrollLeft: 0, scrollTop: 0 });
const src = iframe.getAttribute("__playwright_src__");
if (!src) {
iframe.setAttribute("src", blankSnapshotUrl2);
} else {
const url2 = new URL(win.location.href);
const index = url2.pathname.lastIndexOf("/snapshot/");
if (index !== -1)
url2.pathname = url2.pathname.substring(0, index + 1);
url2.pathname += src.substring(1);
iframe.setAttribute("src", url2.toString());
}
}
{
const body = root.querySelector(`body[__playwright_custom_elements__]`);
if (body && win.customElements) {
const customElements = (body.getAttribute("__playwright_custom_elements__") || "").split(",");
for (const elementName of customElements)
win.customElements.define(elementName, class extends HTMLElement {
});
}
}
for (const element2 of root.querySelectorAll(`template[__playwright_shadow_root_]`)) {
const template = element2;
const shadowRoot = template.parentElement.attachShadow({ mode: "open" });
shadowRoot.appendChild(template.content);
template.remove();
visit(shadowRoot);
}
for (const element2 of root.querySelectorAll("a"))
element2.addEventListener("click", (event) => {
event.preventDefault();
});
if ("adoptedStyleSheets" in root) {
const adoptedSheets = [...root.adoptedStyleSheets];
for (const element2 of root.querySelectorAll(`template[__playwright_style_sheet_]`)) {
const template = element2;
const sheet = new CSSStyleSheet();
sheet.replaceSync(template.getAttribute("__playwright_style_sheet_"));
adoptedSheets.push(sheet);
}
root.adoptedStyleSheets = adoptedSheets;
}
canvasElements.push(...root.querySelectorAll("canvas"));
};
const onLoad = () => {
win.removeEventListener("load", onLoad);
for (const element2 of scrollTops) {
element2.scrollTop = +element2.getAttribute("__playwright_scroll_top_");
element2.removeAttribute("__playwright_scroll_top_");
if (frameBoundingRectsInfo.frames.has(element2))
frameBoundingRectsInfo.frames.get(element2).scrollTop = element2.scrollTop;
}
for (const element2 of scrollLefts) {
element2.scrollLeft = +element2.getAttribute("__playwright_scroll_left_");
element2.removeAttribute("__playwright_scroll_left_");
if (frameBoundingRectsInfo.frames.has(element2))
frameBoundingRectsInfo.frames.get(element2).scrollLeft = element2.scrollLeft;
}
win.document.styleSheets[0].disabled = true;
const search = new URL(win.location.href).searchParams;
const isTopFrame = win === topSnapshotWindow;
if (isTopFrame && search.get("pointX") && search.get("pointY")) {
const pointX = +search.get("pointX");
const pointY = +search.get("pointY");
const pointElement = win.document.createElement("x-pw-pointer");
pointElement.style.position = "fixed";
pointElement.style.backgroundColor = "#f44336";
pointElement.style.width = "20px";
pointElement.style.height = "20px";
pointElement.style.borderRadius = "10px";
pointElement.style.margin = "-10px 0 0 -10px";
pointElement.style.zIndex = "2147483646";
pointElement.style.display = "flex";
pointElement.style.alignItems = "center";
pointElement.style.justifyContent = "center";
const target = targetElements[0];
const targetBox = target?.getBoundingClientRect();
const targetCenter = target ? { x: targetBox.left + targetBox.width / 2, y: targetBox.top + targetBox.height / 2 } : null;
pointElement.style.left = (targetCenter?.x ?? pointX) + "px";
pointElement.style.top = (targetCenter?.y ?? pointY) + "px";
const isAligned = !targetCenter || Math.abs(targetCenter.x - pointX) <= 10 && Math.abs(targetCenter.y - pointY) <= 10;
if (!isAligned) {
const warningElement = win.document.createElement("x-pw-pointer-warning");
warningElement.textContent = "\u26A0";
warningElement.style.fontSize = "19px";
warningElement.style.color = "white";
warningElement.style.marginTop = "-3.5px";
warningElement.style.userSelect = "none";
pointElement.appendChild(warningElement);
pointElement.setAttribute("title", kPointerWarningTitle);
}
win.document.documentElement.appendChild(pointElement);
}
if (canvasElements.length > 0) {
let drawCheckerboard2 = function(context2, canvas) {
function createCheckerboardPattern() {
const pattern = win.document.createElement("canvas");
pattern.width = pattern.width / Math.floor(pattern.width / 24);
pattern.height = pattern.height / Math.floor(pattern.height / 24);
const context3 = pattern.getContext("2d");
context3.fillStyle = "lightgray";
context3.fillRect(0, 0, pattern.width, pattern.height);
context3.fillStyle = "white";
context3.fillRect(0, 0, pattern.width / 2, pattern.height / 2);
context3.fillRect(pattern.width / 2, pattern.height / 2, pattern.width, pattern.height);
return context3.createPattern(pattern, "repeat");
}
context2.fillStyle = createCheckerboardPattern();
context2.fillRect(0, 0, canvas.width, canvas.height);
};
var drawCheckerboard = drawCheckerboard2;
const img = new Image();
img.onload = () => {
for (const canvas of canvasElements) {
const context2 = canvas.getContext("2d");
const boundingRectAttribute = canvas.getAttribute("__playwright_bounding_rect__");
canvas.removeAttribute("__playwright_bounding_rect__");
if (!boundingRectAttribute)
continue;
let boundingRect;
try {
boundingRect = JSON.parse(boundingRectAttribute);
} catch (e) {
continue;
}
let currWindow = win;
while (currWindow !== topSnapshotWindow) {
const iframe = currWindow.frameElement;
currWindow = currWindow.parent;
const iframeInfo = currWindow["__playwright_frame_bounding_rects__"]?.frames.get(iframe);
if (!iframeInfo?.boundingRect)
break;
const leftOffset = iframeInfo.boundingRect.left - iframeInfo.scrollLeft;
const topOffset = iframeInfo.boundingRect.top - iframeInfo.scrollTop;
boundingRect.left += leftOffset;
boundingRect.top += topOffset;
boundingRect.right += leftOffset;
boundingRect.bottom += topOffset;
}
const { width, height } = topSnapshotWindow["__playwright_frame_bounding_rects__"].viewport;
boundingRect.left = boundingRect.left / width;
boundingRect.top = boundingRect.top / height;
boundingRect.right = boundingRect.right / width;
boundingRect.bottom = boundingRect.bottom / height;
const partiallyUncaptured = boundingRect.right > 1 || boundingRect.bottom > 1;
const fullyUncaptured = boundingRect.left > 1 || boundingRect.top > 1;
if (fullyUncaptured) {
canvas.title = `Playwright couldn't capture canvas contents because it's located outside the viewport.`;
continue;
}
drawCheckerboard2(context2, canvas);
if (shouldPopulateCanvasFromScreenshot) {
context2.drawImage(img, boundingRect.left * img.width, boundingRect.top * img.height, (boundingRect.right - boundingRect.left) * img.width, (boundingRect.bottom - boundingRect.top) * img.height, 0, 0, canvas.width, canvas.height);
if (partiallyUncaptured)
canvas.title = `Playwright couldn't capture full canvas contents because it's located partially outside the viewport.`;
else
canvas.title = `Canvas contents are displayed on a best-effort basis based on viewport screenshots taken during test execution.`;
} else {
canvas.title = "Canvas content display is disabled.";
}
if (isUnderTest2)
console.log(`canvas drawn:`, JSON.stringify([boundingRect.left, boundingRect.top, boundingRect.right - boundingRect.left, boundingRect.bottom - boundingRect.top].map((v) => Math.floor(v * 100))));
}
};
img.onerror = () => {
for (const canvas of canvasElements) {
const context2 = canvas.getContext("2d");
drawCheckerboard2(context2, canvas);
canvas.title = `Playwright couldn't show canvas contents because the screenshot failed to load.`;
}
};
img.src = location.href.replace("/snapshot", "/closest-screenshot");
}
};
const onDOMContentLoaded = () => visit(win.document);
win.addEventListener("load", onLoad);
win.addEventListener("DOMContentLoaded", onDOMContentLoaded);
}
const safe = (value2) => JSON.stringify(value2).replace(/</g, "\\u003c");
return `
(${applyPlaywrightAttributes.toString()})(${safe(blankSnapshotUrl)},${safe(viewport)}${targetIds.map((id) => `, ${safe(String(id))}`).join("")})`;
}
function rewriteURLForCustomProtocol(href) {
if (href.startsWith(kLegacyBlobPrefix))
href = href.substring(kLegacyBlobPrefix.length);
try {
const url2 = new URL(href);
if (url2.protocol === "javascript:" || url2.protocol === "vbscript:")
return "javascript:void(0)";
const isBlob = url2.protocol === "blob:";
const isFile = url2.protocol === "file:";
if (!isBlob && !isFile && schemas.includes(url2.protocol))
return href;
const prefix = "pw-" + url2.protocol.slice(0, url2.protocol.length - 1);
if (!isFile)
url2.protocol = "https:";
url2.hostname = url2.hostname ? `${prefix}--${url2.hostname}` : prefix;
if (isFile) {
url2.protocol = "https:";
}
return url2.toString();
} catch {
return href;
}
}
function rewriteURLsInStyleSheetForCustomProtocol(text2) {
return text2.replace(urlInCSSRegex, (match, protocol) => {
const isBlob = protocol === "blob:";
const isFile = protocol === "file:";
if (!isBlob && !isFile && schemas.includes(protocol))
return match;
return match.replace(protocol + "//", `https://pw-${protocol.slice(0, -1)}--`);
});
}
function escapeURLsInStyleSheet(text2) {
const replacer = (match, url2) => {
if (url2.includes("</"))
return match.replace(url2, encodeURI(url2));
return match;
};
return text2.replace(urlToEscapeRegex1, replacer).replace(urlToEscapeRegex2, replacer);
}
var SnapshotRenderer, autoClosing, kAllowedMetaHttpEquivs, schemas, kLegacyBlobPrefix, urlInCSSRegex, urlToEscapeRegex1, urlToEscapeRegex2, blankSnapshotUrl;
var init_snapshotRenderer = __esm({
"packages/isomorphic/trace/snapshotRenderer.ts"() {
"use strict";
init_stringUtils();
SnapshotRenderer = class {
constructor(htmlCache, resources, snapshots, screencastFrames, index) {
this._htmlCache = htmlCache;
this._resources = resources;
this._snapshots = snapshots;
this._index = index;
this._snapshot = snapshots[index];
this._callId = snapshots[index].callId;
this._screencastFrames = screencastFrames;
this.snapshotName = snapshots[index].snapshotName;
}
snapshot() {
return this._snapshots[this._index];
}
viewport() {
return this._snapshots[this._index].viewport;
}
closestScreenshot() {
const { wallTime, timestamp } = this.snapshot();
const closestFrame = wallTime && this._screencastFrames[0]?.frameSwapWallTime ? findClosest(this._screencastFrames, (frame) => frame.frameSwapWallTime, wallTime) : findClosest(this._screencastFrames, (frame) => frame.timestamp, timestamp);
return closestFrame?.sha1;
}
render() {
const result2 = [];
const visit = (n, snapshotIndex, parentTag, parentAttrs) => {
if (typeof n === "string") {
if (parentTag === "STYLE" || parentTag === "style")
result2.push(escapeURLsInStyleSheet(rewriteURLsInStyleSheetForCustomProtocol(n)));
else
result2.push(escapeHTML(n));
return;
}
if (isSubtreeReferenceSnapshot(n)) {
const referenceIndex = snapshotIndex - n[0][0];
if (referenceIndex >= 0 && referenceIndex <= snapshotIndex) {
const nodes = snapshotNodes(this._snapshots[referenceIndex]);
const nodeIndex = n[0][1];
if (nodeIndex >= 0 && nodeIndex < nodes.length)
return visit(nodes[nodeIndex], referenceIndex, parentTag, parentAttrs);
}
} else if (isNodeNameAttributesChildNodesSnapshot(n)) {
const [name, nodeAttrs, ...children] = n;
if (name.toUpperCase() === "SCRIPT")
return;
const nodeName = name === "NOSCRIPT" ? "X-NOSCRIPT" : name;
const attrs = Object.entries(nodeAttrs || {});
result2.push("<", nodeName);
const kCurrentSrcAttribute = "__playwright_current_src__";
const isFrame = nodeName === "IFRAME" || nodeName === "FRAME";
const isAnchor = nodeName === "A";
const isImg = nodeName === "IMG";
const isMeta = nodeName === "META";
const isImgWithCurrentSrc = isImg && attrs.some((a) => a[0] === kCurrentSrcAttribute);
const isSourceInsidePictureWithCurrentSrc = nodeName === "SOURCE" && parentTag === "PICTURE" && parentAttrs?.some((a) => a[0] === kCurrentSrcAttribute);
const hasUnsafeHttpEquiv = isMeta && attrs.some((a) => a[0].toLowerCase() === "http-equiv" && !kAllowedMetaHttpEquivs.has(a[1].trim().toLowerCase()));
for (const [attr, value2] of attrs) {
let attrName = attr;
if (isFrame && attr.toLowerCase() === "src") {
attrName = "__playwright_src__";
}
if (isImg && attr === kCurrentSrcAttribute) {
attrName = "src";
}
if (["src", "srcset"].includes(attr.toLowerCase()) && (isImgWithCurrentSrc || isSourceInsidePictureWithCurrentSrc)) {
attrName = "_" + attrName;
}
if (hasUnsafeHttpEquiv && (attr.toLowerCase() === "http-equiv" || attr.toLowerCase() === "content")) {
attrName = "_" + attr;
}
let attrValue = value2;
if (!isAnchor && (attr.toLowerCase() === "href" || attr.toLowerCase() === "src" || attr === kCurrentSrcAttribute))
attrValue = rewriteURLForCustomProtocol(value2);
result2.push(" ", attrName, '="', escapeHTMLAttribute(attrValue), '"');
}
result2.push(">");
for (const child of children)
visit(child, snapshotIndex, nodeName, attrs);
if (!autoClosing.has(nodeName))
result2.push("</", nodeName, ">");
return;
} else {
return;
}
};
const snapshot3 = this._snapshot;
const html = this._htmlCache.getOrCompute(this, () => {
visit(snapshot3.html, this._index, void 0, void 0);
const safeDoctype = snapshot3.doctype?.replace(/[^a-zA-Z0-9]/g, "");
const prefix = safeDoctype ? `<!DOCTYPE ${safeDoctype}>` : "";
const html2 = prefix + [
// Hide the document in order to prevent flickering. We will unhide once script has processed shadow.
"<style>*,*::before,*::after { visibility: hidden }</style>",
`<script>${snapshotScript(this.viewport(), this._callId, this.snapshotName)}</script>`
].join("") + result2.join("");
return { value: html2, size: html2.length };
});
return { html, pageId: snapshot3.pageId, frameId: snapshot3.frameId, index: this._index };
}
resourceByUrl(url2, method) {
const snapshot3 = this._snapshot;
let sameFrameResource;
let otherFrameResource;
for (const resource of this._resources) {
if (typeof resource._monotonicTime === "number" && resource._monotonicTime >= snapshot3.timestamp)
break;
if (resource.response.status === 304) {
continue;
}
if (resource.request.url === url2 && resource.request.method === method) {
if (resource._frameref === snapshot3.frameId)
sameFrameResource = resource;
else
otherFrameResource = resource;
}
}
let result2 = sameFrameResource ?? otherFrameResource;
if (result2 && method.toUpperCase() === "GET") {
let override = snapshot3.resourceOverrides.find((o) => o.url === url2);
if (override?.ref) {
const index = this._index - override.ref;
if (index >= 0 && index < this._snapshots.length)
override = this._snapshots[index].resourceOverrides.find((o) => o.url === url2);
}
if (override?.sha1) {
result2 = {
...result2,
response: {
...result2.response,
content: {
...result2.response.content,
_sha1: override.sha1
}
}
};
}
}
return result2;
}
};
autoClosing = /* @__PURE__ */ new Set(["AREA", "BASE", "BR", "COL", "COMMAND", "EMBED", "HR", "IMG", "INPUT", "KEYGEN", "LINK", "MENUITEM", "META", "PARAM", "SOURCE", "TRACK", "WBR"]);
kAllowedMetaHttpEquivs = /* @__PURE__ */ new Set(["content-type", "content-language", "default-style", "x-ua-compatible"]);
schemas = ["about:", "blob:", "data:", "file:", "ftp:", "http:", "https:", "mailto:", "sftp:", "ws:", "wss:"];
kLegacyBlobPrefix = "http://playwright.bloburl/#";
urlInCSSRegex = /url\(['"]?([\w-]+:)\/\//ig;
urlToEscapeRegex1 = /url\(\s*'([^']*)'\s*\)/ig;
urlToEscapeRegex2 = /url\(\s*"([^"]*)"\s*\)/ig;
blankSnapshotUrl = "data:text/html;base64," + btoa(`<body></body><style>body { color-scheme: light dark; background: light-dark(white, #333) }</style>`);
}
});
// packages/isomorphic/lruCache.ts
var LRUCache;
var init_lruCache = __esm({
"packages/isomorphic/lruCache.ts"() {
"use strict";
LRUCache = class {
constructor(maxSize) {
this._maxSize = maxSize;
this._map = /* @__PURE__ */ new Map();
this._size = 0;
}
getOrCompute(key, compute) {
if (this._map.has(key)) {
const result3 = this._map.get(key);
this._map.delete(key);
this._map.set(key, result3);
return result3.value;
}
const result2 = compute();
while (this._map.size && this._size + result2.size > this._maxSize) {
const [firstKey, firstValue] = this._map.entries().next().value;
this._size -= firstValue.size;
this._map.delete(firstKey);
}
this._map.set(key, result2);
this._size += result2.size;
return result2.value;
}
};
}
});
// packages/isomorphic/trace/snapshotStorage.ts
var SnapshotStorage;
var init_snapshotStorage = __esm({
"packages/isomorphic/trace/snapshotStorage.ts"() {
"use strict";
init_snapshotRenderer();
init_lruCache();
SnapshotStorage = class {
constructor() {
this._frameSnapshots = /* @__PURE__ */ new Map();
this._cache = new LRUCache(1e8);
// 100MB per each trace
this._contextToResources = /* @__PURE__ */ new Map();
this._resourceUrlsWithOverrides = /* @__PURE__ */ new Set();
}
addResource(contextId4, resource) {
resource.request.url = rewriteURLForCustomProtocol(resource.request.url);
this._ensureResourcesForContext(contextId4).push(resource);
}
addFrameSnapshot(contextId4, snapshot3, screencastFrames) {
for (const override of snapshot3.resourceOverrides)
override.url = rewriteURLForCustomProtocol(override.url);
let frameSnapshots = this._frameSnapshots.get(snapshot3.frameId);
if (!frameSnapshots) {
frameSnapshots = {
raw: [],
renderers: []
};
this._frameSnapshots.set(snapshot3.frameId, frameSnapshots);
if (snapshot3.isMainFrame)
this._frameSnapshots.set(snapshot3.pageId, frameSnapshots);
}
frameSnapshots.raw.push(snapshot3);
const resources = this._ensureResourcesForContext(contextId4);
const renderer = new SnapshotRenderer(this._cache, resources, frameSnapshots.raw, screencastFrames, frameSnapshots.raw.length - 1);
frameSnapshots.renderers.push(renderer);
return renderer;
}
snapshotByName(pageOrFrameId, snapshotName) {
const snapshot3 = this._frameSnapshots.get(pageOrFrameId);
return snapshot3?.renderers.find((r) => r.snapshotName === snapshotName);
}
snapshotsForTest() {
return [...this._frameSnapshots.keys()];
}
finalize() {
for (const resources of this._contextToResources.values())
resources.sort((a, b) => (a._monotonicTime || 0) - (b._monotonicTime || 0));
for (const frameSnapshots of this._frameSnapshots.values()) {
for (const snapshot3 of frameSnapshots.raw) {
for (const override of snapshot3.resourceOverrides)
this._resourceUrlsWithOverrides.add(override.url);
}
}
}
hasResourceOverride(url2) {
return this._resourceUrlsWithOverrides.has(url2);
}
_ensureResourcesForContext(contextId4) {
let resources = this._contextToResources.get(contextId4);
if (!resources) {
resources = [];
this._contextToResources.set(contextId4, resources);
}
return resources;
}
};
}
});
// packages/isomorphic/trace/traceUtils.ts
function parseClientSideCallMetadata(data) {
const result2 = /* @__PURE__ */ new Map();
const { files, stacks } = data;
for (const s of stacks) {
const [id, ff] = s;
result2.set(`call@${id}`, ff.map((f) => ({ file: files[f[0]], line: f[1], column: f[2], function: f[3] })));
}
return result2;
}
function serializeClientSideCallMetadata(metadatas) {
const fileNames = /* @__PURE__ */ new Map();
const stacks = [];
for (const m of metadatas) {
if (!m.stack || !m.stack.length)
continue;
const stack = [];
for (const frame of m.stack) {
let ordinal = fileNames.get(frame.file);
if (typeof ordinal !== "number") {
ordinal = fileNames.size;
fileNames.set(frame.file, ordinal);
}
const stackFrame = [ordinal, frame.line || 0, frame.column || 0, frame.function || ""];
stack.push(stackFrame);
}
stacks.push([m.id, stack]);
}
return { files: [...fileNames.keys()], stacks };
}
var init_traceUtils = __esm({
"packages/isomorphic/trace/traceUtils.ts"() {
"use strict";
}
});
// packages/isomorphic/trace/traceModernizer.ts
var TraceVersionError, latestVersion, TraceModernizer;
var init_traceModernizer = __esm({
"packages/isomorphic/trace/traceModernizer.ts"() {
"use strict";
TraceVersionError = class extends Error {
constructor(message) {
super(message);
this.name = "TraceVersionError";
}
};
latestVersion = 8;
TraceModernizer = class {
constructor(contextEntry, snapshotStorage) {
this._actionMap = /* @__PURE__ */ new Map();
this._pageEntries = /* @__PURE__ */ new Map();
this._jsHandles = /* @__PURE__ */ new Map();
this._consoleObjects = /* @__PURE__ */ new Map();
this._contextEntry = contextEntry;
this._snapshotStorage = snapshotStorage;
}
appendTrace(trace) {
for (const line of trace.split("\n"))
this._appendEvent(line);
}
actions() {
return [...this._actionMap.values()];
}
_pageEntry(pageId4) {
let pageEntry = this._pageEntries.get(pageId4);
if (!pageEntry) {
pageEntry = {
pageId: pageId4,
screencastFrames: []
};
this._pageEntries.set(pageId4, pageEntry);
this._contextEntry.pages.push(pageEntry);
}
return pageEntry;
}
_appendEvent(line) {
if (!line)
return;
const events = this._modernize(JSON.parse(line));
for (const event of events)
this._innerAppendEvent(event);
}
_innerAppendEvent(event) {
const contextEntry = this._contextEntry;
switch (event.type) {
case "context-options": {
if (event.version > latestVersion)
throw new TraceVersionError("The trace was created by a newer version of Playwright and is not supported by this version of the viewer. Please use latest Playwright to open the trace.");
this._version = event.version;
contextEntry.origin = event.origin;
contextEntry.browserName = event.browserName;
contextEntry.channel = event.channel;
contextEntry.title = event.title;
contextEntry.platform = event.platform;
contextEntry.playwrightVersion = event.playwrightVersion;
contextEntry.wallTime = event.wallTime;
contextEntry.startTime = event.monotonicTime;
contextEntry.sdkLanguage = event.sdkLanguage;
contextEntry.options = event.options;
contextEntry.testIdAttributeName = event.testIdAttributeName;
contextEntry.contextId = event.contextId ?? "";
contextEntry.testTimeout = event.testTimeout;
break;
}
case "screencast-frame": {
this._pageEntry(event.pageId).screencastFrames.push(event);
break;
}
case "before": {
this._actionMap.set(event.callId, { ...event, type: "action", endTime: 0, log: [] });
break;
}
case "input": {
const existing = this._actionMap.get(event.callId);
existing.inputSnapshot = event.inputSnapshot;
existing.point = event.point;
break;
}
case "log": {
const existing = this._actionMap.get(event.callId);
if (!existing)
return;
existing.log.push({
time: event.time,
message: event.message
});
break;
}
case "after": {
const existing = this._actionMap.get(event.callId);
existing.afterSnapshot = event.afterSnapshot;
existing.endTime = event.endTime;
existing.result = event.result;
existing.error = event.error;
existing.attachments = event.attachments;
existing.annotations = event.annotations;
if (event.point)
existing.point = event.point;
break;
}
case "action": {
this._actionMap.set(event.callId, { ...event, log: [] });
break;
}
case "event": {
contextEntry.events.push(event);
break;
}
case "stdout": {
contextEntry.stdio.push(event);
break;
}
case "stderr": {
contextEntry.stdio.push(event);
break;
}
case "error": {
contextEntry.errors.push(event);
break;
}
case "console": {
contextEntry.events.push(event);
break;
}
case "resource-snapshot":
this._snapshotStorage.addResource(this._contextEntry.contextId, event.snapshot);
contextEntry.resources.push(event.snapshot);
break;
case "frame-snapshot":
this._snapshotStorage.addFrameSnapshot(this._contextEntry.contextId, event.snapshot, this._pageEntry(event.snapshot.pageId).screencastFrames);
break;
}
if ("pageId" in event && event.pageId)
this._pageEntry(event.pageId);
if (event.type === "action" || event.type === "before")
contextEntry.startTime = Math.min(contextEntry.startTime, event.startTime);
if (event.type === "action" || event.type === "after")
contextEntry.endTime = Math.max(contextEntry.endTime, event.endTime);
if (event.type === "event") {
contextEntry.startTime = Math.min(contextEntry.startTime, event.time);
contextEntry.endTime = Math.max(contextEntry.endTime, event.time);
}
if (event.type === "screencast-frame") {
contextEntry.startTime = Math.min(contextEntry.startTime, event.timestamp);
contextEntry.endTime = Math.max(contextEntry.endTime, event.timestamp);
}
}
_processedContextCreatedEvent() {
return this._version !== void 0;
}
_modernize(event) {
let version3 = this._version ?? event.version ?? 6;
let events = [event];
for (; version3 < latestVersion; ++version3)
events = this[`_modernize_${version3}_to_${version3 + 1}`].call(this, events);
return events;
}
_modernize_0_to_1(events) {
for (const event of events) {
if (event.type !== "action")
continue;
if (typeof event.metadata.error === "string")
event.metadata.error = { error: { name: "Error", message: event.metadata.error } };
}
return events;
}
_modernize_1_to_2(events) {
for (const event of events) {
if (event.type !== "frame-snapshot" || !event.snapshot.isMainFrame)
continue;
event.snapshot.viewport = this._contextEntry.options?.viewport || { width: 1280, height: 720 };
}
return events;
}
_modernize_2_to_3(events) {
for (const event of events) {
if (event.type !== "resource-snapshot" || event.snapshot.request)
continue;
const resource = event.snapshot;
event.snapshot = {
_frameref: resource.frameId,
request: {
url: resource.url,
method: resource.method,
headers: resource.requestHeaders,
postData: resource.requestSha1 ? { _sha1: resource.requestSha1 } : void 0
},
response: {
status: resource.status,
headers: resource.responseHeaders,
content: {
mimeType: resource.contentType,
_sha1: resource.responseSha1
}
},
_monotonicTime: resource.timestamp
};
}
return events;
}
_modernize_3_to_4(events) {
const result2 = [];
for (const event of events) {
const e = this._modernize_event_3_to_4(event);
if (e)
result2.push(e);
}
return result2;
}
_modernize_event_3_to_4(event) {
if (event.type !== "action" && event.type !== "event") {
return event;
}
const metadata = event.metadata;
if (metadata.internal || metadata.method.startsWith("tracing"))
return null;
if (event.type === "event") {
if (metadata.method === "__create__" && metadata.type === "ConsoleMessage") {
return {
type: "object",
class: metadata.type,
guid: metadata.params.guid,
initializer: metadata.params.initializer
};
}
return {
type: "event",
time: metadata.startTime,
class: metadata.type,
method: metadata.method,
params: metadata.params,
pageId: metadata.pageId
};
}
return {
type: "action",
callId: metadata.id,
startTime: metadata.startTime,
endTime: metadata.endTime,
apiName: metadata.apiName || metadata.type + "." + metadata.method,
class: metadata.type,
method: metadata.method,
params: metadata.params,
// eslint-disable-next-line no-restricted-globals
wallTime: metadata.wallTime || Date.now(),
log: metadata.log,
beforeSnapshot: metadata.snapshots.find((s) => s.title === "before")?.snapshotName,
inputSnapshot: metadata.snapshots.find((s) => s.title === "input")?.snapshotName,
afterSnapshot: metadata.snapshots.find((s) => s.title === "after")?.snapshotName,
error: metadata.error?.error,
result: metadata.result,
point: metadata.point,
pageId: metadata.pageId
};
}
_modernize_4_to_5(events) {
const result2 = [];
for (const event of events) {
const e = this._modernize_event_4_to_5(event);
if (e)
result2.push(e);
}
return result2;
}
_modernize_event_4_to_5(event) {
if (event.type === "event" && event.method === "__create__" && event.class === "JSHandle")
this._jsHandles.set(event.params.guid, event.params.initializer);
if (event.type === "object") {
if (event.class !== "ConsoleMessage")
return null;
const args = event.initializer.args?.map((arg) => {
if (arg.guid) {
const handle = this._jsHandles.get(arg.guid);
return { preview: handle?.preview || "", value: "" };
}
return { preview: arg.preview || "", value: arg.value || "" };
});
this._consoleObjects.set(event.guid, {
type: event.initializer.type,
text: event.initializer.text,
location: event.initializer.location,
args
});
return null;
}
if (event.type === "event" && event.method === "console") {
const consoleMessage = this._consoleObjects.get(event.params.message?.guid || "");
if (!consoleMessage)
return null;
return {
type: "console",
time: event.time,
pageId: event.pageId,
messageType: consoleMessage.type,
text: consoleMessage.text,
args: consoleMessage.args,
location: consoleMessage.location
};
}
return event;
}
_modernize_5_to_6(events) {
const result2 = [];
for (const event of events) {
result2.push(event);
if (event.type !== "after" || !event.log.length)
continue;
for (const log2 of event.log) {
result2.push({
type: "log",
callId: event.callId,
message: log2,
time: -1
});
}
}
return result2;
}
_modernize_6_to_7(events) {
const result2 = [];
if (!this._processedContextCreatedEvent() && events[0].type !== "context-options") {
const event = {
type: "context-options",
origin: "testRunner",
version: 6,
browserName: "",
options: {},
platform: "unknown",
wallTime: 0,
monotonicTime: 0,
sdkLanguage: "javascript",
contextId: ""
};
result2.push(event);
}
for (const event of events) {
if (event.type === "context-options") {
result2.push({ ...event, monotonicTime: 0, origin: "library", contextId: "" });
continue;
}
if (event.type === "before" || event.type === "action") {
if (!this._contextEntry.wallTime)
this._contextEntry.wallTime = event.wallTime;
const eventAsV6 = event;
const eventAsV7 = event;
eventAsV7.stepId = `${eventAsV6.apiName}@${eventAsV6.wallTime}`;
result2.push(eventAsV7);
} else {
result2.push(event);
}
}
return result2;
}
_modernize_7_to_8(events) {
const result2 = [];
for (const event of events) {
if (event.type === "before" || event.type === "action") {
const eventAsV7 = event;
const eventAsV8 = event;
if (eventAsV7.apiName) {
eventAsV8.title = eventAsV7.apiName;
delete eventAsV8.apiName;
}
eventAsV8.stepId = eventAsV7.stepId ?? eventAsV7.callId;
result2.push(eventAsV8);
} else {
result2.push(event);
}
}
return result2;
}
};
}
});
// packages/isomorphic/trace/traceLoader.ts
function stripEncodingFromContentType(contentType) {
const charset = contentType.match(/^(.*);\s*charset=.*$/);
if (charset)
return charset[1];
return contentType;
}
function createEmptyContext() {
return {
origin: "testRunner",
startTime: Number.MAX_SAFE_INTEGER,
wallTime: Number.MAX_SAFE_INTEGER,
endTime: 0,
browserName: "",
options: {
deviceScaleFactor: 1,
isMobile: false,
viewport: { width: 1280, height: 800 }
},
pages: [],
resources: [],
actions: [],
events: [],
errors: [],
stdio: [],
hasSource: false,
contextId: ""
};
}
var TraceLoader;
var init_traceLoader = __esm({
"packages/isomorphic/trace/traceLoader.ts"() {
"use strict";
init_traceUtils();
init_snapshotStorage();
init_traceModernizer();
TraceLoader = class {
constructor() {
this.contextEntries = [];
this._resourceToContentType = /* @__PURE__ */ new Map();
}
async load(backend, traceFile, unzipProgress) {
this._backend = backend;
const prefix = traceFile?.match(/(.+)\.trace$/)?.[1];
const prefixes = [];
let hasSource = false;
for (const entryName of await this._backend.entryNames()) {
const match = entryName.match(/(.+)\.trace$/);
if (match && (!prefix || prefix === match[1]))
prefixes.push(match[1] || "");
if (entryName.includes("src@"))
hasSource = true;
}
if (!prefixes.length)
throw new Error("Cannot find .trace file");
this._snapshotStorage = new SnapshotStorage();
const total = prefixes.length * 3;
let done = 0;
for (const prefix2 of prefixes) {
const contextEntry = createEmptyContext();
contextEntry.hasSource = hasSource;
const modernizer = new TraceModernizer(contextEntry, this._snapshotStorage);
const trace = await this._backend.readText(prefix2 + ".trace") || "";
modernizer.appendTrace(trace);
unzipProgress?.(++done, total);
const network = await this._backend.readText(prefix2 + ".network") || "";
modernizer.appendTrace(network);
unzipProgress?.(++done, total);
contextEntry.actions = modernizer.actions().sort((a1, a2) => a1.startTime - a2.startTime);
if (!backend.isLive()) {
for (const action of contextEntry.actions.slice().reverse()) {
if (!action.endTime && !action.error) {
for (const a of contextEntry.actions) {
if (a.parentId === action.callId && action.endTime < a.endTime)
action.endTime = a.endTime;
}
}
}
}
const stacks = await this._backend.readText(prefix2 + ".stacks");
if (stacks) {
const callMetadata = parseClientSideCallMetadata(JSON.parse(stacks));
for (const action of contextEntry.actions)
action.stack = action.stack || callMetadata.get(action.callId);
}
unzipProgress?.(++done, total);
for (const resource of contextEntry.resources) {
if (resource.request.postData?._sha1)
this._resourceToContentType.set(resource.request.postData._sha1, stripEncodingFromContentType(resource.request.postData.mimeType));
if (resource.response.content?._sha1)
this._resourceToContentType.set(resource.response.content._sha1, stripEncodingFromContentType(resource.response.content.mimeType));
}
this.contextEntries.push(contextEntry);
}
this._snapshotStorage.finalize();
}
async hasEntry(filename) {
return this._backend.hasEntry(filename);
}
async resourceForSha1(sha1) {
const blob = await this._backend.readBlob("resources/" + sha1);
const contentType = this._resourceToContentType.get(sha1);
if (!blob || contentType === void 0 || contentType === "x-unknown")
return blob;
return new Blob([blob], { type: contentType });
}
storage() {
return this._snapshotStorage;
}
};
}
});
// packages/isomorphic/trace/traceModel.ts
function indexModel(context2) {
for (const page of context2.pages)
page[contextSymbol] = context2;
for (let i = 0; i < context2.actions.length; ++i) {
const action = context2.actions[i];
action[contextSymbol] = context2;
}
let lastNonRouteAction = void 0;
for (let i = context2.actions.length - 1; i >= 0; i--) {
const action = context2.actions[i];
action[nextInContextSymbol] = lastNonRouteAction;
if (action.class !== "Route")
lastNonRouteAction = action;
}
for (const event of context2.events)
event[contextSymbol] = context2;
for (const resource of context2.resources)
resource[contextSymbol] = context2;
}
function mergeActionsAndUpdateTiming(contexts) {
const result2 = [];
const actions = mergeActionsAndUpdateTimingSameTrace(contexts);
result2.push(...actions);
result2.sort((a1, a2) => {
if (a2.parentId === a1.callId)
return 1;
if (a1.parentId === a2.callId)
return -1;
return a1.endTime - a2.endTime;
});
for (let i = 1; i < result2.length; ++i)
result2[i][prevByEndTimeSymbol] = result2[i - 1];
result2.sort((a1, a2) => {
if (a2.parentId === a1.callId)
return -1;
if (a1.parentId === a2.callId)
return 1;
return a1.startTime - a2.startTime;
});
for (let i = 0; i + 1 < result2.length; ++i)
result2[i][nextByStartTimeSymbol] = result2[i + 1];
return result2;
}
function mergeActionsAndUpdateTimingSameTrace(contexts) {
const map = /* @__PURE__ */ new Map();
const libraryContexts = contexts.filter((context2) => context2.origin === "library");
const testRunnerContexts = contexts.filter((context2) => context2.origin === "testRunner");
if (!testRunnerContexts.length || !libraryContexts.length) {
return contexts.map((context2) => {
return context2.actions.map((action) => ({ ...action, context: context2 }));
}).flat();
}
for (const context2 of libraryContexts) {
for (const action of context2.actions) {
map.set(action.stepId || `tmp-step@${++lastTmpStepId}`, { ...action, context: context2 });
}
}
const delta = monotonicTimeDeltaBetweenLibraryAndRunner(testRunnerContexts, map);
if (delta)
adjustMonotonicTime(libraryContexts, delta);
const nonPrimaryIdToPrimaryId = /* @__PURE__ */ new Map();
for (const context2 of testRunnerContexts) {
for (const action of context2.actions) {
const existing = action.stepId && map.get(action.stepId);
if (existing) {
nonPrimaryIdToPrimaryId.set(action.callId, existing.callId);
if (action.error)
existing.error = action.error;
if (action.attachments)
existing.attachments = action.attachments;
if (action.annotations)
existing.annotations = action.annotations;
if (action.parentId)
existing.parentId = nonPrimaryIdToPrimaryId.get(action.parentId) ?? action.parentId;
if (action.group)
existing.group = action.group;
existing.startTime = action.startTime;
existing.endTime = action.endTime;
continue;
}
if (action.parentId)
action.parentId = nonPrimaryIdToPrimaryId.get(action.parentId) ?? action.parentId;
map.set(action.stepId || `tmp-step@${++lastTmpStepId}`, { ...action, context: context2 });
}
}
return [...map.values()];
}
function adjustMonotonicTime(contexts, monotonicTimeDelta) {
for (const context2 of contexts) {
context2.startTime += monotonicTimeDelta;
context2.endTime += monotonicTimeDelta;
for (const action of context2.actions) {
if (action.startTime)
action.startTime += monotonicTimeDelta;
if (action.endTime)
action.endTime += monotonicTimeDelta;
}
for (const event of context2.events)
event.time += monotonicTimeDelta;
for (const event of context2.stdio)
event.timestamp += monotonicTimeDelta;
for (const page of context2.pages) {
for (const frame of page.screencastFrames)
frame.timestamp += monotonicTimeDelta;
}
for (const resource of context2.resources) {
if (resource._monotonicTime)
resource._monotonicTime += monotonicTimeDelta;
}
}
}
function monotonicTimeDeltaBetweenLibraryAndRunner(nonPrimaryContexts, libraryActions) {
for (const context2 of nonPrimaryContexts) {
for (const action of context2.actions) {
if (!action.startTime)
continue;
const libraryAction = action.stepId ? libraryActions.get(action.stepId) : void 0;
if (libraryAction)
return action.startTime - libraryAction.startTime;
}
}
return 0;
}
function buildActionTree(actions) {
const itemMap = /* @__PURE__ */ new Map();
for (const action of actions) {
itemMap.set(action.callId, {
id: action.callId,
parent: void 0,
children: [],
action
});
}
const rootItem = { action: { ...kFakeRootAction }, id: "", parent: void 0, children: [] };
for (const item of itemMap.values()) {
rootItem.action.startTime = Math.min(rootItem.action.startTime, item.action.startTime);
rootItem.action.endTime = Math.max(rootItem.action.endTime, item.action.endTime);
const parent = item.action.parentId ? itemMap.get(item.action.parentId) || rootItem : rootItem;
parent.children.push(item);
item.parent = parent;
}
const inheritStack = (item) => {
for (const child of item.children) {
child.action.stack = child.action.stack ?? item.action.stack;
inheritStack(child);
}
};
inheritStack(rootItem);
return { rootItem, itemMap };
}
function context(action) {
return action[contextSymbol];
}
function nextInContext(action) {
return action[nextInContextSymbol];
}
function previousActionByEndTime(action) {
return action[prevByEndTimeSymbol];
}
function nextActionByStartTime(action) {
return action[nextByStartTimeSymbol];
}
function stats(action) {
let errors = 0;
let warnings = 0;
for (const event of eventsForAction(action)) {
if (event.type === "console") {
const type3 = event.messageType;
if (type3 === "warning")
++warnings;
else if (type3 === "error")
++errors;
}
if (event.type === "event" && event.method === "pageError")
++errors;
}
return { errors, warnings };
}
function eventsForAction(action) {
let result2 = action[eventsSymbol];
if (result2)
return result2;
const nextAction = nextInContext(action);
result2 = context(action).events.filter((event) => {
return event.time >= action.startTime && (!nextAction || event.time < nextAction.startTime);
});
action[eventsSymbol] = result2;
return result2;
}
function collectSources(actions, errorDescriptors) {
const result2 = /* @__PURE__ */ new Map();
for (const action of actions) {
for (const frame of action.stack || []) {
let source8 = result2.get(frame.file);
if (!source8) {
source8 = { errors: [], content: void 0 };
result2.set(frame.file, source8);
}
}
}
for (const error of errorDescriptors) {
const { action, stack, message } = error;
if (!action || !stack)
continue;
result2.get(stack[0].file)?.errors.push({
line: stack[0].line || 0,
message
});
}
return result2;
}
var contextSymbol, nextInContextSymbol, prevByEndTimeSymbol, nextByStartTimeSymbol, eventsSymbol, TraceModel, lastTmpStepId, kFakeRootAction;
var init_traceModel = __esm({
"packages/isomorphic/trace/traceModel.ts"() {
"use strict";
init_protocolFormatter();
contextSymbol = Symbol("context");
nextInContextSymbol = Symbol("nextInContext");
prevByEndTimeSymbol = Symbol("prevByEndTime");
nextByStartTimeSymbol = Symbol("nextByStartTime");
eventsSymbol = Symbol("events");
TraceModel = class {
constructor(traceUri, contexts) {
contexts.forEach((contextEntry) => indexModel(contextEntry));
const libraryContext = contexts.find((context2) => context2.origin === "library");
this.traceUri = traceUri;
this.browserName = libraryContext?.browserName || "";
this.sdkLanguage = libraryContext?.sdkLanguage;
this.channel = libraryContext?.channel;
this.testIdAttributeName = libraryContext?.testIdAttributeName;
this.platform = libraryContext?.platform || "";
this.playwrightVersion = contexts.find((c) => c.playwrightVersion)?.playwrightVersion;
this.title = libraryContext?.title || "";
this.options = libraryContext?.options || {};
this.testTimeout = contexts.find((c) => c.origin === "testRunner")?.testTimeout;
this.actions = mergeActionsAndUpdateTiming(contexts);
this.pages = [].concat(...contexts.map((c) => c.pages));
this.wallTime = contexts.map((c) => c.wallTime).reduce((prev, cur) => Math.min(prev || Number.MAX_VALUE, cur), Number.MAX_VALUE);
this.startTime = contexts.map((c) => c.startTime).reduce((prev, cur) => Math.min(prev, cur), Number.MAX_VALUE);
this.endTime = contexts.map((c) => c.endTime).reduce((prev, cur) => Math.max(prev, cur), Number.MIN_VALUE);
this.events = [].concat(...contexts.map((c) => c.events));
this.stdio = [].concat(...contexts.map((c) => c.stdio));
this.errors = [].concat(...contexts.map((c) => c.errors));
this.hasSource = contexts.some((c) => c.hasSource);
this.hasStepData = contexts.some((context2) => context2.origin === "testRunner");
this.resources = [...contexts.map((c) => c.resources)].flat().map((entry) => ({ ...entry, id: `${entry.pageref}-${entry.startedDateTime}-${entry.request.url}` }));
this.attachments = this.actions.flatMap((action) => action.attachments?.map((attachment) => ({ ...attachment, callId: action.callId, traceUri })) ?? []);
this.visibleAttachments = this.attachments.filter((attachment) => !attachment.name.startsWith("_"));
this.events.sort((a1, a2) => a1.time - a2.time);
this.resources.sort((a1, a2) => a1._monotonicTime - a2._monotonicTime);
this.errorDescriptors = this.hasStepData ? this._errorDescriptorsFromTestRunner() : this._errorDescriptorsFromActions();
this.sources = collectSources(this.actions, this.errorDescriptors);
this.actionCounters = /* @__PURE__ */ new Map();
for (const action of this.actions) {
action.group = action.group ?? getActionGroup({ type: action.class, method: action.method });
if (action.group)
this.actionCounters.set(action.group, 1 + (this.actionCounters.get(action.group) || 0));
}
}
createRelativeUrl(path59) {
const url2 = new URL("http://localhost/" + path59);
url2.searchParams.set("trace", this.traceUri);
return url2.toString().substring("http://localhost/".length);
}
failedAction() {
return this.actions.findLast((a) => a.error);
}
filteredActions(actionsFilter) {
const filter = new Set(actionsFilter);
return this.actions.filter((action) => !action.group || filter.has(action.group));
}
renderActionTree(filter) {
const actions = this.filteredActions(filter ?? []);
const { rootItem } = buildActionTree(actions);
const actionTree = [];
const visit = (actionItem, indent) => {
const title = renderTitleForCall({ ...actionItem.action, type: actionItem.action.class });
actionTree.push(`${indent}${title || actionItem.id}`);
for (const child of actionItem.children)
visit(child, indent + " ");
};
rootItem.children.forEach((a) => visit(a, ""));
return actionTree;
}
_errorDescriptorsFromActions() {
const errors = [];
for (const action of this.actions || []) {
if (!action.error?.message)
continue;
errors.push({
action,
stack: action.stack,
message: action.error.message
});
}
return errors;
}
_errorDescriptorsFromTestRunner() {
return this.errors.filter((e) => !!e.message).map((error, i) => ({
stack: error.stack,
message: error.message
}));
}
};
lastTmpStepId = 0;
kFakeRootAction = {
type: "action",
callId: "",
startTime: 0,
endTime: 0,
class: "",
method: "",
params: {},
log: [],
context: {
origin: "library",
startTime: 0,
endTime: 0,
browserName: "",
wallTime: 0,
options: {},
pages: [],
resources: [],
actions: [],
events: [],
stdio: [],
errors: [],
hasSource: false,
contextId: ""
}
};
}
});
// packages/isomorphic/yaml.ts
function yamlEscapeKeyIfNeeded(str) {
if (!yamlStringNeedsQuotes(str))
return str;
return `'` + str.replace(/'/g, `''`) + `'`;
}
function yamlEscapeValueIfNeeded(str) {
if (!yamlStringNeedsQuotes(str))
return str;
return '"' + str.replace(/[\\"\x00-\x1f\x7f-\x9f]/g, (c) => {
switch (c) {
case "\\":
return "\\\\";
case '"':
return '\\"';
case "\b":
return "\\b";
case "\f":
return "\\f";
case "\n":
return "\\n";
case "\r":
return "\\r";
case " ":
return "\\t";
default:
const code = c.charCodeAt(0);
return "\\x" + code.toString(16).padStart(2, "0");
}
}) + '"';
}
function yamlStringNeedsQuotes(str) {
if (str.length === 0)
return true;
if (/^\s|\s$/.test(str))
return true;
if (/[\x00-\x08\x0b\x0c\x0e-\x1f\x7f-\x9f]/.test(str))
return true;
if (/^-/.test(str))
return true;
if (/[\n:](\s|$)/.test(str))
return true;
if (/\s#/.test(str))
return true;
if (/[\n\r]/.test(str))
return true;
if (/^[&*\],?!>|@"'#%]/.test(str))
return true;
if (/[{}`]/.test(str))
return true;
if (/^\[/.test(str))
return true;
if (!isNaN(Number(str)) || ["y", "n", "yes", "no", "true", "false", "on", "off", "null"].includes(str.toLowerCase()))
return true;
return false;
}
var init_yaml = __esm({
"packages/isomorphic/yaml.ts"() {
"use strict";
}
});
// packages/isomorphic/index.ts
var isomorphic_exports = {};
__export(isomorphic_exports, {
CSharpLocatorFactory: () => CSharpLocatorFactory,
DEFAULT_PLAYWRIGHT_LAUNCH_TIMEOUT: () => DEFAULT_PLAYWRIGHT_LAUNCH_TIMEOUT,
DEFAULT_PLAYWRIGHT_TIMEOUT: () => DEFAULT_PLAYWRIGHT_TIMEOUT,
InvalidSelectorError: () => InvalidSelectorError,
JavaLocatorFactory: () => JavaLocatorFactory,
JavaScriptLocatorFactory: () => JavaScriptLocatorFactory,
JsonlLocatorFactory: () => JsonlLocatorFactory,
KeyParser: () => KeyParser,
LongStandingScope: () => LongStandingScope,
ManualPromise: () => ManualPromise,
MultiMap: () => MultiMap,
ParserError: () => ParserError,
PythonLocatorFactory: () => PythonLocatorFactory,
Semaphore: () => Semaphore,
SnapshotServer: () => SnapshotServer,
SnapshotStorage: () => SnapshotStorage,
TraceLoader: () => TraceLoader,
TraceModel: () => TraceModel,
ansiRegex: () => ansiRegex,
ariaNodesEqual: () => ariaNodesEqual,
asLocator: () => asLocator,
asLocatorDescription: () => asLocatorDescription,
asLocators: () => asLocators,
assert: () => assert,
buildActionTree: () => buildActionTree,
bytesToString: () => bytesToString,
cacheNormalizedWhitespaces: () => cacheNormalizedWhitespaces,
captureRawStack: () => captureRawStack,
constructURLBasedOnBaseURL: () => constructURLBasedOnBaseURL,
context: () => context,
customCSSNames: () => customCSSNames,
deserializeURLMatch: () => deserializeURLMatch,
escapeForAttributeSelector: () => escapeForAttributeSelector,
escapeForTextSelector: () => escapeForTextSelector,
escapeHTML: () => escapeHTML,
escapeHTMLAttribute: () => escapeHTMLAttribute,
escapeRegExp: () => escapeRegExp,
escapeTemplateString: () => escapeTemplateString,
escapeWithQuotes: () => escapeWithQuotes,
eventsForAction: () => eventsForAction,
findNewNode: () => findNewNode,
formatObject: () => formatObject,
formatObjectOrVoid: () => formatObjectOrVoid,
formatProtocolParam: () => formatProtocolParam,
getActionGroup: () => getActionGroup,
getExtensionForMimeType: () => getExtensionForMimeType,
getMetainfo: () => getMetainfo,
getMimeTypeForPath: () => getMimeTypeForPath,
globToRegexPattern: () => globToRegexPattern,
hasPointerCursor: () => hasPointerCursor,
headersArrayToObject: () => headersArrayToObject,
headersObjectToArray: () => headersObjectToArray,
isError: () => isError,
isHttpUrl: () => isHttpUrl,
isInvalidSelectorError: () => isInvalidSelectorError,
isJsonMimeType: () => isJsonMimeType,
isObject: () => isObject,
isRegExp: () => isRegExp,
isString: () => isString,
isTextualMimeType: () => isTextualMimeType,
isURLPattern: () => isURLPattern,
isXmlMimeType: () => isXmlMimeType,
locatorCustomDescription: () => locatorCustomDescription,
locatorOrSelectorAsSelector: () => locatorOrSelectorAsSelector,
longestCommonSubstring: () => longestCommonSubstring,
methodMetainfo: () => methodMetainfo,
monotonicTime: () => monotonicTime,
msToString: () => msToString,
nextActionByStartTime: () => nextActionByStartTime,
noColors: () => noColors,
normalizeEscapedRegexQuotes: () => normalizeEscapedRegexQuotes,
normalizeWhiteSpace: () => normalizeWhiteSpace,
padImageToSize: () => padImageToSize,
parseAriaSnapshot: () => parseAriaSnapshot,
parseAriaSnapshotUnsafe: () => parseAriaSnapshotUnsafe,
parseAttributeSelector: () => parseAttributeSelector,
parseCSS: () => parseCSS,
parseClientSideCallMetadata: () => parseClientSideCallMetadata,
parseErrorStack: () => parseErrorStack,
parseRegex: () => parseRegex,
parseSelector: () => parseSelector,
parseStackFrame: () => parseStackFrame,
pollAgainstDeadline: () => pollAgainstDeadline,
previousActionByEndTime: () => previousActionByEndTime,
quoteCSSAttributeValue: () => quoteCSSAttributeValue,
raceAgainstDeadline: () => raceAgainstDeadline,
renderTitleForCall: () => renderTitleForCall,
resolveGlobToRegexPattern: () => resolveGlobToRegexPattern,
rewriteErrorMessage: () => rewriteErrorMessage,
scaleImageToSize: () => scaleImageToSize,
serializeClientSideCallMetadata: () => serializeClientSideCallMetadata,
serializeExpectedTextValues: () => serializeExpectedTextValues,
serializeSelector: () => serializeSelector,
serializeURLMatch: () => serializeURLMatch,
serializeURLPattern: () => serializeURLPattern,
setTimeOrigin: () => setTimeOrigin,
signalToPromise: () => signalToPromise,
splitErrorMessage: () => splitErrorMessage,
splitSelectorByFrame: () => splitSelectorByFrame,
stats: () => stats,
stringifySelector: () => stringifySelector,
stringifyStackFrames: () => stringifyStackFrames,
stripAnsiEscapes: () => stripAnsiEscapes,
textValue: () => textValue,
timeOrigin: () => timeOrigin,
toSnakeCase: () => toSnakeCase,
toTitleCase: () => toTitleCase,
trimString: () => trimString,
trimStringWithEllipsis: () => trimStringWithEllipsis,
unsafeLocatorOrSelectorAsSelector: () => unsafeLocatorOrSelectorAsSelector,
urlMatches: () => urlMatches,
urlMatchesEqual: () => urlMatchesEqual,
validate: () => validate,
visitAllSelectorParts: () => visitAllSelectorParts,
webColors: () => webColors,
yamlEscapeKeyIfNeeded: () => yamlEscapeKeyIfNeeded,
yamlEscapeValueIfNeeded: () => yamlEscapeValueIfNeeded
});
var init_isomorphic = __esm({
"packages/isomorphic/index.ts"() {
"use strict";
init_ariaSnapshot();
init_expectUtils();
init_assert();
init_colors();
init_headers();
init_imageUtils();
init_jsonSchema();
init_locatorGenerators();
init_manualPromise();
init_mimeType();
init_multimap();
init_protocolFormatter();
init_protocolMetainfo();
init_rtti();
init_semaphore();
init_stackTrace();
init_stringUtils();
init_formatUtils();
init_time();
init_timeoutRunner();
init_snapshotServer();
init_urlMatch();
init_cssParser();
init_locatorParser();
init_selectorParser();
init_snapshotStorage();
init_traceLoader();
init_traceModel();
init_traceUtils();
init_yaml();
}
});
// packages/utils/ascii.ts
function wrapInASCIIBox(text2, padding = 0) {
const lines = text2.split("\n");
const maxLength = Math.max(...lines.map((line) => line.length));
return [
"\u2554" + "\u2550".repeat(maxLength + padding * 2) + "\u2557",
...lines.map((line) => "\u2551" + " ".repeat(padding) + line + " ".repeat(maxLength - line.length + padding) + "\u2551"),
"\u255A" + "\u2550".repeat(maxLength + padding * 2) + "\u255D"
].join("\n");
}
function jsonStringifyForceASCII(object) {
return JSON.stringify(object).replace(
/[\u007f-\uffff]/g,
(c) => "\\u" + ("0000" + c.charCodeAt(0).toString(16)).slice(-4)
);
}
var init_ascii = __esm({
"packages/utils/ascii.ts"() {
"use strict";
}
});
// packages/utils/chromiumChannels.ts
function defaultUserDataDirForChannel(channel) {
return channelToDefaultUserDataDir.get(channel)?.[process.platform];
}
function isChromiumChannelName(channel) {
return channelToDefaultUserDataDir.has(channel);
}
var import_os, import_path, channelToDefaultUserDataDir;
var init_chromiumChannels = __esm({
"packages/utils/chromiumChannels.ts"() {
"use strict";
import_os = __toESM(require("os"));
import_path = __toESM(require("path"));
channelToDefaultUserDataDir = /* @__PURE__ */ new Map([
["chrome", {
"linux": import_path.default.join(import_os.default.homedir(), ".config", "google-chrome"),
"darwin": import_path.default.join(import_os.default.homedir(), "Library", "Application Support", "Google", "Chrome"),
"win32": import_path.default.join(process.env.LOCALAPPDATA || import_path.default.join(import_os.default.homedir(), "AppData", "Local"), "Google", "Chrome", "User Data")
}],
["chrome-beta", {
"linux": import_path.default.join(import_os.default.homedir(), ".config", "google-chrome-beta"),
"darwin": import_path.default.join(import_os.default.homedir(), "Library", "Application Support", "Google", "Chrome Beta"),
"win32": import_path.default.join(process.env.LOCALAPPDATA || import_path.default.join(import_os.default.homedir(), "AppData", "Local"), "Google", "Chrome Beta", "User Data")
}],
["chrome-dev", {
"linux": import_path.default.join(import_os.default.homedir(), ".config", "google-chrome-unstable"),
"darwin": import_path.default.join(import_os.default.homedir(), "Library", "Application Support", "Google", "Chrome Dev"),
"win32": import_path.default.join(process.env.LOCALAPPDATA || import_path.default.join(import_os.default.homedir(), "AppData", "Local"), "Google", "Chrome Dev", "User Data")
}],
["chrome-canary", {
"linux": import_path.default.join(import_os.default.homedir(), ".config", "google-chrome-canary"),
"darwin": import_path.default.join(import_os.default.homedir(), "Library", "Application Support", "Google", "Chrome Canary"),
"win32": import_path.default.join(process.env.LOCALAPPDATA || import_path.default.join(import_os.default.homedir(), "AppData", "Local"), "Google", "Chrome SxS", "User Data")
}],
["msedge", {
"linux": import_path.default.join(import_os.default.homedir(), ".config", "microsoft-edge"),
"darwin": import_path.default.join(import_os.default.homedir(), "Library", "Application Support", "Microsoft Edge"),
"win32": import_path.default.join(process.env.LOCALAPPDATA || import_path.default.join(import_os.default.homedir(), "AppData", "Local"), "Microsoft", "Edge", "User Data")
}],
["msedge-beta", {
"linux": import_path.default.join(import_os.default.homedir(), ".config", "microsoft-edge-beta"),
"darwin": import_path.default.join(import_os.default.homedir(), "Library", "Application Support", "Microsoft Edge Beta"),
"win32": import_path.default.join(process.env.LOCALAPPDATA || import_path.default.join(import_os.default.homedir(), "AppData", "Local"), "Microsoft", "Edge Beta", "User Data")
}],
["msedge-dev", {
"linux": import_path.default.join(import_os.default.homedir(), ".config", "microsoft-edge-dev"),
"darwin": import_path.default.join(import_os.default.homedir(), "Library", "Application Support", "Microsoft Edge Dev"),
"win32": import_path.default.join(process.env.LOCALAPPDATA || import_path.default.join(import_os.default.homedir(), "AppData", "Local"), "Microsoft", "Edge Dev", "User Data")
}],
["msedge-canary", {
"linux": import_path.default.join(import_os.default.homedir(), ".config", "microsoft-edge-canary"),
"darwin": import_path.default.join(import_os.default.homedir(), "Library", "Application Support", "Microsoft Edge Canary"),
"win32": import_path.default.join(process.env.LOCALAPPDATA || import_path.default.join(import_os.default.homedir(), "AppData", "Local"), "Microsoft", "Edge SxS", "User Data")
}]
]);
}
});
// packages/utils/third_party/pixelmatch.js
var require_pixelmatch = __commonJS({
"packages/utils/third_party/pixelmatch.js"(exports2, module2) {
"use strict";
module2.exports = pixelmatch2;
var defaultOptions = {
threshold: 0.1,
// matching threshold (0 to 1); smaller is more sensitive
includeAA: false,
// whether to skip anti-aliasing detection
alpha: 0.1,
// opacity of original image in diff output
aaColor: [255, 255, 0],
// color of anti-aliased pixels in diff output
diffColor: [255, 0, 0],
// color of different pixels in diff output
diffColorAlt: null,
// whether to detect dark on light differences between img1 and img2 and set an alternative color to differentiate between the two
diffMask: false
// draw the diff over a transparent background (a mask)
};
function pixelmatch2(img1, img2, output, width, height, options2) {
if (!isPixelData(img1) || !isPixelData(img2) || output && !isPixelData(output))
throw new Error("Image data: Uint8Array, Uint8ClampedArray or Buffer expected.");
if (img1.length !== img2.length || output && output.length !== img1.length)
throw new Error("Image sizes do not match.");
if (img1.length !== width * height * 4) throw new Error("Image data size does not match width/height.");
options2 = Object.assign({}, defaultOptions, options2);
const len = width * height;
const a32 = new Uint32Array(img1.buffer, img1.byteOffset, len);
const b32 = new Uint32Array(img2.buffer, img2.byteOffset, len);
let identical = true;
for (let i = 0; i < len; i++) {
if (a32[i] !== b32[i]) {
identical = false;
break;
}
}
if (identical) {
if (output && !options2.diffMask) {
for (let i = 0; i < len; i++) drawGrayPixel(img1, 4 * i, options2.alpha, output);
}
return 0;
}
const maxDelta = 35215 * options2.threshold * options2.threshold;
let diff2 = 0;
for (let y = 0; y < height; y++) {
for (let x = 0; x < width; x++) {
const pos = (y * width + x) * 4;
const delta = colorDelta(img1, img2, pos, pos);
if (Math.abs(delta) > maxDelta) {
if (!options2.includeAA && (antialiased(img1, x, y, width, height, img2) || antialiased(img2, x, y, width, height, img1))) {
if (output && !options2.diffMask) drawPixel2(output, pos, ...options2.aaColor);
} else {
if (output) {
drawPixel2(output, pos, ...delta < 0 && options2.diffColorAlt || options2.diffColor);
}
diff2++;
}
} else if (output) {
if (!options2.diffMask) drawGrayPixel(img1, pos, options2.alpha, output);
}
}
}
return diff2;
}
function isPixelData(arr) {
return ArrayBuffer.isView(arr) && arr.constructor.BYTES_PER_ELEMENT === 1;
}
function antialiased(img, x1, y1, width, height, img2) {
const x0 = Math.max(x1 - 1, 0);
const y0 = Math.max(y1 - 1, 0);
const x2 = Math.min(x1 + 1, width - 1);
const y2 = Math.min(y1 + 1, height - 1);
const pos = (y1 * width + x1) * 4;
let zeroes = x1 === x0 || x1 === x2 || y1 === y0 || y1 === y2 ? 1 : 0;
let min = 0;
let max = 0;
let minX, minY, maxX, maxY;
for (let x = x0; x <= x2; x++) {
for (let y = y0; y <= y2; y++) {
if (x === x1 && y === y1) continue;
const delta = colorDelta(img, img, pos, (y * width + x) * 4, true);
if (delta === 0) {
zeroes++;
if (zeroes > 2) return false;
} else if (delta < min) {
min = delta;
minX = x;
minY = y;
} else if (delta > max) {
max = delta;
maxX = x;
maxY = y;
}
}
}
if (min === 0 || max === 0) return false;
return hasManySiblings(img, minX, minY, width, height) && hasManySiblings(img2, minX, minY, width, height) || hasManySiblings(img, maxX, maxY, width, height) && hasManySiblings(img2, maxX, maxY, width, height);
}
function hasManySiblings(img, x1, y1, width, height) {
const x0 = Math.max(x1 - 1, 0);
const y0 = Math.max(y1 - 1, 0);
const x2 = Math.min(x1 + 1, width - 1);
const y2 = Math.min(y1 + 1, height - 1);
const pos = (y1 * width + x1) * 4;
let zeroes = x1 === x0 || x1 === x2 || y1 === y0 || y1 === y2 ? 1 : 0;
for (let x = x0; x <= x2; x++) {
for (let y = y0; y <= y2; y++) {
if (x === x1 && y === y1) continue;
const pos2 = (y * width + x) * 4;
if (img[pos] === img[pos2] && img[pos + 1] === img[pos2 + 1] && img[pos + 2] === img[pos2 + 2] && img[pos + 3] === img[pos2 + 3]) zeroes++;
if (zeroes > 2) return true;
}
}
return false;
}
function colorDelta(img1, img2, k, m, yOnly) {
let r1 = img1[k + 0];
let g1 = img1[k + 1];
let b1 = img1[k + 2];
let a1 = img1[k + 3];
let r2 = img2[m + 0];
let g2 = img2[m + 1];
let b2 = img2[m + 2];
let a2 = img2[m + 3];
if (a1 === a2 && r1 === r2 && g1 === g2 && b1 === b2) return 0;
if (a1 < 255) {
a1 /= 255;
r1 = blend(r1, a1);
g1 = blend(g1, a1);
b1 = blend(b1, a1);
}
if (a2 < 255) {
a2 /= 255;
r2 = blend(r2, a2);
g2 = blend(g2, a2);
b2 = blend(b2, a2);
}
const y1 = rgb2y(r1, g1, b1);
const y2 = rgb2y(r2, g2, b2);
const y = y1 - y2;
if (yOnly) return y;
const i = rgb2i(r1, g1, b1) - rgb2i(r2, g2, b2);
const q = rgb2q(r1, g1, b1) - rgb2q(r2, g2, b2);
const delta = 0.5053 * y * y + 0.299 * i * i + 0.1957 * q * q;
return y1 > y2 ? -delta : delta;
}
function rgb2y(r, g, b) {
return r * 0.29889531 + g * 0.58662247 + b * 0.11448223;
}
function rgb2i(r, g, b) {
return r * 0.59597799 - g * 0.2741761 - b * 0.32180189;
}
function rgb2q(r, g, b) {
return r * 0.21147017 - g * 0.52261711 + b * 0.31114694;
}
function blend(c, a) {
return 255 + (c - 255) * a;
}
function drawPixel2(output, pos, r, g, b) {
output[pos + 0] = r;
output[pos + 1] = g;
output[pos + 2] = b;
output[pos + 3] = 255;
}
function drawGrayPixel(img, i, alpha, output) {
const r = img[i + 0];
const g = img[i + 1];
const b = img[i + 2];
const val = blend(rgb2y(r, g, b), alpha * img[i + 3] / 255);
drawPixel2(output, i, val, val, val);
}
}
});
// packages/utils/image_tools/colorUtils.ts
function blendWithWhite(c, a) {
return 255 + (c - 255) * a;
}
function rgb2gray(r, g, b) {
return 77 * r + 150 * g + 29 * b + 128 >> 8;
}
function colorDeltaE94(rgb1, rgb2) {
const [l1, a1, b1] = xyz2lab(srgb2xyz(rgb1));
const [l2, a2, b2] = xyz2lab(srgb2xyz(rgb2));
const deltaL = l1 - l2;
const deltaA = a1 - a2;
const deltaB = b1 - b2;
const c1 = Math.sqrt(a1 ** 2 + b1 ** 2);
const c2 = Math.sqrt(a2 ** 2 + b2 ** 2);
const deltaC = c1 - c2;
let deltaH = deltaA ** 2 + deltaB ** 2 - deltaC ** 2;
deltaH = deltaH < 0 ? 0 : Math.sqrt(deltaH);
const k1 = 0.045;
const k2 = 0.015;
const kL = 1;
const kC = 1;
const kH = 1;
const sC = 1 + k1 * c1;
const sH = 1 + k2 * c1;
const sL = 1;
return Math.sqrt((deltaL / sL / kL) ** 2 + (deltaC / sC / kC) ** 2 + (deltaH / sH / kH) ** 2);
}
function srgb2xyz(rgb) {
let r = rgb[0] / 255;
let g = rgb[1] / 255;
let b = rgb[2] / 255;
r = r > 0.04045 ? Math.pow((r + 0.055) / 1.055, 2.4) : r / 12.92;
g = g > 0.04045 ? Math.pow((g + 0.055) / 1.055, 2.4) : g / 12.92;
b = b > 0.04045 ? Math.pow((b + 0.055) / 1.055, 2.4) : b / 12.92;
return [
r * 0.4124 + g * 0.3576 + b * 0.1805,
r * 0.2126 + g * 0.7152 + b * 0.0722,
r * 0.0193 + g * 0.1192 + b * 0.9505
];
}
function xyz2lab(xyz) {
const x = xyz[0] / 0.950489;
const y = xyz[1];
const z30 = xyz[2] / 1.08884;
const fx = x > sigma_pow3 ? x ** (1 / 3) : x / 3 / sigma_pow2 + 4 / 29;
const fy = y > sigma_pow3 ? y ** (1 / 3) : y / 3 / sigma_pow2 + 4 / 29;
const fz = z30 > sigma_pow3 ? z30 ** (1 / 3) : z30 / 3 / sigma_pow2 + 4 / 29;
const l = 116 * fy - 16;
const a = 500 * (fx - fy);
const b = 200 * (fy - fz);
return [l, a, b];
}
var sigma_pow2, sigma_pow3;
var init_colorUtils = __esm({
"packages/utils/image_tools/colorUtils.ts"() {
"use strict";
sigma_pow2 = 6 * 6 / 29 / 29;
sigma_pow3 = 6 * 6 * 6 / 29 / 29 / 29;
}
});
// packages/utils/image_tools/imageChannel.ts
var ImageChannel;
var init_imageChannel = __esm({
"packages/utils/image_tools/imageChannel.ts"() {
"use strict";
init_colorUtils();
ImageChannel = class _ImageChannel {
static intoRGB(width, height, data, options2 = {}) {
const {
paddingSize = 0,
paddingColorOdd = [255, 0, 255],
paddingColorEven = [0, 255, 0]
} = options2;
const newWidth = width + 2 * paddingSize;
const newHeight = height + 2 * paddingSize;
const r = new Uint8Array(newWidth * newHeight);
const g = new Uint8Array(newWidth * newHeight);
const b = new Uint8Array(newWidth * newHeight);
for (let y = 0; y < newHeight; ++y) {
for (let x = 0; x < newWidth; ++x) {
const index = y * newWidth + x;
if (y >= paddingSize && y < newHeight - paddingSize && x >= paddingSize && x < newWidth - paddingSize) {
const offset = ((y - paddingSize) * width + (x - paddingSize)) * 4;
const alpha = data[offset + 3] === 255 ? 1 : data[offset + 3] / 255;
r[index] = blendWithWhite(data[offset], alpha);
g[index] = blendWithWhite(data[offset + 1], alpha);
b[index] = blendWithWhite(data[offset + 2], alpha);
} else {
const color = (y + x) % 2 === 0 ? paddingColorEven : paddingColorOdd;
r[index] = color[0];
g[index] = color[1];
b[index] = color[2];
}
}
}
return [
new _ImageChannel(newWidth, newHeight, r),
new _ImageChannel(newWidth, newHeight, g),
new _ImageChannel(newWidth, newHeight, b)
];
}
constructor(width, height, data) {
this.data = data;
this.width = width;
this.height = height;
}
get(x, y) {
return this.data[y * this.width + x];
}
boundXY(x, y) {
return [
Math.min(Math.max(x, 0), this.width - 1),
Math.min(Math.max(y, 0), this.height - 1)
];
}
};
}
});
// packages/utils/image_tools/stats.ts
function ssim(stats2, x1, y1, x2, y2) {
const mean1 = stats2.meanC1(x1, y1, x2, y2);
const mean2 = stats2.meanC2(x1, y1, x2, y2);
const var1 = stats2.varianceC1(x1, y1, x2, y2);
const var2 = stats2.varianceC2(x1, y1, x2, y2);
const cov = stats2.covariance(x1, y1, x2, y2);
const c1 = (0.01 * DYNAMIC_RANGE) ** 2;
const c2 = (0.03 * DYNAMIC_RANGE) ** 2;
return (2 * mean1 * mean2 + c1) * (2 * cov + c2) / (mean1 ** 2 + mean2 ** 2 + c1) / (var1 + var2 + c2);
}
var DYNAMIC_RANGE, FastStats;
var init_stats = __esm({
"packages/utils/image_tools/stats.ts"() {
"use strict";
DYNAMIC_RANGE = 2 ** 8 - 1;
FastStats = class {
constructor(c1, c2) {
this.c1 = c1;
this.c2 = c2;
const { width, height } = c1;
this._partialSumC1 = new Array(width * height);
this._partialSumC2 = new Array(width * height);
this._partialSumSq1 = new Array(width * height);
this._partialSumSq2 = new Array(width * height);
this._partialSumMult = new Array(width * height);
const recalc = (mx, idx, initial, x, y) => {
mx[idx] = initial;
if (y > 0)
mx[idx] += mx[(y - 1) * width + x];
if (x > 0)
mx[idx] += mx[y * width + x - 1];
if (x > 0 && y > 0)
mx[idx] -= mx[(y - 1) * width + x - 1];
};
for (let y = 0; y < height; ++y) {
for (let x = 0; x < width; ++x) {
const idx = y * width + x;
recalc(this._partialSumC1, idx, this.c1.data[idx], x, y);
recalc(this._partialSumC2, idx, this.c2.data[idx], x, y);
recalc(this._partialSumSq1, idx, this.c1.data[idx] * this.c1.data[idx], x, y);
recalc(this._partialSumSq2, idx, this.c2.data[idx] * this.c2.data[idx], x, y);
recalc(this._partialSumMult, idx, this.c1.data[idx] * this.c2.data[idx], x, y);
}
}
}
_sum(partialSum, x1, y1, x2, y2) {
const width = this.c1.width;
let result2 = partialSum[y2 * width + x2];
if (y1 > 0)
result2 -= partialSum[(y1 - 1) * width + x2];
if (x1 > 0)
result2 -= partialSum[y2 * width + x1 - 1];
if (x1 > 0 && y1 > 0)
result2 += partialSum[(y1 - 1) * width + x1 - 1];
return result2;
}
meanC1(x1, y1, x2, y2) {
const N = (y2 - y1 + 1) * (x2 - x1 + 1);
return this._sum(this._partialSumC1, x1, y1, x2, y2) / N;
}
meanC2(x1, y1, x2, y2) {
const N = (y2 - y1 + 1) * (x2 - x1 + 1);
return this._sum(this._partialSumC2, x1, y1, x2, y2) / N;
}
varianceC1(x1, y1, x2, y2) {
const N = (y2 - y1 + 1) * (x2 - x1 + 1);
return (this._sum(this._partialSumSq1, x1, y1, x2, y2) - this._sum(this._partialSumC1, x1, y1, x2, y2) ** 2 / N) / N;
}
varianceC2(x1, y1, x2, y2) {
const N = (y2 - y1 + 1) * (x2 - x1 + 1);
return (this._sum(this._partialSumSq2, x1, y1, x2, y2) - this._sum(this._partialSumC2, x1, y1, x2, y2) ** 2 / N) / N;
}
covariance(x1, y1, x2, y2) {
const N = (y2 - y1 + 1) * (x2 - x1 + 1);
return (this._sum(this._partialSumMult, x1, y1, x2, y2) - this._sum(this._partialSumC1, x1, y1, x2, y2) * this._sum(this._partialSumC2, x1, y1, x2, y2) / N) / N;
}
};
}
});
// packages/utils/image_tools/compare.ts
function drawPixel(width, data, x, y, r, g, b) {
const idx = (y * width + x) * 4;
data[idx + 0] = r;
data[idx + 1] = g;
data[idx + 2] = b;
data[idx + 3] = 255;
}
function compare(actual, expected, diff2, width, height, options2 = {}) {
const {
maxColorDeltaE94 = 1
} = options2;
const paddingSize = Math.max(VARIANCE_WINDOW_RADIUS, SSIM_WINDOW_RADIUS);
const paddingColorEven = [255, 0, 255];
const paddingColorOdd = [0, 255, 0];
const [r1, g1, b1] = ImageChannel.intoRGB(width, height, expected, {
paddingSize,
paddingColorEven,
paddingColorOdd
});
const [r2, g2, b2] = ImageChannel.intoRGB(width, height, actual, {
paddingSize,
paddingColorEven,
paddingColorOdd
});
const noop = (x, y) => {
};
const drawRedPixel = diff2 ? (x, y) => drawPixel(width, diff2, x - paddingSize, y - paddingSize, 255, 0, 0) : noop;
const drawYellowPixel = diff2 ? (x, y) => drawPixel(width, diff2, x - paddingSize, y - paddingSize, 255, 255, 0) : noop;
const drawGrayPixel = diff2 ? (x, y) => {
const gray = rgb2gray(r1.get(x, y), g1.get(x, y), b1.get(x, y));
const value2 = blendWithWhite(gray, 0.1);
drawPixel(width, diff2, x - paddingSize, y - paddingSize, value2, value2, value2);
} : noop;
let fastR;
let fastG;
let fastB;
let diffCount = 0;
for (let y = paddingSize; y < r1.height - paddingSize; ++y) {
for (let x = paddingSize; x < r1.width - paddingSize; ++x) {
if (r1.get(x, y) === r2.get(x, y) && g1.get(x, y) === g2.get(x, y) && b1.get(x, y) === b2.get(x, y)) {
drawGrayPixel(x, y);
continue;
}
const delta = colorDeltaE94(
[r1.get(x, y), g1.get(x, y), b1.get(x, y)],
[r2.get(x, y), g2.get(x, y), b2.get(x, y)]
);
if (delta <= maxColorDeltaE94) {
drawGrayPixel(x, y);
continue;
}
if (!fastR || !fastG || !fastB) {
fastR = new FastStats(r1, r2);
fastG = new FastStats(g1, g2);
fastB = new FastStats(b1, b2);
}
const [varX1, varY1] = r1.boundXY(x - VARIANCE_WINDOW_RADIUS, y - VARIANCE_WINDOW_RADIUS);
const [varX2, varY2] = r1.boundXY(x + VARIANCE_WINDOW_RADIUS, y + VARIANCE_WINDOW_RADIUS);
const var1 = fastR.varianceC1(varX1, varY1, varX2, varY2) + fastG.varianceC1(varX1, varY1, varX2, varY2) + fastB.varianceC1(varX1, varY1, varX2, varY2);
const var2 = fastR.varianceC2(varX1, varY1, varX2, varY2) + fastG.varianceC2(varX1, varY1, varX2, varY2) + fastB.varianceC2(varX1, varY1, varX2, varY2);
if (var1 === 0 || var2 === 0) {
drawRedPixel(x, y);
++diffCount;
continue;
}
const [ssimX1, ssimY1] = r1.boundXY(x - SSIM_WINDOW_RADIUS, y - SSIM_WINDOW_RADIUS);
const [ssimX2, ssimY2] = r1.boundXY(x + SSIM_WINDOW_RADIUS, y + SSIM_WINDOW_RADIUS);
const ssimRGB = (ssim(fastR, ssimX1, ssimY1, ssimX2, ssimY2) + ssim(fastG, ssimX1, ssimY1, ssimX2, ssimY2) + ssim(fastB, ssimX1, ssimY1, ssimX2, ssimY2)) / 3;
const isAntialiased = ssimRGB >= 0.99;
if (isAntialiased) {
drawYellowPixel(x, y);
} else {
drawRedPixel(x, y);
++diffCount;
}
}
}
return diffCount;
}
var SSIM_WINDOW_RADIUS, VARIANCE_WINDOW_RADIUS;
var init_compare = __esm({
"packages/utils/image_tools/compare.ts"() {
"use strict";
init_colorUtils();
init_imageChannel();
init_stats();
SSIM_WINDOW_RADIUS = 15;
VARIANCE_WINDOW_RADIUS = 1;
}
});
// packages/utils/comparators.ts
function getComparator(mimeType) {
if (mimeType === "image/png")
return compareImages.bind(null, "image/png");
if (mimeType === "image/jpeg")
return compareImages.bind(null, "image/jpeg");
if (mimeType === "text/plain")
return compareText;
return compareBuffersOrStrings;
}
function compareBuffersOrStrings(actualBuffer, expectedBuffer) {
if (typeof actualBuffer === "string")
return compareText(actualBuffer, expectedBuffer);
if (!actualBuffer || !(actualBuffer instanceof Buffer))
return { errorMessage: "Actual result should be a Buffer or a string." };
if (Buffer.compare(actualBuffer, expectedBuffer))
return { errorMessage: "Buffers differ" };
return null;
}
function compareImages(mimeType, actualBuffer, expectedBuffer, options2 = {}) {
if (!actualBuffer || !(actualBuffer instanceof Buffer))
return { errorMessage: "Actual result should be a Buffer." };
validateBuffer(expectedBuffer, mimeType);
let actual = mimeType === "image/png" ? PNG.sync.read(actualBuffer) : jpegjs.decode(actualBuffer, { maxMemoryUsageInMB: JPEG_JS_MAX_BUFFER_SIZE_IN_MB });
let expected = mimeType === "image/png" ? PNG.sync.read(expectedBuffer) : jpegjs.decode(expectedBuffer, { maxMemoryUsageInMB: JPEG_JS_MAX_BUFFER_SIZE_IN_MB });
const size = { width: Math.max(expected.width, actual.width), height: Math.max(expected.height, actual.height) };
let sizesMismatchError = "";
if (expected.width !== actual.width || expected.height !== actual.height) {
sizesMismatchError = `Expected an image ${expected.width}px by ${expected.height}px, received ${actual.width}px by ${actual.height}px. `;
actual = padImageToSize(actual, size);
expected = padImageToSize(expected, size);
}
const diff2 = new PNG({ width: size.width, height: size.height });
let count;
if (options2.comparator === "ssim-cie94") {
count = compare(expected.data, actual.data, diff2.data, size.width, size.height, {
// All ΔE* formulae are originally designed to have the difference of 1.0 stand for a "just noticeable difference" (JND).
// See https://en.wikipedia.org/wiki/Color_difference#CIELAB_%CE%94E*
maxColorDeltaE94: 1
});
} else if ((options2.comparator ?? "pixelmatch") === "pixelmatch") {
count = (0, import_pixelmatch.default)(expected.data, actual.data, diff2.data, size.width, size.height, {
threshold: options2.threshold ?? 0.2
});
} else {
throw new Error(`Configuration specifies unknown comparator "${options2.comparator}"`);
}
const maxDiffPixels1 = options2.maxDiffPixels;
const maxDiffPixels2 = options2.maxDiffPixelRatio !== void 0 ? expected.width * expected.height * options2.maxDiffPixelRatio : void 0;
let maxDiffPixels;
if (maxDiffPixels1 !== void 0 && maxDiffPixels2 !== void 0)
maxDiffPixels = Math.min(maxDiffPixels1, maxDiffPixels2);
else
maxDiffPixels = maxDiffPixels1 ?? maxDiffPixels2 ?? 0;
const ratio = Math.ceil(count / (expected.width * expected.height) * 100) / 100;
const pixelsMismatchError = count > maxDiffPixels ? `${count} pixels (ratio ${ratio.toFixed(2)} of all image pixels) are different.` : "";
if (pixelsMismatchError || sizesMismatchError)
return { errorMessage: sizesMismatchError + pixelsMismatchError, diff: PNG.sync.write(diff2) };
return null;
}
function validateBuffer(buffer, mimeType) {
if (mimeType === "image/png") {
const pngMagicNumber = [137, 80, 78, 71, 13, 10, 26, 10];
if (buffer.length < pngMagicNumber.length || !pngMagicNumber.every((byte, index) => buffer[index] === byte))
throw new Error("Could not decode expected image as PNG.");
} else if (mimeType === "image/jpeg") {
const jpegMagicNumber = [255, 216];
if (buffer.length < jpegMagicNumber.length || !jpegMagicNumber.every((byte, index) => buffer[index] === byte))
throw new Error("Could not decode expected image as JPEG.");
}
}
function compareText(actual, expectedBuffer) {
if (typeof actual !== "string")
return { errorMessage: "Actual result should be a string" };
let expected = expectedBuffer.toString("utf-8");
if (expected === actual)
return null;
if (!actual.endsWith("\n"))
actual += "\n";
if (!expected.endsWith("\n"))
expected += "\n";
const lines = diff.createPatch("file", expected, actual, void 0, void 0, { context: 5 }).split("\n");
const coloredLines = lines.slice(4).map((line) => {
if (line.startsWith("-"))
return colors.green(line);
if (line.startsWith("+"))
return colors.red(line);
if (line.startsWith("@@"))
return colors.dim(line);
return line;
});
const errorMessage = coloredLines.join("\n");
return { errorMessage };
}
var import_pixelmatch, jpegjs, colors, diff, PNG, JPEG_JS_MAX_BUFFER_SIZE_IN_MB;
var init_comparators = __esm({
"packages/utils/comparators.ts"() {
"use strict";
init_imageUtils();
import_pixelmatch = __toESM(require_pixelmatch());
init_compare();
jpegjs = require("./utilsBundle").jpegjs;
colors = require("./utilsBundle").colors;
diff = require("./utilsBundle").diff;
({ PNG } = require("./utilsBundle"));
JPEG_JS_MAX_BUFFER_SIZE_IN_MB = 5 * 1024;
}
});
// packages/utils/crypto.ts
function createGuid() {
return import_crypto.default.randomBytes(16).toString("hex");
}
function calculateSha1(buffer) {
const hash = import_crypto.default.createHash("sha1");
hash.update(buffer);
return hash.digest("hex");
}
function encodeBase128(value2) {
const bytes = [];
do {
let byte = value2 & 127;
value2 >>>= 7;
if (bytes.length > 0)
byte |= 128;
bytes.push(byte);
} while (value2 > 0);
return Buffer.from(bytes.reverse());
}
function generateSelfSignedCertificate() {
const { privateKey, publicKey } = import_crypto.default.generateKeyPairSync("rsa", { modulusLength: 2048 });
const publicKeyDer = publicKey.export({ type: "pkcs1", format: "der" });
const oneYearInMilliseconds = 365 * 24 * 60 * 60 * 1e3;
const notBefore = new Date((/* @__PURE__ */ new Date()).getTime() - oneYearInMilliseconds);
const notAfter = new Date((/* @__PURE__ */ new Date()).getTime() + oneYearInMilliseconds);
const tbsCertificate = DER.encodeSequence([
DER.encodeExplicitContextDependent(0, DER.encodeInteger(1)),
// version
DER.encodeInteger(1),
// serialNumber
DER.encodeSequence([
DER.encodeObjectIdentifier("1.2.840.113549.1.1.11"),
// sha256WithRSAEncryption PKCS #1
DER.encodeNull()
]),
// signature
DER.encodeSequence([
DER.encodeSet([
DER.encodeSequence([
DER.encodeObjectIdentifier("2.5.4.3"),
// commonName X.520 DN component
DER.encodePrintableString("localhost")
])
]),
DER.encodeSet([
DER.encodeSequence([
DER.encodeObjectIdentifier("2.5.4.10"),
// organizationName X.520 DN component
DER.encodePrintableString("Playwright Client Certificate Support")
])
])
]),
// issuer
DER.encodeSequence([
DER.encodeDate(notBefore),
// notBefore
DER.encodeDate(notAfter)
// notAfter
]),
// validity
DER.encodeSequence([
DER.encodeSet([
DER.encodeSequence([
DER.encodeObjectIdentifier("2.5.4.3"),
// commonName X.520 DN component
DER.encodePrintableString("localhost")
])
]),
DER.encodeSet([
DER.encodeSequence([
DER.encodeObjectIdentifier("2.5.4.10"),
// organizationName X.520 DN component
DER.encodePrintableString("Playwright Client Certificate Support")
])
])
]),
// subject
DER.encodeSequence([
DER.encodeSequence([
DER.encodeObjectIdentifier("1.2.840.113549.1.1.1"),
// rsaEncryption PKCS #1
DER.encodeNull()
]),
DER.encodeBitString(publicKeyDer)
])
// SubjectPublicKeyInfo
]);
const signature = import_crypto.default.sign("sha256", tbsCertificate, privateKey);
const certificate = DER.encodeSequence([
tbsCertificate,
DER.encodeSequence([
DER.encodeObjectIdentifier("1.2.840.113549.1.1.11"),
// sha256WithRSAEncryption PKCS #1
DER.encodeNull()
]),
DER.encodeBitString(signature)
]);
const certPem = [
"-----BEGIN CERTIFICATE-----",
// Split the base64 string into lines of 64 characters
certificate.toString("base64").match(/.{1,64}/g).join("\n"),
"-----END CERTIFICATE-----"
].join("\n");
return {
cert: certPem,
key: privateKey.export({ type: "pkcs1", format: "pem" })
};
}
var import_crypto, DER;
var init_crypto = __esm({
"packages/utils/crypto.ts"() {
"use strict";
import_crypto = __toESM(require("crypto"));
init_assert();
DER = class {
static encodeSequence(data) {
return this._encode(48, Buffer.concat(data));
}
static encodeInteger(data) {
assert(data >= -128 && data <= 127);
return this._encode(2, Buffer.from([data]));
}
static encodeObjectIdentifier(oid) {
const parts = oid.split(".").map((v) => Number(v));
const output = [encodeBase128(40 * parts[0] + parts[1])];
for (let i = 2; i < parts.length; i++)
output.push(encodeBase128(parts[i]));
return this._encode(6, Buffer.concat(output));
}
static encodeNull() {
return Buffer.from([5, 0]);
}
static encodeSet(data) {
assert(data.length === 1, "Only one item in the set is supported. We'd need to sort the data to support more.");
return this._encode(49, Buffer.concat(data));
}
static encodeExplicitContextDependent(tag, data) {
return this._encode(160 + tag, data);
}
static encodePrintableString(data) {
return this._encode(19, Buffer.from(data));
}
static encodeBitString(data) {
const unusedBits = 0;
const content = Buffer.concat([Buffer.from([unusedBits]), data]);
return this._encode(3, content);
}
static encodeDate(date) {
const year = date.getUTCFullYear();
const isGeneralizedTime = year >= 2050;
const parts = [
isGeneralizedTime ? year.toString() : year.toString().slice(-2),
(date.getUTCMonth() + 1).toString().padStart(2, "0"),
date.getUTCDate().toString().padStart(2, "0"),
date.getUTCHours().toString().padStart(2, "0"),
date.getUTCMinutes().toString().padStart(2, "0"),
date.getUTCSeconds().toString().padStart(2, "0")
];
const encodedDate = parts.join("") + "Z";
const tag = isGeneralizedTime ? 24 : 23;
return this._encode(tag, Buffer.from(encodedDate));
}
static _encode(tag, data) {
const lengthBytes = this._encodeLength(data.length);
return Buffer.concat([Buffer.from([tag]), lengthBytes, data]);
}
static _encodeLength(length) {
if (length < 128) {
return Buffer.from([length]);
} else {
const lengthBytes = [];
while (length > 0) {
lengthBytes.unshift(length & 255);
length >>= 8;
}
return Buffer.from([128 | lengthBytes.length, ...lengthBytes]);
}
}
};
}
});
// packages/utils/env.ts
function getFromENV(name) {
let value2 = process.env[name];
value2 = value2 === void 0 ? process.env[`npm_config_${name.toLowerCase()}`] : value2;
value2 = value2 === void 0 ? process.env[`npm_package_config_${name.toLowerCase()}`] : value2;
return value2;
}
function getAsBooleanFromENV(name, defaultValue) {
const value2 = getFromENV(name);
if (value2 === "false" || value2 === "0")
return false;
if (value2)
return true;
return !!defaultValue;
}
function getPackageManager() {
const env = process.env.npm_config_user_agent || "";
if (env.includes("yarn"))
return "yarn";
if (env.includes("pnpm"))
return "pnpm";
return "npm";
}
function getPackageManagerExecCommand() {
const packageManager = getPackageManager();
if (packageManager === "yarn")
return "yarn";
if (packageManager === "pnpm")
return "pnpm exec";
return "npx";
}
function isLikelyNpxGlobal() {
return process.argv.length >= 2 && process.argv[1].includes("_npx");
}
function setPlaywrightTestProcessEnv() {
return process.env["PLAYWRIGHT_TEST"] = "1";
}
function guessClientName() {
if (process.env.CLAUDECODE)
return "Claude Code";
if (process.env.COPILOT_CLI)
return "GitHub Copilot";
return "playwright-cli";
}
function isCodingAgent() {
return !!process.env.CLAUDECODE || !!process.env.COPILOT_CLI;
}
var init_env = __esm({
"packages/utils/env.ts"() {
"use strict";
}
});
// packages/utils/debug.ts
function debugMode() {
if (_debugMode === "console")
return "console";
if (_debugMode === "0" || _debugMode === "false")
return "";
return _debugMode ? "inspector" : "";
}
function isUnderTest() {
return _isUnderTest;
}
var _debugMode, _isUnderTest;
var init_debug = __esm({
"packages/utils/debug.ts"() {
"use strict";
init_env();
_debugMode = getFromENV("PWDEBUG") || "";
_isUnderTest = getAsBooleanFromENV("PWTEST_UNDER_TEST");
}
});
// packages/utils/debugLogger.ts
var import_fs, debug, debugLoggerColorMap, DebugLogger, debugLogger, kLogCount, RecentLogsCollector;
var init_debugLogger = __esm({
"packages/utils/debugLogger.ts"() {
"use strict";
import_fs = __toESM(require("fs"));
debug = require("./utilsBundle").debug;
debugLoggerColorMap = {
"api": 45,
// cyan
"protocol": 34,
// green
"install": 34,
// green
"download": 34,
// green
"browser": 0,
// reset
"socks": 92,
// purple
"client-certificates": 92,
// purple
"error": 160,
// red,
"channel": 33,
// blue
"server": 45,
// cyan
"server:channel": 34,
// green
"server:metadata": 33,
// blue,
"recorder": 45
// cyan
};
DebugLogger = class {
constructor() {
this._debuggers = /* @__PURE__ */ new Map();
if (process.env.DEBUG_FILE) {
const ansiRegex2 = new RegExp([
"[\\u001B\\u009B][[\\]()#;?]*(?:(?:(?:[a-zA-Z\\d]*(?:;[-a-zA-Z\\d\\/#&.:=?%@~_]*)*)?\\u0007)",
"(?:(?:\\d{1,4}(?:;\\d{0,4})*)?[\\dA-PR-TZcf-ntqry=><~]))"
].join("|"), "g");
const stream3 = import_fs.default.createWriteStream(process.env.DEBUG_FILE);
debug.log = (data) => {
stream3.write(data.replace(ansiRegex2, ""));
stream3.write("\n");
};
}
}
log(name, message) {
let cachedDebugger = this._debuggers.get(name);
if (!cachedDebugger) {
cachedDebugger = debug(`pw:${name}`);
this._debuggers.set(name, cachedDebugger);
cachedDebugger.color = debugLoggerColorMap[name] || 0;
}
cachedDebugger(message);
}
isEnabled(name) {
return debug.enabled(`pw:${name}`);
}
};
debugLogger = new DebugLogger();
kLogCount = 150;
RecentLogsCollector = class {
constructor() {
this._logs = [];
this._listeners = [];
}
log(message) {
this._logs.push(message);
if (this._logs.length === kLogCount * 2)
this._logs.splice(0, kLogCount);
for (const listener of this._listeners)
listener(message);
}
recentLogs() {
if (this._logs.length > kLogCount)
return this._logs.slice(-kLogCount);
return this._logs;
}
onMessage(listener) {
for (const message of this._logs)
listener(message);
this._listeners.push(listener);
}
};
}
});
// packages/utils/eventsHelper.ts
var EventsHelper, eventsHelper;
var init_eventsHelper = __esm({
"packages/utils/eventsHelper.ts"() {
"use strict";
EventsHelper = class {
static addEventListener(emitter, eventName, handler) {
emitter.on(eventName, handler);
return { emitter, eventName, handler, dispose: async () => {
emitter.removeListener(eventName, handler);
} };
}
static removeEventListeners(listeners) {
for (const listener of listeners)
listener.emitter.removeListener(listener.eventName, listener.handler);
listeners.splice(0, listeners.length);
}
};
eventsHelper = EventsHelper;
}
});
// packages/utils/fileUtils.ts
async function mkdirIfNeeded(filePath) {
await import_fs2.default.promises.mkdir(import_path2.default.dirname(filePath), { recursive: true }).catch(() => {
});
}
async function removeFolders(dirs) {
return await Promise.all(dirs.map(
(dir) => import_fs2.default.promises.rm(dir, { recursive: true, force: true, maxRetries: 10 }).catch((e) => e)
));
}
function canAccessFile(file) {
if (!file)
return false;
try {
import_fs2.default.accessSync(file);
return true;
} catch (e) {
return false;
}
}
function isWritable(file) {
try {
import_fs2.default.accessSync(file, import_fs2.default.constants.W_OK);
return true;
} catch {
return false;
}
}
function isSystemDirectory(dir) {
const resolved = import_path2.default.resolve(dir);
if (process.platform === "win32") {
const systemRoot = import_path2.default.resolve(process.env.SystemRoot || "C:\\Windows");
return isPathInside(systemRoot.toLowerCase(), resolved.toLowerCase());
}
return resolved === "/";
}
async function copyFileAndMakeWritable(from, to) {
await import_fs2.default.promises.copyFile(from, to);
await import_fs2.default.promises.chmod(to, 436);
}
function addSuffixToFilePath(filePath, suffix) {
const ext = import_path2.default.extname(filePath);
const base = filePath.substring(0, filePath.length - ext.length);
return base + suffix + ext;
}
function sanitizeForFilePath(s) {
return s.replace(/[\x00-\x2C\x2E-\x2F\x3A-\x40\x5B-\x60\x7B-\x7F]+/g, "-");
}
function isPathInside(root, candidate) {
const resolvedRoot = import_path2.default.resolve(root);
const resolvedCandidate = import_path2.default.resolve(candidate);
if (resolvedCandidate === resolvedRoot)
return true;
return resolvedCandidate.startsWith(resolvedRoot + import_path2.default.sep);
}
function resolveWithinRoot(root, fileName) {
if (import_path2.default.isAbsolute(fileName))
return null;
const resolvedFile = import_path2.default.resolve(root, fileName);
return isPathInside(root, resolvedFile) ? resolvedFile : null;
}
function toPosixPath(aPath) {
return aPath.split(import_path2.default.sep).join(import_path2.default.posix.sep);
}
function makeSocketPath(domain, name) {
const userNameHash = calculateSha1(process.env.USERNAME || process.env.USER || "default").slice(0, 8);
if (process.platform === "win32") {
const socketsDir = process.env.PLAYWRIGHT_SOCKETS_DIR;
const suffix = socketsDir ? `-${calculateSha1(socketsDir).slice(0, 8)}` : "";
return `\\\\.\\pipe\\pw-${userNameHash}-${domain}-${name}${suffix}`;
}
const baseDir = process.env.PLAYWRIGHT_SOCKETS_DIR || import_path2.default.join(import_os2.default.tmpdir(), `pw-${userNameHash}`);
const dir = import_path2.default.join(baseDir, domain);
const result2 = import_path2.default.join(dir, `${name}.sock`);
import_fs2.default.mkdirSync(dir, { recursive: true });
return result2;
}
var import_fs2, import_os2, import_path2, existsAsync;
var init_fileUtils = __esm({
"packages/utils/fileUtils.ts"() {
"use strict";
import_fs2 = __toESM(require("fs"));
import_os2 = __toESM(require("os"));
import_path2 = __toESM(require("path"));
init_crypto();
existsAsync = (path59) => new Promise((resolve) => import_fs2.default.stat(path59, (err) => resolve(!err)));
}
});
// packages/utils/linuxUtils.ts
function getLinuxDistributionInfoSync() {
if (process.platform !== "linux")
return void 0;
if (!osRelease && !didFailToReadOSRelease) {
try {
const osReleaseText = import_fs3.default.readFileSync("/etc/os-release", "utf8");
const fields = parseOSReleaseText(osReleaseText);
osRelease = {
id: fields.get("id") ?? "",
version: fields.get("version_id") ?? ""
};
} catch (e) {
didFailToReadOSRelease = true;
}
}
return osRelease;
}
function parseOSReleaseText(osReleaseText) {
const fields = /* @__PURE__ */ new Map();
for (const line of osReleaseText.split("\n")) {
const tokens = line.split("=");
const name = tokens.shift();
let value2 = tokens.join("=").trim();
if (value2.startsWith('"') && value2.endsWith('"'))
value2 = value2.substring(1, value2.length - 1);
if (!name)
continue;
fields.set(name.toLowerCase(), value2);
}
return fields;
}
var import_fs3, didFailToReadOSRelease, osRelease;
var init_linuxUtils = __esm({
"packages/utils/linuxUtils.ts"() {
"use strict";
import_fs3 = __toESM(require("fs"));
didFailToReadOSRelease = false;
}
});
// packages/utils/hostPlatform.ts
function calculatePlatform() {
if (process.env.PLAYWRIGHT_HOST_PLATFORM_OVERRIDE) {
return {
hostPlatform: process.env.PLAYWRIGHT_HOST_PLATFORM_OVERRIDE,
isOfficiallySupportedPlatform: false
};
}
const platform = import_os3.default.platform();
if (platform === "darwin") {
const ver = import_os3.default.release().split(".").map((a) => parseInt(a, 10));
let macVersion = "";
if (ver[0] < 18) {
macVersion = "mac10.13";
} else if (ver[0] === 18) {
macVersion = "mac10.14";
} else if (ver[0] === 19) {
macVersion = "mac10.15";
} else if (ver[0] < 25) {
macVersion = "mac" + (ver[0] - 9);
if (import_os3.default.cpus().some((cpu) => cpu.model.includes("Apple")))
macVersion += "-arm64";
} else {
const LAST_STABLE_MACOS_MAJOR_VERSION = 26;
macVersion = "mac" + Math.min(ver[0] + 1, LAST_STABLE_MACOS_MAJOR_VERSION);
if (import_os3.default.cpus().some((cpu) => cpu.model.includes("Apple")))
macVersion += "-arm64";
}
return { hostPlatform: macVersion, isOfficiallySupportedPlatform: true };
}
if (platform === "linux") {
if (!["x64", "arm64"].includes(import_os3.default.arch()))
return { hostPlatform: "<unknown>", isOfficiallySupportedPlatform: false };
const archSuffix = "-" + import_os3.default.arch();
const distroInfo = getLinuxDistributionInfoSync();
if (distroInfo?.id === "ubuntu" || distroInfo?.id === "pop" || distroInfo?.id === "neon" || distroInfo?.id === "tuxedo") {
const isUbuntu = distroInfo?.id === "ubuntu";
const version3 = distroInfo?.version;
const major = parseInt(distroInfo.version, 10);
if (major < 20)
return { hostPlatform: "ubuntu18.04" + archSuffix, isOfficiallySupportedPlatform: false };
if (major < 22)
return { hostPlatform: "ubuntu20.04" + archSuffix, isOfficiallySupportedPlatform: isUbuntu && version3 === "20.04" };
if (major < 24)
return { hostPlatform: "ubuntu22.04" + archSuffix, isOfficiallySupportedPlatform: isUbuntu && version3 === "22.04" };
if (major < 26)
return { hostPlatform: "ubuntu24.04" + archSuffix, isOfficiallySupportedPlatform: isUbuntu && version3 === "24.04" };
return { hostPlatform: "ubuntu" + distroInfo.version + archSuffix, isOfficiallySupportedPlatform: false };
}
if (distroInfo?.id === "linuxmint") {
const mintMajor = parseInt(distroInfo.version, 10);
if (mintMajor <= 20)
return { hostPlatform: "ubuntu20.04" + archSuffix, isOfficiallySupportedPlatform: false };
if (mintMajor === 21)
return { hostPlatform: "ubuntu22.04" + archSuffix, isOfficiallySupportedPlatform: false };
return { hostPlatform: "ubuntu24.04" + archSuffix, isOfficiallySupportedPlatform: false };
}
if (distroInfo?.id === "debian" || distroInfo?.id === "raspbian") {
const isOfficiallySupportedPlatform2 = distroInfo?.id === "debian";
if (distroInfo?.version === "11")
return { hostPlatform: "debian11" + archSuffix, isOfficiallySupportedPlatform: isOfficiallySupportedPlatform2 };
if (distroInfo?.version === "12")
return { hostPlatform: "debian12" + archSuffix, isOfficiallySupportedPlatform: isOfficiallySupportedPlatform2 };
if (distroInfo?.version === "13")
return { hostPlatform: "debian13" + archSuffix, isOfficiallySupportedPlatform: isOfficiallySupportedPlatform2 };
if (distroInfo?.version === "")
return { hostPlatform: "debian13" + archSuffix, isOfficiallySupportedPlatform: isOfficiallySupportedPlatform2 };
}
return { hostPlatform: "ubuntu24.04" + archSuffix, isOfficiallySupportedPlatform: false };
}
if (platform === "win32")
return { hostPlatform: "win64", isOfficiallySupportedPlatform: true };
return { hostPlatform: "<unknown>", isOfficiallySupportedPlatform: false };
}
function toShortPlatform(hostPlatform2) {
if (hostPlatform2 === "<unknown>")
return "<unknown>";
if (hostPlatform2 === "win64")
return "win-x64";
if (hostPlatform2.startsWith("mac"))
return hostPlatform2.endsWith("arm64") ? "mac-arm64" : "mac-x64";
return hostPlatform2.endsWith("arm64") ? "linux-arm64" : "linux-x64";
}
var import_os3, hostPlatform, isOfficiallySupportedPlatform, shortPlatform;
var init_hostPlatform = __esm({
"packages/utils/hostPlatform.ts"() {
"use strict";
import_os3 = __toESM(require("os"));
init_linuxUtils();
({ hostPlatform, isOfficiallySupportedPlatform } = calculatePlatform());
shortPlatform = toShortPlatform(hostPlatform);
}
});
// packages/utils/happyEyeballs.ts
async function createSocket(host, port) {
return new Promise((resolve, reject) => {
if (import_net.default.isIP(host)) {
const socket = import_net.default.createConnection({ host, port });
socket.on("connect", () => resolve(socket));
socket.on("error", (error) => reject(error));
} else {
createConnectionAsync(
{ host, port },
(err, socket) => {
if (err)
reject(err);
if (socket)
resolve(socket);
},
/* useTLS */
false
).catch((err) => reject(err));
}
});
}
async function createConnectionAsync(options2, oncreate, useTLS) {
const lookup = options2.__testHookLookup || lookupAddresses;
const hostname = clientRequestArgsToHostName(options2);
const addresses = await lookup(hostname);
const dnsLookupAt = monotonicTime();
const sockets = /* @__PURE__ */ new Set();
let firstError;
let errorCount = 0;
const handleError = (socket, err) => {
if (!sockets.delete(socket))
return;
++errorCount;
firstError ??= err;
if (errorCount === addresses.length)
oncreate?.(firstError);
};
const connected = new ManualPromise();
for (const { address } of addresses) {
const socket = useTLS ? import_tls.default.connect({
...options2,
port: options2.port,
host: address,
servername: hostname
}) : import_net.default.createConnection({
...options2,
port: options2.port,
host: address
});
socket[kDNSLookupAt] = dnsLookupAt;
socket.on("connect", () => {
socket[kTCPConnectionAt] = monotonicTime();
connected.resolve();
oncreate?.(null, socket);
sockets.delete(socket);
for (const s of sockets)
s.destroy();
sockets.clear();
});
socket.on("timeout", () => {
socket.destroy();
handleError(socket, new Error("Connection timeout"));
});
socket.on("error", (e) => handleError(socket, e));
sockets.add(socket);
await Promise.race([
connected,
new Promise((f) => setTimeout(f, connectionAttemptDelayMs))
]);
if (connected.isDone())
break;
}
}
async function lookupAddresses(hostname) {
const [v4Result, v6Result] = await Promise.allSettled([
import_dns.default.promises.lookup(hostname, { all: true, family: 4 }),
import_dns.default.promises.lookup(hostname, { all: true, family: 6 })
]);
const v4Addresses = v4Result.status === "fulfilled" ? v4Result.value : [];
const v6Addresses = v6Result.status === "fulfilled" ? v6Result.value : [];
if (!v4Addresses.length && !v6Addresses.length) {
if (v4Result.status === "rejected")
throw v4Result.reason;
throw v6Result.reason;
}
const result2 = [];
for (let i = 0; i < Math.max(v4Addresses.length, v6Addresses.length); i++) {
if (v6Addresses[i])
result2.push(v6Addresses[i]);
if (v4Addresses[i])
result2.push(v4Addresses[i]);
}
return result2;
}
function clientRequestArgsToHostName(options2) {
if (options2.hostname)
return options2.hostname;
if (options2.host)
return options2.host;
throw new Error("Either options.hostname or options.host must be provided");
}
function timingForSocket(socket) {
return {
dnsLookupAt: socket[kDNSLookupAt],
tcpConnectionAt: socket[kTCPConnectionAt]
};
}
var import_dns, import_http, import_https, import_net, import_tls, connectionAttemptDelayMs, kDNSLookupAt, kTCPConnectionAt, HttpHappyEyeballsAgent, HttpsHappyEyeballsAgent, httpsHappyEyeballsAgent, httpHappyEyeballsAgent;
var init_happyEyeballs = __esm({
"packages/utils/happyEyeballs.ts"() {
"use strict";
import_dns = __toESM(require("dns"));
import_http = __toESM(require("http"));
import_https = __toESM(require("https"));
import_net = __toESM(require("net"));
import_tls = __toESM(require("tls"));
init_assert();
init_manualPromise();
init_time();
connectionAttemptDelayMs = 300;
kDNSLookupAt = Symbol("kDNSLookupAt");
kTCPConnectionAt = Symbol("kTCPConnectionAt");
HttpHappyEyeballsAgent = class extends import_http.default.Agent {
createConnection(options2, oncreate) {
if (import_net.default.isIP(clientRequestArgsToHostName(options2)))
return import_net.default.createConnection(options2);
createConnectionAsync(
options2,
oncreate,
/* useTLS */
false
).catch((err) => oncreate?.(err));
}
};
HttpsHappyEyeballsAgent = class extends import_https.default.Agent {
createConnection(options2, oncreate) {
if (import_net.default.isIP(clientRequestArgsToHostName(options2)))
return import_tls.default.connect(options2);
createConnectionAsync(
options2,
oncreate,
/* useTLS */
true
).catch((err) => oncreate?.(err));
}
};
httpsHappyEyeballsAgent = new HttpsHappyEyeballsAgent({ keepAlive: true });
httpHappyEyeballsAgent = new HttpHappyEyeballsAgent({ keepAlive: true });
}
});
// packages/utils/network.ts
function httpRequest(params2, onResponse, onError) {
let url2 = new URL(params2.url);
const options2 = {
method: params2.method || "GET",
headers: params2.headers
};
if (params2.rejectUnauthorized !== void 0)
options2.rejectUnauthorized = params2.rejectUnauthorized;
const proxyURL = getProxyForUrl(params2.url);
if (proxyURL) {
const parsedProxyURL = normalizeProxyURL(proxyURL);
if (params2.url.startsWith("http:")) {
options2.path = url2.toString();
url2 = parsedProxyURL;
} else {
options2.agent = new HttpsProxyAgent(parsedProxyURL);
}
}
options2.agent ??= url2.protocol === "https:" ? httpsHappyEyeballsAgent : httpHappyEyeballsAgent;
let cancelRequest;
const requestCallback = (res) => {
const statusCode = res.statusCode || 0;
if (statusCode >= 300 && statusCode < 400 && res.headers.location) {
request2.destroy();
cancelRequest = httpRequest({ ...params2, url: new URL(res.headers.location, params2.url).toString() }, onResponse, onError).cancel;
} else {
onResponse(res);
}
};
const request2 = url2.protocol === "https:" ? import_https2.default.request(url2, options2, requestCallback) : import_http2.default.request(url2, options2, requestCallback);
request2.on("error", onError);
if (params2.socketTimeout !== void 0) {
request2.setTimeout(params2.socketTimeout, () => {
onError(new Error(`Request to ${params2.url} timed out after ${params2.socketTimeout}ms`));
request2.abort();
});
}
cancelRequest = (e) => {
try {
request2.destroy(e);
} catch {
}
};
request2.end(params2.data);
return { cancel: (e) => cancelRequest(e) };
}
function shouldBypassProxy(url2, bypass) {
if (!bypass)
return false;
const domains = bypass.split(",").map((s) => {
s = s.trim();
if (!s.startsWith("."))
s = "." + s;
return s;
});
const domain = "." + url2.hostname;
return domains.some((d) => domain.endsWith(d));
}
function normalizeProxyURL(proxy) {
proxy = proxy.trim();
if (!/^\w+:\/\//.test(proxy))
proxy = "http://" + proxy;
return new URL(proxy);
}
function createProxyAgent(proxy, forUrl) {
if (!proxy)
return;
if (forUrl && proxy.bypass && shouldBypassProxy(forUrl, proxy.bypass))
return;
const proxyURL = normalizeProxyURL(proxy.server);
if (proxyURL.protocol?.startsWith("socks")) {
if (proxyURL.protocol === "socks5:")
proxyURL.protocol = "socks5h:";
else if (proxyURL.protocol === "socks4:")
proxyURL.protocol = "socks4a:";
return new SocksProxyAgent(proxyURL);
}
if (proxy.username) {
proxyURL.username = proxy.username;
proxyURL.password = proxy.password || "";
}
if (forUrl && ["ws:", "wss:"].includes(forUrl.protocol)) {
return new HttpsProxyAgent(proxyURL);
}
return new HttpsProxyAgent(proxyURL);
}
function createHttpServer(...args) {
const server = import_http2.default.createServer(...args);
decorateServer(server);
return server;
}
function createHttpsServer(...args) {
const server = import_https2.default.createServer(...args);
decorateServer(server);
return server;
}
function createHttp2Server(...args) {
const server = import_http22.default.createSecureServer(...args);
decorateServer(server);
return server;
}
async function startHttpServer(server, options2) {
const { host = "localhost", port = 0 } = options2;
const errorPromise = new ManualPromise();
const errorListener = (error) => errorPromise.reject(error);
server.on("error", errorListener);
try {
server.listen(port, host);
await Promise.race([
new Promise((cb) => server.once("listening", cb)),
errorPromise
]);
} finally {
server.removeListener("error", errorListener);
}
}
async function isURLAvailable(url2, ignoreHTTPSErrors, onLog, onStdErr) {
let statusCode = await httpStatusCode(url2, ignoreHTTPSErrors, onLog, onStdErr);
if (statusCode === 404 && url2.pathname === "/") {
const indexUrl = new URL(url2);
indexUrl.pathname = "/index.html";
statusCode = await httpStatusCode(indexUrl, ignoreHTTPSErrors, onLog, onStdErr);
}
return statusCode >= 200 && statusCode < 404;
}
async function httpStatusCode(url2, ignoreHTTPSErrors, onLog, onStdErr) {
return new Promise((resolve) => {
onLog?.(`HTTP GET: ${url2}`);
httpRequest({
url: url2.toString(),
headers: { Accept: "*/*" },
rejectUnauthorized: !ignoreHTTPSErrors
}, (res) => {
res.resume();
const statusCode = res.statusCode ?? 0;
onLog?.(`HTTP Status: ${statusCode}`);
resolve(statusCode);
}, (error) => {
if (error.code === "DEPTH_ZERO_SELF_SIGNED_CERT")
onStdErr?.(`[WebServer] Self-signed certificate detected. Try adding ignoreHTTPSErrors: true to config.webServer.`);
onLog?.(`Error while checking if ${url2} is available: ${error.message}`);
resolve(0);
});
});
}
function decorateServer(server) {
const sockets = /* @__PURE__ */ new Set();
server.on("connection", (socket) => {
sockets.add(socket);
socket.once("close", () => sockets.delete(socket));
});
const close3 = server.close;
server.close = (callback) => {
for (const socket of sockets)
socket.destroy();
sockets.clear();
return close3.call(server, callback);
};
}
var import_http2, import_http22, import_https2, HttpsProxyAgent, SocksProxyAgent, getProxyForUrl, NET_DEFAULT_TIMEOUT;
var init_network = __esm({
"packages/utils/network.ts"() {
"use strict";
import_http2 = __toESM(require("http"));
import_http22 = __toESM(require("http2"));
import_https2 = __toESM(require("https"));
init_manualPromise();
init_happyEyeballs();
({ HttpsProxyAgent } = require("./utilsBundle"));
({ SocksProxyAgent } = require("./utilsBundle"));
({ getProxyForUrl } = require("./utilsBundle"));
NET_DEFAULT_TIMEOUT = 3e4;
}
});
// packages/utils/httpServer.ts
function computeAllowedHosts(requested, bound) {
const loopback = /* @__PURE__ */ new Set(["127.0.0.1", "::1", "localhost"]);
const isLoopback = (h) => h !== void 0 && loopback.has(h.toLowerCase());
if (!isLoopback(requested) && requested !== void 0)
return null;
if (!isLoopback(bound) && requested === void 0)
return null;
return /* @__PURE__ */ new Set(["localhost", "127.0.0.1", "[::1]"]);
}
function urlHostFromAddress(address) {
return address.family === "IPv6" ? `[${address.address}]` : address.address;
}
function hostnameFromHostHeader(host) {
if (host.startsWith("[")) {
const end = host.indexOf("]");
return end < 0 ? host : host.substring(0, end + 1);
}
const colon = host.indexOf(":");
return colon < 0 ? host : host.substring(0, colon);
}
function serveFolder(folder) {
const server = new HttpServer();
const folderRoot = import_path3.default.resolve(folder);
server.routePrefix("/", (request2, response2) => {
let relativePath = new URL("http://localhost" + request2.url).pathname;
if (relativePath.startsWith("/trace/file")) {
const url2 = new URL("http://localhost" + request2.url);
const requested = url2.searchParams.get("path");
if (!requested)
return false;
const resolved = import_path3.default.resolve(requested);
if (!isPathInside(folderRoot, resolved)) {
response2.statusCode = 403;
response2.end();
return true;
}
try {
return server.serveFile(request2, response2, resolved);
} catch (e) {
return false;
}
}
if (relativePath === "/")
relativePath = "/index.html";
const absolutePath = import_path3.default.join(folder, ...relativePath.split("/"));
return server.serveFile(request2, response2, absolutePath);
});
return server;
}
var import_fs4, import_path3, mime, wsServer, HttpServer;
var init_httpServer = __esm({
"packages/utils/httpServer.ts"() {
"use strict";
import_fs4 = __toESM(require("fs"));
import_path3 = __toESM(require("path"));
init_assert();
init_crypto();
init_fileUtils();
init_network();
mime = require("./utilsBundle").mime;
({ wsServer } = require("./utilsBundle"));
HttpServer = class {
constructor() {
this._urlPrefixPrecise = "";
this._urlPrefixHumanReadable = "";
this._port = 0;
this._started = false;
this._routes = [];
// Allowed Host headers; null disables the check (host bound to a public address).
this._allowedHosts = null;
this._server = createHttpServer(this._onRequest.bind(this));
}
server() {
return this._server;
}
routePrefix(prefix, handler) {
this._routes.push({ prefix, handler });
}
routePath(path59, handler) {
this._routes.push({ exact: path59, handler });
}
port() {
return this._port;
}
createWebSocket(transportFactory, guid) {
assert(!this._wsGuid, "can only create one main websocket transport per server");
this._wsGuid = guid || createGuid();
const wsPath = "/" + this._wsGuid;
const wss = new wsServer({ noServer: true });
this._server.on("upgrade", (request2, socket, head) => {
const pathname = new URL(request2.url ?? "/", "http://localhost").pathname;
if (pathname !== wsPath)
return;
wss.handleUpgrade(request2, socket, head, (ws3) => wss.emit("connection", ws3, request2));
});
wss.on("connection", (ws3, request2) => {
const url2 = new URL(request2.url ?? "/", "http://localhost");
const transport = transportFactory(url2);
transport.sendEvent = (method, params2) => ws3.send(JSON.stringify({ method, params: params2 }));
transport.close = () => ws3.close();
transport.onconnect();
ws3.on("message", async (message) => {
const { id, method, params: params2 } = JSON.parse(String(message));
try {
const result2 = await transport.dispatch(method, params2);
ws3.send(JSON.stringify({ id, result: result2 }));
} catch (e) {
ws3.send(JSON.stringify({ id, error: String(e) }));
}
});
ws3.on("close", () => transport.onclose());
ws3.on("error", () => transport.onclose());
});
}
wsGuid() {
return this._wsGuid;
}
async createViteDevServer(options2) {
const loadVite = new Function('return import("vite")');
const vite = await loadVite();
return await vite.createServer({
root: options2.root,
base: options2.base,
server: {
middlewareMode: true,
// Dedicated path so Vite's HMR websocket does not collide with any
// websocket HttpServer owns via createWebSocket().
hmr: { path: "/__vite_hmr", server: this._server }
},
appType: "spa",
clearScreen: false
});
}
// HMR end
// Vite's middleware `next` callback for the "no middleware matched" case;
// emits a 404 only if nothing downstream already responded.
static notFoundFallback(response2) {
return () => {
if (!response2.headersSent) {
response2.statusCode = 404;
response2.end();
}
};
}
async start(options2 = {}) {
assert(!this._started, "server already started");
this._started = true;
const host = options2.host;
if (options2.preferredPort) {
try {
await startHttpServer(this._server, { port: options2.preferredPort, host });
} catch (e) {
if (!e || !e.message || !e.message.includes("EADDRINUSE"))
throw e;
await startHttpServer(this._server, { host });
}
} else {
await startHttpServer(this._server, { port: options2.port, host });
}
const address = this._server.address();
assert(address, "Could not bind server socket");
if (typeof address === "string") {
this._urlPrefixPrecise = address;
this._urlPrefixHumanReadable = address;
} else {
this._port = address.port;
this._urlPrefixPrecise = `http://${urlHostFromAddress(address)}:${address.port}`;
this._urlPrefixHumanReadable = `http://${host ?? "localhost"}:${address.port}`;
this._allowedHosts = computeAllowedHosts(host, address.address);
}
}
async stop() {
await new Promise((cb) => this._server.close(cb));
}
urlPrefix(purpose) {
return purpose === "human-readable" ? this._urlPrefixHumanReadable : this._urlPrefixPrecise;
}
serveFile(request2, response2, absoluteFilePath, headers) {
try {
for (const [name, value2] of Object.entries(headers || {}))
response2.setHeader(name, value2);
if (request2.headers.range)
this._serveRangeFile(request2, response2, absoluteFilePath);
else
this._serveFile(response2, absoluteFilePath);
return true;
} catch (e) {
return false;
}
}
_serveFile(response2, absoluteFilePath) {
const content = import_fs4.default.readFileSync(absoluteFilePath);
response2.statusCode = 200;
const contentType = mime.getType(import_path3.default.extname(absoluteFilePath)) || "application/octet-stream";
response2.setHeader("Content-Type", contentType);
response2.setHeader("Content-Length", content.byteLength);
response2.end(content);
}
_serveRangeFile(request2, response2, absoluteFilePath) {
const range = request2.headers.range;
if (!range || !range.startsWith("bytes=") || range.includes(", ") || [...range].filter((char) => char === "-").length !== 1) {
response2.statusCode = 400;
return response2.end("Bad request");
}
const [startStr, endStr] = range.replace(/bytes=/, "").split("-");
let start3;
let end;
const size = import_fs4.default.statSync(absoluteFilePath).size;
if (startStr !== "" && endStr === "") {
start3 = +startStr;
end = size - 1;
} else if (startStr === "" && endStr !== "") {
start3 = size - +endStr;
end = size - 1;
} else {
start3 = +startStr;
end = +endStr;
}
if (Number.isNaN(start3) || Number.isNaN(end) || start3 >= size || end >= size || start3 > end) {
response2.writeHead(416, {
"Content-Range": `bytes */${size}`
});
return response2.end();
}
response2.writeHead(206, {
"Content-Range": `bytes ${start3}-${end}/${size}`,
"Accept-Ranges": "bytes",
"Content-Length": end - start3 + 1,
"Content-Type": mime.getType(import_path3.default.extname(absoluteFilePath))
});
const readable = import_fs4.default.createReadStream(absoluteFilePath, { start: start3, end });
readable.pipe(response2);
}
_onRequest(request2, response2) {
if (request2.method === "OPTIONS") {
response2.writeHead(200);
response2.end();
return;
}
if (this._allowedHosts) {
const host = request2.headers.host?.toLowerCase();
const hostname = host ? hostnameFromHostHeader(host) : void 0;
if (!hostname || !this._allowedHosts.has(hostname)) {
response2.statusCode = 403;
response2.end();
return;
}
}
request2.on("error", () => response2.end());
try {
if (!request2.url) {
response2.end();
return;
}
const url2 = new URL("http://localhost" + request2.url);
for (const route2 of this._routes) {
if (route2.exact && url2.pathname === route2.exact && route2.handler(request2, response2))
return;
if (route2.prefix && url2.pathname.startsWith(route2.prefix) && route2.handler(request2, response2))
return;
}
response2.statusCode = 404;
response2.end();
} catch (e) {
response2.end();
}
}
};
}
});
// packages/utils/zones.ts
function currentZone() {
return asyncLocalStorage.getStore() ?? emptyZone;
}
var import_async_hooks, asyncLocalStorage, Zone, emptyZone;
var init_zones = __esm({
"packages/utils/zones.ts"() {
"use strict";
import_async_hooks = require("async_hooks");
asyncLocalStorage = new import_async_hooks.AsyncLocalStorage();
Zone = class _Zone {
constructor(asyncLocalStorage2, store) {
this._asyncLocalStorage = asyncLocalStorage2;
this._data = store;
}
with(type3, data) {
return new _Zone(this._asyncLocalStorage, new Map(this._data).set(type3, data));
}
without(type3) {
const data = type3 ? new Map(this._data) : /* @__PURE__ */ new Map();
data.delete(type3);
return new _Zone(this._asyncLocalStorage, data);
}
run(func) {
return this._asyncLocalStorage.run(this, func);
}
data(type3) {
return this._data.get(type3);
}
};
emptyZone = new Zone(asyncLocalStorage, /* @__PURE__ */ new Map());
}
});
// packages/utils/nodePlatform.ts
function setBoxedStackPrefixes(prefixes) {
boxedStackPrefixes = prefixes;
}
var import_crypto4, import_fs5, import_path4, util, import_stream, import_events, colors2, pipelineAsync, NodeZone, boxedStackPrefixes, nodePlatform, ReadableStreamImpl, WritableStreamImpl;
var init_nodePlatform = __esm({
"packages/utils/nodePlatform.ts"() {
"use strict";
import_crypto4 = __toESM(require("crypto"));
import_fs5 = __toESM(require("fs"));
import_path4 = __toESM(require("path"));
util = __toESM(require("util"));
import_stream = require("stream");
import_events = require("events");
init_debugLogger();
init_zones();
init_debug();
colors2 = require("./utilsBundle").colors;
pipelineAsync = util.promisify(import_stream.pipeline);
NodeZone = class _NodeZone {
constructor(zone) {
this._zone = zone;
}
push(data) {
return new _NodeZone(this._zone.with("apiZone", data));
}
pop() {
return new _NodeZone(this._zone.without("apiZone"));
}
run(func) {
return this._zone.run(func);
}
data() {
return this._zone.data("apiZone");
}
};
boxedStackPrefixes = [];
nodePlatform = (coreDir) => ({
name: "node",
boxedStackPrefixes: () => {
if (process.env.PWDEBUGIMPL)
return [];
return [coreDir, ...boxedStackPrefixes];
},
calculateSha1: (text2) => {
const sha1 = import_crypto4.default.createHash("sha1");
sha1.update(text2);
return Promise.resolve(sha1.digest("hex"));
},
colors: colors2,
coreDir,
createGuid: () => import_crypto4.default.randomBytes(16).toString("hex"),
defaultMaxListeners: () => import_events.EventEmitter.defaultMaxListeners,
fs: () => import_fs5.default,
env: process.env,
inspectCustom: util.inspect.custom,
isDebugMode: () => debugMode() === "inspector",
isJSDebuggerAttached: () => !!require("inspector").url(),
isLogEnabled(name) {
return debugLogger.isEnabled(name);
},
isUnderTest: () => isUnderTest(),
log(name, message) {
debugLogger.log(name, message);
},
path: () => import_path4.default,
pathSeparator: import_path4.default.sep,
showInternalStackFrames: () => !!process.env.PWDEBUGIMPL,
async streamFile(path59, stream3) {
await pipelineAsync(import_fs5.default.createReadStream(path59), stream3);
},
streamReadable: (channel) => {
return new ReadableStreamImpl(channel);
},
streamWritable: (channel) => {
return new WritableStreamImpl(channel);
},
zones: {
current: () => new NodeZone(currentZone()),
empty: new NodeZone(emptyZone)
}
});
ReadableStreamImpl = class extends import_stream.Readable {
constructor(channel) {
super();
this._channel = channel;
}
async _read() {
const result2 = await this._channel.read({ size: 1024 * 1024 });
if (result2.binary.byteLength)
this.push(result2.binary);
else
this.push(null);
}
_destroy(error, callback) {
this._channel.close().catch((e) => null);
super._destroy(error, callback);
}
};
WritableStreamImpl = class extends import_stream.Writable {
constructor(channel) {
super();
this._channel = channel;
}
async _write(chunk, encoding, callback) {
const error = await this._channel.write({ binary: typeof chunk === "string" ? Buffer.from(chunk) : chunk }).catch((e) => e);
callback(error || null);
}
async _final(callback) {
const error = await this._channel.close().catch((e) => e);
callback(error || null);
}
};
}
});
// packages/utils/processLauncher.ts
async function gracefullyCloseAll() {
await Promise.all(Array.from(gracefullyCloseSet).map((gracefullyClose) => gracefullyClose().catch((e) => {
})));
}
function gracefullyProcessExitDoNotHang(code, onExit2) {
const beforeExit = onExit2 ? () => onExit2().catch(() => {
}) : () => Promise.resolve();
const callback = () => beforeExit().then(() => process.exit(code));
setTimeout(callback, 3e4);
gracefullyCloseAll().then(callback);
}
function exitHandler() {
for (const kill of killSet)
kill();
}
function sigintHandler() {
const exitWithCode130 = () => {
if (isUnderTest()) {
setTimeout(() => process.exit(130), 1e3);
} else {
process.exit(130);
}
};
if (sigintHandlerCalled) {
process.off("SIGINT", sigintHandler);
for (const kill of killSet)
kill();
exitWithCode130();
} else {
sigintHandlerCalled = true;
gracefullyCloseAll().then(() => exitWithCode130());
}
}
function sigtermHandler() {
gracefullyCloseAll();
}
function sighupHandler() {
gracefullyCloseAll();
}
function addProcessHandlerIfNeeded(name) {
if (!installedHandlers.has(name)) {
installedHandlers.add(name);
process.on(name, processHandlers[name]);
}
}
function removeProcessHandlersIfNeeded() {
if (killSet.size)
return;
for (const handler of installedHandlers)
process.off(handler, processHandlers[handler]);
installedHandlers.clear();
}
async function launchProcess(options2) {
const stdio = options2.stdio === "pipe" ? ["ignore", "pipe", "pipe", "pipe", "pipe"] : ["pipe", "pipe", "pipe"];
options2.log(`<launching> ${options2.command} ${options2.args ? options2.args.join(" ") : ""}`);
const spawnOptions = {
// On non-windows platforms, `detached: true` makes child process a leader of a new
// process group, making it possible to kill child process tree with `.kill(-pid)` command.
// @see https://nodejs.org/api/child_process.html#child_process_options_detached
detached: process.platform !== "win32",
env: options2.env,
cwd: options2.cwd,
shell: options2.shell,
stdio
};
const spawnedProcess = childProcess.spawn(options2.command, options2.args || [], spawnOptions);
const cleanup = async () => {
options2.log(`[pid=${spawnedProcess.pid || "N/A"}] starting temporary directories cleanup`);
const errors = await removeFolders(options2.tempDirectories);
for (let i = 0; i < options2.tempDirectories.length; ++i) {
if (errors[i])
options2.log(`[pid=${spawnedProcess.pid || "N/A"}] exception while removing ${options2.tempDirectories[i]}: ${errors[i]}`);
}
options2.log(`[pid=${spawnedProcess.pid || "N/A"}] finished temporary directories cleanup`);
};
spawnedProcess.on("error", () => {
});
if (!spawnedProcess.pid) {
let failed;
const failedPromise = new Promise((f, r) => failed = f);
spawnedProcess.once("error", (error) => {
failed(new Error("Failed to launch: " + error));
});
return failedPromise.then(async (error) => {
await cleanup();
throw error;
});
}
options2.log(`<launched> pid=${spawnedProcess.pid}`);
const stdout = readline.createInterface({ input: spawnedProcess.stdout });
stdout.on("line", (data) => {
options2.log(`[pid=${spawnedProcess.pid}][out] ` + data);
});
const stderr = readline.createInterface({ input: spawnedProcess.stderr });
stderr.on("line", (data) => {
options2.log(`[pid=${spawnedProcess.pid}][err] ` + data);
});
let processClosed = false;
let fulfillCleanup = () => {
};
const waitForCleanup = new Promise((f) => fulfillCleanup = f);
spawnedProcess.once("close", (exitCode, signal) => {
options2.log(`[pid=${spawnedProcess.pid}] <process did exit: exitCode=${exitCode}, signal=${signal}>`);
processClosed = true;
gracefullyCloseSet.delete(gracefullyClose);
killSet.delete(killProcessAndCleanup);
removeProcessHandlersIfNeeded();
options2.onExit(exitCode, signal);
cleanup().then(fulfillCleanup);
});
addProcessHandlerIfNeeded("exit");
if (options2.handleSIGINT)
addProcessHandlerIfNeeded("SIGINT");
if (options2.handleSIGTERM)
addProcessHandlerIfNeeded("SIGTERM");
if (options2.handleSIGHUP)
addProcessHandlerIfNeeded("SIGHUP");
gracefullyCloseSet.add(gracefullyClose);
killSet.add(killProcessAndCleanup);
let gracefullyClosing = false;
async function gracefullyClose() {
if (gracefullyClosing) {
options2.log(`[pid=${spawnedProcess.pid}] <forcefully close>`);
killProcess();
await waitForCleanup;
return;
}
gracefullyClosing = true;
options2.log(`[pid=${spawnedProcess.pid}] <gracefully close start>`);
await options2.attemptToGracefullyClose().catch(() => killProcess());
await waitForCleanup;
options2.log(`[pid=${spawnedProcess.pid}] <gracefully close end>`);
}
function killProcess() {
gracefullyCloseSet.delete(gracefullyClose);
killSet.delete(killProcessAndCleanup);
removeProcessHandlersIfNeeded();
options2.log(`[pid=${spawnedProcess.pid}] <kill>`);
if (spawnedProcess.pid && !spawnedProcess.killed && !processClosed) {
options2.log(`[pid=${spawnedProcess.pid}] <will force kill>`);
try {
if (process.platform === "win32") {
const taskkillProcess = childProcess.spawnSync(`taskkill /pid ${spawnedProcess.pid} /T /F`, { shell: true });
const [stdout2, stderr2] = [taskkillProcess.stdout.toString(), taskkillProcess.stderr.toString()];
if (stdout2)
options2.log(`[pid=${spawnedProcess.pid}] taskkill stdout: ${stdout2}`);
if (stderr2)
options2.log(`[pid=${spawnedProcess.pid}] taskkill stderr: ${stderr2}`);
} else {
process.kill(-spawnedProcess.pid, "SIGKILL");
}
} catch (e) {
options2.log(`[pid=${spawnedProcess.pid}] exception while trying to kill process: ${e}`);
}
} else {
options2.log(`[pid=${spawnedProcess.pid}] <skipped force kill spawnedProcess.killed=${spawnedProcess.killed} processClosed=${processClosed}>`);
}
}
function killProcessAndCleanup() {
killProcess();
options2.log(`[pid=${spawnedProcess.pid || "N/A"}] starting temporary directories cleanup`);
for (const dir of options2.tempDirectories) {
try {
import_fs6.default.rmSync(dir, { force: true, recursive: true, maxRetries: 5 });
} catch (e) {
options2.log(`[pid=${spawnedProcess.pid || "N/A"}] exception while removing ${dir}: ${e}`);
}
}
options2.log(`[pid=${spawnedProcess.pid || "N/A"}] finished temporary directories cleanup`);
}
function killAndWait() {
killProcess();
return waitForCleanup;
}
return { launchedProcess: spawnedProcess, gracefullyClose, kill: killAndWait };
}
function envArrayToObject(env) {
const result2 = {};
for (const { name, value: value2 } of env)
result2[name] = value2;
return result2;
}
var childProcess, import_fs6, readline, gracefullyCloseSet, killSet, sigintHandlerCalled, installedHandlers, processHandlers;
var init_processLauncher = __esm({
"packages/utils/processLauncher.ts"() {
"use strict";
childProcess = __toESM(require("child_process"));
import_fs6 = __toESM(require("fs"));
readline = __toESM(require("readline"));
init_fileUtils();
init_debug();
gracefullyCloseSet = /* @__PURE__ */ new Set();
killSet = /* @__PURE__ */ new Set();
sigintHandlerCalled = false;
installedHandlers = /* @__PURE__ */ new Set();
processHandlers = {
exit: exitHandler,
SIGINT: sigintHandler,
SIGTERM: sigtermHandler,
SIGHUP: sighupHandler
};
}
});
// packages/utils/profiler.ts
async function startProfiling() {
if (!profileDir)
return;
session = new (require("inspector")).Session();
session.connect();
await new Promise((f) => {
session.post("Profiler.enable", () => {
session.post("Profiler.start", f);
});
});
}
async function stopProfiling(profileName) {
if (!profileDir)
return;
await new Promise((f) => session.post("Profiler.stop", (err, { profile }) => {
if (!err) {
import_fs7.default.mkdirSync(profileDir, { recursive: true });
import_fs7.default.writeFileSync(import_path5.default.join(profileDir, profileName + ".json"), JSON.stringify(profile));
}
f();
}));
}
var import_fs7, import_path5, profileDir, session;
var init_profiler = __esm({
"packages/utils/profiler.ts"() {
"use strict";
import_fs7 = __toESM(require("fs"));
import_path5 = __toESM(require("path"));
profileDir = process.env.PWTEST_PROFILE_DIR || "";
}
});
// packages/utils/serializedFS.ts
var import_fs8, yazl, APPEND_CHUNK_SIZE, SerializedFS;
var init_serializedFS = __esm({
"packages/utils/serializedFS.ts"() {
"use strict";
import_fs8 = __toESM(require("fs"));
init_manualPromise();
yazl = require("./utilsBundle").yazl;
APPEND_CHUNK_SIZE = 64 * 1024;
SerializedFS = class {
constructor() {
this._buffers = /* @__PURE__ */ new Map();
this._operations = [];
this._operationsDone = new ManualPromise();
this._operationsDone.resolve();
}
mkdir(dir) {
this._appendOperation({ op: "mkdir", dir });
}
writeFile(file, content, skipIfExists) {
this._buffers.delete(file);
this._appendOperation({ op: "writeFile", file, content, skipIfExists });
}
appendFile(file, text2, flush) {
if (!this._buffers.has(file))
this._buffers.set(file, []);
const buffer = this._buffers.get(file);
buffer.push(text2);
let size = 0;
for (const chunk of buffer)
size += chunk.length;
if (flush || size >= APPEND_CHUNK_SIZE)
this._flushFile(file);
}
_flushFile(file) {
const buffer = this._buffers.get(file);
if (buffer === void 0)
return;
const content = buffer.join("");
this._buffers.delete(file);
this._appendOperation({ op: "appendFile", file, content });
}
copyFile(from, to) {
this._flushFile(from);
this._buffers.delete(to);
this._appendOperation({ op: "copyFile", from, to });
}
async syncAndGetError() {
for (const file of this._buffers.keys())
this._flushFile(file);
await this._operationsDone;
return this._error;
}
zip(entries, zipFileName) {
for (const file of this._buffers.keys())
this._flushFile(file);
this._appendOperation({ op: "zip", entries, zipFileName });
}
// This method serializes all writes to the trace.
_appendOperation(op) {
const last = this._operations[this._operations.length - 1];
if (last?.op === "appendFile" && op.op === "appendFile" && last.file === op.file && last.content.length < APPEND_CHUNK_SIZE) {
last.content += op.content;
return;
}
this._operations.push(op);
if (this._operationsDone.isDone())
this._performOperations();
}
async _performOperations() {
this._operationsDone = new ManualPromise();
while (this._operations.length) {
const op = this._operations.shift();
if (this._error)
continue;
try {
await this._performOperation(op);
} catch (e) {
this._error = e;
}
}
this._operationsDone.resolve();
}
async _performOperation(op) {
switch (op.op) {
case "mkdir": {
await import_fs8.default.promises.mkdir(op.dir, { recursive: true });
return;
}
case "writeFile": {
if (op.skipIfExists)
await import_fs8.default.promises.writeFile(op.file, op.content, { flag: "wx" }).catch(() => {
});
else
await import_fs8.default.promises.writeFile(op.file, op.content);
return;
}
case "copyFile": {
await import_fs8.default.promises.copyFile(op.from, op.to);
return;
}
case "appendFile": {
await import_fs8.default.promises.appendFile(op.file, op.content);
return;
}
case "zip": {
const zipFile = new yazl.ZipFile();
const result2 = new ManualPromise();
zipFile.on("error", (error) => result2.reject(error));
for (const entry of op.entries)
zipFile.addFile(entry.value, entry.name);
zipFile.end();
zipFile.outputStream.pipe(import_fs8.default.createWriteStream(op.zipFileName)).on("close", () => result2.resolve()).on("error", (error) => result2.reject(error));
await result2;
return;
}
}
}
};
}
});
// packages/utils/socksProxy.ts
function hexToNumber(hex) {
return [...hex].reduce((value2, digit2) => {
const code = digit2.charCodeAt(0);
if (code >= 48 && code <= 57)
return value2 + code;
if (code >= 97 && code <= 102)
return value2 + (code - 97) + 10;
if (code >= 65 && code <= 70)
return value2 + (code - 65) + 10;
throw new Error("Invalid IPv6 token " + hex);
}, 0);
}
function ipToSocksAddress(address) {
if (import_net2.default.isIPv4(address)) {
return [
1,
// IPv4
...address.split(".", 4).map((t) => +t & 255)
// Address
];
}
if (import_net2.default.isIPv6(address)) {
const result2 = [4];
const tokens = address.split(":", 8);
while (tokens.length < 8)
tokens.unshift("");
for (const token of tokens) {
const value2 = hexToNumber(token);
result2.push(value2 >> 8 & 255, value2 & 255);
}
return result2;
}
throw new Error("Only IPv4 and IPv6 addresses are supported");
}
function starMatchToRegex(pattern) {
const source8 = pattern.split("*").map((s) => {
return s.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
}).join(".*");
return new RegExp("^" + source8 + "$");
}
function parsePattern(pattern) {
if (!pattern)
return () => false;
const matchers = pattern.split(",").map((token) => {
const match = token.match(/^(.*?)(?::(\d+))?$/);
if (!match)
throw new Error(`Unsupported token "${token}" in pattern "${pattern}"`);
const tokenPort = match[2] ? +match[2] : void 0;
const portMatches = (port) => tokenPort === void 0 || tokenPort === port;
let tokenHost = match[1];
if (tokenHost === "<loopback>") {
return (host, port) => {
if (!portMatches(port))
return false;
return host === "localhost" || host.endsWith(".localhost") || host === "127.0.0.1" || host === "[::1]";
};
}
if (tokenHost === "*")
return (host, port) => portMatches(port);
if (import_net2.default.isIPv4(tokenHost) || import_net2.default.isIPv6(tokenHost))
return (host, port) => host === tokenHost && portMatches(port);
if (tokenHost[0] === ".")
tokenHost = "*" + tokenHost;
const tokenRegex = starMatchToRegex(tokenHost);
return (host, port) => {
if (!portMatches(port))
return false;
if (import_net2.default.isIPv4(host) || import_net2.default.isIPv6(host))
return false;
return !!host.match(tokenRegex);
};
});
return (host, port) => matchers.some((matcher) => matcher(host, port));
}
var import_events2, import_net2, SocksConnection, SocksProxy, SocksProxyHandler;
var init_socksProxy = __esm({
"packages/utils/socksProxy.ts"() {
"use strict";
import_events2 = __toESM(require("events"));
import_net2 = __toESM(require("net"));
init_assert();
init_crypto();
init_debugLogger();
init_happyEyeballs();
SocksConnection = class {
constructor(uid, socket, client) {
this._buffer = Buffer.from([]);
this._offset = 0;
this._fence = 0;
this._uid = uid;
this._socket = socket;
this._client = client;
this._boundOnData = this._onData.bind(this);
socket.on("data", this._boundOnData);
socket.on("close", () => this._onClose());
socket.on("end", () => this._onClose());
socket.on("error", () => this._onClose());
this._run().catch(() => this._socket.end());
}
async _run() {
assert(await this._authenticate());
const { command, host, port } = await this._parseRequest();
if (command !== 1 /* CONNECT */) {
this._writeBytes(Buffer.from([
5,
7 /* CommandNotSupported */,
0,
// RSV
1,
// IPv4
0,
0,
0,
0,
// Address
0,
0
// Port
]));
return;
}
this._socket.off("data", this._boundOnData);
this._client.onSocketRequested({ uid: this._uid, host, port });
}
async _authenticate() {
const version3 = await this._readByte();
assert(version3 === 5, "The VER field must be set to x05 for this version of the protocol, was " + version3);
const nMethods = await this._readByte();
assert(nMethods, "No authentication methods specified");
const methods = await this._readBytes(nMethods);
for (const method of methods) {
if (method === 0) {
this._writeBytes(Buffer.from([version3, method]));
return true;
}
}
this._writeBytes(Buffer.from([version3, 255 /* NO_ACCEPTABLE_METHODS */]));
return false;
}
async _parseRequest() {
const version3 = await this._readByte();
assert(version3 === 5, "The VER field must be set to x05 for this version of the protocol, was " + version3);
const command = await this._readByte();
await this._readByte();
const addressType = await this._readByte();
let host = "";
switch (addressType) {
case 1 /* IPv4 */:
host = (await this._readBytes(4)).join(".");
break;
case 3 /* FqName */:
const length = await this._readByte();
host = (await this._readBytes(length)).toString();
break;
case 4 /* IPv6 */:
const bytes = await this._readBytes(16);
const tokens = [];
for (let i = 0; i < 8; ++i)
tokens.push(bytes.readUInt16BE(i * 2).toString(16));
host = tokens.join(":");
break;
}
const port = (await this._readBytes(2)).readUInt16BE(0);
this._buffer = Buffer.from([]);
this._offset = 0;
this._fence = 0;
return {
command,
host,
port
};
}
async _readByte() {
const buffer = await this._readBytes(1);
return buffer[0];
}
async _readBytes(length) {
this._fence = this._offset + length;
if (!this._buffer || this._buffer.length < this._fence)
await new Promise((f) => this._fenceCallback = f);
this._offset += length;
return this._buffer.slice(this._offset - length, this._offset);
}
_writeBytes(buffer) {
if (this._socket.writable)
this._socket.write(buffer);
}
_onClose() {
this._client.onSocketClosed({ uid: this._uid });
}
_onData(buffer) {
this._buffer = Buffer.concat([this._buffer, buffer]);
if (this._fenceCallback && this._buffer.length >= this._fence) {
const callback = this._fenceCallback;
this._fenceCallback = void 0;
callback();
}
}
socketConnected(host, port) {
this._writeBytes(Buffer.from([
5,
0 /* Succeeded */,
0,
// RSV
...ipToSocksAddress(host),
// ATYP, Address
port >> 8,
port & 255
// Port
]));
this._socket.on("data", (data) => this._client.onSocketData({ uid: this._uid, data }));
}
socketFailed(errorCode) {
const buffer = Buffer.from([
5,
0,
0,
// RSV
...ipToSocksAddress("0.0.0.0"),
// ATYP, Address
0,
0
// Port
]);
switch (errorCode) {
case "ENOENT":
case "ENOTFOUND":
case "ETIMEDOUT":
case "EHOSTUNREACH":
buffer[1] = 4 /* HostUnreachable */;
break;
case "ENETUNREACH":
buffer[1] = 3 /* NetworkUnreachable */;
break;
case "ECONNREFUSED":
buffer[1] = 5 /* ConnectionRefused */;
break;
case "ERULESET":
buffer[1] = 2 /* NotAllowedByRuleSet */;
break;
}
this._writeBytes(buffer);
this._socket.end();
}
sendData(data) {
this._socket.write(data);
}
end() {
this._socket.end();
}
error(error) {
this._socket.destroy(new Error(error));
}
};
SocksProxy = class _SocksProxy extends import_events2.default {
constructor() {
super();
this._connections = /* @__PURE__ */ new Map();
this._sockets = /* @__PURE__ */ new Set();
this._closed = false;
this._patternMatcher = () => false;
this._directSockets = /* @__PURE__ */ new Map();
this._server = new import_net2.default.Server((socket) => {
const uid = createGuid();
const connection = new SocksConnection(uid, socket, this);
this._connections.set(uid, connection);
});
this._server.on("connection", (socket) => {
if (this._closed) {
socket.destroy();
return;
}
this._sockets.add(socket);
socket.once("close", () => this._sockets.delete(socket));
});
}
static {
this.Events = {
SocksRequested: "socksRequested",
SocksData: "socksData",
SocksClosed: "socksClosed"
};
}
setPattern(pattern) {
try {
this._patternMatcher = parsePattern(pattern);
} catch (e) {
this._patternMatcher = () => false;
}
}
async _handleDirect(request2) {
try {
const socket = await createSocket(request2.host, request2.port);
socket.on("data", (data) => this._connections.get(request2.uid)?.sendData(data));
socket.on("error", (error) => {
this._connections.get(request2.uid)?.error(error.message);
this._directSockets.delete(request2.uid);
});
socket.on("end", () => {
this._connections.get(request2.uid)?.end();
this._directSockets.delete(request2.uid);
});
const localAddress = socket.localAddress;
const localPort = socket.localPort;
this._directSockets.set(request2.uid, socket);
this._connections.get(request2.uid)?.socketConnected(localAddress, localPort);
} catch (error) {
this._connections.get(request2.uid)?.socketFailed(error.code);
}
}
port() {
return this._port;
}
async listen(port, hostname) {
return new Promise((f) => {
this._server.listen(port, hostname, () => {
const port2 = this._server.address().port;
this._port = port2;
f(port2);
});
});
}
async close() {
if (this._closed)
return;
this._closed = true;
for (const socket of this._sockets)
socket.destroy();
this._sockets.clear();
await new Promise((f) => this._server.close(f));
}
onSocketRequested(payload) {
if (!this._patternMatcher(payload.host, payload.port)) {
this._handleDirect(payload);
return;
}
this.emit(_SocksProxy.Events.SocksRequested, payload);
}
onSocketData(payload) {
const direct = this._directSockets.get(payload.uid);
if (direct) {
direct.write(payload.data);
return;
}
this.emit(_SocksProxy.Events.SocksData, payload);
}
onSocketClosed(payload) {
const direct = this._directSockets.get(payload.uid);
if (direct) {
direct.destroy();
this._directSockets.delete(payload.uid);
return;
}
this.emit(_SocksProxy.Events.SocksClosed, payload);
}
socketConnected({ uid, host, port }) {
this._connections.get(uid)?.socketConnected(host, port);
}
socketFailed({ uid, errorCode }) {
this._connections.get(uid)?.socketFailed(errorCode);
}
sendSocketData({ uid, data }) {
this._connections.get(uid)?.sendData(data);
}
sendSocketEnd({ uid }) {
this._connections.get(uid)?.end();
}
sendSocketError({ uid, error }) {
this._connections.get(uid)?.error(error);
}
};
SocksProxyHandler = class _SocksProxyHandler extends import_events2.default {
constructor(pattern, redirectPortForTest) {
super();
this._sockets = /* @__PURE__ */ new Map();
this._patternMatcher = () => false;
this._patternMatcher = parsePattern(pattern);
this._redirectPortForTest = redirectPortForTest;
}
static {
this.Events = {
SocksConnected: "socksConnected",
SocksData: "socksData",
SocksError: "socksError",
SocksFailed: "socksFailed",
SocksEnd: "socksEnd"
};
}
cleanup() {
for (const uid of this._sockets.keys())
this.socketClosed({ uid });
}
async socketRequested({ uid, host, port }) {
debugLogger.log("socks", `[${uid}] => request ${host}:${port}`);
if (!this._patternMatcher(host, port)) {
const payload = { uid, errorCode: "ERULESET" };
debugLogger.log("socks", `[${uid}] <= pattern error ${payload.errorCode}`);
this.emit(_SocksProxyHandler.Events.SocksFailed, payload);
return;
}
if (host === "local.playwright")
host = "localhost";
try {
if (this._redirectPortForTest)
port = this._redirectPortForTest;
const socket = await createSocket(host, port);
socket.on("data", (data) => {
const payload2 = { uid, data };
this.emit(_SocksProxyHandler.Events.SocksData, payload2);
});
socket.on("error", (error) => {
const payload2 = { uid, error: error.message };
debugLogger.log("socks", `[${uid}] <= network socket error ${payload2.error}`);
this.emit(_SocksProxyHandler.Events.SocksError, payload2);
this._sockets.delete(uid);
});
socket.on("end", () => {
const payload2 = { uid };
debugLogger.log("socks", `[${uid}] <= network socket closed`);
this.emit(_SocksProxyHandler.Events.SocksEnd, payload2);
this._sockets.delete(uid);
});
const localAddress = socket.localAddress;
const localPort = socket.localPort;
this._sockets.set(uid, socket);
const payload = { uid, host: localAddress, port: localPort };
debugLogger.log("socks", `[${uid}] <= connected to network ${payload.host}:${payload.port}`);
this.emit(_SocksProxyHandler.Events.SocksConnected, payload);
} catch (error) {
const payload = { uid, errorCode: error.code };
debugLogger.log("socks", `[${uid}] <= connect error ${payload.errorCode}`);
this.emit(_SocksProxyHandler.Events.SocksFailed, payload);
}
}
sendSocketData({ uid, data }) {
this._sockets.get(uid)?.write(data);
}
socketClosed({ uid }) {
debugLogger.log("socks", `[${uid}] <= browser socket closed`);
this._sockets.get(uid)?.destroy();
this._sockets.delete(uid);
}
};
}
});
// packages/utils/spawnAsync.ts
function spawnAsync(cmd, args, options2 = {}) {
const process2 = (0, import_child_process.spawn)(cmd, args, Object.assign({ windowsHide: true }, options2));
return new Promise((resolve) => {
let stdout = "";
let stderr = "";
if (process2.stdout)
process2.stdout.on("data", (data) => stdout += data.toString());
if (process2.stderr)
process2.stderr.on("data", (data) => stderr += data.toString());
process2.on("close", (code) => resolve({ stdout, stderr, code }));
process2.on("error", (error) => resolve({ stdout, stderr, code: 0, error }));
});
}
var import_child_process;
var init_spawnAsync = __esm({
"packages/utils/spawnAsync.ts"() {
"use strict";
import_child_process = require("child_process");
}
});
// packages/utils/stringWidth.ts
function characterWidth(c) {
return getEastAsianWidth.eastAsianWidth(c.codePointAt(0));
}
function stringWidth(v) {
let width = 0;
for (const { segment } of new Intl.Segmenter(void 0, { granularity: "grapheme" }).segment(v))
width += characterWidth(segment);
return width;
}
function suffixOfWidth(v, width) {
const segments = [...new Intl.Segmenter(void 0, { granularity: "grapheme" }).segment(v)];
let suffixBegin = v.length;
for (const { segment, index } of segments.reverse()) {
const segmentWidth = stringWidth(segment);
if (segmentWidth > width)
break;
width -= segmentWidth;
suffixBegin = index;
}
return v.substring(suffixBegin);
}
function fitToWidth(line, width, prefix) {
const prefixLength = prefix ? stripAnsiEscapes(prefix).length : 0;
width -= prefixLength;
if (stringWidth(line) <= width)
return line;
const parts = line.split(ansiRegex);
const taken = [];
for (let i = parts.length - 1; i >= 0; i--) {
if (i % 2) {
taken.push(parts[i]);
} else {
let part = suffixOfWidth(parts[i], width);
const wasTruncated = part.length < parts[i].length;
if (wasTruncated && parts[i].length > 0) {
part = "\u2026" + suffixOfWidth(parts[i], width - 1);
}
taken.push(part);
width -= stringWidth(part);
}
}
return taken.reverse().join("");
}
var getEastAsianWidth;
var init_stringWidth = __esm({
"packages/utils/stringWidth.ts"() {
"use strict";
init_stringUtils();
getEastAsianWidth = require("./utilsBundle").getEastAsianWidth;
}
});
// packages/utils/task.ts
function makeWaitForNextTask() {
if (process.versions.electron)
return (callback) => setTimeout(callback, 0);
if (parseInt(process.versions.node, 10) >= 11)
return setImmediate;
let spinning = false;
const callbacks = [];
const loop = () => {
const callback = callbacks.shift();
if (!callback) {
spinning = false;
return;
}
setImmediate(loop);
callback();
};
return (callback) => {
callbacks.push(callback);
if (!spinning) {
spinning = true;
setImmediate(loop);
}
};
}
var init_task = __esm({
"packages/utils/task.ts"() {
"use strict";
}
});
// packages/utils/wsServer.ts
var wsServer2, lastConnectionId, kConnectionSymbol, perMessageDeflate, WSServer;
var init_wsServer = __esm({
"packages/utils/wsServer.ts"() {
"use strict";
init_httpServer();
init_network();
init_debugLogger();
({ wsServer: wsServer2 } = require("./utilsBundle"));
lastConnectionId = 0;
kConnectionSymbol = Symbol("kConnection");
perMessageDeflate = {
serverNoContextTakeover: true,
zlibDeflateOptions: {
level: 3
},
zlibInflateOptions: {
chunkSize: 10 * 1024
},
threshold: 10 * 1024
};
WSServer = class {
constructor(delegate) {
// Allowed Host headers for HTTP requests. null disables the check (server bound to a public address).
this._allowedHosts = null;
this._delegate = delegate;
}
async listen(port = 0, hostname, path59) {
debugLogger.log("server", `Server started at ${/* @__PURE__ */ new Date()}`);
hostname ??= "localhost";
const server = createHttpServer((request2, response2) => this._onRequest(request2, response2));
server.on("error", (error) => debugLogger.log("server", String(error)));
this.server = server;
const wsEndpoint = await new Promise((resolve, reject) => {
server.listen(port, hostname, () => {
const address = server.address();
if (!address) {
reject(new Error("Could not bind server socket"));
return;
}
if (typeof address === "string") {
resolve(`${address}${path59}`);
return;
}
this._allowedHosts = computeAllowedHosts(hostname, address.address);
resolve(`ws://${urlHostFromAddress(address)}:${address.port}${path59}`);
}).on("error", reject);
});
debugLogger.log("server", "Listening at " + wsEndpoint);
this._wsServer = new wsServer2({
noServer: true,
perMessageDeflate
});
this._wsServer.on("headers", (headers) => this._delegate.onHeaders(headers));
server.on("upgrade", (request2, socket, head) => {
const pathname = new URL("http://localhost" + request2.url).pathname;
if (pathname !== path59) {
socket.write(`HTTP/${request2.httpVersion} 400 Bad Request\r
\r
`);
socket.destroy();
return;
}
if (this._allowedHosts && !this._isAllowedOrigin(request2.headers.origin)) {
socket.write(`HTTP/${request2.httpVersion} 403 Forbidden\r
\r
`);
socket.destroy();
return;
}
const upgradeResult = this._delegate.onUpgrade(request2, socket);
if (upgradeResult) {
socket.write(upgradeResult.error);
socket.destroy();
return;
}
this._wsServer.handleUpgrade(request2, socket, head, (ws3) => this._wsServer.emit("connection", ws3, request2));
});
this._wsServer.on("connection", (ws3, request2) => {
debugLogger.log("server", "Connected client ws.extension=" + ws3.extensions);
const url2 = new URL("http://localhost" + (request2.url || ""));
const id = String(++lastConnectionId);
debugLogger.log("server", `[${id}] serving connection: ${request2.url}`);
const connection = this._delegate.onConnection(request2, url2, ws3, id);
ws3[kConnectionSymbol] = connection;
});
return wsEndpoint;
}
_onRequest(request2, response2) {
if (this._allowedHosts) {
const host = request2.headers.host?.toLowerCase();
const hostname = host ? hostnameFromHostHeader(host) : void 0;
if (!hostname || !this._allowedHosts.has(hostname)) {
response2.statusCode = 403;
response2.end();
return;
}
}
this._delegate.onRequest(request2, response2);
}
_isAllowedOrigin(origin) {
if (!origin)
return true;
try {
const hostname = new URL(origin).hostname.toLowerCase();
const bracketed = hostname.includes(":") ? `[${hostname}]` : hostname;
return this._allowedHosts.has(hostname) || this._allowedHosts.has(bracketed);
} catch {
return false;
}
}
async close() {
const server = this._wsServer;
if (!server)
return;
debugLogger.log("server", "closing websocket server");
const waitForClose = new Promise((f) => server.close(f));
await Promise.all(Array.from(server.clients).map(async (ws3) => {
const connection = ws3[kConnectionSymbol];
if (connection)
await connection.close();
try {
ws3.terminate();
} catch (e) {
}
}));
await waitForClose;
debugLogger.log("server", "closing http server");
if (this.server)
await new Promise((f) => this.server.close(f));
this._wsServer = void 0;
this.server = void 0;
debugLogger.log("server", "closed server");
}
};
}
});
// packages/utils/third_party/yauzl/pend.js
var require_pend = __commonJS({
"packages/utils/third_party/yauzl/pend.js"(exports2, module2) {
"use strict";
module2.exports = Pend;
function Pend() {
this.pending = 0;
this.max = Infinity;
this.listeners = [];
this.waiting = [];
this.error = null;
}
Pend.prototype.go = function(fn) {
if (this.pending < this.max) {
pendGo(this, fn);
} else {
this.waiting.push(fn);
}
};
Pend.prototype.wait = function(cb) {
if (this.pending === 0) {
cb(this.error);
} else {
this.listeners.push(cb);
}
};
Pend.prototype.hold = function() {
return pendHold(this);
};
function pendHold(self) {
self.pending += 1;
var called = false;
return onCb;
function onCb(err) {
if (called) throw new Error("callback called twice");
called = true;
self.error = self.error || err;
self.pending -= 1;
if (self.waiting.length > 0 && self.pending < self.max) {
pendGo(self, self.waiting.shift());
} else if (self.pending === 0) {
var listeners = self.listeners;
self.listeners = [];
listeners.forEach(cbListener);
}
}
function cbListener(listener) {
listener(self.error);
}
}
function pendGo(self, fn) {
fn(pendHold(self));
}
}
});
// packages/utils/third_party/yauzl/fd-slicer.js
var require_fd_slicer = __commonJS({
"packages/utils/third_party/yauzl/fd-slicer.js"(exports2) {
"use strict";
var fs61 = require("fs");
var util3 = require("util");
var stream3 = require("stream");
var Readable2 = stream3.Readable;
var Writable2 = stream3.Writable;
var PassThrough = stream3.PassThrough;
var Pend = require_pend();
var EventEmitter21 = require("events").EventEmitter;
exports2.createFromBuffer = createFromBuffer;
exports2.createFromFd = createFromFd;
exports2.BufferSlicer = BufferSlicer;
exports2.FdSlicer = FdSlicer;
util3.inherits(FdSlicer, EventEmitter21);
function FdSlicer(fd, options2) {
options2 = options2 || {};
EventEmitter21.call(this);
this.fd = fd;
this.pend = new Pend();
this.pend.max = 1;
this.refCount = 0;
this.autoClose = !!options2.autoClose;
}
FdSlicer.prototype.read = function(buffer, offset, length, position, callback) {
var self = this;
self.pend.go(function(cb) {
fs61.read(self.fd, buffer, offset, length, position, function(err, bytesRead, buffer2) {
cb();
callback(err, bytesRead, buffer2);
});
});
};
FdSlicer.prototype.write = function(buffer, offset, length, position, callback) {
var self = this;
self.pend.go(function(cb) {
fs61.write(self.fd, buffer, offset, length, position, function(err, written, buffer2) {
cb();
callback(err, written, buffer2);
});
});
};
FdSlicer.prototype.createReadStream = function(options2) {
return new ReadStream(this, options2);
};
FdSlicer.prototype.createWriteStream = function(options2) {
return new WriteStream(this, options2);
};
FdSlicer.prototype.ref = function() {
this.refCount += 1;
};
FdSlicer.prototype.unref = function() {
var self = this;
self.refCount -= 1;
if (self.refCount > 0) return;
if (self.refCount < 0) throw new Error("invalid unref");
if (self.autoClose) {
fs61.close(self.fd, onCloseDone);
}
function onCloseDone(err) {
if (err) {
self.emit("error", err);
} else {
self.emit("close");
}
}
};
util3.inherits(ReadStream, Readable2);
function ReadStream(context2, options2) {
options2 = options2 || {};
Readable2.call(this, options2);
this.context = context2;
this.context.ref();
this.start = options2.start || 0;
this.endOffset = options2.end;
this.pos = this.start;
this._destroyed = false;
}
ReadStream.prototype._read = function(n) {
var self = this;
if (self._destroyed) return;
var toRead = Math.min(self._readableState.highWaterMark, n);
if (self.endOffset != null) {
toRead = Math.min(toRead, self.endOffset - self.pos);
}
if (toRead <= 0) {
self._destroyed = true;
self.push(null);
self.context.unref();
return;
}
self.context.pend.go(function(cb) {
if (self._destroyed) return cb();
var buffer = Buffer.allocUnsafe(toRead);
fs61.read(self.context.fd, buffer, 0, toRead, self.pos, function(err, bytesRead) {
if (self._destroyed) return cb();
if (err) {
self.destroy(err);
} else if (bytesRead === 0) {
self._destroyed = true;
self.push(null);
self.context.unref();
} else {
self.pos += bytesRead;
self.push(buffer.slice(0, bytesRead));
}
cb();
});
});
};
ReadStream.prototype.destroy = function(err) {
if (err == null && !this.readableEnded) {
err = new Error("stream destroyed");
}
return Readable2.prototype.destroy.call(this, err);
};
ReadStream.prototype._destroy = function(err, cb) {
if (!this._destroyed) {
this._destroyed = true;
this.context.unref();
}
cb(err);
};
util3.inherits(WriteStream, Writable2);
function WriteStream(context2, options2) {
options2 = options2 || {};
Writable2.call(this, options2);
this.context = context2;
this.context.ref();
this.start = options2.start || 0;
this.endOffset = options2.end == null ? Infinity : +options2.end;
this.bytesWritten = 0;
this.pos = this.start;
this._destroyed = false;
this.on("finish", this.destroy.bind(this));
}
WriteStream.prototype._write = function(buffer, encoding, callback) {
var self = this;
if (self._destroyed) return;
if (self.pos + buffer.length > self.endOffset) {
var err = new Error("maximum file length exceeded");
err.code = "ETOOBIG";
self.destroy();
callback(err);
return;
}
self.context.pend.go(function(cb) {
if (self._destroyed) return cb();
fs61.write(self.context.fd, buffer, 0, buffer.length, self.pos, function(err2, bytes) {
if (err2) {
self.destroy();
cb();
callback(err2);
} else {
self.bytesWritten += bytes;
self.pos += bytes;
self.emit("progress");
cb();
callback();
}
});
});
};
WriteStream.prototype._destroy = function(err, cb) {
if (!this._destroyed) {
this._destroyed = true;
this.context.unref();
}
cb(err);
};
util3.inherits(BufferSlicer, EventEmitter21);
function BufferSlicer(buffer, options2) {
EventEmitter21.call(this);
options2 = options2 || {};
this.refCount = 0;
this.buffer = buffer;
this.maxChunkSize = options2.maxChunkSize || Number.MAX_SAFE_INTEGER;
}
BufferSlicer.prototype.read = function(buffer, offset, length, position, callback) {
if (!(0 <= offset && offset <= buffer.length)) throw new RangeError("offset outside buffer: 0 <= " + offset + " <= " + buffer.length);
if (position < 0) throw new RangeError("position is negative: " + position);
if (offset + length > buffer.length) {
length = buffer.length - offset;
}
if (position + length > this.buffer.length) {
length = this.buffer.length - position;
}
if (length <= 0) {
setImmediate(function() {
callback(null, 0);
});
return;
}
this.buffer.copy(buffer, offset, position, position + length);
setImmediate(function() {
callback(null, length);
});
};
BufferSlicer.prototype.write = function(buffer, offset, length, position, callback) {
buffer.copy(this.buffer, position, offset, offset + length);
setImmediate(function() {
callback(null, length, buffer);
});
};
BufferSlicer.prototype.createReadStream = function(options2) {
options2 = options2 || {};
var readStream = new PassThrough(options2);
readStream._destroyed = false;
readStream.start = options2.start || 0;
readStream.endOffset = options2.end;
readStream.pos = readStream.endOffset || this.buffer.length;
var entireSlice = this.buffer.slice(readStream.start, readStream.pos);
var offset = 0;
while (true) {
var nextOffset = offset + this.maxChunkSize;
if (nextOffset >= entireSlice.length) {
if (offset < entireSlice.length) {
readStream.write(entireSlice.slice(offset, entireSlice.length));
}
break;
}
readStream.write(entireSlice.slice(offset, nextOffset));
offset = nextOffset;
}
readStream.end();
readStream._destroy = function(err, cb) {
readStream._destroyed = true;
PassThrough.prototype._destroy.call(readStream, err, cb);
};
return readStream;
};
BufferSlicer.prototype.createWriteStream = function(options2) {
var bufferSlicer = this;
options2 = options2 || {};
var writeStream = new Writable2(options2);
writeStream.start = options2.start || 0;
writeStream.endOffset = options2.end == null ? this.buffer.length : +options2.end;
writeStream.bytesWritten = 0;
writeStream.pos = writeStream.start;
writeStream._destroyed = false;
writeStream._write = function(buffer, encoding, callback) {
if (writeStream._destroyed) return;
var end = writeStream.pos + buffer.length;
if (end > writeStream.endOffset) {
var err = new Error("maximum file length exceeded");
err.code = "ETOOBIG";
writeStream._destroyed = true;
callback(err);
return;
}
buffer.copy(bufferSlicer.buffer, writeStream.pos, 0, buffer.length);
writeStream.bytesWritten += buffer.length;
writeStream.pos = end;
writeStream.emit("progress");
callback();
};
writeStream._destroy = function(err, cb) {
writeStream._destroyed = true;
cb(err);
};
return writeStream;
};
BufferSlicer.prototype.ref = function() {
this.refCount += 1;
};
BufferSlicer.prototype.unref = function() {
this.refCount -= 1;
if (this.refCount < 0) {
throw new Error("invalid unref");
}
};
function createFromBuffer(buffer, options2) {
return new BufferSlicer(buffer, options2);
}
function createFromFd(fd, options2) {
return new FdSlicer(fd, options2);
}
}
});
// packages/utils/third_party/yauzl/buffer-crc32.js
var require_buffer_crc32 = __commonJS({
"packages/utils/third_party/yauzl/buffer-crc32.js"(exports2, module2) {
"use strict";
var Buffer2 = require("buffer").Buffer;
var CRC_TABLE = [
0,
1996959894,
3993919788,
2567524794,
124634137,
1886057615,
3915621685,
2657392035,
249268274,
2044508324,
3772115230,
2547177864,
162941995,
2125561021,
3887607047,
2428444049,
498536548,
1789927666,
4089016648,
2227061214,
450548861,
1843258603,
4107580753,
2211677639,
325883990,
1684777152,
4251122042,
2321926636,
335633487,
1661365465,
4195302755,
2366115317,
997073096,
1281953886,
3579855332,
2724688242,
1006888145,
1258607687,
3524101629,
2768942443,
901097722,
1119000684,
3686517206,
2898065728,
853044451,
1172266101,
3705015759,
2882616665,
651767980,
1373503546,
3369554304,
3218104598,
565507253,
1454621731,
3485111705,
3099436303,
671266974,
1594198024,
3322730930,
2970347812,
795835527,
1483230225,
3244367275,
3060149565,
1994146192,
31158534,
2563907772,
4023717930,
1907459465,
112637215,
2680153253,
3904427059,
2013776290,
251722036,
2517215374,
3775830040,
2137656763,
141376813,
2439277719,
3865271297,
1802195444,
476864866,
2238001368,
4066508878,
1812370925,
453092731,
2181625025,
4111451223,
1706088902,
314042704,
2344532202,
4240017532,
1658658271,
366619977,
2362670323,
4224994405,
1303535960,
984961486,
2747007092,
3569037538,
1256170817,
1037604311,
2765210733,
3554079995,
1131014506,
879679996,
2909243462,
3663771856,
1141124467,
855842277,
2852801631,
3708648649,
1342533948,
654459306,
3188396048,
3373015174,
1466479909,
544179635,
3110523913,
3462522015,
1591671054,
702138776,
2966460450,
3352799412,
1504918807,
783551873,
3082640443,
3233442989,
3988292384,
2596254646,
62317068,
1957810842,
3939845945,
2647816111,
81470997,
1943803523,
3814918930,
2489596804,
225274430,
2053790376,
3826175755,
2466906013,
167816743,
2097651377,
4027552580,
2265490386,
503444072,
1762050814,
4150417245,
2154129355,
426522225,
1852507879,
4275313526,
2312317920,
282753626,
1742555852,
4189708143,
2394877945,
397917763,
1622183637,
3604390888,
2714866558,
953729732,
1340076626,
3518719985,
2797360999,
1068828381,
1219638859,
3624741850,
2936675148,
906185462,
1090812512,
3747672003,
2825379669,
829329135,
1181335161,
3412177804,
3160834842,
628085408,
1382605366,
3423369109,
3138078467,
570562233,
1426400815,
3317316542,
2998733608,
733239954,
1555261956,
3268935591,
3050360625,
752459403,
1541320221,
2607071920,
3965973030,
1969922972,
40735498,
2617837225,
3943577151,
1913087877,
83908371,
2512341634,
3803740692,
2075208622,
213261112,
2463272603,
3855990285,
2094854071,
198958881,
2262029012,
4057260610,
1759359992,
534414190,
2176718541,
4139329115,
1873836001,
414664567,
2282248934,
4279200368,
1711684554,
285281116,
2405801727,
4167216745,
1634467795,
376229701,
2685067896,
3608007406,
1308918612,
956543938,
2808555105,
3495958263,
1231636301,
1047427035,
2932959818,
3654703836,
1088359270,
936918e3,
2847714899,
3736837829,
1202900863,
817233897,
3183342108,
3401237130,
1404277552,
615818150,
3134207493,
3453421203,
1423857449,
601450431,
3009837614,
3294710456,
1567103746,
711928724,
3020668471,
3272380065,
1510334235,
755167117
];
if (typeof Int32Array !== "undefined") {
CRC_TABLE = new Int32Array(CRC_TABLE);
}
function ensureBuffer(input) {
if (Buffer2.isBuffer(input)) {
return input;
}
var hasNewBufferAPI = typeof Buffer2.alloc === "function" && typeof Buffer2.from === "function";
if (typeof input === "number") {
return hasNewBufferAPI ? Buffer2.alloc(input) : new Buffer2(input);
} else if (typeof input === "string") {
return hasNewBufferAPI ? Buffer2.from(input) : new Buffer2(input);
} else {
throw new Error("input must be buffer, number, or string, received " + typeof input);
}
}
function bufferizeInt(num) {
var tmp = ensureBuffer(4);
tmp.writeInt32BE(num, 0);
return tmp;
}
function _crc32(buf, previous) {
buf = ensureBuffer(buf);
if (Buffer2.isBuffer(previous)) {
previous = previous.readUInt32BE(0);
}
var crc = ~~previous ^ -1;
for (var n = 0; n < buf.length; n++) {
crc = CRC_TABLE[(crc ^ buf[n]) & 255] ^ crc >>> 8;
}
return crc ^ -1;
}
function crc32() {
return bufferizeInt(_crc32.apply(null, arguments));
}
crc32.signed = function() {
return _crc32.apply(null, arguments);
};
crc32.unsigned = function() {
return _crc32.apply(null, arguments) >>> 0;
};
module2.exports = crc32;
}
});
// packages/utils/third_party/yauzl/index.js
var require_yauzl = __commonJS({
"packages/utils/third_party/yauzl/index.js"(exports2) {
"use strict";
var fs61 = require("fs");
var zlib2 = require("zlib");
var fd_slicer = require_fd_slicer();
var crc32 = require_buffer_crc32();
var util3 = require("util");
var EventEmitter21 = require("events").EventEmitter;
var Transform2 = require("stream").Transform;
var PassThrough = require("stream").PassThrough;
var Writable2 = require("stream").Writable;
exports2.open = open7;
exports2.fromFd = fromFd;
exports2.fromBuffer = fromBuffer;
exports2.fromRandomAccessReader = fromRandomAccessReader;
exports2.dosDateTimeToDate = dosDateTimeToDate;
exports2.getFileNameLowLevel = getFileNameLowLevel;
exports2.validateFileName = validateFileName;
exports2.parseExtraFields = parseExtraFields;
exports2.ZipFile = ZipFile2;
exports2.Entry = Entry;
exports2.LocalFileHeader = LocalFileHeader;
exports2.RandomAccessReader = RandomAccessReader;
function open7(path59, options2, callback) {
if (typeof options2 === "function") {
callback = options2;
options2 = null;
}
if (options2 == null) options2 = {};
if (options2.autoClose == null) options2.autoClose = true;
if (options2.lazyEntries == null) options2.lazyEntries = false;
if (options2.decodeStrings == null) options2.decodeStrings = true;
if (options2.validateEntrySizes == null) options2.validateEntrySizes = true;
if (options2.strictFileNames == null) options2.strictFileNames = false;
if (callback == null) callback = defaultCallback;
fs61.open(path59, "r", function(err, fd) {
if (err) return callback(err);
fromFd(fd, options2, function(err2, zipfile) {
if (err2) fs61.close(fd, defaultCallback);
callback(err2, zipfile);
});
});
}
function fromFd(fd, options2, callback) {
if (typeof options2 === "function") {
callback = options2;
options2 = null;
}
if (options2 == null) options2 = {};
if (options2.autoClose == null) options2.autoClose = false;
if (options2.lazyEntries == null) options2.lazyEntries = false;
if (options2.decodeStrings == null) options2.decodeStrings = true;
if (options2.validateEntrySizes == null) options2.validateEntrySizes = true;
if (options2.strictFileNames == null) options2.strictFileNames = false;
if (callback == null) callback = defaultCallback;
fs61.fstat(fd, function(err, stats2) {
if (err) return callback(err);
var reader = fd_slicer.createFromFd(fd, { autoClose: true });
fromRandomAccessReader(reader, stats2.size, options2, callback);
});
}
function fromBuffer(buffer, options2, callback) {
if (typeof options2 === "function") {
callback = options2;
options2 = null;
}
if (options2 == null) options2 = {};
options2.autoClose = false;
if (options2.lazyEntries == null) options2.lazyEntries = false;
if (options2.decodeStrings == null) options2.decodeStrings = true;
if (options2.validateEntrySizes == null) options2.validateEntrySizes = true;
if (options2.strictFileNames == null) options2.strictFileNames = false;
var reader = fd_slicer.createFromBuffer(buffer, { maxChunkSize: 65536 });
fromRandomAccessReader(reader, buffer.length, options2, callback);
}
function fromRandomAccessReader(reader, totalSize, options2, callback) {
if (typeof options2 === "function") {
callback = options2;
options2 = null;
}
if (options2 == null) options2 = {};
if (options2.autoClose == null) options2.autoClose = true;
if (options2.lazyEntries == null) options2.lazyEntries = false;
if (options2.decodeStrings == null) options2.decodeStrings = true;
var decodeStrings = !!options2.decodeStrings;
if (options2.validateEntrySizes == null) options2.validateEntrySizes = true;
if (options2.strictFileNames == null) options2.strictFileNames = false;
if (callback == null) callback = defaultCallback;
if (typeof totalSize !== "number") throw new Error("expected totalSize parameter to be a number");
if (totalSize > Number.MAX_SAFE_INTEGER) {
throw new Error("zip file too large. only file sizes up to 2^52 are supported due to JavaScript's Number type being an IEEE 754 double.");
}
reader.ref();
var eocdrWithoutCommentSize = 22;
var zip64EocdlSize = 20;
var maxCommentSize = 65535;
var bufferSize = Math.min(zip64EocdlSize + eocdrWithoutCommentSize + maxCommentSize, totalSize);
var buffer = newBuffer(bufferSize);
var bufferReadStart = totalSize - buffer.length;
readAndAssertNoEof(reader, buffer, 0, bufferSize, bufferReadStart, function(err) {
if (err) return callback(err);
for (var i = bufferSize - eocdrWithoutCommentSize; i >= 0; i -= 1) {
if (buffer.readUInt32LE(i) !== 101010256) continue;
var eocdrBuffer = buffer.subarray(i);
var diskNumber = eocdrBuffer.readUInt16LE(4);
var entryCount = eocdrBuffer.readUInt16LE(10);
var centralDirectoryOffset = eocdrBuffer.readUInt32LE(16);
var commentLength = eocdrBuffer.readUInt16LE(20);
var expectedCommentLength = eocdrBuffer.length - eocdrWithoutCommentSize;
if (commentLength !== expectedCommentLength) {
return callback(new Error("Invalid comment length. Expected: " + expectedCommentLength + ". Found: " + commentLength + ". Are there extra bytes at the end of the file? Or is the end of central dir signature `PK\u263A\u263B` in the comment?"));
}
var comment = decodeStrings ? decodeBuffer(eocdrBuffer.subarray(22), false) : eocdrBuffer.subarray(22);
if (i - zip64EocdlSize >= 0 && buffer.readUInt32LE(i - zip64EocdlSize) === 117853008) {
var zip64EocdlBuffer = buffer.subarray(i - zip64EocdlSize, i - zip64EocdlSize + zip64EocdlSize);
var zip64EocdrOffset = readUInt64LE(zip64EocdlBuffer, 8);
var zip64EocdrBuffer = newBuffer(56);
return readAndAssertNoEof(reader, zip64EocdrBuffer, 0, zip64EocdrBuffer.length, zip64EocdrOffset, function(err2) {
if (err2) return callback(err2);
if (zip64EocdrBuffer.readUInt32LE(0) !== 101075792) {
return callback(new Error("invalid zip64 end of central directory record signature"));
}
diskNumber = zip64EocdrBuffer.readUInt32LE(16);
if (diskNumber !== 0) {
return callback(new Error("multi-disk zip files are not supported: found disk number: " + diskNumber));
}
entryCount = readUInt64LE(zip64EocdrBuffer, 32);
centralDirectoryOffset = readUInt64LE(zip64EocdrBuffer, 48);
return callback(null, new ZipFile2(reader, centralDirectoryOffset, totalSize, entryCount, comment, options2.autoClose, options2.lazyEntries, decodeStrings, options2.validateEntrySizes, options2.strictFileNames));
});
}
if (diskNumber !== 0) {
return callback(new Error("multi-disk zip files are not supported: found disk number: " + diskNumber));
}
return callback(null, new ZipFile2(reader, centralDirectoryOffset, totalSize, entryCount, comment, options2.autoClose, options2.lazyEntries, decodeStrings, options2.validateEntrySizes, options2.strictFileNames));
}
callback(new Error("End of central directory record signature not found. Either not a zip file, or file is truncated."));
});
}
util3.inherits(ZipFile2, EventEmitter21);
function ZipFile2(reader, centralDirectoryOffset, fileSize, entryCount, comment, autoClose, lazyEntries, decodeStrings, validateEntrySizes, strictFileNames) {
var self = this;
EventEmitter21.call(self);
self.reader = reader;
self.reader.on("error", function(err) {
emitError(self, err);
});
self.reader.once("close", function() {
self.emit("close");
});
self.readEntryCursor = centralDirectoryOffset;
self.fileSize = fileSize;
self.entryCount = entryCount;
self.comment = comment;
self.entriesRead = 0;
self.autoClose = !!autoClose;
self.lazyEntries = !!lazyEntries;
self.decodeStrings = !!decodeStrings;
self.validateEntrySizes = !!validateEntrySizes;
self.strictFileNames = !!strictFileNames;
self.isOpen = true;
self.emittedError = false;
if (!self.lazyEntries) self._readEntry();
}
ZipFile2.prototype.close = function() {
if (!this.isOpen) return;
this.isOpen = false;
this.reader.unref();
};
function emitErrorAndAutoClose(self, err) {
if (self.autoClose) self.close();
emitError(self, err);
}
function emitError(self, err) {
if (self.emittedError) return;
self.emittedError = true;
self.emit("error", err);
}
ZipFile2.prototype.readEntry = function() {
if (!this.lazyEntries) throw new Error("readEntry() called without lazyEntries:true");
this._readEntry();
};
ZipFile2.prototype._readEntry = function() {
var self = this;
if (self.entryCount === self.entriesRead) {
setImmediate(function() {
if (self.autoClose) self.close();
if (self.emittedError) return;
self.emit("end");
});
return;
}
if (self.emittedError) return;
var buffer = newBuffer(46);
readAndAssertNoEof(self.reader, buffer, 0, buffer.length, self.readEntryCursor, function(err) {
if (err) return emitErrorAndAutoClose(self, err);
if (self.emittedError) return;
var entry = new Entry();
var signature = buffer.readUInt32LE(0);
if (signature !== 33639248) return emitErrorAndAutoClose(self, new Error("invalid central directory file header signature: 0x" + signature.toString(16)));
entry.versionMadeBy = buffer.readUInt16LE(4);
entry.versionNeededToExtract = buffer.readUInt16LE(6);
entry.generalPurposeBitFlag = buffer.readUInt16LE(8);
entry.compressionMethod = buffer.readUInt16LE(10);
entry.lastModFileTime = buffer.readUInt16LE(12);
entry.lastModFileDate = buffer.readUInt16LE(14);
entry.crc32 = buffer.readUInt32LE(16);
entry.compressedSize = buffer.readUInt32LE(20);
entry.uncompressedSize = buffer.readUInt32LE(24);
entry.fileNameLength = buffer.readUInt16LE(28);
entry.extraFieldLength = buffer.readUInt16LE(30);
entry.fileCommentLength = buffer.readUInt16LE(32);
entry.internalFileAttributes = buffer.readUInt16LE(36);
entry.externalFileAttributes = buffer.readUInt32LE(38);
entry.relativeOffsetOfLocalHeader = buffer.readUInt32LE(42);
if (entry.generalPurposeBitFlag & 64) return emitErrorAndAutoClose(self, new Error("strong encryption is not supported"));
self.readEntryCursor += 46;
buffer = newBuffer(entry.fileNameLength + entry.extraFieldLength + entry.fileCommentLength);
readAndAssertNoEof(self.reader, buffer, 0, buffer.length, self.readEntryCursor, function(err2) {
if (err2) return emitErrorAndAutoClose(self, err2);
if (self.emittedError) return;
entry.fileNameRaw = buffer.subarray(0, entry.fileNameLength);
var fileCommentStart = entry.fileNameLength + entry.extraFieldLength;
entry.extraFieldRaw = buffer.subarray(entry.fileNameLength, fileCommentStart);
entry.fileCommentRaw = buffer.subarray(fileCommentStart, fileCommentStart + entry.fileCommentLength);
try {
entry.extraFields = parseExtraFields(entry.extraFieldRaw);
} catch (err3) {
return emitErrorAndAutoClose(self, err3);
}
if (self.decodeStrings) {
var isUtf8 = (entry.generalPurposeBitFlag & 2048) !== 0;
entry.fileComment = decodeBuffer(entry.fileCommentRaw, isUtf8);
entry.fileName = getFileNameLowLevel(entry.generalPurposeBitFlag, entry.fileNameRaw, entry.extraFields, self.strictFileNames);
var errorMessage = validateFileName(entry.fileName);
if (errorMessage != null) return emitErrorAndAutoClose(self, new Error(errorMessage));
} else {
entry.fileComment = entry.fileCommentRaw;
entry.fileName = entry.fileNameRaw;
}
entry.comment = entry.fileComment;
self.readEntryCursor += buffer.length;
self.entriesRead += 1;
for (var i = 0; i < entry.extraFields.length; i++) {
var extraField = entry.extraFields[i];
if (extraField.id !== 1) continue;
var zip64EiefBuffer = extraField.data;
var index = 0;
if (entry.uncompressedSize === 4294967295) {
if (index + 8 > zip64EiefBuffer.length) {
return emitErrorAndAutoClose(self, new Error("zip64 extended information extra field does not include uncompressed size"));
}
entry.uncompressedSize = readUInt64LE(zip64EiefBuffer, index);
index += 8;
}
if (entry.compressedSize === 4294967295) {
if (index + 8 > zip64EiefBuffer.length) {
return emitErrorAndAutoClose(self, new Error("zip64 extended information extra field does not include compressed size"));
}
entry.compressedSize = readUInt64LE(zip64EiefBuffer, index);
index += 8;
}
if (entry.relativeOffsetOfLocalHeader === 4294967295) {
if (index + 8 > zip64EiefBuffer.length) {
return emitErrorAndAutoClose(self, new Error("zip64 extended information extra field does not include relative header offset"));
}
entry.relativeOffsetOfLocalHeader = readUInt64LE(zip64EiefBuffer, index);
index += 8;
}
break;
}
if (self.validateEntrySizes && entry.compressionMethod === 0) {
var expectedCompressedSize = entry.uncompressedSize;
if (entry.isEncrypted()) {
expectedCompressedSize += 12;
}
if (entry.compressedSize !== expectedCompressedSize) {
var msg = "compressed/uncompressed size mismatch for stored file: " + entry.compressedSize + " != " + entry.uncompressedSize;
return emitErrorAndAutoClose(self, new Error(msg));
}
}
self.emit("entry", entry);
if (!self.lazyEntries) self._readEntry();
});
});
};
ZipFile2.prototype.openReadStream = function(entry, options2, callback) {
var self = this;
var relativeStart = 0;
var relativeEnd = entry.compressedSize;
if (callback == null) {
callback = options2;
options2 = null;
}
if (options2 == null) {
options2 = {};
} else {
if (options2.decrypt != null) {
if (!entry.isEncrypted()) {
throw new Error("options.decrypt can only be specified for encrypted entries");
}
if (options2.decrypt !== false) throw new Error("invalid options.decrypt value: " + options2.decrypt);
if (entry.isCompressed()) {
if (options2.decompress !== false) throw new Error("entry is encrypted and compressed, and options.decompress !== false");
}
}
if (options2.decompress != null) {
if (!entry.isCompressed()) {
throw new Error("options.decompress can only be specified for compressed entries");
}
if (!(options2.decompress === false || options2.decompress === true)) {
throw new Error("invalid options.decompress value: " + options2.decompress);
}
}
if (options2.start != null || options2.end != null) {
if (entry.isCompressed() && options2.decompress !== false) {
throw new Error("start/end range not allowed for compressed entry without options.decompress === false");
}
if (entry.isEncrypted() && options2.decrypt !== false) {
throw new Error("start/end range not allowed for encrypted entry without options.decrypt === false");
}
}
if (options2.start != null) {
relativeStart = options2.start;
if (relativeStart < 0) throw new Error("options.start < 0");
if (relativeStart > entry.compressedSize) throw new Error("options.start > entry.compressedSize");
}
if (options2.end != null) {
relativeEnd = options2.end;
if (relativeEnd < 0) throw new Error("options.end < 0");
if (relativeEnd > entry.compressedSize) throw new Error("options.end > entry.compressedSize");
if (relativeEnd < relativeStart) throw new Error("options.end < options.start");
}
}
if (!self.isOpen) return callback(new Error("closed"));
if (entry.isEncrypted()) {
if (options2.decrypt !== false) return callback(new Error("entry is encrypted, and options.decrypt !== false"));
}
var decompress;
if (entry.compressionMethod === 0) {
decompress = false;
} else if (entry.compressionMethod === 8) {
decompress = options2.decompress != null ? options2.decompress : true;
} else {
return callback(new Error("unsupported compression method: " + entry.compressionMethod));
}
self.readLocalFileHeader(entry, { minimal: true }, function(err, localFileHeader) {
if (err) return callback(err);
self.openReadStreamLowLevel(
localFileHeader.fileDataStart,
entry.compressedSize,
relativeStart,
relativeEnd,
decompress,
entry.uncompressedSize,
callback
);
});
};
ZipFile2.prototype.openReadStreamLowLevel = function(fileDataStart, compressedSize, relativeStart, relativeEnd, decompress, uncompressedSize, callback) {
var self = this;
var fileDataEnd = fileDataStart + compressedSize;
var readStream = self.reader.createReadStream({
start: fileDataStart + relativeStart,
end: fileDataStart + relativeEnd
});
var endpointStream = readStream;
if (decompress) {
var destroyed = false;
var inflateFilter = zlib2.createInflateRaw();
readStream.on("error", function(err) {
setImmediate(function() {
if (!destroyed) inflateFilter.emit("error", err);
});
});
readStream.pipe(inflateFilter);
if (self.validateEntrySizes) {
endpointStream = new AssertByteCountStream(uncompressedSize);
inflateFilter.on("error", function(err) {
setImmediate(function() {
if (!destroyed) endpointStream.emit("error", err);
});
});
inflateFilter.pipe(endpointStream);
} else {
endpointStream = inflateFilter;
}
installDestroyFn(endpointStream, function() {
destroyed = true;
if (inflateFilter !== endpointStream) inflateFilter.unpipe(endpointStream);
readStream.unpipe(inflateFilter);
readStream.destroy();
});
}
callback(null, endpointStream);
};
ZipFile2.prototype.readLocalFileHeader = function(entry, options2, callback) {
var self = this;
if (callback == null) {
callback = options2;
options2 = null;
}
if (options2 == null) options2 = {};
self.reader.ref();
var buffer = newBuffer(30);
readAndAssertNoEof(self.reader, buffer, 0, buffer.length, entry.relativeOffsetOfLocalHeader, function(err) {
try {
if (err) return callback(err);
var signature = buffer.readUInt32LE(0);
if (signature !== 67324752) {
return callback(new Error("invalid local file header signature: 0x" + signature.toString(16)));
}
var fileNameLength = buffer.readUInt16LE(26);
var extraFieldLength = buffer.readUInt16LE(28);
var fileDataStart = entry.relativeOffsetOfLocalHeader + 30 + fileNameLength + extraFieldLength;
if (fileDataStart + entry.compressedSize > self.fileSize) {
return callback(new Error("file data overflows file bounds: " + fileDataStart + " + " + entry.compressedSize + " > " + self.fileSize));
}
if (options2.minimal) {
return callback(null, { fileDataStart });
}
var localFileHeader = new LocalFileHeader();
localFileHeader.fileDataStart = fileDataStart;
localFileHeader.versionNeededToExtract = buffer.readUInt16LE(4);
localFileHeader.generalPurposeBitFlag = buffer.readUInt16LE(6);
localFileHeader.compressionMethod = buffer.readUInt16LE(8);
localFileHeader.lastModFileTime = buffer.readUInt16LE(10);
localFileHeader.lastModFileDate = buffer.readUInt16LE(12);
localFileHeader.crc32 = buffer.readUInt32LE(14);
localFileHeader.compressedSize = buffer.readUInt32LE(18);
localFileHeader.uncompressedSize = buffer.readUInt32LE(22);
localFileHeader.fileNameLength = fileNameLength;
localFileHeader.extraFieldLength = extraFieldLength;
buffer = newBuffer(fileNameLength + extraFieldLength);
self.reader.ref();
readAndAssertNoEof(self.reader, buffer, 0, buffer.length, entry.relativeOffsetOfLocalHeader + 30, function(err2) {
try {
if (err2) return callback(err2);
localFileHeader.fileName = buffer.subarray(0, fileNameLength);
localFileHeader.extraField = buffer.subarray(fileNameLength);
return callback(null, localFileHeader);
} finally {
self.reader.unref();
}
});
} finally {
self.reader.unref();
}
});
};
function Entry() {
}
Entry.prototype.getLastModDate = function(options2) {
if (options2 == null) options2 = {};
if (!options2.forceDosFormat) {
for (var i = 0; i < this.extraFields.length; i++) {
var extraField = this.extraFields[i];
if (extraField.id === 21589) {
var data = extraField.data;
if (data.length < 5) continue;
var flags = data[0];
var HAS_MTIME = 1;
if (!(flags & HAS_MTIME)) continue;
var posixTimestamp = data.readInt32LE(1);
return new Date(posixTimestamp * 1e3);
} else if (extraField.id === 10) {
var data = extraField.data;
if (data.length !== 32) continue;
if (data.readUInt16LE(4) !== 1) continue;
if (data.readUInt16LE(6) !== 24) continue;
var hundredNanoSecondsSince1601 = data.readUInt32LE(8) + 4294967296 * data.readInt32LE(12);
var millisecondsSince1970 = hundredNanoSecondsSince1601 / 1e4 - 116444736e5;
return new Date(millisecondsSince1970);
}
}
}
return dosDateTimeToDate(this.lastModFileDate, this.lastModFileTime, options2.timezone);
};
Entry.prototype.isEncrypted = function() {
return (this.generalPurposeBitFlag & 1) !== 0;
};
Entry.prototype.isCompressed = function() {
return this.compressionMethod === 8;
};
function LocalFileHeader() {
}
function dosDateTimeToDate(date, time, timezone) {
var day = date & 31;
var month = (date >> 5 & 15) - 1;
var year = (date >> 9 & 127) + 1980;
var millisecond = 0;
var second = (time & 31) * 2;
var minute = time >> 5 & 63;
var hour = time >> 11 & 31;
if (timezone == null || timezone === "local") {
return new Date(year, month, day, hour, minute, second, millisecond);
} else if (timezone === "UTC") {
return new Date(Date.UTC(year, month, day, hour, minute, second, millisecond));
} else {
throw new Error("unrecognized options.timezone: " + options.timezone);
}
}
function getFileNameLowLevel(generalPurposeBitFlag, fileNameBuffer, extraFields, strictFileNames) {
var fileName = null;
for (var i = 0; i < extraFields.length; i++) {
var extraField = extraFields[i];
if (extraField.id === 28789) {
if (extraField.data.length < 6) {
continue;
}
if (extraField.data.readUInt8(0) !== 1) {
continue;
}
var oldNameCrc32 = extraField.data.readUInt32LE(1);
if (crc32.unsigned(fileNameBuffer) !== oldNameCrc32) {
continue;
}
fileName = decodeBuffer(extraField.data.subarray(5), true);
break;
}
}
if (fileName == null) {
var isUtf8 = (generalPurposeBitFlag & 2048) !== 0;
fileName = decodeBuffer(fileNameBuffer, isUtf8);
}
if (!strictFileNames) {
fileName = fileName.replace(/\\/g, "/");
}
return fileName;
}
function validateFileName(fileName) {
if (fileName.indexOf("\\") !== -1) {
return "invalid characters in fileName: " + fileName;
}
if (/^[a-zA-Z]:/.test(fileName) || /^\//.test(fileName)) {
return "absolute path: " + fileName;
}
if (fileName.split("/").indexOf("..") !== -1) {
return "invalid relative path: " + fileName;
}
return null;
}
function parseExtraFields(extraFieldBuffer) {
var extraFields = [];
var i = 0;
while (i < extraFieldBuffer.length - 3) {
var headerId = extraFieldBuffer.readUInt16LE(i + 0);
var dataSize = extraFieldBuffer.readUInt16LE(i + 2);
var dataStart = i + 4;
var dataEnd = dataStart + dataSize;
if (dataEnd > extraFieldBuffer.length) throw new Error("extra field length exceeds extra field buffer size");
var dataBuffer = extraFieldBuffer.subarray(dataStart, dataEnd);
extraFields.push({
id: headerId,
data: dataBuffer
});
i = dataEnd;
}
return extraFields;
}
function readAndAssertNoEof(reader, buffer, offset, length, position, callback) {
if (length === 0) {
return setImmediate(function() {
callback(null, newBuffer(0));
});
}
reader.read(buffer, offset, length, position, function(err, bytesRead) {
if (err) return callback(err);
if (bytesRead < length) {
return callback(new Error("unexpected EOF"));
}
callback();
});
}
util3.inherits(AssertByteCountStream, Transform2);
function AssertByteCountStream(byteCount) {
Transform2.call(this);
this.actualByteCount = 0;
this.expectedByteCount = byteCount;
}
AssertByteCountStream.prototype._transform = function(chunk, encoding, cb) {
this.actualByteCount += chunk.length;
if (this.actualByteCount > this.expectedByteCount) {
var msg = "too many bytes in the stream. expected " + this.expectedByteCount + ". got at least " + this.actualByteCount;
return cb(new Error(msg));
}
cb(null, chunk);
};
AssertByteCountStream.prototype._flush = function(cb) {
if (this.actualByteCount < this.expectedByteCount) {
var msg = "not enough bytes in the stream. expected " + this.expectedByteCount + ". got only " + this.actualByteCount;
return cb(new Error(msg));
}
cb();
};
util3.inherits(RandomAccessReader, EventEmitter21);
function RandomAccessReader() {
EventEmitter21.call(this);
this.refCount = 0;
}
RandomAccessReader.prototype.ref = function() {
this.refCount += 1;
};
RandomAccessReader.prototype.unref = function() {
var self = this;
self.refCount -= 1;
if (self.refCount > 0) return;
if (self.refCount < 0) throw new Error("invalid unref");
self.close(onCloseDone);
function onCloseDone(err) {
if (err) return self.emit("error", err);
self.emit("close");
}
};
RandomAccessReader.prototype.createReadStream = function(options2) {
if (options2 == null) options2 = {};
var start3 = options2.start;
var end = options2.end;
if (start3 === end) {
var emptyStream = new PassThrough();
setImmediate(function() {
emptyStream.end();
});
return emptyStream;
}
var stream3 = this._readStreamForRange(start3, end);
var destroyed = false;
var refUnrefFilter = new RefUnrefFilter(this);
stream3.on("error", function(err) {
setImmediate(function() {
if (!destroyed) refUnrefFilter.emit("error", err);
});
});
installDestroyFn(refUnrefFilter, function() {
stream3.unpipe(refUnrefFilter);
refUnrefFilter.unref();
stream3.destroy();
});
var byteCounter = new AssertByteCountStream(end - start3);
refUnrefFilter.on("error", function(err) {
setImmediate(function() {
if (!destroyed) byteCounter.emit("error", err);
});
});
installDestroyFn(byteCounter, function() {
destroyed = true;
refUnrefFilter.unpipe(byteCounter);
refUnrefFilter.destroy();
});
return stream3.pipe(refUnrefFilter).pipe(byteCounter);
};
RandomAccessReader.prototype._readStreamForRange = function(start3, end) {
throw new Error("not implemented");
};
RandomAccessReader.prototype.read = function(buffer, offset, length, position, callback) {
var readStream = this.createReadStream({ start: position, end: position + length });
var writeStream = new Writable2();
var written = 0;
writeStream._write = function(chunk, encoding, cb) {
chunk.copy(buffer, offset + written, 0, chunk.length);
written += chunk.length;
cb();
};
writeStream.on("finish", callback);
readStream.on("error", function(error) {
callback(error);
});
readStream.pipe(writeStream);
};
RandomAccessReader.prototype.close = function(callback) {
setImmediate(callback);
};
util3.inherits(RefUnrefFilter, PassThrough);
function RefUnrefFilter(context2) {
PassThrough.call(this);
this.context = context2;
this.context.ref();
this.unreffedYet = false;
}
RefUnrefFilter.prototype._flush = function(cb) {
this.unref();
cb();
};
RefUnrefFilter.prototype.unref = function(cb) {
if (this.unreffedYet) return;
this.unreffedYet = true;
this.context.unref();
};
var cp437 = "\0\u263A\u263B\u2665\u2666\u2663\u2660\u2022\u25D8\u25CB\u25D9\u2642\u2640\u266A\u266B\u263C\u25BA\u25C4\u2195\u203C\xB6\xA7\u25AC\u21A8\u2191\u2193\u2192\u2190\u221F\u2194\u25B2\u25BC !\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~\u2302\xC7\xFC\xE9\xE2\xE4\xE0\xE5\xE7\xEA\xEB\xE8\xEF\xEE\xEC\xC4\xC5\xC9\xE6\xC6\xF4\xF6\xF2\xFB\xF9\xFF\xD6\xDC\xA2\xA3\xA5\u20A7\u0192\xE1\xED\xF3\xFA\xF1\xD1\xAA\xBA\xBF\u2310\xAC\xBD\xBC\xA1\xAB\xBB\u2591\u2592\u2593\u2502\u2524\u2561\u2562\u2556\u2555\u2563\u2551\u2557\u255D\u255C\u255B\u2510\u2514\u2534\u252C\u251C\u2500\u253C\u255E\u255F\u255A\u2554\u2569\u2566\u2560\u2550\u256C\u2567\u2568\u2564\u2565\u2559\u2558\u2552\u2553\u256B\u256A\u2518\u250C\u2588\u2584\u258C\u2590\u2580\u03B1\xDF\u0393\u03C0\u03A3\u03C3\xB5\u03C4\u03A6\u0398\u03A9\u03B4\u221E\u03C6\u03B5\u2229\u2261\xB1\u2265\u2264\u2320\u2321\xF7\u2248\xB0\u2219\xB7\u221A\u207F\xB2\u25A0\xA0";
function decodeBuffer(buffer, isUtf8) {
if (isUtf8) {
return buffer.toString("utf8");
} else {
var result2 = "";
for (var i = 0; i < buffer.length; i++) {
result2 += cp437[buffer[i]];
}
return result2;
}
}
function readUInt64LE(buffer, offset) {
var lower32 = buffer.readUInt32LE(offset);
var upper32 = buffer.readUInt32LE(offset + 4);
return upper32 * 4294967296 + lower32;
}
var newBuffer;
if (typeof Buffer.allocUnsafe === "function") {
newBuffer = function(len) {
return Buffer.allocUnsafe(len);
};
} else {
newBuffer = function(len) {
return new Buffer(len);
};
}
function installDestroyFn(stream3, fn) {
if (typeof stream3.destroy === "function") {
stream3._destroy = function(err, cb) {
fn();
if (cb != null) cb(err);
};
} else {
stream3.destroy = fn;
}
}
function defaultCallback(err) {
if (err) throw err;
}
}
});
// packages/utils/zipFile.ts
var yauzl, ZipFile;
var init_zipFile = __esm({
"packages/utils/zipFile.ts"() {
"use strict";
yauzl = __toESM(require_yauzl());
ZipFile = class {
constructor(fileName) {
this._entries = /* @__PURE__ */ new Map();
this._fileName = fileName;
this._openedPromise = this._open();
}
async _open() {
await new Promise((fulfill, reject) => {
yauzl.open(this._fileName, { autoClose: false }, (e, z30) => {
if (e) {
reject(e);
return;
}
this._zipFile = z30;
this._zipFile.on("entry", (entry) => {
this._entries.set(entry.fileName, entry);
});
this._zipFile.on("end", fulfill);
});
});
}
async entries() {
await this._openedPromise;
return [...this._entries.keys()];
}
async read(entryPath) {
await this._openedPromise;
const entry = this._entries.get(entryPath);
if (!entry)
throw new Error(`${entryPath} not found in file ${this._fileName}`);
return new Promise((resolve, reject) => {
this._zipFile.openReadStream(entry, (error, readStream) => {
if (error || !readStream) {
reject(error || "Entry not found");
return;
}
const buffers = [];
readStream.on("data", (data) => buffers.push(data));
readStream.on("end", () => resolve(Buffer.concat(buffers)));
});
});
}
close() {
this._zipFile?.close();
}
};
}
});
// packages/utils/third_party/extractZip.ts
async function extractZip(zipPath, opts) {
debug2("creating target directory", opts.dir);
if (!import_path6.default.isAbsolute(opts.dir))
throw new Error("Target directory is expected to be absolute");
await import_fs9.promises.mkdir(opts.dir, { recursive: true });
opts.dir = await import_fs9.promises.realpath(opts.dir);
return new Extractor(zipPath, opts).extract();
}
var import_fs9, import_path6, import_stream2, import_util, import_yauzl, debugPkg, getStream, debug2, openZip, pipeline2, Extractor;
var init_extractZip = __esm({
"packages/utils/third_party/extractZip.ts"() {
"use strict";
import_fs9 = require("fs");
import_path6 = __toESM(require("path"));
import_stream2 = __toESM(require("stream"));
import_util = require("util");
import_yauzl = __toESM(require_yauzl());
debugPkg = require("./utilsBundle").debug;
getStream = require("./utilsBundle").getStream;
debug2 = debugPkg("extract-zip");
openZip = (0, import_util.promisify)(import_yauzl.default.open);
pipeline2 = (0, import_util.promisify)(import_stream2.default.pipeline);
Extractor = class {
constructor(zipPath, opts) {
this.canceled = false;
this.zipPath = zipPath;
this.opts = opts;
}
async extract() {
debug2("opening", this.zipPath, "with opts", this.opts);
this.zipfile = await openZip(this.zipPath, { lazyEntries: true });
this.canceled = false;
return new Promise((resolve, reject) => {
this.zipfile.on("error", (err) => {
this.canceled = true;
reject(err);
});
this.zipfile.readEntry();
this.zipfile.on("close", () => {
if (!this.canceled) {
debug2("zip extraction complete");
resolve();
}
});
this.zipfile.on("entry", async (entry) => {
if (this.canceled) {
debug2("skipping entry", entry.fileName, { cancelled: this.canceled });
return;
}
debug2("zipfile entry", entry.fileName);
if (entry.fileName.startsWith("__MACOSX/")) {
this.zipfile.readEntry();
return;
}
const destDir = import_path6.default.dirname(import_path6.default.join(this.opts.dir, entry.fileName));
try {
await import_fs9.promises.mkdir(destDir, { recursive: true });
const canonicalDestDir = await import_fs9.promises.realpath(destDir);
const relativeDestDir = import_path6.default.relative(this.opts.dir, canonicalDestDir);
if (relativeDestDir.split(import_path6.default.sep).includes(".."))
throw new Error(`Out of bound path "${canonicalDestDir}" found while processing file ${entry.fileName}`);
await this.extractEntry(entry);
debug2("finished processing", entry.fileName);
this.zipfile.readEntry();
} catch (err) {
this.canceled = true;
this.zipfile.close();
reject(err);
}
});
});
}
async extractEntry(entry) {
if (this.canceled) {
debug2("skipping entry extraction", entry.fileName, { cancelled: this.canceled });
return;
}
if (this.opts.onEntry)
this.opts.onEntry(entry, this.zipfile);
const dest = import_path6.default.join(this.opts.dir, entry.fileName);
const mode = entry.externalFileAttributes >> 16 & 65535;
const IFMT = 61440;
const IFDIR = 16384;
const IFLNK = 40960;
const symlink = (mode & IFMT) === IFLNK;
let isDir = (mode & IFMT) === IFDIR;
if (!isDir && entry.fileName.endsWith("/"))
isDir = true;
const madeBy = entry.versionMadeBy >> 8;
if (!isDir)
isDir = madeBy === 0 && entry.externalFileAttributes === 16;
debug2("extracting entry", { filename: entry.fileName, isDir, isSymlink: symlink });
const procMode = this.getExtractedMode(mode, isDir) & 511;
const destDir = isDir ? dest : import_path6.default.dirname(dest);
const mkdirOptions = { recursive: true };
if (isDir)
mkdirOptions.mode = procMode;
debug2("mkdir", { dir: destDir, ...mkdirOptions });
await import_fs9.promises.mkdir(destDir, mkdirOptions);
if (isDir)
return;
debug2("opening read stream", dest);
const readStream = await (0, import_util.promisify)(this.zipfile.openReadStream.bind(this.zipfile))(entry);
if (symlink) {
const link = await getStream(readStream);
debug2("creating symlink", link, dest);
await import_fs9.promises.symlink(link, dest);
} else {
await pipeline2(readStream, (0, import_fs9.createWriteStream)(dest, { mode: procMode }));
}
}
getExtractedMode(entryMode, isDir) {
let mode = entryMode;
if (mode === 0) {
if (isDir) {
if (this.opts.defaultDirMode)
mode = Number(this.opts.defaultDirMode);
if (!mode)
mode = 493;
} else {
if (this.opts.defaultFileMode)
mode = Number(this.opts.defaultFileMode);
if (!mode)
mode = 420;
}
}
return mode;
}
};
}
});
// packages/utils/third_party/lockfile.ts
function probe(file, fs61, callback) {
const cachedPrecision = fs61[cacheSymbol];
if (cachedPrecision) {
return fs61.stat(file, (err, stat) => {
if (err)
return callback(err);
callback(null, stat.mtime, cachedPrecision);
});
}
const mtime = new Date(Math.ceil(Date.now() / 1e3) * 1e3 + 5);
fs61.utimes(file, mtime, mtime, (err) => {
if (err)
return callback(err);
fs61.stat(file, (err2, stat) => {
if (err2)
return callback(err2);
const precision = stat.mtime.getTime() % 1e3 === 0 ? "s" : "ms";
Object.defineProperty(fs61, cacheSymbol, { value: precision });
callback(null, stat.mtime, precision);
});
});
}
function getMtime(precision) {
let now = Date.now();
if (precision === "s")
now = Math.ceil(now / 1e3) * 1e3;
return new Date(now);
}
function getLockFile(file, options2) {
return options2.lockfilePath || `${file}.lock`;
}
function resolveCanonicalPath(file, options2, callback) {
if (!options2.realpath)
return callback(null, import_path7.default.resolve(file));
options2.fs.realpath(file, callback);
}
function acquireLock(file, options2, callback) {
const lockfilePath = getLockFile(file, options2);
options2.fs.mkdir(lockfilePath, (err) => {
if (!err) {
return probe(lockfilePath, options2.fs, (err2, mtime, mtimePrecision) => {
if (err2) {
options2.fs.rmdir(lockfilePath, () => {
});
return callback(err2);
}
callback(null, mtime, mtimePrecision);
});
}
if (err.code !== "EEXIST")
return callback(err);
if (options2.stale <= 0)
return callback(Object.assign(new Error("Lock file is already being held"), { code: "ELOCKED", file }));
options2.fs.stat(lockfilePath, (err2, stat) => {
if (err2) {
if (err2.code === "ENOENT")
return acquireLock(file, { ...options2, stale: 0 }, callback);
return callback(err2);
}
if (!isLockStale(stat, options2))
return callback(Object.assign(new Error("Lock file is already being held"), { code: "ELOCKED", file }));
removeLock(file, options2, (err3) => {
if (err3)
return callback(err3);
acquireLock(file, { ...options2, stale: 0 }, callback);
});
});
});
}
function isLockStale(stat, options2) {
return stat.mtime.getTime() < Date.now() - options2.stale;
}
function removeLock(file, options2, callback) {
options2.fs.rmdir(getLockFile(file, options2), (err) => {
if (err && err.code !== "ENOENT")
return callback(err);
callback(null);
});
}
function updateLock(file, options2) {
const lock2 = locks[file];
if (lock2.updateTimeout)
return;
lock2.updateDelay = lock2.updateDelay || options2.update;
lock2.updateTimeout = setTimeout(() => {
lock2.updateTimeout = null;
options2.fs.stat(lock2.lockfilePath, (err, stat) => {
const isOverThreshold = lock2.lastUpdate + options2.stale < Date.now();
if (err) {
if (err.code === "ENOENT" || isOverThreshold)
return setLockAsCompromised(file, lock2, Object.assign(err, { code: "ECOMPROMISED" }));
lock2.updateDelay = 1e3;
return updateLock(file, options2);
}
const isMtimeOurs = lock2.mtime.getTime() === stat.mtime.getTime();
if (!isMtimeOurs) {
return setLockAsCompromised(
file,
lock2,
Object.assign(
new Error("Unable to update lock within the stale threshold"),
{ code: "ECOMPROMISED" }
)
);
}
const mtime = getMtime(lock2.mtimePrecision);
options2.fs.utimes(lock2.lockfilePath, mtime, mtime, (err2) => {
const isOverThreshold2 = lock2.lastUpdate + options2.stale < Date.now();
if (lock2.released)
return;
if (err2) {
if (err2.code === "ENOENT" || isOverThreshold2)
return setLockAsCompromised(file, lock2, Object.assign(err2, { code: "ECOMPROMISED" }));
lock2.updateDelay = 1e3;
return updateLock(file, options2);
}
lock2.mtime = mtime;
lock2.lastUpdate = Date.now();
lock2.updateDelay = null;
updateLock(file, options2);
});
});
}, lock2.updateDelay);
if (lock2.updateTimeout && lock2.updateTimeout.unref)
lock2.updateTimeout.unref();
}
function setLockAsCompromised(file, lock2, err) {
lock2.released = true;
if (lock2.updateTimeout)
clearTimeout(lock2.updateTimeout);
if (locks[file] === lock2)
delete locks[file];
lock2.options.onCompromised(err);
}
function lockImpl(file, options2, callback) {
const resolvedOptions = {
stale: 1e4,
update: null,
realpath: true,
retries: 0,
fs: gracefulFs,
onCompromised: (err) => {
throw err;
},
...options2
};
resolvedOptions.retries = resolvedOptions.retries || 0;
resolvedOptions.retries = typeof resolvedOptions.retries === "number" ? { retries: resolvedOptions.retries } : resolvedOptions.retries;
resolvedOptions.stale = Math.max(resolvedOptions.stale || 0, 2e3);
resolvedOptions.update = resolvedOptions.update === null || resolvedOptions.update === void 0 ? resolvedOptions.stale / 2 : resolvedOptions.update || 0;
resolvedOptions.update = Math.max(Math.min(resolvedOptions.update, resolvedOptions.stale / 2), 1e3);
resolveCanonicalPath(file, resolvedOptions, (err, resolvedFile) => {
if (err)
return callback(err);
const canonicalFile = resolvedFile;
const operation = retry.operation(resolvedOptions.retries);
operation.attempt(() => {
acquireLock(canonicalFile, resolvedOptions, (err2, mtime, mtimePrecision) => {
if (operation.retry(err2 || void 0))
return;
if (err2)
return callback(operation.mainError());
const lock2 = locks[canonicalFile] = {
lockfilePath: getLockFile(canonicalFile, resolvedOptions),
mtime,
mtimePrecision,
options: resolvedOptions,
lastUpdate: Date.now()
};
updateLock(canonicalFile, resolvedOptions);
callback(null, (releasedCallback) => {
if (lock2.released) {
return releasedCallback && releasedCallback(Object.assign(new Error("Lock is already released"), { code: "ERELEASED" }));
}
unlock(canonicalFile, { ...resolvedOptions, realpath: false }, releasedCallback || (() => {
}));
});
});
});
});
}
function unlock(file, options2, callback) {
const resolvedOptions = {
stale: 1e4,
update: null,
realpath: true,
retries: 0,
fs: gracefulFs,
onCompromised: (err) => {
throw err;
},
...options2
};
resolveCanonicalPath(file, resolvedOptions, (err, resolvedFile) => {
if (err)
return callback(err);
const canonicalFile = resolvedFile;
const lock2 = locks[canonicalFile];
if (!lock2)
return callback(Object.assign(new Error("Lock is not acquired/owned by you"), { code: "ENOTACQUIRED" }));
if (lock2.updateTimeout)
clearTimeout(lock2.updateTimeout);
lock2.released = true;
delete locks[canonicalFile];
removeLock(canonicalFile, resolvedOptions, callback);
});
}
function toPromise(method) {
return (...args) => new Promise((resolve, reject) => {
args.push((err, result2) => {
if (err)
reject(err);
else
resolve(result2);
});
method(...args);
});
}
function ensureCleanup() {
if (cleanupInitialized)
return;
cleanupInitialized = true;
onExit(() => {
for (const file in locks) {
const options2 = locks[file].options;
try {
options2.fs.rmdirSync(getLockFile(file, options2));
} catch (e) {
}
}
});
}
async function lock(file, options2) {
ensureCleanup();
const release = await toPromise(lockImpl)(file, options2 || {});
return toPromise(release);
}
var import_path7, gracefulFs, retry, onExit, locks, cacheSymbol, cleanupInitialized;
var init_lockfile = __esm({
"packages/utils/third_party/lockfile.ts"() {
"use strict";
import_path7 = __toESM(require("path"));
gracefulFs = require("./utilsBundle").gracefulFs;
retry = require("./utilsBundle").retry;
onExit = require("./utilsBundle").onExit;
locks = {};
cacheSymbol = Symbol();
cleanupInitialized = false;
}
});
// packages/utils/index.ts
var utils_exports = {};
__export(utils_exports, {
FastStats: () => FastStats,
HttpServer: () => HttpServer,
ImageChannel: () => ImageChannel,
NET_DEFAULT_TIMEOUT: () => NET_DEFAULT_TIMEOUT,
RecentLogsCollector: () => RecentLogsCollector,
SerializedFS: () => SerializedFS,
SocksProxy: () => SocksProxy,
SocksProxyHandler: () => SocksProxyHandler,
WSServer: () => WSServer,
ZipFile: () => ZipFile,
Zone: () => Zone,
addSuffixToFilePath: () => addSuffixToFilePath,
blendWithWhite: () => blendWithWhite,
calculateSha1: () => calculateSha1,
canAccessFile: () => canAccessFile,
colorDeltaE94: () => colorDeltaE94,
compare: () => compare,
compareBuffersOrStrings: () => compareBuffersOrStrings,
computeAllowedHosts: () => computeAllowedHosts,
copyFileAndMakeWritable: () => copyFileAndMakeWritable,
createGuid: () => createGuid,
createHttp2Server: () => createHttp2Server,
createHttpServer: () => createHttpServer,
createHttpsServer: () => createHttpsServer,
createProxyAgent: () => createProxyAgent,
currentZone: () => currentZone,
debugLogger: () => debugLogger,
debugMode: () => debugMode,
decorateServer: () => decorateServer,
defaultUserDataDirForChannel: () => defaultUserDataDirForChannel,
emptyZone: () => emptyZone,
envArrayToObject: () => envArrayToObject,
eventsHelper: () => eventsHelper,
existsAsync: () => existsAsync,
extractZip: () => extractZip,
fitToWidth: () => fitToWidth,
generateSelfSignedCertificate: () => generateSelfSignedCertificate,
getAsBooleanFromENV: () => getAsBooleanFromENV,
getComparator: () => getComparator,
getFromENV: () => getFromENV,
getPackageManager: () => getPackageManager,
getPackageManagerExecCommand: () => getPackageManagerExecCommand,
gracefullyCloseAll: () => gracefullyCloseAll,
gracefullyCloseSet: () => gracefullyCloseSet,
gracefullyProcessExitDoNotHang: () => gracefullyProcessExitDoNotHang,
guessClientName: () => guessClientName,
hostPlatform: () => hostPlatform,
hostnameFromHostHeader: () => hostnameFromHostHeader,
httpRequest: () => httpRequest,
isChromiumChannelName: () => isChromiumChannelName,
isCodingAgent: () => isCodingAgent,
isLikelyNpxGlobal: () => isLikelyNpxGlobal,
isOfficiallySupportedPlatform: () => isOfficiallySupportedPlatform,
isPathInside: () => isPathInside,
isSystemDirectory: () => isSystemDirectory,
isURLAvailable: () => isURLAvailable,
isUnderTest: () => isUnderTest,
isWritable: () => isWritable,
jsonStringifyForceASCII: () => jsonStringifyForceASCII,
launchProcess: () => launchProcess,
lock: () => lock,
makeSocketPath: () => makeSocketPath,
makeWaitForNextTask: () => makeWaitForNextTask,
mkdirIfNeeded: () => mkdirIfNeeded,
nodePlatform: () => nodePlatform,
parsePattern: () => parsePattern,
perMessageDeflate: () => perMessageDeflate,
removeFolders: () => removeFolders,
resolveWithinRoot: () => resolveWithinRoot,
rgb2gray: () => rgb2gray,
sanitizeForFilePath: () => sanitizeForFilePath,
serveFolder: () => serveFolder,
setBoxedStackPrefixes: () => setBoxedStackPrefixes,
setPlaywrightTestProcessEnv: () => setPlaywrightTestProcessEnv,
shortPlatform: () => shortPlatform,
spawnAsync: () => spawnAsync,
srgb2xyz: () => srgb2xyz,
ssim: () => ssim,
startHttpServer: () => startHttpServer,
startProfiling: () => startProfiling,
stopProfiling: () => stopProfiling,
stringWidth: () => stringWidth,
toPosixPath: () => toPosixPath,
urlHostFromAddress: () => urlHostFromAddress,
wrapInASCIIBox: () => wrapInASCIIBox,
xyz2lab: () => xyz2lab
});
var init_utils = __esm({
"packages/utils/index.ts"() {
"use strict";
init_ascii();
init_chromiumChannels();
init_comparators();
init_crypto();
init_debug();
init_debugLogger();
init_env();
init_eventsHelper();
init_fileUtils();
init_hostPlatform();
init_httpServer();
init_network();
init_nodePlatform();
init_processLauncher();
init_profiler();
init_serializedFS();
init_socksProxy();
init_spawnAsync();
init_stringWidth();
init_task();
init_wsServer();
init_zipFile();
init_zones();
init_extractZip();
init_lockfile();
init_colorUtils();
init_compare();
init_imageChannel();
init_stats();
}
});
// packages/playwright-core/src/client/eventEmitter.ts
function checkListener(listener) {
if (typeof listener !== "function")
throw new TypeError('The "listener" argument must be of type Function. Received type ' + typeof listener);
}
function unwrapListener(l) {
return wrappedListener(l) ?? l;
}
function unwrapListeners(arr) {
return arr.map((l) => wrappedListener(l) ?? l);
}
function wrappedListener(l) {
return l.listener;
}
var EventEmitter3, OnceWrapper;
var init_eventEmitter = __esm({
"packages/playwright-core/src/client/eventEmitter.ts"() {
"use strict";
EventEmitter3 = class {
constructor(platform) {
this._events = void 0;
this._eventsCount = 0;
this._maxListeners = void 0;
this._pendingHandlers = /* @__PURE__ */ new Map();
this._platform = platform;
if (this._events === void 0 || this._events === Object.getPrototypeOf(this)._events) {
this._events = /* @__PURE__ */ Object.create(null);
this._eventsCount = 0;
}
this._maxListeners = this._maxListeners || void 0;
this.on = this.addListener;
this.off = this.removeListener;
}
setMaxListeners(n) {
if (typeof n !== "number" || n < 0 || Number.isNaN(n))
throw new RangeError('The value of "n" is out of range. It must be a non-negative number. Received ' + n + ".");
this._maxListeners = n;
return this;
}
getMaxListeners() {
return this._maxListeners === void 0 ? this._platform.defaultMaxListeners() : this._maxListeners;
}
emit(type3, ...args) {
const events = this._events;
if (events === void 0)
return false;
const handler = events?.[type3];
if (handler === void 0)
return false;
if (typeof handler === "function") {
this._callHandler(type3, handler, args);
} else {
const len = handler.length;
const listeners = handler.slice();
for (let i = 0; i < len; ++i)
this._callHandler(type3, listeners[i], args);
}
return true;
}
_callHandler(type3, handler, args) {
const promise = Reflect.apply(handler, this, args);
if (!(promise instanceof Promise))
return;
let set = this._pendingHandlers.get(type3);
if (!set) {
set = /* @__PURE__ */ new Set();
this._pendingHandlers.set(type3, set);
}
set.add(promise);
promise.catch((e) => {
if (this._rejectionHandler)
this._rejectionHandler(e);
else
throw e;
}).finally(() => set.delete(promise));
}
addListener(type3, listener) {
return this._addListener(type3, listener, false);
}
on(type3, listener) {
return this._addListener(type3, listener, false);
}
_addListener(type3, listener, prepend) {
checkListener(listener);
let events = this._events;
let existing;
if (events === void 0) {
events = this._events = /* @__PURE__ */ Object.create(null);
this._eventsCount = 0;
} else {
if (events.newListener !== void 0) {
this.emit("newListener", type3, unwrapListener(listener));
events = this._events;
}
existing = events[type3];
}
if (existing === void 0) {
existing = events[type3] = listener;
++this._eventsCount;
} else {
if (typeof existing === "function") {
existing = events[type3] = prepend ? [listener, existing] : [existing, listener];
} else if (prepend) {
existing.unshift(listener);
} else {
existing.push(listener);
}
const m = this.getMaxListeners();
if (m > 0 && existing.length > m && !existing.warned) {
existing.warned = true;
const w = new Error("Possible EventEmitter memory leak detected. " + existing.length + " " + String(type3) + " listeners added. Use emitter.setMaxListeners() to increase limit");
w.name = "MaxListenersExceededWarning";
w.emitter = this;
w.type = type3;
w.count = existing.length;
if (!this._platform.isUnderTest()) {
console.warn(w);
}
}
}
return this;
}
prependListener(type3, listener) {
return this._addListener(type3, listener, true);
}
once(type3, listener) {
checkListener(listener);
this.on(type3, new OnceWrapper(this, type3, listener).wrapperFunction);
return this;
}
prependOnceListener(type3, listener) {
checkListener(listener);
this.prependListener(type3, new OnceWrapper(this, type3, listener).wrapperFunction);
return this;
}
removeListener(type3, listener) {
checkListener(listener);
const events = this._events;
if (events === void 0)
return this;
const list = events[type3];
if (list === void 0)
return this;
if (list === listener || list.listener === listener) {
if (--this._eventsCount === 0) {
this._events = /* @__PURE__ */ Object.create(null);
} else {
delete events[type3];
if (events.removeListener)
this.emit("removeListener", type3, list.listener ?? listener);
}
} else if (typeof list !== "function") {
let position = -1;
let originalListener;
for (let i = list.length - 1; i >= 0; i--) {
if (list[i] === listener || wrappedListener(list[i]) === listener) {
originalListener = wrappedListener(list[i]);
position = i;
break;
}
}
if (position < 0)
return this;
if (position === 0)
list.shift();
else
list.splice(position, 1);
if (list.length === 1)
events[type3] = list[0];
if (events.removeListener !== void 0)
this.emit("removeListener", type3, originalListener || listener);
}
return this;
}
off(type3, listener) {
return this.removeListener(type3, listener);
}
removeAllListeners(type3, options2) {
this._removeAllListeners(type3);
if (!options2)
return this;
if (options2.behavior === "wait") {
const errors = [];
this._rejectionHandler = (error) => errors.push(error);
return this._waitFor(type3).then(() => {
if (errors.length)
throw errors[0];
});
}
if (options2.behavior === "ignoreErrors")
this._rejectionHandler = () => {
};
return Promise.resolve();
}
_removeAllListeners(type3) {
const events = this._events;
if (!events)
return;
if (!events.removeListener) {
if (type3 === void 0) {
this._events = /* @__PURE__ */ Object.create(null);
this._eventsCount = 0;
} else if (events[type3] !== void 0) {
if (--this._eventsCount === 0)
this._events = /* @__PURE__ */ Object.create(null);
else
delete events[type3];
}
return;
}
if (type3 === void 0) {
const keys = Object.keys(events);
let key;
for (let i = 0; i < keys.length; ++i) {
key = keys[i];
if (key === "removeListener")
continue;
this._removeAllListeners(key);
}
this._removeAllListeners("removeListener");
this._events = /* @__PURE__ */ Object.create(null);
this._eventsCount = 0;
return;
}
const listeners = events[type3];
if (typeof listeners === "function") {
this.removeListener(type3, listeners);
} else if (listeners !== void 0) {
for (let i = listeners.length - 1; i >= 0; i--)
this.removeListener(type3, listeners[i]);
}
}
listeners(type3) {
return this._listeners(this, type3, true);
}
rawListeners(type3) {
return this._listeners(this, type3, false);
}
listenerCount(type3) {
const events = this._events;
if (events !== void 0) {
const listener = events[type3];
if (typeof listener === "function")
return 1;
if (listener !== void 0)
return listener.length;
}
return 0;
}
eventNames() {
return this._eventsCount > 0 && this._events ? Reflect.ownKeys(this._events) : [];
}
async _waitFor(type3) {
let promises = [];
if (type3) {
promises = [...this._pendingHandlers.get(type3) || []];
} else {
promises = [];
for (const [, pending] of this._pendingHandlers)
promises.push(...pending);
}
await Promise.all(promises);
}
_listeners(target, type3, unwrap) {
const events = target._events;
if (events === void 0)
return [];
const listener = events[type3];
if (listener === void 0)
return [];
if (typeof listener === "function")
return unwrap ? [unwrapListener(listener)] : [listener];
return unwrap ? unwrapListeners(listener) : listener.slice();
}
};
OnceWrapper = class {
constructor(eventEmitter, eventType, listener) {
this._fired = false;
this._eventEmitter = eventEmitter;
this._eventType = eventType;
this._listener = listener;
this.wrapperFunction = this._handle.bind(this);
this.wrapperFunction.listener = listener;
}
_handle(...args) {
if (this._fired)
return;
this._fired = true;
this._eventEmitter.removeListener(this._eventType, this.wrapperFunction);
return this._listener.apply(this._eventEmitter, args);
}
};
}
});
// packages/playwright-core/src/bootstrap.ts
var init_bootstrap = __esm({
"packages/playwright-core/src/bootstrap.ts"() {
"use strict";
if (process.env.PW_INSTRUMENT_MODULES) {
const Module = require("module");
const originalLoad = Module._load;
const root = { name: "<root>", selfMs: 0, totalMs: 0, childrenMs: 0, children: [] };
let current = root;
const stack = [];
Module._load = function(request2, _parent, _isMain) {
const node = { name: request2, selfMs: 0, totalMs: 0, childrenMs: 0, children: [] };
current.children.push(node);
stack.push(current);
current = node;
const start3 = performance.now();
let result2;
try {
result2 = originalLoad.apply(this, arguments);
} catch (e) {
current = stack.pop();
current.children.pop();
throw e;
}
const duration = performance.now() - start3;
node.totalMs = duration;
node.selfMs = Math.max(0, duration - node.childrenMs);
current = stack.pop();
current.childrenMs += duration;
return result2;
};
process.on("exit", () => {
function printTree(node, prefix, isLast, lines2, depth) {
if (node.totalMs < 1 && depth > 0)
return;
const connector = depth === 0 ? "" : isLast ? "\u2514\u2500\u2500 " : "\u251C\u2500\u2500 ";
const time = `${node.totalMs.toFixed(1).padStart(8)}ms`;
const self = node.children.length ? ` (self: ${node.selfMs.toFixed(1)}ms)` : "";
lines2.push(`${time} ${prefix}${connector}${node.name}${self}`);
const childPrefix = prefix + (depth === 0 ? "" : isLast ? " " : "\u2502 ");
const sorted2 = node.children.slice().sort((a, b) => b.totalMs - a.totalMs);
for (let i = 0; i < sorted2.length; i++)
printTree(sorted2[i], childPrefix, i === sorted2.length - 1, lines2, depth + 1);
}
let totalModules = 0;
function count(n) {
totalModules++;
n.children.forEach(count);
}
root.children.forEach(count);
const lines = [];
const sorted = root.children.slice().sort((a, b) => b.totalMs - a.totalMs);
for (let i = 0; i < sorted.length; i++)
printTree(sorted[i], "", i === sorted.length - 1, lines, 0);
const totalMs = root.children.reduce((s, c) => s + c.totalMs, 0);
process.stderr.write(`
--- Module load tree: ${totalModules} modules, ${totalMs.toFixed(0)}ms total ---
` + lines.join("\n") + "\n");
const flat = /* @__PURE__ */ new Map();
function gather(n) {
const existing = flat.get(n.name);
if (existing) {
existing.selfMs += n.selfMs;
existing.totalMs += n.totalMs;
existing.count++;
} else {
flat.set(n.name, { selfMs: n.selfMs, totalMs: n.totalMs, count: 1 });
}
n.children.forEach(gather);
}
root.children.forEach(gather);
const top50 = [...flat.entries()].sort((a, b) => b[1].selfMs - a[1].selfMs).slice(0, 50);
const flatLines = top50.map(
([mod, { selfMs, totalMs: totalMs2, count: count2 }]) => `${selfMs.toFixed(1).padStart(8)}ms self ${totalMs2.toFixed(1).padStart(8)}ms total (x${String(count2).padStart(3)}) ${mod}`
);
process.stderr.write(`
--- Top 50 modules by self time ---
` + flatLines.join("\n") + "\n");
});
}
}
});
// packages/playwright-core/src/package.ts
function libPath(...parts) {
return import_path8.default.join(packageRoot, "lib", ...parts);
}
var import_path8, packageRoot, packageJSON, binPath;
var init_package = __esm({
"packages/playwright-core/src/package.ts"() {
"use strict";
import_path8 = __toESM(require("path"));
packageRoot = import_path8.default.join(__dirname, "..");
packageJSON = require(import_path8.default.join(packageRoot, "package.json"));
binPath = import_path8.default.join(packageRoot, "bin");
}
});
// packages/playwright-core/src/tools/trace/traceParser.ts
async function extractTrace(traceFile, outDir) {
const zipFile = new ZipFile(traceFile);
try {
const entries = await zipFile.entries();
for (const entry of entries) {
const outPath = resolveWithinRoot(outDir, entry);
if (!outPath)
throw new Error(`Trace entry '${entry}' escapes output directory`);
await import_fs10.default.promises.mkdir(import_path9.default.dirname(outPath), { recursive: true });
const buffer = await zipFile.read(entry);
await import_fs10.default.promises.writeFile(outPath, buffer);
}
} finally {
zipFile.close();
}
}
var import_fs10, import_path9, DirTraceLoaderBackend;
var init_traceParser = __esm({
"packages/playwright-core/src/tools/trace/traceParser.ts"() {
"use strict";
import_fs10 = __toESM(require("fs"));
import_path9 = __toESM(require("path"));
init_fileUtils();
init_zipFile();
DirTraceLoaderBackend = class {
constructor(dir) {
this._dir = dir;
}
isLive() {
return false;
}
async entryNames() {
const entries = [];
const walk = async (dir, prefix) => {
const items = await import_fs10.default.promises.readdir(dir, { withFileTypes: true });
for (const item of items) {
if (item.isDirectory())
await walk(import_path9.default.join(dir, item.name), prefix ? `${prefix}/${item.name}` : item.name);
else
entries.push(prefix ? `${prefix}/${item.name}` : item.name);
}
};
await walk(this._dir, "");
return entries;
}
async hasEntry(entryName) {
const resolved = resolveWithinRoot(this._dir, entryName);
if (!resolved)
return false;
try {
await import_fs10.default.promises.access(resolved);
return true;
} catch {
return false;
}
}
async readText(entryName) {
const resolved = resolveWithinRoot(this._dir, entryName);
if (!resolved)
return;
try {
return await import_fs10.default.promises.readFile(resolved, "utf-8");
} catch {
}
}
async readBlob(entryName) {
const resolved = resolveWithinRoot(this._dir, entryName);
if (!resolved)
return;
try {
const buffer = await import_fs10.default.promises.readFile(resolved);
return new Blob([new Uint8Array(buffer)]);
} catch {
}
}
};
}
});
// packages/playwright-core/src/tools/trace/traceUtils.ts
function ensureTraceOpen() {
if (!import_fs11.default.existsSync(traceDir))
throw new Error(`No trace opened. Run 'npx playwright trace open <file>' first.`);
return traceDir;
}
async function closeTrace() {
if (import_fs11.default.existsSync(traceDir))
await import_fs11.default.promises.rm(traceDir, { recursive: true });
}
async function openTrace(traceFile) {
const filePath = import_path10.default.resolve(traceFile);
if (!import_fs11.default.existsSync(filePath))
throw new Error(`Trace file not found: ${filePath}`);
await closeTrace();
await import_fs11.default.promises.mkdir(traceDir, { recursive: true });
if (filePath.endsWith(".zip"))
await extractTrace(filePath, traceDir);
else
await import_fs11.default.promises.writeFile(import_path10.default.join(traceDir, ".link"), filePath, "utf-8");
}
async function loadTrace() {
const dir = ensureTraceOpen();
const linkFile = import_path10.default.join(dir, ".link");
let traceDir2;
let traceFile;
if (import_fs11.default.existsSync(linkFile)) {
const tracePath = await import_fs11.default.promises.readFile(linkFile, "utf-8");
traceDir2 = import_path10.default.dirname(tracePath);
traceFile = import_path10.default.basename(tracePath);
} else {
traceDir2 = dir;
}
const backend = new DirTraceLoaderBackend(traceDir2);
const loader = new TraceLoader();
await loader.load(backend, traceFile);
const model = new TraceModel(traceDir2, loader.contextEntries);
return new LoadedTrace(model, loader, buildOrdinalMap(model));
}
function formatTimestamp(ms, base) {
const relative = ms - base;
if (relative < 0)
return "0:00.000";
const totalMs = Math.floor(relative);
const minutes = Math.floor(totalMs / 6e4);
const seconds = Math.floor(totalMs % 6e4 / 1e3);
const millis = totalMs % 1e3;
return `${minutes}:${seconds.toString().padStart(2, "0")}.${millis.toString().padStart(3, "0")}`;
}
function actionTitle(action) {
return renderTitleForCall({ ...action, type: action.class }) || `${action.class}.${action.method}`;
}
async function saveOutputFile(fileName, content, explicitOutput) {
let outFile;
if (explicitOutput) {
outFile = explicitOutput;
} else {
const resolved = resolveWithinRoot(cliOutputDir, fileName);
if (!resolved)
throw new Error(`Attachment name '${fileName}' escapes output directory`);
await import_fs11.default.promises.mkdir(import_path10.default.dirname(resolved), { recursive: true });
outFile = resolved;
}
await import_fs11.default.promises.writeFile(outFile, content);
return outFile;
}
function buildOrdinalMap(model) {
const actions = model.actions.filter((a) => a.group !== "configuration");
const { rootItem } = buildActionTree(actions);
const ordinalToCallId = /* @__PURE__ */ new Map();
const callIdToOrdinal = /* @__PURE__ */ new Map();
let ordinal = 1;
const visit = (item) => {
ordinalToCallId.set(ordinal, item.action.callId);
callIdToOrdinal.set(item.action.callId, ordinal);
ordinal++;
for (const child of item.children)
visit(child);
};
for (const child of rootItem.children)
visit(child);
return { ordinalToCallId, callIdToOrdinal };
}
var import_fs11, import_path10, traceDir, cliOutputDir, LoadedTrace;
var init_traceUtils2 = __esm({
"packages/playwright-core/src/tools/trace/traceUtils.ts"() {
"use strict";
import_fs11 = __toESM(require("fs"));
import_path10 = __toESM(require("path"));
init_traceModel();
init_traceLoader();
init_protocolFormatter();
init_fileUtils();
init_traceParser();
traceDir = import_path10.default.join(".playwright-cli", "trace");
cliOutputDir = ".playwright-cli";
LoadedTrace = class {
constructor(model, loader, ordinals) {
this.model = model;
this.loader = loader;
this.ordinalToCallId = ordinals.ordinalToCallId;
this.callIdToOrdinal = ordinals.callIdToOrdinal;
}
resolveActionId(actionId) {
const ordinal = parseInt(actionId, 10);
if (!isNaN(ordinal)) {
const callId = this.ordinalToCallId.get(ordinal);
if (callId)
return this.model.actions.find((a) => a.callId === callId);
}
return this.model.actions.find((a) => a.callId === actionId);
}
};
}
});
// packages/playwright-core/src/tools/trace/traceOpen.ts
async function traceOpen(traceFile) {
await openTrace(traceFile);
await traceInfo();
}
async function traceInfo() {
const trace = await loadTrace();
const model = trace.model;
const info = {
browser: model.browserName || "unknown",
platform: model.platform || "unknown",
playwrightVersion: model.playwrightVersion || "unknown",
title: model.title || "",
duration: msToString(model.endTime - model.startTime),
durationMs: model.endTime - model.startTime,
startTime: model.wallTime ? new Date(model.wallTime).toISOString() : "unknown",
viewport: model.options.viewport ? `${model.options.viewport.width}x${model.options.viewport.height}` : "default",
actions: model.actions.length,
pages: model.pages.length,
network: model.resources.length,
errors: model.errorDescriptors.length,
attachments: model.attachments.length,
consoleMessages: model.events.filter((e) => e.type === "console").length
};
console.log("");
console.log(` Browser: ${info.browser}`);
console.log(` Platform: ${info.platform}`);
console.log(` Playwright: ${info.playwrightVersion}`);
if (info.title)
console.log(` Title: ${info.title}`);
console.log(` Duration: ${info.duration}`);
console.log(` Start time: ${info.startTime}`);
console.log(` Viewport: ${info.viewport}`);
console.log(` Actions: ${info.actions}`);
console.log(` Pages: ${info.pages}`);
console.log(` Network: ${info.network} requests`);
console.log(` Errors: ${info.errors}`);
console.log(` Attachments: ${info.attachments}`);
console.log(` Console: ${info.consoleMessages} messages`);
console.log("");
}
var init_traceOpen = __esm({
"packages/playwright-core/src/tools/trace/traceOpen.ts"() {
"use strict";
init_formatUtils();
init_traceUtils2();
}
});
// packages/playwright-core/src/tools/trace/traceActions.ts
async function traceActions(options2) {
const trace = await loadTrace();
const actions = filterActions(trace.model.actions, options2);
const { rootItem } = buildActionTree(actions);
console.log(` ${"#".padStart(4)} ${"Time".padEnd(9)} ${"Action".padEnd(55)} ${"Duration".padStart(8)}`);
console.log(` ${"\u2500".repeat(4)} ${"\u2500".repeat(9)} ${"\u2500".repeat(55)} ${"\u2500".repeat(8)}`);
const visit = (item, indent) => {
const action = item.action;
const ordinal = trace.callIdToOrdinal.get(action.callId) ?? "?";
const ts = formatTimestamp(action.startTime, trace.model.startTime);
const duration = action.endTime ? msToString(action.endTime - action.startTime) : "running";
const title = actionTitle(action);
const locator2 = actionLocator(action);
const error = action.error ? " \u2717" : "";
const prefix = ` ${(ordinal + ".").padStart(4)} ${ts} ${indent}`;
console.log(`${prefix}${title.padEnd(Math.max(1, 55 - indent.length))} ${duration.padStart(8)}${error}`);
if (locator2)
console.log(`${" ".repeat(prefix.length)}${locator2}`);
for (const child of item.children)
visit(child, indent + " ");
};
for (const child of rootItem.children)
visit(child, "");
}
function filterActions(actions, options2) {
let result2 = actions.filter((a) => a.group !== "configuration");
if (options2.grep) {
const pattern = new RegExp(options2.grep, "i");
result2 = result2.filter((a) => pattern.test(actionTitle(a)) || pattern.test(actionLocator(a) || ""));
}
if (options2.errorsOnly)
result2 = result2.filter((a) => !!a.error);
return result2;
}
function actionLocator(action, sdkLanguage) {
return action.params.selector ? asLocatorDescription(sdkLanguage || "javascript", action.params.selector) : void 0;
}
async function traceAction(actionId) {
const trace = await loadTrace();
const action = trace.resolveActionId(actionId);
if (!action) {
console.error(`Action '${actionId}' not found. Use 'trace actions' to see available action IDs.`);
process.exitCode = 1;
return;
}
const title = actionTitle(action);
console.log(`
${title}
`);
console.log(" Time");
console.log(` start: ${formatTimestamp(action.startTime, trace.model.startTime)}`);
const duration = action.endTime ? msToString(action.endTime - action.startTime) : action.error ? "Timed Out" : "Running";
console.log(` duration: ${duration}`);
const paramKeys = Object.keys(action.params).filter((name) => name !== "info");
if (paramKeys.length) {
console.log("\n Parameters");
for (const key of paramKeys) {
const value2 = formatParamValue(action.params[key]);
console.log(` ${key}: ${value2}`);
}
}
if (action.result) {
console.log("\n Return value");
for (const [key, value2] of Object.entries(action.result))
console.log(` ${key}: ${formatParamValue(value2)}`);
}
if (action.error) {
console.log("\n Error");
console.log(` ${action.error.message}`);
}
if (action.log.length) {
console.log("\n Log");
for (const entry of action.log) {
const time = entry.time !== -1 ? formatTimestamp(entry.time, trace.model.startTime) : "";
console.log(` ${time.padEnd(12)} ${entry.message}`);
}
}
if (action.stack?.length) {
console.log("\n Source");
for (const frame of action.stack.slice(0, 5)) {
const file = frame.file.replace(/.*[/\\](.*)/, "$1");
console.log(` ${file}:${frame.line}:${frame.column}`);
}
}
const snapshots = [];
if (action.beforeSnapshot)
snapshots.push("before");
if (action.inputSnapshot)
snapshots.push("input");
if (action.afterSnapshot)
snapshots.push("after");
if (snapshots.length) {
console.log("\n Snapshots");
console.log(` available: ${snapshots.join(", ")}`);
console.log(` usage: npx playwright trace snapshot ${actionId} --name <${snapshots.join("|")}>`);
}
console.log("");
}
function formatParamValue(value2) {
if (value2 === void 0 || value2 === null)
return String(value2);
if (typeof value2 === "string")
return `"${value2}"`;
if (typeof value2 !== "object")
return String(value2);
if (value2.guid)
return "<handle>";
return JSON.stringify(value2).slice(0, 1e3);
}
var init_traceActions = __esm({
"packages/playwright-core/src/tools/trace/traceActions.ts"() {
"use strict";
init_traceModel();
init_locatorGenerators();
init_formatUtils();
init_traceUtils2();
}
});
// packages/playwright-core/src/tools/trace/traceRequests.ts
async function traceRequests(options2) {
const trace = await loadTrace();
const model = trace.model;
let indexed = model.resources.map((r, i) => ({ resource: r, ordinal: i + 1 }));
if (options2.grep) {
const pattern = new RegExp(options2.grep, "i");
indexed = indexed.filter(({ resource: r }) => pattern.test(r.request.url));
}
if (options2.method)
indexed = indexed.filter(({ resource: r }) => r.request.method.toLowerCase() === options2.method.toLowerCase());
if (options2.status) {
const code = parseInt(options2.status, 10);
indexed = indexed.filter(({ resource: r }) => r.response.status === code);
}
if (options2.failed)
indexed = indexed.filter(({ resource: r }) => r.response.status >= 400 || r.response.status === -1);
if (!indexed.length) {
console.log(" No network requests");
return;
}
console.log(` ${"#".padStart(4)} ${"Method".padEnd(8)} ${"Status".padEnd(8)} ${"Name".padEnd(45)} ${"Duration".padStart(10)} ${"Size".padStart(8)} ${"Route".padEnd(10)}`);
console.log(` ${"\u2500".repeat(4)} ${"\u2500".repeat(8)} ${"\u2500".repeat(8)} ${"\u2500".repeat(45)} ${"\u2500".repeat(10)} ${"\u2500".repeat(8)} ${"\u2500".repeat(10)}`);
for (const { resource: r, ordinal } of indexed) {
let name;
try {
const url2 = new URL(r.request.url);
name = url2.pathname.substring(url2.pathname.lastIndexOf("/") + 1);
if (!name)
name = url2.host;
if (url2.search)
name += url2.search;
} catch {
name = r.request.url;
}
if (name.length > 45)
name = name.substring(0, 42) + "...";
const status = r.response.status > 0 ? String(r.response.status) : "ERR";
const size = r.response._transferSize > 0 ? r.response._transferSize : r.response.bodySize;
const route2 = formatRouteStatus(r);
console.log(` ${(ordinal + ".").padStart(4)} ${r.request.method.padEnd(8)} ${status.padEnd(8)} ${name.padEnd(45)} ${msToString(r.time).padStart(10)} ${bytesToString2(size).padStart(8)} ${route2.padEnd(10)}`);
}
}
async function traceRequest(requestId) {
const trace = await loadTrace();
const model = trace.model;
const ordinal = parseInt(requestId, 10);
const resource = !isNaN(ordinal) && ordinal >= 1 && ordinal <= model.resources.length ? model.resources[ordinal - 1] : void 0;
if (!resource) {
console.error(`Request '${requestId}' not found. Use 'trace requests' to see available request IDs.`);
process.exitCode = 1;
return;
}
const r = resource;
const status = r.response.status > 0 ? `${r.response.status} ${r.response.statusText}` : "ERR";
const size = r.response._transferSize > 0 ? r.response._transferSize : r.response.bodySize;
console.log(`
${r.request.method} ${r.request.url}
`);
console.log(" General");
console.log(` status: ${status}`);
console.log(` duration: ${msToString(r.time)}`);
console.log(` size: ${bytesToString2(size)}`);
if (r.response.content.mimeType)
console.log(` type: ${r.response.content.mimeType}`);
const route2 = formatRouteStatus(r);
if (route2)
console.log(` route: ${route2}`);
if (r.serverIPAddress)
console.log(` server: ${r.serverIPAddress}${r._serverPort ? ":" + r._serverPort : ""}`);
if (r.response._failureText)
console.log(` error: ${r.response._failureText}`);
if (r.request.headers.length) {
console.log("\n Request headers");
for (const h of r.request.headers)
console.log(` ${h.name}: ${h.value}`);
}
if (r.request.postData) {
console.log("\n Request body");
const resource2 = r.request.postData._sha1 ?? r.request.postData._file;
if (resource2) {
console.log(` ${import_path11.default.relative(process.cwd(), import_path11.default.join(trace.model.traceUri, "resources", resource2))}`);
} else {
const text2 = r.request.postData.text.length > 2e3 ? r.request.postData.text.substring(0, 2e3) + "..." : r.request.postData.text;
console.log(` ${text2}`);
}
}
if (r.response.headers.length) {
console.log("\n Response headers");
for (const h of r.response.headers)
console.log(` ${h.name}: ${h.value}`);
}
if (r.response.bodySize > 0) {
const resource2 = r.response.content._sha1 ?? r.response.content._file;
if (resource2) {
console.log("\n Response body");
console.log(` ${import_path11.default.relative(process.cwd(), import_path11.default.join(trace.model.traceUri, "resources", resource2))}`);
} else if (r.response.content.text) {
const text2 = r.response.content.text.length > 2e3 ? r.response.content.text.substring(0, 2e3) + "..." : r.response.content.text;
console.log("\n Response body");
console.log(` ${text2}`);
}
}
if (r._securityDetails) {
console.log("\n Security");
if (r._securityDetails.protocol)
console.log(` protocol: ${r._securityDetails.protocol}`);
if (r._securityDetails.subjectName)
console.log(` subject: ${r._securityDetails.subjectName}`);
if (r._securityDetails.issuer)
console.log(` issuer: ${r._securityDetails.issuer}`);
}
console.log("");
}
function bytesToString2(bytes) {
if (bytes < 0 || !isFinite(bytes))
return "-";
if (bytes === 0)
return "0";
if (bytes < 1e3)
return bytes.toFixed(0);
const kb = bytes / 1024;
if (kb < 1e3)
return kb.toFixed(1) + "K";
const mb = kb / 1024;
if (mb < 1e3)
return mb.toFixed(1) + "M";
const gb = mb / 1024;
return gb.toFixed(1) + "G";
}
function formatRouteStatus(r) {
if (r._wasAborted)
return "aborted";
if (r._wasContinued)
return "continued";
if (r._wasFulfilled)
return "fulfilled";
if (r._apiRequest)
return "api";
return "";
}
var import_path11;
var init_traceRequests = __esm({
"packages/playwright-core/src/tools/trace/traceRequests.ts"() {
"use strict";
import_path11 = __toESM(require("path"));
init_formatUtils();
init_traceUtils2();
}
});
// packages/playwright-core/src/tools/trace/traceConsole.ts
async function traceConsole(options2) {
const trace = await loadTrace();
const model = trace.model;
const items = [];
for (const event of model.events) {
if (event.type === "console") {
if (options2.stdio)
continue;
const level = event.messageType;
if (options2.errorsOnly && level !== "error")
continue;
if (options2.warnings && level !== "error" && level !== "warning")
continue;
const url2 = event.location.url;
const filename = url2 ? url2.substring(url2.lastIndexOf("/") + 1) : "<anonymous>";
items.push({
type: "browser",
level,
text: event.text,
location: `${filename}:${event.location.lineNumber}`,
timestamp: event.time
});
}
if (event.type === "event" && event.method === "pageError") {
if (options2.stdio)
continue;
const error = event.params.error;
items.push({
type: "browser",
level: "error",
text: error?.error?.message || String(error?.value || ""),
timestamp: event.time
});
}
}
for (const event of model.stdio) {
if (options2.browser)
continue;
if (options2.errorsOnly && event.type !== "stderr")
continue;
if (options2.warnings && event.type !== "stderr")
continue;
let text2 = "";
if (event.text)
text2 = event.text.trim();
if (event.base64)
text2 = Buffer.from(event.base64, "base64").toString("utf-8").trim();
if (!text2)
continue;
items.push({
type: event.type,
level: event.type === "stderr" ? "error" : "info",
text: text2,
timestamp: event.timestamp
});
}
items.sort((a, b) => a.timestamp - b.timestamp);
if (!items.length) {
console.log(" No console entries");
return;
}
for (const item of items) {
const ts = formatTimestamp(item.timestamp, model.startTime);
const source8 = item.type === "browser" ? "[browser]" : `[${item.type}]`;
const level = item.level.padEnd(8);
const location2 = item.location ? ` ${item.location}` : "";
console.log(` ${ts} ${source8.padEnd(10)} ${level} ${item.text}${location2}`);
}
}
var init_traceConsole = __esm({
"packages/playwright-core/src/tools/trace/traceConsole.ts"() {
"use strict";
init_traceUtils2();
}
});
// packages/playwright-core/src/tools/trace/traceErrors.ts
async function traceErrors() {
const trace = await loadTrace();
const model = trace.model;
if (!model.errorDescriptors.length) {
console.log(" No errors");
return;
}
for (const error of model.errorDescriptors) {
if (error.action) {
const title = actionTitle(error.action);
console.log(`
\u2717 ${title}`);
} else {
console.log(`
\u2717 Error`);
}
if (error.stack?.length) {
const frame = error.stack[0];
const file = frame.file.replace(/.*[/\\](.*)/, "$1");
console.log(` at ${file}:${frame.line}:${frame.column}`);
}
console.log("");
const indented = error.message.split("\n").map((l) => ` ${l}`).join("\n");
console.log(indented);
}
console.log("");
}
var init_traceErrors = __esm({
"packages/playwright-core/src/tools/trace/traceErrors.ts"() {
"use strict";
init_traceUtils2();
}
});
// packages/isomorphic/disposable.ts
async function disposeAll(disposables) {
const copy = [...disposables];
disposables.length = 0;
await Promise.all(copy.map((d) => d.dispose()));
}
var init_disposable = __esm({
"packages/isomorphic/disposable.ts"() {
"use strict";
}
});
// packages/playwright-core/src/server/userAgent.ts
function getUserAgent() {
if (cachedUserAgent)
return cachedUserAgent;
try {
cachedUserAgent = determineUserAgent();
} catch (e) {
cachedUserAgent = "Playwright/unknown";
}
return cachedUserAgent;
}
function determineUserAgent() {
let osIdentifier = "unknown";
let osVersion = "unknown";
if (process.platform === "win32") {
const version3 = import_os4.default.release().split(".");
osIdentifier = "windows";
osVersion = `${version3[0]}.${version3[1]}`;
} else if (process.platform === "darwin") {
const version3 = (0, import_child_process2.execSync)("sw_vers -productVersion", { stdio: ["ignore", "pipe", "ignore"] }).toString().trim().split(".");
osIdentifier = "macOS";
osVersion = `${version3[0]}.${version3[1]}`;
} else if (process.platform === "linux") {
const distroInfo = getLinuxDistributionInfoSync();
if (distroInfo) {
osIdentifier = distroInfo.id || "linux";
osVersion = distroInfo.version || "unknown";
} else {
osIdentifier = "linux";
}
}
const additionalTokens = [];
if (process.env.CI)
additionalTokens.push("CI/1");
const serializedTokens = additionalTokens.length ? " " + additionalTokens.join(" ") : "";
const { embedderName, embedderVersion } = getEmbedderName();
return `Playwright/${getPlaywrightVersion()} (${import_os4.default.arch()}; ${osIdentifier} ${osVersion}) ${embedderName}/${embedderVersion}${serializedTokens}`;
}
function getEmbedderName() {
let embedderName = "unknown";
let embedderVersion = "unknown";
if (!process.env.PW_LANG_NAME) {
embedderName = "node";
embedderVersion = process.version.substring(1).split(".").slice(0, 2).join(".");
} else if (["node", "python", "java", "csharp"].includes(process.env.PW_LANG_NAME)) {
embedderName = process.env.PW_LANG_NAME;
embedderVersion = process.env.PW_LANG_NAME_VERSION ?? "unknown";
}
return { embedderName, embedderVersion };
}
function getPlaywrightVersion(majorMinorOnly = false) {
const version3 = process.env.PW_VERSION_OVERRIDE || packageJSON.version;
return majorMinorOnly ? version3.split(".").slice(0, 2).join(".") : version3;
}
var import_child_process2, import_os4, cachedUserAgent;
var init_userAgent = __esm({
"packages/playwright-core/src/server/userAgent.ts"() {
"use strict";
import_child_process2 = require("child_process");
import_os4 = __toESM(require("os"));
init_linuxUtils();
init_package();
}
});
// packages/playwright-core/src/generated/clockSource.ts
var source;
var init_clockSource = __esm({
"packages/playwright-core/src/generated/clockSource.ts"() {
"use strict";
source = '\nvar __commonJS = obj => {\n let required = false;\n let result;\n return function __require() {\n if (!required) {\n required = true;\n let fn;\n for (const name in obj) { fn = obj[name]; break; }\n const module = { exports: {} };\n fn(module.exports, module);\n result = module.exports;\n }\n return result;\n }\n};\nvar __export = (target, all) => {for (var name in all) target[name] = all[name];};\nvar __toESM = mod => ({ ...mod, \'default\': mod });\nvar __toCommonJS = mod => ({ ...mod, __esModule: true });\n\n\n// packages/injected/src/clock.ts\nvar clock_exports = {};\n__export(clock_exports, {\n ClockController: () => ClockController,\n createClock: () => createClock,\n inject: () => inject,\n install: () => install\n});\nmodule.exports = __toCommonJS(clock_exports);\nvar ClockController = class {\n constructor(embedder) {\n this._duringTick = false;\n this._uniqueTimerId = idCounterStart;\n this.disposables = [];\n this._log = [];\n this._timers = /* @__PURE__ */ new Map();\n this._now = { time: asWallTime(0), isFixedTime: false, ticks: 0, origin: asWallTime(-1) };\n this._embedder = embedder;\n }\n uninstall() {\n this.disposables.forEach((dispose) => dispose());\n this.disposables.length = 0;\n }\n now() {\n this._replayLogOnce();\n this._syncRealTime();\n return this._now.time;\n }\n install(time) {\n this._replayLogOnce();\n this._innerInstall(asWallTime(time));\n }\n setSystemTime(time) {\n this._replayLogOnce();\n this._innerSetTime(asWallTime(time));\n }\n setFixedTime(time) {\n this._replayLogOnce();\n this._innerSetFixedTime(asWallTime(time));\n }\n performanceNow() {\n this._replayLogOnce();\n this._syncRealTime();\n return this._now.ticks;\n }\n _syncRealTime() {\n if (!this._realTime)\n return;\n const now = this._embedder.performanceNow();\n const sinceLastSync = now - this._realTime.lastSyncTicks;\n if (sinceLastSync > 0) {\n this._advanceNow(shiftTicks(this._now.ticks, sinceLastSync));\n this._realTime.lastSyncTicks = now;\n }\n }\n _innerSetTime(time) {\n this._now.time = time;\n this._now.isFixedTime = false;\n if (this._now.origin < 0)\n this._now.origin = this._now.time;\n }\n _innerInstall(time) {\n if (this._now.origin < 0)\n this._now.ticks = 0;\n this._innerSetTime(time);\n }\n _innerSetFixedTime(time) {\n this._innerSetTime(time);\n this._now.isFixedTime = true;\n }\n _advanceNow(to) {\n if (this._now.ticks > to) {\n return;\n }\n if (!this._now.isFixedTime)\n this._now.time = asWallTime(this._now.time + to - this._now.ticks);\n this._now.ticks = to;\n }\n async log(type, time, param) {\n this._log.push({ type, time, param });\n }\n async runFor(ticks) {\n this._replayLogOnce();\n if (ticks < 0)\n throw new TypeError("Negative ticks are not supported");\n await this._runWithDisabledRealTimeSync(async () => {\n await this._runTo(shiftTicks(this._now.ticks, ticks));\n });\n }\n async _runTo(to) {\n to = Math.ceil(to);\n if (this._now.ticks > to)\n return;\n let firstException;\n while (true) {\n const result = await this._callFirstTimer(to);\n if (!result.timerFound)\n break;\n firstException = firstException || result.error;\n }\n this._advanceNow(to);\n if (firstException)\n throw firstException;\n }\n async pauseAt(time) {\n this._replayLogOnce();\n await this._innerPause();\n const toConsume = time - this._now.time;\n await this._innerFastForwardTo(shiftTicks(this._now.ticks, toConsume));\n return toConsume;\n }\n async _innerPause() {\n var _a;\n this._realTime = void 0;\n await ((_a = this._currentRealTimeTimer) == null ? void 0 : _a.dispose());\n this._currentRealTimeTimer = void 0;\n }\n resume() {\n this._replayLogOnce();\n this._innerResume();\n }\n _innerResume() {\n const now = this._embedder.performanceNow();\n this._realTime = { startTicks: now, lastSyncTicks: now };\n this._updateRealTimeTimer();\n }\n _updateRealTimeTimer() {\n var _a;\n if ((_a = this._currentRealTimeTimer) == null ? void 0 : _a.promise) {\n return;\n }\n const firstTimer = this._firstTimer();\n const nextTick = Math.min(firstTimer ? firstTimer.callAt : this._now.ticks + maxTimeout, this._now.ticks + 100);\n const callAt = this._currentRealTimeTimer ? Math.min(this._currentRealTimeTimer.callAt, nextTick) : nextTick;\n if (this._currentRealTimeTimer) {\n this._currentRealTimeTimer.cancel();\n this._currentRealTimeTimer = void 0;\n }\n const realTimeTimer = {\n callAt,\n promise: void 0,\n cancel: this._embedder.setTimeout(() => {\n this._syncRealTime();\n realTimeTimer.promise = this._runTo(this._now.ticks).catch((e) => console.error(e));\n void realTimeTimer.promise.then(() => {\n this._currentRealTimeTimer = void 0;\n if (this._realTime)\n this._updateRealTimeTimer();\n });\n }, callAt - this._now.ticks),\n dispose: async () => {\n realTimeTimer.cancel();\n await realTimeTimer.promise;\n }\n };\n this._currentRealTimeTimer = realTimeTimer;\n }\n async _runWithDisabledRealTimeSync(fn) {\n if (!this._realTime) {\n await fn();\n return;\n }\n await this._innerPause();\n try {\n await fn();\n } finally {\n this._innerResume();\n }\n }\n async fastForward(ticks) {\n this._replayLogOnce();\n await this._runWithDisabledRealTimeSync(async () => {\n await this._innerFastForwardTo(shiftTicks(this._now.ticks, ticks | 0));\n });\n }\n async _innerFastForwardTo(to) {\n if (to < this._now.ticks)\n throw new Error("Cannot fast-forward to the past");\n for (const timer of this._timers.values()) {\n if (to > timer.callAt)\n timer.callAt = to;\n }\n await this._runTo(to);\n }\n addTimer(options) {\n this._replayLogOnce();\n if (options.type === "AnimationFrame" /* AnimationFrame */ && !options.func)\n throw new Error("Callback must be provided to requestAnimationFrame calls");\n if (options.type === "IdleCallback" /* IdleCallback */ && !options.func)\n throw new Error("Callback must be provided to requestIdleCallback calls");\n if (["Timeout" /* Timeout */, "Interval" /* Interval */].includes(options.type) && !options.func && options.delay === void 0)\n throw new Error("Callback must be provided to timer calls");\n let delay = options.delay ? +options.delay : 0;\n if (!Number.isFinite(delay))\n delay = 0;\n delay = delay > maxTimeout ? 1 : delay;\n delay = Math.max(0, delay);\n const timer = {\n type: options.type,\n func: options.func,\n args: options.args || [],\n delay,\n callAt: shiftTicks(this._now.ticks, delay || (this._duringTick ? 1 : 0)),\n createdAt: this._now.ticks,\n id: this._uniqueTimerId++,\n error: new Error()\n };\n this._timers.set(timer.id, timer);\n if (this._realTime)\n this._updateRealTimeTimer();\n return timer.id;\n }\n countTimers() {\n return this._timers.size;\n }\n _firstTimer(beforeTick) {\n let firstTimer = null;\n for (const timer of this._timers.values()) {\n const isInRange = beforeTick === void 0 || timer.callAt <= beforeTick;\n if (isInRange && (!firstTimer || compareTimers(firstTimer, timer) === 1))\n firstTimer = timer;\n }\n return firstTimer;\n }\n _takeFirstTimer(beforeTick) {\n const timer = this._firstTimer(beforeTick);\n if (!timer)\n return null;\n this._advanceNow(timer.callAt);\n if (timer.type === "Interval" /* Interval */)\n timer.callAt = shiftTicks(timer.callAt, timer.delay);\n else\n this._timers.delete(timer.id);\n return timer;\n }\n async _callFirstTimer(beforeTick) {\n const timer = this._takeFirstTimer(beforeTick);\n if (!timer)\n return { timerFound: false };\n this._duringTick = true;\n try {\n if (typeof timer.func !== "function") {\n let error2;\n try {\n (() => {\n globalThis.eval(timer.func);\n })();\n } catch (e) {\n error2 = e;\n }\n await new Promise((f) => this._embedder.setTimeout(f));\n return { timerFound: true, error: error2 };\n }\n let args = timer.args;\n if (timer.type === "AnimationFrame" /* AnimationFrame */)\n args = [this._now.ticks];\n else if (timer.type === "IdleCallback" /* IdleCallback */)\n args = [{ didTimeout: false, timeRemaining: () => 0 }];\n let error;\n try {\n timer.func.apply(null, args);\n } catch (e) {\n error = e;\n }\n await new Promise((f) => this._embedder.setTimeout(f));\n return { timerFound: true, error };\n } finally {\n this._duringTick = false;\n }\n }\n getTimeToNextFrame() {\n this._replayLogOnce();\n return 16 - this._now.ticks % 16;\n }\n clearTimer(timerId, type) {\n this._replayLogOnce();\n if (!timerId) {\n return;\n }\n const id = Number(timerId);\n if (Number.isNaN(id) || id < idCounterStart) {\n const handlerName = getClearHandler(type);\n new Error(`Clock: ${handlerName} was invoked to clear a native timer instead of one created by the clock library.`);\n }\n const timer = this._timers.get(id);\n if (timer) {\n if (timer.type === type || timer.type === "Timeout" && type === "Interval" || timer.type === "Interval" && type === "Timeout") {\n this._timers.delete(id);\n } else {\n const clear = getClearHandler(type);\n const schedule = getScheduleHandler(timer.type);\n throw new Error(\n `Cannot clear timer: timer created with ${schedule}() but cleared with ${clear}()`\n );\n }\n }\n }\n _replayLogOnce() {\n if (!this._log.length)\n return;\n let lastLogTime = -1;\n let isPaused = false;\n for (const { type, time, param } of this._log) {\n if (!isPaused && lastLogTime !== -1)\n this._advanceNow(shiftTicks(this._now.ticks, time - lastLogTime));\n lastLogTime = time;\n if (type === "install") {\n this._innerInstall(asWallTime(param));\n } else if (type === "fastForward" || type === "runFor") {\n this._advanceNow(shiftTicks(this._now.ticks, param));\n } else if (type === "pauseAt") {\n isPaused = true;\n this._innerSetTime(asWallTime(param));\n } else if (type === "resume") {\n isPaused = false;\n } else if (type === "setFixedTime") {\n this._innerSetFixedTime(asWallTime(param));\n } else if (type === "setSystemTime") {\n this._innerSetTime(asWallTime(param));\n }\n }\n if (!isPaused) {\n if (lastLogTime > 0)\n this._advanceNow(shiftTicks(this._now.ticks, this._embedder.dateNow() - lastLogTime));\n this._innerResume();\n } else {\n this._realTime = void 0;\n }\n this._log.length = 0;\n }\n};\nfunction mirrorDateProperties(target, source) {\n for (const prop in source) {\n if (source.hasOwnProperty(prop))\n target[prop] = source[prop];\n }\n target.toString = () => source.toString();\n target.prototype = source.prototype;\n target.parse = source.parse;\n target.UTC = source.UTC;\n target.prototype.toUTCString = source.prototype.toUTCString;\n target.isFake = true;\n return target;\n}\nfunction createDate(clock, NativeDate) {\n function ClockDate(year, month, date, hour, minute, second, ms) {\n if (!(this instanceof ClockDate))\n return new NativeDate(clock.now()).toString();\n switch (arguments.length) {\n case 0:\n return new NativeDate(clock.now());\n case 1:\n return new NativeDate(year);\n case 2:\n return new NativeDate(year, month);\n case 3:\n return new NativeDate(year, month, date);\n case 4:\n return new NativeDate(year, month, date, hour);\n case 5:\n return new NativeDate(year, month, date, hour, minute);\n case 6:\n return new NativeDate(\n year,\n month,\n date,\n hour,\n minute,\n second\n );\n default:\n return new NativeDate(\n year,\n month,\n date,\n hour,\n minute,\n second,\n ms\n );\n }\n }\n ClockDate.now = () => clock.now();\n return mirrorDateProperties(ClockDate, NativeDate);\n}\nfunction createIntl(clock, NativeIntl) {\n const ClockIntl = {};\n for (const key of Object.getOwnPropertyNames(NativeIntl))\n ClockIntl[key] = NativeIntl[key];\n ClockIntl.DateTimeFormat = function(...args) {\n const realFormatter = new NativeIntl.DateTimeFormat(...args);\n const formatter = {\n formatRange: realFormatter.formatRange.bind(realFormatter),\n formatRangeToParts: realFormatter.formatRangeToParts.bind(realFormatter),\n resolvedOptions: realFormatter.resolvedOptions.bind(realFormatter),\n format: (date) => realFormatter.format(date || clock.now()),\n formatToParts: (date) => realFormatter.formatToParts(date || clock.now())\n };\n return formatter;\n };\n ClockIntl.DateTimeFormat.prototype = Object.create(\n NativeIntl.DateTimeFormat.prototype\n );\n ClockIntl.DateTimeFormat.supportedLocalesOf = NativeIntl.DateTimeFormat.supportedLocalesOf;\n return ClockIntl;\n}\nfunction compareTimers(a, b) {\n if (a.callAt < b.callAt)\n return -1;\n if (a.callAt > b.callAt)\n return 1;\n if (a.type === "Immediate" /* Immediate */ && b.type !== "Immediate" /* Immediate */)\n return -1;\n if (a.type !== "Immediate" /* Immediate */ && b.type === "Immediate" /* Immediate */)\n return 1;\n if (a.createdAt < b.createdAt)\n return -1;\n if (a.createdAt > b.createdAt)\n return 1;\n if (a.id < b.id)\n return -1;\n if (a.id > b.id)\n return 1;\n}\nvar maxTimeout = Math.pow(2, 31) - 1;\nvar idCounterStart = 1e12;\nfunction platformOriginals(globalObject) {\n const raw = {\n setTimeout: globalObject.setTimeout,\n clearTimeout: globalObject.clearTimeout,\n setInterval: globalObject.setInterval,\n clearInterval: globalObject.clearInterval,\n requestAnimationFrame: globalObject.requestAnimationFrame ? globalObject.requestAnimationFrame : void 0,\n cancelAnimationFrame: globalObject.cancelAnimationFrame ? globalObject.cancelAnimationFrame : void 0,\n requestIdleCallback: globalObject.requestIdleCallback ? globalObject.requestIdleCallback : void 0,\n cancelIdleCallback: globalObject.cancelIdleCallback ? globalObject.cancelIdleCallback : void 0,\n Date: globalObject.Date,\n performance: globalObject.performance,\n Intl: globalObject.Intl,\n AbortSignal: globalObject.AbortSignal\n };\n const bound = { ...raw };\n for (const key of Object.keys(bound)) {\n if (key !== "Date" && key !== "AbortSignal" && typeof bound[key] === "function")\n bound[key] = bound[key].bind(globalObject);\n }\n return { raw, bound };\n}\nfunction getScheduleHandler(type) {\n if (type === "IdleCallback" || type === "AnimationFrame")\n return `request${type}`;\n return `set${type}`;\n}\nfunction createApi(clock, originals, browserName) {\n return {\n setTimeout: (func, timeout, ...args) => {\n const delay = timeout ? +timeout : timeout;\n return clock.addTimer({\n type: "Timeout" /* Timeout */,\n func,\n args,\n delay\n });\n },\n clearTimeout: (timerId) => {\n if (timerId)\n clock.clearTimer(timerId, "Timeout" /* Timeout */);\n },\n setInterval: (func, timeout, ...args) => {\n const delay = timeout ? +timeout : timeout;\n return clock.addTimer({\n type: "Interval" /* Interval */,\n func,\n args,\n delay\n });\n },\n clearInterval: (timerId) => {\n if (timerId)\n return clock.clearTimer(timerId, "Interval" /* Interval */);\n },\n requestAnimationFrame: (callback) => {\n return clock.addTimer({\n type: "AnimationFrame" /* AnimationFrame */,\n func: callback,\n delay: clock.getTimeToNextFrame()\n });\n },\n cancelAnimationFrame: (timerId) => {\n if (timerId)\n return clock.clearTimer(timerId, "AnimationFrame" /* AnimationFrame */);\n },\n requestIdleCallback: (callback, options) => {\n let timeToNextIdlePeriod = 0;\n if (clock.countTimers() > 0)\n timeToNextIdlePeriod = 50;\n return clock.addTimer({\n type: "IdleCallback" /* IdleCallback */,\n func: callback,\n delay: (options == null ? void 0 : options.timeout) ? Math.min(options == null ? void 0 : options.timeout, timeToNextIdlePeriod) : timeToNextIdlePeriod\n });\n },\n cancelIdleCallback: (timerId) => {\n if (timerId)\n return clock.clearTimer(timerId, "IdleCallback" /* IdleCallback */);\n },\n Intl: originals.Intl ? createIntl(clock, originals.Intl) : void 0,\n Date: createDate(clock, originals.Date),\n performance: originals.performance ? fakePerformance(clock, originals.performance) : void 0,\n AbortSignal: originals.AbortSignal ? fakeAbortSignal(clock, originals.AbortSignal, browserName) : void 0\n };\n}\nfunction getClearHandler(type) {\n if (type === "IdleCallback" || type === "AnimationFrame")\n return `cancel${type}`;\n return `clear${type}`;\n}\nvar FakePerformanceEntry = class {\n constructor(name, entryType, startTime, duration) {\n this.name = name;\n this.entryType = entryType;\n this.startTime = startTime;\n this.duration = duration;\n }\n toJSON() {\n return JSON.stringify({ ...this });\n }\n};\nfunction fakePerformance(clock, performance) {\n const result = {\n now: () => clock.performanceNow()\n };\n result.__defineGetter__("timeOrigin", () => clock._now.origin || 0);\n for (const key of Object.keys(performance.__proto__)) {\n if (key === "now" || key === "timeOrigin")\n continue;\n if (key === "getEntries" || key === "getEntriesByName" || key === "getEntriesByType")\n result[key] = () => [];\n else if (key === "mark")\n result[key] = (name) => new FakePerformanceEntry(name, "mark", 0, 0);\n else if (key === "measure")\n result[key] = (name) => new FakePerformanceEntry(name, "measure", 0, 50);\n else\n result[key] = () => {\n };\n }\n return result;\n}\nfunction fakeAbortSignal(clock, abortSignal, browserName) {\n Object.defineProperty(abortSignal, "timeout", {\n value(ms) {\n const controller = new AbortController();\n clock.addTimer({\n delay: ms,\n type: "Timeout" /* Timeout */,\n func: () => controller.abort(\n new DOMException(\n browserName === "chromium" ? "signal timed out" : "The operation timed out.",\n "TimeoutError"\n )\n )\n });\n return controller.signal;\n }\n });\n return abortSignal;\n}\nfunction createClock(globalObject, config = {}) {\n const originals = platformOriginals(globalObject);\n const embedder = {\n dateNow: () => originals.raw.Date.now(),\n performanceNow: () => Math.ceil(originals.raw.performance.now()),\n setTimeout: (task, timeout) => {\n const timerId = originals.bound.setTimeout(task, timeout);\n return () => originals.bound.clearTimeout(timerId);\n },\n setInterval: (task, delay) => {\n const intervalId = originals.bound.setInterval(task, delay);\n return () => originals.bound.clearInterval(intervalId);\n }\n };\n const clock = new ClockController(embedder);\n const api = createApi(clock, originals.bound, config.browserName);\n return { clock, api, originals: originals.raw };\n}\nfunction install(globalObject, config = {}) {\n var _a, _b;\n if ((_a = globalObject.Date) == null ? void 0 : _a.isFake) {\n throw new TypeError(`Can\'t install fake timers twice on the same global object.`);\n }\n const { clock, api, originals } = createClock(globalObject, config);\n const toFake = ((_b = config.toFake) == null ? void 0 : _b.length) ? config.toFake : Object.keys(originals);\n for (const method of toFake) {\n if (method === "Date") {\n globalObject.Date = mirrorDateProperties(api.Date, globalObject.Date);\n } else if (method === "Intl") {\n globalObject.Intl = api[method];\n } else if (method === "AbortSignal") {\n globalObject.AbortSignal = api[method];\n } else if (method === "performance") {\n globalObject.performance = api[method];\n const kEventTimeStamp = Symbol("playwrightEventTimeStamp");\n Object.defineProperty(Event.prototype, "timeStamp", {\n get() {\n var _a2;\n if (!this[kEventTimeStamp])\n this[kEventTimeStamp] = (_a2 = api.performance) == null ? void 0 : _a2.now();\n return this[kEventTimeStamp];\n }\n });\n } else {\n globalObject[method] = (...args) => {\n return api[method].apply(api, args);\n };\n }\n clock.disposables.push(() => {\n globalObject[method] = originals[method];\n });\n }\n return { clock, api, originals };\n}\nfunction inject(globalObject, browserName) {\n const builtins = platformOriginals(globalObject).bound;\n const { clock: controller } = install(globalObject, { browserName });\n controller.resume();\n return {\n controller,\n builtins\n };\n}\nfunction asWallTime(n) {\n return n;\n}\nfunction shiftTicks(ticks, ms) {\n return ticks + ms;\n}\n';
}
});
// packages/playwright-core/src/protocol/serializers.ts
function parseSerializedValue(value2, handles) {
return innerParseSerializedValue(value2, handles, /* @__PURE__ */ new Map(), []);
}
function innerParseSerializedValue(value2, handles, refs, accessChain) {
if (value2.ref !== void 0)
return refs.get(value2.ref);
if (value2.n !== void 0)
return value2.n;
if (value2.s !== void 0)
return value2.s;
if (value2.b !== void 0)
return value2.b;
if (value2.v !== void 0) {
if (value2.v === "undefined")
return void 0;
if (value2.v === "null")
return null;
if (value2.v === "NaN")
return NaN;
if (value2.v === "Infinity")
return Infinity;
if (value2.v === "-Infinity")
return -Infinity;
if (value2.v === "-0")
return -0;
}
if (value2.d !== void 0)
return new Date(value2.d);
if (value2.u !== void 0)
return new URL(value2.u);
if (value2.bi !== void 0)
return BigInt(value2.bi);
if (value2.e !== void 0) {
const error = new Error(value2.e.m);
error.name = value2.e.n;
error.stack = value2.e.s;
return error;
}
if (value2.r !== void 0)
return new RegExp(value2.r.p, value2.r.f);
if (value2.ta !== void 0) {
const ctor = typedArrayKindToConstructor[value2.ta.k];
return new ctor(value2.ta.b.buffer, value2.ta.b.byteOffset, value2.ta.b.length / ctor.BYTES_PER_ELEMENT);
}
if (value2.a !== void 0) {
const result2 = [];
refs.set(value2.id, result2);
for (let i = 0; i < value2.a.length; i++)
result2.push(innerParseSerializedValue(value2.a[i], handles, refs, [...accessChain, i]));
return result2;
}
if (value2.o !== void 0) {
const result2 = {};
refs.set(value2.id, result2);
for (const { k, v } of value2.o)
result2[k] = innerParseSerializedValue(v, handles, refs, [...accessChain, k]);
return result2;
}
if (value2.h !== void 0) {
if (handles === void 0)
throw new Error("Unexpected handle");
return handles[value2.h];
}
throw new Error(`Attempting to deserialize unexpected value${accessChainToDisplayString(accessChain)}: ${value2}`);
}
function serializeValue(value2, handleSerializer) {
return innerSerializeValue(value2, handleSerializer, { lastId: 0, visited: /* @__PURE__ */ new Map() }, []);
}
function innerSerializeValue(value2, handleSerializer, visitorInfo, accessChain) {
const handle = handleSerializer(value2);
if ("fallThrough" in handle)
value2 = handle.fallThrough;
else
return handle;
if (typeof value2 === "symbol")
return { v: "undefined" };
if (Object.is(value2, void 0))
return { v: "undefined" };
if (Object.is(value2, null))
return { v: "null" };
if (Object.is(value2, NaN))
return { v: "NaN" };
if (Object.is(value2, Infinity))
return { v: "Infinity" };
if (Object.is(value2, -Infinity))
return { v: "-Infinity" };
if (Object.is(value2, -0))
return { v: "-0" };
if (typeof value2 === "boolean")
return { b: value2 };
if (typeof value2 === "number")
return { n: value2 };
if (typeof value2 === "string")
return { s: value2 };
if (typeof value2 === "bigint")
return { bi: value2.toString() };
if (isError2(value2))
return { e: { n: value2.name, m: value2.message, s: value2.stack || "" } };
if (isDate(value2))
return { d: value2.toJSON() };
if (isURL(value2))
return { u: value2.toJSON() };
if (isRegExp4(value2))
return { r: { p: value2.source, f: value2.flags } };
const typedArrayKind = constructorToTypedArrayKind.get(value2.constructor);
if (typedArrayKind)
return { ta: { b: Buffer.from(value2.buffer, value2.byteOffset, value2.byteLength), k: typedArrayKind } };
const id = visitorInfo.visited.get(value2);
if (id)
return { ref: id };
if (Array.isArray(value2)) {
const a = [];
const id2 = ++visitorInfo.lastId;
visitorInfo.visited.set(value2, id2);
for (let i = 0; i < value2.length; ++i)
a.push(innerSerializeValue(value2[i], handleSerializer, visitorInfo, [...accessChain, i]));
return { a, id: id2 };
}
if (typeof value2 === "object") {
const o = [];
const id2 = ++visitorInfo.lastId;
visitorInfo.visited.set(value2, id2);
for (const name of Object.keys(value2))
o.push({ k: name, v: innerSerializeValue(value2[name], handleSerializer, visitorInfo, [...accessChain, name]) });
return { o, id: id2 };
}
throw new Error(`Attempting to serialize unexpected value${accessChainToDisplayString(accessChain)}: ${value2}`);
}
function accessChainToDisplayString(accessChain) {
const chainString = accessChain.map((accessor, i) => {
if (typeof accessor === "string")
return i ? `.${accessor}` : accessor;
return `[${accessor}]`;
}).join("");
return chainString.length > 0 ? ` at position "${chainString}"` : "";
}
function isRegExp4(obj) {
return obj instanceof RegExp || Object.prototype.toString.call(obj) === "[object RegExp]";
}
function isDate(obj) {
return obj instanceof Date || Object.prototype.toString.call(obj) === "[object Date]";
}
function isURL(obj) {
return obj instanceof URL || Object.prototype.toString.call(obj) === "[object URL]";
}
function isError2(obj) {
const proto = obj ? Object.getPrototypeOf(obj) : null;
return obj instanceof Error || proto?.name === "Error" || proto && isError2(proto);
}
var typedArrayKindToConstructor, constructorToTypedArrayKind;
var init_serializers = __esm({
"packages/playwright-core/src/protocol/serializers.ts"() {
"use strict";
typedArrayKindToConstructor = {
i8: Int8Array,
ui8: Uint8Array,
ui8c: Uint8ClampedArray,
i16: Int16Array,
ui16: Uint16Array,
i32: Int32Array,
ui32: Uint32Array,
f32: Float32Array,
f64: Float64Array,
bi64: BigInt64Array,
bui64: BigUint64Array
};
constructorToTypedArrayKind = new Map(Object.entries(typedArrayKindToConstructor).map(([k, v]) => [v, k]));
}
});
// packages/playwright-core/src/server/errors.ts
function isTargetClosedError(error) {
return error instanceof TargetClosedError || error.name === "TargetClosedError";
}
function serializeError(e) {
if (isError(e))
return { error: { message: e.message, stack: e.stack, name: e.name } };
return { value: serializeValue(e, (value2) => ({ fallThrough: value2 })) };
}
function parseError(error) {
if (!error.error) {
if (error.value === void 0)
throw new Error("Serialized error must have either an error or a value");
return parseSerializedValue(error.value, void 0);
}
const e = new Error(error.error.message);
e.stack = error.error.stack || "";
e.name = error.error.name;
return e;
}
var CustomError, TimeoutError, TargetClosedError;
var init_errors = __esm({
"packages/playwright-core/src/server/errors.ts"() {
"use strict";
init_rtti();
init_serializers();
CustomError = class extends Error {
constructor(message) {
super(message);
this.name = this.constructor.name;
}
};
TimeoutError = class extends CustomError {
};
TargetClosedError = class extends CustomError {
constructor(cause, logs) {
super((cause || "Target page, context or browser has been closed") + (logs || ""));
}
};
}
});
// packages/playwright-core/src/server/progress.ts
function isAbortError(error) {
return error instanceof TimeoutError || !!error[kAbortErrorSymbol];
}
async function raceUncancellableOperationWithCleanup(progress2, run, cleanup) {
let aborted = false;
try {
return await progress2.race(run().then(async (t) => {
if (aborted)
await cleanup(t);
return t;
}));
} catch (error) {
aborted = true;
throw error;
}
}
var ProgressController, kAbortErrorSymbol, nullProgress;
var init_progress = __esm({
"packages/playwright-core/src/server/progress.ts"() {
"use strict";
init_manualPromise();
init_assert();
init_time();
init_debugLogger();
init_errors();
ProgressController = class _ProgressController {
constructor(metadata, onCallLog) {
this._forceAbortPromise = new ManualPromise();
this._donePromise = new ManualPromise();
this._state = "before";
this.metadata = metadata || { id: "", startTime: 0, endTime: 0, type: "Internal", method: "", params: {}, log: [], internal: true };
this._onCallLog = onCallLog;
this._forceAbortPromise.catch((e) => null);
this._controller = new AbortController();
}
static createForSdkObject(sdkObject, callMetadata) {
const logName = sdkObject.logName || "api";
return new _ProgressController(callMetadata, (message) => {
if (logName === "api" && sdkObject.attribution.playwright?.options.isInternalPlaywright)
return;
debugLogger.log(logName, message);
sdkObject.instrumentation.onCallLog(sdkObject, callMetadata, logName, message);
});
}
async abort(error) {
if (this._state === "running") {
error[kAbortErrorSymbol] = true;
this._state = { error };
this._forceAbortPromise.reject(error);
this._controller.abort(error);
}
await this._donePromise;
}
async run(task, timeout) {
const deadline = timeout ? monotonicTime() + timeout : 0;
assert(this._state === "before");
this._state = "running";
let timer;
let outerProgress;
let allowConcurrent = false;
const progress2 = {
timeout: timeout ?? 0,
deadline,
disableTimeout: () => {
clearTimeout(timer);
},
log: (message) => {
if (this._state === "running")
this.metadata.log.push(message);
this._onCallLog?.(message);
},
metadata: this.metadata,
setAllowConcurrentOrNestedRaces: (allow) => {
allowConcurrent = allow;
},
race: (promise) => {
if (process.env.PW_DETECT_NESTED_PROGRESS) {
const innerProgress = new Error().stack;
if (outerProgress && !allowConcurrent && outerProgress !== innerProgress) {
console.error("Cannot call race() inside another race()");
console.error("<<<<<OUTER>>>>>:", outerProgress);
console.error("<<<<<INNER>>>>>:", innerProgress);
}
outerProgress = innerProgress;
}
const promises = Array.isArray(promise) ? promise : [promise];
if (!promises.length)
return Promise.resolve();
return Promise.race([...promises, this._forceAbortPromise]).finally(() => outerProgress = void 0);
},
wait: async (timeout2) => {
let timer2;
const promise = new Promise((f) => timer2 = setTimeout(f, timeout2));
return progress2.race(promise).finally(() => clearTimeout(timer2));
},
signal: this._controller.signal
};
if (deadline) {
const timeoutError = new TimeoutError(`Timeout ${timeout}ms exceeded.`);
timer = setTimeout(() => {
if (this.metadata.pauseStartTime && !this.metadata.pauseEndTime)
return;
if (this._state === "running") {
this._state = { error: timeoutError };
this._forceAbortPromise.reject(timeoutError);
this._controller.abort(timeoutError);
}
}, deadline - monotonicTime());
}
try {
const result2 = await task(progress2);
this._state = "finished";
return result2;
} catch (error) {
this._state = { error };
throw error;
} finally {
clearTimeout(timer);
this._donePromise.resolve();
}
}
};
kAbortErrorSymbol = Symbol("kAbortError");
nullProgress = {
timeout: 0,
deadline: 0,
disableTimeout() {
},
log() {
},
race(promise) {
const promises = Array.isArray(promise) ? promise : [promise];
return Promise.race(promises);
},
wait: async (timeout) => await new Promise((f) => setTimeout(f, timeout)),
signal: new AbortController().signal,
metadata: {
id: "",
startTime: 0,
endTime: 0,
type: "",
method: "",
params: {},
log: [],
internal: true
},
setAllowConcurrentOrNestedRaces() {
}
};
}
});
// packages/playwright-core/src/server/clock.ts
function parseTicks(value2) {
if (typeof value2 === "number")
return value2;
if (!value2)
return 0;
const str = value2;
const strings = str.split(":");
const l = strings.length;
let i = l;
let ms = 0;
let parsed;
if (l > 3 || !/^(\d\d:){0,2}\d\d?$/.test(str)) {
throw new Error(
`Clock only understands numbers, 'mm:ss' and 'hh:mm:ss'`
);
}
while (i--) {
parsed = parseInt(strings[i], 10);
if (parsed >= 60)
throw new Error(`Invalid time ${str}`);
ms += parsed * Math.pow(60, l - i - 1);
}
return ms * 1e3;
}
function parseTime(epoch) {
if (!epoch)
return 0;
if (typeof epoch === "number")
return epoch;
const parsed = new Date(epoch);
if (!isFinite(parsed.getTime()))
throw new Error(`Invalid date: ${epoch}`);
return parsed.getTime();
}
var Clock;
var init_clock = __esm({
"packages/playwright-core/src/server/clock.ts"() {
"use strict";
init_clockSource();
init_progress();
Clock = class {
constructor(browserContext) {
this._initScripts = [];
this._browserContext = browserContext;
}
async uninstall(progress2) {
await progress2.race(Promise.all(this._initScripts.map((script) => script.dispose())));
this._initScripts = [];
}
async fastForward(ticks) {
await this._installIfNeeded();
const ticksMillis = parseTicks(ticks);
this._initScripts.push(await this._browserContext.addInitScript(nullProgress, `globalThis.__pwClock.controller.log('fastForward', ${Date.now()}, ${ticksMillis})`));
await this._evaluateInFrames(`globalThis.__pwClock.controller.fastForward(${ticksMillis})`);
}
async install(time) {
await this._installIfNeeded();
const timeMillis = time !== void 0 ? parseTime(time) : Date.now();
this._initScripts.push(await this._browserContext.addInitScript(nullProgress, `globalThis.__pwClock.controller.log('install', ${Date.now()}, ${timeMillis})`));
await this._evaluateInFrames(`globalThis.__pwClock.controller.install(${timeMillis})`);
}
async pauseAt(ticks) {
await this._installIfNeeded();
const timeMillis = parseTime(ticks);
this._initScripts.push(await this._browserContext.addInitScript(nullProgress, `globalThis.__pwClock.controller.log('pauseAt', ${Date.now()}, ${timeMillis})`));
await this._evaluateInFrames(`globalThis.__pwClock.controller.pauseAt(${timeMillis})`);
}
resumeNoReply() {
if (!this._initScripts.length)
return;
const doResume = async () => {
this._initScripts.push(await this._browserContext.addInitScript(nullProgress, `globalThis.__pwClock.controller.log('resume', ${Date.now()})`));
await this._evaluateInFrames(`globalThis.__pwClock.controller.resume()`);
};
doResume().catch(() => {
});
}
async resume(progress2) {
await progress2.race(this._installIfNeeded());
this._initScripts.push(await this._browserContext.addInitScript(nullProgress, `globalThis.__pwClock.controller.log('resume', ${Date.now()})`));
await progress2.race(this._evaluateInFrames(`globalThis.__pwClock.controller.resume()`));
}
async setFixedTime(time) {
await this._installIfNeeded();
const timeMillis = parseTime(time);
this._initScripts.push(await this._browserContext.addInitScript(nullProgress, `globalThis.__pwClock.controller.log('setFixedTime', ${Date.now()}, ${timeMillis})`));
await this._evaluateInFrames(`globalThis.__pwClock.controller.setFixedTime(${timeMillis})`);
}
async setSystemTime(time) {
await this._installIfNeeded();
const timeMillis = parseTime(time);
this._initScripts.push(await this._browserContext.addInitScript(nullProgress, `globalThis.__pwClock.controller.log('setSystemTime', ${Date.now()}, ${timeMillis})`));
await this._evaluateInFrames(`globalThis.__pwClock.controller.setSystemTime(${timeMillis})`);
}
async runFor(ticks) {
await this._installIfNeeded();
const ticksMillis = parseTicks(ticks);
this._initScripts.push(await this._browserContext.addInitScript(nullProgress, `globalThis.__pwClock.controller.log('runFor', ${Date.now()}, ${ticksMillis})`));
await this._evaluateInFrames(`globalThis.__pwClock.controller.runFor(${ticksMillis})`);
}
async _installIfNeeded() {
if (this._initScripts.length)
return;
const script = `(() => {
const module = {};
${source}
if (!globalThis.__pwClock)
globalThis.__pwClock = (module.exports.inject())(globalThis, ${JSON.stringify(this._browserContext._browser.options.name)});
})();`;
const initScript = await this._browserContext.addInitScript(nullProgress, script);
await this._evaluateInFrames(script);
this._initScripts.push(initScript);
}
async _evaluateInFrames(script) {
await this._browserContext.safeNonStallingEvaluateInAllFrames(script, "main", { throwOnJSErrors: true });
}
};
}
});
// packages/playwright-core/src/server/instrumentation.ts
function createRootSdkObject() {
const fakeParent = { attribution: {}, instrumentation: createInstrumentation() };
const root = new SdkObject(fakeParent);
root.guid = "";
return root;
}
function createInstrumentation() {
const listeners = /* @__PURE__ */ new Map();
return new Proxy({}, {
get: (obj, prop) => {
if (typeof prop !== "string")
return obj[prop];
if (prop === "addListener")
return (listener, context2) => listeners.set(listener, context2);
if (prop === "removeListener")
return (listener) => listeners.delete(listener);
if (!prop.startsWith("on"))
return obj[prop];
return async (sdkObject, ...params2) => {
for (const [listener, context2] of listeners) {
if (!context2 || sdkObject.attribution.context === context2)
await listener[prop]?.(sdkObject, ...params2);
}
};
}
});
}
var import_events3, SdkObject;
var init_instrumentation = __esm({
"packages/playwright-core/src/server/instrumentation.ts"() {
"use strict";
import_events3 = require("events");
init_crypto();
init_debugLogger();
SdkObject = class extends import_events3.EventEmitter {
constructor(parent, guidPrefix, guid) {
super();
this.guid = guid || `${guidPrefix || ""}@${createGuid()}`;
this.setMaxListeners(0);
this.attribution = { ...parent.attribution };
this.instrumentation = parent.instrumentation;
}
apiLog(message) {
if (!this.attribution.playwright.options.isInternalPlaywright)
debugLogger.log("api", message);
}
closeReason() {
return this.attribution.worker?._closeReason || this.attribution.page?._closeReason || this.attribution.context?._closeReason || this.attribution.browser?._closeReason;
}
};
}
});
// packages/playwright-core/src/server/debugger.ts
function matchesLocation(metadata, location2) {
return !!metadata.location?.file.includes(location2.file) && (location2.line === void 0 || metadata.location.line === location2.line) && (location2.column === void 0 || metadata.location.column === location2.column);
}
var symbol, Debugger;
var init_debugger = __esm({
"packages/playwright-core/src/server/debugger.ts"() {
"use strict";
init_protocolMetainfo();
init_time();
init_instrumentation();
init_browserContext();
symbol = Symbol("Debugger");
Debugger = class _Debugger extends SdkObject {
constructor(context2) {
super(context2, "debugger");
this._pauseAt = {};
this._enabled = false;
this._pauseBeforeWaitingActions = false;
this._muted = false;
this._context = context2;
this._context[symbol] = this;
context2.instrumentation.addListener(this, context2);
this._context.once(BrowserContext.Events.Close, () => {
this._context.instrumentation.removeListener(this);
});
}
static {
this.Events = {
PausedStateChanged: "pausedstatechanged"
};
}
requestPause(progress2) {
if (this.isPaused())
throw new Error("Debugger is already paused");
this.setPauseBeforeWaitingActions();
this.setPauseAt({ next: true });
}
doResume(progress2) {
if (!this.isPaused())
throw new Error("Debugger is not paused");
this.resume();
}
next(progress2) {
if (!this.isPaused())
throw new Error("Debugger is not paused");
this.setPauseBeforeWaitingActions();
this.setPauseAt({ next: true });
this.resume();
}
runTo(progress2, location2) {
if (!this.isPaused())
throw new Error("Debugger is not paused");
this.setPauseBeforeWaitingActions();
this.setPauseAt({ location: location2 });
this.resume();
}
async setMuted(muted) {
this._muted = muted;
}
async onBeforeCall(sdkObject, metadata) {
if (this._muted || metadata.internal)
return;
const metainfo = getMetainfo(metadata);
const pauseOnPauseCall = this._enabled && metadata.type === "BrowserContext" && metadata.method === "pause";
const pauseBeforeAction = !!this._pauseAt.next && !!metainfo?.pause && (this._pauseBeforeWaitingActions || !metainfo?.isAutoWaiting);
const pauseOnLocation = !!this._pauseAt.location && matchesLocation(metadata, this._pauseAt.location);
if (pauseOnPauseCall || pauseBeforeAction || pauseOnLocation)
await this._pause(sdkObject, metadata);
}
async onBeforeInputAction(sdkObject, metadata) {
if (this._muted || metadata.internal)
return;
const metainfo = getMetainfo(metadata);
const pauseBeforeInput = !!this._pauseAt.next && !!metainfo?.pause && !!metainfo?.isAutoWaiting && !this._pauseBeforeWaitingActions;
if (pauseBeforeInput)
await this._pause(sdkObject, metadata);
}
async _pause(sdkObject, metadata) {
if (this._muted || metadata.internal)
return;
if (this._pausedCall)
return;
this._pauseAt = {};
metadata.pauseStartTime = monotonicTime();
const result2 = new Promise((resolve) => {
this._pausedCall = { metadata, sdkObject, resolve };
});
this.emit(_Debugger.Events.PausedStateChanged);
return result2;
}
resume() {
if (!this._pausedCall)
return;
this._pausedCall.metadata.pauseEndTime = monotonicTime();
this._pausedCall.resolve();
this._pausedCall = void 0;
this.emit(_Debugger.Events.PausedStateChanged);
}
setPauseBeforeWaitingActions() {
this._pauseBeforeWaitingActions = true;
}
setPauseAt(at = {}) {
this._enabled = true;
this._pauseAt = at;
}
isPaused(metadata) {
if (metadata)
return this._pausedCall?.metadata === metadata;
return !!this._pausedCall;
}
pausedDetails() {
return this._pausedCall;
}
};
}
});
// packages/playwright-core/src/server/dialog.ts
var Dialog, DialogManager;
var init_dialog = __esm({
"packages/playwright-core/src/server/dialog.ts"() {
"use strict";
init_assert();
init_instrumentation();
Dialog = class extends SdkObject {
constructor(page, type3, message, onHandle, defaultValue) {
super(page, "dialog");
this._handled = false;
this._page = page;
this._type = type3;
this._message = message;
this._onHandle = onHandle;
this._defaultValue = defaultValue || "";
}
async accept(progress2, promptText) {
await progress2.race(this._accept(promptText));
}
async dismiss(progress2) {
await progress2.race(this._dismiss());
}
page() {
return this._page;
}
type() {
return this._type;
}
message() {
return this._message;
}
defaultValue() {
return this._defaultValue;
}
async _accept(promptText) {
assert(!this._handled, "Cannot accept dialog which is already handled!");
this._handled = true;
this._page.browserContext.dialogManager._dialogWillClose(this);
await this._onHandle(true, promptText);
}
async _dismiss() {
assert(!this._handled, "Cannot dismiss dialog which is already handled!");
this._handled = true;
this._page.browserContext.dialogManager._dialogWillClose(this);
await this._onHandle(false);
}
async _close() {
if (this._type === "beforeunload")
await this._accept();
else
await this._dismiss();
}
};
DialogManager = class {
constructor(instrumentation) {
this._dialogHandlers = /* @__PURE__ */ new Set();
this._openedDialogs = /* @__PURE__ */ new Set();
this._instrumentation = instrumentation;
}
dialogDidOpen(dialog) {
for (const frame of dialog.page().frameManager.frames())
frame.invalidateNonStallingEvaluations("JavaScript dialog interrupted evaluation");
this._openedDialogs.add(dialog);
this._instrumentation.onDialog(dialog);
let hasHandlers = false;
for (const handler of this._dialogHandlers) {
if (handler(dialog))
hasHandlers = true;
}
if (!hasHandlers)
dialog._close().then(() => {
});
}
_dialogWillClose(dialog) {
this._openedDialogs.delete(dialog);
}
addDialogHandler(handler) {
this._dialogHandlers.add(handler);
}
removeDialogHandler(handler) {
this._dialogHandlers.delete(handler);
if (!this._dialogHandlers.size) {
for (const dialog of this._openedDialogs)
dialog._close().catch(() => {
});
}
}
hasOpenDialogsForPage(page) {
return [...this._openedDialogs].some((dialog) => dialog.page() === page);
}
async closeBeforeUnloadDialogs() {
await Promise.all([...this._openedDialogs].map(async (dialog) => {
if (dialog.type() === "beforeunload")
await dialog._dismiss();
}));
}
};
}
});
// packages/playwright-core/src/server/network.ts
function filterCookies(cookies, urls) {
const parsedURLs = urls.map((s) => new URL(s));
return cookies.filter((c) => {
if (!parsedURLs.length)
return true;
for (const parsedURL of parsedURLs) {
let domain = c.domain;
if (!domain.startsWith("."))
domain = "." + domain;
if (!("." + parsedURL.hostname).endsWith(domain))
continue;
if (!parsedURL.pathname.startsWith(c.path))
continue;
if (parsedURL.protocol !== "https:" && !isLocalHostname(parsedURL.hostname) && c.secure)
continue;
return true;
}
return false;
});
}
function isLocalHostname(hostname) {
return hostname === "localhost" || hostname.endsWith(".localhost");
}
function isForbiddenHeader(name, value2) {
const lowerName = name.toLowerCase();
if (FORBIDDEN_HEADER_NAMES.has(lowerName))
return true;
if (lowerName.startsWith("proxy-"))
return true;
if (lowerName.startsWith("sec-"))
return true;
if (lowerName === "x-http-method" || lowerName === "x-http-method-override" || lowerName === "x-method-override") {
if (value2 && FORBIDDEN_METHODS.has(value2.toUpperCase()))
return true;
}
return false;
}
function applyHeadersOverrides(original, overrides) {
const forbiddenHeaders = original.filter((header) => isForbiddenHeader(header.name, header.value));
const allowedHeaders = overrides.filter((header) => !isForbiddenHeader(header.name, header.value));
return mergeHeaders([allowedHeaders, forbiddenHeaders]);
}
function rewriteCookies(cookies) {
return cookies.map((c) => {
assert(c.url || c.domain && c.path, "Cookie should have a url or a domain/path pair");
assert(!(c.url && c.domain), "Cookie should have either url or domain");
assert(!(c.url && c.path), "Cookie should have either url or path");
assert(!(c.expires && c.expires < 0 && c.expires !== -1), "Cookie should have a valid expires, only -1 or a positive number for the unix timestamp in seconds is allowed");
assert(!(c.expires && c.expires > 0 && c.expires > kMaxCookieExpiresDateInSeconds), "Cookie should have a valid expires, only -1 or a positive number for the unix timestamp in seconds is allowed");
const copy = { ...c };
if (copy.url) {
assert(copy.url !== "about:blank", `Blank page can not have cookie "${c.name}"`);
assert(!copy.url.startsWith("data:"), `Data URL page can not have cookie "${c.name}"`);
const url2 = new URL(copy.url);
copy.domain = url2.hostname;
copy.path = url2.pathname.substring(0, url2.pathname.lastIndexOf("/") + 1);
copy.secure = url2.protocol === "https:";
}
return copy;
});
}
function parseURL2(url2) {
try {
return new URL(url2);
} catch (e) {
return null;
}
}
function stripFragmentFromUrl(url2) {
if (!url2.includes("#"))
return url2;
return url2.substring(0, url2.indexOf("#"));
}
function statusText(status) {
return STATUS_TEXTS[String(status)] || "Unknown";
}
function singleHeader(name, value2) {
return [{ name, value: value2 }];
}
function mergeHeaders(headers) {
const lowerCaseToValue = /* @__PURE__ */ new Map();
const lowerCaseToOriginalCase = /* @__PURE__ */ new Map();
for (const h of headers) {
if (!h)
continue;
for (const { name, value: value2 } of h) {
const lower = name.toLowerCase();
lowerCaseToOriginalCase.set(lower, name);
lowerCaseToValue.set(lower, value2);
}
}
const result2 = [];
for (const [lower, value2] of lowerCaseToValue)
result2.push({ name: lowerCaseToOriginalCase.get(lower), value: value2 });
return result2;
}
var FORBIDDEN_HEADER_NAMES, FORBIDDEN_METHODS, kMaxCookieExpiresDateInSeconds, Request, Route, Response2, WebSocket, STATUS_TEXTS;
var init_network2 = __esm({
"packages/playwright-core/src/server/network.ts"() {
"use strict";
init_manualPromise();
init_assert();
init_browserContext();
init_fetch();
init_instrumentation();
FORBIDDEN_HEADER_NAMES = /* @__PURE__ */ new Set([
"accept-charset",
"accept-encoding",
"access-control-request-headers",
"access-control-request-method",
"connection",
"content-length",
"cookie",
"date",
"dnt",
"expect",
"host",
"keep-alive",
"origin",
"referer",
"set-cookie",
"te",
"trailer",
"transfer-encoding",
"upgrade",
"via"
]);
FORBIDDEN_METHODS = /* @__PURE__ */ new Set(["CONNECT", "TRACE", "TRACK"]);
kMaxCookieExpiresDateInSeconds = 253402300799;
Request = class _Request extends SdkObject {
constructor(context2, frame, serviceWorker, redirectedFrom, documentId, url2, resourceType, method, postData, headers) {
super(frame || context2, "request");
this._response = null;
this._redirectedTo = null;
this._failureText = null;
this._frame = null;
this._serviceWorker = null;
this._rawRequestHeadersPromise = new ManualPromise();
this._waitForResponsePromise = new ManualPromise();
this._responseEndTiming = -1;
assert(!url2.startsWith("data:"), "Data urls should not fire requests");
this._context = context2;
this._frame = frame;
this._serviceWorker = serviceWorker;
this._redirectedFrom = redirectedFrom;
if (redirectedFrom)
redirectedFrom._redirectedTo = this;
this._documentId = documentId;
this._url = stripFragmentFromUrl(url2);
this._resourceType = resourceType;
this._method = method;
this._postData = postData;
this._headers = headers;
this._isFavicon = url2.endsWith("/favicon.ico") || !!redirectedFrom?._isFavicon;
}
static {
this.Events = {
Response: "response"
};
}
async rawRequestHeaders(progress2) {
return await progress2.race(this._rawRequestHeaders());
}
async response(progress2) {
return await progress2.race(this._waitForResponse());
}
_setFailureText(failureText) {
this._failureText = failureText;
this._waitForResponsePromise.resolve(null);
}
_applyOverrides(overrides) {
this._overrides = { ...this._overrides, ...overrides };
return this._overrides;
}
overrides() {
return this._overrides;
}
url() {
return this._overrides?.url || this._url;
}
resourceType() {
return this._resourceType;
}
method() {
return this._overrides?.method || this._method;
}
postDataBuffer() {
return this._overrides?.postData || this._postData;
}
headers() {
return this._overrides?.headers || this._headers;
}
headerValue(name) {
const lowerCaseName = name.toLowerCase();
return this.headers().find((h) => h.name.toLowerCase() === lowerCaseName)?.value;
}
// "null" means no raw headers available - we'll use provisional headers as raw headers.
setRawRequestHeaders(headers) {
if (!this._rawRequestHeadersPromise.isDone())
this._rawRequestHeadersPromise.resolve(headers || this._headers);
}
async _rawRequestHeaders() {
return this._overrides?.headers || this._rawRequestHeadersPromise;
}
_waitForResponse() {
return this._waitForResponsePromise;
}
_existingResponse() {
return this._response;
}
_setResponse(response2) {
this._response = response2;
this._waitForResponsePromise.resolve(response2);
this.emit(_Request.Events.Response, response2);
}
_finalRequest() {
return this._redirectedTo ? this._redirectedTo._finalRequest() : this;
}
frame() {
return this._frame;
}
serviceWorker() {
return this._serviceWorker;
}
isNavigationRequest() {
return !!this._documentId;
}
redirectedFrom() {
return this._redirectedFrom;
}
failure() {
if (this._failureText === null)
return null;
return {
errorText: this._failureText
};
}
// TODO(bidi): remove once post body is available.
_setBodySize(size) {
this._bodySize = size;
}
bodySize() {
return this._bodySize || this.postDataBuffer()?.length || 0;
}
async _requestHeadersSize() {
let headersSize = 4;
headersSize += this.method().length;
headersSize += new URL(this.url()).pathname.length;
headersSize += 8;
const headers = await this._rawRequestHeaders();
for (const header of headers)
headersSize += header.name.length + header.value.length + 4;
return headersSize;
}
};
Route = class extends SdkObject {
constructor(request2, delegate) {
super(request2._frame || request2._context, "route");
this._handled = false;
this._futureHandlers = [];
this._request = request2;
this._delegate = delegate;
this._request._context.addRouteInFlight(this);
}
handle(handlers) {
this._futureHandlers = [...handlers];
this.continue({ isFallback: true }).catch(() => {
});
}
async removeHandler(handler) {
this._futureHandlers = this._futureHandlers.filter((h) => h !== handler);
if (handler === this._currentHandler) {
await this.continue({ isFallback: true }).catch(() => {
});
return;
}
}
request() {
return this._request;
}
async abort(errorCode = "failed") {
this._startHandling();
this._request._context.emit(BrowserContext.Events.RequestAborted, this._request);
await this._delegate.abort(errorCode);
this._endHandling();
}
redirectNavigationRequest(url2) {
this._startHandling();
assert(this._request.isNavigationRequest());
this._request.frame().redirectNavigation(url2, this._request._documentId, this._request.headerValue("referer"));
this._endHandling();
}
async fulfill(overrides) {
this._startHandling();
let body = overrides.body;
let isBase64 = overrides.isBase64 || false;
if (body === void 0) {
if (overrides.fetchResponseUid) {
const buffer = this._request._context.fetchRequest.fetchResponses.get(overrides.fetchResponseUid) || APIRequestContext.findResponseBody(overrides.fetchResponseUid);
assert(buffer, "Fetch response has been disposed");
body = buffer.toString("base64");
isBase64 = true;
} else {
body = "";
isBase64 = false;
}
} else if (!overrides.status || overrides.status < 200 || overrides.status >= 400) {
this._request._responseBodyOverride = { body, isBase64 };
}
const headers = [...overrides.headers || []];
this._maybeAddCorsHeaders(headers);
this._request._context.emit(BrowserContext.Events.RequestFulfilled, this._request);
await this._delegate.fulfill({
status: overrides.status || 200,
headers,
body,
isBase64
});
this._endHandling();
}
// See https://github.com/microsoft/playwright/issues/12929
_maybeAddCorsHeaders(headers) {
const origin = this._request.headerValue("origin");
if (!origin)
return;
const requestUrl = new URL(this._request.url());
if (!requestUrl.protocol.startsWith("http"))
return;
if (requestUrl.origin === origin.trim())
return;
const corsHeader = headers.find(({ name }) => name === "access-control-allow-origin");
if (corsHeader)
return;
headers.push({ name: "access-control-allow-origin", value: origin });
headers.push({ name: "access-control-allow-credentials", value: "true" });
headers.push({ name: "vary", value: "Origin" });
}
async continue(overrides) {
if (overrides.url) {
const newUrl = new URL(overrides.url);
const oldUrl = new URL(this._request.url());
if (oldUrl.protocol !== newUrl.protocol)
throw new Error("New URL must have same protocol as overridden URL");
}
if (overrides.headers) {
overrides.headers = applyHeadersOverrides(this._request._headers, overrides.headers);
}
overrides = this._request._applyOverrides(overrides);
const nextHandler = this._futureHandlers.shift();
if (nextHandler) {
this._currentHandler = nextHandler;
nextHandler(this, this._request);
return;
}
if (!overrides.isFallback)
this._request._context.emit(BrowserContext.Events.RequestContinued, this._request);
this._startHandling();
await this._delegate.continue(overrides);
this._endHandling();
}
_startHandling() {
assert(!this._handled, "Route is already handled!");
this._handled = true;
this._currentHandler = void 0;
}
_endHandling() {
this._futureHandlers = [];
this._currentHandler = void 0;
this._request._context.removeRouteInFlight(this);
}
};
Response2 = class extends SdkObject {
constructor(request2, status, statusText2, headers, timing, getResponseBodyCallback, fromServiceWorker) {
super(request2.frame() || request2._context, "response");
this._contentPromise = null;
this._finishedPromise = new ManualPromise();
this._headersMap = /* @__PURE__ */ new Map();
this._serverAddrPromise = new ManualPromise();
this._securityDetailsPromise = new ManualPromise();
this._rawResponseHeadersPromise = new ManualPromise();
this._httpVersionPromise = new ManualPromise();
this._encodedBodySizePromise = new ManualPromise();
this._transferSizePromise = new ManualPromise();
this._responseHeadersSizePromise = new ManualPromise();
this._request = request2;
this._timing = timing;
this._status = status;
this._statusText = statusText2;
this._url = request2.url();
this._headers = headers;
for (const { name, value: value2 } of this._headers)
this._headersMap.set(name.toLowerCase(), value2);
this._getResponseBodyCallback = getResponseBodyCallback;
this._request._setResponse(this);
this._fromServiceWorker = fromServiceWorker;
}
async body(progress2) {
return await progress2.race(this.internalBody());
}
async securityDetails(progress2) {
return await progress2.race(this.internalSecurityDetails());
}
async serverAddr(progress2) {
return await progress2.race(this._serverAddrPromise) || null;
}
async rawResponseHeaders(progress2) {
return await progress2.race(this._rawResponseHeadersPromise);
}
async httpVersion(progress2) {
return await progress2.race(this._httpVersion());
}
async sizes(progress2) {
return await progress2.race(this._sizes());
}
_serverAddrFinished(addr) {
this._serverAddrPromise.resolve(addr);
}
_securityDetailsFinished(securityDetails) {
this._securityDetailsPromise.resolve(securityDetails);
}
_requestFinished(responseEndTiming) {
this._request._responseEndTiming = Math.max(responseEndTiming, this._timing.responseStart);
if (this._timing.requestStart === -1)
this._timing.requestStart = this._request._responseEndTiming;
this._finishedPromise.resolve();
}
_setHttpVersion(httpVersion) {
this._httpVersionPromise.resolve(httpVersion);
}
url() {
return this._url;
}
status() {
return this._status;
}
statusText() {
return this._statusText;
}
headers() {
return this._headers;
}
headerValue(name) {
return this._headersMap.get(name);
}
// "null" means no raw headers available - we'll use provisional headers as raw headers.
setRawResponseHeaders(headers) {
if (!this._rawResponseHeadersPromise.isDone())
this._rawResponseHeadersPromise.resolve(headers || this._headers);
}
setTransferSize(size) {
this._transferSizePromise.resolve(size);
}
setEncodedBodySize(size) {
this._encodedBodySizePromise.resolve(size);
}
setResponseHeadersSize(size) {
this._responseHeadersSizePromise.resolve(size);
}
timing() {
return this._timing;
}
async internalSecurityDetails() {
return await this._securityDetailsPromise || null;
}
internalBody() {
if (!this._contentPromise) {
this._contentPromise = this._finishedPromise.then(async () => {
if (this._status >= 300 && this._status <= 399)
throw new Error("Response body is unavailable for redirect responses");
if (this._request._responseBodyOverride) {
const { body, isBase64 } = this._request._responseBodyOverride;
return Buffer.from(body, isBase64 ? "base64" : "utf-8");
}
return this._getResponseBodyCallback();
});
}
return this._contentPromise;
}
request() {
return this._request;
}
finished() {
return this._finishedPromise;
}
frame() {
return this._request.frame();
}
async _httpVersion() {
const httpVersion = await this._httpVersionPromise || null;
if (!httpVersion)
return "HTTP/1.1";
if (httpVersion === "http/1.1")
return "HTTP/1.1";
if (httpVersion === "h2")
return "HTTP/2.0";
return httpVersion;
}
fromServiceWorker() {
return this._fromServiceWorker;
}
async responseHeadersSize() {
const availableSize = await this._responseHeadersSizePromise;
if (availableSize !== null)
return availableSize;
let headersSize = 4;
headersSize += 8;
headersSize += 3;
headersSize += this.statusText().length;
const headers = await this._rawResponseHeadersPromise;
for (const header of headers)
headersSize += header.name.length + header.value.length + 4;
headersSize += 2;
return headersSize;
}
async _sizes() {
const requestHeadersSize = await this._request._requestHeadersSize();
const responseHeadersSize = await this.responseHeadersSize();
let encodedBodySize = await this._encodedBodySizePromise;
if (encodedBodySize === null) {
const headers = await this._rawResponseHeadersPromise;
const contentLength = headers.find((h) => h.name.toLowerCase() === "content-length")?.value;
encodedBodySize = contentLength ? +contentLength : 0;
}
let transferSize = await this._transferSizePromise;
if (transferSize === null) {
transferSize = responseHeadersSize + encodedBodySize;
}
return {
requestBodySize: this._request.bodySize(),
requestHeadersSize,
responseBodySize: encodedBodySize,
responseHeadersSize,
transferSize
};
}
};
WebSocket = class _WebSocket extends SdkObject {
constructor(parent, url2) {
super(parent, "ws");
this._notified = false;
this._url = url2;
}
static {
this.Events = {
Close: "close",
SocketError: "socketerror",
FrameReceived: "framereceived",
FrameSent: "framesent"
};
}
markAsNotified() {
if (this._notified)
return false;
this._notified = true;
return true;
}
url() {
return this._url;
}
frameSent(opcode, data) {
this.emit(_WebSocket.Events.FrameSent, { opcode, data });
}
frameReceived(opcode, data) {
this.emit(_WebSocket.Events.FrameReceived, { opcode, data });
}
error(errorMessage) {
this.emit(_WebSocket.Events.SocketError, errorMessage);
}
closed() {
this.emit(_WebSocket.Events.Close);
}
};
STATUS_TEXTS = {
"100": "Continue",
"101": "Switching Protocols",
"102": "Processing",
"103": "Early Hints",
"200": "OK",
"201": "Created",
"202": "Accepted",
"203": "Non-Authoritative Information",
"204": "No Content",
"205": "Reset Content",
"206": "Partial Content",
"207": "Multi-Status",
"208": "Already Reported",
"226": "IM Used",
"300": "Multiple Choices",
"301": "Moved Permanently",
"302": "Found",
"303": "See Other",
"304": "Not Modified",
"305": "Use Proxy",
"306": "Switch Proxy",
"307": "Temporary Redirect",
"308": "Permanent Redirect",
"400": "Bad Request",
"401": "Unauthorized",
"402": "Payment Required",
"403": "Forbidden",
"404": "Not Found",
"405": "Method Not Allowed",
"406": "Not Acceptable",
"407": "Proxy Authentication Required",
"408": "Request Timeout",
"409": "Conflict",
"410": "Gone",
"411": "Length Required",
"412": "Precondition Failed",
"413": "Payload Too Large",
"414": "URI Too Long",
"415": "Unsupported Media Type",
"416": "Range Not Satisfiable",
"417": "Expectation Failed",
"418": "I'm a teapot",
"421": "Misdirected Request",
"422": "Unprocessable Entity",
"423": "Locked",
"424": "Failed Dependency",
"425": "Too Early",
"426": "Upgrade Required",
"428": "Precondition Required",
"429": "Too Many Requests",
"431": "Request Header Fields Too Large",
"451": "Unavailable For Legal Reasons",
"500": "Internal Server Error",
"501": "Not Implemented",
"502": "Bad Gateway",
"503": "Service Unavailable",
"504": "Gateway Timeout",
"505": "HTTP Version Not Supported",
"506": "Variant Also Negotiates",
"507": "Insufficient Storage",
"508": "Loop Detected",
"510": "Not Extended",
"511": "Network Authentication Required"
};
}
});
// packages/playwright-core/src/server/cookieStore.ts
function parseRawCookie(header) {
const pairs = header.split(";").filter((s) => s.trim().length > 0).map((p) => {
let key = "";
let value3 = "";
const separatorPos = p.indexOf("=");
if (separatorPos === -1) {
key = p.trim();
} else {
key = p.slice(0, separatorPos).trim();
value3 = p.slice(separatorPos + 1).trim();
}
return [key, value3];
});
if (!pairs.length)
return null;
const [name, value2] = pairs[0];
const cookie = {
name,
value: value2
};
for (let i = 1; i < pairs.length; i++) {
const [name2, value3] = pairs[i];
switch (name2.toLowerCase()) {
case "expires":
const expiresMs = +new Date(value3);
if (isFinite(expiresMs)) {
if (expiresMs <= 0)
cookie.expires = 0;
else
cookie.expires = Math.min(expiresMs / 1e3, kMaxCookieExpiresDateInSeconds);
}
break;
case "max-age":
const maxAgeSec = parseInt(value3, 10);
if (isFinite(maxAgeSec)) {
if (maxAgeSec <= 0)
cookie.expires = 0;
else
cookie.expires = Math.min(Date.now() / 1e3 + maxAgeSec, kMaxCookieExpiresDateInSeconds);
}
break;
case "domain":
cookie.domain = value3.toLocaleLowerCase() || "";
if (cookie.domain && !cookie.domain.startsWith(".") && cookie.domain.includes("."))
cookie.domain = "." + cookie.domain;
break;
case "path":
cookie.path = value3 || "";
break;
case "secure":
cookie.secure = true;
break;
case "httponly":
cookie.httpOnly = true;
break;
case "samesite":
switch (value3.toLowerCase()) {
case "none":
cookie.sameSite = "None";
break;
case "lax":
cookie.sameSite = "Lax";
break;
case "strict":
cookie.sameSite = "Strict";
break;
}
break;
}
}
return cookie;
}
function domainMatches(value2, domain) {
if (value2 === domain)
return true;
if (!domain.startsWith("."))
return false;
value2 = "." + value2;
return value2.endsWith(domain);
}
function pathMatches(value2, path59) {
if (value2 === path59)
return true;
if (!value2.endsWith("/"))
value2 = value2 + "/";
if (!path59.endsWith("/"))
path59 = path59 + "/";
return value2.startsWith(path59);
}
var Cookie, CookieStore;
var init_cookieStore = __esm({
"packages/playwright-core/src/server/cookieStore.ts"() {
"use strict";
init_network2();
Cookie = class {
constructor(data) {
this._raw = data;
}
_name() {
return this._raw.name;
}
// https://datatracker.ietf.org/doc/html/rfc6265#section-5.4
matches(url2) {
if (this._raw.secure && (url2.protocol !== "https:" && !isLocalHostname(url2.hostname)))
return false;
if (!domainMatches(url2.hostname, this._raw.domain))
return false;
if (!pathMatches(url2.pathname, this._raw.path))
return false;
return true;
}
_equals(other) {
return this._raw.name === other._raw.name && this._raw.domain === other._raw.domain && this._raw.path === other._raw.path;
}
_networkCookie() {
return this._raw;
}
_updateExpiresFrom(other) {
this._raw.expires = other._raw.expires;
}
_expired() {
if (this._raw.expires === -1)
return false;
return this._raw.expires * 1e3 < Date.now();
}
};
CookieStore = class _CookieStore {
constructor() {
this._nameToCookies = /* @__PURE__ */ new Map();
}
addCookies(cookies) {
for (const cookie of cookies)
this._addCookie(new Cookie(cookie));
}
cookies(url2) {
const result2 = [];
for (const cookie of this._cookiesIterator()) {
if (cookie.matches(url2))
result2.push(cookie._networkCookie());
}
return result2;
}
allCookies() {
const result2 = [];
for (const cookie of this._cookiesIterator())
result2.push(cookie._networkCookie());
return result2;
}
_addCookie(cookie) {
let set = this._nameToCookies.get(cookie._name());
if (!set) {
set = /* @__PURE__ */ new Set();
this._nameToCookies.set(cookie._name(), set);
}
for (const other of set) {
if (other._equals(cookie))
set.delete(other);
}
set.add(cookie);
_CookieStore.pruneExpired(set);
}
*_cookiesIterator() {
for (const [name, cookies] of this._nameToCookies) {
_CookieStore.pruneExpired(cookies);
for (const cookie of cookies)
yield cookie;
if (cookies.size === 0)
this._nameToCookies.delete(name);
}
}
static pruneExpired(cookies) {
for (const cookie of cookies) {
if (cookie._expired())
cookies.delete(cookie);
}
}
};
}
});
// packages/playwright-core/src/server/formData.ts
function generateUniqueBoundaryString() {
const charCodes = [];
for (let i = 0; i < 16; i++)
charCodes.push(alphaNumericEncodingMap[Math.floor(Math.random() * alphaNumericEncodingMap.length)]);
return "----WebKitFormBoundary" + String.fromCharCode(...charCodes);
}
var mime2, MultipartFormData, alphaNumericEncodingMap;
var init_formData = __esm({
"packages/playwright-core/src/server/formData.ts"() {
"use strict";
mime2 = require("./utilsBundle").mime;
MultipartFormData = class {
constructor() {
this._chunks = [];
this._boundary = generateUniqueBoundaryString();
}
contentTypeHeader() {
return `multipart/form-data; boundary=${this._boundary}`;
}
addField(name, value2) {
this._beginMultiPartHeader(name);
this._finishMultiPartHeader();
this._chunks.push(Buffer.from(value2));
this._finishMultiPartField();
}
addFileField(name, value2) {
this._beginMultiPartHeader(name);
this._chunks.push(Buffer.from(`; filename="${value2.name}"`));
this._chunks.push(Buffer.from(`\r
content-type: ${value2.mimeType || mime2.getType(value2.name) || "application/octet-stream"}`));
this._finishMultiPartHeader();
this._chunks.push(value2.buffer);
this._finishMultiPartField();
}
finish() {
this._addBoundary(true);
return Buffer.concat(this._chunks);
}
_beginMultiPartHeader(name) {
this._addBoundary();
this._chunks.push(Buffer.from(`content-disposition: form-data; name="${name}"`));
}
_finishMultiPartHeader() {
this._chunks.push(Buffer.from(`\r
\r
`));
}
_finishMultiPartField() {
this._chunks.push(Buffer.from(`\r
`));
}
_addBoundary(isLastBoundary) {
this._chunks.push(Buffer.from("--" + this._boundary));
if (isLastBoundary)
this._chunks.push(Buffer.from("--"));
this._chunks.push(Buffer.from("\r\n"));
}
};
alphaNumericEncodingMap = [
65,
66,
67,
68,
69,
70,
71,
72,
73,
74,
75,
76,
77,
78,
79,
80,
81,
82,
83,
84,
85,
86,
87,
88,
89,
90,
97,
98,
99,
100,
101,
102,
103,
104,
105,
106,
107,
108,
109,
110,
111,
112,
113,
114,
115,
116,
117,
118,
119,
120,
121,
122,
48,
49,
50,
51,
52,
53,
54,
55,
56,
57,
65,
66
];
}
});
// packages/playwright-core/src/server/socksClientCertificatesInterceptor.ts
function loadDummyServerCertsIfNeeded() {
if (dummyServerTlsOptions)
return;
const { cert, key } = generateSelfSignedCertificate();
dummyServerTlsOptions = { key, cert };
}
function normalizeOrigin(origin) {
try {
return new URL(origin).origin;
} catch (error) {
return origin;
}
}
function convertClientCertificatesToTLSOptions(clientCertificates) {
if (!clientCertificates || !clientCertificates.length)
return;
const tlsOptions = {
pfx: [],
key: [],
cert: []
};
for (const cert of clientCertificates) {
if (cert.cert)
tlsOptions.cert.push(cert.cert);
if (cert.key)
tlsOptions.key.push({ pem: cert.key, passphrase: cert.passphrase });
if (cert.pfx)
tlsOptions.pfx.push({ buf: cert.pfx, passphrase: cert.passphrase });
}
return tlsOptions;
}
function getMatchingTLSOptionsForOrigin(clientCertificates, origin) {
const matchingCerts = clientCertificates?.filter(
(c) => normalizeOrigin(c.origin) === origin
);
return convertClientCertificatesToTLSOptions(matchingCerts);
}
function rewriteToLocalhostIfNeeded(host) {
return host === "local.playwright" ? "localhost" : host;
}
function rewriteOpenSSLErrorIfNeeded(error) {
if (error.message !== "unsupported" && error.code !== "ERR_CRYPTO_UNSUPPORTED_OPERATION")
return error;
return rewriteErrorMessage(error, [
"Unsupported TLS certificate.",
"Most likely, the security algorithm of the given certificate was deprecated by OpenSSL.",
"For more details, see https://github.com/openssl/openssl/blob/master/README-PROVIDERS.md#the-legacy-provider",
"You could probably modernize the certificate by following the steps at https://github.com/nodejs/node/issues/40672#issuecomment-1243648223"
].join("\n"));
}
function parseALPNFromClientHello(buffer) {
if (buffer.length < 6)
return null;
if (buffer[0] !== 22)
return null;
let offset = 5;
if (buffer[offset] !== 1)
return null;
offset += 4;
offset += 2;
offset += 32;
if (offset >= buffer.length)
return null;
const sessionIdLength = buffer[offset];
offset += 1 + sessionIdLength;
if (offset + 2 > buffer.length)
return null;
const cipherSuitesLength = buffer.readUInt16BE(offset);
offset += 2 + cipherSuitesLength;
if (offset >= buffer.length)
return null;
const compressionMethodsLength = buffer[offset];
offset += 1 + compressionMethodsLength;
if (offset + 2 > buffer.length)
return null;
const extensionsLength = buffer.readUInt16BE(offset);
offset += 2;
const extensionsEnd = offset + extensionsLength;
if (extensionsEnd > buffer.length)
return null;
while (offset + 4 <= extensionsEnd) {
const extensionType = buffer.readUInt16BE(offset);
offset += 2;
const extensionLength = buffer.readUInt16BE(offset);
offset += 2;
if (offset + extensionLength > extensionsEnd)
return null;
if (extensionType === 16)
return parseALPNExtension(buffer.subarray(offset, offset + extensionLength));
offset += extensionLength;
}
return null;
}
function parseALPNExtension(buffer) {
if (buffer.length < 2)
return null;
const listLength = buffer.readUInt16BE(0);
if (listLength !== buffer.length - 2)
return null;
const protocols = [];
let offset = 2;
while (offset < buffer.length) {
const protocolLength = buffer[offset];
offset += 1;
if (offset + protocolLength > buffer.length)
break;
const protocol = buffer.subarray(offset, offset + protocolLength).toString("utf8");
protocols.push(protocol);
offset += protocolLength;
}
return protocols.length > 0 ? protocols : null;
}
var import_events4, import_http23, import_net3, import_stream3, import_tls2, getProxyForUrl2, dummyServerTlsOptions, SocksProxyConnection, ClientCertificatesProxy;
var init_socksClientCertificatesInterceptor = __esm({
"packages/playwright-core/src/server/socksClientCertificatesInterceptor.ts"() {
"use strict";
import_events4 = require("events");
import_http23 = __toESM(require("http2"));
import_net3 = __toESM(require("net"));
import_stream3 = __toESM(require("stream"));
import_tls2 = __toESM(require("tls"));
init_socksProxy();
init_debugLogger();
init_happyEyeballs();
init_stringUtils();
init_crypto();
init_stackTrace();
init_network();
init_browserContext();
({ getProxyForUrl: getProxyForUrl2 } = require("./utilsBundle"));
dummyServerTlsOptions = void 0;
SocksProxyConnection = class {
constructor(socksProxy, uid, host, port) {
this._firstPackageReceived = false;
this._closed = false;
this.socksProxy = socksProxy;
this.uid = uid;
this.host = host;
this.port = port;
this._serverCloseEventListener = () => {
this._browserEncrypted.destroy();
};
this._browserEncrypted = new import_stream3.default.Duplex({
read: () => {
},
write: (data, encoding, callback) => {
this.socksProxy._socksProxy.sendSocketData({ uid: this.uid, data });
callback();
},
destroy: (error, callback) => {
if (error)
socksProxy._socksProxy.sendSocketError({ uid: this.uid, error: error.message });
else
socksProxy._socksProxy.sendSocketEnd({ uid: this.uid });
callback();
}
});
}
async connect() {
const proxyAgent = this.socksProxy._getProxyAgent(this.host, this.port);
if (proxyAgent)
this._serverEncrypted = await proxyAgent.connect(new import_events4.EventEmitter(), { host: rewriteToLocalhostIfNeeded(this.host), port: this.port, secureEndpoint: false });
else
this._serverEncrypted = await createSocket(rewriteToLocalhostIfNeeded(this.host), this.port);
this._serverEncrypted.once("close", this._serverCloseEventListener);
this._serverEncrypted.once("error", (error) => this._browserEncrypted.destroy(error));
if (this._closed) {
this._serverEncrypted.destroy();
return;
}
this.socksProxy._socksProxy.socketConnected({
uid: this.uid,
host: this._serverEncrypted.localAddress,
port: this._serverEncrypted.localPort
});
}
onClose() {
this._serverEncrypted.destroy();
this._browserEncrypted.destroy();
this._closed = true;
}
onData(data) {
if (!this._firstPackageReceived) {
this._firstPackageReceived = true;
if (data[0] === 22)
this._establishTlsTunnel(this._browserEncrypted, data);
else
this._establishPlaintextTunnel(this._browserEncrypted);
}
this._browserEncrypted.push(data);
}
_establishPlaintextTunnel(browserEncrypted) {
browserEncrypted.pipe(this._serverEncrypted);
this._serverEncrypted.pipe(browserEncrypted);
}
_establishTlsTunnel(browserEncrypted, clientHello) {
const browserALPNProtocols = parseALPNFromClientHello(clientHello) || ["http/1.1"];
debugLogger.log("client-certificates", `Browser->Proxy ${this.host}:${this.port} offers ALPN ${browserALPNProtocols}`);
const serverDecrypted = import_tls2.default.connect({
socket: this._serverEncrypted,
host: this.host,
port: this.port,
rejectUnauthorized: !this.socksProxy.ignoreHTTPSErrors,
ALPNProtocols: browserALPNProtocols,
servername: !import_net3.default.isIP(this.host) ? this.host : void 0,
secureContext: this.socksProxy.secureContextMap.get(new URL(`https://${this.host}:${this.port}`).origin)
}, async () => {
const browserDecrypted = await this._upgradeToTLSIfNeeded(browserEncrypted, serverDecrypted.alpnProtocol);
debugLogger.log("client-certificates", `Proxy->Server ${this.host}:${this.port} chooses ALPN ${browserDecrypted.alpnProtocol}`);
browserDecrypted.pipe(serverDecrypted);
serverDecrypted.pipe(browserDecrypted);
const cleanup = (error) => this._serverEncrypted.destroy(error);
browserDecrypted.once("error", cleanup);
serverDecrypted.once("error", cleanup);
browserDecrypted.once("close", cleanup);
serverDecrypted.once("close", cleanup);
if (this._closed)
serverDecrypted.destroy();
});
serverDecrypted.once("error", async (error) => {
debugLogger.log("client-certificates", `error when connecting to server: ${error.message.replaceAll("\n", " ")}`);
this._serverEncrypted.removeListener("close", this._serverCloseEventListener);
this._serverEncrypted.destroy();
const browserDecrypted = await this._upgradeToTLSIfNeeded(this._browserEncrypted, serverDecrypted.alpnProtocol);
const responseBody = escapeHTML("Playwright client-certificate error: " + error.message).replaceAll("\n", " <br>");
if (browserDecrypted.alpnProtocol === "h2") {
if ("performServerHandshake" in import_http23.default) {
const session2 = import_http23.default.performServerHandshake(browserDecrypted);
session2.on("error", (error2) => {
this._browserEncrypted.destroy(error2);
});
session2.once("stream", (stream3) => {
const cleanup = (error2) => {
session2.close();
this._browserEncrypted.destroy(error2);
};
stream3.once("end", cleanup);
stream3.once("error", cleanup);
stream3.respond({
[import_http23.default.constants.HTTP2_HEADER_CONTENT_TYPE]: "text/html",
[import_http23.default.constants.HTTP2_HEADER_STATUS]: 503
});
stream3.end(responseBody);
});
} else {
this._browserEncrypted.destroy(error);
}
} else {
browserDecrypted.end([
"HTTP/1.1 503 Internal Server Error",
"Content-Type: text/html; charset=utf-8",
"Content-Length: " + Buffer.byteLength(responseBody),
"",
responseBody
].join("\r\n"));
}
});
}
async _upgradeToTLSIfNeeded(socket, alpnProtocol) {
this._brorwserDecrypted ??= new Promise((resolve, reject) => {
const dummyServer = import_tls2.default.createServer({
...dummyServerTlsOptions,
ALPNProtocols: [alpnProtocol || "http/1.1"]
});
dummyServer.emit("connection", socket);
dummyServer.once("secureConnection", (tlsSocket) => {
dummyServer.close();
resolve(tlsSocket);
});
dummyServer.once("error", (error) => {
dummyServer.close();
reject(error);
});
});
return this._brorwserDecrypted;
}
};
ClientCertificatesProxy = class _ClientCertificatesProxy {
constructor(contextOptions) {
this._connections = /* @__PURE__ */ new Map();
this.secureContextMap = /* @__PURE__ */ new Map();
verifyClientCertificates(contextOptions.clientCertificates);
this.ignoreHTTPSErrors = contextOptions.ignoreHTTPSErrors;
this._proxy = contextOptions.proxy;
this._initSecureContexts(contextOptions.clientCertificates);
this._socksProxy = new SocksProxy();
this._socksProxy.setPattern("*");
this._socksProxy.addListener(SocksProxy.Events.SocksRequested, async (payload) => {
try {
const connection = new SocksProxyConnection(this, payload.uid, payload.host, payload.port);
await connection.connect();
this._connections.set(payload.uid, connection);
} catch (error) {
debugLogger.log("client-certificates", `Failed to connect to ${payload.host}:${payload.port}: ${error.message}`);
this._socksProxy.socketFailed({ uid: payload.uid, errorCode: error.code });
}
});
this._socksProxy.addListener(SocksProxy.Events.SocksData, (payload) => {
this._connections.get(payload.uid)?.onData(payload.data);
});
this._socksProxy.addListener(SocksProxy.Events.SocksClosed, (payload) => {
this._connections.get(payload.uid)?.onClose();
this._connections.delete(payload.uid);
});
loadDummyServerCertsIfNeeded();
}
_getProxyAgent(host, port) {
const proxyFromOptions = createProxyAgent(this._proxy);
if (proxyFromOptions)
return proxyFromOptions;
const proxyFromEnv = getProxyForUrl2(`https://${host}:${port}`);
if (proxyFromEnv)
return createProxyAgent({ server: proxyFromEnv });
}
_initSecureContexts(clientCertificates) {
const origin2certs = /* @__PURE__ */ new Map();
for (const cert of clientCertificates || []) {
const origin = normalizeOrigin(cert.origin);
const certs = origin2certs.get(origin) || [];
certs.push(cert);
origin2certs.set(origin, certs);
}
for (const [origin, certs] of origin2certs) {
try {
this.secureContextMap.set(origin, import_tls2.default.createSecureContext(convertClientCertificatesToTLSOptions(certs)));
} catch (error) {
error = rewriteOpenSSLErrorIfNeeded(error);
throw rewriteErrorMessage(error, `Failed to load client certificate: ${error.message}`);
}
}
}
static async create(progress2, contextOptions) {
const proxy = new _ClientCertificatesProxy(contextOptions);
try {
await progress2.race(proxy._socksProxy.listen(0, "127.0.0.1"));
return proxy;
} catch (error) {
await progress2.race(proxy.close());
throw error;
}
}
proxySettings() {
return { server: `socks5://127.0.0.1:${this._socksProxy.port()}` };
}
async close() {
await this._socksProxy.close();
}
};
}
});
// packages/playwright-core/src/server/trace/recorder/snapshotterInjected.ts
function frameSnapshotStreamer(snapshotStreamer, removeNoScript) {
if (window[snapshotStreamer])
return;
const kShadowAttribute = "__playwright_shadow_root_";
const kValueAttribute = "__playwright_value_";
const kCheckedAttribute = "__playwright_checked_";
const kSelectedAttribute = "__playwright_selected_";
const kScrollTopAttribute = "__playwright_scroll_top_";
const kScrollLeftAttribute = "__playwright_scroll_left_";
const kStyleSheetAttribute = "__playwright_style_sheet_";
const kTargetAttribute = "__playwright_target__";
const kCustomElementsAttribute = "__playwright_custom_elements__";
const kCurrentSrcAttribute = "__playwright_current_src__";
const kBoundingRectAttribute = "__playwright_bounding_rect__";
const kPopoverOpenAttribute = "__playwright_popover_open_";
const kDialogOpenAttribute = "__playwright_dialog_open_";
const kSnapshotFrameId = Symbol("__playwright_snapshot_frameid_");
const kCachedData = Symbol("__playwright_snapshot_cache_");
const kEndOfList = Symbol("__playwright_end_of_list_");
function resetCachedData(obj) {
delete obj[kCachedData];
}
function ensureCachedData(obj) {
if (!obj[kCachedData])
obj[kCachedData] = {};
return obj[kCachedData];
}
function removeHash2(url2) {
try {
const u = new URL(url2);
u.hash = "";
return u.toString();
} catch (e) {
return url2;
}
}
class Streamer {
constructor() {
this._lastSnapshotNumber = 0;
this._staleStyleSheets = /* @__PURE__ */ new Set();
this._modifiedStyleSheets = /* @__PURE__ */ new Set();
this._readingStyleSheet = false;
const invalidateCSSGroupingRule = (rule) => {
if (rule.parentStyleSheet)
this._invalidateStyleSheet(rule.parentStyleSheet);
};
this._interceptNativeMethod(window.CSSStyleSheet.prototype, "insertRule", (sheet) => this._invalidateStyleSheet(sheet));
this._interceptNativeMethod(window.CSSStyleSheet.prototype, "deleteRule", (sheet) => this._invalidateStyleSheet(sheet));
this._interceptNativeMethod(window.CSSStyleSheet.prototype, "addRule", (sheet) => this._invalidateStyleSheet(sheet));
this._interceptNativeMethod(window.CSSStyleSheet.prototype, "removeRule", (sheet) => this._invalidateStyleSheet(sheet));
this._interceptNativeGetter(window.CSSStyleSheet.prototype, "rules", (sheet) => this._invalidateStyleSheet(sheet));
this._interceptNativeGetter(window.CSSStyleSheet.prototype, "cssRules", (sheet) => this._invalidateStyleSheet(sheet));
this._interceptNativeMethod(window.CSSStyleSheet.prototype, "replaceSync", (sheet) => this._invalidateStyleSheet(sheet));
this._interceptNativeMethod(window.CSSGroupingRule.prototype, "insertRule", invalidateCSSGroupingRule);
this._interceptNativeMethod(window.CSSGroupingRule.prototype, "deleteRule", invalidateCSSGroupingRule);
this._interceptNativeGetter(window.CSSGroupingRule.prototype, "cssRules", invalidateCSSGroupingRule);
this._interceptNativeSetter(window.StyleSheet.prototype, "disabled", (sheet) => {
if (sheet instanceof CSSStyleSheet)
this._invalidateStyleSheet(sheet);
});
this._interceptNativeAsyncMethod(window.CSSStyleSheet.prototype, "replace", (sheet) => this._invalidateStyleSheet(sheet));
this._fakeBase = document.createElement("base");
this._observer = new MutationObserver((list) => this._handleMutations(list));
const observerConfig = { attributes: true, subtree: true };
this._observer.observe(document, observerConfig);
this._refreshListenersWhenNeeded();
}
_refreshListenersWhenNeeded() {
this._refreshListeners();
const customEventName = "__playwright_snapshotter_global_listeners_check__";
let seenEvent = false;
const handleCustomEvent = () => seenEvent = true;
window.addEventListener(customEventName, handleCustomEvent);
const observer = new MutationObserver((entries) => {
const newDocumentElement = entries.some((entry) => Array.from(entry.addedNodes).includes(document.documentElement));
if (newDocumentElement) {
seenEvent = false;
window.dispatchEvent(new CustomEvent(customEventName));
if (!seenEvent) {
window.addEventListener(customEventName, handleCustomEvent);
this._refreshListeners();
}
}
});
observer.observe(document, { childList: true });
}
_refreshListeners() {
document.addEventListener("__playwright_mark_target__", (event) => {
if (!event.detail)
return;
const callId = event.detail;
event.composedPath()[0].__playwright_target__ = callId;
});
document.addEventListener("__playwright_unmark_target__", (event) => {
if (!event.detail)
return;
const callId = event.detail;
if (event.composedPath()[0].__playwright_target__ === callId)
delete event.composedPath()[0].__playwright_target__;
});
}
_interceptNativeMethod(obj, method, cb) {
const native = obj[method];
if (!native)
return;
obj[method] = function(...args) {
const result2 = native.call(this, ...args);
cb(this, result2);
return result2;
};
}
_interceptNativeAsyncMethod(obj, method, cb) {
const native = obj[method];
if (!native)
return;
obj[method] = async function(...args) {
const result2 = await native.call(this, ...args);
cb(this, result2);
return result2;
};
}
_interceptNativeGetter(obj, prop, cb) {
const descriptor = Object.getOwnPropertyDescriptor(obj, prop);
Object.defineProperty(obj, prop, {
...descriptor,
get: function() {
const result2 = descriptor.get.call(this);
cb(this, result2);
return result2;
}
});
}
_interceptNativeSetter(obj, prop, cb) {
const descriptor = Object.getOwnPropertyDescriptor(obj, prop);
Object.defineProperty(obj, prop, {
...descriptor,
set: function(value2) {
const result2 = descriptor.set.call(this, value2);
cb(this, value2);
return result2;
}
});
}
_handleMutations(list) {
for (const mutation of list)
ensureCachedData(mutation.target).attributesCached = void 0;
}
_invalidateStyleSheet(sheet) {
if (this._readingStyleSheet)
return;
this._staleStyleSheets.add(sheet);
if (sheet.href !== null)
this._modifiedStyleSheets.add(sheet);
}
_updateStyleElementStyleSheetTextIfNeeded(sheet, forceText) {
const data = ensureCachedData(sheet);
if (this._staleStyleSheets.has(sheet) || forceText && data.cssText === void 0) {
this._staleStyleSheets.delete(sheet);
try {
data.cssText = this._getSheetText(sheet);
} catch (e) {
data.cssText = "";
}
}
return data.cssText;
}
// Returns either content, ref, or no override.
_updateLinkStyleSheetTextIfNeeded(sheet, snapshotNumber) {
const data = ensureCachedData(sheet);
if (this._staleStyleSheets.has(sheet)) {
this._staleStyleSheets.delete(sheet);
try {
data.cssText = this._getSheetText(sheet);
data.cssRef = snapshotNumber;
return data.cssText;
} catch (e) {
}
}
return data.cssRef === void 0 ? void 0 : snapshotNumber - data.cssRef;
}
markIframe(iframeElement, frameId) {
iframeElement[kSnapshotFrameId] = frameId;
}
reset() {
this._staleStyleSheets.clear();
const visitNode = (node) => {
resetCachedData(node);
if (node.nodeType === Node.ELEMENT_NODE) {
const element2 = node;
if (element2.shadowRoot)
visitNode(element2.shadowRoot);
}
for (let child = node.firstChild; child; child = child.nextSibling)
visitNode(child);
};
visitNode(document.documentElement);
visitNode(this._fakeBase);
}
__sanitizeMetaAttribute(name, value2, httpEquiv) {
if (name === "charset")
return "utf-8";
if (httpEquiv.toLowerCase() !== "content-type" || name !== "content")
return value2;
const [type3, ...params2] = value2.split(";");
if (type3 !== "text/html" || params2.length <= 0)
return value2;
const charsetParamIdx = params2.findIndex((param) => param.trim().startsWith("charset="));
if (charsetParamIdx > -1)
params2[charsetParamIdx] = "charset=utf-8";
return `${type3}; ${params2.join("; ")}`;
}
_sanitizeUrl(url2) {
if (url2.startsWith("javascript:") || url2.startsWith("vbscript:"))
return "";
return url2;
}
_sanitizeSrcSet(srcset) {
return srcset.split(",").map((src) => {
src = src.trim();
const spaceIndex = src.lastIndexOf(" ");
if (spaceIndex === -1)
return this._sanitizeUrl(src);
return this._sanitizeUrl(src.substring(0, spaceIndex).trim()) + src.substring(spaceIndex);
}).join(", ");
}
_resolveUrl(base, url2) {
if (url2 === "")
return "";
try {
return new URL(url2, base).href;
} catch (e) {
return url2;
}
}
_getSheetBase(sheet) {
let rootSheet = sheet;
while (rootSheet.parentStyleSheet)
rootSheet = rootSheet.parentStyleSheet;
if (rootSheet.ownerNode)
return rootSheet.ownerNode.baseURI;
return document.baseURI;
}
_getSheetText(sheet) {
this._readingStyleSheet = true;
try {
if (sheet.disabled)
return "";
const rules = [];
for (const rule of sheet.cssRules)
rules.push(rule.cssText);
return rules.join("\n");
} finally {
this._readingStyleSheet = false;
}
}
captureSnapshot(needsReset) {
const timestamp = performance.now();
const snapshotNumber = ++this._lastSnapshotNumber;
if (needsReset)
this.reset();
let nodeCounter = 0;
let shadowDomNesting = 0;
let headNesting = 0;
this._handleMutations(this._observer.takeRecords());
const definedCustomElements = /* @__PURE__ */ new Set();
const visitNode = (node) => {
const nodeType = node.nodeType;
const nodeName = nodeType === Node.DOCUMENT_FRAGMENT_NODE ? "template" : node.nodeName;
if (nodeType !== Node.ELEMENT_NODE && nodeType !== Node.DOCUMENT_FRAGMENT_NODE && nodeType !== Node.TEXT_NODE)
return;
if (nodeName === "SCRIPT")
return;
if (nodeName === "LINK" && nodeType === Node.ELEMENT_NODE) {
const rel = node.getAttribute("rel")?.toLowerCase();
if (rel === "preload" || rel === "prefetch")
return;
}
if (removeNoScript && nodeName === "NOSCRIPT")
return;
if (nodeName === "META") {
const httpEquiv = node.httpEquiv.toLowerCase();
if (httpEquiv === "content-security-policy" || httpEquiv === "refresh" || httpEquiv === "set-cookie")
return;
}
if ((nodeName === "IFRAME" || nodeName === "FRAME") && headNesting)
return;
const data = ensureCachedData(node);
const values = [];
let equals = !!data.cached;
let extraNodes = 0;
const expectValue = (value2) => {
equals = equals && data.cached[values.length] === value2;
values.push(value2);
};
const checkAndReturn = (n) => {
data.attributesCached = true;
if (equals)
return { equals: true, n: [[snapshotNumber - data.ref[0], data.ref[1]]] };
nodeCounter += extraNodes;
data.ref = [snapshotNumber, nodeCounter++];
data.cached = values;
return { equals: false, n };
};
if (nodeType === Node.TEXT_NODE) {
const value2 = node.nodeValue || "";
expectValue(value2);
return checkAndReturn(value2);
}
if (nodeName === "STYLE") {
const sheet = node.sheet;
let cssText;
if (sheet)
cssText = this._updateStyleElementStyleSheetTextIfNeeded(sheet);
cssText = cssText || node.textContent || "";
expectValue(cssText);
extraNodes++;
return checkAndReturn([nodeName, {}, cssText]);
}
const attrs = {};
const result3 = [nodeName, attrs];
const visitChild = (child) => {
const snapshot3 = visitNode(child);
if (snapshot3) {
result3.push(snapshot3.n);
expectValue(child);
equals = equals && snapshot3.equals;
}
};
const visitChildStyleSheet = (child) => {
const snapshot3 = visitStyleSheet(child);
if (snapshot3) {
result3.push(snapshot3.n);
expectValue(child);
equals = equals && snapshot3.equals;
}
};
if (nodeType === Node.DOCUMENT_FRAGMENT_NODE)
attrs[kShadowAttribute] = "open";
if (nodeType === Node.ELEMENT_NODE) {
const element2 = node;
if (element2.localName.includes("-") && window.customElements?.get(element2.localName))
definedCustomElements.add(element2.localName);
if (nodeName === "INPUT" || nodeName === "TEXTAREA") {
const value2 = element2.value;
expectValue(kValueAttribute);
expectValue(value2);
attrs[kValueAttribute] = value2;
}
if (nodeName === "INPUT" && ["checkbox", "radio"].includes(element2.type)) {
const value2 = element2.checked ? "true" : "false";
expectValue(kCheckedAttribute);
expectValue(value2);
attrs[kCheckedAttribute] = value2;
}
if (nodeName === "OPTION") {
const value2 = element2.selected ? "true" : "false";
expectValue(kSelectedAttribute);
expectValue(value2);
attrs[kSelectedAttribute] = value2;
}
if (nodeName === "CANVAS" || nodeName === "IFRAME" || nodeName === "FRAME") {
const boundingRect = element2.getBoundingClientRect();
const value2 = JSON.stringify({
left: boundingRect.left,
top: boundingRect.top,
right: boundingRect.right,
bottom: boundingRect.bottom
});
expectValue(kBoundingRectAttribute);
expectValue(value2);
attrs[kBoundingRectAttribute] = value2;
}
if (element2.popover && element2.matches && element2.matches(":popover-open")) {
const value2 = "true";
expectValue(kPopoverOpenAttribute);
expectValue(value2);
attrs[kPopoverOpenAttribute] = value2;
}
if (nodeName === "DIALOG" && element2.open) {
const value2 = element2.matches(":modal") ? "modal" : "true";
expectValue(kDialogOpenAttribute);
expectValue(value2);
attrs[kDialogOpenAttribute] = value2;
}
if (element2.scrollTop) {
expectValue(kScrollTopAttribute);
expectValue(element2.scrollTop);
attrs[kScrollTopAttribute] = "" + element2.scrollTop;
}
if (element2.scrollLeft) {
expectValue(kScrollLeftAttribute);
expectValue(element2.scrollLeft);
attrs[kScrollLeftAttribute] = "" + element2.scrollLeft;
}
if (element2.shadowRoot) {
++shadowDomNesting;
visitChild(element2.shadowRoot);
--shadowDomNesting;
}
if ("__playwright_target__" in element2) {
expectValue(kTargetAttribute);
expectValue(element2["__playwright_target__"]);
attrs[kTargetAttribute] = element2["__playwright_target__"];
}
}
if (nodeName === "HEAD") {
++headNesting;
this._fakeBase.setAttribute("href", document.baseURI);
visitChild(this._fakeBase);
}
for (let child = node.firstChild; child; child = child.nextSibling)
visitChild(child);
if (nodeName === "HEAD")
--headNesting;
expectValue(kEndOfList);
let documentOrShadowRoot = null;
if (node.ownerDocument.documentElement === node)
documentOrShadowRoot = node.ownerDocument;
else if (node.nodeType === Node.DOCUMENT_FRAGMENT_NODE)
documentOrShadowRoot = node;
if (documentOrShadowRoot) {
for (const sheet of documentOrShadowRoot.adoptedStyleSheets || [])
visitChildStyleSheet(sheet);
expectValue(kEndOfList);
}
if (nodeName === "IFRAME" || nodeName === "FRAME") {
const element2 = node;
const frameId = element2[kSnapshotFrameId];
const name = "src";
const value2 = frameId ? `/snapshot/${frameId}` : "";
expectValue(name);
expectValue(value2);
attrs[name] = value2;
}
if (nodeName === "BODY" && definedCustomElements.size) {
const value2 = [...definedCustomElements].join(",");
expectValue(kCustomElementsAttribute);
expectValue(value2);
attrs[kCustomElementsAttribute] = value2;
}
if (nodeName === "IMG" || nodeName === "PICTURE") {
const value2 = nodeName === "PICTURE" ? "" : this._sanitizeUrl(node.currentSrc);
expectValue(kCurrentSrcAttribute);
expectValue(value2);
attrs[kCurrentSrcAttribute] = value2;
}
if (equals && data.attributesCached && !shadowDomNesting)
return checkAndReturn(result3);
if (nodeType === Node.ELEMENT_NODE) {
const element2 = node;
for (let i = 0; i < element2.attributes.length; i++) {
const name = element2.attributes[i].name;
if (nodeName === "LINK" && name === "integrity")
continue;
if (nodeName === "IFRAME" && (name === "src" || name === "srcdoc" || name === "sandbox"))
continue;
if (nodeName === "FRAME" && name === "src")
continue;
if (nodeName === "DIALOG" && name === "open")
continue;
let value2 = element2.attributes[i].value;
if (nodeName === "META")
value2 = this.__sanitizeMetaAttribute(name, value2, node.httpEquiv);
else if (name === "src" && nodeName === "IMG")
value2 = this._sanitizeUrl(value2);
else if (name === "srcset" && nodeName === "IMG")
value2 = this._sanitizeSrcSet(value2);
else if (name === "srcset" && nodeName === "SOURCE")
value2 = this._sanitizeSrcSet(value2);
else if (name === "href" && nodeName === "LINK")
value2 = this._sanitizeUrl(value2);
else if (name.startsWith("on"))
value2 = "";
expectValue(name);
expectValue(value2);
attrs[name] = value2;
}
expectValue(kEndOfList);
}
if (result3.length === 2 && !Object.keys(attrs).length)
result3.pop();
return checkAndReturn(result3);
};
const visitStyleSheet = (sheet) => {
const data = ensureCachedData(sheet);
const oldCSSText = data.cssText;
const cssText = this._updateStyleElementStyleSheetTextIfNeeded(
sheet,
true
/* forceText */
);
if (cssText === oldCSSText)
return { equals: true, n: [[snapshotNumber - data.ref[0], data.ref[1]]] };
data.ref = [snapshotNumber, nodeCounter++];
return {
equals: false,
n: ["template", {
[kStyleSheetAttribute]: cssText
}]
};
};
let html;
if (document.documentElement) {
const { n } = visitNode(document.documentElement);
html = n;
} else {
html = ["html"];
}
const result2 = {
html,
doctype: document.doctype ? document.doctype.name : void 0,
resourceOverrides: [],
viewport: {
width: window.innerWidth,
height: window.innerHeight
},
url: location.href,
wallTime: Date.now(),
collectionTime: 0
};
for (const sheet of this._modifiedStyleSheets) {
if (sheet.href === null)
continue;
const content = this._updateLinkStyleSheetTextIfNeeded(sheet, snapshotNumber);
if (content === void 0) {
continue;
}
const base = this._getSheetBase(sheet);
const url2 = removeHash2(this._resolveUrl(base, sheet.href));
result2.resourceOverrides.push({ url: url2, content, contentType: "text/css" });
}
result2.collectionTime = performance.now() - timestamp;
return result2;
}
}
window[snapshotStreamer] = new Streamer();
}
var init_snapshotterInjected = __esm({
"packages/playwright-core/src/server/trace/recorder/snapshotterInjected.ts"() {
"use strict";
}
});
// packages/isomorphic/utilityScriptSerializers.ts
function isRegExp5(obj) {
try {
return obj instanceof RegExp || Object.prototype.toString.call(obj) === "[object RegExp]";
} catch (error) {
return false;
}
}
function isDate2(obj) {
try {
return obj instanceof Date || Object.prototype.toString.call(obj) === "[object Date]";
} catch (error) {
return false;
}
}
function isURL2(obj) {
try {
return obj instanceof URL || Object.prototype.toString.call(obj) === "[object URL]";
} catch (error) {
return false;
}
}
function isError3(obj) {
try {
return obj instanceof Error || obj && Object.getPrototypeOf(obj)?.name === "Error";
} catch (error) {
return false;
}
}
function isTypedArray(obj, constructor) {
try {
return obj instanceof constructor || Object.prototype.toString.call(obj) === `[object ${constructor.name}]`;
} catch (error) {
return false;
}
}
function isArrayBuffer(obj) {
try {
return obj instanceof ArrayBuffer || Object.prototype.toString.call(obj) === "[object ArrayBuffer]";
} catch (error) {
return false;
}
}
function typedArrayToBase64(array) {
if ("toBase64" in array)
return array.toBase64();
const binary = Array.from(new Uint8Array(array.buffer, array.byteOffset, array.byteLength)).map((b) => String.fromCharCode(b)).join("");
return btoa(binary);
}
function base64ToTypedArray(base64, TypedArrayConstructor) {
const binary = atob(base64);
const bytes = new Uint8Array(binary.length);
for (let i = 0; i < binary.length; i++)
bytes[i] = binary.charCodeAt(i);
return new TypedArrayConstructor(bytes.buffer);
}
function parseEvaluationResultValue(value2, handles = [], refs = /* @__PURE__ */ new Map()) {
if (Object.is(value2, void 0))
return void 0;
if (typeof value2 === "object" && value2) {
if ("ref" in value2)
return refs.get(value2.ref);
if ("v" in value2) {
if (value2.v === "undefined")
return void 0;
if (value2.v === "null")
return null;
if (value2.v === "NaN")
return NaN;
if (value2.v === "Infinity")
return Infinity;
if (value2.v === "-Infinity")
return -Infinity;
if (value2.v === "-0")
return -0;
return void 0;
}
if ("d" in value2) {
return new Date(value2.d);
}
if ("u" in value2)
return new URL(value2.u);
if ("bi" in value2)
return BigInt(value2.bi);
if ("e" in value2) {
const error = new Error(value2.e.m);
error.name = value2.e.n;
error.stack = value2.e.s;
return error;
}
if ("r" in value2)
return new RegExp(value2.r.p, value2.r.f);
if ("a" in value2) {
const result2 = [];
refs.set(value2.id, result2);
for (const a of value2.a)
result2.push(parseEvaluationResultValue(a, handles, refs));
return result2;
}
if ("o" in value2) {
const result2 = {};
refs.set(value2.id, result2);
for (const { k, v } of value2.o) {
if (k === "__proto__")
continue;
result2[k] = parseEvaluationResultValue(v, handles, refs);
}
return result2;
}
if ("h" in value2)
return handles[value2.h];
if ("ta" in value2)
return base64ToTypedArray(value2.ta.b, typedArrayConstructors[value2.ta.k]);
if ("ab" in value2)
return base64ToTypedArray(value2.ab.b, Uint8Array).buffer;
}
return value2;
}
function serializeAsCallArgument(value2, handleSerializer) {
return serialize(value2, handleSerializer, { visited: /* @__PURE__ */ new Map(), lastId: 0 });
}
function serialize(value2, handleSerializer, visitorInfo) {
if (value2 && typeof value2 === "object") {
if (typeof globalThis.Window === "function" && value2 instanceof globalThis.Window)
return "ref: <Window>";
if (typeof globalThis.Document === "function" && value2 instanceof globalThis.Document)
return "ref: <Document>";
if (typeof globalThis.Node === "function" && value2 instanceof globalThis.Node)
return "ref: <Node>";
}
return innerSerialize(value2, handleSerializer, visitorInfo);
}
function innerSerialize(value2, handleSerializer, visitorInfo) {
const result2 = handleSerializer(value2);
if ("fallThrough" in result2)
value2 = result2.fallThrough;
else
return result2;
if (typeof value2 === "symbol")
return { v: "undefined" };
if (Object.is(value2, void 0))
return { v: "undefined" };
if (Object.is(value2, null))
return { v: "null" };
if (Object.is(value2, NaN))
return { v: "NaN" };
if (Object.is(value2, Infinity))
return { v: "Infinity" };
if (Object.is(value2, -Infinity))
return { v: "-Infinity" };
if (Object.is(value2, -0))
return { v: "-0" };
if (typeof value2 === "boolean")
return value2;
if (typeof value2 === "number")
return value2;
if (typeof value2 === "string")
return value2;
if (typeof value2 === "bigint")
return { bi: value2.toString() };
if (isError3(value2)) {
let stack;
if (value2.stack?.startsWith(value2.name + ": " + value2.message)) {
stack = value2.stack;
} else {
stack = `${value2.name}: ${value2.message}
${value2.stack}`;
}
return { e: { n: value2.name, m: value2.message, s: stack } };
}
if (isDate2(value2))
return { d: value2.toJSON() };
if (isURL2(value2))
return { u: value2.toJSON() };
if (isRegExp5(value2))
return { r: { p: value2.source, f: value2.flags } };
for (const [k, ctor] of Object.entries(typedArrayConstructors)) {
if (isTypedArray(value2, ctor))
return { ta: { b: typedArrayToBase64(value2), k } };
}
if (isArrayBuffer(value2))
return { ab: { b: typedArrayToBase64(new Uint8Array(value2)) } };
const id = visitorInfo.visited.get(value2);
if (id)
return { ref: id };
if (Array.isArray(value2)) {
const a = [];
const id2 = ++visitorInfo.lastId;
visitorInfo.visited.set(value2, id2);
for (let i = 0; i < value2.length; ++i)
a.push(serialize(value2[i], handleSerializer, visitorInfo));
return { a, id: id2 };
}
if (typeof value2 === "object") {
const o = [];
const id2 = ++visitorInfo.lastId;
visitorInfo.visited.set(value2, id2);
for (const name of Object.keys(value2)) {
let item;
try {
item = value2[name];
} catch (e) {
continue;
}
if (name === "toJSON" && typeof item === "function")
o.push({ k: name, v: { o: [], id: 0 } });
else
o.push({ k: name, v: serialize(item, handleSerializer, visitorInfo) });
}
let jsonWrapper;
try {
if (o.length === 0 && value2.toJSON && typeof value2.toJSON === "function")
jsonWrapper = { value: value2.toJSON() };
} catch (e) {
}
if (jsonWrapper)
return innerSerialize(jsonWrapper.value, handleSerializer, visitorInfo);
return { o, id: id2 };
}
}
var typedArrayConstructors;
var init_utilityScriptSerializers = __esm({
"packages/isomorphic/utilityScriptSerializers.ts"() {
"use strict";
typedArrayConstructors = {
i8: Int8Array,
ui8: Uint8Array,
ui8c: Uint8ClampedArray,
i16: Int16Array,
ui16: Uint16Array,
i32: Int32Array,
ui32: Uint32Array,
// TODO: add Float16Array once it's in baseline
f32: Float32Array,
f64: Float64Array,
bi64: BigInt64Array,
bui64: BigUint64Array
};
}
});
// packages/playwright-core/src/server/disposable.ts
async function disposeAll2(disposables) {
const copy = [...disposables];
disposables.length = 0;
await Promise.all(copy.map((d) => d.dispose()));
}
var DisposableObject;
var init_disposable2 = __esm({
"packages/playwright-core/src/server/disposable.ts"() {
"use strict";
init_instrumentation();
DisposableObject = class extends SdkObject {
constructor(parent) {
super(parent, "disposable");
this.parent = parent;
}
};
}
});
// packages/playwright-core/src/server/console.ts
var ConsoleMessage;
var init_console = __esm({
"packages/playwright-core/src/server/console.ts"() {
"use strict";
ConsoleMessage = class {
constructor(page, worker, type3, text2, args, location2, timestamp) {
this._page = page;
this._worker = worker;
this._type = type3;
this._text = text2;
this._args = args;
this._location = location2 || { url: "", lineNumber: 0, columnNumber: 0 };
this._timestamp = timestamp;
}
page() {
return this._page;
}
worker() {
return this._worker;
}
type() {
return this._type;
}
text() {
if (this._text === void 0)
this._text = this._args.map((arg) => arg.preview()).join(" ");
return this._text;
}
args() {
return this._args;
}
location() {
return this._location;
}
timestamp() {
return this._timestamp;
}
};
}
});
// packages/playwright-core/src/server/fileChooser.ts
var FileChooser;
var init_fileChooser = __esm({
"packages/playwright-core/src/server/fileChooser.ts"() {
"use strict";
FileChooser = class {
constructor(elementHandle, isMultiple) {
this._elementHandle = elementHandle;
this._isMultiple = isMultiple;
}
element() {
return this._elementHandle;
}
isMultiple() {
return this._isMultiple;
}
};
}
});
// packages/playwright-core/src/generated/utilityScriptSource.ts
var source2;
var init_utilityScriptSource = __esm({
"packages/playwright-core/src/generated/utilityScriptSource.ts"() {
"use strict";
source2 = '\nvar __commonJS = obj => {\n let required = false;\n let result;\n return function __require() {\n if (!required) {\n required = true;\n let fn;\n for (const name in obj) { fn = obj[name]; break; }\n const module = { exports: {} };\n fn(module.exports, module);\n result = module.exports;\n }\n return result;\n }\n};\nvar __export = (target, all) => {for (var name in all) target[name] = all[name];};\nvar __toESM = mod => ({ ...mod, \'default\': mod });\nvar __toCommonJS = mod => ({ ...mod, __esModule: true });\n\n\n// packages/injected/src/utilityScript.ts\nvar utilityScript_exports = {};\n__export(utilityScript_exports, {\n UtilityScript: () => UtilityScript\n});\nmodule.exports = __toCommonJS(utilityScript_exports);\n\n// packages/isomorphic/utilityScriptSerializers.ts\nfunction isRegExp(obj) {\n try {\n return obj instanceof RegExp || Object.prototype.toString.call(obj) === "[object RegExp]";\n } catch (error) {\n return false;\n }\n}\nfunction isDate(obj) {\n try {\n return obj instanceof Date || Object.prototype.toString.call(obj) === "[object Date]";\n } catch (error) {\n return false;\n }\n}\nfunction isURL(obj) {\n try {\n return obj instanceof URL || Object.prototype.toString.call(obj) === "[object URL]";\n } catch (error) {\n return false;\n }\n}\nfunction isError(obj) {\n var _a;\n try {\n return obj instanceof Error || obj && ((_a = Object.getPrototypeOf(obj)) == null ? void 0 : _a.name) === "Error";\n } catch (error) {\n return false;\n }\n}\nfunction isTypedArray(obj, constructor) {\n try {\n return obj instanceof constructor || Object.prototype.toString.call(obj) === `[object ${constructor.name}]`;\n } catch (error) {\n return false;\n }\n}\nfunction isArrayBuffer(obj) {\n try {\n return obj instanceof ArrayBuffer || Object.prototype.toString.call(obj) === "[object ArrayBuffer]";\n } catch (error) {\n return false;\n }\n}\nvar typedArrayConstructors = {\n i8: Int8Array,\n ui8: Uint8Array,\n ui8c: Uint8ClampedArray,\n i16: Int16Array,\n ui16: Uint16Array,\n i32: Int32Array,\n ui32: Uint32Array,\n // TODO: add Float16Array once it\'s in baseline\n f32: Float32Array,\n f64: Float64Array,\n bi64: BigInt64Array,\n bui64: BigUint64Array\n};\nfunction typedArrayToBase64(array) {\n if ("toBase64" in array)\n return array.toBase64();\n const binary = Array.from(new Uint8Array(array.buffer, array.byteOffset, array.byteLength)).map((b) => String.fromCharCode(b)).join("");\n return btoa(binary);\n}\nfunction base64ToTypedArray(base64, TypedArrayConstructor) {\n const binary = atob(base64);\n const bytes = new Uint8Array(binary.length);\n for (let i = 0; i < binary.length; i++)\n bytes[i] = binary.charCodeAt(i);\n return new TypedArrayConstructor(bytes.buffer);\n}\nfunction parseEvaluationResultValue(value, handles = [], refs = /* @__PURE__ */ new Map()) {\n if (Object.is(value, void 0))\n return void 0;\n if (typeof value === "object" && value) {\n if ("ref" in value)\n return refs.get(value.ref);\n if ("v" in value) {\n if (value.v === "undefined")\n return void 0;\n if (value.v === "null")\n return null;\n if (value.v === "NaN")\n return NaN;\n if (value.v === "Infinity")\n return Infinity;\n if (value.v === "-Infinity")\n return -Infinity;\n if (value.v === "-0")\n return -0;\n return void 0;\n }\n if ("d" in value) {\n return new Date(value.d);\n }\n if ("u" in value)\n return new URL(value.u);\n if ("bi" in value)\n return BigInt(value.bi);\n if ("e" in value) {\n const error = new Error(value.e.m);\n error.name = value.e.n;\n error.stack = value.e.s;\n return error;\n }\n if ("r" in value)\n return new RegExp(value.r.p, value.r.f);\n if ("a" in value) {\n const result = [];\n refs.set(value.id, result);\n for (const a of value.a)\n result.push(parseEvaluationResultValue(a, handles, refs));\n return result;\n }\n if ("o" in value) {\n const result = {};\n refs.set(value.id, result);\n for (const { k, v } of value.o) {\n if (k === "__proto__")\n continue;\n result[k] = parseEvaluationResultValue(v, handles, refs);\n }\n return result;\n }\n if ("h" in value)\n return handles[value.h];\n if ("ta" in value)\n return base64ToTypedArray(value.ta.b, typedArrayConstructors[value.ta.k]);\n if ("ab" in value)\n return base64ToTypedArray(value.ab.b, Uint8Array).buffer;\n }\n return value;\n}\nfunction serializeAsCallArgument(value, handleSerializer) {\n return serialize(value, handleSerializer, { visited: /* @__PURE__ */ new Map(), lastId: 0 });\n}\nfunction serialize(value, handleSerializer, visitorInfo) {\n if (value && typeof value === "object") {\n if (typeof globalThis.Window === "function" && value instanceof globalThis.Window)\n return "ref: <Window>";\n if (typeof globalThis.Document === "function" && value instanceof globalThis.Document)\n return "ref: <Document>";\n if (typeof globalThis.Node === "function" && value instanceof globalThis.Node)\n return "ref: <Node>";\n }\n return innerSerialize(value, handleSerializer, visitorInfo);\n}\nfunction innerSerialize(value, handleSerializer, visitorInfo) {\n var _a;\n const result = handleSerializer(value);\n if ("fallThrough" in result)\n value = result.fallThrough;\n else\n return result;\n if (typeof value === "symbol")\n return { v: "undefined" };\n if (Object.is(value, void 0))\n return { v: "undefined" };\n if (Object.is(value, null))\n return { v: "null" };\n if (Object.is(value, NaN))\n return { v: "NaN" };\n if (Object.is(value, Infinity))\n return { v: "Infinity" };\n if (Object.is(value, -Infinity))\n return { v: "-Infinity" };\n if (Object.is(value, -0))\n return { v: "-0" };\n if (typeof value === "boolean")\n return value;\n if (typeof value === "number")\n return value;\n if (typeof value === "string")\n return value;\n if (typeof value === "bigint")\n return { bi: value.toString() };\n if (isError(value)) {\n let stack;\n if ((_a = value.stack) == null ? void 0 : _a.startsWith(value.name + ": " + value.message)) {\n stack = value.stack;\n } else {\n stack = `${value.name}: ${value.message}\n${value.stack}`;\n }\n return { e: { n: value.name, m: value.message, s: stack } };\n }\n if (isDate(value))\n return { d: value.toJSON() };\n if (isURL(value))\n return { u: value.toJSON() };\n if (isRegExp(value))\n return { r: { p: value.source, f: value.flags } };\n for (const [k, ctor] of Object.entries(typedArrayConstructors)) {\n if (isTypedArray(value, ctor))\n return { ta: { b: typedArrayToBase64(value), k } };\n }\n if (isArrayBuffer(value))\n return { ab: { b: typedArrayToBase64(new Uint8Array(value)) } };\n const id = visitorInfo.visited.get(value);\n if (id)\n return { ref: id };\n if (Array.isArray(value)) {\n const a = [];\n const id2 = ++visitorInfo.lastId;\n visitorInfo.visited.set(value, id2);\n for (let i = 0; i < value.length; ++i)\n a.push(serialize(value[i], handleSerializer, visitorInfo));\n return { a, id: id2 };\n }\n if (typeof value === "object") {\n const o = [];\n const id2 = ++visitorInfo.lastId;\n visitorInfo.visited.set(value, id2);\n for (const name of Object.keys(value)) {\n let item;\n try {\n item = value[name];\n } catch (e) {\n continue;\n }\n if (name === "toJSON" && typeof item === "function")\n o.push({ k: name, v: { o: [], id: 0 } });\n else\n o.push({ k: name, v: serialize(item, handleSerializer, visitorInfo) });\n }\n let jsonWrapper;\n try {\n if (o.length === 0 && value.toJSON && typeof value.toJSON === "function")\n jsonWrapper = { value: value.toJSON() };\n } catch (e) {\n }\n if (jsonWrapper)\n return innerSerialize(jsonWrapper.value, handleSerializer, visitorInfo);\n return { o, id: id2 };\n }\n}\n\n// packages/injected/src/utilityScript.ts\nvar UtilityScript = class {\n constructor(global, isUnderTest) {\n var _a, _b, _c, _d, _e, _f, _g, _h;\n this.global = global;\n this.isUnderTest = isUnderTest;\n if (global.__pwClock) {\n this.builtins = global.__pwClock.builtins;\n } else {\n this.builtins = {\n setTimeout: (_a = global.setTimeout) == null ? void 0 : _a.bind(global),\n clearTimeout: (_b = global.clearTimeout) == null ? void 0 : _b.bind(global),\n setInterval: (_c = global.setInterval) == null ? void 0 : _c.bind(global),\n clearInterval: (_d = global.clearInterval) == null ? void 0 : _d.bind(global),\n requestAnimationFrame: (_e = global.requestAnimationFrame) == null ? void 0 : _e.bind(global),\n cancelAnimationFrame: (_f = global.cancelAnimationFrame) == null ? void 0 : _f.bind(global),\n requestIdleCallback: (_g = global.requestIdleCallback) == null ? void 0 : _g.bind(global),\n cancelIdleCallback: (_h = global.cancelIdleCallback) == null ? void 0 : _h.bind(global),\n performance: global.performance,\n Intl: global.Intl,\n Date: global.Date,\n AbortSignal: global.AbortSignal\n };\n }\n if (this.isUnderTest)\n global.builtins = this.builtins;\n }\n evaluate(isFunction, returnByValue, expression, argCount, ...argsAndHandles) {\n const args = argsAndHandles.slice(0, argCount);\n const handles = argsAndHandles.slice(argCount);\n const parameters = [];\n for (let i = 0; i < args.length; i++)\n parameters[i] = parseEvaluationResultValue(args[i], handles);\n let result = this.global.eval(expression);\n if (isFunction === true) {\n result = result(...parameters);\n } else if (isFunction === false) {\n result = result;\n } else {\n if (typeof result === "function")\n result = result(...parameters);\n }\n return returnByValue ? this._promiseAwareJsonValueNoThrow(result) : result;\n }\n jsonValue(returnByValue, value) {\n if (value === void 0)\n return void 0;\n return serializeAsCallArgument(value, (value2) => ({ fallThrough: value2 }));\n }\n _promiseAwareJsonValueNoThrow(value) {\n const safeJson = (value2) => {\n try {\n return this.jsonValue(true, value2);\n } catch (e) {\n return void 0;\n }\n };\n if (value && typeof value === "object" && typeof value.then === "function") {\n return (async () => {\n const promiseValue = await value;\n return safeJson(promiseValue);\n })();\n }\n return safeJson(value);\n }\n};\n';
}
});
// packages/playwright-core/src/server/javascript.ts
async function evaluate(context2, returnByValue, pageFunction, ...args) {
return evaluateExpression(context2, String(pageFunction), { returnByValue, isFunction: typeof pageFunction === "function" }, ...args);
}
async function evaluateExpression(context2, expression2, options2, ...args) {
expression2 = normalizeEvaluationExpression(expression2, options2.isFunction);
const handles = [];
const toDispose = [];
const pushHandle = (handle) => {
handles.push(handle);
return handles.length - 1;
};
args = args.map((arg) => serializeAsCallArgument(arg, (handle) => {
if (handle instanceof JSHandle) {
if (!handle._objectId)
return { fallThrough: handle._value };
if (handle._disposed)
throw new JavaScriptErrorInEvaluate("JSHandle is disposed!");
const adopted = context2.adoptIfNeeded(handle);
if (adopted === null)
return { h: pushHandle(Promise.resolve(handle)) };
toDispose.push(adopted);
return { h: pushHandle(adopted) };
}
return { fallThrough: handle };
}));
const utilityScriptObjects = [];
for (const handle of await Promise.all(handles)) {
if (handle._context !== context2)
throw new JavaScriptErrorInEvaluate("JSHandles can be evaluated only in the context they were created!");
utilityScriptObjects.push(handle);
}
const utilityScriptValues = [options2.isFunction, options2.returnByValue, expression2, args.length, ...args];
const script = `(utilityScript, ...args) => utilityScript.evaluate(...args)`;
try {
return await context2._evaluateWithArguments(script, options2.returnByValue || false, utilityScriptValues, utilityScriptObjects);
} finally {
toDispose.map((handlePromise) => handlePromise.then((handle) => handle.dispose()));
}
}
function parseUnserializableValue(unserializableValue) {
if (unserializableValue === "NaN")
return NaN;
if (unserializableValue === "Infinity")
return Infinity;
if (unserializableValue === "-Infinity")
return -Infinity;
if (unserializableValue === "-0")
return -0;
}
function normalizeEvaluationExpression(expression2, isFunction2) {
expression2 = expression2.trim();
if (isFunction2) {
try {
new Function("(" + expression2 + ")");
} catch (e1) {
if (expression2.startsWith("async "))
expression2 = "async function " + expression2.substring("async ".length);
else
expression2 = "function " + expression2;
try {
new Function("(" + expression2 + ")");
} catch (e2) {
throw new Error("Passed function is not well-serializable!");
}
}
}
if (/^(async)?\s*function(\s|\()/.test(expression2))
expression2 = "(" + expression2 + ")";
return expression2;
}
function isJavaScriptErrorInEvaluate(error) {
return error instanceof JavaScriptErrorInEvaluate;
}
function sparseArrayToString(entries) {
const arrayEntries = [];
for (const { name, value: value2 } of entries) {
const index = +name;
if (isNaN(index) || index < 0)
continue;
arrayEntries.push({ index, value: value2 });
}
arrayEntries.sort((a, b) => a.index - b.index);
let lastIndex = -1;
const tokens = [];
for (const { index, value: value2 } of arrayEntries) {
const emptyItems = index - lastIndex - 1;
if (emptyItems === 1)
tokens.push(`empty`);
else if (emptyItems > 1)
tokens.push(`empty x ${emptyItems}`);
tokens.push(String(value2));
lastIndex = index;
}
return "[" + tokens.join(", ") + "]";
}
var ExecutionContext, JSHandle, JavaScriptErrorInEvaluate;
var init_javascript = __esm({
"packages/playwright-core/src/server/javascript.ts"() {
"use strict";
init_utilityScriptSerializers();
init_manualPromise();
init_debug();
init_instrumentation();
init_utilityScriptSource();
ExecutionContext = class extends SdkObject {
constructor(parent, delegate, worldNameForTest) {
super(parent, "execution-context");
this._contextDestroyedScope = new LongStandingScope();
this.worldNameForTest = worldNameForTest;
this.delegate = delegate;
}
contextDestroyed(reason) {
this._contextDestroyedScope.close(new Error(reason));
}
async raceAgainstContextDestroyed(promise) {
return this._contextDestroyedScope.race(promise);
}
rawEvaluateJSON(expression2) {
return this.raceAgainstContextDestroyed(this.delegate.rawEvaluateJSON(expression2));
}
rawEvaluateHandle(expression2) {
return this.raceAgainstContextDestroyed(this.delegate.rawEvaluateHandle(this, expression2));
}
async _evaluateWithArguments(expression2, returnByValue, values, handles) {
const utilityScript = await this._utilityScript();
return this.raceAgainstContextDestroyed(this.delegate.evaluateWithArguments(expression2, returnByValue, utilityScript, values, handles));
}
getProperties(object) {
return this.raceAgainstContextDestroyed(this.delegate.getProperties(object));
}
_releaseHandle(handle) {
return this.delegate.releaseHandle(handle);
}
adoptIfNeeded(handle) {
return null;
}
_utilityScript() {
if (!this._utilityScriptPromise) {
const source8 = `
(() => {
const module = {};
${source2}
return new (module.exports.UtilityScript())(globalThis, ${isUnderTest()});
})();`;
this._utilityScriptPromise = this.raceAgainstContextDestroyed(this.delegate.rawEvaluateHandle(this, source8)).then((handle) => {
handle._setPreview("UtilityScript");
return handle;
});
}
return this._utilityScriptPromise;
}
};
JSHandle = class extends SdkObject {
constructor(context2, type3, preview, objectId, value2) {
super(context2, "handle");
this.__jshandle = true;
this._disposed = false;
this._context = context2;
this._objectId = objectId;
this._value = value2;
this._objectType = type3;
this._preview = this._objectId ? preview || `JSHandle@${this._objectType}` : String(value2);
if (this._objectId && globalThis.leakedJSHandles)
globalThis.leakedJSHandles.set(this, new Error("Leaked JSHandle"));
}
async evaluateExpression(progress2, expression2, options2, arg) {
return await progress2.race(this.internalEvaluateExpression(expression2, options2, arg));
}
async evaluateExpressionHandle(progress2, expression2, options2, arg) {
return await progress2.race(this._evaluateExpressionHandle(expression2, options2, arg));
}
async getProperty(progress2, propertyName) {
return await progress2.race(this._getProperty(propertyName));
}
async getProperties(progress2) {
return await progress2.race(this.internalGetProperties());
}
async jsonValue(progress2) {
return await progress2.race(this._jsonValue());
}
async evaluate(pageFunction, arg) {
return evaluate(this._context, true, pageFunction, this, arg);
}
async evaluateHandle(pageFunction, arg) {
return evaluate(this._context, false, pageFunction, this, arg);
}
async internalEvaluateExpression(expression2, options2, arg) {
return await evaluateExpression(this._context, expression2, { ...options2, returnByValue: true }, this, arg);
}
async _evaluateExpressionHandle(expression2, options2, arg) {
return await evaluateExpression(this._context, expression2, { ...options2, returnByValue: false }, this, arg);
}
async _getProperty(propertyName) {
const objectHandle = await this.evaluateHandle((object, propertyName2) => {
const result3 = { __proto__: null };
result3[propertyName2] = object[propertyName2];
return result3;
}, propertyName);
const properties = await objectHandle.internalGetProperties();
const result2 = properties.get(propertyName);
objectHandle.dispose();
return result2;
}
async internalGetProperties() {
if (!this._objectId)
return /* @__PURE__ */ new Map();
return this._context.getProperties(this);
}
rawValue() {
return this._value;
}
async _jsonValue() {
if (!this._objectId)
return this._value;
const script = `(utilityScript, ...args) => utilityScript.jsonValue(...args)`;
return this._context._evaluateWithArguments(script, true, [true], [this]);
}
asElement() {
return null;
}
dispose() {
if (this._disposed)
return;
this._disposed = true;
if (this._objectId) {
this._context._releaseHandle(this).catch((e) => {
});
if (globalThis.leakedJSHandles)
globalThis.leakedJSHandles.delete(this);
}
}
toString() {
return this._preview;
}
_setPreviewCallback(callback) {
this._previewCallback = callback;
}
preview() {
return this._preview;
}
worldNameForTest() {
return this._context.worldNameForTest;
}
_setPreview(preview) {
this._preview = preview;
if (this._previewCallback)
this._previewCallback(preview);
}
};
JavaScriptErrorInEvaluate = class extends Error {
};
}
});
// packages/playwright-core/src/server/fileUploadUtils.ts
async function filesExceedUploadLimit(files) {
const sizes = await Promise.all(files.map(async (file) => (await import_fs12.default.promises.stat(file)).size));
return sizes.reduce((total, size) => total + size, 0) >= fileUploadSizeLimit;
}
async function prepareFilesForUpload(frame, params2) {
const { payloads, streams, directoryStream } = params2;
let { localPaths, localDirectory } = params2;
if ([payloads, localPaths, localDirectory, streams, directoryStream].filter(Boolean).length !== 1)
throw new Error("Exactly one of payloads, localPaths and streams must be provided");
if (streams)
localPaths = streams.map((c) => c.path());
if (directoryStream)
localDirectory = directoryStream.path();
if (localPaths) {
for (const p of localPaths)
assert(import_path12.default.isAbsolute(p) && import_path12.default.resolve(p) === p, "Paths provided to localPaths must be absolute and fully resolved.");
}
let fileBuffers = payloads;
if (!frame._page.browserContext._browser._isCollocatedWithServer) {
if (localPaths) {
if (await filesExceedUploadLimit(localPaths))
throw new Error("Cannot transfer files larger than 50Mb to a browser not co-located with the server");
fileBuffers = await Promise.all(localPaths.map(async (item) => {
return {
name: import_path12.default.basename(item),
buffer: await import_fs12.default.promises.readFile(item),
lastModifiedMs: (await import_fs12.default.promises.stat(item)).mtimeMs
};
}));
localPaths = void 0;
}
}
const filePayloads = fileBuffers?.map((payload) => ({
name: payload.name,
mimeType: payload.mimeType || mime3.getType(payload.name) || "application/octet-stream",
buffer: payload.buffer.toString("base64"),
lastModifiedMs: payload.lastModifiedMs
}));
return { localPaths, localDirectory, filePayloads };
}
var import_fs12, import_path12, mime3, fileUploadSizeLimit;
var init_fileUploadUtils = __esm({
"packages/playwright-core/src/server/fileUploadUtils.ts"() {
"use strict";
import_fs12 = __toESM(require("fs"));
import_path12 = __toESM(require("path"));
init_assert();
mime3 = require("./utilsBundle").mime;
fileUploadSizeLimit = 50 * 1024 * 1024;
}
});
// packages/playwright-core/src/generated/injectedScriptSource.ts
var source3;
var init_injectedScriptSource = __esm({
"packages/playwright-core/src/generated/injectedScriptSource.ts"() {
"use strict";
source3 = '\nvar __commonJS = obj => {\n let required = false;\n let result;\n return function __require() {\n if (!required) {\n required = true;\n let fn;\n for (const name in obj) { fn = obj[name]; break; }\n const module = { exports: {} };\n fn(module.exports, module);\n result = module.exports;\n }\n return result;\n }\n};\nvar __export = (target, all) => {for (var name in all) target[name] = all[name];};\nvar __toESM = mod => ({ ...mod, \'default\': mod });\nvar __toCommonJS = mod => ({ ...mod, __esModule: true });\n\n\n// packages/injected/src/injectedScript.ts\nvar injectedScript_exports = {};\n__export(injectedScript_exports, {\n InjectedScript: () => InjectedScript\n});\nmodule.exports = __toCommonJS(injectedScript_exports);\n\n// packages/isomorphic/ariaSnapshot.ts\nfunction ariaNodesEqual(a, b) {\n if (a.role !== b.role || a.name !== b.name)\n return false;\n if (!ariaPropsEqual(a, b) || hasPointerCursor(a) !== hasPointerCursor(b))\n return false;\n const aKeys = Object.keys(a.props);\n const bKeys = Object.keys(b.props);\n return aKeys.length === bKeys.length && aKeys.every((k) => a.props[k] === b.props[k]);\n}\nfunction hasPointerCursor(ariaNode) {\n return ariaNode.box.cursor === "pointer";\n}\nfunction ariaPropsEqual(a, b) {\n return a.active === b.active && a.checked === b.checked && a.disabled === b.disabled && a.expanded === b.expanded && a.selected === b.selected && a.level === b.level && a.pressed === b.pressed;\n}\nfunction parseAriaSnapshot(yaml, text, options = {}) {\n var _a;\n const lineCounter = new yaml.LineCounter();\n const parseOptions = {\n keepSourceTokens: true,\n lineCounter,\n ...options\n };\n const yamlDoc = yaml.parseDocument(text, parseOptions);\n const errors = [];\n const convertRange = (range) => {\n return [lineCounter.linePos(range[0]), lineCounter.linePos(range[1])];\n };\n const addError = (error) => {\n errors.push({\n message: error.message,\n range: [lineCounter.linePos(error.pos[0]), lineCounter.linePos(error.pos[1])]\n });\n };\n const convertSeq = (container, seq) => {\n for (const item of seq.items) {\n const itemIsString = item instanceof yaml.Scalar && typeof item.value === "string";\n if (itemIsString) {\n const childNode = KeyParser.parse(item, parseOptions, errors);\n if (childNode) {\n container.children = container.children || [];\n container.children.push(childNode);\n }\n continue;\n }\n const itemIsMap = item instanceof yaml.YAMLMap;\n if (itemIsMap) {\n convertMap(container, item);\n continue;\n }\n errors.push({\n message: "Sequence items should be strings or maps",\n range: convertRange(item.range || seq.range)\n });\n }\n };\n const convertMap = (container, map) => {\n var _a2;\n for (const entry of map.items) {\n container.children = container.children || [];\n const keyIsString = entry.key instanceof yaml.Scalar && typeof entry.key.value === "string";\n if (!keyIsString) {\n errors.push({\n message: "Only string keys are supported",\n range: convertRange(entry.key.range || map.range)\n });\n continue;\n }\n const key = entry.key;\n const value = entry.value;\n if (key.value === "text") {\n const valueIsString = value instanceof yaml.Scalar && typeof value.value === "string";\n if (!valueIsString) {\n errors.push({\n message: "Text value should be a string",\n range: convertRange(entry.value.range || map.range)\n });\n continue;\n }\n container.children.push({\n kind: "text",\n text: textValue(value.value)\n });\n continue;\n }\n if (key.value === "/children") {\n const valueIsString = value instanceof yaml.Scalar && typeof value.value === "string";\n if (!valueIsString || value.value !== "contain" && value.value !== "equal" && value.value !== "deep-equal") {\n errors.push({\n message: \'Strict value should be "contain", "equal" or "deep-equal"\',\n range: convertRange(entry.value.range || map.range)\n });\n continue;\n }\n container.containerMode = value.value;\n continue;\n }\n if (key.value.startsWith("/")) {\n const valueIsString = value instanceof yaml.Scalar && typeof value.value === "string";\n if (!valueIsString) {\n errors.push({\n message: "Property value should be a string",\n range: convertRange(entry.value.range || map.range)\n });\n continue;\n }\n container.props = (_a2 = container.props) != null ? _a2 : {};\n container.props[key.value.slice(1)] = textValue(value.value);\n continue;\n }\n const childNode = KeyParser.parse(key, parseOptions, errors);\n if (!childNode)\n continue;\n const valueIsScalar = value instanceof yaml.Scalar;\n if (valueIsScalar) {\n const type = typeof value.value;\n if (type !== "string" && type !== "number" && type !== "boolean") {\n errors.push({\n message: "Node value should be a string or a sequence",\n range: convertRange(entry.value.range || map.range)\n });\n continue;\n }\n container.children.push({\n ...childNode,\n children: [{\n kind: "text",\n text: textValue(String(value.value))\n }]\n });\n continue;\n }\n const valueIsSequence = value instanceof yaml.YAMLSeq;\n if (valueIsSequence) {\n container.children.push(childNode);\n convertSeq(childNode, value);\n continue;\n }\n errors.push({\n message: "Map values should be strings or sequences",\n range: convertRange(entry.value.range || map.range)\n });\n }\n };\n const fragment = { kind: "role", role: "fragment" };\n yamlDoc.errors.forEach(addError);\n if (errors.length)\n return { errors, fragment };\n if (!(yamlDoc.contents instanceof yaml.YAMLSeq)) {\n errors.push({\n message: \'Aria snapshot must be a YAML sequence, elements starting with " -"\',\n range: yamlDoc.contents ? convertRange(yamlDoc.contents.range) : [{ line: 0, col: 0 }, { line: 0, col: 0 }]\n });\n }\n if (errors.length)\n return { errors, fragment };\n convertSeq(fragment, yamlDoc.contents);\n if (errors.length)\n return { errors, fragment: emptyFragment };\n if (((_a = fragment.children) == null ? void 0 : _a.length) === 1 && (!fragment.containerMode || fragment.containerMode === "contain"))\n return { fragment: fragment.children[0], errors: [] };\n return { fragment, errors: [] };\n}\nvar emptyFragment = { kind: "role", role: "fragment" };\nfunction normalizeWhitespace(text) {\n return text.replace(/[\\u200b\\u00ad]/g, "").replace(/[\\r\\n\\s\\t]+/g, " ").trim();\n}\nfunction textValue(value) {\n return {\n raw: value,\n normalized: normalizeWhitespace(value)\n };\n}\nvar KeyParser = class _KeyParser {\n static parse(text, options, errors) {\n try {\n return new _KeyParser(text.value)._parse();\n } catch (e) {\n if (e instanceof ParserError) {\n const message = options.prettyErrors === false ? e.message : e.message + ":\\n\\n" + text.value + "\\n" + " ".repeat(e.pos) + "^\\n";\n errors.push({\n message,\n range: [options.lineCounter.linePos(text.range[0]), options.lineCounter.linePos(text.range[0] + e.pos)]\n });\n return null;\n }\n throw e;\n }\n }\n constructor(input) {\n this._input = input;\n this._pos = 0;\n this._length = input.length;\n }\n _peek() {\n return this._input[this._pos] || "";\n }\n _next() {\n if (this._pos < this._length)\n return this._input[this._pos++];\n return null;\n }\n _eof() {\n return this._pos >= this._length;\n }\n _isWhitespace() {\n return !this._eof() && /\\s/.test(this._peek());\n }\n _skipWhitespace() {\n while (this._isWhitespace())\n this._pos++;\n }\n _readIdentifier(type) {\n if (this._eof())\n this._throwError(`Unexpected end of input when expecting ${type}`);\n const start = this._pos;\n while (!this._eof() && /[a-zA-Z]/.test(this._peek()))\n this._pos++;\n return this._input.slice(start, this._pos);\n }\n _readString() {\n let result = "";\n let escaped = false;\n while (!this._eof()) {\n const ch = this._next();\n if (escaped) {\n result += ch;\n escaped = false;\n } else if (ch === "\\\\") {\n escaped = true;\n } else if (ch === \'"\') {\n return result;\n } else {\n result += ch;\n }\n }\n this._throwError("Unterminated string");\n }\n _throwError(message, offset = 0) {\n throw new ParserError(message, offset || this._pos);\n }\n _readRegex() {\n let result = "";\n let escaped = false;\n let insideClass = false;\n while (!this._eof()) {\n const ch = this._next();\n if (escaped) {\n result += ch;\n escaped = false;\n } else if (ch === "\\\\") {\n escaped = true;\n result += ch;\n } else if (ch === "/" && !insideClass) {\n return { pattern: result };\n } else if (ch === "[") {\n insideClass = true;\n result += ch;\n } else if (ch === "]" && insideClass) {\n result += ch;\n insideClass = false;\n } else {\n result += ch;\n }\n }\n this._throwError("Unterminated regex");\n }\n _readStringOrRegex() {\n const ch = this._peek();\n if (ch === \'"\') {\n this._next();\n return normalizeWhitespace(this._readString());\n }\n if (ch === "/") {\n this._next();\n return this._readRegex();\n }\n return null;\n }\n _readAttributes(result) {\n let errorPos = this._pos;\n while (true) {\n this._skipWhitespace();\n if (this._peek() === "[") {\n this._next();\n this._skipWhitespace();\n errorPos = this._pos;\n const flagName = this._readIdentifier("attribute");\n this._skipWhitespace();\n let flagValue = "";\n if (this._peek() === "=") {\n this._next();\n this._skipWhitespace();\n errorPos = this._pos;\n while (this._peek() !== "]" && !this._isWhitespace() && !this._eof())\n flagValue += this._next();\n }\n this._skipWhitespace();\n if (this._peek() !== "]")\n this._throwError("Expected ]");\n this._next();\n this._applyAttribute(result, flagName, flagValue || "true", errorPos);\n } else {\n break;\n }\n }\n }\n _parse() {\n this._skipWhitespace();\n const role = this._readIdentifier("role");\n this._skipWhitespace();\n const name = this._readStringOrRegex() || "";\n const result = { kind: "role", role, name };\n this._readAttributes(result);\n this._skipWhitespace();\n if (!this._eof())\n this._throwError("Unexpected input");\n return result;\n }\n _applyAttribute(node, key, value, errorPos) {\n if (key === "checked") {\n this._assert(value === "true" || value === "false" || value === "mixed", \'Value of "checked" attribute must be a boolean or "mixed"\', errorPos);\n node.checked = value === "true" ? true : value === "false" ? false : "mixed";\n return;\n }\n if (key === "disabled") {\n this._assert(value === "true" || value === "false", \'Value of "disabled" attribute must be a boolean\', errorPos);\n node.disabled = value === "true";\n return;\n }\n if (key === "expanded") {\n this._assert(value === "true" || value === "false", \'Value of "expanded" attribute must be a boolean\', errorPos);\n node.expanded = value === "true";\n return;\n }\n if (key === "active") {\n this._assert(value === "true" || value === "false", \'Value of "active" attribute must be a boolean\', errorPos);\n node.active = value === "true";\n return;\n }\n if (key === "level") {\n this._assert(!isNaN(Number(value)), \'Value of "level" attribute must be a number\', errorPos);\n node.level = Number(value);\n return;\n }\n if (key === "pressed") {\n this._assert(value === "true" || value === "false" || value === "mixed", \'Value of "pressed" attribute must be a boolean or "mixed"\', errorPos);\n node.pressed = value === "true" ? true : value === "false" ? false : "mixed";\n return;\n }\n if (key === "selected") {\n this._assert(value === "true" || value === "false", \'Value of "selected" attribute must be a boolean\', errorPos);\n node.selected = value === "true";\n return;\n }\n this._assert(false, `Unsupported attribute [${key}]`, errorPos);\n }\n _assert(value, message, valuePos) {\n if (!value)\n this._throwError(message || "Assertion error", valuePos);\n }\n};\nvar ParserError = class extends Error {\n constructor(message, pos) {\n super(message);\n this.pos = pos;\n }\n};\nfunction findNewNode(from, to) {\n var _a, _b;\n function fillMap(root, map, position) {\n let size = 1;\n let childPosition = position + size;\n for (const child of root.children || []) {\n if (typeof child === "string") {\n size++;\n childPosition++;\n } else {\n size += fillMap(child, map, childPosition);\n childPosition += size;\n }\n }\n if (!["none", "presentation", "fragment", "iframe", "generic"].includes(root.role) && root.name) {\n let byRole = map.get(root.role);\n if (!byRole) {\n byRole = /* @__PURE__ */ new Map();\n map.set(root.role, byRole);\n }\n const existing = byRole.get(root.name);\n const sizeAndPosition = size * 100 - position;\n if (!existing || existing.sizeAndPosition < sizeAndPosition)\n byRole.set(root.name, { node: root, sizeAndPosition });\n }\n return size;\n }\n const fromMap = /* @__PURE__ */ new Map();\n if (from)\n fillMap(from, fromMap, 0);\n const toMap = /* @__PURE__ */ new Map();\n fillMap(to, toMap, 0);\n const result = [];\n for (const [role, byRole] of toMap) {\n for (const [name, byName] of byRole) {\n const inFrom = (_a = fromMap.get(role)) == null ? void 0 : _a.get(name);\n if (!inFrom)\n result.push(byName);\n }\n }\n result.sort((a, b) => b.sizeAndPosition - a.sizeAndPosition);\n return (_b = result[0]) == null ? void 0 : _b.node;\n}\n\n// packages/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);\n const c2 = next(2);\n const 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/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 css selector "${selector}". Did you mean to CSS.escape it?`;\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 || // TODO: Consider using these for something, e.g. to escape complex strings.\n // For example :xpath{ (//div/bar[@attr="foo"])[2]/baz }\n // Or this way :xpath( {complex-xpath-goes-here("hello")} )\n token instanceof OpenCurlyToken || token instanceof CloseCurlyToken || // TODO: Consider treating these as strings?\n token instanceof URLToken || token instanceof PercentageToken;\n });\n if (unsupportedToken)\n throw new InvalidSelectorError(`Unsupported token "${unsupportedToken.toSource()}" while parsing css selector "${selector}". Did you mean to CSS.escape it?`);\n let pos = 0;\n const names = /* @__PURE__ */ new Set();\n function unexpected() {\n return new InvalidSelectorError(`Unexpected token "${tokens[pos].toSource()}" while parsing css selector "${selector}". Did you mean to CSS.escape it?`);\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 isOpenParen(p = pos) {\n return tokens[p] instanceof OpenParenToken;\n }\n function isCloseParen(p = pos) {\n return tokens[p] instanceof CloseParenToken;\n }\n function isFunction(p = pos) {\n return tokens[p] instanceof FunctionToken;\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 (isFunction()) {\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 let balance = 1;\n while (!isEOF()) {\n if (isOpenParen() || isFunction())\n balance++;\n if (isCloseParen())\n balance--;\n if (!balance)\n break;\n s += tokens[pos++].toSource();\n }\n return s;\n }\n const result = consumeFunctionArguments();\n if (!isEOF())\n throw unexpected();\n if (result.some((arg) => typeof arg !== "object" || !("simples" in arg)))\n throw new InvalidSelectorError(`Error while parsing css selector "${selector}". Did you mean to CSS.escape it?`);\n return { selector: result, names: Array.from(names) };\n}\n\n// packages/isomorphic/selectorParser.ts\nvar kNestedSelectorNames = /* @__PURE__ */ new Set(["internal:has", "internal:has-not", "internal:and", "internal:or", "internal:chain", "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, forceEngineName) {\n if (typeof selector === "string")\n return selector;\n return selector.parts.map((p, i) => {\n let includeEngine = true;\n if (!forceEngineName && i !== selector.capture) {\n if (p.name === "css")\n includeEngine = false;\n else if (p.name === "xpath" && p.source.startsWith("//") || p.source.startsWith(".."))\n includeEngine = false;\n }\n const prefix = includeEngine ? 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 expression");\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/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 quoteCSSAttributeValue(text) {\n return `"${text.replace(/["\\\\]/g, (char) => "\\\\" + char)}"`;\n}\nvar normalizedWhitespaceCache;\nfunction cacheNormalizedWhitespaces() {\n normalizedWhitespaceCache = /* @__PURE__ */ new Map();\n}\nfunction normalizeWhiteSpace(text) {\n let result = normalizedWhitespaceCache == null ? void 0 : normalizedWhitespaceCache.get(text);\n if (result === void 0) {\n result = text.replace(/[\\u200b\\u00ad]/g, "").trim().replace(/\\s+/g, " ");\n normalizedWhitespaceCache == null ? void 0 : normalizedWhitespaceCache.set(text, result);\n }\n return result;\n}\nfunction normalizeEscapedRegexQuotes(source) {\n return source.replace(/(^|[^\\\\])(\\\\\\\\)*\\\\([\'"`])/g, "$1$2$3");\n}\nfunction escapeRegexForSelector(re) {\n if (re.unicode || re.unicodeSets)\n return String(re);\n return String(re).replace(/(^|[^\\\\])(\\\\\\\\)*(["\'`])/g, "$1$2\\\\$3").replace(/>>/g, "\\\\>\\\\>");\n}\nfunction escapeForTextSelector(text, exact) {\n if (typeof text !== "string")\n return escapeRegexForSelector(text);\n return `${JSON.stringify(text)}${exact ? "s" : "i"}`;\n}\nfunction escapeForAttributeSelector(value, exact) {\n if (typeof value !== "string")\n return escapeRegexForSelector(value);\n return `"${value.replace(/\\\\/g, "\\\\\\\\").replace(/["]/g, \'\\\\"\')}"${exact ? "s" : "i"}`;\n}\nfunction trimString(input, cap, suffix = "") {\n if (input.length <= cap)\n return input;\n const chars = [...input];\n if (chars.length > cap)\n return chars.slice(0, cap - suffix.length).join("") + suffix;\n return chars.join("");\n}\nfunction trimStringWithEllipsis(input, cap) {\n return trimString(input, cap, "\\u2026");\n}\nfunction escapeRegExp(s) {\n return s.replace(/[.*+?^${}()|[\\]\\\\]/g, "\\\\$&");\n}\nfunction longestCommonSubstring(s1, s2) {\n const n = s1.length;\n const m = s2.length;\n let maxLen = 0;\n let endingIndex = 0;\n const dp = Array(n + 1).fill(null).map(() => Array(m + 1).fill(0));\n for (let i = 1; i <= n; i++) {\n for (let j = 1; j <= m; j++) {\n if (s1[i - 1] === s2[j - 1]) {\n dp[i][j] = dp[i - 1][j - 1] + 1;\n if (dp[i][j] > maxLen) {\n maxLen = dp[i][j];\n endingIndex = i;\n }\n }\n }\n }\n return s1.slice(endingIndex - maxLen, endingIndex);\n}\nvar ansiRegex = new RegExp("([\\\\u001B\\\\u009B][[\\\\]()#;?]*(?:(?:(?:[a-zA-Z\\\\d]*(?:;[-a-zA-Z\\\\d\\\\/#&.:=?%@~_]*)*)?\\\\u0007)|(?:(?:\\\\d{1,4}(?:;\\\\d{0,4})*)?[\\\\dA-PR-TZcf-ntqry=><~])))", "g");\n\n// packages/isomorphic/locatorGenerators.ts\nfunction asLocator(lang, selector, isFrameLocator = false) {\n return asLocators(lang, selector, isFrameLocator, 1)[0];\n}\nfunction asLocators(lang, selector, isFrameLocator = false, maxOutputSize = 20, preferredQuote) {\n try {\n return innerAsLocators(new generators[lang](preferredQuote), parseSelector(selector), isFrameLocator, maxOutputSize);\n } catch (e) {\n return [selector];\n }\n}\nfunction innerAsLocators(factory, parsed, isFrameLocator = false, maxOutputSize = 20) {\n const parts = [...parsed.parts];\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 === "internal:describe")\n continue;\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 === "visible") {\n tokens.push([factory.generateLocator(base, "visible", part.body), factory.generateLocator(base, "default", `visible=${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:chain") {\n const inners = innerAsLocators(factory, part.body.parsed, false, maxOutputSize);\n tokens.push(inners.map((inner) => factory.generateLocator(base, "chain", 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 if (attr.name === "description") {\n options.exact = attr.caseSensitive;\n options.description = 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 if (part.name === "internal:control" && part.body === "enter-frame") {\n const lastTokens = tokens[tokens.length - 1];\n const lastPart = parts[index - 1];\n const transformed = lastTokens.map((token) => factory.chainLocators([token, factory.generateLocator(base, "frame", "")]));\n if (["xpath", "css"].includes(lastPart.name)) {\n transformed.push(\n factory.generateLocator(base, "frame-locator", stringifySelector({ parts: [lastPart] })),\n factory.generateLocator(base, "frame-locator", stringifySelector({ parts: [lastPart] }, true))\n );\n }\n lastTokens.splice(0, lastTokens.length, ...transformed);\n nextBase = "frame-locator";\n continue;\n }\n const nextPart = parts[index + 1];\n const selectorPart = stringifySelector({ parts: [part] });\n const locatorPart = factory.generateLocator(base, "default", selectorPart);\n if (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 let locatorPartWithEngine;\n if (["xpath", "css"].includes(part.name)) {\n const selectorPart2 = stringifySelector(\n { parts: [part] },\n /* forceEngineName */\n true\n );\n locatorPartWithEngine = factory.generateLocator(base, "default", selectorPart2);\n }\n tokens.push([locatorPart, locatorPartWithEngine].filter(Boolean));\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 result.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 constructor(preferredQuote) {\n this.preferredQuote = preferredQuote;\n }\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-locator":\n return `frameLocator(${this.quote(body)})`;\n case "frame":\n return `contentFrame()`;\n case "nth":\n return `nth(${body})`;\n case "first":\n return `first()`;\n case "last":\n return `last()`;\n case "visible":\n return `filter({ visible: ${body === "true" ? "true" : "false"} })`;\n case "role":\n const attrs = [];\n if (isRegExp(options.name))\n attrs.push(`name: ${this.regexToSourceString(options.name)}`);\n else if (typeof options.name === "string")\n attrs.push(`name: ${this.quote(options.name)}`);\n if (isRegExp(options.description))\n attrs.push(`description: ${this.regexToSourceString(options.description)}`);\n else if (typeof options.description === "string")\n attrs.push(`description: ${this.quote(options.description)}`);\n if (options.exact && (typeof options.name === "string" || typeof options.description === "string"))\n attrs.push(`exact: true`);\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 "chain":\n return `locator(${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 regexToSourceString(re) {\n return normalizeEscapedRegexQuotes(String(re));\n }\n toCallWithExact(method, body, exact) {\n if (isRegExp(body))\n return `${method}(${this.regexToSourceString(body)})`;\n return exact ? `${method}(${this.quote(body)}, { exact: true })` : `${method}(${this.quote(body)})`;\n }\n toHasText(body) {\n if (isRegExp(body))\n return this.regexToSourceString(body);\n return this.quote(body);\n }\n toTestIdValue(value) {\n if (isRegExp(value))\n return this.regexToSourceString(value);\n return this.quote(value);\n }\n quote(text) {\n var _a;\n return escapeWithQuotes(text, (_a = this.preferredQuote) != null ? _a : "\'");\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-locator":\n return `frame_locator(${this.quote(body)})`;\n case "frame":\n return `content_frame`;\n case "nth":\n return `nth(${body})`;\n case "first":\n return `first`;\n case "last":\n return `last`;\n case "visible":\n return `filter(visible=${body === "true" ? "True" : "False"})`;\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 (isRegExp(options.description))\n attrs.push(`description=${this.regexToString(options.description)}`);\n else if (typeof options.description === "string")\n attrs.push(`description=${this.quote(options.description)}`);\n if (options.exact && (typeof options.name === "string" || typeof options.description === "string"))\n attrs.push(`exact=True`);\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 "chain":\n return `locator(${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"${normalizeEscapedRegexQuotes(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-locator":\n return `frameLocator(${this.quote(body)})`;\n case "frame":\n return `contentFrame()`;\n case "nth":\n return `nth(${body})`;\n case "first":\n return `first()`;\n case "last":\n return `last()`;\n case "visible":\n return `filter(new ${clazz}.FilterOptions().setVisible(${body === "true" ? "true" : "false"}))`;\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 (isRegExp(options.description))\n attrs.push(`.setDescription(${this.regexToString(options.description)})`);\n else if (typeof options.description === "string")\n attrs.push(`.setDescription(${this.quote(options.description)})`);\n if (options.exact && (typeof options.name === "string" || typeof options.description === "string"))\n attrs.push(`.setExact(true)`);\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 "chain":\n return `locator(${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(normalizeEscapedRegexQuotes(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-locator":\n return `FrameLocator(${this.quote(body)})`;\n case "frame":\n return `ContentFrame`;\n case "nth":\n return `Nth(${body})`;\n case "first":\n return `First`;\n case "last":\n return `Last`;\n case "visible":\n return `Filter(new() { Visible = ${body === "true" ? "true" : "false"} })`;\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 (isRegExp(options.description))\n attrs.push(`DescriptionRegex = ${this.regexToString(options.description)}`);\n else if (typeof options.description === "string")\n attrs.push(`Description = ${this.quote(options.description)}`);\n if (options.exact && (typeof options.name === "string" || typeof options.description === "string"))\n attrs.push(`Exact = true`);\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 "chain":\n return `Locator(${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(normalizeEscapedRegexQuotes(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: JavaScriptLocatorFactory,\n python: PythonLocatorFactory,\n java: JavaLocatorFactory,\n csharp: CSharpLocatorFactory,\n jsonl: JsonlLocatorFactory\n};\nfunction isRegExp(obj) {\n return obj instanceof RegExp;\n}\n\n// packages/isomorphic/yaml.ts\nfunction yamlEscapeKeyIfNeeded(str) {\n if (!yamlStringNeedsQuotes(str))\n return str;\n return `\'` + str.replace(/\'/g, `\'\'`) + `\'`;\n}\nfunction yamlEscapeValueIfNeeded(str) {\n if (!yamlStringNeedsQuotes(str))\n return str;\n return \'"\' + str.replace(/[\\\\"\\x00-\\x1f\\x7f-\\x9f]/g, (c) => {\n switch (c) {\n case "\\\\":\n return "\\\\\\\\";\n case \'"\':\n return \'\\\\"\';\n case "\\b":\n return "\\\\b";\n case "\\f":\n return "\\\\f";\n case "\\n":\n return "\\\\n";\n case "\\r":\n return "\\\\r";\n case " ":\n return "\\\\t";\n default:\n const code = c.charCodeAt(0);\n return "\\\\x" + code.toString(16).padStart(2, "0");\n }\n }) + \'"\';\n}\nfunction yamlStringNeedsQuotes(str) {\n if (str.length === 0)\n return true;\n if (/^\\s|\\s$/.test(str))\n return true;\n if (/[\\x00-\\x08\\x0b\\x0c\\x0e-\\x1f\\x7f-\\x9f]/.test(str))\n return true;\n if (/^-/.test(str))\n return true;\n if (/[\\n:](\\s|$)/.test(str))\n return true;\n if (/\\s#/.test(str))\n return true;\n if (/[\\n\\r]/.test(str))\n return true;\n if (/^[&*\\],?!>|@"\'#%]/.test(str))\n return true;\n if (/[{}`]/.test(str))\n return true;\n if (/^\\[/.test(str))\n return true;\n if (!isNaN(Number(str)) || ["y", "n", "yes", "no", "true", "false", "on", "off", "null"].includes(str.toLowerCase()))\n return true;\n return false;\n}\n\n// packages/injected/src/domUtils.ts\nvar globalOptions = {};\nfunction setGlobalOptions(options) {\n globalOptions = options;\n}\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 const cache = pseudo === "::before" ? cacheStyleBefore : pseudo === "::after" ? cacheStyleAfter : cacheStyle;\n if (cache && cache.has(element))\n return cache.get(element);\n const style = element.ownerDocument && element.ownerDocument.defaultView ? element.ownerDocument.defaultView.getComputedStyle(element, pseudo) : void 0;\n cache == null ? void 0 : cache.set(element, style);\n return style;\n}\nfunction isElementStyleVisibilityVisible(element, style) {\n style = style != null ? style : getElementComputedStyle(element);\n if (!style)\n return true;\n if (Element.prototype.checkVisibility && globalOptions.browserNameForWorkarounds !== "webkit") {\n if (!element.checkVisibility())\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 computeBox(element) {\n const style = getElementComputedStyle(element);\n if (!style)\n return { visible: true, inline: false };\n const cursor = style.cursor;\n if (style.display === "contents") {\n for (let child = element.firstChild; child; child = child.nextSibling) {\n if (child.nodeType === 1 && isElementVisible(child))\n return { visible: true, inline: false, cursor };\n if (child.nodeType === 3 && isVisibleTextNode(child))\n return { visible: true, inline: true, cursor };\n }\n return { visible: false, inline: false, cursor };\n }\n if (!isElementStyleVisibilityVisible(element, style))\n return { cursor, visible: false, inline: false };\n const rect = element.getBoundingClientRect();\n return { cursor, visible: rect.width > 0 && rect.height > 0, inline: style.display === "inline" };\n}\nfunction isElementVisible(element) {\n return computeBox(element).visible;\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}\nfunction elementSafeTagName(element) {\n const tagName = element.tagName;\n if (typeof tagName === "string")\n return tagName.toUpperCase();\n if (element instanceof HTMLFormElement)\n return "FORM";\n return element.tagName.toUpperCase();\n}\nvar cacheStyle;\nvar cacheStyleBefore;\nvar cacheStyleAfter;\nvar cachesCounter = 0;\nfunction beginDOMCaches() {\n ++cachesCounter;\n cacheStyle != null ? cacheStyle : cacheStyle = /* @__PURE__ */ new Map();\n cacheStyleBefore != null ? cacheStyleBefore : cacheStyleBefore = /* @__PURE__ */ new Map();\n cacheStyleAfter != null ? cacheStyleAfter : cacheStyleAfter = /* @__PURE__ */ new Map();\n}\nfunction endDOMCaches() {\n if (!--cachesCounter) {\n cacheStyle = void 0;\n cacheStyleBefore = void 0;\n cacheStyleAfter = void 0;\n }\n}\n\n// packages/injected/src/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", void 0],\n ["aria-busy", void 0],\n ["aria-controls", void 0],\n ["aria-current", void 0],\n ["aria-describedby", void 0],\n ["aria-details", void 0],\n // Global use deprecated in ARIA 1.2\n // [\'aria-disabled\', undefined],\n ["aria-dropeffect", void 0],\n // Global use deprecated in ARIA 1.2\n // [\'aria-errormessage\', undefined],\n ["aria-flowto", void 0],\n ["aria-grabbed", void 0],\n // Global use deprecated in ARIA 1.2\n // [\'aria-haspopup\', undefined],\n ["aria-hidden", void 0],\n // Global use deprecated in ARIA 1.2\n // [\'aria-invalid\', undefined],\n ["aria-keyshortcuts", void 0],\n ["aria-label", ["caption", "code", "deletion", "emphasis", "generic", "insertion", "paragraph", "presentation", "strong", "subscript", "superscript"]],\n ["aria-labelledby", ["caption", "code", "deletion", "emphasis", "generic", "insertion", "paragraph", "presentation", "strong", "subscript", "superscript"]],\n ["aria-live", void 0],\n ["aria-owns", void 0],\n ["aria-relevant", void 0],\n ["aria-roledescription", ["generic"]]\n];\nfunction hasGlobalAriaAttribute(element, forRole) {\n return kGlobalAriaAttributes.some(([attr, prohibited]) => {\n return !(prohibited == null ? void 0 : prohibited.includes(forRole || "")) && element.hasAttribute(attr);\n });\n}\nfunction hasTabIndex(element) {\n return !Number.isNaN(Number(String(element.getAttribute("tabindex"))));\n}\nfunction isFocusable(element) {\n return !isNativelyDisabled(element) && (isNativelyFocusable(element) || hasTabIndex(element));\n}\nfunction isNativelyFocusable(element) {\n const tagName = elementSafeTagName(element);\n if (["BUTTON", "DETAILS", "SELECT", "TEXTAREA"].includes(tagName))\n return true;\n if (tagName === "A" || tagName === "AREA")\n return element.hasAttribute("href");\n if (tagName === "INPUT")\n return !element.hidden;\n return false;\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") === "" && !e.getAttribute("title") && !hasGlobalAriaAttribute(e) && !hasTabIndex(e) ? "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 && elementSafeTagName(list) === "DATALIST" ? "combobox" : "textbox";\n }\n if (type === "hidden")\n return null;\n if (type === "file")\n return "button";\n return inputTypeToRole[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 "SEARCH": () => "search",\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 // For <svg> we default to Chrome behavior:\n // - Chrome reports \'img\'.\n // - Firefox reports \'diagram\' that is not in official ARIA spec yet.\n // - Safari reports \'no role\', but still computes accessible name.\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 const scope = e.getAttribute("scope");\n if (scope === "col" || scope === "colgroup")\n return "columnheader";\n if (scope === "row" || scope === "rowgroup")\n return "rowheader";\n const nextSibling = e.nextElementSibling;\n const prevSibling = e.previousElementSibling;\n const row = !!e.parentElement && elementSafeTagName(e.parentElement) === "TR" ? e.parentElement : void 0;\n if (!nextSibling && !prevSibling) {\n if (row) {\n const table = closestCrossShadow(row, "table");\n if (table && table.rows.length <= 1)\n return null;\n }\n return "columnheader";\n }\n if (isHeaderCell(nextSibling) && isHeaderCell(prevSibling))\n return "columnheader";\n if (isNonEmptyDataCell(nextSibling) || isNonEmptyDataCell(prevSibling))\n return "rowheader";\n return "columnheader";\n },\n "THEAD": () => "rowgroup",\n "TIME": () => "time",\n "TR": () => "row",\n "UL": () => "list"\n};\nfunction isHeaderCell(element) {\n return !!element && elementSafeTagName(element) === "TH";\n}\nfunction isNonEmptyDataCell(element) {\n var _a;\n if (!element || elementSafeTagName(element) !== "TD")\n return false;\n return !!(((_a = element.textContent) == null ? void 0 : _a.trim()) || element.children.length > 0);\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[elementSafeTagName(element)]) == 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[elementSafeTagName(ancestor)];\n if (!parents || !parent || !parents.includes(elementSafeTagName(parent)))\n break;\n const parentExplicitRole = getExplicitAriaRole(parent);\n if ((parentExplicitRole === "none" || parentExplicitRole === "presentation") && !hasPresentationConflictResolution(parent, parentExplicitRole))\n return parentExplicitRole;\n ancestor = parent;\n }\n return implicitRole;\n}\nvar validRoles = [\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 "complementary",\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 "insertion",\n "link",\n "list",\n "listbox",\n "listitem",\n "log",\n "main",\n "mark",\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 "region",\n "row",\n "rowgroup",\n "rowheader",\n "scrollbar",\n "search",\n "searchbox",\n "separator",\n "slider",\n "spinbutton",\n "status",\n "strong",\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];\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, role) {\n return hasGlobalAriaAttribute(element, role) || isFocusable(element);\n}\nfunction getAriaRole(element) {\n const explicitRole = getExplicitAriaRole(element);\n if (!explicitRole)\n return getImplicitAriaRole(element);\n if (explicitRole === "none" || explicitRole === "presentation") {\n const implicitRole = getImplicitAriaRole(element);\n if (hasPresentationConflictResolution(element, implicitRole))\n return implicitRole;\n }\n return explicitRole;\n}\nfunction getAriaBoolean(attr) {\n return attr === null ? void 0 : attr.toLowerCase() === "true";\n}\nfunction isElementIgnoredForAria(element) {\n return ["STYLE", "SCRIPT", "NOSCRIPT", "TEMPLATE"].includes(elementSafeTagName(element));\n}\nfunction isElementHiddenForAria(element) {\n if (isElementIgnoredForAria(element))\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 result = [];\n for (const id of ids) {\n const firstElement = root.querySelector("#" + CSS.escape(id));\n if (firstElement && !result.includes(firstElement))\n result.push(firstElement);\n }\n return result;\n } catch (e) {\n return [];\n }\n}\nfunction trimFlatString(s) {\n return s.trim();\n}\nfunction asFlatString(s) {\n return s.split("\\xA0").map((chunk) => chunk.replace(/\\r\\n/g, "\\n").replace(/[\\u200b\\u00ad]/g, "").replace(/\\s\\s*/g, " ")).join("\\xA0").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 getCSSContent(element, pseudo) {\n const cache = pseudo === "::before" ? cachePseudoContentBefore : pseudo === "::after" ? cachePseudoContentAfter : cachePseudoContent;\n if (cache == null ? void 0 : cache.has(element))\n return cache == null ? void 0 : cache.get(element);\n const style = getElementComputedStyle(element, pseudo);\n let content;\n if (style) {\n const contentValue = style.content;\n if (contentValue && contentValue !== "none" && contentValue !== "normal") {\n if (style.display !== "none" && style.visibility !== "hidden") {\n content = parseCSSContentPropertyAsString(element, contentValue, !!pseudo);\n }\n }\n }\n if (pseudo && content !== void 0) {\n const display = (style == null ? void 0 : style.display) || "inline";\n if (display !== "inline")\n content = " " + content + " ";\n }\n if (cache)\n cache.set(element, content);\n return content;\n}\nfunction parseCSSContentPropertyAsString(element, content, isPseudo) {\n if (!content || content === "none" || content === "normal") {\n return;\n }\n try {\n let tokens = tokenize(content).filter((token) => !(token instanceof WhitespaceToken));\n const delimIndex = tokens.findIndex((token) => token instanceof DelimToken && token.value === "/");\n if (delimIndex !== -1) {\n tokens = tokens.slice(delimIndex + 1);\n } else if (!isPseudo) {\n return;\n }\n const accumulated = [];\n let index = 0;\n while (index < tokens.length) {\n if (tokens[index] instanceof StringToken) {\n accumulated.push(tokens[index].value);\n index++;\n } else if (index + 2 < tokens.length && tokens[index] instanceof FunctionToken && tokens[index].value === "attr" && tokens[index + 1] instanceof IdentToken && tokens[index + 2] instanceof CloseParenToken) {\n const attrName = tokens[index + 1].value;\n accumulated.push(element.getAttribute(attrName) || "");\n index += 3;\n } else {\n return;\n }\n }\n return accumulated.join("");\n } catch {\n }\n}\nfunction getAriaLabelledByElements(element) {\n const ref = element.getAttribute("aria-labelledby");\n if (ref === null)\n return null;\n const refs = getIdRefs(element, ref);\n return refs.length ? refs : null;\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 = asFlatString(getTextAlternativeInternal(element, {\n includeHidden,\n visitedElements: /* @__PURE__ */ new Set(),\n embeddedInTargetElement: "self"\n }));\n }\n cache == null ? void 0 : cache.set(element, accessibleName);\n }\n return accessibleName;\n}\nfunction getElementAccessibleDescription(element, includeHidden) {\n const cache = includeHidden ? cacheAccessibleDescriptionHidden : cacheAccessibleDescription;\n let accessibleDescription = cache == null ? void 0 : cache.get(element);\n if (accessibleDescription === void 0) {\n accessibleDescription = "";\n if (element.hasAttribute("aria-describedby")) {\n const describedBy = getIdRefs(element, element.getAttribute("aria-describedby"));\n accessibleDescription = asFlatString(describedBy.map((ref) => getTextAlternativeInternal(ref, {\n includeHidden,\n visitedElements: /* @__PURE__ */ new Set(),\n embeddedInDescribedBy: { element: ref, hidden: isElementHiddenForAria(ref) }\n })).join(" "));\n } else if (element.hasAttribute("aria-description")) {\n accessibleDescription = asFlatString(element.getAttribute("aria-description") || "");\n } else {\n accessibleDescription = asFlatString(element.getAttribute("title") || "");\n }\n cache == null ? void 0 : cache.set(element, accessibleDescription);\n }\n return accessibleDescription;\n}\nfunction getAriaInvalid(element) {\n const ariaInvalid = element.getAttribute("aria-invalid");\n if (!ariaInvalid || ariaInvalid.trim() === "" || ariaInvalid.toLocaleLowerCase() === "false")\n return "false";\n if (ariaInvalid === "true" || ariaInvalid === "grammar" || ariaInvalid === "spelling")\n return ariaInvalid;\n return "true";\n}\nfunction getValidityInvalid(element) {\n if ("validity" in element) {\n const validity = element.validity;\n return (validity == null ? void 0 : validity.valid) === false;\n }\n return false;\n}\nfunction getElementAccessibleErrorMessage(element) {\n const cache = cacheAccessibleErrorMessage;\n let accessibleErrorMessage = cacheAccessibleErrorMessage == null ? void 0 : cacheAccessibleErrorMessage.get(element);\n if (accessibleErrorMessage === void 0) {\n accessibleErrorMessage = "";\n const isAriaInvalid = getAriaInvalid(element) !== "false";\n const isValidityInvalid = getValidityInvalid(element);\n if (isAriaInvalid || isValidityInvalid) {\n const errorMessageId = element.getAttribute("aria-errormessage");\n const errorMessages = getIdRefs(element, errorMessageId);\n const parts = errorMessages.map((errorMessage) => asFlatString(\n getTextAlternativeInternal(errorMessage, {\n visitedElements: /* @__PURE__ */ new Set(),\n embeddedInDescribedBy: { element: errorMessage, hidden: isElementHiddenForAria(errorMessage) }\n })\n ));\n accessibleErrorMessage = parts.join(" ").trim();\n }\n cache == null ? void 0 : cache.set(element, accessibleErrorMessage);\n }\n return accessibleErrorMessage;\n}\nfunction getTextAlternativeInternal(element, options) {\n var _a, _b, _c, _d;\n if (options.visitedElements.has(element))\n return "";\n const childOptions = {\n ...options,\n embeddedInTargetElement: options.embeddedInTargetElement === "self" ? "descendant" : options.embeddedInTargetElement\n };\n if (!options.includeHidden) {\n const isEmbeddedInHiddenReferenceTraversal = !!((_a = options.embeddedInLabelledBy) == null ? void 0 : _a.hidden) || !!((_b = options.embeddedInDescribedBy) == null ? void 0 : _b.hidden) || !!((_c = options.embeddedInNativeTextAlternative) == null ? void 0 : _c.hidden) || !!((_d = options.embeddedInLabel) == null ? void 0 : _d.hidden);\n if (isElementIgnoredForAria(element) || !isEmbeddedInHiddenReferenceTraversal && isElementHiddenForAria(element)) {\n options.visitedElements.add(element);\n return "";\n }\n }\n const labelledBy = getAriaLabelledByElements(element);\n if (!options.embeddedInLabelledBy) {\n const accessibleName = (labelledBy || []).map((ref) => getTextAlternativeInternal(ref, {\n ...options,\n embeddedInLabelledBy: { element: ref, hidden: isElementHiddenForAria(ref) },\n embeddedInDescribedBy: void 0,\n embeddedInTargetElement: void 0,\n embeddedInLabel: void 0,\n embeddedInNativeTextAlternative: void 0\n })).join(" ");\n if (accessibleName)\n return accessibleName;\n }\n const role = getAriaRole(element) || "";\n const tagName = elementSafeTagName(element);\n if (!!options.embeddedInLabel || !!options.embeddedInLabelledBy || options.embeddedInTargetElement === "descendant") {\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 (tagName === "INPUT" || 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 (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 if (!selectedOptions.length && tagName === "INPUT") {\n return element.value;\n }\n return selectedOptions.map((option) => getTextAlternativeInternal(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 (trimFlatString(ariaLabel)) {\n options.visitedElements.add(element);\n return ariaLabel;\n }\n if (!["presentation", "none"].includes(role)) {\n if (tagName === "INPUT" && ["button", "submit", "reset"].includes(element.type)) {\n options.visitedElements.add(element);\n const value = element.value || "";\n if (trimFlatString(value))\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 (tagName === "INPUT" && element.type === "file") {\n options.visitedElements.add(element);\n const labels = element.labels || [];\n if (labels.length && !options.embeddedInLabelledBy)\n return getAccessibleNameFromAssociatedLabels(labels, options);\n return "Choose File";\n }\n if (tagName === "INPUT" && element.type === "image") {\n options.visitedElements.add(element);\n const labels = element.labels || [];\n if (labels.length && !options.embeddedInLabelledBy)\n return getAccessibleNameFromAssociatedLabels(labels, options);\n const alt = element.getAttribute("alt") || "";\n if (trimFlatString(alt))\n return alt;\n const title = element.getAttribute("title") || "";\n if (trimFlatString(title))\n return title;\n return "Submit";\n }\n if (!labelledBy && tagName === "BUTTON") {\n options.visitedElements.add(element);\n const labels = element.labels || [];\n if (labels.length)\n return getAccessibleNameFromAssociatedLabels(labels, options);\n }\n if (!labelledBy && tagName === "OUTPUT") {\n options.visitedElements.add(element);\n const labels = element.labels || [];\n if (labels.length)\n return getAccessibleNameFromAssociatedLabels(labels, options);\n return element.getAttribute("title") || "";\n }\n if (!labelledBy && (tagName === "TEXTAREA" || tagName === "SELECT" || tagName === "INPUT")) {\n options.visitedElements.add(element);\n const labels = element.labels || [];\n if (labels.length)\n return getAccessibleNameFromAssociatedLabels(labels, options);\n const usePlaceholder = tagName === "INPUT" && ["text", "password", "search", "tel", "email", "url"].includes(element.type) || 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 && tagName === "FIELDSET") {\n options.visitedElements.add(element);\n for (let child = element.firstElementChild; child; child = child.nextElementSibling) {\n if (elementSafeTagName(child) === "LEGEND") {\n return getTextAlternativeInternal(child, {\n ...childOptions,\n embeddedInNativeTextAlternative: { element: child, hidden: isElementHiddenForAria(child) }\n });\n }\n }\n const title = element.getAttribute("title") || "";\n return title;\n }\n if (!labelledBy && tagName === "FIGURE") {\n options.visitedElements.add(element);\n for (let child = element.firstElementChild; child; child = child.nextElementSibling) {\n if (elementSafeTagName(child) === "FIGCAPTION") {\n return getTextAlternativeInternal(child, {\n ...childOptions,\n embeddedInNativeTextAlternative: { element: child, hidden: isElementHiddenForAria(child) }\n });\n }\n }\n const title = element.getAttribute("title") || "";\n return title;\n }\n if (tagName === "IMG") {\n options.visitedElements.add(element);\n const alt = element.getAttribute("alt") || "";\n if (trimFlatString(alt))\n return alt;\n const title = element.getAttribute("title") || "";\n return title;\n }\n if (tagName === "TABLE") {\n options.visitedElements.add(element);\n for (let child = element.firstElementChild; child; child = child.nextElementSibling) {\n if (elementSafeTagName(child) === "CAPTION") {\n return getTextAlternativeInternal(child, {\n ...childOptions,\n embeddedInNativeTextAlternative: { element: child, hidden: isElementHiddenForAria(child) }\n });\n }\n }\n const summary = element.getAttribute("summary") || "";\n if (summary)\n return summary;\n }\n if (tagName === "AREA") {\n options.visitedElements.add(element);\n const alt = element.getAttribute("alt") || "";\n if (trimFlatString(alt))\n return alt;\n const title = element.getAttribute("title") || "";\n return title;\n }\n if (tagName === "SVG" || element.ownerSVGElement) {\n options.visitedElements.add(element);\n for (let child = element.firstElementChild; child; child = child.nextElementSibling) {\n if (elementSafeTagName(child) === "TITLE" && child.ownerSVGElement) {\n return getTextAlternativeInternal(child, {\n ...childOptions,\n embeddedInLabelledBy: { element: child, hidden: isElementHiddenForAria(child) }\n });\n }\n }\n }\n if (element.ownerSVGElement && tagName === "A") {\n const title = element.getAttribute("xlink:title") || "";\n if (trimFlatString(title)) {\n options.visitedElements.add(element);\n return title;\n }\n }\n }\n const shouldNameFromContentForSummary = tagName === "SUMMARY" && !["presentation", "none"].includes(role);\n if (allowsNameFromContent(role, options.embeddedInTargetElement === "descendant") || shouldNameFromContentForSummary || !!options.embeddedInLabelledBy || !!options.embeddedInDescribedBy || !!options.embeddedInLabel || !!options.embeddedInNativeTextAlternative) {\n options.visitedElements.add(element);\n const accessibleName = innerAccumulatedElementText(element, childOptions);\n const maybeTrimmedAccessibleName = options.embeddedInTargetElement === "self" ? trimFlatString(accessibleName) : accessibleName;\n if (maybeTrimmedAccessibleName)\n return accessibleName;\n }\n if (!["presentation", "none"].includes(role) || tagName === "IFRAME") {\n options.visitedElements.add(element);\n const title = element.getAttribute("title") || "";\n if (trimFlatString(title))\n return title;\n }\n options.visitedElements.add(element);\n return "";\n}\nfunction innerAccumulatedElementText(element, options) {\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 = getTextAlternativeInternal(node, options);\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(getCSSContent(element, "::before") || "");\n const content = getCSSContent(element);\n if (content !== void 0) {\n tokens.push(content);\n } else {\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 }\n tokens.push(getCSSContent(element, "::after") || "");\n return tokens.join("");\n}\nvar kAriaSelectedRoles = ["gridcell", "option", "row", "tab", "rowheader", "columnheader", "treeitem"];\nfunction getAriaSelected(element) {\n if (elementSafeTagName(element) === "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 getCheckedAllowMixed(element) {\n return getChecked(element, true);\n}\nfunction getCheckedWithoutMixed(element) {\n const result = getChecked(element, false);\n return result;\n}\nfunction getChecked(element, allowMixed) {\n const tagName = elementSafeTagName(element);\n if (allowMixed && tagName === "INPUT" && element.indeterminate)\n return "mixed";\n if (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 kAriaReadonlyRoles = ["checkbox", "combobox", "grid", "gridcell", "listbox", "radiogroup", "slider", "spinbutton", "textbox", "columnheader", "rowheader", "searchbox", "switch", "treegrid"];\nfunction getReadonly(element) {\n const tagName = elementSafeTagName(element);\n if (["INPUT", "TEXTAREA", "SELECT"].includes(tagName))\n return element.hasAttribute("readonly");\n if (kAriaReadonlyRoles.includes(getAriaRole(element) || ""))\n return element.getAttribute("aria-readonly") === "true";\n if (element.isContentEditable)\n return false;\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 (elementSafeTagName(element) === "DETAILS")\n return element.open;\n if (kAriaExpandedRoles.includes(getAriaRole(element) || "")) {\n const expanded = element.getAttribute("aria-expanded");\n if (expanded === null)\n return void 0;\n if (expanded === "true")\n return true;\n return false;\n }\n return void 0;\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 }[elementSafeTagName(element)];\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 return isNativelyDisabled(element) || hasExplicitAriaDisabled(element);\n}\nfunction isNativelyDisabled(element) {\n const isNativeFormControl = ["BUTTON", "INPUT", "SELECT", "TEXTAREA", "OPTION", "OPTGROUP"].includes(elementSafeTagName(element));\n return isNativeFormControl && (element.hasAttribute("disabled") || belongsToDisabledOptGroup(element) || belongsToDisabledFieldSet(element));\n}\nfunction belongsToDisabledOptGroup(element) {\n return elementSafeTagName(element) === "OPTION" && !!element.closest("OPTGROUP[DISABLED]");\n}\nfunction belongsToDisabledFieldSet(element) {\n const fieldSetElement = element == null ? void 0 : element.closest("FIELDSET[DISABLED]");\n if (!fieldSetElement)\n return false;\n const legendElement = fieldSetElement.querySelector(":scope > LEGEND");\n return !legendElement || !legendElement.contains(element);\n}\nfunction hasExplicitAriaDisabled(element, isAncestor = false) {\n if (!element)\n return false;\n if (isAncestor || 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 return hasExplicitAriaDisabled(parentElementOrShadowHost(element), true);\n }\n return false;\n}\nfunction getAccessibleNameFromAssociatedLabels(labels, options) {\n return [...labels].map((label) => getTextAlternativeInternal(label, {\n ...options,\n embeddedInLabel: { element: label, hidden: isElementHiddenForAria(label) },\n embeddedInNativeTextAlternative: void 0,\n embeddedInLabelledBy: void 0,\n embeddedInDescribedBy: void 0,\n embeddedInTargetElement: void 0\n })).filter((accessibleName) => !!accessibleName).join(" ");\n}\nfunction receivesPointerEvents(element) {\n const cache = cachePointerEvents;\n let e = element;\n let result;\n const parents = [];\n for (; e; e = parentElementOrShadowHost(e)) {\n const cached = cache.get(e);\n if (cached !== void 0) {\n result = cached;\n break;\n }\n parents.push(e);\n const style = getElementComputedStyle(e);\n if (!style) {\n result = true;\n break;\n }\n const value = style.pointerEvents;\n if (value) {\n result = value !== "none";\n break;\n }\n }\n if (result === void 0)\n result = true;\n for (const parent of parents)\n cache.set(parent, result);\n return result;\n}\nvar cacheAccessibleName;\nvar cacheAccessibleNameHidden;\nvar cacheAccessibleDescription;\nvar cacheAccessibleDescriptionHidden;\nvar cacheAccessibleErrorMessage;\nvar cacheIsHidden;\nvar cachePseudoContent;\nvar cachePseudoContentBefore;\nvar cachePseudoContentAfter;\nvar cachePointerEvents;\nvar cachesCounter2 = 0;\nfunction beginAriaCaches() {\n beginDOMCaches();\n ++cachesCounter2;\n cacheAccessibleName != null ? cacheAccessibleName : cacheAccessibleName = /* @__PURE__ */ new Map();\n cacheAccessibleNameHidden != null ? cacheAccessibleNameHidden : cacheAccessibleNameHidden = /* @__PURE__ */ new Map();\n cacheAccessibleDescription != null ? cacheAccessibleDescription : cacheAccessibleDescription = /* @__PURE__ */ new Map();\n cacheAccessibleDescriptionHidden != null ? cacheAccessibleDescriptionHidden : cacheAccessibleDescriptionHidden = /* @__PURE__ */ new Map();\n cacheAccessibleErrorMessage != null ? cacheAccessibleErrorMessage : cacheAccessibleErrorMessage = /* @__PURE__ */ new Map();\n cacheIsHidden != null ? cacheIsHidden : cacheIsHidden = /* @__PURE__ */ new Map();\n cachePseudoContent != null ? cachePseudoContent : cachePseudoContent = /* @__PURE__ */ new Map();\n cachePseudoContentBefore != null ? cachePseudoContentBefore : cachePseudoContentBefore = /* @__PURE__ */ new Map();\n cachePseudoContentAfter != null ? cachePseudoContentAfter : cachePseudoContentAfter = /* @__PURE__ */ new Map();\n cachePointerEvents != null ? cachePointerEvents : cachePointerEvents = /* @__PURE__ */ new Map();\n}\nfunction endAriaCaches() {\n if (!--cachesCounter2) {\n cacheAccessibleName = void 0;\n cacheAccessibleNameHidden = void 0;\n cacheAccessibleDescription = void 0;\n cacheAccessibleDescriptionHidden = void 0;\n cacheAccessibleErrorMessage = void 0;\n cacheIsHidden = void 0;\n cachePseudoContent = void 0;\n cachePseudoContentBefore = void 0;\n cachePseudoContentAfter = void 0;\n cachePointerEvents = void 0;\n }\n endDOMCaches();\n}\nvar inputTypeToRole = {\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};\n\n// packages/injected/src/ariaSnapshot.ts\nvar lastRef = 0;\nfunction toInternalOptions(options) {\n const renderBoxes = options.boxes;\n if (options.mode === "ai") {\n return {\n visibility: "ariaOrVisible",\n refs: "interactable",\n refPrefix: options.refPrefix,\n includeGenericRole: true,\n renderActive: !options.doNotRenderActive,\n renderCursorPointer: true,\n renderBoxes\n };\n }\n if (options.mode === "autoexpect") {\n return { visibility: "ariaAndVisible", refs: "none", renderBoxes };\n }\n if (options.mode === "codegen") {\n return { visibility: "aria", refs: "none", renderStringsAsRegex: true, renderBoxes };\n }\n return { visibility: "aria", refs: "none", renderBoxes };\n}\nfunction generateAriaTree(rootElement, publicOptions) {\n const options = toInternalOptions(publicOptions);\n const visited = /* @__PURE__ */ new Set();\n const snapshot = {\n root: { role: "fragment", name: "", children: [], props: {}, box: computeBox(rootElement), receivesPointerEvents: true },\n elements: /* @__PURE__ */ new Map(),\n refs: /* @__PURE__ */ new Map(),\n iframeRefs: []\n };\n setAriaNodeElement(snapshot.root, rootElement);\n const visit = (ariaNode, node, parentElementVisible) => {\n if (visited.has(node))\n return;\n visited.add(node);\n if (node.nodeType === Node.TEXT_NODE && node.nodeValue) {\n if (!parentElementVisible)\n return;\n const text = node.nodeValue;\n if (ariaNode.role !== "textbox" && text)\n ariaNode.children.push(node.nodeValue || "");\n return;\n }\n if (node.nodeType !== Node.ELEMENT_NODE)\n return;\n const element = node;\n const isElementVisibleForAria = !isElementHiddenForAria(element);\n let visible = isElementVisibleForAria;\n if (options.visibility === "ariaOrVisible")\n visible = isElementVisibleForAria || isElementVisible(element);\n if (options.visibility === "ariaAndVisible")\n visible = isElementVisibleForAria && isElementVisible(element);\n if (options.visibility === "aria" && !visible)\n return;\n const ariaChildren = [];\n if (element.hasAttribute("aria-owns")) {\n const ids = element.getAttribute("aria-owns").split(/\\s+/);\n for (const id of ids) {\n const ownedElement = rootElement.ownerDocument.getElementById(id);\n if (ownedElement)\n ariaChildren.push(ownedElement);\n }\n }\n const childAriaNode = visible ? toAriaNode(element, options) : null;\n if (childAriaNode) {\n if (childAriaNode.ref) {\n snapshot.elements.set(childAriaNode.ref, element);\n snapshot.refs.set(element, childAriaNode.ref);\n if (childAriaNode.role === "iframe")\n snapshot.iframeRefs.push(childAriaNode.ref);\n }\n ariaNode.children.push(childAriaNode);\n }\n processElement(childAriaNode || ariaNode, element, ariaChildren, visible);\n };\n function processElement(ariaNode, element, ariaChildren, parentElementVisible) {\n var _a;\n const display = ((_a = getElementComputedStyle(element)) == null ? void 0 : _a.display) || "inline";\n const treatAsBlock = display !== "inline" || element.nodeName === "BR" ? " " : "";\n if (treatAsBlock)\n ariaNode.children.push(treatAsBlock);\n ariaNode.children.push(getCSSContent(element, "::before") || "");\n const assignedNodes = element.nodeName === "SLOT" ? element.assignedNodes() : [];\n if (assignedNodes.length) {\n for (const child of assignedNodes)\n visit(ariaNode, child, parentElementVisible);\n } else {\n for (let child = element.firstChild; child; child = child.nextSibling) {\n if (!child.assignedSlot)\n visit(ariaNode, child, parentElementVisible);\n }\n if (element.shadowRoot) {\n for (let child = element.shadowRoot.firstChild; child; child = child.nextSibling)\n visit(ariaNode, child, parentElementVisible);\n }\n }\n for (const child of ariaChildren)\n visit(ariaNode, child, parentElementVisible);\n ariaNode.children.push(getCSSContent(element, "::after") || "");\n if (treatAsBlock)\n ariaNode.children.push(treatAsBlock);\n if (ariaNode.children.length === 1 && ariaNode.name === ariaNode.children[0])\n ariaNode.children = [];\n if (ariaNode.role === "link" && element.hasAttribute("href")) {\n const href = element.getAttribute("href");\n ariaNode.props["url"] = href;\n }\n if (ariaNode.role === "textbox" && element.hasAttribute("placeholder") && element.getAttribute("placeholder") !== ariaNode.name) {\n const placeholder = element.getAttribute("placeholder");\n ariaNode.props["placeholder"] = placeholder;\n }\n }\n beginAriaCaches();\n try {\n visit(snapshot.root, rootElement, true);\n } finally {\n endAriaCaches();\n }\n normalizeStringChildren(snapshot.root);\n normalizeGenericRoles(snapshot.root);\n return snapshot;\n}\nfunction computeAriaRef(ariaNode, options) {\n var _a;\n if (options.refs === "none")\n return;\n if (options.refs === "interactable" && (!ariaNode.box.visible || !ariaNode.receivesPointerEvents))\n return;\n const element = ariaNodeElement(ariaNode);\n let ariaRef = element._ariaRef;\n if (!ariaRef || ariaRef.role !== ariaNode.role || ariaRef.name !== ariaNode.name) {\n ariaRef = { role: ariaNode.role, name: ariaNode.name, ref: ((_a = options.refPrefix) != null ? _a : "") + "e" + ++lastRef };\n element._ariaRef = ariaRef;\n }\n ariaNode.ref = ariaRef.ref;\n}\nfunction toAriaNode(element, options) {\n var _a;\n const active = element.ownerDocument.activeElement === element;\n if (element.nodeName === "IFRAME") {\n const ariaNode = {\n role: "iframe",\n name: "",\n children: [],\n props: {},\n box: computeBox(element),\n receivesPointerEvents: true,\n active\n };\n setAriaNodeElement(ariaNode, element);\n computeAriaRef(ariaNode, options);\n return ariaNode;\n }\n const defaultRole = options.includeGenericRole ? "generic" : null;\n const role = (_a = getAriaRole(element)) != null ? _a : defaultRole;\n if (!role || role === "presentation" || role === "none")\n return null;\n const name = normalizeWhiteSpace(getElementAccessibleName(element, false) || "");\n const receivesPointerEvents2 = receivesPointerEvents(element);\n const box = computeBox(element);\n if (role === "generic" && box.inline && element.childNodes.length === 1 && element.childNodes[0].nodeType === Node.TEXT_NODE)\n return null;\n const result = {\n role,\n name,\n children: [],\n props: {},\n box,\n receivesPointerEvents: receivesPointerEvents2,\n active\n };\n setAriaNodeElement(result, element);\n computeAriaRef(result, options);\n if (kAriaCheckedRoles.includes(role))\n result.checked = getAriaChecked(element);\n if (kAriaDisabledRoles.includes(role))\n result.disabled = getAriaDisabled(element);\n if (kAriaExpandedRoles.includes(role))\n result.expanded = getAriaExpanded(element);\n if (kAriaLevelRoles.includes(role))\n result.level = getAriaLevel(element);\n if (kAriaPressedRoles.includes(role))\n result.pressed = getAriaPressed(element);\n if (kAriaSelectedRoles.includes(role))\n result.selected = getAriaSelected(element);\n if (element instanceof HTMLInputElement || element instanceof HTMLTextAreaElement) {\n if (element.type !== "checkbox" && element.type !== "radio" && element.type !== "file")\n result.children = [element.value];\n }\n return result;\n}\nfunction normalizeGenericRoles(node) {\n const normalizeChildren = (node2) => {\n const result = [];\n for (const child of node2.children || []) {\n if (typeof child === "string") {\n result.push(child);\n continue;\n }\n const normalized = normalizeChildren(child);\n result.push(...normalized);\n }\n const removeSelf = node2.role === "generic" && !node2.name && result.length <= 1 && result.every((c) => typeof c !== "string" && !!c.ref);\n if (removeSelf)\n return result;\n node2.children = result;\n return [node2];\n };\n normalizeChildren(node);\n}\nfunction normalizeStringChildren(rootA11yNode) {\n const flushChildren = (buffer, normalizedChildren) => {\n if (!buffer.length)\n return;\n const text = normalizeWhiteSpace(buffer.join(""));\n if (text)\n normalizedChildren.push(text);\n buffer.length = 0;\n };\n const visit = (ariaNode) => {\n const normalizedChildren = [];\n const buffer = [];\n for (const child of ariaNode.children || []) {\n if (typeof child === "string") {\n buffer.push(child);\n } else {\n flushChildren(buffer, normalizedChildren);\n visit(child);\n normalizedChildren.push(child);\n }\n }\n flushChildren(buffer, normalizedChildren);\n ariaNode.children = normalizedChildren.length ? normalizedChildren : [];\n if (ariaNode.children.length === 1 && ariaNode.children[0] === ariaNode.name)\n ariaNode.children = [];\n };\n visit(rootA11yNode);\n}\nfunction matchesStringOrRegex(text, template) {\n if (!template)\n return true;\n if (!text)\n return false;\n if (typeof template === "string")\n return text === template;\n return !!text.match(new RegExp(template.pattern));\n}\nfunction matchesTextValue(text, template) {\n if (!(template == null ? void 0 : template.normalized))\n return true;\n if (!text)\n return false;\n if (text === template.normalized)\n return true;\n if (text === template.raw)\n return true;\n const regex = cachedRegex(template);\n if (regex)\n return !!text.match(regex);\n return false;\n}\nvar cachedRegexSymbol = Symbol("cachedRegex");\nfunction cachedRegex(template) {\n if (template[cachedRegexSymbol] !== void 0)\n return template[cachedRegexSymbol];\n const { raw } = template;\n const canBeRegex = raw.startsWith("/") && raw.endsWith("/") && raw.length > 1;\n let regex;\n try {\n regex = canBeRegex ? new RegExp(raw.slice(1, -1)) : null;\n } catch (e) {\n regex = null;\n }\n template[cachedRegexSymbol] = regex;\n return regex;\n}\nfunction matchesExpectAriaTemplate(rootElement, template) {\n const snapshot = generateAriaTree(rootElement, { mode: "default" });\n const matches = matchesNodeDeep(snapshot.root, template, false, false);\n return {\n matches,\n received: {\n raw: renderAriaTree(snapshot, { mode: "default" }).text,\n regex: renderAriaTree(snapshot, { mode: "codegen" }).text\n }\n };\n}\nfunction getAllElementsMatchingExpectAriaTemplate(rootElement, template) {\n const root = generateAriaTree(rootElement, { mode: "default" }).root;\n const matches = matchesNodeDeep(root, template, true, false);\n return matches.map((n) => ariaNodeElement(n));\n}\nfunction matchesNode(node, template, isDeepEqual) {\n var _a;\n if (typeof node === "string" && template.kind === "text")\n return matchesTextValue(node, template.text);\n if (node === null || typeof node !== "object" || template.kind !== "role")\n return false;\n if (template.role !== "fragment" && template.role !== node.role)\n return false;\n if (template.checked !== void 0 && template.checked !== node.checked)\n return false;\n if (template.disabled !== void 0 && template.disabled !== node.disabled)\n return false;\n if (template.expanded !== void 0 && template.expanded !== node.expanded)\n return false;\n if (template.level !== void 0 && template.level !== node.level)\n return false;\n if (template.pressed !== void 0 && template.pressed !== node.pressed)\n return false;\n if (template.selected !== void 0 && template.selected !== node.selected)\n return false;\n if (!matchesStringOrRegex(node.name, template.name))\n return false;\n if (!matchesTextValue(node.props.url, (_a = template.props) == null ? void 0 : _a.url))\n return false;\n if (template.containerMode === "contain")\n return containsList(node.children || [], template.children || []);\n if (template.containerMode === "equal")\n return listEqual(node.children || [], template.children || [], false);\n if (template.containerMode === "deep-equal" || isDeepEqual)\n return listEqual(node.children || [], template.children || [], true);\n return containsList(node.children || [], template.children || []);\n}\nfunction listEqual(children, template, isDeepEqual) {\n if (template.length !== children.length)\n return false;\n for (let i = 0; i < template.length; ++i) {\n if (!matchesNode(children[i], template[i], isDeepEqual))\n return false;\n }\n return true;\n}\nfunction containsList(children, template) {\n if (template.length > children.length)\n return false;\n const cc = children.slice();\n const tt = template.slice();\n for (const t of tt) {\n let c = cc.shift();\n while (c) {\n if (matchesNode(c, t, false))\n break;\n c = cc.shift();\n }\n if (!c)\n return false;\n }\n return true;\n}\nfunction matchesNodeDeep(root, template, collectAll, isDeepEqual) {\n const results = [];\n const visit = (node, parent) => {\n if (matchesNode(node, template, isDeepEqual)) {\n const result = typeof node === "string" ? parent : node;\n if (result)\n results.push(result);\n return !collectAll;\n }\n if (typeof node === "string")\n return false;\n for (const child of node.children || []) {\n if (visit(child, node))\n return true;\n }\n return false;\n };\n visit(root, null);\n return results;\n}\nfunction buildByRefMap(root, map = /* @__PURE__ */ new Map()) {\n if (root == null ? void 0 : root.ref)\n map.set(root.ref, root);\n for (const child of (root == null ? void 0 : root.children) || []) {\n if (typeof child !== "string")\n buildByRefMap(child, map);\n }\n return map;\n}\nfunction compareSnapshots(ariaSnapshot, previousSnapshot) {\n var _a;\n const previousByRef = buildByRefMap(previousSnapshot == null ? void 0 : previousSnapshot.root);\n const result = /* @__PURE__ */ new Map();\n const visit = (ariaNode, previousNode) => {\n let same = ariaNode.children.length === (previousNode == null ? void 0 : previousNode.children.length) && ariaNodesEqual(ariaNode, previousNode);\n let canBeSkipped = same;\n for (let childIndex = 0; childIndex < ariaNode.children.length; childIndex++) {\n const child = ariaNode.children[childIndex];\n const previousChild = previousNode == null ? void 0 : previousNode.children[childIndex];\n if (typeof child === "string") {\n same && (same = child === previousChild);\n canBeSkipped && (canBeSkipped = child === previousChild);\n } else {\n let previous = typeof previousChild !== "string" ? previousChild : void 0;\n if (child.ref)\n previous = previousByRef.get(child.ref);\n const sameChild = visit(child, previous);\n if (!previous || !sameChild && !child.ref || previous !== previousChild)\n canBeSkipped = false;\n same && (same = sameChild && previous === previousChild);\n }\n }\n result.set(ariaNode, same ? "same" : canBeSkipped ? "skip" : "changed");\n return same;\n };\n visit(ariaSnapshot.root, previousByRef.get((_a = previousSnapshot == null ? void 0 : previousSnapshot.root) == null ? void 0 : _a.ref));\n return result;\n}\nfunction filterSnapshotDiff(nodes, statusMap) {\n const result = [];\n const visit = (ariaNode) => {\n const status = statusMap.get(ariaNode);\n if (status === "same") {\n } else if (status === "skip") {\n for (const child of ariaNode.children) {\n if (typeof child !== "string")\n visit(child);\n }\n } else {\n result.push(ariaNode);\n }\n };\n for (const node of nodes) {\n if (typeof node === "string")\n result.push(node);\n else\n visit(node);\n }\n return result;\n}\nfunction indent(depth) {\n return " ".repeat(depth);\n}\nfunction renderAriaTree(ariaSnapshot, publicOptions, previousSnapshot) {\n const options = toInternalOptions(publicOptions);\n const lines = [];\n const iframeDepths = {};\n const includeText = options.renderStringsAsRegex ? textContributesInfo : () => true;\n const renderString = options.renderStringsAsRegex ? convertToBestGuessRegex : (str) => str;\n let nodesToRender = ariaSnapshot.root.role === "fragment" ? ariaSnapshot.root.children : [ariaSnapshot.root];\n const statusMap = compareSnapshots(ariaSnapshot, previousSnapshot);\n if (previousSnapshot)\n nodesToRender = filterSnapshotDiff(nodesToRender, statusMap);\n const visitText = (text, depth) => {\n if (publicOptions.depth && depth > publicOptions.depth)\n return;\n const escaped = yamlEscapeValueIfNeeded(renderString(text));\n if (escaped)\n lines.push(indent(depth) + "- text: " + escaped);\n };\n const createKey = (ariaNode, renderCursorPointer) => {\n let key = ariaNode.role;\n if (ariaNode.name && ariaNode.name.length <= 900) {\n const name = renderString(ariaNode.name);\n if (name) {\n const stringifiedName = name.startsWith("/") && name.endsWith("/") ? name : JSON.stringify(name);\n key += " " + stringifiedName;\n }\n }\n if (ariaNode.checked === "mixed")\n key += ` [checked=mixed]`;\n if (ariaNode.checked === true)\n key += ` [checked]`;\n if (ariaNode.disabled)\n key += ` [disabled]`;\n if (ariaNode.expanded)\n key += ` [expanded]`;\n if (ariaNode.active && options.renderActive)\n key += ` [active]`;\n if (ariaNode.level)\n key += ` [level=${ariaNode.level}]`;\n if (ariaNode.pressed === "mixed")\n key += ` [pressed=mixed]`;\n if (ariaNode.pressed === true)\n key += ` [pressed]`;\n if (ariaNode.selected === true)\n key += ` [selected]`;\n if (ariaNode.ref) {\n key += ` [ref=${ariaNode.ref}]`;\n if (renderCursorPointer && hasPointerCursor(ariaNode))\n key += " [cursor=pointer]";\n }\n if (options.renderBoxes) {\n const element = ariaNodeElement(ariaNode);\n if (element) {\n const r = element.getBoundingClientRect();\n key += ` [box=${Math.round(r.x)},${Math.round(r.y)},${Math.round(r.width)},${Math.round(r.height)}]`;\n }\n }\n return key;\n };\n const getSingleInlinedTextChild = (ariaNode) => {\n return (ariaNode == null ? void 0 : ariaNode.children.length) === 1 && typeof ariaNode.children[0] === "string" && !Object.keys(ariaNode.props).length ? ariaNode.children[0] : void 0;\n };\n const visit = (ariaNode, depth, renderCursorPointer) => {\n if (publicOptions.depth && depth > publicOptions.depth)\n return;\n if (ariaNode.role === "iframe" && ariaNode.ref)\n iframeDepths[ariaNode.ref] = depth;\n if (statusMap.get(ariaNode) === "same" && ariaNode.ref) {\n lines.push(indent(depth) + `- ref=${ariaNode.ref} [unchanged]`);\n return;\n }\n const isDiffRoot = !!previousSnapshot && !depth;\n const escapedKey = indent(depth) + "- " + (isDiffRoot ? "<changed> " : "") + yamlEscapeKeyIfNeeded(createKey(ariaNode, renderCursorPointer));\n const singleInlinedTextChild = getSingleInlinedTextChild(ariaNode);\n const isAtDepthLimit = !!publicOptions.depth && depth === publicOptions.depth;\n const hasNoChildren = !singleInlinedTextChild && (!ariaNode.children.length || isAtDepthLimit);\n if (hasNoChildren && !Object.keys(ariaNode.props).length) {\n lines.push(escapedKey);\n } else if (singleInlinedTextChild !== void 0) {\n const shouldInclude = includeText(ariaNode, singleInlinedTextChild);\n if (shouldInclude)\n lines.push(escapedKey + ": " + yamlEscapeValueIfNeeded(renderString(singleInlinedTextChild)));\n else\n lines.push(escapedKey);\n } else {\n lines.push(escapedKey + ":");\n for (const [name, value] of Object.entries(ariaNode.props))\n lines.push(indent(depth + 1) + "- /" + name + ": " + yamlEscapeValueIfNeeded(value));\n const inCursorPointer = !!ariaNode.ref && renderCursorPointer && hasPointerCursor(ariaNode);\n for (const child of ariaNode.children) {\n if (typeof child === "string")\n visitText(includeText(ariaNode, child) ? child : "", depth + 1);\n else\n visit(child, depth + 1, renderCursorPointer && !inCursorPointer);\n }\n }\n };\n for (const nodeToRender of nodesToRender) {\n if (typeof nodeToRender === "string")\n visitText(nodeToRender, 0);\n else\n visit(nodeToRender, 0, !!options.renderCursorPointer);\n }\n return { text: lines.join("\\n"), iframeDepths };\n}\nfunction convertToBestGuessRegex(text) {\n const dynamicContent = [\n // 550e8400-e29b-41d4-a716-446655440000\n { regex: /\\b[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}\\b/, replacement: "[0-9a-fA-F-]+" },\n // 2mb\n { regex: /\\b[\\d,.]+[bkmBKM]+\\b/, replacement: "[\\\\d,.]+[bkmBKM]+" },\n // 2ms, 20s\n { regex: /\\b\\d+[hmsp]+\\b/, replacement: "\\\\d+[hmsp]+" },\n { regex: /\\b[\\d,.]+[hmsp]+\\b/, replacement: "[\\\\d,.]+[hmsp]+" },\n // Do not replace single digits with regex by default.\n // 2+ digits: [Issue 22, 22.3, 2.33, 2,333]\n { regex: /\\b\\d+,\\d+\\b/, replacement: "\\\\d+,\\\\d+" },\n { regex: /\\b\\d+\\.\\d{2,}\\b/, replacement: "\\\\d+\\\\.\\\\d+" },\n { regex: /\\b\\d{2,}\\.\\d+\\b/, replacement: "\\\\d+\\\\.\\\\d+" },\n { regex: /\\b\\d{2,}\\b/, replacement: "\\\\d+" }\n ];\n let pattern = "";\n let lastIndex = 0;\n const combinedRegex = new RegExp(dynamicContent.map((r) => "(" + r.regex.source + ")").join("|"), "g");\n text.replace(combinedRegex, (match, ...args) => {\n const offset = args[args.length - 2];\n const groups = args.slice(0, -2);\n pattern += escapeRegExp(text.slice(lastIndex, offset));\n for (let i = 0; i < groups.length; i++) {\n if (groups[i]) {\n const { replacement } = dynamicContent[i];\n pattern += replacement;\n break;\n }\n }\n lastIndex = offset + match.length;\n return match;\n });\n if (!pattern)\n return text;\n pattern += escapeRegExp(text.slice(lastIndex));\n return String(new RegExp(pattern));\n}\nfunction textContributesInfo(node, text) {\n if (!text.length)\n return false;\n if (!node.name)\n return true;\n const substr = text.length <= 200 && node.name.length <= 200 ? longestCommonSubstring(text, node.name) : "";\n let filtered = text;\n while (substr && filtered.includes(substr))\n filtered = filtered.replace(substr, "");\n return filtered.trim().length / text.length > 0.1;\n}\nvar elementSymbol = Symbol("element");\nfunction ariaNodeElement(ariaNode) {\n return ariaNode[elementSymbol];\n}\nfunction setAriaNodeElement(ariaNode, element) {\n ariaNode[elementSymbol] = element;\n}\nfunction findNewElement(from, to) {\n const node = findNewNode(from, to);\n return node ? ariaNodeElement(node) : void 0;\n}\n\n// packages/injected/src/highlight.css?inline\nvar highlight_default = ":host{font-size:13px;font-family:system-ui,Ubuntu,Droid Sans,sans-serif;color:#333}svg{position:absolute;height:0}x-pw-tooltip{backdrop-filter:blur(5px);background-color:#fff;border-radius:6px;box-shadow:0 .5rem 1.2rem #0000004d;display:none;font-size:12.8px;font-weight:400;left:0;line-height:1.5;max-width:600px;position:absolute;top:0;padding:0;flex-direction:column;overflow:hidden}x-pw-tooltip-line{display:flex;max-width:600px;padding:6px;user-select:none;cursor:pointer}x-pw-tooltip-footer{display:flex;max-width:600px;padding:6px;user-select:none;color:#777}x-pw-dialog{background-color:#fff;pointer-events:auto;border-radius:6px;box-shadow:0 .5rem 1.2rem #0000004d;display:flex;flex-direction:column;position:absolute;z-index:10;font-size:13px}x-pw-dialog:not(.autosize){width:400px;height:150px}x-pw-dialog-body{display:flex;flex-direction:column;flex:auto}x-pw-dialog-body label{margin:5px 8px;display:flex;flex-direction:row;align-items:center}x-pw-highlight{position:absolute;top:0;left:0;width:0;height:0}x-pw-action-point{position:absolute;width:20px;height:20px;background:red;border-radius:10px;margin:-10px 0 0 -10px;z-index:2}x-pw-title{position:absolute;backdrop-filter:blur(5px);background-color:#00000080;color:#fff;border-radius:6px;padding:6px;font-size:24px;line-height:1.4;white-space:nowrap;user-select:none;z-index:3}x-pw-user-overlays,x-pw-user-overlay{position:absolute;inset:0}@keyframes pw-fade-out{0%{opacity:1}to{opacity:0}}x-pw-separator{height:1px;margin:6px 9px;background:#949494e5}x-pw-tool-gripper{height:28px;width:24px;margin:2px 0;cursor:grab}x-pw-tool-gripper:active{cursor:grabbing}x-pw-tool-gripper>x-div{width:16px;height:16px;margin:6px 4px;clip-path:url(#icon-gripper);background-color:#555}x-pw-tools-list>label{display:flex;align-items:center;margin:0 10px;user-select:none}x-pw-tools-list{display:flex;width:100%;border-bottom:1px solid #dddddd}x-pw-tool-item{pointer-events:auto;height:28px;width:28px;border-radius:3px}x-pw-tool-item:not(.disabled){cursor:pointer}x-pw-tool-item:not(.disabled):hover{background-color:#dbdbdb}x-pw-tool-item.toggled{background-color:#8acae480}x-pw-tool-item.toggled:not(.disabled):hover{background-color:#8acae4c4}x-pw-tool-item>x-div{width:16px;height:16px;margin:6px;background-color:#3a3a3a}x-pw-tool-item.disabled>x-div{background-color:#61616180;cursor:default}x-pw-tool-item.record.toggled{background-color:transparent}x-pw-tool-item.record.toggled:not(.disabled):hover{background-color:#dbdbdb}x-pw-tool-item.record.toggled>x-div{background-color:#a1260d}x-pw-tool-item.record.disabled.toggled>x-div{opacity:.8}x-pw-tool-item.accept>x-div{background-color:#388a34}x-pw-tool-item.record>x-div{clip-path:url(#icon-circle-large-filled)}x-pw-tool-item.record.toggled>x-div{clip-path:url(#icon-stop-circle)}x-pw-tool-item.pick-locator>x-div{clip-path:url(#icon-inspect)}x-pw-tool-item.text>x-div{clip-path:url(#icon-whole-word)}x-pw-tool-item.visibility>x-div{clip-path:url(#icon-eye)}x-pw-tool-item.value>x-div{clip-path:url(#icon-symbol-constant)}x-pw-tool-item.snapshot>x-div{clip-path:url(#icon-gist)}x-pw-tool-item.accept>x-div{clip-path:url(#icon-check)}x-pw-tool-item.cancel>x-div{clip-path:url(#icon-close)}x-pw-tool-item.succeeded>x-div{clip-path:url(#icon-pass);background-color:#388a34!important}x-pw-overlay{position:absolute;top:0;max-width:min-content;z-index:2147483647;background:transparent;pointer-events:auto}x-pw-overlay x-pw-tools-list{background-color:#fffd;box-shadow:#0000001a 0 5px 5px;border-radius:3px;border-bottom:none}x-pw-overlay x-pw-tool-item{margin:2px}textarea.text-editor{font-family:system-ui,Ubuntu,Droid Sans,sans-serif;flex:auto;border:none;margin:6px 10px;color:#333;outline:1px solid transparent!important;resize:none;padding:0;font-size:13px}textarea.text-editor.does-not-match{outline:1px solid red!important}x-div{display:block}x-spacer{flex:auto}*{box-sizing:border-box}*[hidden]{display:none!important}x-locator-editor{flex:none;width:100%;height:60px;padding:4px;border-bottom:1px solid #dddddd;outline:1px solid transparent}x-locator-editor.does-not-match{outline:1px solid red}.CodeMirror{width:100%!important;height:100%!important}x-pw-action-list{flex:auto;display:flex;flex-direction:column;user-select:none}x-pw-action-item{padding:6px 10px;cursor:pointer;overflow:hidden}x-pw-action-item:hover{background-color:#f2f2f2}x-pw-action-item:last-child{border-bottom-left-radius:6px;border-bottom-right-radius:6px}\\n";\n\n// packages/injected/src/highlight.ts\nvar Highlight = class {\n constructor(injectedScript) {\n this._renderedEntries = [];\n this._userOverlays = /* @__PURE__ */ new Map();\n this._userOverlayHidden = false;\n this._language = "javascript";\n this._elementHighlightSelectors = /* @__PURE__ */ new Map();\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.setAttribute("popover", "manual");\n this._glassPaneElement.style.inset = "0";\n this._glassPaneElement.style.width = "100%";\n this._glassPaneElement.style.height = "100%";\n this._glassPaneElement.style.maxWidth = "none";\n this._glassPaneElement.style.maxHeight = "none";\n this._glassPaneElement.style.padding = "0";\n this._glassPaneElement.style.margin = "0";\n this._glassPaneElement.style.border = "none";\n this._glassPaneElement.style.overflow = "visible";\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._titleElement = document.createElement("x-pw-title");\n this._titleElement.setAttribute("hidden", "true");\n this._userOverlayContainer = document.createElement("x-pw-user-overlays");\n this._userOverlayContainer.setAttribute("hidden", "true");\n this._glassPaneShadow = this._glassPaneElement.attachShadow({ mode: this._isUnderTest ? "open" : "closed" });\n if (typeof this._glassPaneShadow.adoptedStyleSheets.push === "function") {\n const sheet = new this._injectedScript.window.CSSStyleSheet();\n sheet.replaceSync(highlight_default);\n this._glassPaneShadow.adoptedStyleSheets.push(sheet);\n } else {\n const styleElement = this._injectedScript.document.createElement("style");\n styleElement.textContent = highlight_default;\n this._glassPaneShadow.appendChild(styleElement);\n }\n this._glassPaneShadow.appendChild(this._actionPointElement);\n this._glassPaneShadow.appendChild(this._titleElement);\n this._glassPaneShadow.appendChild(this._userOverlayContainer);\n }\n install() {\n if (!this._injectedScript.document.documentElement)\n return;\n if (!this._injectedScript.document.documentElement.contains(this._glassPaneElement) || this._glassPaneElement.nextElementSibling)\n this._injectedScript.document.documentElement.appendChild(this._glassPaneElement);\n this._bringToFront();\n }\n _bringToFront() {\n this._glassPaneElement.hidePopover();\n this._glassPaneElement.showPopover();\n }\n setLanguage(language) {\n this._language = language;\n }\n addElementHighlight(selector, cssStyle) {\n const key = stringifySelector(selector);\n this._elementHighlightSelectors.set(key, { selector, cssStyle });\n this._ensureElementHighlightRaf();\n }\n removeElementHighlight(selector) {\n const key = stringifySelector(selector);\n if (!this._elementHighlightSelectors.delete(key))\n return;\n if (this._elementHighlightSelectors.size === 0) {\n if (this._rafRequest) {\n this._injectedScript.utils.builtins.cancelAnimationFrame(this._rafRequest);\n this._rafRequest = void 0;\n }\n this.clearHighlight();\n }\n }\n _ensureElementHighlightRaf() {\n if (this._rafRequest)\n return;\n const tick = () => {\n const entries = [];\n for (const { selector, cssStyle } of this._elementHighlightSelectors.values()) {\n const elements = this._injectedScript.querySelectorAll(selector, this._injectedScript.document.documentElement);\n const locator = asLocator(this._language, stringifySelector(selector));\n const color = elements.length > 1 ? "#f6b26b7f" : "#6fa8dc7f";\n for (let i = 0; i < elements.length; ++i) {\n const suffix = elements.length > 1 ? ` [${i + 1} of ${elements.length}]` : "";\n entries.push({ element: elements[i], color, tooltipText: locator + suffix, cssStyle });\n }\n }\n this.updateHighlight(entries);\n this._rafRequest = this._injectedScript.utils.builtins.requestAnimationFrame(tick);\n };\n this._rafRequest = this._injectedScript.utils.builtins.requestAnimationFrame(tick);\n }\n uninstall() {\n if (this._rafRequest) {\n this._injectedScript.utils.builtins.cancelAnimationFrame(this._rafRequest);\n this._rafRequest = void 0;\n }\n this._elementHighlightSelectors.clear();\n this._glassPaneElement.remove();\n }\n showActionPoint(x, y, fadeDuration) {\n this._actionPointElement.style.top = y + "px";\n this._actionPointElement.style.left = x + "px";\n this._actionPointElement.hidden = false;\n if (fadeDuration)\n this._actionPointElement.style.animation = `pw-fade-out ${fadeDuration}ms ease-out forwards`;\n else\n this._actionPointElement.style.animation = "";\n }\n hideActionPoint() {\n this._actionPointElement.hidden = true;\n }\n showActionTitle(text, fadeDuration, position, fontSize) {\n this._titleElement.textContent = text;\n this._titleElement.hidden = false;\n if (fadeDuration) {\n const fadeTime = fadeDuration / 4;\n this._titleElement.style.animation = `pw-fade-out ${fadeTime}ms ease-out ${fadeDuration - fadeTime}ms forwards`;\n } else {\n this._titleElement.style.animation = "";\n }\n this._titleElement.style.top = "";\n this._titleElement.style.bottom = "";\n this._titleElement.style.left = "";\n this._titleElement.style.right = "";\n this._titleElement.style.transform = "";\n switch (position) {\n case "top-left":\n this._titleElement.style.top = "6px";\n this._titleElement.style.left = "6px";\n break;\n case "top":\n this._titleElement.style.top = "6px";\n this._titleElement.style.left = "50%";\n this._titleElement.style.transform = "translateX(-50%)";\n break;\n case "bottom-left":\n this._titleElement.style.bottom = "6px";\n this._titleElement.style.left = "6px";\n break;\n case "bottom":\n this._titleElement.style.bottom = "6px";\n this._titleElement.style.left = "50%";\n this._titleElement.style.transform = "translateX(-50%)";\n break;\n case "bottom-right":\n this._titleElement.style.bottom = "6px";\n this._titleElement.style.right = "6px";\n break;\n case "top-right":\n default:\n this._titleElement.style.top = "6px";\n this._titleElement.style.right = "6px";\n break;\n }\n if (fontSize)\n this._titleElement.style.fontSize = fontSize + "px";\n }\n hideActionTitle() {\n this._titleElement.hidden = true;\n }\n addUserOverlay(id, html) {\n const element = this._injectedScript.document.createElement("div");\n element.className = "x-pw-user-overlay";\n element.innerHTML = html;\n for (const script of element.querySelectorAll("script"))\n script.remove();\n for (const el of element.querySelectorAll("*")) {\n for (const attr of [...el.attributes]) {\n if (attr.name.startsWith("on"))\n el.removeAttribute(attr.name);\n }\n }\n this._userOverlays.set(id, element);\n this._userOverlayContainer.appendChild(element);\n this._userOverlayContainer.hidden = this._userOverlayHidden;\n return id;\n }\n getUserOverlay(id) {\n return this._userOverlays.get(id);\n }\n removeUserOverlay(id) {\n const element = this._userOverlays.get(id);\n if (element) {\n element.remove();\n this._userOverlays.delete(id);\n }\n if (this._userOverlays.size === 0)\n this._userOverlayContainer.hidden = true;\n }\n setUserOverlaysVisible(visible) {\n this._userOverlayHidden = !visible;\n this._userOverlayContainer.hidden = !visible || this._userOverlays.size === 0;\n }\n clearHighlight() {\n var _a, _b;\n for (const entry of this._renderedEntries) {\n (_a = entry.highlightElement) == null ? void 0 : _a.remove();\n (_b = entry.tooltipElement) == null ? void 0 : _b.remove();\n }\n this._renderedEntries = [];\n }\n maskElements(elements, color) {\n this.updateHighlight(elements.map((element) => ({ element, color })));\n }\n updateHighlight(entries) {\n if (this._highlightIsUpToDate(entries))\n return;\n this.clearHighlight();\n for (const entry of entries) {\n const highlightElement = this._createHighlightElement();\n this._glassPaneShadow.appendChild(highlightElement);\n let tooltipElement;\n if (entry.tooltipText) {\n tooltipElement = this._injectedScript.document.createElement("x-pw-tooltip");\n this._glassPaneShadow.appendChild(tooltipElement);\n tooltipElement.style.top = "0";\n tooltipElement.style.left = "0";\n tooltipElement.style.display = "flex";\n const lineElement = this._injectedScript.document.createElement("x-pw-tooltip-line");\n lineElement.textContent = entry.tooltipText;\n tooltipElement.appendChild(lineElement);\n }\n this._renderedEntries.push({ targetElement: entry.element, box: toDOMRect(entry.box), color: entry.color, borderColor: entry.borderColor, fadeDuration: entry.fadeDuration, cssStyle: entry.cssStyle, tooltipElement, highlightElement });\n }\n for (const entry of this._renderedEntries) {\n if (!entry.box && !entry.targetElement)\n continue;\n entry.box = entry.box || entry.targetElement.getBoundingClientRect();\n if (!entry.tooltipElement)\n continue;\n const { anchorLeft, anchorTop } = this.tooltipPosition(entry.box, entry.tooltipElement);\n entry.tooltipTop = anchorTop;\n entry.tooltipLeft = anchorLeft;\n }\n for (const entry of this._renderedEntries) {\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 = entry.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 (entry.borderColor)\n entry.highlightElement.style.border = "2px solid " + entry.borderColor;\n if (entry.fadeDuration)\n entry.highlightElement.style.animation = `pw-fade-out ${entry.fadeDuration}ms ease-out forwards`;\n if (entry.cssStyle)\n entry.highlightElement.style.cssText += ";" + entry.cssStyle;\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 firstBox() {\n var _a;\n return (_a = this._renderedEntries[0]) == null ? void 0 : _a.box;\n }\n firstTooltipBox() {\n const entry = this._renderedEntries[0];\n if (!entry || !entry.tooltipElement || entry.tooltipLeft === void 0 || entry.tooltipTop === void 0)\n return;\n return {\n x: entry.tooltipLeft,\n y: entry.tooltipTop,\n left: entry.tooltipLeft,\n top: entry.tooltipTop,\n width: entry.tooltipElement.offsetWidth,\n height: entry.tooltipElement.offsetHeight,\n bottom: entry.tooltipTop + entry.tooltipElement.offsetHeight,\n right: entry.tooltipLeft + entry.tooltipElement.offsetWidth,\n toJSON: () => {\n }\n };\n }\n // Note: there is a copy of this method in dialog.tsx. Please fix bugs in both places.\n tooltipPosition(box, tooltipElement) {\n const tooltipWidth = tooltipElement.offsetWidth;\n const tooltipHeight = tooltipElement.offsetHeight;\n const totalWidth = this._glassPaneElement.offsetWidth;\n const totalHeight = this._glassPaneElement.offsetHeight;\n let anchorLeft = Math.max(5, box.left);\n if (anchorLeft + tooltipWidth > totalWidth - 5)\n anchorLeft = totalWidth - tooltipWidth - 5;\n let anchorTop = Math.max(0, box.bottom) + 5;\n if (anchorTop + tooltipHeight > totalHeight - 5) {\n if (Math.max(0, box.top) > tooltipHeight + 5) {\n anchorTop = Math.max(0, box.top) - tooltipHeight - 5;\n } else {\n anchorTop = totalHeight - 5 - tooltipHeight;\n }\n }\n return { anchorLeft, anchorTop };\n }\n _highlightIsUpToDate(entries) {\n if (entries.length !== this._renderedEntries.length)\n return false;\n for (let i = 0; i < this._renderedEntries.length; ++i) {\n if (entries[i].element !== this._renderedEntries[i].targetElement)\n return false;\n if (entries[i].color !== this._renderedEntries[i].color)\n return false;\n if (entries[i].cssStyle !== this._renderedEntries[i].cssStyle)\n return false;\n const oldBox = this._renderedEntries[i].box;\n if (!oldBox)\n return false;\n const box = entries[i].box ? toDOMRect(entries[i].box) : entries[i].element.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 return this._injectedScript.document.createElement("x-pw-highlight");\n }\n appendChild(element) {\n this._glassPaneShadow.appendChild(element);\n }\n onGlassPaneClick(handler) {\n this._glassPaneElement.style.pointerEvents = "auto";\n this._glassPaneElement.style.backgroundColor = "rgba(0, 0, 0, 0.3)";\n this._glassPaneElement.addEventListener("click", handler);\n }\n offGlassPaneClick(handler) {\n this._glassPaneElement.style.pointerEvents = "none";\n this._glassPaneElement.style.backgroundColor = "transparent";\n this._glassPaneElement.removeEventListener("click", handler);\n }\n};\nfunction toDOMRect(box) {\n if (!box)\n return void 0;\n return new DOMRect(box.x, box.y, box.width, box.height);\n}\n\n// packages/injected/src/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/injected/src/selectorUtils.ts\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: "", normalized: "", immediate: [] };\n if (!shouldSkipForTextMatching(root)) {\n let currentImmediate = "";\n if (root instanceof HTMLInputElement && (root.type === "submit" || root.type === "button")) {\n value = { full: root.value, normalized: normalizeWhiteSpace(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 if (child.nodeType === Node.COMMENT_NODE) {\n continue;\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 if (value.full)\n value.normalized = normalizeWhiteSpace(value.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, normalized: normalizeWhiteSpace(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/injected/src/roleSelectorEngine.ts\nvar kSupportedAttributes = ["selected", "checked", "pressed", "expanded", "level", "disabled", "name", "description", "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.nameExact = attr.caseSensitive;\n break;\n }\n case "description": {\n if (attr.op === "<truthy>")\n throw new Error(`"description" attribute must have a value`);\n if (typeof attr.value !== "string" && !(attr.value instanceof RegExp))\n throw new Error(`"description" attribute must be a string or a regular expression`);\n options.description = attr.value;\n options.descriptionOp = attr.op;\n options.descriptionExact = 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.nameExact && options.nameOp === "=")\n options.nameOp = "*=";\n if (!matchesAttributePart(accessibleName, { name: "", jsonPath: [], op: options.nameOp || "=", value: options.name, caseSensitive: !!options.nameExact }))\n return;\n }\n if (options.description !== void 0) {\n const accessibleDescription = normalizeWhiteSpace(getElementAccessibleDescription(element, !!options.includeHidden));\n if (typeof options.description === "string")\n options.description = normalizeWhiteSpace(options.description);\n if (internal && !options.descriptionExact && options.descriptionOp === "=")\n options.descriptionOp = "*=";\n if (!matchesAttributePart(accessibleDescription, { name: "", jsonPath: [], op: options.descriptionOp || "=", value: options.description, caseSensitive: !!options.descriptionExact }))\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/injected/src/selectorEvaluator.ts\nvar SelectorEvaluatorImpl = class {\n constructor() {\n this._retainCacheCounter = 0;\n this._cacheText = /* @__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._engines = /* @__PURE__ */ new Map();\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 // TODO: we can implement efficient "query" by matching "args" and returning\n // all parents/descendants, just have to be careful with the ":scope" matching.\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) => elementText2.normalized.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) => elementText2.normalized.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/injected/src/selectorGenerator.ts\nvar kTextScoreRange = 10;\nvar kExactPenalty = kTextScoreRange / 2;\nvar kTestIdScore = 1;\nvar kOtherTestIdScore = 2;\nvar kIframeByAttributeScore = 10;\nvar kBeginPenalizedScore = 50;\nvar kRoleWithNameScore = 100;\nvar kPlaceholderScore = 120;\nvar kLabelScore = 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;\nvar kScoreThresholdForTextExpect = 1e3;\nfunction generateSelector(injectedScript, targetElement, options) {\n var _a;\n injectedScript._evaluator.begin();\n const cache = { allowText: /* @__PURE__ */ new Map(), disallowText: /* @__PURE__ */ new Map() };\n beginAriaCaches();\n beginDOMCaches();\n try {\n let selectors = [];\n if (options.forTextExpect) {\n let targetTokens = cssFallback(injectedScript, targetElement.ownerDocument.documentElement, options);\n for (let element = targetElement; element; element = parentElementOrShadowHost(element)) {\n const tokens = generateSelectorFor(cache, injectedScript, element, { ...options, noText: true });\n if (!tokens)\n continue;\n const score = combineScores(tokens);\n if (score <= kScoreThresholdForTextExpect) {\n targetTokens = tokens;\n break;\n }\n }\n selectors = [joinTokens(targetTokens)];\n } else {\n if (!targetElement.matches("input,textarea,select") && !targetElement.isContentEditable) {\n const interactiveParent = closestCrossShadow(targetElement, "button,select,input,[role=button],[role=checkbox],[role=radio],a,[role=link]", options.root);\n if (interactiveParent && isElementVisible(interactiveParent))\n targetElement = interactiveParent;\n }\n if (options.multiple) {\n const withText = generateSelectorFor(cache, injectedScript, targetElement, options);\n const withoutText = generateSelectorFor(cache, injectedScript, targetElement, { ...options, noText: true });\n let tokens = [withText, withoutText];\n cache.allowText.clear();\n cache.disallowText.clear();\n if (withText && hasCSSIdToken(withText))\n tokens.push(generateSelectorFor(cache, injectedScript, targetElement, { ...options, noCSSId: true }));\n if (withoutText && hasCSSIdToken(withoutText))\n tokens.push(generateSelectorFor(cache, injectedScript, targetElement, { ...options, noText: true, noCSSId: true }));\n tokens = tokens.filter(Boolean);\n if (!tokens.length) {\n const css = cssFallback(injectedScript, targetElement, options);\n tokens.push(css);\n if (hasCSSIdToken(css))\n tokens.push(cssFallback(injectedScript, targetElement, { ...options, noCSSId: true }));\n }\n selectors = [...new Set(tokens.map((t) => joinTokens(t)))];\n } else {\n const targetTokens = generateSelectorFor(cache, injectedScript, targetElement, options) || cssFallback(injectedScript, targetElement, options);\n selectors = [joinTokens(targetTokens)];\n }\n }\n const selector = selectors[0];\n const parsedSelector = injectedScript.parseSelector(selector);\n return {\n selector,\n selectors,\n elements: injectedScript.querySelectorAll(parsedSelector, (_a = options.root) != null ? _a : targetElement.ownerDocument)\n };\n } finally {\n endDOMCaches();\n endAriaCaches();\n injectedScript._evaluator.end();\n }\n}\nfunction generateSelectorFor(cache, injectedScript, targetElement, options) {\n var _a;\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 let result = null;\n const updateResult = (candidate) => {\n if (!result || combineScores(candidate) < combineScores(result))\n result = candidate;\n };\n const candidates = [];\n if (!options.noText) {\n for (const candidate of buildTextCandidates(injectedScript, targetElement, !options.isRecursive))\n candidates.push({ candidate, isTextCandidate: true });\n }\n for (const token of buildNoTextCandidates(injectedScript, targetElement, options)) {\n if (options.omitInternalEngines && token.engine.startsWith("internal:"))\n continue;\n candidates.push({ candidate: [token], isTextCandidate: false });\n }\n candidates.sort((a, b) => combineScores(a.candidate) - combineScores(b.candidate));\n for (const { candidate, isTextCandidate } of candidates) {\n const elements = injectedScript.querySelectorAll(injectedScript.parseSelector(joinTokens(candidate)), (_a = options.root) != null ? _a : targetElement.ownerDocument);\n if (!elements.includes(targetElement)) {\n continue;\n }\n if (elements.length === 1) {\n updateResult(candidate);\n break;\n }\n const index = elements.indexOf(targetElement);\n if (index > 5) {\n continue;\n }\n updateResult([...candidate, { engine: "nth", selector: String(index), score: kNthScore }]);\n if (options.isRecursive) {\n continue;\n }\n for (let parent = parentElementOrShadowHost(targetElement); parent && parent !== options.root; parent = parentElementOrShadowHost(parent)) {\n const filtered = elements.filter((e) => isInsideScope(parent, e) && e !== parent);\n const newIndex = filtered.indexOf(targetElement);\n if (filtered.length > 5 || newIndex === -1 || newIndex === index && filtered.length > 1) {\n continue;\n }\n const inParent = filtered.length === 1 ? candidate : [...candidate, { engine: "nth", selector: String(newIndex), score: kNthScore }];\n const idealSelectorForParent = { engine: "", selector: "", score: 1 };\n if (result && combineScores([idealSelectorForParent, ...inParent]) >= combineScores(result)) {\n continue;\n }\n const noText = !!options.noText || isTextCandidate;\n const cacheMap = noText ? cache.disallowText : cache.allowText;\n let parentTokens = cacheMap.get(parent);\n if (parentTokens === void 0) {\n parentTokens = generateSelectorFor(cache, injectedScript, parent, { ...options, isRecursive: true, noText }) || cssFallback(injectedScript, parent, options);\n cacheMap.set(parent, parentTokens);\n }\n if (!parentTokens)\n continue;\n updateResult([...parentTokens, ...inParent]);\n }\n }\n return result;\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}=${quoteCSSAttributeValue(element.getAttribute(attr))}]`, score: kOtherTestIdScore });\n }\n if (!options.noCSSId) {\n const idAttr = element.getAttribute("id");\n if (idAttr && !isGuidLike(idAttr))\n candidates.push({ engine: "css", selector: makeSelectorForId(idAttr), score: kCSSIdScore });\n }\n candidates.push({ engine: "css", selector: escapeNodeName(element), 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: `${escapeNodeName(element)}[${attribute}=${quoteCSSAttributeValue(element.getAttribute(attribute))}]`, score: kIframeByAttributeScore });\n }\n if (element.getAttribute(options.testIdAttributeName))\n candidates.push({ engine: "css", selector: `[${options.testIdAttributeName}=${quoteCSSAttributeValue(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, true)}]`, score: kPlaceholderScoreExact });\n for (const alternative of suitableTextAlternatives(input.placeholder))\n candidates.push({ engine: "internal:attr", selector: `[placeholder=${escapeForAttributeSelector(alternative.text, false)}]`, score: kPlaceholderScore - alternative.scoreBonus });\n }\n }\n const labels = getElementLabels(injectedScript._evaluator._cacheText, element);\n for (const label of labels) {\n const labelText = label.normalized;\n candidates.push({ engine: "internal:label", selector: escapeForTextSelector(labelText, true), score: kLabelScoreExact });\n for (const alternative of suitableTextAlternatives(labelText))\n candidates.push({ engine: "internal:label", selector: escapeForTextSelector(alternative.text, false), score: kLabelScore - alternative.scoreBonus });\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("name") && ["BUTTON", "FORM", "FIELDSET", "FRAME", "IFRAME", "INPUT", "KEYGEN", "OBJECT", "OUTPUT", "SELECT", "TEXTAREA", "MAP", "META", "PARAM"].includes(element.nodeName))\n candidates.push({ engine: "css", selector: `${escapeNodeName(element)}[name=${quoteCSSAttributeValue(element.getAttribute("name"))}]`, score: kCSSInputTypeNameScore });\n if (["INPUT", "TEXTAREA"].includes(element.nodeName) && element.getAttribute("type") !== "hidden") {\n if (element.getAttribute("type"))\n candidates.push({ engine: "css", selector: `${escapeNodeName(element)}[type=${quoteCSSAttributeValue(element.getAttribute("type"))}]`, score: kCSSInputTypeNameScore });\n }\n if (["INPUT", "TEXTAREA", "SELECT"].includes(element.nodeName) && element.getAttribute("type") !== "hidden")\n candidates.push({ engine: "css", selector: escapeNodeName(element), 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 title = element.getAttribute("title");\n if (title) {\n candidates.push([{ engine: "internal:attr", selector: `[title=${escapeForAttributeSelector(title, true)}]`, score: kTitleScoreExact }]);\n for (const alternative of suitableTextAlternatives(title))\n candidates.push([{ engine: "internal:attr", selector: `[title=${escapeForAttributeSelector(alternative.text, false)}]`, score: kTitleScore - alternative.scoreBonus }]);\n }\n const alt = element.getAttribute("alt");\n if (alt && ["APPLET", "AREA", "IMG", "INPUT"].includes(element.nodeName)) {\n candidates.push([{ engine: "internal:attr", selector: `[alt=${escapeForAttributeSelector(alt, true)}]`, score: kAltTextScoreExact }]);\n for (const alternative of suitableTextAlternatives(alt))\n candidates.push([{ engine: "internal:attr", selector: `[alt=${escapeForAttributeSelector(alternative.text, false)}]`, score: kAltTextScore - alternative.scoreBonus }]);\n }\n const text = elementText(injectedScript._evaluator._cacheText, element).normalized;\n const textAlternatives = text ? suitableTextAlternatives(text) : [];\n if (text) {\n if (isTargetNode) {\n if (text.length <= 80)\n candidates.push([{ engine: "internal:text", selector: escapeForTextSelector(text, true), score: kTextScoreExact }]);\n for (const alternative of textAlternatives)\n candidates.push([{ engine: "internal:text", selector: escapeForTextSelector(alternative.text, false), score: kTextScore - alternative.scoreBonus }]);\n }\n const cssToken = { engine: "css", selector: escapeNodeName(element), score: kCSSTagNameScore };\n for (const alternative of textAlternatives)\n candidates.push([cssToken, { engine: "internal:has-text", selector: escapeForTextSelector(alternative.text, false), score: kTextScore - alternative.scoreBonus }]);\n if (isTargetNode && text.length <= 80) {\n const re = new RegExp("^" + escapeRegExp(text) + "$");\n candidates.push([cssToken, { engine: "internal:has-text", selector: escapeForTextSelector(re, false), score: kTextScoreRegex }]);\n }\n }\n const ariaRole = getAriaRole(element);\n if (ariaRole && !["none", "presentation"].includes(ariaRole)) {\n const ariaName = getElementAccessibleName(element, false);\n if (ariaName && !ariaName.match(/^\\p{Co}+$/u)) {\n const roleToken = { engine: "internal:role", selector: `${ariaRole}[name=${escapeForAttributeSelector(ariaName, true)}]`, score: kRoleWithNameScoreExact };\n candidates.push([roleToken]);\n for (const alternative of suitableTextAlternatives(ariaName))\n candidates.push([{ engine: "internal:role", selector: `${ariaRole}[name=${escapeForAttributeSelector(alternative.text, false)}]`, score: kRoleWithNameScore - alternative.scoreBonus }]);\n const ariaDescription = getElementAccessibleDescription(element, false);\n if (ariaDescription) {\n candidates.push([{ engine: "internal:role", selector: `${ariaRole}[name=${escapeForAttributeSelector(ariaName, true)}][description=${escapeForAttributeSelector(ariaDescription, true)}]`, score: kRoleWithNameScoreExact + 1 }]);\n for (const alternative of suitableTextAlternatives(ariaName))\n candidates.push([{ engine: "internal:role", selector: `${ariaRole}[name=${escapeForAttributeSelector(alternative.text, false)}][description=${escapeForAttributeSelector(ariaDescription, true)}]`, score: kRoleWithNameScore - alternative.scoreBonus + 1 }]);\n }\n } else {\n const roleToken = { engine: "internal:role", selector: `${ariaRole}`, score: kRoleWithoutNameScore };\n const ariaDescription = getElementAccessibleDescription(element, false);\n if (ariaDescription)\n candidates.push([{ engine: "internal:role", selector: `${ariaRole}[description=${escapeForAttributeSelector(ariaDescription, true)}]`, score: kRoleWithoutNameScore + 1 }]);\n for (const alternative of textAlternatives)\n candidates.push([roleToken, { engine: "internal:has-text", selector: escapeForTextSelector(alternative.text, false), score: kTextScore - alternative.scoreBonus }]);\n if (isTargetNode && text.length <= 80) {\n const re = new RegExp("^" + escapeRegExp(text) + "$");\n candidates.push([roleToken, { engine: "internal:has-text", selector: escapeForTextSelector(re, false), score: kTextScoreRegex }]);\n }\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=${quoteCSSAttributeValue(id)}]`;\n}\nfunction hasCSSIdToken(tokens) {\n return tokens.some((token) => token.engine === "css" && (token.selector.startsWith("#") || token.selector.startsWith(\'[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 let bestTokenForLevel = "";\n if (element.id && !options.noCSSId) {\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].map(escapeClassName);\n for (let i = 0; i < classes.length; ++i) {\n const token = "." + 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 nodeName = element.nodeName;\n const sameTagSiblings = siblings.filter((sibling) => sibling.nodeName === nodeName);\n const token = sameTagSiblings.indexOf(element) === 0 ? escapeNodeName(element) : `${escapeNodeName(element)}: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 = escapeNodeName(element);\n }\n tokens.unshift(bestTokenForLevel);\n }\n return makeStrict(uniqueCSSSelector());\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 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 trimWordBoundary(text, maxLength) {\n if (text.length <= maxLength)\n return text;\n text = text.substring(0, maxLength);\n const match = text.match(/^(.*)\\b(.+?)$/);\n if (!match)\n return "";\n return match[1].trimEnd();\n}\nfunction suitableTextAlternatives(text) {\n let result = [];\n {\n const match = text.match(/^([\\d.,]+)[^.,\\w]/);\n const leadingNumberLength = match ? match[1].length : 0;\n if (leadingNumberLength) {\n const alt = trimWordBoundary(text.substring(leadingNumberLength).trimStart(), 80);\n result.push({ text: alt, scoreBonus: alt.length <= 30 ? 2 : 1 });\n }\n }\n {\n const match = text.match(/[^.,\\w]([\\d.,]+)$/);\n const trailingNumberLength = match ? match[1].length : 0;\n if (trailingNumberLength) {\n const alt = trimWordBoundary(text.substring(0, text.length - trailingNumberLength).trimEnd(), 80);\n result.push({ text: alt, scoreBonus: alt.length <= 30 ? 2 : 1 });\n }\n }\n if (text.length <= 30) {\n result.push({ text, scoreBonus: 0 });\n } else {\n result.push({ text: trimWordBoundary(text, 80), scoreBonus: 0 });\n result.push({ text: trimWordBoundary(text, 30), scoreBonus: 1 });\n }\n result = result.filter((r) => r.text);\n if (!result.length)\n result.push({ text: text.substring(0, 80), scoreBonus: 0 });\n return result;\n}\nfunction escapeNodeName(node) {\n return node.nodeName.toLocaleLowerCase().replace(/[:\\.]/g, (char) => "\\\\" + char);\n}\nfunction escapeClassName(className) {\n let result = "";\n for (let i = 0; i < className.length; i++)\n result += cssEscapeCharacter(className, i);\n return result;\n}\nfunction cssEscapeCharacter(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}\n\n// packages/injected/src/xpathSelectorEngine.ts\nvar XPathEngine = {\n queryAll(root, selector) {\n if (selector.startsWith("/") && root.nodeType !== Node.DOCUMENT_NODE)\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/isomorphic/locatorUtils.ts\nfunction getByAttributeTextSelector(attrName, text, options) {\n return `internal:attr=[${attrName}=${escapeForAttributeSelector(text, (options == null ? void 0 : options.exact) || false)}]`;\n}\nfunction getByTestIdSelector(testIdAttributeName, testId) {\n return `internal:testid=[${testIdAttributeName}=${escapeForAttributeSelector(testId, true)}]`;\n}\nfunction getByLabelSelector(text, options) {\n return "internal:label=" + escapeForTextSelector(text, !!(options == null ? void 0 : options.exact));\n}\nfunction getByAltTextSelector(text, options) {\n return getByAttributeTextSelector("alt", text, options);\n}\nfunction getByTitleSelector(text, options) {\n return getByAttributeTextSelector("title", text, options);\n}\nfunction getByPlaceholderSelector(text, options) {\n return getByAttributeTextSelector("placeholder", text, options);\n}\nfunction getByTextSelector(text, options) {\n return "internal:text=" + escapeForTextSelector(text, !!(options == null ? void 0 : options.exact));\n}\nfunction getByRoleSelector(role, options = {}) {\n const props = [];\n if (options.checked !== void 0)\n props.push(["checked", String(options.checked)]);\n if (options.disabled !== void 0)\n props.push(["disabled", String(options.disabled)]);\n if (options.selected !== void 0)\n props.push(["selected", String(options.selected)]);\n if (options.expanded !== void 0)\n props.push(["expanded", String(options.expanded)]);\n if (options.includeHidden !== void 0)\n props.push(["include-hidden", String(options.includeHidden)]);\n if (options.level !== void 0)\n props.push(["level", String(options.level)]);\n if (options.name !== void 0)\n props.push(["name", escapeForAttributeSelector(options.name, !!options.exact)]);\n if (options.description !== void 0)\n props.push(["description", escapeForAttributeSelector(options.description, !!options.exact)]);\n if (options.pressed !== void 0)\n props.push(["pressed", String(options.pressed)]);\n return `internal:role=${role}${props.map(([n, v]) => `[${n}=${v}]`).join("")}`;\n}\n\n// packages/injected/src/consoleApi.ts\nvar selectorSymbol = Symbol("selector");\nselectorSymbol;\nvar _Locator = class _Locator {\n constructor(injectedScript, selector, options) {\n if (options == null ? void 0 : options.hasText)\n selector += ` >> internal:has-text=${escapeForTextSelector(options.hasText, false)}`;\n if (options == null ? void 0 : options.hasNotText)\n selector += ` >> internal:has-not-text=${escapeForTextSelector(options.hasNotText, false)}`;\n if (options == null ? void 0 : options.has)\n selector += ` >> internal:has=` + JSON.stringify(options.has[selectorSymbol]);\n if (options == null ? void 0 : options.hasNot)\n selector += ` >> internal:has-not=` + JSON.stringify(options.hasNot[selectorSymbol]);\n if ((options == null ? void 0 : options.visible) !== void 0)\n selector += ` >> visible=${options.visible ? "true" : "false"}`;\n this[selectorSymbol] = selector;\n if (selector) {\n const parsed = injectedScript.parseSelector(selector);\n this.element = injectedScript.querySelector(parsed, injectedScript.document, false);\n this.elements = injectedScript.querySelectorAll(parsed, injectedScript.document);\n }\n const selectorBase = selector;\n const self = this;\n self.locator = (selector2, options2) => {\n return new _Locator(injectedScript, selectorBase ? selectorBase + " >> " + selector2 : selector2, options2);\n };\n self.getByTestId = (testId) => self.locator(getByTestIdSelector(injectedScript.testIdAttributeNameForStrictErrorAndConsoleCodegen(), testId));\n self.getByAltText = (text, options2) => self.locator(getByAltTextSelector(text, options2));\n self.getByLabel = (text, options2) => self.locator(getByLabelSelector(text, options2));\n self.getByPlaceholder = (text, options2) => self.locator(getByPlaceholderSelector(text, options2));\n self.getByText = (text, options2) => self.locator(getByTextSelector(text, options2));\n self.getByTitle = (text, options2) => self.locator(getByTitleSelector(text, options2));\n self.getByRole = (role, options2 = {}) => self.locator(getByRoleSelector(role, options2));\n self.filter = (options2) => new _Locator(injectedScript, selector, options2);\n self.first = () => self.locator("nth=0");\n self.last = () => self.locator("nth=-1");\n self.nth = (index) => self.locator(`nth=${index}`);\n self.and = (locator) => new _Locator(injectedScript, selectorBase + ` >> internal:and=` + JSON.stringify(locator[selectorSymbol]));\n self.or = (locator) => new _Locator(injectedScript, selectorBase + ` >> internal:or=` + JSON.stringify(locator[selectorSymbol]));\n }\n};\nvar Locator = _Locator;\nvar ConsoleAPI = class {\n constructor(injectedScript) {\n this._injectedScript = injectedScript;\n }\n install() {\n if (this._injectedScript.window.playwright)\n return;\n this._injectedScript.window.playwright = {\n $: (selector, strict) => this._querySelector(selector, !!strict),\n $$: (selector) => this._querySelectorAll(selector),\n inspect: (selector) => this._inspect(selector),\n selector: (element) => this._selector(element),\n generateLocator: (element, language) => this._generateLocator(element, language),\n ariaSnapshot: (element, options) => {\n return this._injectedScript.ariaSnapshot(element || this._injectedScript.document.body, options || { mode: "default" });\n },\n resume: () => this._resume(),\n ...new Locator(this._injectedScript, "")\n };\n delete this._injectedScript.window.playwright.filter;\n delete this._injectedScript.window.playwright.first;\n delete this._injectedScript.window.playwright.last;\n delete this._injectedScript.window.playwright.nth;\n delete this._injectedScript.window.playwright.and;\n delete this._injectedScript.window.playwright.or;\n }\n _querySelector(selector, strict) {\n if (typeof selector !== "string")\n throw new Error(`Usage: playwright.query(\'Playwright >> selector\').`);\n const parsed = this._injectedScript.parseSelector(selector);\n return this._injectedScript.querySelector(parsed, this._injectedScript.document, strict);\n }\n _querySelectorAll(selector) {\n if (typeof selector !== "string")\n throw new Error(`Usage: playwright.$$(\'Playwright >> selector\').`);\n const parsed = this._injectedScript.parseSelector(selector);\n return this._injectedScript.querySelectorAll(parsed, this._injectedScript.document);\n }\n _inspect(selector) {\n if (typeof selector !== "string")\n throw new Error(`Usage: playwright.inspect(\'Playwright >> selector\').`);\n this._injectedScript.window.inspect(this._querySelector(selector, false));\n }\n _selector(element) {\n if (!(element instanceof Element))\n throw new Error(`Usage: playwright.selector(element).`);\n return this._injectedScript.generateSelectorSimple(element);\n }\n _generateLocator(element, language) {\n if (!(element instanceof Element))\n throw new Error(`Usage: playwright.locator(element).`);\n const selector = this._injectedScript.generateSelectorSimple(element);\n return asLocator(language || "javascript", selector);\n }\n _resume() {\n if (!this._injectedScript.window.__pw_resume)\n return false;\n this._injectedScript.window.__pw_resume().catch(() => {\n });\n }\n};\n\n// packages/isomorphic/utilityScriptSerializers.ts\nfunction isRegExp2(obj) {\n try {\n return obj instanceof RegExp || Object.prototype.toString.call(obj) === "[object RegExp]";\n } catch (error) {\n return false;\n }\n}\nfunction isDate(obj) {\n try {\n return obj instanceof Date || Object.prototype.toString.call(obj) === "[object Date]";\n } catch (error) {\n return false;\n }\n}\nfunction isURL(obj) {\n try {\n return obj instanceof URL || Object.prototype.toString.call(obj) === "[object URL]";\n } catch (error) {\n return false;\n }\n}\nfunction isError(obj) {\n var _a;\n try {\n return obj instanceof Error || obj && ((_a = Object.getPrototypeOf(obj)) == null ? void 0 : _a.name) === "Error";\n } catch (error) {\n return false;\n }\n}\nfunction isTypedArray(obj, constructor) {\n try {\n return obj instanceof constructor || Object.prototype.toString.call(obj) === `[object ${constructor.name}]`;\n } catch (error) {\n return false;\n }\n}\nfunction isArrayBuffer(obj) {\n try {\n return obj instanceof ArrayBuffer || Object.prototype.toString.call(obj) === "[object ArrayBuffer]";\n } catch (error) {\n return false;\n }\n}\nvar typedArrayConstructors = {\n i8: Int8Array,\n ui8: Uint8Array,\n ui8c: Uint8ClampedArray,\n i16: Int16Array,\n ui16: Uint16Array,\n i32: Int32Array,\n ui32: Uint32Array,\n // TODO: add Float16Array once it\'s in baseline\n f32: Float32Array,\n f64: Float64Array,\n bi64: BigInt64Array,\n bui64: BigUint64Array\n};\nfunction typedArrayToBase64(array) {\n if ("toBase64" in array)\n return array.toBase64();\n const binary = Array.from(new Uint8Array(array.buffer, array.byteOffset, array.byteLength)).map((b) => String.fromCharCode(b)).join("");\n return btoa(binary);\n}\nfunction base64ToTypedArray(base64, TypedArrayConstructor) {\n const binary = atob(base64);\n const bytes = new Uint8Array(binary.length);\n for (let i = 0; i < binary.length; i++)\n bytes[i] = binary.charCodeAt(i);\n return new TypedArrayConstructor(bytes.buffer);\n}\nfunction parseEvaluationResultValue(value, handles = [], refs = /* @__PURE__ */ new Map()) {\n if (Object.is(value, void 0))\n return void 0;\n if (typeof value === "object" && value) {\n if ("ref" in value)\n return refs.get(value.ref);\n if ("v" in value) {\n if (value.v === "undefined")\n return void 0;\n if (value.v === "null")\n return null;\n if (value.v === "NaN")\n return NaN;\n if (value.v === "Infinity")\n return Infinity;\n if (value.v === "-Infinity")\n return -Infinity;\n if (value.v === "-0")\n return -0;\n return void 0;\n }\n if ("d" in value) {\n return new Date(value.d);\n }\n if ("u" in value)\n return new URL(value.u);\n if ("bi" in value)\n return BigInt(value.bi);\n if ("e" in value) {\n const error = new Error(value.e.m);\n error.name = value.e.n;\n error.stack = value.e.s;\n return error;\n }\n if ("r" in value)\n return new RegExp(value.r.p, value.r.f);\n if ("a" in value) {\n const result = [];\n refs.set(value.id, result);\n for (const a of value.a)\n result.push(parseEvaluationResultValue(a, handles, refs));\n return result;\n }\n if ("o" in value) {\n const result = {};\n refs.set(value.id, result);\n for (const { k, v } of value.o) {\n if (k === "__proto__")\n continue;\n result[k] = parseEvaluationResultValue(v, handles, refs);\n }\n return result;\n }\n if ("h" in value)\n return handles[value.h];\n if ("ta" in value)\n return base64ToTypedArray(value.ta.b, typedArrayConstructors[value.ta.k]);\n if ("ab" in value)\n return base64ToTypedArray(value.ab.b, Uint8Array).buffer;\n }\n return value;\n}\nfunction serializeAsCallArgument(value, handleSerializer) {\n return serialize(value, handleSerializer, { visited: /* @__PURE__ */ new Map(), lastId: 0 });\n}\nfunction serialize(value, handleSerializer, visitorInfo) {\n if (value && typeof value === "object") {\n if (typeof globalThis.Window === "function" && value instanceof globalThis.Window)\n return "ref: <Window>";\n if (typeof globalThis.Document === "function" && value instanceof globalThis.Document)\n return "ref: <Document>";\n if (typeof globalThis.Node === "function" && value instanceof globalThis.Node)\n return "ref: <Node>";\n }\n return innerSerialize(value, handleSerializer, visitorInfo);\n}\nfunction innerSerialize(value, handleSerializer, visitorInfo) {\n var _a;\n const result = handleSerializer(value);\n if ("fallThrough" in result)\n value = result.fallThrough;\n else\n return result;\n if (typeof value === "symbol")\n return { v: "undefined" };\n if (Object.is(value, void 0))\n return { v: "undefined" };\n if (Object.is(value, null))\n return { v: "null" };\n if (Object.is(value, NaN))\n return { v: "NaN" };\n if (Object.is(value, Infinity))\n return { v: "Infinity" };\n if (Object.is(value, -Infinity))\n return { v: "-Infinity" };\n if (Object.is(value, -0))\n return { v: "-0" };\n if (typeof value === "boolean")\n return value;\n if (typeof value === "number")\n return value;\n if (typeof value === "string")\n return value;\n if (typeof value === "bigint")\n return { bi: value.toString() };\n if (isError(value)) {\n let stack;\n if ((_a = value.stack) == null ? void 0 : _a.startsWith(value.name + ": " + value.message)) {\n stack = value.stack;\n } else {\n stack = `${value.name}: ${value.message}\n${value.stack}`;\n }\n return { e: { n: value.name, m: value.message, s: stack } };\n }\n if (isDate(value))\n return { d: value.toJSON() };\n if (isURL(value))\n return { u: value.toJSON() };\n if (isRegExp2(value))\n return { r: { p: value.source, f: value.flags } };\n for (const [k, ctor] of Object.entries(typedArrayConstructors)) {\n if (isTypedArray(value, ctor))\n return { ta: { b: typedArrayToBase64(value), k } };\n }\n if (isArrayBuffer(value))\n return { ab: { b: typedArrayToBase64(new Uint8Array(value)) } };\n const id = visitorInfo.visited.get(value);\n if (id)\n return { ref: id };\n if (Array.isArray(value)) {\n const a = [];\n const id2 = ++visitorInfo.lastId;\n visitorInfo.visited.set(value, id2);\n for (let i = 0; i < value.length; ++i)\n a.push(serialize(value[i], handleSerializer, visitorInfo));\n return { a, id: id2 };\n }\n if (typeof value === "object") {\n const o = [];\n const id2 = ++visitorInfo.lastId;\n visitorInfo.visited.set(value, id2);\n for (const name of Object.keys(value)) {\n let item;\n try {\n item = value[name];\n } catch (e) {\n continue;\n }\n if (name === "toJSON" && typeof item === "function")\n o.push({ k: name, v: { o: [], id: 0 } });\n else\n o.push({ k: name, v: serialize(item, handleSerializer, visitorInfo) });\n }\n let jsonWrapper;\n try {\n if (o.length === 0 && value.toJSON && typeof value.toJSON === "function")\n jsonWrapper = { value: value.toJSON() };\n } catch (e) {\n }\n if (jsonWrapper)\n return innerSerialize(jsonWrapper.value, handleSerializer, visitorInfo);\n return { o, id: id2 };\n }\n}\n\n// packages/injected/src/utilityScript.ts\nvar UtilityScript = class {\n constructor(global, isUnderTest) {\n var _a, _b, _c, _d, _e, _f, _g, _h;\n this.global = global;\n this.isUnderTest = isUnderTest;\n if (global.__pwClock) {\n this.builtins = global.__pwClock.builtins;\n } else {\n this.builtins = {\n setTimeout: (_a = global.setTimeout) == null ? void 0 : _a.bind(global),\n clearTimeout: (_b = global.clearTimeout) == null ? void 0 : _b.bind(global),\n setInterval: (_c = global.setInterval) == null ? void 0 : _c.bind(global),\n clearInterval: (_d = global.clearInterval) == null ? void 0 : _d.bind(global),\n requestAnimationFrame: (_e = global.requestAnimationFrame) == null ? void 0 : _e.bind(global),\n cancelAnimationFrame: (_f = global.cancelAnimationFrame) == null ? void 0 : _f.bind(global),\n requestIdleCallback: (_g = global.requestIdleCallback) == null ? void 0 : _g.bind(global),\n cancelIdleCallback: (_h = global.cancelIdleCallback) == null ? void 0 : _h.bind(global),\n performance: global.performance,\n Intl: global.Intl,\n Date: global.Date,\n AbortSignal: global.AbortSignal\n };\n }\n if (this.isUnderTest)\n global.builtins = this.builtins;\n }\n evaluate(isFunction, returnByValue, expression, argCount, ...argsAndHandles) {\n const args = argsAndHandles.slice(0, argCount);\n const handles = argsAndHandles.slice(argCount);\n const parameters = [];\n for (let i = 0; i < args.length; i++)\n parameters[i] = parseEvaluationResultValue(args[i], handles);\n let result = this.global.eval(expression);\n if (isFunction === true) {\n result = result(...parameters);\n } else if (isFunction === false) {\n result = result;\n } else {\n if (typeof result === "function")\n result = result(...parameters);\n }\n return returnByValue ? this._promiseAwareJsonValueNoThrow(result) : result;\n }\n jsonValue(returnByValue, value) {\n if (value === void 0)\n return void 0;\n return serializeAsCallArgument(value, (value2) => ({ fallThrough: value2 }));\n }\n _promiseAwareJsonValueNoThrow(value) {\n const safeJson = (value2) => {\n try {\n return this.jsonValue(true, value2);\n } catch (e) {\n return void 0;\n }\n };\n if (value && typeof value === "object" && typeof value.then === "function") {\n return (async () => {\n const promiseValue = await value;\n return safeJson(promiseValue);\n })();\n }\n return safeJson(value);\n }\n};\n\n// packages/injected/src/injectedScript.ts\nvar InjectedScript = class {\n constructor(window, options) {\n this._testIdAttributeNameForStrictErrorAndConsoleCodegen = "data-testid";\n this._lastAriaSnapshotForTrack = /* @__PURE__ */ new Map();\n // Recorder must use any external dependencies through InjectedScript.\n // Otherwise it will end up with a copy of all modules it uses, and any\n // module-level globals will be duplicated, which leads to subtle bugs.\n this.utils = {\n asLocator,\n cacheNormalizedWhitespaces,\n elementText,\n getAriaRole,\n getElementAccessibleDescription,\n getElementAccessibleName,\n isElementVisible,\n isInsideScope,\n normalizeWhiteSpace,\n parseAriaSnapshot,\n generateAriaTree,\n findNewElement,\n // Builtins protect injected code from clock emulation.\n builtins: null\n };\n this.window = window;\n this.document = window.document;\n this.isUnderTest = options.isUnderTest;\n this.utils.builtins = new UtilityScript(window, options.isUnderTest).builtins;\n this._sdkLanguage = options.sdkLanguage;\n this._testIdAttributeNameForStrictErrorAndConsoleCodegen = options.testIdAttributeName;\n this._evaluator = new SelectorEvaluatorImpl();\n this.consoleApi = new ConsoleAPI(this);\n this.onGlobalListenersRemoved = /* @__PURE__ */ new Set();\n this._autoClosingTags = /* @__PURE__ */ new Set(["AREA", "BASE", "BR", "COL", "COMMAND", "EMBED", "HR", "IMG", "INPUT", "KEYGEN", "LINK", "MENUITEM", "META", "PARAM", "SOURCE", "TRACK", "WBR"]);\n this._booleanAttributes = /* @__PURE__ */ new Set(["checked", "selected", "disabled", "readonly", "multiple"]);\n this._eventTypes = /* @__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 ["deviceorientation", "deviceorientation"],\n ["deviceorientationabsolute", "deviceorientation"],\n ["devicemotion", "devicemotion"]\n ]);\n this._hoverHitTargetInterceptorEvents = /* @__PURE__ */ new Set(["mousemove"]);\n this._tapHitTargetInterceptorEvents = /* @__PURE__ */ new Set(["pointerdown", "pointerup", "touchstart", "touchend", "touchcancel"]);\n this._mouseHitTargetInterceptorEvents = /* @__PURE__ */ new Set(["mousedown", "mouseup", "pointerdown", "pointerup", "click", "auxclick", "dblclick", "contextmenu"]);\n this._allHitTargetInterceptorEvents = /* @__PURE__ */ new Set([...this._hoverHitTargetInterceptorEvents, ...this._tapHitTargetInterceptorEvents, ...this._mouseHitTargetInterceptorEvents]);\n this._engines = /* @__PURE__ */ new Map();\n this._engines.set("xpath", XPathEngine);\n this._engines.set("xpath:light", XPathEngine);\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:chain", this._createInternalChainEngine());\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 this._engines.set("internal:describe", this._createDescribeEngine());\n this._engines.set("aria-ref", this._createAriaRefEngine());\n for (const { name, source } of options.customEngines)\n this._engines.set(name, this.eval(source));\n this._stableRafCount = options.stableRafCount;\n this._browserName = options.browserName;\n this._shouldPrependErrorPrefix = !!options.shouldPrependErrorPrefix;\n this._isUtilityWorld = !!options.isUtilityWorld;\n setGlobalOptions({ browserNameForWorkarounds: options.browserName });\n this._setupGlobalListenersRemovalDetection();\n this._setupHitTargetInterceptors();\n if (this.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);\n }\n generateSelectorSimple(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 this.checkDeprecatedSelectorUsage(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 ariaSnapshot(node, options) {\n return this.incrementalAriaSnapshot(node, options).full;\n }\n incrementalAriaSnapshot(node, options) {\n if (node.nodeType !== Node.ELEMENT_NODE)\n throw this.createStacklessError("Can only capture aria snapshot of Element nodes.");\n const ariaSnapshot = generateAriaTree(node, options);\n const rendered = renderAriaTree(ariaSnapshot, options);\n let incremental;\n if (options.track) {\n const previousSnapshot = this._lastAriaSnapshotForTrack.get(options.track);\n if (previousSnapshot)\n incremental = renderAriaTree(ariaSnapshot, options, previousSnapshot).text;\n this._lastAriaSnapshotForTrack.set(options.track, ariaSnapshot);\n }\n this._lastAriaSnapshotForQuery = ariaSnapshot;\n return { full: rendered.text, incremental, iframeRefs: ariaSnapshot.iframeRefs, iframeDepths: rendered.iframeDepths };\n }\n ariaSnapshotForRecorder() {\n const tree = generateAriaTree(this.document.body, { mode: "ai" });\n const { text: ariaSnapshot } = renderAriaTree(tree, { mode: "ai" });\n return { ariaSnapshot, refs: tree.refs };\n }\n getAllElementsMatchingExpectAriaTemplate(document, template) {\n return getAllElementsMatchingExpectAriaTemplate(document.documentElement, template);\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 if (root.nodeType === 11 && selector.parts.length === 1 && selector.parts[0].name === "css" && selector.parts[0].source === ":scope")\n return [root];\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 _createDescribeEngine() {\n const queryAll = (root) => {\n if (root.nodeType !== 1)\n return [];\n return [root];\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 const visible = body === "true";\n return isElementVisible(root) === visible ? [root] : [];\n };\n return { queryAll };\n }\n _createInternalChainEngine() {\n const queryAll = (root, body) => {\n return this.querySelectorAll(body.parsed, 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 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 this.utils.builtins.requestAnimationFrame(() => {\n });\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") && !element.isContentEditable) {\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("a, input, textarea, button, select, [role=link], [role=button], [role=checkbox], [role=radio]") && !element.isContentEditable) {\n const enclosingLabel = element.closest("label");\n if (enclosingLabel && enclosingLabel.control)\n element = enclosingLabel.control;\n }\n }\n return element;\n }\n async checkElementStates(node, states) {\n if (states.includes("stable")) {\n const stableResult = await this._checkElementIsStable(node);\n if (stableResult === false)\n return { missingState: "stable" };\n if (stableResult === "error:notconnected")\n return "error:notconnected";\n }\n for (const state of states) {\n if (state !== "stable") {\n const result = this.elementState(node, state);\n if (result.received === "error:notconnected")\n return "error:notconnected";\n if (!result.matches)\n return { missingState: state };\n }\n }\n }\n async _checkElementIsStable(node) {\n const continuePolling = Symbol("continuePolling");\n let lastRect;\n let stableRafCounter = 0;\n let lastTime = 0;\n const check = () => {\n const element = this.retarget(node, "no-follow-label");\n if (!element)\n return "error:notconnected";\n const time = this.utils.builtins.performance.now();\n if (this._stableRafCount > 1 && time - lastTime < 15)\n return 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 if (lastRect) {\n const samePosition = rect.x === lastRect.x && rect.y === lastRect.y && rect.width === lastRect.width && rect.height === lastRect.height;\n if (!samePosition)\n return false;\n if (++stableRafCounter >= this._stableRafCount)\n return true;\n }\n lastRect = rect;\n return continuePolling;\n };\n let fulfill;\n let reject;\n const result = new Promise((f, r) => {\n fulfill = f;\n reject = r;\n });\n const raf = () => {\n try {\n const success = check();\n if (success !== continuePolling)\n fulfill(success);\n else\n this.utils.builtins.requestAnimationFrame(raf);\n } catch (e) {\n reject(e);\n }\n };\n this.utils.builtins.requestAnimationFrame(raf);\n return result;\n }\n _createAriaRefEngine() {\n const queryAll = (root, selector) => {\n var _a, _b;\n const result = (_b = (_a = this._lastAriaSnapshotForQuery) == null ? void 0 : _a.elements) == null ? void 0 : _b.get(selector);\n return result && result.isConnected ? [result] : [];\n };\n return { queryAll };\n }\n elementState(node, state) {\n const element = this.retarget(node, ["visible", "hidden"].includes(state) ? "none" : "follow-label");\n if (!element || !element.isConnected) {\n if (state === "hidden")\n return { matches: true, received: "hidden" };\n return { matches: false, received: "error:notconnected" };\n }\n if (state === "visible" || state === "hidden") {\n const visible = isElementVisible(element);\n return {\n matches: state === "visible" ? visible : !visible,\n received: visible ? "visible" : "hidden"\n };\n }\n if (state === "disabled" || state === "enabled") {\n const disabled = getAriaDisabled(element);\n return {\n matches: state === "disabled" ? disabled : !disabled,\n received: disabled ? "disabled" : "enabled"\n };\n }\n if (state === "editable") {\n const disabled = getAriaDisabled(element);\n const readonly = getReadonly(element);\n if (readonly === "error")\n throw this.createStacklessError("Element is not an <input>, <textarea>, <select> or [contenteditable] and does not have a role allowing [aria-readonly]");\n return {\n matches: !disabled && !readonly,\n received: disabled ? "disabled" : readonly ? "readOnly" : "editable"\n };\n }\n if (state === "checked" || state === "unchecked") {\n const need = state === "checked";\n const checked = getCheckedWithoutMixed(element);\n if (checked === "error")\n throw this.createStacklessError("Not a checkbox or radio button");\n const isRadio = element.nodeName === "INPUT" && element.type === "radio";\n return {\n matches: need === checked,\n received: checked ? "checked" : "unchecked",\n isRadio\n };\n }\n if (state === "indeterminate") {\n const checked = getCheckedAllowMixed(element);\n if (checked === "error")\n throw this.createStacklessError("Not a checkbox or radio button");\n return {\n matches: checked === "mixed",\n received: checked === true ? "checked" : checked === false ? "unchecked" : "mixed"\n };\n }\n throw this.createStacklessError(`Unexpected element state "${state}"`);\n }\n selectOptions(node, optionsToSelect) {\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 normalizedOptionLabel = normalizeWhiteSpace(option.label);\n const filter = (optionToSelect) => {\n if (optionToSelect instanceof Node)\n return option === optionToSelect;\n const matchesLabel = (label) => label === option.label || normalizeWhiteSpace(label) === normalizedOptionLabel;\n let matches = true;\n if (optionToSelect.valueOrLabel !== void 0)\n matches = matches && (optionToSelect.valueOrLabel === option.value || matchesLabel(optionToSelect.valueOrLabel));\n if (optionToSelect.value !== void 0)\n matches = matches && optionToSelect.value === option.value;\n if (optionToSelect.label !== void 0)\n matches = matches && matchesLabel(optionToSelect.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 if (!this.elementState(option, "enabled").matches)\n return "error:optionnotenabled";\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 return "error:optionsnotfound";\n select.value = void 0;\n selectedOptions.forEach((option) => option.selected = true);\n select.dispatchEvent(new Event("input", { bubbles: true, composed: true }));\n select.dispatchEvent(new Event("change", { bubbles: true }));\n return selectedOptions.map((option) => option.value);\n }\n fill(node, value) {\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-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 throw this.createStacklessError(`Input of type "${type}" cannot be filled`);\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 (type === "color")\n value = value.toLowerCase();\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, composed: 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 element.focus();\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 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, lastModified: file.lastModifiedMs });\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, composed: true }));\n input.dispatchEvent(new Event("change", { bubbles: true }));\n }\n expectHitTarget(hitPoint, targetElement) {\n var _a;\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 = (_a = hitElement.assignedSlot) != null ? _a : 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 // Life of a pointer action, for example click.\n //\n // 0. Retry items 1 and 2 while action fails due to navigation or element being detached.\n // 1. Resolve selector to an element.\n // 2. Retry the following steps until the element is detached or frame navigates away.\n // 2a. Wait for the element to be stable (not moving), visible and enabled.\n // 2b. Scroll element into view. Scrolling alternates between:\n // - Built-in protocol scrolling.\n // - Anchoring to the top/left, bottom/right and center/center.\n // This is to scroll elements from under sticky headers/footers.\n // 2c. Click point is calculated, either based on explicitly specified position,\n // or some visible point of the element based on protocol content quads.\n // 2d. Click point relative to page viewport is converted relative to the target iframe\n // for the next hit-point check.\n // 2e. (injected) Hit target at the click point must be a descendant of the target element.\n // This prevents mis-clicking in edge cases like <iframe> overlaying the target.\n // 2f. (injected) Events specific for click (or some other action type) are intercepted on\n // the Window with capture:true. See 2i for details.\n // Note: this step is skipped for drag&drop (see inline comments for the reason).\n // 2g. Necessary keyboard modifiers are pressed.\n // 2h. Click event is issued (mousemove + mousedown + mouseup).\n // 2i. (injected) For each event, we check that hit target at the event point\n // is a descendant of the target element.\n // This guarantees no race between issuing the event and handling it in the page,\n // for example due to layout shift.\n // When hit target check fails, we block all future events in the page.\n // 2j. Keyboard modifiers are restored.\n // 2k. (injected) Event interceptor is removed.\n // 2l. All navigations triggered between 2g-2k are awaited to be either committed or canceled.\n // 2m. If failed, wait for increasing amount of time before the next retry.\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": this._hoverHitTargetInterceptorEvents,\n "tap": this._tapHitTargetInterceptorEvents,\n "mouse": this._mouseHitTargetInterceptorEvents\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, eventInitObj) {\n var _a, _b, _c, _d, _e;\n let event;\n const eventInit = { bubbles: true, cancelable: true, composed: true, ...eventInitObj };\n switch (this._eventTypes.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 if (this._browserName === "webkit") {\n const createTouch = (t) => {\n var _a2, _b2, _c2;\n if (t instanceof Touch)\n return t;\n let pageX = t.pageX;\n if (pageX === void 0 && t.clientX !== void 0)\n pageX = t.clientX + (((_a2 = this.document.scrollingElement) == null ? void 0 : _a2.scrollLeft) || 0);\n let pageY = t.pageY;\n if (pageY === void 0 && t.clientY !== void 0)\n pageY = t.clientY + (((_b2 = this.document.scrollingElement) == null ? void 0 : _b2.scrollTop) || 0);\n return this.document.createTouch(this.window, (_c2 = t.target) != null ? _c2 : node, t.identifier, pageX, pageY, t.screenX, t.screenY, t.radiusX, t.radiusY, t.rotationAngle, t.force);\n };\n const createTouchList = (touches) => {\n if (touches instanceof TouchList || !touches)\n return touches;\n return this.document.createTouchList(...touches.map(createTouch));\n };\n (_a = eventInit.target) != null ? _a : eventInit.target = node;\n eventInit.touches = createTouchList(eventInit.touches);\n eventInit.targetTouches = createTouchList(eventInit.targetTouches);\n eventInit.changedTouches = createTouchList(eventInit.changedTouches);\n event = new TouchEvent(type, eventInit);\n } else {\n (_b = eventInit.target) != null ? _b : eventInit.target = node;\n eventInit.touches = (_c = eventInit.touches) == null ? void 0 : _c.map((t) => {\n var _a2;\n return t instanceof Touch ? t : new Touch({ ...t, target: (_a2 = t.target) != null ? _a2 : node });\n });\n eventInit.targetTouches = (_d = eventInit.targetTouches) == null ? void 0 : _d.map((t) => {\n var _a2;\n return t instanceof Touch ? t : new Touch({ ...t, target: (_a2 = t.target) != null ? _a2 : node });\n });\n eventInit.changedTouches = (_e = eventInit.changedTouches) == null ? void 0 : _e.map((t) => {\n var _a2;\n return t instanceof Touch ? t : new Touch({ ...t, target: (_a2 = t.target) != null ? _a2 : node });\n });\n event = new TouchEvent(type, eventInit);\n }\n break;\n }\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 case "deviceorientation":\n try {\n event = new DeviceOrientationEvent(type, eventInit);\n } catch {\n const { bubbles, cancelable, alpha, beta, gamma, absolute } = eventInit;\n event = this.document.createEvent("DeviceOrientationEvent");\n event.initDeviceOrientationEvent(type, bubbles, cancelable, alpha, beta, gamma, absolute);\n }\n break;\n case "devicemotion":\n try {\n event = new DeviceMotionEvent(type, eventInit);\n } catch {\n const { bubbles, cancelable, acceleration, accelerationIncludingGravity, rotationRate, interval } = eventInit;\n event = this.document.createEvent("DeviceMotionEvent");\n event.initDeviceMotionEvent(type, bubbles, cancelable, acceleration, accelerationIncludingGravity, rotationRate, interval);\n }\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 && this._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 const attrText = trimStringWithEllipsis(attrs.join(""), 500);\n if (this._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 const text = onlyText ? element.textContent || "" : children.length ? "\\u2026" : "";\n return oneLine(`<${element.nodeName.toLowerCase()}${attrText}>${trimStringWithEllipsis(text, 50)}</${element.nodeName.toLowerCase()}>`);\n }\n _generateSelectors(elements) {\n this._evaluator.begin();\n beginAriaCaches();\n beginDOMCaches();\n try {\n const maxElements = this._isUtilityWorld && this._browserName === "firefox" ? 2 : 10;\n const infos = elements.slice(0, maxElements).map((m) => ({\n preview: this.previewNode(m),\n selector: this.generateSelectorSimple(m)\n }));\n return infos.map((info, i) => `${i + 1}) ${info.preview} aka ${asLocator(this._sdkLanguage, info.selector)}`);\n } finally {\n endDOMCaches();\n endAriaCaches();\n this._evaluator.end();\n }\n }\n strictModeViolationError(selector, matches) {\n const lines = this._generateSelectors(matches).map((line) => `\n ` + line);\n if (lines.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 checkDeprecatedSelectorUsage(selector, matches) {\n const kDeprecatedSelectors = /* @__PURE__ */ new Set([\n "_react",\n "_vue",\n "xpath:light",\n "text:light",\n "id:light",\n "data-testid:light",\n "data-test-id:light",\n "data-test:light"\n ]);\n if (!matches.length)\n return;\n const deperecated = selector.parts.find((part) => kDeprecatedSelectors.has(part.name));\n if (!deperecated)\n return;\n const lines = this._generateSelectors(matches).map((line) => `\n ` + line);\n if (lines.length < matches.length)\n lines.push("\\n ...");\n throw this.createStacklessError(`"${deperecated.name}" selector is not supported: ${asLocator(this._sdkLanguage, stringifySelector(selector))} resolved to ${matches.length} element${matches.length === 1 ? "" : "s"}:${lines.join("")}\n`);\n }\n createStacklessError(message) {\n const error = this._shouldPrependErrorPrefix ? new Error("Error: " + message) : new Error(message);\n if (this._browserName === "firefox") {\n error.stack = "";\n return error;\n }\n delete error.stack;\n return error;\n }\n createHighlight() {\n return new Highlight(this);\n }\n maskSelectors(selectors, color) {\n const highlight = this._createHighlight();\n const elements = [];\n for (const selector of selectors)\n elements.push(this.querySelectorAll(selector, this.document.documentElement));\n highlight.maskElements(elements.flat(), color);\n }\n _createHighlight() {\n if (this._highlight)\n this.hideHighlight();\n this._highlight = new Highlight(this);\n this._highlight.install();\n return this._highlight;\n }\n _ensureHighlight() {\n if (!this._highlight) {\n this._highlight = new Highlight(this);\n this._highlight.install();\n }\n return this._highlight;\n }\n addHighlight(selector, style) {\n const highlight = this._ensureHighlight();\n highlight.addElementHighlight(selector, style);\n }\n removeHighlight(selector) {\n const highlight = this._ensureHighlight();\n highlight.removeElementHighlight(selector);\n }\n setScreencastAnnotation(annotation) {\n var _a;\n const highlight = this._ensureHighlight();\n if (!annotation) {\n highlight.updateHighlight([]);\n highlight.hideActionPoint();\n highlight.hideActionTitle();\n return;\n }\n const fadeDuration = (_a = annotation.duration) != null ? _a : 500;\n if (annotation.box) {\n highlight.updateHighlight([{\n box: annotation.box,\n color: "rgba(0, 128, 255, 0.15)",\n borderColor: "rgba(0, 128, 255, 0.6)",\n fadeDuration\n }]);\n }\n if (annotation.point)\n highlight.showActionPoint(annotation.point.x, annotation.point.y, fadeDuration);\n if (annotation.actionTitle)\n highlight.showActionTitle(annotation.actionTitle, fadeDuration, annotation.position, annotation.fontSize);\n }\n addUserOverlay(id, html) {\n const highlight = this._ensureHighlight();\n highlight.addUserOverlay(id, html);\n }\n getUserOverlay(id) {\n const highlight = this._ensureHighlight();\n return highlight.getUserOverlay(id);\n }\n removeUserOverlay(id) {\n const highlight = this._ensureHighlight();\n highlight.removeUserOverlay(id);\n }\n setUserOverlaysVisible(visible) {\n const highlight = this._ensureHighlight();\n highlight.setUserOverlaysVisible(visible);\n }\n hideHighlight() {\n if (this._highlight) {\n this._highlight.uninstall();\n delete this._highlight;\n }\n }\n markTargetElements(markedElements, callId) {\n var _a, _b;\n if (((_a = this._markedElements) == null ? void 0 : _a.callId) !== callId)\n this._markedElements = void 0;\n const previous = ((_b = this._markedElements) == null ? void 0 : _b.elements) || /* @__PURE__ */ new Set();\n const unmarkEvent = new CustomEvent("__playwright_unmark_target__", {\n bubbles: true,\n cancelable: true,\n detail: callId,\n composed: true\n });\n for (const element of previous) {\n if (!markedElements.has(element))\n element.dispatchEvent(unmarkEvent);\n }\n const markEvent = new CustomEvent("__playwright_mark_target__", {\n bubbles: true,\n cancelable: true,\n detail: callId,\n composed: true\n });\n for (const element of markedElements) {\n if (!previous.has(element))\n element.dispatchEvent(markEvent);\n }\n this._markedElements = { callId, elements: markedElements };\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 this._allHitTargetInterceptorEvents)\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 core = await this._expectCore(element, options, elements);\n const ariaSnapshot = this._ariaSnapshotForExpect(element, options);\n if (core.received === void 0 && ariaSnapshot === void 0)\n return { matches: core.matches, missingReceived: core.missingReceived };\n return { matches: core.matches, received: { value: core.received, ariaSnapshot }, missingReceived: core.missingReceived };\n }\n _ariaSnapshotForExpect(element, options) {\n const expression = options.expression;\n if (expression === "to.have.count" || expression.endsWith(".array"))\n return void 0;\n if (expression === "to.match.aria")\n return void 0;\n if (element && isElementVisible(element)) {\n const isContainment = expression === "to.have.text";\n return this._renderAriaSnapshot(element, { mode: "default", depth: isContainment ? void 0 : 1 });\n }\n if (!this.document.body)\n return void 0;\n return this._renderAriaSnapshot(this.document.body, { mode: "default" });\n }\n _renderAriaSnapshot(element, options) {\n return renderAriaTree(generateAriaTree(element, options), options).text;\n }\n async _expectCore(element, options, elements) {\n var _a, _b;\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 if (options.expression === "to.have.title" && ((_a = options == null ? void 0 : options.expectedText) == null ? void 0 : _a[0])) {\n const matcher = new ExpectedTextMatcher(options.expectedText[0]);\n const received = this.document.title;\n return { received, matches: matcher.matches(received) };\n }\n if (options.expression === "to.have.url" && ((_b = options == null ? void 0 : options.expectedText) == null ? void 0 : _b[0])) {\n const matcher = new ExpectedTextMatcher(options.expectedText[0]);\n const received = this.document.location.href;\n return { received, matches: matcher.matches(received) };\n }\n if (options.expression === "to.match.aria" && !options.selector) {\n if (!this.document.body)\n return { matches: options.isNot, missingReceived: true };\n const result = matchesExpectAriaTemplate(this.document.body, options.expectedValue);\n return {\n received: result.received,\n matches: !!result.matches.length\n };\n }\n return { matches: options.isNot, missingReceived: 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 result;\n if (expression === "to.have.attribute") {\n const hasAttribute = element.hasAttribute(options.expressionArg);\n result = {\n matches: hasAttribute,\n received: hasAttribute ? "attribute present" : "attribute not present"\n };\n } else if (expression === "to.be.checked") {\n const { checked, indeterminate } = options.expectedValue;\n if (indeterminate) {\n if (checked !== void 0)\n throw this.createStacklessError("Can\'t assert indeterminate and checked at the same time");\n result = this.elementState(element, "indeterminate");\n } else {\n result = this.elementState(element, checked === false ? "unchecked" : "checked");\n }\n } else if (expression === "to.be.disabled") {\n result = this.elementState(element, "disabled");\n } else if (expression === "to.be.editable") {\n result = this.elementState(element, "editable");\n } else if (expression === "to.be.readonly") {\n result = this.elementState(element, "editable");\n result.matches = !result.matches;\n } else if (expression === "to.be.empty") {\n if (element.nodeName === "INPUT" || element.nodeName === "TEXTAREA") {\n const value = element.value;\n result = { matches: !value, received: value ? "notEmpty" : "empty" };\n } else {\n const text = (_a = element.textContent) == null ? void 0 : _a.trim();\n result = { matches: !text, received: text ? "notEmpty" : "empty" };\n }\n } else if (expression === "to.be.enabled") {\n result = this.elementState(element, "enabled");\n } else if (expression === "to.be.focused") {\n const focused = this._activelyFocused(element).isFocused;\n result = {\n matches: focused,\n received: focused ? "focused" : "inactive"\n };\n } else if (expression === "to.be.hidden") {\n result = this.elementState(element, "hidden");\n } else if (expression === "to.be.visible") {\n result = this.elementState(element, "visible");\n } else if (expression === "to.be.attached") {\n result = {\n matches: true,\n received: "attached"\n };\n } else if (expression === "to.be.detached") {\n result = {\n matches: false,\n received: "attached"\n };\n }\n if (result) {\n if (result.received === "error:notconnected")\n throw this.createStacklessError("Element is not connected");\n return result;\n }\n }\n {\n if (expression === "to.have.property") {\n let target = element;\n const properties = options.expressionArg.split(".");\n for (let i = 0; i < properties.length - 1; i++) {\n if (typeof target !== "object" || !(properties[i] in target))\n return { received: void 0, matches: false };\n target = target[properties[i]];\n }\n const received = target[properties[properties.length - 1]];\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 if (expression === "to.match.aria") {\n const result = matchesExpectAriaTemplate(element, options.expectedValue);\n return {\n received: result.received,\n matches: !!result.matches.length\n };\n }\n }\n {\n let received;\n if (expression === "to.have.attribute.value") {\n const value = element.getAttribute(options.expressionArg);\n if (value === null)\n return { received: null, matches: false };\n received = value;\n } else if (["to.have.class", "to.contain.class"].includes(expression)) {\n if (!options.expectedText)\n throw this.createStacklessError("Expected text is not provided for " + expression);\n return {\n received: element.classList.toString(),\n matches: new ExpectedTextMatcher(options.expectedText[0]).matchesClassList(\n this,\n element.classList,\n /* partial */\n expression === "to.contain.class"\n )\n };\n } else if (expression === "to.have.css") {\n received = this.window.getComputedStyle(element, options.pseudo ? `::${options.pseudo}` : void 0).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.accessible.name") {\n received = getElementAccessibleName(\n element,\n false\n /* includeHidden */\n );\n } else if (expression === "to.have.accessible.description") {\n received = getElementAccessibleDescription(\n element,\n false\n /* includeHidden */\n );\n } else if (expression === "to.have.accessible.error.message") {\n received = getElementAccessibleErrorMessage(element);\n } else if (expression === "to.have.role") {\n received = getAriaRole(element) || "";\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 matches2 = received2 === options.expectedNumber;\n return { received: received2, matches: matches2 };\n }\n if (!options.expectedText)\n throw this.createStacklessError("Expected text is not provided for " + expression);\n if (["to.have.class.array", "to.contain.class.array"].includes(expression)) {\n const receivedClassLists = elements.map((e) => e.classList);\n const received2 = receivedClassLists.map(String);\n if (receivedClassLists.length !== options.expectedText.length)\n return { received: received2, matches: false };\n const matches2 = this._matchSequentially(\n options.expectedText,\n receivedClassLists,\n (matcher, r) => matcher.matchesClassList(\n this,\n r,\n /* partial */\n expression === "to.contain.class.array"\n )\n );\n return {\n received: received2,\n matches: matches2\n };\n }\n if (!["to.contain.text.array", "to.have.text.array"].includes(expression))\n throw this.createStacklessError("Unknown expect matcher: " + expression);\n const received = elements.map((e) => options.useInnerText ? e.innerText : elementText(/* @__PURE__ */ new Map(), e).full);\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 matches = this._matchSequentially(options.expectedText, received, (matcher, r) => matcher.matches(r));\n return { received, matches };\n }\n _matchSequentially(expectedText, received, matchFn) {\n const matchers = expectedText.map((e) => new ExpectedTextMatcher(e));\n let mIndex = 0;\n let rIndex = 0;\n while (mIndex < matchers.length && rIndex < received.length) {\n if (matchFn(matchers[mIndex], received[rIndex]))\n ++mIndex;\n ++rIndex;\n }\n return mIndex === matchers.length;\n }\n};\nfunction oneLine(s) {\n return s.replace(/\\n/g, "\\u21B5").replace(/\\t/g, "\\u21C6");\n}\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) => elementText2.normalized === 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) => elementText2.normalized.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 matchesClassList(injectedScript, classList, partial) {\n if (partial) {\n if (this._regex)\n throw injectedScript.createStacklessError("Partial matching does not support regular expressions. Please provide a string value.");\n return this._string.split(/\\s+/g).filter(Boolean).every((className) => classList.contains(className));\n }\n return this.matches(classList.toString());\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';
}
});
// packages/playwright-core/src/server/dom.ts
function isNonRecoverableDOMError(error) {
return error instanceof NonRecoverableDOMError;
}
function throwRetargetableDOMError(result2) {
if (result2 === "error:notconnected")
throwElementIsNotAttached();
return result2;
}
function throwElementIsNotAttached() {
throw new Error("Element is not attached to the DOM");
}
function assertDone(result2) {
}
function roundPoint(point) {
return {
x: (point.x * 100 | 0) / 100,
y: (point.y * 100 | 0) / 100
};
}
function quadToRect(quad) {
let minX = Infinity;
let minY = Infinity;
let maxX = -Infinity;
let maxY = -Infinity;
for (const point of quad) {
minX = Math.min(minX, point.x);
minY = Math.min(minY, point.y);
maxX = Math.max(maxX, point.x);
maxY = Math.max(maxY, point.y);
}
return { x: minX, y: minY, width: maxX - minX, height: maxY - minY };
}
function quadMiddlePoint(quad) {
const result2 = { x: 0, y: 0 };
for (const point of quad) {
result2.x += point.x / 4;
result2.y += point.y / 4;
}
return result2;
}
function triangleArea(p1, p2, p3) {
return Math.abs(p1.x * (p2.y - p3.y) + p2.x * (p3.y - p1.y) + p3.x * (p1.y - p2.y)) / 2;
}
function isPointInsideQuad(point, quad) {
const area1 = triangleArea(point, quad[0], quad[1]) + triangleArea(point, quad[1], quad[2]) + triangleArea(point, quad[2], quad[3]) + triangleArea(point, quad[3], quad[0]);
const area2 = triangleArea(quad[0], quad[1], quad[2]) + triangleArea(quad[1], quad[2], quad[3]);
if (Math.abs(area1 - area2) > 0.1)
return false;
return point.x < Math.max(quad[0].x, quad[1].x, quad[2].x, quad[3].x) && point.y < Math.max(quad[0].y, quad[1].y, quad[2].y, quad[3].y);
}
function findIntegerPointInsideQuad(quad) {
const point = quadMiddlePoint(quad);
point.x = Math.floor(point.x);
point.y = Math.floor(point.y);
if (isPointInsideQuad(point, quad))
return point;
point.x += 1;
if (isPointInsideQuad(point, quad))
return point;
point.y += 1;
if (isPointInsideQuad(point, quad))
return point;
point.x -= 1;
if (isPointInsideQuad(point, quad))
return point;
}
var import_fs13, import_path13, mime4, NonRecoverableDOMError, FrameExecutionContext, ElementHandle, kUnableToAdoptErrorMessage;
var init_dom = __esm({
"packages/playwright-core/src/server/dom.ts"() {
"use strict";
import_fs13 = __toESM(require("fs"));
import_path13 = __toESM(require("path"));
init_debug();
init_javascript();
init_fileUploadUtils();
init_injectedScriptSource();
mime4 = require("./utilsBundle").mime;
NonRecoverableDOMError = class extends Error {
};
FrameExecutionContext = class extends ExecutionContext {
constructor(delegate, frame, world) {
super(frame, delegate, world || "content-script");
this.frame = frame;
this.world = world;
}
adoptIfNeeded(handle) {
if (handle instanceof ElementHandle && handle._context !== this)
return this.frame._page.delegate.adoptElementHandle(handle, this);
return null;
}
async evaluate(pageFunction, arg) {
return evaluate(this, true, pageFunction, arg);
}
async evaluateHandle(pageFunction, arg) {
return evaluate(this, false, pageFunction, arg);
}
async evaluateExpression(expression2, options2, arg) {
return evaluateExpression(this, expression2, { ...options2, returnByValue: true }, arg);
}
async evaluateExpressionHandle(expression2, options2, arg) {
return evaluateExpression(this, expression2, { ...options2, returnByValue: false }, arg);
}
injectedScript() {
if (!this._injectedScriptPromise) {
const customEngines = [];
const selectorsRegistry = this.frame._page.browserContext.selectors();
for (const [name, { source: source9 }] of selectorsRegistry._engines)
customEngines.push({ name, source: `(${source9})` });
const sdkLanguage = this.frame._page.browserContext._browser.sdkLanguage();
const options2 = {
isUnderTest: isUnderTest(),
sdkLanguage,
testIdAttributeName: selectorsRegistry.testIdAttributeName(),
stableRafCount: this.frame._page.delegate.rafCountForStablePosition(),
browserName: this.frame._page.browserContext._browser.options.name,
shouldPrependErrorPrefix: this.delegate.shouldPrependErrorPrefix(),
isUtilityWorld: this.world === "utility",
customEngines
};
const source8 = `
(() => {
const module = {};
${source3}
return new (module.exports.InjectedScript())(globalThis, ${JSON.stringify(options2)});
})();
`;
this._injectedScriptPromise = this.rawEvaluateHandle(source8).then((handle) => {
handle._setPreview("InjectedScript");
return handle;
});
}
return this._injectedScriptPromise;
}
};
ElementHandle = class extends JSHandle {
constructor(context2, objectId) {
super(context2, "node", void 0, objectId);
this.__elementhandle = true;
this._page = context2.frame._page;
this._frame = context2.frame;
this._initializePreview().catch((e) => {
});
}
async _initializePreview() {
const utility = await this._context.injectedScript();
this._setPreview(await utility.evaluate((injected, e) => "JSHandle@" + injected.previewNode(e), this));
}
asElement() {
return this;
}
async evaluateInUtility(pageFunction, arg) {
try {
const utility = await this._frame.utilityContext();
return await utility.evaluate(pageFunction, [await utility.injectedScript(), this, arg]);
} catch (e) {
if (this._frame.isNonRetriableError(e))
throw e;
return "error:notconnected";
}
}
async _evaluateHandleInUtility(pageFunction, arg) {
try {
const utility = await this._frame.utilityContext();
return await utility.evaluateHandle(pageFunction, [await utility.injectedScript(), this, arg]);
} catch (e) {
if (this._frame.isNonRetriableError(e))
throw e;
return "error:notconnected";
}
}
async ownerFrame(progress2) {
return await progress2.race(this._ownerFrame());
}
async _ownerFrame() {
const frameId = await this._page.delegate.getOwnerFrame(this);
if (!frameId)
return null;
const frame = this._page.frameManager.frame(frameId);
if (frame)
return frame;
for (const page of this._page.browserContext.pages()) {
const frame2 = page.frameManager.frame(frameId);
if (frame2)
return frame2;
}
return null;
}
async _isIframeElement() {
return this.evaluateInUtility(([injected, node]) => node && (node.nodeName === "IFRAME" || node.nodeName === "FRAME"), {});
}
async contentFrame(progress2) {
return progress2.race(this._contentFrame());
}
async _contentFrame() {
const isFrameElement = throwRetargetableDOMError(await this._isIframeElement());
if (!isFrameElement)
return null;
return this._page.delegate.getContentFrame(this);
}
async getAttribute(progress2, name) {
return this._frame.getAttribute(progress2, ":scope", name, {}, this);
}
async inputValue(progress2) {
return this._frame.inputValue(progress2, ":scope", {}, this);
}
async textContent(progress2) {
return this._frame.textContent(progress2, ":scope", {}, this);
}
async innerText(progress2) {
return this._frame.innerText(progress2, ":scope", {}, this);
}
async innerHTML(progress2) {
return this._frame.innerHTML(progress2, ":scope", {}, this);
}
async dispatchEvent(progress2, type3, eventInit = {}) {
return this._frame.dispatchEvent(progress2, ":scope", type3, eventInit, {}, this);
}
async _scrollRectIntoViewIfNeeded(progress2, rect) {
return await progress2.race(this._page.delegate.scrollRectIntoViewIfNeeded(this, rect));
}
async _waitAndScrollIntoViewIfNeeded(progress2, waitForVisible) {
const result2 = await this._retryAction(progress2, "scroll into view", async (progress3) => {
progress3.log(` waiting for element to be stable`);
const waitResult = await progress3.race(this.evaluateInUtility(async ([injected, node, { waitForVisible: waitForVisible2 }]) => {
return await injected.checkElementStates(node, waitForVisible2 ? ["visible", "stable"] : ["stable"]);
}, { waitForVisible }));
if (waitResult)
return waitResult;
return await this._scrollRectIntoViewIfNeeded(progress3);
}, {});
assertDone(throwRetargetableDOMError(result2));
}
async scrollIntoViewIfNeeded(progress2) {
await this._waitAndScrollIntoViewIfNeeded(
progress2,
false
/* waitForVisible */
);
}
async _clickablePoint(progress2) {
const intersectQuadWithViewport = (quad2) => {
return quad2.map((point) => ({
x: Math.min(Math.max(point.x, 0), metrics.width),
y: Math.min(Math.max(point.y, 0), metrics.height)
}));
};
const computeQuadArea = (quad2) => {
let area = 0;
for (let i = 0; i < quad2.length; ++i) {
const p1 = quad2[i];
const p2 = quad2[(i + 1) % quad2.length];
area += (p1.x * p2.y - p2.x * p1.y) / 2;
}
return Math.abs(area);
};
const [quads, metrics] = await progress2.race(Promise.all([
this._page.delegate.getContentQuads(this),
this._page.mainFrame().utilityContext().then((utility) => utility.evaluate(() => ({ width: innerWidth, height: innerHeight })))
]));
if (quads === "error:notconnected")
return quads;
if (!quads || !quads.length)
return "error:notvisible";
const filtered = quads.map((quad2) => intersectQuadWithViewport(quad2)).filter((quad2) => computeQuadArea(quad2) > 0.99);
if (!filtered.length)
return "error:notinviewport";
const quad = filtered[0];
const box = quadToRect(quad);
if (this._page.browserContext._browser.options.name === "firefox") {
for (const q of filtered) {
const integerPoint = findIntegerPointInsideQuad(q);
if (integerPoint)
return { point: integerPoint, box };
}
}
return { point: quadMiddlePoint(quad), box };
}
async _offsetPoint(progress2, offset) {
const [box, border] = await progress2.race(Promise.all([
this.boundingBox(progress2),
this.evaluateInUtility(([injected, node]) => injected.getElementBorderWidth(node), {}).catch((e) => {
})
]));
if (!box || !border)
return "error:notvisible";
if (border === "error:notconnected")
return border;
return {
point: {
x: box.x + border.left + offset.x,
y: box.y + border.top + offset.y
},
box
};
}
async _retryAction(progress2, actionName, action, options2) {
let retry2 = 0;
const waitTime = [0, 20, 100, 100, 500];
const noAutoWaiting = options2.__testHookNoAutoWaiting ?? options2.noAutoWaiting;
while (true) {
if (retry2) {
progress2.log(`retrying ${actionName} action${options2.trial ? " (trial run)" : ""}`);
const timeout = waitTime[Math.min(retry2 - 1, waitTime.length - 1)];
if (timeout) {
progress2.log(` waiting ${timeout}ms`);
const result3 = await progress2.race(this.evaluateInUtility(([injected, node, timeout2]) => new Promise((f) => setTimeout(f, timeout2)), timeout));
if (result3 === "error:notconnected")
return result3;
}
} else {
progress2.log(`attempting ${actionName} action${options2.trial ? " (trial run)" : ""}`);
}
if (!options2.skipActionPreChecks && !options2.force && !noAutoWaiting)
await this._frame._page.performActionPreChecks(progress2);
const result2 = await action(progress2, retry2);
++retry2;
if (result2 === "error:notvisible") {
if (options2.force || noAutoWaiting)
throw new NonRecoverableDOMError("Element is not visible");
progress2.log(" element is not visible");
continue;
}
if (result2 === "error:notinviewport") {
if (options2.force || noAutoWaiting)
throw new NonRecoverableDOMError("Element is outside of the viewport");
progress2.log(" element is outside of the viewport");
continue;
}
if (result2 === "error:optionsnotfound") {
if (noAutoWaiting)
throw new NonRecoverableDOMError("Did not find some options");
progress2.log(" did not find some options");
continue;
}
if (result2 === "error:optionnotenabled") {
if (noAutoWaiting)
throw new NonRecoverableDOMError("Option being selected is not enabled");
progress2.log(" option being selected is not enabled");
continue;
}
if (typeof result2 === "object" && "hitTargetDescription" in result2) {
if (noAutoWaiting)
throw new NonRecoverableDOMError(`${result2.hitTargetDescription} intercepts pointer events`);
progress2.log(` ${result2.hitTargetDescription} intercepts pointer events`);
continue;
}
if (typeof result2 === "object" && "missingState" in result2) {
if (noAutoWaiting)
throw new NonRecoverableDOMError(`Element is not ${result2.missingState}`);
progress2.log(` element is not ${result2.missingState}`);
continue;
}
return result2;
}
}
async _retryPointerAction(progress2, actionName, waitForEnabled, action, options2) {
const skipActionPreChecks = actionName === "move and up";
return await this._retryAction(progress2, actionName, async (progress3, retry2) => {
const scrollOptions = [
void 0,
{ block: "end", inline: "end" },
{ block: "center", inline: "center" },
{ block: "start", inline: "start" }
];
const forceScrollOptions = scrollOptions[retry2 % scrollOptions.length];
return await this._performPointerAction(progress3, actionName, waitForEnabled, action, forceScrollOptions, options2);
}, { ...options2, skipActionPreChecks });
}
async _performPointerAction(progress2, actionName, waitForEnabled, action, forceScrollOptions, options2) {
const { force = false, position } = options2;
const doScrollIntoView = async (progress3) => {
if (forceScrollOptions) {
return await progress3.race(this.evaluateInUtility(([injected, node, options3]) => {
if (node.nodeType === 1)
node.scrollIntoView(options3);
return "done";
}, forceScrollOptions));
}
return await this._scrollRectIntoViewIfNeeded(progress3, position ? { x: position.x, y: position.y, width: 0, height: 0 } : void 0);
};
if (this._frame.parentFrame()) {
await doScrollIntoView(progress2).catch(() => {
});
}
if (options2.__testHookBeforeStable)
await progress2.race(options2.__testHookBeforeStable());
if (!force) {
const elementStates = waitForEnabled ? ["visible", "enabled", "stable"] : ["visible", "stable"];
progress2.log(` waiting for element to be ${waitForEnabled ? "visible, enabled and stable" : "visible and stable"}`);
const result2 = await progress2.race(this.evaluateInUtility(async ([injected, node, { elementStates: elementStates2 }]) => {
return await injected.checkElementStates(node, elementStates2);
}, { elementStates }));
if (result2)
return result2;
progress2.log(` element is ${waitForEnabled ? "visible, enabled and stable" : "visible and stable"}`);
}
if (options2.__testHookAfterStable)
await progress2.race(options2.__testHookAfterStable());
progress2.log(" scrolling into view if needed");
const scrolled = await doScrollIntoView(progress2);
if (scrolled !== "done")
return scrolled;
progress2.log(" done scrolling");
const maybeResult = position ? await this._offsetPoint(progress2, position) : await this._clickablePoint(progress2);
if (typeof maybeResult === "string")
return maybeResult;
const point = roundPoint(maybeResult.point);
progress2.metadata.point = point;
progress2.metadata.box = maybeResult.box;
await progress2.race(this.instrumentation.onBeforeInputAction(this, progress2.metadata));
let hitTargetInterceptionHandle;
if (force) {
progress2.log(` forcing action`);
} else {
if (options2.__testHookBeforeHitTarget)
await progress2.race(options2.__testHookBeforeHitTarget());
const frameCheckResult = await this._checkFrameIsHitTarget(progress2, point);
if (frameCheckResult === "error:notconnected" || "hitTargetDescription" in frameCheckResult)
return frameCheckResult;
const hitPoint = frameCheckResult.framePoint;
const actionType = actionName === "move and up" ? "drag" : actionName === "hover" || actionName === "tap" ? actionName : "mouse";
const handle = await progress2.race(this._evaluateHandleInUtility(([injected, node, { actionType: actionType2, hitPoint: hitPoint2, trial }]) => injected.setupHitTargetInterceptor(node, actionType2, hitPoint2, trial), { actionType, hitPoint, trial: !!options2.trial }));
if (handle === "error:notconnected")
return handle;
if (!handle._objectId) {
const error = handle.rawValue();
if (error === "error:notconnected")
return error;
return { hitTargetDescription: error };
}
hitTargetInterceptionHandle = handle;
}
const actionResult = await this._page.frameManager.waitForSignalsCreatedBy(progress2, options2.waitAfter === true, async (progress3) => {
if (options2.__testHookBeforePointerAction)
await progress3.race(options2.__testHookBeforePointerAction());
let restoreModifiers;
if (options2 && options2.modifiers)
restoreModifiers = await this._page.keyboard.ensureModifiers(progress3, options2.modifiers);
progress3.log(` performing ${actionName} action`);
await action(progress3, point);
if (restoreModifiers)
await this._page.keyboard.ensureModifiers(progress3, restoreModifiers);
if (hitTargetInterceptionHandle) {
const stopHitTargetInterception = this._frame.raceAgainstEvaluationStallingEvents(() => {
return hitTargetInterceptionHandle.evaluate((h) => h.stop());
}).catch((e) => "done").finally(() => {
hitTargetInterceptionHandle?.dispose();
});
if (options2.waitAfter !== false) {
const hitTargetResult = await progress3.race(stopHitTargetInterception);
if (hitTargetResult !== "done")
return hitTargetResult;
}
}
progress3.log(` ${options2.trial ? "trial " : ""}${actionName} action done`);
progress3.log(" waiting for scheduled navigations to finish");
if (options2.__testHookAfterPointerAction)
await progress3.race(options2.__testHookAfterPointerAction());
return "done";
}).finally(() => {
const stopPromise = hitTargetInterceptionHandle?.evaluate((h) => h.stop()).catch(() => {
});
stopPromise?.then(() => hitTargetInterceptionHandle?.dispose());
});
if (actionResult !== "done")
return actionResult;
progress2.log(" navigations have finished");
return "done";
}
async _markAsTargetElement(progress2) {
if (!progress2.metadata.id)
return;
await progress2.race(this.evaluateInUtility(([injected, node, callId]) => {
if (node.nodeType === 1)
injected.markTargetElements(/* @__PURE__ */ new Set([node]), callId);
}, progress2.metadata.id));
}
async hover(progress2, options2) {
await this._markAsTargetElement(progress2);
const result2 = await this._hover(progress2, options2);
return assertDone(throwRetargetableDOMError(result2));
}
_hover(progress2, options2) {
return this._retryPointerAction(progress2, "hover", false, (progress3, point) => this._page.mouse.move(progress3, point.x, point.y), { ...options2, waitAfter: "disabled" });
}
async click(progress2, options2) {
await this._markAsTargetElement(progress2);
const result2 = await this._click(progress2, { ...options2, waitAfter: !options2.noWaitAfter });
return assertDone(throwRetargetableDOMError(result2));
}
_click(progress2, options2) {
return this._retryPointerAction(progress2, "click", true, (progress3, point) => this._page.mouse.click(progress3, point.x, point.y, options2), options2);
}
async dblclick(progress2, options2) {
await this._markAsTargetElement(progress2);
const result2 = await this._dblclick(progress2, options2);
return assertDone(throwRetargetableDOMError(result2));
}
_dblclick(progress2, options2) {
return this._retryPointerAction(progress2, "dblclick", true, (progress3, point) => this._page.mouse.click(progress3, point.x, point.y, { ...options2, clickCount: 2 }), { ...options2, waitAfter: "disabled" });
}
async tap(progress2, options2) {
await this._markAsTargetElement(progress2);
const result2 = await this._tap(progress2, options2);
return assertDone(throwRetargetableDOMError(result2));
}
_tap(progress2, options2) {
return this._retryPointerAction(progress2, "tap", true, (progress3, point) => this._page.touchscreen.tap(progress3, point.x, point.y), { ...options2, waitAfter: "disabled" });
}
async selectOption(progress2, elements, values, options2) {
await this._markAsTargetElement(progress2);
const result2 = await this._selectOption(progress2, elements, values, options2);
return throwRetargetableDOMError(result2);
}
async _beforeNonPointerAction(progress2) {
if (progress2.metadata.annotate)
progress2.metadata.box = await this.boundingBox(progress2) || void 0;
await progress2.race(this.instrumentation.onBeforeInputAction(this, progress2.metadata));
}
async _selectOption(progress2, elements, values, options2) {
let resultingOptions = [];
const result2 = await this._retryAction(progress2, "select option", async (progress3) => {
await this._beforeNonPointerAction(progress3);
if (!options2.force)
progress3.log(` waiting for element to be visible and enabled`);
const optionsToSelect = [...elements, ...values];
const result3 = await progress3.race(this.evaluateInUtility(async ([injected, node, { optionsToSelect: optionsToSelect2, force }]) => {
if (!force) {
const checkResult = await injected.checkElementStates(node, ["visible", "enabled"]);
if (checkResult)
return checkResult;
}
return injected.selectOptions(node, optionsToSelect2);
}, { optionsToSelect, force: options2.force }));
if (Array.isArray(result3)) {
progress3.log(" selected specified option(s)");
resultingOptions = result3;
return "done";
}
return result3;
}, options2);
if (result2 === "error:notconnected")
return result2;
return resultingOptions;
}
async fill(progress2, value2, options2) {
await this._markAsTargetElement(progress2);
const result2 = await this._fill(progress2, value2, options2);
assertDone(throwRetargetableDOMError(result2));
}
async _fill(progress2, value2, options2) {
progress2.log(` fill("${value2}")`);
return await this._retryAction(progress2, "fill", async (progress3) => {
await this._beforeNonPointerAction(progress3);
if (!options2.force)
progress3.log(" waiting for element to be visible, enabled and editable");
const result2 = await progress3.race(this.evaluateInUtility(async ([injected, node, { value: value3, force }]) => {
if (!force) {
const checkResult = await injected.checkElementStates(node, ["visible", "enabled", "editable"]);
if (checkResult)
return checkResult;
}
return injected.fill(node, value3);
}, { value: value2, force: options2.force }));
if (result2 === "needsinput") {
if (value2)
await this._page.keyboard.insertText(progress3, value2);
else
await this._page.keyboard.press(progress3, "Delete");
return "done";
} else {
return result2;
}
}, options2);
}
async selectText(progress2, options2) {
const result2 = await this._retryAction(progress2, "selectText", async (progress3) => {
if (!options2.force)
progress3.log(" waiting for element to be visible");
return await progress3.race(this.evaluateInUtility(async ([injected, node, { force }]) => {
if (!force) {
const checkResult = await injected.checkElementStates(node, ["visible"]);
if (checkResult)
return checkResult;
}
return injected.selectText(node);
}, { force: options2.force }));
}, options2);
assertDone(throwRetargetableDOMError(result2));
}
async setInputFiles(progress2, params2) {
const inputFileItems = await progress2.race(prepareFilesForUpload(this._frame, params2));
await this._markAsTargetElement(progress2);
const result2 = await this._setInputFiles(progress2, inputFileItems);
return assertDone(throwRetargetableDOMError(result2));
}
async _drop(progress2, inputFileItems, data, options2) {
const { filePayloads, localPaths } = inputFileItems;
let payloads;
if (localPaths && !filePayloads) {
payloads = await Promise.all(localPaths.map(async (p) => ({
name: import_path13.default.basename(p),
mimeType: mime4.getType(p) || "application/octet-stream",
buffer: (await import_fs13.default.promises.readFile(p)).toString("base64"),
lastModifiedMs: (await import_fs13.default.promises.stat(p)).mtimeMs
})));
} else {
payloads = (filePayloads ?? []).map((p) => ({
name: p.name,
mimeType: p.mimeType || "application/octet-stream",
buffer: p.buffer,
lastModifiedMs: p.lastModifiedMs
}));
}
return this._retryPointerAction(progress2, "drop", false, async (progress3, point) => {
const mainContext = await progress3.race(this._frame.mainContext());
const handle = this._context === mainContext ? this : await progress3.race(this._page.delegate.adoptElementHandle(this, mainContext));
const disposeHandle = handle !== this;
try {
const result2 = await progress3.race(handle.evaluate((node, { payloads: payloads2, data: data2, point: point2 }) => {
if (!node.isConnected || node.nodeType !== 1)
return "error:notconnected";
const element2 = node;
const dt = new DataTransfer();
for (const p of payloads2) {
const bytes = Uint8Array.from(atob(p.buffer), (c) => c.charCodeAt(0));
const file = new File([bytes], p.name, { type: p.mimeType, lastModified: p.lastModifiedMs });
dt.items.add(file);
}
for (const entry of data2)
dt.setData(entry.mimeType, entry.value);
const makeEvent = (type3) => new DragEvent(type3, {
bubbles: true,
cancelable: true,
composed: true,
clientX: point2.x,
clientY: point2.y,
dataTransfer: dt
});
element2.dispatchEvent(makeEvent("dragenter"));
const over = makeEvent("dragover");
element2.dispatchEvent(over);
if (!over.defaultPrevented) {
element2.dispatchEvent(makeEvent("dragleave"));
return "not-accepted";
}
element2.dispatchEvent(makeEvent("drop"));
return "accepted";
}, { payloads, data, point }));
if (result2 === "not-accepted")
throw new NonRecoverableDOMError("Drop target did not accept the drop \u2014 its dragover handler did not call preventDefault()");
} finally {
if (disposeHandle)
handle.dispose();
}
}, { ...options2, waitAfter: "disabled" });
}
async _setInputFiles(progress2, items) {
const { filePayloads, localPaths, localDirectory } = items;
const multiple = filePayloads && filePayloads.length > 1 || localPaths && localPaths.length > 1;
const result2 = await progress2.race(this._evaluateHandleInUtility(([injected, node, { multiple: multiple2, directoryUpload }]) => {
const element2 = injected.retarget(node, "follow-label");
if (!element2)
return;
if (element2.tagName !== "INPUT")
throw injected.createStacklessError("Node is not an HTMLInputElement");
const inputElement = element2;
if (multiple2 && !inputElement.multiple && !inputElement.webkitdirectory)
throw injected.createStacklessError("Non-multiple file input can only accept single file");
if (directoryUpload && !inputElement.webkitdirectory)
throw injected.createStacklessError("File input does not support directories, pass individual files instead");
if (!directoryUpload && inputElement.webkitdirectory)
throw injected.createStacklessError("[webkitdirectory] input requires passing a path to a directory");
return inputElement;
}, { multiple, directoryUpload: !!localDirectory }));
if (result2 === "error:notconnected" || !result2.asElement())
return "error:notconnected";
const retargeted = result2.asElement();
await this._beforeNonPointerAction(progress2);
if (localPaths || localDirectory) {
const localPathsOrDirectory = localDirectory ? [localDirectory] : localPaths;
await progress2.race(Promise.all(localPathsOrDirectory.map((localPath) => import_fs13.default.promises.access(localPath, import_fs13.default.constants.F_OK))));
const waitForInputEvent = localDirectory ? this.evaluate((node) => new Promise((fulfill) => {
node.addEventListener("input", fulfill, { once: true });
})).catch(() => {
}) : Promise.resolve();
await this._page.delegate.setInputFilePaths(progress2, retargeted, localPathsOrDirectory);
await progress2.race(waitForInputEvent);
} else {
await progress2.race(retargeted.evaluateInUtility(([injected, node, files]) => injected.setInputFiles(node, files), filePayloads));
}
return "done";
}
async focus(progress2) {
await this._markAsTargetElement(progress2);
const result2 = await this._focus(progress2);
return assertDone(throwRetargetableDOMError(result2));
}
async _focus(progress2, resetSelectionIfNotFocused) {
return await progress2.race(this.evaluateInUtility(([injected, node, resetSelectionIfNotFocused2]) => injected.focusNode(node, resetSelectionIfNotFocused2), resetSelectionIfNotFocused));
}
async _blur(progress2) {
return await progress2.race(this.evaluateInUtility(([injected, node]) => injected.blurNode(node), {}));
}
async type(progress2, text2, options2) {
await this._markAsTargetElement(progress2);
const result2 = await this._type(progress2, text2, options2);
return assertDone(throwRetargetableDOMError(result2));
}
async _type(progress2, text2, options2) {
progress2.log(`elementHandle.type("${text2}")`);
await this._beforeNonPointerAction(progress2);
const result2 = await this._focus(
progress2,
true
/* resetSelectionIfNotFocused */
);
if (result2 !== "done")
return result2;
await this._page.keyboard.type(progress2, text2, options2);
return "done";
}
async press(progress2, key, options2) {
await this._markAsTargetElement(progress2);
const result2 = await this._press(progress2, key, options2);
return assertDone(throwRetargetableDOMError(result2));
}
async _press(progress2, key, options2) {
progress2.log(`elementHandle.press("${key}")`);
await this._beforeNonPointerAction(progress2);
return this._page.frameManager.waitForSignalsCreatedBy(progress2, !options2.noWaitAfter, async (progress3) => {
const result2 = await this._focus(
progress3,
true
/* resetSelectionIfNotFocused */
);
if (result2 !== "done")
return result2;
await this._page.keyboard.press(progress3, key, options2);
return "done";
});
}
async check(progress2, options2) {
const result2 = await this._setChecked(progress2, true, options2);
return assertDone(throwRetargetableDOMError(result2));
}
async uncheck(progress2, options2) {
const result2 = await this._setChecked(progress2, false, options2);
return assertDone(throwRetargetableDOMError(result2));
}
async _setChecked(progress2, state, options2) {
const isChecked = async (progress3) => {
const result3 = await progress3.race(this.evaluateInUtility(([injected, node]) => injected.elementState(node, "checked"), {}));
if (result3 === "error:notconnected" || result3.received === "error:notconnected")
throwElementIsNotAttached();
return { matches: result3.matches, isRadio: result3.isRadio };
};
await this._markAsTargetElement(progress2);
const checkedState = await isChecked(progress2);
if (checkedState.matches === state)
return "done";
if (!state && checkedState.isRadio)
throw new NonRecoverableDOMError("Cannot uncheck radio button. Radio buttons can only be unchecked by selecting another radio button in the same group.");
const result2 = await this._click(progress2, { ...options2, waitAfter: "disabled" });
if (result2 !== "done")
return result2;
if (options2.trial)
return "done";
const finalState = await isChecked(progress2);
if (finalState.matches !== state)
throw new NonRecoverableDOMError("Clicking the checkbox did not change its state");
return "done";
}
async boundingBox(progress2) {
return await progress2.race(this._page.delegate.getBoundingBox(this));
}
async screenshot(progress2, options2) {
return await this._page.screenshotter.screenshotElement(progress2, this, options2);
}
async querySelector(progress2, selector, options2) {
return progress2.race(this._querySelector(selector, options2));
}
async _querySelector(selector, options2) {
return this._frame.selectors.query(selector, options2, this);
}
async querySelectorAll(progress2, selector) {
return progress2.race(this._querySelectorAll(selector));
}
async _querySelectorAll(selector) {
return this._frame.selectors.queryAll(selector, this);
}
async evalOnSelector(progress2, selector, strict, expression2, isFunction2, arg) {
return this._frame.evalOnSelector(progress2, selector, strict, expression2, isFunction2, arg, this);
}
async evalOnSelectorAll(progress2, selector, expression2, isFunction2, arg) {
return this._frame.evalOnSelectorAll(progress2, selector, expression2, isFunction2, arg, this);
}
async isVisible(progress2) {
return this._frame.isVisible(progress2, ":scope", {}, this);
}
async isHidden(progress2) {
return this._frame.isHidden(progress2, ":scope", {}, this);
}
async isEnabled(progress2) {
return this._frame.isEnabled(progress2, ":scope", {}, this);
}
async isDisabled(progress2) {
return this._frame.isDisabled(progress2, ":scope", {}, this);
}
async isEditable(progress2) {
return this._frame.isEditable(progress2, ":scope", {}, this);
}
async isChecked(progress2) {
return this._frame.isChecked(progress2, ":scope", {}, this);
}
async waitForElementState(progress2, state) {
const actionName = `wait for ${state}`;
const result2 = await this._retryAction(progress2, actionName, async (progress3) => {
return await progress3.race(this.evaluateInUtility(async ([injected, node, state2]) => {
return await injected.checkElementStates(node, [state2]) || "done";
}, state));
}, {});
assertDone(throwRetargetableDOMError(result2));
}
async waitForSelector(progress2, selector, options2) {
return await this._frame.waitForSelector(progress2, selector, true, options2, this);
}
async _adoptTo(context2) {
if (this._context !== context2) {
const adopted = await this._page.delegate.adoptElementHandle(this, context2);
this.dispose();
return adopted;
}
return this;
}
async _checkFrameIsHitTarget(progress2, point) {
let frame = this._frame;
const data = [];
while (frame.parentFrame()) {
const frameElement = await frame.frameElement(progress2);
const box = await frameElement.boundingBox(progress2);
const style = await progress2.race(frameElement.evaluateInUtility(([injected, iframe]) => injected.describeIFrameStyle(iframe), {}).catch((e) => "error:notconnected"));
if (!box || style === "error:notconnected")
return "error:notconnected";
if (style === "transformed") {
return { framePoint: void 0 };
}
const pointInFrame = { x: point.x - box.x - style.left, y: point.y - box.y - style.top };
data.push({ frame, frameElement, pointInFrame });
frame = frame.parentFrame();
}
data.push({ frame, frameElement: null, pointInFrame: point });
for (let i = data.length - 1; i > 0; i--) {
const element2 = data[i - 1].frameElement;
const point2 = data[i].pointInFrame;
const hitTargetResult = await progress2.race(element2.evaluateInUtility(([injected, element3, hitPoint]) => {
return injected.expectHitTarget(hitPoint, element3);
}, point2));
if (hitTargetResult !== "done")
return hitTargetResult;
}
return { framePoint: data[0].pointInFrame };
}
};
kUnableToAdoptErrorMessage = "Unable to adopt element handle from a different document";
}
});
// packages/playwright-core/src/server/frameSelectors.ts
async function adoptIfNeeded(handle, context2) {
if (handle._context === context2)
return handle;
const adopted = await handle._page.delegate.adoptElementHandle(handle, context2);
handle.dispose();
return adopted;
}
var FrameSelectors;
var init_frameSelectors = __esm({
"packages/playwright-core/src/server/frameSelectors.ts"() {
"use strict";
init_selectorParser();
init_locatorGenerators();
FrameSelectors = class {
constructor(frame) {
this.frame = frame;
}
_parseSelector(selector, options2) {
const strict = typeof options2?.strict === "boolean" ? options2.strict : !!this.frame._page.browserContext._options.strictSelectors;
return this.frame._page.browserContext.selectors().parseSelector(selector, strict);
}
async query(selector, options2, scope) {
const resolved = await this.resolveInjectedForSelector(selector, options2, scope);
if (!resolved)
return null;
const handle = await resolved.injected.evaluateHandle((injected, { info, scope: scope2 }) => {
return injected.querySelector(info.parsed, scope2 || document, info.strict);
}, { info: resolved.info, scope: resolved.scope });
const elementHandle = handle.asElement();
if (!elementHandle) {
handle.dispose();
return null;
}
return adoptIfNeeded(elementHandle, await resolved.frame.mainContext());
}
async queryArrayInMainWorld(selector, scope) {
const resolved = await this.resolveInjectedForSelector(selector, { mainWorld: true }, scope);
if (!resolved)
throw new Error(`Failed to find frame for selector "${selector}"`);
return await resolved.injected.evaluateHandle((injected, { info, scope: scope2 }) => {
const elements = injected.querySelectorAll(info.parsed, scope2 || document);
injected.checkDeprecatedSelectorUsage(info.parsed, elements);
return elements;
}, { info: resolved.info, scope: resolved.scope });
}
async queryCount(selector, options2) {
const resolved = await this.resolveInjectedForSelector(selector);
if (!resolved)
throw new Error(`Failed to find frame for selector "${selector}"`);
await options2.__testHookBeforeQuery?.();
return await resolved.injected.evaluate((injected, { info }) => {
const elements = injected.querySelectorAll(info.parsed, document);
injected.checkDeprecatedSelectorUsage(info.parsed, elements);
return elements.length;
}, { info: resolved.info });
}
async queryAll(selector, scope) {
const resolved = await this.resolveInjectedForSelector(selector, {}, scope);
if (!resolved)
return [];
const arrayHandle = await resolved.injected.evaluateHandle((injected, { info, scope: scope2 }) => {
const elements = injected.querySelectorAll(info.parsed, scope2 || document);
injected.checkDeprecatedSelectorUsage(info.parsed, elements);
return elements;
}, { info: resolved.info, scope: resolved.scope });
const properties = await arrayHandle.internalGetProperties();
arrayHandle.dispose();
const targetContext = await resolved.frame.mainContext();
const result2 = [];
for (const property of properties.values()) {
const elementHandle = property.asElement();
if (elementHandle)
result2.push(adoptIfNeeded(elementHandle, targetContext));
else
property.dispose();
}
return Promise.all(result2);
}
_jumpToAriaRefFrameIfNeeded(selector, info, frame) {
if (info.parsed.parts[0].name !== "aria-ref")
return frame;
const body = info.parsed.parts[0].body;
const match = body.match(/^f(\d+)e\d+$/);
if (!match)
return frame;
const frameSeq = +match[1];
const jumptToFrame = this.frame._page.frameManager.frames().find((frame2) => frame2.seq === frameSeq);
if (!jumptToFrame)
throw new InvalidSelectorError(`Invalid frame in aria-ref selector "${selector}"`);
return jumptToFrame;
}
async resolveFrameForSelector(selector, options2 = {}, scope) {
let frame = this.frame;
const frameChunks = splitSelectorByFrame(selector);
for (const chunk of frameChunks) {
visitAllSelectorParts(chunk, (part, nested) => {
if (nested && part.name === "internal:control" && part.body === "enter-frame") {
const locator2 = asLocator(this.frame._page.browserContext._browser.sdkLanguage(), selector);
throw new InvalidSelectorError(`Frame locators are not allowed inside composite locators, while querying "${locator2}"`);
}
});
}
for (let i = 0; i < frameChunks.length - 1; ++i) {
const info = this._parseSelector(frameChunks[i], options2);
frame = this._jumpToAriaRefFrameIfNeeded(selector, info, frame);
const context2 = await frame.context(info.world);
const injectedScript = await context2.injectedScript();
const handle = await injectedScript.evaluateHandle((injected, { info: info2, scope: scope2, selectorString }) => {
const element3 = injected.querySelector(info2.parsed, scope2 || document, info2.strict);
if (element3 && element3.nodeName !== "IFRAME" && element3.nodeName !== "FRAME")
throw injected.createStacklessError(`Selector "${selectorString}" resolved to ${injected.previewNode(element3)}, <iframe> was expected`);
return element3;
}, { info, scope: i === 0 ? scope : void 0, selectorString: stringifySelector(info.parsed) });
const element2 = handle.asElement();
if (!element2)
return null;
const maybeFrame = await frame._page.delegate.getContentFrame(element2);
element2.dispose();
if (!maybeFrame)
return null;
frame = maybeFrame;
}
if (frame !== this.frame)
scope = void 0;
const lastChunk = frame.selectors._parseSelector(frameChunks[frameChunks.length - 1], options2);
frame = this._jumpToAriaRefFrameIfNeeded(selector, lastChunk, frame);
return { frame, info: lastChunk, scope };
}
async resolveInjectedForSelector(selector, options2, scope) {
const resolved = await this.resolveFrameForSelector(selector, options2, scope);
if (!resolved)
return;
const context2 = await resolved.frame.context(options2?.mainWorld ? "main" : resolved.info.world);
const injected = await context2.injectedScript();
return { injected, info: resolved.info, frame: resolved.frame, scope: resolved.scope };
}
};
}
});
// packages/playwright-core/src/server/helper.ts
var MAX_LOG_LENGTH, Helper, helper;
var init_helper = __esm({
"packages/playwright-core/src/server/helper.ts"() {
"use strict";
init_debugLogger();
init_eventsHelper();
MAX_LOG_LENGTH = process.env.MAX_LOG_LENGTH ? +process.env.MAX_LOG_LENGTH : Infinity;
Helper = class {
static completeUserURL(urlString) {
if (urlString.startsWith("localhost") || urlString.startsWith("127.0.0.1"))
urlString = "http://" + urlString;
return urlString;
}
static enclosingIntRect(rect) {
const x = Math.floor(rect.x + 1e-3);
const y = Math.floor(rect.y + 1e-3);
const x2 = Math.ceil(rect.x + rect.width - 1e-3);
const y2 = Math.ceil(rect.y + rect.height - 1e-3);
return { x, y, width: x2 - x, height: y2 - y };
}
static enclosingIntSize(size) {
return { width: Math.floor(size.width + 1e-3), height: Math.floor(size.height + 1e-3) };
}
static getViewportSizeFromWindowFeatures(features) {
const widthString = features.find((f) => f.startsWith("width="));
const heightString = features.find((f) => f.startsWith("height="));
const width = widthString ? parseInt(widthString.substring(6), 10) : NaN;
const height = heightString ? parseInt(heightString.substring(7), 10) : NaN;
if (!Number.isNaN(width) && !Number.isNaN(height))
return { width, height };
return null;
}
static waitForEvent(progress2, emitter, event, predicate) {
const listeners = [];
const dispose = () => eventsHelper.removeEventListeners(listeners);
const promise = progress2.race(new Promise((resolve, reject) => {
listeners.push(eventsHelper.addEventListener(emitter, event, (eventArg) => {
try {
if (predicate && !predicate(eventArg))
return;
resolve(eventArg);
} catch (e) {
reject(e);
}
}));
})).finally(() => dispose());
return { promise, dispose };
}
static secondsToRoundishMillis(value2) {
return (value2 * 1e6 | 0) / 1e3;
}
static millisToRoundishMillis(value2) {
return (value2 * 1e3 | 0) / 1e3;
}
static debugProtocolLogger(protocolLogger) {
return (direction, message) => {
if (protocolLogger)
protocolLogger(direction, message);
if (debugLogger.isEnabled("protocol")) {
let text2 = JSON.stringify(message);
if (text2.length > MAX_LOG_LENGTH)
text2 = text2.substring(0, MAX_LOG_LENGTH / 2) + " <<<<<( LOG TRUNCATED )>>>>> " + text2.substring(text2.length - MAX_LOG_LENGTH / 2);
debugLogger.log("protocol", (direction === "send" ? "SEND \u25BA " : "\u25C0 RECV ") + text2);
}
};
}
static formatBrowserLogs(logs, disconnectReason) {
if (!disconnectReason && !logs.length)
return "";
return "\n" + (disconnectReason ? disconnectReason + "\n" : "") + logs.join("\n");
}
};
helper = Helper;
}
});
// packages/playwright-core/src/server/types.ts
var kLifecycleEvents;
var init_types = __esm({
"packages/playwright-core/src/server/types.ts"() {
"use strict";
kLifecycleEvents = /* @__PURE__ */ new Set(["load", "domcontentloaded", "networkidle", "commit"]);
}
});
// packages/playwright-core/src/server/protocolError.ts
function isProtocolError(e) {
return e instanceof ProtocolError;
}
function isSessionClosedError(e) {
return e instanceof ProtocolError && (e.type === "closed" || e.type === "crashed");
}
var ProtocolError;
var init_protocolError = __esm({
"packages/playwright-core/src/server/protocolError.ts"() {
"use strict";
init_stackTrace();
ProtocolError = class extends Error {
constructor(type3, method, logs) {
super();
this.type = type3;
this.method = method;
this.logs = logs;
}
setMessage(message) {
rewriteErrorMessage(this, `Protocol error (${this.method}): ${message}`);
}
browserLogMessage() {
return this.logs ? "\nBrowser logs:\n" + this.logs : "";
}
};
}
});
// packages/playwright-core/src/server/callLog.ts
function compressCallLog(log2) {
const lines = [];
for (const block of findRepeatedSubsequences(log2)) {
for (let i = 0; i < block.sequence.length; i++) {
const line = block.sequence[i];
const leadingWhitespace = line.match(/^\s*/);
const whitespacePrefix = " " + leadingWhitespace?.[0] || "";
const countPrefix = `${block.count} \xD7 `;
if (block.count > 1 && i === 0)
lines.push(whitespacePrefix + countPrefix + line.trim());
else if (block.count > 1)
lines.push(whitespacePrefix + " ".repeat(countPrefix.length - 2) + "- " + line.trim());
else
lines.push(whitespacePrefix + "- " + line.trim());
}
}
return lines;
}
function findRepeatedSubsequences(s) {
const n = s.length;
const result2 = [];
let i = 0;
const arraysEqual = (a1, a2) => {
if (a1.length !== a2.length)
return false;
for (let j = 0; j < a1.length; j++) {
if (a1[j] !== a2[j])
return false;
}
return true;
};
while (i < n) {
let maxRepeatCount = 1;
let maxRepeatSubstr = [s[i]];
let maxRepeatLength = 1;
for (let p = 1; p <= n - i; p++) {
const substr = s.slice(i, i + p);
let k = 1;
while (i + p * k <= n && arraysEqual(s.slice(i + p * (k - 1), i + p * k), substr))
k += 1;
k -= 1;
if (k > 1 && k * p > maxRepeatCount * maxRepeatLength) {
maxRepeatCount = k;
maxRepeatSubstr = substr;
maxRepeatLength = p;
}
}
result2.push({ sequence: maxRepeatSubstr, count: maxRepeatCount });
i += maxRepeatLength * maxRepeatCount;
}
return result2;
}
var findRepeatedSubsequencesForTest;
var init_callLog = __esm({
"packages/playwright-core/src/server/callLog.ts"() {
"use strict";
findRepeatedSubsequencesForTest = findRepeatedSubsequences;
}
});
// packages/playwright-core/src/server/frames.ts
function verifyLifecycle(name, waitUntil) {
if (waitUntil === "networkidle0")
waitUntil = "networkidle";
if (!kLifecycleEvents.has(waitUntil))
throw new Error(`${name}: expected one of (load|domcontentloaded|networkidle|commit)`);
return waitUntil;
}
function renderUnexpectedValue(expression2, received) {
if (expression2 === "to.match.aria")
return received ? received.raw : received;
return received;
}
var NavigationAbortedError, kDummyFrameId, FrameManager, FrameEvent, Frame, SignalBarrier;
var init_frames = __esm({
"packages/playwright-core/src/server/frames.ts"() {
"use strict";
init_selectorParser();
init_manualPromise();
init_eventsHelper();
init_manualPromise();
init_locatorGenerators();
init_assert();
init_urlMatch();
init_task();
init_protocolFormatter();
init_browserContext();
init_dom();
init_errors();
init_fileUploadUtils();
init_frameSelectors();
init_helper();
init_instrumentation();
init_javascript();
init_network2();
init_page();
init_progress();
init_types();
init_protocolError();
init_callLog();
NavigationAbortedError = class extends Error {
constructor(documentId, message) {
super(message);
this.documentId = documentId;
}
};
kDummyFrameId = "<dummy>";
FrameManager = class {
constructor(page) {
this._frames = /* @__PURE__ */ new Map();
this._consoleMessageTags = /* @__PURE__ */ new Map();
this._signalBarriers = /* @__PURE__ */ new Set();
this._webSockets = /* @__PURE__ */ new Map();
this._nextFrameSeq = 0;
this._page = page;
this._mainFrame = void 0;
}
_allocateFrameSeq() {
return this._nextFrameSeq++;
}
createDummyMainFrameIfNeeded() {
if (!this._mainFrame)
this.frameAttached(kDummyFrameId, null);
}
dispose() {
for (const frame of this._frames.values()) {
frame._stopNetworkIdleTimer();
frame.invalidateNonStallingEvaluations("Target crashed");
}
}
mainFrame() {
return this._mainFrame;
}
frames() {
const frames = [];
collect(this._mainFrame);
return frames;
function collect(frame) {
frames.push(frame);
for (const subframe of frame._getChildFrames())
collect(subframe);
}
}
frame(frameId) {
return this._frames.get(frameId) || null;
}
frameAttached(frameId, parentFrameId) {
const parentFrame = parentFrameId ? this._frames.get(parentFrameId) : null;
if (!parentFrame) {
if (this._mainFrame) {
this._frames.delete(this._mainFrame._id);
this._mainFrame._id = frameId;
} else {
assert(!this._frames.has(frameId));
this._mainFrame = new Frame(this._page, frameId, parentFrame);
}
this._frames.set(frameId, this._mainFrame);
return this._mainFrame;
} else {
assert(!this._frames.has(frameId));
const frame = new Frame(this._page, frameId, parentFrame);
this._frames.set(frameId, frame);
this._page.emit(Page.Events.FrameAttached, frame);
return frame;
}
}
async waitForSignalsCreatedBy(progress2, waitAfter, action) {
if (!waitAfter)
return action(progress2);
const barrier = new SignalBarrier(progress2);
this._signalBarriers.add(barrier);
try {
const result2 = await action(progress2);
await progress2.race(this._page.delegate.inputActionEpilogue());
await barrier.waitFor(progress2);
await new Promise(makeWaitForNextTask());
return result2;
} finally {
this._signalBarriers.delete(barrier);
}
}
frameWillPotentiallyRequestNavigation() {
for (const barrier of this._signalBarriers)
barrier.retain();
}
frameDidPotentiallyRequestNavigation() {
for (const barrier of this._signalBarriers)
barrier.release();
}
frameRequestedNavigation(frameId, documentId) {
const frame = this._frames.get(frameId);
if (!frame)
return;
for (const barrier of this._signalBarriers)
barrier.addFrameNavigation(frame);
if (frame.pendingDocument() && frame.pendingDocument().documentId === documentId) {
return;
}
const request2 = documentId ? Array.from(frame._inflightRequests).find((request3) => request3._documentId === documentId) : void 0;
frame._setPendingDocument({ documentId, request: request2 });
}
frameCommittedNewDocumentNavigation(frameId, url2, name, documentId, initial) {
const frame = this._frames.get(frameId);
this.removeChildFramesRecursively(frame);
this._clearWebSockets(frame);
frame._url = url2;
frame._name = name;
let keepPending;
const pendingDocument = frame.pendingDocument();
if (pendingDocument) {
if (pendingDocument.documentId === void 0) {
pendingDocument.documentId = documentId;
}
if (pendingDocument.documentId === documentId) {
frame._currentDocument = pendingDocument;
} else {
keepPending = pendingDocument;
frame._currentDocument = { documentId, request: void 0 };
}
frame._setPendingDocument(void 0);
} else {
frame._currentDocument = { documentId, request: void 0 };
}
frame._onClearLifecycle();
const navigationEvent = { url: url2, name, newDocument: frame._currentDocument, isPublic: true };
this._fireInternalFrameNavigation(frame, navigationEvent);
if (!initial) {
frame.apiLog(` navigated to "${url2}"`);
this._page.frameNavigatedToNewDocument(frame);
}
frame._setPendingDocument(keepPending);
}
frameCommittedSameDocumentNavigation(frameId, url2) {
const frame = this._frames.get(frameId);
if (!frame)
return;
const pending = frame.pendingDocument();
if (pending && pending.documentId === void 0 && pending.request === void 0) {
frame._setPendingDocument(void 0);
}
frame._url = url2;
const navigationEvent = { url: url2, name: frame._name, isPublic: true };
this._fireInternalFrameNavigation(frame, navigationEvent);
frame.apiLog(` navigated to "${url2}"`);
}
frameAbortedNavigation(frameId, errorText, documentId) {
const frame = this._frames.get(frameId);
if (!frame || !frame.pendingDocument())
return;
if (documentId !== void 0 && frame.pendingDocument().documentId !== documentId)
return;
const navigationEvent = {
url: frame._url,
name: frame._name,
newDocument: frame.pendingDocument(),
error: new NavigationAbortedError(documentId, errorText),
isPublic: !(documentId && frame._redirectedNavigations.has(documentId))
};
frame._setPendingDocument(void 0);
this._fireInternalFrameNavigation(frame, navigationEvent);
}
frameDetached(frameId) {
const frame = this._frames.get(frameId);
if (frame) {
this._removeFramesRecursively(frame);
this._page.mainFrame()._recalculateNetworkIdle();
}
}
frameLifecycleEvent(frameId, event) {
const frame = this._frames.get(frameId);
if (frame)
frame.onLifecycleEvent(event);
}
requestStarted(request2, route2) {
const frame = request2.frame();
this._inflightRequestStarted(request2);
if (request2._documentId)
frame._setPendingDocument({ documentId: request2._documentId, request: request2 });
if (request2._isFavicon) {
route2?.abort("aborted").catch(() => {
});
return;
}
this._page.addNetworkRequest(request2);
this._page.emitOnContext(BrowserContext.Events.Request, request2);
if (route2)
new Route(request2, route2).handle([...this._page.requestInterceptors, ...this._page.browserContext.requestInterceptors]);
}
requestReceivedResponse(response2) {
if (response2.request()._isFavicon)
return;
this._page.emitOnContext(BrowserContext.Events.Response, response2);
}
reportRequestFinished(request2, response2) {
this._inflightRequestFinished(request2);
if (request2._isFavicon)
return;
this._page.emitOnContext(BrowserContext.Events.RequestFinished, { request: request2, response: response2 });
}
requestFailed(request2, canceled) {
const frame = request2.frame();
this._inflightRequestFinished(request2);
if (frame.pendingDocument() && frame.pendingDocument().request === request2) {
let errorText = request2.failure().errorText;
if (canceled)
errorText += "; maybe frame was detached?";
this.frameAbortedNavigation(frame._id, errorText, frame.pendingDocument().documentId);
}
if (request2._isFavicon)
return;
this._page.emitOnContext(BrowserContext.Events.RequestFailed, request2);
}
removeChildFramesRecursively(frame) {
for (const child of frame._getChildFrames())
this._removeFramesRecursively(child);
}
_removeFramesRecursively(frame) {
this.removeChildFramesRecursively(frame);
frame._onDetached();
this._frames.delete(frame._id);
if (!this._page.isClosed())
this._page.emit(Page.Events.FrameDetached, frame);
}
_inflightRequestFinished(request2) {
const frame = request2.frame();
if (request2._isFavicon)
return;
if (!frame._inflightRequests.has(request2))
return;
frame._inflightRequests.delete(request2);
if (frame._inflightRequests.size === 0)
frame._startNetworkIdleTimer();
}
_inflightRequestStarted(request2) {
const frame = request2.frame();
if (request2._isFavicon)
return;
frame._inflightRequests.add(request2);
if (frame._inflightRequests.size === 1)
frame._stopNetworkIdleTimer();
}
interceptConsoleMessage(message) {
if (message.type() !== "debug")
return false;
const tag = message.text();
const handler = this._consoleMessageTags.get(tag);
if (!handler)
return false;
this._consoleMessageTags.delete(tag);
handler();
return true;
}
_clearWebSockets(frame) {
if (frame.parentFrame())
return;
this._webSockets.clear();
}
onWebSocketCreated(requestId, url2) {
const ws3 = new WebSocket(this._page, url2);
this._webSockets.set(requestId, ws3);
}
onWebSocketRequest(requestId) {
const ws3 = this._webSockets.get(requestId);
if (ws3 && ws3.markAsNotified())
this._page.emit(Page.Events.WebSocket, ws3);
}
onWebSocketResponse(requestId, status, statusText2) {
const ws3 = this._webSockets.get(requestId);
if (status < 400)
return;
if (ws3)
ws3.error(`${statusText2}: ${status}`);
}
onWebSocketFrameSent(requestId, opcode, data) {
const ws3 = this._webSockets.get(requestId);
if (ws3)
ws3.frameSent(opcode, data);
}
webSocketFrameReceived(requestId, opcode, data) {
const ws3 = this._webSockets.get(requestId);
if (ws3)
ws3.frameReceived(opcode, data);
}
webSocketClosed(requestId) {
const ws3 = this._webSockets.get(requestId);
if (ws3)
ws3.closed();
this._webSockets.delete(requestId);
}
webSocketError(requestId, errorMessage) {
const ws3 = this._webSockets.get(requestId);
if (ws3)
ws3.error(errorMessage);
}
_fireInternalFrameNavigation(frame, event) {
frame.emit(Frame.Events.InternalNavigation, event);
}
};
FrameEvent = {
InternalNavigation: "internalnavigation",
AddLifecycle: "addlifecycle",
RemoveLifecycle: "removelifecycle"
};
Frame = class _Frame extends SdkObject {
constructor(page, id, parentFrame) {
super(page, "frame");
this._firedLifecycleEvents = /* @__PURE__ */ new Set();
this._firedNetworkIdleSelf = false;
this._url = "";
this._contextData = /* @__PURE__ */ new Map();
this._childFrames = /* @__PURE__ */ new Set();
this._name = "";
this._inflightRequests = /* @__PURE__ */ new Set();
this._setContentCounter = 0;
this._detachedScope = new LongStandingScope();
this._raceAgainstEvaluationStallingEventsPromises = /* @__PURE__ */ new Set();
this._redirectedNavigations = /* @__PURE__ */ new Map();
this.attribution.frame = this;
this.seq = page.frameManager._allocateFrameSeq();
this._id = id;
this._page = page;
this._parentFrame = parentFrame;
this._currentDocument = { documentId: void 0, request: void 0 };
this.selectors = new FrameSelectors(this);
this._contextData.set("main", { contextPromise: new ManualPromise(), context: null });
this._contextData.set("utility", { contextPromise: new ManualPromise(), context: null });
this._setContext("main", null);
this._setContext("utility", null);
if (this._parentFrame)
this._parentFrame._childFrames.add(this);
this._firedLifecycleEvents.add("commit");
if (id !== kDummyFrameId)
this._startNetworkIdleTimer();
}
static {
this.Events = FrameEvent;
}
_isDetached() {
return this._detachedScope.isClosed();
}
onLifecycleEvent(event) {
if (this._firedLifecycleEvents.has(event))
return;
this._firedLifecycleEvents.add(event);
this.emit(_Frame.Events.AddLifecycle, event);
if (this === this._page.mainFrame() && this._url !== "about:blank")
this.apiLog(` "${event}" event fired`);
this._page.mainFrame()._recalculateNetworkIdle();
}
_onClearLifecycle() {
for (const event of this._firedLifecycleEvents)
this.emit(_Frame.Events.RemoveLifecycle, event);
this._firedLifecycleEvents.clear();
this._inflightRequests = new Set(Array.from(this._inflightRequests).filter((request2) => request2 === this._currentDocument.request));
this._stopNetworkIdleTimer();
if (this._inflightRequests.size === 0)
this._startNetworkIdleTimer();
this._page.mainFrame()._recalculateNetworkIdle(this);
this.onLifecycleEvent("commit");
}
_setPendingDocument(documentInfo) {
this._pendingDocument = documentInfo;
if (documentInfo)
this.invalidateNonStallingEvaluations("Navigation interrupted the evaluation");
}
pendingDocument() {
return this._pendingDocument;
}
invalidateNonStallingEvaluations(message) {
if (!this._raceAgainstEvaluationStallingEventsPromises.size)
return;
const error = new Error(message);
for (const promise of this._raceAgainstEvaluationStallingEventsPromises)
promise.reject(error);
}
async raceAgainstEvaluationStallingEvents(cb) {
if (this._pendingDocument)
throw new Error("Frame is currently attempting a navigation");
if (this._page.browserContext.dialogManager.hasOpenDialogsForPage(this._page))
throw new Error("Open JavaScript dialog prevents evaluation");
const promise = new ManualPromise();
this._raceAgainstEvaluationStallingEventsPromises.add(promise);
try {
return await Promise.race([
cb(),
promise
]);
} finally {
this._raceAgainstEvaluationStallingEventsPromises.delete(promise);
}
}
nonStallingRawEvaluateInExistingMainContext(expression2) {
return this.raceAgainstEvaluationStallingEvents(() => {
const context2 = this._existingMainContext();
if (!context2)
throw new Error("Frame does not yet have a main execution context");
return context2.rawEvaluateJSON(expression2);
});
}
nonStallingEvaluateInExistingContext(expression2, world) {
return this.raceAgainstEvaluationStallingEvents(() => {
const context2 = this._contextData.get(world)?.context;
if (!context2)
throw new Error("Frame does not yet have the execution context");
return context2.evaluateExpression(expression2, { isFunction: false });
});
}
_recalculateNetworkIdle(frameThatAllowsRemovingNetworkIdle) {
let isNetworkIdle = this._firedNetworkIdleSelf;
for (const child of this._childFrames) {
child._recalculateNetworkIdle(frameThatAllowsRemovingNetworkIdle);
if (!child._firedLifecycleEvents.has("networkidle"))
isNetworkIdle = false;
}
if (isNetworkIdle && !this._firedLifecycleEvents.has("networkidle")) {
this._firedLifecycleEvents.add("networkidle");
this.emit(_Frame.Events.AddLifecycle, "networkidle");
if (this === this._page.mainFrame() && this._url !== "about:blank")
this.apiLog(` "networkidle" event fired`);
}
if (frameThatAllowsRemovingNetworkIdle !== this && this._firedLifecycleEvents.has("networkidle") && !isNetworkIdle) {
this._firedLifecycleEvents.delete("networkidle");
this.emit(_Frame.Events.RemoveLifecycle, "networkidle");
}
}
async raceNavigationAction(progress2, action) {
progress2.setAllowConcurrentOrNestedRaces(true);
const result2 = await progress2.race(LongStandingScope.raceMultiple([
this._detachedScope,
this._page.openScope
], action().catch((e) => {
if (e instanceof NavigationAbortedError && e.documentId) {
const data = this._redirectedNavigations.get(e.documentId);
if (data) {
progress2.log(`waiting for redirected navigation to "${data.url}"`);
return progress2.race(data.gotoPromise);
}
}
throw e;
})));
progress2.setAllowConcurrentOrNestedRaces(false);
return result2;
}
redirectNavigation(url2, documentId, referer) {
const controller = new ProgressController();
const data = {
url: url2,
gotoPromise: controller.run((progress2) => this.gotoImpl(progress2, url2, { referer }), 0)
};
this._redirectedNavigations.set(documentId, data);
data.gotoPromise.finally(() => this._redirectedNavigations.delete(documentId));
}
async goto(progress2, url2, options2 = {}) {
const constructedNavigationURL = constructURLBasedOnBaseURL(this._page.browserContext._options.baseURL, url2);
return this.raceNavigationAction(progress2, async () => this.gotoImpl(progress2, constructedNavigationURL, options2));
}
async gotoImpl(progress2, url2, options2) {
const waitUntil = verifyLifecycle("waitUntil", options2.waitUntil === void 0 ? "load" : options2.waitUntil);
progress2.log(`navigating to "${url2}", waiting until "${waitUntil}"`);
const headers = this._page.extraHTTPHeaders() || [];
const refererHeader = headers.find((h) => h.name.toLowerCase() === "referer");
let referer = refererHeader ? refererHeader.value : void 0;
if (options2.referer !== void 0) {
if (referer !== void 0 && referer !== options2.referer)
throw new Error('"referer" is already specified as extra HTTP header');
referer = options2.referer;
}
url2 = helper.completeUserURL(url2);
const navigationEvents = [];
const collectNavigations = (arg) => navigationEvents.push(arg);
this.on(_Frame.Events.InternalNavigation, collectNavigations);
let navigateResult;
try {
navigateResult = await progress2.race(this._page.delegate.navigateFrame(this, url2, referer));
} finally {
this.off(_Frame.Events.InternalNavigation, collectNavigations);
}
let event;
if (navigateResult.newDocumentId) {
const predicate = (event2) => {
return event2.newDocument && (event2.newDocument.documentId === navigateResult.newDocumentId || !event2.error);
};
const events = navigationEvents.filter(predicate);
if (events.length)
event = events[0];
else
event = await helper.waitForEvent(progress2, this, _Frame.Events.InternalNavigation, predicate).promise;
if (event.newDocument.documentId !== navigateResult.newDocumentId) {
throw new NavigationAbortedError(navigateResult.newDocumentId, `Navigation to "${url2}" is interrupted by another navigation to "${event.url}"`);
}
if (event.error)
throw event.error;
} else {
const predicate = (e) => !e.newDocument;
const events = navigationEvents.filter(predicate);
if (events.length)
event = events[0];
else
event = await helper.waitForEvent(progress2, this, _Frame.Events.InternalNavigation, predicate).promise;
}
if (!this._firedLifecycleEvents.has(waitUntil))
await helper.waitForEvent(progress2, this, _Frame.Events.AddLifecycle, (e) => e === waitUntil).promise;
const request2 = event.newDocument ? event.newDocument.request : void 0;
const response2 = request2 ? await request2._finalRequest().response(progress2) : null;
return response2;
}
async waitForNavigation(progress2, requiresNewDocument, options2) {
const waitUntil = verifyLifecycle("waitUntil", options2.waitUntil === void 0 ? "load" : options2.waitUntil);
progress2.log(`waiting for navigation until "${waitUntil}"`);
const navigationEvent = await helper.waitForEvent(progress2, this, _Frame.Events.InternalNavigation, (event) => {
if (event.error)
return true;
if (requiresNewDocument && !event.newDocument)
return false;
progress2.log(` navigated to "${this._url}"`);
return true;
}).promise;
if (navigationEvent.error)
throw navigationEvent.error;
if (!this._firedLifecycleEvents.has(waitUntil))
await helper.waitForEvent(progress2, this, _Frame.Events.AddLifecycle, (e) => e === waitUntil).promise;
const request2 = navigationEvent.newDocument ? navigationEvent.newDocument.request : void 0;
return request2 ? request2._finalRequest().response(progress2) : null;
}
async waitForLoadState(progress2, state) {
const waitUntil = verifyLifecycle("state", state);
if (!this._firedLifecycleEvents.has(waitUntil))
await helper.waitForEvent(progress2, this, _Frame.Events.AddLifecycle, (e) => e === waitUntil).promise;
}
async frameElement(progress2) {
return await progress2.race(this._page.delegate.getFrameElement(this));
}
context(world) {
return this._contextData.get(world).contextPromise.then((contextOrDestroyedReason) => {
if (contextOrDestroyedReason instanceof ExecutionContext)
return contextOrDestroyedReason;
throw new Error(contextOrDestroyedReason.destroyedReason);
});
}
mainContext() {
return this.context("main");
}
_existingMainContext() {
return this._contextData.get("main")?.context || null;
}
utilityContext() {
return this.context("utility");
}
async evaluateExpression(progress2, expression2, options2 = {}, arg) {
return await progress2.race(this._evaluateExpression(expression2, options2, arg));
}
async _evaluateExpression(expression2, options2 = {}, arg) {
const context2 = await this.context(options2.world ?? "main");
const value2 = await context2.evaluateExpression(expression2, options2, arg);
return value2;
}
async evaluateExpressionHandle(progress2, expression2, options2 = {}, arg) {
return await progress2.race(this._evaluateExpressionHandle(expression2, options2, arg));
}
async _evaluateExpressionHandle(expression2, options2 = {}, arg) {
const context2 = await this.context(options2.world ?? "main");
const value2 = await context2.evaluateExpressionHandle(expression2, options2, arg);
return value2;
}
async querySelector(progress2, selector, options2) {
this.apiLog(` finding element using the selector "${selector}"`);
return progress2.race(this.selectors.query(selector, options2));
}
async waitForSelector(progress2, selector, performActionPreChecksAndLog, options2, scope) {
if (options2.visibility)
throw new Error("options.visibility is not supported, did you mean options.state?");
if (options2.waitFor && options2.waitFor !== "visible")
throw new Error("options.waitFor is not supported, did you mean options.state?");
const { state = "visible" } = options2;
if (!["attached", "detached", "visible", "hidden"].includes(state))
throw new Error(`state: expected one of (attached|detached|visible|hidden)`);
if (performActionPreChecksAndLog)
progress2.log(`waiting for ${this._asLocator(selector)}${state === "attached" ? "" : " to be " + state}`);
const promise = this.retryWithProgressAndBackoff(progress2, async (progress3, continuePolling) => {
if (performActionPreChecksAndLog)
await this._page.performActionPreChecks(progress3);
const resolved = await progress3.race(this.selectors.resolveInjectedForSelector(selector, options2, scope));
if (!resolved) {
if (state === "hidden" || state === "detached")
return null;
return continuePolling;
}
const result2 = await progress3.race(resolved.injected.evaluateHandle((injected, { info, root }) => {
if (root && !root.isConnected)
throw injected.createStacklessError("Element is not attached to the DOM");
const elements = injected.querySelectorAll(info.parsed, root || document);
const element3 = elements[0];
const visible2 = element3 ? injected.utils.isElementVisible(element3) : false;
let log3 = "";
if (elements.length > 1) {
if (info.strict)
throw injected.strictModeViolationError(info.parsed, elements);
log3 = ` locator resolved to ${elements.length} elements. Proceeding with the first one: ${injected.previewNode(elements[0])}`;
} else if (element3) {
log3 = ` locator resolved to ${visible2 ? "visible" : "hidden"} ${injected.previewNode(element3)}`;
}
injected.checkDeprecatedSelectorUsage(info.parsed, elements);
return { log: log3, element: element3, visible: visible2, attached: !!element3 };
}, { info: resolved.info, root: resolved.frame === this ? scope : void 0 }));
const { log: log2, visible, attached } = await progress3.race(result2.evaluate((r) => ({ log: r.log, visible: r.visible, attached: r.attached })));
if (log2)
progress3.log(log2);
const success = { attached, detached: !attached, visible, hidden: !visible }[state];
if (!success) {
result2.dispose();
return continuePolling;
}
if (options2.omitReturnValue) {
result2.dispose();
return null;
}
const element2 = state === "attached" || state === "visible" ? await progress3.race(result2.evaluateHandle((r) => r.element)) : null;
result2.dispose();
if (!element2)
return null;
if (options2.__testHookBeforeAdoptNode)
await progress3.race(options2.__testHookBeforeAdoptNode());
try {
const mainContext = await progress3.race(resolved.frame.mainContext());
return await progress3.race(element2._adoptTo(mainContext));
} catch (e) {
return continuePolling;
}
});
return scope ? scope._context.raceAgainstContextDestroyed(promise) : promise;
}
async dispatchEvent(progress2, selector, type3, eventInit = {}, options2, scope) {
await this._callOnElementOnceMatches(progress2, selector, (injectedScript, element2, data) => {
injectedScript.dispatchEvent(element2, data.type, data.eventInit);
}, { type: type3, eventInit }, { mainWorld: true, ...options2 }, scope);
}
async evalOnSelector(progress2, selector, strict, expression2, isFunction2, arg, scope) {
return progress2.race(this._evalOnSelector(selector, strict, expression2, isFunction2, arg, scope));
}
async _evalOnSelector(selector, strict, expression2, isFunction2, arg, scope) {
const handle = await this.selectors.query(selector, { strict }, scope);
if (!handle)
throw new Error(`Failed to find element matching selector "${selector}"`);
const result2 = await handle.internalEvaluateExpression(expression2, { isFunction: isFunction2 }, arg);
handle.dispose();
return result2;
}
async evalOnSelectorAll(progress2, selector, expression2, isFunction2, arg, scope) {
return progress2.race(this._evalOnSelectorAll(selector, expression2, isFunction2, arg, scope));
}
async _evalOnSelectorAll(selector, expression2, isFunction2, arg, scope) {
const arrayHandle = await this.selectors.queryArrayInMainWorld(selector, scope);
const result2 = await arrayHandle.internalEvaluateExpression(expression2, { isFunction: isFunction2 }, arg);
arrayHandle.dispose();
return result2;
}
async maskSelectors(selectors, color) {
const context2 = await this.utilityContext();
const injectedScript = await context2.injectedScript();
await injectedScript.evaluate((injected, { parsed, color: color2 }) => {
injected.maskSelectors(parsed, color2);
}, { parsed: selectors, color });
}
async querySelectorAll(progress2, selector) {
return progress2.race(this.selectors.queryAll(selector));
}
async queryCount(progress2, selector, options2) {
try {
return await progress2.race(this.selectors.queryCount(selector, options2));
} catch (e) {
if (this.isNonRetriableError(e))
throw e;
return 0;
}
}
async content(progress2) {
return progress2.race(this._content());
}
async _content() {
try {
const context2 = await this.utilityContext();
return await context2.evaluate(() => {
let retVal = "";
if (document.doctype)
retVal = new XMLSerializer().serializeToString(document.doctype);
if (document.documentElement)
retVal += document.documentElement.outerHTML;
return retVal;
});
} catch (e) {
if (this.isNonRetriableError(e))
throw e;
throw new Error(`Unable to retrieve content because the page is navigating and changing the content.`);
}
}
async setContent(progress2, html, options2) {
const tag = `--playwright--set--content--${this._id}--${++this._setContentCounter}--`;
await this.raceNavigationAction(progress2, async () => {
const waitUntil = options2.waitUntil === void 0 ? "load" : options2.waitUntil;
progress2.log(`setting frame content, waiting until "${waitUntil}"`);
const context2 = await progress2.race(this.utilityContext());
const tagPromise = new ManualPromise();
this._page.frameManager._consoleMessageTags.set(tag, () => {
this._onClearLifecycle();
tagPromise.resolve();
});
progress2.setAllowConcurrentOrNestedRaces(true);
const lifecyclePromise = progress2.race(tagPromise).then(() => this.waitForLoadState(progress2, waitUntil));
const contentPromise = progress2.race(context2.evaluate(({ html: html2, tag: tag2 }) => {
document.open();
console.debug(tag2);
document.write(html2);
document.close();
}, { html, tag }));
await Promise.all([contentPromise, lifecyclePromise]);
progress2.setAllowConcurrentOrNestedRaces(false);
return null;
}).finally(() => {
this._page.frameManager._consoleMessageTags.delete(tag);
});
}
name() {
return this._name || "";
}
url() {
return this._url;
}
origin() {
if (!this._url.startsWith("http"))
return;
return parseURL2(this._url)?.origin;
}
parentFrame() {
return this._parentFrame;
}
_getChildFrames() {
return Array.from(this._childFrames);
}
async addScriptTag(progress2, params2) {
return await progress2.race(this._addScriptTag(params2));
}
async _addScriptTag(params2) {
const {
url: url2 = null,
content = null,
type: type3 = ""
} = params2;
if (!url2 && !content)
throw new Error("Provide an object with a `url`, `path` or `content` property");
const context2 = await this.mainContext();
return this._raceWithCSPError(async () => {
if (url2 !== null)
return (await context2.evaluateHandle(addScriptUrl, { url: url2, type: type3 })).asElement();
const result2 = (await context2.evaluateHandle(addScriptContent, { content, type: type3 })).asElement();
if (this._page.delegate.cspErrorsAsynchronousForInlineScripts)
await context2.evaluate(() => true);
return result2;
});
async function addScriptUrl(params3) {
const script = document.createElement("script");
script.src = params3.url;
if (params3.type)
script.type = params3.type;
const promise = new Promise((res, rej) => {
script.onload = res;
script.onerror = (e) => rej(typeof e === "string" ? new Error(e) : new Error(`Failed to load script at ${script.src}`));
});
document.head.appendChild(script);
await promise;
return script;
}
function addScriptContent(params3) {
const script = document.createElement("script");
script.type = params3.type || "text/javascript";
script.text = params3.content;
let error = null;
script.onerror = (e) => error = e;
document.head.appendChild(script);
if (error)
throw error;
return script;
}
}
async addStyleTag(progress2, params2) {
return await progress2.race(this._addStyleTag(params2));
}
async _addStyleTag(params2) {
const {
url: url2 = null,
content = null
} = params2;
if (!url2 && !content)
throw new Error("Provide an object with a `url`, `path` or `content` property");
const context2 = await this.mainContext();
return this._raceWithCSPError(async () => {
if (url2 !== null)
return (await context2.evaluateHandle(addStyleUrl, url2)).asElement();
return (await context2.evaluateHandle(addStyleContent, content)).asElement();
});
async function addStyleUrl(url3) {
const link = document.createElement("link");
link.rel = "stylesheet";
link.href = url3;
const promise = new Promise((res, rej) => {
link.onload = res;
link.onerror = rej;
});
document.head.appendChild(link);
await promise;
return link;
}
async function addStyleContent(content2) {
const style = document.createElement("style");
style.type = "text/css";
style.appendChild(document.createTextNode(content2));
const promise = new Promise((res, rej) => {
style.onload = res;
style.onerror = rej;
});
document.head.appendChild(style);
await promise;
return style;
}
}
async _raceWithCSPError(func) {
const listeners = [];
let result2;
let error;
let cspMessage;
const actionPromise = func().then((r) => result2 = r).catch((e) => error = e);
const errorPromise = new Promise((resolve) => {
listeners.push(eventsHelper.addEventListener(this._page.browserContext, BrowserContext.Events.Console, (message) => {
if (message.page() !== this._page || message.type() !== "error")
return;
if (message.text().includes("Content-Security-Policy") || message.text().includes("Content Security Policy")) {
cspMessage = message;
resolve();
}
}));
});
await Promise.race([actionPromise, errorPromise]);
eventsHelper.removeEventListeners(listeners);
if (cspMessage)
throw new Error(cspMessage.text());
if (error)
throw error;
return result2;
}
async retryWithProgressAndBackoff(progress2, action) {
const backoffScale = [20, 50, 100, 100, 500];
if (progress2.timeout) {
while (backoffScale.length && backoffScale[backoffScale.length - 1] > progress2.timeout / 5)
backoffScale.pop();
}
return await this.retryWithProgressAndTimeouts(progress2, backoffScale, action);
}
async retryWithProgressAndTimeouts(progress2, timeouts, action) {
const continuePolling = Symbol("continuePolling");
timeouts = [0, ...timeouts];
let timeoutIndex = 0;
while (true) {
const timeout = timeouts[Math.min(timeoutIndex++, timeouts.length - 1)];
if (timeout) {
const actionPromise = new Promise((f) => setTimeout(f, timeout));
await progress2.race(LongStandingScope.raceMultiple([
this._page.openScope,
this._detachedScope
], actionPromise));
}
try {
const result2 = await action(progress2, continuePolling);
if (result2 === continuePolling)
continue;
return result2;
} catch (e) {
if (this.isNonRetriableError(e))
throw e;
continue;
}
}
}
isNonRetriableError(e) {
if (isAbortError(e))
return true;
if (isJavaScriptErrorInEvaluate(e) || isSessionClosedError(e))
return true;
if (isNonRecoverableDOMError(e) || isInvalidSelectorError(e))
return true;
if (this._isDetached())
return true;
return false;
}
async _retryWithProgressIfNotConnected(progress2, selector, options2, action) {
progress2.log(`waiting for ${this._asLocator(selector)}`);
const noAutoWaiting = options2.__testHookNoAutoWaiting ?? options2.noAutoWaiting;
const performActionPreChecks = (options2.performActionPreChecks ?? !options2.force) && !noAutoWaiting;
return this.retryWithProgressAndBackoff(progress2, async (progress3, continuePolling) => {
if (performActionPreChecks)
await this._page.performActionPreChecks(progress3);
const resolved = await progress3.race(this.selectors.resolveInjectedForSelector(selector, { strict: options2.strict }));
if (!resolved) {
if (noAutoWaiting)
throw new NonRecoverableDOMError("Element(s) not found");
return continuePolling;
}
const result2 = await progress3.race(resolved.injected.evaluateHandle((injected, { info, callId }) => {
const elements = injected.querySelectorAll(info.parsed, document);
if (callId)
injected.markTargetElements(new Set(elements), callId);
const element3 = elements[0];
let log3 = "";
if (elements.length > 1) {
if (info.strict)
throw injected.strictModeViolationError(info.parsed, elements);
log3 = ` locator resolved to ${elements.length} elements. Proceeding with the first one: ${injected.previewNode(elements[0])}`;
} else if (element3) {
log3 = ` locator resolved to ${injected.previewNode(element3)}`;
}
injected.checkDeprecatedSelectorUsage(info.parsed, elements);
return { log: log3, success: !!element3, element: element3 };
}, { info: resolved.info, callId: progress3.metadata.id }));
const { log: log2, success } = await progress3.race(result2.evaluate((r) => ({ log: r.log, success: r.success })));
if (log2)
progress3.log(log2);
if (!success) {
if (noAutoWaiting)
throw new NonRecoverableDOMError("Element(s) not found");
result2.dispose();
return continuePolling;
}
const element2 = await progress3.race(result2.evaluateHandle((r) => r.element));
result2.dispose();
try {
const result3 = await action(progress3, element2);
if (result3 === "error:notconnected") {
if (noAutoWaiting)
throw new NonRecoverableDOMError("Element is not attached to the DOM");
progress3.log("element was detached from the DOM, retrying");
return continuePolling;
}
return result3;
} finally {
element2?.dispose();
}
});
}
async rafrafTimeoutScreenshotElementWithProgress(progress2, selector, timeout, options2) {
return await this._retryWithProgressIfNotConnected(progress2, selector, { strict: true, performActionPreChecks: true }, async (progress3, handle) => {
await handle._frame.rafrafTimeout(progress3, timeout);
return await this._page.screenshotter.screenshotElement(progress3, handle, options2);
});
}
async click(progress2, selector, options2) {
return assertDone(await this._retryWithProgressIfNotConnected(progress2, selector, options2, (progress3, handle) => handle._click(progress3, { ...options2, waitAfter: !options2.noWaitAfter })));
}
async dblclick(progress2, selector, options2) {
return assertDone(await this._retryWithProgressIfNotConnected(progress2, selector, options2, (progress3, handle) => handle._dblclick(progress3, options2)));
}
async dragAndDrop(progress2, source8, target, options2) {
assertDone(await this._retryWithProgressIfNotConnected(progress2, source8, options2, async (progress3, handle) => {
return handle._retryPointerAction(progress3, "move and down", false, async (progress4, point) => {
await this._page.mouse.move(progress4, point.x, point.y);
await this._page.mouse.down(progress4);
}, {
...options2,
waitAfter: "disabled",
position: options2.sourcePosition
});
}));
assertDone(await this._retryWithProgressIfNotConnected(progress2, target, { ...options2, performActionPreChecks: false }, async (progress3, handle) => {
return handle._retryPointerAction(progress3, "move and up", false, async (progress4, point) => {
await this._page.mouse.move(progress4, point.x, point.y, { steps: options2.steps });
await this._page.mouse.up(progress4);
}, {
...options2,
waitAfter: "disabled",
position: options2.targetPosition
});
}));
}
async tap(progress2, selector, options2) {
if (!this._page.browserContext._options.hasTouch)
throw new Error("The page does not support tap. Use hasTouch context option to enable touch support.");
return assertDone(await this._retryWithProgressIfNotConnected(progress2, selector, options2, (progress3, handle) => handle._tap(progress3, options2)));
}
async fill(progress2, selector, value2, options2) {
return assertDone(await this._retryWithProgressIfNotConnected(progress2, selector, options2, (progress3, handle) => handle._fill(progress3, value2, options2)));
}
async focus(progress2, selector, options2) {
assertDone(await this._retryWithProgressIfNotConnected(progress2, selector, options2, (progress3, handle) => handle._focus(progress3)));
}
async blur(progress2, selector, options2) {
assertDone(await this._retryWithProgressIfNotConnected(progress2, selector, options2, (progress3, handle) => handle._blur(progress3)));
}
async resolveSelector(progress2, selector, options2 = {}) {
const element2 = await progress2.race(this.selectors.query(selector, options2));
if (!element2)
throw new Error(`No element matching ${selector}`);
const generated = await progress2.race(element2.evaluateInUtility(async ([injected, node]) => {
return injected.generateSelectorSimple(node);
}, {}));
if (!generated)
throw new Error(`Unable to generate locator for ${selector}`);
let frame = element2._frame;
const result2 = [generated];
while (frame?.parentFrame()) {
const frameElement = await frame.frameElement(progress2);
if (frameElement) {
const generated2 = await progress2.race(frameElement.evaluateInUtility(async ([injected, node]) => {
return injected.generateSelectorSimple(node);
}, {}));
frameElement.dispose();
if (generated2 === "error:notconnected" || !generated2)
throw new Error(`Unable to generate locator for ${selector}`);
result2.push(generated2);
}
frame = frame.parentFrame();
}
const resolvedSelector = result2.reverse().join(" >> internal:control=enter-frame >> ");
return { resolvedSelector };
}
async textContent(progress2, selector, options2, scope) {
return this._callOnElementOnceMatches(progress2, selector, (injected, element2) => element2.textContent, void 0, options2, scope);
}
async innerText(progress2, selector, options2, scope) {
return this._callOnElementOnceMatches(progress2, selector, (injectedScript, element2) => {
if (element2.namespaceURI !== "http://www.w3.org/1999/xhtml")
throw injectedScript.createStacklessError("Node is not an HTMLElement");
return element2.innerText;
}, void 0, options2, scope);
}
async innerHTML(progress2, selector, options2, scope) {
return this._callOnElementOnceMatches(progress2, selector, (injected, element2) => element2.innerHTML, void 0, options2, scope);
}
async getAttribute(progress2, selector, name, options2, scope) {
return this._callOnElementOnceMatches(progress2, selector, (injected, element2, data) => element2.getAttribute(data.name), { name }, options2, scope);
}
async inputValue(progress2, selector, options2, scope) {
return this._callOnElementOnceMatches(progress2, selector, (injectedScript, node) => {
const element2 = injectedScript.retarget(node, "follow-label");
if (!element2 || element2.nodeName !== "INPUT" && element2.nodeName !== "TEXTAREA" && element2.nodeName !== "SELECT")
throw injectedScript.createStacklessError("Node is not an <input>, <textarea> or <select> element");
return element2.value;
}, void 0, options2, scope);
}
async addHighlight(progress2, selector, style) {
const resolved = await progress2.race(this.selectors.resolveInjectedForSelector(selector));
if (!resolved)
return;
return await progress2.race(resolved.injected.evaluate((injected, { info, style: style2 }) => {
return injected.addHighlight(info.parsed, style2);
}, { info: resolved.info, style }));
}
async removeHighlight(progress2, selector) {
const resolved = await progress2.race(this.selectors.resolveInjectedForSelector(selector));
if (!resolved)
return;
return await progress2.race(resolved.injected.evaluate((injected, { info }) => {
return injected.removeHighlight(info.parsed);
}, { info: resolved.info }));
}
async hideHighlight() {
return this.raceAgainstEvaluationStallingEvents(async () => {
const context2 = await this.utilityContext();
const injectedScript = await context2.injectedScript();
return await injectedScript.evaluate((injected) => {
return injected.hideHighlight();
});
});
}
async _elementState(progress2, selector, state, options2, scope) {
const result2 = await this._callOnElementOnceMatches(progress2, selector, (injected, element2, data) => {
return injected.elementState(element2, data.state);
}, { state }, options2, scope);
if (result2.received === "error:notconnected")
throwElementIsNotAttached();
return result2.matches;
}
async isVisible(progress2, selector, options2 = {}, scope) {
progress2.log(` checking visibility of ${this._asLocator(selector)}`);
return await this.isVisibleInternal(progress2, selector, options2, scope);
}
async isVisibleInternal(progress2, selector, options2 = {}, scope) {
try {
const resolved = await progress2.race(this.selectors.resolveInjectedForSelector(selector, options2, scope));
if (!resolved)
return false;
return await progress2.race(resolved.injected.evaluate((injected, { info, root }) => {
const element2 = injected.querySelector(info.parsed, root || document, info.strict);
const state = element2 ? injected.elementState(element2, "visible") : { matches: false, received: "error:notconnected" };
return state.matches;
}, { info: resolved.info, root: resolved.frame === this ? scope : void 0 }));
} catch (e) {
if (this.isNonRetriableError(e))
throw e;
return false;
}
}
async isHidden(progress2, selector, options2 = {}, scope) {
return !await this.isVisible(progress2, selector, options2, scope);
}
async isDisabled(progress2, selector, options2, scope) {
return this._elementState(progress2, selector, "disabled", options2, scope);
}
async isEnabled(progress2, selector, options2, scope) {
return this._elementState(progress2, selector, "enabled", options2, scope);
}
async isEditable(progress2, selector, options2, scope) {
return this._elementState(progress2, selector, "editable", options2, scope);
}
async isChecked(progress2, selector, options2, scope) {
return this._elementState(progress2, selector, "checked", options2, scope);
}
async hover(progress2, selector, options2) {
return assertDone(await this._retryWithProgressIfNotConnected(progress2, selector, options2, (progress3, handle) => handle._hover(progress3, options2)));
}
async selectOption(progress2, selector, elements, values, options2) {
return await this._retryWithProgressIfNotConnected(progress2, selector, options2, (progress3, handle) => handle._selectOption(progress3, elements, values, options2));
}
async setInputFiles(progress2, selector, params2) {
const inputFileItems = await progress2.race(prepareFilesForUpload(this, params2));
return assertDone(await this._retryWithProgressIfNotConnected(progress2, selector, params2, (progress3, handle) => handle._setInputFiles(progress3, inputFileItems)));
}
async drop(progress2, selector, params2, options2) {
const hasFiles = !!(params2.payloads?.length || params2.localPaths?.length || params2.streams?.length);
const hasData = !!params2.data?.length;
if (!hasFiles && !hasData)
throw new Error('At least one of "files" or "data" must be provided.');
const inputFileItems = hasFiles ? await progress2.race(prepareFilesForUpload(this, params2)) : { filePayloads: void 0, localPaths: void 0 };
const data = params2.data ?? [];
assertDone(await this._retryWithProgressIfNotConnected(progress2, selector, options2, (progress3, handle) => handle._drop(progress3, inputFileItems, data, options2)));
}
async type(progress2, selector, text2, options2) {
return assertDone(await this._retryWithProgressIfNotConnected(progress2, selector, options2, (progress3, handle) => handle._type(progress3, text2, options2)));
}
async press(progress2, selector, key, options2) {
return assertDone(await this._retryWithProgressIfNotConnected(progress2, selector, options2, (progress3, handle) => handle._press(progress3, key, options2)));
}
async check(progress2, selector, options2) {
return assertDone(await this._retryWithProgressIfNotConnected(progress2, selector, options2, (progress3, handle) => handle._setChecked(progress3, true, options2)));
}
async uncheck(progress2, selector, options2) {
return assertDone(await this._retryWithProgressIfNotConnected(progress2, selector, options2, (progress3, handle) => handle._setChecked(progress3, false, options2)));
}
async waitForTimeout(progress2, timeout) {
return progress2.wait(timeout);
}
async expect(progress2, selector, options2) {
progress2.log(`${renderTitleForCall(progress2.metadata)}${options2.timeoutForLogs ? ` with timeout ${options2.timeoutForLogs}ms` : ""}`);
const lastIntermediateResult = { isSet: false };
const fixupMetadataError = (result2) => {
if (result2.matches === options2.isNot)
progress2.metadata.error = { error: { name: "Expect", message: "Expect failed" } };
};
try {
if (selector)
progress2.log(`waiting for ${this._asLocator(selector)}`);
if (!options2.noAutoWaiting)
await this._page.performActionPreChecks(progress2);
try {
const resultOneShot = await this._expectInternal(progress2, selector, options2, lastIntermediateResult, true);
if (options2.noAutoWaiting || resultOneShot.matches !== options2.isNot)
return resultOneShot;
} catch (e) {
if (options2.noAutoWaiting || this.isNonRetriableError(e))
throw e;
}
const result2 = await this.retryWithProgressAndBackoff(progress2, async (progress3, continuePolling) => {
if (!options2.noAutoWaiting)
await this._page.performActionPreChecks(progress3);
const { matches, received } = await this._expectInternal(progress3, selector, options2, lastIntermediateResult, false);
if (matches === options2.isNot) {
return continuePolling;
}
return { matches, received };
});
fixupMetadataError(result2);
return result2;
} catch (e) {
const result2 = { matches: options2.isNot, log: compressCallLog(progress2.metadata.log) };
if (isInvalidSelectorError(e)) {
result2.errorMessage = "Error: " + e.message;
} else if (isJavaScriptErrorInEvaluate(e)) {
result2.errorMessage = e.message;
} else if (lastIntermediateResult.isSet) {
result2.received = lastIntermediateResult.received;
result2.errorMessage = lastIntermediateResult.errorMessage;
}
if (e instanceof TimeoutError)
result2.timedOut = true;
fixupMetadataError(result2);
return result2;
}
}
async _expectInternal(progress2, selector, options2, lastIntermediateResult, noAbort) {
const progressLog = (text2) => progress2.log(text2);
const callId = progress2.metadata.id;
if (noAbort)
progress2 = nullProgress;
const selectorInFrame = selector ? await progress2.race(this.selectors.resolveFrameForSelector(selector, { strict: true })) : void 0;
const { frame, info } = selectorInFrame || { frame: this, info: void 0 };
const world = options2.expression === "to.have.property" ? "main" : info?.world ?? "utility";
const context2 = await progress2.race(frame.context(world));
const injected = await progress2.race(context2.injectedScript());
const { log: log2, matches, received, missingReceived } = await progress2.race(injected.evaluate(async (injected2, { info: info2, options: options3, callId: callId2 }) => {
const elements = info2 ? injected2.querySelectorAll(info2.parsed, document) : [];
if (callId2)
injected2.markTargetElements(new Set(elements), callId2);
const isArray = options3.expression === "to.have.count" || options3.expression.endsWith(".array");
let log3 = "";
if (isArray)
log3 = ` locator resolved to ${elements.length} element${elements.length === 1 ? "" : "s"}`;
else if (elements.length > 1)
throw injected2.strictModeViolationError(info2.parsed, elements);
else if (elements.length)
log3 = ` locator resolved to ${injected2.previewNode(elements[0])}`;
if (info2)
injected2.checkDeprecatedSelectorUsage(info2.parsed, elements);
return { log: log3, ...await injected2.expect(elements[0], options3, elements) };
}, { info, options: options2, callId }));
if (log2)
progressLog(log2);
if (matches === options2.isNot) {
lastIntermediateResult.errorMessage = missingReceived ? "Error: element(s) not found" : void 0;
lastIntermediateResult.received = received;
lastIntermediateResult.isSet = true;
if (!missingReceived && !Array.isArray(received?.value))
progressLog(` unexpected value "${renderUnexpectedValue(options2.expression, received?.value)}"`);
}
return { matches, received };
}
async waitForFunctionExpression(progress2, expression2, isFunction2, arg, options2, world = "main") {
if (typeof options2.pollingInterval === "number")
assert(options2.pollingInterval > 0, "Cannot poll with non-positive interval: " + options2.pollingInterval);
expression2 = normalizeEvaluationExpression(expression2, isFunction2);
return this.retryWithProgressAndTimeouts(progress2, [100], async () => {
const context2 = world === "main" ? await progress2.race(this.mainContext()) : await progress2.race(this.utilityContext());
const injectedScript = await progress2.race(context2.injectedScript());
const handle = await progress2.race(injectedScript.evaluateHandle((injected, { expression: expression3, isFunction: isFunction3, polling, arg: arg2 }) => {
let evaledExpression;
const predicate = () => {
let result3 = evaledExpression ?? globalThis.eval(expression3);
if (isFunction3 === true) {
evaledExpression = result3;
result3 = result3(arg2);
} else if (isFunction3 === false) {
result3 = result3;
} else {
if (typeof result3 === "function") {
evaledExpression = result3;
result3 = result3(arg2);
}
}
return result3;
};
let fulfill;
let reject;
let aborted = false;
const result2 = new Promise((f, r) => {
fulfill = f;
reject = r;
});
const next = () => {
if (aborted)
return;
try {
const success = predicate();
if (success) {
fulfill(success);
return;
}
if (typeof polling !== "number")
injected.utils.builtins.requestAnimationFrame(next);
else
injected.utils.builtins.setTimeout(next, polling);
} catch (e) {
reject(e);
}
};
next();
return { result: result2, abort: () => aborted = true };
}, { expression: expression2, isFunction: isFunction2, polling: options2.pollingInterval, arg }));
try {
return await progress2.race(handle.evaluateHandle((h) => h.result));
} catch (error) {
await handle.evaluate((h) => h.abort()).catch(() => {
});
throw error;
} finally {
handle.dispose();
}
});
}
async waitForFunctionValueInUtility(progress2, pageFunction) {
const expression2 = `() => {
const result = (${pageFunction})();
if (!result)
return result;
return JSON.stringify(result);
}`;
const handle = await this.waitForFunctionExpression(progress2, expression2, true, void 0, {}, "utility");
return JSON.parse(handle.rawValue());
}
async title(progress2) {
return progress2.race(this._title());
}
async _title() {
try {
return await this.raceAgainstEvaluationStallingEvents(async () => {
const context2 = await this.utilityContext();
return await context2.evaluate(() => document.title);
});
} catch {
const url2 = this.pendingDocument()?.request?.url();
if (url2)
return `Loading ${url2}`;
return "";
}
}
async rafrafTimeout(progress2, timeout) {
if (timeout === 0)
return;
const context2 = await progress2.race(this.utilityContext());
progress2.setAllowConcurrentOrNestedRaces(true);
await Promise.all([
// wait for double raf
progress2.race(context2.evaluate(() => new Promise((x) => {
requestAnimationFrame(() => {
requestAnimationFrame(x);
});
}))),
progress2.wait(timeout)
]);
progress2.setAllowConcurrentOrNestedRaces(false);
}
_onDetached() {
this._stopNetworkIdleTimer();
this._detachedScope.close(new Error("Frame was detached"));
for (const data of this._contextData.values()) {
if (data.context)
data.context.contextDestroyed("Frame was detached");
data.contextPromise.resolve({ destroyedReason: "Frame was detached" });
}
if (this._parentFrame)
this._parentFrame._childFrames.delete(this);
this._parentFrame = null;
}
async _callOnElementOnceMatches(progress2, selector, body, taskData, options2, scope) {
const callbackText = body.toString();
progress2.log(`waiting for ${this._asLocator(selector)}`);
const promise = this.retryWithProgressAndBackoff(progress2, async (progress3, continuePolling) => {
const resolved = await progress3.race(this.selectors.resolveInjectedForSelector(selector, options2, scope));
if (!resolved)
return continuePolling;
const { log: log2, success, value: value2 } = await progress3.race(resolved.injected.evaluate((injected, { info, callbackText: callbackText2, taskData: taskData2, callId, root }) => {
const callback = injected.eval(callbackText2);
const element2 = injected.querySelector(info.parsed, root || document, info.strict);
if (!element2)
return { success: false };
const log3 = ` locator resolved to ${injected.previewNode(element2)}`;
if (callId)
injected.markTargetElements(/* @__PURE__ */ new Set([element2]), callId);
return { log: log3, success: true, value: callback(injected, element2, taskData2) };
}, { info: resolved.info, callbackText, taskData, callId: progress3.metadata.id, root: resolved.frame === this ? scope : void 0 }));
if (log2)
progress3.log(log2);
if (!success)
return continuePolling;
return value2;
});
return scope ? scope._context.raceAgainstContextDestroyed(promise) : promise;
}
_setContext(world, context2) {
const data = this._contextData.get(world);
data.context = context2;
if (context2)
data.contextPromise.resolve(context2);
else
data.contextPromise = new ManualPromise();
}
contextCreated(world, context2) {
const data = this._contextData.get(world);
if (data.context) {
data.context.contextDestroyed("Execution context was destroyed, most likely because of a navigation");
this._setContext(world, null);
}
this._setContext(world, context2);
}
contextDestroyed(context2) {
if (this._detachedScope.isClosed())
return;
context2.contextDestroyed("Execution context was destroyed, most likely because of a navigation");
for (const [world, data] of this._contextData) {
if (data.context === context2)
this._setContext(world, null);
}
}
_startNetworkIdleTimer() {
assert(!this._networkIdleTimer);
if (this._firedLifecycleEvents.has("networkidle") || this._detachedScope.isClosed())
return;
this._networkIdleTimer = setTimeout(() => {
this._firedNetworkIdleSelf = true;
this._page.mainFrame()._recalculateNetworkIdle();
}, 500);
}
_stopNetworkIdleTimer() {
if (this._networkIdleTimer)
clearTimeout(this._networkIdleTimer);
this._networkIdleTimer = void 0;
this._firedNetworkIdleSelf = false;
}
async extendInjectedScript(source8, arg) {
const context2 = await this.context("main");
const injectedScriptHandle = await context2.injectedScript();
await injectedScriptHandle.evaluate((injectedScript, { source: source9, arg: arg2 }) => {
injectedScript.extend(source9, arg2);
}, { source: source8, arg });
}
async ariaSnapshot(progress2, options2 = {}) {
if (options2.selector && options2.track)
throw new Error("Cannot specify both selector and track options");
if (options2.selector && options2.mode !== "ai") {
const snapshot4 = await this._retryWithProgressIfNotConnected(progress2, options2.selector, { strict: true, performActionPreChecks: true }, async (progress3, handle) => {
return await progress3.race(handle.evaluateInUtility(([injected, element2, opts]) => injected.ariaSnapshot(element2, opts), { mode: "default", depth: options2.depth, boxes: options2.boxes }));
});
return { snapshot: snapshot4 };
}
let targetFrame;
let info;
if (options2.selector) {
const resolved = await progress2.race(this.selectors.resolveInjectedForSelector(options2.selector, { strict: true }));
if (!resolved)
throw new Error(`Selector "${options2.selector}" did not resolve to any element`);
targetFrame = resolved.frame;
info = resolved.info;
} else {
targetFrame = this;
}
const result2 = await ariaSnapshotForFrame(progress2, targetFrame, { ...options2, info });
const snapshot3 = options2.track && result2.incremental ? result2.incremental.join("\n") : result2.full.join("\n");
return { snapshot: snapshot3 };
}
_asLocator(selector) {
return asLocator(this._page.browserContext._browser.sdkLanguage(), selector);
}
};
SignalBarrier = class {
constructor(progress2) {
this._protectCount = 0;
this._promise = new ManualPromise();
this._progress = progress2;
this.retain();
}
waitFor(progress2) {
this.release();
return progress2.race(this._promise);
}
addFrameNavigation(frame) {
if (frame.parentFrame())
return;
this.retain();
const waiter = helper.waitForEvent(this._progress, frame, Frame.Events.InternalNavigation, (e) => {
if (!e.isPublic)
return false;
if (!e.error && this._progress)
this._progress.log(` navigated to "${frame._url}"`);
return true;
});
LongStandingScope.raceMultiple([
frame._page.openScope,
frame._detachedScope
], waiter.promise).catch(() => {
}).finally(() => {
waiter.dispose();
this.release();
});
}
retain() {
++this._protectCount;
}
release() {
--this._protectCount;
if (!this._protectCount)
this._promise.resolve();
}
};
}
});
// packages/playwright-core/src/server/usKeyboardLayout.ts
var keypadLocation, USKeyboardLayout;
var init_usKeyboardLayout = __esm({
"packages/playwright-core/src/server/usKeyboardLayout.ts"() {
"use strict";
keypadLocation = 3;
USKeyboardLayout = {
// Functions row
"Escape": { "keyCode": 27, "key": "Escape" },
"F1": { "keyCode": 112, "key": "F1" },
"F2": { "keyCode": 113, "key": "F2" },
"F3": { "keyCode": 114, "key": "F3" },
"F4": { "keyCode": 115, "key": "F4" },
"F5": { "keyCode": 116, "key": "F5" },
"F6": { "keyCode": 117, "key": "F6" },
"F7": { "keyCode": 118, "key": "F7" },
"F8": { "keyCode": 119, "key": "F8" },
"F9": { "keyCode": 120, "key": "F9" },
"F10": { "keyCode": 121, "key": "F10" },
"F11": { "keyCode": 122, "key": "F11" },
"F12": { "keyCode": 123, "key": "F12" },
// Numbers row
"Backquote": { "keyCode": 192, "shiftKey": "~", "key": "`" },
"Digit1": { "keyCode": 49, "shiftKey": "!", "key": "1" },
"Digit2": { "keyCode": 50, "shiftKey": "@", "key": "2" },
"Digit3": { "keyCode": 51, "shiftKey": "#", "key": "3" },
"Digit4": { "keyCode": 52, "shiftKey": "$", "key": "4" },
"Digit5": { "keyCode": 53, "shiftKey": "%", "key": "5" },
"Digit6": { "keyCode": 54, "shiftKey": "^", "key": "6" },
"Digit7": { "keyCode": 55, "shiftKey": "&", "key": "7" },
"Digit8": { "keyCode": 56, "shiftKey": "*", "key": "8" },
"Digit9": { "keyCode": 57, "shiftKey": "(", "key": "9" },
"Digit0": { "keyCode": 48, "shiftKey": ")", "key": "0" },
"Minus": { "keyCode": 189, "shiftKey": "_", "key": "-" },
"Equal": { "keyCode": 187, "shiftKey": "+", "key": "=" },
"Backslash": { "keyCode": 220, "shiftKey": "|", "key": "\\" },
"Backspace": { "keyCode": 8, "key": "Backspace" },
// First row
"Tab": { "keyCode": 9, "key": "Tab" },
"KeyQ": { "keyCode": 81, "shiftKey": "Q", "key": "q" },
"KeyW": { "keyCode": 87, "shiftKey": "W", "key": "w" },
"KeyE": { "keyCode": 69, "shiftKey": "E", "key": "e" },
"KeyR": { "keyCode": 82, "shiftKey": "R", "key": "r" },
"KeyT": { "keyCode": 84, "shiftKey": "T", "key": "t" },
"KeyY": { "keyCode": 89, "shiftKey": "Y", "key": "y" },
"KeyU": { "keyCode": 85, "shiftKey": "U", "key": "u" },
"KeyI": { "keyCode": 73, "shiftKey": "I", "key": "i" },
"KeyO": { "keyCode": 79, "shiftKey": "O", "key": "o" },
"KeyP": { "keyCode": 80, "shiftKey": "P", "key": "p" },
"BracketLeft": { "keyCode": 219, "shiftKey": "{", "key": "[" },
"BracketRight": { "keyCode": 221, "shiftKey": "}", "key": "]" },
// Second row
"CapsLock": { "keyCode": 20, "key": "CapsLock" },
"KeyA": { "keyCode": 65, "shiftKey": "A", "key": "a" },
"KeyS": { "keyCode": 83, "shiftKey": "S", "key": "s" },
"KeyD": { "keyCode": 68, "shiftKey": "D", "key": "d" },
"KeyF": { "keyCode": 70, "shiftKey": "F", "key": "f" },
"KeyG": { "keyCode": 71, "shiftKey": "G", "key": "g" },
"KeyH": { "keyCode": 72, "shiftKey": "H", "key": "h" },
"KeyJ": { "keyCode": 74, "shiftKey": "J", "key": "j" },
"KeyK": { "keyCode": 75, "shiftKey": "K", "key": "k" },
"KeyL": { "keyCode": 76, "shiftKey": "L", "key": "l" },
"Semicolon": { "keyCode": 186, "shiftKey": ":", "key": ";" },
"Quote": { "keyCode": 222, "shiftKey": '"', "key": "'" },
"Enter": { "keyCode": 13, "key": "Enter", "text": "\r" },
// Third row
"ShiftLeft": { "keyCode": 160, "keyCodeWithoutLocation": 16, "key": "Shift", "location": 1 },
"KeyZ": { "keyCode": 90, "shiftKey": "Z", "key": "z" },
"KeyX": { "keyCode": 88, "shiftKey": "X", "key": "x" },
"KeyC": { "keyCode": 67, "shiftKey": "C", "key": "c" },
"KeyV": { "keyCode": 86, "shiftKey": "V", "key": "v" },
"KeyB": { "keyCode": 66, "shiftKey": "B", "key": "b" },
"KeyN": { "keyCode": 78, "shiftKey": "N", "key": "n" },
"KeyM": { "keyCode": 77, "shiftKey": "M", "key": "m" },
"Comma": { "keyCode": 188, "shiftKey": "<", "key": "," },
"Period": { "keyCode": 190, "shiftKey": ">", "key": "." },
"Slash": { "keyCode": 191, "shiftKey": "?", "key": "/" },
"ShiftRight": { "keyCode": 161, "keyCodeWithoutLocation": 16, "key": "Shift", "location": 2 },
// Last row
"ControlLeft": { "keyCode": 162, "keyCodeWithoutLocation": 17, "key": "Control", "location": 1 },
"MetaLeft": { "keyCode": 91, "key": "Meta", "location": 1 },
"AltLeft": { "keyCode": 164, "keyCodeWithoutLocation": 18, "key": "Alt", "location": 1 },
"Space": { "keyCode": 32, "key": " " },
"AltRight": { "keyCode": 165, "keyCodeWithoutLocation": 18, "key": "Alt", "location": 2 },
"AltGraph": { "keyCode": 225, "key": "AltGraph" },
"MetaRight": { "keyCode": 92, "key": "Meta", "location": 2 },
"ContextMenu": { "keyCode": 93, "key": "ContextMenu" },
"ControlRight": { "keyCode": 163, "keyCodeWithoutLocation": 17, "key": "Control", "location": 2 },
// Center block
"PrintScreen": { "keyCode": 44, "key": "PrintScreen" },
"ScrollLock": { "keyCode": 145, "key": "ScrollLock" },
"Pause": { "keyCode": 19, "key": "Pause" },
"PageUp": { "keyCode": 33, "key": "PageUp" },
"PageDown": { "keyCode": 34, "key": "PageDown" },
"Insert": { "keyCode": 45, "key": "Insert" },
"Delete": { "keyCode": 46, "key": "Delete" },
"Home": { "keyCode": 36, "key": "Home" },
"End": { "keyCode": 35, "key": "End" },
"ArrowLeft": { "keyCode": 37, "key": "ArrowLeft" },
"ArrowUp": { "keyCode": 38, "key": "ArrowUp" },
"ArrowRight": { "keyCode": 39, "key": "ArrowRight" },
"ArrowDown": { "keyCode": 40, "key": "ArrowDown" },
// Media keys
"AudioVolumeMute": { "keyCode": 173, "key": "AudioVolumeMute" },
"AudioVolumeDown": { "keyCode": 174, "key": "AudioVolumeDown" },
"AudioVolumeUp": { "keyCode": 175, "key": "AudioVolumeUp" },
"MediaTrackNext": { "keyCode": 176, "key": "MediaTrackNext" },
"MediaTrackPrevious": { "keyCode": 177, "key": "MediaTrackPrevious" },
"MediaPlayPause": { "keyCode": 179, "key": "MediaPlayPause" },
// Numpad
"NumLock": { "keyCode": 144, "key": "NumLock" },
"NumpadDivide": { "keyCode": 111, "key": "/", "location": 3 },
"NumpadMultiply": { "keyCode": 106, "key": "*", "location": 3 },
"NumpadSubtract": { "keyCode": 109, "key": "-", "location": 3 },
"Numpad7": { "keyCode": 36, "shiftKeyCode": 103, "key": "Home", "shiftKey": "7", "location": 3 },
"Numpad8": { "keyCode": 38, "shiftKeyCode": 104, "key": "ArrowUp", "shiftKey": "8", "location": 3 },
"Numpad9": { "keyCode": 33, "shiftKeyCode": 105, "key": "PageUp", "shiftKey": "9", "location": 3 },
"Numpad4": { "keyCode": 37, "shiftKeyCode": 100, "key": "ArrowLeft", "shiftKey": "4", "location": 3 },
"Numpad5": { "keyCode": 12, "shiftKeyCode": 101, "key": "Clear", "shiftKey": "5", "location": 3 },
"Numpad6": { "keyCode": 39, "shiftKeyCode": 102, "key": "ArrowRight", "shiftKey": "6", "location": 3 },
"NumpadAdd": { "keyCode": 107, "key": "+", "location": 3 },
"Numpad1": { "keyCode": 35, "shiftKeyCode": 97, "key": "End", "shiftKey": "1", "location": 3 },
"Numpad2": { "keyCode": 40, "shiftKeyCode": 98, "key": "ArrowDown", "shiftKey": "2", "location": 3 },
"Numpad3": { "keyCode": 34, "shiftKeyCode": 99, "key": "PageDown", "shiftKey": "3", "location": 3 },
"Numpad0": { "keyCode": 45, "shiftKeyCode": 96, "key": "Insert", "shiftKey": "0", "location": 3 },
"NumpadDecimal": { "keyCode": 46, "shiftKeyCode": 110, "key": "\0", "shiftKey": ".", "location": 3 },
"NumpadEnter": { "keyCode": 13, "key": "Enter", "text": "\r", "location": 3 }
};
}
});
// packages/playwright-core/src/server/input.ts
function resolveSmartModifierString(key) {
if (key === "ControlOrMeta")
return process.platform === "darwin" ? "Meta" : "Control";
return key;
}
function resolveSmartModifier(m) {
return resolveSmartModifierString(m);
}
function buildLayoutClosure(layout) {
const result2 = /* @__PURE__ */ new Map();
for (const code in layout) {
const definition = layout[code];
const description = {
key: definition.key || "",
keyCode: definition.keyCode || 0,
keyCodeWithoutLocation: definition.keyCodeWithoutLocation || definition.keyCode || 0,
code,
text: definition.text || "",
location: definition.location || 0
};
if (definition.key.length === 1)
description.text = description.key;
let shiftedDescription;
if (definition.shiftKey) {
assert(definition.shiftKey.length === 1);
shiftedDescription = { ...description };
shiftedDescription.key = definition.shiftKey;
shiftedDescription.text = definition.shiftKey;
if (definition.shiftKeyCode)
shiftedDescription.keyCode = definition.shiftKeyCode;
}
result2.set(code, { ...description, shifted: shiftedDescription });
if (aliases.has(code)) {
for (const alias of aliases.get(code))
result2.set(alias, description);
}
if (definition.location)
continue;
if (description.key.length === 1)
result2.set(description.key, description);
if (shiftedDescription)
result2.set(shiftedDescription.key, { ...shiftedDescription, shifted: void 0 });
}
return result2;
}
var keypadLocation2, kModifiers, Keyboard, Mouse, aliases, usKeyboardLayout, Touchscreen;
var init_input = __esm({
"packages/playwright-core/src/server/input.ts"() {
"use strict";
init_assert();
init_usKeyboardLayout();
init_dom();
keypadLocation2 = keypadLocation;
kModifiers = ["Alt", "Control", "Meta", "Shift"];
Keyboard = class {
constructor(raw, page) {
this._pressedModifiers = /* @__PURE__ */ new Set();
this._pressedKeys = /* @__PURE__ */ new Set();
this._raw = raw;
this._page = page;
}
async apiDown(progress2, key) {
await progress2.race(this._page.instrumentation.onBeforeInputAction(this._page, progress2.metadata));
await this.down(progress2, key);
}
async down(progress2, key) {
const description = this._keyDescriptionForString(key);
const autoRepeat = this._pressedKeys.has(description.code);
this._pressedKeys.add(description.code);
if (kModifiers.includes(description.key))
this._pressedModifiers.add(description.key);
await this._raw.keydown(progress2, this._pressedModifiers, key, description, autoRepeat);
}
_keyDescriptionForString(str) {
const keyString = resolveSmartModifierString(str);
let description = usKeyboardLayout.get(keyString);
if (!description)
throw new NonRecoverableDOMError(`Unknown key: "${keyString}"`);
const shift = this._pressedModifiers.has("Shift");
description = shift && description.shifted ? description.shifted : description;
if (this._pressedModifiers.size > 1 || !this._pressedModifiers.has("Shift") && this._pressedModifiers.size === 1)
return { ...description, text: "" };
return description;
}
async apiUp(progress2, key) {
await progress2.race(this._page.instrumentation.onBeforeInputAction(this._page, progress2.metadata));
await this.up(progress2, key);
}
async up(progress2, key) {
const description = this._keyDescriptionForString(key);
if (kModifiers.includes(description.key))
this._pressedModifiers.delete(description.key);
this._pressedKeys.delete(description.code);
await this._raw.keyup(progress2, this._pressedModifiers, key, description);
}
async apiInsertText(progress2, text2) {
await progress2.race(this._page.instrumentation.onBeforeInputAction(this._page, progress2.metadata));
await this.insertText(progress2, text2);
}
async insertText(progress2, text2) {
await this._raw.sendText(progress2, text2);
}
async apiType(progress2, text2, options2) {
await progress2.race(this._page.instrumentation.onBeforeInputAction(this._page, progress2.metadata));
await this.type(progress2, text2, options2);
}
async type(progress2, text2, options2) {
const delay = options2 && options2.delay || void 0;
for (const char of text2) {
if (usKeyboardLayout.has(char)) {
await this.press(progress2, char, { delay });
} else {
if (delay)
await progress2.wait(delay);
await this.insertText(progress2, char);
}
}
}
async apiPress(progress2, key, options2 = {}) {
await progress2.race(this._page.instrumentation.onBeforeInputAction(this._page, progress2.metadata));
await this.press(progress2, key, options2);
}
async press(progress2, key, options2 = {}) {
function split(keyString) {
const keys = [];
let building = "";
for (const char of keyString) {
if (char === "+" && building) {
keys.push(building);
building = "";
} else {
building += char;
}
}
keys.push(building);
return keys;
}
const tokens = split(key);
key = tokens[tokens.length - 1];
for (let i = 0; i < tokens.length - 1; ++i)
await this.down(progress2, tokens[i]);
await this.down(progress2, key);
if (options2.delay)
await progress2.wait(options2.delay);
await this.up(progress2, key);
for (let i = tokens.length - 2; i >= 0; --i)
await this.up(progress2, tokens[i]);
}
async ensureModifiers(progress2, mm) {
const modifiers = mm.map(resolveSmartModifier);
for (const modifier of modifiers) {
if (!kModifiers.includes(modifier))
throw new Error("Unknown modifier " + modifier);
}
const restore = Array.from(this._pressedModifiers);
for (const key of kModifiers) {
const needDown = modifiers.includes(key);
const isDown = this._pressedModifiers.has(key);
if (needDown && !isDown)
await this.down(progress2, key);
else if (!needDown && isDown)
await this.up(progress2, key);
}
return restore;
}
_modifiers() {
return this._pressedModifiers;
}
};
Mouse = class {
constructor(raw, page) {
this._x = 0;
this._y = 0;
this._lastButton = "none";
this._buttons = /* @__PURE__ */ new Set();
this._raw = raw;
this._page = page;
this._keyboard = this._page.keyboard;
}
_currentPoint() {
return { x: this._x, y: this._y };
}
async apiMove(progress2, x, y, options2 = {}) {
progress2.metadata.point = { x, y };
await progress2.race(this._page.instrumentation.onBeforeInputAction(this._page, progress2.metadata));
await this.move(progress2, x, y, options2);
}
async move(progress2, x, y, options2 = {}) {
const { steps = 1 } = options2;
const fromX = this._x;
const fromY = this._y;
this._x = x;
this._y = y;
for (let i = 1; i <= steps; i++) {
const middleX = fromX + (x - fromX) * (i / steps);
const middleY = fromY + (y - fromY) * (i / steps);
await this._raw.move(progress2, middleX, middleY, this._lastButton, this._buttons, this._keyboard._modifiers(), !!options2.forClick);
}
}
async apiDown(progress2, options2 = {}) {
progress2.metadata.point = this._currentPoint();
await progress2.race(this._page.instrumentation.onBeforeInputAction(this._page, progress2.metadata));
await this.down(progress2, options2);
}
async down(progress2, options2 = {}) {
const { button = "left", clickCount = 1 } = options2;
this._lastButton = button;
this._buttons.add(button);
await this._raw.down(progress2, this._x, this._y, this._lastButton, this._buttons, this._keyboard._modifiers(), clickCount);
}
async apiUp(progress2, options2 = {}) {
progress2.metadata.point = this._currentPoint();
await progress2.race(this._page.instrumentation.onBeforeInputAction(this._page, progress2.metadata));
await this.up(progress2, options2);
}
async up(progress2, options2 = {}) {
const { button = "left", clickCount = 1 } = options2;
this._lastButton = "none";
this._buttons.delete(button);
await this._raw.up(progress2, this._x, this._y, button, this._buttons, this._keyboard._modifiers(), clickCount);
}
async apiClick(progress2, x, y, options2 = {}) {
progress2.metadata.point = { x, y };
await progress2.race(this._page.instrumentation.onBeforeInputAction(this._page, progress2.metadata));
await this.click(progress2, x, y, options2);
}
async click(progress2, x, y, options2 = {}) {
const { delay = null, clickCount = 1, steps } = options2;
if (delay) {
await this.move(progress2, x, y, { forClick: true, steps });
for (let cc = 1; cc <= clickCount; ++cc) {
await this.down(progress2, { ...options2, clickCount: cc });
await progress2.wait(delay);
await this.up(progress2, { ...options2, clickCount: cc });
if (cc < clickCount)
await progress2.wait(delay);
}
} else {
progress2.setAllowConcurrentOrNestedRaces(true);
const promises = [];
const movePromise = this.move(progress2, x, y, { forClick: true, steps });
if (steps !== void 0 && steps > 1)
await movePromise;
else
promises.push(movePromise);
for (let cc = 1; cc <= clickCount; ++cc) {
promises.push(this.down(progress2, { ...options2, clickCount: cc }));
promises.push(this.up(progress2, { ...options2, clickCount: cc }));
}
await Promise.all(promises);
progress2.setAllowConcurrentOrNestedRaces(false);
}
}
async apiWheel(progress2, deltaX, deltaY) {
await progress2.race(this._page.instrumentation.onBeforeInputAction(this._page, progress2.metadata));
await this._raw.wheel(progress2, this._x, this._y, this._buttons, this._keyboard._modifiers(), deltaX, deltaY);
}
};
aliases = /* @__PURE__ */ new Map([
["ShiftLeft", ["Shift"]],
["ControlLeft", ["Control"]],
["AltLeft", ["Alt"]],
["MetaLeft", ["Meta"]],
["Enter", ["\n", "\r"]]
]);
usKeyboardLayout = buildLayoutClosure(USKeyboardLayout);
Touchscreen = class {
constructor(raw, page) {
this._raw = raw;
this._page = page;
}
async apiTap(progress2, x, y) {
if (!this._page.browserContext._options.hasTouch)
throw new Error("hasTouch must be enabled on the browser context before using the touchscreen.");
await progress2.race(this._page.instrumentation.onBeforeInputAction(this._page, progress2.metadata));
await this.tap(progress2, x, y);
}
async tap(progress2, x, y) {
await this._raw.tap(progress2, x, y, this._page.keyboard._modifiers());
}
};
}
});
// packages/playwright-core/src/server/screenshotter.ts
function inPagePrepareForScreenshots(screenshotStyle, hideCaret, disableAnimations, syncAnimations) {
if (syncAnimations) {
const style = document.createElement("style");
style.textContent = "body {}";
document.head.appendChild(style);
document.documentElement.getBoundingClientRect();
style.remove();
}
if (!screenshotStyle && !hideCaret && !disableAnimations)
return;
const collectRoots = (root, roots2 = []) => {
roots2.push(root);
const walker = document.createTreeWalker(root, NodeFilter.SHOW_ELEMENT);
do {
const node = walker.currentNode;
const shadowRoot = node instanceof Element ? node.shadowRoot : null;
if (shadowRoot)
collectRoots(shadowRoot, roots2);
} while (walker.nextNode());
return roots2;
};
const roots = collectRoots(document);
const cleanupCallbacks = [];
if (screenshotStyle) {
for (const root of roots) {
const styleTag = document.createElement("style");
styleTag.textContent = screenshotStyle;
if (root === document)
document.documentElement.append(styleTag);
else
root.append(styleTag);
cleanupCallbacks.push(() => {
styleTag.remove();
});
}
}
if (hideCaret) {
const elements = /* @__PURE__ */ new Map();
for (const root of roots) {
root.querySelectorAll("input,textarea,[contenteditable]").forEach((element2) => {
elements.set(element2, {
value: element2.style.getPropertyValue("caret-color"),
priority: element2.style.getPropertyPriority("caret-color")
});
element2.style.setProperty("caret-color", "transparent", "important");
});
}
cleanupCallbacks.push(() => {
for (const [element2, value2] of elements)
element2.style.setProperty("caret-color", value2.value, value2.priority);
});
}
if (disableAnimations) {
const infiniteAnimationsToResume = /* @__PURE__ */ new Set();
const handleAnimations = (root) => {
for (const animation of root.getAnimations()) {
if (!animation.effect || animation.playbackRate === 0 || infiniteAnimationsToResume.has(animation))
continue;
const endTime = animation.effect.getComputedTiming().endTime;
if (Number.isFinite(endTime)) {
try {
animation.finish();
} catch (e) {
}
} else {
try {
animation.cancel();
infiniteAnimationsToResume.add(animation);
} catch (e) {
}
}
}
};
for (const root of roots) {
const handleRootAnimations = handleAnimations.bind(null, root);
handleRootAnimations();
root.addEventListener("transitionrun", handleRootAnimations);
root.addEventListener("animationstart", handleRootAnimations);
cleanupCallbacks.push(() => {
root.removeEventListener("transitionrun", handleRootAnimations);
root.removeEventListener("animationstart", handleRootAnimations);
});
}
cleanupCallbacks.push(() => {
for (const animation of infiniteAnimationsToResume) {
try {
animation.play();
} catch (e) {
}
}
});
}
window.__pwCleanupScreenshot = () => {
for (const cleanupCallback of cleanupCallbacks)
cleanupCallback();
delete window.__pwCleanupScreenshot;
};
}
function trimClipToSize(clip, size) {
const p1 = {
x: Math.max(0, Math.min(clip.x, size.width)),
y: Math.max(0, Math.min(clip.y, size.height))
};
const p2 = {
x: Math.max(0, Math.min(clip.x + clip.width, size.width)),
y: Math.max(0, Math.min(clip.y + clip.height, size.height))
};
const result2 = { x: p1.x, y: p1.y, width: p2.x - p1.x, height: p2.y - p1.y };
assert(result2.width && result2.height, "Clipped area is either empty or outside the resulting image");
return result2;
}
function validateScreenshotOptions(options2) {
let format2 = null;
if (options2.type) {
assert(options2.type === "png" || options2.type === "jpeg", "Unknown options.type value: " + options2.type);
format2 = options2.type;
}
if (!format2)
format2 = "png";
if (options2.quality !== void 0) {
assert(format2 === "jpeg", "options.quality is unsupported for the " + format2 + " screenshots");
assert(typeof options2.quality === "number", "Expected options.quality to be a number but found " + typeof options2.quality);
assert(Number.isInteger(options2.quality), "Expected options.quality to be an integer");
assert(options2.quality >= 0 && options2.quality <= 100, "Expected options.quality to be between 0 and 100 (inclusive), got " + options2.quality);
}
if (options2.clip) {
assert(typeof options2.clip.x === "number", "Expected options.clip.x to be a number but found " + typeof options2.clip.x);
assert(typeof options2.clip.y === "number", "Expected options.clip.y to be a number but found " + typeof options2.clip.y);
assert(typeof options2.clip.width === "number", "Expected options.clip.width to be a number but found " + typeof options2.clip.width);
assert(typeof options2.clip.height === "number", "Expected options.clip.height to be a number but found " + typeof options2.clip.height);
assert(options2.clip.width !== 0, "Expected options.clip.width not to be 0.");
assert(options2.clip.height !== 0, "Expected options.clip.height not to be 0.");
}
return format2;
}
var Screenshotter, TaskQueue;
var init_screenshotter = __esm({
"packages/playwright-core/src/server/screenshotter.ts"() {
"use strict";
init_multimap();
init_assert();
init_helper();
Screenshotter = class {
constructor(page) {
this._queue = new TaskQueue();
this._page = page;
this._queue = new TaskQueue();
}
async _originalViewportSize(progress2) {
let viewportSize = this._page.emulatedSize()?.viewport;
if (!viewportSize)
viewportSize = await this._page.mainFrame().waitForFunctionValueInUtility(progress2, () => ({ width: window.innerWidth, height: window.innerHeight }));
return viewportSize;
}
async _fullPageSize(progress2) {
const fullPageSize = await this._page.mainFrame().waitForFunctionValueInUtility(progress2, () => {
if (!document.body || !document.documentElement)
return null;
return {
width: Math.max(
document.body.scrollWidth,
document.documentElement.scrollWidth,
document.body.offsetWidth,
document.documentElement.offsetWidth,
document.body.clientWidth,
document.documentElement.clientWidth
),
height: Math.max(
document.body.scrollHeight,
document.documentElement.scrollHeight,
document.body.offsetHeight,
document.documentElement.offsetHeight,
document.body.clientHeight,
document.documentElement.clientHeight
)
};
});
return fullPageSize;
}
async screenshotPage(progress2, options2) {
const format2 = validateScreenshotOptions(options2);
return this._queue._postTask(async () => {
progress2.log("taking page screenshot");
const viewportSize = await this._originalViewportSize(progress2);
await this._preparePageForScreenshot(progress2, this._page.mainFrame(), options2.style, options2.caret !== "initial", options2.animations === "disabled");
try {
if (options2.fullPage) {
const fullPageSize = await this._fullPageSize(progress2);
let documentRect = { x: 0, y: 0, width: fullPageSize.width, height: fullPageSize.height };
const fitsViewport = fullPageSize.width <= viewportSize.width && fullPageSize.height <= viewportSize.height;
if (options2.clip)
documentRect = trimClipToSize(options2.clip, documentRect);
return await this._screenshot(progress2, format2, documentRect, void 0, fitsViewport, options2);
}
const viewportRect = options2.clip ? trimClipToSize(options2.clip, viewportSize) : { x: 0, y: 0, ...viewportSize };
return await this._screenshot(progress2, format2, void 0, viewportRect, true, options2);
} finally {
await this._restorePageAfterScreenshot();
}
});
}
async screenshotElement(progress2, handle, options2) {
const format2 = validateScreenshotOptions(options2);
return this._queue._postTask(async () => {
progress2.log("taking element screenshot");
const viewportSize = await this._originalViewportSize(progress2);
await this._preparePageForScreenshot(progress2, handle._frame, options2.style, options2.caret !== "initial", options2.animations === "disabled");
try {
await handle._waitAndScrollIntoViewIfNeeded(
progress2,
true
/* waitForVisible */
);
const boundingBox = await handle.boundingBox(progress2);
assert(boundingBox, "Node is either not visible or not an HTMLElement");
assert(boundingBox.width !== 0, "Node has 0 width.");
assert(boundingBox.height !== 0, "Node has 0 height.");
const fitsViewport = boundingBox.width <= viewportSize.width && boundingBox.height <= viewportSize.height;
const scrollOffset = await this._page.mainFrame().waitForFunctionValueInUtility(progress2, () => ({ x: window.scrollX, y: window.scrollY }));
const documentRect = { ...boundingBox };
documentRect.x += scrollOffset.x;
documentRect.y += scrollOffset.y;
return await this._screenshot(progress2, format2, helper.enclosingIntRect(documentRect), void 0, fitsViewport, options2);
} finally {
await this._restorePageAfterScreenshot();
}
});
}
async _preparePageForScreenshot(progress2, frame, screenshotStyle, hideCaret, disableAnimations) {
if (disableAnimations)
progress2.log(" disabled all CSS animations");
const syncAnimations = this._page.delegate.shouldToggleStyleSheetToSyncAnimations();
await progress2.race(this._page.safeNonStallingEvaluateInAllFrames("(" + inPagePrepareForScreenshots.toString() + `)(${JSON.stringify(screenshotStyle)}, ${hideCaret}, ${disableAnimations}, ${syncAnimations})`, "utility"));
try {
if (!process.env.PW_TEST_SCREENSHOT_NO_FONTS_READY) {
progress2.log("waiting for fonts to load...");
await progress2.race(frame.nonStallingEvaluateInExistingContext("document.fonts.ready", "utility").catch(() => {
}));
progress2.log("fonts loaded");
}
} catch (error) {
await progress2.race(this._restorePageAfterScreenshot());
throw error;
}
}
async _restorePageAfterScreenshot() {
await this._page.safeNonStallingEvaluateInAllFrames("window.__pwCleanupScreenshot && window.__pwCleanupScreenshot()", "utility");
}
async _maskElements(progress2, options2) {
if (!options2.mask || !options2.mask.length)
return () => Promise.resolve();
const framesToParsedSelectors = new MultiMap();
await progress2.race(Promise.all((options2.mask || []).map(async ({ frame, selector }) => {
const pair = await frame.selectors.resolveFrameForSelector(selector);
if (pair)
framesToParsedSelectors.set(pair.frame, pair.info.parsed);
})));
const frames = [...framesToParsedSelectors.keys()];
const cleanup = async () => {
await Promise.all(frames.map((frame) => frame.hideHighlight()));
};
try {
const promises = frames.map((frame) => frame.maskSelectors(framesToParsedSelectors.get(frame), options2.maskColor || "#F0F"));
await progress2.race(Promise.all(promises));
return cleanup;
} catch (error) {
cleanup().catch(() => {
});
throw error;
}
}
async _screenshot(progress2, format2, documentRect, viewportRect, fitsViewport, options2) {
if (options2.__testHookBeforeScreenshot)
await progress2.race(options2.__testHookBeforeScreenshot());
const shouldSetDefaultBackground = options2.omitBackground && format2 === "png";
if (shouldSetDefaultBackground)
await progress2.race(this._page.delegate.setBackgroundColor({ r: 0, g: 0, b: 0, a: 0 }));
const cleanupHighlight = await this._maskElements(progress2, options2);
try {
const quality = format2 === "jpeg" ? options2.quality ?? 80 : void 0;
const buffer = await this._page.delegate.takeScreenshot(progress2, format2, documentRect, viewportRect, quality, fitsViewport, options2.scale || "device");
await progress2.race(cleanupHighlight());
if (shouldSetDefaultBackground)
await progress2.race(this._page.delegate.setBackgroundColor());
if (options2.__testHookAfterScreenshot)
await progress2.race(options2.__testHookAfterScreenshot());
return buffer;
} catch (error) {
cleanupHighlight().catch(() => {
});
if (shouldSetDefaultBackground)
this._page.delegate.setBackgroundColor().catch(() => {
});
throw error;
}
}
};
TaskQueue = class {
constructor() {
this._chain = Promise.resolve();
}
_postTask(task) {
const result2 = this._chain.then(task);
this._chain = result2.catch(() => {
});
return result2;
}
};
}
});
// packages/playwright-core/src/generated/bindingsControllerSource.ts
var source4;
var init_bindingsControllerSource = __esm({
"packages/playwright-core/src/generated/bindingsControllerSource.ts"() {
"use strict";
source4 = '\nvar __commonJS = obj => {\n let required = false;\n let result;\n return function __require() {\n if (!required) {\n required = true;\n let fn;\n for (const name in obj) { fn = obj[name]; break; }\n const module = { exports: {} };\n fn(module.exports, module);\n result = module.exports;\n }\n return result;\n }\n};\nvar __export = (target, all) => {for (var name in all) target[name] = all[name];};\nvar __toESM = mod => ({ ...mod, \'default\': mod });\nvar __toCommonJS = mod => ({ ...mod, __esModule: true });\n\n\n// packages/injected/src/bindingsController.ts\nvar bindingsController_exports = {};\n__export(bindingsController_exports, {\n BindingsController: () => BindingsController\n});\nmodule.exports = __toCommonJS(bindingsController_exports);\n\n// packages/isomorphic/utilityScriptSerializers.ts\nfunction isRegExp(obj) {\n try {\n return obj instanceof RegExp || Object.prototype.toString.call(obj) === "[object RegExp]";\n } catch (error) {\n return false;\n }\n}\nfunction isDate(obj) {\n try {\n return obj instanceof Date || Object.prototype.toString.call(obj) === "[object Date]";\n } catch (error) {\n return false;\n }\n}\nfunction isURL(obj) {\n try {\n return obj instanceof URL || Object.prototype.toString.call(obj) === "[object URL]";\n } catch (error) {\n return false;\n }\n}\nfunction isError(obj) {\n var _a;\n try {\n return obj instanceof Error || obj && ((_a = Object.getPrototypeOf(obj)) == null ? void 0 : _a.name) === "Error";\n } catch (error) {\n return false;\n }\n}\nfunction isTypedArray(obj, constructor) {\n try {\n return obj instanceof constructor || Object.prototype.toString.call(obj) === `[object ${constructor.name}]`;\n } catch (error) {\n return false;\n }\n}\nfunction isArrayBuffer(obj) {\n try {\n return obj instanceof ArrayBuffer || Object.prototype.toString.call(obj) === "[object ArrayBuffer]";\n } catch (error) {\n return false;\n }\n}\nvar typedArrayConstructors = {\n i8: Int8Array,\n ui8: Uint8Array,\n ui8c: Uint8ClampedArray,\n i16: Int16Array,\n ui16: Uint16Array,\n i32: Int32Array,\n ui32: Uint32Array,\n // TODO: add Float16Array once it\'s in baseline\n f32: Float32Array,\n f64: Float64Array,\n bi64: BigInt64Array,\n bui64: BigUint64Array\n};\nfunction typedArrayToBase64(array) {\n if ("toBase64" in array)\n return array.toBase64();\n const binary = Array.from(new Uint8Array(array.buffer, array.byteOffset, array.byteLength)).map((b) => String.fromCharCode(b)).join("");\n return btoa(binary);\n}\nfunction serializeAsCallArgument(value, handleSerializer) {\n return serialize(value, handleSerializer, { visited: /* @__PURE__ */ new Map(), lastId: 0 });\n}\nfunction serialize(value, handleSerializer, visitorInfo) {\n if (value && typeof value === "object") {\n if (typeof globalThis.Window === "function" && value instanceof globalThis.Window)\n return "ref: <Window>";\n if (typeof globalThis.Document === "function" && value instanceof globalThis.Document)\n return "ref: <Document>";\n if (typeof globalThis.Node === "function" && value instanceof globalThis.Node)\n return "ref: <Node>";\n }\n return innerSerialize(value, handleSerializer, visitorInfo);\n}\nfunction innerSerialize(value, handleSerializer, visitorInfo) {\n var _a;\n const result = handleSerializer(value);\n if ("fallThrough" in result)\n value = result.fallThrough;\n else\n return result;\n if (typeof value === "symbol")\n return { v: "undefined" };\n if (Object.is(value, void 0))\n return { v: "undefined" };\n if (Object.is(value, null))\n return { v: "null" };\n if (Object.is(value, NaN))\n return { v: "NaN" };\n if (Object.is(value, Infinity))\n return { v: "Infinity" };\n if (Object.is(value, -Infinity))\n return { v: "-Infinity" };\n if (Object.is(value, -0))\n return { v: "-0" };\n if (typeof value === "boolean")\n return value;\n if (typeof value === "number")\n return value;\n if (typeof value === "string")\n return value;\n if (typeof value === "bigint")\n return { bi: value.toString() };\n if (isError(value)) {\n let stack;\n if ((_a = value.stack) == null ? void 0 : _a.startsWith(value.name + ": " + value.message)) {\n stack = value.stack;\n } else {\n stack = `${value.name}: ${value.message}\n${value.stack}`;\n }\n return { e: { n: value.name, m: value.message, s: stack } };\n }\n if (isDate(value))\n return { d: value.toJSON() };\n if (isURL(value))\n return { u: value.toJSON() };\n if (isRegExp(value))\n return { r: { p: value.source, f: value.flags } };\n for (const [k, ctor] of Object.entries(typedArrayConstructors)) {\n if (isTypedArray(value, ctor))\n return { ta: { b: typedArrayToBase64(value), k } };\n }\n if (isArrayBuffer(value))\n return { ab: { b: typedArrayToBase64(new Uint8Array(value)) } };\n const id = visitorInfo.visited.get(value);\n if (id)\n return { ref: id };\n if (Array.isArray(value)) {\n const a = [];\n const id2 = ++visitorInfo.lastId;\n visitorInfo.visited.set(value, id2);\n for (let i = 0; i < value.length; ++i)\n a.push(serialize(value[i], handleSerializer, visitorInfo));\n return { a, id: id2 };\n }\n if (typeof value === "object") {\n const o = [];\n const id2 = ++visitorInfo.lastId;\n visitorInfo.visited.set(value, id2);\n for (const name of Object.keys(value)) {\n let item;\n try {\n item = value[name];\n } catch (e) {\n continue;\n }\n if (name === "toJSON" && typeof item === "function")\n o.push({ k: name, v: { o: [], id: 0 } });\n else\n o.push({ k: name, v: serialize(item, handleSerializer, visitorInfo) });\n }\n let jsonWrapper;\n try {\n if (o.length === 0 && value.toJSON && typeof value.toJSON === "function")\n jsonWrapper = { value: value.toJSON() };\n } catch (e) {\n }\n if (jsonWrapper)\n return innerSerialize(jsonWrapper.value, handleSerializer, visitorInfo);\n return { o, id: id2 };\n }\n}\n\n// packages/injected/src/bindingsController.ts\nvar BindingsController = class {\n constructor(global, globalBindingName) {\n this._bindings = /* @__PURE__ */ new Map();\n this._global = global;\n this._globalBindingName = globalBindingName;\n }\n addBinding(bindingName) {\n const data = {\n callbacks: /* @__PURE__ */ new Map(),\n lastSeq: 0,\n removed: false\n };\n this._bindings.set(bindingName, data);\n this._global[bindingName] = (...args) => {\n if (data.removed)\n throw new Error(`binding "${bindingName}" has been removed`);\n const seq = ++data.lastSeq;\n const promise = new Promise((resolve, reject) => data.callbacks.set(seq, { resolve, reject }));\n const serializedArgs = [];\n for (let i = 0; i < args.length; i++) {\n serializedArgs[i] = serializeAsCallArgument(args[i], (v) => {\n return { fallThrough: v };\n });\n }\n const payload = { name: bindingName, seq, serializedArgs };\n this._global[this._globalBindingName](JSON.stringify(payload));\n return promise;\n };\n }\n removeBinding(bindingName) {\n const data = this._bindings.get(bindingName);\n if (data)\n data.removed = true;\n this._bindings.delete(bindingName);\n delete this._global[bindingName];\n }\n deliverBindingResult(arg) {\n const callbacks = this._bindings.get(arg.name).callbacks;\n if ("error" in arg)\n callbacks.get(arg.seq).reject(arg.error);\n else\n callbacks.get(arg.seq).resolve(arg.result);\n callbacks.delete(arg.seq);\n }\n};\n';
}
});
// packages/playwright-core/src/server/overlay.ts
var Overlay;
var init_overlay = __esm({
"packages/playwright-core/src/server/overlay.ts"() {
"use strict";
init_crypto();
init_debugLogger();
init_stringUtils();
init_page();
Overlay = class {
constructor(page) {
this._overlays = /* @__PURE__ */ new Map();
this._page = page;
this._page.on(Page.Events.InternalFrameNavigatedToNewDocument, (frame) => {
if (frame.parentFrame())
return;
for (const [id, html] of this._overlays)
this._doAdd(id, html).catch((e) => debugLogger.log("error", e));
});
}
dispose() {
}
async show(html, duration) {
const id = createGuid();
this._overlays.set(id, html);
await this._doAdd(id, html).catch((e) => debugLogger.log("error", e));
if (duration) {
await new Promise((f) => setTimeout(f, duration));
await this.remove(id);
}
return id;
}
async _doAdd(id, html) {
const utility = await this._page.mainFrame().utilityContext();
await utility.evaluate(({ injected, html: html2, id: id2 }) => {
return injected.addUserOverlay(id2, html2);
}, { injected: await utility.injectedScript(), html, id });
}
async remove(id) {
this._overlays.delete(id);
const utility = await this._page.mainFrame().utilityContext();
await utility.evaluate(({ injected, id: id2 }) => {
injected.removeUserOverlay(id2);
}, { injected: await utility.injectedScript(), id }).catch((e) => debugLogger.log("error", e));
}
async chapter(options2) {
const fadeDuration = 300;
const descriptionHtml = options2.description ? `<div id="description">${escapeHTML(options2.description)}</div>` : "";
const styleSheet = `
@keyframes pw-chapter-fade-in {
from { opacity: 0; }
to { opacity: 1; }
}
@keyframes pw-chapter-fade-out {
from { opacity: 1; }
to { opacity: 0; }
}
#background {
position: absolute;
inset: 0;
display: flex;
align-items: center;
justify-content: center;
backdrop-filter: blur(2px);
animation: pw-chapter-fade-in ${fadeDuration}ms ease-out forwards;
}
#background.fade-out {
animation: pw-chapter-fade-out ${fadeDuration}ms ease-in forwards;
}
#content {
background: rgba(0, 0, 0, 0.7);
border: 1px solid rgba(255, 255, 255, 0.15);
border-radius: 16px;
padding: 40px 56px;
max-width: 560px;
box-shadow: 0 8px 32px rgba(0, 0, 0, 0.4);
}
#title {
color: white;
font-family: system-ui, -apple-system, sans-serif;
font-size: 28px;
font-weight: 600;
line-height: 1.3;
text-align: center;
letter-spacing: -0.01em;
}
#description {
color: rgba(255, 255, 255, 0.7);
font-family: system-ui, -apple-system, sans-serif;
font-size: 15px;
line-height: 1.5;
margin-top: 12px;
text-align: center;
}
`;
const duration = options2.duration ?? 2e3;
const html = `<style>${styleSheet}</style><div id="background"><div id="content"><div id="title">${escapeHTML(options2.title)}</div>${descriptionHtml}</div></div>`;
const id = await this.show(html);
await new Promise((f) => setTimeout(f, duration));
const utility = await this._page.mainFrame().utilityContext();
await utility.evaluate(({ injected, id: id2, fadeDuration: fadeDuration2 }) => {
const overlay = injected.getUserOverlay(id2);
const bg = overlay?.querySelector("#background");
if (bg)
bg.classList.add("fade-out");
return new Promise((f) => injected.utils.builtins.setTimeout(f, fadeDuration2));
}, { injected: await utility.injectedScript(), id, fadeDuration }).catch((e) => debugLogger.log("error", e));
await this.remove(id);
}
async setVisible(visible) {
if (!this._overlays.size)
return;
const utility = await this._page.mainFrame().utilityContext();
await utility.evaluate(({ injected, visible: visible2 }) => {
injected.setUserOverlaysVisible(visible2);
}, { injected: await utility.injectedScript(), visible }).catch((e) => debugLogger.log("error", e));
}
};
}
});
// packages/playwright-core/src/server/screencast.ts
var Screencast;
var init_screencast = __esm({
"packages/playwright-core/src/server/screencast.ts"() {
"use strict";
init_manualPromise();
init_protocolFormatter();
init_debugLogger();
Screencast = class {
constructor(page) {
this._clients = /* @__PURE__ */ new Map();
this.page = page;
this.page.instrumentation.addListener(this, page.browserContext);
}
async handlePageOrContextClose() {
const clients = [...this._clients.keys()];
this._clients.clear();
for (const client of clients) {
if (client.gracefulClose)
await client.gracefulClose();
}
}
dispose() {
for (const client of this._clients.keys())
client.dispose();
this._clients.clear();
this.page.instrumentation.removeListener(this);
}
showActions(options2) {
this._actions = options2;
}
hideActions() {
this._actions = void 0;
}
addClient(client) {
const isFirst = this._clients.size === 0;
this._clients.set(client, new ManualPromise());
if (isFirst) {
this._startScreencast(client.size, client.quality);
} else if (this._lastFrame) {
const frame = this._lastFrame;
setTimeout(() => {
if (this._clients.has(client))
void client.onFrame(frame);
}, 0);
}
return { size: this._size };
}
removeClient(client) {
const disconnected = this._clients.get(client);
if (!disconnected)
return;
this._clients.delete(client);
disconnected.resolve();
if (!this._clients.size)
this._stopScreencast();
}
_startScreencast(size, quality) {
this._size = size;
if (!this._size) {
const viewport = this.page.browserContext._options.viewport || { width: 800, height: 600 };
const scale = Math.min(1, 800 / Math.max(viewport.width, viewport.height));
this._size = {
width: Math.floor(viewport.width * scale),
height: Math.floor(viewport.height * scale)
};
}
this._size = {
width: this._size.width & ~1,
height: this._size.height & ~1
};
this.page.delegate.startScreencast({
width: this._size.width,
height: this._size.height,
quality: quality ?? 90
});
}
_stopScreencast() {
this._lastFrame = void 0;
this.page.delegate.stopScreencast();
}
onScreencastFrame(frame, ack) {
this._lastFrame = frame;
const asyncResults = [];
for (const [client, disconnected] of this._clients) {
const result2 = client.onFrame(frame);
if (!result2)
continue;
asyncResults.push(Promise.race([result2.catch(() => {
}), disconnected]));
}
if (ack) {
if (!asyncResults.length)
ack();
else
Promise.race(asyncResults).then(ack);
}
}
async onBeforeCall(sdkObject, metadata, parentId) {
if (!this._actions)
return;
metadata.annotate = true;
}
async onBeforeInputAction(sdkObject, metadata) {
if (!this._actions)
return;
const page = sdkObject.attribution.page;
if (!page)
return;
const actionTitle2 = renderTitleForCall(metadata);
const utility = await page.mainFrame().utilityContext();
await utility.evaluate(async (options2) => {
const { injected, duration } = options2;
injected.setScreencastAnnotation(options2);
await new Promise((f) => injected.utils.builtins.setTimeout(f, duration));
injected.setScreencastAnnotation(null);
}, {
injected: await utility.injectedScript(),
duration: this._actions?.duration ?? 500,
point: metadata.point,
box: metadata.box,
actionTitle: actionTitle2,
position: this._actions?.position,
fontSize: this._actions?.fontSize
}).catch((e) => debugLogger.log("error", e));
}
};
}
});
// packages/playwright-core/src/server/page.ts
async function ariaSnapshotForFrame(progress2, frame, options2 = {}) {
const snapshot3 = await frame.retryWithProgressAndTimeouts(progress2, [1e3, 2e3, 4e3, 8e3], async (progress3, continuePolling) => {
try {
const context2 = await progress3.race(frame.utilityContext());
const injectedScript = await progress3.race(context2.injectedScript());
const snapshotOrRetry = await progress3.race(injectedScript.evaluate((injected, options3) => {
if (options3.info) {
const element2 = injected.querySelector(options3.info.parsed, injected.document, options3.info.strict);
if (!element2)
return false;
return injected.incrementalAriaSnapshot(element2, options3);
}
const node = injected.document.body;
if (!node)
return true;
return injected.incrementalAriaSnapshot(node, options3);
}, {
mode: options2.mode ?? "default",
refPrefix: frame.seq ? "f" + frame.seq : "",
track: options2.track,
doNotRenderActive: options2.doNotRenderActive,
info: options2.info,
depth: options2.depth,
boxes: options2.boxes
}));
if (snapshotOrRetry === true)
return continuePolling;
if (snapshotOrRetry === false)
throw new NonRecoverableDOMError(`Selector "${stringifySelector(options2.info.parsed)}" does not match any element`);
return snapshotOrRetry;
} catch (e) {
if (frame.isNonRetriableError(e))
throw e;
return continuePolling;
}
});
const renderedIframeRefs = snapshot3.iframeRefs.filter((ref) => ref in snapshot3.iframeDepths);
progress2.setAllowConcurrentOrNestedRaces(true);
const childSnapshotPromises = renderedIframeRefs.map((ref) => {
const iframeDepth = snapshot3.iframeDepths[ref];
const childDepth = options2.depth ? options2.depth - iframeDepth - 1 : void 0;
return ariaSnapshotFrameRef(progress2, frame, ref, { ...options2, depth: childDepth });
});
const childSnapshots = await Promise.all(childSnapshotPromises);
progress2.setAllowConcurrentOrNestedRaces(false);
const full = [];
let incremental;
if (snapshot3.incremental !== void 0) {
incremental = snapshot3.incremental.split("\n");
for (let i = 0; i < renderedIframeRefs.length; i++) {
const childSnapshot = childSnapshots[i];
if (childSnapshot.incremental)
incremental.push(...childSnapshot.incremental);
else if (childSnapshot.full.length)
incremental.push("- <changed> iframe [ref=" + renderedIframeRefs[i] + "]:", ...childSnapshot.full.map((l) => " " + l));
}
}
for (const line of snapshot3.full.split("\n")) {
const match = line.match(/^(\s*)- iframe (?:\[active\] )?\[ref=([^\]]*)\]/);
if (!match) {
full.push(line);
continue;
}
const leadingSpace = match[1];
const ref = match[2];
const childSnapshot = childSnapshots[renderedIframeRefs.indexOf(ref)] ?? { full: [] };
full.push(childSnapshot.full.length ? line + ":" : line);
full.push(...childSnapshot.full.map((l) => leadingSpace + " " + l));
}
return { full, incremental };
}
async function ariaSnapshotFrameRef(progress2, parentFrame, frameRef, options2) {
const frameSelector = `aria-ref=${frameRef} >> internal:control=enter-frame`;
const frameBodySelector = `${frameSelector} >> body`;
const child = await progress2.race(parentFrame.selectors.resolveFrameForSelector(frameBodySelector, { strict: true }));
if (!child)
return { full: [] };
try {
return await ariaSnapshotForFrame(progress2, child.frame, { ...options2, info: void 0 });
} catch {
return { full: [] };
}
}
function ensureArrayLimit(array, limit) {
if (array.length > limit)
return array.splice(0, limit / 10);
return [];
}
var PageEvent, navigationMarkSymbol, Page, WorkerEvent, Worker, PageBinding, InitScript;
var init_page = __esm({
"packages/playwright-core/src/server/page.ts"() {
"use strict";
init_selectorParser();
init_manualPromise();
init_utilityScriptSerializers();
init_comparators();
init_debugLogger();
init_manualPromise();
init_assert();
init_protocolFormatter();
init_stringUtils();
init_locatorGenerators();
init_browserContext();
init_disposable2();
init_console();
init_errors();
init_fileChooser();
init_frames();
init_helper();
init_input();
init_instrumentation();
init_javascript();
init_screenshotter();
init_callLog();
init_bindingsControllerSource();
init_overlay();
init_dom();
init_screencast();
PageEvent = {
Close: "close",
Crash: "crash",
Download: "download",
EmulatedSizeChanged: "emulatedsizechanged",
FileChooser: "filechooser",
FrameAttached: "frameattached",
FrameDetached: "framedetached",
InternalFrameNavigatedToNewDocument: "internalframenavigatedtonewdocument",
LocatorHandlerTriggered: "locatorhandlertriggered",
WebSocket: "websocket",
Worker: "worker"
};
navigationMarkSymbol = Symbol("navigationMark");
Page = class _Page extends SdkObject {
constructor(delegate, browserContext) {
super(browserContext, "page");
this._closedState = "open";
this.closedPromise = new ManualPromise();
this._initializedPromise = new ManualPromise();
this._consoleMessages = [];
this._pageErrors = [];
this._crashed = false;
this.openScope = new LongStandingScope();
this._emulatedMedia = {};
this._fileChooserInterceptedBy = /* @__PURE__ */ new Set();
this._pageBindings = /* @__PURE__ */ new Map();
this.initScripts = [];
this._workers = /* @__PURE__ */ new Map();
this.requestInterceptors = [];
this._locatorHandlers = /* @__PURE__ */ new Map();
this._lastLocatorHandlerUid = 0;
this._locatorHandlerRunningCounter = 0;
this._networkRequests = [];
this.attribution.page = this;
this.delegate = delegate;
this.browserContext = browserContext;
this.keyboard = new Keyboard(delegate.rawKeyboard, this);
this.mouse = new Mouse(delegate.rawMouse, this);
this.touchscreen = new Touchscreen(delegate.rawTouchscreen, this);
this.screenshotter = new Screenshotter(this);
this.frameManager = new FrameManager(this);
this.overlay = new Overlay(this);
this.screencast = new Screencast(this);
if (delegate.pdf)
this.pdf = delegate.pdf.bind(delegate);
this.coverage = delegate.coverage ? delegate.coverage() : null;
this.isStorageStatePage = browserContext.isCreatingStorageStatePage();
}
static {
this.Events = PageEvent;
}
async reportAsNew(opener, error) {
if (opener) {
const openerPageOrError = await opener.waitForInitializedOrError();
if (openerPageOrError instanceof _Page && !openerPageOrError.isClosed())
this._opener = openerPageOrError;
}
this._markInitialized(error);
}
_markInitialized(error = void 0) {
if (error) {
if (this.browserContext.isClosingOrClosed())
return;
this.frameManager.createDummyMainFrameIfNeeded();
}
this._initialized = error || this;
this.emitOnContext(BrowserContext.Events.Page, this);
for (const pageError of this._pageErrors)
this.emitOnContext(BrowserContext.Events.PageError, pageError, this);
for (const message of this._consoleMessages)
this.emitOnContext(BrowserContext.Events.Console, message);
if (this.isClosed())
this.emit(_Page.Events.Close);
else
this.instrumentation.onPageOpen(this);
this._initializedPromise.resolve(this._initialized);
}
initializedOrUndefined() {
return this._initialized ? this : void 0;
}
waitForInitializedOrError() {
return this._initializedPromise;
}
emitOnContext(event, ...args) {
if (this.isStorageStatePage)
return;
this.browserContext.emit(event, ...args);
}
async resetForReuse(progress2) {
await this.mainFrame().gotoImpl(progress2, "about:blank", {});
this._emulatedSize = void 0;
this._emulatedMedia = {};
this._extraHTTPHeaders = void 0;
await progress2.race(Promise.all([
this.delegate.updateEmulatedViewportSize(),
this.delegate.updateEmulateMedia(),
this.delegate.updateExtraHTTPHeaders()
]));
await this.delegate.resetForReuse(progress2);
}
_didClose() {
this.frameManager.dispose();
this.screencast.dispose();
this.overlay.dispose();
assert(this._closedState !== "closed", "Page closed twice");
this._closedState = "closed";
this.emit(_Page.Events.Close);
this.browserContext.emit(BrowserContext.Events.PageClosed, this);
this.closedPromise.resolve();
this.instrumentation.onPageClose(this);
this.openScope.close(new TargetClosedError(this.closeReason()));
}
_didCrash() {
this.frameManager.dispose();
this.screencast.dispose();
this.overlay.dispose();
this.emit(_Page.Events.Crash);
this._crashed = true;
this.instrumentation.onPageClose(this);
this.openScope.close(new Error("Page crashed"));
}
async _onFileChooserOpened(handle) {
let multiple;
try {
multiple = await handle.evaluate((element2) => !!element2.multiple);
} catch (e) {
return;
}
if (!this.listenerCount(_Page.Events.FileChooser)) {
handle.dispose();
return;
}
const fileChooser = new FileChooser(handle, multiple);
this.emit(_Page.Events.FileChooser, fileChooser);
}
opener() {
return this._opener;
}
mainFrame() {
return this.frameManager.mainFrame();
}
frames() {
return this.frameManager.frames();
}
async exposeBinding(progress2, name, playwrightBinding) {
if (this._pageBindings.has(name))
throw new Error(`Function "${name}" has been already registered`);
if (this.browserContext._pageBindings.has(name))
throw new Error(`Function "${name}" has been already registered in the browser context`);
await progress2.race(this.browserContext.exposePlaywrightBindingIfNeeded());
const binding = new PageBinding(this, name, playwrightBinding);
this._pageBindings.set(name, binding);
try {
await progress2.race(this.delegate.addInitScript(binding.initScript));
await progress2.race(this.safeNonStallingEvaluateInAllFrames(binding.initScript.source, "main"));
return binding;
} catch (error) {
this._pageBindings.delete(name);
throw error;
}
}
async removeExposedBinding(binding) {
if (this._pageBindings.get(binding.name) !== binding)
return;
this._pageBindings.delete(binding.name);
await this.delegate.removeInitScripts([binding.initScript]);
const cleanup = `{ ${binding.cleanupScript} };`;
await this.safeNonStallingEvaluateInAllFrames(cleanup, "main");
}
async setExtraHTTPHeaders(progress2, headers) {
const oldHeaders = this._extraHTTPHeaders;
try {
this._extraHTTPHeaders = headers;
await progress2.race(this.delegate.updateExtraHTTPHeaders());
} catch (error) {
this._extraHTTPHeaders = oldHeaders;
this.delegate.updateExtraHTTPHeaders().catch(() => {
});
throw error;
}
}
extraHTTPHeaders() {
return this._extraHTTPHeaders;
}
addNetworkRequest(request2) {
this._networkRequests.push(request2);
ensureArrayLimit(this._networkRequests, 100);
}
networkRequests() {
return this._networkRequests;
}
async onBindingCalled(payload, context2) {
if (this._closedState === "closed")
return;
await PageBinding.dispatch(this, payload, context2);
}
addConsoleMessage(worker, type3, args, location2, text2, timestamp) {
const message = new ConsoleMessage(this, worker, type3, text2, args, location2, timestamp);
const intercepted = this.frameManager.interceptConsoleMessage(message);
if (intercepted) {
args.forEach((arg) => arg.dispose());
return;
}
this._consoleMessages.push(message);
ensureArrayLimit(this._consoleMessages, 200);
if (this._initialized)
this.emitOnContext(BrowserContext.Events.Console, message);
}
clearConsoleMessages() {
this._consoleMessages.length = 0;
}
consoleMessages(filter) {
if (filter === "all")
return this._consoleMessages;
const marked = this._consoleMessages.findLastIndex((m) => m[navigationMarkSymbol]);
return marked === -1 ? this._consoleMessages : this._consoleMessages.slice(marked + 1);
}
addPageError(error, location2) {
const pageError = { error, location: location2 };
this._pageErrors.push(pageError);
ensureArrayLimit(this._pageErrors, 200);
if (this._initialized)
this.emitOnContext(BrowserContext.Events.PageError, pageError, this);
}
clearPageErrors() {
this._pageErrors.length = 0;
}
pageErrors(filter) {
if (filter === "all")
return this._pageErrors.map((e) => e.error);
const marked = this._pageErrors.findLastIndex((e) => e[navigationMarkSymbol]);
return (marked === -1 ? this._pageErrors : this._pageErrors.slice(marked + 1)).map((e) => e.error);
}
async reload(progress2, options2) {
return this.mainFrame().raceNavigationAction(progress2, async () => {
const [response2] = await Promise.all([
// Reload must be a new document, and should not be confused with a stray pushState.
this.mainFrame().waitForNavigation(progress2, true, options2),
progress2.race(this.delegate.reload())
]);
return response2;
});
}
async goBack(progress2, options2) {
return this.mainFrame().raceNavigationAction(progress2, async () => {
let error;
const waitPromise = this.mainFrame().waitForNavigation(progress2, false, options2).catch((e) => {
error = e;
return null;
});
const result2 = await progress2.race(this.delegate.goBack());
if (!result2) {
waitPromise.catch(() => {
});
return null;
}
const response2 = await waitPromise;
if (error)
throw error;
return response2;
});
}
async goForward(progress2, options2) {
return this.mainFrame().raceNavigationAction(progress2, async () => {
let error;
const waitPromise = this.mainFrame().waitForNavigation(progress2, false, options2).catch((e) => {
error = e;
return null;
});
const result2 = await progress2.race(this.delegate.goForward());
if (!result2) {
waitPromise.catch(() => {
});
return null;
}
const response2 = await waitPromise;
if (error)
throw error;
return response2;
});
}
requestGC(progress2) {
return progress2.race(this.delegate.requestGC());
}
registerLocatorHandler(selector, noWaitAfter) {
const uid = ++this._lastLocatorHandlerUid;
this._locatorHandlers.set(uid, { selector, noWaitAfter });
return uid;
}
resolveLocatorHandler(uid, remove) {
const handler = this._locatorHandlers.get(uid);
if (remove)
this._locatorHandlers.delete(uid);
if (handler) {
handler.resolved?.resolve();
handler.resolved = void 0;
}
}
unregisterLocatorHandler(uid) {
this._locatorHandlers.delete(uid);
}
async performActionPreChecks(progress2) {
await this._performWaitForNavigationCheck(progress2);
await this._performLocatorHandlersCheckpoint(progress2);
await this._performWaitForNavigationCheck(progress2);
}
async _performWaitForNavigationCheck(progress2) {
if (process.env.PLAYWRIGHT_SKIP_NAVIGATION_CHECK)
return;
const mainFrame = this.frameManager.mainFrame();
if (!mainFrame || !mainFrame.pendingDocument())
return;
const url2 = mainFrame.pendingDocument()?.request?.url();
const toUrl = url2 ? `" ${trimStringWithEllipsis(url2, 200)}"` : "";
progress2.log(` waiting for${toUrl} navigation to finish...`);
await helper.waitForEvent(progress2, mainFrame, Frame.Events.InternalNavigation, (e) => {
if (!e.isPublic)
return false;
if (!e.error)
progress2.log(` navigated to "${trimStringWithEllipsis(mainFrame.url(), 200)}"`);
return true;
}).promise;
}
async _performLocatorHandlersCheckpoint(progress2) {
if (this._locatorHandlerRunningCounter)
return;
for (const [uid, handler] of this._locatorHandlers) {
if (!handler.resolved) {
if (await this.mainFrame().isVisibleInternal(progress2, handler.selector, { strict: true })) {
handler.resolved = new ManualPromise();
this.emit(_Page.Events.LocatorHandlerTriggered, uid);
}
}
if (handler.resolved) {
++this._locatorHandlerRunningCounter;
progress2.log(` found ${asLocator(this.browserContext._browser.sdkLanguage(), handler.selector)}, intercepting action to run the handler`);
const promise = handler.resolved.then(async () => {
if (!handler.noWaitAfter) {
progress2.log(` locator handler has finished, waiting for ${asLocator(this.browserContext._browser.sdkLanguage(), handler.selector)} to be hidden`);
await this.mainFrame().waitForSelector(progress2, handler.selector, false, { state: "hidden" });
} else {
progress2.log(` locator handler has finished`);
}
});
try {
progress2.setAllowConcurrentOrNestedRaces(true);
await progress2.race(this.openScope.race(promise));
} finally {
progress2.setAllowConcurrentOrNestedRaces(false);
--this._locatorHandlerRunningCounter;
}
progress2.log(` interception handler has finished, continuing`);
}
}
}
async emulateMedia(progress2, options2) {
const oldEmulatedMedia = { ...this._emulatedMedia };
if (options2.media !== void 0)
this._emulatedMedia.media = options2.media;
if (options2.colorScheme !== void 0)
this._emulatedMedia.colorScheme = options2.colorScheme;
if (options2.reducedMotion !== void 0)
this._emulatedMedia.reducedMotion = options2.reducedMotion;
if (options2.forcedColors !== void 0)
this._emulatedMedia.forcedColors = options2.forcedColors;
if (options2.contrast !== void 0)
this._emulatedMedia.contrast = options2.contrast;
try {
await progress2.race(this.delegate.updateEmulateMedia());
} catch (error) {
this._emulatedMedia = oldEmulatedMedia;
this.delegate.updateEmulateMedia().catch(() => {
});
throw error;
}
}
emulatedMedia() {
const contextOptions = this.browserContext._options;
return {
media: this._emulatedMedia.media || "no-override",
colorScheme: this._emulatedMedia.colorScheme !== void 0 ? this._emulatedMedia.colorScheme : contextOptions.colorScheme ?? "light",
reducedMotion: this._emulatedMedia.reducedMotion !== void 0 ? this._emulatedMedia.reducedMotion : contextOptions.reducedMotion ?? "no-preference",
forcedColors: this._emulatedMedia.forcedColors !== void 0 ? this._emulatedMedia.forcedColors : contextOptions.forcedColors ?? "none",
contrast: this._emulatedMedia.contrast !== void 0 ? this._emulatedMedia.contrast : contextOptions.contrast ?? "no-preference"
};
}
async setViewportSize(progress2, viewportSize) {
const oldEmulatedSize = this._emulatedSize;
try {
this._setEmulatedSize({ viewport: { ...viewportSize }, screen: { ...viewportSize } });
await progress2.race(this.delegate.updateEmulatedViewportSize());
} catch (error) {
this._emulatedSize = oldEmulatedSize;
this.delegate.updateEmulatedViewportSize().catch(() => {
});
throw error;
}
}
setEmulatedSizeFromWindowOpen(emulatedSize) {
this._setEmulatedSize(emulatedSize);
}
_setEmulatedSize(emulatedSize) {
this._emulatedSize = emulatedSize;
this.emit(_Page.Events.EmulatedSizeChanged);
}
emulatedSize() {
if (this._emulatedSize)
return this._emulatedSize;
const contextOptions = this.browserContext._options;
return contextOptions.viewport ? { viewport: contextOptions.viewport, screen: contextOptions.screen || contextOptions.viewport } : void 0;
}
async bringToFront(progress2) {
await progress2.race(this.delegate.bringToFront());
}
async addInitScript(progress2, source8) {
return await progress2.race(this._addInitScript(source8));
}
async _addInitScript(source8) {
const initScript = new InitScript(this, source8);
this.initScripts.push(initScript);
try {
await this.delegate.addInitScript(initScript);
} catch (error) {
initScript.dispose().catch(() => {
});
throw error;
}
return initScript;
}
async removeInitScript(initScript) {
this.initScripts = this.initScripts.filter((script) => initScript !== script);
await this.delegate.removeInitScripts([initScript]);
}
needsRequestInterception() {
return this.requestInterceptors.length > 0 || this.browserContext.requestInterceptors.length > 0;
}
async addRequestInterceptor(progress2, handler, prepend) {
if (prepend)
this.requestInterceptors.unshift(handler);
else
this.requestInterceptors.push(handler);
await progress2.race(this.delegate.updateRequestInterception());
}
async removeRequestInterceptor(handler) {
const index = this.requestInterceptors.indexOf(handler);
if (index === -1)
return;
this.requestInterceptors.splice(index, 1);
await this.browserContext.notifyRoutesInFlightAboutRemovedHandler(handler);
await this.delegate.updateRequestInterception();
}
async expectScreenshot(progress2, options2) {
const locator2 = options2.locator;
const rafrafScreenshot = locator2 ? async (progress3, timeout) => {
return await locator2.frame.rafrafTimeoutScreenshotElementWithProgress(progress3, locator2.selector, timeout, options2 || {});
} : async (progress3, timeout) => {
await this.performActionPreChecks(progress3);
await this.mainFrame().rafrafTimeout(progress3, timeout);
return await this.screenshotter.screenshotPage(progress3, options2 || {});
};
const comparator = getComparator("image/png");
if (!options2.expected && options2.isNot)
return { errorMessage: '"not" matcher requires expected result' };
try {
const format2 = validateScreenshotOptions(options2 || {});
if (format2 !== "png")
throw new Error("Only PNG screenshots are supported");
} catch (error) {
return { errorMessage: error.message };
}
let intermediateResult;
const areEqualScreenshots = (actual, expected, previous) => {
const comparatorResult = actual && expected ? comparator(actual, expected, options2) : void 0;
if (comparatorResult !== void 0 && !!comparatorResult === !!options2.isNot)
return true;
if (comparatorResult)
intermediateResult = { errorMessage: comparatorResult.errorMessage, diff: comparatorResult.diff, actual, previous };
return false;
};
try {
let actual;
let previous;
const pollIntervals = [0, 100, 250, 500];
progress2.log(`${renderTitleForCall(progress2.metadata)}${options2.timeout ? ` with timeout ${options2.timeout}ms` : ""}`);
if (options2.expected)
progress2.log(` verifying given screenshot expectation`);
else
progress2.log(` generating new stable screenshot expectation`);
let isFirstIteration = true;
while (true) {
if (this.isClosed())
throw new Error("The page has closed");
const screenshotTimeout = pollIntervals.shift() ?? 1e3;
if (screenshotTimeout)
progress2.log(`waiting ${screenshotTimeout}ms before taking screenshot`);
previous = actual;
actual = await rafrafScreenshot(progress2, screenshotTimeout).catch((e) => {
if (this.mainFrame().isNonRetriableError(e))
throw e;
progress2.log(`failed to take screenshot - ` + e.message);
return void 0;
});
if (!actual)
continue;
const expectation = options2.expected && isFirstIteration ? options2.expected : previous;
if (areEqualScreenshots(actual, expectation, previous))
break;
if (intermediateResult)
progress2.log(intermediateResult.errorMessage);
isFirstIteration = false;
}
if (!isFirstIteration)
progress2.log(`captured a stable screenshot`);
if (!options2.expected)
return { actual };
if (isFirstIteration) {
progress2.log(`screenshot matched expectation`);
return {};
}
if (areEqualScreenshots(actual, options2.expected, void 0)) {
progress2.log(`screenshot matched expectation`);
return {};
}
throw new Error(intermediateResult.errorMessage);
} catch (e) {
if (isJavaScriptErrorInEvaluate(e) || isInvalidSelectorError(e))
throw e;
let errorMessage = e.message;
if (e instanceof TimeoutError && intermediateResult?.previous)
errorMessage = `Failed to take two consecutive stable screenshots.`;
return {
log: compressCallLog(e.message ? [...progress2.metadata.log, e.message] : progress2.metadata.log),
...intermediateResult,
errorMessage,
timedOut: e instanceof TimeoutError
};
}
}
async screenshot(progress2, options2) {
return await this.screenshotter.screenshotPage(progress2, options2);
}
async close(progress2, options2 = {}) {
await progress2.race(this._close(options2));
}
async _close(options2 = {}) {
if (this._closedState === "closed")
return;
if (options2.reason)
this._closeReason = options2.reason;
const runBeforeUnload = !!options2.runBeforeUnload;
if (!runBeforeUnload)
await this.screencast.handlePageOrContextClose();
if (this._closedState !== "closing") {
if (!runBeforeUnload)
this._closedState = "closing";
await this.delegate.closePage(runBeforeUnload).catch((e) => debugLogger.log("error", e));
}
if (!runBeforeUnload)
await this.closedPromise;
}
isClosed() {
return this._closedState === "closed";
}
hasCrashed() {
return this._crashed;
}
isClosedOrClosingOrCrashed() {
return this._closedState !== "open" || this._crashed;
}
addWorker(workerId, worker) {
this._workers.set(workerId, worker);
this.emit(_Page.Events.Worker, worker);
}
removeWorker(workerId) {
const worker = this._workers.get(workerId);
if (!worker)
return;
worker.didClose();
this._workers.delete(workerId);
}
clearWorkers() {
for (const [workerId, worker] of this._workers) {
worker.didClose();
this._workers.delete(workerId);
}
}
async setFileChooserInterceptedBy(progress2, enabled, by) {
await progress2.race(this._setFileChooserInterceptedBy(enabled, by));
}
async _setFileChooserInterceptedBy(enabled, by) {
const wasIntercepted = this.fileChooserIntercepted();
if (enabled)
this._fileChooserInterceptedBy.add(by);
else
this._fileChooserInterceptedBy.delete(by);
if (wasIntercepted !== this.fileChooserIntercepted())
await this.delegate.updateFileChooserInterception();
}
fileChooserIntercepted() {
return this._fileChooserInterceptedBy.size > 0;
}
frameNavigatedToNewDocument(frame) {
this.emit(_Page.Events.InternalFrameNavigatedToNewDocument, frame);
this.browserContext.emit(BrowserContext.Events.InternalFrameNavigatedToNewDocument, frame, this);
const origin = frame.origin();
if (origin)
this.browserContext.addVisitedOrigin(origin);
if (frame === this.mainFrame()) {
if (this._consoleMessages.length > 0)
this._consoleMessages[this._consoleMessages.length - 1][navigationMarkSymbol] = true;
if (this._pageErrors.length > 0)
this._pageErrors[this._pageErrors.length - 1][navigationMarkSymbol] = true;
}
}
allInitScripts() {
const bindings = [...this.browserContext._pageBindings.values(), ...this._pageBindings.values()].map((binding) => binding.initScript);
if (this.browserContext.bindingsInitScript)
bindings.unshift(this.browserContext.bindingsInitScript);
return [...bindings, ...this.browserContext.initScripts, ...this.initScripts];
}
getBinding(name) {
return this._pageBindings.get(name) || this.browserContext._pageBindings.get(name);
}
async safeNonStallingEvaluateInAllFrames(expression2, world, options2 = {}) {
await Promise.all(this.frames().map(async (frame) => {
try {
await frame.nonStallingEvaluateInExistingContext(expression2, world);
} catch (e) {
if (options2.throwOnJSErrors && isJavaScriptErrorInEvaluate(e))
throw e;
}
}));
}
async hideHighlight() {
await Promise.all(this.frames().map((frame) => frame.hideHighlight().catch(() => {
})));
}
async setDockTile(image) {
await this.delegate.setDockTile(image);
}
};
WorkerEvent = {
Console: "console",
Close: "close"
};
Worker = class _Worker extends SdkObject {
constructor(parent, url2, onDisconnect) {
super(parent, "worker");
this._executionContextPromise = new ManualPromise();
this._workerScriptLoaded = false;
this.existingExecutionContext = null;
this.openScope = new LongStandingScope();
this.attribution.worker = this;
this.url = url2;
this._onDisconnect = onDisconnect;
}
static {
this.Events = WorkerEvent;
}
createExecutionContext(delegate) {
this.existingExecutionContext = new ExecutionContext(this, delegate, "worker");
if (this._workerScriptLoaded)
this._executionContextPromise.resolve(this.existingExecutionContext);
return this.existingExecutionContext;
}
destroyExecutionContext(errorMessage) {
if (this.existingExecutionContext)
this.existingExecutionContext.contextDestroyed(errorMessage);
this.existingExecutionContext = null;
this._workerScriptLoaded = false;
this._executionContextPromise = new ManualPromise();
}
workerScriptLoaded() {
this._workerScriptLoaded = true;
if (this.existingExecutionContext)
this._executionContextPromise.resolve(this.existingExecutionContext);
}
didClose() {
if (this.existingExecutionContext)
this.existingExecutionContext.contextDestroyed("Worker was closed");
this.emit(_Worker.Events.Close, this);
this.openScope.close(new Error("Worker closed"));
}
async evaluateExpression(progress2, expression2, isFunction2, arg) {
return progress2.race(evaluateExpression(await this._executionContextPromise, expression2, { returnByValue: true, isFunction: isFunction2 }, arg));
}
async evaluateExpressionHandle(progress2, expression2, isFunction2, arg) {
return progress2.race(evaluateExpression(await this._executionContextPromise, expression2, { returnByValue: false, isFunction: isFunction2 }, arg));
}
async disconnect(progress2, options2 = {}) {
if (!this._onDisconnect)
throw new Error("Cannot disconnect from this worker");
this._closeReason = options2.reason;
await progress2.race(this._onDisconnect());
}
};
PageBinding = class _PageBinding extends DisposableObject {
static {
this.kController = "__playwright__binding__controller__";
}
static {
this.kBindingName = "__playwright__binding__";
}
static createInitScript(browserContext) {
return new InitScript(browserContext, `
(() => {
const module = {};
${source4}
const property = '${_PageBinding.kController}';
if (!globalThis[property])
globalThis[property] = new (module.exports.BindingsController())(globalThis, '${_PageBinding.kBindingName}');
})();
`);
}
constructor(parent, name, playwrightFunction) {
super(parent);
this.name = name;
this.playwrightFunction = playwrightFunction;
this.initScript = new InitScript(parent, `globalThis['${_PageBinding.kController}'].addBinding(${JSON.stringify(name)})`);
this.cleanupScript = `globalThis['${_PageBinding.kController}'].removeBinding(${JSON.stringify(name)})`;
}
static async dispatch(page, payload, context2) {
const { name, seq, serializedArgs } = JSON.parse(payload);
try {
assert(context2.world);
const binding = page.getBinding(name);
if (!binding)
throw new Error(`Function "${name}" is not exposed`);
if (!Array.isArray(serializedArgs))
throw new Error(`serializedArgs is not an array. This can happen when Array.prototype.toJSON is defined incorrectly`);
const args = serializedArgs.map((a) => parseEvaluationResultValue(a));
const result2 = await binding.playwrightFunction({ frame: context2.frame, page, context: page.browserContext }, ...args);
context2.evaluateExpressionHandle(`arg => globalThis['${_PageBinding.kController}'].deliverBindingResult(arg)`, { isFunction: true }, { name, seq, result: result2 }).catch((e) => debugLogger.log("error", e));
} catch (error) {
context2.evaluateExpressionHandle(`arg => globalThis['${_PageBinding.kController}'].deliverBindingResult(arg)`, { isFunction: true }, { name, seq, error }).catch((e) => debugLogger.log("error", e));
}
}
async dispose() {
await this.parent.removeExposedBinding(this);
}
};
InitScript = class extends DisposableObject {
constructor(owner, source8) {
super(owner);
this.source = `(() => {
${source8}
})();`;
}
async dispose() {
await this.parent.removeInitScript(this);
}
};
}
});
// packages/playwright-core/src/server/trace/recorder/snapshotter.ts
var mime5, Snapshotter, kNeedsResetSymbol;
var init_snapshotter = __esm({
"packages/playwright-core/src/server/trace/recorder/snapshotter.ts"() {
"use strict";
init_time();
init_crypto();
init_debugLogger();
init_eventsHelper();
init_snapshotterInjected();
init_browserContext();
init_page();
init_progress();
mime5 = require("./utilsBundle").mime;
Snapshotter = class {
constructor(context2, delegate) {
this._eventListeners = [];
this._started = false;
this._context = context2;
this._delegate = delegate;
const guid = createGuid();
this._snapshotStreamer = "__playwright_snapshot_streamer_" + guid;
}
started() {
return this._started;
}
async start(progress2) {
this._started = true;
if (!this._initScript)
await this._initialize(progress2);
await progress2.race(this.reset());
}
async reset() {
if (this._started)
await this._context.safeNonStallingEvaluateInAllFrames(`window["${this._snapshotStreamer}"].reset()`, "main");
}
stop() {
this._started = false;
}
async resetForReuse() {
if (this._initScript) {
eventsHelper.removeEventListeners(this._eventListeners);
await this._initScript.dispose();
this._initScript = void 0;
}
}
async _initialize(progress2) {
for (const page of this._context.pages())
this._onPage(page);
this._eventListeners = [
eventsHelper.addEventListener(this._context, BrowserContext.Events.Page, this._onPage.bind(this))
];
const { javaScriptEnabled } = this._context._options;
const initScriptSource = `(${frameSnapshotStreamer})("${this._snapshotStreamer}", ${javaScriptEnabled || javaScriptEnabled === void 0})`;
this._initScript = await this._context.addInitScript(progress2, initScriptSource);
await progress2.race(this._context.safeNonStallingEvaluateInAllFrames(initScriptSource, "main"));
}
dispose() {
eventsHelper.removeEventListeners(this._eventListeners);
}
async _captureFrameSnapshot(frame) {
const needsReset = !!frame[kNeedsResetSymbol];
frame[kNeedsResetSymbol] = false;
const expression2 = `window["${this._snapshotStreamer}"].captureSnapshot(${needsReset ? "true" : "false"})`;
try {
return await frame.nonStallingRawEvaluateInExistingMainContext(expression2);
} catch (e) {
frame[kNeedsResetSymbol] = true;
debugLogger.log("error", e);
}
}
async captureSnapshot(page, callId, snapshotName) {
const snapshots = page.frames().map(async (frame) => {
const data = await this._captureFrameSnapshot(frame);
if (!data || !this._started)
return;
const snapshot3 = {
callId,
snapshotName,
pageId: page.guid,
frameId: frame.guid,
frameUrl: data.url,
doctype: data.doctype,
html: data.html,
viewport: data.viewport,
timestamp: monotonicTime(),
wallTime: data.wallTime,
collectionTime: data.collectionTime,
resourceOverrides: [],
isMainFrame: page.mainFrame() === frame
};
for (const { url: url2, content, contentType } of data.resourceOverrides) {
if (typeof content === "string") {
const buffer = Buffer.from(content);
const sha1 = calculateSha1(buffer) + "." + (mime5.getExtension(contentType) || "dat");
this._delegate.onSnapshotterBlob({ sha1, buffer });
snapshot3.resourceOverrides.push({ url: url2, sha1 });
} else {
snapshot3.resourceOverrides.push({ url: url2, ref: content });
}
}
this._delegate.onFrameSnapshot(snapshot3);
});
await Promise.all(snapshots);
}
_onPage(page) {
for (const frame of page.frames())
this._annotateFrameHierarchy(frame);
this._eventListeners.push(eventsHelper.addEventListener(page, Page.Events.FrameAttached, (frame) => this._annotateFrameHierarchy(frame)));
}
_annotateFrameHierarchy(frame) {
(async () => {
const frameElement = await frame.frameElement(nullProgress);
const parent = frame.parentFrame();
if (!parent)
return;
const context2 = await parent.mainContext();
await context2?.evaluate(({ snapshotStreamer, frameElement: frameElement2, frameId }) => {
window[snapshotStreamer].markIframe(frameElement2, frameId);
}, { snapshotStreamer: this._snapshotStreamer, frameElement, frameId: frame.guid });
frameElement.dispose();
})().catch(() => {
});
}
};
kNeedsResetSymbol = Symbol("kNeedsReset");
}
});
// packages/playwright-core/src/server/artifact.ts
var import_fs14, Artifact;
var init_artifact = __esm({
"packages/playwright-core/src/server/artifact.ts"() {
"use strict";
import_fs14 = __toESM(require("fs"));
init_manualPromise();
init_assert();
init_errors();
init_instrumentation();
Artifact = class extends SdkObject {
constructor(parent, localPath, unaccessibleErrorMessage, cancelCallback) {
super(parent, "artifact");
this._finishedPromise = new ManualPromise();
this._saveCallbacks = [];
this._finished = false;
this._deleted = false;
this._localPath = localPath;
this._unaccessibleErrorMessage = unaccessibleErrorMessage;
this._cancelCallback = cancelCallback;
}
async localPathAfterFinished(progress2) {
return await progress2.race(this._localPathAfterFinished());
}
async failureError(progress2) {
return await progress2.race(this._failureError());
}
async cancel(progress2) {
return await progress2.race(this._cancel());
}
async delete(progress2) {
return await progress2.race(this._delete());
}
localPath() {
return this._localPath;
}
async _localPathAfterFinished() {
if (this._unaccessibleErrorMessage)
throw new Error(this._unaccessibleErrorMessage);
await this._finishedPromise;
if (this._failureErrorValue)
throw this._failureErrorValue;
return this._localPath;
}
saveAs(progress2, saveCallback) {
if (this._unaccessibleErrorMessage)
throw new Error(this._unaccessibleErrorMessage);
if (this._deleted)
throw new Error(`File already deleted. Save before deleting.`);
if (this._failureErrorValue)
throw this._failureErrorValue;
if (this._finished) {
saveCallback(this._localPath).catch(() => {
});
return;
}
this._saveCallbacks.push(saveCallback);
}
async _failureError() {
if (this._unaccessibleErrorMessage)
return this._unaccessibleErrorMessage;
await this._finishedPromise;
return this._failureErrorValue?.message || null;
}
async _cancel() {
assert(this._cancelCallback !== void 0);
return this._cancelCallback();
}
async _delete() {
if (this._unaccessibleErrorMessage)
return;
const fileName = await this._localPathAfterFinished();
if (this._deleted)
return;
this._deleted = true;
if (fileName)
await import_fs14.default.promises.unlink(fileName).catch((e) => {
});
}
async deleteOnContextClose() {
if (this._deleted)
return;
this._deleted = true;
if (!this._unaccessibleErrorMessage)
await import_fs14.default.promises.unlink(this._localPath).catch((e) => {
});
await this.reportFinished(new TargetClosedError(this.closeReason()));
}
async reportFinished(error) {
if (this._finished)
return;
this._finished = true;
this._failureErrorValue = error;
if (error) {
for (const callback of this._saveCallbacks)
await callback("", error);
} else {
for (const callback of this._saveCallbacks)
await callback(this._localPath);
}
this._saveCallbacks = [];
this._finishedPromise.resolve();
}
};
}
});
// packages/playwright-core/src/protocol/validatorPrimitives.ts
function findValidator(type3, method, kind) {
const validator = maybeFindValidator(type3, method, kind);
if (!validator)
throw new ValidationError(`Unknown scheme for ${kind}: ${type3}.${method}`);
return validator;
}
function maybeFindValidator(type3, method, kind) {
const schemeName = type3 + (kind === "Initializer" ? "" : method[0].toUpperCase() + method.substring(1)) + kind;
return scheme[schemeName];
}
function createMetadataValidator() {
return tOptional(scheme["Metadata"]);
}
var ValidationError, scheme, tFloat, tInt, tBoolean, tString, tBinary, tAny, tOptional, tArray, tObject, tEnum, tChannel, tType;
var init_validatorPrimitives = __esm({
"packages/playwright-core/src/protocol/validatorPrimitives.ts"() {
"use strict";
ValidationError = class extends Error {
};
scheme = {};
tFloat = (arg, path59, context2) => {
if (arg instanceof Number)
return arg.valueOf();
if (typeof arg === "number")
return arg;
throw new ValidationError(`${path59}: expected float, got ${typeof arg}`);
};
tInt = (arg, path59, context2) => {
let value2;
if (arg instanceof Number)
value2 = arg.valueOf();
else if (typeof arg === "number")
value2 = arg;
else
throw new ValidationError(`${path59}: expected integer, got ${typeof arg}`);
if (!Number.isInteger(value2))
throw new ValidationError(`${path59}: expected integer, got float ${value2}`);
return value2;
};
tBoolean = (arg, path59, context2) => {
if (arg instanceof Boolean)
return arg.valueOf();
if (typeof arg === "boolean")
return arg;
throw new ValidationError(`${path59}: expected boolean, got ${typeof arg}`);
};
tString = (arg, path59, context2) => {
if (arg instanceof String)
return arg.valueOf();
if (typeof arg === "string")
return arg;
throw new ValidationError(`${path59}: expected string, got ${typeof arg}`);
};
tBinary = (arg, path59, context2) => {
if (context2.binary === "fromBase64") {
if (arg instanceof String)
return Buffer.from(arg.valueOf(), "base64");
if (typeof arg === "string")
return Buffer.from(arg, "base64");
throw new ValidationError(`${path59}: expected base64-encoded buffer, got ${typeof arg}`);
}
if (context2.binary === "toBase64") {
if (!(arg instanceof Buffer))
throw new ValidationError(`${path59}: expected Buffer, got ${typeof arg}`);
return arg.toString("base64");
}
if (context2.binary === "buffer") {
if (!(arg instanceof Buffer) && !(arg instanceof Object))
throw new ValidationError(`${path59}: expected Buffer, got ${typeof arg}`);
return arg;
}
throw new ValidationError(`Unsupported binary behavior "${context2.binary}"`);
};
tAny = (arg, path59, context2) => {
return arg;
};
tOptional = (v) => {
return (arg, path59, context2) => {
if (Object.is(arg, void 0))
return arg;
return v(arg, path59, context2);
};
};
tArray = (v) => {
return (arg, path59, context2) => {
if (!Array.isArray(arg))
throw new ValidationError(`${path59}: expected array, got ${typeof arg}`);
return arg.map((x, index) => v(x, path59 + "[" + index + "]", context2));
};
};
tObject = (s) => {
return (arg, path59, context2) => {
if (Object.is(arg, null))
throw new ValidationError(`${path59}: expected object, got null`);
if (typeof arg !== "object")
throw new ValidationError(`${path59}: expected object, got ${typeof arg}`);
const result2 = {};
for (const [key, v] of Object.entries(s)) {
const value2 = v(arg[key], path59 ? path59 + "." + key : key, context2);
if (!Object.is(value2, void 0))
result2[key] = value2;
}
if (context2.isUnderTest()) {
for (const [key, value2] of Object.entries(arg)) {
if (key.startsWith("__testHook"))
result2[key] = value2;
}
}
return result2;
};
};
tEnum = (e) => {
return (arg, path59, context2) => {
if (!e.includes(arg))
throw new ValidationError(`${path59}: expected one of (${e.join("|")})`);
return arg;
};
};
tChannel = (names) => {
return (arg, path59, context2) => {
return context2.tChannelImpl(names, arg, path59, context2);
};
};
tType = (name) => {
return (arg, path59, context2) => {
const v = scheme[name];
if (!v)
throw new ValidationError(path59 + ': unknown type "' + name + '"');
return v(arg, path59, context2);
};
};
}
});
// packages/playwright-core/src/protocol/validator.ts
var init_validator = __esm({
"packages/playwright-core/src/protocol/validator.ts"() {
"use strict";
init_validatorPrimitives();
init_validatorPrimitives();
scheme.AndroidInitializer = tOptional(tObject({}));
scheme.AndroidDevicesParams = tObject({
host: tOptional(tString),
port: tOptional(tInt),
omitDriverInstall: tOptional(tBoolean)
});
scheme.AndroidDevicesResult = tObject({
devices: tArray(tChannel(["AndroidDevice"]))
});
scheme.AndroidSocketInitializer = tOptional(tObject({}));
scheme.AndroidSocketDataEvent = tObject({
data: tBinary
});
scheme.AndroidSocketCloseEvent = tOptional(tObject({}));
scheme.AndroidSocketWriteParams = tObject({
data: tBinary
});
scheme.AndroidSocketWriteResult = tOptional(tObject({}));
scheme.AndroidSocketCloseParams = tOptional(tObject({}));
scheme.AndroidSocketCloseResult = tOptional(tObject({}));
scheme.AndroidDeviceInitializer = tObject({
model: tString,
serial: tString
});
scheme.AndroidDeviceCloseEvent = tOptional(tObject({}));
scheme.AndroidDeviceWebViewAddedEvent = tObject({
webView: tType("AndroidWebView")
});
scheme.AndroidDeviceWebViewRemovedEvent = tObject({
socketName: tString
});
scheme.AndroidDeviceWaitParams = tObject({
androidSelector: tType("AndroidSelector"),
state: tOptional(tEnum(["gone"])),
timeout: tFloat
});
scheme.AndroidDeviceWaitResult = tOptional(tObject({}));
scheme.AndroidDeviceFillParams = tObject({
androidSelector: tType("AndroidSelector"),
text: tString,
timeout: tFloat
});
scheme.AndroidDeviceFillResult = tOptional(tObject({}));
scheme.AndroidDeviceTapParams = tObject({
androidSelector: tType("AndroidSelector"),
duration: tOptional(tFloat),
timeout: tFloat
});
scheme.AndroidDeviceTapResult = tOptional(tObject({}));
scheme.AndroidDeviceDragParams = tObject({
androidSelector: tType("AndroidSelector"),
dest: tType("Point"),
speed: tOptional(tFloat),
timeout: tFloat
});
scheme.AndroidDeviceDragResult = tOptional(tObject({}));
scheme.AndroidDeviceFlingParams = tObject({
androidSelector: tType("AndroidSelector"),
direction: tEnum(["up", "down", "left", "right"]),
speed: tOptional(tFloat),
timeout: tFloat
});
scheme.AndroidDeviceFlingResult = tOptional(tObject({}));
scheme.AndroidDeviceLongTapParams = tObject({
androidSelector: tType("AndroidSelector"),
timeout: tFloat
});
scheme.AndroidDeviceLongTapResult = tOptional(tObject({}));
scheme.AndroidDevicePinchCloseParams = tObject({
androidSelector: tType("AndroidSelector"),
percent: tFloat,
speed: tOptional(tFloat),
timeout: tFloat
});
scheme.AndroidDevicePinchCloseResult = tOptional(tObject({}));
scheme.AndroidDevicePinchOpenParams = tObject({
androidSelector: tType("AndroidSelector"),
percent: tFloat,
speed: tOptional(tFloat),
timeout: tFloat
});
scheme.AndroidDevicePinchOpenResult = tOptional(tObject({}));
scheme.AndroidDeviceScrollParams = tObject({
androidSelector: tType("AndroidSelector"),
direction: tEnum(["up", "down", "left", "right"]),
percent: tFloat,
speed: tOptional(tFloat),
timeout: tFloat
});
scheme.AndroidDeviceScrollResult = tOptional(tObject({}));
scheme.AndroidDeviceSwipeParams = tObject({
androidSelector: tType("AndroidSelector"),
direction: tEnum(["up", "down", "left", "right"]),
percent: tFloat,
speed: tOptional(tFloat),
timeout: tFloat
});
scheme.AndroidDeviceSwipeResult = tOptional(tObject({}));
scheme.AndroidDeviceInfoParams = tObject({
androidSelector: tType("AndroidSelector")
});
scheme.AndroidDeviceInfoResult = tObject({
info: tType("AndroidElementInfo")
});
scheme.AndroidDeviceScreenshotParams = tOptional(tObject({}));
scheme.AndroidDeviceScreenshotResult = tObject({
binary: tBinary
});
scheme.AndroidDeviceInputTypeParams = tObject({
text: tString
});
scheme.AndroidDeviceInputTypeResult = tOptional(tObject({}));
scheme.AndroidDeviceInputPressParams = tObject({
key: tString
});
scheme.AndroidDeviceInputPressResult = tOptional(tObject({}));
scheme.AndroidDeviceInputTapParams = tObject({
point: tType("Point")
});
scheme.AndroidDeviceInputTapResult = tOptional(tObject({}));
scheme.AndroidDeviceInputSwipeParams = tObject({
segments: tArray(tType("Point")),
steps: tInt
});
scheme.AndroidDeviceInputSwipeResult = tOptional(tObject({}));
scheme.AndroidDeviceInputDragParams = tObject({
from: tType("Point"),
to: tType("Point"),
steps: tInt
});
scheme.AndroidDeviceInputDragResult = tOptional(tObject({}));
scheme.AndroidDeviceLaunchBrowserParams = tObject({
noDefaultViewport: tOptional(tBoolean),
viewport: tOptional(tObject({
width: tInt,
height: tInt
})),
screen: tOptional(tObject({
width: tInt,
height: tInt
})),
ignoreHTTPSErrors: tOptional(tBoolean),
clientCertificates: tOptional(tArray(tObject({
origin: tString,
cert: tOptional(tBinary),
key: tOptional(tBinary),
passphrase: tOptional(tString),
pfx: tOptional(tBinary)
}))),
javaScriptEnabled: tOptional(tBoolean),
bypassCSP: tOptional(tBoolean),
userAgent: tOptional(tString),
locale: tOptional(tString),
timezoneId: tOptional(tString),
geolocation: tOptional(tObject({
longitude: tFloat,
latitude: tFloat,
accuracy: tOptional(tFloat)
})),
permissions: tOptional(tArray(tString)),
extraHTTPHeaders: tOptional(tArray(tType("NameValue"))),
offline: tOptional(tBoolean),
httpCredentials: tOptional(tObject({
username: tString,
password: tString,
origin: tOptional(tString),
send: tOptional(tEnum(["always", "unauthorized"]))
})),
deviceScaleFactor: tOptional(tFloat),
isMobile: tOptional(tBoolean),
hasTouch: tOptional(tBoolean),
colorScheme: tOptional(tEnum(["dark", "light", "no-preference", "no-override"])),
reducedMotion: tOptional(tEnum(["reduce", "no-preference", "no-override"])),
forcedColors: tOptional(tEnum(["active", "none", "no-override"])),
acceptDownloads: tOptional(tEnum(["accept", "deny", "internal-browser-default"])),
contrast: tOptional(tEnum(["no-preference", "more", "no-override"])),
baseURL: tOptional(tString),
recordVideo: tOptional(tObject({
dir: tOptional(tString),
size: tOptional(tObject({
width: tInt,
height: tInt
})),
showActions: tOptional(tObject({
duration: tOptional(tFloat),
position: tOptional(tEnum(["top-left", "top", "top-right", "bottom-left", "bottom", "bottom-right"])),
fontSize: tOptional(tInt)
}))
})),
strictSelectors: tOptional(tBoolean),
serviceWorkers: tOptional(tEnum(["allow", "block"])),
selectorEngines: tOptional(tArray(tType("SelectorEngine"))),
testIdAttributeName: tOptional(tString),
pkg: tOptional(tString),
args: tOptional(tArray(tString)),
proxy: tOptional(tObject({
server: tString,
bypass: tOptional(tString),
username: tOptional(tString),
password: tOptional(tString)
}))
});
scheme.AndroidDeviceLaunchBrowserResult = tObject({
context: tChannel(["BrowserContext"])
});
scheme.AndroidDeviceOpenParams = tObject({
command: tString
});
scheme.AndroidDeviceOpenResult = tObject({
socket: tChannel(["AndroidSocket"])
});
scheme.AndroidDeviceShellParams = tObject({
command: tString
});
scheme.AndroidDeviceShellResult = tObject({
result: tBinary
});
scheme.AndroidDeviceInstallApkParams = tObject({
file: tBinary,
args: tOptional(tArray(tString))
});
scheme.AndroidDeviceInstallApkResult = tOptional(tObject({}));
scheme.AndroidDevicePushParams = tObject({
file: tBinary,
path: tString,
mode: tOptional(tInt)
});
scheme.AndroidDevicePushResult = tOptional(tObject({}));
scheme.AndroidDeviceConnectToWebViewParams = tObject({
socketName: tString
});
scheme.AndroidDeviceConnectToWebViewResult = tObject({
context: tChannel(["BrowserContext"])
});
scheme.AndroidDeviceCloseParams = tOptional(tObject({}));
scheme.AndroidDeviceCloseResult = tOptional(tObject({}));
scheme.AndroidWebView = tObject({
pid: tInt,
pkg: tString,
socketName: tString
});
scheme.AndroidSelector = tObject({
checkable: tOptional(tBoolean),
checked: tOptional(tBoolean),
clazz: tOptional(tString),
clickable: tOptional(tBoolean),
depth: tOptional(tInt),
desc: tOptional(tString),
enabled: tOptional(tBoolean),
focusable: tOptional(tBoolean),
focused: tOptional(tBoolean),
hasChild: tOptional(tObject({
androidSelector: tType("AndroidSelector")
})),
hasDescendant: tOptional(tObject({
androidSelector: tType("AndroidSelector"),
maxDepth: tOptional(tInt)
})),
longClickable: tOptional(tBoolean),
pkg: tOptional(tString),
res: tOptional(tString),
scrollable: tOptional(tBoolean),
selected: tOptional(tBoolean),
text: tOptional(tString)
});
scheme.AndroidElementInfo = tObject({
children: tOptional(tArray(tType("AndroidElementInfo"))),
clazz: tString,
desc: tString,
res: tString,
pkg: tString,
text: tString,
bounds: tType("Rect"),
checkable: tBoolean,
checked: tBoolean,
clickable: tBoolean,
enabled: tBoolean,
focusable: tBoolean,
focused: tBoolean,
longClickable: tBoolean,
scrollable: tBoolean,
selected: tBoolean
});
scheme.APIRequestContextInitializer = tObject({
tracing: tChannel(["Tracing"])
});
scheme.APIRequestContextFetchParams = tObject({
url: tString,
encodedParams: tOptional(tString),
params: tOptional(tArray(tType("NameValue"))),
method: tOptional(tString),
headers: tOptional(tArray(tType("NameValue"))),
postData: tOptional(tBinary),
jsonData: tOptional(tString),
formData: tOptional(tArray(tType("NameValue"))),
multipartData: tOptional(tArray(tType("FormField"))),
timeout: tFloat,
failOnStatusCode: tOptional(tBoolean),
ignoreHTTPSErrors: tOptional(tBoolean),
maxRedirects: tOptional(tInt),
maxRetries: tOptional(tInt)
});
scheme.APIRequestContextFetchResult = tObject({
response: tType("APIResponse")
});
scheme.APIRequestContextFetchResponseBodyParams = tObject({
fetchUid: tString
});
scheme.APIRequestContextFetchResponseBodyResult = tObject({
binary: tOptional(tBinary)
});
scheme.APIRequestContextFetchLogParams = tObject({
fetchUid: tString
});
scheme.APIRequestContextFetchLogResult = tObject({
log: tArray(tString)
});
scheme.APIRequestContextStorageStateParams = tObject({
indexedDB: tOptional(tBoolean)
});
scheme.APIRequestContextStorageStateResult = tObject({
cookies: tArray(tType("NetworkCookie")),
origins: tArray(tType("OriginStorage"))
});
scheme.APIRequestContextDisposeAPIResponseParams = tObject({
fetchUid: tString
});
scheme.APIRequestContextDisposeAPIResponseResult = tOptional(tObject({}));
scheme.APIRequestContextDisposeParams = tObject({
reason: tOptional(tString)
});
scheme.APIRequestContextDisposeResult = tOptional(tObject({}));
scheme.APIResponse = tObject({
fetchUid: tString,
url: tString,
status: tInt,
statusText: tString,
headers: tArray(tType("NameValue"))
});
scheme.ArtifactInitializer = tObject({
absolutePath: tString
});
scheme.ArtifactPathAfterFinishedParams = tOptional(tObject({}));
scheme.ArtifactPathAfterFinishedResult = tObject({
value: tString
});
scheme.ArtifactSaveAsParams = tObject({
path: tString
});
scheme.ArtifactSaveAsResult = tOptional(tObject({}));
scheme.ArtifactSaveAsStreamParams = tOptional(tObject({}));
scheme.ArtifactSaveAsStreamResult = tObject({
stream: tChannel(["Stream"])
});
scheme.ArtifactFailureParams = tOptional(tObject({}));
scheme.ArtifactFailureResult = tObject({
error: tOptional(tString)
});
scheme.ArtifactStreamParams = tOptional(tObject({}));
scheme.ArtifactStreamResult = tObject({
stream: tChannel(["Stream"])
});
scheme.ArtifactCancelParams = tOptional(tObject({}));
scheme.ArtifactCancelResult = tOptional(tObject({}));
scheme.ArtifactDeleteParams = tOptional(tObject({}));
scheme.ArtifactDeleteResult = tOptional(tObject({}));
scheme.StreamInitializer = tOptional(tObject({}));
scheme.StreamReadParams = tObject({
size: tOptional(tInt)
});
scheme.StreamReadResult = tObject({
binary: tBinary
});
scheme.StreamCloseParams = tOptional(tObject({}));
scheme.StreamCloseResult = tOptional(tObject({}));
scheme.WritableStreamInitializer = tOptional(tObject({}));
scheme.WritableStreamWriteParams = tObject({
binary: tBinary
});
scheme.WritableStreamWriteResult = tOptional(tObject({}));
scheme.WritableStreamCloseParams = tOptional(tObject({}));
scheme.WritableStreamCloseResult = tOptional(tObject({}));
scheme.BrowserInitializer = tObject({
version: tString,
name: tString,
browserName: tEnum(["chromium", "firefox", "webkit"])
});
scheme.BrowserContextEvent = tObject({
context: tChannel(["BrowserContext"])
});
scheme.BrowserCloseEvent = tOptional(tObject({}));
scheme.BrowserStartServerParams = tObject({
title: tString,
workspaceDir: tOptional(tString),
metadata: tOptional(tAny),
host: tOptional(tString),
port: tOptional(tInt)
});
scheme.BrowserStartServerResult = tObject({
endpoint: tString
});
scheme.BrowserStopServerParams = tOptional(tObject({}));
scheme.BrowserStopServerResult = tOptional(tObject({}));
scheme.BrowserCloseParams = tObject({
reason: tOptional(tString)
});
scheme.BrowserCloseResult = tOptional(tObject({}));
scheme.BrowserKillForTestsParams = tOptional(tObject({}));
scheme.BrowserKillForTestsResult = tOptional(tObject({}));
scheme.BrowserDefaultUserAgentForTestParams = tOptional(tObject({}));
scheme.BrowserDefaultUserAgentForTestResult = tObject({
userAgent: tString
});
scheme.BrowserNewContextParams = tObject({
noDefaultViewport: tOptional(tBoolean),
viewport: tOptional(tObject({
width: tInt,
height: tInt
})),
screen: tOptional(tObject({
width: tInt,
height: tInt
})),
ignoreHTTPSErrors: tOptional(tBoolean),
clientCertificates: tOptional(tArray(tObject({
origin: tString,
cert: tOptional(tBinary),
key: tOptional(tBinary),
passphrase: tOptional(tString),
pfx: tOptional(tBinary)
}))),
javaScriptEnabled: tOptional(tBoolean),
bypassCSP: tOptional(tBoolean),
userAgent: tOptional(tString),
locale: tOptional(tString),
timezoneId: tOptional(tString),
geolocation: tOptional(tObject({
longitude: tFloat,
latitude: tFloat,
accuracy: tOptional(tFloat)
})),
permissions: tOptional(tArray(tString)),
extraHTTPHeaders: tOptional(tArray(tType("NameValue"))),
offline: tOptional(tBoolean),
httpCredentials: tOptional(tObject({
username: tString,
password: tString,
origin: tOptional(tString),
send: tOptional(tEnum(["always", "unauthorized"]))
})),
deviceScaleFactor: tOptional(tFloat),
isMobile: tOptional(tBoolean),
hasTouch: tOptional(tBoolean),
colorScheme: tOptional(tEnum(["dark", "light", "no-preference", "no-override"])),
reducedMotion: tOptional(tEnum(["reduce", "no-preference", "no-override"])),
forcedColors: tOptional(tEnum(["active", "none", "no-override"])),
acceptDownloads: tOptional(tEnum(["accept", "deny", "internal-browser-default"])),
contrast: tOptional(tEnum(["no-preference", "more", "no-override"])),
baseURL: tOptional(tString),
recordVideo: tOptional(tObject({
dir: tOptional(tString),
size: tOptional(tObject({
width: tInt,
height: tInt
})),
showActions: tOptional(tObject({
duration: tOptional(tFloat),
position: tOptional(tEnum(["top-left", "top", "top-right", "bottom-left", "bottom", "bottom-right"])),
fontSize: tOptional(tInt)
}))
})),
strictSelectors: tOptional(tBoolean),
serviceWorkers: tOptional(tEnum(["allow", "block"])),
selectorEngines: tOptional(tArray(tType("SelectorEngine"))),
testIdAttributeName: tOptional(tString),
proxy: tOptional(tObject({
server: tString,
bypass: tOptional(tString),
username: tOptional(tString),
password: tOptional(tString)
})),
storageState: tOptional(tObject({
cookies: tOptional(tArray(tType("SetNetworkCookie"))),
origins: tOptional(tArray(tType("SetOriginStorage")))
}))
});
scheme.BrowserNewContextResult = tObject({
context: tChannel(["BrowserContext"])
});
scheme.BrowserNewContextForReuseParams = tObject({
noDefaultViewport: tOptional(tBoolean),
viewport: tOptional(tObject({
width: tInt,
height: tInt
})),
screen: tOptional(tObject({
width: tInt,
height: tInt
})),
ignoreHTTPSErrors: tOptional(tBoolean),
clientCertificates: tOptional(tArray(tObject({
origin: tString,
cert: tOptional(tBinary),
key: tOptional(tBinary),
passphrase: tOptional(tString),
pfx: tOptional(tBinary)
}))),
javaScriptEnabled: tOptional(tBoolean),
bypassCSP: tOptional(tBoolean),
userAgent: tOptional(tString),
locale: tOptional(tString),
timezoneId: tOptional(tString),
geolocation: tOptional(tObject({
longitude: tFloat,
latitude: tFloat,
accuracy: tOptional(tFloat)
})),
permissions: tOptional(tArray(tString)),
extraHTTPHeaders: tOptional(tArray(tType("NameValue"))),
offline: tOptional(tBoolean),
httpCredentials: tOptional(tObject({
username: tString,
password: tString,
origin: tOptional(tString),
send: tOptional(tEnum(["always", "unauthorized"]))
})),
deviceScaleFactor: tOptional(tFloat),
isMobile: tOptional(tBoolean),
hasTouch: tOptional(tBoolean),
colorScheme: tOptional(tEnum(["dark", "light", "no-preference", "no-override"])),
reducedMotion: tOptional(tEnum(["reduce", "no-preference", "no-override"])),
forcedColors: tOptional(tEnum(["active", "none", "no-override"])),
acceptDownloads: tOptional(tEnum(["accept", "deny", "internal-browser-default"])),
contrast: tOptional(tEnum(["no-preference", "more", "no-override"])),
baseURL: tOptional(tString),
recordVideo: tOptional(tObject({
dir: tOptional(tString),
size: tOptional(tObject({
width: tInt,
height: tInt
})),
showActions: tOptional(tObject({
duration: tOptional(tFloat),
position: tOptional(tEnum(["top-left", "top", "top-right", "bottom-left", "bottom", "bottom-right"])),
fontSize: tOptional(tInt)
}))
})),
strictSelectors: tOptional(tBoolean),
serviceWorkers: tOptional(tEnum(["allow", "block"])),
selectorEngines: tOptional(tArray(tType("SelectorEngine"))),
testIdAttributeName: tOptional(tString),
proxy: tOptional(tObject({
server: tString,
bypass: tOptional(tString),
username: tOptional(tString),
password: tOptional(tString)
})),
storageState: tOptional(tObject({
cookies: tOptional(tArray(tType("SetNetworkCookie"))),
origins: tOptional(tArray(tType("SetOriginStorage")))
}))
});
scheme.BrowserNewContextForReuseResult = tObject({
context: tChannel(["BrowserContext"])
});
scheme.BrowserDisconnectFromReusedContextParams = tObject({
reason: tString
});
scheme.BrowserDisconnectFromReusedContextResult = tOptional(tObject({}));
scheme.BrowserNewBrowserCDPSessionParams = tOptional(tObject({}));
scheme.BrowserNewBrowserCDPSessionResult = tObject({
session: tChannel(["CDPSession"])
});
scheme.BrowserStartTracingParams = tObject({
page: tOptional(tChannel(["Page"])),
screenshots: tOptional(tBoolean),
categories: tOptional(tArray(tString))
});
scheme.BrowserStartTracingResult = tOptional(tObject({}));
scheme.BrowserStopTracingParams = tOptional(tObject({}));
scheme.BrowserStopTracingResult = tObject({
artifact: tChannel(["Artifact"])
});
scheme.BrowserContextInitializer = tObject({
debugger: tChannel(["Debugger"]),
requestContext: tChannel(["APIRequestContext"]),
tracing: tChannel(["Tracing"]),
options: tObject({
noDefaultViewport: tOptional(tBoolean),
viewport: tOptional(tObject({
width: tInt,
height: tInt
})),
screen: tOptional(tObject({
width: tInt,
height: tInt
})),
ignoreHTTPSErrors: tOptional(tBoolean),
clientCertificates: tOptional(tArray(tObject({
origin: tString,
cert: tOptional(tBinary),
key: tOptional(tBinary),
passphrase: tOptional(tString),
pfx: tOptional(tBinary)
}))),
javaScriptEnabled: tOptional(tBoolean),
bypassCSP: tOptional(tBoolean),
userAgent: tOptional(tString),
locale: tOptional(tString),
timezoneId: tOptional(tString),
geolocation: tOptional(tObject({
longitude: tFloat,
latitude: tFloat,
accuracy: tOptional(tFloat)
})),
permissions: tOptional(tArray(tString)),
extraHTTPHeaders: tOptional(tArray(tType("NameValue"))),
offline: tOptional(tBoolean),
httpCredentials: tOptional(tObject({
username: tString,
password: tString,
origin: tOptional(tString),
send: tOptional(tEnum(["always", "unauthorized"]))
})),
deviceScaleFactor: tOptional(tFloat),
isMobile: tOptional(tBoolean),
hasTouch: tOptional(tBoolean),
colorScheme: tOptional(tEnum(["dark", "light", "no-preference", "no-override"])),
reducedMotion: tOptional(tEnum(["reduce", "no-preference", "no-override"])),
forcedColors: tOptional(tEnum(["active", "none", "no-override"])),
acceptDownloads: tOptional(tEnum(["accept", "deny", "internal-browser-default"])),
contrast: tOptional(tEnum(["no-preference", "more", "no-override"])),
baseURL: tOptional(tString),
recordVideo: tOptional(tObject({
dir: tOptional(tString),
size: tOptional(tObject({
width: tInt,
height: tInt
})),
showActions: tOptional(tObject({
duration: tOptional(tFloat),
position: tOptional(tEnum(["top-left", "top", "top-right", "bottom-left", "bottom", "bottom-right"])),
fontSize: tOptional(tInt)
}))
})),
strictSelectors: tOptional(tBoolean),
serviceWorkers: tOptional(tEnum(["allow", "block"])),
selectorEngines: tOptional(tArray(tType("SelectorEngine"))),
testIdAttributeName: tOptional(tString)
})
});
scheme.BrowserContextBindingCallEvent = tObject({
binding: tChannel(["BindingCall"])
});
scheme.BrowserContextConsoleEvent = tObject({
type: tString,
text: tString,
args: tArray(tChannel(["ElementHandle", "JSHandle"])),
location: tObject({
url: tString,
lineNumber: tInt,
columnNumber: tInt
}),
timestamp: tFloat,
page: tOptional(tChannel(["Page"])),
worker: tOptional(tChannel(["Worker"]))
});
scheme.BrowserContextCloseEvent = tOptional(tObject({}));
scheme.BrowserContextDialogEvent = tObject({
dialog: tChannel(["Dialog"])
});
scheme.BrowserContextPageEvent = tObject({
page: tChannel(["Page"])
});
scheme.BrowserContextPageErrorEvent = tObject({
error: tType("SerializedError"),
page: tChannel(["Page"]),
location: tObject({
url: tString,
line: tInt,
column: tInt
})
});
scheme.BrowserContextRouteEvent = tObject({
route: tChannel(["Route"])
});
scheme.BrowserContextWebSocketRouteEvent = tObject({
webSocketRoute: tChannel(["WebSocketRoute"])
});
scheme.BrowserContextServiceWorkerEvent = tObject({
worker: tChannel(["Worker"])
});
scheme.BrowserContextRequestEvent = tObject({
request: tChannel(["Request"]),
page: tOptional(tChannel(["Page"]))
});
scheme.BrowserContextRequestFailedEvent = tObject({
request: tChannel(["Request"]),
failureText: tOptional(tString),
responseEndTiming: tFloat,
page: tOptional(tChannel(["Page"]))
});
scheme.BrowserContextRequestFinishedEvent = tObject({
request: tChannel(["Request"]),
response: tOptional(tChannel(["Response"])),
responseEndTiming: tFloat,
page: tOptional(tChannel(["Page"]))
});
scheme.BrowserContextResponseEvent = tObject({
response: tChannel(["Response"]),
page: tOptional(tChannel(["Page"]))
});
scheme.BrowserContextRecorderEventEvent = tObject({
event: tEnum(["actionAdded", "actionUpdated", "signalAdded"]),
data: tAny,
page: tChannel(["Page"]),
code: tString
});
scheme.BrowserContextAddCookiesParams = tObject({
cookies: tArray(tType("SetNetworkCookie"))
});
scheme.BrowserContextAddCookiesResult = tOptional(tObject({}));
scheme.BrowserContextAddInitScriptParams = tObject({
source: tString
});
scheme.BrowserContextAddInitScriptResult = tObject({
disposable: tChannel(["Disposable"])
});
scheme.BrowserContextClearCookiesParams = tObject({
name: tOptional(tString),
nameRegexSource: tOptional(tString),
nameRegexFlags: tOptional(tString),
domain: tOptional(tString),
domainRegexSource: tOptional(tString),
domainRegexFlags: tOptional(tString),
path: tOptional(tString),
pathRegexSource: tOptional(tString),
pathRegexFlags: tOptional(tString)
});
scheme.BrowserContextClearCookiesResult = tOptional(tObject({}));
scheme.BrowserContextClearPermissionsParams = tOptional(tObject({}));
scheme.BrowserContextClearPermissionsResult = tOptional(tObject({}));
scheme.BrowserContextCloseParams = tObject({
reason: tOptional(tString)
});
scheme.BrowserContextCloseResult = tOptional(tObject({}));
scheme.BrowserContextCookiesParams = tObject({
urls: tArray(tString)
});
scheme.BrowserContextCookiesResult = tObject({
cookies: tArray(tType("NetworkCookie"))
});
scheme.BrowserContextExposeBindingParams = tObject({
name: tString
});
scheme.BrowserContextExposeBindingResult = tObject({
disposable: tChannel(["Disposable"])
});
scheme.BrowserContextGrantPermissionsParams = tObject({
permissions: tArray(tString),
origin: tOptional(tString)
});
scheme.BrowserContextGrantPermissionsResult = tOptional(tObject({}));
scheme.BrowserContextNewPageParams = tOptional(tObject({}));
scheme.BrowserContextNewPageResult = tObject({
page: tChannel(["Page"])
});
scheme.BrowserContextRegisterSelectorEngineParams = tObject({
selectorEngine: tType("SelectorEngine")
});
scheme.BrowserContextRegisterSelectorEngineResult = tOptional(tObject({}));
scheme.BrowserContextSetTestIdAttributeNameParams = tObject({
testIdAttributeName: tString
});
scheme.BrowserContextSetTestIdAttributeNameResult = tOptional(tObject({}));
scheme.BrowserContextSetExtraHTTPHeadersParams = tObject({
headers: tArray(tType("NameValue"))
});
scheme.BrowserContextSetExtraHTTPHeadersResult = tOptional(tObject({}));
scheme.BrowserContextSetGeolocationParams = tObject({
geolocation: tOptional(tObject({
longitude: tFloat,
latitude: tFloat,
accuracy: tOptional(tFloat)
}))
});
scheme.BrowserContextSetGeolocationResult = tOptional(tObject({}));
scheme.BrowserContextSetHTTPCredentialsParams = tObject({
httpCredentials: tOptional(tObject({
username: tString,
password: tString,
origin: tOptional(tString)
}))
});
scheme.BrowserContextSetHTTPCredentialsResult = tOptional(tObject({}));
scheme.BrowserContextSetNetworkInterceptionPatternsParams = tObject({
patterns: tArray(tObject({
glob: tOptional(tString),
regexSource: tOptional(tString),
regexFlags: tOptional(tString),
urlPattern: tOptional(tType("URLPattern"))
}))
});
scheme.BrowserContextSetNetworkInterceptionPatternsResult = tOptional(tObject({}));
scheme.BrowserContextSetWebSocketInterceptionPatternsParams = tObject({
patterns: tArray(tObject({
glob: tOptional(tString),
regexSource: tOptional(tString),
regexFlags: tOptional(tString),
urlPattern: tOptional(tType("URLPattern"))
}))
});
scheme.BrowserContextSetWebSocketInterceptionPatternsResult = tOptional(tObject({}));
scheme.BrowserContextSetOfflineParams = tObject({
offline: tBoolean
});
scheme.BrowserContextSetOfflineResult = tOptional(tObject({}));
scheme.BrowserContextStorageStateParams = tObject({
indexedDB: tOptional(tBoolean)
});
scheme.BrowserContextStorageStateResult = tObject({
cookies: tArray(tType("NetworkCookie")),
origins: tArray(tType("OriginStorage"))
});
scheme.BrowserContextSetStorageStateParams = tObject({
storageState: tOptional(tObject({
cookies: tOptional(tArray(tType("SetNetworkCookie"))),
origins: tOptional(tArray(tType("SetOriginStorage")))
}))
});
scheme.BrowserContextSetStorageStateResult = tOptional(tObject({}));
scheme.BrowserContextPauseParams = tOptional(tObject({}));
scheme.BrowserContextPauseResult = tOptional(tObject({}));
scheme.BrowserContextEnableRecorderParams = tObject({
language: tOptional(tString),
mode: tOptional(tEnum(["inspecting", "recording"])),
recorderMode: tOptional(tEnum(["default", "api"])),
pauseOnNextStatement: tOptional(tBoolean),
testIdAttributeName: tOptional(tString),
launchOptions: tOptional(tAny),
contextOptions: tOptional(tAny),
device: tOptional(tString),
saveStorage: tOptional(tString),
outputFile: tOptional(tString),
handleSIGINT: tOptional(tBoolean),
omitCallTracking: tOptional(tBoolean)
});
scheme.BrowserContextEnableRecorderResult = tOptional(tObject({}));
scheme.BrowserContextDisableRecorderParams = tOptional(tObject({}));
scheme.BrowserContextDisableRecorderResult = tOptional(tObject({}));
scheme.BrowserContextExposeConsoleApiParams = tOptional(tObject({}));
scheme.BrowserContextExposeConsoleApiResult = tOptional(tObject({}));
scheme.BrowserContextNewCDPSessionParams = tObject({
page: tOptional(tChannel(["Page"])),
frame: tOptional(tChannel(["Frame"]))
});
scheme.BrowserContextNewCDPSessionResult = tObject({
session: tChannel(["CDPSession"])
});
scheme.BrowserContextCreateTempFilesParams = tObject({
rootDirName: tOptional(tString),
items: tArray(tObject({
name: tString,
lastModifiedMs: tOptional(tFloat)
}))
});
scheme.BrowserContextCreateTempFilesResult = tObject({
rootDir: tOptional(tChannel(["WritableStream"])),
writableStreams: tArray(tChannel(["WritableStream"]))
});
scheme.BrowserContextUpdateSubscriptionParams = tObject({
event: tEnum(["console", "dialog", "request", "response", "requestFinished", "requestFailed"]),
enabled: tBoolean
});
scheme.BrowserContextUpdateSubscriptionResult = tOptional(tObject({}));
scheme.BrowserContextClockFastForwardParams = tObject({
ticksNumber: tOptional(tFloat),
ticksString: tOptional(tString)
});
scheme.BrowserContextClockFastForwardResult = tOptional(tObject({}));
scheme.BrowserContextClockInstallParams = tObject({
timeNumber: tOptional(tFloat),
timeString: tOptional(tString)
});
scheme.BrowserContextClockInstallResult = tOptional(tObject({}));
scheme.BrowserContextClockPauseAtParams = tObject({
timeNumber: tOptional(tFloat),
timeString: tOptional(tString)
});
scheme.BrowserContextClockPauseAtResult = tOptional(tObject({}));
scheme.BrowserContextClockResumeParams = tOptional(tObject({}));
scheme.BrowserContextClockResumeResult = tOptional(tObject({}));
scheme.BrowserContextClockRunForParams = tObject({
ticksNumber: tOptional(tFloat),
ticksString: tOptional(tString)
});
scheme.BrowserContextClockRunForResult = tOptional(tObject({}));
scheme.BrowserContextClockSetFixedTimeParams = tObject({
timeNumber: tOptional(tFloat),
timeString: tOptional(tString)
});
scheme.BrowserContextClockSetFixedTimeResult = tOptional(tObject({}));
scheme.BrowserContextClockSetSystemTimeParams = tObject({
timeNumber: tOptional(tFloat),
timeString: tOptional(tString)
});
scheme.BrowserContextClockSetSystemTimeResult = tOptional(tObject({}));
scheme.BrowserTypeInitializer = tObject({
executablePath: tString,
name: tString
});
scheme.BrowserTypeLaunchParams = tObject({
channel: tOptional(tString),
executablePath: tOptional(tString),
args: tOptional(tArray(tString)),
ignoreAllDefaultArgs: tOptional(tBoolean),
ignoreDefaultArgs: tOptional(tArray(tString)),
handleSIGINT: tOptional(tBoolean),
handleSIGTERM: tOptional(tBoolean),
handleSIGHUP: tOptional(tBoolean),
timeout: tFloat,
env: tOptional(tArray(tType("NameValue"))),
headless: tOptional(tBoolean),
proxy: tOptional(tObject({
server: tString,
bypass: tOptional(tString),
username: tOptional(tString),
password: tOptional(tString)
})),
downloadsPath: tOptional(tString),
tracesDir: tOptional(tString),
artifactsDir: tOptional(tString),
chromiumSandbox: tOptional(tBoolean),
firefoxUserPrefs: tOptional(tAny),
cdpPort: tOptional(tInt),
slowMo: tOptional(tFloat)
});
scheme.BrowserTypeLaunchResult = tObject({
browser: tChannel(["Browser"])
});
scheme.BrowserTypeLaunchPersistentContextParams = tObject({
channel: tOptional(tString),
executablePath: tOptional(tString),
args: tOptional(tArray(tString)),
ignoreAllDefaultArgs: tOptional(tBoolean),
ignoreDefaultArgs: tOptional(tArray(tString)),
handleSIGINT: tOptional(tBoolean),
handleSIGTERM: tOptional(tBoolean),
handleSIGHUP: tOptional(tBoolean),
timeout: tFloat,
env: tOptional(tArray(tType("NameValue"))),
headless: tOptional(tBoolean),
proxy: tOptional(tObject({
server: tString,
bypass: tOptional(tString),
username: tOptional(tString),
password: tOptional(tString)
})),
downloadsPath: tOptional(tString),
tracesDir: tOptional(tString),
artifactsDir: tOptional(tString),
chromiumSandbox: tOptional(tBoolean),
firefoxUserPrefs: tOptional(tAny),
cdpPort: tOptional(tInt),
noDefaultViewport: tOptional(tBoolean),
viewport: tOptional(tObject({
width: tInt,
height: tInt
})),
screen: tOptional(tObject({
width: tInt,
height: tInt
})),
ignoreHTTPSErrors: tOptional(tBoolean),
clientCertificates: tOptional(tArray(tObject({
origin: tString,
cert: tOptional(tBinary),
key: tOptional(tBinary),
passphrase: tOptional(tString),
pfx: tOptional(tBinary)
}))),
javaScriptEnabled: tOptional(tBoolean),
bypassCSP: tOptional(tBoolean),
userAgent: tOptional(tString),
locale: tOptional(tString),
timezoneId: tOptional(tString),
geolocation: tOptional(tObject({
longitude: tFloat,
latitude: tFloat,
accuracy: tOptional(tFloat)
})),
permissions: tOptional(tArray(tString)),
extraHTTPHeaders: tOptional(tArray(tType("NameValue"))),
offline: tOptional(tBoolean),
httpCredentials: tOptional(tObject({
username: tString,
password: tString,
origin: tOptional(tString),
send: tOptional(tEnum(["always", "unauthorized"]))
})),
deviceScaleFactor: tOptional(tFloat),
isMobile: tOptional(tBoolean),
hasTouch: tOptional(tBoolean),
colorScheme: tOptional(tEnum(["dark", "light", "no-preference", "no-override"])),
reducedMotion: tOptional(tEnum(["reduce", "no-preference", "no-override"])),
forcedColors: tOptional(tEnum(["active", "none", "no-override"])),
acceptDownloads: tOptional(tEnum(["accept", "deny", "internal-browser-default"])),
contrast: tOptional(tEnum(["no-preference", "more", "no-override"])),
baseURL: tOptional(tString),
recordVideo: tOptional(tObject({
dir: tOptional(tString),
size: tOptional(tObject({
width: tInt,
height: tInt
})),
showActions: tOptional(tObject({
duration: tOptional(tFloat),
position: tOptional(tEnum(["top-left", "top", "top-right", "bottom-left", "bottom", "bottom-right"])),
fontSize: tOptional(tInt)
}))
})),
strictSelectors: tOptional(tBoolean),
serviceWorkers: tOptional(tEnum(["allow", "block"])),
selectorEngines: tOptional(tArray(tType("SelectorEngine"))),
testIdAttributeName: tOptional(tString),
userDataDir: tString,
slowMo: tOptional(tFloat)
});
scheme.BrowserTypeLaunchPersistentContextResult = tObject({
browser: tChannel(["Browser"]),
context: tChannel(["BrowserContext"])
});
scheme.BrowserTypeConnectOverCDPParams = tObject({
endpointURL: tString,
headers: tOptional(tArray(tType("NameValue"))),
slowMo: tOptional(tFloat),
timeout: tFloat,
isLocal: tOptional(tBoolean),
noDefaults: tOptional(tBoolean)
});
scheme.BrowserTypeConnectOverCDPResult = tObject({
browser: tChannel(["Browser"]),
defaultContext: tOptional(tChannel(["BrowserContext"]))
});
scheme.BrowserTypeConnectToWorkerParams = tObject({
endpoint: tString,
timeout: tFloat
});
scheme.BrowserTypeConnectToWorkerResult = tObject({
worker: tChannel(["Worker"])
});
scheme.Metadata = tObject({
location: tOptional(tObject({
file: tString,
line: tOptional(tInt),
column: tOptional(tInt)
})),
title: tOptional(tString),
internal: tOptional(tBoolean),
stepId: tOptional(tString)
});
scheme.ClientSideCallMetadata = tObject({
id: tInt,
stack: tOptional(tArray(tType("StackFrame")))
});
scheme.SDKLanguage = tEnum(["javascript", "python", "java", "csharp"]);
scheme.DisposableInitializer = tOptional(tObject({}));
scheme.DisposableDisposeParams = tOptional(tObject({}));
scheme.DisposableDisposeResult = tOptional(tObject({}));
scheme.EventTargetInitializer = tOptional(tObject({}));
scheme.EventTargetWaitForEventInfoParams = tObject({
info: tObject({
waitId: tString,
phase: tEnum(["before", "after", "log"]),
event: tOptional(tString),
message: tOptional(tString),
error: tOptional(tString)
})
});
scheme.AndroidDeviceWaitForEventInfoParams = tType("EventTargetWaitForEventInfoParams");
scheme.BrowserContextWaitForEventInfoParams = tType("EventTargetWaitForEventInfoParams");
scheme.ElectronApplicationWaitForEventInfoParams = tType("EventTargetWaitForEventInfoParams");
scheme.WebSocketWaitForEventInfoParams = tType("EventTargetWaitForEventInfoParams");
scheme.PageWaitForEventInfoParams = tType("EventTargetWaitForEventInfoParams");
scheme.DebuggerWaitForEventInfoParams = tType("EventTargetWaitForEventInfoParams");
scheme.WorkerWaitForEventInfoParams = tType("EventTargetWaitForEventInfoParams");
scheme.EventTargetWaitForEventInfoResult = tOptional(tObject({}));
scheme.AndroidDeviceWaitForEventInfoResult = tType("EventTargetWaitForEventInfoResult");
scheme.BrowserContextWaitForEventInfoResult = tType("EventTargetWaitForEventInfoResult");
scheme.ElectronApplicationWaitForEventInfoResult = tType("EventTargetWaitForEventInfoResult");
scheme.WebSocketWaitForEventInfoResult = tType("EventTargetWaitForEventInfoResult");
scheme.PageWaitForEventInfoResult = tType("EventTargetWaitForEventInfoResult");
scheme.DebuggerWaitForEventInfoResult = tType("EventTargetWaitForEventInfoResult");
scheme.WorkerWaitForEventInfoResult = tType("EventTargetWaitForEventInfoResult");
scheme.ElectronInitializer = tOptional(tObject({}));
scheme.ElectronLaunchParams = tObject({
executablePath: tOptional(tString),
args: tOptional(tArray(tString)),
chromiumSandbox: tOptional(tBoolean),
cwd: tOptional(tString),
env: tOptional(tArray(tType("NameValue"))),
timeout: tFloat,
acceptDownloads: tOptional(tEnum(["accept", "deny", "internal-browser-default"])),
bypassCSP: tOptional(tBoolean),
colorScheme: tOptional(tEnum(["dark", "light", "no-preference", "no-override"])),
extraHTTPHeaders: tOptional(tArray(tType("NameValue"))),
geolocation: tOptional(tObject({
longitude: tFloat,
latitude: tFloat,
accuracy: tOptional(tFloat)
})),
httpCredentials: tOptional(tObject({
username: tString,
password: tString,
origin: tOptional(tString)
})),
ignoreHTTPSErrors: tOptional(tBoolean),
locale: tOptional(tString),
offline: tOptional(tBoolean),
recordVideo: tOptional(tObject({
dir: tOptional(tString),
size: tOptional(tObject({
width: tInt,
height: tInt
})),
showActions: tOptional(tObject({
duration: tOptional(tFloat),
position: tOptional(tEnum(["top-left", "top", "top-right", "bottom-left", "bottom", "bottom-right"])),
fontSize: tOptional(tInt)
}))
})),
strictSelectors: tOptional(tBoolean),
timezoneId: tOptional(tString),
tracesDir: tOptional(tString),
artifactsDir: tOptional(tString),
selectorEngines: tOptional(tArray(tType("SelectorEngine"))),
testIdAttributeName: tOptional(tString)
});
scheme.ElectronLaunchResult = tObject({
electronApplication: tChannel(["ElectronApplication"])
});
scheme.ElectronApplicationInitializer = tObject({
context: tChannel(["BrowserContext"])
});
scheme.ElectronApplicationCloseEvent = tOptional(tObject({}));
scheme.ElectronApplicationConsoleEvent = tObject({
type: tString,
text: tString,
args: tArray(tChannel(["ElementHandle", "JSHandle"])),
location: tObject({
url: tString,
lineNumber: tInt,
columnNumber: tInt
}),
timestamp: tFloat
});
scheme.ElectronApplicationBrowserWindowParams = tObject({
page: tChannel(["Page"])
});
scheme.ElectronApplicationBrowserWindowResult = tObject({
handle: tChannel(["ElementHandle", "JSHandle"])
});
scheme.ElectronApplicationEvaluateExpressionParams = tObject({
expression: tString,
isFunction: tOptional(tBoolean),
arg: tType("SerializedArgument")
});
scheme.ElectronApplicationEvaluateExpressionResult = tObject({
value: tType("SerializedValue")
});
scheme.ElectronApplicationEvaluateExpressionHandleParams = tObject({
expression: tString,
isFunction: tOptional(tBoolean),
arg: tType("SerializedArgument")
});
scheme.ElectronApplicationEvaluateExpressionHandleResult = tObject({
handle: tChannel(["ElementHandle", "JSHandle"])
});
scheme.ElectronApplicationUpdateSubscriptionParams = tObject({
event: tEnum(["console"]),
enabled: tBoolean
});
scheme.ElectronApplicationUpdateSubscriptionResult = tOptional(tObject({}));
scheme.FrameInitializer = tObject({
url: tString,
name: tString,
parentFrame: tOptional(tChannel(["Frame"])),
loadStates: tArray(tType("LifecycleEvent"))
});
scheme.FrameLoadstateEvent = tObject({
add: tOptional(tType("LifecycleEvent")),
remove: tOptional(tType("LifecycleEvent"))
});
scheme.FrameNavigatedEvent = tObject({
url: tString,
name: tString,
newDocument: tOptional(tObject({
request: tOptional(tChannel(["Request"]))
})),
error: tOptional(tString)
});
scheme.FrameEvalOnSelectorParams = tObject({
selector: tString,
strict: tOptional(tBoolean),
expression: tString,
isFunction: tOptional(tBoolean),
arg: tType("SerializedArgument")
});
scheme.FrameEvalOnSelectorResult = tObject({
value: tType("SerializedValue")
});
scheme.FrameEvalOnSelectorAllParams = tObject({
selector: tString,
expression: tString,
isFunction: tOptional(tBoolean),
arg: tType("SerializedArgument")
});
scheme.FrameEvalOnSelectorAllResult = tObject({
value: tType("SerializedValue")
});
scheme.FrameAddScriptTagParams = tObject({
url: tOptional(tString),
content: tOptional(tString),
type: tOptional(tString)
});
scheme.FrameAddScriptTagResult = tObject({
element: tChannel(["ElementHandle"])
});
scheme.FrameAddStyleTagParams = tObject({
url: tOptional(tString),
content: tOptional(tString)
});
scheme.FrameAddStyleTagResult = tObject({
element: tChannel(["ElementHandle"])
});
scheme.FrameAriaSnapshotParams = tObject({
mode: tOptional(tEnum(["ai", "default"])),
track: tOptional(tString),
selector: tOptional(tString),
depth: tOptional(tInt),
boxes: tOptional(tBoolean),
timeout: tFloat
});
scheme.FrameAriaSnapshotResult = tObject({
snapshot: tString
});
scheme.FrameBlurParams = tObject({
selector: tString,
strict: tOptional(tBoolean),
timeout: tFloat
});
scheme.FrameBlurResult = tOptional(tObject({}));
scheme.FrameCheckParams = tObject({
selector: tString,
strict: tOptional(tBoolean),
force: tOptional(tBoolean),
position: tOptional(tType("Point")),
timeout: tFloat,
trial: tOptional(tBoolean)
});
scheme.FrameCheckResult = tOptional(tObject({}));
scheme.FrameClickParams = tObject({
selector: tString,
strict: tOptional(tBoolean),
force: tOptional(tBoolean),
noWaitAfter: tOptional(tBoolean),
modifiers: tOptional(tArray(tEnum(["Alt", "Control", "ControlOrMeta", "Meta", "Shift"]))),
position: tOptional(tType("Point")),
delay: tOptional(tFloat),
button: tOptional(tEnum(["left", "right", "middle"])),
clickCount: tOptional(tInt),
timeout: tFloat,
trial: tOptional(tBoolean),
steps: tOptional(tInt)
});
scheme.FrameClickResult = tOptional(tObject({}));
scheme.FrameContentParams = tOptional(tObject({}));
scheme.FrameContentResult = tObject({
value: tString
});
scheme.FrameDragAndDropParams = tObject({
source: tString,
target: tString,
force: tOptional(tBoolean),
timeout: tFloat,
trial: tOptional(tBoolean),
sourcePosition: tOptional(tType("Point")),
targetPosition: tOptional(tType("Point")),
strict: tOptional(tBoolean),
steps: tOptional(tInt)
});
scheme.FrameDragAndDropResult = tOptional(tObject({}));
scheme.FrameDropParams = tObject({
selector: tString,
strict: tOptional(tBoolean),
position: tOptional(tType("Point")),
payloads: tOptional(tArray(tObject({
name: tString,
mimeType: tOptional(tString),
buffer: tBinary
}))),
localPaths: tOptional(tArray(tString)),
streams: tOptional(tArray(tChannel(["WritableStream"]))),
data: tOptional(tArray(tObject({
mimeType: tString,
value: tString
}))),
timeout: tFloat
});
scheme.FrameDropResult = tOptional(tObject({}));
scheme.FrameDblclickParams = tObject({
selector: tString,
strict: tOptional(tBoolean),
force: tOptional(tBoolean),
modifiers: tOptional(tArray(tEnum(["Alt", "Control", "ControlOrMeta", "Meta", "Shift"]))),
position: tOptional(tType("Point")),
delay: tOptional(tFloat),
button: tOptional(tEnum(["left", "right", "middle"])),
timeout: tFloat,
trial: tOptional(tBoolean),
steps: tOptional(tInt)
});
scheme.FrameDblclickResult = tOptional(tObject({}));
scheme.FrameDispatchEventParams = tObject({
selector: tString,
strict: tOptional(tBoolean),
type: tString,
eventInit: tType("SerializedArgument"),
timeout: tFloat
});
scheme.FrameDispatchEventResult = tOptional(tObject({}));
scheme.FrameEvaluateExpressionParams = tObject({
expression: tString,
isFunction: tOptional(tBoolean),
arg: tType("SerializedArgument")
});
scheme.FrameEvaluateExpressionResult = tObject({
value: tType("SerializedValue")
});
scheme.FrameEvaluateExpressionHandleParams = tObject({
expression: tString,
isFunction: tOptional(tBoolean),
arg: tType("SerializedArgument")
});
scheme.FrameEvaluateExpressionHandleResult = tObject({
handle: tChannel(["ElementHandle", "JSHandle"])
});
scheme.FrameFillParams = tObject({
selector: tString,
strict: tOptional(tBoolean),
value: tString,
force: tOptional(tBoolean),
timeout: tFloat
});
scheme.FrameFillResult = tOptional(tObject({}));
scheme.FrameFocusParams = tObject({
selector: tString,
strict: tOptional(tBoolean),
timeout: tFloat
});
scheme.FrameFocusResult = tOptional(tObject({}));
scheme.FrameFrameElementParams = tOptional(tObject({}));
scheme.FrameFrameElementResult = tObject({
element: tChannel(["ElementHandle"])
});
scheme.FrameResolveSelectorParams = tObject({
selector: tString
});
scheme.FrameResolveSelectorResult = tObject({
resolvedSelector: tString
});
scheme.FrameHighlightParams = tObject({
selector: tString,
style: tOptional(tString)
});
scheme.FrameHighlightResult = tOptional(tObject({}));
scheme.FrameHideHighlightParams = tObject({
selector: tString
});
scheme.FrameHideHighlightResult = tOptional(tObject({}));
scheme.FrameGetAttributeParams = tObject({
selector: tString,
strict: tOptional(tBoolean),
name: tString,
timeout: tFloat
});
scheme.FrameGetAttributeResult = tObject({
value: tOptional(tString)
});
scheme.FrameGotoParams = tObject({
url: tString,
timeout: tFloat,
waitUntil: tOptional(tType("LifecycleEvent")),
referer: tOptional(tString)
});
scheme.FrameGotoResult = tObject({
response: tOptional(tChannel(["Response"]))
});
scheme.FrameHoverParams = tObject({
selector: tString,
strict: tOptional(tBoolean),
force: tOptional(tBoolean),
modifiers: tOptional(tArray(tEnum(["Alt", "Control", "ControlOrMeta", "Meta", "Shift"]))),
position: tOptional(tType("Point")),
timeout: tFloat,
trial: tOptional(tBoolean)
});
scheme.FrameHoverResult = tOptional(tObject({}));
scheme.FrameInnerHTMLParams = tObject({
selector: tString,
strict: tOptional(tBoolean),
timeout: tFloat
});
scheme.FrameInnerHTMLResult = tObject({
value: tString
});
scheme.FrameInnerTextParams = tObject({
selector: tString,
strict: tOptional(tBoolean),
timeout: tFloat
});
scheme.FrameInnerTextResult = tObject({
value: tString
});
scheme.FrameInputValueParams = tObject({
selector: tString,
strict: tOptional(tBoolean),
timeout: tFloat
});
scheme.FrameInputValueResult = tObject({
value: tString
});
scheme.FrameIsCheckedParams = tObject({
selector: tString,
strict: tOptional(tBoolean),
timeout: tFloat
});
scheme.FrameIsCheckedResult = tObject({
value: tBoolean
});
scheme.FrameIsDisabledParams = tObject({
selector: tString,
strict: tOptional(tBoolean),
timeout: tFloat
});
scheme.FrameIsDisabledResult = tObject({
value: tBoolean
});
scheme.FrameIsEnabledParams = tObject({
selector: tString,
strict: tOptional(tBoolean),
timeout: tFloat
});
scheme.FrameIsEnabledResult = tObject({
value: tBoolean
});
scheme.FrameIsHiddenParams = tObject({
selector: tString,
strict: tOptional(tBoolean)
});
scheme.FrameIsHiddenResult = tObject({
value: tBoolean
});
scheme.FrameIsVisibleParams = tObject({
selector: tString,
strict: tOptional(tBoolean)
});
scheme.FrameIsVisibleResult = tObject({
value: tBoolean
});
scheme.FrameIsEditableParams = tObject({
selector: tString,
strict: tOptional(tBoolean),
timeout: tFloat
});
scheme.FrameIsEditableResult = tObject({
value: tBoolean
});
scheme.FramePressParams = tObject({
selector: tString,
strict: tOptional(tBoolean),
key: tString,
delay: tOptional(tFloat),
noWaitAfter: tOptional(tBoolean),
timeout: tFloat
});
scheme.FramePressResult = tOptional(tObject({}));
scheme.FrameQuerySelectorParams = tObject({
selector: tString,
strict: tOptional(tBoolean)
});
scheme.FrameQuerySelectorResult = tObject({
element: tOptional(tChannel(["ElementHandle"]))
});
scheme.FrameQuerySelectorAllParams = tObject({
selector: tString
});
scheme.FrameQuerySelectorAllResult = tObject({
elements: tArray(tChannel(["ElementHandle"]))
});
scheme.FrameQueryCountParams = tObject({
selector: tString
});
scheme.FrameQueryCountResult = tObject({
value: tInt
});
scheme.FrameSelectOptionParams = tObject({
selector: tString,
strict: tOptional(tBoolean),
elements: tOptional(tArray(tChannel(["ElementHandle"]))),
options: tOptional(tArray(tObject({
valueOrLabel: tOptional(tString),
value: tOptional(tString),
label: tOptional(tString),
index: tOptional(tInt)
}))),
force: tOptional(tBoolean),
timeout: tFloat
});
scheme.FrameSelectOptionResult = tObject({
values: tArray(tString)
});
scheme.FrameSetContentParams = tObject({
html: tString,
timeout: tFloat,
waitUntil: tOptional(tType("LifecycleEvent"))
});
scheme.FrameSetContentResult = tOptional(tObject({}));
scheme.FrameSetInputFilesParams = tObject({
selector: tString,
strict: tOptional(tBoolean),
payloads: tOptional(tArray(tObject({
name: tString,
mimeType: tOptional(tString),
buffer: tBinary
}))),
localDirectory: tOptional(tString),
directoryStream: tOptional(tChannel(["WritableStream"])),
localPaths: tOptional(tArray(tString)),
streams: tOptional(tArray(tChannel(["WritableStream"]))),
timeout: tFloat
});
scheme.FrameSetInputFilesResult = tOptional(tObject({}));
scheme.FrameTapParams = tObject({
selector: tString,
strict: tOptional(tBoolean),
force: tOptional(tBoolean),
modifiers: tOptional(tArray(tEnum(["Alt", "Control", "ControlOrMeta", "Meta", "Shift"]))),
position: tOptional(tType("Point")),
timeout: tFloat,
trial: tOptional(tBoolean)
});
scheme.FrameTapResult = tOptional(tObject({}));
scheme.FrameTextContentParams = tObject({
selector: tString,
strict: tOptional(tBoolean),
timeout: tFloat
});
scheme.FrameTextContentResult = tObject({
value: tOptional(tString)
});
scheme.FrameTitleParams = tOptional(tObject({}));
scheme.FrameTitleResult = tObject({
value: tString
});
scheme.FrameTypeParams = tObject({
selector: tString,
strict: tOptional(tBoolean),
text: tString,
delay: tOptional(tFloat),
timeout: tFloat
});
scheme.FrameTypeResult = tOptional(tObject({}));
scheme.FrameUncheckParams = tObject({
selector: tString,
strict: tOptional(tBoolean),
force: tOptional(tBoolean),
position: tOptional(tType("Point")),
timeout: tFloat,
trial: tOptional(tBoolean)
});
scheme.FrameUncheckResult = tOptional(tObject({}));
scheme.FrameWaitForTimeoutParams = tObject({
waitTimeout: tFloat
});
scheme.FrameWaitForTimeoutResult = tOptional(tObject({}));
scheme.FrameWaitForFunctionParams = tObject({
expression: tString,
isFunction: tOptional(tBoolean),
arg: tType("SerializedArgument"),
timeout: tFloat,
pollingInterval: tOptional(tFloat)
});
scheme.FrameWaitForFunctionResult = tObject({
handle: tChannel(["ElementHandle", "JSHandle"])
});
scheme.FrameWaitForSelectorParams = tObject({
selector: tString,
strict: tOptional(tBoolean),
timeout: tFloat,
state: tOptional(tEnum(["attached", "detached", "visible", "hidden"])),
omitReturnValue: tOptional(tBoolean)
});
scheme.FrameWaitForSelectorResult = tObject({
element: tOptional(tChannel(["ElementHandle"]))
});
scheme.FrameExpectParams = tObject({
selector: tOptional(tString),
expression: tString,
expressionArg: tOptional(tAny),
pseudo: tOptional(tEnum(["before", "after"])),
expectedText: tOptional(tArray(tType("ExpectedTextValue"))),
expectedNumber: tOptional(tFloat),
expectedValue: tOptional(tType("SerializedArgument")),
useInnerText: tOptional(tBoolean),
isNot: tBoolean,
timeout: tFloat
});
scheme.FrameExpectResult = tObject({
matches: tBoolean,
received: tOptional(tObject({
value: tOptional(tType("SerializedValue")),
ariaSnapshot: tOptional(tString)
})),
timedOut: tOptional(tBoolean),
errorMessage: tOptional(tString),
log: tOptional(tArray(tString))
});
scheme.JSHandleInitializer = tObject({
preview: tString
});
scheme.JSHandlePreviewUpdatedEvent = tObject({
preview: tString
});
scheme.ElementHandlePreviewUpdatedEvent = tType("JSHandlePreviewUpdatedEvent");
scheme.JSHandleDisposeParams = tOptional(tObject({}));
scheme.ElementHandleDisposeParams = tType("JSHandleDisposeParams");
scheme.JSHandleDisposeResult = tOptional(tObject({}));
scheme.ElementHandleDisposeResult = tType("JSHandleDisposeResult");
scheme.JSHandleEvaluateExpressionParams = tObject({
expression: tString,
isFunction: tOptional(tBoolean),
arg: tType("SerializedArgument")
});
scheme.ElementHandleEvaluateExpressionParams = tType("JSHandleEvaluateExpressionParams");
scheme.JSHandleEvaluateExpressionResult = tObject({
value: tType("SerializedValue")
});
scheme.ElementHandleEvaluateExpressionResult = tType("JSHandleEvaluateExpressionResult");
scheme.JSHandleEvaluateExpressionHandleParams = tObject({
expression: tString,
isFunction: tOptional(tBoolean),
arg: tType("SerializedArgument")
});
scheme.ElementHandleEvaluateExpressionHandleParams = tType("JSHandleEvaluateExpressionHandleParams");
scheme.JSHandleEvaluateExpressionHandleResult = tObject({
handle: tChannel(["ElementHandle", "JSHandle"])
});
scheme.ElementHandleEvaluateExpressionHandleResult = tType("JSHandleEvaluateExpressionHandleResult");
scheme.JSHandleGetPropertyListParams = tOptional(tObject({}));
scheme.ElementHandleGetPropertyListParams = tType("JSHandleGetPropertyListParams");
scheme.JSHandleGetPropertyListResult = tObject({
properties: tArray(tObject({
name: tString,
value: tChannel(["ElementHandle", "JSHandle"])
}))
});
scheme.ElementHandleGetPropertyListResult = tType("JSHandleGetPropertyListResult");
scheme.JSHandleGetPropertyParams = tObject({
name: tString
});
scheme.ElementHandleGetPropertyParams = tType("JSHandleGetPropertyParams");
scheme.JSHandleGetPropertyResult = tObject({
handle: tChannel(["ElementHandle", "JSHandle"])
});
scheme.ElementHandleGetPropertyResult = tType("JSHandleGetPropertyResult");
scheme.JSHandleJsonValueParams = tOptional(tObject({}));
scheme.ElementHandleJsonValueParams = tType("JSHandleJsonValueParams");
scheme.JSHandleJsonValueResult = tObject({
value: tType("SerializedValue")
});
scheme.ElementHandleJsonValueResult = tType("JSHandleJsonValueResult");
scheme.ElementHandleInitializer = tObject({
preview: tString
});
scheme.ElementHandleEvalOnSelectorParams = tObject({
selector: tString,
strict: tOptional(tBoolean),
expression: tString,
isFunction: tOptional(tBoolean),
arg: tType("SerializedArgument")
});
scheme.ElementHandleEvalOnSelectorResult = tObject({
value: tType("SerializedValue")
});
scheme.ElementHandleEvalOnSelectorAllParams = tObject({
selector: tString,
expression: tString,
isFunction: tOptional(tBoolean),
arg: tType("SerializedArgument")
});
scheme.ElementHandleEvalOnSelectorAllResult = tObject({
value: tType("SerializedValue")
});
scheme.ElementHandleBoundingBoxParams = tOptional(tObject({}));
scheme.ElementHandleBoundingBoxResult = tObject({
value: tOptional(tType("Rect"))
});
scheme.ElementHandleCheckParams = tObject({
force: tOptional(tBoolean),
position: tOptional(tType("Point")),
timeout: tFloat,
trial: tOptional(tBoolean)
});
scheme.ElementHandleCheckResult = tOptional(tObject({}));
scheme.ElementHandleClickParams = tObject({
force: tOptional(tBoolean),
noWaitAfter: tOptional(tBoolean),
modifiers: tOptional(tArray(tEnum(["Alt", "Control", "ControlOrMeta", "Meta", "Shift"]))),
position: tOptional(tType("Point")),
delay: tOptional(tFloat),
button: tOptional(tEnum(["left", "right", "middle"])),
clickCount: tOptional(tInt),
timeout: tFloat,
trial: tOptional(tBoolean),
steps: tOptional(tInt)
});
scheme.ElementHandleClickResult = tOptional(tObject({}));
scheme.ElementHandleContentFrameParams = tOptional(tObject({}));
scheme.ElementHandleContentFrameResult = tObject({
frame: tOptional(tChannel(["Frame"]))
});
scheme.ElementHandleDblclickParams = tObject({
force: tOptional(tBoolean),
modifiers: tOptional(tArray(tEnum(["Alt", "Control", "ControlOrMeta", "Meta", "Shift"]))),
position: tOptional(tType("Point")),
delay: tOptional(tFloat),
button: tOptional(tEnum(["left", "right", "middle"])),
timeout: tFloat,
trial: tOptional(tBoolean),
steps: tOptional(tInt)
});
scheme.ElementHandleDblclickResult = tOptional(tObject({}));
scheme.ElementHandleDispatchEventParams = tObject({
type: tString,
eventInit: tType("SerializedArgument")
});
scheme.ElementHandleDispatchEventResult = tOptional(tObject({}));
scheme.ElementHandleFillParams = tObject({
value: tString,
force: tOptional(tBoolean),
timeout: tFloat
});
scheme.ElementHandleFillResult = tOptional(tObject({}));
scheme.ElementHandleFocusParams = tOptional(tObject({}));
scheme.ElementHandleFocusResult = tOptional(tObject({}));
scheme.ElementHandleGetAttributeParams = tObject({
name: tString
});
scheme.ElementHandleGetAttributeResult = tObject({
value: tOptional(tString)
});
scheme.ElementHandleHoverParams = tObject({
force: tOptional(tBoolean),
modifiers: tOptional(tArray(tEnum(["Alt", "Control", "ControlOrMeta", "Meta", "Shift"]))),
position: tOptional(tType("Point")),
timeout: tFloat,
trial: tOptional(tBoolean)
});
scheme.ElementHandleHoverResult = tOptional(tObject({}));
scheme.ElementHandleInnerHTMLParams = tOptional(tObject({}));
scheme.ElementHandleInnerHTMLResult = tObject({
value: tString
});
scheme.ElementHandleInnerTextParams = tOptional(tObject({}));
scheme.ElementHandleInnerTextResult = tObject({
value: tString
});
scheme.ElementHandleInputValueParams = tOptional(tObject({}));
scheme.ElementHandleInputValueResult = tObject({
value: tString
});
scheme.ElementHandleIsCheckedParams = tOptional(tObject({}));
scheme.ElementHandleIsCheckedResult = tObject({
value: tBoolean
});
scheme.ElementHandleIsDisabledParams = tOptional(tObject({}));
scheme.ElementHandleIsDisabledResult = tObject({
value: tBoolean
});
scheme.ElementHandleIsEditableParams = tOptional(tObject({}));
scheme.ElementHandleIsEditableResult = tObject({
value: tBoolean
});
scheme.ElementHandleIsEnabledParams = tOptional(tObject({}));
scheme.ElementHandleIsEnabledResult = tObject({
value: tBoolean
});
scheme.ElementHandleIsHiddenParams = tOptional(tObject({}));
scheme.ElementHandleIsHiddenResult = tObject({
value: tBoolean
});
scheme.ElementHandleIsVisibleParams = tOptional(tObject({}));
scheme.ElementHandleIsVisibleResult = tObject({
value: tBoolean
});
scheme.ElementHandleOwnerFrameParams = tOptional(tObject({}));
scheme.ElementHandleOwnerFrameResult = tObject({
frame: tOptional(tChannel(["Frame"]))
});
scheme.ElementHandlePressParams = tObject({
key: tString,
delay: tOptional(tFloat),
timeout: tFloat,
noWaitAfter: tOptional(tBoolean)
});
scheme.ElementHandlePressResult = tOptional(tObject({}));
scheme.ElementHandleQuerySelectorParams = tObject({
selector: tString,
strict: tOptional(tBoolean)
});
scheme.ElementHandleQuerySelectorResult = tObject({
element: tOptional(tChannel(["ElementHandle"]))
});
scheme.ElementHandleQuerySelectorAllParams = tObject({
selector: tString
});
scheme.ElementHandleQuerySelectorAllResult = tObject({
elements: tArray(tChannel(["ElementHandle"]))
});
scheme.ElementHandleScreenshotParams = tObject({
timeout: tFloat,
type: tOptional(tEnum(["png", "jpeg"])),
quality: tOptional(tInt),
omitBackground: tOptional(tBoolean),
caret: tOptional(tEnum(["hide", "initial"])),
animations: tOptional(tEnum(["disabled", "allow"])),
scale: tOptional(tEnum(["css", "device"])),
mask: tOptional(tArray(tObject({
frame: tChannel(["Frame"]),
selector: tString
}))),
maskColor: tOptional(tString),
style: tOptional(tString)
});
scheme.ElementHandleScreenshotResult = tObject({
binary: tBinary
});
scheme.ElementHandleScrollIntoViewIfNeededParams = tObject({
timeout: tFloat
});
scheme.ElementHandleScrollIntoViewIfNeededResult = tOptional(tObject({}));
scheme.ElementHandleSelectOptionParams = tObject({
elements: tOptional(tArray(tChannel(["ElementHandle"]))),
options: tOptional(tArray(tObject({
valueOrLabel: tOptional(tString),
value: tOptional(tString),
label: tOptional(tString),
index: tOptional(tInt)
}))),
force: tOptional(tBoolean),
timeout: tFloat
});
scheme.ElementHandleSelectOptionResult = tObject({
values: tArray(tString)
});
scheme.ElementHandleSelectTextParams = tObject({
force: tOptional(tBoolean),
timeout: tFloat
});
scheme.ElementHandleSelectTextResult = tOptional(tObject({}));
scheme.ElementHandleSetInputFilesParams = tObject({
payloads: tOptional(tArray(tObject({
name: tString,
mimeType: tOptional(tString),
buffer: tBinary
}))),
localDirectory: tOptional(tString),
directoryStream: tOptional(tChannel(["WritableStream"])),
localPaths: tOptional(tArray(tString)),
streams: tOptional(tArray(tChannel(["WritableStream"]))),
timeout: tFloat
});
scheme.ElementHandleSetInputFilesResult = tOptional(tObject({}));
scheme.ElementHandleTapParams = tObject({
force: tOptional(tBoolean),
modifiers: tOptional(tArray(tEnum(["Alt", "Control", "ControlOrMeta", "Meta", "Shift"]))),
position: tOptional(tType("Point")),
timeout: tFloat,
trial: tOptional(tBoolean)
});
scheme.ElementHandleTapResult = tOptional(tObject({}));
scheme.ElementHandleTextContentParams = tOptional(tObject({}));
scheme.ElementHandleTextContentResult = tObject({
value: tOptional(tString)
});
scheme.ElementHandleTypeParams = tObject({
text: tString,
delay: tOptional(tFloat),
timeout: tFloat
});
scheme.ElementHandleTypeResult = tOptional(tObject({}));
scheme.ElementHandleUncheckParams = tObject({
force: tOptional(tBoolean),
position: tOptional(tType("Point")),
timeout: tFloat,
trial: tOptional(tBoolean)
});
scheme.ElementHandleUncheckResult = tOptional(tObject({}));
scheme.ElementHandleWaitForElementStateParams = tObject({
state: tEnum(["visible", "hidden", "stable", "enabled", "disabled", "editable"]),
timeout: tFloat
});
scheme.ElementHandleWaitForElementStateResult = tOptional(tObject({}));
scheme.ElementHandleWaitForSelectorParams = tObject({
selector: tString,
strict: tOptional(tBoolean),
timeout: tFloat,
state: tOptional(tEnum(["attached", "detached", "visible", "hidden"]))
});
scheme.ElementHandleWaitForSelectorResult = tObject({
element: tOptional(tChannel(["ElementHandle"]))
});
scheme.LocalUtilsInitializer = tObject({
deviceDescriptors: tArray(tObject({
name: tString,
descriptor: tObject({
userAgent: tString,
viewport: tObject({
width: tInt,
height: tInt
}),
screen: tOptional(tObject({
width: tInt,
height: tInt
})),
deviceScaleFactor: tFloat,
isMobile: tBoolean,
hasTouch: tBoolean,
defaultBrowserType: tEnum(["chromium", "firefox", "webkit"])
})
}))
});
scheme.LocalUtilsZipParams = tObject({
zipFile: tString,
entries: tArray(tType("NameValue")),
stacksId: tOptional(tString),
mode: tEnum(["write", "append"]),
includeSources: tBoolean,
additionalSources: tOptional(tArray(tString))
});
scheme.LocalUtilsZipResult = tOptional(tObject({}));
scheme.LocalUtilsHarOpenParams = tObject({
file: tString
});
scheme.LocalUtilsHarOpenResult = tObject({
harId: tOptional(tString),
error: tOptional(tString)
});
scheme.LocalUtilsHarLookupParams = tObject({
harId: tString,
url: tString,
method: tString,
headers: tArray(tType("NameValue")),
postData: tOptional(tBinary),
isNavigationRequest: tBoolean
});
scheme.LocalUtilsHarLookupResult = tObject({
action: tEnum(["error", "redirect", "fulfill", "noentry"]),
message: tOptional(tString),
redirectURL: tOptional(tString),
status: tOptional(tInt),
headers: tOptional(tArray(tType("NameValue"))),
body: tOptional(tBinary)
});
scheme.LocalUtilsHarCloseParams = tObject({
harId: tString
});
scheme.LocalUtilsHarCloseResult = tOptional(tObject({}));
scheme.LocalUtilsHarUnzipParams = tObject({
zipFile: tString,
harFile: tString,
resourcesDir: tOptional(tString)
});
scheme.LocalUtilsHarUnzipResult = tOptional(tObject({}));
scheme.LocalUtilsConnectParams = tObject({
endpoint: tString,
headers: tOptional(tAny),
exposeNetwork: tOptional(tString),
slowMo: tOptional(tFloat),
timeout: tFloat,
socksProxyRedirectPortForTest: tOptional(tInt)
});
scheme.LocalUtilsConnectResult = tObject({
pipe: tChannel(["JsonPipe"]),
headers: tArray(tType("NameValue"))
});
scheme.LocalUtilsTracingStartedParams = tObject({
tracesDir: tOptional(tString),
traceName: tString,
live: tOptional(tBoolean)
});
scheme.LocalUtilsTracingStartedResult = tObject({
stacksId: tString
});
scheme.LocalUtilsAddStackToTracingNoReplyParams = tObject({
callData: tType("ClientSideCallMetadata")
});
scheme.LocalUtilsAddStackToTracingNoReplyResult = tOptional(tObject({}));
scheme.LocalUtilsTraceDiscardedParams = tObject({
stacksId: tString
});
scheme.LocalUtilsTraceDiscardedResult = tOptional(tObject({}));
scheme.LocalUtilsGlobToRegexParams = tObject({
glob: tString,
baseURL: tOptional(tString),
webSocketUrl: tOptional(tBoolean)
});
scheme.LocalUtilsGlobToRegexResult = tObject({
regex: tString
});
scheme.SetNetworkCookie = tObject({
name: tString,
value: tString,
url: tOptional(tString),
domain: tOptional(tString),
path: tOptional(tString),
expires: tOptional(tFloat),
httpOnly: tOptional(tBoolean),
secure: tOptional(tBoolean),
sameSite: tOptional(tEnum(["Strict", "Lax", "None"])),
partitionKey: tOptional(tString),
_crHasCrossSiteAncestor: tOptional(tBoolean)
});
scheme.NetworkCookie = tObject({
name: tString,
value: tString,
domain: tString,
path: tString,
expires: tFloat,
httpOnly: tBoolean,
secure: tBoolean,
sameSite: tEnum(["Strict", "Lax", "None"]),
partitionKey: tOptional(tString),
_crHasCrossSiteAncestor: tOptional(tBoolean)
});
scheme.RequestInitializer = tObject({
frame: tOptional(tChannel(["Frame"])),
serviceWorker: tOptional(tChannel(["Worker"])),
url: tString,
resourceType: tString,
method: tString,
postData: tOptional(tBinary),
headers: tArray(tType("NameValue")),
isNavigationRequest: tBoolean,
redirectedFrom: tOptional(tChannel(["Request"]))
});
scheme.RequestResponseParams = tOptional(tObject({}));
scheme.RequestResponseResult = tObject({
response: tOptional(tChannel(["Response"]))
});
scheme.RequestRawRequestHeadersParams = tOptional(tObject({}));
scheme.RequestRawRequestHeadersResult = tObject({
headers: tArray(tType("NameValue"))
});
scheme.RouteInitializer = tObject({
request: tChannel(["Request"])
});
scheme.RouteRedirectNavigationRequestParams = tObject({
url: tString
});
scheme.RouteRedirectNavigationRequestResult = tOptional(tObject({}));
scheme.RouteAbortParams = tObject({
errorCode: tOptional(tString)
});
scheme.RouteAbortResult = tOptional(tObject({}));
scheme.RouteContinueParams = tObject({
url: tOptional(tString),
method: tOptional(tString),
headers: tOptional(tArray(tType("NameValue"))),
postData: tOptional(tBinary),
isFallback: tBoolean
});
scheme.RouteContinueResult = tOptional(tObject({}));
scheme.RouteFulfillParams = tObject({
status: tOptional(tInt),
headers: tOptional(tArray(tType("NameValue"))),
body: tOptional(tString),
isBase64: tOptional(tBoolean),
fetchResponseUid: tOptional(tString)
});
scheme.RouteFulfillResult = tOptional(tObject({}));
scheme.WebSocketRouteInitializer = tObject({
url: tString,
protocols: tArray(tString)
});
scheme.WebSocketRouteMessageFromPageEvent = tObject({
message: tString,
isBase64: tBoolean
});
scheme.WebSocketRouteMessageFromServerEvent = tObject({
message: tString,
isBase64: tBoolean
});
scheme.WebSocketRouteClosePageEvent = tObject({
code: tOptional(tInt),
reason: tOptional(tString),
wasClean: tBoolean
});
scheme.WebSocketRouteCloseServerEvent = tObject({
code: tOptional(tInt),
reason: tOptional(tString),
wasClean: tBoolean
});
scheme.WebSocketRouteConnectParams = tOptional(tObject({}));
scheme.WebSocketRouteConnectResult = tOptional(tObject({}));
scheme.WebSocketRouteEnsureOpenedParams = tOptional(tObject({}));
scheme.WebSocketRouteEnsureOpenedResult = tOptional(tObject({}));
scheme.WebSocketRouteSendToPageParams = tObject({
message: tString,
isBase64: tBoolean
});
scheme.WebSocketRouteSendToPageResult = tOptional(tObject({}));
scheme.WebSocketRouteSendToServerParams = tObject({
message: tString,
isBase64: tBoolean
});
scheme.WebSocketRouteSendToServerResult = tOptional(tObject({}));
scheme.WebSocketRouteClosePageParams = tObject({
code: tOptional(tInt),
reason: tOptional(tString),
wasClean: tBoolean
});
scheme.WebSocketRouteClosePageResult = tOptional(tObject({}));
scheme.WebSocketRouteCloseServerParams = tObject({
code: tOptional(tInt),
reason: tOptional(tString),
wasClean: tBoolean
});
scheme.WebSocketRouteCloseServerResult = tOptional(tObject({}));
scheme.ResourceTiming = tObject({
startTime: tFloat,
domainLookupStart: tFloat,
domainLookupEnd: tFloat,
connectStart: tFloat,
secureConnectionStart: tFloat,
connectEnd: tFloat,
requestStart: tFloat,
responseStart: tFloat
});
scheme.ResponseInitializer = tObject({
request: tChannel(["Request"]),
url: tString,
status: tInt,
statusText: tString,
headers: tArray(tType("NameValue")),
timing: tType("ResourceTiming"),
fromServiceWorker: tBoolean
});
scheme.ResponseBodyParams = tOptional(tObject({}));
scheme.ResponseBodyResult = tObject({
binary: tBinary
});
scheme.ResponseSecurityDetailsParams = tOptional(tObject({}));
scheme.ResponseSecurityDetailsResult = tObject({
value: tOptional(tType("SecurityDetails"))
});
scheme.ResponseServerAddrParams = tOptional(tObject({}));
scheme.ResponseServerAddrResult = tObject({
value: tOptional(tType("RemoteAddr"))
});
scheme.ResponseRawResponseHeadersParams = tOptional(tObject({}));
scheme.ResponseRawResponseHeadersResult = tObject({
headers: tArray(tType("NameValue"))
});
scheme.ResponseHttpVersionParams = tOptional(tObject({}));
scheme.ResponseHttpVersionResult = tObject({
value: tString
});
scheme.ResponseSizesParams = tOptional(tObject({}));
scheme.ResponseSizesResult = tObject({
sizes: tType("RequestSizes")
});
scheme.SecurityDetails = tObject({
issuer: tOptional(tString),
protocol: tOptional(tString),
subjectName: tOptional(tString),
validFrom: tOptional(tFloat),
validTo: tOptional(tFloat)
});
scheme.RequestSizes = tObject({
requestBodySize: tInt,
requestHeadersSize: tInt,
responseBodySize: tInt,
responseHeadersSize: tInt
});
scheme.RemoteAddr = tObject({
ipAddress: tString,
port: tInt
});
scheme.WebSocketInitializer = tObject({
url: tString
});
scheme.WebSocketOpenEvent = tOptional(tObject({}));
scheme.WebSocketFrameSentEvent = tObject({
opcode: tInt,
data: tString
});
scheme.WebSocketFrameReceivedEvent = tObject({
opcode: tInt,
data: tString
});
scheme.WebSocketSocketErrorEvent = tObject({
error: tString
});
scheme.WebSocketCloseEvent = tOptional(tObject({}));
scheme.PageInitializer = tObject({
mainFrame: tChannel(["Frame"]),
viewportSize: tOptional(tObject({
width: tInt,
height: tInt
})),
isClosed: tBoolean,
opener: tOptional(tChannel(["Page"])),
video: tOptional(tChannel(["Artifact"]))
});
scheme.PageBindingCallEvent = tObject({
binding: tChannel(["BindingCall"])
});
scheme.PageCloseEvent = tOptional(tObject({}));
scheme.PageCrashEvent = tOptional(tObject({}));
scheme.PageDownloadEvent = tObject({
url: tString,
suggestedFilename: tString,
artifact: tChannel(["Artifact"])
});
scheme.PageViewportSizeChangedEvent = tObject({
viewportSize: tOptional(tObject({
width: tInt,
height: tInt
}))
});
scheme.PageFileChooserEvent = tObject({
element: tChannel(["ElementHandle"]),
isMultiple: tBoolean
});
scheme.PageFrameAttachedEvent = tObject({
frame: tChannel(["Frame"])
});
scheme.PageFrameDetachedEvent = tObject({
frame: tChannel(["Frame"])
});
scheme.PageLocatorHandlerTriggeredEvent = tObject({
uid: tInt
});
scheme.PageRouteEvent = tObject({
route: tChannel(["Route"])
});
scheme.PageScreencastFrameEvent = tObject({
data: tBinary,
viewportWidth: tInt,
viewportHeight: tInt
});
scheme.PageWebSocketRouteEvent = tObject({
webSocketRoute: tChannel(["WebSocketRoute"])
});
scheme.PageWebSocketEvent = tObject({
webSocket: tChannel(["WebSocket"])
});
scheme.PageWorkerEvent = tObject({
worker: tChannel(["Worker"])
});
scheme.PageAddInitScriptParams = tObject({
source: tString
});
scheme.PageAddInitScriptResult = tObject({
disposable: tChannel(["Disposable"])
});
scheme.PageCloseParams = tObject({
runBeforeUnload: tOptional(tBoolean),
reason: tOptional(tString)
});
scheme.PageCloseResult = tOptional(tObject({}));
scheme.PageClearConsoleMessagesParams = tOptional(tObject({}));
scheme.PageClearConsoleMessagesResult = tOptional(tObject({}));
scheme.PageConsoleMessagesParams = tObject({
filter: tOptional(tType("ConsoleMessagesFilter"))
});
scheme.PageConsoleMessagesResult = tObject({
messages: tArray(tObject({
type: tString,
text: tString,
args: tArray(tChannel(["ElementHandle", "JSHandle"])),
location: tObject({
url: tString,
lineNumber: tInt,
columnNumber: tInt
}),
timestamp: tFloat
}))
});
scheme.PageEmulateMediaParams = tObject({
media: tOptional(tEnum(["screen", "print", "no-override"])),
colorScheme: tOptional(tEnum(["dark", "light", "no-preference", "no-override"])),
reducedMotion: tOptional(tEnum(["reduce", "no-preference", "no-override"])),
forcedColors: tOptional(tEnum(["active", "none", "no-override"])),
contrast: tOptional(tEnum(["no-preference", "more", "no-override"]))
});
scheme.PageEmulateMediaResult = tOptional(tObject({}));
scheme.PageExposeBindingParams = tObject({
name: tString
});
scheme.PageExposeBindingResult = tObject({
disposable: tChannel(["Disposable"])
});
scheme.PageGoBackParams = tObject({
timeout: tFloat,
waitUntil: tOptional(tType("LifecycleEvent"))
});
scheme.PageGoBackResult = tObject({
response: tOptional(tChannel(["Response"]))
});
scheme.PageGoForwardParams = tObject({
timeout: tFloat,
waitUntil: tOptional(tType("LifecycleEvent"))
});
scheme.PageGoForwardResult = tObject({
response: tOptional(tChannel(["Response"]))
});
scheme.PageRequestGCParams = tOptional(tObject({}));
scheme.PageRequestGCResult = tOptional(tObject({}));
scheme.PageRegisterLocatorHandlerParams = tObject({
selector: tString,
noWaitAfter: tOptional(tBoolean)
});
scheme.PageRegisterLocatorHandlerResult = tObject({
uid: tInt
});
scheme.PageResolveLocatorHandlerNoReplyParams = tObject({
uid: tInt,
remove: tOptional(tBoolean)
});
scheme.PageResolveLocatorHandlerNoReplyResult = tOptional(tObject({}));
scheme.PageUnregisterLocatorHandlerParams = tObject({
uid: tInt
});
scheme.PageUnregisterLocatorHandlerResult = tOptional(tObject({}));
scheme.PageReloadParams = tObject({
timeout: tFloat,
waitUntil: tOptional(tType("LifecycleEvent"))
});
scheme.PageReloadResult = tObject({
response: tOptional(tChannel(["Response"]))
});
scheme.PageExpectScreenshotParams = tObject({
expected: tOptional(tBinary),
timeout: tFloat,
isNot: tBoolean,
locator: tOptional(tObject({
frame: tChannel(["Frame"]),
selector: tString
})),
comparator: tOptional(tString),
maxDiffPixels: tOptional(tInt),
maxDiffPixelRatio: tOptional(tFloat),
threshold: tOptional(tFloat),
fullPage: tOptional(tBoolean),
clip: tOptional(tType("Rect")),
omitBackground: tOptional(tBoolean),
caret: tOptional(tEnum(["hide", "initial"])),
animations: tOptional(tEnum(["disabled", "allow"])),
scale: tOptional(tEnum(["css", "device"])),
mask: tOptional(tArray(tObject({
frame: tChannel(["Frame"]),
selector: tString
}))),
maskColor: tOptional(tString),
style: tOptional(tString)
});
scheme.PageExpectScreenshotResult = tObject({
diff: tOptional(tBinary),
errorMessage: tOptional(tString),
actual: tOptional(tBinary),
previous: tOptional(tBinary),
timedOut: tOptional(tBoolean),
log: tOptional(tArray(tString))
});
scheme.PageScreenshotParams = tObject({
timeout: tFloat,
type: tOptional(tEnum(["png", "jpeg"])),
quality: tOptional(tInt),
fullPage: tOptional(tBoolean),
clip: tOptional(tType("Rect")),
omitBackground: tOptional(tBoolean),
caret: tOptional(tEnum(["hide", "initial"])),
animations: tOptional(tEnum(["disabled", "allow"])),
scale: tOptional(tEnum(["css", "device"])),
mask: tOptional(tArray(tObject({
frame: tChannel(["Frame"]),
selector: tString
}))),
maskColor: tOptional(tString),
style: tOptional(tString)
});
scheme.PageScreenshotResult = tObject({
binary: tBinary
});
scheme.PageSetExtraHTTPHeadersParams = tObject({
headers: tArray(tType("NameValue"))
});
scheme.PageSetExtraHTTPHeadersResult = tOptional(tObject({}));
scheme.PageSetNetworkInterceptionPatternsParams = tObject({
patterns: tArray(tObject({
glob: tOptional(tString),
regexSource: tOptional(tString),
regexFlags: tOptional(tString),
urlPattern: tOptional(tType("URLPattern"))
}))
});
scheme.PageSetNetworkInterceptionPatternsResult = tOptional(tObject({}));
scheme.PageSetWebSocketInterceptionPatternsParams = tObject({
patterns: tArray(tObject({
glob: tOptional(tString),
regexSource: tOptional(tString),
regexFlags: tOptional(tString),
urlPattern: tOptional(tType("URLPattern"))
}))
});
scheme.PageSetWebSocketInterceptionPatternsResult = tOptional(tObject({}));
scheme.PageSetViewportSizeParams = tObject({
viewportSize: tObject({
width: tInt,
height: tInt
})
});
scheme.PageSetViewportSizeResult = tOptional(tObject({}));
scheme.PageKeyboardDownParams = tObject({
key: tString
});
scheme.PageKeyboardDownResult = tOptional(tObject({}));
scheme.PageKeyboardUpParams = tObject({
key: tString
});
scheme.PageKeyboardUpResult = tOptional(tObject({}));
scheme.PageKeyboardInsertTextParams = tObject({
text: tString
});
scheme.PageKeyboardInsertTextResult = tOptional(tObject({}));
scheme.PageKeyboardTypeParams = tObject({
text: tString,
delay: tOptional(tFloat)
});
scheme.PageKeyboardTypeResult = tOptional(tObject({}));
scheme.PageKeyboardPressParams = tObject({
key: tString,
delay: tOptional(tFloat)
});
scheme.PageKeyboardPressResult = tOptional(tObject({}));
scheme.PageMouseMoveParams = tObject({
x: tFloat,
y: tFloat,
steps: tOptional(tInt)
});
scheme.PageMouseMoveResult = tOptional(tObject({}));
scheme.PageMouseDownParams = tObject({
button: tOptional(tEnum(["left", "right", "middle"])),
clickCount: tOptional(tInt)
});
scheme.PageMouseDownResult = tOptional(tObject({}));
scheme.PageMouseUpParams = tObject({
button: tOptional(tEnum(["left", "right", "middle"])),
clickCount: tOptional(tInt)
});
scheme.PageMouseUpResult = tOptional(tObject({}));
scheme.PageMouseClickParams = tObject({
x: tFloat,
y: tFloat,
delay: tOptional(tFloat),
button: tOptional(tEnum(["left", "right", "middle"])),
clickCount: tOptional(tInt)
});
scheme.PageMouseClickResult = tOptional(tObject({}));
scheme.PageMouseWheelParams = tObject({
deltaX: tFloat,
deltaY: tFloat
});
scheme.PageMouseWheelResult = tOptional(tObject({}));
scheme.PageTouchscreenTapParams = tObject({
x: tFloat,
y: tFloat
});
scheme.PageTouchscreenTapResult = tOptional(tObject({}));
scheme.PageClearPageErrorsParams = tOptional(tObject({}));
scheme.PageClearPageErrorsResult = tOptional(tObject({}));
scheme.PagePageErrorsParams = tObject({
filter: tOptional(tType("ConsoleMessagesFilter"))
});
scheme.PagePageErrorsResult = tObject({
errors: tArray(tType("SerializedError"))
});
scheme.PagePdfParams = tObject({
scale: tOptional(tFloat),
displayHeaderFooter: tOptional(tBoolean),
headerTemplate: tOptional(tString),
footerTemplate: tOptional(tString),
printBackground: tOptional(tBoolean),
landscape: tOptional(tBoolean),
pageRanges: tOptional(tString),
format: tOptional(tString),
width: tOptional(tString),
height: tOptional(tString),
preferCSSPageSize: tOptional(tBoolean),
margin: tOptional(tObject({
top: tOptional(tString),
bottom: tOptional(tString),
left: tOptional(tString),
right: tOptional(tString)
})),
tagged: tOptional(tBoolean),
outline: tOptional(tBoolean)
});
scheme.PagePdfResult = tObject({
pdf: tBinary
});
scheme.PageRequestsParams = tOptional(tObject({}));
scheme.PageRequestsResult = tObject({
requests: tArray(tChannel(["Request"]))
});
scheme.PageStartJSCoverageParams = tObject({
resetOnNavigation: tOptional(tBoolean),
reportAnonymousScripts: tOptional(tBoolean)
});
scheme.PageStartJSCoverageResult = tOptional(tObject({}));
scheme.PageStopJSCoverageParams = tOptional(tObject({}));
scheme.PageStopJSCoverageResult = tObject({
entries: tArray(tObject({
url: tString,
scriptId: tString,
source: tOptional(tString),
functions: tArray(tObject({
functionName: tString,
isBlockCoverage: tBoolean,
ranges: tArray(tObject({
startOffset: tInt,
endOffset: tInt,
count: tInt
}))
}))
}))
});
scheme.PageStartCSSCoverageParams = tObject({
resetOnNavigation: tOptional(tBoolean)
});
scheme.PageStartCSSCoverageResult = tOptional(tObject({}));
scheme.PageStopCSSCoverageParams = tOptional(tObject({}));
scheme.PageStopCSSCoverageResult = tObject({
entries: tArray(tObject({
url: tString,
text: tOptional(tString),
ranges: tArray(tObject({
start: tInt,
end: tInt
}))
}))
});
scheme.PageBringToFrontParams = tOptional(tObject({}));
scheme.PageBringToFrontResult = tOptional(tObject({}));
scheme.PagePickLocatorParams = tOptional(tObject({}));
scheme.PagePickLocatorResult = tObject({
selector: tString
});
scheme.PageCancelPickLocatorParams = tOptional(tObject({}));
scheme.PageCancelPickLocatorResult = tOptional(tObject({}));
scheme.PageHideHighlightParams = tOptional(tObject({}));
scheme.PageHideHighlightResult = tOptional(tObject({}));
scheme.PageScreencastShowOverlayParams = tObject({
html: tString,
duration: tOptional(tFloat)
});
scheme.PageScreencastShowOverlayResult = tObject({
id: tString
});
scheme.PageScreencastRemoveOverlayParams = tObject({
id: tString
});
scheme.PageScreencastRemoveOverlayResult = tOptional(tObject({}));
scheme.PageScreencastChapterParams = tObject({
title: tString,
description: tOptional(tString),
duration: tOptional(tFloat)
});
scheme.PageScreencastChapterResult = tOptional(tObject({}));
scheme.PageScreencastSetOverlayVisibleParams = tObject({
visible: tBoolean
});
scheme.PageScreencastSetOverlayVisibleResult = tOptional(tObject({}));
scheme.PageScreencastShowActionsParams = tObject({
duration: tOptional(tFloat),
position: tOptional(tEnum(["top-left", "top", "top-right", "bottom-left", "bottom", "bottom-right"])),
fontSize: tOptional(tInt)
});
scheme.PageScreencastShowActionsResult = tOptional(tObject({}));
scheme.PageScreencastHideActionsParams = tOptional(tObject({}));
scheme.PageScreencastHideActionsResult = tOptional(tObject({}));
scheme.PageScreencastStartParams = tObject({
size: tOptional(tObject({
width: tInt,
height: tInt
})),
quality: tOptional(tInt),
sendFrames: tOptional(tBoolean),
record: tOptional(tBoolean)
});
scheme.PageScreencastStartResult = tObject({
artifact: tOptional(tChannel(["Artifact"]))
});
scheme.PageScreencastStopParams = tOptional(tObject({}));
scheme.PageScreencastStopResult = tOptional(tObject({}));
scheme.PageUpdateSubscriptionParams = tObject({
event: tEnum(["console", "dialog", "fileChooser", "request", "response", "requestFinished", "requestFailed"]),
enabled: tBoolean
});
scheme.PageUpdateSubscriptionResult = tOptional(tObject({}));
scheme.PageSetDockTileParams = tObject({
image: tBinary
});
scheme.PageSetDockTileResult = tOptional(tObject({}));
scheme.RootInitializer = tOptional(tObject({}));
scheme.RootInitializeParams = tObject({
sdkLanguage: tType("SDKLanguage")
});
scheme.RootInitializeResult = tObject({
playwright: tChannel(["Playwright"])
});
scheme.PlaywrightInitializer = tObject({
chromium: tChannel(["BrowserType"]),
firefox: tChannel(["BrowserType"]),
webkit: tChannel(["BrowserType"]),
android: tChannel(["Android"]),
electron: tChannel(["Electron"]),
utils: tOptional(tChannel(["LocalUtils"])),
preLaunchedBrowser: tOptional(tChannel(["Browser"])),
preConnectedAndroidDevice: tOptional(tChannel(["AndroidDevice"])),
socksSupport: tOptional(tChannel(["SocksSupport"]))
});
scheme.PlaywrightNewRequestParams = tObject({
baseURL: tOptional(tString),
userAgent: tOptional(tString),
ignoreHTTPSErrors: tOptional(tBoolean),
extraHTTPHeaders: tOptional(tArray(tType("NameValue"))),
failOnStatusCode: tOptional(tBoolean),
clientCertificates: tOptional(tArray(tObject({
origin: tString,
cert: tOptional(tBinary),
key: tOptional(tBinary),
passphrase: tOptional(tString),
pfx: tOptional(tBinary)
}))),
maxRedirects: tOptional(tInt),
httpCredentials: tOptional(tObject({
username: tString,
password: tString,
origin: tOptional(tString),
send: tOptional(tEnum(["always", "unauthorized"]))
})),
proxy: tOptional(tObject({
server: tString,
bypass: tOptional(tString),
username: tOptional(tString),
password: tOptional(tString)
})),
storageState: tOptional(tObject({
cookies: tOptional(tArray(tType("NetworkCookie"))),
origins: tOptional(tArray(tType("SetOriginStorage")))
})),
tracesDir: tOptional(tString)
});
scheme.PlaywrightNewRequestResult = tObject({
request: tChannel(["APIRequestContext"])
});
scheme.DebugControllerInitializer = tOptional(tObject({}));
scheme.DebugControllerInspectRequestedEvent = tObject({
selector: tString,
locator: tString,
ariaSnapshot: tString
});
scheme.DebugControllerSetModeRequestedEvent = tObject({
mode: tString
});
scheme.DebugControllerStateChangedEvent = tObject({
pageCount: tInt
});
scheme.DebugControllerSourceChangedEvent = tObject({
text: tString,
header: tOptional(tString),
footer: tOptional(tString),
actions: tOptional(tArray(tString))
});
scheme.DebugControllerPausedEvent = tObject({
paused: tBoolean
});
scheme.DebugControllerInitializeParams = tObject({
codegenId: tString,
sdkLanguage: tType("SDKLanguage")
});
scheme.DebugControllerInitializeResult = tOptional(tObject({}));
scheme.DebugControllerSetReportStateChangedParams = tObject({
enabled: tBoolean
});
scheme.DebugControllerSetReportStateChangedResult = tOptional(tObject({}));
scheme.DebugControllerSetRecorderModeParams = tObject({
mode: tEnum(["inspecting", "recording", "none"]),
testIdAttributeName: tOptional(tString),
generateAutoExpect: tOptional(tBoolean)
});
scheme.DebugControllerSetRecorderModeResult = tOptional(tObject({}));
scheme.DebugControllerHighlightParams = tObject({
selector: tOptional(tString),
ariaTemplate: tOptional(tString)
});
scheme.DebugControllerHighlightResult = tOptional(tObject({}));
scheme.DebugControllerHideHighlightParams = tOptional(tObject({}));
scheme.DebugControllerHideHighlightResult = tOptional(tObject({}));
scheme.DebugControllerResumeParams = tOptional(tObject({}));
scheme.DebugControllerResumeResult = tOptional(tObject({}));
scheme.DebugControllerKillParams = tOptional(tObject({}));
scheme.DebugControllerKillResult = tOptional(tObject({}));
scheme.SocksSupportInitializer = tOptional(tObject({}));
scheme.SocksSupportSocksRequestedEvent = tObject({
uid: tString,
host: tString,
port: tInt
});
scheme.SocksSupportSocksDataEvent = tObject({
uid: tString,
data: tBinary
});
scheme.SocksSupportSocksClosedEvent = tObject({
uid: tString
});
scheme.SocksSupportSocksConnectedParams = tObject({
uid: tString,
host: tString,
port: tInt
});
scheme.SocksSupportSocksConnectedResult = tOptional(tObject({}));
scheme.SocksSupportSocksFailedParams = tObject({
uid: tString,
errorCode: tString
});
scheme.SocksSupportSocksFailedResult = tOptional(tObject({}));
scheme.SocksSupportSocksDataParams = tObject({
uid: tString,
data: tBinary
});
scheme.SocksSupportSocksDataResult = tOptional(tObject({}));
scheme.SocksSupportSocksErrorParams = tObject({
uid: tString,
error: tString
});
scheme.SocksSupportSocksErrorResult = tOptional(tObject({}));
scheme.SocksSupportSocksEndParams = tObject({
uid: tString
});
scheme.SocksSupportSocksEndResult = tOptional(tObject({}));
scheme.JsonPipeInitializer = tOptional(tObject({}));
scheme.JsonPipeMessageEvent = tObject({
message: tAny
});
scheme.JsonPipeClosedEvent = tObject({
reason: tOptional(tString)
});
scheme.JsonPipeSendParams = tObject({
message: tAny
});
scheme.JsonPipeSendResult = tOptional(tObject({}));
scheme.JsonPipeCloseParams = tOptional(tObject({}));
scheme.JsonPipeCloseResult = tOptional(tObject({}));
scheme.ExpectedTextValue = tObject({
string: tOptional(tString),
regexSource: tOptional(tString),
regexFlags: tOptional(tString),
matchSubstring: tOptional(tBoolean),
ignoreCase: tOptional(tBoolean),
normalizeWhiteSpace: tOptional(tBoolean)
});
scheme.SelectorEngine = tObject({
name: tString,
source: tString,
contentScript: tOptional(tBoolean)
});
scheme.FormField = tObject({
name: tString,
value: tOptional(tString),
file: tOptional(tObject({
name: tString,
mimeType: tOptional(tString),
buffer: tBinary
}))
});
scheme.LifecycleEvent = tEnum(["load", "domcontentloaded", "networkidle", "commit"]);
scheme.ConsoleMessagesFilter = tEnum(["all", "since-navigation"]);
scheme.RecorderSource = tObject({
isRecorded: tBoolean,
id: tString,
label: tString,
text: tString,
language: tString,
highlight: tArray(tObject({
line: tInt,
type: tString
})),
revealLine: tOptional(tInt),
group: tOptional(tString)
});
scheme.IndexedDBDatabase = tObject({
name: tString,
version: tInt,
stores: tArray(tObject({
name: tString,
autoIncrement: tBoolean,
keyPath: tOptional(tString),
keyPathArray: tOptional(tArray(tString)),
records: tArray(tObject({
key: tOptional(tAny),
keyEncoded: tOptional(tAny),
value: tOptional(tAny),
valueEncoded: tOptional(tAny)
})),
indexes: tArray(tObject({
name: tString,
keyPath: tOptional(tString),
keyPathArray: tOptional(tArray(tString)),
multiEntry: tBoolean,
unique: tBoolean
}))
}))
});
scheme.SetOriginStorage = tObject({
origin: tString,
localStorage: tArray(tType("NameValue")),
indexedDB: tOptional(tArray(tType("IndexedDBDatabase")))
});
scheme.OriginStorage = tObject({
origin: tString,
localStorage: tArray(tType("NameValue")),
indexedDB: tOptional(tArray(tType("IndexedDBDatabase")))
});
scheme.RecordHarOptions = tObject({
content: tOptional(tEnum(["embed", "attach", "omit"])),
mode: tOptional(tEnum(["full", "minimal"])),
urlGlob: tOptional(tString),
urlRegexSource: tOptional(tString),
urlRegexFlags: tOptional(tString),
harPath: tOptional(tString),
resourcesDir: tOptional(tString)
});
scheme.CDPSessionInitializer = tOptional(tObject({}));
scheme.CDPSessionEventEvent = tObject({
method: tString,
params: tOptional(tAny)
});
scheme.CDPSessionCloseEvent = tOptional(tObject({}));
scheme.CDPSessionSendParams = tObject({
method: tString,
params: tOptional(tAny)
});
scheme.CDPSessionSendResult = tObject({
result: tAny
});
scheme.CDPSessionDetachParams = tOptional(tObject({}));
scheme.CDPSessionDetachResult = tOptional(tObject({}));
scheme.BindingCallInitializer = tObject({
frame: tChannel(["Frame"]),
name: tString,
args: tArray(tType("SerializedValue"))
});
scheme.BindingCallRejectParams = tObject({
error: tType("SerializedError")
});
scheme.BindingCallRejectResult = tOptional(tObject({}));
scheme.BindingCallResolveParams = tObject({
result: tType("SerializedArgument")
});
scheme.BindingCallResolveResult = tOptional(tObject({}));
scheme.DebuggerInitializer = tOptional(tObject({}));
scheme.DebuggerPausedStateChangedEvent = tObject({
pausedDetails: tOptional(tObject({
location: tObject({
file: tString,
line: tOptional(tInt),
column: tOptional(tInt)
}),
title: tString,
stack: tOptional(tString)
}))
});
scheme.DebuggerRequestPauseParams = tOptional(tObject({}));
scheme.DebuggerRequestPauseResult = tOptional(tObject({}));
scheme.DebuggerResumeParams = tOptional(tObject({}));
scheme.DebuggerResumeResult = tOptional(tObject({}));
scheme.DebuggerNextParams = tOptional(tObject({}));
scheme.DebuggerNextResult = tOptional(tObject({}));
scheme.DebuggerRunToParams = tObject({
location: tObject({
file: tString,
line: tOptional(tInt),
column: tOptional(tInt)
})
});
scheme.DebuggerRunToResult = tOptional(tObject({}));
scheme.DialogInitializer = tObject({
page: tOptional(tChannel(["Page"])),
type: tString,
message: tString,
defaultValue: tString
});
scheme.DialogAcceptParams = tObject({
promptText: tOptional(tString)
});
scheme.DialogAcceptResult = tOptional(tObject({}));
scheme.DialogDismissParams = tOptional(tObject({}));
scheme.DialogDismissResult = tOptional(tObject({}));
scheme.SerializedValue = tObject({
n: tOptional(tFloat),
b: tOptional(tBoolean),
s: tOptional(tString),
v: tOptional(tEnum(["null", "undefined", "NaN", "Infinity", "-Infinity", "-0"])),
d: tOptional(tString),
u: tOptional(tString),
bi: tOptional(tString),
ta: tOptional(tObject({
b: tBinary,
k: tEnum(["i8", "ui8", "ui8c", "i16", "ui16", "i32", "ui32", "f32", "f64", "bi64", "bui64"])
})),
e: tOptional(tObject({
m: tString,
n: tString,
s: tString
})),
r: tOptional(tObject({
p: tString,
f: tString
})),
a: tOptional(tArray(tType("SerializedValue"))),
o: tOptional(tArray(tObject({
k: tString,
v: tType("SerializedValue")
}))),
h: tOptional(tInt),
id: tOptional(tInt),
ref: tOptional(tInt)
});
scheme.SerializedArgument = tObject({
value: tType("SerializedValue"),
handles: tArray(tChannel("*"))
});
scheme.SerializedError = tObject({
error: tOptional(tObject({
message: tString,
name: tString,
stack: tOptional(tString)
})),
value: tOptional(tType("SerializedValue"))
});
scheme.StackFrame = tObject({
file: tString,
line: tInt,
column: tInt,
function: tOptional(tString)
});
scheme.Point = tObject({
x: tFloat,
y: tFloat
});
scheme.Rect = tObject({
x: tFloat,
y: tFloat,
width: tFloat,
height: tFloat
});
scheme.URLPattern = tObject({
hash: tString,
hostname: tString,
password: tString,
pathname: tString,
port: tString,
protocol: tString,
search: tString,
username: tString
});
scheme.NameValue = tObject({
name: tString,
value: tString
});
scheme.TracingInitializer = tOptional(tObject({}));
scheme.TracingTracingStartParams = tObject({
name: tOptional(tString),
snapshots: tOptional(tBoolean),
screenshots: tOptional(tBoolean),
live: tOptional(tBoolean)
});
scheme.TracingTracingStartResult = tOptional(tObject({}));
scheme.TracingTracingStartChunkParams = tObject({
name: tOptional(tString),
title: tOptional(tString)
});
scheme.TracingTracingStartChunkResult = tObject({
traceName: tString
});
scheme.TracingTracingGroupParams = tObject({
name: tString,
location: tOptional(tObject({
file: tString,
line: tOptional(tInt),
column: tOptional(tInt)
}))
});
scheme.TracingTracingGroupResult = tOptional(tObject({}));
scheme.TracingTracingGroupEndParams = tOptional(tObject({}));
scheme.TracingTracingGroupEndResult = tOptional(tObject({}));
scheme.TracingTracingStopChunkParams = tObject({
mode: tEnum(["archive", "discard", "entries"])
});
scheme.TracingTracingStopChunkResult = tObject({
artifact: tOptional(tChannel(["Artifact"])),
entries: tOptional(tArray(tType("NameValue")))
});
scheme.TracingTracingStopParams = tOptional(tObject({}));
scheme.TracingTracingStopResult = tOptional(tObject({}));
scheme.TracingHarStartParams = tObject({
page: tOptional(tChannel(["Page"])),
options: tType("RecordHarOptions")
});
scheme.TracingHarStartResult = tObject({
harId: tString
});
scheme.TracingHarExportParams = tObject({
harId: tOptional(tString),
mode: tEnum(["archive", "entries"])
});
scheme.TracingHarExportResult = tObject({
artifact: tOptional(tChannel(["Artifact"])),
entries: tOptional(tArray(tType("NameValue")))
});
scheme.WorkerInitializer = tObject({
url: tString
});
scheme.WorkerConsoleEvent = tObject({
type: tString,
text: tString,
args: tArray(tChannel(["ElementHandle", "JSHandle"])),
location: tObject({
url: tString,
lineNumber: tInt,
columnNumber: tInt
}),
timestamp: tFloat
});
scheme.WorkerCloseEvent = tOptional(tObject({}));
scheme.WorkerDisconnectParams = tObject({
reason: tOptional(tString)
});
scheme.WorkerDisconnectResult = tOptional(tObject({}));
scheme.WorkerEvaluateExpressionParams = tObject({
expression: tString,
isFunction: tOptional(tBoolean),
arg: tType("SerializedArgument")
});
scheme.WorkerEvaluateExpressionResult = tObject({
value: tType("SerializedValue")
});
scheme.WorkerEvaluateExpressionHandleParams = tObject({
expression: tString,
isFunction: tOptional(tBoolean),
arg: tType("SerializedArgument")
});
scheme.WorkerEvaluateExpressionHandleResult = tObject({
handle: tChannel(["ElementHandle", "JSHandle"])
});
scheme.WorkerUpdateSubscriptionParams = tObject({
event: tEnum(["console"]),
enabled: tBoolean
});
scheme.WorkerUpdateSubscriptionResult = tOptional(tObject({}));
}
});
// packages/playwright-core/src/server/dispatchers/dispatcher.ts
function setMaxDispatchersForTest(value2) {
maxDispatchersOverride = value2;
}
function maxDispatchersForBucket(gcBucket) {
return maxDispatchersOverride ?? {
"JSHandle": 1e5,
"ElementHandle": 1e5
}[gcBucket] ?? 1e4;
}
var import_events5, metadataValidator, maxDispatchersOverride, Dispatcher, RootDispatcher, DispatcherConnection;
var init_dispatcher = __esm({
"packages/playwright-core/src/server/dispatchers/dispatcher.ts"() {
"use strict";
import_events5 = require("events");
init_protocolMetainfo();
init_eventsHelper();
init_debug();
init_assert();
init_time();
init_stackTrace();
init_validator();
init_errors();
init_instrumentation();
init_protocolError();
init_callLog();
init_progress();
metadataValidator = createMetadataValidator();
Dispatcher = class extends import_events5.EventEmitter {
constructor(parent, object, type3, initializer, gcBucket) {
super();
this._dispatchers = /* @__PURE__ */ new Map();
this._disposed = false;
this._eventListeners = [];
this._activeProgressControllers = /* @__PURE__ */ new Set();
this.connection = parent instanceof DispatcherConnection ? parent : parent.connection;
this._parent = parent instanceof DispatcherConnection ? void 0 : parent;
const guid = object.guid;
this._guid = guid;
this._type = type3;
this._object = object;
this._gcBucket = gcBucket ?? type3;
this.connection.registerDispatcher(this);
if (this._parent) {
assert(!this._parent._dispatchers.has(guid));
this._parent._dispatchers.set(guid, this);
}
if (this._parent)
this.connection.sendCreate(this._parent, type3, guid, initializer);
this.connection.maybeDisposeStaleDispatchers(this._gcBucket);
}
parentScope() {
return this._parent;
}
addObjectListener(eventName, handler) {
this._eventListeners.push(eventsHelper.addEventListener(this._object, eventName, handler));
}
adopt(child) {
if (child._parent === this)
return;
const oldParent = child._parent;
oldParent._dispatchers.delete(child._guid);
this._dispatchers.set(child._guid, child);
child._parent = this;
this.connection.sendAdopt(this, child);
}
async _runCommand(callMetadata, method, validParams) {
const controller = ProgressController.createForSdkObject(this._object, callMetadata);
this._activeProgressControllers.add(controller);
try {
return await controller.run((progress2) => this[method](validParams, progress2), validParams?.timeout);
} finally {
this._activeProgressControllers.delete(controller);
}
}
_dispatchEvent(method, params2) {
if (this._disposed) {
if (isUnderTest())
throw new Error(`${this._guid} is sending "${String(method)}" event after being disposed`);
return;
}
this.connection.sendEvent(this, method, params2);
}
_dispose(reason) {
this._disposeRecursively(new TargetClosedError(this._object.closeReason()));
this.connection.sendDispose(this, reason);
}
_onDispose() {
}
async stopPendingOperations(error) {
const controllers = [];
const collect = (dispatcher) => {
controllers.push(...dispatcher._activeProgressControllers);
for (const child of [...dispatcher._dispatchers.values()])
collect(child);
};
collect(this);
await Promise.all(controllers.map((controller) => controller.abort(error)));
}
_disposeRecursively(error) {
assert(!this._disposed, `${this._guid} is disposed more than once`);
for (const controller of this._activeProgressControllers) {
if (!controller.metadata.potentiallyClosesScope)
controller.abort(error).catch(() => {
});
}
this._onDispose();
this._disposed = true;
eventsHelper.removeEventListeners(this._eventListeners);
this._parent?._dispatchers.delete(this._guid);
const list = this.connection._dispatchersByBucket.get(this._gcBucket);
list?.delete(this._guid);
this.connection._dispatcherByGuid.delete(this._guid);
this.connection._dispatcherByObject.delete(this._object);
for (const dispatcher of [...this._dispatchers.values()])
dispatcher._disposeRecursively(error);
this._dispatchers.clear();
}
_debugScopeState() {
return {
_guid: this._guid,
objects: Array.from(this._dispatchers.values()).map((o) => o._debugScopeState())
};
}
async waitForEventInfo() {
}
};
RootDispatcher = class extends Dispatcher {
constructor(connection, createPlaywright2) {
super(connection, createRootSdkObject(), "Root", {});
this.createPlaywright = createPlaywright2;
this._initialized = false;
}
async initialize(params2, progress2) {
assert(this.createPlaywright);
assert(!this._initialized);
this._initialized = true;
return {
playwright: await progress2.race(this.createPlaywright(this, params2))
};
}
};
DispatcherConnection = class {
constructor(isLocal) {
this._dispatcherByGuid = /* @__PURE__ */ new Map();
this._dispatcherByObject = /* @__PURE__ */ new Map();
this._dispatchersByBucket = /* @__PURE__ */ new Map();
this.onmessage = (message) => {
};
this._waitOperations = /* @__PURE__ */ new Map();
this._isLocal = !!isLocal;
}
sendEvent(dispatcher, event, params2) {
const validator = findValidator(dispatcher._type, event, "Event");
params2 = validator(params2, "", this._validatorToWireContext());
this.onmessage({ guid: dispatcher._guid, method: event, params: params2 });
}
sendCreate(parent, type3, guid, initializer) {
const validator = findValidator(type3, "", "Initializer");
initializer = validator(initializer, "", this._validatorToWireContext());
this.onmessage({ guid: parent._guid, method: "__create__", params: { type: type3, initializer, guid } });
}
sendAdopt(parent, dispatcher) {
this.onmessage({ guid: parent._guid, method: "__adopt__", params: { guid: dispatcher._guid } });
}
sendDispose(dispatcher, reason) {
this.onmessage({ guid: dispatcher._guid, method: "__dispose__", params: { reason } });
}
_validatorToWireContext() {
return {
tChannelImpl: this._tChannelImplToWire.bind(this),
binary: this._isLocal ? "buffer" : "toBase64",
isUnderTest
};
}
_validatorFromWireContext() {
return {
tChannelImpl: this._tChannelImplFromWire.bind(this),
binary: this._isLocal ? "buffer" : "fromBase64",
isUnderTest
};
}
_tChannelImplFromWire(names, arg, path59, context2) {
if (arg && typeof arg === "object" && typeof arg.guid === "string") {
const guid = arg.guid;
const dispatcher = this._dispatcherByGuid.get(guid);
if (!dispatcher)
throw new ValidationError(`${path59}: no object with guid ${guid}`);
if (names !== "*" && !names.includes(dispatcher._type))
throw new ValidationError(`${path59}: object with guid ${guid} has type ${dispatcher._type}, expected ${names.toString()}`);
return dispatcher;
}
throw new ValidationError(`${path59}: expected guid for ${names.toString()}`);
}
_tChannelImplToWire(names, arg, path59, context2) {
if (arg instanceof Dispatcher) {
if (names !== "*" && !names.includes(arg._type))
throw new ValidationError(`${path59}: dispatcher with guid ${arg._guid} has type ${arg._type}, expected ${names.toString()}`);
return { guid: arg._guid };
}
throw new ValidationError(`${path59}: expected dispatcher ${names.toString()}`);
}
existingDispatcher(object) {
return this._dispatcherByObject.get(object);
}
registerDispatcher(dispatcher) {
assert(!this._dispatcherByGuid.has(dispatcher._guid));
this._dispatcherByGuid.set(dispatcher._guid, dispatcher);
this._dispatcherByObject.set(dispatcher._object, dispatcher);
let list = this._dispatchersByBucket.get(dispatcher._gcBucket);
if (!list) {
list = /* @__PURE__ */ new Set();
this._dispatchersByBucket.set(dispatcher._gcBucket, list);
}
list.add(dispatcher._guid);
}
maybeDisposeStaleDispatchers(gcBucket) {
const maxDispatchers = maxDispatchersForBucket(gcBucket);
const list = this._dispatchersByBucket.get(gcBucket);
if (!list || list.size <= maxDispatchers)
return;
const dispatchersArray = [...list];
const disposeCount = maxDispatchers / 10 | 0;
this._dispatchersByBucket.set(gcBucket, new Set(dispatchersArray.slice(disposeCount)));
for (let i = 0; i < disposeCount; ++i) {
const d = this._dispatcherByGuid.get(dispatchersArray[i]);
if (!d)
continue;
d._dispose("gc");
}
}
async dispatch(message) {
const { id, guid, method, params: params2, metadata } = message;
const dispatcher = this._dispatcherByGuid.get(guid);
if (!dispatcher) {
this.onmessage({ id, error: serializeError(new TargetClosedError(void 0)) });
return;
}
let validParams;
let validMetadata;
try {
const validator = findValidator(dispatcher._type, method, "Params");
const validatorContext = this._validatorFromWireContext();
validParams = validator(params2, "", validatorContext);
validMetadata = metadataValidator(metadata, "", validatorContext);
if (typeof dispatcher[method] !== "function")
throw new Error(`Mismatching dispatcher: "${dispatcher._type}" does not implement "${method}"`);
} catch (e) {
this.onmessage({ id, error: serializeError(e) });
return;
}
const metainfo = getMetainfo({ type: dispatcher._type, method });
if (metainfo?.internal) {
validMetadata.internal = true;
}
const sdkObject = dispatcher._object;
const callMetadata = {
id: `call@${id}`,
location: validMetadata.location,
title: validMetadata.title,
internal: validMetadata.internal,
stepId: validMetadata.stepId,
objectId: sdkObject.guid,
pageId: sdkObject.attribution?.page?.guid,
frameId: sdkObject.attribution?.frame?.guid,
startTime: monotonicTime(),
endTime: 0,
type: dispatcher._type,
method,
params: params2 || {},
log: []
};
if (params2?.info?.waitId) {
const info = params2.info;
switch (info.phase) {
case "before": {
this._waitOperations.set(info.waitId, callMetadata);
await sdkObject.instrumentation.onBeforeCall(sdkObject, callMetadata);
this.onmessage({ id });
return;
}
case "log": {
const originalMetadata = this._waitOperations.get(info.waitId);
originalMetadata.log.push(info.message);
sdkObject.instrumentation.onCallLog(sdkObject, originalMetadata, "api", info.message);
this.onmessage({ id });
return;
}
case "after": {
const originalMetadata = this._waitOperations.get(info.waitId);
originalMetadata.endTime = monotonicTime();
originalMetadata.error = info.error ? { error: { name: "Error", message: info.error } } : void 0;
this._waitOperations.delete(info.waitId);
await sdkObject.instrumentation.onAfterCall(sdkObject, originalMetadata);
this.onmessage({ id });
return;
}
}
}
await sdkObject.instrumentation.onBeforeCall(sdkObject, callMetadata);
const response2 = { id };
try {
if (this._dispatcherByGuid.get(guid) !== dispatcher)
throw new TargetClosedError(sdkObject.closeReason());
const result2 = await dispatcher._runCommand(callMetadata, method, validParams);
const validator = findValidator(dispatcher._type, method, "Result");
response2.result = validator(result2, "", this._validatorToWireContext());
callMetadata.result = result2;
} catch (e) {
if (isTargetClosedError(e)) {
const reason = sdkObject.closeReason();
if (reason)
rewriteErrorMessage(e, reason);
} else if (isProtocolError(e)) {
if (e.type === "closed")
e = new TargetClosedError(sdkObject.closeReason(), e.browserLogMessage());
else if (e.type === "crashed")
rewriteErrorMessage(e, "Target crashed " + e.browserLogMessage());
}
response2.error = serializeError(e);
callMetadata.error = response2.error;
} finally {
callMetadata.endTime = monotonicTime();
await sdkObject.instrumentation.onAfterCall(sdkObject, callMetadata);
if (metainfo?.slowMo)
await this._doSlowMo(sdkObject);
}
if (response2.error)
response2.log = compressCallLog(callMetadata.log);
this.onmessage(response2);
}
async _doSlowMo(sdkObject) {
const slowMo = sdkObject.attribution.browser?.options.slowMo;
if (slowMo)
await new Promise((f) => setTimeout(f, slowMo));
}
};
}
});
// packages/playwright-core/src/server/har/harTracer.ts
function createHarEntry(pageRef, method, url2, frameref, options2) {
const harEntry = {
pageref: pageRef,
startedDateTime: (/* @__PURE__ */ new Date()).toISOString(),
time: -1,
request: {
method,
url: url2.toString(),
httpVersion: FALLBACK_HTTP_VERSION,
cookies: [],
headers: [],
queryString: [...url2.searchParams].map((e) => ({ name: e[0], value: e[1] })),
headersSize: -1,
bodySize: -1
},
response: {
status: -1,
statusText: "",
httpVersion: FALLBACK_HTTP_VERSION,
cookies: [],
headers: [],
content: {
size: -1,
mimeType: "x-unknown"
},
headersSize: -1,
bodySize: -1,
redirectURL: "",
_transferSize: options2.omitSizes ? void 0 : -1
},
cache: {},
timings: {
send: -1,
wait: -1,
receive: -1
},
_frameref: options2.includeTraceInfo ? frameref : void 0,
_monotonicTime: options2.includeTraceInfo ? monotonicTime() : void 0
};
return harEntry;
}
function parseCookie(c) {
const cookie = {
name: "",
value: ""
};
let first = true;
for (const pair of c.split(/; */)) {
const indexOfEquals = pair.indexOf("=");
const name = indexOfEquals !== -1 ? pair.substr(0, indexOfEquals).trim() : pair.trim();
const value2 = indexOfEquals !== -1 ? pair.substr(indexOfEquals + 1, pair.length).trim() : "";
if (first) {
first = false;
cookie.name = name;
cookie.value = value2;
continue;
}
if (name === "Domain")
cookie.domain = value2;
if (name === "Expires")
cookie.expires = safeDateToISOString(value2);
if (name === "HttpOnly")
cookie.httpOnly = true;
if (name === "Max-Age")
cookie.expires = safeDateToISOString(Date.now() + +value2 * 1e3);
if (name === "Path")
cookie.path = value2;
if (name === "SameSite")
cookie.sameSite = value2;
if (name === "Secure")
cookie.secure = true;
}
return cookie;
}
function safeDateToISOString(value2) {
try {
return new Date(value2).toISOString();
} catch (e) {
}
}
var mime6, FALLBACK_HTTP_VERSION, HarTracer, startedDateSymbol;
var init_harTracer = __esm({
"packages/playwright-core/src/server/har/harTracer.ts"() {
"use strict";
init_manualPromise();
init_eventsHelper();
init_assert();
init_crypto();
init_time();
init_mimeType();
init_urlMatch();
init_userAgent();
init_browserContext();
init_fetch();
init_frames();
init_helper();
init_network2();
init_progress();
mime6 = require("./utilsBundle").mime;
FALLBACK_HTTP_VERSION = "HTTP/1.1";
HarTracer = class {
constructor(context2, page, delegate, options2) {
this._barrierPromises = /* @__PURE__ */ new Set();
this._pageEntries = /* @__PURE__ */ new Map();
this._eventListeners = [];
this._started = false;
this._context = context2;
this._page = page;
this._delegate = delegate;
this._options = options2;
if (options2.slimMode) {
options2.omitSecurityDetails = true;
options2.omitCookies = true;
options2.omitTiming = true;
options2.omitServerIP = true;
options2.omitSizes = true;
options2.omitPages = true;
}
this._entrySymbol = Symbol("requestHarEntry");
this._baseURL = context2 instanceof APIRequestContext ? context2._defaultOptions().baseURL : context2._options.baseURL;
}
start(options2) {
if (this._started)
return;
this._options.omitScripts = options2.omitScripts;
this._started = true;
const apiRequest = this._context instanceof APIRequestContext ? this._context : this._context.fetchRequest;
this._eventListeners = [
eventsHelper.addEventListener(apiRequest, APIRequestContext.Events.Request, (event) => this._onAPIRequest(event)),
eventsHelper.addEventListener(apiRequest, APIRequestContext.Events.RequestFinished, (event) => this._onAPIRequestFinished(event))
];
if (this._context instanceof BrowserContext) {
this._eventListeners.push(
eventsHelper.addEventListener(this._context, BrowserContext.Events.Page, (page) => this._createPageEntryIfNeeded(page)),
eventsHelper.addEventListener(this._context, BrowserContext.Events.Request, (request2) => this._onRequest(request2)),
eventsHelper.addEventListener(this._context, BrowserContext.Events.RequestFinished, ({ request: request2, response: response2 }) => this._onRequestFinished(request2, response2).catch(() => {
})),
eventsHelper.addEventListener(this._context, BrowserContext.Events.RequestFailed, (request2) => this._onRequestFailed(request2)),
eventsHelper.addEventListener(this._context, BrowserContext.Events.Response, (response2) => this._onResponse(response2)),
eventsHelper.addEventListener(this._context, BrowserContext.Events.RequestAborted, (request2) => this._onRequestAborted(request2)),
eventsHelper.addEventListener(this._context, BrowserContext.Events.RequestFulfilled, (request2) => this._onRequestFulfilled(request2)),
eventsHelper.addEventListener(this._context, BrowserContext.Events.RequestContinued, (request2) => this._onRequestContinued(request2))
);
for (const page of this._context.pages())
this._createPageEntryIfNeeded(page);
}
}
_shouldIncludeEntryWithUrl(urlString) {
return !this._options.urlFilter || urlMatches(this._baseURL, urlString, this._options.urlFilter);
}
_entryForRequest(request2) {
return request2[this._entrySymbol];
}
_createPageEntryIfNeeded(page) {
if (!page)
return;
if (this._options.omitPages)
return;
if (this._page && page !== this._page)
return;
let pageEntry = this._pageEntries.get(page);
if (!pageEntry) {
const date = /* @__PURE__ */ new Date();
pageEntry = {
startedDateTime: date.toISOString(),
id: page.guid,
title: "",
pageTimings: this._options.omitTiming ? {} : {
onContentLoad: -1,
onLoad: -1
}
};
pageEntry[startedDateSymbol] = date;
page.mainFrame().on(Frame.Events.AddLifecycle, (event) => {
if (event === "load")
this._onLoad(page, pageEntry);
if (event === "domcontentloaded")
this._onDOMContentLoaded(page, pageEntry);
});
this._pageEntries.set(page, pageEntry);
}
return pageEntry;
}
_onDOMContentLoaded(page, pageEntry) {
const promise = page.mainFrame().evaluateExpression(nullProgress, String(() => {
return {
title: document.title,
domContentLoaded: performance.timing.domContentLoadedEventStart
};
}), { isFunction: true, world: "utility" }).then((result2) => {
pageEntry.title = result2.title;
if (!this._options.omitTiming)
pageEntry.pageTimings.onContentLoad = result2.domContentLoaded;
}).catch(() => {
});
this._addBarrier(page, promise);
}
_onLoad(page, pageEntry) {
const promise = page.mainFrame().evaluateExpression(nullProgress, String(() => {
return {
title: document.title,
loaded: performance.timing.loadEventStart
};
}), { isFunction: true, world: "utility" }).then((result2) => {
pageEntry.title = result2.title;
if (!this._options.omitTiming)
pageEntry.pageTimings.onLoad = result2.loaded;
}).catch(() => {
});
this._addBarrier(page, promise);
}
_addBarrier(target, promise) {
if (!target)
return null;
if (!this._options.waitForContentOnStop)
return;
const race = target.openScope.safeRace(promise);
this._barrierPromises.add(race);
race.then(() => this._barrierPromises.delete(race));
}
_onAPIRequest(event) {
if (!this._shouldIncludeEntryWithUrl(event.url.toString()))
return;
const harEntry = createHarEntry(void 0, event.method, event.url, void 0, this._options);
harEntry._apiRequest = true;
if (!this._options.omitCookies)
harEntry.request.cookies = event.cookies;
harEntry.request.headers = Object.entries(event.headers).map(([name, value2]) => ({ name, value: value2 }));
harEntry.request.postData = this._postDataForBuffer(event.postData || null, event.headers["content-type"], this._options.content);
if (!this._options.omitSizes)
harEntry.request.bodySize = event.postData?.length || 0;
event[this._entrySymbol] = harEntry;
if (this._started)
this._delegate.onEntryStarted(harEntry);
}
_onAPIRequestFinished(event) {
const harEntry = this._entryForRequest(event.requestEvent);
if (!harEntry)
return;
harEntry.response.status = event.statusCode;
harEntry.response.statusText = event.statusMessage;
harEntry.response.httpVersion = event.httpVersion;
harEntry.response.redirectURL = event.headers.location || "";
if (!this._options.omitServerIP) {
harEntry.serverIPAddress = event.serverIPAddress;
harEntry._serverPort = event.serverPort;
}
if (!this._options.omitTiming) {
harEntry.timings = event.timings;
this._computeHarEntryTotalTime(harEntry);
}
if (!this._options.omitSecurityDetails)
harEntry._securityDetails = event.securityDetails;
for (let i = 0; i < event.rawHeaders.length; i += 2) {
harEntry.response.headers.push({
name: event.rawHeaders[i],
value: event.rawHeaders[i + 1]
});
}
harEntry.response.cookies = this._options.omitCookies ? [] : event.cookies.map((c) => {
return {
...c,
expires: c.expires === -1 ? void 0 : safeDateToISOString(c.expires)
};
});
const content = harEntry.response.content;
const contentType = event.headers["content-type"];
if (contentType)
content.mimeType = contentType;
this._storeResponseContent(event.body, content, "other");
if (!this._options.omitSizes)
harEntry.response.bodySize = event.body?.length ?? 0;
if (this._started)
this._delegate.onEntryFinished(harEntry);
}
_onRequest(request2) {
if (!this._shouldIncludeEntryWithUrl(request2.url()))
return;
const page = request2.frame()?._page;
if (this._page && page !== this._page)
return;
const url2 = parseURL2(request2.url());
if (!url2)
return;
const pageEntry = this._createPageEntryIfNeeded(page);
const harEntry = createHarEntry(pageEntry?.id, request2.method(), url2, request2.frame()?.guid, this._options);
this._recordRequestHeadersAndCookies(harEntry, request2.headers());
harEntry.request.postData = this._postDataForRequest(request2, this._options.content);
if (!this._options.omitSizes)
harEntry.request.bodySize = request2.bodySize();
if (request2.redirectedFrom()) {
const fromEntry = this._entryForRequest(request2.redirectedFrom());
if (fromEntry)
fromEntry.response.redirectURL = request2.url();
}
request2[this._entrySymbol] = harEntry;
assert(this._started);
this._delegate.onEntryStarted(harEntry);
}
_recordRequestHeadersAndCookies(harEntry, headers) {
if (!this._options.omitCookies) {
harEntry.request.cookies = [];
for (const header of headers.filter((header2) => header2.name.toLowerCase() === "cookie"))
harEntry.request.cookies.push(...header.value.split(";").map(parseCookie));
}
harEntry.request.headers = headers;
}
_recordRequestOverrides(harEntry, request2) {
if (!request2.overrides() || !this._options.recordRequestOverrides)
return;
harEntry.request.method = request2.method();
harEntry.request.url = request2.url();
harEntry.request.postData = this._postDataForRequest(request2, this._options.content);
this._recordRequestHeadersAndCookies(harEntry, request2.headers());
}
async _onRequestFinished(request2, response2) {
if (!response2)
return;
const harEntry = this._entryForRequest(request2);
if (!harEntry)
return;
const page = request2.frame()?._page;
if (!this._options.omitServerIP) {
this._addBarrier(page || request2.serviceWorker(), response2.serverAddr(nullProgress).then((server) => {
if (server?.ipAddress)
harEntry.serverIPAddress = server.ipAddress;
if (server?.port)
harEntry._serverPort = server.port;
}));
}
if (!this._options.omitSecurityDetails) {
this._addBarrier(page || request2.serviceWorker(), response2.securityDetails(nullProgress).then((details) => {
if (details)
harEntry._securityDetails = details;
}));
}
const compressionCalculationBarrier = this._options.omitSizes ? void 0 : {
_encodedBodySize: -1,
_decodedBodySize: -1,
barrier: new ManualPromise(),
_check: function() {
if (this._encodedBodySize !== -1 && this._decodedBodySize !== -1) {
harEntry.response.content.compression = Math.max(0, this._decodedBodySize - this._encodedBodySize);
this.barrier.resolve();
}
},
setEncodedBodySize: function(encodedBodySize) {
this._encodedBodySize = encodedBodySize;
this._check();
},
setDecodedBodySize: function(decodedBodySize) {
this._decodedBodySize = decodedBodySize;
this._check();
}
};
if (compressionCalculationBarrier)
this._addBarrier(page || request2.serviceWorker(), compressionCalculationBarrier.barrier);
const promise = response2.internalBody().then((buffer) => {
if (this._options.omitScripts && request2.resourceType() === "script") {
compressionCalculationBarrier?.setDecodedBodySize(0);
return;
}
const content = harEntry.response.content;
compressionCalculationBarrier?.setDecodedBodySize(buffer.length);
this._storeResponseContent(buffer, content, request2.resourceType());
}).catch(() => {
compressionCalculationBarrier?.setDecodedBodySize(0);
}).then(() => {
if (this._started)
this._delegate.onEntryFinished(harEntry);
});
this._addBarrier(page || request2.serviceWorker(), promise);
this._addBarrier(page || request2.serviceWorker(), response2.httpVersion(nullProgress).then((httpVersion) => {
harEntry.request.httpVersion = httpVersion;
harEntry.response.httpVersion = httpVersion;
}));
const timing = response2.timing();
harEntry.timings.receive = response2.request()._responseEndTiming !== -1 ? helper.millisToRoundishMillis(response2.request()._responseEndTiming - timing.responseStart) : -1;
this._computeHarEntryTotalTime(harEntry);
if (!this._options.omitSizes) {
this._addBarrier(page || request2.serviceWorker(), response2.sizes(nullProgress).then((sizes) => {
harEntry.response.bodySize = sizes.responseBodySize;
harEntry.response.headersSize = sizes.responseHeadersSize;
harEntry.response._transferSize = sizes.transferSize;
harEntry.request.headersSize = sizes.requestHeadersSize;
compressionCalculationBarrier?.setEncodedBodySize(sizes.responseBodySize);
}));
}
}
async _onRequestFailed(request2) {
const harEntry = this._entryForRequest(request2);
if (!harEntry)
return;
if (request2._failureText !== null)
harEntry.response._failureText = request2._failureText;
this._recordRequestOverrides(harEntry, request2);
if (this._started)
this._delegate.onEntryFinished(harEntry);
}
_onRequestAborted(request2) {
const harEntry = this._entryForRequest(request2);
if (harEntry)
harEntry._wasAborted = true;
}
_onRequestFulfilled(request2) {
const harEntry = this._entryForRequest(request2);
if (harEntry)
harEntry._wasFulfilled = true;
}
_onRequestContinued(request2) {
const harEntry = this._entryForRequest(request2);
if (harEntry)
harEntry._wasContinued = true;
}
_storeResponseContent(buffer, content, resourceType) {
if (!buffer) {
content.size = 0;
return;
}
if (!this._options.omitSizes)
content.size = buffer.length;
if (this._options.content === "embed") {
if (isTextualMimeType(content.mimeType) && resourceType !== "font") {
content.text = buffer.toString();
} else {
content.text = buffer.toString("base64");
content.encoding = "base64";
}
} else if (this._options.content === "attach") {
const sha1 = calculateSha1(buffer) + "." + (mime6.getExtension(content.mimeType) || "dat");
if (this._options.includeTraceInfo)
content._sha1 = sha1;
else
content._file = sha1;
if (this._started)
this._delegate.onContentBlob(sha1, buffer);
}
}
_onResponse(response2) {
const harEntry = this._entryForRequest(response2.request());
if (!harEntry)
return;
const page = response2.frame()?._page;
const pageEntry = this._createPageEntryIfNeeded(page);
const request2 = response2.request();
harEntry.response = {
status: response2.status(),
statusText: response2.statusText(),
httpVersion: FALLBACK_HTTP_VERSION,
// These are bad values that will be overwritten below.
cookies: [],
headers: [],
content: {
size: -1,
mimeType: "x-unknown"
},
headersSize: -1,
bodySize: -1,
redirectURL: "",
_transferSize: this._options.omitSizes ? void 0 : -1
};
if (!this._options.omitTiming) {
const startDateTime = pageEntry ? pageEntry[startedDateSymbol].valueOf() : 0;
const timing = response2.timing();
if (pageEntry && startDateTime > timing.startTime)
pageEntry.startedDateTime = new Date(timing.startTime).toISOString();
const dns2 = timing.domainLookupEnd !== -1 ? helper.millisToRoundishMillis(timing.domainLookupEnd - timing.domainLookupStart) : -1;
const connect2 = timing.connectEnd !== -1 ? helper.millisToRoundishMillis(timing.connectEnd - timing.connectStart) : -1;
const ssl = timing.connectEnd !== -1 ? helper.millisToRoundishMillis(timing.connectEnd - timing.secureConnectionStart) : -1;
const wait2 = timing.responseStart !== -1 ? helper.millisToRoundishMillis(timing.responseStart - timing.requestStart) : -1;
const receive = -1;
harEntry.timings = {
dns: dns2,
connect: connect2,
ssl,
send: 0,
wait: wait2,
receive
};
this._computeHarEntryTotalTime(harEntry);
}
this._recordRequestOverrides(harEntry, request2);
this._addBarrier(page || request2.serviceWorker(), request2.rawRequestHeaders(nullProgress).then((headers) => {
this._recordRequestHeadersAndCookies(harEntry, headers);
}));
this._recordResponseHeaders(harEntry, response2.headers());
this._addBarrier(page || request2.serviceWorker(), response2.rawResponseHeaders(nullProgress).then((headers) => {
this._recordResponseHeaders(harEntry, headers);
}));
}
_recordResponseHeaders(harEntry, headers) {
if (!this._options.omitCookies) {
harEntry.response.cookies = headers.filter((header) => header.name.toLowerCase() === "set-cookie").map((header) => parseCookie(header.value));
}
harEntry.response.headers = headers;
const contentType = headers.find((header) => header.name.toLowerCase() === "content-type");
if (contentType)
harEntry.response.content.mimeType = contentType.value;
}
_computeHarEntryTotalTime(harEntry) {
harEntry.time = [
harEntry.timings.dns,
harEntry.timings.connect,
harEntry.timings.ssl,
harEntry.timings.wait,
harEntry.timings.receive
].reduce((pre, cur) => (cur || -1) > 0 ? cur + pre : pre, 0);
}
async flush() {
await Promise.all(this._barrierPromises);
}
stop() {
this._started = false;
eventsHelper.removeEventListeners(this._eventListeners);
this._barrierPromises.clear();
const context2 = this._context instanceof BrowserContext ? this._context : void 0;
const log2 = {
version: "1.2",
creator: {
name: "Playwright",
version: getPlaywrightVersion()
},
browser: {
name: context2?._browser.options.name || "",
version: context2?._browser.version() || ""
},
pages: this._pageEntries.size ? Array.from(this._pageEntries.values()) : void 0,
entries: []
};
if (!this._options.omitTiming) {
for (const pageEntry of log2.pages || []) {
const startDateTime = pageEntry[startedDateSymbol].valueOf();
if (typeof pageEntry.pageTimings.onContentLoad === "number" && pageEntry.pageTimings.onContentLoad >= 0)
pageEntry.pageTimings.onContentLoad -= startDateTime;
else
pageEntry.pageTimings.onContentLoad = -1;
if (typeof pageEntry.pageTimings.onLoad === "number" && pageEntry.pageTimings.onLoad >= 0)
pageEntry.pageTimings.onLoad -= startDateTime;
else
pageEntry.pageTimings.onLoad = -1;
}
}
this._pageEntries.clear();
return log2;
}
_postDataForRequest(request2, content) {
const postData = request2.postDataBuffer();
if (!postData)
return;
const contentType = request2.headerValue("content-type");
return this._postDataForBuffer(postData, contentType, content);
}
_postDataForBuffer(postData, contentType, content) {
if (!postData)
return;
contentType ??= "application/octet-stream";
const result2 = {
mimeType: contentType,
text: "",
params: []
};
if (content === "embed" && contentType !== "application/octet-stream")
result2.text = postData.toString();
if (content === "attach") {
const sha1 = calculateSha1(postData) + "." + (mime6.getExtension(contentType) || "dat");
if (this._options.includeTraceInfo)
result2._sha1 = sha1;
else
result2._file = sha1;
this._delegate.onContentBlob(sha1, postData);
}
if (contentType === "application/x-www-form-urlencoded") {
const parsed = new URLSearchParams(postData.toString());
for (const [name, value2] of parsed.entries())
result2.params.push({ name, value: value2 });
}
return result2;
}
};
startedDateSymbol = Symbol("startedDate");
}
});
// packages/playwright-core/src/server/har/harRecorder.ts
var import_path14, HarRecorder;
var init_harRecorder = __esm({
"packages/playwright-core/src/server/har/harRecorder.ts"() {
"use strict";
import_path14 = __toESM(require("path"));
init_serializedFS();
init_artifact();
init_harTracer();
HarRecorder = class {
constructor(context2, fallbackDir, harId, page, options2) {
this._fs = new SerializedFS();
this._isFlushed = false;
this._entries = [];
this._writtenContentEntries = /* @__PURE__ */ new Set();
this._context = context2;
const isServer = !!context2.attribution.playwright.options.isServer;
this._harFilePath = !isServer && options2.harPath ? options2.harPath : import_path14.default.join(fallbackDir, `${harId}.har`);
if (!isServer && options2.resourcesDir)
this._resourcesDir = options2.resourcesDir;
else if (!isServer && options2.harPath)
this._resourcesDir = import_path14.default.dirname(options2.harPath);
else
this._resourcesDir = import_path14.default.join(fallbackDir, `${harId}-resources`);
const urlFilterRe = options2.urlRegexSource !== void 0 && options2.urlRegexFlags !== void 0 ? new RegExp(options2.urlRegexSource, options2.urlRegexFlags) : void 0;
const content = options2.content || "embed";
this._tracer = new HarTracer(context2, page, this, {
content,
slimMode: options2.mode === "minimal",
includeTraceInfo: false,
recordRequestOverrides: true,
waitForContentOnStop: true,
urlFilter: urlFilterRe ?? options2.urlGlob
});
this._tracer.start({ omitScripts: false });
}
onEntryStarted(entry) {
this._entries.push(entry);
}
onEntryFinished(entry) {
}
onContentBlob(sha1, buffer) {
if (this._writtenContentEntries.has(sha1))
return;
if (!this._writtenContentEntries.size)
this._fs.mkdir(this._resourcesDir);
this._writtenContentEntries.add(sha1);
this._fs.writeFile(
import_path14.default.join(this._resourcesDir, sha1),
buffer,
true
/* skipIfExists */
);
}
async _flush() {
if (this._isFlushed)
return;
this._isFlushed = true;
await this._tracer.flush();
const log2 = this._tracer.stop();
log2.entries = this._entries;
this._fs.mkdir(import_path14.default.dirname(this._harFilePath));
const file = this._harFilePath;
this._fs.writeFile(file, "");
this._fs.appendFile(file, '{"log":{"version":' + JSON.stringify(log2.version));
this._fs.appendFile(file, ',"creator":' + JSON.stringify(log2.creator));
if (log2.browser)
this._fs.appendFile(file, ',"browser":' + JSON.stringify(log2.browser));
if (log2.pages) {
this._fs.appendFile(file, ',"pages":[');
for (let i = 0; i < log2.pages.length; i++) {
if (i)
this._fs.appendFile(file, ",");
this._fs.appendFile(file, JSON.stringify(log2.pages[i]));
}
this._fs.appendFile(file, "]");
}
this._fs.appendFile(file, ',"entries":[');
for (let i = 0; i < log2.entries.length; i++) {
if (i)
this._fs.appendFile(file, ",");
this._fs.appendFile(file, JSON.stringify(log2.entries[i]));
}
this._fs.appendFile(file, "]");
if (log2.comment !== void 0)
this._fs.appendFile(file, ',"comment":' + JSON.stringify(log2.comment));
this._fs.appendFile(
file,
"}}",
true
/* flush */
);
}
async flush() {
await this._flush();
const error = await this._fs.syncAndGetError();
if (error)
throw error;
}
async export(mode) {
await this._flush();
const entries = [{ name: "har.har", value: this._harFilePath }];
for (const sha1 of this._writtenContentEntries)
entries.push({ name: sha1, value: import_path14.default.join(this._resourcesDir, sha1) });
const zipPath = this._harFilePath + ".zip";
if (mode === "archive")
this._fs.zip(entries, zipPath);
const error = await this._fs.syncAndGetError();
if (error)
throw error;
if (mode === "entries")
return { entries };
const artifact = new Artifact(this._context, zipPath);
artifact.reportFinished();
return { artifact };
}
};
}
});
// packages/playwright-core/src/server/trace/recorder/tracing.ts
function visitTraceEvent(object, sha1s) {
if (Array.isArray(object))
return object.map((o) => visitTraceEvent(o, sha1s));
if (object instanceof Dispatcher)
return `<${object._type}>`;
if (object instanceof Buffer)
return `<Buffer>`;
if (object instanceof Date)
return object;
if (typeof object === "object") {
const result2 = {};
for (const key in object) {
if (key === "sha1" || key === "_sha1" || key.endsWith("Sha1")) {
const sha1 = object[key];
if (sha1)
sha1s.add(sha1);
}
result2[key] = visitTraceEvent(object[key], sha1s);
}
return result2;
}
return object;
}
function createBeforeActionTraceEvent(metadata, parentId) {
if (metadata.internal || metadata.method.startsWith("tracing"))
return null;
const event = {
type: "before",
callId: metadata.id,
startTime: metadata.startTime,
title: metadata.title,
class: metadata.type,
method: metadata.method,
params: metadata.params,
stepId: metadata.stepId,
pageId: metadata.pageId
};
if (parentId)
event.parentId = parentId;
return event;
}
function createInputActionTraceEvent(metadata) {
if (metadata.internal || metadata.method.startsWith("tracing"))
return null;
return {
type: "input",
callId: metadata.id,
point: metadata.point
};
}
function createActionLogTraceEvent(metadata, message) {
if (metadata.internal || metadata.method.startsWith("tracing"))
return null;
return {
type: "log",
callId: metadata.id,
time: monotonicTime(),
message
};
}
function createAfterActionTraceEvent(metadata) {
if (metadata.internal || metadata.method.startsWith("tracing"))
return null;
return {
type: "after",
callId: metadata.id,
endTime: metadata.endTime,
error: metadata.error?.error,
result: metadata.result,
point: metadata.point
};
}
var import_fs15, import_os5, import_path15, version, Tracing, throttledRate, unthrottleDuration, ScreencastTracingRecorder;
var init_tracing = __esm({
"packages/playwright-core/src/server/trace/recorder/tracing.ts"() {
"use strict";
import_fs15 = __toESM(require("fs"));
import_os5 = __toESM(require("os"));
import_path15 = __toESM(require("path"));
init_protocolMetainfo();
init_assert();
init_time();
init_manualPromise();
init_eventsHelper();
init_crypto();
init_fileUtils();
init_serializedFS();
init_userAgent();
init_snapshotter();
init_artifact();
init_browserContext();
init_dispatcher();
init_errors();
init_harRecorder();
init_harTracer();
init_instrumentation();
init_progress();
version = 8;
Tracing = class extends SdkObject {
constructor(context2, tracesDir) {
super(context2, "tracing");
this._fs = new SerializedFS();
this._screencastListeners = [];
this._pageTracingRecorders = /* @__PURE__ */ new Map();
this._eventListeners = [];
this._isStopping = false;
this._allResources = /* @__PURE__ */ new Set();
this._pendingHarEntries = /* @__PURE__ */ new Set();
this._started = false;
this.harRecorders = /* @__PURE__ */ new Map();
this._context = context2;
this._precreatedTracesDir = tracesDir;
this._harTracer = new HarTracer(context2, null, this, {
content: "attach",
includeTraceInfo: true,
recordRequestOverrides: false,
waitForContentOnStop: false
});
const testIdAttributeName2 = "selectors" in context2 ? context2.selectors().testIdAttributeName() : void 0;
this._contextCreatedEvent = {
version,
type: "context-options",
origin: "library",
browserName: "",
playwrightVersion: getPlaywrightVersion(),
options: {},
platform: process.platform,
wallTime: 0,
monotonicTime: 0,
sdkLanguage: this._sdkLanguage(),
testIdAttributeName: testIdAttributeName2,
contextId: context2.guid
};
if (context2 instanceof BrowserContext) {
this._snapshotter = new Snapshotter(context2, this);
assert(tracesDir, "tracesDir must be specified for BrowserContext");
this._contextCreatedEvent.browserName = context2._browser.options.name;
this._contextCreatedEvent.channel = context2._browser.options.channel;
this._contextCreatedEvent.options = context2._options;
}
}
_sdkLanguage() {
return this._context instanceof BrowserContext ? this._context._browser.sdkLanguage() : this._context.attribution.playwright.options.sdkLanguage;
}
async resetForReuse(progress2) {
await this.stopChunk(progress2, { mode: "discard" }).catch(() => {
});
await progress2.race(this._stop());
if (this._snapshotter)
await progress2.race(this._snapshotter.resetForReuse());
}
start(progress2, options2) {
if (this._isStopping)
throw new Error("Cannot start tracing while stopping");
if (this._state)
throw new Error("Tracing has been already started");
this._contextCreatedEvent.sdkLanguage = this._sdkLanguage();
const traceName = options2.name || createGuid();
const tracesDir = this._createTracesDirIfNeeded();
this._state = {
options: options2,
traceName,
tracesDir,
traceFile: import_path15.default.join(tracesDir, traceName + ".trace"),
networkFile: import_path15.default.join(tracesDir, traceName + ".network"),
resourcesDir: import_path15.default.join(tracesDir, "resources"),
chunkOrdinal: 0,
traceSha1s: /* @__PURE__ */ new Set(),
networkSha1s: /* @__PURE__ */ new Set(),
recording: false,
callIds: /* @__PURE__ */ new Set(),
groupStack: []
};
this._fs.mkdir(this._state.resourcesDir);
this._fs.writeFile(this._state.networkFile, "");
if (options2.snapshots)
this._harTracer.start({ omitScripts: !options2.live });
this._started = true;
}
async startChunk(progress2, options2 = {}) {
if (this._state && this._state.recording)
await this.stopChunk(progress2, { mode: "discard" });
if (!this._state)
throw new Error("Must start tracing before starting a new chunk");
if (this._isStopping)
throw new Error("Cannot start a trace chunk while stopping");
this._state.recording = true;
this._state.callIds.clear();
const preserveNetworkResources = this._context instanceof BrowserContext;
if (options2.name && options2.name !== this._state.traceName)
this._changeTraceName(this._state, options2.name, preserveNetworkResources);
else
this._allocateNewTraceFile(this._state);
if (!preserveNetworkResources)
this._fs.writeFile(this._state.networkFile, "");
this._fs.mkdir(import_path15.default.dirname(this._state.traceFile));
const event = {
...this._contextCreatedEvent,
title: options2.title,
wallTime: Date.now(),
monotonicTime: monotonicTime()
};
this._appendTraceEvent(event);
this._context.instrumentation.addListener(this, this._context);
this._eventListeners.push(
eventsHelper.addEventListener(this._context, BrowserContext.Events.Console, this._onConsoleMessage.bind(this)),
eventsHelper.addEventListener(this._context, BrowserContext.Events.PageError, this._onPageError.bind(this))
);
if (this._state.options.screenshots)
this._startScreencast();
if (this._state.options.snapshots)
await this._snapshotter?.start(progress2);
return { traceName: this._state.traceName };
}
_currentGroupId() {
return this._state?.groupStack.length ? this._state.groupStack[this._state.groupStack.length - 1] : void 0;
}
group(progress2, name, location2) {
if (!this._state)
return;
const metadata = progress2.metadata;
const stackFrames = [];
const { file, line, column } = location2 ?? metadata.location ?? {};
if (file) {
stackFrames.push({
file,
line: line ?? 0,
column: column ?? 0
});
}
const event = {
type: "before",
callId: metadata.id,
startTime: metadata.startTime,
title: name,
class: "Tracing",
method: "tracingGroup",
params: {},
stepId: metadata.stepId,
stack: stackFrames
};
if (this._currentGroupId())
event.parentId = this._currentGroupId();
this._state.groupStack.push(event.callId);
this._appendTraceEvent(event);
}
groupEnd(progress2) {
this._groupEnd();
}
_groupEnd() {
if (!this._state)
return;
const callId = this._state.groupStack.pop();
if (!callId)
return;
const event = {
type: "after",
callId,
endTime: monotonicTime()
};
this._appendTraceEvent(event);
}
_startScreencast() {
if (!(this._context instanceof BrowserContext))
return;
for (const page of this._context.pages())
this._startScreencastInPage(page);
this._screencastListeners.push(
eventsHelper.addEventListener(this._context, BrowserContext.Events.Page, this._startScreencastInPage.bind(this))
);
}
_stopScreencast() {
eventsHelper.removeEventListeners(this._screencastListeners);
for (const recorder of this._pageTracingRecorders.values())
recorder.dispose();
this._pageTracingRecorders.clear();
}
_allocateNewTraceFile(state) {
const suffix = state.chunkOrdinal ? `-chunk${state.chunkOrdinal}` : ``;
state.chunkOrdinal++;
state.traceFile = import_path15.default.join(state.tracesDir, `${state.traceName}${suffix}.trace`);
}
_changeTraceName(state, name, preserveNetworkResources) {
state.traceName = name;
state.chunkOrdinal = 0;
this._allocateNewTraceFile(state);
const newNetworkFile = import_path15.default.join(state.tracesDir, name + ".network");
if (preserveNetworkResources)
this._fs.copyFile(state.networkFile, newNetworkFile);
state.networkFile = newNetworkFile;
}
async stop(progress2) {
await progress2.race(this._stop());
}
async _stop() {
if (!this._state)
return;
if (this._isStopping)
throw new Error(`Tracing is already stopping`);
if (this._state.recording)
throw new Error(`Must stop trace file before stopping tracing`);
this._closeAllGroups();
this._harTracer.stop();
this.flushHarEntries();
await this._fs.syncAndGetError().finally(() => {
this._state = void 0;
});
}
async deleteTmpTracesDir() {
if (this._tracesTmpDir)
await removeFolders([this._tracesTmpDir]);
}
_createTracesDirIfNeeded() {
if (this._precreatedTracesDir)
return this._precreatedTracesDir;
this._tracesTmpDir = import_fs15.default.mkdtempSync(import_path15.default.join(import_os5.default.tmpdir(), "playwright-tracing-"));
return this._tracesTmpDir;
}
abort() {
this._snapshotter?.dispose();
this._harTracer.stop();
}
async flush() {
this.abort();
for (const harRecorder of this.harRecorders.values())
await harRecorder.flush();
await this._fs.syncAndGetError();
}
harStart(page, options2) {
const harId = createGuid();
const artifactsDir = this._context instanceof BrowserContext ? this._context._browser.options.artifactsDir : this._createTracesDirIfNeeded();
this.harRecorders.set(harId, new HarRecorder(this._context, artifactsDir, harId, page, options2));
return harId;
}
async harExport(progress2, harId, mode) {
const recorder = this.harRecorders.get(harId || "");
const result2 = await progress2.race(recorder.export(mode));
this.harRecorders.delete(harId || "");
return result2;
}
_closeAllGroups() {
while (this._currentGroupId())
this._groupEnd();
}
async stopChunk(progress2, params2) {
if (this._isStopping)
throw new Error(`Tracing is already stopping`);
this._isStopping = true;
if (!this._state || !this._state.recording) {
this._isStopping = false;
if (params2.mode !== "discard")
throw new Error(`Must start tracing before stopping`);
return {};
}
this._closeAllGroups();
this._context.instrumentation.removeListener(this);
eventsHelper.removeEventListeners(this._eventListeners);
if (this._state.options.screenshots)
this._stopScreencast();
if (this._state.options.snapshots)
this._snapshotter?.stop();
this.flushHarEntries();
const newNetworkFile = import_path15.default.join(this._state.tracesDir, this._state.traceName + `-pwnetcopy-${this._state.chunkOrdinal}.network`);
const entries = [];
entries.push({ name: "trace.trace", value: this._state.traceFile });
entries.push({ name: "trace.network", value: newNetworkFile });
for (const sha1 of /* @__PURE__ */ new Set([...this._state.traceSha1s, ...this._state.networkSha1s]))
entries.push({ name: import_path15.default.join("resources", sha1), value: import_path15.default.join(this._state.resourcesDir, sha1) });
this._state.traceSha1s = /* @__PURE__ */ new Set();
if (params2.mode === "discard") {
this._isStopping = false;
this._state.recording = false;
return {};
}
this._fs.copyFile(this._state.networkFile, newNetworkFile);
const zipFileName = this._state.traceFile + ".zip";
if (params2.mode === "archive")
this._fs.zip(entries, zipFileName);
let error;
try {
await progress2.race(this._fs.syncAndGetError());
} catch (e) {
error = e;
}
this._isStopping = false;
if (this._state)
this._state.recording = false;
if (error) {
if (!isAbortError(error) && this._context instanceof BrowserContext && !this._context._browser.isConnected())
return {};
throw error;
}
if (params2.mode === "entries")
return { entries };
const artifact = new Artifact(this._context, zipFileName);
artifact.reportFinished();
return { artifact };
}
async _captureSnapshot(snapshotName, sdkObject, metadata) {
if (!snapshotName || !sdkObject.attribution.page)
return;
await this._snapshotter?.captureSnapshot(sdkObject.attribution.page, metadata.id, snapshotName).catch(() => {
});
}
_shouldCaptureSnapshot(sdkObject, metadata, phase) {
if (!sdkObject.attribution.page || !this._snapshotter?.started())
return;
const metainfo = getMetainfo(metadata);
if (!metainfo?.snapshot)
return false;
switch (phase) {
case "before":
return !metainfo.input || !!metainfo.isAutoWaiting;
case "input":
return !!metainfo.input;
case "after":
return true;
}
}
onBeforeCall(sdkObject, metadata, parentId) {
const event = createBeforeActionTraceEvent(metadata, parentId ?? this._currentGroupId());
if (!event)
return Promise.resolve();
this._temporarilyDisableThrottling(sdkObject.attribution.page);
if (this._shouldCaptureSnapshot(sdkObject, metadata, "before"))
event.beforeSnapshot = `before@${metadata.id}`;
this._state?.callIds.add(metadata.id);
this._appendTraceEvent(event);
return this._captureSnapshot(event.beforeSnapshot, sdkObject, metadata);
}
onBeforeInputAction(sdkObject, metadata) {
if (!this._state?.callIds.has(metadata.id))
return Promise.resolve();
const event = createInputActionTraceEvent(metadata);
if (!event)
return Promise.resolve();
this._temporarilyDisableThrottling(sdkObject.attribution.page);
if (this._shouldCaptureSnapshot(sdkObject, metadata, "input"))
event.inputSnapshot = `input@${metadata.id}`;
this._appendTraceEvent(event);
return this._captureSnapshot(event.inputSnapshot, sdkObject, metadata);
}
onCallLog(sdkObject, metadata, logName, message) {
if (!this._state?.callIds.has(metadata.id))
return;
if (metadata.internal)
return;
if (logName !== "api")
return;
const event = createActionLogTraceEvent(metadata, message);
if (event)
this._appendTraceEvent(event);
}
onAfterCall(sdkObject, metadata) {
if (!this._state?.callIds.has(metadata.id))
return Promise.resolve();
this._state?.callIds.delete(metadata.id);
const event = createAfterActionTraceEvent(metadata);
if (!event)
return Promise.resolve();
this._temporarilyDisableThrottling(sdkObject.attribution.page);
if (this._shouldCaptureSnapshot(sdkObject, metadata, "after"))
event.afterSnapshot = `after@${metadata.id}`;
this._appendTraceEvent(event);
return this._captureSnapshot(event.afterSnapshot, sdkObject, metadata);
}
onEntryStarted(entry) {
this._pendingHarEntries.add(entry);
}
onEntryFinished(entry) {
this._pendingHarEntries.delete(entry);
const event = { type: "resource-snapshot", snapshot: entry };
const visited = visitTraceEvent(event, this._state.networkSha1s);
this._fs.appendFile(
this._state.networkFile,
JSON.stringify(visited) + "\n",
true
/* flush */
);
}
flushHarEntries() {
const harLines = [];
for (const entry of this._pendingHarEntries) {
const event = { type: "resource-snapshot", snapshot: entry };
const visited = visitTraceEvent(event, this._state.networkSha1s);
harLines.push(JSON.stringify(visited));
}
this._pendingHarEntries.clear();
if (harLines.length)
this._fs.appendFile(
this._state.networkFile,
harLines.join("\n") + "\n",
true
/* flush */
);
}
onContentBlob(sha1, buffer) {
this._appendResource(sha1, buffer);
}
onSnapshotterBlob(blob) {
this._appendResource(blob.sha1, blob.buffer);
}
onFrameSnapshot(snapshot3) {
this._appendTraceEvent({ type: "frame-snapshot", snapshot: snapshot3 });
}
_onConsoleMessage(message) {
const event = {
type: "console",
messageType: message.type(),
text: message.text(),
args: message.args().map((a) => ({ preview: a.toString(), value: a.rawValue() })),
location: message.location(),
time: monotonicTime(),
pageId: message.page()?.guid
};
this._appendTraceEvent(event);
}
onDialog(dialog) {
const event = {
type: "event",
time: monotonicTime(),
class: "BrowserContext",
method: "dialog",
params: { pageId: dialog.page().guid, type: dialog.type(), message: dialog.message(), defaultValue: dialog.defaultValue() }
};
this._appendTraceEvent(event);
}
onDownload(page, download) {
const event = {
type: "event",
time: monotonicTime(),
class: "BrowserContext",
method: "download",
params: { pageId: page.guid, url: download.url, suggestedFilename: download.suggestedFilename() }
};
this._appendTraceEvent(event);
}
onPageOpen(page) {
const event = {
type: "event",
time: monotonicTime(),
class: "BrowserContext",
method: "page",
params: { pageId: page.guid, openerPageId: page.opener()?.guid }
};
this._appendTraceEvent(event);
}
onPageClose(page) {
const event = {
type: "event",
time: monotonicTime(),
class: "BrowserContext",
method: "pageClosed",
params: { pageId: page.guid }
};
this._appendTraceEvent(event);
}
dispose(params2) {
if (this._started)
this.stopChunk(nullProgress, params2).then(() => this._stop()).catch(() => {
});
this._started = false;
}
_onPageError(pageError, page) {
const event = {
type: "event",
time: monotonicTime(),
class: "BrowserContext",
method: "pageError",
params: {
error: serializeError(pageError.error),
location: {
url: pageError.location.url,
line: pageError.location.lineNumber,
column: pageError.location.columnNumber
}
},
pageId: page.guid
};
this._appendTraceEvent(event);
}
_temporarilyDisableThrottling(page) {
if (page)
this._pageTracingRecorders.get(page)?.temporarilyDisableThrottling();
}
_startScreencastInPage(page) {
const prefix = page.guid;
const onFrame = (params2) => {
const suffix = Date.now();
const sha1 = `${prefix}-${suffix}.jpeg`;
const event = {
type: "screencast-frame",
pageId: page.guid,
sha1,
width: params2.viewportWidth,
height: params2.viewportHeight,
timestamp: monotonicTime(),
frameSwapWallTime: params2.frameSwapWallTime
};
this._appendResource(sha1, params2.buffer);
this._appendTraceEvent(event);
};
this._pageTracingRecorders.set(page, new ScreencastTracingRecorder(page.screencast, onFrame));
}
_appendTraceEvent(event) {
const visited = visitTraceEvent(event, this._state.traceSha1s);
const flush = this._state.options.live || event.type !== "event" && event.type !== "console" && event.type !== "log";
this._fs.appendFile(this._state.traceFile, JSON.stringify(visited) + "\n", flush);
}
_appendResource(sha1, buffer) {
if (this._allResources.has(sha1))
return;
this._allResources.add(sha1);
const resourcePath = import_path15.default.join(this._state.resourcesDir, sha1);
this._fs.writeFile(
resourcePath,
buffer,
true
/* skipIfExists */
);
}
};
throttledRate = 200;
unthrottleDuration = 500;
ScreencastTracingRecorder = class {
constructor(screencast, onFrame) {
this._unthrottledUntil = 0;
this._screencast = screencast;
this._client = {
onFrame: (frame) => {
const time = monotonicTime();
if (time < this._unthrottledUntil) {
onFrame(frame);
return;
}
if (this._pendingAck)
return;
onFrame(frame);
this._pendingAck = new ManualPromise();
this._timer = setTimeout(() => this._clearPendingAck(), throttledRate);
return this._pendingAck;
},
gracefulClose: () => this.dispose(),
dispose: () => this.dispose()
};
this._screencast.addClient(this._client);
}
dispose() {
this._screencast.removeClient(this._client);
this._clearPendingAck();
}
temporarilyDisableThrottling() {
this._unthrottledUntil = monotonicTime() + unthrottleDuration;
this._clearPendingAck();
}
_clearPendingAck() {
this._pendingAck?.resolve();
this._pendingAck = void 0;
if (this._timer) {
clearTimeout(this._timer);
this._timer = void 0;
}
}
};
}
});
// packages/playwright-core/src/server/fetch.ts
function toHeadersArray(rawHeaders) {
const result2 = [];
for (let i = 0; i < rawHeaders.length; i += 2)
result2.push({ name: rawHeaders[i], value: rawHeaders[i + 1] });
return result2;
}
function parseCookie2(header) {
const raw = parseRawCookie(header);
if (!raw)
return null;
const cookie = {
domain: "",
path: "",
expires: -1,
httpOnly: false,
secure: false,
// From https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Set-Cookie/SameSite
// The cookie-sending behavior if SameSite is not specified is SameSite=Lax.
sameSite: "Lax",
...raw
};
return cookie;
}
function serializePostData(params2, headers) {
assert((params2.postData ? 1 : 0) + (params2.jsonData ? 1 : 0) + (params2.formData ? 1 : 0) + (params2.multipartData ? 1 : 0) <= 1, `Only one of 'data', 'form' or 'multipart' can be specified`);
if (params2.jsonData !== void 0) {
setHeader(headers, "content-type", "application/json", true);
return Buffer.from(params2.jsonData, "utf8");
} else if (params2.formData) {
const searchParams = new URLSearchParams();
for (const { name, value: value2 } of params2.formData)
searchParams.append(name, value2);
setHeader(headers, "content-type", "application/x-www-form-urlencoded", true);
return Buffer.from(searchParams.toString(), "utf8");
} else if (params2.multipartData) {
const formData = new MultipartFormData();
for (const field of params2.multipartData) {
if (field.file)
formData.addFileField(field.name, field.file);
else if (field.value)
formData.addField(field.name, field.value);
}
setHeader(headers, "content-type", formData.contentTypeHeader(), true);
return formData.finish();
} else if (params2.postData !== void 0) {
setHeader(headers, "content-type", "application/octet-stream", true);
return params2.postData;
}
return void 0;
}
function setHeader(headers, name, value2, keepExisting = false) {
const existing = Object.entries(headers).find((pair) => pair[0].toLowerCase() === name.toLowerCase());
if (!existing)
headers[name] = value2;
else if (!keepExisting)
headers[existing[0]] = value2;
}
function getHeader(headers, name) {
const existing = Object.entries(headers).find((pair) => pair[0].toLowerCase() === name.toLowerCase());
return existing ? existing[1] : void 0;
}
function removeHeader(headers, name) {
const existing = Object.entries(headers).find((pair) => pair[0].toLowerCase() === name.toLowerCase());
if (existing)
delete headers[existing[0]];
}
function setBasicAuthorizationHeader(headers, credentials) {
const { username, password } = credentials;
const encoded = Buffer.from(`${username || ""}:${password || ""}`).toString("base64");
setHeader(headers, "authorization", `Basic ${encoded}`);
}
var import_http3, import_https3, import_stream4, import_tls3, zlib, APIRequestContext, SafeEmptyStreamTransform, BrowserContextAPIRequestContext, GlobalAPIRequestContext, redirectStatus;
var init_fetch = __esm({
"packages/playwright-core/src/server/fetch.ts"() {
"use strict";
import_http3 = __toESM(require("http"));
import_https3 = __toESM(require("https"));
import_stream4 = require("stream");
import_tls3 = require("tls");
zlib = __toESM(require("zlib"));
init_crypto();
init_happyEyeballs();
init_assert();
init_urlMatch();
init_eventsHelper();
init_time();
init_network();
init_userAgent();
init_browserContext();
init_cookieStore();
init_formData();
init_instrumentation();
init_progress();
init_socksClientCertificatesInterceptor();
init_tracing();
APIRequestContext = class _APIRequestContext extends SdkObject {
constructor(parent) {
super(parent, "request-context");
this.fetchResponses = /* @__PURE__ */ new Map();
this.fetchLog = /* @__PURE__ */ new Map();
_APIRequestContext.allInstances.add(this);
}
static {
this.Events = {
Dispose: "dispose",
Request: "request",
RequestFinished: "requestfinished"
};
}
static {
this.allInstances = /* @__PURE__ */ new Set();
}
static findResponseBody(guid) {
for (const request2 of _APIRequestContext.allInstances) {
const body = request2.fetchResponses.get(guid);
if (body)
return body;
}
return void 0;
}
fetchResponseBody(progress2, fetchUid) {
return this.fetchResponses.get(fetchUid);
}
fetchLogForUid(progress2, fetchUid) {
return this.fetchLog.get(fetchUid) || [];
}
disposeResponse(progress2, fetchUid) {
this._disposeResponse(fetchUid);
}
_disposeImpl() {
_APIRequestContext.allInstances.delete(this);
this.fetchResponses.clear();
this.fetchLog.clear();
this.emit(_APIRequestContext.Events.Dispose);
}
_disposeResponse(fetchUid) {
this.fetchResponses.delete(fetchUid);
this.fetchLog.delete(fetchUid);
}
_storeResponseBody(body) {
const uid = createGuid();
this.fetchResponses.set(uid, body);
return uid;
}
async fetch(progress2, params2) {
const defaults = this._defaultOptions();
const headers = {
"user-agent": defaults.userAgent,
"accept": "*/*",
"accept-encoding": "gzip,deflate,br"
};
if (defaults.extraHTTPHeaders) {
for (const { name, value: value2 } of defaults.extraHTTPHeaders)
setHeader(headers, name, value2);
}
if (params2.headers) {
for (const { name, value: value2 } of params2.headers)
setHeader(headers, name, value2);
}
const requestUrl = new URL(constructURLBasedOnBaseURL(defaults.baseURL, params2.url));
if (params2.encodedParams) {
requestUrl.search = params2.encodedParams;
} else if (params2.params) {
for (const { name, value: value2 } of params2.params)
requestUrl.searchParams.append(name, value2);
}
const credentials = this._getHttpCredentials(requestUrl);
if (credentials?.send === "always")
setBasicAuthorizationHeader(headers, credentials);
const method = params2.method?.toUpperCase() || "GET";
const proxy = defaults.proxy;
let agent;
if (proxy?.server !== "per-context")
agent = createProxyAgent(proxy, requestUrl);
let maxRedirects = params2.maxRedirects ?? (defaults.maxRedirects ?? 20);
maxRedirects = maxRedirects === 0 ? -1 : maxRedirects;
const options2 = {
method,
headers,
agent,
maxRedirects,
...getMatchingTLSOptionsForOrigin(this._defaultOptions().clientCertificates, requestUrl.origin),
__testHookLookup: params2.__testHookLookup
};
if (params2.ignoreHTTPSErrors || defaults.ignoreHTTPSErrors)
options2.rejectUnauthorized = false;
const postData = serializePostData(params2, headers);
if (postData)
setHeader(headers, "content-length", String(postData.byteLength));
const fetchResponse = await this._sendRequestWithRetries(progress2, requestUrl, options2, postData, params2.maxRetries);
const fetchUid = this._storeResponseBody(fetchResponse.body);
this.fetchLog.set(fetchUid, progress2.metadata.log);
const failOnStatusCode = params2.failOnStatusCode !== void 0 ? params2.failOnStatusCode : !!defaults.failOnStatusCode;
if (failOnStatusCode && (fetchResponse.status < 200 || fetchResponse.status >= 400)) {
let responseText = "";
if (fetchResponse.body.byteLength) {
let text2 = fetchResponse.body.toString("utf8");
if (text2.length > 1e3)
text2 = text2.substring(0, 997) + "...";
responseText = `
Response text:
${text2}`;
}
throw new Error(`${fetchResponse.status} ${fetchResponse.statusText}${responseText}`);
}
return { ...fetchResponse, fetchUid };
}
_parseSetCookieHeader(responseUrl, setCookie) {
if (!setCookie)
return [];
const url2 = new URL(responseUrl);
const defaultPath = "/" + url2.pathname.substr(1).split("/").slice(0, -1).join("/");
const cookies = [];
for (const header of setCookie) {
const cookie = parseCookie2(header);
if (!cookie)
continue;
if (!cookie.domain)
cookie.domain = url2.hostname;
else
assert(cookie.domain.startsWith(".") || !cookie.domain.includes("."));
if (!domainMatches(url2.hostname, cookie.domain))
continue;
if (!cookie.path || !cookie.path.startsWith("/"))
cookie.path = defaultPath;
cookies.push(cookie);
}
return cookies;
}
async _updateRequestCookieHeader(progress2, url2, headers) {
if (getHeader(headers, "cookie") !== void 0)
return;
const contextCookies = await this.cookies(progress2, url2);
const cookies = contextCookies.filter((c) => new Cookie(c).matches(url2));
if (cookies.length) {
const valueArray = cookies.map((c) => `${c.name}=${c.value}`);
setHeader(headers, "cookie", valueArray.join("; "));
}
}
async _sendRequestWithRetries(progress2, url2, options2, postData, maxRetries) {
maxRetries ??= 0;
let backoff = 250;
for (let i = 0; i <= maxRetries; i++) {
try {
return await this._sendRequest(progress2, url2, options2, postData);
} catch (e) {
if (isAbortError(e))
throw e;
e = rewriteOpenSSLErrorIfNeeded(e);
if (maxRetries === 0)
throw e;
if (i === maxRetries)
throw new Error(`Failed after ${i + 1} attempt(s): ${e}`);
if (e.code !== "ECONNRESET")
throw e;
progress2.log(` Received ECONNRESET, will retry after ${backoff}ms.`);
await progress2.wait(backoff);
backoff *= 2;
}
}
throw new Error("Unreachable");
}
async _sendRequest(progress2, url2, options2, postData) {
await this._updateRequestCookieHeader(progress2, url2, options2.headers);
const requestCookies = getHeader(options2.headers, "cookie")?.split(";").map((p) => {
const indexOfEquals = p.indexOf("=");
const name = indexOfEquals !== -1 ? p.substring(0, indexOfEquals).trim() : p.trim();
const value2 = indexOfEquals !== -1 ? p.substring(indexOfEquals + 1).trim() : "";
return { name, value: value2 };
}) || [];
const requestEvent = {
url: url2,
method: options2.method,
headers: options2.headers,
cookies: requestCookies,
postData
};
this.emit(_APIRequestContext.Events.Request, requestEvent);
let destroyRequest;
progress2.setAllowConcurrentOrNestedRaces(true);
const resultPromise = new Promise((fulfill, reject) => {
const requestConstructor = (url2.protocol === "https:" ? import_https3.default : import_http3.default).request;
const agent = options2.agent || (url2.protocol === "https:" ? httpsHappyEyeballsAgent : httpHappyEyeballsAgent);
const requestOptions = { ...options2, agent };
const startAt = monotonicTime();
let reusedSocketAt;
let dnsLookupAt;
let tcpConnectionAt;
let tlsHandshakeAt;
let requestFinishAt;
let serverIPAddress;
let serverPort;
let securityDetails;
const listeners = [];
const request2 = requestConstructor(url2, requestOptions, async (response2) => {
const responseAt = monotonicTime();
const notifyRequestFinished = (body2) => {
const endAt = monotonicTime();
const connectEnd = tlsHandshakeAt ?? tcpConnectionAt;
const timings = {
send: requestFinishAt - startAt,
wait: responseAt - requestFinishAt,
receive: endAt - responseAt,
dns: dnsLookupAt ? dnsLookupAt - startAt : -1,
connect: connectEnd ? connectEnd - startAt : -1,
// "If [ssl] is defined then the time is also included in the connect field "
ssl: tlsHandshakeAt ? tlsHandshakeAt - tcpConnectionAt : -1,
blocked: reusedSocketAt ? reusedSocketAt - startAt : -1
};
const requestFinishedEvent = {
requestEvent,
httpVersion: response2.httpVersion,
statusCode: response2.statusCode || 0,
statusMessage: response2.statusMessage || "",
headers: response2.headers,
rawHeaders: response2.rawHeaders,
cookies,
body: body2,
timings,
serverIPAddress,
serverPort,
securityDetails
};
this.emit(_APIRequestContext.Events.RequestFinished, requestFinishedEvent);
};
progress2.log(`\u2190 ${response2.statusCode} ${response2.statusMessage}`);
for (const [name, value2] of Object.entries(response2.headers))
progress2.log(` ${name}: ${value2}`);
const cookies = this._parseSetCookieHeader(response2.url || url2.toString(), response2.headers["set-cookie"]);
if (cookies.length) {
try {
await this.addCookies(cookies);
} catch (e) {
await Promise.all(cookies.map((c) => this.addCookies([c]).catch(() => {
})));
}
}
if (redirectStatus.includes(response2.statusCode) && options2.maxRedirects >= 0) {
if (options2.maxRedirects === 0) {
reject(new Error("Max redirect count exceeded"));
request2.destroy();
return;
}
const headers = { ...options2.headers };
removeHeader(headers, `cookie`);
const status = response2.statusCode;
let method = options2.method;
if ((status === 301 || status === 302) && method === "POST" || status === 303 && !["GET", "HEAD"].includes(method)) {
method = "GET";
postData = void 0;
removeHeader(headers, `content-encoding`);
removeHeader(headers, `content-language`);
removeHeader(headers, `content-length`);
removeHeader(headers, `content-location`);
removeHeader(headers, `content-type`);
}
const redirectOptions = {
method,
headers,
agent: options2.agent,
maxRedirects: options2.maxRedirects - 1,
__testHookLookup: options2.__testHookLookup
};
if (options2.rejectUnauthorized === false)
redirectOptions.rejectUnauthorized = false;
const locationHeaderValue = Buffer.from(response2.headers.location ?? "", "latin1").toString("utf8");
if (locationHeaderValue) {
let locationURL;
try {
locationURL = new URL(locationHeaderValue, url2);
} catch (error) {
reject(new Error(`uri requested responds with an invalid redirect URL: ${locationHeaderValue}`));
request2.destroy();
return;
}
if (headers["host"])
headers["host"] = locationURL.host;
if (locationURL.origin !== url2.origin)
removeHeader(headers, "authorization");
Object.assign(
redirectOptions,
getMatchingTLSOptionsForOrigin(this._defaultOptions().clientCertificates, locationURL.origin)
);
notifyRequestFinished();
fulfill(this._sendRequest(progress2, locationURL, redirectOptions, postData));
request2.destroy();
return;
}
}
if (response2.statusCode === 401 && !getHeader(options2.headers, "authorization")) {
const auth = response2.headers["www-authenticate"];
const credentials = this._getHttpCredentials(url2);
if (auth?.trim().startsWith("Basic") && credentials) {
setBasicAuthorizationHeader(options2.headers, credentials);
notifyRequestFinished();
fulfill(this._sendRequest(progress2, url2, options2, postData));
request2.destroy();
return;
}
}
response2.on("aborted", () => reject(new Error("aborted")));
const chunks = [];
const notifyBodyFinished = () => {
const body2 = Buffer.concat(chunks);
notifyRequestFinished(body2);
fulfill({
url: response2.url || url2.toString(),
status: response2.statusCode || 0,
statusText: response2.statusMessage || "",
headers: toHeadersArray(response2.rawHeaders),
body: body2
});
};
let body = response2;
let transform2;
const encoding = response2.headers["content-encoding"];
if (encoding === "gzip" || encoding === "x-gzip") {
transform2 = zlib.createGunzip({
flush: zlib.constants.Z_SYNC_FLUSH,
finishFlush: zlib.constants.Z_SYNC_FLUSH
});
} else if (encoding === "br") {
transform2 = zlib.createBrotliDecompress({
flush: zlib.constants.BROTLI_OPERATION_FLUSH,
finishFlush: zlib.constants.BROTLI_OPERATION_FLUSH
});
} else if (encoding === "deflate") {
transform2 = zlib.createInflate();
}
if (transform2) {
const emptyStreamTransform = new SafeEmptyStreamTransform(notifyBodyFinished);
body = (0, import_stream4.pipeline)(response2, emptyStreamTransform, transform2, (e) => {
if (e)
reject(new Error(`failed to decompress '${encoding}' encoding: ${e.message}`));
});
body.on("error", (e) => reject(new Error(`failed to decompress '${encoding}' encoding: ${e}`)));
} else {
body.on("error", reject);
}
body.on("data", (chunk) => chunks.push(chunk));
body.on("end", notifyBodyFinished);
});
request2.on("error", reject);
destroyRequest = () => request2.destroy();
listeners.push(
eventsHelper.addEventListener(this, _APIRequestContext.Events.Dispose, () => {
reject(new Error("Request context disposed."));
request2.destroy();
})
);
request2.on("close", () => eventsHelper.removeEventListeners(listeners));
request2.on("socket", (socket) => {
if (request2.reusedSocket) {
reusedSocketAt = monotonicTime();
return;
}
const happyEyeBallsTimings = timingForSocket(socket);
dnsLookupAt = happyEyeBallsTimings.dnsLookupAt;
tcpConnectionAt = happyEyeBallsTimings.tcpConnectionAt;
listeners.push(
eventsHelper.addEventListener(socket, "lookup", () => {
dnsLookupAt = monotonicTime();
}),
eventsHelper.addEventListener(socket, "connect", () => {
tcpConnectionAt = monotonicTime();
}),
eventsHelper.addEventListener(socket, "secureConnect", () => {
tlsHandshakeAt = monotonicTime();
if (socket instanceof import_tls3.TLSSocket) {
const peerCertificate = socket.getPeerCertificate();
securityDetails = {
protocol: socket.getProtocol() ?? void 0,
subjectName: peerCertificate.subject.CN,
validFrom: new Date(peerCertificate.valid_from).getTime() / 1e3,
validTo: new Date(peerCertificate.valid_to).getTime() / 1e3,
issuer: peerCertificate.issuer.CN
};
}
})
);
serverIPAddress = socket.remoteAddress;
serverPort = socket.remotePort;
});
request2.on("finish", () => {
requestFinishAt = monotonicTime();
});
progress2.log(`\u2192 ${options2.method} ${url2.toString()}`);
if (options2.headers) {
for (const [name, value2] of Object.entries(options2.headers))
progress2.log(` ${name}: ${value2}`);
}
if (postData)
request2.write(postData);
request2.end();
});
return progress2.race(resultPromise).catch((error) => {
destroyRequest?.();
throw error;
}).finally(() => {
progress2.setAllowConcurrentOrNestedRaces(false);
});
}
_getHttpCredentials(url2) {
if (!this._defaultOptions().httpCredentials?.origin || url2.origin.toLowerCase() === this._defaultOptions().httpCredentials?.origin?.toLowerCase())
return this._defaultOptions().httpCredentials;
return void 0;
}
};
SafeEmptyStreamTransform = class extends import_stream4.Transform {
constructor(onEmptyStreamCallback) {
super();
this._receivedSomeData = false;
this._onEmptyStreamCallback = onEmptyStreamCallback;
}
_transform(chunk, encoding, callback) {
this._receivedSomeData = true;
callback(null, chunk);
}
_flush(callback) {
if (this._receivedSomeData)
callback(null);
else
this._onEmptyStreamCallback();
}
};
BrowserContextAPIRequestContext = class extends APIRequestContext {
constructor(context2) {
super(context2);
this._context = context2;
context2.once(BrowserContext.Events.Close, () => this._disposeImpl());
}
tracing() {
return this._context.tracing;
}
async dispose(options2) {
this._closeReason = options2.reason;
this.fetchResponses.clear();
}
_defaultOptions() {
return {
userAgent: this._context._options.userAgent || this._context._browser.userAgent(),
extraHTTPHeaders: this._context._options.extraHTTPHeaders,
failOnStatusCode: void 0,
httpCredentials: this._context._options.httpCredentials,
proxy: this._context._options.proxy || this._context._browser.options.proxy,
ignoreHTTPSErrors: this._context._options.ignoreHTTPSErrors,
baseURL: this._context._options.baseURL,
clientCertificates: this._context._options.clientCertificates
};
}
async addCookies(cookies) {
await this._context.addCookies(cookies);
}
async cookies(progress2, url2) {
return await this._context.cookies(progress2, url2.toString());
}
async storageState(progress2, indexedDB) {
return this._context.storageState(progress2, indexedDB);
}
};
GlobalAPIRequestContext = class extends APIRequestContext {
constructor(playwright2, options2) {
super(playwright2);
this._cookieStore = new CookieStore();
this.attribution.context = this;
if (options2.storageState) {
this._origins = options2.storageState.origins?.map((origin) => ({ indexedDB: [], ...origin }));
this._cookieStore.addCookies(options2.storageState.cookies || []);
}
verifyClientCertificates(options2.clientCertificates);
this._options = {
baseURL: options2.baseURL,
userAgent: options2.userAgent || getUserAgent(),
extraHTTPHeaders: options2.extraHTTPHeaders,
failOnStatusCode: !!options2.failOnStatusCode,
ignoreHTTPSErrors: !!options2.ignoreHTTPSErrors,
maxRedirects: options2.maxRedirects,
httpCredentials: options2.httpCredentials,
clientCertificates: options2.clientCertificates,
proxy: options2.proxy
};
this._tracing = new Tracing(this, options2.tracesDir);
}
tracing() {
return this._tracing;
}
async dispose(options2) {
this._closeReason = options2.reason;
await this._tracing.flush();
await this._tracing.deleteTmpTracesDir();
this._disposeImpl();
}
_defaultOptions() {
return this._options;
}
async addCookies(cookies) {
this._cookieStore.addCookies(cookies);
}
async cookies(progress2, url2) {
return this._cookieStore.cookies(url2);
}
async storageState(progress2, indexedDB = false) {
return {
cookies: this._cookieStore.allCookies(),
origins: (this._origins || []).map((origin) => ({ ...origin, indexedDB: indexedDB ? origin.indexedDB : [] }))
};
}
};
redirectStatus = [301, 302, 303, 307, 308];
}
});
// packages/playwright-core/src/server/utils.ts
async function fetchData(progress2, params2, onError) {
const promise = new ManualPromise();
const { cancel } = httpRequest(params2, async (response2) => {
if (response2.statusCode !== 200) {
const error = onError ? await onError(params2, response2) : new Error(`fetch failed: server returned code ${response2.statusCode}. URL: ${params2.url}`);
promise.reject(error);
return;
}
let body = "";
response2.on("data", (chunk) => body += chunk);
response2.on("error", (error) => promise.reject(error));
response2.on("end", () => promise.resolve(body));
}, (error) => promise.reject(error));
if (!progress2)
return promise;
try {
return await progress2.race(promise);
} catch (error) {
cancel(error);
throw error;
}
}
var init_utils2 = __esm({
"packages/playwright-core/src/server/utils.ts"() {
"use strict";
init_manualPromise();
init_network();
}
});
// packages/playwright-core/src/server/registry/nativeDeps.ts
var deps;
var init_nativeDeps = __esm({
"packages/playwright-core/src/server/registry/nativeDeps.ts"() {
"use strict";
deps = {
"ubuntu20.04-x64": {
tools: [
"xvfb",
"fonts-noto-color-emoji",
"ttf-unifont",
"libfontconfig",
"libfreetype6",
"xfonts-cyrillic",
"xfonts-scalable",
"fonts-liberation",
"fonts-ipafont-gothic",
"fonts-wqy-zenhei",
"fonts-tlwg-loma-otf",
"ttf-ubuntu-font-family"
],
chromium: [
"fonts-liberation",
"libasound2",
"libatk-bridge2.0-0",
"libatk1.0-0",
"libatspi2.0-0",
"libcairo2",
"libcups2",
"libdbus-1-3",
"libdrm2",
"libegl1",
"libgbm1",
"libglib2.0-0",
"libgtk-3-0",
"libnspr4",
"libnss3",
"libpango-1.0-0",
"libx11-6",
"libx11-xcb1",
"libxcb1",
"libxcomposite1",
"libxdamage1",
"libxext6",
"libxfixes3",
"libxrandr2",
"libxshmfence1"
],
firefox: [
"ffmpeg",
"libatk1.0-0",
"libcairo-gobject2",
"libcairo2",
"libdbus-1-3",
"libdbus-glib-1-2",
"libfontconfig1",
"libfreetype6",
"libgdk-pixbuf2.0-0",
"libglib2.0-0",
"libgtk-3-0",
"libpango-1.0-0",
"libpangocairo-1.0-0",
"libpangoft2-1.0-0",
"libx11-6",
"libx11-xcb1",
"libxcb-shm0",
"libxcb1",
"libxcomposite1",
"libxcursor1",
"libxdamage1",
"libxext6",
"libxfixes3",
"libxi6",
"libxrender1",
"libxt6",
"libxtst6"
],
webkit: [
"libenchant-2-2",
"libflite1",
"libx264-155",
"libatk-bridge2.0-0",
"libatk1.0-0",
"libcairo2",
"libegl1",
"libenchant1c2a",
"libepoxy0",
"libevdev2",
"libfontconfig1",
"libfreetype6",
"libgdk-pixbuf2.0-0",
"libgl1",
"libgles2",
"libglib2.0-0",
"libgtk-3-0",
"libgudev-1.0-0",
"libharfbuzz-icu0",
"libharfbuzz0b",
"libhyphen0",
"libicu66",
"libjpeg-turbo8",
"libnghttp2-14",
"libnotify4",
"libopengl0",
"libopenjp2-7",
"libopus0",
"libpango-1.0-0",
"libpng16-16",
"libsecret-1-0",
"libvpx6",
"libwayland-client0",
"libwayland-egl1",
"libwayland-server0",
"libwebp6",
"libwebpdemux2",
"libwoff1",
"libx11-6",
"libxcomposite1",
"libxdamage1",
"libxkbcommon0",
"libxml2",
"libxslt1.1",
"libatomic1",
"libevent-2.1-7"
],
lib2package: {
"libflite.so.1": "libflite1",
"libflite_usenglish.so.1": "libflite1",
"libflite_cmu_grapheme_lang.so.1": "libflite1",
"libflite_cmu_grapheme_lex.so.1": "libflite1",
"libflite_cmu_indic_lang.so.1": "libflite1",
"libflite_cmu_indic_lex.so.1": "libflite1",
"libflite_cmulex.so.1": "libflite1",
"libflite_cmu_time_awb.so.1": "libflite1",
"libflite_cmu_us_awb.so.1": "libflite1",
"libflite_cmu_us_kal16.so.1": "libflite1",
"libflite_cmu_us_kal.so.1": "libflite1",
"libflite_cmu_us_rms.so.1": "libflite1",
"libflite_cmu_us_slt.so.1": "libflite1",
"libx264.so": "libx264-155",
"libasound.so.2": "libasound2",
"libatk-1.0.so.0": "libatk1.0-0",
"libatk-bridge-2.0.so.0": "libatk-bridge2.0-0",
"libatspi.so.0": "libatspi2.0-0",
"libcairo-gobject.so.2": "libcairo-gobject2",
"libcairo.so.2": "libcairo2",
"libcups.so.2": "libcups2",
"libdbus-1.so.3": "libdbus-1-3",
"libdbus-glib-1.so.2": "libdbus-glib-1-2",
"libdrm.so.2": "libdrm2",
"libEGL.so.1": "libegl1",
"libenchant.so.1": "libenchant1c2a",
"libevdev.so.2": "libevdev2",
"libepoxy.so.0": "libepoxy0",
"libfontconfig.so.1": "libfontconfig1",
"libfreetype.so.6": "libfreetype6",
"libgbm.so.1": "libgbm1",
"libgdk_pixbuf-2.0.so.0": "libgdk-pixbuf2.0-0",
"libgdk-3.so.0": "libgtk-3-0",
"libgdk-x11-2.0.so.0": "libgtk2.0-0",
"libgio-2.0.so.0": "libglib2.0-0",
"libGL.so.1": "libgl1",
"libGLESv2.so.2": "libgles2",
"libglib-2.0.so.0": "libglib2.0-0",
"libgmodule-2.0.so.0": "libglib2.0-0",
"libgobject-2.0.so.0": "libglib2.0-0",
"libgthread-2.0.so.0": "libglib2.0-0",
"libgtk-3.so.0": "libgtk-3-0",
"libgtk-x11-2.0.so.0": "libgtk2.0-0",
"libgudev-1.0.so.0": "libgudev-1.0-0",
"libharfbuzz-icu.so.0": "libharfbuzz-icu0",
"libharfbuzz.so.0": "libharfbuzz0b",
"libhyphen.so.0": "libhyphen0",
"libicui18n.so.66": "libicu66",
"libicuuc.so.66": "libicu66",
"libjpeg.so.8": "libjpeg-turbo8",
"libnotify.so.4": "libnotify4",
"libnspr4.so": "libnspr4",
"libnss3.so": "libnss3",
"libnssutil3.so": "libnss3",
"libOpenGL.so.0": "libopengl0",
"libopenjp2.so.7": "libopenjp2-7",
"libopus.so.0": "libopus0",
"libpango-1.0.so.0": "libpango-1.0-0",
"libpangocairo-1.0.so.0": "libpangocairo-1.0-0",
"libpangoft2-1.0.so.0": "libpangoft2-1.0-0",
"libpng16.so.16": "libpng16-16",
"libsecret-1.so.0": "libsecret-1-0",
"libsmime3.so": "libnss3",
"libvpx.so.6": "libvpx6",
"libwayland-client.so.0": "libwayland-client0",
"libwayland-egl.so.1": "libwayland-egl1",
"libwayland-server.so.0": "libwayland-server0",
"libwebp.so.6": "libwebp6",
"libwebpdemux.so.2": "libwebpdemux2",
"libwoff2dec.so.1.0.2": "libwoff1",
"libX11-xcb.so.1": "libx11-xcb1",
"libX11.so.6": "libx11-6",
"libxcb-dri3.so.0": "libxcb-dri3-0",
"libxcb-shm.so.0": "libxcb-shm0",
"libxcb.so.1": "libxcb1",
"libXcomposite.so.1": "libxcomposite1",
"libXcursor.so.1": "libxcursor1",
"libXdamage.so.1": "libxdamage1",
"libXext.so.6": "libxext6",
"libXfixes.so.3": "libxfixes3",
"libXi.so.6": "libxi6",
"libxkbcommon.so.0": "libxkbcommon0",
"libxml2.so.2": "libxml2",
"libXrandr.so.2": "libxrandr2",
"libXrender.so.1": "libxrender1",
"libxslt.so.1": "libxslt1.1",
"libXt.so.6": "libxt6",
"libXtst.so.6": "libxtst6",
"libxshmfence.so.1": "libxshmfence1",
"libatomic.so.1": "libatomic1",
"libenchant-2.so.2": "libenchant-2-2",
"libevent-2.1.so.7": "libevent-2.1-7"
}
},
"ubuntu22.04-x64": {
tools: [
"xvfb",
"fonts-noto-color-emoji",
"fonts-unifont",
"libfontconfig1",
"libfreetype6",
"xfonts-cyrillic",
"xfonts-scalable",
"fonts-liberation",
"fonts-ipafont-gothic",
"fonts-wqy-zenhei",
"fonts-tlwg-loma-otf",
"fonts-freefont-ttf"
],
chromium: [
"libasound2",
"libatk-bridge2.0-0",
"libatk1.0-0",
"libatspi2.0-0",
"libcairo2",
"libcups2",
"libdbus-1-3",
"libdrm2",
"libgbm1",
"libglib2.0-0",
"libnspr4",
"libnss3",
"libpango-1.0-0",
"libwayland-client0",
"libx11-6",
"libxcb1",
"libxcomposite1",
"libxdamage1",
"libxext6",
"libxfixes3",
"libxkbcommon0",
"libxrandr2"
],
firefox: [
"ffmpeg",
"libasound2",
"libatk1.0-0",
"libcairo-gobject2",
"libcairo2",
"libdbus-1-3",
"libdbus-glib-1-2",
"libfontconfig1",
"libfreetype6",
"libgdk-pixbuf-2.0-0",
"libglib2.0-0",
"libgtk-3-0",
"libpango-1.0-0",
"libpangocairo-1.0-0",
"libx11-6",
"libx11-xcb1",
"libxcb-shm0",
"libxcb1",
"libxcomposite1",
"libxcursor1",
"libxdamage1",
"libxext6",
"libxfixes3",
"libxi6",
"libxrandr2",
"libxrender1",
"libxtst6"
],
webkit: [
"libsoup-3.0-0",
"libenchant-2-2",
"gstreamer1.0-libav",
"gstreamer1.0-plugins-bad",
"gstreamer1.0-plugins-base",
"gstreamer1.0-plugins-good",
"libicu70",
"libatk-bridge2.0-0",
"libatk1.0-0",
"libcairo2",
"libdbus-1-3",
"libdrm2",
"libegl1",
"libepoxy0",
"libevdev2",
"libffi7",
"libfontconfig1",
"libfreetype6",
"libgbm1",
"libgdk-pixbuf-2.0-0",
"libgles2",
"libglib2.0-0",
"libglx0",
"libgstreamer-gl1.0-0",
"libgstreamer-plugins-base1.0-0",
"libgstreamer1.0-0",
"libgtk-4-1",
"libgudev-1.0-0",
"libharfbuzz-icu0",
"libharfbuzz0b",
"libhyphen0",
"libjpeg-turbo8",
"liblcms2-2",
"libmanette-0.2-0",
"libnotify4",
"libopengl0",
"libopenjp2-7",
"libopus0",
"libpango-1.0-0",
"libpng16-16",
"libproxy1v5",
"libsecret-1-0",
"libwayland-client0",
"libwayland-egl1",
"libwayland-server0",
"libwebpdemux2",
"libwoff1",
"libx11-6",
"libxcomposite1",
"libxdamage1",
"libxkbcommon0",
"libxml2",
"libxslt1.1",
"libx264-163",
"libatomic1",
"libevent-2.1-7",
"libavif13"
],
lib2package: {
"libavif.so.13": "libavif13",
"libsoup-3.0.so.0": "libsoup-3.0-0",
"libasound.so.2": "libasound2",
"libatk-1.0.so.0": "libatk1.0-0",
"libatk-bridge-2.0.so.0": "libatk-bridge2.0-0",
"libatspi.so.0": "libatspi2.0-0",
"libcairo-gobject.so.2": "libcairo-gobject2",
"libcairo.so.2": "libcairo2",
"libcups.so.2": "libcups2",
"libdbus-1.so.3": "libdbus-1-3",
"libdbus-glib-1.so.2": "libdbus-glib-1-2",
"libdrm.so.2": "libdrm2",
"libEGL.so.1": "libegl1",
"libepoxy.so.0": "libepoxy0",
"libevdev.so.2": "libevdev2",
"libffi.so.7": "libffi7",
"libfontconfig.so.1": "libfontconfig1",
"libfreetype.so.6": "libfreetype6",
"libgbm.so.1": "libgbm1",
"libgdk_pixbuf-2.0.so.0": "libgdk-pixbuf-2.0-0",
"libgdk-3.so.0": "libgtk-3-0",
"libgio-2.0.so.0": "libglib2.0-0",
"libGLESv2.so.2": "libgles2",
"libglib-2.0.so.0": "libglib2.0-0",
"libGLX.so.0": "libglx0",
"libgmodule-2.0.so.0": "libglib2.0-0",
"libgobject-2.0.so.0": "libglib2.0-0",
"libgstallocators-1.0.so.0": "libgstreamer-plugins-base1.0-0",
"libgstapp-1.0.so.0": "libgstreamer-plugins-base1.0-0",
"libgstaudio-1.0.so.0": "libgstreamer-plugins-base1.0-0",
"libgstbase-1.0.so.0": "libgstreamer1.0-0",
"libgstfft-1.0.so.0": "libgstreamer-plugins-base1.0-0",
"libgstgl-1.0.so.0": "libgstreamer-gl1.0-0",
"libgstpbutils-1.0.so.0": "libgstreamer-plugins-base1.0-0",
"libgstreamer-1.0.so.0": "libgstreamer1.0-0",
"libgsttag-1.0.so.0": "libgstreamer-plugins-base1.0-0",
"libgstvideo-1.0.so.0": "libgstreamer-plugins-base1.0-0",
"libgtk-3.so.0": "libgtk-3-0",
"libgtk-4.so.1": "libgtk-4-1",
"libgudev-1.0.so.0": "libgudev-1.0-0",
"libharfbuzz-icu.so.0": "libharfbuzz-icu0",
"libharfbuzz.so.0": "libharfbuzz0b",
"libhyphen.so.0": "libhyphen0",
"libjpeg.so.8": "libjpeg-turbo8",
"liblcms2.so.2": "liblcms2-2",
"libmanette-0.2.so.0": "libmanette-0.2-0",
"libnotify.so.4": "libnotify4",
"libnspr4.so": "libnspr4",
"libnss3.so": "libnss3",
"libnssutil3.so": "libnss3",
"libOpenGL.so.0": "libopengl0",
"libopenjp2.so.7": "libopenjp2-7",
"libopus.so.0": "libopus0",
"libpango-1.0.so.0": "libpango-1.0-0",
"libpangocairo-1.0.so.0": "libpangocairo-1.0-0",
"libpng16.so.16": "libpng16-16",
"libproxy.so.1": "libproxy1v5",
"libsecret-1.so.0": "libsecret-1-0",
"libsmime3.so": "libnss3",
"libwayland-client.so.0": "libwayland-client0",
"libwayland-egl.so.1": "libwayland-egl1",
"libwayland-server.so.0": "libwayland-server0",
"libwebpdemux.so.2": "libwebpdemux2",
"libwoff2dec.so.1.0.2": "libwoff1",
"libX11-xcb.so.1": "libx11-xcb1",
"libX11.so.6": "libx11-6",
"libxcb-shm.so.0": "libxcb-shm0",
"libxcb.so.1": "libxcb1",
"libXcomposite.so.1": "libxcomposite1",
"libXcursor.so.1": "libxcursor1",
"libXdamage.so.1": "libxdamage1",
"libXext.so.6": "libxext6",
"libXfixes.so.3": "libxfixes3",
"libXi.so.6": "libxi6",
"libxkbcommon.so.0": "libxkbcommon0",
"libxml2.so.2": "libxml2",
"libXrandr.so.2": "libxrandr2",
"libXrender.so.1": "libxrender1",
"libxslt.so.1": "libxslt1.1",
"libXtst.so.6": "libxtst6",
"libicui18n.so.60": "libicu70",
"libicuuc.so.66": "libicu70",
"libicui18n.so.66": "libicu70",
"libwebp.so.6": "libwebp6",
"libenchant-2.so.2": "libenchant-2-2",
"libx264.so": "libx264-163",
"libvpx.so.7": "libvpx7",
"libatomic.so.1": "libatomic1",
"libevent-2.1.so.7": "libevent-2.1-7"
}
},
"ubuntu24.04-x64": {
tools: [
"xvfb",
"fonts-noto-color-emoji",
"fonts-unifont",
"libfontconfig1",
"libfreetype6",
"xfonts-cyrillic",
"xfonts-scalable",
"fonts-liberation",
"fonts-ipafont-gothic",
"fonts-wqy-zenhei",
"fonts-tlwg-loma-otf",
"fonts-freefont-ttf"
],
chromium: [
"libasound2t64",
"libatk-bridge2.0-0t64",
"libatk1.0-0t64",
"libatspi2.0-0t64",
"libcairo2",
"libcups2t64",
"libdbus-1-3",
"libdrm2",
"libgbm1",
"libglib2.0-0t64",
"libnspr4",
"libnss3",
"libpango-1.0-0",
"libx11-6",
"libxcb1",
"libxcomposite1",
"libxdamage1",
"libxext6",
"libxfixes3",
"libxkbcommon0",
"libxrandr2"
],
firefox: [
"libasound2t64",
"libatk1.0-0t64",
"libavcodec60",
"libcairo-gobject2",
"libcairo2",
"libdbus-1-3",
"libfontconfig1",
"libfreetype6",
"libgdk-pixbuf-2.0-0",
"libglib2.0-0t64",
"libgtk-3-0t64",
"libpango-1.0-0",
"libpangocairo-1.0-0",
"libx11-6",
"libx11-xcb1",
"libxcb-shm0",
"libxcb1",
"libxcomposite1",
"libxcursor1",
"libxdamage1",
"libxext6",
"libxfixes3",
"libxi6",
"libxrandr2",
"libxrender1"
],
webkit: [
"gstreamer1.0-libav",
"gstreamer1.0-plugins-bad",
"gstreamer1.0-plugins-base",
"gstreamer1.0-plugins-good",
"libicu74",
"libatomic1",
"libatk-bridge2.0-0t64",
"libatk1.0-0t64",
"libcairo-gobject2",
"libcairo2",
"libdbus-1-3",
"libdrm2",
"libenchant-2-2",
"libepoxy0",
"libevent-2.1-7t64",
"libflite1",
"libfontconfig1",
"libfreetype6",
"libgbm1",
"libgdk-pixbuf-2.0-0",
"libgles2",
"libglib2.0-0t64",
"libgstreamer-gl1.0-0",
"libgstreamer-plugins-bad1.0-0",
"libgstreamer-plugins-base1.0-0",
"libgstreamer1.0-0",
"libgtk-4-1",
"libharfbuzz-icu0",
"libharfbuzz0b",
"libhyphen0",
"libicu74",
"libjpeg-turbo8",
"liblcms2-2",
"libmanette-0.2-0",
"libopus0",
"libpango-1.0-0",
"libpangocairo-1.0-0",
"libpng16-16t64",
"libsecret-1-0",
"libvpx9",
"libwayland-client0",
"libwayland-egl1",
"libwayland-server0",
"libwebp7",
"libwebpdemux2",
"libwoff1",
"libx11-6",
"libxkbcommon0",
"libxml2",
"libxslt1.1",
"libx264-164",
"libavif16"
],
lib2package: {
"libavif.so.16": "libavif16",
"libasound.so.2": "libasound2t64",
"libatk-1.0.so.0": "libatk1.0-0t64",
"libatk-bridge-2.0.so.0": "libatk-bridge2.0-0t64",
"libatomic.so.1": "libatomic1",
"libatspi.so.0": "libatspi2.0-0t64",
"libcairo-gobject.so.2": "libcairo-gobject2",
"libcairo.so.2": "libcairo2",
"libcups.so.2": "libcups2t64",
"libdbus-1.so.3": "libdbus-1-3",
"libdrm.so.2": "libdrm2",
"libenchant-2.so.2": "libenchant-2-2",
"libepoxy.so.0": "libepoxy0",
"libevent-2.1.so.7": "libevent-2.1-7t64",
"libflite_cmu_grapheme_lang.so.1": "libflite1",
"libflite_cmu_grapheme_lex.so.1": "libflite1",
"libflite_cmu_indic_lang.so.1": "libflite1",
"libflite_cmu_indic_lex.so.1": "libflite1",
"libflite_cmu_time_awb.so.1": "libflite1",
"libflite_cmu_us_awb.so.1": "libflite1",
"libflite_cmu_us_kal.so.1": "libflite1",
"libflite_cmu_us_kal16.so.1": "libflite1",
"libflite_cmu_us_rms.so.1": "libflite1",
"libflite_cmu_us_slt.so.1": "libflite1",
"libflite_cmulex.so.1": "libflite1",
"libflite_usenglish.so.1": "libflite1",
"libflite.so.1": "libflite1",
"libfontconfig.so.1": "libfontconfig1",
"libfreetype.so.6": "libfreetype6",
"libgbm.so.1": "libgbm1",
"libgdk_pixbuf-2.0.so.0": "libgdk-pixbuf-2.0-0",
"libgdk-3.so.0": "libgtk-3-0t64",
"libgio-2.0.so.0": "libglib2.0-0t64",
"libGLESv2.so.2": "libgles2",
"libglib-2.0.so.0": "libglib2.0-0t64",
"libgmodule-2.0.so.0": "libglib2.0-0t64",
"libgobject-2.0.so.0": "libglib2.0-0t64",
"libgstallocators-1.0.so.0": "libgstreamer-plugins-base1.0-0",
"libgstapp-1.0.so.0": "libgstreamer-plugins-base1.0-0",
"libgstaudio-1.0.so.0": "libgstreamer-plugins-base1.0-0",
"libgstbase-1.0.so.0": "libgstreamer1.0-0",
"libgstcodecparsers-1.0.so.0": "libgstreamer-plugins-bad1.0-0",
"libgstfft-1.0.so.0": "libgstreamer-plugins-base1.0-0",
"libgstgl-1.0.so.0": "libgstreamer-gl1.0-0",
"libgstpbutils-1.0.so.0": "libgstreamer-plugins-base1.0-0",
"libgstreamer-1.0.so.0": "libgstreamer1.0-0",
"libgsttag-1.0.so.0": "libgstreamer-plugins-base1.0-0",
"libgstvideo-1.0.so.0": "libgstreamer-plugins-base1.0-0",
"libgtk-3.so.0": "libgtk-3-0t64",
"libgtk-4.so.1": "libgtk-4-1",
"libharfbuzz-icu.so.0": "libharfbuzz-icu0",
"libharfbuzz.so.0": "libharfbuzz0b",
"libhyphen.so.0": "libhyphen0",
"libicudata.so.74": "libicu74",
"libicui18n.so.74": "libicu74",
"libicuuc.so.74": "libicu74",
"libjpeg.so.8": "libjpeg-turbo8",
"liblcms2.so.2": "liblcms2-2",
"libmanette-0.2.so.0": "libmanette-0.2-0",
"libnspr4.so": "libnspr4",
"libnss3.so": "libnss3",
"libnssutil3.so": "libnss3",
"libopus.so.0": "libopus0",
"libpango-1.0.so.0": "libpango-1.0-0",
"libpangocairo-1.0.so.0": "libpangocairo-1.0-0",
"libpng16.so.16": "libpng16-16t64",
"libsecret-1.so.0": "libsecret-1-0",
"libsmime3.so": "libnss3",
"libsoup-3.0.so.0": "libsoup-3.0-0",
"libvpx.so.9": "libvpx9",
"libwayland-client.so.0": "libwayland-client0",
"libwayland-egl.so.1": "libwayland-egl1",
"libwayland-server.so.0": "libwayland-server0",
"libwebp.so.7": "libwebp7",
"libwebpdemux.so.2": "libwebpdemux2",
"libwoff2dec.so.1.0.2": "libwoff1",
"libX11-xcb.so.1": "libx11-xcb1",
"libX11.so.6": "libx11-6",
"libxcb-shm.so.0": "libxcb-shm0",
"libxcb.so.1": "libxcb1",
"libXcomposite.so.1": "libxcomposite1",
"libXcursor.so.1": "libxcursor1",
"libXdamage.so.1": "libxdamage1",
"libXext.so.6": "libxext6",
"libXfixes.so.3": "libxfixes3",
"libXi.so.6": "libxi6",
"libxkbcommon.so.0": "libxkbcommon0",
"libxml2.so.2": "libxml2",
"libXrandr.so.2": "libxrandr2",
"libXrender.so.1": "libxrender1",
"libxslt.so.1": "libxslt1.1",
"libx264.so": "libx264-164"
}
},
"debian11-x64": {
tools: [
"xvfb",
"fonts-noto-color-emoji",
"fonts-unifont",
"libfontconfig1",
"libfreetype6",
"xfonts-cyrillic",
"xfonts-scalable",
"fonts-liberation",
"fonts-ipafont-gothic",
"fonts-wqy-zenhei",
"fonts-tlwg-loma-otf",
"fonts-freefont-ttf"
],
chromium: [
"libasound2",
"libatk-bridge2.0-0",
"libatk1.0-0",
"libatspi2.0-0",
"libcairo2",
"libcups2",
"libdbus-1-3",
"libdrm2",
"libgbm1",
"libglib2.0-0",
"libnspr4",
"libnss3",
"libpango-1.0-0",
"libwayland-client0",
"libx11-6",
"libxcb1",
"libxcomposite1",
"libxdamage1",
"libxext6",
"libxfixes3",
"libxkbcommon0",
"libxrandr2"
],
firefox: [
"libasound2",
"libatk1.0-0",
"libcairo-gobject2",
"libcairo2",
"libdbus-1-3",
"libdbus-glib-1-2",
"libfontconfig1",
"libfreetype6",
"libgdk-pixbuf-2.0-0",
"libglib2.0-0",
"libgtk-3-0",
"libharfbuzz0b",
"libpango-1.0-0",
"libpangocairo-1.0-0",
"libx11-6",
"libx11-xcb1",
"libxcb-shm0",
"libxcb1",
"libxcomposite1",
"libxcursor1",
"libxdamage1",
"libxext6",
"libxfixes3",
"libxi6",
"libxrandr2",
"libxrender1",
"libxtst6"
],
webkit: [
"gstreamer1.0-libav",
"gstreamer1.0-plugins-bad",
"gstreamer1.0-plugins-base",
"gstreamer1.0-plugins-good",
"libatk-bridge2.0-0",
"libatk1.0-0",
"libcairo2",
"libdbus-1-3",
"libdrm2",
"libegl1",
"libenchant-2-2",
"libepoxy0",
"libevdev2",
"libfontconfig1",
"libfreetype6",
"libgbm1",
"libgdk-pixbuf-2.0-0",
"libgles2",
"libglib2.0-0",
"libglx0",
"libgstreamer-gl1.0-0",
"libgstreamer-plugins-base1.0-0",
"libgstreamer1.0-0",
"libgtk-3-0",
"libgudev-1.0-0",
"libharfbuzz-icu0",
"libharfbuzz0b",
"libhyphen0",
"libicu67",
"libjpeg62-turbo",
"liblcms2-2",
"libmanette-0.2-0",
"libnghttp2-14",
"libnotify4",
"libopengl0",
"libopenjp2-7",
"libopus0",
"libpango-1.0-0",
"libpng16-16",
"libproxy1v5",
"libsecret-1-0",
"libwayland-client0",
"libwayland-egl1",
"libwayland-server0",
"libwebp6",
"libwebpdemux2",
"libwoff1",
"libx11-6",
"libxcomposite1",
"libxdamage1",
"libxkbcommon0",
"libxml2",
"libxslt1.1",
"libatomic1",
"libevent-2.1-7"
],
lib2package: {
"libasound.so.2": "libasound2",
"libatk-1.0.so.0": "libatk1.0-0",
"libatk-bridge-2.0.so.0": "libatk-bridge2.0-0",
"libatspi.so.0": "libatspi2.0-0",
"libcairo-gobject.so.2": "libcairo-gobject2",
"libcairo.so.2": "libcairo2",
"libcups.so.2": "libcups2",
"libdbus-1.so.3": "libdbus-1-3",
"libdbus-glib-1.so.2": "libdbus-glib-1-2",
"libdrm.so.2": "libdrm2",
"libEGL.so.1": "libegl1",
"libenchant-2.so.2": "libenchant-2-2",
"libepoxy.so.0": "libepoxy0",
"libevdev.so.2": "libevdev2",
"libfontconfig.so.1": "libfontconfig1",
"libfreetype.so.6": "libfreetype6",
"libgbm.so.1": "libgbm1",
"libgdk_pixbuf-2.0.so.0": "libgdk-pixbuf-2.0-0",
"libgdk-3.so.0": "libgtk-3-0",
"libgio-2.0.so.0": "libglib2.0-0",
"libGLESv2.so.2": "libgles2",
"libglib-2.0.so.0": "libglib2.0-0",
"libGLX.so.0": "libglx0",
"libgmodule-2.0.so.0": "libglib2.0-0",
"libgobject-2.0.so.0": "libglib2.0-0",
"libgstallocators-1.0.so.0": "libgstreamer-plugins-base1.0-0",
"libgstapp-1.0.so.0": "libgstreamer-plugins-base1.0-0",
"libgstaudio-1.0.so.0": "libgstreamer-plugins-base1.0-0",
"libgstbase-1.0.so.0": "libgstreamer1.0-0",
"libgstfft-1.0.so.0": "libgstreamer-plugins-base1.0-0",
"libgstgl-1.0.so.0": "libgstreamer-gl1.0-0",
"libgstpbutils-1.0.so.0": "libgstreamer-plugins-base1.0-0",
"libgstreamer-1.0.so.0": "libgstreamer1.0-0",
"libgsttag-1.0.so.0": "libgstreamer-plugins-base1.0-0",
"libgstvideo-1.0.so.0": "libgstreamer-plugins-base1.0-0",
"libgtk-3.so.0": "libgtk-3-0",
"libgudev-1.0.so.0": "libgudev-1.0-0",
"libharfbuzz-icu.so.0": "libharfbuzz-icu0",
"libharfbuzz.so.0": "libharfbuzz0b",
"libhyphen.so.0": "libhyphen0",
"libicui18n.so.67": "libicu67",
"libicuuc.so.67": "libicu67",
"libjpeg.so.62": "libjpeg62-turbo",
"liblcms2.so.2": "liblcms2-2",
"libmanette-0.2.so.0": "libmanette-0.2-0",
"libnotify.so.4": "libnotify4",
"libnspr4.so": "libnspr4",
"libnss3.so": "libnss3",
"libnssutil3.so": "libnss3",
"libOpenGL.so.0": "libopengl0",
"libopenjp2.so.7": "libopenjp2-7",
"libopus.so.0": "libopus0",
"libpango-1.0.so.0": "libpango-1.0-0",
"libpangocairo-1.0.so.0": "libpangocairo-1.0-0",
"libpng16.so.16": "libpng16-16",
"libproxy.so.1": "libproxy1v5",
"libsecret-1.so.0": "libsecret-1-0",
"libsmime3.so": "libnss3",
"libwayland-client.so.0": "libwayland-client0",
"libwayland-egl.so.1": "libwayland-egl1",
"libwayland-server.so.0": "libwayland-server0",
"libwebp.so.6": "libwebp6",
"libwebpdemux.so.2": "libwebpdemux2",
"libwoff2dec.so.1.0.2": "libwoff1",
"libX11-xcb.so.1": "libx11-xcb1",
"libX11.so.6": "libx11-6",
"libxcb-shm.so.0": "libxcb-shm0",
"libxcb.so.1": "libxcb1",
"libXcomposite.so.1": "libxcomposite1",
"libXcursor.so.1": "libxcursor1",
"libXdamage.so.1": "libxdamage1",
"libXext.so.6": "libxext6",
"libXfixes.so.3": "libxfixes3",
"libXi.so.6": "libxi6",
"libxkbcommon.so.0": "libxkbcommon0",
"libxml2.so.2": "libxml2",
"libXrandr.so.2": "libxrandr2",
"libXrender.so.1": "libxrender1",
"libxslt.so.1": "libxslt1.1",
"libXtst.so.6": "libxtst6",
"libatomic.so.1": "libatomic1",
"libevent-2.1.so.7": "libevent-2.1-7"
}
},
"debian12-x64": {
tools: [
"xvfb",
"fonts-noto-color-emoji",
"fonts-unifont",
"libfontconfig1",
"libfreetype6",
"xfonts-scalable",
"fonts-liberation",
"fonts-ipafont-gothic",
"fonts-wqy-zenhei",
"fonts-tlwg-loma-otf",
"fonts-freefont-ttf"
],
chromium: [
"libasound2",
"libatk-bridge2.0-0",
"libatk1.0-0",
"libatspi2.0-0",
"libcairo2",
"libcups2",
"libdbus-1-3",
"libdrm2",
"libgbm1",
"libglib2.0-0",
"libnspr4",
"libnss3",
"libpango-1.0-0",
"libx11-6",
"libxcb1",
"libxcomposite1",
"libxdamage1",
"libxext6",
"libxfixes3",
"libxkbcommon0",
"libxrandr2"
],
firefox: [
"libasound2",
"libatk1.0-0",
"libcairo-gobject2",
"libcairo2",
"libdbus-1-3",
"libdbus-glib-1-2",
"libfontconfig1",
"libfreetype6",
"libgdk-pixbuf-2.0-0",
"libglib2.0-0",
"libgtk-3-0",
"libharfbuzz0b",
"libpango-1.0-0",
"libpangocairo-1.0-0",
"libx11-6",
"libx11-xcb1",
"libxcb-shm0",
"libxcb1",
"libxcomposite1",
"libxcursor1",
"libxdamage1",
"libxext6",
"libxfixes3",
"libxi6",
"libxrandr2",
"libxrender1",
"libxtst6"
],
webkit: [
"libsoup-3.0-0",
"gstreamer1.0-libav",
"gstreamer1.0-plugins-bad",
"gstreamer1.0-plugins-base",
"gstreamer1.0-plugins-good",
"libatk-bridge2.0-0",
"libatk1.0-0",
"libcairo2",
"libdbus-1-3",
"libdrm2",
"libegl1",
"libenchant-2-2",
"libepoxy0",
"libevdev2",
"libfontconfig1",
"libfreetype6",
"libgbm1",
"libgdk-pixbuf-2.0-0",
"libgles2",
"libglib2.0-0",
"libglx0",
"libgstreamer-gl1.0-0",
"libgstreamer-plugins-base1.0-0",
"libgstreamer1.0-0",
"libgtk-4-1",
"libgudev-1.0-0",
"libharfbuzz-icu0",
"libharfbuzz0b",
"libhyphen0",
"libicu72",
"libjpeg62-turbo",
"liblcms2-2",
"libmanette-0.2-0",
"libnotify4",
"libopengl0",
"libopenjp2-7",
"libopus0",
"libpango-1.0-0",
"libpng16-16",
"libproxy1v5",
"libsecret-1-0",
"libwayland-client0",
"libwayland-egl1",
"libwayland-server0",
"libwebp7",
"libwebpdemux2",
"libwoff1",
"libx11-6",
"libxcomposite1",
"libxdamage1",
"libxkbcommon0",
"libxml2",
"libxslt1.1",
"libatomic1",
"libevent-2.1-7",
"libavif15"
],
lib2package: {
"libavif.so.15": "libavif15",
"libsoup-3.0.so.0": "libsoup-3.0-0",
"libasound.so.2": "libasound2",
"libatk-1.0.so.0": "libatk1.0-0",
"libatk-bridge-2.0.so.0": "libatk-bridge2.0-0",
"libatspi.so.0": "libatspi2.0-0",
"libcairo.so.2": "libcairo2",
"libcups.so.2": "libcups2",
"libdbus-1.so.3": "libdbus-1-3",
"libdrm.so.2": "libdrm2",
"libgbm.so.1": "libgbm1",
"libgio-2.0.so.0": "libglib2.0-0",
"libglib-2.0.so.0": "libglib2.0-0",
"libgobject-2.0.so.0": "libglib2.0-0",
"libnspr4.so": "libnspr4",
"libnss3.so": "libnss3",
"libnssutil3.so": "libnss3",
"libpango-1.0.so.0": "libpango-1.0-0",
"libsmime3.so": "libnss3",
"libX11.so.6": "libx11-6",
"libxcb.so.1": "libxcb1",
"libXcomposite.so.1": "libxcomposite1",
"libXdamage.so.1": "libxdamage1",
"libXext.so.6": "libxext6",
"libXfixes.so.3": "libxfixes3",
"libxkbcommon.so.0": "libxkbcommon0",
"libXrandr.so.2": "libxrandr2",
"libgtk-4.so.1": "libgtk-4-1"
}
},
"debian13-x64": {
tools: [
"xvfb",
"fonts-noto-color-emoji",
"fonts-unifont",
"libfontconfig1",
"libfreetype6",
"xfonts-scalable",
"fonts-liberation",
"fonts-ipafont-gothic",
"fonts-wqy-zenhei",
"fonts-tlwg-loma-otf",
"fonts-freefont-ttf"
],
chromium: [
"libasound2t64",
"libatk-bridge2.0-0t64",
"libatk1.0-0t64",
"libatspi2.0-0t64",
"libcairo2",
"libcups2t64",
"libdbus-1-3",
"libdrm2",
"libgbm1",
"libglib2.0-0t64",
"libnspr4",
"libnss3",
"libpango-1.0-0",
"libx11-6",
"libxcb1",
"libxcomposite1",
"libxdamage1",
"libxext6",
"libxfixes3",
"libxkbcommon0",
"libxrandr2"
],
firefox: [
"libasound2",
"libatk1.0-0t64",
"libcairo-gobject2",
"libcairo2",
"libdbus-1-3",
"libdbus-glib-1-2",
"libfontconfig1",
"libfreetype6",
"libgdk-pixbuf-2.0-0",
"libglib2.0-0t64",
"libgtk-3-0t64",
"libharfbuzz0b",
"libpango-1.0-0",
"libpangocairo-1.0-0",
"libx11-6",
"libx11-xcb1",
"libxcb-shm0",
"libxcb1",
"libxcomposite1",
"libxcursor1",
"libxdamage1",
"libxext6",
"libxfixes3",
"libxi6",
"libxrandr2",
"libxrender1",
"libxtst6"
],
webkit: [
"libsoup-3.0-0",
"gstreamer1.0-libav",
"gstreamer1.0-plugins-bad",
"gstreamer1.0-plugins-base",
"gstreamer1.0-plugins-good",
"libatk-bridge2.0-0t64",
"libatk1.0-0t64",
"libcairo2",
"libdbus-1-3",
"libdrm2",
"libegl1",
"libenchant-2-2",
"libepoxy0",
"libevdev2",
"libfontconfig1",
"libfreetype6",
"libgbm1",
"libgdk-pixbuf-2.0-0",
"libgles2",
"libglib2.0-0t64",
"libglx0",
"libgstreamer-gl1.0-0",
"libgstreamer-plugins-base1.0-0",
"libgstreamer1.0-0",
"libgtk-4-1",
"libgudev-1.0-0",
"libharfbuzz-icu0",
"libharfbuzz0b",
"libhyphen0",
"libicu76",
"libjpeg62-turbo",
"liblcms2-2",
"libmanette-0.2-0",
"libnotify4",
"libopengl0",
"libopenjp2-7",
"libopus0",
"libpango-1.0-0",
"libpng16-16t64",
"libproxy1v5",
"libsecret-1-0",
"libwayland-client0",
"libwayland-egl1",
"libwayland-server0",
"libwebp7",
"libwebpdemux2",
"libwoff1",
"libx11-6",
"libxcomposite1",
"libxdamage1",
"libxkbcommon0",
"libxml2",
"libxslt1.1",
"libatomic1",
"libevent-2.1-7t64",
"libavif16"
],
lib2package: {
"libicudata.so.74": "libicu76",
"libicui18n.so.74": "libicu76",
"libicuuc.so.74": "libicu76",
"libevent-2.1.so.7": "libevent-2.1-7t64",
"libpng16.so.16": "libpng16-16t64",
"libgdk-3.so.0": "libgtk-3-0t64",
"libgtk-3.so.0": "libgtk-3-0t64",
"libavif.so.16": "libavif16",
"libsoup-3.0.so.0": "libsoup-3.0-0",
"libasound.so.2": "libasound2t64",
"libatk-1.0.so.0": "libatk1.0-0t64",
"libatk-bridge-2.0.so.0": "libatk-bridge2.0-0t64",
"libatspi.so.0": "libatspi2.0-0t64",
"libcairo.so.2": "libcairo2",
"libcups.so.2": "libcups2t64",
"libdbus-1.so.3": "libdbus-1-3",
"libdrm.so.2": "libdrm2",
"libgbm.so.1": "libgbm1",
"libgio-2.0.so.0": "libglib2.0-0t64",
"libglib-2.0.so.0": "libglib2.0-0t64",
"libgobject-2.0.so.0": "libglib2.0-0t64",
"libnspr4.so": "libnspr4",
"libnss3.so": "libnss3",
"libnssutil3.so": "libnss3",
"libpango-1.0.so.0": "libpango-1.0-0",
"libsmime3.so": "libnss3",
"libX11.so.6": "libx11-6",
"libxcb.so.1": "libxcb1",
"libXcomposite.so.1": "libxcomposite1",
"libXdamage.so.1": "libxdamage1",
"libXext.so.6": "libxext6",
"libXfixes.so.3": "libxfixes3",
"libxkbcommon.so.0": "libxkbcommon0",
"libXrandr.so.2": "libxrandr2",
"libgtk-4.so.1": "libgtk-4-1"
}
}
};
deps["ubuntu20.04-arm64"] = {
tools: [...deps["ubuntu20.04-x64"].tools],
chromium: [...deps["ubuntu20.04-x64"].chromium],
firefox: [
...deps["ubuntu20.04-x64"].firefox
],
webkit: [
...deps["ubuntu20.04-x64"].webkit
],
lib2package: {
...deps["ubuntu20.04-x64"].lib2package
}
};
deps["ubuntu22.04-arm64"] = {
tools: [...deps["ubuntu22.04-x64"].tools],
chromium: [...deps["ubuntu22.04-x64"].chromium],
firefox: [
...deps["ubuntu22.04-x64"].firefox
],
webkit: [
...deps["ubuntu22.04-x64"].webkit
],
lib2package: {
...deps["ubuntu22.04-x64"].lib2package
}
};
deps["ubuntu24.04-arm64"] = {
tools: [...deps["ubuntu24.04-x64"].tools],
chromium: [...deps["ubuntu24.04-x64"].chromium],
firefox: [
...deps["ubuntu24.04-x64"].firefox
],
webkit: [
...deps["ubuntu24.04-x64"].webkit
],
lib2package: {
...deps["ubuntu24.04-x64"].lib2package
}
};
deps["debian11-arm64"] = {
tools: [...deps["debian11-x64"].tools],
chromium: [...deps["debian11-x64"].chromium],
firefox: [
...deps["debian11-x64"].firefox
],
webkit: [
...deps["debian11-x64"].webkit
],
lib2package: {
...deps["debian11-x64"].lib2package
}
};
deps["debian12-arm64"] = {
tools: [...deps["debian12-x64"].tools],
chromium: [...deps["debian12-x64"].chromium],
firefox: [
...deps["debian12-x64"].firefox
],
webkit: [
...deps["debian12-x64"].webkit
],
lib2package: {
...deps["debian12-x64"].lib2package
}
};
deps["debian13-arm64"] = {
tools: [...deps["debian13-x64"].tools],
chromium: [...deps["debian13-x64"].chromium],
firefox: [
...deps["debian13-x64"].firefox
],
webkit: [
...deps["debian13-x64"].webkit
],
lib2package: {
...deps["debian13-x64"].lib2package
}
};
}
});
// packages/playwright-core/src/server/registry/dependencies.ts
async function writeDockerVersion(dockerImageNameTemplate) {
await import_fs16.default.promises.mkdir(import_path16.default.dirname(dockerVersionFilePath), { recursive: true });
await import_fs16.default.promises.writeFile(dockerVersionFilePath, JSON.stringify(dockerVersion(dockerImageNameTemplate), null, 2), "utf8");
await import_fs16.default.promises.chmod(dockerVersionFilePath, 511);
}
function dockerVersion(dockerImageNameTemplate) {
return {
driverVersion: languageBindingVersion,
dockerImageName: dockerImageNameTemplate.replace("%version%", languageBindingVersion)
};
}
function readDockerVersionSync() {
try {
const data = JSON.parse(import_fs16.default.readFileSync(dockerVersionFilePath, "utf8"));
return {
...data,
dockerImageNameTemplate: data.dockerImageName.replace(data.driverVersion, "%version%")
};
} catch (e) {
return null;
}
}
function isSupportedWindowsVersion() {
if (import_os6.default.platform() !== "win32" || import_os6.default.arch() !== "x64")
return false;
const [major, minor] = import_os6.default.release().split(".").map((token) => parseInt(token, 10));
return major > 6 || major === 6 && minor > 1;
}
async function installDependenciesWindows(targets, dryRun) {
if (targets.has("chromium")) {
const command = "powershell.exe";
const args = ["-ExecutionPolicy", "Bypass", "-File", import_path16.default.join(binPath, "install_media_pack.ps1")];
if (dryRun) {
console.log(`${command} ${quoteProcessArgs(args).join(" ")}`);
return;
}
const { code } = await spawnAsync(command, args, { cwd: binPath, stdio: "inherit" });
if (code !== 0)
throw new Error("Failed to install windows dependencies!");
}
}
async function installDependenciesLinux(targets, dryRun) {
const libraries = [];
const platform = hostPlatform;
if (!isOfficiallySupportedPlatform)
console.warn(`BEWARE: your OS is not officially supported by Playwright; installing dependencies for ${platform} as a fallback.`);
for (const target of targets) {
const info = deps[platform];
if (!info) {
console.warn(`Cannot install dependencies for ${platform} with Playwright ${getPlaywrightVersion()}!`);
return;
}
libraries.push(...info[target]);
}
const uniqueLibraries = Array.from(new Set(libraries));
if (dryRun) {
await reportMissingDependenciesLinux(uniqueLibraries);
return;
}
console.log(`Installing dependencies...`);
const commands2 = [];
commands2.push("apt-get update");
commands2.push([
"apt-get",
"install",
"-y",
"--no-install-recommends",
...uniqueLibraries
].join(" "));
const { command, args, elevatedPermissions } = await transformCommandsForRoot(commands2);
if (elevatedPermissions)
console.log("Switching to root user to install dependencies...");
const child = childProcess2.spawn(command, args, { stdio: "inherit" });
await new Promise((resolve, reject) => {
child.on("exit", (code) => code === 0 ? resolve() : reject(new Error(`Installation process exited with code: ${code}`)));
child.on("error", reject);
});
}
async function reportMissingDependenciesLinux(packages) {
const { code, stdout, stderr, error } = await spawnAsync("apt-get", ["install", "-s", "--no-install-recommends", ...packages], {});
if (error)
throw new Error(`Failed to run 'apt-get install -s' to simulate dependency install: ${error.message}`);
if (code !== 0)
throw new Error(`'apt-get install -s' exited with code ${code}:
${stderr || stdout}`);
const missingPackages = [];
for (const line of stdout.split("\n")) {
const match = /^Inst (\S+) /.exec(line);
if (match)
missingPackages.push(match[1]);
}
if (!missingPackages.length) {
console.log("All system dependencies are installed.");
return;
}
console.log(`Missing system dependencies (${missingPackages.length}):
${missingPackages.sort().map((p) => ` ${p}`).join("\n")}`);
process.exitCode = 1;
}
async function validateDependenciesWindows(sdkLanguage, windowsExeAndDllDirectories) {
const directoryPaths = windowsExeAndDllDirectories;
const lddPaths = [];
for (const directoryPath of directoryPaths)
lddPaths.push(...await executablesOrSharedLibraries(directoryPath));
const allMissingDeps = await Promise.all(lddPaths.map((lddPath) => missingFileDependenciesWindows(sdkLanguage, lddPath)));
const missingDeps = /* @__PURE__ */ new Set();
for (const deps2 of allMissingDeps) {
for (const dep of deps2)
missingDeps.add(dep);
}
if (!missingDeps.size)
return;
let isCrtMissing = false;
let isMediaFoundationMissing = false;
for (const dep of missingDeps) {
if (dep.startsWith("api-ms-win-crt") || dep === "vcruntime140.dll" || dep === "vcruntime140_1.dll" || dep === "msvcp140.dll")
isCrtMissing = true;
else if (dep === "mf.dll" || dep === "mfplat.dll" || dep === "msmpeg2vdec.dll" || dep === "evr.dll" || dep === "avrt.dll")
isMediaFoundationMissing = true;
}
const details = [];
if (isCrtMissing) {
details.push(
`Some of the Universal C Runtime files cannot be found on the system. You can fix`,
`that by installing Microsoft Visual C++ Redistributable for Visual Studio from:`,
`https://support.microsoft.com/en-us/help/2977003/the-latest-supported-visual-c-downloads`,
``
);
}
if (isMediaFoundationMissing) {
details.push(
`Some of the Media Foundation files cannot be found on the system. If you are`,
`on Windows Server try fixing this by running the following command in PowerShell`,
`as Administrator:`,
``,
` Install-WindowsFeature Server-Media-Foundation`,
``,
`For Windows N editions visit:`,
`https://support.microsoft.com/en-us/help/3145500/media-feature-pack-list-for-windows-n-editions`,
``
);
}
details.push(
`Full list of missing libraries:`,
` ${[...missingDeps].join("\n ")}`,
``
);
const message = `Host system is missing dependencies!
${details.join("\n")}`;
if (isSupportedWindowsVersion()) {
throw new Error(message);
} else {
console.warn(`WARNING: running on unsupported windows version!`);
console.warn(message);
}
}
async function validateDependenciesLinux(sdkLanguage, linuxLddDirectories, dlOpenLibraries) {
const directoryPaths = linuxLddDirectories;
const lddPaths = [];
for (const directoryPath of directoryPaths)
lddPaths.push(...await executablesOrSharedLibraries(directoryPath));
const missingDepsPerFile = await Promise.all(lddPaths.map((lddPath) => missingFileDependencies(lddPath, directoryPaths)));
const missingDeps = /* @__PURE__ */ new Set();
for (const deps2 of missingDepsPerFile) {
for (const dep of deps2)
missingDeps.add(dep);
}
for (const dep of await missingDLOPENLibraries(dlOpenLibraries))
missingDeps.add(dep);
if (!missingDeps.size)
return;
const allMissingDeps = new Set(missingDeps);
const missingPackages = /* @__PURE__ */ new Set();
const libraryToPackageNameMapping = deps[hostPlatform] ? {
...deps[hostPlatform]?.lib2package || {},
...MANUAL_LIBRARY_TO_PACKAGE_NAME_UBUNTU
} : {};
for (const missingDep of missingDeps) {
const packageName = libraryToPackageNameMapping[missingDep];
if (packageName) {
missingPackages.add(packageName);
missingDeps.delete(missingDep);
}
}
const maybeSudo = process.getuid?.() && import_os6.default.platform() !== "win32" ? "sudo " : "";
const dockerInfo = readDockerVersionSync();
const errorLines = [
`Host system is missing dependencies to run browsers.`
];
if (dockerInfo && !dockerInfo.driverVersion.startsWith(getPlaywrightVersion(
true
/* majorMinorOnly */
) + ".")) {
const pwVersion = getPlaywrightVersion();
const requiredDockerImage = dockerInfo.dockerImageName.replace(dockerInfo.driverVersion, pwVersion);
errorLines.push(...[
`This is most likely due to Docker image version not matching Playwright version:`,
`- Playwright : ${pwVersion}`,
`- Docker image: ${dockerInfo.driverVersion}`,
``,
`Either:`,
`- (recommended) use Docker image "${requiredDockerImage}"`,
`- (alternative 1) run the following command inside Docker to install missing dependencies:`,
``,
` ${maybeSudo}${buildPlaywrightCLICommand(sdkLanguage, "install-deps")}`,
``,
`- (alternative 2) use apt inside Docker:`,
``,
` ${maybeSudo}apt-get install ${[...missingPackages].join("\\\n ")}`,
``,
`<3 Playwright Team`
]);
} else if (missingPackages.size && !missingDeps.size) {
errorLines.push(...[
`Please install them with the following command:`,
``,
` ${maybeSudo}${buildPlaywrightCLICommand(sdkLanguage, "install-deps")}`,
``,
`Alternatively, use apt:`,
` ${maybeSudo}apt-get install ${[...missingPackages].join("\\\n ")}`,
``,
`<3 Playwright Team`
]);
} else {
errorLines.push(...[
`Missing libraries:`,
...[...allMissingDeps].map((dep) => " " + dep)
]);
}
throw new Error("\n" + wrapInASCIIBox(errorLines.join("\n"), 1));
}
function isSharedLib(basename) {
switch (import_os6.default.platform()) {
case "linux":
return basename.endsWith(".so") || basename.includes(".so.");
case "win32":
return basename.endsWith(".dll");
default:
return false;
}
}
async function executablesOrSharedLibraries(directoryPath) {
if (!import_fs16.default.existsSync(directoryPath))
return [];
const allPaths = (await import_fs16.default.promises.readdir(directoryPath)).map((file) => import_path16.default.resolve(directoryPath, file));
const allStats = await Promise.all(allPaths.map((aPath) => import_fs16.default.promises.stat(aPath)));
const filePaths = allPaths.filter((aPath, index) => allStats[index].isFile());
const executablersOrLibraries = (await Promise.all(filePaths.map(async (filePath) => {
const basename = import_path16.default.basename(filePath).toLowerCase();
if (isSharedLib(basename))
return filePath;
if (await checkExecutable(filePath))
return filePath;
return false;
}))).filter(Boolean);
return executablersOrLibraries;
}
async function missingFileDependenciesWindows(sdkLanguage, filePath) {
const executable = registry.findExecutable("winldd").executablePathOrDie(sdkLanguage);
const dirname = import_path16.default.dirname(filePath);
const { stdout, code } = await spawnAsync(executable, [filePath], {
cwd: dirname,
env: {
...process.env,
LD_LIBRARY_PATH: process.env.LD_LIBRARY_PATH ? `${process.env.LD_LIBRARY_PATH}:${dirname}` : dirname
}
});
if (code !== 0)
return [];
const missingDeps = stdout.split("\n").map((line) => line.trim()).filter((line) => line.endsWith("not found") && line.includes("=>")).map((line) => line.split("=>")[0].trim().toLowerCase());
return missingDeps;
}
async function missingFileDependencies(filePath, extraLDPaths) {
const dirname = import_path16.default.dirname(filePath);
let LD_LIBRARY_PATH = extraLDPaths.join(":");
if (process.env.LD_LIBRARY_PATH)
LD_LIBRARY_PATH = `${process.env.LD_LIBRARY_PATH}:${LD_LIBRARY_PATH}`;
const { stdout, code } = await spawnAsync("ldd", [filePath], {
cwd: dirname,
env: {
...process.env,
LD_LIBRARY_PATH
}
});
if (code !== 0)
return [];
const missingDeps = stdout.split("\n").map((line) => line.trim()).filter((line) => line.endsWith("not found") && line.includes("=>")).map((line) => line.split("=>")[0].trim());
return missingDeps;
}
async function missingDLOPENLibraries(libraries) {
if (!libraries.length)
return [];
const { stdout, code, error } = await spawnAsync("/sbin/ldconfig", ["-p"], {});
if (code !== 0 || error)
return [];
const isLibraryAvailable = (library) => stdout.toLowerCase().includes(library.toLowerCase());
return libraries.filter((library) => !isLibraryAvailable(library));
}
function quoteProcessArgs(args) {
return args.map((arg) => {
if (arg.includes(" "))
return `"${arg}"`;
return arg;
});
}
async function transformCommandsForRoot(commands2) {
const isRoot = process.getuid?.() === 0;
if (isRoot)
return { command: "sh", args: ["-c", `${commands2.join("&& ")}`], elevatedPermissions: false };
const sudoExists = await spawnAsync("which", ["sudo"]);
if (sudoExists.code === 0)
return { command: "sudo", args: ["--", "sh", "-c", `${commands2.join("&& ")}`], elevatedPermissions: true };
return { command: "su", args: ["root", "-c", `${commands2.join("&& ")}`], elevatedPermissions: true };
}
var childProcess2, import_fs16, import_os6, import_path16, languageBindingVersion, dockerVersionFilePath, checkExecutable, MANUAL_LIBRARY_TO_PACKAGE_NAME_UBUNTU;
var init_dependencies = __esm({
"packages/playwright-core/src/server/registry/dependencies.ts"() {
"use strict";
childProcess2 = __toESM(require("child_process"));
import_fs16 = __toESM(require("fs"));
import_os6 = __toESM(require("os"));
import_path16 = __toESM(require("path"));
init_ascii();
init_hostPlatform();
init_spawnAsync();
init_userAgent();
init_nativeDeps();
init_package();
init_registry();
languageBindingVersion = process.env.PW_CLI_DISPLAY_VERSION || packageJSON.version;
dockerVersionFilePath = "/ms-playwright/.docker-info";
checkExecutable = (filePath) => {
if (process.platform === "win32")
return filePath.endsWith(".exe");
return import_fs16.default.promises.access(filePath, import_fs16.default.constants.X_OK).then(() => true).catch(() => false);
};
MANUAL_LIBRARY_TO_PACKAGE_NAME_UBUNTU = {
// libgstlibav.so (the only actual library provided by gstreamer1.0-libav) is not
// in the ldconfig cache, so we detect the actual library required for playing h.264
// and if it's missing recommend installing missing gstreamer lib.
// gstreamer1.0-libav -> libavcodec57 -> libx264-152
"libx264.so": "gstreamer1.0-libav"
};
}
});
// packages/playwright-core/src/server/registry/browserFetcher.ts
async function downloadBrowserWithProgressBar(title, browserDirectory, executablePath, downloadURLs, downloadFileName, downloadSocketTimeout, force) {
if (await existsAsync(browserDirectoryToMarkerFilePath(browserDirectory))) {
debugLogger.log("install", `${title} is already downloaded.`);
if (force)
debugLogger.log("install", `force-downloading ${title}.`);
else
return;
}
const uniqueTempDir = await import_fs17.default.promises.mkdtemp(import_path17.default.join(import_os7.default.tmpdir(), "playwright-download-"));
const zipPath = import_path17.default.join(uniqueTempDir, downloadFileName);
try {
const retryCount = 5;
for (let attempt = 1; attempt <= retryCount; ++attempt) {
debugLogger.log("install", `downloading ${title} - attempt #${attempt}`);
const url2 = downloadURLs[(attempt - 1) % downloadURLs.length];
logPolitely(`Downloading ${title}` + colors3.dim(` from ${url2}`));
const { error } = await downloadBrowserWithProgressBarOutOfProcess(title, browserDirectory, url2, zipPath, executablePath, downloadSocketTimeout);
if (!error) {
debugLogger.log("install", `SUCCESS installing ${title}`);
break;
}
if (await existsAsync(zipPath))
await import_fs17.default.promises.unlink(zipPath);
if (await existsAsync(browserDirectory))
await removeFolders([browserDirectory]);
const errorMessage = error?.message || "";
debugLogger.log("install", `attempt #${attempt} - ERROR: ${errorMessage}`);
if (attempt >= retryCount)
throw error;
}
} catch (e) {
debugLogger.log("install", `FAILED installation ${title} with error: ${e}`);
process.exitCode = 1;
throw e;
} finally {
await removeFolders([uniqueTempDir]);
}
logPolitely(`${title} downloaded to ${browserDirectory}`);
}
function downloadBrowserWithProgressBarOutOfProcess(title, browserDirectory, url2, zipPath, executablePath, socketTimeout) {
const cp = childProcess3.fork(libPath("entry", "oopBrowserDownload.js"));
const promise = new ManualPromise();
const progress2 = getDownloadProgress();
cp.on("message", (message) => {
if (message?.method === "log")
debugLogger.log("install", message.params.message);
if (message?.method === "progress")
progress2(message.params.done, message.params.total);
});
cp.on("exit", (code) => {
if (code !== 0) {
promise.resolve({ error: new Error(`Download failure, code=${code}`) });
return;
}
if (!import_fs17.default.existsSync(browserDirectoryToMarkerFilePath(browserDirectory)))
promise.resolve({ error: new Error(`Download failure, ${browserDirectoryToMarkerFilePath(browserDirectory)} does not exist`) });
else
promise.resolve({ error: null });
});
cp.on("error", (error) => {
promise.resolve({ error });
});
debugLogger.log("install", `running download:`);
debugLogger.log("install", `-- from url: ${url2}`);
debugLogger.log("install", `-- to location: ${zipPath}`);
const downloadParams = {
title,
browserDirectory,
url: url2,
zipPath,
executablePath,
socketTimeout,
userAgent: getUserAgent()
};
cp.send({ method: "download", params: downloadParams });
return promise;
}
function logPolitely(toBeLogged) {
const logLevel = process.env.npm_config_loglevel;
const logLevelDisplay = ["silent", "error", "warn"].indexOf(logLevel || "") > -1;
if (!logLevelDisplay)
console.log(toBeLogged);
}
function getDownloadProgress() {
if (process.stdout.isTTY)
return getAnimatedDownloadProgress();
return getBasicDownloadProgress();
}
function getAnimatedDownloadProgress() {
let progressBar;
let lastDownloadedBytes = 0;
return (downloadedBytes, totalBytes) => {
if (!progressBar) {
progressBar = new ProgressBar(
`${toMegabytes(
totalBytes
)} [:bar] :percent :etas`,
{
complete: "=",
incomplete: " ",
width: 20,
total: totalBytes
}
);
}
const delta = downloadedBytes - lastDownloadedBytes;
lastDownloadedBytes = downloadedBytes;
progressBar.tick(delta);
};
}
function getBasicDownloadProgress() {
const totalRows = 10;
const stepWidth = 8;
let lastRow = -1;
return (downloadedBytes, totalBytes) => {
const percentage = downloadedBytes / totalBytes;
const row = Math.floor(totalRows * percentage);
if (row > lastRow) {
lastRow = row;
const percentageString = String(percentage * 100 | 0).padStart(3);
console.log(`|${"\u25A0".repeat(row * stepWidth)}${" ".repeat((totalRows - row) * stepWidth)}| ${percentageString}% of ${toMegabytes(totalBytes)}`);
}
};
}
function toMegabytes(bytes) {
const mb = bytes / 1024 / 1024;
return `${Math.round(mb * 10) / 10} MiB`;
}
var childProcess3, import_fs17, import_os7, import_path17, ProgressBar, colors3;
var init_browserFetcher = __esm({
"packages/playwright-core/src/server/registry/browserFetcher.ts"() {
"use strict";
childProcess3 = __toESM(require("child_process"));
import_fs17 = __toESM(require("fs"));
import_os7 = __toESM(require("os"));
import_path17 = __toESM(require("path"));
init_manualPromise();
init_debugLogger();
init_fileUtils();
init_userAgent();
init_package();
init_registry();
ProgressBar = require("./utilsBundle").progress;
colors3 = require("./utilsBundle").colors;
}
});
// packages/playwright-core/src/server/registry/oopDownloadBrowserMain.ts
function log(message) {
process.send?.({ method: "log", params: { message } });
}
function progress(done, total) {
process.send?.({ method: "progress", params: { done, total } });
}
function browserDirectoryToMarkerFilePath2(browserDirectory) {
return import_path18.default.join(browserDirectory, "INSTALLATION_COMPLETE");
}
function downloadFile(options2) {
let downloadedBytes = 0;
let totalBytes = 0;
let chunked = false;
const promise = new ManualPromise();
httpRequest({
url: options2.url,
headers: {
"User-Agent": options2.userAgent
},
socketTimeout: options2.socketTimeout
}, (response2) => {
log(`-- response status code: ${response2.statusCode}`);
if (response2.statusCode !== 200) {
let content = "";
const handleError = () => {
const error = new Error(`Download failed: server returned code ${response2.statusCode} body '${content}'. URL: ${options2.url}`);
response2.resume();
promise.reject(error);
};
response2.on("data", (chunk) => content += chunk).on("end", handleError).on("error", handleError);
return;
}
chunked = response2.headers["transfer-encoding"] === "chunked";
log(`-- is chunked: ${chunked}`);
totalBytes = parseInt(response2.headers["content-length"] || "0", 10);
log(`-- total bytes: ${totalBytes}`);
const file = import_fs18.default.createWriteStream(options2.zipPath);
file.on("finish", () => {
if (!chunked && downloadedBytes !== totalBytes) {
log(`-- download failed, size mismatch: ${downloadedBytes} != ${totalBytes}`);
promise.reject(new Error(`Download failed: size mismatch, file size: ${downloadedBytes}, expected size: ${totalBytes} URL: ${options2.url}`));
} else {
log(`-- download complete, size: ${downloadedBytes}`);
promise.resolve();
}
});
file.on("error", (error) => promise.reject(error));
response2.pipe(file);
response2.on("data", onData);
response2.on("error", (error) => {
file.close();
if (error?.code === "ECONNRESET") {
log(`-- download failed, server closed connection`);
promise.reject(new Error(`Download failed: server closed connection. URL: ${options2.url}`));
} else {
log(`-- download failed, unexpected error`);
promise.reject(new Error(`Download failed: ${error?.message ?? error}. URL: ${options2.url}`));
}
});
}, (error) => promise.reject(error));
return promise;
function onData(chunk) {
downloadedBytes += chunk.length;
if (!chunked)
progress(downloadedBytes, totalBytes);
}
}
async function main(options2) {
await downloadFile(options2);
log(`SUCCESS downloading ${options2.title}`);
log(`removing existing browser directory if any`);
await removeFolders([options2.browserDirectory]);
log(`extracting archive`);
await extractZip(options2.zipPath, { dir: options2.browserDirectory });
if (options2.executablePath) {
log(`fixing permissions at ${options2.executablePath}`);
await import_fs18.default.promises.chmod(options2.executablePath, 493);
}
await import_fs18.default.promises.writeFile(browserDirectoryToMarkerFilePath2(options2.browserDirectory), "");
}
function runOopDownloadBrowserMain() {
process.on("message", async (message) => {
const { method, params: params2 } = message;
if (method === "download") {
try {
await main(params2);
process.exit(0);
} catch (e) {
console.error(e);
process.exit(1);
}
}
});
process.on("disconnect", () => {
process.exit(0);
});
}
var import_fs18, import_path18;
var init_oopDownloadBrowserMain = __esm({
"packages/playwright-core/src/server/registry/oopDownloadBrowserMain.ts"() {
"use strict";
import_fs18 = __toESM(require("fs"));
import_path18 = __toESM(require("path"));
init_manualPromise();
init_network();
init_fileUtils();
init_extractZip();
}
});
// packages/playwright-core/src/server/registry/index.ts
var registry_exports = {};
__export(registry_exports, {
Registry: () => Registry,
browserDirectoryToMarkerFilePath: () => browserDirectoryToMarkerFilePath,
buildPlaywrightCLICommand: () => buildPlaywrightCLICommand,
defaultCacheDirectory: () => defaultCacheDirectory,
defaultRegistryDirectory: () => defaultRegistryDirectory,
findChromiumChannelBestEffort: () => findChromiumChannelBestEffort,
installBrowsersForNpmInstall: () => installBrowsersForNpmInstall,
registry: () => registry,
registryDirectory: () => registryDirectory,
runOopDownloadBrowserMain: () => runOopDownloadBrowserMain,
writeDockerVersion: () => writeDockerVersion
});
function cftUrl(suffix) {
return ({ browserVersion }) => {
return {
path: `builds/cft/${browserVersion}/${suffix}`,
mirrors: [
"https://cdn.playwright.dev"
]
};
};
}
function isBrowserDirectory(browserDirectory) {
const baseName = import_path19.default.basename(browserDirectory);
for (const browserName of allDownloadableDirectoriesThatEverExisted) {
if (baseName.startsWith(browserName.replace(/-/g, "_") + "-"))
return true;
}
return false;
}
function readDescriptors(browsersJSON) {
return browsersJSON["browsers"].map((obj) => {
const name = obj.name;
const revisionOverride = (obj.revisionOverrides || {})[hostPlatform];
const revision = revisionOverride || obj.revision;
const browserDirectoryPrefix = revisionOverride ? `${name}_${hostPlatform}_special` : `${name}`;
const descriptor = {
name,
revision,
hasRevisionOverride: !!revisionOverride,
// We only put browser version for the supported operating systems.
browserVersion: revisionOverride ? void 0 : obj.browserVersion,
title: obj["title"],
installByDefault: !!obj.installByDefault,
// Method `isBrowserDirectory` determines directory to be browser iff
// it starts with some browser name followed by '-'. Some browser names
// are prefixes of others, e.g. 'webkit' is a prefix of `webkit-technology-preview`.
// To avoid older registries erroneously removing 'webkit-technology-preview', we have to
// ensure that browser folders to never include dashes inside.
dir: import_path19.default.join(registryDirectory, browserDirectoryPrefix.replace(/-/g, "_") + "-" + revision)
};
return descriptor;
});
}
function browserDirectoryToMarkerFilePath(browserDirectory) {
return import_path19.default.join(browserDirectory, "INSTALLATION_COMPLETE");
}
function buildPlaywrightCLICommand(sdkLanguage, parameters) {
switch (sdkLanguage) {
case "python":
return `playwright ${parameters}`;
case "java":
return `mvn exec:java -e -D exec.mainClass=com.microsoft.playwright.CLI -D exec.args="${parameters}"`;
case "csharp":
return `pwsh bin/Debug/netX/playwright.ps1 ${parameters}`;
default: {
const packageManagerCommand = getPackageManagerExecCommand();
return `${packageManagerCommand} playwright ${parameters}`;
}
}
}
async function installBrowsersForNpmInstall(browsers) {
if (getAsBooleanFromENV("PLAYWRIGHT_SKIP_BROWSER_DOWNLOAD")) {
logPolitely("Skipping browsers download because `PLAYWRIGHT_SKIP_BROWSER_DOWNLOAD` env variable is set");
return false;
}
const executables = [];
if (process.platform === "win32")
executables.push(registry.findExecutable("winldd"));
for (const browserName of browsers) {
const executable = registry.findExecutable(browserName);
if (!executable || executable.installType === "none")
throw new Error(`Cannot install ${browserName}`);
executables.push(executable);
}
await registry.install(executables);
}
function findChromiumChannelBestEffort(sdkLanguage) {
let channel = null;
for (const name of ["chromium", "chrome", "msedge"]) {
try {
registry.findExecutable(name).executablePathOrDie(sdkLanguage);
channel = name === "chromium" ? void 0 : name;
break;
} catch (e) {
}
}
if (channel === null) {
const installCommand = buildPlaywrightCLICommand(sdkLanguage, `install chromium`);
const prettyMessage = [
`No chromium-based browser found on the system.`,
`Please run the following command to download one:`,
``,
` ${installCommand}`,
``,
`<3 Playwright Team`
].join("\n");
throw new Error("\n" + wrapInASCIIBox(prettyMessage, 1));
}
return channel;
}
function lowercaseAllKeys(json) {
if (typeof json !== "object" || !json)
return json;
if (Array.isArray(json))
return json.map(lowercaseAllKeys);
const result2 = {};
for (const [key, value2] of Object.entries(json))
result2[key.toLowerCase()] = lowercaseAllKeys(value2);
return result2;
}
var import_fs19, import_os8, import_path19, util2, PACKAGE_PATH, BIN_PATH, PLAYWRIGHT_CDN_MIRRORS, EXECUTABLE_PATHS, DOWNLOAD_PATHS, defaultCacheDirectory, defaultRegistryDirectory, registryDirectory, allDownloadableDirectoriesThatEverExisted, chromiumAliases, Registry, registry;
var init_registry = __esm({
"packages/playwright-core/src/server/registry/index.ts"() {
"use strict";
import_fs19 = __toESM(require("fs"));
import_os8 = __toESM(require("os"));
import_path19 = __toESM(require("path"));
util2 = __toESM(require("util"));
init_ascii();
init_debugLogger();
init_hostPlatform();
init_network();
init_spawnAsync();
init_fileUtils();
init_crypto();
init_env();
init_lockfile();
init_utils2();
init_userAgent();
init_dependencies();
init_dependencies();
init_browserFetcher();
init_package();
init_dependencies();
init_oopDownloadBrowserMain();
PACKAGE_PATH = packageRoot;
BIN_PATH = binPath;
PLAYWRIGHT_CDN_MIRRORS = [
"https://cdn.playwright.dev/dbazure/download/playwright",
// ESRP CDN
"https://playwright.download.prss.microsoft.com/dbazure/download/playwright",
// Directly hit ESRP CDN
"https://cdn.playwright.dev"
// Hit the Storage Bucket directly
];
if (process.env.PW_TEST_CDN_THAT_SHOULD_WORK) {
for (let i = 0; i < PLAYWRIGHT_CDN_MIRRORS.length; i++) {
const cdn = PLAYWRIGHT_CDN_MIRRORS[i];
if (cdn !== process.env.PW_TEST_CDN_THAT_SHOULD_WORK) {
const parsedCDN = new URL(cdn);
parsedCDN.hostname = parsedCDN.hostname + ".does-not-resolve.playwright.dev";
PLAYWRIGHT_CDN_MIRRORS[i] = parsedCDN.toString();
}
}
}
EXECUTABLE_PATHS = {
"chromium": {
"<unknown>": void 0,
"linux-x64": ["chrome-linux64", "chrome"],
"linux-arm64": ["chrome-linux", "chrome"],
// non-cft build
"mac-x64": ["chrome-mac-x64", "Google Chrome for Testing.app", "Contents", "MacOS", "Google Chrome for Testing"],
"mac-arm64": ["chrome-mac-arm64", "Google Chrome for Testing.app", "Contents", "MacOS", "Google Chrome for Testing"],
"win-x64": ["chrome-win64", "chrome.exe"]
},
"chromium-headless-shell": {
"<unknown>": void 0,
"linux-x64": ["chrome-headless-shell-linux64", "chrome-headless-shell"],
"linux-arm64": ["chrome-linux", "headless_shell"],
// non-cft build
"mac-x64": ["chrome-headless-shell-mac-x64", "chrome-headless-shell"],
"mac-arm64": ["chrome-headless-shell-mac-arm64", "chrome-headless-shell"],
"win-x64": ["chrome-headless-shell-win64", "chrome-headless-shell.exe"]
},
"chromium-tip-of-tree": {
"<unknown>": void 0,
"linux-x64": ["chrome-linux64", "chrome"],
"linux-arm64": ["chrome-linux", "chrome"],
// non-cft build
"mac-x64": ["chrome-mac-x64", "Google Chrome for Testing.app", "Contents", "MacOS", "Google Chrome for Testing"],
"mac-arm64": ["chrome-mac-arm64", "Google Chrome for Testing.app", "Contents", "MacOS", "Google Chrome for Testing"],
"win-x64": ["chrome-win64", "chrome.exe"]
},
"chromium-tip-of-tree-headless-shell": {
"<unknown>": void 0,
"linux-x64": ["chrome-headless-shell-linux64", "chrome-headless-shell"],
"linux-arm64": ["chrome-linux", "headless_shell"],
// non-cft build
"mac-x64": ["chrome-headless-shell-mac-x64", "chrome-headless-shell"],
"mac-arm64": ["chrome-headless-shell-mac-arm64", "chrome-headless-shell"],
"win-x64": ["chrome-headless-shell-win64", "chrome-headless-shell.exe"]
},
"firefox": {
"<unknown>": void 0,
"linux-x64": ["firefox", "firefox"],
"linux-arm64": ["firefox", "firefox"],
"mac-x64": ["firefox", "Nightly.app", "Contents", "MacOS", "firefox"],
"mac-arm64": ["firefox", "Nightly.app", "Contents", "MacOS", "firefox"],
"win-x64": ["firefox", "firefox.exe"]
},
"webkit": {
"<unknown>": void 0,
"linux-x64": ["pw_run.sh"],
"linux-arm64": ["pw_run.sh"],
"mac-x64": ["pw_run.sh"],
"mac-arm64": ["pw_run.sh"],
"win-x64": ["Playwright.exe"]
},
"ffmpeg": {
"<unknown>": void 0,
"linux-x64": ["ffmpeg-linux"],
"linux-arm64": ["ffmpeg-linux"],
"mac-x64": ["ffmpeg-mac"],
"mac-arm64": ["ffmpeg-mac"],
"win-x64": ["ffmpeg-win64.exe"]
},
"winldd": {
"<unknown>": void 0,
"linux-x64": void 0,
"linux-arm64": void 0,
"mac-x64": void 0,
"mac-arm64": void 0,
"win-x64": ["PrintDeps.exe"]
}
};
DOWNLOAD_PATHS = {
"chromium": {
"<unknown>": void 0,
"ubuntu18.04-x64": void 0,
"ubuntu20.04-x64": cftUrl("linux64/chrome-linux64.zip"),
"ubuntu22.04-x64": cftUrl("linux64/chrome-linux64.zip"),
"ubuntu24.04-x64": cftUrl("linux64/chrome-linux64.zip"),
"ubuntu18.04-arm64": void 0,
"ubuntu20.04-arm64": "builds/chromium/%s/chromium-linux-arm64.zip",
"ubuntu22.04-arm64": "builds/chromium/%s/chromium-linux-arm64.zip",
"ubuntu24.04-arm64": "builds/chromium/%s/chromium-linux-arm64.zip",
"debian11-x64": cftUrl("linux64/chrome-linux64.zip"),
"debian11-arm64": "builds/chromium/%s/chromium-linux-arm64.zip",
"debian12-x64": cftUrl("linux64/chrome-linux64.zip"),
"debian12-arm64": "builds/chromium/%s/chromium-linux-arm64.zip",
"debian13-x64": cftUrl("linux64/chrome-linux64.zip"),
"debian13-arm64": "builds/chromium/%s/chromium-linux-arm64.zip",
"mac10.13": cftUrl("mac-x64/chrome-mac-x64.zip"),
"mac10.14": cftUrl("mac-x64/chrome-mac-x64.zip"),
"mac10.15": cftUrl("mac-x64/chrome-mac-x64.zip"),
"mac11": cftUrl("mac-x64/chrome-mac-x64.zip"),
"mac11-arm64": cftUrl("mac-arm64/chrome-mac-arm64.zip"),
"mac12": cftUrl("mac-x64/chrome-mac-x64.zip"),
"mac12-arm64": cftUrl("mac-arm64/chrome-mac-arm64.zip"),
"mac13": cftUrl("mac-x64/chrome-mac-x64.zip"),
"mac13-arm64": cftUrl("mac-arm64/chrome-mac-arm64.zip"),
"mac14": cftUrl("mac-x64/chrome-mac-x64.zip"),
"mac14-arm64": cftUrl("mac-arm64/chrome-mac-arm64.zip"),
"mac15": cftUrl("mac-x64/chrome-mac-x64.zip"),
"mac15-arm64": cftUrl("mac-arm64/chrome-mac-arm64.zip"),
"mac26": cftUrl("mac-x64/chrome-mac-x64.zip"),
"mac26-arm64": cftUrl("mac-arm64/chrome-mac-arm64.zip"),
"win64": cftUrl("win64/chrome-win64.zip")
},
"chromium-headless-shell": {
"<unknown>": void 0,
"ubuntu18.04-x64": void 0,
"ubuntu20.04-x64": cftUrl("linux64/chrome-headless-shell-linux64.zip"),
"ubuntu22.04-x64": cftUrl("linux64/chrome-headless-shell-linux64.zip"),
"ubuntu24.04-x64": cftUrl("linux64/chrome-headless-shell-linux64.zip"),
"ubuntu18.04-arm64": void 0,
"ubuntu20.04-arm64": "builds/chromium/%s/chromium-headless-shell-linux-arm64.zip",
"ubuntu22.04-arm64": "builds/chromium/%s/chromium-headless-shell-linux-arm64.zip",
"ubuntu24.04-arm64": "builds/chromium/%s/chromium-headless-shell-linux-arm64.zip",
"debian11-x64": cftUrl("linux64/chrome-headless-shell-linux64.zip"),
"debian11-arm64": "builds/chromium/%s/chromium-headless-shell-linux-arm64.zip",
"debian12-x64": cftUrl("linux64/chrome-headless-shell-linux64.zip"),
"debian12-arm64": "builds/chromium/%s/chromium-headless-shell-linux-arm64.zip",
"debian13-x64": cftUrl("linux64/chrome-headless-shell-linux64.zip"),
"debian13-arm64": "builds/chromium/%s/chromium-headless-shell-linux-arm64.zip",
"mac10.13": void 0,
"mac10.14": void 0,
"mac10.15": void 0,
"mac11": cftUrl("mac-x64/chrome-headless-shell-mac-x64.zip"),
"mac11-arm64": cftUrl("mac-arm64/chrome-headless-shell-mac-arm64.zip"),
"mac12": cftUrl("mac-x64/chrome-headless-shell-mac-x64.zip"),
"mac12-arm64": cftUrl("mac-arm64/chrome-headless-shell-mac-arm64.zip"),
"mac13": cftUrl("mac-x64/chrome-headless-shell-mac-x64.zip"),
"mac13-arm64": cftUrl("mac-arm64/chrome-headless-shell-mac-arm64.zip"),
"mac14": cftUrl("mac-x64/chrome-headless-shell-mac-x64.zip"),
"mac14-arm64": cftUrl("mac-arm64/chrome-headless-shell-mac-arm64.zip"),
"mac15": cftUrl("mac-x64/chrome-headless-shell-mac-x64.zip"),
"mac15-arm64": cftUrl("mac-arm64/chrome-headless-shell-mac-arm64.zip"),
"mac26": cftUrl("mac-x64/chrome-headless-shell-mac-x64.zip"),
"mac26-arm64": cftUrl("mac-arm64/chrome-headless-shell-mac-arm64.zip"),
"win64": cftUrl("win64/chrome-headless-shell-win64.zip")
},
"chromium-tip-of-tree": {
"<unknown>": void 0,
"ubuntu18.04-x64": void 0,
"ubuntu20.04-x64": cftUrl("linux64/chrome-linux64.zip"),
"ubuntu22.04-x64": cftUrl("linux64/chrome-linux64.zip"),
"ubuntu24.04-x64": cftUrl("linux64/chrome-linux64.zip"),
"ubuntu18.04-arm64": void 0,
"ubuntu20.04-arm64": "builds/chromium-tip-of-tree/%s/chromium-tip-of-tree-linux-arm64.zip",
"ubuntu22.04-arm64": "builds/chromium-tip-of-tree/%s/chromium-tip-of-tree-linux-arm64.zip",
"ubuntu24.04-arm64": "builds/chromium-tip-of-tree/%s/chromium-tip-of-tree-linux-arm64.zip",
"debian11-x64": cftUrl("linux64/chrome-linux64.zip"),
"debian11-arm64": "builds/chromium-tip-of-tree/%s/chromium-tip-of-tree-linux-arm64.zip",
"debian12-x64": cftUrl("linux64/chrome-linux64.zip"),
"debian12-arm64": "builds/chromium-tip-of-tree/%s/chromium-tip-of-tree-linux-arm64.zip",
"debian13-x64": cftUrl("linux64/chrome-linux64.zip"),
"debian13-arm64": "builds/chromium-tip-of-tree/%s/chromium-tip-of-tree-linux-arm64.zip",
"mac10.13": cftUrl("mac-x64/chrome-mac-x64.zip"),
"mac10.14": cftUrl("mac-x64/chrome-mac-x64.zip"),
"mac10.15": cftUrl("mac-x64/chrome-mac-x64.zip"),
"mac11": cftUrl("mac-x64/chrome-mac-x64.zip"),
"mac11-arm64": cftUrl("mac-arm64/chrome-mac-arm64.zip"),
"mac12": cftUrl("mac-x64/chrome-mac-x64.zip"),
"mac12-arm64": cftUrl("mac-arm64/chrome-mac-arm64.zip"),
"mac13": cftUrl("mac-x64/chrome-mac-x64.zip"),
"mac13-arm64": cftUrl("mac-arm64/chrome-mac-arm64.zip"),
"mac14": cftUrl("mac-x64/chrome-mac-x64.zip"),
"mac14-arm64": cftUrl("mac-arm64/chrome-mac-arm64.zip"),
"mac15": cftUrl("mac-x64/chrome-mac-x64.zip"),
"mac15-arm64": cftUrl("mac-arm64/chrome-mac-arm64.zip"),
"mac26": cftUrl("mac-x64/chrome-mac-x64.zip"),
"mac26-arm64": cftUrl("mac-arm64/chrome-mac-arm64.zip"),
"win64": cftUrl("win64/chrome-win64.zip")
},
"chromium-tip-of-tree-headless-shell": {
"<unknown>": void 0,
"ubuntu18.04-x64": void 0,
"ubuntu20.04-x64": cftUrl("linux64/chrome-headless-shell-linux64.zip"),
"ubuntu22.04-x64": cftUrl("linux64/chrome-headless-shell-linux64.zip"),
"ubuntu24.04-x64": cftUrl("linux64/chrome-headless-shell-linux64.zip"),
"ubuntu18.04-arm64": void 0,
"ubuntu20.04-arm64": "builds/chromium-tip-of-tree/%s/chromium-tip-of-tree-headless-shell-linux-arm64.zip",
"ubuntu22.04-arm64": "builds/chromium-tip-of-tree/%s/chromium-tip-of-tree-headless-shell-linux-arm64.zip",
"ubuntu24.04-arm64": "builds/chromium-tip-of-tree/%s/chromium-tip-of-tree-headless-shell-linux-arm64.zip",
"debian11-x64": cftUrl("linux64/chrome-headless-shell-linux64.zip"),
"debian11-arm64": "builds/chromium-tip-of-tree/%s/chromium-tip-of-tree-headless-shell-linux-arm64.zip",
"debian12-x64": cftUrl("linux64/chrome-headless-shell-linux64.zip"),
"debian12-arm64": "builds/chromium-tip-of-tree/%s/chromium-tip-of-tree-headless-shell-linux-arm64.zip",
"debian13-x64": cftUrl("linux64/chrome-headless-shell-linux64.zip"),
"debian13-arm64": "builds/chromium-tip-of-tree/%s/chromium-tip-of-tree-headless-shell-linux-arm64.zip",
"mac10.13": void 0,
"mac10.14": void 0,
"mac10.15": void 0,
"mac11": cftUrl("mac-x64/chrome-headless-shell-mac-x64.zip"),
"mac11-arm64": cftUrl("mac-arm64/chrome-headless-shell-mac-arm64.zip"),
"mac12": cftUrl("mac-x64/chrome-headless-shell-mac-x64.zip"),
"mac12-arm64": cftUrl("mac-arm64/chrome-headless-shell-mac-arm64.zip"),
"mac13": cftUrl("mac-x64/chrome-headless-shell-mac-x64.zip"),
"mac13-arm64": cftUrl("mac-arm64/chrome-headless-shell-mac-arm64.zip"),
"mac14": cftUrl("mac-x64/chrome-headless-shell-mac-x64.zip"),
"mac14-arm64": cftUrl("mac-arm64/chrome-headless-shell-mac-arm64.zip"),
"mac15": cftUrl("mac-x64/chrome-headless-shell-mac-x64.zip"),
"mac15-arm64": cftUrl("mac-arm64/chrome-headless-shell-mac-arm64.zip"),
"mac26": cftUrl("mac-x64/chrome-headless-shell-mac-x64.zip"),
"mac26-arm64": cftUrl("mac-arm64/chrome-headless-shell-mac-arm64.zip"),
"win64": cftUrl("win64/chrome-headless-shell-win64.zip")
},
"firefox": {
"<unknown>": void 0,
"ubuntu18.04-x64": void 0,
"ubuntu20.04-x64": "builds/firefox/%s/firefox-ubuntu-20.04.zip",
"ubuntu22.04-x64": "builds/firefox/%s/firefox-ubuntu-22.04.zip",
"ubuntu24.04-x64": "builds/firefox/%s/firefox-ubuntu-24.04.zip",
"ubuntu18.04-arm64": void 0,
"ubuntu20.04-arm64": "builds/firefox/%s/firefox-ubuntu-20.04-arm64.zip",
"ubuntu22.04-arm64": "builds/firefox/%s/firefox-ubuntu-22.04-arm64.zip",
"ubuntu24.04-arm64": "builds/firefox/%s/firefox-ubuntu-24.04-arm64.zip",
"debian11-x64": "builds/firefox/%s/firefox-debian-11.zip",
"debian11-arm64": "builds/firefox/%s/firefox-debian-11-arm64.zip",
"debian12-x64": "builds/firefox/%s/firefox-debian-12.zip",
"debian12-arm64": "builds/firefox/%s/firefox-debian-12-arm64.zip",
"debian13-x64": "builds/firefox/%s/firefox-debian-13.zip",
"debian13-arm64": "builds/firefox/%s/firefox-debian-13-arm64.zip",
"mac10.13": "builds/firefox/%s/firefox-mac.zip",
"mac10.14": "builds/firefox/%s/firefox-mac.zip",
"mac10.15": "builds/firefox/%s/firefox-mac.zip",
"mac11": "builds/firefox/%s/firefox-mac.zip",
"mac11-arm64": "builds/firefox/%s/firefox-mac-arm64.zip",
"mac12": "builds/firefox/%s/firefox-mac.zip",
"mac12-arm64": "builds/firefox/%s/firefox-mac-arm64.zip",
"mac13": "builds/firefox/%s/firefox-mac.zip",
"mac13-arm64": "builds/firefox/%s/firefox-mac-arm64.zip",
"mac14": "builds/firefox/%s/firefox-mac.zip",
"mac14-arm64": "builds/firefox/%s/firefox-mac-arm64.zip",
"mac15": "builds/firefox/%s/firefox-mac.zip",
"mac15-arm64": "builds/firefox/%s/firefox-mac-arm64.zip",
"mac26": "builds/firefox/%s/firefox-mac.zip",
"mac26-arm64": "builds/firefox/%s/firefox-mac-arm64.zip",
"win64": "builds/firefox/%s/firefox-win64.zip"
},
"firefox-beta": {
"<unknown>": void 0,
"ubuntu18.04-x64": void 0,
"ubuntu20.04-x64": "builds/firefox-beta/%s/firefox-beta-ubuntu-20.04.zip",
"ubuntu22.04-x64": "builds/firefox-beta/%s/firefox-beta-ubuntu-22.04.zip",
"ubuntu24.04-x64": "builds/firefox-beta/%s/firefox-beta-ubuntu-24.04.zip",
"ubuntu18.04-arm64": void 0,
"ubuntu20.04-arm64": void 0,
"ubuntu22.04-arm64": "builds/firefox-beta/%s/firefox-beta-ubuntu-22.04-arm64.zip",
"ubuntu24.04-arm64": "builds/firefox-beta/%s/firefox-beta-ubuntu-24.04-arm64.zip",
"debian11-x64": "builds/firefox-beta/%s/firefox-beta-debian-11.zip",
"debian11-arm64": "builds/firefox-beta/%s/firefox-beta-debian-11-arm64.zip",
"debian12-x64": "builds/firefox-beta/%s/firefox-beta-debian-12.zip",
"debian12-arm64": "builds/firefox-beta/%s/firefox-beta-debian-12-arm64.zip",
"debian13-x64": "builds/firefox-beta/%s/firefox-beta-debian-12.zip",
"debian13-arm64": "builds/firefox-beta/%s/firefox-beta-debian-12-arm64.zip",
"mac10.13": "builds/firefox-beta/%s/firefox-beta-mac.zip",
"mac10.14": "builds/firefox-beta/%s/firefox-beta-mac.zip",
"mac10.15": "builds/firefox-beta/%s/firefox-beta-mac.zip",
"mac11": "builds/firefox-beta/%s/firefox-beta-mac.zip",
"mac11-arm64": "builds/firefox-beta/%s/firefox-beta-mac-arm64.zip",
"mac12": "builds/firefox-beta/%s/firefox-beta-mac.zip",
"mac12-arm64": "builds/firefox-beta/%s/firefox-beta-mac-arm64.zip",
"mac13": "builds/firefox-beta/%s/firefox-beta-mac.zip",
"mac13-arm64": "builds/firefox-beta/%s/firefox-beta-mac-arm64.zip",
"mac14": "builds/firefox-beta/%s/firefox-beta-mac.zip",
"mac14-arm64": "builds/firefox-beta/%s/firefox-beta-mac-arm64.zip",
"mac15": "builds/firefox-beta/%s/firefox-beta-mac.zip",
"mac15-arm64": "builds/firefox-beta/%s/firefox-beta-mac-arm64.zip",
"mac26": "builds/firefox-beta/%s/firefox-beta-mac.zip",
"mac26-arm64": "builds/firefox-beta/%s/firefox-beta-mac-arm64.zip",
"win64": "builds/firefox-beta/%s/firefox-beta-win64.zip"
},
"webkit": {
"<unknown>": void 0,
"ubuntu18.04-x64": void 0,
"ubuntu20.04-x64": "builds/webkit/%s/webkit-ubuntu-20.04.zip",
"ubuntu22.04-x64": "builds/webkit/%s/webkit-ubuntu-22.04.zip",
"ubuntu24.04-x64": "builds/webkit/%s/webkit-ubuntu-24.04.zip",
"ubuntu18.04-arm64": void 0,
"ubuntu20.04-arm64": "builds/webkit/%s/webkit-ubuntu-20.04-arm64.zip",
"ubuntu22.04-arm64": "builds/webkit/%s/webkit-ubuntu-22.04-arm64.zip",
"ubuntu24.04-arm64": "builds/webkit/%s/webkit-ubuntu-24.04-arm64.zip",
"debian11-x64": "builds/webkit/%s/webkit-debian-11.zip",
"debian11-arm64": "builds/webkit/%s/webkit-debian-11-arm64.zip",
"debian12-x64": "builds/webkit/%s/webkit-debian-12.zip",
"debian12-arm64": "builds/webkit/%s/webkit-debian-12-arm64.zip",
"debian13-x64": "builds/webkit/%s/webkit-debian-13.zip",
"debian13-arm64": "builds/webkit/%s/webkit-debian-13-arm64.zip",
"mac10.13": void 0,
"mac10.14": void 0,
"mac10.15": void 0,
"mac11": void 0,
"mac11-arm64": void 0,
"mac12": void 0,
"mac12-arm64": void 0,
"mac13": void 0,
"mac13-arm64": void 0,
"mac14": "builds/webkit/%s/webkit-mac-14.zip",
"mac14-arm64": "builds/webkit/%s/webkit-mac-14-arm64.zip",
"mac15": "builds/webkit/%s/webkit-mac-15.zip",
"mac15-arm64": "builds/webkit/%s/webkit-mac-15-arm64.zip",
"mac26": "builds/webkit/%s/webkit-mac-15.zip",
"mac26-arm64": "builds/webkit/%s/webkit-mac-15-arm64.zip",
"win64": "builds/webkit/%s/webkit-win64.zip"
},
"ffmpeg": {
"<unknown>": void 0,
"ubuntu18.04-x64": void 0,
"ubuntu20.04-x64": "builds/ffmpeg/%s/ffmpeg-linux.zip",
"ubuntu22.04-x64": "builds/ffmpeg/%s/ffmpeg-linux.zip",
"ubuntu24.04-x64": "builds/ffmpeg/%s/ffmpeg-linux.zip",
"ubuntu18.04-arm64": void 0,
"ubuntu20.04-arm64": "builds/ffmpeg/%s/ffmpeg-linux-arm64.zip",
"ubuntu22.04-arm64": "builds/ffmpeg/%s/ffmpeg-linux-arm64.zip",
"ubuntu24.04-arm64": "builds/ffmpeg/%s/ffmpeg-linux-arm64.zip",
"debian11-x64": "builds/ffmpeg/%s/ffmpeg-linux.zip",
"debian11-arm64": "builds/ffmpeg/%s/ffmpeg-linux-arm64.zip",
"debian12-x64": "builds/ffmpeg/%s/ffmpeg-linux.zip",
"debian12-arm64": "builds/ffmpeg/%s/ffmpeg-linux-arm64.zip",
"debian13-x64": "builds/ffmpeg/%s/ffmpeg-linux.zip",
"debian13-arm64": "builds/ffmpeg/%s/ffmpeg-linux-arm64.zip",
"mac10.13": "builds/ffmpeg/%s/ffmpeg-mac.zip",
"mac10.14": "builds/ffmpeg/%s/ffmpeg-mac.zip",
"mac10.15": "builds/ffmpeg/%s/ffmpeg-mac.zip",
"mac11": "builds/ffmpeg/%s/ffmpeg-mac.zip",
"mac11-arm64": "builds/ffmpeg/%s/ffmpeg-mac-arm64.zip",
"mac12": "builds/ffmpeg/%s/ffmpeg-mac.zip",
"mac12-arm64": "builds/ffmpeg/%s/ffmpeg-mac-arm64.zip",
"mac13": "builds/ffmpeg/%s/ffmpeg-mac.zip",
"mac13-arm64": "builds/ffmpeg/%s/ffmpeg-mac-arm64.zip",
"mac14": "builds/ffmpeg/%s/ffmpeg-mac.zip",
"mac14-arm64": "builds/ffmpeg/%s/ffmpeg-mac-arm64.zip",
"mac15": "builds/ffmpeg/%s/ffmpeg-mac.zip",
"mac15-arm64": "builds/ffmpeg/%s/ffmpeg-mac-arm64.zip",
"mac26": "builds/ffmpeg/%s/ffmpeg-mac.zip",
"mac26-arm64": "builds/ffmpeg/%s/ffmpeg-mac-arm64.zip",
"win64": "builds/ffmpeg/%s/ffmpeg-win64.zip"
},
"winldd": {
"<unknown>": void 0,
"ubuntu18.04-x64": void 0,
"ubuntu20.04-x64": void 0,
"ubuntu22.04-x64": void 0,
"ubuntu24.04-x64": void 0,
"ubuntu18.04-arm64": void 0,
"ubuntu20.04-arm64": void 0,
"ubuntu22.04-arm64": void 0,
"ubuntu24.04-arm64": void 0,
"debian11-x64": void 0,
"debian11-arm64": void 0,
"debian12-x64": void 0,
"debian12-arm64": void 0,
"debian13-x64": void 0,
"debian13-arm64": void 0,
"mac10.13": void 0,
"mac10.14": void 0,
"mac10.15": void 0,
"mac11": void 0,
"mac11-arm64": void 0,
"mac12": void 0,
"mac12-arm64": void 0,
"mac13": void 0,
"mac13-arm64": void 0,
"mac14": void 0,
"mac14-arm64": void 0,
"mac15": void 0,
"mac15-arm64": void 0,
"mac26": void 0,
"mac26-arm64": void 0,
"win64": "builds/winldd/%s/winldd-win64.zip"
},
"android": {
"<unknown>": "builds/android/%s/android.zip",
"ubuntu18.04-x64": void 0,
"ubuntu20.04-x64": "builds/android/%s/android.zip",
"ubuntu22.04-x64": "builds/android/%s/android.zip",
"ubuntu24.04-x64": "builds/android/%s/android.zip",
"ubuntu18.04-arm64": void 0,
"ubuntu20.04-arm64": "builds/android/%s/android.zip",
"ubuntu22.04-arm64": "builds/android/%s/android.zip",
"ubuntu24.04-arm64": "builds/android/%s/android.zip",
"debian11-x64": "builds/android/%s/android.zip",
"debian11-arm64": "builds/android/%s/android.zip",
"debian12-x64": "builds/android/%s/android.zip",
"debian12-arm64": "builds/android/%s/android.zip",
"debian13-x64": "builds/android/%s/android.zip",
"debian13-arm64": "builds/android/%s/android.zip",
"mac10.13": "builds/android/%s/android.zip",
"mac10.14": "builds/android/%s/android.zip",
"mac10.15": "builds/android/%s/android.zip",
"mac11": "builds/android/%s/android.zip",
"mac11-arm64": "builds/android/%s/android.zip",
"mac12": "builds/android/%s/android.zip",
"mac12-arm64": "builds/android/%s/android.zip",
"mac13": "builds/android/%s/android.zip",
"mac13-arm64": "builds/android/%s/android.zip",
"mac14": "builds/android/%s/android.zip",
"mac14-arm64": "builds/android/%s/android.zip",
"mac15": "builds/android/%s/android.zip",
"mac15-arm64": "builds/android/%s/android.zip",
"mac26": "builds/android/%s/android.zip",
"mac26-arm64": "builds/android/%s/android.zip",
"win64": "builds/android/%s/android.zip"
}
};
defaultCacheDirectory = (() => {
if (process.platform === "linux")
return process.env.XDG_CACHE_HOME || import_path19.default.join(import_os8.default.homedir(), ".cache");
if (process.platform === "darwin")
return import_path19.default.join(import_os8.default.homedir(), "Library", "Caches");
if (process.platform === "win32")
return process.env.LOCALAPPDATA || import_path19.default.join(import_os8.default.homedir(), "AppData", "Local");
throw new Error("Unsupported platform: " + process.platform);
})();
defaultRegistryDirectory = import_path19.default.join(defaultCacheDirectory, "ms-playwright");
registryDirectory = (() => {
let result2;
const envDefined = getFromENV("PLAYWRIGHT_BROWSERS_PATH");
if (envDefined === "0")
result2 = import_path19.default.join(packageRoot, ".local-browsers");
else if (envDefined)
result2 = envDefined;
else
result2 = defaultRegistryDirectory;
if (!import_path19.default.isAbsolute(result2)) {
result2 = import_path19.default.resolve(getFromENV("INIT_CWD") || process.cwd(), result2);
}
return result2;
})();
allDownloadableDirectoriesThatEverExisted = ["android", "chromium", "firefox", "webkit", "ffmpeg", "firefox-beta", "chromium-tip-of-tree", "chromium-headless-shell", "chromium-tip-of-tree-headless-shell", "winldd"];
chromiumAliases = ["chrome-for-testing"];
Registry = class {
constructor(browsersJSON) {
const descriptors = readDescriptors(browsersJSON);
const findExecutablePath = (dir, name) => {
const tokens = EXECUTABLE_PATHS[name][shortPlatform];
return tokens ? import_path19.default.join(dir, ...tokens) : void 0;
};
const executablePathOrDie = (name, e, installByDefault, sdkLanguage) => {
if (!e)
throw new Error(`${name} is not supported on ${hostPlatform}`);
const installCommand = buildPlaywrightCLICommand(sdkLanguage, `install${installByDefault ? "" : " " + name}`);
if (!canAccessFile(e)) {
const currentDockerVersion = readDockerVersionSync();
const preferredDockerVersion = currentDockerVersion ? dockerVersion(currentDockerVersion.dockerImageNameTemplate) : null;
const isOutdatedDockerImage = currentDockerVersion && preferredDockerVersion && currentDockerVersion.dockerImageName !== preferredDockerVersion.dockerImageName;
const isFfmpeg = name === "ffmpeg";
let prettyMessage;
if (isOutdatedDockerImage) {
prettyMessage = [
`Looks like Playwright was just updated to ${preferredDockerVersion.driverVersion}.`,
`Please update docker image as well.`,
`- current: ${currentDockerVersion.dockerImageName}`,
`- required: ${preferredDockerVersion.dockerImageName}`,
``,
`<3 Playwright Team`
].join("\n");
} else if (isFfmpeg) {
prettyMessage = [
`Video rendering requires ffmpeg binary.`,
`Downloading it will not affect any of the system-wide settings.`,
`Please run the following command:`,
``,
` ${buildPlaywrightCLICommand(sdkLanguage, "install ffmpeg")}`,
``,
`<3 Playwright Team`
].join("\n");
} else {
prettyMessage = [
`Looks like Playwright was just installed or updated.`,
`Please run the following command to download new browser${installByDefault ? "s" : ""}:`,
``,
` ${installCommand}`,
``,
`<3 Playwright Team`
].join("\n");
}
throw new Error(`Executable doesn't exist at ${e}
${wrapInASCIIBox(prettyMessage, 1)}`);
}
return e;
};
this._executables = [];
const chromium = descriptors.find((d) => d.name === "chromium");
const chromiumExecutable = findExecutablePath(chromium.dir, "chromium");
this._executables.push({
name: "chromium",
browserName: "chromium",
directory: chromium.dir,
executablePath: () => chromiumExecutable,
executablePathOrDie: (sdkLanguage) => executablePathOrDie("chromium", chromiumExecutable, chromium.installByDefault, sdkLanguage),
installType: chromium.installByDefault ? "download-by-default" : "download-on-demand",
_validateHostRequirements: (sdkLanguage) => this._validateHostRequirements(sdkLanguage, chromium.dir, ["chrome-linux"], [], ["chrome-win"]),
downloadURLs: this._downloadURLs(chromium),
title: chromium.title,
revision: chromium.revision,
browserVersion: chromium.browserVersion,
_install: (force) => this._downloadExecutable(chromium, force, chromiumExecutable),
_dependencyGroup: "chromium",
_isHermeticInstallation: true
});
const chromiumHeadlessShell = descriptors.find((d) => d.name === "chromium-headless-shell");
const chromiumHeadlessShellExecutable = findExecutablePath(chromiumHeadlessShell.dir, "chromium-headless-shell");
this._executables.push({
name: "chromium-headless-shell",
browserName: "chromium",
directory: chromiumHeadlessShell.dir,
executablePath: () => chromiumHeadlessShellExecutable,
executablePathOrDie: (sdkLanguage) => executablePathOrDie("chromium", chromiumHeadlessShellExecutable, chromiumHeadlessShell.installByDefault, sdkLanguage),
installType: chromiumHeadlessShell.installByDefault ? "download-by-default" : "download-on-demand",
_validateHostRequirements: (sdkLanguage) => this._validateHostRequirements(sdkLanguage, chromiumHeadlessShell.dir, ["chrome-linux"], [], ["chrome-win"]),
downloadURLs: this._downloadURLs(chromiumHeadlessShell),
title: chromiumHeadlessShell.title,
revision: chromiumHeadlessShell.revision,
browserVersion: chromiumHeadlessShell.browserVersion,
_install: (force) => this._downloadExecutable(chromiumHeadlessShell, force, chromiumHeadlessShellExecutable),
_dependencyGroup: "chromium",
_isHermeticInstallation: true
});
const chromiumTipOfTreeHeadlessShell = descriptors.find((d) => d.name === "chromium-tip-of-tree-headless-shell");
const chromiumTipOfTreeHeadlessShellExecutable = findExecutablePath(chromiumTipOfTreeHeadlessShell.dir, "chromium-tip-of-tree-headless-shell");
this._executables.push({
name: "chromium-tip-of-tree-headless-shell",
browserName: "chromium",
directory: chromiumTipOfTreeHeadlessShell.dir,
executablePath: () => chromiumTipOfTreeHeadlessShellExecutable,
executablePathOrDie: (sdkLanguage) => executablePathOrDie("chromium", chromiumTipOfTreeHeadlessShellExecutable, chromiumTipOfTreeHeadlessShell.installByDefault, sdkLanguage),
installType: chromiumTipOfTreeHeadlessShell.installByDefault ? "download-by-default" : "download-on-demand",
_validateHostRequirements: (sdkLanguage) => this._validateHostRequirements(sdkLanguage, chromiumTipOfTreeHeadlessShell.dir, ["chrome-linux"], [], ["chrome-win"]),
downloadURLs: this._downloadURLs(chromiumTipOfTreeHeadlessShell),
title: chromiumTipOfTreeHeadlessShell.title,
revision: chromiumTipOfTreeHeadlessShell.revision,
browserVersion: chromiumTipOfTreeHeadlessShell.browserVersion,
_install: (force) => this._downloadExecutable(chromiumTipOfTreeHeadlessShell, force, chromiumTipOfTreeHeadlessShellExecutable),
_dependencyGroup: "chromium",
_isHermeticInstallation: true
});
const chromiumTipOfTree = descriptors.find((d) => d.name === "chromium-tip-of-tree");
const chromiumTipOfTreeExecutable = findExecutablePath(chromiumTipOfTree.dir, "chromium-tip-of-tree");
this._executables.push({
name: "chromium-tip-of-tree",
browserName: "chromium",
directory: chromiumTipOfTree.dir,
executablePath: () => chromiumTipOfTreeExecutable,
executablePathOrDie: (sdkLanguage) => executablePathOrDie("chromium-tip-of-tree", chromiumTipOfTreeExecutable, chromiumTipOfTree.installByDefault, sdkLanguage),
installType: chromiumTipOfTree.installByDefault ? "download-by-default" : "download-on-demand",
_validateHostRequirements: (sdkLanguage) => this._validateHostRequirements(sdkLanguage, chromiumTipOfTree.dir, ["chrome-linux"], [], ["chrome-win"]),
downloadURLs: this._downloadURLs(chromiumTipOfTree),
title: chromiumTipOfTree.title,
revision: chromiumTipOfTree.revision,
browserVersion: chromiumTipOfTree.browserVersion,
_install: (force) => this._downloadExecutable(chromiumTipOfTree, force, chromiumTipOfTreeExecutable),
_dependencyGroup: "chromium",
_isHermeticInstallation: true
});
this._executables.push(this._createChromiumChannel("chrome", {
"linux": "/opt/google/chrome/chrome",
"darwin": "/Applications/Google Chrome.app/Contents/MacOS/Google Chrome",
"win32": `\\Google\\Chrome\\Application\\chrome.exe`
}, () => this._installChromiumChannel("chrome", {
"linux": "reinstall_chrome_stable_linux.sh",
"darwin": "reinstall_chrome_stable_mac.sh",
"win32": "reinstall_chrome_stable_win.ps1"
})));
this._executables.push(this._createChromiumChannel("chrome-beta", {
"linux": "/opt/google/chrome-beta/chrome",
"darwin": "/Applications/Google Chrome Beta.app/Contents/MacOS/Google Chrome Beta",
"win32": `\\Google\\Chrome Beta\\Application\\chrome.exe`
}, () => this._installChromiumChannel("chrome-beta", {
"linux": "reinstall_chrome_beta_linux.sh",
"darwin": "reinstall_chrome_beta_mac.sh",
"win32": "reinstall_chrome_beta_win.ps1"
})));
this._executables.push(this._createChromiumChannel("chrome-dev", {
"linux": "/opt/google/chrome-unstable/chrome",
"darwin": "/Applications/Google Chrome Dev.app/Contents/MacOS/Google Chrome Dev",
"win32": `\\Google\\Chrome Dev\\Application\\chrome.exe`
}));
this._executables.push(this._createChromiumChannel("chrome-canary", {
"linux": "/opt/google/chrome-canary/chrome",
"darwin": "/Applications/Google Chrome Canary.app/Contents/MacOS/Google Chrome Canary",
"win32": `\\Google\\Chrome SxS\\Application\\chrome.exe`
}));
this._executables.push(this._createChromiumChannel("msedge", {
"linux": "/opt/microsoft/msedge/msedge",
"darwin": "/Applications/Microsoft Edge.app/Contents/MacOS/Microsoft Edge",
"win32": `\\Microsoft\\Edge\\Application\\msedge.exe`
}, () => this._installMSEdgeChannel("msedge", {
"linux": "reinstall_msedge_stable_linux.sh",
"darwin": "reinstall_msedge_stable_mac.sh",
"win32": "reinstall_msedge_stable_win.ps1"
})));
this._executables.push(this._createChromiumChannel("msedge-beta", {
"linux": "/opt/microsoft/msedge-beta/msedge",
"darwin": "/Applications/Microsoft Edge Beta.app/Contents/MacOS/Microsoft Edge Beta",
"win32": `\\Microsoft\\Edge Beta\\Application\\msedge.exe`
}, () => this._installMSEdgeChannel("msedge-beta", {
"darwin": "reinstall_msedge_beta_mac.sh",
"linux": "reinstall_msedge_beta_linux.sh",
"win32": "reinstall_msedge_beta_win.ps1"
})));
this._executables.push(this._createChromiumChannel("msedge-dev", {
"linux": "/opt/microsoft/msedge-dev/msedge",
"darwin": "/Applications/Microsoft Edge Dev.app/Contents/MacOS/Microsoft Edge Dev",
"win32": `\\Microsoft\\Edge Dev\\Application\\msedge.exe`
}, () => this._installMSEdgeChannel("msedge-dev", {
"darwin": "reinstall_msedge_dev_mac.sh",
"linux": "reinstall_msedge_dev_linux.sh",
"win32": "reinstall_msedge_dev_win.ps1"
})));
this._executables.push(this._createChromiumChannel("msedge-canary", {
"linux": "",
"darwin": "/Applications/Microsoft Edge Canary.app/Contents/MacOS/Microsoft Edge Canary",
"win32": `\\Microsoft\\Edge SxS\\Application\\msedge.exe`
}));
this._executables.push(this._createBidiFirefoxChannel("moz-firefox", {
"linux": "/snap/bin/firefox",
"darwin": "/Applications/Firefox.app/Contents/MacOS/firefox",
"win32": "\\Mozilla Firefox\\firefox.exe"
}));
this._executables.push(this._createBidiFirefoxChannel("moz-firefox-beta", {
"linux": "/opt/firefox-beta/firefox",
"darwin": "/Applications/Firefox.app/Contents/MacOS/firefox",
"win32": "\\Mozilla Firefox\\firefox.exe"
}));
this._executables.push(this._createBidiFirefoxChannel("moz-firefox-nightly", {
"linux": "/opt/firefox-nightly/firefox",
"darwin": "/Applications/Firefox Nightly.app/Contents/MacOS/firefox",
"win32": "\\Mozilla Firefox\\firefox.exe"
}));
const firefox = descriptors.find((d) => d.name === "firefox");
const firefoxExecutable = findExecutablePath(firefox.dir, "firefox");
this._executables.push({
name: "firefox",
browserName: "firefox",
directory: firefox.dir,
executablePath: () => firefoxExecutable,
executablePathOrDie: (sdkLanguage) => executablePathOrDie("firefox", firefoxExecutable, firefox.installByDefault, sdkLanguage),
installType: firefox.installByDefault ? "download-by-default" : "download-on-demand",
_validateHostRequirements: (sdkLanguage) => this._validateHostRequirements(sdkLanguage, firefox.dir, ["firefox"], [], ["firefox"]),
downloadURLs: this._downloadURLs(firefox),
title: firefox.title,
revision: firefox.revision,
browserVersion: firefox.browserVersion,
_install: (force) => this._downloadExecutable(firefox, force, firefoxExecutable),
_dependencyGroup: "firefox",
_isHermeticInstallation: true
});
const firefoxBeta = descriptors.find((d) => d.name === "firefox-beta");
const firefoxBetaExecutable = findExecutablePath(firefoxBeta.dir, "firefox");
this._executables.push({
name: "firefox-beta",
browserName: "firefox",
directory: firefoxBeta.dir,
executablePath: () => firefoxBetaExecutable,
executablePathOrDie: (sdkLanguage) => executablePathOrDie("firefox-beta", firefoxBetaExecutable, firefoxBeta.installByDefault, sdkLanguage),
installType: firefoxBeta.installByDefault ? "download-by-default" : "download-on-demand",
_validateHostRequirements: (sdkLanguage) => this._validateHostRequirements(sdkLanguage, firefoxBeta.dir, ["firefox"], [], ["firefox"]),
downloadURLs: this._downloadURLs(firefoxBeta),
title: firefoxBeta.title,
revision: firefoxBeta.revision,
browserVersion: firefoxBeta.browserVersion,
_install: (force) => this._downloadExecutable(firefoxBeta, force, firefoxBetaExecutable),
_dependencyGroup: "firefox",
_isHermeticInstallation: true
});
const webkit = descriptors.find((d) => d.name === "webkit");
const webkitExecutable = findExecutablePath(webkit.dir, "webkit");
const webkitLinuxLddDirectories = [
import_path19.default.join("minibrowser-gtk"),
import_path19.default.join("minibrowser-gtk", "bin"),
import_path19.default.join("minibrowser-gtk", "lib"),
import_path19.default.join("minibrowser-gtk", "sys", "lib"),
import_path19.default.join("minibrowser-wpe"),
import_path19.default.join("minibrowser-wpe", "bin"),
import_path19.default.join("minibrowser-wpe", "lib"),
import_path19.default.join("minibrowser-wpe", "sys", "lib")
];
this._executables.push({
name: "webkit",
browserName: "webkit",
directory: webkit.dir,
executablePath: () => webkitExecutable,
executablePathOrDie: (sdkLanguage) => executablePathOrDie("webkit", webkitExecutable, webkit.installByDefault, sdkLanguage),
installType: webkit.installByDefault ? "download-by-default" : "download-on-demand",
_validateHostRequirements: (sdkLanguage) => this._validateHostRequirements(sdkLanguage, webkit.dir, webkitLinuxLddDirectories, ["libGLESv2.so.2", "libx264.so"], [""]),
downloadURLs: this._downloadURLs(webkit),
title: webkit.title,
revision: webkit.revision,
browserVersion: webkit.browserVersion,
_install: (force) => this._downloadExecutable(webkit, force, webkitExecutable),
_dependencyGroup: "webkit",
_isHermeticInstallation: true
});
this._executables.push({
name: "webkit-wsl",
browserName: "webkit",
directory: webkit.dir,
executablePath: () => webkitExecutable,
executablePathOrDie: (sdkLanguage) => executablePathOrDie("webkit", webkitExecutable, webkit.installByDefault, sdkLanguage),
installType: "download-on-demand",
title: "Webkit in WSL",
_validateHostRequirements: (sdkLanguage) => Promise.resolve(),
_isHermeticInstallation: true,
_install: async () => {
if (process.platform !== "win32")
throw new Error(`WebKit via WSL is only supported on Windows`);
const script = import_path19.default.join(BIN_PATH, "install_webkit_wsl.ps1");
const { code } = await spawnAsync("powershell.exe", [
"-ExecutionPolicy",
"Bypass",
"-File",
script
], {
stdio: "inherit"
});
if (code !== 0)
throw new Error(`Failed to install WebKit via WSL`);
}
});
const ffmpeg = descriptors.find((d) => d.name === "ffmpeg");
const ffmpegExecutable = findExecutablePath(ffmpeg.dir, "ffmpeg");
this._executables.push({
name: "ffmpeg",
browserName: void 0,
directory: ffmpeg.dir,
executablePath: () => ffmpegExecutable,
executablePathOrDie: (sdkLanguage) => executablePathOrDie("ffmpeg", ffmpegExecutable, ffmpeg.installByDefault, sdkLanguage),
installType: ffmpeg.installByDefault ? "download-by-default" : "download-on-demand",
_validateHostRequirements: () => Promise.resolve(),
downloadURLs: this._downloadURLs(ffmpeg),
title: ffmpeg.title,
revision: ffmpeg.revision,
_install: (force) => this._downloadExecutable(ffmpeg, force, ffmpegExecutable),
_dependencyGroup: "tools",
_isHermeticInstallation: true
});
const winldd = descriptors.find((d) => d.name === "winldd");
const winlddExecutable = findExecutablePath(winldd.dir, "winldd");
this._executables.push({
name: "winldd",
browserName: void 0,
directory: winldd.dir,
executablePath: () => winlddExecutable,
executablePathOrDie: (sdkLanguage) => executablePathOrDie("winldd", winlddExecutable, winldd.installByDefault, sdkLanguage),
installType: process.platform === "win32" ? "download-by-default" : "none",
_validateHostRequirements: () => Promise.resolve(),
downloadURLs: this._downloadURLs(winldd),
title: winldd.title,
revision: winldd.revision,
_install: (force) => this._downloadExecutable(winldd, force, winlddExecutable),
_dependencyGroup: "tools",
_isHermeticInstallation: true
});
const android = descriptors.find((d) => d.name === "android");
this._executables.push({
name: "android",
browserName: void 0,
directory: android.dir,
executablePath: () => void 0,
executablePathOrDie: () => "",
installType: "download-on-demand",
_validateHostRequirements: () => Promise.resolve(),
downloadURLs: this._downloadURLs(android),
title: android.title,
revision: android.revision,
_install: (force) => this._downloadExecutable(android, force),
_dependencyGroup: "tools",
_isHermeticInstallation: true
});
}
_createChromiumChannel(name, lookAt, install2) {
const executablePath = (sdkLanguage, shouldThrow) => {
const suffix = lookAt[process.platform];
if (!suffix) {
if (shouldThrow)
throw new Error(`Chromium distribution '${name}' is not supported on ${process.platform}`);
return void 0;
}
const prefixes = process.platform === "win32" ? [
process.env.LOCALAPPDATA,
process.env.PROGRAMFILES,
process.env["PROGRAMFILES(X86)"],
// In some cases there is no PROGRAMFILES/(86) env var set but HOMEDRIVE is set.
process.env.HOMEDRIVE + "\\Program Files",
process.env.HOMEDRIVE + "\\Program Files (x86)"
].filter(Boolean) : [""];
for (const prefix of prefixes) {
const executablePath2 = import_path19.default.join(prefix, suffix);
if (canAccessFile(executablePath2))
return executablePath2;
}
if (!shouldThrow)
return void 0;
const location2 = prefixes.length ? ` at ${import_path19.default.join(prefixes[0], suffix)}` : ``;
const installation = install2 ? `
Run "${buildPlaywrightCLICommand(sdkLanguage, "install " + name)}"` : "";
throw new Error(`Chromium distribution '${name}' is not found${location2}${installation}`);
};
return {
name,
browserName: "chromium",
directory: void 0,
executablePath: () => executablePath("", false),
executablePathOrDie: (sdkLanguage) => executablePath(sdkLanguage, true),
installType: install2 ? "install-script" : "none",
_validateHostRequirements: () => Promise.resolve(),
_isHermeticInstallation: false,
_install: install2
};
}
_createBidiFirefoxChannel(name, lookAt, install2) {
const executablePath = (sdkLanguage, shouldThrow) => {
const suffix = lookAt[process.platform];
if (!suffix) {
if (shouldThrow)
throw new Error(`Firefox distribution '${name}' is not supported on ${process.platform}`);
return void 0;
}
const prefixes = process.platform === "win32" ? [
process.env.LOCALAPPDATA,
process.env.PROGRAMFILES,
process.env["PROGRAMFILES(X86)"],
// In some cases there is no PROGRAMFILES/(86) env var set but HOMEDRIVE is set.
process.env.HOMEDRIVE + "\\Program Files",
process.env.HOMEDRIVE + "\\Program Files (x86)"
].filter(Boolean) : [""];
for (const prefix of prefixes) {
const executablePath2 = import_path19.default.join(prefix, suffix);
if (canAccessFile(executablePath2))
return executablePath2;
}
if (shouldThrow)
throw new Error(`Cannot find Firefox installation for channel '${name}' at the standard system paths. ${`Tried paths:
${prefixes.map((p) => import_path19.default.join(p, suffix)).join("\n ")}`}`);
return void 0;
};
return {
name,
browserName: "firefox",
directory: void 0,
executablePath: () => executablePath("", false),
executablePathOrDie: (sdkLanguage) => executablePath(sdkLanguage, true),
installType: "none",
_validateHostRequirements: () => Promise.resolve(),
_isHermeticInstallation: true,
_install: install2
};
}
executables() {
return this._executables;
}
findExecutable(name) {
return this._executables.find((b) => b.name === name);
}
defaultExecutables() {
return this._executables.filter((e) => e.installType === "download-by-default");
}
_dedupe(executables) {
return Array.from(new Set(executables));
}
async _validateHostRequirements(sdkLanguage, browserDirectory, linuxLddDirectories, dlOpenLibraries, windowsExeAndDllDirectories) {
if (import_os8.default.platform() === "linux")
return await validateDependenciesLinux(sdkLanguage, linuxLddDirectories.map((d) => import_path19.default.join(browserDirectory, d)), dlOpenLibraries);
if (import_os8.default.platform() === "win32" && import_os8.default.arch() === "x64")
return await validateDependenciesWindows(sdkLanguage, windowsExeAndDllDirectories.map((d) => import_path19.default.join(browserDirectory, d)));
}
async installDeps(executablesToInstallDeps, dryRun) {
const executables = this._dedupe(executablesToInstallDeps);
const targets = /* @__PURE__ */ new Set();
for (const executable of executables) {
if (executable._dependencyGroup)
targets.add(executable._dependencyGroup);
}
targets.add("tools");
if (import_os8.default.platform() === "win32")
return await installDependenciesWindows(targets, dryRun);
if (import_os8.default.platform() === "linux")
return await installDependenciesLinux(targets, dryRun);
}
async install(executablesToInstall, options2) {
const executables = this._dedupe(executablesToInstall);
await import_fs19.default.promises.mkdir(registryDirectory, { recursive: true });
const lockfilePath = import_path19.default.join(registryDirectory, "__dirlock");
const linksDir = import_path19.default.join(registryDirectory, ".links");
let releaseLock;
try {
releaseLock = await lock(registryDirectory, {
retries: {
// Retry 20 times during 10 minutes with
// exponential back-off.
// See documentation at: https://www.npmjs.com/package/retry#retrytimeoutsoptions
retries: 20,
factor: 1.27579
},
onCompromised: (err) => {
throw new Error(`${err.message} Path: ${lockfilePath}`);
},
lockfilePath
});
await import_fs19.default.promises.mkdir(linksDir, { recursive: true });
await import_fs19.default.promises.writeFile(import_path19.default.join(linksDir, calculateSha1(PACKAGE_PATH)), PACKAGE_PATH);
if (!getAsBooleanFromENV("PLAYWRIGHT_SKIP_BROWSER_GC"))
await this._validateInstallationCache(linksDir);
for (const executable of executables) {
if (!executable._install)
throw new Error(`ERROR: Playwright does not support installing ${executable.name}`);
if (!getAsBooleanFromENV("CI") && !executable._isHermeticInstallation && !options2?.force && executable.executablePath()) {
const { embedderName } = getEmbedderName();
const command = buildPlaywrightCLICommand(embedderName, "install --force " + executable.name);
process.stderr.write("\n" + wrapInASCIIBox([
`ATTENTION: "${executable.name}" is already installed on the system!`,
``,
`"${executable.name}" installation is not hermetic; installing newer version`,
`requires *removal* of a current installation first.`,
``,
`To *uninstall* current version and re-install latest "${executable.name}":`,
``,
`- Close all running instances of "${executable.name}", if any`,
`- Use "--force" to install browser:`,
``,
` ${command}`,
``,
`<3 Playwright Team`
].join("\n"), 1) + "\n\n");
return;
}
await executable._install(!!options2?.force);
}
} catch (e) {
if (e.code === "ELOCKED") {
const rmCommand = process.platform === "win32" ? "rm -R" : "rm -rf";
throw new Error("\n" + wrapInASCIIBox([
`An active lockfile is found at:`,
``,
` ${lockfilePath}`,
``,
`Either:`,
`- wait a few minutes if other Playwright is installing browsers in parallel`,
`- remove lock manually with:`,
``,
` ${rmCommand} ${lockfilePath}`,
``,
`<3 Playwright Team`
].join("\n"), 1));
} else {
throw e;
}
} finally {
if (releaseLock)
await releaseLock();
}
}
async uninstall(all) {
const linksDir = import_path19.default.join(registryDirectory, ".links");
if (all) {
const links = await import_fs19.default.promises.readdir(linksDir).catch(() => []);
for (const link of links)
await import_fs19.default.promises.unlink(import_path19.default.join(linksDir, link));
} else {
await import_fs19.default.promises.unlink(import_path19.default.join(linksDir, calculateSha1(PACKAGE_PATH))).catch(() => {
});
}
await this._validateInstallationCache(linksDir);
return {
numberOfBrowsersLeft: (await import_fs19.default.promises.readdir(registryDirectory).catch(() => [])).filter((browserDirectory) => isBrowserDirectory(browserDirectory)).length
};
}
async validateHostRequirementsForExecutablesIfNeeded(executables, sdkLanguage) {
if (getAsBooleanFromENV("PLAYWRIGHT_SKIP_VALIDATE_HOST_REQUIREMENTS")) {
process.stderr.write("Skipping host requirements validation logic because `PLAYWRIGHT_SKIP_VALIDATE_HOST_REQUIREMENTS` env variable is set.\n");
return;
}
for (const executable of executables)
await this._validateHostRequirementsForExecutableIfNeeded(executable, sdkLanguage);
}
async _validateHostRequirementsForExecutableIfNeeded(executable, sdkLanguage) {
const kMaximumReValidationPeriod = 30 * 24 * 60 * 60 * 1e3;
if (!executable.directory)
return;
const markerFile = import_path19.default.join(executable.directory, "DEPENDENCIES_VALIDATED");
if (await import_fs19.default.promises.stat(markerFile).then((stat) => Date.now() - stat.mtime.getTime() < kMaximumReValidationPeriod).catch(() => false))
return;
debugLogger.log("install", `validating host requirements for "${executable.name}"`);
try {
await executable._validateHostRequirements(sdkLanguage);
debugLogger.log("install", `validation passed for ${executable.name}`);
} catch (error) {
debugLogger.log("install", `validation failed for ${executable.name}`);
throw error;
}
await import_fs19.default.promises.writeFile(markerFile, "").catch(() => {
});
}
_downloadURLs(descriptor) {
const paths = DOWNLOAD_PATHS[descriptor.name];
const downloadPathTemplate = paths[hostPlatform] || paths["<unknown>"];
if (!downloadPathTemplate)
return [];
let downloadPath;
let mirrors;
if (typeof downloadPathTemplate === "function") {
const result2 = downloadPathTemplate(descriptor);
downloadPath = result2.path;
mirrors = result2.mirrors;
} else {
downloadPath = util2.format(downloadPathTemplate, descriptor.revision);
mirrors = PLAYWRIGHT_CDN_MIRRORS;
}
let downloadHostEnv;
if (descriptor.name.startsWith("chromium"))
downloadHostEnv = "PLAYWRIGHT_CHROMIUM_DOWNLOAD_HOST";
else if (descriptor.name.startsWith("firefox"))
downloadHostEnv = "PLAYWRIGHT_FIREFOX_DOWNLOAD_HOST";
else if (descriptor.name.startsWith("webkit"))
downloadHostEnv = "PLAYWRIGHT_WEBKIT_DOWNLOAD_HOST";
const customHostOverride = downloadHostEnv && getFromENV(downloadHostEnv) || getFromENV("PLAYWRIGHT_DOWNLOAD_HOST");
if (customHostOverride)
mirrors = [customHostOverride];
return mirrors.map((mirror) => `${mirror}/${downloadPath}`);
}
async _downloadExecutable(descriptor, force, executablePath) {
const downloadURLs = this._downloadURLs(descriptor);
if (!downloadURLs.length)
throw new Error(`ERROR: Playwright does not support ${descriptor.name} on ${hostPlatform}`);
if (!isOfficiallySupportedPlatform)
logPolitely(`BEWARE: your OS is not officially supported by Playwright; downloading fallback build for ${hostPlatform}.`);
if (descriptor.hasRevisionOverride) {
const message = `You are using a frozen ${descriptor.name} browser which does not receive updates anymore on ${hostPlatform}. Please update to the latest version of your operating system to test up-to-date browsers.`;
if (process.env.GITHUB_ACTIONS)
console.log(`::warning title=Playwright::${message}`);
else
logPolitely(message);
}
const title = this.calculateDownloadTitle(descriptor);
const downloadFileName = `playwright-download-${descriptor.name}-${hostPlatform}-${descriptor.revision}.zip`;
const downloadSocketTimeoutEnv = getFromENV("PLAYWRIGHT_DOWNLOAD_CONNECTION_TIMEOUT");
const downloadSocketTimeout = +(downloadSocketTimeoutEnv || "0") || NET_DEFAULT_TIMEOUT;
await downloadBrowserWithProgressBar(title, descriptor.dir, executablePath, downloadURLs, downloadFileName, downloadSocketTimeout, force).catch((e) => {
throw new Error(`Failed to download ${title}, caused by
${e.stack}`);
});
}
calculateDownloadTitle(descriptor) {
const title = descriptor.title ?? descriptor.name.split("-").map((word) => {
return word === "ffmpeg" ? "FFmpeg" : word.charAt(0).toUpperCase() + word.slice(1);
}).join(" ");
const version3 = descriptor.browserVersion ? " " + descriptor.browserVersion : "";
return `${title}${version3} (playwright ${descriptor.name} v${descriptor.revision})`;
}
async _installMSEdgeChannel(channel, scripts) {
const scriptArgs = [];
if (process.platform !== "linux") {
const products = lowercaseAllKeys(JSON.parse(await fetchData(void 0, { url: "https://edgeupdates.microsoft.com/api/products" })));
const productName = {
"msedge": "Stable",
"msedge-beta": "Beta",
"msedge-dev": "Dev"
}[channel];
const product = products.find((product2) => product2.product === productName);
const searchConfig = {
darwin: { platform: "MacOS", arch: "universal", artifact: "pkg" },
win32: { platform: "Windows", arch: "x64", artifact: "msi" }
}[process.platform];
const release = searchConfig ? product.releases.find((release2) => release2.platform === searchConfig.platform && release2.architecture === searchConfig.arch && release2.artifacts.length > 0) : null;
const artifact = release ? release.artifacts.find((artifact2) => artifact2.artifactname === searchConfig.artifact) : null;
if (artifact)
scriptArgs.push(
artifact.location
/* url */
);
else
throw new Error(`Cannot install ${channel} on ${process.platform}`);
}
await this._installChromiumChannel(channel, scripts, scriptArgs);
}
async _installChromiumChannel(channel, scripts, scriptArgs = []) {
const scriptName = scripts[process.platform];
if (!scriptName)
throw new Error(`Cannot install ${channel} on ${process.platform}`);
const cwd = BIN_PATH;
const isPowerShell = scriptName.endsWith(".ps1");
if (isPowerShell) {
const args = [
"-ExecutionPolicy",
"Bypass",
"-File",
import_path19.default.join(BIN_PATH, scriptName),
...scriptArgs
];
const { code } = await spawnAsync("powershell.exe", args, { cwd, stdio: "inherit" });
if (code !== 0)
throw new Error(`Failed to install ${channel}`);
} else {
const shellArgs = scriptArgs.map((a) => `'${a.replace(/'/g, `'\\''`)}'`).join(" ");
const { command, args, elevatedPermissions } = await transformCommandsForRoot([`bash "${import_path19.default.join(BIN_PATH, scriptName)}" ${shellArgs}`]);
if (elevatedPermissions)
console.log("Switching to root user to install dependencies...");
const { code } = await spawnAsync(command, args, { cwd, stdio: "inherit" });
if (code !== 0)
throw new Error(`Failed to install ${channel}`);
}
}
async listInstalledBrowsers() {
const linksDir = import_path19.default.join(registryDirectory, ".links");
const { browsers } = await this._traverseBrowserInstallations(linksDir);
return browsers.filter((browser) => import_fs19.default.existsSync(browser.browserPath));
}
async _validateInstallationCache(linksDir) {
const { browsers, brokenLinks } = await this._traverseBrowserInstallations(linksDir);
await this._deleteStaleBrowsers(browsers);
await this._deleteBrokenInstallations(brokenLinks);
}
async _traverseBrowserInstallations(linksDir) {
const browserList = [];
const brokenLinks = [];
for (const fileName of await import_fs19.default.promises.readdir(linksDir)) {
const linkPath = import_path19.default.join(linksDir, fileName);
let linkTarget = "";
try {
linkTarget = (await import_fs19.default.promises.readFile(linkPath)).toString();
const browsersJSON = require(import_path19.default.join(linkTarget, "browsers.json"));
const descriptors = readDescriptors(browsersJSON);
for (const browserName of allDownloadableDirectoriesThatEverExisted) {
const descriptor = descriptors.find((d) => d.name === browserName);
if (!descriptor)
continue;
const browserPath = descriptor.dir;
const browserVersion = parseInt(descriptor.revision, 10);
browserList.push({
browserName,
browserVersion,
browserPath,
referenceDir: linkTarget
});
}
} catch (e) {
brokenLinks.push(linkPath);
}
}
return { browsers: browserList, brokenLinks };
}
async _deleteStaleBrowsers(browserList) {
const usedBrowserPaths = /* @__PURE__ */ new Set();
for (const browser of browserList) {
const { browserName, browserVersion, browserPath } = browser;
const shouldHaveMarkerFile = browserName === "chromium" && (browserVersion >= 786218 || browserVersion < 3e5) || browserName === "firefox" && browserVersion >= 1128 || browserName === "webkit" && browserVersion >= 1307 || // All new applications have a marker file right away.
browserName !== "firefox" && browserName !== "chromium" && browserName !== "webkit";
if (!shouldHaveMarkerFile || await existsAsync(browserDirectoryToMarkerFilePath(browserPath)))
usedBrowserPaths.add(browserPath);
}
let downloadedBrowsers = (await import_fs19.default.promises.readdir(registryDirectory)).map((file) => import_path19.default.join(registryDirectory, file));
downloadedBrowsers = downloadedBrowsers.filter((file) => isBrowserDirectory(file));
const directories = new Set(downloadedBrowsers);
for (const browserDirectory of usedBrowserPaths)
directories.delete(browserDirectory);
for (const directory of directories)
logPolitely("Removing unused browser at " + directory);
await removeFolders([...directories]);
}
async _deleteBrokenInstallations(brokenLinks) {
for (const linkPath of brokenLinks)
await import_fs19.default.promises.unlink(linkPath).catch((e) => {
});
}
_defaultBrowsersToInstall(options2) {
let executables = this.defaultExecutables();
if (options2.shell === "no")
executables = executables.filter((e) => e.name !== "chromium-headless-shell" && e.name !== "chromium-tip-of-tree-headless-shell");
if (options2.shell === "only")
executables = executables.filter((e) => e.name !== "chromium" && e.name !== "chromium-tip-of-tree");
return executables;
}
suggestedBrowsersToInstall() {
const names = this.executables().filter((e) => e.installType !== "none").map((e) => e.name);
names.push(...chromiumAliases);
return names.sort().join(", ");
}
isChromiumAlias(name) {
return chromiumAliases.includes(name);
}
resolveBrowsers(aliases2, options2) {
if (aliases2.length === 0)
return this._defaultBrowsersToInstall(options2);
const faultyArguments = [];
const executables = [];
const handleArgument = (arg) => {
const executable = this.findExecutable(arg);
if (!executable || executable.installType === "none")
faultyArguments.push(arg);
else
executables.push(executable);
if (executable?.browserName)
executables.push(this.findExecutable("ffmpeg"));
};
for (const alias of aliases2) {
if (alias === "chromium" || chromiumAliases.includes(alias)) {
if (options2.shell !== "only")
handleArgument("chromium");
if (options2.shell !== "no")
handleArgument("chromium-headless-shell");
} else if (alias === "chromium-tip-of-tree") {
if (options2.shell !== "only")
handleArgument("chromium-tip-of-tree");
if (options2.shell !== "no")
handleArgument("chromium-tip-of-tree-headless-shell");
} else {
handleArgument(alias);
}
}
if (process.platform === "win32")
executables.push(this.findExecutable("winldd"));
if (faultyArguments.length)
throw new Error(`Invalid installation targets: ${faultyArguments.map((name) => `'${name}'`).join(", ")}. Expecting one of: ${this.suggestedBrowsersToInstall()}`);
return executables;
}
};
registry = new Registry(require(import_path19.default.join(packageRoot, "browsers.json")));
}
});
// packages/playwright-core/src/server/launchApp.ts
async function launchApp(browserType, options2) {
const args = [...options2.persistentContextOptions?.args ?? []];
let channel = options2.persistentContextOptions?.channel;
if (browserType.name() === "chromium") {
args.push(
"--app=data:text/html,",
`--window-size=${options2.windowSize.width},${options2.windowSize.height}`,
...options2.windowPosition ? [`--window-position=${options2.windowPosition.x},${options2.windowPosition.y}`] : [],
"--test-type="
);
if (!channel && !options2.persistentContextOptions?.executablePath)
channel = findChromiumChannelBestEffort(options2.sdkLanguage);
}
const controller = new ProgressController();
let context2;
try {
context2 = await controller.run((progress2) => browserType.launchPersistentContext(progress2, "", {
ignoreDefaultArgs: ["--enable-automation"],
...options2?.persistentContextOptions,
channel,
noDefaultViewport: options2.persistentContextOptions?.noDefaultViewport ?? true,
acceptDownloads: options2?.persistentContextOptions?.acceptDownloads ?? (isUnderTest() ? "accept" : "internal-browser-default"),
colorScheme: options2?.persistentContextOptions?.colorScheme ?? "no-override",
args
}), 0);
} catch (error) {
if (channel) {
error = rewriteErrorMessage(error, [
`Failed to launch "${channel}" channel.`,
"Using custom channels could lead to unexpected behavior due to Enterprise policies (chrome://policy).",
"Install the default browser instead:",
wrapInASCIIBox(`${buildPlaywrightCLICommand(options2.sdkLanguage, "install")}`, 2)
].join("\n"));
}
throw error;
}
const [page] = context2.pages();
if (browserType.name() === "chromium" && process.platform === "darwin") {
context2.on("page", async (newPage) => {
if (newPage.mainFrame().url() === "chrome://new-tab-page/") {
await page.bringToFront(nullProgress);
await newPage.close(nullProgress);
}
});
}
if (browserType.name() === "chromium")
await installAppIcon(page);
return { context: context2, page };
}
async function installAppIcon(page) {
const icon = await import_fs20.default.promises.readFile(libPath("server", "chromium", "appIcon.png"));
const crPage = page.delegate;
await crPage._mainFrameSession._client.send("Browser.setDockTile", {
image: icon.toString("base64")
});
}
async function syncLocalStorageWithSettings(page, appName) {
if (isUnderTest())
return;
const settingsFile = import_path20.default.join(registryDirectory, ".settings", `${appName}.json`);
const controller = new ProgressController();
await controller.run(async (progress2) => {
await page.exposeBinding(progress2, "_saveSerializedSettings", (_, settings2) => {
import_fs20.default.mkdirSync(import_path20.default.dirname(settingsFile), { recursive: true });
import_fs20.default.writeFileSync(settingsFile, settings2);
});
const settings = await progress2.race(import_fs20.default.promises.readFile(settingsFile, "utf-8").catch(() => "{}"));
await page.addInitScript(
progress2,
`(${String((settings2) => {
if (location && location.protocol === "data:")
return;
if (window.top !== window)
return;
Object.entries(settings2).map(([k, v]) => localStorage[k] = v);
window.saveSettings = () => {
window._saveSerializedSettings(JSON.stringify({ ...localStorage }));
};
})})(${settings});
`
);
});
}
var import_fs20, import_path20;
var init_launchApp = __esm({
"packages/playwright-core/src/server/launchApp.ts"() {
"use strict";
import_fs20 = __toESM(require("fs"));
import_path20 = __toESM(require("path"));
init_debug();
init_stackTrace();
init_ascii();
init_package();
init_registry();
init_registry();
init_progress();
}
});
// packages/playwright-core/src/server/recorder/throttledFile.ts
var import_fs21, ThrottledFile;
var init_throttledFile = __esm({
"packages/playwright-core/src/server/recorder/throttledFile.ts"() {
"use strict";
import_fs21 = __toESM(require("fs"));
ThrottledFile = class {
constructor(file) {
this._file = file;
}
setContent(text2) {
this._text = text2;
if (!this._timer)
this._timer = setTimeout(() => this.flush(), 250);
}
flush() {
if (this._timer) {
clearTimeout(this._timer);
this._timer = void 0;
}
if (this._text)
import_fs21.default.writeFileSync(this._file, this._text);
this._text = void 0;
}
};
}
});
// packages/playwright-core/src/server/codegen/language.ts
function generateCode(actions, languageGenerator, options2) {
const header = languageGenerator.generateHeader(options2);
const footer = languageGenerator.generateFooter(options2.saveStorage);
const actionTexts = actions.map((a) => generateActionText(languageGenerator, a, !!options2.generateAutoExpect)).filter(Boolean);
const text2 = [header, ...actionTexts, footer].join("\n");
return { header, footer, actionTexts, text: text2 };
}
function generateActionText(generator, action, generateAutoExpect) {
let text2 = generator.generateAction(action);
if (!text2)
return;
if (generateAutoExpect && action.action.preconditionSelector) {
const expectAction = {
frame: action.frame,
startTime: action.startTime,
endTime: action.startTime,
action: {
name: "assertVisible",
selector: action.action.preconditionSelector,
signals: []
}
};
const expectText = generator.generateAction(expectAction);
if (expectText)
text2 = expectText + "\n\n" + text2;
}
return text2;
}
function sanitizeDeviceOptions(device, options2) {
const cleanedOptions = {};
for (const property in options2) {
if (JSON.stringify(device[property]) !== JSON.stringify(options2[property]))
cleanedOptions[property] = options2[property];
}
return cleanedOptions;
}
function toSignalMap(action) {
let popup;
let download;
let dialog;
for (const signal of action.signals) {
if (signal.name === "popup")
popup = signal;
else if (signal.name === "download")
download = signal;
else if (signal.name === "dialog")
dialog = signal;
}
return {
popup,
download,
dialog
};
}
function toKeyboardModifiers(modifiers) {
const result2 = [];
if (modifiers & 1)
result2.push("Alt");
if (modifiers & 2)
result2.push("ControlOrMeta");
if (modifiers & 4)
result2.push("ControlOrMeta");
if (modifiers & 8)
result2.push("Shift");
return result2;
}
function toClickOptionsForSourceCode(action) {
const modifiers = toKeyboardModifiers(action.modifiers);
const options2 = {};
if (action.button !== "left")
options2.button = action.button;
if (modifiers.length)
options2.modifiers = modifiers;
if (action.clickCount > 2)
options2.clickCount = action.clickCount;
if (action.position)
options2.position = action.position;
return options2;
}
var init_language = __esm({
"packages/playwright-core/src/server/codegen/language.ts"() {
"use strict";
}
});
// packages/playwright-core/src/server/deviceDescriptorsSource.json
var deviceDescriptorsSource_default;
var init_deviceDescriptorsSource = __esm({
"packages/playwright-core/src/server/deviceDescriptorsSource.json"() {
deviceDescriptorsSource_default = {
"Blackberry PlayBook": {
userAgent: "Mozilla/5.0 (PlayBook; U; RIM Tablet OS 2.1.0; en-US) AppleWebKit/536.2+ (KHTML like Gecko) Version/26.4 Safari/536.2+",
viewport: {
width: 600,
height: 1024
},
deviceScaleFactor: 1,
isMobile: true,
hasTouch: true,
defaultBrowserType: "webkit"
},
"Blackberry PlayBook landscape": {
userAgent: "Mozilla/5.0 (PlayBook; U; RIM Tablet OS 2.1.0; en-US) AppleWebKit/536.2+ (KHTML like Gecko) Version/26.4 Safari/536.2+",
viewport: {
width: 1024,
height: 600
},
deviceScaleFactor: 1,
isMobile: true,
hasTouch: true,
defaultBrowserType: "webkit"
},
"BlackBerry Z30": {
userAgent: "Mozilla/5.0 (BB10; Touch) AppleWebKit/537.10+ (KHTML, like Gecko) Version/26.4 Mobile Safari/537.10+",
viewport: {
width: 360,
height: 640
},
deviceScaleFactor: 2,
isMobile: true,
hasTouch: true,
defaultBrowserType: "webkit"
},
"BlackBerry Z30 landscape": {
userAgent: "Mozilla/5.0 (BB10; Touch) AppleWebKit/537.10+ (KHTML, like Gecko) Version/26.4 Mobile Safari/537.10+",
viewport: {
width: 640,
height: 360
},
deviceScaleFactor: 2,
isMobile: true,
hasTouch: true,
defaultBrowserType: "webkit"
},
"Galaxy Note 3": {
userAgent: "Mozilla/5.0 (Linux; U; Android 4.3; en-us; SM-N900T Build/JSS15J) AppleWebKit/534.30 (KHTML, like Gecko) Version/26.4 Mobile Safari/534.30",
viewport: {
width: 360,
height: 640
},
deviceScaleFactor: 3,
isMobile: true,
hasTouch: true,
defaultBrowserType: "webkit"
},
"Galaxy Note 3 landscape": {
userAgent: "Mozilla/5.0 (Linux; U; Android 4.3; en-us; SM-N900T Build/JSS15J) AppleWebKit/534.30 (KHTML, like Gecko) Version/26.4 Mobile Safari/534.30",
viewport: {
width: 640,
height: 360
},
deviceScaleFactor: 3,
isMobile: true,
hasTouch: true,
defaultBrowserType: "webkit"
},
"Galaxy Note II": {
userAgent: "Mozilla/5.0 (Linux; U; Android 4.1; en-us; GT-N7100 Build/JRO03C) AppleWebKit/534.30 (KHTML, like Gecko) Version/26.4 Mobile Safari/534.30",
viewport: {
width: 360,
height: 640
},
deviceScaleFactor: 2,
isMobile: true,
hasTouch: true,
defaultBrowserType: "webkit"
},
"Galaxy Note II landscape": {
userAgent: "Mozilla/5.0 (Linux; U; Android 4.1; en-us; GT-N7100 Build/JRO03C) AppleWebKit/534.30 (KHTML, like Gecko) Version/26.4 Mobile Safari/534.30",
viewport: {
width: 640,
height: 360
},
deviceScaleFactor: 2,
isMobile: true,
hasTouch: true,
defaultBrowserType: "webkit"
},
"Galaxy S III": {
userAgent: "Mozilla/5.0 (Linux; U; Android 4.0; en-us; GT-I9300 Build/IMM76D) AppleWebKit/534.30 (KHTML, like Gecko) Version/26.4 Mobile Safari/534.30",
viewport: {
width: 360,
height: 640
},
deviceScaleFactor: 2,
isMobile: true,
hasTouch: true,
defaultBrowserType: "webkit"
},
"Galaxy S III landscape": {
userAgent: "Mozilla/5.0 (Linux; U; Android 4.0; en-us; GT-I9300 Build/IMM76D) AppleWebKit/534.30 (KHTML, like Gecko) Version/26.4 Mobile Safari/534.30",
viewport: {
width: 640,
height: 360
},
deviceScaleFactor: 2,
isMobile: true,
hasTouch: true,
defaultBrowserType: "webkit"
},
"Galaxy S5": {
userAgent: "Mozilla/5.0 (Linux; Android 5.0; SM-G900P Build/LRX21T) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/148.0.7778.96 Mobile Safari/537.36",
viewport: {
width: 360,
height: 640
},
deviceScaleFactor: 3,
isMobile: true,
hasTouch: true,
defaultBrowserType: "chromium"
},
"Galaxy S5 landscape": {
userAgent: "Mozilla/5.0 (Linux; Android 5.0; SM-G900P Build/LRX21T) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/148.0.7778.96 Mobile Safari/537.36",
viewport: {
width: 640,
height: 360
},
deviceScaleFactor: 3,
isMobile: true,
hasTouch: true,
defaultBrowserType: "chromium"
},
"Galaxy S8": {
userAgent: "Mozilla/5.0 (Linux; Android 7.0; SM-G950U Build/NRD90M) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/148.0.7778.96 Mobile Safari/537.36",
viewport: {
width: 360,
height: 740
},
deviceScaleFactor: 3,
isMobile: true,
hasTouch: true,
defaultBrowserType: "chromium"
},
"Galaxy S8 landscape": {
userAgent: "Mozilla/5.0 (Linux; Android 7.0; SM-G950U Build/NRD90M) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/148.0.7778.96 Mobile Safari/537.36",
viewport: {
width: 740,
height: 360
},
deviceScaleFactor: 3,
isMobile: true,
hasTouch: true,
defaultBrowserType: "chromium"
},
"Galaxy S9+": {
userAgent: "Mozilla/5.0 (Linux; Android 8.0.0; SM-G965U Build/R16NW) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/148.0.7778.96 Mobile Safari/537.36",
viewport: {
width: 320,
height: 658
},
deviceScaleFactor: 4.5,
isMobile: true,
hasTouch: true,
defaultBrowserType: "chromium"
},
"Galaxy S9+ landscape": {
userAgent: "Mozilla/5.0 (Linux; Android 8.0.0; SM-G965U Build/R16NW) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/148.0.7778.96 Mobile Safari/537.36",
viewport: {
width: 658,
height: 320
},
deviceScaleFactor: 4.5,
isMobile: true,
hasTouch: true,
defaultBrowserType: "chromium"
},
"Galaxy S24": {
userAgent: "Mozilla/5.0 (Linux; Android 14; SM-S921U) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/148.0.7778.96 Mobile Safari/537.36",
viewport: {
width: 360,
height: 780
},
deviceScaleFactor: 3,
isMobile: true,
hasTouch: true,
defaultBrowserType: "chromium"
},
"Galaxy S24 landscape": {
userAgent: "Mozilla/5.0 (Linux; Android 14; SM-S921U) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/148.0.7778.96 Mobile Safari/537.36",
viewport: {
width: 780,
height: 360
},
deviceScaleFactor: 3,
isMobile: true,
hasTouch: true,
defaultBrowserType: "chromium"
},
"Galaxy A55": {
userAgent: "Mozilla/5.0 (Linux; Android 14; SM-A556B) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/148.0.7778.96 Mobile Safari/537.36",
viewport: {
width: 480,
height: 1040
},
deviceScaleFactor: 2.25,
isMobile: true,
hasTouch: true,
defaultBrowserType: "chromium"
},
"Galaxy A55 landscape": {
userAgent: "Mozilla/5.0 (Linux; Android 14; SM-A556B) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/148.0.7778.96 Mobile Safari/537.36",
viewport: {
width: 1040,
height: 480
},
deviceScaleFactor: 2.25,
isMobile: true,
hasTouch: true,
defaultBrowserType: "chromium"
},
"Galaxy Tab S4": {
userAgent: "Mozilla/5.0 (Linux; Android 8.1.0; SM-T837A) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/148.0.7778.96 Safari/537.36",
viewport: {
width: 712,
height: 1138
},
deviceScaleFactor: 2.25,
isMobile: true,
hasTouch: true,
defaultBrowserType: "chromium"
},
"Galaxy Tab S4 landscape": {
userAgent: "Mozilla/5.0 (Linux; Android 8.1.0; SM-T837A) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/148.0.7778.96 Safari/537.36",
viewport: {
width: 1138,
height: 712
},
deviceScaleFactor: 2.25,
isMobile: true,
hasTouch: true,
defaultBrowserType: "chromium"
},
"Galaxy Tab S9": {
userAgent: "Mozilla/5.0 (Linux; Android 14; SM-X710) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/148.0.7778.96 Safari/537.36",
viewport: {
width: 640,
height: 1024
},
deviceScaleFactor: 2.5,
isMobile: true,
hasTouch: true,
defaultBrowserType: "chromium"
},
"Galaxy Tab S9 landscape": {
userAgent: "Mozilla/5.0 (Linux; Android 14; SM-X710) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/148.0.7778.96 Safari/537.36",
viewport: {
width: 1024,
height: 640
},
deviceScaleFactor: 2.5,
isMobile: true,
hasTouch: true,
defaultBrowserType: "chromium"
},
"iPad (gen 5)": {
userAgent: "Mozilla/5.0 (iPad; CPU OS 12_2 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/26.4 Mobile/15E148 Safari/604.1",
viewport: {
width: 768,
height: 1024
},
deviceScaleFactor: 2,
isMobile: true,
hasTouch: true,
defaultBrowserType: "webkit"
},
"iPad (gen 5) landscape": {
userAgent: "Mozilla/5.0 (iPad; CPU OS 12_2 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/26.4 Mobile/15E148 Safari/604.1",
viewport: {
width: 1024,
height: 768
},
deviceScaleFactor: 2,
isMobile: true,
hasTouch: true,
defaultBrowserType: "webkit"
},
"iPad (gen 6)": {
userAgent: "Mozilla/5.0 (iPad; CPU OS 12_2 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/26.4 Mobile/15E148 Safari/604.1",
viewport: {
width: 768,
height: 1024
},
deviceScaleFactor: 2,
isMobile: true,
hasTouch: true,
defaultBrowserType: "webkit"
},
"iPad (gen 6) landscape": {
userAgent: "Mozilla/5.0 (iPad; CPU OS 12_2 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/26.4 Mobile/15E148 Safari/604.1",
viewport: {
width: 1024,
height: 768
},
deviceScaleFactor: 2,
isMobile: true,
hasTouch: true,
defaultBrowserType: "webkit"
},
"iPad (gen 7)": {
userAgent: "Mozilla/5.0 (iPad; CPU OS 12_2 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/26.4 Mobile/15E148 Safari/604.1",
viewport: {
width: 810,
height: 1080
},
deviceScaleFactor: 2,
isMobile: true,
hasTouch: true,
defaultBrowserType: "webkit"
},
"iPad (gen 7) landscape": {
userAgent: "Mozilla/5.0 (iPad; CPU OS 12_2 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/26.4 Mobile/15E148 Safari/604.1",
viewport: {
width: 1080,
height: 810
},
deviceScaleFactor: 2,
isMobile: true,
hasTouch: true,
defaultBrowserType: "webkit"
},
"iPad (gen 11)": {
userAgent: "Mozilla/5.0 (iPad; CPU OS 18_5 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/26.4 Mobile/19E241 Safari/604.1",
viewport: {
width: 656,
height: 944
},
deviceScaleFactor: 2.5,
isMobile: true,
hasTouch: true,
defaultBrowserType: "webkit"
},
"iPad (gen 11) landscape": {
userAgent: "Mozilla/5.0 (iPad; CPU OS 18_5 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/26.4 Mobile/19E241 Safari/604.1",
viewport: {
width: 944,
height: 656
},
deviceScaleFactor: 2.5,
isMobile: true,
hasTouch: true,
defaultBrowserType: "webkit"
},
"iPad Mini": {
userAgent: "Mozilla/5.0 (iPad; CPU OS 12_2 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/26.4 Mobile/15E148 Safari/604.1",
viewport: {
width: 768,
height: 1024
},
deviceScaleFactor: 2,
isMobile: true,
hasTouch: true,
defaultBrowserType: "webkit"
},
"iPad Mini landscape": {
userAgent: "Mozilla/5.0 (iPad; CPU OS 12_2 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/26.4 Mobile/15E148 Safari/604.1",
viewport: {
width: 1024,
height: 768
},
deviceScaleFactor: 2,
isMobile: true,
hasTouch: true,
defaultBrowserType: "webkit"
},
"iPad Pro 11": {
userAgent: "Mozilla/5.0 (iPad; CPU OS 12_2 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/26.4 Mobile/15E148 Safari/604.1",
viewport: {
width: 834,
height: 1194
},
deviceScaleFactor: 2,
isMobile: true,
hasTouch: true,
defaultBrowserType: "webkit"
},
"iPad Pro 11 landscape": {
userAgent: "Mozilla/5.0 (iPad; CPU OS 12_2 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/26.4 Mobile/15E148 Safari/604.1",
viewport: {
width: 1194,
height: 834
},
deviceScaleFactor: 2,
isMobile: true,
hasTouch: true,
defaultBrowserType: "webkit"
},
"iPhone 6": {
userAgent: "Mozilla/5.0 (iPhone; CPU iPhone OS 11_0 like Mac OS X) AppleWebKit/604.1.38 (KHTML, like Gecko) Version/26.4 Mobile/15A372 Safari/604.1",
viewport: {
width: 375,
height: 667
},
deviceScaleFactor: 2,
isMobile: true,
hasTouch: true,
defaultBrowserType: "webkit"
},
"iPhone 6 landscape": {
userAgent: "Mozilla/5.0 (iPhone; CPU iPhone OS 11_0 like Mac OS X) AppleWebKit/604.1.38 (KHTML, like Gecko) Version/26.4 Mobile/15A372 Safari/604.1",
viewport: {
width: 667,
height: 375
},
deviceScaleFactor: 2,
isMobile: true,
hasTouch: true,
defaultBrowserType: "webkit"
},
"iPhone 6 Plus": {
userAgent: "Mozilla/5.0 (iPhone; CPU iPhone OS 11_0 like Mac OS X) AppleWebKit/604.1.38 (KHTML, like Gecko) Version/26.4 Mobile/15A372 Safari/604.1",
viewport: {
width: 414,
height: 736
},
deviceScaleFactor: 3,
isMobile: true,
hasTouch: true,
defaultBrowserType: "webkit"
},
"iPhone 6 Plus landscape": {
userAgent: "Mozilla/5.0 (iPhone; CPU iPhone OS 11_0 like Mac OS X) AppleWebKit/604.1.38 (KHTML, like Gecko) Version/26.4 Mobile/15A372 Safari/604.1",
viewport: {
width: 736,
height: 414
},
deviceScaleFactor: 3,
isMobile: true,
hasTouch: true,
defaultBrowserType: "webkit"
},
"iPhone 7": {
userAgent: "Mozilla/5.0 (iPhone; CPU iPhone OS 11_0 like Mac OS X) AppleWebKit/604.1.38 (KHTML, like Gecko) Version/26.4 Mobile/15A372 Safari/604.1",
viewport: {
width: 375,
height: 667
},
deviceScaleFactor: 2,
isMobile: true,
hasTouch: true,
defaultBrowserType: "webkit"
},
"iPhone 7 landscape": {
userAgent: "Mozilla/5.0 (iPhone; CPU iPhone OS 11_0 like Mac OS X) AppleWebKit/604.1.38 (KHTML, like Gecko) Version/26.4 Mobile/15A372 Safari/604.1",
viewport: {
width: 667,
height: 375
},
deviceScaleFactor: 2,
isMobile: true,
hasTouch: true,
defaultBrowserType: "webkit"
},
"iPhone 7 Plus": {
userAgent: "Mozilla/5.0 (iPhone; CPU iPhone OS 11_0 like Mac OS X) AppleWebKit/604.1.38 (KHTML, like Gecko) Version/26.4 Mobile/15A372 Safari/604.1",
viewport: {
width: 414,
height: 736
},
deviceScaleFactor: 3,
isMobile: true,
hasTouch: true,
defaultBrowserType: "webkit"
},
"iPhone 7 Plus landscape": {
userAgent: "Mozilla/5.0 (iPhone; CPU iPhone OS 11_0 like Mac OS X) AppleWebKit/604.1.38 (KHTML, like Gecko) Version/26.4 Mobile/15A372 Safari/604.1",
viewport: {
width: 736,
height: 414
},
deviceScaleFactor: 3,
isMobile: true,
hasTouch: true,
defaultBrowserType: "webkit"
},
"iPhone 8": {
userAgent: "Mozilla/5.0 (iPhone; CPU iPhone OS 11_0 like Mac OS X) AppleWebKit/604.1.38 (KHTML, like Gecko) Version/26.4 Mobile/15A372 Safari/604.1",
viewport: {
width: 375,
height: 667
},
deviceScaleFactor: 2,
isMobile: true,
hasTouch: true,
defaultBrowserType: "webkit"
},
"iPhone 8 landscape": {
userAgent: "Mozilla/5.0 (iPhone; CPU iPhone OS 11_0 like Mac OS X) AppleWebKit/604.1.38 (KHTML, like Gecko) Version/26.4 Mobile/15A372 Safari/604.1",
viewport: {
width: 667,
height: 375
},
deviceScaleFactor: 2,
isMobile: true,
hasTouch: true,
defaultBrowserType: "webkit"
},
"iPhone 8 Plus": {
userAgent: "Mozilla/5.0 (iPhone; CPU iPhone OS 11_0 like Mac OS X) AppleWebKit/604.1.38 (KHTML, like Gecko) Version/26.4 Mobile/15A372 Safari/604.1",
viewport: {
width: 414,
height: 736
},
deviceScaleFactor: 3,
isMobile: true,
hasTouch: true,
defaultBrowserType: "webkit"
},
"iPhone 8 Plus landscape": {
userAgent: "Mozilla/5.0 (iPhone; CPU iPhone OS 11_0 like Mac OS X) AppleWebKit/604.1.38 (KHTML, like Gecko) Version/26.4 Mobile/15A372 Safari/604.1",
viewport: {
width: 736,
height: 414
},
deviceScaleFactor: 3,
isMobile: true,
hasTouch: true,
defaultBrowserType: "webkit"
},
"iPhone SE": {
userAgent: "Mozilla/5.0 (iPhone; CPU iPhone OS 10_3_1 like Mac OS X) AppleWebKit/603.1.30 (KHTML, like Gecko) Version/26.4 Mobile/14E304 Safari/602.1",
viewport: {
width: 320,
height: 568
},
deviceScaleFactor: 2,
isMobile: true,
hasTouch: true,
defaultBrowserType: "webkit"
},
"iPhone SE landscape": {
userAgent: "Mozilla/5.0 (iPhone; CPU iPhone OS 10_3_1 like Mac OS X) AppleWebKit/603.1.30 (KHTML, like Gecko) Version/26.4 Mobile/14E304 Safari/602.1",
viewport: {
width: 568,
height: 320
},
deviceScaleFactor: 2,
isMobile: true,
hasTouch: true,
defaultBrowserType: "webkit"
},
"iPhone SE (3rd gen)": {
userAgent: "Mozilla/5.0 (iPhone; CPU iPhone OS 18_5 like Mac OS X) AppleWebKit/603.1.30 (KHTML, like Gecko) Version/26.4 Mobile/19E241 Safari/602.1",
viewport: {
width: 375,
height: 667
},
deviceScaleFactor: 2,
isMobile: true,
hasTouch: true,
defaultBrowserType: "webkit"
},
"iPhone SE (3rd gen) landscape": {
userAgent: "Mozilla/5.0 (iPhone; CPU iPhone OS 18_5 like Mac OS X) AppleWebKit/603.1.30 (KHTML, like Gecko) Version/26.4 Mobile/19E241 Safari/602.1",
viewport: {
width: 667,
height: 375
},
deviceScaleFactor: 2,
isMobile: true,
hasTouch: true,
defaultBrowserType: "webkit"
},
"iPhone X": {
userAgent: "Mozilla/5.0 (iPhone; CPU iPhone OS 11_0 like Mac OS X) AppleWebKit/604.1.38 (KHTML, like Gecko) Version/26.4 Mobile/15A372 Safari/604.1",
viewport: {
width: 375,
height: 812
},
deviceScaleFactor: 3,
isMobile: true,
hasTouch: true,
defaultBrowserType: "webkit"
},
"iPhone X landscape": {
userAgent: "Mozilla/5.0 (iPhone; CPU iPhone OS 11_0 like Mac OS X) AppleWebKit/604.1.38 (KHTML, like Gecko) Version/26.4 Mobile/15A372 Safari/604.1",
viewport: {
width: 812,
height: 375
},
deviceScaleFactor: 3,
isMobile: true,
hasTouch: true,
defaultBrowserType: "webkit"
},
"iPhone XR": {
userAgent: "Mozilla/5.0 (iPhone; CPU iPhone OS 12_0 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/26.4 Mobile/15E148 Safari/604.1",
viewport: {
width: 414,
height: 896
},
deviceScaleFactor: 3,
isMobile: true,
hasTouch: true,
defaultBrowserType: "webkit"
},
"iPhone XR landscape": {
userAgent: "Mozilla/5.0 (iPhone; CPU iPhone OS 12_0 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/26.4 Mobile/15E148 Safari/604.1",
viewport: {
width: 896,
height: 414
},
deviceScaleFactor: 3,
isMobile: true,
hasTouch: true,
defaultBrowserType: "webkit"
},
"iPhone 11": {
userAgent: "Mozilla/5.0 (iPhone; CPU iPhone OS 12_2 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/26.4 Mobile/15E148 Safari/604.1",
screen: {
width: 414,
height: 896
},
viewport: {
width: 414,
height: 715
},
deviceScaleFactor: 2,
isMobile: true,
hasTouch: true,
defaultBrowserType: "webkit"
},
"iPhone 11 landscape": {
userAgent: "Mozilla/5.0 (iPhone; CPU iPhone OS 12_2 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/26.4 Mobile/15E148 Safari/604.1",
screen: {
width: 414,
height: 896
},
viewport: {
width: 800,
height: 364
},
deviceScaleFactor: 2,
isMobile: true,
hasTouch: true,
defaultBrowserType: "webkit"
},
"iPhone 11 Pro": {
userAgent: "Mozilla/5.0 (iPhone; CPU iPhone OS 12_2 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/26.4 Mobile/15E148 Safari/604.1",
screen: {
width: 375,
height: 812
},
viewport: {
width: 375,
height: 635
},
deviceScaleFactor: 3,
isMobile: true,
hasTouch: true,
defaultBrowserType: "webkit"
},
"iPhone 11 Pro landscape": {
userAgent: "Mozilla/5.0 (iPhone; CPU iPhone OS 12_2 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/26.4 Mobile/15E148 Safari/604.1",
screen: {
width: 375,
height: 812
},
viewport: {
width: 724,
height: 325
},
deviceScaleFactor: 3,
isMobile: true,
hasTouch: true,
defaultBrowserType: "webkit"
},
"iPhone 11 Pro Max": {
userAgent: "Mozilla/5.0 (iPhone; CPU iPhone OS 12_2 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/26.4 Mobile/15E148 Safari/604.1",
screen: {
width: 414,
height: 896
},
viewport: {
width: 414,
height: 715
},
deviceScaleFactor: 3,
isMobile: true,
hasTouch: true,
defaultBrowserType: "webkit"
},
"iPhone 11 Pro Max landscape": {
userAgent: "Mozilla/5.0 (iPhone; CPU iPhone OS 12_2 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/26.4 Mobile/15E148 Safari/604.1",
screen: {
width: 414,
height: 896
},
viewport: {
width: 808,
height: 364
},
deviceScaleFactor: 3,
isMobile: true,
hasTouch: true,
defaultBrowserType: "webkit"
},
"iPhone 12": {
userAgent: "Mozilla/5.0 (iPhone; CPU iPhone OS 14_4 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/26.4 Mobile/15E148 Safari/604.1",
screen: {
width: 390,
height: 844
},
viewport: {
width: 390,
height: 664
},
deviceScaleFactor: 3,
isMobile: true,
hasTouch: true,
defaultBrowserType: "webkit"
},
"iPhone 12 landscape": {
userAgent: "Mozilla/5.0 (iPhone; CPU iPhone OS 14_4 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/26.4 Mobile/15E148 Safari/604.1",
screen: {
width: 390,
height: 844
},
viewport: {
width: 750,
height: 340
},
deviceScaleFactor: 3,
isMobile: true,
hasTouch: true,
defaultBrowserType: "webkit"
},
"iPhone 12 Pro": {
userAgent: "Mozilla/5.0 (iPhone; CPU iPhone OS 14_4 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/26.4 Mobile/15E148 Safari/604.1",
screen: {
width: 390,
height: 844
},
viewport: {
width: 390,
height: 664
},
deviceScaleFactor: 3,
isMobile: true,
hasTouch: true,
defaultBrowserType: "webkit"
},
"iPhone 12 Pro landscape": {
userAgent: "Mozilla/5.0 (iPhone; CPU iPhone OS 14_4 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/26.4 Mobile/15E148 Safari/604.1",
screen: {
width: 390,
height: 844
},
viewport: {
width: 750,
height: 340
},
deviceScaleFactor: 3,
isMobile: true,
hasTouch: true,
defaultBrowserType: "webkit"
},
"iPhone 12 Pro Max": {
userAgent: "Mozilla/5.0 (iPhone; CPU iPhone OS 14_4 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/26.4 Mobile/15E148 Safari/604.1",
screen: {
width: 428,
height: 926
},
viewport: {
width: 428,
height: 746
},
deviceScaleFactor: 3,
isMobile: true,
hasTouch: true,
defaultBrowserType: "webkit"
},
"iPhone 12 Pro Max landscape": {
userAgent: "Mozilla/5.0 (iPhone; CPU iPhone OS 14_4 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/26.4 Mobile/15E148 Safari/604.1",
screen: {
width: 428,
height: 926
},
viewport: {
width: 832,
height: 378
},
deviceScaleFactor: 3,
isMobile: true,
hasTouch: true,
defaultBrowserType: "webkit"
},
"iPhone 12 Mini": {
userAgent: "Mozilla/5.0 (iPhone; CPU iPhone OS 14_4 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/26.4 Mobile/15E148 Safari/604.1",
screen: {
width: 375,
height: 812
},
viewport: {
width: 375,
height: 629
},
deviceScaleFactor: 3,
isMobile: true,
hasTouch: true,
defaultBrowserType: "webkit"
},
"iPhone 12 Mini landscape": {
userAgent: "Mozilla/5.0 (iPhone; CPU iPhone OS 14_4 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/26.4 Mobile/15E148 Safari/604.1",
screen: {
width: 375,
height: 812
},
viewport: {
width: 712,
height: 325
},
deviceScaleFactor: 3,
isMobile: true,
hasTouch: true,
defaultBrowserType: "webkit"
},
"iPhone 13": {
userAgent: "Mozilla/5.0 (iPhone; CPU iPhone OS 15_0 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/26.4 Mobile/15E148 Safari/604.1",
screen: {
width: 390,
height: 844
},
viewport: {
width: 390,
height: 664
},
deviceScaleFactor: 3,
isMobile: true,
hasTouch: true,
defaultBrowserType: "webkit"
},
"iPhone 13 landscape": {
userAgent: "Mozilla/5.0 (iPhone; CPU iPhone OS 15_0 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/26.4 Mobile/15E148 Safari/604.1",
screen: {
width: 390,
height: 844
},
viewport: {
width: 750,
height: 342
},
deviceScaleFactor: 3,
isMobile: true,
hasTouch: true,
defaultBrowserType: "webkit"
},
"iPhone 13 Pro": {
userAgent: "Mozilla/5.0 (iPhone; CPU iPhone OS 15_0 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/26.4 Mobile/15E148 Safari/604.1",
screen: {
width: 390,
height: 844
},
viewport: {
width: 390,
height: 664
},
deviceScaleFactor: 3,
isMobile: true,
hasTouch: true,
defaultBrowserType: "webkit"
},
"iPhone 13 Pro landscape": {
userAgent: "Mozilla/5.0 (iPhone; CPU iPhone OS 15_0 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/26.4 Mobile/15E148 Safari/604.1",
screen: {
width: 390,
height: 844
},
viewport: {
width: 750,
height: 342
},
deviceScaleFactor: 3,
isMobile: true,
hasTouch: true,
defaultBrowserType: "webkit"
},
"iPhone 13 Pro Max": {
userAgent: "Mozilla/5.0 (iPhone; CPU iPhone OS 15_0 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/26.4 Mobile/15E148 Safari/604.1",
screen: {
width: 428,
height: 926
},
viewport: {
width: 428,
height: 746
},
deviceScaleFactor: 3,
isMobile: true,
hasTouch: true,
defaultBrowserType: "webkit"
},
"iPhone 13 Pro Max landscape": {
userAgent: "Mozilla/5.0 (iPhone; CPU iPhone OS 15_0 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/26.4 Mobile/15E148 Safari/604.1",
screen: {
width: 428,
height: 926
},
viewport: {
width: 832,
height: 380
},
deviceScaleFactor: 3,
isMobile: true,
hasTouch: true,
defaultBrowserType: "webkit"
},
"iPhone 13 Mini": {
userAgent: "Mozilla/5.0 (iPhone; CPU iPhone OS 15_0 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/26.4 Mobile/15E148 Safari/604.1",
screen: {
width: 375,
height: 812
},
viewport: {
width: 375,
height: 629
},
deviceScaleFactor: 3,
isMobile: true,
hasTouch: true,
defaultBrowserType: "webkit"
},
"iPhone 13 Mini landscape": {
userAgent: "Mozilla/5.0 (iPhone; CPU iPhone OS 15_0 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/26.4 Mobile/15E148 Safari/604.1",
screen: {
width: 375,
height: 812
},
viewport: {
width: 712,
height: 327
},
deviceScaleFactor: 3,
isMobile: true,
hasTouch: true,
defaultBrowserType: "webkit"
},
"iPhone 14": {
userAgent: "Mozilla/5.0 (iPhone; CPU iPhone OS 16_0 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/26.4 Mobile/15E148 Safari/604.1",
screen: {
width: 390,
height: 844
},
viewport: {
width: 390,
height: 664
},
deviceScaleFactor: 3,
isMobile: true,
hasTouch: true,
defaultBrowserType: "webkit"
},
"iPhone 14 landscape": {
userAgent: "Mozilla/5.0 (iPhone; CPU iPhone OS 16_0 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/26.4 Mobile/15E148 Safari/604.1",
screen: {
width: 390,
height: 844
},
viewport: {
width: 750,
height: 340
},
deviceScaleFactor: 3,
isMobile: true,
hasTouch: true,
defaultBrowserType: "webkit"
},
"iPhone 14 Plus": {
userAgent: "Mozilla/5.0 (iPhone; CPU iPhone OS 16_0 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/26.4 Mobile/15E148 Safari/604.1",
screen: {
width: 428,
height: 926
},
viewport: {
width: 428,
height: 746
},
deviceScaleFactor: 3,
isMobile: true,
hasTouch: true,
defaultBrowserType: "webkit"
},
"iPhone 14 Plus landscape": {
userAgent: "Mozilla/5.0 (iPhone; CPU iPhone OS 16_0 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/26.4 Mobile/15E148 Safari/604.1",
screen: {
width: 428,
height: 926
},
viewport: {
width: 832,
height: 378
},
deviceScaleFactor: 3,
isMobile: true,
hasTouch: true,
defaultBrowserType: "webkit"
},
"iPhone 14 Pro": {
userAgent: "Mozilla/5.0 (iPhone; CPU iPhone OS 16_0 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/26.4 Mobile/15E148 Safari/604.1",
screen: {
width: 393,
height: 852
},
viewport: {
width: 393,
height: 660
},
deviceScaleFactor: 3,
isMobile: true,
hasTouch: true,
defaultBrowserType: "webkit"
},
"iPhone 14 Pro landscape": {
userAgent: "Mozilla/5.0 (iPhone; CPU iPhone OS 16_0 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/26.4 Mobile/15E148 Safari/604.1",
screen: {
width: 393,
height: 852
},
viewport: {
width: 734,
height: 343
},
deviceScaleFactor: 3,
isMobile: true,
hasTouch: true,
defaultBrowserType: "webkit"
},
"iPhone 14 Pro Max": {
userAgent: "Mozilla/5.0 (iPhone; CPU iPhone OS 16_0 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/26.4 Mobile/15E148 Safari/604.1",
screen: {
width: 430,
height: 932
},
viewport: {
width: 430,
height: 740
},
deviceScaleFactor: 3,
isMobile: true,
hasTouch: true,
defaultBrowserType: "webkit"
},
"iPhone 14 Pro Max landscape": {
userAgent: "Mozilla/5.0 (iPhone; CPU iPhone OS 16_0 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/26.4 Mobile/15E148 Safari/604.1",
screen: {
width: 430,
height: 932
},
viewport: {
width: 814,
height: 380
},
deviceScaleFactor: 3,
isMobile: true,
hasTouch: true,
defaultBrowserType: "webkit"
},
"iPhone 15": {
userAgent: "Mozilla/5.0 (iPhone; CPU iPhone OS 17_5 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/26.4 Mobile/15E148 Safari/604.1",
screen: {
width: 393,
height: 852
},
viewport: {
width: 393,
height: 659
},
deviceScaleFactor: 3,
isMobile: true,
hasTouch: true,
defaultBrowserType: "webkit"
},
"iPhone 15 landscape": {
userAgent: "Mozilla/5.0 (iPhone; CPU iPhone OS 17_5 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/26.4 Mobile/15E148 Safari/604.1",
screen: {
width: 393,
height: 852
},
viewport: {
width: 734,
height: 343
},
deviceScaleFactor: 3,
isMobile: true,
hasTouch: true,
defaultBrowserType: "webkit"
},
"iPhone 15 Plus": {
userAgent: "Mozilla/5.0 (iPhone; CPU iPhone OS 17_5 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/26.4 Mobile/15E148 Safari/604.1",
screen: {
width: 430,
height: 932
},
viewport: {
width: 430,
height: 739
},
deviceScaleFactor: 3,
isMobile: true,
hasTouch: true,
defaultBrowserType: "webkit"
},
"iPhone 15 Plus landscape": {
userAgent: "Mozilla/5.0 (iPhone; CPU iPhone OS 17_5 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/26.4 Mobile/15E148 Safari/604.1",
screen: {
width: 430,
height: 932
},
viewport: {
width: 814,
height: 380
},
deviceScaleFactor: 3,
isMobile: true,
hasTouch: true,
defaultBrowserType: "webkit"
},
"iPhone 15 Pro": {
userAgent: "Mozilla/5.0 (iPhone; CPU iPhone OS 17_5 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/26.4 Mobile/15E148 Safari/604.1",
screen: {
width: 393,
height: 852
},
viewport: {
width: 393,
height: 659
},
deviceScaleFactor: 3,
isMobile: true,
hasTouch: true,
defaultBrowserType: "webkit"
},
"iPhone 15 Pro landscape": {
userAgent: "Mozilla/5.0 (iPhone; CPU iPhone OS 17_5 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/26.4 Mobile/15E148 Safari/604.1",
screen: {
width: 393,
height: 852
},
viewport: {
width: 734,
height: 343
},
deviceScaleFactor: 3,
isMobile: true,
hasTouch: true,
defaultBrowserType: "webkit"
},
"iPhone 15 Pro Max": {
userAgent: "Mozilla/5.0 (iPhone; CPU iPhone OS 17_5 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/26.4 Mobile/15E148 Safari/604.1",
screen: {
width: 430,
height: 932
},
viewport: {
width: 430,
height: 739
},
deviceScaleFactor: 3,
isMobile: true,
hasTouch: true,
defaultBrowserType: "webkit"
},
"iPhone 15 Pro Max landscape": {
userAgent: "Mozilla/5.0 (iPhone; CPU iPhone OS 17_5 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/26.4 Mobile/15E148 Safari/604.1",
screen: {
width: 430,
height: 932
},
viewport: {
width: 814,
height: 380
},
deviceScaleFactor: 3,
isMobile: true,
hasTouch: true,
defaultBrowserType: "webkit"
},
"Kindle Fire HDX": {
userAgent: "Mozilla/5.0 (Linux; U; en-us; KFAPWI Build/JDQ39) AppleWebKit/535.19 (KHTML, like Gecko) Silk/3.13 Safari/535.19 Silk-Accelerated=true",
viewport: {
width: 800,
height: 1280
},
deviceScaleFactor: 2,
isMobile: true,
hasTouch: true,
defaultBrowserType: "webkit"
},
"Kindle Fire HDX landscape": {
userAgent: "Mozilla/5.0 (Linux; U; en-us; KFAPWI Build/JDQ39) AppleWebKit/535.19 (KHTML, like Gecko) Silk/3.13 Safari/535.19 Silk-Accelerated=true",
viewport: {
width: 1280,
height: 800
},
deviceScaleFactor: 2,
isMobile: true,
hasTouch: true,
defaultBrowserType: "webkit"
},
"LG Optimus L70": {
userAgent: "Mozilla/5.0 (Linux; U; Android 4.4.2; en-us; LGMS323 Build/KOT49I.MS32310c) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/148.0.7778.96 Mobile Safari/537.36",
viewport: {
width: 384,
height: 640
},
deviceScaleFactor: 1.25,
isMobile: true,
hasTouch: true,
defaultBrowserType: "chromium"
},
"LG Optimus L70 landscape": {
userAgent: "Mozilla/5.0 (Linux; U; Android 4.4.2; en-us; LGMS323 Build/KOT49I.MS32310c) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/148.0.7778.96 Mobile Safari/537.36",
viewport: {
width: 640,
height: 384
},
deviceScaleFactor: 1.25,
isMobile: true,
hasTouch: true,
defaultBrowserType: "chromium"
},
"Microsoft Lumia 550": {
userAgent: "Mozilla/5.0 (Windows Phone 10.0; Android 4.2.1; Microsoft; Lumia 550) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/148.0.7778.96 Mobile Safari/537.36 Edge/14.14263",
viewport: {
width: 360,
height: 640
},
deviceScaleFactor: 2,
isMobile: true,
hasTouch: true,
defaultBrowserType: "chromium"
},
"Microsoft Lumia 550 landscape": {
userAgent: "Mozilla/5.0 (Windows Phone 10.0; Android 4.2.1; Microsoft; Lumia 550) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/148.0.7778.96 Mobile Safari/537.36 Edge/14.14263",
viewport: {
width: 640,
height: 360
},
deviceScaleFactor: 2,
isMobile: true,
hasTouch: true,
defaultBrowserType: "chromium"
},
"Microsoft Lumia 950": {
userAgent: "Mozilla/5.0 (Windows Phone 10.0; Android 4.2.1; Microsoft; Lumia 950) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/148.0.7778.96 Mobile Safari/537.36 Edge/14.14263",
viewport: {
width: 360,
height: 640
},
deviceScaleFactor: 4,
isMobile: true,
hasTouch: true,
defaultBrowserType: "chromium"
},
"Microsoft Lumia 950 landscape": {
userAgent: "Mozilla/5.0 (Windows Phone 10.0; Android 4.2.1; Microsoft; Lumia 950) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/148.0.7778.96 Mobile Safari/537.36 Edge/14.14263",
viewport: {
width: 640,
height: 360
},
deviceScaleFactor: 4,
isMobile: true,
hasTouch: true,
defaultBrowserType: "chromium"
},
"Nexus 10": {
userAgent: "Mozilla/5.0 (Linux; Android 6.0.1; Nexus 10 Build/MOB31T) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/148.0.7778.96 Safari/537.36",
viewport: {
width: 800,
height: 1280
},
deviceScaleFactor: 2,
isMobile: true,
hasTouch: true,
defaultBrowserType: "chromium"
},
"Nexus 10 landscape": {
userAgent: "Mozilla/5.0 (Linux; Android 6.0.1; Nexus 10 Build/MOB31T) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/148.0.7778.96 Safari/537.36",
viewport: {
width: 1280,
height: 800
},
deviceScaleFactor: 2,
isMobile: true,
hasTouch: true,
defaultBrowserType: "chromium"
},
"Nexus 4": {
userAgent: "Mozilla/5.0 (Linux; Android 4.4.2; Nexus 4 Build/KOT49H) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/148.0.7778.96 Mobile Safari/537.36",
viewport: {
width: 384,
height: 640
},
deviceScaleFactor: 2,
isMobile: true,
hasTouch: true,
defaultBrowserType: "chromium"
},
"Nexus 4 landscape": {
userAgent: "Mozilla/5.0 (Linux; Android 4.4.2; Nexus 4 Build/KOT49H) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/148.0.7778.96 Mobile Safari/537.36",
viewport: {
width: 640,
height: 384
},
deviceScaleFactor: 2,
isMobile: true,
hasTouch: true,
defaultBrowserType: "chromium"
},
"Nexus 5": {
userAgent: "Mozilla/5.0 (Linux; Android 6.0; Nexus 5 Build/MRA58N) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/148.0.7778.96 Mobile Safari/537.36",
viewport: {
width: 360,
height: 640
},
deviceScaleFactor: 3,
isMobile: true,
hasTouch: true,
defaultBrowserType: "chromium"
},
"Nexus 5 landscape": {
userAgent: "Mozilla/5.0 (Linux; Android 6.0; Nexus 5 Build/MRA58N) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/148.0.7778.96 Mobile Safari/537.36",
viewport: {
width: 640,
height: 360
},
deviceScaleFactor: 3,
isMobile: true,
hasTouch: true,
defaultBrowserType: "chromium"
},
"Nexus 5X": {
userAgent: "Mozilla/5.0 (Linux; Android 8.0.0; Nexus 5X Build/OPR4.170623.006) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/148.0.7778.96 Mobile Safari/537.36",
viewport: {
width: 412,
height: 732
},
deviceScaleFactor: 2.625,
isMobile: true,
hasTouch: true,
defaultBrowserType: "chromium"
},
"Nexus 5X landscape": {
userAgent: "Mozilla/5.0 (Linux; Android 8.0.0; Nexus 5X Build/OPR4.170623.006) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/148.0.7778.96 Mobile Safari/537.36",
viewport: {
width: 732,
height: 412
},
deviceScaleFactor: 2.625,
isMobile: true,
hasTouch: true,
defaultBrowserType: "chromium"
},
"Nexus 6": {
userAgent: "Mozilla/5.0 (Linux; Android 7.1.1; Nexus 6 Build/N6F26U) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/148.0.7778.96 Mobile Safari/537.36",
viewport: {
width: 412,
height: 732
},
deviceScaleFactor: 3.5,
isMobile: true,
hasTouch: true,
defaultBrowserType: "chromium"
},
"Nexus 6 landscape": {
userAgent: "Mozilla/5.0 (Linux; Android 7.1.1; Nexus 6 Build/N6F26U) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/148.0.7778.96 Mobile Safari/537.36",
viewport: {
width: 732,
height: 412
},
deviceScaleFactor: 3.5,
isMobile: true,
hasTouch: true,
defaultBrowserType: "chromium"
},
"Nexus 6P": {
userAgent: "Mozilla/5.0 (Linux; Android 8.0.0; Nexus 6P Build/OPP3.170518.006) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/148.0.7778.96 Mobile Safari/537.36",
viewport: {
width: 412,
height: 732
},
deviceScaleFactor: 3.5,
isMobile: true,
hasTouch: true,
defaultBrowserType: "chromium"
},
"Nexus 6P landscape": {
userAgent: "Mozilla/5.0 (Linux; Android 8.0.0; Nexus 6P Build/OPP3.170518.006) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/148.0.7778.96 Mobile Safari/537.36",
viewport: {
width: 732,
height: 412
},
deviceScaleFactor: 3.5,
isMobile: true,
hasTouch: true,
defaultBrowserType: "chromium"
},
"Nexus 7": {
userAgent: "Mozilla/5.0 (Linux; Android 6.0.1; Nexus 7 Build/MOB30X) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/148.0.7778.96 Safari/537.36",
viewport: {
width: 600,
height: 960
},
deviceScaleFactor: 2,
isMobile: true,
hasTouch: true,
defaultBrowserType: "chromium"
},
"Nexus 7 landscape": {
userAgent: "Mozilla/5.0 (Linux; Android 6.0.1; Nexus 7 Build/MOB30X) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/148.0.7778.96 Safari/537.36",
viewport: {
width: 960,
height: 600
},
deviceScaleFactor: 2,
isMobile: true,
hasTouch: true,
defaultBrowserType: "chromium"
},
"Nokia Lumia 520": {
userAgent: "Mozilla/5.0 (compatible; MSIE 10.0; Windows Phone 8.0; Trident/6.0; IEMobile/10.0; ARM; Touch; NOKIA; Lumia 520)",
viewport: {
width: 320,
height: 533
},
deviceScaleFactor: 1.5,
isMobile: true,
hasTouch: true,
defaultBrowserType: "chromium"
},
"Nokia Lumia 520 landscape": {
userAgent: "Mozilla/5.0 (compatible; MSIE 10.0; Windows Phone 8.0; Trident/6.0; IEMobile/10.0; ARM; Touch; NOKIA; Lumia 520)",
viewport: {
width: 533,
height: 320
},
deviceScaleFactor: 1.5,
isMobile: true,
hasTouch: true,
defaultBrowserType: "chromium"
},
"Nokia N9": {
userAgent: "Mozilla/5.0 (MeeGo; NokiaN9) AppleWebKit/534.13 (KHTML, like Gecko) NokiaBrowser/8.5.0 Mobile Safari/534.13",
viewport: {
width: 480,
height: 854
},
deviceScaleFactor: 1,
isMobile: true,
hasTouch: true,
defaultBrowserType: "webkit"
},
"Nokia N9 landscape": {
userAgent: "Mozilla/5.0 (MeeGo; NokiaN9) AppleWebKit/534.13 (KHTML, like Gecko) NokiaBrowser/8.5.0 Mobile Safari/534.13",
viewport: {
width: 854,
height: 480
},
deviceScaleFactor: 1,
isMobile: true,
hasTouch: true,
defaultBrowserType: "webkit"
},
"Pixel 2": {
userAgent: "Mozilla/5.0 (Linux; Android 8.0; Pixel 2 Build/OPD3.170816.012) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/148.0.7778.96 Mobile Safari/537.36",
viewport: {
width: 411,
height: 731
},
deviceScaleFactor: 2.625,
isMobile: true,
hasTouch: true,
defaultBrowserType: "chromium"
},
"Pixel 2 landscape": {
userAgent: "Mozilla/5.0 (Linux; Android 8.0; Pixel 2 Build/OPD3.170816.012) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/148.0.7778.96 Mobile Safari/537.36",
viewport: {
width: 731,
height: 411
},
deviceScaleFactor: 2.625,
isMobile: true,
hasTouch: true,
defaultBrowserType: "chromium"
},
"Pixel 2 XL": {
userAgent: "Mozilla/5.0 (Linux; Android 8.0.0; Pixel 2 XL Build/OPD1.170816.004) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/148.0.7778.96 Mobile Safari/537.36",
viewport: {
width: 411,
height: 823
},
deviceScaleFactor: 3.5,
isMobile: true,
hasTouch: true,
defaultBrowserType: "chromium"
},
"Pixel 2 XL landscape": {
userAgent: "Mozilla/5.0 (Linux; Android 8.0.0; Pixel 2 XL Build/OPD1.170816.004) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/148.0.7778.96 Mobile Safari/537.36",
viewport: {
width: 823,
height: 411
},
deviceScaleFactor: 3.5,
isMobile: true,
hasTouch: true,
defaultBrowserType: "chromium"
},
"Pixel 3": {
userAgent: "Mozilla/5.0 (Linux; Android 9; Pixel 3 Build/PQ1A.181105.017.A1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/148.0.7778.96 Mobile Safari/537.36",
viewport: {
width: 393,
height: 786
},
deviceScaleFactor: 2.75,
isMobile: true,
hasTouch: true,
defaultBrowserType: "chromium"
},
"Pixel 3 landscape": {
userAgent: "Mozilla/5.0 (Linux; Android 9; Pixel 3 Build/PQ1A.181105.017.A1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/148.0.7778.96 Mobile Safari/537.36",
viewport: {
width: 786,
height: 393
},
deviceScaleFactor: 2.75,
isMobile: true,
hasTouch: true,
defaultBrowserType: "chromium"
},
"Pixel 4": {
userAgent: "Mozilla/5.0 (Linux; Android 10; Pixel 4) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/148.0.7778.96 Mobile Safari/537.36",
viewport: {
width: 353,
height: 745
},
deviceScaleFactor: 3,
isMobile: true,
hasTouch: true,
defaultBrowserType: "chromium"
},
"Pixel 4 landscape": {
userAgent: "Mozilla/5.0 (Linux; Android 10; Pixel 4) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/148.0.7778.96 Mobile Safari/537.36",
viewport: {
width: 745,
height: 353
},
deviceScaleFactor: 3,
isMobile: true,
hasTouch: true,
defaultBrowserType: "chromium"
},
"Pixel 4a (5G)": {
userAgent: "Mozilla/5.0 (Linux; Android 11; Pixel 4a (5G)) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/148.0.7778.96 Mobile Safari/537.36",
screen: {
width: 412,
height: 892
},
viewport: {
width: 412,
height: 765
},
deviceScaleFactor: 2.63,
isMobile: true,
hasTouch: true,
defaultBrowserType: "chromium"
},
"Pixel 4a (5G) landscape": {
userAgent: "Mozilla/5.0 (Linux; Android 11; Pixel 4a (5G)) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/148.0.7778.96 Mobile Safari/537.36",
screen: {
height: 892,
width: 412
},
viewport: {
width: 840,
height: 312
},
deviceScaleFactor: 2.63,
isMobile: true,
hasTouch: true,
defaultBrowserType: "chromium"
},
"Pixel 5": {
userAgent: "Mozilla/5.0 (Linux; Android 11; Pixel 5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/148.0.7778.96 Mobile Safari/537.36",
screen: {
width: 393,
height: 851
},
viewport: {
width: 393,
height: 727
},
deviceScaleFactor: 2.75,
isMobile: true,
hasTouch: true,
defaultBrowserType: "chromium"
},
"Pixel 5 landscape": {
userAgent: "Mozilla/5.0 (Linux; Android 11; Pixel 5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/148.0.7778.96 Mobile Safari/537.36",
screen: {
width: 851,
height: 393
},
viewport: {
width: 802,
height: 293
},
deviceScaleFactor: 2.75,
isMobile: true,
hasTouch: true,
defaultBrowserType: "chromium"
},
"Pixel 7": {
userAgent: "Mozilla/5.0 (Linux; Android 14; Pixel 7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/148.0.7778.96 Mobile Safari/537.36",
screen: {
width: 412,
height: 915
},
viewport: {
width: 412,
height: 839
},
deviceScaleFactor: 2.625,
isMobile: true,
hasTouch: true,
defaultBrowserType: "chromium"
},
"Pixel 7 landscape": {
userAgent: "Mozilla/5.0 (Linux; Android 14; Pixel 7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/148.0.7778.96 Mobile Safari/537.36",
screen: {
width: 915,
height: 412
},
viewport: {
width: 863,
height: 360
},
deviceScaleFactor: 2.625,
isMobile: true,
hasTouch: true,
defaultBrowserType: "chromium"
},
"Moto G4": {
userAgent: "Mozilla/5.0 (Linux; Android 7.0; Moto G (4)) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/148.0.7778.96 Mobile Safari/537.36",
viewport: {
width: 360,
height: 640
},
deviceScaleFactor: 3,
isMobile: true,
hasTouch: true,
defaultBrowserType: "chromium"
},
"Moto G4 landscape": {
userAgent: "Mozilla/5.0 (Linux; Android 7.0; Moto G (4)) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/148.0.7778.96 Mobile Safari/537.36",
viewport: {
width: 640,
height: 360
},
deviceScaleFactor: 3,
isMobile: true,
hasTouch: true,
defaultBrowserType: "chromium"
},
"Desktop Chrome HiDPI": {
userAgent: "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/148.0.7778.96 Safari/537.36",
screen: {
width: 1792,
height: 1120
},
viewport: {
width: 1280,
height: 720
},
deviceScaleFactor: 2,
isMobile: false,
hasTouch: false,
defaultBrowserType: "chromium"
},
"Desktop Edge HiDPI": {
userAgent: "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/148.0.7778.96 Safari/537.36 Edg/148.0.7778.96",
screen: {
width: 1792,
height: 1120
},
viewport: {
width: 1280,
height: 720
},
deviceScaleFactor: 2,
isMobile: false,
hasTouch: false,
defaultBrowserType: "chromium"
},
"Desktop Firefox HiDPI": {
userAgent: "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:150.0.2) Gecko/20100101 Firefox/150.0.2",
screen: {
width: 1792,
height: 1120
},
viewport: {
width: 1280,
height: 720
},
deviceScaleFactor: 2,
isMobile: false,
hasTouch: false,
defaultBrowserType: "firefox"
},
"Desktop Safari": {
userAgent: "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/26.4 Safari/605.1.15",
screen: {
width: 1792,
height: 1120
},
viewport: {
width: 1280,
height: 720
},
deviceScaleFactor: 2,
isMobile: false,
hasTouch: false,
defaultBrowserType: "webkit"
},
"Desktop Chrome": {
userAgent: "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/148.0.7778.96 Safari/537.36",
screen: {
width: 1920,
height: 1080
},
viewport: {
width: 1280,
height: 720
},
deviceScaleFactor: 1,
isMobile: false,
hasTouch: false,
defaultBrowserType: "chromium"
},
"Desktop Edge": {
userAgent: "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/148.0.7778.96 Safari/537.36 Edg/148.0.7778.96",
screen: {
width: 1920,
height: 1080
},
viewport: {
width: 1280,
height: 720
},
deviceScaleFactor: 1,
isMobile: false,
hasTouch: false,
defaultBrowserType: "chromium"
},
"Desktop Firefox": {
userAgent: "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:150.0.2) Gecko/20100101 Firefox/150.0.2",
screen: {
width: 1920,
height: 1080
},
viewport: {
width: 1280,
height: 720
},
deviceScaleFactor: 1,
isMobile: false,
hasTouch: false,
defaultBrowserType: "firefox"
}
};
}
});
// packages/playwright-core/src/server/deviceDescriptors.ts
var deviceDescriptors;
var init_deviceDescriptors = __esm({
"packages/playwright-core/src/server/deviceDescriptors.ts"() {
"use strict";
init_deviceDescriptorsSource();
deviceDescriptors = deviceDescriptorsSource_default;
}
});
// packages/playwright-core/src/server/codegen/csharp.ts
function formatObject2(value2, indent = " ", name = "") {
if (typeof value2 === "string") {
if (["colorScheme", "modifiers", "button", "recordHarContent", "recordHarMode", "serviceWorkers"].includes(name))
return `${getEnumName(name)}.${toPascal(value2)}`;
return quote(value2);
}
if (Array.isArray(value2))
return `new[] { ${value2.map((o) => formatObject2(o, indent, name)).join(", ")} }`;
if (typeof value2 === "object") {
const keys = Object.keys(value2).filter((key) => value2[key] !== void 0).sort();
if (!keys.length)
return `new()`;
const tokens = [];
for (const key of keys) {
const property = getPropertyName(key);
tokens.push(`${property} = ${formatObject2(value2[key], indent, key)},`);
}
return `new()
{
${indent}${tokens.join(`
${indent}`)}
${indent}}`;
}
if (name === "latitude" || name === "longitude")
return String(value2) + "m";
return String(value2);
}
function getEnumName(value2) {
switch (value2) {
case "modifiers":
return "KeyboardModifier";
case "button":
return "MouseButton";
case "recordHarMode":
return "HarMode";
case "recordHarContent":
return "HarContentPolicy";
case "serviceWorkers":
return "ServiceWorkerPolicy";
default:
return toPascal(value2);
}
}
function getPropertyName(key) {
switch (key) {
case "storageState":
return "StorageStatePath";
case "viewport":
return "ViewportSize";
default:
return toPascal(key);
}
}
function toPascal(value2) {
return value2[0].toUpperCase() + value2.slice(1);
}
function formatContextOptions(contextOptions, deviceName) {
const options2 = { ...contextOptions };
delete options2.recordHar;
const device = deviceName && deviceDescriptors[deviceName];
if (!device) {
if (!Object.entries(options2).length)
return "";
return formatObject2(options2, " ");
}
if (!Object.entries(sanitizeDeviceOptions(device, options2)).length)
return `playwright.Devices[${quote(deviceName)}]`;
delete options2["defaultBrowserType"];
return formatObject2(options2, " ");
}
function quote(text2) {
return escapeWithQuotes(text2, '"');
}
var CSharpLanguageGenerator, CSharpFormatter;
var init_csharp = __esm({
"packages/playwright-core/src/server/codegen/csharp.ts"() {
"use strict";
init_locatorGenerators();
init_stringUtils();
init_language();
init_deviceDescriptors();
CSharpLanguageGenerator = class {
constructor(mode) {
this.groupName = ".NET C#";
this.highlighter = "csharp";
if (mode === "library") {
this.name = "Library";
this.id = "csharp";
} else if (mode === "mstest") {
this.name = "MSTest";
this.id = "csharp-mstest";
} else if (mode === "nunit") {
this.name = "NUnit";
this.id = "csharp-nunit";
} else if (mode === "xunit") {
this.name = "xUnit";
this.id = "csharp-xunit";
} else {
throw new Error(`Unknown C# language mode: ${mode}`);
}
this._mode = mode;
}
generateAction(actionInContext) {
const action = this._generateActionInner(actionInContext);
if (action)
return action;
return "";
}
_generateActionInner(actionInContext) {
const action = actionInContext.action;
if (this._mode !== "library" && (action.name === "openPage" || action.name === "closePage"))
return "";
const pageAlias = this._formatPageAlias(actionInContext.frame.pageAlias);
const formatter = new CSharpFormatter(this._mode === "library" ? 0 : 8);
if (action.name === "openPage") {
formatter.add(`var ${pageAlias} = await context.NewPageAsync();`);
if (action.url && action.url !== "about:blank" && action.url !== "chrome://newtab/")
formatter.add(`await ${pageAlias}.GotoAsync(${quote(action.url)});`);
return formatter.format();
}
const locators = actionInContext.frame.framePath.map((selector) => `.${this._asLocator(selector)}.ContentFrame`);
const subject = `${pageAlias}${locators.join("")}`;
const signals = toSignalMap(action);
if (signals.dialog) {
formatter.add(` void ${pageAlias}_Dialog${signals.dialog.dialogAlias}_EventHandler(object sender, IDialog dialog)
{
Console.WriteLine($"Dialog message: {dialog.Message}");
dialog.DismissAsync();
${pageAlias}.Dialog -= ${pageAlias}_Dialog${signals.dialog.dialogAlias}_EventHandler;
}
${pageAlias}.Dialog += ${pageAlias}_Dialog${signals.dialog.dialogAlias}_EventHandler;`);
}
const lines = [];
lines.push(this._generateActionCall(subject, actionInContext));
if (signals.download) {
lines.unshift(`var download${signals.download.downloadAlias} = await ${pageAlias}.RunAndWaitForDownloadAsync(async () =>
{`);
lines.push(`});`);
}
if (signals.popup) {
lines.unshift(`var ${this._formatPageAlias(signals.popup.popupAlias)} = await ${pageAlias}.RunAndWaitForPopupAsync(async () =>
{`);
lines.push(`});`);
}
for (const line of lines)
formatter.add(line);
return formatter.format();
}
_formatPageAlias(pageAlias) {
if (this._mode === "library")
return pageAlias;
if (pageAlias === "page")
return "Page";
return pageAlias;
}
_generateActionCall(subject, actionInContext) {
const action = actionInContext.action;
switch (action.name) {
case "openPage":
throw Error("Not reached");
case "closePage":
return `await ${subject}.CloseAsync();`;
case "click": {
let method = "Click";
if (action.clickCount === 2)
method = "DblClick";
const options2 = toClickOptionsForSourceCode(action);
if (!Object.entries(options2).length)
return `await ${subject}.${this._asLocator(action.selector)}.${method}Async();`;
const optionsString = formatObject2(options2, " ");
return `await ${subject}.${this._asLocator(action.selector)}.${method}Async(${optionsString});`;
}
case "hover": {
const optionsString = action.position ? formatObject2({ position: action.position }, " ") : "";
return `await ${subject}.${this._asLocator(action.selector)}.HoverAsync(${optionsString});`;
}
case "check":
return `await ${subject}.${this._asLocator(action.selector)}.CheckAsync();`;
case "uncheck":
return `await ${subject}.${this._asLocator(action.selector)}.UncheckAsync();`;
case "fill":
return `await ${subject}.${this._asLocator(action.selector)}.FillAsync(${quote(action.text)});`;
case "setInputFiles":
return `await ${subject}.${this._asLocator(action.selector)}.SetInputFilesAsync(${formatObject2(action.files)});`;
case "press": {
const modifiers = toKeyboardModifiers(action.modifiers);
const shortcut = [...modifiers, action.key].join("+");
return `await ${subject}.${this._asLocator(action.selector)}.PressAsync(${quote(shortcut)});`;
}
case "navigate":
return `await ${subject}.GotoAsync(${quote(action.url)});`;
case "select":
return `await ${subject}.${this._asLocator(action.selector)}.SelectOptionAsync(${formatObject2(action.options)});`;
case "assertText":
return `await Expect(${subject}.${this._asLocator(action.selector)}).${action.substring ? "ToContainTextAsync" : "ToHaveTextAsync"}(${quote(action.text)});`;
case "assertChecked":
return `await Expect(${subject}.${this._asLocator(action.selector)})${action.checked ? "" : ".Not"}.ToBeCheckedAsync();`;
case "assertVisible":
return `await Expect(${subject}.${this._asLocator(action.selector)}).ToBeVisibleAsync();`;
case "assertValue": {
const assertion = action.value ? `ToHaveValueAsync(${quote(action.value)})` : `ToBeEmptyAsync()`;
return `await Expect(${subject}.${this._asLocator(action.selector)}).${assertion};`;
}
case "assertSnapshot":
return `await Expect(${subject}.${this._asLocator(action.selector)}).ToMatchAriaSnapshotAsync(${quote(action.ariaSnapshot)});`;
}
}
_asLocator(selector) {
return asLocator("csharp", selector);
}
generateHeader(options2) {
if (this._mode === "library")
return this.generateStandaloneHeader(options2);
return this.generateTestRunnerHeader(options2);
}
generateStandaloneHeader(options2) {
const formatter = new CSharpFormatter(0);
formatter.add(`
using Microsoft.Playwright;
using System;
using System.Threading.Tasks;
using var playwright = await Playwright.CreateAsync();
await using var browser = await playwright.${toPascal(options2.browserName)}.LaunchAsync(${formatObject2(options2.launchOptions, " ")});
var context = await browser.NewContextAsync(${formatContextOptions(options2.contextOptions, options2.deviceName)});`);
if (options2.contextOptions.recordHar) {
const url2 = options2.contextOptions.recordHar.urlFilter;
formatter.add(` await context.RouteFromHARAsync(${quote(options2.contextOptions.recordHar.path)}${url2 ? `, ${formatObject2({ url: url2 }, " ")}` : ""});`);
}
formatter.newLine();
return formatter.format();
}
generateTestRunnerHeader(options2) {
const formatter = new CSharpFormatter(0);
const playwrightNamespace = this._mode === "nunit" ? "NUnit" : this._mode === "xunit" ? "Xunit" : "MSTest";
const classAttributes = this._mode === "nunit" ? `[Parallelizable(ParallelScope.Self)]
[TestFixture]
` : this._mode === "mstest" ? `[TestClass]
` : "";
formatter.add(`
using Microsoft.Playwright.${playwrightNamespace};
using Microsoft.Playwright;${this._mode === "xunit" ? `
using Xunit;` : ""}
${classAttributes}public class Tests : PageTest
{`);
const formattedContextOptions = formatContextOptions(options2.contextOptions, options2.deviceName);
if (formattedContextOptions) {
formatter.add(`public override BrowserNewContextOptions ContextOptions()
{
return ${formattedContextOptions};
}`);
formatter.newLine();
}
const testAttribute = this._mode === "nunit" ? "Test" : this._mode === "xunit" ? "Fact" : "TestMethod";
formatter.add(` [${testAttribute}]
public async Task MyTest()
{`);
if (options2.contextOptions.recordHar) {
const url2 = options2.contextOptions.recordHar.urlFilter;
formatter.add(` await Context.RouteFromHARAsync(${quote(options2.contextOptions.recordHar.path)}${url2 ? `, ${formatObject2({ url: url2 }, " ")}` : ""});`);
}
return formatter.format();
}
generateFooter(saveStorage) {
const offset = this._mode === "library" ? "" : " ";
let storageStateLine = saveStorage ? `
${offset}await context.StorageStateAsync(new()
${offset}{
${offset} Path = ${quote(saveStorage)}
${offset}});
` : "";
if (this._mode !== "library")
storageStateLine += ` }
}
`;
return storageStateLine;
}
};
CSharpFormatter = class {
constructor(offset = 0) {
this._lines = [];
this._baseIndent = " ".repeat(4);
this._baseOffset = " ".repeat(offset);
}
prepend(text2) {
this._lines = text2.trim().split("\n").map((line) => line.trim()).concat(this._lines);
}
add(text2) {
this._lines.push(...text2.trim().split("\n").map((line) => line.trim()));
}
newLine() {
this._lines.push("");
}
format() {
let spaces = "";
let previousLine = "";
return this._lines.map((line) => {
if (line === "")
return line;
if (line.startsWith("}") || line.startsWith("]") || line.includes("});") || line === ");")
spaces = spaces.substring(this._baseIndent.length);
const extraSpaces = /^(for|while|if).*\(.*\)$/.test(previousLine) ? this._baseIndent : "";
previousLine = line;
line = spaces + extraSpaces + line;
if (line.endsWith("{") || line.endsWith("[") || line.endsWith("("))
spaces += this._baseIndent;
if (line.endsWith("));"))
spaces = spaces.substring(this._baseIndent.length);
return this._baseOffset + line;
}).join("\n");
}
};
}
});
// packages/playwright-core/src/server/codegen/javascript.ts
function formatOptions(value2, hasArguments) {
const keys = Object.keys(value2).filter((key) => value2[key] !== void 0);
if (!keys.length)
return "";
return (hasArguments ? ", " : "") + formatObject(value2);
}
function formatContextOptions2(options2, deviceName, isTest) {
const device = deviceName && deviceDescriptors[deviceName];
options2 = { ...options2, recordHar: void 0 };
if (!device)
return formatObjectOrVoid(options2);
let serializedObject = formatObjectOrVoid(sanitizeDeviceOptions(device, options2));
if (!serializedObject)
serializedObject = "{\n}";
const lines = serializedObject.split("\n");
lines.splice(1, 0, `...devices[${quote2(deviceName)}],`);
return lines.join("\n");
}
function quote2(text2) {
return escapeWithQuotes(text2, "'");
}
function wrapWithStep(description, body) {
return description ? `await test.step(\`${description}\`, async () => {
${body}
});` : body;
}
function quoteMultiline(text2, indent = " ") {
const escape = (text3) => text3.replace(/\\/g, "\\\\").replace(/`/g, "\\`").replace(/\$\{/g, "\\${");
const lines = text2.split("\n");
if (lines.length === 1)
return "`" + escape(text2) + "`";
return "`\n" + lines.map((line) => indent + escape(line).replace(/\${/g, "\\${")).join("\n") + `
${indent}\``;
}
function isMultilineString(text2) {
return text2.match(/`[\S\s]*`/)?.[0].includes("\n");
}
var JavaScriptLanguageGenerator, JavaScriptFormatter;
var init_javascript2 = __esm({
"packages/playwright-core/src/server/codegen/javascript.ts"() {
"use strict";
init_locatorGenerators();
init_stringUtils();
init_language();
init_deviceDescriptors();
JavaScriptLanguageGenerator = class {
constructor(isTest) {
this.groupName = "Node.js";
this.highlighter = "javascript";
this.id = isTest ? "playwright-test" : "javascript";
this.name = isTest ? "Test Runner" : "Library";
this._isTest = isTest;
}
generateAction(actionInContext) {
const action = actionInContext.action;
if (this._isTest && (action.name === "openPage" || action.name === "closePage"))
return "";
const pageAlias = actionInContext.frame.pageAlias;
const formatter = new JavaScriptFormatter(2);
if (action.name === "openPage") {
formatter.add(`const ${pageAlias} = await context.newPage();`);
if (action.url && action.url !== "about:blank" && action.url !== "chrome://newtab/")
formatter.add(`await ${pageAlias}.goto(${quote2(action.url)});`);
return formatter.format();
}
const locators = actionInContext.frame.framePath.map((selector) => `.${this._asLocator(selector)}.contentFrame()`);
const subject = `${pageAlias}${locators.join("")}`;
const signals = toSignalMap(action);
if (signals.dialog) {
formatter.add(` ${pageAlias}.once('dialog', dialog => {
console.log(\`Dialog message: \${dialog.message()}\`);
dialog.dismiss().catch(() => {});
});`);
}
if (signals.popup)
formatter.add(`const ${signals.popup.popupAlias}Promise = ${pageAlias}.waitForEvent('popup');`);
if (signals.download)
formatter.add(`const download${signals.download.downloadAlias}Promise = ${pageAlias}.waitForEvent('download');`);
formatter.add(wrapWithStep(actionInContext.description, this._generateActionCall(subject, actionInContext)));
if (signals.popup)
formatter.add(`const ${signals.popup.popupAlias} = await ${signals.popup.popupAlias}Promise;`);
if (signals.download)
formatter.add(`const download${signals.download.downloadAlias} = await download${signals.download.downloadAlias}Promise;`);
return formatter.format();
}
_generateActionCall(subject, actionInContext) {
const action = actionInContext.action;
switch (action.name) {
case "openPage":
throw Error("Not reached");
case "closePage":
return `await ${subject}.close();`;
case "click": {
let method = "click";
if (action.clickCount === 2)
method = "dblclick";
const options2 = toClickOptionsForSourceCode(action);
const optionsString = formatOptions(options2, false);
return `await ${subject}.${this._asLocator(action.selector)}.${method}(${optionsString});`;
}
case "hover":
return `await ${subject}.${this._asLocator(action.selector)}.hover(${formatOptions({ position: action.position }, false)});`;
case "check":
return `await ${subject}.${this._asLocator(action.selector)}.check();`;
case "uncheck":
return `await ${subject}.${this._asLocator(action.selector)}.uncheck();`;
case "fill":
return `await ${subject}.${this._asLocator(action.selector)}.fill(${quote2(action.text)});`;
case "setInputFiles":
return `await ${subject}.${this._asLocator(action.selector)}.setInputFiles(${formatObject(action.files.length === 1 ? action.files[0] : action.files)});`;
case "press": {
const modifiers = toKeyboardModifiers(action.modifiers);
const shortcut = [...modifiers, action.key].join("+");
return `await ${subject}.${this._asLocator(action.selector)}.press(${quote2(shortcut)});`;
}
case "navigate":
return `await ${subject}.goto(${quote2(action.url)});`;
case "select":
return `await ${subject}.${this._asLocator(action.selector)}.selectOption(${formatObject(action.options.length === 1 ? action.options[0] : action.options)});`;
case "assertText":
return `${this._isTest ? "" : "// "}await expect(${subject}.${this._asLocator(action.selector)}).${action.substring ? "toContainText" : "toHaveText"}(${quote2(action.text)});`;
case "assertChecked":
return `${this._isTest ? "" : "// "}await expect(${subject}.${this._asLocator(action.selector)})${action.checked ? "" : ".not"}.toBeChecked();`;
case "assertVisible":
return `${this._isTest ? "" : "// "}await expect(${subject}.${this._asLocator(action.selector)}).toBeVisible();`;
case "assertValue": {
const assertion = action.value ? `toHaveValue(${quote2(action.value)})` : `toBeEmpty()`;
return `${this._isTest ? "" : "// "}await expect(${subject}.${this._asLocator(action.selector)}).${assertion};`;
}
case "assertSnapshot": {
const commentIfNeeded = this._isTest ? "" : "// ";
return `${commentIfNeeded}await expect(${subject}.${this._asLocator(action.selector)}).toMatchAriaSnapshot(${quoteMultiline(action.ariaSnapshot, `${commentIfNeeded} `)});`;
}
}
}
_asLocator(selector) {
return asLocator("javascript", selector);
}
generateHeader(options2) {
if (this._isTest)
return this.generateTestHeader(options2);
return this.generateStandaloneHeader(options2);
}
generateFooter(saveStorage) {
if (this._isTest)
return this.generateTestFooter(saveStorage);
return this.generateStandaloneFooter(saveStorage);
}
generateTestHeader(options2) {
const formatter = new JavaScriptFormatter();
const useText = formatContextOptions2(options2.contextOptions, options2.deviceName, this._isTest);
formatter.add(`
import { test, expect${options2.deviceName ? ", devices" : ""} } from '@playwright/test';
${useText ? "\ntest.use(" + useText + ");\n" : ""}
test('test', async ({ page }) => {`);
if (options2.contextOptions.recordHar) {
const url2 = options2.contextOptions.recordHar.urlFilter;
formatter.add(` await page.routeFromHAR(${quote2(options2.contextOptions.recordHar.path)}${url2 ? `, ${formatOptions({ url: url2 }, false)}` : ""});`);
}
return formatter.format();
}
generateTestFooter(saveStorage) {
return `});`;
}
generateStandaloneHeader(options2) {
const formatter = new JavaScriptFormatter();
formatter.add(`
const { ${options2.browserName}${options2.deviceName ? ", devices" : ""} } = require('playwright');
(async () => {
const browser = await ${options2.browserName}.launch(${formatObjectOrVoid(options2.launchOptions)});
const context = await browser.newContext(${formatContextOptions2(options2.contextOptions, options2.deviceName, false)});`);
if (options2.contextOptions.recordHar)
formatter.add(` await context.routeFromHAR(${quote2(options2.contextOptions.recordHar.path)});`);
return formatter.format();
}
generateStandaloneFooter(saveStorage) {
const storageStateLine = saveStorage ? `
await context.storageState({ path: ${quote2(saveStorage)} });` : "";
return `
// ---------------------${storageStateLine}
await context.close();
await browser.close();
})();`;
}
};
JavaScriptFormatter = class {
constructor(offset = 0) {
this._lines = [];
this._baseIndent = " ".repeat(2);
this._baseOffset = " ".repeat(offset);
}
prepend(text2) {
const trim = isMultilineString(text2) ? (line) => line : (line) => line.trim();
this._lines = text2.trim().split("\n").map(trim).concat(this._lines);
}
add(text2) {
const trim = isMultilineString(text2) ? (line) => line : (line) => line.trim();
this._lines.push(...text2.trim().split("\n").map(trim));
}
newLine() {
this._lines.push("");
}
format() {
let spaces = "";
let previousLine = "";
return this._lines.map((line) => {
if (line === "")
return line;
if (line.startsWith("}") || line.startsWith("]"))
spaces = spaces.substring(this._baseIndent.length);
const extraSpaces = /^(for|while|if|try).*\(.*\)$/.test(previousLine) ? this._baseIndent : "";
previousLine = line;
const callCarryOver = line.startsWith(".set");
line = spaces + extraSpaces + (callCarryOver ? this._baseIndent : "") + line;
if (line.endsWith("{") || line.endsWith("["))
spaces += this._baseIndent;
return this._baseOffset + line;
}).join("\n");
}
};
}
});
// packages/playwright-core/src/server/codegen/java.ts
function formatPath(files) {
if (Array.isArray(files)) {
if (files.length === 0)
return "new Path[0]";
return `new Path[] {${files.map((s) => "Paths.get(" + quote3(s) + ")").join(", ")}}`;
}
return `Paths.get(${quote3(files)})`;
}
function formatSelectOption(options2) {
if (Array.isArray(options2)) {
if (options2.length === 0)
return "new String[0]";
return `new String[] {${options2.map((s) => quote3(s)).join(", ")}}`;
}
return quote3(options2);
}
function formatLaunchOptions(options2) {
const lines = [];
if (!Object.keys(options2).filter((key) => options2[key] !== void 0).length)
return "";
lines.push("new BrowserType.LaunchOptions()");
if (options2.channel)
lines.push(` .setChannel(${quote3(options2.channel)})`);
if (typeof options2.headless === "boolean")
lines.push(` .setHeadless(false)`);
return lines.join("\n");
}
function formatContextOptions3(contextOptions, deviceName) {
const lines = [];
if (!Object.keys(contextOptions).length && !deviceName)
return "";
const device = deviceName ? deviceDescriptors[deviceName] : {};
const options2 = { ...device, ...contextOptions };
lines.push("new Browser.NewContextOptions()");
if (options2.acceptDownloads)
lines.push(` .setAcceptDownloads(true)`);
if (options2.bypassCSP)
lines.push(` .setBypassCSP(true)`);
if (options2.colorScheme)
lines.push(` .setColorScheme(ColorScheme.${options2.colorScheme.toUpperCase()})`);
if (options2.deviceScaleFactor)
lines.push(` .setDeviceScaleFactor(${options2.deviceScaleFactor})`);
if (options2.geolocation)
lines.push(` .setGeolocation(${options2.geolocation.latitude}, ${options2.geolocation.longitude})`);
if (options2.hasTouch)
lines.push(` .setHasTouch(${options2.hasTouch})`);
if (options2.isMobile)
lines.push(` .setIsMobile(${options2.isMobile})`);
if (options2.locale)
lines.push(` .setLocale(${quote3(options2.locale)})`);
if (options2.proxy)
lines.push(` .setProxy(new Proxy(${quote3(options2.proxy.server)}))`);
if (options2.serviceWorkers)
lines.push(` .setServiceWorkers(ServiceWorkerPolicy.${options2.serviceWorkers.toUpperCase()})`);
if (options2.storageState)
lines.push(` .setStorageStatePath(Paths.get(${quote3(options2.storageState)}))`);
if (options2.timezoneId)
lines.push(` .setTimezoneId(${quote3(options2.timezoneId)})`);
if (options2.userAgent)
lines.push(` .setUserAgent(${quote3(options2.userAgent)})`);
if (options2.viewport)
lines.push(` .setViewportSize(${options2.viewport.width}, ${options2.viewport.height})`);
return lines.join("\n");
}
function formatClickOptions(options2) {
const lines = [];
if (options2.button)
lines.push(` .setButton(MouseButton.${options2.button.toUpperCase()})`);
if (options2.modifiers)
lines.push(` .setModifiers(Arrays.asList(${options2.modifiers.map((m) => `KeyboardModifier.${m.toUpperCase()}`).join(", ")}))`);
if (options2.clickCount)
lines.push(` .setClickCount(${options2.clickCount})`);
if (options2.position)
lines.push(` .setPosition(${options2.position.x}, ${options2.position.y})`);
if (!lines.length)
return "";
lines.unshift(`new Locator.ClickOptions()`);
return lines.join("\n");
}
function quote3(text2) {
return escapeWithQuotes(text2, '"');
}
var JavaLanguageGenerator;
var init_java = __esm({
"packages/playwright-core/src/server/codegen/java.ts"() {
"use strict";
init_locatorGenerators();
init_stringUtils();
init_language();
init_deviceDescriptors();
init_javascript2();
JavaLanguageGenerator = class {
constructor(mode) {
this.groupName = "Java";
this.highlighter = "java";
if (mode === "library") {
this.name = "Library";
this.id = "java";
} else if (mode === "junit") {
this.name = "JUnit";
this.id = "java-junit";
} else {
throw new Error(`Unknown Java language mode: ${mode}`);
}
this._mode = mode;
}
generateAction(actionInContext) {
const action = actionInContext.action;
const pageAlias = actionInContext.frame.pageAlias;
const offset = this._mode === "junit" ? 4 : 6;
const formatter = new JavaScriptFormatter(offset);
if (this._mode !== "library" && (action.name === "openPage" || action.name === "closePage"))
return "";
if (action.name === "openPage") {
formatter.add(`Page ${pageAlias} = context.newPage();`);
if (action.url && action.url !== "about:blank" && action.url !== "chrome://newtab/")
formatter.add(`${pageAlias}.navigate(${quote3(action.url)});`);
return formatter.format();
}
const locators = actionInContext.frame.framePath.map((selector) => `.${this._asLocator(selector, false)}.contentFrame()`);
const subject = `${pageAlias}${locators.join("")}`;
const signals = toSignalMap(action);
if (signals.dialog) {
formatter.add(` ${pageAlias}.onceDialog(dialog -> {
System.out.println(String.format("Dialog message: %s", dialog.message()));
dialog.dismiss();
});`);
}
let code = this._generateActionCall(subject, actionInContext, !!actionInContext.frame.framePath.length);
if (signals.popup) {
code = `Page ${signals.popup.popupAlias} = ${pageAlias}.waitForPopup(() -> {
${code}
});`;
}
if (signals.download) {
code = `Download download${signals.download.downloadAlias} = ${pageAlias}.waitForDownload(() -> {
${code}
});`;
}
formatter.add(code);
return formatter.format();
}
_generateActionCall(subject, actionInContext, inFrameLocator) {
const action = actionInContext.action;
switch (action.name) {
case "openPage":
throw Error("Not reached");
case "closePage":
return `${subject}.close();`;
case "click": {
let method = "click";
if (action.clickCount === 2)
method = "dblclick";
const options2 = toClickOptionsForSourceCode(action);
const optionsText = formatClickOptions(options2);
return `${subject}.${this._asLocator(action.selector, inFrameLocator)}.${method}(${optionsText});`;
}
case "hover": {
const optionsText = action.position ? `new Locator.HoverOptions().setPosition(${action.position.x}, ${action.position.y})` : "";
return `${subject}.${this._asLocator(action.selector, inFrameLocator)}.hover(${optionsText});`;
}
case "check":
return `${subject}.${this._asLocator(action.selector, inFrameLocator)}.check();`;
case "uncheck":
return `${subject}.${this._asLocator(action.selector, inFrameLocator)}.uncheck();`;
case "fill":
return `${subject}.${this._asLocator(action.selector, inFrameLocator)}.fill(${quote3(action.text)});`;
case "setInputFiles":
return `${subject}.${this._asLocator(action.selector, inFrameLocator)}.setInputFiles(${formatPath(action.files.length === 1 ? action.files[0] : action.files)});`;
case "press": {
const modifiers = toKeyboardModifiers(action.modifiers);
const shortcut = [...modifiers, action.key].join("+");
return `${subject}.${this._asLocator(action.selector, inFrameLocator)}.press(${quote3(shortcut)});`;
}
case "navigate":
return `${subject}.navigate(${quote3(action.url)});`;
case "select":
return `${subject}.${this._asLocator(action.selector, inFrameLocator)}.selectOption(${formatSelectOption(action.options.length === 1 ? action.options[0] : action.options)});`;
case "assertText":
return `assertThat(${subject}.${this._asLocator(action.selector, inFrameLocator)}).${action.substring ? "containsText" : "hasText"}(${quote3(action.text)});`;
case "assertChecked":
return `assertThat(${subject}.${this._asLocator(action.selector, inFrameLocator)})${action.checked ? "" : ".not()"}.isChecked();`;
case "assertVisible":
return `assertThat(${subject}.${this._asLocator(action.selector, inFrameLocator)}).isVisible();`;
case "assertValue": {
const assertion = action.value ? `hasValue(${quote3(action.value)})` : `isEmpty()`;
return `assertThat(${subject}.${this._asLocator(action.selector, inFrameLocator)}).${assertion};`;
}
case "assertSnapshot":
return `assertThat(${subject}.${this._asLocator(action.selector, inFrameLocator)}).matchesAriaSnapshot(${quote3(action.ariaSnapshot)});`;
}
}
_asLocator(selector, inFrameLocator) {
return asLocator("java", selector, inFrameLocator);
}
generateHeader(options2) {
const formatter = new JavaScriptFormatter();
if (this._mode === "junit") {
formatter.add(`
import com.microsoft.playwright.junit.UsePlaywright;
import com.microsoft.playwright.Page;
import com.microsoft.playwright.options.*;
${options2.contextOptions.recordHar ? `import java.nio.file.Paths;
` : ""}import org.junit.jupiter.api.*;
import static com.microsoft.playwright.assertions.PlaywrightAssertions.*;
@UsePlaywright
public class TestExample {
@Test
void test(Page page) {`);
if (options2.contextOptions.recordHar) {
const url2 = options2.contextOptions.recordHar.urlFilter;
const recordHarOptions = typeof url2 === "string" ? `, new Page.RouteFromHAROptions()
.setUrl(${quote3(url2)})` : "";
formatter.add(` page.routeFromHAR(Paths.get(${quote3(options2.contextOptions.recordHar.path)})${recordHarOptions});`);
}
return formatter.format();
}
formatter.add(`
import com.microsoft.playwright.*;
import com.microsoft.playwright.options.*;
import static com.microsoft.playwright.assertions.PlaywrightAssertions.assertThat;
${options2.contextOptions.recordHar ? `import java.nio.file.Paths;
` : ""}import java.util.*;
public class Example {
public static void main(String[] args) {
try (Playwright playwright = Playwright.create()) {
Browser browser = playwright.${options2.browserName}().launch(${formatLaunchOptions(options2.launchOptions)});
BrowserContext context = browser.newContext(${formatContextOptions3(options2.contextOptions, options2.deviceName)});`);
if (options2.contextOptions.recordHar) {
const url2 = options2.contextOptions.recordHar.urlFilter;
const recordHarOptions = typeof url2 === "string" ? `, new BrowserContext.RouteFromHAROptions()
.setUrl(${quote3(url2)})` : "";
formatter.add(` context.routeFromHAR(Paths.get(${quote3(options2.contextOptions.recordHar.path)})${recordHarOptions});`);
}
return formatter.format();
}
generateFooter(saveStorage) {
const storageStateLine = saveStorage ? `
context.storageState(new BrowserContext.StorageStateOptions().setPath(${quote3(saveStorage)}));
` : "";
if (this._mode === "junit") {
return `${storageStateLine} }
}`;
}
return `${storageStateLine} }
}
}`;
}
};
}
});
// packages/playwright-core/src/server/codegen/jsonl.ts
var JsonlLanguageGenerator;
var init_jsonl = __esm({
"packages/playwright-core/src/server/codegen/jsonl.ts"() {
"use strict";
init_locatorGenerators();
JsonlLanguageGenerator = class {
constructor() {
this.id = "jsonl";
this.groupName = "";
this.name = "JSONL";
this.highlighter = "javascript";
}
generateAction(actionInContext) {
const locator2 = actionInContext.action.selector ? JSON.parse(asLocator("jsonl", actionInContext.action.selector)) : void 0;
const entry = {
...actionInContext.action,
...actionInContext.frame,
locator: locator2,
ariaSnapshot: void 0
};
return JSON.stringify(entry);
}
generateHeader(options2) {
return JSON.stringify(options2);
}
generateFooter(saveStorage) {
return "";
}
};
}
});
// packages/playwright-core/src/server/codegen/python.ts
function formatValue(value2) {
if (value2 === false)
return "False";
if (value2 === true)
return "True";
if (value2 === void 0)
return "None";
if (Array.isArray(value2))
return `[${value2.map(formatValue).join(", ")}]`;
if (typeof value2 === "string")
return quote4(value2);
if (typeof value2 === "object")
return JSON.stringify(value2);
return String(value2);
}
function formatOptions2(value2, hasArguments, asDict) {
const keys = Object.keys(value2).filter((key) => value2[key] !== void 0).sort();
if (!keys.length)
return "";
return (hasArguments ? ", " : "") + keys.map((key) => {
if (asDict)
return `"${toSnakeCase(key)}": ${formatValue(value2[key])}`;
return `${toSnakeCase(key)}=${formatValue(value2[key])}`;
}).join(", ");
}
function formatContextOptions4(options2, deviceName, asDict) {
options2 = { ...options2, recordHar: void 0 };
const device = deviceName && deviceDescriptors[deviceName];
if (!device)
return formatOptions2(options2, false, asDict);
return `**playwright.devices[${quote4(deviceName)}]` + formatOptions2(sanitizeDeviceOptions(device, options2), true, asDict);
}
function quote4(text2) {
return escapeWithQuotes(text2, '"');
}
var PythonLanguageGenerator, PythonFormatter;
var init_python = __esm({
"packages/playwright-core/src/server/codegen/python.ts"() {
"use strict";
init_locatorGenerators();
init_stringUtils();
init_language();
init_deviceDescriptors();
PythonLanguageGenerator = class {
constructor(isAsync, isPyTest) {
this.groupName = "Python";
this.highlighter = "python";
this.id = isPyTest ? "python-pytest" : isAsync ? "python-async" : "python";
this.name = isPyTest ? "Pytest" : isAsync ? "Library Async" : "Library";
this._isAsync = isAsync;
this._isPyTest = isPyTest;
this._awaitPrefix = isAsync ? "await " : "";
this._asyncPrefix = isAsync ? "async " : "";
}
generateAction(actionInContext) {
const action = actionInContext.action;
if (this._isPyTest && (action.name === "openPage" || action.name === "closePage"))
return "";
const pageAlias = actionInContext.frame.pageAlias;
const formatter = new PythonFormatter(4);
if (action.name === "openPage") {
formatter.add(`${pageAlias} = ${this._awaitPrefix}context.new_page()`);
if (action.url && action.url !== "about:blank" && action.url !== "chrome://newtab/")
formatter.add(`${this._awaitPrefix}${pageAlias}.goto(${quote4(action.url)})`);
return formatter.format();
}
const locators = actionInContext.frame.framePath.map((selector) => `.${this._asLocator(selector)}.content_frame`);
const subject = `${pageAlias}${locators.join("")}`;
const signals = toSignalMap(action);
if (signals.dialog)
formatter.add(` ${pageAlias}.once("dialog", lambda dialog: dialog.dismiss())`);
let code = `${this._awaitPrefix}${this._generateActionCall(subject, actionInContext)}`;
if (signals.popup) {
code = `${this._asyncPrefix}with ${pageAlias}.expect_popup() as ${signals.popup.popupAlias}_info {
${code}
}
${signals.popup.popupAlias} = ${this._awaitPrefix}${signals.popup.popupAlias}_info.value`;
}
if (signals.download) {
code = `${this._asyncPrefix}with ${pageAlias}.expect_download() as download${signals.download.downloadAlias}_info {
${code}
}
download${signals.download.downloadAlias} = ${this._awaitPrefix}download${signals.download.downloadAlias}_info.value`;
}
formatter.add(code);
return formatter.format();
}
_generateActionCall(subject, actionInContext) {
const action = actionInContext.action;
switch (action.name) {
case "openPage":
throw Error("Not reached");
case "closePage":
return `${subject}.close()`;
case "click": {
let method = "click";
if (action.clickCount === 2)
method = "dblclick";
const options2 = toClickOptionsForSourceCode(action);
const optionsString = formatOptions2(options2, false);
return `${subject}.${this._asLocator(action.selector)}.${method}(${optionsString})`;
}
case "hover":
return `${subject}.${this._asLocator(action.selector)}.hover(${formatOptions2({ position: action.position }, false)})`;
case "check":
return `${subject}.${this._asLocator(action.selector)}.check()`;
case "uncheck":
return `${subject}.${this._asLocator(action.selector)}.uncheck()`;
case "fill":
return `${subject}.${this._asLocator(action.selector)}.fill(${quote4(action.text)})`;
case "setInputFiles":
return `${subject}.${this._asLocator(action.selector)}.set_input_files(${formatValue(action.files.length === 1 ? action.files[0] : action.files)})`;
case "press": {
const modifiers = toKeyboardModifiers(action.modifiers);
const shortcut = [...modifiers, action.key].join("+");
return `${subject}.${this._asLocator(action.selector)}.press(${quote4(shortcut)})`;
}
case "navigate":
return `${subject}.goto(${quote4(action.url)})`;
case "select":
return `${subject}.${this._asLocator(action.selector)}.select_option(${formatValue(action.options.length === 1 ? action.options[0] : action.options)})`;
case "assertText":
return `expect(${subject}.${this._asLocator(action.selector)}).${action.substring ? "to_contain_text" : "to_have_text"}(${quote4(action.text)})`;
case "assertChecked":
return `expect(${subject}.${this._asLocator(action.selector)}).${action.checked ? "to_be_checked()" : "not_to_be_checked()"}`;
case "assertVisible":
return `expect(${subject}.${this._asLocator(action.selector)}).to_be_visible()`;
case "assertValue": {
const assertion = action.value ? `to_have_value(${quote4(action.value)})` : `to_be_empty()`;
return `expect(${subject}.${this._asLocator(action.selector)}).${assertion};`;
}
case "assertSnapshot":
return `expect(${subject}.${this._asLocator(action.selector)}).to_match_aria_snapshot(${quote4(action.ariaSnapshot)})`;
}
}
_asLocator(selector) {
return asLocator("python", selector);
}
generateHeader(options2) {
const formatter = new PythonFormatter();
const recordHar = options2.contextOptions.recordHar;
if (this._isPyTest) {
const contextOptions = formatContextOptions4(
options2.contextOptions,
options2.deviceName,
true
/* asDict */
);
const fixture = contextOptions ? `
@pytest.fixture(scope="session")
def browser_context_args(browser_context_args, playwright) {
return {${contextOptions}}
}
` : "";
formatter.add(`${options2.deviceName || contextOptions ? "import pytest\n" : ""}import re
from playwright.sync_api import Page, expect
${fixture}
def test_example(page: Page) -> None {`);
if (recordHar)
formatter.add(` page.route_from_har(${quote4(recordHar.path)}${typeof recordHar.urlFilter === "string" ? `, url=${quote4(recordHar.urlFilter)}` : ""})`);
} else if (this._isAsync) {
formatter.add(`
import asyncio
import re
from playwright.async_api import Playwright, async_playwright, expect
async def run(playwright: Playwright) -> None {
browser = await playwright.${options2.browserName}.launch(${formatOptions2(options2.launchOptions, false)})
context = await browser.new_context(${formatContextOptions4(options2.contextOptions, options2.deviceName)})`);
if (recordHar)
formatter.add(` await context.route_from_har(${quote4(recordHar.path)}${typeof recordHar.urlFilter === "string" ? `, url=${quote4(recordHar.urlFilter)}` : ""})`);
} else {
formatter.add(`
import re
from playwright.sync_api import Playwright, sync_playwright, expect
def run(playwright: Playwright) -> None {
browser = playwright.${options2.browserName}.launch(${formatOptions2(options2.launchOptions, false)})
context = browser.new_context(${formatContextOptions4(options2.contextOptions, options2.deviceName)})`);
if (recordHar)
formatter.add(` context.route_from_har(${quote4(recordHar.path)}${typeof recordHar.urlFilter === "string" ? `, url=${quote4(recordHar.urlFilter)}` : ""})`);
}
return formatter.format();
}
generateFooter(saveStorage) {
if (this._isPyTest) {
return "";
} else if (this._isAsync) {
const storageStateLine = saveStorage ? `
await context.storage_state(path=${quote4(saveStorage)})` : "";
return `
# ---------------------${storageStateLine}
await context.close()
await browser.close()
async def main() -> None:
async with async_playwright() as playwright:
await run(playwright)
asyncio.run(main())
`;
} else {
const storageStateLine = saveStorage ? `
context.storage_state(path=${quote4(saveStorage)})` : "";
return `
# ---------------------${storageStateLine}
context.close()
browser.close()
with sync_playwright() as playwright:
run(playwright)
`;
}
}
};
PythonFormatter = class {
constructor(offset = 0) {
this._lines = [];
this._baseIndent = " ".repeat(4);
this._baseOffset = " ".repeat(offset);
}
prepend(text2) {
this._lines = text2.trim().split("\n").map((line) => line.trim()).concat(this._lines);
}
add(text2) {
this._lines.push(...text2.trim().split("\n").map((line) => line.trim()));
}
newLine() {
this._lines.push("");
}
format() {
let spaces = "";
const lines = [];
this._lines.forEach((line) => {
if (line === "")
return lines.push(line);
if (line === "}") {
spaces = spaces.substring(this._baseIndent.length);
return;
}
line = spaces + line;
if (line.endsWith("{")) {
spaces += this._baseIndent;
line = line.substring(0, line.length - 1).trimEnd() + ":";
}
return lines.push(this._baseOffset + line);
});
return lines.join("\n");
}
};
}
});
// packages/playwright-core/src/server/codegen/languages.ts
function languageSet() {
return /* @__PURE__ */ new Set([
new JavaScriptLanguageGenerator(
/* isPlaywrightTest */
true
),
new JavaScriptLanguageGenerator(
/* isPlaywrightTest */
false
),
new PythonLanguageGenerator(
/* isAsync */
false,
/* isPytest */
true
),
new PythonLanguageGenerator(
/* isAsync */
false,
/* isPytest */
false
),
new PythonLanguageGenerator(
/* isAsync */
true,
/* isPytest */
false
),
new CSharpLanguageGenerator("mstest"),
new CSharpLanguageGenerator("nunit"),
new CSharpLanguageGenerator("xunit"),
new CSharpLanguageGenerator("library"),
new JavaLanguageGenerator("junit"),
new JavaLanguageGenerator("library"),
new JsonlLanguageGenerator()
]);
}
var init_languages = __esm({
"packages/playwright-core/src/server/codegen/languages.ts"() {
"use strict";
init_csharp();
init_java();
init_javascript2();
init_jsonl();
init_python();
}
});
// packages/playwright-core/src/server/recorder/recorderUtils.ts
function buildFullSelector(framePath, selector) {
return [...framePath, selector].join(" >> internal:control=enter-frame >> ");
}
function metadataToCallLog(metadata, status) {
const title = renderTitleForCall(metadata);
if (metadata.error)
status = "error";
const params2 = {
url: metadata.params?.url,
selector: metadata.params?.selector
};
let duration = metadata.endTime ? metadata.endTime - metadata.startTime : void 0;
if (typeof duration === "number" && metadata.pauseStartTime && metadata.pauseEndTime) {
duration -= metadata.pauseEndTime - metadata.pauseStartTime;
duration = Math.max(duration, 0);
}
const callLog = {
id: metadata.id,
messages: metadata.log,
title: title ?? "",
status,
error: metadata.error?.error?.message,
params: params2,
duration
};
return callLog;
}
function mainFrameForAction(pageAliases, actionInContext) {
const pageAlias = actionInContext.frame.pageAlias;
const page = [...pageAliases.entries()].find(([, alias]) => pageAlias === alias)?.[0];
if (!page)
throw new Error(`Internal error: page ${pageAlias} not found in [${[...pageAliases.values()]}]`);
return page.mainFrame();
}
function isSameAction(a, b) {
return a.action.name === b.action.name && a.frame.pageAlias === b.frame.pageAlias && a.frame.framePath.join("|") === b.frame.framePath.join("|");
}
function isSameSelector(action, lastAction) {
return "selector" in action.action && "selector" in lastAction.action && action.action.selector === lastAction.action.selector;
}
function isShortlyAfter(action, lastAction) {
return action.startTime - lastAction.startTime < 500;
}
function shouldMergeAction(action, lastAction) {
if (!lastAction)
return false;
switch (action.action.name) {
case "fill":
return isSameAction(action, lastAction) && isSameSelector(action, lastAction);
case "navigate":
return isSameAction(action, lastAction);
case "click":
return isSameAction(action, lastAction) && isSameSelector(action, lastAction) && isShortlyAfter(action, lastAction) && action.action.clickCount > lastAction.action.clickCount;
}
return false;
}
function collapseActions(actions) {
const result2 = [];
for (const action of actions) {
const lastAction = result2[result2.length - 1];
const shouldMerge = shouldMergeAction(action, lastAction);
if (!shouldMerge) {
result2.push(action);
continue;
}
const startTime = result2[result2.length - 1].startTime;
result2[result2.length - 1] = action;
result2[result2.length - 1].startTime = startTime;
}
return result2;
}
async function generateFrameSelector(progress2, frame) {
const selectorPromises = [];
progress2.setAllowConcurrentOrNestedRaces(true);
while (frame) {
const parent = frame.parentFrame();
if (!parent)
break;
selectorPromises.push(generateFrameSelectorInParent(progress2, parent, frame));
frame = parent;
}
const result2 = await Promise.all(selectorPromises);
progress2.setAllowConcurrentOrNestedRaces(false);
return result2.reverse();
}
async function generateFrameSelectorInParent(prgoress, parent, frame) {
const result2 = await raceAgainstDeadline(async () => {
try {
const frameElement = await frame.frameElement(prgoress);
if (!frameElement || !parent)
return;
const utility = await parent.utilityContext();
const injected = await utility.injectedScript();
const selector = await injected.evaluate((injected2, element2) => {
return injected2.generateSelectorSimple(element2);
}, frameElement);
return selector;
} catch (e) {
}
}, monotonicTime() + 2e3);
if (!result2.timedOut && result2.result)
return result2.result;
if (frame.name())
return `iframe[name=${quoteCSSAttributeValue(frame.name())}]`;
return `iframe[src=${quoteCSSAttributeValue(frame.url())}]`;
}
var init_recorderUtils = __esm({
"packages/playwright-core/src/server/recorder/recorderUtils.ts"() {
"use strict";
init_protocolFormatter();
init_timeoutRunner();
init_time();
init_stringUtils();
}
});
// packages/playwright-core/src/server/recorder/recorderSignalProcessor.ts
var RecorderSignalProcessor;
var init_recorderSignalProcessor = __esm({
"packages/playwright-core/src/server/recorder/recorderSignalProcessor.ts"() {
"use strict";
init_time();
init_debug();
init_recorderUtils();
init_progress();
RecorderSignalProcessor = class {
constructor(actionSink) {
this._lastAction = null;
this._delegate = actionSink;
}
addAction(actionInContext) {
this._lastAction = actionInContext;
this._delegate.addAction(actionInContext);
}
signal(pageAlias, frame, signal) {
const timestamp = monotonicTime();
if (signal.name === "navigation" && frame._page.mainFrame() === frame) {
const lastAction = this._lastAction;
const signalThreshold = isUnderTest() ? 500 : 5e3;
let generateGoto = false;
if (!lastAction)
generateGoto = true;
else if (lastAction.action.name !== "click" && lastAction.action.name !== "press" && lastAction.action.name !== "fill")
generateGoto = true;
else if (timestamp - lastAction.startTime > signalThreshold)
generateGoto = true;
if (generateGoto) {
this.addAction({
frame: {
pageGuid: frame._page.guid,
pageAlias,
framePath: []
},
action: {
name: "navigate",
url: frame.url(),
signals: []
},
startTime: timestamp,
endTime: timestamp
});
}
return;
}
generateFrameSelector(nullProgress, frame).then((framePath) => {
const signalInContext = {
frame: {
pageGuid: frame._page.guid,
pageAlias,
framePath
},
signal,
timestamp
};
this._delegate.addSignal(signalInContext);
});
}
};
}
});
// packages/playwright-core/src/generated/pollingRecorderSource.ts
var source5;
var init_pollingRecorderSource = __esm({
"packages/playwright-core/src/generated/pollingRecorderSource.ts"() {
"use strict";
source5 = '\nvar __commonJS = obj => {\n let required = false;\n let result;\n return function __require() {\n if (!required) {\n required = true;\n let fn;\n for (const name in obj) { fn = obj[name]; break; }\n const module = { exports: {} };\n fn(module.exports, module);\n result = module.exports;\n }\n return result;\n }\n};\nvar __export = (target, all) => {for (var name in all) target[name] = all[name];};\nvar __toESM = mod => ({ ...mod, \'default\': mod });\nvar __toCommonJS = mod => ({ ...mod, __esModule: true });\n\n\n// packages/injected/src/recorder/pollingRecorder.ts\nvar pollingRecorder_exports = {};\n__export(pollingRecorder_exports, {\n PollingRecorder: () => PollingRecorder,\n default: () => pollingRecorder_default\n});\nmodule.exports = __toCommonJS(pollingRecorder_exports);\n\n// packages/injected/src/recorder/clipPaths.ts\nvar svgJson = { "tagName": "svg", "children": [{ "tagName": "defs", "children": [{ "tagName": "clipPath", "attrs": { "width": "16", "height": "16", "viewBox": "0 0 16 16", "fill": "currentColor", "id": "icon-gripper" }, "children": [{ "tagName": "path", "attrs": { "d": "M5 3h2v2H5zm0 4h2v2H5zm0 4h2v2H5zm4-8h2v2H9zm0 4h2v2H9zm0 4h2v2H9z" } }] }, { "tagName": "clipPath", "attrs": { "width": "16", "height": "16", "viewBox": "0 0 16 16", "fill": "currentColor", "id": "icon-circle-large-filled" }, "children": [{ "tagName": "path", "attrs": { "d": "M8 1a6.8 6.8 0 0 1 1.86.253 6.899 6.899 0 0 1 3.083 1.805 6.903 6.903 0 0 1 1.804 3.083C14.916 6.738 15 7.357 15 8s-.084 1.262-.253 1.86a6.9 6.9 0 0 1-.704 1.674 7.157 7.157 0 0 1-2.516 2.509 6.966 6.966 0 0 1-1.668.71A6.984 6.984 0 0 1 8 15a6.984 6.984 0 0 1-1.86-.246 7.098 7.098 0 0 1-1.674-.711 7.3 7.3 0 0 1-1.415-1.094 7.295 7.295 0 0 1-1.094-1.415 7.098 7.098 0 0 1-.71-1.675A6.985 6.985 0 0 1 1 8c0-.643.082-1.262.246-1.86a6.968 6.968 0 0 1 .711-1.667 7.156 7.156 0 0 1 2.509-2.516 6.895 6.895 0 0 1 1.675-.704A6.808 6.808 0 0 1 8 1z" } }] }, { "tagName": "clipPath", "attrs": { "width": "16", "height": "16", "viewBox": "0 0 16 16", "fill": "currentColor", "id": "icon-stop-circle" }, "children": [{ "tagName": "path", "attrs": { "d": "M6 6h4v4H6z" } }, { "tagName": "path", "attrs": { "fill-rule": "evenodd", "clip-rule": "evenodd", "d": "M8.6 1c1.6.1 3.1.9 4.2 2 1.3 1.4 2 3.1 2 5.1 0 1.6-.6 3.1-1.6 4.4-1 1.2-2.4 2.1-4 2.4-1.6.3-3.2.1-4.6-.7-1.4-.8-2.5-2-3.1-3.5C.9 9.2.8 7.5 1.3 6c.5-1.6 1.4-2.9 2.8-3.8C5.4 1.3 7 .9 8.6 1zm.5 12.9c1.3-.3 2.5-1 3.4-2.1.8-1.1 1.3-2.4 1.2-3.8 0-1.6-.6-3.2-1.7-4.3-1-1-2.2-1.6-3.6-1.7-1.3-.1-2.7.2-3.8 1-1.1.8-1.9 1.9-2.3 3.3-.4 1.3-.4 2.7.2 4 .6 1.3 1.5 2.3 2.7 3 1.2.7 2.6.9 3.9.6z" } }] }, { "tagName": "clipPath", "attrs": { "width": "16", "height": "16", "viewBox": "0 0 16 16", "fill": "currentColor", "id": "icon-inspect" }, "children": [{ "tagName": "path", "attrs": { "fill-rule": "evenodd", "clip-rule": "evenodd", "d": "M1 3l1-1h12l1 1v6h-1V3H2v8h5v1H2l-1-1V3zm14.707 9.707L9 6v9.414l2.707-2.707h4zM10 13V8.414l3.293 3.293h-2L10 13z" } }] }, { "tagName": "clipPath", "attrs": { "width": "16", "height": "16", "viewBox": "0 0 16 16", "fill": "currentColor", "id": "icon-whole-word" }, "children": [{ "tagName": "path", "attrs": { "fill-rule": "evenodd", "clip-rule": "evenodd", "d": "M0 11H1V13H15V11H16V14H15H1H0V11Z" } }, { "tagName": "path", "attrs": { "d": "M6.84048 11H5.95963V10.1406H5.93814C5.555 10.7995 4.99104 11.1289 4.24625 11.1289C3.69839 11.1289 3.26871 10.9839 2.95718 10.6938C2.64924 10.4038 2.49527 10.0189 2.49527 9.53906C2.49527 8.51139 3.10041 7.91341 4.3107 7.74512L5.95963 7.51416C5.95963 6.57959 5.58186 6.1123 4.82632 6.1123C4.16389 6.1123 3.56591 6.33789 3.03238 6.78906V5.88672C3.57307 5.54297 4.19612 5.37109 4.90152 5.37109C6.19416 5.37109 6.84048 6.05501 6.84048 7.42285V11ZM5.95963 8.21777L4.63297 8.40039C4.22476 8.45768 3.91682 8.55973 3.70914 8.70654C3.50145 8.84977 3.39761 9.10579 3.39761 9.47461C3.39761 9.74316 3.4925 9.96338 3.68228 10.1353C3.87564 10.3035 4.13166 10.3877 4.45035 10.3877C4.8872 10.3877 5.24706 10.2355 5.52994 9.93115C5.8164 9.62321 5.95963 9.2347 5.95963 8.76562V8.21777Z" } }, { "tagName": "path", "attrs": { "d": "M9.3475 10.2051H9.32601V11H8.44515V2.85742H9.32601V6.4668H9.3475C9.78076 5.73633 10.4146 5.37109 11.2489 5.37109C11.9543 5.37109 12.5057 5.61816 12.9032 6.1123C13.3042 6.60286 13.5047 7.26172 13.5047 8.08887C13.5047 9.00911 13.2809 9.74674 12.8333 10.3018C12.3857 10.8532 11.7734 11.1289 10.9964 11.1289C10.2695 11.1289 9.71989 10.821 9.3475 10.2051ZM9.32601 7.98682V8.75488C9.32601 9.20964 9.47282 9.59635 9.76644 9.91504C10.0636 10.2301 10.4396 10.3877 10.8944 10.3877C11.4279 10.3877 11.8451 10.1836 12.1458 9.77539C12.4502 9.36719 12.6024 8.79964 12.6024 8.07275C12.6024 7.46045 12.4609 6.98063 12.1781 6.6333C11.8952 6.28597 11.512 6.1123 11.0286 6.1123C10.5166 6.1123 10.1048 6.29134 9.7933 6.64941C9.48177 7.00391 9.32601 7.44971 9.32601 7.98682Z" } }] }, { "tagName": "clipPath", "attrs": { "width": "16", "height": "16", "viewBox": "0 0 16 16", "fill": "currentColor", "id": "icon-eye" }, "children": [{ "tagName": "path", "attrs": { "d": "M7.99993 6.00316C9.47266 6.00316 10.6666 7.19708 10.6666 8.66981C10.6666 10.1426 9.47266 11.3365 7.99993 11.3365C6.52715 11.3365 5.33324 10.1426 5.33324 8.66981C5.33324 7.19708 6.52715 6.00316 7.99993 6.00316ZM7.99993 7.00315C7.07946 7.00315 6.33324 7.74935 6.33324 8.66981C6.33324 9.59028 7.07946 10.3365 7.99993 10.3365C8.9204 10.3365 9.6666 9.59028 9.6666 8.66981C9.6666 7.74935 8.9204 7.00315 7.99993 7.00315ZM7.99993 3.66675C11.0756 3.66675 13.7307 5.76675 14.4673 8.70968C14.5344 8.97755 14.3716 9.24908 14.1037 9.31615C13.8358 9.38315 13.5643 9.22041 13.4973 8.95248C12.8713 6.45205 10.6141 4.66675 7.99993 4.66675C5.38454 4.66675 3.12664 6.45359 2.50182 8.95555C2.43491 9.22341 2.16348 9.38635 1.89557 9.31948C1.62766 9.25255 1.46471 8.98115 1.53162 8.71321C2.26701 5.76856 4.9229 3.66675 7.99993 3.66675Z" } }] }, { "tagName": "clipPath", "attrs": { "width": "16", "height": "16", "viewBox": "0 0 16 16", "fill": "currentColor", "id": "icon-symbol-constant" }, "children": [{ "tagName": "path", "attrs": { "fill-rule": "evenodd", "clip-rule": "evenodd", "d": "M4 6h8v1H4V6zm8 3H4v1h8V9z" } }, { "tagName": "path", "attrs": { "fill-rule": "evenodd", "clip-rule": "evenodd", "d": "M1 4l1-1h12l1 1v8l-1 1H2l-1-1V4zm1 0v8h12V4H2z" } }] }, { "tagName": "clipPath", "attrs": { "width": "16", "height": "16", "viewBox": "0 0 16 16", "fill": "currentColor", "id": "icon-check" }, "children": [{ "tagName": "path", "attrs": { "fill-rule": "evenodd", "clip-rule": "evenodd", "d": "M14.431 3.323l-8.47 10-.79-.036-3.35-4.77.818-.574 2.978 4.24 8.051-9.506.764.646z" } }] }, { "tagName": "clipPath", "attrs": { "width": "16", "height": "16", "viewBox": "0 0 16 16", "fill": "currentColor", "id": "icon-close" }, "children": [{ "tagName": "path", "attrs": { "fill-rule": "evenodd", "clip-rule": "evenodd", "d": "M8 8.707l3.646 3.647.708-.707L8.707 8l3.647-3.646-.707-.708L8 7.293 4.354 3.646l-.707.708L7.293 8l-3.646 3.646.707.708L8 8.707z" } }] }, { "tagName": "clipPath", "attrs": { "width": "16", "height": "16", "viewBox": "0 0 16 16", "fill": "currentColor", "id": "icon-pass" }, "children": [{ "tagName": "path", "attrs": { "d": "M6.27 10.87h.71l4.56-4.56-.71-.71-4.2 4.21-1.92-1.92L4 8.6l2.27 2.27z" } }, { "tagName": "path", "attrs": { "fill-rule": "evenodd", "clip-rule": "evenodd", "d": "M8.6 1c1.6.1 3.1.9 4.2 2 1.3 1.4 2 3.1 2 5.1 0 1.6-.6 3.1-1.6 4.4-1 1.2-2.4 2.1-4 2.4-1.6.3-3.2.1-4.6-.7-1.4-.8-2.5-2-3.1-3.5C.9 9.2.8 7.5 1.3 6c.5-1.6 1.4-2.9 2.8-3.8C5.4 1.3 7 .9 8.6 1zm.5 12.9c1.3-.3 2.5-1 3.4-2.1.8-1.1 1.3-2.4 1.2-3.8 0-1.6-.6-3.2-1.7-4.3-1-1-2.2-1.6-3.6-1.7-1.3-.1-2.7.2-3.8 1-1.1.8-1.9 1.9-2.3 3.3-.4 1.3-.4 2.7.2 4 .6 1.3 1.5 2.3 2.7 3 1.2.7 2.6.9 3.9.6z" } }] }, { "tagName": "clipPath", "attrs": { "width": "16", "height": "16", "viewBox": "0 0 16 16", "fill": "currentColor", "id": "icon-gist" }, "children": [{ "tagName": "path", "attrs": { "fill-rule": "evenodd", "clip-rule": "evenodd", "d": "M10.57 1.14l3.28 3.3.15.36v9.7l-.5.5h-11l-.5-.5v-13l.5-.5h7.72l.35.14zM10 5h3l-3-3v3zM3 2v12h10V6H9.5L9 5.5V2H3zm2.062 7.533l1.817-1.828L6.17 7 4 9.179v.707l2.171 2.174.707-.707-1.816-1.82zM8.8 7.714l.7-.709 2.189 2.175v.709L9.5 12.062l-.705-.709 1.831-1.82L8.8 7.714z" } }] }] }] };\nvar clipPaths_default = svgJson;\n\n// packages/injected/src/recorder/recorder.ts\nvar HighlightColors = {\n multiple: "#f6b26b7f",\n single: "#6fa8dc7f",\n assert: "#8acae480",\n action: "#dc6f6f7f"\n};\nvar NoneTool = class {\n};\nvar InspectTool = class {\n constructor(recorder, assertVisibility) {\n this._hoveredModel = null;\n this._hoveredElement = null;\n this._recorder = recorder;\n this._assertVisibility = assertVisibility;\n }\n cursor() {\n return "pointer";\n }\n uninstall() {\n this._hoveredModel = null;\n this._hoveredElement = null;\n }\n onClick(event) {\n var _a;\n consumeEvent(event);\n if (event.button !== 0)\n return;\n if ((_a = this._hoveredModel) == null ? void 0 : _a.selector)\n this._commit(this._hoveredModel.selector, this._hoveredModel);\n }\n onPointerDown(event) {\n consumeEvent(event);\n }\n onPointerUp(event) {\n consumeEvent(event);\n }\n onMouseDown(event) {\n consumeEvent(event);\n }\n onMouseUp(event) {\n consumeEvent(event);\n }\n onMouseMove(event) {\n var _a;\n consumeEvent(event);\n let target = this._recorder.deepEventTarget(event);\n if (!target.isConnected)\n target = null;\n if (this._hoveredElement === target)\n return;\n this._hoveredElement = target;\n let model = null;\n if (this._hoveredElement) {\n const generated = this._recorder.injectedScript.generateSelector(this._hoveredElement, { testIdAttributeName: this._recorder.state.testIdAttributeName, multiple: false });\n model = {\n selector: generated.selector,\n elements: generated.elements,\n tooltipText: this._recorder.injectedScript.utils.asLocator(this._recorder.state.language, generated.selector),\n color: this._assertVisibility ? HighlightColors.assert : HighlightColors.single\n };\n }\n if (((_a = this._hoveredModel) == null ? void 0 : _a.selector) === (model == null ? void 0 : model.selector))\n return;\n this._hoveredModel = model;\n this._recorder.updateHighlight(model, true);\n }\n onMouseEnter(event) {\n consumeEvent(event);\n }\n onMouseLeave(event) {\n consumeEvent(event);\n const window = this._recorder.injectedScript.window;\n if (window.top !== window && this._recorder.deepEventTarget(event).nodeType === Node.DOCUMENT_NODE)\n this._reset(true);\n }\n onKeyDown(event) {\n consumeEvent(event);\n if (event.key === "Escape") {\n if (this._assertVisibility)\n this._recorder.setMode("recording");\n }\n }\n onKeyUp(event) {\n consumeEvent(event);\n }\n onScroll(event) {\n this._reset(false);\n }\n _commit(selector, model) {\n var _a;\n if (this._assertVisibility) {\n void this._recorder.recordAction({\n name: "assertVisible",\n selector,\n signals: []\n });\n this._recorder.setMode("recording");\n (_a = this._recorder.overlay) == null ? void 0 : _a.flashToolSucceeded("assertingVisibility");\n } else {\n this._recorder.elementPicked(selector, model);\n }\n }\n _reset(userGesture) {\n this._hoveredElement = null;\n this._hoveredModel = null;\n this._recorder.updateHighlight(null, userGesture);\n }\n};\nvar RecordActionTool = class {\n constructor(recorder) {\n this._hoveredModel = null;\n this._hoveredElement = null;\n this._activeModel = null;\n this._expectProgrammaticKeyUp = false;\n this._observer = null;\n this._recorder = recorder;\n this._performingActions = /* @__PURE__ */ new Set();\n this._dialog = new Dialog(recorder);\n }\n cursor() {\n return "pointer";\n }\n _installObserverIfNeeded() {\n var _a;\n if (this._observer)\n return;\n if (!((_a = this._recorder.injectedScript.document) == null ? void 0 : _a.body))\n return;\n this._observer = new MutationObserver((mutations) => {\n if (!this._hoveredElement)\n return;\n for (const mutation of mutations) {\n for (const node of mutation.removedNodes) {\n if (node === this._hoveredElement || node.contains(this._hoveredElement))\n this._resetHoveredModel();\n }\n }\n });\n this._observer.observe(this._recorder.injectedScript.document.body, { childList: true, subtree: true });\n }\n uninstall() {\n var _a;\n (_a = this._observer) == null ? void 0 : _a.disconnect();\n this._observer = null;\n this._hoveredModel = null;\n this._hoveredElement = null;\n this._activeModel = null;\n this._expectProgrammaticKeyUp = false;\n this._dialog.close();\n }\n onClick(event) {\n if (this._dialog.isShowing()) {\n if (event.button === 2 && event.type === "auxclick") {\n consumeEvent(event);\n }\n return;\n }\n if (isRangeInput(this._hoveredElement))\n return;\n if (this._shouldIgnoreMouseEvent(event))\n return;\n if (this._actionInProgress(event))\n return;\n if (this._consumedDueToNoModel(event, this._hoveredModel))\n return;\n if (event.button === 2 && event.type === "auxclick") {\n this._showActionListDialog(this._hoveredModel, event);\n return;\n }\n const checkbox = asCheckbox(this._recorder.deepEventTarget(event));\n if (checkbox && event.detail === 1) {\n this._performAction({\n name: checkbox.checked ? "check" : "uncheck",\n selector: this._hoveredModel.selector,\n signals: []\n });\n return;\n }\n this._cancelPendingClickAction();\n if (event.detail === 1) {\n this._pendingClickAction = {\n action: {\n name: "click",\n selector: this._hoveredModel.selector,\n position: positionForEvent(event),\n signals: [],\n button: buttonForEvent(event),\n modifiers: modifiersForEvent(event),\n clickCount: event.detail\n },\n timeout: this._recorder.injectedScript.utils.builtins.setTimeout(() => this._commitPendingClickAction(), 200)\n };\n }\n }\n onDblClick(event) {\n if (this._dialog.isShowing())\n return;\n if (isRangeInput(this._hoveredElement))\n return;\n if (this._shouldIgnoreMouseEvent(event))\n return;\n if (this._actionInProgress(event))\n return;\n if (this._consumedDueToNoModel(event, this._hoveredModel))\n return;\n this._cancelPendingClickAction();\n this._performAction({\n name: "click",\n selector: this._hoveredModel.selector,\n position: positionForEvent(event),\n signals: [],\n button: buttonForEvent(event),\n modifiers: modifiersForEvent(event),\n clickCount: event.detail\n });\n }\n _commitPendingClickAction() {\n if (this._pendingClickAction)\n this._performAction(this._pendingClickAction.action);\n this._cancelPendingClickAction();\n }\n _cancelPendingClickAction() {\n if (this._pendingClickAction)\n this._recorder.injectedScript.utils.builtins.clearTimeout(this._pendingClickAction.timeout);\n this._pendingClickAction = void 0;\n }\n onContextMenu(event) {\n if (this._dialog.isShowing()) {\n consumeEvent(event);\n return;\n }\n if (this._shouldIgnoreMouseEvent(event))\n return;\n if (this._actionInProgress(event))\n return;\n if (this._consumedDueToNoModel(event, this._hoveredModel))\n return;\n this._showActionListDialog(this._hoveredModel, event);\n }\n onPointerDown(event) {\n if (this._dialog.isShowing())\n return;\n if (this._shouldIgnoreMouseEvent(event))\n return;\n this._consumeWhenAboutToPerform(event);\n }\n onPointerUp(event) {\n if (this._dialog.isShowing())\n return;\n if (this._shouldIgnoreMouseEvent(event))\n return;\n this._consumeWhenAboutToPerform(event);\n }\n onMouseDown(event) {\n if (this._dialog.isShowing())\n return;\n if (this._shouldIgnoreMouseEvent(event))\n return;\n this._consumeWhenAboutToPerform(event);\n this._activeModel = this._hoveredModel;\n }\n onMouseUp(event) {\n if (this._dialog.isShowing())\n return;\n if (this._shouldIgnoreMouseEvent(event))\n return;\n this._consumeWhenAboutToPerform(event);\n }\n onMouseMove(event) {\n if (this._dialog.isShowing())\n return;\n const target = this._recorder.deepEventTarget(event);\n if (this._hoveredElement === target)\n return;\n this._hoveredElement = target;\n this._updateModelForHoveredElement();\n }\n onMouseLeave(event) {\n if (this._dialog.isShowing())\n return;\n const window = this._recorder.injectedScript.window;\n if (window.top !== window && this._recorder.deepEventTarget(event).nodeType === Node.DOCUMENT_NODE) {\n this._hoveredElement = null;\n this._updateModelForHoveredElement();\n }\n }\n onFocus(event) {\n if (this._dialog.isShowing())\n return;\n this._onFocus(true);\n }\n onInput(event) {\n if (this._dialog.isShowing())\n return;\n const target = this._recorder.deepEventTarget(event);\n if (target.nodeName === "INPUT" && target.type.toLowerCase() === "file") {\n this._recordAction({\n name: "setInputFiles",\n selector: this._activeModel.selector,\n signals: [],\n files: [...target.files || []].map((file) => file.name)\n });\n return;\n }\n if (isRangeInput(target)) {\n this._recordAction({\n name: "fill",\n // must use hoveredModel instead of activeModel for it to work in webkit\n selector: this._hoveredModel.selector,\n signals: [],\n text: target.value\n });\n return;\n }\n if (["INPUT", "TEXTAREA"].includes(target.nodeName) || target.isContentEditable) {\n if (target.nodeName === "INPUT" && ["checkbox", "radio"].includes(target.type.toLowerCase())) {\n return;\n }\n if (this._consumedDueWrongTarget(event))\n return;\n this._recordAction({\n name: "fill",\n selector: this._activeModel.selector,\n signals: [],\n text: target.isContentEditable ? target.innerText : target.value\n });\n }\n if (target.nodeName === "SELECT") {\n const selectElement = target;\n this._recordAction({\n name: "select",\n selector: this._activeModel.selector,\n options: [...selectElement.selectedOptions].map((option) => option.value),\n signals: []\n });\n }\n }\n onKeyDown(event) {\n if (this._dialog.isShowing())\n return;\n if (!this._shouldGenerateKeyPressFor(event))\n return;\n if (this._actionInProgress(event)) {\n this._expectProgrammaticKeyUp = true;\n return;\n }\n if (this._consumedDueWrongTarget(event))\n return;\n if (event.key === " ") {\n const checkbox = asCheckbox(this._recorder.deepEventTarget(event));\n if (checkbox && event.detail === 0) {\n this._performAction({\n name: checkbox.checked ? "uncheck" : "check",\n selector: this._activeModel.selector,\n signals: []\n });\n return;\n }\n }\n this._performAction({\n name: "press",\n selector: this._activeModel.selector,\n signals: [],\n key: event.key,\n modifiers: modifiersForEvent(event)\n });\n }\n onKeyUp(event) {\n if (this._dialog.isShowing())\n return;\n if (!this._shouldGenerateKeyPressFor(event))\n return;\n if (!this._expectProgrammaticKeyUp) {\n consumeEvent(event);\n return;\n }\n this._expectProgrammaticKeyUp = false;\n }\n onScroll(event) {\n if (this._dialog.isShowing())\n return;\n this._resetHoveredModel();\n }\n _showActionListDialog(model, event) {\n consumeEvent(event);\n const actionPosition = positionForEvent(event);\n const actions = [\n {\n title: "Click",\n cb: () => this._performAction({\n name: "click",\n selector: model.selector,\n position: actionPosition,\n signals: [],\n button: "left",\n modifiers: 0,\n clickCount: 0\n })\n },\n {\n title: "Right click",\n cb: () => this._performAction({\n name: "click",\n selector: model.selector,\n position: actionPosition,\n signals: [],\n button: "right",\n modifiers: 0,\n clickCount: 0\n })\n },\n {\n title: "Double click",\n cb: () => this._performAction({\n name: "click",\n selector: model.selector,\n position: actionPosition,\n signals: [],\n button: "left",\n modifiers: 0,\n clickCount: 2\n })\n },\n {\n title: "Hover",\n cb: () => this._performAction({\n name: "hover",\n selector: model.selector,\n position: actionPosition,\n signals: []\n })\n },\n {\n title: "Pick locator",\n cb: () => this._recorder.elementPicked(model.selector, model)\n }\n ];\n const listElement = this._recorder.document.createElement("x-pw-action-list");\n listElement.setAttribute("role", "list");\n listElement.setAttribute("aria-label", "Choose action");\n for (const action of actions) {\n const actionElement = this._recorder.document.createElement("x-pw-action-item");\n actionElement.setAttribute("role", "listitem");\n actionElement.textContent = action.title;\n actionElement.setAttribute("aria-label", action.title);\n actionElement.addEventListener("click", () => {\n this._dialog.close();\n action.cb();\n });\n listElement.appendChild(actionElement);\n }\n const dialogElement = this._dialog.show({\n label: "Choose action",\n body: listElement,\n autosize: true\n });\n const anchorBox = this._recorder.highlight.firstTooltipBox() || model.elements[0].getBoundingClientRect();\n const dialogPosition = this._recorder.highlight.tooltipPosition(anchorBox, dialogElement);\n this._dialog.moveTo(dialogPosition.anchorTop, dialogPosition.anchorLeft);\n }\n _resetHoveredModel() {\n this._hoveredModel = null;\n this._hoveredElement = null;\n this._updateHighlight(false);\n }\n _onFocus(userGesture) {\n const activeElement = deepActiveElement(this._recorder.document);\n if (userGesture && activeElement === this._recorder.document.body)\n return;\n const result = activeElement ? this._recorder.injectedScript.generateSelector(activeElement, { testIdAttributeName: this._recorder.state.testIdAttributeName }) : null;\n this._activeModel = result && result.selector ? { ...result, color: HighlightColors.action } : null;\n if (userGesture) {\n this._hoveredElement = activeElement;\n this._updateModelForHoveredElement();\n }\n }\n _shouldIgnoreMouseEvent(event) {\n const target = this._recorder.deepEventTarget(event);\n const nodeName = target.nodeName;\n if (nodeName === "SELECT" || nodeName === "OPTION")\n return true;\n if (nodeName === "INPUT" && ["date", "range"].includes(target.type))\n return true;\n return false;\n }\n _actionInProgress(event) {\n const isKeyEvent = event instanceof KeyboardEvent;\n const isMouseOrPointerEvent = event instanceof MouseEvent || event instanceof PointerEvent;\n for (const action of this._performingActions) {\n if (isKeyEvent && action.name === "press" && event.key === action.key)\n return true;\n if (isMouseOrPointerEvent && (action.name === "click" || action.name === "hover" || action.name === "check" || action.name === "uncheck"))\n return true;\n }\n consumeEvent(event);\n return false;\n }\n _consumedDueToNoModel(event, model) {\n if (model)\n return false;\n consumeEvent(event);\n return true;\n }\n _consumedDueWrongTarget(event) {\n if (this._activeModel && this._activeModel.elements[0] === this._recorder.deepEventTarget(event))\n return false;\n consumeEvent(event);\n return true;\n }\n _consumeWhenAboutToPerform(event) {\n if (!this._performingActions.size)\n consumeEvent(event);\n }\n _reportPerformedActionForTests() {\n if (!this._recorder.injectedScript.isUnderTest)\n return;\n console.error("Action performed for test: " + JSON.stringify({\n // eslint-disable-line no-console\n hovered: this._hoveredModel ? this._hoveredModel.selector : null,\n active: this._activeModel ? this._activeModel.selector : null\n }));\n }\n _recordAction(action) {\n void this._recorder.recordAction(action).then(() => this._reportPerformedActionForTests());\n }\n _performAction(action) {\n this._recorder.updateHighlight(null, false);\n this._performingActions.add(action);\n void this._recorder.performAction(action).finally(() => {\n this._performingActions.delete(action);\n this._onFocus(false);\n }).then(() => this._reportPerformedActionForTests());\n }\n _shouldGenerateKeyPressFor(event) {\n if (typeof event.key !== "string")\n return false;\n if (event.key === "Enter" && (this._recorder.deepEventTarget(event).nodeName === "TEXTAREA" || this._recorder.deepEventTarget(event).isContentEditable))\n return false;\n if (["Backspace", "Delete", "AltGraph"].includes(event.key))\n return false;\n if (event.key === "@" && event.code === "KeyL")\n return false;\n if (navigator.platform.includes("Mac")) {\n if (event.key === "v" && event.metaKey)\n return false;\n } else {\n if (event.key === "v" && event.ctrlKey)\n return false;\n if (event.key === "Insert" && event.shiftKey)\n return false;\n }\n if (["Shift", "Control", "Meta", "Alt", "Process"].includes(event.key))\n return false;\n const hasModifier = event.ctrlKey || event.altKey || event.metaKey;\n if (event.key.length === 1 && !hasModifier)\n return !!asCheckbox(this._recorder.deepEventTarget(event));\n return true;\n }\n _updateModelForHoveredElement() {\n this._installObserverIfNeeded();\n if (this._performingActions.size)\n return;\n if (!this._hoveredElement || !this._hoveredElement.isConnected) {\n this._hoveredModel = null;\n this._hoveredElement = null;\n this._updateHighlight(true);\n return;\n }\n const { selector, elements } = this._recorder.injectedScript.generateSelector(this._hoveredElement, { testIdAttributeName: this._recorder.state.testIdAttributeName });\n if (this._hoveredModel && this._hoveredModel.selector === selector)\n return;\n this._hoveredModel = selector ? { selector, elements, color: HighlightColors.action } : null;\n this._updateHighlight(true);\n }\n _updateHighlight(userGesture) {\n this._recorder.updateHighlight(this._hoveredModel, userGesture);\n }\n};\nvar JsonRecordActionTool = class {\n constructor(recorder) {\n this._recorder = recorder;\n }\n install() {\n this._recorder.highlight.uninstall();\n }\n uninstall() {\n this._recorder.highlight.install();\n }\n onClick(event) {\n const element = this._recorder.deepEventTarget(event);\n if (isRangeInput(element))\n return;\n if (event.button === 2 && event.type === "auxclick")\n return;\n if (this._shouldIgnoreMouseEvent(event))\n return;\n const checkbox = asCheckbox(element);\n const { ariaSnapshot, selector, ref } = this._ariaSnapshot(element);\n if (checkbox && event.detail === 1) {\n void this._recorder.recordAction({\n name: checkbox.checked ? "check" : "uncheck",\n selector,\n ref,\n signals: [],\n ariaSnapshot\n });\n return;\n }\n void this._recorder.recordAction({\n name: "click",\n selector,\n ref,\n ariaSnapshot,\n position: positionForEvent(event),\n signals: [],\n button: buttonForEvent(event),\n modifiers: modifiersForEvent(event),\n clickCount: event.detail\n });\n }\n onContextMenu(event) {\n const element = this._recorder.deepEventTarget(event);\n const { ariaSnapshot, selector, ref } = this._ariaSnapshot(element);\n void this._recorder.recordAction({\n name: "click",\n selector,\n ref,\n ariaSnapshot,\n position: positionForEvent(event),\n signals: [],\n button: "right",\n modifiers: modifiersForEvent(event),\n clickCount: 1\n });\n }\n onInput(event) {\n const element = this._recorder.deepEventTarget(event);\n const { ariaSnapshot, selector, ref } = this._ariaSnapshot(element);\n if (isRangeInput(element)) {\n void this._recorder.recordAction({\n name: "fill",\n selector,\n ref,\n ariaSnapshot,\n signals: [],\n text: element.value\n });\n return;\n }\n if (["INPUT", "TEXTAREA"].includes(element.nodeName) || element.isContentEditable) {\n if (element.nodeName === "INPUT" && ["checkbox", "radio"].includes(element.type.toLowerCase())) {\n return;\n }\n void this._recorder.recordAction({\n name: "fill",\n ref,\n selector,\n ariaSnapshot,\n signals: [],\n text: element.isContentEditable ? element.innerText : element.value\n });\n return;\n }\n if (element.nodeName === "SELECT") {\n const selectElement = element;\n void this._recorder.recordAction({\n name: "select",\n selector,\n ref,\n ariaSnapshot,\n options: [...selectElement.selectedOptions].map((option) => option.value),\n signals: []\n });\n return;\n }\n }\n onKeyDown(event) {\n if (!this._shouldGenerateKeyPressFor(event))\n return;\n const element = this._recorder.deepEventTarget(event);\n const { ariaSnapshot, selector, ref } = this._ariaSnapshot(element);\n if (event.key === " ") {\n const checkbox = asCheckbox(element);\n if (checkbox && event.detail === 0) {\n void this._recorder.recordAction({\n name: checkbox.checked ? "uncheck" : "check",\n selector,\n ref,\n ariaSnapshot,\n signals: []\n });\n return;\n }\n }\n void this._recorder.recordAction({\n name: "press",\n selector,\n ref,\n ariaSnapshot,\n signals: [],\n key: event.key,\n modifiers: modifiersForEvent(event)\n });\n }\n _shouldIgnoreMouseEvent(event) {\n const target = this._recorder.deepEventTarget(event);\n const nodeName = target.nodeName;\n if (nodeName === "SELECT" || nodeName === "OPTION")\n return true;\n if (nodeName === "INPUT" && ["date", "range"].includes(target.type))\n return true;\n return false;\n }\n _shouldGenerateKeyPressFor(event) {\n if (typeof event.key !== "string")\n return false;\n if (event.key === "Enter" && (this._recorder.deepEventTarget(event).nodeName === "TEXTAREA" || this._recorder.deepEventTarget(event).isContentEditable))\n return false;\n if (["Backspace", "Delete", "AltGraph"].includes(event.key))\n return false;\n if (event.key === "@" && event.code === "KeyL")\n return false;\n if (navigator.platform.includes("Mac")) {\n if (event.key === "v" && event.metaKey)\n return false;\n } else {\n if (event.key === "v" && event.ctrlKey)\n return false;\n if (event.key === "Insert" && event.shiftKey)\n return false;\n }\n if (["Shift", "Control", "Meta", "Alt", "Process"].includes(event.key))\n return false;\n const hasModifier = event.ctrlKey || event.altKey || event.metaKey;\n if (event.key.length === 1 && !hasModifier)\n return !this._isEditable(this._recorder.deepEventTarget(event));\n return true;\n }\n _isEditable(element) {\n if (element.nodeName === "TEXTAREA" || element.nodeName === "INPUT")\n return true;\n if (element.isContentEditable)\n return true;\n return false;\n }\n _ariaSnapshot(element) {\n const { ariaSnapshot, refs } = this._recorder.injectedScript.ariaSnapshotForRecorder();\n const ref = element ? refs.get(element) : void 0;\n const elementInfo = element ? this._recorder.injectedScript.generateSelector(element, { testIdAttributeName: this._recorder.state.testIdAttributeName }) : void 0;\n return { ariaSnapshot, selector: elementInfo == null ? void 0 : elementInfo.selector, ref };\n }\n};\nvar TextAssertionTool = class {\n constructor(recorder, kind) {\n this._hoverHighlight = null;\n this._action = null;\n this._recorder = recorder;\n this._textCache = /* @__PURE__ */ new Map();\n this._kind = kind;\n this._dialog = new Dialog(recorder);\n }\n cursor() {\n return "pointer";\n }\n uninstall() {\n this._dialog.close();\n this._hoverHighlight = null;\n }\n onClick(event) {\n consumeEvent(event);\n if (this._kind === "value") {\n this._commitAssertValue();\n } else {\n if (!this._dialog.isShowing())\n this._showDialog();\n }\n }\n onMouseDown(event) {\n const target = this._recorder.deepEventTarget(event);\n if (this._elementHasValue(target))\n event.preventDefault();\n }\n onPointerUp(event) {\n var _a;\n const target = (_a = this._hoverHighlight) == null ? void 0 : _a.elements[0];\n if (this._kind === "value" && target && (target.nodeName === "INPUT" || target.nodeName === "SELECT") && target.disabled) {\n this._commitAssertValue();\n }\n }\n onMouseMove(event) {\n var _a;\n if (this._dialog.isShowing())\n return;\n const target = this._recorder.deepEventTarget(event);\n if (((_a = this._hoverHighlight) == null ? void 0 : _a.elements[0]) === target)\n return;\n if (this._kind === "text" || this._kind === "snapshot") {\n this._hoverHighlight = this._recorder.injectedScript.utils.elementText(this._textCache, target).full ? { elements: [target], selector: "", color: HighlightColors.assert } : null;\n } else if (this._elementHasValue(target)) {\n const generated = this._recorder.injectedScript.generateSelector(target, { testIdAttributeName: this._recorder.state.testIdAttributeName });\n this._hoverHighlight = { selector: generated.selector, elements: generated.elements, color: HighlightColors.assert };\n } else {\n this._hoverHighlight = null;\n }\n this._recorder.updateHighlight(this._hoverHighlight, true);\n }\n onKeyDown(event) {\n if (event.key === "Escape")\n this._recorder.setMode("recording");\n consumeEvent(event);\n }\n onScroll(event) {\n this._recorder.updateHighlight(this._hoverHighlight, false);\n }\n _elementHasValue(element) {\n return element.nodeName === "TEXTAREA" || element.nodeName === "SELECT" || element.nodeName === "INPUT" && !["button", "image", "reset", "submit"].includes(element.type);\n }\n _generateAction() {\n var _a;\n this._textCache.clear();\n const target = (_a = this._hoverHighlight) == null ? void 0 : _a.elements[0];\n if (!target)\n return null;\n if (this._kind === "value") {\n if (!this._elementHasValue(target))\n return null;\n const { selector } = this._recorder.injectedScript.generateSelector(target, { testIdAttributeName: this._recorder.state.testIdAttributeName });\n if (target.nodeName === "INPUT" && ["checkbox", "radio"].includes(target.type.toLowerCase())) {\n return {\n name: "assertChecked",\n selector,\n signals: [],\n // Interestingly, inputElement.checked is reversed inside this event handler.\n checked: !target.checked\n };\n } else {\n return {\n name: "assertValue",\n selector,\n signals: [],\n value: target.value\n };\n }\n } else if (this._kind === "snapshot") {\n const generated = this._recorder.injectedScript.generateSelector(target, { testIdAttributeName: this._recorder.state.testIdAttributeName, forTextExpect: true });\n this._hoverHighlight = { selector: generated.selector, elements: generated.elements, color: HighlightColors.assert };\n this._recorder.updateHighlight(this._hoverHighlight, true);\n return {\n name: "assertSnapshot",\n selector: this._hoverHighlight.selector,\n signals: [],\n ariaSnapshot: this._recorder.injectedScript.ariaSnapshot(target, { mode: "codegen" })\n };\n } else {\n const generated = this._recorder.injectedScript.generateSelector(target, { testIdAttributeName: this._recorder.state.testIdAttributeName, forTextExpect: true });\n this._hoverHighlight = { selector: generated.selector, elements: generated.elements, color: HighlightColors.assert };\n this._recorder.updateHighlight(this._hoverHighlight, true);\n return {\n name: "assertText",\n selector: this._hoverHighlight.selector,\n signals: [],\n text: this._recorder.injectedScript.utils.elementText(this._textCache, target).normalized,\n substring: true\n };\n }\n }\n _renderValue(action) {\n if ((action == null ? void 0 : action.name) === "assertText")\n return this._recorder.injectedScript.utils.normalizeWhiteSpace(action.text);\n if ((action == null ? void 0 : action.name) === "assertChecked")\n return String(action.checked);\n if ((action == null ? void 0 : action.name) === "assertValue")\n return action.value;\n if ((action == null ? void 0 : action.name) === "assertSnapshot")\n return action.ariaSnapshot;\n return "";\n }\n _commit() {\n if (!this._action || !this._dialog.isShowing())\n return;\n this._dialog.close();\n void this._recorder.recordAction(this._action);\n this._recorder.setMode("recording");\n }\n _showDialog() {\n var _a, _b, _c, _d;\n if (!((_a = this._hoverHighlight) == null ? void 0 : _a.elements[0]))\n return;\n this._action = this._generateAction();\n if (((_b = this._action) == null ? void 0 : _b.name) === "assertText") {\n this._showTextDialog(this._action);\n } else if (((_c = this._action) == null ? void 0 : _c.name) === "assertSnapshot") {\n void this._recorder.recordAction(this._action);\n this._recorder.setMode("recording");\n (_d = this._recorder.overlay) == null ? void 0 : _d.flashToolSucceeded("assertingSnapshot");\n }\n }\n _showTextDialog(action) {\n const textElement = this._recorder.document.createElement("textarea");\n textElement.setAttribute("spellcheck", "false");\n textElement.value = this._renderValue(action);\n textElement.classList.add("text-editor");\n const updateAndValidate = () => {\n var _a;\n const newValue = this._recorder.injectedScript.utils.normalizeWhiteSpace(textElement.value);\n const target = (_a = this._hoverHighlight) == null ? void 0 : _a.elements[0];\n if (!target)\n return;\n action.text = newValue;\n const targetText = this._recorder.injectedScript.utils.elementText(this._textCache, target).normalized;\n const matches = newValue && targetText.includes(newValue);\n textElement.classList.toggle("does-not-match", !matches);\n };\n textElement.addEventListener("input", updateAndValidate);\n const label = "Assert that element contains text";\n const dialogElement = this._dialog.show({\n label,\n body: textElement,\n onCommit: () => this._commit()\n });\n const position = this._recorder.highlight.tooltipPosition(this._recorder.highlight.firstBox(), dialogElement);\n this._dialog.moveTo(position.anchorTop, position.anchorLeft);\n textElement.focus();\n }\n _commitAssertValue() {\n var _a;\n if (this._kind !== "value")\n return;\n const action = this._generateAction();\n if (!action)\n return;\n void this._recorder.recordAction(action);\n this._recorder.setMode("recording");\n (_a = this._recorder.overlay) == null ? void 0 : _a.flashToolSucceeded("assertingValue");\n }\n};\nvar Overlay = class {\n constructor(recorder) {\n this._listeners = [];\n this._offsetX = 0;\n this._measure = { width: 0, height: 0 };\n this._recorder = recorder;\n const document = this._recorder.document;\n this._overlayElement = document.createElement("x-pw-overlay");\n const toolsListElement = document.createElement("x-pw-tools-list");\n this._overlayElement.appendChild(toolsListElement);\n this._dragHandle = document.createElement("x-pw-tool-gripper");\n this._dragHandle.appendChild(document.createElement("x-div"));\n toolsListElement.appendChild(this._dragHandle);\n this._recordToggle = this._recorder.document.createElement("x-pw-tool-item");\n this._recordToggle.title = "Record";\n this._recordToggle.classList.add("record");\n this._recordToggle.appendChild(this._recorder.document.createElement("x-div"));\n toolsListElement.appendChild(this._recordToggle);\n this._pickLocatorToggle = this._recorder.document.createElement("x-pw-tool-item");\n this._pickLocatorToggle.title = "Pick locator";\n this._pickLocatorToggle.classList.add("pick-locator");\n this._pickLocatorToggle.appendChild(this._recorder.document.createElement("x-div"));\n toolsListElement.appendChild(this._pickLocatorToggle);\n this._assertVisibilityToggle = this._recorder.document.createElement("x-pw-tool-item");\n this._assertVisibilityToggle.title = "Assert visibility";\n this._assertVisibilityToggle.classList.add("visibility");\n this._assertVisibilityToggle.appendChild(this._recorder.document.createElement("x-div"));\n toolsListElement.appendChild(this._assertVisibilityToggle);\n this._assertTextToggle = this._recorder.document.createElement("x-pw-tool-item");\n this._assertTextToggle.title = "Assert text";\n this._assertTextToggle.classList.add("text");\n this._assertTextToggle.appendChild(this._recorder.document.createElement("x-div"));\n toolsListElement.appendChild(this._assertTextToggle);\n this._assertValuesToggle = this._recorder.document.createElement("x-pw-tool-item");\n this._assertValuesToggle.title = "Assert value";\n this._assertValuesToggle.classList.add("value");\n this._assertValuesToggle.appendChild(this._recorder.document.createElement("x-div"));\n toolsListElement.appendChild(this._assertValuesToggle);\n this._assertSnapshotToggle = this._recorder.document.createElement("x-pw-tool-item");\n this._assertSnapshotToggle.title = "Assert snapshot";\n this._assertSnapshotToggle.classList.add("snapshot");\n this._assertSnapshotToggle.appendChild(this._recorder.document.createElement("x-div"));\n toolsListElement.appendChild(this._assertSnapshotToggle);\n this._updateVisualPosition();\n this._refreshListeners();\n }\n _refreshListeners() {\n removeEventListeners(this._listeners);\n this._listeners = [\n addEventListener(this._dragHandle, "mousedown", (event) => {\n this._dragState = { offsetX: this._offsetX, dragStart: { x: event.clientX, y: 0 } };\n }),\n addEventListener(this._recordToggle, "click", () => {\n if (this._recordToggle.classList.contains("disabled"))\n return;\n this._recorder.setMode(this._recorder.state.mode === "none" || this._recorder.state.mode === "standby" || this._recorder.state.mode === "inspecting" ? "recording" : "standby");\n }),\n addEventListener(this._pickLocatorToggle, "click", () => {\n if (this._pickLocatorToggle.classList.contains("disabled"))\n return;\n const newMode = {\n "inspecting": "standby",\n "none": "inspecting",\n "standby": "inspecting",\n "recording": "recording-inspecting",\n "recording-inspecting": "recording",\n "assertingText": "recording-inspecting",\n "assertingVisibility": "recording-inspecting",\n "assertingValue": "recording-inspecting",\n "assertingSnapshot": "recording-inspecting"\n };\n this._recorder.setMode(newMode[this._recorder.state.mode]);\n }),\n addEventListener(this._assertVisibilityToggle, "click", () => {\n if (!this._assertVisibilityToggle.classList.contains("disabled"))\n this._recorder.setMode(this._recorder.state.mode === "assertingVisibility" ? "recording" : "assertingVisibility");\n }),\n addEventListener(this._assertTextToggle, "click", () => {\n if (!this._assertTextToggle.classList.contains("disabled"))\n this._recorder.setMode(this._recorder.state.mode === "assertingText" ? "recording" : "assertingText");\n }),\n addEventListener(this._assertValuesToggle, "click", () => {\n if (!this._assertValuesToggle.classList.contains("disabled"))\n this._recorder.setMode(this._recorder.state.mode === "assertingValue" ? "recording" : "assertingValue");\n }),\n addEventListener(this._assertSnapshotToggle, "click", () => {\n if (!this._assertSnapshotToggle.classList.contains("disabled"))\n this._recorder.setMode(this._recorder.state.mode === "assertingSnapshot" ? "recording" : "assertingSnapshot");\n })\n ];\n }\n install() {\n this._recorder.highlight.appendChild(this._overlayElement);\n this._refreshListeners();\n this._updateVisualPosition();\n }\n contains(element) {\n return this._recorder.injectedScript.utils.isInsideScope(this._overlayElement, element);\n }\n setUIState(state) {\n const isRecording = state.mode === "recording" || state.mode === "assertingText" || state.mode === "assertingVisibility" || state.mode === "assertingValue" || state.mode === "assertingSnapshot" || state.mode === "recording-inspecting";\n this._recordToggle.classList.toggle("toggled", isRecording);\n this._recordToggle.title = isRecording ? "Stop Recording" : "Start Recording";\n this._pickLocatorToggle.classList.toggle("toggled", state.mode === "inspecting" || state.mode === "recording-inspecting");\n this._assertVisibilityToggle.classList.toggle("toggled", state.mode === "assertingVisibility");\n this._assertVisibilityToggle.classList.toggle("disabled", state.mode === "none" || state.mode === "standby" || state.mode === "inspecting");\n this._assertTextToggle.classList.toggle("toggled", state.mode === "assertingText");\n this._assertTextToggle.classList.toggle("disabled", state.mode === "none" || state.mode === "standby" || state.mode === "inspecting");\n this._assertValuesToggle.classList.toggle("toggled", state.mode === "assertingValue");\n this._assertValuesToggle.classList.toggle("disabled", state.mode === "none" || state.mode === "standby" || state.mode === "inspecting");\n this._assertSnapshotToggle.classList.toggle("toggled", state.mode === "assertingSnapshot");\n this._assertSnapshotToggle.classList.toggle("disabled", state.mode === "none" || state.mode === "standby" || state.mode === "inspecting");\n if (this._offsetX !== state.overlay.offsetX) {\n this._offsetX = state.overlay.offsetX;\n this._updateVisualPosition();\n }\n if (state.mode === "none")\n this._hideOverlay();\n else\n this._showOverlay();\n }\n flashToolSucceeded(tool) {\n let element;\n if (tool === "assertingVisibility")\n element = this._assertVisibilityToggle;\n else if (tool === "assertingSnapshot")\n element = this._assertSnapshotToggle;\n else\n element = this._assertValuesToggle;\n element.classList.add("succeeded");\n this._recorder.injectedScript.utils.builtins.setTimeout(() => element.classList.remove("succeeded"), 2e3);\n }\n _hideOverlay() {\n this._overlayElement.setAttribute("hidden", "true");\n }\n _showOverlay() {\n if (!this._overlayElement.hasAttribute("hidden"))\n return;\n this._overlayElement.removeAttribute("hidden");\n this._updateVisualPosition();\n }\n _updateVisualPosition() {\n this._measure = this._overlayElement.getBoundingClientRect();\n this._overlayElement.style.left = (this._recorder.injectedScript.window.innerWidth - this._measure.width) / 2 + this._offsetX + "px";\n }\n onMouseMove(event) {\n if (!event.buttons) {\n this._dragState = void 0;\n return false;\n }\n if (this._dragState) {\n this._offsetX = this._dragState.offsetX + event.clientX - this._dragState.dragStart.x;\n const halfGapSize = (this._recorder.injectedScript.window.innerWidth - this._measure.width) / 2 - 10;\n this._offsetX = Math.max(-halfGapSize, Math.min(halfGapSize, this._offsetX));\n this._updateVisualPosition();\n this._recorder.setOverlayState({ offsetX: this._offsetX });\n consumeEvent(event);\n return true;\n }\n return false;\n }\n onMouseUp(event) {\n if (this._dragState) {\n consumeEvent(event);\n return true;\n }\n return false;\n }\n onClick(event) {\n if (this._dragState) {\n this._dragState = void 0;\n consumeEvent(event);\n return true;\n }\n return false;\n }\n onDblClick(event) {\n return false;\n }\n};\nvar Recorder = class {\n constructor(injectedScript, options) {\n this._listeners = [];\n this._lastHighlightedSelector = void 0;\n this._lastHighlightedAriaTemplateJSON = "undefined";\n this.state = {\n mode: "none",\n testIdAttributeName: "data-testid",\n language: "javascript",\n overlay: { offsetX: 0 }\n };\n this._delegate = {};\n var _a, _b;\n this.document = injectedScript.document;\n this.injectedScript = injectedScript;\n this.highlight = injectedScript.createHighlight();\n this._tools = {\n "none": new NoneTool(),\n "standby": new NoneTool(),\n "inspecting": new InspectTool(this, false),\n "recording": (options == null ? void 0 : options.recorderMode) === "api" ? new JsonRecordActionTool(this) : new RecordActionTool(this),\n "recording-inspecting": new InspectTool(this, false),\n "assertingText": new TextAssertionTool(this, "text"),\n "assertingVisibility": new InspectTool(this, true),\n "assertingValue": new TextAssertionTool(this, "value"),\n "assertingSnapshot": new TextAssertionTool(this, "snapshot")\n };\n this._currentTool = this._tools.none;\n (_b = (_a = this._currentTool).install) == null ? void 0 : _b.call(_a);\n if (injectedScript.window.top === injectedScript.window && !(options == null ? void 0 : options.hideToolbar)) {\n this.overlay = new Overlay(this);\n this.overlay.setUIState(this.state);\n }\n this._stylesheet = new injectedScript.window.CSSStyleSheet();\n this._stylesheet.replaceSync(`\n body[data-pw-cursor=pointer] *, body[data-pw-cursor=pointer] *::after { cursor: pointer !important; }\n body[data-pw-cursor=text] *, body[data-pw-cursor=text] *::after { cursor: text !important; }\n `);\n this.installListeners();\n injectedScript.utils.cacheNormalizedWhitespaces();\n if (injectedScript.isUnderTest)\n console.error("Recorder script ready for test");\n injectedScript.consoleApi.install();\n }\n installListeners() {\n var _a, _b, _c;\n removeEventListeners(this._listeners);\n this._listeners = [\n addEventListener(this.document, "click", (event) => this._onClick(event), true),\n addEventListener(this.document, "auxclick", (event) => this._onClick(event), true),\n addEventListener(this.document, "dblclick", (event) => this._onDblClick(event), true),\n addEventListener(this.document, "contextmenu", (event) => this._onContextMenu(event), true),\n addEventListener(this.document, "dragstart", (event) => this._onDragStart(event), true),\n addEventListener(this.document, "input", (event) => this._onInput(event), true),\n addEventListener(this.document, "keydown", (event) => this._onKeyDown(event), true),\n addEventListener(this.document, "keyup", (event) => this._onKeyUp(event), true),\n addEventListener(this.document, "pointerdown", (event) => this._onPointerDown(event), true),\n addEventListener(this.document, "pointerup", (event) => this._onPointerUp(event), true),\n addEventListener(this.document, "mousedown", (event) => this._onMouseDown(event), true),\n addEventListener(this.document, "mouseup", (event) => this._onMouseUp(event), true),\n addEventListener(this.document, "mousemove", (event) => this._onMouseMove(event), true),\n addEventListener(this.document, "mouseleave", (event) => this._onMouseLeave(event), true),\n addEventListener(this.document, "mouseenter", (event) => this._onMouseEnter(event), true),\n addEventListener(this.document, "focus", (event) => this._onFocus(event), true),\n addEventListener(this.document, "scroll", (event) => this._onScroll(event), true)\n ];\n this.highlight.install();\n let recreationInterval;\n const recreate = () => {\n this.highlight.install();\n recreationInterval = this.injectedScript.utils.builtins.setTimeout(recreate, 500);\n };\n recreationInterval = this.injectedScript.utils.builtins.setTimeout(recreate, 500);\n this._listeners.push(() => this.injectedScript.utils.builtins.clearTimeout(recreationInterval));\n this.highlight.appendChild(createSvgElement(this.document, clipPaths_default));\n (_a = this.overlay) == null ? void 0 : _a.install();\n (_c = (_b = this._currentTool) == null ? void 0 : _b.install) == null ? void 0 : _c.call(_b);\n this.document.adoptedStyleSheets.push(this._stylesheet);\n }\n _switchCurrentTool() {\n var _a, _b, _c, _d, _e, _f;\n const newTool = this._tools[this.state.mode];\n if (newTool === this._currentTool)\n return;\n (_b = (_a = this._currentTool).uninstall) == null ? void 0 : _b.call(_a);\n this.clearHighlight();\n this._currentTool = newTool;\n (_d = (_c = this._currentTool).install) == null ? void 0 : _d.call(_c);\n const cursor = (_e = newTool.cursor) == null ? void 0 : _e.call(newTool);\n if (cursor)\n (_f = this.injectedScript.document.body) == null ? void 0 : _f.setAttribute("data-pw-cursor", cursor);\n }\n setUIState(state, delegate) {\n var _a;\n this._delegate = delegate;\n if (state.actionPoint && this.state.actionPoint && state.actionPoint.x === this.state.actionPoint.x && state.actionPoint.y === this.state.actionPoint.y) {\n } else if (!state.actionPoint && !this.state.actionPoint) {\n } else {\n if (state.actionPoint)\n this.highlight.showActionPoint(state.actionPoint.x, state.actionPoint.y);\n else\n this.highlight.hideActionPoint();\n }\n this.state = state;\n this.highlight.setLanguage(state.language);\n this._switchCurrentTool();\n (_a = this.overlay) == null ? void 0 : _a.setUIState(state);\n let highlight = "noop";\n if (state.actionSelector !== this._lastHighlightedSelector) {\n const entries = state.actionSelector ? entriesForSelectorHighlight(this.injectedScript, state.language, state.actionSelector, this.document) : null;\n highlight = (entries == null ? void 0 : entries.length) ? entries : "clear";\n this._lastHighlightedSelector = (entries == null ? void 0 : entries.length) ? state.actionSelector : void 0;\n }\n const ariaTemplateJSON = JSON.stringify(state.ariaTemplate);\n if (this._lastHighlightedAriaTemplateJSON !== ariaTemplateJSON) {\n const elements = state.ariaTemplate ? this.injectedScript.getAllElementsMatchingExpectAriaTemplate(this.document, state.ariaTemplate) : [];\n if (elements.length) {\n const color = elements.length > 1 ? HighlightColors.multiple : HighlightColors.single;\n highlight = elements.map((element) => ({ element, color }));\n this._lastHighlightedAriaTemplateJSON = ariaTemplateJSON;\n } else {\n if (!this._lastHighlightedSelector)\n highlight = "clear";\n this._lastHighlightedAriaTemplateJSON = "undefined";\n }\n }\n if (highlight === "clear")\n this.highlight.clearHighlight();\n else if (highlight !== "noop")\n this.highlight.updateHighlight(highlight);\n }\n clearHighlight() {\n this.updateHighlight(null, false);\n }\n _onClick(event) {\n var _a, _b, _c;\n if (!event.isTrusted)\n return;\n if ((_a = this.overlay) == null ? void 0 : _a.onClick(event))\n return;\n if (this._ignoreOverlayEvent(event))\n return;\n (_c = (_b = this._currentTool).onClick) == null ? void 0 : _c.call(_b, event);\n }\n _onDblClick(event) {\n var _a, _b, _c;\n if (!event.isTrusted)\n return;\n if ((_a = this.overlay) == null ? void 0 : _a.onDblClick(event))\n return;\n if (this._ignoreOverlayEvent(event))\n return;\n (_c = (_b = this._currentTool).onDblClick) == null ? void 0 : _c.call(_b, event);\n }\n _onContextMenu(event) {\n var _a, _b;\n if (!event.isTrusted)\n return;\n (_b = (_a = this._currentTool).onContextMenu) == null ? void 0 : _b.call(_a, event);\n }\n _onDragStart(event) {\n var _a, _b;\n if (!event.isTrusted)\n return;\n if (this._ignoreOverlayEvent(event))\n return;\n (_b = (_a = this._currentTool).onDragStart) == null ? void 0 : _b.call(_a, event);\n }\n _onPointerDown(event) {\n var _a, _b;\n if (!event.isTrusted)\n return;\n if (this._ignoreOverlayEvent(event))\n return;\n (_b = (_a = this._currentTool).onPointerDown) == null ? void 0 : _b.call(_a, event);\n }\n _onPointerUp(event) {\n var _a, _b;\n if (!event.isTrusted)\n return;\n if (this._ignoreOverlayEvent(event))\n return;\n (_b = (_a = this._currentTool).onPointerUp) == null ? void 0 : _b.call(_a, event);\n }\n _onMouseDown(event) {\n var _a, _b;\n if (!event.isTrusted)\n return;\n if (this._ignoreOverlayEvent(event))\n return;\n (_b = (_a = this._currentTool).onMouseDown) == null ? void 0 : _b.call(_a, event);\n }\n _onMouseUp(event) {\n var _a, _b, _c;\n if (!event.isTrusted)\n return;\n if ((_a = this.overlay) == null ? void 0 : _a.onMouseUp(event))\n return;\n if (this._ignoreOverlayEvent(event))\n return;\n (_c = (_b = this._currentTool).onMouseUp) == null ? void 0 : _c.call(_b, event);\n }\n _onMouseMove(event) {\n var _a, _b, _c;\n if (!event.isTrusted)\n return;\n if ((_a = this.overlay) == null ? void 0 : _a.onMouseMove(event))\n return;\n if (this._ignoreOverlayEvent(event))\n return;\n (_c = (_b = this._currentTool).onMouseMove) == null ? void 0 : _c.call(_b, event);\n }\n _onMouseEnter(event) {\n var _a, _b;\n if (!event.isTrusted)\n return;\n if (this._ignoreOverlayEvent(event))\n return;\n (_b = (_a = this._currentTool).onMouseEnter) == null ? void 0 : _b.call(_a, event);\n }\n _onMouseLeave(event) {\n var _a, _b;\n if (!event.isTrusted)\n return;\n if (this._ignoreOverlayEvent(event))\n return;\n (_b = (_a = this._currentTool).onMouseLeave) == null ? void 0 : _b.call(_a, event);\n }\n _onFocus(event) {\n var _a, _b;\n if (!event.isTrusted)\n return;\n if (this._ignoreOverlayEvent(event))\n return;\n (_b = (_a = this._currentTool).onFocus) == null ? void 0 : _b.call(_a, event);\n }\n _onScroll(event) {\n var _a, _b;\n if (!event.isTrusted)\n return;\n this._lastHighlightedSelector = void 0;\n this._lastHighlightedAriaTemplateJSON = "undefined";\n this.highlight.hideActionPoint();\n (_b = (_a = this._currentTool).onScroll) == null ? void 0 : _b.call(_a, event);\n }\n _onInput(event) {\n var _a, _b;\n if (this._ignoreOverlayEvent(event))\n return;\n (_b = (_a = this._currentTool).onInput) == null ? void 0 : _b.call(_a, event);\n }\n _onKeyDown(event) {\n var _a, _b;\n if (!event.isTrusted)\n return;\n if (this._ignoreOverlayEvent(event))\n return;\n (_b = (_a = this._currentTool).onKeyDown) == null ? void 0 : _b.call(_a, event);\n }\n _onKeyUp(event) {\n var _a, _b;\n if (!event.isTrusted)\n return;\n if (this._ignoreOverlayEvent(event))\n return;\n (_b = (_a = this._currentTool).onKeyUp) == null ? void 0 : _b.call(_a, event);\n }\n updateHighlight(model, userGesture) {\n this._lastHighlightedSelector = void 0;\n this._lastHighlightedAriaTemplateJSON = "undefined";\n this._updateHighlight(model, userGesture);\n }\n _updateHighlight(model, userGesture) {\n var _a, _b;\n let tooltipText = model == null ? void 0 : model.tooltipText;\n if (tooltipText === void 0 && (model == null ? void 0 : model.selector))\n tooltipText = this.injectedScript.utils.asLocator(this.state.language, model.selector);\n if (model)\n this.highlight.updateHighlight(model.elements.map((element) => ({ element, color: model.color, tooltipText })));\n else\n this.highlight.clearHighlight();\n if (userGesture)\n (_b = (_a = this._delegate).highlightUpdated) == null ? void 0 : _b.call(_a);\n }\n _ignoreOverlayEvent(event) {\n return event.composedPath().some((e) => {\n const nodeName = e.nodeName || "";\n return nodeName.toLowerCase() === "x-pw-glass";\n });\n }\n deepEventTarget(event) {\n var _a;\n for (const element of event.composedPath()) {\n if (!((_a = this.overlay) == null ? void 0 : _a.contains(element)))\n return element;\n }\n return event.composedPath()[0];\n }\n setMode(mode) {\n var _a, _b;\n void ((_b = (_a = this._delegate).setMode) == null ? void 0 : _b.call(_a, mode));\n }\n _captureAutoExpectSnapshot() {\n const documentElement = this.injectedScript.document.documentElement;\n return documentElement ? this.injectedScript.utils.generateAriaTree(documentElement, { mode: "autoexpect" }) : void 0;\n }\n async performAction(action) {\n var _a, _b, _c;\n const previousSnapshot = this._lastActionAutoexpectSnapshot;\n this._lastActionAutoexpectSnapshot = this._captureAutoExpectSnapshot();\n if (!isAssertAction(action) && this._lastActionAutoexpectSnapshot) {\n const element = this.injectedScript.utils.findNewElement(previousSnapshot == null ? void 0 : previousSnapshot.root, (_a = this._lastActionAutoexpectSnapshot) == null ? void 0 : _a.root);\n action.preconditionSelector = element ? this.injectedScript.generateSelector(element, { testIdAttributeName: this.state.testIdAttributeName }).selector : void 0;\n if (action.preconditionSelector === action.selector)\n action.preconditionSelector = void 0;\n }\n await ((_c = (_b = this._delegate).performAction) == null ? void 0 : _c.call(_b, action).catch(() => {\n }));\n }\n async recordAction(action) {\n var _a, _b;\n this._lastActionAutoexpectSnapshot = this._captureAutoExpectSnapshot();\n await ((_b = (_a = this._delegate).recordAction) == null ? void 0 : _b.call(_a, action));\n }\n setOverlayState(state) {\n var _a, _b;\n void ((_b = (_a = this._delegate).setOverlayState) == null ? void 0 : _b.call(_a, state));\n }\n elementPicked(selector, model) {\n var _a, _b;\n const ariaSnapshot = this.injectedScript.ariaSnapshot(model.elements[0], { mode: "default" });\n void ((_b = (_a = this._delegate).elementPicked) == null ? void 0 : _b.call(_a, { selector, ariaSnapshot }));\n }\n};\nvar Dialog = class {\n constructor(recorder) {\n this._dialogElement = null;\n this._recorder = recorder;\n }\n isShowing() {\n return !!this._dialogElement;\n }\n show(options) {\n const acceptButton = this._recorder.document.createElement("x-pw-tool-item");\n acceptButton.title = "Accept";\n acceptButton.classList.add("accept");\n acceptButton.appendChild(this._recorder.document.createElement("x-div"));\n acceptButton.addEventListener("click", () => {\n var _a;\n return (_a = options.onCommit) == null ? void 0 : _a.call(options);\n });\n const cancelButton = this._recorder.document.createElement("x-pw-tool-item");\n cancelButton.title = "Close";\n cancelButton.classList.add("cancel");\n cancelButton.appendChild(this._recorder.document.createElement("x-div"));\n cancelButton.addEventListener("click", () => {\n var _a;\n this.close();\n (_a = options.onCancel) == null ? void 0 : _a.call(options);\n });\n this._dialogElement = this._recorder.document.createElement("x-pw-dialog");\n if (options.autosize)\n this._dialogElement.classList.add("autosize");\n this._keyboardListener = (event) => {\n var _a;\n if (event.key === "Escape") {\n this.close();\n (_a = options.onCancel) == null ? void 0 : _a.call(options);\n return;\n }\n if (options.onCommit && event.key === "Enter" && (event.ctrlKey || event.metaKey)) {\n if (this._dialogElement)\n options.onCommit();\n return;\n }\n };\n this._onGlassPaneClickHandler = (event) => {\n var _a;\n this.close();\n (_a = options.onCancel) == null ? void 0 : _a.call(options);\n };\n this._dialogElement.addEventListener("click", (event) => event.stopPropagation());\n const toolbarElement = this._recorder.document.createElement("x-pw-tools-list");\n const labelElement = this._recorder.document.createElement("label");\n labelElement.textContent = options.label;\n toolbarElement.appendChild(labelElement);\n toolbarElement.appendChild(this._recorder.document.createElement("x-spacer"));\n if (options.onCommit)\n toolbarElement.appendChild(acceptButton);\n toolbarElement.appendChild(cancelButton);\n this._dialogElement.appendChild(toolbarElement);\n const bodyElement = this._recorder.document.createElement("x-pw-dialog-body");\n bodyElement.appendChild(options.body);\n this._dialogElement.appendChild(bodyElement);\n this._recorder.highlight.appendChild(this._dialogElement);\n this._recorder.highlight.onGlassPaneClick(this._onGlassPaneClickHandler);\n this._recorder.document.addEventListener("keydown", this._keyboardListener, true);\n return this._dialogElement;\n }\n moveTo(top, left) {\n if (!this._dialogElement)\n return;\n this._dialogElement.style.top = top + "px";\n this._dialogElement.style.left = left + "px";\n }\n close() {\n if (!this._dialogElement)\n return;\n this._dialogElement.remove();\n this._recorder.highlight.offGlassPaneClick(this._onGlassPaneClickHandler);\n this._recorder.document.removeEventListener("keydown", this._keyboardListener);\n this._dialogElement = null;\n }\n};\nfunction deepActiveElement(document) {\n let activeElement = document.activeElement;\n while (activeElement && activeElement.shadowRoot && activeElement.shadowRoot.activeElement)\n activeElement = activeElement.shadowRoot.activeElement;\n return activeElement;\n}\nfunction modifiersForEvent(event) {\n return (event.altKey ? 1 : 0) | (event.ctrlKey ? 2 : 0) | (event.metaKey ? 4 : 0) | (event.shiftKey ? 8 : 0);\n}\nfunction buttonForEvent(event) {\n switch (event.which) {\n case 1:\n return "left";\n case 2:\n return "middle";\n case 3:\n return "right";\n }\n return "left";\n}\nfunction positionForEvent(event) {\n const targetElement = event.target;\n if (targetElement.nodeName !== "CANVAS")\n return;\n return {\n x: event.offsetX,\n y: event.offsetY\n };\n}\nfunction consumeEvent(e) {\n e.preventDefault();\n e.stopPropagation();\n e.stopImmediatePropagation();\n}\nfunction asCheckbox(node) {\n if (!node || node.nodeName !== "INPUT")\n return null;\n const inputElement = node;\n return ["checkbox", "radio"].includes(inputElement.type) ? inputElement : null;\n}\nfunction isRangeInput(node) {\n if (!node || node.nodeName !== "INPUT")\n return false;\n const inputElement = node;\n return inputElement.type.toLowerCase() === "range";\n}\nfunction addEventListener(target, eventName, listener, useCapture) {\n target.addEventListener(eventName, listener, useCapture);\n const remove = () => {\n target.removeEventListener(eventName, listener, useCapture);\n };\n return remove;\n}\nfunction removeEventListeners(listeners) {\n for (const listener of listeners)\n listener();\n listeners.splice(0, listeners.length);\n}\nfunction entriesForSelectorHighlight(injectedScript, language, selector, ownerDocument) {\n try {\n const parsedSelector = injectedScript.parseSelector(selector);\n const elements = injectedScript.querySelectorAll(parsedSelector, ownerDocument);\n const color = elements.length > 1 ? HighlightColors.multiple : HighlightColors.single;\n const locator = injectedScript.utils.asLocator(language, selector);\n return elements.map((element, index) => {\n const suffix = elements.length > 1 ? ` [${index + 1} of ${elements.length}]` : "";\n return { element, color, tooltipText: locator + suffix };\n });\n } catch (e) {\n return [];\n }\n}\nfunction createSvgElement(doc, { tagName, attrs, children }) {\n const elem = doc.createElementNS("http://www.w3.org/2000/svg", tagName);\n if (attrs) {\n for (const [k, v] of Object.entries(attrs))\n elem.setAttribute(k, v);\n }\n if (children) {\n for (const c of children)\n elem.appendChild(createSvgElement(doc, c));\n }\n return elem;\n}\nfunction isAssertAction(action) {\n return action.name.startsWith("assert");\n}\n\n// packages/injected/src/recorder/pollingRecorder.ts\nvar PollingRecorder = class {\n constructor(injectedScript, options) {\n this._recorder = new Recorder(injectedScript, options);\n this._embedder = injectedScript.window;\n injectedScript.onGlobalListenersRemoved.add(() => this._recorder.installListeners());\n const refreshOverlay = () => {\n this._lastStateJSON = void 0;\n this._pollRecorderMode().catch((e) => console.log(e));\n };\n this._embedder.__pw_refreshOverlay = refreshOverlay;\n refreshOverlay();\n }\n async _pollRecorderMode() {\n const pollPeriod = 1e3;\n if (this._pollRecorderModeTimer)\n this._recorder.injectedScript.utils.builtins.clearTimeout(this._pollRecorderModeTimer);\n const state = await this._embedder.__pw_recorderState().catch(() => null);\n if (!state) {\n this._pollRecorderModeTimer = this._recorder.injectedScript.utils.builtins.setTimeout(() => this._pollRecorderMode(), pollPeriod);\n return;\n }\n const stringifiedState = JSON.stringify(state);\n if (this._lastStateJSON !== stringifiedState) {\n this._lastStateJSON = stringifiedState;\n const win = this._recorder.document.defaultView;\n if (win.top !== win) {\n state.actionPoint = void 0;\n }\n this._recorder.setUIState(state, this);\n }\n this._pollRecorderModeTimer = this._recorder.injectedScript.utils.builtins.setTimeout(() => this._pollRecorderMode(), pollPeriod);\n }\n async performAction(action) {\n await this._embedder.__pw_recorderPerformAction(action);\n }\n async recordAction(action) {\n await this._embedder.__pw_recorderRecordAction(action);\n }\n async elementPicked(elementInfo) {\n await this._embedder.__pw_recorderElementPicked(elementInfo);\n }\n async setMode(mode) {\n await this._embedder.__pw_recorderSetMode(mode);\n }\n async setOverlayState(state) {\n await this._embedder.__pw_recorderSetOverlayState(state);\n }\n};\nvar pollingRecorder_default = PollingRecorder;\n';
}
});
// packages/playwright-core/src/server/recorder/recorderRunner.ts
async function performAction(progress2, pageAliases, actionInContext) {
const mainFrame = mainFrameForAction(pageAliases, actionInContext);
return await performActionImpl(progress2, mainFrame, actionInContext);
}
async function performActionImpl(progress2, mainFrame, actionInContext) {
const { action } = actionInContext;
if (action.name === "navigate") {
await mainFrame.goto(progress2, action.url);
return;
}
if (action.name === "openPage")
throw Error("Not reached");
if (action.name === "closePage") {
await mainFrame._page.close(progress2);
return;
}
const selector = buildFullSelector(actionInContext.frame.framePath, action.selector);
if (action.name === "click") {
const options2 = toClickOptions(action);
await mainFrame.click(progress2, selector, { ...options2, strict: true });
return;
}
if (action.name === "hover") {
await mainFrame.hover(progress2, selector, { position: action.position, strict: true });
return;
}
if (action.name === "press") {
const modifiers = toKeyboardModifiers(action.modifiers);
const shortcut = [...modifiers, action.key].join("+");
await mainFrame.press(progress2, selector, shortcut, { strict: true });
return;
}
if (action.name === "fill") {
await mainFrame.fill(progress2, selector, action.text, { strict: true });
return;
}
if (action.name === "setInputFiles") {
await mainFrame.setInputFiles(progress2, selector, { selector, payloads: [], strict: true });
return;
}
if (action.name === "check") {
await mainFrame.check(progress2, selector, { strict: true });
return;
}
if (action.name === "uncheck") {
await mainFrame.uncheck(progress2, selector, { strict: true });
return;
}
if (action.name === "select") {
const values = action.options.map((value2) => ({ value: value2 }));
await mainFrame.selectOption(progress2, selector, [], values, { strict: true });
return;
}
if (action.name === "assertChecked") {
await mainFrame.expect(progress2, selector, {
selector,
expression: "to.be.checked",
expectedValue: { checked: action.checked },
isNot: !action.checked
});
return;
}
if (action.name === "assertText") {
await mainFrame.expect(progress2, selector, {
selector,
expression: "to.have.text",
expectedText: serializeExpectedTextValues([action.text], { matchSubstring: true, normalizeWhiteSpace: true }),
isNot: false
});
return;
}
if (action.name === "assertValue") {
await mainFrame.expect(progress2, selector, {
selector,
expression: "to.have.value",
expectedValue: action.value,
isNot: false
});
return;
}
if (action.name === "assertVisible") {
await mainFrame.expect(progress2, selector, {
selector,
expression: "to.be.visible",
isNot: false
});
return;
}
throw new Error("Internal error: unexpected action " + action.name);
}
function toClickOptions(action) {
const modifiers = toKeyboardModifiers(action.modifiers);
const options2 = {};
if (action.button !== "left")
options2.button = action.button;
if (modifiers.length)
options2.modifiers = modifiers;
if (action.clickCount > 1)
options2.clickCount = action.clickCount;
if (action.position)
options2.position = action.position;
return options2;
}
var init_recorderRunner = __esm({
"packages/playwright-core/src/server/recorder/recorderRunner.ts"() {
"use strict";
init_expectUtils();
init_language();
init_recorderUtils();
}
});
// packages/playwright-core/src/server/recorder.ts
function isScreenshotCommand(metadata) {
return metadata.method.toLowerCase().includes("screenshot");
}
function languageForFile(file) {
if (file.endsWith(".py"))
return "python";
if (file.endsWith(".java"))
return "java";
if (file.endsWith(".cs"))
return "csharp";
return "javascript";
}
var import_events6, import_fs22, recorderSymbol, RecorderEvent, Recorder;
var init_recorder = __esm({
"packages/playwright-core/src/server/recorder.ts"() {
"use strict";
import_events6 = __toESM(require("events"));
import_fs22 = __toESM(require("fs"));
init_locatorParser();
init_selectorParser();
init_manualPromise();
init_debug();
init_eventsHelper();
init_time();
init_browserContext();
init_debugger();
init_recorderUtils();
init_progress();
init_recorderSignalProcessor();
init_pollingRecorderSource();
init_frames();
init_page();
init_recorderRunner();
recorderSymbol = Symbol("recorderSymbol");
RecorderEvent = {
PausedStateChanged: "pausedStateChanged",
ModeChanged: "modeChanged",
ElementPicked: "elementPicked",
CallLogsUpdated: "callLogsUpdated",
UserSourcesChanged: "userSourcesChanged",
ActionAdded: "actionAdded",
SignalAdded: "signalAdded",
PageNavigated: "pageNavigated",
ContextClosed: "contextClosed"
};
Recorder = class _Recorder extends import_events6.default {
constructor(context2, params2) {
super();
this._highlightedElement = {};
this._overlayState = { offsetX: 0 };
this._currentCallsMetadata = /* @__PURE__ */ new Map();
this._userSources = /* @__PURE__ */ new Map();
this._omitCallTracking = false;
this._currentLanguage = "javascript";
this._pageAliases = /* @__PURE__ */ new Map();
this._lastPopupOrdinal = 0;
this._lastDialogOrdinal = -1;
this._lastDownloadOrdinal = -1;
this._listeners = [];
this._enabled = false;
this._callLogs = [];
this._context = context2;
this._params = params2;
this._mode = params2.mode || "none";
this._recorderMode = params2.recorderMode ?? "default";
this.handleSIGINT = params2.handleSIGINT;
this._signalProcessor = new RecorderSignalProcessor({
addAction: (actionInContext) => {
if (this._enabled)
this.emit(RecorderEvent.ActionAdded, actionInContext);
},
addSignal: (signal) => {
if (this._enabled)
this.emit(RecorderEvent.SignalAdded, signal);
}
});
context2.on(BrowserContext.Events.BeforeClose, () => {
this.emit(RecorderEvent.ContextClosed);
});
this._listeners.push(eventsHelper.addEventListener(process, "exit", () => {
this.emit(RecorderEvent.ContextClosed);
}));
this._setEnabled(params2.mode === "recording");
this._omitCallTracking = !!params2.omitCallTracking;
this._debugger = context2.debugger();
context2.instrumentation.addListener(this, context2);
if (isUnderTest()) {
this._overlayState.offsetX = 200;
}
}
static forContext(context2, params2) {
let recorderPromise = context2[recorderSymbol];
if (!recorderPromise) {
recorderPromise = _Recorder._create(context2, params2);
context2[recorderSymbol] = recorderPromise;
}
return recorderPromise;
}
static async existingForContext(context2) {
const recorderPromise = context2[recorderSymbol];
return await recorderPromise;
}
static async _create(context2, params2 = {}) {
const recorder = new _Recorder(context2, params2);
await recorder._install();
return recorder;
}
async _install() {
this.emit(RecorderEvent.ModeChanged, this._mode);
this.emit(RecorderEvent.PausedStateChanged, this._debugger.isPaused());
this._context.once(BrowserContext.Events.Close, () => {
eventsHelper.removeEventListeners(this._listeners);
this._context.instrumentation.removeListener(this);
this.emit(RecorderEvent.ContextClosed);
});
const controller = new ProgressController();
await controller.run(async (progress2) => {
await this._context.exposeBinding(progress2, "__pw_recorderState", async (source8) => {
let actionSelector;
let actionPoint;
const hasActiveScreenshotCommand = [...this._currentCallsMetadata.keys()].some(isScreenshotCommand);
if (!hasActiveScreenshotCommand) {
actionSelector = await this._scopeHighlightedSelectorToFrame(source8.frame);
for (const [metadata, sdkObject] of this._currentCallsMetadata) {
if (source8.page === sdkObject.attribution.page) {
actionPoint = metadata.point || actionPoint;
actionSelector = actionSelector || metadata.params.selector;
}
}
}
let mode = this._mode;
if (this._pickLocatorPage && source8.page !== this._pickLocatorPage)
mode = "none";
const uiState = {
mode,
actionPoint,
actionSelector,
ariaTemplate: this._highlightedElement.ariaTemplate,
language: this._currentLanguage,
testIdAttributeName: this._testIdAttributeName(),
overlay: this._overlayState
};
return uiState;
});
await this._context.exposeBinding(progress2, "__pw_recorderElementPicked", async ({ frame }, elementInfo) => {
const selectorChain = await generateFrameSelector(progress2, frame);
this.emit(RecorderEvent.ElementPicked, { selector: buildFullSelector(selectorChain, elementInfo.selector), ariaSnapshot: elementInfo.ariaSnapshot }, true);
});
await this._context.exposeBinding(progress2, "__pw_recorderSetMode", async ({ frame }, mode) => {
if (frame.parentFrame())
return;
await this.setMode(mode);
});
await this._context.exposeBinding(progress2, "__pw_recorderSetOverlayState", async ({ frame }, state) => {
if (frame.parentFrame())
return;
this._overlayState = state;
});
await this._context.exposeBinding(progress2, "__pw_resume", () => {
this._debugger.resume();
});
this._context.on(BrowserContext.Events.Page, (page) => this._onPage(page));
for (const page of this._context.pages())
this._onPage(page);
this._context.dialogManager.addDialogHandler((dialog) => {
this._onDialog(dialog.page());
return false;
});
await this._context.exposeBinding(
progress2,
"__pw_recorderPerformAction",
(source8, action) => this._performAction(progress2, source8.frame, action)
);
await this._context.exposeBinding(
progress2,
"__pw_recorderRecordAction",
(source8, action) => this._recordAction(progress2, source8.frame, action)
);
await progress2.race(this._context.extendInjectedScript(source5, { recorderMode: this._recorderMode, hideToolbar: !!this._params.hideToolbar }));
});
if (this._debugger.isPaused())
this._pausedStateChanged();
this._debugger.on(Debugger.Events.PausedStateChanged, () => this._pausedStateChanged());
}
_pausedStateChanged() {
const pausedDetails = this._debugger.pausedDetails();
if (pausedDetails && !this._currentCallsMetadata.has(pausedDetails.metadata))
this.onBeforeCall(pausedDetails.sdkObject, pausedDetails.metadata);
this.emit(RecorderEvent.PausedStateChanged, this._debugger.isPaused());
this._updateUserSources();
this._updateCallLog([...this._currentCallsMetadata.keys()]);
}
mode() {
return this._mode;
}
async setMode(mode) {
if (this._mode === mode)
return;
this._highlightedElement = {};
this._mode = mode;
this.emit(RecorderEvent.ModeChanged, this._mode);
this._setEnabled(this._isRecording());
this._debugger.setMuted(this._isRecording());
if (this._mode !== "none" && this._mode !== "standby") {
let pageToFocus = this._pickLocatorPage;
if (!pageToFocus && this._context.pages().length === 1)
pageToFocus = this._context.pages()[0];
pageToFocus?.bringToFront(nullProgress).catch(() => {
});
}
await this._refreshOverlay();
}
async pickLocator(progress2, page) {
if (this._mode !== "none")
await progress2.race(this.setMode("none"));
const selectorPromise = new ManualPromise();
let recorderChangedState = false;
const onElementPicked = (elementInfo) => {
selectorPromise.resolve(elementInfo.selector);
};
const onModeChanged = () => {
if (this._mode === "inspecting")
return;
recorderChangedState = true;
selectorPromise.reject(new Error("Locator picking was cancelled"));
};
const listeners = [
eventsHelper.addEventListener(this, RecorderEvent.ElementPicked, onElementPicked),
eventsHelper.addEventListener(this, RecorderEvent.ModeChanged, onModeChanged)
];
try {
const doPickLocator = async () => {
selectorPromise.catch(() => {
});
this._pickLocatorPage = page;
await this.setMode("inspecting");
return await selectorPromise;
};
return await progress2.race(doPickLocator());
} finally {
eventsHelper.removeEventListeners(listeners);
this._pickLocatorPage = void 0;
if (!recorderChangedState)
await progress2.race(this.setMode("none"));
}
}
url() {
const page = this._context.pages()[0];
return page?.mainFrame().url();
}
async setHighlightedSelector(selector) {
this._highlightedElement = { selector: locatorOrSelectorAsSelector(this._currentLanguage, selector, this._context.selectors().testIdAttributeName()) };
await this._refreshOverlay();
}
async setHighlightedAriaTemplate(ariaTemplate) {
this._highlightedElement = { ariaTemplate };
await this._refreshOverlay();
}
step() {
this._debugger.setPauseAt({ next: true });
this._debugger.resume();
}
async setLanguage(language) {
this._currentLanguage = language;
await this._refreshOverlay();
}
resume() {
this._debugger.resume();
}
pause() {
this._debugger.setPauseAt({ next: true });
}
paused() {
return this._debugger.isPaused();
}
close() {
this._debugger.resume();
}
async hideHighlightedSelector() {
this._highlightedElement = {};
await this._refreshOverlay();
}
pausedSourceId() {
const pausedDetails = this._debugger.pausedDetails();
if (pausedDetails?.metadata.location) {
const source8 = this._userSources.get(pausedDetails.metadata.location.file);
if (source8)
return source8.id;
}
}
userSources() {
return [...this._userSources.values()];
}
callLog() {
return this._callLogs;
}
async _scopeHighlightedSelectorToFrame(frame) {
if (!this._highlightedElement.selector)
return;
try {
const mainFrame = frame._page.mainFrame();
const resolved = await mainFrame.selectors.resolveFrameForSelector(this._highlightedElement.selector);
if (!resolved)
return "";
if (resolved?.frame === mainFrame)
return stringifySelector(resolved.info.parsed);
if (resolved?.frame === frame)
return stringifySelector(resolved.info.parsed);
return "";
} catch {
return "";
}
}
async _refreshOverlay() {
await Promise.all(this._context.pages().map(
(page) => page.safeNonStallingEvaluateInAllFrames("window.__pw_refreshOverlay()", "main")
));
}
async onBeforeCall(sdkObject, metadata) {
if (this._omitCallTracking || this._isRecording())
return;
this._currentCallsMetadata.set(metadata, sdkObject);
this._updateUserSources();
this._updateCallLog([metadata]);
if (isScreenshotCommand(metadata))
this.hideHighlightedSelector();
else if (metadata.params && metadata.params.selector)
this._highlightedElement = { selector: metadata.params.selector };
}
async onAfterCall(sdkObject, metadata) {
if (this._omitCallTracking || this._isRecording())
return;
if (!metadata.error)
this._currentCallsMetadata.delete(metadata);
this._updateUserSources();
this._updateCallLog([metadata]);
}
_updateUserSources() {
for (const source8 of this._userSources.values()) {
source8.highlight = [];
source8.revealLine = void 0;
}
for (const metadata of this._currentCallsMetadata.keys()) {
if (!metadata.location)
continue;
const { file, line } = metadata.location;
let source8 = this._userSources.get(file);
if (!source8) {
source8 = { isRecorded: false, label: file, id: file, text: this._readSource(file), highlight: [], language: languageForFile(file) };
this._userSources.set(file, source8);
}
if (line) {
const paused = this._debugger.isPaused(metadata);
source8.highlight.push({ line, type: metadata.error ? "error" : paused ? "paused" : "running" });
source8.revealLine = line;
}
}
this.emit(RecorderEvent.UserSourcesChanged, this.userSources(), this.pausedSourceId());
}
async onCallLog(sdkObject, metadata, logName, message) {
this._updateCallLog([metadata]);
}
_updateCallLog(metadatas) {
if (this._isRecording())
return;
const logs = [];
for (const metadata of metadatas) {
if (!metadata.method || metadata.internal)
continue;
let status = "done";
if (this._currentCallsMetadata.has(metadata))
status = "in-progress";
if (this._debugger.isPaused(metadata))
status = "paused";
logs.push(metadataToCallLog(metadata, status));
}
this._callLogs = logs;
this.emit(RecorderEvent.CallLogsUpdated, logs);
}
_isRecording() {
return ["recording", "assertingText", "assertingVisibility", "assertingValue", "assertingSnapshot"].includes(this._mode);
}
_readSource(fileName) {
try {
return import_fs22.default.readFileSync(fileName, "utf-8");
} catch (e) {
return "// No source available";
}
}
_setEnabled(enabled) {
this._enabled = enabled;
}
async _onPage(page) {
const frame = page.mainFrame();
page.on(Page.Events.Close, () => {
this._signalProcessor.addAction({
frame: this._describeMainFrame(page),
action: {
name: "closePage",
signals: []
},
startTime: monotonicTime()
});
this._pageAliases.delete(page);
this._filePrimaryURLChanged();
});
frame.on(Frame.Events.InternalNavigation, (event) => {
if (event.isPublic && !event.error) {
this._onFrameNavigated(frame, page);
this._filePrimaryURLChanged();
}
});
page.on(Page.Events.Download, () => this._onDownload(page));
const suffix = this._pageAliases.size ? String(++this._lastPopupOrdinal) : "";
const pageAlias = "page" + suffix;
this._pageAliases.set(page, pageAlias);
if (page.opener()) {
this._onPopup(page.opener(), page);
} else {
this._signalProcessor.addAction({
frame: this._describeMainFrame(page),
action: {
name: "openPage",
url: page.mainFrame().url(),
signals: []
},
startTime: monotonicTime()
});
}
this._filePrimaryURLChanged();
}
_filePrimaryURLChanged() {
const page = this._context.pages()[0];
this.emit(RecorderEvent.PageNavigated, page?.mainFrame().url());
}
clear() {
if (this._params.mode === "recording") {
for (const page of this._context.pages())
this._onFrameNavigated(page.mainFrame(), page);
}
}
_describeMainFrame(page) {
return {
pageGuid: page.guid,
pageAlias: this._pageAliases.get(page),
framePath: []
};
}
async _describeFrame(progress2, frame) {
return {
pageGuid: frame._page.guid,
pageAlias: this._pageAliases.get(frame._page),
framePath: await generateFrameSelector(progress2, frame)
};
}
_testIdAttributeName() {
return this._params.testIdAttributeName || this._context.selectors().testIdAttributeName() || "data-testid";
}
async _createActionInContext(progress2, frame, action) {
const frameDescription = await this._describeFrame(progress2, frame);
const actionInContext = {
frame: frameDescription,
action,
description: void 0,
startTime: monotonicTime()
};
return actionInContext;
}
async _performAction(progress2, frame, action) {
const actionInContext = await this._createActionInContext(progress2, frame, action);
this._signalProcessor.addAction(actionInContext);
if (actionInContext.action.name !== "openPage" && actionInContext.action.name !== "closePage")
await performAction(progress2, this._pageAliases, actionInContext);
actionInContext.endTime = monotonicTime();
}
async _recordAction(progress2, frame, action) {
const actionInContext = await this._createActionInContext(progress2, frame, action);
this._signalProcessor.addAction(actionInContext);
}
_onFrameNavigated(frame, page) {
const pageAlias = this._pageAliases.get(page);
this._signalProcessor.signal(pageAlias, frame, { name: "navigation", url: frame.url() });
}
_onPopup(page, popup) {
const pageAlias = this._pageAliases.get(page);
const popupAlias = this._pageAliases.get(popup);
this._signalProcessor.signal(pageAlias, page.mainFrame(), { name: "popup", popupAlias });
}
_onDownload(page) {
const pageAlias = this._pageAliases.get(page);
++this._lastDownloadOrdinal;
this._signalProcessor.signal(pageAlias, page.mainFrame(), { name: "download", downloadAlias: this._lastDownloadOrdinal ? String(this._lastDownloadOrdinal) : "" });
}
_onDialog(page) {
const pageAlias = this._pageAliases.get(page);
++this._lastDialogOrdinal;
this._signalProcessor.signal(pageAlias, page.mainFrame(), { name: "dialog", dialogAlias: this._lastDialogOrdinal ? String(this._lastDialogOrdinal) : "" });
}
};
}
});
// packages/utils/pipeTransport.ts
var PipeTransport;
var init_pipeTransport = __esm({
"packages/utils/pipeTransport.ts"() {
"use strict";
init_task();
PipeTransport = class {
constructor(pipeWrite, pipeRead, closeable, endian = "le") {
this._data = Buffer.from([]);
this._waitForNextTask = makeWaitForNextTask();
this._closed = false;
this._bytesLeft = 0;
this._pipeWrite = pipeWrite;
this._endian = endian;
this._closeableStream = closeable;
pipeRead.on("data", (buffer) => this._dispatch(buffer));
pipeRead.on("close", () => {
this._closed = true;
if (this.onclose)
this.onclose();
});
this.onmessage = void 0;
this.onclose = void 0;
}
send(message) {
if (this._closed)
throw new Error("Pipe has been closed");
const data = Buffer.from(message, "utf-8");
const dataLength = Buffer.alloc(4);
if (this._endian === "be")
dataLength.writeUInt32BE(data.length, 0);
else
dataLength.writeUInt32LE(data.length, 0);
this._pipeWrite.write(dataLength);
this._pipeWrite.write(data);
}
close() {
this._closeableStream.close();
}
_dispatch(buffer) {
this._data = Buffer.concat([this._data, buffer]);
while (true) {
if (!this._bytesLeft && this._data.length < 4) {
break;
}
if (!this._bytesLeft) {
this._bytesLeft = this._endian === "be" ? this._data.readUInt32BE(0) : this._data.readUInt32LE(0);
this._data = this._data.slice(4);
}
if (!this._bytesLeft || this._data.length < this._bytesLeft) {
break;
}
const message = this._data.slice(0, this._bytesLeft);
this._data = this._data.slice(this._bytesLeft);
this._bytesLeft = 0;
this._waitForNextTask(() => {
if (this.onmessage)
this.onmessage(message.toString("utf-8"));
});
}
}
};
}
});
// packages/playwright-core/src/server/chromium/chromiumSwitches.ts
var disabledFeatures, chromiumSwitches;
var init_chromiumSwitches = __esm({
"packages/playwright-core/src/server/chromium/chromiumSwitches.ts"() {
"use strict";
disabledFeatures = [
// See https://github.com/microsoft/playwright/issues/14047
"AvoidUnnecessaryBeforeUnloadCheckSync",
// See https://github.com/microsoft/playwright/issues/38568
"BoundaryEventDispatchTracksNodeRemoval",
"DestroyProfileOnBrowserClose",
// See https://github.com/microsoft/playwright/pull/13854
"DialMediaRouteProvider",
"GlobalMediaControls",
// See https://github.com/microsoft/playwright/pull/27605
"HttpsUpgrades",
// Hides the Lens feature in the URL address bar. Its not working in unofficial builds.
"LensOverlay",
// See https://github.com/microsoft/playwright/pull/8162
"MediaRouter",
// See https://github.com/microsoft/playwright/issues/28023
"PaintHolding",
// See https://github.com/microsoft/playwright/issues/32230
"ThirdPartyStoragePartitioning",
// See https://github.com/microsoft/playwright/issues/16126
"Translate",
// See https://issues.chromium.org/u/1/issues/435410220
"AutoDeElevate",
// See https://github.com/microsoft/playwright/issues/37714
"RenderDocument",
// Prevents downloading optimization hints on startup.
"OptimizationHints",
// Disables forced sign-in in Edge.
"msForceBrowserSignIn",
// Disables updating the preferred version in LaunchServices preferences on mac.
"msEdgeUpdateLaunchServicesPreferredVersion"
].filter(Boolean);
chromiumSwitches = (options2) => [
"--disable-field-trial-config",
// https://source.chromium.org/chromium/chromium/src/+/main:testing/variations/README.md
"--disable-background-networking",
"--disable-background-timer-throttling",
"--disable-backgrounding-occluded-windows",
"--disable-back-forward-cache",
// Avoids surprises like main request not being intercepted during page.goBack().
"--disable-breakpad",
"--disable-client-side-phishing-detection",
"--disable-component-extensions-with-background-pages",
"--disable-component-update",
// Avoids unneeded network activity after startup.
"--no-default-browser-check",
"--disable-default-apps",
"--disable-dev-shm-usage",
"--disable-edgeupdater",
// Disables Edge-specific updater on mac.
"--disable-extensions",
"--disable-features=" + disabledFeatures.join(","),
process.env.PLAYWRIGHT_LEGACY_SCREENSHOT ? "" : "--enable-features=CDPScreenshotNewSurface",
"--allow-pre-commit-input",
"--disable-hang-monitor",
"--disable-ipc-flooding-protection",
"--disable-popup-blocking",
"--disable-prompt-on-repost",
"--disable-renderer-backgrounding",
"--force-color-profile=srgb",
"--metrics-recording-only",
"--no-first-run",
"--password-store=basic",
"--use-mock-keychain",
// See https://chromium-review.googlesource.com/c/chromium/src/+/2436773
"--no-service-autorun",
"--export-tagged-pdf",
// https://chromium-review.googlesource.com/c/chromium/src/+/4853540
"--disable-search-engine-choice-screen",
// https://issues.chromium.org/41491762
"--unsafely-disable-devtools-self-xss-warnings",
// Edge can potentially restart on Windows (msRelaunchNoCompatLayer) which looses its file descriptors (stdout/stderr) and CDP (3/4). Disable until fixed upstream.
"--edge-skip-compat-layer-relaunch",
// This disables Chrome for Testing infobar that is visible in the persistent context.
// The switch is ignored everywhere else, including Chromium/Chrome/Edge.
"--disable-infobars",
// Less annoying popups.
"--disable-search-engine-choice-screen",
// Prevents the "three dots" menu crash in IdentityManager::HasPrimaryAccount for ephemeral contexts.
options2?.android ? "" : "--disable-sync"
].filter(Boolean);
}
});
// packages/playwright-core/src/server/chromium/crConnection.ts
var ConnectionEvents, kBrowserCloseMessageId, CRConnection, CRSession, CDPSession;
var init_crConnection = __esm({
"packages/playwright-core/src/server/chromium/crConnection.ts"() {
"use strict";
init_debugLogger();
init_assert();
init_eventsHelper();
init_helper();
init_protocolError();
init_instrumentation();
ConnectionEvents = {
Disconnected: Symbol("ConnectionEvents.Disconnected")
};
kBrowserCloseMessageId = -9999;
CRConnection = class extends SdkObject {
constructor(parent, transport, protocolLogger, browserLogsCollector) {
super(parent, "cr-connection");
this._lastId = 0;
this._sessions = /* @__PURE__ */ new Map();
this._closed = false;
this.setMaxListeners(0);
this._transport = transport;
this._protocolLogger = protocolLogger;
this._browserLogsCollector = browserLogsCollector;
this.rootSession = new CRSession(this, null, "");
this._sessions.set("", this.rootSession);
this._transport.onmessage = this._onMessage.bind(this);
this._transport.onclose = this._onClose.bind(this);
}
_rawSend(sessionId, method, params2) {
const id = ++this._lastId;
const message = { id, method, params: params2 };
if (sessionId)
message.sessionId = sessionId;
this._protocolLogger("send", message);
this._transport.send(message);
return id;
}
async _onMessage(message) {
this._protocolLogger("receive", message);
if (message.id === kBrowserCloseMessageId)
return;
const session2 = this._sessions.get(message.sessionId || "");
if (session2)
session2._onMessage(message);
}
_onClose(reason) {
this._closed = true;
this._transport.onmessage = void 0;
this._transport.onclose = void 0;
this._browserDisconnectedLogs = helper.formatBrowserLogs(this._browserLogsCollector.recentLogs(), reason);
this.rootSession.dispose();
Promise.resolve().then(() => this.emit(ConnectionEvents.Disconnected));
}
close() {
if (!this._closed)
this._transport.close();
}
async createBrowserSession() {
const { sessionId } = await this.rootSession.send("Target.attachToBrowserTarget");
return new CDPSession(this.rootSession, sessionId);
}
};
CRSession = class _CRSession extends SdkObject {
constructor(connection, parentSession, sessionId, eventListener) {
super(connection, "cr-session");
this._callbacks = /* @__PURE__ */ new Map();
this._crashed = false;
this._closed = false;
this.setMaxListeners(0);
this._connection = connection;
this._parentSession = parentSession;
this._sessionId = sessionId;
this._eventListener = eventListener;
}
_markAsCrashed() {
this._crashed = true;
}
createChildSession(sessionId, eventListener) {
const session2 = new _CRSession(this._connection, this, sessionId, eventListener);
this._connection._sessions.set(sessionId, session2);
return session2;
}
async send(method, params2) {
if (this._crashed || this._closed || this._connection._closed || this._connection._browserDisconnectedLogs)
throw new ProtocolError(this._crashed ? "crashed" : "closed", void 0, this._connection._browserDisconnectedLogs);
const id = this._connection._rawSend(this._sessionId, method, params2);
return new Promise((resolve, reject) => {
this._callbacks.set(id, { resolve, reject, error: new ProtocolError("error", method) });
});
}
_sendMayFail(method, params2) {
return this.send(method, params2).catch((error) => debugLogger.log("error", error));
}
_onMessage(object) {
if (object.id && this._callbacks.has(object.id)) {
const callback = this._callbacks.get(object.id);
this._callbacks.delete(object.id);
if (object.error) {
callback.error.setMessage(object.error.message);
callback.reject(callback.error);
} else {
callback.resolve(object.result);
}
} else if (object.id && object.error?.code === -32001) {
} else {
assert(!object.id, object?.error?.message || void 0);
Promise.resolve().then(() => {
if (this._eventListener)
this._eventListener(object.method, object.params);
this.emit(object.method, object.params);
});
}
}
async detach() {
if (this._closed)
throw new Error(`Session already detached. Most likely the page has been closed.`);
if (!this._parentSession)
throw new Error("Root session cannot be closed");
await this._sendMayFail("Runtime.runIfWaitingForDebugger");
await this._parentSession.send("Target.detachFromTarget", { sessionId: this._sessionId });
this.dispose();
}
dispose() {
this._closed = true;
this._connection._sessions.delete(this._sessionId);
for (const callback of this._callbacks.values()) {
callback.error.setMessage(`Internal server error, session closed.`);
callback.error.type = this._crashed ? "crashed" : "closed";
callback.error.logs = this._connection._browserDisconnectedLogs;
callback.reject(callback.error);
}
this._callbacks.clear();
}
};
CDPSession = class _CDPSession extends SdkObject {
constructor(parentSession, sessionId) {
super(parentSession, "cdp-session");
this._listeners = [];
this._session = parentSession.createChildSession(sessionId, (method, params2) => this.emit(_CDPSession.Events.Event, { method, params: params2 }));
this._listeners = [eventsHelper.addEventListener(parentSession, "Target.detachedFromTarget", (event) => {
if (event.sessionId === sessionId)
this._onClose();
})];
}
static {
this.Events = {
Event: "event",
Closed: "close"
};
}
async send(progress2, method, params2) {
return await progress2.race(this._send(method, params2));
}
async detach(progress2) {
return await progress2.race(this._session.detach());
}
async _send(method, params2) {
return await this._session.send(method, params2);
}
async attachToTarget(targetId) {
const { sessionId } = await this._send("Target.attachToTarget", { targetId, flatten: true });
return new _CDPSession(this._session, sessionId);
}
_onClose() {
eventsHelper.removeEventListeners(this._listeners);
this._session.dispose();
this.emit(_CDPSession.Events.Closed);
}
};
}
});
// packages/playwright-core/src/server/chromium/crCoverage.ts
function convertToDisjointRanges(nestedRanges) {
const points = [];
for (const range of nestedRanges) {
points.push({ offset: range.startOffset, type: 0, range });
points.push({ offset: range.endOffset, type: 1, range });
}
points.sort((a, b) => {
if (a.offset !== b.offset)
return a.offset - b.offset;
if (a.type !== b.type)
return b.type - a.type;
const aLength = a.range.endOffset - a.range.startOffset;
const bLength = b.range.endOffset - b.range.startOffset;
if (a.type === 0)
return bLength - aLength;
return aLength - bLength;
});
const hitCountStack = [];
const results = [];
let lastOffset = 0;
for (const point of points) {
if (hitCountStack.length && lastOffset < point.offset && hitCountStack[hitCountStack.length - 1] > 0) {
const lastResult = results.length ? results[results.length - 1] : null;
if (lastResult && lastResult.end === lastOffset)
lastResult.end = point.offset;
else
results.push({ start: lastOffset, end: point.offset });
}
lastOffset = point.offset;
if (point.type === 0)
hitCountStack.push(point.range.count);
else
hitCountStack.pop();
}
return results.filter((range) => range.end - range.start > 1);
}
var CRCoverage, JSCoverage, CSSCoverage;
var init_crCoverage = __esm({
"packages/playwright-core/src/server/chromium/crCoverage.ts"() {
"use strict";
init_eventsHelper();
init_assert();
init_progress();
CRCoverage = class {
constructor(client) {
this._jsCoverage = new JSCoverage(client);
this._cssCoverage = new CSSCoverage(client);
}
async startJSCoverage(progress2, options2) {
await raceUncancellableOperationWithCleanup(progress2, () => this._jsCoverage.start(options2), () => this._jsCoverage.stop());
}
async stopJSCoverage() {
return await this._jsCoverage.stop();
}
async startCSSCoverage(progress2, options2) {
await raceUncancellableOperationWithCleanup(progress2, () => this._cssCoverage.start(options2), () => this._cssCoverage.stop());
}
async stopCSSCoverage() {
return await this._cssCoverage.stop();
}
};
JSCoverage = class {
constructor(client) {
this._reportAnonymousScripts = false;
this._client = client;
this._enabled = false;
this._scriptIds = /* @__PURE__ */ new Set();
this._scriptSources = /* @__PURE__ */ new Map();
this._eventListeners = [];
this._resetOnNavigation = false;
}
async start(options2) {
assert(!this._enabled, "JSCoverage is already enabled");
const {
resetOnNavigation = true,
reportAnonymousScripts = false
} = options2;
this._resetOnNavigation = resetOnNavigation;
this._reportAnonymousScripts = reportAnonymousScripts;
this._enabled = true;
this._scriptIds.clear();
this._scriptSources.clear();
this._eventListeners = [
eventsHelper.addEventListener(this._client, "Debugger.scriptParsed", this._onScriptParsed.bind(this)),
eventsHelper.addEventListener(this._client, "Runtime.executionContextsCleared", this._onExecutionContextsCleared.bind(this)),
eventsHelper.addEventListener(this._client, "Debugger.paused", this._onDebuggerPaused.bind(this))
];
await Promise.all([
this._client.send("Profiler.enable"),
this._client.send("Profiler.startPreciseCoverage", { callCount: true, detailed: true }),
this._client.send("Debugger.enable"),
this._client.send("Debugger.setSkipAllPauses", { skip: true })
]);
}
_onDebuggerPaused() {
this._client.send("Debugger.resume");
}
_onExecutionContextsCleared() {
if (!this._resetOnNavigation)
return;
this._scriptIds.clear();
this._scriptSources.clear();
}
async _onScriptParsed(event) {
this._scriptIds.add(event.scriptId);
if (!event.url && !this._reportAnonymousScripts)
return;
const response2 = await this._client._sendMayFail("Debugger.getScriptSource", { scriptId: event.scriptId });
if (response2)
this._scriptSources.set(event.scriptId, response2.scriptSource);
}
async stop() {
if (!this._enabled)
return { entries: [] };
const [profileResponse] = await Promise.all([
this._client.send("Profiler.takePreciseCoverage"),
this._client.send("Profiler.stopPreciseCoverage"),
this._client.send("Profiler.disable"),
this._client.send("Debugger.disable")
]);
eventsHelper.removeEventListeners(this._eventListeners);
this._enabled = false;
const coverage = { entries: [] };
for (const entry of profileResponse.result) {
if (!this._scriptIds.has(entry.scriptId))
continue;
if (!entry.url && !this._reportAnonymousScripts)
continue;
const source8 = this._scriptSources.get(entry.scriptId);
if (source8)
coverage.entries.push({ ...entry, source: source8 });
else
coverage.entries.push(entry);
}
return coverage;
}
};
CSSCoverage = class {
constructor(client) {
this._client = client;
this._enabled = false;
this._stylesheetURLs = /* @__PURE__ */ new Map();
this._stylesheetSources = /* @__PURE__ */ new Map();
this._eventListeners = [];
this._resetOnNavigation = false;
}
async start(options2) {
assert(!this._enabled, "CSSCoverage is already enabled");
const { resetOnNavigation = true } = options2;
this._resetOnNavigation = resetOnNavigation;
this._enabled = true;
this._stylesheetURLs.clear();
this._stylesheetSources.clear();
this._eventListeners = [
eventsHelper.addEventListener(this._client, "CSS.styleSheetAdded", this._onStyleSheet.bind(this)),
eventsHelper.addEventListener(this._client, "Runtime.executionContextsCleared", this._onExecutionContextsCleared.bind(this))
];
await Promise.all([
this._client.send("DOM.enable"),
this._client.send("CSS.enable"),
this._client.send("CSS.startRuleUsageTracking")
]);
}
_onExecutionContextsCleared() {
if (!this._resetOnNavigation)
return;
this._stylesheetURLs.clear();
this._stylesheetSources.clear();
}
async _onStyleSheet(event) {
const header = event.header;
if (!header.sourceURL)
return;
const response2 = await this._client._sendMayFail("CSS.getStyleSheetText", { styleSheetId: header.styleSheetId });
if (response2) {
this._stylesheetURLs.set(header.styleSheetId, header.sourceURL);
this._stylesheetSources.set(header.styleSheetId, response2.text);
}
}
async stop() {
if (!this._enabled)
return { entries: [] };
const ruleTrackingResponse = await this._client.send("CSS.stopRuleUsageTracking");
await Promise.all([
this._client.send("CSS.disable"),
this._client.send("DOM.disable")
]);
eventsHelper.removeEventListeners(this._eventListeners);
this._enabled = false;
const styleSheetIdToCoverage = /* @__PURE__ */ new Map();
for (const entry of ruleTrackingResponse.ruleUsage) {
let ranges = styleSheetIdToCoverage.get(entry.styleSheetId);
if (!ranges) {
ranges = [];
styleSheetIdToCoverage.set(entry.styleSheetId, ranges);
}
ranges.push({
startOffset: entry.startOffset,
endOffset: entry.endOffset,
count: entry.used ? 1 : 0
});
}
const coverage = { entries: [] };
for (const styleSheetId of this._stylesheetURLs.keys()) {
const url2 = this._stylesheetURLs.get(styleSheetId);
const text2 = this._stylesheetSources.get(styleSheetId);
const ranges = convertToDisjointRanges(styleSheetIdToCoverage.get(styleSheetId) || []);
coverage.entries.push({ url: url2, ranges, text: text2 });
}
return coverage;
}
};
}
});
// packages/playwright-core/src/server/chromium/crProtocolHelper.ts
function getExceptionMessage(exceptionDetails) {
if (exceptionDetails.exception)
return exceptionDetails.exception.description || String(exceptionDetails.exception.value);
let message = exceptionDetails.text;
if (exceptionDetails.stackTrace) {
for (const callframe of exceptionDetails.stackTrace.callFrames) {
const location2 = callframe.url + ":" + callframe.lineNumber + ":" + callframe.columnNumber;
const functionName = callframe.functionName || "<anonymous>";
message += `
at ${functionName} (${location2})`;
}
}
return message;
}
async function releaseObject(client, objectId) {
await client.send("Runtime.releaseObject", { objectId }).catch((error) => {
});
}
async function saveProtocolStream(client, handle, path59) {
let eof = false;
await mkdirIfNeeded(path59);
const fd = await import_fs23.default.promises.open(path59, "w");
while (!eof) {
const response2 = await client.send("IO.read", { handle });
eof = response2.eof;
const buf = Buffer.from(response2.data, response2.base64Encoded ? "base64" : void 0);
await fd.write(buf);
}
await fd.close();
await client.send("IO.close", { handle });
}
async function readProtocolStream(client, handle) {
let eof = false;
const chunks = [];
while (!eof) {
const response2 = await client.send("IO.read", { handle });
eof = response2.eof;
const buf = Buffer.from(response2.data, response2.base64Encoded ? "base64" : void 0);
chunks.push(buf);
}
await client.send("IO.close", { handle });
return Buffer.concat(chunks);
}
function stackTraceToLocation(stackTrace) {
return stackTrace && stackTrace.callFrames.length ? {
url: stackTrace.callFrames[0].url,
lineNumber: stackTrace.callFrames[0].lineNumber,
columnNumber: stackTrace.callFrames[0].columnNumber
} : { url: "", lineNumber: 0, columnNumber: 0 };
}
function exceptionToError(exceptionDetails) {
const messageWithStack = getExceptionMessage(exceptionDetails);
const lines = messageWithStack.split("\n");
const firstStackTraceLine = lines.findIndex((line) => line.startsWith(" at"));
let messageWithName = "";
let stack = "";
if (firstStackTraceLine === -1) {
messageWithName = messageWithStack;
} else {
messageWithName = lines.slice(0, firstStackTraceLine).join("\n");
stack = messageWithStack;
}
const { name, message } = splitErrorMessage(messageWithName);
const err = new Error(message);
err.stack = stack;
const nameOverride = exceptionDetails.exception?.preview?.properties.find((o) => o.name === "name");
err.name = nameOverride ? nameOverride.value ?? "Error" : name;
return err;
}
function toModifiersMask(modifiers) {
let mask = 0;
if (modifiers.has("Alt"))
mask |= 1;
if (modifiers.has("Control"))
mask |= 2;
if (modifiers.has("Meta"))
mask |= 4;
if (modifiers.has("Shift"))
mask |= 8;
return mask;
}
function toButtonsMask(buttons) {
let mask = 0;
if (buttons.has("left"))
mask |= 1;
if (buttons.has("right"))
mask |= 2;
if (buttons.has("middle"))
mask |= 4;
return mask;
}
var import_fs23;
var init_crProtocolHelper = __esm({
"packages/playwright-core/src/server/chromium/crProtocolHelper.ts"() {
"use strict";
import_fs23 = __toESM(require("fs"));
init_stackTrace();
init_fileUtils();
}
});
// packages/playwright-core/src/server/chromium/crDragDrop.ts
var DragManager;
var init_crDragDrop = __esm({
"packages/playwright-core/src/server/chromium/crDragDrop.ts"() {
"use strict";
init_assert();
init_crProtocolHelper();
DragManager = class {
constructor(page) {
this._dragState = null;
this._lastPosition = { x: 0, y: 0 };
this._crPage = page;
}
async cancelDrag() {
if (!this._dragState)
return false;
await this._crPage._mainFrameSession._client.send("Input.dispatchDragEvent", {
type: "dragCancel",
x: this._lastPosition.x,
y: this._lastPosition.y,
data: {
items: [],
dragOperationsMask: 65535
}
});
this._dragState = null;
return true;
}
async interceptDragCausedByMove(progress2, x, y, button, buttons, modifiers, moveCallback) {
this._lastPosition = { x, y };
if (this._dragState) {
await progress2.race(this._crPage._mainFrameSession._client.send("Input.dispatchDragEvent", {
type: "dragOver",
x,
y,
data: this._dragState,
modifiers: toModifiersMask(modifiers)
}));
return;
}
if (button !== "left")
return moveCallback(progress2);
const client = this._crPage._mainFrameSession._client;
let onDragIntercepted;
const dragInterceptedPromise = new Promise((x2) => onDragIntercepted = x2);
function setupDragListeners() {
let didStartDrag = Promise.resolve(false);
let dragEvent = null;
const dragListener = (event) => dragEvent = event;
const mouseListener = () => {
didStartDrag = new Promise((callback) => {
window.addEventListener("dragstart", dragListener, { once: true, capture: true });
setTimeout(() => callback(dragEvent ? !dragEvent.defaultPrevented : false), 0);
});
};
window.addEventListener("mousemove", mouseListener, { once: true, capture: true });
window.__cleanupDrag = async () => {
const val = await didStartDrag;
window.removeEventListener("mousemove", mouseListener, { capture: true });
window.removeEventListener("dragstart", dragListener, { capture: true });
delete window.__cleanupDrag;
return val;
};
}
try {
let expectingDrag = false;
await progress2.race(this._crPage._page.safeNonStallingEvaluateInAllFrames(`(${setupDragListeners.toString()})()`, "utility"));
client.on("Input.dragIntercepted", onDragIntercepted);
await progress2.race(client.send("Input.setInterceptDrags", { enabled: true }));
try {
await moveCallback(progress2);
expectingDrag = (await progress2.race(Promise.all(this._crPage._page.frames().map(async (frame) => {
return frame.nonStallingEvaluateInExistingContext("window.__cleanupDrag?.()", "utility").catch(() => false);
})))).some((x2) => x2);
} finally {
client.off("Input.dragIntercepted", onDragIntercepted);
await progress2.race(client.send("Input.setInterceptDrags", { enabled: false }));
}
this._dragState = expectingDrag ? (await dragInterceptedPromise).data : null;
} catch (error) {
this._crPage._page.safeNonStallingEvaluateInAllFrames("window.__cleanupDrag?.()", "utility").catch(() => {
});
throw error;
}
if (this._dragState) {
await progress2.race(this._crPage._mainFrameSession._client.send("Input.dispatchDragEvent", {
type: "dragEnter",
x,
y,
data: this._dragState,
modifiers: toModifiersMask(modifiers)
}));
}
}
isDragging() {
return !!this._dragState;
}
async drop(progress2, x, y, modifiers) {
assert(this._dragState, "missing drag state");
await progress2.race(this._crPage._mainFrameSession._client.send("Input.dispatchDragEvent", {
type: "drop",
x,
y,
data: this._dragState,
modifiers: toModifiersMask(modifiers)
}));
this._dragState = null;
}
};
}
});
// packages/playwright-core/src/server/chromium/crExecutionContext.ts
function rewriteError(error) {
if (error.message.includes("Object reference chain is too long"))
throw new Error("Cannot serialize result: object reference chain is too long.");
if (error.message.includes("Object couldn't be returned by value"))
return { result: { type: "undefined" } };
if (error instanceof TypeError && error.message.startsWith("Converting circular structure to JSON"))
rewriteErrorMessage(error, error.message + " Are you passing a nested JSHandle?");
if (!isJavaScriptErrorInEvaluate(error) && !isSessionClosedError(error))
throw new Error("Execution context was destroyed, most likely because of a navigation.");
throw error;
}
function potentiallyUnserializableValue(remoteObject) {
const value2 = remoteObject.value;
const unserializableValue = remoteObject.unserializableValue;
return unserializableValue ? parseUnserializableValue(unserializableValue) : value2;
}
function renderPreview(object) {
if (object.type === "undefined")
return "undefined";
if ("value" in object)
return String(object.value);
if (object.unserializableValue)
return String(object.unserializableValue);
if (object.description === "Object" && object.preview) {
const tokens = [];
for (const { name, value: value2 } of object.preview.properties)
tokens.push(`${name}: ${value2}`);
return `{${tokens.join(", ")}}`;
}
if (object.subtype === "array" && object.preview)
return sparseArrayToString(object.preview.properties);
return object.description;
}
function createHandle(context2, remoteObject) {
if (remoteObject.subtype === "node") {
assert(context2 instanceof FrameExecutionContext);
return new ElementHandle(context2, remoteObject.objectId);
}
return new JSHandle(context2, remoteObject.subtype || remoteObject.type, renderPreview(remoteObject), remoteObject.objectId, potentiallyUnserializableValue(remoteObject));
}
var CRExecutionContext;
var init_crExecutionContext = __esm({
"packages/playwright-core/src/server/chromium/crExecutionContext.ts"() {
"use strict";
init_assert();
init_stackTrace();
init_utilityScriptSerializers();
init_crProtocolHelper();
init_javascript();
init_dom();
init_protocolError();
CRExecutionContext = class {
constructor(client, contextPayload) {
this._client = client;
this._contextId = contextPayload.id;
}
async rawEvaluateJSON(expression2) {
const { exceptionDetails, result: remoteObject } = await this._client.send("Runtime.evaluate", {
expression: expression2,
contextId: this._contextId,
returnByValue: true
}).catch(rewriteError);
if (exceptionDetails)
throw new JavaScriptErrorInEvaluate(getExceptionMessage(exceptionDetails));
return remoteObject.value;
}
async rawEvaluateHandle(context2, expression2) {
const { exceptionDetails, result: remoteObject } = await this._client.send("Runtime.evaluate", {
expression: expression2,
contextId: this._contextId
}).catch(rewriteError);
if (exceptionDetails)
throw new JavaScriptErrorInEvaluate(getExceptionMessage(exceptionDetails));
return createHandle(context2, remoteObject);
}
async evaluateWithArguments(expression2, returnByValue, utilityScript, values, handles) {
const { exceptionDetails, result: remoteObject } = await this._client.send("Runtime.callFunctionOn", {
functionDeclaration: expression2,
objectId: utilityScript._objectId,
arguments: [
{ objectId: utilityScript._objectId },
...values.map((value2) => ({ value: value2 })),
...handles.map((handle) => ({ objectId: handle._objectId }))
],
returnByValue,
awaitPromise: true,
userGesture: true
}).catch(rewriteError);
if (exceptionDetails)
throw new JavaScriptErrorInEvaluate(getExceptionMessage(exceptionDetails));
return returnByValue ? parseEvaluationResultValue(remoteObject.value) : createHandle(utilityScript._context, remoteObject);
}
async getProperties(object) {
const response2 = await this._client.send("Runtime.getProperties", {
objectId: object._objectId,
ownProperties: true
});
const result2 = /* @__PURE__ */ new Map();
for (const property of response2.result) {
if (!property.enumerable || !property.value)
continue;
result2.set(property.name, createHandle(object._context, property.value));
}
return result2;
}
async releaseHandle(handle) {
if (!handle._objectId)
return;
await releaseObject(this._client, handle._objectId);
}
shouldPrependErrorPrefix() {
return false;
}
};
}
});
// packages/playwright-core/src/server/macEditingCommands.ts
var macEditingCommands;
var init_macEditingCommands = __esm({
"packages/playwright-core/src/server/macEditingCommands.ts"() {
"use strict";
macEditingCommands = {
"Backspace": "deleteBackward:",
"Enter": "insertNewline:",
"NumpadEnter": "insertNewline:",
"Escape": "cancelOperation:",
"ArrowUp": "moveUp:",
"ArrowDown": "moveDown:",
"ArrowLeft": "moveLeft:",
"ArrowRight": "moveRight:",
"F5": "complete:",
"Delete": "deleteForward:",
"Home": "scrollToBeginningOfDocument:",
"End": "scrollToEndOfDocument:",
"PageUp": "scrollPageUp:",
"PageDown": "scrollPageDown:",
"Shift+Backspace": "deleteBackward:",
"Shift+Enter": "insertNewline:",
"Shift+NumpadEnter": "insertNewline:",
"Shift+Escape": "cancelOperation:",
"Shift+ArrowUp": "moveUpAndModifySelection:",
"Shift+ArrowDown": "moveDownAndModifySelection:",
"Shift+ArrowLeft": "moveLeftAndModifySelection:",
"Shift+ArrowRight": "moveRightAndModifySelection:",
"Shift+F5": "complete:",
"Shift+Delete": "deleteForward:",
"Shift+Home": "moveToBeginningOfDocumentAndModifySelection:",
"Shift+End": "moveToEndOfDocumentAndModifySelection:",
"Shift+PageUp": "pageUpAndModifySelection:",
"Shift+PageDown": "pageDownAndModifySelection:",
"Shift+Numpad5": "delete:",
"Control+Tab": "selectNextKeyView:",
"Control+Enter": "insertLineBreak:",
"Control+NumpadEnter": "insertLineBreak:",
"Control+Quote": "insertSingleQuoteIgnoringSubstitution:",
"Control+KeyA": "moveToBeginningOfParagraph:",
"Control+KeyB": "moveBackward:",
"Control+KeyD": "deleteForward:",
"Control+KeyE": "moveToEndOfParagraph:",
"Control+KeyF": "moveForward:",
"Control+KeyH": "deleteBackward:",
"Control+KeyK": "deleteToEndOfParagraph:",
"Control+KeyL": "centerSelectionInVisibleArea:",
"Control+KeyN": "moveDown:",
"Control+KeyO": ["insertNewlineIgnoringFieldEditor:", "moveBackward:"],
"Control+KeyP": "moveUp:",
"Control+KeyT": "transpose:",
"Control+KeyV": "pageDown:",
"Control+KeyY": "yank:",
"Control+Backspace": "deleteBackwardByDecomposingPreviousCharacter:",
"Control+ArrowUp": "scrollPageUp:",
"Control+ArrowDown": "scrollPageDown:",
"Control+ArrowLeft": "moveToLeftEndOfLine:",
"Control+ArrowRight": "moveToRightEndOfLine:",
"Shift+Control+Enter": "insertLineBreak:",
"Shift+Control+NumpadEnter": "insertLineBreak:",
"Shift+Control+Tab": "selectPreviousKeyView:",
"Shift+Control+Quote": "insertDoubleQuoteIgnoringSubstitution:",
"Shift+Control+KeyA": "moveToBeginningOfParagraphAndModifySelection:",
"Shift+Control+KeyB": "moveBackwardAndModifySelection:",
"Shift+Control+KeyE": "moveToEndOfParagraphAndModifySelection:",
"Shift+Control+KeyF": "moveForwardAndModifySelection:",
"Shift+Control+KeyN": "moveDownAndModifySelection:",
"Shift+Control+KeyP": "moveUpAndModifySelection:",
"Shift+Control+KeyV": "pageDownAndModifySelection:",
"Shift+Control+Backspace": "deleteBackwardByDecomposingPreviousCharacter:",
"Shift+Control+ArrowUp": "scrollPageUp:",
"Shift+Control+ArrowDown": "scrollPageDown:",
"Shift+Control+ArrowLeft": "moveToLeftEndOfLineAndModifySelection:",
"Shift+Control+ArrowRight": "moveToRightEndOfLineAndModifySelection:",
"Alt+Backspace": "deleteWordBackward:",
"Alt+Enter": "insertNewlineIgnoringFieldEditor:",
"Alt+NumpadEnter": "insertNewlineIgnoringFieldEditor:",
"Alt+Escape": "complete:",
"Alt+ArrowUp": ["moveBackward:", "moveToBeginningOfParagraph:"],
"Alt+ArrowDown": ["moveForward:", "moveToEndOfParagraph:"],
"Alt+ArrowLeft": "moveWordLeft:",
"Alt+ArrowRight": "moveWordRight:",
"Alt+Delete": "deleteWordForward:",
"Alt+PageUp": "pageUp:",
"Alt+PageDown": "pageDown:",
"Shift+Alt+Backspace": "deleteWordBackward:",
"Shift+Alt+Enter": "insertNewlineIgnoringFieldEditor:",
"Shift+Alt+NumpadEnter": "insertNewlineIgnoringFieldEditor:",
"Shift+Alt+Escape": "complete:",
"Shift+Alt+ArrowUp": "moveParagraphBackwardAndModifySelection:",
"Shift+Alt+ArrowDown": "moveParagraphForwardAndModifySelection:",
"Shift+Alt+ArrowLeft": "moveWordLeftAndModifySelection:",
"Shift+Alt+ArrowRight": "moveWordRightAndModifySelection:",
"Shift+Alt+Delete": "deleteWordForward:",
"Shift+Alt+PageUp": "pageUp:",
"Shift+Alt+PageDown": "pageDown:",
"Control+Alt+KeyB": "moveWordBackward:",
"Control+Alt+KeyF": "moveWordForward:",
"Control+Alt+Backspace": "deleteWordBackward:",
"Shift+Control+Alt+KeyB": "moveWordBackwardAndModifySelection:",
"Shift+Control+Alt+KeyF": "moveWordForwardAndModifySelection:",
"Shift+Control+Alt+Backspace": "deleteWordBackward:",
"Meta+NumpadSubtract": "cancel:",
"Meta+Backspace": "deleteToBeginningOfLine:",
"Meta+ArrowUp": "moveToBeginningOfDocument:",
"Meta+ArrowDown": "moveToEndOfDocument:",
"Meta+ArrowLeft": "moveToLeftEndOfLine:",
"Meta+ArrowRight": "moveToRightEndOfLine:",
"Shift+Meta+NumpadSubtract": "cancel:",
"Shift+Meta+Backspace": "deleteToBeginningOfLine:",
"Shift+Meta+ArrowUp": "moveToBeginningOfDocumentAndModifySelection:",
"Shift+Meta+ArrowDown": "moveToEndOfDocumentAndModifySelection:",
"Shift+Meta+ArrowLeft": "moveToLeftEndOfLineAndModifySelection:",
"Shift+Meta+ArrowRight": "moveToRightEndOfLineAndModifySelection:",
"Meta+KeyA": "selectAll:",
"Meta+KeyC": "copy:",
"Meta+KeyX": "cut:",
"Meta+KeyV": "paste:",
"Meta+KeyZ": "undo:",
"Shift+Meta+KeyZ": "redo:"
};
}
});
// packages/playwright-core/src/server/chromium/crInput.ts
var RawKeyboardImpl, RawMouseImpl, RawTouchscreenImpl;
var init_crInput = __esm({
"packages/playwright-core/src/server/chromium/crInput.ts"() {
"use strict";
init_stringUtils();
init_input();
init_macEditingCommands();
init_crProtocolHelper();
RawKeyboardImpl = class {
constructor(_client, _isMac, _dragManger) {
this._client = _client;
this._isMac = _isMac;
this._dragManger = _dragManger;
}
_commandsForCode(code, modifiers) {
if (!this._isMac)
return [];
const parts = [];
for (const modifier of ["Shift", "Control", "Alt", "Meta"]) {
if (modifiers.has(modifier))
parts.push(modifier);
}
parts.push(code);
const shortcut = parts.join("+");
let commands2 = macEditingCommands[shortcut] || [];
if (isString(commands2))
commands2 = [commands2];
commands2 = commands2.filter((x) => !x.startsWith("insert"));
return commands2.map((c) => c.substring(0, c.length - 1));
}
async keydown(progress2, modifiers, keyName, description, autoRepeat) {
const { code, key, location: location2, text: text2 } = description;
if (code === "Escape" && await progress2.race(this._dragManger.cancelDrag()))
return;
const commands2 = this._commandsForCode(code, modifiers);
await progress2.race(this._client.send("Input.dispatchKeyEvent", {
type: text2 ? "keyDown" : "rawKeyDown",
modifiers: toModifiersMask(modifiers),
windowsVirtualKeyCode: description.keyCodeWithoutLocation,
code,
commands: commands2,
key,
text: text2,
unmodifiedText: text2,
autoRepeat,
location: location2,
isKeypad: location2 === keypadLocation2
}));
}
async keyup(progress2, modifiers, keyName, description) {
const { code, key, location: location2 } = description;
await progress2.race(this._client.send("Input.dispatchKeyEvent", {
type: "keyUp",
modifiers: toModifiersMask(modifiers),
key,
windowsVirtualKeyCode: description.keyCodeWithoutLocation,
code,
location: location2
}));
}
async sendText(progress2, text2) {
await progress2.race(this._client.send("Input.insertText", { text: text2 }));
}
};
RawMouseImpl = class {
constructor(page, client, dragManager) {
this._page = page;
this._client = client;
this._dragManager = dragManager;
}
async move(progress2, x, y, button, buttons, modifiers, forClick) {
const actualMove = async (progress3) => {
await progress3.race(this._client.send("Input.dispatchMouseEvent", {
type: "mouseMoved",
button,
buttons: toButtonsMask(buttons),
x,
y,
modifiers: toModifiersMask(modifiers),
force: buttons.size > 0 ? 0.5 : 0
}));
};
if (forClick) {
await actualMove(progress2);
return;
}
await this._dragManager.interceptDragCausedByMove(progress2, x, y, button, buttons, modifiers, actualMove);
}
async down(progress2, x, y, button, buttons, modifiers, clickCount) {
if (this._dragManager.isDragging())
return;
await progress2.race(this._client.send("Input.dispatchMouseEvent", {
type: "mousePressed",
button,
buttons: toButtonsMask(buttons),
x,
y,
modifiers: toModifiersMask(modifiers),
clickCount,
force: buttons.size > 0 ? 0.5 : 0
}));
}
async up(progress2, x, y, button, buttons, modifiers, clickCount) {
if (this._dragManager.isDragging()) {
await this._dragManager.drop(progress2, x, y, modifiers);
return;
}
await progress2.race(this._client.send("Input.dispatchMouseEvent", {
type: "mouseReleased",
button,
buttons: toButtonsMask(buttons),
x,
y,
modifiers: toModifiersMask(modifiers),
clickCount
}));
}
async wheel(progress2, x, y, buttons, modifiers, deltaX, deltaY) {
await progress2.race(this._client.send("Input.dispatchMouseEvent", {
type: "mouseWheel",
x,
y,
modifiers: toModifiersMask(modifiers),
deltaX,
deltaY
}));
}
};
RawTouchscreenImpl = class {
constructor(client) {
this._client = client;
}
async tap(progress2, x, y, modifiers) {
await progress2.race(Promise.all([
this._client.send("Input.dispatchTouchEvent", {
type: "touchStart",
modifiers: toModifiersMask(modifiers),
touchPoints: [{
x,
y
}]
}),
this._client.send("Input.dispatchTouchEvent", {
type: "touchEnd",
modifiers: toModifiersMask(modifiers),
touchPoints: []
})
]));
}
};
}
});
// packages/playwright-core/src/server/chromium/crNetworkManager.ts
async function catchDisallowedErrors(callback) {
try {
return await callback();
} catch (e) {
if (isProtocolError(e) && e.message.includes("Invalid http status code or phrase"))
throw e;
if (isProtocolError(e) && e.message.includes("Unsafe header"))
throw e;
}
}
function splitSetCookieHeader(headers) {
const index = headers.findIndex(({ name }) => name.toLowerCase() === "set-cookie");
if (index === -1)
return headers;
const header = headers[index];
const values = header.value.split("\n");
if (values.length === 1)
return headers;
const result2 = headers.slice();
result2.splice(index, 1, ...values.map((value2) => ({ name: header.name, value: value2 })));
return result2;
}
function toResourceType(type3) {
switch (type3) {
case "Document":
return "document";
case "Stylesheet":
return "stylesheet";
case "Image":
return "image";
case "Media":
return "media";
case "Font":
return "font";
case "Script":
return "script";
case "TextTrack":
return "texttrack";
case "XHR":
return "xhr";
case "Fetch":
return "fetch";
case "EventSource":
return "eventsource";
case "WebSocket":
return "websocket";
case "Manifest":
return "manifest";
case "Ping":
return "ping";
case "CSPViolationReport":
return "cspreport";
case "Prefetch":
case "SignedExchange":
case "Preflight":
case "FedCM":
default:
return "other";
}
}
var CRNetworkManager, InterceptableRequest, RouteImpl, errorReasons, ResponseExtraInfoTracker;
var init_crNetworkManager = __esm({
"packages/playwright-core/src/server/chromium/crNetworkManager.ts"() {
"use strict";
init_eventsHelper();
init_assert();
init_headers();
init_helper();
init_network2();
init_protocolError();
CRNetworkManager = class {
constructor(page, serviceWorker) {
this._requestIdToRequest = /* @__PURE__ */ new Map();
this._requestIdToRequestWillBeSentEvent = /* @__PURE__ */ new Map();
this._credentials = null;
this._attemptedAuthentications = /* @__PURE__ */ new Set();
this._userRequestInterceptionEnabled = false;
this._protocolRequestInterceptionEnabled = false;
this._offline = false;
this._extraHTTPHeaders = [];
this._requestIdToRequestPausedEvent = /* @__PURE__ */ new Map();
this._responseExtraInfoTracker = new ResponseExtraInfoTracker();
this._sessions = /* @__PURE__ */ new Map();
this._page = page;
this._serviceWorker = serviceWorker;
}
async addSession(session2, workerFrame, isMain) {
const sessionInfo = { session: session2, isMain, workerFrame, eventListeners: [] };
sessionInfo.eventListeners = [
eventsHelper.addEventListener(session2, "Fetch.requestPaused", this._onRequestPaused.bind(this, sessionInfo)),
eventsHelper.addEventListener(session2, "Fetch.authRequired", this._onAuthRequired.bind(this, sessionInfo)),
eventsHelper.addEventListener(session2, "Network.requestWillBeSent", this._onRequestWillBeSent.bind(this, sessionInfo)),
eventsHelper.addEventListener(session2, "Network.requestWillBeSentExtraInfo", this._onRequestWillBeSentExtraInfo.bind(this)),
eventsHelper.addEventListener(session2, "Network.requestServedFromCache", this._onRequestServedFromCache.bind(this)),
eventsHelper.addEventListener(session2, "Network.responseReceived", this._onResponseReceived.bind(this, sessionInfo)),
eventsHelper.addEventListener(session2, "Network.responseReceivedExtraInfo", this._onResponseReceivedExtraInfo.bind(this)),
eventsHelper.addEventListener(session2, "Network.loadingFinished", this._onLoadingFinished.bind(this, sessionInfo)),
eventsHelper.addEventListener(session2, "Network.loadingFailed", this._onLoadingFailed.bind(this, sessionInfo))
];
if (this._page) {
sessionInfo.eventListeners.push(...[
eventsHelper.addEventListener(session2, "Network.webSocketCreated", (e) => this._page.frameManager.onWebSocketCreated(e.requestId, e.url)),
eventsHelper.addEventListener(session2, "Network.webSocketWillSendHandshakeRequest", (e) => this._page.frameManager.onWebSocketRequest(e.requestId)),
eventsHelper.addEventListener(session2, "Network.webSocketHandshakeResponseReceived", (e) => this._page.frameManager.onWebSocketResponse(e.requestId, e.response.status, e.response.statusText)),
eventsHelper.addEventListener(session2, "Network.webSocketFrameSent", (e) => e.response.payloadData && this._page.frameManager.onWebSocketFrameSent(e.requestId, e.response.opcode, e.response.payloadData)),
eventsHelper.addEventListener(session2, "Network.webSocketFrameReceived", (e) => e.response.payloadData && this._page.frameManager.webSocketFrameReceived(e.requestId, e.response.opcode, e.response.payloadData)),
eventsHelper.addEventListener(session2, "Network.webSocketClosed", (e) => this._page.frameManager.webSocketClosed(e.requestId)),
eventsHelper.addEventListener(session2, "Network.webSocketFrameError", (e) => this._page.frameManager.webSocketError(e.requestId, e.errorMessage))
]);
}
this._sessions.set(session2, sessionInfo);
await Promise.all([
session2.send("Network.enable"),
this._updateProtocolRequestInterceptionForSession(
sessionInfo,
true
/* initial */
),
this._setOfflineForSession(
sessionInfo,
true
/* initial */
),
this._setExtraHTTPHeadersForSession(
sessionInfo,
true
/* initial */
)
]);
}
removeSession(session2) {
const info = this._sessions.get(session2);
if (info)
eventsHelper.removeEventListeners(info.eventListeners);
this._sessions.delete(session2);
}
async _forEachSession(cb) {
await Promise.all([...this._sessions.values()].map((info) => {
if (info.isMain)
return cb(info);
return cb(info).catch((e) => {
if (isSessionClosedError(e))
return;
throw e;
});
}));
}
async authenticate(credentials) {
this._credentials = credentials;
await this._updateProtocolRequestInterception();
}
async setOffline(offline) {
if (offline === this._offline)
return;
this._offline = offline;
await this._forEachSession((info) => this._setOfflineForSession(info));
}
async _setOfflineForSession(info, initial) {
if (initial && !this._offline)
return;
if (info.workerFrame)
return;
await info.session.send("Network.emulateNetworkConditions", {
offline: this._offline,
// values of 0 remove any active throttling. crbug.com/456324#c9
latency: 0,
downloadThroughput: -1,
uploadThroughput: -1
});
}
async setRequestInterception(value2) {
this._userRequestInterceptionEnabled = value2;
await this._updateProtocolRequestInterception();
}
async _updateProtocolRequestInterception() {
const enabled = this._userRequestInterceptionEnabled || !!this._credentials;
if (enabled === this._protocolRequestInterceptionEnabled)
return;
this._protocolRequestInterceptionEnabled = enabled;
await this._forEachSession((info) => this._updateProtocolRequestInterceptionForSession(info));
}
async _updateProtocolRequestInterceptionForSession(info, initial) {
const enabled = this._protocolRequestInterceptionEnabled;
if (initial && !enabled)
return;
const cachePromise = info.session.send("Network.setCacheDisabled", { cacheDisabled: enabled });
let fetchPromise = Promise.resolve(void 0);
if (!info.workerFrame) {
if (enabled)
fetchPromise = info.session.send("Fetch.enable", { handleAuthRequests: true, patterns: [{ urlPattern: "*", requestStage: "Request" }] });
else
fetchPromise = info.session.send("Fetch.disable");
}
await Promise.all([cachePromise, fetchPromise]);
}
async setExtraHTTPHeaders(extraHTTPHeaders) {
if (!this._extraHTTPHeaders.length && !extraHTTPHeaders.length)
return;
this._extraHTTPHeaders = extraHTTPHeaders;
await this._forEachSession((info) => this._setExtraHTTPHeadersForSession(info));
}
async _setExtraHTTPHeadersForSession(info, initial) {
if (initial && !this._extraHTTPHeaders.length)
return;
await info.session.send("Network.setExtraHTTPHeaders", { headers: headersArrayToObject(
this._extraHTTPHeaders,
false
/* lowerCase */
) });
}
async clearCache() {
await this._forEachSession(async (info) => {
await info.session.send("Network.setCacheDisabled", { cacheDisabled: true });
if (!this._protocolRequestInterceptionEnabled)
await info.session.send("Network.setCacheDisabled", { cacheDisabled: false });
if (!info.workerFrame)
await info.session.send("Network.clearBrowserCache");
});
}
_onRequestWillBeSent(sessionInfo, event) {
if (this._protocolRequestInterceptionEnabled && !event.request.url.startsWith("data:")) {
const requestId = event.requestId;
const requestPausedEvent = this._requestIdToRequestPausedEvent.get(requestId);
if (requestPausedEvent) {
this._onRequest(sessionInfo, event, requestPausedEvent.sessionInfo, requestPausedEvent.event);
this._requestIdToRequestPausedEvent.delete(requestId);
} else {
this._requestIdToRequestWillBeSentEvent.set(event.requestId, { sessionInfo, event });
}
} else {
this._onRequest(sessionInfo, event, void 0, void 0);
}
}
_onRequestServedFromCache(event) {
this._responseExtraInfoTracker.requestServedFromCache(event);
}
_onRequestWillBeSentExtraInfo(event) {
this._responseExtraInfoTracker.requestWillBeSentExtraInfo(event);
}
_onAuthRequired(sessionInfo, event) {
let response2 = "Default";
const shouldProvideCredentials = this._shouldProvideCredentials(event.request.url);
if (this._attemptedAuthentications.has(event.requestId)) {
response2 = "CancelAuth";
} else if (shouldProvideCredentials) {
response2 = "ProvideCredentials";
this._attemptedAuthentications.add(event.requestId);
}
const { username, password } = shouldProvideCredentials && this._credentials ? this._credentials : { username: void 0, password: void 0 };
sessionInfo.session._sendMayFail("Fetch.continueWithAuth", {
requestId: event.requestId,
authChallengeResponse: { response: response2, username, password }
});
}
_shouldProvideCredentials(url2) {
if (!this._credentials)
return false;
return !this._credentials.origin || new URL(url2).origin.toLowerCase() === this._credentials.origin.toLowerCase();
}
_onRequestPaused(sessionInfo, event) {
if (!event.networkId) {
sessionInfo.session._sendMayFail("Fetch.continueRequest", { requestId: event.requestId });
return;
}
if (event.request.url.startsWith("data:"))
return;
const requestId = event.networkId;
const requestWillBeSentEvent = this._requestIdToRequestWillBeSentEvent.get(requestId);
if (requestWillBeSentEvent) {
this._onRequest(requestWillBeSentEvent.sessionInfo, requestWillBeSentEvent.event, sessionInfo, event);
this._requestIdToRequestWillBeSentEvent.delete(requestId);
} else {
const existingRequest = this._requestIdToRequest.get(requestId);
const alreadyContinuedParams = existingRequest?._originalRequestRoute?._alreadyContinuedParams;
if (alreadyContinuedParams && !event.redirectedRequestId) {
sessionInfo.session._sendMayFail("Fetch.continueRequest", {
...alreadyContinuedParams,
requestId: event.requestId
});
return;
}
this._requestIdToRequestPausedEvent.set(requestId, { sessionInfo, event });
}
}
_onRequest(requestWillBeSentSessionInfo, requestWillBeSentEvent, requestPausedSessionInfo, requestPausedEvent) {
if (requestWillBeSentEvent.request.url.startsWith("data:"))
return;
let redirectedFrom = null;
if (requestWillBeSentEvent.redirectResponse) {
const request3 = this._requestIdToRequest.get(requestWillBeSentEvent.requestId);
if (request3) {
this._handleRequestRedirect(request3, requestWillBeSentEvent.redirectResponse, requestWillBeSentEvent.timestamp, requestWillBeSentEvent.redirectHasExtraInfo);
redirectedFrom = request3;
}
}
let frame = requestWillBeSentEvent.frameId ? this._page?.frameManager.frame(requestWillBeSentEvent.frameId) : requestWillBeSentSessionInfo.workerFrame;
if (!frame && this._page && requestPausedEvent && requestPausedEvent.frameId)
frame = this._page.frameManager.frame(requestPausedEvent.frameId);
if (!frame && this._page && requestWillBeSentEvent.frameId === (this._page?.delegate)._targetId) {
frame = this._page.frameManager.frameAttached(requestWillBeSentEvent.frameId, null);
}
const isInterceptedOptionsPreflight = !!requestPausedEvent && requestPausedEvent.request.method === "OPTIONS" && requestWillBeSentEvent.initiator.type === "preflight";
if (isInterceptedOptionsPreflight && (this._page || this._serviceWorker).needsRequestInterception()) {
const requestHeaders = requestPausedEvent.request.headers;
const responseHeaders = [
{ name: "Access-Control-Allow-Origin", value: requestHeaders["Origin"] || "*" },
{ name: "Access-Control-Allow-Methods", value: requestHeaders["Access-Control-Request-Method"] || "GET, POST, OPTIONS, DELETE" },
{ name: "Access-Control-Allow-Credentials", value: "true" }
];
if (requestHeaders["Access-Control-Request-Headers"])
responseHeaders.push({ name: "Access-Control-Allow-Headers", value: requestHeaders["Access-Control-Request-Headers"] });
requestPausedSessionInfo.session._sendMayFail("Fetch.fulfillRequest", {
requestId: requestPausedEvent.requestId,
responseCode: 204,
responsePhrase: statusText(204),
responseHeaders,
body: ""
});
return;
}
if (!frame && !this._serviceWorker) {
if (requestPausedEvent)
requestPausedSessionInfo.session._sendMayFail("Fetch.continueRequest", { requestId: requestPausedEvent.requestId });
return;
}
let route2 = null;
let headersOverride;
if (requestPausedEvent) {
if (redirectedFrom || !this._userRequestInterceptionEnabled && this._protocolRequestInterceptionEnabled) {
headersOverride = redirectedFrom?._originalRequestRoute?._alreadyContinuedParams?.headers;
if (headersOverride) {
const originalHeaders = Object.entries(requestPausedEvent.request.headers).map(([name, value2]) => ({ name, value: value2 }));
headersOverride = applyHeadersOverrides(originalHeaders, headersOverride);
}
requestPausedSessionInfo.session._sendMayFail("Fetch.continueRequest", { requestId: requestPausedEvent.requestId, headers: headersOverride });
} else {
route2 = new RouteImpl(requestPausedSessionInfo.session, requestPausedEvent.requestId);
}
}
const isNavigationRequest = requestWillBeSentEvent.requestId === requestWillBeSentEvent.loaderId && requestWillBeSentEvent.type === "Document";
const documentId = isNavigationRequest ? requestWillBeSentEvent.loaderId : void 0;
const request2 = new InterceptableRequest({
session: requestWillBeSentSessionInfo.session,
context: (this._page || this._serviceWorker).browserContext,
frame: frame || null,
serviceWorker: this._serviceWorker || null,
documentId,
route: route2,
requestWillBeSentEvent,
requestPausedEvent,
redirectedFrom,
headersOverride: headersOverride || null
});
this._requestIdToRequest.set(requestWillBeSentEvent.requestId, request2);
if (route2) {
request2.request.setRawRequestHeaders(headersObjectToArray(requestPausedEvent.request.headers, "\n"));
}
(this._page?.frameManager || this._serviceWorker).requestStarted(request2.request, route2 || void 0);
}
_createResponse(request2, responsePayload, hasExtraInfo) {
const getResponseBody = async () => {
const contentLengthHeader = Object.entries(responsePayload.headers).find((header) => header[0].toLowerCase() === "content-length");
const expectedLength = contentLengthHeader ? +contentLengthHeader[1] : void 0;
const session2 = request2.session;
const response3 = await session2.send("Network.getResponseBody", { requestId: request2._requestId });
if (response3.body || !expectedLength)
return Buffer.from(response3.body, response3.base64Encoded ? "base64" : "utf8");
if (request2._originalRequestRoute?._fulfilled)
return Buffer.from("");
const resource = await session2.send("Network.loadNetworkResource", { url: request2.request.url(), frameId: this._serviceWorker ? void 0 : request2.request.frame()._id, options: { disableCache: false, includeCredentials: true } });
const chunks = [];
while (resource.resource.stream) {
const chunk = await session2.send("IO.read", { handle: resource.resource.stream });
chunks.push(Buffer.from(chunk.data, chunk.base64Encoded ? "base64" : "utf-8"));
if (chunk.eof) {
await session2.send("IO.close", { handle: resource.resource.stream });
break;
}
}
return Buffer.concat(chunks);
};
const timingPayload = responsePayload.timing;
let timing;
if (timingPayload && !this._responseExtraInfoTracker.servedFromCache(request2._requestId)) {
timing = {
startTime: (timingPayload.requestTime - request2._timestamp + request2._wallTime) * 1e3,
domainLookupStart: timingPayload.dnsStart,
domainLookupEnd: timingPayload.dnsEnd,
connectStart: timingPayload.connectStart,
secureConnectionStart: timingPayload.sslStart,
connectEnd: timingPayload.connectEnd,
requestStart: timingPayload.sendStart,
responseStart: timingPayload.receiveHeadersEnd
};
} else {
timing = {
startTime: request2._wallTime * 1e3,
domainLookupStart: -1,
domainLookupEnd: -1,
connectStart: -1,
secureConnectionStart: -1,
connectEnd: -1,
requestStart: -1,
responseStart: -1
};
}
const response2 = new Response2(request2.request, responsePayload.status, responsePayload.statusText, headersObjectToArray(responsePayload.headers), timing, getResponseBody, !!responsePayload.fromServiceWorker);
response2._setHttpVersion(responsePayload?.protocol ?? null);
if (responsePayload?.remoteIPAddress && typeof responsePayload?.remotePort === "number") {
response2._serverAddrFinished({
ipAddress: responsePayload.remoteIPAddress,
port: responsePayload.remotePort
});
} else {
response2._serverAddrFinished();
}
response2._securityDetailsFinished({
protocol: responsePayload?.securityDetails?.protocol,
subjectName: responsePayload?.securityDetails?.subjectName,
issuer: responsePayload?.securityDetails?.issuer,
validFrom: responsePayload?.securityDetails?.validFrom,
validTo: responsePayload?.securityDetails?.validTo
});
this._responseExtraInfoTracker.processResponse(request2._requestId, response2, hasExtraInfo);
return response2;
}
_deleteRequest(request2) {
this._requestIdToRequest.delete(request2._requestId);
if (request2._interceptionId)
this._attemptedAuthentications.delete(request2._interceptionId);
}
_handleRequestRedirect(request2, responsePayload, timestamp, hasExtraInfo) {
const response2 = this._createResponse(request2, responsePayload, hasExtraInfo);
response2.setTransferSize(null);
response2.setEncodedBodySize(null);
response2._requestFinished((timestamp - request2._timestamp) * 1e3);
this._deleteRequest(request2);
(this._page?.frameManager || this._serviceWorker).requestReceivedResponse(response2);
(this._page?.frameManager || this._serviceWorker).reportRequestFinished(request2.request, response2);
}
_onResponseReceivedExtraInfo(event) {
this._responseExtraInfoTracker.responseReceivedExtraInfo(event);
}
_onResponseReceived(sessionInfo, event) {
let request2 = this._requestIdToRequest.get(event.requestId);
if (!request2 && event.response.fromServiceWorker) {
const requestWillBeSentEvent = this._requestIdToRequestWillBeSentEvent.get(event.requestId);
if (requestWillBeSentEvent) {
this._requestIdToRequestWillBeSentEvent.delete(event.requestId);
this._onRequest(sessionInfo, requestWillBeSentEvent.event, void 0, void 0);
request2 = this._requestIdToRequest.get(event.requestId);
}
}
if (!request2)
return;
const response2 = this._createResponse(request2, event.response, event.hasExtraInfo);
(this._page?.frameManager || this._serviceWorker).requestReceivedResponse(response2);
}
_onLoadingFinished(sessionInfo, event) {
this._responseExtraInfoTracker.loadingFinished(event);
const request2 = this._requestIdToRequest.get(event.requestId);
if (!request2)
return;
this._maybeUpdateRequestSession(sessionInfo, request2);
const response2 = request2.request._existingResponse();
if (response2) {
response2.setTransferSize(event.encodedDataLength);
response2.responseHeadersSize().then((size) => response2.setEncodedBodySize(event.encodedDataLength - size));
response2._requestFinished(helper.secondsToRoundishMillis(event.timestamp - request2._timestamp));
}
this._deleteRequest(request2);
(this._page?.frameManager || this._serviceWorker).reportRequestFinished(request2.request, response2);
}
_onLoadingFailed(sessionInfo, event) {
this._responseExtraInfoTracker.loadingFailed(event);
let request2 = this._requestIdToRequest.get(event.requestId);
if (!request2) {
const requestWillBeSentEvent = this._requestIdToRequestWillBeSentEvent.get(event.requestId);
if (requestWillBeSentEvent) {
this._requestIdToRequestWillBeSentEvent.delete(event.requestId);
this._onRequest(sessionInfo, requestWillBeSentEvent.event, void 0, void 0);
request2 = this._requestIdToRequest.get(event.requestId);
}
}
if (!request2)
return;
this._maybeUpdateRequestSession(sessionInfo, request2);
const response2 = request2.request._existingResponse();
if (response2) {
response2.setTransferSize(null);
response2.setEncodedBodySize(null);
response2._requestFinished(helper.secondsToRoundishMillis(event.timestamp - request2._timestamp));
} else {
request2.request.setRawRequestHeaders(null);
}
this._deleteRequest(request2);
request2.request._setFailureText(event.errorText || event.blockedReason || "");
(this._page?.frameManager || this._serviceWorker).requestFailed(request2.request, !!event.canceled);
}
_maybeUpdateRequestSession(sessionInfo, request2) {
if (request2.session !== sessionInfo.session && !sessionInfo.isMain && (request2._documentId === request2._requestId || sessionInfo.workerFrame))
request2.session = sessionInfo.session;
}
};
InterceptableRequest = class {
constructor(options2) {
const { session: session2, context: context2, frame, documentId, route: route2, requestWillBeSentEvent, requestPausedEvent, redirectedFrom, serviceWorker, headersOverride } = options2;
this.session = session2;
this._timestamp = requestWillBeSentEvent.timestamp;
this._wallTime = requestWillBeSentEvent.wallTime;
this._requestId = requestWillBeSentEvent.requestId;
this._interceptionId = requestPausedEvent && requestPausedEvent.requestId;
this._documentId = documentId;
this._originalRequestRoute = route2 ?? redirectedFrom?._originalRequestRoute;
const {
headers,
method,
url: url2,
postDataEntries = null
} = requestPausedEvent ? requestPausedEvent.request : requestWillBeSentEvent.request;
let postDataBuffer = null;
const entries = postDataEntries?.filter((entry) => entry.bytes);
if (entries && entries.length)
postDataBuffer = Buffer.concat(entries.map((entry) => Buffer.from(entry.bytes, "base64")));
this.request = new Request(context2, frame, serviceWorker, redirectedFrom?.request || null, documentId, url2, toResourceType(requestWillBeSentEvent.type || "Other"), method, postDataBuffer, headersOverride || headersObjectToArray(headers));
}
};
RouteImpl = class {
constructor(session2, interceptionId) {
this._fulfilled = false;
this._session = session2;
this._interceptionId = interceptionId;
}
async continue(overrides) {
this._alreadyContinuedParams = {
requestId: this._interceptionId,
url: overrides.url,
headers: overrides.headers,
method: overrides.method,
postData: overrides.postData ? overrides.postData.toString("base64") : void 0
};
await catchDisallowedErrors(async () => {
await this._session.send("Fetch.continueRequest", this._alreadyContinuedParams);
});
}
async fulfill(response2) {
this._fulfilled = true;
const body = response2.isBase64 ? response2.body : Buffer.from(response2.body).toString("base64");
const responseHeaders = splitSetCookieHeader(response2.headers);
await catchDisallowedErrors(async () => {
await this._session.send("Fetch.fulfillRequest", {
requestId: this._interceptionId,
responseCode: response2.status,
responsePhrase: statusText(response2.status),
responseHeaders,
body
});
});
}
async abort(errorCode = "failed") {
const errorReason = errorReasons[errorCode];
assert(errorReason, "Unknown error code: " + errorCode);
await catchDisallowedErrors(async () => {
await this._session.send("Fetch.failRequest", {
requestId: this._interceptionId,
errorReason
});
});
}
};
errorReasons = {
"aborted": "Aborted",
"accessdenied": "AccessDenied",
"addressunreachable": "AddressUnreachable",
"blockedbyclient": "BlockedByClient",
"blockedbyresponse": "BlockedByResponse",
"connectionaborted": "ConnectionAborted",
"connectionclosed": "ConnectionClosed",
"connectionfailed": "ConnectionFailed",
"connectionrefused": "ConnectionRefused",
"connectionreset": "ConnectionReset",
"internetdisconnected": "InternetDisconnected",
"namenotresolved": "NameNotResolved",
"timedout": "TimedOut",
"failed": "Failed"
};
ResponseExtraInfoTracker = class {
constructor() {
this._requests = /* @__PURE__ */ new Map();
}
requestWillBeSentExtraInfo(event) {
const info = this._getOrCreateEntry(event.requestId);
info.requestWillBeSentExtraInfo.push(event);
this._patchHeaders(info, info.requestWillBeSentExtraInfo.length - 1);
this._checkFinished(info);
}
requestServedFromCache(event) {
const info = this._getOrCreateEntry(event.requestId);
info.servedFromCache = true;
}
servedFromCache(requestId) {
const info = this._requests.get(requestId);
return !!info?.servedFromCache;
}
responseReceivedExtraInfo(event) {
const info = this._getOrCreateEntry(event.requestId);
info.responseReceivedExtraInfo.push(event);
this._patchHeaders(info, info.responseReceivedExtraInfo.length - 1);
this._checkFinished(info);
}
processResponse(requestId, response2, hasExtraInfo) {
let info = this._requests.get(requestId);
if (!hasExtraInfo || info?.servedFromCache) {
response2.request().setRawRequestHeaders(null);
response2.setResponseHeadersSize(null);
response2.setRawResponseHeaders(null);
return;
}
info = this._getOrCreateEntry(requestId);
info.responses.push(response2);
this._patchHeaders(info, info.responses.length - 1);
}
loadingFinished(event) {
const info = this._requests.get(event.requestId);
if (!info)
return;
info.loadingFinished = event;
this._checkFinished(info);
}
loadingFailed(event) {
const info = this._requests.get(event.requestId);
if (!info)
return;
info.loadingFailed = event;
this._checkFinished(info);
}
_getOrCreateEntry(requestId) {
let info = this._requests.get(requestId);
if (!info) {
info = {
requestId,
requestWillBeSentExtraInfo: [],
responseReceivedExtraInfo: [],
responses: []
};
this._requests.set(requestId, info);
}
return info;
}
_patchHeaders(info, index) {
const response2 = info.responses[index];
const requestExtraInfo = info.requestWillBeSentExtraInfo[index];
if (response2 && requestExtraInfo) {
response2.request().setRawRequestHeaders(headersObjectToArray(requestExtraInfo.headers, "\n"));
info.requestWillBeSentExtraInfo[index] = void 0;
}
const responseExtraInfo = info.responseReceivedExtraInfo[index];
if (response2 && responseExtraInfo) {
response2.setResponseHeadersSize(responseExtraInfo.headersText?.length || 0);
response2.setRawResponseHeaders(headersObjectToArray(responseExtraInfo.headers, "\n"));
info.responseReceivedExtraInfo[index] = void 0;
}
}
_checkFinished(info) {
if (!info.loadingFinished && !info.loadingFailed)
return;
if (info.responses.length <= info.responseReceivedExtraInfo.length) {
this._stopTracking(info.requestId);
return;
}
}
_stopTracking(requestId) {
this._requests.delete(requestId);
}
};
}
});
// packages/playwright-core/src/server/chromium/crPdf.ts
function convertPrintParameterToInches(text2) {
if (text2 === void 0)
return void 0;
let unit = text2.substring(text2.length - 2).toLowerCase();
let valueText = "";
if (unitToPixels.hasOwnProperty(unit)) {
valueText = text2.substring(0, text2.length - 2);
} else {
unit = "px";
valueText = text2;
}
const value2 = Number(valueText);
assert(!isNaN(value2), "Failed to parse parameter value: " + text2);
const pixels = value2 * unitToPixels[unit];
return pixels / 96;
}
var PagePaperFormats, unitToPixels, CRPDF;
var init_crPdf = __esm({
"packages/playwright-core/src/server/chromium/crPdf.ts"() {
"use strict";
init_assert();
init_crProtocolHelper();
PagePaperFormats = {
letter: { width: 8.5, height: 11 },
legal: { width: 8.5, height: 14 },
tabloid: { width: 11, height: 17 },
ledger: { width: 17, height: 11 },
a0: { width: 33.1, height: 46.8 },
a1: { width: 23.4, height: 33.1 },
a2: { width: 16.54, height: 23.4 },
a3: { width: 11.7, height: 16.54 },
a4: { width: 8.27, height: 11.7 },
a5: { width: 5.83, height: 8.27 },
a6: { width: 4.13, height: 5.83 }
};
unitToPixels = {
"px": 1,
"in": 96,
"cm": 37.8,
"mm": 3.78
};
CRPDF = class {
constructor(client) {
this._client = client;
}
async generate(options2) {
const {
scale = 1,
displayHeaderFooter = false,
headerTemplate = "",
footerTemplate = "",
printBackground = false,
landscape = false,
pageRanges = "",
preferCSSPageSize = false,
margin = {},
tagged = false,
outline = false
} = options2;
let paperWidth = 8.5;
let paperHeight = 11;
if (options2.format) {
const format2 = PagePaperFormats[options2.format.toLowerCase()];
assert(format2, "Unknown paper format: " + options2.format);
paperWidth = format2.width;
paperHeight = format2.height;
} else {
paperWidth = convertPrintParameterToInches(options2.width) || paperWidth;
paperHeight = convertPrintParameterToInches(options2.height) || paperHeight;
}
const marginTop = convertPrintParameterToInches(margin.top) || 0;
const marginLeft = convertPrintParameterToInches(margin.left) || 0;
const marginBottom = convertPrintParameterToInches(margin.bottom) || 0;
const marginRight = convertPrintParameterToInches(margin.right) || 0;
const generateDocumentOutline = outline;
const generateTaggedPDF = tagged;
const result2 = await this._client.send("Page.printToPDF", {
transferMode: "ReturnAsStream",
landscape,
displayHeaderFooter,
headerTemplate,
footerTemplate,
printBackground,
scale,
paperWidth,
paperHeight,
marginTop,
marginBottom,
marginLeft,
marginRight,
pageRanges,
preferCSSPageSize,
generateTaggedPDF,
generateDocumentOutline
});
return await readProtocolStream(this._client, result2.stream);
}
};
}
});
// packages/playwright-core/src/server/chromium/defaultFontFamilies.ts
var platformToFontFamilies;
var init_defaultFontFamilies = __esm({
"packages/playwright-core/src/server/chromium/defaultFontFamilies.ts"() {
"use strict";
platformToFontFamilies = {
"linux": {
"fontFamilies": {
"standard": "Times New Roman",
"fixed": "Monospace",
"serif": "Times New Roman",
"sansSerif": "Arial",
"cursive": "Comic Sans MS",
"fantasy": "Impact"
}
},
"mac": {
"fontFamilies": {
"standard": "Times",
"fixed": "Courier",
"serif": "Times",
"sansSerif": "Helvetica",
"cursive": "Apple Chancery",
"fantasy": "Papyrus"
},
"forScripts": [
{
"script": "jpan",
"fontFamilies": {
"standard": "Hiragino Kaku Gothic ProN",
"fixed": "Osaka-Mono",
"serif": "Hiragino Mincho ProN",
"sansSerif": "Hiragino Kaku Gothic ProN"
}
},
{
"script": "hang",
"fontFamilies": {
"standard": "Apple SD Gothic Neo",
"serif": "AppleMyungjo",
"sansSerif": "Apple SD Gothic Neo"
}
},
{
"script": "hans",
"fontFamilies": {
"standard": ",PingFang SC,STHeiti",
"serif": "Songti SC",
"sansSerif": ",PingFang SC,STHeiti",
"cursive": "Kaiti SC"
}
},
{
"script": "hant",
"fontFamilies": {
"standard": ",PingFang TC,Heiti TC",
"serif": "Songti TC",
"sansSerif": ",PingFang TC,Heiti TC",
"cursive": "Kaiti TC"
}
}
]
},
"win": {
"fontFamilies": {
"standard": "Times New Roman",
"fixed": "Consolas",
"serif": "Times New Roman",
"sansSerif": "Arial",
"cursive": "Comic Sans MS",
"fantasy": "Impact"
},
"forScripts": [
{
"script": "cyrl",
"fontFamilies": {
"standard": "Times New Roman",
"fixed": "Courier New",
"serif": "Times New Roman",
"sansSerif": "Arial"
}
},
{
"script": "arab",
"fontFamilies": {
"fixed": "Courier New",
"sansSerif": "Segoe UI"
}
},
{
"script": "grek",
"fontFamilies": {
"standard": "Times New Roman",
"fixed": "Courier New",
"serif": "Times New Roman",
"sansSerif": "Arial"
}
},
{
"script": "jpan",
"fontFamilies": {
"standard": ",Meiryo,Yu Gothic",
"fixed": "MS Gothic",
"serif": ",Yu Mincho,MS PMincho",
"sansSerif": ",Meiryo,Yu Gothic"
}
},
{
"script": "hang",
"fontFamilies": {
"standard": "Malgun Gothic",
"fixed": "Gulimche",
"serif": "Batang",
"sansSerif": "Malgun Gothic",
"cursive": "Gungsuh"
}
},
{
"script": "hans",
"fontFamilies": {
"standard": "Microsoft YaHei",
"fixed": "NSimsun",
"serif": "Simsun",
"sansSerif": "Microsoft YaHei",
"cursive": "KaiTi"
}
},
{
"script": "hant",
"fontFamilies": {
"standard": "Microsoft JhengHei",
"fixed": "MingLiU",
"serif": "PMingLiU",
"sansSerif": "Microsoft JhengHei",
"cursive": "DFKai-SB"
}
}
]
}
};
}
});
// packages/playwright-core/src/server/videoRecorder.ts
function startAutomaticVideoRecording(page) {
const recordVideo = page.browserContext._options.recordVideo;
if (!recordVideo)
return;
const recorder = new VideoRecorder(page.screencast);
if (page.browserContext._options.recordVideo?.showActions)
page.screencast.showActions(page.browserContext._options.recordVideo?.showActions);
const dir = recordVideo.dir ?? page.browserContext._browser.options.artifactsDir;
const artifact = recorder.start({ size: recordVideo.size, fileName: import_path21.default.join(dir, page.guid + ".webm") });
page.video = artifact;
}
function createWhiteImage(width, height) {
const data = Buffer.alloc(width * height * 4, 255);
return jpegjs2.encode({ data, width, height }, 80).data;
}
var import_path21, jpegjs2, fps, VideoRecorder, FfmpegVideoRecorder;
var init_videoRecorder = __esm({
"packages/playwright-core/src/server/videoRecorder.ts"() {
"use strict";
import_path21 = __toESM(require("path"));
init_processLauncher();
init_assert();
init_crypto();
init_debugLogger();
init_fileUtils();
init_time();
init_artifact();
init_registry();
jpegjs2 = require("./utilsBundle").jpegjs;
fps = 25;
VideoRecorder = class {
constructor(screencast) {
this._screencast = screencast;
}
start(options2) {
assert(!this._artifact);
const ffmpegPath = registry.findExecutable("ffmpeg").executablePathOrDie(this._screencast.page.browserContext._browser.sdkLanguage());
const outputFile2 = options2.fileName ?? import_path21.default.join(this._screencast.page.browserContext._browser.options.artifactsDir, createGuid() + ".webm");
this._client = {
onFrame: (frame) => this._videoRecorder.writeFrame(frame.buffer, frame.frameSwapWallTime / 1e3),
gracefulClose: () => this.stop(),
dispose: () => this.stop().catch((e) => debugLogger.log("error", `Failed to stop video recorder: ${String(e)}`)),
size: options2.size
};
const { size } = this._screencast.addClient(this._client);
const videoSize = options2.size ?? size;
this._videoRecorder = new FfmpegVideoRecorder(ffmpegPath, videoSize, outputFile2);
this._artifact = new Artifact(this._screencast.page.browserContext, outputFile2);
return this._artifact;
}
async stop() {
if (!this._artifact)
return;
const artifact = this._artifact;
this._artifact = void 0;
const client = this._client;
this._client = void 0;
const videoRecorder = this._videoRecorder;
this._videoRecorder = void 0;
this._screencast.removeClient(client);
await videoRecorder._stop();
await artifact.reportFinished();
}
};
FfmpegVideoRecorder = class {
constructor(ffmpegPath, size, outputFile2) {
this._process = null;
this._gracefullyClose = null;
this._lastWritePromise = Promise.resolve();
this._firstFrameTimestamp = 0;
this._lastFrame = null;
this._lastWriteNodeTime = 0;
this._frameQueue = [];
this._isStopped = false;
if (!outputFile2.endsWith(".webm"))
throw new Error("File must have .webm extension");
this._outputFile = outputFile2;
this._ffmpegPath = ffmpegPath;
this._size = size;
this._launchPromise = this._launch().catch((e) => e);
}
async _launch() {
await mkdirIfNeeded(this._outputFile);
const w = this._size.width;
const h = this._size.height;
const args = `-loglevel error -f image2pipe -avioflags direct -fpsprobesize 0 -probesize 32 -analyzeduration 0 -c:v mjpeg -i pipe:0 -y -an -r ${fps} -c:v vp8 -qmin 0 -qmax 50 -crf 8 -deadline realtime -speed 8 -b:v 1M -threads 1 -vf pad=${w}:${h}:0:0:gray,crop=${w}:${h}:0:0`.split(" ");
args.push(this._outputFile);
const { launchedProcess, gracefullyClose } = await launchProcess({
command: this._ffmpegPath,
args,
stdio: "stdin",
log: (message) => debugLogger.log("browser", message),
tempDirectories: [],
attemptToGracefullyClose: async () => {
debugLogger.log("browser", "Closing stdin...");
launchedProcess.stdin.end();
},
onExit: (exitCode, signal) => {
debugLogger.log("browser", `ffmpeg onkill exitCode=${exitCode} signal=${signal}`);
}
});
launchedProcess.stdin.on("finish", () => {
debugLogger.log("browser", "ffmpeg finished input.");
});
launchedProcess.stdin.on("error", () => {
debugLogger.log("browser", "ffmpeg error.");
});
this._process = launchedProcess;
this._gracefullyClose = gracefullyClose;
}
writeFrame(frame, timestamp) {
this._launchPromise.then((error) => {
if (error)
return;
this._writeFrame(frame, timestamp);
});
}
_writeFrame(frame, timestamp) {
assert(this._process);
if (this._isStopped)
return;
if (!this._firstFrameTimestamp)
this._firstFrameTimestamp = timestamp;
const frameNumber = Math.floor((timestamp - this._firstFrameTimestamp) * fps);
if (this._lastFrame) {
const repeatCount = frameNumber - this._lastFrame.frameNumber;
for (let i = 0; i < repeatCount; ++i)
this._frameQueue.push(this._lastFrame.buffer);
this._lastWritePromise = this._lastWritePromise.then(() => this._sendFrames());
}
this._lastFrame = { buffer: frame, timestamp, frameNumber };
this._lastWriteNodeTime = monotonicTime();
}
async _sendFrames() {
while (this._frameQueue.length)
await this._sendFrame(this._frameQueue.shift());
}
async _sendFrame(frame) {
return new Promise((f) => this._process.stdin.write(frame, f)).then((error) => {
if (error)
debugLogger.log("browser", `ffmpeg failed to write: ${String(error)}`);
});
}
async _stop() {
const error = await this._launchPromise;
if (error)
throw error;
if (this._isStopped)
return;
if (!this._lastFrame) {
this._writeFrame(createWhiteImage(this._size.width, this._size.height), monotonicTime());
}
const addTime = Math.max((monotonicTime() - this._lastWriteNodeTime) / 1e3, 1);
this._writeFrame(Buffer.from([]), this._lastFrame.timestamp + addTime);
this._isStopped = true;
try {
await this._lastWritePromise;
await this._gracefullyClose();
} catch (e) {
debugLogger.log("error", `ffmpeg failed to stop: ${String(e)}`);
}
}
};
}
});
// packages/playwright-core/src/server/chromium/crPage.ts
async function emulateLocale(session2, locale) {
try {
await session2.send("Emulation.setLocaleOverride", { locale });
} catch (exception) {
if (exception.message.includes("Another locale override is already in effect"))
return;
throw exception;
}
}
async function emulateTimezone(session2, timezoneId) {
try {
await session2.send("Emulation.setTimezoneOverride", { timezoneId });
} catch (exception) {
if (exception.message.includes("Timezone override is already in effect"))
return;
if (exception.message.includes("Invalid timezone"))
throw new Error(`Invalid timezone ID: ${timezoneId}`);
throw exception;
}
}
function calculateUserAgentMetadata(options2) {
const ua = options2.userAgent;
if (!ua)
return void 0;
const metadata = {
mobile: !!options2.isMobile,
model: "",
architecture: "x86",
platform: "Windows",
platformVersion: ""
};
const androidMatch = ua.match(/Android (\d+(\.\d+)?(\.\d+)?)/);
const iPhoneMatch = ua.match(/iPhone OS (\d+(_\d+)?)/);
const iPadMatch = ua.match(/iPad; CPU OS (\d+(_\d+)?)/);
const macOSMatch = ua.match(/Mac OS X (\d+(_\d+)?(_\d+)?)/);
const windowsMatch = ua.match(/Windows\D+(\d+(\.\d+)?(\.\d+)?)/);
if (androidMatch) {
metadata.platform = "Android";
metadata.platformVersion = androidMatch[1];
metadata.architecture = "arm";
} else if (iPhoneMatch) {
metadata.platform = "iOS";
metadata.platformVersion = iPhoneMatch[1];
metadata.architecture = "arm";
} else if (iPadMatch) {
metadata.platform = "iOS";
metadata.platformVersion = iPadMatch[1];
metadata.architecture = "arm";
} else if (macOSMatch) {
metadata.platform = "macOS";
metadata.platformVersion = macOSMatch[1];
if (!ua.includes("Intel"))
metadata.architecture = "arm";
} else if (windowsMatch) {
metadata.platform = "Windows";
metadata.platformVersion = windowsMatch[1];
} else if (ua.toLowerCase().includes("linux")) {
metadata.platform = "Linux";
}
if (ua.includes("ARM"))
metadata.architecture = "arm";
return metadata;
}
var CRPage, FrameSession;
var init_crPage = __esm({
"packages/playwright-core/src/server/chromium/crPage.ts"() {
"use strict";
init_assert();
init_stackTrace();
init_eventsHelper();
init_dialog();
init_dom();
init_frames();
init_helper();
init_network2();
init_page();
init_crCoverage();
init_crDragDrop();
init_crExecutionContext();
init_crInput();
init_crNetworkManager();
init_crPdf();
init_crProtocolHelper();
init_defaultFontFamilies();
init_errors();
init_protocolError();
init_videoRecorder();
init_progress();
CRPage = class {
constructor(client, targetId, browserContext, opener, bits) {
this._sessions = /* @__PURE__ */ new Map();
// Holds window features for the next popup being opened via window.open,
// until the popup target arrives. This could be racy if two oopifs
// simultaneously call window.open with window features: the order
// of their Page.windowOpen events is not guaranteed to match the order
// of new popup targets.
this._nextWindowOpenPopupFeatures = [];
this._targetId = targetId;
this._opener = opener;
const dragManager = new DragManager(this);
this.rawKeyboard = new RawKeyboardImpl(client, browserContext._browser._platform() === "mac", dragManager);
this.rawMouse = new RawMouseImpl(this, client, dragManager);
this.rawTouchscreen = new RawTouchscreenImpl(client);
this._pdf = new CRPDF(client);
this._coverage = new CRCoverage(client);
this._browserContext = browserContext;
this._page = new Page(this, browserContext);
this.utilityWorldName = `__playwright_utility_world_${this._page.guid}`;
this._networkManager = new CRNetworkManager(this._page, null);
this.updateOffline();
this.updateExtraHTTPHeaders();
this.updateHttpCredentials();
this.updateRequestInterception();
this._mainFrameSession = new FrameSession(this, client, targetId, null);
this._sessions.set(targetId, this._mainFrameSession);
if (opener && !browserContext._options.noDefaultViewport) {
const features = opener._nextWindowOpenPopupFeatures.shift() || [];
const viewportSize = helper.getViewportSizeFromWindowFeatures(features);
if (viewportSize)
this._page.setEmulatedSizeFromWindowOpen({ viewport: viewportSize, screen: viewportSize });
}
this._mainFrameSession._initialize(bits.hasUIWindow).then(
() => this._page.reportAsNew(this._opener?._page, void 0),
(error) => this._page.reportAsNew(this._opener?._page, error)
);
}
static mainFrameSession(page) {
const crPage = page.delegate;
return crPage._mainFrameSession;
}
async _forAllFrameSessions(cb) {
const frameSessions = Array.from(this._sessions.values());
await Promise.all(frameSessions.map((frameSession) => {
if (frameSession._isMainFrame())
return cb(frameSession);
return cb(frameSession).catch((e) => {
if (isSessionClosedError(e))
return;
throw e;
});
}));
}
_sessionForFrame(frame) {
while (!this._sessions.has(frame._id)) {
const parent = frame.parentFrame();
if (!parent)
throw new Error(`Frame has been detached.`);
frame = parent;
}
return this._sessions.get(frame._id);
}
_sessionForHandle(handle) {
const frame = handle._context.frame;
return this._sessionForFrame(frame);
}
willBeginDownload() {
this._mainFrameSession._willBeginDownload();
}
didClose() {
for (const session2 of this._sessions.values())
session2.dispose();
this._page._didClose();
}
async navigateFrame(frame, url2, referrer) {
return this._sessionForFrame(frame)._navigate(frame, url2, referrer);
}
async updateExtraHTTPHeaders() {
const headers = mergeHeaders([
this._browserContext._options.extraHTTPHeaders,
this._page.extraHTTPHeaders()
]);
await this._networkManager.setExtraHTTPHeaders(headers);
}
async updateGeolocation() {
await this._forAllFrameSessions((frame) => frame._updateGeolocation(false));
}
async updateOffline() {
await this._networkManager.setOffline(!!this._browserContext._options.offline);
}
async updateHttpCredentials() {
await this._networkManager.authenticate(this._browserContext._options.httpCredentials || null);
}
async updateEmulatedViewportSize(preserveWindowBoundaries) {
await this._mainFrameSession._updateViewport(preserveWindowBoundaries);
}
async bringToFront() {
await this._mainFrameSession._client.send("Page.bringToFront");
}
async updateEmulateMedia() {
await this._forAllFrameSessions((frame) => frame._updateEmulateMedia());
}
async updateUserAgent() {
await this._forAllFrameSessions((frame) => frame._updateUserAgent());
}
async updateRequestInterception() {
await this._networkManager.setRequestInterception(this._page.needsRequestInterception());
}
async updateFileChooserInterception() {
await this._forAllFrameSessions((frame) => frame._updateFileChooserInterception(false));
}
async reload() {
await this._mainFrameSession._client.send("Page.reload");
}
async _go(delta) {
const history = await this._mainFrameSession._client.send("Page.getNavigationHistory");
const entry = history.entries[history.currentIndex + delta];
if (!entry)
return false;
await this._mainFrameSession._client.send("Page.navigateToHistoryEntry", { entryId: entry.id });
return true;
}
goBack() {
return this._go(-1);
}
goForward() {
return this._go(1);
}
async requestGC() {
await this._mainFrameSession._client.send("HeapProfiler.collectGarbage");
}
async addInitScript(initScript, world = "main") {
await this._forAllFrameSessions((frame) => frame._evaluateOnNewDocument(initScript, world));
}
async exposePlaywrightBinding() {
await this._forAllFrameSessions((frame) => frame.exposePlaywrightBinding());
}
async removeInitScripts(initScripts) {
await this._forAllFrameSessions((frame) => frame._removeEvaluatesOnNewDocument(initScripts));
}
async closePage(runBeforeUnload) {
if (runBeforeUnload)
await this._mainFrameSession._client.send("Page.close");
else
await this._browserContext._browser._closePage(this);
}
async setBackgroundColor(color) {
await this._mainFrameSession._client.send("Emulation.setDefaultBackgroundColorOverride", { color });
}
async takeScreenshot(progress2, format2, documentRect, viewportRect, quality, fitsViewport, scale) {
const { visualViewport, contentSize, cssContentSize } = await progress2.race(this._mainFrameSession._client.send("Page.getLayoutMetrics"));
if (!documentRect) {
documentRect = {
x: visualViewport.pageX + viewportRect.x,
y: visualViewport.pageY + viewportRect.y,
...helper.enclosingIntSize({
width: viewportRect.width / visualViewport.scale,
height: viewportRect.height / visualViewport.scale
})
};
}
const clip = { ...documentRect, scale: viewportRect ? visualViewport.scale : 1 };
if (scale === "css") {
const deviceScaleFactor = this._mainFrameSession._metricsOverride?.deviceScaleFactor || contentSize.width / cssContentSize.width || 1;
clip.scale /= deviceScaleFactor;
}
const result2 = await progress2.race(this._mainFrameSession._client.send("Page.captureScreenshot", { format: format2, quality, clip, captureBeyondViewport: !fitsViewport }));
return Buffer.from(result2.data, "base64");
}
async getContentFrame(handle) {
return this._sessionForHandle(handle)._getContentFrame(handle);
}
async getOwnerFrame(handle) {
return this._sessionForHandle(handle)._getOwnerFrame(handle);
}
async getBoundingBox(handle) {
return this._sessionForHandle(handle)._getBoundingBox(handle);
}
async scrollRectIntoViewIfNeeded(handle, rect) {
return this._sessionForHandle(handle)._scrollRectIntoViewIfNeeded(handle, rect);
}
startScreencast(options2) {
this._mainFrameSession._client.send("Page.startScreencast", {
format: "jpeg",
quality: options2.quality,
maxWidth: options2.width,
maxHeight: options2.height
}).catch(() => {
});
}
stopScreencast() {
this._mainFrameSession._client._sendMayFail("Page.stopScreencast").catch(() => {
});
}
rafCountForStablePosition() {
return 1;
}
async getContentQuads(handle) {
return this._sessionForHandle(handle)._getContentQuads(handle);
}
async setInputFilePaths(progress2, handle, files) {
const frame = await handle.ownerFrame(progress2);
if (!frame)
throw new Error("Cannot set input files to detached input element");
const parentSession = this._sessionForFrame(frame);
await progress2.race(parentSession._client.send("DOM.setFileInputFiles", {
objectId: handle._objectId,
files
}));
}
async adoptElementHandle(handle, to) {
return this._sessionForHandle(handle)._adoptElementHandle(handle, to);
}
async inputActionEpilogue() {
await this._mainFrameSession._client.send("Page.enable").catch((e) => {
});
}
async resetForReuse(progress2) {
await this.rawMouse.move(progress2, -1, -1, "none", /* @__PURE__ */ new Set(), /* @__PURE__ */ new Set(), true);
}
async pdf(options2) {
return this._pdf.generate(options2);
}
coverage() {
return this._coverage;
}
async getFrameElement(frame) {
let parent = frame.parentFrame();
if (!parent)
throw new Error("Frame has been detached.");
const parentSession = this._sessionForFrame(parent);
const { backendNodeId } = await parentSession._client.send("DOM.getFrameOwner", { frameId: frame._id }).catch((e) => {
if (e instanceof Error && e.message.includes("Frame with the given id was not found."))
rewriteErrorMessage(e, "Frame has been detached.");
throw e;
});
parent = frame.parentFrame();
if (!parent)
throw new Error("Frame has been detached.");
return parentSession._adoptBackendNodeId(backendNodeId, await parent.mainContext());
}
shouldToggleStyleSheetToSyncAnimations() {
return false;
}
async setDockTile(image) {
await this._mainFrameSession._client.send("Browser.setDockTile", { image: image.toString("base64") });
}
};
FrameSession = class _FrameSession {
constructor(crPage, client, targetId, parentSession) {
this._childSessions = /* @__PURE__ */ new Set();
this._contextIdToContext = /* @__PURE__ */ new Map();
this._eventListeners = [];
this._firstNonInitialNavigationCommittedFulfill = () => {
};
this._firstNonInitialNavigationCommittedReject = (e) => {
};
// Marks the oopif session that remote -> local transition has happened in the parent.
// See Target.detachedFromTarget handler for details.
this._swappedIn = false;
this._workerSessions = /* @__PURE__ */ new Map();
this._initScriptIds = /* @__PURE__ */ new Map();
this._client = client;
this._crPage = crPage;
this._page = crPage._page;
this._targetId = targetId;
this._parentSession = parentSession;
if (parentSession)
parentSession._childSessions.add(this);
this._firstNonInitialNavigationCommittedPromise = new Promise((f, r) => {
this._firstNonInitialNavigationCommittedFulfill = f;
this._firstNonInitialNavigationCommittedReject = r;
});
this._firstNonInitialNavigationCommittedPromise.catch(() => {
});
}
_isMainFrame() {
return this._targetId === this._crPage._targetId;
}
_addRendererListeners() {
this._eventListeners.push(...[
eventsHelper.addEventListener(this._client, "Log.entryAdded", (event) => this._onLogEntryAdded(event)),
eventsHelper.addEventListener(this._client, "Page.fileChooserOpened", (event) => this._onFileChooserOpened(event)),
eventsHelper.addEventListener(this._client, "Page.frameAttached", (event) => this._onFrameAttached(event.frameId, event.parentFrameId)),
eventsHelper.addEventListener(this._client, "Page.frameDetached", (event) => this._onFrameDetached(event.frameId, event.reason)),
eventsHelper.addEventListener(this._client, "Page.frameNavigated", (event) => this._onFrameNavigated(event.frame, false)),
eventsHelper.addEventListener(this._client, "Page.frameRequestedNavigation", (event) => this._onFrameRequestedNavigation(event)),
eventsHelper.addEventListener(this._client, "Page.javascriptDialogOpening", (event) => this._onDialog(event)),
eventsHelper.addEventListener(this._client, "Page.navigatedWithinDocument", (event) => this._onFrameNavigatedWithinDocument(event.frameId, event.url)),
eventsHelper.addEventListener(this._client, "Runtime.bindingCalled", (event) => this._onBindingCalled(event)),
eventsHelper.addEventListener(this._client, "Runtime.consoleAPICalled", (event) => this._onConsoleAPI(event)),
eventsHelper.addEventListener(this._client, "Runtime.exceptionThrown", (exception) => this._handleException(exception.exceptionDetails)),
eventsHelper.addEventListener(this._client, "Runtime.executionContextCreated", (event) => this._onExecutionContextCreated(event.context)),
eventsHelper.addEventListener(this._client, "Runtime.executionContextDestroyed", (event) => this._onExecutionContextDestroyed(event.executionContextId)),
eventsHelper.addEventListener(this._client, "Runtime.executionContextsCleared", (event) => this._onExecutionContextsCleared())
]);
}
_addBrowserListeners() {
this._eventListeners.push(...[
eventsHelper.addEventListener(this._client, "Target.attachedToTarget", (event) => this._onAttachedToTarget(event)),
eventsHelper.addEventListener(this._client, "Target.detachedFromTarget", (event) => this._onDetachedFromTarget(event)),
eventsHelper.addEventListener(this._client, "Inspector.targetCrashed", (event) => this._onTargetCrashed()),
eventsHelper.addEventListener(this._client, "Page.screencastFrame", (event) => this._onScreencastFrame(event)),
eventsHelper.addEventListener(this._client, "Page.windowOpen", (event) => this._onWindowOpen(event))
]);
}
async _initialize(hasUIWindow) {
if (!this._page.isStorageStatePage && hasUIWindow && !this._crPage._browserContext._browser.isClank() && !this._crPage._browserContext._options.noDefaultViewport) {
try {
const { windowId } = await this._client.send("Browser.getWindowForTarget");
this._windowId = windowId;
} catch {
}
}
if (this._isMainFrame() && hasUIWindow && !this._page.isStorageStatePage)
startAutomaticVideoRecording(this._crPage._page);
let lifecycleEventsEnabled;
if (!this._isMainFrame())
this._addRendererListeners();
this._addBrowserListeners();
this._bufferedAttachedToTargetEvents = [];
const promises = [
this._client.send("Page.enable"),
this._client.send("Page.getFrameTree").then(({ frameTree }) => {
if (this._isMainFrame()) {
this._handleFrameTree(frameTree);
this._addRendererListeners();
}
const attachedToTargetEvents = this._bufferedAttachedToTargetEvents || [];
this._bufferedAttachedToTargetEvents = void 0;
for (const event of attachedToTargetEvents)
this._onAttachedToTarget(event);
const localFrames = this._isMainFrame() ? this._page.frames() : [this._page.frameManager.frame(this._targetId)];
for (const frame of localFrames) {
this._client._sendMayFail("Page.createIsolatedWorld", {
frameId: frame._id,
grantUniveralAccess: true,
worldName: this._crPage.utilityWorldName
});
}
const isInitialEmptyPage = this._isMainFrame() && this._page.mainFrame().url() === ":";
if (isInitialEmptyPage) {
lifecycleEventsEnabled.catch((e) => {
}).then(() => {
this._eventListeners.push(eventsHelper.addEventListener(this._client, "Page.lifecycleEvent", (event) => this._onLifecycleEvent(event)));
});
} else {
this._firstNonInitialNavigationCommittedFulfill();
this._eventListeners.push(eventsHelper.addEventListener(this._client, "Page.lifecycleEvent", (event) => this._onLifecycleEvent(event)));
}
}),
this._client.send("Log.enable", {}),
lifecycleEventsEnabled = this._client.send("Page.setLifecycleEventsEnabled", { enabled: true }),
this._client.send("Runtime.enable", {}),
this._client.send("Page.addScriptToEvaluateOnNewDocument", {
source: "",
worldName: this._crPage.utilityWorldName
}),
this._crPage._networkManager.addSession(this._client, void 0, this._isMainFrame()),
this._client.send("Target.setAutoAttach", { autoAttach: true, waitForDebuggerOnStart: true, flatten: true })
];
if (!this._page.isStorageStatePage) {
const skipDefaultOverrides = this._crPage._browserContext._browser.options.noDefaults && this._crPage._browserContext === this._crPage._browserContext._browser._defaultContext;
if (this._crPage._browserContext.needsPlaywrightBinding())
promises.push(this.exposePlaywrightBinding());
if (this._isMainFrame() && !skipDefaultOverrides)
promises.push(this._client.send("Emulation.setFocusEmulationEnabled", { enabled: true }));
const options2 = this._crPage._browserContext._options;
if (options2.bypassCSP)
promises.push(this._client.send("Page.setBypassCSP", { enabled: true }));
if (options2.ignoreHTTPSErrors || options2.internalIgnoreHTTPSErrors)
promises.push(this._client.send("Security.setIgnoreCertificateErrors", { ignore: true }));
if (this._isMainFrame())
promises.push(this._updateViewport());
if (options2.hasTouch)
promises.push(this._client.send("Emulation.setTouchEmulationEnabled", { enabled: true }));
if (options2.javaScriptEnabled === false)
promises.push(this._client.send("Emulation.setScriptExecutionDisabled", { value: true }));
if (options2.userAgent || options2.locale)
promises.push(this._updateUserAgent());
if (options2.locale)
promises.push(emulateLocale(this._client, options2.locale));
if (options2.timezoneId)
promises.push(emulateTimezone(this._client, options2.timezoneId));
if (!this._crPage._browserContext._browser.options.headful)
promises.push(this._setDefaultFontFamilies(this._client));
promises.push(this._updateGeolocation(true));
if (!skipDefaultOverrides)
promises.push(this._updateEmulateMedia());
promises.push(this._updateFileChooserInterception(true));
for (const initScript of this._crPage._page.allInitScripts())
promises.push(this._evaluateOnNewDocument(
initScript,
"main",
true
/* runImmediately */
));
}
promises.push(this._client.send("Runtime.runIfWaitingForDebugger"));
promises.push(this._firstNonInitialNavigationCommittedPromise);
await Promise.all(promises);
}
dispose() {
this._firstNonInitialNavigationCommittedReject(new TargetClosedError(this._page.closeReason()));
for (const childSession of this._childSessions)
childSession.dispose();
if (this._parentSession)
this._parentSession._childSessions.delete(this);
eventsHelper.removeEventListeners(this._eventListeners);
this._crPage._networkManager.removeSession(this._client);
this._crPage._sessions.delete(this._targetId);
this._client.dispose();
}
async _navigate(frame, url2, referrer) {
const response2 = await this._client.send("Page.navigate", { url: url2, referrer, frameId: frame._id, referrerPolicy: "unsafeUrl" });
if (response2.isDownload)
throw new NavigationAbortedError(response2.loaderId, "Download is starting");
if (response2.errorText)
throw new NavigationAbortedError(response2.loaderId, `${response2.errorText} at ${url2}`);
return { newDocumentId: response2.loaderId };
}
_onLifecycleEvent(event) {
if (this._eventBelongsToStaleFrame(event.frameId))
return;
if (event.name === "load")
this._page.frameManager.frameLifecycleEvent(event.frameId, "load");
else if (event.name === "DOMContentLoaded")
this._page.frameManager.frameLifecycleEvent(event.frameId, "domcontentloaded");
}
_handleFrameTree(frameTree) {
this._onFrameAttached(frameTree.frame.id, frameTree.frame.parentId || null);
this._onFrameNavigated(frameTree.frame, true);
if (!frameTree.childFrames)
return;
for (const child of frameTree.childFrames)
this._handleFrameTree(child);
}
_eventBelongsToStaleFrame(frameId) {
const frame = this._page.frameManager.frame(frameId);
if (!frame)
return true;
const session2 = this._crPage._sessionForFrame(frame);
return session2 && session2 !== this && !session2._swappedIn;
}
_onFrameAttached(frameId, parentFrameId) {
const frameSession = this._crPage._sessions.get(frameId);
if (frameSession && frameId !== this._targetId) {
frameSession._swappedIn = true;
const frame = this._page.frameManager.frame(frameId);
if (frame)
this._page.frameManager.removeChildFramesRecursively(frame);
return;
}
if (parentFrameId && !this._page.frameManager.frame(parentFrameId)) {
return;
}
this._page.frameManager.frameAttached(frameId, parentFrameId);
}
_onFrameNavigated(framePayload, initial) {
if (this._eventBelongsToStaleFrame(framePayload.id))
return;
this._page.frameManager.frameCommittedNewDocumentNavigation(framePayload.id, framePayload.url + (framePayload.urlFragment || ""), framePayload.name || "", framePayload.loaderId, initial);
if (!initial)
this._firstNonInitialNavigationCommittedFulfill();
}
_onFrameRequestedNavigation(payload) {
if (this._eventBelongsToStaleFrame(payload.frameId))
return;
if (payload.disposition === "currentTab")
this._page.frameManager.frameRequestedNavigation(payload.frameId);
}
_onFrameNavigatedWithinDocument(frameId, url2) {
if (this._eventBelongsToStaleFrame(frameId))
return;
this._page.frameManager.frameCommittedSameDocumentNavigation(frameId, url2);
}
_onFrameDetached(frameId, reason) {
if (this._crPage._sessions.has(frameId)) {
return;
}
if (reason === "swap") {
const frame = this._page.frameManager.frame(frameId);
if (frame)
this._page.frameManager.removeChildFramesRecursively(frame);
return;
}
this._page.frameManager.frameDetached(frameId);
}
_onExecutionContextCreated(contextPayload) {
const frame = contextPayload.auxData ? this._page.frameManager.frame(contextPayload.auxData.frameId) : null;
if (!frame || this._eventBelongsToStaleFrame(frame._id))
return;
const delegate = new CRExecutionContext(this._client, contextPayload);
let worldName = null;
if (contextPayload.auxData && !!contextPayload.auxData.isDefault)
worldName = "main";
else if (contextPayload.name === this._crPage.utilityWorldName)
worldName = "utility";
const context2 = new FrameExecutionContext(delegate, frame, worldName);
if (worldName)
frame.contextCreated(worldName, context2);
this._contextIdToContext.set(contextPayload.id, context2);
}
_onExecutionContextDestroyed(executionContextId) {
const context2 = this._contextIdToContext.get(executionContextId);
if (!context2)
return;
this._contextIdToContext.delete(executionContextId);
context2.frame.contextDestroyed(context2);
}
_onExecutionContextsCleared() {
for (const contextId4 of Array.from(this._contextIdToContext.keys()))
this._onExecutionContextDestroyed(contextId4);
}
_onAttachedToTarget(event) {
if (this._bufferedAttachedToTargetEvents) {
this._bufferedAttachedToTargetEvents.push(event);
return;
}
const session2 = this._client.createChildSession(event.sessionId);
if (event.targetInfo.type === "iframe") {
const targetId = event.targetInfo.targetId;
let frame = this._page.frameManager.frame(targetId);
if (!frame && event.targetInfo.parentFrameId) {
frame = this._page.frameManager.frameAttached(targetId, event.targetInfo.parentFrameId);
}
if (!frame)
return;
this._page.frameManager.removeChildFramesRecursively(frame);
for (const [contextId4, context2] of this._contextIdToContext) {
if (context2.frame === frame)
this._onExecutionContextDestroyed(contextId4);
}
const frameSession = new _FrameSession(this._crPage, session2, targetId, this);
this._crPage._sessions.set(targetId, frameSession);
frameSession._initialize(false).catch((e) => e);
return;
}
if (event.targetInfo.type !== "worker") {
session2.detach().catch(() => {
});
return;
}
const url2 = event.targetInfo.url;
const worker = new Worker(this._page, url2);
this._page.addWorker(event.sessionId, worker);
this._workerSessions.set(event.sessionId, session2);
session2.once("Runtime.executionContextCreated", async (event2) => {
worker.createExecutionContext(new CRExecutionContext(session2, event2.context));
});
if (this._crPage._browserContext._browser.majorVersion() >= 143)
session2.on("Inspector.workerScriptLoaded", () => worker.workerScriptLoaded());
else
worker.workerScriptLoaded();
session2._sendMayFail("Runtime.enable");
this._crPage._networkManager.addSession(session2, this._page.frameManager.frame(this._targetId) ?? void 0).catch(() => {
});
session2._sendMayFail("Runtime.runIfWaitingForDebugger");
session2._sendMayFail("Target.setAutoAttach", { autoAttach: true, waitForDebuggerOnStart: true, flatten: true });
session2.on("Target.attachedToTarget", (event2) => this._onAttachedToTarget(event2));
session2.on("Target.detachedFromTarget", (event2) => this._onDetachedFromTarget(event2));
session2.on("Runtime.consoleAPICalled", (event2) => {
const args = event2.args.map((o) => createHandle(worker.existingExecutionContext, o));
this._page.addConsoleMessage(worker, event2.type, args, stackTraceToLocation(event2.stackTrace), void 0, event2.timestamp);
});
session2.on("Runtime.exceptionThrown", (exception) => this._page.addPageError(exceptionToError(exception.exceptionDetails), stackTraceToLocation(exception.exceptionDetails.stackTrace)));
}
_onDetachedFromTarget(event) {
const workerSession = this._workerSessions.get(event.sessionId);
if (workerSession) {
workerSession.dispose();
this._page.removeWorker(event.sessionId);
return;
}
const childFrameSession = this._crPage._sessions.get(event.targetId);
if (!childFrameSession)
return;
if (childFrameSession._swappedIn) {
childFrameSession.dispose();
return;
}
this._client.send("Page.enable").catch((e) => null).then(() => {
if (!childFrameSession._swappedIn)
this._page.frameManager.frameDetached(event.targetId);
childFrameSession.dispose();
});
}
_onWindowOpen(event) {
this._crPage._nextWindowOpenPopupFeatures.push(event.windowFeatures);
}
async _onConsoleAPI(event) {
if (event.executionContextId === 0) {
return;
}
const context2 = this._contextIdToContext.get(event.executionContextId);
if (!context2)
return;
const values = event.args.map((arg) => createHandle(context2, arg));
this._page.addConsoleMessage(null, event.type, values, stackTraceToLocation(event.stackTrace), void 0, event.timestamp);
}
async _onBindingCalled(event) {
const pageOrError = await this._crPage._page.waitForInitializedOrError();
if (!(pageOrError instanceof Error)) {
const context2 = this._contextIdToContext.get(event.executionContextId);
if (context2)
await this._page.onBindingCalled(event.payload, context2);
}
}
_onDialog(event) {
if (!this._page.frameManager.frame(this._targetId))
return;
this._page.browserContext.dialogManager.dialogDidOpen(new Dialog(
this._page,
event.type,
event.message,
async (accept, promptText) => {
if (this._isMainFrame() && event.type === "beforeunload" && !accept)
this._page.frameManager.frameAbortedNavigation(this._page.mainFrame()._id, "navigation cancelled by beforeunload dialog");
await this._client.send("Page.handleJavaScriptDialog", { accept, promptText });
},
event.defaultPrompt
));
}
_handleException(exceptionDetails) {
this._page.addPageError(exceptionToError(exceptionDetails), stackTraceToLocation(exceptionDetails.stackTrace));
}
async _onTargetCrashed() {
this._client._markAsCrashed();
this._page._didCrash();
}
_onLogEntryAdded(event) {
const { level, text: text2, args, source: source8, url: url2, lineNumber } = event.entry;
if (args)
args.map((arg) => releaseObject(this._client, arg.objectId));
if (source8 !== "worker") {
const location2 = {
url: url2 || "",
lineNumber: lineNumber || 0,
columnNumber: 0
};
this._page.addConsoleMessage(null, level, [], location2, text2, event.entry.timestamp);
}
}
async _onFileChooserOpened(event) {
if (!event.backendNodeId)
return;
const frame = this._page.frameManager.frame(event.frameId);
if (!frame)
return;
let handle;
try {
const utilityContext = await frame.utilityContext();
handle = await this._adoptBackendNodeId(event.backendNodeId, utilityContext);
} catch (e) {
return;
}
await this._page._onFileChooserOpened(handle);
}
_willBeginDownload() {
if (!this._crPage._page.initializedOrUndefined()) {
this._firstNonInitialNavigationCommittedReject(new Error("Starting new page download"));
}
}
_onScreencastFrame(payload) {
const buffer = Buffer.from(payload.data, "base64");
this._page.screencast.onScreencastFrame({
buffer,
frameSwapWallTime: payload.metadata.timestamp ? payload.metadata.timestamp * 1e3 : Date.now(),
viewportWidth: payload.metadata.deviceWidth,
viewportHeight: payload.metadata.deviceHeight
}, () => {
this._client._sendMayFail("Page.screencastFrameAck", { sessionId: payload.sessionId });
});
}
async _updateGeolocation(initial) {
const geolocation = this._crPage._browserContext._options.geolocation;
if (!initial || geolocation)
await this._client.send("Emulation.setGeolocationOverride", geolocation || {});
}
async _updateViewport(preserveWindowBoundaries) {
if (this._crPage._browserContext._browser.isClank())
return;
assert(this._isMainFrame());
const options2 = this._crPage._browserContext._options;
const emulatedSize = this._page.emulatedSize();
if (!emulatedSize)
return;
const viewportSize = emulatedSize.viewport;
const screenSize = emulatedSize.screen;
const isLandscape = screenSize.width > screenSize.height;
const metricsOverride = {
mobile: !!options2.isMobile,
width: viewportSize.width,
height: viewportSize.height,
screenWidth: screenSize.width,
screenHeight: screenSize.height,
deviceScaleFactor: options2.deviceScaleFactor || 1,
screenOrientation: !!options2.isMobile ? isLandscape ? { angle: 90, type: "landscapePrimary" } : { angle: 0, type: "portraitPrimary" } : { angle: 0, type: "landscapePrimary" },
dontSetVisibleSize: preserveWindowBoundaries
};
if (JSON.stringify(this._metricsOverride) === JSON.stringify(metricsOverride))
return;
const promises = [];
if (!preserveWindowBoundaries && this._windowId) {
let insets = { width: 0, height: 0 };
if (this._crPage._browserContext._browser.options.headful) {
insets = { width: 24, height: 88 };
if (process.platform === "win32")
insets = { width: 16, height: 88 };
else if (process.platform === "linux")
insets = { width: 8, height: 85 };
else if (process.platform === "darwin")
insets = { width: 2, height: 80 };
if (this._crPage._browserContext.isPersistentContext()) {
insets.height += 46;
}
}
promises.push(this.setWindowBounds({
width: viewportSize.width + insets.width,
height: viewportSize.height + insets.height
}));
}
promises.push(this._client.send("Emulation.setDeviceMetricsOverride", metricsOverride));
await Promise.all(promises);
this._metricsOverride = metricsOverride;
}
async windowBounds() {
const { bounds } = await this._client.send("Browser.getWindowBounds", {
windowId: this._windowId
});
return bounds;
}
async setWindowBounds(bounds) {
return await this._client.send("Browser.setWindowBounds", {
windowId: this._windowId,
bounds
});
}
async _updateEmulateMedia() {
const emulatedMedia = this._page.emulatedMedia();
const media = emulatedMedia.media === "no-override" ? "" : emulatedMedia.media;
const colorScheme = emulatedMedia.colorScheme === "no-override" ? "" : emulatedMedia.colorScheme;
const reducedMotion = emulatedMedia.reducedMotion === "no-override" ? "" : emulatedMedia.reducedMotion;
const forcedColors = emulatedMedia.forcedColors === "no-override" ? "" : emulatedMedia.forcedColors;
const contrast = emulatedMedia.contrast === "no-override" ? "" : emulatedMedia.contrast;
const features = [
{ name: "prefers-color-scheme", value: colorScheme },
{ name: "prefers-reduced-motion", value: reducedMotion },
{ name: "forced-colors", value: forcedColors },
{ name: "prefers-contrast", value: contrast }
];
await this._client.send("Emulation.setEmulatedMedia", { media, features });
}
async _updateUserAgent() {
const options2 = this._crPage._browserContext._options;
await this._client.send("Emulation.setUserAgentOverride", {
userAgent: options2.userAgent || "",
acceptLanguage: options2.locale,
userAgentMetadata: calculateUserAgentMetadata(options2)
});
}
async _setDefaultFontFamilies(session2) {
const fontFamilies = platformToFontFamilies[this._crPage._browserContext._browser._platform()];
await session2.send("Page.setFontFamilies", fontFamilies);
}
async _updateFileChooserInterception(initial) {
const enabled = this._page.fileChooserIntercepted();
if (initial && !enabled)
return;
await this._client.send("Page.setInterceptFileChooserDialog", { enabled }).catch(() => {
});
}
async _evaluateOnNewDocument(initScript, world, runImmediately) {
const worldName = world === "utility" ? this._crPage.utilityWorldName : void 0;
const { identifier } = await this._client.send("Page.addScriptToEvaluateOnNewDocument", { source: initScript.source, worldName, runImmediately });
this._initScriptIds.set(initScript, identifier);
}
async _removeEvaluatesOnNewDocument(initScripts) {
const ids = [];
for (const script of initScripts) {
const id = this._initScriptIds.get(script);
if (id)
ids.push(id);
this._initScriptIds.delete(script);
}
await Promise.all(ids.map((identifier) => this._client.send("Page.removeScriptToEvaluateOnNewDocument", { identifier }).catch(() => {
})));
}
async exposePlaywrightBinding() {
await this._client.send("Runtime.addBinding", { name: PageBinding.kBindingName });
}
async _getContentFrame(handle) {
const nodeInfo = await this._client.send("DOM.describeNode", {
objectId: handle._objectId
});
if (!nodeInfo || typeof nodeInfo.node.frameId !== "string")
return null;
return this._page.frameManager.frame(nodeInfo.node.frameId);
}
async _getOwnerFrame(handle) {
const documentElement = await handle.evaluateHandle((node) => {
const doc = node;
if (doc.documentElement && doc.documentElement.ownerDocument === doc)
return doc.documentElement;
return node.ownerDocument ? node.ownerDocument.documentElement : null;
});
if (!documentElement)
return null;
if (!documentElement._objectId)
return null;
const nodeInfo = await this._client.send("DOM.describeNode", {
objectId: documentElement._objectId
});
const frameId = nodeInfo && typeof nodeInfo.node.frameId === "string" ? nodeInfo.node.frameId : null;
documentElement.dispose();
return frameId;
}
async _getBoundingBox(handle) {
const result2 = await this._client._sendMayFail("DOM.getBoxModel", {
objectId: handle._objectId
});
if (!result2)
return null;
const quad = result2.model.border;
const x = Math.min(quad[0], quad[2], quad[4], quad[6]);
const y = Math.min(quad[1], quad[3], quad[5], quad[7]);
const width = Math.max(quad[0], quad[2], quad[4], quad[6]) - x;
const height = Math.max(quad[1], quad[3], quad[5], quad[7]) - y;
const position = await this._framePosition();
if (!position)
return null;
return { x: x + position.x, y: y + position.y, width, height };
}
async _framePosition() {
const frame = this._page.frameManager.frame(this._targetId);
if (!frame)
return null;
if (frame === this._page.mainFrame())
return { x: 0, y: 0 };
const element2 = await frame.frameElement(nullProgress);
const box = await element2.boundingBox(nullProgress);
return box;
}
async _scrollRectIntoViewIfNeeded(handle, rect) {
return await this._client.send("DOM.scrollIntoViewIfNeeded", {
objectId: handle._objectId,
rect
}).then(() => "done").catch((e) => {
if (e instanceof Error && e.message.includes("Node does not have a layout object"))
return "error:notvisible";
if (e instanceof Error && e.message.includes("Node is detached from document"))
return "error:notconnected";
throw e;
});
}
async _getContentQuads(handle) {
const result2 = await this._client._sendMayFail("DOM.getContentQuads", {
objectId: handle._objectId
});
if (!result2)
return null;
const position = await this._framePosition();
if (!position)
return null;
return result2.quads.map((quad) => [
{ x: quad[0] + position.x, y: quad[1] + position.y },
{ x: quad[2] + position.x, y: quad[3] + position.y },
{ x: quad[4] + position.x, y: quad[5] + position.y },
{ x: quad[6] + position.x, y: quad[7] + position.y }
]);
}
async _adoptElementHandle(handle, to) {
const nodeInfo = await this._client.send("DOM.describeNode", {
objectId: handle._objectId
});
return this._adoptBackendNodeId(nodeInfo.node.backendNodeId, to);
}
async _adoptBackendNodeId(backendNodeId, to) {
const result2 = await this._client._sendMayFail("DOM.resolveNode", {
backendNodeId,
executionContextId: to.delegate._contextId
});
if (!result2 || result2.object.subtype === "null")
throw new Error(kUnableToAdoptErrorMessage);
return createHandle(to, result2.object).asElement();
}
};
}
});
// packages/playwright-core/src/server/chromium/crServiceWorker.ts
var CRServiceWorker;
var init_crServiceWorker = __esm({
"packages/playwright-core/src/server/chromium/crServiceWorker.ts"() {
"use strict";
init_page();
init_crExecutionContext();
init_crNetworkManager();
init_browserContext();
init_network2();
init_console();
init_crProtocolHelper();
CRServiceWorker = class extends Worker {
constructor(browserContext, session2, url2) {
super(browserContext, url2);
this._session = session2;
this.browserContext = browserContext;
if (!process.env.PLAYWRIGHT_DISABLE_SERVICE_WORKER_NETWORK)
this._networkManager = new CRNetworkManager(null, this);
session2.on("Inspector.targetCrashed", () => this.destroyExecutionContext("Service worker restarted"));
session2.on("Runtime.executionContextCreated", (event) => {
this.createExecutionContext(new CRExecutionContext(session2, event.context));
if (this.browserContext._browser.majorVersion() < 143)
this.workerScriptLoaded();
});
if (this.browserContext._browser.majorVersion() >= 143)
session2.on("Inspector.workerScriptLoaded", () => this.workerScriptLoaded());
if (this._networkManager && this._isNetworkInspectionEnabled()) {
this.updateRequestInterception();
this.updateExtraHTTPHeaders();
this.updateHttpCredentials();
this.updateOffline();
this._networkManager.addSession(
session2,
void 0,
true
/* isMain */
).catch(() => {
});
}
session2.on("Runtime.consoleAPICalled", (event) => {
if (!this.existingExecutionContext || process.env.PLAYWRIGHT_DISABLE_SERVICE_WORKER_CONSOLE)
return;
const args = event.args.map((o) => createHandle(this.existingExecutionContext, o));
const message = new ConsoleMessage(null, this, event.type, void 0, args, stackTraceToLocation(event.stackTrace), event.timestamp);
this.browserContext.emit(BrowserContext.Events.Console, message);
});
session2.send("Runtime.enable", {}).catch((e) => {
});
session2.send("Runtime.runIfWaitingForDebugger").catch((e) => {
});
session2.on("Inspector.targetReloadedAfterCrash", () => {
session2._sendMayFail("Runtime.runIfWaitingForDebugger", {});
});
}
didClose() {
this._networkManager?.removeSession(this._session);
this._session.dispose();
super.didClose();
}
async updateOffline() {
if (!this._isNetworkInspectionEnabled())
return;
await this._networkManager?.setOffline(!!this.browserContext._options.offline).catch(() => {
});
}
async updateHttpCredentials() {
if (!this._isNetworkInspectionEnabled())
return;
await this._networkManager?.authenticate(this.browserContext._options.httpCredentials || null).catch(() => {
});
}
async updateExtraHTTPHeaders() {
if (!this._isNetworkInspectionEnabled())
return;
await this._networkManager?.setExtraHTTPHeaders(this.browserContext._options.extraHTTPHeaders || []).catch(() => {
});
}
async updateRequestInterception() {
if (!this._isNetworkInspectionEnabled())
return;
await this._networkManager?.setRequestInterception(this.needsRequestInterception()).catch(() => {
});
}
needsRequestInterception() {
return this._isNetworkInspectionEnabled() && this.browserContext.requestInterceptors.length > 0;
}
reportRequestFinished(request2, response2) {
this.browserContext.emit(BrowserContext.Events.RequestFinished, { request: request2, response: response2 });
}
requestFailed(request2, _canceled) {
this.browserContext.emit(BrowserContext.Events.RequestFailed, request2);
}
requestReceivedResponse(response2) {
this.browserContext.emit(BrowserContext.Events.Response, response2);
}
requestStarted(request2, route2) {
this.browserContext.emit(BrowserContext.Events.Request, request2);
if (route2)
new Route(request2, route2).handle(this.browserContext.requestInterceptors);
}
_isNetworkInspectionEnabled() {
return this.browserContext._options.serviceWorkers !== "block";
}
};
}
});
// packages/playwright-core/src/server/chromium/crBrowser.ts
function shouldProxyLoopback(bypass) {
if (process.env.PLAYWRIGHT_DISABLE_FORCED_CHROMIUM_PROXIED_LOOPBACK)
return false;
const hosts = (bypass || "").split(",").map((s) => s.trim());
const shouldBypassSomeLoopback = ["localhost", "127.0.0.1", "::1", "[::]", "[::1]", "<loopback>", "<-loopback>"].some((host) => hosts.includes(host));
return !shouldBypassSomeLoopback;
}
var import_path22, CRBrowser, CREvents, CRBrowserContext;
var init_crBrowser = __esm({
"packages/playwright-core/src/server/chromium/crBrowser.ts"() {
"use strict";
import_path22 = __toESM(require("path"));
init_assert();
init_crypto();
init_artifact();
init_browser();
init_browserContext();
init_frames();
init_network2();
init_page();
init_crConnection();
init_crPage();
init_crProtocolHelper();
init_crServiceWorker();
CRBrowser = class _CRBrowser extends Browser {
constructor(parent, connection, options2) {
super(parent, options2);
this._clientRootSessionPromise = null;
this._contexts = /* @__PURE__ */ new Map();
this._crPages = /* @__PURE__ */ new Map();
this._serviceWorkers = /* @__PURE__ */ new Map();
this._version = "";
this._majorVersion = 0;
this._revision = "";
this._tracingRecording = false;
this._userAgent = "";
this._connection = connection;
this._session = this._connection.rootSession;
this._connection.on(ConnectionEvents.Disconnected, () => this._didDisconnect());
this._session.on("Target.attachedToTarget", this._onAttachedToTarget.bind(this));
this._session.on("Target.detachedFromTarget", this._onDetachedFromTarget.bind(this));
this._session.on("Browser.downloadWillBegin", this._onDownloadWillBegin.bind(this));
this._session.on("Browser.downloadProgress", this._onDownloadProgress.bind(this));
}
static async connect(parent, transport, options2, devtools) {
options2 = { ...options2 };
const connection = new CRConnection(parent, transport, options2.protocolLogger, options2.browserLogsCollector);
const browser = new _CRBrowser(parent, connection, options2);
browser._devtools = devtools;
if (browser.isClank())
browser._isCollocatedWithServer = false;
const session2 = connection.rootSession;
if (options2.__testHookOnConnectToBrowser)
await options2.__testHookOnConnectToBrowser();
const version3 = await session2.send("Browser.getVersion");
browser._revision = version3.revision;
browser._version = version3.product.substring(version3.product.indexOf("/") + 1);
try {
browser._majorVersion = +browser._version.split(".")[0];
} catch {
}
browser._userAgent = version3.userAgent;
browser.options.headful = !version3.userAgent.includes("Headless");
if (!options2.persistent) {
await session2.send("Target.setAutoAttach", { autoAttach: true, waitForDebuggerOnStart: true, flatten: true });
return browser;
}
browser._defaultContext = new CRBrowserContext(browser, void 0, options2.persistent);
await Promise.all([
session2.send("Target.setAutoAttach", { autoAttach: true, waitForDebuggerOnStart: true, flatten: true }).then(async () => {
await session2.send("Target.getTargetInfo");
}),
browser._defaultContext.initialize()
]);
await browser._waitForAllPagesToBeInitialized();
return browser;
}
async doCreateNewContext(options2) {
const proxy = options2.proxyOverride || options2.proxy;
let proxyBypassList = void 0;
if (proxy) {
if (shouldProxyLoopback(proxy.bypass))
proxyBypassList = "<-loopback>" + (proxy.bypass ? `,${proxy.bypass}` : "");
else
proxyBypassList = proxy.bypass;
}
const { browserContextId } = await this._session.send("Target.createBrowserContext", {
disposeOnDetach: true,
proxyServer: proxy ? proxy.server : void 0,
proxyBypassList
});
const context2 = new CRBrowserContext(this, browserContextId, options2);
await context2.initialize();
this._contexts.set(browserContextId, context2);
return context2;
}
contexts() {
return Array.from(this._contexts.values());
}
version() {
return this._version;
}
majorVersion() {
return this._majorVersion;
}
userAgent() {
return this._userAgent;
}
_platform() {
if (this._userAgent.includes("Windows"))
return "win";
if (this._userAgent.includes("Macintosh"))
return "mac";
return "linux";
}
isClank() {
return this.options.name === "clank";
}
async _waitForAllPagesToBeInitialized() {
await Promise.all([...this._crPages.values()].map((crPage) => crPage._page.waitForInitializedOrError()));
}
_onAttachedToTarget({ targetInfo, sessionId, waitingForDebugger }) {
if (targetInfo.type === "browser")
return;
const session2 = this._session.createChildSession(sessionId);
assert(targetInfo.browserContextId, "targetInfo: " + JSON.stringify(targetInfo, null, 2));
let context2 = this._contexts.get(targetInfo.browserContextId) || null;
if (!context2) {
context2 = this._defaultContext;
}
if (targetInfo.type === "other" && targetInfo.url.startsWith("devtools://devtools") && this._devtools) {
this._devtools.install(session2);
return;
}
const treatOtherAsPage = targetInfo.type === "other" && process.env.PW_CHROMIUM_ATTACH_TO_OTHER;
if (!context2 || targetInfo.type === "other" && !treatOtherAsPage) {
session2.detach().catch(() => {
});
return;
}
assert(!this._crPages.has(targetInfo.targetId), "Duplicate target " + targetInfo.targetId);
assert(!this._serviceWorkers.has(targetInfo.targetId), "Duplicate target " + targetInfo.targetId);
if (targetInfo.type === "page" || treatOtherAsPage) {
const opener = targetInfo.openerId ? this._crPages.get(targetInfo.openerId) || null : null;
const crPage = new CRPage(session2, targetInfo.targetId, context2, opener, { hasUIWindow: targetInfo.type === "page" });
this._crPages.set(targetInfo.targetId, crPage);
return;
}
if (targetInfo.type === "service_worker") {
const serviceWorker = new CRServiceWorker(context2, session2, targetInfo.url);
this._serviceWorkers.set(targetInfo.targetId, serviceWorker);
context2.emit(CRBrowserContext.CREvents.ServiceWorker, serviceWorker);
return;
}
session2.detach().catch(() => {
});
}
_onDetachedFromTarget(payload) {
const targetId = payload.targetId;
const crPage = this._crPages.get(targetId);
if (crPage) {
this._crPages.delete(targetId);
crPage.didClose();
return;
}
const serviceWorker = this._serviceWorkers.get(targetId);
if (serviceWorker) {
this._serviceWorkers.delete(targetId);
serviceWorker.didClose();
return;
}
}
_didDisconnect() {
for (const crPage of this._crPages.values())
crPage.didClose();
this._crPages.clear();
for (const serviceWorker of this._serviceWorkers.values())
serviceWorker.didClose();
this._serviceWorkers.clear();
this.didClose();
}
_findOwningPage(frameId) {
for (const crPage of this._crPages.values()) {
const frame = crPage._page.frameManager.frame(frameId);
if (frame)
return crPage;
}
return null;
}
_onDownloadWillBegin(payload) {
const page = this._findOwningPage(payload.frameId);
if (!page) {
return;
}
page.willBeginDownload();
let originPage = page._page.initializedOrUndefined();
if (!originPage && page._opener)
originPage = page._opener._page.initializedOrUndefined();
if (!originPage)
return;
this.downloadCreated(originPage, payload.guid, payload.url, payload.suggestedFilename);
}
_onDownloadProgress(payload) {
if (payload.state === "completed")
this.downloadFinished(payload.guid, "");
if (payload.state === "canceled")
this.downloadFinished(payload.guid, this._closeReason || "canceled");
}
async _closePage(crPage) {
await this._session.send("Target.closeTarget", { targetId: crPage._targetId });
}
async newBrowserCDPSession() {
return await this._connection.createBrowserSession();
}
async startTracing(page, options2 = {}) {
assert(!this._tracingRecording, "Cannot start recording trace while already recording trace.");
this._tracingClient = page ? page.delegate._mainFrameSession._client : this._session;
const defaultCategories = [
"-*",
"devtools.timeline",
"v8.execute",
"disabled-by-default-devtools.timeline",
"disabled-by-default-devtools.timeline.frame",
"toplevel",
"blink.console",
"blink.user_timing",
"latencyInfo",
"disabled-by-default-devtools.timeline.stack",
"disabled-by-default-v8.cpu_profiler",
"disabled-by-default-v8.cpu_profiler.hires"
];
const {
screenshots = false,
categories: categories2 = defaultCategories
} = options2;
if (screenshots)
categories2.push("disabled-by-default-devtools.screenshot");
this._tracingRecording = true;
await this._tracingClient.send("Tracing.start", {
transferMode: "ReturnAsStream",
categories: categories2.join(",")
});
}
async stopTracing() {
assert(this._tracingClient, "Tracing was not started.");
const [event] = await Promise.all([
new Promise((f) => this._tracingClient.once("Tracing.tracingComplete", f)),
this._tracingClient.send("Tracing.end")
]);
const tracingPath = import_path22.default.join(this.options.artifactsDir, createGuid() + ".crtrace");
await saveProtocolStream(this._tracingClient, event.stream, tracingPath);
this._tracingRecording = false;
const artifact = new Artifact(this, tracingPath);
artifact.reportFinished();
return artifact;
}
isConnected() {
return !this._connection._closed;
}
async _clientRootSession() {
if (!this._clientRootSessionPromise)
this._clientRootSessionPromise = this._connection.createBrowserSession();
return this._clientRootSessionPromise;
}
};
CREvents = {
ServiceWorker: "serviceworker"
};
CRBrowserContext = class extends BrowserContext {
static {
this.CREvents = CREvents;
}
constructor(browser, browserContextId, options2) {
super(browser, options2, browserContextId);
this.authenticateProxyViaCredentials();
}
async initialize() {
assert(!Array.from(this._browser._crPages.values()).some((page) => page._browserContext === this));
const promises = [super.initialize()];
if (this._browser.options.name !== "clank" && this._options.acceptDownloads !== "internal-browser-default") {
promises.push(this._browser._session.send("Browser.setDownloadBehavior", {
behavior: this._options.acceptDownloads === "accept" ? "allowAndName" : "deny",
browserContextId: this._browserContextId,
downloadPath: this._browser.options.downloadsPath,
eventsEnabled: true
}));
}
await Promise.all(promises);
}
_crPages() {
return [...this._browser._crPages.values()].filter((crPage) => crPage._browserContext === this);
}
possiblyUninitializedPages() {
return this._crPages().map((crPage) => crPage._page);
}
async doCreateNewPage() {
const { targetId } = await this._browser._session.send("Target.createTarget", { url: "about:blank", browserContextId: this._browserContextId });
return this._browser._crPages.get(targetId)._page;
}
async doGetCookies(urls) {
const { cookies } = await this._browser._session.send("Storage.getCookies", { browserContextId: this._browserContextId });
return filterCookies(cookies.map((c) => {
const { name, value: value2, domain, path: path59, expires, httpOnly, secure, sameSite } = c;
const copy = {
name,
value: value2,
domain,
path: path59,
expires,
httpOnly,
secure,
sameSite: sameSite ?? "Lax"
};
if (c.partitionKey) {
copy._crHasCrossSiteAncestor = c.partitionKey.hasCrossSiteAncestor;
copy.partitionKey = c.partitionKey.topLevelSite;
}
return copy;
}), urls);
}
async addCookies(cookies) {
function toChromiumCookie(cookie) {
const { name, value: value2, url: url2, domain, path: path59, expires, httpOnly, secure, sameSite, partitionKey, _crHasCrossSiteAncestor } = cookie;
const copy = {
name,
value: value2,
url: url2,
domain,
path: path59,
expires,
httpOnly,
secure,
sameSite
};
if (partitionKey) {
copy.partitionKey = {
topLevelSite: partitionKey,
// _crHasCrossSiteAncestor is non-standard, set it true by default if the cookie is partitioned.
hasCrossSiteAncestor: _crHasCrossSiteAncestor ?? true
};
}
return copy;
}
await this._browser._session.send("Storage.setCookies", {
cookies: rewriteCookies(cookies).map(toChromiumCookie),
browserContextId: this._browserContextId
});
}
async doClearCookies() {
await this._browser._session.send("Storage.clearCookies", { browserContextId: this._browserContextId });
}
async doGrantPermissions(origin, permissions) {
const webPermissionToProtocol = /* @__PURE__ */ new Map([
["geolocation", "geolocation"],
["midi", "midi"],
["notifications", "notifications"],
["camera", "videoCapture"],
["microphone", "audioCapture"],
["background-sync", "backgroundSync"],
["ambient-light-sensor", "sensors"],
["accelerometer", "sensors"],
["gyroscope", "sensors"],
["magnetometer", "sensors"],
["clipboard-read", "clipboardReadWrite"],
["clipboard-write", "clipboardSanitizedWrite"],
["payment-handler", "paymentHandler"],
// chrome-specific permissions we have.
["midi-sysex", "midiSysex"],
["storage-access", "storageAccess"],
["local-fonts", "localFonts"],
["local-network-access", ["localNetworkAccess", "localNetwork", "loopbackNetwork"]],
["screen-wake-lock", "wakeLockScreen"]
]);
const grantPermissions = async (mapping) => {
const filtered = permissions.flatMap((permission) => {
const protocolPermission = mapping.get(permission);
if (!protocolPermission)
throw new Error("Unknown permission: " + permission);
return typeof protocolPermission === "string" ? [protocolPermission] : protocolPermission;
});
await this._browser._session.send("Browser.grantPermissions", { origin: origin === "*" ? void 0 : origin, browserContextId: this._browserContextId, permissions: filtered });
};
try {
await grantPermissions(webPermissionToProtocol);
} catch (e) {
const fallbackMapping = new Map(webPermissionToProtocol);
fallbackMapping.set("local-network-access", ["localNetworkAccess"]);
await grantPermissions(fallbackMapping);
}
}
async doClearPermissions() {
await this._browser._session.send("Browser.resetPermissions", { browserContextId: this._browserContextId });
}
async setGeolocation(geolocation) {
verifyGeolocation(geolocation);
this._options.geolocation = geolocation;
for (const page of this.pages())
await page.delegate.updateGeolocation();
}
async doUpdateExtraHTTPHeaders() {
for (const page of this.pages())
await page.delegate.updateExtraHTTPHeaders();
for (const sw of this.serviceWorkers())
await sw.updateExtraHTTPHeaders();
}
async setUserAgent(userAgent) {
this._options.userAgent = userAgent;
for (const page of this.pages())
await page.delegate.updateUserAgent();
}
async doUpdateOffline() {
for (const page of this.pages())
await page.delegate.updateOffline();
for (const sw of this.serviceWorkers())
await sw.updateOffline();
}
async doSetHTTPCredentials(httpCredentials) {
this._options.httpCredentials = httpCredentials;
for (const page of this.pages())
await page.delegate.updateHttpCredentials();
for (const sw of this.serviceWorkers())
await sw.updateHttpCredentials();
}
async doAddInitScript(initScript) {
for (const page of this.pages())
await page.delegate.addInitScript(initScript);
}
async doRemoveInitScripts(initScripts) {
for (const page of this.pages())
await page.delegate.removeInitScripts(initScripts);
}
async doUpdateRequestInterception() {
for (const page of this.pages())
await page.delegate.updateRequestInterception();
for (const sw of this.serviceWorkers())
await sw.updateRequestInterception();
}
async doUpdateDefaultViewport() {
}
async doUpdateDefaultEmulatedMedia() {
}
async doExposePlaywrightBinding() {
for (const page of this._crPages())
await page.exposePlaywrightBinding();
}
async doClose(reason) {
await this.dialogManager.closeBeforeUnloadDialogs();
if (!this._browserContextId) {
return "close-browser";
}
await Promise.all([...this._downloads].map((download) => download.cancel().catch(() => {
})));
await this._browser._session.send("Target.disposeBrowserContext", { browserContextId: this._browserContextId });
this._browser._contexts.delete(this._browserContextId);
for (const [targetId, serviceWorker] of this._browser._serviceWorkers) {
if (serviceWorker.browserContext !== this)
continue;
serviceWorker.didClose();
this._browser._serviceWorkers.delete(targetId);
}
}
onClosePersistent() {
}
async clearCache() {
for (const page of this._crPages())
await page._networkManager.clearCache();
}
async cancelDownload(guid) {
await this._browser._session.send("Browser.cancelDownload", {
guid,
browserContextId: this._browserContextId
});
}
serviceWorkers() {
return Array.from(this._browser._serviceWorkers.values()).filter((serviceWorker) => serviceWorker.browserContext === this);
}
async newCDPSession(page) {
let targetId = null;
if (page instanceof Page) {
targetId = page.delegate._targetId;
} else if (page instanceof Frame) {
const session2 = page._page.delegate._sessions.get(page._id);
if (!session2)
throw new Error(`This frame does not have a separate CDP session, it is a part of the parent frame's session`);
targetId = session2._targetId;
} else {
throw new Error("page: expected Page or Frame");
}
const rootSession = await this._browser._clientRootSession();
return rootSession.attachToTarget(targetId);
}
};
}
});
// packages/playwright-core/src/server/android/android.ts
function encodeWebFrame(data) {
return import_utilsBundle.wsSender.frame(Buffer.from(data), {
opcode: 1,
mask: true,
fin: true,
readOnly: true
})[0];
}
var import_events7, import_fs24, import_os9, import_path23, import_utilsBundle, debug3, ARTIFACTS_FOLDER, Android, AndroidDevice, AndroidBrowser, ClankBrowserProcess;
var init_android = __esm({
"packages/playwright-core/src/server/android/android.ts"() {
"use strict";
import_events7 = require("events");
import_fs24 = __toESM(require("fs"));
import_os9 = __toESM(require("os"));
import_path23 = __toESM(require("path"));
init_pipeTransport();
init_crypto();
init_debug();
init_env();
init_task();
init_debugLogger();
init_fileUtils();
init_processLauncher();
import_utilsBundle = require("./utilsBundle");
init_browserContext();
init_chromiumSwitches();
init_crBrowser();
init_helper();
init_instrumentation();
init_progress();
init_registry();
debug3 = require("./utilsBundle").debug;
ARTIFACTS_FOLDER = import_path23.default.join(import_os9.default.tmpdir(), "playwright-artifacts-");
Android = class extends SdkObject {
constructor(parent, backend) {
super(parent, "android");
this._devices = /* @__PURE__ */ new Map();
this._backend = backend;
}
async devices(progress2, options2) {
const devices = (await progress2.race(this._backend.devices(options2))).filter((d) => d.status === "device");
const newSerials = /* @__PURE__ */ new Set();
for (const d of devices) {
newSerials.add(d.serial);
if (this._devices.has(d.serial))
continue;
await progress2.race(AndroidDevice.create(this, d, options2).then((device) => this._devices.set(d.serial, device)));
}
for (const d of this._devices.keys()) {
if (!newSerials.has(d))
this._devices.delete(d);
}
return [...this._devices.values()];
}
_deviceClosed(device) {
this._devices.delete(device.serial);
}
};
AndroidDevice = class _AndroidDevice extends SdkObject {
constructor(android, backend, model, options2) {
super(android, "android-device");
this._lastId = 0;
this._callbacks = /* @__PURE__ */ new Map();
this._webViews = /* @__PURE__ */ new Map();
this._browserConnections = /* @__PURE__ */ new Set();
this._isClosed = false;
this._android = android;
this._backend = backend;
this.model = model;
this.serial = backend.serial;
this._options = options2;
this.logName = "browser";
}
static {
this.Events = {
WebViewAdded: "webViewAdded",
WebViewRemoved: "webViewRemoved",
Close: "close"
};
}
static async create(android, backend, options2) {
await backend.init();
const model = await backend.runCommand("shell:getprop ro.product.model");
const device = new _AndroidDevice(android, backend, model.toString().trim(), options2);
await device._init();
return device;
}
async _init() {
await this._refreshWebViews();
const poll = () => {
this._pollingWebViews = setTimeout(() => this._refreshWebViews().then(poll).catch(() => {
this._close().catch(() => {
});
}), 500);
};
poll();
}
async shell(progress2, command) {
return await progress2.race(this._shell(command));
}
async _shell(command) {
const result2 = await this._backend.runCommand(`shell:${command}`);
await this._refreshWebViews();
return result2;
}
async open(progress2, command) {
return await this._open(progress2, command);
}
async screenshot(progress2) {
return await progress2.race(this._backend.runCommand(`shell:screencap -p`));
}
async _driver() {
if (this._isClosed)
return;
if (!this._driverPromise) {
const controller = new ProgressController();
this._driverPromise = controller.run((progress2) => this._installDriver(progress2));
}
return this._driverPromise;
}
async _installDriver(progress2) {
debug3("pw:android")("Stopping the old driver");
await progress2.race(this._shell(`am force-stop com.microsoft.playwright.androiddriver`));
if (!this._options.omitDriverInstall) {
debug3("pw:android")("Uninstalling the old driver");
await this.shell(progress2, `cmd package uninstall com.microsoft.playwright.androiddriver`);
await this.shell(progress2, `cmd package uninstall com.microsoft.playwright.androiddriver.test`);
debug3("pw:android")("Installing the new driver");
const executable = registry.findExecutable("android");
const packageManagerCommand = getPackageManagerExecCommand();
for (const file of ["android-driver.apk", "android-driver-target.apk"]) {
const fullName = import_path23.default.join(executable.directory, file);
if (!import_fs24.default.existsSync(fullName))
throw new Error(`Please install Android driver apk using '${packageManagerCommand} playwright install android'`);
await this.installApk(progress2, await progress2.race(import_fs24.default.promises.readFile(fullName)));
}
} else {
debug3("pw:android")("Skipping the driver installation");
}
debug3("pw:android")("Starting the new driver");
this._shell("am instrument -w com.microsoft.playwright.androiddriver.test/androidx.test.runner.AndroidJUnitRunner").catch((e) => debug3("pw:android")(e));
const socket = await this._waitForLocalAbstract(progress2, "playwright_android_driver_socket");
const transport = new PipeTransport(socket, socket, socket, "be");
transport.onmessage = (message) => {
const response2 = JSON.parse(message);
const { id, result: result2, error } = response2;
const callback = this._callbacks.get(id);
if (!callback)
return;
if (error)
callback.reject(new Error(error));
else
callback.fulfill(result2);
this._callbacks.delete(id);
};
return transport;
}
async _waitForLocalAbstract(progress2, socketName) {
let socket;
debug3("pw:android")(`Polling the socket localabstract:${socketName}`);
while (!socket) {
try {
socket = await this._open(progress2, `localabstract:${socketName}`);
} catch (e) {
if (isAbortError(e))
throw e;
await progress2.wait(250);
}
}
debug3("pw:android")(`Connected to localabstract:${socketName}`);
return socket;
}
async send(progress2, method, params2 = {}) {
return await progress2.race(this._send(method, params2));
}
async _send(method, params2 = {}) {
params2 = {
...params2,
// Patch the timeout in, just in case it's missing in one of the commands.
timeout: params2.timeout || 0
};
if (params2.androidSelector) {
params2.selector = params2.androidSelector;
delete params2.androidSelector;
}
const driver = await this._driver();
if (!driver)
throw new Error("Device is closed");
const id = ++this._lastId;
const result2 = new Promise((fulfill, reject) => this._callbacks.set(id, { fulfill, reject }));
driver.send(JSON.stringify({ id, method, params: params2 }));
return result2;
}
async close(progress2) {
await progress2.race(this._close());
}
async _close() {
if (this._isClosed)
return;
this._isClosed = true;
if (this._pollingWebViews)
clearTimeout(this._pollingWebViews);
for (const connection of this._browserConnections)
await connection.close();
if (this._driverPromise) {
const driver = await this._driver();
driver?.close();
}
await this._backend.close();
this._android._deviceClosed(this);
this.emit(_AndroidDevice.Events.Close);
}
async launchBrowser(progress2, pkg = "com.android.chrome", options2) {
debug3("pw:android")("Force-stopping", pkg);
await progress2.race(this._backend.runCommand(`shell:am force-stop ${pkg}`));
const socketName = isUnderTest() ? "webview_devtools_remote_playwright_test" : "playwright_" + createGuid() + "_devtools_remote";
const commandLine = this._defaultArgs(options2, socketName).join(" ");
debug3("pw:android")("Starting", pkg, commandLine);
await progress2.race(this._backend.runCommand(`shell:echo "${Buffer.from(commandLine).toString("base64")}" | base64 -d > /data/local/tmp/chrome-command-line`));
await progress2.race(this._backend.runCommand(`shell:am start -a android.intent.action.VIEW -d about:blank ${pkg}`));
const browserContext = await this._connectToBrowser(progress2, socketName, options2);
try {
await progress2.race(this._backend.runCommand(`shell:rm /data/local/tmp/chrome-command-line`));
return browserContext;
} catch (error) {
await browserContext.close(progress2, { reason: "Failed to launch" }).catch(() => {
});
throw error;
}
}
_defaultArgs(options2, socketName) {
const chromeArguments = [
"_",
"--disable-fre",
"--no-default-browser-check",
`--remote-debugging-socket-name=${socketName}`,
...chromiumSwitches({ android: true }),
...this._innerDefaultArgs(options2)
];
return chromeArguments;
}
_innerDefaultArgs(options2) {
const { args = [], proxy } = options2;
const chromeArguments = [];
if (proxy) {
chromeArguments.push(`--proxy-server=${proxy.server}`);
const proxyBypassRules = [];
if (proxy.bypass)
proxyBypassRules.push(...proxy.bypass.split(",").map((t) => t.trim()).map((t) => t.startsWith(".") ? "*" + t : t));
if (shouldProxyLoopback(proxy.bypass))
proxyBypassRules.push("<-loopback>");
if (proxyBypassRules.length > 0)
chromeArguments.push(`--proxy-bypass-list=${proxyBypassRules.join(";")}`);
}
chromeArguments.push(...args);
return chromeArguments;
}
async connectToWebView(progress2, socketName) {
const webView = this._webViews.get(socketName);
if (!webView)
throw new Error("WebView has been closed");
return await this._connectToBrowser(progress2, socketName);
}
async _connectToBrowser(progress2, socketName, options2 = {}) {
const socket = await this._waitForLocalAbstract(progress2, socketName);
try {
const androidBrowser = new AndroidBrowser(this, socket);
await progress2.race(androidBrowser._init());
this._browserConnections.add(androidBrowser);
const artifactsDir = await progress2.race(import_fs24.default.promises.mkdtemp(ARTIFACTS_FOLDER));
const cleanupArtifactsDir = async () => {
const errors = (await removeFolders([artifactsDir])).filter(Boolean);
for (let i = 0; i < (errors || []).length; ++i)
debug3("pw:android")(`exception while removing ${artifactsDir}: ${errors[i]}`);
};
gracefullyCloseSet.add(cleanupArtifactsDir);
socket.on("close", async () => {
gracefullyCloseSet.delete(cleanupArtifactsDir);
cleanupArtifactsDir().catch((e) => debug3("pw:android")(`could not cleanup artifacts dir: ${e}`));
});
const browserOptions = {
name: "clank",
browserType: "chromium",
slowMo: 0,
persistent: { ...options2, noDefaultViewport: true },
artifactsDir,
downloadsPath: artifactsDir,
tracesDir: artifactsDir,
browserProcess: new ClankBrowserProcess(androidBrowser),
proxy: options2.proxy,
protocolLogger: helper.debugProtocolLogger(),
browserLogsCollector: new RecentLogsCollector(),
originalLaunchOptions: {}
};
validateBrowserContextOptions(options2, browserOptions);
const browser = await progress2.race(CRBrowser.connect(this.attribution.playwright, androidBrowser, browserOptions));
const defaultContext = browser._defaultContext;
await defaultContext.loadDefaultContextAsIs(progress2);
return defaultContext;
} catch (error) {
socket.close();
throw error;
}
}
_open(progress2, command) {
return raceUncancellableOperationWithCleanup(progress2, () => this._backend.open(command), (socket) => socket.close());
}
webViews() {
return [...this._webViews.values()];
}
async installApk(progress2, content, options2) {
const args = options2 && options2.args ? options2.args : ["-r", "-t", "-S"];
debug3("pw:android")("Opening install socket");
const installSocket = await this._open(progress2, `shell:cmd package install ${args.join(" ")} ${content.length}`);
debug3("pw:android")("Writing driver bytes: " + content.length);
await progress2.race(installSocket.write(content));
const success = await progress2.race(new Promise((f) => installSocket.on("data", f)));
debug3("pw:android")("Written driver bytes: " + success);
installSocket.close();
}
async push(progress2, content, path59, mode = 420) {
const socket = await this._open(progress2, `sync:`);
const sendHeader = async (progress3, command, length) => {
const buffer = Buffer.alloc(command.length + 4);
buffer.write(command, 0);
buffer.writeUInt32LE(length, command.length);
await progress3.race(socket.write(buffer));
};
const send = async (progress3, command, data) => {
await sendHeader(progress3, command, data.length);
await progress3.race(socket.write(data));
};
await send(progress2, "SEND", Buffer.from(`${path59},${mode}`));
const maxChunk = 65535;
for (let i = 0; i < content.length; i += maxChunk)
await send(progress2, "DATA", content.slice(i, i + maxChunk));
await sendHeader(progress2, "DONE", Date.now() / 1e3 | 0);
const result2 = await progress2.race(new Promise((f) => socket.once("data", f)));
const code = result2.slice(0, 4).toString();
if (code !== "OKAY")
throw new Error("Could not push: " + code);
socket.close();
}
async _refreshWebViews() {
const sockets = (await this._backend.runCommand(`shell:cat /proc/net/unix | grep webview_devtools_remote`)).toString().split("\n");
if (this._isClosed)
return;
const socketNames = /* @__PURE__ */ new Set();
for (const line of sockets) {
const matchSocketName = line.match(/[^@]+@(.*?webview_devtools_remote_?.*)/);
if (!matchSocketName)
continue;
const socketName = matchSocketName[1];
socketNames.add(socketName);
if (this._webViews.has(socketName))
continue;
const match = line.match(/[^@]+@.*?webview_devtools_remote_?(\d*)/);
let pid = -1;
if (match && match[1])
pid = +match[1];
const pkg = await this._extractPkg(pid);
if (this._isClosed)
return;
const webView = { pid, pkg, socketName };
this._webViews.set(socketName, webView);
this.emit(_AndroidDevice.Events.WebViewAdded, webView);
}
for (const p of this._webViews.keys()) {
if (!socketNames.has(p)) {
this._webViews.delete(p);
this.emit(_AndroidDevice.Events.WebViewRemoved, p);
}
}
}
async _extractPkg(pid) {
let pkg = "";
if (pid === -1)
return pkg;
const procs = (await this._backend.runCommand(`shell:ps -A | grep ${pid}`)).toString().split("\n");
for (const proc of procs) {
const match = proc.match(/[^\s]+\s+(\d+).*$/);
if (!match)
continue;
pkg = proc.substring(proc.lastIndexOf(" ") + 1);
}
return pkg;
}
};
AndroidBrowser = class extends import_events7.EventEmitter {
constructor(device, socket) {
super();
this._waitForNextTask = makeWaitForNextTask();
this.setMaxListeners(0);
this.device = device;
this._socket = socket;
this._socket.on("close", () => {
this._waitForNextTask(() => {
if (this.onclose)
this.onclose();
});
});
this._receiver = new import_utilsBundle.wsReceiver();
this._receiver.on("message", (message) => {
this._waitForNextTask(() => {
if (this.onmessage)
this.onmessage(JSON.parse(message));
});
});
}
async _init() {
await this._socket.write(Buffer.from(`GET /devtools/browser HTTP/1.1\r
Upgrade: WebSocket\r
Connection: Upgrade\r
Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==\r
Sec-WebSocket-Version: 13\r
\r
`));
await new Promise((f) => this._socket.once("data", f));
this._socket.on("data", (data) => this._receiver._write(data, "binary", () => {
}));
}
async send(s) {
await this._socket.write(encodeWebFrame(JSON.stringify(s)));
}
async close() {
this._socket.close();
}
};
ClankBrowserProcess = class {
constructor(browser) {
this._browser = browser;
}
async kill() {
}
async close() {
await this._browser.close();
}
};
}
});
// packages/playwright-core/src/server/android/backendAdb.ts
async function runCommand(command, host = "127.0.0.1", port = 5037, serial) {
debug4("pw:adb:runCommand")(command, serial);
const socket = new BufferedSocketWrapper(command, import_net4.default.createConnection({ host, port }));
try {
if (serial) {
await socket.write(encodeMessage(`host:transport:${serial}`));
const status2 = await socket.read(4);
assert(status2.toString() === "OKAY", status2.toString());
}
await socket.write(encodeMessage(command));
const status = await socket.read(4);
assert(status.toString() === "OKAY", status.toString());
let commandOutput;
if (!command.startsWith("shell:")) {
const remainingLength = parseInt((await socket.read(4)).toString(), 16);
commandOutput = await socket.read(remainingLength);
} else {
commandOutput = await socket.readAll();
}
return commandOutput;
} finally {
socket.close();
}
}
async function open2(command, host = "127.0.0.1", port = 5037, serial) {
const socket = new BufferedSocketWrapper(command, import_net4.default.createConnection({ host, port }));
if (serial) {
await socket.write(encodeMessage(`host:transport:${serial}`));
const status2 = await socket.read(4);
assert(status2.toString() === "OKAY", status2.toString());
}
await socket.write(encodeMessage(command));
const status = await socket.read(4);
assert(status.toString() === "OKAY", status.toString());
return socket;
}
function encodeMessage(message) {
let lenHex = message.length.toString(16);
lenHex = "0".repeat(4 - lenHex.length) + lenHex;
return Buffer.from(lenHex + message);
}
var import_events8, import_net4, debug4, AdbBackend, AdbDevice, BufferedSocketWrapper;
var init_backendAdb = __esm({
"packages/playwright-core/src/server/android/backendAdb.ts"() {
"use strict";
import_events8 = require("events");
import_net4 = __toESM(require("net"));
init_assert();
debug4 = require("./utilsBundle").debug;
AdbBackend = class {
async devices(options2 = {}) {
const result2 = await runCommand("host:devices", options2.host, options2.port);
const lines = result2.toString().trim().split("\n");
return lines.map((line) => {
const [serial, status] = line.trim().split(" ");
return new AdbDevice(serial, status, options2.host, options2.port);
});
}
};
AdbDevice = class {
constructor(serial, status, host, port) {
this._closed = false;
this.serial = serial;
this.status = status;
this.host = host;
this.port = port;
}
async init() {
}
async close() {
this._closed = true;
}
runCommand(command) {
if (this._closed)
throw new Error("Device is closed");
return runCommand(command, this.host, this.port, this.serial);
}
async open(command) {
if (this._closed)
throw new Error("Device is closed");
const result2 = await open2(command, this.host, this.port, this.serial);
result2.becomeSocket();
return result2;
}
};
BufferedSocketWrapper = class extends import_events8.EventEmitter {
constructor(command, socket) {
super();
this._buffer = Buffer.from([]);
this._isSocket = false;
this._isClosed = false;
this._command = command;
this._socket = socket;
this._connectPromise = new Promise((f) => this._socket.on("connect", f));
this._socket.on("data", (data) => {
debug4("pw:adb:data")(data.toString());
if (this._isSocket) {
this.emit("data", data);
return;
}
this._buffer = Buffer.concat([this._buffer, data]);
if (this._notifyReader)
this._notifyReader();
});
this._socket.on("close", () => {
this._isClosed = true;
if (this._notifyReader)
this._notifyReader();
this.close();
this.emit("close");
});
this._socket.on("error", (error) => this.emit("error", error));
}
async write(data) {
debug4("pw:adb:send")(data.toString().substring(0, 100) + "...");
await this._connectPromise;
await new Promise((f) => this._socket.write(data, f));
}
close() {
if (this._isClosed)
return;
debug4("pw:adb")("Close " + this._command);
this._socket.destroy();
}
async read(length) {
await this._connectPromise;
assert(!this._isSocket, "Can not read by length in socket mode");
while (this._buffer.length < length)
await new Promise((f) => this._notifyReader = f);
const result2 = this._buffer.slice(0, length);
this._buffer = this._buffer.slice(length);
debug4("pw:adb:recv")(result2.toString().substring(0, 100) + "...");
return result2;
}
async readAll() {
while (!this._isClosed)
await new Promise((f) => this._notifyReader = f);
return this._buffer;
}
becomeSocket() {
assert(!this._buffer.length);
this._isSocket = true;
}
};
}
});
// packages/playwright-core/src/server/pipeTransport.ts
var PipeTransport2;
var init_pipeTransport2 = __esm({
"packages/playwright-core/src/server/pipeTransport.ts"() {
"use strict";
init_debugLogger();
init_task();
PipeTransport2 = class {
constructor(pipeWrite, pipeRead) {
this._pendingBuffers = [];
this._waitForNextTask = makeWaitForNextTask();
this._closed = false;
this._pipeRead = pipeRead;
this._pipeWrite = pipeWrite;
pipeRead.on("data", (buffer) => this._dispatch(buffer));
pipeRead.on("close", () => {
this._closed = true;
if (this._onclose)
this._onclose.call(null);
});
pipeRead.on("error", (e) => debugLogger.log("error", e));
pipeWrite.on("error", (e) => debugLogger.log("error", e));
this.onmessage = void 0;
}
get onclose() {
return this._onclose;
}
set onclose(onclose) {
this._onclose = onclose;
if (onclose && !this._pipeRead.readable)
onclose();
}
send(message) {
if (this._closed)
throw new Error("Pipe has been closed");
this._pipeWrite.write(JSON.stringify(message));
this._pipeWrite.write("\0");
}
close() {
throw new Error("unimplemented");
}
_dispatch(buffer) {
let end = buffer.indexOf("\0");
if (end === -1) {
this._pendingBuffers.push(buffer);
return;
}
this._pendingBuffers.push(buffer.slice(0, end));
const message = Buffer.concat(this._pendingBuffers).toString();
this._waitForNextTask(() => {
if (this.onmessage)
this.onmessage.call(null, JSON.parse(message));
});
let start3 = end + 1;
end = buffer.indexOf("\0", start3);
while (end !== -1) {
const message2 = buffer.toString(void 0, start3, end);
this._waitForNextTask(() => {
if (this.onmessage)
this.onmessage.call(null, JSON.parse(message2));
});
start3 = end + 1;
end = buffer.indexOf("\0", start3);
}
this._pendingBuffers = [buffer.slice(start3)];
}
};
}
});
// packages/playwright-core/src/server/transport.ts
function stripQueryParams(url2) {
try {
const u = new URL(url2);
u.search = "";
u.hash = "";
return u.toString();
} catch {
return url2;
}
}
var ws, perMessageDeflate2, WebSocketTransport;
var init_transport = __esm({
"packages/playwright-core/src/server/transport.ts"() {
"use strict";
init_happyEyeballs();
init_task();
ws = require("./utilsBundle").ws;
perMessageDeflate2 = {
clientNoContextTakeover: true,
zlibDeflateOptions: {
level: 3
},
zlibInflateOptions: {
chunkSize: 10 * 1024
},
threshold: 10 * 1024
};
WebSocketTransport = class _WebSocketTransport {
constructor(progress2, url2, logUrl, options2) {
this.headers = [];
this.wsEndpoint = url2;
this._logUrl = logUrl;
this._ws = new ws(url2, [], {
maxPayload: 256 * 1024 * 1024,
// 256Mb,
headers: options2.headers,
followRedirects: options2.followRedirects,
agent: /^(https|wss):\/\//.test(url2) ? httpsHappyEyeballsAgent : httpHappyEyeballsAgent,
perMessageDeflate: perMessageDeflate2
});
this._ws.on("upgrade", (response2) => {
for (let i = 0; i < response2.rawHeaders.length; i += 2) {
this.headers.push({ name: response2.rawHeaders[i], value: response2.rawHeaders[i + 1] });
if (options2.debugLogHeader && response2.rawHeaders[i] === options2.debugLogHeader)
progress2?.log(response2.rawHeaders[i + 1]);
}
});
this._progress = progress2;
const messageWrap = makeWaitForNextTask();
this._ws.addEventListener("message", (event) => {
messageWrap(() => {
const eventData = event.data;
let parsedJson;
try {
parsedJson = JSON.parse(eventData);
} catch (e) {
this._progress?.log(`<closing ws> Closing websocket due to malformed JSON. eventData=${eventData} e=${e?.message}`);
this._ws.close();
return;
}
try {
if (this.onmessage)
this.onmessage.call(null, parsedJson);
} catch (e) {
this._progress?.log(`<closing ws> Closing websocket due to failed onmessage callback. eventData=${eventData} e=${e?.message}`);
this._ws.close();
}
});
});
this._ws.addEventListener("close", (event) => {
this._progress?.log(`<ws disconnected> ${logUrl} code=${event.code} reason=${event.reason}`);
if (this.onclose)
this.onclose.call(null, event.reason);
});
this._ws.addEventListener("error", (error) => this._progress?.log(`<ws error> ${logUrl} ${error.type} ${error.message}`));
}
static async connect(progress2, url2, options2 = {}) {
return await _WebSocketTransport._connect(
progress2,
url2,
options2,
false
/* hadRedirects */
);
}
static async _connect(progress2, url2, options2, hadRedirects) {
const logUrl = stripQueryParams(url2);
progress2?.log(`<ws connecting> ${logUrl}`);
const transport = new _WebSocketTransport(progress2, url2, logUrl, { ...options2, followRedirects: !!options2.followRedirects && hadRedirects });
const resultPromise = new Promise((fulfill, reject) => {
transport._ws.on("open", async () => {
progress2?.log(`<ws connected> ${logUrl}`);
fulfill({});
});
transport._ws.on("error", (event) => {
progress2?.log(`<ws connect error> ${logUrl} ${event.message}`);
reject(new Error("WebSocket error: " + event.message));
transport._ws.close();
});
transport._ws.on("unexpected-response", (request2, response2) => {
if (options2.followRedirects && !hadRedirects && (response2.statusCode === 301 || response2.statusCode === 302 || response2.statusCode === 307 || response2.statusCode === 308)) {
fulfill({ redirect: response2 });
transport._ws.close();
return;
}
for (let i = 0; i < response2.rawHeaders.length; i += 2) {
if (options2.debugLogHeader && response2.rawHeaders[i] === options2.debugLogHeader)
progress2?.log(response2.rawHeaders[i + 1]);
}
const chunks = [];
const errorPrefix = `${logUrl} ${response2.statusCode} ${response2.statusMessage}`;
response2.on("data", (chunk) => chunks.push(chunk));
response2.on("close", () => {
const error = chunks.length ? `${errorPrefix}
${Buffer.concat(chunks)}` : errorPrefix;
progress2?.log(`<ws unexpected response> ${error}`);
reject(new Error("WebSocket error: " + error));
transport._ws.close();
});
});
});
try {
const result2 = progress2 ? await progress2.race(resultPromise) : await resultPromise;
if (result2.redirect) {
const newHeaders = Object.fromEntries(Object.entries(options2.headers || {}).filter(([name]) => {
return !name.includes("access-key") && name.toLowerCase() !== "authorization";
}));
return _WebSocketTransport._connect(
progress2,
result2.redirect.headers.location,
{ ...options2, headers: newHeaders },
true
/* hadRedirects */
);
}
return transport;
} catch (error) {
await transport.closeAndWait();
throw error;
}
}
send(message) {
this._ws.send(JSON.stringify(message));
}
close() {
this._progress?.log(`<ws disconnecting> ${this._logUrl}`);
this._ws.close();
}
async closeAndWait() {
if (this._ws.readyState === ws.CLOSED)
return;
const promise = new Promise((f) => this._ws.once("close", f));
this.close();
await promise;
}
};
}
});
// packages/playwright-core/src/server/browserType.ts
function copyTestHooks(from, to) {
for (const [key, value2] of Object.entries(from)) {
if (key.startsWith("__testHook"))
to[key] = value2;
}
}
var import_fs25, import_os10, import_path24, kNoXServerRunningError, BrowserType;
var init_browserType = __esm({
"packages/playwright-core/src/server/browserType.ts"() {
"use strict";
import_fs25 = __toESM(require("fs"));
import_os10 = __toESM(require("os"));
import_path24 = __toESM(require("path"));
init_assert();
init_manualPromise();
init_time();
init_debug();
init_fileUtils();
init_processLauncher();
init_debugLogger();
init_browserContext();
init_helper();
init_instrumentation();
init_pipeTransport2();
init_protocolError();
init_registry();
init_socksClientCertificatesInterceptor();
init_transport();
kNoXServerRunningError = "Looks like you launched a headed browser without having a XServer running.\nSet either 'headless: true' or use 'xvfb-run <your-playwright-app>' before running Playwright.\n\n<3 Playwright Team";
BrowserType = class extends SdkObject {
constructor(parent, browserName) {
super(parent, "browser-type");
this.attribution.browserType = this;
this._name = browserName;
this.logName = "browser";
}
executablePath() {
return registry.findExecutable(this._name).executablePath() || "";
}
name() {
return this._name;
}
async launch(progress2, options2, protocolLogger) {
options2 = this._validateLaunchOptions(options2);
const seleniumHubUrl = options2.__testHookSeleniumRemoteURL || process.env.SELENIUM_REMOTE_URL;
if (seleniumHubUrl)
return this.launchWithSeleniumHub(progress2, seleniumHubUrl, options2);
return this._innerLaunchWithRetries(progress2, options2, void 0, helper.debugProtocolLogger(protocolLogger)).catch((e) => {
throw this._rewriteStartupLog(e);
});
}
async launchPersistentContext(progress2, userDataDir, options2) {
const launchOptions = this._validateLaunchOptions(options2);
let clientCertificatesProxy;
if (options2.clientCertificates?.length) {
clientCertificatesProxy = await ClientCertificatesProxy.create(progress2, options2);
launchOptions.proxyOverride = clientCertificatesProxy.proxySettings();
options2 = { ...options2 };
options2.internalIgnoreHTTPSErrors = true;
}
try {
const browser = await this._innerLaunchWithRetries(progress2, launchOptions, options2, helper.debugProtocolLogger(), userDataDir).catch((e) => {
throw this._rewriteStartupLog(e);
});
browser._defaultContext._clientCertificatesProxy = clientCertificatesProxy;
return browser._defaultContext;
} catch (error) {
await clientCertificatesProxy?.close().catch(() => {
});
throw error;
}
}
async _innerLaunchWithRetries(progress2, options2, persistent, protocolLogger, userDataDir) {
try {
return await this._innerLaunch(progress2, options2, persistent, protocolLogger, userDataDir);
} catch (error) {
const errorMessage = typeof error === "object" && typeof error.message === "string" ? error.message : "";
if (errorMessage.includes("Inconsistency detected by ld.so")) {
progress2.log(`<restarting browser due to hitting race condition in glibc>`);
return this._innerLaunch(progress2, options2, persistent, protocolLogger, userDataDir);
}
throw error;
}
}
async _innerLaunch(progress2, options2, persistent, protocolLogger, maybeUserDataDir) {
options2.proxy = options2.proxy ? normalizeProxySettings(options2.proxy) : void 0;
const browserLogsCollector = new RecentLogsCollector();
const { browserProcess, userDataDir, artifactsDir, transport, wsEndpoint } = await this._launchProcess(progress2, options2, !!persistent, browserLogsCollector, maybeUserDataDir);
try {
if (options2.__testHookBeforeCreateBrowser)
await progress2.race(options2.__testHookBeforeCreateBrowser());
const browserOptions = {
name: this._name,
browserType: this._name,
channel: options2.channel,
slowMo: options2.slowMo,
persistent,
headful: !options2.headless,
artifactsDir,
downloadsPath: options2.downloadsPath || artifactsDir,
tracesDir: options2.tracesDir || artifactsDir,
browserProcess,
customExecutablePath: options2.executablePath,
proxy: options2.proxy,
protocolLogger,
browserLogsCollector,
wsEndpoint,
originalLaunchOptions: options2,
userDataDir: persistent ? userDataDir : void 0
};
if (persistent)
validateBrowserContextOptions(persistent, browserOptions);
copyTestHooks(options2, browserOptions);
const browser = await progress2.race(this.connectToTransport(transport, browserOptions, browserLogsCollector));
browser._userDataDirForTest = userDataDir;
if (persistent && !options2.ignoreAllDefaultArgs)
await browser._defaultContext.loadDefaultContext(progress2);
return browser;
} catch (error) {
await progress2.race(browserProcess.close().catch(() => {
}));
throw error;
}
}
async _prepareToLaunch(options2, isPersistent, userDataDir) {
const {
ignoreDefaultArgs,
ignoreAllDefaultArgs,
args = [],
executablePath = null
} = options2;
const tempDirectories = [];
let artifactsDir;
if (options2.artifactsDir) {
artifactsDir = options2.artifactsDir;
} else {
artifactsDir = await import_fs25.default.promises.mkdtemp(import_path24.default.join(import_os10.default.tmpdir(), "playwright-artifacts-"));
tempDirectories.push(artifactsDir);
}
if (userDataDir) {
assert(import_path24.default.isAbsolute(userDataDir), "userDataDir must be an absolute path");
if (!await existsAsync(userDataDir))
await import_fs25.default.promises.mkdir(userDataDir, { recursive: true, mode: 448 });
} else {
userDataDir = await import_fs25.default.promises.mkdtemp(import_path24.default.join(import_os10.default.tmpdir(), `playwright_${this._name}dev_profile-`));
tempDirectories.push(userDataDir);
}
await this.prepareUserDataDir(options2, userDataDir);
const browserArguments = [];
if (ignoreAllDefaultArgs)
browserArguments.push(...args);
else if (ignoreDefaultArgs)
browserArguments.push(...(await this.defaultArgs(options2, isPersistent, userDataDir)).filter((arg) => ignoreDefaultArgs.indexOf(arg) === -1));
else
browserArguments.push(...await this.defaultArgs(options2, isPersistent, userDataDir));
let executable;
if (executablePath) {
if (!await existsAsync(executablePath))
throw new Error(`Failed to launch ${this._name} because executable doesn't exist at ${executablePath}`);
executable = executablePath;
} else {
const registryExecutable = registry.findExecutable(this.getExecutableName(options2));
if (!registryExecutable || registryExecutable.browserName !== this._name)
throw new Error(`Unsupported ${this._name} channel "${options2.channel}"`);
executable = registryExecutable.executablePathOrDie(this.attribution.playwright.options.sdkLanguage);
await registry.validateHostRequirementsForExecutablesIfNeeded([registryExecutable], this.attribution.playwright.options.sdkLanguage);
}
return { executable, browserArguments, userDataDir, artifactsDir, tempDirectories };
}
async _launchProcess(progress2, options2, isPersistent, browserLogsCollector, userDataDir) {
const {
handleSIGINT = true,
handleSIGTERM = true,
handleSIGHUP = true
} = options2;
const env = options2.env ? envArrayToObject(options2.env) : process.env;
const prepared = await progress2.race(this._prepareToLaunch(options2, isPersistent, userDataDir));
let transport = void 0;
let browserProcess = void 0;
const exitPromise = new ManualPromise();
const { launchedProcess, gracefullyClose, kill } = await progress2.race(launchProcess({
command: prepared.executable,
args: prepared.browserArguments,
env: this.amendEnvironment(env, prepared.userDataDir, isPersistent, options2),
handleSIGINT,
handleSIGTERM,
handleSIGHUP,
log: (message) => {
progress2.log(message);
browserLogsCollector.log(message);
},
stdio: "pipe",
tempDirectories: prepared.tempDirectories,
attemptToGracefullyClose: async () => {
if (options2.__testHookGracefullyClose)
await options2.__testHookGracefullyClose();
if (transport) {
this.attemptToGracefullyCloseBrowser(transport);
} else {
throw new Error("Force-killing the browser because no transport is available to gracefully close it.");
}
},
onExit: (exitCode, signal) => {
exitPromise.resolve();
if (browserProcess && browserProcess.onclose)
browserProcess.onclose(exitCode, signal);
}
}));
async function closeOrKill(timeout) {
let timer;
try {
await Promise.race([
gracefullyClose(),
new Promise((resolve, reject) => timer = setTimeout(reject, timeout))
]);
} catch (ignored) {
await kill().catch((ignored2) => {
});
} finally {
clearTimeout(timer);
}
}
browserProcess = {
onclose: void 0,
process: launchedProcess,
close: () => closeOrKill(options2.__testHookBrowserCloseTimeout || DEFAULT_PLAYWRIGHT_TIMEOUT),
kill
};
try {
const { wsEndpoint } = await progress2.race([
this.waitForReadyState(options2, browserLogsCollector),
exitPromise.then(() => ({ wsEndpoint: void 0 }))
]);
if (exitPromise.isDone()) {
const log2 = helper.formatBrowserLogs(browserLogsCollector.recentLogs());
const updatedLog = this.doRewriteStartupLog(log2);
throw new Error(`Failed to launch the browser process.
Browser logs:
${updatedLog}`);
}
if (!this.supportsPipeTransport()) {
transport = await WebSocketTransport.connect(progress2, wsEndpoint);
} else {
const stdio = launchedProcess.stdio;
transport = new PipeTransport2(stdio[3], stdio[4]);
}
return { browserProcess, artifactsDir: prepared.artifactsDir, userDataDir: prepared.userDataDir, transport, wsEndpoint };
} catch (error) {
await progress2.race(closeOrKill(DEFAULT_PLAYWRIGHT_TIMEOUT).catch(() => {
}));
throw error;
}
}
async connectOverCDP(progress2, endpointURL, options2) {
throw new Error("CDP connections are only supported by Chromium");
}
async connectToWorker(progress2, endpoint) {
throw new Error("CDP connections are only supported by Chromium");
}
async launchWithSeleniumHub(progress2, hubUrl, options2) {
throw new Error("Connecting to SELENIUM_REMOTE_URL is only supported by Chromium");
}
_validateLaunchOptions(options2) {
let { headless = true, downloadsPath, proxy } = options2;
if (debugMode() === "inspector")
headless = false;
if (downloadsPath && !import_path24.default.isAbsolute(downloadsPath))
downloadsPath = import_path24.default.join(process.cwd(), downloadsPath);
if (options2.socksProxyPort)
proxy = { server: `socks5://127.0.0.1:${options2.socksProxyPort}` };
return { ...options2, headless, downloadsPath, proxy };
}
_createUserDataDirArgMisuseError(userDataDirArg) {
switch (this.attribution.playwright.options.sdkLanguage) {
case "java":
return new Error(`Pass userDataDir parameter to 'BrowserType.launchPersistentContext(userDataDir, options)' instead of specifying '${userDataDirArg}' argument`);
case "python":
return new Error(`Pass user_data_dir parameter to 'browser_type.launch_persistent_context(user_data_dir, **kwargs)' instead of specifying '${userDataDirArg}' argument`);
case "csharp":
return new Error(`Pass userDataDir parameter to 'BrowserType.LaunchPersistentContextAsync(userDataDir, options)' instead of specifying '${userDataDirArg}' argument`);
default:
return new Error(`Pass userDataDir parameter to 'browserType.launchPersistentContext(userDataDir, options)' instead of specifying '${userDataDirArg}' argument`);
}
}
_rewriteStartupLog(error) {
if (!isProtocolError(error))
return error;
if (error.logs)
error.logs = this.doRewriteStartupLog(error.logs);
return error;
}
async waitForReadyState(options2, browserLogsCollector) {
return {};
}
async prepareUserDataDir(options2, userDataDir) {
}
supportsPipeTransport() {
return true;
}
getExecutableName(options2) {
return options2.channel || this._name;
}
};
}
});
// packages/playwright-core/src/server/bidi/bidiConnection.ts
var import_events9, kBrowserCloseMessageId2, kShutdownSessionNewMessageId, BidiConnection, BidiSession;
var init_bidiConnection = __esm({
"packages/playwright-core/src/server/bidi/bidiConnection.ts"() {
"use strict";
import_events9 = require("events");
init_debugLogger();
init_helper();
init_protocolError();
kBrowserCloseMessageId2 = Number.MAX_SAFE_INTEGER - 1;
kShutdownSessionNewMessageId = kBrowserCloseMessageId2 - 1;
BidiConnection = class {
constructor(transport, onDisconnect, protocolLogger, browserLogsCollector) {
this._lastId = 0;
this._closed = false;
this._browsingContextToSession = /* @__PURE__ */ new Map();
this._realmToBrowsingContext = /* @__PURE__ */ new Map();
// TODO: shared/service workers might have multiple owner realms.
this._realmToOwnerRealm = /* @__PURE__ */ new Map();
this._transport = transport;
this._onDisconnect = onDisconnect;
this._protocolLogger = protocolLogger;
this._browserLogsCollector = browserLogsCollector;
this.browserSession = new BidiSession(this, "", (message) => {
this.rawSend(message);
});
this._transport.onmessage = this._dispatchMessage.bind(this);
this._transport.onclose = this._onClose.bind(this);
}
nextMessageId() {
return ++this._lastId;
}
rawSend(message) {
this._protocolLogger("send", message);
this._transport.send(message);
}
_dispatchMessage(message) {
this._protocolLogger("receive", message);
const object = message;
if (object.type === "event") {
if (object.method === "script.realmCreated") {
if ("context" in object.params)
this._realmToBrowsingContext.set(object.params.realm, object.params.context);
if (object.params.type === "dedicated-worker")
this._realmToOwnerRealm.set(object.params.realm, object.params.owners[0]);
} else if (object.method === "script.realmDestroyed") {
this._realmToBrowsingContext.delete(object.params.realm);
this._realmToOwnerRealm.delete(object.params.realm);
}
let context2;
let realm;
if ("context" in object.params) {
context2 = object.params.context;
} else if (object.method === "log.entryAdded" || object.method === "script.message") {
context2 = object.params.source?.context;
realm = object.params.source?.realm;
} else if (object.method === "script.realmCreated" && object.params.type === "dedicated-worker") {
realm = object.params.owners[0];
}
if (!context2 && realm) {
while (this._realmToOwnerRealm.get(realm))
realm = this._realmToOwnerRealm.get(realm);
context2 = this._realmToBrowsingContext.get(realm);
}
if (context2) {
const session2 = this._browsingContextToSession.get(context2);
if (session2) {
session2.dispatchMessage(message);
return;
}
}
} else if (message.id) {
for (const session2 of this._browsingContextToSession.values()) {
if (session2.hasCallback(message.id)) {
session2.dispatchMessage(message);
return;
}
}
}
this.browserSession.dispatchMessage(message);
}
_onClose(reason) {
this._closed = true;
this._transport.onmessage = void 0;
this._transport.onclose = void 0;
this._browserDisconnectedLogs = helper.formatBrowserLogs(this._browserLogsCollector.recentLogs(), reason);
this.browserSession.dispose();
this._onDisconnect();
}
isClosed() {
return this._closed;
}
close() {
if (!this._closed)
this._transport.close();
}
createMainFrameBrowsingContextSession(bowsingContextId) {
const result2 = new BidiSession(this, bowsingContextId, (message) => this.rawSend(message));
this._browsingContextToSession.set(bowsingContextId, result2);
return result2;
}
};
BidiSession = class extends import_events9.EventEmitter {
constructor(connection, sessionId, rawSend) {
super();
this._disposed = false;
this._callbacks = /* @__PURE__ */ new Map();
this._crashed = false;
this._browsingContexts = /* @__PURE__ */ new Set();
this.setMaxListeners(0);
this.connection = connection;
this.sessionId = sessionId;
this._rawSend = rawSend;
this.on = super.on;
this.off = super.removeListener;
this.addListener = super.addListener;
this.removeListener = super.removeListener;
this.once = super.once;
}
addFrameBrowsingContext(context2) {
this._browsingContexts.add(context2);
this.connection._browsingContextToSession.set(context2, this);
}
removeFrameBrowsingContext(context2) {
this._browsingContexts.delete(context2);
this.connection._browsingContextToSession.delete(context2);
}
async send(method, params2) {
if (this._crashed || this._disposed || this.connection._browserDisconnectedLogs)
throw new ProtocolError(this._crashed ? "crashed" : "closed", void 0, this.connection._browserDisconnectedLogs);
const id = this.connection.nextMessageId();
const messageObj = { id, method, params: params2 };
this._rawSend(messageObj);
return new Promise((resolve, reject) => {
this._callbacks.set(id, { resolve, reject, error: new ProtocolError("error", method) });
});
}
sendMayFail(method, params2) {
return this.send(method, params2).catch((error) => debugLogger.log("error", error));
}
markAsCrashed() {
this._crashed = true;
}
isDisposed() {
return this._disposed;
}
dispose() {
this._disposed = true;
this.connection._browsingContextToSession.delete(this.sessionId);
for (const context2 of this._browsingContexts)
this.connection._browsingContextToSession.delete(context2);
this._browsingContexts.clear();
for (const callback of this._callbacks.values()) {
callback.error.type = this._crashed ? "crashed" : "closed";
callback.error.setMessage(`Internal server error, session ${callback.error.type}.`);
callback.error.logs = this.connection._browserDisconnectedLogs;
callback.reject(callback.error);
}
this._callbacks.clear();
}
hasCallback(id) {
return this._callbacks.has(id);
}
dispatchMessage(message) {
const object = message;
if (object.id === kBrowserCloseMessageId2 || object.id === kShutdownSessionNewMessageId)
return;
if (object.id && this._callbacks.has(object.id)) {
const callback = this._callbacks.get(object.id);
this._callbacks.delete(object.id);
if (object.type === "error") {
callback.error.setMessage(object.error + "\nMessage: " + object.message);
callback.reject(callback.error);
} else if (object.type === "success") {
callback.resolve(object.result);
} else {
callback.error.setMessage("Internal error, unexpected response type: " + JSON.stringify(object));
callback.reject(callback.error);
}
} else if (object.id) {
} else {
Promise.resolve().then(() => this.emit(object.method, object.params));
}
}
};
}
});
// packages/playwright-core/src/server/bidi/third_party/bidiProtocolCore.ts
var Session, BrowsingContext, Emulation, Network, Script, Log, Input;
var init_bidiProtocolCore = __esm({
"packages/playwright-core/src/server/bidi/third_party/bidiProtocolCore.ts"() {
"use strict";
((Session3) => {
let UserPromptHandlerType;
((UserPromptHandlerType2) => {
UserPromptHandlerType2["Accept"] = "accept";
UserPromptHandlerType2["Dismiss"] = "dismiss";
UserPromptHandlerType2["Ignore"] = "ignore";
})(UserPromptHandlerType = Session3.UserPromptHandlerType || (Session3.UserPromptHandlerType = {}));
})(Session || (Session = {}));
((BrowsingContext2) => {
let ReadinessState;
((ReadinessState2) => {
ReadinessState2["None"] = "none";
ReadinessState2["Interactive"] = "interactive";
ReadinessState2["Complete"] = "complete";
})(ReadinessState = BrowsingContext2.ReadinessState || (BrowsingContext2.ReadinessState = {}));
})(BrowsingContext || (BrowsingContext = {}));
((BrowsingContext2) => {
let UserPromptType;
((UserPromptType2) => {
UserPromptType2["Alert"] = "alert";
UserPromptType2["Beforeunload"] = "beforeunload";
UserPromptType2["Confirm"] = "confirm";
UserPromptType2["Prompt"] = "prompt";
})(UserPromptType = BrowsingContext2.UserPromptType || (BrowsingContext2.UserPromptType = {}));
})(BrowsingContext || (BrowsingContext = {}));
((BrowsingContext2) => {
let CreateType;
((CreateType2) => {
CreateType2["Tab"] = "tab";
CreateType2["Window"] = "window";
})(CreateType = BrowsingContext2.CreateType || (BrowsingContext2.CreateType = {}));
})(BrowsingContext || (BrowsingContext = {}));
((Emulation2) => {
let ForcedColorsModeTheme;
((ForcedColorsModeTheme2) => {
ForcedColorsModeTheme2["Light"] = "light";
ForcedColorsModeTheme2["Dark"] = "dark";
})(ForcedColorsModeTheme = Emulation2.ForcedColorsModeTheme || (Emulation2.ForcedColorsModeTheme = {}));
})(Emulation || (Emulation = {}));
((Emulation2) => {
let ScreenOrientationNatural;
((ScreenOrientationNatural2) => {
ScreenOrientationNatural2["Portrait"] = "portrait";
ScreenOrientationNatural2["Landscape"] = "landscape";
})(ScreenOrientationNatural = Emulation2.ScreenOrientationNatural || (Emulation2.ScreenOrientationNatural = {}));
})(Emulation || (Emulation = {}));
((Network3) => {
let CollectorType;
((CollectorType2) => {
CollectorType2["Blob"] = "blob";
})(CollectorType = Network3.CollectorType || (Network3.CollectorType = {}));
})(Network || (Network = {}));
((Network3) => {
let SameSite;
((SameSite2) => {
SameSite2["Strict"] = "strict";
SameSite2["Lax"] = "lax";
SameSite2["None"] = "none";
SameSite2["Default"] = "default";
})(SameSite = Network3.SameSite || (Network3.SameSite = {}));
})(Network || (Network = {}));
((Network3) => {
let DataType;
((DataType2) => {
DataType2["Request"] = "request";
DataType2["Response"] = "response";
})(DataType = Network3.DataType || (Network3.DataType = {}));
})(Network || (Network = {}));
((Network3) => {
let InterceptPhase;
((InterceptPhase2) => {
InterceptPhase2["BeforeRequestSent"] = "beforeRequestSent";
InterceptPhase2["ResponseStarted"] = "responseStarted";
InterceptPhase2["AuthRequired"] = "authRequired";
})(InterceptPhase = Network3.InterceptPhase || (Network3.InterceptPhase = {}));
})(Network || (Network = {}));
((Script2) => {
let ResultOwnership;
((ResultOwnership2) => {
ResultOwnership2["Root"] = "root";
ResultOwnership2["None"] = "none";
})(ResultOwnership = Script2.ResultOwnership || (Script2.ResultOwnership = {}));
})(Script || (Script = {}));
((Log2) => {
let Level;
((Level2) => {
Level2["Debug"] = "debug";
Level2["Info"] = "info";
Level2["Warn"] = "warn";
Level2["Error"] = "error";
})(Level = Log2.Level || (Log2.Level = {}));
})(Log || (Log = {}));
((Input2) => {
let PointerType;
((PointerType2) => {
PointerType2["Mouse"] = "mouse";
PointerType2["Pen"] = "pen";
PointerType2["Touch"] = "touch";
})(PointerType = Input2.PointerType || (Input2.PointerType = {}));
})(Input || (Input = {}));
}
});
// packages/playwright-core/src/server/bidi/third_party/bidiProtocolPermissions.ts
var Permissions;
var init_bidiProtocolPermissions = __esm({
"packages/playwright-core/src/server/bidi/third_party/bidiProtocolPermissions.ts"() {
"use strict";
((Permissions2) => {
let PermissionState;
((PermissionState2) => {
PermissionState2["Granted"] = "granted";
PermissionState2["Denied"] = "denied";
PermissionState2["Prompt"] = "prompt";
})(PermissionState = Permissions2.PermissionState || (Permissions2.PermissionState = {}));
})(Permissions || (Permissions = {}));
}
});
// packages/playwright-core/src/server/bidi/third_party/bidiProtocol.ts
var init_bidiProtocol = __esm({
"packages/playwright-core/src/server/bidi/third_party/bidiProtocol.ts"() {
"use strict";
init_bidiProtocolCore();
init_bidiProtocolPermissions();
}
});
// packages/playwright-core/src/server/bidi/bidiNetworkManager.ts
function fromBidiHeaders(bidiHeaders) {
const result2 = [];
for (const { name, value: value2 } of bidiHeaders)
result2.push({ name, value: bidiBytesValueToString(value2) });
return result2;
}
function toBidiRequestHeaders(allHeaders) {
const bidiHeaders = toBidiHeaders(allHeaders);
return { headers: bidiHeaders };
}
function toBidiResponseHeaders(headers) {
const setCookieHeaders = headers.filter((h) => h.name.toLowerCase() === "set-cookie");
const otherHeaders = headers.filter((h) => h.name.toLowerCase() !== "set-cookie");
const rawCookies = setCookieHeaders.map((h) => parseRawCookie(h.value));
const cookies = rawCookies.filter(Boolean).map((c) => {
return {
...c,
value: { type: "string", value: c.value },
sameSite: toBidiSameSite(c.sameSite)
};
});
return { cookies, headers: toBidiHeaders(otherHeaders) };
}
function toBidiHeaders(headers) {
return headers.map(({ name, value: value2 }) => ({ name, value: { type: "string", value: value2 } }));
}
function bidiBytesValueToString(value2) {
if (value2.type === "string")
return value2.value;
if (value2.type === "base64")
return Buffer.from(value2.type, "base64").toString("binary");
return "unknown value type: " + value2.type;
}
function toBidiSameSite(sameSite) {
if (!sameSite)
return void 0;
if (sameSite === "Strict")
return Network.SameSite.Strict;
if (sameSite === "Lax")
return Network.SameSite.Lax;
return Network.SameSite.None;
}
function resourceTypeFromBidi(requestDestination, requestInitiatorType, eventInitiatorType) {
switch (requestDestination) {
case "audio":
return "media";
case "audioworklet":
return "script";
case "document":
return "document";
case "font":
return "font";
case "frame":
return "document";
case "iframe":
return "document";
case "image":
return "image";
case "object":
return "other";
case "paintworklet":
return "script";
case "script":
return "script";
case "serviceworker":
return "script";
case "sharedworker":
return "script";
case "style":
return "stylesheet";
case "track":
return "texttrack";
case "video":
return "media";
case "worker":
return "script";
case "":
switch (requestInitiatorType) {
case "fetch":
return "fetch";
case "font":
return "font";
case "xmlhttprequest":
return "xhr";
case null:
return eventInitiatorType === "script" ? "xhr" : "document";
default:
return "other";
}
default:
return "other";
}
}
var REQUEST_BODY_HEADERS, BidiNetworkManager, BidiRequest, BidiRouteImpl;
var init_bidiNetworkManager = __esm({
"packages/playwright-core/src/server/bidi/bidiNetworkManager.ts"() {
"use strict";
init_eventsHelper();
init_cookieStore();
init_network2();
init_bidiProtocol();
REQUEST_BODY_HEADERS = /* @__PURE__ */ new Set(["content-encoding", "content-language", "content-location", "content-type"]);
BidiNetworkManager = class {
constructor(bidiSession, page) {
this._userRequestInterceptionEnabled = false;
this._protocolRequestInterceptionEnabled = false;
this._attemptedAuthentications = /* @__PURE__ */ new Set();
this._session = bidiSession;
this._requests = /* @__PURE__ */ new Map();
this._page = page;
this._eventListeners = [
eventsHelper.addEventListener(bidiSession, "network.beforeRequestSent", this._onBeforeRequestSent.bind(this)),
eventsHelper.addEventListener(bidiSession, "network.responseStarted", this._onResponseStarted.bind(this)),
eventsHelper.addEventListener(bidiSession, "network.responseCompleted", this._onResponseCompleted.bind(this)),
eventsHelper.addEventListener(bidiSession, "network.fetchError", this._onFetchError.bind(this)),
eventsHelper.addEventListener(bidiSession, "network.authRequired", this._onAuthRequired.bind(this))
];
}
dispose() {
eventsHelper.removeEventListeners(this._eventListeners);
}
_onBeforeRequestSent(param) {
if (param.request.url.startsWith("data:"))
return;
const redirectedFrom = param.redirectCount ? this._requests.get(param.request.request) || null : null;
const frame = redirectedFrom ? redirectedFrom.request.frame() : param.context ? this._page.frameManager.frame(param.context) : null;
if (!frame)
return;
if (redirectedFrom)
this._deleteRequest(redirectedFrom._id);
if (param.request.method === "OPTIONS") {
const requestHeaders = Object.fromEntries(param.request.headers.map((h) => [h.name.toLowerCase(), bidiBytesValueToString(h.value)]));
if (param.initiator?.type === "preflight" || requestHeaders["access-control-request-method"]) {
if (param.intercepts) {
const responseHeaders = [
{ name: "Access-Control-Allow-Origin", value: requestHeaders["origin"] || "*" },
{ name: "Access-Control-Allow-Methods", value: requestHeaders["access-control-request-method"] },
{ name: "Access-Control-Allow-Credentials", value: "true" }
];
if (requestHeaders["access-control-request-headers"])
responseHeaders.push({ name: "Access-Control-Allow-Headers", value: requestHeaders["access-control-request-headers"] });
this._session.sendMayFail("network.provideResponse", {
request: param.request.request,
statusCode: 204,
headers: toBidiHeaders(responseHeaders)
});
}
return;
}
}
let route2;
let headersOverride;
if (param.intercepts) {
if (redirectedFrom) {
let params2 = {};
if (redirectedFrom._originalRequestRoute?._alreadyContinuedHeaders) {
const originalHeaders = fromBidiHeaders(param.request.headers);
headersOverride = applyHeadersOverrides(originalHeaders, redirectedFrom._originalRequestRoute._alreadyContinuedHeaders);
if (redirectedFrom.request.method() === "POST" && param.request.method === "GET")
headersOverride = headersOverride.filter(({ name }) => !REQUEST_BODY_HEADERS.has(name.toLowerCase()));
params2 = toBidiRequestHeaders(headersOverride);
}
this._session.sendMayFail("network.continueRequest", {
request: param.request.request,
...params2
});
} else {
route2 = new BidiRouteImpl(this._session, param.request.request);
}
}
const request2 = new BidiRequest(frame, redirectedFrom, param, route2, headersOverride);
this._requests.set(request2._id, request2);
this._page.frameManager.requestStarted(request2.request, route2);
}
_onResponseStarted(params2) {
const request2 = this._requests.get(params2.request.request);
if (!request2)
return;
const getResponseBody = async () => {
const { bytes } = await this._session.send("network.getData", { request: params2.request.request, dataType: Network.DataType.Response });
const encoding = bytes.type === "base64" ? "base64" : "utf8";
return Buffer.from(bytes.value, encoding);
};
const timings = params2.request.timings;
const startTime = timings.requestTime;
function relativeToStart(time) {
if (!time)
return -1;
return time - startTime;
}
const timing = {
startTime,
requestStart: relativeToStart(timings.requestStart),
responseStart: relativeToStart(timings.responseStart),
domainLookupStart: relativeToStart(timings.dnsStart),
domainLookupEnd: relativeToStart(timings.dnsEnd),
connectStart: relativeToStart(timings.connectStart),
secureConnectionStart: relativeToStart(timings.tlsStart),
connectEnd: relativeToStart(timings.connectEnd)
};
const response2 = new Response2(request2.request, params2.response.status, params2.response.statusText, fromBidiHeaders(params2.response.headers), timing, getResponseBody, false);
response2._serverAddrFinished();
response2._securityDetailsFinished();
response2._setHttpVersion(params2.response.protocol);
response2.setRawResponseHeaders(null);
response2.setResponseHeadersSize(params2.response.headersSize);
this._page.frameManager.requestReceivedResponse(response2);
}
_onResponseCompleted(params2) {
const request2 = this._requests.get(params2.request.request);
if (!request2)
return;
const response2 = request2.request._existingResponse();
response2.setTransferSize(params2.response.bodySize);
response2.setEncodedBodySize(params2.response.bodySize);
const isRedirected = response2.status() >= 300 && response2.status() <= 399;
const responseEndTime = params2.request.timings.responseEnd - response2.timing().startTime;
if (isRedirected) {
response2._requestFinished(responseEndTime);
} else {
this._deleteRequest(request2._id);
response2._requestFinished(responseEndTime);
}
this._page.frameManager.reportRequestFinished(request2.request, response2);
}
_onFetchError(params2) {
const request2 = this._requests.get(params2.request.request);
if (!request2)
return;
this._deleteRequest(request2._id);
const response2 = request2.request._existingResponse();
if (response2) {
response2.setTransferSize(null);
response2.setEncodedBodySize(null);
response2._requestFinished(-1);
}
request2.request._setFailureText(params2.errorText);
this._page.frameManager.requestFailed(request2.request, params2.errorText === "NS_BINDING_ABORTED");
}
_onAuthRequired(params2) {
const isBasic = params2.response.authChallenges?.some((challenge) => challenge.scheme.startsWith("Basic"));
const credentials = this._page.browserContext._options.httpCredentials;
if (isBasic && credentials && (!credentials.origin || new URL(params2.request.url).origin.toLowerCase() === credentials.origin.toLowerCase())) {
if (this._attemptedAuthentications.has(params2.request.request)) {
this._session.sendMayFail("network.continueWithAuth", {
request: params2.request.request,
action: "cancel"
});
} else {
this._attemptedAuthentications.add(params2.request.request);
this._session.sendMayFail("network.continueWithAuth", {
request: params2.request.request,
action: "provideCredentials",
credentials: {
type: "password",
username: credentials.username,
password: credentials.password
}
});
}
} else {
this._session.sendMayFail("network.continueWithAuth", {
request: params2.request.request,
action: "cancel"
});
}
}
_deleteRequest(requestId) {
this._requests.delete(requestId);
this._attemptedAuthentications.delete(requestId);
}
async setRequestInterception(value2) {
this._userRequestInterceptionEnabled = value2;
await this._updateProtocolRequestInterception();
}
async setCredentials(credentials) {
this._credentials = credentials;
await this._updateProtocolRequestInterception();
}
async _updateProtocolRequestInterception(initial) {
const enabled = this._userRequestInterceptionEnabled || !!this._credentials;
if (enabled === this._protocolRequestInterceptionEnabled)
return;
this._protocolRequestInterceptionEnabled = enabled;
if (initial && !enabled)
return;
const contexts = [this._page.delegate._session.sessionId];
const cachePromise = this._session.send("network.setCacheBehavior", { cacheBehavior: enabled ? "bypass" : "default", contexts });
let interceptPromise = Promise.resolve(void 0);
if (enabled) {
interceptPromise = this._session.send("network.addIntercept", {
phases: [Network.InterceptPhase.BeforeRequestSent],
contexts
}).then((r) => {
this._intercepId = r.intercept;
});
} else if (this._intercepId) {
interceptPromise = this._session.send("network.removeIntercept", { intercept: this._intercepId });
this._intercepId = void 0;
}
await Promise.all([cachePromise, interceptPromise]);
}
};
BidiRequest = class {
constructor(frame, redirectedFrom, payload, route2, headersOverride) {
this._id = payload.request.request;
if (redirectedFrom)
redirectedFrom._redirectedTo = this;
const postDataBuffer = null;
this.request = new Request(
frame._page.browserContext,
frame,
null,
redirectedFrom ? redirectedFrom.request : null,
payload.navigation ?? void 0,
payload.request.url,
resourceTypeFromBidi(payload.request.destination, payload.request.initiatorType, payload.initiator?.type),
payload.request.method,
postDataBuffer,
headersOverride || fromBidiHeaders(payload.request.headers)
);
this.request.setRawRequestHeaders(null);
this.request._setBodySize(payload.request.bodySize || 0);
this._originalRequestRoute = route2 ?? redirectedFrom?._originalRequestRoute;
route2?._setRequest(this.request);
}
_finalRequest() {
let request2 = this;
while (request2._redirectedTo)
request2 = request2._redirectedTo;
return request2;
}
};
BidiRouteImpl = class {
constructor(session2, requestId) {
this._session = session2;
this._requestId = requestId;
}
_setRequest(request2) {
this._request = request2;
}
async continue(overrides) {
let headers = overrides.headers || this._request.headers();
if (overrides.postData && headers) {
headers = headers.map((header) => {
if (header.name.toLowerCase() === "content-length")
return { name: header.name, value: overrides.postData.byteLength.toString() };
return header;
});
}
this._alreadyContinuedHeaders = headers;
await this._session.sendMayFail("network.continueRequest", {
request: this._requestId,
url: overrides.url,
method: overrides.method,
...toBidiRequestHeaders(this._alreadyContinuedHeaders),
body: overrides.postData ? { type: "base64", value: Buffer.from(overrides.postData).toString("base64") } : void 0
});
}
async fulfill(response2) {
const base64body = response2.isBase64 ? response2.body : Buffer.from(response2.body).toString("base64");
const headers = response2.headers.filter((h) => h.name.toLowerCase() !== "content-encoding");
await this._session.sendMayFail("network.provideResponse", {
request: this._requestId,
statusCode: response2.status,
reasonPhrase: statusText(response2.status),
...toBidiResponseHeaders(headers),
body: { type: "base64", value: base64body }
});
}
async abort(errorCode) {
await this._session.sendMayFail("network.failRequest", {
request: this._requestId
});
}
};
}
});
// packages/playwright-core/src/server/bidi/third_party/bidiSerializer.ts
var UnserializableError, BidiSerializer, isPlainObject, isRegExp6, isDate3;
var init_bidiSerializer = __esm({
"packages/playwright-core/src/server/bidi/third_party/bidiSerializer.ts"() {
"use strict";
UnserializableError = class extends Error {
};
BidiSerializer = class _BidiSerializer {
static serialize(arg) {
switch (typeof arg) {
case "symbol":
case "function":
throw new UnserializableError(`Unable to serializable ${typeof arg}`);
case "object":
return _BidiSerializer._serializeObject(arg);
case "undefined":
return {
type: "undefined"
};
case "number":
return _BidiSerializer._serializeNumber(arg);
case "bigint":
return {
type: "bigint",
value: arg.toString()
};
case "string":
return {
type: "string",
value: arg
};
case "boolean":
return {
type: "boolean",
value: arg
};
}
}
static _serializeNumber(arg) {
let value2;
if (Object.is(arg, -0)) {
value2 = "-0";
} else if (Object.is(arg, Infinity)) {
value2 = "Infinity";
} else if (Object.is(arg, -Infinity)) {
value2 = "-Infinity";
} else if (Object.is(arg, NaN)) {
value2 = "NaN";
} else {
value2 = arg;
}
return {
type: "number",
value: value2
};
}
static _serializeObject(arg) {
if (arg === null) {
return {
type: "null"
};
} else if (Array.isArray(arg)) {
const parsedArray = arg.map((subArg) => {
return _BidiSerializer.serialize(subArg);
});
return {
type: "array",
value: parsedArray
};
} else if (isPlainObject(arg)) {
try {
JSON.stringify(arg);
} catch (error) {
if (error instanceof TypeError && error.message.startsWith("Converting circular structure to JSON")) {
error.message += " Recursive objects are not allowed.";
}
throw error;
}
const parsedObject = [];
for (const key in arg) {
parsedObject.push([_BidiSerializer.serialize(key), _BidiSerializer.serialize(arg[key])]);
}
return {
type: "object",
value: parsedObject
};
} else if (isRegExp6(arg)) {
return {
type: "regexp",
value: {
pattern: arg.source,
flags: arg.flags
}
};
} else if (isDate3(arg)) {
return {
type: "date",
value: arg.toISOString()
};
}
throw new UnserializableError(
"Custom object serialization not possible. Use plain objects instead."
);
}
};
isPlainObject = (obj) => {
return typeof obj === "object" && obj?.constructor === Object;
};
isRegExp6 = (obj) => {
return typeof obj === "object" && obj?.constructor === RegExp;
};
isDate3 = (obj) => {
return typeof obj === "object" && obj?.constructor === Date;
};
}
});
// packages/playwright-core/src/server/bidi/bidiDeserializer.ts
function deserializeBidiValue(result2, internalIdMap = /* @__PURE__ */ new Map()) {
switch (result2.type) {
case "undefined":
return void 0;
case "null":
return null;
case "number":
return typeof result2.value === "number" ? result2.value : parseUnserializableValue(result2.value);
case "boolean":
return Boolean(result2.value);
case "string":
return result2.value;
case "bigint":
return BigInt(result2.value);
case "array":
return deserializeBidiList(result2, internalIdMap);
case "arraybuffer":
return getValue(result2, internalIdMap, () => ({}));
case "date":
return getValue(result2, internalIdMap, () => new Date(result2.value));
case "error":
return getValue(result2, internalIdMap, () => {
const error = new Error();
error.stack = "";
return error;
});
case "function":
return void 0;
case "generator":
return getValue(result2, internalIdMap, () => ({}));
case "htmlcollection":
return { ...deserializeBidiList(result2, internalIdMap) };
case "map":
return getValue(result2, internalIdMap, () => ({}));
case "node":
return "ref: <Node>";
case "nodelist":
return { ...deserializeBidiList(result2, internalIdMap) };
case "object":
return deserializeBidiMapping(result2, internalIdMap);
case "promise":
return getValue(result2, internalIdMap, () => ({}));
case "proxy":
return getValue(result2, internalIdMap, () => ({}));
case "regexp":
return getValue(result2, internalIdMap, () => new RegExp(result2.value.pattern, result2.value.flags));
case "set":
return getValue(result2, internalIdMap, () => ({}));
case "symbol":
return void 0;
case "typedarray":
return void 0;
case "weakmap":
return getValue(result2, internalIdMap, () => ({}));
case "weakset":
return getValue(result2, internalIdMap, () => ({}));
case "window":
return "ref: <Window>";
}
}
function getValue(bidiValue, internalIdMap, defaultValue) {
if ("internalId" in bidiValue && bidiValue.internalId) {
if (internalIdMap.has(bidiValue.internalId)) {
return internalIdMap.get(bidiValue.internalId);
} else {
const value2 = defaultValue();
internalIdMap.set(bidiValue.internalId, value2);
return value2;
}
} else {
return defaultValue();
}
}
function deserializeBidiList(bidiValue, internalIdMap) {
const result2 = getValue(bidiValue, internalIdMap, () => []);
for (const val of bidiValue.value || [])
result2.push(deserializeBidiValue(val, internalIdMap));
return result2;
}
function deserializeBidiMapping(bidiValue, internalIdMap) {
const result2 = getValue(bidiValue, internalIdMap, () => ({}));
for (const [serializedKey, serializedValue] of bidiValue.value || []) {
const key = typeof serializedKey === "string" ? serializedKey : deserializeBidiValue(serializedKey, internalIdMap);
const value2 = deserializeBidiValue(serializedValue, internalIdMap);
result2[key] = value2;
}
return result2;
}
var init_bidiDeserializer = __esm({
"packages/playwright-core/src/server/bidi/bidiDeserializer.ts"() {
"use strict";
init_javascript();
}
});
// packages/playwright-core/src/server/bidi/bidiExecutionContext.ts
function rewriteError2(error) {
if (error.message.includes("too much recursion") || error.message.includes("stack limit exceeded"))
throw new Error("Cannot serialize result: object reference chain is too long.");
throw error;
}
function renderPreview2(remoteObject, nested = false) {
switch (remoteObject.type) {
case "undefined":
case "null":
return remoteObject.type;
case "number":
case "boolean":
case "string":
return String(remoteObject.value);
case "bigint":
return `${remoteObject.value}n`;
case "date":
return String(new Date(remoteObject.value));
case "regexp":
return String(new RegExp(remoteObject.value.pattern, remoteObject.value.flags));
case "node":
return remoteObject.value?.localName || "Node";
case "object":
if (nested)
return "Object";
const tokens = [];
for (const [name, value2] of remoteObject.value || []) {
if (typeof name === "string")
tokens.push(`${name}: ${renderPreview2(value2, true)}`);
}
return `{${tokens.join(", ")}}`;
case "array":
case "htmlcollection":
case "nodelist":
if (nested || !remoteObject.value)
return remoteObject.value ? `Array(${remoteObject.value.length})` : "Array";
return `[${remoteObject.value.map((v) => renderPreview2(v, true)).join(", ")}]`;
case "map":
return remoteObject.value ? `Map(${remoteObject.value.length})` : "Map";
case "set":
return remoteObject.value ? `Set(${remoteObject.value.length})` : "Set";
case "arraybuffer":
return "ArrayBuffer";
case "error":
return "Error";
case "function":
return "Function";
case "generator":
return "Generator";
case "promise":
return "Promise";
case "proxy":
return "Proxy";
case "symbol":
return "Symbol()";
case "typedarray":
return "TypedArray";
case "weakmap":
return "WeakMap";
case "weakset":
return "WeakSet";
case "window":
return "Window";
}
}
function createHandle2(context2, remoteObject) {
if (remoteObject.type === "node") {
assert(context2 instanceof FrameExecutionContext);
return new ElementHandle(context2, remoteObject.handle);
}
const objectId = "handle" in remoteObject ? remoteObject.handle : void 0;
const preview = renderPreview2(remoteObject);
const handle = new JSHandle(context2, remoteObject.type, preview, objectId, deserializeBidiValue(remoteObject));
handle._setPreview(preview);
return handle;
}
var BidiExecutionContext;
var init_bidiExecutionContext = __esm({
"packages/playwright-core/src/server/bidi/bidiExecutionContext.ts"() {
"use strict";
init_utilityScriptSerializers();
init_assert();
init_javascript();
init_dom();
init_bidiProtocol();
init_bidiSerializer();
init_bidiDeserializer();
BidiExecutionContext = class {
constructor(session2, realmInfo) {
this._session = session2;
if (realmInfo.type === "window") {
this._target = {
context: realmInfo.context,
sandbox: realmInfo.sandbox
};
} else {
this._target = {
realm: realmInfo.realm
};
}
}
async rawEvaluateJSON(expression2) {
const response2 = await this._session.send("script.evaluate", {
expression: expression2,
target: this._target,
serializationOptions: {
maxObjectDepth: 10,
maxDomDepth: 10
},
awaitPromise: true,
userActivation: true
});
if (response2.type === "success")
return deserializeBidiValue(response2.result);
if (response2.type === "exception")
throw new JavaScriptErrorInEvaluate(response2.exceptionDetails.text);
throw new JavaScriptErrorInEvaluate("Unexpected response type: " + JSON.stringify(response2));
}
async rawEvaluateHandle(context2, expression2) {
const response2 = await this._session.send("script.evaluate", {
expression: expression2,
target: this._target,
resultOwnership: Script.ResultOwnership.Root,
// Necessary for the handle to be returned.
serializationOptions: { maxObjectDepth: 0, maxDomDepth: 0 },
awaitPromise: true,
userActivation: true
});
if (response2.type === "success") {
if ("handle" in response2.result)
return createHandle2(context2, response2.result);
throw new JavaScriptErrorInEvaluate("Cannot get handle: " + JSON.stringify(response2.result));
}
if (response2.type === "exception")
throw new JavaScriptErrorInEvaluate(response2.exceptionDetails.text);
throw new JavaScriptErrorInEvaluate("Unexpected response type: " + JSON.stringify(response2));
}
async evaluateWithArguments(functionDeclaration, returnByValue, utilityScript, values, handles) {
const response2 = await this._session.send("script.callFunction", {
functionDeclaration,
target: this._target,
arguments: [
{ handle: utilityScript._objectId },
...values.map(BidiSerializer.serialize),
...handles.map((handle) => ({ handle: handle._objectId }))
],
resultOwnership: returnByValue ? void 0 : Script.ResultOwnership.Root,
// Necessary for the handle to be returned.
serializationOptions: returnByValue ? {} : { maxObjectDepth: 0, maxDomDepth: 0 },
awaitPromise: true,
userActivation: true
}).catch(rewriteError2);
if (response2.type === "exception")
throw new JavaScriptErrorInEvaluate(response2.exceptionDetails.text);
if (response2.type === "success") {
if (returnByValue)
return parseEvaluationResultValue(deserializeBidiValue(response2.result));
return createHandle2(utilityScript._context, response2.result);
}
throw new JavaScriptErrorInEvaluate("Unexpected response type: " + JSON.stringify(response2));
}
async getProperties(handle) {
const names = await handle.evaluate((object) => {
const names2 = [];
const descriptors = Object.getOwnPropertyDescriptors(object);
for (const name in descriptors) {
if (descriptors[name]?.enumerable)
names2.push(name);
}
return names2;
});
const values = await Promise.all(names.map(async (name) => {
const value2 = await this._rawCallFunction("(object, name) => object[name]", [{ handle: handle._objectId }, { type: "string", value: name }], true, false);
return createHandle2(handle._context, value2);
}));
const map = /* @__PURE__ */ new Map();
for (let i = 0; i < names.length; i++)
map.set(names[i], values[i]);
return map;
}
async releaseHandle(handle) {
if (!handle._objectId)
return;
await this._session.send("script.disown", {
target: this._target,
handles: [handle._objectId]
});
}
shouldPrependErrorPrefix() {
return false;
}
async nodeIdForElementHandle(handle) {
const shared = await this._remoteValueForReference({ handle: handle._objectId });
if (!("sharedId" in shared))
throw new Error("Element is not a node");
return {
sharedId: shared.sharedId
};
}
async remoteObjectForNodeId(context2, nodeId) {
const result2 = await this._remoteValueForReference(nodeId, true);
if (!("handle" in result2))
throw new Error("Can't get remote object for nodeId");
return createHandle2(context2, result2);
}
async contentFrameIdForFrame(handle) {
const contentWindow = await this._rawCallFunction("e => e.contentWindow", [{ handle: handle._objectId }]);
if (contentWindow?.type === "window")
return contentWindow.value.context;
return null;
}
async frameIdForWindowHandle(handle) {
if (!handle._objectId)
throw new Error("JSHandle is not a DOM node handle");
const contentWindow = await this._remoteValueForReference({ handle: handle._objectId });
if (contentWindow.type === "window")
return contentWindow.value.context;
return null;
}
async _remoteValueForReference(reference, createHandle5) {
return await this._rawCallFunction("e => e", [reference], createHandle5);
}
async _rawCallFunction(functionDeclaration, args, createHandle5, awaitPromise = true) {
const response2 = await this._session.send("script.callFunction", {
functionDeclaration,
target: this._target,
arguments: args,
// "Root" is necessary for the handle to be returned.
resultOwnership: createHandle5 ? Script.ResultOwnership.Root : Script.ResultOwnership.None,
serializationOptions: { maxObjectDepth: 0, maxDomDepth: 0 },
awaitPromise,
userActivation: true
});
if (response2.type === "exception")
throw new JavaScriptErrorInEvaluate(response2.exceptionDetails.text);
if (response2.type === "success")
return response2.result;
throw new JavaScriptErrorInEvaluate("Unexpected response type: " + JSON.stringify(response2));
}
};
}
});
// packages/playwright-core/src/server/bidi/third_party/bidiKeyboard.ts
var getBidiKeyValue;
var init_bidiKeyboard = __esm({
"packages/playwright-core/src/server/bidi/third_party/bidiKeyboard.ts"() {
"use strict";
getBidiKeyValue = (keyName) => {
switch (keyName) {
case "\r":
case "\n":
keyName = "Enter";
break;
}
if ([...keyName].length === 1) {
return keyName;
}
switch (keyName) {
case "Cancel":
return "\uE001";
case "Help":
return "\uE002";
case "Backspace":
return "\uE003";
case "Tab":
return "\uE004";
case "Clear":
return "\uE005";
case "Enter":
return "\uE007";
case "Shift":
case "ShiftLeft":
return "\uE008";
case "Control":
case "ControlLeft":
return "\uE009";
case "Alt":
case "AltLeft":
return "\uE00A";
case "Pause":
return "\uE00B";
case "Escape":
return "\uE00C";
case "PageUp":
return "\uE00E";
case "PageDown":
return "\uE00F";
case "End":
return "\uE010";
case "Home":
return "\uE011";
case "ArrowLeft":
return "\uE012";
case "ArrowUp":
return "\uE013";
case "ArrowRight":
return "\uE014";
case "ArrowDown":
return "\uE015";
case "Insert":
return "\uE016";
case "Delete":
return "\uE017";
case "NumpadEqual":
return "\uE019";
case "Numpad0":
return "\uE01A";
case "Numpad1":
return "\uE01B";
case "Numpad2":
return "\uE01C";
case "Numpad3":
return "\uE01D";
case "Numpad4":
return "\uE01E";
case "Numpad5":
return "\uE01F";
case "Numpad6":
return "\uE020";
case "Numpad7":
return "\uE021";
case "Numpad8":
return "\uE022";
case "Numpad9":
return "\uE023";
case "NumpadMultiply":
return "\uE024";
case "NumpadAdd":
return "\uE025";
case "NumpadSubtract":
return "\uE027";
case "NumpadDecimal":
return "\uE028";
case "NumpadDivide":
return "\uE029";
case "F1":
return "\uE031";
case "F2":
return "\uE032";
case "F3":
return "\uE033";
case "F4":
return "\uE034";
case "F5":
return "\uE035";
case "F6":
return "\uE036";
case "F7":
return "\uE037";
case "F8":
return "\uE038";
case "F9":
return "\uE039";
case "F10":
return "\uE03A";
case "F11":
return "\uE03B";
case "F12":
return "\uE03C";
case "Meta":
case "MetaLeft":
return "\uE03D";
case "ShiftRight":
return "\uE050";
case "ControlRight":
return "\uE051";
case "AltRight":
return "\uE052";
case "MetaRight":
return "\uE053";
case "Space":
return " ";
case "Digit0":
return "0";
case "Digit1":
return "1";
case "Digit2":
return "2";
case "Digit3":
return "3";
case "Digit4":
return "4";
case "Digit5":
return "5";
case "Digit6":
return "6";
case "Digit7":
return "7";
case "Digit8":
return "8";
case "Digit9":
return "9";
case "KeyA":
return "a";
case "KeyB":
return "b";
case "KeyC":
return "c";
case "KeyD":
return "d";
case "KeyE":
return "e";
case "KeyF":
return "f";
case "KeyG":
return "g";
case "KeyH":
return "h";
case "KeyI":
return "i";
case "KeyJ":
return "j";
case "KeyK":
return "k";
case "KeyL":
return "l";
case "KeyM":
return "m";
case "KeyN":
return "n";
case "KeyO":
return "o";
case "KeyP":
return "p";
case "KeyQ":
return "q";
case "KeyR":
return "r";
case "KeyS":
return "s";
case "KeyT":
return "t";
case "KeyU":
return "u";
case "KeyV":
return "v";
case "KeyW":
return "w";
case "KeyX":
return "x";
case "KeyY":
return "y";
case "KeyZ":
return "z";
case "Semicolon":
return ";";
case "Equal":
return "=";
case "Comma":
return ",";
case "Minus":
return "-";
case "Period":
return ".";
case "Slash":
return "/";
case "Backquote":
return "`";
case "BracketLeft":
return "[";
case "Backslash":
return "\\";
case "BracketRight":
return "]";
case "Quote":
return '"';
default:
throw new Error(`Unknown key: "${keyName}"`);
}
};
}
});
// packages/playwright-core/src/server/bidi/bidiInput.ts
function toBidiButton(button) {
switch (button) {
case "left":
return 0;
case "right":
return 2;
case "middle":
return 1;
}
throw new Error("Unknown button: " + button);
}
var RawKeyboardImpl2, RawMouseImpl2, RawTouchscreenImpl2;
var init_bidiInput = __esm({
"packages/playwright-core/src/server/bidi/bidiInput.ts"() {
"use strict";
init_input();
init_bidiKeyboard();
init_bidiProtocol();
RawKeyboardImpl2 = class {
constructor(session2) {
this._session = session2;
}
setSession(session2) {
this._session = session2;
}
async keydown(progress2, modifiers, keyName, description, autoRepeat) {
keyName = resolveSmartModifierString(keyName);
const actions = [];
actions.push({ type: "keyDown", value: getBidiKeyValue(keyName) });
await this._performActions(progress2, actions);
}
async keyup(progress2, modifiers, keyName, description) {
keyName = resolveSmartModifierString(keyName);
const actions = [];
actions.push({ type: "keyUp", value: getBidiKeyValue(keyName) });
await this._performActions(progress2, actions);
}
async sendText(progress2, text2) {
const actions = [];
for (const char of text2) {
const value2 = getBidiKeyValue(char);
actions.push({ type: "keyDown", value: value2 });
actions.push({ type: "keyUp", value: value2 });
}
await this._performActions(progress2, actions);
}
async _performActions(progress2, actions) {
await progress2.race(this._session.send("input.performActions", {
context: this._session.sessionId,
actions: [
{
type: "key",
id: "pw_keyboard",
actions
}
]
}));
}
};
RawMouseImpl2 = class {
constructor(session2) {
this._session = session2;
}
async move(progress2, x, y, button, buttons, modifiers, forClick) {
await this._performActions(progress2, [{ type: "pointerMove", x, y }]);
}
async down(progress2, x, y, button, buttons, modifiers, clickCount) {
await this._performActions(progress2, [{ type: "pointerDown", button: toBidiButton(button) }]);
}
async up(progress2, x, y, button, buttons, modifiers, clickCount) {
await this._performActions(progress2, [{ type: "pointerUp", button: toBidiButton(button) }]);
}
async wheel(progress2, x, y, buttons, modifiers, deltaX, deltaY) {
x = Math.floor(x);
y = Math.floor(y);
await progress2.race(this._session.send("input.performActions", {
context: this._session.sessionId,
actions: [
{
type: "wheel",
id: "pw_mouse_wheel",
actions: [{ type: "scroll", x, y, deltaX, deltaY }]
}
]
}));
}
async _performActions(progress2, actions) {
await progress2.race(this._session.send("input.performActions", {
context: this._session.sessionId,
actions: [
{
type: "pointer",
id: "pw_mouse",
parameters: {
pointerType: Input.PointerType.Mouse
},
actions
}
]
}));
}
};
RawTouchscreenImpl2 = class {
constructor(session2) {
this._session = session2;
}
async tap(progress2, x, y, modifiers) {
}
};
}
});
// packages/playwright-core/src/server/bidi/bidiPdf.ts
function convertPrintParameterToInches2(text2) {
if (text2 === void 0)
return void 0;
let unit = text2.substring(text2.length - 2).toLowerCase();
let valueText = "";
if (unitToPixels2.hasOwnProperty(unit)) {
valueText = text2.substring(0, text2.length - 2);
} else {
unit = "px";
valueText = text2;
}
const value2 = Number(valueText);
assert(!isNaN(value2), "Failed to parse parameter value: " + text2);
const pixels = value2 * unitToPixels2[unit];
return pixels / 96;
}
var PagePaperFormats2, unitToPixels2, BidiPDF;
var init_bidiPdf = __esm({
"packages/playwright-core/src/server/bidi/bidiPdf.ts"() {
"use strict";
init_assert();
PagePaperFormats2 = {
letter: { width: 8.5, height: 11 },
legal: { width: 8.5, height: 14 },
tabloid: { width: 11, height: 17 },
ledger: { width: 17, height: 11 },
a0: { width: 33.1, height: 46.8 },
a1: { width: 23.4, height: 33.1 },
a2: { width: 16.54, height: 23.4 },
a3: { width: 11.7, height: 16.54 },
a4: { width: 8.27, height: 11.7 },
a5: { width: 5.83, height: 8.27 },
a6: { width: 4.13, height: 5.83 }
};
unitToPixels2 = {
"px": 1,
"in": 96,
"cm": 37.8,
"mm": 3.78
};
BidiPDF = class {
constructor(session2) {
this._session = session2;
}
async generate(options2) {
const {
scale = 1,
printBackground = false,
landscape = false,
pageRanges = "",
margin = {}
} = options2;
let paperWidth = 8.5;
let paperHeight = 11;
if (options2.format) {
const format2 = PagePaperFormats2[options2.format.toLowerCase()];
assert(format2, "Unknown paper format: " + options2.format);
paperWidth = format2.width;
paperHeight = format2.height;
} else {
paperWidth = convertPrintParameterToInches2(options2.width) || paperWidth;
paperHeight = convertPrintParameterToInches2(options2.height) || paperHeight;
}
const { data } = await this._session.send("browsingContext.print", {
context: this._session.sessionId,
background: printBackground,
margin: {
bottom: convertPrintParameterToInches2(margin.bottom) || 0,
left: convertPrintParameterToInches2(margin.left) || 0,
right: convertPrintParameterToInches2(margin.right) || 0,
top: convertPrintParameterToInches2(margin.top) || 0
},
orientation: landscape ? "landscape" : "portrait",
page: {
width: paperWidth,
height: paperHeight
},
pageRanges: pageRanges ? pageRanges.split(",").map((r) => r.trim()) : void 0,
scale
});
return Buffer.from(data, "base64");
}
};
}
});
// packages/playwright-core/src/server/bidi/bidiPage.ts
function toBidiExecutionContext(executionContext) {
return executionContext.delegate;
}
var UTILITY_WORLD_NAME, kPlaywrightBindingChannel, BidiPage;
var init_bidiPage = __esm({
"packages/playwright-core/src/server/bidi/bidiPage.ts"() {
"use strict";
init_debugLogger();
init_eventsHelper();
init_dialog();
init_dom();
init_bidiBrowser();
init_page();
init_bidiExecutionContext();
init_bidiInput();
init_bidiNetworkManager();
init_bidiPdf();
init_bidiProtocol();
init_progress();
init_network2();
UTILITY_WORLD_NAME = "__playwright_utility_world__";
kPlaywrightBindingChannel = "playwrightChannel";
BidiPage = class {
constructor(browserContext, bidiSession, opener) {
this._realmToWorkerContext = /* @__PURE__ */ new Map();
this._sessionListeners = [];
this._initScriptIds = /* @__PURE__ */ new Map();
this._fragmentNavigations = /* @__PURE__ */ new Set();
this._session = bidiSession;
this._opener = opener;
this.rawKeyboard = new RawKeyboardImpl2(bidiSession);
this.rawMouse = new RawMouseImpl2(bidiSession);
this.rawTouchscreen = new RawTouchscreenImpl2(bidiSession);
this._contextIdToContext = /* @__PURE__ */ new Map();
this._page = new Page(this, browserContext);
this._browserContext = browserContext;
this._networkManager = new BidiNetworkManager(this._session, this._page);
this._pdf = new BidiPDF(this._session);
this._page.on(Page.Events.FrameDetached, (frame) => this._removeContextsForFrame(frame, false));
this._sessionListeners = [
eventsHelper.addEventListener(bidiSession, "script.realmCreated", this._onRealmCreated.bind(this)),
eventsHelper.addEventListener(bidiSession, "script.message", this._onScriptMessage.bind(this)),
eventsHelper.addEventListener(bidiSession, "browsingContext.contextDestroyed", this._onBrowsingContextDestroyed.bind(this)),
eventsHelper.addEventListener(bidiSession, "browsingContext.navigationStarted", this._onNavigationStarted.bind(this)),
eventsHelper.addEventListener(bidiSession, "browsingContext.navigationCommitted", this._onNavigationCommitted.bind(this)),
eventsHelper.addEventListener(bidiSession, "browsingContext.navigationAborted", this._onNavigationAborted.bind(this)),
eventsHelper.addEventListener(bidiSession, "browsingContext.navigationFailed", this._onNavigationFailed.bind(this)),
eventsHelper.addEventListener(bidiSession, "browsingContext.fragmentNavigated", this._onFragmentNavigated.bind(this)),
eventsHelper.addEventListener(bidiSession, "browsingContext.historyUpdated", this._onHistoryUpdated.bind(this)),
eventsHelper.addEventListener(bidiSession, "browsingContext.domContentLoaded", this._onDomContentLoaded.bind(this)),
eventsHelper.addEventListener(bidiSession, "browsingContext.load", this._onLoad.bind(this)),
eventsHelper.addEventListener(bidiSession, "browsingContext.downloadWillBegin", this._onDownloadWillBegin.bind(this)),
eventsHelper.addEventListener(bidiSession, "browsingContext.downloadEnd", this._onDownloadEnded.bind(this)),
eventsHelper.addEventListener(bidiSession, "browsingContext.userPromptOpened", this._onUserPromptOpened.bind(this)),
eventsHelper.addEventListener(bidiSession, "log.entryAdded", this._onLogEntryAdded.bind(this)),
eventsHelper.addEventListener(bidiSession, "input.fileDialogOpened", this._onFileDialogOpened.bind(this))
];
this._initialize().then(
() => this._page.reportAsNew(this._opener?._page),
(error) => this._page.reportAsNew(this._opener?._page, error)
);
}
async _initialize() {
this._onFrameAttached(this._session.sessionId, null);
await Promise.all([
this.updateHttpCredentials()
// If the page is created by the Playwright client's call, some initialization
// may be pending. Wait for it to complete before reporting the page as new.
]);
}
didClose() {
this._session.dispose();
eventsHelper.removeEventListeners(this._sessionListeners);
this._page._didClose();
}
_onFrameAttached(frameId, parentFrameId) {
return this._page.frameManager.frameAttached(frameId, parentFrameId);
}
_removeContextsForFrame(frame, notifyFrame) {
for (const [contextId4, context2] of this._contextIdToContext) {
if (context2.frame === frame) {
this._contextIdToContext.delete(contextId4);
if (notifyFrame)
frame.contextDestroyed(context2);
}
}
}
_onRealmCreated(realmInfo) {
if (realmInfo.type === "dedicated-worker") {
const delegate2 = new BidiExecutionContext(this._session, realmInfo);
const worker = new Worker(this._page, realmInfo.origin);
this._realmToWorkerContext.set(realmInfo.realm, worker.createExecutionContext(delegate2));
worker.workerScriptLoaded();
this._page.addWorker(realmInfo.realm, worker);
return;
}
if (this._contextIdToContext.has(realmInfo.realm))
return;
if (realmInfo.type !== "window")
return;
const frame = this._page.frameManager.frame(realmInfo.context);
if (!frame)
return;
let worldName;
if (!realmInfo.sandbox) {
worldName = "main";
this._touchUtilityWorld(realmInfo.context);
} else if (realmInfo.sandbox === UTILITY_WORLD_NAME) {
worldName = "utility";
} else {
return;
}
const delegate = new BidiExecutionContext(this._session, realmInfo);
const context2 = new FrameExecutionContext(delegate, frame, worldName);
frame.contextCreated(worldName, context2);
this._contextIdToContext.set(realmInfo.realm, context2);
}
async _touchUtilityWorld(context2) {
await this._session.sendMayFail("script.evaluate", {
expression: "1 + 1",
target: {
context: context2,
sandbox: UTILITY_WORLD_NAME
},
serializationOptions: {
maxObjectDepth: 10,
maxDomDepth: 10
},
awaitPromise: true,
userActivation: true
});
}
_onRealmDestroyed(params2) {
const context2 = this._contextIdToContext.get(params2.realm);
if (context2) {
this._contextIdToContext.delete(params2.realm);
context2.frame.contextDestroyed(context2);
return true;
}
const existed = this._realmToWorkerContext.delete(params2.realm);
if (existed) {
this._page.removeWorker(params2.realm);
return true;
}
return false;
}
// TODO: route the message directly to the browser
_onBrowsingContextDestroyed(params2) {
this._browserContext._browser._onBrowsingContextDestroyed(params2);
}
_onNavigationStarted(params2) {
const frameId = params2.context;
this._page.frameManager.frameRequestedNavigation(frameId, params2.navigation);
}
_onNavigationCommitted(params2) {
const frameId = params2.context;
const frame = this._page.frameManager.frame(frameId);
this._browserContext.doGrantGlobalPermissionsForURL(params2.url).catch((error) => debugLogger.log("error", error));
this._page.frameManager.frameCommittedNewDocumentNavigation(
frameId,
params2.url,
frame._name,
params2.navigation,
/* initial */
false
);
}
_onDomContentLoaded(params2) {
const frameId = params2.context;
this._page.frameManager.frameLifecycleEvent(frameId, "domcontentloaded");
}
_onLoad(params2) {
this._page.frameManager.frameLifecycleEvent(params2.context, "load");
}
_onNavigationAborted(params2) {
this._page.frameManager.frameAbortedNavigation(params2.context, "Navigation aborted", params2.navigation || void 0);
}
_onNavigationFailed(params2) {
this._page.frameManager.frameAbortedNavigation(params2.context, "Navigation failed", params2.navigation || void 0);
}
_onFragmentNavigated(params2) {
if (params2.navigation)
this._fragmentNavigations.add(params2.navigation);
this._page.frameManager.frameCommittedSameDocumentNavigation(params2.context, params2.url);
}
_onHistoryUpdated(params2) {
this._page.frameManager.frameCommittedSameDocumentNavigation(params2.context, params2.url);
}
_onUserPromptOpened(event) {
this._page.browserContext.dialogManager.dialogDidOpen(new Dialog(
this._page,
event.type,
event.message,
async (accept, userText) => {
await this._session.send("browsingContext.handleUserPrompt", { context: event.context, accept, userText });
},
event.defaultValue
));
}
_onDownloadWillBegin(event) {
if (!event.navigation)
return;
this._page.frameManager.frameAbortedNavigation(event.context, "Download is starting");
let originPage = this._page.initializedOrUndefined();
if (!originPage && this._opener)
originPage = this._opener._page.initializedOrUndefined();
if (!originPage)
return;
this._browserContext._browser.downloadCreated(originPage, event.navigation, event.url, event.suggestedFilename);
}
_onDownloadEnded(event) {
if (!event.navigation)
return;
this._browserContext._browser.downloadFinished(event.navigation, event.status === "canceled" ? "canceled" : void 0);
}
_onLogEntryAdded(params2) {
if (params2.type === "javascript" && params2.level === "error") {
let errorName = "";
let errorMessage;
if (params2.text?.includes(": ")) {
const index = params2.text.indexOf(": ");
errorName = params2.text.substring(0, index);
errorMessage = params2.text.substring(index + 2);
} else {
errorMessage = params2.text ?? void 0;
}
const error = new Error(errorMessage);
error.name = errorName;
error.stack = `${params2.text}
${params2.stackTrace?.callFrames.map((f) => {
const location4 = `${f.url}:${f.lineNumber + 1}:${f.columnNumber + 1}`;
return f.functionName ? ` at ${f.functionName} (${location4})` : ` at ${location4}`;
}).join("\n")}`;
const callFrame2 = params2.stackTrace?.callFrames[0];
const location3 = callFrame2 ?? { url: "", lineNumber: 1, columnNumber: 1 };
this._page.addPageError(error, location3);
return;
}
if (params2.type !== "console")
return;
const entry = params2;
const context2 = this._contextIdToContext.get(params2.source.realm) ?? this._realmToWorkerContext.get(params2.source.realm);
if (!context2)
return;
const callFrame = params2.stackTrace?.callFrames[0];
const location2 = callFrame ?? { url: "", lineNumber: 1, columnNumber: 1 };
const type3 = entry.method === "warn" ? "warning" : entry.method;
const text2 = (entry.method === "timeLog" || entry.method === "timeEnd") && entry.text ? entry.text : void 0;
this._page.addConsoleMessage(null, type3, entry.args.map((arg) => createHandle2(context2, arg)), location2, text2, params2.timestamp);
}
async _onFileDialogOpened(params2) {
if (!params2.element)
return;
const frame = this._page.frameManager.frame(params2.context);
if (!frame)
return;
const executionContext = await frame.mainContext();
try {
const handle = await toBidiExecutionContext(executionContext).remoteObjectForNodeId(executionContext, { sharedId: params2.element.sharedId });
await this._page._onFileChooserOpened(handle);
} catch {
}
}
async navigateFrame(frame, url2, referrer) {
const { navigation } = await this._session.send("browsingContext.navigate", {
context: frame._id,
url: url2
});
if (navigation && this._fragmentNavigations.has(navigation)) {
this._fragmentNavigations.delete(navigation);
return {};
}
return { newDocumentId: navigation || void 0 };
}
async updateExtraHTTPHeaders() {
const allHeaders = mergeHeaders([
this._browserContext._options.extraHTTPHeaders,
this._page.extraHTTPHeaders()
]);
await this._session.send("network.setExtraHeaders", {
headers: allHeaders.map(({ name, value: value2 }) => ({ name, value: { type: "string", value: value2 } })),
contexts: [this._session.sessionId]
});
}
async updateEmulateMedia() {
}
async updateUserAgent() {
}
async bringToFront() {
await this._session.send("browsingContext.activate", {
context: this._session.sessionId
});
}
async updateEmulatedViewportSize() {
const options2 = this._browserContext._options;
const emulatedSize = this._page.emulatedSize();
if (!emulatedSize)
return;
const screenSize = emulatedSize.screen;
const viewportSize = emulatedSize.viewport;
await Promise.all([
this._session.send("browsingContext.setViewport", {
context: this._session.sessionId,
viewport: {
width: viewportSize.width,
height: viewportSize.height
},
devicePixelRatio: options2.deviceScaleFactor || 1
}),
this._session.send("emulation.setScreenOrientationOverride", {
contexts: [this._session.sessionId],
screenOrientation: getScreenOrientation(!!options2.isMobile, screenSize)
}),
this._session.send("emulation.setScreenSettingsOverride", {
contexts: [this._session.sessionId],
screenArea: {
width: screenSize.width,
height: screenSize.height
}
})
]);
}
async updateRequestInterception() {
await this._networkManager.setRequestInterception(this._page.requestInterceptors.length > 0);
}
async updateOffline() {
}
async updateHttpCredentials() {
await this._networkManager.setCredentials(this._browserContext._options.httpCredentials);
}
async updateFileChooserInterception() {
}
async reload() {
await this._session.send("browsingContext.reload", {
context: this._session.sessionId,
// ignoreCache: true,
wait: BrowsingContext.ReadinessState.Interactive
});
}
async goBack() {
return await this._session.send("browsingContext.traverseHistory", {
context: this._session.sessionId,
delta: -1
}).then(() => true).catch(() => false);
}
async goForward() {
return await this._session.send("browsingContext.traverseHistory", {
context: this._session.sessionId,
delta: 1
}).then(() => true).catch(() => false);
}
async requestGC() {
const result2 = await this._session.send("script.evaluate", {
expression: "TestUtils.gc()",
target: { context: this._session.sessionId },
awaitPromise: true
});
if (result2.type === "exception")
throw new Error("Method not implemented.");
}
async _onScriptMessage(event) {
if (event.channel !== kPlaywrightBindingChannel)
return;
const pageOrError = await this._page.waitForInitializedOrError();
if (pageOrError instanceof Error)
return;
const context2 = this._contextIdToContext.get(event.source.realm);
if (!context2)
return;
if (event.data.type !== "string")
return;
await this._page.onBindingCalled(event.data.value, context2);
}
async addInitScript(initScript) {
const { script } = await this._session.send("script.addPreloadScript", {
// TODO: remove function call from the source.
functionDeclaration: `() => { return ${initScript.source} }`,
// TODO: push to iframes?
contexts: [this._session.sessionId]
});
this._initScriptIds.set(initScript, script);
}
async removeInitScripts(initScripts) {
const ids = [];
for (const script of initScripts) {
const id = this._initScriptIds.get(script);
if (id)
ids.push(id);
this._initScriptIds.delete(script);
}
await Promise.all(ids.map((script) => this._session.send("script.removePreloadScript", { script })));
}
async closePage(runBeforeUnload) {
if (runBeforeUnload) {
this._session.sendMayFail("browsingContext.close", {
context: this._session.sessionId,
promptUnload: runBeforeUnload
});
} else {
await this._session.send("browsingContext.close", {
context: this._session.sessionId,
promptUnload: runBeforeUnload
});
}
}
async setBackgroundColor(color) {
if (color)
throw new Error("Not implemented");
}
async takeScreenshot(progress2, format2, documentRect, viewportRect, quality, fitsViewport, scale) {
const rect = documentRect || viewportRect;
const { data } = await progress2.race(this._session.send("browsingContext.captureScreenshot", {
context: this._session.sessionId,
format: {
type: `image/${format2 === "png" ? "png" : "jpeg"}`,
quality: quality !== void 0 ? quality / 100 : void 0
},
origin: documentRect ? "document" : "viewport",
clip: {
type: "box",
...rect
}
}));
return Buffer.from(data, "base64");
}
async getContentFrame(handle) {
const executionContext = toBidiExecutionContext(handle._context);
const frameId = await executionContext.contentFrameIdForFrame(handle);
if (!frameId)
return null;
return this._page.frameManager.frame(frameId);
}
async getOwnerFrame(handle) {
const windowHandle = await handle.evaluateHandle((node) => {
const doc = node.ownerDocument ?? node;
return doc.defaultView;
});
if (!windowHandle)
return null;
const executionContext = toBidiExecutionContext(handle._context);
return executionContext.frameIdForWindowHandle(windowHandle);
}
async getBoundingBox(handle) {
const box = await handle.evaluate((element2) => {
if (!(element2 instanceof Element) || element2.getClientRects().length === 0)
return null;
const rect = element2.getBoundingClientRect();
return { x: rect.x, y: rect.y, width: rect.width, height: rect.height };
});
if (!box)
return null;
const position = await this._framePosition(handle._frame);
if (!position)
return null;
box.x += position.x;
box.y += position.y;
return box;
}
// TODO: move to Frame.
async _framePosition(frame) {
if (frame === this._page.mainFrame())
return { x: 0, y: 0 };
const element2 = await frame.frameElement(nullProgress);
const box = await element2.boundingBox(nullProgress);
if (!box)
return null;
const style = await element2.evaluateInUtility(([injected, iframe]) => injected.describeIFrameStyle(iframe), {}).catch((e) => "error:notconnected");
if (style === "error:notconnected" || style === "transformed")
return null;
box.x += style.left;
box.y += style.top;
return box;
}
async scrollRectIntoViewIfNeeded(handle, rect) {
return await handle.evaluateInUtility(([injected, node]) => {
node.scrollIntoView({
block: "center",
inline: "center",
behavior: "instant"
});
}, null).then(() => "done").catch((e) => {
if (e instanceof Error && e.message.includes("Node is detached from document"))
return "error:notconnected";
if (e instanceof Error && e.message.includes("Node does not have a layout object"))
return "error:notvisible";
throw e;
});
}
startScreencast(options2) {
}
stopScreencast() {
}
rafCountForStablePosition() {
return 1;
}
async getContentQuads(handle) {
const quads = await handle.evaluateInUtility(([injected, node]) => {
if (!node.isConnected)
return "error:notconnected";
const rects = node.getClientRects();
if (!rects)
return null;
return [...rects].map((rect) => [
{ x: rect.left, y: rect.top },
{ x: rect.right, y: rect.top },
{ x: rect.right, y: rect.bottom },
{ x: rect.left, y: rect.bottom }
]);
}, null);
if (!quads || quads === "error:notconnected")
return quads;
const position = await this._framePosition(handle._frame);
if (!position)
return null;
quads.forEach((quad) => quad.forEach((point) => {
point.x += position.x;
point.y += position.y;
}));
return quads;
}
async setInputFilePaths(progress2, handle, paths) {
const fromContext = toBidiExecutionContext(handle._context);
await progress2.race(this._session.send("input.setFiles", {
context: this._session.sessionId,
element: await progress2.race(fromContext.nodeIdForElementHandle(handle)),
files: paths
}));
}
async adoptElementHandle(handle, to) {
const fromContext = toBidiExecutionContext(handle._context);
const nodeId = await fromContext.nodeIdForElementHandle(handle);
const executionContext = toBidiExecutionContext(to);
try {
return await executionContext.remoteObjectForNodeId(to, nodeId);
} catch {
throw new Error(kUnableToAdoptErrorMessage);
}
}
async inputActionEpilogue() {
}
async resetForReuse(progress2) {
}
async pdf(options2) {
return this._pdf.generate(options2);
}
async getFrameElement(frame) {
const parent = frame.parentFrame();
if (!parent)
throw new Error("Frame has been detached.");
const node = await this._getFrameNode(frame);
if (!node?.sharedId)
throw new Error("Frame has been detached.");
const parentFrameExecutionContext = await parent.mainContext();
return await toBidiExecutionContext(parentFrameExecutionContext).remoteObjectForNodeId(parentFrameExecutionContext, { sharedId: node.sharedId });
}
async _getFrameNode(frame) {
const parent = frame.parentFrame();
if (!parent)
return void 0;
const result2 = await this._session.send("browsingContext.locateNodes", {
context: parent._id,
locator: { type: "context", value: { context: frame._id } }
});
const node = result2.nodes[0];
return node;
}
shouldToggleStyleSheetToSyncAnimations() {
return true;
}
async setDockTile(image) {
}
};
}
});
// packages/playwright-core/src/server/bidi/bidiBrowser.ts
function fromBidiSameSite(sameSite) {
switch (sameSite) {
case "strict":
return "Strict";
case "lax":
return "Lax";
case "none":
return "None";
case "default":
return "Lax";
}
return "None";
}
function toBidiSameSite2(sameSite) {
switch (sameSite) {
case "Strict":
return Network.SameSite.Strict;
case "Lax":
return Network.SameSite.Lax;
case "None":
return Network.SameSite.None;
}
return Network.SameSite.None;
}
function getProxyConfiguration(proxySettings) {
if (!proxySettings)
return void 0;
const proxy = {
proxyType: "manual"
};
const url2 = new URL(proxySettings.server);
switch (url2.protocol) {
case "http:":
proxy.httpProxy = url2.host;
break;
case "https:":
proxy.sslProxy = url2.host;
break;
case "socks4:":
proxy.socksProxy = url2.host;
proxy.socksVersion = 4;
break;
case "socks5:":
proxy.socksProxy = url2.host;
proxy.socksVersion = 5;
break;
default:
throw new Error("Invalid proxy server protocol: " + proxySettings.server);
}
const bypass = proxySettings.bypass ?? process.env.PLAYWRIGHT_PROXY_BYPASS_FOR_TESTING;
if (bypass)
proxy.noProxy = bypass.split(",");
return proxy;
}
function getScreenOrientation(isMobile, viewportSize) {
const screenOrientation = {
type: "landscape-primary",
natural: Emulation.ScreenOrientationNatural.Landscape
};
if (isMobile) {
screenOrientation.natural = Emulation.ScreenOrientationNatural.Portrait;
if (viewportSize.width <= viewportSize.height)
screenOrientation.type = "portrait-primary";
}
return screenOrientation;
}
var BidiBrowser, BidiBrowserContext2, Network2;
var init_bidiBrowser = __esm({
"packages/playwright-core/src/server/bidi/bidiBrowser.ts"() {
"use strict";
init_eventsHelper();
init_browser();
init_browserContext();
init_network2();
init_bidiConnection();
init_bidiNetworkManager();
init_bidiPage();
init_page();
init_bidiProtocol();
BidiBrowser = class _BidiBrowser extends Browser {
constructor(parent, transport, options2) {
super(parent, options2);
this._contexts = /* @__PURE__ */ new Map();
this._bidiPages = /* @__PURE__ */ new Map();
this._cacheBehavior = "default";
this._connection = new BidiConnection(transport, this._onDisconnect.bind(this), options2.protocolLogger, options2.browserLogsCollector);
this._browserSession = this._connection.browserSession;
this._eventListeners = [
eventsHelper.addEventListener(this._browserSession, "browsingContext.contextCreated", this._onBrowsingContextCreated.bind(this)),
eventsHelper.addEventListener(this._browserSession, "script.realmDestroyed", this._onScriptRealmDestroyed.bind(this))
];
}
static async connect(parent, transport, options2) {
const browser = new _BidiBrowser(parent, transport, options2);
if (options2.__testHookOnConnectToBrowser)
await options2.__testHookOnConnectToBrowser();
browser._bidiSessionInfo = await browser._browserSession.send("session.new", {
capabilities: {
alwaysMatch: {
"acceptInsecureCerts": options2.persistent?.internalIgnoreHTTPSErrors || options2.persistent?.ignoreHTTPSErrors,
"proxy": getProxyConfiguration(options2.originalLaunchOptions.proxyOverride ?? options2.proxy),
"unhandledPromptBehavior": {
default: Session.UserPromptHandlerType.Ignore
},
"webSocketUrl": true,
// Chrome with WebDriver BiDi does not support prerendering
// yet because WebDriver BiDi behavior is not specified. See
// https://github.com/w3c/webdriver-bidi/issues/321.
"goog:prerenderingDisabled": true
}
}
});
await browser._browserSession.send("session.subscribe", {
events: [
"browsingContext",
"network",
"log",
"script",
"input"
]
});
await browser._browserSession.send("network.addIntercept", { phases: [Network.InterceptPhase.AuthRequired] });
await browser._browserSession.send("network.addDataCollector", {
dataTypes: [Network.DataType.Response],
maxEncodedDataSize: 2e7
// same default as in CDP: https://source.chromium.org/chromium/chromium/src/+/main:third_party/blink/renderer/core/inspector/inspector_network_agent.cc;l=134;drc=4128411589187a396829a827f59a655bed876aa7
});
if (options2.persistent) {
const context2 = new BidiBrowserContext2(browser, void 0, options2.persistent);
browser._defaultContext = context2;
await context2.initialize();
const page = await browser._defaultContext.doCreateNewPage();
await page.waitForInitializedOrError();
}
return browser;
}
_onDisconnect() {
this.didClose();
}
async doCreateNewContext(options2) {
const proxy = options2.proxyOverride || options2.proxy;
const { userContext } = await this._browserSession.send("browser.createUserContext", {
acceptInsecureCerts: options2.internalIgnoreHTTPSErrors || options2.ignoreHTTPSErrors,
proxy: getProxyConfiguration(proxy)
});
const context2 = new BidiBrowserContext2(this, userContext, options2);
await context2.initialize();
this._contexts.set(userContext, context2);
return context2;
}
contexts() {
return Array.from(this._contexts.values());
}
version() {
return this._bidiSessionInfo.capabilities.browserVersion;
}
userAgent() {
return this._bidiSessionInfo.capabilities.userAgent;
}
isConnected() {
return !this._connection.isClosed();
}
async updateCacheBehavior() {
const cacheBehavior = [...this._contexts.values()].some((context2) => context2.requestInterceptors.length > 0) ? "bypass" : "default";
if (this._cacheBehavior !== cacheBehavior) {
await this._browserSession.send("network.setCacheBehavior", { cacheBehavior });
this._cacheBehavior = cacheBehavior;
}
}
_onBrowsingContextCreated(event) {
if (event.parent) {
const parentFrameId = event.parent;
const page2 = this._findPageForFrame(parentFrameId);
if (page2) {
page2._session.addFrameBrowsingContext(event.context);
const frame = page2._page.frameManager.frameAttached(event.context, parentFrameId);
frame._url = event.url;
page2._getFrameNode(frame).then((node) => {
const attributes = node?.value?.attributes;
frame._name = attributes?.name ?? attributes?.id ?? "";
}, () => {
});
return;
}
return;
}
let context2 = this._contexts.get(event.userContext);
if (!context2)
context2 = this._defaultContext;
if (!context2)
return;
context2.doGrantGlobalPermissionsForURL(event.url);
const session2 = this._connection.createMainFrameBrowsingContextSession(event.context);
const opener = event.originalOpener && this._findPageForFrame(event.originalOpener);
const page = new BidiPage(context2, session2, opener || null);
page._page.mainFrame()._url = event.url;
this._bidiPages.set(event.context, page);
}
_onBrowsingContextDestroyed(event) {
if (event.parent) {
this._browserSession.removeFrameBrowsingContext(event.context);
const parentFrameId = event.parent;
for (const page of this._bidiPages.values()) {
const parentFrame = page._page.frameManager.frame(parentFrameId);
if (!parentFrame)
continue;
page._page.frameManager.frameDetached(event.context);
return;
}
return;
}
const bidiPage = this._bidiPages.get(event.context);
if (!bidiPage)
return;
bidiPage.didClose();
this._bidiPages.delete(event.context);
}
_onScriptRealmDestroyed(event) {
for (const page of this._bidiPages.values()) {
if (page._onRealmDestroyed(event))
return;
}
}
_findPageForFrame(frameId) {
for (const page of this._bidiPages.values()) {
if (page._page.frameManager.frame(frameId))
return page;
}
}
};
BidiBrowserContext2 = class extends BrowserContext {
constructor(browser, browserContextId, options2) {
super(browser, options2, browserContextId);
this._originToPermissions = /* @__PURE__ */ new Map();
this._initScriptIds = /* @__PURE__ */ new Map();
this.authenticateProxyViaHeader();
}
_bidiPages() {
return [...this._browser._bidiPages.values()].filter((bidiPage) => bidiPage._browserContext === this);
}
async initialize() {
const promises = [
super.initialize()
];
const downloadBehavior = this._options.acceptDownloads === "accept" ? { type: "allowed", destinationFolder: this._browser.options.downloadsPath } : { type: "denied" };
promises.push(this._browser._browserSession.send("browser.setDownloadBehavior", {
downloadBehavior,
userContexts: [this._userContextId()]
}));
promises.push(this.doUpdateDefaultViewport());
if (this._options.geolocation)
promises.push(this.setGeolocation(this._options.geolocation));
if (this._options.locale) {
promises.push(this._browser._browserSession.send("emulation.setLocaleOverride", {
locale: this._options.locale,
userContexts: [this._userContextId()]
}));
}
if (this._options.timezoneId) {
promises.push(this._browser._browserSession.send("emulation.setTimezoneOverride", {
timezone: this._options.timezoneId,
userContexts: [this._userContextId()]
}));
}
if (this._options.userAgent) {
promises.push(this._browser._browserSession.send("emulation.setUserAgentOverride", {
userAgent: this._options.userAgent,
userContexts: [this._userContextId()]
}));
}
if (this._options.extraHTTPHeaders)
promises.push(this.doUpdateExtraHTTPHeaders());
if (this._options.permissions)
promises.push(this.doGrantPermissions("*", this._options.permissions));
if (this._options.offline)
promises.push(this.doUpdateOffline());
await Promise.all(promises);
}
possiblyUninitializedPages() {
return this._bidiPages().map((bidiPage) => bidiPage._page);
}
async doCreateNewPage() {
const { context: context2 } = await this._browser._browserSession.send("browsingContext.create", {
type: BrowsingContext.CreateType.Window,
userContext: this._browserContextId
});
return this._browser._bidiPages.get(context2)._page;
}
async doGetCookies(urls) {
const { cookies } = await this._browser._browserSession.send(
"storage.getCookies",
{ partition: { type: "storageKey", userContext: this._browserContextId } }
);
return filterCookies(cookies.map((c) => {
const copy = {
name: c.name,
value: bidiBytesValueToString(c.value),
domain: c.domain,
path: c.path,
httpOnly: c.httpOnly,
secure: c.secure,
expires: c.expiry ?? -1,
sameSite: c.sameSite ? fromBidiSameSite(c.sameSite) : "None"
};
return copy;
}), urls);
}
async addCookies(cookies) {
cookies = rewriteCookies(cookies);
const promises = cookies.map(async (c) => {
const cookie = {
name: c.name,
value: { type: "string", value: c.value },
domain: c.domain,
path: c.path,
httpOnly: c.httpOnly,
secure: c.secure,
sameSite: c.sameSite && toBidiSameSite2(c.sameSite),
expiry: c.expires === -1 || c.expires === void 0 ? void 0 : Math.round(c.expires)
};
try {
return await this._browser._browserSession.send(
"storage.setCookie",
{ cookie, partition: { type: "storageKey", userContext: this._browserContextId, sourceOrigin: c.partitionKey } }
);
} catch (e) {
if (!e.message.startsWith("Protocol error (storage.setCookie): unable to set cookie"))
throw e;
}
});
await Promise.all(promises);
}
async doClearCookies() {
await this._browser._browserSession.send(
"storage.deleteCookies",
{ partition: { type: "storageKey", userContext: this._browserContextId } }
);
}
async doGrantPermissions(origin, permissions) {
if (origin === "null")
return;
const currentPermissions = this._originToPermissions.get(origin) || [];
const toGrant = permissions.filter((permission) => !currentPermissions.includes(permission));
this._originToPermissions.set(origin, [...currentPermissions, ...toGrant]);
if (origin === "*") {
await Promise.all(this._bidiPages().flatMap(
(page) => page._page.frames().map(
(frame) => this.doGrantPermissions(new URL(frame._url).origin, permissions)
)
));
} else {
await Promise.all(toGrant.map((permission) => this._setPermission(origin, permission, Permissions.PermissionState.Granted)));
}
}
async doGrantGlobalPermissionsForURL(url2) {
const permissions = this._originToPermissions.get("*");
if (!permissions)
return;
await this.doGrantPermissions(new URL(url2).origin, permissions);
}
async doClearPermissions() {
const currentPermissions = [...this._originToPermissions.entries()];
this._originToPermissions = /* @__PURE__ */ new Map();
await Promise.all(currentPermissions.flatMap(([origin, permissions]) => {
if (origin !== "*")
return permissions.map((p) => this._setPermission(origin, p, Permissions.PermissionState.Prompt));
}));
}
async _setPermission(origin, permission, state) {
await this._browser._browserSession.send("permissions.setPermission", {
descriptor: {
name: permission
},
state,
origin,
userContext: this._userContextId()
});
}
async setGeolocation(geolocation) {
verifyGeolocation(geolocation);
this._options.geolocation = geolocation;
await this._browser._browserSession.send("emulation.setGeolocationOverride", {
coordinates: geolocation ? {
latitude: geolocation.latitude,
longitude: geolocation.longitude,
accuracy: geolocation.accuracy
} : null,
userContexts: [this._userContextId()]
});
}
async doUpdateExtraHTTPHeaders() {
const allHeaders = this._options.extraHTTPHeaders || [];
await this._browser._browserSession.send("network.setExtraHeaders", {
headers: allHeaders.map(({ name, value: value2 }) => ({ name, value: { type: "string", value: value2 } })),
userContexts: [this._userContextId()]
});
}
async setUserAgent(userAgent) {
this._options.userAgent = userAgent;
await this._browser._browserSession.send("emulation.setUserAgentOverride", {
userAgent: userAgent ?? null,
userContexts: [this._userContextId()]
});
}
async doUpdateOffline() {
await this._browser._browserSession.send("emulation.setNetworkConditions", {
networkConditions: this._options.offline ? { type: "offline" } : null,
userContexts: [this._userContextId()]
});
}
async doSetHTTPCredentials(httpCredentials) {
this._options.httpCredentials = httpCredentials;
for (const page of this.pages())
await page.delegate.updateHttpCredentials();
}
async doAddInitScript(initScript) {
const { script } = await this._browser._browserSession.send("script.addPreloadScript", {
// TODO: remove function call from the source.
functionDeclaration: `() => { return ${initScript.source} }`,
userContexts: [this._userContextId()]
});
this._initScriptIds.set(initScript, script);
}
async doRemoveInitScripts(initScripts) {
const ids = [];
for (const script of initScripts) {
const id = this._initScriptIds.get(script);
if (id)
ids.push(id);
this._initScriptIds.delete(script);
}
await Promise.all(ids.map((script) => this._browser._browserSession.send("script.removePreloadScript", { script })));
}
async doUpdateRequestInterception() {
let interceptPromise = Promise.resolve(void 0);
if (this.requestInterceptors.length > 0 && !this._interceptId) {
interceptPromise = this._browser._browserSession.send("network.addIntercept", {
phases: [Network.InterceptPhase.BeforeRequestSent]
}).then(({ intercept }) => this._interceptId = intercept);
}
if (this.requestInterceptors.length === 0 && this._interceptId) {
const intercept = this._interceptId;
this._interceptId = void 0;
interceptPromise = this._browser._browserSession.send("network.removeIntercept", { intercept });
}
await Promise.all([this._browser.updateCacheBehavior(), interceptPromise]);
}
async doUpdateDefaultViewport() {
if (!this._options.viewport && !this._options.screen)
return;
const screenSize = this._options.screen || this._options.viewport;
const viewportSize = this._options.viewport || this._options.screen;
await Promise.all([
this._browser._browserSession.send("browsingContext.setViewport", {
viewport: {
width: viewportSize.width,
height: viewportSize.height
},
devicePixelRatio: this._options.deviceScaleFactor || 1,
userContexts: [this._userContextId()]
}),
this._browser._browserSession.send("emulation.setScreenOrientationOverride", {
screenOrientation: getScreenOrientation(!!this._options.isMobile, screenSize),
userContexts: [this._userContextId()]
}),
this._browser._browserSession.send("emulation.setScreenSettingsOverride", {
screenArea: {
width: screenSize.width,
height: screenSize.height
},
userContexts: [this._userContextId()]
})
]);
}
async doUpdateDefaultEmulatedMedia() {
}
async doExposePlaywrightBinding() {
const args = [{
type: "channel",
value: {
channel: kPlaywrightBindingChannel,
ownership: Script.ResultOwnership.Root
}
}];
const functionDeclaration = `function addMainBinding(callback) { globalThis['${PageBinding.kBindingName}'] = callback; }`;
const promises = [];
promises.push(this._browser._browserSession.send("script.addPreloadScript", {
functionDeclaration,
arguments: args,
userContexts: [this._userContextId()]
}));
promises.push(...this._bidiPages().map((page) => {
const realms = [...page._contextIdToContext].filter(([realm, context2]) => context2.world === "main").map(([realm, context2]) => realm);
return Promise.all(realms.map((realm) => {
return page._session.send("script.callFunction", {
functionDeclaration,
arguments: args,
target: { realm },
awaitPromise: false,
userActivation: false
});
}));
}));
await Promise.all(promises);
}
onClosePersistent() {
}
async clearCache() {
}
async doClose(reason) {
if (!this._browserContextId) {
return "close-browser";
}
await this._browser._browserSession.send("browser.removeUserContext", {
userContext: this._browserContextId
});
await Promise.all(this._bidiPages().map((bidiPage) => bidiPage._page.closedPromise));
this._browser._contexts.delete(this._browserContextId);
}
async cancelDownload(uuid) {
}
_userContextId() {
if (this._browserContextId)
return this._browserContextId;
return "default";
}
};
((Network3) => {
let SameSite;
((SameSite2) => {
SameSite2["Strict"] = "strict";
SameSite2["Lax"] = "lax";
SameSite2["None"] = "none";
})(SameSite = Network3.SameSite || (Network3.SameSite = {}));
})(Network2 || (Network2 = {}));
}
});
// packages/playwright-core/src/server/chromium/crDevTools.ts
var import_fs26, kBindingName, CRDevTools;
var init_crDevTools = __esm({
"packages/playwright-core/src/server/chromium/crDevTools.ts"() {
"use strict";
import_fs26 = __toESM(require("fs"));
kBindingName = "__pw_devtools__";
CRDevTools = class {
constructor(preferencesPath) {
this._preferencesPath = preferencesPath;
this._savePromise = Promise.resolve();
}
install(session2) {
session2.on("Runtime.bindingCalled", async (event) => {
if (event.name !== kBindingName)
return;
const parsed = JSON.parse(event.payload);
let result2 = void 0;
if (parsed.method === "getPreferences") {
if (this._prefs === void 0) {
try {
const json = await import_fs26.default.promises.readFile(this._preferencesPath, "utf8");
this._prefs = JSON.parse(json);
} catch (e) {
this._prefs = {};
}
}
result2 = this._prefs;
} else if (parsed.method === "setPreference") {
this._prefs[parsed.params[0]] = parsed.params[1];
this._save();
} else if (parsed.method === "removePreference") {
delete this._prefs[parsed.params[0]];
this._save();
} else if (parsed.method === "clearPreferences") {
this._prefs = {};
this._save();
}
session2.send("Runtime.evaluate", {
expression: `window.DevToolsAPI.embedderMessageAck(${parsed.id}, ${JSON.stringify(result2)})`,
contextId: event.executionContextId
}).catch((e) => null);
});
Promise.all([
session2.send("Runtime.enable"),
session2.send("Runtime.addBinding", { name: kBindingName }),
session2.send("Page.enable"),
session2.send("Page.addScriptToEvaluateOnNewDocument", { source: `
(() => {
const init = () => {
// Lazy init happens when InspectorFrontendHost is initialized.
// At this point DevToolsHost is ready to be used.
const host = window.DevToolsHost;
const old = host.sendMessageToEmbedder.bind(host);
host.sendMessageToEmbedder = message => {
if (['getPreferences', 'setPreference', 'removePreference', 'clearPreferences'].includes(JSON.parse(message).method))
window.${kBindingName}(message);
else
old(message);
};
};
let value;
Object.defineProperty(window, 'InspectorFrontendHost', {
configurable: true,
enumerable: true,
get() { return value; },
set(v) { value = v; init(); },
});
})()
` }),
session2.send("Runtime.runIfWaitingForDebugger")
]).catch((e) => null);
}
_save() {
this._savePromise = this._savePromise.then(async () => {
await import_fs26.default.promises.writeFile(this._preferencesPath, JSON.stringify(this._prefs)).catch((e) => null);
});
}
};
}
});
// packages/playwright-core/src/server/chromium/chromium.ts
function profileInUseError(logs) {
const markers = [
"Failed to create a ProcessSingleton for your profile directory.",
"Opening in existing browser session."
];
for (const log2 of logs) {
const marker = markers.find((m) => log2.includes(m));
if (marker) {
return new Error(
`${marker} This usually means that the profile is already in use by another instance of Chromium.`
);
}
}
}
async function waitForReadyState(options2, browserLogsCollector) {
if (!options2.args?.some((a) => a.startsWith("--remote-debugging-port")))
return {};
const result2 = new ManualPromise();
browserLogsCollector.onMessage((message) => {
const error = profileInUseError([message]);
if (error)
result2.reject(error);
const match = message.match(/DevTools listening on (.*)/);
if (match)
result2.resolve({ wsEndpoint: match[1] });
});
return result2;
}
async function urlToWSEndpoint(progress2, endpointURL, headers) {
if (endpointURL.startsWith("ws"))
return endpointURL;
progress2.log(`<ws preparing> retrieving websocket url from ${endpointURL}`);
const url2 = new URL(endpointURL);
if (!url2.pathname.endsWith("/"))
url2.pathname += "/";
url2.pathname += "json/version/";
const httpURL = url2.toString();
const json = await fetchData(
progress2,
{
url: httpURL,
headers
},
async (_, resp) => new Error(`Unexpected status ${resp.statusCode} when connecting to ${httpURL}.
This does not look like a DevTools server, try connecting via ws://.`)
);
return JSON.parse(json).webSocketDebuggerUrl;
}
async function resolveChannelEndpoint(progress2, channel) {
const userDataDir = defaultUserDataDirForChannel(channel);
if (!userDataDir)
throw new Error(`Connecting to ${channel} by channel name is not supported on ${process.platform}.`);
const devToolsActivePortPath = import_path25.default.join(userDataDir, "DevToolsActivePort");
progress2.log(`<ws preparing> reading ${devToolsActivePortPath}`);
const contents = await progress2.race(import_fs27.default.promises.readFile(devToolsActivePortPath, "utf-8").catch(() => void 0));
if (!contents) {
throw new Error(
`Could not connect to ${channel}: DevToolsActivePort file not found at ${devToolsActivePortPath}.
` + remoteDebuggingHint(channel)
);
}
const port = parseInt(contents.trim(), 10);
if (isNaN(port))
throw new Error(`Could not connect to ${channel}: invalid DevToolsActivePort file at ${devToolsActivePortPath}.`);
const endpoint = `ws://localhost:${port}/devtools/browser`;
progress2.log(`<ws preparing> resolved channel "${channel}" to ${endpoint}`);
return endpoint;
}
async function seleniumErrorHandler(params2, response2) {
const body = await streamToString(response2);
let message = body;
try {
const json = JSON.parse(body);
message = json.value.localizedMessage || json.value.message;
} catch (e) {
}
return new Error(`Error connecting to Selenium at ${params2.url}: ${message}`);
}
function addProtocol(url2) {
if (!["ws://", "wss://", "http://", "https://"].some((protocol) => url2.startsWith(protocol)))
return "http://" + url2;
return url2;
}
function streamToString(stream3) {
return new Promise((resolve, reject) => {
const chunks = [];
stream3.on("data", (chunk) => chunks.push(Buffer.from(chunk)));
stream3.on("error", reject);
stream3.on("end", () => resolve(Buffer.concat(chunks).toString("utf8")));
});
}
function parseSeleniumRemoteParams(env, progress2) {
try {
const parsed = JSON.parse(env.value);
progress2.log(`<selenium> using additional ${env.name} "${env.value}"`);
return parsed;
} catch (e) {
progress2.log(`<selenium> ignoring additional ${env.name} "${env.value}": ${e}`);
}
}
function remoteDebuggingHint(channel) {
return `Make sure ${channel} is running with remote debugging enabled. Navigate to
chrome://inspect/#remote-debugging
and check "Allow remote debugging for this browser instance".
`;
}
var import_fs27, import_os11, import_path25, ARTIFACTS_FOLDER2, Chromium;
var init_chromium = __esm({
"packages/playwright-core/src/server/chromium/chromium.ts"() {
"use strict";
import_fs27 = __toESM(require("fs"));
import_os11 = __toESM(require("os"));
import_path25 = __toESM(require("path"));
init_manualPromise();
init_ascii();
init_chromiumChannels();
init_debugLogger();
init_fileUtils();
init_processLauncher();
init_debug();
init_headers();
init_utils2();
init_userAgent();
init_chromiumSwitches();
init_crBrowser();
init_crConnection();
init_browserContext();
init_browserType();
init_helper();
init_registry();
init_transport();
init_crDevTools();
init_browser();
init_page();
init_crExecutionContext();
init_console();
init_crProtocolHelper();
ARTIFACTS_FOLDER2 = import_path25.default.join(import_os11.default.tmpdir(), "playwright-artifacts-");
Chromium = class extends BrowserType {
constructor(parent, bidiChromium) {
super(parent, "chromium");
this._bidiChromium = bidiChromium;
if (debugMode() === "inspector")
this._devtools = this._createDevTools();
}
launch(progress2, options2, protocolLogger) {
if (options2.channel?.startsWith("bidi-"))
return this._bidiChromium.launch(progress2, options2, protocolLogger);
return super.launch(progress2, options2, protocolLogger);
}
async launchPersistentContext(progress2, userDataDir, options2) {
if (options2.channel?.startsWith("bidi-"))
return this._bidiChromium.launchPersistentContext(progress2, userDataDir, options2);
return super.launchPersistentContext(progress2, userDataDir, options2);
}
async connectOverCDP(progress2, endpointURL, options2) {
return await this._connectOverCDPInternal(progress2, endpointURL, options2);
}
async _connectOverCDPInternal(progress2, endpointURL, options2, onClose) {
let headersMap;
if (options2.headers)
headersMap = headersArrayToObject(options2.headers, false);
if (!headersMap)
headersMap = { "User-Agent": getUserAgent() };
else if (headersMap && !Object.keys(headersMap).some((key) => key.toLowerCase() === "user-agent"))
headersMap["User-Agent"] = getUserAgent();
const channel = isChromiumChannelName(endpointURL) ? endpointURL : void 0;
if (channel)
endpointURL = await resolveChannelEndpoint(progress2, endpointURL);
let wsEndpoint;
let chromeTransport;
try {
wsEndpoint = await urlToWSEndpoint(progress2, endpointURL, headersMap);
chromeTransport = await WebSocketTransport.connect(progress2, wsEndpoint, { headers: headersMap, followRedirects: true, debugLogHeader: "x-playwright-debug-log" });
} catch (e) {
if (channel)
throw new Error(`Could not connect to ${channel}.
${remoteDebuggingHint(channel)}`);
throw e;
}
const closeAndWait = async () => await chromeTransport.closeAndWait();
return this._connectOverCDPImpl(progress2, chromeTransport, closeAndWait, options2, onClose);
}
async _connectOverCDPImpl(progress2, transport, closeAndWait, options2, onClose) {
const artifactsDir = await progress2.race(import_fs27.default.promises.mkdtemp(ARTIFACTS_FOLDER2));
const doCleanup = async () => {
await removeFolders([artifactsDir]);
const cb = onClose;
onClose = void 0;
await cb?.();
};
const doClose = async () => {
await closeAndWait();
await doCleanup();
};
try {
const browserProcess = { close: doClose, kill: doClose };
const persistent = {
noDefaultViewport: true,
...options2.noDefaults ? { acceptDownloads: "internal-browser-default" } : {}
};
const browserOptions = {
slowMo: options2.slowMo,
name: "chromium",
browserType: "chromium",
persistent,
browserProcess,
protocolLogger: helper.debugProtocolLogger(),
browserLogsCollector: new RecentLogsCollector(),
artifactsDir,
downloadsPath: options2.downloadsPath || artifactsDir,
tracesDir: options2.tracesDir || artifactsDir,
originalLaunchOptions: {},
noDefaults: options2.noDefaults
};
validateBrowserContextOptions(persistent, browserOptions);
const browser = await progress2.race(CRBrowser.connect(this.attribution.playwright, transport, browserOptions));
if (!options2.isLocal)
browser._isCollocatedWithServer = false;
browser.on(Browser.Events.Disconnected, doCleanup);
return browser;
} catch (error) {
await progress2.race(doClose().catch(() => {
}));
throw error;
}
}
async connectToWorker(progress2, endpoint) {
const wsEndpoint = await urlToWSEndpoint(progress2, endpoint, {});
const transport = await WebSocketTransport.connect(progress2, wsEndpoint);
try {
const connection = new CRConnection(this, transport, helper.debugProtocolLogger(), new RecentLogsCollector());
const session2 = connection.rootSession;
const worker = new Worker(this, "", () => transport.closeAndWait());
session2.on("Runtime.executionContextCreated", (event) => {
const isDefault = event.context.auxData?.isDefault;
if (isDefault === false) {
return;
}
worker.workerScriptLoaded();
worker.createExecutionContext(new CRExecutionContext(session2, event.context));
});
session2.on("Runtime.executionContextDestroyed", () => worker.destroyExecutionContext("Execution context was destroyed"));
session2.on("Runtime.consoleAPICalled", (event) => {
if (!worker.existingExecutionContext)
return;
const args = event.args.map((o) => createHandle(worker.existingExecutionContext, o));
const message = new ConsoleMessage(null, worker, event.type, void 0, args, stackTraceToLocation(event.stackTrace), event.timestamp);
worker.emit(Worker.Events.Console, message);
});
connection.on(ConnectionEvents.Disconnected, () => worker.didClose());
session2._sendMayFail("Runtime.enable");
session2._sendMayFail("Runtime.runIfWaitingForDebugger");
return worker;
} catch (error) {
await progress2.race(transport.closeAndWait().catch(() => {
}));
throw error;
}
}
_createDevTools() {
const directory = registry.findExecutable("chromium").directory;
return directory ? new CRDevTools(import_path25.default.join(directory, "devtools-preferences.json")) : void 0;
}
async connectToTransport(transport, options2, browserLogsCollector) {
try {
return await CRBrowser.connect(this.attribution.playwright, transport, options2, this._devtools);
} catch (e) {
const error = profileInUseError(browserLogsCollector.recentLogs());
if (error)
throw error;
throw e;
}
}
doRewriteStartupLog(logs) {
if (logs.includes("Missing X server"))
logs = "\n" + wrapInASCIIBox(kNoXServerRunningError, 1);
if (!logs.includes("crbug.com/357670") && !logs.includes("No usable sandbox!") && !logs.includes("crbug.com/638180"))
return logs;
return [
`Chromium sandboxing failed!`,
`================================`,
`To avoid the sandboxing issue, do either of the following:`,
` - (preferred): Configure your environment to support sandboxing`,
` - (alternative): Launch Chromium without sandbox using 'chromiumSandbox: false' option`,
`================================`,
``
].join("\n");
}
amendEnvironment(env) {
return env;
}
attemptToGracefullyCloseBrowser(transport) {
const message = { method: "Browser.close", id: kBrowserCloseMessageId, params: {} };
transport.send(message);
}
async launchWithSeleniumHub(progress2, hubUrl, options2) {
if (!hubUrl.endsWith("/"))
hubUrl = hubUrl + "/";
const args = this._innerDefaultArgs(options2);
args.push("--remote-debugging-port=0");
const isEdge = options2.channel && options2.channel.startsWith("msedge");
let desiredCapabilities = {
"browserName": isEdge ? "MicrosoftEdge" : "chrome",
[isEdge ? "ms:edgeOptions" : "goog:chromeOptions"]: { args }
};
if (process.env.SELENIUM_REMOTE_CAPABILITIES) {
const remoteCapabilities = parseSeleniumRemoteParams({ name: "capabilities", value: process.env.SELENIUM_REMOTE_CAPABILITIES }, progress2);
if (remoteCapabilities)
desiredCapabilities = { ...desiredCapabilities, ...remoteCapabilities };
}
let headers = {};
if (process.env.SELENIUM_REMOTE_HEADERS) {
const remoteHeaders = parseSeleniumRemoteParams({ name: "headers", value: process.env.SELENIUM_REMOTE_HEADERS }, progress2);
if (remoteHeaders)
headers = remoteHeaders;
}
progress2.log(`<selenium> connecting to ${hubUrl}`);
const response2 = await fetchData(progress2, {
url: hubUrl + "session",
method: "POST",
headers: {
"Content-Type": "application/json; charset=utf-8",
...headers
},
data: JSON.stringify({
capabilities: { alwaysMatch: desiredCapabilities }
})
}, seleniumErrorHandler);
const value2 = JSON.parse(response2).value;
const sessionId = value2.sessionId;
progress2.log(`<selenium> connected to sessionId=${sessionId}`);
const disconnectFromSelenium = async () => {
progress2.log(`<selenium> disconnecting from sessionId=${sessionId}`);
await fetchData(void 0, {
url: hubUrl + "session/" + sessionId,
method: "DELETE",
headers
}).catch((error) => progress2.log(`<error disconnecting from selenium>: ${error}`));
progress2.log(`<selenium> disconnected from sessionId=${sessionId}`);
gracefullyCloseSet.delete(disconnectFromSelenium);
};
gracefullyCloseSet.add(disconnectFromSelenium);
try {
const capabilities = value2.capabilities;
let endpointURL;
if (capabilities["se:cdp"]) {
progress2.log(`<selenium> using selenium v4`);
const endpointURLString = addProtocol(capabilities["se:cdp"]);
endpointURL = new URL(endpointURLString);
if (endpointURL.hostname === "localhost" || endpointURL.hostname === "127.0.0.1")
endpointURL.hostname = new URL(hubUrl).hostname;
progress2.log(`<selenium> retrieved endpoint ${endpointURL.toString()} for sessionId=${sessionId}`);
} else {
progress2.log(`<selenium> using selenium v3`);
const maybeChromeOptions = capabilities["goog:chromeOptions"];
const chromeOptions = maybeChromeOptions && typeof maybeChromeOptions === "object" ? maybeChromeOptions : void 0;
const debuggerAddress = chromeOptions && typeof chromeOptions.debuggerAddress === "string" ? chromeOptions.debuggerAddress : void 0;
const chromeOptionsURL = typeof maybeChromeOptions === "string" ? maybeChromeOptions : void 0;
const endpointURLString = addProtocol(debuggerAddress || chromeOptionsURL).replace("localhost", "127.0.0.1");
progress2.log(`<selenium> retrieved endpoint ${endpointURLString} for sessionId=${sessionId}`);
endpointURL = new URL(endpointURLString);
if (endpointURL.hostname === "localhost" || endpointURL.hostname === "127.0.0.1") {
const sessionInfoUrl = new URL(hubUrl).origin + "/grid/api/testsession?session=" + sessionId;
try {
const sessionResponse = await fetchData(progress2, {
url: sessionInfoUrl,
method: "GET",
headers
}, seleniumErrorHandler);
const proxyId = JSON.parse(sessionResponse).proxyId;
endpointURL.hostname = new URL(proxyId).hostname;
progress2.log(`<selenium> resolved endpoint ip ${endpointURL.toString()} for sessionId=${sessionId}`);
} catch (e) {
progress2.log(`<selenium> unable to resolve endpoint ip for sessionId=${sessionId}, running in standalone?`);
}
}
}
return await this._connectOverCDPInternal(progress2, endpointURL.toString(), {
...options2,
headers: headersObjectToArray(headers)
}, disconnectFromSelenium);
} catch (e) {
await progress2.race(disconnectFromSelenium());
throw e;
}
}
async defaultArgs(options2, isPersistent, userDataDir) {
const chromeArguments = this._innerDefaultArgs(options2);
chromeArguments.push(`--user-data-dir=${userDataDir}`);
chromeArguments.push("--remote-debugging-pipe");
if (isPersistent)
chromeArguments.push("about:blank");
else
chromeArguments.push("--no-startup-window");
return chromeArguments;
}
_innerDefaultArgs(options2) {
const { args = [] } = options2;
const userDataDirArg = args.find((arg) => arg.startsWith("--user-data-dir"));
if (userDataDirArg)
throw this._createUserDataDirArgMisuseError("--user-data-dir");
if (args.find((arg) => arg.startsWith("--remote-debugging-pipe")))
throw new Error("Playwright manages remote debugging connection itself.");
if (args.find((arg) => !arg.startsWith("-")))
throw new Error("Arguments can not specify page to be opened");
const chromeArguments = [...chromiumSwitches()];
chromeArguments.push("--enable-unsafe-swiftshader");
if (options2.headless) {
chromeArguments.push("--headless");
chromeArguments.push(
"--hide-scrollbars",
"--mute-audio",
"--blink-settings=primaryHoverType=2,availableHoverTypes=2,primaryPointerType=4,availablePointerTypes=4"
);
}
if (options2.chromiumSandbox !== true)
chromeArguments.push("--no-sandbox");
const proxy = options2.proxyOverride || options2.proxy;
if (proxy) {
const proxyURL = new URL(proxy.server);
const isSocks = proxyURL.protocol === "socks5:";
if (isSocks && !options2.socksProxyPort) {
chromeArguments.push(`--host-resolver-rules="MAP * ~NOTFOUND , EXCLUDE ${proxyURL.hostname}"`);
}
chromeArguments.push(`--proxy-server=${proxy.server}`);
const proxyBypassRules = [];
if (proxy.bypass)
proxyBypassRules.push(...proxy.bypass.split(",").map((t) => t.trim()).map((t) => t.startsWith(".") ? "*" + t : t));
if (options2.socksProxyPort || shouldProxyLoopback(proxy.bypass))
proxyBypassRules.push("<-loopback>");
if (proxyBypassRules.length > 0)
chromeArguments.push(`--proxy-bypass-list=${proxyBypassRules.join(";")}`);
}
chromeArguments.push(...args);
return chromeArguments;
}
async waitForReadyState(options2, browserLogsCollector) {
return waitForReadyState(options2, browserLogsCollector);
}
getExecutableName(options2) {
if (options2.channel && registry.isChromiumAlias(options2.channel))
return "chromium";
if (options2.channel === "chromium-tip-of-tree")
return options2.headless ? "chromium-tip-of-tree-headless-shell" : "chromium-tip-of-tree";
if (options2.channel)
return options2.channel;
return options2.headless ? "chromium-headless-shell" : "chromium";
}
};
}
});
// packages/playwright-core/src/server/bidi/bidiOverCdp.ts
var bidiOverCdp_exports = {};
__export(bidiOverCdp_exports, {
connectBidiOverCdp: () => connectBidiOverCdp
});
async function connectBidiOverCdp(cdp) {
let server = void 0;
const bidiTransport = new BidiTransportImpl();
const bidiConnection = new BidiConnection2(bidiTransport, () => server?.close());
const cdpTransportImpl = new CdpTransportImpl(cdp);
const cdpConnection = new bidiCdpConnection.MapperCdpConnection(cdpTransportImpl, bidiServerLogger);
cdp.onclose = () => bidiConnection.onclose?.();
server = await bidiMapper.BidiServer.createAndStart(
bidiTransport,
cdpConnection,
await cdpConnection.createBrowserSession(),
/* selfTargetId= */
"",
void 0,
bidiServerLogger
);
return bidiConnection;
}
var bidiMapper, bidiCdpConnection, bidiServerLogger, BidiTransportImpl, BidiConnection2, CdpTransportImpl;
var init_bidiOverCdp = __esm({
"packages/playwright-core/src/server/bidi/bidiOverCdp.ts"() {
"use strict";
bidiMapper = __toESM(require("chromium-bidi/lib/cjs/bidiMapper/BidiMapper"));
bidiCdpConnection = __toESM(require("chromium-bidi/lib/cjs/cdp/CdpConnection"));
init_debugLogger();
bidiServerLogger = (prefix, ...args) => {
debugLogger.log(prefix, args);
};
BidiTransportImpl = class {
setOnMessage(handler) {
this._handler = handler;
}
sendMessage(message) {
return this._bidiConnection.onmessage?.(message);
}
close() {
this._bidiConnection.onclose?.();
}
};
BidiConnection2 = class {
constructor(bidiTransport, closeCallback) {
this._bidiTransport = bidiTransport;
this._bidiTransport._bidiConnection = this;
this._closeCallback = closeCallback;
}
send(s) {
this._bidiTransport._handler?.(s);
}
close() {
this._closeCallback();
}
};
CdpTransportImpl = class {
constructor(connection) {
this._connection = connection;
this._connection.onmessage = (message) => {
this._handler?.(JSON.stringify(message));
};
}
setOnMessage(handler) {
this._handler = handler;
}
sendMessage(message) {
return this._connection.send(JSON.parse(message));
}
close() {
this._connection.close();
}
};
}
});
// packages/playwright-core/src/server/bidi/bidiChromium.ts
var import_os12, BidiChromium, kBidiOverCdpWrapper;
var init_bidiChromium = __esm({
"packages/playwright-core/src/server/bidi/bidiChromium.ts"() {
"use strict";
import_os12 = __toESM(require("os"));
init_ascii();
init_browserType();
init_bidiBrowser();
init_bidiConnection();
init_chromiumSwitches();
init_chromium();
init_crBrowser();
BidiChromium = class extends BrowserType {
constructor(parent) {
super(parent, "chromium");
}
async connectToTransport(transport, options2, browserLogsCollector) {
const bidiOverCdp = (init_bidiOverCdp(), __toCommonJS(bidiOverCdp_exports));
const bidiTransport = await bidiOverCdp.connectBidiOverCdp(transport);
transport[kBidiOverCdpWrapper] = bidiTransport;
try {
return BidiBrowser.connect(this.attribution.playwright, bidiTransport, options2);
} catch (e) {
const error = profileInUseError(browserLogsCollector.recentLogs());
if (error)
throw error;
throw e;
}
}
doRewriteStartupLog(logs) {
if (logs.includes("Missing X server"))
logs = "\n" + wrapInASCIIBox(kNoXServerRunningError, 1);
if (!logs.includes("crbug.com/357670") && !logs.includes("No usable sandbox!") && !logs.includes("crbug.com/638180"))
return logs;
return [
`Chromium sandboxing failed!`,
`================================`,
`To avoid the sandboxing issue, do either of the following:`,
` - (preferred): Configure your environment to support sandboxing`,
` - (alternative): Launch Chromium without sandbox using 'chromiumSandbox: false' option`,
`================================`,
``
].join("\n");
}
amendEnvironment(env) {
return env;
}
attemptToGracefullyCloseBrowser(transport) {
const bidiTransport = transport[kBidiOverCdpWrapper];
if (bidiTransport)
transport = bidiTransport;
transport.send({ method: "browser.close", params: {}, id: kBrowserCloseMessageId2 });
}
supportsPipeTransport() {
return false;
}
async defaultArgs(options2, isPersistent, userDataDir) {
const chromeArguments = this._innerDefaultArgs(options2);
chromeArguments.push(`--user-data-dir=${userDataDir}`);
chromeArguments.push("--remote-debugging-port=0");
if (isPersistent)
chromeArguments.push("about:blank");
else
chromeArguments.push("--no-startup-window");
return chromeArguments;
}
async waitForReadyState(options2, browserLogsCollector) {
return waitForReadyState({ ...options2, args: ["--remote-debugging-port=0"] }, browserLogsCollector);
}
_innerDefaultArgs(options2) {
const { args = [] } = options2;
const userDataDirArg = args.find((arg) => arg.startsWith("--user-data-dir"));
if (userDataDirArg)
throw this._createUserDataDirArgMisuseError("--user-data-dir");
if (args.find((arg) => arg.startsWith("--remote-debugging-pipe")))
throw new Error("Playwright manages remote debugging connection itself.");
if (args.find((arg) => !arg.startsWith("-")))
throw new Error("Arguments can not specify page to be opened");
const chromeArguments = [...chromiumSwitches()];
if (import_os12.default.platform() === "darwin") {
chromeArguments.push("--enable-unsafe-swiftshader");
}
if (options2.headless) {
chromeArguments.push("--headless");
chromeArguments.push(
"--hide-scrollbars",
"--mute-audio",
"--blink-settings=primaryHoverType=2,availableHoverTypes=2,primaryPointerType=4,availablePointerTypes=4"
);
}
if (options2.chromiumSandbox !== true)
chromeArguments.push("--no-sandbox");
const proxy = options2.proxyOverride || options2.proxy;
if (proxy) {
const proxyURL = new URL(proxy.server);
const isSocks = proxyURL.protocol === "socks5:";
if (isSocks && !options2.socksProxyPort) {
chromeArguments.push(`--host-resolver-rules="MAP * ~NOTFOUND , EXCLUDE ${proxyURL.hostname}"`);
}
chromeArguments.push(`--proxy-server=${proxy.server}`);
const proxyBypassRules = [];
if (proxy.bypass)
proxyBypassRules.push(...proxy.bypass.split(",").map((t) => t.trim()).map((t) => t.startsWith(".") ? "*" + t : t));
if (options2.socksProxyPort || shouldProxyLoopback(proxy.bypass))
proxyBypassRules.push("<-loopback>");
if (proxyBypassRules.length > 0)
chromeArguments.push(`--proxy-bypass-list=${proxyBypassRules.join(";")}`);
}
chromeArguments.push(...args);
return chromeArguments;
}
getExecutableName(options2) {
switch (options2.channel) {
case "bidi-chromium":
return "chromium";
case "bidi-chrome":
return "chrome";
case "bidi-chrome-beta":
return "chrome-beta";
case "bidi-chrome-dev":
return "chrome-dev";
case "bidi-chrome-canary":
return "chrome-canary";
}
throw new Error(`Unsupported Bidi Chromium channel: ${options2.channel}`);
}
};
kBidiOverCdpWrapper = Symbol("kBidiConnectionWrapper");
}
});
// packages/playwright-core/src/server/bidi/third_party/firefoxPrefs.ts
async function createProfile(options2) {
if (!import_fs28.default.existsSync(options2.path)) {
await import_fs28.default.promises.mkdir(options2.path, {
recursive: true
});
}
await writePreferences({
preferences: {
...defaultProfilePreferences(options2.preferences),
...options2.preferences
},
path: options2.path
});
}
function defaultProfilePreferences(extraPrefs) {
const server = "dummy.test";
const defaultPrefs = {
// Make sure Shield doesn't hit the network.
"app.normandy.api_url": "",
// Disable Firefox old build background check
"app.update.checkInstallTime": false,
// Disable automatically upgrading Firefox
"app.update.disabledForTesting": true,
// Increase the APZ content response timeout to 1 minute
"apz.content_response_timeout": 6e4,
// Prevent various error message on the console
// jest-puppeteer asserts that no error message is emitted by the console
"browser.contentblocking.features.standard": "-tp,tpPrivate,cookieBehavior0,-cm,-fp",
// Enable the dump function: which sends messages to the system
// console
// https://bugzilla.mozilla.org/show_bug.cgi?id=1543115
"browser.dom.window.dump.enabled": true,
// Make sure newtab weather doesn't hit the network to retrieve weather data.
"browser.newtabpage.activity-stream.discoverystream.region-weather-config": "",
// Make sure newtab wallpapers don't hit the network to retrieve wallpaper data.
"browser.newtabpage.activity-stream.newtabWallpapers.enabled": false,
"browser.newtabpage.activity-stream.newtabWallpapers.v2.enabled": false,
// Make sure Topsites doesn't hit the network to retrieve sponsored tiles.
"browser.newtabpage.activity-stream.showSponsoredTopSites": false,
// Disable topstories
"browser.newtabpage.activity-stream.feeds.system.topstories": false,
// Always display a blank page
"browser.newtabpage.enabled": false,
// Background thumbnails in particular cause grief: and disabling
// thumbnails in general cannot hurt
"browser.pagethumbnails.capturing_disabled": true,
// Disable safebrowsing components.
"browser.safebrowsing.blockedURIs.enabled": false,
"browser.safebrowsing.downloads.enabled": false,
"browser.safebrowsing.malware.enabled": false,
"browser.safebrowsing.phishing.enabled": false,
// Disable updates to search engines.
"browser.search.update": false,
// Do not restore the last open set of tabs if the browser has crashed
"browser.sessionstore.resume_from_crash": false,
// Skip check for default browser on startup
"browser.shell.checkDefaultBrowser": false,
// Disable newtabpage
"browser.startup.homepage": "about:blank",
// Do not redirect user when a milstone upgrade of Firefox is detected
"browser.startup.homepage_override.mstone": "ignore",
// Start with a blank page about:blank
"browser.startup.page": 0,
// Do not allow background tabs to be zombified on Android: otherwise for
// tests that open additional tabs: the test harness tab itself might get
// unloaded
"browser.tabs.disableBackgroundZombification": false,
// Do not warn when closing all other open tabs
"browser.tabs.warnOnCloseOtherTabs": false,
// Do not warn when multiple tabs will be opened
"browser.tabs.warnOnOpen": false,
// Do not automatically offer translations, as tests do not expect this.
"browser.translations.automaticallyPopup": false,
// Disable the UI tour.
"browser.uitour.enabled": false,
// Turn off search suggestions in the location bar so as not to trigger
// network connections.
"browser.urlbar.suggest.searches": false,
// Disable first run splash page on Windows 10
"browser.usedOnWindows10.introURL": "",
// Do not warn on quitting Firefox
"browser.warnOnQuit": false,
// Defensively disable data reporting systems
"datareporting.healthreport.documentServerURI": `http://${server}/dummy/healthreport/`,
"datareporting.healthreport.logging.consoleEnabled": false,
"datareporting.healthreport.service.enabled": false,
"datareporting.healthreport.service.firstRun": false,
"datareporting.healthreport.uploadEnabled": false,
"datareporting.usage.uploadEnabled": false,
"telemetry.fog.test.localhost_port": -1,
// Do not show datareporting policy notifications which can interfere with tests
"datareporting.policy.dataSubmissionEnabled": false,
"datareporting.policy.dataSubmissionPolicyBypassNotification": true,
// DevTools JSONViewer sometimes fails to load dependencies with its require.js.
// This doesn't affect Puppeteer but spams console (Bug 1424372)
"devtools.jsonview.enabled": false,
// Disable popup-blocker
"dom.disable_open_during_load": false,
// Enable the support for File object creation in the content process
// Required for |Page.setFileInputFiles| protocol method.
"dom.file.createInChild": true,
// Disable the ProcessHangMonitor
"dom.ipc.reportProcessHangs": false,
// Disable slow script dialogues
"dom.max_chrome_script_run_time": 0,
"dom.max_script_run_time": 0,
// Disable background timer throttling to allow tests to run in parallel
// without a decrease in performance.
"dom.min_background_timeout_value": 0,
"dom.min_background_timeout_value_without_budget_throttling": 0,
"dom.timeout.enable_budget_timer_throttling": false,
// Disable HTTPS-First upgrades
"dom.security.https_first": false,
// Only load extensions from the application and user profile
// AddonManager.SCOPE_PROFILE + AddonManager.SCOPE_APPLICATION
"extensions.autoDisableScopes": 0,
"extensions.enabledScopes": 5,
// Disable metadata caching for installed add-ons by default
"extensions.getAddons.cache.enabled": false,
// Disable installing any distribution extensions or add-ons.
"extensions.installDistroAddons": false,
// Disabled screenshots extension
"extensions.screenshots.disabled": true,
// Turn off extension updates so they do not bother tests
"extensions.update.enabled": false,
// Turn off extension updates so they do not bother tests
"extensions.update.notifyUser": false,
// Make sure opening about:addons will not hit the network
"extensions.webservice.discoverURL": `http://${server}/dummy/discoveryURL`,
// Allow the application to have focus even it runs in the background
"focusmanager.testmode": true,
// Disable useragent updates
"general.useragent.updates.enabled": false,
// Always use network provider for geolocation tests so we bypass the
// macOS dialog raised by the corelocation provider
"geo.provider.testing": true,
// Do not scan Wifi
"geo.wifi.scan": false,
// No hang monitor
"hangmonitor.timeout": 0,
// Show chrome errors and warnings in the error console
"javascript.options.showInConsole": true,
// Do not throttle rendering (requestAnimationFrame) in background tabs
"layout.testing.top-level-always-active": true,
// Disable download and usage of OpenH264: and Widevine plugins
"media.gmp-manager.updateEnabled": false,
// Disable the GFX sanity window
"media.sanity-test.disabled": true,
// Disable connectivity service pings
"network.connectivity-service.enabled": false,
// Disable experimental feature that is only available in Nightly
"network.cookie.sameSite.laxByDefault": false,
// Do not prompt for temporary redirects
"network.http.prompt-temp-redirect": false,
// Disable speculative connections so they are not reported as leaking
// when they are hanging around
"network.http.speculative-parallel-limit": 0,
// Do not automatically switch between offline and online
"network.manage-offline-status": false,
// Make sure SNTP requests do not hit the network
"network.sntp.pools": server,
// Disable Flash.
"plugin.state.flash": 0,
"privacy.trackingprotection.enabled": false,
// Can be removed once Firefox 89 is no longer supported
// https://bugzilla.mozilla.org/show_bug.cgi?id=1710839
"remote.enabled": true,
// Don't do network connections for mitm priming
"security.certerrors.mitm.priming.enabled": false,
// Local documents have access to all other local documents,
// including directory listings
"security.fileuri.strict_origin_policy": false,
// Do not wait for the notification button security delay
"security.notification_enable_delay": 0,
// Do not automatically fill sign-in forms with known usernames and
// passwords
"signon.autofillForms": false,
// Disable password capture, so that tests that include forms are not
// influenced by the presence of the persistent doorhanger notification
"signon.rememberSignons": false,
// Disable first-run welcome page
"startup.homepage_welcome_url": "about:blank",
// Disable first-run welcome page
"startup.homepage_welcome_url.additional": "",
// Disable browser animations (tabs, fullscreen, sliding alerts)
"toolkit.cosmeticAnimations.enabled": false,
// Prevent starting into safe mode after application crashes
"toolkit.startup.max_resumed_crashes": -1,
// Enable TestUtils
"dom.testing.testutils.enabled": true
};
return Object.assign(defaultPrefs, extraPrefs);
}
async function writePreferences(options2) {
const prefsPath = import_path26.default.join(options2.path, "prefs.js");
const lines = Object.entries(options2.preferences).map(([key, value2]) => {
return `user_pref(${JSON.stringify(key)}, ${JSON.stringify(value2)});`;
});
const result2 = await Promise.allSettled([
import_fs28.default.promises.writeFile(import_path26.default.join(options2.path, "user.js"), lines.join("\n")),
// Create a backup of the preferences file if it already exitsts.
import_fs28.default.promises.access(prefsPath, import_fs28.default.constants.F_OK).then(
async () => {
await import_fs28.default.promises.copyFile(
prefsPath,
import_path26.default.join(options2.path, "prefs.js.playwright")
);
},
// Swallow only if file does not exist
() => {
}
)
]);
for (const command of result2) {
if (command.status === "rejected") {
throw command.reason;
}
}
}
var import_fs28, import_path26;
var init_firefoxPrefs = __esm({
"packages/playwright-core/src/server/bidi/third_party/firefoxPrefs.ts"() {
"use strict";
import_fs28 = __toESM(require("fs"));
import_path26 = __toESM(require("path"));
}
});
// packages/playwright-core/src/server/bidi/bidiFirefox.ts
var import_os13, import_path27, BidiFirefox;
var init_bidiFirefox = __esm({
"packages/playwright-core/src/server/bidi/bidiFirefox.ts"() {
"use strict";
import_os13 = __toESM(require("os"));
import_path27 = __toESM(require("path"));
init_manualPromise();
init_ascii();
init_browserType();
init_bidiBrowser();
init_bidiConnection();
init_firefoxPrefs();
BidiFirefox = class extends BrowserType {
constructor(parent) {
super(parent, "firefox");
}
executablePath() {
return "";
}
async connectToTransport(transport, options2) {
return BidiBrowser.connect(this.attribution.playwright, transport, options2);
}
doRewriteStartupLog(logs) {
if (logs.includes(`as root in a regular user's session is not supported.`))
logs = "\n" + wrapInASCIIBox(`Firefox is unable to launch if the $HOME folder isn't owned by the current user.
Workaround: Set the HOME=/root environment variable${process.env.GITHUB_ACTION ? " in your GitHub Actions workflow file" : ""} when running Playwright.`, 1);
if (logs.includes("no DISPLAY environment variable specified"))
logs = "\n" + wrapInASCIIBox(kNoXServerRunningError, 1);
return logs;
}
amendEnvironment(env) {
if (!import_path27.default.isAbsolute(import_os13.default.homedir()))
throw new Error(`Cannot launch Firefox with relative home directory. Did you set ${import_os13.default.platform() === "win32" ? "USERPROFILE" : "HOME"} to a relative path?`);
env = {
...env,
"MOZ_CRASHREPORTER": "1",
"MOZ_CRASHREPORTER_NO_REPORT": "1",
"MOZ_CRASHREPORTER_SHUTDOWN": "1"
};
if (import_os13.default.platform() === "linux") {
return { ...env, SNAP_NAME: void 0, SNAP_INSTANCE_NAME: void 0 };
}
return env;
}
attemptToGracefullyCloseBrowser(transport) {
this._attemptToGracefullyCloseBrowser(transport).catch(() => {
});
}
async _attemptToGracefullyCloseBrowser(transport) {
if (!transport.onmessage) {
transport.send({ method: "session.new", params: { capabilities: {} }, id: kShutdownSessionNewMessageId });
await new Promise((resolve) => {
transport.onmessage = (message) => {
if (message.id === kShutdownSessionNewMessageId)
resolve(true);
};
});
}
transport.send({ method: "browser.close", params: {}, id: kBrowserCloseMessageId2 });
}
supportsPipeTransport() {
return false;
}
async prepareUserDataDir(options2, userDataDir) {
await createProfile({
path: userDataDir,
preferences: options2.firefoxUserPrefs || {}
});
}
async defaultArgs(options2, isPersistent, userDataDir) {
const { args = [], headless } = options2;
const userDataDirArg = args.find((arg) => arg.startsWith("-profile") || arg.startsWith("--profile"));
if (userDataDirArg)
throw this._createUserDataDirArgMisuseError("--profile");
if (args.find((arg) => !arg.startsWith("-")))
throw new Error("Arguments can not specify page to be opened");
const firefoxArguments = ["--remote-debugging-port=0"];
if (headless)
firefoxArguments.push("--headless");
else
firefoxArguments.push("--foreground");
firefoxArguments.push(`--profile`, userDataDir);
firefoxArguments.push(...args);
return firefoxArguments;
}
async waitForReadyState(options2, browserLogsCollector) {
const result2 = new ManualPromise();
browserLogsCollector.onMessage((message) => {
const match = message.match(/WebDriver BiDi listening on (ws:\/\/.*)$/);
if (match)
result2.resolve({ wsEndpoint: match[1] + "/session" });
});
return result2;
}
};
}
});
// packages/playwright-core/src/server/debugController.ts
function wireListeners(recorder, debugController) {
if (recorder[wiredSymbol])
return;
recorder[wiredSymbol] = true;
const actions = [];
const languageGenerator = new JavaScriptLanguageGenerator(
/* isPlaywrightTest */
true
);
const actionsChanged = () => {
const aa = collapseActions(actions);
const { header, footer, text: text2, actionTexts } = generateCode(aa, languageGenerator, {
browserName: "chromium",
launchOptions: {},
contextOptions: {},
generateAutoExpect: debugController._generateAutoExpect
});
debugController.emit(DebugController.Events.SourceChanged, { text: text2, header, footer, actions: actionTexts });
};
recorder.on(RecorderEvent.ElementPicked, (elementInfo) => {
const locator2 = asLocator(debugController._sdkLanguage, elementInfo.selector);
debugController.emit(DebugController.Events.InspectRequested, { selector: elementInfo.selector, locator: locator2, ariaSnapshot: elementInfo.ariaSnapshot });
});
recorder.on(RecorderEvent.PausedStateChanged, (paused) => {
debugController.emit(DebugController.Events.Paused, { paused });
});
recorder.on(RecorderEvent.ModeChanged, (mode) => {
debugController.emit(DebugController.Events.SetModeRequested, { mode });
});
recorder.on(RecorderEvent.ActionAdded, (action) => {
actions.push(action);
actionsChanged();
});
recorder.on(RecorderEvent.SignalAdded, (signal) => {
const lastAction = actions.findLast((a) => a.frame.pageGuid === signal.frame.pageGuid);
if (lastAction)
lastAction.action.signals.push(signal.signal);
actionsChanged();
});
}
var yaml, DebugController, wiredSymbol;
var init_debugController = __esm({
"packages/playwright-core/src/server/debugController.ts"() {
"use strict";
init_ariaSnapshot();
init_locatorParser();
init_processLauncher();
init_locatorGenerators();
init_instrumentation();
init_recorder();
init_language();
init_recorderUtils();
init_javascript2();
yaml = require("./utilsBundle").yaml;
DebugController = class _DebugController extends SdkObject {
constructor(playwright2) {
super({ attribution: { isInternalPlaywright: true }, instrumentation: createInstrumentation() }, void 0, "DebugController");
this._sdkLanguage = "javascript";
this._generateAutoExpect = false;
this._playwright = playwright2;
}
static {
this.Events = {
StateChanged: "stateChanged",
InspectRequested: "inspectRequested",
SourceChanged: "sourceChanged",
Paused: "paused",
SetModeRequested: "setModeRequested"
};
}
initialize(progress2, codegenId2, sdkLanguage) {
this._sdkLanguage = sdkLanguage;
}
dispose() {
this._setReportStateChanged(false);
}
setReportStateChanged(progress2, enabled) {
this._setReportStateChanged(enabled);
}
_setReportStateChanged(enabled) {
if (enabled && !this._trackHierarchyListener) {
this._trackHierarchyListener = {
onPageOpen: () => this._emitSnapshot(false),
onPageClose: () => this._emitSnapshot(false)
};
this._playwright.instrumentation.addListener(this._trackHierarchyListener, null);
this._emitSnapshot(true);
} else if (!enabled && this._trackHierarchyListener) {
this._playwright.instrumentation.removeListener(this._trackHierarchyListener);
this._trackHierarchyListener = void 0;
}
}
async setRecorderMode(progress2, params2) {
await this._closeBrowsersWithoutPages(progress2);
this._generateAutoExpect = !!params2.generateAutoExpect;
if (params2.mode === "none") {
const promises = [];
for (const recorder of await progress2.race(this._allRecorders())) {
promises.push(recorder.hideHighlightedSelector());
promises.push(recorder.setMode("none"));
}
await progress2.race(Promise.all(promises));
return;
}
if (!this._playwright.allBrowsers().length)
await this._playwright.chromium.launch(progress2, { headless: !!process.env.PW_DEBUG_CONTROLLER_HEADLESS });
const pages = this._playwright.allPages();
if (!pages.length) {
const [browser] = this._playwright.allBrowsers();
const context2 = await browser.newContextForReuse(progress2, {});
await context2.newPage(progress2);
}
if (params2.testIdAttributeName) {
for (const page of this._playwright.allPages())
page.browserContext.selectors().setTestIdAttributeName(params2.testIdAttributeName);
}
for (const recorder of await progress2.race(this._allRecorders())) {
recorder.hideHighlightedSelector();
recorder.setMode(params2.mode);
}
}
async highlight(progress2, params2) {
if (params2.selector)
unsafeLocatorOrSelectorAsSelector(this._sdkLanguage, params2.selector, "data-testid");
const ariaTemplate = params2.ariaTemplate ? parseAriaSnapshotUnsafe(yaml, params2.ariaTemplate) : void 0;
const promises = [];
for (const recorder of await progress2.race(this._allRecorders())) {
if (ariaTemplate)
promises.push(recorder.setHighlightedAriaTemplate(ariaTemplate));
else if (params2.selector)
promises.push(recorder.setHighlightedSelector(params2.selector));
}
await progress2.race(Promise.all(promises));
}
async hideHighlight(progress2) {
const promises = [];
for (const recorder of await progress2.race(this._allRecorders()))
promises.push(recorder.hideHighlightedSelector());
promises.push(...this._playwright.allPages().map((p) => p.hideHighlight().catch(() => {
})));
await progress2.race(Promise.all(promises));
}
async resume(progress2) {
for (const recorder of await progress2.race(this._allRecorders()))
recorder.resume();
}
kill(progress2) {
gracefullyProcessExitDoNotHang(0);
}
_emitSnapshot(initial) {
const pageCount = this._playwright.allPages().length;
if (initial && !pageCount)
return;
this.emit(_DebugController.Events.StateChanged, { pageCount });
}
async _allRecorders() {
const contexts = /* @__PURE__ */ new Set();
for (const page of this._playwright.allPages())
contexts.add(page.browserContext);
const recorders = await Promise.all([...contexts].map((c) => Recorder.forContext(c, { omitCallTracking: true })));
const nonNullRecorders = recorders.filter(Boolean);
for (const recorder of recorders)
wireListeners(recorder, this);
return nonNullRecorders;
}
async _closeBrowsersWithoutPages(progress2) {
for (const browser of this._playwright.allBrowsers()) {
for (const context2 of browser.contexts()) {
if (!context2.pages().length)
await context2.close(progress2, { reason: "Browser collected" });
}
if (!browser.contexts())
await browser.close(progress2, { reason: "Browser collected" });
}
}
};
wiredSymbol = Symbol("wired");
}
});
// packages/playwright-core/src/server/electron/electron.ts
async function waitForLine(progress2, process2, regex) {
const promise = new ManualPromise();
const rl = readline2.createInterface({ input: process2.stderr });
const failError = new Error("Process failed to launch!");
const listeners = [
eventsHelper.addEventListener(rl, "line", onLine),
eventsHelper.addEventListener(rl, "close", () => promise.reject(failError)),
eventsHelper.addEventListener(process2, "exit", () => promise.reject(failError)),
// It is Ok to remove error handler because we did not create process and there is another listener.
eventsHelper.addEventListener(process2, "error", () => promise.reject(failError))
];
function onLine(line) {
const match = line.match(regex);
if (match)
promise.resolve(match);
}
try {
return await progress2.race(promise);
} finally {
eventsHelper.removeEventListeners(listeners);
}
}
function escapeDoubleQuotes(str) {
return str.replace(/"/g, '\\"');
}
var import_fs29, import_os14, import_path28, readline2, ARTIFACTS_FOLDER3, ElectronApplication, Electron;
var init_electron = __esm({
"packages/playwright-core/src/server/electron/electron.ts"() {
"use strict";
import_fs29 = __toESM(require("fs"));
import_os14 = __toESM(require("os"));
import_path28 = __toESM(require("path"));
readline2 = __toESM(require("readline"));
init_ascii();
init_debugLogger();
init_eventsHelper();
init_processLauncher();
init_manualPromise();
init_package();
init_browserContext();
init_crBrowser();
init_crConnection();
init_crExecutionContext();
init_crProtocolHelper();
init_console();
init_helper();
init_instrumentation();
init_javascript();
init_transport();
init_progress();
ARTIFACTS_FOLDER3 = import_path28.default.join(import_os14.default.tmpdir(), "playwright-artifacts-");
ElectronApplication = class _ElectronApplication extends SdkObject {
constructor(parent, browser, nodeConnection, process2) {
super(parent, "electron-app");
this._nodeElectronHandlePromise = new ManualPromise();
this._process = process2;
this._browserContext = browser._defaultContext;
this._nodeConnection = nodeConnection;
this._nodeSession = nodeConnection.rootSession;
this._nodeSession.on("Runtime.executionContextCreated", async (event) => {
if (!event.context.auxData || !event.context.auxData.isDefault)
return;
const crExecutionContext = new CRExecutionContext(this._nodeSession, event.context);
this._nodeExecutionContext = new ExecutionContext(this, crExecutionContext, "electron");
const { result: remoteObject } = await crExecutionContext._client.send("Runtime.evaluate", {
expression: `require('electron')`,
contextId: event.context.id,
// Needed after Electron 28 to get access to require: https://github.com/microsoft/playwright/issues/28048
includeCommandLineAPI: true
});
this._nodeElectronHandlePromise.resolve(new JSHandle(this._nodeExecutionContext, "object", "ElectronModule", remoteObject.objectId));
});
this._nodeSession.on("Runtime.consoleAPICalled", (event) => this._onConsoleAPI(event));
const appClosePromise = new Promise((f) => this.once(_ElectronApplication.Events.Close, f));
this._browserContext.setCustomCloseHandler(async () => {
const electronHandle = await this._nodeElectronHandlePromise;
await electronHandle.evaluate(({ app }) => app.quit()).catch(() => {
});
this._nodeConnection.close();
await appClosePromise;
});
}
static {
this.Events = {
Close: "close",
Console: "console"
};
}
async _onConsoleAPI(event) {
if (event.executionContextId === 0) {
return;
}
if (!this._nodeExecutionContext)
return;
const args = event.args.map((arg) => createHandle(this._nodeExecutionContext, arg));
const message = new ConsoleMessage(null, null, event.type, void 0, args, stackTraceToLocation(event.stackTrace), event.timestamp);
this.emit(_ElectronApplication.Events.Console, message);
}
async initialize() {
await this._nodeSession.send("Runtime.enable", {});
await this._nodeSession.send("Runtime.evaluate", { expression: "__playwright_run()" });
}
process() {
return this._process;
}
context() {
return this._browserContext;
}
async close(progress2) {
await this._browserContext.close(progress2, { reason: "Application exited" });
}
async browserWindow(progress2, page) {
const targetId = page.delegate._targetId;
const electronHandle = await progress2.race(this._nodeElectronHandlePromise);
return await progress2.race(electronHandle.evaluateHandle(({ BrowserWindow, webContents }, targetId2) => {
const wc = webContents.fromDevToolsTargetId(targetId2);
return BrowserWindow.fromWebContents(wc);
}, targetId));
}
};
Electron = class extends SdkObject {
constructor(playwright2) {
super(playwright2, "electron");
this.logName = "browser";
}
async launch(progress2, options2) {
let app = void 0;
let electronArguments = ["--inspect=0", "--remote-debugging-port=0", ...options2.args || []];
if (import_os14.default.platform() === "linux") {
if (!options2.chromiumSandbox && electronArguments.indexOf("--no-sandbox") === -1)
electronArguments.unshift("--no-sandbox");
}
let artifactsDir;
const tempDirectories = [];
if (options2.artifactsDir) {
artifactsDir = options2.artifactsDir;
} else {
artifactsDir = await progress2.race(import_fs29.default.promises.mkdtemp(ARTIFACTS_FOLDER3));
tempDirectories.push(artifactsDir);
}
const browserLogsCollector = new RecentLogsCollector();
const env = options2.env ? envArrayToObject(options2.env) : process.env;
let command;
if (options2.executablePath) {
command = options2.executablePath;
} else {
try {
command = require("electron/index.js");
} catch (error) {
if (error?.code === "MODULE_NOT_FOUND") {
throw new Error("\n" + wrapInASCIIBox([
"Electron executablePath not found!",
"Please install it using `npm install -D electron` or set the executablePath to your Electron executable."
].join("\n"), 1));
}
throw error;
}
electronArguments.unshift("-r", libPath("server", "electron", "loader.js"));
}
let shell = false;
if (process.platform === "win32") {
shell = true;
command = [command, ...electronArguments].map((arg) => `"${escapeDoubleQuotes(arg)}"`).join(" ");
electronArguments = [];
}
delete env.NODE_OPTIONS;
const { launchedProcess, gracefullyClose, kill } = await progress2.race(launchProcess({
command,
args: electronArguments,
env,
log: (message) => {
progress2.log(message);
browserLogsCollector.log(message);
},
shell,
stdio: "pipe",
cwd: options2.cwd,
tempDirectories,
attemptToGracefullyClose: () => app.close(nullProgress),
handleSIGINT: true,
handleSIGTERM: true,
handleSIGHUP: true,
onExit: () => app?.emit(ElectronApplication.Events.Close)
}));
const waitForXserverError = waitForLine(progress2, launchedProcess, /Unable to open X display/).then(() => {
throw new Error([
"Unable to open X display!",
`================================`,
"Most likely this is because there is no X server available.",
"Use 'xvfb-run' on Linux to launch your tests with an emulated display server.",
"For example: 'xvfb-run npm run test:e2e'",
`================================`,
progress2.metadata.log
].join("\n"));
});
const nodeMatchPromise = waitForLine(progress2, launchedProcess, /^Debugger listening on (ws:\/\/.*)$/);
const chromeMatchPromise = waitForLine(progress2, launchedProcess, /^DevTools listening on (ws:\/\/.*)$/);
const debuggerDisconnectPromise = waitForLine(progress2, launchedProcess, /Waiting for the debugger to disconnect\.\.\./);
try {
const nodeMatch = await nodeMatchPromise;
const nodeTransport = await WebSocketTransport.connect(progress2, nodeMatch[1]);
const nodeConnection = new CRConnection(this, nodeTransport, helper.debugProtocolLogger(), browserLogsCollector);
debuggerDisconnectPromise.then(() => {
nodeTransport.close();
}).catch(() => {
});
const chromeMatch = await progress2.race(Promise.race([
chromeMatchPromise,
waitForXserverError
]));
const chromeTransport = await WebSocketTransport.connect(progress2, chromeMatch[1]);
const browserProcess = {
onclose: void 0,
process: launchedProcess,
close: gracefullyClose,
kill
};
const contextOptions = {
...options2,
noDefaultViewport: true
};
const browserOptions = {
name: "electron",
browserType: "chromium",
headful: true,
persistent: contextOptions,
browserProcess,
protocolLogger: helper.debugProtocolLogger(),
browserLogsCollector,
artifactsDir,
downloadsPath: artifactsDir,
tracesDir: options2.tracesDir || artifactsDir,
originalLaunchOptions: {}
};
validateBrowserContextOptions(contextOptions, browserOptions);
const browser = await progress2.race(CRBrowser.connect(this.attribution.playwright, chromeTransport, browserOptions));
app = new ElectronApplication(this, browser, nodeConnection, launchedProcess);
await progress2.race(app.initialize());
return app;
} catch (error) {
await progress2.race(kill());
throw error;
}
}
};
}
});
// packages/playwright-core/src/server/firefox/ffConnection.ts
var import_events10, ConnectionEvents2, kBrowserCloseMessageId3, FFConnection, FFSession;
var init_ffConnection = __esm({
"packages/playwright-core/src/server/firefox/ffConnection.ts"() {
"use strict";
import_events10 = require("events");
init_debugLogger();
init_helper();
init_protocolError();
ConnectionEvents2 = {
Disconnected: Symbol("Disconnected")
};
kBrowserCloseMessageId3 = -9999;
FFConnection = class extends import_events10.EventEmitter {
constructor(transport, protocolLogger, browserLogsCollector) {
super();
this.setMaxListeners(0);
this._transport = transport;
this._protocolLogger = protocolLogger;
this._browserLogsCollector = browserLogsCollector;
this._lastId = 0;
this._sessions = /* @__PURE__ */ new Map();
this._closed = false;
this.rootSession = new FFSession(this, "", (message) => this._rawSend(message));
this._sessions.set("", this.rootSession);
this._transport.onmessage = this._onMessage.bind(this);
this._transport.onclose = this._onClose.bind(this);
}
nextMessageId() {
return ++this._lastId;
}
_rawSend(message) {
this._protocolLogger("send", message);
this._transport.send(message);
}
async _onMessage(message) {
this._protocolLogger("receive", message);
if (message.id === kBrowserCloseMessageId3)
return;
const session2 = this._sessions.get(message.sessionId || "");
if (session2)
session2.dispatchMessage(message);
}
_onClose(reason) {
this._closed = true;
this._transport.onmessage = void 0;
this._transport.onclose = void 0;
this._browserDisconnectedLogs = helper.formatBrowserLogs(this._browserLogsCollector.recentLogs(), reason);
this.rootSession.dispose();
Promise.resolve().then(() => this.emit(ConnectionEvents2.Disconnected));
}
close() {
if (!this._closed)
this._transport.close();
}
createSession(sessionId) {
const session2 = new FFSession(this, sessionId, (message) => this._rawSend({ ...message, sessionId }));
this._sessions.set(sessionId, session2);
return session2;
}
};
FFSession = class extends import_events10.EventEmitter {
constructor(connection, sessionId, rawSend) {
super();
this._disposed = false;
this._crashed = false;
this.setMaxListeners(0);
this._callbacks = /* @__PURE__ */ new Map();
this._connection = connection;
this._sessionId = sessionId;
this._rawSend = rawSend;
}
markAsCrashed() {
this._crashed = true;
}
async send(method, params2) {
if (this._crashed || this._disposed || this._connection._closed || this._connection._browserDisconnectedLogs)
throw new ProtocolError(this._crashed ? "crashed" : "closed", void 0, this._connection._browserDisconnectedLogs);
const id = this._connection.nextMessageId();
this._rawSend({ method, params: params2, id });
return new Promise((resolve, reject) => {
this._callbacks.set(id, { resolve, reject, error: new ProtocolError("error", method) });
});
}
sendMayFail(method, params2) {
return this.send(method, params2).catch((error) => debugLogger.log("error", error));
}
dispatchMessage(object) {
if (object.id) {
const callback = this._callbacks.get(object.id);
if (callback) {
this._callbacks.delete(object.id);
if (object.error) {
callback.error.setMessage(object.error.message);
callback.reject(callback.error);
} else {
callback.resolve(object.result);
}
}
} else {
Promise.resolve().then(() => this.emit(object.method, object.params));
}
}
dispose() {
this._disposed = true;
this._connection._sessions.delete(this._sessionId);
for (const callback of this._callbacks.values()) {
callback.error.type = this._crashed ? "crashed" : "closed";
callback.error.logs = this._connection._browserDisconnectedLogs;
callback.reject(callback.error);
}
this._callbacks.clear();
}
};
}
});
// packages/playwright-core/src/server/firefox/ffExecutionContext.ts
function checkException(exceptionDetails) {
if (!exceptionDetails)
return;
if (exceptionDetails.value)
throw new JavaScriptErrorInEvaluate(JSON.stringify(exceptionDetails.value));
else
throw new JavaScriptErrorInEvaluate(exceptionDetails.text + (exceptionDetails.stack ? "\n" + exceptionDetails.stack : ""));
}
function rewriteError3(error) {
if (error.message.includes("cyclic object value") || error.message.includes("Object is not serializable"))
return { result: { type: "undefined", value: void 0 } };
if (error instanceof TypeError && error.message.startsWith("Converting circular structure to JSON"))
rewriteErrorMessage(error, error.message + " Are you passing a nested JSHandle?");
if (!isJavaScriptErrorInEvaluate(error) && !isSessionClosedError(error))
throw new Error("Execution context was destroyed, most likely because of a navigation.");
throw error;
}
function potentiallyUnserializableValue2(remoteObject) {
const value2 = remoteObject.value;
const unserializableValue = remoteObject.unserializableValue;
return unserializableValue ? parseUnserializableValue(unserializableValue) : value2;
}
function renderPreview3(object) {
if (object.type === "undefined")
return "undefined";
if (object.unserializableValue)
return String(object.unserializableValue);
if (object.type === "symbol")
return "Symbol()";
if (object.subtype === "regexp")
return "RegExp";
if (object.subtype === "weakmap")
return "WeakMap";
if (object.subtype === "weakset")
return "WeakSet";
if (object.subtype)
return object.subtype[0].toUpperCase() + object.subtype.slice(1);
if ("value" in object)
return String(object.value);
}
function createHandle3(context2, remoteObject) {
if (remoteObject.subtype === "node") {
assert(context2 instanceof FrameExecutionContext);
return new ElementHandle(context2, remoteObject.objectId);
}
return new JSHandle(context2, remoteObject.subtype || remoteObject.type || "", renderPreview3(remoteObject), remoteObject.objectId, potentiallyUnserializableValue2(remoteObject));
}
var FFExecutionContext;
var init_ffExecutionContext = __esm({
"packages/playwright-core/src/server/firefox/ffExecutionContext.ts"() {
"use strict";
init_assert();
init_stackTrace();
init_utilityScriptSerializers();
init_javascript();
init_dom();
init_protocolError();
FFExecutionContext = class {
constructor(session2, executionContextId) {
this._session = session2;
this._executionContextId = executionContextId;
}
async rawEvaluateJSON(expression2) {
const payload = await this._session.send("Runtime.evaluate", {
expression: expression2,
returnByValue: true,
executionContextId: this._executionContextId
}).catch(rewriteError3);
checkException(payload.exceptionDetails);
return payload.result.value;
}
async rawEvaluateHandle(context2, expression2) {
const payload = await this._session.send("Runtime.evaluate", {
expression: expression2,
returnByValue: false,
executionContextId: this._executionContextId
}).catch(rewriteError3);
checkException(payload.exceptionDetails);
return createHandle3(context2, payload.result);
}
async evaluateWithArguments(expression2, returnByValue, utilityScript, values, handles) {
const payload = await this._session.send("Runtime.callFunction", {
functionDeclaration: expression2,
args: [
{ objectId: utilityScript._objectId, value: void 0 },
...values.map((value2) => ({ value: value2 })),
...handles.map((handle) => ({ objectId: handle._objectId, value: void 0 }))
],
returnByValue,
executionContextId: this._executionContextId
}).catch(rewriteError3);
checkException(payload.exceptionDetails);
if (returnByValue)
return parseEvaluationResultValue(payload.result.value);
return createHandle3(utilityScript._context, payload.result);
}
async getProperties(object) {
const response2 = await this._session.send("Runtime.getObjectProperties", {
executionContextId: this._executionContextId,
objectId: object._objectId
});
const result2 = /* @__PURE__ */ new Map();
for (const property of response2.properties)
result2.set(property.name, createHandle3(object._context, property.value));
return result2;
}
async releaseHandle(handle) {
if (!handle._objectId)
return;
await this._session.send("Runtime.disposeObject", {
executionContextId: this._executionContextId,
objectId: handle._objectId
});
}
shouldPrependErrorPrefix() {
return true;
}
};
}
});
// packages/playwright-core/src/server/firefox/ffInput.ts
function toFirefoxKeyDescription(description) {
const override = kFirefoxKeyOverrides.get(description.key);
if (!override)
return description;
return {
...description,
...override
};
}
function toModifiersMask2(modifiers) {
let mask = 0;
if (modifiers.has("Alt"))
mask |= 1;
if (modifiers.has("Control"))
mask |= 2;
if (modifiers.has("Shift"))
mask |= 4;
if (modifiers.has("Meta"))
mask |= 8;
return mask;
}
function toButtonNumber(button) {
if (button === "left")
return 0;
if (button === "middle")
return 1;
if (button === "right")
return 2;
return 0;
}
function toButtonsMask2(buttons) {
let mask = 0;
if (buttons.has("left"))
mask |= 1;
if (buttons.has("right"))
mask |= 2;
if (buttons.has("middle"))
mask |= 4;
return mask;
}
var kFirefoxKeyOverrides, RawKeyboardImpl3, RawMouseImpl3, RawTouchscreenImpl3;
var init_ffInput = __esm({
"packages/playwright-core/src/server/firefox/ffInput.ts"() {
"use strict";
kFirefoxKeyOverrides = /* @__PURE__ */ new Map([
["AudioVolumeMute", { code: "VolumeMute", keyCodeWithoutLocation: 181 }],
["AudioVolumeDown", { code: "VolumeDown", keyCodeWithoutLocation: 182 }],
["AudioVolumeUp", { code: "VolumeUp", keyCodeWithoutLocation: 183 }]
]);
RawKeyboardImpl3 = class {
constructor(client) {
this._client = client;
}
async keydown(progress2, modifiers, keyName, description, autoRepeat) {
const keyDescription = toFirefoxKeyDescription(description);
let text2 = keyDescription.text;
if (text2 === "\r")
text2 = "";
const { code, key, location: location2 } = keyDescription;
await progress2.race(this._client.send("Page.dispatchKeyEvent", {
type: "keydown",
keyCode: keyDescription.keyCodeWithoutLocation,
code,
key,
repeat: autoRepeat,
location: location2,
text: text2
}));
}
async keyup(progress2, modifiers, keyName, description) {
const keyDescription = toFirefoxKeyDescription(description);
const { code, key, location: location2 } = keyDescription;
await progress2.race(this._client.send("Page.dispatchKeyEvent", {
type: "keyup",
key,
keyCode: keyDescription.keyCodeWithoutLocation,
code,
location: location2,
repeat: false
}));
}
async sendText(progress2, text2) {
await progress2.race(this._client.send("Page.insertText", { text: text2 }));
}
};
RawMouseImpl3 = class {
constructor(client) {
this._client = client;
}
async move(progress2, x, y, button, buttons, modifiers, forClick) {
await progress2.race(this._client.send("Page.dispatchMouseEvent", {
type: "mousemove",
button: 0,
buttons: toButtonsMask2(buttons),
x: Math.floor(x),
y: Math.floor(y),
modifiers: toModifiersMask2(modifiers)
}));
}
async down(progress2, x, y, button, buttons, modifiers, clickCount) {
await progress2.race(this._client.send("Page.dispatchMouseEvent", {
type: "mousedown",
button: toButtonNumber(button),
buttons: toButtonsMask2(buttons),
x: Math.floor(x),
y: Math.floor(y),
modifiers: toModifiersMask2(modifiers),
clickCount
}));
}
async up(progress2, x, y, button, buttons, modifiers, clickCount) {
await progress2.race(this._client.send("Page.dispatchMouseEvent", {
type: "mouseup",
button: toButtonNumber(button),
buttons: toButtonsMask2(buttons),
x: Math.floor(x),
y: Math.floor(y),
modifiers: toModifiersMask2(modifiers),
clickCount
}));
}
async wheel(progress2, x, y, buttons, modifiers, deltaX, deltaY) {
await this._page.mainFrame().evaluateExpression(progress2, `new Promise(requestAnimationFrame)`, { world: "utility" });
await progress2.race(this._client.send("Page.dispatchWheelEvent", {
deltaX,
deltaY,
x: Math.floor(x),
y: Math.floor(y),
deltaZ: 0,
modifiers: toModifiersMask2(modifiers)
}));
}
setPage(page) {
this._page = page;
}
};
RawTouchscreenImpl3 = class {
constructor(client) {
this._client = client;
}
async tap(progress2, x, y, modifiers) {
await progress2.race(this._client.send("Page.dispatchTapEvent", {
x,
y,
modifiers: toModifiersMask2(modifiers)
}));
}
};
}
});
// packages/playwright-core/src/server/firefox/ffNetworkManager.ts
function parseMultivalueHeaders(headers) {
const result2 = [];
for (const header of headers) {
const separator = header.name.toLowerCase() === "set-cookie" ? "\n" : ",";
const tokens = header.value.split(separator).map((s) => s.trim());
for (const token of tokens)
result2.push({ name: header.name, value: token });
}
return result2;
}
var FFNetworkManager, causeToResourceType, internalCauseToResourceType, InterceptableRequest2, FFRouteImpl;
var init_ffNetworkManager = __esm({
"packages/playwright-core/src/server/firefox/ffNetworkManager.ts"() {
"use strict";
init_eventsHelper();
init_network2();
FFNetworkManager = class {
constructor(session2, page) {
this._session = session2;
this._requests = /* @__PURE__ */ new Map();
this._page = page;
this._eventListeners = [
eventsHelper.addEventListener(session2, "Network.requestWillBeSent", this._onRequestWillBeSent.bind(this)),
eventsHelper.addEventListener(session2, "Network.responseReceived", this._onResponseReceived.bind(this)),
eventsHelper.addEventListener(session2, "Network.requestFinished", this._onRequestFinished.bind(this)),
eventsHelper.addEventListener(session2, "Network.requestFailed", this._onRequestFailed.bind(this))
];
}
dispose() {
eventsHelper.removeEventListeners(this._eventListeners);
}
async setRequestInterception(enabled) {
await Promise.all([
this._session.send("Network.setRequestInterception", { enabled }),
this._session.send("Page.setCacheDisabled", { cacheDisabled: enabled })
]);
}
_onRequestWillBeSent(event) {
const redirectedFrom = event.redirectedFrom ? this._requests.get(event.redirectedFrom) || null : null;
const frame = redirectedFrom ? redirectedFrom.request.frame() : event.frameId ? this._page.frameManager.frame(event.frameId) : null;
if (!frame)
return;
if (event.method === "OPTIONS" && !event.isIntercepted)
return;
if (redirectedFrom)
this._requests.delete(redirectedFrom._id);
const request2 = new InterceptableRequest2(frame, redirectedFrom, event);
let route2;
if (event.isIntercepted)
route2 = new FFRouteImpl(this._session, request2);
this._requests.set(request2._id, request2);
this._page.frameManager.requestStarted(request2.request, route2);
}
_onResponseReceived(event) {
const request2 = this._requests.get(event.requestId);
if (!request2)
return;
const getResponseBody = async () => {
const response3 = await this._session.send("Network.getResponseBody", {
requestId: request2._id
});
if (response3.evicted)
throw new Error(`Response body for ${request2.request.method()} ${request2.request.url()} was evicted!`);
return Buffer.from(response3.base64body, "base64");
};
const startTime = event.timing.startTime;
function relativeToStart(time) {
if (!time)
return -1;
return (time - startTime) / 1e3;
}
const timing = {
startTime: startTime / 1e3,
domainLookupStart: relativeToStart(event.timing.domainLookupStart),
domainLookupEnd: relativeToStart(event.timing.domainLookupEnd),
connectStart: relativeToStart(event.timing.connectStart),
secureConnectionStart: relativeToStart(event.timing.secureConnectionStart),
connectEnd: relativeToStart(event.timing.connectEnd),
requestStart: relativeToStart(event.timing.requestStart),
responseStart: relativeToStart(event.timing.responseStart)
};
const response2 = new Response2(request2.request, event.status, event.statusText, parseMultivalueHeaders(event.headers), timing, getResponseBody, event.fromServiceWorker);
if (event?.remoteIPAddress && typeof event?.remotePort === "number") {
response2._serverAddrFinished({
ipAddress: event.remoteIPAddress,
port: event.remotePort
});
} else {
response2._serverAddrFinished();
}
response2._securityDetailsFinished({
protocol: event?.securityDetails?.protocol,
subjectName: event?.securityDetails?.subjectName,
issuer: event?.securityDetails?.issuer,
validFrom: event?.securityDetails?.validFrom,
validTo: event?.securityDetails?.validTo
});
response2.setRawResponseHeaders(null);
response2.setResponseHeadersSize(null);
this._page.frameManager.requestReceivedResponse(response2);
}
_onRequestFinished(event) {
const request2 = this._requests.get(event.requestId);
if (!request2)
return;
const response2 = request2.request._existingResponse();
response2.setTransferSize(event.transferSize);
response2.setEncodedBodySize(event.encodedBodySize);
const isRedirected = response2.status() >= 300 && response2.status() <= 399;
const responseEndTime = event.responseEndTime ? event.responseEndTime / 1e3 - response2.timing().startTime : -1;
if (isRedirected) {
response2._requestFinished(responseEndTime);
} else {
this._requests.delete(request2._id);
response2._requestFinished(responseEndTime);
}
response2._setHttpVersion(event.protocolVersion ?? null);
this._page.frameManager.reportRequestFinished(request2.request, response2);
}
_onRequestFailed(event) {
const request2 = this._requests.get(event.requestId);
if (!request2)
return;
this._requests.delete(request2._id);
const response2 = request2.request._existingResponse();
if (response2) {
response2.setTransferSize(null);
response2.setEncodedBodySize(null);
response2._requestFinished(-1);
response2._setHttpVersion(null);
}
request2.request._setFailureText(event.errorCode);
this._page.frameManager.requestFailed(request2.request, event.errorCode === "NS_BINDING_ABORTED");
}
};
causeToResourceType = {
TYPE_INVALID: "other",
TYPE_OTHER: "other",
TYPE_SCRIPT: "script",
TYPE_IMAGE: "image",
TYPE_STYLESHEET: "stylesheet",
TYPE_OBJECT: "other",
TYPE_DOCUMENT: "document",
TYPE_SUBDOCUMENT: "document",
TYPE_REFRESH: "document",
TYPE_XBL: "other",
TYPE_PING: "other",
TYPE_XMLHTTPREQUEST: "xhr",
TYPE_OBJECT_SUBREQUEST: "other",
TYPE_DTD: "other",
TYPE_FONT: "font",
TYPE_MEDIA: "media",
TYPE_WEBSOCKET: "websocket",
TYPE_CSP_REPORT: "cspreport",
TYPE_XSLT: "other",
TYPE_BEACON: "beacon",
TYPE_FETCH: "fetch",
TYPE_IMAGESET: "image",
TYPE_WEB_MANIFEST: "manifest"
};
internalCauseToResourceType = {
TYPE_INTERNAL_EVENTSOURCE: "eventsource"
};
InterceptableRequest2 = class {
constructor(frame, redirectedFrom, payload) {
this._id = payload.requestId;
if (redirectedFrom)
redirectedFrom._redirectedTo = this;
let postDataBuffer = null;
if (payload.postData)
postDataBuffer = Buffer.from(payload.postData, "base64");
this.request = new Request(
frame._page.browserContext,
frame,
null,
redirectedFrom ? redirectedFrom.request : null,
payload.navigationId,
payload.url,
internalCauseToResourceType[payload.internalCause] || causeToResourceType[payload.cause] || "other",
payload.method,
postDataBuffer,
payload.headers
);
this.request.setRawRequestHeaders(null);
}
_finalRequest() {
let request2 = this;
while (request2._redirectedTo)
request2 = request2._redirectedTo;
return request2;
}
};
FFRouteImpl = class {
constructor(session2, request2) {
this._session = session2;
this._request = request2;
}
async continue(overrides) {
await this._session.sendMayFail("Network.resumeInterceptedRequest", {
requestId: this._request._id,
url: overrides.url,
method: overrides.method,
headers: overrides.headers,
postData: overrides.postData ? Buffer.from(overrides.postData).toString("base64") : void 0
});
}
async fulfill(response2) {
const base64body = response2.isBase64 ? response2.body : Buffer.from(response2.body).toString("base64");
await this._session.sendMayFail("Network.fulfillInterceptedRequest", {
requestId: this._request._id,
status: response2.status,
statusText: statusText(response2.status),
headers: response2.headers,
base64body
});
}
async abort(errorCode) {
await this._session.sendMayFail("Network.abortInterceptedRequest", {
requestId: this._request._id,
errorCode
});
}
};
}
});
// packages/playwright-core/src/server/firefox/ffPage.ts
function webSocketId(frameId, wsid) {
return `${frameId}---${wsid}`;
}
var UTILITY_WORLD_NAME2, FFPage;
var init_ffPage = __esm({
"packages/playwright-core/src/server/firefox/ffPage.ts"() {
"use strict";
init_stackTrace();
init_eventsHelper();
init_dialog();
init_dom();
init_page();
init_page();
init_ffConnection();
init_ffExecutionContext();
init_ffInput();
init_ffNetworkManager();
init_errors();
init_videoRecorder();
UTILITY_WORLD_NAME2 = "__playwright_utility_world__";
FFPage = class {
constructor(session2, browserContext, opener) {
this.cspErrorsAsynchronousForInlineScripts = true;
this._reportedAsNew = false;
this._workers = /* @__PURE__ */ new Map();
this._initScripts = [];
this._session = session2;
this._opener = opener;
this.rawKeyboard = new RawKeyboardImpl3(session2);
this.rawMouse = new RawMouseImpl3(session2);
this.rawTouchscreen = new RawTouchscreenImpl3(session2);
this._contextIdToContext = /* @__PURE__ */ new Map();
this._browserContext = browserContext;
this._page = new Page(this, browserContext);
this.rawMouse.setPage(this._page);
this._networkManager = new FFNetworkManager(session2, this._page);
this._page.on(Page.Events.FrameDetached, (frame) => this._removeContextsForFrame(frame));
this._eventListeners = [
eventsHelper.addEventListener(this._session, "Page.eventFired", this._onEventFired.bind(this)),
eventsHelper.addEventListener(this._session, "Page.frameAttached", this._onFrameAttached.bind(this)),
eventsHelper.addEventListener(this._session, "Page.frameDetached", this._onFrameDetached.bind(this)),
eventsHelper.addEventListener(this._session, "Page.navigationAborted", this._onNavigationAborted.bind(this)),
eventsHelper.addEventListener(this._session, "Page.navigationCommitted", this._onNavigationCommitted.bind(this)),
eventsHelper.addEventListener(this._session, "Page.navigationStarted", this._onNavigationStarted.bind(this)),
eventsHelper.addEventListener(this._session, "Page.sameDocumentNavigation", this._onSameDocumentNavigation.bind(this)),
eventsHelper.addEventListener(this._session, "Runtime.executionContextCreated", this._onExecutionContextCreated.bind(this)),
eventsHelper.addEventListener(this._session, "Runtime.executionContextDestroyed", this._onExecutionContextDestroyed.bind(this)),
eventsHelper.addEventListener(this._session, "Runtime.executionContextsCleared", this._onExecutionContextsCleared.bind(this)),
eventsHelper.addEventListener(this._session, "Page.linkClicked", (event) => this._onLinkClicked(event.phase)),
eventsHelper.addEventListener(this._session, "Page.uncaughtError", this._onUncaughtError.bind(this)),
eventsHelper.addEventListener(this._session, "Runtime.console", this._onConsole.bind(this)),
eventsHelper.addEventListener(this._session, "Page.dialogOpened", this._onDialogOpened.bind(this)),
eventsHelper.addEventListener(this._session, "Page.bindingCalled", this._onBindingCalled.bind(this)),
eventsHelper.addEventListener(this._session, "Page.fileChooserOpened", this._onFileChooserOpened.bind(this)),
eventsHelper.addEventListener(this._session, "Page.workerCreated", this._onWorkerCreated.bind(this)),
eventsHelper.addEventListener(this._session, "Page.workerDestroyed", this._onWorkerDestroyed.bind(this)),
eventsHelper.addEventListener(this._session, "Page.dispatchMessageFromWorker", this._onDispatchMessageFromWorker.bind(this)),
eventsHelper.addEventListener(this._session, "Page.crashed", this._onCrashed.bind(this)),
eventsHelper.addEventListener(this._session, "Page.webSocketCreated", this._onWebSocketCreated.bind(this)),
eventsHelper.addEventListener(this._session, "Page.webSocketClosed", this._onWebSocketClosed.bind(this)),
eventsHelper.addEventListener(this._session, "Page.webSocketFrameReceived", this._onWebSocketFrameReceived.bind(this)),
eventsHelper.addEventListener(this._session, "Page.webSocketFrameSent", this._onWebSocketFrameSent.bind(this)),
eventsHelper.addEventListener(this._session, "Page.screencastFrame", this._onScreencastFrame.bind(this))
];
const promises = [];
if (!this._page.isStorageStatePage)
startAutomaticVideoRecording(this._page);
promises.push(new Promise((f) => this._session.once("Page.ready", f)));
Promise.all(promises).then(() => this._reportAsNew(), (error) => this._reportAsNew(error));
this.addInitScript(new InitScript(this._page, ""), UTILITY_WORLD_NAME2).catch((e) => this._reportAsNew(e));
}
_reportAsNew(error) {
if (this._reportedAsNew)
return;
this._reportedAsNew = true;
this._page.reportAsNew(this._opener?._page, error);
}
_onWebSocketCreated(event) {
this._page.frameManager.onWebSocketCreated(webSocketId(event.frameId, event.wsid), event.requestURL);
this._page.frameManager.onWebSocketRequest(webSocketId(event.frameId, event.wsid));
}
_onWebSocketClosed(event) {
if (event.error)
this._page.frameManager.webSocketError(webSocketId(event.frameId, event.wsid), event.error);
this._page.frameManager.webSocketClosed(webSocketId(event.frameId, event.wsid));
}
_onWebSocketFrameReceived(event) {
this._page.frameManager.webSocketFrameReceived(webSocketId(event.frameId, event.wsid), event.opcode, event.data);
}
_onWebSocketFrameSent(event) {
this._page.frameManager.onWebSocketFrameSent(webSocketId(event.frameId, event.wsid), event.opcode, event.data);
}
_onExecutionContextCreated(payload) {
const { executionContextId, auxData } = payload;
const frame = this._page.frameManager.frame(auxData.frameId);
if (!frame)
return;
const delegate = new FFExecutionContext(this._session, executionContextId);
let worldName = null;
if (auxData.name === UTILITY_WORLD_NAME2)
worldName = "utility";
else if (!auxData.name)
worldName = "main";
const context2 = new FrameExecutionContext(delegate, frame, worldName);
if (worldName)
frame.contextCreated(worldName, context2);
this._contextIdToContext.set(executionContextId, context2);
}
_onExecutionContextDestroyed(payload) {
const { executionContextId } = payload;
const context2 = this._contextIdToContext.get(executionContextId);
if (!context2)
return;
this._contextIdToContext.delete(executionContextId);
context2.frame.contextDestroyed(context2);
}
_onExecutionContextsCleared() {
for (const executionContextId of Array.from(this._contextIdToContext.keys()))
this._onExecutionContextDestroyed({ executionContextId });
}
_removeContextsForFrame(frame) {
for (const [contextId4, context2] of this._contextIdToContext) {
if (context2.frame === frame)
this._contextIdToContext.delete(contextId4);
}
}
_onLinkClicked(phase) {
if (phase === "before")
this._page.frameManager.frameWillPotentiallyRequestNavigation();
else
this._page.frameManager.frameDidPotentiallyRequestNavigation();
}
_onNavigationStarted(params2) {
this._page.frameManager.frameRequestedNavigation(params2.frameId, params2.navigationId);
}
_onNavigationAborted(params2) {
this._page.frameManager.frameAbortedNavigation(params2.frameId, params2.errorText, params2.navigationId);
}
_onNavigationCommitted(params2) {
for (const [workerId, worker] of this._workers) {
if (worker.frameId === params2.frameId)
this._onWorkerDestroyed({ workerId });
}
this._page.frameManager.frameCommittedNewDocumentNavigation(params2.frameId, params2.url, params2.name || "", params2.navigationId || "", false);
}
_onSameDocumentNavigation(params2) {
this._page.frameManager.frameCommittedSameDocumentNavigation(params2.frameId, params2.url);
}
_onFrameAttached(params2) {
this._page.frameManager.frameAttached(params2.frameId, params2.parentFrameId);
}
_onFrameDetached(params2) {
this._page.frameManager.frameDetached(params2.frameId);
}
_onEventFired(payload) {
const { frameId, name } = payload;
if (name === "load")
this._page.frameManager.frameLifecycleEvent(frameId, "load");
if (name === "DOMContentLoaded")
this._page.frameManager.frameLifecycleEvent(frameId, "domcontentloaded");
}
_onUncaughtError(params2) {
const { name, message } = splitErrorMessage(params2.message);
const error = new Error(message);
error.stack = params2.message + "\n" + params2.stack.split("\n").filter(Boolean).map((a) => a.replace(/([^@]*)@(.*)/, " at $1 ($2)")).join("\n");
error.name = name;
this._page.addPageError(error, params2.location);
}
_onConsole(payload) {
const { type: type3, args, executionContextId, location: location2 } = payload;
const context2 = this._contextIdToContext.get(executionContextId);
if (!context2)
return;
const timestamp = Date.now();
this._page.addConsoleMessage(null, type3 === "warn" ? "warning" : type3, args.map((arg) => createHandle3(context2, arg)), location2, void 0, timestamp);
}
_onDialogOpened(params2) {
this._page.browserContext.dialogManager.dialogDidOpen(new Dialog(
this._page,
params2.type,
params2.message,
async (accept, promptText) => {
await this._session.sendMayFail("Page.handleDialog", { dialogId: params2.dialogId, accept, promptText });
},
params2.defaultValue
));
}
async _onBindingCalled(event) {
const pageOrError = await this._page.waitForInitializedOrError();
if (!(pageOrError instanceof Error)) {
const context2 = this._contextIdToContext.get(event.executionContextId);
if (context2)
await this._page.onBindingCalled(event.payload, context2);
}
}
async _onFileChooserOpened(payload) {
const { executionContextId, element: element2 } = payload;
const context2 = this._contextIdToContext.get(executionContextId);
if (!context2)
return;
const handle = createHandle3(context2, element2).asElement();
await this._page._onFileChooserOpened(handle);
}
async _onWorkerCreated(event) {
const workerId = event.workerId;
const worker = new Worker(this._page, event.url);
const workerSession = new FFSession(this._session._connection, workerId, (message) => {
this._session.send("Page.sendMessageToWorker", {
frameId: event.frameId,
workerId,
message: JSON.stringify(message)
}).catch((e) => {
workerSession.dispatchMessage({ id: message.id, method: "", params: {}, error: { message: e.message, data: void 0 } });
});
});
this._workers.set(workerId, { session: workerSession, frameId: event.frameId });
this._page.addWorker(workerId, worker);
workerSession.once("Runtime.executionContextCreated", (event2) => {
worker.createExecutionContext(new FFExecutionContext(workerSession, event2.executionContextId));
worker.workerScriptLoaded();
});
workerSession.on("Runtime.console", (event2) => {
const { type: type3, args, location: location2 } = event2;
const context2 = worker.existingExecutionContext;
this._page.addConsoleMessage(worker, type3, args.map((arg) => createHandle3(context2, arg)), location2, void 0, Date.now());
});
}
_onWorkerDestroyed(event) {
const workerId = event.workerId;
const worker = this._workers.get(workerId);
if (!worker)
return;
worker.session.dispose();
this._workers.delete(workerId);
this._page.removeWorker(workerId);
}
async _onDispatchMessageFromWorker(event) {
const worker = this._workers.get(event.workerId);
if (!worker)
return;
worker.session.dispatchMessage(JSON.parse(event.message));
}
async _onCrashed(event) {
this._session.markAsCrashed();
this._page._didCrash();
}
didClose() {
this._reportAsNew(new TargetClosedError(this._page.closeReason()));
this._session.dispose();
eventsHelper.removeEventListeners(this._eventListeners);
this._networkManager.dispose();
this._page._didClose();
}
async navigateFrame(frame, url2, referer) {
const response2 = await this._session.send("Page.navigate", { url: url2, referer, frameId: frame._id });
return { newDocumentId: response2.navigationId || void 0 };
}
async updateExtraHTTPHeaders() {
await this._session.send("Network.setExtraHTTPHeaders", { headers: this._page.extraHTTPHeaders() || [] });
}
async updateEmulatedViewportSize() {
const viewportSize = this._page.emulatedSize()?.viewport ?? null;
await this._session.send("Page.setViewportSize", { viewportSize });
}
async bringToFront() {
await this._session.send("Page.bringToFront", {});
}
async updateEmulateMedia() {
const emulatedMedia = this._page.emulatedMedia();
const colorScheme = emulatedMedia.colorScheme === "no-override" ? void 0 : emulatedMedia.colorScheme;
const reducedMotion = emulatedMedia.reducedMotion === "no-override" ? void 0 : emulatedMedia.reducedMotion;
const forcedColors = emulatedMedia.forcedColors === "no-override" ? void 0 : emulatedMedia.forcedColors;
const contrast = emulatedMedia.contrast === "no-override" ? void 0 : emulatedMedia.contrast;
await this._session.send("Page.setEmulatedMedia", {
// Empty string means reset.
type: emulatedMedia.media === "no-override" ? "" : emulatedMedia.media,
colorScheme,
reducedMotion,
forcedColors,
contrast
});
}
async updateRequestInterception() {
await this._networkManager.setRequestInterception(this._page.needsRequestInterception());
}
async updateFileChooserInterception() {
const enabled = this._page.fileChooserIntercepted();
await this._session.send("Page.setInterceptFileChooserDialog", { enabled }).catch(() => {
});
}
async reload() {
await this._session.send("Page.reload");
}
async goBack() {
const { success } = await this._session.send("Page.goBack", { frameId: this._page.mainFrame()._id });
return success;
}
async goForward() {
const { success } = await this._session.send("Page.goForward", { frameId: this._page.mainFrame()._id });
return success;
}
async requestGC() {
await this._session.send("Heap.collectGarbage");
}
async addInitScript(initScript, worldName) {
this._initScripts.push({ initScript, worldName });
await this._updateInitScripts();
}
async removeInitScripts(initScripts) {
const set = new Set(initScripts);
this._initScripts = this._initScripts.filter((s) => !set.has(s.initScript));
await this._updateInitScripts();
}
async _updateInitScripts() {
await this._session.send("Page.setInitScripts", { scripts: this._initScripts.map((s) => ({ script: s.initScript.source, worldName: s.worldName })) });
}
async closePage(runBeforeUnload) {
await this._session.send("Page.close", { runBeforeUnload });
}
async setBackgroundColor(color) {
if (color)
throw new Error("Not implemented");
}
async takeScreenshot(progress2, format2, documentRect, viewportRect, quality, fitsViewport, scale) {
if (!documentRect) {
const scrollOffset = await this._page.mainFrame().waitForFunctionValueInUtility(progress2, () => ({ x: window.scrollX, y: window.scrollY }));
documentRect = {
x: viewportRect.x + scrollOffset.x,
y: viewportRect.y + scrollOffset.y,
width: viewportRect.width,
height: viewportRect.height
};
}
const { data } = await progress2.race(this._session.send("Page.screenshot", {
mimeType: "image/" + format2,
clip: documentRect,
quality,
omitDeviceScaleFactor: scale === "css"
}));
return Buffer.from(data, "base64");
}
async getContentFrame(handle) {
const { contentFrameId } = await this._session.send("Page.describeNode", {
frameId: handle._context.frame._id,
objectId: handle._objectId
});
if (!contentFrameId)
return null;
return this._page.frameManager.frame(contentFrameId);
}
async getOwnerFrame(handle) {
const { ownerFrameId } = await this._session.send("Page.describeNode", {
frameId: handle._context.frame._id,
objectId: handle._objectId
});
return ownerFrameId || null;
}
async getBoundingBox(handle) {
const quads = await this.getContentQuads(handle);
if (!quads || !quads.length)
return null;
let minX = Infinity;
let maxX = -Infinity;
let minY = Infinity;
let maxY = -Infinity;
for (const quad of quads) {
for (const point of quad) {
minX = Math.min(minX, point.x);
maxX = Math.max(maxX, point.x);
minY = Math.min(minY, point.y);
maxY = Math.max(maxY, point.y);
}
}
return { x: minX, y: minY, width: maxX - minX, height: maxY - minY };
}
async scrollRectIntoViewIfNeeded(handle, rect) {
return await this._session.send("Page.scrollIntoViewIfNeeded", {
frameId: handle._context.frame._id,
objectId: handle._objectId,
rect
}).then(() => "done").catch((e) => {
if (e instanceof Error && e.message.includes("Node is detached from document"))
return "error:notconnected";
if (e instanceof Error && e.message.includes("Node does not have a layout object"))
return "error:notvisible";
throw e;
});
}
startScreencast(options2) {
this._session.sendMayFail("Page.startScreencast", { width: options2.width, height: options2.height, quality: options2.quality });
}
stopScreencast() {
this._session.sendMayFail("Page.stopScreencast");
}
_onScreencastFrame(event) {
const buffer = Buffer.from(event.data, "base64");
this._page.screencast.onScreencastFrame({
buffer,
frameSwapWallTime: event.timestamp * 1e3,
// timestamp is in seconds, we need to convert to milliseconds.
viewportWidth: event.deviceWidth,
viewportHeight: event.deviceHeight
}, () => {
this._session.sendMayFail("Page.screencastFrameAck");
});
}
rafCountForStablePosition() {
return 1;
}
async getContentQuads(handle) {
const result2 = await this._session.sendMayFail("Page.getContentQuads", {
frameId: handle._context.frame._id,
objectId: handle._objectId
});
if (!result2)
return null;
return result2.quads.map((quad) => [quad.p1, quad.p2, quad.p3, quad.p4]);
}
async setInputFilePaths(progress2, handle, files) {
await progress2.race(this._session.send("Page.setFileInputFiles", {
frameId: handle._context.frame._id,
objectId: handle._objectId,
files
}));
}
async adoptElementHandle(handle, to) {
const result2 = await this._session.send("Page.adoptNode", {
frameId: handle._context.frame._id,
objectId: handle._objectId,
executionContextId: to.delegate._executionContextId
});
if (!result2.remoteObject)
throw new Error(kUnableToAdoptErrorMessage);
return createHandle3(to, result2.remoteObject);
}
async inputActionEpilogue() {
}
async resetForReuse(progress2) {
await this.rawMouse.move(progress2, -1, -1, "none", /* @__PURE__ */ new Set(), /* @__PURE__ */ new Set(), false);
}
async getFrameElement(frame) {
const parent = frame.parentFrame();
if (!parent)
throw new Error("Frame has been detached.");
const context2 = await parent.mainContext();
const result2 = await this._session.send("Page.adoptNode", {
frameId: frame._id,
executionContextId: context2.delegate._executionContextId
});
if (!result2.remoteObject)
throw new Error("Frame has been detached.");
return createHandle3(context2, result2.remoteObject);
}
shouldToggleStyleSheetToSyncAnimations() {
return false;
}
async setDockTile(image) {
}
};
}
});
// packages/playwright-core/src/server/firefox/ffBrowser.ts
function toJugglerProxyOptions(proxy) {
const proxyServer = new URL(proxy.server);
let port = parseInt(proxyServer.port, 10);
let type3 = "http";
if (proxyServer.protocol === "socks5:")
type3 = "socks";
else if (proxyServer.protocol === "socks4:")
type3 = "socks4";
else if (proxyServer.protocol === "https:")
type3 = "https";
if (proxyServer.port === "") {
if (proxyServer.protocol === "http:")
port = 80;
else if (proxyServer.protocol === "https:")
port = 443;
}
return {
type: type3,
bypass: proxy.bypass ? proxy.bypass.split(",").map((domain) => domain.trim()) : [],
host: proxyServer.hostname,
port,
username: proxy.username,
password: proxy.password
};
}
var FFBrowser, FFBrowserContext, kBandaidFirefoxUserPrefs;
var init_ffBrowser = __esm({
"packages/playwright-core/src/server/firefox/ffBrowser.ts"() {
"use strict";
init_assert();
init_browser();
init_browserContext();
init_network2();
init_ffConnection();
init_ffPage();
init_page();
FFBrowser = class _FFBrowser extends Browser {
constructor(parent, connection, options2) {
super(parent, options2);
this._version = "";
this._userAgent = "";
this._connection = connection;
this.session = connection.rootSession;
this._ffPages = /* @__PURE__ */ new Map();
this._contexts = /* @__PURE__ */ new Map();
this._connection.on(ConnectionEvents2.Disconnected, () => this._onDisconnect());
this.session.on("Browser.attachedToTarget", this._onAttachedToTarget.bind(this));
this.session.on("Browser.detachedFromTarget", this._onDetachedFromTarget.bind(this));
this.session.on("Browser.downloadCreated", this._onDownloadCreated.bind(this));
this.session.on("Browser.downloadFinished", this._onDownloadFinished.bind(this));
}
static async connect(parent, transport, options2) {
const connection = new FFConnection(transport, options2.protocolLogger, options2.browserLogsCollector);
const browser = new _FFBrowser(parent, connection, options2);
if (options2.__testHookOnConnectToBrowser)
await options2.__testHookOnConnectToBrowser();
let firefoxUserPrefs = options2.originalLaunchOptions.firefoxUserPrefs ?? {};
if (Object.keys(kBandaidFirefoxUserPrefs).length)
firefoxUserPrefs = { ...kBandaidFirefoxUserPrefs, ...firefoxUserPrefs };
const promises = [
browser.session.send("Browser.enable", {
attachToDefaultContext: !!options2.persistent,
userPrefs: Object.entries(firefoxUserPrefs).map(([name, value2]) => ({ name, value: value2 }))
}),
browser._initVersion()
];
if (options2.persistent) {
browser._defaultContext = new FFBrowserContext(browser, void 0, options2.persistent);
promises.push(browser._defaultContext.initialize());
}
const proxy = options2.originalLaunchOptions.proxyOverride || options2.proxy;
if (proxy)
promises.push(browser.session.send("Browser.setBrowserProxy", toJugglerProxyOptions(proxy)));
await Promise.all(promises);
return browser;
}
async _initVersion() {
const result2 = await this.session.send("Browser.getInfo");
this._version = result2.version.substring(result2.version.indexOf("/") + 1);
this._userAgent = result2.userAgent;
}
isConnected() {
return !this._connection._closed;
}
async doCreateNewContext(options2) {
if (options2.isMobile)
throw new Error("options.isMobile is not supported in Firefox");
const { browserContextId } = await this.session.send("Browser.createBrowserContext", { removeOnDetach: true });
const context2 = new FFBrowserContext(this, browserContextId, options2);
await context2.initialize();
this._contexts.set(browserContextId, context2);
return context2;
}
contexts() {
return Array.from(this._contexts.values());
}
version() {
return this._version;
}
userAgent() {
return this._userAgent;
}
_onDetachedFromTarget(payload) {
const ffPage = this._ffPages.get(payload.targetId);
this._ffPages.delete(payload.targetId);
ffPage.didClose();
}
_onAttachedToTarget(payload) {
const { targetId, browserContextId, openerId, type: type3 } = payload.targetInfo;
assert(type3 === "page");
const context2 = browserContextId ? this._contexts.get(browserContextId) : this._defaultContext;
assert(context2, `Unknown context id:${browserContextId}, _defaultContext: ${this._defaultContext}`);
const session2 = this._connection.createSession(payload.sessionId);
const opener = openerId ? this._ffPages.get(openerId) : null;
const ffPage = new FFPage(session2, context2, opener);
this._ffPages.set(targetId, ffPage);
}
_onDownloadCreated(payload) {
const ffPage = this._ffPages.get(payload.pageTargetId);
if (!ffPage)
return;
ffPage._page.frameManager.frameAbortedNavigation(payload.frameId, "Download is starting");
let originPage = ffPage._page.initializedOrUndefined();
if (!originPage) {
ffPage._reportAsNew(new Error("Starting new page download"));
if (ffPage._opener)
originPage = ffPage._opener._page.initializedOrUndefined();
}
if (!originPage)
return;
this.downloadCreated(originPage, payload.uuid, payload.url, payload.suggestedFileName);
}
_onDownloadFinished(payload) {
const error = payload.canceled ? "canceled" : payload.error;
this.downloadFinished(payload.uuid, error);
}
_onDisconnect() {
for (const ffPage of this._ffPages.values())
ffPage.didClose();
this._ffPages.clear();
this.didClose();
}
};
FFBrowserContext = class extends BrowserContext {
constructor(browser, browserContextId, options2) {
super(browser, options2, browserContextId);
}
async initialize() {
assert(!this._ffPages().length);
const browserContextId = this._browserContextId;
const promises = [
super.initialize(),
this._updateInitScripts()
];
if (this._options.acceptDownloads !== "internal-browser-default") {
promises.push(this._browser.session.send("Browser.setDownloadOptions", {
browserContextId,
downloadOptions: {
behavior: this._options.acceptDownloads === "accept" ? "saveToDisk" : "cancel",
downloadsDir: this._browser.options.downloadsPath
}
}));
}
promises.push(this.doUpdateDefaultViewport());
if (this._options.hasTouch)
promises.push(this._browser.session.send("Browser.setTouchOverride", { browserContextId, hasTouch: true }));
if (this._options.userAgent)
promises.push(this._browser.session.send("Browser.setUserAgentOverride", { browserContextId, userAgent: this._options.userAgent }));
if (this._options.bypassCSP)
promises.push(this._browser.session.send("Browser.setBypassCSP", { browserContextId, bypassCSP: true }));
if (this._options.ignoreHTTPSErrors || this._options.internalIgnoreHTTPSErrors)
promises.push(this._browser.session.send("Browser.setIgnoreHTTPSErrors", { browserContextId, ignoreHTTPSErrors: true }));
if (this._options.javaScriptEnabled === false)
promises.push(this._browser.session.send("Browser.setJavaScriptDisabled", { browserContextId, javaScriptDisabled: true }));
if (this._options.locale)
promises.push(this._browser.session.send("Browser.setLocaleOverride", { browserContextId, locale: this._options.locale }));
if (this._options.timezoneId)
promises.push(this._browser.session.send("Browser.setTimezoneOverride", { browserContextId, timezoneId: this._options.timezoneId }));
if (this._options.extraHTTPHeaders || this._options.locale)
promises.push(this.doUpdateExtraHTTPHeaders());
if (this._options.httpCredentials)
promises.push(this.innerSetHTTPCredentials(this._options.httpCredentials));
if (this._options.geolocation)
promises.push(this.setGeolocation(this._options.geolocation));
if (this._options.offline)
promises.push(this.doUpdateOffline());
promises.push(this.doUpdateDefaultEmulatedMedia());
const proxy = this._options.proxyOverride || this._options.proxy;
if (proxy) {
promises.push(this._browser.session.send("Browser.setContextProxy", {
browserContextId: this._browserContextId,
...toJugglerProxyOptions(proxy)
}));
}
await Promise.all(promises);
}
_ffPages() {
return Array.from(this._browser._ffPages.values()).filter((ffPage) => ffPage._browserContext === this);
}
possiblyUninitializedPages() {
return this._ffPages().map((ffPage) => ffPage._page);
}
async doCreateNewPage() {
const { targetId } = await this._browser.session.send("Browser.newPage", {
browserContextId: this._browserContextId
}).catch((e) => {
if (e.message.includes("Failed to override timezone"))
throw new Error(`Invalid timezone ID: ${this._options.timezoneId}`);
throw e;
});
return this._browser._ffPages.get(targetId)._page;
}
async doGetCookies(urls) {
const { cookies } = await this._browser.session.send("Browser.getCookies", { browserContextId: this._browserContextId });
return filterCookies(cookies.map((c) => {
const { name, value: value2, domain, path: path59, expires, httpOnly, secure, sameSite } = c;
return {
name,
value: value2,
domain,
path: path59,
expires,
httpOnly,
secure,
sameSite
};
}), urls);
}
async addCookies(cookies) {
const cc = rewriteCookies(cookies).map((c) => {
const { name, value: value2, url: url2, domain, path: path59, expires, httpOnly, secure, sameSite } = c;
return {
name,
value: value2,
url: url2,
domain,
path: path59,
expires: expires === -1 ? void 0 : expires,
httpOnly,
secure,
sameSite
};
});
await this._browser.session.send("Browser.setCookies", { browserContextId: this._browserContextId, cookies: cc });
}
async doClearCookies() {
await this._browser.session.send("Browser.clearCookies", { browserContextId: this._browserContextId });
}
async doGrantPermissions(origin, permissions) {
const webPermissionToProtocol = /* @__PURE__ */ new Map([
["geolocation", "geo"],
["persistent-storage", "persistent-storage"],
["push", "push"],
["notifications", "desktop-notification"],
["screen-wake-lock", "screen-wake-lock"]
]);
const filtered = permissions.map((permission) => {
const protocolPermission = webPermissionToProtocol.get(permission);
if (!protocolPermission)
throw new Error("Unknown permission: " + permission);
return protocolPermission;
});
await this._browser.session.send("Browser.grantPermissions", { origin, browserContextId: this._browserContextId, permissions: filtered });
}
async doClearPermissions() {
await this._browser.session.send("Browser.resetPermissions", { browserContextId: this._browserContextId });
}
async setGeolocation(geolocation) {
verifyGeolocation(geolocation);
this._options.geolocation = geolocation;
await this._browser.session.send("Browser.setGeolocationOverride", { browserContextId: this._browserContextId, geolocation: geolocation || null });
}
async doUpdateExtraHTTPHeaders() {
let allHeaders = this._options.extraHTTPHeaders || [];
if (this._options.locale)
allHeaders = mergeHeaders([allHeaders, singleHeader("Accept-Language", this._options.locale)]);
await this._browser.session.send("Browser.setExtraHTTPHeaders", { browserContextId: this._browserContextId, headers: allHeaders });
}
async setUserAgent(userAgent) {
await this._browser.session.send("Browser.setUserAgentOverride", { browserContextId: this._browserContextId, userAgent: userAgent || null });
}
async doUpdateOffline() {
await this._browser.session.send("Browser.setOnlineOverride", { browserContextId: this._browserContextId, override: this._options.offline ? "offline" : "online" });
}
async doSetHTTPCredentials(httpCredentials) {
this._options.httpCredentials = httpCredentials;
let credentials = null;
if (httpCredentials) {
const { username, password, origin } = httpCredentials;
credentials = { username, password, origin };
}
await this._browser.session.send("Browser.setHTTPCredentials", { browserContextId: this._browserContextId, credentials });
}
async doAddInitScript(initScript) {
await this._updateInitScripts();
}
async doRemoveInitScripts(initScripts) {
await this._updateInitScripts();
}
async _updateInitScripts() {
const bindingScripts = [...this._pageBindings.values()].map((binding) => binding.initScript.source);
if (this.bindingsInitScript)
bindingScripts.unshift(this.bindingsInitScript.source);
const initScripts = this.initScripts.map((script) => script.source);
await this._browser.session.send("Browser.setInitScripts", { browserContextId: this._browserContextId, scripts: [...bindingScripts, ...initScripts].map((script) => ({ script })) });
}
async doUpdateRequestInterception() {
await Promise.all([
this._browser.session.send("Browser.setRequestInterception", { browserContextId: this._browserContextId, enabled: this.requestInterceptors.length > 0 }),
this._browser.session.send("Browser.setCacheDisabled", { browserContextId: this._browserContextId, cacheDisabled: this.requestInterceptors.length > 0 })
]);
}
async doUpdateDefaultViewport() {
if (!this._options.viewport)
return;
const viewport = {
viewportSize: { width: this._options.viewport.width, height: this._options.viewport.height },
deviceScaleFactor: this._options.deviceScaleFactor || 1
};
await this._browser.session.send("Browser.setDefaultViewport", { browserContextId: this._browserContextId, viewport });
}
async doUpdateDefaultEmulatedMedia() {
if (this._options.colorScheme !== "no-override") {
await this._browser.session.send("Browser.setColorScheme", {
browserContextId: this._browserContextId,
colorScheme: this._options.colorScheme !== void 0 ? this._options.colorScheme : "light"
});
}
if (this._options.reducedMotion !== "no-override") {
await this._browser.session.send("Browser.setReducedMotion", {
browserContextId: this._browserContextId,
reducedMotion: this._options.reducedMotion !== void 0 ? this._options.reducedMotion : "no-preference"
});
}
if (this._options.forcedColors !== "no-override") {
await this._browser.session.send("Browser.setForcedColors", {
browserContextId: this._browserContextId,
forcedColors: this._options.forcedColors !== void 0 ? this._options.forcedColors : "none"
});
}
if (this._options.contrast !== "no-override") {
await this._browser.session.send("Browser.setContrast", {
browserContextId: this._browserContextId,
contrast: this._options.contrast !== void 0 ? this._options.contrast : "no-preference"
});
}
}
async doExposePlaywrightBinding() {
this._browser.session.send("Browser.addBinding", { browserContextId: this._browserContextId, name: PageBinding.kBindingName, script: "" });
}
onClosePersistent() {
}
async clearCache() {
await this._browser.session.send("Browser.clearCache");
}
async doClose(reason) {
if (!this._browserContextId) {
return "close-browser";
} else {
await this._browser.session.send("Browser.removeBrowserContext", { browserContextId: this._browserContextId });
this._browser._contexts.delete(this._browserContextId);
}
}
async cancelDownload(uuid) {
await this._browser.session.send("Browser.cancelDownload", { uuid });
}
};
kBandaidFirefoxUserPrefs = {};
}
});
// packages/playwright-core/src/server/firefox/firefox.ts
var import_os15, import_path29, Firefox;
var init_firefox = __esm({
"packages/playwright-core/src/server/firefox/firefox.ts"() {
"use strict";
import_os15 = __toESM(require("os"));
import_path29 = __toESM(require("path"));
init_manualPromise();
init_ascii();
init_ffBrowser();
init_ffConnection();
init_browserType();
Firefox = class extends BrowserType {
constructor(parent, bidiFirefox) {
super(parent, "firefox");
this._bidiFirefox = bidiFirefox;
}
launch(progress2, options2, protocolLogger) {
if (options2.channel?.startsWith("moz-"))
return this._bidiFirefox.launch(progress2, options2, protocolLogger);
return super.launch(progress2, options2, protocolLogger);
}
async launchPersistentContext(progress2, userDataDir, options2) {
if (options2.channel?.startsWith("moz-"))
return this._bidiFirefox.launchPersistentContext(progress2, userDataDir, options2);
return super.launchPersistentContext(progress2, userDataDir, options2);
}
connectToTransport(transport, options2) {
return FFBrowser.connect(this.attribution.playwright, transport, options2);
}
doRewriteStartupLog(logs) {
if (logs.includes(`as root in a regular user's session is not supported.`))
logs = "\n" + wrapInASCIIBox(`Firefox is unable to launch if the $HOME folder isn't owned by the current user.
Workaround: Set the HOME=/root environment variable${process.env.GITHUB_ACTION ? " in your GitHub Actions workflow file" : ""} when running Playwright.`, 1);
if (logs.includes("no DISPLAY environment variable specified"))
logs = "\n" + wrapInASCIIBox(kNoXServerRunningError, 1);
return logs;
}
amendEnvironment(env) {
if (!import_path29.default.isAbsolute(import_os15.default.homedir()))
throw new Error(`Cannot launch Firefox with relative home directory. Did you set ${import_os15.default.platform() === "win32" ? "USERPROFILE" : "HOME"} to a relative path?`);
if (import_os15.default.platform() === "linux") {
return { ...env, SNAP_NAME: void 0, SNAP_INSTANCE_NAME: void 0 };
}
return env;
}
attemptToGracefullyCloseBrowser(transport) {
const message = { method: "Browser.close", params: {}, id: kBrowserCloseMessageId3 };
transport.send(message);
}
async defaultArgs(options2, isPersistent, userDataDir) {
const { args = [], headless } = options2;
const userDataDirArg = args.find((arg) => arg.startsWith("-profile") || arg.startsWith("--profile"));
if (userDataDirArg)
throw this._createUserDataDirArgMisuseError("--profile");
if (args.find((arg) => arg.startsWith("-juggler")))
throw new Error("Use the port parameter instead of -juggler argument");
const firefoxArguments = ["-no-remote"];
if (headless) {
firefoxArguments.push("-headless");
} else {
firefoxArguments.push("-wait-for-browser");
firefoxArguments.push("-foreground");
}
firefoxArguments.push(`-profile`, userDataDir);
firefoxArguments.push("-juggler-pipe");
firefoxArguments.push(...args);
if (isPersistent)
firefoxArguments.push("about:blank");
else
firefoxArguments.push("-silent");
return firefoxArguments;
}
waitForReadyState(options2, browserLogsCollector) {
const result2 = new ManualPromise();
browserLogsCollector.onMessage((message) => {
if (message.includes("Juggler listening to the pipe"))
result2.resolve({});
});
return result2;
}
};
}
});
// packages/playwright-core/src/server/webkit/wkConnection.ts
var import_events11, kBrowserCloseMessageId4, kPageProxyMessageReceived, WKConnection, WKSession;
var init_wkConnection = __esm({
"packages/playwright-core/src/server/webkit/wkConnection.ts"() {
"use strict";
import_events11 = require("events");
init_debugLogger();
init_assert();
init_helper();
init_protocolError();
kBrowserCloseMessageId4 = -9999;
kPageProxyMessageReceived = Symbol("kPageProxyMessageReceived");
WKConnection = class {
constructor(transport, onDisconnect, protocolLogger, browserLogsCollector) {
this._lastId = 0;
this._closed = false;
this._transport = transport;
this._onDisconnect = onDisconnect;
this._protocolLogger = protocolLogger;
this._browserLogsCollector = browserLogsCollector;
this.browserSession = new WKSession(this, "", (message) => {
this.rawSend(message);
});
this._transport.onmessage = this._dispatchMessage.bind(this);
this._transport.onclose = this._onClose.bind(this);
}
nextMessageId() {
return ++this._lastId;
}
rawSend(message) {
this._protocolLogger("send", message);
this._transport.send(message);
}
_dispatchMessage(message) {
this._protocolLogger("receive", message);
if (message.id === kBrowserCloseMessageId4)
return;
if (message.pageProxyId) {
const payload = { message, pageProxyId: message.pageProxyId };
this.browserSession.dispatchMessage({ method: kPageProxyMessageReceived, params: payload });
return;
}
this.browserSession.dispatchMessage(message);
}
_onClose(reason) {
this._closed = true;
this._transport.onmessage = void 0;
this._transport.onclose = void 0;
this._browserDisconnectedLogs = helper.formatBrowserLogs(this._browserLogsCollector.recentLogs(), reason);
this.browserSession.dispose();
this._onDisconnect();
}
isClosed() {
return this._closed;
}
close() {
if (!this._closed)
this._transport.close();
}
};
WKSession = class extends import_events11.EventEmitter {
constructor(connection, sessionId, rawSend) {
super();
this._disposed = false;
this._callbacks = /* @__PURE__ */ new Map();
this._crashed = false;
this.setMaxListeners(0);
this.connection = connection;
this.sessionId = sessionId;
this._rawSend = rawSend;
}
async send(method, params2) {
if (this._crashed || this._disposed || this.connection._browserDisconnectedLogs)
throw new ProtocolError(this._crashed ? "crashed" : "closed", void 0, this.connection._browserDisconnectedLogs);
const id = this.connection.nextMessageId();
const messageObj = { id, method, params: params2 };
this._rawSend(messageObj);
return new Promise((resolve, reject) => {
this._callbacks.set(id, { resolve, reject, error: new ProtocolError("error", method) });
});
}
sendMayFail(method, params2) {
return this.send(method, params2).catch((error) => debugLogger.log("error", error));
}
markAsCrashed() {
this._crashed = true;
}
isDisposed() {
return this._disposed;
}
dispose() {
for (const callback of this._callbacks.values()) {
callback.error.type = this._crashed ? "crashed" : "closed";
callback.error.logs = this.connection._browserDisconnectedLogs;
callback.reject(callback.error);
}
this._callbacks.clear();
this._disposed = true;
}
dispatchMessage(object) {
if (object.id && this._callbacks.has(object.id)) {
const callback = this._callbacks.get(object.id);
this._callbacks.delete(object.id);
if (object.error) {
callback.error.setMessage(object.error.message);
callback.reject(callback.error);
} else {
callback.resolve(object.result);
}
} else if (object.id && !object.error) {
assert(this.isDisposed(), JSON.stringify(object));
} else {
Promise.resolve().then(() => this.emit(object.method, object.params));
}
}
};
}
});
// packages/playwright-core/src/server/webkit/wkExecutionContext.ts
function potentiallyUnserializableValue3(remoteObject) {
const value2 = remoteObject.value;
const isUnserializable = remoteObject.type === "number" && ["NaN", "-Infinity", "Infinity", "-0"].includes(remoteObject.description);
return isUnserializable ? parseUnserializableValue(remoteObject.description) : value2;
}
function rewriteError4(error) {
if (error.message.includes("Object has too long reference chain"))
throw new Error("Cannot serialize result: object reference chain is too long.");
if (!isJavaScriptErrorInEvaluate(error) && !isSessionClosedError(error))
return new Error("Execution context was destroyed, most likely because of a navigation.");
return error;
}
function renderPreview4(object) {
if (object.type === "undefined")
return "undefined";
if ("value" in object)
return String(object.value);
if (object.description === "Object" && object.preview) {
const tokens = [];
for (const { name, value: value2 } of object.preview.properties)
tokens.push(`${name}: ${value2}`);
return `{${tokens.join(", ")}}`;
}
if (object.subtype === "array" && object.preview)
return sparseArrayToString(object.preview.properties);
return object.description;
}
function createHandle4(context2, remoteObject) {
if (remoteObject.subtype === "node") {
assert(context2 instanceof FrameExecutionContext);
return new ElementHandle(context2, remoteObject.objectId);
}
const isPromise = remoteObject.className === "Promise";
return new JSHandle(context2, isPromise ? "promise" : remoteObject.subtype || remoteObject.type, renderPreview4(remoteObject), remoteObject.objectId, potentiallyUnserializableValue3(remoteObject));
}
var WKExecutionContext;
var init_wkExecutionContext = __esm({
"packages/playwright-core/src/server/webkit/wkExecutionContext.ts"() {
"use strict";
init_assert();
init_utilityScriptSerializers();
init_javascript();
init_dom();
init_protocolError();
WKExecutionContext = class {
constructor(session2, contextId4) {
this._session = session2;
this._contextId = contextId4;
}
async rawEvaluateJSON(expression2) {
try {
const response2 = await this._session.send("Runtime.evaluate", {
expression: expression2,
contextId: this._contextId,
returnByValue: true
});
if (response2.wasThrown)
throw new JavaScriptErrorInEvaluate(response2.result.description);
return response2.result.value;
} catch (error) {
throw rewriteError4(error);
}
}
async rawEvaluateHandle(context2, expression2) {
try {
const response2 = await this._session.send("Runtime.evaluate", {
expression: expression2,
contextId: this._contextId,
returnByValue: false
});
if (response2.wasThrown)
throw new JavaScriptErrorInEvaluate(response2.result.description);
return createHandle4(context2, response2.result);
} catch (error) {
throw rewriteError4(error);
}
}
async evaluateWithArguments(expression2, returnByValue, utilityScript, values, handles) {
try {
const response2 = await this._session.send("Runtime.callFunctionOn", {
functionDeclaration: expression2,
objectId: utilityScript._objectId,
arguments: [
{ objectId: utilityScript._objectId },
...values.map((value2) => ({ value: value2 })),
...handles.map((handle) => ({ objectId: handle._objectId }))
],
returnByValue,
emulateUserGesture: true,
awaitPromise: true
});
if (response2.wasThrown)
throw new JavaScriptErrorInEvaluate(response2.result.description);
if (returnByValue)
return parseEvaluationResultValue(response2.result.value);
return createHandle4(utilityScript._context, response2.result);
} catch (error) {
throw rewriteError4(error);
}
}
async getProperties(object) {
const response2 = await this._session.send("Runtime.getProperties", {
objectId: object._objectId,
ownProperties: true
});
const result2 = /* @__PURE__ */ new Map();
for (const property of response2.properties) {
if (!property.enumerable || !property.value)
continue;
result2.set(property.name, createHandle4(object._context, property.value));
}
return result2;
}
async releaseHandle(handle) {
if (!handle._objectId)
return;
await this._session.send("Runtime.releaseObject", { objectId: handle._objectId });
}
shouldPrependErrorPrefix() {
return false;
}
};
}
});
// packages/playwright-core/src/server/webkit/wkInput.ts
function toModifiersMask3(modifiers) {
let mask = 0;
if (modifiers.has("Shift"))
mask |= 1;
if (modifiers.has("Control"))
mask |= 2;
if (modifiers.has("Alt"))
mask |= 4;
if (modifiers.has("Meta"))
mask |= 8;
return mask;
}
function toButtonsMask3(buttons) {
let mask = 0;
if (buttons.has("left"))
mask |= 1;
if (buttons.has("right"))
mask |= 2;
if (buttons.has("middle"))
mask |= 4;
return mask;
}
var RawKeyboardImpl4, RawMouseImpl4, RawTouchscreenImpl4;
var init_wkInput = __esm({
"packages/playwright-core/src/server/webkit/wkInput.ts"() {
"use strict";
init_stringUtils();
init_input();
init_macEditingCommands();
RawKeyboardImpl4 = class {
constructor(session2) {
this._pageProxySession = session2;
}
setSession(session2) {
this._session = session2;
}
async keydown(progress2, modifiers, keyName, description, autoRepeat) {
const parts = [];
for (const modifier of ["Shift", "Control", "Alt", "Meta"]) {
if (modifiers.has(modifier))
parts.push(modifier);
}
const { code, keyCode, key, text: text2 } = description;
parts.push(code);
const shortcut = parts.join("+");
let commands2 = macEditingCommands[shortcut];
if (isString(commands2))
commands2 = [commands2];
await progress2.race(this._pageProxySession.send("Input.dispatchKeyEvent", {
type: "keyDown",
modifiers: toModifiersMask3(modifiers),
windowsVirtualKeyCode: keyCode,
code,
key,
text: text2,
unmodifiedText: text2,
autoRepeat,
macCommands: commands2,
isKeypad: description.location === keypadLocation2
}));
}
async keyup(progress2, modifiers, keyName, description) {
const { code, key } = description;
await progress2.race(this._pageProxySession.send("Input.dispatchKeyEvent", {
type: "keyUp",
modifiers: toModifiersMask3(modifiers),
key,
windowsVirtualKeyCode: description.keyCode,
code,
isKeypad: description.location === keypadLocation2
}));
}
async sendText(progress2, text2) {
await progress2.race(this._session.send("Page.insertText", { text: text2 }));
}
};
RawMouseImpl4 = class {
constructor(session2) {
this._pageProxySession = session2;
}
setSession(session2) {
this._session = session2;
}
async move(progress2, x, y, button, buttons, modifiers, forClick) {
await progress2.race(this._pageProxySession.send("Input.dispatchMouseEvent", {
type: "move",
button,
buttons: toButtonsMask3(buttons),
x,
y,
modifiers: toModifiersMask3(modifiers)
}));
}
async down(progress2, x, y, button, buttons, modifiers, clickCount) {
await progress2.race(this._pageProxySession.send("Input.dispatchMouseEvent", {
type: "down",
button,
buttons: toButtonsMask3(buttons),
x,
y,
modifiers: toModifiersMask3(modifiers),
clickCount
}));
}
async up(progress2, x, y, button, buttons, modifiers, clickCount) {
await progress2.race(this._pageProxySession.send("Input.dispatchMouseEvent", {
type: "up",
button,
buttons: toButtonsMask3(buttons),
x,
y,
modifiers: toModifiersMask3(modifiers),
clickCount
}));
}
async wheel(progress2, x, y, buttons, modifiers, deltaX, deltaY) {
if (this._page?.browserContext._options.isMobile)
throw new Error("Mouse wheel is not supported in mobile WebKit");
await progress2.race(this._session.send("Page.updateScrollingState"));
await this._page.mainFrame().evaluateExpression(progress2, `new Promise(requestAnimationFrame)`, { world: "utility" });
await progress2.race(this._pageProxySession.send("Input.dispatchWheelEvent", {
x,
y,
deltaX,
deltaY,
modifiers: toModifiersMask3(modifiers)
}));
}
setPage(page) {
this._page = page;
}
};
RawTouchscreenImpl4 = class {
constructor(session2) {
this._pageProxySession = session2;
}
async tap(progress2, x, y, modifiers) {
await progress2.race(this._pageProxySession.send("Input.dispatchTapEvent", {
x,
y,
modifiers: toModifiersMask3(modifiers)
}));
}
};
}
});
// packages/playwright-core/src/server/webkit/wkInterceptableRequest.ts
function wkMillisToRoundishMillis(value2) {
if (value2 === -1e3)
return -1;
if (value2 <= 0) {
return -1;
}
return (value2 * 1e3 | 0) / 1e3;
}
function toResourceType2(type3) {
switch (type3) {
case "Document":
return "document";
case "StyleSheet":
return "stylesheet";
case "Image":
return "image";
case "Font":
return "font";
case "Script":
return "script";
case "XHR":
return "xhr";
case "Fetch":
return "fetch";
case "Ping":
return "ping";
case "Beacon":
return "beacon";
case "WebSocket":
return "websocket";
case "EventSource":
return "eventsource";
default:
return "other";
}
}
var errorReasons2, WKInterceptableRequest, WKRouteImpl;
var init_wkInterceptableRequest = __esm({
"packages/playwright-core/src/server/webkit/wkInterceptableRequest.ts"() {
"use strict";
init_assert();
init_headers();
init_network2();
errorReasons2 = {
"aborted": "Cancellation",
"accessdenied": "AccessControl",
"addressunreachable": "General",
"blockedbyclient": "Cancellation",
"blockedbyresponse": "General",
"connectionaborted": "General",
"connectionclosed": "General",
"connectionfailed": "General",
"connectionrefused": "General",
"connectionreset": "General",
"internetdisconnected": "General",
"namenotresolved": "General",
"timedout": "Timeout",
"failed": "General"
};
WKInterceptableRequest = class {
constructor(session2, frame, event, redirectedFrom, documentId) {
this._session = session2;
this._requestId = event.requestId;
const resourceType = event.type ? toResourceType2(event.type) : redirectedFrom ? redirectedFrom.request.resourceType() : "other";
let postDataBuffer = null;
this._timestamp = event.timestamp;
this._wallTime = event.walltime * 1e3;
if (event.request.postData)
postDataBuffer = Buffer.from(event.request.postData, "base64");
this.request = new Request(
frame._page.browserContext,
frame,
null,
redirectedFrom?.request || null,
documentId,
event.request.url,
resourceType,
event.request.method,
postDataBuffer,
headersObjectToArray(event.request.headers)
);
}
adoptRequestFromNewProcess(newSession, requestId) {
this._session = newSession;
this._requestId = requestId;
}
createResponse(responsePayload) {
const getResponseBody = async () => {
const response3 = await this._session.send("Network.getResponseBody", { requestId: this._requestId });
return Buffer.from(response3.body, response3.base64Encoded ? "base64" : "utf8");
};
const timingPayload = responsePayload.timing;
const timing = {
startTime: this._wallTime,
domainLookupStart: timingPayload ? wkMillisToRoundishMillis(timingPayload.domainLookupStart) : -1,
domainLookupEnd: timingPayload ? wkMillisToRoundishMillis(timingPayload.domainLookupEnd) : -1,
connectStart: timingPayload ? wkMillisToRoundishMillis(timingPayload.connectStart) : -1,
secureConnectionStart: timingPayload ? wkMillisToRoundishMillis(timingPayload.secureConnectionStart) : -1,
connectEnd: timingPayload ? wkMillisToRoundishMillis(timingPayload.connectEnd) : -1,
requestStart: timingPayload ? wkMillisToRoundishMillis(timingPayload.requestStart) : -1,
responseStart: timingPayload ? wkMillisToRoundishMillis(timingPayload.responseStart) : -1
};
const setCookieSeparator = process.platform === "darwin" ? "," : "playwright-set-cookie-separator";
const response2 = new Response2(this.request, responsePayload.status, responsePayload.statusText, headersObjectToArray(responsePayload.headers, ",", setCookieSeparator), timing, getResponseBody, responsePayload.source === "service-worker");
response2.setRawResponseHeaders(null);
response2.setTransferSize(null);
if (responsePayload.requestHeaders && Object.keys(responsePayload.requestHeaders).length) {
const headers = { ...responsePayload.requestHeaders };
if (!headers["host"])
headers["Host"] = new URL(this.request.url()).host;
this.request.setRawRequestHeaders(headersObjectToArray(headers));
} else {
this.request.setRawRequestHeaders(null);
}
return response2;
}
};
WKRouteImpl = class {
constructor(session2, requestId) {
this._session = session2;
this._requestId = requestId;
}
async abort(errorCode) {
const errorType = errorReasons2[errorCode];
assert(errorType, "Unknown error code: " + errorCode);
await this._session.sendMayFail("Network.interceptRequestWithError", { requestId: this._requestId, errorType });
}
async fulfill(response2) {
if (300 <= response2.status && response2.status < 400)
throw new Error("Cannot fulfill with redirect status: " + response2.status);
let mimeType = response2.isBase64 ? "application/octet-stream" : "text/plain";
const headers = headersArrayToObject(
response2.headers,
true
/* lowerCase */
);
const contentType = headers["content-type"];
if (contentType)
mimeType = contentType.split(";")[0].trim();
await this._session.sendMayFail("Network.interceptRequestWithResponse", {
requestId: this._requestId,
status: response2.status,
statusText: statusText(response2.status),
mimeType,
headers,
base64Encoded: response2.isBase64,
content: response2.body
});
}
async continue(overrides) {
await this._session.sendMayFail("Network.interceptWithRequest", {
requestId: this._requestId,
url: overrides.url,
method: overrides.method,
headers: overrides.headers ? headersArrayToObject(
overrides.headers,
false
/* lowerCase */
) : void 0,
postData: overrides.postData ? Buffer.from(overrides.postData).toString("base64") : void 0
});
}
};
}
});
// packages/playwright-core/src/server/webkit/wkProvisionalPage.ts
var WKProvisionalPage;
var init_wkProvisionalPage = __esm({
"packages/playwright-core/src/server/webkit/wkProvisionalPage.ts"() {
"use strict";
init_eventsHelper();
init_assert();
WKProvisionalPage = class {
constructor(session2, page) {
this._sessionListeners = [];
this._mainFrameId = null;
this._session = session2;
this._wkPage = page;
this._coopNavigationRequest = page._page.mainFrame().pendingDocument()?.request;
const overrideFrameId = (handler) => {
return (payload) => {
if (payload.frameId)
payload.frameId = this._wkPage._page.frameManager.mainFrame()._id;
handler(payload);
};
};
const wkPage = this._wkPage;
this._sessionListeners = [
eventsHelper.addEventListener(session2, "Network.requestWillBeSent", overrideFrameId((e) => this._onRequestWillBeSent(e))),
eventsHelper.addEventListener(session2, "Network.requestIntercepted", overrideFrameId((e) => wkPage._onRequestIntercepted(session2, e))),
eventsHelper.addEventListener(session2, "Network.responseReceived", overrideFrameId((e) => wkPage._onResponseReceived(session2, e))),
eventsHelper.addEventListener(session2, "Network.loadingFinished", overrideFrameId((e) => this._onLoadingFinished(e))),
eventsHelper.addEventListener(session2, "Network.loadingFailed", overrideFrameId((e) => this._onLoadingFailed(e)))
];
this.initializationPromise = this._wkPage._initializeSession(session2, true, ({ frameTree }) => this._handleFrameTree(frameTree));
}
coopNavigationRequest() {
return this._coopNavigationRequest;
}
dispose() {
eventsHelper.removeEventListeners(this._sessionListeners);
}
commit() {
assert(this._mainFrameId);
this._wkPage._onFrameAttached(this._mainFrameId, null);
}
_onRequestWillBeSent(event) {
if (this._coopNavigationRequest && this._coopNavigationRequest.url() === event.request.url) {
this._wkPage._adoptRequestFromNewProcess(this._coopNavigationRequest, this._session, event.requestId);
return;
}
this._wkPage._onRequestWillBeSent(this._session, event);
}
_onLoadingFinished(event) {
this._coopNavigationRequest = void 0;
this._wkPage._onLoadingFinished(event);
}
_onLoadingFailed(event) {
this._coopNavigationRequest = void 0;
this._wkPage._onLoadingFailed(this._session, event);
}
_handleFrameTree(frameTree) {
assert(!frameTree.frame.parentId);
this._mainFrameId = frameTree.frame.id;
}
};
}
});
// packages/playwright-core/src/server/webkit/wkWorkers.ts
var WKWorkers;
var init_wkWorkers = __esm({
"packages/playwright-core/src/server/webkit/wkWorkers.ts"() {
"use strict";
init_eventsHelper();
init_page();
init_wkConnection();
init_wkExecutionContext();
WKWorkers = class {
constructor(page) {
this._sessionListeners = [];
this._workerSessions = /* @__PURE__ */ new Map();
this._page = page;
}
setSession(session2) {
eventsHelper.removeEventListeners(this._sessionListeners);
this.clear();
this._sessionListeners = [
eventsHelper.addEventListener(session2, "Worker.workerCreated", (event) => {
const worker = new Worker(this._page, event.url);
const workerSession = new WKSession(session2.connection, event.workerId, (message) => {
session2.send("Worker.sendMessageToWorker", {
workerId: event.workerId,
message: JSON.stringify(message)
}).catch((e) => {
workerSession.dispatchMessage({ id: message.id, error: { message: e.message } });
});
});
this._workerSessions.set(event.workerId, workerSession);
worker.createExecutionContext(new WKExecutionContext(workerSession, void 0));
worker.workerScriptLoaded();
this._page.addWorker(event.workerId, worker);
workerSession.on("Console.messageAdded", (event2) => this._onConsoleMessage(worker, event2));
Promise.all([
workerSession.send("Runtime.enable"),
workerSession.send("Console.enable"),
session2.send("Worker.initialized", { workerId: event.workerId })
]).catch((e) => {
this._page.removeWorker(event.workerId);
});
}),
eventsHelper.addEventListener(session2, "Worker.dispatchMessageFromWorker", (event) => {
const workerSession = this._workerSessions.get(event.workerId);
if (!workerSession)
return;
workerSession.dispatchMessage(JSON.parse(event.message));
}),
eventsHelper.addEventListener(session2, "Worker.workerTerminated", (event) => {
const workerSession = this._workerSessions.get(event.workerId);
if (!workerSession)
return;
workerSession.dispose();
this._workerSessions.delete(event.workerId);
this._page.removeWorker(event.workerId);
})
];
}
clear() {
this._page.clearWorkers();
this._workerSessions.clear();
}
async initializeSession(session2) {
await session2.send("Worker.enable");
}
async _onConsoleMessage(worker, event) {
const { type: type3, level, text: text2, parameters, url: url2, line: lineNumber, column: columnNumber } = event.message;
let derivedType = type3 || "";
if (type3 === "log")
derivedType = level;
else if (type3 === "timing")
derivedType = "timeEnd";
const handles = (parameters || []).map((p) => {
return createHandle4(worker.existingExecutionContext, p);
});
const location2 = {
url: url2 || "",
lineNumber: (lineNumber || 1) - 1,
columnNumber: (columnNumber || 1) - 1
};
const timestamp = event.message.timestamp ? event.message.timestamp * 1e3 : Date.now();
this._page.addConsoleMessage(worker, derivedType, handles, location2, handles.length ? void 0 : text2, timestamp);
}
};
}
});
// packages/playwright-core/src/server/webkit/wkPage.ts
function parseRemoteAddress(value2) {
if (!value2)
return;
try {
const colon = value2.lastIndexOf(":");
const dot = value2.lastIndexOf(".");
if (dot < 0) {
return {
ipAddress: `[${value2.slice(0, colon)}]`,
port: +value2.slice(colon + 1)
};
}
if (colon > dot) {
const [address, port] = value2.split(":");
return {
ipAddress: address,
port: +port
};
} else {
const [address, port] = value2.split(".");
return {
ipAddress: `[${address}]`,
port: +port
};
}
} catch (_) {
}
}
function isLoadedSecurely(url2, timing) {
try {
const u = new URL(url2);
if (u.protocol !== "https:" && u.protocol !== "wss:" && u.protocol !== "sftp:")
return false;
if (timing.secureConnectionStart === -1 && timing.connectStart !== -1)
return false;
return true;
} catch (_) {
}
}
var PNG2, jpegjs3, UTILITY_WORLD_NAME3, enableFrameSessions, WKPage, WKFrame;
var init_wkPage = __esm({
"packages/playwright-core/src/server/webkit/wkPage.ts"() {
"use strict";
init_headers();
init_stackTrace();
init_eventsHelper();
init_hostPlatform();
init_assert();
init_dialog();
init_dom();
init_errors();
init_helper();
init_network2();
init_page();
init_wkConnection();
init_wkExecutionContext();
init_wkInput();
init_wkInterceptableRequest();
init_wkProvisionalPage();
init_wkWorkers();
init_webkit();
init_registry();
init_videoRecorder();
init_progress();
({ PNG: PNG2 } = require("./utilsBundle"));
jpegjs3 = require("./utilsBundle").jpegjs;
UTILITY_WORLD_NAME3 = "__playwright_utility_world__";
enableFrameSessions = !process.env.WK_DISABLE_FRAME_SESSIONS && parseInt(registry.findExecutable("webkit").revision, 10) >= 2245 && parseInt(registry.findExecutable("webkit").revision, 10) <= 2255;
WKPage = class _WKPage {
constructor(browserContext, pageProxySession, opener) {
this._provisionalPage = null;
this._targetIdToFrameSession = /* @__PURE__ */ new Map();
this._requestIdToRequest = /* @__PURE__ */ new Map();
this._requestIdToRequestWillBeSentEvent = /* @__PURE__ */ new Map();
this._sessionListeners = [];
this._firstNonInitialNavigationCommittedFulfill = () => {
};
this._firstNonInitialNavigationCommittedReject = (e) => {
};
this._lastConsoleMessage = null;
this._requestIdToResponseReceivedPayloadEvent = /* @__PURE__ */ new Map();
this._screencastGeneration = 0;
this._pageProxySession = pageProxySession;
this._opener = opener;
this.rawKeyboard = new RawKeyboardImpl4(pageProxySession);
this.rawMouse = new RawMouseImpl4(pageProxySession);
this.rawTouchscreen = new RawTouchscreenImpl4(pageProxySession);
this._contextIdToContext = /* @__PURE__ */ new Map();
this._page = new Page(this, browserContext);
this.rawMouse.setPage(this._page);
this._workers = new WKWorkers(this._page);
this._session = void 0;
this._browserContext = browserContext;
this._page.on(Page.Events.FrameDetached, (frame) => this._removeContextsForFrame(frame, false));
this._eventListeners = [
eventsHelper.addEventListener(this._pageProxySession, "Target.targetCreated", this._onTargetCreated.bind(this)),
eventsHelper.addEventListener(this._pageProxySession, "Target.targetDestroyed", this._onTargetDestroyed.bind(this)),
eventsHelper.addEventListener(this._pageProxySession, "Target.dispatchMessageFromTarget", this._onDispatchMessageFromTarget.bind(this)),
eventsHelper.addEventListener(this._pageProxySession, "Target.didCommitProvisionalTarget", this._onDidCommitProvisionalTarget.bind(this)),
eventsHelper.addEventListener(this._pageProxySession, "Screencast.screencastFrame", this._onScreencastFrame.bind(this))
];
this._firstNonInitialNavigationCommittedPromise = new Promise((f, r) => {
this._firstNonInitialNavigationCommittedFulfill = f;
this._firstNonInitialNavigationCommittedReject = r;
});
this._firstNonInitialNavigationCommittedPromise.catch(() => {
});
if (opener && !browserContext._options.noDefaultViewport && opener._nextWindowOpenPopupFeatures) {
const viewportSize = helper.getViewportSizeFromWindowFeatures(opener._nextWindowOpenPopupFeatures);
opener._nextWindowOpenPopupFeatures = void 0;
if (viewportSize)
this._page.setEmulatedSizeFromWindowOpen({ viewport: viewportSize, screen: viewportSize });
}
}
async _initializePageProxySession() {
if (this._page.isStorageStatePage)
return;
const promises = [
this._pageProxySession.send("Dialog.enable"),
this._pageProxySession.send("Emulation.setActiveAndFocused", { active: true })
];
const contextOptions = this._browserContext._options;
if (contextOptions.javaScriptEnabled === false)
promises.push(this._pageProxySession.send("Emulation.setJavaScriptEnabled", { enabled: false }));
promises.push(this._updateViewport());
promises.push(this.updateHttpCredentials());
if (this._browserContext._permissions.size) {
for (const [key, value2] of this._browserContext._permissions)
promises.push(this._grantPermissions(key, value2));
}
startAutomaticVideoRecording(this._page);
await Promise.all(promises);
}
_setSession(session2) {
eventsHelper.removeEventListeners(this._sessionListeners);
this._session = session2;
this.rawKeyboard.setSession(session2);
this.rawMouse.setSession(session2);
this._addSessionListeners();
this._workers.setSession(session2);
}
// This method is called for provisional targets as well. The session passed as the parameter
// may be different from the current session and may be destroyed without becoming current.
async _initializeSession(session2, provisional, resourceTreeHandler) {
await this._initializeSessionMayThrow(session2, resourceTreeHandler).catch((e) => {
if (provisional && session2.isDisposed())
return;
if (this._session === session2)
throw e;
});
}
async _initializeSessionMayThrow(session2, resourceTreeHandler) {
const [, frameTree] = await Promise.all([
// Page agent must be enabled before Runtime.
session2.send("Page.enable"),
session2.send("Page.getResourceTree")
]);
resourceTreeHandler(frameTree);
const promises = [
// Resource tree should be received before first execution context.
session2.send("Runtime.enable"),
session2.send("Page.createUserWorld", { name: UTILITY_WORLD_NAME3 }).catch((_) => {
}),
// Worlds are per-process
session2.send("Network.enable"),
this._workers.initializeSession(session2)
];
if (enableFrameSessions)
this._initializeFrameSessions(frameTree.frameTree, promises);
else
promises.push(session2.send("Console.enable"));
if (this._page.browserContext.needsPlaywrightBinding())
promises.push(session2.send("Runtime.addBinding", { name: PageBinding.kBindingName }));
if (this._page.needsRequestInterception()) {
promises.push(session2.send("Network.setInterceptionEnabled", { enabled: true }));
promises.push(session2.send("Network.setResourceCachingDisabled", { disabled: true }));
promises.push(session2.send("Network.addInterception", { url: ".*", stage: "request", isRegex: true }));
}
if (this._page.isStorageStatePage) {
await Promise.all(promises);
return;
}
const contextOptions = this._browserContext._options;
if (contextOptions.userAgent)
promises.push(this.updateUserAgent());
const emulatedMedia = this._page.emulatedMedia();
if (emulatedMedia.media || emulatedMedia.colorScheme || emulatedMedia.reducedMotion || emulatedMedia.forcedColors || emulatedMedia.contrast)
promises.push(_WKPage._setEmulateMedia(session2, emulatedMedia.media, emulatedMedia.colorScheme, emulatedMedia.reducedMotion, emulatedMedia.forcedColors, emulatedMedia.contrast));
const bootstrapScript = this._calculateBootstrapScript();
if (bootstrapScript.length)
promises.push(session2.send("Page.setBootstrapScript", { source: bootstrapScript }));
this._page.frames().map((frame) => frame.evaluateExpression(nullProgress, bootstrapScript).catch((e) => {
}));
if (contextOptions.bypassCSP)
promises.push(session2.send("Page.setBypassCSP", { enabled: true }));
const emulatedSize = this._page.emulatedSize();
if (emulatedSize) {
promises.push(session2.send("Page.setScreenSizeOverride", {
width: emulatedSize.screen.width,
height: emulatedSize.screen.height
}));
}
promises.push(this.updateEmulateMedia());
promises.push(session2.send("Network.setExtraHTTPHeaders", { headers: headersArrayToObject(
this._calculateExtraHTTPHeaders(),
false
/* lowerCase */
) }));
if (contextOptions.offline)
promises.push(session2.send("Network.setEmulateOfflineState", { offline: true }));
promises.push(session2.send("Page.setTouchEmulationEnabled", { enabled: !!contextOptions.hasTouch }));
if (contextOptions.timezoneId) {
promises.push(session2.send("Page.setTimeZone", { timeZone: contextOptions.timezoneId }).catch((e) => {
throw new Error(`Invalid timezone ID: ${contextOptions.timezoneId}`);
}));
}
if (this._page.fileChooserIntercepted())
promises.push(session2.send("Page.setInterceptFileChooserDialog", { enabled: true }));
promises.push(session2.send("Page.overrideSetting", { setting: "DeviceOrientationEventEnabled", value: contextOptions.isMobile }));
promises.push(session2.send("Page.overrideSetting", { setting: "FullScreenEnabled", value: !contextOptions.isMobile }));
promises.push(session2.send("Page.overrideSetting", { setting: "NotificationsEnabled", value: !contextOptions.isMobile }));
promises.push(session2.send("Page.overrideSetting", { setting: "PointerLockEnabled", value: !contextOptions.isMobile }));
promises.push(session2.send("Page.overrideSetting", { setting: "InputTypeMonthEnabled", value: contextOptions.isMobile }));
promises.push(session2.send("Page.overrideSetting", { setting: "InputTypeWeekEnabled", value: contextOptions.isMobile }));
promises.push(session2.send("Page.overrideSetting", { setting: "FixedBackgroundsPaintRelativeToDocument", value: contextOptions.isMobile }));
await Promise.all(promises);
}
_initializeFrameSessions(frame, promises) {
const session2 = this._targetIdToFrameSession.get(`frame-${frame.frame.id}`);
if (session2)
promises.push(session2.initialize());
for (const childFrame of frame.childFrames || [])
this._initializeFrameSessions(childFrame, promises);
}
_onDidCommitProvisionalTarget(event) {
const { oldTargetId, newTargetId } = event;
assert(this._provisionalPage);
assert(this._provisionalPage._session.sessionId === newTargetId, "Unknown new target: " + newTargetId);
assert(this._session.sessionId === oldTargetId, "Unknown old target: " + oldTargetId);
const newSession = this._provisionalPage._session;
this._provisionalPage.commit();
this._provisionalPage.dispose();
this._provisionalPage = null;
this._setSession(newSession);
}
_onTargetDestroyed(event) {
const { targetId, crashed } = event;
if (this._provisionalPage && this._provisionalPage._session.sessionId === targetId) {
this._maybeCancelCoopNavigationRequest(this._provisionalPage);
this._provisionalPage._session.dispose();
this._provisionalPage.dispose();
this._provisionalPage = null;
} else if (this._session.sessionId === targetId) {
this._session.dispose();
eventsHelper.removeEventListeners(this._sessionListeners);
if (crashed) {
this._session.markAsCrashed();
this._page._didCrash();
}
} else if (this._targetIdToFrameSession.has(targetId)) {
this._targetIdToFrameSession.get(targetId).dispose();
this._targetIdToFrameSession.delete(targetId);
}
}
didClose() {
this._pageProxySession.dispose();
eventsHelper.removeEventListeners(this._sessionListeners);
eventsHelper.removeEventListeners(this._eventListeners);
if (this._session)
this._session.dispose();
if (this._provisionalPage) {
this._provisionalPage._session.dispose();
this._provisionalPage.dispose();
this._provisionalPage = null;
}
this._firstNonInitialNavigationCommittedReject(new TargetClosedError(this._page.closeReason()));
this._page._didClose();
}
dispatchMessageToSession(message) {
this._pageProxySession.dispatchMessage(message);
}
handleProvisionalLoadFailed(event) {
if (!this._page.initializedOrUndefined()) {
this._firstNonInitialNavigationCommittedReject(new Error("Initial load failed"));
return;
}
if (!this._provisionalPage)
return;
let errorText = event.error;
if (errorText.includes("cancelled"))
errorText += "; maybe frame was detached?";
this._page.frameManager.frameAbortedNavigation(this._page.mainFrame()._id, errorText, event.loaderId);
}
handleWindowOpen(event) {
this._nextWindowOpenPopupFeatures = event.windowFeatures;
}
async _onTargetCreated(event) {
const { targetInfo } = event;
const session2 = new WKSession(this._pageProxySession.connection, targetInfo.targetId, (message) => {
this._pageProxySession.send("Target.sendMessageToTarget", {
message: JSON.stringify(message),
targetId: targetInfo.targetId
}).catch((e) => {
session2.dispatchMessage({ id: message.id, error: { message: e.message } });
});
});
if (targetInfo.type === "frame") {
if (enableFrameSessions) {
const wkFrame = new WKFrame(this, session2);
this._targetIdToFrameSession.set(targetInfo.targetId, wkFrame);
await wkFrame.initialize().catch((e) => {
});
}
return;
}
assert(targetInfo.type === "page", "Only page targets are expected in WebKit, received: " + targetInfo.type);
if (!targetInfo.isProvisional) {
assert(!this._page.initializedOrUndefined());
let pageOrError;
try {
this._setSession(session2);
await Promise.all([
this._initializePageProxySession(),
this._initializeSession(session2, false, ({ frameTree }) => this._handleFrameTree(frameTree))
]);
pageOrError = this._page;
} catch (e) {
pageOrError = e;
}
if (targetInfo.isPaused)
this._pageProxySession.sendMayFail("Target.resume", { targetId: targetInfo.targetId });
if (pageOrError instanceof Page && this._page.mainFrame().url() === "") {
try {
await this._firstNonInitialNavigationCommittedPromise;
} catch (e) {
pageOrError = e;
}
}
this._page.reportAsNew(this._opener?._page, pageOrError instanceof Page ? void 0 : pageOrError);
} else {
assert(targetInfo.isProvisional);
assert(!this._provisionalPage);
this._provisionalPage = new WKProvisionalPage(session2, this);
if (targetInfo.isPaused) {
this._provisionalPage.initializationPromise.then(() => {
this._pageProxySession.sendMayFail("Target.resume", { targetId: targetInfo.targetId });
});
}
}
}
_onDispatchMessageFromTarget(event) {
const { targetId, message } = event;
if (this._provisionalPage && this._provisionalPage._session.sessionId === targetId)
this._provisionalPage._session.dispatchMessage(JSON.parse(message));
else if (this._session.sessionId === targetId)
this._session.dispatchMessage(JSON.parse(message));
else if (this._targetIdToFrameSession.has(targetId))
this._targetIdToFrameSession.get(targetId)._session.dispatchMessage(JSON.parse(message));
else
throw new Error("Unknown target: " + targetId);
}
_addSessionListeners() {
this._sessionListeners = [
eventsHelper.addEventListener(this._session, "Page.frameNavigated", (event) => this._onFrameNavigated(event.frame, false)),
eventsHelper.addEventListener(this._session, "Page.navigatedWithinDocument", (event) => this._onFrameNavigatedWithinDocument(event.frameId, event.url)),
eventsHelper.addEventListener(this._session, "Page.frameAttached", (event) => this._onFrameAttached(event.frameId, event.parentFrameId)),
eventsHelper.addEventListener(this._session, "Page.frameDetached", (event) => this._onFrameDetached(event.frameId)),
eventsHelper.addEventListener(this._session, "Page.willCheckNavigationPolicy", (event) => this._onWillCheckNavigationPolicy(event.frameId)),
eventsHelper.addEventListener(this._session, "Page.didCheckNavigationPolicy", (event) => this._onDidCheckNavigationPolicy(event.frameId, event.cancel)),
eventsHelper.addEventListener(this._session, "Page.loadEventFired", (event) => this._page.frameManager.frameLifecycleEvent(event.frameId, "load")),
eventsHelper.addEventListener(this._session, "Page.domContentEventFired", (event) => this._page.frameManager.frameLifecycleEvent(event.frameId, "domcontentloaded")),
eventsHelper.addEventListener(this._session, "Runtime.executionContextCreated", (event) => this._onExecutionContextCreated(event.context)),
eventsHelper.addEventListener(this._session, "Runtime.bindingCalled", (event) => this._onBindingCalled(event.contextId, event.argument)),
eventsHelper.addEventListener(this._session, "Console.messageAdded", (event) => this._onConsoleMessage(event)),
eventsHelper.addEventListener(this._session, "Console.messageRepeatCountUpdated", (event) => this._onConsoleRepeatCountUpdated(event)),
eventsHelper.addEventListener(this._pageProxySession, "Dialog.javascriptDialogOpening", (event) => this._onDialog(event)),
eventsHelper.addEventListener(this._session, "Page.fileChooserOpened", (event) => this._onFileChooserOpened(event)),
eventsHelper.addEventListener(this._session, "Network.requestWillBeSent", (e) => this._onRequestWillBeSent(this._session, e)),
eventsHelper.addEventListener(this._session, "Network.requestIntercepted", (e) => this._onRequestIntercepted(this._session, e)),
eventsHelper.addEventListener(this._session, "Network.responseReceived", (e) => this._onResponseReceived(this._session, e)),
eventsHelper.addEventListener(this._session, "Network.loadingFinished", (e) => this._onLoadingFinished(e)),
eventsHelper.addEventListener(this._session, "Network.loadingFailed", (e) => this._onLoadingFailed(this._session, e)),
eventsHelper.addEventListener(this._session, "Network.webSocketCreated", (e) => this._page.frameManager.onWebSocketCreated(e.requestId, e.url)),
eventsHelper.addEventListener(this._session, "Network.webSocketWillSendHandshakeRequest", (e) => this._page.frameManager.onWebSocketRequest(e.requestId)),
eventsHelper.addEventListener(this._session, "Network.webSocketHandshakeResponseReceived", (e) => this._page.frameManager.onWebSocketResponse(e.requestId, e.response.status, e.response.statusText)),
eventsHelper.addEventListener(this._session, "Network.webSocketFrameSent", (e) => e.response.payloadData && this._page.frameManager.onWebSocketFrameSent(e.requestId, e.response.opcode, e.response.payloadData)),
eventsHelper.addEventListener(this._session, "Network.webSocketFrameReceived", (e) => e.response.payloadData && this._page.frameManager.webSocketFrameReceived(e.requestId, e.response.opcode, e.response.payloadData)),
eventsHelper.addEventListener(this._session, "Network.webSocketClosed", (e) => this._page.frameManager.webSocketClosed(e.requestId)),
eventsHelper.addEventListener(this._session, "Network.webSocketFrameError", (e) => this._page.frameManager.webSocketError(e.requestId, e.errorMessage))
];
}
async _updateState(method, params2) {
await this._forAllSessions((session2) => session2.send(method, params2).then());
}
async _forAllSessions(callback) {
const sessions = [
this._session
];
if (this._provisionalPage)
sessions.push(this._provisionalPage._session);
await Promise.all(sessions.map((session2) => callback(session2).catch((e) => {
})));
}
_onWillCheckNavigationPolicy(frameId) {
if (this._provisionalPage)
return;
this._page.frameManager.frameRequestedNavigation(frameId);
}
_onDidCheckNavigationPolicy(frameId, cancel) {
if (!cancel)
return;
if (this._provisionalPage)
return;
this._page.frameManager.frameAbortedNavigation(frameId, "Navigation canceled by policy check");
}
_handleFrameTree(frameTree) {
this._onFrameAttached(frameTree.frame.id, frameTree.frame.parentId || null);
this._onFrameNavigated(frameTree.frame, true);
this._page.frameManager.frameLifecycleEvent(frameTree.frame.id, "domcontentloaded");
this._page.frameManager.frameLifecycleEvent(frameTree.frame.id, "load");
if (!frameTree.childFrames)
return;
for (const child of frameTree.childFrames)
this._handleFrameTree(child);
}
_onFrameAttached(frameId, parentFrameId) {
return this._page.frameManager.frameAttached(frameId, parentFrameId);
}
_onFrameNavigated(framePayload, initial) {
const frame = this._page.frameManager.frame(framePayload.id);
assert(frame);
this._removeContextsForFrame(frame, true);
if (!framePayload.parentId)
this._workers.clear();
this._page.frameManager.frameCommittedNewDocumentNavigation(framePayload.id, framePayload.url, framePayload.name || "", framePayload.loaderId, initial);
if (!initial)
this._firstNonInitialNavigationCommittedFulfill();
}
_onFrameNavigatedWithinDocument(frameId, url2) {
this._page.frameManager.frameCommittedSameDocumentNavigation(frameId, url2);
}
_onFrameDetached(frameId) {
this._page.frameManager.frameDetached(frameId);
}
_removeContextsForFrame(frame, notifyFrame) {
for (const [contextId4, context2] of this._contextIdToContext) {
if (context2.frame === frame) {
this._contextIdToContext.delete(contextId4);
if (notifyFrame)
frame.contextDestroyed(context2);
}
}
}
_onExecutionContextCreated(contextPayload) {
if (this._contextIdToContext.has(contextPayload.id))
return;
const frame = this._page.frameManager.frame(contextPayload.frameId);
if (!frame)
return;
const delegate = new WKExecutionContext(this._session, contextPayload.id);
let worldName = null;
if (contextPayload.type === "normal")
worldName = "main";
else if (contextPayload.type === "user" && contextPayload.name === UTILITY_WORLD_NAME3)
worldName = "utility";
const context2 = new FrameExecutionContext(delegate, frame, worldName);
if (worldName)
frame.contextCreated(worldName, context2);
this._contextIdToContext.set(contextPayload.id, context2);
}
async _onBindingCalled(contextId4, argument) {
const pageOrError = await this._page.waitForInitializedOrError();
if (!(pageOrError instanceof Error)) {
const context2 = this._contextIdToContext.get(contextId4);
if (context2)
await this._page.onBindingCalled(argument, context2);
}
}
async navigateFrame(frame, url2, referrer) {
if (this._pageProxySession.isDisposed())
throw new TargetClosedError(this._page.closeReason());
const pageProxyId = this._pageProxySession.sessionId;
const result2 = await this._pageProxySession.connection.browserSession.send("Playwright.navigate", { url: url2, pageProxyId, frameId: frame._id, referrer });
return { newDocumentId: result2.loaderId };
}
_onConsoleMessage(event) {
const { type: type3, level, text: text2, parameters, url: url2, line: lineNumber, column: columnNumber, source: source8 } = event.message;
if (level === "error" && source8 === "javascript") {
const { name, message } = splitErrorMessage(text2);
let stack;
if (event.message.stackTrace) {
stack = text2 + "\n" + event.message.stackTrace.callFrames.map((callFrame) => {
return ` at ${callFrame.functionName || "unknown"} (${callFrame.url}:${callFrame.lineNumber}:${callFrame.columnNumber})`;
}).join("\n");
} else {
stack = "";
}
this._lastConsoleMessage = null;
const error = new Error(message);
error.stack = stack;
error.name = name;
this._page.addPageError(error, {
url: url2 || "",
lineNumber: (lineNumber || 1) - 1,
columnNumber: (columnNumber || 1) - 1
});
return;
}
let derivedType = type3 || "";
if (type3 === "log")
derivedType = level;
else if (type3 === "timing")
derivedType = "timeEnd";
const handles = [];
for (const p of parameters || []) {
let context2;
if (p.objectId) {
const objectId = JSON.parse(p.objectId);
context2 = this._contextIdToContext.get(objectId.injectedScriptId);
} else {
context2 = [...this._contextIdToContext.values()].find((c) => c.frame === this._page.mainFrame());
}
if (!context2)
return;
handles.push(createHandle4(context2, p));
}
this._lastConsoleMessage = {
derivedType,
text: text2,
handles,
count: 0,
location: {
url: url2 || "",
lineNumber: (lineNumber || 1) - 1,
columnNumber: (columnNumber || 1) - 1
}
};
this._onConsoleRepeatCountUpdated({ count: 1, timestamp: event.message.timestamp });
}
_onConsoleRepeatCountUpdated(event) {
if (this._lastConsoleMessage) {
const {
derivedType,
text: text2,
handles,
count,
location: location2
} = this._lastConsoleMessage;
const timestamp = event.timestamp ? event.timestamp * 1e3 : Date.now();
for (let i = count; i < event.count; ++i)
this._page.addConsoleMessage(null, derivedType, handles, location2, handles.length ? void 0 : text2, timestamp);
this._lastConsoleMessage.count = event.count;
}
}
_onDialog(event) {
this._page.browserContext.dialogManager.dialogDidOpen(new Dialog(
this._page,
event.type,
event.message,
async (accept, promptText) => {
if (event.type === "beforeunload" && !accept)
this._page.frameManager.frameAbortedNavigation(this._page.mainFrame()._id, "navigation cancelled by beforeunload dialog");
await this._pageProxySession.send("Dialog.handleJavaScriptDialog", { accept, promptText });
},
event.defaultPrompt
));
}
async _onFileChooserOpened(event) {
let handle;
try {
const context2 = await this._page.frameManager.frame(event.frameId).mainContext();
handle = createHandle4(context2, event.element).asElement();
} catch (e) {
return;
}
await this._page._onFileChooserOpened(handle);
}
static async _setEmulateMedia(session2, mediaType, colorScheme, reducedMotion, forcedColors, contrast) {
const promises = [];
promises.push(session2.send("Page.setEmulatedMedia", { media: mediaType === "no-override" ? "" : mediaType }));
let appearance = void 0;
switch (colorScheme) {
case "light":
appearance = "Light";
break;
case "dark":
appearance = "Dark";
break;
case "no-override":
appearance = void 0;
break;
}
promises.push(session2.send("Page.overrideUserPreference", { name: "PrefersColorScheme", value: appearance }));
let reducedMotionWk = void 0;
switch (reducedMotion) {
case "reduce":
reducedMotionWk = "Reduce";
break;
case "no-preference":
reducedMotionWk = "NoPreference";
break;
case "no-override":
reducedMotionWk = void 0;
break;
}
promises.push(session2.send("Page.overrideUserPreference", { name: "PrefersReducedMotion", value: reducedMotionWk }));
let forcedColorsWk = void 0;
switch (forcedColors) {
case "active":
forcedColorsWk = "Active";
break;
case "none":
forcedColorsWk = "None";
break;
case "no-override":
forcedColorsWk = void 0;
break;
}
promises.push(session2.send("Page.setForcedColors", { forcedColors: forcedColorsWk }));
let contrastWk = void 0;
switch (contrast) {
case "more":
contrastWk = "More";
break;
case "no-preference":
contrastWk = "NoPreference";
break;
case "no-override":
contrastWk = void 0;
break;
}
promises.push(session2.send("Page.overrideUserPreference", { name: "PrefersContrast", value: contrastWk }));
await Promise.all(promises);
}
async updateExtraHTTPHeaders() {
await this._updateState("Network.setExtraHTTPHeaders", { headers: headersArrayToObject(
this._calculateExtraHTTPHeaders(),
false
/* lowerCase */
) });
}
_calculateExtraHTTPHeaders() {
const locale = this._browserContext._options.locale;
const headers = mergeHeaders([
this._browserContext._options.extraHTTPHeaders,
this._page.extraHTTPHeaders(),
locale ? singleHeader("Accept-Language", locale) : void 0
]);
return headers;
}
async updateEmulateMedia() {
const emulatedMedia = this._page.emulatedMedia();
const colorScheme = emulatedMedia.colorScheme;
const reducedMotion = emulatedMedia.reducedMotion;
const forcedColors = emulatedMedia.forcedColors;
const contrast = emulatedMedia.contrast;
await this._forAllSessions((session2) => _WKPage._setEmulateMedia(session2, emulatedMedia.media, colorScheme, reducedMotion, forcedColors, contrast));
}
async updateEmulatedViewportSize() {
this._browserContext._validateEmulatedViewport(this._page.emulatedSize()?.viewport);
await this._updateViewport();
}
async updateUserAgent() {
const contextOptions = this._browserContext._options;
this._updateState("Page.overrideUserAgent", { value: contextOptions.userAgent });
}
async bringToFront() {
await this._pageProxySession.send("Target.activate", {
targetId: this._session.sessionId
});
}
async _updateViewport() {
const options2 = this._browserContext._options;
const emulatedSize = this._page.emulatedSize();
if (!emulatedSize)
return;
const viewportSize = emulatedSize.viewport;
const screenSize = emulatedSize.screen;
const promises = [
this._pageProxySession.send("Emulation.setDeviceMetricsOverride", {
width: viewportSize.width,
height: viewportSize.height,
fixedLayout: !!options2.isMobile,
deviceScaleFactor: options2.deviceScaleFactor || 1
}),
this._session.send("Page.setScreenSizeOverride", {
width: screenSize.width,
height: screenSize.height
})
];
if (options2.isMobile) {
const angle = viewportSize.width > viewportSize.height ? 90 : 0;
promises.push(this._pageProxySession.send("Emulation.setOrientationOverride", { angle }));
}
await Promise.all(promises);
if (!this._browserContext._browser?.options.headful && (hostPlatform === "ubuntu22.04-x64" || hostPlatform.startsWith("debian12")))
await new Promise((r) => setTimeout(r, 500));
}
async updateRequestInterception() {
const enabled = this._page.needsRequestInterception();
await Promise.all([
this._updateState("Network.setInterceptionEnabled", { enabled }),
this._updateState("Network.setResourceCachingDisabled", { disabled: enabled }),
this._updateState("Network.addInterception", { url: ".*", stage: "request", isRegex: true })
]);
}
async updateOffline() {
await this._updateState("Network.setEmulateOfflineState", { offline: !!this._browserContext._options.offline });
}
async updateHttpCredentials() {
const credentials = this._browserContext._options.httpCredentials || { username: "", password: "", origin: "" };
await this._pageProxySession.send("Emulation.setAuthCredentials", { username: credentials.username, password: credentials.password, origin: credentials.origin });
}
async updateFileChooserInterception() {
const enabled = this._page.fileChooserIntercepted();
await this._session.send("Page.setInterceptFileChooserDialog", { enabled }).catch(() => {
});
}
async reload() {
await this._session.send("Page.reload");
}
goBack() {
return this._session.send("Page.goBack").then(() => true).catch((error) => {
if (error instanceof Error && error.message.includes(`Protocol error (Page.goBack): Failed to go`))
return false;
throw error;
});
}
goForward() {
return this._session.send("Page.goForward").then(() => true).catch((error) => {
if (error instanceof Error && error.message.includes(`Protocol error (Page.goForward): Failed to go`))
return false;
throw error;
});
}
async requestGC() {
await this._session.send("Heap.gc");
}
async addInitScript(initScript) {
await this._updateBootstrapScript();
}
async removeInitScripts(initScripts) {
await this._updateBootstrapScript();
}
async exposePlaywrightBinding() {
await this._updateState("Runtime.addBinding", { name: PageBinding.kBindingName });
}
_calculateBootstrapScript() {
const scripts = [];
if (!this._page.browserContext._options.isMobile) {
scripts.push("delete window.orientation");
scripts.push("delete window.ondevicemotion");
scripts.push("delete window.ondeviceorientation");
}
scripts.push('if (!window.safari) window.safari = { pushNotification: { toString() { return "[object SafariRemoteNotification]"; } } };');
scripts.push("if (!window.GestureEvent) window.GestureEvent = function GestureEvent() {};");
scripts.push(this._publicKeyCredentialScript());
scripts.push(...this._page.allInitScripts().map((script) => script.source));
return scripts.join(";\n");
}
_publicKeyCredentialScript() {
function polyfill() {
window.PublicKeyCredential ??= {
async getClientCapabilities() {
return {};
},
async isConditionalMediationAvailable() {
return false;
},
async isUserVerifyingPlatformAuthenticatorAvailable() {
return false;
}
};
}
return `(${polyfill.toString()})();`;
}
async _updateBootstrapScript() {
await this._updateState("Page.setBootstrapScript", { source: this._calculateBootstrapScript() });
}
async closePage(runBeforeUnload) {
await this._pageProxySession.sendMayFail("Target.close", {
targetId: this._session.sessionId,
runBeforeUnload
});
}
async setBackgroundColor(color) {
await this._session.send("Page.setDefaultBackgroundColorOverride", { color });
}
_toolbarHeight() {
if (this._page.browserContext._browser?.options.headful) {
if (hostPlatform === "mac10.15")
return 55;
if (hostPlatform === "mac26-arm64" || hostPlatform === "mac26")
return 69;
return 59;
}
return 0;
}
validateScreenshotDimension(side, omitDeviceScaleFactor) {
if (process.platform === "darwin")
return;
if (!omitDeviceScaleFactor && this._page.browserContext._options.deviceScaleFactor)
side = Math.ceil(side * this._page.browserContext._options.deviceScaleFactor);
if (side > 32767)
throw new Error("Cannot take screenshot larger than 32767 pixels on any dimension");
}
async takeScreenshot(progress2, format2, documentRect, viewportRect, quality, fitsViewport, scale) {
const rect = documentRect || viewportRect;
const omitDeviceScaleFactor = scale === "css";
this.validateScreenshotDimension(rect.width, omitDeviceScaleFactor);
this.validateScreenshotDimension(rect.height, omitDeviceScaleFactor);
const result2 = await progress2.race(this._session.send("Page.snapshotRect", { ...rect, coordinateSystem: documentRect ? "Page" : "Viewport", omitDeviceScaleFactor }));
const prefix = "data:image/png;base64,";
let buffer = Buffer.from(result2.dataURL.substr(prefix.length), "base64");
if (format2 === "jpeg")
buffer = jpegjs3.encode(PNG2.sync.read(buffer), quality).data;
return buffer;
}
async getContentFrame(handle) {
const nodeInfo = await this._session.send("DOM.describeNode", {
objectId: handle._objectId
});
if (!nodeInfo.contentFrameId)
return null;
return this._page.frameManager.frame(nodeInfo.contentFrameId);
}
async getOwnerFrame(handle) {
if (!handle._objectId)
return null;
const nodeInfo = await this._session.send("DOM.describeNode", {
objectId: handle._objectId
});
return nodeInfo.ownerFrameId || null;
}
async getBoundingBox(handle) {
const quads = await this.getContentQuads(handle);
if (!quads || !quads.length)
return null;
let minX = Infinity;
let maxX = -Infinity;
let minY = Infinity;
let maxY = -Infinity;
for (const quad of quads) {
for (const point of quad) {
minX = Math.min(minX, point.x);
maxX = Math.max(maxX, point.x);
minY = Math.min(minY, point.y);
maxY = Math.max(maxY, point.y);
}
}
return { x: minX, y: minY, width: maxX - minX, height: maxY - minY };
}
async scrollRectIntoViewIfNeeded(handle, rect) {
return await this._session.send("DOM.scrollIntoViewIfNeeded", {
objectId: handle._objectId,
rect
}).then(() => "done").catch((e) => {
if (e instanceof Error && e.message.includes("Node does not have a layout object"))
return "error:notvisible";
if (e instanceof Error && e.message.includes("Node is detached from document"))
return "error:notconnected";
throw e;
});
}
startScreencast(options2) {
this._pageProxySession.send("Screencast.startScreencast", {
quality: options2.quality,
width: options2.width,
height: options2.height,
toolbarHeight: this._toolbarHeight()
}).then(({ generation }) => this._screencastGeneration = generation).catch(() => {
});
}
stopScreencast() {
this._pageProxySession.sendMayFail("Screencast.stopScreencast");
}
_onScreencastFrame(event) {
const generation = this._screencastGeneration;
const buffer = Buffer.from(event.data, "base64");
this._page.screencast.onScreencastFrame({
buffer,
frameSwapWallTime: event.timestamp ? event.timestamp * 1e3 : Date.now(),
viewportWidth: event.deviceWidth,
viewportHeight: event.deviceHeight
}, () => {
this._pageProxySession.sendMayFail("Screencast.screencastFrameAck", { generation });
});
}
rafCountForStablePosition() {
return process.platform === "win32" ? 5 : 1;
}
async getContentQuads(handle) {
const result2 = await this._session.sendMayFail("DOM.getContentQuads", {
objectId: handle._objectId
});
if (!result2)
return null;
return result2.quads.map((quad) => [
{ x: quad[0], y: quad[1] },
{ x: quad[2], y: quad[3] },
{ x: quad[4], y: quad[5] },
{ x: quad[6], y: quad[7] }
]);
}
async setInputFilePaths(progress2, handle, paths) {
const pageProxyId = this._pageProxySession.sessionId;
const objectId = handle._objectId;
if (this._browserContext._browser?.options.channel === "webkit-wsl")
paths = await progress2.race(Promise.all(paths.map((path59) => translatePathToWSL(path59))));
await progress2.race(Promise.all([
this._pageProxySession.connection.browserSession.send("Playwright.grantFileReadAccess", { pageProxyId, paths }),
this._session.send("DOM.setInputFiles", { objectId, paths })
]));
}
async adoptElementHandle(handle, to) {
const result2 = await this._session.sendMayFail("DOM.resolveNode", {
objectId: handle._objectId,
executionContextId: to.delegate._contextId
});
if (!result2 || result2.object.subtype === "null")
throw new Error(kUnableToAdoptErrorMessage);
return createHandle4(to, result2.object);
}
async inputActionEpilogue() {
}
async resetForReuse(progress2) {
}
async getFrameElement(frame) {
const parent = frame.parentFrame();
if (!parent)
throw new Error("Frame has been detached.");
const context2 = await parent.mainContext();
const result2 = await this._session.send("DOM.resolveNode", {
frameId: frame._id,
executionContextId: context2.delegate._contextId
});
if (!result2 || result2.object.subtype === "null")
throw new Error("Frame has been detached.");
return createHandle4(context2, result2.object);
}
_maybeCancelCoopNavigationRequest(provisionalPage) {
const navigationRequest = provisionalPage.coopNavigationRequest();
for (const [requestId, request2] of this._requestIdToRequest) {
if (request2.request === navigationRequest) {
this._onLoadingFailed(provisionalPage._session, {
requestId,
errorText: "Provisiolal navigation canceled.",
timestamp: request2._timestamp,
canceled: true
});
return;
}
}
}
_adoptRequestFromNewProcess(navigationRequest, newSession, newRequestId) {
for (const [requestId, request2] of this._requestIdToRequest) {
if (request2.request === navigationRequest) {
this._requestIdToRequest.delete(requestId);
request2.adoptRequestFromNewProcess(newSession, newRequestId);
this._requestIdToRequest.set(newRequestId, request2);
return;
}
}
}
_onRequestWillBeSent(session2, event) {
if (event.request.url.startsWith("data:"))
return;
if (event.request.url.startsWith("about:"))
return;
if (this._page.needsRequestInterception() && !event.redirectResponse)
this._requestIdToRequestWillBeSentEvent.set(event.requestId, event);
else
this._onRequest(session2, event, false);
}
_onRequest(session2, event, intercepted) {
let redirectedFrom = null;
if (event.redirectResponse) {
const request3 = this._requestIdToRequest.get(event.requestId);
if (request3) {
this._handleRequestRedirect(request3, event.requestId, event.redirectResponse, event.timestamp);
redirectedFrom = request3;
}
}
const frame = redirectedFrom ? redirectedFrom.request.frame() : this._page.frameManager.frame(event.frameId);
if (!frame)
return;
const isNavigationRequest = event.type === "Document";
const documentId = isNavigationRequest ? event.loaderId : void 0;
const request2 = new WKInterceptableRequest(session2, frame, event, redirectedFrom, documentId);
let route2;
if (intercepted) {
route2 = new WKRouteImpl(session2, event.requestId);
request2.request.setRawRequestHeaders(null);
}
this._requestIdToRequest.set(event.requestId, request2);
this._page.frameManager.requestStarted(request2.request, route2);
}
_handleRequestRedirect(request2, requestId, responsePayload, timestamp) {
const response2 = request2.createResponse(responsePayload);
response2._setHttpVersion(null);
response2._securityDetailsFinished();
response2._serverAddrFinished();
response2.setResponseHeadersSize(null);
response2.setEncodedBodySize(null);
response2._requestFinished(responsePayload.timing ? helper.secondsToRoundishMillis(timestamp - request2._timestamp) : -1);
this._requestIdToRequest.delete(requestId);
this._page.frameManager.requestReceivedResponse(response2);
this._page.frameManager.reportRequestFinished(request2.request, response2);
}
_onRequestIntercepted(session2, event) {
const requestWillBeSentEvent = this._requestIdToRequestWillBeSentEvent.get(event.requestId);
if (!requestWillBeSentEvent) {
session2.sendMayFail("Network.interceptWithRequest", { requestId: event.requestId });
return;
}
this._requestIdToRequestWillBeSentEvent.delete(event.requestId);
this._onRequest(session2, requestWillBeSentEvent, true);
}
_onResponseReceived(session2, event) {
const requestWillBeSentEvent = this._requestIdToRequestWillBeSentEvent.get(event.requestId);
if (requestWillBeSentEvent) {
this._requestIdToRequestWillBeSentEvent.delete(event.requestId);
this._onRequest(session2, requestWillBeSentEvent, false);
}
const request2 = this._requestIdToRequest.get(event.requestId);
if (!request2)
return;
this._requestIdToResponseReceivedPayloadEvent.set(event.requestId, event);
const response2 = request2.createResponse(event.response);
this._page.frameManager.requestReceivedResponse(response2);
if (response2.status() === 204 && request2.request.isNavigationRequest()) {
this._onLoadingFailed(session2, {
requestId: event.requestId,
errorText: "Aborted: 204 No Content",
timestamp: event.timestamp
});
}
}
_onLoadingFinished(event) {
const request2 = this._requestIdToRequest.get(event.requestId);
if (!request2)
return;
const response2 = request2.request._existingResponse();
if (response2) {
const responseReceivedPayload = this._requestIdToResponseReceivedPayloadEvent.get(event.requestId);
response2._serverAddrFinished(parseRemoteAddress(event?.metrics?.remoteAddress));
response2._securityDetailsFinished({
protocol: isLoadedSecurely(response2.url(), response2.timing()) ? event.metrics?.securityConnection?.protocol : void 0,
subjectName: responseReceivedPayload?.response.security?.certificate?.subject,
validFrom: responseReceivedPayload?.response.security?.certificate?.validFrom,
validTo: responseReceivedPayload?.response.security?.certificate?.validUntil
});
response2._setHttpVersion(event.metrics?.protocol ?? null);
response2.setEncodedBodySize(event.metrics?.responseBodyBytesReceived ?? null);
response2.setResponseHeadersSize(event.metrics?.responseHeaderBytesReceived ?? null);
response2._requestFinished(helper.secondsToRoundishMillis(event.timestamp - request2._timestamp));
} else {
request2.request.setRawRequestHeaders(null);
}
this._requestIdToResponseReceivedPayloadEvent.delete(event.requestId);
this._requestIdToRequest.delete(event.requestId);
this._page.frameManager.reportRequestFinished(request2.request, response2);
}
_onLoadingFailed(session2, event) {
const requestWillBeSentEvent = this._requestIdToRequestWillBeSentEvent.get(event.requestId);
if (requestWillBeSentEvent) {
this._requestIdToRequestWillBeSentEvent.delete(event.requestId);
this._onRequest(session2, requestWillBeSentEvent, false);
}
const request2 = this._requestIdToRequest.get(event.requestId);
if (!request2)
return;
const response2 = request2.request._existingResponse();
if (response2) {
response2._serverAddrFinished();
response2._securityDetailsFinished();
response2._setHttpVersion(null);
response2.setResponseHeadersSize(null);
response2.setEncodedBodySize(null);
response2._requestFinished(helper.secondsToRoundishMillis(event.timestamp - request2._timestamp));
} else {
request2.request.setRawRequestHeaders(null);
}
this._requestIdToRequest.delete(event.requestId);
request2.request._setFailureText(event.errorText);
this._page.frameManager.requestFailed(request2.request, event.errorText.includes("cancelled"));
}
async _grantPermissions(origin, permissions) {
const webPermissionToProtocol = /* @__PURE__ */ new Map([
["geolocation", "geolocation"],
["notifications", "notifications"],
["clipboard-read", "clipboard-read"],
["screen-wake-lock", "screen-wake-lock"]
]);
const filtered = permissions.map((permission) => {
const protocolPermission = webPermissionToProtocol.get(permission);
if (!protocolPermission)
throw new Error("Unknown permission: " + permission);
return protocolPermission;
});
await this._pageProxySession.send("Emulation.grantPermissions", { origin, permissions: filtered });
}
async _clearPermissions() {
await this._pageProxySession.send("Emulation.resetPermissions", {});
}
shouldToggleStyleSheetToSyncAnimations() {
return true;
}
async setDockTile(image) {
}
};
WKFrame = class {
constructor(page, session2) {
this._sessionListeners = [];
this._initializePromise = null;
this._page = page;
this._session = session2;
}
async initialize() {
if (this._initializePromise)
return this._initializePromise;
this._initializePromise = this._initializeImpl();
return this._initializePromise;
}
async _initializeImpl() {
this._sessionListeners = [
eventsHelper.addEventListener(this._session, "Console.messageAdded", (event) => this._page._onConsoleMessage(event)),
eventsHelper.addEventListener(this._session, "Console.messageRepeatCountUpdated", (event) => this._page._onConsoleRepeatCountUpdated(event))
];
await this._session.send("Console.enable");
}
dispose() {
eventsHelper.removeEventListeners(this._sessionListeners);
this._session.dispose();
}
};
}
});
// packages/playwright-core/src/server/webkit/wkBrowser.ts
var BROWSER_VERSION, DEFAULT_USER_AGENT, WKBrowser, WKBrowserContext;
var init_wkBrowser = __esm({
"packages/playwright-core/src/server/webkit/wkBrowser.ts"() {
"use strict";
init_assert();
init_browser();
init_browserContext();
init_network2();
init_wkConnection();
init_wkPage();
init_webkit();
BROWSER_VERSION = "26.4";
DEFAULT_USER_AGENT = `Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/${BROWSER_VERSION} Safari/605.1.15`;
WKBrowser = class _WKBrowser extends Browser {
constructor(parent, transport, options2) {
super(parent, options2);
this._contexts = /* @__PURE__ */ new Map();
this._wkPages = /* @__PURE__ */ new Map();
this._connection = new WKConnection(transport, this._onDisconnect.bind(this), options2.protocolLogger, options2.browserLogsCollector);
this._browserSession = this._connection.browserSession;
this._browserSession.on("Playwright.pageProxyCreated", this._onPageProxyCreated.bind(this));
this._browserSession.on("Playwright.pageProxyDestroyed", this._onPageProxyDestroyed.bind(this));
this._browserSession.on("Playwright.provisionalLoadFailed", (event) => this._onProvisionalLoadFailed(event));
this._browserSession.on("Playwright.windowOpen", (event) => this._onWindowOpen(event));
this._browserSession.on("Playwright.downloadCreated", this._onDownloadCreated.bind(this));
this._browserSession.on("Playwright.downloadFilenameSuggested", this._onDownloadFilenameSuggested.bind(this));
this._browserSession.on("Playwright.downloadFinished", this._onDownloadFinished.bind(this));
this._browserSession.on(kPageProxyMessageReceived, this._onPageProxyMessageReceived.bind(this));
}
static async connect(parent, transport, options2) {
const browser = new _WKBrowser(parent, transport, options2);
if (options2.__testHookOnConnectToBrowser)
await options2.__testHookOnConnectToBrowser();
const promises = [
browser._browserSession.send("Playwright.enable")
];
if (options2.persistent) {
options2.persistent.userAgent ||= DEFAULT_USER_AGENT;
browser._defaultContext = new WKBrowserContext(browser, void 0, options2.persistent);
promises.push(browser._defaultContext.initialize());
}
await Promise.all(promises);
return browser;
}
_onDisconnect() {
for (const wkPage of this._wkPages.values())
wkPage.didClose();
this._wkPages.clear();
this.didClose();
}
async doCreateNewContext(options2) {
const proxy = options2.proxyOverride || options2.proxy;
const createOptions = proxy ? {
// Enable socks5 hostname resolution on Windows.
// See https://github.com/microsoft/playwright/issues/20451
proxyServer: process.platform === "win32" && this.attribution.browser?.options.channel !== "webkit-wsl" ? proxy.server.replace(/^socks5:\/\//, "socks5h://") : proxy.server,
proxyBypassList: proxy.bypass
} : void 0;
const { browserContextId } = await this._browserSession.send("Playwright.createContext", createOptions);
options2.userAgent = options2.userAgent || DEFAULT_USER_AGENT;
const context2 = new WKBrowserContext(this, browserContextId, options2);
await context2.initialize();
this._contexts.set(browserContextId, context2);
return context2;
}
contexts() {
return Array.from(this._contexts.values());
}
version() {
return BROWSER_VERSION;
}
userAgent() {
return DEFAULT_USER_AGENT;
}
_onDownloadCreated(payload) {
const page = this._wkPages.get(payload.pageProxyId);
if (!page)
return;
let frameId = payload.frameId;
if (!page._page.frameManager.frame(frameId))
frameId = page._page.mainFrame()._id;
page._page.frameManager.frameAbortedNavigation(frameId, "Download is starting");
let originPage = page._page.initializedOrUndefined();
if (!originPage) {
page._firstNonInitialNavigationCommittedReject(new Error("Starting new page download"));
if (page._opener)
originPage = page._opener._page.initializedOrUndefined();
}
if (!originPage)
return;
this.downloadCreated(originPage, payload.uuid, payload.url);
}
_onDownloadFilenameSuggested(payload) {
this.downloadFilenameSuggested(payload.uuid, payload.suggestedFilename);
}
_onDownloadFinished(payload) {
this.downloadFinished(payload.uuid, payload.error);
}
_onPageProxyCreated(event) {
const pageProxyId = event.pageProxyId;
let context2 = null;
if (event.browserContextId) {
context2 = this._contexts.get(event.browserContextId) || null;
}
if (!context2)
context2 = this._defaultContext;
if (!context2)
return;
const pageProxySession = new WKSession(this._connection, pageProxyId, (message) => {
this._connection.rawSend({ ...message, pageProxyId });
});
const opener = event.openerId ? this._wkPages.get(event.openerId) : void 0;
const wkPage = new WKPage(context2, pageProxySession, opener || null);
this._wkPages.set(pageProxyId, wkPage);
}
_onPageProxyDestroyed(event) {
const pageProxyId = event.pageProxyId;
const wkPage = this._wkPages.get(pageProxyId);
if (!wkPage)
return;
this._wkPages.delete(pageProxyId);
wkPage.didClose();
}
_onPageProxyMessageReceived(event) {
const wkPage = this._wkPages.get(event.pageProxyId);
if (!wkPage)
return;
wkPage.dispatchMessageToSession(event.message);
}
_onProvisionalLoadFailed(event) {
const wkPage = this._wkPages.get(event.pageProxyId);
if (!wkPage)
return;
wkPage.handleProvisionalLoadFailed(event);
}
_onWindowOpen(event) {
const wkPage = this._wkPages.get(event.pageProxyId);
if (!wkPage)
return;
wkPage.handleWindowOpen(event);
}
isConnected() {
return !this._connection.isClosed();
}
};
WKBrowserContext = class extends BrowserContext {
constructor(browser, browserContextId, options2) {
super(browser, options2, browserContextId);
this._validateEmulatedViewport(options2.viewport);
this.authenticateProxyViaHeader();
}
async initialize() {
assert(!this._wkPages().length);
const browserContextId = this._browserContextId;
const promises = [super.initialize()];
promises.push(this._browser._browserSession.send("Playwright.setDownloadBehavior", {
behavior: this._options.acceptDownloads === "accept" ? "allow" : "deny",
downloadPath: this._browser.options.channel === "webkit-wsl" ? await translatePathToWSL(this._browser.options.downloadsPath) : this._browser.options.downloadsPath,
browserContextId
}));
if (this._options.ignoreHTTPSErrors || this._options.internalIgnoreHTTPSErrors)
promises.push(this._browser._browserSession.send("Playwright.setIgnoreCertificateErrors", { browserContextId, ignore: true }));
if (this._options.locale)
promises.push(this._browser._browserSession.send("Playwright.setLanguages", { browserContextId, languages: [this._options.locale] }));
if (this._options.geolocation)
promises.push(this.setGeolocation(this._options.geolocation));
if (this._options.offline)
promises.push(this.doUpdateOffline());
if (this._options.httpCredentials)
promises.push(this.innerSetHTTPCredentials(this._options.httpCredentials));
await Promise.all(promises);
}
_wkPages() {
return Array.from(this._browser._wkPages.values()).filter((wkPage) => wkPage._browserContext === this);
}
possiblyUninitializedPages() {
return this._wkPages().map((wkPage) => wkPage._page);
}
async doCreateNewPage() {
const { pageProxyId } = await this._browser._browserSession.send("Playwright.createPage", { browserContextId: this._browserContextId });
return this._browser._wkPages.get(pageProxyId)._page;
}
async doGetCookies(urls) {
const { cookies } = await this._browser._browserSession.send("Playwright.getAllCookies", { browserContextId: this._browserContextId });
return filterCookies(cookies.map((c) => {
const { name, value: value2, domain, path: path59, expires, httpOnly, secure, sameSite } = c;
const copy = {
name,
value: value2,
domain,
path: path59,
expires: expires === -1 ? -1 : expires / 1e3,
httpOnly,
secure,
sameSite
};
return copy;
}), urls);
}
async addCookies(cookies) {
const cc = rewriteCookies(cookies).map((c) => {
const { name, value: value2, domain, path: path59, expires, httpOnly, secure, sameSite } = c;
const copy = {
name,
value: value2,
domain,
path: path59,
expires: expires && expires !== -1 ? expires * 1e3 : expires,
httpOnly,
secure,
sameSite,
session: expires === -1 || expires === void 0
};
return copy;
});
await this._browser._browserSession.send("Playwright.setCookies", { cookies: cc, browserContextId: this._browserContextId });
}
async doClearCookies() {
await this._browser._browserSession.send("Playwright.deleteAllCookies", { browserContextId: this._browserContextId });
}
async doGrantPermissions(origin, permissions) {
await Promise.all(this.pages().map((page) => page.delegate._grantPermissions(origin, permissions)));
}
async doClearPermissions() {
await Promise.all(this.pages().map((page) => page.delegate._clearPermissions()));
}
async setGeolocation(geolocation) {
verifyGeolocation(geolocation);
this._options.geolocation = geolocation;
const payload = geolocation ? { ...geolocation, timestamp: Date.now() } : void 0;
await this._browser._browserSession.send("Playwright.setGeolocationOverride", { browserContextId: this._browserContextId, geolocation: payload });
}
async doUpdateExtraHTTPHeaders() {
for (const page of this.pages())
await page.delegate.updateExtraHTTPHeaders();
}
async setUserAgent(userAgent) {
this._options.userAgent = userAgent;
for (const page of this.pages())
await page.delegate.updateUserAgent();
}
async doUpdateOffline() {
for (const page of this.pages())
await page.delegate.updateOffline();
}
async doSetHTTPCredentials(httpCredentials) {
this._options.httpCredentials = httpCredentials;
for (const page of this.pages())
await page.delegate.updateHttpCredentials();
}
async doAddInitScript(initScript) {
for (const page of this.pages())
await page.delegate._updateBootstrapScript();
}
async doRemoveInitScripts(initScripts) {
for (const page of this.pages())
await page.delegate._updateBootstrapScript();
}
async doUpdateRequestInterception() {
for (const page of this.pages())
await page.delegate.updateRequestInterception();
}
async doUpdateDefaultViewport() {
}
async doUpdateDefaultEmulatedMedia() {
}
async doExposePlaywrightBinding() {
for (const page of this.pages())
await page.delegate.exposePlaywrightBinding();
}
onClosePersistent() {
}
async clearCache() {
await this._browser._browserSession.send("Playwright.clearMemoryCache", {
browserContextId: this._browserContextId
});
}
async doClose(reason) {
if (!this._browserContextId) {
return "close-browser";
} else {
await this._browser._browserSession.send("Playwright.deleteContext", { browserContextId: this._browserContextId });
this._browser._contexts.delete(this._browserContextId);
}
}
async cancelDownload(uuid) {
await this._browser._browserSession.send("Playwright.cancelDownload", { uuid });
}
_validateEmulatedViewport(viewportSize) {
if (!viewportSize)
return;
if (process.platform === "win32" && this._browser.options.headful && (viewportSize.width < 250 || viewportSize.height < 240))
throw new Error(`WebKit on Windows has a minimal viewport of 250x240.`);
}
};
}
});
// packages/playwright-core/src/server/webkit/webkit.ts
async function translatePathToWSL(path59) {
const { stdout } = await spawnAsync("wsl.exe", ["-d", "playwright", "--cd", "/home/pwuser", "wslpath", path59.replace(/\\/g, "\\\\")]);
return stdout.toString().trim();
}
var import_path30, WebKit;
var init_webkit = __esm({
"packages/playwright-core/src/server/webkit/webkit.ts"() {
"use strict";
import_path30 = __toESM(require("path"));
init_ascii();
init_spawnAsync();
init_wkConnection();
init_browserType();
init_wkBrowser();
WebKit = class extends BrowserType {
constructor(parent) {
super(parent, "webkit");
}
connectToTransport(transport, options2) {
return WKBrowser.connect(this.attribution.playwright, transport, options2);
}
amendEnvironment(env, userDataDir, isPersistent, options2) {
return {
...env,
CURL_COOKIE_JAR_PATH: process.platform === "win32" && isPersistent ? import_path30.default.join(userDataDir, "cookiejar.db") : void 0
};
}
doRewriteStartupLog(logs) {
if (logs.includes("Failed to open display") || logs.includes("cannot open display"))
logs = "\n" + wrapInASCIIBox(kNoXServerRunningError, 1);
return logs;
}
attemptToGracefullyCloseBrowser(transport) {
transport.send({ method: "Playwright.close", params: {}, id: kBrowserCloseMessageId4 });
}
async defaultArgs(options2, isPersistent, userDataDir) {
const { args = [], headless } = options2;
const userDataDirArg = args.find((arg) => arg.startsWith("--user-data-dir"));
if (userDataDirArg)
throw this._createUserDataDirArgMisuseError("--user-data-dir");
if (args.find((arg) => !arg.startsWith("-")))
throw new Error("Arguments can not specify page to be opened");
const webkitArguments = ["--inspector-pipe"];
if (process.platform === "win32" && options2.channel !== "webkit-wsl")
webkitArguments.push("--disable-accelerated-compositing");
if (headless)
webkitArguments.push("--headless");
if (isPersistent)
webkitArguments.push(`--user-data-dir=${options2.channel === "webkit-wsl" ? await translatePathToWSL(userDataDir) : userDataDir}`);
else
webkitArguments.push(`--no-startup-window`);
const proxy = options2.proxyOverride || options2.proxy;
if (proxy) {
if (process.platform === "darwin") {
webkitArguments.push(`--proxy=${proxy.server}`);
if (proxy.bypass)
webkitArguments.push(`--proxy-bypass-list=${proxy.bypass}`);
} else if (process.platform === "linux" || process.platform === "win32" && options2.channel === "webkit-wsl") {
webkitArguments.push(`--proxy=${proxy.server}`);
if (proxy.bypass)
webkitArguments.push(...proxy.bypass.split(",").map((t) => `--ignore-host=${t}`));
} else if (process.platform === "win32") {
webkitArguments.push(`--curl-proxy=${proxy.server.replace(/^socks5:\/\//, "socks5h://")}`);
if (proxy.bypass)
webkitArguments.push(`--curl-noproxy=${proxy.bypass}`);
}
}
webkitArguments.push(...args);
if (isPersistent)
webkitArguments.push("about:blank");
return webkitArguments;
}
};
}
});
// packages/playwright-core/src/server/playwright.ts
var playwright_exports = {};
__export(playwright_exports, {
Playwright: () => Playwright,
createPlaywright: () => createPlaywright
});
function createPlaywright(options2) {
return new Playwright(options2);
}
var Playwright;
var init_playwright = __esm({
"packages/playwright-core/src/server/playwright.ts"() {
"use strict";
init_android();
init_backendAdb();
init_bidiChromium();
init_bidiFirefox();
init_chromium();
init_debugController();
init_electron();
init_firefox();
init_instrumentation();
init_webkit();
Playwright = class extends SdkObject {
constructor(options2) {
super(createRootSdkObject(), void 0, "Playwright");
this._allPages = /* @__PURE__ */ new Set();
this._allBrowsers = /* @__PURE__ */ new Set();
this.options = options2;
this.attribution.playwright = this;
this.instrumentation.addListener({
onBrowserOpen: (browser) => this._allBrowsers.add(browser),
onBrowserClose: (browser) => this._allBrowsers.delete(browser),
onPageOpen: (page) => this._allPages.add(page),
onPageClose: (page) => this._allPages.delete(page)
}, null);
this.chromium = new Chromium(this, new BidiChromium(this));
this.firefox = new Firefox(this, new BidiFirefox(this));
this.webkit = new WebKit(this);
this.electron = new Electron(this);
this.android = new Android(this, new AdbBackend());
this.debugController = new DebugController(this);
}
allBrowsers() {
return [...this._allBrowsers];
}
allPages() {
return [...this._allPages];
}
};
}
});
// packages/playwright-core/src/server/recorder/recorderApp.ts
function determinePrimaryGeneratorId(sdkLanguage) {
for (const language of languageSet()) {
if (language.highlighter === sdkLanguage)
return language.id;
}
return sdkLanguage;
}
function findPageByGuid(context2, guid) {
return context2.pages().find((p) => p.guid === guid);
}
function createRecorderFrontend(page) {
return new Proxy({}, {
get: (_target, prop) => {
if (typeof prop !== "string")
return void 0;
return (params2) => {
page.mainFrame().evaluateExpression(nullProgress, ((event) => {
window.dispatch(event);
}).toString(), { isFunction: true }, { method: prop, params: params2 }).catch(() => {
});
};
}
});
}
var import_fs30, import_path31, mime7, RecorderApp, ProgrammaticRecorderApp, recorderAppSymbol;
var init_recorderApp = __esm({
"packages/playwright-core/src/server/recorder/recorderApp.ts"() {
"use strict";
import_fs30 = __toESM(require("fs"));
import_path31 = __toESM(require("path"));
init_debug();
init_package();
init_launchApp();
init_launchApp();
init_progress();
init_throttledFile();
init_languages();
init_recorderUtils();
init_language();
init_recorder();
init_browserContext();
mime7 = require("./utilsBundle").mime;
RecorderApp = class _RecorderApp {
constructor(recorder, params2, page, wsEndpointForTest) {
this._throttledOutputFile = null;
this._actions = [];
this._userSources = [];
this._recorderSources = [];
this._page = page;
this._recorder = recorder;
this._frontend = createRecorderFrontend(page);
this.wsEndpointForTest = wsEndpointForTest;
this._languageGeneratorOptions = {
browserName: params2.browserName,
launchOptions: { headless: false, ...params2.launchOptions, tracesDir: void 0 },
contextOptions: { ...params2.contextOptions },
deviceName: params2.device,
saveStorage: params2.saveStorage
};
this._throttledOutputFile = params2.outputFile ? new ThrottledFile(params2.outputFile) : null;
this._primaryGeneratorId = process.env.TEST_INSPECTOR_LANGUAGE || params2.language || determinePrimaryGeneratorId(params2.sdkLanguage);
this._selectedGeneratorId = this._primaryGeneratorId;
for (const languageGenerator of languageSet()) {
if (languageGenerator.id === this._primaryGeneratorId)
this._recorder.setLanguage(languageGenerator.highlighter);
}
}
async _init(inspectedContext) {
await syncLocalStorageWithSettings(this._page, "recorder");
const controller = new ProgressController();
await controller.run(async (progress2) => {
await this._page.addRequestInterceptor(progress2, (route2) => {
if (!route2.request().url().startsWith("https://playwright/")) {
route2.continue({ isFallback: true }).catch(() => {
});
return;
}
const uri = route2.request().url().substring("https://playwright/".length);
const file = import_path31.default.join(libPath("vite", "recorder"), uri);
import_fs30.default.promises.readFile(file).then((buffer) => {
route2.fulfill({
status: 200,
headers: [
{ name: "Content-Type", value: mime7.getType(import_path31.default.extname(file)) || "application/octet-stream" }
],
body: buffer.toString("base64"),
isBase64: true
}).catch(() => {
});
});
});
await this._createDispatcher(progress2);
this._page.once("close", () => {
this._recorder.close();
this._page.browserContext.close(nullProgress, { reason: "Recorder window closed" }).catch(() => {
});
delete inspectedContext[recorderAppSymbol];
});
await this._page.mainFrame().goto(progress2, "https://playwright/index.html");
});
const url2 = this._recorder.url();
if (url2)
this._frontend.pageNavigated({ url: url2 });
this._frontend.modeChanged({ mode: this._recorder.mode() });
this._frontend.pauseStateChanged({ paused: this._recorder.paused() });
this._updateActions("reveal");
this._onUserSourcesChanged(this._recorder.userSources(), this._recorder.pausedSourceId());
this._frontend.callLogsUpdated({ callLogs: this._recorder.callLog() });
this._wireListeners(this._recorder);
}
async _createDispatcher(progress2) {
const dispatcher = {
clear: async () => {
this._actions = [];
this._updateActions("reveal");
this._recorder.clear();
},
fileChanged: async (params2) => {
const source8 = [...this._recorderSources, ...this._userSources].find((s) => s.id === params2.fileId);
if (source8) {
if (source8.isRecorded)
this._selectedGeneratorId = source8.id;
await this._recorder.setLanguage(source8.language);
}
},
setAutoExpect: async (params2) => {
this._languageGeneratorOptions.generateAutoExpect = params2.autoExpect;
this._updateActions();
},
setMode: async (params2) => {
await this._recorder.setMode(params2.mode);
},
resume: async () => {
this._recorder.resume();
},
pause: async () => {
this._recorder.pause();
},
step: async () => {
this._recorder.step();
},
highlightRequested: async (params2) => {
if (params2.selector)
await this._recorder.setHighlightedSelector(params2.selector);
if (params2.ariaTemplate)
await this._recorder.setHighlightedAriaTemplate(params2.ariaTemplate);
}
};
await this._page.exposeBinding(progress2, "sendCommand", async (_, data) => {
const { method, params: params2 } = data;
return await dispatcher[method].call(dispatcher, params2);
});
}
static async show(context2, params2) {
if (process.env.PW_CODEGEN_NO_INSPECTOR)
return;
const recorder = await Recorder.forContext(context2, params2);
if (params2.recorderMode === "api") {
const browserName = context2._browser.options.name;
await ProgrammaticRecorderApp.run(context2, recorder, browserName, params2);
return;
}
await _RecorderApp._show(recorder, context2, params2);
}
async close() {
await this._page.close(nullProgress);
}
static showInspectorNoReply(context2) {
if (process.env.PW_CODEGEN_NO_INSPECTOR)
return;
void Recorder.forContext(context2, {}).then((recorder) => _RecorderApp._show(recorder, context2, {})).catch(() => {
});
}
static async _show(recorder, inspectedContext, params2) {
if (inspectedContext[recorderAppSymbol])
return;
inspectedContext[recorderAppSymbol] = true;
const sdkLanguage = inspectedContext._browser.sdkLanguage();
const isChromium = inspectedContext._browser.options.browserType === "chromium";
const headed = !!inspectedContext._browser.options.headful;
const { createPlaywright: createPlaywright2 } = (init_playwright(), __toCommonJS(playwright_exports));
const recorderPlaywright = createPlaywright2({ sdkLanguage: "javascript", isInternalPlaywright: true });
const { context: appContext, page } = await launchApp(recorderPlaywright.chromium, {
sdkLanguage,
windowSize: { width: 600, height: 600 },
windowPosition: { x: 1020, y: 10 },
persistentContextOptions: {
noDefaultViewport: true,
headless: !!process.env.PWTEST_CLI_HEADLESS || isUnderTest() && !headed,
args: isUnderTest() ? ["--remote-debugging-port=0"] : void 0,
handleSIGINT: params2.handleSIGINT,
executablePath: isChromium ? inspectedContext._browser.options.customExecutablePath : void 0,
// Use the same channel as the inspected context to guarantee that the browser is installed.
channel: isChromium ? inspectedContext._browser.options.channel : void 0
}
});
const controller = new ProgressController();
await controller.run(async (progress2) => {
await appContext._browser._defaultContext.loadDefaultContextAsIs(progress2);
});
const appParams = {
browserName: inspectedContext._browser.options.name,
sdkLanguage: inspectedContext._browser.sdkLanguage(),
wsEndpointForTest: inspectedContext._browser.options.wsEndpoint,
headed: !!inspectedContext._browser.options.headful,
executablePath: isChromium ? inspectedContext._browser.options.customExecutablePath : void 0,
channel: isChromium ? inspectedContext._browser.options.channel : void 0,
...params2
};
const recorderApp = new _RecorderApp(recorder, appParams, page, appContext._browser.options.wsEndpoint);
await recorderApp._init(inspectedContext);
inspectedContext.recorderAppForTest = recorderApp;
}
_wireListeners(recorder) {
recorder.on(RecorderEvent.ActionAdded, (action) => {
this._onActionAdded(action);
});
recorder.on(RecorderEvent.SignalAdded, (signal) => {
this._onSignalAdded(signal);
});
recorder.on(RecorderEvent.PageNavigated, (url2) => {
this._frontend.pageNavigated({ url: url2 });
});
recorder.on(RecorderEvent.ContextClosed, () => {
this._throttledOutputFile?.flush();
this._page.browserContext.close(nullProgress, { reason: "Recorder window closed" }).catch(() => {
});
});
recorder.on(RecorderEvent.ModeChanged, (mode) => {
this._frontend.modeChanged({ mode });
});
recorder.on(RecorderEvent.PausedStateChanged, (paused) => {
this._frontend.pauseStateChanged({ paused });
});
recorder.on(RecorderEvent.UserSourcesChanged, (sources, pausedSourceId) => {
this._onUserSourcesChanged(sources, pausedSourceId);
});
recorder.on(RecorderEvent.ElementPicked, (elementInfo, userGesture) => {
if (userGesture)
this._page.bringToFront(nullProgress).catch(() => {
});
this._frontend.elementPicked({ elementInfo, userGesture });
});
recorder.on(RecorderEvent.CallLogsUpdated, (callLogs) => {
this._frontend.callLogsUpdated({ callLogs });
});
}
_onActionAdded(action) {
this._actions.push(action);
this._updateActions("reveal");
}
_onSignalAdded(signal) {
const lastAction = this._actions.findLast((a) => a.frame.pageGuid === signal.frame.pageGuid);
if (lastAction)
lastAction.action.signals.push(signal.signal);
this._updateActions();
}
_onUserSourcesChanged(sources, pausedSourceId) {
if (!sources.length && !this._userSources.length)
return;
this._userSources = sources;
this._pushAllSources();
this._revealSource(pausedSourceId);
}
_pushAllSources() {
const sources = [...this._userSources, ...this._recorderSources];
this._frontend.sourcesUpdated({ sources });
}
_revealSource(sourceId) {
if (!sourceId)
return;
this._frontend.sourceRevealRequested({ sourceId });
}
_updateActions(reveal) {
const recorderSources = [];
const actions = collapseActions(this._actions);
let revealSourceId;
for (const languageGenerator of languageSet()) {
const { header, footer, actionTexts, text: text2 } = generateCode(actions, languageGenerator, this._languageGeneratorOptions);
const source8 = {
isRecorded: true,
label: languageGenerator.name,
group: languageGenerator.groupName,
id: languageGenerator.id,
text: text2,
header,
footer,
actions: actionTexts,
language: languageGenerator.highlighter,
highlight: []
};
source8.revealLine = text2.split("\n").length - 1;
recorderSources.push(source8);
if (languageGenerator.id === this._primaryGeneratorId)
this._throttledOutputFile?.setContent(source8.text);
if (reveal === "reveal" && source8.id === this._selectedGeneratorId)
revealSourceId = source8.id;
}
this._recorderSources = recorderSources;
this._pushAllSources();
this._revealSource(revealSourceId);
}
};
ProgrammaticRecorderApp = class {
static async run(inspectedContext, recorder, browserName, params2) {
let lastAction = null;
const languages = [...languageSet()];
const languageGeneratorOptions = {
browserName,
launchOptions: { headless: false, ...params2.launchOptions, tracesDir: void 0 },
contextOptions: { ...params2.contextOptions },
deviceName: params2.device,
saveStorage: params2.saveStorage
};
const languageGenerator = languages.find((l) => l.id === params2.language) ?? languages.find((l) => l.id === "playwright-test");
recorder.on(RecorderEvent.ActionAdded, (action) => {
const page = findPageByGuid(inspectedContext, action.frame.pageGuid);
if (!page)
return;
const { actionTexts } = generateCode([action], languageGenerator, languageGeneratorOptions);
if (!lastAction || !shouldMergeAction(action, lastAction))
inspectedContext.emit(BrowserContext.Events.RecorderEvent, { event: "actionAdded", data: action, page, code: actionTexts.join("\n") });
else
inspectedContext.emit(BrowserContext.Events.RecorderEvent, { event: "actionUpdated", data: action, page, code: actionTexts.join("\n") });
lastAction = action;
});
recorder.on(RecorderEvent.SignalAdded, (signal) => {
const page = findPageByGuid(inspectedContext, signal.frame.pageGuid);
if (!page)
return;
inspectedContext.emit(BrowserContext.Events.RecorderEvent, { event: "signalAdded", data: signal, page, code: "" });
});
}
};
recorderAppSymbol = Symbol("recorderApp");
}
});
// packages/playwright-core/src/server/selectors.ts
var Selectors;
var init_selectors = __esm({
"packages/playwright-core/src/server/selectors.ts"() {
"use strict";
init_selectorParser();
init_crypto();
Selectors = class {
constructor(engines, testIdAttributeName2) {
this.guid = `selectors@${createGuid()}`;
this._builtinEngines = /* @__PURE__ */ new Set([
"css",
"css:light",
"xpath",
"xpath:light",
"_react",
"_vue",
"text",
"text:light",
"id",
"id:light",
"data-testid",
"data-testid:light",
"data-test-id",
"data-test-id:light",
"data-test",
"data-test:light",
"nth",
"visible",
"internal:control",
"internal:has",
"internal:has-not",
"internal:has-text",
"internal:has-not-text",
"internal:and",
"internal:or",
"internal:chain",
"role",
"internal:attr",
"internal:label",
"internal:text",
"internal:role",
"internal:testid",
"internal:describe",
"aria-ref"
]);
this._builtinEnginesInMainWorld = /* @__PURE__ */ new Set([
"_react",
"_vue"
]);
this._engines = /* @__PURE__ */ new Map();
this._testIdAttributeName = testIdAttributeName2 ?? "data-testid";
for (const engine of engines)
this.register(engine);
}
register(engine) {
if (!engine.name.match(/^[a-zA-Z_0-9-]+$/))
throw new Error("Selector engine name may only contain [a-zA-Z0-9_] characters");
if (this._builtinEngines.has(engine.name) || engine.name === "zs" || engine.name === "zs:light")
throw new Error(`"${engine.name}" is a predefined selector engine`);
if (this._engines.has(engine.name))
throw new Error(`"${engine.name}" selector engine has been already registered`);
this._engines.set(engine.name, engine);
}
testIdAttributeName() {
return this._testIdAttributeName;
}
setTestIdAttributeName(testIdAttributeName2) {
this._testIdAttributeName = testIdAttributeName2;
}
parseSelector(selector, strict) {
const parsed = typeof selector === "string" ? parseSelector(selector) : selector;
let needsMainWorld = false;
visitAllSelectorParts(parsed, (part) => {
const name = part.name;
const custom = this._engines.get(name);
if (!custom && !this._builtinEngines.has(name))
throw new InvalidSelectorError(`Unknown engine "${name}" while parsing selector ${stringifySelector(parsed)}`);
if (custom && !custom.contentScript)
needsMainWorld = true;
if (this._builtinEnginesInMainWorld.has(name))
needsMainWorld = true;
});
return {
parsed,
world: needsMainWorld ? "main" : "utility",
strict
};
}
};
}
});
// packages/playwright-core/src/generated/storageScriptSource.ts
var source6;
var init_storageScriptSource = __esm({
"packages/playwright-core/src/generated/storageScriptSource.ts"() {
"use strict";
source6 = '\nvar __commonJS = obj => {\n let required = false;\n let result;\n return function __require() {\n if (!required) {\n required = true;\n let fn;\n for (const name in obj) { fn = obj[name]; break; }\n const module = { exports: {} };\n fn(module.exports, module);\n result = module.exports;\n }\n return result;\n }\n};\nvar __export = (target, all) => {for (var name in all) target[name] = all[name];};\nvar __toESM = mod => ({ ...mod, \'default\': mod });\nvar __toCommonJS = mod => ({ ...mod, __esModule: true });\n\n\n// packages/injected/src/storageScript.ts\nvar storageScript_exports = {};\n__export(storageScript_exports, {\n StorageScript: () => StorageScript\n});\nmodule.exports = __toCommonJS(storageScript_exports);\n\n// packages/isomorphic/utilityScriptSerializers.ts\nfunction isRegExp(obj) {\n try {\n return obj instanceof RegExp || Object.prototype.toString.call(obj) === "[object RegExp]";\n } catch (error) {\n return false;\n }\n}\nfunction isDate(obj) {\n try {\n return obj instanceof Date || Object.prototype.toString.call(obj) === "[object Date]";\n } catch (error) {\n return false;\n }\n}\nfunction isURL(obj) {\n try {\n return obj instanceof URL || Object.prototype.toString.call(obj) === "[object URL]";\n } catch (error) {\n return false;\n }\n}\nfunction isError(obj) {\n var _a;\n try {\n return obj instanceof Error || obj && ((_a = Object.getPrototypeOf(obj)) == null ? void 0 : _a.name) === "Error";\n } catch (error) {\n return false;\n }\n}\nfunction isTypedArray(obj, constructor) {\n try {\n return obj instanceof constructor || Object.prototype.toString.call(obj) === `[object ${constructor.name}]`;\n } catch (error) {\n return false;\n }\n}\nfunction isArrayBuffer(obj) {\n try {\n return obj instanceof ArrayBuffer || Object.prototype.toString.call(obj) === "[object ArrayBuffer]";\n } catch (error) {\n return false;\n }\n}\nvar typedArrayConstructors = {\n i8: Int8Array,\n ui8: Uint8Array,\n ui8c: Uint8ClampedArray,\n i16: Int16Array,\n ui16: Uint16Array,\n i32: Int32Array,\n ui32: Uint32Array,\n // TODO: add Float16Array once it\'s in baseline\n f32: Float32Array,\n f64: Float64Array,\n bi64: BigInt64Array,\n bui64: BigUint64Array\n};\nfunction typedArrayToBase64(array) {\n if ("toBase64" in array)\n return array.toBase64();\n const binary = Array.from(new Uint8Array(array.buffer, array.byteOffset, array.byteLength)).map((b) => String.fromCharCode(b)).join("");\n return btoa(binary);\n}\nfunction base64ToTypedArray(base64, TypedArrayConstructor) {\n const binary = atob(base64);\n const bytes = new Uint8Array(binary.length);\n for (let i = 0; i < binary.length; i++)\n bytes[i] = binary.charCodeAt(i);\n return new TypedArrayConstructor(bytes.buffer);\n}\nfunction parseEvaluationResultValue(value, handles = [], refs = /* @__PURE__ */ new Map()) {\n if (Object.is(value, void 0))\n return void 0;\n if (typeof value === "object" && value) {\n if ("ref" in value)\n return refs.get(value.ref);\n if ("v" in value) {\n if (value.v === "undefined")\n return void 0;\n if (value.v === "null")\n return null;\n if (value.v === "NaN")\n return NaN;\n if (value.v === "Infinity")\n return Infinity;\n if (value.v === "-Infinity")\n return -Infinity;\n if (value.v === "-0")\n return -0;\n return void 0;\n }\n if ("d" in value) {\n return new Date(value.d);\n }\n if ("u" in value)\n return new URL(value.u);\n if ("bi" in value)\n return BigInt(value.bi);\n if ("e" in value) {\n const error = new Error(value.e.m);\n error.name = value.e.n;\n error.stack = value.e.s;\n return error;\n }\n if ("r" in value)\n return new RegExp(value.r.p, value.r.f);\n if ("a" in value) {\n const result = [];\n refs.set(value.id, result);\n for (const a of value.a)\n result.push(parseEvaluationResultValue(a, handles, refs));\n return result;\n }\n if ("o" in value) {\n const result = {};\n refs.set(value.id, result);\n for (const { k, v } of value.o) {\n if (k === "__proto__")\n continue;\n result[k] = parseEvaluationResultValue(v, handles, refs);\n }\n return result;\n }\n if ("h" in value)\n return handles[value.h];\n if ("ta" in value)\n return base64ToTypedArray(value.ta.b, typedArrayConstructors[value.ta.k]);\n if ("ab" in value)\n return base64ToTypedArray(value.ab.b, Uint8Array).buffer;\n }\n return value;\n}\nfunction serializeAsCallArgument(value, handleSerializer) {\n return serialize(value, handleSerializer, { visited: /* @__PURE__ */ new Map(), lastId: 0 });\n}\nfunction serialize(value, handleSerializer, visitorInfo) {\n if (value && typeof value === "object") {\n if (typeof globalThis.Window === "function" && value instanceof globalThis.Window)\n return "ref: <Window>";\n if (typeof globalThis.Document === "function" && value instanceof globalThis.Document)\n return "ref: <Document>";\n if (typeof globalThis.Node === "function" && value instanceof globalThis.Node)\n return "ref: <Node>";\n }\n return innerSerialize(value, handleSerializer, visitorInfo);\n}\nfunction innerSerialize(value, handleSerializer, visitorInfo) {\n var _a;\n const result = handleSerializer(value);\n if ("fallThrough" in result)\n value = result.fallThrough;\n else\n return result;\n if (typeof value === "symbol")\n return { v: "undefined" };\n if (Object.is(value, void 0))\n return { v: "undefined" };\n if (Object.is(value, null))\n return { v: "null" };\n if (Object.is(value, NaN))\n return { v: "NaN" };\n if (Object.is(value, Infinity))\n return { v: "Infinity" };\n if (Object.is(value, -Infinity))\n return { v: "-Infinity" };\n if (Object.is(value, -0))\n return { v: "-0" };\n if (typeof value === "boolean")\n return value;\n if (typeof value === "number")\n return value;\n if (typeof value === "string")\n return value;\n if (typeof value === "bigint")\n return { bi: value.toString() };\n if (isError(value)) {\n let stack;\n if ((_a = value.stack) == null ? void 0 : _a.startsWith(value.name + ": " + value.message)) {\n stack = value.stack;\n } else {\n stack = `${value.name}: ${value.message}\n${value.stack}`;\n }\n return { e: { n: value.name, m: value.message, s: stack } };\n }\n if (isDate(value))\n return { d: value.toJSON() };\n if (isURL(value))\n return { u: value.toJSON() };\n if (isRegExp(value))\n return { r: { p: value.source, f: value.flags } };\n for (const [k, ctor] of Object.entries(typedArrayConstructors)) {\n if (isTypedArray(value, ctor))\n return { ta: { b: typedArrayToBase64(value), k } };\n }\n if (isArrayBuffer(value))\n return { ab: { b: typedArrayToBase64(new Uint8Array(value)) } };\n const id = visitorInfo.visited.get(value);\n if (id)\n return { ref: id };\n if (Array.isArray(value)) {\n const a = [];\n const id2 = ++visitorInfo.lastId;\n visitorInfo.visited.set(value, id2);\n for (let i = 0; i < value.length; ++i)\n a.push(serialize(value[i], handleSerializer, visitorInfo));\n return { a, id: id2 };\n }\n if (typeof value === "object") {\n const o = [];\n const id2 = ++visitorInfo.lastId;\n visitorInfo.visited.set(value, id2);\n for (const name of Object.keys(value)) {\n let item;\n try {\n item = value[name];\n } catch (e) {\n continue;\n }\n if (name === "toJSON" && typeof item === "function")\n o.push({ k: name, v: { o: [], id: 0 } });\n else\n o.push({ k: name, v: serialize(item, handleSerializer, visitorInfo) });\n }\n let jsonWrapper;\n try {\n if (o.length === 0 && value.toJSON && typeof value.toJSON === "function")\n jsonWrapper = { value: value.toJSON() };\n } catch (e) {\n }\n if (jsonWrapper)\n return innerSerialize(jsonWrapper.value, handleSerializer, visitorInfo);\n return { o, id: id2 };\n }\n}\n\n// packages/injected/src/storageScript.ts\nvar StorageScript = class {\n constructor(isFirefox) {\n this._isFirefox = isFirefox;\n this._global = globalThis;\n }\n _idbRequestToPromise(request) {\n return new Promise((resolve, reject) => {\n request.addEventListener("success", () => resolve(request.result));\n request.addEventListener("error", () => reject(request.error));\n });\n }\n _isPlainObject(v) {\n const ctor = v == null ? void 0 : v.constructor;\n if (this._isFirefox) {\n const constructorImpl = ctor == null ? void 0 : ctor.toString();\n if ((constructorImpl == null ? void 0 : constructorImpl.startsWith("function Object() {")) && (constructorImpl == null ? void 0 : constructorImpl.includes("[native code]")))\n return true;\n }\n return ctor === Object;\n }\n _trySerialize(value) {\n let trivial = true;\n const encoded = serializeAsCallArgument(value, (v) => {\n const isTrivial = this._isPlainObject(v) || Array.isArray(v) || typeof v === "string" || typeof v === "number" || typeof v === "boolean" || Object.is(v, null);\n if (!isTrivial)\n trivial = false;\n return { fallThrough: v };\n });\n if (trivial)\n return { trivial: value };\n return { encoded };\n }\n async _collectDB(dbInfo) {\n if (!dbInfo.name)\n throw new Error("Database name is empty");\n if (!dbInfo.version)\n throw new Error("Database version is unset");\n const db = await this._idbRequestToPromise(indexedDB.open(dbInfo.name));\n if (db.objectStoreNames.length === 0)\n return { name: dbInfo.name, version: dbInfo.version, stores: [] };\n const transaction = db.transaction(db.objectStoreNames, "readonly");\n const stores = await Promise.all([...db.objectStoreNames].map(async (storeName) => {\n const objectStore = transaction.objectStore(storeName);\n const keys = await this._idbRequestToPromise(objectStore.getAllKeys());\n const records = await Promise.all(keys.map(async (key) => {\n const record = {};\n if (objectStore.keyPath === null) {\n const { encoded: encoded2, trivial: trivial2 } = this._trySerialize(key);\n if (trivial2)\n record.key = trivial2;\n else\n record.keyEncoded = encoded2;\n }\n const value = await this._idbRequestToPromise(objectStore.get(key));\n const { encoded, trivial } = this._trySerialize(value);\n if (trivial)\n record.value = trivial;\n else\n record.valueEncoded = encoded;\n return record;\n }));\n const indexes = [...objectStore.indexNames].map((indexName) => {\n const index = objectStore.index(indexName);\n return {\n name: index.name,\n keyPath: typeof index.keyPath === "string" ? index.keyPath : void 0,\n keyPathArray: Array.isArray(index.keyPath) ? index.keyPath : void 0,\n multiEntry: index.multiEntry,\n unique: index.unique\n };\n });\n return {\n name: storeName,\n records,\n indexes,\n autoIncrement: objectStore.autoIncrement,\n keyPath: typeof objectStore.keyPath === "string" ? objectStore.keyPath : void 0,\n keyPathArray: Array.isArray(objectStore.keyPath) ? objectStore.keyPath : void 0\n };\n }));\n return {\n name: dbInfo.name,\n version: dbInfo.version,\n stores\n };\n }\n async collect(recordIndexedDB) {\n const localStorage = Object.keys(this._global.localStorage).map((name) => ({ name, value: this._global.localStorage.getItem(name) }));\n if (!recordIndexedDB)\n return { localStorage };\n try {\n const databases = await this._global.indexedDB.databases();\n const indexedDB2 = await Promise.all(databases.map((db) => this._collectDB(db)));\n return { localStorage, indexedDB: indexedDB2 };\n } catch (e) {\n throw new Error("Unable to serialize IndexedDB: " + e.message);\n }\n }\n async _restoreDB(dbInfo) {\n const openRequest = this._global.indexedDB.open(dbInfo.name, dbInfo.version);\n openRequest.addEventListener("upgradeneeded", () => {\n var _a, _b;\n const db2 = openRequest.result;\n for (const store of dbInfo.stores) {\n const objectStore = db2.createObjectStore(store.name, { autoIncrement: store.autoIncrement, keyPath: (_a = store.keyPathArray) != null ? _a : store.keyPath });\n for (const index of store.indexes)\n objectStore.createIndex(index.name, (_b = index.keyPathArray) != null ? _b : index.keyPath, { unique: index.unique, multiEntry: index.multiEntry });\n }\n });\n const db = await this._idbRequestToPromise(openRequest);\n if (db.objectStoreNames.length === 0)\n return;\n const transaction = db.transaction(db.objectStoreNames, "readwrite");\n await Promise.all(dbInfo.stores.map(async (store) => {\n const objectStore = transaction.objectStore(store.name);\n await Promise.all(store.records.map(async (record) => {\n var _a, _b;\n await this._idbRequestToPromise(\n objectStore.add(\n (_a = record.value) != null ? _a : parseEvaluationResultValue(record.valueEncoded),\n (_b = record.key) != null ? _b : parseEvaluationResultValue(record.keyEncoded)\n )\n );\n }));\n }));\n }\n async restore(originState) {\n var _a, _b, _c;\n const registrations = this._global.navigator.serviceWorker ? await this._global.navigator.serviceWorker.getRegistrations() : [];\n await Promise.all(registrations.map(async (r) => {\n if (!r.installing && !r.waiting && !r.active)\n r.unregister().catch(() => {\n });\n else\n await r.unregister().catch(() => {\n });\n }));\n try {\n for (const db of await ((_b = (_a = this._global.indexedDB).databases) == null ? void 0 : _b.call(_a)) || []) {\n if (db.name)\n this._global.indexedDB.deleteDatabase(db.name);\n }\n await Promise.all(((_c = originState == null ? void 0 : originState.indexedDB) != null ? _c : []).map((dbInfo) => this._restoreDB(dbInfo)));\n } catch (e) {\n throw new Error("Unable to restore IndexedDB: " + e.message);\n }\n this._global.sessionStorage.clear();\n this._global.localStorage.clear();\n for (const { name, value } of (originState == null ? void 0 : originState.localStorage) || [])\n this._global.localStorage.setItem(name, value);\n }\n};\n';
}
});
// packages/playwright-core/src/server/browserContext.ts
function validateBrowserContextOptions(options2, browserOptions) {
if (options2.noDefaultViewport && options2.deviceScaleFactor !== void 0)
throw new Error(`"deviceScaleFactor" option is not supported with null "viewport"`);
if (options2.noDefaultViewport && !!options2.isMobile)
throw new Error(`"isMobile" option is not supported with null "viewport"`);
if (options2.acceptDownloads === void 0 && browserOptions.name !== "electron")
options2.acceptDownloads = "accept";
else if (options2.acceptDownloads === void 0 && browserOptions.name === "electron")
options2.acceptDownloads = "internal-browser-default";
if (!options2.viewport && !options2.noDefaultViewport)
options2.viewport = { width: 1280, height: 720 };
if (options2.proxy)
options2.proxy = normalizeProxySettings(options2.proxy);
verifyGeolocation(options2.geolocation);
}
function verifyGeolocation(geolocation) {
if (!geolocation)
return;
geolocation.accuracy = geolocation.accuracy || 0;
const { longitude, latitude, accuracy } = geolocation;
if (longitude < -180 || longitude > 180)
throw new Error(`geolocation.longitude: precondition -180 <= LONGITUDE <= 180 failed.`);
if (latitude < -90 || latitude > 90)
throw new Error(`geolocation.latitude: precondition -90 <= LATITUDE <= 90 failed.`);
if (accuracy < 0)
throw new Error(`geolocation.accuracy: precondition 0 <= ACCURACY failed.`);
}
function verifyClientCertificates(clientCertificates) {
if (!clientCertificates)
return;
for (const cert of clientCertificates) {
if (!cert.origin)
throw new Error(`clientCertificates.origin is required`);
if (!cert.cert && !cert.key && !cert.passphrase && !cert.pfx)
throw new Error("None of cert, key, passphrase or pfx is specified");
if (cert.cert && !cert.key)
throw new Error("cert is specified without key");
if (!cert.cert && cert.key)
throw new Error("key is specified without cert");
if (cert.pfx && (cert.cert || cert.key))
throw new Error("pfx is specified together with cert, key or passphrase");
}
}
function normalizeProxySettings(proxy) {
let { server, bypass } = proxy;
let url2;
try {
url2 = new URL(server);
if (!url2.host || !url2.protocol)
url2 = new URL("http://" + server);
} catch (e) {
url2 = new URL("http://" + server);
}
if (url2.protocol === "socks4:" && (proxy.username || proxy.password))
throw new Error(`Socks4 proxy protocol does not support authentication`);
if (url2.protocol === "socks5:" && (proxy.username || proxy.password))
throw new Error(`Browser does not support socks5 proxy authentication`);
server = url2.protocol + "//" + url2.host;
if (bypass)
bypass = bypass.split(",").map((t) => t.trim()).join(",");
return { ...proxy, server, bypass };
}
var import_fs31, BrowserContextEvent, BrowserContext, paramsThatAllowContextReuse, defaultNewContextParamValues;
var init_browserContext = __esm({
"packages/playwright-core/src/server/browserContext.ts"() {
"use strict";
import_fs31 = __toESM(require("fs"));
init_stackTrace();
init_debug();
init_clock();
init_debugger();
init_dialog();
init_fetch();
init_helper();
init_instrumentation();
init_network2();
init_page();
init_page();
init_recorderApp();
init_selectors();
init_tracing();
init_storageScriptSource();
init_progress();
BrowserContextEvent = {
Console: "console",
Close: "close",
Page: "page",
// Can't use just 'error' due to node.js special treatment of error events.
// @see https://nodejs.org/api/events.html#events_error_events
PageError: "pageerror",
Request: "request",
Response: "response",
RequestFailed: "requestfailed",
RequestFinished: "requestfinished",
RequestAborted: "requestaborted",
RequestFulfilled: "requestfulfilled",
RequestContinued: "requestcontinued",
BeforeClose: "beforeclose",
RecorderEvent: "recorderevent",
PageClosed: "pageclosed",
InternalFrameNavigatedToNewDocument: "internalframenavigatedtonewdocument"
};
BrowserContext = class _BrowserContext extends SdkObject {
constructor(browser, options2, browserContextId) {
super(browser, "browser-context");
this._pageBindings = /* @__PURE__ */ new Map();
this.requestInterceptors = [];
this._closedStatus = "open";
this._permissions = /* @__PURE__ */ new Map();
this._downloads = /* @__PURE__ */ new Set();
this._origins = /* @__PURE__ */ new Set();
this._tempDirs = [];
this._creatingStorageStatePage = false;
this.initScripts = [];
this._routesInFlight = /* @__PURE__ */ new Set();
this._consoleApiExposed = false;
this.attribution.context = this;
this._browser = browser;
this._options = options2;
this._browserContextId = browserContextId;
this._isPersistentContext = !browserContextId;
this._closePromise = new Promise((fulfill) => this._closePromiseFulfill = fulfill);
this._selectors = new Selectors(options2.selectorEngines || [], options2.testIdAttributeName);
this.fetchRequest = new BrowserContextAPIRequestContext(this);
this.tracing = new Tracing(this, browser.options.tracesDir);
this.clock = new Clock(this);
this.dialogManager = new DialogManager(this.instrumentation);
}
static {
this.Events = BrowserContextEvent;
}
isPersistentContext() {
return this._isPersistentContext;
}
selectors() {
return this._selectors;
}
async initialize() {
if (this.attribution.playwright.options.isInternalPlaywright)
return;
this._debugger = new Debugger(this);
const shouldEnableDebugger = !this.attribution.playwright.options.isServer && (isUnderTest() || !!this._browser.options.headful);
if (shouldEnableDebugger) {
this._debugger.setPauseAt();
this._debugger.on(Debugger.Events.PausedStateChanged, () => {
if (this._debugger.isPaused())
RecorderApp.showInspectorNoReply(this);
});
}
if (debugMode() === "inspector") {
this._debugger.setPauseAt({ next: true });
await RecorderApp.show(this, { pauseOnNextStatement: true });
}
if (debugMode() === "console")
await this._exposeConsoleApi();
if (this._options.serviceWorkers === "block")
await this.addInitScript(nullProgress, `
if (navigator.serviceWorker) navigator.serviceWorker.register = async () => { console.warn('Service Worker registration blocked by Playwright'); };
`);
if (this._options.permissions)
await this.grantPermissions(this._options.permissions);
}
debugger() {
return this._debugger;
}
async exposeConsoleApi(progress2) {
await progress2.race(this._exposeConsoleApi());
}
async _exposeConsoleApi() {
if (this._consoleApiExposed)
return;
this._consoleApiExposed = true;
await this.extendInjectedScript(`
function installConsoleApi(injectedScript) { injectedScript.consoleApi.install(); }
module.exports = { default: () => installConsoleApi };
`);
}
canResetForReuse() {
if (this._closedStatus !== "open")
return false;
return true;
}
static reusableContextHash(params2) {
const paramsCopy = { ...params2 };
if (paramsCopy.selectorEngines?.length === 0)
delete paramsCopy.selectorEngines;
for (const k of Object.keys(paramsCopy)) {
const key = k;
if (paramsCopy[key] === defaultNewContextParamValues[key])
delete paramsCopy[key];
}
for (const key of paramsThatAllowContextReuse)
delete paramsCopy[key];
return JSON.stringify(paramsCopy);
}
async resetForReuse(progress2, params2) {
await this.tracing.resetForReuse(progress2);
if (params2) {
for (const key of paramsThatAllowContextReuse)
this._options[key] = params2[key];
if (params2.testIdAttributeName)
this.selectors().setTestIdAttributeName(params2.testIdAttributeName);
}
let page = this.pages()[0];
const otherPages = this.possiblyUninitializedPages().filter((p) => p !== page);
for (const p of otherPages)
await p.close(progress2);
if (page && page.hasCrashed()) {
await page.close(progress2);
page = void 0;
}
await page?.mainFrame().gotoImpl(progress2, "about:blank", {});
await this.clock.uninstall(progress2);
await progress2.race(this.setUserAgent(this._options.userAgent));
await progress2.race(this.doUpdateDefaultEmulatedMedia());
await progress2.race(this.doUpdateDefaultViewport());
await this.setStorageState(progress2, this._options.storageState, "resetForReuse");
await page?.resetForReuse(progress2);
}
browserClosed() {
for (const page of this.pages())
page._didClose();
this._didCloseInternal();
}
_didCloseInternal() {
if (this._closedStatus === "closed") {
return;
}
this._clientCertificatesProxy?.close().catch(() => {
});
this.tracing.abort();
if (this._isPersistentContext)
this.onClosePersistent();
this._closePromiseFulfill(new Error("Context closed"));
this.emit(_BrowserContext.Events.Close);
}
pages() {
return this.possiblyUninitializedPages().filter((page) => page.initializedOrUndefined());
}
async cookies(progress2, urls = []) {
return await progress2.race(this._cookies(urls));
}
async _cookies(urls = []) {
if (urls && !Array.isArray(urls))
urls = [urls];
return await this.doGetCookies(urls);
}
async clearCookies(options2) {
const currentCookies = await this._cookies();
await this.doClearCookies();
const matches = (cookie, prop, value2) => {
if (!value2)
return true;
if (value2 instanceof RegExp) {
value2.lastIndex = 0;
return value2.test(cookie[prop]);
}
return cookie[prop] === value2;
};
const cookiesToReadd = currentCookies.filter((cookie) => {
return !matches(cookie, "name", options2.name) || !matches(cookie, "domain", options2.domain) || !matches(cookie, "path", options2.path);
});
await this.addCookies(cookiesToReadd);
}
setHTTPCredentials(progress2, httpCredentials) {
return progress2.race(this.innerSetHTTPCredentials(httpCredentials));
}
innerSetHTTPCredentials(httpCredentials) {
return this.doSetHTTPCredentials(httpCredentials);
}
getBindingClient(name) {
return this._pageBindings.get(name)?.forClient;
}
async exposePlaywrightBindingIfNeeded() {
this._playwrightBindingExposed ??= (async () => {
await this.doExposePlaywrightBinding();
this.bindingsInitScript = PageBinding.createInitScript(this);
this.initScripts.push(this.bindingsInitScript);
await this.doAddInitScript(this.bindingsInitScript);
await this.safeNonStallingEvaluateInAllFrames(this.bindingsInitScript.source, "main");
})();
return await this._playwrightBindingExposed;
}
needsPlaywrightBinding() {
return this._playwrightBindingExposed !== void 0;
}
async exposeBinding(progress2, name, playwrightBinding, forClient) {
if (this._pageBindings.has(name))
throw new Error(`Function "${name}" has been already registered`);
for (const page of this.pages()) {
if (page.getBinding(name))
throw new Error(`Function "${name}" has been already registered in one of the pages`);
}
await progress2.race(this.exposePlaywrightBindingIfNeeded());
const binding = new PageBinding(this, name, playwrightBinding);
binding.forClient = forClient;
this._pageBindings.set(name, binding);
try {
await progress2.race(this.doAddInitScript(binding.initScript));
await progress2.race(this.safeNonStallingEvaluateInAllFrames(binding.initScript.source, "main"));
return binding;
} catch (error) {
this._pageBindings.delete(name);
throw error;
}
}
async removeExposedBinding(binding) {
if (this._pageBindings.get(binding.name) !== binding)
return;
this._pageBindings.delete(binding.name);
await this.doRemoveInitScripts([binding.initScript]);
const cleanup = `{ ${binding.cleanupScript} };`;
await this.safeNonStallingEvaluateInAllFrames(cleanup, "main");
}
async grantPermissions(permissions, origin) {
let resolvedOrigin = "*";
if (origin) {
const url2 = new URL(origin);
resolvedOrigin = url2.origin;
}
const existing = new Set(this._permissions.get(resolvedOrigin) || []);
permissions.forEach((p) => existing.add(p));
const list = [...existing.values()];
this._permissions.set(resolvedOrigin, list);
await this.doGrantPermissions(resolvedOrigin, list);
}
async clearPermissions() {
this._permissions.clear();
await this.doClearPermissions();
}
async setExtraHTTPHeaders(progress2, headers) {
const oldHeaders = this._options.extraHTTPHeaders;
this._options.extraHTTPHeaders = headers;
try {
await progress2.race(this.doUpdateExtraHTTPHeaders());
} catch (error) {
this._options.extraHTTPHeaders = oldHeaders;
this.doUpdateExtraHTTPHeaders().catch(() => {
});
throw error;
}
}
async setOffline(progress2, offline) {
const oldOffline = this._options.offline;
this._options.offline = offline;
try {
await progress2.race(this.doUpdateOffline());
} catch (error) {
this._options.offline = oldOffline;
this.doUpdateOffline().catch(() => {
});
throw error;
}
}
async loadDefaultContextAsIs(progress2) {
if (!this.possiblyUninitializedPages().length) {
const waitForEvent2 = helper.waitForEvent(progress2, this, _BrowserContext.Events.Page);
await progress2.race(Promise.race([waitForEvent2.promise, this._closePromise]));
}
const page = this.possiblyUninitializedPages()[0];
if (!page)
return;
const pageOrError = await progress2.race(page.waitForInitializedOrError());
if (pageOrError instanceof Error)
throw pageOrError;
await page.mainFrame().waitForLoadState(progress2, "load");
return page;
}
async loadDefaultContext(progress2) {
const defaultPage = await this.loadDefaultContextAsIs(progress2);
if (!defaultPage)
return;
const browserName = this._browser.options.name;
if (this._options.isMobile && browserName === "chromium" || this._options.locale && browserName === "webkit") {
await this.newPage(progress2);
await defaultPage.close(progress2);
}
}
authenticateProxyViaHeader() {
const proxy = this._options.proxy || this._browser.options.proxy || { username: void 0, password: void 0 };
const { username, password } = proxy;
if (username) {
this._options.httpCredentials = { username, password };
const token = Buffer.from(`${username}:${password}`).toString("base64");
this._options.extraHTTPHeaders = mergeHeaders([
this._options.extraHTTPHeaders,
singleHeader("Proxy-Authorization", `Basic ${token}`)
]);
}
}
authenticateProxyViaCredentials() {
const proxy = this._options.proxy || this._browser.options.proxy;
if (!proxy)
return;
const { username, password } = proxy;
if (username)
this._options.httpCredentials = { username, password: password || "" };
}
async addInitScript(progress2, source8) {
return await progress2.race(this._internalAddInitScript(source8));
}
async _internalAddInitScript(source8) {
const initScript = new InitScript(this, source8);
this.initScripts.push(initScript);
try {
await this.doAddInitScript(initScript);
return initScript;
} catch (error) {
initScript.dispose().catch(() => {
});
throw error;
}
}
async removeInitScript(initScript) {
this.initScripts = this.initScripts.filter((script) => initScript !== script);
await this.doRemoveInitScripts([initScript]);
}
async addRequestInterceptor(progress2, handler) {
this.requestInterceptors.push(handler);
await progress2.race(this.doUpdateRequestInterception());
}
async removeRequestInterceptor(handler) {
const index = this.requestInterceptors.indexOf(handler);
if (index === -1)
return;
this.requestInterceptors.splice(index, 1);
await this.notifyRoutesInFlightAboutRemovedHandler(handler);
await this.doUpdateRequestInterception();
}
isClosingOrClosed() {
return this._closedStatus !== "open";
}
async _deleteAllDownloads() {
await Promise.all(Array.from(this._downloads).map((download) => download.artifact.deleteOnContextClose()));
}
async _deleteAllTempDirs() {
await Promise.all(this._tempDirs.map(async (dir) => await import_fs31.default.promises.unlink(dir).catch((e) => {
})));
}
setCustomCloseHandler(handler) {
this._customCloseHandler = handler;
}
async close(progress2, options2) {
if (this._closedStatus === "open") {
if (options2.reason)
this._closeReason = options2.reason;
this.emit(_BrowserContext.Events.BeforeClose);
this._closedStatus = "closing";
await progress2.race(this.tracing.flush());
await progress2.race(Promise.all(this.pages().map((page) => page.screencast.handlePageOrContextClose())));
if (this._customCloseHandler) {
await progress2.race(this._customCloseHandler());
} else {
const disposition = await progress2.race(this.doClose(options2.reason));
if (disposition === "close-browser")
await this._browser.close(progress2, { reason: options2.reason });
}
const promises = [];
promises.push(this._deleteAllDownloads());
promises.push(this._deleteAllTempDirs());
await progress2.race(Promise.all(promises));
if (!this._customCloseHandler)
this._didCloseInternal();
}
await this._closePromise;
}
async newPage(progress2, forStorageState) {
let page;
try {
this._creatingStorageStatePage = !!forStorageState;
page = await progress2.race(this.doCreateNewPage());
const pageOrError = await progress2.race(page.waitForInitializedOrError());
if (pageOrError instanceof Page) {
if (pageOrError.isClosed())
throw new Error("Page has been closed.");
return pageOrError;
}
throw pageOrError;
} catch (error) {
await page?.close(progress2, { reason: "Failed to create page" }).catch(() => {
});
throw error;
} finally {
this._creatingStorageStatePage = false;
}
}
addVisitedOrigin(origin) {
this._origins.add(origin);
}
async storageState(progress2, indexedDB = false) {
const result2 = {
cookies: await this.cookies(progress2),
origins: []
};
const originsToSave = new Set(this._origins);
const collectScript = `(() => {
const module = {};
${source6}
const script = new (module.exports.StorageScript())(${this._browser.options.name === "firefox"});
return script.collect(${indexedDB});
})()`;
for (const page of this.pages()) {
const origin = page.mainFrame().origin();
if (!origin || !originsToSave.has(origin))
continue;
try {
const storage = await progress2.race(page.mainFrame().nonStallingEvaluateInExistingContext(collectScript, "utility"));
if (storage.localStorage.length || storage.indexedDB?.length)
result2.origins.push({ origin, localStorage: storage.localStorage, indexedDB: storage.indexedDB });
originsToSave.delete(origin);
} catch {
}
}
if (originsToSave.size) {
const page = await this.newPage(
progress2,
true
/* forStorageState */
);
try {
await page.addRequestInterceptor(progress2, (route2) => {
route2.fulfill({ body: "<html></html>" }).catch(() => {
});
}, "prepend");
for (const origin of originsToSave) {
const frame = page.mainFrame();
await frame.gotoImpl(progress2, origin, {});
const storage = await frame.evaluateExpression(progress2, collectScript, { world: "utility" });
if (storage.localStorage.length || storage.indexedDB?.length)
result2.origins.push({ origin, localStorage: storage.localStorage, indexedDB: storage.indexedDB });
}
} finally {
await page.close(progress2);
}
}
return result2;
}
isCreatingStorageStatePage() {
return this._creatingStorageStatePage;
}
async setStorageState(progress2, state, mode) {
let page;
let interceptor;
try {
if (mode !== "initial") {
await progress2.race(this.clearCache());
await progress2.race(this.doClearCookies());
}
if (state?.cookies)
await progress2.race(this.addCookies(state.cookies));
const newOrigins = new Map(state?.origins?.map((p) => [p.origin, p]) || []);
const allOrigins = /* @__PURE__ */ new Set([...this._origins, ...newOrigins.keys()]);
if (allOrigins.size) {
if (mode === "resetForReuse")
page = this.pages()[0];
if (!page)
page = await this.newPage(
progress2,
mode !== "resetForReuse"
/* forStorageState */
);
interceptor = (route2) => {
route2.fulfill({ body: "<html></html>" }).catch(() => {
});
};
await page.addRequestInterceptor(progress2, interceptor, "prepend");
for (const origin of allOrigins) {
const frame = page.mainFrame();
await frame.gotoImpl(progress2, origin, {});
const restoreScript = `(() => {
const module = {};
${source6}
const script = new (module.exports.StorageScript())(${this._browser.options.name === "firefox"});
return script.restore(${JSON.stringify(newOrigins.get(origin))});
})()`;
await frame.evaluateExpression(progress2, restoreScript, { world: "utility" });
}
}
this._origins = /* @__PURE__ */ new Set([...newOrigins.keys()]);
} catch (error) {
rewriteErrorMessage(error, `Error setting storage state:
` + error.message);
throw error;
} finally {
if (mode !== "resetForReuse")
await page?.close(progress2);
else if (interceptor)
await page?.removeRequestInterceptor(interceptor);
}
}
async extendInjectedScript(source8, arg) {
const installInFrame = (frame) => frame.extendInjectedScript(source8, arg).catch(() => {
});
const installInPage = (page) => {
page.on(Page.Events.InternalFrameNavigatedToNewDocument, installInFrame);
return Promise.all(page.frames().map(installInFrame));
};
this.on(_BrowserContext.Events.Page, installInPage);
return Promise.all(this.pages().map(installInPage));
}
async safeNonStallingEvaluateInAllFrames(expression2, world, options2 = {}) {
await Promise.all(this.pages().map((page) => page.safeNonStallingEvaluateInAllFrames(expression2, world, options2)));
}
addRouteInFlight(route2) {
this._routesInFlight.add(route2);
}
removeRouteInFlight(route2) {
this._routesInFlight.delete(route2);
}
async notifyRoutesInFlightAboutRemovedHandler(handler) {
await Promise.all([...this._routesInFlight].map((route2) => route2.removeHandler(handler)));
}
};
paramsThatAllowContextReuse = [
"colorScheme",
"forcedColors",
"reducedMotion",
"contrast",
"screen",
"userAgent",
"viewport",
"testIdAttributeName"
];
defaultNewContextParamValues = {
noDefaultViewport: false,
ignoreHTTPSErrors: false,
javaScriptEnabled: true,
bypassCSP: false,
offline: false,
isMobile: false,
hasTouch: false,
acceptDownloads: "accept",
strictSelectors: false,
serviceWorkers: "allow",
locale: "en-US"
};
}
});
// packages/playwright-core/src/server/download.ts
var import_path32, Download;
var init_download = __esm({
"packages/playwright-core/src/server/download.ts"() {
"use strict";
import_path32 = __toESM(require("path"));
init_assert();
init_page();
init_artifact();
Download = class {
constructor(page, downloadsPath, uuid, url2, suggestedFilename, downloadFilename) {
const unaccessibleErrorMessage = page.browserContext._options.acceptDownloads === "deny" ? "Pass { acceptDownloads: true } when you are creating your browser context." : void 0;
const downloadPath = import_path32.default.join(downloadsPath, downloadFilename ?? uuid);
this.artifact = new Artifact(page, downloadPath, unaccessibleErrorMessage, () => this.cancel());
this._page = page;
this.url = url2;
this._uuid = uuid;
this._suggestedFilename = suggestedFilename;
page.browserContext._downloads.add(this);
if (suggestedFilename !== void 0)
this._fireDownloadEvent();
}
cancel() {
return this._page.browserContext.cancelDownload(this._uuid);
}
filenameSuggested(suggestedFilename) {
assert(this._suggestedFilename === void 0);
this._suggestedFilename = suggestedFilename;
this._fireDownloadEvent();
}
suggestedFilename() {
return this._suggestedFilename;
}
_fireDownloadEvent() {
this._page.instrumentation.onDownload(this._page, this);
this._page.emit(Page.Events.Download, this);
}
};
}
});
// packages/playwright-core/src/remote/serverTransport.ts
var import_events12, WebSocketServerTransport, SocketServerTransport;
var init_serverTransport = __esm({
"packages/playwright-core/src/remote/serverTransport.ts"() {
"use strict";
import_events12 = require("events");
WebSocketServerTransport = class {
constructor(ws3) {
this._ws = ws3;
}
send(message) {
this._ws.send(message);
}
close(reason) {
this._ws.close(reason?.code, reason?.reason);
}
on(event, handler) {
this._ws.on(event, handler);
}
isClosed() {
return this._ws.readyState === this._ws.CLOSING || this._ws.readyState === this._ws.CLOSED;
}
};
SocketServerTransport = class extends import_events12.EventEmitter {
constructor(socket) {
super();
this._closed = false;
this._pendingBuffers = [];
this._socket = socket;
socket.on("data", (buffer) => this._dispatch(buffer));
socket.on("close", () => {
this._closed = true;
super.emit("close");
});
socket.on("error", (error) => {
super.emit("error", error);
});
}
send(message) {
if (this._closed)
return;
this._socket.write(message);
this._socket.write("\0");
}
close(reason) {
if (this._closed)
return;
this._closed = true;
this._socket.end();
}
isClosed() {
return this._closed;
}
_dispatch(buffer) {
let end = buffer.indexOf("\0");
if (end === -1) {
this._pendingBuffers.push(buffer);
return;
}
this._pendingBuffers.push(buffer.slice(0, end));
const message = Buffer.concat(this._pendingBuffers).toString();
super.emit("message", message);
let start3 = end + 1;
end = buffer.indexOf("\0", start3);
while (end !== -1) {
super.emit("message", buffer.toString(void 0, start3, end));
start3 = end + 1;
end = buffer.indexOf("\0", start3);
}
this._pendingBuffers = [buffer.slice(start3)];
}
};
}
});
// packages/playwright-core/src/remote/playwrightPipeServer.ts
var import_net5, import_fs32, PlaywrightPipeServer;
var init_playwrightPipeServer = __esm({
"packages/playwright-core/src/remote/playwrightPipeServer.ts"() {
"use strict";
import_net5 = __toESM(require("net"));
import_fs32 = __toESM(require("fs"));
init_debugLogger();
init_network();
init_semaphore();
init_playwrightConnection();
init_serverTransport();
init_browser();
PlaywrightPipeServer = class {
constructor(browser) {
this._connections = /* @__PURE__ */ new Set();
this._connectionId = 0;
this._browser = browser;
browser.on(Browser.Events.Disconnected, () => this.close());
}
async listen(pipeName) {
if (!pipeName.startsWith("\\\\.\\pipe\\")) {
try {
import_fs32.default.unlinkSync(pipeName);
} catch {
}
}
this._server = import_net5.default.createServer((socket) => {
const id = String(++this._connectionId);
debugLogger.log("server", `[${id}] pipe client connected`);
const transport = new SocketServerTransport(socket);
const connection = new PlaywrightConnection(
new Semaphore(1),
transport,
false,
this._browser.attribution.playwright,
() => this._initPreLaunchedBrowserMode(id),
id
);
this._connections.add(connection);
transport.on("close", () => this._connections.delete(connection));
});
decorateServer(this._server);
await new Promise((resolve, reject) => {
this._server.listen(pipeName, () => resolve());
this._server.on("error", reject);
});
debugLogger.log("server", `Pipe server listening at ${pipeName}`);
}
async _initPreLaunchedBrowserMode(id) {
debugLogger.log("server", `[${id}] engaged pre-launched (browser) pipe mode`);
return {
preLaunchedBrowser: this._browser,
sharedBrowser: true,
denyLaunch: true
};
}
async close() {
if (!this._server)
return;
debugLogger.log("server", "closing pipe server");
for (const connection of this._connections)
await connection.close({ code: 1001, reason: "Server closing" });
this._connections.clear();
await new Promise((f) => this._server.close(() => f()));
this._server = void 0;
debugLogger.log("server", "closed pipe server");
}
};
}
});
// packages/playwright-core/src/remote/playwrightWebSocketServer.ts
var PlaywrightWebSocketServer;
var init_playwrightWebSocketServer = __esm({
"packages/playwright-core/src/remote/playwrightWebSocketServer.ts"() {
"use strict";
init_debugLogger();
init_wsServer();
init_semaphore();
init_playwrightConnection();
init_serverTransport();
init_browser();
PlaywrightWebSocketServer = class {
constructor(browser, path59) {
this._browser = browser;
browser.on(Browser.Events.Disconnected, () => this.close());
const semaphore = new Semaphore(Infinity);
this._wsServer = new WSServer({
onRequest: (request2, response2) => {
response2.end("Running");
},
onUpgrade: () => void 0,
onHeaders: () => {
},
onConnection: (request2, url2, ws3, id) => {
debugLogger.log("server", `[${id}] ws client connected`);
return new PlaywrightConnection(
semaphore,
new WebSocketServerTransport(ws3),
false,
this._browser.attribution.playwright,
() => this._initPreLaunchedBrowserMode(id),
id
);
}
});
}
async _initPreLaunchedBrowserMode(id) {
debugLogger.log("server", `[${id}] engaged pre-launched (browser) ws mode`);
return {
preLaunchedBrowser: this._browser,
sharedBrowser: true,
denyLaunch: true
};
}
async listen(port = 0, hostname, path59) {
return await this._wsServer.listen(port, hostname, path59 || "/");
}
async close() {
await this._wsServer.close();
}
};
}
});
// packages/playwright-core/src/serverRegistry.ts
async function canConnectTo(descriptor) {
if (!descriptor.endpoint)
return false;
if (descriptor.endpoint.startsWith("ws://") || descriptor.endpoint.startsWith("wss://")) {
return await new Promise((resolve) => {
const url2 = new URL(descriptor.endpoint);
const socket = import_net6.default.createConnection(Number(url2.port), url2.hostname, () => {
socket.destroy();
resolve(true);
});
socket.on("error", () => resolve(false));
});
}
return await new Promise((resolve) => {
const socket = import_net6.default.createConnection(descriptor.endpoint ?? descriptor.pipeName, () => {
socket.destroy();
resolve(true);
});
socket.on("error", () => resolve(false));
});
}
var import_events13, import_fs33, import_net6, import_path33, import_os16, chokidar, packageVersion, ServerRegistry, defaultCacheDirectory2, registryDirectory2, serverRegistry;
var init_serverRegistry = __esm({
"packages/playwright-core/src/serverRegistry.ts"() {
"use strict";
import_events13 = require("events");
import_fs33 = __toESM(require("fs"));
import_net6 = __toESM(require("net"));
import_path33 = __toESM(require("path"));
import_os16 = __toESM(require("os"));
init_package();
chokidar = require("./utilsBundle").chokidar;
packageVersion = packageJSON.version;
ServerRegistry = class extends import_events13.EventEmitter {
constructor() {
super(...arguments);
this._descriptors = /* @__PURE__ */ new Map();
this._watcherRefs = 0;
}
on(event, listener) {
return super.on(event, listener);
}
off(event, listener) {
return super.off(event, listener);
}
watch() {
this._watcherRefs++;
if (!this._watcher)
this._startWatcher();
let disposed = false;
return () => {
if (disposed)
return;
disposed = true;
this._watcherRefs--;
if (this._watcherRefs === 0)
this._stopWatcher();
};
}
ready() {
return this._ready ?? Promise.resolve();
}
async list() {
const ownWatcher = !this._watcher;
let dispose;
if (ownWatcher)
dispose = this.watch();
try {
await this._ready;
const statuses = await Promise.all(
[...this._descriptors.values()].map(async (descriptor) => {
const canConnect = await canConnectTo(descriptor);
return { descriptor, canConnect };
})
);
const result2 = /* @__PURE__ */ new Map();
for (const { descriptor, canConnect } of statuses) {
if (!canConnect) {
await import_fs33.default.promises.unlink(import_path33.default.join(this._browsersDir(), descriptor.browser.guid)).catch(() => {
});
continue;
}
const key = descriptor.workspaceDir ?? "";
let list = result2.get(key);
if (!list) {
list = [];
result2.set(key, list);
}
list.push(descriptor);
}
return result2;
} finally {
dispose?.();
}
}
async create(browser, endpoint) {
const file = import_path33.default.join(this._browsersDir(), browser.guid);
await import_fs33.default.promises.mkdir(this._browsersDir(), { recursive: true });
const descriptor = {
playwrightVersion: packageVersion,
playwrightLib: packageRoot,
title: endpoint.title,
browser,
endpoint: endpoint.endpoint,
workspaceDir: endpoint.workspaceDir
};
await import_fs33.default.promises.writeFile(file, JSON.stringify(descriptor, null, 2), "utf-8");
}
async delete(guid) {
const file = import_path33.default.join(this._browsersDir(), guid);
await import_fs33.default.promises.unlink(file).catch(() => {
});
}
async deleteUserData(guid) {
const filePath = import_path33.default.join(this._browsersDir(), guid);
const content = await import_fs33.default.promises.readFile(filePath, "utf-8");
const descriptor = JSON.parse(content);
if (descriptor.browser.userDataDir)
await import_fs33.default.promises.rm(descriptor.browser.userDataDir, { recursive: true, force: true });
await import_fs33.default.promises.unlink(filePath);
}
readDescriptor(guid) {
const cached = this._descriptors.get(guid);
if (cached)
return cached;
const filePath = import_path33.default.join(this._browsersDir(), guid);
const content = import_fs33.default.readFileSync(filePath, "utf-8");
return JSON.parse(content);
}
async find(name) {
const entries = await this.list();
for (const [, browsers] of entries) {
for (const browser of browsers) {
if (browser.title === name)
return browser;
}
}
return null;
}
_browsersDir() {
return process.env.PLAYWRIGHT_SERVER_REGISTRY || registryDirectory2;
}
_startWatcher() {
const dir = this._browsersDir();
try {
import_fs33.default.mkdirSync(dir, { recursive: true });
} catch {
}
const watcher = chokidar.watch(dir, {
ignoreInitial: false,
depth: 0,
awaitWriteFinish: { stabilityThreshold: 50, pollInterval: 20 }
});
this._watcher = watcher;
this._ready = new Promise((resolve, reject) => {
watcher.once("ready", () => resolve());
watcher.once("error", reject);
});
watcher.on("add", (file) => this._onAddOrChange(file, "added"));
watcher.on("change", (file) => this._onAddOrChange(file, "changed"));
watcher.on("unlink", (file) => {
const guid = import_path33.default.basename(file);
if (this._descriptors.delete(guid))
this.emit("removed", guid);
});
}
_onAddOrChange(file, event) {
const guid = import_path33.default.basename(file);
let descriptor;
try {
descriptor = JSON.parse(import_fs33.default.readFileSync(file, "utf-8"));
} catch {
return;
}
this._descriptors.set(guid, descriptor);
this.emit(event, descriptor);
}
_stopWatcher() {
const watcher = this._watcher;
this._watcher = void 0;
this._ready = void 0;
this._descriptors.clear();
void watcher?.close();
}
};
defaultCacheDirectory2 = (() => {
if (process.platform === "linux")
return process.env.XDG_CACHE_HOME || import_path33.default.join(import_os16.default.homedir(), ".cache");
if (process.platform === "darwin")
return import_path33.default.join(import_os16.default.homedir(), "Library", "Caches");
if (process.platform === "win32")
return process.env.LOCALAPPDATA || import_path33.default.join(import_os16.default.homedir(), "AppData", "Local");
throw new Error("Unsupported platform: " + process.platform);
})();
registryDirectory2 = import_path33.default.join(defaultCacheDirectory2, "ms-playwright", "b");
serverRegistry = new ServerRegistry();
}
});
// packages/playwright-core/src/server/browser.ts
function asClientLaunchOptions(serverOptions) {
return {
...serverOptions,
env: serverOptions.env ? Object.fromEntries(serverOptions.env.map(({ name, value: value2 }) => [name, value2])) : void 0
};
}
var import_fs34, Browser, BrowserServer;
var init_browser = __esm({
"packages/playwright-core/src/server/browser.ts"() {
"use strict";
import_fs34 = __toESM(require("fs"));
init_fileUtils();
init_crypto();
init_browserContext();
init_download();
init_instrumentation();
init_socksClientCertificatesInterceptor();
init_playwrightPipeServer();
init_playwrightWebSocketServer();
init_serverRegistry();
init_progress();
Browser = class _Browser extends SdkObject {
constructor(parent, options2) {
super(parent, "browser");
this._downloads = /* @__PURE__ */ new Map();
this._defaultContext = null;
this._startedClosing = false;
this._isCollocatedWithServer = true;
this.attribution.browser = this;
this.options = options2;
this.instrumentation.onBrowserOpen(this);
this._server = new BrowserServer(this);
}
static {
this.Events = {
Context: "context",
Disconnected: "disconnected"
};
}
sdkLanguage() {
return this.options.sdkLanguage || this.attribution.playwright.options.sdkLanguage;
}
async newContext(progress2, options2) {
validateBrowserContextOptions(options2, this.options);
let clientCertificatesProxy;
let context2;
try {
if (options2.clientCertificates?.length) {
clientCertificatesProxy = await ClientCertificatesProxy.create(progress2, options2);
options2 = { ...options2 };
options2.proxyOverride = clientCertificatesProxy.proxySettings();
options2.internalIgnoreHTTPSErrors = true;
}
context2 = await progress2.race(this.doCreateNewContext(options2));
context2._clientCertificatesProxy = clientCertificatesProxy;
if (options2.__testHookBeforeSetStorageState)
await progress2.race(options2.__testHookBeforeSetStorageState());
await context2.setStorageState(progress2, options2.storageState, "initial");
this.emit(_Browser.Events.Context, context2);
return context2;
} catch (error) {
await context2?.close(progress2, { reason: "Failed to create context" }).catch(() => {
});
await clientCertificatesProxy?.close().catch(() => {
});
throw error;
}
}
async newContextForReuse(progress2, params2) {
const hash = BrowserContext.reusableContextHash(params2);
if (!this._contextForReuse || hash !== this._contextForReuse.hash || !this._contextForReuse.context.canResetForReuse()) {
if (this._contextForReuse)
await this._contextForReuse.context.close(progress2, { reason: "Context reused" });
this._contextForReuse = { context: await this.newContext(progress2, params2), hash };
return this._contextForReuse.context;
}
await this._contextForReuse.context.resetForReuse(progress2, params2);
return this._contextForReuse.context;
}
contextForReuse() {
return this._contextForReuse?.context;
}
downloadCreated(page, uuid, url2, suggestedFilename, downloadFilename) {
const download = new Download(page, this.options.downloadsPath || "", uuid, url2, suggestedFilename, downloadFilename);
this._downloads.set(uuid, download);
}
downloadFilenameSuggested(uuid, suggestedFilename) {
const download = this._downloads.get(uuid);
if (!download)
return;
download.filenameSuggested(suggestedFilename);
}
downloadFinished(uuid, error) {
const download = this._downloads.get(uuid);
if (!download)
return;
download.artifact.reportFinished(error ? new Error(error) : void 0);
this._downloads.delete(uuid);
}
async startServer(progress2, title, options2) {
return await progress2.race(this._server.start(title, options2));
}
async stopServer(progress2) {
await progress2.race(this._server.stop());
}
didClose() {
for (const context2 of this.contexts())
context2.browserClosed();
if (this._defaultContext)
this._defaultContext.browserClosed();
this.stopServer(nullProgress).catch(() => {
});
this.emit(_Browser.Events.Disconnected);
this.instrumentation.onBrowserClose(this);
}
async close(progress2, options2) {
return await progress2.race(this._close(options2));
}
async _close(options2) {
if (!this._startedClosing) {
if (options2.reason)
this._closeReason = options2.reason;
this._startedClosing = true;
await this.options.browserProcess.close();
}
if (this.isConnected())
await new Promise((x) => this.once(_Browser.Events.Disconnected, x));
}
async killForTests(progress2) {
await progress2.race(this.options.browserProcess.kill());
}
};
BrowserServer = class {
constructor(browser) {
this._isStarted = false;
this._browser = browser;
}
async start(title, options2) {
if (this._isStarted)
throw new Error(`Server is already started.`);
this._isStarted = true;
let endpoint;
if (options2.host !== void 0 || options2.port !== void 0) {
this._wsServer = new PlaywrightWebSocketServer(this._browser, "/");
endpoint = await this._wsServer.listen(options2.port ?? 0, options2.host, "/" + createGuid());
} else {
this._pipeServer = new PlaywrightPipeServer(this._browser);
this._pipeSocketPath = await this._socketPath();
await this._pipeServer.listen(this._pipeSocketPath);
endpoint = this._pipeSocketPath;
}
const browserInfo2 = {
guid: this._browser.guid,
browserName: this._browser.options.browserType,
launchOptions: asClientLaunchOptions(this._browser.options.originalLaunchOptions),
userDataDir: this._browser.options.userDataDir
};
await serverRegistry.create(browserInfo2, {
title,
endpoint,
workspaceDir: options2.workspaceDir,
metadata: options2.metadata
});
return { endpoint };
}
async stop() {
if (!this._browser.options.userDataDir)
await serverRegistry.delete(this._browser.guid);
if (this._pipeSocketPath && process.platform !== "win32")
await import_fs34.default.promises.unlink(this._pipeSocketPath).catch(() => {
});
await this._pipeServer?.close();
await this._wsServer?.close();
this._pipeServer = void 0;
this._wsServer = void 0;
this._isStarted = false;
}
async _socketPath() {
return makeSocketPath("browser", this._browser.guid.slice(0, 14));
}
};
}
});
// packages/playwright-core/src/server/dispatchers/cdpSessionDispatcher.ts
var CDPSessionDispatcher;
var init_cdpSessionDispatcher = __esm({
"packages/playwright-core/src/server/dispatchers/cdpSessionDispatcher.ts"() {
"use strict";
init_dispatcher();
init_crConnection();
CDPSessionDispatcher = class extends Dispatcher {
constructor(scope, cdpSession) {
super(scope, cdpSession, "CDPSession", {});
this._type_CDPSession = true;
this.addObjectListener(CDPSession.Events.Event, ({ method, params: params2 }) => this._dispatchEvent("event", { method, params: params2 }));
this.addObjectListener(CDPSession.Events.Closed, () => {
this._dispatchEvent("close");
this._dispose();
});
}
async send(params2, progress2) {
return { result: await this._object.send(progress2, params2.method, params2.params) };
}
async detach(_, progress2) {
progress2.metadata.potentiallyClosesScope = true;
await this._object.detach(progress2);
}
};
}
});
// packages/playwright-core/src/server/dispatchers/debuggerDispatcher.ts
var DebuggerDispatcher;
var init_debuggerDispatcher = __esm({
"packages/playwright-core/src/server/dispatchers/debuggerDispatcher.ts"() {
"use strict";
init_protocolFormatter();
init_dispatcher();
init_debugger();
DebuggerDispatcher = class _DebuggerDispatcher extends Dispatcher {
constructor(scope, debugger_) {
super(scope, debugger_, "Debugger", {});
this._type_EventTarget = true;
this._type_Debugger = true;
this.addObjectListener(Debugger.Events.PausedStateChanged, () => {
this._dispatchEvent("pausedStateChanged", { pausedDetails: this._serializePausedDetails() });
});
this._dispatchEvent("pausedStateChanged", { pausedDetails: this._serializePausedDetails() });
}
static from(scope, debugger_) {
const result2 = scope.connection.existingDispatcher(debugger_);
return result2 || new _DebuggerDispatcher(scope, debugger_);
}
_serializePausedDetails() {
const details = this._object.pausedDetails();
if (!details)
return void 0;
const { metadata } = details;
return {
location: {
file: metadata.location?.file ?? "<unknown>",
line: metadata.location?.line,
column: metadata.location?.column
},
title: renderTitleForCall(metadata)
};
}
async requestPause(params2, progress2) {
this._object.requestPause(progress2);
}
async resume(params2, progress2) {
this._object.doResume(progress2);
}
async next(params2, progress2) {
this._object.next(progress2);
}
async runTo(params2, progress2) {
this._object.runTo(progress2, params2.location);
}
};
}
});
// packages/playwright-core/src/server/dispatchers/streamDispatcher.ts
var StreamSdkObject, StreamDispatcher;
var init_streamDispatcher = __esm({
"packages/playwright-core/src/server/dispatchers/streamDispatcher.ts"() {
"use strict";
init_manualPromise();
init_dispatcher();
init_instrumentation();
StreamSdkObject = class extends SdkObject {
constructor(parent, stream3) {
super(parent, "stream");
this.stream = stream3;
}
};
StreamDispatcher = class extends Dispatcher {
constructor(scope, stream3) {
super(scope, new StreamSdkObject(scope._object, stream3), "Stream", {});
this._type_Stream = true;
this._ended = false;
stream3.once("end", () => this._ended = true);
stream3.once("error", () => this._ended = true);
}
async read(params2, progress2) {
const stream3 = this._object.stream;
if (this._ended)
return { binary: Buffer.from("") };
if (!stream3.readableLength) {
const readyPromise = new ManualPromise();
const done = () => readyPromise.resolve();
stream3.on("readable", done);
stream3.on("end", done);
stream3.on("error", done);
try {
await progress2.race(readyPromise);
} finally {
stream3.off("readable", done);
stream3.off("end", done);
stream3.off("error", done);
}
}
const buffer = stream3.read(Math.min(stream3.readableLength, params2.size || stream3.readableLength));
return { binary: buffer || Buffer.from("") };
}
async close(params2, progress2) {
this._object.stream.destroy();
}
};
}
});
// packages/playwright-core/src/server/dispatchers/artifactDispatcher.ts
var import_fs35, ArtifactDispatcher;
var init_artifactDispatcher = __esm({
"packages/playwright-core/src/server/dispatchers/artifactDispatcher.ts"() {
"use strict";
import_fs35 = __toESM(require("fs"));
init_fileUtils();
init_dispatcher();
init_streamDispatcher();
ArtifactDispatcher = class _ArtifactDispatcher extends Dispatcher {
constructor(scope, artifact) {
super(scope, artifact, "Artifact", {
absolutePath: artifact.localPath()
});
this._type_Artifact = true;
}
static from(parentScope, artifact) {
return _ArtifactDispatcher.fromNullable(parentScope, artifact);
}
static fromNullable(parentScope, artifact) {
if (!artifact)
return void 0;
const result2 = parentScope.connection.existingDispatcher(artifact);
return result2 || new _ArtifactDispatcher(parentScope, artifact);
}
async pathAfterFinished(params2, progress2) {
const path59 = await this._object.localPathAfterFinished(progress2);
return { value: path59 };
}
async saveAs(params2, progress2) {
return await progress2.race(new Promise((resolve, reject) => {
this._object.saveAs(progress2, async (localPath, error) => {
if (error) {
reject(error);
return;
}
try {
await mkdirIfNeeded(params2.path);
await import_fs35.default.promises.copyFile(localPath, params2.path);
resolve();
} catch (e) {
reject(e);
}
});
}));
}
async saveAsStream(params2, progress2) {
return await progress2.race(new Promise((resolve, reject) => {
this._object.saveAs(progress2, async (localPath, error) => {
if (error) {
reject(error);
return;
}
try {
const readable = import_fs35.default.createReadStream(localPath, { highWaterMark: 1024 * 1024 });
const stream3 = new StreamDispatcher(this, readable);
resolve({ stream: stream3 });
await new Promise((resolve2) => {
readable.on("close", resolve2);
readable.on("end", resolve2);
readable.on("error", resolve2);
});
} catch (e) {
reject(e);
}
});
}));
}
async stream(params2, progress2) {
const fileName = await this._object.localPathAfterFinished(progress2);
const readable = import_fs35.default.createReadStream(fileName, { highWaterMark: 1024 * 1024 });
return { stream: new StreamDispatcher(this, readable) };
}
async failure(params2, progress2) {
const error = await this._object.failureError(progress2);
return { error: error || void 0 };
}
async cancel(params2, progress2) {
await this._object.cancel(progress2);
}
async delete(params2, progress2) {
progress2.metadata.potentiallyClosesScope = true;
await this._object.delete(progress2);
this._dispose();
}
};
}
});
// packages/playwright-core/src/server/dispatchers/jsHandleDispatcher.ts
function parseArgument(arg) {
return parseSerializedValue(arg.value, arg.handles.map((a) => a._object));
}
function serializeResult(arg) {
return serializeValue(arg, (value2) => ({ fallThrough: value2 }));
}
var JSHandleDispatcher;
var init_jsHandleDispatcher = __esm({
"packages/playwright-core/src/server/dispatchers/jsHandleDispatcher.ts"() {
"use strict";
init_dispatcher();
init_elementHandlerDispatcher();
init_serializers();
JSHandleDispatcher = class _JSHandleDispatcher extends Dispatcher {
constructor(scope, jsHandle) {
super(scope, jsHandle, jsHandle.asElement() ? "ElementHandle" : "JSHandle", {
preview: jsHandle.toString()
});
this._type_JSHandle = true;
jsHandle._setPreviewCallback((preview) => this._dispatchEvent("previewUpdated", { preview }));
}
static fromJSHandle(scope, handle) {
return scope.connection.existingDispatcher(handle) || new _JSHandleDispatcher(scope, handle);
}
async evaluateExpression(params2, progress2) {
const jsHandle = await this._object.evaluateExpression(progress2, params2.expression, { isFunction: params2.isFunction }, parseArgument(params2.arg));
return { value: serializeResult(jsHandle) };
}
async evaluateExpressionHandle(params2, progress2) {
const jsHandle = await this._object.evaluateExpressionHandle(progress2, params2.expression, { isFunction: params2.isFunction }, parseArgument(params2.arg));
return { handle: ElementHandleDispatcher.fromJSOrElementHandle(this.parentScope(), jsHandle) };
}
async getProperty(params2, progress2) {
const jsHandle = await this._object.getProperty(progress2, params2.name);
return { handle: ElementHandleDispatcher.fromJSOrElementHandle(this.parentScope(), jsHandle) };
}
async getPropertyList(params2, progress2) {
const map = await this._object.getProperties(progress2);
const properties = [];
for (const [name, value2] of map) {
properties.push({ name, value: ElementHandleDispatcher.fromJSOrElementHandle(this.parentScope(), value2) });
}
return { properties };
}
async jsonValue(params2, progress2) {
return { value: serializeResult(await this._object.jsonValue(progress2)) };
}
async dispose(_, progress2) {
progress2.metadata.potentiallyClosesScope = true;
this._object.dispose();
this._dispose();
}
};
}
});
// packages/playwright-core/src/generated/webSocketMockSource.ts
var source7;
var init_webSocketMockSource = __esm({
"packages/playwright-core/src/generated/webSocketMockSource.ts"() {
"use strict";
source7 = `
var __commonJS = obj => {
let required = false;
let result;
return function __require() {
if (!required) {
required = true;
let fn;
for (const name in obj) { fn = obj[name]; break; }
const module = { exports: {} };
fn(module.exports, module);
result = module.exports;
}
return result;
}
};
var __export = (target, all) => {for (var name in all) target[name] = all[name];};
var __toESM = mod => ({ ...mod, 'default': mod });
var __toCommonJS = mod => ({ ...mod, __esModule: true });
// packages/injected/src/webSocketMock.ts
var webSocketMock_exports = {};
__export(webSocketMock_exports, {
inject: () => inject
});
module.exports = __toCommonJS(webSocketMock_exports);
function inject(globalThis) {
if (globalThis.__pwWebSocketDispatch)
return;
function generateId() {
const bytes = new Uint8Array(32);
globalThis.crypto.getRandomValues(bytes);
const hex = "0123456789abcdef";
return [...bytes].map((value) => {
const high = Math.floor(value / 16);
const low = value % 16;
return hex[high] + hex[low];
}).join("");
}
function bufferToData(b) {
let s = "";
for (let i = 0; i < b.length; i++)
s += String.fromCharCode(b[i]);
return { data: globalThis.btoa(s), isBase64: true };
}
function stringToBuffer(s) {
s = globalThis.atob(s);
const b = new Uint8Array(s.length);
for (let i = 0; i < s.length; i++)
b[i] = s.charCodeAt(i);
return b.buffer;
}
function messageToData(message, cb) {
if (message instanceof globalThis.Blob)
return message.arrayBuffer().then((buffer) => cb(bufferToData(new Uint8Array(buffer))));
if (typeof message === "string")
return cb({ data: message, isBase64: false });
if (ArrayBuffer.isView(message))
return cb(bufferToData(new Uint8Array(message.buffer, message.byteOffset, message.byteLength)));
return cb(bufferToData(new Uint8Array(message)));
}
function dataToMessage(data, binaryType) {
if (!data.isBase64)
return data.data;
const buffer = stringToBuffer(data.data);
return binaryType === "arraybuffer" ? buffer : new Blob([buffer]);
}
const binding = globalThis.__pwWebSocketBinding;
const NativeWebSocket = globalThis.WebSocket;
const idToWebSocket = /* @__PURE__ */ new Map();
globalThis.__pwWebSocketDispatch = (request) => {
const ws = idToWebSocket.get(request.id);
if (!ws)
return;
if (request.type === "connect")
ws._apiConnect();
if (request.type === "passthrough")
ws._apiPassThrough();
if (request.type === "ensureOpened")
ws._apiEnsureOpened();
if (request.type === "sendToPage")
ws._apiSendToPage(dataToMessage(request.data, ws.binaryType));
if (request.type === "closePage")
ws._apiClosePage(request.code, request.reason, request.wasClean);
if (request.type === "sendToServer")
ws._apiSendToServer(dataToMessage(request.data, ws.binaryType));
if (request.type === "closeServer")
ws._apiCloseServer(request.code, request.reason, request.wasClean);
};
const _WebSocketMock = class _WebSocketMock extends EventTarget {
constructor(url, protocols) {
var _a, _b;
super();
// WebSocket.CLOSED
this.CONNECTING = 0;
// WebSocket.CONNECTING
this.OPEN = 1;
// WebSocket.OPEN
this.CLOSING = 2;
// WebSocket.CLOSING
this.CLOSED = 3;
// WebSocket.CLOSED
this._oncloseListener = null;
this._onerrorListener = null;
this._onmessageListener = null;
this._onopenListener = null;
this.bufferedAmount = 0;
this.extensions = "";
this.protocol = "";
this.readyState = 0;
this._origin = "";
this._passthrough = false;
this._wsBufferedMessages = [];
this._binaryType = "blob";
this.url = new URL(url, globalThis.window.document.baseURI).href.replace(/^http/, "ws");
this._origin = (_b = (_a = URL.parse(this.url)) == null ? void 0 : _a.origin) != null ? _b : "";
this._protocols = protocols;
this._id = generateId();
idToWebSocket.set(this._id, this);
const protocolsList = Array.isArray(protocols) ? [...protocols] : protocols ? [protocols] : [];
binding({ type: "onCreate", id: this._id, url: this.url, protocols: protocolsList });
}
// --- native WebSocket implementation ---
get binaryType() {
return this._binaryType;
}
set binaryType(type) {
this._binaryType = type;
if (this._ws)
this._ws.binaryType = type;
}
get onclose() {
return this._oncloseListener;
}
set onclose(listener) {
if (this._oncloseListener)
this.removeEventListener("close", this._oncloseListener);
this._oncloseListener = listener;
if (this._oncloseListener)
this.addEventListener("close", this._oncloseListener);
}
get onerror() {
return this._onerrorListener;
}
set onerror(listener) {
if (this._onerrorListener)
this.removeEventListener("error", this._onerrorListener);
this._onerrorListener = listener;
if (this._onerrorListener)
this.addEventListener("error", this._onerrorListener);
}
get onopen() {
return this._onopenListener;
}
set onopen(listener) {
if (this._onopenListener)
this.removeEventListener("open", this._onopenListener);
this._onopenListener = listener;
if (this._onopenListener)
this.addEventListener("open", this._onopenListener);
}
get onmessage() {
return this._onmessageListener;
}
set onmessage(listener) {
if (this._onmessageListener)
this.removeEventListener("message", this._onmessageListener);
this._onmessageListener = listener;
if (this._onmessageListener)
this.addEventListener("message", this._onmessageListener);
}
send(message) {
if (this.readyState === _WebSocketMock.CONNECTING)
throw new DOMException(\`Failed to execute 'send' on 'WebSocket': Still in CONNECTING state.\`);
if (this.readyState !== _WebSocketMock.OPEN)
throw new DOMException(\`WebSocket is already in CLOSING or CLOSED state.\`);
if (this._passthrough) {
if (this._ws)
this._apiSendToServer(message);
} else {
messageToData(message, (data) => binding({ type: "onMessageFromPage", id: this._id, data }));
}
}
close(code, reason) {
if (code !== void 0 && code !== 1e3 && (code < 3e3 || code > 4999))
throw new DOMException(\`Failed to execute 'close' on 'WebSocket': The close code must be either 1000, or between 3000 and 4999. \${code} is neither.\`);
if (this.readyState === _WebSocketMock.OPEN || this.readyState === _WebSocketMock.CONNECTING)
this.readyState = _WebSocketMock.CLOSING;
if (this._passthrough)
this._apiCloseServer(code, reason, true);
else
binding({ type: "onClosePage", id: this._id, code, reason, wasClean: true });
}
// --- methods called from the routing API ---
_apiEnsureOpened() {
if (!this._ws)
this._ensureOpened();
}
_apiSendToPage(message) {
this._ensureOpened();
if (this.readyState !== _WebSocketMock.OPEN)
throw new DOMException(\`WebSocket is already in CLOSING or CLOSED state.\`);
this.dispatchEvent(new MessageEvent("message", { data: message, origin: this._origin, cancelable: true }));
}
_apiSendToServer(message) {
if (!this._ws)
throw new Error("Cannot send a message before connecting to the server");
if (this._ws.readyState === _WebSocketMock.CONNECTING)
this._wsBufferedMessages.push(message);
else
this._ws.send(message);
}
_apiConnect() {
if (this._ws)
throw new Error("Can only connect to the server once");
this._ws = new NativeWebSocket(this.url, this._protocols);
this._ws.binaryType = this._binaryType;
this._ws.onopen = () => {
for (const message of this._wsBufferedMessages)
this._ws.send(message);
this._wsBufferedMessages = [];
this._ensureOpened();
};
this._ws.onclose = (event) => {
this._onWSClose(event.code, event.reason, event.wasClean);
};
this._ws.onmessage = (event) => {
if (this._passthrough)
this._apiSendToPage(event.data);
else
messageToData(event.data, (data) => binding({ type: "onMessageFromServer", id: this._id, data }));
};
this._ws.onerror = () => {
const event = new Event("error", { cancelable: true });
this.dispatchEvent(event);
};
}
// This method connects to the server, and passes all messages through,
// as if WebSocketMock was not engaged.
_apiPassThrough() {
this._passthrough = true;
this._apiConnect();
}
_apiCloseServer(code, reason, wasClean) {
if (!this._ws) {
this._onWSClose(code, reason, wasClean);
return;
}
if (this._ws.readyState === _WebSocketMock.CONNECTING || this._ws.readyState === _WebSocketMock.OPEN)
this._ws.close(code, reason);
}
_apiClosePage(code, reason, wasClean) {
if (this.readyState === _WebSocketMock.CLOSED)
return;
this.readyState = _WebSocketMock.CLOSED;
this.dispatchEvent(new CloseEvent("close", { code, reason, wasClean, cancelable: true }));
this._maybeCleanup();
if (this._passthrough)
this._apiCloseServer(code, reason, wasClean);
else
binding({ type: "onClosePage", id: this._id, code, reason, wasClean });
}
// --- internals ---
_ensureOpened() {
var _a;
if (this.readyState !== _WebSocketMock.CONNECTING)
return;
this.extensions = ((_a = this._ws) == null ? void 0 : _a.extensions) || "";
if (this._ws)
this.protocol = this._ws.protocol;
else if (Array.isArray(this._protocols))
this.protocol = this._protocols[0] || "";
else
this.protocol = this._protocols || "";
this.readyState = _WebSocketMock.OPEN;
this.dispatchEvent(new Event("open", { cancelable: true }));
}
_onWSClose(code, reason, wasClean) {
if (this._passthrough)
this._apiClosePage(code, reason, wasClean);
else
binding({ type: "onCloseServer", id: this._id, code, reason, wasClean });
if (this._ws) {
this._ws.onopen = null;
this._ws.onclose = null;
this._ws.onmessage = null;
this._ws.onerror = null;
this._ws = void 0;
this._wsBufferedMessages = [];
}
this._maybeCleanup();
}
_maybeCleanup() {
if (this.readyState === _WebSocketMock.CLOSED && !this._ws)
idToWebSocket.delete(this._id);
}
};
_WebSocketMock.CONNECTING = 0;
// WebSocket.CONNECTING
_WebSocketMock.OPEN = 1;
// WebSocket.OPEN
_WebSocketMock.CLOSING = 2;
// WebSocket.CLOSING
_WebSocketMock.CLOSED = 3;
let WebSocketMock = _WebSocketMock;
globalThis.WebSocket = class WebSocket extends WebSocketMock {
};
}
`;
}
});
// packages/playwright-core/src/server/dispatchers/webSocketRouteDispatcher.ts
function matchesPattern(dispatcher, baseURL, url2) {
for (const pattern of dispatcher._webSocketInterceptionPatterns || []) {
if (urlMatches(baseURL, url2, deserializeURLMatch(pattern), true))
return true;
}
return false;
}
var WebSocketRouteDispatcher, kBindingName2;
var init_webSocketRouteDispatcher = __esm({
"packages/playwright-core/src/server/dispatchers/webSocketRouteDispatcher.ts"() {
"use strict";
init_urlMatch();
init_eventsHelper();
init_page();
init_dispatcher();
init_pageDispatcher();
init_webSocketMockSource();
init_instrumentation();
WebSocketRouteDispatcher = class _WebSocketRouteDispatcher extends Dispatcher {
constructor(scope, id, url2, protocols, frame) {
super(scope, new SdkObject(scope._object, "webSocketRoute"), "WebSocketRoute", { url: url2, protocols });
this._type_WebSocketRoute = true;
this._id = id;
this._frame = frame;
this._eventListeners.push(
// When the frame navigates or detaches, there will be no more communication
// from the mock websocket, so pretend like it was closed.
eventsHelper.addEventListener(frame._page, Page.Events.InternalFrameNavigatedToNewDocument, (frame2) => {
if (frame2 === this._frame)
this._executionContextGone();
}),
eventsHelper.addEventListener(frame._page, Page.Events.FrameDetached, (frame2) => {
if (frame2 === this._frame)
this._executionContextGone();
}),
eventsHelper.addEventListener(frame._page, Page.Events.Close, () => this._executionContextGone()),
eventsHelper.addEventListener(frame._page, Page.Events.Crash, () => this._executionContextGone())
);
_WebSocketRouteDispatcher._idToDispatcher.set(this._id, this);
scope._dispatchEvent("webSocketRoute", { webSocketRoute: this });
}
static {
this._idToDispatcher = /* @__PURE__ */ new Map();
}
static async install(progress2, connection, target) {
const context2 = target instanceof Page ? target.browserContext : target;
let data = context2.getBindingClient(kBindingName2);
if (data && data.connection !== connection)
throw new Error("Another client is already routing WebSockets");
if (!data) {
data = { counter: 0, connection, binding: null };
data.binding = await context2.exposeBinding(progress2, kBindingName2, (source8, payload) => {
if (payload.type === "onCreate") {
const contextDispatcher = connection.existingDispatcher(context2);
const pageDispatcher = contextDispatcher ? PageDispatcher.fromNullable(contextDispatcher, source8.page) : void 0;
let scope;
if (pageDispatcher && matchesPattern(pageDispatcher, context2._options.baseURL, payload.url))
scope = pageDispatcher;
else if (contextDispatcher && matchesPattern(contextDispatcher, context2._options.baseURL, payload.url))
scope = contextDispatcher;
if (scope) {
new _WebSocketRouteDispatcher(scope, payload.id, payload.url, payload.protocols, source8.frame);
} else {
const request2 = { id: payload.id, type: "passthrough" };
source8.frame.evaluateExpression(progress2, `globalThis.__pwWebSocketDispatch(${JSON.stringify(request2)})`).catch(() => {
});
}
return;
}
const dispatcher = _WebSocketRouteDispatcher._idToDispatcher.get(payload.id);
if (payload.type === "onMessageFromPage")
dispatcher?._dispatchEvent("messageFromPage", { message: payload.data.data, isBase64: payload.data.isBase64 });
if (payload.type === "onMessageFromServer")
dispatcher?._dispatchEvent("messageFromServer", { message: payload.data.data, isBase64: payload.data.isBase64 });
if (payload.type === "onClosePage")
dispatcher?._dispatchEvent("closePage", { code: payload.code, reason: payload.reason, wasClean: payload.wasClean });
if (payload.type === "onCloseServer")
dispatcher?._dispatchEvent("closeServer", { code: payload.code, reason: payload.reason, wasClean: payload.wasClean });
}, data);
}
++data.counter;
return await target.addInitScript(progress2, `
(() => {
const module = {};
${source7}
(module.exports.inject())(globalThis);
})();
`);
}
static async uninstall(connection, target, initScript) {
const context2 = target instanceof Page ? target.browserContext : target;
const data = context2.getBindingClient(kBindingName2);
if (!data || data.connection !== connection)
return;
if (--data.counter <= 0)
await data.binding.dispose();
await initScript.dispose();
}
async connect(params2, progress2) {
await this._evaluateAPIRequest(progress2, { id: this._id, type: "connect" });
}
async ensureOpened(params2, progress2) {
await this._evaluateAPIRequest(progress2, { id: this._id, type: "ensureOpened" });
}
async sendToPage(params2, progress2) {
await this._evaluateAPIRequest(progress2, { id: this._id, type: "sendToPage", data: { data: params2.message, isBase64: params2.isBase64 } });
}
async sendToServer(params2, progress2) {
await this._evaluateAPIRequest(progress2, { id: this._id, type: "sendToServer", data: { data: params2.message, isBase64: params2.isBase64 } });
}
async closePage(params2, progress2) {
await this._evaluateAPIRequest(progress2, { id: this._id, type: "closePage", code: params2.code, reason: params2.reason, wasClean: params2.wasClean });
}
async closeServer(params2, progress2) {
await this._evaluateAPIRequest(progress2, { id: this._id, type: "closeServer", code: params2.code, reason: params2.reason, wasClean: params2.wasClean });
}
async _evaluateAPIRequest(progress2, request2) {
await this._frame.evaluateExpression(progress2, `globalThis.__pwWebSocketDispatch(${JSON.stringify(request2)})`).catch(() => {
});
}
_onDispose() {
_WebSocketRouteDispatcher._idToDispatcher.delete(this._id);
}
_executionContextGone() {
if (!this._disposed) {
this._dispatchEvent("closePage", { wasClean: true });
this._dispatchEvent("closeServer", { wasClean: true });
}
}
};
kBindingName2 = "__pwWebSocketBinding";
}
});
// packages/playwright-core/src/server/dispatchers/disposableDispatcher.ts
var DisposableDispatcher;
var init_disposableDispatcher = __esm({
"packages/playwright-core/src/server/dispatchers/disposableDispatcher.ts"() {
"use strict";
init_dispatcher();
DisposableDispatcher = class extends Dispatcher {
constructor(scope, disposable) {
super(scope, disposable, "Disposable", {});
this._type_Disposable = true;
}
async dispose(_, progress2) {
progress2.metadata.potentiallyClosesScope = true;
await progress2.race(this._object.dispose());
this._dispose();
}
};
}
});
// packages/playwright-core/src/server/dispatchers/pageDispatcher.ts
function createVideoDispatcher(parentScope, video) {
return ArtifactDispatcher.from(parentScope.parentScope(), video);
}
var PageDispatcher, WorkerDispatcher, BindingCallDispatcher;
var init_pageDispatcher = __esm({
"packages/playwright-core/src/server/dispatchers/pageDispatcher.ts"() {
"use strict";
init_urlMatch();
init_page();
init_dispatcher();
init_errors();
init_artifactDispatcher();
init_elementHandlerDispatcher();
init_frameDispatcher();
init_jsHandleDispatcher();
init_networkDispatchers();
init_networkDispatchers();
init_networkDispatchers();
init_webSocketRouteDispatcher();
init_disposableDispatcher();
init_instrumentation();
init_recorder();
init_disposable2();
init_videoRecorder();
init_progress();
PageDispatcher = class _PageDispatcher extends Dispatcher {
constructor(parentScope, page) {
const mainFrame = FrameDispatcher.from(parentScope, page.mainFrame());
super(parentScope, page, "Page", {
mainFrame,
viewportSize: page.emulatedSize()?.viewport,
isClosed: page.isClosed(),
opener: _PageDispatcher.fromNullable(parentScope, page.opener()),
video: page.video ? createVideoDispatcher(parentScope, page.video) : void 0
});
this._type_EventTarget = true;
this._type_Page = true;
this._subscriptions = /* @__PURE__ */ new Set();
this._webSocketInterceptionPatterns = [];
this._disposables = [];
this._interceptionUrlMatchers = [];
this._locatorHandlers = /* @__PURE__ */ new Set();
this._jsCoverageActive = false;
this._cssCoverageActive = false;
this.adopt(mainFrame);
this._page = page;
this._requestInterceptor = (route2, request2) => {
const matchesSome = this._interceptionUrlMatchers.some((urlMatch) => urlMatches(this._page.browserContext._options.baseURL, request2.url(), urlMatch));
if (!matchesSome) {
route2.continue({ isFallback: true }).catch(() => {
});
return;
}
this._dispatchEvent("route", { route: new RouteDispatcher(RequestDispatcher.from(this.parentScope(), request2), route2) });
};
this.addObjectListener(Page.Events.Close, () => {
this._dispatchEvent("close");
this._dispose();
});
this.addObjectListener(Page.Events.Crash, () => this._dispatchEvent("crash"));
this.addObjectListener(Page.Events.Download, (download) => {
this._dispatchEvent("download", { url: download.url, suggestedFilename: download.suggestedFilename(), artifact: ArtifactDispatcher.from(parentScope, download.artifact) });
});
this.addObjectListener(Page.Events.EmulatedSizeChanged, () => this._dispatchEvent("viewportSizeChanged", { viewportSize: page.emulatedSize()?.viewport }));
this.addObjectListener(Page.Events.FileChooser, (fileChooser) => this._dispatchEvent("fileChooser", {
element: ElementHandleDispatcher.from(mainFrame, fileChooser.element()),
isMultiple: fileChooser.isMultiple()
}));
this.addObjectListener(Page.Events.FrameAttached, (frame) => this._onFrameAttached(frame));
this.addObjectListener(Page.Events.FrameDetached, (frame) => this._onFrameDetached(frame));
this.addObjectListener(Page.Events.LocatorHandlerTriggered, (uid) => this._dispatchEvent("locatorHandlerTriggered", { uid }));
this.addObjectListener(Page.Events.WebSocket, (webSocket) => this._dispatchEvent("webSocket", { webSocket: new WebSocketDispatcher(this, webSocket) }));
this.addObjectListener(Page.Events.Worker, (worker) => this._dispatchEvent("worker", { worker: new WorkerDispatcher(this, worker) }));
const frames = page.frameManager.frames();
for (let i = 1; i < frames.length; i++)
this._onFrameAttached(frames[i]);
}
static from(parentScope, page) {
return _PageDispatcher.fromNullable(parentScope, page);
}
static fromNullable(parentScope, page) {
if (!page)
return void 0;
const result2 = parentScope.connection.existingDispatcher(page);
return result2 || new _PageDispatcher(parentScope, page);
}
page() {
return this._page;
}
async exposeBinding(params2, progress2) {
const binding = await this._page.exposeBinding(progress2, params2.name, (source8, ...args) => {
if (this._disposed)
return;
const binding2 = new BindingCallDispatcher(this, params2.name, source8, args);
this._dispatchEvent("bindingCall", { binding: binding2 });
return binding2.promise();
});
this._disposables.push(binding);
return { disposable: new DisposableDispatcher(this, binding) };
}
async setExtraHTTPHeaders(params2, progress2) {
await this._page.setExtraHTTPHeaders(progress2, params2.headers);
}
async reload(params2, progress2) {
return { response: ResponseDispatcher.fromNullable(this.parentScope(), await this._page.reload(progress2, params2)) };
}
async goBack(params2, progress2) {
return { response: ResponseDispatcher.fromNullable(this.parentScope(), await this._page.goBack(progress2, params2)) };
}
async goForward(params2, progress2) {
return { response: ResponseDispatcher.fromNullable(this.parentScope(), await this._page.goForward(progress2, params2)) };
}
async requestGC(params2, progress2) {
await this._page.requestGC(progress2);
}
async registerLocatorHandler(params2, progress2) {
const uid = this._page.registerLocatorHandler(params2.selector, params2.noWaitAfter);
this._locatorHandlers.add(uid);
return { uid };
}
async resolveLocatorHandlerNoReply(params2, progress2) {
this._page.resolveLocatorHandler(params2.uid, params2.remove);
}
async unregisterLocatorHandler(params2, progress2) {
this._page.unregisterLocatorHandler(params2.uid);
this._locatorHandlers.delete(params2.uid);
}
async emulateMedia(params2, progress2) {
await this._page.emulateMedia(progress2, {
media: params2.media,
colorScheme: params2.colorScheme,
reducedMotion: params2.reducedMotion,
forcedColors: params2.forcedColors,
contrast: params2.contrast
});
}
async setViewportSize(params2, progress2) {
await this._page.setViewportSize(progress2, params2.viewportSize);
}
async addInitScript(params2, progress2) {
const initScript = await this._page.addInitScript(progress2, params2.source);
this._disposables.push(initScript);
return { disposable: new DisposableDispatcher(this, initScript) };
}
async setNetworkInterceptionPatterns(params2, progress2) {
const hadMatchers = this._interceptionUrlMatchers.length > 0;
if (!params2.patterns.length) {
if (hadMatchers)
await progress2.race(this._page.removeRequestInterceptor(this._requestInterceptor));
this._interceptionUrlMatchers = [];
} else {
this._interceptionUrlMatchers = params2.patterns.map(deserializeURLMatch);
if (!hadMatchers)
await this._page.addRequestInterceptor(progress2, this._requestInterceptor);
}
}
async setWebSocketInterceptionPatterns(params2, progress2) {
this._webSocketInterceptionPatterns = params2.patterns;
if (params2.patterns.length && !this._routeWebSocketInitScript)
this._routeWebSocketInitScript = await WebSocketRouteDispatcher.install(progress2, this.connection, this._page);
}
async expectScreenshot(params2, progress2) {
const mask = (params2.mask || []).map(({ frame, selector }) => ({
frame: frame._object,
selector
}));
const locator2 = params2.locator ? {
frame: params2.locator.frame._object,
selector: params2.locator.selector
} : void 0;
return await this._page.expectScreenshot(progress2, {
...params2,
locator: locator2,
mask
});
}
async screenshot(params2, progress2) {
const mask = (params2.mask || []).map(({ frame, selector }) => ({
frame: frame._object,
selector
}));
return { binary: await this._page.screenshot(progress2, { ...params2, mask }) };
}
async close(params2, progress2) {
if (!params2.runBeforeUnload)
progress2.metadata.potentiallyClosesScope = true;
await this._page.close(progress2, params2);
}
async updateSubscription(params2, progress2) {
if (params2.event === "fileChooser")
await this._page.setFileChooserInterceptedBy(progress2, params2.enabled, this);
if (params2.enabled)
this._subscriptions.add(params2.event);
else
this._subscriptions.delete(params2.event);
}
async keyboardDown(params2, progress2) {
await this._page.keyboard.apiDown(progress2, params2.key);
}
async keyboardUp(params2, progress2) {
await this._page.keyboard.apiUp(progress2, params2.key);
}
async keyboardInsertText(params2, progress2) {
await this._page.keyboard.apiInsertText(progress2, params2.text);
}
async keyboardType(params2, progress2) {
await this._page.keyboard.apiType(progress2, params2.text, params2);
}
async keyboardPress(params2, progress2) {
await this._page.keyboard.apiPress(progress2, params2.key, params2);
}
async clearConsoleMessages(params2, progress2) {
this._page.clearConsoleMessages();
}
async consoleMessages(params2, progress2) {
this._subscriptions.add("console");
return { messages: this._page.consoleMessages(params2.filter).map((message) => this.parentScope().serializeConsoleMessage(message, this)) };
}
async clearPageErrors(params2, progress2) {
this._page.clearPageErrors();
}
async pageErrors(params2, progress2) {
return { errors: this._page.pageErrors(params2.filter).map((error) => serializeError(error)) };
}
async mouseMove(params2, progress2) {
await this._page.mouse.apiMove(progress2, params2.x, params2.y, params2);
}
async mouseDown(params2, progress2) {
await this._page.mouse.apiDown(progress2, params2);
}
async mouseUp(params2, progress2) {
await this._page.mouse.apiUp(progress2, params2);
}
async mouseClick(params2, progress2) {
await this._page.mouse.apiClick(progress2, params2.x, params2.y, params2);
}
async mouseWheel(params2, progress2) {
await this._page.mouse.apiWheel(progress2, params2.deltaX, params2.deltaY);
}
async touchscreenTap(params2, progress2) {
progress2.metadata.point = { x: params2.x, y: params2.y };
await this._page.touchscreen.apiTap(progress2, params2.x, params2.y);
}
async pdf(params2, progress2) {
if (!this._page.pdf)
throw new Error("PDF generation is only supported for Headless Chromium");
const buffer = await progress2.race(this._page.pdf(params2));
return { pdf: buffer };
}
async requests(params2, progress2) {
this._subscriptions.add("request");
return { requests: this._page.networkRequests().map((request2) => RequestDispatcher.from(this.parentScope(), request2)) };
}
async bringToFront(params2, progress2) {
await this._page.bringToFront(progress2);
}
async pickLocator(params2, progress2) {
const recorder = await progress2.race(Recorder.forContext(this._page.browserContext, { omitCallTracking: true, hideToolbar: true }));
const selector = await recorder.pickLocator(progress2, this._page);
return { selector };
}
async cancelPickLocator(params2, progress2) {
const recorder = await progress2.race(Recorder.existingForContext(this._page.browserContext));
if (recorder)
await progress2.race(recorder.setMode("none"));
}
async hideHighlight(params2, progress2) {
await progress2.race(this._page.hideHighlight());
}
async screencastShowOverlay(params2) {
const id = await this._page.overlay.show(params2.html, params2.duration);
return { id };
}
async screencastRemoveOverlay(params2) {
await this._page.overlay.remove(params2.id);
}
async screencastChapter(params2) {
await this._page.overlay.chapter(params2);
}
async screencastSetOverlayVisible(params2) {
await this._page.overlay.setVisible(params2.visible);
}
async screencastShowActions(params2) {
this._page.screencast.showActions({ duration: params2.duration, position: params2.position, fontSize: params2.fontSize });
}
async screencastHideActions() {
this._page.screencast.hideActions();
}
async screencastStart(params2, progress2) {
if (this._screencastClient || this._videoRecorder)
throw new Error("Screencast is already running");
if (params2.sendFrames) {
this._screencastClient = {
onFrame: (frame) => {
this._dispatchEvent("screencastFrame", { data: frame.buffer, viewportWidth: frame.viewportWidth, viewportHeight: frame.viewportHeight });
},
dispose: () => {
},
size: params2.size,
quality: params2.quality
};
this._page.screencast.addClient(this._screencastClient);
}
let artifact;
if (params2.record) {
this._videoRecorder = new VideoRecorder(this._page.screencast);
artifact = this._videoRecorder.start(params2);
}
return { artifact: artifact ? createVideoDispatcher(this.parentScope(), artifact) : void 0 };
}
async screencastStop(params2, progress2) {
if (this._videoRecorder) {
await this._videoRecorder.stop();
this._videoRecorder = void 0;
}
const client = this._screencastClient;
this._screencastClient = void 0;
if (client)
this._page.screencast.removeClient(client);
}
async startJSCoverage(params2, progress2) {
const coverage = this._page.coverage;
await coverage.startJSCoverage(progress2, params2);
this._jsCoverageActive = true;
}
async stopJSCoverage(params2, progress2) {
this._jsCoverageActive = false;
const coverage = this._page.coverage;
return await progress2.race(coverage.stopJSCoverage());
}
async startCSSCoverage(params2, progress2) {
const coverage = this._page.coverage;
await coverage.startCSSCoverage(progress2, params2);
this._cssCoverageActive = true;
}
async stopCSSCoverage(params2, progress2) {
this._cssCoverageActive = false;
const coverage = this._page.coverage;
return await progress2.race(coverage.stopCSSCoverage());
}
_onFrameAttached(frame) {
this._dispatchEvent("frameAttached", { frame: FrameDispatcher.from(this.parentScope(), frame) });
}
_onFrameDetached(frame) {
this._dispatchEvent("frameDetached", { frame: FrameDispatcher.from(this.parentScope(), frame) });
}
_onDispose() {
if (this._page.isClosedOrClosingOrCrashed())
return;
this._interceptionUrlMatchers = [];
this._page.removeRequestInterceptor(this._requestInterceptor).catch(() => {
});
disposeAll2(this._disposables).catch(() => {
});
if (this._routeWebSocketInitScript)
WebSocketRouteDispatcher.uninstall(this.connection, this._page, this._routeWebSocketInitScript).catch(() => {
});
this._routeWebSocketInitScript = void 0;
for (const uid of this._locatorHandlers)
this._page.unregisterLocatorHandler(uid);
this._locatorHandlers.clear();
this._page.setFileChooserInterceptedBy(nullProgress, false, this).catch(() => {
});
if (this._jsCoverageActive)
this._page.coverage.stopJSCoverage().catch(() => {
});
this._jsCoverageActive = false;
if (this._cssCoverageActive)
this._page.coverage.stopCSSCoverage().catch(() => {
});
this._cssCoverageActive = false;
this.screencastStop({}, void 0).catch(() => {
});
}
async setDockTile(params2) {
await this._page.setDockTile(params2.image);
}
};
WorkerDispatcher = class _WorkerDispatcher extends Dispatcher {
constructor(scope, worker) {
super(scope, worker, "Worker", {
url: worker.url
});
this._type_Worker = true;
this._type_EventTarget = true;
this._subscriptions = /* @__PURE__ */ new Set();
this.addObjectListener(Worker.Events.Console, (message) => {
if (!this._subscriptions.has("console"))
return;
this._dispatchEvent("console", {
type: message.type(),
text: message.text(),
args: message.args().map((a) => JSHandleDispatcher.fromJSHandle(this, a)),
location: message.location(),
timestamp: message.timestamp()
});
});
this.addObjectListener(Worker.Events.Close, () => this._dispatchEvent("close"));
}
static fromNullable(scope, worker) {
if (!worker)
return void 0;
const result2 = scope.connection.existingDispatcher(worker);
return result2 || new _WorkerDispatcher(scope, worker);
}
async disconnect(params2, progress2) {
progress2.metadata.potentiallyClosesScope = true;
await this._object.disconnect(progress2, params2);
}
async evaluateExpression(params2, progress2) {
return { value: serializeResult(await this._object.evaluateExpression(progress2, params2.expression, params2.isFunction, parseArgument(params2.arg))) };
}
async evaluateExpressionHandle(params2, progress2) {
return { handle: JSHandleDispatcher.fromJSHandle(this, await this._object.evaluateExpressionHandle(progress2, params2.expression, params2.isFunction, parseArgument(params2.arg))) };
}
async updateSubscription(params2, progress2) {
if (params2.enabled)
this._subscriptions.add(params2.event);
else
this._subscriptions.delete(params2.event);
}
};
BindingCallDispatcher = class extends Dispatcher {
constructor(scope, name, source8, args) {
const frameDispatcher = FrameDispatcher.from(scope.parentScope(), source8.frame);
super(scope, new SdkObject(scope._object, "bindingCall"), "BindingCall", {
frame: frameDispatcher,
name,
args: args.map(serializeResult)
});
this._type_BindingCall = true;
this._promise = new Promise((resolve, reject) => {
this._resolve = resolve;
this._reject = reject;
});
}
promise() {
return this._promise;
}
async resolve(params2, progress2) {
this._resolve(parseArgument(params2.result));
this._dispose();
}
async reject(params2, progress2) {
this._reject(parseError(params2.error));
this._dispose();
}
};
}
});
// packages/playwright-core/src/server/dispatchers/dialogDispatcher.ts
var DialogDispatcher;
var init_dialogDispatcher = __esm({
"packages/playwright-core/src/server/dispatchers/dialogDispatcher.ts"() {
"use strict";
init_dispatcher();
init_pageDispatcher();
DialogDispatcher = class extends Dispatcher {
constructor(scope, dialog) {
const page = PageDispatcher.fromNullable(scope, dialog.page().initializedOrUndefined());
super(page || scope, dialog, "Dialog", {
page,
type: dialog.type(),
message: dialog.message(),
defaultValue: dialog.defaultValue()
});
this._type_Dialog = true;
}
async accept(params2, progress2) {
await this._object.accept(progress2, params2.promptText);
}
async dismiss(params2, progress2) {
await this._object.dismiss(progress2);
}
};
}
});
// packages/playwright-core/src/server/dispatchers/tracingDispatcher.ts
var TracingDispatcher;
var init_tracingDispatcher = __esm({
"packages/playwright-core/src/server/dispatchers/tracingDispatcher.ts"() {
"use strict";
init_artifactDispatcher();
init_dispatcher();
init_progress();
TracingDispatcher = class _TracingDispatcher extends Dispatcher {
constructor(scope, tracing) {
super(scope, tracing, "Tracing", {});
this._type_Tracing = true;
this._started = false;
}
static from(scope, tracing) {
const result2 = scope.connection.existingDispatcher(tracing);
return result2 || new _TracingDispatcher(scope, tracing);
}
async tracingStart(params2, progress2) {
this._object.start(progress2, params2);
this._started = true;
}
async tracingStartChunk(params2, progress2) {
return await this._object.startChunk(progress2, params2);
}
async tracingGroup(params2, progress2) {
const { name, location: location2 } = params2;
this._object.group(progress2, name, location2);
}
async tracingGroupEnd(params2, progress2) {
this._object.groupEnd(progress2);
}
async tracingStopChunk(params2, progress2) {
const { artifact, entries } = await this._object.stopChunk(progress2, params2);
return { artifact: artifact ? ArtifactDispatcher.from(this, artifact) : void 0, entries };
}
async tracingStop(params2, progress2) {
await this._object.stop(progress2);
}
async harStart(params2, progress2) {
const harId = this._object.harStart(params2.page ? params2.page._object : null, params2.options);
return { harId };
}
async harExport(params2, progress2) {
const { artifact, entries } = await this._object.harExport(progress2, params2.harId, params2.mode);
return {
artifact: artifact ? ArtifactDispatcher.from(this, artifact) : void 0,
entries
};
}
_onDispose() {
if (this._started)
this._object.stopChunk(nullProgress, { mode: "discard" }).then(() => this._object.stop(nullProgress)).catch(() => {
});
this._started = false;
}
};
}
});
// packages/playwright-core/src/server/dispatchers/writableStreamDispatcher.ts
var import_fs36, WritableStreamSdkObject, WritableStreamDispatcher;
var init_writableStreamDispatcher = __esm({
"packages/playwright-core/src/server/dispatchers/writableStreamDispatcher.ts"() {
"use strict";
import_fs36 = __toESM(require("fs"));
init_dispatcher();
init_instrumentation();
WritableStreamSdkObject = class extends SdkObject {
constructor(parent, streamOrDirectory, lastModifiedMs) {
super(parent, "stream");
this.streamOrDirectory = streamOrDirectory;
this.lastModifiedMs = lastModifiedMs;
}
};
WritableStreamDispatcher = class extends Dispatcher {
constructor(scope, streamOrDirectory, lastModifiedMs) {
super(scope, new WritableStreamSdkObject(scope._object, streamOrDirectory, lastModifiedMs), "WritableStream", {});
this._type_WritableStream = true;
}
async write(params2, progress2) {
if (typeof this._object.streamOrDirectory === "string")
throw new Error("Cannot write to a directory");
const stream3 = this._object.streamOrDirectory;
await progress2.race(new Promise((fulfill, reject) => {
stream3.write(params2.binary, (error) => {
if (error)
reject(error);
else
fulfill();
});
}));
}
async close(params2, progress2) {
if (typeof this._object.streamOrDirectory === "string")
throw new Error("Cannot close a directory");
const stream3 = this._object.streamOrDirectory;
await progress2.race(new Promise((fulfill) => stream3.end(fulfill)));
if (this._object.lastModifiedMs)
await progress2.race(import_fs36.default.promises.utimes(this.path(), new Date(this._object.lastModifiedMs), new Date(this._object.lastModifiedMs)));
}
path() {
if (typeof this._object.streamOrDirectory === "string")
return this._object.streamOrDirectory;
return this._object.streamOrDirectory.path;
}
};
}
});
// packages/playwright-core/src/server/dispatchers/browserContextDispatcher.ts
var import_fs37, import_path34, BrowserContextDispatcher;
var init_browserContextDispatcher = __esm({
"packages/playwright-core/src/server/dispatchers/browserContextDispatcher.ts"() {
"use strict";
import_fs37 = __toESM(require("fs"));
import_path34 = __toESM(require("path"));
init_urlMatch();
init_crypto();
init_browserContext();
init_cdpSessionDispatcher();
init_debuggerDispatcher();
init_dialogDispatcher();
init_dispatcher();
init_frameDispatcher();
init_networkDispatchers();
init_pageDispatcher();
init_crBrowser();
init_errors();
init_disposableDispatcher();
init_tracingDispatcher();
init_webSocketRouteDispatcher();
init_writableStreamDispatcher();
init_recorder();
init_recorderApp();
init_elementHandlerDispatcher();
init_jsHandleDispatcher();
init_disposable2();
BrowserContextDispatcher = class _BrowserContextDispatcher extends Dispatcher {
constructor(parentScope, context2) {
const debugger_ = DebuggerDispatcher.from(parentScope, context2.debugger());
const requestContext = APIRequestContextDispatcher.from(parentScope, context2.fetchRequest);
const tracing = TracingDispatcher.from(parentScope, context2.tracing);
super(parentScope, context2, "BrowserContext", {
debugger: debugger_,
requestContext,
tracing,
options: context2._options
});
this._type_EventTarget = true;
this._type_BrowserContext = true;
this._subscriptions = /* @__PURE__ */ new Set();
this._webSocketInterceptionPatterns = [];
this._disposables = [];
this._clockPaused = false;
this._interceptionUrlMatchers = [];
this.adopt(debugger_);
this.adopt(requestContext);
this.adopt(tracing);
this._requestInterceptor = (route2, request2) => {
const matchesSome = this._interceptionUrlMatchers.some((urlMatch) => urlMatches(this._context._options.baseURL, request2.url(), urlMatch));
const routeDispatcher = this.connection.existingDispatcher(route2);
if (!matchesSome || routeDispatcher) {
route2.continue({ isFallback: true }).catch(() => {
});
return;
}
this._dispatchEvent("route", { route: new RouteDispatcher(RequestDispatcher.from(this, request2), route2) });
};
this._context = context2;
for (const page of context2.pages())
this._dispatchEvent("page", { page: PageDispatcher.from(this, page) });
this.addObjectListener(BrowserContext.Events.Page, (page) => {
this._dispatchEvent("page", { page: PageDispatcher.from(this, page) });
});
this.addObjectListener(BrowserContext.Events.Close, () => {
this._dispatchEvent("close");
this._dispose();
});
this.addObjectListener(BrowserContext.Events.PageError, (pageError, page) => {
this._dispatchEvent("pageError", {
error: serializeError(pageError.error),
page: PageDispatcher.from(this, page),
location: {
url: pageError.location.url,
line: pageError.location.lineNumber,
column: pageError.location.columnNumber
}
});
});
this.addObjectListener(BrowserContext.Events.Console, (message) => {
const pageDispatcher = PageDispatcher.fromNullable(this, message.page());
const workerDispatcher = WorkerDispatcher.fromNullable(this, message.worker());
if (this._shouldDispatchEvent(message.page(), "console") || workerDispatcher?._subscriptions.has("console")) {
this._dispatchEvent("console", {
page: pageDispatcher,
worker: workerDispatcher,
...this.serializeConsoleMessage(message, workerDispatcher || pageDispatcher)
});
}
});
this._dialogHandler = (dialog) => {
if (!this._shouldDispatchEvent(dialog.page(), "dialog"))
return false;
this._dispatchEvent("dialog", { dialog: new DialogDispatcher(this, dialog) });
return true;
};
context2.dialogManager.addDialogHandler(this._dialogHandler);
if (context2._browser.options.name === "chromium" && this._object._browser instanceof CRBrowser) {
for (const serviceWorker of context2.serviceWorkers())
this._dispatchEvent("serviceWorker", { worker: new WorkerDispatcher(this, serviceWorker) });
this.addObjectListener(CRBrowserContext.CREvents.ServiceWorker, (serviceWorker) => this._dispatchEvent("serviceWorker", { worker: new WorkerDispatcher(this, serviceWorker) }));
}
this.addObjectListener(BrowserContext.Events.Request, (request2) => {
const redirectFromDispatcher = request2.redirectedFrom() && this.connection.existingDispatcher(request2.redirectedFrom());
if (!redirectFromDispatcher && !this._shouldDispatchNetworkEvent(request2, "request") && !request2.isNavigationRequest())
return;
const requestDispatcher = RequestDispatcher.from(this, request2);
this._dispatchEvent("request", {
request: requestDispatcher,
page: PageDispatcher.fromNullable(this, request2.frame()?._page.initializedOrUndefined())
});
});
this.addObjectListener(BrowserContext.Events.Response, (response2) => {
const requestDispatcher = this.connection.existingDispatcher(response2.request());
if (!requestDispatcher && !this._shouldDispatchNetworkEvent(response2.request(), "response"))
return;
this._dispatchEvent("response", {
response: ResponseDispatcher.from(this, response2),
page: PageDispatcher.fromNullable(this, response2.frame()?._page.initializedOrUndefined())
});
});
this.addObjectListener(BrowserContext.Events.RequestFailed, (request2) => {
const requestDispatcher = this.connection.existingDispatcher(request2);
if (!requestDispatcher && !this._shouldDispatchNetworkEvent(request2, "requestFailed"))
return;
this._dispatchEvent("requestFailed", {
request: RequestDispatcher.from(this, request2),
failureText: request2._failureText || void 0,
responseEndTiming: request2._responseEndTiming,
page: PageDispatcher.fromNullable(this, request2.frame()?._page.initializedOrUndefined())
});
});
this.addObjectListener(BrowserContext.Events.RequestFinished, ({ request: request2, response: response2 }) => {
const requestDispatcher = this.connection.existingDispatcher(request2);
if (!requestDispatcher && !this._shouldDispatchNetworkEvent(request2, "requestFinished"))
return;
this._dispatchEvent("requestFinished", {
request: RequestDispatcher.from(this, request2),
response: ResponseDispatcher.fromNullable(this, response2),
responseEndTiming: request2._responseEndTiming,
page: PageDispatcher.fromNullable(this, request2.frame()?._page.initializedOrUndefined())
});
});
this.addObjectListener(BrowserContext.Events.RecorderEvent, ({ event, data, page, code }) => {
this._dispatchEvent("recorderEvent", { event, data, code, page: PageDispatcher.from(this, page) });
});
}
static from(parentScope, context2) {
const result2 = parentScope.connection.existingDispatcher(context2);
return result2 || new _BrowserContextDispatcher(parentScope, context2);
}
_shouldDispatchNetworkEvent(request2, event) {
return this._shouldDispatchEvent(request2.frame()?._page?.initializedOrUndefined(), event);
}
_shouldDispatchEvent(page, event) {
if (this._subscriptions.has(event))
return true;
const pageDispatcher = page ? this.connection.existingDispatcher(page) : void 0;
if (pageDispatcher?._subscriptions.has(event))
return true;
return false;
}
serializeConsoleMessage(message, jsScope) {
return {
type: message.type(),
text: message.text(),
args: message.args().map((a) => {
const elementHandle = a.asElement();
if (elementHandle)
return ElementHandleDispatcher.from(FrameDispatcher.from(this, elementHandle._frame), elementHandle);
return JSHandleDispatcher.fromJSHandle(jsScope, a);
}),
location: message.location(),
timestamp: message.timestamp()
};
}
async createTempFiles(params2, progress2) {
const dir = this._context._browser.options.artifactsDir;
const tmpDir = import_path34.default.join(dir, "upload-" + createGuid());
const tempDirWithRootName = params2.rootDirName ? import_path34.default.join(tmpDir, import_path34.default.basename(params2.rootDirName)) : tmpDir;
await progress2.race(import_fs37.default.promises.mkdir(tempDirWithRootName, { recursive: true }));
this._context._tempDirs.push(tmpDir);
return {
rootDir: params2.rootDirName ? new WritableStreamDispatcher(this, tempDirWithRootName) : void 0,
writableStreams: await Promise.all(params2.items.map(async (item) => {
await progress2.race(import_fs37.default.promises.mkdir(import_path34.default.dirname(import_path34.default.join(tempDirWithRootName, item.name)), { recursive: true }));
const file = import_fs37.default.createWriteStream(import_path34.default.join(tempDirWithRootName, item.name));
return new WritableStreamDispatcher(this, file, item.lastModifiedMs);
}))
};
}
async exposeBinding(params2, progress2) {
const binding = await this._context.exposeBinding(progress2, params2.name, (source8, ...args) => {
if (this._disposed)
return;
const pageDispatcher = PageDispatcher.from(this, source8.page);
const binding2 = new BindingCallDispatcher(pageDispatcher, params2.name, source8, args);
this._dispatchEvent("bindingCall", { binding: binding2 });
return binding2.promise();
});
this._disposables.push(binding);
return { disposable: new DisposableDispatcher(this, binding) };
}
async newPage(params2, progress2) {
return { page: PageDispatcher.from(this, await this._context.newPage(progress2)) };
}
async cookies(params2, progress2) {
return { cookies: await this._context.cookies(progress2, params2.urls) };
}
async addCookies(params2, progress2) {
await progress2.race(this._context.addCookies(params2.cookies));
}
async clearCookies(params2, progress2) {
const nameRe = params2.nameRegexSource !== void 0 && params2.nameRegexFlags !== void 0 ? new RegExp(params2.nameRegexSource, params2.nameRegexFlags) : void 0;
const domainRe = params2.domainRegexSource !== void 0 && params2.domainRegexFlags !== void 0 ? new RegExp(params2.domainRegexSource, params2.domainRegexFlags) : void 0;
const pathRe = params2.pathRegexSource !== void 0 && params2.pathRegexFlags !== void 0 ? new RegExp(params2.pathRegexSource, params2.pathRegexFlags) : void 0;
await progress2.race(this._context.clearCookies({
name: nameRe || params2.name,
domain: domainRe || params2.domain,
path: pathRe || params2.path
}));
}
async grantPermissions(params2, progress2) {
await progress2.race(this._context.grantPermissions(params2.permissions, params2.origin));
}
async clearPermissions(params2, progress2) {
await progress2.race(this._context.clearPermissions());
}
async setGeolocation(params2, progress2) {
await progress2.race(this._context.setGeolocation(params2.geolocation));
}
async setExtraHTTPHeaders(params2, progress2) {
await this._context.setExtraHTTPHeaders(progress2, params2.headers);
}
async setOffline(params2, progress2) {
await this._context.setOffline(progress2, params2.offline);
}
async setHTTPCredentials(params2, progress2) {
await this._context.setHTTPCredentials(progress2, params2.httpCredentials);
}
async addInitScript(params2, progress2) {
const initScript = await this._context.addInitScript(progress2, params2.source);
this._disposables.push(initScript);
return { disposable: new DisposableDispatcher(this, initScript) };
}
async setNetworkInterceptionPatterns(params2, progress2) {
const hadMatchers = this._interceptionUrlMatchers.length > 0;
if (!params2.patterns.length) {
if (hadMatchers)
await progress2.race(this._context.removeRequestInterceptor(this._requestInterceptor));
this._interceptionUrlMatchers = [];
} else {
this._interceptionUrlMatchers = params2.patterns.map(deserializeURLMatch);
if (!hadMatchers)
await this._context.addRequestInterceptor(progress2, this._requestInterceptor);
}
}
async setWebSocketInterceptionPatterns(params2, progress2) {
this._webSocketInterceptionPatterns = params2.patterns;
if (params2.patterns.length && !this._routeWebSocketInitScript)
this._routeWebSocketInitScript = await WebSocketRouteDispatcher.install(progress2, this.connection, this._context);
}
async storageState(params2, progress2) {
return await this._context.storageState(progress2, params2.indexedDB);
}
async setStorageState(params2, progress2) {
await this._context.setStorageState(progress2, params2.storageState, "api");
}
async close(params2, progress2) {
progress2.metadata.potentiallyClosesScope = true;
await this._context.close(progress2, params2);
}
async enableRecorder(params2, progress2) {
await progress2.race(RecorderApp.show(this._context, params2));
}
async disableRecorder(params2, progress2) {
const recorder = await progress2.race(Recorder.existingForContext(this._context));
if (recorder)
await progress2.race(recorder.setMode("none"));
}
async exposeConsoleApi(params2, progress2) {
await this._context.exposeConsoleApi(progress2);
}
async pause(params2, progress2) {
}
async newCDPSession(params2, progress2) {
if (this._object._browser.options.browserType !== "chromium")
throw new Error(`CDP session is only available in Chromium`);
if (!params2.page && !params2.frame || params2.page && params2.frame)
throw new Error(`CDP session must be initiated with either Page or Frame, not none or both`);
const crBrowserContext = this._object;
return { session: new CDPSessionDispatcher(this, await progress2.race(crBrowserContext.newCDPSession((params2.page ? params2.page : params2.frame)._object))) };
}
async clockFastForward(params2, progress2) {
await progress2.race(this._context.clock.fastForward(params2.ticksString ?? params2.ticksNumber ?? 0));
}
async clockInstall(params2, progress2) {
await progress2.race(this._context.clock.install(params2.timeString ?? params2.timeNumber ?? void 0));
}
async clockPauseAt(params2, progress2) {
await progress2.race(this._context.clock.pauseAt(params2.timeString ?? params2.timeNumber ?? 0));
this._clockPaused = true;
}
async clockResume(params2, progress2) {
await this._context.clock.resume(progress2);
this._clockPaused = false;
}
async clockRunFor(params2, progress2) {
await progress2.race(this._context.clock.runFor(params2.ticksString ?? params2.ticksNumber ?? 0));
}
async clockSetFixedTime(params2, progress2) {
await progress2.race(this._context.clock.setFixedTime(params2.timeString ?? params2.timeNumber ?? 0));
}
async clockSetSystemTime(params2, progress2) {
await progress2.race(this._context.clock.setSystemTime(params2.timeString ?? params2.timeNumber ?? 0));
}
async updateSubscription(params2, progress2) {
if (params2.enabled)
this._subscriptions.add(params2.event);
else
this._subscriptions.delete(params2.event);
}
async registerSelectorEngine(params2, progress2) {
this._object.selectors().register(params2.selectorEngine);
}
async setTestIdAttributeName(params2, progress2) {
this._object.selectors().setTestIdAttributeName(params2.testIdAttributeName);
}
_onDispose() {
if (this._context.isClosingOrClosed())
return;
this._context.dialogManager.removeDialogHandler(this._dialogHandler);
this._interceptionUrlMatchers = [];
this._context.removeRequestInterceptor(this._requestInterceptor).catch(() => {
});
disposeAll2(this._disposables).catch(() => {
});
if (this._routeWebSocketInitScript)
WebSocketRouteDispatcher.uninstall(this.connection, this._context, this._routeWebSocketInitScript).catch(() => {
});
this._routeWebSocketInitScript = void 0;
if (this._clockPaused)
this._context.clock.resumeNoReply();
this._clockPaused = false;
}
};
}
});
// packages/playwright-core/src/server/dispatchers/elementHandlerDispatcher.ts
var ElementHandleDispatcher;
var init_elementHandlerDispatcher = __esm({
"packages/playwright-core/src/server/dispatchers/elementHandlerDispatcher.ts"() {
"use strict";
init_browserContextDispatcher();
init_frameDispatcher();
init_jsHandleDispatcher();
ElementHandleDispatcher = class _ElementHandleDispatcher extends JSHandleDispatcher {
constructor(scope, elementHandle) {
super(scope, elementHandle);
this._type_ElementHandle = true;
this._elementHandle = elementHandle;
}
static from(scope, handle) {
return scope.connection.existingDispatcher(handle) || new _ElementHandleDispatcher(scope, handle);
}
static fromNullable(scope, handle) {
if (!handle)
return void 0;
return scope.connection.existingDispatcher(handle) || new _ElementHandleDispatcher(scope, handle);
}
static fromJSOrElementHandle(scope, handle) {
const result2 = scope.connection.existingDispatcher(handle);
if (result2)
return result2;
const elementHandle = handle.asElement();
if (!elementHandle)
return new JSHandleDispatcher(scope, handle);
return new _ElementHandleDispatcher(scope, elementHandle);
}
async ownerFrame(params2, progress2) {
const frame = await this._elementHandle.ownerFrame(progress2);
return { frame: frame ? FrameDispatcher.from(this._browserContextDispatcher(), frame) : void 0 };
}
async contentFrame(params2, progress2) {
const frame = await this._elementHandle.contentFrame(progress2);
return { frame: frame ? FrameDispatcher.from(this._browserContextDispatcher(), frame) : void 0 };
}
async getAttribute(params2, progress2) {
const value2 = await this._elementHandle.getAttribute(progress2, params2.name);
return { value: value2 === null ? void 0 : value2 };
}
async inputValue(params2, progress2) {
const value2 = await this._elementHandle.inputValue(progress2);
return { value: value2 };
}
async textContent(params2, progress2) {
const value2 = await this._elementHandle.textContent(progress2);
return { value: value2 === null ? void 0 : value2 };
}
async innerText(params2, progress2) {
return { value: await this._elementHandle.innerText(progress2) };
}
async innerHTML(params2, progress2) {
return { value: await this._elementHandle.innerHTML(progress2) };
}
async isChecked(params2, progress2) {
return { value: await this._elementHandle.isChecked(progress2) };
}
async isDisabled(params2, progress2) {
return { value: await this._elementHandle.isDisabled(progress2) };
}
async isEditable(params2, progress2) {
return { value: await this._elementHandle.isEditable(progress2) };
}
async isEnabled(params2, progress2) {
return { value: await this._elementHandle.isEnabled(progress2) };
}
async isHidden(params2, progress2) {
return { value: await this._elementHandle.isHidden(progress2) };
}
async isVisible(params2, progress2) {
return { value: await this._elementHandle.isVisible(progress2) };
}
async dispatchEvent(params2, progress2) {
await this._elementHandle.dispatchEvent(progress2, params2.type, parseArgument(params2.eventInit));
}
async scrollIntoViewIfNeeded(params2, progress2) {
await this._elementHandle.scrollIntoViewIfNeeded(progress2);
}
async hover(params2, progress2) {
return await this._elementHandle.hover(progress2, params2);
}
async click(params2, progress2) {
return await this._elementHandle.click(progress2, params2);
}
async dblclick(params2, progress2) {
return await this._elementHandle.dblclick(progress2, params2);
}
async tap(params2, progress2) {
return await this._elementHandle.tap(progress2, params2);
}
async selectOption(params2, progress2) {
const elements = (params2.elements || []).map((e) => e._elementHandle);
return { values: await this._elementHandle.selectOption(progress2, elements, params2.options || [], params2) };
}
async fill(params2, progress2) {
return await this._elementHandle.fill(progress2, params2.value, params2);
}
async selectText(params2, progress2) {
await this._elementHandle.selectText(progress2, params2);
}
async setInputFiles(params2, progress2) {
return await this._elementHandle.setInputFiles(progress2, params2);
}
async focus(params2, progress2) {
await this._elementHandle.focus(progress2);
}
async type(params2, progress2) {
return await this._elementHandle.type(progress2, params2.text, params2);
}
async press(params2, progress2) {
return await this._elementHandle.press(progress2, params2.key, params2);
}
async check(params2, progress2) {
return await this._elementHandle.check(progress2, params2);
}
async uncheck(params2, progress2) {
return await this._elementHandle.uncheck(progress2, params2);
}
async boundingBox(params2, progress2) {
const value2 = await this._elementHandle.boundingBox(progress2);
return { value: value2 || void 0 };
}
async screenshot(params2, progress2) {
const mask = (params2.mask || []).map(({ frame, selector }) => ({
frame: frame._object,
selector
}));
return { binary: await this._elementHandle.screenshot(progress2, { ...params2, mask }) };
}
async querySelector(params2, progress2) {
const handle = await this._elementHandle.querySelector(progress2, params2.selector, params2);
return { element: _ElementHandleDispatcher.fromNullable(this.parentScope(), handle) };
}
async querySelectorAll(params2, progress2) {
const elements = await this._elementHandle.querySelectorAll(progress2, params2.selector);
return { elements: elements.map((e) => _ElementHandleDispatcher.from(this.parentScope(), e)) };
}
async evalOnSelector(params2, progress2) {
return { value: serializeResult(await this._elementHandle.evalOnSelector(progress2, params2.selector, !!params2.strict, params2.expression, params2.isFunction, parseArgument(params2.arg))) };
}
async evalOnSelectorAll(params2, progress2) {
return { value: serializeResult(await this._elementHandle.evalOnSelectorAll(progress2, params2.selector, params2.expression, params2.isFunction, parseArgument(params2.arg))) };
}
async waitForElementState(params2, progress2) {
await this._elementHandle.waitForElementState(progress2, params2.state);
}
async waitForSelector(params2, progress2) {
return { element: _ElementHandleDispatcher.fromNullable(this.parentScope(), await this._elementHandle.waitForSelector(progress2, params2.selector, params2)) };
}
_browserContextDispatcher() {
const parentScope = this.parentScope().parentScope();
if (parentScope instanceof BrowserContextDispatcher)
return parentScope;
return parentScope.parentScope();
}
};
}
});
// packages/playwright-core/src/server/dispatchers/frameDispatcher.ts
var yaml2, FrameDispatcher;
var init_frameDispatcher = __esm({
"packages/playwright-core/src/server/dispatchers/frameDispatcher.ts"() {
"use strict";
init_ariaSnapshot();
init_frames();
init_dispatcher();
init_elementHandlerDispatcher();
init_jsHandleDispatcher();
init_networkDispatchers();
init_networkDispatchers();
yaml2 = require("./utilsBundle").yaml;
FrameDispatcher = class _FrameDispatcher extends Dispatcher {
constructor(scope, frame) {
const gcBucket = frame._page.mainFrame() === frame ? "MainFrame" : "Frame";
const pageDispatcher = scope.connection.existingDispatcher(frame._page);
super(pageDispatcher || scope, frame, "Frame", {
url: frame.url(),
name: frame.name(),
parentFrame: _FrameDispatcher.fromNullable(scope, frame.parentFrame()),
loadStates: Array.from(frame._firedLifecycleEvents)
}, gcBucket);
this._type_Frame = true;
this._browserContextDispatcher = scope;
this._frame = frame;
this.addObjectListener(Frame.Events.AddLifecycle, (lifecycleEvent) => {
this._dispatchEvent("loadstate", { add: lifecycleEvent });
});
this.addObjectListener(Frame.Events.RemoveLifecycle, (lifecycleEvent) => {
this._dispatchEvent("loadstate", { remove: lifecycleEvent });
});
this.addObjectListener(Frame.Events.InternalNavigation, (event) => {
if (!event.isPublic)
return;
const params2 = { url: event.url, name: event.name, error: event.error ? event.error.message : void 0 };
if (event.newDocument)
params2.newDocument = { request: RequestDispatcher.fromNullable(this._browserContextDispatcher, event.newDocument.request || null) };
this._dispatchEvent("navigated", params2);
});
}
static from(scope, frame) {
const result2 = scope.connection.existingDispatcher(frame);
return result2 || new _FrameDispatcher(scope, frame);
}
static fromNullable(scope, frame) {
if (!frame)
return;
return _FrameDispatcher.from(scope, frame);
}
async goto(params2, progress2) {
return { response: ResponseDispatcher.fromNullable(this._browserContextDispatcher, await this._frame.goto(progress2, params2.url, params2)) };
}
async frameElement(params2, progress2) {
return { element: ElementHandleDispatcher.from(this, await this._frame.frameElement(progress2)) };
}
async evaluateExpression(params2, progress2) {
return { value: serializeResult(await this._frame.evaluateExpression(progress2, params2.expression, { isFunction: params2.isFunction }, parseArgument(params2.arg))) };
}
async evaluateExpressionHandle(params2, progress2) {
return { handle: ElementHandleDispatcher.fromJSOrElementHandle(this, await this._frame.evaluateExpressionHandle(progress2, params2.expression, { isFunction: params2.isFunction }, parseArgument(params2.arg))) };
}
async waitForSelector(params2, progress2) {
return { element: ElementHandleDispatcher.fromNullable(this, await this._frame.waitForSelector(progress2, params2.selector, true, params2)) };
}
async dispatchEvent(params2, progress2) {
return this._frame.dispatchEvent(progress2, params2.selector, params2.type, parseArgument(params2.eventInit), params2);
}
async evalOnSelector(params2, progress2) {
return { value: serializeResult(await this._frame.evalOnSelector(progress2, params2.selector, !!params2.strict, params2.expression, params2.isFunction, parseArgument(params2.arg))) };
}
async evalOnSelectorAll(params2, progress2) {
return { value: serializeResult(await this._frame.evalOnSelectorAll(progress2, params2.selector, params2.expression, params2.isFunction, parseArgument(params2.arg))) };
}
async querySelector(params2, progress2) {
return { element: ElementHandleDispatcher.fromNullable(this, await this._frame.querySelector(progress2, params2.selector, params2)) };
}
async querySelectorAll(params2, progress2) {
const elements = await this._frame.querySelectorAll(progress2, params2.selector);
return { elements: elements.map((e) => ElementHandleDispatcher.from(this, e)) };
}
async queryCount(params2, progress2) {
return { value: await this._frame.queryCount(progress2, params2.selector, params2) };
}
async content(params2, progress2) {
return { value: await this._frame.content(progress2) };
}
async setContent(params2, progress2) {
return await this._frame.setContent(progress2, params2.html, params2);
}
async addScriptTag(params2, progress2) {
return { element: ElementHandleDispatcher.from(this, await this._frame.addScriptTag(progress2, params2)) };
}
async addStyleTag(params2, progress2) {
return { element: ElementHandleDispatcher.from(this, await this._frame.addStyleTag(progress2, params2)) };
}
async ariaSnapshot(params2, progress2) {
return await this._frame.ariaSnapshot(progress2, params2);
}
async click(params2, progress2) {
progress2.metadata.potentiallyClosesScope = true;
return await this._frame.click(progress2, params2.selector, params2);
}
async dblclick(params2, progress2) {
return await this._frame.dblclick(progress2, params2.selector, params2);
}
async dragAndDrop(params2, progress2) {
return await this._frame.dragAndDrop(progress2, params2.source, params2.target, params2);
}
async drop(params2, progress2) {
return await this._frame.drop(progress2, params2.selector, params2, params2);
}
async tap(params2, progress2) {
return await this._frame.tap(progress2, params2.selector, params2);
}
async fill(params2, progress2) {
return await this._frame.fill(progress2, params2.selector, params2.value, params2);
}
async focus(params2, progress2) {
await this._frame.focus(progress2, params2.selector, params2);
}
async blur(params2, progress2) {
await this._frame.blur(progress2, params2.selector, params2);
}
async textContent(params2, progress2) {
const value2 = await this._frame.textContent(progress2, params2.selector, params2);
return { value: value2 === null ? void 0 : value2 };
}
async innerText(params2, progress2) {
return { value: await this._frame.innerText(progress2, params2.selector, params2) };
}
async innerHTML(params2, progress2) {
return { value: await this._frame.innerHTML(progress2, params2.selector, params2) };
}
async resolveSelector(params2, progress2) {
return await this._frame.resolveSelector(progress2, params2.selector);
}
async getAttribute(params2, progress2) {
const value2 = await this._frame.getAttribute(progress2, params2.selector, params2.name, params2);
return { value: value2 === null ? void 0 : value2 };
}
async inputValue(params2, progress2) {
const value2 = await this._frame.inputValue(progress2, params2.selector, params2);
return { value: value2 };
}
async isChecked(params2, progress2) {
return { value: await this._frame.isChecked(progress2, params2.selector, params2) };
}
async isDisabled(params2, progress2) {
return { value: await this._frame.isDisabled(progress2, params2.selector, params2) };
}
async isEditable(params2, progress2) {
return { value: await this._frame.isEditable(progress2, params2.selector, params2) };
}
async isEnabled(params2, progress2) {
return { value: await this._frame.isEnabled(progress2, params2.selector, params2) };
}
async isHidden(params2, progress2) {
return { value: await this._frame.isHidden(progress2, params2.selector, params2) };
}
async isVisible(params2, progress2) {
return { value: await this._frame.isVisible(progress2, params2.selector, params2) };
}
async hover(params2, progress2) {
return await this._frame.hover(progress2, params2.selector, params2);
}
async selectOption(params2, progress2) {
const elements = (params2.elements || []).map((e) => e._elementHandle);
return { values: await this._frame.selectOption(progress2, params2.selector, elements, params2.options || [], params2) };
}
async setInputFiles(params2, progress2) {
return await this._frame.setInputFiles(progress2, params2.selector, params2);
}
async type(params2, progress2) {
return await this._frame.type(progress2, params2.selector, params2.text, params2);
}
async press(params2, progress2) {
return await this._frame.press(progress2, params2.selector, params2.key, params2);
}
async check(params2, progress2) {
return await this._frame.check(progress2, params2.selector, params2);
}
async uncheck(params2, progress2) {
return await this._frame.uncheck(progress2, params2.selector, params2);
}
async waitForTimeout(params2, progress2) {
return await this._frame.waitForTimeout(progress2, params2.waitTimeout);
}
async waitForFunction(params2, progress2) {
return { handle: ElementHandleDispatcher.fromJSOrElementHandle(this, await this._frame.waitForFunctionExpression(progress2, params2.expression, params2.isFunction, parseArgument(params2.arg), params2)) };
}
async title(params2, progress2) {
return { value: await this._frame.title(progress2) };
}
async highlight(params2, progress2) {
return await this._frame.addHighlight(progress2, params2.selector, params2.style);
}
async hideHighlight(params2, progress2) {
return await this._frame.removeHighlight(progress2, params2.selector);
}
async expect(params2, progress2) {
progress2.metadata.potentiallyClosesScope = true;
let expectedValue = params2.expectedValue ? parseArgument(params2.expectedValue) : void 0;
if (params2.expression === "to.match.aria" && expectedValue)
expectedValue = parseAriaSnapshotUnsafe(yaml2, expectedValue);
const result2 = await this._frame.expect(progress2, params2.selector, { ...params2, expectedValue, timeoutForLogs: params2.timeout });
const channelResult = {
matches: result2.matches,
log: result2.log,
timedOut: result2.timedOut,
errorMessage: result2.errorMessage
};
if (result2.received !== void 0) {
channelResult.received = {
value: result2.received.value !== void 0 ? serializeResult(result2.received.value) : void 0,
ariaSnapshot: result2.received.ariaSnapshot
};
}
return channelResult;
}
};
}
});
// packages/playwright-core/src/server/dispatchers/networkDispatchers.ts
var RequestDispatcher, ResponseDispatcher, RouteDispatcher, WebSocketDispatcher, APIRequestContextDispatcher;
var init_networkDispatchers = __esm({
"packages/playwright-core/src/server/dispatchers/networkDispatchers.ts"() {
"use strict";
init_network2();
init_dispatcher();
init_frameDispatcher();
init_pageDispatcher();
init_tracingDispatcher();
RequestDispatcher = class _RequestDispatcher extends Dispatcher {
static from(scope, request2) {
const result2 = scope.connection.existingDispatcher(request2);
return result2 || new _RequestDispatcher(scope, request2);
}
static fromNullable(scope, request2) {
return request2 ? _RequestDispatcher.from(scope, request2) : void 0;
}
constructor(scope, request2) {
const postData = request2.postDataBuffer();
const frame = request2.frame();
const page = request2.frame()?._page;
const pageDispatcher = page ? scope.connection.existingDispatcher(page) : null;
const frameDispatcher = FrameDispatcher.fromNullable(scope, frame);
super(pageDispatcher || frameDispatcher || scope, request2, "Request", {
frame: frameDispatcher,
serviceWorker: WorkerDispatcher.fromNullable(scope, request2.serviceWorker()),
url: request2.url(),
resourceType: request2.resourceType(),
method: request2.method(),
postData: postData === null ? void 0 : postData,
headers: request2.headers(),
isNavigationRequest: request2.isNavigationRequest(),
redirectedFrom: _RequestDispatcher.fromNullable(scope, request2.redirectedFrom())
});
this._type_Request = true;
this._browserContextDispatcher = scope;
ResponseDispatcher.fromNullable(scope, request2._existingResponse());
}
async rawRequestHeaders(params2, progress2) {
return { headers: await this._object.rawRequestHeaders(progress2) };
}
async response(params2, progress2) {
return { response: ResponseDispatcher.fromNullable(this._browserContextDispatcher, await this._object.response(progress2)) };
}
};
ResponseDispatcher = class _ResponseDispatcher extends Dispatcher {
constructor(scope, response2) {
super(scope, response2, "Response", {
// TODO: responses in popups can point to non-reported requests.
request: scope,
url: response2.url(),
status: response2.status(),
statusText: response2.statusText(),
headers: response2.headers(),
timing: response2.timing(),
fromServiceWorker: response2.fromServiceWorker()
});
this._type_Response = true;
}
static from(scope, response2) {
const requestDispatcher = RequestDispatcher.from(scope, response2.request());
const result2 = scope.connection.existingDispatcher(response2);
return result2 || new _ResponseDispatcher(requestDispatcher, response2);
}
static fromNullable(scope, response2) {
return response2 ? _ResponseDispatcher.from(scope, response2) : void 0;
}
async body(params2, progress2) {
return { binary: await this._object.body(progress2) };
}
async securityDetails(params2, progress2) {
return { value: await this._object.securityDetails(progress2) || void 0 };
}
async serverAddr(params2, progress2) {
return { value: await this._object.serverAddr(progress2) || void 0 };
}
async rawResponseHeaders(params2, progress2) {
return { headers: await this._object.rawResponseHeaders(progress2) };
}
async httpVersion(params2, progress2) {
return { value: await this._object.httpVersion(progress2) };
}
async sizes(params2, progress2) {
return { sizes: await this._object.sizes(progress2) };
}
};
RouteDispatcher = class extends Dispatcher {
constructor(scope, route2) {
super(scope, route2, "Route", {
// Context route can point to a non-reported request, so we send the request in the initializer.
request: scope
});
this._type_Route = true;
this._handled = false;
}
_checkNotHandled() {
if (this._handled)
throw new Error("Route is already handled!");
this._handled = true;
}
async continue(params2, progress2) {
this._checkNotHandled();
await progress2.race(this._object.continue({
url: params2.url,
method: params2.method,
headers: params2.headers,
postData: params2.postData,
isFallback: params2.isFallback
}));
}
async fulfill(params2, progress2) {
this._checkNotHandled();
await progress2.race(this._object.fulfill(params2));
}
async abort(params2, progress2) {
this._checkNotHandled();
await progress2.race(this._object.abort(params2.errorCode || "failed"));
}
async redirectNavigationRequest(params2, progress2) {
this._checkNotHandled();
this._object.redirectNavigationRequest(params2.url);
}
};
WebSocketDispatcher = class extends Dispatcher {
constructor(scope, webSocket) {
super(scope, webSocket, "WebSocket", {
url: webSocket.url()
});
this._type_EventTarget = true;
this._type_WebSocket = true;
this.addObjectListener(WebSocket.Events.FrameSent, (event) => this._dispatchEvent("frameSent", event));
this.addObjectListener(WebSocket.Events.FrameReceived, (event) => this._dispatchEvent("frameReceived", event));
this.addObjectListener(WebSocket.Events.SocketError, (error) => this._dispatchEvent("socketError", { error }));
this.addObjectListener(WebSocket.Events.Close, () => this._dispatchEvent("close", {}));
}
};
APIRequestContextDispatcher = class _APIRequestContextDispatcher extends Dispatcher {
constructor(parentScope, request2) {
const tracing = TracingDispatcher.from(parentScope, request2.tracing());
super(parentScope, request2, "APIRequestContext", {
tracing
});
this._type_APIRequestContext = true;
this.adopt(tracing);
}
static from(scope, request2) {
const result2 = scope.connection.existingDispatcher(request2);
return result2 || new _APIRequestContextDispatcher(scope, request2);
}
static fromNullable(scope, request2) {
return request2 ? _APIRequestContextDispatcher.from(scope, request2) : void 0;
}
async storageState(params2, progress2) {
return await this._object.storageState(progress2, params2.indexedDB);
}
async dispose(params2, progress2) {
progress2.metadata.potentiallyClosesScope = true;
await progress2.race(this._object.dispose(params2));
this._dispose();
}
async fetch(params2, progress2) {
const response2 = await this._object.fetch(progress2, params2);
return { response: response2 };
}
async fetchResponseBody(params2, progress2) {
return { binary: this._object.fetchResponseBody(progress2, params2.fetchUid) };
}
async fetchLog(params2, progress2) {
return { log: this._object.fetchLogForUid(progress2, params2.fetchUid) };
}
async disposeAPIResponse(params2, progress2) {
this._object.disposeResponse(progress2, params2.fetchUid);
}
};
}
});
// packages/playwright-core/src/server/dispatchers/androidDispatcher.ts
function fixupAndroidElementInfo(info) {
info.clazz = info.clazz || "";
info.pkg = info.pkg || "";
info.res = info.res || "";
info.desc = info.desc || "";
info.text = info.text || "";
for (const child of info.children || [])
fixupAndroidElementInfo(child);
}
var AndroidDispatcher, AndroidDeviceDispatcher, SocketSdkObject, AndroidSocketDispatcher, keyMap;
var init_androidDispatcher = __esm({
"packages/playwright-core/src/server/dispatchers/androidDispatcher.ts"() {
"use strict";
init_eventsHelper();
init_browserContextDispatcher();
init_dispatcher();
init_android();
init_instrumentation();
AndroidDispatcher = class extends Dispatcher {
constructor(scope, android, denyLaunch) {
super(scope, android, "Android", {});
this._type_Android = true;
this._denyLaunch = denyLaunch;
}
async devices(params2, progress2) {
if (this._denyLaunch)
throw new Error(`Connecting to Android devices is not allowed.`);
const devices = await this._object.devices(progress2, params2);
return {
devices: devices.map((d) => AndroidDeviceDispatcher.from(this, d))
};
}
};
AndroidDeviceDispatcher = class _AndroidDeviceDispatcher extends Dispatcher {
constructor(scope, device) {
super(scope, device, "AndroidDevice", {
model: device.model,
serial: device.serial
});
this._type_EventTarget = true;
this._type_AndroidDevice = true;
for (const webView of device.webViews())
this._dispatchEvent("webViewAdded", { webView });
this.addObjectListener(AndroidDevice.Events.WebViewAdded, (webView) => this._dispatchEvent("webViewAdded", { webView }));
this.addObjectListener(AndroidDevice.Events.WebViewRemoved, (socketName) => this._dispatchEvent("webViewRemoved", { socketName }));
this.addObjectListener(AndroidDevice.Events.Close, () => this._dispatchEvent("close"));
}
static from(scope, device) {
const result2 = scope.connection.existingDispatcher(device);
return result2 || new _AndroidDeviceDispatcher(scope, device);
}
async wait(params2, progress2) {
await this._object.send(progress2, "wait", params2);
}
async fill(params2, progress2) {
await this._object.send(progress2, "click", { selector: params2.androidSelector });
await this._object.send(progress2, "fill", params2);
}
async tap(params2, progress2) {
await this._object.send(progress2, "click", params2);
}
async drag(params2, progress2) {
await this._object.send(progress2, "drag", params2);
}
async fling(params2, progress2) {
await this._object.send(progress2, "fling", params2);
}
async longTap(params2, progress2) {
await this._object.send(progress2, "longClick", params2);
}
async pinchClose(params2, progress2) {
await this._object.send(progress2, "pinchClose", params2);
}
async pinchOpen(params2, progress2) {
await this._object.send(progress2, "pinchOpen", params2);
}
async scroll(params2, progress2) {
await this._object.send(progress2, "scroll", params2);
}
async swipe(params2, progress2) {
await this._object.send(progress2, "swipe", params2);
}
async info(params2, progress2) {
const info = await this._object.send(progress2, "info", params2);
fixupAndroidElementInfo(info);
return { info };
}
async inputType(params2, progress2) {
const text2 = params2.text;
const keyCodes = [];
for (let i = 0; i < text2.length; ++i) {
const code = keyMap.get(text2[i].toUpperCase());
if (code === void 0)
throw new Error("No mapping for " + text2[i] + " found");
keyCodes.push(code);
}
await progress2.race(Promise.all(keyCodes.map((keyCode) => this._object.send(progress2, "inputPress", { keyCode }))));
}
async inputPress(params2, progress2) {
if (!keyMap.has(params2.key))
throw new Error("Unknown key: " + params2.key);
await this._object.send(progress2, "inputPress", { keyCode: keyMap.get(params2.key) });
}
async inputTap(params2, progress2) {
await this._object.send(progress2, "inputClick", params2);
}
async inputSwipe(params2, progress2) {
await this._object.send(progress2, "inputSwipe", params2);
}
async inputDrag(params2, progress2) {
await this._object.send(progress2, "inputDrag", params2);
}
async screenshot(params2, progress2) {
return { binary: await this._object.screenshot(progress2) };
}
async shell(params2, progress2) {
return { result: await this._object.shell(progress2, params2.command) };
}
async open(params2, progress2) {
const socket = await this._object.open(progress2, params2.command);
return { socket: new AndroidSocketDispatcher(this, new SocketSdkObject(this._object, socket)) };
}
async installApk(params2, progress2) {
await this._object.installApk(progress2, params2.file, { args: params2.args });
}
async push(params2, progress2) {
await this._object.push(progress2, params2.file, params2.path, params2.mode);
}
async launchBrowser(params2, progress2) {
const context2 = await this._object.launchBrowser(progress2, params2.pkg, params2);
return { context: BrowserContextDispatcher.from(this, context2) };
}
async close(params2, progress2) {
await this._object.close(progress2);
}
async connectToWebView(params2, progress2) {
return { context: BrowserContextDispatcher.from(this, await this._object.connectToWebView(progress2, params2.socketName)) };
}
};
SocketSdkObject = class extends SdkObject {
constructor(parent, socket) {
super(parent, "socket");
this._socket = socket;
this._eventListeners = [
eventsHelper.addEventListener(socket, "data", (data) => this.emit("data", data)),
eventsHelper.addEventListener(socket, "close", () => {
eventsHelper.removeEventListeners(this._eventListeners);
this.emit("close");
})
];
}
async write(data) {
await this._socket.write(data);
}
close() {
this._socket.close();
}
};
AndroidSocketDispatcher = class extends Dispatcher {
constructor(scope, socket) {
super(scope, socket, "AndroidSocket", {});
this._type_AndroidSocket = true;
this.addObjectListener("data", (data) => this._dispatchEvent("data", { data }));
this.addObjectListener("close", () => {
this._dispatchEvent("close");
this._dispose();
});
}
async write(params2, progress2) {
await progress2.race(this._object.write(params2.data));
}
async close(params2, progress2) {
this._object.close();
}
};
keyMap = /* @__PURE__ */ new Map([
["Unknown", 0],
["SoftLeft", 1],
["SoftRight", 2],
["Home", 3],
["Back", 4],
["Call", 5],
["EndCall", 6],
["0", 7],
["1", 8],
["2", 9],
["3", 10],
["4", 11],
["5", 12],
["6", 13],
["7", 14],
["8", 15],
["9", 16],
["Star", 17],
["*", 17],
["Pound", 18],
["#", 18],
["DialUp", 19],
["DialDown", 20],
["DialLeft", 21],
["DialRight", 22],
["DialCenter", 23],
["VolumeUp", 24],
["VolumeDown", 25],
["Power", 26],
["Camera", 27],
["Clear", 28],
["A", 29],
["B", 30],
["C", 31],
["D", 32],
["E", 33],
["F", 34],
["G", 35],
["H", 36],
["I", 37],
["J", 38],
["K", 39],
["L", 40],
["M", 41],
["N", 42],
["O", 43],
["P", 44],
["Q", 45],
["R", 46],
["S", 47],
["T", 48],
["U", 49],
["V", 50],
["W", 51],
["X", 52],
["Y", 53],
["Z", 54],
["Comma", 55],
[",", 55],
["Period", 56],
[".", 56],
["AltLeft", 57],
["AltRight", 58],
["ShiftLeft", 59],
["ShiftRight", 60],
["Tab", 61],
[" ", 61],
["Space", 62],
[" ", 62],
["Sym", 63],
["Explorer", 64],
["Envelop", 65],
["Enter", 66],
["Del", 67],
["Grave", 68],
["Minus", 69],
["-", 69],
["Equals", 70],
["=", 70],
["LeftBracket", 71],
["(", 71],
["RightBracket", 72],
[")", 72],
["Backslash", 73],
["\\", 73],
["Semicolon", 74],
[";", 74],
["Apostrophe", 75],
["`", 75],
["Slash", 76],
["/", 76],
["At", 77],
["@", 77],
["Num", 78],
["HeadsetHook", 79],
["Focus", 80],
["Plus", 81],
["Menu", 82],
["Notification", 83],
["Search", 84],
["MediaPlayPause", 85],
["MediaStop", 86],
["MediaNext", 87],
["MediaPrevious", 88],
["MediaRewind", 89],
["MediaFastForward", 90],
["MediaPlay", 126],
["MediaPause", 127],
["MediaClose", 128],
["MediaEject", 129],
["MediaRecord", 130],
["ChannelUp", 166],
["ChannelDown", 167],
["AppSwitch", 187],
["Assist", 219],
["MediaAudioTrack", 222],
["MediaTopMenu", 226],
["MediaSkipForward", 272],
["MediaSkipBackward", 273],
["MediaStepForward", 274],
["MediaStepBackward", 275],
["Cut", 277],
["Copy", 278],
["Paste", 279]
]);
}
});
// packages/playwright-core/src/server/dispatchers/browserDispatcher.ts
var BrowserDispatcher;
var init_browserDispatcher = __esm({
"packages/playwright-core/src/server/dispatchers/browserDispatcher.ts"() {
"use strict";
init_browser();
init_browserContextDispatcher();
init_cdpSessionDispatcher();
init_dispatcher();
init_browserContext();
init_artifactDispatcher();
init_progress();
BrowserDispatcher = class extends Dispatcher {
constructor(scope, browser, options2 = {}) {
super(scope, browser, "Browser", { version: browser.version(), name: browser.options.name, browserName: browser.options.browserType });
this._type_Browser = true;
this._isolatedContexts = /* @__PURE__ */ new Set();
this._options = options2;
if (!options2.isolateContexts) {
this.addObjectListener(Browser.Events.Context, (context2) => this._dispatchEvent("context", { context: BrowserContextDispatcher.from(this, context2) }));
this.addObjectListener(Browser.Events.Disconnected, () => this._didClose());
if (browser._defaultContext)
this._dispatchEvent("context", { context: BrowserContextDispatcher.from(this, browser._defaultContext) });
for (const context2 of browser.contexts())
this._dispatchEvent("context", { context: BrowserContextDispatcher.from(this, context2) });
}
}
_didClose() {
this._dispatchEvent("close");
this._dispose();
}
async newContext(params2, progress2) {
if (params2.recordVideo && this._object.attribution.playwright.options.isServer)
params2.recordVideo.dir = void 0;
if (!this._options.isolateContexts) {
const context3 = await this._object.newContext(progress2, params2);
const contextDispatcher2 = BrowserContextDispatcher.from(this, context3);
return { context: contextDispatcher2 };
}
const context2 = await this._object.newContext(progress2, params2);
this._isolatedContexts.add(context2);
context2.on(BrowserContext.Events.Close, () => this._isolatedContexts.delete(context2));
const contextDispatcher = BrowserContextDispatcher.from(this, context2);
this._dispatchEvent("context", { context: contextDispatcher });
return { context: contextDispatcher };
}
async newContextForReuse(params2, progress2) {
const context2 = await this._object.newContextForReuse(progress2, params2);
const contextDispatcher = BrowserContextDispatcher.from(this, context2);
this._dispatchEvent("context", { context: contextDispatcher });
return { context: contextDispatcher };
}
async disconnectFromReusedContext(params2, progress2) {
const context2 = this._object.contextForReuse();
const contextDispatcher = context2 ? this.connection.existingDispatcher(context2) : void 0;
if (contextDispatcher) {
await progress2.race(contextDispatcher.stopPendingOperations(new Error(params2.reason)));
contextDispatcher._dispose();
}
}
async close(params2, progress2) {
if (this._options.ignoreStopAndKill)
return;
progress2.metadata.potentiallyClosesScope = true;
await this._object.close(progress2, params2);
}
async killForTests(params2, progress2) {
if (this._options.ignoreStopAndKill)
return;
progress2.metadata.potentiallyClosesScope = true;
await this._object.killForTests(progress2);
}
async defaultUserAgentForTest() {
return { userAgent: this._object.userAgent() };
}
async newBrowserCDPSession(params2, progress2) {
if (this._object.options.browserType !== "chromium")
throw new Error(`CDP session is only available in Chromium`);
const crBrowser = this._object;
return { session: new CDPSessionDispatcher(this, await progress2.race(crBrowser.newBrowserCDPSession())) };
}
async startTracing(params2, progress2) {
if (this._object.options.browserType !== "chromium")
throw new Error(`Tracing is only available in Chromium`);
const crBrowser = this._object;
await progress2.race(crBrowser.startTracing(params2.page ? params2.page._object : void 0, params2));
}
async stopTracing(params2, progress2) {
if (this._object.options.browserType !== "chromium")
throw new Error(`Tracing is only available in Chromium`);
const crBrowser = this._object;
return { artifact: ArtifactDispatcher.from(this, await progress2.race(crBrowser.stopTracing())) };
}
async startServer(params2, progress2) {
return await this._object.startServer(progress2, params2.title, params2);
}
async stopServer(params2, progress2) {
await this._object.stopServer(progress2);
}
async cleanupContexts() {
await Promise.all(Array.from(this._isolatedContexts).map((context2) => context2.close(nullProgress, { reason: "Global context cleanup (connection terminated)" })));
}
};
}
});
// packages/playwright-core/src/server/dispatchers/browserTypeDispatcher.ts
var BrowserTypeDispatcher;
var init_browserTypeDispatcher = __esm({
"packages/playwright-core/src/server/dispatchers/browserTypeDispatcher.ts"() {
"use strict";
init_browserContextDispatcher();
init_browserDispatcher();
init_pageDispatcher();
init_dispatcher();
BrowserTypeDispatcher = class extends Dispatcher {
constructor(scope, browserType, denyLaunch) {
super(scope, browserType, "BrowserType", {
executablePath: browserType.executablePath(),
name: browserType.name()
});
this._type_BrowserType = true;
this._denyLaunch = denyLaunch;
}
async launch(params2, progress2) {
if (this._denyLaunch)
throw new Error(`Launching more browsers is not allowed.`);
const browser = await this._object.launch(progress2, params2);
return { browser: new BrowserDispatcher(this, browser) };
}
async launchPersistentContext(params2, progress2) {
if (this._denyLaunch)
throw new Error(`Launching more browsers is not allowed.`);
const browserContext = await this._object.launchPersistentContext(progress2, params2.userDataDir, params2);
const browserDispatcher = new BrowserDispatcher(this, browserContext._browser);
const contextDispatcher = BrowserContextDispatcher.from(browserDispatcher, browserContext);
return { browser: browserDispatcher, context: contextDispatcher };
}
async connectOverCDP(params2, progress2) {
if (this._denyLaunch)
throw new Error(`Launching more browsers is not allowed.`);
const browser = await this._object.connectOverCDP(progress2, params2.endpointURL, params2);
const browserDispatcher = new BrowserDispatcher(this, browser);
return {
browser: browserDispatcher,
defaultContext: browser._defaultContext ? BrowserContextDispatcher.from(browserDispatcher, browser._defaultContext) : void 0
};
}
async connectToWorker(params2, progress2) {
if (this._denyLaunch)
throw new Error(`Launching more browsers is not allowed.`);
const worker = await this._object.connectToWorker(progress2, params2.endpoint);
return { worker: new WorkerDispatcher(this, worker) };
}
};
}
});
// packages/playwright-core/src/server/dispatchers/electronDispatcher.ts
var ElectronDispatcher, ElectronApplicationDispatcher;
var init_electronDispatcher = __esm({
"packages/playwright-core/src/server/dispatchers/electronDispatcher.ts"() {
"use strict";
init_browserContextDispatcher();
init_dispatcher();
init_jsHandleDispatcher();
init_electron();
ElectronDispatcher = class extends Dispatcher {
constructor(scope, electron, denyLaunch) {
super(scope, electron, "Electron", {});
this._type_Electron = true;
this._denyLaunch = denyLaunch;
}
async launch(params2, progress2) {
if (this._denyLaunch)
throw new Error(`Launching more browsers is not allowed.`);
const electronApplication = await this._object.launch(progress2, params2);
return { electronApplication: new ElectronApplicationDispatcher(this, electronApplication) };
}
};
ElectronApplicationDispatcher = class extends Dispatcher {
constructor(scope, electronApplication) {
super(scope, electronApplication, "ElectronApplication", {
context: BrowserContextDispatcher.from(scope, electronApplication.context())
});
this._type_EventTarget = true;
this._type_ElectronApplication = true;
this._subscriptions = /* @__PURE__ */ new Set();
this.addObjectListener(ElectronApplication.Events.Close, () => {
this._dispatchEvent("close");
this._dispose();
});
this.addObjectListener(ElectronApplication.Events.Console, (message) => {
if (!this._subscriptions.has("console"))
return;
this._dispatchEvent("console", {
type: message.type(),
text: message.text(),
args: message.args().map((a) => JSHandleDispatcher.fromJSHandle(this, a)),
location: message.location(),
timestamp: message.timestamp()
});
});
}
async browserWindow(params2, progress2) {
const handle = await this._object.browserWindow(progress2, params2.page.page());
return { handle: JSHandleDispatcher.fromJSHandle(this, handle) };
}
async evaluateExpression(params2, progress2) {
const handle = await progress2.race(this._object._nodeElectronHandlePromise);
return { value: serializeResult(await handle.evaluateExpression(progress2, params2.expression, { isFunction: params2.isFunction }, parseArgument(params2.arg))) };
}
async evaluateExpressionHandle(params2, progress2) {
const handle = await progress2.race(this._object._nodeElectronHandlePromise);
const result2 = await handle.evaluateExpressionHandle(progress2, params2.expression, { isFunction: params2.isFunction }, parseArgument(params2.arg));
return { handle: JSHandleDispatcher.fromJSHandle(this, result2) };
}
async updateSubscription(params2, progress2) {
if (params2.enabled)
this._subscriptions.add(params2.event);
else
this._subscriptions.delete(params2.event);
}
};
}
});
// packages/playwright-core/src/server/harBackend.ts
function countMatchingHeaders(harHeaders, headers) {
const set = new Set(headers.map((h) => h.name.toLowerCase() + ":" + h.value));
let matches = 0;
for (const h of harHeaders) {
if (set.has(h.name.toLowerCase() + ":" + h.value))
++matches;
}
return matches;
}
function multipartBoundary(headers) {
const contentType = headers.find((h) => h.name.toLowerCase() === "content-type");
if (!contentType?.value.includes("multipart/form-data"))
return void 0;
const boundary = contentType.value.match(/boundary=(\S+)/);
if (boundary)
return boundary[1];
return void 0;
}
var import_fs38, import_path35, redirectStatus2, HarBackend;
var init_harBackend = __esm({
"packages/playwright-core/src/server/harBackend.ts"() {
"use strict";
import_fs38 = __toESM(require("fs"));
import_path35 = __toESM(require("path"));
init_crypto();
init_fileUtils();
redirectStatus2 = [301, 302, 303, 307, 308];
HarBackend = class {
constructor(harFile, baseDir, zipFile) {
this.id = createGuid();
this._harFile = harFile;
this._baseDir = baseDir;
this._zipFile = zipFile;
}
async lookup(url2, method, headers, postData, isNavigationRequest) {
let entry;
try {
entry = await this._harFindResponse(url2, method, headers, postData);
} catch (e) {
return { action: "error", message: "HAR error: " + e.message };
}
if (!entry)
return { action: "noentry" };
if (entry.request.url !== url2 && isNavigationRequest)
return { action: "redirect", redirectURL: entry.request.url };
const response2 = entry.response;
try {
const buffer = await this._loadContent(response2.content);
return {
action: "fulfill",
status: response2.status,
headers: response2.headers,
body: buffer
};
} catch (e) {
return { action: "error", message: e.message };
}
}
async _loadContent(content) {
const file = content._file;
let buffer;
if (file) {
if (this._zipFile) {
buffer = await this._zipFile.read(file);
} else {
const resolved = import_path35.default.resolve(this._baseDir, file);
if (!isPathInside(this._baseDir, resolved))
throw new Error(`HAR entry _file escapes base directory: ${file}`);
buffer = await import_fs38.default.promises.readFile(resolved);
}
} else {
buffer = Buffer.from(content.text || "", content.encoding === "base64" ? "base64" : "utf-8");
}
return buffer;
}
async _harFindResponse(url2, method, headers, postData) {
const harLog = this._harFile.log;
const visited = /* @__PURE__ */ new Set();
while (true) {
const entries = [];
for (const candidate of harLog.entries) {
if (candidate.request.url !== url2 || candidate.request.method !== method)
continue;
if (method === "POST" && postData && candidate.request.postData) {
const buffer = await this._loadContent(candidate.request.postData);
if (!buffer.equals(postData)) {
const boundary = multipartBoundary(headers);
if (!boundary)
continue;
const candidataBoundary = multipartBoundary(candidate.request.headers);
if (!candidataBoundary)
continue;
if (postData.toString().replaceAll(boundary, "") !== buffer.toString().replaceAll(candidataBoundary, ""))
continue;
}
}
entries.push(candidate);
}
if (!entries.length)
return;
let entry = entries[0];
if (entries.length > 1) {
const list = [];
for (const candidate of entries) {
const matchingHeaders = countMatchingHeaders(candidate.request.headers, headers);
list.push({ candidate, matchingHeaders });
}
list.sort((a, b) => b.matchingHeaders - a.matchingHeaders);
entry = list[0].candidate;
}
if (visited.has(entry))
throw new Error(`Found redirect cycle for ${url2}`);
visited.add(entry);
const locationHeader = entry.response.headers.find((h) => h.name.toLowerCase() === "location");
if (redirectStatus2.includes(entry.response.status) && locationHeader) {
const locationURL = new URL(locationHeader.value, url2);
url2 = locationURL.toString();
if ((entry.response.status === 301 || entry.response.status === 302) && method === "POST" || entry.response.status === 303 && !["GET", "HEAD"].includes(method)) {
method = "GET";
}
continue;
}
return entry;
}
}
dispose() {
this._zipFile?.close();
}
};
}
});
// packages/playwright-core/src/server/localUtils.ts
async function zip(progress2, stackSessions, params2) {
const promise = new ManualPromise();
const zipFile = new yazl2.ZipFile();
zipFile.on("error", (error) => promise.reject(error));
const addFile = (file, name) => {
try {
if (import_fs39.default.statSync(file).isFile())
zipFile.addFile(file, name);
} catch (e) {
}
};
for (const entry of params2.entries)
addFile(entry.value, entry.name);
const stackSession = params2.stacksId ? stackSessions.get(params2.stacksId) : void 0;
if (stackSession?.callStacks.length) {
await progress2.race(stackSession.writer);
const buffer = Buffer.from(JSON.stringify(serializeClientSideCallMetadata(stackSession.callStacks)));
zipFile.addBuffer(buffer, "trace.stacks");
}
if (params2.includeSources) {
const sourceFiles = new Set(params2.additionalSources);
for (const { stack } of stackSession?.callStacks || []) {
if (!stack)
continue;
for (const { file } of stack)
sourceFiles.add(file);
}
for (const sourceFile of sourceFiles)
addFile(sourceFile, "resources/src@" + calculateSha1(sourceFile) + ".txt");
}
if (params2.mode === "write") {
await progress2.race(import_fs39.default.promises.mkdir(import_path36.default.dirname(params2.zipFile), { recursive: true }));
zipFile.end(void 0, () => {
zipFile.outputStream.pipe(import_fs39.default.createWriteStream(params2.zipFile)).on("close", () => promise.resolve()).on("error", (error) => promise.reject(error));
});
await progress2.race(promise);
await deleteStackSession(progress2, stackSessions, params2.stacksId);
return;
}
const tempFile = params2.zipFile + ".tmp";
await progress2.race(import_fs39.default.promises.rename(params2.zipFile, tempFile));
yauzl3.open(tempFile, (err, inZipFile) => {
if (err) {
promise.reject(err);
return;
}
assert(inZipFile);
let pendingEntries = inZipFile.entryCount;
inZipFile.on("entry", (entry) => {
inZipFile.openReadStream(entry, (err2, readStream) => {
if (err2) {
promise.reject(err2);
return;
}
zipFile.addReadStream(readStream, entry.fileName);
if (--pendingEntries === 0) {
zipFile.end(void 0, () => {
zipFile.outputStream.pipe(import_fs39.default.createWriteStream(params2.zipFile)).on("close", () => {
import_fs39.default.promises.unlink(tempFile).then(() => {
promise.resolve();
}).catch((error) => promise.reject(error));
});
});
}
});
});
});
await progress2.race(promise);
await deleteStackSession(progress2, stackSessions, params2.stacksId);
}
async function deleteStackSession(progress2, stackSessions, stacksId) {
const session2 = stacksId ? stackSessions.get(stacksId) : void 0;
if (!session2)
return;
await progress2.race(session2.writer);
stackSessions.delete(stacksId);
if (session2.tmpDir)
await progress2.race(removeFolders([session2.tmpDir]));
}
async function harOpen(progress2, harBackends, params2) {
let harBackend;
if (params2.file.endsWith(".zip")) {
const zipFile = new ZipFile(params2.file);
try {
const entryNames = await progress2.race(zipFile.entries());
const harEntryName = entryNames.find((e) => e.endsWith(".har"));
if (!harEntryName)
return { error: "Specified archive does not have a .har file" };
const har = await progress2.race(zipFile.read(harEntryName));
const harFile = JSON.parse(har.toString());
harBackend = new HarBackend(harFile, null, zipFile);
} catch (error) {
zipFile.close();
throw error;
}
} else {
const harFile = JSON.parse(await progress2.race(import_fs39.default.promises.readFile(params2.file, "utf-8")));
harBackend = new HarBackend(harFile, import_path36.default.dirname(params2.file), null);
}
harBackends.set(harBackend.id, harBackend);
return { harId: harBackend.id };
}
async function harLookup(progress2, harBackends, params2) {
const harBackend = harBackends.get(params2.harId);
if (!harBackend)
return { action: "error", message: `Internal error: har was not opened` };
return await progress2.race(harBackend.lookup(params2.url, params2.method, params2.headers, params2.postData, params2.isNavigationRequest));
}
function harClose(harBackends, params2) {
const harBackend = harBackends.get(params2.harId);
if (harBackend) {
harBackends.delete(harBackend.id);
harBackend.dispose();
}
}
async function harUnzip(progress2, params2) {
const resourcesDir = params2.resourcesDir ?? import_path36.default.dirname(params2.zipFile);
const zipFile = new ZipFile(params2.zipFile);
let resourcesDirCreated = false;
try {
for (const entry of await progress2.race(zipFile.entries())) {
const buffer = await progress2.race(zipFile.read(entry));
if (entry === "har.har") {
await progress2.race(import_fs39.default.promises.writeFile(params2.harFile, buffer));
} else {
if (!resourcesDirCreated) {
await progress2.race(import_fs39.default.promises.mkdir(resourcesDir, { recursive: true }));
resourcesDirCreated = true;
}
const outPath = resolveWithinRoot(resourcesDir, entry);
if (!outPath)
throw new Error(`HAR zip entry '${entry}' escapes output directory`);
await progress2.race(import_fs39.default.promises.writeFile(outPath, buffer));
}
}
await progress2.race(import_fs39.default.promises.unlink(params2.zipFile));
} finally {
zipFile.close();
}
}
async function tracingStarted(progress2, stackSessions, params2) {
let tmpDir = void 0;
if (!params2.tracesDir)
tmpDir = await progress2.race(import_fs39.default.promises.mkdtemp(import_path36.default.join(import_os17.default.tmpdir(), "playwright-tracing-")));
const traceStacksFile = import_path36.default.join(params2.tracesDir || tmpDir, params2.traceName + ".stacks");
stackSessions.set(traceStacksFile, { callStacks: [], file: traceStacksFile, writer: Promise.resolve(), tmpDir, live: params2.live });
return { stacksId: traceStacksFile };
}
async function traceDiscarded(progress2, stackSessions, params2) {
await deleteStackSession(progress2, stackSessions, params2.stacksId);
}
function addStackToTracingNoReply(stackSessions, params2) {
for (const session2 of stackSessions.values()) {
session2.callStacks.push(params2.callData);
if (session2.live) {
session2.writer = session2.writer.then(() => {
const buffer = Buffer.from(JSON.stringify(serializeClientSideCallMetadata(session2.callStacks)));
return import_fs39.default.promises.writeFile(session2.file, buffer);
});
}
}
}
var import_fs39, import_os17, import_path36, yauzl3, yazl2;
var init_localUtils = __esm({
"packages/playwright-core/src/server/localUtils.ts"() {
"use strict";
import_fs39 = __toESM(require("fs"));
import_os17 = __toESM(require("os"));
import_path36 = __toESM(require("path"));
yauzl3 = __toESM(require_yauzl());
init_manualPromise();
init_traceUtils();
init_assert();
init_crypto();
init_zipFile();
init_fileUtils();
init_harBackend();
yazl2 = require("./utilsBundle").yazl;
}
});
// packages/playwright-core/src/server/dispatchers/jsonPipeDispatcher.ts
var JsonPipeDispatcher;
var init_jsonPipeDispatcher = __esm({
"packages/playwright-core/src/server/dispatchers/jsonPipeDispatcher.ts"() {
"use strict";
init_dispatcher();
init_instrumentation();
JsonPipeDispatcher = class extends Dispatcher {
constructor(scope) {
super(scope, new SdkObject(scope._object, "jsonPipe"), "JsonPipe", {});
this._type_JsonPipe = true;
}
async send(params2, progress2) {
this.emit("message", params2.message);
}
async close(params2, progress2) {
this.emit("close");
if (!this._disposed) {
this._dispatchEvent("closed", {});
this._dispose();
}
}
dispatch(message) {
if (!this._disposed)
this._dispatchEvent("message", { message });
}
wasClosed(reason) {
if (!this._disposed) {
this._dispatchEvent("closed", { reason });
this._dispose();
}
}
dispose() {
this._dispose();
}
};
}
});
// packages/playwright-core/src/server/socksInterceptor.ts
function tChannelForSocks(names, arg, path59, context2) {
throw new ValidationError(`${path59}: channels are not expected in SocksSupport`);
}
var import_events14, SocksInterceptor;
var init_socksInterceptor = __esm({
"packages/playwright-core/src/server/socksInterceptor.ts"() {
"use strict";
import_events14 = __toESM(require("events"));
init_socksProxy();
init_debug();
init_validator();
SocksInterceptor = class {
constructor(transport, pattern, redirectPortForTest) {
this._ids = /* @__PURE__ */ new Set();
this._handler = new SocksProxyHandler(pattern, redirectPortForTest);
let lastId = -1;
this._channel = new Proxy(new import_events14.default(), {
get: (obj, prop) => {
if (prop in obj || obj[prop] !== void 0 || typeof prop !== "string")
return obj[prop];
return (params2) => {
try {
const id = --lastId;
this._ids.add(id);
const validator = findValidator("SocksSupport", prop, "Params");
params2 = validator(params2, "", { tChannelImpl: tChannelForSocks, binary: "toBase64", isUnderTest });
transport.send({ id, guid: this._socksSupportObjectGuid, method: prop, params: params2, metadata: { stack: [], apiName: "", internal: true } });
} catch (e) {
}
};
}
});
this._handler.on(SocksProxyHandler.Events.SocksConnected, (payload) => this._channel.socksConnected(payload));
this._handler.on(SocksProxyHandler.Events.SocksData, (payload) => this._channel.socksData(payload));
this._handler.on(SocksProxyHandler.Events.SocksError, (payload) => this._channel.socksError(payload));
this._handler.on(SocksProxyHandler.Events.SocksFailed, (payload) => this._channel.socksFailed(payload));
this._handler.on(SocksProxyHandler.Events.SocksEnd, (payload) => this._channel.socksEnd(payload));
this._channel.on("socksRequested", (payload) => this._handler.socketRequested(payload));
this._channel.on("socksClosed", (payload) => this._handler.socketClosed(payload));
this._channel.on("socksData", (payload) => this._handler.sendSocketData(payload));
}
cleanup() {
this._handler.cleanup();
}
interceptMessage(message) {
if (this._ids.has(message.id)) {
this._ids.delete(message.id);
return true;
}
if (message.method === "__create__" && message.params.type === "SocksSupport") {
this._socksSupportObjectGuid = message.params.guid;
return false;
}
if (this._socksSupportObjectGuid && message.guid === this._socksSupportObjectGuid) {
const validator = findValidator("SocksSupport", message.method, "Event");
const params2 = validator(message.params, "", { tChannelImpl: tChannelForSocks, binary: "fromBase64", isUnderTest });
this._channel.emit(message.method, params2);
return true;
}
return false;
}
};
}
});
// packages/playwright-core/src/server/dispatchers/localUtilsDispatcher.ts
async function urlToWSEndpoint2(progress2, endpointURL) {
if (endpointURL.startsWith("ws"))
return endpointURL;
progress2.log(`<ws preparing> retrieving websocket url from ${endpointURL}`);
const fetchUrl = new URL(endpointURL);
if (!fetchUrl.pathname.endsWith("/"))
fetchUrl.pathname += "/";
fetchUrl.pathname += "json";
const json = await fetchData(progress2, {
url: fetchUrl.toString(),
method: "GET",
headers: { "User-Agent": getUserAgent() }
}, async (params2, response2) => {
return new Error(`Unexpected status ${response2.statusCode} when connecting to ${fetchUrl.toString()}.
This does not look like a Playwright server, try connecting via ws://.`);
});
const wsUrl = new URL(endpointURL);
let wsEndpointPath = JSON.parse(json).wsEndpointPath;
if (wsEndpointPath.startsWith("/"))
wsEndpointPath = wsEndpointPath.substring(1);
if (!wsUrl.pathname.endsWith("/"))
wsUrl.pathname += "/";
wsUrl.pathname += wsEndpointPath;
wsUrl.protocol = wsUrl.protocol === "https:" ? "wss:" : "ws:";
return wsUrl.toString();
}
var import_net7, LocalUtilsDispatcher;
var init_localUtilsDispatcher = __esm({
"packages/playwright-core/src/server/dispatchers/localUtilsDispatcher.ts"() {
"use strict";
import_net7 = __toESM(require("net"));
init_urlMatch();
init_utils2();
init_userAgent();
init_dispatcher();
init_instrumentation();
init_localUtils();
init_deviceDescriptors();
init_jsonPipeDispatcher();
init_pipeTransport2();
init_socksInterceptor();
init_transport();
LocalUtilsDispatcher = class extends Dispatcher {
constructor(scope, playwright2) {
const localUtils = new SdkObject(playwright2, "localUtils", "localUtils");
localUtils.logName = "browser";
const deviceDescriptors2 = Object.entries(deviceDescriptors).map(([name, descriptor]) => ({ name, descriptor }));
super(scope, localUtils, "LocalUtils", {
deviceDescriptors: deviceDescriptors2
});
this._harBackends = /* @__PURE__ */ new Map();
this._stackSessions = /* @__PURE__ */ new Map();
this._type_LocalUtils = true;
}
async zip(params2, progress2) {
return await zip(progress2, this._stackSessions, params2);
}
async harOpen(params2, progress2) {
return await harOpen(progress2, this._harBackends, params2);
}
async harLookup(params2, progress2) {
return await harLookup(progress2, this._harBackends, params2);
}
async harClose(params2, progress2) {
harClose(this._harBackends, params2);
}
async harUnzip(params2, progress2) {
return await harUnzip(progress2, params2);
}
async tracingStarted(params2, progress2) {
return await tracingStarted(progress2, this._stackSessions, params2);
}
async traceDiscarded(params2, progress2) {
return await traceDiscarded(progress2, this._stackSessions, params2);
}
async addStackToTracingNoReply(params2, progress2) {
addStackToTracingNoReply(this._stackSessions, params2);
}
async connect(params2, progress2) {
if (URL.canParse(params2.endpoint))
return await this._connectOverWebSocket(progress2, params2);
return await progress2.race(this._connectOverPipe(params2));
}
async _connectOverWebSocket(progress2, params2) {
const wsHeaders = {
"User-Agent": getUserAgent(),
"x-playwright-proxy": params2.exposeNetwork ?? "",
...params2.headers
};
const wsEndpoint = await urlToWSEndpoint2(progress2, params2.endpoint);
const transport = await WebSocketTransport.connect(progress2, wsEndpoint, { headers: wsHeaders, followRedirects: true, debugLogHeader: "x-playwright-debug-log" });
const socksInterceptor = new SocksInterceptor(transport, params2.exposeNetwork, params2.socksProxyRedirectPortForTest);
const pipe = new JsonPipeDispatcher(this);
transport.onmessage = (json) => {
if (socksInterceptor.interceptMessage(json))
return;
const cb = () => {
try {
pipe.dispatch(json);
} catch (e) {
transport.close();
}
};
if (params2.slowMo)
setTimeout(cb, params2.slowMo);
else
cb();
};
pipe.on("message", (message) => {
transport.send(message);
});
transport.onclose = (reason) => {
socksInterceptor?.cleanup();
pipe.wasClosed(reason);
};
pipe.on("close", () => transport.close());
return { pipe, headers: transport.headers };
}
async _connectOverPipe(params2) {
const socket = await new Promise((resolve, reject) => {
const conn = import_net7.default.connect(params2.endpoint, () => resolve(conn));
conn.on("error", reject);
});
const transport = new PipeTransport2(socket, socket);
const pipe = new JsonPipeDispatcher(this);
transport.onmessage = (json) => {
const cb = () => {
try {
pipe.dispatch(json);
} catch (e) {
transport.close();
}
};
if (params2.slowMo)
setTimeout(cb, params2.slowMo);
else
cb();
};
pipe.on("message", (message) => {
transport.send(message);
});
transport.onclose = (reason) => {
pipe.wasClosed(reason);
};
pipe.on("close", () => socket.end());
return { pipe, headers: [] };
}
async globToRegex(params2, progress2) {
const regex = resolveGlobToRegexPattern(params2.baseURL, params2.glob, params2.webSocketUrl);
return { regex };
}
};
}
});
// packages/playwright-core/src/server/dispatchers/playwrightDispatcher.ts
var PlaywrightDispatcher, SocksSupportDispatcher;
var init_playwrightDispatcher = __esm({
"packages/playwright-core/src/server/dispatchers/playwrightDispatcher.ts"() {
"use strict";
init_socksProxy();
init_eventsHelper();
init_fetch();
init_androidDispatcher();
init_androidDispatcher();
init_browserDispatcher();
init_browserTypeDispatcher();
init_dispatcher();
init_electronDispatcher();
init_localUtilsDispatcher();
init_networkDispatchers();
init_instrumentation();
PlaywrightDispatcher = class extends Dispatcher {
constructor(scope, playwright2, options2 = {}) {
const denyLaunch = options2.denyLaunch ?? false;
const chromium = new BrowserTypeDispatcher(scope, playwright2.chromium, denyLaunch);
const firefox = new BrowserTypeDispatcher(scope, playwright2.firefox, denyLaunch);
const webkit = new BrowserTypeDispatcher(scope, playwright2.webkit, denyLaunch);
const android = new AndroidDispatcher(scope, playwright2.android, denyLaunch);
const initializer = {
chromium,
firefox,
webkit,
android,
electron: new ElectronDispatcher(scope, playwright2.electron, denyLaunch),
utils: playwright2.options.isServer ? void 0 : new LocalUtilsDispatcher(scope, playwright2),
socksSupport: options2.socksProxy ? new SocksSupportDispatcher(scope, playwright2, options2.socksProxy) : void 0
};
let browserDispatcher;
if (options2.preLaunchedBrowser) {
const browserTypeDispatcher = initializer[options2.preLaunchedBrowser.options.name];
browserDispatcher = new BrowserDispatcher(browserTypeDispatcher, options2.preLaunchedBrowser, {
ignoreStopAndKill: true,
isolateContexts: !options2.sharedBrowser
});
initializer.preLaunchedBrowser = browserDispatcher;
}
if (options2.preLaunchedAndroidDevice)
initializer.preConnectedAndroidDevice = new AndroidDeviceDispatcher(android, options2.preLaunchedAndroidDevice);
super(scope, playwright2, "Playwright", initializer);
this._type_Playwright = true;
this._browserDispatcher = browserDispatcher;
this._denyLaunch = denyLaunch;
}
async newRequest(params2, progress2) {
if (this._denyLaunch)
throw new Error(`Creating new API request contexts is not allowed.`);
const request2 = new GlobalAPIRequestContext(this._object, params2);
return { request: APIRequestContextDispatcher.from(this.parentScope(), request2) };
}
async cleanup() {
await this._browserDispatcher?.cleanupContexts();
}
};
SocksSupportDispatcher = class extends Dispatcher {
constructor(scope, parent, socksProxy) {
super(scope, new SdkObject(parent, "socksSupport"), "SocksSupport", {});
this._type_SocksSupport = true;
this._socksProxy = socksProxy;
this._socksListeners = [
eventsHelper.addEventListener(socksProxy, SocksProxy.Events.SocksRequested, (payload) => this._dispatchEvent("socksRequested", payload)),
eventsHelper.addEventListener(socksProxy, SocksProxy.Events.SocksData, (payload) => this._dispatchEvent("socksData", payload)),
eventsHelper.addEventListener(socksProxy, SocksProxy.Events.SocksClosed, (payload) => this._dispatchEvent("socksClosed", payload))
];
}
async socksConnected(params2, progress2) {
this._socksProxy?.socketConnected(params2);
}
async socksFailed(params2, progress2) {
this._socksProxy?.socketFailed(params2);
}
async socksData(params2, progress2) {
this._socksProxy?.sendSocketData(params2);
}
async socksError(params2, progress2) {
this._socksProxy?.sendSocketError(params2);
}
async socksEnd(params2, progress2) {
this._socksProxy?.sendSocketEnd(params2);
}
_onDispose() {
eventsHelper.removeEventListeners(this._socksListeners);
}
};
}
});
// packages/playwright-core/src/server/trace/viewer/traceViewer.ts
function validateTraceUrlOrPath(traceFileOrUrl) {
if (!traceFileOrUrl)
return traceFileOrUrl;
if (traceFileOrUrl.startsWith("http://") || traceFileOrUrl.startsWith("https://"))
return traceFileOrUrl;
let traceFile = traceFileOrUrl;
if (traceFile.endsWith(".json"))
return toFilePathUrl(traceFile);
try {
const stat = import_fs40.default.statSync(traceFile);
if (stat.isDirectory())
traceFile = import_path37.default.join(traceFile, tracesDirMarker);
return toFilePathUrl(traceFile);
} catch {
throw new Error(`Trace file ${traceFileOrUrl} does not exist!`);
}
}
async function startTraceViewerServer(options2) {
const server = new HttpServer();
const allowedRoots = (options2?.allowedFileRoots ?? [process.cwd()]).map((r) => import_path37.default.resolve(r));
const isAllowed = (filePath) => allowedRoots.some((root) => isPathInside(root, filePath));
const serveTraceDataRoute = (request2, response2, relativePath) => {
if (!relativePath.startsWith("/file"))
return false;
const url2 = new URL("http://localhost" + request2.url);
try {
const filePath = import_path37.default.resolve(url2.searchParams.get("path"));
if (!isAllowed(filePath)) {
response2.statusCode = 403;
response2.end();
return true;
}
if (import_fs40.default.existsSync(filePath))
return server.serveFile(request2, response2, filePath);
if (filePath.endsWith(".json")) {
const fullPrefix = filePath.substring(0, filePath.length - ".json".length);
return sendTraceDescriptor(response2, import_path37.default.dirname(fullPrefix), import_path37.default.basename(fullPrefix));
}
if (filePath.endsWith(tracesDirMarker))
return sendTraceDescriptor(response2, import_path37.default.dirname(filePath));
} catch {
}
response2.statusCode = 404;
response2.end();
return true;
};
if (false) {
const traceViewerRoot = import_path37.default.resolve(__dirname, "..", "..", "trace-viewer");
const devServer = await server.createViteDevServer({ root: traceViewerRoot, base: "/trace/" });
server.routePrefix("/trace", (request2, response2) => {
const url2 = new URL("http://localhost" + request2.url);
const relativePath = url2.pathname.slice("/trace".length);
if (relativePath.startsWith("/file"))
return serveTraceDataRoute(request2, response2, relativePath);
if (relativePath === "/sw.bundle.js")
return server.serveFile(request2, response2, import_path37.default.join(libPath("vite", "traceViewer"), "sw.bundle.js"));
devServer.middlewares(request2, response2, HttpServer.notFoundFallback(response2));
return true;
});
} else {
server.routePrefix("/trace", (request2, response2) => {
const url2 = new URL("http://localhost" + request2.url);
const relativePath = url2.pathname.slice("/trace".length);
if (relativePath.startsWith("/file"))
return serveTraceDataRoute(request2, response2, relativePath);
const absolutePath = import_path37.default.join(libPath("vite", "traceViewer"), ...relativePath.split("/"));
return server.serveFile(request2, response2, absolutePath);
});
}
const transport = options2?.transport || (options2?.isServer ? new StdinServer() : void 0);
if (transport)
server.createWebSocket(() => transport);
const { host, port } = options2 || {};
await server.start({ preferredPort: port, host });
return server;
}
async function installRootRedirect(server, traceUrl, options2) {
const params2 = new URLSearchParams();
if (import_path37.default.sep !== import_path37.default.posix.sep)
params2.set("pathSeparator", import_path37.default.sep);
if (traceUrl)
params2.append("trace", traceUrl);
if (server.wsGuid())
params2.append("ws", server.wsGuid());
if (options2?.isServer)
params2.append("isServer", "");
if (isUnderTest())
params2.append("isUnderTest", "true");
for (const arg of options2.args || [])
params2.append("arg", arg);
if (options2.grep)
params2.append("grep", options2.grep);
if (options2.grepInvert)
params2.append("grepInvert", options2.grepInvert);
for (const project of options2.project || [])
params2.append("project", project);
for (const reporter of options2.reporter || [])
params2.append("reporter", reporter);
const urlPath = `./trace/${options2.webApp || "index.html"}?${params2.toString()}`;
server.routePath("/", (_, response2) => {
response2.statusCode = 302;
response2.setHeader("Location", urlPath);
response2.end();
return true;
});
}
async function runTraceViewerApp(traceUrl, browserName, options2) {
traceUrl = validateTraceUrlOrPath(traceUrl);
const server = await startTraceViewerServer({ ...options2, allowedFileRoots: traceFileRoots(traceUrl, options2.allowedFileRoots) });
await installRootRedirect(server, traceUrl, options2);
const page = await openTraceViewerApp(server.urlPrefix("precise"), browserName, options2);
page.on("close", () => gracefullyProcessExitDoNotHang(0));
return page;
}
async function runTraceInBrowser(traceUrl, options2) {
traceUrl = validateTraceUrlOrPath(traceUrl);
const server = await startTraceViewerServer({ ...options2, allowedFileRoots: traceFileRoots(traceUrl, options2.allowedFileRoots) });
await installRootRedirect(server, traceUrl, options2);
await openTraceInBrowser(server.urlPrefix("human-readable"));
}
function traceFileRoots(traceUrl, configured) {
if (configured)
return configured;
if (traceUrl?.startsWith("file://")) {
try {
return [import_path37.default.dirname(import_url.default.fileURLToPath(traceUrl))];
} catch {
}
}
return [process.cwd()];
}
async function openTraceViewerApp(url2, browserName, options2) {
const traceViewerPlaywright = createPlaywright({ sdkLanguage: "javascript", isInternalPlaywright: true });
const traceViewerBrowser = isUnderTest() ? "chromium" : browserName;
const { context: context2, page } = await launchApp(traceViewerPlaywright[traceViewerBrowser], {
sdkLanguage: traceViewerPlaywright.options.sdkLanguage,
windowSize: { width: 1280, height: 800 },
persistentContextOptions: {
...options2?.persistentContextOptions,
args: isUnderTest() ? ["--remote-debugging-port=0"] : void 0,
headless: !!options2?.headless,
colorScheme: isUnderTest() ? "light" : void 0
}
});
const controller = new ProgressController();
await controller.run(async (progress2) => {
await context2._browser._defaultContext.loadDefaultContextAsIs(progress2);
if (process.env.PWTEST_PRINT_WS_ENDPOINT) {
process.stderr.write("DevTools listening on: " + context2._browser.options.wsEndpoint + "\n");
}
if (!isUnderTest())
await progress2.race(syncLocalStorageWithSettings(page, "traceviewer"));
if (isUnderTest())
page.on("close", () => context2.close(nullProgress, { reason: "Trace viewer closed" }).catch(() => {
}));
await page.mainFrame().goto(progress2, url2);
});
return page;
}
async function openTraceInBrowser(url2) {
console.log("\nListening on " + url2);
if (!isUnderTest() && !isCodingAgent())
await open4(url2.replace("0.0.0.0", "localhost")).catch(() => {
});
}
function sendTraceDescriptor(response2, traceDir2, tracePrefix) {
response2.statusCode = 200;
response2.setHeader("Content-Type", "application/json");
response2.end(JSON.stringify(traceDescriptor(traceDir2, tracePrefix)));
return true;
}
function traceDescriptor(traceDir2, tracePrefix) {
const result2 = {
entries: []
};
for (const name of import_fs40.default.readdirSync(traceDir2)) {
if (!tracePrefix || name.startsWith(tracePrefix))
result2.entries.push({ name, path: toFilePathUrl(import_path37.default.join(traceDir2, name)) });
}
const resourcesDir = import_path37.default.join(traceDir2, "resources");
if (import_fs40.default.existsSync(resourcesDir)) {
for (const name of import_fs40.default.readdirSync(resourcesDir))
result2.entries.push({ name: "resources/" + name, path: toFilePathUrl(import_path37.default.join(resourcesDir, name)) });
}
return result2;
}
function toFilePathUrl(filePath) {
return `file?path=${encodeURIComponent(filePath)}`;
}
var import_fs40, import_path37, import_url, open4, tracesDirMarker, StdinServer;
var init_traceViewer = __esm({
"packages/playwright-core/src/server/trace/viewer/traceViewer.ts"() {
"use strict";
import_fs40 = __toESM(require("fs"));
import_path37 = __toESM(require("path"));
import_url = __toESM(require("url"));
init_httpServer();
init_processLauncher();
init_debug();
init_env();
init_fileUtils();
init_package();
init_launchApp();
init_launchApp();
init_playwright();
init_progress();
init_progress();
open4 = require("./utilsBundle").open;
tracesDirMarker = "traces.dir";
StdinServer = class {
constructor() {
process.stdin.on("data", (data) => {
const url2 = validateTraceUrlOrPath(data.toString().trim());
if (!url2 || url2 === this._traceUrl)
return;
if (url2.endsWith(".json"))
this._pollLoadTrace(url2);
else
this._loadTrace(url2);
});
process.stdin.on("close", () => gracefullyProcessExitDoNotHang(0));
}
onconnect() {
}
async dispatch(method, params2) {
if (method === "initialize") {
if (this._traceUrl)
this._loadTrace(this._traceUrl);
}
}
onclose() {
}
_loadTrace(traceUrl) {
this._traceUrl = traceUrl;
clearTimeout(this._pollTimer);
this.sendEvent?.("loadTraceRequested", { traceUrl });
}
_pollLoadTrace(url2) {
this._loadTrace(url2);
this._pollTimer = setTimeout(() => {
this._pollLoadTrace(url2);
}, 500);
}
};
}
});
// packages/playwright-core/src/server/index.ts
var server_exports = {};
__export(server_exports, {
Browser: () => Browser,
BrowserContext: () => BrowserContext,
DispatcherConnection: () => DispatcherConnection,
Page: () => Page,
PlaywrightDispatcher: () => PlaywrightDispatcher,
Request: () => Request,
RequestDispatcher: () => RequestDispatcher,
Response: () => Response2,
ResponseDispatcher: () => ResponseDispatcher,
RootDispatcher: () => RootDispatcher,
WebSocketTransport: () => WebSocketTransport,
createPlaywright: () => createPlaywright,
deviceDescriptors: () => deviceDescriptors,
findRepeatedSubsequencesForTest: () => findRepeatedSubsequencesForTest,
installRootRedirect: () => installRootRedirect,
nullProgress: () => nullProgress,
openTraceInBrowser: () => openTraceInBrowser,
openTraceViewerApp: () => openTraceViewerApp,
runTraceViewerApp: () => runTraceViewerApp,
setMaxDispatchersForTest: () => setMaxDispatchersForTest,
startTraceViewerServer: () => startTraceViewerServer
});
var init_server = __esm({
"packages/playwright-core/src/server/index.ts"() {
"use strict";
init_browser();
init_browserContext();
init_callLog();
init_deviceDescriptors();
init_dispatcher();
init_networkDispatchers();
init_playwrightDispatcher();
init_network2();
init_page();
init_playwright();
init_progress();
init_transport();
init_traceViewer();
}
});
// packages/playwright-core/src/server/dispatchers/debugControllerDispatcher.ts
var DebugControllerDispatcher;
var init_debugControllerDispatcher = __esm({
"packages/playwright-core/src/server/dispatchers/debugControllerDispatcher.ts"() {
"use strict";
init_eventsHelper();
init_debugController();
init_dispatcher();
DebugControllerDispatcher = class extends Dispatcher {
constructor(connection, debugController) {
super(connection, debugController, "DebugController", {});
this._type_DebugController = true;
this._listeners = [
eventsHelper.addEventListener(this._object, DebugController.Events.StateChanged, (params2) => {
this._dispatchEvent("stateChanged", params2);
}),
eventsHelper.addEventListener(this._object, DebugController.Events.InspectRequested, ({ selector, locator: locator2, ariaSnapshot }) => {
this._dispatchEvent("inspectRequested", { selector, locator: locator2, ariaSnapshot });
}),
eventsHelper.addEventListener(this._object, DebugController.Events.SourceChanged, ({ text: text2, header, footer, actions }) => {
this._dispatchEvent("sourceChanged", { text: text2, header, footer, actions });
}),
eventsHelper.addEventListener(this._object, DebugController.Events.Paused, ({ paused }) => {
this._dispatchEvent("paused", { paused });
}),
eventsHelper.addEventListener(this._object, DebugController.Events.SetModeRequested, ({ mode }) => {
this._dispatchEvent("setModeRequested", { mode });
})
];
}
async initialize(params2, progress2) {
this._object.initialize(progress2, params2.codegenId, params2.sdkLanguage);
}
async setReportStateChanged(params2, progress2) {
this._object.setReportStateChanged(progress2, params2.enabled);
}
async setRecorderMode(params2, progress2) {
await this._object.setRecorderMode(progress2, params2);
}
async highlight(params2, progress2) {
await this._object.highlight(progress2, params2);
}
async hideHighlight(params2, progress2) {
await this._object.hideHighlight(progress2);
}
async resume(params2, progress2) {
await this._object.resume(progress2);
}
async kill(params2, progress2) {
this._object.kill(progress2);
}
_onDispose() {
eventsHelper.removeEventListeners(this._listeners);
this._object.dispose();
}
};
}
});
// packages/playwright-core/src/remote/playwrightConnection.ts
var PlaywrightConnection;
var init_playwrightConnection = __esm({
"packages/playwright-core/src/remote/playwrightConnection.ts"() {
"use strict";
init_profiler();
init_debugLogger();
init_time();
init_server();
init_android();
init_browser();
init_debugControllerDispatcher();
PlaywrightConnection = class {
constructor(semaphore, transport, controller, playwright2, initialize, id) {
this._cleanups = [];
this._transport = transport;
this._semaphore = semaphore;
this._id = id;
this._profileName = (/* @__PURE__ */ new Date()).toISOString();
const lock2 = this._semaphore.acquire();
this._dispatcherConnection = new DispatcherConnection();
this._dispatcherConnection.onmessage = async (message) => {
await lock2;
if (!transport.isClosed()) {
const messageString = JSON.stringify(message);
if (debugLogger.isEnabled("server:channel"))
debugLogger.log("server:channel", `[${this._id}] ${monotonicTime() * 1e3} SEND \u25BA ${messageString}`);
if (debugLogger.isEnabled("server:metadata"))
this.logServerMetadata(message, messageString, "SEND");
transport.send(messageString);
}
};
transport.on("message", async (message) => {
await lock2;
const messageString = Buffer.from(message).toString();
const jsonMessage = JSON.parse(messageString);
if (debugLogger.isEnabled("server:channel"))
debugLogger.log("server:channel", `[${this._id}] ${monotonicTime() * 1e3} \u25C0 RECV ${messageString}`);
if (debugLogger.isEnabled("server:metadata"))
this.logServerMetadata(jsonMessage, messageString, "RECV");
this._dispatcherConnection.dispatch(jsonMessage);
});
transport.on("close", () => this._onDisconnect());
transport.on("error", (error) => this._onDisconnect(error));
if (controller) {
debugLogger.log("server", `[${this._id}] engaged reuse controller mode`);
this._root = new DebugControllerDispatcher(this._dispatcherConnection, playwright2.debugController);
return;
}
this._root = new RootDispatcher(this._dispatcherConnection, async (scope, params2) => {
await startProfiling();
const options2 = await initialize();
if (options2.preLaunchedBrowser) {
const browser = options2.preLaunchedBrowser;
browser.options.sdkLanguage = params2.sdkLanguage;
browser.on(Browser.Events.Disconnected, () => {
this.close({ code: 1001, reason: "Browser closed" });
});
}
if (options2.preLaunchedAndroidDevice) {
const androidDevice = options2.preLaunchedAndroidDevice;
androidDevice.on(AndroidDevice.Events.Close, () => {
this.close({ code: 1001, reason: "Android device disconnected" });
});
}
if (options2.dispose)
this._cleanups.push(options2.dispose);
const dispatcher = new PlaywrightDispatcher(scope, playwright2, options2);
this._cleanups.push(() => dispatcher.cleanup());
return dispatcher;
});
}
_onDisconnect(error) {
if (!this._onDisconnectPromise)
this._onDisconnectPromise = this._doDisconnect(error);
return this._onDisconnectPromise;
}
async _doDisconnect(error) {
debugLogger.log("server", `[${this._id}] disconnected. error: ${error}`);
await this._root.stopPendingOperations(new Error("Disconnected")).catch(() => {
});
this._root._dispose();
debugLogger.log("server", `[${this._id}] starting cleanup`);
for (const cleanup of this._cleanups)
await cleanup().catch(() => {
});
await stopProfiling(this._profileName);
this._semaphore.release();
debugLogger.log("server", `[${this._id}] finished cleanup`);
}
logServerMetadata(message, messageString, direction) {
const serverLogMetadata = {
wallTime: Date.now(),
id: message.id,
guid: message.guid,
method: message.method,
payloadSizeInBytes: Buffer.byteLength(messageString, "utf-8")
};
debugLogger.log("server:metadata", (direction === "SEND" ? "SEND \u25BA " : "\u25C0 RECV ") + JSON.stringify(serverLogMetadata));
}
async close(reason) {
if (!this._onDisconnectPromise) {
debugLogger.log("server", `[${this._id}] force closing connection: ${reason?.reason || ""} (${reason?.code || 0})`);
try {
this._transport.close(reason);
} catch (e) {
}
}
await this._onDisconnect();
}
};
}
});
// packages/playwright-core/src/remote/playwrightServer.ts
var playwrightServer_exports = {};
__export(playwrightServer_exports, {
PlaywrightServer: () => PlaywrightServer
});
function userAgentVersionMatchesErrorMessage(userAgent) {
const match = userAgent.match(/^Playwright\/(\d+\.\d+\.\d+)/);
if (!match) {
return;
}
const received = match[1].split(".").slice(0, 2).join(".");
const expected = getPlaywrightVersion(true);
if (received !== expected) {
return wrapInASCIIBox([
`Playwright version mismatch:`,
` - server version: v${expected}`,
` - client version: v${received}`,
``,
`If you are using VSCode extension, restart VSCode.`,
``,
`If you are connecting to a remote service,`,
`keep your local Playwright version in sync`,
`with the remote service version.`,
``,
`<3 Playwright Team`
].join("\n"), 1);
}
}
function launchOptionsHash(options2) {
const copy = { ...options2 };
for (const k of Object.keys(copy)) {
const key = k;
if (copy[key] === defaultLaunchOptions[key])
delete copy[key];
}
for (const key of optionsThatAllowBrowserReuse)
delete copy[key];
return JSON.stringify(copy);
}
function filterLaunchOptions(options2, allowUnsafe) {
return {
channel: options2.channel,
args: allowUnsafe ? options2.args : void 0,
ignoreAllDefaultArgs: allowUnsafe ? options2.ignoreAllDefaultArgs : void 0,
ignoreDefaultArgs: allowUnsafe ? options2.ignoreDefaultArgs : void 0,
timeout: options2.timeout,
headless: options2.headless,
proxy: options2.proxy,
chromiumSandbox: allowUnsafe ? options2.chromiumSandbox : void 0,
firefoxUserPrefs: allowUnsafe ? options2.firefoxUserPrefs : void 0,
slowMo: options2.slowMo,
executablePath: isUnderTest() || allowUnsafe ? options2.executablePath : void 0,
downloadsPath: allowUnsafe ? options2.downloadsPath : void 0,
artifactsDir: isUnderTest() || allowUnsafe ? options2.artifactsDir : void 0
};
}
var PlaywrightServer, defaultLaunchOptions, optionsThatAllowBrowserReuse;
var init_playwrightServer = __esm({
"packages/playwright-core/src/remote/playwrightServer.ts"() {
"use strict";
init_semaphore();
init_time();
init_wsServer();
init_ascii();
init_socksProxy();
init_debugLogger();
init_debug();
init_userAgent();
init_playwrightConnection();
init_serverTransport();
init_playwright();
init_browser();
init_progress();
PlaywrightServer = class {
constructor(options2) {
this._dontReuseBrowsers = /* @__PURE__ */ new Set();
this._options = options2;
if (options2.preLaunchedBrowser) {
this._playwright = options2.preLaunchedBrowser.attribution.playwright;
this._dontReuse(options2.preLaunchedBrowser);
}
if (options2.preLaunchedAndroidDevice)
this._playwright = options2.preLaunchedAndroidDevice._android.attribution.playwright;
this._playwright ??= createPlaywright({ sdkLanguage: "javascript", isServer: true });
const browserSemaphore = new Semaphore(this._options.maxConnections);
const controllerSemaphore = new Semaphore(1);
const reuseBrowserSemaphore = new Semaphore(1);
this._wsServer = new WSServer({
onRequest: (request2, response2) => {
if (request2.method === "GET" && request2.url === "/json") {
response2.setHeader("Content-Type", "application/json");
response2.end(JSON.stringify({
wsEndpointPath: this._options.path
}));
return;
}
response2.end("Running");
},
onUpgrade: (request2, socket) => {
const uaError = userAgentVersionMatchesErrorMessage(request2.headers["user-agent"] || "");
if (uaError)
return { error: `HTTP/${request2.httpVersion} 428 Precondition Required\r
\r
${uaError}` };
},
onHeaders: (headers) => {
if (process.env.PWTEST_SERVER_WS_HEADERS)
headers.push(process.env.PWTEST_SERVER_WS_HEADERS);
},
onConnection: (request2, url2, ws3, id) => {
const browserHeader = request2.headers["x-playwright-browser"];
const browserName = url2.searchParams.get("browser") || (Array.isArray(browserHeader) ? browserHeader[0] : browserHeader) || null;
const proxyHeader = request2.headers["x-playwright-proxy"];
const proxyValue = url2.searchParams.get("proxy") || (Array.isArray(proxyHeader) ? proxyHeader[0] : proxyHeader);
const launchOptionsHeader = request2.headers["x-playwright-launch-options"] || "";
const launchOptionsHeaderValue = Array.isArray(launchOptionsHeader) ? launchOptionsHeader[0] : launchOptionsHeader;
const launchOptionsParam = url2.searchParams.get("launch-options");
let launchOptions = { timeout: DEFAULT_PLAYWRIGHT_LAUNCH_TIMEOUT };
try {
launchOptions = JSON.parse(launchOptionsParam || launchOptionsHeaderValue);
if (!launchOptions.timeout)
launchOptions.timeout = DEFAULT_PLAYWRIGHT_LAUNCH_TIMEOUT;
} catch (e) {
}
const isExtension = this._options.mode === "extension";
launchOptions = filterLaunchOptions(launchOptions, isExtension || !!this._options.unsafe);
if (this._options.artifactsDir)
launchOptions.artifactsDir = this._options.artifactsDir;
if (isExtension) {
const connectFilter = url2.searchParams.get("connect");
if (connectFilter) {
if (connectFilter !== "first")
throw new Error(`Unknown connect filter: ${connectFilter}`);
return new PlaywrightConnection(
browserSemaphore,
new WebSocketServerTransport(ws3),
false,
this._playwright,
() => this._initConnectMode(id, connectFilter, browserName, launchOptions),
id
);
}
if (url2.searchParams.has("debug-controller")) {
return new PlaywrightConnection(
controllerSemaphore,
new WebSocketServerTransport(ws3),
true,
this._playwright,
async () => {
throw new Error("shouldnt be used");
},
id
);
}
return new PlaywrightConnection(
reuseBrowserSemaphore,
new WebSocketServerTransport(ws3),
false,
this._playwright,
() => this._initReuseBrowsersMode(browserName, launchOptions, id),
id
);
}
if (this._options.mode === "launchServer" || this._options.mode === "launchServerShared") {
if (this._options.preLaunchedBrowser) {
return new PlaywrightConnection(
browserSemaphore,
new WebSocketServerTransport(ws3),
false,
this._playwright,
() => this._initPreLaunchedBrowserMode(id),
id
);
}
return new PlaywrightConnection(
browserSemaphore,
new WebSocketServerTransport(ws3),
false,
this._playwright,
() => this._initPreLaunchedAndroidMode(id),
id
);
}
return new PlaywrightConnection(
browserSemaphore,
new WebSocketServerTransport(ws3),
false,
this._playwright,
() => this._initLaunchBrowserMode(browserName, proxyValue, launchOptions, id),
id
);
}
});
}
async _initReuseBrowsersMode(browserName, launchOptions, id) {
debugLogger.log("server", `[${id}] engaged reuse browsers mode for ${browserName}`);
const requestedOptions = launchOptionsHash(launchOptions);
let browser = this._playwright.allBrowsers().find((b) => {
if (b.options.name !== browserName)
return false;
if (this._dontReuseBrowsers.has(b))
return false;
const existingOptions = launchOptionsHash({ ...b.options.originalLaunchOptions, timeout: DEFAULT_PLAYWRIGHT_LAUNCH_TIMEOUT });
return existingOptions === requestedOptions;
});
for (const b of this._playwright.allBrowsers()) {
if (b === browser)
continue;
if (this._dontReuseBrowsers.has(b))
continue;
if (b.options.name === browserName && b.options.channel === launchOptions.channel)
await b.close(nullProgress, { reason: "Connection terminated" });
}
if (!browser) {
const browserType = this._playwright[browserName || "chromium"];
const controller = new ProgressController();
browser = await controller.run((progress2) => browserType.launch(progress2, {
...launchOptions,
headless: !!process.env.PW_DEBUG_CONTROLLER_HEADLESS
}), launchOptions.timeout);
}
return {
preLaunchedBrowser: browser,
denyLaunch: true,
dispose: async () => {
for (const context2 of browser.contexts()) {
if (!context2.pages().length)
await context2.close(nullProgress, { reason: "Connection terminated" });
}
}
};
}
async _initConnectMode(id, filter, browserName, launchOptions) {
browserName ??= "chromium";
debugLogger.log("server", `[${id}] engaged connect mode`);
let browser = this._playwright.allBrowsers().find((b) => b.options.name === browserName);
if (!browser) {
const browserType = this._playwright[browserName];
const controller = new ProgressController();
browser = await controller.run((progress2) => browserType.launch(progress2, launchOptions), launchOptions.timeout);
this._dontReuse(browser);
}
return {
preLaunchedBrowser: browser,
denyLaunch: true,
sharedBrowser: true
};
}
async _initPreLaunchedBrowserMode(id) {
debugLogger.log("server", `[${id}] engaged pre-launched (browser) mode`);
const browser = this._options.preLaunchedBrowser;
for (const b of this._playwright.allBrowsers()) {
if (b !== browser)
await b.close(nullProgress, { reason: "Connection terminated" });
}
return {
preLaunchedBrowser: browser,
socksProxy: this._options.preLaunchedSocksProxy,
sharedBrowser: this._options.mode === "launchServerShared",
denyLaunch: true
};
}
async _initPreLaunchedAndroidMode(id) {
debugLogger.log("server", `[${id}] engaged pre-launched (Android) mode`);
const androidDevice = this._options.preLaunchedAndroidDevice;
return {
preLaunchedAndroidDevice: androidDevice,
denyLaunch: true
};
}
async _initLaunchBrowserMode(browserName, proxyValue, launchOptions, id) {
debugLogger.log("server", `[${id}] engaged launch mode for "${browserName}"`);
let socksProxy;
if (proxyValue) {
socksProxy = new SocksProxy();
socksProxy.setPattern(proxyValue);
launchOptions.socksProxyPort = await socksProxy.listen(0);
debugLogger.log("server", `[${id}] started socks proxy on port ${launchOptions.socksProxyPort}`);
} else {
launchOptions.socksProxyPort = void 0;
}
const browserType = this._playwright[browserName];
const controller = new ProgressController();
const browser = await controller.run((progress2) => browserType.launch(progress2, launchOptions), launchOptions.timeout);
this._dontReuseBrowsers.add(browser);
return {
preLaunchedBrowser: browser,
socksProxy,
denyLaunch: true,
dispose: async () => {
await browser.close(nullProgress, { reason: "Connection terminated" });
socksProxy?.close();
}
};
}
_dontReuse(browser) {
this._dontReuseBrowsers.add(browser);
browser.on(Browser.Events.Disconnected, () => {
this._dontReuseBrowsers.delete(browser);
});
}
async listen(port = 0, hostname) {
return this._wsServer.listen(port, hostname, this._options.path);
}
async close() {
await this._wsServer.close();
for (const browser of this._playwright.allBrowsers())
await browser.close(nullProgress, { reason: "Server closed" });
}
};
defaultLaunchOptions = {
ignoreAllDefaultArgs: false,
handleSIGINT: false,
handleSIGTERM: false,
handleSIGHUP: false,
headless: true
};
optionsThatAllowBrowserReuse = [
"headless",
"timeout",
"tracesDir",
"artifactsDir"
];
}
});
// packages/playwright-core/src/androidServerImpl.ts
var import_events15, AndroidServerLauncherImpl;
var init_androidServerImpl = __esm({
"packages/playwright-core/src/androidServerImpl.ts"() {
"use strict";
import_events15 = __toESM(require("events"));
init_crypto();
init_playwrightServer();
init_playwright();
init_progress();
AndroidServerLauncherImpl = class {
async launchServer(options2 = {}) {
const playwright2 = createPlaywright({ sdkLanguage: "javascript", isServer: true });
const controller = new ProgressController();
let devices = await controller.run((progress2) => playwright2.android.devices(progress2, {
host: options2.adbHost,
port: options2.adbPort,
omitDriverInstall: options2.omitDriverInstall
}));
if (devices.length === 0)
throw new Error("No devices found");
if (options2.deviceSerialNumber) {
devices = devices.filter((d) => d.serial === options2.deviceSerialNumber);
if (devices.length === 0)
throw new Error(`No device with serial number '${options2.deviceSerialNumber}' was found`);
}
if (devices.length > 1)
throw new Error(`More than one device found. Please specify deviceSerialNumber`);
const device = devices[0];
const path59 = options2.wsPath ? options2.wsPath.startsWith("/") ? options2.wsPath : `/${options2.wsPath}` : `/${createGuid()}`;
const server = new PlaywrightServer({ mode: "launchServer", path: path59, maxConnections: 1, preLaunchedAndroidDevice: device });
const wsEndpoint = await server.listen(options2.port, options2.host);
const browserServer = new import_events15.default();
browserServer.wsEndpoint = () => wsEndpoint;
browserServer.close = () => device.close(nullProgress);
browserServer.kill = () => device.close(nullProgress);
device.on("close", () => {
server.close();
browserServer.emit("close");
});
return browserServer;
}
};
}
});
// packages/playwright-core/src/browserServerImpl.ts
function toProtocolLogger(logger) {
return logger ? (direction, message) => {
if (logger.isEnabled("protocol", "verbose"))
logger.log("protocol", "verbose", (direction === "send" ? "SEND \u25BA " : "\u25C0 RECV ") + JSON.stringify(message), [], {});
} : void 0;
}
function envObjectToArray(env) {
const result2 = [];
for (const name in env) {
if (!Object.is(env[name], void 0))
result2.push({ name, value: String(env[name]) });
}
return result2;
}
var import_events16, BrowserServerLauncherImpl;
var init_browserServerImpl = __esm({
"packages/playwright-core/src/browserServerImpl.ts"() {
"use strict";
import_events16 = __toESM(require("events"));
init_crypto();
init_debug();
init_stackTrace();
init_time();
init_playwrightServer();
init_helper();
init_playwright();
init_validatorPrimitives();
init_progress();
BrowserServerLauncherImpl = class {
constructor(browserName) {
this._browserName = browserName;
}
async launchServer(options2 = {}) {
const playwright2 = createPlaywright({ sdkLanguage: "javascript", isServer: true });
const metadata = { id: "", startTime: 0, endTime: 0, type: "Internal", method: "", params: {}, log: [], internal: true };
const validatorContext = {
tChannelImpl: (names, arg, path60) => {
throw new ValidationError(`${path60}: channels are not expected in launchServer`);
},
binary: "buffer",
isUnderTest
};
let launchOptions = {
...options2,
ignoreDefaultArgs: Array.isArray(options2.ignoreDefaultArgs) ? options2.ignoreDefaultArgs : void 0,
ignoreAllDefaultArgs: !!options2.ignoreDefaultArgs && !Array.isArray(options2.ignoreDefaultArgs),
env: options2.env ? envObjectToArray(options2.env) : void 0,
timeout: options2.timeout ?? DEFAULT_PLAYWRIGHT_LAUNCH_TIMEOUT
};
let browser;
try {
const controller = new ProgressController(metadata);
browser = await controller.run(async (progress2) => {
if (options2._userDataDir !== void 0) {
const validator = scheme["BrowserTypeLaunchPersistentContextParams"];
launchOptions = validator({ ...launchOptions, userDataDir: options2._userDataDir }, "", validatorContext);
const context2 = await playwright2[this._browserName].launchPersistentContext(progress2, options2._userDataDir, launchOptions);
return context2._browser;
} else {
const validator = scheme["BrowserTypeLaunchParams"];
launchOptions = validator(launchOptions, "", validatorContext);
return await playwright2[this._browserName].launch(progress2, launchOptions, toProtocolLogger(options2.logger));
}
});
} catch (e) {
const log2 = helper.formatBrowserLogs(metadata.log);
rewriteErrorMessage(e, `${e.message} Failed to launch browser.${log2}`);
throw e;
}
const path59 = options2.wsPath ? options2.wsPath.startsWith("/") ? options2.wsPath : `/${options2.wsPath}` : `/${createGuid()}`;
const server = new PlaywrightServer({ mode: options2._sharedBrowser ? "launchServerShared" : "launchServer", path: path59, maxConnections: Infinity, preLaunchedBrowser: browser });
const wsEndpoint = await server.listen(options2.port, options2.host);
const browserServer = new import_events16.default();
browserServer.process = () => browser.options.browserProcess.process;
browserServer.wsEndpoint = () => wsEndpoint;
browserServer.close = () => browser.options.browserProcess.close();
browserServer[Symbol.asyncDispose] = browserServer.close;
browserServer.kill = () => browser.options.browserProcess.kill();
browserServer._disconnectForTest = () => server.close();
browserServer._userDataDirForTest = browser._userDataDirForTest;
browser.options.browserProcess.onclose = (exitCode, signal) => {
server.close();
browserServer.emit("close", exitCode, signal);
};
return browserServer;
}
};
}
});
// packages/playwright-core/src/client/clientStackTrace.ts
function captureLibraryStackTrace(platform) {
const stack = captureRawStack();
let parsedFrames = stack.map((line) => {
const frame = parseStackFrame(line, platform.pathSeparator, platform.showInternalStackFrames());
if (!frame || !frame.file)
return null;
const isPlaywrightLibrary = !!platform.coreDir && frame.file.startsWith(platform.coreDir);
const parsed = {
frame,
frameText: line,
isPlaywrightLibrary
};
return parsed;
}).filter(Boolean);
let apiName = "";
for (let i = 0; i < parsedFrames.length - 1; i++) {
const parsedFrame = parsedFrames[i];
if (parsedFrame.isPlaywrightLibrary && !parsedFrames[i + 1].isPlaywrightLibrary) {
apiName = apiName || normalizeAPIName(parsedFrame.frame.function);
break;
}
}
function normalizeAPIName(name) {
if (!name)
return "";
const match = name.match(/(API|JS|CDP|[A-Z])([^\d]+)\d?\.(.*)/);
if (!match)
return name;
return match[1].toLowerCase() + match[2] + "." + match[3];
}
const filterPrefixes = platform.boxedStackPrefixes();
parsedFrames = parsedFrames.filter((f) => {
if (filterPrefixes.some((prefix) => f.frame.file.startsWith(prefix)))
return false;
return true;
});
return {
frames: parsedFrames.map((p) => p.frame),
apiName
};
}
var init_clientStackTrace = __esm({
"packages/playwright-core/src/client/clientStackTrace.ts"() {
"use strict";
init_stackTrace();
}
});
// packages/playwright-core/src/client/channelOwner.ts
function logApiCall(platform, logger, message) {
if (logger && logger.isEnabled("api", "info"))
logger.log("api", "info", message, [], { color: "cyan" });
platform.log("api", message);
}
function tChannelImplToWire(names, arg, path59, context2) {
if (arg._object instanceof ChannelOwner && (names === "*" || names.includes(arg._object._type)))
return { guid: arg._object._guid };
throw new ValidationError(`${path59}: expected channel ${names.toString()}`);
}
var ChannelOwner;
var init_channelOwner = __esm({
"packages/playwright-core/src/client/channelOwner.ts"() {
"use strict";
init_protocolMetainfo();
init_stackTrace();
init_eventEmitter();
init_validator();
init_clientStackTrace();
ChannelOwner = class _ChannelOwner extends EventEmitter3 {
constructor(parent, type3, guid, initializer) {
const connection = parent instanceof _ChannelOwner ? parent._connection : parent;
super(connection._platform);
this._objects = /* @__PURE__ */ new Map();
this._eventToSubscriptionMapping = /* @__PURE__ */ new Map();
this._wasCollected = false;
this.setMaxListeners(0);
this._connection = connection;
this._type = type3;
this._guid = guid;
this._parent = parent instanceof _ChannelOwner ? parent : void 0;
this._instrumentation = this._connection._instrumentation;
this._connection._objects.set(guid, this);
if (this._parent) {
this._parent._objects.set(guid, this);
this._logger = this._parent._logger;
}
this._channel = this._createChannel(new EventEmitter3(connection._platform));
this._initializer = initializer;
}
_setEventToSubscriptionMapping(mapping) {
this._eventToSubscriptionMapping = mapping;
}
_updateSubscription(event, enabled) {
const protocolEvent = this._eventToSubscriptionMapping.get(String(event));
if (protocolEvent)
this._channel.updateSubscription({ event: protocolEvent, enabled }).catch(() => {
});
}
on(event, listener) {
if (!this.listenerCount(event))
this._updateSubscription(event, true);
super.on(event, listener);
return this;
}
addListener(event, listener) {
if (!this.listenerCount(event))
this._updateSubscription(event, true);
super.addListener(event, listener);
return this;
}
prependListener(event, listener) {
if (!this.listenerCount(event))
this._updateSubscription(event, true);
super.prependListener(event, listener);
return this;
}
off(event, listener) {
super.off(event, listener);
if (!this.listenerCount(event))
this._updateSubscription(event, false);
return this;
}
removeListener(event, listener) {
super.removeListener(event, listener);
if (!this.listenerCount(event))
this._updateSubscription(event, false);
return this;
}
_adopt(child) {
child._parent._objects.delete(child._guid);
this._objects.set(child._guid, child);
child._parent = this;
}
_dispose(reason) {
if (this._parent)
this._parent._objects.delete(this._guid);
this._connection._objects.delete(this._guid);
this._wasCollected = reason === "gc";
for (const object of [...this._objects.values()])
object._dispose(reason);
this._objects.clear();
}
_debugScopeState() {
return {
_guid: this._guid,
objects: Array.from(this._objects.values()).map((o) => o._debugScopeState())
};
}
_validatorToWireContext() {
return {
tChannelImpl: tChannelImplToWire,
binary: this._connection.rawBuffers() ? "buffer" : "toBase64",
isUnderTest: () => this._platform.isUnderTest()
};
}
_createChannel(base) {
const channel = new Proxy(base, {
get: (obj, prop) => {
if (typeof prop === "string") {
const validator = maybeFindValidator(this._type, prop, "Params");
const { internal } = getMetainfo({ type: this._type, method: prop }) || {};
if (validator) {
return async (params2) => {
return await this._wrapApiCall(async (apiZone) => {
const validatedParams = validator(params2, "", this._validatorToWireContext());
if (!apiZone.internal && !apiZone.reported) {
apiZone.reported = true;
this._instrumentation.onApiCallBegin(apiZone, { type: this._type, method: prop, params: params2 });
logApiCall(this._platform, this._logger, `=> ${apiZone.apiName} started`);
return await this._connection.sendMessageToServer(this, prop, validatedParams, apiZone);
}
return await this._connection.sendMessageToServer(this, prop, validatedParams, { internal: true });
}, { internal });
};
}
}
return obj[prop];
}
});
channel._object = this;
return channel;
}
async _wrapApiCall(func, options2) {
const logger = this._logger;
const existingApiZone = this._platform.zones.current().data();
if (existingApiZone)
return await func(existingApiZone);
const stackTrace = captureLibraryStackTrace(this._platform);
const apiZone = { title: options2?.title, apiName: stackTrace.apiName, frames: stackTrace.frames, internal: options2?.internal ?? false, reported: false, userData: void 0, stepId: void 0 };
try {
const result2 = await this._platform.zones.current().push(apiZone).run(async () => await func(apiZone));
if (!options2?.internal) {
logApiCall(this._platform, logger, `<= ${apiZone.apiName} succeeded`);
this._instrumentation.onApiCallEnd(apiZone);
}
return result2;
} catch (e) {
const innerError = (this._platform.showInternalStackFrames() || this._platform.isUnderTest()) && e.stack ? "\n<inner error>\n" + e.stack : "";
if (apiZone.apiName && !apiZone.apiName.includes("<anonymous>"))
e.message = apiZone.apiName + ": " + e.message;
const stackFrames = "\n" + stringifyStackFrames(stackTrace.frames).join("\n") + innerError;
if (stackFrames.trim())
e.stack = e.message + stackFrames;
else
e.stack = "";
if (!options2?.internal) {
apiZone.error = e;
logApiCall(this._platform, logger, `<= ${apiZone.apiName} failed`);
this._instrumentation.onApiCallEnd(apiZone);
}
throw e;
}
}
toJSON() {
return {
_type: this._type,
_guid: this._guid
};
}
};
}
});
// packages/playwright-core/src/client/cdpSession.ts
var CDPSession2;
var init_cdpSession = __esm({
"packages/playwright-core/src/client/cdpSession.ts"() {
"use strict";
init_channelOwner();
CDPSession2 = class extends ChannelOwner {
static from(cdpSession) {
return cdpSession._object;
}
constructor(parent, type3, guid, initializer) {
super(parent, type3, guid, initializer);
this._channel.on("event", (event) => {
this.emit(event.method, event.params);
this.emit("event", event);
});
this._channel.on("close", () => {
this.emit("close", this);
});
this.on = super.on;
this.addListener = super.addListener;
this.off = super.removeListener;
this.removeListener = super.removeListener;
this.once = super.once;
}
async send(method, params2) {
const result2 = await this._channel.send({ method, params: params2 });
return result2.result;
}
async detach() {
return await this._channel.detach();
}
};
}
});
// packages/playwright-core/src/client/clientHelper.ts
function envObjectToArray2(env) {
const result2 = [];
for (const name in env) {
if (!Object.is(env[name], void 0))
result2.push({ name, value: String(env[name]) });
}
return result2;
}
async function evaluationScript(platform, fun, arg, addSourceUrl = true) {
if (typeof fun === "function") {
const source8 = fun.toString();
const argString = Object.is(arg, void 0) ? "undefined" : JSON.stringify(arg);
return `(${source8})(${argString})`;
}
if (arg !== void 0)
throw new Error("Cannot evaluate a string with arguments");
if (isString(fun))
return fun;
if (fun.content !== void 0)
return fun.content;
if (fun.path !== void 0) {
let source8 = await platform.fs().promises.readFile(fun.path, "utf8");
if (addSourceUrl)
source8 = addSourceUrlToScript(source8, fun.path);
return source8;
}
throw new Error("Either path or content property must be present");
}
function addSourceUrlToScript(source8, path59) {
return `${source8}
//# sourceURL=${path59.replace(/\n/g, "")}`;
}
var init_clientHelper = __esm({
"packages/playwright-core/src/client/clientHelper.ts"() {
"use strict";
init_rtti();
}
});
// packages/playwright-core/src/client/clock.ts
function parseTime2(time) {
if (typeof time === "number")
return { timeNumber: time };
if (typeof time === "string")
return { timeString: time };
if (!isFinite(time.getTime()))
throw new Error(`Invalid date: ${time}`);
return { timeNumber: time.getTime() };
}
function parseTicks2(ticks) {
return {
ticksNumber: typeof ticks === "number" ? ticks : void 0,
ticksString: typeof ticks === "string" ? ticks : void 0
};
}
var Clock2;
var init_clock2 = __esm({
"packages/playwright-core/src/client/clock.ts"() {
"use strict";
Clock2 = class {
constructor(browserContext) {
this._browserContext = browserContext;
}
async install(options2 = {}) {
await this._browserContext._channel.clockInstall(options2.time !== void 0 ? parseTime2(options2.time) : {});
}
async fastForward(ticks) {
await this._browserContext._channel.clockFastForward(parseTicks2(ticks));
}
async pauseAt(time) {
await this._browserContext._channel.clockPauseAt(parseTime2(time));
}
async resume() {
await this._browserContext._channel.clockResume({});
}
async runFor(ticks) {
await this._browserContext._channel.clockRunFor(parseTicks2(ticks));
}
async setFixedTime(time) {
await this._browserContext._channel.clockSetFixedTime(parseTime2(time));
}
async setSystemTime(time) {
await this._browserContext._channel.clockSetSystemTime(parseTime2(time));
}
};
}
});
// packages/playwright-core/src/client/errors.ts
function isTargetClosedError2(error) {
return error instanceof TargetClosedError2;
}
function serializeError2(e) {
if (isError(e))
return { error: { message: e.message, stack: e.stack, name: e.name } };
return { value: serializeValue(e, (value2) => ({ fallThrough: value2 })) };
}
function parseError2(error) {
if (!error.error) {
if (error.value === void 0)
throw new Error("Serialized error must have either an error or a value");
return parseSerializedValue(error.value, void 0);
}
if (error.error.name === "TimeoutError") {
const e2 = new TimeoutError2(error.error.message);
e2.stack = error.error.stack || "";
return e2;
}
if (error.error.name === "TargetClosedError") {
const e2 = new TargetClosedError2(error.error.message);
e2.stack = error.error.stack || "";
return e2;
}
const e = new Error(error.error.message);
e.stack = error.error.stack || "";
e.name = error.error.name;
return e;
}
var TimeoutError2, TargetClosedError2;
var init_errors2 = __esm({
"packages/playwright-core/src/client/errors.ts"() {
"use strict";
init_rtti();
init_serializers();
TimeoutError2 = class extends Error {
constructor(message) {
super(message);
this.name = "TimeoutError";
}
};
TargetClosedError2 = class extends Error {
constructor(cause) {
super(cause || "Target page, context or browser has been closed");
}
};
}
});
// packages/playwright-core/src/client/jsHandle.ts
function serializeArgument(arg) {
const handles = [];
const pushHandle = (channel) => {
handles.push(channel);
return handles.length - 1;
};
const value2 = serializeValue(arg, (value3) => {
if (value3 instanceof JSHandle2)
return { h: pushHandle(value3._channel) };
return { fallThrough: value3 };
});
return { value: value2, handles };
}
function parseResult(value2) {
return parseSerializedValue(value2, void 0);
}
function assertMaxArguments(count, max) {
if (count > max)
throw new Error("Too many arguments. If you need to pass more than 1 argument to the function wrap them in an object.");
}
var JSHandle2;
var init_jsHandle = __esm({
"packages/playwright-core/src/client/jsHandle.ts"() {
"use strict";
init_channelOwner();
init_errors2();
init_serializers();
JSHandle2 = class _JSHandle extends ChannelOwner {
static from(handle) {
return handle._object;
}
constructor(parent, type3, guid, initializer) {
super(parent, type3, guid, initializer);
this._preview = this._initializer.preview;
this._channel.on("previewUpdated", ({ preview }) => this._preview = preview);
}
async evaluate(pageFunction, arg) {
const result2 = await this._channel.evaluateExpression({ expression: String(pageFunction), isFunction: typeof pageFunction === "function", arg: serializeArgument(arg) });
return parseResult(result2.value);
}
async evaluateHandle(pageFunction, arg) {
const result2 = await this._channel.evaluateExpressionHandle({ expression: String(pageFunction), isFunction: typeof pageFunction === "function", arg: serializeArgument(arg) });
return _JSHandle.from(result2.handle);
}
async getProperty(propertyName) {
const result2 = await this._channel.getProperty({ name: propertyName });
return _JSHandle.from(result2.handle);
}
async getProperties() {
const map = /* @__PURE__ */ new Map();
for (const { name, value: value2 } of (await this._channel.getPropertyList()).properties)
map.set(name, _JSHandle.from(value2));
return map;
}
async jsonValue() {
return parseResult((await this._channel.jsonValue()).value);
}
asElement() {
return null;
}
async [Symbol.asyncDispose]() {
await this.dispose();
}
async dispose() {
try {
await this._channel.dispose();
} catch (e) {
if (isTargetClosedError2(e))
return;
throw e;
}
}
toString() {
return this._preview;
}
};
}
});
// packages/playwright-core/src/client/consoleMessage.ts
var ConsoleMessage2;
var init_consoleMessage = __esm({
"packages/playwright-core/src/client/consoleMessage.ts"() {
"use strict";
init_jsHandle();
ConsoleMessage2 = class {
constructor(platform, event, page, worker) {
this._page = page;
this._worker = worker;
this._event = event;
if (platform.inspectCustom)
this[platform.inspectCustom] = () => this._inspect();
}
worker() {
return this._worker;
}
page() {
return this._page;
}
type() {
return this._event.type;
}
text() {
return this._event.text;
}
args() {
return this._event.args.map(JSHandle2.from);
}
location() {
const { url: url2, lineNumber, columnNumber } = this._event.location;
return { url: url2, line: lineNumber, column: columnNumber, lineNumber, columnNumber };
}
timestamp() {
return this._event.timestamp;
}
_inspect() {
return this.text();
}
};
}
});
// packages/playwright-core/src/client/events.ts
var Events;
var init_events = __esm({
"packages/playwright-core/src/client/events.ts"() {
"use strict";
Events = {
AndroidDevice: {
WebView: "webview",
Close: "close"
},
AndroidSocket: {
Data: "data",
Close: "close"
},
AndroidWebView: {
Close: "close"
},
Browser: {
Context: "context",
Disconnected: "disconnected"
},
Debugger: {
PausedStateChanged: "pausedstatechanged"
},
BrowserContext: {
Console: "console",
Close: "close",
Dialog: "dialog",
Download: "download",
FrameAttached: "frameattached",
FrameDetached: "framedetached",
FrameNavigated: "framenavigated",
Page: "page",
PageClose: "pageclose",
PageLoad: "pageload",
// Can't use just 'error' due to node.js special treatment of error events.
// @see https://nodejs.org/api/events.html#events_error_events
WebError: "weberror",
BackgroundPage: "backgroundpage",
// Deprecated in v1.56, never emitted anymore.
ServiceWorker: "serviceworker",
Request: "request",
Response: "response",
RequestFailed: "requestfailed",
RequestFinished: "requestfinished"
},
BrowserServer: {
Close: "close"
},
Page: {
Close: "close",
Crash: "crash",
Console: "console",
Dialog: "dialog",
Download: "download",
FileChooser: "filechooser",
DOMContentLoaded: "domcontentloaded",
// Can't use just 'error' due to node.js special treatment of error events.
// @see https://nodejs.org/api/events.html#events_error_events
PageError: "pageerror",
Request: "request",
Response: "response",
RequestFailed: "requestfailed",
RequestFinished: "requestfinished",
FrameAttached: "frameattached",
FrameDetached: "framedetached",
FrameNavigated: "framenavigated",
Load: "load",
Popup: "popup",
WebSocket: "websocket",
Worker: "worker"
},
WebSocket: {
Close: "close",
Error: "socketerror",
FrameReceived: "framereceived",
FrameSent: "framesent"
},
Worker: {
Close: "close",
Console: "console"
},
ElectronApplication: {
Close: "close",
Console: "console",
Window: "window"
}
};
}
});
// packages/playwright-core/src/client/debugger.ts
var Debugger2;
var init_debugger2 = __esm({
"packages/playwright-core/src/client/debugger.ts"() {
"use strict";
init_channelOwner();
init_events();
Debugger2 = class extends ChannelOwner {
constructor(parent, type3, guid, initializer) {
super(parent, type3, guid, initializer);
this._pausedDetails = null;
this._channel.on("pausedStateChanged", ({ pausedDetails }) => {
this._pausedDetails = pausedDetails ?? null;
this.emit(Events.Debugger.PausedStateChanged);
});
}
static from(channel) {
return channel._object;
}
async requestPause() {
await this._channel.requestPause();
}
async resume() {
await this._channel.resume();
}
async next() {
await this._channel.next();
}
async runTo(location2) {
await this._channel.runTo({ location: location2 });
}
pausedDetails() {
return this._pausedDetails;
}
};
}
});
// packages/playwright-core/src/client/stream.ts
var Stream;
var init_stream = __esm({
"packages/playwright-core/src/client/stream.ts"() {
"use strict";
init_channelOwner();
Stream = class extends ChannelOwner {
static from(Stream2) {
return Stream2._object;
}
constructor(parent, type3, guid, initializer) {
super(parent, type3, guid, initializer);
}
stream() {
return this._platform.streamReadable(this._channel);
}
};
}
});
// packages/playwright-core/src/client/fileUtils.ts
async function mkdirIfNeeded2(platform, filePath) {
await platform.fs().promises.mkdir(platform.path().dirname(filePath), { recursive: true }).catch(() => {
});
}
var fileUploadSizeLimit2;
var init_fileUtils2 = __esm({
"packages/playwright-core/src/client/fileUtils.ts"() {
"use strict";
fileUploadSizeLimit2 = 50 * 1024 * 1024;
}
});
// packages/playwright-core/src/client/artifact.ts
var Artifact2;
var init_artifact2 = __esm({
"packages/playwright-core/src/client/artifact.ts"() {
"use strict";
init_channelOwner();
init_stream();
init_fileUtils2();
Artifact2 = class extends ChannelOwner {
static from(channel) {
return channel._object;
}
async pathAfterFinished() {
if (this._connection.isRemote())
throw new Error(`Path is not available when connecting remotely. Use saveAs() to save a local copy.`);
return (await this._channel.pathAfterFinished()).value;
}
async saveAs(path59) {
if (!this._connection.isRemote()) {
await this._channel.saveAs({ path: path59 });
return;
}
const result2 = await this._channel.saveAsStream();
const stream3 = Stream.from(result2.stream);
await mkdirIfNeeded2(this._platform, path59);
await new Promise((resolve, reject) => {
stream3.stream().pipe(this._platform.fs().createWriteStream(path59)).on("finish", resolve).on("error", reject);
});
}
async failure() {
return (await this._channel.failure()).error || null;
}
async createReadStream() {
const result2 = await this._channel.stream();
const stream3 = Stream.from(result2.stream);
return stream3.stream();
}
async readIntoBuffer() {
const stream3 = await this.createReadStream();
return await new Promise((resolve, reject) => {
const chunks = [];
stream3.on("data", (chunk) => {
chunks.push(chunk);
});
stream3.on("end", () => {
resolve(Buffer.concat(chunks));
});
stream3.on("error", reject);
});
}
async cancel() {
return await this._channel.cancel();
}
async delete() {
return await this._channel.delete();
}
};
}
});
// packages/playwright-core/src/client/coverage.ts
var Coverage;
var init_coverage = __esm({
"packages/playwright-core/src/client/coverage.ts"() {
"use strict";
Coverage = class {
constructor(channel) {
this._channel = channel;
}
async startJSCoverage(options2 = {}) {
await this._channel.startJSCoverage(options2);
}
async stopJSCoverage() {
return (await this._channel.stopJSCoverage()).entries;
}
async startCSSCoverage(options2 = {}) {
await this._channel.startCSSCoverage(options2);
}
async stopCSSCoverage() {
return (await this._channel.stopCSSCoverage()).entries;
}
};
}
});
// packages/playwright-core/src/client/disposable.ts
var DisposableObject2, DisposableStub;
var init_disposable3 = __esm({
"packages/playwright-core/src/client/disposable.ts"() {
"use strict";
init_channelOwner();
init_errors2();
DisposableObject2 = class extends ChannelOwner {
static from(channel) {
return channel._object;
}
async [Symbol.asyncDispose]() {
await this.dispose();
}
async dispose() {
try {
await this._channel.dispose();
} catch (e) {
if (isTargetClosedError2(e))
return;
throw e;
}
}
};
DisposableStub = class {
constructor(dispose) {
this._dispose = dispose;
}
async [Symbol.asyncDispose]() {
await this.dispose();
}
async dispose() {
if (!this._dispose)
return;
try {
const dispose = this._dispose;
this._dispose = void 0;
await dispose();
} catch (e) {
if (isTargetClosedError2(e))
return;
throw e;
}
}
};
}
});
// packages/playwright-core/src/client/download.ts
var Download2;
var init_download2 = __esm({
"packages/playwright-core/src/client/download.ts"() {
"use strict";
Download2 = class {
constructor(page, url2, suggestedFilename, artifact) {
this._page = page;
this._url = url2;
this._suggestedFilename = suggestedFilename;
this._artifact = artifact;
}
page() {
return this._page;
}
url() {
return this._url;
}
suggestedFilename() {
return this._suggestedFilename;
}
async path() {
return await this._artifact.pathAfterFinished();
}
async saveAs(path59) {
return await this._artifact.saveAs(path59);
}
async failure() {
return await this._artifact.failure();
}
async createReadStream() {
return await this._artifact.createReadStream();
}
async cancel() {
return await this._artifact.cancel();
}
async delete() {
return await this._artifact.delete();
}
};
}
});
// packages/isomorphic/locatorUtils.ts
function getByAttributeTextSelector(attrName, text2, options2) {
return `internal:attr=[${attrName}=${escapeForAttributeSelector(text2, options2?.exact || false)}]`;
}
function getByTestIdSelector(testIdAttributeName2, testId) {
return `internal:testid=[${testIdAttributeName2}=${escapeForAttributeSelector(testId, true)}]`;
}
function getByLabelSelector(text2, options2) {
return "internal:label=" + escapeForTextSelector(text2, !!options2?.exact);
}
function getByAltTextSelector(text2, options2) {
return getByAttributeTextSelector("alt", text2, options2);
}
function getByTitleSelector(text2, options2) {
return getByAttributeTextSelector("title", text2, options2);
}
function getByPlaceholderSelector(text2, options2) {
return getByAttributeTextSelector("placeholder", text2, options2);
}
function getByTextSelector(text2, options2) {
return "internal:text=" + escapeForTextSelector(text2, !!options2?.exact);
}
function getByRoleSelector(role, options2 = {}) {
const props = [];
if (options2.checked !== void 0)
props.push(["checked", String(options2.checked)]);
if (options2.disabled !== void 0)
props.push(["disabled", String(options2.disabled)]);
if (options2.selected !== void 0)
props.push(["selected", String(options2.selected)]);
if (options2.expanded !== void 0)
props.push(["expanded", String(options2.expanded)]);
if (options2.includeHidden !== void 0)
props.push(["include-hidden", String(options2.includeHidden)]);
if (options2.level !== void 0)
props.push(["level", String(options2.level)]);
if (options2.name !== void 0)
props.push(["name", escapeForAttributeSelector(options2.name, !!options2.exact)]);
if (options2.description !== void 0)
props.push(["description", escapeForAttributeSelector(options2.description, !!options2.exact)]);
if (options2.pressed !== void 0)
props.push(["pressed", String(options2.pressed)]);
return `internal:role=${role}${props.map(([n, v]) => `[${n}=${v}]`).join("")}`;
}
var init_locatorUtils = __esm({
"packages/isomorphic/locatorUtils.ts"() {
"use strict";
init_stringUtils();
}
});
// packages/playwright-core/src/client/locator.ts
function testIdAttributeName() {
return _testIdAttributeName;
}
function setTestIdAttribute(attributeName) {
_testIdAttributeName = attributeName;
}
function cssObjectToString(style) {
return Object.entries(style).map(([key, value2]) => {
const property = key.startsWith("--") ? key : key.replace(/[A-Z]/g, (m) => "-" + m.toLowerCase());
return `${property}: ${value2}`;
}).join("; ");
}
var Locator, FrameLocator, _testIdAttributeName;
var init_locator = __esm({
"packages/playwright-core/src/client/locator.ts"() {
"use strict";
init_locatorGenerators();
init_locatorUtils();
init_stringUtils();
init_rtti();
init_time();
init_elementHandle();
init_disposable3();
Locator = class _Locator {
constructor(frame, selector, options2) {
this._apiName = "Locator";
this._frame = frame;
this._selector = selector;
if (options2?.hasText)
this._selector += ` >> internal:has-text=${escapeForTextSelector(options2.hasText, false)}`;
if (options2?.hasNotText)
this._selector += ` >> internal:has-not-text=${escapeForTextSelector(options2.hasNotText, false)}`;
if (options2?.has) {
const locator2 = options2.has;
if (locator2._frame !== frame)
throw new Error(`Inner "has" locator must belong to the same frame.`);
this._selector += ` >> internal:has=` + JSON.stringify(locator2._selector);
}
if (options2?.hasNot) {
const locator2 = options2.hasNot;
if (locator2._frame !== frame)
throw new Error(`Inner "hasNot" locator must belong to the same frame.`);
this._selector += ` >> internal:has-not=` + JSON.stringify(locator2._selector);
}
if (options2?.visible !== void 0)
this._selector += ` >> visible=${options2.visible ? "true" : "false"}`;
if (this._frame._platform.inspectCustom)
this[this._frame._platform.inspectCustom] = () => this._inspect();
}
async _withElement(task, options2) {
const timeout = this._frame._timeout({ timeout: options2.timeout });
const deadline = timeout ? monotonicTime() + timeout : 0;
return await this._frame._wrapApiCall(async () => {
const result2 = await this._frame._channel.waitForSelector({ selector: this._selector, strict: true, state: "attached", timeout });
const handle = ElementHandle2.fromNullable(result2.element);
if (!handle)
throw new Error(`Could not resolve ${this._selector} to DOM Element`);
try {
return await task(handle, deadline ? deadline - monotonicTime() : 0);
} finally {
await handle.dispose();
}
}, { title: options2.title, internal: options2.internal });
}
_equals(locator2) {
return this._frame === locator2._frame && this._selector === locator2._selector;
}
page() {
return this._frame.page();
}
async boundingBox(options2) {
return await this._withElement((h) => h.boundingBox(), { title: "Bounding box", timeout: options2?.timeout });
}
async check(options2 = {}) {
return await this._frame.check(this._selector, { strict: true, ...options2 });
}
async click(options2 = {}) {
return await this._frame.click(this._selector, { strict: true, ...options2 });
}
async dblclick(options2 = {}) {
await this._frame.dblclick(this._selector, { strict: true, ...options2 });
}
async dispatchEvent(type3, eventInit = {}, options2) {
return await this._frame.dispatchEvent(this._selector, type3, eventInit, { strict: true, ...options2 });
}
async dragTo(target, options2 = {}) {
return await this._frame.dragAndDrop(this._selector, target._selector, {
strict: true,
...options2
});
}
async drop(payload, options2 = {}) {
await this._frame._drop(this._selector, payload, { strict: true, ...options2 });
}
async evaluate(pageFunction, arg, options2) {
return await this._withElement((h) => h.evaluate(pageFunction, arg), { title: "Evaluate", timeout: options2?.timeout });
}
async evaluateAll(pageFunction, arg) {
return await this._frame.$$eval(this._selector, pageFunction, arg);
}
async evaluateHandle(pageFunction, arg, options2) {
return await this._withElement((h) => h.evaluateHandle(pageFunction, arg), { title: "Evaluate", timeout: options2?.timeout });
}
async fill(value2, options2 = {}) {
return await this._frame.fill(this._selector, value2, { strict: true, ...options2 });
}
async clear(options2 = {}) {
await this._frame._wrapApiCall(() => this.fill("", options2), { title: "Clear" });
}
async _highlight() {
return await this._frame._highlight(this._selector);
}
async highlight(options2 = {}) {
const style = typeof options2.style === "object" ? cssObjectToString(options2.style) : options2.style;
await this._frame._highlight(this._selector, style);
return new DisposableStub(() => this.hideHighlight());
}
async hideHighlight() {
await this._frame._hideHighlight(this._selector);
}
locator(selectorOrLocator, options2) {
if (isString(selectorOrLocator))
return new _Locator(this._frame, this._selector + " >> " + selectorOrLocator, options2);
if (selectorOrLocator._frame !== this._frame)
throw new Error(`Locators must belong to the same frame.`);
return new _Locator(this._frame, this._selector + " >> internal:chain=" + JSON.stringify(selectorOrLocator._selector), options2);
}
getByTestId(testId) {
return this.locator(getByTestIdSelector(testIdAttributeName(), testId));
}
getByAltText(text2, options2) {
return this.locator(getByAltTextSelector(text2, options2));
}
getByLabel(text2, options2) {
return this.locator(getByLabelSelector(text2, options2));
}
getByPlaceholder(text2, options2) {
return this.locator(getByPlaceholderSelector(text2, options2));
}
getByText(text2, options2) {
return this.locator(getByTextSelector(text2, options2));
}
getByTitle(text2, options2) {
return this.locator(getByTitleSelector(text2, options2));
}
getByRole(role, options2 = {}) {
return this.locator(getByRoleSelector(role, options2));
}
frameLocator(selector) {
return new FrameLocator(this._frame, this._selector + " >> " + selector);
}
filter(options2) {
return new _Locator(this._frame, this._selector, options2);
}
async elementHandle(options2) {
return await this._frame.waitForSelector(this._selector, { strict: true, state: "attached", ...options2 });
}
async elementHandles() {
return await this._frame.$$(this._selector);
}
contentFrame() {
return new FrameLocator(this._frame, this._selector);
}
describe(description) {
return new _Locator(this._frame, this._selector + " >> internal:describe=" + JSON.stringify(description));
}
description() {
return locatorCustomDescription(this._selector) || null;
}
first() {
return new _Locator(this._frame, this._selector + " >> nth=0");
}
last() {
return new _Locator(this._frame, this._selector + ` >> nth=-1`);
}
nth(index) {
return new _Locator(this._frame, this._selector + ` >> nth=${index}`);
}
and(locator2) {
if (locator2._frame !== this._frame)
throw new Error(`Locators must belong to the same frame.`);
return new _Locator(this._frame, this._selector + ` >> internal:and=` + JSON.stringify(locator2._selector));
}
or(locator2) {
if (locator2._frame !== this._frame)
throw new Error(`Locators must belong to the same frame.`);
return new _Locator(this._frame, this._selector + ` >> internal:or=` + JSON.stringify(locator2._selector));
}
async focus(options2) {
return await this._frame.focus(this._selector, { strict: true, ...options2 });
}
async blur(options2) {
await this._frame._channel.blur({ selector: this._selector, strict: true, ...options2, timeout: this._frame._timeout(options2) });
}
// options are only here for testing
async count(_options) {
return await this._frame._queryCount(this._selector, _options);
}
async normalize() {
const { resolvedSelector } = await this._frame._channel.resolveSelector({ selector: this._selector });
return new _Locator(this._frame, resolvedSelector);
}
async getAttribute(name, options2) {
return await this._frame.getAttribute(this._selector, name, { strict: true, ...options2 });
}
async hover(options2 = {}) {
return await this._frame.hover(this._selector, { strict: true, ...options2 });
}
async innerHTML(options2) {
return await this._frame.innerHTML(this._selector, { strict: true, ...options2 });
}
async innerText(options2) {
return await this._frame.innerText(this._selector, { strict: true, ...options2 });
}
async inputValue(options2) {
return await this._frame.inputValue(this._selector, { strict: true, ...options2 });
}
async isChecked(options2) {
return await this._frame.isChecked(this._selector, { strict: true, ...options2 });
}
async isDisabled(options2) {
return await this._frame.isDisabled(this._selector, { strict: true, ...options2 });
}
async isEditable(options2) {
return await this._frame.isEditable(this._selector, { strict: true, ...options2 });
}
async isEnabled(options2) {
return await this._frame.isEnabled(this._selector, { strict: true, ...options2 });
}
async isHidden(options2) {
return await this._frame.isHidden(this._selector, { strict: true, ...options2 });
}
async isVisible(options2) {
return await this._frame.isVisible(this._selector, { strict: true, ...options2 });
}
async press(key, options2 = {}) {
return await this._frame.press(this._selector, key, { strict: true, ...options2 });
}
async screenshot(options2 = {}) {
const mask = options2.mask;
return await this._withElement((h, timeout) => h.screenshot({ ...options2, mask, timeout }), { title: "Screenshot", timeout: options2.timeout });
}
async ariaSnapshot(options2 = {}) {
const result2 = await this._frame._channel.ariaSnapshot({ timeout: this._frame._timeout(options2), mode: options2.mode, selector: this._selector, depth: options2.depth, boxes: options2.boxes });
return result2.snapshot;
}
async scrollIntoViewIfNeeded(options2 = {}) {
return await this._withElement((h, timeout) => h.scrollIntoViewIfNeeded({ ...options2, timeout }), { title: "Scroll into view", timeout: options2.timeout });
}
async selectOption(values, options2 = {}) {
return await this._frame.selectOption(this._selector, values, { strict: true, ...options2 });
}
async selectText(options2 = {}) {
return await this._withElement((h, timeout) => h.selectText({ ...options2, timeout }), { title: "Select text", timeout: options2.timeout });
}
async setChecked(checked, options2) {
if (checked)
await this.check(options2);
else
await this.uncheck(options2);
}
async setInputFiles(files, options2 = {}) {
return await this._frame.setInputFiles(this._selector, files, { strict: true, ...options2 });
}
async tap(options2 = {}) {
return await this._frame.tap(this._selector, { strict: true, ...options2 });
}
async textContent(options2) {
return await this._frame.textContent(this._selector, { strict: true, ...options2 });
}
async type(text2, options2 = {}) {
return await this._frame.type(this._selector, text2, { strict: true, ...options2 });
}
async pressSequentially(text2, options2 = {}) {
return await this.type(text2, options2);
}
async uncheck(options2 = {}) {
return await this._frame.uncheck(this._selector, { strict: true, ...options2 });
}
async all() {
return new Array(await this.count()).fill(0).map((e, i) => this.nth(i));
}
async allInnerTexts() {
return await this._frame.$$eval(this._selector, (ee) => ee.map((e) => e.innerText));
}
async allTextContents() {
return await this._frame.$$eval(this._selector, (ee) => ee.map((e) => e.textContent || ""));
}
async waitFor(options2) {
await this._frame._channel.waitForSelector({ selector: this._selector, strict: true, omitReturnValue: true, ...options2, timeout: this._frame._timeout(options2) });
}
async _expect(expression2, options2) {
return this._frame._expect(expression2, {
...options2,
selector: this._selector
});
}
_inspect() {
return this.toString();
}
toString() {
return asLocatorDescription("javascript", this._selector);
}
};
FrameLocator = class _FrameLocator {
constructor(frame, selector) {
this._frame = frame;
this._frameSelector = selector;
}
locator(selectorOrLocator, options2) {
if (isString(selectorOrLocator))
return new Locator(this._frame, this._frameSelector + " >> internal:control=enter-frame >> " + selectorOrLocator, options2);
if (selectorOrLocator._frame !== this._frame)
throw new Error(`Locators must belong to the same frame.`);
return new Locator(this._frame, this._frameSelector + " >> internal:control=enter-frame >> " + selectorOrLocator._selector, options2);
}
getByTestId(testId) {
return this.locator(getByTestIdSelector(testIdAttributeName(), testId));
}
getByAltText(text2, options2) {
return this.locator(getByAltTextSelector(text2, options2));
}
getByLabel(text2, options2) {
return this.locator(getByLabelSelector(text2, options2));
}
getByPlaceholder(text2, options2) {
return this.locator(getByPlaceholderSelector(text2, options2));
}
getByText(text2, options2) {
return this.locator(getByTextSelector(text2, options2));
}
getByTitle(text2, options2) {
return this.locator(getByTitleSelector(text2, options2));
}
getByRole(role, options2 = {}) {
return this.locator(getByRoleSelector(role, options2));
}
owner() {
return new Locator(this._frame, this._frameSelector);
}
frameLocator(selector) {
return new _FrameLocator(this._frame, this._frameSelector + " >> internal:control=enter-frame >> " + selector);
}
first() {
return new _FrameLocator(this._frame, this._frameSelector + " >> nth=0");
}
last() {
return new _FrameLocator(this._frame, this._frameSelector + ` >> nth=-1`);
}
nth(index) {
return new _FrameLocator(this._frame, this._frameSelector + ` >> nth=${index}`);
}
};
_testIdAttributeName = "data-testid";
}
});
// packages/playwright-core/src/client/timeoutSettings.ts
var TimeoutSettings;
var init_timeoutSettings = __esm({
"packages/playwright-core/src/client/timeoutSettings.ts"() {
"use strict";
init_time();
TimeoutSettings = class {
constructor(platform, parent) {
this._parent = parent;
this._platform = platform;
}
setDefaultTimeout(timeout) {
this._defaultTimeout = timeout;
}
setDefaultNavigationTimeout(timeout) {
this._defaultNavigationTimeout = timeout;
}
defaultNavigationTimeout() {
return this._defaultNavigationTimeout;
}
defaultTimeout() {
return this._defaultTimeout;
}
navigationTimeout(options2) {
if (typeof options2.timeout === "number")
return options2.timeout;
if (this._defaultNavigationTimeout !== void 0)
return this._defaultNavigationTimeout;
if (this._platform.isDebugMode())
return 0;
if (this._defaultTimeout !== void 0)
return this._defaultTimeout;
if (this._parent)
return this._parent.navigationTimeout(options2);
return DEFAULT_PLAYWRIGHT_TIMEOUT;
}
timeout(options2) {
if (typeof options2.timeout === "number")
return options2.timeout;
if (this._platform.isDebugMode())
return 0;
if (this._defaultTimeout !== void 0)
return this._defaultTimeout;
if (this._parent)
return this._parent.timeout(options2);
return DEFAULT_PLAYWRIGHT_TIMEOUT;
}
launchTimeout(options2) {
if (typeof options2.timeout === "number")
return options2.timeout;
if (this._platform.isDebugMode())
return 0;
if (this._parent)
return this._parent.launchTimeout(options2);
return DEFAULT_PLAYWRIGHT_LAUNCH_TIMEOUT;
}
};
}
});
// packages/playwright-core/src/client/waiter.ts
function waitForEvent(emitter, event, savedZone, predicate) {
let listener;
const promise = new Promise((resolve, reject) => {
listener = async (eventArg) => {
await savedZone.run(async () => {
try {
if (predicate && !await predicate(eventArg))
return;
emitter.removeListener(event, listener);
resolve(eventArg);
} catch (e) {
emitter.removeListener(event, listener);
reject(e);
}
});
};
emitter.addListener(event, listener);
});
const dispose = () => emitter.removeListener(event, listener);
return { promise, dispose };
}
function waitForTimeout(timeout) {
let timeoutId;
const promise = new Promise((resolve) => timeoutId = setTimeout(resolve, timeout));
const dispose = () => clearTimeout(timeoutId);
return { promise, dispose };
}
function formatLogRecording(log2) {
if (!log2.length)
return "";
const header = ` logs `;
const headerLength = 60;
const leftLength = (headerLength - header.length) / 2;
const rightLength = headerLength - header.length - leftLength;
return `
${"=".repeat(leftLength)}${header}${"=".repeat(rightLength)}
${log2.join("\n")}
${"=".repeat(headerLength)}`;
}
var Waiter;
var init_waiter = __esm({
"packages/playwright-core/src/client/waiter.ts"() {
"use strict";
init_stackTrace();
init_errors2();
Waiter = class _Waiter {
constructor(channelOwner, event) {
this._failures = [];
this._logs = [];
this._waitId = channelOwner._platform.createGuid();
this._channelOwner = channelOwner;
this._savedZone = channelOwner._platform.zones.current().pop();
this._channelOwner._channel.waitForEventInfo({ info: { waitId: this._waitId, phase: "before", event } }).catch(() => {
});
this._dispose = [
() => this._channelOwner._wrapApiCall(async () => {
await this._channelOwner._channel.waitForEventInfo({ info: { waitId: this._waitId, phase: "after", error: this._error } });
}, { internal: true }).catch(() => {
})
];
}
static createForEvent(channelOwner, event) {
return new _Waiter(channelOwner, event);
}
async waitForEvent(emitter, event, predicate) {
const { promise, dispose } = waitForEvent(emitter, event, this._savedZone, predicate);
return await this.waitForPromise(promise, dispose);
}
rejectOnEvent(emitter, event, error, predicate) {
const { promise, dispose } = waitForEvent(emitter, event, this._savedZone, predicate);
this._rejectOn(promise.then(() => {
throw typeof error === "function" ? error() : error;
}), dispose);
}
rejectOnTimeout(timeout, message) {
if (!timeout)
return;
const { promise, dispose } = waitForTimeout(timeout);
this._rejectOn(promise.then(() => {
throw new TimeoutError2(message);
}), dispose);
}
rejectImmediately(error) {
this._immediateError = error;
}
dispose() {
for (const dispose of this._dispose)
dispose();
}
async waitForPromise(promise, dispose) {
try {
if (this._immediateError)
throw this._immediateError;
const result2 = await Promise.race([promise, ...this._failures]);
if (dispose)
dispose();
return result2;
} catch (e) {
if (dispose)
dispose();
this._error = e.message;
this.dispose();
rewriteErrorMessage(e, e.message + formatLogRecording(this._logs));
throw e;
}
}
log(s) {
this._logs.push(s);
this._channelOwner._wrapApiCall(async () => {
await this._channelOwner._channel.waitForEventInfo({ info: { waitId: this._waitId, phase: "log", message: s } });
}, { internal: true }).catch(() => {
});
}
_rejectOn(promise, dispose) {
this._failures.push(promise);
if (dispose)
this._dispose.push(dispose);
}
};
}
});
// packages/playwright-core/src/client/worker.ts
var Worker2;
var init_worker = __esm({
"packages/playwright-core/src/client/worker.ts"() {
"use strict";
init_manualPromise();
init_channelOwner();
init_consoleMessage();
init_errors2();
init_events();
init_jsHandle();
init_timeoutSettings();
init_waiter();
Worker2 = class _Worker extends ChannelOwner {
constructor(parent, type3, guid, initializer) {
super(parent, type3, guid, initializer);
// Set for service workers.
this._closedScope = new LongStandingScope();
this._setEventToSubscriptionMapping(/* @__PURE__ */ new Map([
[Events.Worker.Console, "console"]
]));
this._channel.on("console", (event) => {
this.emit(Events.Worker.Console, new ConsoleMessage2(this._platform, event, null, this));
});
this._channel.on("close", () => {
if (this._page)
this._page._workers.delete(this);
if (this._context)
this._context._serviceWorkers.delete(this);
this.emit(Events.Worker.Close, this);
});
this.once(Events.Worker.Close, () => this._closedScope.close(this._closeErrorWithReason()));
}
static fromNullable(worker) {
return worker ? _Worker.from(worker) : null;
}
static from(worker) {
return worker._object;
}
_closeErrorWithReason() {
if (this._closeReason)
return new TargetClosedError2(this._closeReason);
return this._page?._closeErrorWithReason() || new TargetClosedError2();
}
url() {
return this._initializer.url;
}
async evaluate(pageFunction, arg) {
assertMaxArguments(arguments.length, 2);
const result2 = await this._channel.evaluateExpression({ expression: String(pageFunction), isFunction: typeof pageFunction === "function", arg: serializeArgument(arg) });
return parseResult(result2.value);
}
async evaluateHandle(pageFunction, arg) {
assertMaxArguments(arguments.length, 2);
const result2 = await this._channel.evaluateExpressionHandle({ expression: String(pageFunction), isFunction: typeof pageFunction === "function", arg: serializeArgument(arg) });
return JSHandle2.from(result2.handle);
}
async waitForEvent(event, optionsOrPredicate = {}) {
return await this._wrapApiCall(async () => {
const timeoutSettings = this._page?._timeoutSettings ?? this._context?._timeoutSettings ?? new TimeoutSettings(this._platform);
const timeout = timeoutSettings.timeout(typeof optionsOrPredicate === "function" ? {} : optionsOrPredicate);
const predicate = typeof optionsOrPredicate === "function" ? optionsOrPredicate : optionsOrPredicate.predicate;
const waiter = Waiter.createForEvent(this, event);
waiter.rejectOnTimeout(timeout, `Timeout ${timeout}ms exceeded while waiting for event "${event}"`);
if (event !== Events.Worker.Close)
waiter.rejectOnEvent(this, Events.Worker.Close, () => this._closeErrorWithReason());
const result2 = await waiter.waitForEvent(this, event, predicate);
waiter.dispose();
return result2;
});
}
async _disconnect(options2 = {}) {
this._closeReason = options2.reason;
try {
await this._channel.disconnect(options2);
} catch (e) {
if (isTargetClosedError2(e))
return;
throw e;
}
}
};
}
});
// packages/playwright-core/src/client/tracing.ts
var Tracing2;
var init_tracing2 = __esm({
"packages/playwright-core/src/client/tracing.ts"() {
"use strict";
init_rtti();
init_artifact2();
init_channelOwner();
init_disposable3();
Tracing2 = class extends ChannelOwner {
constructor(parent, type3, guid, initializer) {
super(parent, type3, guid, initializer);
this._includeSources = false;
this._additionalSources = /* @__PURE__ */ new Set();
this._isLive = false;
this._isTracing = false;
this._harRecorders = /* @__PURE__ */ new Map();
}
static from(channel) {
return channel._object;
}
async start(options2 = {}) {
await this._wrapApiCall(async () => {
this._includeSources = !!options2.sources;
this._isLive = !!options2.live;
await this._channel.tracingStart({
name: options2.name,
snapshots: options2.snapshots,
screenshots: options2.screenshots,
live: options2.live
});
const { traceName } = await this._channel.tracingStartChunk({ name: options2.name, title: options2.title });
await this._startCollectingStacks(traceName, this._isLive);
});
}
async startChunk(options2 = {}) {
await this._wrapApiCall(async () => {
const { traceName } = await this._channel.tracingStartChunk(options2);
await this._startCollectingStacks(traceName, this._isLive);
});
}
async group(name, options2 = {}) {
if (options2.location)
this._additionalSources.add(options2.location.file);
await this._channel.tracingGroup({ name, location: options2.location });
return new DisposableStub(() => this.groupEnd());
}
async groupEnd() {
await this._channel.tracingGroupEnd();
}
async _startCollectingStacks(traceName, live) {
if (!this._isTracing) {
this._isTracing = true;
this._connection.setIsTracing(true);
}
const result2 = await this._connection.localUtils()?.tracingStarted({ tracesDir: this._tracesDir, traceName, live });
this._stacksId = result2?.stacksId;
}
async stopChunk(options2 = {}) {
await this._wrapApiCall(async () => {
await this._doStopChunk(options2.path);
});
}
async stop(options2 = {}) {
await this._wrapApiCall(async () => {
await this._doStopChunk(options2.path);
await this._channel.tracingStop();
});
}
async startHar(path59, options2 = {}) {
await this._wrapApiCall(async () => {
if (this._harId)
throw new Error("HAR recording has already been started");
if (options2.resourcesDir && path59.endsWith(".zip"))
throw new Error("resourcesDir option is not compatible with a .zip har file");
const defaultContent = path59.endsWith(".zip") ? "attach" : "embed";
this._harId = await this._recordIntoHAR(path59, null, {
url: options2.urlFilter,
updateContent: options2.content ?? defaultContent,
updateMode: options2.mode ?? "full",
resourcesDir: options2.resourcesDir
});
});
return new DisposableStub(() => this.stopHar());
}
async stopHar() {
await this._wrapApiCall(async () => {
const harId = this._harId;
if (!harId)
throw new Error("HAR recording has not been started");
this._harId = void 0;
await this._exportHAR(harId);
});
}
async _recordIntoHAR(har, page, options2 = {}) {
const isZip = har.endsWith(".zip");
const { harId } = await this._channel.harStart({
page: page?._channel,
options: {
content: options2.updateContent ?? "attach",
urlGlob: isString(options2.url) ? options2.url : void 0,
urlRegexSource: isRegExp(options2.url) ? options2.url.source : void 0,
urlRegexFlags: isRegExp(options2.url) ? options2.url.flags : void 0,
mode: options2.updateMode ?? "minimal",
harPath: isZip ? void 0 : har,
resourcesDir: options2.resourcesDir
}
});
this._harRecorders.set(harId, { path: har, resourcesDir: options2.resourcesDir });
return harId;
}
async _exportHAR(harId) {
const harParams = this._harRecorders.get(harId);
if (!harParams)
return;
this._harRecorders.delete(harId);
const isLocal = !this._connection.isRemote();
const isZip = harParams.path.endsWith(".zip");
if (isLocal) {
const { entries } = await this._channel.harExport({ harId, mode: "entries" });
if (!isZip) {
return;
}
const localUtils2 = this._connection.localUtils();
if (!localUtils2)
throw new Error("Cannot save zipped HAR in thin clients");
await localUtils2.zip({ zipFile: harParams.path, entries, mode: "write", includeSources: false, additionalSources: [] });
return;
}
const { artifact: artifactChannel } = await this._channel.harExport({ harId, mode: "archive" });
const artifact = Artifact2.from(artifactChannel);
if (isZip) {
await artifact.saveAs(harParams.path);
await artifact.delete();
return;
}
const localUtils = this._connection.localUtils();
if (!localUtils)
throw new Error("Uncompressed har is not supported in thin clients");
await artifact.saveAs(harParams.path + ".tmp");
await localUtils.harUnzip({ zipFile: harParams.path + ".tmp", harFile: harParams.path, resourcesDir: harParams.resourcesDir });
await artifact.delete();
}
async _exportAllHars() {
await this._wrapApiCall(async () => {
await Promise.all([...this._harRecorders.keys()].map((harId) => this._exportHAR(harId)));
}, { internal: true });
}
async _doStopChunk(filePath) {
this._resetStackCounter();
const additionalSources = [...this._additionalSources];
this._additionalSources.clear();
if (!filePath) {
await this._channel.tracingStopChunk({ mode: "discard" });
if (this._stacksId)
await this._connection.localUtils().traceDiscarded({ stacksId: this._stacksId });
return;
}
const localUtils = this._connection.localUtils();
if (!localUtils)
throw new Error("Cannot save trace in thin clients");
const isLocal = !this._connection.isRemote();
if (isLocal) {
const result3 = await this._channel.tracingStopChunk({ mode: "entries" });
await localUtils.zip({ zipFile: filePath, entries: result3.entries, mode: "write", stacksId: this._stacksId, includeSources: this._includeSources, additionalSources });
return;
}
const result2 = await this._channel.tracingStopChunk({ mode: "archive" });
if (!result2.artifact) {
if (this._stacksId)
await localUtils.traceDiscarded({ stacksId: this._stacksId });
return;
}
const artifact = Artifact2.from(result2.artifact);
await artifact.saveAs(filePath);
await artifact.delete();
await localUtils.zip({ zipFile: filePath, entries: [], mode: "append", stacksId: this._stacksId, includeSources: this._includeSources, additionalSources });
}
_resetStackCounter() {
if (this._isTracing) {
this._isTracing = false;
this._connection.setIsTracing(false);
}
}
};
}
});
// packages/playwright-core/src/client/fetch.ts
async function toFormField(platform, name, value2) {
const typeOfValue = typeof value2;
if (isFilePayload(value2)) {
const payload = value2;
if (!Buffer.isBuffer(payload.buffer))
throw new Error(`Unexpected buffer type of 'data.${name}'`);
return { name, file: filePayloadToJson(payload) };
} else if (typeOfValue === "string" || typeOfValue === "number" || typeOfValue === "boolean") {
return { name, value: String(value2) };
} else {
return { name, file: await readStreamToJson(platform, value2) };
}
}
function isJsonParsable(value2) {
if (typeof value2 !== "string")
return false;
try {
JSON.parse(value2);
return true;
} catch (e) {
if (e instanceof SyntaxError)
return false;
else
throw e;
}
}
function filePayloadToJson(payload) {
return {
name: payload.name,
mimeType: payload.mimeType,
buffer: payload.buffer
};
}
async function readStreamToJson(platform, stream3) {
const buffer = await new Promise((resolve, reject) => {
const chunks = [];
stream3.on("data", (chunk) => chunks.push(chunk));
stream3.on("end", () => resolve(Buffer.concat(chunks)));
stream3.on("error", (err) => reject(err));
});
const streamPath = Buffer.isBuffer(stream3.path) ? stream3.path.toString("utf8") : stream3.path;
return {
name: platform.path().basename(streamPath),
buffer
};
}
function isJsonContentType(headers) {
if (!headers)
return false;
for (const { name, value: value2 } of headers) {
if (name.toLocaleLowerCase() === "content-type")
return value2 === "application/json";
}
return false;
}
function objectToArray(map) {
if (!map)
return void 0;
const result2 = [];
for (const [name, value2] of Object.entries(map)) {
if (value2 !== void 0)
result2.push({ name, value: String(value2) });
}
return result2;
}
function isFilePayload(value2) {
return typeof value2 === "object" && value2["name"] && value2["mimeType"] && value2["buffer"];
}
var APIRequest, APIRequestContext2, APIResponse;
var init_fetch2 = __esm({
"packages/playwright-core/src/client/fetch.ts"() {
"use strict";
init_assert();
init_headers();
init_rtti();
init_browserContext2();
init_channelOwner();
init_errors2();
init_network3();
init_tracing2();
init_fileUtils2();
init_timeoutSettings();
APIRequest = class {
constructor(playwright2) {
this._contexts = /* @__PURE__ */ new Set();
this._playwright = playwright2;
}
async newContext(options2 = {}) {
options2 = { ...options2 };
await this._playwright._instrumentation.runBeforeCreateRequestContext(options2);
const storageState2 = typeof options2.storageState === "string" ? JSON.parse(await this._playwright._platform.fs().promises.readFile(options2.storageState, "utf8")) : options2.storageState;
const context2 = APIRequestContext2.from((await this._playwright._channel.newRequest({
...options2,
extraHTTPHeaders: options2.extraHTTPHeaders ? headersObjectToArray(options2.extraHTTPHeaders) : void 0,
storageState: storageState2,
tracesDir: this._playwright._defaultLaunchOptions?.tracesDir,
// We do not expose tracesDir in the API, so do not allow options to accidentally override it.
clientCertificates: await toClientCertificatesProtocol(this._playwright._platform, options2.clientCertificates)
})).request);
this._contexts.add(context2);
context2._request = this;
context2._timeoutSettings.setDefaultTimeout(options2.timeout ?? this._playwright._defaultContextTimeout);
context2.tracing._tracesDir = this._playwright._defaultLaunchOptions?.tracesDir;
await context2._instrumentation.runAfterCreateRequestContext(context2);
return context2;
}
};
APIRequestContext2 = class extends ChannelOwner {
static from(channel) {
return channel._object;
}
constructor(parent, type3, guid, initializer) {
super(parent, type3, guid, initializer);
this.tracing = Tracing2.from(initializer.tracing);
this._timeoutSettings = new TimeoutSettings(this._platform);
}
async [Symbol.asyncDispose]() {
await this.dispose();
}
async dispose(options2 = {}) {
this._closeReason = options2.reason;
await this._instrumentation.runBeforeCloseRequestContext(this);
await this.tracing._exportAllHars();
try {
await this._channel.dispose(options2);
} catch (e) {
if (isTargetClosedError2(e))
return;
throw e;
}
this.tracing._resetStackCounter();
this._request?._contexts.delete(this);
}
async delete(url2, options2) {
return await this.fetch(url2, {
...options2,
method: "DELETE"
});
}
async head(url2, options2) {
return await this.fetch(url2, {
...options2,
method: "HEAD"
});
}
async get(url2, options2) {
return await this.fetch(url2, {
...options2,
method: "GET"
});
}
async patch(url2, options2) {
return await this.fetch(url2, {
...options2,
method: "PATCH"
});
}
async post(url2, options2) {
return await this.fetch(url2, {
...options2,
method: "POST"
});
}
async put(url2, options2) {
return await this.fetch(url2, {
...options2,
method: "PUT"
});
}
async fetch(urlOrRequest, options2 = {}) {
const url2 = isString(urlOrRequest) ? urlOrRequest : void 0;
const request2 = isString(urlOrRequest) ? void 0 : urlOrRequest;
return await this._innerFetch({ url: url2, request: request2, ...options2 });
}
async _innerFetch(options2 = {}) {
return await this._wrapApiCall(async () => {
if (this._closeReason)
throw new TargetClosedError2(this._closeReason);
assert(options2.request || typeof options2.url === "string", "First argument must be either URL string or Request");
assert((options2.data === void 0 ? 0 : 1) + (options2.form === void 0 ? 0 : 1) + (options2.multipart === void 0 ? 0 : 1) <= 1, `Only one of 'data', 'form' or 'multipart' can be specified`);
assert(options2.maxRedirects === void 0 || options2.maxRedirects >= 0, `'maxRedirects' must be greater than or equal to '0'`);
assert(options2.maxRetries === void 0 || options2.maxRetries >= 0, `'maxRetries' must be greater than or equal to '0'`);
const url2 = options2.url !== void 0 ? options2.url : options2.request.url();
const method = options2.method || options2.request?.method();
let encodedParams = void 0;
if (typeof options2.params === "string")
encodedParams = options2.params;
else if (options2.params instanceof URLSearchParams)
encodedParams = options2.params.toString();
const headersObj = options2.headers || options2.request?.headers();
const headers = headersObj ? headersObjectToArray(headersObj) : void 0;
let jsonData;
let formData;
let multipartData;
let postDataBuffer;
if (options2.data !== void 0) {
if (isString(options2.data)) {
if (isJsonContentType(headers))
jsonData = isJsonParsable(options2.data) ? options2.data : JSON.stringify(options2.data);
else
postDataBuffer = Buffer.from(options2.data, "utf8");
} else if (Buffer.isBuffer(options2.data)) {
postDataBuffer = options2.data;
} else if (typeof options2.data === "object" || typeof options2.data === "number" || typeof options2.data === "boolean") {
jsonData = JSON.stringify(options2.data);
} else {
throw new Error(`Unexpected 'data' type`);
}
} else if (options2.form) {
if (globalThis.FormData && options2.form instanceof FormData) {
formData = [];
for (const [name, value2] of options2.form.entries()) {
if (typeof value2 !== "string")
throw new Error(`Expected string for options.form["${name}"], found File. Please use options.multipart instead.`);
formData.push({ name, value: value2 });
}
} else {
formData = objectToArray(options2.form);
}
} else if (options2.multipart) {
multipartData = [];
if (globalThis.FormData && options2.multipart instanceof FormData) {
const form = options2.multipart;
for (const [name, value2] of form.entries()) {
if (isString(value2)) {
multipartData.push({ name, value: value2 });
} else {
const file = {
name: value2.name,
mimeType: value2.type,
buffer: Buffer.from(await value2.arrayBuffer())
};
multipartData.push({ name, file });
}
}
} else {
for (const [name, value2] of Object.entries(options2.multipart))
multipartData.push(await toFormField(this._platform, name, value2));
}
}
if (postDataBuffer === void 0 && jsonData === void 0 && formData === void 0 && multipartData === void 0)
postDataBuffer = options2.request?.postDataBuffer() || void 0;
const fixtures = {
__testHookLookup: options2.__testHookLookup
};
const result2 = await this._channel.fetch({
url: url2,
params: typeof options2.params === "object" ? objectToArray(options2.params) : void 0,
encodedParams,
method,
headers,
postData: postDataBuffer,
jsonData,
formData,
multipartData,
timeout: this._timeoutSettings.timeout(options2),
failOnStatusCode: options2.failOnStatusCode,
ignoreHTTPSErrors: options2.ignoreHTTPSErrors,
maxRedirects: options2.maxRedirects,
maxRetries: options2.maxRetries,
...fixtures
});
return new APIResponse(this, result2.response);
});
}
async storageState(options2 = {}) {
const state = await this._channel.storageState({ indexedDB: options2.indexedDB });
if (options2.path) {
await mkdirIfNeeded2(this._platform, options2.path);
await this._platform.fs().promises.writeFile(options2.path, JSON.stringify(state, void 0, 2), "utf8");
}
return state;
}
};
APIResponse = class {
constructor(context2, initializer) {
this._apiName = "APIResponse";
this._request = context2;
this._initializer = initializer;
this._headers = new RawHeaders(this._initializer.headers);
if (context2._platform.inspectCustom)
this[context2._platform.inspectCustom] = () => this._inspect();
}
ok() {
return this._initializer.status >= 200 && this._initializer.status <= 299;
}
url() {
return this._initializer.url;
}
status() {
return this._initializer.status;
}
statusText() {
return this._initializer.statusText;
}
headers() {
return this._headers.headers();
}
headersArray() {
return this._headers.headersArray();
}
async body() {
return await this._request._wrapApiCall(async () => {
try {
const result2 = await this._request._channel.fetchResponseBody({ fetchUid: this._fetchUid() });
if (result2.binary === void 0)
throw new Error("Response has been disposed");
return result2.binary;
} catch (e) {
if (isTargetClosedError2(e))
throw new Error("Response has been disposed");
throw e;
}
}, { internal: true });
}
async text() {
const content = await this.body();
return content.toString("utf8");
}
async json() {
const content = await this.text();
return JSON.parse(content);
}
async [Symbol.asyncDispose]() {
await this.dispose();
}
async dispose() {
await this._request._channel.disposeAPIResponse({ fetchUid: this._fetchUid() });
}
_inspect() {
const headers = this.headersArray().map(({ name, value: value2 }) => ` ${name}: ${value2}`);
return `APIResponse: ${this.status()} ${this.statusText()}
${headers.join("\n")}`;
}
_fetchUid() {
return this._initializer.fetchUid;
}
async _fetchLog() {
const { log: log2 } = await this._request._channel.fetchLog({ fetchUid: this._fetchUid() });
return log2;
}
};
}
});
// packages/playwright-core/src/client/network.ts
function validateHeaders(headers) {
for (const key of Object.keys(headers)) {
const value2 = headers[key];
if (!Object.is(value2, void 0) && !isString(value2))
throw new Error(`Expected value of header "${key}" to be String, but "${typeof value2}" is found.`);
}
}
var Request2, Route2, WebSocketRoute, WebSocketRouteHandler, Response3, WebSocket2, RouteHandler, RawHeaders;
var init_network3 = __esm({
"packages/playwright-core/src/client/network.ts"() {
"use strict";
init_assert();
init_headers();
init_urlMatch();
init_manualPromise();
init_multimap();
init_rtti();
init_stackTrace();
init_mimeType();
init_worker();
init_waiter();
init_frame();
init_fetch2();
init_events();
init_errors2();
init_channelOwner();
Request2 = class _Request extends ChannelOwner {
constructor(parent, type3, guid, initializer) {
super(parent, type3, guid, initializer);
this._redirectedFrom = null;
this._redirectedTo = null;
this._failureText = null;
this._response = null;
this._fallbackOverrides = {};
this._redirectedFrom = _Request.fromNullable(initializer.redirectedFrom);
if (this._redirectedFrom)
this._redirectedFrom._redirectedTo = this;
this._provisionalHeaders = new RawHeaders(initializer.headers);
this._timing = {
startTime: 0,
domainLookupStart: -1,
domainLookupEnd: -1,
connectStart: -1,
secureConnectionStart: -1,
connectEnd: -1,
requestStart: -1,
responseStart: -1,
responseEnd: -1
};
}
static from(request2) {
return request2._object;
}
static fromNullable(request2) {
return request2 ? _Request.from(request2) : null;
}
url() {
return this._fallbackOverrides.url || this._initializer.url;
}
resourceType() {
return this._initializer.resourceType;
}
method() {
return this._fallbackOverrides.method || this._initializer.method;
}
postData() {
return (this._fallbackOverrides.postDataBuffer || this._initializer.postData)?.toString("utf-8") || null;
}
postDataBuffer() {
return this._fallbackOverrides.postDataBuffer || this._initializer.postData || null;
}
postDataJSON() {
const postData = this.postData();
if (!postData)
return null;
const contentType = this.headers()["content-type"];
if (contentType?.includes("application/x-www-form-urlencoded")) {
const entries = {};
const parsed = new URLSearchParams(postData);
for (const [k, v] of parsed.entries())
entries[k] = v;
return entries;
}
try {
return JSON.parse(postData);
} catch (e) {
throw new Error("POST data is not a valid JSON object: " + postData);
}
}
/**
* @deprecated
*/
headers() {
if (this._fallbackOverrides.headers)
return RawHeaders._fromHeadersObjectLossy(this._fallbackOverrides.headers).headers();
return this._provisionalHeaders.headers();
}
async _actualHeaders() {
if (this._fallbackOverrides.headers)
return RawHeaders._fromHeadersObjectLossy(this._fallbackOverrides.headers);
if (!this._actualHeadersPromise) {
this._actualHeadersPromise = this._wrapApiCall(async () => {
return new RawHeaders((await this._channel.rawRequestHeaders()).headers);
}, { internal: true });
}
return await this._actualHeadersPromise;
}
async allHeaders() {
return (await this._actualHeaders()).headers();
}
async headersArray() {
return (await this._actualHeaders()).headersArray();
}
async headerValue(name) {
return (await this._actualHeaders()).get(name);
}
async response() {
return Response3.fromNullable((await this._channel.response()).response);
}
async _internalResponse() {
return Response3.fromNullable((await this._channel.response()).response);
}
existingResponse() {
return this._response;
}
frame() {
if (!this._initializer.frame) {
assert(this.serviceWorker());
throw new Error("Service Worker requests do not have an associated frame.");
}
const frame = Frame2.from(this._initializer.frame);
if (!frame._page) {
throw new Error([
"Frame for this navigation request is not available, because the request",
"was issued before the frame is created. You can check whether the request",
"is a navigation request by calling isNavigationRequest() method."
].join("\n"));
}
return frame;
}
_safePage() {
return Frame2.fromNullable(this._initializer.frame)?._page || null;
}
serviceWorker() {
return this._initializer.serviceWorker ? Worker2.from(this._initializer.serviceWorker) : null;
}
isNavigationRequest() {
return this._initializer.isNavigationRequest;
}
redirectedFrom() {
return this._redirectedFrom;
}
redirectedTo() {
return this._redirectedTo;
}
failure() {
if (this._failureText === null)
return null;
return {
errorText: this._failureText
};
}
timing() {
return this._timing;
}
async sizes() {
const response2 = await this.response();
if (!response2)
throw new Error("Unable to fetch sizes for failed request");
return (await response2._channel.sizes()).sizes;
}
_setResponseEndTiming(responseEndTiming) {
this._timing.responseEnd = responseEndTiming;
if (this._timing.responseStart === -1)
this._timing.responseStart = responseEndTiming;
}
_finalRequest() {
return this._redirectedTo ? this._redirectedTo._finalRequest() : this;
}
_applyFallbackOverrides(overrides) {
if (overrides.url)
this._fallbackOverrides.url = overrides.url;
if (overrides.method)
this._fallbackOverrides.method = overrides.method;
if (overrides.headers)
this._fallbackOverrides.headers = overrides.headers;
if (isString(overrides.postData))
this._fallbackOverrides.postDataBuffer = Buffer.from(overrides.postData, "utf-8");
else if (overrides.postData instanceof Buffer)
this._fallbackOverrides.postDataBuffer = overrides.postData;
else if (overrides.postData)
this._fallbackOverrides.postDataBuffer = Buffer.from(JSON.stringify(overrides.postData), "utf-8");
}
_fallbackOverridesForContinue() {
return this._fallbackOverrides;
}
_targetClosedScope() {
return this.serviceWorker()?._closedScope || this._safePage()?._closedOrCrashedScope || new LongStandingScope();
}
};
Route2 = class extends ChannelOwner {
constructor(parent, type3, guid, initializer) {
super(parent, type3, guid, initializer);
this._handlingPromise = null;
this._didThrow = false;
}
static from(route2) {
return route2._object;
}
request() {
return Request2.from(this._initializer.request);
}
async _raceWithTargetClose(promise) {
return await this.request()._targetClosedScope().safeRace(promise);
}
async _startHandling() {
this._handlingPromise = new ManualPromise();
return await this._handlingPromise;
}
async fallback(options2 = {}) {
this._checkNotHandled();
this.request()._applyFallbackOverrides(options2);
this._reportHandled(false);
}
async abort(errorCode) {
await this._handleRoute(async () => {
await this._raceWithTargetClose(this._channel.abort({ errorCode }));
});
}
async _redirectNavigationRequest(url2) {
await this._handleRoute(async () => {
await this._raceWithTargetClose(this._channel.redirectNavigationRequest({ url: url2 }));
});
}
async fetch(options2 = {}) {
return await this._wrapApiCall(async () => {
return await this._context.request._innerFetch({ request: this.request(), data: options2.postData, ...options2 });
});
}
async fulfill(options2 = {}) {
await this._handleRoute(async () => {
await this._innerFulfill(options2);
});
}
async _handleRoute(callback) {
this._checkNotHandled();
try {
await callback();
this._reportHandled(true);
} catch (e) {
this._didThrow = true;
throw e;
}
}
async _innerFulfill(options2 = {}) {
let fetchResponseUid;
let { status: statusOption, headers: headersOption, body } = options2;
if (options2.json !== void 0) {
assert(options2.body === void 0, "Can specify either body or json parameters");
body = JSON.stringify(options2.json);
}
if (options2.response instanceof APIResponse) {
statusOption ??= options2.response.status();
headersOption ??= options2.response.headers();
if (body === void 0 && options2.path === void 0) {
if (options2.response._request._connection === this._connection)
fetchResponseUid = options2.response._fetchUid();
else
body = await options2.response.body();
}
}
let isBase64 = false;
let length = 0;
if (options2.path) {
const buffer = await this._platform.fs().promises.readFile(options2.path);
body = buffer.toString("base64");
isBase64 = true;
length = buffer.length;
} else if (isString(body)) {
isBase64 = false;
length = Buffer.byteLength(body);
} else if (body) {
length = body.length;
body = body.toString("base64");
isBase64 = true;
}
const headers = {};
for (const header of Object.keys(headersOption || {}))
headers[header.toLowerCase()] = String(headersOption[header]);
if (options2.contentType)
headers["content-type"] = String(options2.contentType);
else if (options2.json)
headers["content-type"] = "application/json";
else if (options2.path)
headers["content-type"] = getMimeTypeForPath(options2.path) || "application/octet-stream";
if (length && !("content-length" in headers))
headers["content-length"] = String(length);
await this._raceWithTargetClose(this._channel.fulfill({
status: statusOption || 200,
headers: headersObjectToArray(headers),
body,
isBase64,
fetchResponseUid
}));
}
async continue(options2 = {}) {
await this._handleRoute(async () => {
this.request()._applyFallbackOverrides(options2);
await this._innerContinue(
false
/* isFallback */
);
});
}
_checkNotHandled() {
if (!this._handlingPromise)
throw new Error("Route is already handled!");
}
_reportHandled(done) {
const chain = this._handlingPromise;
this._handlingPromise = null;
chain.resolve(done);
}
async _innerContinue(isFallback) {
const options2 = this.request()._fallbackOverridesForContinue();
return await this._raceWithTargetClose(this._channel.continue({
url: options2.url,
method: options2.method,
headers: options2.headers ? headersObjectToArray(options2.headers) : void 0,
postData: options2.postDataBuffer,
isFallback
}));
}
};
WebSocketRoute = class extends ChannelOwner {
constructor(parent, type3, guid, initializer) {
super(parent, type3, guid, initializer);
this._connected = false;
this._server = {
onMessage: (handler) => {
this._onServerMessage = handler;
},
onClose: (handler) => {
this._onServerClose = handler;
},
connectToServer: () => {
throw new Error(`connectToServer must be called on the page-side WebSocketRoute`);
},
url: () => {
return this._initializer.url;
},
protocols: () => {
return [...this._initializer.protocols];
},
close: async (options2 = {}) => {
await this._channel.closeServer({ ...options2, wasClean: true }).catch(() => {
});
},
send: (message) => {
if (isString(message))
this._channel.sendToServer({ message, isBase64: false }).catch(() => {
});
else
this._channel.sendToServer({ message: message.toString("base64"), isBase64: true }).catch(() => {
});
},
async [Symbol.asyncDispose]() {
await this.close();
}
};
this._channel.on("messageFromPage", ({ message, isBase64 }) => {
if (this._onPageMessage)
this._onPageMessage(isBase64 ? Buffer.from(message, "base64") : message);
else if (this._connected)
this._channel.sendToServer({ message, isBase64 }).catch(() => {
});
});
this._channel.on("messageFromServer", ({ message, isBase64 }) => {
if (this._onServerMessage)
this._onServerMessage(isBase64 ? Buffer.from(message, "base64") : message);
else
this._channel.sendToPage({ message, isBase64 }).catch(() => {
});
});
this._channel.on("closePage", ({ code, reason, wasClean }) => {
if (this._onPageClose)
this._onPageClose(code, reason);
else
this._channel.closeServer({ code, reason, wasClean }).catch(() => {
});
});
this._channel.on("closeServer", ({ code, reason, wasClean }) => {
if (this._onServerClose)
this._onServerClose(code, reason);
else
this._channel.closePage({ code, reason, wasClean }).catch(() => {
});
});
}
static from(route2) {
return route2._object;
}
url() {
return this._initializer.url;
}
protocols() {
return [...this._initializer.protocols];
}
async close(options2 = {}) {
await this._channel.closePage({ ...options2, wasClean: true }).catch(() => {
});
}
connectToServer() {
if (this._connected)
throw new Error("Already connected to the server");
this._connected = true;
this._channel.connect().catch(() => {
});
return this._server;
}
send(message) {
if (isString(message))
this._channel.sendToPage({ message, isBase64: false }).catch(() => {
});
else
this._channel.sendToPage({ message: message.toString("base64"), isBase64: true }).catch(() => {
});
}
onMessage(handler) {
this._onPageMessage = handler;
}
onClose(handler) {
this._onPageClose = handler;
}
async [Symbol.asyncDispose]() {
await this.close();
}
async _afterHandle() {
if (this._connected)
return;
await this._channel.ensureOpened().catch(() => {
});
}
};
WebSocketRouteHandler = class {
constructor(baseURL, url2, handler) {
this._baseURL = baseURL;
this.url = url2;
this.handler = handler;
if (typeof url2 === "string")
resolveGlobToRegexPattern(baseURL, url2, true);
}
static prepareInterceptionPatterns(handlers) {
const patterns = [];
let all = false;
for (const handler of handlers) {
const serialized = serializeURLMatch(handler.url);
if (serialized)
patterns.push(serialized);
else
all = true;
}
if (all)
return [{ glob: "**/*" }];
return patterns;
}
matches(wsURL) {
return urlMatches(this._baseURL, wsURL, this.url, true);
}
async handle(webSocketRoute) {
const handler = this.handler;
await handler(webSocketRoute);
await webSocketRoute._afterHandle();
}
};
Response3 = class _Response extends ChannelOwner {
constructor(parent, type3, guid, initializer) {
super(parent, type3, guid, initializer);
this._finishedPromise = new ManualPromise();
this._provisionalHeaders = new RawHeaders(initializer.headers);
this._request = Request2.from(this._initializer.request);
this._request._response = this;
Object.assign(this._request._timing, this._initializer.timing);
}
static from(response2) {
return response2._object;
}
static fromNullable(response2) {
return response2 ? _Response.from(response2) : null;
}
url() {
return this._initializer.url;
}
ok() {
return this._initializer.status === 0 || this._initializer.status >= 200 && this._initializer.status <= 299;
}
status() {
return this._initializer.status;
}
statusText() {
return this._initializer.statusText;
}
fromServiceWorker() {
return this._initializer.fromServiceWorker;
}
/**
* @deprecated
*/
headers() {
return this._provisionalHeaders.headers();
}
async _actualHeaders() {
if (!this._actualHeadersPromise) {
this._actualHeadersPromise = (async () => {
return new RawHeaders((await this._channel.rawResponseHeaders()).headers);
})();
}
return await this._actualHeadersPromise;
}
async allHeaders() {
return (await this._actualHeaders()).headers();
}
async headersArray() {
return (await this._actualHeaders()).headersArray().slice();
}
async headerValue(name) {
return (await this._actualHeaders()).get(name);
}
async headerValues(name) {
return (await this._actualHeaders()).getAll(name);
}
async finished() {
return await this.request()._targetClosedScope().race(this._finishedPromise);
}
async body() {
return (await this._channel.body()).binary;
}
async text() {
const content = await this.body();
return content.toString("utf8");
}
async json() {
const content = await this.text();
return JSON.parse(content);
}
request() {
return this._request;
}
frame() {
return this._request.frame();
}
async serverAddr() {
return (await this._channel.serverAddr()).value || null;
}
async securityDetails() {
return (await this._channel.securityDetails()).value || null;
}
async httpVersion() {
return (await this._channel.httpVersion()).value;
}
};
WebSocket2 = class extends ChannelOwner {
static from(webSocket) {
return webSocket._object;
}
constructor(parent, type3, guid, initializer) {
super(parent, type3, guid, initializer);
this._isClosed = false;
this._page = parent;
this._channel.on("frameSent", (event) => {
if (event.opcode === 1)
this.emit(Events.WebSocket.FrameSent, { payload: event.data });
else if (event.opcode === 2)
this.emit(Events.WebSocket.FrameSent, { payload: Buffer.from(event.data, "base64") });
});
this._channel.on("frameReceived", (event) => {
if (event.opcode === 1)
this.emit(Events.WebSocket.FrameReceived, { payload: event.data });
else if (event.opcode === 2)
this.emit(Events.WebSocket.FrameReceived, { payload: Buffer.from(event.data, "base64") });
});
this._channel.on("socketError", ({ error }) => this.emit(Events.WebSocket.Error, error));
this._channel.on("close", () => {
this._isClosed = true;
this.emit(Events.WebSocket.Close, this);
});
}
url() {
return this._initializer.url;
}
isClosed() {
return this._isClosed;
}
async waitForEvent(event, optionsOrPredicate = {}) {
return await this._wrapApiCall(async () => {
const timeout = this._page._timeoutSettings.timeout(typeof optionsOrPredicate === "function" ? {} : optionsOrPredicate);
const predicate = typeof optionsOrPredicate === "function" ? optionsOrPredicate : optionsOrPredicate.predicate;
const waiter = Waiter.createForEvent(this, event);
waiter.rejectOnTimeout(timeout, `Timeout ${timeout}ms exceeded while waiting for event "${event}"`);
if (event !== Events.WebSocket.Error)
waiter.rejectOnEvent(this, Events.WebSocket.Error, new Error("Socket error"));
if (event !== Events.WebSocket.Close)
waiter.rejectOnEvent(this, Events.WebSocket.Close, new Error("Socket closed"));
waiter.rejectOnEvent(this._page, Events.Page.Close, () => this._page._closeErrorWithReason());
const result2 = await waiter.waitForEvent(this, event, predicate);
waiter.dispose();
return result2;
});
}
};
RouteHandler = class {
constructor(platform, baseURL, url2, handler, times = Number.MAX_SAFE_INTEGER) {
this.handledCount = 0;
this._ignoreException = false;
this._activeInvocations = /* @__PURE__ */ new Set();
this._baseURL = baseURL;
this._times = times;
this.url = url2;
this.handler = handler;
this._savedZone = platform.zones.current().pop();
if (typeof url2 === "string")
resolveGlobToRegexPattern(baseURL, url2);
}
static prepareInterceptionPatterns(handlers) {
const patterns = [];
let all = false;
for (const handler of handlers) {
const serialized = serializeURLMatch(handler.url);
if (serialized)
patterns.push(serialized);
else
all = true;
}
if (all)
return [{ glob: "**/*" }];
return patterns;
}
matches(requestURL) {
return urlMatches(this._baseURL, requestURL, this.url);
}
async handle(route2) {
return await this._savedZone.run(async () => this._handleImpl(route2));
}
async _handleImpl(route2) {
const handlerInvocation = { complete: new ManualPromise(), route: route2 };
this._activeInvocations.add(handlerInvocation);
try {
return await this._handleInternal(route2);
} catch (e) {
if (this._ignoreException)
return false;
if (isTargetClosedError2(e)) {
rewriteErrorMessage(e, `"${e.message}" while running route callback.
Consider awaiting \`await page.unrouteAll({ behavior: 'ignoreErrors' })\`
before the end of the test to ignore remaining routes in flight.`);
}
throw e;
} finally {
handlerInvocation.complete.resolve();
this._activeInvocations.delete(handlerInvocation);
}
}
async stop(behavior) {
if (behavior === "ignoreErrors") {
this._ignoreException = true;
} else {
const promises = [];
for (const activation of this._activeInvocations) {
if (!activation.route._didThrow)
promises.push(activation.complete);
}
await Promise.all(promises);
}
}
async _handleInternal(route2) {
++this.handledCount;
const handledPromise = route2._startHandling();
const handler = this.handler;
const [handled] = await Promise.all([
handledPromise,
handler(route2, route2.request())
]);
return handled;
}
willExpire() {
return this.handledCount + 1 >= this._times;
}
};
RawHeaders = class _RawHeaders {
constructor(headers) {
this._headersMap = new MultiMap();
this._headersArray = headers;
for (const header of headers)
this._headersMap.set(header.name.toLowerCase(), header.value);
}
static _fromHeadersObjectLossy(headers) {
const headersArray = Object.entries(headers).map(([name, value2]) => ({
name,
value: value2
})).filter((header) => header.value !== void 0);
return new _RawHeaders(headersArray);
}
get(name) {
const values = this.getAll(name);
if (!values || !values.length)
return null;
return values.join(name.toLowerCase() === "set-cookie" ? "\n" : ", ");
}
getAll(name) {
return [...this._headersMap.get(name.toLowerCase())];
}
headers() {
const result2 = {};
for (const name of this._headersMap.keys())
result2[name] = this.get(name);
return result2;
}
headersArray() {
return this._headersArray;
}
};
}
});
// packages/playwright-core/src/client/types.ts
var kLifecycleEvents2;
var init_types2 = __esm({
"packages/playwright-core/src/client/types.ts"() {
"use strict";
kLifecycleEvents2 = /* @__PURE__ */ new Set(["load", "domcontentloaded", "networkidle", "commit"]);
}
});
// packages/playwright-core/src/client/frame.ts
function verifyLoadState(name, waitUntil) {
if (waitUntil === "networkidle0")
waitUntil = "networkidle";
if (!kLifecycleEvents2.has(waitUntil))
throw new Error(`${name}: expected one of (load|domcontentloaded|networkidle|commit)`);
return waitUntil;
}
var Frame2;
var init_frame = __esm({
"packages/playwright-core/src/client/frame.ts"() {
"use strict";
init_assert();
init_locatorUtils();
init_urlMatch();
init_eventEmitter();
init_channelOwner();
init_clientHelper();
init_elementHandle();
init_events();
init_jsHandle();
init_locator();
init_network3();
init_types2();
init_waiter();
init_timeoutSettings();
Frame2 = class _Frame extends ChannelOwner {
constructor(parent, type3, guid, initializer) {
super(parent, type3, guid, initializer);
this._parentFrame = null;
this._url = "";
this._name = "";
this._detached = false;
this._childFrames = /* @__PURE__ */ new Set();
this._eventEmitter = new EventEmitter3(parent._platform);
this._eventEmitter.setMaxListeners(0);
this._parentFrame = _Frame.fromNullable(initializer.parentFrame);
if (this._parentFrame)
this._parentFrame._childFrames.add(this);
this._name = initializer.name;
this._url = initializer.url;
this._loadStates = new Set(initializer.loadStates);
this._channel.on("loadstate", (event) => {
if (event.add) {
this._loadStates.add(event.add);
this._eventEmitter.emit("loadstate", event.add);
}
if (event.remove)
this._loadStates.delete(event.remove);
if (!this._parentFrame && event.add === "load" && this._page) {
this._page.emit(Events.Page.Load, this._page);
this._page.context().emit(Events.BrowserContext.PageLoad, this._page);
}
if (!this._parentFrame && event.add === "domcontentloaded" && this._page)
this._page.emit(Events.Page.DOMContentLoaded, this._page);
});
this._channel.on("navigated", (event) => {
this._url = event.url;
this._name = event.name;
this._eventEmitter.emit("navigated", event);
if (!event.error && this._page) {
this._page.emit(Events.Page.FrameNavigated, this);
this._page.context().emit(Events.BrowserContext.FrameNavigated, this);
}
});
}
static from(frame) {
return frame._object;
}
static fromNullable(frame) {
return frame ? _Frame.from(frame) : null;
}
page() {
return this._page;
}
_timeout(options2) {
const timeoutSettings = this._page?._timeoutSettings || new TimeoutSettings(this._platform);
return timeoutSettings.timeout(options2 || {});
}
_navigationTimeout(options2) {
const timeoutSettings = this._page?._timeoutSettings || new TimeoutSettings(this._platform);
return timeoutSettings.navigationTimeout(options2 || {});
}
async goto(url2, options2 = {}) {
const waitUntil = verifyLoadState("waitUntil", options2.waitUntil === void 0 ? "load" : options2.waitUntil);
return Response3.fromNullable((await this._channel.goto({ url: url2, ...options2, waitUntil, timeout: this._navigationTimeout(options2) })).response);
}
_setupNavigationWaiter(options2) {
const waiter = new Waiter(this._page, "");
if (this._page.isClosed())
waiter.rejectImmediately(this._page._closeErrorWithReason());
waiter.rejectOnEvent(this._page, Events.Page.Close, () => this._page._closeErrorWithReason());
waiter.rejectOnEvent(this._page, Events.Page.Crash, new Error("Navigation failed because page crashed!"));
waiter.rejectOnEvent(this._page, Events.Page.FrameDetached, new Error("Navigating frame was detached!"), (frame) => frame === this);
const timeout = this._page._timeoutSettings.navigationTimeout(options2);
waiter.rejectOnTimeout(timeout, `Timeout ${timeout}ms exceeded.`);
return waiter;
}
async waitForNavigation(options2 = {}) {
return await this._page._wrapApiCall(async () => {
const waitUntil = verifyLoadState("waitUntil", options2.waitUntil === void 0 ? "load" : options2.waitUntil);
const waiter = this._setupNavigationWaiter(options2);
const toUrl = typeof options2.url === "string" ? ` to "${options2.url}"` : "";
waiter.log(`waiting for navigation${toUrl} until "${waitUntil}"`);
const navigatedEvent = await waiter.waitForEvent(this._eventEmitter, "navigated", (event) => {
if (event.error)
return true;
waiter.log(` navigated to "${event.url}"`);
return urlMatches(this._page?.context()._options.baseURL, event.url, options2.url);
});
if (navigatedEvent.error) {
const e = new Error(navigatedEvent.error);
e.stack = "";
await waiter.waitForPromise(Promise.reject(e));
}
if (!this._loadStates.has(waitUntil)) {
await waiter.waitForEvent(this._eventEmitter, "loadstate", (s) => {
waiter.log(` "${s}" event fired`);
return s === waitUntil;
});
}
const request2 = navigatedEvent.newDocument ? Request2.fromNullable(navigatedEvent.newDocument.request) : null;
const response2 = request2 ? await waiter.waitForPromise(request2._finalRequest()._internalResponse()) : null;
waiter.dispose();
return response2;
}, { title: "Wait for navigation" });
}
async waitForLoadState(state = "load", options2 = {}) {
state = verifyLoadState("state", state);
return await this._page._wrapApiCall(async () => {
const waiter = this._setupNavigationWaiter(options2);
if (this._loadStates.has(state)) {
waiter.log(` not waiting, "${state}" event already fired`);
} else {
await waiter.waitForEvent(this._eventEmitter, "loadstate", (s) => {
waiter.log(` "${s}" event fired`);
return s === state;
});
}
waiter.dispose();
}, { title: `Wait for load state "${state}"` });
}
async waitForURL(url2, options2 = {}) {
if (urlMatches(this._page?.context()._options.baseURL, this.url(), url2))
return await this.waitForLoadState(options2.waitUntil, options2);
await this.waitForNavigation({ url: url2, ...options2 });
}
async frameElement() {
return ElementHandle2.from((await this._channel.frameElement()).element);
}
async evaluateHandle(pageFunction, arg) {
assertMaxArguments(arguments.length, 2);
const result2 = await this._channel.evaluateExpressionHandle({ expression: String(pageFunction), isFunction: typeof pageFunction === "function", arg: serializeArgument(arg) });
return JSHandle2.from(result2.handle);
}
async evaluate(pageFunction, arg) {
assertMaxArguments(arguments.length, 2);
const result2 = await this._channel.evaluateExpression({ expression: String(pageFunction), isFunction: typeof pageFunction === "function", arg: serializeArgument(arg) });
return parseResult(result2.value);
}
async _evaluateExposeUtilityScript(pageFunction, arg) {
assertMaxArguments(arguments.length, 2);
const result2 = await this._channel.evaluateExpression({ expression: String(pageFunction), isFunction: typeof pageFunction === "function", arg: serializeArgument(arg) });
return parseResult(result2.value);
}
async $(selector, options2) {
const result2 = await this._channel.querySelector({ selector, ...options2 });
return ElementHandle2.fromNullable(result2.element);
}
async waitForSelector(selector, options2 = {}) {
if (options2.visibility)
throw new Error("options.visibility is not supported, did you mean options.state?");
if (options2.waitFor && options2.waitFor !== "visible")
throw new Error("options.waitFor is not supported, did you mean options.state?");
const result2 = await this._channel.waitForSelector({ selector, ...options2, timeout: this._timeout(options2) });
return ElementHandle2.fromNullable(result2.element);
}
async dispatchEvent(selector, type3, eventInit, options2 = {}) {
await this._channel.dispatchEvent({ selector, type: type3, eventInit: serializeArgument(eventInit), ...options2, timeout: this._timeout(options2) });
}
async $eval(selector, pageFunction, arg) {
assertMaxArguments(arguments.length, 3);
const result2 = await this._channel.evalOnSelector({ selector, expression: String(pageFunction), isFunction: typeof pageFunction === "function", arg: serializeArgument(arg) });
return parseResult(result2.value);
}
async $$eval(selector, pageFunction, arg) {
assertMaxArguments(arguments.length, 3);
const result2 = await this._channel.evalOnSelectorAll({ selector, expression: String(pageFunction), isFunction: typeof pageFunction === "function", arg: serializeArgument(arg) });
return parseResult(result2.value);
}
async $$(selector) {
const result2 = await this._channel.querySelectorAll({ selector });
return result2.elements.map((e) => ElementHandle2.from(e));
}
async _queryCount(selector, options2) {
return (await this._channel.queryCount({ selector, ...options2 })).value;
}
async content() {
return (await this._channel.content()).value;
}
async setContent(html, options2 = {}) {
const waitUntil = verifyLoadState("waitUntil", options2.waitUntil === void 0 ? "load" : options2.waitUntil);
await this._channel.setContent({ html, ...options2, waitUntil, timeout: this._navigationTimeout(options2) });
}
name() {
return this._name || "";
}
url() {
return this._url;
}
parentFrame() {
return this._parentFrame;
}
childFrames() {
return Array.from(this._childFrames);
}
isDetached() {
return this._detached;
}
async addScriptTag(options2 = {}) {
const copy = { ...options2 };
if (copy.path) {
copy.content = (await this._platform.fs().promises.readFile(copy.path)).toString();
copy.content = addSourceUrlToScript(copy.content, copy.path);
}
return ElementHandle2.from((await this._channel.addScriptTag({ ...copy })).element);
}
async addStyleTag(options2 = {}) {
const copy = { ...options2 };
if (copy.path) {
copy.content = (await this._platform.fs().promises.readFile(copy.path)).toString();
copy.content += "/*# sourceURL=" + copy.path.replace(/\n/g, "") + "*/";
}
return ElementHandle2.from((await this._channel.addStyleTag({ ...copy })).element);
}
async click(selector, options2 = {}) {
return await this._channel.click({ selector, ...options2, timeout: this._timeout(options2) });
}
async dblclick(selector, options2 = {}) {
return await this._channel.dblclick({ selector, ...options2, timeout: this._timeout(options2) });
}
async dragAndDrop(source8, target, options2 = {}) {
return await this._channel.dragAndDrop({ source: source8, target, ...options2, timeout: this._timeout(options2) });
}
async _drop(selector, payload, options2 = {}) {
let fileParams = {};
if (payload.files !== void 0) {
const converted = await convertInputFiles(this._platform, payload.files, this.page().context());
if (converted.localDirectory || converted.directoryStream)
throw new Error("Dropping a directory is not supported \u2014 pass individual files.");
fileParams = { payloads: converted.payloads, localPaths: converted.localPaths, streams: converted.streams };
}
const dataArray = payload.data ? Object.entries(payload.data).map(([mimeType, value2]) => ({ mimeType, value: value2 })) : void 0;
await this._channel.drop({
selector,
...fileParams,
data: dataArray,
...options2,
timeout: this._timeout(options2)
});
}
async tap(selector, options2 = {}) {
return await this._channel.tap({ selector, ...options2, timeout: this._timeout(options2) });
}
async fill(selector, value2, options2 = {}) {
return await this._channel.fill({ selector, value: value2, ...options2, timeout: this._timeout(options2) });
}
async _highlight(selector, style) {
return await this._channel.highlight({ selector, style });
}
async _hideHighlight(selector) {
return await this._channel.hideHighlight({ selector });
}
locator(selector, options2) {
return new Locator(this, selector, options2);
}
getByTestId(testId) {
return this.locator(getByTestIdSelector(testIdAttributeName(), testId));
}
getByAltText(text2, options2) {
return this.locator(getByAltTextSelector(text2, options2));
}
getByLabel(text2, options2) {
return this.locator(getByLabelSelector(text2, options2));
}
getByPlaceholder(text2, options2) {
return this.locator(getByPlaceholderSelector(text2, options2));
}
getByText(text2, options2) {
return this.locator(getByTextSelector(text2, options2));
}
getByTitle(text2, options2) {
return this.locator(getByTitleSelector(text2, options2));
}
getByRole(role, options2 = {}) {
return this.locator(getByRoleSelector(role, options2));
}
frameLocator(selector) {
return new FrameLocator(this, selector);
}
async focus(selector, options2 = {}) {
await this._channel.focus({ selector, ...options2, timeout: this._timeout(options2) });
}
async textContent(selector, options2 = {}) {
const value2 = (await this._channel.textContent({ selector, ...options2, timeout: this._timeout(options2) })).value;
return value2 === void 0 ? null : value2;
}
async innerText(selector, options2 = {}) {
return (await this._channel.innerText({ selector, ...options2, timeout: this._timeout(options2) })).value;
}
async innerHTML(selector, options2 = {}) {
return (await this._channel.innerHTML({ selector, ...options2, timeout: this._timeout(options2) })).value;
}
async getAttribute(selector, name, options2 = {}) {
const value2 = (await this._channel.getAttribute({ selector, name, ...options2, timeout: this._timeout(options2) })).value;
return value2 === void 0 ? null : value2;
}
async inputValue(selector, options2 = {}) {
return (await this._channel.inputValue({ selector, ...options2, timeout: this._timeout(options2) })).value;
}
async isChecked(selector, options2 = {}) {
return (await this._channel.isChecked({ selector, ...options2, timeout: this._timeout(options2) })).value;
}
async isDisabled(selector, options2 = {}) {
return (await this._channel.isDisabled({ selector, ...options2, timeout: this._timeout(options2) })).value;
}
async isEditable(selector, options2 = {}) {
return (await this._channel.isEditable({ selector, ...options2, timeout: this._timeout(options2) })).value;
}
async isEnabled(selector, options2 = {}) {
return (await this._channel.isEnabled({ selector, ...options2, timeout: this._timeout(options2) })).value;
}
async isHidden(selector, options2 = {}) {
return (await this._channel.isHidden({ selector, ...options2 })).value;
}
async isVisible(selector, options2 = {}) {
return (await this._channel.isVisible({ selector, ...options2 })).value;
}
async hover(selector, options2 = {}) {
await this._channel.hover({ selector, ...options2, timeout: this._timeout(options2) });
}
async selectOption(selector, values, options2 = {}) {
return (await this._channel.selectOption({ selector, ...convertSelectOptionValues(values), ...options2, timeout: this._timeout(options2) })).values;
}
async setInputFiles(selector, files, options2 = {}) {
const converted = await convertInputFiles(this._platform, files, this.page().context());
await this._channel.setInputFiles({ selector, ...converted, ...options2, timeout: this._timeout(options2) });
}
async type(selector, text2, options2 = {}) {
await this._channel.type({ selector, text: text2, ...options2, timeout: this._timeout(options2) });
}
async press(selector, key, options2 = {}) {
await this._channel.press({ selector, key, ...options2, timeout: this._timeout(options2) });
}
async check(selector, options2 = {}) {
await this._channel.check({ selector, ...options2, timeout: this._timeout(options2) });
}
async uncheck(selector, options2 = {}) {
await this._channel.uncheck({ selector, ...options2, timeout: this._timeout(options2) });
}
async setChecked(selector, checked, options2) {
if (checked)
await this.check(selector, options2);
else
await this.uncheck(selector, options2);
}
async waitForTimeout(timeout) {
await this._channel.waitForTimeout({ waitTimeout: timeout });
}
async waitForFunction(pageFunction, arg, options2 = {}) {
if (typeof options2.polling === "string")
assert(options2.polling === "raf", "Unknown polling option: " + options2.polling);
const result2 = await this._channel.waitForFunction({
...options2,
pollingInterval: options2.polling === "raf" ? void 0 : options2.polling,
expression: String(pageFunction),
isFunction: typeof pageFunction === "function",
arg: serializeArgument(arg),
timeout: this._timeout(options2)
});
return JSHandle2.from(result2.handle);
}
async title() {
return (await this._channel.title()).value;
}
async _expect(expression2, options2) {
const params2 = { expression: expression2, ...options2, isNot: !!options2.isNot };
params2.expectedValue = serializeArgument(options2.expectedValue);
const channelResult = await this._channel.expect(params2);
const result2 = {
matches: channelResult.matches,
log: channelResult.log,
timedOut: channelResult.timedOut,
errorMessage: channelResult.errorMessage
};
if (channelResult.received !== void 0) {
result2.received = {
value: channelResult.received.value !== void 0 ? parseResult(channelResult.received.value) : void 0,
ariaSnapshot: channelResult.received.ariaSnapshot
};
}
return result2;
}
};
}
});
// packages/playwright-core/src/client/writableStream.ts
var WritableStream;
var init_writableStream = __esm({
"packages/playwright-core/src/client/writableStream.ts"() {
"use strict";
init_channelOwner();
WritableStream = class extends ChannelOwner {
static from(Stream2) {
return Stream2._object;
}
constructor(parent, type3, guid, initializer) {
super(parent, type3, guid, initializer);
}
stream() {
return this._platform.streamWritable(this._channel);
}
};
}
});
// packages/playwright-core/src/client/elementHandle.ts
function convertSelectOptionValues(values) {
if (values === null)
return {};
if (!Array.isArray(values))
values = [values];
if (!values.length)
return {};
for (let i = 0; i < values.length; i++)
assert(values[i] !== null, `options[${i}]: expected object, got null`);
if (values[0] instanceof ElementHandle2)
return { elements: values.map((v) => v._elementChannel) };
if (isString(values[0]))
return { options: values.map((valueOrLabel) => ({ valueOrLabel })) };
return { options: values };
}
function filePayloadExceedsSizeLimit(payloads) {
return payloads.reduce((size, item) => size + (item.buffer ? item.buffer.byteLength : 0), 0) >= fileUploadSizeLimit2;
}
async function resolvePathsAndDirectoryForInputFiles(platform, items) {
let localPaths;
let localDirectory;
for (const item of items) {
const stat = await platform.fs().promises.stat(item);
if (stat.isDirectory()) {
if (localDirectory)
throw new Error("Multiple directories are not supported");
localDirectory = platform.path().resolve(item);
} else {
localPaths ??= [];
localPaths.push(platform.path().resolve(item));
}
}
if (localPaths?.length && localDirectory)
throw new Error("File paths must be all files or a single directory");
return [localPaths, localDirectory];
}
async function convertInputFiles(platform, files, context2) {
const items = Array.isArray(files) ? files.slice() : [files];
if (items.some((item) => typeof item === "string")) {
if (!items.every((item) => typeof item === "string"))
throw new Error("File paths cannot be mixed with buffers");
const [localPaths, localDirectory] = await resolvePathsAndDirectoryForInputFiles(platform, items);
if (context2._connection.isRemote()) {
const files2 = localDirectory ? (await platform.fs().promises.readdir(localDirectory, { withFileTypes: true, recursive: true })).filter((f) => f.isFile()).map((f) => platform.path().join(f.parentPath, f.name)) : localPaths;
const { writableStreams, rootDir } = await context2._wrapApiCall(async () => context2._channel.createTempFiles({
rootDirName: localDirectory ? platform.path().basename(localDirectory) : void 0,
items: await Promise.all(files2.map(async (file) => {
const lastModifiedMs = (await platform.fs().promises.stat(file)).mtimeMs;
return {
name: localDirectory ? platform.path().relative(localDirectory, file) : platform.path().basename(file),
lastModifiedMs
};
}))
}), { internal: true });
for (let i = 0; i < files2.length; i++) {
const writable = WritableStream.from(writableStreams[i]);
await platform.streamFile(files2[i], writable.stream());
}
return {
directoryStream: rootDir,
streams: localDirectory ? void 0 : writableStreams
};
}
return {
localPaths,
localDirectory
};
}
const payloads = items;
if (filePayloadExceedsSizeLimit(payloads))
throw new Error("Cannot set buffer larger than 50Mb, please write it to a file and pass its path instead.");
return { payloads };
}
function determineScreenshotType(options2) {
if (options2.path) {
const mimeType = getMimeTypeForPath(options2.path);
if (mimeType === "image/png")
return "png";
else if (mimeType === "image/jpeg")
return "jpeg";
throw new Error(`path: unsupported mime type "${mimeType}"`);
}
return options2.type;
}
var ElementHandle2;
var init_elementHandle = __esm({
"packages/playwright-core/src/client/elementHandle.ts"() {
"use strict";
init_assert();
init_rtti();
init_mimeType();
init_frame();
init_jsHandle();
init_fileUtils2();
init_writableStream();
ElementHandle2 = class _ElementHandle extends JSHandle2 {
static from(handle) {
return handle._object;
}
static fromNullable(handle) {
return handle ? _ElementHandle.from(handle) : null;
}
constructor(parent, type3, guid, initializer) {
super(parent, type3, guid, initializer);
this._frame = parent;
this._elementChannel = this._channel;
}
asElement() {
return this;
}
async ownerFrame() {
return Frame2.fromNullable((await this._elementChannel.ownerFrame()).frame);
}
async contentFrame() {
return Frame2.fromNullable((await this._elementChannel.contentFrame()).frame);
}
async getAttribute(name) {
const value2 = (await this._elementChannel.getAttribute({ name })).value;
return value2 === void 0 ? null : value2;
}
async inputValue() {
return (await this._elementChannel.inputValue()).value;
}
async textContent() {
const value2 = (await this._elementChannel.textContent()).value;
return value2 === void 0 ? null : value2;
}
async innerText() {
return (await this._elementChannel.innerText()).value;
}
async innerHTML() {
return (await this._elementChannel.innerHTML()).value;
}
async isChecked() {
return (await this._elementChannel.isChecked()).value;
}
async isDisabled() {
return (await this._elementChannel.isDisabled()).value;
}
async isEditable() {
return (await this._elementChannel.isEditable()).value;
}
async isEnabled() {
return (await this._elementChannel.isEnabled()).value;
}
async isHidden() {
return (await this._elementChannel.isHidden()).value;
}
async isVisible() {
return (await this._elementChannel.isVisible()).value;
}
async dispatchEvent(type3, eventInit = {}) {
await this._elementChannel.dispatchEvent({ type: type3, eventInit: serializeArgument(eventInit) });
}
async scrollIntoViewIfNeeded(options2 = {}) {
await this._elementChannel.scrollIntoViewIfNeeded({ ...options2, timeout: this._frame._timeout(options2) });
}
async hover(options2 = {}) {
await this._elementChannel.hover({ ...options2, timeout: this._frame._timeout(options2) });
}
async click(options2 = {}) {
return await this._elementChannel.click({ ...options2, timeout: this._frame._timeout(options2) });
}
async dblclick(options2 = {}) {
return await this._elementChannel.dblclick({ ...options2, timeout: this._frame._timeout(options2) });
}
async tap(options2 = {}) {
return await this._elementChannel.tap({ ...options2, timeout: this._frame._timeout(options2) });
}
async selectOption(values, options2 = {}) {
const result2 = await this._elementChannel.selectOption({ ...convertSelectOptionValues(values), ...options2, timeout: this._frame._timeout(options2) });
return result2.values;
}
async fill(value2, options2 = {}) {
return await this._elementChannel.fill({ value: value2, ...options2, timeout: this._frame._timeout(options2) });
}
async selectText(options2 = {}) {
await this._elementChannel.selectText({ ...options2, timeout: this._frame._timeout(options2) });
}
async setInputFiles(files, options2 = {}) {
const frame = await this.ownerFrame();
if (!frame)
throw new Error("Cannot set input files to detached element");
const converted = await convertInputFiles(this._platform, files, frame.page().context());
await this._elementChannel.setInputFiles({ ...converted, ...options2, timeout: this._frame._timeout(options2) });
}
async focus() {
await this._elementChannel.focus();
}
async type(text2, options2 = {}) {
await this._elementChannel.type({ text: text2, ...options2, timeout: this._frame._timeout(options2) });
}
async press(key, options2 = {}) {
await this._elementChannel.press({ key, ...options2, timeout: this._frame._timeout(options2) });
}
async check(options2 = {}) {
return await this._elementChannel.check({ ...options2, timeout: this._frame._timeout(options2) });
}
async uncheck(options2 = {}) {
return await this._elementChannel.uncheck({ ...options2, timeout: this._frame._timeout(options2) });
}
async setChecked(checked, options2) {
if (checked)
await this.check(options2);
else
await this.uncheck(options2);
}
async boundingBox() {
const value2 = (await this._elementChannel.boundingBox()).value;
return value2 === void 0 ? null : value2;
}
async screenshot(options2 = {}) {
const mask = options2.mask;
const copy = { ...options2, mask: void 0, timeout: this._frame._timeout(options2) };
if (!copy.type)
copy.type = determineScreenshotType(options2);
if (mask) {
copy.mask = mask.map((locator2) => ({
frame: locator2._frame._channel,
selector: locator2._selector
}));
}
const result2 = await this._elementChannel.screenshot(copy);
if (options2.path) {
await mkdirIfNeeded2(this._platform, options2.path);
await this._platform.fs().promises.writeFile(options2.path, result2.binary);
}
return result2.binary;
}
async $(selector) {
return _ElementHandle.fromNullable((await this._elementChannel.querySelector({ selector })).element);
}
async $$(selector) {
const result2 = await this._elementChannel.querySelectorAll({ selector });
return result2.elements.map((h) => _ElementHandle.from(h));
}
async $eval(selector, pageFunction, arg) {
const result2 = await this._elementChannel.evalOnSelector({ selector, expression: String(pageFunction), isFunction: typeof pageFunction === "function", arg: serializeArgument(arg) });
return parseResult(result2.value);
}
async $$eval(selector, pageFunction, arg) {
const result2 = await this._elementChannel.evalOnSelectorAll({ selector, expression: String(pageFunction), isFunction: typeof pageFunction === "function", arg: serializeArgument(arg) });
return parseResult(result2.value);
}
async waitForElementState(state, options2 = {}) {
return await this._elementChannel.waitForElementState({ state, ...options2, timeout: this._frame._timeout(options2) });
}
async waitForSelector(selector, options2 = {}) {
const result2 = await this._elementChannel.waitForSelector({ selector, ...options2, timeout: this._frame._timeout(options2) });
return _ElementHandle.fromNullable(result2.element);
}
};
}
});
// packages/playwright-core/src/client/fileChooser.ts
var FileChooser2;
var init_fileChooser2 = __esm({
"packages/playwright-core/src/client/fileChooser.ts"() {
"use strict";
FileChooser2 = class {
constructor(page, elementHandle, isMultiple) {
this._page = page;
this._elementHandle = elementHandle;
this._isMultiple = isMultiple;
}
element() {
return this._elementHandle;
}
isMultiple() {
return this._isMultiple;
}
page() {
return this._page;
}
async setFiles(files, options2) {
return await this._elementHandle.setInputFiles(files, options2);
}
};
}
});
// packages/playwright-core/src/client/harRouter.ts
var HarRouter;
var init_harRouter = __esm({
"packages/playwright-core/src/client/harRouter.ts"() {
"use strict";
HarRouter = class _HarRouter {
static async create(localUtils, file, notFoundAction, options2) {
const { harId, error } = await localUtils.harOpen({ file });
if (error)
throw new Error(error);
return new _HarRouter(localUtils, harId, notFoundAction, options2);
}
constructor(localUtils, harId, notFoundAction, options2) {
this._localUtils = localUtils;
this._harId = harId;
this._options = options2;
this._notFoundAction = notFoundAction;
}
async _handle(route2) {
const request2 = route2.request();
const response2 = await this._localUtils.harLookup({
harId: this._harId,
url: request2.url(),
method: request2.method(),
headers: await request2.headersArray(),
postData: request2.postDataBuffer() || void 0,
isNavigationRequest: request2.isNavigationRequest()
});
if (response2.action === "redirect") {
route2._platform.log("api", `HAR: ${route2.request().url()} redirected to ${response2.redirectURL}`);
await route2._redirectNavigationRequest(response2.redirectURL);
return;
}
if (response2.action === "fulfill") {
if (response2.status === -1)
return;
const transformedHeaders = response2.headers.reduce((headersMap, { name, value: value2 }) => {
if (name.toLowerCase() !== "set-cookie") {
headersMap[name] = value2;
} else {
if (!headersMap["set-cookie"])
headersMap["set-cookie"] = value2;
else
headersMap["set-cookie"] += `
${value2}`;
}
return headersMap;
}, {});
await route2.fulfill({
status: response2.status,
headers: transformedHeaders,
body: response2.body
});
return;
}
if (response2.action === "error")
route2._platform.log("api", "HAR: " + response2.message);
if (this._notFoundAction === "abort") {
await route2.abort();
return;
}
await route2.fallback();
}
async addContextRoute(context2) {
await context2.route(this._options.urlMatch || "**/*", (route2) => this._handle(route2));
}
async addPageRoute(page) {
await page.route(this._options.urlMatch || "**/*", (route2) => this._handle(route2));
}
async [Symbol.asyncDispose]() {
await this.dispose();
}
dispose() {
this._localUtils.harClose({ harId: this._harId }).catch(() => {
});
}
};
}
});
// packages/playwright-core/src/client/input.ts
var Keyboard2, Mouse2, Touchscreen2;
var init_input2 = __esm({
"packages/playwright-core/src/client/input.ts"() {
"use strict";
Keyboard2 = class {
constructor(page) {
this._page = page;
}
async down(key) {
await this._page._channel.keyboardDown({ key });
}
async up(key) {
await this._page._channel.keyboardUp({ key });
}
async insertText(text2) {
await this._page._channel.keyboardInsertText({ text: text2 });
}
async type(text2, options2 = {}) {
await this._page._channel.keyboardType({ text: text2, ...options2 });
}
async press(key, options2 = {}) {
await this._page._channel.keyboardPress({ key, ...options2 });
}
};
Mouse2 = class {
constructor(page) {
this._page = page;
}
async move(x, y, options2 = {}) {
await this._page._channel.mouseMove({ x, y, ...options2 });
}
async down(options2 = {}) {
await this._page._channel.mouseDown({ ...options2 });
}
async up(options2 = {}) {
await this._page._channel.mouseUp(options2);
}
async click(x, y, options2 = {}) {
await this._page._channel.mouseClick({ x, y, ...options2 });
}
async dblclick(x, y, options2 = {}) {
await this._page._wrapApiCall(async () => {
await this.click(x, y, { ...options2, clickCount: 2 });
}, { title: "Double click" });
}
async wheel(deltaX, deltaY) {
await this._page._channel.mouseWheel({ deltaX, deltaY });
}
};
Touchscreen2 = class {
constructor(page) {
this._page = page;
}
async tap(x, y) {
await this._page._channel.touchscreenTap({ x, y });
}
};
}
});
// packages/playwright-core/src/client/video.ts
var Video;
var init_video = __esm({
"packages/playwright-core/src/client/video.ts"() {
"use strict";
init_eventEmitter();
Video = class extends EventEmitter3 {
constructor(page, connection, artifact) {
super(page._platform);
this._isRemote = false;
this._isRemote = connection.isRemote();
this._artifact = artifact;
}
async path() {
if (this._isRemote)
throw new Error(`Path is not available when connecting remotely. Use saveAs() to save a local copy.`);
if (!this._artifact)
throw new Error("Video recording has not been started.");
return this._artifact._initializer.absolutePath;
}
async saveAs(path59) {
if (!this._artifact)
throw new Error("Video recording has not been started.");
return await this._artifact.saveAs(path59);
}
async delete() {
if (this._artifact)
await this._artifact.delete();
}
};
}
});
// packages/playwright-core/src/client/screencast.ts
var Screencast2;
var init_screencast2 = __esm({
"packages/playwright-core/src/client/screencast.ts"() {
"use strict";
init_artifact2();
init_disposable3();
Screencast2 = class {
constructor(page) {
this._started = false;
this._onFrame = null;
this._page = page;
this._page._channel.on("screencastFrame", ({ data, viewportWidth, viewportHeight }) => {
void this._onFrame?.({ data, viewportWidth, viewportHeight });
});
}
async start(options2 = {}) {
if (this._started)
throw new Error("Screencast is already started");
this._started = true;
if (options2.onFrame)
this._onFrame = options2.onFrame;
const result2 = await this._page._channel.screencastStart({
size: options2.size,
quality: options2.quality,
sendFrames: !!options2.onFrame,
record: !!options2.path
});
if (result2.artifact) {
this._artifact = Artifact2.from(result2.artifact);
this._savePath = options2.path;
}
return new DisposableStub(() => this.stop());
}
async stop() {
await this._page._wrapApiCall(async () => {
this._started = false;
this._onFrame = null;
await this._page._channel.screencastStop();
if (this._savePath)
await this._artifact?.saveAs(this._savePath);
this._artifact = void 0;
this._savePath = void 0;
});
}
async showActions(options2) {
await this._page._channel.screencastShowActions({ duration: options2?.duration, position: options2?.position, fontSize: options2?.fontSize });
return new DisposableStub(() => this._page._channel.screencastHideActions());
}
async hideActions() {
await this._page._channel.screencastHideActions();
}
async showOverlay(html, options2) {
const { id } = await this._page._channel.screencastShowOverlay({ html, duration: options2?.duration });
return new DisposableStub(() => this._page._channel.screencastRemoveOverlay({ id }));
}
async showChapter(title, options2) {
await this._page._channel.screencastChapter({ title, ...options2 });
}
async showOverlays() {
await this._page._channel.screencastSetOverlayVisible({ visible: true });
}
async hideOverlays() {
await this._page._channel.screencastSetOverlayVisible({ visible: false });
}
};
}
});
// packages/playwright-core/src/client/page.ts
function trimUrl(param) {
if (isRegExp(param))
return `/${trimStringWithEllipsis(param.source, 50)}/${param.flags}`;
if (isString(param))
return `"${trimStringWithEllipsis(param, 50)}"`;
}
var Page2, BindingCall;
var init_page2 = __esm({
"packages/playwright-core/src/client/page.ts"() {
"use strict";
init_assert();
init_headers();
init_stringUtils();
init_urlMatch();
init_manualPromise();
init_rtti();
init_artifact2();
init_channelOwner();
init_clientHelper();
init_coverage();
init_disposable3();
init_download2();
init_elementHandle();
init_errors2();
init_events();
init_fileChooser2();
init_frame();
init_harRouter();
init_input2();
init_jsHandle();
init_network3();
init_video();
init_screencast2();
init_waiter();
init_worker();
init_timeoutSettings();
init_fileUtils2();
init_consoleMessage();
Page2 = class _Page extends ChannelOwner {
constructor(parent, type3, guid, initializer) {
super(parent, type3, guid, initializer);
this._apiName = "Page";
this._frames = /* @__PURE__ */ new Set();
this._workers = /* @__PURE__ */ new Set();
this._closed = false;
this._closedOrCrashedScope = new LongStandingScope();
this._routes = [];
this._webSocketRoutes = [];
this._bindings = /* @__PURE__ */ new Map();
this._closeWasCalled = false;
this._harRouters = [];
this._locatorHandlers = /* @__PURE__ */ new Map();
this._instrumentation.onPage(this);
this._browserContext = parent;
this._timeoutSettings = new TimeoutSettings(this._platform, this._browserContext._timeoutSettings);
this.keyboard = new Keyboard2(this);
this.mouse = new Mouse2(this);
this.request = this._browserContext.request;
this.touchscreen = new Touchscreen2(this);
this.clock = this._browserContext.clock;
this._mainFrame = Frame2.from(initializer.mainFrame);
this._mainFrame._page = this;
this._frames.add(this._mainFrame);
this._viewportSize = initializer.viewportSize;
this._closed = initializer.isClosed;
this._opener = _Page.fromNullable(initializer.opener);
this._video = new Video(this, this._connection, initializer.video ? Artifact2.from(initializer.video) : void 0);
this.screencast = new Screencast2(this);
this._channel.on("bindingCall", ({ binding }) => this._onBinding(BindingCall.from(binding)));
this._channel.on("close", () => this._onClose());
this._channel.on("crash", () => this._onCrash());
this._channel.on("download", ({ url: url2, suggestedFilename, artifact }) => {
const artifactObject = Artifact2.from(artifact);
const download = new Download2(this, url2, suggestedFilename, artifactObject);
this.emit(Events.Page.Download, download);
this._browserContext.emit(Events.BrowserContext.Download, download);
});
this._channel.on("fileChooser", ({ element: element2, isMultiple }) => this.emit(Events.Page.FileChooser, new FileChooser2(this, ElementHandle2.from(element2), isMultiple)));
this._channel.on("frameAttached", ({ frame }) => this._onFrameAttached(Frame2.from(frame)));
this._channel.on("frameDetached", ({ frame }) => this._onFrameDetached(Frame2.from(frame)));
this._channel.on("locatorHandlerTriggered", ({ uid }) => this._onLocatorHandlerTriggered(uid));
this._channel.on("route", ({ route: route2 }) => this._onRoute(Route2.from(route2)));
this._channel.on("webSocketRoute", ({ webSocketRoute }) => this._onWebSocketRoute(WebSocketRoute.from(webSocketRoute)));
this._channel.on("viewportSizeChanged", ({ viewportSize }) => this._viewportSize = viewportSize);
this._channel.on("webSocket", ({ webSocket }) => this.emit(Events.Page.WebSocket, WebSocket2.from(webSocket)));
this._channel.on("worker", ({ worker }) => this._onWorker(Worker2.from(worker)));
this.coverage = new Coverage(this._channel);
this.once(Events.Page.Close, () => this._closedOrCrashedScope.close(this._closeErrorWithReason()));
this.once(Events.Page.Crash, () => this._closedOrCrashedScope.close(new TargetClosedError2()));
this._setEventToSubscriptionMapping(/* @__PURE__ */ new Map([
[Events.Page.Console, "console"],
[Events.Page.Dialog, "dialog"],
[Events.Page.Request, "request"],
[Events.Page.Response, "response"],
[Events.Page.RequestFinished, "requestFinished"],
[Events.Page.RequestFailed, "requestFailed"],
[Events.Page.FileChooser, "fileChooser"]
]));
}
static from(page) {
return page._object;
}
static fromNullable(page) {
return page ? _Page.from(page) : null;
}
_onFrameAttached(frame) {
frame._page = this;
this._frames.add(frame);
if (frame._parentFrame)
frame._parentFrame._childFrames.add(frame);
this.emit(Events.Page.FrameAttached, frame);
this._browserContext.emit(Events.BrowserContext.FrameAttached, frame);
}
_onFrameDetached(frame) {
this._frames.delete(frame);
frame._detached = true;
if (frame._parentFrame)
frame._parentFrame._childFrames.delete(frame);
this.emit(Events.Page.FrameDetached, frame);
this._browserContext.emit(Events.BrowserContext.FrameDetached, frame);
}
async _onRoute(route2) {
route2._context = this.context();
const routeHandlers = this._routes.slice();
for (const routeHandler of routeHandlers) {
if (this._closeWasCalled || this._browserContext.isClosed())
return;
if (!routeHandler.matches(route2.request().url()))
continue;
const index = this._routes.indexOf(routeHandler);
if (index === -1)
continue;
if (routeHandler.willExpire())
this._routes.splice(index, 1);
const handled = await routeHandler.handle(route2);
if (!this._routes.length)
this._updateInterceptionPatterns({ internal: true }).catch(() => {
});
if (handled)
return;
}
await this._browserContext._onRoute(route2);
}
async _onWebSocketRoute(webSocketRoute) {
const routeHandler = this._webSocketRoutes.find((route2) => route2.matches(webSocketRoute.url()));
if (routeHandler)
await routeHandler.handle(webSocketRoute);
else
await this._browserContext._onWebSocketRoute(webSocketRoute);
}
async _onBinding(bindingCall) {
const func = this._bindings.get(bindingCall._initializer.name);
if (func) {
await bindingCall.call(func);
return;
}
await this._browserContext._onBinding(bindingCall);
}
_onWorker(worker) {
this._workers.add(worker);
worker._page = this;
this.emit(Events.Page.Worker, worker);
}
_onClose() {
this._closed = true;
this._browserContext._pages.delete(this);
this._disposeHarRouters();
this.emit(Events.Page.Close, this);
this._browserContext.emit(Events.BrowserContext.PageClose, this);
}
_onCrash() {
this.emit(Events.Page.Crash, this);
}
context() {
return this._browserContext;
}
async opener() {
if (!this._opener || this._opener.isClosed())
return null;
return this._opener;
}
mainFrame() {
return this._mainFrame;
}
frame(frameSelector) {
const name = isString(frameSelector) ? frameSelector : frameSelector.name;
const url2 = isObject(frameSelector) ? frameSelector.url : void 0;
assert(name || url2, "Either name or url matcher should be specified");
return this.frames().find((f) => {
if (name)
return f.name() === name;
return urlMatches(this._browserContext._options.baseURL, f.url(), url2);
}) || null;
}
frames() {
return [...this._frames];
}
setDefaultNavigationTimeout(timeout) {
this._timeoutSettings.setDefaultNavigationTimeout(timeout);
}
setDefaultTimeout(timeout) {
this._timeoutSettings.setDefaultTimeout(timeout);
}
video() {
if (!this._browserContext._options.recordVideo)
return null;
return this._video;
}
async pickLocator() {
const { selector } = await this._channel.pickLocator({});
return this.locator(selector);
}
async cancelPickLocator() {
await this._channel.cancelPickLocator({});
}
async hideHighlight() {
await this._channel.hideHighlight({});
}
async $(selector, options2) {
return await this._mainFrame.$(selector, options2);
}
async waitForSelector(selector, options2) {
return await this._mainFrame.waitForSelector(selector, options2);
}
async dispatchEvent(selector, type3, eventInit, options2) {
return await this._mainFrame.dispatchEvent(selector, type3, eventInit, options2);
}
async evaluateHandle(pageFunction, arg) {
assertMaxArguments(arguments.length, 2);
return await this._mainFrame.evaluateHandle(pageFunction, arg);
}
async $eval(selector, pageFunction, arg) {
assertMaxArguments(arguments.length, 3);
return await this._mainFrame.$eval(selector, pageFunction, arg);
}
async $$eval(selector, pageFunction, arg) {
assertMaxArguments(arguments.length, 3);
return await this._mainFrame.$$eval(selector, pageFunction, arg);
}
async $$(selector) {
return await this._mainFrame.$$(selector);
}
async addScriptTag(options2 = {}) {
return await this._mainFrame.addScriptTag(options2);
}
async addStyleTag(options2 = {}) {
return await this._mainFrame.addStyleTag(options2);
}
async exposeFunction(name, callback) {
const result2 = await this._channel.exposeBinding({ name });
const binding = (source8, ...args) => callback(...args);
this._bindings.set(name, binding);
return DisposableObject2.from(result2.disposable);
}
async exposeBinding(name, callback) {
const result2 = await this._channel.exposeBinding({ name });
this._bindings.set(name, callback);
return DisposableObject2.from(result2.disposable);
}
async setExtraHTTPHeaders(headers) {
validateHeaders(headers);
await this._channel.setExtraHTTPHeaders({ headers: headersObjectToArray(headers) });
}
url() {
return this._mainFrame.url();
}
async content() {
return await this._mainFrame.content();
}
async setContent(html, options2) {
return await this._mainFrame.setContent(html, options2);
}
async goto(url2, options2) {
return await this._mainFrame.goto(url2, options2);
}
async reload(options2 = {}) {
const waitUntil = verifyLoadState("waitUntil", options2.waitUntil === void 0 ? "load" : options2.waitUntil);
return Response3.fromNullable((await this._channel.reload({ ...options2, waitUntil, timeout: this._timeoutSettings.navigationTimeout(options2) })).response);
}
async addLocatorHandler(locator2, handler, options2 = {}) {
if (locator2._frame !== this._mainFrame)
throw new Error(`Locator must belong to the main frame of this page`);
if (options2.times === 0)
return;
const { uid } = await this._channel.registerLocatorHandler({ selector: locator2._selector, noWaitAfter: options2.noWaitAfter });
this._locatorHandlers.set(uid, { locator: locator2, handler, times: options2.times });
}
async _onLocatorHandlerTriggered(uid) {
let remove = false;
try {
const handler = this._locatorHandlers.get(uid);
if (handler && handler.times !== 0) {
if (handler.times !== void 0)
handler.times--;
await handler.handler(handler.locator);
}
remove = handler?.times === 0;
} finally {
if (remove)
this._locatorHandlers.delete(uid);
this._channel.resolveLocatorHandlerNoReply({ uid, remove }).catch(() => {
});
}
}
async removeLocatorHandler(locator2) {
for (const [uid, data] of this._locatorHandlers) {
if (data.locator._equals(locator2)) {
this._locatorHandlers.delete(uid);
await this._channel.unregisterLocatorHandler({ uid }).catch(() => {
});
}
}
}
async waitForLoadState(state, options2) {
return await this._mainFrame.waitForLoadState(state, options2);
}
async waitForNavigation(options2) {
return await this._mainFrame.waitForNavigation(options2);
}
async waitForURL(url2, options2) {
return await this._mainFrame.waitForURL(url2, options2);
}
async waitForRequest(urlOrPredicate, options2 = {}) {
const predicate = async (request2) => {
if (isString(urlOrPredicate) || isRegExp(urlOrPredicate))
return urlMatches(this._browserContext._options.baseURL, request2.url(), urlOrPredicate);
return await urlOrPredicate(request2);
};
const trimmedUrl = trimUrl(urlOrPredicate);
const logLine = trimmedUrl ? `waiting for request ${trimmedUrl}` : void 0;
return await this._waitForEvent(Events.Page.Request, { predicate, timeout: options2.timeout }, logLine);
}
async waitForResponse(urlOrPredicate, options2 = {}) {
const predicate = async (response2) => {
if (isString(urlOrPredicate) || isRegExp(urlOrPredicate))
return urlMatches(this._browserContext._options.baseURL, response2.url(), urlOrPredicate);
return await urlOrPredicate(response2);
};
const trimmedUrl = trimUrl(urlOrPredicate);
const logLine = trimmedUrl ? `waiting for response ${trimmedUrl}` : void 0;
return await this._waitForEvent(Events.Page.Response, { predicate, timeout: options2.timeout }, logLine);
}
async waitForEvent(event, optionsOrPredicate = {}) {
return await this._waitForEvent(event, optionsOrPredicate, `waiting for event "${event}"`);
}
_closeErrorWithReason() {
return new TargetClosedError2(this._closeReason || this._browserContext._effectiveCloseReason());
}
async _waitForEvent(event, optionsOrPredicate, logLine) {
return await this._wrapApiCall(async () => {
const timeout = this._timeoutSettings.timeout(typeof optionsOrPredicate === "function" ? {} : optionsOrPredicate);
const predicate = typeof optionsOrPredicate === "function" ? optionsOrPredicate : optionsOrPredicate.predicate;
const waiter = Waiter.createForEvent(this, event);
if (logLine)
waiter.log(logLine);
waiter.rejectOnTimeout(timeout, `Timeout ${timeout}ms exceeded while waiting for event "${event}"`);
if (event !== Events.Page.Crash)
waiter.rejectOnEvent(this, Events.Page.Crash, new Error("Page crashed"));
if (event !== Events.Page.Close)
waiter.rejectOnEvent(this, Events.Page.Close, () => this._closeErrorWithReason());
const result2 = await waiter.waitForEvent(this, event, predicate);
waiter.dispose();
return result2;
});
}
async goBack(options2 = {}) {
const waitUntil = verifyLoadState("waitUntil", options2.waitUntil === void 0 ? "load" : options2.waitUntil);
return Response3.fromNullable((await this._channel.goBack({ ...options2, waitUntil, timeout: this._timeoutSettings.navigationTimeout(options2) })).response);
}
async goForward(options2 = {}) {
const waitUntil = verifyLoadState("waitUntil", options2.waitUntil === void 0 ? "load" : options2.waitUntil);
return Response3.fromNullable((await this._channel.goForward({ ...options2, waitUntil, timeout: this._timeoutSettings.navigationTimeout(options2) })).response);
}
async requestGC() {
await this._channel.requestGC();
}
async emulateMedia(options2 = {}) {
await this._channel.emulateMedia({
media: options2.media === null ? "no-override" : options2.media,
colorScheme: options2.colorScheme === null ? "no-override" : options2.colorScheme,
reducedMotion: options2.reducedMotion === null ? "no-override" : options2.reducedMotion,
forcedColors: options2.forcedColors === null ? "no-override" : options2.forcedColors,
contrast: options2.contrast === null ? "no-override" : options2.contrast
});
}
async setViewportSize(viewportSize) {
this._viewportSize = viewportSize;
await this._channel.setViewportSize({ viewportSize });
}
viewportSize() {
return this._viewportSize || null;
}
async evaluate(pageFunction, arg) {
assertMaxArguments(arguments.length, 2);
return await this._mainFrame.evaluate(pageFunction, arg);
}
async addInitScript(script, arg) {
const source8 = await evaluationScript(this._platform, script, arg);
return DisposableObject2.from((await this._channel.addInitScript({ source: source8 })).disposable);
}
async route(url2, handler, options2 = {}) {
this._routes.unshift(new RouteHandler(this._platform, this._browserContext._options.baseURL, url2, handler, options2.times));
await this._updateInterceptionPatterns({ title: "Route requests" });
return new DisposableStub(() => this.unroute(url2, handler));
}
async routeFromHAR(har, options2 = {}) {
const localUtils = this._connection.localUtils();
if (!localUtils)
throw new Error("Route from har is not supported in thin clients");
if (options2.update) {
await this._browserContext.tracing._recordIntoHAR(har, this, options2);
return;
}
const harRouter = await HarRouter.create(localUtils, har, options2.notFound || "abort", { urlMatch: options2.url });
this._harRouters.push(harRouter);
await harRouter.addPageRoute(this);
}
async routeWebSocket(url2, handler) {
this._webSocketRoutes.unshift(new WebSocketRouteHandler(this._browserContext._options.baseURL, url2, handler));
await this._updateWebSocketInterceptionPatterns({ title: "Route WebSockets" });
}
_disposeHarRouters() {
this._harRouters.forEach((router) => router.dispose());
this._harRouters = [];
}
async unrouteAll(options2) {
await this._unrouteInternal(this._routes, [], options2?.behavior);
this._disposeHarRouters();
}
async unroute(url2, handler) {
const removed = [];
const remaining = [];
for (const route2 of this._routes) {
if (urlMatchesEqual(route2.url, url2) && (!handler || route2.handler === handler))
removed.push(route2);
else
remaining.push(route2);
}
await this._unrouteInternal(removed, remaining, "default");
}
async _unrouteInternal(removed, remaining, behavior) {
this._routes = remaining;
if (behavior && behavior !== "default") {
const promises = removed.map((routeHandler) => routeHandler.stop(behavior));
await Promise.all(promises);
}
await this._updateInterceptionPatterns({ title: "Unroute requests" });
}
async _updateInterceptionPatterns(options2) {
const patterns = RouteHandler.prepareInterceptionPatterns(this._routes);
await this._wrapApiCall(() => this._channel.setNetworkInterceptionPatterns({ patterns }), options2);
}
async _updateWebSocketInterceptionPatterns(options2) {
const patterns = WebSocketRouteHandler.prepareInterceptionPatterns(this._webSocketRoutes);
await this._wrapApiCall(() => this._channel.setWebSocketInterceptionPatterns({ patterns }), options2);
}
async screenshot(options2 = {}) {
const mask = options2.mask;
const copy = { ...options2, mask: void 0, timeout: this._timeoutSettings.timeout(options2) };
if (!copy.type)
copy.type = determineScreenshotType(options2);
if (mask) {
copy.mask = mask.map((locator2) => ({
frame: locator2._frame._channel,
selector: locator2._selector
}));
}
const result2 = await this._channel.screenshot(copy);
if (options2.path) {
await mkdirIfNeeded2(this._platform, options2.path);
await this._platform.fs().promises.writeFile(options2.path, result2.binary);
}
return result2.binary;
}
async _expectScreenshot(options2) {
const mask = options2?.mask ? options2?.mask.map((locator3) => ({
frame: locator3._frame._channel,
selector: locator3._selector
})) : void 0;
const locator2 = options2.locator ? {
frame: options2.locator._frame._channel,
selector: options2.locator._selector
} : void 0;
return await this._channel.expectScreenshot({
...options2,
isNot: !!options2.isNot,
locator: locator2,
mask
});
}
async title() {
return await this._mainFrame.title();
}
async bringToFront() {
await this._channel.bringToFront();
}
async [Symbol.asyncDispose]() {
await this.close();
}
async close(options2 = {}) {
this._closeReason = options2.reason;
if (!options2.runBeforeUnload)
this._closeWasCalled = true;
try {
if (this._ownedContext)
await this._ownedContext.close();
else
await this._channel.close(options2);
} catch (e) {
if (isTargetClosedError2(e) && !options2.runBeforeUnload)
return;
throw e;
}
}
isClosed() {
return this._closed;
}
async click(selector, options2) {
return await this._mainFrame.click(selector, options2);
}
async dragAndDrop(source8, target, options2) {
return await this._mainFrame.dragAndDrop(source8, target, options2);
}
async dblclick(selector, options2) {
await this._mainFrame.dblclick(selector, options2);
}
async tap(selector, options2) {
return await this._mainFrame.tap(selector, options2);
}
async fill(selector, value2, options2) {
return await this._mainFrame.fill(selector, value2, options2);
}
async clearConsoleMessages() {
await this._channel.clearConsoleMessages();
}
async consoleMessages(options2) {
const { messages } = await this._channel.consoleMessages({ filter: options2?.filter });
return messages.map((message) => new ConsoleMessage2(this._platform, message, this, null));
}
async clearPageErrors() {
await this._channel.clearPageErrors();
}
async pageErrors(options2) {
const { errors } = await this._channel.pageErrors({ filter: options2?.filter });
return errors.map((error) => parseError2(error));
}
locator(selector, options2) {
return this.mainFrame().locator(selector, options2);
}
getByTestId(testId) {
return this.mainFrame().getByTestId(testId);
}
getByAltText(text2, options2) {
return this.mainFrame().getByAltText(text2, options2);
}
getByLabel(text2, options2) {
return this.mainFrame().getByLabel(text2, options2);
}
getByPlaceholder(text2, options2) {
return this.mainFrame().getByPlaceholder(text2, options2);
}
getByText(text2, options2) {
return this.mainFrame().getByText(text2, options2);
}
getByTitle(text2, options2) {
return this.mainFrame().getByTitle(text2, options2);
}
getByRole(role, options2 = {}) {
return this.mainFrame().getByRole(role, options2);
}
frameLocator(selector) {
return this.mainFrame().frameLocator(selector);
}
async focus(selector, options2) {
return await this._mainFrame.focus(selector, options2);
}
async textContent(selector, options2) {
return await this._mainFrame.textContent(selector, options2);
}
async innerText(selector, options2) {
return await this._mainFrame.innerText(selector, options2);
}
async innerHTML(selector, options2) {
return await this._mainFrame.innerHTML(selector, options2);
}
async getAttribute(selector, name, options2) {
return await this._mainFrame.getAttribute(selector, name, options2);
}
async inputValue(selector, options2) {
return await this._mainFrame.inputValue(selector, options2);
}
async isChecked(selector, options2) {
return await this._mainFrame.isChecked(selector, options2);
}
async isDisabled(selector, options2) {
return await this._mainFrame.isDisabled(selector, options2);
}
async isEditable(selector, options2) {
return await this._mainFrame.isEditable(selector, options2);
}
async isEnabled(selector, options2) {
return await this._mainFrame.isEnabled(selector, options2);
}
async isHidden(selector, options2) {
return await this._mainFrame.isHidden(selector, options2);
}
async isVisible(selector, options2) {
return await this._mainFrame.isVisible(selector, options2);
}
async hover(selector, options2) {
return await this._mainFrame.hover(selector, options2);
}
async selectOption(selector, values, options2) {
return await this._mainFrame.selectOption(selector, values, options2);
}
async setInputFiles(selector, files, options2) {
return await this._mainFrame.setInputFiles(selector, files, options2);
}
async type(selector, text2, options2) {
return await this._mainFrame.type(selector, text2, options2);
}
async press(selector, key, options2) {
return await this._mainFrame.press(selector, key, options2);
}
async check(selector, options2) {
return await this._mainFrame.check(selector, options2);
}
async uncheck(selector, options2) {
return await this._mainFrame.uncheck(selector, options2);
}
async setChecked(selector, checked, options2) {
return await this._mainFrame.setChecked(selector, checked, options2);
}
async waitForTimeout(timeout) {
return await this._mainFrame.waitForTimeout(timeout);
}
async waitForFunction(pageFunction, arg, options2) {
return await this._mainFrame.waitForFunction(pageFunction, arg, options2);
}
async requests() {
const { requests: requests2 } = await this._channel.requests();
return requests2.map((request2) => Request2.from(request2));
}
workers() {
return [...this._workers];
}
async pause(_options) {
if (this._platform.isJSDebuggerAttached())
return;
const defaultNavigationTimeout = this._browserContext._timeoutSettings.defaultNavigationTimeout();
const defaultTimeout = this._browserContext._timeoutSettings.defaultTimeout();
this._browserContext.setDefaultNavigationTimeout(0);
this._browserContext.setDefaultTimeout(0);
this._instrumentation?.onWillPause({ keepTestTimeout: !!_options?.__testHookKeepTestTimeout });
await this._closedOrCrashedScope.safeRace(this.context()._channel.pause());
this._browserContext.setDefaultNavigationTimeout(defaultNavigationTimeout);
this._browserContext.setDefaultTimeout(defaultTimeout);
}
async pdf(options2 = {}) {
const transportOptions = { ...options2 };
if (transportOptions.margin)
transportOptions.margin = { ...transportOptions.margin };
if (typeof options2.width === "number")
transportOptions.width = options2.width + "px";
if (typeof options2.height === "number")
transportOptions.height = options2.height + "px";
for (const margin of ["top", "right", "bottom", "left"]) {
const index = margin;
if (options2.margin && typeof options2.margin[index] === "number")
transportOptions.margin[index] = transportOptions.margin[index] + "px";
}
const result2 = await this._channel.pdf(transportOptions);
if (options2.path) {
const platform = this._platform;
await platform.fs().promises.mkdir(platform.path().dirname(options2.path), { recursive: true });
await platform.fs().promises.writeFile(options2.path, result2.pdf);
}
return result2.pdf;
}
async ariaSnapshot(options2 = {}) {
const result2 = await this.mainFrame()._channel.ariaSnapshot({ timeout: this._timeoutSettings.timeout(options2), track: options2._track, mode: options2.mode, depth: options2.depth, boxes: options2.boxes });
return result2.snapshot;
}
async _setDockTile(image) {
await this._channel.setDockTile({ image });
}
};
BindingCall = class extends ChannelOwner {
static from(channel) {
return channel._object;
}
constructor(parent, type3, guid, initializer) {
super(parent, type3, guid, initializer);
}
async call(func) {
try {
const frame = Frame2.from(this._initializer.frame);
const source8 = {
context: frame._page.context(),
page: frame._page,
frame
};
const result2 = await func(source8, ...this._initializer.args.map(parseResult));
this._channel.resolve({ result: serializeArgument(result2) }).catch(() => {
});
} catch (e) {
this._channel.reject({ error: serializeError2(e) }).catch(() => {
});
}
}
};
}
});
// packages/playwright-core/src/client/dialog.ts
var Dialog2;
var init_dialog2 = __esm({
"packages/playwright-core/src/client/dialog.ts"() {
"use strict";
init_channelOwner();
init_page2();
init_errors2();
Dialog2 = class extends ChannelOwner {
static from(dialog) {
return dialog._object;
}
constructor(parent, type3, guid, initializer) {
super(parent, type3, guid, initializer);
this._page = Page2.fromNullable(initializer.page);
}
page() {
return this._page;
}
type() {
return this._initializer.type;
}
message() {
return this._initializer.message;
}
defaultValue() {
return this._initializer.defaultValue;
}
async accept(promptText) {
await this._channel.accept({ promptText });
}
async dismiss() {
try {
await this._channel.dismiss();
} catch (e) {
if (isTargetClosedError2(e))
return;
throw e;
}
}
};
}
});
// packages/playwright-core/src/client/webError.ts
var WebError;
var init_webError = __esm({
"packages/playwright-core/src/client/webError.ts"() {
"use strict";
WebError = class {
constructor(page, error, location2) {
this._page = page;
this._error = error;
this._location = location2;
}
page() {
return this._page;
}
error() {
return this._error;
}
location() {
return this._location;
}
};
}
});
// packages/playwright-core/src/client/browserContext.ts
async function prepareStorageState(platform, storageState2) {
if (typeof storageState2 !== "string")
return storageState2;
try {
return JSON.parse(await platform.fs().promises.readFile(storageState2, "utf8"));
} catch (e) {
rewriteErrorMessage(e, `Error reading storage state from ${storageState2}:
` + e.message);
throw e;
}
}
async function prepareBrowserContextParams(platform, options2) {
if (options2.extraHTTPHeaders)
validateHeaders(options2.extraHTTPHeaders);
const contextParams = {
...options2,
viewport: options2.viewport === null ? void 0 : options2.viewport,
noDefaultViewport: options2.viewport === null,
extraHTTPHeaders: options2.extraHTTPHeaders ? headersObjectToArray(options2.extraHTTPHeaders) : void 0,
storageState: options2.storageState ? await prepareStorageState(platform, options2.storageState) : void 0,
serviceWorkers: options2.serviceWorkers,
colorScheme: options2.colorScheme === null ? "no-override" : options2.colorScheme,
reducedMotion: options2.reducedMotion === null ? "no-override" : options2.reducedMotion,
forcedColors: options2.forcedColors === null ? "no-override" : options2.forcedColors,
contrast: options2.contrast === null ? "no-override" : options2.contrast,
acceptDownloads: toAcceptDownloadsProtocol(options2.acceptDownloads),
clientCertificates: await toClientCertificatesProtocol(platform, options2.clientCertificates)
};
if (contextParams.recordVideo && contextParams.recordVideo.dir)
contextParams.recordVideo.dir = platform.path().resolve(contextParams.recordVideo.dir);
return contextParams;
}
function toAcceptDownloadsProtocol(acceptDownloads) {
if (acceptDownloads === void 0)
return void 0;
if (acceptDownloads)
return "accept";
return "deny";
}
async function toClientCertificatesProtocol(platform, certs) {
if (!certs)
return void 0;
const bufferizeContent = async (value2, path59) => {
if (value2)
return value2;
if (path59)
return await platform.fs().promises.readFile(path59);
};
return await Promise.all(certs.map(async (cert) => ({
origin: cert.origin,
cert: await bufferizeContent(cert.cert, cert.certPath),
key: await bufferizeContent(cert.key, cert.keyPath),
pfx: await bufferizeContent(cert.pfx, cert.pfxPath),
passphrase: cert.passphrase
})));
}
var BrowserContext2;
var init_browserContext2 = __esm({
"packages/playwright-core/src/client/browserContext.ts"() {
"use strict";
init_headers();
init_urlMatch();
init_rtti();
init_stackTrace();
init_cdpSession();
init_channelOwner();
init_clientHelper();
init_clock2();
init_consoleMessage();
init_debugger2();
init_dialog2();
init_disposable3();
init_errors2();
init_events();
init_fetch2();
init_frame();
init_harRouter();
init_network3();
init_page2();
init_tracing2();
init_waiter();
init_webError();
init_worker();
init_timeoutSettings();
init_fileUtils2();
BrowserContext2 = class _BrowserContext extends ChannelOwner {
constructor(parent, type3, guid, initializer) {
super(parent, type3, guid, initializer);
this._pages = /* @__PURE__ */ new Set();
this._routes = [];
this._webSocketRoutes = [];
// Browser is null for browser contexts created outside of normal browser, e.g. android or electron.
this._browser = null;
this._bindings = /* @__PURE__ */ new Map();
this._forReuse = false;
this._serviceWorkers = /* @__PURE__ */ new Set();
this._closingStatus = "none";
this._harRouters = [];
this._options = initializer.options;
this._timeoutSettings = new TimeoutSettings(this._platform);
this.debugger = Debugger2.from(initializer.debugger);
this.tracing = Tracing2.from(initializer.tracing);
this.request = APIRequestContext2.from(initializer.requestContext);
this.request._timeoutSettings = this._timeoutSettings;
this.clock = new Clock2(this);
this._channel.on("bindingCall", ({ binding }) => this._onBinding(BindingCall.from(binding)));
this._channel.on("close", () => this._onClose());
this._channel.on("page", ({ page }) => this._onPage(Page2.from(page)));
this._channel.on("route", ({ route: route2 }) => this._onRoute(Route2.from(route2)));
this._channel.on("webSocketRoute", ({ webSocketRoute }) => this._onWebSocketRoute(WebSocketRoute.from(webSocketRoute)));
this._channel.on("serviceWorker", ({ worker }) => {
const serviceWorker = Worker2.from(worker);
serviceWorker._context = this;
this._serviceWorkers.add(serviceWorker);
this.emit(Events.BrowserContext.ServiceWorker, serviceWorker);
});
this._channel.on("console", (event) => {
const worker = Worker2.fromNullable(event.worker);
const page = Page2.fromNullable(event.page);
const consoleMessage = new ConsoleMessage2(this._platform, event, page, worker);
worker?.emit(Events.Worker.Console, consoleMessage);
page?.emit(Events.Page.Console, consoleMessage);
if (worker && this._serviceWorkers.has(worker)) {
const scope = this._serviceWorkerScope(worker);
for (const page2 of this._pages) {
if (scope && page2.url().startsWith(scope))
page2.emit(Events.Page.Console, consoleMessage);
}
}
this.emit(Events.BrowserContext.Console, consoleMessage);
});
this._channel.on("pageError", ({ error, page, location: location2 }) => {
const pageObject = Page2.from(page);
const parsedError = parseError2(error);
this.emit(Events.BrowserContext.WebError, new WebError(pageObject, parsedError, location2));
if (pageObject)
pageObject.emit(Events.Page.PageError, parsedError);
});
this._channel.on("dialog", ({ dialog }) => {
const dialogObject = Dialog2.from(dialog);
let hasListeners = this.emit(Events.BrowserContext.Dialog, dialogObject);
const page = dialogObject.page();
if (page)
hasListeners = page.emit(Events.Page.Dialog, dialogObject) || hasListeners;
if (!hasListeners) {
if (dialogObject.type() === "beforeunload")
dialog.accept({}).catch(() => {
});
else
dialog.dismiss().catch(() => {
});
}
});
this._channel.on("request", ({ request: request2, page }) => this._onRequest(Request2.from(request2), Page2.fromNullable(page)));
this._channel.on("requestFailed", ({ request: request2, failureText, responseEndTiming, page }) => this._onRequestFailed(Request2.from(request2), responseEndTiming, failureText, Page2.fromNullable(page)));
this._channel.on("requestFinished", (params2) => this._onRequestFinished(params2));
this._channel.on("response", ({ response: response2, page }) => this._onResponse(Response3.from(response2), Page2.fromNullable(page)));
this._channel.on("recorderEvent", ({ event, data, page, code }) => {
if (event === "actionAdded")
this._onRecorderEventSink?.actionAdded?.(Page2.from(page), data, code);
else if (event === "actionUpdated")
this._onRecorderEventSink?.actionUpdated?.(Page2.from(page), data, code);
else if (event === "signalAdded")
this._onRecorderEventSink?.signalAdded?.(Page2.from(page), data);
});
this._closedPromise = new Promise((f) => this.once(Events.BrowserContext.Close, f));
this._setEventToSubscriptionMapping(/* @__PURE__ */ new Map([
[Events.BrowserContext.Console, "console"],
[Events.BrowserContext.Dialog, "dialog"],
[Events.BrowserContext.Request, "request"],
[Events.BrowserContext.Response, "response"],
[Events.BrowserContext.RequestFinished, "requestFinished"],
[Events.BrowserContext.RequestFailed, "requestFailed"]
]));
}
static from(context2) {
return context2._object;
}
static fromNullable(context2) {
return context2 ? _BrowserContext.from(context2) : null;
}
async _initializeHarFromOptions(recordHar) {
if (!recordHar)
return;
const defaultContent = recordHar.path.endsWith(".zip") ? "attach" : "embed";
await this.tracing._recordIntoHAR(recordHar.path, null, {
url: recordHar.urlFilter,
updateContent: recordHar.content ?? (recordHar.omitContent ? "omit" : defaultContent),
updateMode: recordHar.mode ?? "full"
});
}
_onPage(page) {
this._pages.add(page);
this.emit(Events.BrowserContext.Page, page);
if (page._opener && !page._opener.isClosed())
page._opener.emit(Events.Page.Popup, page);
}
_onRequest(request2, page) {
this.emit(Events.BrowserContext.Request, request2);
if (page)
page.emit(Events.Page.Request, request2);
}
_onResponse(response2, page) {
this.emit(Events.BrowserContext.Response, response2);
if (page)
page.emit(Events.Page.Response, response2);
}
_onRequestFailed(request2, responseEndTiming, failureText, page) {
request2._failureText = failureText || null;
request2._setResponseEndTiming(responseEndTiming);
this.emit(Events.BrowserContext.RequestFailed, request2);
if (page)
page.emit(Events.Page.RequestFailed, request2);
}
_onRequestFinished(params2) {
const { responseEndTiming } = params2;
const request2 = Request2.from(params2.request);
const response2 = Response3.fromNullable(params2.response);
const page = Page2.fromNullable(params2.page);
request2._setResponseEndTiming(responseEndTiming);
this.emit(Events.BrowserContext.RequestFinished, request2);
if (page)
page.emit(Events.Page.RequestFinished, request2);
if (response2)
response2._finishedPromise.resolve(null);
}
async _onRoute(route2) {
route2._context = this;
const page = route2.request()._safePage();
const routeHandlers = this._routes.slice();
for (const routeHandler of routeHandlers) {
if (page?._closeWasCalled || this.isClosed())
return;
if (!routeHandler.matches(route2.request().url()))
continue;
const index = this._routes.indexOf(routeHandler);
if (index === -1)
continue;
if (routeHandler.willExpire())
this._routes.splice(index, 1);
const handled = await routeHandler.handle(route2);
if (!this._routes.length)
this._updateInterceptionPatterns({ internal: true }).catch(() => {
});
if (handled)
return;
}
await route2._innerContinue(
true
/* isFallback */
).catch(() => {
});
}
async _onWebSocketRoute(webSocketRoute) {
const routeHandler = this._webSocketRoutes.find((route2) => route2.matches(webSocketRoute.url()));
if (routeHandler)
await routeHandler.handle(webSocketRoute);
else
webSocketRoute.connectToServer();
}
async _onBinding(bindingCall) {
const func = this._bindings.get(bindingCall._initializer.name);
if (!func)
return;
await bindingCall.call(func);
}
_serviceWorkerScope(serviceWorker) {
try {
let url2 = new URL(".", serviceWorker.url()).href;
if (!url2.endsWith("/"))
url2 += "/";
return url2;
} catch {
return null;
}
}
setDefaultNavigationTimeout(timeout) {
this._timeoutSettings.setDefaultNavigationTimeout(timeout);
}
setDefaultTimeout(timeout) {
this._timeoutSettings.setDefaultTimeout(timeout);
}
browser() {
return this._browser;
}
pages() {
return [...this._pages];
}
isClosed() {
return this._closingStatus !== "none";
}
async newPage() {
if (this._ownerPage)
throw new Error("Please use browser.newContext()");
return Page2.from((await this._channel.newPage()).page);
}
async cookies(urls) {
if (!urls)
urls = [];
if (urls && typeof urls === "string")
urls = [urls];
return (await this._channel.cookies({ urls })).cookies;
}
async addCookies(cookies) {
await this._channel.addCookies({ cookies });
}
async clearCookies(options2 = {}) {
await this._channel.clearCookies({
name: isString(options2.name) ? options2.name : void 0,
nameRegexSource: isRegExp(options2.name) ? options2.name.source : void 0,
nameRegexFlags: isRegExp(options2.name) ? options2.name.flags : void 0,
domain: isString(options2.domain) ? options2.domain : void 0,
domainRegexSource: isRegExp(options2.domain) ? options2.domain.source : void 0,
domainRegexFlags: isRegExp(options2.domain) ? options2.domain.flags : void 0,
path: isString(options2.path) ? options2.path : void 0,
pathRegexSource: isRegExp(options2.path) ? options2.path.source : void 0,
pathRegexFlags: isRegExp(options2.path) ? options2.path.flags : void 0
});
}
async grantPermissions(permissions, options2) {
await this._channel.grantPermissions({ permissions, ...options2 });
}
async clearPermissions() {
await this._channel.clearPermissions();
}
async setGeolocation(geolocation) {
await this._channel.setGeolocation({ geolocation: geolocation || void 0 });
}
async setExtraHTTPHeaders(headers) {
validateHeaders(headers);
await this._channel.setExtraHTTPHeaders({ headers: headersObjectToArray(headers) });
}
async setOffline(offline) {
await this._channel.setOffline({ offline });
}
async setHTTPCredentials(httpCredentials) {
await this._channel.setHTTPCredentials({ httpCredentials: httpCredentials || void 0 });
}
async addInitScript(script, arg) {
const source8 = await evaluationScript(this._platform, script, arg);
return DisposableObject2.from((await this._channel.addInitScript({ source: source8 })).disposable);
}
async exposeBinding(name, callback) {
const result2 = await this._channel.exposeBinding({ name });
this._bindings.set(name, callback);
return DisposableObject2.from(result2.disposable);
}
async exposeFunction(name, callback) {
const result2 = await this._channel.exposeBinding({ name });
const binding = (source8, ...args) => callback(...args);
this._bindings.set(name, binding);
return DisposableObject2.from(result2.disposable);
}
async route(url2, handler, options2 = {}) {
this._routes.unshift(new RouteHandler(this._platform, this._options.baseURL, url2, handler, options2.times));
await this._updateInterceptionPatterns({ title: "Route requests" });
return new DisposableStub(() => this.unroute(url2, handler));
}
async routeWebSocket(url2, handler) {
this._webSocketRoutes.unshift(new WebSocketRouteHandler(this._options.baseURL, url2, handler));
await this._updateWebSocketInterceptionPatterns({ title: "Route WebSockets" });
}
async routeFromHAR(har, options2 = {}) {
const localUtils = this._connection.localUtils();
if (!localUtils)
throw new Error("Route from har is not supported in thin clients");
if (options2.update) {
await this.tracing._recordIntoHAR(har, null, options2);
return;
}
const harRouter = await HarRouter.create(localUtils, har, options2.notFound || "abort", { urlMatch: options2.url });
this._harRouters.push(harRouter);
await harRouter.addContextRoute(this);
}
_disposeHarRouters() {
this._harRouters.forEach((router) => router.dispose());
this._harRouters = [];
}
async unrouteAll(options2) {
await this._unrouteInternal(this._routes, [], options2?.behavior);
this._disposeHarRouters();
}
async unroute(url2, handler) {
const removed = [];
const remaining = [];
for (const route2 of this._routes) {
if (urlMatchesEqual(route2.url, url2) && (!handler || route2.handler === handler))
removed.push(route2);
else
remaining.push(route2);
}
await this._unrouteInternal(removed, remaining, "default");
}
async _unrouteInternal(removed, remaining, behavior) {
this._routes = remaining;
if (behavior && behavior !== "default") {
const promises = removed.map((routeHandler) => routeHandler.stop(behavior));
await Promise.all(promises);
}
await this._updateInterceptionPatterns({ title: "Unroute requests" });
}
async _updateInterceptionPatterns(options2) {
const patterns = RouteHandler.prepareInterceptionPatterns(this._routes);
await this._wrapApiCall(() => this._channel.setNetworkInterceptionPatterns({ patterns }), options2);
}
async _updateWebSocketInterceptionPatterns(options2) {
const patterns = WebSocketRouteHandler.prepareInterceptionPatterns(this._webSocketRoutes);
await this._wrapApiCall(() => this._channel.setWebSocketInterceptionPatterns({ patterns }), options2);
}
_effectiveCloseReason() {
return this._closeReason || this._browser?._closeReason;
}
async waitForEvent(event, optionsOrPredicate = {}) {
return await this._wrapApiCall(async () => {
const timeout = this._timeoutSettings.timeout(typeof optionsOrPredicate === "function" ? {} : optionsOrPredicate);
const predicate = typeof optionsOrPredicate === "function" ? optionsOrPredicate : optionsOrPredicate.predicate;
const waiter = Waiter.createForEvent(this, event);
waiter.rejectOnTimeout(timeout, `Timeout ${timeout}ms exceeded while waiting for event "${event}"`);
if (event !== Events.BrowserContext.Close)
waiter.rejectOnEvent(this, Events.BrowserContext.Close, () => new TargetClosedError2(this._effectiveCloseReason()));
const result2 = await waiter.waitForEvent(this, event, predicate);
waiter.dispose();
return result2;
});
}
async storageState(options2 = {}) {
const state = await this._channel.storageState({ indexedDB: options2.indexedDB });
if (options2.path) {
await mkdirIfNeeded2(this._platform, options2.path);
await this._platform.fs().promises.writeFile(options2.path, JSON.stringify(state, void 0, 2), "utf8");
}
return state;
}
async setStorageState(storageState2) {
const state = await prepareStorageState(this._platform, storageState2);
await this._channel.setStorageState({ storageState: state });
}
backgroundPages() {
return [];
}
serviceWorkers() {
return [...this._serviceWorkers];
}
async newCDPSession(page) {
if (!(page instanceof Page2) && !(page instanceof Frame2))
throw new Error("page: expected Page or Frame");
const result2 = await this._channel.newCDPSession(page instanceof Page2 ? { page: page._channel } : { frame: page._channel });
return CDPSession2.from(result2.session);
}
_onClose() {
this._closingStatus = "closed";
this._browser?._contexts.delete(this);
this._browser?._browserType._contexts.delete(this);
this._browser?._browserType._playwright.selectors._contextsForSelectors.delete(this);
this._disposeHarRouters();
this.tracing._resetStackCounter();
this.emit(Events.BrowserContext.Close, this);
}
async [Symbol.asyncDispose]() {
await this.close();
}
async close(options2 = {}) {
if (this.isClosed())
return;
this._closeReason = options2.reason;
this._closingStatus = "closing";
await this.request.dispose(options2);
await this._instrumentation.runBeforeCloseBrowserContext(this);
await this.tracing._exportAllHars();
await this._channel.close(options2);
await this._closedPromise;
}
async _enableRecorder(params2, eventSink) {
if (eventSink)
this._onRecorderEventSink = eventSink;
await this._channel.enableRecorder(params2);
}
async _disableRecorder() {
this._onRecorderEventSink = void 0;
await this._channel.disableRecorder();
}
async _exposeConsoleApi() {
await this._channel.exposeConsoleApi();
}
};
}
});
// packages/playwright-core/src/client/browser.ts
var Browser2;
var init_browser2 = __esm({
"packages/playwright-core/src/client/browser.ts"() {
"use strict";
init_artifact2();
init_browserContext2();
init_cdpSession();
init_channelOwner();
init_errors2();
init_events();
init_fileUtils2();
Browser2 = class extends ChannelOwner {
constructor(parent, type3, guid, initializer) {
super(parent, type3, guid, initializer);
this._contexts = /* @__PURE__ */ new Set();
this._isConnected = true;
this._shouldCloseConnectionOnClose = false;
this._options = {};
this._name = initializer.name;
this._browserName = initializer.browserName;
this._channel.on("context", ({ context: context2 }) => this._didCreateContext(BrowserContext2.from(context2)));
this._channel.on("close", () => this._didClose());
this._closedPromise = new Promise((f) => this.once(Events.Browser.Disconnected, f));
}
static from(browser) {
return browser._object;
}
browserType() {
return this._browserType;
}
async newContext(options2 = {}) {
return await this._innerNewContext(options2, false);
}
async _newContextForReuse(options2 = {}) {
return await this._innerNewContext(options2, true);
}
async _disconnectFromReusedContext(reason) {
const context2 = [...this._contexts].find((context3) => context3._forReuse);
if (!context2)
return;
await this._instrumentation.runBeforeCloseBrowserContext(context2);
for (const page of context2.pages())
page._onClose();
context2._onClose();
await this._channel.disconnectFromReusedContext({ reason });
}
async _innerNewContext(userOptions = {}, forReuse) {
const options2 = this._browserType._playwright.selectors._withSelectorOptions(userOptions);
await this._instrumentation.runBeforeCreateBrowserContext(options2);
const contextOptions = await prepareBrowserContextParams(this._platform, options2);
const response2 = forReuse ? await this._channel.newContextForReuse(contextOptions) : await this._channel.newContext(contextOptions);
const context2 = BrowserContext2.from(response2.context);
if (forReuse)
context2._forReuse = true;
if (options2.logger)
context2._logger = options2.logger;
await context2._initializeHarFromOptions(options2.recordHar);
await this._instrumentation.runAfterCreateBrowserContext(context2);
return context2;
}
_connectToBrowserType(browserType, browserOptions, logger) {
this._browserType = browserType;
this._options = browserOptions;
this._logger = logger;
for (const context2 of this._contexts)
this._setupBrowserContext(context2);
}
_didCreateContext(context2) {
context2._browser = this;
this._contexts.add(context2);
if (this._browserType)
this._setupBrowserContext(context2);
this.emit(Events.Browser.Context, context2);
}
_setupBrowserContext(context2) {
context2._logger = this._logger;
context2.tracing._tracesDir = this._options.tracesDir;
this._browserType._contexts.add(context2);
this._browserType._playwright.selectors._contextsForSelectors.add(context2);
context2.setDefaultTimeout(this._browserType._playwright._defaultContextTimeout);
context2.setDefaultNavigationTimeout(this._browserType._playwright._defaultContextNavigationTimeout);
}
contexts() {
return [...this._contexts];
}
version() {
return this._initializer.version;
}
async bind(title, options2 = {}) {
const { endpoint } = await this._channel.startServer({ title, ...options2 });
return { endpoint };
}
async unbind() {
await this._channel.stopServer();
}
async newPage(options2 = {}) {
return await this._wrapApiCall(async () => {
const context2 = await this.newContext(options2);
const page = await context2.newPage();
page._ownedContext = context2;
context2._ownerPage = page;
return page;
}, { title: "Create page" });
}
isConnected() {
return this._isConnected;
}
async newBrowserCDPSession() {
return CDPSession2.from((await this._channel.newBrowserCDPSession()).session);
}
async startTracing(page, options2 = {}) {
this._path = options2.path;
await this._channel.startTracing({ ...options2, page: page ? page._channel : void 0 });
}
async stopTracing() {
const artifact = Artifact2.from((await this._channel.stopTracing()).artifact);
const buffer = await artifact.readIntoBuffer();
await artifact.delete();
if (this._path) {
await mkdirIfNeeded2(this._platform, this._path);
await this._platform.fs().promises.writeFile(this._path, buffer);
this._path = void 0;
}
return buffer;
}
async [Symbol.asyncDispose]() {
await this.close();
}
async close(options2 = {}) {
this._closeReason = options2.reason;
try {
if (this._shouldCloseConnectionOnClose)
this._connection.close();
else
await this._channel.close(options2);
await this._closedPromise;
} catch (e) {
if (isTargetClosedError2(e))
return;
throw e;
}
}
_didClose() {
this._isConnected = false;
this.emit(Events.Browser.Disconnected, this);
}
};
}
});
// packages/playwright-core/src/client/connect.ts
async function connectToBrowser(playwright2, params2) {
const deadline = params2.timeout ? monotonicTime() + params2.timeout : 0;
const nameParam = params2.browserName ? { "x-playwright-browser": params2.browserName } : {};
const headers = { ...nameParam, ...params2.headers };
const connectParams = {
endpoint: params2.endpoint,
headers,
exposeNetwork: params2.exposeNetwork,
slowMo: params2.slowMo,
timeout: params2.timeout || 0
};
if (params2.__testHookRedirectPortForwarding)
connectParams.socksProxyRedirectPortForTest = params2.__testHookRedirectPortForwarding;
const connection = await connectToEndpoint(playwright2._connection, connectParams);
let browser;
connection.on("close", () => {
for (const context2 of browser?.contexts() || []) {
for (const page of context2.pages())
page._onClose();
context2._onClose();
}
setTimeout(() => browser?._didClose(), 0);
});
const result2 = await raceAgainstDeadline(async () => {
if (params2.__testHookBeforeCreateBrowser)
await params2.__testHookBeforeCreateBrowser();
const playwright3 = await connection.initializePlaywright();
if (!playwright3._initializer.preLaunchedBrowser) {
connection.close();
throw new Error("Malformed endpoint. Did you use BrowserType.launchServer method?");
}
playwright3.selectors = playwright3.selectors;
browser = Browser2.from(playwright3._initializer.preLaunchedBrowser);
browser._shouldCloseConnectionOnClose = true;
browser.on(Events.Browser.Disconnected, () => connection.close());
return browser;
}, deadline);
if (!result2.timedOut) {
return result2.result;
} else {
connection.close();
throw new Error(`Timeout ${params2.timeout}ms exceeded`);
}
}
async function connectToEndpoint(parentConnection, params2) {
const localUtils = parentConnection.localUtils();
const transport = localUtils ? new JsonPipeTransport(localUtils) : new WebSocketTransport2();
const connectHeaders = await transport.connect(params2);
const connection = new Connection(parentConnection._platform, localUtils, parentConnection._instrumentation, connectHeaders);
connection.markAsRemote();
connection.on("close", () => transport.close());
let closeError;
const onTransportClosed = (reason) => {
connection.close(reason || closeError);
};
transport.onClose((reason) => onTransportClosed(reason));
connection.onmessage = (message) => transport.send(message).catch(() => onTransportClosed());
transport.onMessage((message) => {
try {
connection.dispatch(message);
} catch (e) {
closeError = String(e);
transport.close().catch(() => {
});
}
});
return connection;
}
var JsonPipeTransport, WebSocketTransport2;
var init_connect = __esm({
"packages/playwright-core/src/client/connect.ts"() {
"use strict";
init_time();
init_timeoutRunner();
init_browser2();
init_connection();
init_events();
JsonPipeTransport = class {
constructor(owner) {
this._owner = owner;
}
async connect(params2) {
const { pipe, headers: connectHeaders } = await this._owner._channel.connect(params2);
this._pipe = pipe;
return connectHeaders;
}
async send(message) {
await this._pipe.send({ message });
}
onMessage(callback) {
this._pipe.on("message", ({ message }) => callback(message));
}
onClose(callback) {
this._pipe.on("closed", ({ reason }) => callback(reason));
}
async close() {
await this._pipe.close().catch(() => {
});
}
};
WebSocketTransport2 = class {
async connect(params2) {
this._ws = new window.WebSocket(params2.endpoint);
return [];
}
async send(message) {
this._ws.send(JSON.stringify(message));
}
onMessage(callback) {
this._ws.addEventListener("message", (event) => callback(JSON.parse(event.data)));
}
onClose(callback) {
this._ws.addEventListener("close", () => callback());
}
async close() {
this._ws.close();
}
};
}
});
// packages/playwright-core/src/client/android.ts
async function loadFile(platform, file) {
if (isString(file))
return await platform.fs().promises.readFile(file);
return file;
}
function toSelectorChannel(selector) {
const {
checkable,
checked,
clazz,
clickable,
depth,
desc,
enabled,
focusable,
focused,
hasChild,
hasDescendant,
longClickable,
pkg,
res,
scrollable,
selected,
text: text2
} = selector;
const toRegex = (value2) => {
if (value2 === void 0)
return void 0;
if (isRegExp(value2))
return value2.source;
return "^" + value2.replace(/[|\\{}()[\]^$+*?.]/g, "\\$&").replace(/-/g, "\\x2d") + "$";
};
return {
checkable,
checked,
clazz: toRegex(clazz),
pkg: toRegex(pkg),
desc: toRegex(desc),
res: toRegex(res),
text: toRegex(text2),
clickable,
depth,
enabled,
focusable,
focused,
hasChild: hasChild ? { androidSelector: toSelectorChannel(hasChild.selector) } : void 0,
hasDescendant: hasDescendant ? { androidSelector: toSelectorChannel(hasDescendant.selector), maxDepth: hasDescendant.maxDepth } : void 0,
longClickable,
scrollable,
selected
};
}
var Android2, AndroidDevice2, AndroidSocket, AndroidInput, AndroidWebView;
var init_android2 = __esm({
"packages/playwright-core/src/client/android.ts"() {
"use strict";
init_rtti();
init_time();
init_timeoutRunner();
init_eventEmitter();
init_browserContext2();
init_channelOwner();
init_errors2();
init_events();
init_waiter();
init_timeoutSettings();
init_connect();
Android2 = class extends ChannelOwner {
static from(android) {
return android._object;
}
constructor(parent, type3, guid, initializer) {
super(parent, type3, guid, initializer);
this._timeoutSettings = new TimeoutSettings(this._platform);
}
setDefaultTimeout(timeout) {
this._timeoutSettings.setDefaultTimeout(timeout);
}
async devices(options2 = {}) {
const { devices } = await this._channel.devices(options2);
return devices.map((d) => AndroidDevice2.from(d));
}
async launchServer(options2 = {}) {
if (!this._serverLauncher)
throw new Error("Launching server is not supported");
return await this._serverLauncher.launchServer(options2);
}
async connect(endpoint, options2 = {}) {
return await this._wrapApiCall(async () => {
const deadline = options2.timeout ? monotonicTime() + options2.timeout : 0;
const headers = { "x-playwright-browser": "android", ...options2.headers };
const connectParams = { endpoint, headers, slowMo: options2.slowMo, timeout: options2.timeout || 0 };
const connection = await connectToEndpoint(this._connection, connectParams);
let device;
connection.on("close", () => {
device?._didClose();
});
const result2 = await raceAgainstDeadline(async () => {
const playwright2 = await connection.initializePlaywright();
if (!playwright2._initializer.preConnectedAndroidDevice) {
connection.close();
throw new Error("Malformed endpoint. Did you use Android.launchServer method?");
}
device = AndroidDevice2.from(playwright2._initializer.preConnectedAndroidDevice);
device._shouldCloseConnectionOnClose = true;
device.on(Events.AndroidDevice.Close, () => connection.close());
return device;
}, deadline);
if (!result2.timedOut) {
return result2.result;
} else {
connection.close();
throw new Error(`Timeout ${options2.timeout}ms exceeded`);
}
});
}
};
AndroidDevice2 = class extends ChannelOwner {
constructor(parent, type3, guid, initializer) {
super(parent, type3, guid, initializer);
this._webViews = /* @__PURE__ */ new Map();
this._shouldCloseConnectionOnClose = false;
this._android = parent;
this.input = new AndroidInput(this);
this._timeoutSettings = new TimeoutSettings(this._platform, parent._timeoutSettings);
this._channel.on("webViewAdded", ({ webView }) => this._onWebViewAdded(webView));
this._channel.on("webViewRemoved", ({ socketName }) => this._onWebViewRemoved(socketName));
this._channel.on("close", () => this._didClose());
}
static from(androidDevice) {
return androidDevice._object;
}
_onWebViewAdded(webView) {
const view = new AndroidWebView(this, webView);
this._webViews.set(webView.socketName, view);
this.emit(Events.AndroidDevice.WebView, view);
}
_onWebViewRemoved(socketName) {
const view = this._webViews.get(socketName);
this._webViews.delete(socketName);
if (view)
view.emit(Events.AndroidWebView.Close);
}
setDefaultTimeout(timeout) {
this._timeoutSettings.setDefaultTimeout(timeout);
}
serial() {
return this._initializer.serial;
}
model() {
return this._initializer.model;
}
webViews() {
return [...this._webViews.values()];
}
async webView(selector, options2) {
const predicate = (v) => {
if (selector.pkg)
return v.pkg() === selector.pkg;
if (selector.socketName)
return v._socketName() === selector.socketName;
return false;
};
const webView = [...this._webViews.values()].find(predicate);
if (webView)
return webView;
return await this.waitForEvent("webview", { ...options2, predicate });
}
async wait(selector, options2 = {}) {
await this._channel.wait({ androidSelector: toSelectorChannel(selector), ...options2, timeout: this._timeoutSettings.timeout(options2) });
}
async fill(selector, text2, options2 = {}) {
await this._channel.fill({ androidSelector: toSelectorChannel(selector), text: text2, ...options2, timeout: this._timeoutSettings.timeout(options2) });
}
async press(selector, key, options2 = {}) {
await this.tap(selector, options2);
await this.input.press(key);
}
async tap(selector, options2 = {}) {
await this._channel.tap({ androidSelector: toSelectorChannel(selector), ...options2, timeout: this._timeoutSettings.timeout(options2) });
}
async drag(selector, dest, options2 = {}) {
await this._channel.drag({ androidSelector: toSelectorChannel(selector), dest, ...options2, timeout: this._timeoutSettings.timeout(options2) });
}
async fling(selector, direction, options2 = {}) {
await this._channel.fling({ androidSelector: toSelectorChannel(selector), direction, ...options2, timeout: this._timeoutSettings.timeout(options2) });
}
async longTap(selector, options2 = {}) {
await this._channel.longTap({ androidSelector: toSelectorChannel(selector), ...options2, timeout: this._timeoutSettings.timeout(options2) });
}
async pinchClose(selector, percent, options2 = {}) {
await this._channel.pinchClose({ androidSelector: toSelectorChannel(selector), percent, ...options2, timeout: this._timeoutSettings.timeout(options2) });
}
async pinchOpen(selector, percent, options2 = {}) {
await this._channel.pinchOpen({ androidSelector: toSelectorChannel(selector), percent, ...options2, timeout: this._timeoutSettings.timeout(options2) });
}
async scroll(selector, direction, percent, options2 = {}) {
await this._channel.scroll({ androidSelector: toSelectorChannel(selector), direction, percent, ...options2, timeout: this._timeoutSettings.timeout(options2) });
}
async swipe(selector, direction, percent, options2 = {}) {
await this._channel.swipe({ androidSelector: toSelectorChannel(selector), direction, percent, ...options2, timeout: this._timeoutSettings.timeout(options2) });
}
async info(selector) {
return (await this._channel.info({ androidSelector: toSelectorChannel(selector) })).info;
}
async screenshot(options2 = {}) {
const { binary } = await this._channel.screenshot();
if (options2.path)
await this._platform.fs().promises.writeFile(options2.path, binary);
return binary;
}
async [Symbol.asyncDispose]() {
await this.close();
}
async close() {
try {
if (this._shouldCloseConnectionOnClose)
this._connection.close();
else
await this._channel.close();
} catch (e) {
if (isTargetClosedError2(e))
return;
throw e;
}
}
_didClose() {
this.emit(Events.AndroidDevice.Close, this);
}
async shell(command) {
const { result: result2 } = await this._channel.shell({ command });
return result2;
}
async open(command) {
return AndroidSocket.from((await this._channel.open({ command })).socket);
}
async installApk(file, options2) {
await this._channel.installApk({ file: await loadFile(this._platform, file), args: options2 && options2.args });
}
async push(file, path59, options2) {
await this._channel.push({ file: await loadFile(this._platform, file), path: path59, mode: options2 ? options2.mode : void 0 });
}
async launchBrowser(options2 = {}) {
const contextOptions = await prepareBrowserContextParams(this._platform, options2);
const result2 = await this._channel.launchBrowser(contextOptions);
const context2 = BrowserContext2.from(result2.context);
const selectors = this._android._playwright.selectors;
selectors._contextsForSelectors.add(context2);
context2.once(Events.BrowserContext.Close, () => selectors._contextsForSelectors.delete(context2));
await context2._initializeHarFromOptions(options2.recordHar);
return context2;
}
async waitForEvent(event, optionsOrPredicate = {}) {
return await this._wrapApiCall(async () => {
const timeout = this._timeoutSettings.timeout(typeof optionsOrPredicate === "function" ? {} : optionsOrPredicate);
const predicate = typeof optionsOrPredicate === "function" ? optionsOrPredicate : optionsOrPredicate.predicate;
const waiter = Waiter.createForEvent(this, event);
waiter.rejectOnTimeout(timeout, `Timeout ${timeout}ms exceeded while waiting for event "${event}"`);
if (event !== Events.AndroidDevice.Close)
waiter.rejectOnEvent(this, Events.AndroidDevice.Close, () => new TargetClosedError2());
const result2 = await waiter.waitForEvent(this, event, predicate);
waiter.dispose();
return result2;
});
}
};
AndroidSocket = class extends ChannelOwner {
static from(androidDevice) {
return androidDevice._object;
}
constructor(parent, type3, guid, initializer) {
super(parent, type3, guid, initializer);
this._channel.on("data", ({ data }) => this.emit(Events.AndroidSocket.Data, data));
this._channel.on("close", () => this.emit(Events.AndroidSocket.Close));
}
async write(data) {
await this._channel.write({ data });
}
async close() {
await this._channel.close();
}
async [Symbol.asyncDispose]() {
await this.close();
}
};
AndroidInput = class {
constructor(device) {
this._device = device;
}
async type(text2) {
await this._device._channel.inputType({ text: text2 });
}
async press(key) {
await this._device._channel.inputPress({ key });
}
async tap(point) {
await this._device._channel.inputTap({ point });
}
async swipe(from, segments, steps) {
await this._device._channel.inputSwipe({ segments, steps });
}
async drag(from, to, steps) {
await this._device._channel.inputDrag({ from, to, steps });
}
};
AndroidWebView = class extends EventEmitter3 {
constructor(device, data) {
super(device._platform);
this._device = device;
this._data = data;
}
pid() {
return this._data.pid;
}
pkg() {
return this._data.pkg;
}
_socketName() {
return this._data.socketName;
}
async page() {
if (!this._pagePromise)
this._pagePromise = this._fetchPage();
return await this._pagePromise;
}
async _fetchPage() {
const { context: context2 } = await this._device._channel.connectToWebView({ socketName: this._data.socketName });
return BrowserContext2.from(context2).pages()[0];
}
};
}
});
// packages/playwright-core/src/client/browserType.ts
var BrowserType2;
var init_browserType2 = __esm({
"packages/playwright-core/src/client/browserType.ts"() {
"use strict";
init_assert();
init_headers();
init_browser2();
init_browserContext2();
init_channelOwner();
init_clientHelper();
init_connect();
init_timeoutSettings();
init_worker();
BrowserType2 = class extends ChannelOwner {
constructor() {
super(...arguments);
this._contexts = /* @__PURE__ */ new Set();
}
static from(browserType) {
return browserType._object;
}
executablePath() {
if (!this._initializer.executablePath)
throw new Error("Browser is not supported on current platform");
return this._initializer.executablePath;
}
name() {
return this._initializer.name;
}
async launch(options2 = {}) {
assert(!options2.userDataDir, "userDataDir option is not supported in `browserType.launch`. Use `browserType.launchPersistentContext` instead");
assert(!options2.port, "Cannot specify a port without launching as a server.");
const logger = options2.logger || this._playwright._defaultLaunchOptions?.logger;
options2 = { ...this._playwright._defaultLaunchOptions, ...options2 };
const launchOptions = {
...options2,
ignoreDefaultArgs: Array.isArray(options2.ignoreDefaultArgs) ? options2.ignoreDefaultArgs : void 0,
ignoreAllDefaultArgs: !!options2.ignoreDefaultArgs && !Array.isArray(options2.ignoreDefaultArgs),
env: options2.env ? envObjectToArray2(options2.env) : void 0,
timeout: new TimeoutSettings(this._platform).launchTimeout(options2)
};
return await this._wrapApiCall(async () => {
const browser = Browser2.from((await this._channel.launch(launchOptions)).browser);
browser._connectToBrowserType(this, options2, logger);
return browser;
});
}
async launchServer(options2 = {}) {
if (!this._serverLauncher)
throw new Error("Launching server is not supported");
options2 = { ...this._playwright._defaultLaunchOptions, ...options2 };
return await this._serverLauncher.launchServer(options2);
}
async launchPersistentContext(userDataDir, options2 = {}) {
assert(!options2.port, "Cannot specify a port without launching as a server.");
options2 = this._playwright.selectors._withSelectorOptions({
...this._playwright._defaultLaunchOptions,
...options2
});
await this._instrumentation.runBeforeCreateBrowserContext(options2);
const logger = options2.logger || this._playwright._defaultLaunchOptions?.logger;
const contextParams = await prepareBrowserContextParams(this._platform, options2);
const persistentParams = {
...contextParams,
ignoreDefaultArgs: Array.isArray(options2.ignoreDefaultArgs) ? options2.ignoreDefaultArgs : void 0,
ignoreAllDefaultArgs: !!options2.ignoreDefaultArgs && !Array.isArray(options2.ignoreDefaultArgs),
env: options2.env ? envObjectToArray2(options2.env) : void 0,
channel: options2.channel,
userDataDir: this._platform.path().isAbsolute(userDataDir) || !userDataDir ? userDataDir : this._platform.path().resolve(userDataDir),
timeout: new TimeoutSettings(this._platform).launchTimeout(options2)
};
const context2 = await this._wrapApiCall(async () => {
const result2 = await this._channel.launchPersistentContext(persistentParams);
const browser = Browser2.from(result2.browser);
browser._connectToBrowserType(this, options2, logger);
const context3 = BrowserContext2.from(result2.context);
await context3._initializeHarFromOptions(options2.recordHar);
return context3;
});
await this._instrumentation.runAfterCreateBrowserContext(context2);
return context2;
}
async connect(optionsOrEndpoint, options2) {
if (typeof optionsOrEndpoint === "string")
return await this._connect({ ...options2, endpoint: optionsOrEndpoint });
assert(optionsOrEndpoint.wsEndpoint, "options.wsEndpoint is required");
return await this._connect({ ...options2, endpoint: optionsOrEndpoint.wsEndpoint });
}
async _connect(params2) {
return await this._wrapApiCall(async () => {
const browser = await connectToBrowser(this._playwright, { browserName: this.name(), ...params2 });
browser._connectToBrowserType(this, {}, void 0);
return browser;
});
}
async connectOverCDP(endpointURLOrOptions, options2) {
if (typeof endpointURLOrOptions === "string")
return await this._connectOverCDP(endpointURLOrOptions, options2);
const endpointURL = "endpointURL" in endpointURLOrOptions ? endpointURLOrOptions.endpointURL : endpointURLOrOptions.wsEndpoint;
assert(endpointURL, "Cannot connect over CDP without wsEndpoint.");
return await this.connectOverCDP(endpointURL, endpointURLOrOptions);
}
async _connectOverCDP(endpointURL, params2 = {}) {
if (this.name() !== "chromium")
throw new Error("Connecting over CDP is only supported in Chromium.");
const headers = params2.headers ? headersObjectToArray(params2.headers) : void 0;
const result2 = await this._channel.connectOverCDP({
endpointURL,
headers,
slowMo: params2.slowMo,
timeout: new TimeoutSettings(this._platform).timeout(params2),
isLocal: params2.isLocal,
noDefaults: params2.noDefaults
});
const browser = Browser2.from(result2.browser);
browser._connectToBrowserType(this, {}, void 0);
if (result2.defaultContext)
await this._instrumentation.runAfterCreateBrowserContext(BrowserContext2.from(result2.defaultContext));
return browser;
}
async _connectToWorker(endpoint, options2 = {}) {
if (this.name() !== "chromium")
throw new Error("Connecting to workers is only supported in Chromium.");
const result2 = await this._channel.connectToWorker({
endpoint,
timeout: new TimeoutSettings(this._platform).timeout(options2)
});
return Worker2.from(result2.worker);
}
};
}
});
// packages/playwright-core/src/client/clientInstrumentation.ts
function createInstrumentation2() {
const listeners = [];
return new Proxy({}, {
get: (obj, prop) => {
if (typeof prop !== "string")
return obj[prop];
if (prop === "addListener")
return (listener) => listeners.push(listener);
if (prop === "removeListener")
return (listener) => listeners.splice(listeners.indexOf(listener), 1);
if (prop === "removeAllListeners")
return () => listeners.splice(0, listeners.length);
if (prop.startsWith("run")) {
return async (...params2) => {
for (const listener of listeners)
await listener[prop]?.(...params2);
};
}
if (prop.startsWith("on")) {
return (...params2) => {
for (const listener of listeners)
listener[prop]?.(...params2);
};
}
return obj[prop];
}
});
}
var init_clientInstrumentation = __esm({
"packages/playwright-core/src/client/clientInstrumentation.ts"() {
"use strict";
}
});
// packages/playwright-core/src/client/electron.ts
var Electron2, ElectronApplication2;
var init_electron2 = __esm({
"packages/playwright-core/src/client/electron.ts"() {
"use strict";
init_browserContext2();
init_channelOwner();
init_clientHelper();
init_consoleMessage();
init_errors2();
init_events();
init_jsHandle();
init_waiter();
init_timeoutSettings();
Electron2 = class extends ChannelOwner {
static from(electron) {
return electron._object;
}
constructor(parent, type3, guid, initializer) {
super(parent, type3, guid, initializer);
}
async launch(options2 = {}) {
options2 = this._playwright.selectors._withSelectorOptions(options2);
const params2 = {
...await prepareBrowserContextParams(this._platform, options2),
env: envObjectToArray2(options2.env ? options2.env : this._platform.env),
tracesDir: options2.tracesDir,
artifactsDir: options2.artifactsDir,
timeout: new TimeoutSettings(this._platform).launchTimeout(options2)
};
const app = ElectronApplication2.from((await this._channel.launch(params2)).electronApplication);
this._playwright.selectors._contextsForSelectors.add(app._context);
app.once(Events.ElectronApplication.Close, () => this._playwright.selectors._contextsForSelectors.delete(app._context));
await app._context._initializeHarFromOptions(options2.recordHar);
app._context.tracing._tracesDir = options2.tracesDir;
return app;
}
};
ElectronApplication2 = class extends ChannelOwner {
constructor(parent, type3, guid, initializer) {
super(parent, type3, guid, initializer);
this._windows = /* @__PURE__ */ new Set();
this._timeoutSettings = new TimeoutSettings(this._platform);
this._context = BrowserContext2.from(initializer.context);
for (const page of this._context._pages)
this._onPage(page);
this._context.on(Events.BrowserContext.Page, (page) => this._onPage(page));
this._channel.on("close", () => {
this.emit(Events.ElectronApplication.Close);
});
this._channel.on("console", (event) => this.emit(Events.ElectronApplication.Console, new ConsoleMessage2(this._platform, event, null, null)));
this._setEventToSubscriptionMapping(/* @__PURE__ */ new Map([
[Events.ElectronApplication.Console, "console"]
]));
}
static from(electronApplication) {
return electronApplication._object;
}
process() {
return this._connection.toImpl?.(this)?.process();
}
_onPage(page) {
this._windows.add(page);
this.emit(Events.ElectronApplication.Window, page);
page.once(Events.Page.Close, () => this._windows.delete(page));
}
windows() {
return [...this._windows];
}
async firstWindow(options2) {
if (this._windows.size)
return this._windows.values().next().value;
return await this.waitForEvent("window", options2);
}
context() {
return this._context;
}
async [Symbol.asyncDispose]() {
await this.close();
}
async close() {
try {
await this._context.close();
} catch (e) {
if (isTargetClosedError2(e))
return;
throw e;
}
}
async waitForEvent(event, optionsOrPredicate = {}) {
return await this._wrapApiCall(async () => {
const timeout = this._timeoutSettings.timeout(typeof optionsOrPredicate === "function" ? {} : optionsOrPredicate);
const predicate = typeof optionsOrPredicate === "function" ? optionsOrPredicate : optionsOrPredicate.predicate;
const waiter = Waiter.createForEvent(this, event);
waiter.rejectOnTimeout(timeout, `Timeout ${timeout}ms exceeded while waiting for event "${event}"`);
if (event !== Events.ElectronApplication.Close)
waiter.rejectOnEvent(this, Events.ElectronApplication.Close, () => new TargetClosedError2());
const result2 = await waiter.waitForEvent(this, event, predicate);
waiter.dispose();
return result2;
});
}
async browserWindow(page) {
const result2 = await this._channel.browserWindow({ page: page._channel });
return JSHandle2.from(result2.handle);
}
async evaluate(pageFunction, arg) {
const result2 = await this._channel.evaluateExpression({ expression: String(pageFunction), isFunction: typeof pageFunction === "function", arg: serializeArgument(arg) });
return parseResult(result2.value);
}
async evaluateHandle(pageFunction, arg) {
const result2 = await this._channel.evaluateExpressionHandle({ expression: String(pageFunction), isFunction: typeof pageFunction === "function", arg: serializeArgument(arg) });
return JSHandle2.from(result2.handle);
}
};
}
});
// packages/playwright-core/src/client/jsonPipe.ts
var JsonPipe;
var init_jsonPipe = __esm({
"packages/playwright-core/src/client/jsonPipe.ts"() {
"use strict";
init_channelOwner();
JsonPipe = class extends ChannelOwner {
static from(jsonPipe) {
return jsonPipe._object;
}
constructor(parent, type3, guid, initializer) {
super(parent, type3, guid, initializer);
}
channel() {
return this._channel;
}
};
}
});
// packages/playwright-core/src/client/localUtils.ts
var LocalUtils;
var init_localUtils2 = __esm({
"packages/playwright-core/src/client/localUtils.ts"() {
"use strict";
init_channelOwner();
LocalUtils = class extends ChannelOwner {
constructor(parent, type3, guid, initializer) {
super(parent, type3, guid, initializer);
this.devices = {};
for (const { name, descriptor } of initializer.deviceDescriptors)
this.devices[name] = descriptor;
}
async zip(params2) {
return await this._channel.zip(params2);
}
async harOpen(params2) {
return await this._channel.harOpen(params2);
}
async harLookup(params2) {
return await this._channel.harLookup(params2);
}
async harClose(params2) {
return await this._channel.harClose(params2);
}
async harUnzip(params2) {
return await this._channel.harUnzip(params2);
}
async tracingStarted(params2) {
return await this._channel.tracingStarted(params2);
}
async traceDiscarded(params2) {
return await this._channel.traceDiscarded(params2);
}
async addStackToTracingNoReply(params2) {
return await this._channel.addStackToTracingNoReply(params2);
}
};
}
});
// packages/playwright-core/src/client/selectors.ts
var Selectors2;
var init_selectors2 = __esm({
"packages/playwright-core/src/client/selectors.ts"() {
"use strict";
init_clientHelper();
init_locator();
Selectors2 = class {
constructor(platform) {
this._selectorEngines = [];
this._contextsForSelectors = /* @__PURE__ */ new Set();
this._platform = platform;
}
async register(name, script, options2 = {}) {
if (this._selectorEngines.some((engine) => engine.name === name))
throw new Error(`selectors.register: "${name}" selector engine has been already registered`);
const source8 = await evaluationScript(this._platform, script, void 0, false);
const selectorEngine = { ...options2, name, source: source8 };
for (const context2 of this._contextsForSelectors)
await context2._channel.registerSelectorEngine({ selectorEngine });
this._selectorEngines.push(selectorEngine);
}
setTestIdAttribute(attributeName) {
this._testIdAttributeName = attributeName;
setTestIdAttribute(attributeName);
for (const context2 of this._contextsForSelectors) {
context2._options.testIdAttributeName = attributeName;
context2._channel.setTestIdAttributeName({ testIdAttributeName: attributeName }).catch(() => {
});
}
}
_withSelectorOptions(options2) {
return { ...options2, selectorEngines: this._selectorEngines, testIdAttributeName: this._testIdAttributeName };
}
};
}
});
// packages/playwright-core/src/client/playwright.ts
var Playwright2;
var init_playwright2 = __esm({
"packages/playwright-core/src/client/playwright.ts"() {
"use strict";
init_android2();
init_browser2();
init_browserType2();
init_channelOwner();
init_electron2();
init_errors2();
init_fetch2();
init_selectors2();
Playwright2 = class extends ChannelOwner {
constructor(parent, type3, guid, initializer) {
super(parent, type3, guid, initializer);
this.request = new APIRequest(this);
this.chromium = BrowserType2.from(initializer.chromium);
this.chromium._playwright = this;
this.firefox = BrowserType2.from(initializer.firefox);
this.firefox._playwright = this;
this.webkit = BrowserType2.from(initializer.webkit);
this.webkit._playwright = this;
this._android = Android2.from(initializer.android);
this._android._playwright = this;
this._electron = Electron2.from(initializer.electron);
this._electron._playwright = this;
this.devices = this._connection.localUtils()?.devices ?? {};
this.selectors = new Selectors2(this._connection._platform);
this.errors = { TimeoutError: TimeoutError2 };
}
static from(channel) {
return channel._object;
}
_browserTypes() {
return [this.chromium, this.firefox, this.webkit];
}
_preLaunchedBrowser() {
const browser = Browser2.from(this._initializer.preLaunchedBrowser);
browser._connectToBrowserType(this[browser._name], {}, void 0);
return browser;
}
_allContexts() {
return this._browserTypes().flatMap((type3) => [...type3._contexts]);
}
_allPages() {
return this._allContexts().flatMap((context2) => context2.pages());
}
};
}
});
// packages/playwright-core/src/client/connection.ts
function formatCallLog(platform, log2) {
if (!log2 || !log2.some((l) => !!l))
return "";
return `
Call log:
${platform.colors.dim(log2.join("\n"))}
`;
}
var Root, DummyChannelOwner, Connection;
var init_connection = __esm({
"packages/playwright-core/src/client/connection.ts"() {
"use strict";
init_stackTrace();
init_eventEmitter();
init_android2();
init_artifact2();
init_browser2();
init_browserContext2();
init_browserType2();
init_cdpSession();
init_channelOwner();
init_clientInstrumentation();
init_debugger2();
init_dialog2();
init_disposable3();
init_electron2();
init_elementHandle();
init_errors2();
init_fetch2();
init_frame();
init_jsHandle();
init_jsonPipe();
init_localUtils2();
init_network3();
init_page2();
init_playwright2();
init_stream();
init_tracing2();
init_worker();
init_writableStream();
init_validator();
Root = class extends ChannelOwner {
constructor(connection) {
super(connection, "Root", "", {});
}
async initialize() {
return Playwright2.from((await this._channel.initialize({
sdkLanguage: "javascript"
})).playwright);
}
};
DummyChannelOwner = class extends ChannelOwner {
};
Connection = class extends EventEmitter3 {
constructor(platform, localUtils, instrumentation, headers = []) {
super(platform);
this._objects = /* @__PURE__ */ new Map();
this.onmessage = (message) => {
};
this._lastId = 0;
this._callbacks = /* @__PURE__ */ new Map();
this._isRemote = false;
this._rawBuffers = false;
this._tracingCount = 0;
this._objectFactories = /* @__PURE__ */ new Map();
this._instrumentation = instrumentation || createInstrumentation2();
this._localUtils = localUtils;
this._rootObject = new Root(this);
this.headers = headers;
this.registerObjectFactories({
Android: (parent, type3, guid, init) => new Android2(parent, type3, guid, init),
AndroidDevice: (parent, type3, guid, init) => new AndroidDevice2(parent, type3, guid, init),
AndroidSocket: (parent, type3, guid, init) => new AndroidSocket(parent, type3, guid, init),
APIRequestContext: (parent, type3, guid, init) => new APIRequestContext2(parent, type3, guid, init),
Artifact: (parent, type3, guid, init) => new Artifact2(parent, type3, guid, init),
BindingCall: (parent, type3, guid, init) => new BindingCall(parent, type3, guid, init),
Browser: (parent, type3, guid, init) => new Browser2(parent, type3, guid, init),
BrowserContext: (parent, type3, guid, init) => new BrowserContext2(parent, type3, guid, init),
BrowserType: (parent, type3, guid, init) => new BrowserType2(parent, type3, guid, init),
CDPSession: (parent, type3, guid, init) => new CDPSession2(parent, type3, guid, init),
Debugger: (parent, type3, guid, init) => new Debugger2(parent, type3, guid, init),
Dialog: (parent, type3, guid, init) => new Dialog2(parent, type3, guid, init),
Disposable: (parent, type3, guid, init) => new DisposableObject2(parent, type3, guid, init),
Electron: (parent, type3, guid, init) => new Electron2(parent, type3, guid, init),
ElectronApplication: (parent, type3, guid, init) => new ElectronApplication2(parent, type3, guid, init),
ElementHandle: (parent, type3, guid, init) => new ElementHandle2(parent, type3, guid, init),
Frame: (parent, type3, guid, init) => new Frame2(parent, type3, guid, init),
JSHandle: (parent, type3, guid, init) => new JSHandle2(parent, type3, guid, init),
JsonPipe: (parent, type3, guid, init) => new JsonPipe(parent, type3, guid, init),
LocalUtils: (parent, type3, guid, init) => {
const result2 = new LocalUtils(parent, type3, guid, init);
if (!this._localUtils)
this._localUtils = result2;
return result2;
},
Page: (parent, type3, guid, init) => new Page2(parent, type3, guid, init),
Playwright: (parent, type3, guid, init) => new Playwright2(parent, type3, guid, init),
Request: (parent, type3, guid, init) => new Request2(parent, type3, guid, init),
Response: (parent, type3, guid, init) => new Response3(parent, type3, guid, init),
Route: (parent, type3, guid, init) => new Route2(parent, type3, guid, init),
Stream: (parent, type3, guid, init) => new Stream(parent, type3, guid, init),
SocksSupport: (parent, type3, guid, init) => new DummyChannelOwner(parent, type3, guid, init),
Tracing: (parent, type3, guid, init) => new Tracing2(parent, type3, guid, init),
WebSocket: (parent, type3, guid, init) => new WebSocket2(parent, type3, guid, init),
WebSocketRoute: (parent, type3, guid, init) => new WebSocketRoute(parent, type3, guid, init),
Worker: (parent, type3, guid, init) => new Worker2(parent, type3, guid, init),
WritableStream: (parent, type3, guid, init) => new WritableStream(parent, type3, guid, init)
});
}
registerObjectFactories(factories) {
for (const [type3, factory] of Object.entries(factories))
this._objectFactories.set(type3, factory);
}
markAsRemote() {
this._isRemote = true;
}
isRemote() {
return this._isRemote;
}
useRawBuffers() {
this._rawBuffers = true;
}
rawBuffers() {
return this._rawBuffers;
}
localUtils() {
return this._localUtils;
}
async initializePlaywright() {
return await this._rootObject.initialize();
}
getObjectWithKnownName(guid) {
return this._objects.get(guid);
}
setIsTracing(isTracing) {
if (isTracing)
this._tracingCount++;
else
this._tracingCount--;
}
async sendMessageToServer(object, method, params2, options2) {
if (this._closedError)
throw this._closedError;
if (object._wasCollected)
throw new Error("The object has been collected to prevent unbounded heap growth.");
const guid = object._guid;
const type3 = object._type;
const id = ++this._lastId;
const message = { id, guid, method, params: params2 };
if (this._platform.isLogEnabled("channel")) {
this._platform.log("channel", "SEND> " + JSON.stringify(message));
}
const location2 = options2.frames?.[0] ? { file: options2.frames[0].file, line: options2.frames[0].line, column: options2.frames[0].column } : void 0;
const metadata = { title: options2.title, location: location2, internal: options2.internal, stepId: options2.stepId };
if (this._tracingCount && options2.frames && type3 !== "LocalUtils")
this._localUtils?.addStackToTracingNoReply({ callData: { stack: options2.frames ?? [], id } }).catch(() => {
});
this._platform.zones.empty.run(() => this.onmessage({ ...message, metadata }));
return await new Promise((resolve, reject) => this._callbacks.set(id, { resolve, reject, title: options2.title, type: type3, method }));
}
_validatorFromWireContext() {
return {
tChannelImpl: this._tChannelImplFromWire.bind(this),
binary: this._rawBuffers ? "buffer" : "fromBase64",
isUnderTest: () => this._platform.isUnderTest()
};
}
dispatch(message) {
if (this._closedError)
return;
const { id, guid, method, params: params2, result: result2, error, log: log2 } = message;
if (id) {
if (this._platform.isLogEnabled("channel"))
this._platform.log("channel", "<RECV " + JSON.stringify(message));
const callback = this._callbacks.get(id);
if (!callback)
throw new Error(`Cannot find command to respond: ${id}`);
this._callbacks.delete(id);
if (error && !result2) {
const parsedError = parseError2(error);
rewriteErrorMessage(parsedError, parsedError.message + formatCallLog(this._platform, log2));
callback.reject(parsedError);
} else {
const validator2 = findValidator(callback.type, callback.method, "Result");
callback.resolve(validator2(result2, "", this._validatorFromWireContext()));
}
return;
}
if (this._platform.isLogEnabled("channel"))
this._platform.log("channel", "<EVENT " + JSON.stringify(message));
if (method === "__create__") {
this._createRemoteObject(guid, params2.type, params2.guid, params2.initializer);
return;
}
const object = this._objects.get(guid);
if (!object)
throw new Error(`Cannot find object to "${method}": ${guid}`);
if (method === "__adopt__") {
const child = this._objects.get(params2.guid);
if (!child)
throw new Error(`Unknown new child: ${params2.guid}`);
object._adopt(child);
return;
}
if (method === "__dispose__") {
object._dispose(params2.reason);
return;
}
const validator = findValidator(object._type, method, "Event");
object._channel.emit(method, validator(params2, "", this._validatorFromWireContext()));
}
close(cause) {
if (this._closedError)
return;
this._closedError = new TargetClosedError2(cause);
for (const callback of this._callbacks.values())
callback.reject(this._closedError);
this._callbacks.clear();
this.emit("close");
}
_tChannelImplFromWire(names, arg, path59, context2) {
if (arg && typeof arg === "object" && typeof arg.guid === "string") {
const object = this._objects.get(arg.guid);
if (!object)
throw new Error(`Object with guid ${arg.guid} was not bound in the connection`);
if (names !== "*" && !names.includes(object._type))
throw new ValidationError(`${path59}: expected channel ${names.toString()}`);
return object._channel;
}
throw new ValidationError(`${path59}: expected channel ${names.toString()}`);
}
_createRemoteObject(parentGuid, type3, guid, initializer) {
const parent = this._objects.get(parentGuid);
if (!parent)
throw new Error(`Cannot find parent object ${parentGuid} to create ${guid}`);
const validator = findValidator(type3, "", "Initializer");
initializer = validator(initializer, "", this._validatorFromWireContext());
const factory = this._objectFactories.get(type3);
if (!factory)
throw new Error("Missing type " + type3);
return factory(parent, type3, guid, initializer);
}
};
}
});
// packages/playwright-core/src/inprocess.ts
var inprocess_exports = {};
__export(inprocess_exports, {
createInProcessPlaywright: () => createInProcessPlaywright,
playwright: () => playwright
});
function createInProcessPlaywright() {
const playwright2 = createPlaywright({ sdkLanguage: process.env.PW_LANG_NAME || "javascript" });
const clientConnection = new Connection(nodePlatform(packageRoot));
clientConnection.useRawBuffers();
const dispatcherConnection = new DispatcherConnection(
true
/* local */
);
dispatcherConnection.onmessage = (message) => clientConnection.dispatch(message);
clientConnection.onmessage = (message) => dispatcherConnection.dispatch(message);
const rootScope = new RootDispatcher(dispatcherConnection);
new PlaywrightDispatcher(rootScope, playwright2);
const playwrightAPI = clientConnection.getObjectWithKnownName("Playwright");
playwrightAPI.chromium._serverLauncher = new BrowserServerLauncherImpl("chromium");
playwrightAPI.firefox._serverLauncher = new BrowserServerLauncherImpl("firefox");
playwrightAPI.webkit._serverLauncher = new BrowserServerLauncherImpl("webkit");
playwrightAPI._android._serverLauncher = new AndroidServerLauncherImpl();
dispatcherConnection.onmessage = (message) => setImmediate(() => clientConnection.dispatch(message));
clientConnection.onmessage = (message) => setImmediate(() => dispatcherConnection.dispatch(message));
clientConnection.toImpl = (x) => {
if (x instanceof Connection)
return x === clientConnection ? dispatcherConnection : void 0;
if (!x)
return dispatcherConnection._dispatcherByGuid.get("");
return dispatcherConnection._dispatcherByGuid.get(x._guid)._object;
};
return playwrightAPI;
}
var playwright;
var init_inprocess = __esm({
"packages/playwright-core/src/inprocess.ts"() {
"use strict";
init_nodePlatform();
init_androidServerImpl();
init_browserServerImpl();
init_server();
init_connection();
init_package();
playwright = createInProcessPlaywright();
}
});
// packages/playwright-core/src/tools/backend/utils.ts
async function waitForCompletion(tab2, callback) {
const requests2 = [];
const requestListener = (request2) => requests2.push(request2);
const disposeListeners = () => {
tab2.page.off("request", requestListener);
};
tab2.page.on("request", requestListener);
let result2;
try {
result2 = await callback();
await tab2.waitForTimeout(500);
} finally {
disposeListeners();
}
const requestedNavigation = requests2.some((request2) => request2.isNavigationRequest());
if (requestedNavigation) {
await tab2.page.mainFrame().waitForLoadState("load", { timeout: 1e4 }).catch(() => {
});
return result2;
}
const promises = [];
for (const request2 of requests2) {
if (["document", "stylesheet", "script", "xhr", "fetch"].includes(request2.resourceType()))
promises.push(request2.response().then((r) => r?.finished()).catch(() => {
}));
else
promises.push(request2.response().catch(() => {
}));
}
const timeout = new Promise((resolve) => setTimeout(resolve, 5e3));
await Promise.race([Promise.all(promises), timeout]);
if (requests2.length)
await tab2.waitForTimeout(500);
return result2;
}
function eventWaiter(page, event, timeout) {
const disposables = [];
const eventPromise = new Promise((resolve, reject) => {
page.on(event, resolve);
disposables.push(() => page.off(event, resolve));
});
let abort;
const abortPromise = new Promise((resolve, reject) => {
abort = () => resolve(void 0);
});
const timeoutPromise = new Promise((f) => {
const timeoutId = setTimeout(() => f(void 0), timeout);
disposables.push(() => clearTimeout(timeoutId));
});
return {
promise: Promise.race([eventPromise, abortPromise, timeoutPromise]).finally(() => disposables.forEach((dispose) => dispose())),
abort
};
}
var init_utils3 = __esm({
"packages/playwright-core/src/tools/backend/utils.ts"() {
"use strict";
}
});
// packages/playwright-core/src/tools/backend/logFile.ts
var import_fs41, import_path38, debug5, LogFile;
var init_logFile = __esm({
"packages/playwright-core/src/tools/backend/logFile.ts"() {
"use strict";
import_fs41 = __toESM(require("fs"));
import_path38 = __toESM(require("path"));
debug5 = require("./utilsBundle").debug;
LogFile = class {
constructor(context2, startTime, filePrefix, title) {
this._stopped = false;
this._line = 0;
this._entries = 0;
this._lastLine = 0;
this._lastEntries = 0;
this._writeChain = Promise.resolve();
this._context = context2;
this._startTime = startTime;
this._filePrefix = filePrefix;
this._title = title;
}
appendLine(wallTime, text2) {
this._writeChain = this._writeChain.then(() => this._write(wallTime, text2)).catch((e) => debug5("pw:tools:error")(e));
}
stop() {
this._stopped = true;
}
async take(relativeTo) {
const logChunk = await this._take();
if (!logChunk)
return void 0;
const logFilePath = relativeTo ? import_path38.default.relative(relativeTo, logChunk.file) : logChunk.file;
const lineRange = logChunk.fromLine === logChunk.toLine ? `#L${logChunk.fromLine}` : `#L${logChunk.fromLine}-L${logChunk.toLine}`;
return `${logFilePath}${lineRange}`;
}
async _take() {
await this._writeChain;
if (!this._file || this._entries === this._lastEntries)
return void 0;
const chunk = {
type: this._title.toLowerCase(),
file: this._file,
fromLine: this._lastLine + 1,
toLine: this._line,
entryCount: this._entries - this._lastEntries
};
this._lastLine = this._line;
this._lastEntries = this._entries;
return chunk;
}
async _write(wallTime, text2) {
if (this._stopped)
return;
this._file ??= await this._context.outputFile({ prefix: this._filePrefix, ext: "log", date: new Date(this._startTime) }, { origin: "code" });
const relativeTime = Math.round(wallTime - this._startTime);
const logLine = `[${String(relativeTime).padStart(8, " ")}ms] ${text2}
`;
await import_fs41.default.promises.appendFile(this._file, logLine);
const lineCount = logLine.split("\n").length - 1;
this._line += lineCount;
this._entries++;
}
};
}
});
// packages/playwright-core/src/tools/backend/tool.ts
function defineTool(tool) {
return tool;
}
function defineTabTool(tool) {
return {
...tool,
handle: async (context2, params2, response2, signal) => {
const tab2 = await context2.ensureTab();
const modalStates = tab2.modalStates().map((state) => state.type);
if (tool.clearsModalState && !modalStates.includes(tool.clearsModalState))
response2.addError(`Error: The tool "${tool.schema.name}" can only be used when there is related modal state present.`);
else if (!tool.clearsModalState && modalStates.length)
response2.addError(`Error: Tool "${tool.schema.name}" does not handle the modal state.`);
else
return tool.handle(tab2, params2, response2, signal);
}
};
}
var init_tool = __esm({
"packages/playwright-core/src/tools/backend/tool.ts"() {
"use strict";
}
});
// packages/playwright-core/src/tools/backend/dialogs.ts
var z, handleDialog, dialogs_default;
var init_dialogs = __esm({
"packages/playwright-core/src/tools/backend/dialogs.ts"() {
"use strict";
init_tool();
z = require("./utilsBundle").z;
handleDialog = defineTabTool({
capability: "core",
schema: {
name: "browser_handle_dialog",
title: "Handle a dialog",
description: "Handle a dialog",
inputSchema: z.object({
accept: z.boolean().describe("Whether to accept the dialog."),
promptText: z.string().optional().describe("The text of the prompt in case of a prompt dialog.")
}),
type: "action"
},
handle: async (tab2, params2, response2) => {
const dialogState = tab2.modalStates().find((state) => state.type === "dialog");
if (!dialogState)
throw new Error("No dialog visible");
tab2.clearModalState(dialogState);
await tab2.waitForCompletion(async () => {
if (params2.accept)
await dialogState.dialog.accept(params2.promptText);
else
await dialogState.dialog.dismiss();
});
},
clearsModalState: "dialog"
});
dialogs_default = [
handleDialog
];
}
});
// packages/playwright-core/src/tools/backend/snapshot.ts
var z2, elementTargetDescription, optionalElementSchema, elementSchema, snapshot, clickSchema, click, drag, hover, selectOptionSchema, selectOption, generateLocator, check, uncheck, snapshot_default;
var init_snapshot = __esm({
"packages/playwright-core/src/tools/backend/snapshot.ts"() {
"use strict";
init_stringUtils();
init_tool();
z2 = require("./utilsBundle").z;
elementTargetDescription = "Exact target element reference from the page snapshot, or a unique element selector";
optionalElementSchema = z2.object({
element: z2.string().optional().describe("Human-readable element description used to obtain permission to interact with the element"),
target: z2.string().optional().describe(elementTargetDescription)
});
elementSchema = z2.object({
element: z2.string().optional().describe("Human-readable element description used to obtain permission to interact with the element"),
target: z2.string().describe(elementTargetDescription)
});
snapshot = defineTabTool({
capability: "core",
schema: {
name: "browser_snapshot",
title: "Page snapshot",
description: "Capture accessibility snapshot of the current page, this is better than screenshot",
inputSchema: z2.object({
target: z2.string().optional().describe(elementTargetDescription),
filename: z2.string().optional().describe("Save snapshot to markdown file instead of returning it in the response."),
depth: z2.number().optional().describe("Limit the depth of the snapshot tree"),
boxes: z2.boolean().optional().describe("Include each element's bounding box as [box=x,y,width,height] in the snapshot. Coordinates are viewport-relative, in CSS pixels (Element.getBoundingClientRect)")
}),
type: "readOnly"
},
handle: async (tab2, params2, response2) => {
let resolved = { locator: void 0, resolved: "" };
if (params2.target)
resolved = await tab2.targetLocator({ target: params2.target });
response2.setIncludeFullSnapshot(params2.filename, resolved.locator, params2.depth, params2.boxes);
}
});
clickSchema = elementSchema.extend({
doubleClick: z2.boolean().optional().describe("Whether to perform a double click instead of a single click"),
button: z2.enum(["left", "right", "middle"]).optional().describe("Button to click, defaults to left"),
modifiers: z2.array(z2.enum(["Alt", "Control", "ControlOrMeta", "Meta", "Shift"])).optional().describe("Modifier keys to press")
});
click = defineTabTool({
capability: "core",
schema: {
name: "browser_click",
title: "Click",
description: "Perform click on a web page",
inputSchema: clickSchema,
type: "input"
},
handle: async (tab2, params2, response2) => {
response2.setIncludeSnapshot();
const { locator: locator2, resolved } = await tab2.targetLocator(params2);
const options2 = {
button: params2.button,
modifiers: params2.modifiers,
...tab2.actionTimeoutOptions
};
const optionsArg = formatObjectOrVoid(options2);
if (params2.doubleClick)
response2.addCode(`await page.${resolved}.dblclick(${optionsArg});`);
else
response2.addCode(`await page.${resolved}.click(${optionsArg});`);
await tab2.waitForCompletion(async () => {
if (params2.doubleClick)
await locator2.dblclick(options2);
else
await locator2.click(options2);
});
}
});
drag = defineTabTool({
capability: "core",
schema: {
name: "browser_drag",
title: "Drag mouse",
description: "Perform drag and drop between two elements",
inputSchema: z2.object({
startElement: z2.string().optional().describe("Human-readable source element description used to obtain the permission to interact with the element"),
startTarget: z2.string().describe(elementTargetDescription),
endElement: z2.string().optional().describe("Human-readable target element description used to obtain the permission to interact with the element"),
endTarget: z2.string().describe(elementTargetDescription)
}),
type: "input"
},
handle: async (tab2, params2, response2) => {
response2.setIncludeSnapshot();
const [start3, end] = await tab2.targetLocators([
{ target: params2.startTarget, element: params2.startElement },
{ target: params2.endTarget, element: params2.endElement }
]);
await tab2.waitForCompletion(async () => {
await start3.locator.dragTo(end.locator, tab2.actionTimeoutOptions);
});
response2.addCode(`await page.${start3.resolved}.dragTo(page.${end.resolved});`);
}
});
hover = defineTabTool({
capability: "core",
schema: {
name: "browser_hover",
title: "Hover mouse",
description: "Hover over element on page",
inputSchema: elementSchema,
type: "input"
},
handle: async (tab2, params2, response2) => {
response2.setIncludeSnapshot();
const { locator: locator2, resolved } = await tab2.targetLocator(params2);
response2.addCode(`await page.${resolved}.hover();`);
await locator2.hover(tab2.actionTimeoutOptions);
}
});
selectOptionSchema = elementSchema.extend({
values: z2.array(z2.string()).describe("Array of values to select in the dropdown. This can be a single value or multiple values.")
});
selectOption = defineTabTool({
capability: "core",
schema: {
name: "browser_select_option",
title: "Select option",
description: "Select an option in a dropdown",
inputSchema: selectOptionSchema,
type: "input"
},
handle: async (tab2, params2, response2) => {
response2.setIncludeSnapshot();
const { locator: locator2, resolved } = await tab2.targetLocator(params2);
response2.addCode(`await page.${resolved}.selectOption(${formatObject(params2.values)});`);
await locator2.selectOption(params2.values, tab2.actionTimeoutOptions);
}
});
generateLocator = defineTabTool({
capability: "testing",
schema: {
name: "browser_generate_locator",
title: "Create locator for element",
description: "Generate locator for the given element to use in tests",
inputSchema: elementSchema,
type: "readOnly"
},
handle: async (tab2, params2, response2) => {
const { resolved } = await tab2.targetLocator(params2);
response2.addTextResult(resolved);
}
});
check = defineTabTool({
capability: "core-input",
skillOnly: true,
schema: {
name: "browser_check",
title: "Check",
description: "Check a checkbox or radio button",
inputSchema: elementSchema,
type: "input"
},
handle: async (tab2, params2, response2) => {
const { locator: locator2, resolved } = await tab2.targetLocator(params2);
response2.addCode(`await page.${resolved}.check();`);
await locator2.check(tab2.actionTimeoutOptions);
}
});
uncheck = defineTabTool({
capability: "core-input",
skillOnly: true,
schema: {
name: "browser_uncheck",
title: "Uncheck",
description: "Uncheck a checkbox or radio button",
inputSchema: elementSchema,
type: "input"
},
handle: async (tab2, params2, response2) => {
const { locator: locator2, resolved } = await tab2.targetLocator(params2);
response2.addCode(`await page.${resolved}.uncheck();`);
await locator2.uncheck(tab2.actionTimeoutOptions);
}
});
snapshot_default = [
snapshot,
click,
drag,
hover,
selectOption,
generateLocator,
check,
uncheck
];
}
});
// packages/playwright-core/src/tools/backend/files.ts
var z3, uploadFile, drop, files_default;
var init_files = __esm({
"packages/playwright-core/src/tools/backend/files.ts"() {
"use strict";
init_stringUtils();
init_tool();
init_snapshot();
z3 = require("./utilsBundle").z;
uploadFile = defineTabTool({
capability: "core",
schema: {
name: "browser_file_upload",
title: "Upload files",
description: "Upload one or multiple files",
inputSchema: z3.object({
paths: z3.array(z3.string()).optional().describe("The absolute paths to the files to upload. Can be single file or multiple files. If omitted, file chooser is cancelled.")
}),
type: "action"
},
handle: async (tab2, params2, response2) => {
response2.setIncludeSnapshot();
const modalState = tab2.modalStates().find((state) => state.type === "fileChooser");
if (!modalState)
throw new Error("No file chooser visible");
if (params2.paths)
await Promise.all(params2.paths.map((filePath) => response2.resolveClientFilename(filePath)));
response2.addCode(`await fileChooser.setFiles(${JSON.stringify(params2.paths)})`);
tab2.clearModalState(modalState);
await tab2.waitForCompletion(async () => {
if (params2.paths)
await modalState.fileChooser.setFiles(params2.paths);
});
},
clearsModalState: "fileChooser"
});
drop = defineTabTool({
capability: "core",
schema: {
name: "browser_drop",
title: "Drop files or data onto an element",
description: 'Drop files or MIME-typed data onto an element, as if dragged from outside the page. At least one of "paths" or "data" must be provided.',
inputSchema: elementSchema.extend({
paths: z3.array(z3.string()).optional().describe("Absolute paths to files to drop onto the element."),
data: z3.record(z3.string(), z3.string()).optional().describe('Data to drop, as a map of MIME type to string value (e.g. {"text/plain": "hello", "text/uri-list": "https://example.com"}).')
}),
type: "action"
},
handle: async (tab2, params2, response2) => {
if (!params2.paths?.length && !params2.data)
throw new Error('At least one of "paths" or "data" must be provided.');
response2.setIncludeSnapshot();
const { locator: locator2, resolved } = await tab2.targetLocator(params2);
if (params2.paths)
await Promise.all(params2.paths.map((p) => response2.resolveClientFilename(p)));
const payload = {};
if (params2.paths?.length)
payload.files = params2.paths.length === 1 ? params2.paths[0] : params2.paths;
if (params2.data)
payload.data = params2.data;
await tab2.waitForCompletion(async () => {
await locator2.drop(payload, tab2.actionTimeoutOptions);
});
response2.addCode(`await page.${resolved}.drop(${formatObject(payload)});`);
}
});
files_default = [
uploadFile,
drop
];
}
});
// packages/playwright-core/src/tools/backend/tab.ts
function messageToConsoleMessage(message) {
return {
type: message.type(),
timestamp: message.timestamp(),
text: message.text(),
toString: () => `[${message.type().toUpperCase()}] ${message.text()} @ ${message.location().url}:${message.location().lineNumber}`
};
}
function pageErrorToConsoleMessage(errorOrValue) {
if (errorOrValue instanceof Error) {
return {
type: "error",
timestamp: Date.now(),
text: errorOrValue.message,
toString: () => errorOrValue.stack || errorOrValue.message
};
}
return {
type: "error",
timestamp: Date.now(),
text: String(errorOrValue),
toString: () => String(errorOrValue)
};
}
function renderModalStates(config, modalStates) {
const result2 = [];
if (modalStates.length === 0)
result2.push("- There is no modal state present");
for (const state of modalStates)
result2.push(`- [${state.description}]: can be handled by ${config.skillMode ? state.clearedBy.skill : state.clearedBy.tool}`);
return result2;
}
function shouldIncludeMessage(thresholdLevel, type3) {
const messageLevel = consoleLevelForMessageType(type3);
return consoleMessageLevels.indexOf(messageLevel) <= consoleMessageLevels.indexOf(thresholdLevel || "info");
}
function consoleLevelForMessageType(type3) {
switch (type3) {
case "assert":
case "error":
return "error";
case "warning":
return "warning";
case "count":
case "dir":
case "dirxml":
case "info":
case "log":
case "table":
case "time":
case "timeEnd":
return "info";
case "clear":
case "debug":
case "endGroup":
case "profile":
case "profileEnd":
case "startGroup":
case "startGroupCollapsed":
case "trace":
return "debug";
default:
return "info";
}
}
function sanitizeForFilePath2(s) {
const sanitize = (s2) => s2.replace(/[\x00-\x2C\x2E-\x2F\x3A-\x40\x5B-\x60\x7B-\x7F]+/g, "-");
const separator = s.lastIndexOf(".");
if (separator === -1)
return sanitize(s);
return sanitize(s.substring(0, separator)) + "." + sanitize(s.substring(separator + 1));
}
function tabHeaderEquals(a, b) {
return a.title === b.title && a.url === b.url && a.current === b.current && a.crashed === b.crashed && a.console.errors === b.console.errors && a.console.warnings === b.console.warnings && a.console.total === b.console.total;
}
var import_events27, debug6, TabEvents, Tab, consoleMessageLevels, tabSymbol;
var init_tab = __esm({
"packages/playwright-core/src/tools/backend/tab.ts"() {
"use strict";
import_events27 = require("events");
init_locatorGenerators();
init_locatorParser();
init_manualPromise();
init_eventsHelper();
init_disposable();
init_utils3();
init_logFile();
init_dialogs();
init_files();
debug6 = require("./utilsBundle").debug;
TabEvents = {
modalState: "modalState"
};
Tab = class _Tab extends import_events27.EventEmitter {
constructor(context2, page, onPageClose) {
super();
this._lastHeader = { title: "about:blank", url: "about:blank", current: false, crashed: false, console: { total: 0, warnings: 0, errors: 0 } };
this._downloads = [];
this._requests = [];
this.crashed = false;
this._modalStates = [];
this._recentEventEntries = [];
this.context = context2;
this.page = page;
this._onPageClose = onPageClose;
const p = page;
this._disposables = [
eventsHelper.addEventListener(p, "console", (event) => this._handleConsoleMessage(messageToConsoleMessage(event))),
eventsHelper.addEventListener(p, "pageerror", (error) => this._handleConsoleMessage(pageErrorToConsoleMessage(error))),
eventsHelper.addEventListener(p, "request", (request2) => this._handleRequest(request2)),
eventsHelper.addEventListener(p, "response", (response2) => this._handleResponse(response2)),
eventsHelper.addEventListener(p, "requestfailed", (request2) => this._handleRequestFailed(request2)),
eventsHelper.addEventListener(p, "close", () => this._onClose()),
eventsHelper.addEventListener(p, "crash", () => {
this.crashed = true;
}),
eventsHelper.addEventListener(p, "filechooser", (chooser) => {
this.setModalState({
type: "fileChooser",
description: "File chooser",
fileChooser: chooser,
clearedBy: { tool: uploadFile.schema.name, skill: "upload" }
});
}),
eventsHelper.addEventListener(p, "dialog", (dialog) => this._dialogShown(dialog)),
eventsHelper.addEventListener(p, "download", (download) => {
void this._downloadStarted(download);
})
];
page[tabSymbol] = this;
const wallTime = Date.now();
this._consoleLog = new LogFile(this.context, wallTime, "console", "Console");
this._initializedPromise = this._initialize();
this.actionTimeoutOptions = { timeout: context2.config.timeouts?.action };
this.navigationTimeoutOptions = { timeout: context2.config.timeouts?.navigation };
this.expectTimeoutOptions = { timeout: context2.config.timeouts?.expect };
}
async dispose() {
await disposeAll(this._disposables);
this._consoleLog.stop();
}
static forPage(page) {
return page[tabSymbol];
}
static async collectConsoleMessages(page) {
const result2 = [];
const messages = await page.consoleMessages().catch(() => []);
for (const message of messages)
result2.push(messageToConsoleMessage(message));
const errors = await page.pageErrors().catch(() => []);
for (const error of errors)
result2.push(pageErrorToConsoleMessage(error));
return result2;
}
async _initialize() {
for (const message of await _Tab.collectConsoleMessages(this.page))
this._handleConsoleMessage(message);
const requests2 = await this.page.requests().catch(() => []);
for (const request2 of requests2.filter((r) => r.existingResponse() || r.failure()))
this._requests.push(request2);
for (const initPage of this.context.config.browser?.initPage || []) {
try {
const { default: func } = require(initPage);
await func({ page: this.page });
} catch (e) {
const reason = e instanceof Error ? e.message : String(e);
throw new Error(`Failed to load init page "${initPage}": ${reason}`, { cause: e });
}
}
}
modalStates() {
return this._modalStates;
}
setModalState(modalState) {
this._modalStates.push(modalState);
this.emit(TabEvents.modalState, modalState);
}
clearModalState(modalState) {
this._modalStates = this._modalStates.filter((state) => state !== modalState);
}
_dialogShown(dialog) {
this.setModalState({
type: "dialog",
description: `"${dialog.type()}" dialog with message "${dialog.message()}"`,
dialog,
clearedBy: { tool: handleDialog.schema.name, skill: "dialog-accept or dialog-dismiss" }
});
}
async _downloadStarted(download) {
const outputFile2 = await this.context.outputFile({ suggestedFilename: sanitizeForFilePath2(download.suggestedFilename()), prefix: "download", ext: "bin" }, { origin: "code" });
const entry = {
download,
finished: false,
outputFile: outputFile2
};
this._downloads.push(entry);
this._addLogEntry({ type: "download-start", wallTime: Date.now(), download: entry });
await download.saveAs(entry.outputFile);
entry.finished = true;
this._addLogEntry({ type: "download-finish", wallTime: Date.now(), download: entry });
}
_clearCollectedArtifacts() {
this._downloads.length = 0;
this._requests.length = 0;
this._recentEventEntries.length = 0;
this._resetLogs();
}
_resetLogs() {
const wallTime = Date.now();
this._consoleLog.stop();
this._consoleLog = new LogFile(this.context, wallTime, "console", "Console");
}
_handleRequest(request2) {
this._requests.push(request2);
const wallTime = request2.timing().startTime || Date.now();
this._addLogEntry({ type: "request", wallTime, request: request2 });
}
_handleResponse(response2) {
const timing = response2.request().timing();
const wallTime = timing.responseStart + timing.startTime;
this._addLogEntry({ type: "request", wallTime, request: response2.request() });
}
_handleRequestFailed(request2) {
this._requests.push(request2);
const timing = request2.timing();
const wallTime = timing.responseEnd + timing.startTime;
this._addLogEntry({ type: "request", wallTime, request: request2 });
}
_handleConsoleMessage(message) {
const wallTime = message.timestamp;
this._addLogEntry({ type: "console", wallTime, message });
if (shouldIncludeMessage(this.context.config.console?.level, message.type))
this._consoleLog.appendLine(wallTime, message.toString());
}
logErrorMessage(text2) {
this._handleConsoleMessage(pageErrorToConsoleMessage(new Error(text2)));
}
_addLogEntry(entry) {
this._recentEventEntries.push(entry);
}
_onClose() {
this._clearCollectedArtifacts();
this._onPageClose(this);
}
async headerSnapshot() {
let title;
let consoleCounts = { total: 0, errors: 0, warnings: 0 };
if (!this.crashed) {
await this._raceAgainstModalStates(async () => {
title = await this.page.title();
});
consoleCounts = await this.consoleMessageCount();
}
const newHeader = {
title: title ?? "",
url: this.page.url(),
current: this.isCurrentTab(),
crashed: this.crashed,
console: consoleCounts
};
if (!tabHeaderEquals(this._lastHeader, newHeader)) {
this._lastHeader = newHeader;
return { ...this._lastHeader, changed: true };
}
return { ...this._lastHeader, changed: false };
}
isCurrentTab() {
return this === this.context.currentTab();
}
async waitForLoadState(state, options2) {
await this._initializedPromise;
await this.page.waitForLoadState(state, options2).catch((e) => debug6("pw:tools:error")(e));
}
async checkUrlAndNavigate(url2) {
try {
new URL(url2);
} catch (e) {
if (url2.startsWith("localhost"))
url2 = "http://" + url2;
else
url2 = "https://" + url2;
}
this.context.checkUrlAllowed(url2);
await this.navigate(url2);
return url2;
}
async navigate(url2) {
await this._initializedPromise;
this._clearCollectedArtifacts();
const { promise: downloadEvent, abort: abortDownloadEvent } = eventWaiter(this.page, "download", 3e3);
try {
await this.page.goto(url2, { waitUntil: "domcontentloaded", ...this.navigationTimeoutOptions });
abortDownloadEvent();
} catch (_e) {
const e = _e;
const mightBeDownload = e.message.includes("net::ERR_ABORTED") || e.message.includes("Download is starting");
if (!mightBeDownload)
throw e;
const download = await downloadEvent;
if (!download)
throw e;
await new Promise((resolve) => setTimeout(resolve, 500));
return;
}
await this.waitForLoadState("load", { timeout: 5e3 });
}
async consoleMessageCount() {
await this._initializedPromise;
const messages = await this.page.consoleMessages({ filter: "since-navigation" });
const pageErrors = await this.page.pageErrors({ filter: "since-navigation" });
let errors = pageErrors.length;
let warnings = 0;
for (const message of messages) {
if (message.type() === "error")
errors++;
else if (message.type() === "warning")
warnings++;
}
return { total: messages.length + pageErrors.length, errors, warnings };
}
async consoleMessages(level, all) {
await this._initializedPromise;
const result2 = [];
const messages = await this.page.consoleMessages({ filter: all ? "all" : "since-navigation" });
for (const message of messages) {
const cm = messageToConsoleMessage(message);
if (shouldIncludeMessage(level, cm.type))
result2.push(cm);
}
if (shouldIncludeMessage(level, "error")) {
const errors = await this.page.pageErrors({ filter: all ? "all" : "since-navigation" });
for (const error of errors)
result2.push(pageErrorToConsoleMessage(error));
}
return result2;
}
async clearConsoleMessages() {
await this._initializedPromise;
await Promise.all([
this.page.clearConsoleMessages(),
this.page.clearPageErrors()
]);
}
async requests() {
await this._initializedPromise;
return this._requests;
}
async clearRequests() {
await this._initializedPromise;
this._requests.length = 0;
}
async captureSnapshot(root, depth, boxes, relativeTo) {
await this._initializedPromise;
let tabSnapshot;
const modalStates = await this._raceAgainstModalStates(async () => {
const ariaSnapshot = root ? await root.ariaSnapshot({ mode: "ai", depth, boxes }) : await this.page.ariaSnapshot({ mode: "ai", depth, boxes });
tabSnapshot = {
ariaSnapshot,
modalStates: [],
events: []
};
});
if (tabSnapshot) {
tabSnapshot.consoleLink = await this._consoleLog.take(relativeTo);
tabSnapshot.events = this._recentEventEntries;
this._recentEventEntries = [];
}
return tabSnapshot ?? {
ariaSnapshot: "",
modalStates,
events: []
};
}
_javaScriptBlocked() {
return this._modalStates.some((state) => state.type === "dialog");
}
async _raceAgainstModalStates(action) {
if (this.modalStates().length)
return this.modalStates();
const promise = new ManualPromise();
const listener = (modalState) => promise.resolve([modalState]);
this.once(TabEvents.modalState, listener);
return await Promise.race([
action().then(() => {
this.off(TabEvents.modalState, listener);
return [];
}),
promise
]);
}
async waitForCompletion(callback) {
await this._initializedPromise;
await this._raceAgainstModalStates(() => waitForCompletion(this, callback));
}
async targetLocator(params2) {
await this._initializedPromise;
return (await this.targetLocators([params2]))[0];
}
async targetLocators(params2) {
await this._initializedPromise;
return Promise.all(params2.map(async (param) => {
if (!param.target.match(/^(f\d+)?e\d+$/)) {
const selector = locatorOrSelectorAsSelector("javascript", param.target, this.context.config.testIdAttribute || "data-testid");
const handle = await this.page.$(selector);
if (!handle)
throw new Error(`"${param.target}" does not match any elements.`);
handle.dispose().catch(() => {
});
return { locator: this.page.locator(selector), resolved: asLocator("javascript", selector) };
} else {
try {
let locator2 = this.page.locator(`aria-ref=${param.target}`);
if (param.element)
locator2 = locator2.describe(param.element);
const resolved = await locator2.normalize();
return { locator: locator2, resolved: resolved.toString() };
} catch (e) {
throw new Error(`Ref ${param.target} not found in the current page snapshot. Try capturing new snapshot.`);
}
}
}));
}
async waitForTimeout(time) {
if (this._javaScriptBlocked()) {
await new Promise((f) => setTimeout(f, time));
return;
}
await this.page.evaluate(() => new Promise((f) => setTimeout(f, 1e3))).catch(() => {
});
}
};
consoleMessageLevels = ["error", "warning", "info", "debug"];
tabSymbol = Symbol("tabSymbol");
}
});
// packages/playwright-core/src/tools/backend/context.ts
function originOrHostGlob(originOrHost) {
const wildcardPortMatch = originOrHost.match(/^(https?:\/\/[^/:]+):\*$/);
if (wildcardPortMatch)
return `${wildcardPortMatch[1]}:*/**`;
try {
const url2 = new URL(originOrHost);
if (url2.origin !== "null")
return `${url2.origin}/**`;
} catch {
}
return `*://${originOrHost}/**`;
}
async function workspaceFile(options2, fileName, perCallWorkspaceDir) {
const workspace = perCallWorkspaceDir ?? options2.cwd;
const resolvedName = import_path39.default.resolve(workspace, fileName);
await checkFile(options2, resolvedName, { origin: "llm" });
return resolvedName;
}
function outputDir(options2) {
if (options2.config.outputDir)
return import_path39.default.resolve(options2.config.outputDir);
const baseName = options2.config.skillMode ? ".playwright-cli" : ".playwright-mcp";
if (isSystemDirectory(options2.cwd) || !isWritable(options2.cwd))
return import_path39.default.join(import_os18.default.tmpdir(), baseName);
return import_path39.default.join(options2.cwd, baseName);
}
async function outputFile(options2, fileName, flags) {
const resolvedFile = import_path39.default.resolve(outputDir(options2), fileName);
await checkFile(options2, resolvedFile, flags);
await import_fs42.default.promises.mkdir(import_path39.default.dirname(resolvedFile), { recursive: true });
debug7("pw:mcp:file")(resolvedFile);
return resolvedFile;
}
async function checkFile(options2, resolvedFilename, flags) {
if (flags.origin === "code" || options2.config.allowUnrestrictedFileAccess || options2.config.skillMode)
return;
const output = outputDir(options2);
const workspace = options2.cwd;
if (!isPathInside(output, resolvedFilename) && !isPathInside(workspace, resolvedFilename))
throw new Error(`File access denied: ${resolvedFilename} is outside allowed roots. Allowed roots: ${output}, ${workspace}`);
}
var import_fs42, import_os18, import_path39, debug7, testDebug, Context;
var init_context = __esm({
"packages/playwright-core/src/tools/backend/context.ts"() {
"use strict";
import_fs42 = __toESM(require("fs"));
import_os18 = __toESM(require("os"));
import_path39 = __toESM(require("path"));
init_stringUtils();
init_disposable();
init_eventsHelper();
init_fileUtils();
init_inprocess();
init_tab();
debug7 = require("./utilsBundle").debug;
testDebug = debug7("pw:mcp:test");
Context = class {
constructor(browserContext, options2) {
this._tabs = [];
this._routes = [];
this._disposables = [];
this._pendingUnhandledRejections = [];
this._unhandledRejectionListeners = /* @__PURE__ */ new Set();
this._onUnhandledRejection = (reason) => {
this._pendingUnhandledRejections.push(reason);
for (const listener of this._unhandledRejectionListeners)
listener(reason);
};
this.config = options2.config;
this.sessionLog = options2.sessionLog;
this.options = options2;
this._rawBrowserContext = browserContext;
testDebug("create context");
process.on("unhandledRejection", this._onUnhandledRejection);
}
async dispose() {
process.off("unhandledRejection", this._onUnhandledRejection);
await disposeAll(this._disposables);
for (const tab2 of this._tabs)
await tab2.dispose();
this._tabs.length = 0;
this._currentTab = void 0;
await this.stopVideoRecording();
}
drainPendingUnhandledRejections() {
const reasons = this._pendingUnhandledRejections.slice();
this._pendingUnhandledRejections.length = 0;
return reasons;
}
onUnhandledRejection(listener) {
this._unhandledRejectionListeners.add(listener);
return () => this._unhandledRejectionListeners.delete(listener);
}
debugger() {
return this._rawBrowserContext.debugger;
}
tabs() {
return this._tabs;
}
currentTab() {
return this._currentTab;
}
currentTabOrDie() {
if (!this._currentTab)
throw new Error("No open pages available.");
return this._currentTab;
}
async newTab() {
const browserContext = await this.ensureBrowserContext();
const page = await browserContext.newPage();
this._currentTab = this._tabs.find((t) => t.page === page);
return this._currentTab;
}
async selectTab(index) {
const tab2 = this._tabs[index];
if (!tab2)
throw new Error(`Tab ${index} not found`);
await tab2.page.bringToFront();
this._currentTab = tab2;
return tab2;
}
async ensureTab() {
await this.ensureBrowserContext();
const crashed = this._currentTab?.crashed;
if (crashed) {
await this._currentTab.page.close().catch(() => {
});
this._currentTab = void 0;
}
if (!this._currentTab)
await this.newTab();
if (crashed)
this._currentTab.logErrorMessage("Page crashed and was reset to about:blank.");
return this._currentTab;
}
async closeTab(index) {
const tab2 = index === void 0 ? this._currentTab : this._tabs[index];
if (!tab2)
throw new Error(`Tab ${index} not found`);
const url2 = tab2.page.url();
await tab2.page.close();
return url2;
}
async workspaceFile(fileName, perCallWorkspaceDir) {
return await workspaceFile(this.options, fileName, perCallWorkspaceDir);
}
async outputFile(template, options2) {
const baseName = template.suggestedFilename || `${template.prefix}-${(template.date ?? /* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-")}${template.ext ? "." + template.ext : ""}`;
return await outputFile(this.options, baseName, options2);
}
async startVideoRecording(fileName, params2) {
if (this._video)
throw new Error("Video recording has already been started.");
this._video = { params: params2, fileName, fileNames: [] };
const browserContext = await this.ensureBrowserContext();
for (const page of browserContext.pages())
await this._startPageVideo(page);
}
async stopVideoRecording() {
if (!this._video)
return [];
const video = this._video;
for (const page of this._rawBrowserContext.pages())
await page.screencast.stop();
this._video = void 0;
return [...video.fileNames];
}
async _startPageVideo(page) {
if (!this._video)
return;
const suffix = this._video.fileNames.length ? `-${this._video.fileNames.length}` : "";
let fileName = this._video.fileName;
if (fileName && suffix) {
const ext = import_path39.default.extname(fileName);
fileName = import_path39.default.basename(fileName, ext) + suffix + ext;
}
this._video.fileNames.push(fileName);
await page.screencast.start({ path: fileName, ...this._video.params });
}
_onPageCreated(page) {
const tab2 = new Tab(this, page, (tab3) => this._onPageClosed(tab3));
this._tabs.push(tab2);
if (!this._currentTab)
this._currentTab = tab2;
this._startPageVideo(page).catch(() => {
});
}
_onPageClosed(tab2) {
const index = this._tabs.indexOf(tab2);
if (index === -1)
return;
this._tabs.splice(index, 1);
if (this._currentTab === tab2)
this._currentTab = this._tabs[Math.min(index, this._tabs.length - 1)];
}
routes() {
return this._routes;
}
async addRoute(entry) {
const browserContext = await this.ensureBrowserContext();
await browserContext.route(entry.pattern, entry.handler);
this._routes.push(entry);
}
async removeRoute(pattern) {
let removed = 0;
const browserContext = await this.ensureBrowserContext();
if (pattern) {
const toRemove = this._routes.filter((r) => r.pattern === pattern);
for (const route2 of toRemove)
await browserContext.unroute(route2.pattern, route2.handler);
this._routes = this._routes.filter((r) => r.pattern !== pattern);
removed = toRemove.length;
} else {
for (const route2 of this._routes)
await browserContext.unroute(route2.pattern, route2.handler);
removed = this._routes.length;
this._routes = [];
}
return removed;
}
isRunningTool() {
return this._runningToolName !== void 0;
}
setRunningTool(name) {
this._runningToolName = name;
}
async _setupRequestInterception(context2) {
if (this.config.network?.allowedOrigins?.length) {
this._disposables.push(await context2.route("**", (route2) => route2.abort("blockedbyclient")));
for (const origin of this.config.network.allowedOrigins) {
const glob = originOrHostGlob(origin);
this._disposables.push(await context2.route(glob, (route2) => route2.continue()));
}
}
if (this.config.network?.blockedOrigins?.length) {
for (const origin of this.config.network.blockedOrigins)
this._disposables.push(await context2.route(originOrHostGlob(origin), (route2) => route2.abort("blockedbyclient")));
}
}
async ensureBrowserContext() {
if (this._browserContextPromise)
return this._browserContextPromise;
this._browserContextPromise = this._initializeBrowserContext();
return this._browserContextPromise;
}
async _initializeBrowserContext() {
if (this.config.testIdAttribute)
playwright.selectors.setTestIdAttribute(this.config.testIdAttribute);
const browserContext = this._rawBrowserContext;
await this._setupRequestInterception(browserContext);
if (this.config.saveTrace) {
await browserContext.tracing.start({
name: "trace-" + Date.now(),
screenshots: true,
snapshots: true,
live: true
});
this._disposables.push({
dispose: async () => {
await browserContext.tracing.stop();
}
});
}
for (const initScript of this.config.browser?.initScript || [])
this._disposables.push(await browserContext.addInitScript({ path: import_path39.default.resolve(this.options.cwd, initScript) }));
for (const page of browserContext.pages())
this._onPageCreated(page);
this._disposables.push(eventsHelper.addEventListener(browserContext, "page", (page) => this._onPageCreated(page)));
return browserContext;
}
checkUrlAllowed(url2) {
if (this.config.allowUnrestrictedFileAccess)
return;
if (!URL.canParse(url2))
return;
if (new URL(url2).protocol === "file:")
throw new Error(`Access to "file:" protocol is blocked. Attempted URL: "${url2}"`);
}
lookupSecret(secretName) {
if (!this.config.secrets?.[secretName])
return { value: secretName, code: escapeWithQuotes(secretName, "'") };
return {
value: this.config.secrets[secretName],
code: `process.env['${secretName}']`
};
}
};
}
});
// packages/playwright-core/src/tools/backend/screenshot.ts
function scaleImageToFitMessage(buffer, imageType) {
const image = imageType === "png" ? PNG3.sync.read(buffer) : jpegjs4.decode(buffer, { maxMemoryUsageInMB: 512 });
const pixels = image.width * image.height;
const shrink = Math.min(1568 / image.width, 1568 / image.height, Math.sqrt(1.15 * 1024 * 1024 / pixels));
if (shrink > 1)
return buffer;
const width = image.width * shrink | 0;
const height = image.height * shrink | 0;
const scaledImage = scaleImageToSize(image, { width, height });
return imageType === "png" ? PNG3.sync.write(scaledImage) : jpegjs4.encode(scaledImage, 80).data;
}
var jpegjs4, PNG3, z4, screenshotSchema, screenshot, screenshot_default;
var init_screenshot = __esm({
"packages/playwright-core/src/tools/backend/screenshot.ts"() {
"use strict";
init_stringUtils();
init_imageUtils();
init_tool();
init_snapshot();
jpegjs4 = require("./utilsBundle").jpegjs;
({ PNG: PNG3 } = require("./utilsBundle"));
z4 = require("./utilsBundle").z;
screenshotSchema = optionalElementSchema.extend({
type: z4.enum(["png", "jpeg"]).default("png").describe("Image format for the screenshot. Default is png."),
filename: z4.string().optional().describe("File name to save the screenshot to. Defaults to `page-{timestamp}.{png|jpeg}` if not specified. Prefer relative file names to stay within the output directory."),
fullPage: z4.boolean().optional().describe("When true, takes a screenshot of the full scrollable page, instead of the currently visible viewport. Cannot be used with element screenshots.")
});
screenshot = defineTabTool({
capability: "core",
schema: {
name: "browser_take_screenshot",
title: "Take a screenshot",
description: `Take a screenshot of the current page. You can't perform actions based on the screenshot, use browser_snapshot for actions.`,
inputSchema: screenshotSchema,
type: "readOnly"
},
handle: async (tab2, params2, response2) => {
if (params2.fullPage && params2.target)
throw new Error("fullPage cannot be used with element screenshots.");
const fileType = params2.type || "png";
const options2 = {
type: fileType,
quality: fileType === "png" ? void 0 : 90,
scale: "css",
...tab2.actionTimeoutOptions,
...params2.fullPage !== void 0 && { fullPage: params2.fullPage }
};
const screenshotTargetLabel = params2.target ? params2.element || "element" : params2.fullPage ? "full page" : "viewport";
const target = params2.target ? await tab2.targetLocator({ element: params2.element, target: params2.target }) : null;
const data = target ? await target.locator.screenshot(options2) : await tab2.page.screenshot(options2);
const resolvedFile = await response2.resolveClientFile({ prefix: target ? "element" : "page", ext: fileType, suggestedFilename: params2.filename }, `Screenshot of ${screenshotTargetLabel}`);
response2.addCode(`// Screenshot ${screenshotTargetLabel} and save it as ${resolvedFile.relativeName}`);
if (target)
response2.addCode(`await page.${target.resolved}.screenshot(${formatObject({ ...options2, path: resolvedFile.relativeName })});`);
else
response2.addCode(`await page.screenshot(${formatObject({ ...options2, path: resolvedFile.relativeName })});`);
await response2.addFileResult(resolvedFile, data);
if (!params2.filename)
await response2.registerImageResult(data, fileType);
}
});
screenshot_default = [
screenshot
];
}
});
// packages/playwright-core/src/tools/backend/response.ts
function renderTabMarkdown(tab2) {
const lines = [`- Page URL: ${tab2.url}`];
if (tab2.title)
lines.push(`- Page Title: ${tab2.title}`);
if (tab2.crashed)
lines.push(`- Page status: crashed`);
if (tab2.console.errors || tab2.console.warnings)
lines.push(`- Console: ${tab2.console.errors} errors, ${tab2.console.warnings} warnings`);
return lines;
}
function renderTabsMarkdown(tabs) {
if (!tabs.length)
return ["No open tabs. Navigate to a URL to create one."];
const lines = [];
for (let i = 0; i < tabs.length; i++) {
const tab2 = tabs[i];
const current = tab2.current ? " (current)" : "";
const crashed = tab2.crashed ? " [crashed]" : "";
lines.push(`- ${i}:${current} [${tab2.title}](${tab2.url})${crashed}`);
}
return lines;
}
function sanitizeUnicode(text2) {
return text2.toWellFormed?.() ?? text2;
}
function parseSections(text2) {
const sections = /* @__PURE__ */ new Map();
const sectionHeaders = text2.split(/^### /m).slice(1);
for (const section of sectionHeaders) {
const firstNewlineIndex = section.indexOf("\n");
if (firstNewlineIndex === -1)
continue;
const sectionName = section.substring(0, firstNewlineIndex);
const sectionContent = section.substring(firstNewlineIndex + 1).trim();
sections.set(sectionName, sectionContent);
}
return sections;
}
function parseResponse(response2, cwd) {
if (response2.content?.[0].type !== "text")
return void 0;
const text2 = response2.content[0].text;
const sections = parseSections(text2);
const error = sections.get("Error");
const result2 = sections.get("Result");
const code = sections.get("Ran Playwright code");
const tabs = sections.get("Open tabs");
const page = sections.get("Page");
const snapshotSection = sections.get("Snapshot");
const events = sections.get("Events");
const modalState = sections.get("Modal state");
const paused = sections.get("Paused");
const codeNoFrame = code?.replace(/^```js\n/, "").replace(/\n```$/, "");
const isError4 = response2.isError;
const attachments = response2.content.length > 1 ? response2.content.slice(1) : void 0;
let snapshot3;
let inlineSnapshot;
if (snapshotSection) {
const match = snapshotSection.match(/\[Snapshot\]\(([^)]+)\)/);
if (match) {
if (cwd) {
try {
snapshot3 = import_fs43.default.readFileSync(import_path40.default.resolve(cwd, match[1]), "utf-8");
} catch {
}
}
} else {
inlineSnapshot = snapshotSection.replace(/^```yaml\n?/, "").replace(/\n?```$/, "");
}
}
return {
result: result2,
error,
code: codeNoFrame,
tabs,
page,
snapshot: snapshot3,
inlineSnapshot,
events,
modalState,
paused,
isError: isError4,
attachments,
text: text2
};
}
var import_fs43, import_path40, debug8, requestDebug, Response4;
var init_response = __esm({
"packages/playwright-core/src/tools/backend/response.ts"() {
"use strict";
import_fs43 = __toESM(require("fs"));
import_path40 = __toESM(require("path"));
init_tab();
init_screenshot();
debug8 = require("./utilsBundle").debug;
requestDebug = debug8("pw:mcp:request");
Response4 = class {
constructor(context2, toolName, toolArgs, options2) {
this._results = [];
this._errors = [];
this._code = [];
this._includeSnapshot = "none";
this._isClose = false;
this._imageResults = [];
this._context = context2;
this.toolName = toolName;
this.toolArgs = toolArgs;
this._clientWorkspace = options2?.relativeTo ?? context2.options.cwd;
this._json = options2?.json ?? false;
this._raw = this._json || (options2?.raw ?? false);
}
_computeRelativeTo(fileName) {
const rel = import_path40.default.relative(this._clientWorkspace, fileName);
if (import_path40.default.dirname(rel) === "." && !rel.startsWith("."))
return "./" + rel;
return rel;
}
async resolveClientFile(template, title) {
let fileName;
if (template.suggestedFilename)
fileName = await this.resolveClientFilename(template.suggestedFilename);
else
fileName = await this._context.outputFile(template, { origin: "llm" });
const relativeName = this._computeRelativeTo(fileName);
const printableLink = `- [${title}](${relativeName})`;
return { fileName, relativeName, printableLink };
}
async resolveClientFilename(filename) {
return await this._context.workspaceFile(filename, this._clientWorkspace);
}
addTextResult(text2) {
this._results.push(text2);
}
async addResult(title, data, file) {
if (file.suggestedFilename || typeof data !== "string") {
const resolvedFile = await this.resolveClientFile(file, title);
await this.addFileResult(resolvedFile, data);
} else {
this.addTextResult(data);
}
}
async _writeFile(resolvedFile, data) {
if (typeof data === "string")
await import_fs43.default.promises.writeFile(resolvedFile.fileName, this._redactSecrets(data), "utf-8");
else if (data)
await import_fs43.default.promises.writeFile(resolvedFile.fileName, data);
}
async addFileResult(resolvedFile, data) {
await this._writeFile(resolvedFile, data);
this.addTextResult(resolvedFile.printableLink);
}
addFileLink(title, fileName) {
const relativeName = this._computeRelativeTo(fileName);
this.addTextResult(`- [${title}](${relativeName})`);
}
async registerImageResult(data, imageType) {
this._imageResults.push({ data, imageType });
}
setClose() {
this._isClose = true;
}
addError(error) {
this._errors.push(error);
}
addCode(code) {
this._code.push(code);
}
setIncludeSnapshot() {
this._includeSnapshot = this._context.config.snapshot?.mode ?? "full";
}
setIncludeFullSnapshot(includeSnapshotFileName, root, depth, boxes) {
this._includeSnapshot = "explicit";
this._includeSnapshotFileName = includeSnapshotFileName;
this._includeSnapshotDepth = depth;
this._includeSnapshotBoxes = boxes;
this._includeSnapshotRoot = root;
}
_redactSecrets(text2) {
for (const [secretName, secretValue] of Object.entries(this._context.config.secrets ?? {})) {
if (!secretValue)
continue;
text2 = text2.replaceAll(secretValue, `<secret>${secretName}</secret>`);
}
return text2;
}
async serialize() {
const allSections = await this._build();
const rawSections = ["Error", "Result", "Snapshot"];
const sections = this._raw ? allSections.filter((section) => rawSections.includes(section.title)) : allSections;
let serializedText;
if (this._json) {
const payload = {};
const isError4 = sections.some((section) => section.isError);
if (isError4)
payload.isError = true;
for (const section of sections) {
if (!section.content.length)
continue;
const key = section.title.toLowerCase();
if (key === "snapshot") {
const match = section.content[0]?.match(/^- \[Snapshot\]\(([^)]+)\)$/);
payload.snapshot = match ? { file: match[1] } : section.content.join("\n");
} else {
payload[key] = section.content.join("\n");
}
}
serializedText = JSON.stringify(payload, null, 2);
} else {
const text2 = [];
for (const section of sections) {
if (!section.content.length)
continue;
if (!this._raw) {
text2.push(`### ${section.title}`);
if (section.codeframe)
text2.push(`\`\`\`${section.codeframe}`);
text2.push(...section.content);
if (section.codeframe)
text2.push("```");
} else {
text2.push(...section.content);
}
}
serializedText = text2.join("\n");
}
const content = [
{
type: "text",
text: sanitizeUnicode(this._redactSecrets(serializedText))
}
];
if (this._context.config.imageResponses !== "omit") {
for (const imageResult of this._imageResults) {
const scaledData = scaleImageToFitMessage(imageResult.data, imageResult.imageType);
content.push({ type: "image", data: scaledData.toString("base64"), mimeType: imageResult.imageType === "png" ? "image/png" : "image/jpeg" });
}
}
return {
content,
...this._isClose ? { isClose: true } : {},
...sections.some((section) => section.isError) ? { isError: true } : {}
};
}
async _build() {
const sections = [];
const addSection = (title, content, codeframe) => {
const section = { title, content, isError: title === "Error", codeframe };
sections.push(section);
return content;
};
if (this._errors.length)
addSection("Error", this._errors);
if (this._results.length)
addSection("Result", this._results);
if (this._context.config.codegen !== "none" && this._code.length)
addSection("Ran Playwright code", this._code, "js");
const tabSnapshot = this._context.currentTab() ? await this._context.currentTabOrDie().captureSnapshot(this._includeSnapshotRoot, this._includeSnapshotDepth, this._includeSnapshotBoxes, this._clientWorkspace) : void 0;
const tabHeaders = await Promise.all(this._context.tabs().map((tab2) => tab2.headerSnapshot()));
if (this._includeSnapshot !== "none" || tabHeaders.some((header) => header.changed)) {
if (tabHeaders.length !== 1)
addSection("Open tabs", renderTabsMarkdown(tabHeaders));
addSection("Page", renderTabMarkdown(tabHeaders.find((h) => h.current) ?? tabHeaders[0]));
}
if (this._context.tabs().length === 0)
this._isClose = true;
if (tabSnapshot?.modalStates.length)
addSection("Modal state", renderModalStates(this._context.config, tabSnapshot.modalStates));
if (tabSnapshot && this._includeSnapshot !== "none") {
if (this._includeSnapshot !== "explicit" || this._includeSnapshotFileName) {
const suggestedFilename = this._includeSnapshotFileName === "<auto>" ? void 0 : this._includeSnapshotFileName;
const resolvedFile = await this.resolveClientFile({ prefix: "page", ext: "yml", suggestedFilename }, "Snapshot");
await this._writeFile(resolvedFile, tabSnapshot.ariaSnapshot);
addSection("Snapshot", [resolvedFile.printableLink]);
} else {
addSection("Snapshot", [tabSnapshot.ariaSnapshot], "yaml");
}
}
const text2 = [];
if (tabSnapshot?.consoleLink)
text2.push(`- New console entries: ${tabSnapshot.consoleLink}`);
if (tabSnapshot?.events.filter((event) => event.type !== "request").length) {
for (const event of tabSnapshot.events) {
if (event.type === "download-start")
text2.push(`- Downloading file ${event.download.download.suggestedFilename()} ...`);
else if (event.type === "download-finish")
text2.push(`- Downloaded file ${event.download.download.suggestedFilename()} to "${this._computeRelativeTo(event.download.outputFile)}"`);
}
}
if (text2.length)
addSection("Events", text2);
const pausedDetails = this._context.debugger().pausedDetails();
if (pausedDetails) {
addSection("Paused", [
`- ${pausedDetails.title} at ${this._computeRelativeTo(pausedDetails.location.file)}${pausedDetails.location.line ? ":" + pausedDetails.location.line : ""}`,
"- Use any tools to explore and interact, resume by calling resume/step-over/pause-at"
]);
}
return sections;
}
};
}
});
// packages/playwright-core/src/tools/backend/sessionLog.ts
var import_fs44, import_path41, SessionLog;
var init_sessionLog = __esm({
"packages/playwright-core/src/tools/backend/sessionLog.ts"() {
"use strict";
import_fs44 = __toESM(require("fs"));
import_path41 = __toESM(require("path"));
init_context();
init_response();
SessionLog = class _SessionLog {
constructor(sessionFolder, cwd) {
this._sessionFileQueue = Promise.resolve();
this._folder = sessionFolder;
this._file = import_path41.default.join(this._folder, "session.md");
this._cwd = cwd;
}
static async create(config, cwd) {
const sessionFolder = await outputFile({ config, cwd }, `session-${Date.now()}`, { origin: "code" });
await import_fs44.default.promises.mkdir(sessionFolder, { recursive: true });
console.error(`Session: ${sessionFolder}`);
return new _SessionLog(sessionFolder, cwd);
}
logResponse(toolName, toolArgs, responseObject) {
const parsed = { ...parseResponse(responseObject, this._cwd), text: void 0 };
const lines = [""];
lines.push(
`### Tool call: ${toolName}`,
`- Args`,
"```json",
JSON.stringify(toolArgs, null, 2),
"```"
);
if (parsed) {
lines.push(`- Result`);
lines.push("```json");
lines.push(JSON.stringify(parsed, null, 2));
lines.push("```");
}
lines.push("");
this._sessionFileQueue = this._sessionFileQueue.then(() => import_fs44.default.promises.appendFile(this._file, lines.join("\n")));
}
};
}
});
// packages/playwright-core/src/tools/backend/browserBackend.ts
function formatRejectionReason(reason) {
if (reason instanceof Error)
return reason.stack ?? reason.message;
return String(reason);
}
var debug9, BrowserBackend;
var init_browserBackend = __esm({
"packages/playwright-core/src/tools/backend/browserBackend.ts"() {
"use strict";
init_context();
init_response();
init_sessionLog();
debug9 = require("./utilsBundle").debug;
BrowserBackend = class {
constructor(config, browserContext, tools) {
this._disconnected = false;
this._config = config;
this._tools = tools;
this.browserContext = browserContext;
const markDisconnected = () => {
this._disconnected = true;
};
this.browserContext.once("close", markDisconnected);
this.browserContext.browser()?.once("disconnected", markDisconnected);
}
async initialize(clientInfo) {
this._sessionLog = this._config.saveSession ? await SessionLog.create(this._config, clientInfo.cwd) : void 0;
this._context = new Context(this.browserContext, {
config: this._config,
sessionLog: this._sessionLog,
cwd: clientInfo.cwd
});
}
async dispose() {
await this._context?.dispose().catch((e) => debug9("pw:tools:error")(e));
}
async callTool(name, rawArguments = {}, signal) {
const json = !!rawArguments._meta?.json;
const formatError = (message) => ({
content: [{ type: "text", text: json ? JSON.stringify({ isError: true, error: message }, null, 2) : `### Error
${message}` }],
isError: true
});
const tool = this._tools.find((tool2) => tool2.schema.name === name);
if (!tool)
return formatError(`Tool "${name}" not found`);
const parsedArguments = tool.schema.inputSchema.parse(rawArguments);
const cwd = rawArguments._meta?.cwd;
const raw = !!rawArguments._meta?.raw;
const context2 = this._context;
const response2 = new Response4(context2, name, parsedArguments, { relativeTo: cwd, raw, json });
context2.setRunningTool(name);
let responseObject;
try {
await tool.handle(context2, parsedArguments, response2, signal);
for (const reason of context2.drainPendingUnhandledRejections())
response2.addError(formatRejectionReason(reason));
responseObject = await response2.serialize();
this._sessionLog?.logResponse(name, parsedArguments, responseObject);
} catch (error) {
const messages = [String(error), ...context2.drainPendingUnhandledRejections().map(formatRejectionReason)];
responseObject = formatError(messages.join("\n\n"));
} finally {
context2.setRunningTool(void 0);
}
if (this._disconnected)
responseObject.isClose = true;
return responseObject;
}
};
}
});
// packages/playwright-core/src/tools/backend/common.ts
var z5, close, resize, common_default;
var init_common = __esm({
"packages/playwright-core/src/tools/backend/common.ts"() {
"use strict";
init_tool();
init_response();
z5 = require("./utilsBundle").z;
close = defineTool({
capability: "core",
schema: {
name: "browser_close",
title: "Close browser",
description: "Close the page",
inputSchema: z5.object({}),
type: "action"
},
handle: async (context2, params2, response2) => {
const result2 = renderTabsMarkdown([]);
response2.addTextResult(result2.join("\n"));
response2.addCode(`await page.close()`);
response2.setClose();
}
});
resize = defineTabTool({
capability: "core",
schema: {
name: "browser_resize",
title: "Resize browser window",
description: "Resize the browser window",
inputSchema: z5.object({
width: z5.number().describe("Width of the browser window"),
height: z5.number().describe("Height of the browser window")
}),
type: "action"
},
handle: async (tab2, params2, response2) => {
response2.addCode(`await page.setViewportSize({ width: ${params2.width}, height: ${params2.height} });`);
await tab2.page.setViewportSize({ width: params2.width, height: params2.height });
}
});
common_default = [
close,
resize
];
}
});
// packages/playwright-core/src/tools/backend/config.ts
var z6, configShow, config_default;
var init_config = __esm({
"packages/playwright-core/src/tools/backend/config.ts"() {
"use strict";
init_tool();
z6 = require("./utilsBundle").z;
configShow = defineTool({
capability: "config",
schema: {
name: "browser_get_config",
title: "Get config",
description: "Get the final resolved config after merging CLI options, environment variables and config file.",
inputSchema: z6.object({}),
type: "readOnly"
},
handle: async (context2, params2, response2) => {
response2.addTextResult(JSON.stringify(context2.config, null, 2));
}
});
config_default = [
configShow
];
}
});
// packages/playwright-core/src/tools/backend/console.ts
var z7, console2, consoleClear, console_default;
var init_console2 = __esm({
"packages/playwright-core/src/tools/backend/console.ts"() {
"use strict";
init_tool();
z7 = require("./utilsBundle").z;
console2 = defineTabTool({
capability: "core",
schema: {
name: "browser_console_messages",
title: "Get console messages",
description: "Returns all console messages",
inputSchema: z7.object({
level: z7.enum(["error", "warning", "info", "debug"]).default("info").describe('Level of the console messages to return. Each level includes the messages of more severe levels. Defaults to "info".'),
all: z7.boolean().optional().describe("Return all console messages since the beginning of the session, not just since the last navigation. Defaults to false."),
filename: z7.string().optional().describe("Filename to save the console messages to. If not provided, messages are returned as text.")
}),
type: "readOnly"
},
handle: async (tab2, params2, response2) => {
const count = await tab2.consoleMessageCount();
const header = [`Total messages: ${count.total} (Errors: ${count.errors}, Warnings: ${count.warnings})`];
const messages = await tab2.consoleMessages(params2.level, params2.all);
if (messages.length !== count.total)
header.push(`Returning ${messages.length} messages for level "${params2.level}"`);
const text2 = [...header, "", ...messages.map((message) => message.toString())].join("\n");
await response2.addResult("Console", text2, { prefix: "console", ext: "log", suggestedFilename: params2.filename });
}
});
consoleClear = defineTabTool({
capability: "core",
skillOnly: true,
schema: {
name: "browser_console_clear",
title: "Clear console messages",
description: "Clear all console messages",
inputSchema: z7.object({}),
type: "readOnly"
},
handle: async (tab2) => {
await tab2.clearConsoleMessages();
}
});
console_default = [
console2,
consoleClear
];
}
});
// packages/playwright-core/src/tools/backend/cookies.ts
var z8, cookieList, cookieGet, cookieSet, cookieDelete, cookieClear, cookies_default;
var init_cookies = __esm({
"packages/playwright-core/src/tools/backend/cookies.ts"() {
"use strict";
init_tool();
z8 = require("./utilsBundle").z;
cookieList = defineTool({
capability: "storage",
schema: {
name: "browser_cookie_list",
title: "List cookies",
description: "List all cookies (optionally filtered by domain/path)",
inputSchema: z8.object({
domain: z8.string().optional().describe("Filter cookies by domain"),
path: z8.string().optional().describe("Filter cookies by path")
}),
type: "readOnly"
},
handle: async (context2, params2, response2) => {
const browserContext = await context2.ensureBrowserContext();
let cookies = await browserContext.cookies();
if (params2.domain)
cookies = cookies.filter((c) => c.domain.includes(params2.domain));
if (params2.path)
cookies = cookies.filter((c) => c.path.startsWith(params2.path));
if (cookies.length === 0)
response2.addTextResult("No cookies found");
else
response2.addTextResult(cookies.map((c) => `${c.name}=${c.value} (domain: ${c.domain}, path: ${c.path})`).join("\n"));
response2.addCode(`await page.context().cookies();`);
}
});
cookieGet = defineTool({
capability: "storage",
schema: {
name: "browser_cookie_get",
title: "Get cookie",
description: "Get a specific cookie by name",
inputSchema: z8.object({
name: z8.string().describe("Cookie name to get")
}),
type: "readOnly"
},
handle: async (context2, params2, response2) => {
const browserContext = await context2.ensureBrowserContext();
const cookies = await browserContext.cookies();
const cookie = cookies.find((c) => c.name === params2.name);
if (!cookie)
response2.addTextResult(`Cookie '${params2.name}' not found`);
else
response2.addTextResult(`${cookie.name}=${cookie.value} (domain: ${cookie.domain}, path: ${cookie.path}, httpOnly: ${cookie.httpOnly}, secure: ${cookie.secure}, sameSite: ${cookie.sameSite})`);
response2.addCode(`await page.context().cookies();`);
}
});
cookieSet = defineTool({
capability: "storage",
schema: {
name: "browser_cookie_set",
title: "Set cookie",
description: "Set a cookie with optional flags (domain, path, expires, httpOnly, secure, sameSite)",
inputSchema: z8.object({
name: z8.string().describe("Cookie name"),
value: z8.string().describe("Cookie value"),
domain: z8.string().optional().describe("Cookie domain"),
path: z8.string().optional().describe("Cookie path"),
expires: z8.number().optional().describe("Cookie expiration as Unix timestamp"),
httpOnly: z8.boolean().optional().describe("Whether the cookie is HTTP only"),
secure: z8.boolean().optional().describe("Whether the cookie is secure"),
sameSite: z8.enum(["Strict", "Lax", "None"]).optional().describe("Cookie SameSite attribute")
}),
type: "action"
},
handle: async (context2, params2, response2) => {
const browserContext = await context2.ensureBrowserContext();
const tab2 = await context2.ensureTab();
const url2 = new URL(tab2.page.url());
const cookie = {
name: params2.name,
value: params2.value,
domain: params2.domain || url2.hostname,
path: params2.path || "/"
};
if (params2.expires !== void 0)
cookie.expires = params2.expires;
if (params2.httpOnly !== void 0)
cookie.httpOnly = params2.httpOnly;
if (params2.secure !== void 0)
cookie.secure = params2.secure;
if (params2.sameSite !== void 0)
cookie.sameSite = params2.sameSite;
await browserContext.addCookies([cookie]);
response2.addCode(`await page.context().addCookies([${JSON.stringify(cookie)}]);`);
}
});
cookieDelete = defineTool({
capability: "storage",
schema: {
name: "browser_cookie_delete",
title: "Delete cookie",
description: "Delete a specific cookie",
inputSchema: z8.object({
name: z8.string().describe("Cookie name to delete")
}),
type: "action"
},
handle: async (context2, params2, response2) => {
const browserContext = await context2.ensureBrowserContext();
await browserContext.clearCookies({ name: params2.name });
response2.addCode(`await page.context().clearCookies({ name: '${params2.name}' });`);
}
});
cookieClear = defineTool({
capability: "storage",
schema: {
name: "browser_cookie_clear",
title: "Clear cookies",
description: "Clear all cookies",
inputSchema: z8.object({}),
type: "action"
},
handle: async (context2, params2, response2) => {
const browserContext = await context2.ensureBrowserContext();
await browserContext.clearCookies();
response2.addCode(`await page.context().clearCookies();`);
}
});
cookies_default = [
cookieList,
cookieGet,
cookieSet,
cookieDelete,
cookieClear
];
}
});
// packages/playwright-core/src/tools/backend/devtools.ts
var import_child_process3, z9, resume, highlight, hideHighlight, annotate, devtools_default;
var init_devtools = __esm({
"packages/playwright-core/src/tools/backend/devtools.ts"() {
"use strict";
import_child_process3 = require("child_process");
init_package();
init_tool();
init_snapshot();
z9 = require("./utilsBundle").z;
resume = defineTool({
capability: "devtools",
schema: {
name: "browser_resume",
title: "Resume paused script execution",
description: "Resume script execution after it was paused. When called with step set to true, execution will pause again before the next action.",
inputSchema: z9.object({
step: z9.boolean().optional().describe("When true, execution will pause again before the next action, allowing step-by-step debugging."),
location: z9.string().optional().describe('Pause execution at a specific <file>:<line>, e.g. "example.spec.ts:42".')
}),
type: "action"
},
handle: async (context2, params2, response2) => {
const browserContext = await context2.ensureBrowserContext();
const pausedPromise = new Promise((resolve) => {
const listener = () => {
if (browserContext.debugger.pausedDetails()) {
browserContext.debugger.off("pausedstatechanged", listener);
resolve();
}
};
browserContext.debugger.on("pausedstatechanged", listener);
});
if (params2.location) {
const [file, lineStr] = params2.location.split(":");
let location2;
if (lineStr) {
const line = Number(lineStr);
if (isNaN(line))
throw new Error(`Invalid location "${params2.location}", expected format is <file>:<line>, e.g. "example.spec.ts:42"`);
location2 = { file, line };
} else {
location2 = { file: params2.location };
}
await browserContext.debugger.runTo(location2);
} else if (params2.step) {
await browserContext.debugger.next();
} else {
await browserContext.debugger.resume();
}
await pausedPromise;
}
});
highlight = defineTabTool({
capability: "devtools",
schema: {
name: "browser_highlight",
title: "Highlight element",
description: "Show a persistent highlight overlay around the element on the page.",
inputSchema: elementSchema.extend({
style: z9.string().optional().describe('Additional inline CSS applied to the highlight overlay, e.g. "outline: 2px dashed red".')
}),
type: "readOnly"
},
handle: async (tab2, params2, response2) => {
const { locator: locator2 } = await tab2.targetLocator(params2);
await locator2.highlight({ style: params2.style });
response2.addTextResult(`Highlighted ${locator2}`);
}
});
hideHighlight = defineTabTool({
capability: "devtools",
schema: {
name: "browser_hide_highlight",
title: "Hide element highlight",
description: "Remove a highlight overlay previously added for the element.",
inputSchema: optionalElementSchema.extend({
element: z9.string().optional().describe("Human-readable element description used when adding the highlight; must match the value passed to browser_highlight.")
}),
type: "readOnly"
},
handle: async (tab2, params2, response2) => {
if (params2.target) {
const { locator: locator2 } = await tab2.targetLocator({ target: params2.target, element: params2.element });
await locator2.hideHighlight();
response2.addTextResult(`Hid highlight for ${locator2}`);
} else {
await tab2.page.hideHighlight();
response2.addTextResult(`Hid page highlight`);
}
}
});
annotate = defineTabTool({
capability: "devtools",
schema: {
name: "browser_annotate",
title: "Annotate the current page",
description: "Open the Playwright Dashboard in annotation mode for the current page and wait for the user to draw annotations. Returns the annotated screenshot, ARIA snapshot, and the list of annotations.",
inputSchema: z9.object({}),
type: "readOnly"
},
handle: async (tab2, params2, response2, signal) => {
const pageId4 = tab2.page._guid;
const daemonScript = libPath("entry", "dashboardApp.js");
const daemonArgs = [daemonScript, `--pageId=${pageId4}`];
const daemon = (0, import_child_process3.spawn)(process.execPath, daemonArgs, { detached: true, stdio: "ignore" });
daemon.unref();
const client = (0, import_child_process3.spawn)(process.execPath, [...daemonArgs, "--annotate"], {
stdio: ["pipe", "pipe", "inherit"]
});
const onAbort = () => client.kill();
signal?.addEventListener("abort", onAbort);
const stdoutChunks = [];
client.stdout.on("data", (chunk) => stdoutChunks.push(chunk));
const exitCode = await new Promise((resolve) => client.on("exit", (code) => resolve(code)));
signal?.removeEventListener("abort", onAbort);
if (signal?.aborted) {
response2.addTextResult("Annotation cancelled.");
return;
}
if (exitCode !== 0) {
response2.addError(`Annotation client exited with code ${exitCode}`);
return;
}
const text2 = Buffer.concat(stdoutChunks).toString("utf8").trim();
if (!text2) {
response2.addTextResult("No annotations were submitted.");
return;
}
const { frames, feedback } = JSON.parse(text2);
if (!frames || frames.length === 0) {
response2.addTextResult("No annotations were submitted.");
return;
}
const date = /* @__PURE__ */ new Date();
if (feedback)
response2.addTextResult(feedback);
const multi = frames.length > 1;
for (let i = 0; i < frames.length; i++) {
const frame = frames[i];
const idx = i + 1;
const session2 = frame.sessionTitle || "session";
const tab3 = frame.title || "tab";
response2.addTextResult(`${multi ? `## Screenshot ${idx}
` : ""}${session2} / ${tab3} @ ${frame.url} (${frame.viewportWidth}x${frame.viewportHeight})`);
for (const a of frame.annotations)
response2.addTextResult(` { x: ${a.x}, y: ${a.y}, width: ${a.width}, height: ${a.height} }: ${a.text}`);
if (frame.data)
await response2.addResult(`Annotation image${multi ? " " + idx : ""}`, Buffer.from(frame.data, "base64"), { prefix: `annotations${multi ? "-" + idx : ""}`, ext: "png", date });
if (frame.ariaSnapshot)
await response2.addResult(`Annotation snapshot${multi ? " " + idx : ""}`, Buffer.from(frame.ariaSnapshot, "utf8"), { prefix: `annotations${multi ? "-" + idx : ""}`, ext: "yaml", date });
}
}
});
devtools_default = [resume, highlight, hideHighlight, annotate];
}
});
// packages/playwright-core/src/tools/backend/evaluate.ts
var z10, evaluateSchema, evaluate2, evaluate_default;
var init_evaluate = __esm({
"packages/playwright-core/src/tools/backend/evaluate.ts"() {
"use strict";
init_stringUtils();
init_tool();
init_snapshot();
z10 = require("./utilsBundle").z;
evaluateSchema = optionalElementSchema.extend({
function: z10.string().describe("() => { /* code */ } or (element) => { /* code */ } when element is provided"),
filename: z10.string().optional().describe("Filename to save the result to. If not provided, result is returned as text.")
});
evaluate2 = defineTabTool({
capability: "core",
schema: {
name: "browser_evaluate",
title: "Evaluate JavaScript",
description: "Evaluate JavaScript expression on page or element",
inputSchema: evaluateSchema,
type: "action"
},
handle: async (tab, params, response) => {
let locator;
const expression = params.function;
if (params.target)
locator = await tab.targetLocator({ target: params.target, element: params.element || "element" });
await tab.waitForCompletion(async () => {
let evalResult;
if (locator?.locator) {
evalResult = await locator.locator.evaluate(async (element, expr) => {
const value = eval(`(${expr})`);
const isFunction = typeof value === "function";
const result = await (isFunction ? value(element) : value);
return { result, isFunction };
}, expression);
} else {
evalResult = await tab.page.evaluate(async (expr) => {
const value = eval(`(${expr})`);
const isFunction = typeof value === "function";
const result = await (isFunction ? value() : value);
return { result, isFunction };
}, expression);
}
const codeExpression = evalResult.isFunction ? expression : `() => (${expression})`;
if (locator)
response.addCode(`await page.${locator.resolved}.evaluate(${escapeWithQuotes(codeExpression)});`);
else
response.addCode(`await page.evaluate(${escapeWithQuotes(codeExpression)});`);
const text = JSON.stringify(evalResult.result, null, 2) ?? "undefined";
await response.addResult("Evaluation result", text, { prefix: "result", ext: "json", suggestedFilename: params.filename });
}).catch((e) => {
response.addError(e instanceof Error ? e.message : String(e));
});
}
});
evaluate_default = [
evaluate2
];
}
});
// packages/playwright-core/src/tools/backend/form.ts
var z11, fillForm, form_default;
var init_form = __esm({
"packages/playwright-core/src/tools/backend/form.ts"() {
"use strict";
init_stringUtils();
init_tool();
init_snapshot();
z11 = require("./utilsBundle").z;
fillForm = defineTabTool({
capability: "core",
schema: {
name: "browser_fill_form",
title: "Fill form",
description: "Fill multiple form fields",
inputSchema: z11.object({
fields: z11.array(elementSchema.extend({
name: z11.string().describe("Human-readable field name"),
type: z11.enum(["textbox", "checkbox", "radio", "combobox", "slider"]).describe("Type of the field"),
value: z11.string().describe("Value to fill in the field. If the field is a checkbox, the value should be `true` or `false`. If the field is a combobox, the value should be the text of the option.")
})).describe("Fields to fill in")
}),
type: "input"
},
handle: async (tab2, params2, response2) => {
for (const field of params2.fields) {
const { locator: locator2, resolved } = await tab2.targetLocator({ element: field.name, target: field.target });
const locatorSource = `await page.${resolved}`;
if (field.type === "textbox" || field.type === "slider") {
const secret = tab2.context.lookupSecret(field.value);
await locator2.fill(secret.value, tab2.actionTimeoutOptions);
response2.addCode(`${locatorSource}.fill(${secret.code});`);
} else if (field.type === "checkbox" || field.type === "radio") {
await locator2.setChecked(field.value === "true", tab2.actionTimeoutOptions);
response2.addCode(`${locatorSource}.setChecked(${field.value});`);
} else if (field.type === "combobox") {
await locator2.selectOption({ label: field.value }, tab2.actionTimeoutOptions);
response2.addCode(`${locatorSource}.selectOption(${escapeWithQuotes(field.value)});`);
}
}
}
});
form_default = [
fillForm
];
}
});
// packages/playwright-core/src/tools/backend/keyboard.ts
var z12, press, pressSequentially, typeSchema, type, keydown, keyup, keyboard_default;
var init_keyboard = __esm({
"packages/playwright-core/src/tools/backend/keyboard.ts"() {
"use strict";
init_tool();
init_snapshot();
z12 = require("./utilsBundle").z;
press = defineTabTool({
capability: "core-input",
schema: {
name: "browser_press_key",
title: "Press a key",
description: "Press a key on the keyboard",
inputSchema: z12.object({
key: z12.string().describe("Name of the key to press or a character to generate, such as `ArrowLeft` or `a`")
}),
type: "input"
},
handle: async (tab2, params2, response2) => {
response2.addCode(`// Press ${params2.key}`);
response2.addCode(`await page.keyboard.press('${params2.key}');`);
if (params2.key === "Enter") {
response2.setIncludeSnapshot();
await tab2.waitForCompletion(async () => {
await tab2.page.keyboard.press("Enter");
});
} else {
await tab2.page.keyboard.press(params2.key);
}
}
});
pressSequentially = defineTabTool({
capability: "core-input",
skillOnly: true,
schema: {
name: "browser_press_sequentially",
title: "Type text key by key",
description: "Type text key by key on the keyboard",
inputSchema: z12.object({
text: z12.string().describe("Text to type"),
submit: z12.boolean().optional().describe("Whether to submit entered text (press Enter after)")
}),
type: "input"
},
handle: async (tab2, params2, response2) => {
response2.addCode(`// Press ${params2.text}`);
response2.addCode(`await page.keyboard.type('${params2.text}');`);
await tab2.page.keyboard.type(params2.text);
if (params2.submit) {
response2.addCode(`await page.keyboard.press('Enter');`);
response2.setIncludeSnapshot();
await tab2.waitForCompletion(async () => {
await tab2.page.keyboard.press("Enter");
});
}
}
});
typeSchema = elementSchema.extend({
text: z12.string().describe("Text to type into the element"),
submit: z12.boolean().optional().describe("Whether to submit entered text (press Enter after)"),
slowly: z12.boolean().optional().describe("Whether to type one character at a time. Useful for triggering key handlers in the page. By default entire text is filled in at once.")
});
type = defineTabTool({
capability: "core-input",
schema: {
name: "browser_type",
title: "Type text",
description: "Type text into editable element",
inputSchema: typeSchema,
type: "input"
},
handle: async (tab2, params2, response2) => {
const { locator: locator2, resolved } = await tab2.targetLocator(params2);
const secret = tab2.context.lookupSecret(params2.text);
const action = async () => {
if (params2.slowly) {
response2.setIncludeSnapshot();
response2.addCode(`await page.${resolved}.pressSequentially(${secret.code});`);
await locator2.pressSequentially(secret.value, tab2.actionTimeoutOptions);
} else {
response2.addCode(`await page.${resolved}.fill(${secret.code});`);
await locator2.fill(secret.value, tab2.actionTimeoutOptions);
}
if (params2.submit) {
response2.setIncludeSnapshot();
response2.addCode(`await page.${resolved}.press('Enter');`);
await locator2.press("Enter", tab2.actionTimeoutOptions);
}
};
if (params2.submit || params2.slowly)
await tab2.waitForCompletion(action);
else
await action();
}
});
keydown = defineTabTool({
capability: "core-input",
skillOnly: true,
schema: {
name: "browser_keydown",
title: "Press a key down",
description: "Press a key down on the keyboard",
inputSchema: z12.object({
key: z12.string().describe("Name of the key to press or a character to generate, such as `ArrowLeft` or `a`")
}),
type: "input"
},
handle: async (tab2, params2, response2) => {
response2.addCode(`await page.keyboard.down('${params2.key}');`);
await tab2.page.keyboard.down(params2.key);
}
});
keyup = defineTabTool({
capability: "core-input",
skillOnly: true,
schema: {
name: "browser_keyup",
title: "Press a key up",
description: "Press a key up on the keyboard",
inputSchema: z12.object({
key: z12.string().describe("Name of the key to press or a character to generate, such as `ArrowLeft` or `a`")
}),
type: "input"
},
handle: async (tab2, params2, response2) => {
response2.addCode(`await page.keyboard.up('${params2.key}');`);
await tab2.page.keyboard.up(params2.key);
}
});
keyboard_default = [
press,
type,
pressSequentially,
keydown,
keyup
];
}
});
// packages/playwright-core/src/tools/backend/mouse.ts
var z13, mouseMove, mouseDown, mouseUp, mouseWheel, mouseClick, mouseDrag, mouse_default;
var init_mouse = __esm({
"packages/playwright-core/src/tools/backend/mouse.ts"() {
"use strict";
init_stringUtils();
init_tool();
z13 = require("./utilsBundle").z;
mouseMove = defineTabTool({
capability: "vision",
schema: {
name: "browser_mouse_move_xy",
title: "Move mouse",
description: "Move mouse to a given position",
inputSchema: z13.object({
x: z13.number().describe("X coordinate"),
y: z13.number().describe("Y coordinate")
}),
type: "input"
},
handle: async (tab2, params2, response2) => {
response2.addCode(`// Move mouse to (${params2.x}, ${params2.y})`);
response2.addCode(`await page.mouse.move(${params2.x}, ${params2.y});`);
await tab2.page.mouse.move(params2.x, params2.y);
}
});
mouseDown = defineTabTool({
capability: "vision",
schema: {
name: "browser_mouse_down",
title: "Press mouse down",
description: "Press mouse down",
inputSchema: z13.object({
button: z13.enum(["left", "right", "middle"]).optional().describe("Button to press, defaults to left")
}),
type: "input"
},
handle: async (tab2, params2, response2) => {
const options2 = { button: params2.button };
const optionsArg = formatObjectOrVoid(options2);
response2.addCode(`// Press mouse down`);
response2.addCode(`await page.mouse.down(${optionsArg});`);
await tab2.page.mouse.down(options2);
}
});
mouseUp = defineTabTool({
capability: "vision",
schema: {
name: "browser_mouse_up",
title: "Press mouse up",
description: "Press mouse up",
inputSchema: z13.object({
button: z13.enum(["left", "right", "middle"]).optional().describe("Button to press, defaults to left")
}),
type: "input"
},
handle: async (tab2, params2, response2) => {
const options2 = { button: params2.button };
const optionsArg = formatObjectOrVoid(options2);
response2.addCode(`// Press mouse up`);
response2.addCode(`await page.mouse.up(${optionsArg});`);
await tab2.page.mouse.up(options2);
}
});
mouseWheel = defineTabTool({
capability: "vision",
schema: {
name: "browser_mouse_wheel",
title: "Scroll mouse wheel",
description: "Scroll mouse wheel",
inputSchema: z13.object({
deltaX: z13.number().default(0).describe("X delta"),
deltaY: z13.number().default(0).describe("Y delta")
}),
type: "input"
},
handle: async (tab2, params2, response2) => {
response2.addCode(`// Scroll mouse wheel`);
response2.addCode(`await page.mouse.wheel(${params2.deltaX}, ${params2.deltaY});`);
await tab2.page.mouse.wheel(params2.deltaX, params2.deltaY);
}
});
mouseClick = defineTabTool({
capability: "vision",
schema: {
name: "browser_mouse_click_xy",
title: "Click",
description: "Click mouse button at a given position",
inputSchema: z13.object({
x: z13.number().describe("X coordinate"),
y: z13.number().describe("Y coordinate"),
button: z13.enum(["left", "right", "middle"]).optional().describe("Button to click, defaults to left"),
clickCount: z13.number().optional().describe("Number of clicks, defaults to 1"),
delay: z13.number().optional().describe("Time to wait between mouse down and mouse up in milliseconds, defaults to 0")
}),
type: "input"
},
handle: async (tab2, params2, response2) => {
response2.setIncludeSnapshot();
const options2 = {
button: params2.button,
clickCount: params2.clickCount,
delay: params2.delay
};
const formatted = formatObjectOrVoid(options2);
const optionsArg = formatted ? `, ${formatted}` : "";
response2.addCode(`// Click mouse at coordinates (${params2.x}, ${params2.y})`);
response2.addCode(`await page.mouse.click(${params2.x}, ${params2.y}${optionsArg});`);
await tab2.waitForCompletion(async () => {
await tab2.page.mouse.click(params2.x, params2.y, options2);
});
}
});
mouseDrag = defineTabTool({
capability: "vision",
schema: {
name: "browser_mouse_drag_xy",
title: "Drag mouse",
description: "Drag left mouse button to a given position",
inputSchema: z13.object({
startX: z13.number().describe("Start X coordinate"),
startY: z13.number().describe("Start Y coordinate"),
endX: z13.number().describe("End X coordinate"),
endY: z13.number().describe("End Y coordinate")
}),
type: "input"
},
handle: async (tab2, params2, response2) => {
response2.setIncludeSnapshot();
response2.addCode(`// Drag mouse from (${params2.startX}, ${params2.startY}) to (${params2.endX}, ${params2.endY})`);
response2.addCode(`await page.mouse.move(${params2.startX}, ${params2.startY});`);
response2.addCode(`await page.mouse.down();`);
response2.addCode(`await page.mouse.move(${params2.endX}, ${params2.endY});`);
response2.addCode(`await page.mouse.up();`);
await tab2.waitForCompletion(async () => {
await tab2.page.mouse.move(params2.startX, params2.startY);
await tab2.page.mouse.down();
await tab2.page.mouse.move(params2.endX, params2.endY);
await tab2.page.mouse.up();
});
}
});
mouse_default = [
mouseMove,
mouseClick,
mouseDrag,
mouseDown,
mouseUp,
mouseWheel
];
}
});
// packages/playwright-core/src/tools/backend/navigate.ts
var z14, navigate, goBack, goForward, reload, navigate_default;
var init_navigate = __esm({
"packages/playwright-core/src/tools/backend/navigate.ts"() {
"use strict";
init_tool();
z14 = require("./utilsBundle").z;
navigate = defineTool({
capability: "core-navigation",
schema: {
name: "browser_navigate",
title: "Navigate to a URL",
description: "Navigate to a URL",
inputSchema: z14.object({
url: z14.string().describe("The URL to navigate to")
}),
type: "action"
},
handle: async (context2, params2, response2) => {
const tab2 = await context2.ensureTab();
const url2 = await tab2.checkUrlAndNavigate(params2.url);
response2.setIncludeSnapshot();
response2.addCode(`await page.goto('${url2}');`);
}
});
goBack = defineTabTool({
capability: "core-navigation",
schema: {
name: "browser_navigate_back",
title: "Go back",
description: "Go back to the previous page in the history",
inputSchema: z14.object({}),
type: "action"
},
handle: async (tab2, params2, response2) => {
await tab2.page.goBack(tab2.navigationTimeoutOptions);
response2.setIncludeSnapshot();
response2.addCode(`await page.goBack();`);
}
});
goForward = defineTabTool({
capability: "core-navigation",
skillOnly: true,
schema: {
name: "browser_navigate_forward",
title: "Go forward",
description: "Go forward to the next page in the history",
inputSchema: z14.object({}),
type: "action"
},
handle: async (tab2, params2, response2) => {
await tab2.page.goForward(tab2.navigationTimeoutOptions);
response2.setIncludeSnapshot();
response2.addCode(`await page.goForward();`);
}
});
reload = defineTabTool({
capability: "core-navigation",
skillOnly: true,
schema: {
name: "browser_reload",
title: "Reload the page",
description: "Reload the current page",
inputSchema: z14.object({}),
type: "action"
},
handle: async (tab2, params2, response2) => {
await tab2.page.reload(tab2.navigationTimeoutOptions);
response2.setIncludeSnapshot();
response2.addCode(`await page.reload();`);
}
});
navigate_default = [
navigate,
goBack,
goForward,
reload
];
}
});
// packages/playwright-core/src/tools/backend/network.ts
function isSuccessfulResponse(request2) {
if (request2.failure())
return false;
const response2 = request2.existingResponse();
return !!response2 && response2.status() < 400;
}
function isFetch(request2) {
return ["fetch", "xhr"].includes(request2.resourceType());
}
function renderRequestLine(request2) {
const response2 = request2.existingResponse();
let line = `[${request2.method().toUpperCase()}] ${request2.url()}`;
if (response2)
line += ` => [${response2.status()}] ${response2.statusText()}`;
else if (request2.failure())
line += ` => [FAILED] ${request2.failure()?.errorText ?? "Unknown error"}`;
return line;
}
function renderRequestDetails(index, request2, skillMode) {
const httpResponse = request2.existingResponse();
const responseHeaders = httpResponse?.headers();
const lines = [];
lines.push(`#${index} [${request2.method().toUpperCase()}] ${request2.url()}`);
lines.push("");
lines.push(" General");
if (httpResponse)
lines.push(` status: [${httpResponse.status()}] ${httpResponse.statusText()}`);
else if (request2.failure())
lines.push(` status: [FAILED] ${request2.failure()?.errorText ?? "Unknown error"}`);
const duration = computeDurationMs(request2);
if (duration !== void 0)
lines.push(` duration: ${duration}ms`);
lines.push(` type: ${request2.resourceType()}`);
const contentType = responseHeaders?.["content-type"];
if (contentType)
lines.push(` mimeType: ${contentType.split(";")[0].trim()}`);
appendHeaderSection(lines, "Request headers", request2.headers());
if (responseHeaders)
appendHeaderSection(lines, "Response headers", responseHeaders);
const hints = [];
if (request2.postData())
hints.push(partHint(skillMode, "request-body", index));
if (canHaveResponseBody(httpResponse))
hints.push(partHint(skillMode, "response-body", index));
if (hints.length)
lines.push("", ...hints);
return lines.join("\n");
}
function partHint(skillMode, part, index) {
const subject = part === "request-body" ? "request body" : "response body";
return skillMode ? `Run \`${part} ${index}\` to read the ${subject}.` : `Call browser_network_request with part="${part}" to read the ${subject}.`;
}
function canHaveResponseBody(httpResponse) {
if (!httpResponse)
return false;
const status = httpResponse.status();
return status !== 204 && status !== 304 && !(status >= 100 && status < 200);
}
function appendHeaderSection(lines, title, headers) {
const entries = Object.entries(headers);
if (!entries.length)
return;
lines.push("");
lines.push(` ${title}`);
for (const [k, v] of entries)
lines.push(` ${k}: ${v}`);
}
function computeDurationMs(request2) {
const timing = request2.timing();
if (!timing || timing.responseEnd < 0)
return void 0;
return Math.round(timing.responseEnd);
}
async function renderRequestPart(request2, part, response2, suggestedFilename) {
if (part === "request-headers") {
await response2.addResult("Request headers", renderHeaders(request2.headers()), { prefix: "request", ext: "txt", suggestedFilename });
return;
}
if (part === "request-body") {
const data = request2.postData();
if (data !== null)
await response2.addResult("Request body", data, { prefix: "request", ext: "txt", suggestedFilename });
return;
}
const httpResponse = request2.existingResponse();
if (!httpResponse)
return;
if (part === "response-headers") {
await response2.addResult("Response headers", renderHeaders(httpResponse.headers()), { prefix: "response", ext: "txt", suggestedFilename });
return;
}
const contentType = httpResponse.headers()["content-type"];
if (isTextualMimeType(contentType ?? "")) {
let text2;
try {
text2 = await httpResponse.text();
} catch {
return;
}
await response2.addResult("Response body", text2, { prefix: "response", ext: "txt", suggestedFilename });
return;
}
const path59 = await saveResponseBody(request2, response2, suggestedFilename);
if (path59 !== void 0)
response2.addTextResult(path59);
}
function renderHeaders(headers) {
return Object.entries(headers).map(([k, v]) => `${k}: ${v}`).join("\n");
}
async function saveResponseBody(request2, response2, suggestedFilename) {
const httpResponse = request2.existingResponse();
if (!canHaveResponseBody(httpResponse))
return void 0;
let body;
try {
body = await httpResponse.body();
} catch {
return void 0;
}
if (!body.length)
return void 0;
const ext = getExtensionForMimeType(httpResponse.headers()["content-type"]);
const resolved = await response2.resolveClientFile({ prefix: "response", ext, suggestedFilename }, "Response body");
await import_fs45.default.promises.writeFile(resolved.fileName, body);
return resolved.relativeName;
}
var import_fs45, z15, requests, REQUEST_PARTS, request, networkClear, networkStateSet, network_default;
var init_network4 = __esm({
"packages/playwright-core/src/tools/backend/network.ts"() {
"use strict";
import_fs45 = __toESM(require("fs"));
init_mimeType();
init_tool();
z15 = require("./utilsBundle").z;
requests = defineTabTool({
capability: "core",
schema: {
name: "browser_network_requests",
title: "List network requests",
description: "Returns a numbered list of network requests since loading the page. Use browser_network_request with the number to get full details.",
inputSchema: z15.object({
static: z15.boolean().default(false).describe("Whether to include successful static resources like images, fonts, scripts, etc. Defaults to false."),
filter: z15.string().optional().describe('Only return requests whose URL matches this regexp (e.g. "/api/.*user").'),
filename: z15.string().optional().describe("Filename to save the network requests to. If not provided, requests are returned as text.")
}),
type: "readOnly"
},
handle: async (tab2, params2, response2) => {
const allRequests = await tab2.requests();
const filter = params2.filter ? new RegExp(params2.filter) : void 0;
const lines = [];
let hiddenStaticCount = 0;
for (let i = 0; i < allRequests.length; i++) {
const request2 = allRequests[i];
if (!params2.static && !isFetch(request2) && isSuccessfulResponse(request2)) {
hiddenStaticCount++;
continue;
}
if (filter) {
filter.lastIndex = 0;
if (!filter.test(request2.url()))
continue;
}
lines.push(`${i + 1}. ${renderRequestLine(request2)}`);
}
if (hiddenStaticCount > 0) {
const optionName = tab2.context.config.skillMode ? "--static" : '"static"';
lines.push(`
Note: ${hiddenStaticCount} static request${hiddenStaticCount === 1 ? "" : "s"} not shown, run with ${optionName} option to see ${hiddenStaticCount === 1 ? "it" : "them"}.`);
}
await response2.addResult("Network", lines.join("\n"), { prefix: "network", ext: "log", suggestedFilename: params2.filename });
}
});
REQUEST_PARTS = ["request-headers", "request-body", "response-headers", "response-body"];
request = defineTabTool({
capability: "core",
schema: {
name: "browser_network_request",
title: "Show network request details",
description: "Returns full details (headers and body) of a single network request, or a single part if `part` is set. Use the number from browser_network_requests.",
inputSchema: z15.object({
index: z15.number().int().min(1).describe("1-based index of the request, as printed by browser_network_requests."),
part: z15.enum(REQUEST_PARTS).optional().describe("Return only this part of the request. Omit to return full details."),
filename: z15.string().optional().describe("Filename to save the result to. If not provided, output is returned as text.")
}),
type: "readOnly"
},
handle: async (tab2, params2, response2) => {
const allRequests = await tab2.requests();
const request2 = allRequests[params2.index - 1];
if (!request2) {
response2.addError(`Request #${params2.index} not found. Use browser_network_requests to see available indexes.`);
return;
}
if (params2.part) {
await renderRequestPart(request2, params2.part, response2, params2.filename);
return;
}
await response2.addResult("Request", renderRequestDetails(params2.index, request2, !!tab2.context.config.skillMode), { prefix: "request", ext: "log", suggestedFilename: params2.filename });
}
});
networkClear = defineTabTool({
capability: "core",
skillOnly: true,
schema: {
name: "browser_network_clear",
title: "Clear network requests",
description: "Clear all network requests",
inputSchema: z15.object({}),
type: "readOnly"
},
handle: async (tab2, params2, response2) => {
await tab2.clearRequests();
}
});
networkStateSet = defineTool({
capability: "network",
schema: {
name: "browser_network_state_set",
title: "Set network state",
description: "Sets the browser network state to online or offline. When offline, all network requests will fail.",
inputSchema: z15.object({
state: z15.enum(["online", "offline"]).describe('Set to "offline" to simulate offline mode, "online" to restore network connectivity')
}),
type: "action"
},
handle: async (context2, params2, response2) => {
const browserContext = await context2.ensureBrowserContext();
const offline = params2.state === "offline";
await browserContext.setOffline(offline);
response2.addTextResult(`Network is now ${params2.state}`);
response2.addCode(`await page.context().setOffline(${offline});`);
}
});
network_default = [
requests,
request,
networkClear,
networkStateSet
];
}
});
// packages/playwright-core/src/tools/backend/pdf.ts
var z16, pdfSchema, pdf, pdf_default;
var init_pdf = __esm({
"packages/playwright-core/src/tools/backend/pdf.ts"() {
"use strict";
init_stringUtils();
init_tool();
z16 = require("./utilsBundle").z;
pdfSchema = z16.object({
filename: z16.string().optional().describe("File name to save the pdf to. Defaults to `page-{timestamp}.pdf` if not specified. Prefer relative file names to stay within the output directory.")
});
pdf = defineTabTool({
capability: "pdf",
schema: {
name: "browser_pdf_save",
title: "Save as PDF",
description: "Save page as PDF",
inputSchema: pdfSchema,
type: "readOnly"
},
handle: async (tab2, params2, response2) => {
const data = await tab2.page.pdf();
const result2 = await response2.resolveClientFile({ prefix: "page", ext: "pdf", suggestedFilename: params2.filename }, "Page as pdf");
await response2.addFileResult(result2, data);
response2.addCode(`await page.pdf(${formatObject({ path: result2.relativeName })});`);
}
});
pdf_default = [
pdf
];
}
});
// packages/playwright-core/src/tools/backend/route.ts
var z17, route, routeList, unroute, route_default;
var init_route = __esm({
"packages/playwright-core/src/tools/backend/route.ts"() {
"use strict";
init_tool();
z17 = require("./utilsBundle").z;
route = defineTool({
capability: "network",
schema: {
name: "browser_route",
title: "Mock network requests",
description: "Set up a route to mock network requests matching a URL pattern",
inputSchema: z17.object({
pattern: z17.string().describe('URL pattern to match (e.g., "**/api/users", "**/*.{png,jpg}")'),
status: z17.number().optional().describe("HTTP status code to return (default: 200)"),
body: z17.string().optional().describe("Response body (text or JSON string)"),
contentType: z17.string().optional().describe('Content-Type header (e.g., "application/json", "text/html")'),
headers: z17.array(z17.string()).optional().describe('Headers to add in "Name: Value" format'),
removeHeaders: z17.string().optional().describe("Comma-separated list of header names to remove from request")
}),
type: "action"
},
handle: async (context2, params2, response2) => {
const addHeaders = params2.headers ? Object.fromEntries(params2.headers.map((h) => {
const colonIndex = h.indexOf(":");
return [h.substring(0, colonIndex).trim(), h.substring(colonIndex + 1).trim()];
})) : void 0;
const removeHeaders = params2.removeHeaders ? params2.removeHeaders.split(",").map((h) => h.trim()) : void 0;
const handler = async (route2) => {
if (params2.body !== void 0 || params2.status !== void 0) {
await route2.fulfill({
status: params2.status ?? 200,
contentType: params2.contentType,
body: params2.body
});
return;
}
const headers = { ...route2.request().headers() };
if (addHeaders) {
for (const [key, value2] of Object.entries(addHeaders))
headers[key] = value2;
}
if (removeHeaders) {
for (const header of removeHeaders)
delete headers[header.toLowerCase()];
}
await route2.continue({ headers });
};
const entry = {
pattern: params2.pattern,
status: params2.status,
body: params2.body,
contentType: params2.contentType,
addHeaders,
removeHeaders,
handler
};
await context2.addRoute(entry);
response2.addTextResult(`Route added for pattern: ${params2.pattern}`);
response2.addCode(`await page.context().route('${params2.pattern}', async route => { /* route handler */ });`);
}
});
routeList = defineTool({
capability: "network",
schema: {
name: "browser_route_list",
title: "List network routes",
description: "List all active network routes",
inputSchema: z17.object({}),
type: "readOnly"
},
handle: async (context2, params2, response2) => {
const routes = context2.routes();
if (routes.length === 0) {
response2.addTextResult("No active routes");
return;
}
const lines = [];
for (let i = 0; i < routes.length; i++) {
const route2 = routes[i];
const details = [];
if (route2.status !== void 0)
details.push(`status=${route2.status}`);
if (route2.body !== void 0)
details.push(`body=${route2.body.length > 50 ? route2.body.substring(0, 50) + "..." : route2.body}`);
if (route2.contentType)
details.push(`contentType=${route2.contentType}`);
if (route2.addHeaders)
details.push(`addHeaders=${JSON.stringify(route2.addHeaders)}`);
if (route2.removeHeaders)
details.push(`removeHeaders=${route2.removeHeaders.join(",")}`);
const detailsStr = details.length ? ` (${details.join(", ")})` : "";
lines.push(`${i + 1}. ${route2.pattern}${detailsStr}`);
}
response2.addTextResult(lines.join("\n"));
}
});
unroute = defineTool({
capability: "network",
schema: {
name: "browser_unroute",
title: "Remove network routes",
description: "Remove network routes matching a pattern (or all routes if no pattern specified)",
inputSchema: z17.object({
pattern: z17.string().optional().describe("URL pattern to unroute (omit to remove all routes)")
}),
type: "action"
},
handle: async (context2, params2, response2) => {
const removed = await context2.removeRoute(params2.pattern);
if (params2.pattern)
response2.addTextResult(`Removed ${removed} route(s) for pattern: ${params2.pattern}`);
else
response2.addTextResult(`Removed all ${removed} route(s)`);
}
});
route_default = [
route,
routeList,
unroute
];
}
});
// packages/playwright-core/src/tools/backend/runCode.ts
var import_fs46, import_vm, z18, codeSchema, runCode, runCode_default;
var init_runCode = __esm({
"packages/playwright-core/src/tools/backend/runCode.ts"() {
"use strict";
import_fs46 = __toESM(require("fs"));
import_vm = __toESM(require("vm"));
init_manualPromise();
init_tool();
z18 = require("./utilsBundle").z;
codeSchema = z18.object({
code: z18.string().optional().describe(`A JavaScript function containing Playwright code to execute. It will be invoked with a single argument, page, which you can use for any page interaction. For example: \`async (page) => { await page.getByRole('button', { name: 'Submit' }).click(); return await page.title(); }\``),
filename: z18.string().optional().describe("Load code from the specified file. If both code and filename are provided, code will be ignored.")
});
runCode = defineTabTool({
capability: "core",
schema: {
name: "browser_run_code_unsafe",
title: "Run Playwright code (unsafe)",
description: "Run a Playwright code snippet. Unsafe: executes arbitrary JavaScript in the Playwright server process and is RCE-equivalent.",
inputSchema: codeSchema,
type: "action"
},
handle: async (tab2, params2, response2) => {
let code = params2.code;
if (params2.filename) {
const resolvedPath = await response2.resolveClientFilename(params2.filename);
code = await import_fs46.default.promises.readFile(resolvedPath, "utf-8");
}
response2.addCode(`await (${code})(page);`);
const __end__ = new ManualPromise();
const context2 = {
page: tab2.page,
__end__
};
import_vm.default.createContext(context2);
const unsubscribe = tab2.context.onUnhandledRejection((reason) => {
if (!__end__.isDone())
__end__.reject(reason instanceof Error ? reason : new Error(String(reason)));
});
try {
await tab2.waitForCompletion(async () => {
context2.__fn__ = import_vm.default.runInContext("(" + code + ")", context2);
const snippet = "(async () => {\n try {\n const result = await __fn__(page);\n __end__.resolve(JSON.stringify(result));\n } catch (e) {\n __end__.reject(e);\n }\n})()";
const iifePromise = import_vm.default.runInContext(snippet, context2);
await Promise.race([iifePromise, __end__]);
const result2 = await __end__;
if (typeof result2 === "string")
response2.addTextResult(result2);
});
} finally {
unsubscribe();
}
}
});
runCode_default = [
runCode
];
}
});
// packages/playwright-core/src/tools/backend/storage.ts
var z19, storageState, setStorageState, storage_default;
var init_storage = __esm({
"packages/playwright-core/src/tools/backend/storage.ts"() {
"use strict";
init_tool();
z19 = require("./utilsBundle").z;
storageState = defineTool({
capability: "storage",
schema: {
name: "browser_storage_state",
title: "Save storage state",
description: "Save storage state (cookies, local storage) to a file for later reuse",
inputSchema: z19.object({
filename: z19.string().optional().describe("File name to save the storage state to. Defaults to `storage-state-{timestamp}.json` if not specified.")
}),
type: "readOnly"
},
handle: async (context2, params2, response2) => {
const browserContext = await context2.ensureBrowserContext();
const state = await browserContext.storageState();
const serializedState = JSON.stringify(state, null, 2);
const resolvedFile = await response2.resolveClientFile({ prefix: "storage-state", ext: "json", suggestedFilename: params2.filename }, "Storage state");
response2.addCode(`await page.context().storageState({ path: '${resolvedFile.relativeName}' });`);
await response2.addFileResult(resolvedFile, serializedState);
}
});
setStorageState = defineTool({
capability: "storage",
schema: {
name: "browser_set_storage_state",
title: "Restore storage state",
description: "Restore storage state (cookies, local storage) from a file. This clears existing cookies and local storage before restoring.",
inputSchema: z19.object({
filename: z19.string().describe("Path to the storage state file to restore from")
}),
type: "action"
},
handle: async (context2, params2, response2) => {
const browserContext = await context2.ensureBrowserContext();
const resolvedFilename = await response2.resolveClientFilename(params2.filename);
await browserContext.setStorageState(resolvedFilename);
response2.addTextResult(`Storage state restored from ${params2.filename}`);
response2.addCode(`await page.context().setStorageState('${params2.filename}');`);
}
});
storage_default = [
storageState,
setStorageState
];
}
});
// packages/playwright-core/src/tools/backend/tabs.ts
var z20, browserTabs, tabs_default;
var init_tabs = __esm({
"packages/playwright-core/src/tools/backend/tabs.ts"() {
"use strict";
init_tool();
init_response();
z20 = require("./utilsBundle").z;
browserTabs = defineTool({
capability: "core-tabs",
schema: {
name: "browser_tabs",
title: "Manage tabs",
description: "List, create, close, or select a browser tab.",
inputSchema: z20.object({
action: z20.enum(["list", "new", "close", "select"]).describe("Operation to perform"),
index: z20.number().optional().describe("Tab index, used for close/select. If omitted for close, current tab is closed."),
url: z20.string().optional().describe("URL to navigate to in the new tab, used for new.")
}),
type: "action"
},
handle: async (context2, params2, response2) => {
switch (params2.action) {
case "list": {
await context2.ensureTab();
break;
}
case "new": {
const tab2 = await context2.newTab();
if (params2.url) {
const url2 = await tab2.checkUrlAndNavigate(params2.url);
response2.setIncludeSnapshot();
response2.addCode(`await page.goto('${url2}');`);
}
break;
}
case "close": {
await context2.closeTab(params2.index);
break;
}
case "select": {
if (params2.index === void 0)
throw new Error("Tab index is required");
await context2.selectTab(params2.index);
break;
}
}
const tabHeaders = await Promise.all(context2.tabs().map((tab2) => tab2.headerSnapshot()));
const result2 = renderTabsMarkdown(tabHeaders);
response2.addTextResult(result2.join("\n"));
}
});
tabs_default = [
browserTabs
];
}
});
// packages/playwright-core/src/tools/backend/tracing.ts
var z21, tracingStart, tracingStop, tracing_default, traceLegendSymbol;
var init_tracing3 = __esm({
"packages/playwright-core/src/tools/backend/tracing.ts"() {
"use strict";
init_tool();
z21 = require("./utilsBundle").z;
tracingStart = defineTool({
capability: "devtools",
schema: {
name: "browser_start_tracing",
title: "Start tracing",
description: "Start trace recording",
inputSchema: z21.object({}),
type: "readOnly"
},
handle: async (context2, params2, response2) => {
const browserContext = await context2.ensureBrowserContext();
const tracesDir = await context2.outputFile({ prefix: "", suggestedFilename: `traces`, ext: "" }, { origin: "code" });
const name = "trace-" + Date.now();
await browserContext.tracing.start({
name,
screenshots: true,
snapshots: true,
live: true
});
response2.addTextResult(`Trace recording started`);
response2.addFileLink("Action log", `${tracesDir}/${name}.trace`);
response2.addFileLink("Network log", `${tracesDir}/${name}.network`);
response2.addFileLink("Resources", `${tracesDir}/resources`);
browserContext.tracing[traceLegendSymbol] = { tracesDir, name };
}
});
tracingStop = defineTool({
capability: "devtools",
schema: {
name: "browser_stop_tracing",
title: "Stop tracing",
description: "Stop trace recording",
inputSchema: z21.object({}),
type: "readOnly"
},
handle: async (context2, params2, response2) => {
const browserContext = await context2.ensureBrowserContext();
await browserContext.tracing.stop();
const traceLegend = browserContext.tracing[traceLegendSymbol];
if (!traceLegend)
throw new Error("Tracing is not started");
delete browserContext.tracing[traceLegendSymbol];
response2.addTextResult(`Trace recording stopped.`);
response2.addFileLink("Trace", `${traceLegend.tracesDir}/${traceLegend.name}.trace`);
response2.addFileLink("Network log", `${traceLegend.tracesDir}/${traceLegend.name}.network`);
response2.addFileLink("Resources", `${traceLegend.tracesDir}/resources`);
}
});
tracing_default = [
tracingStart,
tracingStop
];
traceLegendSymbol = Symbol("tracesDir");
}
});
// packages/playwright-core/src/tools/backend/verify.ts
var z22, verifyElement, verifyText, verifyList, verifyValue, verify_default;
var init_verify = __esm({
"packages/playwright-core/src/tools/backend/verify.ts"() {
"use strict";
init_stringUtils();
init_tool();
z22 = require("./utilsBundle").z;
verifyElement = defineTabTool({
capability: "testing",
schema: {
name: "browser_verify_element_visible",
title: "Verify element visible",
description: "Verify element is visible on the page",
inputSchema: z22.object({
role: z22.string().describe('ROLE of the element. Can be found in the snapshot like this: `- {ROLE} "Accessible Name":`'),
accessibleName: z22.string().describe('ACCESSIBLE_NAME of the element. Can be found in the snapshot like this: `- role "{ACCESSIBLE_NAME}"`')
}),
type: "assertion"
},
handle: async (tab2, params2, response2) => {
for (const frame of tab2.page.frames()) {
const locator2 = frame.getByRole(params2.role, { name: params2.accessibleName });
if (await locator2.count() > 0) {
const resolved = await locator2.normalize();
response2.addCode(`await expect(page.${resolved}).toBeVisible();`);
response2.addTextResult("Done");
return;
}
}
response2.addError(`Element with role "${params2.role}" and accessible name "${params2.accessibleName}" not found`);
}
});
verifyText = defineTabTool({
capability: "testing",
schema: {
name: "browser_verify_text_visible",
title: "Verify text visible",
description: `Verify text is visible on the page. Prefer ${verifyElement.schema.name} if possible.`,
inputSchema: z22.object({
text: z22.string().describe('TEXT to verify. Can be found in the snapshot like this: `- role "Accessible Name": {TEXT}` or like this: `- text: {TEXT}`')
}),
type: "assertion"
},
handle: async (tab2, params2, response2) => {
for (const frame of tab2.page.frames()) {
const locator2 = frame.getByText(params2.text).filter({ visible: true });
if (await locator2.count() > 0) {
const resolved = await locator2.normalize();
response2.addCode(`await expect(page.${resolved}).toBeVisible();`);
response2.addTextResult("Done");
return;
}
}
response2.addError("Text not found");
}
});
verifyList = defineTabTool({
capability: "testing",
schema: {
name: "browser_verify_list_visible",
title: "Verify list visible",
description: "Verify list is visible on the page",
inputSchema: z22.object({
element: z22.string().describe("Human-readable list description"),
target: z22.string().describe("Exact target element reference that points to the list"),
items: z22.array(z22.string()).describe("Items to verify")
}),
type: "assertion"
},
handle: async (tab2, params2, response2) => {
const { locator: locator2 } = await tab2.targetLocator(params2);
const itemTexts = [];
for (const item of params2.items) {
const itemLocator = locator2.getByText(item);
if (await itemLocator.count() === 0) {
response2.addError(`Item "${item}" not found`);
return;
}
itemTexts.push(await itemLocator.textContent(tab2.expectTimeoutOptions));
}
const ariaSnapshot = `\`
- list:
${itemTexts.map((t) => ` - listitem: ${escapeWithQuotes(t, '"')}`).join("\n")}
\``;
response2.addCode(`await expect(page.locator('body')).toMatchAriaSnapshot(${ariaSnapshot});`);
response2.addTextResult("Done");
}
});
verifyValue = defineTabTool({
capability: "testing",
schema: {
name: "browser_verify_value",
title: "Verify value",
description: "Verify element value",
inputSchema: z22.object({
type: z22.enum(["textbox", "checkbox", "radio", "combobox", "slider"]).describe("Type of the element"),
element: z22.string().describe("Human-readable element description"),
target: z22.string().describe("Exact target element reference from the page snapshot"),
value: z22.string().describe('Value to verify. For checkbox, use "true" or "false".')
}),
type: "assertion"
},
handle: async (tab2, params2, response2) => {
const { locator: locator2, resolved } = await tab2.targetLocator(params2);
const locatorSource = `page.${resolved}`;
if (params2.type === "textbox" || params2.type === "slider" || params2.type === "combobox") {
const value2 = await locator2.inputValue(tab2.expectTimeoutOptions);
if (value2 !== params2.value) {
response2.addError(`Expected value "${params2.value}", but got "${value2}"`);
return;
}
response2.addCode(`await expect(${locatorSource}).toHaveValue(${escapeWithQuotes(params2.value)});`);
} else if (params2.type === "checkbox" || params2.type === "radio") {
const value2 = await locator2.isChecked(tab2.expectTimeoutOptions);
if (value2 !== (params2.value === "true")) {
response2.addError(`Expected value "${params2.value}", but got "${value2}"`);
return;
}
const matcher = value2 ? "toBeChecked" : "not.toBeChecked";
response2.addCode(`await expect(${locatorSource}).${matcher}();`);
}
response2.addTextResult("Done");
}
});
verify_default = [
verifyElement,
verifyText,
verifyList,
verifyValue
];
}
});
// packages/playwright-core/src/tools/backend/video.ts
var z23, videoStart, videoStop, videoChapter, video_default;
var init_video2 = __esm({
"packages/playwright-core/src/tools/backend/video.ts"() {
"use strict";
init_tool();
z23 = require("./utilsBundle").z;
videoStart = defineTool({
capability: "devtools",
schema: {
name: "browser_start_video",
title: "Start video",
description: "Start video recording",
inputSchema: z23.object({
filename: z23.string().optional().describe("Filename to save the video."),
size: z23.object({
width: z23.number().describe("Video width"),
height: z23.number().describe("Video height")
}).optional().describe("Video size")
}),
type: "readOnly"
},
handle: async (context2, params2, response2) => {
const resolvedFile = await response2.resolveClientFile({ prefix: "video", ext: "webm", suggestedFilename: params2.filename }, "Video");
await context2.startVideoRecording(resolvedFile.fileName, { size: params2.size });
response2.addTextResult("Video recording started.");
}
});
videoStop = defineTool({
capability: "devtools",
schema: {
name: "browser_stop_video",
title: "Stop video",
description: "Stop video recording",
inputSchema: z23.object({}),
type: "readOnly"
},
handle: async (context2, params2, response2) => {
const fileNames = await context2.stopVideoRecording();
if (!fileNames.length) {
response2.addTextResult("No videos were recorded.");
return;
}
for (const fileName of fileNames) {
const resolvedFile = await response2.resolveClientFile({
prefix: "video",
ext: "webm",
suggestedFilename: fileName
}, "Video");
await response2.addFileResult(resolvedFile, null);
}
}
});
videoChapter = defineTool({
capability: "devtools",
schema: {
name: "browser_video_chapter",
title: "Video chapter",
description: "Add a chapter marker to the video recording. Shows a full-screen chapter card with blurred backdrop.",
inputSchema: z23.object({
title: z23.string().describe("Chapter title"),
description: z23.string().optional().describe("Chapter description"),
duration: z23.number().optional().describe("Duration in milliseconds to show the chapter card")
}),
type: "readOnly"
},
handle: async (context2, params2, response2) => {
const tab2 = context2.currentTabOrDie();
await tab2.page.screencast.showChapter(params2.title, {
description: params2.description,
duration: params2.duration
});
response2.addTextResult(`Chapter '${params2.title}' added.`);
}
});
video_default = [
videoStart,
videoStop,
videoChapter
];
}
});
// packages/playwright-core/src/tools/backend/wait.ts
var z24, wait, wait_default;
var init_wait = __esm({
"packages/playwright-core/src/tools/backend/wait.ts"() {
"use strict";
init_tool();
z24 = require("./utilsBundle").z;
wait = defineTool({
capability: "core",
schema: {
name: "browser_wait_for",
title: "Wait for",
description: "Wait for text to appear or disappear or a specified time to pass",
inputSchema: z24.object({
time: z24.number().optional().describe("The time to wait in seconds"),
text: z24.string().optional().describe("The text to wait for"),
textGone: z24.string().optional().describe("The text to wait for to disappear")
}),
type: "assertion"
},
handle: async (context2, params2, response2) => {
if (!params2.text && !params2.textGone && !params2.time)
throw new Error("Either time, text or textGone must be provided");
if (params2.time) {
response2.addCode(`await new Promise(f => setTimeout(f, ${params2.time} * 1000));`);
await new Promise((f) => setTimeout(f, Math.min(3e4, params2.time * 1e3)));
}
const tab2 = context2.currentTabOrDie();
const locator2 = params2.text ? tab2.page.getByText(params2.text).first() : void 0;
const goneLocator = params2.textGone ? tab2.page.getByText(params2.textGone).first() : void 0;
if (goneLocator) {
response2.addCode(`await page.getByText(${JSON.stringify(params2.textGone)}).first().waitFor({ state: 'hidden' });`);
await goneLocator.waitFor({ state: "hidden" });
}
if (locator2) {
response2.addCode(`await page.getByText(${JSON.stringify(params2.text)}).first().waitFor({ state: 'visible' });`);
await locator2.waitFor({ state: "visible" });
}
response2.addTextResult(`Waited for ${params2.text || params2.textGone || params2.time}`);
response2.setIncludeSnapshot();
}
});
wait_default = [
wait
];
}
});
// packages/playwright-core/src/tools/backend/webstorage.ts
var z25, localStorageList, localStorageGet, localStorageSet, localStorageDelete, localStorageClear, sessionStorageList, sessionStorageGet, sessionStorageSet, sessionStorageDelete, sessionStorageClear, webstorage_default;
var init_webstorage = __esm({
"packages/playwright-core/src/tools/backend/webstorage.ts"() {
"use strict";
init_tool();
z25 = require("./utilsBundle").z;
localStorageList = defineTabTool({
capability: "storage",
schema: {
name: "browser_localstorage_list",
title: "List localStorage",
description: "List all localStorage key-value pairs",
inputSchema: z25.object({}),
type: "readOnly"
},
handle: async (tab2, params2, response2) => {
const items = await tab2.page.evaluate(() => {
const result2 = [];
for (let i = 0; i < localStorage.length; i++) {
const key = localStorage.key(i);
if (key !== null)
result2.push({ key, value: localStorage.getItem(key) || "" });
}
return result2;
});
if (items.length === 0)
response2.addTextResult("No localStorage items found");
else
response2.addTextResult(items.map((item) => `${item.key}=${item.value}`).join("\n"));
response2.addCode(`await page.evaluate(() => ({ ...localStorage }));`);
}
});
localStorageGet = defineTabTool({
capability: "storage",
schema: {
name: "browser_localstorage_get",
title: "Get localStorage item",
description: "Get a localStorage item by key",
inputSchema: z25.object({
key: z25.string().describe("Key to get")
}),
type: "readOnly"
},
handle: async (tab2, params2, response2) => {
const value2 = await tab2.page.evaluate((key) => localStorage.getItem(key), params2.key);
if (value2 === null)
response2.addTextResult(`localStorage key '${params2.key}' not found`);
else
response2.addTextResult(`${params2.key}=${value2}`);
response2.addCode(`await page.evaluate(() => localStorage.getItem('${params2.key}'));`);
}
});
localStorageSet = defineTabTool({
capability: "storage",
schema: {
name: "browser_localstorage_set",
title: "Set localStorage item",
description: "Set a localStorage item",
inputSchema: z25.object({
key: z25.string().describe("Key to set"),
value: z25.string().describe("Value to set")
}),
type: "action"
},
handle: async (tab2, params2, response2) => {
await tab2.page.evaluate(({ key, value: value2 }) => localStorage.setItem(key, value2), params2);
response2.addCode(`await page.evaluate(() => localStorage.setItem('${params2.key}', '${params2.value}'));`);
}
});
localStorageDelete = defineTabTool({
capability: "storage",
schema: {
name: "browser_localstorage_delete",
title: "Delete localStorage item",
description: "Delete a localStorage item",
inputSchema: z25.object({
key: z25.string().describe("Key to delete")
}),
type: "action"
},
handle: async (tab2, params2, response2) => {
await tab2.page.evaluate((key) => localStorage.removeItem(key), params2.key);
response2.addCode(`await page.evaluate(() => localStorage.removeItem('${params2.key}'));`);
}
});
localStorageClear = defineTabTool({
capability: "storage",
schema: {
name: "browser_localstorage_clear",
title: "Clear localStorage",
description: "Clear all localStorage",
inputSchema: z25.object({}),
type: "action"
},
handle: async (tab2, params2, response2) => {
await tab2.page.evaluate(() => localStorage.clear());
response2.addCode(`await page.evaluate(() => localStorage.clear());`);
}
});
sessionStorageList = defineTabTool({
capability: "storage",
schema: {
name: "browser_sessionstorage_list",
title: "List sessionStorage",
description: "List all sessionStorage key-value pairs",
inputSchema: z25.object({}),
type: "readOnly"
},
handle: async (tab2, params2, response2) => {
const items = await tab2.page.evaluate(() => {
const result2 = [];
for (let i = 0; i < sessionStorage.length; i++) {
const key = sessionStorage.key(i);
if (key !== null)
result2.push({ key, value: sessionStorage.getItem(key) || "" });
}
return result2;
});
if (items.length === 0)
response2.addTextResult("No sessionStorage items found");
else
response2.addTextResult(items.map((item) => `${item.key}=${item.value}`).join("\n"));
response2.addCode(`await page.evaluate(() => ({ ...sessionStorage }));`);
}
});
sessionStorageGet = defineTabTool({
capability: "storage",
schema: {
name: "browser_sessionstorage_get",
title: "Get sessionStorage item",
description: "Get a sessionStorage item by key",
inputSchema: z25.object({
key: z25.string().describe("Key to get")
}),
type: "readOnly"
},
handle: async (tab2, params2, response2) => {
const value2 = await tab2.page.evaluate((key) => sessionStorage.getItem(key), params2.key);
if (value2 === null)
response2.addTextResult(`sessionStorage key '${params2.key}' not found`);
else
response2.addTextResult(`${params2.key}=${value2}`);
response2.addCode(`await page.evaluate(() => sessionStorage.getItem('${params2.key}'));`);
}
});
sessionStorageSet = defineTabTool({
capability: "storage",
schema: {
name: "browser_sessionstorage_set",
title: "Set sessionStorage item",
description: "Set a sessionStorage item",
inputSchema: z25.object({
key: z25.string().describe("Key to set"),
value: z25.string().describe("Value to set")
}),
type: "action"
},
handle: async (tab2, params2, response2) => {
await tab2.page.evaluate(({ key, value: value2 }) => sessionStorage.setItem(key, value2), params2);
response2.addCode(`await page.evaluate(() => sessionStorage.setItem('${params2.key}', '${params2.value}'));`);
}
});
sessionStorageDelete = defineTabTool({
capability: "storage",
schema: {
name: "browser_sessionstorage_delete",
title: "Delete sessionStorage item",
description: "Delete a sessionStorage item",
inputSchema: z25.object({
key: z25.string().describe("Key to delete")
}),
type: "action"
},
handle: async (tab2, params2, response2) => {
await tab2.page.evaluate((key) => sessionStorage.removeItem(key), params2.key);
response2.addCode(`await page.evaluate(() => sessionStorage.removeItem('${params2.key}'));`);
}
});
sessionStorageClear = defineTabTool({
capability: "storage",
schema: {
name: "browser_sessionstorage_clear",
title: "Clear sessionStorage",
description: "Clear all sessionStorage",
inputSchema: z25.object({}),
type: "action"
},
handle: async (tab2, params2, response2) => {
await tab2.page.evaluate(() => sessionStorage.clear());
response2.addCode(`await page.evaluate(() => sessionStorage.clear());`);
}
});
webstorage_default = [
localStorageList,
localStorageGet,
localStorageSet,
localStorageDelete,
localStorageClear,
sessionStorageList,
sessionStorageGet,
sessionStorageSet,
sessionStorageDelete,
sessionStorageClear
];
}
});
// packages/playwright-core/src/tools/backend/tools.ts
function filteredTools(config) {
return browserTools.filter((tool) => tool.capability.startsWith("core") || config.capabilities?.includes(tool.capability)).filter((tool) => !tool.skillOnly).map((tool) => ({
...tool,
schema: {
...tool.schema,
// Note: we first ensure that "selector" property is present, so that we can omit() it without an error.
inputSchema: tool.schema.inputSchema.extend({ selector: z26.string(), startSelector: z26.string(), endSelector: z26.string() }).omit({ selector: true, startSelector: true, endSelector: true })
}
}));
}
var z26, browserTools;
var init_tools = __esm({
"packages/playwright-core/src/tools/backend/tools.ts"() {
"use strict";
init_common();
init_config();
init_console2();
init_cookies();
init_devtools();
init_dialogs();
init_evaluate();
init_files();
init_form();
init_keyboard();
init_mouse();
init_navigate();
init_network4();
init_pdf();
init_route();
init_runCode();
init_snapshot();
init_screenshot();
init_storage();
init_tabs();
init_tracing3();
init_verify();
init_video2();
init_wait();
init_webstorage();
z26 = require("./utilsBundle").z;
browserTools = [
...common_default,
...config_default,
...console_default,
...cookies_default,
...devtools_default,
...dialogs_default,
...evaluate_default,
...files_default,
...form_default,
...keyboard_default,
...mouse_default,
...navigate_default,
...network_default,
...pdf_default,
...route_default,
...runCode_default,
...screenshot_default,
...snapshot_default,
...storage_default,
...tabs_default,
...tracing_default,
...verify_default,
...video_default,
...wait_default,
...webstorage_default
];
}
});
// packages/playwright-core/src/tools/cli-daemon/command.ts
function declareCommand(command) {
return command;
}
function parseCommand(command, args) {
const optionsObject = { ...args };
delete optionsObject["_"];
const optionsSchema = (command.options ?? kEmptyOptions).strict();
const options2 = zodParse(optionsSchema, optionsObject, "option");
const argsSchema = (command.args ?? kEmptyArgs).strict();
const argNames = [...Object.keys(argsSchema.shape)];
const argv = args["_"].slice(1);
if (argv.length > argNames.length)
throw new Error(`error: too many arguments: expected ${argNames.length}, received ${argv.length}`);
const argsObject = {};
argNames.forEach((name, index) => argsObject[name] = argv[index]);
const parsedArgsObject = zodParse(argsSchema, argsObject, "argument");
const toolName = typeof command.toolName === "function" ? command.toolName({ ...parsedArgsObject, ...options2 }) : command.toolName;
const toolParams = command.toolParams({ ...parsedArgsObject, ...options2 });
return { toolName, toolParams };
}
function zodParse(schema, data, type3) {
try {
return schema.parse(data);
} catch (e) {
throw new Error(e.issues.map((issue) => {
const keys = issue.code === "unrecognized_keys" ? issue.keys : [""];
const props = keys.map((key) => [...issue.path, key].filter(Boolean).join("."));
return props.map((prop) => {
const label = type3 === "option" ? `'--${prop}' option` : `'${prop}' argument`;
switch (issue.code) {
case "invalid_type":
return "error: " + label + ": " + issue.message.replace(/Invalid input:/, "").trim();
case "unrecognized_keys":
return "error: unknown " + label;
default:
return "error: " + label + ": " + issue.message;
}
});
}).flat().join("\n"));
}
}
var z27, kEmptyOptions, kEmptyArgs;
var init_command = __esm({
"packages/playwright-core/src/tools/cli-daemon/command.ts"() {
"use strict";
z27 = require("./utilsBundle").z;
kEmptyOptions = z27.object({});
kEmptyArgs = z27.object({});
}
});
// packages/playwright-core/src/tools/cli-client/minimist.ts
function minimist(args, opts) {
if (!opts)
opts = {};
const bools = {};
const strings = {};
for (const key of toArray(opts.boolean))
bools[key] = true;
for (const key of toArray(opts.string))
strings[key] = true;
const argv = { _: [] };
function setArg(key, val) {
if (argv[key] === void 0 || bools[key] || typeof argv[key] === "boolean")
argv[key] = val;
else if (Array.isArray(argv[key]))
argv[key].push(val);
else
argv[key] = [argv[key], val];
}
let notFlags = [];
const doubleDashIndex = args.indexOf("--");
if (doubleDashIndex !== -1) {
notFlags = args.slice(doubleDashIndex + 1);
args = args.slice(0, doubleDashIndex);
}
for (let i = 0; i < args.length; i++) {
const arg = args[i];
let key;
let next;
if (/^--.+=/.test(arg)) {
const m = arg.match(/^--([^=]+)=([\s\S]*)$/);
key = m[1];
if (bools[key])
throw new Error(`boolean option '--${key}' should not be passed with '=value', use '--${key}' or '--no-${key}' instead`);
setArg(key, m[2]);
} else if (/^--no-.+/.test(arg)) {
key = arg.match(/^--no-(.+)/)[1];
setArg(key, false);
} else if (/^--.+/.test(arg)) {
key = arg.match(/^--(.+)/)[1];
next = args[i + 1];
if (next !== void 0 && !/^(-|--)[^-]/.test(next) && !bools[key]) {
setArg(key, next);
i += 1;
} else if (/^(true|false)$/.test(next)) {
setArg(key, next === "true");
i += 1;
} else {
setArg(key, strings[key] ? "" : true);
}
} else if (/^-[^-]+/.test(arg)) {
const letters = arg.slice(1, -1).split("");
let broken = false;
for (let j = 0; j < letters.length; j++) {
next = arg.slice(j + 2);
if (next === "-") {
setArg(letters[j], next);
continue;
}
if (/[A-Za-z]/.test(letters[j]) && next[0] === "=") {
setArg(letters[j], next.slice(1));
broken = true;
break;
}
if (/[A-Za-z]/.test(letters[j]) && /-?\d+(\.\d*)?(e-?\d+)?$/.test(next)) {
setArg(letters[j], next);
broken = true;
break;
}
if (letters[j + 1] && letters[j + 1].match(/\W/)) {
setArg(letters[j], arg.slice(j + 2));
broken = true;
break;
} else {
setArg(letters[j], strings[letters[j]] ? "" : true);
}
}
key = arg.slice(-1)[0];
if (!broken && key !== "-") {
if (args[i + 1] && !/^(-|--)[^-]/.test(args[i + 1]) && !bools[key]) {
setArg(key, args[i + 1]);
i += 1;
} else if (args[i + 1] && /^(true|false)$/.test(args[i + 1])) {
setArg(key, args[i + 1] === "true");
i += 1;
} else {
setArg(key, strings[key] ? "" : true);
}
}
} else {
argv._.push(arg);
}
}
for (const k of notFlags)
argv._.push(k);
return argv;
}
function toArray(value2) {
if (!value2)
return [];
return Array.isArray(value2) ? value2 : [value2];
}
var init_minimist = __esm({
"packages/playwright-core/src/tools/cli-client/minimist.ts"() {
"use strict";
}
});
// packages/playwright-core/src/tools/cli-daemon/commands.ts
var z28, elementTargetDescription2, numberArg, open5, attach, close2, detach, goto, goBack2, goForward2, reload2, pressKey, type2, keydown2, keyup2, mouseMove2, mouseDown2, mouseUp2, mouseWheel2, click2, doubleClick, drag2, drop2, fill, hover2, select, fileUpload, check2, uncheck2, snapshot2, generateLocator2, highlight2, evaluate3, dialogAccept, dialogDismiss, resize2, runCode2, tabList, tabNew, tabClose, tabSelect, stateLoad, stateSave, cookieList2, cookieGet2, cookieSet2, cookieDelete2, cookieClear2, localStorageList2, localStorageGet2, localStorageSet2, localStorageDelete2, localStorageClear2, sessionStorageList2, sessionStorageGet2, sessionStorageSet2, sessionStorageDelete2, sessionStorageClear2, routeMock, routeList2, unroute2, networkStateSet2, screenshot2, pdfSave, consoleList, networkRequests, filenameOption, networkRequest, networkRequestHeaders, networkRequestBody, networkResponseHeaders, networkResponseBody, tracingStart2, tracingStop2, videoStart2, videoStop2, videoChapter2, dashboardShow, resume2, stepOver, pauseAt, sessionList, sessionCloseAll, killAll, deleteData, configPrint, install, installBrowser, tray, commandsArray, commands;
var init_commands = __esm({
"packages/playwright-core/src/tools/cli-daemon/commands.ts"() {
"use strict";
init_command();
z28 = require("./utilsBundle").z;
elementTargetDescription2 = "Exact target element reference from the page snapshot, or a unique element selector";
numberArg = z28.preprocess((val, ctx) => {
const number = Number(val);
if (Number.isNaN(number)) {
ctx.issues.push({
code: "custom",
message: `expected number, received '${val}'`,
input: val
});
}
return number;
}, z28.number());
open5 = declareCommand({
name: "open",
description: "Open the browser",
category: "core",
args: z28.object({
url: z28.string().optional().describe("The URL to navigate to")
}),
options: z28.object({
browser: z28.string().optional().describe("Browser or chrome channel to use, possible values: chrome, firefox, webkit, msedge."),
config: z28.string().optional().describe("Path to the configuration file, defaults to .playwright/cli.config.json"),
headed: z28.boolean().optional().describe("Run browser in headed mode"),
persistent: z28.boolean().optional().describe("Use persistent browser profile"),
profile: z28.string().optional().describe("Path to a persistent user data directory.")
}),
toolName: "",
toolParams: () => ({})
});
attach = declareCommand({
name: "attach",
description: "Attach to a running Playwright browser",
category: "core",
args: z28.object({
name: z28.string().optional().describe("Bound browser name to attach to")
}),
options: z28.object({
cdp: z28.string().optional().describe("Connect to an existing browser via CDP endpoint URL."),
endpoint: z28.string().optional().describe("Playwright browser server endpoint to attach to."),
extension: z28.union([z28.boolean(), z28.string()]).optional().describe("Connect to browser extension, optionally specify browser name (e.g. --extension=chrome)"),
config: z28.string().optional().describe("Path to the configuration file, defaults to .playwright/cli.config.json"),
session: z28.string().optional().describe('Session name (defaults to bound browser name or "default")')
}),
toolName: "browser_snapshot",
toolParams: () => ({ filename: "<auto>" })
});
close2 = declareCommand({
name: "close",
description: "Close the browser",
category: "core",
args: z28.object({}),
toolName: "",
toolParams: () => ({})
});
detach = declareCommand({
name: "detach",
description: "Detach from an attached browser",
category: "core",
args: z28.object({}),
toolName: "",
toolParams: () => ({})
});
goto = declareCommand({
name: "goto",
description: "Navigate to a URL",
category: "core",
args: z28.object({
url: z28.string().describe("The URL to navigate to")
}),
toolName: "browser_navigate",
toolParams: ({ url: url2 }) => ({ url: url2 })
});
goBack2 = declareCommand({
name: "go-back",
description: "Go back to the previous page",
category: "navigation",
args: z28.object({}),
toolName: "browser_navigate_back",
toolParams: () => ({})
});
goForward2 = declareCommand({
name: "go-forward",
description: "Go forward to the next page",
category: "navigation",
args: z28.object({}),
toolName: "browser_navigate_forward",
toolParams: () => ({})
});
reload2 = declareCommand({
name: "reload",
description: "Reload the current page",
category: "navigation",
args: z28.object({}),
toolName: "browser_reload",
toolParams: () => ({})
});
pressKey = declareCommand({
name: "press",
description: "Press a key on the keyboard, `a`, `ArrowLeft`",
category: "keyboard",
args: z28.object({
key: z28.string().describe("Name of the key to press or a character to generate, such as `ArrowLeft` or `a`")
}),
toolName: "browser_press_key",
toolParams: ({ key }) => ({ key })
});
type2 = declareCommand({
name: "type",
description: "Type text into editable element",
category: "core",
args: z28.object({
text: z28.string().describe("Text to type into the element")
}),
options: z28.object({
submit: z28.boolean().optional().describe("Whether to submit entered text (press Enter after)")
}),
toolName: "browser_press_sequentially",
toolParams: ({ text: text2, submit }) => ({ text: text2, submit })
});
keydown2 = declareCommand({
name: "keydown",
description: "Press a key down on the keyboard",
category: "keyboard",
args: z28.object({
key: z28.string().describe("Name of the key to press or a character to generate, such as `ArrowLeft` or `a`")
}),
toolName: "browser_keydown",
toolParams: ({ key }) => ({ key })
});
keyup2 = declareCommand({
name: "keyup",
description: "Press a key up on the keyboard",
category: "keyboard",
args: z28.object({
key: z28.string().describe("Name of the key to press or a character to generate, such as `ArrowLeft` or `a`")
}),
toolName: "browser_keyup",
toolParams: ({ key }) => ({ key })
});
mouseMove2 = declareCommand({
name: "mousemove",
description: "Move mouse to a given position",
category: "mouse",
args: z28.object({
x: numberArg.describe("X coordinate"),
y: numberArg.describe("Y coordinate")
}),
toolName: "browser_mouse_move_xy",
toolParams: ({ x, y }) => ({ x, y })
});
mouseDown2 = declareCommand({
name: "mousedown",
description: "Press mouse down",
category: "mouse",
args: z28.object({
button: z28.string().optional().describe("Button to press, defaults to left")
}),
toolName: "browser_mouse_down",
toolParams: ({ button }) => ({ button })
});
mouseUp2 = declareCommand({
name: "mouseup",
description: "Press mouse up",
category: "mouse",
args: z28.object({
button: z28.string().optional().describe("Button to press, defaults to left")
}),
toolName: "browser_mouse_up",
toolParams: ({ button }) => ({ button })
});
mouseWheel2 = declareCommand({
name: "mousewheel",
description: "Scroll mouse wheel",
category: "mouse",
args: z28.object({
dx: numberArg.describe("X delta"),
dy: numberArg.describe("Y delta")
}),
toolName: "browser_mouse_wheel",
toolParams: ({ dx: deltaX, dy: deltaY }) => ({ deltaX, deltaY })
});
click2 = declareCommand({
name: "click",
description: "Perform click on a web page",
category: "core",
args: z28.object({
target: z28.string().describe(elementTargetDescription2),
button: z28.string().optional().describe("Button to click, defaults to left")
}),
options: z28.object({
modifiers: z28.array(z28.string()).optional().describe("Modifier keys to press")
}),
toolName: "browser_click",
toolParams: ({ target, button, modifiers }) => ({ target, button, modifiers })
});
doubleClick = declareCommand({
name: "dblclick",
description: "Perform double click on a web page",
category: "core",
args: z28.object({
target: z28.string().describe(elementTargetDescription2),
button: z28.string().optional().describe("Button to click, defaults to left")
}),
options: z28.object({
modifiers: z28.array(z28.string()).optional().describe("Modifier keys to press")
}),
toolName: "browser_click",
toolParams: ({ target, button, modifiers }) => ({ target, button, modifiers, doubleClick: true })
});
drag2 = declareCommand({
name: "drag",
description: "Perform drag and drop between two elements",
category: "core",
args: z28.object({
startTarget: z28.string().describe("Exact source element reference from the page snapshot, or a unique element selector"),
endTarget: z28.string().describe(elementTargetDescription2)
}),
toolName: "browser_drag",
toolParams: ({ startTarget, endTarget }) => ({ startTarget, endTarget })
});
drop2 = declareCommand({
name: "drop",
description: "Drop files or data onto an element",
category: "core",
args: z28.object({
target: z28.string().describe(elementTargetDescription2)
}),
options: z28.object({
path: z28.union([z28.string(), z28.array(z28.string())]).optional().transform((v) => v ? Array.isArray(v) ? v : [v] : void 0).describe("Absolute path to a file to drop onto the element (repeatable)"),
data: z28.union([z28.string(), z28.array(z28.string())]).optional().transform((v) => v ? Array.isArray(v) ? v : [v] : void 0).describe('Data to drop in "mime/type=value" format, e.g. --data "text/plain=hello" (repeatable)')
}),
toolName: "browser_drop",
toolParams: ({ target, path: path59, data }) => {
let dataMap;
if (data) {
dataMap = {};
for (const entry of data) {
const idx = entry.indexOf("=");
if (idx === -1)
throw new Error(`--data must be in "mime/type=value" format, got: ${entry}`);
dataMap[entry.slice(0, idx)] = entry.slice(idx + 1);
}
}
return { target, paths: path59, data: dataMap };
}
});
fill = declareCommand({
name: "fill",
description: "Fill text into editable element",
category: "core",
args: z28.object({
target: z28.string().describe(elementTargetDescription2),
text: z28.string().describe("Text to fill into the element")
}),
options: z28.object({
submit: z28.boolean().optional().describe("Whether to submit entered text (press Enter after)")
}),
toolName: "browser_type",
toolParams: ({ target, text: text2, submit }) => ({ target, text: text2, submit })
});
hover2 = declareCommand({
name: "hover",
description: "Hover over element on page",
category: "core",
args: z28.object({
target: z28.string().describe(elementTargetDescription2)
}),
toolName: "browser_hover",
toolParams: ({ target }) => ({ target })
});
select = declareCommand({
name: "select",
description: "Select an option in a dropdown",
category: "core",
args: z28.object({
target: z28.string().describe(elementTargetDescription2),
val: z28.string().describe("Value to select in the dropdown")
}),
toolName: "browser_select_option",
toolParams: ({ target, val: value2 }) => ({ target, values: [value2] })
});
fileUpload = declareCommand({
name: "upload",
description: "Upload one or multiple files",
category: "core",
args: z28.object({
file: z28.string().describe("The absolute paths to the files to upload")
}),
toolName: "browser_file_upload",
toolParams: ({ file }) => ({ paths: [file] })
});
check2 = declareCommand({
name: "check",
description: "Check a checkbox or radio button",
category: "core",
args: z28.object({
target: z28.string().describe(elementTargetDescription2)
}),
toolName: "browser_check",
toolParams: ({ target }) => ({ target })
});
uncheck2 = declareCommand({
name: "uncheck",
description: "Uncheck a checkbox or radio button",
category: "core",
args: z28.object({
target: z28.string().describe(elementTargetDescription2)
}),
toolName: "browser_uncheck",
toolParams: ({ target }) => ({ target })
});
snapshot2 = declareCommand({
name: "snapshot",
description: "Capture page snapshot to obtain element ref",
category: "core",
args: z28.object({
target: z28.string().optional().describe("Element reference from the previous page snapshot, or a unique element selector for the root element to capture a partial snapshot instead of the whole page")
}),
options: z28.object({
filename: z28.string().optional().describe("Save snapshot to markdown file instead of returning it in the response."),
depth: numberArg.optional().describe("Limit snapshot depth, unlimited by default."),
boxes: z28.boolean().optional().describe("Include each element's bounding box as [box=x,y,width,height] in the snapshot. Coordinates are viewport-relative, in CSS pixels (Element.getBoundingClientRect).")
}),
toolName: "browser_snapshot",
toolParams: ({ filename, target, depth, boxes }) => ({ filename, target, depth, boxes })
});
generateLocator2 = declareCommand({
name: "generate-locator",
description: "Generate a Playwright locator for the given element",
category: "devtools",
args: z28.object({
target: z28.string().describe(elementTargetDescription2)
}),
toolName: "browser_generate_locator",
toolParams: ({ target }) => ({ target })
});
highlight2 = declareCommand({
name: "highlight",
description: "Show (or with --hide, remove) a highlight overlay for an element; `--hide` without a target hides all page highlights.",
category: "devtools",
args: z28.object({
target: z28.string().optional().describe(elementTargetDescription2)
}),
options: z28.object({
hide: z28.boolean().optional().describe("Hide a previously added highlight for this element, or all page highlights when no element is given"),
style: z28.string().optional().describe('Additional inline CSS applied to the highlight overlay, e.g. "outline: 2px dashed red"')
}),
toolName: ({ hide }) => hide ? "browser_hide_highlight" : "browser_highlight",
toolParams: ({ target, style }) => ({ target, style })
});
evaluate3 = declareCommand({
name: "eval",
description: "Evaluate JavaScript expression on page or element",
category: "core",
args: z28.object({
func: z28.string().describe("() => { /* code */ } or (element) => { /* code */ } when element is provided"),
target: z28.string().optional().describe(elementTargetDescription2)
}),
options: z28.object({
filename: z28.string().optional().describe("Save evaluation result to a file instead of returning it in the response.")
}),
toolName: "browser_evaluate",
toolParams: ({ func, target, filename }) => ({ function: func, filename, target })
});
dialogAccept = declareCommand({
name: "dialog-accept",
description: "Accept a dialog",
category: "core",
args: z28.object({
prompt: z28.string().optional().describe("The text of the prompt in case of a prompt dialog.")
}),
toolName: "browser_handle_dialog",
toolParams: ({ prompt: promptText }) => ({ accept: true, promptText })
});
dialogDismiss = declareCommand({
name: "dialog-dismiss",
description: "Dismiss a dialog",
category: "core",
args: z28.object({}),
toolName: "browser_handle_dialog",
toolParams: () => ({ accept: false })
});
resize2 = declareCommand({
name: "resize",
description: "Resize the browser window",
category: "core",
args: z28.object({
w: numberArg.describe("Width of the browser window"),
h: numberArg.describe("Height of the browser window")
}),
toolName: "browser_resize",
toolParams: ({ w: width, h: height }) => ({ width, height })
});
runCode2 = declareCommand({
name: "run-code",
description: "Run Playwright code snippet",
category: "devtools",
args: z28.object({
code: z28.string().optional().describe("A JavaScript function containing Playwright code to execute. It will be invoked with a single argument, page, which you can use for any page interaction.")
}),
options: z28.object({
filename: z28.string().optional().describe("Load code from the specified file.")
}),
toolName: "browser_run_code_unsafe",
toolParams: ({ code, filename }) => ({ code, filename })
});
tabList = declareCommand({
name: "tab-list",
description: "List all tabs",
category: "tabs",
args: z28.object({}),
toolName: "browser_tabs",
toolParams: () => ({ action: "list" })
});
tabNew = declareCommand({
name: "tab-new",
description: "Create a new tab",
category: "tabs",
args: z28.object({
url: z28.string().optional().describe("The URL to navigate to in the new tab. If omitted, the new tab will be blank.")
}),
toolName: "browser_tabs",
toolParams: ({ url: url2 }) => ({ action: "new", url: url2 })
});
tabClose = declareCommand({
name: "tab-close",
description: "Close a browser tab",
category: "tabs",
args: z28.object({
index: numberArg.optional().describe("Tab index. If omitted, current tab is closed.")
}),
toolName: "browser_tabs",
toolParams: ({ index }) => ({ action: "close", index })
});
tabSelect = declareCommand({
name: "tab-select",
description: "Select a browser tab",
category: "tabs",
args: z28.object({
index: numberArg.describe("Tab index")
}),
toolName: "browser_tabs",
toolParams: ({ index }) => ({ action: "select", index })
});
stateLoad = declareCommand({
name: "state-load",
description: "Loads browser storage (authentication) state from a file",
category: "storage",
args: z28.object({
filename: z28.string().describe("File name to load the storage state from.")
}),
toolName: "browser_set_storage_state",
toolParams: ({ filename }) => ({ filename })
});
stateSave = declareCommand({
name: "state-save",
description: "Saves the current storage (authentication) state to a file",
category: "storage",
args: z28.object({
filename: z28.string().optional().describe("File name to save the storage state to.")
}),
toolName: "browser_storage_state",
toolParams: ({ filename }) => ({ filename })
});
cookieList2 = declareCommand({
name: "cookie-list",
description: "List all cookies (optionally filtered by domain/path)",
category: "storage",
raw: true,
args: z28.object({}),
options: z28.object({
domain: z28.string().optional().describe("Filter cookies by domain"),
path: z28.string().optional().describe("Filter cookies by path")
}),
toolName: "browser_cookie_list",
toolParams: ({ domain, path: path59 }) => ({ domain, path: path59 })
});
cookieGet2 = declareCommand({
name: "cookie-get",
description: "Get a specific cookie by name",
category: "storage",
raw: true,
args: z28.object({
name: z28.string().describe("Cookie name")
}),
toolName: "browser_cookie_get",
toolParams: ({ name }) => ({ name })
});
cookieSet2 = declareCommand({
name: "cookie-set",
description: "Set a cookie with optional flags",
category: "storage",
args: z28.object({
name: z28.string().describe("Cookie name"),
value: z28.string().describe("Cookie value")
}),
options: z28.object({
domain: z28.string().optional().describe("Cookie domain"),
path: z28.string().optional().describe("Cookie path"),
expires: numberArg.optional().describe("Cookie expiration as Unix timestamp"),
httpOnly: z28.boolean().optional().describe("Whether the cookie is HTTP only"),
secure: z28.boolean().optional().describe("Whether the cookie is secure"),
sameSite: z28.enum(["Strict", "Lax", "None"]).optional().describe("Cookie SameSite attribute")
}),
toolName: "browser_cookie_set",
toolParams: ({ name, value: value2, domain, path: path59, expires, httpOnly, secure, sameSite }) => ({ name, value: value2, domain, path: path59, expires, httpOnly, secure, sameSite })
});
cookieDelete2 = declareCommand({
name: "cookie-delete",
description: "Delete a specific cookie",
category: "storage",
args: z28.object({
name: z28.string().describe("Cookie name")
}),
toolName: "browser_cookie_delete",
toolParams: ({ name }) => ({ name })
});
cookieClear2 = declareCommand({
name: "cookie-clear",
description: "Clear all cookies",
category: "storage",
args: z28.object({}),
toolName: "browser_cookie_clear",
toolParams: () => ({})
});
localStorageList2 = declareCommand({
name: "localstorage-list",
description: "List all localStorage key-value pairs",
category: "storage",
raw: true,
args: z28.object({}),
toolName: "browser_localstorage_list",
toolParams: () => ({})
});
localStorageGet2 = declareCommand({
name: "localstorage-get",
description: "Get a localStorage item by key",
category: "storage",
raw: true,
args: z28.object({
key: z28.string().describe("Key to get")
}),
toolName: "browser_localstorage_get",
toolParams: ({ key }) => ({ key })
});
localStorageSet2 = declareCommand({
name: "localstorage-set",
description: "Set a localStorage item",
category: "storage",
args: z28.object({
key: z28.string().describe("Key to set"),
value: z28.string().describe("Value to set")
}),
toolName: "browser_localstorage_set",
toolParams: ({ key, value: value2 }) => ({ key, value: value2 })
});
localStorageDelete2 = declareCommand({
name: "localstorage-delete",
description: "Delete a localStorage item",
category: "storage",
args: z28.object({
key: z28.string().describe("Key to delete")
}),
toolName: "browser_localstorage_delete",
toolParams: ({ key }) => ({ key })
});
localStorageClear2 = declareCommand({
name: "localstorage-clear",
description: "Clear all localStorage",
category: "storage",
args: z28.object({}),
toolName: "browser_localstorage_clear",
toolParams: () => ({})
});
sessionStorageList2 = declareCommand({
name: "sessionstorage-list",
description: "List all sessionStorage key-value pairs",
category: "storage",
raw: true,
args: z28.object({}),
toolName: "browser_sessionstorage_list",
toolParams: () => ({})
});
sessionStorageGet2 = declareCommand({
name: "sessionstorage-get",
description: "Get a sessionStorage item by key",
category: "storage",
raw: true,
args: z28.object({
key: z28.string().describe("Key to get")
}),
toolName: "browser_sessionstorage_get",
toolParams: ({ key }) => ({ key })
});
sessionStorageSet2 = declareCommand({
name: "sessionstorage-set",
description: "Set a sessionStorage item",
category: "storage",
args: z28.object({
key: z28.string().describe("Key to set"),
value: z28.string().describe("Value to set")
}),
toolName: "browser_sessionstorage_set",
toolParams: ({ key, value: value2 }) => ({ key, value: value2 })
});
sessionStorageDelete2 = declareCommand({
name: "sessionstorage-delete",
description: "Delete a sessionStorage item",
category: "storage",
args: z28.object({
key: z28.string().describe("Key to delete")
}),
toolName: "browser_sessionstorage_delete",
toolParams: ({ key }) => ({ key })
});
sessionStorageClear2 = declareCommand({
name: "sessionstorage-clear",
description: "Clear all sessionStorage",
category: "storage",
args: z28.object({}),
toolName: "browser_sessionstorage_clear",
toolParams: () => ({})
});
routeMock = declareCommand({
name: "route",
description: "Mock network requests matching a URL pattern",
category: "network",
args: z28.object({
pattern: z28.string().describe('URL pattern to match (e.g., "**/api/users")')
}),
options: z28.object({
status: numberArg.optional().describe("HTTP status code (default: 200)"),
body: z28.string().optional().describe("Response body (text or JSON string)"),
["content-type"]: z28.string().optional().describe("Content-Type header"),
header: z28.union([z28.string(), z28.array(z28.string())]).optional().transform((v) => v ? Array.isArray(v) ? v : [v] : void 0).describe('Header to add in "Name: Value" format (repeatable)'),
["remove-header"]: z28.string().optional().describe("Comma-separated header names to remove")
}),
toolName: "browser_route",
toolParams: ({ pattern, status, body, ["content-type"]: contentType, header: headers, ["remove-header"]: removeHeaders }) => ({
pattern,
status,
body,
contentType,
headers,
removeHeaders
})
});
routeList2 = declareCommand({
name: "route-list",
description: "List all active network routes",
category: "network",
raw: true,
args: z28.object({}),
toolName: "browser_route_list",
toolParams: () => ({})
});
unroute2 = declareCommand({
name: "unroute",
description: "Remove routes matching a pattern (or all routes)",
category: "network",
args: z28.object({
pattern: z28.string().optional().describe("URL pattern to unroute (omit to remove all)")
}),
toolName: "browser_unroute",
toolParams: ({ pattern }) => ({ pattern })
});
networkStateSet2 = declareCommand({
name: "network-state-set",
description: "Set the browser network state to online or offline",
category: "network",
args: z28.object({
state: z28.enum(["online", "offline"]).describe('Set to "offline" to simulate offline mode, "online" to restore network connectivity')
}),
toolName: "browser_network_state_set",
toolParams: ({ state }) => ({ state })
});
screenshot2 = declareCommand({
name: "screenshot",
description: "screenshot of the current page or element",
category: "export",
args: z28.object({
target: z28.string().optional().describe(elementTargetDescription2)
}),
options: z28.object({
filename: z28.string().optional().describe("File name to save the screenshot to. Defaults to `page-{timestamp}.{png|jpeg}` if not specified."),
["full-page"]: z28.boolean().optional().describe("When true, takes a screenshot of the full scrollable page, instead of the currently visible viewport.")
}),
toolName: "browser_take_screenshot",
toolParams: ({ target, filename, ["full-page"]: fullPage }) => ({ filename, target, fullPage })
});
pdfSave = declareCommand({
name: "pdf",
description: "Save page as PDF",
category: "export",
args: z28.object({}),
options: z28.object({
filename: z28.string().optional().describe("File name to save the pdf to. Defaults to `page-{timestamp}.pdf` if not specified.")
}),
toolName: "browser_pdf_save",
toolParams: ({ filename }) => ({ filename })
});
consoleList = declareCommand({
name: "console",
description: "List console messages",
category: "devtools",
args: z28.object({
["min-level"]: z28.string().optional().describe('Level of the console messages to return. Each level includes the messages of more severe levels. Defaults to "info".')
}),
options: z28.object({
clear: z28.boolean().optional().describe("Whether to clear the console list")
}),
toolName: ({ clear }) => clear ? "browser_console_clear" : "browser_console_messages",
toolParams: ({ ["min-level"]: level, clear }) => clear ? {} : { level }
});
networkRequests = declareCommand({
name: "requests",
description: "List all network requests since loading the page. Each request is numbered for use with the `request` command.",
category: "network",
args: z28.object({}),
options: z28.object({
static: z28.boolean().optional().describe("Whether to include successful static resources like images, fonts, scripts, etc. Defaults to false."),
filter: z28.string().optional().describe('Only return requests whose URL matches this regexp (e.g. "/api/.*user").'),
clear: z28.boolean().optional().describe("Whether to clear the network list")
}),
toolName: ({ clear }) => clear ? "browser_network_clear" : "browser_network_requests",
toolParams: ({ static: s, filter, clear }) => clear ? {} : { static: s, filter }
});
filenameOption = z28.string().optional().describe("Filename to save the result to. If not provided, output is returned as text.");
networkRequest = declareCommand({
name: "request",
description: "Show full details (headers, body, response) of a single network request by its number from the `requests` command.",
category: "network",
args: z28.object({
index: numberArg.describe("1-based number of the request as listed by `requests`")
}),
options: z28.object({
filename: filenameOption
}),
toolName: "browser_network_request",
toolParams: ({ index, filename }) => ({ index, filename })
});
networkRequestHeaders = declareCommand({
name: "request-headers",
description: "Print only the request headers for a single network request by its number from the `requests` command.",
category: "network",
raw: true,
args: z28.object({
index: numberArg.describe("1-based number of the request as listed by `requests`")
}),
options: z28.object({
filename: filenameOption
}),
toolName: "browser_network_request",
toolParams: ({ index, filename }) => ({ index, part: "request-headers", filename })
});
networkRequestBody = declareCommand({
name: "request-body",
description: "Print only the request body for a single network request by its number from the `requests` command.",
category: "network",
raw: true,
args: z28.object({
index: numberArg.describe("1-based number of the request as listed by `requests`")
}),
options: z28.object({
filename: filenameOption
}),
toolName: "browser_network_request",
toolParams: ({ index, filename }) => ({ index, part: "request-body", filename })
});
networkResponseHeaders = declareCommand({
name: "response-headers",
description: "Print only the response headers for a single network request by its number from the `requests` command.",
category: "network",
raw: true,
args: z28.object({
index: numberArg.describe("1-based number of the request as listed by `requests`")
}),
options: z28.object({
filename: filenameOption
}),
toolName: "browser_network_request",
toolParams: ({ index, filename }) => ({ index, part: "response-headers", filename })
});
networkResponseBody = declareCommand({
name: "response-body",
description: "Print the response body for a single network request by its number from the `requests` command. Textual bodies are inlined; binary bodies are saved to a file and the path is printed.",
category: "network",
raw: true,
args: z28.object({
index: numberArg.describe("1-based number of the request as listed by `requests`")
}),
options: z28.object({
filename: filenameOption
}),
toolName: "browser_network_request",
toolParams: ({ index, filename }) => ({ index, part: "response-body", filename })
});
tracingStart2 = declareCommand({
name: "tracing-start",
description: "Start trace recording",
category: "devtools",
args: z28.object({}),
toolName: "browser_start_tracing",
toolParams: () => ({})
});
tracingStop2 = declareCommand({
name: "tracing-stop",
description: "Stop trace recording",
category: "devtools",
args: z28.object({}),
toolName: "browser_stop_tracing",
toolParams: () => ({})
});
videoStart2 = declareCommand({
name: "video-start",
description: "Start video recording",
category: "devtools",
args: z28.object({
filename: z28.string().optional().describe("Filename to save the video.")
}),
options: z28.object({
size: z28.string().optional().describe('Video frame size, e.g. "800x600". If not specified, the size of the recorded video will fit 800x800.')
}),
toolName: "browser_start_video",
toolParams: ({ filename, size }) => {
const parsedSize = size ? size.split("x").map(Number) : void 0;
return { filename, size: parsedSize ? { width: parsedSize[0], height: parsedSize[1] } : void 0 };
}
});
videoStop2 = declareCommand({
name: "video-stop",
description: "Stop video recording",
category: "devtools",
toolName: "browser_stop_video",
toolParams: () => ({})
});
videoChapter2 = declareCommand({
name: "video-chapter",
description: "Add a chapter marker to the video recording",
category: "devtools",
args: z28.object({
title: z28.string().describe("Chapter title.")
}),
options: z28.object({
description: z28.string().optional().describe("Chapter description."),
duration: numberArg.optional().describe("Duration in milliseconds to show the chapter card.")
}),
toolName: "browser_video_chapter",
toolParams: ({ title, description, duration }) => ({ title, description, duration })
});
dashboardShow = declareCommand({
name: "show",
description: "Show Playwright Dashboard",
category: "devtools",
raw: true,
args: z28.object({}),
options: z28.object({
port: numberArg.optional().describe("Start as a blocking HTTP server on this port (use 0 for a random port)"),
host: z28.string().optional().describe("Host to bind to when using --port (defaults to localhost)"),
annotate: z28.boolean().optional().describe("Switch the dashboard into annotation mode."),
kill: z28.boolean().optional().describe("Kill the dashboard daemon.")
}),
toolName: ({ annotate: annotate2 }) => annotate2 ? "browser_annotate" : "",
toolParams: () => ({})
});
resume2 = declareCommand({
name: "resume",
description: "Resume the test execution",
category: "devtools",
args: z28.object({}),
toolName: "browser_resume",
toolParams: ({ step }) => ({ step })
});
stepOver = declareCommand({
name: "step-over",
description: "Step over the next call in the test",
category: "devtools",
args: z28.object({}),
toolName: "browser_resume",
toolParams: ({}) => ({ step: true })
});
pauseAt = declareCommand({
name: "pause-at",
description: "Run the test up to a specific location and pause there",
category: "devtools",
args: z28.object({
location: z28.string().describe('Location to pause at. Format is <file>:<line>, e.g. "example.spec.ts:42".')
}),
toolName: "browser_resume",
toolParams: ({ location: location2 }) => ({ location: location2 })
});
sessionList = declareCommand({
name: "list",
description: "List browser sessions",
category: "browsers",
args: z28.object({}),
options: z28.object({
all: z28.boolean().optional().describe("List all browser sessions across all workspaces")
}),
toolName: "",
toolParams: () => ({})
});
sessionCloseAll = declareCommand({
name: "close-all",
description: "Close all browser sessions",
category: "browsers",
toolName: "",
toolParams: () => ({})
});
killAll = declareCommand({
name: "kill-all",
description: "Forcefully kill all browser sessions (for stale/zombie processes)",
category: "browsers",
toolName: "",
toolParams: () => ({})
});
deleteData = declareCommand({
name: "delete-data",
description: "Delete session data",
category: "core",
toolName: "",
toolParams: () => ({})
});
configPrint = declareCommand({
name: "config-print",
description: "Print the final resolved config after merging CLI options, environment variables and config file.",
category: "config",
hidden: true,
toolName: "browser_get_config",
toolParams: () => ({})
});
install = declareCommand({
name: "install",
description: "Initialize workspace",
category: "install",
args: z28.object({}),
options: z28.object({
skills: z28.string().optional().describe("Install skills, possible values: claude (default), agents.")
}),
toolName: "",
toolParams: () => ({})
});
installBrowser = declareCommand({
name: "install-browser",
description: "Install browser",
category: "install",
args: z28.object({
browser: z28.string().optional().describe("Browser to install")
}),
options: z28.object({
["with-deps"]: z28.boolean().optional().describe("Install system dependencies for browsers"),
["dry-run"]: z28.boolean().optional().describe("Do not execute installation, only print information"),
list: z28.boolean().optional().describe("Prints list of browsers from all Playwright installations"),
force: z28.boolean().optional().describe("Force reinstall of already installed browsers"),
["only-shell"]: z28.boolean().optional().describe("Only install headless shell when installing Chromium"),
["no-shell"]: z28.boolean().optional().describe("Do not install Chromium headless shell")
}),
toolName: "",
toolParams: () => ({})
});
tray = declareCommand({
name: "tray",
description: "Run tray",
category: "config",
hidden: true,
toolName: "",
toolParams: () => ({})
});
commandsArray = [
// core category
open5,
attach,
close2,
detach,
goto,
type2,
click2,
doubleClick,
fill,
drag2,
drop2,
hover2,
select,
fileUpload,
check2,
uncheck2,
snapshot2,
evaluate3,
consoleList,
dialogAccept,
dialogDismiss,
resize2,
runCode2,
deleteData,
// navigation category
goBack2,
goForward2,
reload2,
// keyboard category
pressKey,
keydown2,
keyup2,
// mouse category
mouseMove2,
mouseDown2,
mouseUp2,
mouseWheel2,
// export category
screenshot2,
pdfSave,
// tabs category
tabList,
tabNew,
tabClose,
tabSelect,
// storage category
stateLoad,
stateSave,
cookieList2,
cookieGet2,
cookieSet2,
cookieDelete2,
cookieClear2,
localStorageList2,
localStorageGet2,
localStorageSet2,
localStorageDelete2,
localStorageClear2,
sessionStorageList2,
sessionStorageGet2,
sessionStorageSet2,
sessionStorageDelete2,
sessionStorageClear2,
// network category
networkRequests,
networkRequest,
networkRequestHeaders,
networkRequestBody,
networkResponseHeaders,
networkResponseBody,
routeMock,
routeList2,
unroute2,
networkStateSet2,
// config category
configPrint,
// install category
install,
installBrowser,
// devtools category
tracingStart2,
tracingStop2,
videoStart2,
videoStop2,
videoChapter2,
dashboardShow,
pauseAt,
resume2,
stepOver,
generateLocator2,
highlight2,
// session category
sessionList,
sessionCloseAll,
killAll,
// Hidden commands
tray
];
commands = Object.fromEntries(commandsArray.map((cmd) => [cmd.name, cmd]));
}
});
// packages/playwright-core/src/tools/trace/traceSnapshot.ts
async function traceSnapshot(actionId, options2) {
const trace = await loadTrace();
const action = trace.resolveActionId(actionId);
if (!action) {
console.error(`Action '${actionId}' not found.`);
process.exitCode = 1;
return;
}
const pageId4 = action.pageId;
if (!pageId4) {
console.error(`Action '${actionId}' has no associated page.`);
process.exitCode = 1;
return;
}
const callId = action.callId;
const storage = trace.loader.storage();
let snapshotName;
let renderer;
if (options2.name) {
snapshotName = options2.name;
renderer = storage.snapshotByName(pageId4, `${snapshotName}@${callId}`);
} else {
for (const candidate of ["input", "before", "after"]) {
renderer = storage.snapshotByName(pageId4, `${candidate}@${callId}`);
if (renderer) {
snapshotName = candidate;
break;
}
}
}
if (!renderer || !snapshotName) {
console.error(`No snapshot found for action '${actionId}'.`);
process.exitCode = 1;
return;
}
const snapshotKey = `${snapshotName}@${callId}`;
const server = await serveTraceSnapshot(storage, trace.loader, pageId4, snapshotKey);
if (options2.serve) {
console.log(`Serving snapshot at ${server.url}`);
await new Promise(() => {
});
return;
}
await runCommandOnSnapshot(server, options2.browserArgs || []);
}
async function serveTraceSnapshot(storage, loader, pageId4, snapshotKey) {
const snapshotServer = new SnapshotServer(storage, (sha1) => loader.resourceForSha1(sha1));
const httpServer = new HttpServer();
httpServer.routePrefix("/snapshot/", (request2, response2) => {
const url2 = new URL("http://localhost" + request2.url);
const pageOrFrameId = url2.pathname.substring("/snapshot/".length);
const searchParams = url2.searchParams;
searchParams.set("name", snapshotKey);
const snapshotResponse = snapshotServer.serveSnapshot(pageOrFrameId, searchParams, url2.href);
response2.statusCode = snapshotResponse.status;
snapshotResponse.headers.forEach((value2, key) => response2.setHeader(key, value2));
snapshotResponse.text().then((text2) => response2.end(text2));
return true;
});
httpServer.routePrefix("/", (_request, response2) => {
response2.statusCode = 302;
response2.setHeader("Location", `/snapshot/${pageId4}?name=${encodeURIComponent(snapshotKey)}`);
response2.end();
return true;
});
await httpServer.start({ preferredPort: 0 });
return { url: httpServer.urlPrefix("human-readable"), stop: () => httpServer.stop() };
}
async function runCommandOnSnapshot(server, browserArgs) {
const browser = await playwright.chromium.launch({ headless: true });
const context2 = await browser.newContext();
const page = await context2.newPage();
await page.goto(server.url);
const backend = new BrowserBackend({
snapshot: { mode: "full" },
outputMode: "file",
skillMode: true
}, context2, browserTools);
await backend.initialize({ cwd: process.cwd(), clientName: "playwright-cli" });
try {
if (!browserArgs.length)
browserArgs = ["snapshot"];
const args = minimist(browserArgs, { string: ["_"] });
const command = commands[args._[0]];
if (!command)
throw new Error(`Unknown command: ${args._[0]}`);
const { toolName, toolParams } = parseCommand(command, args);
const result2 = await backend.callTool(toolName, toolParams);
const text2 = result2.content[0]?.type === "text" ? result2.content[0].text : void 0;
if (text2)
console.log(text2);
if (result2.isError) {
console.error("Command failed.");
process.exitCode = 1;
}
} catch (e) {
console.error(e.message);
process.exitCode = 1;
} finally {
await server.stop().catch((e) => console.error(e));
await gracefullyCloseAll();
}
}
var init_traceSnapshot = __esm({
"packages/playwright-core/src/tools/trace/traceSnapshot.ts"() {
"use strict";
init_processLauncher();
init_httpServer();
init_snapshotServer();
init_browserBackend();
init_tools();
init_inprocess();
init_command();
init_minimist();
init_commands();
init_traceUtils2();
}
});
// packages/playwright-core/src/tools/trace/traceScreenshot.ts
async function traceScreenshot(actionId, options2) {
const trace = await loadTrace();
const action = trace.resolveActionId(actionId);
if (!action) {
console.error(`Action '${actionId}' not found.`);
process.exitCode = 1;
return;
}
const pageId4 = action.pageId;
if (!pageId4) {
console.error(`Action '${actionId}' has no associated page.`);
process.exitCode = 1;
return;
}
const callId = action.callId;
const storage = trace.loader.storage();
const snapshotNames = ["input", "before", "after"];
let sha1;
for (const name of snapshotNames) {
const renderer = storage.snapshotByName(pageId4, `${name}@${callId}`);
sha1 = renderer?.closestScreenshot();
if (sha1)
break;
}
if (!sha1) {
console.error(`No screenshot found for action '${actionId}'.`);
process.exitCode = 1;
return;
}
const blob = await trace.loader.resourceForSha1(sha1);
if (!blob) {
console.error(`Screenshot resource not found.`);
process.exitCode = 1;
return;
}
const defaultName = `screenshot-${actionId}.png`;
const buffer = Buffer.from(await blob.arrayBuffer());
const outFile = await saveOutputFile(defaultName, buffer, options2.output);
console.log(` Screenshot saved to ${outFile}`);
}
var init_traceScreenshot = __esm({
"packages/playwright-core/src/tools/trace/traceScreenshot.ts"() {
"use strict";
init_traceUtils2();
}
});
// packages/playwright-core/src/tools/trace/traceAttachments.ts
async function traceAttachments() {
const trace = await loadTrace();
if (!trace.model.attachments.length) {
console.log(" No attachments");
return;
}
console.log(` ${"#".padStart(4)} ${"Name".padEnd(40)} ${"Content-Type".padEnd(30)} ${"Action".padEnd(8)}`);
console.log(` ${"\u2500".repeat(4)} ${"\u2500".repeat(40)} ${"\u2500".repeat(30)} ${"\u2500".repeat(8)}`);
for (let i = 0; i < trace.model.attachments.length; i++) {
const a = trace.model.attachments[i];
const actionOrdinal = trace.callIdToOrdinal.get(a.callId);
console.log(` ${(i + 1 + ".").padStart(4)} ${a.name.padEnd(40)} ${a.contentType.padEnd(30)} ${(actionOrdinal !== void 0 ? String(actionOrdinal) : a.callId).padEnd(8)}`);
}
}
async function traceAttachment(attachmentId, options2) {
const trace = await loadTrace();
const ordinal = parseInt(attachmentId, 10);
const attachment = !isNaN(ordinal) && ordinal >= 1 && ordinal <= trace.model.attachments.length ? trace.model.attachments[ordinal - 1] : void 0;
if (!attachment) {
console.error(`Attachment '${attachmentId}' not found. Use 'trace attachments' to see available attachments.`);
process.exitCode = 1;
return;
}
let content;
if (attachment.sha1) {
const blob = await trace.loader.resourceForSha1(attachment.sha1);
if (blob)
content = Buffer.from(await blob.arrayBuffer());
} else if (attachment.base64) {
content = Buffer.from(attachment.base64, "base64");
}
if (!content) {
console.error(`Could not extract attachment content.`);
process.exitCode = 1;
return;
}
const outFile = await saveOutputFile(attachment.name, content, options2.output);
console.log(` Attachment saved to ${outFile}`);
}
var init_traceAttachments = __esm({
"packages/playwright-core/src/tools/trace/traceAttachments.ts"() {
"use strict";
init_traceUtils2();
}
});
// packages/playwright-core/src/tools/trace/installSkill.ts
async function installSkill() {
const cwd = process.cwd();
const skillSource = libPath("tools", "trace", "SKILL.md");
const destDir = import_path42.default.join(cwd, ".claude", "skills", "playwright-trace");
await import_fs47.default.promises.mkdir(destDir, { recursive: true });
const destFile = import_path42.default.join(destDir, "SKILL.md");
await import_fs47.default.promises.copyFile(skillSource, destFile);
console.log(`\u2705 Skill installed to \`${import_path42.default.relative(cwd, destFile)}\`.`);
}
var import_fs47, import_path42;
var init_installSkill = __esm({
"packages/playwright-core/src/tools/trace/installSkill.ts"() {
"use strict";
import_fs47 = __toESM(require("fs"));
import_path42 = __toESM(require("path"));
init_package();
}
});
// packages/playwright-core/src/tools/trace/traceCli.ts
function addTraceCommands(program4, logErrorAndExit2) {
const traceCommand = program4.command("trace").description("inspect trace files from the command line");
traceCommand.command("open <trace>").description("extract trace file for inspection").action(async (trace) => {
traceOpen(trace).catch(logErrorAndExit2);
});
traceCommand.command("close").description("remove extracted trace data").action(async () => {
closeTrace().catch(logErrorAndExit2);
});
traceCommand.command("actions").description("list actions in the trace").option("--grep <pattern>", "filter actions by title pattern").option("--errors-only", "only show failed actions").action(async (options2) => {
traceActions(options2).catch(logErrorAndExit2);
});
traceCommand.command("action <action-id>").description("show details of a specific action").action(async (actionId) => {
traceAction(actionId).catch(logErrorAndExit2);
});
traceCommand.command("requests").description("show network requests").option("--grep <pattern>", "filter by URL pattern").option("--method <method>", "filter by HTTP method").option("--status <code>", "filter by status code").option("--failed", "only show failed requests (status >= 400)").action(async (options2) => {
traceRequests(options2).catch(logErrorAndExit2);
});
traceCommand.command("request <request-id>").description("show details of a specific network request").action(async (requestId) => {
traceRequest(requestId).catch(logErrorAndExit2);
});
traceCommand.command("console").description("show console messages").option("--errors-only", "only show errors").option("--warnings", "show errors and warnings").option("--browser", "only browser console messages").option("--stdio", "only stdout/stderr").action(async (options2) => {
traceConsole(options2).catch(logErrorAndExit2);
});
traceCommand.command("errors").description("show errors with stack traces").action(async () => {
traceErrors().catch(logErrorAndExit2);
});
traceCommand.command("snapshot <action-id>").description("run a playwright-cli command against a DOM snapshot").option("--name <name>", "snapshot phase: before, input, or after").option("--serve", "serve snapshot on localhost and keep running").allowUnknownOption(true).allowExcessArguments(true).action(async (actionId, options2, cmd) => {
try {
const browserArgs = cmd.args.slice(1);
await traceSnapshot(actionId, { ...options2, browserArgs });
} catch (e) {
logErrorAndExit2(e);
}
});
traceCommand.command("screenshot <action-id>").description("save screencast screenshot for an action").option("-o, --output <path>", "output file path").action(async (actionId, options2) => {
traceScreenshot(actionId, options2).catch(logErrorAndExit2);
});
traceCommand.command("attachments").description("list trace attachments").action(async () => {
traceAttachments().catch(logErrorAndExit2);
});
traceCommand.command("attachment <attachment-id>").description("extract a trace attachment by its number").option("-o, --output <path>", "output file path").action(async (attachmentId, options2) => {
traceAttachment(attachmentId, options2).catch(logErrorAndExit2);
});
traceCommand.command("install-skill").description("install SKILL.md for LLM integration").action(async () => {
installSkill().catch(logErrorAndExit2);
});
}
var init_traceCli = __esm({
"packages/playwright-core/src/tools/trace/traceCli.ts"() {
"use strict";
init_traceOpen();
init_traceUtils2();
init_traceActions();
init_traceActions();
init_traceRequests();
init_traceRequests();
init_traceConsole();
init_traceErrors();
init_traceSnapshot();
init_traceScreenshot();
init_traceAttachments();
init_traceAttachments();
init_installSkill();
}
});
// packages/playwright-core/src/cli/driver.ts
function printApiJson() {
console.log(JSON.stringify(require(import_path43.default.join(packageRoot, "api.json"))));
}
function runDriver() {
const dispatcherConnection = new DispatcherConnection();
new RootDispatcher(dispatcherConnection, async (rootScope, { sdkLanguage }) => {
const playwright2 = createPlaywright({ sdkLanguage });
return new PlaywrightDispatcher(rootScope, playwright2);
});
const transport = new PipeTransport(process.stdout, process.stdin);
transport.onmessage = (message) => dispatcherConnection.dispatch(JSON.parse(message));
const isJavaScriptLanguageBinding = !process.env.PW_LANG_NAME || process.env.PW_LANG_NAME === "javascript";
const replacer = !isJavaScriptLanguageBinding && String.prototype.toWellFormed ? (key, value2) => {
if (typeof value2 === "string")
return value2.toWellFormed();
return value2;
} : void 0;
dispatcherConnection.onmessage = (message) => transport.send(JSON.stringify(message, replacer));
transport.onclose = () => {
dispatcherConnection.onmessage = () => {
};
gracefullyProcessExitDoNotHang(0);
};
process.on("SIGINT", () => {
});
}
async function runServer(options2) {
const {
port,
host,
path: path59 = "/",
maxConnections = Infinity,
extension,
artifactsDir,
unsafe
} = options2;
const server = new PlaywrightServer({ mode: extension ? "extension" : "default", path: path59, maxConnections, artifactsDir, unsafe });
const wsEndpoint = await server.listen(port, host);
process.on("exit", () => server.close().catch(console.error));
console.log("Listening on " + wsEndpoint);
process.stdin.on("close", () => gracefullyProcessExitDoNotHang(0));
}
async function launchBrowserServer(browserName, configFile) {
let options2 = {};
if (configFile)
options2 = JSON.parse(import_fs48.default.readFileSync(configFile).toString());
const browserType = playwright[browserName];
const server = await browserType.launchServer(options2);
console.log(server.wsEndpoint());
}
var import_fs48, import_path43;
var init_driver = __esm({
"packages/playwright-core/src/cli/driver.ts"() {
"use strict";
import_fs48 = __toESM(require("fs"));
import_path43 = __toESM(require("path"));
init_pipeTransport();
init_processLauncher();
init_package();
init_inprocess();
init_playwrightServer();
init_server();
}
});
// packages/playwright-core/src/cli/installActions.ts
function printInstalledBrowsers(browsers) {
const browserPaths = /* @__PURE__ */ new Set();
for (const browser of browsers)
browserPaths.add(browser.browserPath);
console.log(` Browsers:`);
for (const browserPath of [...browserPaths].sort())
console.log(` ${browserPath}`);
console.log(` References:`);
const references = /* @__PURE__ */ new Set();
for (const browser of browsers)
references.add(browser.referenceDir);
for (const reference of [...references].sort())
console.log(` ${reference}`);
}
function printGroupedByPlaywrightVersion(browsers) {
const dirToVersion = /* @__PURE__ */ new Map();
for (const browser of browsers) {
if (dirToVersion.has(browser.referenceDir))
continue;
const packageJSON2 = require(import_path44.default.join(browser.referenceDir, "package.json"));
const version3 = packageJSON2.version;
dirToVersion.set(browser.referenceDir, version3);
}
const groupedByPlaywrightMinorVersion = /* @__PURE__ */ new Map();
for (const browser of browsers) {
const version3 = dirToVersion.get(browser.referenceDir);
let entries = groupedByPlaywrightMinorVersion.get(version3);
if (!entries) {
entries = [];
groupedByPlaywrightMinorVersion.set(version3, entries);
}
entries.push(browser);
}
const sortedVersions = [...groupedByPlaywrightMinorVersion.keys()].sort((a, b) => {
const aComponents = a.split(".");
const bComponents = b.split(".");
const aMajor = parseInt(aComponents[0], 10);
const bMajor = parseInt(bComponents[0], 10);
if (aMajor !== bMajor)
return aMajor - bMajor;
const aMinor = parseInt(aComponents[1], 10);
const bMinor = parseInt(bComponents[1], 10);
if (aMinor !== bMinor)
return aMinor - bMinor;
return aComponents.slice(2).join(".").localeCompare(bComponents.slice(2).join("."));
});
for (const version3 of sortedVersions) {
console.log(`
Playwright version: ${version3}`);
printInstalledBrowsers(groupedByPlaywrightMinorVersion.get(version3));
}
}
async function markDockerImage(dockerImageNameTemplate) {
assert(dockerImageNameTemplate, "dockerImageNameTemplate is required");
await writeDockerVersion(dockerImageNameTemplate);
}
async function installBrowsers(args, options2) {
if (isLikelyNpxGlobal()) {
console.error(wrapInASCIIBox([
`WARNING: It looks like you are running 'npx playwright install' without first`,
`installing your project's dependencies.`,
``,
`To avoid unexpected behavior, please install your dependencies first, and`,
`then run Playwright's install command:`,
``,
` npm install`,
` npx playwright install`,
``,
`If your project does not yet depend on Playwright, first install the`,
`applicable npm package (most commonly @playwright/test), and`,
`then run Playwright's install command to download the browsers:`,
``,
` npm install @playwright/test`,
` npx playwright install`,
``
].join("\n"), 1));
}
if (options2.shell === false && options2.onlyShell)
throw new Error(`Only one of --no-shell and --only-shell can be specified`);
const shell = options2.shell === false ? "no" : options2.onlyShell ? "only" : void 0;
const executables = registry.resolveBrowsers(args, { shell });
if (options2.withDeps)
await registry.installDeps(executables, !!options2.dryRun);
if (options2.dryRun && options2.list)
throw new Error(`Only one of --dry-run and --list can be specified`);
if (options2.dryRun) {
for (const executable of executables) {
console.log(registry.calculateDownloadTitle(executable));
console.log(` Install location: ${executable.directory ?? "<system>"}`);
if (executable.downloadURLs?.length) {
const [url2, ...fallbacks] = executable.downloadURLs;
console.log(` Download url: ${url2}`);
for (let i = 0; i < fallbacks.length; ++i)
console.log(` Download fallback ${i + 1}: ${fallbacks[i]}`);
}
console.log(``);
}
} else if (options2.list) {
const browsers = await registry.listInstalledBrowsers();
printGroupedByPlaywrightVersion(browsers);
} else {
await registry.install(executables, { force: options2.force });
await registry.validateHostRequirementsForExecutablesIfNeeded(executables, process.env.PW_LANG_NAME || "javascript").catch((e) => {
e.name = "Playwright Host validation warning";
console.error(e);
});
}
}
async function uninstallBrowsers(options2) {
delete process.env.PLAYWRIGHT_SKIP_BROWSER_GC;
await registry.uninstall(!!options2.all).then(({ numberOfBrowsersLeft }) => {
if (!options2.all && numberOfBrowsersLeft > 0) {
console.log("Successfully uninstalled Playwright browsers for the current Playwright installation.");
console.log(`There are still ${numberOfBrowsersLeft} browsers left, used by other Playwright installations.
To uninstall Playwright browsers for all installations, re-run with --all flag.`);
}
});
}
async function installDeps(args, options2) {
await registry.installDeps(registry.resolveBrowsers(args, {}), !!options2.dryRun);
}
var import_path44;
var init_installActions = __esm({
"packages/playwright-core/src/cli/installActions.ts"() {
"use strict";
import_path44 = __toESM(require("path"));
init_ascii();
init_env();
init_assert();
init_registry();
}
});
// packages/playwright-core/src/cli/browserActions.ts
async function launchContext(options2, extraOptions) {
validateOptions(options2);
const browserType = lookupBrowserType(options2);
const launchOptions = extraOptions;
if (options2.channel)
launchOptions.channel = options2.channel;
launchOptions.handleSIGINT = false;
const contextOptions = (
// Copy the device descriptor since we have to compare and modify the options.
options2.device ? { ...playwright.devices[options2.device] } : {}
);
if (!extraOptions.headless)
contextOptions.deviceScaleFactor = import_os19.default.platform() === "darwin" ? 2 : 1;
if (browserType.name() === "webkit" && process.platform === "linux") {
delete contextOptions.hasTouch;
delete contextOptions.isMobile;
}
if (contextOptions.isMobile && browserType.name() === "firefox")
contextOptions.isMobile = void 0;
if (options2.blockServiceWorkers)
contextOptions.serviceWorkers = "block";
if (options2.proxyServer) {
launchOptions.proxy = {
server: options2.proxyServer
};
if (options2.proxyBypass)
launchOptions.proxy.bypass = options2.proxyBypass;
}
if (options2.viewportSize) {
try {
const [width, height] = options2.viewportSize.split(",").map((n) => +n);
if (isNaN(width) || isNaN(height))
throw new Error("bad values");
contextOptions.viewport = { width, height };
} catch (e) {
throw new Error('Invalid viewport size format: use "width,height", for example --viewport-size="800,600"');
}
}
if (options2.geolocation) {
try {
const [latitude, longitude] = options2.geolocation.split(",").map((n) => parseFloat(n.trim()));
contextOptions.geolocation = {
latitude,
longitude
};
} catch (e) {
throw new Error('Invalid geolocation format, should be "lat,long". For example --geolocation="37.819722,-122.478611"');
}
contextOptions.permissions = ["geolocation"];
}
if (options2.userAgent)
contextOptions.userAgent = options2.userAgent;
if (options2.lang)
contextOptions.locale = options2.lang;
if (options2.colorScheme)
contextOptions.colorScheme = options2.colorScheme;
if (options2.timezone)
contextOptions.timezoneId = options2.timezone;
if (options2.loadStorage)
contextOptions.storageState = options2.loadStorage;
if (options2.ignoreHttpsErrors)
contextOptions.ignoreHTTPSErrors = true;
if (options2.saveHar) {
contextOptions.recordHar = { path: import_path45.default.resolve(process.cwd(), options2.saveHar), mode: "minimal" };
if (options2.saveHarGlob)
contextOptions.recordHar.urlFilter = options2.saveHarGlob;
contextOptions.serviceWorkers = "block";
}
let browser;
let context2;
if (options2.userDataDir) {
context2 = await browserType.launchPersistentContext(options2.userDataDir, { ...launchOptions, ...contextOptions });
browser = context2.browser();
} else {
browser = await browserType.launch(launchOptions);
context2 = await browser.newContext(contextOptions);
}
let closingBrowser = false;
async function closeBrowser() {
if (closingBrowser)
return;
closingBrowser = true;
if (options2.saveStorage)
await context2.storageState({ path: options2.saveStorage }).catch((e) => null);
if (options2.saveHar)
await context2.close();
await browser.close();
}
context2.on("page", (page) => {
page.on("dialog", () => {
});
page.on("close", () => {
const hasPage = browser.contexts().some((context3) => context3.pages().length > 0);
if (hasPage)
return;
closeBrowser().catch(() => {
});
});
});
process.on("SIGINT", async () => {
await closeBrowser();
gracefullyProcessExitDoNotHang(130);
});
const timeout = options2.timeout ? parseInt(options2.timeout, 10) : 0;
context2.setDefaultTimeout(timeout);
context2.setDefaultNavigationTimeout(timeout);
delete launchOptions.headless;
delete launchOptions.executablePath;
delete launchOptions.handleSIGINT;
delete contextOptions.deviceScaleFactor;
return { browser, browserName: browserType.name(), context: context2, contextOptions, launchOptions, closeBrowser };
}
async function openPage(context2, url2) {
let page = context2.pages()[0];
if (!page)
page = await context2.newPage();
if (url2) {
if (import_fs49.default.existsSync(url2))
url2 = "file://" + import_path45.default.resolve(url2);
else if (!url2.startsWith("http") && !url2.startsWith("file://") && !url2.startsWith("about:") && !url2.startsWith("data:"))
url2 = "http://" + url2;
await page.goto(url2);
}
return page;
}
async function open6(options2, url2) {
const { context: context2 } = await launchContext(options2, { headless: !!process.env.PWTEST_CLI_HEADLESS, executablePath: process.env.PWTEST_CLI_EXECUTABLE_PATH });
await context2._exposeConsoleApi();
await openPage(context2, url2);
}
async function codegen(options2, url2) {
const { target: language, output: outputFile2, testIdAttribute: testIdAttributeName2 } = options2;
const tracesDir = import_path45.default.join(import_os19.default.tmpdir(), `playwright-recorder-trace-${Date.now()}`);
const { context: context2, browser, launchOptions, contextOptions, closeBrowser } = await launchContext(options2, {
headless: !!process.env.PWTEST_CLI_HEADLESS,
executablePath: process.env.PWTEST_CLI_EXECUTABLE_PATH,
tracesDir
});
const donePromise = new ManualPromise();
maybeSetupTestHooks(browser, closeBrowser, donePromise);
dotenv.config({ path: "playwright.env" });
await context2._enableRecorder({
language,
launchOptions,
contextOptions,
device: options2.device,
saveStorage: options2.saveStorage,
mode: "recording",
testIdAttributeName: testIdAttributeName2,
outputFile: outputFile2 ? import_path45.default.resolve(outputFile2) : void 0,
handleSIGINT: false
});
await openPage(context2, url2);
donePromise.resolve();
}
async function maybeSetupTestHooks(browser, closeBrowser, donePromise) {
if (!process.env.PWTEST_CLI_IS_UNDER_TEST)
return;
const logs = [];
debug10.log = (...args) => {
const line = require("util").format(...args) + "\n";
logs.push(line);
process.stderr.write(line);
};
browser.on("disconnected", () => {
const hasCrashLine = logs.some((line) => line.includes("process did exit:") && !line.includes("process did exit: exitCode=0, signal=null"));
if (hasCrashLine) {
process.stderr.write("Detected browser crash.\n");
gracefullyProcessExitDoNotHang(1);
}
});
const close3 = async () => {
await donePromise;
await closeBrowser();
};
if (process.env.PWTEST_CLI_EXIT_AFTER_TIMEOUT) {
setTimeout(close3, +process.env.PWTEST_CLI_EXIT_AFTER_TIMEOUT);
return;
}
let stdin = "";
process.stdin.on("data", (data) => {
stdin += data.toString();
if (stdin.startsWith("exit")) {
process.stdin.destroy();
close3();
}
});
}
async function waitForPage(page, captureOptions) {
if (captureOptions.waitForSelector) {
console.log(`Waiting for selector ${captureOptions.waitForSelector}...`);
await page.waitForSelector(captureOptions.waitForSelector);
}
if (captureOptions.waitForTimeout) {
console.log(`Waiting for timeout ${captureOptions.waitForTimeout}...`);
await page.waitForTimeout(parseInt(captureOptions.waitForTimeout, 10));
}
}
async function screenshot3(options2, captureOptions, url2, path59) {
const { context: context2 } = await launchContext(options2, { headless: true });
console.log("Navigating to " + url2);
const page = await openPage(context2, url2);
await waitForPage(page, captureOptions);
console.log("Capturing screenshot into " + path59);
await page.screenshot({ path: path59, fullPage: !!captureOptions.fullPage });
await page.close();
}
async function pdf2(options2, captureOptions, url2, path59) {
if (options2.browser !== "chromium")
throw new Error("PDF creation is only working with Chromium");
const { context: context2 } = await launchContext({ ...options2, browser: "chromium" }, { headless: true });
console.log("Navigating to " + url2);
const page = await openPage(context2, url2);
await waitForPage(page, captureOptions);
console.log("Saving as pdf into " + path59);
await page.pdf({ path: path59, format: captureOptions.paperFormat });
await page.close();
}
function lookupBrowserType(options2) {
let name = options2.browser;
if (options2.device) {
const device = playwright.devices[options2.device];
name = device.defaultBrowserType;
}
let browserType;
switch (name) {
case "chromium":
browserType = playwright.chromium;
break;
case "webkit":
browserType = playwright.webkit;
break;
case "firefox":
browserType = playwright.firefox;
break;
case "cr":
browserType = playwright.chromium;
break;
case "wk":
browserType = playwright.webkit;
break;
case "ff":
browserType = playwright.firefox;
break;
}
if (browserType)
return browserType;
program.help();
}
function validateOptions(options2) {
if (options2.device && !(options2.device in playwright.devices)) {
const lines = [`Device descriptor not found: '${options2.device}', available devices are:`];
for (const name in playwright.devices)
lines.push(` "${name}"`);
throw new Error(lines.join("\n"));
}
if (options2.colorScheme && !["light", "dark"].includes(options2.colorScheme))
throw new Error('Invalid color scheme, should be one of "light", "dark"');
}
var import_fs49, import_os19, import_path45, debug10, dotenv, program;
var init_browserActions = __esm({
"packages/playwright-core/src/cli/browserActions.ts"() {
"use strict";
import_fs49 = __toESM(require("fs"));
import_os19 = __toESM(require("os"));
import_path45 = __toESM(require("path"));
init_processLauncher();
init_manualPromise();
init_inprocess();
debug10 = require("./utilsBundle").debug;
dotenv = require("./utilsBundle").dotenv;
({ program } = require("./utilsBundle"));
}
});
// packages/playwright-core/src/tools/utils/extension.ts
async function isPlaywrightExtensionInstalled(userDataDir) {
let entries;
try {
entries = await import_fs50.default.promises.readdir(userDataDir);
} catch {
return false;
}
for (const entry of entries) {
if (entry !== "Default" && !entry.startsWith("Profile "))
continue;
if (await isExtensionInstalledInProfile(import_path46.default.join(userDataDir, entry)))
return true;
}
return false;
}
async function isExtensionInstalledInProfile(profileDir2) {
if (await pathExists(import_path46.default.join(profileDir2, "Extensions", playwrightExtensionId)))
return true;
try {
const prefs = await import_fs50.default.promises.readFile(import_path46.default.join(profileDir2, "Preferences"), "utf-8");
return prefs.includes(`"${playwrightExtensionId}"`);
} catch {
return false;
}
}
async function pathExists(p) {
try {
await import_fs50.default.promises.access(p);
return true;
} catch {
return false;
}
}
var import_fs50, import_path46, playwrightExtensionId, playwrightExtensionInstallUrl;
var init_extension = __esm({
"packages/playwright-core/src/tools/utils/extension.ts"() {
"use strict";
import_fs50 = __toESM(require("fs"));
import_path46 = __toESM(require("path"));
playwrightExtensionId = "mmlmfjhmonkocbjadbfplnigmagldckm";
playwrightExtensionInstallUrl = `https://chromewebstore.google.com/detail/playwright-extension/${playwrightExtensionId}`;
}
});
// packages/playwright-core/src/tools/cli-client/channelSessions.ts
function isKnownChannel(name) {
return channelToUserDataDir.has(name);
}
async function listChannelSessions() {
if (process.env.PWTEST_CLI_CHANNEL_SCAN_DISABLED_FOR_TEST)
return [];
const result2 = [];
for (const [channel, dirs] of channelToUserDataDir) {
const userDataDir = dirs[process.platform];
if (!userDataDir)
continue;
if (!await pathExists2(userDataDir))
continue;
const [endpoint, extensionInstalled] = await Promise.all([
readEndpoint(userDataDir),
isPlaywrightExtensionInstalled(userDataDir)
]);
result2.push({ channel, userDataDir, endpoint, extensionInstalled });
}
return result2;
}
async function pathExists2(p) {
try {
await import_fs51.default.promises.access(p);
return true;
} catch {
return false;
}
}
async function readEndpoint(userDataDir) {
let contents;
try {
contents = await import_fs51.default.promises.readFile(import_path47.default.join(userDataDir, "DevToolsActivePort"), "utf-8");
} catch {
return void 0;
}
const port = parseInt(contents.trim().split("\n")[0], 10);
if (!Number.isFinite(port))
return void 0;
if (!await isPortOpen(port))
return void 0;
return `http://localhost:${port}`;
}
async function isPortOpen(port) {
return new Promise((resolve) => {
const socket = import_net8.default.createConnection(port, "127.0.0.1");
const done = (value2) => {
socket.destroy();
resolve(value2);
};
socket.once("connect", () => done(true));
socket.once("error", () => done(false));
socket.setTimeout(250, () => done(false));
});
}
var import_fs51, import_net8, import_os20, import_path47, channelToUserDataDir;
var init_channelSessions = __esm({
"packages/playwright-core/src/tools/cli-client/channelSessions.ts"() {
"use strict";
import_fs51 = __toESM(require("fs"));
import_net8 = __toESM(require("net"));
import_os20 = __toESM(require("os"));
import_path47 = __toESM(require("path"));
init_extension();
channelToUserDataDir = /* @__PURE__ */ new Map([
["chrome", {
"linux": import_path47.default.join(import_os20.default.homedir(), ".config", "google-chrome"),
"darwin": import_path47.default.join(import_os20.default.homedir(), "Library", "Application Support", "Google", "Chrome"),
"win32": import_path47.default.join(process.env.LOCALAPPDATA || import_path47.default.join(import_os20.default.homedir(), "AppData", "Local"), "Google", "Chrome", "User Data")
}],
["chrome-beta", {
"linux": import_path47.default.join(import_os20.default.homedir(), ".config", "google-chrome-beta"),
"darwin": import_path47.default.join(import_os20.default.homedir(), "Library", "Application Support", "Google", "Chrome Beta"),
"win32": import_path47.default.join(process.env.LOCALAPPDATA || import_path47.default.join(import_os20.default.homedir(), "AppData", "Local"), "Google", "Chrome Beta", "User Data")
}],
["chrome-dev", {
"linux": import_path47.default.join(import_os20.default.homedir(), ".config", "google-chrome-unstable"),
"darwin": import_path47.default.join(import_os20.default.homedir(), "Library", "Application Support", "Google", "Chrome Dev"),
"win32": import_path47.default.join(process.env.LOCALAPPDATA || import_path47.default.join(import_os20.default.homedir(), "AppData", "Local"), "Google", "Chrome Dev", "User Data")
}],
["chrome-canary", {
"linux": import_path47.default.join(import_os20.default.homedir(), ".config", "google-chrome-canary"),
"darwin": import_path47.default.join(import_os20.default.homedir(), "Library", "Application Support", "Google", "Chrome Canary"),
"win32": import_path47.default.join(process.env.LOCALAPPDATA || import_path47.default.join(import_os20.default.homedir(), "AppData", "Local"), "Google", "Chrome SxS", "User Data")
}],
["msedge", {
"linux": import_path47.default.join(import_os20.default.homedir(), ".config", "microsoft-edge"),
"darwin": import_path47.default.join(import_os20.default.homedir(), "Library", "Application Support", "Microsoft Edge"),
"win32": import_path47.default.join(process.env.LOCALAPPDATA || import_path47.default.join(import_os20.default.homedir(), "AppData", "Local"), "Microsoft", "Edge", "User Data")
}],
["msedge-beta", {
"linux": import_path47.default.join(import_os20.default.homedir(), ".config", "microsoft-edge-beta"),
"darwin": import_path47.default.join(import_os20.default.homedir(), "Library", "Application Support", "Microsoft Edge Beta"),
"win32": import_path47.default.join(process.env.LOCALAPPDATA || import_path47.default.join(import_os20.default.homedir(), "AppData", "Local"), "Microsoft", "Edge Beta", "User Data")
}],
["msedge-dev", {
"linux": import_path47.default.join(import_os20.default.homedir(), ".config", "microsoft-edge-dev"),
"darwin": import_path47.default.join(import_os20.default.homedir(), "Library", "Application Support", "Microsoft Edge Dev"),
"win32": import_path47.default.join(process.env.LOCALAPPDATA || import_path47.default.join(import_os20.default.homedir(), "AppData", "Local"), "Microsoft", "Edge Dev", "User Data")
}],
["msedge-canary", {
"linux": import_path47.default.join(import_os20.default.homedir(), ".config", "microsoft-edge-canary"),
"darwin": import_path47.default.join(import_os20.default.homedir(), "Library", "Application Support", "Microsoft Edge Canary"),
"win32": import_path47.default.join(process.env.LOCALAPPDATA || import_path47.default.join(import_os20.default.homedir(), "AppData", "Local"), "Microsoft", "Edge SxS", "User Data")
}]
]);
}
});
// packages/playwright-core/src/tools/cli-client/output.ts
function parseJsonText(text2) {
try {
return JSON.parse(text2);
} catch {
return text2;
}
}
function renderBrowser(browser) {
const lines = [`- ${browser.name}:`];
lines.push(` - status: ${browser.status}`);
if (browser.status === "open" && !browser.compatible)
lines.push(` - version: v${browser.version} [incompatible please re-open]`);
if (browser.browserType)
lines.push(` - browser-type: ${browser.browserType}${browser.attached ? " (attached)" : ""}`);
if (!browser.attached) {
if (browser.userDataDir === null)
lines.push(` - user-data-dir: <in-memory>`);
else
lines.push(` - user-data-dir: ${browser.userDataDir}`);
if (browser.headed !== void 0)
lines.push(` - headed: ${browser.headed}`);
}
return lines.join("\n");
}
function renderServer(server) {
const lines = [`- browser "${server.title}":`];
lines.push(` - browser: ${server.browser.browserName}`);
lines.push(` - version: v${server.playwrightVersion}`);
if (server.browser.userDataDir)
lines.push(` - data-dir: ${server.browser.userDataDir}`);
else
lines.push(` - data-dir: <in-memory>`);
lines.push(` - run \`playwright-cli attach "${server.title}"\` to attach`);
return lines.join("\n");
}
function renderChannelSession(session2) {
const lines = [`- ${session2.channel}:`];
lines.push(` - data-dir: ${session2.userDataDir}`);
if (session2.extensionInstalled)
lines.push(` - attach (extension): \`playwright-cli attach --extension=${session2.channel}\``);
else
lines.push(` - attach (extension): install at ${playwrightExtensionInstallUrl}`);
if (session2.endpoint) {
lines.push(` - attach (remote debugging): \`playwright-cli attach --cdp=${session2.channel}\``);
} else {
const inspectScheme = session2.channel.startsWith("msedge") ? "edge" : "chrome";
lines.push(` - attach (remote debugging): enable at ${inspectScheme}://inspect/#remote-debugging`);
}
return lines.join("\n");
}
var import_path48, TextOutput, JsonOutput;
var init_output = __esm({
"packages/playwright-core/src/tools/cli-client/output.ts"() {
"use strict";
import_path48 = __toESM(require("path"));
init_extension();
TextOutput = class {
constructor() {
this.json = false;
}
version(v) {
console.log(v);
}
help(text2) {
console.log(text2);
}
errorUnknownCommand(name, globalHelp) {
console.error(`Unknown command: ${name}
`);
console.log(globalHelp);
return process.exit(1);
}
errorUnknownOption(opts, commandHelp) {
console.error(`Unknown option${opts.length > 1 ? "s" : ""}: ${opts.map((f) => `--${f}`).join(", ")}`);
console.log("");
console.log(commandHelp);
return process.exit(1);
}
errorTooManyArguments(expected, received, commandHelp) {
console.error(`error: too many arguments: expected ${expected}, received ${received}`);
console.log("");
console.log(commandHelp);
return process.exit(1);
}
errorAttachConflict() {
console.error(`Error: cannot use target name with --cdp, --endpoint, or --extension`);
return process.exit(1);
}
errorDetachNotAttached(session2) {
console.error(`Error: session '${session2}' was not attached; use \`playwright-cli${session2 !== "default" ? ` -s=${session2}` : ""} close\` to stop it.`);
return process.exit(1);
}
errorBrowserNotOpenForTool(session2) {
console.log(`The browser '${session2}' is not open, please run open first`);
console.log("");
console.log(` playwright-cli${session2 !== "default" ? ` -s=${session2}` : ""} open [params]`);
return process.exit(1);
}
errorAttachNoTarget() {
console.error(`Error: no target specified for attach command; use one of [name], --cdp, --endpoint, or --extension to specify the target to attach to.`);
return process.exit(1);
}
list({ all, browsers, servers, channelSessions }) {
const byWorkspace = /* @__PURE__ */ new Map();
for (const browser of browsers) {
let list = byWorkspace.get(browser.workspace);
if (!list) {
list = [];
byWorkspace.set(browser.workspace, list);
}
list.push(browser);
}
let count = 0;
for (const [workspaceKey, list] of byWorkspace) {
if (count === 0)
console.log("### Browsers");
if (all)
console.log(`${import_path48.default.relative(process.cwd(), workspaceKey) || "/"}:`);
for (const browser of list)
console.log(renderBrowser(browser));
count += list.length;
}
if (!all) {
if (!count)
console.log(" (no browsers)");
return;
}
if (servers?.length) {
if (count)
console.log("");
console.log("### Browser servers available for attach");
const serversByWorkspace = /* @__PURE__ */ new Map();
for (const server of servers) {
let list = serversByWorkspace.get(server.workspaceDir ?? "");
if (!list) {
list = [];
serversByWorkspace.set(server.workspaceDir ?? "", list);
}
list.push(server);
}
for (const [workspaceKey, list] of serversByWorkspace) {
if (workspaceKey)
console.log(`${import_path48.default.relative(process.cwd(), workspaceKey) || "/"}:`);
for (const server of list)
console.log(renderServer(server));
}
count += servers.length;
}
if (!count)
console.log(" (no browsers)");
if (channelSessions?.length) {
console.log("");
console.log("### Browsers available to attach via CDP");
for (const session2 of channelSessions)
console.log(renderChannelSession(session2));
}
}
closeAll(_sessions) {
}
deleteData(session2, result2) {
if (!result2.existed) {
console.log(`No user data found for browser '${session2}'.`);
return;
}
if (result2.deletedUserDataDir)
console.log(`Deleted user data for browser '${session2}'.`);
}
killAll(pids) {
for (const pid of pids)
console.log(`Killed daemon process ${pid}`);
if (pids.length === 0)
console.log("No daemon processes found.");
else
console.log(`Killed ${pids.length} daemon process${pids.length === 1 ? "" : "es"}.`);
}
open(session2, pid, toolResult) {
console.log(`### Browser \`${session2}\` opened with pid ${pid}.`);
if (toolResult)
console.log(toolResult);
}
attach(session2, pid, endpoint, toolResult) {
if (endpoint) {
console.log(`### Session \`${session2}\` created, attached to \`${endpoint}\`.`);
console.log(`Run commands with: playwright-cli --s=${session2} <command>`);
console.log("");
} else {
console.log(`### Browser \`${session2}\` opened with pid ${pid}.`);
}
if (toolResult)
console.log(toolResult);
}
close(session2, wasOpen) {
if (!wasOpen) {
console.log(`Browser '${session2}' is not open.`);
return;
}
console.log(`Browser '${session2}' closed
`);
}
detach(session2, wasAttached) {
if (!wasAttached) {
console.log(`Browser '${session2}' is not attached.`);
return;
}
console.log(`Browser '${session2}' detached
`);
}
installed() {
}
show(_session, pid) {
if (process.env.PWTEST_PRINT_DASHBOARD_PID_FOR_TEST)
console.log(`### Dashboard opened with pid ${pid}.`);
}
toolResult(text2) {
console.log(text2);
}
installStdio() {
return "inherit";
}
};
JsonOutput = class {
constructor() {
this.json = true;
}
version(v) {
this._emit({ version: v });
}
help(text2) {
this._emit({ help: text2 });
}
errorUnknownCommand(name, _globalHelp) {
this._emit({ isError: true, error: `Unknown command: ${name}` });
return process.exit(1);
}
errorUnknownOption(opts, _commandHelp) {
this._emit({ isError: true, error: `Unknown option${opts.length > 1 ? "s" : ""}: ${opts.map((f) => `--${f}`).join(", ")}` });
return process.exit(1);
}
errorTooManyArguments(expected, received, _commandHelp) {
this._emit({ isError: true, error: `error: too many arguments: expected ${expected}, received ${received}` });
return process.exit(1);
}
errorAttachConflict() {
this._emit({ isError: true, error: `cannot use target name with --cdp, --endpoint, or --extension` });
return process.exit(1);
}
errorDetachNotAttached(session2) {
this._emit({ isError: true, error: `session '${session2}' was not attached; use close to stop it.` });
return process.exit(1);
}
errorBrowserNotOpenForTool(session2) {
this._emit({ isError: true, error: `The browser '${session2}' is not open, please run open first` });
return process.exit(1);
}
errorAttachNoTarget() {
this._emit({ isError: true, error: `no target specified for attach command; use one of [name], --cdp, --endpoint, or --extension to specify the target to attach to.` });
return process.exit(1);
}
list({ all, browsers, servers, channelSessions }) {
const payload = { browsers };
if (all) {
payload.servers = servers ?? [];
payload.channelSessions = channelSessions ?? [];
}
this._emit(payload);
}
closeAll(sessions) {
this._emit({ closed: sessions });
}
deleteData(session2, result2) {
this._emit({ session: session2, deleted: result2.existed });
}
killAll(pids) {
this._emit({ killed: pids.length, pids });
}
open(session2, pid, toolResult) {
this._emit({ session: session2, pid, result: parseJsonText(toolResult) });
}
attach(session2, pid, endpoint, toolResult) {
this._emit({
session: session2,
pid,
...endpoint ? { endpoint } : {},
result: parseJsonText(toolResult)
});
}
close(session2, wasOpen) {
this._emit({ session: session2, status: wasOpen ? "closed" : "not-open" });
}
detach(session2, wasAttached) {
this._emit({ session: session2, status: wasAttached ? "detached" : "not-attached" });
}
installed() {
this._emit({ installed: true });
}
show(session2, pid) {
this._emit({ session: session2, pid });
}
toolResult(text2) {
console.log(text2);
}
installStdio() {
return "ignore";
}
_emit(value2) {
console.log(JSON.stringify(value2, null, 2));
}
};
}
});
// packages/playwright-core/src/tools/cli-client/registry.ts
function clientKey(clientInfo) {
return clientInfo.workspaceDir || clientInfo.workspaceDirHash;
}
function createClientInfo() {
const workspaceDir = findWorkspaceDir(process.cwd());
const version3 = process.env.PLAYWRIGHT_CLI_VERSION_FOR_TEST || packageJSON.version;
const hash = import_crypto24.default.createHash("sha1");
hash.update(workspaceDir || packageRoot);
const workspaceDirHash = hash.digest("hex").substring(0, 16);
return {
version: version3,
workspaceDir,
workspaceDirHash,
daemonProfilesDir: daemonProfilesDir(workspaceDirHash),
homeDir: import_os21.default.homedir()
};
}
function findWorkspaceDir(startDir) {
let dir = startDir;
for (let i = 0; i < 10; i++) {
if (import_fs52.default.existsSync(import_path49.default.join(dir, ".playwright")))
return dir;
const parentDir = import_path49.default.dirname(dir);
if (parentDir === dir)
break;
dir = parentDir;
}
return void 0;
}
function explicitSessionName(sessionName) {
return sessionName || process.env.PLAYWRIGHT_CLI_SESSION;
}
function resolveSessionName(sessionName) {
return explicitSessionName(sessionName) || "default";
}
var import_crypto24, import_fs52, import_os21, import_path49, Registry2, baseDaemonDir, daemonProfilesDir;
var init_registry2 = __esm({
"packages/playwright-core/src/tools/cli-client/registry.ts"() {
"use strict";
import_crypto24 = __toESM(require("crypto"));
import_fs52 = __toESM(require("fs"));
import_os21 = __toESM(require("os"));
import_path49 = __toESM(require("path"));
init_package();
Registry2 = class _Registry {
constructor(files) {
this._files = files;
}
entry(clientInfo, sessionName) {
const key = clientKey(clientInfo);
const entries = this._files.get(key) || [];
return entries.find((entry) => entry.config.name === sessionName);
}
entries(clientInfo) {
return this._files.get(clientKey(clientInfo)) || [];
}
entryMap() {
return this._files;
}
async loadEntry(clientInfo, sessionName) {
const entry = await _Registry._loadSessionEntry(clientInfo.daemonProfilesDir, sessionName + ".session");
if (!entry)
throw new Error(`Could not start the session "${sessionName}"`);
const key = clientKey(clientInfo);
let list = this._files.get(key);
if (!list) {
list = [];
this._files.set(key, list);
}
const oldIndex = list.findIndex((e) => e.config.name === sessionName);
if (oldIndex !== -1)
list.splice(oldIndex, 1);
list.push(entry);
return entry;
}
static async _loadSessionEntry(daemonDir, file) {
try {
const fileName = import_path49.default.join(daemonDir, file);
const data = await import_fs52.default.promises.readFile(fileName, "utf-8");
const config = JSON.parse(data);
if (!config.name)
config.name = import_path49.default.basename(file, ".session");
if (!config.timestamp)
config.timestamp = 0;
return { file: fileName, config, daemonDir };
} catch {
return void 0;
}
}
static async load() {
const sessions = /* @__PURE__ */ new Map();
const hashDirs = await import_fs52.default.promises.readdir(baseDaemonDir).catch(() => []);
for (const workspaceDirHash of hashDirs) {
const daemonDir = import_path49.default.join(baseDaemonDir, workspaceDirHash);
const stat = await import_fs52.default.promises.stat(daemonDir);
if (!stat.isDirectory())
continue;
const files = await import_fs52.default.promises.readdir(daemonDir).catch(() => []);
for (const file of files) {
if (!file.endsWith(".session"))
continue;
const entry = await _Registry._loadSessionEntry(daemonDir, file);
if (!entry)
continue;
const key = entry.config.workspaceDir || workspaceDirHash;
let list = sessions.get(key);
if (!list) {
list = [];
sessions.set(key, list);
}
list.push(entry);
}
}
return new _Registry(sessions);
}
};
baseDaemonDir = (() => {
if (process.env.PLAYWRIGHT_DAEMON_SESSION_DIR)
return process.env.PLAYWRIGHT_DAEMON_SESSION_DIR;
let localCacheDir;
if (process.platform === "linux")
localCacheDir = process.env.XDG_CACHE_HOME || import_path49.default.join(import_os21.default.homedir(), ".cache");
if (process.platform === "darwin")
localCacheDir = import_path49.default.join(import_os21.default.homedir(), "Library", "Caches");
if (process.platform === "win32")
localCacheDir = process.env.LOCALAPPDATA || import_path49.default.join(import_os21.default.homedir(), "AppData", "Local");
if (!localCacheDir)
throw new Error("Unsupported platform: " + process.platform);
return import_path49.default.join(localCacheDir, "ms-playwright", "daemon");
})();
daemonProfilesDir = (workspaceDirHash) => {
return import_path49.default.join(baseDaemonDir, workspaceDirHash);
};
}
});
// packages/playwright-core/src/tools/utils/socketConnection.ts
function compareSemver(a, b) {
const aBase = a.replace(/-.*$/, "");
const bBase = b.replace(/-.*$/, "");
const aParts = aBase.split(".").map(Number);
const bParts = bBase.split(".").map(Number);
for (let i = 0; i < 3; i++) {
if (aParts[i] > bParts[i])
return 1;
if (aParts[i] < bParts[i])
return -1;
}
const aTimestamp = parseSuffixTimestamp(a);
const bTimestamp = parseSuffixTimestamp(b);
if (aTimestamp > bTimestamp)
return 1;
if (aTimestamp < bTimestamp)
return -1;
return 0;
}
function parseSuffixTimestamp(version3) {
const match = version3.match(/^\d+\.\d+\.\d+-(?:alpha|beta)-(.+)$/);
if (!match)
return Infinity;
const suffix = match[1];
if (/^\d{4}-\d{2}-\d{2}$/.test(suffix))
return new Date(suffix).getTime();
return Number(suffix);
}
var SocketConnection;
var init_socketConnection = __esm({
"packages/playwright-core/src/tools/utils/socketConnection.ts"() {
"use strict";
SocketConnection = class {
constructor(socket) {
this._pendingBuffers = [];
this._socket = socket;
socket.on("data", (buffer) => this._onData(buffer));
socket.on("close", () => {
this.onclose?.();
});
socket.on("error", (e) => console.error(`error: ${e.message}`));
}
async send(message) {
await new Promise((resolve, reject) => {
this._socket.write(`${JSON.stringify(message)}
`, (error) => {
if (error)
reject(error);
else
resolve(void 0);
});
});
}
close() {
this._socket.destroy();
}
_onData(buffer) {
let end = buffer.indexOf("\n");
if (end === -1) {
this._pendingBuffers.push(buffer);
return;
}
this._pendingBuffers.push(buffer.slice(0, end));
const message = Buffer.concat(this._pendingBuffers).toString();
this._dispatchMessage(message);
let start3 = end + 1;
end = buffer.indexOf("\n", start3);
while (end !== -1) {
const message2 = buffer.toString(void 0, start3, end);
this._dispatchMessage(message2);
start3 = end + 1;
end = buffer.indexOf("\n", start3);
}
this._pendingBuffers = [buffer.slice(start3)];
}
_dispatchMessage(message) {
try {
this.onmessage?.(JSON.parse(message));
} catch (e) {
console.error("failed to dispatch message", e);
}
}
};
}
});
// packages/playwright-core/src/tools/cli-client/session.ts
var import_child_process4, import_fs53, import_net9, import_os22, import_path50, Session2, SocketConnectionClient;
var init_session = __esm({
"packages/playwright-core/src/tools/cli-client/session.ts"() {
"use strict";
import_child_process4 = require("child_process");
import_fs53 = __toESM(require("fs"));
import_net9 = __toESM(require("net"));
import_os22 = __toESM(require("os"));
import_path50 = __toESM(require("path"));
init_package();
init_socketConnection();
init_registry2();
Session2 = class {
constructor(sessionFile) {
this.config = sessionFile.config;
this.name = this.config.name;
this._sessionFile = sessionFile;
}
isCompatible(clientInfo) {
return compareSemver(clientInfo.version, this.config.version) >= 0;
}
async run(clientInfo, args, options2) {
if (!this.isCompatible(clientInfo))
throw new Error(`Client is v${clientInfo.version}, session '${this.name}' is v${this.config.version}. Run
playwright-cli${this.name !== "default" ? ` -s=${this.name}` : ""} open
to restart the browser session.`);
const { socket } = await this._connect();
if (!socket)
throw new Error(`Browser '${this.name}' is not open. Run
playwright-cli${this.name !== "default" ? ` -s=${this.name}` : ""} open
to start the browser session.`);
return await SocketConnectionClient.sendAndClose(socket, "run", { args, cwd: process.cwd(), raw: options2?.raw, json: options2?.json });
}
async stop() {
if (!await this.canConnect())
return { wasOpen: false };
await this._stopDaemon();
return { wasOpen: true };
}
async deleteData() {
await this.stop();
const dataDirs = await import_fs53.default.promises.readdir(this._sessionFile.daemonDir).catch(() => []);
const matchingEntries = dataDirs.filter((file) => file === `${this.name}.session` || file.startsWith(`ud-${this.name}-`));
if (matchingEntries.length === 0)
return { existed: false, deletedUserDataDir: false };
let deletedUserDataDir = false;
for (const entry of matchingEntries) {
const userDataDir = import_path50.default.resolve(this._sessionFile.daemonDir, entry);
for (let i = 0; i < 5; i++) {
try {
await import_fs53.default.promises.rm(userDataDir, { recursive: true });
if (entry.startsWith("ud-"))
deletedUserDataDir = true;
break;
} catch (e) {
if (e.code === "ENOENT")
break;
await new Promise((resolve) => setTimeout(resolve, 1e3));
if (i === 4)
throw e;
}
}
}
return { existed: true, deletedUserDataDir };
}
async _connect() {
return await new Promise((resolve) => {
const socket = import_net9.default.createConnection(this.config.socketPath, () => {
resolve({ socket });
});
socket.on("error", (error) => {
if (import_os22.default.platform() !== "win32")
void import_fs53.default.promises.unlink(this.config.socketPath).catch(() => {
}).then(() => resolve({ error }));
else
resolve({ error });
});
});
}
async canConnect() {
const { socket } = await this._connect();
if (socket) {
socket.destroy();
return true;
}
return false;
}
static async startDaemon(clientInfo, cliArgs, mode) {
await import_fs53.default.promises.mkdir(clientInfo.daemonProfilesDir, { recursive: true });
const cliPath = libPath("entry", "cliDaemon.js");
const sessionName = resolveSessionName(cliArgs.session);
const errLog = import_path50.default.join(clientInfo.daemonProfilesDir, sessionName + ".err");
const err = import_fs53.default.openSync(errLog, "w");
const args = [
cliPath,
sessionName
];
if (cliArgs.headed)
args.push("--headed");
if (cliArgs.extension)
args.push("--extension");
if (cliArgs.browser)
args.push(`--browser=${cliArgs.browser}`);
if (cliArgs.persistent)
args.push("--persistent");
if (cliArgs.profile)
args.push(`--profile=${cliArgs.profile}`);
if (cliArgs.config)
args.push(`--config=${cliArgs.config}`);
if (cliArgs.cdp)
args.push(`--cdp=${cliArgs.cdp}`);
if (cliArgs.endpoint)
args.push(`--endpoint=${cliArgs.endpoint}`);
else if (mode === "attach" && process.env.PLAYWRIGHT_CLI_SESSION)
args.push(`--endpoint=${process.env.PLAYWRIGHT_CLI_SESSION}`);
const child = (0, import_child_process4.spawn)(process.execPath, args, {
detached: true,
stdio: ["ignore", "pipe", err],
cwd: process.cwd()
// Will be used as root.
});
let signalled = false;
const sigintHandler2 = () => {
signalled = true;
child.kill("SIGINT");
};
const sigtermHandler2 = () => {
signalled = true;
child.kill("SIGTERM");
};
process.on("SIGINT", sigintHandler2);
process.on("SIGTERM", sigtermHandler2);
let outLog = "";
const rejectWithPid = (reject, message) => reject(Object.assign(new Error(`Daemon pid=${child.pid}: ${message}`), { daemonPid: child.pid }));
await new Promise((resolve, reject) => {
child.stdout.on("data", (data) => {
outLog += data.toString();
if (!outLog.includes("<EOF>"))
return;
const errorMatch = outLog.match(/### Error\n([\s\S]*)<EOF>/);
const error = errorMatch ? errorMatch[1].trim() : void 0;
if (error) {
const errLogContent = import_fs53.default.readFileSync(errLog, "utf-8");
rejectWithPid(reject, error + (errLogContent ? "\n" + errLogContent : ""));
}
const successMatch = outLog.match(/### Success\nDaemon listening on (.*)\n<EOF>/);
if (successMatch)
resolve();
});
child.on("close", (code) => {
if (!signalled) {
const errLogContent = import_fs53.default.readFileSync(errLog, "utf-8");
rejectWithPid(reject, `Daemon process exited with code ${code}` + (errLogContent ? "\n" + errLogContent : ""));
}
});
});
process.off("SIGINT", sigintHandler2);
process.off("SIGTERM", sigtermHandler2);
child.stdout.destroy();
child.unref();
return { pid: child.pid, sessionName, endpoint: cliArgs.endpoint };
}
async _stopDaemon() {
const { socket } = await this._connect();
if (!socket)
return;
let error;
await SocketConnectionClient.sendAndClose(socket, "stop", {}).catch((e) => error = e);
if (error && !error?.message?.includes("Session closed"))
throw error;
}
async deleteSessionConfig() {
await import_fs53.default.promises.rm(this._sessionFile.file).catch(() => {
});
}
};
SocketConnectionClient = class _SocketConnectionClient {
constructor(socket) {
this._nextMessageId = 1;
this._callbacks = /* @__PURE__ */ new Map();
this._connection = new SocketConnection(socket);
this._connection.onmessage = (message) => this._onMessage(message);
this._connection.onclose = () => this._rejectCallbacks();
}
async send(method, params2 = {}) {
const messageId = this._nextMessageId++;
const message = {
id: messageId,
method,
params: params2
};
const responsePromise = new Promise((resolve, reject) => {
this._callbacks.set(messageId, { resolve, reject, method, params: params2 });
});
const [result2] = await Promise.all([responsePromise, this._connection.send(message)]);
return result2;
}
static async sendAndClose(socket, method, params2 = {}) {
const connection = new _SocketConnectionClient(socket);
try {
return await connection.send(method, params2);
} finally {
connection.close();
}
}
close() {
this._connection.close();
}
_onMessage(object) {
if (object.id && this._callbacks.has(object.id)) {
const callback = this._callbacks.get(object.id);
this._callbacks.delete(object.id);
if (object.error)
callback.reject(new Error(object.error));
else
callback.resolve(object.result);
} else if (object.id) {
throw new Error(`Unexpected message id: ${object.id}`);
} else {
throw new Error(`Unexpected message without id: ${JSON.stringify(object)}`);
}
}
_rejectCallbacks() {
for (const callback of this._callbacks.values())
callback.reject(new Error("Session closed"));
this._callbacks.clear();
}
};
}
});
// packages/playwright-core/src/tools/cli-client/program.ts
async function program2(options2) {
const clientInfo = createClientInfo();
const help = require(libPath("tools", "cli-client", "help.json"));
const argv = process.argv.slice(2);
const boolean = [...help.booleanOptions, ...booleanOptions];
const args = minimist(argv, { boolean, string: ["_"] });
if (args.s) {
args.session = args.s;
delete args.s;
}
const output = args.json ? new JsonOutput() : new TextOutput();
const commandName = args._?.[0];
if (args.version || args.v) {
output.version(options2?.embedderVersion ?? clientInfo.version);
process.exit(0);
}
const command = commandName && help.commands[commandName];
if (args.help || args.h || !commandName) {
if (command) {
output.help(command.help);
} else {
const lines = ["playwright-cli - run playwright mcp commands from terminal"];
if (process.env.CLAUDECODE || process.env.COPILOT_CLI)
lines.push(`Agent skill: ${import_path51.default.relative(process.cwd(), libPath("tools", "cli-client", "skill", "SKILL.md"))}`);
lines.push(help.global);
output.help(lines.join("\n\n"));
}
process.exit(0);
}
if (!command)
output.errorUnknownCommand(commandName, help.global);
validateFlags(args, command, output);
validateArgs(args, command, output);
const registry2 = await Registry2.load();
const sessionName = resolveSessionName(args.session);
switch (commandName) {
case "list": {
const data = await collectList(registry2, clientInfo, !!args.all);
output.list(data);
return;
}
case "close-all": {
const entries = registry2.entries(clientInfo);
const closed = [];
for (const entry of entries) {
await new Session2(entry).stop();
closed.push(entry.config.name);
}
output.closeAll(closed);
return;
}
case "delete-data": {
const entry = registry2.entry(clientInfo, sessionName);
if (!entry) {
output.deleteData(sessionName, { existed: false, deletedUserDataDir: false });
return;
}
const result2 = await new Session2(entry).deleteData();
output.deleteData(sessionName, result2);
return;
}
case "kill-all": {
const pids = await killAllDaemons();
output.killAll(pids);
return;
}
case "open": {
const { pid } = await startSession(sessionName, registry2, clientInfo, args, "open");
const newEntry = await registry2.loadEntry(clientInfo, sessionName);
const params2 = args._.slice(1);
const toolText = await runInSessionOrStop(newEntry, clientInfo, { _: ["goto", ...params2.length ? params2 : ["about:blank"]] }, output);
output.open(sessionName, pid, toolText);
return;
}
case "attach": {
const attachTarget = args._[1];
if (attachTarget && (args.cdp || args.endpoint || args.extension))
output.errorAttachConflict();
if (attachTarget)
args.endpoint = attachTarget;
const extensionChannel = typeof args.extension === "string" ? args.extension : void 0;
if (extensionChannel) {
args.browser = extensionChannel;
args.extension = true;
}
const cdpChannel = typeof args.cdp === "string" && isKnownChannel(args.cdp) ? args.cdp : void 0;
const targetName = attachTarget ?? cdpChannel ?? extensionChannel ?? args.cdp;
if (!targetName)
output.errorAttachNoTarget();
const attachSessionName = explicitSessionName(args.session) ?? attachTarget ?? cdpChannel ?? extensionChannel ?? sessionName;
args.session = attachSessionName;
const { pid } = await startSession(attachSessionName, registry2, clientInfo, args, "attach");
const newEntry = await registry2.loadEntry(clientInfo, attachSessionName);
const toolText = await runInSessionOrStop(newEntry, clientInfo, { _: ["snapshot"], filename: "<auto>" }, output);
output.attach(attachSessionName, pid, targetName, toolText);
return;
}
case "close": {
const closeEntry = registry2.entry(clientInfo, sessionName);
const { wasOpen } = closeEntry ? await new Session2(closeEntry).stop() : { wasOpen: false };
output.close(sessionName, wasOpen);
return;
}
case "detach": {
const detachEntry = registry2.entry(clientInfo, sessionName);
if (detachEntry && !detachEntry.config.attached)
output.errorDetachNotAttached(sessionName);
const { wasOpen } = detachEntry ? await new Session2(detachEntry).stop() : { wasOpen: false };
output.detach(sessionName, wasOpen);
return;
}
case "install":
await runInitWorkspace(args, output);
output.installed();
return;
case "install-browser":
await installBrowser2();
output.installed();
return;
case "show": {
const daemonScript = libPath("entry", "dashboardApp.js");
const daemonArgs = [
daemonScript,
`--sessionName=${sessionName}`,
`--workspaceDir=${clientInfo.workspaceDir ?? ""}`
];
if (args.port !== void 0)
daemonArgs.push(`--port=${args.port}`);
if (args.host !== void 0)
daemonArgs.push(`--host=${args.host}`);
if (args.kill) {
daemonArgs.push(`--kill`);
const child2 = (0, import_child_process5.spawn)(process.execPath, daemonArgs, { stdio: "ignore" });
await new Promise((resolve) => child2.on("exit", () => resolve()));
return;
}
if (args.annotate) {
const entry = registry2.entry(clientInfo, sessionName);
if (!entry)
output.errorBrowserNotOpenForTool(sessionName);
args.raw = true;
const text2 = await runInSession(entry, clientInfo, args, output);
output.toolResult(text2);
return;
}
const foreground = args.port !== void 0;
const child = (0, import_child_process5.spawn)(process.execPath, daemonArgs, {
detached: !foreground,
stdio: foreground ? "inherit" : "ignore"
});
if (foreground) {
await new Promise((resolve) => child.on("exit", () => resolve()));
return;
}
child.unref();
output.show(sessionName, child.pid);
return;
}
default: {
const entry = registry2.entry(clientInfo, sessionName);
if (!entry)
output.errorBrowserNotOpenForTool(sessionName);
if (command.raw)
args.raw = true;
const text2 = await runInSession(entry, clientInfo, args, output);
output.toolResult(text2);
}
}
}
async function startSession(sessionName, registry2, clientInfo, args, mode) {
const entry = registry2.entry(clientInfo, sessionName);
if (entry)
await new Session2(entry).stop();
return await Session2.startDaemon(clientInfo, args, mode);
}
async function runInSession(entry, clientInfo, args, output) {
const raw = !!args.raw;
for (const globalOption of globalOptions)
delete args[globalOption];
const session2 = new Session2(entry);
const result2 = await session2.run(clientInfo, args, { raw, json: output.json });
return result2.text;
}
async function runInSessionOrStop(entry, clientInfo, args, output) {
try {
return await runInSession(entry, clientInfo, args, output);
} catch (e) {
await new Session2(entry).stop().catch(() => {
});
throw e;
}
}
async function runInitWorkspace(args, output) {
const cliPath = libPath("entry", "cliDaemon.js");
const daemonArgs = [cliPath, "--init-workspace", ...args.skills ? ["--init-skills", String(args.skills)] : []];
await new Promise((resolve, reject) => {
const child = (0, import_child_process5.spawn)(process.execPath, daemonArgs, {
stdio: output.installStdio(),
cwd: process.cwd()
});
child.on("close", (code) => {
if (code === 0)
resolve();
else
reject(new Error(`Workspace initialization failed with exit code ${code}`));
});
});
}
async function installBrowser2() {
const argv = process.argv.map((arg) => arg === "install-browser" ? "install" : arg);
const { libCli } = (init_coreBundle(), __toCommonJS(coreBundle_exports));
const { program: program4 } = require("./utilsBundle");
if (!program4.version())
libCli.decorateProgram(program4);
program4.parse(argv);
}
async function killAllDaemons() {
const platform = import_os23.default.platform();
const pidFilterEnv = process.env.PWTEST_KILL_ALL_PID_FILTER_FOR_TEST;
const pidFilter = pidFilterEnv ? new Set(pidFilterEnv.split(",").map((p) => parseInt(p, 10)).filter((n) => !isNaN(n))) : void 0;
const killed = [];
try {
if (platform === "win32") {
const clauses = [`(${daemonProcessPatterns.map((p) => `$_.CommandLine -like '*${p}*'`).join(" -or ")})`];
if (pidFilter)
clauses.push(`(${[...pidFilter].map((p) => `$_.ProcessId -eq ${p}`).join(" -or ")})`);
const whereClause = clauses.join(" -and ");
const result2 = (0, import_child_process5.execSync)(
`powershell -NoProfile -NonInteractive -Command "Get-CimInstance Win32_Process | Where-Object { ${whereClause} } | ForEach-Object { Stop-Process -Id $_.ProcessId -Force -ErrorAction SilentlyContinue; $_.ProcessId }"`,
{ encoding: "utf-8" }
);
const pids = result2.split("\n").map((line) => line.trim()).filter((line) => /^\d+$/.test(line));
for (const pid of pids)
killed.push(parseInt(pid, 10));
} else {
const result2 = (0, import_child_process5.execSync)("ps auxww", { encoding: "utf-8" });
const lines = result2.split("\n");
for (const line of lines) {
if (daemonProcessPatterns.some((p) => line.includes(p))) {
const parts = line.trim().split(/\s+/);
const pid = parts[1];
if (pid && /^\d+$/.test(pid)) {
const numericPid = parseInt(pid, 10);
if (pidFilter && !pidFilter.has(numericPid))
continue;
try {
process.kill(numericPid, "SIGKILL");
killed.push(numericPid);
} catch {
}
}
}
}
}
} catch (e) {
}
return killed;
}
async function collectList(registry2, clientInfo, all) {
const browsers = [];
const entries = registry2.entryMap();
const serverEntries = await serverRegistry.list();
const key = clientKey(clientInfo);
for (const [workspaceKey, list] of entries) {
if (!all && workspaceKey !== key)
continue;
for (const entry of list) {
const session2 = new Session2(entry);
const canConnect = await session2.canConnect();
if (!canConnect) {
await session2.deleteSessionConfig();
continue;
}
const config = session2.config;
const channel = config.browser?.launchOptions.channel ?? config.browser?.browserName;
browsers.push({
name: session2.name,
workspace: workspaceKey,
status: canConnect ? "open" : "closed",
browserType: channel,
userDataDir: config.browser?.userDataDir ?? null,
headed: config.browser ? !config.browser.launchOptions.headless : void 0,
persistent: !!config.cli.persistent,
attached: !!config.attached,
compatible: session2.isCompatible(clientInfo),
version: config.version
});
}
}
if (!all)
return { all, browsers };
const servers = [...serverEntries.values()].flat();
return { all, browsers, servers, channelSessions: await listChannelSessions() };
}
function validateFlags(args, command, output) {
const unknownFlags = [];
for (const key of Object.keys(args)) {
if (key === "_")
continue;
if (globalOptions.includes(key))
continue;
if (!(key in command.flags))
unknownFlags.push(key);
}
if (unknownFlags.length)
output.errorUnknownOption(unknownFlags, command.help);
}
function validateArgs(args, command, output) {
const positional = args._.slice(1);
if (positional.length > command.args.length)
output.errorTooManyArguments(command.args.length, positional.length, command.help);
}
var import_child_process5, import_os23, import_path51, globalOptions, booleanOptions, daemonProcessPatterns;
var init_program = __esm({
"packages/playwright-core/src/tools/cli-client/program.ts"() {
"use strict";
import_child_process5 = require("child_process");
import_os23 = __toESM(require("os"));
import_path51 = __toESM(require("path"));
init_channelSessions();
init_output();
init_registry2();
init_session();
init_package();
init_serverRegistry();
init_minimist();
globalOptions = [
"json",
"raw",
"session"
];
booleanOptions = [
"all",
"help",
"json",
"raw",
"version"
];
daemonProcessPatterns = ["run-mcp-server", "run-cli-server", "cli-daemon", "cliDaemon.js", "dashboardApp.js"];
}
});
// packages/playwright-core/src/cli/program.ts
var program_exports = {};
__export(program_exports, {
decorateProgram: () => decorateProgram
});
function decorateProgram(program4) {
program4.version("Version " + (process.env.PW_CLI_DISPLAY_VERSION || packageJSON.version)).name(buildBasePlaywrightCLICommand(process.env.PW_LANG_NAME));
program4.command("mark-docker-image [dockerImageNameTemplate]", { hidden: true }).description("mark docker image").allowUnknownOption(true).action(async function(dockerImageNameTemplate) {
markDockerImage(dockerImageNameTemplate).catch(logErrorAndExit);
});
commandWithOpenOptions("open [url]", "open page in browser specified via -b, --browser", []).action(async function(url2, options2) {
open6(options2, url2).catch(logErrorAndExit);
}).addHelpText("afterAll", `
Examples:
$ open
$ open -b webkit https://example.com`);
commandWithOpenOptions(
"codegen [url]",
"open page and generate code for user actions",
[
["-o, --output <file name>", "saves the generated script to a file"],
["--target <language>", `language to generate, one of javascript, playwright-test, python, python-async, python-pytest, csharp, csharp-mstest, csharp-nunit, csharp-xunit, java, java-junit`, codegenId()],
["--test-id-attribute <attributeName>", "use the specified attribute to generate data test ID selectors"]
]
).action(async function(url2, options2) {
await codegen(options2, url2);
}).addHelpText("afterAll", `
Examples:
$ codegen
$ codegen --target=python
$ codegen -b webkit https://example.com`);
program4.command("install [browser...]").description("ensure browsers necessary for this version of Playwright are installed").option("--with-deps", "install system dependencies for browsers").option("--dry-run", "do not execute installation, only print information").option("--list", "prints list of browsers from all playwright installations").option("--force", "force reinstall of already installed browsers").option("--only-shell", "only install headless shell when installing chromium").option("--no-shell", "do not install chromium headless shell").action(async function(args, options2) {
try {
await installBrowsers(args, options2);
} catch (e) {
console.log(`Failed to install browsers
${e}`);
gracefullyProcessExitDoNotHang(1);
}
}).addHelpText("afterAll", `
Examples:
- $ install
Install default browsers.
- $ install chrome firefox
Install custom browsers, supports chromium, firefox, webkit, chromium-headless-shell.`);
program4.command("uninstall").description("Removes browsers used by this installation of Playwright from the system (chromium, firefox, webkit, ffmpeg). This does not include branded channels.").option("--all", "Removes all browsers used by any Playwright installation from the system.").action(async (options2) => {
uninstallBrowsers(options2).catch(logErrorAndExit);
});
program4.command("install-deps [browser...]").description("install dependencies necessary to run browsers (will ask for sudo permissions)").option("--dry-run", "Do not modify the system. On Linux, simulate the install via apt-get and exit with a non-zero code if any required packages are missing; on Windows, print the install command.").action(async function(args, options2) {
try {
await installDeps(args, options2);
} catch (e) {
console.log(`Failed to install browser dependencies
${e}`);
gracefullyProcessExitDoNotHang(1);
}
}).addHelpText("afterAll", `
Examples:
- $ install-deps
Install dependencies for default browsers.
- $ install-deps chrome firefox
Install dependencies for specific browsers, supports chromium, firefox, webkit, chromium-headless-shell.
- $ install-deps --dry-run
Report which required system dependencies are missing without modifying the system. Exits non-zero if any are missing. Useful for non-interactive verification scripts.`);
const browsers = [
{ alias: "cr", name: "Chromium", type: "chromium" },
{ alias: "ff", name: "Firefox", type: "firefox" },
{ alias: "wk", name: "WebKit", type: "webkit" }
];
for (const { alias, name, type: type3 } of browsers) {
commandWithOpenOptions(`${alias} [url]`, `open page in ${name}`, []).action(async function(url2, options2) {
open6({ ...options2, browser: type3 }, url2).catch(logErrorAndExit);
}).addHelpText("afterAll", `
Examples:
$ ${alias} https://example.com`);
}
commandWithOpenOptions(
"screenshot <url> <filename>",
"capture a page screenshot",
[
["--wait-for-selector <selector>", "wait for selector before taking a screenshot"],
["--wait-for-timeout <timeout>", "wait for timeout in milliseconds before taking a screenshot"],
["--full-page", "whether to take a full page screenshot (entire scrollable area)"]
]
).action(async function(url2, filename, command) {
screenshot3(command, command, url2, filename).catch(logErrorAndExit);
}).addHelpText("afterAll", `
Examples:
$ screenshot -b webkit https://example.com example.png`);
commandWithOpenOptions(
"pdf <url> <filename>",
"save page as pdf",
[
["--paper-format <format>", "paper format: Letter, Legal, Tabloid, Ledger, A0, A1, A2, A3, A4, A5, A6"],
["--wait-for-selector <selector>", "wait for given selector before saving as pdf"],
["--wait-for-timeout <timeout>", "wait for given timeout in milliseconds before saving as pdf"]
]
).action(async function(url2, filename, options2) {
pdf2(options2, options2, url2, filename).catch(logErrorAndExit);
}).addHelpText("afterAll", `
Examples:
$ pdf https://example.com example.pdf`);
program4.command("run-driver", { hidden: true }).action(async function(options2) {
runDriver();
});
program4.command("run-server", { hidden: true }).option("--port <port>", "Server port").option("--host <host>", "Server host").option("--path <path>", "Endpoint Path", "/").option("--max-clients <maxClients>", "Maximum clients").option("--mode <mode>", 'Server mode, either "default" or "extension"').option("--artifacts-dir <artifactsDir>", "Artifacts directory").option("--unsafe", "Allow clients to set unsafe launch options (args, executablePath, ignoreAllDefaultArgs, etc)").action(async function(options2) {
runServer({
port: options2.port ? +options2.port : void 0,
host: options2.host,
path: options2.path,
maxConnections: options2.maxClients ? +options2.maxClients : Infinity,
extension: options2.mode === "extension" || !!process.env.PW_EXTENSION_MODE,
artifactsDir: options2.artifactsDir,
unsafe: !!options2.unsafe
}).catch(logErrorAndExit);
});
program4.command("print-api-json", { hidden: true }).action(async function(options2) {
printApiJson();
});
program4.command("launch-server", { hidden: true }).requiredOption("--browser <browserName>", 'Browser name, one of "chromium", "firefox" or "webkit"').option("--config <path-to-config-file>", "JSON file with launchServer options").action(async function(options2) {
launchBrowserServer(options2.browser, options2.config);
});
program4.command("show-trace [trace]").option("-b, --browser <browserType>", "browser to use, one of cr, chromium, ff, firefox, wk, webkit", "chromium").option("-h, --host <host>", "Host to serve trace on; specifying this option opens trace in a browser tab").option("-p, --port <port>", "Port to serve trace on, 0 for any free port; specifying this option opens trace in a browser tab").option("--stdin", "Accept trace URLs over stdin to update the viewer").description("show trace viewer").action(async function(trace, options2) {
if (options2.browser === "cr")
options2.browser = "chromium";
if (options2.browser === "ff")
options2.browser = "firefox";
if (options2.browser === "wk")
options2.browser = "webkit";
const openOptions = {
host: options2.host,
port: +options2.port,
isServer: !!options2.stdin
};
if (options2.port !== void 0 || options2.host !== void 0)
runTraceInBrowser(trace, openOptions).catch(logErrorAndExit);
else
runTraceViewerApp(trace, options2.browser, openOptions).catch(logErrorAndExit);
}).addHelpText("afterAll", `
Examples:
$ show-trace
$ show-trace https://example.com/trace.zip`);
addTraceCommands(program4, logErrorAndExit);
program4.command("cli", { hidden: true }).allowExcessArguments(true).allowUnknownOption(true).helpOption(false).action(async (options2) => {
process.argv.splice(process.argv.indexOf("cli"), 1);
program2().catch(logErrorAndExit);
});
}
function logErrorAndExit(e) {
if (process.env.PWDEBUGIMPL)
console.error(e);
else
console.error(e.name + ": " + e.message);
gracefullyProcessExitDoNotHang(1);
}
function codegenId() {
return process.env.PW_LANG_NAME || "playwright-test";
}
function commandWithOpenOptions(command, description, options2) {
let result2 = program3.command(command).description(description);
for (const option of options2)
result2 = result2.option(option[0], ...option.slice(1));
return result2.option("-b, --browser <browserType>", "browser to use, one of cr, chromium, ff, firefox, wk, webkit", "chromium").option("--block-service-workers", "block service workers").option("--channel <channel>", 'Chromium distribution channel, "chrome", "chrome-beta", "msedge-dev", etc').option("--color-scheme <scheme>", 'emulate preferred color scheme, "light" or "dark"').option("--device <deviceName>", 'emulate device, for example "iPhone 11"').option("--geolocation <coordinates>", 'specify geolocation coordinates, for example "37.819722,-122.478611"').option("--ignore-https-errors", "ignore https errors").option("--load-storage <filename>", "load context storage state from the file, previously saved with --save-storage").option("--lang <language>", 'specify language / locale, for example "en-GB"').option("--proxy-server <proxy>", 'specify proxy server, for example "http://myproxy:3128" or "socks5://myproxy:8080"').option("--proxy-bypass <bypass>", 'comma-separated domains to bypass proxy, for example ".com,chromium.org,.domain.com"').option("--save-har <filename>", "save HAR file with all network activity at the end").option("--save-har-glob <glob pattern>", "filter entries in the HAR by matching url against this glob pattern").option("--save-storage <filename>", "save context storage state at the end, for later use with --load-storage").option("--timezone <time zone>", 'time zone to emulate, for example "Europe/Rome"').option("--timeout <timeout>", "timeout for Playwright actions in milliseconds, no timeout by default").option("--user-agent <ua string>", "specify user agent string").option("--user-data-dir <directory>", "use the specified user data directory instead of a new context").option("--viewport-size <size>", 'specify browser viewport size in pixels, for example "1280, 720"');
}
function buildBasePlaywrightCLICommand(cliTargetLang) {
switch (cliTargetLang) {
case "python":
return `playwright`;
case "java":
return `mvn exec:java -e -D exec.mainClass=com.microsoft.playwright.CLI -D exec.args="...options.."`;
case "csharp":
return `pwsh bin/Debug/netX/playwright.ps1`;
default: {
const packageManagerCommand = getPackageManagerExecCommand();
return `${packageManagerCommand} playwright`;
}
}
}
var program3;
var init_program2 = __esm({
"packages/playwright-core/src/cli/program.ts"() {
"use strict";
init_bootstrap();
init_processLauncher();
init_env();
init_package();
init_traceCli();
init_driver();
init_installActions();
init_browserActions();
init_installActions();
init_traceViewer();
init_browserActions();
init_program();
({ program: program3 } = require("./utilsBundle"));
}
});
// packages/playwright-core/src/cli/programWithTestStub.ts
var programWithTestStub_exports = {};
__export(programWithTestStub_exports, {
decorateProgram: () => decorateProgram2
});
function printPlaywrightTestError(command) {
const packages = [];
for (const pkg of ["playwright", "playwright-chromium", "playwright-firefox", "playwright-webkit"]) {
try {
require.resolve(pkg);
packages.push(pkg);
} catch (e) {
}
}
if (!packages.length)
packages.push("playwright");
const packageManager = getPackageManager();
if (packageManager === "yarn") {
console.error(`Please install @playwright/test package before running "yarn playwright ${command}"`);
console.error(` yarn remove ${packages.join(" ")}`);
console.error(" yarn add -D @playwright/test");
} else if (packageManager === "pnpm") {
console.error(`Please install @playwright/test package before running "pnpm exec playwright ${command}"`);
console.error(` pnpm remove ${packages.join(" ")}`);
console.error(" pnpm add -D @playwright/test");
} else {
console.error(`Please install @playwright/test package before running "npx playwright ${command}"`);
console.error(` npm uninstall ${packages.join(" ")}`);
console.error(" npm install -D @playwright/test");
}
}
function addExternalPlaywrightTestCommands(program4) {
for (const [command, description] of kExternalPlaywrightTestCommands) {
const playwrightTest = program4.command(command).allowUnknownOption(true).allowExcessArguments(true);
playwrightTest.description(`${description} Available in @playwright/test package.`);
playwrightTest.action(async () => {
printPlaywrightTestError(command);
gracefullyProcessExitDoNotHang(1);
});
}
}
function decorateProgram2(program4) {
if (!process.env.PW_LANG_NAME)
addExternalPlaywrightTestCommands(program4);
}
var kExternalPlaywrightTestCommands;
var init_programWithTestStub = __esm({
"packages/playwright-core/src/cli/programWithTestStub.ts"() {
"use strict";
init_processLauncher();
init_env();
kExternalPlaywrightTestCommands = [
["test", "Run tests with Playwright Test."],
["show-report", "Show Playwright Test HTML report."],
["merge-reports", "Merge Playwright Test Blob reports"]
];
}
});
// packages/playwright-core/src/outofprocess.ts
var outofprocess_exports = {};
__export(outofprocess_exports, {
start: () => start
});
async function start(env = {}) {
const client = new PlaywrightClient(env);
const playwright2 = await client._playwright;
playwright2.driverProcess = client._driverProcess;
return { playwright: playwright2, stop: () => client.stop() };
}
var childProcess4, import_path52, PlaywrightClient;
var init_outofprocess = __esm({
"packages/playwright-core/src/outofprocess.ts"() {
"use strict";
childProcess4 = __toESM(require("child_process"));
import_path52 = __toESM(require("path"));
init_pipeTransport();
init_nodePlatform();
init_manualPromise();
init_connection();
init_package();
PlaywrightClient = class {
constructor(env) {
this._closePromise = new ManualPromise();
this._driverProcess = childProcess4.fork(import_path52.default.join(packageRoot, "cli.js"), ["run-driver"], {
stdio: "pipe",
detached: true,
env: {
...process.env,
...env
}
});
this._driverProcess.unref();
this._driverProcess.stderr.on("data", (data) => process.stderr.write(data));
const connection = new Connection(nodePlatform(packageRoot));
const transport = new PipeTransport(this._driverProcess.stdin, this._driverProcess.stdout);
connection.onmessage = (message) => transport.send(JSON.stringify(message));
transport.onmessage = (message) => connection.dispatch(JSON.parse(message));
transport.onclose = () => this._closePromise.resolve();
this._playwright = connection.initializePlaywright();
}
async stop() {
this._driverProcess.stdin.destroy();
this._driverProcess.stdout.destroy();
this._driverProcess.stderr.destroy();
await this._closePromise;
}
};
}
});
// packages/playwright-core/src/tools/mcp/log.ts
function logUnhandledError(error) {
errorDebug(error);
}
var debug11, errorDebug, testDebug2;
var init_log = __esm({
"packages/playwright-core/src/tools/mcp/log.ts"() {
"use strict";
debug11 = require("./utilsBundle").debug;
errorDebug = debug11("pw:mcp:error");
testDebug2 = debug11("pw:mcp:test");
}
});
// packages/playwright-core/src/tools/mcp/watchdog.ts
function setupExitWatchdog() {
let isExiting = false;
const handleExit = async (signal) => {
if (isExiting)
return;
isExiting = true;
setTimeout(() => process.exit(0), 15e3);
testDebug2("gracefully closing " + gracefullyCloseSet.size);
await gracefullyCloseAll();
process.exit(0);
};
process.stdin.on("close", () => handleExit("close"));
process.on("SIGINT", () => handleExit("SIGINT"));
process.on("SIGTERM", () => handleExit("SIGTERM"));
}
var init_watchdog = __esm({
"packages/playwright-core/src/tools/mcp/watchdog.ts"() {
"use strict";
init_processLauncher();
init_log();
}
});
// packages/playwright-core/src/tools/utils/mcp/http.ts
async function startMcpHttpServer(config, serverBackendFactory, allowedHosts) {
const httpServer = createHttpServer();
await startHttpServer(httpServer, config);
return await installHttpTransport(httpServer, serverBackendFactory, allowedHosts);
}
function addressToString(address, options2) {
(0, import_assert48.default)(address, "Could not bind server socket");
if (typeof address === "string")
throw new Error("Unexpected address type: " + address);
let host = urlHostFromAddress(address);
if (options2.normalizeLoopback && (host === "0.0.0.0" || host === "[::]" || host === "[::1]" || host === "127.0.0.1"))
host = "localhost";
return `${options2.protocol}://${host}:${address.port}`;
}
async function installHttpTransport(httpServer, serverBackendFactory, allowedHosts) {
const url2 = addressToString(httpServer.address(), { protocol: "http", normalizeLoopback: true });
const host = new URL(url2).host;
allowedHosts = (allowedHosts || [host]).map((h) => h.toLowerCase());
const allowAnyHost = allowedHosts.includes("*");
const sseSessions = /* @__PURE__ */ new Map();
const streamableSessions = /* @__PURE__ */ new Map();
httpServer.on("request", async (req, res) => {
if (!allowAnyHost) {
const host2 = req.headers.host?.toLowerCase();
if (!host2) {
res.statusCode = 400;
return res.end("Missing host");
}
if (!allowedHosts.includes(host2)) {
res.statusCode = 403;
return res.end("Access is only allowed at " + allowedHosts.join(", "));
}
}
const url3 = new URL(`http://localhost${req.url}`);
if (url3.pathname === "/killkillkill") {
if (req.method !== "POST" || req.headers["x-pw-mcp-kill"] !== "1") {
res.statusCode = 405;
return res.end();
}
res.statusCode = 200;
res.end("Killing process");
process.emit("SIGINT");
return;
}
if (url3.pathname.startsWith("/sse"))
await handleSSE(serverBackendFactory, req, res, url3, sseSessions);
else
await handleStreamable(serverBackendFactory, req, res, streamableSessions);
});
return url2;
}
async function handleSSE(serverBackendFactory, req, res, url2, sessions) {
if (req.method === "POST") {
const sessionId = url2.searchParams.get("sessionId");
if (!sessionId) {
res.statusCode = 400;
return res.end("Missing sessionId");
}
const transport = sessions.get(sessionId);
if (!transport) {
res.statusCode = 404;
return res.end("Session not found");
}
return await transport.handlePostMessage(req, res);
} else if (req.method === "GET") {
const transport = new SSEServerTransport("/sse", res);
sessions.set(transport.sessionId, transport);
testDebug3(`create SSE session`);
await connect(serverBackendFactory, transport, false);
res.on("close", () => {
testDebug3(`delete SSE session`);
sessions.delete(transport.sessionId);
});
return;
}
res.statusCode = 405;
res.end("Method not allowed");
}
async function handleStreamable(serverBackendFactory, req, res, sessions) {
const sessionId = req.headers["mcp-session-id"];
if (sessionId) {
const transport = sessions.get(sessionId);
if (!transport) {
res.statusCode = 404;
res.end("Session not found");
return;
}
return await transport.handleRequest(req, res);
}
if (req.method === "POST") {
const transport = new StreamableHTTPServerTransport({
sessionIdGenerator: () => import_crypto25.default.randomUUID(),
onsessioninitialized: async (sessionId2) => {
testDebug3(`create http session`);
await connect(serverBackendFactory, transport, true);
sessions.set(sessionId2, transport);
}
});
transport.onclose = () => {
if (!transport.sessionId)
return;
sessions.delete(transport.sessionId);
testDebug3(`delete http session`);
};
await transport.handleRequest(req, res);
return;
}
res.statusCode = 400;
res.end("Invalid request");
}
var import_assert48, import_crypto25, debug12, SSEServerTransport, StreamableHTTPServerTransport, testDebug3;
var init_http = __esm({
"packages/playwright-core/src/tools/utils/mcp/http.ts"() {
"use strict";
import_assert48 = __toESM(require("assert"));
import_crypto25 = __toESM(require("crypto"));
init_httpServer();
init_network();
init_server2();
debug12 = require("./utilsBundle").debug;
({ SSEServerTransport } = require("./utilsBundle"));
({ StreamableHTTPServerTransport } = require("./utilsBundle"));
testDebug3 = debug12("pw:mcp:test");
}
});
// packages/playwright-core/src/tools/utils/mcp/tool.ts
function toMcpTool(tool) {
const readOnly = tool.type === "readOnly" || tool.type === "assertion";
return {
name: tool.name,
description: tool.description,
inputSchema: zod.toJSONSchema(tool.inputSchema),
annotations: {
title: tool.title,
readOnlyHint: readOnly,
destructiveHint: !readOnly,
openWorldHint: true
}
};
}
var zod;
var init_tool2 = __esm({
"packages/playwright-core/src/tools/utils/mcp/tool.ts"() {
"use strict";
zod = require("./utilsBundle").z;
}
});
// packages/playwright-core/src/tools/utils/mcp/server.ts
async function connect(factory, transport, runHeartbeat) {
const server = createServer(factory.name, factory.version, factory, runHeartbeat);
await server.connect(transport);
}
function createServer(name, version3, factory, runHeartbeat) {
const server = new Server({ name, version: version3 }, {
capabilities: {
tools: {}
}
});
server.setRequestHandler(ListToolsRequestSchema, async () => {
serverDebug("listTools");
return { tools: factory.toolSchemas.map((s) => toMcpTool(s)) };
});
let backendPromise;
const onClose = () => backendPromise?.then((b) => backendManager.disposeBackend(b)).catch(serverDebug);
addServerListener(server, "close", onClose);
server.setRequestHandler(CallToolRequestSchema, async (request2, extra) => {
serverDebug("callTool", request2);
try {
if (!backendPromise) {
backendPromise = initializeServer(server, factory, runHeartbeat).catch((e) => {
backendPromise = void 0;
throw e;
});
}
const backend = await backendPromise;
const toolResult = await backend.callTool(request2.params.name, request2.params.arguments || {}, extra.signal);
if (toolResult.isClose) {
await backendManager.disposeBackend(backend).catch(serverDebug);
backendPromise = void 0;
delete toolResult.isClose;
}
const mergedResult = mergeTextParts(toolResult);
serverDebugResponse("callResult", mergedResult);
return mergedResult;
} catch (error) {
return {
content: [{ type: "text", text: "### Error\n" + String(error) }],
isError: true
};
}
});
return server;
}
function addServerListener(server, event, listener) {
const oldListener = server[`on${event}`];
server[`on${event}`] = () => {
oldListener?.();
listener();
};
}
async function start2(serverBackendFactory, options2 = {}) {
if (options2.port === void 0) {
const transport = new StdioServerTransport();
process.stdin.on("end", () => void transport.close());
await connect(serverBackendFactory, transport, false);
return;
}
const url2 = await startMcpHttpServer(options2, serverBackendFactory, options2.allowedHosts);
const mcpConfig = { mcpServers: {} };
mcpConfig.mcpServers[serverBackendFactory.nameInConfig] = {
url: `${url2}/mcp`
};
const message = [
`Listening on ${url2}`,
"Put this in your client config:",
JSON.stringify(mcpConfig, void 0, 2),
"For legacy SSE transport support, you can use the /sse endpoint instead."
].join("\n");
console.error(message);
}
function firstRootPath(roots) {
return allRootPaths(roots)[0];
}
function allRootPaths(roots) {
const paths = [];
for (const root of roots) {
const url2 = new URL(root.uri);
let rootPath;
try {
rootPath = (0, import_url2.fileURLToPath)(url2);
} catch (e) {
if (e.code === "ERR_INVALID_FILE_URL_PATH" && process.platform === "win32")
rootPath = decodeURIComponent(url2.pathname);
}
if (!rootPath)
continue;
paths.push(rootPath);
}
if (paths.length === 0)
paths.push(process.cwd());
return paths;
}
function mergeTextParts(result2) {
const content = [];
const testParts = [];
for (const part of result2.content) {
if (part.type === "text") {
testParts.push(part.text);
continue;
}
if (testParts.length > 0) {
content.push({ type: "text", text: testParts.join("\n") });
testParts.length = 0;
}
content.push(part);
}
if (testParts.length > 0)
content.push({ type: "text", text: testParts.join("\n") });
return {
...result2,
content
};
}
var import_url2, CallToolRequestSchema, ListToolsRequestSchema, debug13, Server, StdioServerTransport, serverDebug, serverDebugResponse, BackendManager, backendManager, initializeServer, startHeartbeat;
var init_server2 = __esm({
"packages/playwright-core/src/tools/utils/mcp/server.ts"() {
"use strict";
import_url2 = require("url");
init_http();
init_tool2();
({ CallToolRequestSchema, ListToolsRequestSchema } = require("./utilsBundle"));
debug13 = require("./utilsBundle").debug;
({ Server } = require("./utilsBundle"));
({ StdioServerTransport } = require("./utilsBundle"));
serverDebug = debug13("pw:mcp:server");
serverDebugResponse = debug13("pw:mcp:server:response");
BackendManager = class {
constructor() {
this._backends = /* @__PURE__ */ new Map();
}
async createBackend(factory, clientInfo) {
const backend = await factory.create(clientInfo);
await backend.initialize?.(clientInfo);
this._backends.set(backend, factory);
return backend;
}
async disposeBackend(backend) {
const factory = this._backends.get(backend);
if (!factory)
return;
await backend.dispose?.();
await factory.disposed(backend).catch(serverDebug);
this._backends.delete(backend);
}
};
backendManager = new BackendManager();
initializeServer = async (server, factory, runHeartbeat) => {
const capabilities = server.getClientCapabilities();
let clientRoots = [];
if (capabilities?.roots) {
const { roots } = await server.listRoots().catch((e) => {
serverDebug(e);
return { roots: [] };
});
clientRoots = roots;
}
const clientInfo = {
cwd: firstRootPath(clientRoots),
clientName: server.getClientVersion()?.name ?? "Playwright MCP"
};
const backend = await backendManager.createBackend(factory, clientInfo);
if (runHeartbeat)
startHeartbeat(server);
return backend;
};
startHeartbeat = (server) => {
const beat = () => {
Promise.race([
server.ping(),
new Promise((_, reject) => setTimeout(() => reject(new Error("ping timeout")), 5e3))
]).then(() => {
setTimeout(beat, 3e3);
}).catch(() => {
void server.close();
});
};
beat();
};
}
});
// packages/playwright-core/src/tools/mcp/configIni.ts
function configFromIniFile(filePath) {
const content = import_fs54.default.readFileSync(filePath, "utf8");
const parsed = ini.parse(content);
return iniEntriesToConfig(parsed);
}
function iniEntriesToConfig(entries) {
const config = {};
for (const [targetPath, rawValue] of Object.entries(entries)) {
const type3 = longhandTypes[targetPath];
const value2 = type3 ? coerceToType(rawValue, type3) : coerceIniValue(rawValue);
setNestedValue(config, targetPath, value2);
}
return config;
}
function coerceToType(value2, type3) {
switch (type3) {
case "string":
return String(value2);
case "number":
return Number(value2);
case "boolean":
if (typeof value2 === "boolean")
return value2;
return value2 === "true" || value2 === "1";
case "string[]":
if (Array.isArray(value2))
return value2.map(String);
return [String(value2)];
case "size": {
if (typeof value2 === "string" && value2.includes("x")) {
const [w, h] = value2.split("x").map(Number);
if (!isNaN(w) && !isNaN(h) && w > 0 && h > 0)
return { width: w, height: h };
}
return void 0;
}
}
}
function coerceIniValue(value2) {
if (typeof value2 !== "string")
return value2;
const trimmed = value2.trim();
if (trimmed === "")
return trimmed;
const num = Number(trimmed);
if (!isNaN(num))
return num;
return value2;
}
function setNestedValue(obj, dotPath, value2) {
const parts = dotPath.split(".");
let current = obj;
for (let i = 0; i < parts.length - 1; i++) {
const part = parts[i];
if (!(part in current) || typeof current[part] !== "object" || current[part] === null)
current[part] = {};
current = current[part];
}
current[parts[parts.length - 1]] = value2;
}
var import_fs54, ini, longhandTypes;
var init_configIni = __esm({
"packages/playwright-core/src/tools/mcp/configIni.ts"() {
"use strict";
import_fs54 = __toESM(require("fs"));
ini = require("./utilsBundle").ini;
longhandTypes = {
// browser direct
"browser.browserName": "string",
"browser.isolated": "boolean",
"browser.userDataDir": "string",
"browser.cdpEndpoint": "string",
"browser.cdpTimeout": "number",
"browser.remoteEndpoint": "string",
"browser.initPage": "string[]",
"browser.initScript": "string[]",
// browser.launchOptions
"browser.launchOptions.channel": "string",
"browser.launchOptions.headless": "boolean",
"browser.launchOptions.executablePath": "string",
"browser.launchOptions.chromiumSandbox": "boolean",
"browser.launchOptions.args": "string[]",
"browser.launchOptions.downloadsPath": "string",
"browser.launchOptions.handleSIGHUP": "boolean",
"browser.launchOptions.handleSIGINT": "boolean",
"browser.launchOptions.handleSIGTERM": "boolean",
"browser.launchOptions.slowMo": "number",
"browser.launchOptions.timeout": "number",
"browser.launchOptions.tracesDir": "string",
"browser.launchOptions.proxy.server": "string",
"browser.launchOptions.proxy.bypass": "string",
"browser.launchOptions.proxy.username": "string",
"browser.launchOptions.proxy.password": "string",
// browser.contextOptions
"browser.contextOptions.acceptDownloads": "boolean",
"browser.contextOptions.baseURL": "string",
"browser.contextOptions.bypassCSP": "boolean",
"browser.contextOptions.colorScheme": "string",
"browser.contextOptions.contrast": "string",
"browser.contextOptions.deviceScaleFactor": "number",
"browser.contextOptions.forcedColors": "string",
"browser.contextOptions.hasTouch": "boolean",
"browser.contextOptions.ignoreHTTPSErrors": "boolean",
"browser.contextOptions.isMobile": "boolean",
"browser.contextOptions.javaScriptEnabled": "boolean",
"browser.contextOptions.locale": "string",
"browser.contextOptions.offline": "boolean",
"browser.contextOptions.permissions": "string[]",
"browser.contextOptions.reducedMotion": "string",
"browser.contextOptions.screen": "size",
"browser.contextOptions.serviceWorkers": "string",
"browser.contextOptions.storageState": "string",
"browser.contextOptions.strictSelectors": "boolean",
"browser.contextOptions.timezoneId": "string",
"browser.contextOptions.userAgent": "string",
"browser.contextOptions.viewport": "size",
// top-level
"extension": "boolean",
"capabilities": "string[]",
"saveSession": "boolean",
"saveTrace": "boolean",
"saveVideo": "size",
"sharedBrowserContext": "boolean",
"outputDir": "string",
"imageResponses": "string",
"allowUnrestrictedFileAccess": "boolean",
"codegen": "string",
"testIdAttribute": "string",
// server
"server.port": "number",
"server.host": "string",
"server.allowedHosts": "string[]",
// console
"console.level": "string",
// network
"network.allowedOrigins": "string[]",
"network.blockedOrigins": "string[]",
// timeouts
"timeouts.action": "number",
"timeouts.navigation": "number",
// snapshot
"snapshot.mode": "string"
};
}
});
// packages/playwright-core/src/tools/mcp/config.ts
async function fileExistsAsync(resolved) {
try {
return (await import_fs55.default.promises.stat(resolved)).isFile();
} catch {
return false;
}
}
async function resolveConfig(config) {
const merged = mergeConfig(defaultConfig, config);
const browser = await validateBrowserConfig(merged.browser);
return { ...merged, browser };
}
async function resolveCLIConfigForMCP(cliOptions, env) {
const envOverrides = configFromEnv(env);
const cliOverrides = configFromCLIOptions(cliOptions);
const configFile = cliOverrides.configFile ?? envOverrides.configFile;
const configInFile = await loadConfig(configFile);
const configDir = configFile ? import_path53.default.dirname(import_path53.default.resolve(configFile)) : process.cwd();
let result2 = defaultConfig;
result2 = mergeConfig(result2, resolveConfigPaths(configInFile, configDir));
result2 = mergeConfig(result2, resolveConfigPaths(envOverrides, process.cwd()));
result2 = mergeConfig(result2, resolveConfigPaths(cliOverrides, process.cwd()));
const browser = await validateBrowserConfig(result2.browser);
if (browser.launchOptions.headless === void 0)
browser.launchOptions.headless = import_os24.default.platform() === "linux" && !process.env.DISPLAY;
validateOutputDir(result2.outputDir);
return { ...result2, browser, configFile };
}
function validateOutputDir(outputDir2) {
if (!outputDir2)
return;
if (isSystemDirectory(outputDir2))
throw new Error(`--output-dir cannot point to a system directory: ${import_path53.default.resolve(outputDir2)}.`);
}
async function resolveCLIConfigForCLI(daemonProfilesDir2, sessionName, options2, env) {
const config = options2.config ? import_path53.default.resolve(options2.config) : void 0;
try {
const defaultConfigFile2 = import_path53.default.resolve(".playwright", "cli.config.json");
if (!config && import_fs55.default.existsSync(defaultConfigFile2))
options2.config = defaultConfigFile2;
} catch {
}
const daemonOverrides = configFromCLIOptions({
endpoint: options2.endpoint,
cdpEndpoint: options2.cdp,
config: options2.config,
browser: options2.browser,
headless: options2.headed ? false : void 0,
extension: options2.extension,
userDataDir: options2.profile,
snapshotMode: "full"
});
const envOverrides = configFromEnv(env);
const configFile = daemonOverrides.configFile ?? envOverrides.configFile;
const configInFile = await loadConfig(configFile);
const configDir = configFile ? import_path53.default.dirname(import_path53.default.resolve(configFile)) : process.cwd();
const globalConfigPath = import_path53.default.join((env ?? process.env)["PWTEST_CLI_GLOBAL_CONFIG"] ?? import_os24.default.homedir(), ".playwright", "cli.config.json");
const globalConfigExists = import_fs55.default.existsSync(globalConfigPath);
const globalConfigInFile = await loadConfig(globalConfigExists ? globalConfigPath : void 0);
const globalConfigDir = globalConfigExists ? import_path53.default.dirname(globalConfigPath) : process.cwd();
let result2 = defaultConfig;
result2 = mergeConfig(result2, resolveConfigPaths(globalConfigInFile, globalConfigDir));
result2 = mergeConfig(result2, resolveConfigPaths(configInFile, configDir));
result2 = mergeConfig(result2, resolveConfigPaths(envOverrides, process.cwd()));
result2 = mergeConfig(result2, resolveConfigPaths(daemonOverrides, process.cwd()));
if (result2.browser.isolated === void 0)
result2.browser.isolated = !options2.profile && !options2.persistent && !result2.browser.userDataDir && !result2.browser.remoteEndpoint && !result2.browser.cdpEndpoint && !result2.extension;
if (result2.browser.launchOptions.headless === void 0)
result2.browser.launchOptions.headless = true;
const browser = await validateBrowserConfig(result2.browser);
validateOutputDir(result2.outputDir);
if (!result2.extension && !browser.isolated && !browser.userDataDir && !browser.remoteEndpoint && !browser.cdpEndpoint) {
const browserToken = browser.launchOptions?.channel ?? browser?.browserName;
const userDataDir = import_path53.default.resolve(daemonProfilesDir2, `ud-${sessionName}-${browserToken}`);
browser.userDataDir = userDataDir;
}
return { ...result2, browser, configFile, skillMode: true };
}
function resolveExtensionOptions(cliOptions) {
const browser = cliOptions.browser ?? envToString(process.env.PLAYWRIGHT_MCP_BROWSER);
const { channel } = resolveBrowserParam(browser);
const executablePath = cliOptions.executablePath ?? envToString(process.env.PLAYWRIGHT_MCP_EXECUTABLE_PATH);
return { channel: channel ?? "chrome", executablePath };
}
async function validateBrowserConfig(browser) {
let browserName = browser.browserName;
if (!browserName) {
browserName = "chromium";
if (browser.launchOptions.channel === void 0)
browser.launchOptions.channel = "chrome";
}
if (browser.browserName === "chromium" && browser.launchOptions.chromiumSandbox === void 0) {
if (process.platform === "linux")
browser.launchOptions.chromiumSandbox = browser.launchOptions.channel !== "chromium" && browser.launchOptions.channel !== "chrome-for-testing";
else
browser.launchOptions.chromiumSandbox = true;
}
if (browser.isolated && browser.userDataDir)
throw new Error("Browser userDataDir is not supported in isolated mode.");
if (browser.initScript) {
for (const script of browser.initScript) {
if (!await fileExistsAsync(script))
throw new Error(`Init script file does not exist: ${script}`);
}
}
if (browser.initPage) {
for (const page of browser.initPage) {
if (!await fileExistsAsync(page))
throw new Error(`Init page file does not exist: ${page}`);
}
}
if (browser.contextOptions.viewport === void 0) {
if (browser.launchOptions.headless)
browser.contextOptions.viewport = { width: 1280, height: 720 };
else
browser.contextOptions.viewport = null;
}
if (browserName === "chromium") {
browser.launchOptions.args = browser.launchOptions.args ?? [];
if (!browser.launchOptions.args.some((a) => a.includes("--disable-blink-features")))
browser.launchOptions.args.push(`--disable-blink-features=AutomationControlled`);
}
return { ...browser, browserName };
}
function resolveBrowserParam(browserOption) {
switch (browserOption) {
case "chrome":
case "chrome-beta":
case "chrome-canary":
case "chrome-dev":
case "msedge":
case "msedge-beta":
case "msedge-canary":
case "msedge-dev":
return { browserName: "chromium", channel: browserOption };
case "chromium":
return { browserName: "chromium", channel: "chrome-for-testing" };
case "firefox":
return { browserName: "firefox" };
case "webkit":
return { browserName: "webkit" };
default:
return {};
}
}
function configFromCLIOptions(cliOptions) {
const { browserName, channel } = resolveBrowserParam(cliOptions.browser);
const launchOptions = {
channel,
executablePath: cliOptions.executablePath,
headless: cliOptions.headless
};
if (cliOptions.sandbox !== void 0)
launchOptions.chromiumSandbox = cliOptions.sandbox;
if (cliOptions.device && cliOptions.cdpEndpoint)
throw new Error("Device emulation is not supported with cdpEndpoint.");
const contextOptions = cliOptions.device ? playwright.devices[cliOptions.device] : {};
if (cliOptions.proxyServer) {
const proxy = { server: cliOptions.proxyServer };
if (cliOptions.proxyBypass)
proxy.bypass = cliOptions.proxyBypass;
launchOptions.proxy = proxy;
contextOptions.proxy = proxy;
}
if (cliOptions.storageState)
contextOptions.storageState = cliOptions.storageState;
if (cliOptions.userAgent)
contextOptions.userAgent = cliOptions.userAgent;
if (cliOptions.viewportSize)
contextOptions.viewport = cliOptions.viewportSize;
if (cliOptions.ignoreHttpsErrors)
contextOptions.ignoreHTTPSErrors = true;
if (cliOptions.blockServiceWorkers)
contextOptions.serviceWorkers = "block";
if (cliOptions.grantPermissions)
contextOptions.permissions = cliOptions.grantPermissions;
const config = {
browser: {
browserName,
isolated: cliOptions.isolated,
userDataDir: cliOptions.userDataDir,
launchOptions,
contextOptions,
cdpEndpoint: cliOptions.cdpEndpoint,
cdpHeaders: cliOptions.cdpHeader,
cdpTimeout: cliOptions.cdpTimeout,
initPage: cliOptions.initPage,
initScript: cliOptions.initScript,
remoteEndpoint: cliOptions.endpoint
},
extension: cliOptions.extension,
server: {
port: cliOptions.port,
host: cliOptions.host,
allowedHosts: cliOptions.allowedHosts
},
capabilities: cliOptions.caps,
console: {
level: cliOptions.consoleLevel
},
network: {
allowedOrigins: cliOptions.allowedOrigins,
blockedOrigins: cliOptions.blockedOrigins
},
allowUnrestrictedFileAccess: cliOptions.allowUnrestrictedFileAccess,
codegen: cliOptions.codegen,
saveSession: cliOptions.saveSession,
secrets: cliOptions.secrets,
sharedBrowserContext: cliOptions.sharedBrowserContext,
snapshot: cliOptions.snapshotMode ? { mode: cliOptions.snapshotMode } : void 0,
outputDir: cliOptions.outputDir,
imageResponses: cliOptions.imageResponses,
testIdAttribute: cliOptions.testIdAttribute,
timeouts: {
action: cliOptions.timeoutAction,
navigation: cliOptions.timeoutNavigation
}
};
return { ...config, configFile: cliOptions.config };
}
function configFromEnv(env) {
const e = env ?? process.env;
const options2 = {};
options2.allowedHosts = commaSeparatedList(e.PLAYWRIGHT_MCP_ALLOWED_HOSTS);
options2.allowedOrigins = semicolonSeparatedList(e.PLAYWRIGHT_MCP_ALLOWED_ORIGINS);
options2.allowUnrestrictedFileAccess = envToBoolean(e.PLAYWRIGHT_MCP_ALLOW_UNRESTRICTED_FILE_ACCESS);
options2.blockedOrigins = semicolonSeparatedList(e.PLAYWRIGHT_MCP_BLOCKED_ORIGINS);
options2.blockServiceWorkers = envToBoolean(e.PLAYWRIGHT_MCP_BLOCK_SERVICE_WORKERS);
options2.browser = envToString(e.PLAYWRIGHT_MCP_BROWSER);
options2.caps = commaSeparatedList(e.PLAYWRIGHT_MCP_CAPS);
options2.cdpEndpoint = envToString(e.PLAYWRIGHT_MCP_CDP_ENDPOINT);
options2.cdpHeader = headerParser(envToString(e.PLAYWRIGHT_MCP_CDP_HEADERS));
options2.cdpTimeout = numberParser(e.PLAYWRIGHT_MCP_CDP_TIMEOUT);
options2.config = envToString(e.PLAYWRIGHT_MCP_CONFIG);
if (e.PLAYWRIGHT_MCP_CONSOLE_LEVEL)
options2.consoleLevel = enumParser("--console-level", ["error", "warning", "info", "debug"], e.PLAYWRIGHT_MCP_CONSOLE_LEVEL);
options2.device = envToString(e.PLAYWRIGHT_MCP_DEVICE);
options2.executablePath = envToString(e.PLAYWRIGHT_MCP_EXECUTABLE_PATH);
options2.extension = envToBoolean(e.PLAYWRIGHT_MCP_EXTENSION);
options2.grantPermissions = commaSeparatedList(e.PLAYWRIGHT_MCP_GRANT_PERMISSIONS);
options2.headless = envToBoolean(e.PLAYWRIGHT_MCP_HEADLESS);
options2.host = envToString(e.PLAYWRIGHT_MCP_HOST);
options2.ignoreHttpsErrors = envToBoolean(e.PLAYWRIGHT_MCP_IGNORE_HTTPS_ERRORS);
const initPage = envToString(e.PLAYWRIGHT_MCP_INIT_PAGE);
if (initPage)
options2.initPage = [initPage];
const initScript = envToString(e.PLAYWRIGHT_MCP_INIT_SCRIPT);
if (initScript)
options2.initScript = [initScript];
options2.isolated = envToBoolean(e.PLAYWRIGHT_MCP_ISOLATED);
if (e.PLAYWRIGHT_MCP_IMAGE_RESPONSES)
options2.imageResponses = enumParser("--image-responses", ["allow", "omit"], e.PLAYWRIGHT_MCP_IMAGE_RESPONSES);
options2.sandbox = envToBoolean(e.PLAYWRIGHT_MCP_SANDBOX);
options2.outputDir = envToString(e.PLAYWRIGHT_MCP_OUTPUT_DIR);
options2.port = numberParser(e.PLAYWRIGHT_MCP_PORT);
options2.proxyBypass = envToString(e.PLAYWRIGHT_MCP_PROXY_BYPASS);
options2.proxyServer = envToString(e.PLAYWRIGHT_MCP_PROXY_SERVER);
options2.secrets = dotenvFileLoader(e.PLAYWRIGHT_MCP_SECRETS_FILE);
options2.storageState = envToString(e.PLAYWRIGHT_MCP_STORAGE_STATE);
options2.testIdAttribute = envToString(e.PLAYWRIGHT_MCP_TEST_ID_ATTRIBUTE);
options2.timeoutAction = numberParser(e.PLAYWRIGHT_MCP_TIMEOUT_ACTION);
options2.timeoutNavigation = numberParser(e.PLAYWRIGHT_MCP_TIMEOUT_NAVIGATION);
options2.userAgent = envToString(e.PLAYWRIGHT_MCP_USER_AGENT);
options2.userDataDir = envToString(e.PLAYWRIGHT_MCP_USER_DATA_DIR);
options2.viewportSize = resolutionParser("--viewport-size", e.PLAYWRIGHT_MCP_VIEWPORT_SIZE);
return configFromCLIOptions(options2);
}
async function loadConfig(configFile) {
if (!configFile)
return {};
if (configFile.endsWith(".ini"))
return configFromIniFile(configFile);
try {
const data = await import_fs55.default.promises.readFile(configFile, "utf8");
return JSON.parse(data.charCodeAt(0) === 65279 ? data.slice(1) : data);
} catch {
return configFromIniFile(configFile);
}
}
function resolveConfigPaths(config, baseDir) {
if (config.browser?.initPage)
config.browser.initPage = config.browser.initPage.map((p) => import_path53.default.resolve(baseDir, p));
if (config.browser?.initScript)
config.browser.initScript = config.browser.initScript.map((p) => import_path53.default.resolve(baseDir, p));
return config;
}
function pickDefined(obj) {
return Object.fromEntries(
Object.entries(obj ?? {}).filter(([_, v]) => v !== void 0)
);
}
function mergeConfig(base, overrides) {
const browser = {
...pickDefined(base.browser),
...pickDefined(overrides.browser),
browserName: overrides.browser?.browserName ?? base.browser?.browserName,
isolated: overrides.browser?.isolated ?? base.browser?.isolated,
launchOptions: {
...pickDefined(base.browser?.launchOptions),
...pickDefined(overrides.browser?.launchOptions)
},
contextOptions: {
...pickDefined(base.browser?.contextOptions),
...pickDefined(overrides.browser?.contextOptions)
}
};
if (browser.browserName !== "chromium" && browser.launchOptions)
delete browser.launchOptions.channel;
return {
...pickDefined(base),
...pickDefined(overrides),
browser,
console: {
...pickDefined(base.console),
...pickDefined(overrides.console)
},
network: {
...pickDefined(base.network),
...pickDefined(overrides.network)
},
server: {
...pickDefined(base.server),
...pickDefined(overrides.server)
},
snapshot: {
...pickDefined(base.snapshot),
...pickDefined(overrides.snapshot)
},
timeouts: {
...pickDefined(base.timeouts),
...pickDefined(overrides.timeouts)
}
};
}
function semicolonSeparatedList(value2) {
if (!value2)
return void 0;
return value2.split(";").map((v) => v.trim());
}
function commaSeparatedList(value2) {
if (!value2)
return void 0;
return value2.split(",").map((v) => v.trim());
}
function dotenvFileLoader(value2) {
if (!value2)
return void 0;
return dotenv2.parse(import_fs55.default.readFileSync(value2, "utf8"));
}
function numberParser(value2) {
if (!value2)
return void 0;
return +value2;
}
function resolutionParser(name, value2) {
if (!value2)
return void 0;
if (value2.includes("x")) {
const [width, height] = value2.split("x").map((v) => +v);
if (isNaN(width) || isNaN(height) || width <= 0 || height <= 0)
throw new Error(`Invalid resolution format: use ${name}="800x600"`);
return { width, height };
}
if (value2.includes(",")) {
const [width, height] = value2.split(",").map((v) => +v);
if (isNaN(width) || isNaN(height) || width <= 0 || height <= 0)
throw new Error(`Invalid resolution format: use ${name}="800x600"`);
return { width, height };
}
throw new Error(`Invalid resolution format: use ${name}="800x600"`);
}
function headerParser(arg, previous) {
if (!arg)
return previous;
const result2 = { ...previous ?? {} };
const colonIndex = arg.indexOf(":");
const name = colonIndex === -1 ? arg.trim() : arg.substring(0, colonIndex).trim();
const value2 = colonIndex === -1 ? "" : arg.substring(colonIndex + 1).trim();
result2[name] = value2;
return result2;
}
function enumParser(name, options2, value2) {
if (!options2.includes(value2))
throw new Error(`Invalid ${name}: ${value2}. Valid values are: ${options2.join(", ")}`);
return value2;
}
function envToBoolean(value2) {
if (value2 === "true" || value2 === "1")
return true;
if (value2 === "false" || value2 === "0")
return false;
return void 0;
}
function envToString(value2) {
return value2 ? value2.trim() : void 0;
}
var import_fs55, import_path53, import_os24, dotenv2, defaultConfig;
var init_config2 = __esm({
"packages/playwright-core/src/tools/mcp/config.ts"() {
"use strict";
import_fs55 = __toESM(require("fs"));
import_path53 = __toESM(require("path"));
import_os24 = __toESM(require("os"));
init_fileUtils();
init_inprocess();
init_configIni();
dotenv2 = require("./utilsBundle").dotenv;
defaultConfig = {
browser: {
launchOptions: {},
contextOptions: {}
},
timeouts: {
action: 5e3,
navigation: 6e4,
expect: 5e3
}
};
}
});
// packages/playwright-core/src/tools/mcp/cdpRelayV1.ts
var ExtensionProtocolV1;
var init_cdpRelayV1 = __esm({
"packages/playwright-core/src/tools/mcp/cdpRelayV1.ts"() {
"use strict";
ExtensionProtocolV1 = class {
constructor(sendCommand) {
this._sendToCDPClient = null;
this._nextSessionId = 1;
this._sendCommand = sendCommand;
}
connectOverCDP(sendToCDPClient) {
this._sendToCDPClient = sendToCDPClient;
}
handleExtensionEvent(method, params2) {
switch (method) {
case "forwardCDPEvent": {
const p = params2;
const sessionId = p.sessionId || this._connectedTabInfo?.sessionId;
this._sendToCDPClient?.({
sessionId,
method: p.method,
params: p.params
});
break;
}
}
}
async handleCDPCommand(method, params2, sessionId) {
switch (method) {
case "Target.setAutoAttach": {
if (sessionId)
return void 0;
const { targetInfo } = await this._sendCommand("attachToTab", {});
this._connectedTabInfo = {
targetInfo,
sessionId: `pw-tab-${this._nextSessionId++}`
};
this._sendToCDPClient?.({
method: "Target.attachedToTarget",
params: {
sessionId: this._connectedTabInfo.sessionId,
targetInfo: {
...this._connectedTabInfo.targetInfo,
attached: true
},
waitingForDebugger: false
}
});
return { result: {} };
}
case "Target.getTargetInfo": {
return { result: this._connectedTabInfo?.targetInfo };
}
case "Target.createTarget": {
throw new Error("Tab creation is not supported yet.");
}
}
return void 0;
}
async forwardToExtension(method, params2, sessionId) {
if (this._connectedTabInfo?.sessionId === sessionId)
sessionId = void 0;
return await this._sendCommand("forwardCDPCommand", { sessionId, method, params: params2 });
}
ready() {
return Promise.resolve();
}
onExtensionDisconnect(_reason) {
}
};
}
});
// packages/playwright-core/src/tools/mcp/browserModel.ts
var BrowserModel;
var init_browserModel = __esm({
"packages/playwright-core/src/tools/mcp/browserModel.ts"() {
"use strict";
init_log();
BrowserModel = class {
constructor(sendToExtension) {
// Set only while a Playwright CDP connection is attached (see
// `connectOverCDP`). Before that, any attempt to emit to Playwright is a
// no-op — the model is observation-only during the extension handshake.
this._sendToCDPClient = null;
// Tabs observed via chrome.tabs.onCreated, whether or not the debugger is attached yet.
this._knownTabs = /* @__PURE__ */ new Map();
// Subset of _knownTabs we've attached the debugger to and assigned a sessionId.
this._tabSessions = /* @__PURE__ */ new Map();
this._autoAttach = false;
this._nextSessionId = 1;
this._sendToExtension = sendToExtension;
}
// Wires the model's CDP output sink. Called by the handler once the
// extension handshake is done and a Playwright CDP client is ready to
// receive events. Before this call the model is observation-only —
// `Target.attachedToTarget` etc. would be answered into a black hole.
connectOverCDP(sendToCDPClient) {
this._sendToCDPClient = sendToCDPClient;
}
_emit(message) {
this._sendToCDPClient?.(message);
}
// ─── Extension → model inputs ─────────────────────────────────────────
onTabCreated(tab2) {
if (tab2.id === void 0)
return;
this._knownTabs.set(tab2.id, tab2);
if (this._autoAttach)
void this._attachTab(tab2.id).catch(logUnhandledError);
}
onTabRemoved(tabId) {
this._knownTabs.delete(tabId);
this._detachTab(tabId);
}
onDebuggerEvent(source8, method, params2) {
if (source8.tabId === void 0)
return;
const tabSession = this._tabSessions.get(source8.tabId);
if (!tabSession)
return;
const childSessionId = params2?.sessionId;
if (method === "Target.attachedToTarget" && childSessionId)
tabSession.childSessions.add(childSessionId);
else if (method === "Target.detachedFromTarget" && childSessionId)
tabSession.childSessions.delete(childSessionId);
const sessionId = source8.sessionId || tabSession.sessionId;
this._emit({ sessionId, method, params: params2 });
}
onDebuggerDetach(source8) {
if (source8.tabId !== void 0)
this._detachTab(source8.tabId);
}
// ─── Playwright → model commands ──────────────────────────────────────
// Turn auto-attach on and attach to every tab we already know about.
// Called in response to Playwright's `Target.setAutoAttach` on the root session.
async enableAutoAttach() {
this._autoAttach = true;
const tabIds = [...this._knownTabs.keys()];
await Promise.all(tabIds.map((tabId) => this._attachTab(tabId).catch(logUnhandledError)));
}
async createTarget(url2) {
const tab2 = await this._sendToExtension("chrome.tabs.create", [{ url: url2 }]);
if (tab2?.id === void 0)
throw new Error("Failed to create tab");
this._knownTabs.set(tab2.id, tab2);
const tabSession = await this._attachTab(tab2.id);
return { targetId: tabSession.targetInfo?.targetId };
}
async closeTarget(targetId) {
const tabSession = targetId ? this._findTabSession((s) => s.targetInfo?.targetId === targetId) : void 0;
if (!tabSession)
return { success: false };
await this._sendToExtension("chrome.tabs.remove", [tabSession.tabId]);
return { success: true };
}
getTargetInfo(sessionId) {
if (!sessionId)
return void 0;
return this._findTabSession((s) => s.sessionId === sessionId)?.targetInfo;
}
// Forward a CDP command from Playwright to the tab its sessionId resolves to.
async sendCommand(sessionId, method, params2) {
let tabSession = this._findTabSession((s) => s.sessionId === sessionId);
let cdpSessionId;
if (!tabSession) {
tabSession = this._findTabSession((s) => s.childSessions.has(sessionId));
cdpSessionId = sessionId;
}
if (!tabSession)
throw new Error(`No tab found for sessionId: ${sessionId}`);
return await this._sendToExtension("chrome.debugger.sendCommand", [
{ tabId: tabSession.tabId, sessionId: cdpSessionId },
method,
params2
]);
}
// ─── Internals ────────────────────────────────────────────────────────
async _attachTab(tabId) {
const existing = this._tabSessions.get(tabId);
if (existing)
return existing;
await this._sendToExtension("chrome.debugger.attach", [{ tabId }, "1.3"]);
const result2 = await this._sendToExtension("chrome.debugger.sendCommand", [
{ tabId },
"Target.getTargetInfo"
]);
const targetInfo = result2?.targetInfo;
const sessionId = `pw-tab-${this._nextSessionId++}`;
const tabSession = { tabId, sessionId, targetInfo, childSessions: /* @__PURE__ */ new Set() };
this._tabSessions.set(tabId, tabSession);
this._emit({
method: "Target.attachedToTarget",
params: {
sessionId,
targetInfo: { ...targetInfo, attached: true },
waitingForDebugger: false
}
});
return tabSession;
}
_detachTab(tabId) {
const tabSession = this._tabSessions.get(tabId);
if (!tabSession)
return;
this._tabSessions.delete(tabId);
this._emit({
method: "Target.detachedFromTarget",
params: {
sessionId: tabSession.sessionId,
targetId: tabSession.targetInfo?.targetId
}
});
}
_findTabSession(predicate) {
for (const session2 of this._tabSessions.values()) {
if (predicate(session2))
return session2;
}
return void 0;
}
};
}
});
// packages/playwright-core/src/tools/mcp/cdpRelayV2.ts
var ExtensionProtocolV2;
var init_cdpRelayV2 = __esm({
"packages/playwright-core/src/tools/mcp/cdpRelayV2.ts"() {
"use strict";
init_manualPromise();
init_log();
init_browserModel();
ExtensionProtocolV2 = class {
constructor(sendCommand) {
// Resolved by `extension.initialized`. Purely a handshake signal for the
// relay — the model itself is oblivious to this phase.
this._ready = new ManualPromise();
this._model = new BrowserModel(sendCommand);
void this._ready.catch(logUnhandledError);
}
ready() {
return this._ready;
}
connectOverCDP(sendToCDPClient) {
this._model.connectOverCDP(sendToCDPClient);
}
onExtensionDisconnect(reason) {
if (!this._ready.isDone())
this._ready.reject(new Error(`Extension disconnected before initialization: ${reason}`));
}
handleExtensionEvent(method, params2) {
switch (method) {
case "chrome.debugger.onEvent": {
const [source8, cdpMethod, cdpParams] = params2;
this._model.onDebuggerEvent(source8, cdpMethod, cdpParams);
break;
}
case "chrome.debugger.onDetach": {
const [source8] = params2;
this._model.onDebuggerDetach(source8);
break;
}
case "chrome.tabs.onCreated": {
const [tab2] = params2;
this._model.onTabCreated(tab2);
break;
}
case "chrome.tabs.onRemoved": {
const [tabId] = params2;
this._model.onTabRemoved(tabId);
break;
}
case "extension.initialized": {
this._ready.resolve();
break;
}
}
}
async handleCDPCommand(method, params2, sessionId) {
switch (method) {
case "Target.setAutoAttach": {
if (sessionId)
return void 0;
await this._model.enableAutoAttach();
return { result: {} };
}
case "Target.createTarget":
return { result: await this._model.createTarget(params2?.url) };
case "Target.closeTarget":
return { result: await this._model.closeTarget(params2?.targetId) };
case "Target.getTargetInfo":
return { result: this._model.getTargetInfo(sessionId) };
}
return void 0;
}
async forwardToExtension(method, params2, sessionId) {
if (!sessionId)
throw new Error(`Unsupported command without sessionId: ${method}`);
return await this._model.sendCommand(sessionId, method, params2);
}
};
}
});
// packages/playwright-core/src/tools/mcp/protocol.ts
var DEFAULT_VERSION;
var init_protocol = __esm({
"packages/playwright-core/src/tools/mcp/protocol.ts"() {
"use strict";
DEFAULT_VERSION = 2;
}
});
// packages/playwright-core/src/tools/mcp/cdpRelay.ts
var import_child_process6, import_os25, debug14, ws2, wsServer3, debugLogger2, CDPRelayServer, ExtensionConnection;
var init_cdpRelay = __esm({
"packages/playwright-core/src/tools/mcp/cdpRelay.ts"() {
"use strict";
import_child_process6 = require("child_process");
import_os25 = __toESM(require("os"));
init_manualPromise();
init_registry();
init_extension();
init_http();
init_log();
init_cdpRelayV1();
init_cdpRelayV2();
init_protocol();
debug14 = require("./utilsBundle").debug;
ws2 = require("./utilsBundle").ws;
({ wsServer: wsServer3 } = require("./utilsBundle"));
debugLogger2 = debug14("pw:mcp:relay");
CDPRelayServer = class {
constructor(server, browserChannel, executablePath) {
this._cdpConnection = null;
this._extensionConnection = null;
this._extensionConnectionPromise = new ManualPromise();
this._wsHost = addressToString(server.address(), { protocol: "ws" });
this._browserChannel = browserChannel;
this._executablePath = executablePath;
this._protocolVersion = parseInt(process.env.PLAYWRIGHT_EXTENSION_PROTOCOL ?? DEFAULT_VERSION.toString(), 10);
const sendCommand = (method, params2) => {
if (!this._extensionConnection)
throw new Error("Extension not connected");
return this._extensionConnection.send(method, params2);
};
if (this._protocolVersion >= 2)
this._handler = new ExtensionProtocolV2(sendCommand);
else
this._handler = new ExtensionProtocolV1(sendCommand);
const uuid = crypto.randomUUID();
this._cdpPath = `/cdp/${uuid}`;
this._extensionPath = `/extension/${uuid}`;
void this._extensionConnectionPromise.catch(logUnhandledError);
this._wss = new wsServer3({ server });
this._wss.on("connection", this._onConnection.bind(this));
}
cdpEndpoint() {
return `${this._wsHost}${this._cdpPath}`;
}
extensionEndpoint() {
return `${this._wsHost}${this._extensionPath}`;
}
async establishExtensionConnection(clientName) {
debugLogger2("Establishing extension connection");
this._openConnectPageInBrowser(clientName);
debugLogger2("Waiting for incoming extension connection");
await this._extensionConnectionPromise;
await this._handler.ready();
debugLogger2("Extension connection established");
}
_openConnectPageInBrowser(clientName) {
const mcpRelayEndpoint = `${this._wsHost}${this._extensionPath}`;
const url2 = new URL(`chrome-extension://${playwrightExtensionId}/connect.html`);
url2.searchParams.set("mcpRelayUrl", mcpRelayEndpoint);
const client = {
name: clientName,
// Not used anymore.
version: void 0
};
url2.searchParams.set("client", JSON.stringify(client));
url2.searchParams.set("protocolVersion", this._protocolVersion.toString());
const token = process.env.PLAYWRIGHT_MCP_EXTENSION_TOKEN;
if (token)
url2.searchParams.set("token", token);
const href = url2.toString();
const channel = registry.isChromiumAlias(this._browserChannel) ? "chromium" : this._browserChannel;
let executablePath = this._executablePath;
if (!executablePath) {
const executableInfo = registry.findExecutable(channel);
if (!executableInfo)
throw new Error(`Unsupported channel: "${this._browserChannel}"`);
executablePath = executableInfo.executablePath();
if (!executablePath)
throw new Error(`"${this._browserChannel}" executable not found. Make sure it is installed at a standard location.`);
}
const args = [];
const userDataDir = process.env.PWTEST_EXTENSION_USER_DATA_DIR;
if (userDataDir)
args.push(`--user-data-dir=${userDataDir}`);
if (import_os25.default.platform() === "linux" && channel === "chromium")
args.push("--no-sandbox");
args.push(href);
(0, import_child_process6.spawn)(executablePath, args, {
windowsHide: true,
detached: true,
shell: false,
stdio: "ignore"
});
}
stop() {
this._closeConnections("Server stopped");
this._wss.close();
}
_closeConnections(reason) {
this._closeCDPConnection(reason);
this._closeExtensionConnection(reason);
}
_onConnection(ws3, request2) {
const url2 = new URL(`http://localhost${request2.url}`);
debugLogger2(`New connection to ${url2.pathname}`);
if (url2.pathname === this._cdpPath) {
this._handlePlaywrightConnection(ws3);
} else if (url2.pathname === this._extensionPath) {
this._handleExtensionConnection(ws3);
} else {
debugLogger2(`Invalid path: ${url2.pathname}`);
ws3.close(4004, "Invalid path");
}
}
_handlePlaywrightConnection(ws3) {
if (!this._extensionConnection) {
debugLogger2("Rejecting Playwright connection: extension not connected");
ws3.close(1e3, "Extension not connected");
return;
}
if (this._cdpConnection) {
debugLogger2("Rejecting second Playwright connection");
ws3.close(1e3, "Another CDP client already connected");
return;
}
this._cdpConnection = ws3;
this._handler.connectOverCDP((msg) => this._sendToCDPClient(msg));
ws3.on("message", async (data) => {
try {
await this._handlePlaywrightMessage(JSON.parse(data.toString()));
} catch (error) {
debugLogger2(`Error while handling Playwright message
${data.toString()}
`, error);
}
});
ws3.on("close", () => {
this._closeExtensionConnection("Playwright client disconnected");
debugLogger2("Playwright WebSocket closed");
});
ws3.on("error", (error) => {
debugLogger2("Playwright WebSocket error:", error);
});
debugLogger2("Playwright MCP connected");
}
_closeExtensionConnection(reason) {
this._extensionConnection?.close(reason);
if (!this._extensionConnectionPromise.isDone())
this._extensionConnectionPromise.reject(new Error(reason));
}
_closeCDPConnection(reason) {
if (this._cdpConnection?.readyState === ws2.OPEN)
this._cdpConnection.close(1e3, reason);
}
_handleExtensionConnection(ws3) {
if (this._extensionConnection) {
ws3.close(1e3, "Another extension connection already established");
return;
}
this._extensionConnection = new ExtensionConnection(ws3);
this._extensionConnection.onclose = (reason) => {
debugLogger2("Extension WebSocket closed:", reason);
this._handler.onExtensionDisconnect(reason);
this._closeCDPConnection(`Extension disconnected: ${reason}`);
};
this._extensionConnection.onmessage = (method, params2) => this._handler.handleExtensionEvent(method, params2);
this._extensionConnectionPromise.resolve();
}
async _handlePlaywrightMessage(message) {
debugLogger2("\u2190 Playwright:", `${message.method} (id=${message.id})`);
const { id, sessionId, method, params: params2 } = message;
try {
const result2 = await this._handleCDPCommand(method, params2, sessionId);
this._sendToCDPClient({ id, sessionId, result: result2 });
} catch (e) {
debugLogger2("Error in the extension:", e);
this._sendToCDPClient({
id,
sessionId,
error: { message: e.message }
});
}
}
async _handleCDPCommand(method, params2, sessionId) {
switch (method) {
case "Browser.getVersion": {
return {
protocolVersion: "1.3",
product: "Chrome/Extension-Bridge",
userAgent: "CDP-Bridge-Server/1.0.0"
};
}
case "Browser.setDownloadBehavior": {
return {};
}
}
const handled = await this._handler.handleCDPCommand(method, params2, sessionId);
if (handled)
return handled.result;
return await this._handler.forwardToExtension(method, params2, sessionId);
}
_sendToCDPClient(message) {
debugLogger2("\u2192 Playwright:", `${message.method ?? `response(id=${message.id})`}`);
this._cdpConnection?.send(JSON.stringify(message));
}
};
ExtensionConnection = class {
constructor(ws3) {
this._callbacks = /* @__PURE__ */ new Map();
this._lastId = 0;
this._ws = ws3;
this._ws.on("message", this._onMessage.bind(this));
this._ws.on("close", this._onClose.bind(this));
this._ws.on("error", this._onError.bind(this));
}
async send(method, params2) {
if (this._ws.readyState !== ws2.OPEN)
throw new Error(`Unexpected WebSocket state: ${this._ws.readyState}`);
const id = ++this._lastId;
this._ws.send(JSON.stringify({ id, method, params: params2 }));
const error = new Error(`Protocol error: ${method}`);
return new Promise((resolve, reject) => {
this._callbacks.set(id, { resolve, reject, error });
});
}
close(message) {
debugLogger2("closing extension connection:", message);
if (this._ws.readyState === ws2.OPEN)
this._ws.close(1e3, message);
}
_onMessage(event) {
const eventData = event.toString();
let parsedJson;
try {
parsedJson = JSON.parse(eventData);
} catch (e) {
debugLogger2(`<closing ws> Closing websocket due to malformed JSON. eventData=${eventData} e=${e?.message}`);
this._ws.close();
return;
}
try {
this._handleParsedMessage(parsedJson);
} catch (e) {
debugLogger2(`<closing ws> Closing websocket due to failed onmessage callback. eventData=${eventData} e=${e?.message}`);
this._ws.close();
}
}
_handleParsedMessage(object) {
if (object.id && this._callbacks.has(object.id)) {
const callback = this._callbacks.get(object.id);
this._callbacks.delete(object.id);
if (object.error) {
const error = callback.error;
error.message = object.error;
callback.reject(error);
} else {
callback.resolve(object.result);
}
} else if (object.id) {
debugLogger2("\u2190 Extension: unexpected response", object);
} else {
this.onmessage?.(object.method, object.params);
}
}
_onClose(event) {
debugLogger2(`<ws closed> code=${event.code} reason=${event.reason}`);
this._dispose();
this.onclose?.(event.reason);
}
_onError(event) {
debugLogger2(`<ws error> message=${event.message} type=${event.type} target=${event.target}`);
this._dispose();
}
_dispose() {
for (const callback of this._callbacks.values())
callback.reject(new Error("WebSocket closed"));
this._callbacks.clear();
}
};
}
});
// packages/playwright-core/src/tools/mcp/extensionContextFactory.ts
async function createExtensionBrowser(channel, executablePath, clientName) {
if (!executablePath) {
const userDataDir = process.env.PWTEST_EXTENSION_USER_DATA_DIR ?? defaultUserDataDirForChannel(channel);
if (userDataDir && !await isPlaywrightExtensionInstalled(userDataDir))
throw new Error(`Playwright Extension not found in "${userDataDir}". Install it from ${playwrightExtensionInstallUrl}`);
}
const httpServer = createHttpServer();
await startHttpServer(httpServer, {});
const relay = new CDPRelayServer(httpServer, channel, executablePath);
debugLogger3(`CDP relay server started, extension endpoint: ${relay.extensionEndpoint()}.`);
try {
await relay.establishExtensionConnection(clientName);
return await playwright.chromium.connectOverCDP(relay.cdpEndpoint(), { isLocal: true, timeout: 0 });
} catch (error) {
relay.stop();
httpServer.close();
throw error;
}
}
var debug15, debugLogger3;
var init_extensionContextFactory = __esm({
"packages/playwright-core/src/tools/mcp/extensionContextFactory.ts"() {
"use strict";
init_network();
init_chromiumChannels();
init_inprocess();
init_extension();
init_cdpRelay();
debug15 = require("./utilsBundle").debug;
debugLogger3 = debug15("pw:mcp:relay");
}
});
// packages/playwright-core/src/tools/utils/connect.ts
async function connectToBrowserAcrossVersions(descriptor) {
const pw = require(descriptor.playwrightLib);
const browserType = pw[descriptor.browser.browserName];
return await browserType.connect(descriptor.endpoint ?? descriptor.pipeName);
}
var init_connect2 = __esm({
"packages/playwright-core/src/tools/utils/connect.ts"() {
"use strict";
}
});
// packages/playwright-core/src/tools/mcp/browserFactory.ts
async function createBrowserWithInfo(config, clientInfo, cliOptions) {
if (config.browser.remoteEndpoint)
return await createRemoteBrowser(config);
let browser;
let canBind = false;
let ownership = "own";
if (config.browser.cdpEndpoint) {
browser = await createCDPBrowser(config);
canBind = true;
ownership = "attached";
} else if (config.browser.isolated) {
browser = await createIsolatedBrowser(config, clientInfo);
canBind = true;
ownership = "own";
} else if (config.extension) {
const { channel, executablePath } = resolveExtensionOptions(cliOptions);
browser = await createExtensionBrowser(channel, executablePath, clientInfo.clientName);
ownership = "attached";
} else {
browser = await createPersistentBrowser(config, clientInfo);
canBind = true;
ownership = "own";
}
return { browser, browserInfo: browserInfo(browser, config), canBind, ownership };
}
function browserInfo(browser, config) {
return {
// eslint-disable-next-line no-restricted-syntax
guid: browser._guid,
browserName: config.browser.browserName,
launchOptions: config.browser.launchOptions,
userDataDir: config.browser.userDataDir
};
}
async function createIsolatedBrowser(config, clientInfo) {
testDebug2("create browser (isolated)");
const browserType = playwright[config.browser.browserName];
const tracesDir = await computeTracesDir(config, clientInfo);
const browser = await browserType.launch({
tracesDir,
...config.browser.launchOptions,
handleSIGINT: false,
handleSIGTERM: false
}).catch((error) => {
if (error.message.includes("Executable doesn't exist"))
throwBrowserIsNotInstalledError(config);
throw error;
});
return browser;
}
async function createCDPBrowser(config) {
testDebug2("create browser (cdp)");
const browser = await playwright.chromium.connectOverCDP(config.browser.cdpEndpoint, {
headers: config.browser.cdpHeaders,
timeout: config.browser.cdpTimeout
});
return browser;
}
async function createRemoteBrowser(config) {
testDebug2("create browser (remote)");
const descriptor = await serverRegistry.find(config.browser.remoteEndpoint);
if (descriptor) {
const browser2 = await connectToBrowserAcrossVersions(descriptor);
return {
browser: browser2,
browserInfo: {
guid: descriptor.browser.guid,
browserName: descriptor.browser.browserName,
launchOptions: descriptor.browser.launchOptions,
userDataDir: descriptor.browser.userDataDir
},
canBind: false,
ownership: "attached"
};
}
const endpoint = config.browser.remoteEndpoint;
const playwrightObject = playwright;
const browser = await connectToBrowser(playwrightObject, { endpoint });
browser._connectToBrowserType(playwrightObject[browser._browserName], {}, void 0);
return { browser, browserInfo: browserInfo(browser, config), canBind: false, ownership: "attached" };
}
async function createPersistentBrowser(config, clientInfo) {
testDebug2("create browser (persistent)");
const userDataDir = config.browser.userDataDir ?? await createUserDataDir(config, clientInfo);
const tracesDir = await computeTracesDir(config, clientInfo);
if (await isProfileLocked5Times(userDataDir))
throw new Error(`Browser is already in use for ${userDataDir}, use --isolated to run multiple instances of the same browser`);
const browserType = playwright[config.browser.browserName];
const configIgnoreDefaultArgs = config.browser.launchOptions?.ignoreDefaultArgs;
const launchOptions = {
tracesDir,
...config.browser.launchOptions,
...config.browser.contextOptions,
handleSIGINT: false,
handleSIGTERM: false,
ignoreDefaultArgs: configIgnoreDefaultArgs === true ? true : [
"--disable-extensions",
...Array.isArray(configIgnoreDefaultArgs) ? configIgnoreDefaultArgs : []
]
};
try {
const browserContext = await browserType.launchPersistentContext(userDataDir, launchOptions);
const browser = browserContext.browser();
return browser;
} catch (error) {
if (error.message.includes("Executable doesn't exist"))
throwBrowserIsNotInstalledError(config);
if (error.message.includes("cannot open shared object file: No such file or directory")) {
const browserName = launchOptions.channel ?? config.browser.browserName;
throw new Error(`Missing system dependencies required to run browser ${browserName}. Install them with: sudo npx playwright install-deps ${browserName}`);
}
if (error.message.includes("ProcessSingleton") || error.message.includes("exitCode=21"))
throw new Error(`Browser is already in use for ${userDataDir}, use --isolated to run multiple instances of the same browser`);
throw error;
}
}
async function createUserDataDir(config, clientInfo) {
const dir = process.env.PWMCP_PROFILES_DIR_FOR_TEST ?? registryDirectory;
const browserToken = config.browser.launchOptions?.channel ?? config.browser?.browserName;
const rootPathToken = createHash(clientInfo.cwd);
const result2 = import_path54.default.join(dir, `mcp-${browserToken}-${rootPathToken}`);
await import_fs56.default.promises.mkdir(result2, { recursive: true });
return result2;
}
function createHash(data) {
return import_crypto26.default.createHash("sha256").update(data).digest("hex").slice(0, 7);
}
async function computeTracesDir(config, clientInfo) {
return import_path54.default.resolve(outputDir({ config, cwd: clientInfo.cwd }), "traces");
}
async function isProfileLocked5Times(userDataDir) {
for (let i = 0; i < 5; i++) {
if (!isProfileLocked(userDataDir))
return false;
await new Promise((f) => setTimeout(f, 1e3));
}
return true;
}
function isProfileLocked(userDataDir) {
const lockFile = process.platform === "win32" ? "lockfile" : "SingletonLock";
const lockPath = import_path54.default.join(userDataDir, lockFile);
if (process.platform === "win32") {
try {
const fd = import_fs56.default.openSync(lockPath, "r+");
import_fs56.default.closeSync(fd);
return false;
} catch (e) {
return e.code !== "ENOENT";
}
}
try {
const target = import_fs56.default.readlinkSync(lockPath);
const pid = parseInt(target.split("-").pop() || "", 10);
if (isNaN(pid))
return false;
process.kill(pid, 0);
return true;
} catch {
return false;
}
}
function throwBrowserIsNotInstalledError(config) {
const channel = config.browser.launchOptions?.channel ?? config.browser.browserName;
if (config.skillMode)
throw new Error(`Browser "${channel}" is not installed. Run \`playwright-cli install-browser ${channel}\` to install`);
else
throw new Error(`Browser "${channel}" is not installed. Run \`npx @playwright/mcp install-browser ${channel}\` to install`);
}
var import_crypto26, import_fs56, import_path54;
var init_browserFactory = __esm({
"packages/playwright-core/src/tools/mcp/browserFactory.ts"() {
"use strict";
import_crypto26 = __toESM(require("crypto"));
import_fs56 = __toESM(require("fs"));
import_path54 = __toESM(require("path"));
init_inprocess();
init_registry();
init_log();
init_context();
init_extensionContextFactory();
init_connect2();
init_serverRegistry();
init_config2();
init_connect();
}
});
// packages/playwright-core/src/tools/mcp/index.ts
async function createConnection(userConfig = {}, contextGetter) {
const config = await resolveConfig(userConfig);
const tools = filteredTools(config);
const backendFactory = {
name: "api",
nameInConfig: "api",
version: packageJSON.version,
toolSchemas: tools.map((tool) => tool.schema),
create: async (clientInfo) => {
const browser = contextGetter ? new SimpleBrowser(await contextGetter()) : (await createBrowserWithInfo(config, clientInfo, {})).browser;
const context2 = config.browser.isolated ? await browser.newContext(config.browser.contextOptions) : browser.contexts()[0];
return new BrowserBackend(config, context2, tools);
},
disposed: async () => {
}
};
return createServer("api", packageJSON.version, backendFactory, false);
}
var SimpleBrowser;
var init_mcp = __esm({
"packages/playwright-core/src/tools/mcp/index.ts"() {
"use strict";
init_config2();
init_tools();
init_browserFactory();
init_browserBackend();
init_server2();
init_package();
SimpleBrowser = class {
constructor(context2) {
this._context = context2;
}
contexts() {
return [this._context];
}
async newContext() {
throw new Error("Creating a new context is not supported in SimpleBrowserContextFactory.");
}
};
}
});
// packages/playwright-core/src/tools/mcp/program.ts
function decorateMCPCommand(command) {
command.option("--allowed-hosts <hosts...>", "comma-separated list of hosts this server is allowed to serve from. Defaults to the host the server is bound to. Pass '*' to disable the host check.", commaSeparatedList).option("--allowed-origins <origins>", "semicolon-separated list of TRUSTED origins to allow the browser to request. Default is to allow all.\nImportant: *does not* serve as a security boundary and *does not* affect redirects. ", semicolonSeparatedList).option("--allow-unrestricted-file-access", "allow access to files outside of the workspace roots. Also allows unrestricted access to file:// URLs. By default access to file system is restricted to workspace root directories (or cwd if no roots are configured) only, and navigation to file:// URLs is blocked.").option("--blocked-origins <origins>", "semicolon-separated list of origins to block the browser from requesting. Blocklist is evaluated before allowlist. If used without the allowlist, requests not matching the blocklist are still allowed.\nImportant: *does not* serve as a security boundary and *does not* affect redirects.", semicolonSeparatedList).option("--block-service-workers", "block service workers").option("--browser <browser>", "browser or chrome channel to use, possible values: chrome, firefox, webkit, msedge.").option("--caps <caps>", "comma-separated list of additional capabilities to enable, possible values: vision, pdf, devtools.", commaSeparatedList).option("--cdp-endpoint <endpoint>", "CDP endpoint to connect to.").option("--cdp-header <headers...>", "CDP headers to send with the connect request, multiple can be specified.", headerParser).option("--cdp-timeout <timeout>", "timeout in milliseconds for connecting to CDP endpoint, defaults to 30000ms", numberParser).option("--codegen <lang>", 'specify the language to use for code generation, possible values: "typescript", "none". Default is "typescript".', enumParser.bind(null, "--codegen", ["none", "typescript"])).option("--config <path>", "path to the configuration file.").option("--console-level <level>", 'level of console messages to return: "error", "warning", "info", "debug". Each level includes the messages of more severe levels.', enumParser.bind(null, "--console-level", ["error", "warning", "info", "debug"])).option("--device <device>", 'device to emulate, for example: "iPhone 15"').option("--executable-path <path>", "path to the browser executable.").option("--extension", 'Connect to a running browser instance (Edge/Chrome only). Requires the "Playwright Extension" to be installed.').option("--endpoint <endpoint>", "Bound browser endpoint to connect to.").option("--grant-permissions <permissions...>", 'List of permissions to grant to the browser context, for example "geolocation", "clipboard-read", "clipboard-write".', commaSeparatedList).option("--headless", "run browser in headless mode, headed by default").option("--host <host>", "host to bind server to. Default is localhost. Use 0.0.0.0 to bind to all interfaces.").option("--ignore-https-errors", "ignore https errors").option("--init-page <path...>", "path to TypeScript file to evaluate on Playwright page object").option("--init-script <path...>", "path to JavaScript file to add as an initialization script. The script will be evaluated in every page before any of the page's scripts. Can be specified multiple times.").option("--isolated", "keep the browser profile in memory, do not save it to disk.").option("--image-responses <mode>", 'whether to send image responses to the client. Can be "allow" or "omit", Defaults to "allow".', enumParser.bind(null, "--image-responses", ["allow", "omit"])).option("--no-sandbox", "disable the sandbox for all process types that are normally sandboxed.").option("--output-dir <path>", "path to the directory for output files.").option("--output-mode <mode>", 'whether to save snapshots, console messages, network logs to a file or to the standard output. Can be "file" or "stdout". Default is "stdout".', enumParser.bind(null, "--output-mode", ["file", "stdout"])).option("--port <port>", "port to listen on for SSE transport.").option("--proxy-bypass <bypass>", 'comma-separated domains to bypass proxy, for example ".com,chromium.org,.domain.com"').option("--proxy-server <proxy>", 'specify proxy server, for example "http://myproxy:3128" or "socks5://myproxy:8080"').option("--sandbox", "enable the sandbox for all process types that are normally not sandboxed.").option("--save-session", "Whether to save the Playwright MCP session into the output directory.").option("--secrets <path>", "path to a file containing secrets in the dotenv format", dotenvFileLoader).option("--shared-browser-context", "reuse the same browser context between all connected HTTP clients.").option("--snapshot-mode <mode>", 'when taking snapshots for responses, specifies the mode to use. Can be "full" or "none". Default is "full".').option("--storage-state <path>", "path to the storage state file for isolated sessions.").option("--test-id-attribute <attribute>", 'specify the attribute to use for test ids, defaults to "data-testid"').option("--timeout-action <timeout>", "specify action timeout in milliseconds, defaults to 5000ms", numberParser).option("--timeout-navigation <timeout>", "specify navigation timeout in milliseconds, defaults to 60000ms", numberParser).option("--user-agent <ua string>", "specify user agent string").option("--user-data-dir <path>", "path to the user data directory. If not specified, a temporary directory will be created.").option("--viewport-size <size>", 'specify browser viewport size in pixels, for example "1280x720"', resolutionParser.bind(null, "--viewport-size")).addOption(new ProgramOption("--vision", "Legacy option, use --caps=vision instead").hideHelp()).action(async (options2) => {
options2.sandbox = options2.sandbox === true ? void 0 : false;
setupExitWatchdog();
if (options2.vision) {
console.error("The --vision option is deprecated, use --caps=vision instead");
options2.caps = "vision";
}
if (options2.caps?.includes("tracing"))
options2.caps.push("devtools");
const config = await resolveCLIConfigForMCP(options2);
const tools = filteredTools(config);
const useSharedBrowser = config.sharedBrowserContext || config.browser.isolated;
let sharedBrowser;
let clientCount = 0;
const clientNameCounters = /* @__PURE__ */ new Map();
const factory = {
name: "Playwright",
nameInConfig: "playwright",
version: version2,
toolSchemas: tools.map((tool) => tool.schema),
create: async (clientInfo) => {
if (useSharedBrowser && clientCount === 0) {
const { browser: browser2, canBind: canBind2 } = await createBrowserWithInfo(config, clientInfo, options2);
sharedBrowser = browser2;
if (canBind2)
await browser2.bind(clientInfo.clientName, { workspaceDir: clientInfo.cwd });
}
clientCount++;
const { browser, canBind } = sharedBrowser ? { browser: sharedBrowser, canBind: false } : await createBrowserWithInfo(config, clientInfo, options2);
if (canBind) {
const count = (clientNameCounters.get(clientInfo.clientName) ?? 0) + 1;
clientNameCounters.set(clientInfo.clientName, count);
const sessionName = count > 1 ? `${clientInfo.clientName} (${count})` : clientInfo.clientName;
await browser.bind(sessionName, { workspaceDir: clientInfo.cwd });
}
const browserContext = config.browser.isolated ? await browser.newContext(config.browser.contextOptions) : browser.contexts()[0];
return new BrowserBackend(config, browserContext, tools);
},
disposed: async (backend) => {
clientCount--;
if (sharedBrowser && clientCount > 0)
return;
testDebug2("close browser");
sharedBrowser = void 0;
const browserContext = backend.browserContext;
await browserContext.close().catch(() => {
});
await browserContext.browser().close().catch(() => {
});
}
};
await start2(factory, config.server);
});
}
var ProgramOption, version2;
var init_program3 = __esm({
"packages/playwright-core/src/tools/mcp/program.ts"() {
"use strict";
init_server2();
init_config2();
init_watchdog();
init_browserFactory();
init_browserBackend();
init_tools();
init_log();
init_package();
({ ProgramOption } = require("./utilsBundle"));
version2 = packageJSON.version;
}
});
// packages/playwright-core/src/tools/cli-daemon/helpGenerator.ts
function commandArgs(command) {
const args = [];
const shape = command.args ? command.args.shape : {};
for (const [name, schema] of Object.entries(shape)) {
const zodSchema = schema;
const description = zodSchema.description ?? "";
args.push({ name, description, optional: zodSchema.safeParse(void 0).success });
}
return args;
}
function commandArgsText(args) {
return args.map((a) => a.optional ? `[${a.name}]` : `<${a.name}>`).join(" ");
}
function generateCommandHelp(command) {
const args = commandArgs(command);
const lines = [
`playwright-cli ${command.name} ${commandArgsText(args)}`,
"",
command.description,
""
];
if (args.length) {
lines.push("Arguments:");
lines.push(...args.map((a) => formatWithGap(` ${a.optional ? `[${a.name}]` : `<${a.name}>`}`, a.description.toLowerCase())));
}
if (command.options) {
lines.push("Options:");
const optionsShape = command.options.shape;
for (const [name, schema] of Object.entries(optionsShape)) {
const zodSchema = schema;
const description = (zodSchema.description ?? "").toLowerCase();
lines.push(formatWithGap(` --${name}`, description));
}
}
return lines.join("\n");
}
function generateHelp() {
const lines = [];
lines.push("Usage: playwright-cli <command> [args] [options]");
lines.push("Usage: playwright-cli -s=<session> <command> [args] [options]");
const commandsByCategory = /* @__PURE__ */ new Map();
for (const c of categories)
commandsByCategory.set(c.name, []);
for (const command of Object.values(commands)) {
if (command.hidden)
continue;
commandsByCategory.get(command.category).push(command);
}
for (const c of categories) {
const cc = commandsByCategory.get(c.name);
if (!cc.length)
continue;
lines.push(`
${c.title}:`);
for (const command of cc)
lines.push(generateHelpEntry(command));
}
lines.push("\nGlobal options:");
lines.push(formatWithGap(" --help [command]", "print help"));
lines.push(formatWithGap(" --json", "output response as JSON"));
lines.push(formatWithGap(" --raw", "output only the result value, without status and code"));
lines.push(formatWithGap(" --version", "print version"));
return lines.join("\n");
}
function generateHelpEntry(command) {
const args = commandArgs(command);
const prefix = ` ${command.name} ${commandArgsText(args)}`;
const suffix = command.description.toLowerCase();
return formatWithGap(prefix, suffix);
}
function unwrapZodType(schema) {
if ("unwrap" in schema && typeof schema.unwrap === "function")
return unwrapZodType(schema.unwrap());
return schema;
}
function isBooleanSchema(schema) {
return unwrapZodType(schema) instanceof z29.ZodBoolean;
}
function generateHelpJSON() {
const booleanOptions2 = /* @__PURE__ */ new Set();
const commandEntries = {};
for (const [name, command] of Object.entries(commands)) {
const flags = {};
if (command.options) {
const optionsShape = command.options.shape;
for (const [flagName, schema] of Object.entries(optionsShape)) {
const isBoolean = isBooleanSchema(schema);
flags[flagName] = isBoolean ? "boolean" : "string";
if (isBoolean)
booleanOptions2.add(flagName);
}
}
const args = command.args ? Object.keys(command.args.shape) : [];
commandEntries[name] = { help: generateCommandHelp(command), flags, args };
if (command.raw)
commandEntries[name].raw = true;
}
return {
global: generateHelp(),
commands: commandEntries,
booleanOptions: [...booleanOptions2]
};
}
function formatWithGap(prefix, text2, threshold = 30) {
const indent = Math.max(1, threshold - prefix.length);
return prefix + " ".repeat(indent) + text2;
}
var z29, categories;
var init_helpGenerator = __esm({
"packages/playwright-core/src/tools/cli-daemon/helpGenerator.ts"() {
"use strict";
init_commands();
z29 = require("./utilsBundle").z;
categories = [
{ name: "core", title: "Core" },
{ name: "navigation", title: "Navigation" },
{ name: "keyboard", title: "Keyboard" },
{ name: "mouse", title: "Mouse" },
{ name: "export", title: "Save as" },
{ name: "tabs", title: "Tabs" },
{ name: "storage", title: "Storage" },
{ name: "network", title: "Network" },
{ name: "devtools", title: "DevTools" },
{ name: "install", title: "Install" },
{ name: "config", title: "Configuration" },
{ name: "browsers", title: "Browser sessions" }
];
}
});
// packages/playwright-core/src/tools/cli-daemon/daemon.ts
async function socketExists(socketPath) {
try {
const stat = await import_fs57.default.promises.stat(socketPath);
if (stat?.isSocket())
return true;
} catch (e) {
}
return false;
}
async function startCliDaemonServer(sessionName, browserContext, browserInfo2, contextConfig, clientInfo, mcpClientInfo, options2) {
const sessionConfig = createSessionConfig(clientInfo, sessionName, browserInfo2, options2);
const { socketPath } = sessionConfig;
if (process.platform !== "win32" && await socketExists(socketPath)) {
try {
await import_fs57.default.promises.unlink(socketPath);
} catch (error) {
throw error;
}
}
const backend = new BrowserBackend(contextConfig, browserContext, browserTools);
await backend.initialize(mcpClientInfo);
if (browserContext.isClosed())
throw new Error("Browser context was closed before the daemon could start");
const server = import_net10.default.createServer((socket) => {
const connection = new SocketConnection(socket);
const abortController = new AbortController();
connection.onclose = () => abortController.abort();
connection.onmessage = async (message) => {
const { id, method, params: params2 } = message;
try {
if (method === "stop") {
await deleteSessionFile(clientInfo, sessionConfig);
const sendAck = async () => connection.send({ id, result: "ok" }).catch(() => {
});
if (options2?.exitOnClose)
gracefullyProcessExitDoNotHang(0, () => sendAck());
else
await sendAck();
} else if (method === "run") {
const { toolName, toolParams } = parseCliCommand(params2.args);
toolParams._meta = { cwd: params2.cwd, raw: params2.raw || params2.json, json: !!params2.json };
const response2 = await backend.callTool(toolName, toolParams, abortController.signal);
await connection.send({ id, result: formatResult(response2) });
} else {
throw new Error(`Unknown method: ${method}`);
}
} catch (e) {
const error = process.env.PWDEBUGIMPL ? e.stack || e.message : e.message;
connection.send({ id, error }).catch(() => {
});
}
};
});
decorateServer(server);
browserContext.on("close", () => Promise.resolve().then(async () => {
await deleteSessionFile(clientInfo, sessionConfig);
if (options2?.exitOnClose)
gracefullyProcessExitDoNotHang(0);
}));
await new Promise((resolve, reject) => {
server.on("error", reject);
server.listen(socketPath, () => resolve());
});
await saveSessionFile(clientInfo, sessionConfig);
return socketPath;
}
async function saveSessionFile(clientInfo, sessionConfig) {
await import_fs57.default.promises.mkdir(clientInfo.daemonProfilesDir, { recursive: true });
const sessionFile = import_path55.default.join(clientInfo.daemonProfilesDir, `${sessionConfig.name}.session`);
await import_fs57.default.promises.writeFile(sessionFile, JSON.stringify(sessionConfig, null, 2));
}
async function deleteSessionFile(clientInfo, sessionConfig) {
await import_fs57.default.promises.unlink(sessionConfig.socketPath).catch(() => {
});
if (!sessionConfig.cli.persistent) {
const sessionFile = import_path55.default.join(clientInfo.daemonProfilesDir, `${sessionConfig.name}.session`);
await import_fs57.default.promises.rm(sessionFile).catch(() => {
});
}
}
function formatResult(result2) {
const isError4 = result2.isError;
const text2 = result2.content[0].type === "text" ? result2.content[0].text : void 0;
return { isError: isError4, text: text2 };
}
function parseCliCommand(args) {
const command = commands[args._[0]];
if (!command)
throw new Error("Command is required");
return parseCommand(command, args);
}
function daemonSocketPath(clientInfo, sessionName) {
return makeSocketPath("cli", `${clientInfo.workspaceDirHash}-${sessionName}`);
}
function createSessionConfig(clientInfo, sessionName, browserInfo2, options2 = {}) {
return {
name: sessionName,
version: clientInfo.version,
timestamp: Date.now(),
socketPath: daemonSocketPath(clientInfo, sessionName),
workspaceDir: clientInfo.workspaceDir,
attached: options2.ownership === "attached" ? true : void 0,
cli: { persistent: options2.persistent },
browser: {
browserName: browserInfo2.browserName,
launchOptions: browserInfo2.launchOptions,
userDataDir: browserInfo2.userDataDir
}
};
}
var import_fs57, import_net10, import_path55;
var init_daemon = __esm({
"packages/playwright-core/src/tools/cli-daemon/daemon.ts"() {
"use strict";
import_fs57 = __toESM(require("fs"));
import_net10 = __toESM(require("net"));
import_path55 = __toESM(require("path"));
init_network();
init_fileUtils();
init_processLauncher();
init_browserBackend();
init_tools();
init_command();
init_commands();
init_socketConnection();
}
});
// packages/playwright-core/src/tools/cli-daemon/program.ts
function decorateProgram3(program4) {
program4.argument("[session-name]", "name of the session to create or connect to", "default").option("--headed", "run in headed mode (non-headless)").option("--extension", "run with the extension").option("--browser <name>", "browser to use (chromium, chrome, firefox, webkit)").option("--persistent", "use a persistent browser context").option("--profile <path>", "path to the user data dir").option("--config <path>", "path to the config file; by default uses .playwright/cli.config.json in the project directory and ~/.playwright/cli.config.json as global config").option("--cdp <url>", "connect to an existing browser via CDP endpoint URL").option("--endpoint <endpoint>", "attach to a running Playwright browser endpoint").option("--init-workspace", "initialize workspace").option("--init-skills <value>", 'install skills for the given agent type ("claude" or "agents")').action(async (sessionName, options2) => {
if (options2.initWorkspace) {
await initWorkspace(options2.initSkills);
return;
}
setupExitWatchdog();
const clientInfo = createClientInfo();
const mcpConfig = await resolveCLIConfigForCLI(clientInfo.daemonProfilesDir, sessionName, options2);
const mcpClientInfo = {
cwd: process.cwd(),
clientName: guessClientName()
};
try {
const { browser, browserInfo: browserInfo2, canBind, ownership } = await createBrowserWithInfo(mcpConfig, mcpClientInfo, options2);
if (canBind)
await browser.bind(sessionName, { workspaceDir: clientInfo.workspaceDir });
const browserContext = mcpConfig.browser.isolated ? await browser.newContext(mcpConfig.browser.contextOptions) : browser.contexts()[0];
if (!browserContext)
throw new Error("Error: unable to connect to a browser that does not have any contexts");
const persistent = options2.persistent || options2.profile || mcpConfig.browser.userDataDir ? true : void 0;
const socketPath = await startCliDaemonServer(sessionName, browserContext, browserInfo2, mcpConfig, clientInfo, mcpClientInfo, { persistent, exitOnClose: true, ownership });
console.log(`### Success
Daemon listening on ${socketPath}`);
console.log("<EOF>");
} catch (error) {
const message = process.env.PWDEBUGIMPL ? error.stack || error.message : error.message;
console.log(`### Error
${message}`);
console.log("<EOF>");
gracefullyProcessExitDoNotHang(1);
}
});
}
function defaultConfigFile() {
return import_path56.default.resolve(".playwright", "cli.config.json");
}
function globalConfigFile() {
return import_path56.default.join(process.env["PWTEST_CLI_GLOBAL_CONFIG"] ?? import_os26.default.homedir(), ".playwright", "cli.config.json");
}
async function initWorkspace(initSkills) {
const cwd = process.cwd();
const playwrightDir = import_path56.default.join(cwd, ".playwright");
await import_fs58.default.promises.mkdir(playwrightDir, { recursive: true });
console.log(`\u2705 Workspace initialized at \`${cwd}\`.`);
if (initSkills) {
const skillSourceDir = libPath("tools", "cli-client", "skill");
const target = initSkills === "agents" ? "agents" : "claude";
const skillDestDir = import_path56.default.join(cwd, `.${target}`, "skills", "playwright-cli");
if (!import_fs58.default.existsSync(skillSourceDir)) {
console.error("\u274C Skills source directory not found:", skillSourceDir);
process.exit(1);
}
await import_fs58.default.promises.cp(skillSourceDir, skillDestDir, { recursive: true });
console.log(`\u2705 Skills installed to \`${import_path56.default.relative(cwd, skillDestDir)}\`.`);
}
await ensureConfiguredBrowserInstalled();
}
async function ensureConfiguredBrowserInstalled() {
if (getAsBooleanFromENV("PLAYWRIGHT_SKIP_BROWSER_DOWNLOAD"))
return;
if (import_fs58.default.existsSync(defaultConfigFile()) || import_fs58.default.existsSync(globalConfigFile())) {
const clientInfo = createClientInfo();
const config = await resolveCLIConfigForCLI(clientInfo.daemonProfilesDir, "default", {});
const browserName = config.browser.browserName;
const channel = config.browser.launchOptions.channel;
if (!channel || channel.startsWith("chromium"))
await resolveAndInstall(channel ?? browserName);
} else {
const channel = await findOrInstallDefaultBrowser();
if (channel !== "chrome")
await createDefaultConfig(channel);
}
}
async function findOrInstallDefaultBrowser() {
const channels = ["chrome", "msedge"];
for (const channel of channels) {
const executable = registry.findExecutable(channel);
if (!executable?.executablePath())
continue;
await resolveAndInstall("ffmpeg");
console.log(`\u2705 Found ${channel}, will use it as the default browser.`);
return channel;
}
await resolveAndInstall("chromium");
return "chromium";
}
async function resolveAndInstall(nameOrChannel) {
const executables = registry.resolveBrowsers([nameOrChannel], { shell: "no" });
await registry.install(executables);
}
async function createDefaultConfig(channel) {
const config = {
browser: {
browserName: "chromium",
launchOptions: { channel }
}
};
await import_fs58.default.promises.writeFile(defaultConfigFile(), JSON.stringify(config, null, 2));
console.log(`\u2705 Created default config for ${channel} at ${import_path56.default.relative(process.cwd(), defaultConfigFile())}.`);
}
var import_fs58, import_os26, import_path56;
var init_program4 = __esm({
"packages/playwright-core/src/tools/cli-daemon/program.ts"() {
"use strict";
import_fs58 = __toESM(require("fs"));
import_os26 = __toESM(require("os"));
import_path56 = __toESM(require("path"));
init_env();
init_processLauncher();
init_package();
init_daemon();
init_watchdog();
init_browserFactory();
init_config2();
init_registry2();
init_registry();
}
});
// packages/playwright-core/src/tools/dashboard/sessionProvider.ts
var SessionProviderEvent;
var init_sessionProvider = __esm({
"packages/playwright-core/src/tools/dashboard/sessionProvider.ts"() {
"use strict";
SessionProviderEvent = {
SessionsChanged: "sessionsChanged",
TabsChanged: "tabsChanged",
ContextClosed: "contextClosed",
AttachRequested: "attachRequested"
};
}
});
// packages/playwright-core/src/tools/dashboard/dashboardController.ts
function browserId(browser) {
return browser._guid;
}
function pageId(p) {
return p._guid;
}
function contextId(c) {
return c._guid;
}
async function faviconUrl(page) {
const url2 = page.evaluate(async () => {
const response2 = await fetch(document.querySelector('link[rel~="icon"]')?.href ?? "/favicon.ico");
if (!response2.ok)
return void 0;
const blob = await response2.blob();
if (!blob.type.startsWith("image/"))
return void 0;
return await new Promise((resolve, reject) => {
const reader = new FileReader();
reader.onloadend = () => resolve(reader.result);
reader.onerror = reject;
reader.readAsDataURL(blob);
});
}).catch(() => void 0);
const timeout = new Promise((resolve) => setTimeout(() => resolve(void 0), 3e3));
return await Promise.race([url2, timeout]);
}
var import_path57, import_os27, import_fs59, import_crypto27, import_child_process7, DashboardConnection, AttachedPage;
var init_dashboardController = __esm({
"packages/playwright-core/src/tools/dashboard/dashboardController.ts"() {
"use strict";
import_path57 = __toESM(require("path"));
import_os27 = __toESM(require("os"));
import_fs59 = __toESM(require("fs"));
import_crypto27 = __toESM(require("crypto"));
import_child_process7 = require("child_process");
init_eventsHelper();
init_registry2();
init_sessionProvider();
DashboardConnection = class {
constructor(provider, onclose, onconnected, onAnnotationSubmit) {
this._pushTabsScheduled = false;
this._visible = true;
this._annotateWaitingForAttach = false;
this._streams = /* @__PURE__ */ new Map();
this._provider = provider;
this._onclose = onclose;
this._onconnected = onconnected;
this._onAnnotationSubmit = onAnnotationSubmit;
this._recordingDir = import_fs59.default.mkdtempSync(import_path57.default.join(import_os27.default.tmpdir(), "playwright-recordings-"));
}
onconnect() {
this._provider.on(SessionProviderEvent.SessionsChanged, () => {
this._pushSessions();
void this._tryRevealPending();
});
this._provider.on(SessionProviderEvent.TabsChanged, () => this._pushTabs());
this._provider.on(SessionProviderEvent.ContextClosed, (context2) => {
if (this._attachedPage?.page.context() === context2) {
this._attachedPage.dispose();
this._attachedPage = void 0;
}
});
this._provider.on(SessionProviderEvent.AttachRequested, (page) => {
void this._switchAttachedTo(page);
});
this._provider.start();
this._onconnected?.();
}
onclose() {
this._provider.dispose();
this._attachedPage?.dispose();
this._attachedPage = void 0;
for (const stream3 of this._streams.values()) {
void stream3.handle.close().catch(() => {
}).then(() => import_fs59.default.promises.unlink(stream3.path)).catch(() => {
});
}
this._streams.clear();
this._onclose();
}
async dispatch(method, params2) {
const handler = this[method];
if (typeof handler === "function")
return handler.call(this, params2);
const attached = this._attachedPage;
if (!attached)
return;
const onAtt = attached[method];
if (typeof onAtt === "function")
return onAtt.call(attached, params2);
}
async selectTab(params2) {
const page = this._provider.findPage(params2);
if (page)
await this._switchAttachedTo(page);
this._pushTabs();
}
async newTab(params2) {
const context2 = this._provider.findContext(params2);
if (!context2)
return;
const page = await context2.newPage();
await this._switchAttachedTo(page);
this._pushTabs();
}
async closeTab(params2) {
const page = this._provider.findPage(params2);
await page?.close({ reason: "Closed in Dashboard" });
}
async closeSession(params2) {
await this._provider.closeSession(params2.browser);
}
async setVisible(params2) {
if (this._visible === params2.visible)
return;
this._visible = params2.visible;
await this._attachedPage?.setScreencastActive(params2.visible);
}
revealSession(sessionName, workspaceDir) {
this._pendingReveal = { sessionName, workspaceDir };
void this._tryRevealPending();
}
revealPage(pageId4) {
this._pendingReveal = { pageId: pageId4 };
void this._tryRevealPending();
}
async _tryRevealPending() {
const pending = this._pendingReveal;
if (!pending)
return;
const allPages = this._provider.contextEntries().flatMap((e) => e.context.pages().map((page2) => ({ entry: e, page: page2 })));
let page;
if (pending.pageId !== void 0) {
page = allPages.find(({ page: p }) => pageId(p) === pending.pageId)?.page;
} else if (pending.sessionName !== void 0) {
page = allPages.find(({ entry }) => entry.descriptor.title === pending.sessionName && (pending.workspaceDir === void 0 || entry.descriptor.workspaceDir === pending.workspaceDir))?.page;
}
if (!page)
return;
this._pendingReveal = void 0;
await this._switchAttachedTo(page);
this._pushTabs();
}
async submitAnnotation(params2) {
this._onAnnotationSubmit?.(params2.frames, params2.feedback);
}
async cancelAnnotation() {
this._onAnnotationSubmit?.([], "");
}
async reveal(params2) {
switch (import_os27.default.platform()) {
case "darwin":
(0, import_child_process7.execFile)("open", ["-R", params2.path]);
break;
case "win32":
(0, import_child_process7.execFile)("explorer", ["/select,", params2.path]);
break;
case "linux":
(0, import_child_process7.execFile)("xdg-open", [import_path57.default.dirname(params2.path)]);
break;
}
}
async readStream(params2) {
const stream3 = this._streams.get(params2.streamId);
if (!stream3)
throw new Error(`Unknown stream: ${params2.streamId}`);
const buffer = Buffer.alloc(256 * 1024);
const { bytesRead } = await stream3.handle.read(buffer, 0, buffer.length);
if (bytesRead === 0) {
this._streams.delete(params2.streamId);
await stream3.handle.close().catch(() => {
});
await import_fs59.default.promises.unlink(stream3.path).catch(() => {
});
return { data: "", eof: true };
}
return { data: buffer.subarray(0, bytesRead).toString("base64"), eof: false };
}
visible() {
return this._visible;
}
emitSessions(sessions) {
this.sendEvent?.("sessions", { sessions, clientInfo: createClientInfo() });
}
emitTabs(tabs) {
this.sendEvent?.("tabs", { tabs });
}
emitFrame(data, viewportWidth, viewportHeight) {
this.sendEvent?.("frame", { data, viewportWidth, viewportHeight });
}
emitAnnotate() {
if (!this._attachedPage) {
this._annotateWaitingForAttach = true;
return;
}
this.sendEvent?.("annotate", {});
}
emitCancelAnnotate() {
this._annotateWaitingForAttach = false;
this.sendEvent?.("cancelAnnotate", {});
}
artifactsDirFor(context2) {
for (const entry of this._provider.contextEntries()) {
if (entry.context === context2)
return entry.descriptor.browser.launchOptions.artifactsDir ?? this._recordingDir;
}
return this._recordingDir;
}
_pushTabs() {
if (this._pushTabsScheduled)
return;
this._pushTabsScheduled = true;
queueMicrotask(async () => {
this._pushTabsScheduled = false;
try {
const tabs = await this._aggregateTabs();
this.emitTabs(tabs);
} catch {
}
});
}
_pushSessions() {
void (async () => {
try {
const sessions = await this._provider.sessions();
this.emitSessions(sessions);
} catch {
}
})();
}
async _aggregateTabs() {
const attachedPage = this._attachedPage?.page;
const tasks = [];
for (const { browser, context: context2 } of this._provider.contextEntries()) {
for (const page of context2.pages()) {
tasks.push((async () => ({
browser: browserId(browser),
context: contextId(context2),
page: pageId(page),
title: await page.title().catch(() => ""),
url: page.url(),
selected: page === attachedPage,
faviconUrl: await faviconUrl(page)
}))());
}
}
return await Promise.all(tasks);
}
async _switchAttachedTo(page) {
if (this._attachedPage?.page === page)
return;
this._attachedPage?.dispose();
const attached = new AttachedPage(this, page);
this._attachedPage = attached;
try {
await attached.init();
} catch (e) {
if (this._attachedPage === attached)
this._attachedPage = void 0;
attached.dispose();
throw e;
}
if (this._annotateWaitingForAttach && this._attachedPage === attached) {
this._annotateWaitingForAttach = false;
this.sendEvent?.("annotate", {});
}
}
_handleAttachedPageClose(context2) {
this._attachedPage?.dispose();
this._attachedPage = void 0;
const next = context2.pages()[0];
if (next)
void this._switchAttachedTo(next);
this._pushTabs();
}
};
AttachedPage = class {
constructor(owner, page) {
this._listeners = [];
this._screencastRunning = false;
this._recordingPath = null;
this._disposed = false;
this._owner = owner;
this._page = page;
}
get page() {
return this._page;
}
async init() {
this._listeners.push(
eventsHelper.addEventListener(this._page, "close", () => {
this._owner._handleAttachedPageClose(this._page.context());
}),
eventsHelper.addEventListener(this._page, "framenavigated", (frame) => {
if (frame === this._page.mainFrame())
this._owner._pushTabs();
})
);
this._owner._pushTabs();
if (this._owner.visible()) {
this._screencastRunning = true;
await this._startScreencast(this._page);
}
}
dispose() {
this._disposed = true;
this._listeners.forEach((d) => d.dispose());
this._listeners = [];
if (this._screencastRunning)
this._page.screencast.stop().catch(() => {
});
this._screencastRunning = false;
this._recordingPath = null;
}
async setScreencastActive(active) {
if (active && !this._screencastRunning) {
this._screencastRunning = true;
await this._startScreencast(this._page);
} else if (!active && this._screencastRunning) {
this._screencastRunning = false;
await this._page.screencast.stop().catch(() => {
});
}
}
async navigate(params2) {
if (!params2.url)
return;
await this._page.goto(params2.url);
}
async back() {
await this._page.goBack();
}
async forward() {
await this._page.goForward();
}
async reload() {
await this._page.reload();
}
async mousemove(params2) {
await this._page.mouse.move(params2.x, params2.y);
}
async mousedown(params2) {
await this._page.mouse.move(params2.x, params2.y);
await this._page.mouse.down({ button: params2.button || "left" });
}
async mouseup(params2) {
await this._page.mouse.move(params2.x, params2.y);
await this._page.mouse.up({ button: params2.button || "left" });
}
async wheel(params2) {
await this._page.mouse.wheel(params2.deltaX, params2.deltaY);
}
async keydown(params2) {
await this._page.keyboard.down(params2.key);
}
async keyup(params2) {
await this._page.keyboard.up(params2.key);
}
async startRecording() {
const artifactsDir = this._owner.artifactsDirFor(this._page.context());
this._recordingPath = import_path57.default.join(artifactsDir, `recording-${Date.now()}.webm`);
if (this._screencastRunning)
await this._restartScreencast(this._page);
}
async stopRecording() {
const p = this._recordingPath;
if (!p)
throw new Error("No recording in progress");
this._recordingPath = null;
if (this._screencastRunning)
await this._restartScreencast(this._page);
const handle = await import_fs59.default.promises.open(p, "r");
const streamId = import_crypto27.default.randomUUID();
this._owner._streams.set(streamId, { handle, path: p });
return { streamId };
}
async screenshot() {
const buffer = await this._page.screenshot({ type: "png" });
const ariaSnapshot = await this._page.ariaSnapshot({ boxes: true, mode: "ai" });
const vp = await this._viewportSize();
return {
data: buffer.toString("base64"),
viewportWidth: vp.width,
viewportHeight: vp.height,
ariaSnapshot
};
}
async _viewportSize() {
const vp = this._page.viewportSize();
if (vp)
return vp;
return await this._page.evaluate(() => ({ width: window.innerWidth, height: window.innerHeight }));
}
async _startScreencast(page) {
await page.screencast.start({
onFrame: ({ data, viewportWidth, viewportHeight }) => {
if (this._disposed)
return;
this._owner.emitFrame(data.toString("base64"), viewportWidth, viewportHeight);
},
size: { width: 1280, height: 800 },
...this._recordingPath ? { path: this._recordingPath } : {}
});
}
async _restartScreencast(page) {
await page.screencast.stop().catch(() => {
});
await this._startScreencast(page);
}
};
}
});
// packages/playwright-core/src/tools/dashboard/registrySessionProvider.ts
function pageId2(p) {
return p._guid;
}
function contextId2(c) {
return c._guid;
}
var import_events28, BrowserTracker, RegistrySessionProvider;
var init_registrySessionProvider = __esm({
"packages/playwright-core/src/tools/dashboard/registrySessionProvider.ts"() {
"use strict";
import_events28 = require("events");
init_eventsHelper();
init_connect2();
init_serverRegistry();
init_sessionProvider();
BrowserTracker = class _BrowserTracker {
constructor(descriptor, browser, callbacks) {
this._contextListeners = /* @__PURE__ */ new Map();
this._browserListeners = [];
this.descriptor = descriptor;
this.browser = browser;
this._callbacks = callbacks;
}
static async create(descriptor, callbacks) {
try {
const browser = await connectToBrowserAcrossVersions(descriptor);
const slot = new _BrowserTracker(descriptor, browser, callbacks);
for (const context2 of browser.contexts())
slot._wireContext(context2);
slot._browserListeners.push(eventsHelper.addEventListener(browser, "context", (context2) => {
slot._wireContext(context2);
}));
return slot;
} catch {
return void 0;
}
}
contexts() {
return this.browser.contexts();
}
dispose() {
this._browserListeners.forEach((d) => d.dispose());
this._browserListeners = [];
for (const listeners of this._contextListeners.values())
listeners.forEach((d) => d.dispose());
this._contextListeners.clear();
}
_wireContext(context2) {
if (this._contextListeners.has(context2))
return;
const onTabsChanged = () => this._callbacks.onTabsChanged();
const listeners = [
eventsHelper.addEventListener(context2, "page", onTabsChanged),
eventsHelper.addEventListener(context2, "pageload", onTabsChanged),
eventsHelper.addEventListener(context2, "pageclose", onTabsChanged),
eventsHelper.addEventListener(context2, "framenavigated", (frame) => {
if (frame === frame.page().mainFrame())
this._callbacks.onTabsChanged();
}),
eventsHelper.addEventListener(context2, "close", () => {
const ls = this._contextListeners.get(context2);
if (ls) {
ls.forEach((d) => d.dispose());
this._contextListeners.delete(context2);
}
this._callbacks.onContextClosed(context2);
this._callbacks.onTabsChanged();
})
];
this._contextListeners.set(context2, listeners);
this._callbacks.onTabsChanged();
}
};
RegistrySessionProvider = class extends import_events28.EventEmitter {
constructor() {
super(...arguments);
this._trackers = /* @__PURE__ */ new Map();
this._pushSessionsScheduled = false;
this._scheduleSessions = () => {
if (this._pushSessionsScheduled)
return;
this._pushSessionsScheduled = true;
queueMicrotask(async () => {
this._pushSessionsScheduled = false;
try {
const sessions = await this.sessions();
await this._reconcile(sessions);
this.emit(SessionProviderEvent.SessionsChanged);
this.emit(SessionProviderEvent.TabsChanged);
} catch {
}
});
};
}
start() {
this._serverRegistryDispose = serverRegistry.watch();
serverRegistry.on("added", this._scheduleSessions);
serverRegistry.on("removed", this._scheduleSessions);
serverRegistry.on("changed", this._scheduleSessions);
this._scheduleSessions();
}
dispose() {
serverRegistry.off("added", this._scheduleSessions);
serverRegistry.off("removed", this._scheduleSessions);
serverRegistry.off("changed", this._scheduleSessions);
this._serverRegistryDispose?.();
this._serverRegistryDispose = void 0;
for (const tracker of this._trackers.values())
tracker.dispose();
this._trackers.clear();
this.removeAllListeners();
}
async sessions() {
const byWs = await serverRegistry.list();
const sessions = [];
for (const list of byWs.values()) {
for (const status of list) {
if (status.title.startsWith("--playwright-internal"))
continue;
sessions.push(status);
}
}
return sessions;
}
contextEntries() {
const entries = [];
for (const tracker of this._trackers.values()) {
for (const context2 of tracker.contexts())
entries.push({ browser: tracker.browser, context: context2, descriptor: tracker.descriptor });
}
return entries;
}
findContext(params2) {
const tracker = this._trackers.get(params2.browser);
if (!tracker)
return void 0;
return tracker.contexts().find((c) => contextId2(c) === params2.context);
}
findPage(params2) {
const context2 = this.findContext(params2);
return context2?.pages().find((p) => pageId2(p) === params2.page);
}
async closeSession(browserId2) {
const descriptor = serverRegistry.readDescriptor(browserId2);
const browser = await connectToBrowserAcrossVersions(descriptor);
try {
await Promise.all(browser.contexts().map((context2) => context2.close()));
await browser.close();
} catch {
}
}
async _reconcile(sessions) {
const connectable = /* @__PURE__ */ new Map();
for (const status of sessions)
connectable.set(status.browser.guid, status);
for (const [guid, tracker] of this._trackers) {
if (connectable.has(guid))
continue;
tracker.dispose();
this._trackers.delete(guid);
}
for (const [guid, status] of connectable) {
if (this._trackers.has(guid))
continue;
const tracker = await BrowserTracker.create(status, {
onTabsChanged: () => this.emit(SessionProviderEvent.TabsChanged),
onContextClosed: (context2) => this.emit(SessionProviderEvent.ContextClosed, context2)
});
if (!tracker)
continue;
if (this._trackers.has(guid)) {
tracker.dispose();
continue;
}
this._trackers.set(guid, tracker);
}
}
};
}
});
// packages/playwright-core/src/tools/dashboard/identitySessionProvider.ts
function synthesiseDescriptor(browser) {
const browserName = browser.browserType().name();
const browserInfo2 = {
// eslint-disable-next-line no-restricted-syntax -- _guid is very conservative.
guid: browser._guid,
browserName,
launchOptions: {}
};
return {
title: "Playwright",
playwrightVersion: packageJSON.version,
playwrightLib: packageRoot,
browser: browserInfo2
};
}
function pageId3(p) {
return p._guid;
}
function contextId3(c) {
return c._guid;
}
var import_events29, IdentitySessionProvider;
var init_identitySessionProvider = __esm({
"packages/playwright-core/src/tools/dashboard/identitySessionProvider.ts"() {
"use strict";
import_events29 = require("events");
init_eventsHelper();
init_package();
init_sessionProvider();
IdentitySessionProvider = class extends import_events29.EventEmitter {
constructor(context2) {
super();
this._listeners = [];
this._closed = false;
const browser = context2.browser();
if (!browser)
throw new Error("SingleContextDashboardProvider requires a context with an attached browser");
this._context = context2;
this._browser = browser;
this._descriptor = synthesiseDescriptor(browser);
}
start() {
const emitTabsChanged = () => this.emit(SessionProviderEvent.TabsChanged);
this._listeners.push(
eventsHelper.addEventListener(this._context, "page", emitTabsChanged),
eventsHelper.addEventListener(this._context, "pageload", emitTabsChanged),
eventsHelper.addEventListener(this._context, "pageclose", emitTabsChanged),
eventsHelper.addEventListener(this._context, "framenavigated", (frame) => {
if (frame === frame.page().mainFrame())
this.emit(SessionProviderEvent.TabsChanged);
}),
eventsHelper.addEventListener(this._context, "close", () => {
this._closed = true;
this.emit(SessionProviderEvent.ContextClosed, this._context);
this.emit(SessionProviderEvent.TabsChanged);
})
);
this.emit(SessionProviderEvent.SessionsChanged);
this.emit(SessionProviderEvent.TabsChanged);
const firstPage = this._context.pages()[0];
if (firstPage)
this.emit(SessionProviderEvent.AttachRequested, firstPage);
}
dispose() {
this._listeners.forEach((d) => d.dispose());
this._listeners = [];
this.removeAllListeners();
}
async sessions() {
return this._closed ? [] : [this._descriptor];
}
contextEntries() {
if (this._closed)
return [];
return [{ browser: this._browser, context: this._context, descriptor: this._descriptor }];
}
findContext(params2) {
if (this._closed)
return void 0;
if (params2.browser !== this._descriptor.browser.guid)
return void 0;
if (contextId3(this._context) !== params2.context)
return void 0;
return this._context;
}
findPage(params2) {
const context2 = this.findContext(params2);
return context2?.pages().find((p) => pageId3(p) === params2.page);
}
async closeSession() {
}
};
}
});
// packages/playwright-core/src/tools/dashboard/dashboardApp.ts
async function startDashboardServer(provider, options2) {
const httpServer = new HttpServer();
const dashboardDir = libPath("vite", "dashboard");
const connections = /* @__PURE__ */ new Set();
let currentReveal = options2;
let pendingAnnotate = false;
const waitingSockets = /* @__PURE__ */ new Set();
const submitAnnotation = (frames, feedback) => {
if (waitingSockets.size === 0)
return;
const payload = JSON.stringify({ frames, feedback });
for (const socket of waitingSockets) {
socket.write(payload);
socket.end();
}
waitingSockets.clear();
};
httpServer.createWebSocket(() => {
let connection;
connection = new DashboardConnection(provider, () => connections.delete(connection), () => {
if (currentReveal.pageId)
connection.revealPage(currentReveal.pageId);
else if (currentReveal.sessionName)
connection.revealSession(currentReveal.sessionName, currentReveal.workspaceDir);
if (pendingAnnotate) {
pendingAnnotate = false;
connection.emitAnnotate();
}
}, submitAnnotation);
connections.add(connection);
return connection;
});
const wsGuid = httpServer.wsGuid();
httpServer.routePath("/", (_, response2) => {
response2.statusCode = 302;
response2.setHeader("Location", `/index.html?ws=${wsGuid}`);
response2.end();
return true;
});
if (false)
await attachDashboardDevServer(httpServer);
else
attachDashboardStaticServer(httpServer, dashboardDir);
await httpServer.start({ port: options2.port, host: options2.host });
const reveal = (next) => {
currentReveal = next;
if (next.pageId) {
for (const connection of connections)
connection.revealPage(next.pageId);
return;
}
if (!next.sessionName)
return;
for (const connection of connections)
connection.revealSession(next.sessionName, next.workspaceDir);
};
const triggerAnnotate = () => {
if (connections.size === 0) {
pendingAnnotate = true;
return;
}
for (const connection of connections)
connection.emitAnnotate();
};
const notifyAnnotateEnded = () => {
pendingAnnotate = false;
for (const connection of connections)
connection.emitCancelAnnotate();
};
const registerAnnotateWaiter = (socket) => {
waitingSockets.add(socket);
const cleanup = () => {
if (!waitingSockets.delete(socket))
return;
if (waitingSockets.size === 0)
notifyAnnotateEnded();
};
socket.on("close", cleanup);
socket.on("error", cleanup);
};
const close3 = () => httpServer.stop();
return { url: httpServer.urlPrefix("human-readable"), reveal, triggerAnnotate, registerAnnotateWaiter, close: close3 };
}
function attachDashboardStaticServer(httpServer, dashboardDir) {
httpServer.routePrefix("/", (request2, response2) => {
const pathname = new URL(request2.url, `http://${request2.headers.host}`).pathname;
const filePath = pathname === "/" ? "index.html" : pathname.substring(1);
const resolved = import_path58.default.join(dashboardDir, filePath);
if (!resolved.startsWith(dashboardDir))
return false;
return httpServer.serveFile(request2, response2, resolved);
});
}
async function innerOpenDashboardApp(options2) {
const server = await startDashboardServer(new RegistrySessionProvider(), options2);
const { page } = await launchApp2("dashboard", { onClose: () => gracefullyProcessExitDoNotHang(0) });
await page.goto(server.url);
return { page, server };
}
async function launchApp2(appName, options2) {
const channel = findChromiumChannelBestEffort("javascript");
const context2 = await playwright.chromium.launchPersistentContext("", {
ignoreDefaultArgs: ["--enable-automation"],
channel,
headless: !!process.env.PWTEST_DASHBOARD_APP_BIND_TITLE,
args: [
"--app=data:text/html,",
"--test-type=",
`--window-size=1280,800`,
`--window-position=100,100`
],
viewport: null
});
if (process.env.PWTEST_DASHBOARD_APP_BIND_TITLE)
await context2.browser()?.bind(process.env.PWTEST_DASHBOARD_APP_BIND_TITLE, { workspaceDir: process.cwd() });
const [page] = context2.pages();
if (process.platform === "darwin") {
context2.on("page", async (newPage) => {
if (newPage.mainFrame().url() === "chrome://new-tab-page/") {
await page.bringToFront();
await newPage.close();
}
});
}
page.on("close", () => options2?.onClose?.());
const image = await import_fs60.default.promises.readFile(libPath("tools", "dashboard", "appIcon.png"));
await page._setDockTile?.(image);
await syncLocalStorageWithSettings2(page, appName);
return { context: context2, page };
}
async function syncLocalStorageWithSettings2(page, appName) {
const settingsFile = process.env.PWTEST_DASHBOARD_SETTINGS_FILE ?? import_path58.default.join(registryDirectory, ".settings", `${appName}.json`);
await page.exposeBinding("_saveSerializedSettings", (_, settings2) => {
import_fs60.default.mkdirSync(import_path58.default.dirname(settingsFile), { recursive: true });
import_fs60.default.writeFileSync(settingsFile, settings2);
});
const settings = await import_fs60.default.promises.readFile(settingsFile, "utf-8").catch(() => "{}");
await page.addInitScript(
`(${String((settings2) => {
if (location && location.protocol === "data:")
return;
if (window.top !== window)
return;
Object.entries(settings2).map(([k, v]) => localStorage[k] = v);
window.saveSettings = () => {
window._saveSerializedSettings(JSON.stringify({ ...localStorage }));
};
})})(${settings});
`
);
}
function dashboardSocketPath() {
return makeSocketPath("dashboard", "app");
}
function parseOpenArgs() {
const args = minimist(process.argv.slice(2), { string: ["sessionName", "workspaceDir", "host", "pageId"], boolean: ["annotate", "kill"] });
const portStr = args.port;
return {
sessionName: args.sessionName,
workspaceDir: args.workspaceDir,
pageId: args.pageId,
port: portStr !== void 0 ? Number(portStr) : void 0,
host: args.host,
annotate: !!args.annotate,
kill: !!args.kill
};
}
async function acquireSingleton(options2) {
const socketPath = dashboardSocketPath();
if (process.platform !== "win32")
await import_fs60.default.promises.mkdir(import_path58.default.dirname(socketPath), { recursive: true });
return await new Promise((resolve, reject) => {
const server = import_net11.default.createServer();
server.listen(socketPath, () => resolve(server));
server.on("error", (err) => {
if (err.code !== "EADDRINUSE")
return reject(err);
const client = import_net11.default.connect(socketPath, () => {
client.write(JSON.stringify(options2) + "\n");
reject(new Error("already running"));
});
client.on("error", () => {
if (process.platform !== "win32")
import_fs60.default.unlinkSync(socketPath);
server.listen(socketPath, () => resolve(server));
});
});
});
}
async function openDashboardApp() {
const options2 = parseOpenArgs();
if (options2.kill) {
await runKillClient();
return;
}
if (options2.annotate) {
await runAnnotateClient(options2);
return;
}
process.on("unhandledRejection", (error) => {
console.error("Unhandled promise rejection:", error);
});
if (options2.port !== void 0) {
const { url: url2 } = await startDashboardServer(new RegistrySessionProvider(), options2);
console.log(`Listening on ${url2}`);
selfDestructOnParentGone();
return;
}
let server;
process.on("exit", () => server?.close());
try {
server = await acquireSingleton(options2);
} catch {
return;
}
const statePromise = innerOpenDashboardApp(options2);
server?.on("connection", (socket) => {
let buffer = "";
socket.on("data", (data) => {
buffer += data.toString();
const newlineIndex = buffer.indexOf("\n");
if (newlineIndex === -1)
return;
const line = buffer.slice(0, newlineIndex);
buffer = buffer.slice(newlineIndex + 1);
let parsed;
try {
parsed = JSON.parse(line);
} catch {
}
if (!parsed) {
socket.end();
return;
}
if (parsed.kill) {
server?.close();
socket.end(JSON.stringify({ pid: process.pid }) + "\n", () => gracefullyProcessExitDoNotHang(0));
return;
}
void statePromise.then(({ page, server: dashboard }) => {
if (parsed.annotate) {
page?.bringToFront().catch(() => {
});
dashboard.reveal(parsed);
dashboard.triggerAnnotate();
dashboard.registerAnnotateWaiter(socket);
} else {
page?.bringToFront().catch(() => {
});
dashboard.reveal(parsed);
socket.end();
}
});
});
});
await statePromise;
}
async function openDashboardForContext(context2) {
const server = await startDashboardServer(new IdentitySessionProvider(context2), {});
let closed = false;
const close3 = async () => {
if (closed)
return;
closed = true;
await server.close();
};
const { page } = await launchApp2("dashboard", { onClose: () => {
void close3();
} });
context2.on("close", () => {
void close3();
});
await page.goto(server.url);
}
async function runKillClient() {
const socketPath = dashboardSocketPath();
const pid = await new Promise((resolve, reject) => {
const client = import_net11.default.connect(socketPath);
let data = "";
client.once("connect", () => {
client.write(JSON.stringify({ kill: true }) + "\n");
});
client.on("data", (chunk) => {
data += chunk;
});
client.once("end", () => {
let pid2;
try {
pid2 = JSON.parse(data.trim()).pid;
} catch {
}
if (pid2 === void 0)
reject(new Error("Dashboard did not return its PID"));
else
resolve(pid2);
});
client.once("error", () => resolve(void 0));
});
if (pid === void 0)
return;
const deadline = monotonicTime() + 35e3;
while (monotonicTime() < deadline) {
try {
process.kill(pid, 0);
} catch {
return;
}
await new Promise((r) => setTimeout(r, 50));
}
throw new Error(`Dashboard process ${pid} did not exit within the deadline`);
}
async function runAnnotateClient(options2) {
selfDestructOnParentGone();
const socketPath = dashboardSocketPath();
const tryConnect = () => new Promise((resolve) => {
const s = import_net11.default.connect(socketPath);
const onError = () => {
s.destroy();
resolve(void 0);
};
s.once("connect", () => {
s.off("error", onError);
resolve(s);
});
s.once("error", onError);
});
const deadline = Date.now() + 15e3;
let socket;
while (Date.now() < deadline) {
socket = await tryConnect();
if (socket)
break;
await new Promise((r) => setTimeout(r, 200));
}
if (!socket) {
console.error("Dashboard did not start in time.");
gracefullyProcessExitDoNotHang(1);
return;
}
socket.write(JSON.stringify(options2) + "\n");
const chunks = [];
await new Promise((resolve, reject) => {
socket.on("data", (chunk) => chunks.push(chunk));
socket.on("end", () => resolve());
socket.on("error", reject);
});
socket.destroy();
const text2 = Buffer.concat(chunks).toString();
if (!text2)
return;
console.log(text2);
}
function selfDestructOnParentGone() {
process.stdin.on("close", () => {
gracefullyProcessExitDoNotHang(0);
});
}
var import_fs60, import_path58, import_net11;
var init_dashboardApp = __esm({
"packages/playwright-core/src/tools/dashboard/dashboardApp.ts"() {
"use strict";
import_fs60 = __toESM(require("fs"));
import_path58 = __toESM(require("path"));
import_net11 = __toESM(require("net"));
init_httpServer();
init_fileUtils();
init_processLauncher();
init_time();
init_package();
init_inprocess();
init_registry();
init_minimist();
init_dashboardController();
init_registrySessionProvider();
init_identitySessionProvider();
}
});
// packages/playwright-core/src/tools/index.ts
var tools_exports = {};
__export(tools_exports, {
BrowserBackend: () => BrowserBackend,
DirTraceLoaderBackend: () => DirTraceLoaderBackend,
Tab: () => Tab,
browserTools: () => browserTools,
cliProgram: () => program2,
compareSemver: () => compareSemver,
createConnection: () => createConnection,
decorateCliDaemonProgram: () => decorateProgram3,
decorateMCPCommand: () => decorateMCPCommand,
extractTrace: () => extractTrace,
filteredTools: () => filteredTools,
generateHelp: () => generateHelp,
generateHelpJSON: () => generateHelpJSON,
isProfileLocked: () => isProfileLocked,
isSystemDirectory: () => isSystemDirectory,
openDashboardApp: () => openDashboardApp,
openDashboardForContext: () => openDashboardForContext,
outputDir: () => outputDir,
parseResponse: () => parseResponse,
resolveCLIConfigForCLI: () => resolveCLIConfigForCLI,
resolveCLIConfigForMCP: () => resolveCLIConfigForMCP,
setupExitWatchdog: () => setupExitWatchdog,
start: () => start2
});
var init_tools2 = __esm({
"packages/playwright-core/src/tools/index.ts"() {
"use strict";
init_watchdog();
init_browserBackend();
init_response();
init_tab();
init_tools();
init_server2();
init_mcp();
init_config2();
init_context();
init_fileUtils();
init_browserFactory();
init_socketConnection();
init_traceParser();
init_program3();
init_program();
init_helpGenerator();
init_program4();
init_dashboardApp();
}
});
// packages/playwright-core/src/coreBundle.ts
var coreBundle_exports = {};
__export(coreBundle_exports, {
clientEventEmitter: () => EventEmitter3,
getPlaywrightVersion: () => getPlaywrightVersion,
getUserAgent: () => getUserAgent,
inprocess: () => inprocess_exports,
iso: () => isomorphic_exports,
libCli: () => program_exports,
libCliTestStub: () => programWithTestStub_exports,
oop: () => outofprocess_exports,
registry: () => registry_exports,
remote: () => playwrightServer_exports,
server: () => server_exports,
tools: () => tools_exports,
utils: () => utils_exports
});
module.exports = __toCommonJS(coreBundle_exports);
var init_coreBundle = __esm({
"packages/playwright-core/src/coreBundle.ts"() {
init_isomorphic();
init_utils();
init_eventEmitter();
init_program2();
init_programWithTestStub();
init_inprocess();
init_outofprocess();
init_playwrightServer();
init_registry();
init_server();
init_tools2();
init_userAgent();
}
});
init_coreBundle();
// Annotate the CommonJS export names for ESM import in node:
0 && (module.exports = {
clientEventEmitter,
getPlaywrightVersion,
getUserAgent,
inprocess,
iso,
libCli,
libCliTestStub,
oop,
registry,
remote,
server,
tools,
utils
});
/**
* @license
* Copyright 2024 Google Inc.
* Modifications copyright (c) Microsoft Corporation.
* SPDX-License-Identifier: Apache-2.0
*/
/**
* @license
* Copyright 2023 Google Inc.
* SPDX-License-Identifier: Apache-2.0
*/