Bug 1325814 - Support shadow DOM in menus.getTargetElement + test draft
authorRob Wu <rob@robwu.nl>
Wed, 08 Aug 2018 18:06:41 +0200
changeset 828267 e7635292a7fb9ff5575fd3adebde8e43e14abb26
parent 828266 efaa0db71538c4e222ce4afe50882582f664457f
push id118665
push userbmo:rob@robwu.nl
push dateFri, 10 Aug 2018 16:43:13 +0000
bugs1325814
milestone63.0a1
Bug 1325814 - Support shadow DOM in menus.getTargetElement + test MozReview-Commit-ID: JzVM8wVpaeP
browser/components/extensions/child/ext-menus-child.js
browser/components/extensions/test/browser/browser-common.ini
browser/components/extensions/test/browser/browser_ext_menus_targetElement_shadow.js
--- 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);
+});