summaryrefslogtreecommitdiffstats
path: root/browser/components/extensions/test/browser/head_devtools.js
diff options
context:
space:
mode:
Diffstat (limited to 'browser/components/extensions/test/browser/head_devtools.js')
-rw-r--r--browser/components/extensions/test/browser/head_devtools.js162
1 files changed, 162 insertions, 0 deletions
diff --git a/browser/components/extensions/test/browser/head_devtools.js b/browser/components/extensions/test/browser/head_devtools.js
new file mode 100644
index 0000000000..f46254d435
--- /dev/null
+++ b/browser/components/extensions/test/browser/head_devtools.js
@@ -0,0 +1,162 @@
+/* -*- 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, {
+ loader: "resource://devtools/shared/loader/Loader.sys.mjs",
+ DevToolsShim: "chrome://devtools-startup/content/DevToolsShim.sys.mjs",
+});
+XPCOMUtils.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 = await 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.loadURIString(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`);
+ }
+}