Bug 1325814 - Support shadow DOM in menus.getTargetElement + test r=mixedpuppy
authorRob Wu <rob@robwu.nl>
Wed, 08 Aug 2018 18:06:41 +0200
changeset 486153 68dbca6faa6e5d21684d73048cf7beb579acb7b5
parent 486152 a7c2208c1f85a2d301d6a89a6c4c08ec43f60fa4
child 486154 e623335f541313cd59ced846c6cecaeaca7f2c85
push id9719
push userffxbld-merge
push dateFri, 24 Aug 2018 17:49:46 +0000
treeherdermozilla-beta@719ec98fba77 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmixedpuppy
bugs1325814
milestone63.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1325814 - Support shadow DOM in menus.getTargetElement + test r=mixedpuppy 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
@@ -109,16 +109,17 @@ skip-if = (verify && (os == 'linux' || o
 [browser_ext_menus_accesskey.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]
 [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]
 skip-if = (verify && !debug && (os == 'linux'))
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);
+});