Bug 1470910 - Migrate input-box and input-box-spell to a Custom Element;r=paolo draft
authorBrian Grinstead <bgrinstead@mozilla.com>
Thu, 02 Aug 2018 10:35:13 -0700
changeset 825916 2a6fd4d8aaaadbcc3a6ac38fa9948dcbdf801d2a
parent 825915 a83ecfb1eeb59014dcd35a0441f9abd2f8918454
push id118206
push userbgrinstead@mozilla.com
push dateThu, 02 Aug 2018 17:35:29 +0000
reviewerspaolo
bugs1470910
milestone63.0a1
Bug 1470910 - Migrate input-box and input-box-spell to a Custom Element;r=paolo Instead of `<xul:hbox class="textbox-input-box">`, consumers now should use `<xul:moz-input-box />`. This covers the normal case and also handles [spellcheck=true] while sharing much of the code within one class. MozReview-Commit-ID: 3Rb8HHJWMuV
browser/base/content/test/contextMenu/browser_contextmenu_touch.js
browser/base/content/test/performance/browser_appmenu.js
browser/base/content/test/performance/browser_tabopen.js
browser/base/content/test/performance/browser_tabopen_squeeze.js
browser/base/content/test/performance/browser_tabstrip_overflow_underflow.js
browser/base/content/test/performance/browser_urlbar_keyed_search.js
browser/base/content/test/performance/browser_urlbar_search.js
browser/base/content/test/urlbar/browser_pasteAndGo.js
browser/base/content/urlbarBindings.xml
browser/components/customizableui/test/browser_940307_panel_click_closure_handling.js
browser/components/search/content/search.xml
browser/components/search/test/browser_searchbar_openpopup.js
browser/themes/osx/browser.css
browser/themes/windows/browser.css
testing/marionette/puppeteer/firefox/firefox_puppeteer/ui/browser/toolbars.py
toolkit/content/customElements.js
toolkit/content/tests/chrome/test_textbox_dictionary.xul
toolkit/content/widgets/autocomplete.xml
toolkit/content/widgets/menulist.xml
toolkit/content/widgets/numberbox.xml
toolkit/content/widgets/textbox.js
toolkit/content/widgets/textbox.xml
toolkit/content/xul.css
toolkit/themes/linux/global/autocomplete.css
toolkit/themes/linux/global/textbox.css
toolkit/themes/osx/global/autocomplete.css
toolkit/themes/osx/global/in-content/common.css
toolkit/themes/osx/global/textbox.css
toolkit/themes/windows/global/autocomplete.css
toolkit/themes/windows/global/textbox.css
--- a/browser/base/content/test/contextMenu/browser_contextmenu_touch.js
+++ b/browser/base/content/test/contextMenu/browser_contextmenu_touch.js
@@ -71,13 +71,12 @@ add_task(async function test_toolbar_con
   let target = document.getElementById("PanelUI-menu-button");
   await openAndCheckContextMenu(toolbarContextMenu, target);
 });
 
 // Test the urlbar input context menu.
 add_task(async function test_urlbar_contextmenu_touch() {
   let urlbar = document.getElementById("urlbar");
   let textBox = document.getAnonymousElementByAttribute(urlbar,
-                                      "anonid", "textbox-input-box");
-  let menu = document.getAnonymousElementByAttribute(textBox,
-                                      "anonid", "input-box-contextmenu");
+                                      "anonid", "moz-input-box");
+  let menu = textBox.menupopup;
   await openAndCheckContextMenu(menu, textBox);
 });
