From 6bf0a5cb5034a7e684dcc3500e841785237ce2dd Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Sun, 7 Apr 2024 19:32:43 +0200 Subject: Adding upstream version 1:115.7.0. Signed-off-by: Daniel Baumann --- .../browser_ext_devtools_inspectedWindow.js | 540 +++++++++++++++++++++ 1 file changed, 540 insertions(+) create mode 100644 browser/components/extensions/test/browser/browser_ext_devtools_inspectedWindow.js (limited to 'browser/components/extensions/test/browser/browser_ext_devtools_inspectedWindow.js') diff --git a/browser/components/extensions/test/browser/browser_ext_devtools_inspectedWindow.js b/browser/components/extensions/test/browser/browser_ext_devtools_inspectedWindow.js new file mode 100644 index 0000000000..0cfcb33ab3 --- /dev/null +++ b/browser/components/extensions/test/browser/browser_ext_devtools_inspectedWindow.js @@ -0,0 +1,540 @@ +/* -*- Mode: indent-tabs-mode: nil; js-indent-level: 2 -*- */ +/* vim: set sts=2 sw=2 et tw=80: */ +"use strict"; + +loadTestSubscript("head_devtools.js"); + +/** + * Helper that returns the id of the last additional/extension tool for a provided + * toolbox. + * + * @param {object} toolbox + * The DevTools toolbox object. + * @param {string} label + * The expected label for the additional tool. + * @returns {string} the id of the last additional panel. + */ +function getAdditionalPanelId(toolbox, label) { + // Copy the tools array and pop the last element from it. + const panelDef = toolbox.getAdditionalTools().slice().pop(); + is(panelDef.label, label, "Additional panel label is the expected label"); + return panelDef.id; +} + +/** + * Helper that returns the number of existing target actors for the content browserId + * + * @param {Tab} tab + * @returns {Integer} the number of targets + */ +function getTargetActorsCount(tab) { + return SpecialPowers.spawn(tab.linkedBrowser, [], () => { + const { TargetActorRegistry } = ChromeUtils.importESModule( + "resource://devtools/server/actors/targets/target-actor-registry.sys.mjs" + ); + + // Retrieve the target actor instances + return TargetActorRegistry.getTargetActorsCountForBrowserElement( + content.browsingContext.browserId + ); + }); +} + +/** + * this test file ensures that: + * + * - the devtools page gets only a subset of the runtime API namespace. + * - devtools.inspectedWindow.tabId is the same tabId that we can retrieve + * in the background page using the tabs API namespace. + * - devtools API is available in the devtools page sub-frames when a valid + * extension URL has been loaded. + * - devtools.inspectedWindow.eval: + * - returns a serialized version of the evaluation result. + * - returns the expected error object when the return value serialization raises a + * "TypeError: cyclic object value" exception. + * - returns the expected exception when an exception has been raised from the evaluated + * javascript code. + */ +add_task(async function test_devtools_inspectedWindow_tabId() { + let tab = await BrowserTestUtils.openNewForegroundTab( + gBrowser, + "http://mochi.test:8888/" + ); + + async function background() { + browser.test.assertEq( + undefined, + browser.devtools, + "No devtools APIs should be available in the background page" + ); + + const tabs = await browser.tabs.query({ + active: true, + lastFocusedWindow: true, + }); + browser.test.sendMessage("current-tab-id", tabs[0].id); + } + + function devtools_page() { + browser.test.assertEq( + undefined, + browser.runtime.getBackgroundPage, + "The `runtime.getBackgroundPage` API method should be missing in a devtools_page context" + ); + + try { + let tabId = browser.devtools.inspectedWindow.tabId; + browser.test.sendMessage("inspectedWindow-tab-id", tabId); + } catch (err) { + browser.test.sendMessage("inspectedWindow-tab-id", undefined); + throw err; + } + } + + function devtools_page_iframe() { + try { + let tabId = browser.devtools.inspectedWindow.tabId; + browser.test.sendMessage( + "devtools_page_iframe.inspectedWindow-tab-id", + tabId + ); + } catch (err) { + browser.test.fail(`Error: ${err} :: ${err.stack}`); + browser.test.sendMessage( + "devtools_page_iframe.inspectedWindow-tab-id", + undefined + ); + } + } + + let extension = ExtensionTestUtils.loadExtension({ + background, + manifest: { + devtools_page: "devtools_page.html", + }, + files: { + "devtools_page.html": ` + + + + + + + + + `, + "devtools_page.js": devtools_page, + "devtools_page_iframe.html": ` + + + + + + + + `, + "devtools_page_iframe.js": devtools_page_iframe, + }, + }); + + await extension.startup(); + + let backgroundPageCurrentTabId = await extension.awaitMessage( + "current-tab-id" + ); + + await openToolboxForTab(tab); + + let devtoolsInspectedWindowTabId = await extension.awaitMessage( + "inspectedWindow-tab-id" + ); + + is( + devtoolsInspectedWindowTabId, + backgroundPageCurrentTabId, + "Got the expected tabId from devtool.inspectedWindow.tabId" + ); + + let devtoolsPageIframeTabId = await extension.awaitMessage( + "devtools_page_iframe.inspectedWindow-tab-id" + ); + + is( + devtoolsPageIframeTabId, + backgroundPageCurrentTabId, + "Got the expected tabId from devtool.inspectedWindow.tabId called in a devtool_page iframe" + ); + + await closeToolboxForTab(tab); + + await extension.unload(); + + BrowserTestUtils.removeTab(tab); +}); + +add_task(async function test_devtools_inspectedWindow_eval() { + const TEST_TARGET_URL = "http://mochi.test:8888/"; + let tab = await BrowserTestUtils.openNewForegroundTab( + gBrowser, + TEST_TARGET_URL + ); + + function devtools_page() { + browser.test.onMessage.addListener(async (msg, ...args) => { + if (msg !== "inspectedWindow-eval-request") { + browser.test.fail(`Unexpected test message received: ${msg}`); + return; + } + + try { + const [evalResult, errorResult] = + await browser.devtools.inspectedWindow.eval(...args); + browser.test.sendMessage("inspectedWindow-eval-result", { + evalResult, + errorResult, + }); + } catch (err) { + browser.test.sendMessage("inspectedWindow-eval-result"); + browser.test.fail(`Error: ${err} :: ${err.stack}`); + } + }); + browser.test.sendMessage("devtools-page-loaded"); + } + + let extension = ExtensionTestUtils.loadExtension({ + manifest: { + devtools_page: "devtools_page.html", + }, + files: { + "devtools_page.html": ` + + + + + + + + `, + "devtools_page.js": devtools_page, + }, + }); + + await extension.startup(); + + await openToolboxForTab(tab); + + info("Wait the devtools page load"); + await extension.awaitMessage("devtools-page-loaded"); + + const evalTestCases = [ + // Successful evaluation results. + { + args: ["window.location.href"], + expectedResults: { evalResult: TEST_TARGET_URL, errorResult: undefined }, + }, + + // Error evaluation results. + { + args: ["window"], + expectedResults: { + evalResult: undefined, + errorResult: { + isError: true, + code: "E_PROTOCOLERROR", + description: "Inspector protocol error: %s", + details: ["TypeError: cyclic object value"], + }, + }, + }, + + // Exception evaluation results. + { + args: ["throw new Error('fake eval exception');"], + expectedResults: { + evalResult: undefined, + errorResult: { + isException: true, + value: /Error: fake eval exception\n.*moz-extension:\/\//, + }, + }, + }, + ]; + + for (let testCase of evalTestCases) { + info(`test inspectedWindow.eval with ${JSON.stringify(testCase)}`); + + const { args, expectedResults } = testCase; + + extension.sendMessage(`inspectedWindow-eval-request`, ...args); + + const { evalResult, errorResult } = await extension.awaitMessage( + `inspectedWindow-eval-result` + ); + + Assert.deepEqual( + evalResult, + expectedResults.evalResult, + "Got the expected eval result" + ); + + if (errorResult) { + for (const errorPropName of Object.keys(expectedResults.errorResult)) { + const expected = expectedResults.errorResult[errorPropName]; + const actual = errorResult[errorPropName]; + + if (expected instanceof RegExp) { + ok( + expected.test(actual), + `Got exceptionInfo.${errorPropName} value ${actual} matches ${expected}` + ); + } else { + Assert.deepEqual( + actual, + expected, + `Got the expected exceptionInfo.${errorPropName} value` + ); + } + } + } + } + + await closeToolboxForTab(tab); + + await extension.unload(); + + BrowserTestUtils.removeTab(tab); +}); + +/** + * This test asserts that both the page and the panel can use devtools.inspectedWindow. + * See regression in Bug https://bugzilla.mozilla.org/show_bug.cgi?id=1392531 + */ +add_task(async function test_devtools_inspectedWindow_eval_in_page_and_panel() { + const TEST_TARGET_URL = "http://mochi.test:8888/"; + let tab = await BrowserTestUtils.openNewForegroundTab( + gBrowser, + TEST_TARGET_URL + ); + + async function devtools_page() { + await browser.devtools.panels.create( + "test-eval", + "fake-icon.png", + "devtools_panel.html" + ); + + browser.test.onMessage.addListener(async (msg, ...args) => { + switch (msg) { + case "inspectedWindow-page-eval-request": { + const [evalResult, errorResult] = + await browser.devtools.inspectedWindow.eval(...args); + browser.test.sendMessage("inspectedWindow-page-eval-result", { + evalResult, + errorResult, + }); + break; + } + case "inspectedWindow-panel-eval-request": + // Ignore the test message expected by the devtools panel. + break; + default: + browser.test.fail(`Unexpected test message received: ${msg}`); + } + }); + + browser.test.sendMessage("devtools_panel_created"); + } + + function devtools_panel() { + browser.test.onMessage.addListener(async (msg, ...args) => { + switch (msg) { + case "inspectedWindow-panel-eval-request": { + const [evalResult, errorResult] = + await browser.devtools.inspectedWindow.eval(...args); + browser.test.sendMessage("inspectedWindow-panel-eval-result", { + evalResult, + errorResult, + }); + break; + } + case "inspectedWindow-page-eval-request": + // Ignore the test message expected by the devtools page. + break; + default: + browser.test.fail(`Unexpected test message received: ${msg}`); + } + }); + browser.test.sendMessage("devtools_panel_initialized"); + } + + let extension = ExtensionTestUtils.loadExtension({ + manifest: { + devtools_page: "devtools_page.html", + }, + files: { + "devtools_page.html": ` + + + + + + + + `, + "devtools_page.js": devtools_page, + "devtools_panel.html": ` + + + + + + DEVTOOLS PANEL + + + `, + "devtools_panel.js": devtools_panel, + }, + }); + + await extension.startup(); + + const toolbox = await openToolboxForTab(tab); + + info("Wait for devtools_panel_created event"); + await extension.awaitMessage("devtools_panel_created"); + + info("Switch to the extension test panel"); + await openToolboxForTab(tab, getAdditionalPanelId(toolbox, "test-eval")); + + info("Wait for devtools_panel_initialized event"); + await extension.awaitMessage("devtools_panel_initialized"); + + info( + `test inspectedWindow.eval with eval(window.location.href) from the devtools page` + ); + extension.sendMessage( + `inspectedWindow-page-eval-request`, + "window.location.href" + ); + + info("Wait for response from the page"); + let { evalResult } = await extension.awaitMessage( + `inspectedWindow-page-eval-result` + ); + Assert.deepEqual( + evalResult, + TEST_TARGET_URL, + "Got the expected eval result in the page" + ); + + info( + `test inspectedWindow.eval with eval(window.location.href) from the devtools panel` + ); + extension.sendMessage( + `inspectedWindow-panel-eval-request`, + "window.location.href" + ); + + info("Wait for response from the panel"); + ({ evalResult } = await extension.awaitMessage( + `inspectedWindow-panel-eval-result` + )); + Assert.deepEqual( + evalResult, + TEST_TARGET_URL, + "Got the expected eval result in the panel" + ); + + // Cleanup + await closeToolboxForTab(tab); + await extension.unload(); + BrowserTestUtils.removeTab(tab); +}); + +/** + * This test asserts that there's only one target created by the extension, and that + * closing the DevTools toolbox destroys it. + * See Bug 1652016 + */ +add_task(async function test_devtools_inspectedWindow_eval_target_lifecycle() { + const TEST_TARGET_URL = "http://mochi.test:8888/"; + let tab = await BrowserTestUtils.openNewForegroundTab( + gBrowser, + TEST_TARGET_URL + ); + + function devtools_page() { + browser.test.onMessage.addListener(async (msg, ...args) => { + if (msg !== "inspectedWindow-eval-requests") { + browser.test.fail(`Unexpected test message received: ${msg}`); + return; + } + + const promises = []; + for (let i = 0; i < 10; i++) { + promises.push(browser.devtools.inspectedWindow.eval(`${i * 2}`)); + } + + await Promise.all(promises); + browser.test.sendMessage("inspectedWindow-eval-requests-done"); + }); + browser.test.sendMessage("devtools-page-loaded"); + } + + let extension = ExtensionTestUtils.loadExtension({ + manifest: { + devtools_page: "devtools_page.html", + }, + files: { + "devtools_page.html": ` + + + + + + + + `, + "devtools_page.js": devtools_page, + }, + }); + + await extension.startup(); + + await openToolboxForTab(tab); + await extension.awaitMessage("devtools-page-loaded"); + + let targetsCount = await getTargetActorsCount(tab); + is( + targetsCount, + 1, + "There's only one target for the content page, the one for DevTools Toolbox" + ); + + info("Check that evaluating multiple times doesn't create multiple targets"); + const onEvalRequestsDone = extension.awaitMessage( + `inspectedWindow-eval-requests-done` + ); + extension.sendMessage(`inspectedWindow-eval-requests`); + + info("Wait for response from the panel"); + await onEvalRequestsDone; + + targetsCount = await getTargetActorsCount(tab); + is( + targetsCount, + 2, + "Only 1 additional target was created when calling inspectedWindow.eval" + ); + + info( + "Close the toolbox and make sure the extension gets unloaded, and the target destroyed" + ); + await closeToolboxForTab(tab); + + targetsCount = await getTargetActorsCount(tab); + is(targetsCount, 0, "All targets were removed as toolbox was closed"); + + await extension.unload(); + BrowserTestUtils.removeTab(tab); +}); -- cgit v1.2.3