Bug 1542720 - [de-xbl] convert the glodaSearch binding. r=mkmelin DONTBUILD
authorAlessandro Castellani <alessandro@thunderbird.net>
Thu, 19 Sep 2019 09:48:46 +0200
changeset 36858 95aa8237c44ec527e591effa0f3857b0c2a0b29e
parent 36857 6308e5a5c75b4c41bc06e10f804f08fc41c61ba6
child 36859 d52c5f0981525fe1b66bc0f450ce4d667fb22b15
push id395
push userclokep@gmail.com
push dateMon, 02 Dec 2019 19:38:57 +0000
reviewersmkmelin
bugs1542720
Bug 1542720 - [de-xbl] convert the glodaSearch binding. r=mkmelin DONTBUILD
mail/base/content/customElements.js
mail/base/content/gloda-autocomplete-input.js
mail/base/content/glodaFacetTab.js
mail/base/content/mainMailToolbox.inc.xul
mail/base/content/messenger.css
mail/base/content/messenger.xul
mail/base/content/search.xml
mail/base/jar.mn
mail/components/im/content/chat-messenger.inc.xul
mail/components/im/content/chat.css
mail/test/mozmill/quick-filter-bar/test-keyboard-interface.js
mail/themes/linux/mail/searchBox.css
mail/themes/osx/mail/compose/messengercompose.css
mail/themes/osx/mail/searchBox.css
mail/themes/shared/mail/searchBox.css
mail/themes/windows/mail/searchBox.css
--- a/mail/base/content/customElements.js
+++ b/mail/base/content/customElements.js
@@ -9,16 +9,17 @@
 (() => {
   var { Services } = ChromeUtils.import("resource://gre/modules/Services.jsm");
 
   const isDummyDocument =
     document.documentURI == "chrome://extensions/content/dummy.xul";
   if (!isDummyDocument) {
     for (let script of [
       "chrome://chat/content/conversation-browser.js",
+      "chrome://messenger/content/gloda-autocomplete-input.js",
       "chrome://chat/content/chat-tooltip.js",
       "chrome://messenger/content/mailWidgets.js",
       "chrome://messenger/content/statuspanel.js",
       "chrome://messenger/content/foldersummary.js",
       "chrome://messenger/content/addressbook/menulist-addrbooks.js",
       "chrome://messenger/content/folder-menupopup.js",
       "chrome://messenger/content/toolbarbutton-menu-button.js",
     ]) {
rename from mail/base/content/search.xml
rename to mail/base/content/gloda-autocomplete-input.js
--- a/mail/base/content/search.xml
+++ b/mail/base/content/gloda-autocomplete-input.js
@@ -1,262 +1,236 @@
-<?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/. -->
+/**
+ * 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/. */
 
-<!-- import-globals-from mailWindow.js -->
-<!-- import-globals-from nsDragAndDrop.js -->
-
-<!DOCTYPE bindings [
-<!ENTITY % messengerDTD SYSTEM "chrome://messenger/locale/messenger.dtd">
-%messengerDTD;
-]>
+/* global MozXULElement */
 
-<bindings id="SearchBindings"
-   xmlns="http://www.mozilla.org/xbl"
-   xmlns:html="http://www.w3.org/1999/xhtml"
-   xmlns:xul="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
-   xmlns:xbl="http://www.mozilla.org/xbl">
+/* import-globals-from mailWindow.js */
+/* import-globals-from nsDragAndDrop.js */
+
+"use strict";
 
-  <!--
-    - The glodaSearch binding implements a gloda-backed search mechanism.  The
-    -  actual search logic comes from the glodaFacet tab mode in the
-    -  glodaFacetTabType definition.  This binding serves as a means to display
-    -  and alter the current search query if a "glodaFacet" tab is displayed,
-    -  or enter a search query and spawn a new "glodaFacet" tab if one is
-    -  currently not displayed.
-    -
-    - This widget used to have many weird implementation nuances.  Now we are
-    -  just a little bit of extra stuff on top of the toolkit autocomplete
-    -  implementation.  Our deviations are:
-    -  - We collapse ourselves when gloda is disabled; we track the state.
-    -  -
-    -->
-  <binding id="glodaSearch"
-           extends="chrome://global/content/bindings/autocomplete.xml#autocomplete">
+// The autocomplete CE is defined lazily. Create one now to get
+// autocomplete-input defined, allowing us to inherit from it.
+if (!customElements.get("autocomplete-input")) {
+  delete document.createXULElement("input", { is: "autocomplete-input" });
+}
 
-    <handlers>
-      <handler event="drop" phase="capturing"><![CDATA[
-        nsDragAndDrop.drop(event, this.searchInputDNDObserver);
-      ]]></handler>
-
-      <handler event="keypress" group="system" keycode="VK_RETURN"><![CDATA[
-        this.doSearch();
-        event.preventDefault();
-        event.stopPropagation();
-      ]]></handler>
-      <handler event="keypress" keycode="VK_ESCAPE"><![CDATA[
-        this.clearSearch();
-        event.preventDefault();
-        event.stopPropagation();
-      ]]></handler>
-    </handlers>
+customElements.whenDefined("autocomplete-input").then(() => {
+  const { Services } = ChromeUtils.import(
+    "resource://gre/modules/Services.jsm"
+  );
+  const { AppConstants } = ChromeUtils.import(
+    "resource://gre/modules/AppConstants.jsm"
+  );
+  const { GlodaMsgSearcher } = ChromeUtils.import(
+    "resource:///modules/gloda/msg_search.js"
+  );
+  const { GlodaIMSearcher } = ChromeUtils.import(
+    "resource:///modules/search_im.jsm"
+  );
+  const { XPCOMUtils } = ChromeUtils.import(
+    "resource://gre/modules/XPCOMUtils.jsm"
+  );
 
-    <implementation implements="nsIObserver">
-      <constructor><![CDATA[
-        const {Services} = ChromeUtils.import("resource://gre/modules/Services.jsm");
-        const {AppConstants} = ChromeUtils.import("resource://gre/modules/AppConstants.jsm");
-
-        try {
-          this.setAttribute(
-            "placeholder",
-            this.getAttribute("emptytextbase")
-                .replace("#1", this.getAttribute(
-                                 (AppConstants.platform == "macosx") ?
-                                  "keyLabelMac" : "keyLabelNonMac")));
-
-          Services.prefs.addObserver("mailnews.database.global.indexer.enabled",
-                                     this._prefObserver);
-
-          this.glodaCompleter =
-            Cc["@mozilla.org/autocomplete/search;1?name=gloda"]
-              .getService(Ci.nsIAutoCompleteSearch)
-              .wrappedJSObject;
-          Services.obs.addObserver(this, "autocomplete-did-enter-text");
+  /**
+   * The MozGlodaAutocompleteInput widget is used to display the autocomplete search bar.
+   *
+   * @extends {AutocompleteInput}
+   */
+  class MozGlodaAutocompleteInput extends customElements.get(
+    "autocomplete-input"
+  ) {
+    constructor() {
+      super();
 
-          this.glodaEnabled =
-            Services.prefs.getBoolPref("mailnews.database.global.indexer.enabled");
-          this.collapsed = !this.glodaEnabled;
-
-          // make sure we set our emptytext here from the get-go
-          if (this.hasAttribute("placeholder")) {
-            this.placeholder = this.getAttribute("placeholder");
-          }
-        } catch (e) {
-          let { logException } = ChromeUtils.import("resource:///modules/errUtils.js");
-          logException(e, true);
-        }
-      ]]></constructor>
+      this.addEventListener(
+        "drop",
+        event => {
+          nsDragAndDrop.drop(event, this.searchInputDNDObserver);
+        },
+        true
+      );
 
-      <destructor>
-        <![CDATA[
-          const {Services} = ChromeUtils.import("resource://gre/modules/Services.jsm");
+      this.addEventListener("keypress", event => {
+        if (event.keyCode == KeyEvent.DOM_VK_RETURN) {
+          this.doSearch();
+          event.preventDefault();
+          event.stopPropagation();
+        }
 
-          Services.prefs.removeObserver("mailnews.database.global.indexer.enabled",
-                                        this._prefObserver);
-          Services.obs.removeObserver(this, "autocomplete-did-enter-text");
-        ]]>
-      </destructor>
-
-      <field name="_prefObserver"><![CDATA[
-      ({
-        inputSearch: this,
-        observe(subject, topic, data) {
-          const {Services} = ChromeUtils.import("resource://gre/modules/Services.jsm");
+        if (event.keyCode == KeyEvent.DOM_VK_ESCAPE) {
+          this.clearSearch();
+          event.preventDefault();
+          event.stopPropagation();
+        }
+      });
+    }
 
-          if (topic == "nsPref:changed") {
-            subject.QueryInterface(Ci.nsIPrefBranch);
-            switch (data) {
-            case "mailnews.database.global.indexer.enabled":
-              this.inputSearch.glodaEnabled =
-                Services.prefs.getBoolPref(
-                  "mailnews.database.global.indexer.enabled");
-              this.inputSearch.collapsed = !this.inputSearch.glodaEnabled;
-              break;
-            }
-          }
-        },
+    connectedCallback() {
+      if (this.hasConnected) {
+        return;
+      }
 
-        QueryInterface: ChromeUtils.generateQI(["nsIObserver"]),
-      });
-      ]]></field>
-      <field name="glodaCompleter">null</field>
-      <property name="menupopup" readonly="true">
-        <getter><![CDATA[
-          return document.getAnonymousElementByAttribute(this, "anonid", "quick-search-menupopup");
-        ]]></getter>
-      </property>
+      this.hasConnected = true;
+      super.connectedCallback();
+
+      this.setAttribute("is", "gloda-autocomplete-input");
 
-      <property name="state">
-        <getter><![CDATA[
-          return { string: this.value };
-        ]]></getter>
-        <setter><![CDATA[
-          this.value = val.string;
-        ]]></setter>
-      </property>
+      XPCOMUtils.defineLazyPreferenceGetter(
+        this,
+        "glodaEnabled",
+        "mailnews.database.global.indexer.enabled",
+        true,
+        (pref, oldVal, newVal) => {
+          this.toggleAttribute("hidden", !newVal);
+        }
+      );
 
-      // DND Observer
-      <field name="searchInputDNDObserver" readonly="true"><![CDATA[
-      ({
-        inputSearch: this,
+      this.glodaCompleter = null;
 
-        onDrop(aEvent, aXferData, aDragSession) {
-          try {
-            if (aXferData.data) {
-              this.inputSearch.focus();
-              this.inputSearch.value = aXferData.data;
-              // XXX for some reason the input field is _cleared_ even though
-              // the search works.
-              this.inputSearch.doSearch();
-            }
-          } catch (e) {
-            let { logException } = ChromeUtils.import("resource:///modules/errUtils.js");
-            logException(e);
+      // @implements {nsIObserver}
+      this.searchInputDNDObserver = {
+        onDrop: (aEvent, aXferData, aDragSession) => {
+          if (aXferData.data) {
+            this.focus();
+            this.value = aXferData.data;
+            // XXX for some reason the input field is _cleared_ even though
+            // the search works.
+            this.doSearch();
           }
         },
 
         getSupportedFlavours() {
-          var flavourSet = new FlavourSet();
+          let flavourSet = new FlavourSet();
           flavourSet.appendFlavour("text/unicode");
           return flavourSet;
         },
-      })
-      ]]></field>
+      };
 
-      <method name="observe">
-        <parameter name="aSubject"/>
-        <parameter name="aTopic"/>
-        <parameter name="aData"/>
-        <body><![CDATA[
-        try {
-          if (aTopic == "autocomplete-did-enter-text" && aSubject == this) {
+      // @implements {nsIObserver}
+      this.textObserver = {
+        observe: (subject, topic, data) => {
+          if (
+            topic == "autocomplete-did-enter-text" &&
+            subject.popupElement == this.popupElement
+          ) {
             let selectedIndex = this.popup.selectedIndex;
             let curResult = this.glodaCompleter.curResult;
             if (!curResult) {
               // autocomplete didn't even finish.
               return;
             }
             let row = curResult.getObjectAt(selectedIndex);
             if (row == null) {
               return;
             }
             if (row.fullText) {
               // The autocomplete-did-enter-text notification is synchronously
               // generated by nsAutoCompleteController which will attempt to
               // call ClosePopup after we return and then tell the searchbox
-              // about the text entered.  Since doSearch may close the current
+              // about the text entered. Since doSearch may close the current
               // tab (and thus destroy the XUL document that owns the popup and
-              // the input field), the search box may no longer have its XBL
+              // the input field), the search box may no longer have its
               // binding attached when we return and telling it about the
               // entered text could fail.
               // To avoid this, we defer the doSearch call to the next turn of
               // the event loop by using setTimeout.
               setTimeout(this.doSearch.bind(this), 0);
             } else if (row.nounDef) {
               let theQuery = Gloda.newQuery(Gloda.NOUN_MESSAGE);
               if (row.nounDef.name == "tag") {
                 theQuery = theQuery.tags(row.item);
               } else if (row.nounDef.name == "identity") {
                 theQuery = theQuery.involves(row.item);
               }
               theQuery.orderBy("-date");
-              document.getElementById("tabmail").openTab("glodaFacet", {
-                query: theQuery,
-              });
+              document
+                .getElementById("tabmail")
+                .openTab("glodaFacet", {
+                  query: theQuery,
+                });
             }
           }
-        } catch (e) {
-          let { logException } = ChromeUtils.import("resource:///modules/errUtils.js");
-          logException(e);
-        }
-        ]]></body>
-      </method>
+        },
+      };
+
+      let keyLabel =
+        AppConstants.platform == "macosx" ? "keyLabelMac" : "keyLabelNonMac";
+      let placeholder = this.getAttribute("emptytextbase").replace(
+        "#1",
+        this.getAttribute(keyLabel)
+      );
+
+      this.setAttribute("placeholder", placeholder);
 
-      <method name="doSearch">
-        <body><![CDATA[
-          const {Services} = ChromeUtils.import("resource://gre/modules/Services.jsm");
+      this.glodaCompleter = Cc[
+        "@mozilla.org/autocomplete/search;1?name=gloda"
+      ].getService(Ci.nsIAutoCompleteSearch).wrappedJSObject;
+      Services.obs.addObserver(
+        this.textObserver,
+        "autocomplete-did-enter-text"
+      );
+
+      this.toggleAttribute("hidden", !this.glodaEnabled);
 
-          try {
-            if (this.value) {
-              let tabmail = document.getElementById("tabmail");
-              // If the current tab is a gloda search tab, reset the value
-              //  to the initial search value.  Otherwise, clear it.  This
-              //  is the value that 3is going to be saved with the current
-              //  tab when we switch back to it next.
-              let searchString = this.value;
+      // make sure we set our emptytext here from the get-go
+      if (this.hasAttribute("placeholder")) {
+        this.placeholder = this.getAttribute("placeholder");
+      }
+    }
+
+    set state(val) {
+      this.value = val.string;
+    }
+
+    get state() {
+      return { string: this.value };
+    }
 
-              if (tabmail.currentTabInfo.mode.name == "glodaFacet") {
-                // we'd rather reuse the existing tab (and somehow do something
-                // smart with any preexisting facet choices, but that's a
-                // bit hard right now, so doing the cheap thing and closing
-                // this tab and starting over
-                tabmail.closeTab();
-              }
-              this.value = ""; // clear our value, to avoid persistence
-              let args = {
-                searcher: new GlodaMsgSearcher(null, searchString),
-              };
-              if (Services.prefs.getBoolPref("mail.chat.enabled")) {
-                let {
-                  GlodaIMSearcher,
-                } = ChromeUtils.import("resource:///modules/search_im.jsm");
-                args.IMSearcher = new GlodaIMSearcher(null, searchString);
-              }
-              tabmail.openTab("glodaFacet", args);
-            }
-          } catch (e) {
-            let { logException } = ChromeUtils.import("resource:///modules/errUtils.js");
-            logException(e);
-          }
-        ]]>
-        </body>
-      </method>
-      <method name="clearSearch">
-        <body><![CDATA[
-          this.value = "";
-        ]]></body>
-      </method>
-    </implementation>
-  </binding>
+    doSearch() {
+      if (this.value) {
+        let tabmail = document.getElementById("tabmail");
+        // If the current tab is a gloda search tab, reset the value
+        // to the initial search value. Otherwise, clear it. This
+        // is the value that is going to be saved with the current
+        // tab when we switch back to it next.
+        let searchString = this.value;
 
-</bindings>
+        if (tabmail.currentTabInfo.mode.name == "glodaFacet") {
+          // We'd rather reuse the existing tab (and somehow do something
+          // smart with any preexisting facet choices, but that's a
+          // bit hard right now, so doing the cheap thing and closing
+          // this tab and starting over.
+          tabmail.closeTab();
+        }
+        this.value = ""; // clear our value, to avoid persistence
+        let args = {
+          searcher: new GlodaMsgSearcher(null, searchString),
+        };
+        if (Services.prefs.getBoolPref("mail.chat.enabled")) {
+          args.IMSearcher = new GlodaIMSearcher(null, searchString);
+        }
+        tabmail.openTab("glodaFacet", args);
+      }
+    }
+
+    clearSearch() {
+      this.value = "";
+    }
+
+    disconnectedCallback() {
+      Services.obs.removeObserver(
+        this.textObserver,
+        "autocomplete-did-enter-text"
+      );
+    }
+  }
+
+  MozXULElement.implementCustomInterface(MozGlodaAutocompleteInput, [
+    Ci.nsIObserver,
+  ]);
+  customElements.define("gloda-autocomplete-input", MozGlodaAutocompleteInput, {
+    extends: "input",
+  });
+});
--- a/mail/base/content/glodaFacetTab.js
+++ b/mail/base/content/glodaFacetTab.js
@@ -2,20 +2,16 @@
  * 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/. */
 
 var { StringBundle } = ChromeUtils.import(
   "resource:///modules/StringBundle.js"
 );
 
 var { FacetDriver } = ChromeUtils.import("resource:///modules/gloda/facet.js");
-// needed by search.xml to use us
-var { GlodaMsgSearcher } = ChromeUtils.import(
-  "resource:///modules/gloda/msg_search.js"
-);
 
 var glodaFacetTabType = {
   name: "glodaFacet",
   perTabPanel: "vbox",
   lastTabId: 0,
   strings: new StringBundle(
     "chrome://messenger/locale/glodaFacetView.properties"
   ),
--- a/mail/base/content/mainMailToolbox.inc.xul
+++ b/mail/base/content/mainMailToolbox.inc.xul
@@ -316,30 +316,29 @@
                    tooltiptext="&appmenuButton1.tooltip;"/>
 #ifdef MAIN_WINDOW
     <!-- gloda search widget; provides global (message) searching.  -->
     <toolbaritem id="gloda-search" insertafter="button-stop"
                  title="&glodaSearch.title;"
                  align="center"
                  flex="1"
                  class="chromeclass-toolbar-additional">
-        <textbox id="searchInput"
-                 class="searchBox"
-                 flex="1"
-                 type="glodacomplete"
-                 searchbutton="true"
-                 autocompletesearch="gloda"
-                 autocompletepopup="PopupGlodaAutocomplete"
-                 autocompletesearchparam="global"
-                 timeout="200"
-                 maxlength="192"
-                 placeholder=""
-                 emptytextbase="&search.label.base1;"
-                 keyLabelNonMac="&search.keyLabel.nonmac;"
-                 keyLabelMac="&search.keyLabel.mac;"/>
+        <html:input is="gloda-autocomplete-input" id="searchInput"
+                    type="text"
+                    class="searchBox gloda-search"
+                    searchbutton="true"
+                    autocompletesearch="gloda"
+                    autocompletepopup="PopupGlodaAutocomplete"
+                    autocompletesearchparam="global"
+                    timeout="200"
+                    maxlength="192"
+                    placeholder=""
+                    emptytextbase="&search.label.base1;"
+                    keyLabelNonMac="&search.keyLabel.nonmac;"
+                    keyLabelMac="&search.keyLabel.mac;"/>
     </toolbaritem>
 #endif
     <toolbarbutton id="button-compact" class="toolbarbutton-1"
                    insertafter="button-mark"
                    label="&compactButton.label;"
                    tooltiptext="&compactButton.tooltip;"
                    oncommand="goDoCommand('button_compact');"
                    observes="button_compact"/>
--- a/mail/base/content/messenger.css
+++ b/mail/base/content/messenger.css
@@ -37,24 +37,16 @@
 mail-tagfield[collapsed="true"] {
   display: none;
 }
 
 search-value {
   display: -moz-deck;
 }
 
-#searchInput {
-  -moz-binding: url("chrome://messenger/content/search.xml#glodaSearch");
-}
-
-.remote-gloda-search {
-  -moz-binding: url("chrome://messenger/content/search.xml#glodaSearch") !important;
-}
-
 .remote-gloda-search-container {
   min-width: 10em;
 }
 
 .chromeclass-toolbar {
   overflow-x: hidden;
 }
 
--- a/mail/base/content/messenger.xul
+++ b/mail/base/content/messenger.xul
@@ -593,19 +593,16 @@
                        end up being: threadPaneBox, threadpane-splitter, messagepanebox)
                     -->
                   <!-- second panel is the threadPane -->
                   <hbox id="threadPaneBox">
                     <!-- The threadContentArea was specially created to be a place for
                          things that want to be above/below the thread pane, regardless
                          of where the message reader ("messagepane") gets off to. -->
                     <vbox id="threadContentArea" flex="1" persist="width">
-                    <!-- Gloda search facets UI for use when dealing with a gloda-backed
-                         search view, implemented by glodaFacets XBL in search.xml. -->
-                    <box id="glodaSearchFacets"/>
 #include threadTree.inc.xul
                     </vbox>
                   </hbox>
                 <!-- extensions may overlay in additional panels; don't assume that there are only 2! -->
                 </deck> <!-- displayDeck -->
 
                 <!-- if you change this id, please change GetThreadAndMessagePaneSplitter() and MsgToggleMessagePane() -->
                 <splitter id="threadpane-splitter"
@@ -709,29 +706,29 @@
 
     <vbox id="glodaTab" collapsed="true">
       <vbox flex="1" class="chromeTabInstance">
         <vbox class="contentTabToolbox">
           <hbox class="glodaTabToolbar inline-toolbar chromeclass-toolbar" flex="1">
             <spacer flex="1" />
             <spacer flex="1" />
             <hbox flex="1" class="remote-gloda-search-container">
-              <textbox class="remote-gloda-search searchBox"
-                       flex="1"
-                       type="glodacomplete"
-                       searchbutton="true"
-                       autocompletesearch="gloda"
-                       autocompletepopup="PopupGlodaAutocomplete"
-                       autocompletesearchparam="global"
-                       timeout="200"
-                       maxlength="192"
-                       placeholder=""
-                       emptytextbase="&search.label.base1;"
-                       keyLabelNonMac="&search.keyLabel.nonmac;"
-                       keyLabelMac="&search.keyLabel.mac;"/>
+              <html:input is="gloda-autocomplete-input"
+                          type="text"
+                          class="remote-gloda-search searchBox gloda-search"
+                          searchbutton="true"
+                          autocompletesearch="gloda"
+                          autocompletepopup="PopupGlodaAutocomplete"
+                          autocompletesearchparam="global"
+                          timeout="200"
+                          maxlength="192"
+                          placeholder=""
+                          emptytextbase="&search.label.base1;"
+                          keyLabelNonMac="&search.keyLabel.nonmac;"
+                          keyLabelMac="&search.keyLabel.mac;"/>
             </hbox>
           </hbox>
         </vbox>
         <iframe flex="1"/>
       </vbox>
     </vbox>
     <vbox id="preferencesTab" collapsed="true">
       <vbox flex="1">
--- a/mail/base/jar.mn
+++ b/mail/base/jar.mn
@@ -64,17 +64,17 @@ messenger.jar:
     content/messenger/messenger-customization.js    (content/messenger-customization.js)
     content/messenger/searchBar.js                  (content/searchBar.js)
     content/messenger/phishingDetector.js           (content/phishingDetector.js)
     content/messenger/mail-offline.js               (content/mail-offline.js)
     content/messenger/aboutDialog.css               (content/aboutDialog.css)
     content/messenger/converterDialog.css           (content/converterDialog.css)
     content/messenger/notification.css              (content/notification.css)
 *   content/messenger/messenger.css                 (content/messenger.css)
-    content/messenger/search.xml                    (content/search.xml)
+    content/messenger/gloda-autocomplete-input.js   (content/gloda-autocomplete-input.js)
     content/messenger/tabmail.js                    (content/tabmail.js)
     content/messenger/tabmail-tabs.js               (content/tabmail-tabs.js)
     content/messenger/tabbrowser-tab.js             (content/tabbrowser-tab.js)
     content/messenger/tabmail.css                   (content/tabmail.css)
     content/messenger/statuspanel.js                (content/statuspanel.js)
     content/messenger/newTagDialog.xul              (content/newTagDialog.xul)
     content/messenger/newTagDialog.js               (content/newTagDialog.js)
     content/messenger/composerOverlay.css           (content/composerOverlay.css)
--- a/mail/components/im/content/chat-messenger.inc.xul
+++ b/mail/components/im/content/chat-messenger.inc.xul
@@ -64,30 +64,29 @@
                              label="&chatAccountsButton.label;"
                              oncommand="openIMAccountMgr()"/>
 
               <toolbaritem id="gloda-im-search" insertafter="button-stop"
                            title="&amp;glodaSearch.title;"
                            align="center"
                            flex="1"
                            class="chromeclass-toolbar-additional">
-                <textbox id="IMSearchInput"
-                         class="searchBox"
-                         flex="1"
-                         type="glodacomplete"
-                         searchbutton="true"
-                         autocompletesearch="gloda"
-                         autocompletepopup="PopupGlodaAutocomplete"
-                         autocompletesearchparam="global"
-                         timeout="200"
-                         maxlength="192"
-                         placeholder=""
-                         emptytextbase="&search.label.base1;"
-                         keyLabelNonMac="&search.keyLabel.nonmac;"
-                         keyLabelMac="&search.keyLabel.mac;"/>
+                <html:input is="gloda-autocomplete-input" id="IMSearchInput"
+                            type="text"
+                            class="searchBox gloda-search"
+                            searchbutton="true"
+                            autocompletesearch="gloda"
+                            autocompletepopup="PopupGlodaAutocomplete"
+                            autocompletesearchparam="global"
+                            timeout="200"
+                            maxlength="192"
+                            placeholder=""
+                            emptytextbase="&search.label.base1;"
+                            keyLabelNonMac="&search.keyLabel.nonmac;"
+                            keyLabelMac="&search.keyLabel.mac;"/>
               </toolbaritem>
               <toolbarbutton id="button-chat-appmenu"
                              type="menu"
                              class="toolbarbutton-1 button-appmenu"
                              label="&appmenuButton.label;"
                              tooltiptext="&appmenuButton1.tooltip;"/>
             </toolbarpalette>
           </toolbox>
--- a/mail/components/im/content/chat.css
+++ b/mail/components/im/content/chat.css
@@ -33,20 +33,16 @@ richlistitem[is="chat-imconv"] {
 .convUnreadTargetedCount[value="0"] {
   display: none;
 }
 
 #contextPaneFlexibleBox {
   overflow: hidden;
 }
 
-#IMSearchInput {
-  -moz-binding: url("chrome://messenger/content/search.xml#glodaSearch");
-}
-
 #statusTypeIcon:not([disabled]) {
   cursor: pointer;
 }
 
 .status-container {
   width: 15em;
 }
 
--- a/mail/test/mozmill/quick-filter-bar/test-keyboard-interface.js
+++ b/mail/test/mozmill/quick-filter-bar/test-keyboard-interface.js
@@ -114,24 +114,24 @@ function test_escape_rules() {
  * It's fairly important that the gloda search widget eats escape when people
  * press escape in there.  Because gloda is disabled by default, we need to
  * viciously uncollapse it ourselves and then cleanup afterwards...
  */
 function test_escape_does_not_reach_us_from_gloda_search() {
   let glodaSearchWidget = mc.e("searchInput");
   try {
     // uncollapse and focus the gloda search widget
-    glodaSearchWidget.collapsed = false;
+    glodaSearchWidget.removeAttribute("hidden");
     glodaSearchWidget.focus();
 
     mc.keypress(null, "VK_ESCAPE", {});
 
     assert_quick_filter_bar_visible(true);
   } finally {
-    glodaSearchWidget.collapsed = true;
+    glodaSearchWidget.setAttribute("hidden", "hidden");
   }
 }
 
 /**
  * Control-shift-k expands the quick filter bar when it's collapsed. When
  * already expanded, it focuses the text box and selects its text.
  */
 function test_control_shift_k_shows_quick_filter_bar() {
--- a/mail/themes/linux/mail/searchBox.css
+++ b/mail/themes/linux/mail/searchBox.css
@@ -3,31 +3,28 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 @import url("chrome://messenger/skin/shared/searchBox.css");
 
 .quick-search-textbox {
   padding-top: 1px;
 }
 
-.remote-gloda-search,
-#searchInput,
-#IMSearchInput {
+.gloda-search {
   background-image: url(chrome://messenger/skin/icons/search-glass.svg);
   background-position: calc( 100% - 4px);
   background-repeat: no-repeat;
   padding-inline-end: 24px;
   -moz-context-properties: fill, fill-opacity;
   fill: currentColor;
   fill-opacity: 0.54;
+  -moz-box-flex: 1;
 }
 
-.remote-gloda-search:-moz-locale-dir(rtl),
-#searchInput:-moz-locale-dir(rtl),
-#IMSearchInput:-moz-locale-dir(rtl) {
+.gloda-search:-moz-locale-dir(rtl) {
   background-position: 4px;
 }
 
 .remote-gloda-search-container {
   padding-top: 3px;
   padding-bottom: 2px;
 }
 
--- a/mail/themes/osx/mail/compose/messengercompose.css
+++ b/mail/themes/osx/mail/compose/messengercompose.css
@@ -1027,25 +1027,16 @@ toolbarbutton.toolbarbutton-1 > .toolbar
   z-index: 10;
 }
 
 #sidebar-title {
   font: icon;
   color: #000;
 }
 
-#searchInput #sidebar {
-  -moz-appearance: textfield !important;
-  margin: 3px;
-}
-
-#searchInput > .textbox-input-box #sidebar {
-  background-image: none !important;
-}
-
 #titlebar {
   display: none;
 }
 
 #titlebar-buttonbox-container {
   margin-top: 3px;
   margin-inline-start: 7px;
 }
--- a/mail/themes/osx/mail/searchBox.css
+++ b/mail/themes/osx/mail/searchBox.css
@@ -28,18 +28,17 @@
 
 /* special treatment because these boxes are on themable toolbars */
 .searchBox[focused="true"],
 .themeableSearchBox[focused="true"] {
   border-color: -moz-mac-focusring;
   box-shadow: var(--focus-ring-box-shadow);
 }
 
-#searchInput,
-#IMSearchInput,
+.gloda-search,
 #peopleSearchInput {
   margin-top: 2px;
   margin-bottom: 3px;
 }
 
 .remote-gloda-search-container {
   padding-top: 1px;
   padding-bottom: 1px;
--- a/mail/themes/shared/mail/searchBox.css
+++ b/mail/themes/shared/mail/searchBox.css
@@ -25,53 +25,48 @@
 }
 
 .searchBox:-moz-lwtheme-brighttext:hover,
 .themeableSearchBox:-moz-lwtheme-brighttext:not([disabled]):hover {
   box-shadow: 0 1px 3px rgba(255,255,255,.1);
 }
 
 /* special treatment because these boxes are on themable toolbars */
