Bug 1460922 - Proof of concept - render XUL menu DOM inside top-level HTML document draft
authorBrian Grinstead <bgrinstead@mozilla.com>
Fri, 13 Jul 2018 11:05:50 -0700
changeset 817912 2397e347d5a4fd75fce4aada883361c89825e32a
parent 817669 e951f4ad123aa87d1d392c286db14cabb41a8560
push id116221
push userbgrinstead@mozilla.com
push dateFri, 13 Jul 2018 18:06:06 +0000
bugs1460922
milestone63.0a1
Bug 1460922 - Proof of concept - render XUL menu DOM inside top-level HTML document This also demonstrates integration with Fluent, including dynamic attribute changes It can be run with: ./mach run --temp-profile --chrome chrome://browser/content/menu-tester.html --jsconsole MozReview-Commit-ID: BDZZrOONWyy
browser/base/content/menu-tester.html
browser/base/jar.mn
new file mode 100644
--- /dev/null
+++ b/browser/base/content/menu-tester.html
@@ -0,0 +1,87 @@
+<!-- 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/. -->
+<!DOCTYPE html>
+<html dir=""
+      width="900" height="350"
+      >
+<head>
+  <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
+  <link rel="stylesheet" href="chrome://global/content/xul.css" />
+  <link rel="localization" href="browser/preferences/translation.ftl"/>
+  <script type="application/javascript" src="chrome://global/content/l10n.js"></script>
+</head>
+<body role="application">
+  <div id="content"></div>
+
+  <script>
+  Components.utils.import("resource://gre/modules/Services.jsm");
+
+  function parseXULDOM(str) {
+    const d = new DOMParser();
+    d.forceEnableXULXBL();
+    const doc = d.parseFromString(
+      `<box xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">${str}</box>`,
+      "application/xml");
+
+    // Remove whitespace nodes since they confuse XUL parser.
+    const iter = doc.createNodeIterator(doc, NodeFilter.SHOW_TEXT);
+    const pars = [];
+    let currentNode;
+    while (currentNode = iter.nextNode()) {
+      pars.push(currentNode);
+    }
+    pars.forEach(p=>p.remove());
+
+    const range = doc.createRange();
+    range.selectNodeContents(doc.firstChild);
+    return range.extractContents();
+  }
+
+  const XUL_NS = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";
+  const content = document.querySelector("#content");
+
+  // This actually seems to work for a top-level menu!
+  // ./mach run --temp-profile --chrome chrome://browser/content/menu-tester.html --jsconsole
+  content.appendChild(parseXULDOM(`
+    <command id="cmd_newNavigatorTab" oncommand="alert('command worked');"/>
+    <key id="key_newNavigatorTab" key="t" modifiers="accel"/>
+    <toolbar type="menubar">
+       <menubar id="main-menubar">
+        <menu id="file-menu"
+              data-l10n-id="translation-languages-button-remove">
+          <menupopup id="menu_FilePopup">
+            <menuitem id="menu_newNavigatorTab"
+                      data-l10n-id="translation-sites-column"
+                      command="cmd_newNavigatorTab"
+                      key="key_newNavigatorTab"/>
+          </menupopup>
+        </menu>
+       </menubar>
+    </toolbar>
+  `));
+
+  // Demonstrate dynamic changes to the menu updating system menu on OSX:
+  setInterval(() => {
+    let menu = document.querySelector("#file-menu");
+    let menuAttr = menu.getAttribute("data-l10n-id") === "translation-languages-button-remove" ?
+      "translation-languages-column" : "translation-languages-button-remove";
+    menu.setAttribute("data-l10n-id", menuAttr);
+
+    let menuitem = document.querySelector("#menu_newNavigatorTab");
+    let menuitemAttr = menuitem.getAttribute("data-l10n-id") === "translation-sites-column" ?
+      "translation-sites-button-remove" : "translation-sites-column";
+    menuitem.setAttribute("data-l10n-id", menuitemAttr);
+  }, 2000);
+
+  document.addEventListener("contextmenu", (e) => {
+    let doc = document;
+    doc.documentElement.prepend(parseXULDOM(`<popupset><menupopup onpopupshowing='console.log("popupshowing");'><menu label="foo"><menuitem label="bar"></menuitem></menu></menupopup></popupset>`));
+    let popupset = doc.querySelector("popupset");
+    let popup = doc.querySelector("menupopup");
+    console.log(popupset, popup, e.screenX, e.screenY, popup.innerHTML);
+    popup.openPopupAtScreen(e.screenX, e.screenY, true);
+  });
+  </script>
+</body>
+</html>
\ No newline at end of file
--- a/browser/base/jar.mn
+++ b/browser/base/jar.mn
@@ -70,16 +70,17 @@ browser.jar:
         content/browser/defaultthemes/4.icon.png      (content/defaultthemes/4.icon.png)
         content/browser/defaultthemes/4.preview.png   (content/defaultthemes/4.preview.png)
         content/browser/defaultthemes/5.header.png    (content/defaultthemes/5.header.png)
         content/browser/defaultthemes/5.icon.jpg      (content/defaultthemes/5.icon.jpg)
         content/browser/defaultthemes/5.preview.jpg   (content/defaultthemes/5.preview.jpg)
         content/browser/defaultthemes/dark.icon.svg   (content/defaultthemes/dark.icon.svg)
         content/browser/defaultthemes/light.icon.svg  (content/defaultthemes/light.icon.svg)
         content/browser/history-swipe-arrow.svg       (content/history-swipe-arrow.svg)
+        content/browser/menu-tester.html              (content/menu-tester.html)
 *       content/browser/pageinfo/pageInfo.xul         (content/pageinfo/pageInfo.xul)
         content/browser/pageinfo/pageInfo.js          (content/pageinfo/pageInfo.js)
         content/browser/pageinfo/pageInfo.css         (content/pageinfo/pageInfo.css)
         content/browser/pageinfo/feeds.js             (content/pageinfo/feeds.js)
         content/browser/pageinfo/permissions.js       (content/pageinfo/permissions.js)
         content/browser/pageinfo/security.js          (content/pageinfo/security.js)
         content/browser/content-refreshblocker.js     (content/content-refreshblocker.js)
         content/browser/robot.ico                     (content/robot.ico)