Source code

Revision control

Copy as Markdown

Other Tools

/* -*- Mode: indent-tabs-mode: nil; js-indent-level: 2 -*- */
/* vim: set sts=2 sw=2 et tw=80: */
"use strict";
/* exported
assertDevToolsExtensionEnabled,
closeToolboxForTab,
navigateToWithDevToolsOpen
openToolboxForTab,
registerBlankToolboxPanel,
TOOLBOX_BLANK_PANEL_ID,
*/
ChromeUtils.defineESModuleGetters(this, {
});
ChromeUtils.defineLazyGetter(this, "gDevTools", () => {
const { gDevTools } = loader.require("devtools/client/framework/devtools");
return gDevTools;
});
const TOOLBOX_BLANK_PANEL_ID = "testBlankPanel";
// Register a blank custom tool so that we don't need to wait the webconsole
// to be fully loaded/unloaded to prevent intermittent failures (related
// to a webconsole that is still loading when the test has been completed).
async function registerBlankToolboxPanel() {
const testBlankPanel = {
id: TOOLBOX_BLANK_PANEL_ID,
url: "about:blank",
label: "Blank Tool",
isToolSupported() {
return true;
},
build(iframeWindow, toolbox) {
return Promise.resolve({
target: toolbox.target,
toolbox: toolbox,
isReady: true,
panelDoc: iframeWindow.document,
destroy() {},
});
},
};
registerCleanupFunction(() => {
gDevTools.unregisterTool(testBlankPanel.id);
});
gDevTools.registerTool(testBlankPanel);
}
async function openToolboxForTab(tab, panelId = TOOLBOX_BLANK_PANEL_ID) {
if (
panelId == TOOLBOX_BLANK_PANEL_ID &&
!gDevTools.getToolDefinition(panelId)
) {
info(`Registering ${TOOLBOX_BLANK_PANEL_ID} tool to the developer tools`);
registerBlankToolboxPanel();
}
const toolbox = await gDevTools.showToolboxForTab(tab, { toolId: panelId });
const { url, outerWindowID } = toolbox.target.form;
info(
`Developer toolbox opened on panel "${panelId}" for target ${JSON.stringify(
{ url, outerWindowID }
)}`
);
return toolbox;
}
async function closeToolboxForTab(tab) {
await gDevTools.closeToolboxForTab(tab);
const tabUrl = tab.linkedBrowser.currentURI.spec;
info(`Developer toolbox closed for tab "${tabUrl}"`);
}
function assertDevToolsExtensionEnabled(uuid, enabled) {
for (let toolbox of DevToolsShim.getToolboxes()) {
is(
enabled,
!!toolbox.isWebExtensionEnabled(uuid),
`extension is ${enabled ? "enabled" : "disabled"} on toolbox`
);
}
}
/**
* Navigate the currently selected tab to a new URL and wait for it to load.
* Also wait for the toolbox to attach to the new target, if we navigated
* to a new process.
*
* @param {object} tab The tab to redirect.
* @param {string} uri The url to be loaded in the current tab.
* @param {boolean} isErrorPage You may pass `true` is the URL is an error
* page. Otherwise BrowserTestUtils.browserLoaded will wait
* for 'load' event, which never fires for error pages.
*
* @returns {Promise} A promise that resolves when the page has fully loaded.
*/
async function navigateToWithDevToolsOpen(tab, uri, isErrorPage = false) {
const toolbox = gDevTools.getToolboxForTab(tab);
const target = toolbox.target;
// If we're switching origins, we need to wait for the 'switched-target'
// event to make sure everything is ready.
// Navigating from/to pages loaded in the parent process, like about:robots,
// also spawn new targets.
// (If target switching is disabled, the toolbox will reboot)
const onTargetSwitched =
toolbox.commands.targetCommand.once("switched-target");
// Otherwise, if we don't switch target, it is safe to wait for navigate event.
const onNavigate = target.once("navigate");
// If the current top-level target follows the window global lifecycle, a
// target switch will occur regardless of process changes.
const targetFollowsWindowLifecycle =
target.targetForm.followWindowGlobalLifeCycle;
info(`Load document "${uri}"`);
const browser = gBrowser.selectedBrowser;
const currentPID = browser.browsingContext.currentWindowGlobal.osPid;
const currentBrowsingContextID = browser.browsingContext.id;
const onBrowserLoaded = BrowserTestUtils.browserLoaded(
browser,
false,
null,
isErrorPage
);
BrowserTestUtils.startLoadingURIString(browser, uri);
info(`Waiting for page to be loaded…`);
await onBrowserLoaded;
info(`→ page loaded`);
// Compare the PIDs (and not the toolbox's targets) as PIDs are updated also immediately,
// while target may be updated slightly later.
const switchedToAnotherProcess =
currentPID !== browser.browsingContext.currentWindowGlobal.osPid;
const switchedToAnotherBrowsingContext =
currentBrowsingContextID !== browser.browsingContext.id;
// If:
// - the tab navigated to another process, or,
// - the tab navigated to another browsing context, or,
// - if the old target follows the window lifecycle
// then, expect a target switching.
if (
switchedToAnotherProcess ||
targetFollowsWindowLifecycle ||
switchedToAnotherBrowsingContext
) {
info(`Waiting for target switch…`);
await onTargetSwitched;
info(`→ switched-target emitted`);
} else {
info(`Waiting for target 'navigate' event…`);
await onNavigate;
info(`→ 'navigate' emitted`);
}
}