-#searchInput:-moz-lwtheme,
-#IMSearchInput:-moz-lwtheme,
+.gloda-search:-moz-lwtheme,
 #peopleSearchInput:-moz-lwtheme,
 .themeableSearchBox:-moz-lwtheme,
 .remote-gloda-search:-moz-lwtheme {
   background-color: var(--lwt-toolbar-field-background-color, hsla(0,0%,100%,.8));
   color: var(--lwt-toolbar-field-color, black);
 }
 
-#searchInput:not([focused="true"]):-moz-lwtheme,
-#IMSearchInput:not([focused="true"]):-moz-lwtheme,
+.gloda-search:not(:focus):-moz-lwtheme,
 #peopleSearchInput:not([focused="true"]):-moz-lwtheme,
 .themeableSearchBox:not([focused="true"]):-moz-lwtheme,
 .remote-gloda-search:not([focused="true"]):-moz-lwtheme {
   border-color: var(--lwt-toolbar-field-border-color, hsla(240,5%,5%,.25));
 }
 
-#searchInput:-moz-lwtheme:hover,
-#IMSearchInput:-moz-lwtheme:hover,
+.gloda-search:-moz-lwtheme:hover,
 #peopleSearchInput:-moz-lwtheme:hover,
 .themeableSearchBox:-moz-lwtheme:not([disabled]):hover,
 .remote-gloda-search:-moz-lwtheme:hover {
   background-color: var(--lwt-toolbar-field-background-color, white);
 }
 
