From 6bf0a5cb5034a7e684dcc3500e841785237ce2dd Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Sun, 7 Apr 2024 19:32:43 +0200 Subject: Adding upstream version 1:115.7.0. Signed-off-by: Daniel Baumann --- .../test/browser/browser_ext_menus_events.js | 907 +++++++++++++++++++++ 1 file changed, 907 insertions(+) create mode 100644 browser/components/extensions/test/browser/browser_ext_menus_events.js (limited to 'browser/components/extensions/test/browser/browser_ext_menus_events.js') 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": `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(); + }, + }); +}); -- cgit v1.2.3