"use strict"; // This is a test for PageActions.jsm, specifically the context menus. ChromeUtils.defineESModuleGetters(this, { ExtensionCommon: "resource://gre/modules/ExtensionCommon.sys.mjs", }); // Initialization. Must run first. add_setup(async function () { // The page action urlbar button, and therefore the panel, is only shown when // the current tab is actionable -- i.e., a normal web page. about:blank is // not, so open a new tab first thing, and close it when this test is done. let tab = await BrowserTestUtils.openNewForegroundTab({ gBrowser, url: "http://example.com/", }); registerCleanupFunction(async () => { BrowserTestUtils.removeTab(tab); }); await initPageActionsTest(); }); // Opens the context menu on a non-built-in action. (The context menu for // built-in actions is tested in browser_page_action_menu.js.) add_task(async function contextMenu() { // Add an extension with a page action so we can test its context menu. let extension = ExtensionTestUtils.loadExtension({ manifest: { name: "Page action test", page_action: { show_matches: [""] }, }, useAddonManager: "temporary", }); await extension.startup(); let actionId = ExtensionCommon.makeWidgetId(extension.id); // Open the main panel. await promiseOpenPageActionPanel(); let panelButton = BrowserPageActions.panelButtonNodeForActionID(actionId); let cxmenu = document.getElementById("pageActionContextMenu"); let contextMenuPromise; let menuItems; // Open the context menu again on the action's button in the panel. (The // panel should still be open.) contextMenuPromise = promisePanelShown("pageActionContextMenu"); EventUtils.synthesizeMouseAtCenter(panelButton, { type: "contextmenu", button: 2, }); await contextMenuPromise; menuItems = collectContextMenuItems(); Assert.deepEqual(makeMenuItemSpecs(menuItems), makeContextMenuItemSpecs()); // Click the "manage extension" context menu item. about:addons should open. let manageItemIndex = 0; contextMenuPromise = promisePanelHidden("pageActionContextMenu"); let aboutAddonsPromise = BrowserTestUtils.waitForNewTab( gBrowser, "about:addons" ); cxmenu.activateItem(menuItems[manageItemIndex]); let values = await Promise.all([aboutAddonsPromise, contextMenuPromise]); let aboutAddonsTab = values[0]; BrowserTestUtils.removeTab(aboutAddonsTab); // Wait for the urlbar button to become visible again after about:addons is // closed and the test tab becomes selected. await BrowserTestUtils.waitForCondition(() => { return BrowserPageActions.urlbarButtonNodeForActionID(actionId); }, "Waiting for urlbar button to be added back"); // Open the context menu on the action's urlbar button. let urlbarButton = BrowserPageActions.urlbarButtonNodeForActionID(actionId); contextMenuPromise = promisePanelShown("pageActionContextMenu"); EventUtils.synthesizeMouseAtCenter(urlbarButton, { type: "contextmenu", button: 2, }); await contextMenuPromise; menuItems = collectContextMenuItems(); Assert.deepEqual(makeMenuItemSpecs(menuItems), makeContextMenuItemSpecs()); // Click the "manage" context menu item. about:addons should open. contextMenuPromise = promisePanelHidden("pageActionContextMenu"); aboutAddonsPromise = BrowserTestUtils.waitForNewTab(gBrowser, "about:addons"); cxmenu.activateItem(menuItems[manageItemIndex]); values = await Promise.all([aboutAddonsPromise, contextMenuPromise]); aboutAddonsTab = values[0]; BrowserTestUtils.removeTab(aboutAddonsTab); // Wait for the urlbar button to become visible again after about:addons is // closed and the test tab becomes selected. await BrowserTestUtils.waitForCondition(() => { return BrowserPageActions.urlbarButtonNodeForActionID(actionId); }, "Waiting for urlbar button to be added back"); // Open the context menu on the action's urlbar button. urlbarButton = BrowserPageActions.urlbarButtonNodeForActionID(actionId); contextMenuPromise = promisePanelShown("pageActionContextMenu"); EventUtils.synthesizeMouseAtCenter(urlbarButton, { type: "contextmenu", button: 2, }); await contextMenuPromise; menuItems = collectContextMenuItems(); Assert.deepEqual(makeMenuItemSpecs(menuItems), makeContextMenuItemSpecs()); // Below we'll click the "remove extension" context menu item, which first // opens a prompt using the prompt service and requires confirming the prompt. // Set up a mock prompt service that returns 0 to indicate that the user // pressed the OK button. let { prompt } = Services; let promptService = { QueryInterface: ChromeUtils.generateQI(["nsIPromptService"]), confirmEx() { return 0; }, }; Services.prompt = promptService; registerCleanupFunction(() => { Services.prompt = prompt; }); // Now click the "remove extension" context menu item. let removeItemIndex = manageItemIndex + 1; contextMenuPromise = promisePanelHidden("pageActionContextMenu"); let promiseUninstalled = promiseAddonUninstalled(extension.id); cxmenu.activateItem(menuItems[removeItemIndex]); await contextMenuPromise; await promiseUninstalled; await extension.unload(); Services.prompt = prompt; // urlbar tests that run after this one can break if the mouse is left over // the area where the urlbar popup appears, which seems to happen due to the // above synthesized mouse events. Move it over the urlbar. EventUtils.synthesizeMouseAtCenter(gURLBar.inputField, { type: "mousemove" }); gURLBar.focus(); }); // The context menu shouldn't open on separators in the panel. add_task(async function contextMenuOnSeparator() { // Add a non-built-in action so the built-in separator will appear in the // panel. let action = PageActions.addAction( new PageActions.Action({ id: "contextMenuOnSeparator", title: "contextMenuOnSeparator", pinnedToUrlbar: true, }) ); // Open the panel and get the built-in separator. await promiseOpenPageActionPanel(); let separator = BrowserPageActions.panelButtonNodeForActionID( PageActions.ACTION_ID_BUILT_IN_SEPARATOR ); Assert.ok(separator, "The built-in separator should be in the panel"); // Context-click it. popupshowing should be fired, but by the time the event // reaches this listener, preventDefault should have been called on it. let showingPromise = BrowserTestUtils.waitForEvent( document.getElementById("pageActionContextMenu"), "popupshowing", false ); EventUtils.synthesizeMouseAtCenter(separator, { type: "contextmenu", button: 2, }); let event = await showingPromise; Assert.ok( event.defaultPrevented, "defaultPrevented should be true on popupshowing event" ); // Click the main button to hide the main panel. EventUtils.synthesizeMouseAtCenter(BrowserPageActions.mainButtonNode, {}); await promisePageActionPanelHidden(); action.remove(); // urlbar tests that run after this one can break if the mouse is left over // the area where the urlbar popup appears, which seems to happen due to the // above synthesized mouse events. Move it over the urlbar. EventUtils.synthesizeMouseAtCenter(gURLBar.inputField, { type: "mousemove" }); gURLBar.focus(); }); function collectContextMenuItems() { let contextMenu = document.getElementById("pageActionContextMenu"); return Array.prototype.filter.call(contextMenu.children, node => { return window.getComputedStyle(node).visibility == "visible"; }); } function makeMenuItemSpecs(elements) { return elements.map(e => e.localName == "menuseparator" ? {} : { label: e.label } ); } function makeContextMenuItemSpecs() { let items = [ { label: "Manage Extension\u2026" }, { label: "Remove Extension" }, ]; return items; } function promiseAddonUninstalled(addonId) { return new Promise(resolve => { let listener = {}; listener.onUninstalled = addon => { if (addon.id == addonId) { AddonManager.removeAddonListener(listener); resolve(); } }; AddonManager.addAddonListener(listener); }); }