"use strict";
// Copyright 2020 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
    if (k2 === undefined) k2 = k;
    var desc = Object.getOwnPropertyDescriptor(m, k);
    if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
      desc = { enumerable: true, get: function() { return m[k]; } };
    }
    Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
    if (k2 === undefined) k2 = k;
    o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
    Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
    o["default"] = v;
});
var __importStar = (this && this.__importStar) || (function () {
    var ownKeys = function(o) {
        ownKeys = Object.getOwnPropertyNames || function (o) {
            var ar = [];
            for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
            return ar;
        };
        return ownKeys(o);
    };
    return function (mod) {
        if (mod && mod.__esModule) return mod;
        var result = {};
        if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
        __setModuleDefault(result, mod);
        return result;
    };
})();
Object.defineProperty(exports, "__esModule", { value: true });
exports.WasmLocationLabels = exports.BREAKPOINT_ITEM_SELECTOR = exports.PAUSE_ON_UNCAUGHT_EXCEPTION_SELECTOR = exports.ENABLE_OVERRIDES_SELECTOR = exports.OVERRIDES_TAB_SELECTOR = exports.DEBUGGER_PAUSED_EVENT = exports.TURNED_ON_PAUSE_BUTTON_SELECTOR = exports.STEP_OUT_BUTTON = exports.STEP_OVER_BUTTON = exports.STEP_INTO_BUTTON = exports.SELECTED_THREAD_SELECTOR = exports.THREADS_SELECTOR = exports.SCOPE_LOCAL_VALUES_SELECTOR = exports.CODE_LINE_SELECTOR = exports.CODE_LINE_COLUMN_SELECTOR = exports.PAUSE_INDICATOR_SELECTOR = exports.SOURCES_LINES_SELECTOR = exports.RESUME_BUTTON = exports.PAUSE_BUTTON = exports.ACTIVE_LINE = void 0;
exports.getLineNumberElement = getLineNumberElement;
exports.doubleClickSourceTreeItem = doubleClickSourceTreeItem;
exports.waitForSourcesPanel = waitForSourcesPanel;
exports.openSourcesPanel = openSourcesPanel;
exports.openFileInSourcesPanel = openFileInSourcesPanel;
exports.openSnippetsSubPane = openSnippetsSubPane;
exports.createNewSnippet = createNewSnippet;
exports.openOverridesSubPane = openOverridesSubPane;
exports.openFileInEditor = openFileInEditor;
exports.openSourceCodeEditorForFile = openSourceCodeEditorForFile;
exports.getBreakpointHitLocation = getBreakpointHitLocation;
exports.getOpenSources = getOpenSources;
exports.waitForHighlightedLine = waitForHighlightedLine;
exports.getToolbarText = getToolbarText;
exports.addBreakpointForLine = addBreakpointForLine;
exports.removeBreakpointForLine = removeBreakpointForLine;
exports.addLogpointForLine = addLogpointForLine;
exports.isBreakpointSet = isBreakpointSet;
exports.enableInlineBreakpointForLine = enableInlineBreakpointForLine;
exports.disableInlineBreakpointForLine = disableInlineBreakpointForLine;
exports.checkBreakpointDidNotActivate = checkBreakpointDidNotActivate;
exports.getBreakpointDecorators = getBreakpointDecorators;
exports.getNonBreakableLines = getNonBreakableLines;
exports.executionLineHighlighted = executionLineHighlighted;
exports.getCallFrameNames = getCallFrameNames;
exports.getCallFrameLocations = getCallFrameLocations;
exports.switchToCallFrame = switchToCallFrame;
exports.retrieveTopCallFrameScriptLocation = retrieveTopCallFrameScriptLocation;
exports.retrieveTopCallFrameWithoutResuming = retrieveTopCallFrameWithoutResuming;
exports.waitForStackTopMatch = waitForStackTopMatch;
exports.waitForNewLocation = waitForNewLocation;
exports.setEventListenerBreakpoint = setEventListenerBreakpoint;
exports.waitForSourceFiles = waitForSourceFiles;
exports.captureAddedSourceFiles = captureAddedSourceFiles;
exports.reloadPageAndWaitForSourceFile = reloadPageAndWaitForSourceFile;
exports.isEqualOrAbbreviation = isEqualOrAbbreviation;
exports.createSelectorsForWorkerFile = createSelectorsForWorkerFile;
exports.expandSourceTreeItem = expandSourceTreeItem;
exports.expandFileTree = expandFileTree;
exports.readSourcesTreeView = readSourcesTreeView;
exports.readIgnoreListedSources = readIgnoreListedSources;
exports.stepThroughTheCode = stepThroughTheCode;
exports.stepIn = stepIn;
exports.stepOver = stepOver;
exports.stepOut = stepOut;
exports.openNestedWorkerFile = openNestedWorkerFile;
exports.inspectMemory = inspectMemory;
exports.getScopeNames = getScopeNames;
exports.getValuesForScope = getValuesForScope;
exports.waitValuesForScope = waitValuesForScope;
exports.getPausedMessages = getPausedMessages;
exports.getWatchExpressionsValues = getWatchExpressionsValues;
exports.runSnippet = runSnippet;
exports.evaluateSelectedTextInConsole = evaluateSelectedTextInConsole;
exports.addSelectedTextToWatches = addSelectedTextToWatches;
exports.enableLocalOverrides = enableLocalOverrides;
exports.retrieveCodeMirrorEditorContent = retrieveCodeMirrorEditorContent;
exports.waitForLines = waitForLines;
exports.isPrettyPrinted = isPrettyPrinted;
exports.veImpressionForSourcesPanel = veImpressionForSourcesPanel;
const chai_1 = require("chai");
const fs = __importStar(require("node:fs"));
const path = __importStar(require("node:path"));
const paths_js_1 = require("../../conductor/paths.js");
const context_menu_helpers_js_1 = require("./context-menu-helpers.js");
const visual_logging_helpers_js_1 = require("./visual-logging-helpers.js");
exports.ACTIVE_LINE = '.CodeMirror-activeline > pre > span';
exports.PAUSE_BUTTON = '[aria-label="Pause script execution"]';
exports.RESUME_BUTTON = '[aria-label="Resume script execution"]';
exports.SOURCES_LINES_SELECTOR = '.CodeMirror-code > div';
exports.PAUSE_INDICATOR_SELECTOR = '.paused-status';
exports.CODE_LINE_COLUMN_SELECTOR = '.cm-lineNumbers';
exports.CODE_LINE_SELECTOR = '.cm-lineNumbers .cm-gutterElement';
exports.SCOPE_LOCAL_VALUES_SELECTOR = 'li[aria-label="Local"] + ol';
exports.THREADS_SELECTOR = '[aria-label="Threads"]';
exports.SELECTED_THREAD_SELECTOR = '.thread-item[aria-selected="true"] > div.thread-item-title';
exports.STEP_INTO_BUTTON = '[aria-label="Step into next function call"]';
exports.STEP_OVER_BUTTON = '[aria-label="Step over next function call"]';
exports.STEP_OUT_BUTTON = '[aria-label="Step out of current function"]';
exports.TURNED_ON_PAUSE_BUTTON_SELECTOR = 'button.toolbar-state-on';
exports.DEBUGGER_PAUSED_EVENT = 'DevTools.DebuggerPaused';
const WATCH_EXPRESSION_VALUE_SELECTOR = '.watch-expression-tree-item .object-value-string.value';
exports.OVERRIDES_TAB_SELECTOR = '[aria-label="Overrides"]';
exports.ENABLE_OVERRIDES_SELECTOR = '[aria-label="Select folder for overrides"]';
const CLEAR_CONFIGURATION_SELECTOR = '[aria-label="Clear configuration"]';
exports.PAUSE_ON_UNCAUGHT_EXCEPTION_SELECTOR = '.pause-on-uncaught-exceptions';
exports.BREAKPOINT_ITEM_SELECTOR = '.breakpoint-item';
async function getLineNumberElement(lineNumber, devToolsPage) {
    return await devToolsPage.waitForFunction(async () => {
        const visibleLines = await devToolsPage.$$(exports.CODE_LINE_SELECTOR);
        for (let i = 0; i < visibleLines.length; i++) {
            const lineValue = await visibleLines[i].evaluate(node => node.innerText);
            if (lineValue === `${lineNumber}`) {
                return visibleLines[i];
            }
        }
        return null;
    });
}
async function doubleClickSourceTreeItem(selector, devToolsPage) {
    await devToolsPage.click(selector, { clickOptions: { count: 2, offset: { x: 40, y: 10 } } });
}
async function waitForSourcesPanel(devToolsPage) {
    // Wait for the navigation panel to show up
    await devToolsPage.waitFor('.navigator-file-tree-item, .empty-state');
}
async function openSourcesPanel(devToolsPage) {
    // Locate the button for switching to the sources tab.
    await devToolsPage.click('#tab-sources');
    await waitForSourcesPanel(devToolsPage);
    return await devToolsPage.waitForAria('sources');
}
async function openFileInSourcesPanel(testInput, devToolsPage, inspectedPage) {
    await inspectedPage.goToResource(`sources/${testInput}`);
    await openSourcesPanel(devToolsPage);
}
async function openSnippetsSubPane(devToolsPage) {
    const root = await devToolsPage.waitFor('.navigator-tabbed-pane');
    await devToolsPage.clickMoreTabsButton(root);
    await devToolsPage.click('[aria-label="Snippets"]');
    await devToolsPage.waitFor('[aria-label="New snippet"]');
}
/**
 * Creates a new snippet, optionally pre-filling it with the provided content.
 * `snippetName` must not contain spaces or special characters, otherwise
 * `createNewSnippet` will time out.
 * DevTools uses the escaped snippet name for the ARIA label. `createNewSnippet`
 * doesn't mirror the escaping so it won't be able to wait for the snippet
 * entry in the navigation tree to appear.
 */
