/* Any copyright is dedicated to the Public Domain. http://creativecommons.org/publicdomain/zero/1.0/ */ "use strict"; /* import-globals-from helper-addons.js */ Services.scriptloader.loadSubScript(CHROME_URL_ROOT + "helper-addons.js", this); // There are shutdown issues for which multiple rejections are left uncaught. // See bug 1018184 for resolving these issues. const { PromiseTestUtils } = ChromeUtils.importESModule( "resource://testing-common/PromiseTestUtils.sys.mjs" ); PromiseTestUtils.allowMatchingRejectionsGlobally(/File closed/); // Avoid test timeouts that can occur while waiting for the "addon-console-works" message. requestLongerTimeout(2); const ADDON_ID = "test-devtools-webextension@mozilla.org"; const ADDON_NAME = "test-devtools-webextension"; /** * This test file ensures that the webextension addon developer toolbox: * - has a frame list menu and the noautohide toolbar toggle button, and they * can be used to switch the current target to the extension popup page. */ add_task(async function testWebExtensionsToolboxWebConsole() { await enableExtensionDebugging(); // Bug 1686922: Disable the error count button to avoid intermittent failures. await pushPref("devtools.command-button-errorcount.enabled", false); is( Services.prefs.getBoolPref("ui.popup.disable_autohide"), false, "disable_autohide should be initially false" ); const { document, tab, window: aboutDebuggingWindow, } = await openAboutDebugging(); await selectThisFirefoxPage( document, aboutDebuggingWindow.AboutDebugging.store ); const extension = await installTestAddon(document); const onBackgroundFunctionCalled = waitForExtensionTestMessage( extension, "onBackgroundFunctionCalled" ); const onPopupPageFunctionCalled = waitForExtensionTestMessage( extension, "onPopupPageFunctionCalled" ); info("Open a toolbox to debug the addon"); const { devtoolsWindow } = await openAboutDevtoolsToolbox( document, tab, aboutDebuggingWindow, ADDON_NAME ); const toolbox = getToolbox(devtoolsWindow); const webconsole = await toolbox.selectTool("webconsole"); info("Clicking the menu button to disable autohide"); await disablePopupAutohide(toolbox); info("Check that console messages are evaluated in the background context"); const consoleWrapper = webconsole.hud.ui.wrapper; consoleWrapper.dispatchEvaluateExpression("backgroundFunction()"); await onBackgroundFunctionCalled; clickOnAddonWidget(ADDON_ID); info("Wait until the frames list button is displayed"); const btn = await waitFor(() => toolbox.doc.getElementById("command-button-frames") ); info("Clicking the frame list button"); btn.click(); const menuList = toolbox.doc.getElementById("toolbox-frame-menu"); const frames = Array.from(menuList.querySelectorAll(".command")); is(frames.length, 2, "Has the expected number of frames"); const popupFrameBtn = frames .filter(frame => { return frame.querySelector(".label").textContent.endsWith("popup.html"); }) .pop(); ok(popupFrameBtn, "Extension Popup frame found in the listed frames"); info( "Click on the extension popup frame and wait for a `dom-complete` resource" ); const { onDomCompleteResource } = await waitForNextTopLevelDomCompleteResource(toolbox.commands); popupFrameBtn.click(); await onDomCompleteResource; info("Execute `popupPageFunction()`"); consoleWrapper.dispatchEvaluateExpression("popupPageFunction()"); const args = await onPopupPageFunctionCalled; ok(true, "Received console message from the popup page function as expected"); is(args[0], "onPopupPageFunctionCalled", "Got the expected console message"); is( args[1] && args[1].name, ADDON_NAME, "Got the expected manifest from WebExtension API" ); await closeWebExtAboutDevtoolsToolbox(devtoolsWindow, aboutDebuggingWindow); is( Services.prefs.getBoolPref("ui.popup.disable_autohide"), false, "disable_autohide should be reset to false when the toolbox is closed" ); await removeTemporaryExtension(ADDON_NAME, document); await removeTab(tab); }); /** * Helper to wait for a specific message on an Extension instance. */ function waitForExtensionTestMessage(extension, expectedMessage) { return new Promise(done => { extension.on("test-message", function testLogListener(evt, ...args) { const [message] = args; if (message !== expectedMessage) { return; } extension.off("test-message", testLogListener); done(args); }); }); } /** * Install the addon used for this test. * Returns a Promise that resolve the Extension instance that was just * installed. */ async function installTestAddon(doc) { // Start watching for the extension on the Extension Management before we // install it. const onExtensionReady = waitForExtension(ADDON_NAME); // Install the extension. await installTemporaryExtensionFromXPI( { background() { const { browser } = this; window.backgroundFunction = function () { browser.test.sendMessage("onBackgroundFunctionCalled"); }; }, extraProperties: { browser_action: { default_title: "WebExtension Popup Debugging", default_popup: "popup.html", }, }, files: { "popup.html": ` Background Page Body Test Content `, "popup.js": function () { const { browser } = this; window.popupPageFunction = function () { browser.test.sendMessage( "onPopupPageFunctionCalled", browser.runtime.getManifest() ); }; }, }, id: ADDON_ID, name: ADDON_NAME, }, doc ); // The onExtensionReady promise will resolve the extension instance. return onExtensionReady; } /** * Helper to retrieve the Extension instance. */ async function waitForExtension(addonName) { const { Management } = ChromeUtils.importESModule( "resource://gre/modules/Extension.sys.mjs" ); return new Promise(resolve => { Management.on("startup", function listener(event, extension) { if (extension.name != addonName) { return; } Management.off("startup", listener); resolve(extension); }); }); } /** * Disables the popup autohide feature, which is mandatory to debug webextension * popups. */ function disablePopupAutohide(toolbox) { return new Promise(resolve => { toolbox.doc.addEventListener( "popupshown", () => { const menuItem = toolbox.doc.getElementById( "toolbox-meatball-menu-noautohide" ); menuItem.click(); resolve(); }, { once: true } ); toolbox.doc.getElementById("toolbox-meatball-menu-button").click(); }); }