Bug 1088660 - Improve the search bar UI to support one-off searches, r=felipe, a=gavin.
authorFlorian Quèze <florian@queze.net>
Thu, 20 Nov 2014 20:55:38 +0100
changeset 226098 9a8a27107a6f58af3d641797cf680281afc78282
parent 226097 a32832a5fe59245b5ea8576a9d5a0f156311bce4
child 226099 8622ff2cb47b1642bdee99cfd517f12b792f99b0
push id4152
push userflorian@queze.net
push dateThu, 20 Nov 2014 20:07:28 +0000
treeherdermozilla-beta@8622ff2cb47b [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersfelipe, gavin
bugs1088660
milestone34.0
Bug 1088660 - Improve the search bar UI to support one-off searches, r=felipe, a=gavin.
browser/app/profile/firefox.js
browser/base/content/browser.css
browser/base/content/browser.xul
browser/base/content/urlbarBindings.xml
browser/components/customizableui/test/browser_940307_panel_click_closure_handling.js
browser/components/preferences/in-content/jar.mn
browser/components/preferences/in-content/preferences.js
browser/components/preferences/in-content/preferences.xul
browser/components/preferences/in-content/search.css
browser/components/preferences/in-content/search.js
browser/components/preferences/in-content/search.xul
browser/components/preferences/jar.mn
browser/components/preferences/preferences.js
browser/components/preferences/preferences.xul
browser/components/preferences/search.css
browser/components/preferences/search.js
browser/components/preferences/search.xul
browser/components/search/content/search.xml
browser/components/search/test/browser_405664.js
browser/components/search/test/browser_426329.js
browser/locales/en-US/firefox-l10n.js
browser/modules/test/browser_UITour.js
browser/themes/linux/jar.mn
browser/themes/linux/preferences/preferences.css
browser/themes/linux/searchbar.css
browser/themes/osx/jar.mn
browser/themes/osx/preferences/preferences.css
browser/themes/osx/searchbar.css
browser/themes/shared/incontentprefs/icons.png
browser/themes/shared/incontentprefs/icons@2x.png
browser/themes/shared/incontentprefs/preferences.css
browser/themes/shared/search-pref.png
browser/themes/windows/jar.mn
browser/themes/windows/preferences/preferences.css
browser/themes/windows/searchbar.css
toolkit/components/search/SearchSuggestionController.jsm
toolkit/components/search/nsSearchSuggestions.js
toolkit/components/search/tests/xpcshell/test_searchSuggest.js
toolkit/themes/shared/in-content/common.inc.css
--- a/browser/app/profile/firefox.js
+++ b/browser/app/profile/firefox.js
@@ -412,16 +412,18 @@ pref("browser.search.update", true);
 pref("browser.search.update.log", false);
 
 // Check whether we need to perform engine updates every 6 hours
 pref("browser.search.update.interval", 21600);
 
 // enable search suggestions by default
 pref("browser.search.suggest.enabled", true);
 
+pref("browser.search.showOneOffButtons", false);
+
 #ifdef MOZ_OFFICIAL_BRANDING
 // {moz:official} expands to "official"
 pref("browser.search.official", true);
 #endif
 
 pref("browser.sessionhistory.max_entries", 50);
 
 // handle links targeting new windows
--- a/browser/base/content/browser.css
+++ b/browser/base/content/browser.css
@@ -396,16 +396,25 @@ panel[noactions] > richlistbox > richlis
 #urlbar:not([actiontype]) > #urlbar-display-box {
   display: none;
 }
 
 #PopupAutoComplete {
   -moz-binding: url("chrome://browser/content/urlbarBindings.xml#browser-autocomplete-result-popup");
 }
 
+#PopupSearchAutoComplete {
+  -moz-binding: url("chrome://browser/content/urlbarBindings.xml#browser-search-autocomplete-result-popup");
+  margin-left: -23px;
+}
+
+searchbar[oneoffui] {
+  -moz-binding: url("chrome://browser/content/search/search.xml#searchbar-flare") !important;
+}
+
 #PopupAutoCompleteRichResult {
   -moz-binding: url("chrome://browser/content/urlbarBindings.xml#urlbar-rich-result-popup");
 }
 
 #urlbar[pageproxystate="invalid"] > #urlbar-icons > .urlbar-icon,
 #urlbar[pageproxystate="invalid"][focused="true"] > #urlbar-go-button ~ toolbarbutton,
 #urlbar[pageproxystate="valid"] > #urlbar-go-button,
 #urlbar:not([focused="true"]) > #urlbar-go-button {
--- a/browser/base/content/browser.xul
+++ b/browser/base/content/browser.xul
@@ -135,16 +135,19 @@
                oncommand="gotoHistoryIndex(event); event.stopPropagation();"
                onclick="checkForMiddleClick(this, event);"/>
     <tooltip id="aHTMLTooltip" page="true"/>
     <tooltip id="remoteBrowserTooltip"/>
 
     <!-- for search and content formfill/pw manager -->
     <panel type="autocomplete" id="PopupAutoComplete" noautofocus="true" hidden="true"/>
 
+    <!-- for search with one-off buttons -->
+    <panel type="autocomplete" id="PopupSearchAutoComplete" noautofocus="true" hidden="true"/>
+
     <!-- for url bar autocomplete -->
     <panel type="autocomplete-richlistbox" id="PopupAutoCompleteRichResult" noautofocus="true" hidden="true"/>
 
     <!-- for select dropdowns -->
     <menupopup id="ContentSelectDropdown" rolluponmousewheel="true" hidden="true"/>
 
     <!-- for invalid form error message -->
     <panel id="invalid-form-popup" type="arrow" orient="vertical" noautofocus="true" hidden="true" level="parent">
--- a/browser/base/content/urlbarBindings.xml
+++ b/browser/base/content/urlbarBindings.xml
@@ -888,16 +888,185 @@
             var where = whereToOpenLink(aEvent, false, true);
             searchBar.doSearch(search, where);
           }
           ]]></body>
         </method>
       </implementation>
     </binding>
 
+  <!-- Note: this binding is applied to the autocomplete popup used in the Search bar -->
+  <binding id="browser-search-autocomplete-result-popup" extends="chrome://browser/content/urlbarBindings.xml#browser-autocomplete-result-popup">
+    <resources>
+      <stylesheet src="chrome://browser/skin/searchbar.css"/>
+    </resources>
+    <content ignorekeys="true" level="top" consumeoutsideclicks="false">
+      <xul:hbox class="search-panel-header search-panel-current-engine">
+        <xul:image class="searchbar-engine-image" xbl:inherits="src"/>
+        <xul:label anonid="searchbar-engine-name"/>
+      </xul:hbox>
+      <xul:tree anonid="tree" class="autocomplete-tree plain search-panel-tree" hidecolumnpicker="true" flex="1" seltype="single">
+        <xul:treecols anonid="treecols">
+          <xul:treecol id="treecolAutoCompleteValue" class="autocomplete-treecol" flex="1" overflow="true"/>
+        </xul:treecols>
+        <xul:treechildren class="autocomplete-treebody"/>
+      </xul:tree>
+      <xul:hbox anonid="search-panel-one-offs-header"
+                class="search-panel-header search-panel-current-input">
+        <xul:label anonid="searchbar-oneoffheader-before" value="Search for "/>
+        <xul:label anonid="searchbar-oneoffheader-searchtext" flex="1" crop="end" class="search-panel-input-value"/>
+        <xul:label anonid="searchbar-oneoffheader-after" flex="10000" value=" on:"/>
+      </xul:hbox>
+      <xul:description anonid="search-panel-one-offs" class="search-panel-one-offs"/>
+      <xul:button anonid="search-settings"
+                  oncommand="openPreferences('paneSearch')"
+                  class="search-setting-button search-panel-header"
+                  label="Change Search Settings"/>
+    </content>
+    <handlers>
+      <handler event="popupshowing"><![CDATA[
+        let currentEngine = Services.search.currentEngine;
+        let uri = currentEngine.iconURI;
+        if (uri)
+          uri = uri.spec;
+        this.setAttribute("src", PlacesUtils.getImageURLForResolution(window, uri));
+        document.getAnonymousElementByAttribute(this, "anonid", "searchbar-engine-name")
+                .setAttribute("value", currentEngine.name + " Search");
+
+        let headerSearchText =
+          document.getAnonymousElementByAttribute(this, "anonid",
+                                                  "searchbar-oneoffheader-searchtext");
+        let textbox = document.getElementById("searchbar").textbox;
+        let keyPressHandler = function() {
+          headerSearchText.setAttribute("value", textbox.value);
+        };
+        textbox.addEventListener("keyup", keyPressHandler);
+        this.addEventListener("popuphiding", function hiding() {
+          textbox.removeEventListener("keyup", keyPressHandler);
+          this.removeEventListener("popuphiding", hiding);
+        });
+        keyPressHandler();
+
+        const kXULNS =
+          "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";
+
+        let list = document.getAnonymousElementByAttribute(this, "anonid",
+                                                           "search-panel-one-offs")
+        while (list.firstChild)
+          list.firstChild.remove();
+
+        let hiddenList;
+        try {
+          let pref =
+            Services.prefs.getCharPref("browser.search.hiddenOneOffs");
+          hiddenList = pref ? pref.split(",") : [];
+        } catch(e) {
+          hiddenList = [];
+        }
+
+        let engines = Services.search.getVisibleEngines()
+                              .filter(e => e.name != currentEngine.name &&
+                                           hiddenList.indexOf(e.name) == -1);
+
+        let header = document.getAnonymousElementByAttribute(this, "anonid",
+                                                             "search-panel-one-offs-header")
+        header.collapsed = list.collapsed = !engines.length;
+
+        if (!engines.length)
+          return;
+
+        let panel = document.getElementById("PopupSearchAutoComplete");
+        let minWidth = parseInt(panel.width) + 23;
+        panel.setAttribute("style", "min-width: " + minWidth + "px");
+
+        // 49px is the min-width of each search engine button,
+        // adapt this const when changing the css.
+        // It's actually 48px + 1px of right border.
+        // The + 1 is because the last button doesn't have a right border.
+        let panelWidth = parseInt(panel.clientWidth);
+        let enginesPerRow = Math.floor((panelWidth + 1) / 49);
+        let buttonWidth = Math.floor(panelWidth / enginesPerRow);
+        // There will be an emtpy area of:
+        //   panelWidth - enginesPerRow * buttonWidth  px
+        // at the end of each row.
+
+        // If the <description> tag with the list of search engines doesn't have
+        // a fixed height, the panel will be sized incorrectly, causing the bottom
+        // of the suggestion <tree> to be hidden.
+        let rowCount = Math.ceil(engines.length / enginesPerRow);
+        let height = rowCount * 33; // 32px per row, 1px border.
+        list.setAttribute("height", height + "px");
+
+        let dummyItems = enginesPerRow - (engines.length % enginesPerRow || enginesPerRow);
+        for (let i = 0; i < engines.length; ++i) {
+          let engine = engines[i];
+          if (!engine.iconURI)
+            continue;
+          let button = document.createElementNS(kXULNS, "button");
+          let uri = PlacesUtils.getImageURLForResolution(window, engine.iconURI.spec);
+          button.setAttribute("label", engine.name);
+          button.setAttribute("image", uri);
+          button.setAttribute("class", "searchbar-engine-one-off-item");
+          button.setAttribute("tooltiptext", engine.name);
+          button.setAttribute("width", buttonWidth);
+          button.engine = engine;
+
+          if ((i + 1) % enginesPerRow == 0)
+            button.classList.add("last-of-row");
+
+          if (i >= engines.length + dummyItems - enginesPerRow)
+            button.classList.add("last-row");
+
+          list.appendChild(button);
+        }
+
+        while (dummyItems) {
+          let button = document.createElementNS(kXULNS, "button");
+          button.setAttribute("class", "searchbar-engine-one-off-item dummy last-row");
+          button.setAttribute("width", buttonWidth);
+
+          if (!--dummyItems)
+            button.classList.add("last-of-row");
+
+          list.appendChild(button);
+        }
+      ]]></handler>
+
+      <handler event="mousedown"><![CDATA[
+        // Required to receive click events from the buttons on Linux.
+        event.preventDefault();
+      ]]></handler>
+
+      <handler event="mouseover"><![CDATA[
+        let target = event.originalTarget;
+        if (target.localName == "button" &&
+            target.classList.contains("searchbar-engine-one-off-item") &&
+            !target.classList.contains("dummy")) {
+          let list = document.getAnonymousElementByAttribute(this, "anonid",
+                                                             "search-panel-one-offs")
+          for (let button = list.firstChild; button; button = button.nextSibling)
+            button.removeAttribute("selected");
+        }
+      ]]></handler>
+
+      <handler event="click"><![CDATA[
+        if (event.button == 2)
+          return; // ignore right clicks.
+
+        let button = event.originalTarget;
+        if (button.localName != "button" || !button.engine)
+          return;
+
+        let searchbar = document.getElementById("searchbar");
+        searchbar.handleSearchCommand(event, button.engine);
+      ]]></handler>
+    </handlers>
+  </binding>
+
+
     <binding id="urlbar-rich-result-popup" extends="chrome://global/content/bindings/autocomplete.xml#autocomplete-rich-result-popup">
       <implementation>
       <field name="_maxResults">0</field>
 
       <field name="_bundle" readonly="true">
         Cc["@mozilla.org/intl/stringbundle;1"].
           getService(Ci.nsIStringBundleService).
           createBundle("chrome://browser/locale/places/places.properties");