async function createNewSnippet(snippetName, content, devToolsPage) {
    await devToolsPage.click('[aria-label="New snippet"]');
    await devToolsPage.waitFor('[aria-label^="Script snippet"]');
    await devToolsPage.typeText(snippetName);
    await devToolsPage.pressKey('Enter');
    await devToolsPage.waitFor(`[aria-label*="${snippetName}"]`);
    if (content) {
        await devToolsPage.pasteText(content);
        await devToolsPage.pressKey('s', { control: true });
    }
}
async function openOverridesSubPane(devToolsPage) {
    const root = await devToolsPage.waitFor('.navigator-tabbed-pane');
    await devToolsPage.clickMoreTabsButton(root);
    await devToolsPage.click('[aria-label="Overrides"]');
    await devToolsPage.waitFor('[aria-label="Overrides panel"]');
}
async function openFileInEditor(sourceFile, devToolsPage) {
    await waitForSourceFiles("source-file-loaded" /* SourceFileEvents.SOURCE_FILE_LOADED */, files => files.some(f => f.endsWith(sourceFile)), 
    // Open a particular file in the editor
    () => doubleClickSourceTreeItem(`[aria-label="${sourceFile}, file"]`, devToolsPage), devToolsPage);
}
async function openSourceCodeEditorForFile(sourceFile, testInput, devToolsPage, inspectedPage) {
    await openFileInSourcesPanel(testInput, devToolsPage, inspectedPage);
    await openFileInEditor(sourceFile, devToolsPage);
}
async function getBreakpointHitLocation(devToolsPage) {
    const breakpointHitHandle = await devToolsPage.waitFor('.breakpoint-item.hit');
    const locationHandle = await devToolsPage.waitFor('.location', breakpointHitHandle);
    const locationText = await locationHandle.evaluate(location => location.textContent);
    const groupHandle = await breakpointHitHandle.evaluateHandle(x => x.parentElement);
    const groupHeaderTitleHandle = await devToolsPage.waitFor('.group-header-title', groupHandle);
    const groupHeaderTitle = await groupHeaderTitleHandle?.evaluate(header => header.textContent);
    return `${groupHeaderTitle}:${locationText}`;
}
async function getOpenSources(devToolsPage) {
    const sourceTabPane = await devToolsPage.waitFor('#sources-panel-sources-view .tabbed-pane');
    const sourceTabs = await devToolsPage.waitFor('.tabbed-pane-header-tabs', sourceTabPane);
    const openSources = await sourceTabs.$$eval('.tabbed-pane-header-tab', nodes => nodes.map(n => n.getAttribute('aria-label')));
    return openSources;
}
async function waitForHighlightedLine(lineNumber, devToolsPage) {
    await devToolsPage.waitForFunction(async () => {
        const selectedLine = await devToolsPage.waitFor('.cm-highlightedLine');
        const currentlySelectedLineNumber = await selectedLine.evaluate(line => {
            return [...line.parentElement?.childNodes || []].indexOf(line);
        });
        const lineNumbers = await devToolsPage.waitFor('.cm-lineNumbers');
        const text = await lineNumbers.evaluate((node, lineNumber) => node.childNodes[lineNumber].textContent, currentlySelectedLineNumber + 1);
        return Number(text) === lineNumber;
    });
}
async function getToolbarText(devToolsPage) {
    const toolbar = await devToolsPage.waitFor('.sources-toolbar');
    if (!toolbar) {
        return [];
    }
    const textNodes = await devToolsPage.$$('.toolbar-text', toolbar);
    return await Promise.all(textNodes.map(node => node.evaluate(node => node.textContent, node)));
}
async function addBreakpointForLine(index, devToolsPage) {
    await devToolsPage.waitForFunction(async () => !(await isBreakpointSet(index, devToolsPage)));
    const breakpointLine = await getLineNumberElement(index, devToolsPage);
    chai_1.assert.isOk(breakpointLine);
    await devToolsPage.clickElement(breakpointLine);
    await devToolsPage.waitForFunction(async () => await isBreakpointSet(index, devToolsPage));
}
async function removeBreakpointForLine(index, devToolsPage) {
    await devToolsPage.waitForFunction(async () => await isBreakpointSet(index, devToolsPage));
    const breakpointLine = await getLineNumberElement(index, devToolsPage);
    chai_1.assert.isOk(breakpointLine);
    await devToolsPage.clickElement(breakpointLine);
    await devToolsPage.waitForFunction(async () => !(await isBreakpointSet(index, devToolsPage)));
}
async function addLogpointForLine(index, condition, devToolsPage) {
    const breakpointLine = await getLineNumberElement(index, devToolsPage);
    chai_1.assert.isOk(breakpointLine);
    await devToolsPage.waitForFunction(async () => !(await isBreakpointSet(index, devToolsPage)));
    await devToolsPage.clickElement(breakpointLine, { clickOptions: { button: 'right' } });
    await devToolsPage.click('aria/Add logpoint…');
    const editDialog = await devToolsPage.waitFor('.sources-edit-breakpoint-dialog');
    const conditionEditor = await devToolsPage.waitForAria('Code editor', editDialog);
    await conditionEditor.focus();
    await devToolsPage.typeText(condition);
    await devToolsPage.pressKey('Enter');
    await devToolsPage.waitForFunction(async () => await isBreakpointSet(index, devToolsPage));
}
async function isBreakpointSet(lineNumber, devToolsPage) {
    const lineNumberElement = await getLineNumberElement(lineNumber, devToolsPage);
    const breakpointLineParentClasses = await lineNumberElement?.evaluate(n => n.className);
    return breakpointLineParentClasses?.includes('cm-breakpoint');
}
/**
 * @param lineNumber 1-based line number
 * @param index 1-based index of the inline breakpoint in the given line
 */
