"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.
Object.defineProperty(exports, "__esModule", { value: true });
const chai_1 = require("chai");
const events_js_1 = require("../../conductor/events.js");
const application_helpers_js_1 = require("../helpers/application-helpers.js");
const settings_helpers_js_1 = require("../helpers/settings-helpers.js");
const OPENED_WINDOWS_SELECTOR = '[aria-label="Opened Windows"]';
const EXPAND_STACKTRACE_BUTTON_SELECTOR = '.arrow-icon-button';
const STACKTRACE_ROW_SELECTOR = '.stack-preview-container tbody tr';
const APPLICATION_PANEL_SELECTED_SELECTOR = '.tabbed-pane-header-tab.selected[aria-label="Application"]';
const getTrailingURL = (text) => {
    const match = text.match(/http.*$/);
    return match ? match[0] : '';
};
const ensureApplicationPanel = async (devToolsPage) => {
    if ((await devToolsPage.$$(APPLICATION_PANEL_SELECTED_SELECTOR)).length === 0) {
        await devToolsPage.waitForFunction(async () => {
            await devToolsPage.click('#tab-resources');
            return (await devToolsPage.$$(APPLICATION_PANEL_SELECTED_SELECTOR)).length === 1;
        });
    }
};
const getFieldValuesTextContent = async (devToolsPage) => {
    const fieldValues = await Promise.all((await devToolsPage.$$('devtools-report-value')).map(element => element.evaluate(e => e.deepInnerText())));
    if (fieldValues[0]) {
        // This contains some CSS from the svg icon link being rendered. It's
        // system-specific, so we get rid of it and only look at the (URL) text.
        fieldValues[0] = getTrailingURL(fieldValues[0]);
    }
    if (fieldValues[10]?.includes('accelerometer')) {
        fieldValues[10] = 'accelerometer';
    }
    if (fieldValues[11]) {
        // This is the frame's id, which is not deterministic.
        fieldValues[11] = 'frameId';
    }
    // Make sure the length is equivalent to the expected value below
    if (fieldValues.length === 12) {
        return fieldValues;
    }
    return undefined;
};
describe('The Application Tab', () => {
    setup({ dockingMode: 'undocked', disabledDevToolsExperiments: ['protocol-monitor'] });
    it('shows details for a frame when clicked on in the frame tree', async ({ devToolsPage, inspectedPage }) => {
        (0, events_js_1.expectError)('Request Network.enableDeviceBoundSessions failed. {"code":-32603,"message":"Internal error"}');
        await (0, application_helpers_js_1.navigateToApplicationTab)('frame-tree', devToolsPage, inspectedPage);
        await devToolsPage.click('#tab-resources');
        await (0, application_helpers_js_1.navigateToFrame)('top', devToolsPage);
        const fieldValuesTextContent = await devToolsPage.waitForFunction(() => getFieldValuesTextContent(devToolsPage));
        const expected = [
            `${inspectedPage.getResourcesPath()}/application/frame-tree.html`,
            `https://localhost:${inspectedPage.serverPort}`,
            '#document',
            'Yes\nLocalhost is always a secure context',
            'No',
            'none',
            'unsafe-none',
            'None',
            'unavailable\nrequires cross-origin isolated context',
            'unavailable\nLearn more',
            'accelerometer',
            'frameId',
        ];
        chai_1.assert.deepEqual(fieldValuesTextContent, expected);
    });
    it('shows stack traces for OOPIF', async ({ devToolsPage, inspectedPage }) => {
        (0, events_js_1.expectError)('Request CacheStorage.requestCacheNames failed. {"code":-32602,"message":"Invalid security origin"}');
        await (0, application_helpers_js_1.navigateToApplicationTab)('js-oopif', devToolsPage, inspectedPage);
        await devToolsPage.waitForFunction(async () => {
            await (0, application_helpers_js_1.navigateToFrame)('top', devToolsPage);
            await (0, application_helpers_js_1.navigateToFrame)('iframe.html', devToolsPage);
            return (await devToolsPage.$$(EXPAND_STACKTRACE_BUTTON_SELECTOR)).length === 1;
        });
        const stackTraceRowsTextContent = await devToolsPage.waitForFunction(async () => {
            await ensureApplicationPanel(devToolsPage);
            await devToolsPage.click(EXPAND_STACKTRACE_BUTTON_SELECTOR);
            const stackTraceRows = await (0, application_helpers_js_1.getTrimmedTextContent)(STACKTRACE_ROW_SELECTOR, devToolsPage);
            // Make sure the length is equivalent to the expected value below
            if (stackTraceRows.length === 3) {
                return stackTraceRows;
            }
            return undefined;
        });
        const expected = [
            'second @ js-oopif.html:13',
            'first @ js-oopif.js:3',
            '(anonymous) @ js-oopif.js:6',
        ];
        chai_1.assert.deepEqual(stackTraceRowsTextContent, expected);
    });
    it('stack traces for OOPIF with ignore listed frames can be expanded and collapsed', async ({ devToolsPage, inspectedPage }) => {
        (0, events_js_1.expectError)('Request CacheStorage.requestCacheNames failed. {"code":-32602,"message":"Invalid security origin"}');
        await (0, settings_helpers_js_1.setIgnoreListPattern)('js-oopif.js', devToolsPage);
        await (0, application_helpers_js_1.navigateToApplicationTab)('js-oopif', devToolsPage, inspectedPage);
        await devToolsPage.waitForFunction(async () => {
            await (0, application_helpers_js_1.navigateToFrame)('top', devToolsPage);
            await (0, application_helpers_js_1.navigateToFrame)('iframe.html', devToolsPage);
            return (await devToolsPage.$$(EXPAND_STACKTRACE_BUTTON_SELECTOR)).length === 1;
        });
        let stackTraceRowsTextContent = await devToolsPage.waitForFunction(async () => {
            await ensureApplicationPanel(devToolsPage);
            await devToolsPage.click(EXPAND_STACKTRACE_BUTTON_SELECTOR);
            const stackTraceRows = (await Promise.all((await devToolsPage.$$(STACKTRACE_ROW_SELECTOR))
                .map(row => row.evaluate(row => row.checkVisibility() && row.textContent.trim()))))
                .filter(Boolean);
            // Make sure the length is equivalent to the expected value below
            if (stackTraceRows.length === 1) {
                return stackTraceRows;
            }
            return undefined;
        });
        const expectedCollapsed = [
            'second @ js-oopif.html:13',
        ];
        chai_1.assert.deepEqual(stackTraceRowsTextContent, expectedCollapsed);
        // Expand all frames
        await devToolsPage.click('.show-all-link .link');
        stackTraceRowsTextContent = await devToolsPage.waitForFunction(async () => {
            const stackTraceRows = (await Promise.all((await devToolsPage.$$(STACKTRACE_ROW_SELECTOR))
                .map(row => row.evaluate(row => row.checkVisibility() && row.textContent.trim()))))
                .filter(Boolean);
            // Make sure the length is equivalent to the expected value below
            if (stackTraceRows.length === 3) {
                return stackTraceRows;
            }
            return undefined;
        });
        const expectedFull = [
            'second @ js-oopif.html:13',
            'first @ js-oopif.js:3',
            '(anonymous) @ js-oopif.js:6',
        ];
        chai_1.assert.deepEqual(stackTraceRowsTextContent, expectedFull);
        await devToolsPage.click('.show-less-link .link');
        stackTraceRowsTextContent = await devToolsPage.waitForFunction(async () => {
            const stackTraceRows = (await Promise.all((await devToolsPage.$$(STACKTRACE_ROW_SELECTOR))
                .map(row => row.evaluate(row => row.checkVisibility() && row.textContent.trim()))))
                .filter(Boolean);
            // Make sure the length is equivalent to the expected value below
            if (stackTraceRows.length === 1) {
                return stackTraceRows;
            }
            return undefined;
        });
        chai_1.assert.deepEqual(stackTraceRowsTextContent, expectedCollapsed);
    });
    it('shows details for opened windows in the frame tree', async ({ devToolsPage, inspectedPage }) => {
        (0, events_js_1.expectError)('Request Network.enableDeviceBoundSessions failed. {"code":-32603,"message":"Internal error"}');
        await (0, application_helpers_js_1.navigateToApplicationTab)('frame-tree', devToolsPage, inspectedPage);
        await devToolsPage.click('#tab-resources');
        await (0, application_helpers_js_1.navigateToFrame)('top', devToolsPage);
        await inspectedPage.evaluate(() => {
            window.iFrameWindow = window.open('iframe.html');
        });
        // window.open above would put DevTools in the background stopping updates
        // to the application panel.
        await devToolsPage.bringToFront();
        await (0, application_helpers_js_1.navigateToOpenedWindows)(devToolsPage);
        await devToolsPage.waitFor(`${OPENED_WINDOWS_SELECTOR} + ol li:first-child`);
        void devToolsPage.pressKey('ArrowDown');
        const fieldValuesTextContent = await devToolsPage.waitForFunction(async () => {
            const fieldValues = await (0, application_helpers_js_1.getTrimmedTextContent)('.report-field-value', devToolsPage);
            // Make sure the length is equivalent to the expected value below
            if (fieldValues.length === 3 && !fieldValues.includes('')) {
                return fieldValues;
            }
            return undefined;
        });
        const expected = [
            `${inspectedPage.getResourcesPath()}/application/iframe.html`,
            '<#document>',
            'Yes',
        ];
        chai_1.assert.deepEqual(fieldValuesTextContent, expected);
        await inspectedPage.evaluate(() => {
            window.iFrameWindow?.close();
        });
    });
    it('shows dedicated workers in the frame tree', async ({ devToolsPage, inspectedPage }) => {
        (0, events_js_1.expectError)('Request CacheStorage.requestCacheNames failed. {"code":-32602,"message":"Invalid security origin"}');
        (0, events_js_1.expectError)('Request Network.enableDeviceBoundSessions failed. {"code":-32603,"message":"Internal error"}');
        (0, events_js_1.expectError)('Request Network.enableDeviceBoundSessions failed. {"code":-32603,"message":"Internal error"}');
        await (0, application_helpers_js_1.navigateToApplicationTab)('frame-tree', devToolsPage, inspectedPage);
        await (0, application_helpers_js_1.navigateToFrame)('top', devToolsPage);
        // DevTools is not ready yet when the worker is being initially attached.
        // We therefore need to reload the page to see the worker in DevTools.
        await inspectedPage.reload();
        await (0, application_helpers_js_1.navigateToWebWorkers)(devToolsPage);
        void devToolsPage.pressKey('ArrowDown');
        const fieldValuesTextContent = await devToolsPage.waitForFunction(async () => {
            const fieldValues = await (0, application_helpers_js_1.getTrimmedTextContent)('.report-field-value', devToolsPage);
            // Make sure the length is equivalent to the expected value below
            if (fieldValues.length === 3 && fieldValues.every(field => field.trim() !== '')) {
                return fieldValues;
            }
            return undefined;
        });
        const expected = [
            `${inspectedPage.getResourcesPath()}/application/dedicated-worker.js`,
            'Web Worker',
            'None',
        ];
        chai_1.assert.deepEqual(fieldValuesTextContent, expected);
    });
    it('shows service workers in the frame tree', async ({ devToolsPage, inspectedPage }) => {
        (0, events_js_1.expectError)('Request CacheStorage.requestCacheNames failed. {"code":-32602,"message":"Invalid security origin"}');
        (0, events_js_1.expectError)('Request Network.enableDeviceBoundSessions failed. {"code":-32603,"message":"Internal error"}');
        await (0, application_helpers_js_1.navigateToApplicationTab)('service-worker-network', devToolsPage, inspectedPage);
        await (0, application_helpers_js_1.navigateToFrameServiceWorkers)('top', devToolsPage);
        void devToolsPage.pressKey('ArrowDown');
        const fieldValuesTextContent = await devToolsPage.waitForFunction(async () => {
            const fieldValues = await (0, application_helpers_js_1.getTrimmedTextContent)('.report-field-value', devToolsPage);
            // Make sure the length is equivalent to the expected value below
            if (fieldValues.length === 3 && fieldValues.every(field => field.trim() !== '')) {
                return fieldValues;
            }
            return undefined;
        });
        const expected = [
            `${inspectedPage.getResourcesPath()}/application/service-worker.js`,
            'Service Worker',
            'None',
        ];
        chai_1.assert.deepEqual(fieldValuesTextContent, expected);
        // Unregister service worker to prevent leftovers from causing test errors.
        void devToolsPage.pressKey('ArrowUp');
        void devToolsPage.pressKey('ArrowLeft');
        await (0, application_helpers_js_1.unregisterServiceWorker)(devToolsPage);
    });
    it('can handle when JS writes to frame', async ({ devToolsPage, inspectedPage }) => {
        (0, events_js_1.expectError)('Request CacheStorage.requestCacheNames failed. {"code":-32602,"message":"Invalid security origin"}');
        await inspectedPage.goToResource('application/main-frame.html');
        await devToolsPage.click('#tab-resources');
        await (0, application_helpers_js_1.navigateToFrame)('top', devToolsPage);
        await (0, application_helpers_js_1.navigateToFrame)('frameId (iframe.html)', devToolsPage);
        // check iframe's URL after pageload
        const fieldValuesTextContent = await devToolsPage.waitForFunction(() => getFieldValuesTextContent(devToolsPage));
        const expected = [
            `${inspectedPage.getResourcesPath()}/application/iframe.html`,
            `https://localhost:${inspectedPage.serverPort}`,
            'iframe#frameId',
            'Yes\nLocalhost is always a secure context',
            'No',
            'none',
            'unsafe-none',
            'None',
            'unavailable\nrequires cross-origin isolated context',
            'unavailable\nLearn more',
            'accelerometer',
            'frameId',
        ];
        chai_1.assert.deepEqual(fieldValuesTextContent, expected);
        chai_1.assert.includeMembers(['top', 'frameId (iframe.html)', 'iframe.html', 'Other', 'favicon.ico', 'main-frame.html'], await (0, application_helpers_js_1.getFrameTreeTitles)(devToolsPage));
        // write to the iframe using 'document.write()'
        await inspectedPage.evaluate(() => {
            const frame = document.getElementById('frameId');
            const doc = frame.contentDocument;
            if (doc) {
                doc.open();
                doc.write('<h1>Hello world !</h1>');
                doc.close();
            }
        });
        // check that iframe's URL has changed
        await (0, application_helpers_js_1.navigateToFrame)('frameId (main-frame.html)', devToolsPage);
        const fieldValuesTextContent2 = await devToolsPage.waitForFunction(() => getFieldValuesTextContent(devToolsPage));
        const expected2 = [
            `${inspectedPage.getResourcesPath()}/application/main-frame.html`,
            `https://localhost:${inspectedPage.serverPort}`,
            'iframe#frameId',
            'Yes\nLocalhost is always a secure context',
            'No',
            'none',
            'unsafe-none',
            'None',
            'unavailable\nrequires cross-origin isolated context',
            'unavailable\nLearn more',
            'accelerometer',
            'frameId',
        ];
        chai_1.assert.deepEqual(fieldValuesTextContent2, expected2);
        chai_1.assert.includeMembers(['top', 'frameId (main-frame.html)', 'No document detected', 'Other', 'favicon.ico', 'main-frame.html'], await (0, application_helpers_js_1.getFrameTreeTitles)(devToolsPage));
    });
});
//# sourceMappingURL=frame-tree.test.js.map