summaryrefslogtreecommitdiffstats
path: root/browser/components/extensions/test/browser/browser_ext_menus_events.js
diff options
context:
space:
mode:
Diffstat (limited to 'browser/components/extensions/test/browser/browser_ext_menus_events.js')
-rw-r--r--browser/components/extensions/test/browser/browser_ext_menus_events.js907
1 files changed, 907 insertions, 0 deletions
diff --git a/browser/components/extensions/test/browser/browser_ext_menus_events.js b/browser/components/extensions/test/browser/browser_ext_menus_events.js
new file mode 100644
index 0000000000..030128de41
--- /dev/null
+++ b/browser/components/extensions/test/browser/browser_ext_menus_events.js
@@ -0,0 +1,907 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+"use strict";
+
+const { ExtensionPermissions } = ChromeUtils.importESModule(
+ "resource://gre/modules/ExtensionPermissions.sys.mjs"
+);
+
+const PAGE =
+ "http://mochi.test:8888/browser/browser/components/extensions/test/browser/context.html";
+const PAGE_BASE = PAGE.replace("context.html", "");
+const PAGE_HOST_PATTERN = "http://mochi.test/*";
+
+const EXPECT_TARGET_ELEMENT = 13337;
+
+async function grantOptionalPermission(extension, permissions) {
+ let ext = WebExtensionPolicy.getByID(extension.id).extension;
+ return ExtensionPermissions.add(extension.id, permissions, ext);
+}
+
+var someOtherTab, testTab;
+
+add_setup(async function () {
+ await SpecialPowers.pushPrefEnv({
+ set: [["extensions.manifestV3.enabled", true]],
+ });
+
+ // To help diagnose an intermittent later.
+ SimpleTest.requestCompleteLog();
+
+ // Setup the test tab now, rather than for each test
+ someOtherTab = gBrowser.selectedTab;
+ testTab = await BrowserTestUtils.openNewForegroundTab(gBrowser, PAGE);
+ registerCleanupFunction(() => BrowserTestUtils.removeTab(testTab));
+});
+
+// Registers a context menu using menus.create(menuCreateParams) and checks
+// whether the menus.onShown and menus.onHidden events are fired as expected.
+// doOpenMenu must open the menu and its returned promise must resolve after the
+// menu is shown. Similarly, doCloseMenu must hide the menu.
+async function testShowHideEvent({
+ menuCreateParams,
+ id,
+ doOpenMenu,
+ doCloseMenu,
+ expectedShownEvent,
+ expectedShownEventWithPermissions = null,
+ forceTabToBackground = false,
+ manifest_version = 2,
+}) {
+ async function background(menu_create_params) {
+ const [tab] = await browser.tabs.query({
+ active: true,
+ currentWindow: true,
+ });
+ if (browser.pageAction) {
+ await browser.pageAction.show(tab.id);
+ }
+
+ let shownEvents = [];
+ let hiddenEvents = [];
+
+ browser.menus.onShown.addListener((...args) => {
+ browser.test.log(`==> onShown args ${JSON.stringify(args)}`);
+ let [info, shownTab] = args;
+ if (info.targetElementId) {
+ // In this test, we aren't interested in the exact value,
+ // only in whether it is set or not.
+ info.targetElementId = 13337; // = EXPECT_TARGET_ELEMENT
+ }
+ shownEvents.push(info);
+
+ if (menu_create_params.title.includes("TEST_EXPECT_NO_TAB")) {
+ browser.test.assertEq(undefined, shownTab, "expect no tab");
+ } else {
+ browser.test.assertEq(tab.id, shownTab?.id, "expected tab");
+ }
+ browser.test.assertEq(2, args.length, "expected number of onShown args");
+ });
+ browser.menus.onHidden.addListener(event => hiddenEvents.push(event));
+
+ browser.test.onMessage.addListener(async msg => {
+ switch (msg) {
+ case "register-menu":
+ let menuId;
+ await new Promise(resolve => {
+ menuId = browser.menus.create(menu_create_params, resolve);
+ });
+ browser.test.assertEq(
+ 0,
+ shownEvents.length,
+ "no onShown before menu"
+ );
+ browser.test.assertEq(
+ 0,
+ hiddenEvents.length,
+ "no onHidden before menu"
+ );
+ browser.test.sendMessage("menu-registered", menuId);
+ break;
+ case "assert-menu-shown":
+ browser.test.assertEq(1, shownEvents.length, "expected onShown");
+ browser.test.assertEq(
+ 0,
+ hiddenEvents.length,
+ "no onHidden before closing"
+ );
+ browser.test.sendMessage("onShown-event-data", shownEvents[0]);
+ break;
+ case "assert-menu-hidden":
+ browser.test.assertEq(
+ 1,
+ shownEvents.length,
+ "expected no more onShown"
+ );
+ browser.test.assertEq(1, hiddenEvents.length, "expected onHidden");
+ browser.test.sendMessage("onHidden-event-data", hiddenEvents[0]);
+ break;
+ case "optional-menu-shown-with-permissions":
+ browser.test.assertEq(
+ 2,
+ shownEvents.length,
+ "expected second onShown"
+ );
+ browser.test.sendMessage("onShown-event-data2", shownEvents[1]);
+ break;
+ }
+ });
+
+ browser.test.sendMessage("ready");
+ }
+
+ // Tab must initially open as a foreground tab, because the test extension
+ // looks for the active tab.
+ if (gBrowser.selectedTab != testTab) {
+ await BrowserTestUtils.switchTab(gBrowser, testTab);
+ }
+
+ let useAddonManager, browser_specific_settings;
+ const action = manifest_version < 3 ? "browser_action" : "action";
+ // hook up AOM so event pages in MV3 work.
+ if (manifest_version > 2) {
+ browser_specific_settings = { gecko: { id } };
+ useAddonManager = "temporary";
+ }
+ let extension = ExtensionTestUtils.loadExtension({
+ background: `(${background})(${JSON.stringify(menuCreateParams)})`,
+ useAddonManager,
+ manifest: {
+ manifest_version,
+ browser_specific_settings,
+ page_action: {},
+ [action]: {
+ default_popup: "popup.html",
+ default_area: "navbar",
+ },
+ permissions: ["menus"],
+ optional_permissions: [PAGE_HOST_PATTERN],
+ },
+ files: {
+ "popup.html": `<!DOCTYPE html><meta charset="utf-8">Popup body`,
+ },
+ });
+ await extension.startup();
+ await extension.awaitMessage("ready");
+ extension.sendMessage("register-menu");
+ let menuId = await extension.awaitMessage("menu-registered");
+ info(`menu registered ${menuId}`);
+
+ if (forceTabToBackground && gBrowser.selectedTab != someOtherTab) {
+ await BrowserTestUtils.switchTab(gBrowser, someOtherTab);
+ }
+
+ await doOpenMenu(extension, testTab);
+ extension.sendMessage("assert-menu-shown");
+ let shownEvent = await extension.awaitMessage("onShown-event-data");
+
+ // menuCreateParams.id is not set, therefore a numeric ID is generated.
+ expectedShownEvent.menuIds = [menuId];
+ Assert.deepEqual(shownEvent, expectedShownEvent, "expected onShown info");
+
+ await doCloseMenu(extension);
+ extension.sendMessage("assert-menu-hidden");
+ let hiddenEvent = await extension.awaitMessage("onHidden-event-data");
+ is(hiddenEvent, undefined, "expected no event data for onHidden event");
+
+ if (expectedShownEventWithPermissions) {
+ expectedShownEventWithPermissions.menuIds = [menuId];
+ await grantOptionalPermission(extension, {
+ permissions: [],
+ origins: [PAGE_HOST_PATTERN],
+ });
+ await doOpenMenu(extension, testTab);
+ extension.sendMessage("optional-menu-shown-with-permissions");
+ let shownEvent2 = await extension.awaitMessage("onShown-event-data2");
+ Assert.deepEqual(
+ shownEvent2,
+ expectedShownEventWithPermissions,
+ "expected onShown info when host permissions are enabled"
+ );
+ await doCloseMenu(extension);
+ }
+
+ await extension.unload();
+}
+
+// Make sure that we won't trigger onShown when extensions cannot add menus.
+add_task(async function test_no_show_hide_for_unsupported_menu() {
+ let extension = ExtensionTestUtils.loadExtension({
+ background() {
+ let events = [];
+ browser.menus.onShown.addListener(data => events.push(data));
+ browser.menus.onHidden.addListener(() => events.push("onHidden"));
+ browser.test.onMessage.addListener(() => {
+ browser.test.assertEq(
+ "[]",
+ JSON.stringify(events),
+ "Should not have any events when the context is unsupported."
+ );
+ browser.test.notifyPass("done listening to menu events");
+ });
+ },
+ manifest: {
+ permissions: ["menus"],
+ },
+ });
+
+ await extension.startup();
+ // Open and close a menu for which the extension cannot add menu items.
+ await openChromeContextMenu("toolbar-context-menu", "#stop-reload-button");
+ await closeChromeContextMenu("toolbar-context-menu");
+
+ extension.sendMessage("check menu events");
+ await extension.awaitFinish("done listening to menu events");
+
+ await extension.unload();
+});
+
+add_task(async function test_show_hide_without_menu_item() {
+ let extension = ExtensionTestUtils.loadExtension({
+ background() {
+ let events = [];
+ browser.menus.onShown.addListener(data => events.push(data));
+ browser.menus.onHidden.addListener(() => events.push("onHidden"));
+ browser.test.onMessage.addListener(() => {
+ browser.test.sendMessage("events from menuless extension", events);
+ });
+
+ browser.menus.create({
+ title: "never shown",
+ documentUrlPatterns: ["*://url-pattern-that-never-matches/*"],
+ contexts: ["all"],
+ });
+ },
+ manifest: {
+ permissions: ["menus", PAGE_HOST_PATTERN],
+ },
+ });
+
+ await extension.startup();
+
+ // Run another context menu test where onShown/onHidden will fire.
+ await testShowHideEvent({
+ menuCreateParams: {
+ title: "any menu item",
+ contexts: ["all"],
+ },
+ expectedShownEvent: {
+ contexts: ["page", "all"],
+ viewType: "tab",
+ editable: false,
+ frameId: 0,
+ },
+ async doOpenMenu() {
+ await openContextMenu("body");
+ },
+ async doCloseMenu() {
+ await closeExtensionContextMenu();
+ },
+ });
+
+ // Now the menu has been shown and hidden, and in another extension the
+ // onShown/onHidden events have been dispatched.
+ extension.sendMessage("check menu events");
+ let events = await extension.awaitMessage("events from menuless extension");
+ is(events.length, 2, "expect two events");
+ is(events[1], "onHidden", "last event should be onHidden");
+ ok(events[0].targetElementId, "info.targetElementId must be set in onShown");
+ delete events[0].targetElementId;
+ Assert.deepEqual(
+ events[0],
+ {
+ menuIds: [],
+ contexts: ["page", "all"],
+ viewType: "tab",
+ editable: false,
+ pageUrl: PAGE,
+ frameId: 0,
+ },
+ "expected onShown info from menuless extension"
+ );
+ await extension.unload();
+});
+
+add_task(async function test_show_hide_pageAction() {
+ await testShowHideEvent({
+ menuCreateParams: {
+ title: "pageAction item",
+ contexts: ["page_action"],
+ },
+ expectedShownEvent: {
+ contexts: ["page_action", "all"],
+ viewType: undefined,
+ editable: false,
+ },
+ expectedShownEventWithPermissions: {
+ contexts: ["page_action", "all"],
+ viewType: undefined,
+ editable: false,
+ pageUrl: PAGE,
+ },
+ async doOpenMenu(extension) {
+ await openActionContextMenu(extension, "page");
+ },
+ async doCloseMenu() {
+ await closeActionContextMenu(null, "page");
+ },
+ });
+});
+
+add_task(async function test_show_hide_browserAction() {
+ await testShowHideEvent({
+ menuCreateParams: {
+ title: "browserAction item",
+ contexts: ["browser_action"],
+ },
+ expectedShownEvent: {
+ contexts: ["browser_action", "all"],
+ viewType: undefined,
+ editable: false,
+ },
+ expectedShownEventWithPermissions: {
+ contexts: ["browser_action", "all"],
+ viewType: undefined,
+ editable: false,
+ pageUrl: PAGE,
+ },
+ async doOpenMenu(extension) {
+ await openActionContextMenu(extension, "browser");
+ },
+ async doCloseMenu() {
+ await closeActionContextMenu();
+ },
+ });
+});
+
+add_task(async function test_show_hide_browserAction_v3() {
+ await testShowHideEvent({
+ manifest_version: 3,
+ id: "browser-action@mochitest",
+ menuCreateParams: {
+ id: "action_item",
+ title: "Action item",
+ contexts: ["action"],
+ },
+ expectedShownEvent: {
+ contexts: ["action", "all"],
+ viewType: undefined,
+ editable: false,
+ },
+ expectedShownEventWithPermissions: {
+ contexts: ["action", "all"],
+ viewType: undefined,
+ editable: false,
+ pageUrl: PAGE,
+ },
+ async doOpenMenu(extension) {
+ await openActionContextMenu(extension, "browser");
+ },
+ async doCloseMenu() {
+ await closeActionContextMenu();
+ },
+ });
+});
+
+add_task(async function test_show_hide_browserAction_popup() {
+ let popupUrl;
+ await testShowHideEvent({
+ menuCreateParams: {
+ title: "browserAction popup - TEST_EXPECT_NO_TAB",
+ contexts: ["all", "browser_action"],
+ },
+ expectedShownEvent: {
+ contexts: ["page", "all"],
+ viewType: "popup",
+ frameId: 0,
+ editable: false,
+ get pageUrl() {
+ return popupUrl;
+ },
+ targetElementId: EXPECT_TARGET_ELEMENT,
+ },
+ expectedShownEventWithPermissions: {
+ contexts: ["page", "all"],
+ viewType: "popup",
+ frameId: 0,
+ editable: false,
+ get pageUrl() {
+ return popupUrl;
+ },
+ targetElementId: EXPECT_TARGET_ELEMENT,
+ },
+ async doOpenMenu(extension) {
+ popupUrl = `moz-extension://${extension.uuid}/popup.html`;
+ await clickBrowserAction(extension);
+ await openContextMenuInPopup(extension);
+ },
+ async doCloseMenu(extension) {
+ await closeExtensionContextMenu();
+ await closeBrowserAction(extension);
+ },
+ });
+});
+
+add_task(async function test_show_hide_browserAction_popup_v3() {
+ let popupUrl;
+ await testShowHideEvent({
+ manifest_version: 3,
+ id: "browser-action-popup@mochitest",
+ menuCreateParams: {
+ id: "action_popup",
+ title: "Action popup - TEST_EXPECT_NO_TAB",
+ contexts: ["all", "action"],
+ },
+ expectedShownEvent: {
+ contexts: ["page", "all"],
+ viewType: "popup",
+ frameId: 0,
+ editable: false,
+ get pageUrl() {
+ return popupUrl;
+ },
+ targetElementId: EXPECT_TARGET_ELEMENT,
+ },
+ expectedShownEventWithPermissions: {
+ contexts: ["page", "all"],
+ viewType: "popup",
+ frameId: 0,
+ editable: false,
+ get pageUrl() {
+ return popupUrl;
+ },
+ targetElementId: EXPECT_TARGET_ELEMENT,
+ },
+ async doOpenMenu(extension) {
+ popupUrl = `moz-extension://${extension.uuid}/popup.html`;
+ await clickBrowserAction(extension);
+ await openContextMenuInPopup(extension);
+ },
+ async doCloseMenu(extension) {
+ await closeExtensionContextMenu();
+ await closeBrowserAction(extension);
+ },
+ });
+});
+
+// Common code used by test_show_hide_tab and test_show_hide_tab_via_tab_panel.
+async function testShowHideTabMenu({
+ doOpenTabContextMenu,
+ doCloseTabContextMenu,
+}) {
+ await testShowHideEvent({
+ // To verify that the event matches the contextmenu target, switch to
+ // an unrelated tab before opening a contextmenu on the desired tab.
+ forceTabToBackground: true,
+ menuCreateParams: {
+ title: "tab menu item",
+ contexts: ["tab"],
+ },
+ expectedShownEvent: {
+ contexts: ["tab"],
+ viewType: undefined,
+ editable: false,
+ },
+ expectedShownEventWithPermissions: {
+ contexts: ["tab"],
+ viewType: undefined,
+ editable: false,
+ pageUrl: PAGE,
+ },
+ async doOpenMenu(extension, contextTab) {
+ await doOpenTabContextMenu(contextTab);
+ },
+ async doCloseMenu() {
+ await doCloseTabContextMenu();
+ },
+ });
+}
+
+add_task(async function test_show_hide_tab() {
+ await testShowHideTabMenu({
+ async doOpenTabContextMenu(contextTab) {
+ await openTabContextMenu(contextTab);
+ },
+ async doCloseTabContextMenu() {
+ await closeTabContextMenu();
+ },
+ });
+});
+
+// Checks that right-clicking on a tab in the tabs panel (the one that appears
+// when there are many tabs, or when browser.tabs.tabmanager.enabled = true)
+// results in an event that is associated with the expected tab.
+add_task(async function test_show_hide_tab_via_tab_panel() {
+ gTabsPanel.init();
+ const tabContainer = document.getElementById("tabbrowser-tabs");
+ let shouldAddOverflow = !tabContainer.hasAttribute("overflow");
+ const revertTabContainerAttribute = () => {
+ if (shouldAddOverflow) {
+ // Revert attribute if it was changed.
+ tabContainer.removeAttribute("overflow");
+ // The function is going to be called twice, but let's run the logic once.
+ shouldAddOverflow = false;
+ }
+ };
+ if (shouldAddOverflow) {
+ // Ensure the visibility of the "all tabs menu" button (#alltabs-button).
+ tabContainer.setAttribute("overflow", "true");
+ // Register cleanup function in case the test fails before we reach the end.
+ registerCleanupFunction(revertTabContainerAttribute);
+ }
+
+ const allTabsView = document.getElementById("allTabsMenu-allTabsView");
+
+ await testShowHideTabMenu({
+ async doOpenTabContextMenu(contextTab) {
+ // Show the tabs panel.
+ let allTabsPopupShownPromise = BrowserTestUtils.waitForEvent(
+ allTabsView,
+ "ViewShown"
+ );
+ gTabsPanel.showAllTabsPanel();
+ await allTabsPopupShownPromise;
+
+ // Find the menu item that is associated with the given tab
+ let index = Array.prototype.findIndex.call(
+ gTabsPanel.allTabsViewTabs.children,
+ toolbaritem => toolbaritem.tab === contextTab
+ );
+ ok(index !== -1, "sanity check: tabs panel has item for the tab");
+
+ // Finally, open the context menu on it.
+ await openChromeContextMenu(
+ "tabContextMenu",
+ `.all-tabs-item:nth-child(${index + 1})`
+ );
+ },
+ async doCloseTabContextMenu() {
+ await closeTabContextMenu();
+ let allTabsPopupHiddenPromise = BrowserTestUtils.waitForEvent(
+ allTabsView.panelMultiView,
+ "PanelMultiViewHidden"
+ );
+ gTabsPanel.hideAllTabsPanel();
+ await allTabsPopupHiddenPromise;
+ },
+ });
+
+ revertTabContainerAttribute();
+});
+
+add_task(async function test_show_hide_tools_menu() {
+ await testShowHideEvent({
+ menuCreateParams: {
+ title: "menu item",
+ contexts: ["tools_menu"],
+ },
+ expectedShownEvent: {
+ contexts: ["tools_menu"],
+ viewType: undefined,
+ editable: false,
+ },
+ expectedShownEventWithPermissions: {
+ contexts: ["tools_menu"],
+ viewType: undefined,
+ editable: false,
+ pageUrl: PAGE,
+ },
+ async doOpenMenu() {
+ await openToolsMenu();
+ },
+ async doCloseMenu() {
+ await closeToolsMenu();
+ },
+ });
+});
+
+add_task(async function test_show_hide_page() {
+ await testShowHideEvent({
+ menuCreateParams: {
+ title: "page menu item",
+ contexts: ["page"],
+ },
+ expectedShownEvent: {
+ contexts: ["page", "all"],
+ viewType: "tab",
+ editable: false,
+ frameId: 0,
+ },
+ expectedShownEventWithPermissions: {
+ contexts: ["page", "all"],
+ viewType: "tab",
+ editable: false,
+ pageUrl: PAGE,
+ frameId: 0,
+ targetElementId: EXPECT_TARGET_ELEMENT,
+ },
+ async doOpenMenu() {
+ await openContextMenu("body");
+ },
+ async doCloseMenu() {
+ await closeExtensionContextMenu();
+ },
+ });
+});
+
+add_task(async function test_show_hide_frame() {
+ // frame info will be determined before opening the menu.
+ let frameId;
+ await testShowHideEvent({
+ menuCreateParams: {
+ title: "subframe menu item",
+ contexts: ["frame"],
+ },
+ expectedShownEvent: {
+ contexts: ["frame", "all"],
+ viewType: "tab",
+ editable: false,
+ get frameId() {
+ return frameId;
+ },
+ },
+ expectedShownEventWithPermissions: {
+ contexts: ["frame", "all"],
+ viewType: "tab",
+ editable: false,
+ get frameId() {
+ return frameId;
+ },
+ pageUrl: PAGE,
+ frameUrl: PAGE_BASE + "context_frame.html",
+ targetElementId: EXPECT_TARGET_ELEMENT,
+ },
+ async doOpenMenu() {
+ frameId = await SpecialPowers.spawn(
+ gBrowser.selectedBrowser,
+ [],
+ function () {
+ const { WebNavigationFrames } = ChromeUtils.importESModule(
+ "resource://gre/modules/WebNavigationFrames.sys.mjs"
+ );
+
+ let { contentWindow } = content.document.getElementById("frame");
+ return WebNavigationFrames.getFrameId(contentWindow);
+ }
+ );
+ await openContextMenuInFrame();
+ },
+ async doCloseMenu() {
+ await closeExtensionContextMenu();
+ },
+ });
+});
+
+add_task(async function test_show_hide_password() {
+ await testShowHideEvent({
+ menuCreateParams: {
+ title: "password item",
+ contexts: ["password"],
+ },
+ expectedShownEvent: {
+ contexts: ["editable", "password", "all"],
+ viewType: "tab",
+ editable: true,
+ frameId: 0,
+ },
+ expectedShownEventWithPermissions: {
+ contexts: ["editable", "password", "all"],
+ viewType: "tab",
+ editable: true,
+ frameId: 0,
+ pageUrl: PAGE,
+ targetElementId: EXPECT_TARGET_ELEMENT,
+ },
+ async doOpenMenu() {
+ await openContextMenu("#password");
+ },
+ async doCloseMenu() {
+ await closeExtensionContextMenu();
+ },
+ });
+});
+
+add_task(async function test_show_hide_link() {
+ await testShowHideEvent({
+ menuCreateParams: {
+ title: "link item",
+ contexts: ["link"],
+ },
+ expectedShownEvent: {
+ contexts: ["link", "all"],
+ viewType: "tab",
+ editable: false,
+ frameId: 0,
+ },
+ expectedShownEventWithPermissions: {
+ contexts: ["link", "all"],
+ viewType: "tab",
+ editable: false,
+ frameId: 0,
+ linkText: "Some link",
+ linkUrl: PAGE_BASE + "some-link",
+ pageUrl: PAGE,
+ targetElementId: EXPECT_TARGET_ELEMENT,
+ },
+ async doOpenMenu() {
+ await openContextMenu("#link1");
+ },
+ async doCloseMenu() {
+ await closeExtensionContextMenu();
+ },
+ });
+});
+
+add_task(async function test_show_hide_image_link() {
+ await testShowHideEvent({
+ menuCreateParams: {
+ title: "image item",
+ contexts: ["image"],
+ },
+ expectedShownEvent: {
+ contexts: ["image", "link", "all"],
+ viewType: "tab",
+ mediaType: "image",
+ editable: false,
+ frameId: 0,
+ },
+ expectedShownEventWithPermissions: {
+ contexts: ["image", "link", "all"],
+ viewType: "tab",
+ mediaType: "image",
+ editable: false,
+ frameId: 0,
+ // Apparently, when a link has no content, its href is used as linkText.
+ linkText: PAGE_BASE + "image-around-some-link",
+ linkUrl: PAGE_BASE + "image-around-some-link",
+ srcUrl: PAGE_BASE + "ctxmenu-image.png",
+ pageUrl: PAGE,
+ targetElementId: EXPECT_TARGET_ELEMENT,
+ },
+ async doOpenMenu() {
+ await openContextMenu("#img-wrapped-in-link");
+ },
+ async doCloseMenu() {
+ await closeExtensionContextMenu();
+ },
+ });
+});
+
+add_task(async function test_show_hide_editable_selection() {
+ let selectionText;
+ await testShowHideEvent({
+ menuCreateParams: {
+ title: "editable item",
+ contexts: ["editable"],
+ },
+ expectedShownEvent: {
+ contexts: ["editable", "selection", "all"],
+ viewType: "tab",
+ editable: true,
+ frameId: 0,
+ },
+ expectedShownEventWithPermissions: {
+ contexts: ["editable", "selection", "all"],
+ viewType: "tab",
+ editable: true,
+ frameId: 0,
+ pageUrl: PAGE,
+ get selectionText() {
+ return selectionText;
+ },
+ targetElementId: EXPECT_TARGET_ELEMENT,
+ },
+ async doOpenMenu() {
+ // Select lots of text in the test page before opening the menu.
+ selectionText = await SpecialPowers.spawn(
+ gBrowser.selectedBrowser,
+ [],
+ function () {
+ let node = content.document.getElementById("editabletext");
+ node.scrollIntoView();
+ node.select();
+ node.focus();
+ return node.value;
+ }
+ );
+
+ await openContextMenu("#editabletext");
+ },
+ async doCloseMenu() {
+ await closeExtensionContextMenu();
+ },
+ });
+});
+
+add_task(async function test_show_hide_video() {
+ const VIDEO_URL = "data:video/webm,xxx";
+ await testShowHideEvent({
+ menuCreateParams: {
+ title: "video item",
+ contexts: ["video"],
+ },
+ expectedShownEvent: {
+ contexts: ["video", "all"],
+ viewType: "tab",
+ mediaType: "video",
+ editable: false,
+ frameId: 0,
+ },
+ expectedShownEventWithPermissions: {
+ contexts: ["video", "all"],
+ viewType: "tab",
+ mediaType: "video",
+ editable: false,
+ frameId: 0,
+ srcUrl: VIDEO_URL,
+ pageUrl: PAGE,
+ targetElementId: EXPECT_TARGET_ELEMENT,
+ },
+ async doOpenMenu() {
+ await SpecialPowers.spawn(
+ gBrowser.selectedBrowser,
+ [VIDEO_URL],
+ function (VIDEO_URL) {
+ let video = content.document.createElement("video");
+ video.controls = true;
+ video.src = VIDEO_URL;
+ content.document.body.appendChild(video);
+ video.scrollIntoView();
+ video.focus();
+ }
+ );
+
+ await openContextMenu("video");
+ },
+ async doCloseMenu() {
+ await closeExtensionContextMenu();
+ },
+ });
+});
+
+add_task(async function test_show_hide_audio() {
+ const AUDIO_URL = "data:audio/ogg,xxx";
+ await testShowHideEvent({
+ menuCreateParams: {
+ title: "audio item",
+ contexts: ["audio"],
+ },
+ expectedShownEvent: {
+ contexts: ["audio", "all"],
+ viewType: "tab",
+ mediaType: "audio",
+ editable: false,
+ frameId: 0,
+ },
+ expectedShownEventWithPermissions: {
+ contexts: ["audio", "all"],
+ viewType: "tab",
+ mediaType: "audio",
+ editable: false,
+ frameId: 0,
+ srcUrl: AUDIO_URL,
+ pageUrl: PAGE,
+ targetElementId: EXPECT_TARGET_ELEMENT,
+ },
+ async doOpenMenu() {
+ await SpecialPowers.spawn(
+ gBrowser.selectedBrowser,
+ [AUDIO_URL],
+ function (AUDIO_URL) {
+ let audio = content.document.createElement("audio");
+ audio.controls = true;
+ audio.src = AUDIO_URL;
+ content.document.body.appendChild(audio);
+ audio.scrollIntoView();
+ audio.focus();
+ }
+ );
+
+ await openContextMenu("audio");
+ },
+ async doCloseMenu() {
+ await closeExtensionContextMenu();
+ },
+ });
+});