--- a/browser/components/customizableui/test/browser_940307_panel_click_closure_handling.js
+++ b/browser/components/customizableui/test/browser_940307_panel_click_closure_handling.js
@@ -62,38 +62,50 @@ add_task(function() {
 add_task(function*() {
   let searchbar = document.getElementById("searchbar");
   gCustomizeMode.addToPanel(searchbar);
   let placement = CustomizableUI.getPlacementOfWidget("search-container");
   is(placement.area, CustomizableUI.AREA_PANEL, "Should be in panel");
   yield PanelUI.show();
   yield waitForCondition(() => "value" in searchbar && searchbar.value === "");
 
+  // Focusing a non-empty searchbox will cause us to open the
+  // autocomplete panel and search for suggestions, which would
+  // trigger network requests. Temporarily disable suggestions.
+  let suggestEnabled =
+    Services.prefs.getBoolPref("browser.search.suggest.enabled");
+  Services.prefs.setBoolPref("browser.search.suggest.enabled", false);
+
   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 contextMenuShown = promisePanelElementShown(window, contextmenu);
   EventUtils.synthesizeMouseAtCenter(searchbar, {type: "contextmenu", button: 2});
   yield contextMenuShown;
 
   ok(isPanelUIOpen(), "Panel should still be open");
 
   let selectAll = contextmenu.querySelector("[cmd='cmd_selectAll']");
   let contextMenuHidden = promisePanelElementHidden(window, contextmenu);
   EventUtils.synthesizeMouseAtCenter(selectAll, {});
   yield contextMenuHidden;
 
+  // Hide the suggestion panel.
+  searchbar.textbox.popup.hidePopup();
+
   ok(isPanelUIOpen(), "Panel should still be open");
 
   let hiddenPanelPromise = promisePanelHidden(window);
   EventUtils.synthesizeKey("VK_ESCAPE", {});
   yield hiddenPanelPromise;
   ok(!isPanelUIOpen(), "Panel should no longer be open");
+
+  Services.prefs.setBoolPref("browser.search.suggest.enabled", suggestEnabled);
 });
 
 add_task(function*() {
   button = document.createElement("toolbarbutton");
   button.id = "browser_946166_button_disabled";
   button.setAttribute("disabled", "true");
   button.setAttribute("label", "Button");
   PanelUI.contents.appendChild(button);
@@ -116,9 +128,8 @@ registerCleanupFunction(function() {
   }
   // Sadly this isn't task.jsm-enabled, so we can't wait for this to happen. But we should
   // definitely close it here and hope it won't interfere with other tests.
   // Of course, all the tests are meant to do this themselves, but if they fail...
   if (isPanelUIOpen()) {
     PanelUI.hide();
   }
 });
-
--- a/browser/components/preferences/in-content/jar.mn
+++ b/browser/components/preferences/in-content/jar.mn
@@ -9,8 +9,10 @@ browser.jar:
 
 *  content/browser/preferences/in-content/main.js
 *  content/browser/preferences/in-content/privacy.js
 *  content/browser/preferences/in-content/advanced.js
 *  content/browser/preferences/in-content/applications.js
    content/browser/preferences/in-content/content.js
    content/browser/preferences/in-content/sync.js
    content/browser/preferences/in-content/security.js
+   content/browser/preferences/in-content/search.css
+   content/browser/preferences/in-content/search.js
--- a/browser/components/preferences/in-content/preferences.js
+++ b/browser/components/preferences/in-content/preferences.js
@@ -19,16 +19,17 @@ addEventListener("DOMContentLoaded", fun
   init_all();
 });
 
 function init_all() {
   document.documentElement.instantApply = true;
 
   gSubDialog.init();
   gMainPane.init();
+  gSearchPane.init();
   gPrivacyPane.init();
   gAdvancedPane.init();
   gApplicationsPane.init();
   gContentPane.init();
   gSyncPane.init();
   gSecurityPane.init();
 
   var initFinished = new CustomEvent("Initialized", {
--- a/browser/components/preferences/in-content/preferences.xul
+++ b/browser/components/preferences/in-content/preferences.xul
@@ -9,16 +9,17 @@
 
 <?xml-stylesheet href="chrome://browser/skin/preferences/preferences.css"?>
 <?xml-stylesheet href="chrome://global/skin/in-content/common.css"?>
 <?xml-stylesheet
   href="chrome://browser/skin/preferences/in-content/preferences.css"?>
 <?xml-stylesheet
   href="chrome://browser/content/preferences/handlers.css"?>
 <?xml-stylesheet href="chrome://browser/skin/preferences/applications.css"?>
+<?xml-stylesheet href="chrome://browser/content/preferences/in-content/search.css"?>
 
 <!DOCTYPE page [
 <!ENTITY % brandDTD SYSTEM "chrome://branding/locale/brand.dtd">
 <!ENTITY % globalPreferencesDTD SYSTEM "chrome://global/locale/preferences.dtd">
 <!ENTITY % preferencesDTD SYSTEM
   "chrome://browser/locale/preferences/preferences.dtd">
 <!ENTITY % privacyDTD SYSTEM "chrome://browser/locale/preferences/privacy.dtd">
 <!ENTITY % tabsDTD SYSTEM "chrome://browser/locale/preferences/tabs.dtd">
@@ -91,16 +92,26 @@
                     value="paneGeneral"
                     helpTopic="prefs-main"
                     tooltiptext="&paneGeneral.title;"
                     align="center">
         <image class="category-icon"/>
         <label class="category-name" flex="1">&paneGeneral.title;</label>
       </richlistitem>
 
+      <richlistitem id="category-search"
+                    class="category"
+                    value="paneSearch"
+                    helpTopic="prefs-main"
+                    tooltiptext="Search"
+                    align="center">
+        <image class="category-icon"/>
+        <label class="category-name" flex="1">Search</label>
+      </richlistitem>
+
       <richlistitem id="category-content"
                     class="category"
                     value="paneContent"
                     helpTopic="prefs-content"
                     tooltiptext="&paneContent.title;"
                     align="center">
         <image class="category-icon"/>
         <label class="category-name" flex="1">&paneContent.title;</label>
@@ -157,16 +168,17 @@
         <image class="category-icon"/>
         <label class="category-name" flex="1">&paneAdvanced.title;</label>
       </richlistitem>
     </richlistbox>
 
     <vbox class="main-content" flex="1">
       <prefpane id="mainPrefPane">
 #include main.xul
+#include search.xul
 #include privacy.xul
 #include advanced.xul
 #include applications.xul
 #include content.xul
 #include security.xul
 #ifdef MOZ_SERVICES_SYNC
 #include sync.xul
 #endif
new file mode 100644
--- /dev/null
+++ b/browser/components/preferences/in-content/search.css
@@ -0,0 +1,15 @@
+/* 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/. */
+
+#oneClickProvidersList richlistitem {
+  -moz-binding: url("chrome://global/content/bindings/checkbox.xml#checkbox");
+}
+
+.checkbox-label-box {
+  -moz-box-align: center;
+}
+
+.checkbox-icon {
+  -moz-margin-end: 8px;
+}
new file mode 100644
--- /dev/null
+++ b/browser/components/preferences/in-content/search.js
@@ -0,0 +1,73 @@
+/* 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/. */
+
+Components.utils.import("resource://gre/modules/XPCOMUtils.jsm");
+
+var gSearchPane = {
+
+  init: function ()
+  {
+    if (!Services.prefs.getBoolPref("browser.search.showOneOffButtons")) {
+      document.getElementById("category-search").hidden = true;
+      if (document.location.hash == "#search")
+        document.location.hash = "";
+      return;
+    }
+
+    let list = document.getElementById("defaultEngine");
+    let currentEngine = Services.search.currentEngine.name;
+    Services.search.getVisibleEngines().forEach(e => {
+      let item = list.appendItem(e.name);
+      item.setAttribute("class", "menuitem-iconic");
+      item.setAttribute("image", e.iconURI.spec);
+      item.engine = e;
+      if (e.name == currentEngine)
+        list.selectedItem = item;
+    });
+
+    this.displayOneClickEnginesList();
+
+    document.getElementById("oneClickProvidersList")
+            .addEventListener("CheckboxStateChange", gSearchPane.saveOneClickEnginesList);
+  },
+
+  displayOneClickEnginesList: function () {
+    let richlistbox = document.getElementById("oneClickProvidersList");
+    let pref = document.getElementById("browser.search.hiddenOneOffs").value;
+    let hiddenList = pref ? pref.split(",") : [];
+
+    while (richlistbox.firstChild)
+      richlistbox.firstChild.remove();
+
+    let currentEngine = Services.search.currentEngine.name;
+    Services.search.getVisibleEngines().forEach(e => {
+      if (e.name == currentEngine)
+        return;
+
+      let item = document.createElement("richlistitem");
+      item.setAttribute("label", e.name);
+      if (hiddenList.indexOf(e.name) == -1)
+        item.setAttribute("checked", "true");
+      item.setAttribute("src", e.iconURI.spec);
+      richlistbox.appendChild(item);
+    });
+  },
+
+  saveOneClickEnginesList: function () {
+    let richlistbox = document.getElementById("oneClickProvidersList");
+    let hiddenList = [];
+    for (let child of richlistbox.childNodes) {
+      if (!child.checked)
+        hiddenList.push(child.getAttribute("label"));
+    }
+    document.getElementById("browser.search.hiddenOneOffs").value =
+      hiddenList.join(",");
+  },
+
+  setDefaultEngine: function () {
+    Services.search.currentEngine =
+      document.getElementById("defaultEngine").selectedItem.engine;
+    this.displayOneClickEnginesList();
+  }
+};
new file mode 100644
--- /dev/null
+++ b/browser/components/preferences/in-content/search.xul
@@ -0,0 +1,48 @@
+    <preferences id="searchPreferences">
+
+      <!-- Suggest -->
+      <preference id="browser.search.suggest.enabled"
+                  name="browser.search.suggest.enabled"
+                  type="bool"/>
+
+      <!-- One off providers -->
+      <preference id="browser.search.hiddenOneOffs"
+                  name="browser.search.hiddenOneOffs"
+                  type="string"/>
+
+    </preferences>
+
+    <script type="application/javascript"
+            src="chrome://browser/content/preferences/in-content/search.js"/>
+
+<hbox id="header-search"
+      class="header"
+      hidden="true"
+      data-category="paneSearch">
+  <label class="header-name">Search</label>
+</hbox>
+
+
+    <!-- Default Search Engine -->
+    <groupbox id="defaultEngineGroup" align="start" data-category="paneSearch">
+      <caption label="Default Search Engine"/>
+      <label>Choose your default search engine. &brandShortName; uses it in the location bar, search bar, and start pages.</label>
+      <menulist id="defaultEngine" oncommand="gSearchPane.setDefaultEngine();">
+        <menupopup/>
+      </menulist>
+      <checkbox id="suggestionsInSearchFieldsCheckbox"
+                label="Provide search suggestions"
+                accesskey="s"
+                preference="browser.search.suggest.enabled"/>
+    </groupbox>
+
+    <groupbox id="oneClickSearchProvidersGroup" data-category="paneSearch">
+      <caption label="One-click search engines"/>
+      <label>The search bar lets you search alternate engines directly. Choose which ones to display.</label>
+
+      <richlistbox id="oneClickProvidersList" style="min-height: 50px;"/>
+      <hbox pack="end" style="margin-bottom: 2em">
+        <label id="addEngines" class="text-link" value="Add more search providers…"
+               onclick="if (event.button == 0) { Services.wm.getMostRecentWindow('navigator:browser').BrowserSearch.loadAddEngines(); }"/>
+      </hbox>
+    </groupbox>
--- a/browser/components/preferences/jar.mn
+++ b/browser/components/preferences/jar.mn
@@ -26,24 +26,28 @@ browser.jar:
     content/browser/preferences/handlers.css
 *   content/browser/preferences/languages.xul
     content/browser/preferences/languages.js
 *   content/browser/preferences/main.xul
 *   content/browser/preferences/main.js
 *   content/browser/preferences/permissions.xul
 *   content/browser/preferences/permissions.js
 *   content/browser/preferences/preferences.xul
+    content/browser/preferences/preferences.js
     content/browser/preferences/privacy.xul
 *   content/browser/preferences/privacy.js
     content/browser/preferences/sanitize.xul
     content/browser/preferences/sanitize.js
     content/browser/preferences/security.xul
     content/browser/preferences/security.js
     content/browser/preferences/selectBookmark.xul
     content/browser/preferences/selectBookmark.js
 #ifdef MOZ_SERVICES_SYNC
     content/browser/preferences/sync.xul
     content/browser/preferences/sync.js
 #endif
+    content/browser/preferences/search.xul
+    content/browser/preferences/search.css
+    content/browser/preferences/search.js
 *   content/browser/preferences/tabs.xul
 *   content/browser/preferences/tabs.js
 *   content/browser/preferences/translation.xul
     content/browser/preferences/translation.js
new file mode 100644
--- /dev/null
+++ b/browser/components/preferences/preferences.js
@@ -0,0 +1,19 @@
+/* - 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/. */
+
+"use strict";
+
+Components.utils.import("resource://gre/modules/Services.jsm");
+
+if (!Services.prefs.getBoolPref("browser.search.showOneOffButtons")) {
+  addEventListener("load", function onLoad() {
+    removeEventListener("load", onLoad);
+    let pane =
+      document.getAnonymousElementByAttribute(document.documentElement,
+                                              "pane", "paneSearch");
+    pane.hidden = true;
+    if (pane.selected)
+      document.documentElement.showPane(document.getElementById("paneMain"));
+  });
+}
--- a/browser/components/preferences/preferences.xul
+++ b/browser/components/preferences/preferences.xul
@@ -11,16 +11,17 @@
 
 <!-- XXX This should be in applications.xul, but bug 393953 means putting it
    - there causes the Applications pane not to work the first time you open
    - the Preferences dialog in a browsing session, so we work around the problem
    - by putting it here instead.
    -->
 <?xml-stylesheet href="chrome://browser/content/preferences/handlers.css"?>
 <?xml-stylesheet href="chrome://browser/skin/preferences/applications.css"?>
+<?xml-stylesheet href="chrome://browser/content/preferences/search.css"?>
 
 <!DOCTYPE prefwindow [
 <!ENTITY % brandDTD SYSTEM "chrome://branding/locale/brand.dtd">
 <!ENTITY % preferencesDTD SYSTEM "chrome://browser/locale/preferences/preferences.dtd">
 %brandDTD;
 %preferencesDTD;
 ]>
 
@@ -61,29 +62,33 @@
     <stringbundle id="bundleBrand" src="chrome://branding/locale/brand.properties"/>
     <stringbundle id="bundlePreferences"
                   src="chrome://browser/locale/preferences/preferences.properties"/>
 
     <prefpane id="paneMain" label="&paneGeneral.title;"
               src="chrome://browser/content/preferences/main.xul"/>
     <prefpane id="paneTabs" label="&paneTabs.title;"
               src="chrome://browser/content/preferences/tabs.xul"/>
+    <prefpane id="paneSearch" label="Search"
+              src="chrome://browser/content/preferences/search.xul"/>
     <prefpane id="paneContent" label="&paneContent.title;"
               src="chrome://browser/content/preferences/content.xul"/>
     <prefpane id="paneApplications" label="&paneApplications.title;"
               src="chrome://browser/content/preferences/applications.xul"/>
     <prefpane id="panePrivacy" label="&panePrivacy.title;"
               src="chrome://browser/content/preferences/privacy.xul"/>
     <prefpane id="paneSecurity" label="&paneSecurity.title;"
               src="chrome://browser/content/preferences/security.xul"/>
 #ifdef MOZ_SERVICES_SYNC
     <prefpane id="paneSync" label="&paneSync.title;"
               src="chrome://browser/content/preferences/sync.xul"/>
 #endif
     <prefpane id="paneAdvanced" label="&paneAdvanced.title;"
               src="chrome://browser/content/preferences/advanced.xul"/>
 
+    <script type="application/javascript"
+            src="chrome://browser/content/preferences/preferences.js"/>
 #ifdef XP_MACOSX
 #include ../../base/content/browserMountPoints.inc
 #endif
 
 </prefwindow>
 
new file mode 100644
--- /dev/null
+++ b/browser/components/preferences/search.css
@@ -0,0 +1,19 @@
+/* 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/. */
+
+#oneClickProvidersList richlistitem {
+  -moz-binding: url("chrome://global/content/bindings/checkbox.xml#checkbox");
+  -moz-padding-start: 5px;
+  height: 22px; /* setting the height of checkboxes is required to let the
+                   window auto-sizing code work. */
+}
+
+.checkbox-label-box {
+  -moz-box-align: center;
+}
+
+.checkbox-icon {
+  margin: 3px 3px;
+}
+
new file mode 100644
--- /dev/null
+++ b/browser/components/preferences/search.js
@@ -0,0 +1,66 @@
+/* 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/. */
+
+Components.utils.import("resource://gre/modules/XPCOMUtils.jsm");
+
+var gSearchPane = {
+
+  init: function ()
+  {
+    let list = document.getElementById("defaultEngine");
+    let currentEngine = Services.search.currentEngine.name;
+    Services.search.getVisibleEngines().forEach(e => {
+      let item = list.appendItem(e.name);
+      item.setAttribute("class", "menuitem-iconic");
+      item.setAttribute("image", e.iconURI.spec);
+      item.engine = e;
+      if (e.name == currentEngine)
+        list.selectedItem = item;
+    });
+
+    this.displayOneClickEnginesList();
+
+    document.getElementById("oneClickProvidersList")
+            .addEventListener("CheckboxStateChange", gSearchPane.saveOneClickEnginesList);
+  },
+
+  displayOneClickEnginesList: function () {
+    let richlistbox = document.getElementById("oneClickProvidersList");
+    let pref = document.getElementById("browser.search.hiddenOneOffs").value;
+    let hiddenList = pref ? pref.split(",") : [];
+
+    while (richlistbox.firstChild)
+      richlistbox.firstChild.remove();
+
+    let currentEngine = Services.search.currentEngine.name;
+    Services.search.getVisibleEngines().forEach(e => {
+      if (e.name == currentEngine)
+        return;
+
+      let item = document.createElement("richlistitem");
+      item.setAttribute("label", e.name);
+      if (hiddenList.indexOf(e.name) == -1)
+        item.setAttribute("checked", "true");
+      item.setAttribute("src", e.iconURI.spec);
+      richlistbox.appendChild(item);
+    });
+  },
+
+  saveOneClickEnginesList: function () {
+    let richlistbox = document.getElementById("oneClickProvidersList");
+    let hiddenList = [];
+    for (let child of richlistbox.childNodes) {
+      if (!child.checked)
+        hiddenList.push(child.getAttribute("label"));
+    }
+    document.getElementById("browser.search.hiddenOneOffs").value =
+      hiddenList.join(",");
+  },
+
+  setDefaultEngine: function () {
+    Services.search.currentEngine =
+      document.getElementById("defaultEngine").selectedItem.engine;
+    this.displayOneClickEnginesList();
+  }
+};
new file mode 100644
--- /dev/null
+++ b/browser/components/preferences/search.xul
@@ -0,0 +1,61 @@
+<?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/. -->
+
+<!DOCTYPE overlay [
+<!ENTITY % brandDTD SYSTEM "chrome://branding/locale/brand.dtd">
+%brandDTD;
+]>
+
+<overlay id="SearchPaneOverlay"
+         xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
+         xmlns:html="http://www.w3.org/1999/xhtml">
+
+  <prefpane id="paneSearch" helpTopic="prefs-search"
+            onpaneload="gSearchPane.init();">
+
+    <preferences id="searchPreferences">
+
+      <!-- Suggest -->
+      <preference id="browser.search.suggest.enabled"
+                  name="browser.search.suggest.enabled"
+                  type="bool"/>
+
+      <!-- One off providers -->
+      <preference id="browser.search.hiddenOneOffs"
+                  name="browser.search.hiddenOneOffs"
+                  type="string"/>
+
+    </preferences>
+
+    <script type="application/javascript" src="chrome://browser/content/preferences/search.js"/>
+
+    <!-- Default Search Engine -->
+    <groupbox id="defaultEngineGroup" align="start">
+      <caption label="Default Search Engine"/>
+      <label>Choose your default search engine. &brandShortName; uses it in the location bar, search bar, and start pages.</label>
+      <menulist id="defaultEngine" oncommand="gSearchPane.setDefaultEngine();">
+        <menupopup/>
+      </menulist>
+      <checkbox id="suggestionsInSearchFieldsCheckbox"
+                label="Provide search suggestions"
+                accesskey="s"
+                preference="browser.search.suggest.enabled"/>
+    </groupbox>
+
+    <groupbox id="oneClickSearchProvidersGroup">
+      <caption label="One-click search engines"/>
+      <label>The search bar lets you search alternate engines directly. Choose which ones to display.</label>
+
+      <richlistbox id="oneClickProvidersList" style="min-height: 50px;"/>
+      <hbox pack="end" style="margin-bottom: 2em">
+        <label id="addEngines" class="text-link" value="Add more search providers…"
+               onclick="if (event.button == 0) { Services.wm.getMostRecentWindow('navigator:browser').BrowserSearch.loadAddEngines(); }"/>
+      </hbox>
+    </groupbox>
+
+  </prefpane>
+
+</overlay>
--- a/browser/components/search/content/search.xml
+++ b/browser/components/search/content/search.xml
@@ -68,16 +68,22 @@
                      onclick="handleSearchCommand(event);"
                      tooltiptext="&searchEndCap.label;"/>
         </xul:hbox>
       </xul:textbox>
     </content>
 
     <implementation implements="nsIObserver">
       <constructor><![CDATA[
+        if (!this.hasAttribute("oneoffui") &&
+            Services.prefs.getBoolPref("browser.search.showOneOffButtons")) {
+          this.setAttribute("oneoffui", "true");
+          return;
+        }
+
         if (this.parentNode.parentNode.localName == "toolbarpaletteitem")
           return;
         // Make sure we rebuild the popup in onpopupshowing
         this._needToBuildPopup = true;
 
         var os =
                Components.classes["@mozilla.org/observer-service;1"]
                          .getService(Components.interfaces.nsIObserverService);
@@ -95,29 +101,35 @@
             this.updateDisplay();
           } else {
             Components.utils.reportError("Cannot initialize search service, bailing out: " + aStatus);
           }
         }).bind(this));
       ]]></constructor>
 
       <destructor><![CDATA[
+        this.destroy();
+      ]]></destructor>
+
+      <method name="destroy">
+        <body><![CDATA[
         if (this._initialized) {
           this._initialized = false;
 
           var os = Components.classes["@mozilla.org/observer-service;1"]
                              .getService(Components.interfaces.nsIObserverService);
           os.removeObserver(this, "browser-search-engine-modified");
         }
 
         // Make sure to break the cycle from _textbox to us. Otherwise we leak
         // the world. But make sure it's actually pointing to us.
         if (this._textbox.mController.input == this)
           this._textbox.mController.input = null;
-      ]]></destructor>
+        ]]></body>
+      </method>
 
       <field name="_stringBundle">document.getAnonymousElementByAttribute(this,
           "anonid", "searchbar-stringbundle");</field>
       <field name="_textbox">document.getAnonymousElementByAttribute(this,
           "anonid", "searchbar-textbox");</field>
       <field name="_popup">document.getAnonymousElementByAttribute(this,
           "anonid", "searchbar-popup");</field>
       <field name="_ss">null</field>
