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 --- .../browser/browser_ext_menus_targetElement.js | 326 +++++++++++++++++++++ 1 file changed, 326 insertions(+) create mode 100644 browser/components/extensions/test/browser/browser_ext_menus_targetElement.js (limited to 'browser/components/extensions/test/browser/browser_ext_menus_targetElement.js') diff --git a/browser/components/extensions/test/browser/browser_ext_menus_targetElement.js b/browser/components/extensions/test/browser/browser_ext_menus_targetElement.js new file mode 100644 index 0000000000..abfbf26a05 --- /dev/null +++ b/browser/components/extensions/test/browser/browser_ext_menus_targetElement.js @@ -0,0 +1,326 @@ +/* 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 PAGE = + "http://mochi.test:8888/browser/browser/components/extensions/test/browser/context.html"; + +// Loads an extension that records menu visibility events in the current tab. +// The returned extension has two helper functions "openContextMenu" and +// "checkIsValid" that are used to verify the behavior of targetElementId. +async function loadExtensionAndTab() { + let tab = await BrowserTestUtils.openNewForegroundTab(gBrowser, PAGE); + gBrowser.selectedTab = tab; + + function contentScript() { + browser.test.onMessage.addListener( + (msg, targetElementId, expectedSelector, description) => { + browser.test.assertEq("checkIsValid", msg, "Expected message"); + + let expected = expectedSelector + ? document.querySelector(expectedSelector) + : null; + let elem = browser.menus.getTargetElement(targetElementId); + browser.test.assertEq(expected, elem, description); + browser.test.sendMessage("checkIsValidDone"); + } + ); + } + + async function background() { + browser.menus.onShown.addListener(async (info, tab) => { + browser.test.sendMessage("onShownMenu", info.targetElementId); + }); + await browser.tabs.executeScript({ file: "contentScript.js" }); + browser.test.sendMessage("ready"); + } + + let extension = ExtensionTestUtils.loadExtension({ + manifest: { + permissions: ["menus", "http://mochi.test/*"], + }, + background, + files: { + "contentScript.js": contentScript, + }, + }); + + extension.openAndCloseMenu = async selector => { + await openContextMenu(selector); + let targetElementId = await extension.awaitMessage("onShownMenu"); + await closeContextMenu(); + return targetElementId; + }; + + extension.checkIsValid = async ( + targetElementId, + expectedSelector, + description + ) => { + extension.sendMessage( + "checkIsValid", + targetElementId, + expectedSelector, + description + ); + await extension.awaitMessage("checkIsValidDone"); + }; + + await extension.startup(); + await extension.awaitMessage("ready"); + return { extension, tab }; +} + +// Tests that info.targetElementId is only available with the right permissions. +add_task(async function required_permission() { + let { extension, tab } = await loadExtensionAndTab(); + + // Load another extension to verify that the permission from the first + // extension does not enable the "targetElementId" parameter. + function background() { + browser.contextMenus.onShown.addListener((info, tab) => { + browser.test.assertEq( + undefined, + info.targetElementId, + "targetElementId requires permission" + ); + browser.test.sendMessage("onShown"); + }); + browser.contextMenus.onClicked.addListener(async info => { + browser.test.assertEq( + undefined, + info.targetElementId, + "targetElementId requires permission" + ); + const code = ` + browser.test.assertEq(undefined, browser.menus, "menus API requires permission in content script"); + browser.test.assertEq(undefined, browser.contextMenus, "contextMenus API not available in content script."); + `; + await browser.tabs.executeScript({ code }); + browser.test.sendMessage("onClicked"); + }); + browser.contextMenus.create({ title: "menu for page" }, () => { + browser.test.sendMessage("ready"); + }); + } + let extension2 = ExtensionTestUtils.loadExtension({ + manifest: { + permissions: ["contextMenus", "http://mochi.test/*"], + }, + background, + }); + await extension2.startup(); + await extension2.awaitMessage("ready"); + + let menu = await openContextMenu(); + await extension.awaitMessage("onShownMenu"); + let menuItem = menu.getElementsByAttribute("label", "menu for page")[0]; + await closeExtensionContextMenu(menuItem); + + await extension2.awaitMessage("onShown"); + await extension2.awaitMessage("onClicked"); + await extension2.unload(); + + await extension.unload(); + BrowserTestUtils.removeTab(tab); +}); + +// Tests that the basic functionality works as expected. +add_task(async function getTargetElement_in_page() { + let { extension, tab } = await loadExtensionAndTab(); + + for (let selector of ["#img1", "#link1", "#password"]) { + let targetElementId = await extension.openAndCloseMenu(selector); + ok( + Number.isInteger(targetElementId), + `targetElementId (${targetElementId}) should be an integer for ${selector}` + ); + + await extension.checkIsValid( + targetElementId, + selector, + `Expected target to match ${selector}` + ); + } + + await extension.unload(); + BrowserTestUtils.removeTab(tab); +}); + +add_task(async function getTargetElement_in_frame() { + let tab = await BrowserTestUtils.openNewForegroundTab(gBrowser, PAGE); + gBrowser.selectedTab = tab; + + async function background() { + let targetElementId; + browser.menus.onShown.addListener(async (info, tab) => { + browser.test.assertTrue( + info.frameUrl.endsWith("context_frame.html"), + `Expected frame ${info.frameUrl}` + ); + targetElementId = info.targetElementId; + let elem = browser.menus.getTargetElement(targetElementId); + browser.test.assertEq( + null, + elem, + "should not find page element in extension's background" + ); + + await browser.tabs.executeScript(tab.id, { + code: `{ + let elem = browser.menus.getTargetElement(${targetElementId}); + browser.test.assertEq(null, elem, "should not find element from different frame"); + }`, + }); + + await browser.tabs.executeScript(tab.id, { + frameId: info.frameId, + code: `{ + let elem = browser.menus.getTargetElement(${targetElementId}); + browser.test.assertEq(document.body, elem, "should find the target element in the frame"); + }`, + }); + browser.test.sendMessage("pageAndFrameChecked"); + }); + + browser.menus.onClicked.addListener(info => { + browser.test.assertEq( + targetElementId, + info.targetElementId, + "targetElementId in onClicked must match onShown." + ); + browser.test.sendMessage("onClickedChecked"); + }); + + browser.menus.create( + { title: "menu for frame", contexts: ["frame"] }, + () => { + browser.test.sendMessage("ready"); + } + ); + } + let extension = ExtensionTestUtils.loadExtension({ + manifest: { + permissions: ["menus", "http://mochi.test/*"], + }, + background, + }); + + await extension.startup(); + await extension.awaitMessage("ready"); + + let menu = await openContextMenuInFrame(); + await extension.awaitMessage("pageAndFrameChecked"); + let menuItem = menu.getElementsByAttribute("label", "menu for frame")[0]; + await closeExtensionContextMenu(menuItem); + await extension.awaitMessage("onClickedChecked"); + + await extension.unload(); + BrowserTestUtils.removeTab(tab); +}); + +// Test that getTargetElement does not return a detached element. +add_task(async function getTargetElement_after_removing_element() { + let tab = await BrowserTestUtils.openNewForegroundTab(gBrowser, PAGE); + + function background() { + function contentScript(targetElementId) { + let expectedElem = document.getElementById("edit-me"); + let { nextElementSibling } = expectedElem; + + let elem = browser.menus.getTargetElement(targetElementId); + browser.test.assertEq( + expectedElem, + elem, + "Expected target element before element removal" + ); + + expectedElem.remove(); + elem = browser.menus.getTargetElement(targetElementId); + browser.test.assertEq( + null, + elem, + "Expected no target element after element removal." + ); + + nextElementSibling.insertAdjacentElement("beforebegin", expectedElem); + elem = browser.menus.getTargetElement(targetElementId); + browser.test.assertEq( + expectedElem, + elem, + "Expected target element after element restoration." + ); + } + browser.menus.onClicked.addListener(async (info, tab) => { + const code = `(${contentScript})(${info.targetElementId})`; + browser.test.log(code); + await browser.tabs.executeScript(tab.id, { code }); + browser.test.sendMessage("checkedRemovedElement"); + }); + browser.menus.create({ title: "some menu item" }, () => { + browser.test.sendMessage("ready"); + }); + } + let extension = ExtensionTestUtils.loadExtension({ + manifest: { + permissions: ["menus", "http://mochi.test/*"], + }, + background, + }); + + await extension.startup(); + await extension.awaitMessage("ready"); + let menu = await openContextMenu("#edit-me"); + let menuItem = menu.getElementsByAttribute("label", "some menu item")[0]; + await closeExtensionContextMenu(menuItem); + await extension.awaitMessage("checkedRemovedElement"); + await extension.unload(); + BrowserTestUtils.removeTab(tab); +}); + +// Tests whether targetElementId expires after opening a new menu. +add_task(async function expireTargetElement() { + let { extension, tab } = await loadExtensionAndTab(); + + // Open the menu once to get the first element ID. + let targetElementId = await extension.openAndCloseMenu("#longtext"); + + // Open another menu. The previous ID should expire. + await extension.openAndCloseMenu("#longtext"); + await extension.checkIsValid( + targetElementId, + null, + `Expected initial target ID to expire after opening another menu` + ); + + await extension.unload(); + BrowserTestUtils.removeTab(tab); +}); + +// Tests whether targetElementId of different tabs are independent. +add_task(async function independentMenusInDifferentTabs() { + let { extension, tab } = await loadExtensionAndTab(); + + let targetElementId = await extension.openAndCloseMenu("#longtext"); + + let tab2 = await BrowserTestUtils.openNewForegroundTab(gBrowser, PAGE + "?"); + gBrowser.selectedTab = tab2; + + let targetElementId2 = await extension.openAndCloseMenu("#editabletext"); + + await extension.checkIsValid( + targetElementId2, + null, + "targetElementId from different tab should not resolve." + ); + await extension.checkIsValid( + targetElementId, + "#longtext", + "Expected getTargetElement to work after closing a menu in another tab." + ); + + await extension.unload(); + BrowserTestUtils.removeTab(tab); + BrowserTestUtils.removeTab(tab2); +}); -- cgit v1.2.3