Bug 1500626 - Convert <menuitem> bindings to a Custom Element r=surkov
authorBrian Grinstead <bgrinstead@mozilla.com>
Thu, 02 May 2019 19:28:18 +0000
changeset 531159 37d758a90ed9fc26c1d09993ddf73823295ea5d0
parent 531158 ff20472890a1e3fa12e9b24bd9ef0520faea54da
child 531160 a21eae6d674f1c5961414e2f260b3fb35bbe2995
push id11265
push userffxbld-merge
push dateMon, 13 May 2019 10:53:39 +0000
treeherdermozilla-beta@77e0fe8dbdd3 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerssurkov
bugs1500626
milestone68.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 1500626 - Convert <menuitem> bindings to a Custom Element r=surkov Differential Revision: https://phabricator.services.mozilla.com/D9322
browser/components/preferences/browserLanguages.js
devtools/client/shared/test/browser_tableWidget_mouse_interaction.js
dom/tests/mochitest/general/test_offsets.js
layout/reftests/xul/reftest.list
toolkit/content/jar.mn
toolkit/content/tests/chrome/test_menu_hide.xul
toolkit/content/tests/chrome/test_menuitem_blink.xul
toolkit/content/tests/chrome/test_menulist_position.xul
toolkit/content/widgets/menu.js
toolkit/content/widgets/menu.xml
toolkit/content/widgets/popup.xml
toolkit/content/xul.css
toolkit/mozapps/extensions/content/extensions.xml
toolkit/themes/linux/mozapps/viewsource/viewsource.css
--- a/browser/components/preferences/browserLanguages.js
+++ b/browser/components/preferences/browserLanguages.js
@@ -245,18 +245,19 @@ class SortedItemSelectList {
     items.splice(i, 0, item);
     popup.insertBefore(this.createItem(item), menulist.getItemAtIndex(i));
 
     menulist.disabled = menulist.itemCount == 0;
   }
 
   createItem({label, value, className, disabled}) {
     let item = document.createElement("menuitem");
-    item.value = value;
     item.setAttribute("label", label);
+    if (value)
+      item.value = value;
     if (className)
       item.classList.add(className);
     if (disabled)
       item.setAttribute("disabled", "true");
     return item;
   }
 
   /**
--- a/devtools/client/shared/test/browser_tableWidget_mouse_interaction.js
+++ b/devtools/client/shared/test/browser_tableWidget_mouse_interaction.js
@@ -193,21 +193,22 @@ var testMouseInteraction = async functio
   // hiding second column
   // event listener for popupshown
   info("right click on the first column header");
   node = table.tbody.firstChild.firstChild.firstChild;
   let onPopupShown = once(table.menupopup, "popupshown");
   click(node, 2);
   await onPopupShown;
 
-  is(table.menupopup.querySelectorAll("[disabled]").length, 1,
+  is(table.menupopup.querySelectorAll("menuitem[disabled]").length, 1,
      "Only 1 menuitem is disabled");
-  is(table.menupopup.querySelector("[disabled]"),
+  is(table.menupopup.querySelector("menuitem[disabled]"),
      table.menupopup.querySelector("[data-id='col1']"),
      "Which is the unique column");
+
   // popup should be open now
   // clicking on second column label
   let onPopupHidden = once(table.menupopup, "popuphidden");
   event = table.once(TableWidget.EVENTS.HEADER_CONTEXT_MENU);
   node = table.menupopup.querySelector("[data-id='col2']");
   info("selecting to hide the second column");
   ok(!table.tbody.children[2].hasAttribute("hidden"),
      "Column is not hidden before hiding it");
@@ -221,17 +222,17 @@ var testMouseInteraction = async functio
   // hiding third column
   // event listener for popupshown
   info("right clicking on the first column header");
   node = table.tbody.firstChild.firstChild.firstChild;
   onPopupShown = once(table.menupopup, "popupshown");
   click(node, 2);
   await onPopupShown;
 
-  is(table.menupopup.querySelectorAll("[disabled]").length, 1,
+  is(table.menupopup.querySelectorAll("menuitem[disabled]").length, 1,
      "Only 1 menuitem is disabled");
   // popup should be open now
   // clicking on second column label
   onPopupHidden = once(table.menupopup, "popuphidden");
   event = table.once(TableWidget.EVENTS.HEADER_CONTEXT_MENU);
   node = table.menupopup.querySelector("[data-id='col3']");
   info("selecting to hide the second column");
   ok(!table.tbody.children[4].hasAttribute("hidden"),
@@ -246,22 +247,22 @@ var testMouseInteraction = async functio
   // opening again to see if 2 items are disabled now
   // event listener for popupshown
   info("right clicking on the first column header");
   node = table.tbody.firstChild.firstChild.firstChild;
   onPopupShown = once(table.menupopup, "popupshown");
   click(node, 2);
   await onPopupShown;
 
-  is(table.menupopup.querySelectorAll("[disabled]").length, 2,
+  is(table.menupopup.querySelectorAll("menuitem[disabled]").length, 2,
      "2 menuitems are disabled now as only 2 columns remain visible");
-  is(table.menupopup.querySelectorAll("[disabled]")[0],
+  is(table.menupopup.querySelectorAll("menuitem[disabled]")[0],
      table.menupopup.querySelector("[data-id='col1']"),
      "First is the unique column");
-  is(table.menupopup.querySelectorAll("[disabled]")[1],
+  is(table.menupopup.querySelectorAll("menuitem[disabled]")[1],
      table.menupopup.querySelector("[data-id='col4']"),
      "Second is the last column");
 
   // showing back 2nd column
   // popup should be open now
   // clicking on second column label
   onPopupHidden = once(table.menupopup, "popuphidden");
   event = table.once(TableWidget.EVENTS.HEADER_CONTEXT_MENU);
--- a/dom/tests/mochitest/general/test_offsets.js
+++ b/dom/tests/mochitest/general/test_offsets.js
@@ -4,17 +4,17 @@ function testElements(baseid, callback)
 {
   scrollbarWidth = scrollbarHeight = gcs($("scrollbox-test"), "width");
 
   var elements = $(baseid).getElementsByTagName("*");
   for (var t = 0; t < elements.length; t++) {
     var element = elements[t];
 
     // Ignore presentational content inside menus
-    if (element.closest("menu") && element.closest("[aria-hidden=true]")) {
+    if (element.closest("menu, menuitem") && element.closest("[aria-hidden=true]")) {
       continue;
     }
 
     // Ignore content inside a <button>  This can be removed if/when
     // button switches to use shadow DOM.
     let buttonParent = element.closest("button");
     if (buttonParent && buttonParent !== element) {
       continue;
--- a/layout/reftests/xul/reftest.list
+++ b/layout/reftests/xul/reftest.list
@@ -1,14 +1,14 @@
 == css-flex-1.xul css-flex-1-ref.html
 
 == menuitem-key.xul menuitem-key-ref.xul
 # these random-if(Android) are due to differences between Android Native & Xul, see bug 732569
-random-if(Android) == menulist-shrinkwrap-1.xul menulist-shrinkwrap-1-ref.xul
-random-if(Android) == menulist-shrinkwrap-2.xul menulist-shrinkwrap-2-ref.xul
+random-if(Android) == chrome://reftest/content/xul/menulist-shrinkwrap-1.xul chrome://reftest/content/xul/menulist-shrinkwrap-1-ref.xul
+random-if(Android) == chrome://reftest/content/xul/menulist-shrinkwrap-2.xul chrome://reftest/content/xul/menulist-shrinkwrap-2-ref.xul
 == textbox-overflow-1.xul textbox-overflow-1-ref.xul # for bug 749658
 # accesskeys are not normally displayed on Mac, so set a pref to enable them
 pref(ui.key.menuAccessKey,18) == chrome://reftest/content/xul/accesskey.xul accesskey-ref.xul
 pref(layout.css.xul-tree-pseudos.content.enabled,true) fuzzy-if(xulRuntime.widgetToolkit=="gtk3",0-1,0-11) == tree-row-outline-1.xul tree-row-outline-1-ref.xul # win8: bug 1254832
 skip-if(!cocoaWidget) fails-if(webrender&&cocoaWidget) == chrome://reftest/content/xul/mac-tab-toolbar.xul chrome://reftest/content/xul/mac-tab-toolbar-ref.xul
 pref(layout.css.xul-tree-pseudos.content.enabled,true) != tree-row-outline-1.xul tree-row-outline-1-notref.xul
 == text-crop.xul text-crop-ref.xul
 random-if(/^Windows\x20NT\x206\.1/.test(http.oscpu)) == text-small-caps-1.xul text-small-caps-1-ref.xul
--- a/toolkit/content/jar.mn
+++ b/toolkit/content/jar.mn
@@ -64,17 +64,16 @@ toolkit.jar:
    content/global/bindings/autocomplete.xml    (widgets/autocomplete.xml)
    content/global/bindings/button.xml          (widgets/button.xml)
    content/global/bindings/calendar.js         (widgets/calendar.js)
    content/global/bindings/datekeeper.js       (widgets/datekeeper.js)
    content/global/bindings/datepicker.js       (widgets/datepicker.js)
    content/global/bindings/datetimebox.css     (widgets/datetimebox.css)
 *  content/global/bindings/dialog.xml          (widgets/dialog.xml)
    content/global/bindings/general.xml         (widgets/general.xml)
-   content/global/bindings/menu.xml            (widgets/menu.xml)
    content/global/bindings/popup.xml           (widgets/popup.xml)
    content/global/bindings/richlistbox.xml     (widgets/richlistbox.xml)
    content/global/bindings/scrollbox.xml       (widgets/scrollbox.xml)
    content/global/bindings/spinner.js          (widgets/spinner.js)
    content/global/bindings/tabbox.xml          (widgets/tabbox.xml)
 *  content/global/bindings/textbox.xml         (widgets/textbox.xml)
    content/global/bindings/timekeeper.js       (widgets/timekeeper.js)
    content/global/bindings/timepicker.js       (widgets/timepicker.js)
--- a/toolkit/content/tests/chrome/test_menu_hide.xul
+++ b/toolkit/content/tests/chrome/test_menu_hide.xul
@@ -21,35 +21,44 @@
 
 <script class="testbody" type="application/javascript">
 <![CDATA[
 
 SimpleTest.waitForExplicitFinish();
 
 function runTests()
 {
-  var menu = $("menu");
+  let menu = $("menu");
+  let menuitemAddedWhileHidden = menu.appendItem("Added while hidden");
+  ok(!menuitemAddedWhileHidden.querySelector(".menu-text"), "hidden menuitem hasn't rendered yet.");
+
   menu.menupopup.addEventListener("popupshown", () => {
-    var submenu = $("submenu");
+    is(menuitemAddedWhileHidden.querySelector(".menu-text").value, "Added while hidden",
+      "menuitemAddedWhileHidden item has rendered after shown.");
+    let menuitemAddedWhileShown = menu.appendItem("Added while shown");
+    is(menuitemAddedWhileShown.querySelector(".menu-text").value, "Added while shown",
+      "menuitemAddedWhileShown item has eagerly rendered.");
+
+    let submenu = $("submenu");
     is(submenu.querySelector(".menu-text").value, "One", "submenu has rendered.");
 
-    var submenuDynamic = document.createXULElement("menu");
+    let submenuDynamic = document.createXULElement("menu");
     submenuDynamic.setAttribute("label", "Dynamic");
-    ok(!submenuDynamic.querySelector(".menu-text"), "dynamic subment hasn't rendered yet.");
+    ok(!submenuDynamic.querySelector(".menu-text"), "dynamic submenu hasn't rendered yet.");
     menu.menupopup.append(submenuDynamic);
     is(submenuDynamic.querySelector(".menu-text").value, "Dynamic", "dynamic submenu has rendered.");
 
     menu.menupopup.firstElementChild.open = true;
   }, { once: true });
   menu.open = true;
 }
 
 function submenuOpened()
 {
-  var submenu = $("submenu");
+  let submenu = $("submenu");
   is(submenu.getAttribute('_moz-menuactive'), "true", "menu highlighted");
   submenu.hidden = true;
   $("menu").open = false;
 }
 
 function done()
 {
   ok(!$("submenu").hasAttribute('_moz-menuactive'), "menu unhighlighted");
--- a/toolkit/content/tests/chrome/test_menuitem_blink.xul
+++ b/toolkit/content/tests/chrome/test_menuitem_blink.xul
@@ -44,16 +44,19 @@ function test_crash(when, andThen) {
   var menuitem = document.getElementById("menuitem");
   var attrChanges = { "REMOVAL": 0, "ADDITION": 0 };
   var storedEvent = null;
   menupopup.addEventListener("popupshown", function () {
     menupopup.removeEventListener("popupshown", arguments.callee, false);
     menuitem.addEventListener("mouseup", function (e) {
       menuitem.removeEventListener("mouseup", arguments.callee, true);
       menuitem.addEventListener("DOMAttrModified", function (e) {
+        if (e.target != e.currentTarget) {
+          return;
+        }
         if (e.attrName == "_moz-menuactive") {
           if (!attrChanges[e.attrChange])
             attrChanges[e.attrChange] = 1;
           else
             attrChanges[e.attrChange]++;
           storedEvent = e;
           if (e.attrChange == e[when]) {
             menuitem.hidden = true;
--- a/toolkit/content/tests/chrome/test_menulist_position.xul
+++ b/toolkit/content/tests/chrome/test_menulist_position.xul
@@ -37,17 +37,17 @@ function popupShown()
 
   let marginLeft = parseFloat(getComputedStyle(menulist.menupopup).marginLeft);
   ok(isWithinHalfPixel(menurect.left + marginLeft, popuprect.left), "left position");
   ok(isWithinHalfPixel(menurect.right + marginLeft, popuprect.right), "right position");
 
   let index = menulist.selectedIndex;
   if (menulist.selectedItem && navigator.platform.includes("Mac")) {
     let menulistlabel = menulist.querySelector(".menulist-label");
-    let mitemlabel = document.getAnonymousElementByAttribute(menulist.selectedItem, "class", "menu-iconic-text");
+    let mitemlabel = menulist.selectedItem.querySelector(".menu-iconic-text");
 
     ok(isWithinHalfPixel(menulistlabel.getBoundingClientRect().left,
                          mitemlabel.getBoundingClientRect().left),
        "Labels horizontally aligned for index " + index);
     ok(isWithinHalfPixel(menulistlabel.getBoundingClientRect().top,
                          mitemlabel.getBoundingClientRect().top),
        "Labels vertically aligned for index " + index);
 
--- a/toolkit/content/widgets/menu.js
+++ b/toolkit/content/widgets/menu.js
@@ -134,27 +134,123 @@ class MozMenuCaption extends MozMenuBase
       <label class="menu-iconic-highlightable-text" crop="right" aria-hidden="true"></label>
     `));
     this.initializeAttributeInheritance();
   }
 }
 
 customElements.define("menucaption", MozMenuCaption);
 
-// In general, wait to render menus inside menupopups until they are going to be visible:
+// In general, wait to render menus and menuitems inside menupopups
+// until they are going to be visible:
 window.addEventListener("popupshowing", (e) => {
   if (e.originalTarget.ownerDocument != document) {
     return;
   }
   e.originalTarget.setAttribute("hasbeenopened", "true");
-  for (let menu of e.originalTarget.querySelectorAll("menu")) {
-    menu.render();
+  for (let el of e.originalTarget.querySelectorAll("menuitem, menu")) {
+    el.render();
   }
 }, { capture: true });
 
+class MozMenuItem extends MozMenuItemBaseMixin(MozXULElement) {
+  static get inheritedAttributes() {
+    return {
+      ".menu-iconic-text": "value=label,crop,accesskey,highlightable",
+      ".menu-text": "value=label,crop,accesskey,highlightable",
+      ".menu-iconic-highlightable-text": "text=label,crop,accesskey,highlightable",
+      ".menu-iconic-left": "selected,_moz-menuactive,disabled,checked",
+      ".menu-iconic-icon": "src=image,validate,triggeringprincipal=iconloadingprincipal",
+      ".menu-iconic-accel": "value=acceltext",
+      ".menu-accel": "value=acceltext",
+    };
+  }
+
+  static get iconicNoAccelFragment() {
+    // Add aria-hidden="true" on all DOM, since XULMenuAccessible handles accessibility here.
+    let frag = document.importNode(MozXULElement.parseXULToFragment(`
+      <hbox class="menu-iconic-left" align="center" pack="center" aria-hidden="true">
+        <image class="menu-iconic-icon"/>
+      </hbox>
+      <label class="menu-iconic-text" flex="1" crop="right" aria-hidden="true"/>
+      <label class="menu-iconic-highlightable-text" crop="right" aria-hidden="true"/>
+    `), true);
+    Object.defineProperty(this, "iconicNoAccelFragment", {value: frag});
+    return frag;
+  }
+
+  static get iconicFragment() {
+    let frag = document.importNode(MozXULElement.parseXULToFragment(`
+      <hbox class="menu-iconic-left" align="center" pack="center" aria-hidden="true">
+        <image class="menu-iconic-icon"/>
+      </hbox>
+      <label class="menu-iconic-text" flex="1" crop="right" aria-hidden="true"/>
+      <label class="menu-iconic-highlightable-text" crop="right" aria-hidden="true"/>
+      <hbox class="menu-accel-container" aria-hidden="true">
+        <label class="menu-iconic-accel"/>
+      </hbox>
+    `), true);
+    Object.defineProperty(this, "iconicFragment", {value: frag});
+    return frag;
+  }
+
+  static get plainFragment() {
+    let frag = document.importNode(MozXULElement.parseXULToFragment(`
+      <label class="menu-text" crop="right" aria-hidden="true"/>
+      <hbox class="menu-accel-container" aria-hidden="true">
+        <label class="menu-accel"/>
+      </hbox>
+    `), true);
+    Object.defineProperty(this, "plainFragment", {value: frag});
+    return frag;
+  }
+
+  get isIconic() {
+    let type = this.getAttribute("type");
+    return type == "checkbox" || type == "radio" || this.classList.contains("menuitem-iconic");
+  }
+
+  get isMenulistChild() {
+    return this.matches("menulist > menupopup > menuitem");
+  }
+
+  get isInHiddenMenupopup() {
+    return this.matches("menupopup:not([hasbeenopened]) menuitem");
+  }
+
+  render() {
+    if (this.renderedOnce) {
+      return;
+    }
+    this.renderedOnce = true;
+    this.textContent = "";
+    if (this.isMenulistChild) {
+      this.append(this.constructor.iconicNoAccelFragment.cloneNode(true));
+    } else if (this.isIconic) {
+      this.append(this.constructor.iconicFragment.cloneNode(true));
+    } else {
+      this.append(this.constructor.plainFragment.cloneNode(true));
+    }
+
+    this.initializeAttributeInheritance();
+  }
+
+  connectedCallback() {
+    // Eagerly render if we are being inserted into a menulist (since we likely need to
+    // size it), or into an already-opened menupopup (since we are already visible).
+    // Checking isConnectedAndReady is an optimization that will let us quickly skip
+    // non-menulists that are being connected during parse.
+    if (this.isMenulistChild || (this.isConnectedAndReady && !this.isInHiddenMenupopup)) {
+      this.render();
+    }
+  }
+}
+
+customElements.define("menuitem", MozMenuItem);
+
 const isHiddenWindow = document.documentURI == "chrome://browser/content/hiddenWindow.xul";
 
 class MozMenu extends MozMenuBaseMixin(MozElements.MozElementMixin(XULMenuElement)) {
   static get inheritedAttributes() {
     return {
       ".menubar-text": "value=label,accesskey,crop",
       ".menu-iconic-text": "value=label,accesskey,crop,highlightable",
       ".menu-text": "value=label,accesskey,crop",
@@ -163,25 +259,25 @@ class MozMenu extends MozMenuBaseMixin(M
       ".menu-iconic-icon": "src=image,triggeringprincipal=iconloadingprincipal,validate",
       ".menu-iconic-accel": "value=acceltext",
       ".menu-right": "_moz-menuactive,disabled",
       ".menu-accel": "value=acceltext",
     };
   }
 
   get needsEagerRender() {
-    return this.isMenubarChild || this.isSizingPopup || !this.isInHiddenMenupopup;
+    return this.isMenubarChild || this.isMenulistChild || !this.isInHiddenMenupopup;
   }
 
   get isMenubarChild() {
     return this.matches("menubar > menu");
   }
 
-  get isSizingPopup() {
-    return this.matches("[sizetopopup] menu") || this.matches("menulist menu");
+  get isMenulistChild() {
+    return this.matches("menulist > menupopup > menu");
   }
 
   get isInHiddenMenupopup() {
     return this.matches("menupopup:not([hasbeenopened]) menu");
   }
 
   get isIconic() {
     return this.classList.contains("menu-iconic");
deleted file mode 100644
--- a/toolkit/content/widgets/menu.xml
+++ /dev/null
@@ -1,81 +0,0 @@
-<?xml version="1.0"?>
-<!-- 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/. -->
-<!-- globals XULMenuElement -->
-
-<bindings id="menuitemBindings"
-   xmlns="http://www.mozilla.org/xbl"
-   xmlns:xul="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
-   xmlns:xbl="http://www.mozilla.org/xbl">
-
-  <binding id="menuitem-base"
-           extends="chrome://global/content/bindings/general.xml#basetext">
-    <implementation implements="nsIDOMXULSelectControlItemElement, nsIDOMXULContainerItemElement">
-      <property name="value" onset="this.setAttribute('value', val); return val;"
-                             onget="return this.getAttribute('value');"/>
-      <!-- nsIDOMXULSelectControlItemElement -->
-      <property name="selected" readonly="true"
-                onget="return this.getAttribute('selected') == 'true';"/>
-      <property name="control" readonly="true">
-        <getter>
-          <![CDATA[
-            var parent = this.parentNode;
-            // Return the parent if it is a menu or menulist.
-            if (parent && parent.parentNode instanceof XULMenuElement) {
-              return parent.parentNode;
-            }
-            return null;
-          ]]>
-        </getter>
-      </property>
-
-      <!-- nsIDOMXULContainerItemElement -->
-      <property name="parentContainer" readonly="true">
-        <getter>
-          for (var parent = this.parentNode; parent; parent = parent.parentNode) {
-            if (parent instanceof XULMenuElement) {
-              return parent;
-            }
-          }
-          return null;
-        </getter>
-      </property>
-    </implementation>
-  </binding>
-
-  <binding id="menuitem" extends="chrome://global/content/bindings/menu.xml#menuitem-base">
-    <content>
-      <xul:label class="menu-text" xbl:inherits="value=label,accesskey,crop,highlightable" crop="right"/>
-      <xul:hbox class="menu-accel-container" anonid="accel">
-        <xul:label class="menu-accel" xbl:inherits="value=acceltext"/>
-      </xul:hbox>
-    </content>
-  </binding>
-
-  <binding id="menuitem-iconic" extends="chrome://global/content/bindings/menu.xml#menuitem">
-    <content>
-      <xul:hbox class="menu-iconic-left" align="center" pack="center"
-                xbl:inherits="selected,_moz-menuactive,disabled,checked">
-        <xul:image class="menu-iconic-icon" xbl:inherits="src=image,triggeringprincipal=iconloadingprincipal,validate"/>
-      </xul:hbox>
-      <xul:label class="menu-iconic-text" flex="1" xbl:inherits="value=label,accesskey,crop,highlightable" crop="right"/>
-      <xul:label class="menu-iconic-highlightable-text" xbl:inherits="xbl:text=label,crop,accesskey,highlightable" crop="right"/>
-      <xul:hbox class="menu-accel-container" anonid="accel">
-        <xul:label class="menu-iconic-accel" xbl:inherits="value=acceltext"/>
-      </xul:hbox>
-    </content>
-  </binding>
-
-  <binding id="menuitem-iconic-noaccel" extends="chrome://global/content/bindings/menu.xml#menuitem">
-    <content>
-      <xul:hbox class="menu-iconic-left" align="center" pack="center"
-                xbl:inherits="selected,disabled,checked">
-        <xul:image class="menu-iconic-icon" xbl:inherits="src=image,validate"/>
-      </xul:hbox>
-      <xul:label class="menu-iconic-text" flex="1" xbl:inherits="value=label,accesskey,crop,highlightable" crop="right"/>
-      <xul:label class="menu-iconic-highlightable-text" xbl:inherits="xbl:text=label,crop,accesskey,highlightable" crop="right"/>
-    </content>
-  </binding>
-
-</bindings>
--- a/toolkit/content/widgets/popup.xml
+++ b/toolkit/content/widgets/popup.xml
@@ -151,17 +151,17 @@
 
     <handlers>
       <handler event="popupshowing" phase="target">
         <![CDATA[
           var array = [];
           var width = 0;
           for (var menuitem = this.firstElementChild; menuitem; menuitem = menuitem.nextElementSibling) {
             if (menuitem.localName == "menuitem" && menuitem.hasAttribute("acceltext")) {
-              var accel = document.getAnonymousElementByAttribute(menuitem, "anonid", "accel");
+              var accel = menuitem.menuAccel;
               if (accel) {
                 array.push(accel);
                 let accelWidth = accel.getBoundingClientRect().width;
                 if (accelWidth > width) {
                   width = accelWidth;
                 }
               }
             }
--- a/toolkit/content/xul.css
+++ b/toolkit/content/xul.css
@@ -220,31 +220,16 @@ toolbarspring {
 }
 
 /********* menu ***********/
 
 menubar > menu:empty {
   visibility: collapse;
 }
 