@@ -303,17 +315,22 @@
 
       <method name="updateDisplay">
         <body><![CDATA[
           var uri = this.currentEngine.iconURI;
           this.setIcon(this, uri ? uri.spec : "");
 
           var name = this.currentEngine.name;
           var text = this._stringBundle.getFormattedString("searchtip", [name]);
-          this._textbox.placeholder = name;
+
+          if (Services.prefs.getBoolPref("browser.search.showOneOffButtons"))
+            this._textbox.placeholder = "Search";
+          else
+            this._textbox.placeholder = name;
+
           this._textbox.label = text;
           this._textbox.tooltipText = text;
         ]]></body>
       </method>
 
       <!-- Rebuilds the dynamic portion of the popup menu (i.e., the menu items
            for new search engines that can be added to the available list).  This
            is called each time the popup is shown.
@@ -448,56 +465,59 @@
 
           aEvent.preventDefault();
           aEvent.stopPropagation();
         ]]></body>
       </method>
 
       <method name="handleSearchCommand">
         <parameter name="aEvent"/>
+        <parameter name="aEngine"/>
         <body><![CDATA[
           var textBox = this._textbox;
           var textValue = textBox.value;
 
           var where = "current";
           if (aEvent && aEvent.originalTarget.getAttribute("anonid") == "search-go-button") {
             if (aEvent.button == 2)
               return;
             where = whereToOpenLink(aEvent, false, true);
           }
           else {
             var newTabPref = textBox._prefBranch.getBoolPref("browser.search.openintab");
             if ((aEvent && aEvent.altKey) ^ newTabPref)
               where = "tab";
           }
 
-          this.doSearch(textValue, where);
+          this.doSearch(textValue, where, aEngine);
         ]]></body>
       </method>
 
       <method name="doSearch">
         <parameter name="aData"/>
         <parameter name="aWhere"/>