--- a/browser/base/content/test/performance/browser_appmenu.js
+++ b/browser/base/content/test/performance/browser_appmenu.js
@@ -38,17 +38,17 @@ const EXPECTED_APPMENU_OPEN_REFLOWS = [
     maxCount: 7, // This number should only ever go down - never up.
   },
 ];
 
 add_task(async function() {
   await ensureNoPreloadedBrowser();
 
   let textBoxRect = document.getAnonymousElementByAttribute(gURLBar,
-    "anonid", "textbox-input-box").getBoundingClientRect();
+    "anonid", "moz-input-box").getBoundingClientRect();
   let menuButtonRect =
     document.getElementById("PanelUI-menu-button").getBoundingClientRect();
   let frameExpectations = {
     filter: rects => rects.filter(r => !(
       // We expect the menu button to get into the active state.
       r.y1 >= menuButtonRect.top && r.y2 <= menuButtonRect.bottom &&
       r.x1 >= menuButtonRect.left && r.x2 <= menuButtonRect.right
       // XXX For some reason the menu panel isn't in our screenshots,
--- a/browser/base/content/test/performance/browser_tabopen.js
+++ b/browser/base/content/test/performance/browser_tabopen.js
@@ -30,17 +30,17 @@ add_task(async function() {
   await ensureFocusedUrlbar();
 
   let tabStripRect = gBrowser.tabContainer.arrowScrollbox.getBoundingClientRect();
   let firstTabRect = gBrowser.selectedTab.getBoundingClientRect();
   let firstTabLabelRect =
     document.getAnonymousElementByAttribute(gBrowser.selectedTab, "anonid", "tab-label")
             .getBoundingClientRect();
   let textBoxRect = document.getAnonymousElementByAttribute(gURLBar,
-    "anonid", "textbox-input-box").getBoundingClientRect();
+    "anonid", "moz-input-box").getBoundingClientRect();
   let inRange = (val, min, max) => min <= val && val <= max;
 
   // Add a reflow observer and open a new tab.
   await withPerfObserver(async function() {
     let switchDone = BrowserTestUtils.waitForEvent(window, "TabSwitchDone");
     BrowserOpenTab();
     await BrowserTestUtils.waitForEvent(gBrowser.selectedTab, "transitionend",
                                         false, e => e.propertyName === "max-width");
--- a/browser/base/content/test/performance/browser_tabopen_squeeze.js
+++ b/browser/base/content/test/performance/browser_tabopen_squeeze.js
@@ -30,17 +30,17 @@ add_task(async function() {
   const TAB_COUNT_FOR_SQUEEZE = computeMaxTabCount() - 1;
 
   await createTabs(TAB_COUNT_FOR_SQUEEZE);
 
   await ensureFocusedUrlbar();
 
   let tabStripRect = gBrowser.tabContainer.arrowScrollbox.getBoundingClientRect();
   let textBoxRect = document.getAnonymousElementByAttribute(gURLBar,
-    "anonid", "textbox-input-box").getBoundingClientRect();
+    "anonid", "moz-input-box").getBoundingClientRect();
 
   await withPerfObserver(async function() {
     let switchDone = BrowserTestUtils.waitForEvent(window, "TabSwitchDone");
     BrowserOpenTab();
     await BrowserTestUtils.waitForEvent(gBrowser.selectedTab, "transitionend",
       false, e => e.propertyName === "max-width");
     await switchDone;
   }, {expectedReflows: EXPECTED_REFLOWS,
--- a/browser/base/content/test/performance/browser_tabstrip_overflow_underflow.js
+++ b/browser/base/content/test/performance/browser_tabstrip_overflow_underflow.js
@@ -34,17 +34,17 @@ add_task(async function() {
   const TAB_COUNT_FOR_OVERFLOW = computeMaxTabCount();
 
   await createTabs(TAB_COUNT_FOR_OVERFLOW);
 
   await ensureFocusedUrlbar();
 
   let tabStripRect = gBrowser.tabContainer.arrowScrollbox.getBoundingClientRect();
   let textBoxRect = document.getAnonymousElementByAttribute(gURLBar,
-    "anonid", "textbox-input-box").getBoundingClientRect();
+    "anonid", "moz-input-box").getBoundingClientRect();
   let ignoreTabstripRects = {
     filter: rects => rects.filter(r => !(
       // We expect plenty of changed rects within the tab strip.
       r.y1 >= tabStripRect.top && r.y2 <= tabStripRect.bottom &&
       r.x1 >= tabStripRect.left && r.x2 <= tabStripRect.right
     )),
     exceptions: [
       {name: "the urlbar placeolder moves up and down by a few pixels",
--- a/browser/base/content/test/performance/browser_urlbar_keyed_search.js
+++ b/browser/base/content/test/performance/browser_urlbar_keyed_search.js
@@ -138,17 +138,17 @@ add_task(async function() {
   let popup = URLBar.popup;
 
   URLBar.focus();
   URLBar.value = "";
 
   let dropmarkerRect = document.getAnonymousElementByAttribute(gURLBar,
     "anonid", "historydropmarker").getBoundingClientRect();
   let textBoxRect = document.getAnonymousElementByAttribute(gURLBar,
-    "anonid", "textbox-input-box").getBoundingClientRect();
+    "anonid", "moz-input-box").getBoundingClientRect();
 
   await withPerfObserver(async function() {
     let oldInvalidate = popup.invalidate.bind(popup);
     let oldResultsAdded = popup.onResultsAdded.bind(popup);
 
     // We need to invalidate the frame tree outside of the normal
     // mechanism since invalidations and result additions to the
     // URL bar occur without firing JS events (which is how we
--- a/browser/base/content/test/performance/browser_urlbar_search.js
+++ b/browser/base/content/test/performance/browser_urlbar_search.js
@@ -158,17 +158,17 @@ add_task(async function() {
     let hiddenPromise = BrowserTestUtils.waitForEvent(URLBar.popup, "popuphidden");
     EventUtils.synthesizeKey("VK_ESCAPE", {}, win);
     await hiddenPromise;
   };
 
   let dropmarkerRect = document.getAnonymousElementByAttribute(gURLBar,
     "anonid", "historydropmarker").getBoundingClientRect();
   let textBoxRect = document.getAnonymousElementByAttribute(gURLBar,
-    "anonid", "textbox-input-box").getBoundingClientRect();
+    "anonid", "moz-input-box").getBoundingClientRect();
   let expectedRects = {
     filter: rects => rects.filter(r => !(
       // We put text into the urlbar so expect its textbox to change.
       (r.x1 >= textBoxRect.left && r.x2 <= textBoxRect.right &&
        r.y1 >= textBoxRect.top && r.y2 <= textBoxRect.bottom) ||
       // The dropmarker is displayed as active during some of the test.
       // dropmarkerRect.left isn't always an integer, hence the - 1 and + 1
       (r.x1 >= dropmarkerRect.left - 1 && r.x2 <= dropmarkerRect.right + 1 &&
--- a/browser/base/content/test/urlbar/browser_pasteAndGo.js
+++ b/browser/base/content/test/urlbar/browser_pasteAndGo.js
@@ -14,24 +14,22 @@ add_task(async function() {
       await new Promise((resolve, reject) => {
         waitForClipboard(url, function() {
           clipboardHelper.copyString(url);
         }, resolve,
           () => reject(new Error(`Failed to copy string '${url}' to clipboard`))
         );
       });
       let textBox = document.getAnonymousElementByAttribute(gURLBar,
-        "anonid", "textbox-input-box");
-      let cxmenu = document.getAnonymousElementByAttribute(textBox,
-        "anonid", "input-box-contextmenu");
+        "anonid", "moz-input-box");
+      let cxmenu = textBox.menupopup;
       let cxmenuPromise = BrowserTestUtils.waitForEvent(cxmenu, "popupshown");
       EventUtils.synthesizeMouseAtCenter(gURLBar, {type: "contextmenu", button: 2});
       await cxmenuPromise;
-      let menuitem = document.getAnonymousElementByAttribute(textBox,
-        "anonid", "paste-and-go");
+      let menuitem = textBox.getMenuItem("paste-and-go");
       let browserLoadedPromise = BrowserTestUtils.browserLoaded(browser, false, url.replace(/\n/g, ""));
       EventUtils.synthesizeMouseAtCenter(menuitem, {});
       // Using toSource in order to get the newlines escaped:
       info("Paste and go, loading " + url.toSource());
       await browserLoadedPromise;
       ok(true, "Successfully loaded " + url);
     });
   }
@@ -44,24 +42,22 @@ add_task(async function() {
     await new Promise((resolve, reject) => {
       waitForClipboard(url, function() {
         clipboardHelper.copyString(url);
       }, resolve,
         () => reject(new Error(`Failed to copy string '${url}' to clipboard`))
       );
     });
     let textBox = document.getAnonymousElementByAttribute(gURLBar,
-      "anonid", "textbox-input-box");
-    let cxmenu = document.getAnonymousElementByAttribute(textBox,
-      "anonid", "input-box-contextmenu");
+      "anonid", "moz-input-box");
+    let cxmenu = textBox.menupopup;
     let cxmenuPromise = BrowserTestUtils.waitForEvent(cxmenu, "popupshown");
     EventUtils.synthesizeMouseAtCenter(gURLBar, {type: "contextmenu", button: 2});
     await cxmenuPromise;
-    let menuitem = document.getAnonymousElementByAttribute(textBox,
-      "anonid", "paste-and-go");
+    let menuitem = textBox.getMenuItem("paste-and-go");
     let browserLoadedPromise = BrowserTestUtils.browserLoaded(browser, false, url.replace(/\u2028/g, ""));
     EventUtils.synthesizeMouseAtCenter(menuitem, {});
     // Using toSource in order to get the newlines escaped:
     info("Paste and go, loading " + url.toSource());
     await browserLoadedPromise;
     ok(true, "Successfully loaded " + url);
   });
 });
--- a/browser/base/content/urlbarBindings.xml
+++ b/browser/base/content/urlbarBindings.xml
@@ -21,30 +21,30 @@ file, You can obtain one at http://mozil
           xmlns:xul="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
           xmlns:xbl="http://www.mozilla.org/xbl">
 
   <binding id="urlbar" extends="chrome://global/content/bindings/autocomplete.xml#autocomplete">
 
     <content sizetopopup="pref">
       <xul:hbox flex="1" class="urlbar-textbox-container">
         <children includes="image|deck|stack|box"/>
-        <xul:hbox anonid="textbox-input-box"
-                  class="textbox-input-box urlbar-input-box"
+        <xul:moz-input-box anonid="moz-input-box"
+                  class="urlbar-input-box"
                   flex="1" xbl:inherits="tooltiptext=inputtooltiptext">
           <children/>
           <html:input anonid="scheme"
                       class="urlbar-scheme textbox-input"
                       required="required"
                       xbl:inherits="textoverflow,focused"/>
           <html:input anonid="input"
                       class="autocomplete-textbox urlbar-input textbox-input"
                       allowevents="true"
                       inputmode="mozAwesomebar"
                       xbl:inherits="tooltiptext=inputtooltiptext,value,maxlength,disabled,size,readonly,placeholder,tabindex,accesskey,focused,textoverflow"/>
-        </xul:hbox>
+        </xul:moz-input-box>
         <xul:image anonid="urlbar-go-button"
                    class="urlbar-go-button urlbar-icon"
                    onclick="gURLBar.handleCommand(event);"
                    tooltiptext="&goEndCap.tooltip;"
                    xbl:inherits="pageproxystate,parentfocused=focused,usertyping"/>
         <xul:dropmarker anonid="historydropmarker"
                         class="urlbar-history-dropmarker urlbar-icon chromeclass-toolbar-additional"
                         tooltiptext="&urlbar.openHistoryPopup.tooltip;"
@@ -90,19 +90,19 @@ file, You can obtain one at http://mozil
         this.inputField.addEventListener("mousedown", this);
         this.inputField.addEventListener("mousemove", this);
         this.inputField.addEventListener("mouseout", this);
         this.inputField.addEventListener("overflow", this);
         this.inputField.addEventListener("underflow", this);
         this.inputField.addEventListener("scrollend", this);
 
         var textBox = document.getAnonymousElementByAttribute(this,
-                                                "anonid", "textbox-input-box");
-        var cxmenu = document.getAnonymousElementByAttribute(textBox,
-                                            "anonid", "input-box-contextmenu");
+                                                "anonid", "moz-input-box");
+        customElements.upgrade(textBox);
+        var cxmenu = textBox.menupopup;
         var pasteAndGo;
         cxmenu.addEventListener("popupshowing", function() {
           if (!pasteAndGo)
             return;
           var controller = document.commandDispatcher.getControllerForCommand("cmd_paste");
           var enabled = controller.isCommandEnabled("cmd_paste");
           if (enabled)
             pasteAndGo.removeAttribute("disabled");
--- a/browser/components/customizableui/test/browser_940307_panel_click_closure_handling.js
+++ b/browser/components/customizableui/test/browser_940307_panel_click_closure_handling.js
@@ -40,18 +40,18 @@ add_task(async function searchbar_in_pan
   await SpecialPowers.pushPrefEnv({set: [["browser.search.suggest.enabled", false]]});
   let dontShowPopup = e => e.preventDefault();
   let searchbarPopup = searchbar.textbox.popup;
   searchbarPopup.addEventListener("popupshowing", dontShowPopup);
 
   searchbar.value = "foo";
   searchbar.focus();
   // Reaching into this context menu is pretty evil, but hey... it's a test.
-  let textbox = document.getAnonymousElementByAttribute(searchbar.textbox, "anonid", "textbox-input-box");
-  let contextmenu = document.getAnonymousElementByAttribute(textbox, "anonid", "input-box-contextmenu");
+  let textbox = document.getAnonymousElementByAttribute(searchbar.textbox, "anonid", "moz-input-box");
+  let contextmenu = textbox.menupopup;
   let contextMenuShown = promisePanelElementShown(window, contextmenu);
   EventUtils.synthesizeMouseAtCenter(searchbar, {type: "contextmenu", button: 2});
   await contextMenuShown;
 
   ok(isOverflowOpen(), "Panel should still be open");
 
   let selectAll = contextmenu.querySelector("[cmd='cmd_selectAll']");
   let contextMenuHidden = promisePanelElementHidden(window, contextmenu);
--- a/browser/components/search/content/search.xml
+++ b/browser/components/search/content/search.xml
@@ -498,19 +498,20 @@
         if (document.getBindingParent(this).parentNode.parentNode.localName ==
             "toolbarpaletteitem")
           return;
 
         if (Services.prefs.getBoolPref("browser.urlbar.clickSelectsAll"))
           this.setAttribute("clickSelectsAll", true);
 
         var textBox = document.getAnonymousElementByAttribute(this,
-                                              "anonid", "textbox-input-box");
-        var cxmenu = document.getAnonymousElementByAttribute(textBox,
-                                          "anonid", "input-box-contextmenu");
+                                              "anonid", "moz-input-box");
+
+        customElements.upgrade(textBox);
+        var cxmenu = textBox.menupopup;
         cxmenu.addEventListener("popupshowing",
                                 () => { this.initContextMenu(cxmenu); },
                                 {capture: true, once: true});
 
         this.setAttribute("aria-owns", this.popup.id);
         document.getBindingParent(this)._textboxInitialized = true;
       ]]></constructor>
 
--- a/browser/components/search/test/browser_searchbar_openpopup.js
+++ b/browser/components/search/test/browser_searchbar_openpopup.js
@@ -195,17 +195,17 @@ add_task(async function click_opens_popu
   textbox.value = "";
 });
 
 // Right clicking in a non-empty search box when unfocused should open the edit context menu.
 add_no_popup_task(async function right_click_doesnt_open_popup() {
   gURLBar.focus();
   textbox.value = "foo";
 
-  let contextPopup = document.getAnonymousElementByAttribute(textbox.inputField.parentNode, "anonid", "input-box-contextmenu");
+  let contextPopup = textbox.inputField.parentNode.menupopup;
   let promise = promiseEvent(contextPopup, "popupshown");
   context_click(textbox);
   await promise;
 
   is(Services.focus.focusedElement, textbox.inputField, "Should have focused the search bar");
   is(textbox.selectionStart, 0, "Should have selected all of the text");
   is(textbox.selectionEnd, 3, "Should have selected all of the text");
 
@@ -297,19 +297,17 @@ add_task(async function contextmenu_clos
 
   promise = promiseEvent(searchPopup, "popuphidden");
 
   // synthesizeKey does not work with VK_CONTEXT_MENU (bug 1127368)
   EventUtils.synthesizeMouseAtCenter(textbox, { type: "contextmenu", button: null });
 
   await promise;
 
-  let contextPopup =
-    document.getAnonymousElementByAttribute(textbox.inputField.parentNode,
-                                            "anonid", "input-box-contextmenu");
+  let contextPopup = textbox.inputField.parentNode.menupopup;
   promise = promiseEvent(contextPopup, "popuphidden");
   contextPopup.hidePopup();
   await promise;
 
   textbox.value = "";
 });
 
 // Tabbing to the search box should open the popup if it contains text.
--- a/browser/themes/osx/browser.css
+++ b/browser/themes/osx/browser.css
@@ -562,17 +562,17 @@ html|input.urlbar-input {
   }
 
   #editBookmarkPanel .expander-up > .button-box > .button-icon,
   #editBookmarkPanel .expander-down > .button-box > .button-icon {
     width: 9px;
   }
 }
 
-#editBMPanel_tagsField > .textbox-input-box > html|*.textbox-input::placeholder {
+#editBMPanel_tagsField > moz-input-box > html|*.textbox-input::placeholder {
   opacity: 1.0;
   color: #bbb;
 }
 
 /* ----- SIDEBAR ELEMENTS ----- */
 
 %include ../shared/sidebar.inc.css
 
--- a/browser/themes/windows/browser.css
+++ b/browser/themes/windows/browser.css
@@ -542,17 +542,17 @@ menuitem.bookmark-item {
 @media (-moz-windows-default-theme: 0) {
   #urlbar:not(:-moz-lwtheme):not([focused="true"]),
   .searchbar-textbox:not(:-moz-lwtheme):not([focused="true"]) {
     border-color: ThreeDShadow;
   }
 }
 
 html|*.urlbar-input:-moz-lwtheme::placeholder,
-.searchbar-textbox:-moz-lwtheme > .urlbar-textbox-container > .textbox-input-box > html|*.textbox-input::placeholder {
+.searchbar-textbox:-moz-lwtheme > .urlbar-textbox-container > moz-input-box > html|*.textbox-input::placeholder {
   opacity: 1.0;
   color: #777;
 }
 
 /* ::::: URL Bar Zoom Reset Button ::::: */
 @keyframes urlbar-zoom-reset-pulse {
   0% {
     transform: scale(0);
--- a/testing/marionette/puppeteer/firefox/firefox_puppeteer/ui/browser/toolbars.py
+++ b/testing/marionette/puppeteer/firefox/firefox_puppeteer/ui/browser/toolbars.py
@@ -118,17 +118,17 @@ class LocationBar(UIBaseLib):
 
     @property
     def contextmenu(self):
         """Provides access to the urlbar context menu.
 
         :returns: Reference to the urlbar context menu.
         """
         # TODO: This method should be implemented via the menu API.
-        parent = self.urlbar.find_element(By.ANON_ATTRIBUTE, {'anonid': 'textbox-input-box'})
+        parent = self.urlbar.find_element(By.ANON_ATTRIBUTE, {'anonid': 'moz-input-box'})
         return parent.find_element(By.ANON_ATTRIBUTE, {'anonid': 'input-box-contextmenu'})
 
     @property
     def focused(self):
         """Checks the focus state of the location bar.
 
         :returns: `True` if focused, otherwise `False`
         """
--- a/toolkit/content/customElements.js
+++ b/toolkit/content/customElements.js
@@ -30,23 +30,26 @@ class MozXULElement extends XULElement {
    * whichever happens first. The createElement method would return a JavaScript
    * reflector, but the element wouldn't be in the document, so the node wouldn't
    * get XBL attached. After that point, even if the node is inserted into a
    * document, it won't get XBL attached until either the frame is constructed or
    * the reflector is garbage collected and the element is touched again.
    *
    * @param str
    *        String with the XML representation of XUL elements.
+   * @param preamble
+   *        String to be inserted above any markup. This can be used
+   *        to insert XML entity text, for instance.
    *
    * @return DocumentFragment containing the corresponding element tree, including
    *         element nodes but excluding any text node.
    */
-  static parseXULToFragment(str, entities = "") {
+  static parseXULToFragment(str, preamble = "") {
     let doc = gXULDOMParser.parseFromString(`
-      ${entities}
+      ${preamble}
       <box xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
         ${str}
       </box>
     `, "application/xml");
     // The XUL/XBL parser is set to ignore all-whitespace nodes, whereas (X)HTML
     // does not do this. Most XUL code assumes that the whitespace has been
     // stripped out, so we simply remove all text nodes after using the parser.
     let nodeIterator = doc.createNodeIterator(doc, NodeFilter.SHOW_TEXT);
@@ -112,16 +115,17 @@ function getInterfaceProxy(obj) {
 }
 
 // Attach the base class to the window so other scripts can use it:
 window.MozXULElement = MozXULElement;
 
 for (let script of [
   "chrome://global/content/elements/stringbundle.js",
   "chrome://global/content/elements/general.js",
+  "chrome://global/content/elements/textbox.js",
 ]) {
   Services.scriptloader.loadSubScript(script, window);
 }
 
 customElements.setElementCreationCallback("printpreview-toolbar", type => {
   Services.scriptloader.loadSubScript(
     "chrome://global/content/printPreviewToolbar.js", window);
 });
--- a/toolkit/content/tests/chrome/test_textbox_dictionary.xul
+++ b/toolkit/content/tests/chrome/test_textbox_dictionary.xul
@@ -46,44 +46,44 @@ function startTests()
     bringUpContextMenu(textbox);
   });
 }
 
 function runContextMenuTest()
 {
   SimpleTest.executeSoon( function() {
     // The textbox has its children in an hbox XUL element, so get that first
-    var hbox = document.getAnonymousNodes(textbox).item(0);
+    var inputBox = document.getAnonymousElementByAttribute(textbox, "anonid", "moz-input-box");
     
-    var contextMenu = document.getAnonymousElementByAttribute(hbox, "anonid", "input-box-contextmenu");
+    var contextMenu = inputBox.menupopup;
    
     switch(testNum)
     {
       case 0: // "Add to Dictionary" button
-        var addToDict = contextMenu.querySelector("[anonid=spell-add-to-dictionary]");
+        var addToDict = inputBox.getMenuItem("spell-add-to-dictionary");
         ok(!addToDict.hidden, "Is Add to Dictionary visible?");
 
-        var separator = contextMenu.querySelector("[anonid=spell-suggestions-separator]");
+        var separator = inputBox.getMenuItem("spell-suggestions-separator");
         ok(!separator.hidden, "Is separator visible?");
 
         addToDict.doCommand();
         
         contextMenu.hidePopup();
         testNum++;
         
         onSpellCheck(textbox, function () {
           bringUpContextMenu(textbox);
         });
         break;
         
       case 1: // "Undo Add to Dictionary" button
-        var undoAddDict = contextMenu.querySelector("[anonid=spell-undo-add-to-dictionary]");
+        var undoAddDict = inputBox.getMenuItem("spell-undo-add-to-dictionary");
         ok(!undoAddDict.hidden, "Is Undo Add to Dictioanry visible?");
 
-        var separator = contextMenu.querySelector("[anonid=spell-suggestions-separator]");
+        var separator = inputBox.getMenuItem("spell-suggestions-separator");
         ok(!separator.hidden, "Is separator hidden?");
 
         undoAddDict.doCommand();
         
         contextMenu.hidePopup();
         onSpellCheck(textbox, function () {
           SimpleTest.finish();
         });
--- a/toolkit/content/widgets/autocomplete.xml
+++ b/toolkit/content/widgets/autocomplete.xml
@@ -9,22 +9,22 @@
           xmlns:xul="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
           xmlns:xbl="http://www.mozilla.org/xbl">
 
   <binding id="autocomplete"
            extends="chrome://global/content/bindings/textbox.xml#textbox">
     <content sizetopopup="pref">
       <children includes="image|deck|stack|box"/>
 
-      <xul:hbox anonid="textbox-input-box" class="textbox-input-box" flex="1" xbl:inherits="tooltiptext=inputtooltiptext">
+      <xul:moz-input-box anonid="moz-input-box" flex="1" xbl:inherits="tooltiptext=inputtooltiptext">
         <children/>
         <html:input anonid="input" class="autocomplete-textbox textbox-input"
                     allowevents="true"
                     xbl:inherits="tooltiptext=inputtooltiptext,value,type=inputtype,maxlength,disabled,size,readonly,placeholder,tabindex,accesskey,mozactionhint"/>
-      </xul:hbox>
+      </xul:moz-input-box>
       <children includes="hbox"/>
 
       <xul:popupset anonid="popupset" class="autocomplete-result-popupset"/>
 
       <children includes="toolbarbutton"/>
     </content>
 
     <implementation implements="nsIAutoCompleteInput, nsIDOMXULMenuListElement">
--- a/toolkit/content/widgets/menulist.xml
+++ b/toolkit/content/widgets/menulist.xml
@@ -351,20 +351,20 @@
           }
         ]]>
       </destructor>
     </implementation>
   </binding>
 
   <binding id="menulist-editable" extends="chrome://global/content/bindings/menulist.xml#menulist">
     <content sizetopopup="pref">
-      <xul:hbox class="menulist-editable-box textbox-input-box" xbl:inherits="context,disabled,readonly,focused" flex="1">
+      <xul:moz-input-box class="menulist-editable-box moz-input-box" xbl:inherits="context,disabled,readonly,focused" flex="1">
         <html:input class="menulist-editable-input" anonid="input" allowevents="true"
                     xbl:inherits="value=label,value,disabled,tabindex,readonly,placeholder"/>
-      </xul:hbox>
+      </xul:moz-input-box>
       <xul:dropmarker class="menulist-dropmarker" type="menu"
                       xbl:inherits="open,disabled,parentfocused=focused"/>
       <children includes="menupopup"/>
     </content>
 
     <implementation>
       <method name="_selectInputFieldValueInList">
         <body>
--- a/toolkit/content/widgets/numberbox.xml
+++ b/toolkit/content/widgets/numberbox.xml
@@ -13,20 +13,20 @@
   <binding id="numberbox"
            extends="chrome://global/content/bindings/textbox.xml#textbox">
 
     <resources>
       <stylesheet src="chrome://global/skin/numberbox.css"/>
     </resources>
 
     <content>
-      <xul:hbox class="textbox-input-box numberbox-input-box" flex="1" xbl:inherits="context,disabled,focused">
+      <xul:moz-input-box anonid="moz-input-box" class="numberbox-input-box" flex="1" xbl:inherits="context,disabled,focused">
         <html:input class="numberbox-input textbox-input" type="number" anonid="input"
                     xbl:inherits="value,min,max,maxlength,disabled,size,readonly,placeholder,tabindex,accesskey"/>
-      </xul:hbox>
+      </xul:moz-input-box>
     </content>
 
     <implementation>
       <field name="_valueEntered">false</field>
       <field name="_value">0</field>
 
       <property name="value" onget="return String(this.valueNumber)"
                              onset="return this.valueNumber = val;"/>
--- a/toolkit/content/widgets/textbox.js
+++ b/toolkit/content/widgets/textbox.js
@@ -1,186 +1,202 @@
+/* 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/. */
 
-  <binding id="input-box">
-    <content context="_child">
-      <children/>
-      <xul:menupopup anonid="input-box-contextmenu"
-                     class="textbox-contextmenu"
-                     onpopupshowing="var input =
-                                       this.parentNode.getElementsByAttribute('anonid', 'input')[0];
-                                     if (document.commandDispatcher.focusedElement != input)
-                                       input.focus();
-                                     this.parentNode._doPopupItemEnabling(this);"
-                     oncommand="var cmd = event.originalTarget.getAttribute('cmd'); if(cmd) { this.parentNode.doCommand(cmd); event.stopPropagation(); }">
-        <xul:menuitem label="&undoCmd.label;" accesskey="&undoCmd.accesskey;" cmd="cmd_undo"/>
-        <xul:menuseparator/>
-        <xul:menuitem label="&cutCmd.label;" accesskey="&cutCmd.accesskey;" cmd="cmd_cut"/>
-        <xul:menuitem label="&copyCmd.label;" accesskey="&copyCmd.accesskey;" cmd="cmd_copy"/>
-        <xul:menuitem label="&pasteCmd.label;" accesskey="&pasteCmd.accesskey;" cmd="cmd_paste"/>
-        <xul:menuitem label="&deleteCmd.label;" accesskey="&deleteCmd.accesskey;" cmd="cmd_delete"/>
-        <xul:menuseparator/>
-        <xul:menuitem label="&selectAllCmd.label;" accesskey="&selectAllCmd.accesskey;" cmd="cmd_selectAll"/>
-      </xul:menupopup>
-    </content>
+"use strict";
+
+{
 
-    <implementation>
-      <method name="_doPopupItemEnabling">
-        <parameter name="popupNode"/>
-        <body>
-          <![CDATA[
-            var children = popupNode.childNodes;
-            for (var i = 0; i < children.length; i++) {
-              var command = children[i].getAttribute("cmd");
-              if (command) {
-                var controller = document.commandDispatcher.getControllerForCommand(command);
-                var enabled = controller.isCommandEnabled(command);
-                if (enabled)
-                  children[i].removeAttribute("disabled");
-                else
-                  children[i].setAttribute("disabled", "true");
-              }
-            }
-          ]]>
-        </body>
-      </method>
+const cachedFragments = {
+  get entities() {
+    return `<!DOCTYPE bindings [
+      <!ENTITY % textcontextDTD SYSTEM "chrome://global/locale/textcontext.dtd">
+      %textcontextDTD;
+    ]>`;
+  },
+  get editMenuItems() {
+    return `
+      <menuitem label="&undoCmd.label;" accesskey="&undoCmd.accesskey;" cmd="cmd_undo"></menuitem>
+      <menuseparator></menuseparator>
+      <menuitem label="&cutCmd.label;" accesskey="&cutCmd.accesskey;" cmd="cmd_cut"></menuitem>
+      <menuitem label="&copyCmd.label;" accesskey="&copyCmd.accesskey;" cmd="cmd_copy"></menuitem>
+      <menuitem label="&pasteCmd.label;" accesskey="&pasteCmd.accesskey;" cmd="cmd_paste"></menuitem>
+      <menuitem label="&deleteCmd.label;" accesskey="&deleteCmd.accesskey;" cmd="cmd_delete"></menuitem>
+      <menuseparator></menuseparator>
+      <menuitem label="&selectAllCmd.label;" accesskey="&selectAllCmd.accesskey;" cmd="cmd_selectAll"></menuitem>
+    `;
+  },
+  get normal() {
+    delete this.normal;
+    this.normal = MozXULElement.parseXULToFragment(`
+      <menupopup class="textbox-contextmenu">
+        ${this.editMenuItems}
+      </menupopup>
+    `, this.entities);
+    return this.normal;
+  },
+  get spellcheck() {
+    delete this.spellcheck;
+    this.spellcheck = MozXULElement.parseXULToFragment(`
+      <menupopup class="textbox-contextmenu">
+        <menuitem label="&spellNoSuggestions.label;" anonid="spell-no-suggestions" disabled="true"></menuitem>
+        <menuitem label="&spellAddToDictionary.label;" accesskey="&spellAddToDictionary.accesskey;" anonid="spell-add-to-dictionary" oncommand="this.parentNode.parentNode.spellCheckerUI.addToDictionary();"></menuitem>
+        <menuitem label="&spellUndoAddToDictionary.label;" accesskey="&spellUndoAddToDictionary.accesskey;" anonid="spell-undo-add-to-dictionary" oncommand="this.parentNode.parentNode.spellCheckerUI.undoAddToDictionary();"></menuitem>
+        <menuseparator anonid="spell-suggestions-separator"></menuseparator>
+        ${this.editMenuItems}
+        <menuseparator anonid="spell-check-separator"></menuseparator>
+        <menuitem label="&spellCheckToggle.label;" type="checkbox" accesskey="&spellCheckToggle.accesskey;" anonid="spell-check-enabled" oncommand="this.parentNode.parentNode.spellCheckerUI.toggleEnabled();"></menuitem>
+        <menu label="&spellDictionaries.label;" accesskey="&spellDictionaries.accesskey;" anonid="spell-dictionaries">
+          <menupopup anonid="spell-dictionaries-menu" onpopupshowing="event.stopPropagation();" onpopuphiding="event.stopPropagation();"></menupopup>
+        </menu>
+      </menupopup>
+    `, this.entities);
+    return this.spellcheck;
+  }
+};
 
-      <method name="_setMenuItemVisibility">
-        <parameter name="anonid"/>
-        <parameter name="visible"/>
-        <body><![CDATA[
-          document.getAnonymousElementByAttribute(this, "anonid", anonid).
-            hidden = !visible;
-        ]]></body>
-      </method>
+class MozInputBox extends MozXULElement {
+  static get observedAttributes() {
+    return ["spellcheck"];
+  }
+
+  attributeChangedCallback(name, oldValue, newValue) {
+    if (name === "spellcheck") {
+      this.spellcheck = newValue;
+      this._initUI();
+    }
+  }
+
+  connectedCallback() {
+    this.spellcheck = this.hasAttribute("spellcheck");
+    this._initUI();
+  }
 
-      <method name="doCommand">
-        <parameter name="command"/>
-        <body>
-          <![CDATA[
-            var controller = document.commandDispatcher.getControllerForCommand(command);
-            controller.doCommand(command);
-          ]]>
-        </body>
-      </method>
-    </implementation>
-  </binding>
+  _initUI() {
+    if (this.menupopup) {
+      this.menupopup.remove();
+    }
+
+    this.setAttribute("context", "_child");
+    this.appendChild(this.spellcheck ? cachedFragments.spellcheck.cloneNode(true) :
+                                       cachedFragments.normal.cloneNode(true));
+    this.menupopup = this.querySelector(".textbox-contextmenu");
+
+    this.menupopup.addEventListener("popupshowing", event => {
+      var input = this.getElementsByAttribute("anonid", "input")[0];
+      if (document.commandDispatcher.focusedElement != input)
+        input.focus();
+      this._doPopupItemEnabling(event.target);
+    });
+
+    if (this.spellcheck) {
+      this.menupopup.addEventListener("popuphiding", event => {
+        if (this.spellCheckerUI) {
+          this.spellCheckerUI.clearSuggestionsFromMenu();
+          this.spellCheckerUI.clearDictionaryListFromMenu();
+        }
+      });
+    }
 
-  <binding id="input-box-spell" extends="chrome://global/content/bindings/textbox.xml#input-box">
-    <content context="_child">
-      <children/>
-      <xul:menupopup anonid="input-box-contextmenu"
-                     class="textbox-contextmenu"
-                     onpopupshowing="var input =
-                                       this.parentNode.getElementsByAttribute('anonid', 'input')[0];
-                                     if (document.commandDispatcher.focusedElement != input)
-                                       input.focus();
-                                     this.parentNode._doPopupItemEnablingSpell(this);"
-                     onpopuphiding="this.parentNode._doPopupItemDisabling(this);"
-                     oncommand="var cmd = event.originalTarget.getAttribute('cmd'); if(cmd) { this.parentNode.doCommand(cmd); event.stopPropagation(); }">
-        <xul:menuitem label="&spellNoSuggestions.label;" anonid="spell-no-suggestions" disabled="true"/>
-        <xul:menuitem label="&spellAddToDictionary.label;" accesskey="&spellAddToDictionary.accesskey;" anonid="spell-add-to-dictionary"
-                      oncommand="this.parentNode.parentNode.spellCheckerUI.addToDictionary();"/>
-        <xul:menuitem label="&spellUndoAddToDictionary.label;" accesskey="&spellUndoAddToDictionary.accesskey;" anonid="spell-undo-add-to-dictionary"
-                      oncommand="this.parentNode.parentNode.spellCheckerUI.undoAddToDictionary();"/>
-        <xul:menuseparator anonid="spell-suggestions-separator"/>
-        <xul:menuitem label="&undoCmd.label;" accesskey="&undoCmd.accesskey;" cmd="cmd_undo"/>
-        <xul:menuseparator/>
-        <xul:menuitem label="&cutCmd.label;" accesskey="&cutCmd.accesskey;" cmd="cmd_cut"/>
-        <xul:menuitem label="&copyCmd.label;" accesskey="&copyCmd.accesskey;" cmd="cmd_copy"/>
-        <xul:menuitem label="&pasteCmd.label;" accesskey="&pasteCmd.accesskey;" cmd="cmd_paste"/>
-        <xul:menuitem label="&deleteCmd.label;" accesskey="&deleteCmd.accesskey;" cmd="cmd_delete"/>
-        <xul:menuseparator/>
-        <xul:menuitem label="&selectAllCmd.label;" accesskey="&selectAllCmd.accesskey;" cmd="cmd_selectAll"/>
-        <xul:menuseparator anonid="spell-check-separator"/>
-        <xul:menuitem label="&spellCheckToggle.label;" type="checkbox" accesskey="&spellCheckToggle.accesskey;" anonid="spell-check-enabled"
-                      oncommand="this.parentNode.parentNode.spellCheckerUI.toggleEnabled();"/>
-        <xul:menu label="&spellDictionaries.label;" accesskey="&spellDictionaries.accesskey;" anonid="spell-dictionaries">
-          <xul:menupopup anonid="spell-dictionaries-menu"
-                         onpopupshowing="event.stopPropagation();"
-                         onpopuphiding="event.stopPropagation();"/>
-        </xul:menu>
-      </xul:menupopup>
-    </content>
+    this.menupopup.addEventListener("command", event => {
+      var cmd = event.originalTarget.getAttribute("cmd");
+      if (cmd) {
+        this.doCommand(cmd);
+        event.stopPropagation();
+      }
+    });
+  }
+
+  _doPopupItemEnablingSpell(popupNode) {
+    var spellui = this.spellCheckerUI;
+    if (!spellui || !spellui.canSpellCheck) {
+      this._setMenuItemVisibility("spell-no-suggestions", false);
+      this._setMenuItemVisibility("spell-check-enabled", false);
+      this._setMenuItemVisibility("spell-check-separator", false);
+      this._setMenuItemVisibility("spell-add-to-dictionary", false);
+      this._setMenuItemVisibility("spell-undo-add-to-dictionary", false);
+      this._setMenuItemVisibility("spell-suggestions-separator", false);
+      this._setMenuItemVisibility("spell-dictionaries", false);
+      return;
+    }
+
+    spellui.initFromEvent(document.popupRangeParent,
+      document.popupRangeOffset);
 
-    <implementation>
-      <field name="_spellCheckInitialized">false</field>
-      <field name="_enabledCheckbox">
-        document.getAnonymousElementByAttribute(this, "anonid", "spell-check-enabled");
-      </field>
-      <field name="_suggestionsSeparator">
-        document.getAnonymousElementByAttribute(this, "anonid", "spell-no-suggestions");
-      </field>
-      <field name="_dictionariesMenu">
-        document.getAnonymousElementByAttribute(this, "anonid", "spell-dictionaries-menu");
-      </field>
+    var enabled = spellui.enabled;
+    var showUndo = spellui.canSpellCheck && spellui.canUndo();
+
+    var enabledCheckbox = this.getMenuItem("spell-check-enabled");
+    enabledCheckbox.setAttribute("checked", enabled);
+
+    var overMisspelling = spellui.overMisspelling;
+    this._setMenuItemVisibility("spell-add-to-dictionary", overMisspelling);
+    this._setMenuItemVisibility("spell-undo-add-to-dictionary", showUndo);
+    this._setMenuItemVisibility("spell-suggestions-separator", overMisspelling || showUndo);
 
-      <property name="spellCheckerUI" readonly="true">
-        <getter><![CDATA[
-          if (!this._spellCheckInitialized) {
-            this._spellCheckInitialized = true;
+    // suggestion list
+    var suggestionsSeparator = this.getMenuItem("spell-no-suggestions");
+    var numsug = spellui.addSuggestionsToMenu(popupNode, suggestionsSeparator, 5);
+    this._setMenuItemVisibility("spell-no-suggestions", overMisspelling && numsug == 0);
 
-            if (ChromeUtils.getClassName(document) != "XULDocument")
-              return null;
+    // dictionary list
+    var dictionariesMenu = this.getMenuItem("spell-dictionaries-menu");
+    var numdicts = spellui.addDictionaryListToMenu(dictionariesMenu, null);
+    this._setMenuItemVisibility("spell-dictionaries", enabled && numdicts > 1);
+  }
 
-            var textbox = document.getBindingParent(this);
-            if (!textbox || textbox.localName != "textbox")
-              return null;
+  _doPopupItemEnabling(popupNode) {
+    if (this.spellcheck) {
+      this._doPopupItemEnablingSpell(popupNode);
+    }
 
-            try {
-              ChromeUtils.import("resource://gre/modules/InlineSpellChecker.jsm", this);
-              this.InlineSpellCheckerUI = new this.InlineSpellChecker(textbox.editor);
-            } catch (ex) { }
-          }
-
-          return this.InlineSpellCheckerUI;
-        ]]></getter>
-      </property>
+    var children = popupNode.childNodes;
+    for (var i = 0; i < children.length; i++) {
+      var command = children[i].getAttribute("cmd");
+      if (command) {
+        var controller = document.commandDispatcher.getControllerForCommand(command);
+        var enabled = controller.isCommandEnabled(command);
+        if (enabled)
+          children[i].removeAttribute("disabled");
+        else
+          children[i].setAttribute("disabled", "true");
+      }
+    }
+  }
 
-      <method name="_doPopupItemEnablingSpell">
-        <parameter name="popupNode"/>
-        <body>
-          <![CDATA[
-            var spellui = this.spellCheckerUI;
-            if (!spellui || !spellui.canSpellCheck) {
-              this._setMenuItemVisibility("spell-no-suggestions", false);
-              this._setMenuItemVisibility("spell-check-enabled", false);
-              this._setMenuItemVisibility("spell-check-separator", false);
-              this._setMenuItemVisibility("spell-add-to-dictionary", false);
-              this._setMenuItemVisibility("spell-undo-add-to-dictionary", false);
-              this._setMenuItemVisibility("spell-suggestions-separator", false);
-              this._setMenuItemVisibility("spell-dictionaries", false);
-              return;
-            }
+  get spellCheckerUI() {
+    if (!this._spellCheckInitialized) {
+      this._spellCheckInitialized = true;
 
-            spellui.initFromEvent(document.popupRangeParent,
-                                  document.popupRangeOffset);
+      if (ChromeUtils.getClassName(document) != "XULDocument")
+        return null;
+
+      var textbox = document.getBindingParent(this);
+      if (!textbox || textbox.localName != "textbox")
+        return null;
 
-            var enabled = spellui.enabled;
-            var showUndo = spellui.canSpellCheck && spellui.canUndo();
-            this._enabledCheckbox.setAttribute("checked", enabled);
+      try {
+        ChromeUtils.import("resource://gre/modules/InlineSpellChecker.jsm", this);
+        this.InlineSpellCheckerUI = new this.InlineSpellChecker(textbox.editor);
+      } catch (ex) {}
+    }
 
-            var overMisspelling = spellui.overMisspelling;
-            this._setMenuItemVisibility("spell-add-to-dictionary", overMisspelling);
-            this._setMenuItemVisibility("spell-undo-add-to-dictionary", showUndo);
-            this._setMenuItemVisibility("spell-suggestions-separator", overMisspelling || showUndo);
+    return this.InlineSpellCheckerUI;
+  }
 
-            // suggestion list
-            var numsug = spellui.addSuggestionsToMenu(popupNode, this._suggestionsSeparator, 5);
-            this._setMenuItemVisibility("spell-no-suggestions", overMisspelling && numsug == 0);
+  getMenuItem(anonid) {
+    return this.querySelector(`[anonid="${anonid}"]`);
+  }
 
-            // dictionary list
-            var numdicts = spellui.addDictionaryListToMenu(this._dictionariesMenu, null);
-            this._setMenuItemVisibility("spell-dictionaries", enabled && numdicts > 1);
+  _setMenuItemVisibility(anonid, visible) {
+    this.getMenuItem(anonid).hidden = !visible;
+  }
 
-            this._doPopupItemEnabling(popupNode);
-          ]]>
-        </body>
-      </method>
-      <method name="_doPopupItemDisabling">
-        <body><![CDATA[
-          if (this.spellCheckerUI) {
-            this.spellCheckerUI.clearSuggestionsFromMenu();
-            this.spellCheckerUI.clearDictionaryListFromMenu();
-          }
-        ]]></body>
-      </method>
-    </implementation>
-  </binding>
+  doCommand(command) {
+    var controller = document.commandDispatcher.getControllerForCommand(command);
+    controller.doCommand(command);
+  }
+}
+
+customElements.define("moz-input-box", MozInputBox);
+
+}
--- a/toolkit/content/widgets/textbox.xml
+++ b/toolkit/content/widgets/textbox.xml
@@ -21,20 +21,20 @@
   <binding id="textbox">
     <resources>
       <stylesheet src="chrome://global/content/textbox.css"/>
       <stylesheet src="chrome://global/skin/textbox.css"/>
     </resources>
 
     <content>
       <children/>
-      <xul:hbox class="textbox-input-box" flex="1" xbl:inherits="context,spellcheck">
+      <xul:moz-input-box anonid="moz-input-box" flex="1" xbl:inherits="context,spellcheck">
         <html:input class="textbox-input" anonid="input"
                     xbl:inherits="value,type,maxlength,disabled,size,readonly,placeholder,tabindex,accesskey,noinitialfocus,mozactionhint,spellcheck"/>
-      </xul:hbox>
+      </xul:moz-input-box>
     </content>
 
     <implementation implements="nsIDOMXULLabeledControlElement">
       <!-- nsIDOMXULLabeledControlElement -->
       <field name="crop">""</field>
       <field name="image">""</field>
       <field name="command">""</field>
       <field name="accessKey">""</field>
@@ -252,39 +252,38 @@
         if (event.target != this)
           return;
 
         if (!event.button) // context menu opened via keyboard shortcut
           return;
         this._maybeSelectAll();
         // see bug 576135 comment 4
         let box = this.inputField.parentNode;
-        let menu = document.getAnonymousElementByAttribute(box, "anonid", "input-box-contextmenu");
-        box._doPopupItemEnabling(menu);
+        box._doPopupItemEnabling(box.menupopup);
       </handler>
 #endif
     </handlers>
   </binding>
 
   <binding id="search-textbox" extends="chrome://global/content/bindings/textbox.xml#textbox">
     <content>
       <children/>
-      <xul:hbox class="textbox-input-box" flex="1" xbl:inherits="context,spellcheck" align="center">
+      <xul:moz-input-box anonid="moz-input-box" flex="1" xbl:inherits="context,spellcheck" align="center">
         <xul:image class="textbox-search-sign"/>
         <html:input class="textbox-input" anonid="input" mozactionhint="search"
                     xbl:inherits="value,type,maxlength,disabled,size,readonly,placeholder,tabindex,accesskey,mozactionhint,spellcheck"/>
         <xul:deck class="textbox-search-icons" anonid="search-icons">
           <xul:image class="textbox-search-icon" anonid="searchbutton-icon"
                      xbl:inherits="src=image,label=searchbuttonlabel,searchbutton,disabled"/>
           <xul:image class="textbox-search-clear"
                      onclick="document.getBindingParent(this)._clearSearch();"
                      label="&searchTextBox.clear.label;"
                      xbl:inherits="disabled"/>
         </xul:deck>
-      </xul:hbox>
+      </xul:moz-input-box>
     </content>
     <implementation>
       <field name="_timer">null</field>
       <field name="_searchIcons">
         document.getAnonymousElementByAttribute(this, "anonid", "search-icons");
       </field>
       <field name="_searchButtonIcon">
         document.getAnonymousElementByAttribute(this, "anonid", "searchbutton-icon");
@@ -395,202 +394,15 @@
           event.stopPropagation();
         ]]>
       </handler>
     </handlers>
   </binding>
 
   <binding id="textarea" extends="chrome://global/content/bindings/textbox.xml#textbox">
     <content>
-      <xul:hbox class="textbox-input-box" flex="1" xbl:inherits="context,spellcheck">
+      <xul:moz-input-box anonid="moz-input-box" flex="1" xbl:inherits="context,spellcheck">
         <html:textarea class="textbox-textarea" anonid="input"
                        xbl:inherits="xbl:text=value,disabled,tabindex,rows,cols,readonly,wrap,placeholder,mozactionhint,spellcheck"><children/></html:textarea>
-      </xul:hbox>
+      </xul:moz-input-box>
     </content>
   </binding>
-
-  <binding id="input-box">
-    <content context="_child">
-      <children/>
-      <xul:menupopup anonid="input-box-contextmenu"
-                     class="textbox-contextmenu"
-                     onpopupshowing="var input =
-                                       this.parentNode.getElementsByAttribute('anonid', 'input')[0];
-                                     if (document.commandDispatcher.focusedElement != input)
-                                       input.focus();
-                                     this.parentNode._doPopupItemEnabling(this);"
-                     oncommand="var cmd = event.originalTarget.getAttribute('cmd'); if(cmd) { this.parentNode.doCommand(cmd); event.stopPropagation(); }">
-        <xul:menuitem label="&undoCmd.label;" accesskey="&undoCmd.accesskey;" cmd="cmd_undo"/>
-        <xul:menuseparator/>
-        <xul:menuitem label="&cutCmd.label;" accesskey="&cutCmd.accesskey;" cmd="cmd_cut"/>
-        <xul:menuitem label="&copyCmd.label;" accesskey="&copyCmd.accesskey;" cmd="cmd_copy"/>
-        <xul:menuitem label="&pasteCmd.label;" accesskey="&pasteCmd.accesskey;" cmd="cmd_paste"/>
-        <xul:menuitem label="&deleteCmd.label;" accesskey="&deleteCmd.accesskey;" cmd="cmd_delete"/>
-        <xul:menuseparator/>
-        <xul:menuitem label="&selectAllCmd.label;" accesskey="&selectAllCmd.accesskey;" cmd="cmd_selectAll"/>
-      </xul:menupopup>
-    </content>
-
-    <implementation>
-      <method name="_doPopupItemEnabling">
-        <parameter name="popupNode"/>
-        <body>
-          <![CDATA[
-            var children = popupNode.childNodes;
-            for (var i = 0; i < children.length; i++) {
-              var command = children[i].getAttribute("cmd");
-              if (command) {
-                var controller = document.commandDispatcher.getControllerForCommand(command);
-                var enabled = controller.isCommandEnabled(command);
-                if (enabled)
-                  children[i].removeAttribute("disabled");
-                else
-                  children[i].setAttribute("disabled", "true");
-              }
-            }
-          ]]>
-        </body>
-      </method>
-
-      <method name="_setMenuItemVisibility">
-        <parameter name="anonid"/>
-        <parameter name="visible"/>
-        <body><![CDATA[
-          document.getAnonymousElementByAttribute(this, "anonid", anonid).
-            hidden = !visible;
-        ]]></body>
-      </method>
-
-      <method name="doCommand">
-        <parameter name="command"/>
-        <body>
-          <![CDATA[
-            var controller = document.commandDispatcher.getControllerForCommand(command);
-            controller.doCommand(command);
-          ]]>
-        </body>
-      </method>
-    </implementation>
-  </binding>
-
-  <binding id="input-box-spell" extends="chrome://global/content/bindings/textbox.xml#input-box">
-    <content context="_child">
-      <children/>
-      <xul:menupopup anonid="input-box-contextmenu"
-                     class="textbox-contextmenu"
-                     onpopupshowing="var input =
-                                       this.parentNode.getElementsByAttribute('anonid', 'input')[0];
-                                     if (document.commandDispatcher.focusedElement != input)
-                                       input.focus();
-                                     this.parentNode._doPopupItemEnablingSpell(this);"
-                     onpopuphiding="this.parentNode._doPopupItemDisabling(this);"
-                     oncommand="var cmd = event.originalTarget.getAttribute('cmd'); if(cmd) { this.parentNode.doCommand(cmd); event.stopPropagation(); }">
-        <xul:menuitem label="&spellNoSuggestions.label;" anonid="spell-no-suggestions" disabled="true"/>
-        <xul:menuitem label="&spellAddToDictionary.label;" accesskey="&spellAddToDictionary.accesskey;" anonid="spell-add-to-dictionary"
-                      oncommand="this.parentNode.parentNode.spellCheckerUI.addToDictionary();"/>
-        <xul:menuitem label="&spellUndoAddToDictionary.label;" accesskey="&spellUndoAddToDictionary.accesskey;" anonid="spell-undo-add-to-dictionary"
-                      oncommand="this.parentNode.parentNode.spellCheckerUI.undoAddToDictionary();"/>
-        <xul:menuseparator anonid="spell-suggestions-separator"/>
-        <xul:menuitem label="&undoCmd.label;" accesskey="&undoCmd.accesskey;" cmd="cmd_undo"/>
-        <xul:menuseparator/>
-        <xul:menuitem label="&cutCmd.label;" accesskey="&cutCmd.accesskey;" cmd="cmd_cut"/>
-        <xul:menuitem label="&copyCmd.label;" accesskey="&copyCmd.accesskey;" cmd="cmd_copy"/>
-        <xul:menuitem label="&pasteCmd.label;" accesskey="&pasteCmd.accesskey;" cmd="cmd_paste"/>
-        <xul:menuitem label="&deleteCmd.label;" accesskey="&deleteCmd.accesskey;" cmd="cmd_delete"/>
-        <xul:menuseparator/>
-        <xul:menuitem label="&selectAllCmd.label;" accesskey="&selectAllCmd.accesskey;" cmd="cmd_selectAll"/>
-        <xul:menuseparator anonid="spell-check-separator"/>
-        <xul:menuitem label="&spellCheckToggle.label;" type="checkbox" accesskey="&spellCheckToggle.accesskey;" anonid="spell-check-enabled"
-                      oncommand="this.parentNode.parentNode.spellCheckerUI.toggleEnabled();"/>
-        <xul:menu label="&spellDictionaries.label;" accesskey="&spellDictionaries.accesskey;" anonid="spell-dictionaries">
-          <xul:menupopup anonid="spell-dictionaries-menu"
-                         onpopupshowing="event.stopPropagation();"
-                         onpopuphiding="event.stopPropagation();"/>
-        </xul:menu>
-      </xul:menupopup>
-    </content>
-
-    <implementation>
-      <field name="_spellCheckInitialized">false</field>
-      <field name="_enabledCheckbox">
-        document.getAnonymousElementByAttribute(this, "anonid", "spell-check-enabled");
-      </field>
-      <field name="_suggestionsSeparator">
-        document.getAnonymousElementByAttribute(this, "anonid", "spell-no-suggestions");
-      </field>
-      <field name="_dictionariesMenu">
-        document.getAnonymousElementByAttribute(this, "anonid", "spell-dictionaries-menu");
-      </field>
-
-      <property name="spellCheckerUI" readonly="true">
-        <getter><![CDATA[
-          if (!this._spellCheckInitialized) {
-            this._spellCheckInitialized = true;
-
-            if (ChromeUtils.getClassName(document) != "XULDocument")
-              return null;
-
-            var textbox = document.getBindingParent(this);
-            if (!textbox || textbox.localName != "textbox")
-              return null;
-
-            try {
-              ChromeUtils.import("resource://gre/modules/InlineSpellChecker.jsm", this);
-              this.InlineSpellCheckerUI = new this.InlineSpellChecker(textbox.editor);
-            } catch (ex) { }
-          }
-
-          return this.InlineSpellCheckerUI;
-        ]]></getter>
-      </property>
-
-      <method name="_doPopupItemEnablingSpell">
-        <parameter name="popupNode"/>
-        <body>
-          <![CDATA[
-            var spellui = this.spellCheckerUI;
-            if (!spellui || !spellui.canSpellCheck) {
-              this._setMenuItemVisibility("spell-no-suggestions", false);
-              this._setMenuItemVisibility("spell-check-enabled", false);
-              this._setMenuItemVisibility("spell-check-separator", false);
-              this._setMenuItemVisibility("spell-add-to-dictionary", false);
-              this._setMenuItemVisibility("spell-undo-add-to-dictionary", false);
-              this._setMenuItemVisibility("spell-suggestions-separator", false);
-              this._setMenuItemVisibility("spell-dictionaries", false);
-              return;
-            }
-
-            spellui.initFromEvent(document.popupRangeParent,
-                                  document.popupRangeOffset);
-
-            var enabled = spellui.enabled;
-            var showUndo = spellui.canSpellCheck && spellui.canUndo();
-            this._enabledCheckbox.setAttribute("checked", enabled);
-
-            var overMisspelling = spellui.overMisspelling;
-            this._setMenuItemVisibility("spell-add-to-dictionary", overMisspelling);
-            this._setMenuItemVisibility("spell-undo-add-to-dictionary", showUndo);
-            this._setMenuItemVisibility("spell-suggestions-separator", overMisspelling || showUndo);
-
-            // suggestion list
-            var numsug = spellui.addSuggestionsToMenu(popupNode, this._suggestionsSeparator, 5);
-            this._setMenuItemVisibility("spell-no-suggestions", overMisspelling && numsug == 0);
-
-            // dictionary list
-            var numdicts = spellui.addDictionaryListToMenu(this._dictionariesMenu, null);
-            this._setMenuItemVisibility("spell-dictionaries", enabled && numdicts > 1);
-
-            this._doPopupItemEnabling(popupNode);
-          ]]>
-        </body>
-      </method>
-      <method name="_doPopupItemDisabling">
-        <body><![CDATA[
-          if (this.spellCheckerUI) {
-            this.spellCheckerUI.clearSuggestionsFromMenu();
-            this.spellCheckerUI.clearDictionaryListFromMenu();
-          }
-        ]]></body>
-      </method>
-    </implementation>
-  </binding>
-
 </bindings>
--- a/toolkit/content/xul.css
+++ b/toolkit/content/xul.css
@@ -614,32 +614,24 @@ textbox {
   -moz-user-select: text;
   text-shadow: none;
 }
 
 textbox[multiline="true"] {
   -moz-binding: url("chrome://global/content/bindings/textbox.xml#textarea");
 }
 
-.textbox-input-box {
-  -moz-binding: url("chrome://global/content/bindings/textbox.xml#input-box");
-}
-
 html|textarea.textbox-textarea {
   resize: none;
 }
 
-textbox[resizable="true"] > .textbox-input-box > html|textarea.textbox-textarea {
+textbox[resizable="true"] > moz-input-box > html|textarea.textbox-textarea {
   resize: both;
 }
 
-.textbox-input-box[spellcheck="true"] {
-  -moz-binding: url("chrome://global/content/bindings/textbox.xml#input-box-spell");
-}
-
 textbox[type="search"] {
   -moz-binding: url("chrome://global/content/bindings/textbox.xml#search-textbox");
 }
 
 textbox[type="number"] {
   -moz-binding: url("chrome://global/content/bindings/numberbox.xml#numberbox");
 }
 
--- a/toolkit/themes/linux/global/autocomplete.css
+++ b/toolkit/themes/linux/global/autocomplete.css
@@ -108,12 +108,12 @@ html|span.ac-tag {
 .ac-action-text,
 .ac-text-overflow-container {
   padding: 0 !important;
   margin: 0 !important;
 }
 
 /* ::::: textboxes inside toolbarpaletteitems ::::: */
 
-toolbarpaletteitem > toolbaritem > textbox > .textbox-input-box > html|*.textbox-input,
-toolbarpaletteitem > toolbaritem > * > textbox > .textbox-input-box > html|*.textbox-input {
+toolbarpaletteitem > toolbaritem > textbox > moz-input-box > html|*.textbox-input,
+toolbarpaletteitem > toolbaritem > * > textbox > moz-input-box > html|*.textbox-input {
   visibility: hidden;
 }
--- a/toolkit/themes/linux/global/textbox.css
+++ b/toolkit/themes/linux/global/textbox.css
@@ -62,17 +62,17 @@ textbox.plain {
 
 textbox.plain html|*.textbox-input,
 textbox.plain html|*.textbox-textarea {
   padding: 0px !important;
 }
 
 /* ::::: search textbox ::::: */
 
-textbox:not([searchbutton]) > .textbox-input-box > .textbox-search-sign {
+textbox:not([searchbutton]) > moz-input-box > .textbox-search-sign {
   list-style-image: url(chrome://global/skin/icons/search-textbox.svg);
   margin-inline-end: 5px;
 }
 
 .textbox-search-icon[searchbutton] {
   list-style-image: url(chrome://global/skin/icons/search-textbox.svg);
 }
 
@@ -82,11 +82,11 @@ textbox:not([searchbutton]) > .textbox-i
 
 .textbox-search-icon[searchbutton]:not([disabled]) ,
 .textbox-search-clear:not([disabled]) {
   cursor: pointer;
 }
 
 /* ::::: textboxes inside toolbarpaletteitems ::::: */
 
-toolbarpaletteitem > toolbaritem > textbox > .textbox-input-box > html|*.textbox-input {
+toolbarpaletteitem > toolbaritem > textbox > moz-input-box > html|*.textbox-input {
   visibility: hidden;
 }
--- a/toolkit/themes/osx/global/autocomplete.css
+++ b/toolkit/themes/osx/global/autocomplete.css
@@ -96,12 +96,12 @@ html|span.ac-tag {
 .ac-action-text,
 .ac-text-overflow-container {
   padding: 0 !important;
   margin: 0 !important;
 }
 
 /* ::::: textboxes inside toolbarpaletteitems ::::: */
 
-toolbarpaletteitem > toolbaritem > textbox > .textbox-input-box > html|*.textbox-input,
-toolbarpaletteitem > toolbaritem > * > textbox > .textbox-input-box > html|*.textbox-input {
+toolbarpaletteitem > toolbaritem > textbox > moz-input-box > html|*.textbox-input,
+toolbarpaletteitem > toolbaritem > * > textbox > moz-input-box > html|*.textbox-input {
   visibility: hidden;
 }
--- a/toolkit/themes/osx/global/in-content/common.css
+++ b/toolkit/themes/osx/global/in-content/common.css
@@ -82,17 +82,17 @@ xul|radio[focused="true"] > .radio-check
 html|*.numberbox-input::-moz-number-spin-up {
   border-radius: 4px 4px 0 0;
 }
 
 html|*.numberbox-input::-moz-number-spin-down  {
   border-radius: 0 0 4px 4px;
 }
 
-xul|textbox[type="search"]:not([searchbutton]) > .textbox-input-box > .textbox-search-sign {
+xul|textbox[type="search"]:not([searchbutton]) > moz-input-box > .textbox-search-sign {
   list-style-image: url(chrome://global/skin/icons/search-textbox.svg);
   margin-inline-end: 5px;
 }
 
 html|button {
   /* XUL button min-width */
   min-width: 79px;
 }
--- a/toolkit/themes/osx/global/textbox.css
+++ b/toolkit/themes/osx/global/textbox.css
@@ -66,17 +66,17 @@ textbox[type="search"] {
   margin-bottom: 1px;
 }
 
 textbox[type="search"].compact {
   padding: 0;
   font-size: 11px;
 }
 
-textbox[type="search"].compact > .textbox-input-box > .textbox-search-icons > .textbox-search-clear {
+textbox[type="search"].compact > moz-input-box > .textbox-search-icons > .textbox-search-clear {
   width: 11px;
 }
 
 .textbox-search-clear:not([disabled]) {
   cursor: default;
 }
  
 .textbox-search-icons:not([selectedIndex="1"]) {
--- a/toolkit/themes/windows/global/autocomplete.css
+++ b/toolkit/themes/windows/global/autocomplete.css
@@ -106,12 +106,12 @@ html|span.ac-tag {
 .ac-action-text,
 .ac-text-overflow-container {
   padding: 0 !important;
   margin: 0 !important;
 }
 
 /* ::::: textboxes inside toolbarpaletteitems ::::: */
 
-toolbarpaletteitem > toolbaritem > textbox > .textbox-input-box> html|*.textbox-input,
-toolbarpaletteitem > toolbaritem > * > textbox > .textbox-input-box > html|*.textbox-input {
+toolbarpaletteitem > toolbaritem > textbox > moz-input-box > html|*.textbox-input,
+toolbarpaletteitem > toolbaritem > * > textbox > moz-input-box  > html|*.textbox-input {
   visibility: hidden;
 }
--- a/toolkit/themes/windows/global/textbox.css
+++ b/toolkit/themes/windows/global/textbox.css
@@ -68,17 +68,17 @@ textbox.plain {
 
 textbox.plain html|*.textbox-input,
 textbox.plain html|*.textbox-textarea {
   padding: 0px !important;
 }
 
 /* ::::: search textbox ::::: */
 
-textbox:not([searchbutton]) > .textbox-input-box > .textbox-search-sign {
+textbox:not([searchbutton]) > moz-input-box > .textbox-search-sign {
   list-style-image: url(chrome://global/skin/icons/search-textbox.svg);
   margin-inline-end: 5px;
 }
 
 .textbox-search-icon[searchbutton] {
   list-style-image: url(chrome://global/skin/icons/search-textbox.svg);
 }
 
@@ -104,12 +104,12 @@ textbox:not([searchbutton]) > .textbox-i
 }
 
 .textbox-search-clear:not([disabled]):hover:active {
   -moz-image-region: rect(0, 48px, 16px, 32px);
 }
 
 /* ::::: textboxes inside toolbarpaletteitems ::::: */
 
-toolbarpaletteitem > toolbaritem > textbox > .textbox-input-box > html|*.textbox-input {
+toolbarpaletteitem > toolbaritem > textbox > moz-input-box > html|*.textbox-input {
   visibility: hidden;
 }