async function enableInlineBreakpointForLine(line, index, devToolsPage) {
    const decorationSelector = `pierce/.cm-content > :nth-child(${line}) > :nth-child(${index} of .cm-inlineBreakpoint)`;
    await devToolsPage.click(decorationSelector);
    await devToolsPage.waitForFunction(() => devToolsPage.page.$eval(decorationSelector, element => !element.classList.contains('cm-inlineBreakpoint-disabled')));
}
/**
 * @param lineNumber 1-based line number
 * @param index 1-based index of the inline breakpoint in the given line
 * @param expectNoBreakpoint If we should wait for the line to not have any inline breakpoints after
 *                           the click instead of a disabled one.
 */
async function disableInlineBreakpointForLine(line, index, expectNoBreakpoint = false, devToolsPage) {
    const decorationSelector = `pierce/.cm-content > :nth-child(${line}) > :nth-child(${index} of .cm-inlineBreakpoint)`;
    await devToolsPage.click(decorationSelector);
    if (expectNoBreakpoint) {
        await devToolsPage.waitForFunction(() => devToolsPage.page.$$eval(`pierce/.cm-content > :nth-child(${line}) > .cm-inlineBreakpoint`, elements => elements.length === 0));
    }
    else {
        await devToolsPage.waitForFunction(() => devToolsPage.page.$eval(decorationSelector, element => element.classList.contains('cm-inlineBreakpoint-disabled')));
    }
}
async function checkBreakpointDidNotActivate(devToolsPage) {
    // TODO(almuthanna): make sure this check happens at a point where the pause indicator appears if it was active
    // TODO: it should actually wait for rendering to finish.
    await devToolsPage.drainTaskQueue();
    await devToolsPage.waitForNone(exports.PAUSE_INDICATOR_SELECTOR);
}
async function getBreakpointDecorators(disabledOnly = false, expected = 0, devToolsPage) {
    const selector = `.cm-breakpoint${disabledOnly ? '-disabled' : ''}`;
    const breakpointDecorators = await devToolsPage.waitForMany(selector, expected);
    return await Promise.all(breakpointDecorators.map(breakpointDecorator => breakpointDecorator.evaluate(n => Number(n.textContent))));
}
async function getNonBreakableLines(devToolsPage) {
    const selector = '.cm-nonBreakableLine';
    await devToolsPage.waitFor(selector);
    const unbreakableLines = await devToolsPage.$$(selector);
    return await Promise.all(unbreakableLines.map(unbreakableLine => unbreakableLine.evaluate(n => Number(n.textContent))));
}
async function executionLineHighlighted(devToolsPage) {
    return await devToolsPage.waitFor('.cm-executionLine');
}
async function getCallFrameNames(devToolsPage) {
    const selector = '.call-frame-item:not(.hidden) .call-frame-item-title';
    await devToolsPage.waitFor(selector);
    const items = await devToolsPage.$$(selector);
    const promises = items.map(handle => handle.evaluate(el => el.textContent));
    const results = [];
    for (const promise of promises) {
        results.push(await promise);
    }
    return results;
}
async function getCallFrameLocations(devToolsPage) {
    const selector = '.call-frame-item:not(.hidden) .call-frame-location';
    await devToolsPage.waitFor(selector);
    const items = await devToolsPage.$$(selector);
    const promises = items.map(handle => handle.evaluate(el => el.textContent));
    const results = [];
    for (const promise of promises) {
        results.push(await promise);
    }
    return results;
}
async function switchToCallFrame(index, devToolsPage) {
    const selector = `.call-frame-item[aria-posinset="${index}"]`;
    await devToolsPage.click(selector);
    await devToolsPage.waitFor(selector + '[aria-selected="true"]');
}
async function retrieveTopCallFrameScriptLocation(script, target, devToolsPage) {
    // The script will run into a breakpoint, which means that it will not actually
    // finish the evaluation, until we continue executing.
    // Thus, we have to await it at a later point, while stepping through the code.
    const scriptEvaluation = target.evaluate(script);
    // Wait for the evaluation to be paused and shown in the UI
    // and retrieve the top level call frame script location name
    const scriptLocation = await retrieveTopCallFrameWithoutResuming(devToolsPage);
    // Resume the evaluation
    await devToolsPage.click(exports.RESUME_BUTTON);
    // Make sure to await the context evaluate before asserting
    // Otherwise the Puppeteer process might crash on a failure assertion,
    // as its execution context is destroyed
    await scriptEvaluation;
    return scriptLocation;
}
async function retrieveTopCallFrameWithoutResuming(devToolsPage) {
    // Wait for the evaluation to be paused and shown in the UI
    await devToolsPage.waitFor(exports.PAUSE_INDICATOR_SELECTOR);
    // Retrieve the top level call frame script location name
    const locationHandle = await devToolsPage.waitFor('.call-frame-location');
    const scriptLocation = await locationHandle.evaluate(location => location.textContent);
    return scriptLocation;
}
async function waitForStackTopMatch(matcher, devToolsPage) {
    // The call stack is updated asynchronously, so let us wait until we see the correct one
    // (or report the last one we have seen before timeout).
    let stepLocation = '<no call stack>';
    await devToolsPage.waitForFunctionWithTries(async () => {
        stepLocation = await retrieveTopCallFrameWithoutResuming(devToolsPage) ?? '<invalid>';
        return stepLocation?.match(matcher);
    }, { tries: 10 });
    return stepLocation;
}
async function waitForNewLocation(oldLocation, devToolsPage) {
    // The call stack is updated asynchronously, so let us wait until we see the correct one
    // (or report the last one we have seen before timeout).
    let stepLocation = '<no call stack>';
    await devToolsPage.waitForFunction(async () => {
        stepLocation = await retrieveTopCallFrameWithoutResuming(devToolsPage) ?? '<invalid>';
        return stepLocation && stepLocation !== oldLocation;
    });
    return stepLocation;
}
async function setEventListenerBreakpoint(groupName, eventName, devToolsPage) {
    const eventListenerBreakpointsSection = await devToolsPage.waitForAria('Event Listener Breakpoints');
    const expanded = await eventListenerBreakpointsSection.evaluate(el => el.getAttribute('aria-expanded'));
    if (expanded !== 'true') {
        await devToolsPage.click('[aria-label="Event Listener Breakpoints"]');
        await devToolsPage.waitFor('[aria-label="Event Listener Breakpoints"][aria-expanded="true"]');
    }
    const eventSelector = `input[type="checkbox"][title="${eventName}"]`;
    const groupSelector = `input[type="checkbox"][title="${groupName}"]`;
    const groupCheckbox = await devToolsPage.waitFor(groupSelector);
    await devToolsPage.scrollElementIntoView(groupSelector);
    await devToolsPage.waitForVisible(groupSelector);
    const eventCheckbox = await devToolsPage.$(eventSelector);
    if (!eventCheckbox || !(await eventCheckbox.evaluate(x => x.checkVisibility()))) {
        // The tree element is an <li> with a ::before pseudoelement for the triangle.
        // We compute the exact coordinate of the triangle as done by isEventWithinDisclosureTriangle
        const rectData = await groupCheckbox.evaluate(node => {
            const paddingLeftValue = window.getComputedStyle(node).paddingLeft;
            const computedLeftPadding = parseFloat(paddingLeftValue);
            const left = node.getBoundingClientRect().left + computedLeftPadding;
            const top = node.getBoundingClientRect().top;
            const height = node.getBoundingClientRect().height;
            return { left, top, height };
        });
        await devToolsPage.page.mouse.click(rectData.left + 5, rectData.top + rectData.height * 0.5);
        await devToolsPage.waitForVisible(eventSelector);
    }
    await devToolsPage.setCheckBox(eventSelector, true);
}
let nextEventHandlerId = 0;
async function waitForSourceFiles(eventName, waitCondition, action, devToolsPage) {
    const eventHandlerId = nextEventHandlerId++;
    // Install new listener for the event
    await devToolsPage.evaluate((eventName, eventHandlerId) => {
        if (!window.__sourceFileEvents) {
            window.__sourceFileEvents = new Map();
        }
        const handler = (event) => {
            const { detail } = event;
            if (!detail.includes('pptr:')) {
                window.__sourceFileEvents.get(eventHandlerId)?.files.push(detail);
            }
        };
        window.__sourceFileEvents.set(eventHandlerId, { files: [], handler });
        window.addEventListener(eventName, handler);
    }, eventName, eventHandlerId);
    const result = await action();
    await devToolsPage.waitForFunction(async (logger) => {
        const files = await devToolsPage.evaluate(eventHandlerId => window.__sourceFileEvents.get(eventHandlerId)?.files, eventHandlerId);
        chai_1.assert.isOk(files);
        logger.log(`Checking ${files.length} files`);
        return await waitCondition(files);
    }, undefined, 'Waiting for source files to match condition');
    await devToolsPage.evaluate((eventName, eventHandlerId) => {
        const handler = window.__sourceFileEvents.get(eventHandlerId);
        if (!handler) {
            throw new Error('handler unexpectedly unregistered');
        }
        window.__sourceFileEvents.delete(eventHandlerId);
        window.removeEventListener(eventName, handler.handler);
    }, eventName, eventHandlerId);
    return result;
}
async function captureAddedSourceFiles(count, action, devToolsPage) {
    let capturedFileNames;
    await waitForSourceFiles("source-tree-file-added" /* SourceFileEvents.ADDED_TO_SOURCE_TREE */, files => {
        capturedFileNames = files;
        return files.length >= count;
    }, action, devToolsPage);
    return capturedFileNames.map(f => new URL(`http://${f}`).pathname);
}
async function reloadPageAndWaitForSourceFile(sourceFile, devToolsPage, inspectedPage) {
    await waitForSourceFiles("source-file-loaded" /* SourceFileEvents.SOURCE_FILE_LOADED */, files => files.some(f => f.endsWith(sourceFile)), () => inspectedPage.reload(), devToolsPage);
}
function isEqualOrAbbreviation(abbreviated, full) {
    const split = abbreviated.split('…');
    if (split.length === 1) {
        return abbreviated === full;
    }
    chai_1.assert.lengthOf(split, 2);
    return full.startsWith(split[0]) && full.endsWith(split[1]);
}
function createSelectorsForWorkerFile(workerName, folderName, fileName, workerIndex = 1, inspectedPage) {
    const rootSelector = new Array(workerIndex).fill(`[aria-label="${workerName}, worker"]`).join(' ~ ');
    const domainSelector = `${rootSelector} + ol > [aria-label="localhost:${inspectedPage.serverPort}, domain"]`;
    const folderSelector = `${domainSelector} + ol > [aria-label^="${folderName}, "]`;
    const fileSelector = `${folderSelector} + ol > [aria-label="${fileName}, file"]`;
    return {
        rootSelector,
        domainSelector,
        folderSelector,
        fileSelector,
    };
}
async function isExpanded(sourceTreeItem) {
    return await sourceTreeItem.evaluate(element => {
        return element.getAttribute('aria-expanded') === 'true';
    });
}
async function expandSourceTreeItem(selector, devToolsPage) {
    // FIXME(crbug/1112692): Refactor test to remove the timeout.
    await devToolsPage.timeout(50);
    const sourceTreeItem = await devToolsPage.waitFor(selector);
    if (!await isExpanded(sourceTreeItem)) {
        // FIXME(crbug/1112692): Refactor test to remove the timeout.
        await devToolsPage.timeout(50);
        await doubleClickSourceTreeItem(selector, devToolsPage);
    }
}
async function expandFileTree(selectors, devToolsPage) {
    await expandSourceTreeItem(selectors.rootSelector, devToolsPage);
    await expandSourceTreeItem(selectors.domainSelector, devToolsPage);
    if (selectors.folderSelector) {
        await expandSourceTreeItem(selectors.folderSelector, devToolsPage);
    }
    // FIXME(crbug/1112692): Refactor test to remove the timeout.
    await devToolsPage.timeout(50);
    return await devToolsPage.waitFor(selectors.fileSelector);
}
async function readSourcesTreeView(devToolsPage) {
    const items = await devToolsPage.$$('.navigator-folder-tree-item,.navigator-file-tree-item');
    const promises = items.map(handle => handle.evaluate(el => el.textContent));
    const results = await Promise.all(promises);
    return results.map(item => item.replace(/localhost:[0-9]+/, 'localhost:XXXX'));
}
async function readIgnoreListedSources(devToolsPage) {
    const items = await devToolsPage.$$('.navigator-folder-tree-item.is-ignore-listed,.navigator-file-tree-item.is-ignore-listed');
    const promises = items.map(handle => handle.evaluate(el => el.textContent));
    const results = await Promise.all(promises);
    return results.map(item => item.replace(/localhost:[0-9]+/, 'localhost:XXXX'));
}
async function hasPausedEvents(devToolsPage) {
    const events = await devToolsPage.getPendingEvents(exports.DEBUGGER_PAUSED_EVENT);
    return Boolean(events?.length);
}
async function stepThroughTheCode(devToolsPage, checkLineChange = true) {
    const currentLocation = checkLineChange ? await retrieveTopCallFrameWithoutResuming(devToolsPage) : '';
    await devToolsPage.getPendingEvents(exports.DEBUGGER_PAUSED_EVENT);
    await devToolsPage.pressKey('F9');
    await devToolsPage.waitForFunction(() => hasPausedEvents(devToolsPage));
    await devToolsPage.waitFor(exports.PAUSE_INDICATOR_SELECTOR);
    if (checkLineChange) {
        await waitForNewLocation(currentLocation, devToolsPage);
    }
}
async function stepIn(devToolsPage, checkLineChange = true) {
    const currentLocation = checkLineChange ? await retrieveTopCallFrameWithoutResuming(devToolsPage) : '';
    await devToolsPage.getPendingEvents(exports.DEBUGGER_PAUSED_EVENT);
    await devToolsPage.pressKey('F11');
    await devToolsPage.waitForFunction(() => hasPausedEvents(devToolsPage));
    await devToolsPage.waitFor(exports.PAUSE_INDICATOR_SELECTOR);
    if (checkLineChange) {
        await waitForNewLocation(currentLocation, devToolsPage);
    }
}
async function stepOver(devToolsPage, checkLineChange = true) {
    const currentLocation = checkLineChange ? await retrieveTopCallFrameWithoutResuming(devToolsPage) : '';
    await devToolsPage.getPendingEvents(exports.DEBUGGER_PAUSED_EVENT);
    await devToolsPage.pressKey('F10');
    await devToolsPage.waitForFunction(() => hasPausedEvents(devToolsPage));
    await devToolsPage.waitFor(exports.PAUSE_INDICATOR_SELECTOR);
    if (checkLineChange) {
        await waitForNewLocation(currentLocation, devToolsPage);
    }
}
async function stepOut(devToolsPage, checkLineChange = true) {
    const currentLocation = checkLineChange ? await retrieveTopCallFrameWithoutResuming(devToolsPage) : '';
    await devToolsPage.getPendingEvents(exports.DEBUGGER_PAUSED_EVENT);
    await devToolsPage.pressKey('F11', { shift: true });
    await devToolsPage.waitForFunction(() => hasPausedEvents(devToolsPage));
    await devToolsPage.waitFor(exports.PAUSE_INDICATOR_SELECTOR);
    if (checkLineChange) {
        await waitForNewLocation(currentLocation, devToolsPage);
    }
}
async function openNestedWorkerFile(selectors, devToolsPage) {
    await expandFileTree(selectors, devToolsPage);
    // FIXME(crbug/1112692): Refactor test to remove the timeout.
    await devToolsPage.timeout(50);
    await devToolsPage.click(selectors.fileSelector);
}
async function inspectMemory(variableName, devToolsPage) {
    await (0, context_menu_helpers_js_1.openSoftContextMenuAndClickOnItem)(`[data-object-property-name-for-test="${variableName}"]`, 'Open in Memory inspector panel', devToolsPage);
}
async function getScopeNames(devToolsPage) {
    const scopeElements = await devToolsPage.$$('.scope-chain-sidebar-pane-section-title');
    const scopeNames = await Promise.all(scopeElements.map(nodes => nodes.evaluate(n => n.textContent)));
    return scopeNames;
}
async function getValuesForScope(scope, expandCount, waitForNoOfValues, devToolsPage) {
    const scopeSelector = `[aria-label="${scope}"]`;
    await devToolsPage.waitFor(scopeSelector);
    for (let i = 0; i < expandCount; i++) {
        await devToolsPage.click(`${scopeSelector} + ol li[aria-expanded=false]`);
    }
    const valueSelector = `${scopeSelector} + ol .name-and-value`;
    async function readValues() {
        const valueSelectorElements = await devToolsPage.waitForMany(valueSelector, waitForNoOfValues);
        return await Promise.all(valueSelectorElements.map(elem => elem.evaluate(n => n.textContent)));
    }
    let previousValues = await readValues();
    return await devToolsPage.waitForFunction(async function () {
        const values = await readValues();
        if (values.join('') === previousValues.join('')) {
            return values;
        }
        previousValues = values;
        return;
    });
}
async function waitValuesForScope(scope, expandCount, expectedValues, devToolsPage) {
    await devToolsPage.waitForFunction(async () => {
        const values = await getValuesForScope(scope, expandCount, expectedValues.length, devToolsPage);
        return values.every((value, i) => value === expectedValues[i]);
    });
    return expectedValues;
}
async function getPausedMessages(devToolsPage) {
    const messageElement = await devToolsPage.page.waitForSelector('.paused-message');
    chai_1.assert.isOk(messageElement, 'getPausedMessages: did not find .paused-message element.');
    const statusMain = await devToolsPage.waitFor('.status-main', messageElement);
    const statusSub = await devToolsPage.waitFor('.status-sub', messageElement);
    return {
        statusMain: await statusMain.evaluate(x => x.textContent),
        statusSub: await statusSub.evaluate(x => x.textContent),
    };
}
async function getWatchExpressionsValues(devToolsPage) {
    await devToolsPage.waitForFunction(async () => {
        const expandedOption = await devToolsPage.$('.watch-expression-title');
        if (expandedOption) {
            return true;
        }
        await devToolsPage.click('[aria-label="Watch"]');
        // Wait for the click event to settle.
        await devToolsPage.timeout(100);
        return expandedOption !== null;
    });
    await devToolsPage.pressKey('ArrowRight');
    const watchExpressionValue = await devToolsPage.$(WATCH_EXPRESSION_VALUE_SELECTOR);
    if (!watchExpressionValue) {
        return null;
    }
    const values = await devToolsPage.$$(WATCH_EXPRESSION_VALUE_SELECTOR);
    return await Promise.all(values.map(value => value.evaluate(element => element.innerText)));
}
async function runSnippet(devToolsPage) {
    await devToolsPage.pressKey('Enter', { control: true });
}
async function evaluateSelectedTextInConsole(devToolsPage) {
    await devToolsPage.pressKey('E', { control: true, shift: true });
    // TODO: it should actually wait for rendering to finish. Note: it is
    // drained three times because rendering currently takes 3 dependent
    // tasks to finish.
    await devToolsPage.drainTaskQueue();
    await devToolsPage.drainTaskQueue();
    await devToolsPage.drainTaskQueue();
}
async function addSelectedTextToWatches(devToolsPage) {
    await devToolsPage.pressKey('A', { control: true, shift: true });
}
async function enableLocalOverrides(devToolsPage) {
    await openOverridesSubPane(devToolsPage);
    await devToolsPage.click(exports.ENABLE_OVERRIDES_SELECTOR);
    await devToolsPage.waitFor(CLEAR_CONFIGURATION_SELECTOR);
}
class WasmLocationLabels {
    #mappings;
    #source;
    #wasm;
    #devToolsPage;
    #inspectedPage;
    constructor(source, wasm, mappings, devToolsPage, inspectedPage) {
        this.#mappings = mappings;
        this.#source = source;
        this.#wasm = wasm;
        this.#devToolsPage = devToolsPage;
        this.#inspectedPage = inspectedPage;
    }
    static load(source, wasm, devToolsPage, inspectedPage) {
        const mapFileName = path.join(paths_js_1.GEN_DIR, 'test', 'e2e', 'resources', `${wasm}.map.json`);
        const mapFile = JSON.parse(fs.readFileSync(mapFileName, { encoding: 'utf-8' }));
        const sourceFileName = path.join(paths_js_1.GEN_DIR, 'test', 'e2e', 'resources', source);
        const sourceFile = fs.readFileSync(sourceFileName, { encoding: 'utf-8' });
        const labels = new Map();
        for (const [index, line] of sourceFile.split('\n').entries()) {
            if (line.trim().startsWith(';;@')) {
                const label = line.trim().substr(3).trim();
                chai_1.assert.isFalse(labels.has(label), `Label ${label} must be unique`);
                labels.set(label, index + 1);
            }
        }
        const mappings = new Map();
        for (const m of mapFile) {
            const entry = mappings.get(m.source) ?? [];
            if (entry.length === 0) {
                mappings.set(m.source, entry);
            }
            const labelLine = m.originalLine;
            const labelColumn = m.originalColumn;
            const sourceLine = labels.get(`${m.source}:${labelLine}:${labelColumn}`);
            chai_1.assert.isOk(sourceLine);
            entry.push({
                label: m.source,
                moduleOffset: m.generatedColumn,
                bytecode: m.bytecodeOffset,
                sourceLine,
                labelLine,
                labelColumn,
            });
        }
        return new WasmLocationLabels(source, wasm, mappings, devToolsPage, inspectedPage);
    }
    async checkLocationForLabel(label) {
        const pauseLocation = await retrieveTopCallFrameWithoutResuming(this.#devToolsPage);
        const pausedLine = this.#mappings.get(label).find(line => pauseLocation === `${path.basename(this.#wasm)}:0x${line.moduleOffset.toString(16)}` ||
            pauseLocation === `${path.basename(this.#source)}:${line.sourceLine}`);
        chai_1.assert.isOk(pausedLine);
        return pausedLine;
    }
    async addBreakpointsForLabelInSource(label) {
        await openFileInEditor(path.basename(this.#source), this.#devToolsPage);
        await Promise.all(this.#mappings.get(label).map(({ sourceLine }) => addBreakpointForLine(sourceLine, this.#devToolsPage)));
    }
    async addBreakpointsForLabelInWasm(label) {
        await openFileInEditor(path.basename(this.#wasm), this.#devToolsPage);
        const visibleLines = await this.#devToolsPage.$$(exports.CODE_LINE_SELECTOR);
        const lineNumbers = await Promise.all(visibleLines.map(line => line.evaluate(node => node.textContent)));
        const lineNumberLabels = new Map(lineNumbers.map(label => [Number(label), label]));
        await Promise.all(this.#mappings.get(label).map(({ moduleOffset }) => addBreakpointForLine(lineNumberLabels.get(moduleOffset), this.#devToolsPage)));
    }
    async setBreakpointInSourceAndRun(label, script) {
        await this.addBreakpointsForLabelInSource(label);
        void this.#inspectedPage.evaluate(script);
        await this.checkLocationForLabel(label);
    }
    async setBreakpointInWasmAndRun(label, script) {
        await this.addBreakpointsForLabelInWasm(label);
        void this.#inspectedPage.evaluate(script);
        await this.checkLocationForLabel(label);
    }
    async continueAndCheckForLabel(label) {
        await this.#devToolsPage.click(exports.RESUME_BUTTON);
        await this.checkLocationForLabel(label);
    }
    getMappingsForPlugin() {
        return Array.from(this.#mappings.values()).flat();
    }
}
exports.WasmLocationLabels = WasmLocationLabels;
async function retrieveCodeMirrorEditorContent(devToolsPage) {
    const editor = await devToolsPage.waitFor('[aria-label="Code editor"]');
    return await editor.evaluate(node => [...node.querySelectorAll('.cm-line')].map(node => node.textContent || '') || []);
}
async function waitForLines(lineCount, devToolsPage) {
    await devToolsPage.waitFor(new Array(lineCount).fill('.cm-line').join(' ~ '));
}
async function isPrettyPrinted(devToolsPage) {
    const prettyButton = await devToolsPage.waitFor('[title="Pretty print"]');
    const isPretty = await prettyButton.evaluate(e => e.classList.contains('toggled'));
    return isPretty === true;
}
function veImpressionForSourcesPanel() {
    return (0, visual_logging_helpers_js_1.veImpression)('Panel', 'sources', [
        (0, visual_logging_helpers_js_1.veImpression)('Toolbar', 'debug', [
            (0, visual_logging_helpers_js_1.veImpression)('Toggle', 'debugger.toggle-pause'),
            (0, visual_logging_helpers_js_1.veImpression)('Action', 'debugger.step-over'),
            (0, visual_logging_helpers_js_1.veImpression)('Action', 'debugger.step-into'),
            (0, visual_logging_helpers_js_1.veImpression)('Action', 'debugger.step-out'),
            (0, visual_logging_helpers_js_1.veImpression)('Action', 'debugger.step'),
            (0, visual_logging_helpers_js_1.veImpression)('Toggle', 'debugger.toggle-breakpoints-active'),
        ]),
        (0, visual_logging_helpers_js_1.veImpression)('Pane', 'debug', [
            (0, visual_logging_helpers_js_1.veImpression)('SectionHeader', 'sources.watch'),
            (0, visual_logging_helpers_js_1.veImpression)('SectionHeader', 'sources.js-breakpoints'),
            (0, visual_logging_helpers_js_1.veImpression)('SectionHeader', 'sources.scope-chain'),
            (0, visual_logging_helpers_js_1.veImpression)('SectionHeader', 'sources.callstack'),
            (0, visual_logging_helpers_js_1.veImpression)('SectionHeader', 'sources.xhr-breakpoints'),
            (0, visual_logging_helpers_js_1.veImpression)('SectionHeader', 'sources.dom-breakpoints'),
            (0, visual_logging_helpers_js_1.veImpression)('SectionHeader', 'sources.global-listeners'),
            (0, visual_logging_helpers_js_1.veImpression)('SectionHeader', 'sources.event-listener-breakpoints'),
            (0, visual_logging_helpers_js_1.veImpression)('SectionHeader', 'sources.csp-violation-breakpoints'),
            (0, visual_logging_helpers_js_1.veImpression)('Section', 'sources.scope-chain'),
            (0, visual_logging_helpers_js_1.veImpression)('Section', 'sources.callstack'),
            (0, visual_logging_helpers_js_1.veImpression)('Section', 'sources.js-breakpoints', [
                (0, visual_logging_helpers_js_1.veImpression)('Toggle', 'pause-uncaught'),
                (0, visual_logging_helpers_js_1.veImpression)('Toggle', 'pause-on-caught-exception'),
            ]),
        ]),
        (0, visual_logging_helpers_js_1.veImpression)('Pane', 'editor', [
            (0, visual_logging_helpers_js_1.veImpression)('Toolbar', 'bottom'),
            (0, visual_logging_helpers_js_1.veImpression)('Toolbar', 'top', [
                (0, visual_logging_helpers_js_1.veImpression)('ToggleSubpane', 'navigator'),
                (0, visual_logging_helpers_js_1.veImpression)('ToggleSubpane', 'debugger'),
            ]),
        ]),
        (0, visual_logging_helpers_js_1.veImpression)('Toolbar', 'navigator', [
            (0, visual_logging_helpers_js_1.veImpression)('DropDown', 'more-tabs'),
            (0, visual_logging_helpers_js_1.veImpression)('PanelTabHeader', 'navigator-network'),
            (0, visual_logging_helpers_js_1.veImpression)('PanelTabHeader', 'navigator-files'),
            (0, visual_logging_helpers_js_1.veImpression)('DropDown', 'more-options'),
        ]),
        (0, visual_logging_helpers_js_1.veImpression)('Pane', 'navigator-network', [
            (0, visual_logging_helpers_js_1.veImpression)('Tree', undefined, [
                (0, visual_logging_helpers_js_1.veImpression)('TreeItem', 'frame', [
                    (0, visual_logging_helpers_js_1.veImpression)('Expand'),
                    (0, visual_logging_helpers_js_1.veImpression)('TreeItem', 'domain', [
                        (0, visual_logging_helpers_js_1.veImpression)('Expand'),
                        (0, visual_logging_helpers_js_1.veImpression)('TreeItem', 'document', [
                            (0, visual_logging_helpers_js_1.veImpression)('Value', 'title'),
                        ]),
                    ]),
                ]),
            ]),
        ]),
    ]);
}
//# sourceMappingURL=sources-helpers.js.map