+        <parameter name="aEngine"/>
         <body><![CDATA[
           var textBox = this._textbox;
         
           // Save the current value in the form history
           if (aData && !PrivateBrowsingUtils.isWindowPrivate(window)) {
             this.FormHistory.update(
               { op : "bump",
                 fieldname : textBox.getAttribute("autocompletesearchparam"),
                 value : aData },
               { handleError : function(aError) {
                   Components.utils.reportError("Saving search to form history failed: " + aError.message);
               }});
           }
-          
+
+          let engine = aEngine || this.currentEngine;
+          var submission = engine.getSubmission(aData, null, "searchbar");
+          BrowserSearch.recordSearchInHealthReport(engine, "searchbar");
           // null parameter below specifies HTML response for search
-          var submission = this.currentEngine.getSubmission(aData, null, "searchbar");
-          BrowserSearch.recordSearchInHealthReport(this.currentEngine, "searchbar");
           openUILinkIn(submission.uri.spec, aWhere, null, submission.postData);
         ]]></body>
       </method>
     </implementation>
 
     <handlers>
       <handler event="command"><![CDATA[
         const target = event.originalTarget;
@@ -531,20 +551,114 @@
                modifiers="accel"
                action="this.selectEngine(event, (event.detail > 0));"/>
 
       <handler event="focus">
       <![CDATA[
         // Speculatively connect to the current engine's search URI (and
         // suggest URI, if different) to reduce request latency
         this.currentEngine.speculativeConnect({window: window});
+
+        if (this._textbox.value && !this._textbox.open) {
+          this._textbox.showHistoryPopup();
+          // showHistoryPopup does a startSearch("") call, ensure the
+          // controller handles the text from the input box instead:
+          this._textbox.mController.handleText();
+        }
       ]]></handler>
     </handlers>
   </binding>
 
+  <binding id="searchbar-flare" extends="chrome://browser/content/search/search.xml#searchbar">
+    <resources>
+      <stylesheet src="chrome://browser/content/search/searchbarBindings.css"/>
+      <stylesheet src="chrome://browser/skin/searchbar.css"/>
+    </resources>
+    <content>
+      <xul:stringbundle src="chrome://browser/locale/search.properties"
+                        anonid="searchbar-stringbundle"/>
+      <!--
+      There is a dependency between "maxrows" attribute and
+      "SuggestAutoComplete._historyLimit" (nsSearchSuggestions.js). Changing
+      one of them requires changing the other one.
+      -->
+      <xul:textbox class="searchbar-textbox"
+                   anonid="searchbar-textbox"
+                   type="autocomplete"
+                   flex="1"
+                   autocompletepopup="PopupSearchAutoComplete"
+                   autocompletesearch="search-autocomplete"
+                   autocompletesearchparam="searchbar-history"
+                   maxrows="10"
+                   completeselectedindex="true"
+                   tabscrolling="true"
+                   xbl:inherits="disabled,disableautocomplete,searchengine,src,newlines">
+        <!--
+        Empty <box> to properly position the icon within the autocomplete
+        binding's anonymous children (the autocomplete binding positions <box>
+        children differently)
+        -->
+        <xul:box>
+          <xul:hbox class="searchbar-search-button-container">
+            <xul:image class="searchbar-search-button"
+                       anonid="searchbar-search-button"
+                       tooltiptext="&searchEndCap.label;"/>
+          </xul:hbox>
+          <xul:button class="searchbar-engine-button"
+                      type="menu"
+                      anonid="searchbar-engine-button">
+            <xul:image class="searchbar-engine-image" xbl:inherits="src"/>
+            <xul:image class="searchbar-dropmarker-image"/>
+            <xul:menupopup class="searchbar-popup"
+                           anonid="searchbar-popup">
+              <xul:menuseparator/>
+              <xul:menuitem class="open-engine-manager"
+                            anonid="open-engine-manager"
+                            label="&cmd_engineManager.label;"
+                            oncommand="openManager(event);"/>
+            </xul:menupopup>
+          </xul:button>
+        </xul:box>
+        <xul:hbox class="search-go-container">
+          <xul:image class="search-go-button" hidden="true"
+                     anonid="search-go-button"
+                     onclick="handleSearchCommand(event);"
+                     tooltiptext="&searchEndCap.label;"/>
+        </xul:hbox>
+      </xul:textbox>
+    </content>
+    <implementation>
+      <destructor><![CDATA[
+        // For some reason the destructor of the base binding is called
+        // automatically when the window is closed, but now when the node
+        // is removed from the DOM.
+        this.destroy();
+      ]]></destructor>
+
+      <method name="selectEngine">
+        <body><![CDATA[
+          // Override this method to avoid accidentally changing the default
+          // engine using the keyboard shortcuts of the old UI.
+        ]]></body>
+      </method>
+
+      <method name="updateGoButtonVisibility">
+        <body><![CDATA[
+          document.getAnonymousElementByAttribute(this, "anonid",
+                                                  "search-go-button")
+                  .hidden = !this._textbox.value;
+        ]]></body>
+      </method>
+    </implementation>
+    <handlers>
+      <handler event="input" action="this.updateGoButtonVisibility();"/>
+      <handler event="drop" action="this.updateGoButtonVisibility();"/>
+    </handlers>
+  </binding>
+
   <binding id="searchbar-textbox"
       extends="chrome://global/content/bindings/autocomplete.xml#autocomplete">
     <implementation implements="nsIObserver">
       <constructor><![CDATA[
         const kXULNS =
           "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";
 
         if (document.getBindingParent(this).parentNode.parentNode.localName ==
@@ -730,21 +844,155 @@
         </body>
       </method>
 
       <!-- override |onTextEntered| in autocomplete.xml -->
       <method name="onTextEntered">
         <parameter name="aEvent"/>
         <body><![CDATA[
           var evt = aEvent || this.mEnterEvent;
-          document.getBindingParent(this).handleSearchCommand(evt);
+
+          let engine;
+          let oneOff = this.getSelectedOneOff();
+          if (oneOff)
+            engine = oneOff.engine;
+          document.getBindingParent(this).handleSearchCommand(evt, engine);
+
           this.mEnterEvent = null;
         ]]></body>
       </method>
 
+      <method name="getSelectedOneOff">
+        <body><![CDATA[
+          let list = document.getAnonymousElementByAttribute(this.popup, "anonid",
+                                                             "search-panel-one-offs");
+          if (!list)
+            return null;
+
+          for (let button = list.firstChild; button; button = button.nextSibling) {
+            if (button.hasAttribute("selected"))
+              return button;
+          }
+
+          return null;
+        ]]></body>
+      </method>
+
+
+      <method name="handleKeyboardNavigation">
+        <parameter name="aEvent"/>
+        <body><![CDATA[
+          // XXXFlorian This method could likely be shortened with a helper
+          // handling moving the selection within the one-off list and
+          // returning a boolean indicating if the event should be stopped.
+
+          let popup = this.popup;
+          if (!popup.popupOpen)
+            return;
+
+          let list = document.getAnonymousElementByAttribute(popup, "anonid",
+                                                             "search-panel-one-offs");
+          if (!list)
+            return;
+
+          let selectedButton = this.getSelectedOneOff();
+
+          // If the last suggestion is selected, DOWN selects the first one-off.
+          if (aEvent.keyCode == KeyEvent.DOM_VK_DOWN &&
+              popup.selectedIndex + 1 == popup.view.rowCount) {
+            if (selectedButton)
+              selectedButton.removeAttribute("selected");
+            selectedButton = list.firstChild;
+            if (selectedButton)
+              selectedButton.setAttribute("selected", "true");
+          }
+
+          // If no suggestion is selected and a one-off is selected,
+          // UP and DOWN cycle through one-off buttons.
+          if (popup.selectedIndex == -1 && selectedButton &&
+              (aEvent.keyCode == KeyEvent.DOM_VK_DOWN ||
+               aEvent.keyCode == KeyEvent.DOM_VK_UP)) {
+            selectedButton.removeAttribute("selected");
+            if (aEvent.keyCode == KeyEvent.DOM_VK_DOWN)
+              selectedButton = selectedButton.nextSibling;
+            else
+              selectedButton = selectedButton.previousSibling;
+            if (selectedButton && selectedButton.classList.contains("dummy"))
+              selectedButton = null;
+
+            if (selectedButton) {
+              selectedButton.setAttribute("selected", "true");
+
+              aEvent.preventDefault();
+              aEvent.stopPropagation();
+            }
+            else {
+              // Set the selectedIndex to something that will make
+              // handleKeyNavigation (called by autocomplete.xml's onKeyPress
+              // method) reset the text field value to what the user typed.
+              if (aEvent.keyCode == KeyEvent.DOM_VK_UP)
+                popup.selectedIndex = popup.view.rowCount;
+              else
+                popup.selectedIndex = popup.view.rowCount - 1;
+            }
+          }
+
+          // If nothing is selected, UP selects the last one-off button.
+          if (aEvent.keyCode == KeyEvent.DOM_VK_UP &&
+              popup.selectedIndex == -1 && !selectedButton) {
+            selectedButton = list.lastChild;
+            while (selectedButton.classList.contains("dummy"))
+              selectedButton = selectedButton.previousSibling;
+            selectedButton.setAttribute("selected", "true");
+
+            aEvent.preventDefault();
+            aEvent.stopPropagation();
+          }
+
+          if (aEvent.keyCode == KeyEvent.DOM_VK_TAB) {
+            if (selectedButton) {
+              // TAB cycles though the list of one-off buttons.
+              selectedButton.removeAttribute("selected");
+              if (aEvent.shiftKey)
+                selectedButton = selectedButton.previousSibling;
+              else
+                selectedButton = selectedButton.nextSibling;
+
+              // Avoid selecting dummy buttons.
+              if (selectedButton && selectedButton.classList.contains("dummy"))
+                selectedButton = null;
+
+              // If we are out of the list, revert the text field to what the user typed.
+              if (!selectedButton) {
+                // Set the selectedIndex to something that will make
+                // handleKeyNavigation (called by autocomplete.xml's onKeyPress
+                // method) reset the text field value to what the user typed.
+                popup.selectedIndex = aEvent.shiftKey ? 0 : popup.view.rowCount - 1;
+                return;
+              }
+            }
+            else {
+              // If no selection, select the first or last one-off button.
+              if (aEvent.shiftKey) {
+                selectedButton = list.lastChild;
+                while (selectedButton.classList.contains("dummy"))
+                  selectedButton = selectedButton.previousSibling;
+              }
+              else {
+                selectedButton = list.firstChild;
+              }
+            }
+            selectedButton.setAttribute("selected", "true");
+
+            aEvent.preventDefault();
+            aEvent.stopPropagation();
+          }
+        ]]></body>
+      </method>
+
       <!-- nsIController -->
       <field name="searchbarController" readonly="true"><![CDATA[({
         _self: this,
         supportsCommand: function(aCommand) {
           return aCommand == "cmd_clearhistory" ||
                  aCommand == "cmd_togglesuggest";
         },
 
@@ -771,16 +1019,19 @@
             default:
               // do nothing with unrecognized command
           }
         }
       })]]></field>
     </implementation>
 
     <handlers>
+      <handler event="keypress" phase="capturing"
+               action="return this.handleKeyboardNavigation(event);"/>
+
       <handler event="keypress" keycode="VK_UP" modifiers="accel"
                phase="capturing"
                action="document.getBindingParent(this).selectEngine(event, false);"/>
 
       <handler event="keypress" keycode="VK_DOWN" modifiers="accel"
                phase="capturing"
                action="document.getBindingParent(this).selectEngine(event, true);"/>
 
--- a/browser/components/search/test/browser_405664.js
+++ b/browser/components/search/test/browser_405664.js
@@ -1,12 +1,15 @@
 function test() {
   var searchBar = BrowserSearch.searchBar;
   ok(searchBar, "got search bar");
-  
+
+  if (searchBar.getAttribute("oneoffui"))
+    return; // The oneoffui removes the menu that's being tested here.
+
   searchBar.focus();
 
   var pbo = searchBar._popup.popupBoxObject;
   ok(pbo, "popup is nsIPopupBoxObject");
 
   EventUtils.synthesizeKey("VK_UP", { altKey: true });
   is(pbo.popupState, "showing", "popup is opening after Alt+Up");
 
--- a/browser/components/search/test/browser_426329.js
+++ b/browser/components/search/test/browser_426329.js
@@ -223,56 +223,55 @@ add_task(function testShiftMiddleClick()
         "Shift+MiddleClick loaded results in new tab");
   isnot(event.originalTarget, gBrowser.contentDocument,
         "Shift+MiddleClick loaded results in background tab");
   is(event.originalTarget.URL, expectedURL(searchBar.value), "testShiftMiddleClick opened correct search page");
 });
 
 add_task(function testDropText() {
   yield prepareTest();
-  let promisePreventPopup = promiseEvent(searchBar, "popupshowing", true);
   // drop on the search button so that we don't need to worry about the
   // default handlers for textboxes.
-  ChromeUtils.synthesizeDrop(searchBar.searchButton, searchBar.searchButton, [[ {type: "text/plain", data: "Some Text" } ]], "copy", window);
-  yield promisePreventPopup;
+  let searchButton = document.getAnonymousElementByAttribute(searchBar, "anonid", "searchbar-search-button");
+  ChromeUtils.synthesizeDrop(searchButton, searchButton, [[ {type: "text/plain", data: "Some Text" } ]], "copy", window);
   let event = yield promiseOnLoad();
   is(event.originalTarget.URL, expectedURL(searchBar.value), "testDropText opened correct search page");
   is(searchBar.value, "Some Text", "drop text/plain on searchbar");
 });
 
 add_task(function testDropInternalText() {
   yield prepareTest();
-  let promisePreventPopup = promiseEvent(searchBar, "popupshowing", true);
-  ChromeUtils.synthesizeDrop(searchBar.searchButton, searchBar.searchButton, [[ {type: "text/x-moz-text-internal", data: "More Text" } ]], "copy", window);
-  yield promisePreventPopup;
+  let searchButton = document.getAnonymousElementByAttribute(searchBar, "anonid", "searchbar-search-button");
+  ChromeUtils.synthesizeDrop(searchButton, searchButton, [[ {type: "text/x-moz-text-internal", data: "More Text" } ]], "copy", window);
   let event = yield promiseOnLoad();
   is(event.originalTarget.URL, expectedURL(searchBar.value), "testDropInternalText opened correct search page");
   is(searchBar.value, "More Text", "drop text/x-moz-text-internal on searchbar");
 
   // testDropLink implicitly depended on testDropInternalText, so these two tests
   // were merged so that if testDropInternalText failed it wouldn't cause testDropLink
   // to fail unexplainably.
   yield prepareTest();
-  let promisePreventPopup = promiseEvent(searchBar, "popupshowing", true);
   ChromeUtils.synthesizeDrop(searchBar.searchButton, searchBar.searchButton, [[ {type: "text/uri-list", data: "http://www.mozilla.org" } ]], "copy", window);
-  yield promisePreventPopup;
   is(searchBar.value, "More Text", "drop text/uri-list on searchbar shouldn't change anything");
 });
 
 add_task(function testRightClick() {
   preTabNo = gBrowser.tabs.length;
   content.location.href = "about:blank";
   simulateClick({ button: 2 }, searchButton);
   let deferred = Promise.defer();
   setTimeout(function() {
     is(gBrowser.tabs.length, preTabNo, "RightClick did not open new tab");
     is(gBrowser.currentURI.spec, "about:blank", "RightClick did nothing");
     deferred.resolve();
   }, 5000);
   yield deferred.promise;
+  // The click in the searchbox focuses it, which opens the suggestion
+  // panel. Clean up after ourselves.
+  searchBar.textbox.popup.hidePopup();
 });
 
 add_task(function testSearchHistory() {
   var textbox = searchBar._textbox;
   for (var i = 0; i < searchEntries.length; i++) {
     let count = yield countEntries(textbox.getAttribute("autocompletesearchparam"), searchEntries[i]);
     ok(count > 0, "form history entry '" + searchEntries[i] + "' should exist");
   }
@@ -297,9 +296,8 @@ add_task(function testClearHistory() {
 add_task(function asyncCleanup() {
   searchBar.value = "";
   while (gBrowser.tabs.length != 1) {
     gBrowser.removeTab(gBrowser.tabs[0], {animate: false});
   }
   content.location.href = "about:blank";
   yield promiseRemoveEngine();
 });
-
--- a/browser/locales/en-US/firefox-l10n.js
+++ b/browser/locales/en-US/firefox-l10n.js
@@ -1,7 +1,8 @@
 # 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/.
 
 #filter substitution
 
 pref("general.useragent.locale", "@AB_CD@");
+pref("browser.search.showOneOffButtons", true);
--- a/browser/modules/test/browser_UITour.js
+++ b/browser/modules/test/browser_UITour.js
@@ -195,18 +195,23 @@ let tests = [
     gContentAPI.showHighlight("urlbar");
     waitForElementToBeVisible(highlight, checkDefaultEffect, "Highlight should be shown after showHighlight()");
   },
   function test_highlight_search_engine(done) {
     let highlight = document.getElementById("UITourHighlight");
     gContentAPI.showHighlight("urlbar");
     waitForElementToBeVisible(highlight, () => {
 
+      let searchbar = document.getElementById("searchbar");
+      if (searchbar.getAttribute("oneoffui")) {
+        done();
+        return; // The oneoffui removes the menu that's being tested here.
+      }
+
       gContentAPI.showMenu("searchEngines", function() {
-        let searchbar = document.getElementById("searchbar");
         isnot(searchbar, null, "Should have found searchbar");
         let searchPopup = document.getAnonymousElementByAttribute(searchbar,
                                                                    "anonid",
                                                                    "searchbar-popup");
         isnot(searchPopup, null, "Should have found search popup");
 
         function getEngineNode(identifier) {
           let engineNode = null;
--- a/browser/themes/linux/jar.mn
+++ b/browser/themes/linux/jar.mn
@@ -55,16 +55,17 @@ browser.jar:
   skin/classic/browser/pluginInstall-16.png
   skin/classic/browser/pluginInstall-64.png
   skin/classic/browser/pointerLock-16.png
   skin/classic/browser/pointerLock-64.png
   skin/classic/browser/Privacy-16.png
   skin/classic/browser/privatebrowsing-mask.png
   skin/classic/browser/reload-stop-go.png
   skin/classic/browser/searchbar.css
+  skin/classic/browser/search-pref.png              (../shared/search-pref.png)
   skin/classic/browser/Secure.png
   skin/classic/browser/Security-broken.png
   skin/classic/browser/setDesktopBackground.css
   skin/classic/browser/slowStartup-16.png
   skin/classic/browser/theme-switcher-icon.png
   skin/classic/browser/Toolbar.png
   skin/classic/browser/Toolbar-inverted.png
   skin/classic/browser/Toolbar-small.png
--- a/browser/themes/linux/preferences/preferences.css
+++ b/browser/themes/linux/preferences/preferences.css
@@ -15,16 +15,24 @@
 radio[pane=paneMain] {
   -moz-image-region: rect(0px, 32px,  32px, 0px)
 }
 
 radio[pane=paneTabs] {
   -moz-image-region: rect(0px, 64px, 32px, 32px)
 }
 
+#BrowserPreferences radio[pane=paneSearch] {
+  list-style-image: url("chrome://browser/skin/search-pref.png");
+}
+
+.checkbox-check {
+  -moz-appearance: checkbox;
+}
+
 radio[pane=paneContent] {
   -moz-image-region: rect(0px, 96px,  32px, 64px)
 }
 
 radio[pane=paneApplications] {
   -moz-image-region: rect(0px, 128px,  32px, 96px)
 }
 
--- a/browser/themes/linux/searchbar.css
+++ b/browser/themes/linux/searchbar.css
@@ -62,8 +62,144 @@
 menuitem[cmd="cmd_clearhistory"] {
   list-style-image: url("moz-icon://stock/gtk-clear?size=menu");
 }
 
 menuitem[cmd="cmd_clearhistory"][disabled] {
   list-style-image: url("moz-icon://stock/gtk-clear?size=menu&state=disabled");
 }
 
+
+
+
+.searchbar-search-button-container {
+  -moz-box-align: center;
+  padding: 2px 3px;
+  -moz-padding-end: 2px;
+}
+
+.searchbar-search-button {
+  list-style-image: url("chrome://global/skin/icons/Search-glass.png");
+  -moz-image-region: rect(0px 16px 16px 0px);
+}
+
+searchbar[oneoffui] .search-go-button {
+  list-style-image: url("chrome://browser/skin/reload-stop-go.png");
+  -moz-image-region: rect(0, 42px, 14px, 28px);
+}
+
+searchbar[oneoffui] .search-go-button:hover {
+  -moz-image-region: rect(14px, 42px, 28px, 28px);
+}
+
+searchbar[oneoffui] .search-go-button:hover:active {
+  -moz-image-region: rect(28px, 42px, 42px, 28px);
+}
+
+searchbar[oneoffui] .search-go-button:-moz-locale-dir(rtl) > .toolbarbutton-icon {
+  transform: scaleX(-1);
+}
+
+
+.search-panel-current-engine {
+  border-top: none !important;
+  border-bottom: 1px solid #ccc;
+}
+
+.search-panel-header {
+  font-size: 10px;
+  font-weight: normal;
+  background-color: rgb(245, 245, 245);
+  border-top: 1px solid #ccc;
+  margin: 0 1px;
+  padding: 3px 5px;
+  color: #666;
+}
+
+.search-panel-current-input > label {
+  margin: 0 0 !important;
+}
+
+.search-panel-input-value {
+  color: black;
+}
+
+.search-panel-one-offs {
+  margin: 0 0 !important;
+  border-top: 1px solid #ccc;
+}
+
+.searchbar-engine-one-off-item {
+  -moz-appearance: none;
+  display: inline-block;
+  border: none;
+  min-width: 48px;
+  height: 32px;
+  margin: 0 0;
+  padding: 0 0;
+  background: none;
+  background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAAWCAYAAAABxvaqAAAABmJLR0QA/wD/AP+gvaeTAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAB3RJTUUH3gofECQNNVW2/AAAABBJREFUGFdjOHPmzH8GehEA/KpKg9YTf4AAAAAASUVORK5CYII=');
+  background-repeat: no-repeat;
+  background-position: right center;
+}
+
+.searchbar-engine-one-off-item:not(.last-row) {
+  box-sizing: padding-box;
+  border-bottom: 1px solid #ccc;
+}
+
+.searchbar-engine-one-off-item.last-of-row {
+  background-image: none;
+}
+
+.searchbar-engine-one-off-item:hover:not(.dummy),
+.searchbar-engine-one-off-item[selected] {
+  background-color: Highlight;
+  background-image: none;
+}
+
+.searchbar-engine-one-off-item > .button-box {
+  border: none;
+  padding: 0 0;
+}
+
+.searchbar-engine-one-off-item > .button-box > .button-text {
+  display: none;
+}
+
+.searchbar-engine-one-off-item > .button-box > .button-icon {
+  display: -moz-box;
+  -moz-margin-end: 0;
+  width: 16px;
+  height: 16px;
+}
+
+searchbar[oneoffui] .searchbar-engine-button {
+  display: none;
+}
+
+.search-panel-tree > .autocomplete-treebody::-moz-tree-cell {
+  -moz-padding-start: 16px;
+  border-top: none !important;
+}
+
+.searchbar-engine-image {
+  -moz-margin-start: -1px;
+}
+
+.search-setting-button {
+  -moz-appearance: none;
+  border-bottom: none;
+  border-left: none;
+  border-right: none;
+  -moz-border-top-colors: none;
+  min-height: 32px;
+}
+
+.search-setting-button:hover {
+  background-color: hsla(210,4%,10%,.07);
+}
+
+.search-setting-button:hover:active {
+  outline: 1px solid hsla(210,4%,10%,.12);
+  background-color: hsla(210,4%,10%,.12);
+  box-shadow: 0 1px 0 hsla(210,4%,10%,.05) inset;
+}
--- a/browser/themes/osx/jar.mn
+++ b/browser/themes/osx/jar.mn
@@ -94,16 +94,17 @@ browser.jar:
   skin/classic/browser/privatebrowsing-mask-short@2x.png
   skin/classic/browser/reload-stop-go.png
   skin/classic/browser/reload-stop-go@2x.png
   skin/classic/browser/searchbar-dropmarker.png
   skin/classic/browser/searchbar-dropmarker@2x.png
   skin/classic/browser/searchbar.css
   skin/classic/browser/Search.png
   skin/classic/browser/Search@2x.png
+  skin/classic/browser/search-pref.png                             (../shared/search-pref.png)
   skin/classic/browser/Secure-Glyph.png
   skin/classic/browser/Secure-Glyph@2x.png
   skin/classic/browser/slowStartup-16.png
   skin/classic/browser/theme-switcher-icon.png
   skin/classic/browser/theme-switcher-icon@2x.png
   skin/classic/browser/Toolbar.png
   skin/classic/browser/Toolbar@2x.png
   skin/classic/browser/Toolbar-inverted.png
--- a/browser/themes/osx/preferences/preferences.css
+++ b/browser/themes/osx/preferences/preferences.css
@@ -27,16 +27,22 @@ radio[pane=paneMain] {
 } 
 
 /* ----- TABS BUTTON ----- */
 
 radio[pane=paneTabs] {
   -moz-image-region: rect(0px, 64px, 32px, 32px);
 }
 
+/* ----- SEARCH BUTTON ----- */
+
+radio[pane=paneSearch] {
+  list-style-image: url("chrome://browser/skin/search-pref.png");
+}
+
 /* ----- CONTENT BUTTON ----- */
 
 radio[pane=paneContent] {
   -moz-image-region: rect(0px, 96px, 32px, 64px);
 }
 
 /* ----- APPLICATIONS BUTTON ----- */
 
--- a/browser/themes/osx/searchbar.css
+++ b/browser/themes/osx/searchbar.css
@@ -37,27 +37,155 @@
   -moz-margin-start: 2px;
 }
 
 .search-go-container {
   -moz-box-align: center;
   -moz-padding-end: 6px;
 }
 
+.searchbar-search-button-container {
+  -moz-box-align: center;
+  -moz-padding-start: 6px;
+  -moz-padding-end: 4px;
+}
+
+.searchbar-search-button,
 .search-go-button {
   list-style-image: url("chrome://browser/skin/Search.png");
 }
 
+searchbar[oneoffui] .search-go-button {
+  list-style-image: url("chrome://browser/skin/reload-stop-go.png");
+  -moz-image-region: rect(0, 42px, 14px, 28px);
+}
+
+searchbar[oneoffui] .search-go-button:hover:active {
+  -moz-image-region: rect(14px, 42px, 28px, 28px);
+}
+
+searchbar[oneoffui] .search-go-button:-moz-locale-dir(rtl) > .toolbarbutton-icon {
+  transform: scaleX(-1);
+}
+
 @media (min-resolution: 2dppx) {
   .searchbar-engine-image {
     list-style-image: url("chrome://mozapps/skin/places/defaultFavicon@2x.png");
   }
 
   .searchbar-dropmarker-image {
     list-style-image: url("chrome://browser/skin/searchbar-dropmarker@2x.png");
     width: 7px;
   }
 
-  .search-go-button {
+  .search-go-button,
+  .searchbar-search-button {
     list-style-image: url("chrome://browser/skin/Search@2x.png");
     width: 14px;
   }
+
+  searchbar[oneoffui] .search-go-button {
+    list-style-image: url("chrome://browser/skin/reload-stop-go@2x.png");
+    -moz-image-region: rect(0, 84px, 28px, 56px);
+    width: 14px;
+  }
+
+  searchbar[oneoffui] .search-go-button:hover:active {
+    list-style-image: url("chrome://browser/skin/reload-stop-go@2x.png");
+    -moz-image-region: rect(28px, 84px, 56px, 56px);
+    width: 14px;
+  }
 }
+
+.search-panel-current-engine {
+  border-top: none !important;
+  border-bottom: 1px solid #ccc;
+  border-radius: 4px 4px 0 0;
+}
+
+.search-panel-header {
+  font-size: 10px;
+  font-weight: normal;
+  background-color: rgb(245, 245, 245);
+  border-top: 1px solid #ccc;
+  margin: 0;
+  padding: 3px 6px;
+  color: #666;
+}
+
+.search-panel-current-input > label {
+  margin: 0 0 !important;
+}
+
+.search-panel-input-value {
+  color: black;
+}
+
+.search-panel-one-offs {
+  border-top: 1px solid #ccc;
+  margin-bottom: 0 !important;
+}
+
+.searchbar-engine-one-off-item {
+  -moz-appearance: none;
+  display: inline-block;
+  min-width: 48px;
+  height: 32px;
+  margin: 0 0;
+  padding: 0 0;
+  background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAAWCAYAAAABxvaqAAAABmJLR0QA/wD/AP+gvaeTAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAB3RJTUUH3gofECQNNVW2/AAAABBJREFUGFdjOHPmzH8GehEA/KpKg9YTf4AAAAAASUVORK5CYII=');
+  background-repeat: no-repeat;
+  background-position: right center;
+}
+
+.searchbar-engine-one-off-item:not(.last-row) {
+  box-sizing: padding-box;
+  border-bottom: 1px solid #ccc;
+}
+
+.searchbar-engine-one-off-item.last-of-row {
+  background-image: none;
+}
+
+.searchbar-engine-one-off-item:hover:not(.dummy),
+.searchbar-engine-one-off-item[selected] {
+  background-color: Highlight;
+  background-image: none;
+}
+
+.searchbar-engine-one-off-item > .button-box > .button-text {
+  display: none;
+}
+
+.searchbar-engine-one-off-item > .button-box > .button-icon {
+  -moz-margin-start: 0;
+  width: 16px;
+  height: 16px;
+}
+
+searchbar[oneoffui] .searchbar-engine-button {
+  display: none;
+}
+
+.search-panel-tree > .autocomplete-treebody::-moz-tree-cell {
+  -moz-padding-start: 22px;
+  border-top: none !important;
+}
+
+#PopupSearchAutoComplete {
+  border-radius: 4px;
+}
+
+.search-setting-button {
+  -moz-appearance: none;
+  border-radius: 0 0 4px 4px;
+  min-height: 32px;
+}
+
+.search-setting-button:hover {
+  background-color: hsla(210,4%,10%,.07);
+}
+
+.search-setting-button:hover:active {
+  outline: 1px solid hsla(210,4%,10%,.12);
+  background-color: hsla(210,4%,10%,.12);
+  box-shadow: 0 1px 0 hsla(210,4%,10%,.05) inset;
+}
index a833dde7c5fa4787e2681219ae198021ed6628e1..e42a3f71aa79195061e0f82a3f6f5932937e5bbd
GIT binary patch
literal 4749
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
btNwog_Hb;Lkmbvc00000NkvXXu0mjf9c4Sg
index e890cd9e6746f9541323f1ddb3be33a8ed164343..eea84f566aae0c17455f3b81bd29f73fd7d593ce
GIT binary patch
literal 11707
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
G4E`TjRR3TA
--- a/browser/themes/shared/incontentprefs/preferences.css
+++ b/browser/themes/shared/incontentprefs/preferences.css
@@ -28,16 +28,20 @@ prefpane {
 .category-icon {
   list-style-image: url("chrome://browser/skin/preferences/in-content/icons.png");
 }
 
 #category-general > .category-icon {
   -moz-image-region: rect(0, 24px, 24px, 0);
 }
 
+#category-search > .category-icon {
+  -moz-image-region: rect(0, 194px, 24px, 168px);
+}
+
 #category-content > .category-icon {
   -moz-image-region: rect(0, 48px, 24px, 24px);
 }
 
 #category-application > .category-icon {
   -moz-image-region: rect(0, 72px, 24px, 48px);
 }
 
@@ -61,16 +65,20 @@ prefpane {
   .category-icon {
     list-style-image: url("chrome://browser/skin/preferences/in-content/icons@2x.png");
   }
 
   #category-general > .category-icon {
     -moz-image-region: rect(0, 48px, 48px, 0);
   }
 
+  #category-search > .category-icon {
+    -moz-image-region: rect(0, 384px, 48px, 336px);
+  }
+
   #category-content > .category-icon {
     -moz-image-region: rect(0, 96px, 48px, 48px);
   }
 
   #category-application > .category-icon {
     -moz-image-region: rect(0, 144px, 48px, 96px);
   }
 
new file mode 100644
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..3c740eeacc7d374296b3923a762f3f998665216f
GIT binary patch
literal 1455
zc$@*R1yK5lP)<h;3K|Lk000e1NJLTq001BW001Be1^@s6b9#F80000PbVXQnQ*UN;
zcVTj606}DLVr3vnZDD6+Qe|Oed2z{QJOBU%QAtEWRCwCFmt9O-RUE+Yy)A{7QpyGa
zyA~*vGD;&0OohNeCdR-p(UC5gh`jK)C!a8m2g07z7^8+Df-g=a#sri_DMn&4jLAq~
zP}%_l8YtzXAN2dX_tyWZr=)_wpx{aVxwq$@d;h=l|2qGYM1ms{i6rzsdCap6kgFgP
zkTg8!0x^Irg4iw!04Y{9H#awQbad1d7Z)qj($X@c+FdU5($bQ4U|?YC;lqcMFr@<k
z>v;l5L0RL_(2%OUyu1!RPKLu_f~IMLVVLLwilUH2v|8<>_V)H4tXAu<ux{-<04{;@
z)`^LUyAp|{#Ow8je;OOtF6fqg!C)vtv8V_?mB*Bp$oa3;-%zq)m`tX{rlzJZz%t(h
zge_5kn$gkG9@zP-o0~?*=e>hZQ@Fgn{JdO4K|#J*C=di(E+=<=V<QK4oqPMO=9e;r
z>FM+H^MACqw%!APKhFZdj`<z=ysWIO7IqW<?8~opf^^~LjhZUWi^`I{E212~AdMeJ
z3!N_anp*vvNj>p<;P%^ZzM)VkY8x9HUnfaYcNQy9_<FUit*r)pO^yEii;l(S>{eC2
zJX2f!ia}8#w`OHtb|)t%`;nmgJ9k><WCi(8bh^bS5F3<ArK-UhL!1>rQdn46>ht-C
z**UF4B+6L<UvI(H&Phu0!E=ASpt)<;%AOev20e?#BJ%U|OL4lM89<Plnwl911PF)I
z9m>ngT^C<@0Vzic1OFv*d6C)Suv-xTm&-+IDo7*-qG^TzXgVAc@c9m5y1<R5k9yJX
z_p=!~97OlQV339jod|%_Zns+?=X282_~eFRg98EOKMr7FVIhML1Jwwp(}{|aI}sNc
zmY0{csH`d#^2<7{PHZw;>7&e#WO8ycTP7=#!|sW-wY8@(XM{{3PAf5hrQzY>DX4Hv
zRps?EpWj>h(Z`=CJRWcGU;rZN?(UX?#VU%5WR)I|hZz|enZoD8sRs*ReXFah;p*z@
zLa2rXa#?|Rc6Kf}KB4w|+-|o>B!a@|jf{<r33_{b<q#{iEiEnY!47!S)6<Xo`uYaY
zCK4{ikO{5*{rz_Z0znD<4nJF2d%UoqnKv4ZCKg39Qfa}Zii+|wC_dE?v}~KrMmQXf
zt*)-FPhkV&@Xji>(%BCqfN1RL=}}3g(mHSn$>Z^el#~<#N`G_#I-H<hM4$vuWoKt2
zi;2KUTb-Sq-vHF!*4EaK_{ct$3u40_3WZkGYPDv2dwUZ;P6I?L1R5818rs>}F=;fK
z$Ag1|-@=hRI5#)9QCV531ow!b7F>s<xCLT^b$XOp=h2WLSBP<uVQ@oY!~wSlQYsdU
zTN@f0-Up>T6xEPP44jf5&dkhw_dJ$h9rMhOKcEGf0hvV&2%hudHEwVZI+|~hNITvX
zxa}sykr|-YPqG9n!Im%#X}AgIx5GhI7Y_k*H^GJG-QC?|Arb|MR#A(^q6dW*4u?~Z
zl;b{e9|Bx*B*b|DL?NI_Sw|3HiTb$Mia}V8f;caLeF%t&eM{i389~%Tc38}2^C1W_
z;kKGQlt`$zcsC;><30o@g^p_s!T{hs=&B#YXU9bVL?NJ%MIiuyLY*|y14WF1s`?HD
z=2$F~*u=#QgI=$HfRqY_!uyB-^&5~e1EBn`xHSIfpG_ii!jR<U<$a)3DruQa_7(ij
zJ+$fP{7@o8l9iRE!ad_5i|@w(E+Q1p3@WZ!A>-5PnDQOLa_}CNa1MwE-$#xDpzy*J
zkR(hS1BxkTod7_`DW|bQ^nW6ROMCEOoA-ZNfQbY8mj(V7U;rMJ*Q!M;954U?002ov
JPDHLkV1gFSr=I`-
--- a/browser/themes/windows/jar.mn
+++ b/browser/themes/windows/jar.mn
@@ -70,16 +70,17 @@ browser.jar:
         skin/classic/browser/Privacy-32.png
         skin/classic/browser/Privacy-48.png
         skin/classic/browser/privatebrowsing-mask-tabstrip-XPVista7.png
         skin/classic/browser/privatebrowsing-mask-titlebar-XPVista7.png
         skin/classic/browser/privatebrowsing-mask-titlebar-XPVista7-tall.png
         skin/classic/browser/reload-stop-go.png
         skin/classic/browser/searchbar.css
         skin/classic/browser/searchbar-dropdown-arrow.png
+        skin/classic/browser/search-pref.png                         (../shared/search-pref.png)
         skin/classic/browser/Secure24.png
         skin/classic/browser/setDesktopBackground.css
         skin/classic/browser/slowStartup-16.png
         skin/classic/browser/theme-switcher-icon.png
         skin/classic/browser/Toolbar.png                             (Toolbar-XP.png)
         skin/classic/browser/Toolbar-inverted.png
         skin/classic/browser/Toolbar-lunaSilver.png
         skin/classic/browser/toolbarbutton-dropdown-arrow.png
@@ -492,16 +493,17 @@ browser.jar:
         skin/classic/aero/browser/privatebrowsing-mask-tabstrip.png
         skin/classic/aero/browser/privatebrowsing-mask-tabstrip-XPVista7.png
         skin/classic/aero/browser/privatebrowsing-mask-titlebar.png
         skin/classic/aero/browser/privatebrowsing-mask-titlebar-XPVista7.png
         skin/classic/aero/browser/privatebrowsing-mask-titlebar-XPVista7-tall.png
         skin/classic/aero/browser/reload-stop-go.png
         skin/classic/aero/browser/searchbar.css
         skin/classic/aero/browser/searchbar-dropdown-arrow.png       (searchbar-dropdown-arrow-aero.png)
+        skin/classic/aero/browser/search-pref.png                    (../shared/search-pref.png)
         skin/classic/aero/browser/Secure24.png                       (Secure24-aero.png)
         skin/classic/aero/browser/setDesktopBackground.css
         skin/classic/aero/browser/slowStartup-16.png
         skin/classic/aero/browser/theme-switcher-icon.png
         skin/classic/aero/browser/theme-switcher-icon-aero.png
         skin/classic/aero/browser/Toolbar.png
         skin/classic/aero/browser/Toolbar-inverted.png
         skin/classic/aero/browser/Toolbar-aero.png
--- a/browser/themes/windows/preferences/preferences.css
+++ b/browser/themes/windows/preferences/preferences.css
@@ -14,16 +14,20 @@
 radio[pane=paneMain] {
   -moz-image-region: rect(0, 32px,  32px, 0);
 }
 
 radio[pane=paneTabs] {
   -moz-image-region: rect(0, 64px, 32px, 32px);
 }
 
+#BrowserPreferences radio[pane=paneSearch] {
+  list-style-image: url("chrome://browser/skin/search-pref.png");
+}
+
 radio[pane=paneContent] {
   -moz-image-region: rect(0, 96px,  32px, 64px);
 }
 
 radio[pane=paneApplications] {
   -moz-image-region: rect(0, 128px,  32px, 96px);
 }
 
--- a/browser/themes/windows/searchbar.css
+++ b/browser/themes/windows/searchbar.css
@@ -69,8 +69,142 @@
 
 .search-go-button:hover:active {
   -moz-image-region: rect(0px, 48px, 16px, 32px);
 }
 
 .searchbar-engine-menuitem[selected="true"] > .menu-iconic-text {
   font-weight: bold;
 }
+
+
+
+.searchbar-search-button-container {
+  -moz-box-align: center;
+  padding: 3px 4px;
+  -moz-padding-end: 2px;
+}
+
+.searchbar-search-button {
+  list-style-image: url("chrome://global/skin/icons/Search-glass.png");
+  -moz-image-region: rect(0px 16px 16px 0px);
+}
+
+searchbar[oneoffui] .search-go-button {
+  list-style-image: url("chrome://browser/skin/reload-stop-go.png");
+  -moz-image-region: rect(0, 42px, 14px, 28px);
+}
+
+searchbar[oneoffui] .search-go-button:hover {
+  -moz-image-region: rect(14px, 42px, 28px, 28px);
+}
+
+searchbar[oneoffui] .search-go-button:hover:active {
+  -moz-image-region: rect(28px, 42px, 42px, 28px);
+}
+
+searchbar[oneoffui] .search-go-button:-moz-locale-dir(rtl) > .toolbarbutton-icon {
+  transform: scaleX(-1);
+}
+
+
+.search-panel-current-engine {
+  border-top: none !important;
+  border-bottom: 1px solid #ccc;
+}
+
+.search-panel-header {
+  font-size: 10px;
+  font-weight: normal;
+  background-color: rgb(245, 245, 245);
+  border-top: 1px solid #ccc;
+  margin: 0;
+  padding: 3px 6px;
+  color: #666;
+}
+
+.search-panel-current-input > label {
+  margin: 0 0 !important;
+}
+
+.search-panel-input-value {
+  color: black;
+}
+
+.search-panel-one-offs {
+  margin: 0 0 !important;
+  border-top: 1px solid #ccc;
+}
+
+.searchbar-engine-one-off-item {
+  -moz-appearance: none;
+  display: inline-block;
+  border: none;
+  min-width: 48px;
+  height: 32px;
+  margin: 0 0;
+  padding: 0 0;
+  background: none;
+  background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAAWCAYAAAABxvaqAAAABmJLR0QA/wD/AP+gvaeTAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAB3RJTUUH3gofECQNNVW2/AAAABBJREFUGFdjOHPmzH8GehEA/KpKg9YTf4AAAAAASUVORK5CYII=');
+  background-repeat: no-repeat;
+  background-position: right center;
+}
+
+.searchbar-engine-one-off-item:not(.last-row) {
+  box-sizing: padding-box;
+  border-bottom: 1px solid #ccc;
+}
+
+.searchbar-engine-one-off-item.last-of-row {
+  background-image: none;
+}
+
+.searchbar-engine-one-off-item:hover:not(.dummy),
+.searchbar-engine-one-off-item[selected] {
+  background-color: Highlight;
+  background-image: none;
+}
+
+.searchbar-engine-one-off-item > .button-box {
+  border: none;
+  padding: 0 0;
+}
+
+.searchbar-engine-one-off-item > .button-box > .button-text {
+  display: none;
+}
+
+.searchbar-engine-one-off-item > .button-box > .button-icon {
+  width: 16px;
+  height: 16px;
+}
+
+searchbar[oneoffui] .searchbar-engine-button {
+  display: none;
+}
+
+.search-panel-tree > .autocomplete-treebody::-moz-tree-cell {
+  -moz-padding-start: 15px;
+  border-top: none !important;
+}
+
+.searchbar-engine-image {
+  -moz-margin-start: -1px;
+}
+
+.search-setting-button {
+  -moz-appearance: none;
+  border-bottom: none;
+  border-left: none;
+  border-right: none;
+  -moz-border-top-colors: none;
+  min-height: 32px;
+}
+
+.search-setting-button:hover {
+  background-color: hsla(210,4%,10%,.07);
+}
+
+.search-setting-button:hover:active {
+  outline: 1px solid hsla(210,4%,10%,.12);
+  background-color: hsla(210,4%,10%,.12);
+  box-shadow: 0 1px 0 hsla(210,4%,10%,.05) inset;
+}
--- a/toolkit/components/search/SearchSuggestionController.jsm
+++ b/toolkit/components/search/SearchSuggestionController.jsm
@@ -45,20 +45,22 @@ this.SearchSuggestionController = functi
   this._callback = callback;
 };
 
 this.SearchSuggestionController.prototype = {
   /**
    * The maximum number of local form history results to return. This limit is
    * only enforced if remote results are also returned.
    */
-  maxLocalResults: 7,
+  maxLocalResults: 5,
 
   /**
    * The maximum number of remote search engine results to return.
+   * We'll actually only display at most
+   * maxRemoteResults - <displayed local results count> remote results.
    */
   maxRemoteResults: 10,
 
   /**
    * The maximum time (ms) to wait before giving up on a remote suggestions.
    */
   remoteTimeout: REMOTE_TIMEOUT,
 
@@ -348,17 +350,18 @@ this.SearchSuggestionController.prototyp
         let dupIndex = results.remote.indexOf(term);
         if (dupIndex != -1) {
           results.remote.splice(dupIndex, 1);
         }
       }
     }
 
     // Trim the number of results to the maximum requested (now that we've pruned dupes).
-    results.remote = results.remote.slice(0, this.maxRemoteResults);
+    results.remote =
+      results.remote.slice(0, this.maxRemoteResults - results.local.length);
 
     if (this._callback) {
       this._callback(results);
     }
     this._reset();
 
     return results;
   },
--- a/toolkit/components/search/nsSearchSuggestions.js
+++ b/toolkit/components/search/nsSearchSuggestions.js
@@ -68,16 +68,22 @@ SuggestAutoComplete.prototype = {
       // "comments" column values for suggestions starts as empty strings
       let comments = new Array(results.remote.length).fill("", 1);
       comments[0] = this._suggestionLabel;
       // now put the history results above the suggestions
       finalResults = finalResults.concat(results.remote);
       finalComments = finalComments.concat(comments);
     }
 
+    // If no result, add the search term so that the panel is shown anyway.
+    if (!finalResults.length) {
+      finalResults.push(results.term);
+      finalComments.push("");
+    }
+
     // Notify the FE of our new results
     this.onResultsReady(results.term, finalResults, finalComments, results.formHistoryResult);
   },
 
   /**
    * Notifies the front end of new results.
    * @param searchString  the user's query string
    * @param results       an array of results to the search
--- a/toolkit/components/search/tests/xpcshell/test_searchSuggest.js
+++ b/toolkit/components/search/tests/xpcshell/test_searchSuggest.js
@@ -256,18 +256,18 @@ add_task(function* both_identical_with_m
   controller.maxLocalResults = 7;
   controller.maxRemoteResults = 10;
   let result = yield controller.fetch("letter ", false, getEngine);
   do_check_eq(result.term, "letter ");
   do_check_eq(result.local.length, 7);
   for (let i = 0; i < controller.maxLocalResults; i++) {
     do_check_eq(result.local[i], "letter " + String.fromCharCode("A".charCodeAt() + i));
   }
-  do_check_eq(result.remote.length, 10);
-  for (let i = 0; i < controller.maxRemoteResults; i++) {
+  do_check_eq(result.local.length + result.remote.length, 10);
+  for (let i = 0; i < result.remote.length; i++) {
     do_check_eq(result.remote[i],
                 "letter " + String.fromCharCode("A".charCodeAt() + controller.maxLocalResults + i));
   }
 });
 
 add_task(function* noremote_maxLocal() {
   let controller = new SearchSuggestionController();
   controller.maxLocalResults = 2; // (should be ignored because no remote results)
@@ -279,34 +279,34 @@ add_task(function* noremote_maxLocal() {
     do_check_eq(result.local[i], "letter " + String.fromCharCode("A".charCodeAt() + i));
   }
   do_check_eq(result.remote.length, 0);
 });
 
 add_task(function* someremote_maxLocal() {
   let controller = new SearchSuggestionController();
   controller.maxLocalResults = 2;
-  controller.maxRemoteResults = 2;
+  controller.maxRemoteResults = 4;
   let result = yield controller.fetch("letter ", false, getEngine);
   do_check_eq(result.term, "letter ");
   do_check_eq(result.local.length, 2);
   for (let i = 0; i < result.local.length; i++) {
     do_check_eq(result.local[i], "letter " + String.fromCharCode("A".charCodeAt() + i));
   }
   do_check_eq(result.remote.length, 2);
   // "A" and "B" will have been de-duped, start at C for remote results
   for (let i = 0; i < result.remote.length; i++) {
     do_check_eq(result.remote[i], "letter " + String.fromCharCode("C".charCodeAt() + i));
   }
 });
 
 add_task(function* one_of_each() {
   let controller = new SearchSuggestionController();
   controller.maxLocalResults = 1;
-  controller.maxRemoteResults = 1;
+  controller.maxRemoteResults = 2;
   let result = yield controller.fetch("letter ", false, getEngine);
   do_check_eq(result.term, "letter ");
   do_check_eq(result.local.length, 1);
   do_check_eq(result.local[0], "letter A");
   do_check_eq(result.remote.length, 1);
   do_check_eq(result.remote[0], "letter B");
 });
 
@@ -339,17 +339,17 @@ add_task(function* local_result_returned
   do_check_eq(result.remote.length, 0);
   Services.prefs.setBoolPref("browser.search.suggest.enabled", true);
 });
 
 add_task(function* one_of_each_disabled_before_creation_enabled_after_creation_of_controller() {
   Services.prefs.setBoolPref("browser.search.suggest.enabled", false);
   let controller = new SearchSuggestionController();
   controller.maxLocalResults = 1;
-  controller.maxRemoteResults = 1;
+  controller.maxRemoteResults = 2;
   Services.prefs.setBoolPref("browser.search.suggest.enabled", true);
   let result = yield controller.fetch("letter ", false, getEngine);
   do_check_eq(result.term, "letter ");
   do_check_eq(result.local.length, 1);
   do_check_eq(result.local[0], "letter A");
   do_check_eq(result.remote.length, 1);
   do_check_eq(result.remote[0], "letter B");
 });
--- a/toolkit/themes/shared/in-content/common.inc.css
+++ b/toolkit/themes/shared/in-content/common.inc.css
@@ -180,16 +180,20 @@ xul|colorpicker[type="button"] {
 }
 
 xul|button > xul|*.button-box,
 xul|menulist > xul|*.menulist-label-box {
   padding-right: 10px !important;
   padding-left: 10px !important;
 }
 
+xul|menulist > xul|*.menulist-label-box > xul|*.menulist-icon {
+  -moz-margin-end: 5px;
+}
+
 xul|button[type="menu"] > xul|*.button-box > xul|*.button-menu-dropmarker {
   -moz-appearance: none;
   margin: 1px 0;
   -moz-margin-start: 10px;
   padding: 0;
   width: 10px;
   height: 16px;
   border: none;
@@ -451,16 +455,20 @@ xul|*.checkbox-label-box {
 @media (min-resolution: 2dppx) {
   xul|*.checkbox-check[checked] {
     background-size: 12px 12px, auto;
     background-image: url("chrome://global/skin/in-content/check@2x.png"),
                       linear-gradient(#fff, rgba(255,255,255,0.8)) !important;
   }
 }
 
+xul|richlistitem > xul|*.checkbox-check {
+  margin: 3px 6px;
+}
+
 xul|*.radio-check {
   -moz-appearance: none;
   width: 23px;
   height: 23px;
   border: 1px solid #c1c1c1;
   border-radius: 50%;
   -moz-margin-end: 10px;
   background-color: #f1f1f1;