-#searchInput:-moz-lwtheme[focused="true"],
-#IMSearchInput:-moz-lwtheme[focused="true"],
+.gloda-search:-moz-lwtheme:focus,
 #peopleSearchInput:-moz-lwtheme[focused="true"],
 .themeableSearchBox:-moz-lwtheme[focused="true"],
 .remote-gloda-search:-moz-lwtheme[focused="true"] {
   background-color: var(--lwt-toolbar-field-focus, var(--lwt-toolbar-field-background-color, white));
   color: var(--lwt-toolbar-field-focus-color, var(--lwt-toolbar-field-color, black));
   border-color: var(--toolbar-field-focus-border-color);
 }
 
-:root[lwt-selection] #searchInput .textbox-input:-moz-lwtheme::selection,
-:root[lwt-selection] #IMSearchInput .textbox-input:-moz-lwtheme::selection,
+:root[lwt-selection] .gloda-search::selection,
 :root[lwt-selection] #peopleSearchInput .textbox-input:-moz-lwtheme::selection,
 :root[lwt-selection] .themeableSearchBox .textbox-input:-moz-lwtheme::selection,
 :root[lwt-selection] .remote-gloda-search .textbox-input:-moz-lwtheme::selection {
   background-color: var(--lwt-toolbar-field-highlight, Highlight);
   color: var(--lwt-toolbar-field-highlight-text, HighlightText);
 }
 
 #PopupGlodaAutocomplete {