-/********* menuitem ***********/
-
-menuitem {
-  -moz-binding: url("chrome://global/content/bindings/menu.xml#menuitem");
-}
-
-menuitem.menuitem-iconic {
-  -moz-binding: url("chrome://global/content/bindings/menu.xml#menuitem-iconic");
-}
-
-menuitem[type="checkbox"],
-menuitem[type="radio"] {
-  -moz-binding: url("chrome://global/content/bindings/menu.xml#menuitem-iconic");
-}
-
 .menu-text {
   -moz-box-flex: 1;
 }
 
 /********* menupopup, panel, & tooltip ***********/
 
 menupopup {
   -moz-binding: url("chrome://global/content/bindings/popup.xml#popup");
@@ -538,20 +523,16 @@ panel[type="autocomplete-richlistbox"] {
 menulist[popuponly="true"] {
   -moz-appearance: none !important;
   margin: 0 !important;
   height: 0 !important;
   min-height: 0 !important;
   border: 0 !important;
 }
 
-menulist > menupopup > menuitem {
-  -moz-binding: url("chrome://global/content/bindings/menu.xml#menuitem-iconic-noaccel");
-}
-
 menulist > menupopup > .popup-internal-box > .scrollbutton-up,
 menulist > menupopup > .popup-internal-box > .arrowscrollbox-overflow-start-indicator,
 menulist > menupopup > .popup-internal-box > .arrowscrollbox-overflow-end-indicator,
 menulist > menupopup > .popup-internal-box > .scrollbutton-down {
   display: none;
 }
 
 menulist > menupopup > .popup-internal-box > .arrowscrollbox-scrollbox {
--- a/toolkit/mozapps/extensions/content/extensions.xml
+++ b/toolkit/mozapps/extensions/content/extensions.xml
@@ -689,16 +689,19 @@
       </xul:hbox>
     </content>
 
     <implementation>
       <constructor><![CDATA[
         window.customElements.upgrade(this._stateMenulist);
         window.customElements.upgrade(this._enableBtn);
         window.customElements.upgrade(this._disableBtn);
+        window.customElements.upgrade(this._askToActivateMenuitem);
+        window.customElements.upgrade(this._alwaysActivateMenuitem);
+        window.customElements.upgrade(this._neverActivateMenuitem);
 
         this._installStatus = document.getAnonymousElementByAttribute(this, "anonid", "install-status");
         this._installStatus.mControl = this;
 
         this.setAttribute("contextmenu", "addonitem-popup");
 
         this._showStatus("none");
 
--- a/toolkit/themes/linux/mozapps/viewsource/viewsource.css
+++ b/toolkit/themes/linux/mozapps/viewsource/viewsource.css
@@ -1,17 +1,12 @@
 /* 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/. */
 
-/* Stock icons for the menu bar items */
-menuitem:not([type]) {
-  -moz-binding: url("chrome://global/content/bindings/menu.xml#menuitem-iconic");
-}
-
 #menu_savePage {
   list-style-image: url("moz-icon://stock/gtk-save-as?size=menu");
 }
 
 #menu_printPreview {
   list-style-image: url("moz-icon://stock/gtk-print-preview?size=menu");
 }