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
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)