--- a/mail/themes/windows/mail/searchBox.css
+++ b/mail/themes/windows/mail/searchBox.css
@@ -8,39 +8,36 @@
  * The emptytext style would appear to use italics.  This is causing
  * problems for the search box because it has no minwidth and is flexy, so
  * removing the emptytext causes the size of the box to change and this is silly
  * and undesirable.  This change is being made to maintain the generally
  * accepted status quo while reducing breakage.  This will cause visual
  * inconsistency with the quick filter bar unless it gets a change like this
  * too.
  */
-#searchInput {
+.gloda-search {
   font-style: normal !important;
 }
 
 .quick-search-textbox {
   padding-top: 1px;
 }
 
-.remote-gloda-search,
-#searchInput,
-#IMSearchInput {
+.gloda-search {
   background-image: url(chrome://messenger/skin/icons/search-glass.svg);
   background-position: calc( 100% - 4px);
   background-repeat: no-repeat;
   padding-inline-end: 24px;
   -moz-context-properties: fill, fill-opacity;
   fill: currentColor;
   fill-opacity: 0.54;
+  -moz-box-flex: 1;
 }
 
-.remote-gloda-search:-moz-locale-dir(rtl),
-#searchInput:-moz-locale-dir(rtl),
-#IMSearchInput:-moz-locale-dir(rtl) {
+.gloda-search:-moz-locale-dir(rtl) {
   background-position: 4px;
 }
 
 .remote-gloda-search-container {
   padding-top: 1px;
   padding-bottom: 1px;
 }
 
@@ -62,31 +59,29 @@
 @media (-moz-windows-default-theme: 0) {
   .searchBox:not(:-moz-lwtheme):not([focused="true"]),
   .themeableSearchBox:not(:-moz-lwtheme):not([focused="true"]) {
     border-color: ThreeDShadow;
   }
 }
 
 /* Add margins to show the hover box-shadow */
-#searchInput,
-#IMSearchInput,
+.gloda-search,
 #peopleSearchInput {
   margin-top: 3px;
   margin-bottom: 3px;
 }
 
 .searchBox[focused="true"],
 .themeableSearchBox[focused="true"] {
   border-color: Highlight;
 }
 
 /* special treatment because these boxes are on themable toolbars */
-#searchInput,
-#IMSearchInput,
+.gloda-search,
 #peopleSearchInput,
 .remote-gloda-search {
   min-height: 26px;
 }
 
 .autocomplete-richlistitem[type^="gloda-"] {
   padding-inline-start: 12px;
 }