Bug 1325814 - Support shadow DOM in menus.getTargetElement + test
MozReview-Commit-ID: JzVM8wVpaeP
--- a/browser/components/extensions/child/ext-menus-child.js
+++ b/browser/components/extensions/child/ext-menus-child.js
@@ -9,18 +9,17 @@ this.menusChild = class extends Extensio
let {contextMenu} = tabChildGlobal;
let element;
if (contextMenu) {
let {lastMenuTarget} = contextMenu;
if (lastMenuTarget && Math.floor(lastMenuTarget.timeStamp) === targetElementId) {
element = lastMenuTarget.targetRef.get();
}
}
- // TODO: Support shadow DOM (now we return null).
- if (element && context.contentWindow.document.contains(element)) {
+ if (element && element.getRootNode({composed: true}) === context.contentWindow.document) {
return element;
}
return null;
},
},
};
}
};
--- a/browser/components/extensions/test/browser/browser-common.ini
+++ b/browser/components/extensions/test/browser/browser-common.ini
@@ -107,16 +107,17 @@ skip-if = (verify && (os == 'linux' || o
[browser_ext_menus.js]
[browser_ext_menus_activeTab.js]
[browser_ext_menus_errors.js]
[browser_ext_menus_event_order.js]
[browser_ext_menus_events.js]
[browser_ext_menus_refresh.js]
[browser_ext_menus_targetElement.js]
[browser_ext_menus_targetElement_extension.js]
+[browser_ext_menus_targetElement_shadow.js]
[browser_ext_omnibox.js]
skip-if = (debug && (os == 'linux' || os == 'mac')) || (verify && (os == 'linux' || os == 'mac')) # Bug 1416103 (was bug 1417052)
[browser_ext_openPanel.js]
skip-if = (verify && !debug && (os == 'linux' || os == 'mac'))
[browser_ext_optionsPage_browser_style.js]
[browser_ext_optionsPage_modals.js]
[browser_ext_optionsPage_privileges.js]
[browser_ext_pageAction_context.js]
new file mode 100644
--- /dev/null
+++ b/browser/components/extensions/test/browser/browser_ext_menus_targetElement_shadow.js
@@ -0,0 +1,91 @@
+/* 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";
+
+add_task(async function menuInShadowDOM() {
+ Services.prefs.setBoolPref("dom.webcomponents.shadowdom.enabled", true);
+ registerCleanupFunction(() => Services.prefs.clearUserPref("dom.webcomponents.shadowdom.enabled"));
+
+ let tab = await BrowserTestUtils.openNewForegroundTab(gBrowser, PAGE);
+ gBrowser.selectedTab = tab;
+
+ async function background() {
+ browser.menus.onShown.addListener(async (info, tab) => {
+ browser.test.assertTrue(Number.isInteger(info.targetElementId), `${info.targetElementId} should be an integer`);
+ browser.test.assertEq("all,link", info.contexts.sort().join(","), "Expected context");
+ browser.test.assertEq("http://example.com/?shadowlink", info.linkUrl, "Menu target should be a link in the shadow DOM");
+
+ let code = `{
+ try {
+ let elem = browser.menus.getTargetElement(${info.targetElementId});
+ browser.test.assertTrue(elem, "Shadow element must be found");
+ browser.test.assertEq("http://example.com/?shadowlink", elem.href, "Element is a link in shadow DOM " - elem.outerHTML);
+ } catch (e) {
+ browser.test.fail("Unexpected error in getTargetElement: " + e);
+ }
+ }`;
+ await browser.tabs.executeScript(tab.id, {code});
+ browser.test.sendMessage("onShownMenuAndCheckedInfo", info.targetElementId);
+ });
+
+ // Ensure that onShown is registered (workaround for bug 1300234):
+ await browser.menus.removeAll();
+ browser.test.sendMessage("ready");
+ }
+
+ let extension = ExtensionTestUtils.loadExtension({
+ manifest: {
+ permissions: ["menus", "http://mochi.test/*"],
+ },
+ background,
+ });
+
+ await extension.startup();
+ await extension.awaitMessage("ready");
+
+ async function testShadowMenu(setupMenuTarget) {
+ await openContextMenu(setupMenuTarget);
+ await extension.awaitMessage("onShownMenuAndCheckedInfo");
+ await closeContextMenu();
+ }
+
+ info("Clicking in open shadow root");
+ await testShadowMenu(() => {
+ let doc = content.document;
+ doc.body.innerHTML = `<div></div>`;
+ let host = doc.body.firstChild.attachShadow({mode: "open"});
+ host.innerHTML = `<a href="http://example.com/?shadowlink">Test open</a>`;
+ content.testTarget = host.firstChild;
+ return content.testTarget;
+ });
+
+ info("Clicking in closed shadow root");
+ await testShadowMenu(() => {
+ let doc = content.document;
+ doc.body.innerHTML = `<div></div>`;
+ let host = doc.body.firstChild.attachShadow({mode: "closed"});
+ host.innerHTML = `<a href="http://example.com/?shadowlink">Test closed</a>`;
+ content.testTarget = host.firstChild;
+ return content.testTarget;
+ });
+
+ info("Clicking in nested shadow DOM");
+ await testShadowMenu(() => {
+ let doc = content.document;
+ let host;
+ for (let container = doc.body, i = 0; i < 10; ++i) {
+ container.innerHTML = `<div id="level"></div>`;
+ host = container.firstChild.attachShadow({mode: "open"});
+ container = host;
+ }
+ host.innerHTML = `<a href="http://example.com/?shadowlink">Test nested</a>`;
+ content.testTarget = host.firstChild;
+ return content.testTarget;
+ });
+
+ await extension.unload();
+ BrowserTestUtils.removeTab(tab);
+});