Bug 1171344 - [implement] One-off searches on about:home and about:newtab. r=adw
☠☠ backed out by 15155971639c ☠ ☠
authorNihanth Subramanya <nhnt11@.gmail.com>
Mon, 29 Jun 2015 13:52:20 -0700
changeset 284886 e88770aa21600f3afdefd06dc643eb83e8da6b84
parent 284885 ffb2bb96edc32e18deccdca84661d6b98eb668d2
child 284887 7f2ddcfe4537c52265ce168b3ab182892e7dbcc0
push id5067
push userraliiev@mozilla.com
push dateMon, 21 Sep 2015 14:04:52 +0000
treeherdermozilla-beta@14221ffe5b2f [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersadw
bugs1171344
milestone42.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1171344 - [implement] One-off searches on about:home and about:newtab. r=adw
CLOBBER
browser/base/content/abouthome/aboutHome.css
browser/base/content/abouthome/aboutHome.js
browser/base/content/abouthome/aboutHome.xhtml
browser/base/content/browser.js
browser/base/content/contentSearchUI.css
browser/base/content/contentSearchUI.js
browser/base/content/newtab/newTab.css
browser/base/content/newtab/newTab.xul
browser/base/content/newtab/search.js
browser/base/content/searchSuggestionUI.css
browser/base/content/searchSuggestionUI.js
browser/base/content/tab-content.js
browser/base/jar.mn
browser/locales/en-US/chrome/browser/aboutHome.dtd
browser/locales/en-US/chrome/browser/browser.dtd
browser/locales/en-US/chrome/browser/search.properties
browser/modules/AboutHome.jsm
browser/modules/ContentSearch.jsm
browser/themes/linux/jar.mn
browser/themes/osx/jar.mn
browser/themes/shared/search/search-arrow-go.svg
browser/themes/shared/search/search-indicator-magnifying-glass.svg
browser/themes/windows/jar.mn
--- a/CLOBBER
+++ b/CLOBBER
@@ -17,9 +17,9 @@
 #
 # Modifying this file will now automatically clobber the buildbot machines \o/
 #
 
 # Are you updating CLOBBER because you think it's needed for your WebIDL
 # changes to stick? As of bug 928195, this shouldn't be necessary! Please
 # don't change CLOBBER for WebIDL changes any more.
 
-Bug 1178850 requires clobber for Android JNI header changes
+Bug 1171344 requires a clobber due to renaming of a test file.
--- a/browser/base/content/abouthome/aboutHome.css
+++ b/browser/base/content/abouthome/aboutHome.css
@@ -44,130 +44,112 @@ a {
   width: 192px;
   margin: 22px auto 31px;
   background-image: url("chrome://branding/content/about-logo.png");
   background-size: 192px auto;
   background-position: center center;
   background-repeat: no-repeat;
 }
 
-#searchForm,
+#searchIconAndTextContainer,
 #snippets {
   width: 470px;
 }
 
-#searchForm {
-  display: -moz-box;
-}
-
-#searchLogoContainer {
+#searchIconAndTextContainer {
   display: -moz-box;
-  -moz-box-align: center;
-  padding-top: 2px;
-  -moz-padding-end: 8px;
-}
-
-#searchLogoContainer[hidden] {
-  display: none;
-}
-
-#searchEngineLogo {
-  display: inline-block;
-  height: 28px;
-  width: 70px;
-  min-width: 70px;
+  height: 36px;
+  position: relative;
 }
 
 #searchIcon {
-  border: 1px solid transparent;
-  -moz-margin-end: 5px;
-  height: 38px;
-  width: 38px;
-  background: url("chrome://browser/skin/magnifier.png") center center no-repeat;
-  background-size: 26px;
-}
-
-#searchIcon[active],
-#searchIcon:hover {
-  background-color: #e9e9e9;
-  border-color: rgb(226, 227, 229);
-  border-radius: 2.5px;
-}
-
-html[searchUIConfiguration="oldsearchui"] #searchIcon {
-  display: none;
-}
-
-html:not([searchUIConfiguration="oldsearchui"]) #searchText::-moz-placeholder {
-  color: transparent;
-}
-
-html:not([searchUIConfiguration="oldsearchui"]) #searchLogoContainer {
-  display: none;
+  border: 1px transparent;
+  padding: 0;
+  margin: 0;
+  width: 36px;
+  height: 36px;
+  background: url("chrome://browser/skin/search-indicator-magnifying-glass.svg") center center no-repeat;
+  position: absolute;
 }
 
 #searchText {
+  margin-left: 0;
   -moz-box-flex: 1;
-  padding: 6px 8px;
+  padding-top: 6px;
+  padding-bottom: 6px;
+  padding-left: 34px;
+  padding-right: 8px;
   background: hsla(0,0%,100%,.9) padding-box;
   border: 1px solid;
+  border-radius: 2px 0 0 2px;
   border-color: hsla(210,54%,20%,.15) hsla(210,54%,20%,.17) hsla(210,54%,20%,.2);
   box-shadow: 0 1px 0 hsla(210,65%,9%,.02) inset,
               0 0 2px hsla(210,65%,9%,.1) inset,
               0 1px 0 hsla(0,0%,100%,.2);
-  border-radius: 2.5px 0 0 2.5px;
   color: inherit;
 }
 
 #searchText:-moz-dir(rtl) {
-  border-radius: 0 2.5px 2.5px 0;
+  border-radius: 0 2px 2px 0;
 }
 
+#searchText[aria-expanded="true"] {
+  border-radius: 2px 0 0 0;
+}
+
+#searchText[aria-expanded="true"]:-moz-dir(rtl) {
+  border-radius: 0 2px 0 0;
+}
+
+#searchText[keepfocus],
 #searchText:focus,
 #searchText[autofocus] {
   border-color: hsla(206,100%,60%,.6) hsla(206,76%,52%,.6) hsla(204,100%,40%,.6);
 }
 
 #searchSubmit {
   -moz-margin-start: -1px;
-  background: linear-gradient(hsla(0,0%,100%,.8), hsla(0,0%,100%,.1)) padding-box;
-  padding: 0 9px;
+  background: url("chrome://browser/skin/search-arrow-go.svg#search-arrow-go") center center no-repeat, linear-gradient(hsla(0,0%,100%,.8), hsla(0,0%,100%,.1)) padding-box;
+  padding: 0;
   border: 1px solid;
   border-color: hsla(210,54%,20%,.15) hsla(210,54%,20%,.17) hsla(210,54%,20%,.2);
+  border-radius: 0 2px 2px 0;
   -moz-border-start: 1px solid transparent;
-  border-radius: 0 2.5px 2.5px 0;
   box-shadow: 0 0 2px hsla(0,0%,100%,.5) inset,
               0 1px 0 hsla(0,0%,100%,.2);
   color: inherit;
   cursor: pointer;
   transition-property: background-color, border-color, box-shadow;
   transition-duration: 150ms;
+  width: 50px;
 }
 
 #searchSubmit:-moz-dir(rtl) {
-  border-radius: 2.5px 0 0 2.5px;
+  border-radius: 2px 0 0 2px;
 }
 
 #searchText:focus + #searchSubmit,
+#searchText[keepfocus] + #searchSubmit,
 #searchText + #searchSubmit:hover,
 #searchText[autofocus] + #searchSubmit {
   border-color: #59b5fc #45a3e7 #3294d5;
   color: white;
 }
 
 #searchText:focus + #searchSubmit,
+#searchText[keepfocus] + #searchSubmit,
 #searchText[autofocus] + #searchSubmit {
-  background-image: linear-gradient(#4cb1ff, #1793e5);
+  background: url("chrome://browser/skin/search-arrow-go.svg#search-arrow-go-inverted") center center no-repeat, linear-gradient(#4cb1ff, #1793e5);
   box-shadow: 0 1px 0 hsla(0,0%,100%,.2) inset,
               0 0 0 1px hsla(0,0%,100%,.1) inset,
               0 1px 0 hsla(210,54%,20%,.03);
 }
 
 #searchText + #searchSubmit:hover {
-  background-image: linear-gradient(#66bdff, #0d9eff);
+  background: url("chrome://browser/skin/search-arrow-go.svg#search-arrow-go-inverted") center center no-repeat, linear-gradient(#66bdff, #0d9eff);
   box-shadow: 0 1px 0 hsla(0,0%,100%,.2) inset,
               0 0 0 1px hsla(0,0%,100%,.1) inset,
               0 1px 0 hsla(210,54%,20%,.03),
               0 0 4px hsla(206,100%,20%,.2);
 }
 
 #searchText + #searchSubmit:hover:active {
   box-shadow: 0 1px 1px hsla(211,79%,6%,.1) inset,
@@ -175,29 +157,29 @@ html:not([searchUIConfiguration="oldsear
   transition-duration: 0ms;
 }
 
 #defaultSnippet1,
 #defaultSnippet2,
 #rightsSnippet {
   display: block;
   min-height: 38px;
-  background: 30px center no-repeat;
+  background: 0 center no-repeat;
   padding: 6px 0;
-  -moz-padding-start: 79px;
+  -moz-padding-start: 49px;
 }
 
 #rightsSnippet[hidden] {
   display: none;
 }
 
 #defaultSnippet1:-moz-dir(rtl),
 #defaultSnippet2:-moz-dir(rtl),
 #rightsSnippet:-moz-dir(rtl) {
-  background-position: right 30px center;
+  background-position: right 0 center;
 }
 
 #defaultSnippet1 {
   background-image: url("chrome://browser/content/abouthome/snippet1.png");
 }
 
 #defaultSnippet2 {
   background-image: url("chrome://browser/content/abouthome/snippet2.png");
@@ -242,17 +224,17 @@ body[narrow] #launcher[session] {
   padding: 14px 6px;
   min-width: 88px;
   max-width: 176px;
   max-height: 85px;
   vertical-align: top;
   white-space: normal;
   background: transparent padding-box;
   border: 1px solid transparent;
-  border-radius: 2.5px;
+  border-radius: 2px;
   color: #525c66;
   font-size: 75%;
   cursor: pointer;
   transition-property: background-color, border-color, box-shadow;
   transition-duration: 150ms;
 }
 
 body[narrow] #launcher[session] > .launchButton {
@@ -391,20 +373,16 @@ body[narrow] #restorePreviousSession::be
  * At resolutions above 1dppx, prefer downscaling the 2x Retina graphics
  * rather than upscaling the original-size ones (bug 818940).
  */
 @media not all and (max-resolution: 1dppx) {
   #brandLogo {
     background-image: url("chrome://branding/content/about-logo@2x.png");
   }
 
-  #searchIcon {
-    background-image: url("chrome://browser/skin/magnifier@2x.png");
-  }
-
   #defaultSnippet1,
   #defaultSnippet2,
   #rightsSnippet {
     background-size: 40px;
   }
 
   #defaultSnippet1 {
     background-image: url("chrome://browser/content/abouthome/snippet1@2x.png");
--- a/browser/base/content/abouthome/aboutHome.js
+++ b/browser/base/content/abouthome/aboutHome.js
@@ -1,146 +1,14 @@
 /* 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";
 
-const SEARCH_ENGINES = {
-  "Google": {
-    // This is the "2x" image designed for OS X retina resolution, Windows at 192dpi, etc.;
-    // it will be scaled down as necessary on lower-dpi displays.
-    // This needs to be defined in a single line to keep the JS parser from creating many
-    // intermediate strings in memory.  See bug 986672.
-    image: "data:image/png;base64,\
-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"
-  }
-};
-
 // The process of adding a new default snippet involves:
 //   * add a new entity to aboutHome.dtd
 //   * add a <span/> for it in aboutHome.xhtml
 //   * add an entry here in the proper ordering (based on spans)
 // The <a/> part of the snippet will be linked to the corresponding url.
 const DEFAULT_SNIPPETS_URLS = [
   "https://www.mozilla.org/firefox/features/?utm_source=snippet&utm_medium=snippet&utm_campaign=default+feature+snippet"
 , "https://addons.mozilla.org/firefox/?utm_source=snippet&utm_medium=snippet&utm_campaign=addons"
@@ -153,32 +21,32 @@ const DATABASE_NAME = "abouthome";
 const DATABASE_VERSION = 1;
 const DATABASE_STORAGE = "persistent";
 const SNIPPETS_OBJECTSTORE_NAME = "snippets";
 
 // This global tracks if the page has been set up before, to prevent double inits
 let gInitialized = false;
 let gObserver = new MutationObserver(function (mutations) {
   for (let mutation of mutations) {
-    if (mutation.attributeName == "searchEngineName") {
-      setupSearchEngine();
+    if (mutation.attributeName == "snippetsVersion") {
       if (!gInitialized) {
         ensureSnippetsMapThen(loadSnippets);
         gInitialized = true;
       }
       return;
     }
   }
 });
 
 window.addEventListener("pageshow", function () {
   // Delay search engine setup, cause browser.js::BrowserOnAboutPageLoad runs
   // later and may use asynchronous getters.
   window.gObserver.observe(document.documentElement, { attributes: true });
   fitToWidth();
+  setupSearch();
   window.addEventListener("resize", fitToWidth);
 
   // Ask chrome to update snippets.
   var event = new CustomEvent("AboutHomeLoad", {bubbles:true});
   document.dispatchEvent(event);
 });
 
 window.addEventListener("pagehide", function() {
@@ -295,94 +163,38 @@ function ensureSnippetsMapThen(aCallback
 
       setTimeout(invokeCallbacks, 0);
     }
   }
 }
 
 function onSearchSubmit(aEvent)
 {
-  let searchText = document.getElementById("searchText");
-  let searchTerms = searchText.value;
-  let engineName = document.documentElement.getAttribute("searchEngineName");
-
-  if (engineName && searchTerms.length > 0) {
-    // Send an event that will perform a search and Firefox Health Report will
-    // record that a search from about:home has occurred.
-    let eventData = {
-      engineName: engineName,
-      searchTerms: searchTerms,
-      originalEvent: {
-        target: {
-          ownerDocument: null
-        },
-        shiftKey: aEvent.shiftKey,
-        ctrlKey: aEvent.ctrlKey,
-        metaKey: aEvent.metaKey,
-        altKey: aEvent.altKey,
-        button: aEvent.button,
-      },
-    };
-
-    if (searchText.hasAttribute("selection-index")) {
-      eventData.selection = {
-        index: searchText.getAttribute("selection-index"),
-        kind: searchText.getAttribute("selection-kind")
-      };
-    }
-
-    eventData = JSON.stringify(eventData);
-
-    let event = new CustomEvent("AboutHomeSearchEvent", {detail: eventData});
-    document.dispatchEvent(event);
-  }
-
-  gSearchSuggestionController.addInputValueToFormHistory();
-
-  if (aEvent) {
-    aEvent.preventDefault();
-  }
+  gContentSearchController.search(aEvent);
 }
 
 
-let gSearchSuggestionController;
+let gContentSearchController;
 
-function setupSearchEngine()
+function setupSearch()
 {
   // The "autofocus" attribute doesn't focus the form element
   // immediately when the element is first drawn, so the
   // attribute is also used for styling when the page first loads.
   let searchText = document.getElementById("searchText");
   searchText.addEventListener("blur", function searchText_onBlur() {
     searchText.removeEventListener("blur", searchText_onBlur);
     searchText.removeAttribute("autofocus");
   });
- 
-  let searchEngineName = document.documentElement.getAttribute("searchEngineName");
-  let searchEngineInfo = SEARCH_ENGINES[searchEngineName];
-  let logoElt = document.getElementById("searchEngineLogo");
 
-  // Add search engine logo.
-  if (searchEngineInfo && searchEngineInfo.image) {
-    logoElt.parentNode.hidden = false;
-    logoElt.src = searchEngineInfo.image;
-    logoElt.alt = searchEngineName;
-    searchText.placeholder = "";
+  if (!gContentSearchController) {
+    gContentSearchController =
+      new ContentSearchUIController(searchText, searchText.parentNode,
+                                    "abouthome", "homepage");
   }
-  else {
-    logoElt.parentNode.hidden = true;
-    searchText.placeholder = searchEngineName;
-  }
-
-  if (!gSearchSuggestionController) {
-    gSearchSuggestionController =
-      new SearchSuggestionUIController(searchText, searchText.parentNode,
-                                       onSearchSubmit);
-  }
-  gSearchSuggestionController.engineName = searchEngineName;
 }
 
 /**
  * Inform the test harness that we're done loading the page.
  */
 function loadCompleted()
 {
   var event = new CustomEvent("AboutHomeLoadSnippetsCompleted", {bubbles:true});
--- a/browser/base/content/abouthome/aboutHome.xhtml
+++ b/browser/base/content/abouthome/aboutHome.xhtml
@@ -19,39 +19,37 @@
 
 <html xmlns="http://www.w3.org/1999/xhtml">
   <head>
     <title>&abouthome.pageTitle;</title>
 
     <link rel="icon" type="image/png" id="favicon"
           href="chrome://branding/content/icon32.png"/>
     <link rel="stylesheet" type="text/css" media="all"
-          href="chrome://browser/content/searchSuggestionUI.css"/>
+          href="chrome://browser/content/contentSearchUI.css"/>
     <link rel="stylesheet" type="text/css" media="all" defer="defer"
           href="chrome://browser/content/abouthome/aboutHome.css"/>
 
     <script type="text/javascript;version=1.8"
             src="chrome://browser/content/abouthome/aboutHome.js"/>
     <script type="text/javascript;version=1.8"
-            src="chrome://browser/content/searchSuggestionUI.js"/>
+            src="chrome://browser/content/contentSearchUI.js"/>
   </head>
 
   <body dir="&locale.dir;">
     <div class="spacer"/>
     <div id="topSection">
       <div id="brandLogo"></div>
 
-      <div id="searchContainer">
-        <form name="searchForm" id="searchForm" onsubmit="onSearchSubmit(event)">
-          <div id="searchLogoContainer" hidden="true"><img id="searchEngineLogo"/></div>
-          <button id="searchIcon" type="button" />
-          <input type="text" name="q" value="" id="searchText" maxlength="256"
-                 autofocus="autofocus" dir="auto"/>
-          <input id="searchSubmit" type="submit" value="&abouthome.searchEngineButton.label;"/>
-        </form>
+      <div id="searchIconAndTextContainer">
+        <div id="searchIcon"/>
+        <input type="text" name="q" value="" id="searchText" maxlength="256"
+               aria-label="&contentSearchInput.label;" autofocus="autofocus" dir="auto"/>
+        <input id="searchSubmit" type="button" value="" onclick="onSearchSubmit(event)"
+               aria-label="&contentSearchSubmit.label;"/>
       </div>
 
       <div id="snippetContainer">
         <div id="defaultSnippets" hidden="true">
           <span id="defaultSnippet1">&abouthome.defaultSnippet1.v1;</span>
           <span id="defaultSnippet2">&abouthome.defaultSnippet2.v1;</span>
         </div>
         <span id="rightsSnippet" hidden="true">&abouthome.rightsSnippet;</span>
--- a/browser/base/content/browser.js
+++ b/browser/base/content/browser.js
@@ -3462,19 +3462,18 @@ const BrowserSearch = {
       }
       return;
     }
 #endif
     let openSearchPageIfFieldIsNotActive = function(aSearchBar) {
       if (!aSearchBar || document.activeElement != aSearchBar.textbox.inputField) {
         let url = gBrowser.currentURI.spec.toLowerCase();
         let mm = gBrowser.selectedBrowser.messageManager;
-        if (url === "about:home") {
-          AboutHome.focusInput(mm);
-        } else if (url === "about:newtab" && NewTabUtils.allPages.enabled) {
+        if (url === "about:home" ||
+            (url === "about:newtab" && NewTabUtils.allPages.enabled)) {
           ContentSearch.focusInput(mm);
         } else {
           openUILinkIn("about:home", "current");
         }
       }
     };
 
     let searchBar = this.searchBar;
rename from browser/base/content/searchSuggestionUI.css
rename to browser/base/content/contentSearchUI.css
--- a/browser/base/content/searchSuggestionUI.css
+++ b/browser/base/content/contentSearchUI.css
@@ -1,44 +1,152 @@
 /* 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/. */
 
-.searchSuggestionTable {
+.contentSearchSuggestionTable {
   background-color: hsla(0,0%,100%,.99);
-  border: 1px solid;
-  border-color: hsla(210,54%,20%,.15) hsla(210,54%,20%,.17) hsla(210,54%,20%,.2);
+  border: 1px solid hsla(0, 0%, 0%, .2);
+  border-top: none;
+  box-shadow: 0 5px 10px hsla(0, 0%, 0%, .1);
+  position: absolute;
+  left: 0;
+  z-index: 1001;
+  -moz-user-select: none;
+  cursor: default;
+}
+
+.contentSearchSuggestionsList {
+  border-bottom: 1px solid hsl(0, 0%, 92%);
+  width: 100%;
+  height: 100%;
+}
+
+.contentSearchSuggestionTable,
+.contentSearchSuggestionsList {
   border-spacing: 0;
-  border-top: 0;
-  box-shadow: 0 1px 0 hsla(210,65%,9%,.02) inset,
-              0 0 2px hsla(210,65%,9%,.1) inset,
-              0 1px 0 hsla(0,0%,100%,.2);
   overflow: hidden;
   padding: 0;
-  position: absolute;
+  margin: 0;
   text-align: start;
-  z-index: 1001;
 }
 
-.searchSuggestionRow {
-  cursor: default;
+.contentSearchHeaderRow,
+.contentSearchSuggestionRow {
   margin: 0;
   max-width: inherit;
   padding: 0;
 }
 
-.searchSuggestionRow.formHistory + .searchSuggestionRow.remote > td {
-  border-top: 1px solid GrayText;
+.contentSearchHeaderRow > td > img,
+.contentSearchSuggestionRow > td > .historyIcon {
+  margin-right: 8px;
+  margin-bottom: -3px;
+}
+
+.contentSearchSuggestionTable .historyIcon {
+  width: 16px;
+  height: 16px;
+  display: inline-block;
+  background-image: url("chrome://browser/skin/search-history-icon.svg#search-history-icon");
+}
+
+.contentSearchSuggestionRow.selected > td > .historyIcon {
+  background-image: url("chrome://browser/skin/search-history-icon.svg#search-history-icon-active");
 }
 
-.searchSuggestionRow.selected {
-  background-color: hsl(210,100%,40%);
-  color: hsl(0,0%,100%);
+.contentSearchHeader > img {
+  height: 16px;
+  width: 16px;
+  margin: 0;
+  padding: 0;
 }
 
-.searchSuggestionEntry {
+.contentSearchSuggestionRow.remote > td > .historyIcon {
+  visibility: hidden;
+}
+
+.contentSearchSuggestionRow.selected {
+  background-color: Highlight;
+  color: HighlightText;
+}
+
+.contentSearchHeader,
+.contentSearchSuggestionEntry {
   margin: 0;
   max-width: inherit;
   overflow: hidden;
-  padding: 6px 8px;
+  padding: 4px 10px;
   text-overflow: ellipsis;
   white-space: nowrap;
+  font-size: 75%;
 }
+
+.contentSearchHeader {
+  background-color: hsl(0, 0%, 97%);
+  color: #666;
+  border-bottom: 1px solid hsl(0, 0%, 92%);
+}
+
+.contentSearchSuggestionsContainer {
+  margin: 0;
+  padding: 0;
+  border-spacing: 0;
+  width: 100%;
+}
+
+.contentSearchSearchWithHeaderSearchText {
+  white-space: pre;
+  font-weight: bold;
+}
+
+.contentSearchOneOffItem {
+  -moz-appearance: none;
+  height: 32px;
+  margin: 0;
+  padding: 0;
+  border: none;
+  background: none;
+  background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAAWCAYAAAABxvaqAAAABmJLR0QA/wD/AP+gvaeTAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAB3RJTUUH3gofECQNNVW2/AAAABBJREFUGFdjOHPmzH8GehEA/KpKg9YTf4AAAAAASUVORK5CYII=');
+  background-repeat: no-repeat;
+  background-position: right center;
+}
+
+.contentSearchOneOffItem > img {
+  width: 16px;
+  height: 16px;
+  margin-bottom: -2px;
+}
+
+.contentSearchOneOffItem:not(.last-row) {
+  border-bottom: 1px solid hsl(0, 0%, 92%);
+}
+
+.contentSearchOneOffItem.end-of-row {
+  background-image: none;
+}
+
+.contentSearchOneOffItem.selected {
+  background-color: Highlight;
+  background-image: none;
+}
+
+.contentSearchOneOffsTable {
+  width: 100%;
+}
+
+.contentSearchSettingsButton {
+  margin: 0;
+  padding: 0;
+  height: 32px;
+  border: none;
+  border-top: 1px solid hsla(0, 0%, 0%, .08);
+  text-align: center;
+  width: 100%;
+}
+
+.contentSearchSettingsButton.selected {
+  background-color: hsl(0, 0%, 90%);
+}
+
+.contentSearchSettingsButton:active {
+  background-color: hsl(0, 0%, 85%);
+}
rename from browser/base/content/searchSuggestionUI.js
rename to browser/base/content/contentSearchUI.js
--- a/browser/base/content/searchSuggestionUI.js
+++ b/browser/base/content/contentSearchUI.js
@@ -1,19 +1,20 @@
 /* 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";
 
-this.SearchSuggestionUIController = (function () {
+this.ContentSearchUIController = (function () {
 
 const MAX_DISPLAYED_SUGGESTIONS = 6;
 const SUGGESTION_ID_PREFIX = "searchSuggestion";
-const CSS_URI = "chrome://browser/content/searchSuggestionUI.css";
+const ONE_OFF_ID_PREFIX = "oneOff";
+const CSS_URI = "chrome://browser/content/contentSearchUI.css";
 
 const HTML_NS = "http://www.w3.org/1999/xhtml";
 
 /**
  * Creates a new object that manages search suggestions and their UI for a text
  * box.
  *
  * The UI consists of an html:table that's inserted into the DOM after the given
@@ -24,267 +25,372 @@ const HTML_NS = "http://www.w3.org/1999/
  *        Assumed to be an html:input.  xul:textbox is untested but might work.
  * @param tableParent
  *        The suggestion table is appended as a child to this element.  Since
  *        the table is absolutely positioned and its top and left values are set
  *        to be relative to the top and left of the page, either the parent and
  *        all its ancestors should not be positioned elements (i.e., their
  *        positions should be "static"), or the parent's position should be the
  *        top left of the page.
- * @param onClick
- *        A function that's called when a search suggestion is clicked.  Ideally
- *        we could call submit() on inputElement's ancestor form, but that
- *        doesn't trigger submit listeners. The function is passed one argument,
- *        the click event.
+ * @param healthReportKey
+ *        This will be sent with the search data for FHR to record the search.
+ * @param searchPurpose
+ *        Sent with search data, see nsISearchEngine.getSubmission.
  * @param idPrefix
  *        The IDs of elements created by the object will be prefixed with this
  *        string.
  */
-function SearchSuggestionUIController(inputElement, tableParent, onClick=null,
-                                      idPrefix="") {
+function ContentSearchUIController(inputElement, tableParent, healthReportKey,
+                                   searchPurpose, idPrefix="") {
   this.input = inputElement;
-  this.onClick = onClick;
   this._idPrefix = idPrefix;
+  this._healthReportKey = healthReportKey;
+  this._searchPurpose = searchPurpose;
 
   let tableID = idPrefix + "searchSuggestionTable";
   this.input.autocomplete = "off";
   this.input.setAttribute("aria-autocomplete", "true");
   this.input.setAttribute("aria-controls", tableID);
   tableParent.appendChild(this._makeTable(tableID));
 
   this.input.addEventListener("keypress", this);
   this.input.addEventListener("input", this);
   this.input.addEventListener("focus", this);
   this.input.addEventListener("blur", this);
   window.addEventListener("ContentSearchService", this);
 
   this._stickyInputValue = "";
   this._hideSuggestions();
 
-  this._ignoreInputEvent = false;
+  this._getSearchEngines();
+  this._getStrings();
 }
 
-SearchSuggestionUIController.prototype = {
+ContentSearchUIController.prototype = {
 
   // The timeout (ms) of the remote suggestions.  Corresponds to
   // SearchSuggestionController.remoteTimeout.  Uses
   // SearchSuggestionController's default timeout if falsey.
   remoteTimeout: undefined,
+  _oneOffButtons: [],
 
-  get engineName() {
-    return this._engineName;
+  get defaultEngine() {
+    return this._defaultEngine;
   },
 
-  set engineName(val) {
-    this._engineName = val;
+  set defaultEngine(val) {
+    this._defaultEngine = val;
+    this._updateDefaultEngineHeader();
+
     if (val && document.activeElement == this.input) {
       this._speculativeConnect();
     }
   },
 
+  get engines() {
+    return this._engines;
+  },
+
+  set engines(val) {
+    this._engines = val;
+    this._setUpOneOffButtons();
+  },
+
+  // The selectedIndex is the index of the element with the "selected" class in
+  // the list obtained by concatenating the suggestion rows, one-off buttons, and
+  // search settings button.
   get selectedIndex() {
-    for (let i = 0; i < this._table.children.length; i++) {
-      let row = this._table.children[i];
-      if (row.classList.contains("selected")) {
+    let allElts = [...this._suggestionsList.children,
+                   ...this._oneOffButtons,
+                   document.getElementById("contentSearchSettingsButton")];
+    for (let i = 0; i < allElts.length; ++i) {
+      let elt = allElts[i];
+      if (elt.classList.contains("selected")) {
         return i;
       }
     }
     return -1;
   },
 
   set selectedIndex(idx) {
     // Update the table's rows, and the input when there is a selection.
     this._table.removeAttribute("aria-activedescendant");
-    for (let i = 0; i < this._table.children.length; i++) {
-      let row = this._table.children[i];
+    this.input.removeAttribute("aria-activedescendant");
+
+    let allElts = [...this._suggestionsList.children,
+                   ...this._oneOffButtons,
+                   document.getElementById("contentSearchSettingsButton")];
+    for (let i = 0; i < allElts.length; ++i) {
+      let elt = allElts[i];
+      let ariaSelectedElt = i < this.numSuggestions ? elt.firstChild : elt;
       if (i == idx) {
-        row.classList.add("selected");
-        row.firstChild.setAttribute("aria-selected", "true");
-        this._table.setAttribute("aria-activedescendant", row.firstChild.id);
+        elt.classList.add("selected");
+        ariaSelectedElt.setAttribute("aria-selected", "true");
+        this.input.setAttribute("aria-activedescendant", ariaSelectedElt.id);
       }
       else {
-        row.classList.remove("selected");
-        row.firstChild.setAttribute("aria-selected", "false");
+        elt.classList.remove("selected");
+        ariaSelectedElt.setAttribute("aria-selected", "false");
       }
     }
   },
 
+  get selectedEngineName() {
+    let selectedElt = this._table.querySelector(".selected");
+    if (selectedElt && selectedElt.engineName) {
+      return selectedElt.engineName;
+    }
+    return this.defaultEngine.name;
+  },
+
   get numSuggestions() {
-    return this._table.children.length;
+    return this._suggestionsList.children.length;
   },
 
   selectAndUpdateInput: function (idx) {
     this.selectedIndex = idx;
-    this.input.value = idx >= 0 ? this.suggestionAtIndex(idx) :
-                       this._stickyInputValue;
+    let newValue = this.suggestionAtIndex(idx) || this._stickyInputValue;
+    // Setting the input value when the value has not changed commits the current
+    // IME composition, which we don't want to do.
+    if (this.input.value != newValue) {
+      this.input.value = newValue;
+    }
+    this._updateSearchWithHeader();
   },
 
   suggestionAtIndex: function (idx) {
-    let row = this._table.children[idx];
+    let row = this._suggestionsList.children[idx];
     return row ? row.textContent : null;
   },
 
   deleteSuggestionAtIndex: function (idx) {
     // Only form history suggestions can be deleted.
     if (this.isFormHistorySuggestionAtIndex(idx)) {
       let suggestionStr = this.suggestionAtIndex(idx);
       this._sendMsg("RemoveFormHistoryEntry", suggestionStr);
-      this._table.children[idx].remove();
+      this._suggestionsList.children[idx].remove();
       this.selectAndUpdateInput(-1);
     }
   },
 
   isFormHistorySuggestionAtIndex: function (idx) {
-    let row = this._table.children[idx];
+    let row = this._suggestionsList.children[idx];
     return row && row.classList.contains("formHistory");
   },
 
   addInputValueToFormHistory: function () {
     this._sendMsg("AddFormHistoryEntry", this.input.value);
   },
 
   handleEvent: function (event) {
     this["_on" + event.type[0].toUpperCase() + event.type.substr(1)](event);
   },
 
-  _onInput: function (event) {
-    if (this._ignoreInputEvent) {
+  _onCommand: function(aEvent) {
+    if (this.selectedIndex == this.numSuggestions + this._oneOffButtons.length) {
+      // Settings button was selected.
+      this._sendMsg("ManageEngines");
       return;
     }
-    if (this.input.value) {
-      this._getSuggestions();
+
+    this.search(aEvent);
+
+    if (aEvent) {
+      aEvent.preventDefault();
+    }
+  },
+
+  search: function (aEvent) {
+    if (!this.defaultEngine) {
+      return; // Not initialized yet.
+    }
+
+    let searchText = this.input;
+    let searchTerms;
+    if (this._table.hidden ||
+        aEvent.originalTarget.id == "contentSearchDefaultEngineHeader") {
+      searchTerms = searchText.value;
     }
     else {
+      searchTerms = this.suggestionAtIndex(this.selectedIndex) || searchText.value;
+    }
+    // Send an event that will perform a search and Firefox Health Report will
+    // record that a search from the healthReportKey passed to the constructor.
+    let eventData = {
+      engineName: this.selectedEngineName,
+      searchString: searchTerms,
+      healthReportKey: this._healthReportKey,
+      searchPurpose: this._searchPurpose,
+      originalEvent: {
+        shiftKey: aEvent.shiftKey,
+        ctrlKey: aEvent.ctrlKey,
+        metaKey: aEvent.metaKey,
+        altKey: aEvent.altKey,
+        button: aEvent.button,
+      },
+    };
+
+    if (this.suggestionAtIndex(this.selectedIndex)) {
+      eventData.selection = {
+        index: this.selectedIndex,
+        kind: aEvent instanceof MouseEvent ? "mouse" :
+              aEvent instanceof KeyboardEvent ? "key" : undefined,
+      };
+    }
+
+    this._sendMsg("Search", eventData);
+    this.addInputValueToFormHistory();
+  },
+
+  _onInput: function () {
+    if (!this.input.value) {
       this._stickyInputValue = "";
       this._hideSuggestions();
     }
-    this.selectedIndex = -1;
+    else if (this.input.value != this._stickyInputValue) {
+      // Only fetch new suggestions if the input value has changed.
+      this._getSuggestions();
+      this.selectAndUpdateInput(-1);
+    }
+    this._updateSearchWithHeader();
   },
 
   _onKeypress: function (event) {
     let selectedIndexDelta = 0;
     switch (event.keyCode) {
     case event.DOM_VK_UP:
-      if (this.numSuggestions) {
+      if (!this._table.hidden) {
         selectedIndexDelta = -1;
       }
       break;
     case event.DOM_VK_DOWN:
-      if (this.numSuggestions) {
-        selectedIndexDelta = 1;
+      if (this._table.hidden) {
+        this._getSuggestions();
       }
       else {
-        this._getSuggestions();
+        selectedIndexDelta = 1;
       }
       break;
     case event.DOM_VK_RIGHT:
       // Allow normal caret movement until the caret is at the end of the input.
       if (this.input.selectionStart != this.input.selectionEnd ||
           this.input.selectionEnd != this.input.value.length) {
         return;
       }
-      // else, fall through
-    case event.DOM_VK_RETURN:
-      if (this.selectedIndex >= 0) {
+      if (this.numSuggestions && this.selectedIndex >= 0 &&
+          this.selectedIndex < this.numSuggestions) {
         this.input.value = this.suggestionAtIndex(this.selectedIndex);
         this.input.setAttribute("selection-index", this.selectedIndex);
         this.input.setAttribute("selection-kind", "key");
       } else {
         // If we didn't select anything, make sure to remove the attributes
         // in case they were populated last time.
         this.input.removeAttribute("selection-index");
         this.input.removeAttribute("selection-kind");
       }
       this._stickyInputValue = this.input.value;
       this._hideSuggestions();
       break;
+    case event.DOM_VK_RETURN:
+      this._onCommand(event);
+      break;
     case event.DOM_VK_DELETE:
       if (this.selectedIndex >= 0) {
         this.deleteSuggestionAtIndex(this.selectedIndex);
       }
       break;
+    case event.DOM_VK_ESCAPE:
+      if (!this._table.hidden) {
+        this._hideSuggestions();
+      }
     default:
       return;
     }
 
     if (selectedIndexDelta) {
       // Update the selection.
       let newSelectedIndex = this.selectedIndex + selectedIndexDelta;
       if (newSelectedIndex < -1) {
-        newSelectedIndex = this.numSuggestions - 1;
+        newSelectedIndex = this.numSuggestions + this._oneOffButtons.length;
       }
-      else if (this.numSuggestions <= newSelectedIndex) {
+      else if (this.numSuggestions + this._oneOffButtons.length < newSelectedIndex) {
         newSelectedIndex = -1;
       }
       this.selectAndUpdateInput(newSelectedIndex);
 
       // Prevent the input's caret from moving.
       event.preventDefault();
     }
   },
 
   _onFocus: function () {
+    if (this._mousedown) {
+      return;
+    }
+    // When the input box loses focus to something in our table, we refocus it
+    // immediately. This causes the focus highlight to flicker, so we set a
+    // custom attribute which consumers should use for focus highlighting. This
+    // attribute is removed only when we do not immediately refocus the input
+    // box, thus eliminating flicker.
+    this.input.setAttribute("keepfocus", "true");
     this._speculativeConnect();
   },
 
   _onBlur: function () {
+    if (this._mousedown) {
+      // At this point, this.input has lost focus, but a new element has not yet
+      // received it. If we re-focus this.input directly, the new element will
+      // steal focus immediately, so we queue it instead.
+      setTimeout(() => this.input.focus(), 0);
+      return;
+    }
+    this.input.removeAttribute("keepfocus");
     this._hideSuggestions();
   },
 
   _onMousemove: function (event) {
-    this.selectedIndex = this._indexOfTableRowOrDescendent(event.target);
+    this.selectedIndex = this._indexOfTableItem(event.target);
   },
 
-  _onMousedown: function (event) {
+  _onMouseup: function (event) {
     if (event.button == 2) {
       return;
     }
-    let idx = this._indexOfTableRowOrDescendent(event.target);
-    let suggestion = this.suggestionAtIndex(idx);
-    this._stickyInputValue = suggestion;
+    this._onCommand(event);
+  },
 
-    // Setting value commits composition string forcibly.  While IME commits
-    // composition, this needs to ignore input event at committed composition
-    // string which will be overwritten by the suggestion.
-    this._ignoreInputEvent = true;
-    this.input.value = suggestion;
-    this._ignoreInputEvent = false;
-    this.input.setAttribute("selection-index", idx);
-    this.input.setAttribute("selection-kind", "mouse");
-    this._hideSuggestions();
-    if (this.onClick) {
-      this.onClick.call(null, event);
-    }
+  _onClick: function (event) {
+    this._onMouseup(event);
   },
 
   _onContentSearchService: function (event) {
     let methodName = "_onMsg" + event.detail.type;
     if (methodName in this) {
       this[methodName](event.detail.data);
     }
   },
 
+  _onMsgFocusInput: function (event) {
+    this.input.focus();
+  },
+
   _onMsgSuggestions: function (suggestions) {
     // Ignore the suggestions if their search string or engine doesn't match
     // ours.  Due to the async nature of message passing, this can easily happen
     // when the user types quickly.
     if (this._stickyInputValue != suggestions.searchString ||
-        this.engineName != suggestions.engineName) {
+        this.defaultEngine.name != suggestions.engineName) {
       return;
     }
 
-    // Empty the table.
-    while (this._table.firstElementChild) {
-      this._table.firstElementChild.remove();
-    }
+    this._clearSuggestionRows();
 
     // Position and size the table.
-    let { left, bottom } = this.input.getBoundingClientRect();
-    this._table.style.left = (left + window.scrollX) + "px";
-    this._table.style.top = (bottom + window.scrollY) + "px";
+    let { left } = this.input.getBoundingClientRect();
+    this._table.style.top = this.input.offsetHeight + "px";
     this._table.style.minWidth = this.input.offsetWidth + "px";
     this._table.style.maxWidth = (window.innerWidth - left - 40) + "px";
 
     // Add the suggestions to the table.
     let searchWords =
       new Set(suggestions.searchString.trim().toLowerCase().split(/\s+/));
     for (let i = 0; i < MAX_DISPLAYED_SUGGESTIONS; i++) {
       let type, idx;
@@ -295,41 +401,108 @@ SearchSuggestionUIController.prototype =
         let j = i - suggestions.formHistory.length;
         if (j < suggestions.remote.length) {
           [type, idx] = ["remote", j];
         }
         else {
           break;
         }
       }
-      this._table.appendChild(this._makeTableRow(type, suggestions[type][idx],
-                                                 i, searchWords));
+      this._suggestionsList.appendChild(
+        this._makeTableRow(type, suggestions[type][idx], i, searchWords));
     }
 
-    this._table.hidden = false;
-    this.input.setAttribute("aria-expanded", "true");
+    if (this._table.hidden) {
+      this.selectedIndex = -1;
+      this._table.hidden = false;
+      this.input.setAttribute("aria-expanded", "true");
+    }
+  },
+
+  _onMsgState: function (state) {
+    this.defaultEngine = {
+      name: state.currentEngine.name,
+      icon: this._getFaviconURIFromBuffer(state.currentEngine.iconBuffer),
+    };
+    this.engines = state.engines;
+  },
+
+  _onMsgCurrentState: function (state) {
+    this._onMsgState(state);
+  },
+
+  _onMsgCurrentEngine: function (engine) {
+    this.defaultEngine = {
+      name: engine.name,
+      icon: this._getFaviconURIFromBuffer(engine.iconBuffer),
+    };
+    this._setUpOneOffButtons();
+  },
+
+  _onMsgStrings: function (strings) {
+    this._strings = strings;
+    this._updateDefaultEngineHeader();
+    this._updateSearchWithHeader();
+  },
+
+  _updateDefaultEngineHeader: function () {
+    let header = document.getElementById("contentSearchDefaultEngineHeader");
+    if (this.defaultEngine.icon) {
+      header.firstChild.setAttribute("src", this.defaultEngine.icon);
+    }
+    if (!this._strings) {
+      return;
+    }
+    while (header.firstChild.nextSibling) {
+      header.firstChild.nextSibling.remove();
+    }
+    header.appendChild(document.createTextNode(
+      this._strings.searchHeader.replace("%S", this.defaultEngine.name)));
+  },
+
+  _updateSearchWithHeader: function () {
+    if (!this._strings) {
+      return;
+    }
+    let searchWithHeader = document.getElementById("contentSearchSearchWithHeader");
+    while (searchWithHeader.firstChild) {
+      searchWithHeader.firstChild.remove();
+    }
+    if (this.input.value) {
+      searchWithHeader.appendChild(document.createTextNode(this._strings.searchFor));
+      let span = document.createElementNS(HTML_NS, "span");
+      span.setAttribute("class", "contentSearchSearchWithHeaderSearchText");
+      span.appendChild(document.createTextNode(" " + this.input.value + " "));
+      searchWithHeader.appendChild(span);
+      searchWithHeader.appendChild(document.createTextNode(this._strings.searchWith));
+      return;
+    }
+    searchWithHeader.appendChild(document.createTextNode(this._strings.searchWithHeader));
   },
 
   _speculativeConnect: function () {
-    if (this.engineName) {
-      this._sendMsg("SpeculativeConnect", this.engineName);
+    if (this.defaultEngine) {
+      this._sendMsg("SpeculativeConnect", this.defaultEngine.name);
     }
   },
 
   _makeTableRow: function (type, suggestionStr, currentRow, searchWords) {
     let row = document.createElementNS(HTML_NS, "tr");
     row.dir = "auto";
-    row.classList.add("searchSuggestionRow");
+    row.classList.add("contentSearchSuggestionRow");
     row.classList.add(type);
     row.setAttribute("role", "presentation");
     row.addEventListener("mousemove", this);
-    row.addEventListener("mousedown", this);
+    row.addEventListener("mouseup", this);
 
     let entry = document.createElementNS(HTML_NS, "td");
-    entry.classList.add("searchSuggestionEntry");
+    let img = document.createElementNS(HTML_NS, "div");
+    img.setAttribute("class", "historyIcon");
+    entry.appendChild(img);
+    entry.classList.add("contentSearchSuggestionEntry");
     entry.setAttribute("role", "option");
     entry.id = this._idPrefix + SUGGESTION_ID_PREFIX + currentRow;
     entry.setAttribute("aria-selected", "false");
 
     let suggestionWords = suggestionStr.trim().toLowerCase().split(/\s+/);
     for (let i = 0; i < suggestionWords.length; i++) {
       let word = suggestionWords[i];
       let wordSpan = document.createElementNS(HTML_NS, "span");
@@ -342,59 +515,221 @@ SearchSuggestionUIController.prototype =
         entry.appendChild(document.createTextNode(" "));
       }
     }
 
     row.appendChild(entry);
     return row;
   },
 
+  // Converts favicon array buffer into data URI of the right size and dpi.
+  _getFaviconURIFromBuffer: function (buffer) {
+    let blob = new Blob([buffer]);
+    let dpiSize = Math.round(16 * window.devicePixelRatio);
+    let sizeStr = dpiSize + "," + dpiSize;
+    return URL.createObjectURL(blob) + "#-moz-resolution=" + sizeStr;
+  },
+
+  _getSearchEngines: function () {
+    this._sendMsg("GetState");
+  },
+
+  _getStrings: function () {
+    this._sendMsg("GetStrings");
+  },
+
   _getSuggestions: function () {
     this._stickyInputValue = this.input.value;
-    if (this.engineName) {
+    if (this.defaultEngine) {
       this._sendMsg("GetSuggestions", {
-        engineName: this.engineName,
+        engineName: this.defaultEngine.name,
         searchString: this.input.value,
         remoteTimeout: this.remoteTimeout,
       });
     }
   },
 
+  _clearSuggestionRows: function() {
+    while (this._suggestionsList.firstElementChild) {
+      this._suggestionsList.firstElementChild.remove();
+    }
+  },
+
   _hideSuggestions: function () {
     this.input.setAttribute("aria-expanded", "false");
     this._table.hidden = true;
-    while (this._table.firstElementChild) {
-      this._table.firstElementChild.remove();
-    }
-    this.selectAndUpdateInput(-1);
   },
 
-  _indexOfTableRowOrDescendent: function (row) {
-    while (row && row.localName != "tr") {
-      row = row.parentNode;
+  _indexOfTableItem: function (elt) {
+    if (elt.classList.contains("contentSearchOneOffItem")) {
+      return this.numSuggestions + this._oneOffButtons.indexOf(elt);
     }
-    if (!row) {
+    if (elt.classList.contains("contentSearchSettingsButton")) {
+      return this.numSuggestions + this._oneOffButtons.length;
+    }
+    while (elt && elt.localName != "tr") {
+      elt = elt.parentNode;
+    }
+    if (!elt) {
       throw new Error("Element is not a row");
     }
-    return row.rowIndex;
+    return elt.rowIndex;
   },
 
   _makeTable: function (id) {
     this._table = document.createElementNS(HTML_NS, "table");
     this._table.id = id;
     this._table.hidden = true;
-    this._table.classList.add("searchSuggestionTable");
-    this._table.setAttribute("role", "listbox");
+    this._table.classList.add("contentSearchSuggestionTable");
+    this._table.setAttribute("role", "presentation");
+
+    // When the search input box loses focus, we want to immediately give focus
+    // back to it if the blur was because the user clicked somewhere in the table.
+    // onBlur uses the _mousedown flag to detect this.
+    this._table.addEventListener("mousedown", () => { this._mousedown = true; });
+    document.addEventListener("mouseup", () => { delete this._mousedown; });
+
+    // Deselect the selected element on mouseout if it wasn't a suggestion.
+    this._table.addEventListener("mouseout", () => {
+      if (this.selectedIndex >= this.numSuggestions) {
+        this.selectAndUpdateInput(-1);
+      }
+    });
+
+    // If a search is loaded in the same tab, ensure the suggestions dropdown
+    // is hidden immediately when the page starts loading and not when it first
+    // appears, in order to provide timely feedback to the user.
+    window.addEventListener("beforeunload", () => { this._hideSuggestions(); });
+
+    let headerRow = document.createElementNS(HTML_NS, "tr");
+    let header = document.createElementNS(HTML_NS, "td");
+    headerRow.setAttribute("class", "contentSearchHeaderRow");
+    header.setAttribute("class", "contentSearchHeader");
+    let img = document.createElementNS(HTML_NS, "img");
+    img.setAttribute("src", "chrome://browser/skin/search-engine-placeholder.png");
+    header.appendChild(img);
+    header.id = "contentSearchDefaultEngineHeader";
+    headerRow.appendChild(header);
+    headerRow.addEventListener("click", this);
+    this._table.appendChild(headerRow);
+
+    let row = document.createElementNS(HTML_NS, "tr");
+    row.setAttribute("class", "contentSearchSuggestionsContainer");
+    let cell = document.createElementNS(HTML_NS, "td");
+    cell.setAttribute("class", "contentSearchSuggestionsContainer");
+    this._suggestionsList = document.createElementNS(HTML_NS, "table");
+    this._suggestionsList.setAttribute("class", "contentSearchSuggestionsList");
+    cell.appendChild(this._suggestionsList);
+    row.appendChild(cell);
+    this._table.appendChild(row);
+    this._suggestionsList.setAttribute("role", "listbox");
+
+    this._oneOffsTable = document.createElementNS(HTML_NS, "table");
+    this._oneOffsTable.setAttribute("class", "contentSearchOneOffsTable");
+    this._oneOffsTable.classList.add("contentSearchSuggestionsContainer");
+    this._oneOffsTable.setAttribute("role", "group");
+    this._table.appendChild(this._oneOffsTable);
+
+    headerRow = document.createElementNS(HTML_NS, "tr");
+    header = document.createElementNS(HTML_NS, "td");
+    headerRow.setAttribute("class", "contentSearchHeaderRow");
+    header.setAttribute("class", "contentSearchHeader");
+    headerRow.appendChild(header);
+    header.id = "contentSearchSearchWithHeader";
+    this._oneOffsTable.appendChild(headerRow);
+
+    let button = document.createElementNS(HTML_NS, "button");
+    button.appendChild(document.createTextNode("Change Search Settings"));
+    button.setAttribute("class", "contentSearchSettingsButton");
+    button.classList.add("contentSearchHeaderRow");
+    button.classList.add("contentSearchHeader");
+    button.id = "contentSearchSettingsButton";
+    button.addEventListener("click", this);
+    button.addEventListener("mousemove", this);
+    this._table.appendChild(button);
+
     return this._table;
   },
 
+  _setUpOneOffButtons: function () {
+    // Sometimes we receive a CurrentEngine message from the ContentSearch service
+    // before we've received a State message - i.e. before we have our engines.
+    if (!this._engines) {
+      return;
+    }
+
+    while (this._oneOffsTable.firstChild.nextSibling) {
+      this._oneOffsTable.firstChild.nextSibling.remove();
+    }
+
+    this._oneOffButtons = [];
+
+    let engines = this._engines.filter(aEngine => aEngine.name != this.defaultEngine.name);
+    if (!engines.length) {
+      this._oneOffsTable.hidden = true;
+      return;
+    }
+
+    const kDefaultButtonWidth = 49; // 48px + 1px border.
+    let rowWidth = this.input.offsetWidth - 2; // 2px border.
+    let enginesPerRow = Math.floor(rowWidth / kDefaultButtonWidth);
+    let buttonWidth = Math.floor(rowWidth / enginesPerRow);
+
+    let row = document.createElementNS(HTML_NS, "tr");
+    let cell = document.createElementNS(HTML_NS, "td");
+    row.setAttribute("class", "contentSearchSuggestionsContainer");
+    cell.setAttribute("class", "contentSearchSuggestionsContainer");
+
+    for (let i = 0; i < engines.length; ++i) {
+      let engine = engines[i];
+      if (i > 0 && i % enginesPerRow == 0) {
+        row.appendChild(cell);
+        this._oneOffsTable.appendChild(row);
+        row = document.createElementNS(HTML_NS, "tr");
+        cell = document.createElementNS(HTML_NS, "td");
+        row.setAttribute("class", "contentSearchSuggestionsContainer");
+        cell.setAttribute("class", "contentSearchSuggestionsContainer");
+      }
+      let button = document.createElementNS(HTML_NS, "button");
+      button.setAttribute("class", "contentSearchOneOffItem");
+      let img = document.createElementNS(HTML_NS, "img");
+      let uri = "chrome://browser/skin/search-engine-placeholder.png";
+      if (engine.iconBuffer) {
+        uri = this._getFaviconURIFromBuffer(engine.iconBuffer);
+      }
+      img.setAttribute("src", uri);
+      button.appendChild(img);
+      button.style.width = buttonWidth + "px";
+      button.setAttribute("title", engine.name);
+
+      button.engineName = engine.name;
+      button.addEventListener("click", this);
+      button.addEventListener("mousemove", this);
+
+      if (engines.length - i <= enginesPerRow - (i % enginesPerRow)) {
+        button.classList.add("last-row");
+      }
+
+      if ((i + 1) % enginesPerRow == 0) {
+        button.classList.add("end-of-row");
+      }
+
+      button.id = ONE_OFF_ID_PREFIX + i;
+      cell.appendChild(button);
+      this._oneOffButtons.push(button);
+    }
+    row.appendChild(cell);
+    this._oneOffsTable.appendChild(row);
+    this._oneOffsTable.hidden = false;
+  },
+
   _sendMsg: function (type, data=null) {
     dispatchEvent(new CustomEvent("ContentSearchClient", {
       detail: {
         type: type,
         data: data,
       },
     }));
   },
 };
 
-return SearchSuggestionUIController;
+return ContentSearchUIController;
 })();
--- a/browser/base/content/newtab/newTab.css
+++ b/browser/base/content/newtab/newTab.css
@@ -325,144 +325,116 @@ input[type=button] {
   background-color: #fff;
   opacity: 0.01;
 }
 
 /* SEARCH */
 #newtab-search-container {
   display: -moz-box;
   position: relative;
-  -moz-box-align: center;
   -moz-box-pack: center;
 }
 
 #newtab-search-container[page-disabled] {
   opacity: 0;
   pointer-events: none;
 }
 
 #newtab-search-form {
   display: -moz-box;
+  position: relative;
+  height: 36px;
   -moz-box-flex: 1;
-  -moz-box-orient: horizontal;
-  -moz-box-align: center;
-  height: 44px; /* 32 + 6 logo top "padding" + 6 logo bottom "padding" */
-  margin: 26px 20px 10px; /* top: 32 - 6 search form top "padding", bottom: 32 - 16 tiles top margin - 6 logo bottom "padding" */
   max-width: 600px; /* 2 * (290 cell width + 10 cell margin) */
 }
 
-#newtab-search-logo {
-  display: -moz-box;
-  width: 38px;
-  height: 38px; /* 26 image height + 6 top "padding" + 6 bottom "padding" */
-  border: 1px solid transparent;
-  -moz-margin-end: 8px;
-  background-repeat: no-repeat;
-  background-position: center;
-  background-image: url(chrome://global/skin/icons/autocomplete-search.svg#search-icon);
-  background-size: 26px 26px;
-}
-
-#newtab-search-logo.magnifier {
-  width: 38px; /* 26 image width + 6 left "padding" + 6 right "padding" */
-  -moz-margin-end: 5px;
-  background-size: 26px;
-  background-image: url("chrome://browser/skin/magnifier.png");
-}
-
-@media not all and (max-resolution: 1dppx) {
-  #newtab-search-logo.magnifier {
-    background-image: url("chrome://browser/skin/magnifier@2x.png");
-  }
-}
-
-#newtab-search-logo[type="logo"] {
-  background-size: 65px 26px;
-  width: 77px; /* 65 image width + 6 left "padding" + 6 right "padding" */
-}
-
-#newtab-search-logo[type="favicon"] {
-  background-size: 16px 16px;
-}
-
-#newtab-search-logo[hidden] {
-  display: none;
-}
-
-#newtab-search-logo[active],
-#newtab-search-logo:hover {
-  background-color: #e9e9e9;
-  border: 1px solid rgb(226, 227, 229);
-  border-radius: 2.5px;
+#newtab-search-icon {
+  border: 1px transparent;
+  padding: 0;
+  margin: 0;
+  width: 36px;
+  height: 36px;
+  background: url("chrome://browser/skin/search-indicator-magnifying-glass.svg") center center no-repeat;
+  position: absolute;
 }
 
 #newtab-search-text {
-  height: 38px; /* same height as #newtab-search-logo */
   -moz-box-flex: 1;
-
-  padding: 0 8px;
+  padding-top: 6px;
+  padding-bottom: 6px;
+  padding-left: 34px;
+  padding-right: 8px;
   background: hsla(0,0%,100%,.9) padding-box;
   border: 1px solid;
+  border-spacing: 0;
+  border-radius: 2px 0 0 2px;
   border-color: hsla(210,54%,20%,.15) hsla(210,54%,20%,.17) hsla(210,54%,20%,.2);
   box-shadow: 0 1px 0 hsla(210,65%,9%,.02) inset,
               0 0 2px hsla(210,65%,9%,.1) inset,
               0 1px 0 hsla(0,0%,100%,.2);
-  border-radius: 2.5px 0 0 2.5px;
   color: inherit;
 }
 
 #newtab-search-text:-moz-dir(rtl) {
-  border-radius: 0 2.5px 2.5px 0;
+  border-radius: 0 2px 2px 0;
 }
 
+#newtab-search-text[aria-expanded="true"] {
+  border-radius: 2px 0 0 0;
+}
+
+#newtab-search-text[aria-expanded="true"]:-moz-dir(rtl) {
+  border-radius: 0 2px 0 0;
+}
+
+#newtab-search-text[keepfocus],
 #newtab-search-text:focus,
 #newtab-search-text[autofocus] {
   border-color: hsla(206,100%,60%,.6) hsla(206,76%,52%,.6) hsla(204,100%,40%,.6);
 }
 
 #newtab-search-submit {
-  height: 38px; /* same height as #newtab-search-logo */
-  font-size: 13px !important;
-
   -moz-margin-start: -1px;
-  background: linear-gradient(hsla(0,0%,100%,.8), hsla(0,0%,100%,.1)) padding-box;
-  padding: 0 9px;
+  background: url("chrome://browser/skin/search-arrow-go.svg#search-arrow-go") center center no-repeat, linear-gradient(hsla(0,0%,100%,.8), hsla(0,0%,100%,.1)) padding-box;
+  padding: 0;
   border: 1px solid;
   border-color: hsla(210,54%,20%,.15) hsla(210,54%,20%,.17) hsla(210,54%,20%,.2);
+  border-radius: 0 2px 2px 0;
   -moz-border-start: 1px solid transparent;
-  border-radius: 0 2.5px 2.5px 0;
   box-shadow: 0 0 2px hsla(0,0%,100%,.5) inset,
               0 1px 0 hsla(0,0%,100%,.2);
   color: inherit;
   cursor: pointer;
   transition-property: background-color, border-color, box-shadow;
   transition-duration: 150ms;
+  width: 50px;
 }
 
 #newtab-search-submit:-moz-dir(rtl) {
-  border-radius: 2.5px 0 0 2.5px;
+  border-radius: 2px 0 0 2px;
 }
 
 #newtab-search-text:focus + #newtab-search-submit,
 #newtab-search-text + #newtab-search-submit:hover,
 #newtab-search-text[autofocus] + #newtab-search-submit {
   border-color: #59b5fc #45a3e7 #3294d5;
   color: white;
 }
 
 #newtab-search-text:focus + #newtab-search-submit,
+#newtab-search-text[keepfocus] + #newtab-search-submit,
 #newtab-search-text[autofocus] + #newtab-search-submit {
-  background-image: linear-gradient(#4cb1ff, #1793e5);
+  background: url("chrome://browser/skin/search-arrow-go.svg#search-arrow-go-inverted") center center no-repeat, linear-gradient(#4cb1ff, #1793e5);
   box-shadow: 0 1px 0 hsla(0,0%,100%,.2) inset,
               0 0 0 1px hsla(0,0%,100%,.1) inset,
               0 1px 0 hsla(210,54%,20%,.03);
 }
 
 #newtab-search-text + #newtab-search-submit:hover {
-  background-image: linear-gradient(#66bdff, #0d9eff);
+  background: url("chrome://browser/skin/search-arrow-go.svg#search-arrow-go-inverted") center center no-repeat, linear-gradient(#4cb1ff, #1793e5);
   box-shadow: 0 1px 0 hsla(0,0%,100%,.2) inset,
               0 0 0 1px hsla(0,0%,100%,.1) inset,
               0 1px 0 hsla(210,54%,20%,.03),
               0 0 4px hsla(206,100%,20%,.2);
 }
 
 #newtab-search-text + #newtab-search-submit:hover:active {
   box-shadow: 0 1px 1px hsla(211,79%,6%,.1) inset,
@@ -539,48 +511,35 @@ input[type=button] {
   display: table-cell;
   border-top: none;
 }
 
 #newtab-customize-title > label {
   cursor: default;
 }
 
-#newtab-customize-panel > .panel-arrowcontainer > .panel-arrowcontent,
-#newtab-search-panel > .panel-arrowcontainer > .panel-arrowcontent {
+#newtab-customize-panel > .panel-arrowcontainer > .panel-arrowcontent {
   padding: 0;
 }
 
-.newtab-customize-panel-item,
-.newtab-search-panel-engine,
-#newtab-search-manage {
+.newtab-customize-panel-item {
   line-height: 25px;
   padding: 15px;
   -moz-padding-start: 40px;
   font-size: 14px;
   cursor: pointer;
   max-width: 300px;
 }
 
-.newtab-customize-panel-item:not(:first-child),
-.newtab-search-panel-engine {
+.newtab-customize-panel-item:not(:first-child) {
   border-top: 1px solid threedshadow;
 }
 
-.newtab-search-panel-engine > image {
-  -moz-margin-end: 8px;
-  width: 16px;
-  height: 16px;
-  list-style-image: url("chrome://mozapps/skin/places/defaultFavicon.png");
-}
-
 .newtab-customize-panel-subitem > label,
 .newtab-customize-panel-item > label,
-.newtab-search-panel-engine > label,
-#newtab-search-manage > label,
 .newtab-customize-complex-option {
   padding: 0;
   margin: 0;
   cursor: pointer;
 }
 
 .newtab-customize-panel-item,
 .newtab-customize-complex-option {
@@ -620,18 +579,17 @@ input[type=button] {
   background-position: 15px 15px;
   color: #171F26;
 }
 
 .newtab-customize-complex-option:hover > .selectable:not([selected]) + .newtab-customize-panel-subitem {
   background-color: #FFFFFF;
 }
 
-.newtab-customize-panel-item[selected],
-.newtab-search-panel-engine[selected] {
+.newtab-customize-panel-item[selected] {
   background: url("chrome://global/skin/menu/shared-menu-check-active.svg") no-repeat transparent;
   background-size: 16px 16px;
   background-position: 15px 15px;
   color: black;
   font-weight: 600;
 }
 
 .newtab-customize-panel-subitem > .checkbox {
@@ -666,17 +624,17 @@ input[type=button] {
 .newtab-customize-panel-superitem {
   line-height: 20px;
   border-bottom: medium none !important;
   padding: 15px 15px 10px 15px;
   -moz-padding-start: 40px;
   border-top: 1px solid threedshadow;
 }
 
-.searchSuggestionTable {
+.contentSearchSuggestionTable {
   font: message-box;
   font-size: 16px;
 }
 
 /**
  * Onboarding styling
  */
 
--- a/browser/base/content/newtab/newTab.xul
+++ b/browser/base/content/newtab/newTab.xul
@@ -1,39 +1,30 @@
 <?xml version="1.0" encoding="UTF-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/. -->
 
 <?xml-stylesheet href="chrome://global/skin/" type="text/css"?>
-<?xml-stylesheet href="chrome://browser/content/searchSuggestionUI.css" type="text/css"?>
+<?xml-stylesheet href="chrome://browser/content/contentSearchUI.css" type="text/css"?>
 <?xml-stylesheet href="chrome://browser/content/newtab/newTab.css" type="text/css"?>
 <?xml-stylesheet href="chrome://browser/skin/newtab/newTab.css" type="text/css"?>
 
 <!DOCTYPE window [
   <!ENTITY % newTabDTD SYSTEM "chrome://browser/locale/newTab.dtd">
   %newTabDTD;
-  <!ENTITY % searchBarDTD SYSTEM "chrome://browser/locale/searchbar.dtd">
-  %searchBarDTD;
   <!ENTITY % browserDTD SYSTEM "chrome://browser/locale/browser.dtd">
   %browserDTD;
 ]>
 
 <xul:window id="newtab-window" xmlns="http://www.w3.org/1999/xhtml"
             xmlns:xul="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
             title="&newtab.pageTitle;">
 
-  <xul:panel id="newtab-search-panel" orient="vertical" type="arrow"
-             noautohide="true" hidden="true">
-    <xul:hbox id="newtab-search-manage">
-      <xul:label>&changeSearchSettings.button;</xul:label>
-    </xul:hbox>
-  </xul:panel>
-
   <div class="newtab-customize-panel-container">
     <div id="newtab-customize-panel" orient="vertical">
         <div id="newtab-customize-panel-anchor"></div>
         <div id="newtab-customize-title" class="newtab-customize-panel-item">
             <label>&newtab.customize.cog.title2;</label>
         </div>
 
         <div class="newtab-customize-complex-option">
@@ -103,23 +94,23 @@
                       class="newtab-undo-button" />
           <xul:toolbarbutton id="newtab-undo-close-button" tabindex="-1"
                              class="close-icon tabbable"
                              tooltiptext="&newtab.undo.closeTooltip;" />
         </div>
       </div>
 
       <div id="newtab-search-container">
-        <form id="newtab-search-form" name="searchForm">
-          <div id="newtab-search-logo"/>
+        <div id="newtab-search-form">
+          <div id="newtab-search-icon"/>
           <input type="text" name="q" value="" id="newtab-search-text"
-                 maxlength="256" dir="auto"/>
-          <input id="newtab-search-submit" type="submit"
-                 value="&searchEndCap.label;"/>
-        </form>
+                 aria-label="&contentSearchInput.label;" maxlength="256" dir="auto"/>
+          <input id="newtab-search-submit" type="button" value=""
+                 aria-label="&contentSearchSubmit.label;"/>
+        </div>
       </div>
 
       <div id="newtab-horizontal-margin">
         <div class="newtab-side-margin"/>
 
         <div id="newtab-grid">
         </div>
 
@@ -128,12 +119,12 @@
 
       <div id="newtab-margin-bottom"/>
 
     </div>
     <input id="newtab-customize-button" type="button" title="&newtab.customize.title;"/>
   </div>
 
   <xul:script type="text/javascript;version=1.8"
-              src="chrome://browser/content/searchSuggestionUI.js"/>
+              src="chrome://browser/content/contentSearchUI.js"/>
   <xul:script type="text/javascript;version=1.8"
               src="chrome://browser/content/newtab/newTab.js"/>
 </xul:window>
--- a/browser/base/content/newtab/search.js
+++ b/browser/base/content/newtab/search.js
@@ -1,261 +1,15 @@
 #ifdef 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/. */
 #endif
 
 let gSearch = {
-
-  currentEngineName: null,
-
-  get useNewUI() {
-    let newUI = Services.prefs.getBoolPref("browser.search.showOneOffButtons");
-    delete this.useNewUI;
-    this.useNewUI = newUI;
-    return newUI;
-  },
-
   init: function () {
-    for (let idSuffix of this._nodeIDSuffixes) {
-      this._nodes[idSuffix] =
-        document.getElementById("newtab-search-" + idSuffix);
-    }
-
-    if (this.useNewUI) {
-      this._nodes.logo.classList.add("magnifier");
-    }
-
-    window.addEventListener("ContentSearchService", this);
-    this._send("GetState");
-  },
-
-  showPanel: function () {
-    let panel = this._nodes.panel;
-    let logo = this._nodes.logo;
-    panel.hidden = false;
-    panel.openPopup(logo);
-    logo.setAttribute("active", "true");
-    panel.addEventListener("popuphidden", function onHidden() {
-      panel.removeEventListener("popuphidden", onHidden);
-      panel.hidden = true;
-      logo.removeAttribute("active");
-    });
-  },
-
-  search: function (event) {
-    if (event) {
-      event.preventDefault();
-    }
-    let searchText = this._nodes.text;
-    let searchStr = searchText.value;
-    if (this.currentEngineName && searchStr.length) {
-      let useNewTab = event && event.button == 1;
-      let eventData = {
-        engineName: this.currentEngineName,
-        searchString: searchStr,
-        whence: "newtab",
-        originalEvent: {
-          target: {
-            ownerDocument: null
-          },
-          shiftKey: event.shiftKey,
-          ctrlKey: event.ctrlKey,
-          metaKey: event.metaKey,
-          altKey: event.altKey,
-          button: event.button,
-        },
-      }
-
-      if (searchText.hasAttribute("selection-index")) {
-        eventData.selection = {
-          index: searchText.getAttribute("selection-index"),
-          kind: searchText.getAttribute("selection-kind")
-        };
-      }
-
-      this._send("Search", eventData);
-    }
-    this._suggestionController.addInputValueToFormHistory();
-  },
-
-  manageEngines: function () {
-    this._nodes.panel.hidePopup();
-    this._send("ManageEngines");
-  },
-
-  handleEvent: function (event) {
-    let methodName = "on" + event.detail.type;
-    if (this.hasOwnProperty(methodName)) {
-      this[methodName](event.detail.data);
-    }
-  },
-
-  onState: function (data) {
-    this._newEngines = data.engines;
-    this._setCurrentEngine(data.currentEngine);
-    this._initWhenInitalStateReceived();
-  },
-
-  onCurrentState: function (data) {
-    if (this._initialStateReceived) {
-      this._newEngines = data.engines;
-      this._setCurrentEngine(data.currentEngine);
-    }
-  },
-
-  onCurrentEngine: function (engineName) {
-    if (this._initialStateReceived) {
-      this._nodes.panel.hidePopup();
-      this._setCurrentEngine(engineName);
-    }
-  },
-
-  onFocusInput: function () {
-    this._nodes.text.focus();
-  },
-
-  _nodeIDSuffixes: [
-    "form",
-    "logo",
-    "manage",
-    "panel",
-    "text",
-  ],
-
-  _nodes: {},
-
-  _initWhenInitalStateReceived: function () {
-    this._nodes.form.addEventListener("submit", e => this.search(e));
-    this._nodes.logo.addEventListener("click", e => this.showPanel());
-    this._nodes.manage.addEventListener("click", e => this.manageEngines());
-    this._nodes.panel.addEventListener("popupshowing", e => this._setUpPanel());
-    this._initialStateReceived = true;
-    this._initWhenInitalStateReceived = function () {};
-  },
-
-  _send: function (type, data=null) {
-    window.dispatchEvent(new CustomEvent("ContentSearchClient", {
-      detail: {
-        type: type,
-        data: data,
-      },
-    }));
-  },
-
-  _setUpPanel: function () {
-    // The new search UI only contains the "manage" engine entry in the panel
-    if (this.useNewUI) {
-      return;
-    }
-
-    // Build the panel if necessary.
-    if (this._newEngines) {
-      this._buildPanel(this._newEngines);
-      delete this._newEngines;
-    }
-
-    // Set the selected states of the engines.
-    let panel = this._nodes.panel;
-    for (let box of panel.childNodes) {
-      if (box.getAttribute("engine") == this.currentEngineName) {
-        box.setAttribute("selected", "true");
-      }
-      else {
-        box.removeAttribute("selected");
-      }
-    }
-  },
-
-  _buildPanel: function (engines) {
-    let panel = this._nodes.panel;
-
-    // Empty the panel except for the Manage Engines row.
-    let i = 0;
-    while (i < panel.childNodes.length) {
-      let node = panel.childNodes[i];
-      if (node != this._nodes.manage) {
-        panel.removeChild(node);
-      }
-      else {
-        i++;
-      }
-    }
-
-    // Add all the engines.
-    for (let engine of engines) {
-      panel.insertBefore(this._makePanelEngine(panel, engine),
-                         this._nodes.manage);
-    }
-  },
-
-  // Converts favicon array buffer into data URI of the right size and dpi.
-  _getFaviconURIFromBuffer: function (buffer) {
-    let blob = new Blob([buffer]);
-    let dpiSize = Math.round(16 * window.devicePixelRatio);
-    let sizeStr = dpiSize + "," + dpiSize;
-    return URL.createObjectURL(blob) + "#-moz-resolution=" + sizeStr;
-  },
-
-  _makePanelEngine: function (panel, engine) {
-    let box = document.createElementNS(XUL_NAMESPACE, "hbox");
-    box.className = "newtab-search-panel-engine";
-    box.setAttribute("engine", engine.name);
-
-    box.addEventListener("click", () => {
-      this._send("SetCurrentEngine", engine.name);
-      panel.hidePopup();
-      this._nodes.text.focus();
-    });
-
-    let image = document.createElementNS(XUL_NAMESPACE, "image");
-    if (engine.iconBuffer) {
-      let uri = this._getFaviconURIFromBuffer(engine.iconBuffer);
-      image.setAttribute("src", uri);
-    }
-    box.appendChild(image);
-
-    let label = document.createElementNS(XUL_NAMESPACE, "label");
-    label.setAttribute("value", engine.name);
-    box.appendChild(label);
-
-    return box;
-  },
-
-  _setCurrentEngine: function (engine) {
-    this.currentEngineName = engine.name;
-
-    if (!this.useNewUI) {
-      let type = "";
-      let uri;
-      let logoBuf = window.devicePixelRatio >= 2 ?
-                    engine.logo2xBuffer || engine.logoBuffer :
-                    engine.logoBuffer || engine.logo2xBuffer;
-      if (logoBuf) {
-        uri = URL.createObjectURL(new Blob([logoBuf]));
-        type = "logo";
-      }
-      else if (engine.iconBuffer) {
-        uri = this._getFaviconURIFromBuffer(engine.iconBuffer);
-        type = "favicon";
-      }
-      this._nodes.logo.setAttribute("type", type);
-
-      if (uri) {
-        this._nodes.logo.style.backgroundImage = "url(" + uri + ")";
-      }
-      else {
-        this._nodes.logo.style.backgroundImage = "";
-      }
-      this._nodes.text.placeholder = engine.placeholder;
-    }
-
-    // Set up the suggestion controller.
-    if (!this._suggestionController) {
-      let parent = document.getElementById("newtab-scrollbox");
-      this._suggestionController =
-        new SearchSuggestionUIController(this._nodes.text, parent,
-                                         event => this.search(event));
-    }
-    this._suggestionController.engineName = engine.name;
+    document.getElementById("newtab-search-submit")
+            .addEventListener("click", e => this._contentSearchController.search(e));
+    let textbox = document.getElementById("newtab-search-text");
+    this._contentSearchController =
+      new ContentSearchUIController(textbox, textbox.parentNode, "newtab", "newtab");
   },
 };
--- a/browser/base/content/tab-content.js
+++ b/browser/base/content/tab-content.js
@@ -109,22 +109,16 @@ let AboutHomeListener = {
   handleEvent: function(aEvent) {
     if (!this.isAboutHome) {
       return;
     }
     switch (aEvent.type) {
       case "AboutHomeLoad":
         this.onPageLoad();
         break;
-      case "AboutHomeSearchEvent":
-        this.onSearch(aEvent);
-        break;
-      case "AboutHomeSearchPanel":
-        this.onOpenSearchPanel(aEvent);
-        break;
       case "click":
         this.onClick(aEvent);
         break;
       case "pagehide":
         this.onPageHide(aEvent);
         break;
     }
   },
@@ -132,57 +126,49 @@ let AboutHomeListener = {
   receiveMessage: function(aMessage) {
     if (!this.isAboutHome) {
       return;
     }
     switch (aMessage.name) {
       case "AboutHome:Update":
         this.onUpdate(aMessage.data);
         break;
-      case "AboutHome:FocusInput":
-        this.onFocusInput();
-        break;
     }
   },
 
   onUpdate: function(aData) {
     let doc = content.document;
     if (aData.showRestoreLastSession && !PrivateBrowsingUtils.isContentWindowPrivate(content))
       doc.getElementById("launcher").setAttribute("session", "true");
 
     // Inject search engine and snippets URL.
     let docElt = doc.documentElement;
-    // set the following attributes BEFORE searchEngineName, which triggers to
-    // show the snippets when it's set.
+    // Set snippetsVersion last, which triggers to show the snippets when it's set.
     docElt.setAttribute("snippetsURL", aData.snippetsURL);
     if (aData.showKnowYourRights)
       docElt.setAttribute("showKnowYourRights", "true");
     docElt.setAttribute("snippetsVersion", aData.snippetsVersion);
-    docElt.setAttribute("searchEngineName", aData.defaultEngineName);
   },
 
   onPageLoad: function() {
     let doc = content.document;
     if (doc.documentElement.hasAttribute("hasBrowserHandlers")) {
       return;
     }
 
     doc.documentElement.setAttribute("hasBrowserHandlers", "true");
     addMessageListener("AboutHome:Update", this);
-    addMessageListener("AboutHome:FocusInput", this);
     addEventListener("click", this, true);
     addEventListener("pagehide", this, true);
 
     if (!Services.prefs.getBoolPref("browser.search.showOneOffButtons")) {
       doc.documentElement.setAttribute("searchUIConfiguration", "oldsearchui");
     }
 
     sendAsyncMessage("AboutHome:RequestUpdate");
-    doc.addEventListener("AboutHomeSearchEvent", this, true, true);
-    doc.addEventListener("AboutHomeSearchPanel", this, true, true);
   },
 
   onClick: function(aEvent) {
     if (!aEvent.isTrusted || // Don't trust synthetic events
         aEvent.button == 2 || aEvent.target.localName != "button") {
       return;
     }
 
@@ -223,49 +209,30 @@ let AboutHomeListener = {
 
       case "sync":
         sendAsyncMessage("AboutHome:Sync");
         break;
 
       case "settings":
         sendAsyncMessage("AboutHome:Settings");
         break;
-
-      case "searchIcon":
-        sendAsyncMessage("AboutHome:OpenSearchPanel", null, { anchor: originalTarget });
-        break;
     }
   },
 
   onPageHide: function(aEvent) {
     if (aEvent.target.defaultView.frameElement) {
       return;
     }
     removeMessageListener("AboutHome:Update", this);
     removeEventListener("click", this, true);
     removeEventListener("pagehide", this, true);
     if (aEvent.target.documentElement) {
       aEvent.target.documentElement.removeAttribute("hasBrowserHandlers");
     }
   },
-
-  onSearch: function(aEvent) {
-    sendAsyncMessage("AboutHome:Search", { searchData: aEvent.detail });
-  },
-
-  onOpenSearchPanel: function(aEvent) {
-    sendAsyncMessage("AboutHome:OpenSearchPanel");
-  },
-
-  onFocusInput: function () {
-    let searchInput = content.document.getElementById("searchText");
-    if (searchInput) {
-      searchInput.focus();
-    }
-  },
 };
 AboutHomeListener.init(this);
 
 let AboutPrivateBrowsingListener = {
   init(chromeGlobal) {
     chromeGlobal.addEventListener("AboutPrivateBrowsingOpenWindow", this,
                                   false, true);
   },
--- a/browser/base/jar.mn
+++ b/browser/base/jar.mn
@@ -139,18 +139,18 @@ browser.jar:
 #endif
         content/browser/safeMode.css                  (content/safeMode.css)
         content/browser/safeMode.js                   (content/safeMode.js)
         content/browser/safeMode.xul                  (content/safeMode.xul)
 *       content/browser/sanitize.js                   (content/sanitize.js)
 *       content/browser/sanitize.xul                  (content/sanitize.xul)
 *       content/browser/sanitizeDialog.js             (content/sanitizeDialog.js)
         content/browser/sanitizeDialog.css            (content/sanitizeDialog.css)
-        content/browser/searchSuggestionUI.js         (content/searchSuggestionUI.js)
-        content/browser/searchSuggestionUI.css        (content/searchSuggestionUI.css)
+        content/browser/contentSearchUI.js            (content/contentSearchUI.js)
+        content/browser/contentSearchUI.css           (content/contentSearchUI.css)
         content/browser/tabbrowser.css                (content/tabbrowser.css)
         content/browser/tabbrowser.xml                (content/tabbrowser.xml)
         content/browser/urlbarBindings.xml            (content/urlbarBindings.xml)
 *       content/browser/utilityOverlay.js             (content/utilityOverlay.js)
         content/browser/web-panels.js                 (content/web-panels.js)
 *       content/browser/web-panels.xul                (content/web-panels.xul)
 *       content/browser/baseMenuOverlay.xul           (content/baseMenuOverlay.xul)
 *       content/browser/nsContextMenu.js              (content/nsContextMenu.js)
--- a/browser/locales/en-US/chrome/browser/aboutHome.dtd
+++ b/browser/locales/en-US/chrome/browser/aboutHome.dtd
@@ -6,17 +6,17 @@
 %brandDTD;
 <!ENTITY % syncBrandDTD SYSTEM "chrome://browser/locale/syncBrand.dtd">
 %syncBrandDTD;
 
 <!-- These strings are used in the about:home page -->
 
 <!ENTITY abouthome.pageTitle "&brandFullName; Start Page">
 
-<!ENTITY abouthome.searchEngineButton.label "Search">
+<!ENTITY abouthome.search.placeholder    "Search">
 
 <!-- LOCALIZATION NOTE (abouthome.defaultSnippet1.v1):
      text in <a/> will be linked to the Firefox features page on mozilla.com
 -->
 <!ENTITY abouthome.defaultSnippet1.v1 "Thanks for choosing Firefox! To get the most out of your browser, learn more about the <a>latest features</a>.">
 <!-- LOCALIZATION NOTE (abouthome.defaultSnippet2.v1):
      text in <a/> will be linked to the featured add-ons on addons.mozilla.org
 -->
--- a/browser/locales/en-US/chrome/browser/browser.dtd
+++ b/browser/locales/en-US/chrome/browser/browser.dtd
@@ -416,16 +416,22 @@ These should match what Safari and other
   is a fundamental keybinding and we are maintaining a XP binding so that it is easy
   for people to switch to Linux.
 
  -->
 <!ENTITY searchFocus.commandkey       "k">
 <!ENTITY searchFocus.commandkey2      "e">
 <!ENTITY searchFocusUnix.commandkey   "j">
 
+<!-- LOCALIZATION NOTE (contentSearchInput.label, contentSearchSubmit.label):
+     These are set as the aria-label attribute for the search input box and
+     submit button in the in-content search UI, to be used by screen readers. -->
+<!ENTITY contentSearchInput.label     "Search query">
+<!ENTITY contentSearchSubmit.label    "Submit search">
+
 <!-- LOCALIZATION NOTE (searchFor.label, searchWith.label):
      These two strings are used to build the header above the list of one-click
      search providers:  "Search for <used typed keywords> with:" -->
 <!ENTITY searchFor.label              "Search for ">
 <!ENTITY searchWith.label             " with:">
 
 <!-- LOCALIZATION NOTE (search.label, searchAfter.label):
      This string is used to build the header above the list of one-click search
--- a/browser/locales/en-US/chrome/browser/search.properties
+++ b/browser/locales/en-US/chrome/browser/search.properties
@@ -27,8 +27,20 @@ cmd_showSuggestions_accesskey=S
 # a search engine offered by a web page. Each engine is displayed as a
 # menuitem at the bottom of the search panel.
 cmd_addFoundEngine=Add "%S"
 # LOCALIZATION NOTE (cmd_addFoundEngineMenu): When more than 5 engines
 # are offered by a web page, instead of listing all of them in the
 # search panel using the cmd_addFoundEngine string, they will be
 # grouped in a submenu using cmd_addFoundEngineMenu as a label.
 cmd_addFoundEngineMenu=Add search engine
+
+# LOCALIZATION NOTE (searchFor, searchWith):
+# These two strings are used to build the header above the list of one-click
+# search providers:  "Search for <user-typed keywords> with:"
+searchFor=Search for 
+searchWith= with:
+
+# LOCALIZATION NOTE (searchWithHeader):
+# The wording of this string should be as close as possible to
+# searchFor and searchWith. This string will be used instead of
+# them when the user has not typed any keyword.
+searchWithHeader=Search with:
--- a/browser/modules/AboutHome.jsm
+++ b/browser/modules/AboutHome.jsm
@@ -95,36 +95,24 @@ let AboutHome = {
     "AboutHome:Downloads",
     "AboutHome:Bookmarks",
     "AboutHome:History",
     "AboutHome:Apps",
     "AboutHome:Addons",
     "AboutHome:Sync",
     "AboutHome:Settings",
     "AboutHome:RequestUpdate",
-    "AboutHome:Search",
-    "AboutHome:OpenSearchPanel",
   ],
 
   init: function() {
     let mm = Cc["@mozilla.org/globalmessagemanager;1"].getService(Ci.nsIMessageListenerManager);
 
     for (let msg of this.MESSAGES) {
       mm.addMessageListener(msg, this);
     }
-
-    Services.obs.addObserver(this, "browser-search-engine-modified", false);
-  },
-
-  observe: function(aEngine, aTopic, aVerb) {
-    switch (aTopic) {
-      case "browser-search-engine-modified":
-        this.sendAboutHomeData(null);
-        break;
-    }
   },
 
   receiveMessage: function(aMessage) {
     let window = aMessage.target.ownerDocument.defaultView;
 
     switch (aMessage.name) {
       case "AboutHome:RestorePreviousSession":
         let ss = Cc["@mozilla.org/browser/sessionstore;1"].
@@ -174,105 +162,33 @@ let AboutHome = {
 
       case "AboutHome:Settings":
         window.openPreferences();
         break;
 
       case "AboutHome:RequestUpdate":
         this.sendAboutHomeData(aMessage.target);
         break;
-
-      case "AboutHome:Search":
-        let data;
-        try {
-          data = JSON.parse(aMessage.data.searchData);
-        } catch(ex) {
-          Cu.reportError(ex);
-          break;
-        }
-
-        Services.search.init(function(status) {
-          if (!Components.isSuccessCode(status)) {
-            return;
-          }
-
-          let engine = Services.search.currentEngine;
-          if (AppConstants.MOZ_SERVICES_HEALTHREPORT) {
-            window.BrowserSearch.recordSearchInHealthReport(engine, "abouthome", data.selection);
-          }
-
-          // Trigger a search through nsISearchEngine.getSubmission()
-          let submission = engine.getSubmission(data.searchTerms, null, "homepage");
-          let where = window.whereToOpenLink(data.originalEvent);
-
-          // There is a chance that by the time we receive the search message, the
-          // user has switched away from the tab that triggered the search. If,
-          // based on the event, we need to load the search in the same tab that
-          // triggered it (i.e. where == "current"), openUILinkIn will not work
-          // because that tab is no longer the current one. For this case we
-          // manually load the URI in the target browser.
-          if (where == "current") {
-            aMessage.target.loadURIWithFlags(submission.uri.spec,
-                                             Ci.nsIWebNavigation.LOAD_FLAGS_NONE,
-                                             null, null, submission.postData);
-          } else {
-            let params = {
-              postData: submission.postData,
-              inBackground: Services.prefs.getBoolPref("browser.tabs.loadInBackground"),
-            };
-            window.openLinkIn(submission.uri.spec, where, params);
-          }
-          // Used for testing
-          let mm = aMessage.target.messageManager;
-          mm.sendAsyncMessage("AboutHome:SearchTriggered", aMessage.data.searchData);
-        });
-
-        break;
-
-      case "AboutHome:OpenSearchPanel":
-        let panel = window.document.getElementById("abouthome-search-panel");
-        let anchor = aMessage.objects.anchor;
-        panel.hidden = false;
-        panel.openPopup(anchor);
-        anchor.setAttribute("active", "true");
-        panel.addEventListener("popuphidden", function onHidden() {
-          panel.removeEventListener("popuphidden", onHidden);
-          anchor.removeAttribute("active");
-        });
-        break;
     }
   },
 
   // Send all the chrome-privileged data needed by about:home. This
   // gets re-sent when the search engine changes.
   sendAboutHomeData: function(target) {
     let wrapper = {};
     Components.utils.import("resource:///modules/sessionstore/SessionStore.jsm",
       wrapper);
     let ss = wrapper.SessionStore;
 
     ss.promiseInitialized.then(function() {
-      let deferred = Promise.defer();
-
-      Services.search.init(function (status){
-        if (!Components.isSuccessCode(status)) {
-          deferred.reject(status);
-        } else {
-          deferred.resolve(Services.search.defaultEngine.name);
-        }
-      });
-
-      return deferred.promise;
-    }).then(function(engineName) {
       let data = {
         showRestoreLastSession: ss.canRestoreLastSession,
         snippetsURL: AboutHomeUtils.snippetsURL,
         showKnowYourRights: AboutHomeUtils.showKnowYourRights,
         snippetsVersion: AboutHomeUtils.snippetsVersion,
-        defaultEngineName: engineName
       };
 
       if (AboutHomeUtils.showKnowYourRights) {
         // Set pref to indicate we've shown the notification.
         let currentVersion = Services.prefs.getIntPref("browser.rights.version");
         Services.prefs.setBoolPref("browser.rights." + currentVersion + ".shown", true);
       }
 
@@ -280,19 +196,10 @@ let AboutHome = {
         target.messageManager.sendAsyncMessage("AboutHome:Update", data);
       } else {
         let mm = Cc["@mozilla.org/globalmessagemanager;1"].getService(Ci.nsIMessageListenerManager);
         mm.broadcastAsyncMessage("AboutHome:Update", data);
       }
     }).then(null, function onError(x) {
       Cu.reportError("Error in AboutHome.sendAboutHomeData: " + x);
     });
-  },
-
-  /**
-   * Focuses the search input in the page with the given message manager.
-   * @param  messageManager
-   *         The MessageManager object of the selected browser.
-   */
-  focusInput: function (messageManager) {
-    messageManager.sendAsyncMessage("AboutHome:FocusInput");
   }
 };
--- a/browser/modules/ContentSearch.jsm
+++ b/browser/modules/ContentSearch.jsm
@@ -40,25 +40,28 @@ const MAX_SUGGESTIONS = 6;
  *     Adds an entry to the search form history.
  *     data: the entry, a string
  *   GetSuggestions
  *     Retrieves an array of search suggestions given a search string.
  *     data: { engineName, searchString, [remoteTimeout] }
  *   GetState
  *     Retrieves the current search engine state.
  *     data: null
+ *   GetStrings
+ *     Retrieves localized search UI strings.
+ *     data: null
  *   ManageEngines
  *     Opens the search engine management window.
  *     data: null
  *   RemoveFormHistoryEntry
  *     Removes an entry from the search form history.
  *     data: the entry, a string
  *   Search
  *     Performs a search.
- *     data: { engineName, searchString, whence }
+ *     data: { engineName, searchString, healthReportKey, searchPurpose }
  *   SetCurrentEngine
  *     Sets the current engine.
  *     data: the name of the engine
  *   SpeculativeConnect
  *     Speculatively connects to an engine.
  *     data: the name of the engine
  *
  * Outbound messages have the following types:
@@ -67,16 +70,19 @@ const MAX_SUGGESTIONS = 6;
  *     Broadcast when the current engine changes.
  *     data: see _currentEngineObj
  *   CurrentState
  *     Broadcast when the current search state changes.
  *     data: see _currentStateObj
  *   State
  *     Sent in reply to GetState.
  *     data: see _currentStateObj
+ *   Strings
+ *     Sent in reply to GetStrings
+ *     data: Object containing string names and values for the current locale.
  *   Suggestions
  *     Sent in reply to GetSuggestions.
  *     data: see _onMessageGetSuggestions
  */
 
 this.ContentSearch = {
 
   // Inbound events are queued and processed in FIFO order instead of handling
@@ -93,19 +99,34 @@ this.ContentSearch = {
   _destroyedPromise: null,
 
   init: function () {
     Cc["@mozilla.org/globalmessagemanager;1"].
       getService(Ci.nsIMessageListenerManager).
       addMessageListener(INBOUND_MESSAGE, this);
     Services.obs.addObserver(this, "browser-search-engine-modified", false);
     Services.obs.addObserver(this, "shutdown-leaks-before-check", false);
+    Services.prefs.addObserver("browser.search.hiddenOneOffs", this, false);
     this._stringBundle = Services.strings.createBundle("chrome://global/locale/autocomplete.properties");
   },
 
+  get searchSuggestionUIStrings() {
+    if (this._searchSuggestionUIStrings) {
+      return this._searchSuggestionUIStrings;
+    }
+    this._searchSuggestionUIStrings = {};
+    let searchBundle = Services.strings.createBundle("chrome://browser/locale/search.properties");
+    let stringNames = ["searchHeader", "searchPlaceholder", "searchFor",
+                       "searchWith", "searchWithHeader"];
+    for (let name of stringNames) {
+      this._searchSuggestionUIStrings[name] = searchBundle.GetStringFromName(name);
+    }
+    return this._searchSuggestionUIStrings;
+  },
+
   destroy: function () {
     if (this._destroyedPromise) {
       return this._destroyedPromise;
     }
 
     Cc["@mozilla.org/globalmessagemanager;1"].
       getService(Ci.nsIMessageListenerManager).
       removeMessageListener(INBOUND_MESSAGE, this);
@@ -148,16 +169,17 @@ this.ContentSearch = {
       type: "Message",
       data: msg,
     });
     this._processEventQueue();
   },
 
   observe: function (subj, topic, data) {
     switch (topic) {
+    case "nsPref:changed":
     case "browser-search-engine-modified":
       this._eventQueue.push({
         type: "Observe",
         data: data,
       });
       this._processEventQueue();
       break;
     case "shutdown-leaks-before-check":
@@ -196,24 +218,29 @@ this.ContentSearch = {
   }),
 
   _onMessageGetState: function (msg, data) {
     return this._currentStateObj().then(state => {
       this._reply(msg, "State", state);
     });
   },
 
+  _onMessageGetStrings: function (msg, data) {
+    this._reply(msg, "Strings", this.searchSuggestionUIStrings);
+  },
+
   _onMessageSearch: function (msg, data) {
     this._ensureDataHasProperties(data, [
       "engineName",
       "searchString",
-      "whence",
+      "healthReportKey",
+      "searchPurpose",
     ]);
     let engine = Services.search.getEngineByName(data.engineName);
-    let submission = engine.getSubmission(data.searchString, "", data.whence);
+    let submission = engine.getSubmission(data.searchString, "", data.searchPurpose);
     let browser = msg.target;
     let win;
     try {
       win = browser.ownerDocument.defaultView;
     }
     catch (err) {
       // The browser may have been closed between the time its content sent the
       // message and the time we handle it.  In that case, trying to call any
@@ -234,17 +261,17 @@ this.ContentSearch = {
                                submission.postData);
     } else {
       let params = {
         postData: submission.postData,
         inBackground: Services.prefs.getBoolPref("browser.tabs.loadInBackground"),
       };
       win.openUILinkIn(submission.uri.spec, where, params);
     }
-    win.BrowserSearch.recordSearchInHealthReport(engine, data.whence,
+    win.BrowserSearch.recordSearchInHealthReport(engine, data.healthReportKey,
                                                  data.selection || null);
     return Promise.resolve();
   },
 
   _onMessageSetCurrentEngine: function (msg, data) {
     Services.search.currentEngine = Services.search.getEngineByName(data);
     return Promise.resolve();
   },
@@ -409,17 +436,22 @@ this.ContentSearch = {
     }];
   },
 
   _currentStateObj: Task.async(function* () {
     let state = {
       engines: [],
       currentEngine: yield this._currentEngineObj(),
     };
+    let pref = Services.prefs.getCharPref("browser.search.hiddenOneOffs");
+    let hiddenList = pref ? pref.split(",") : [];
     for (let engine of Services.search.getVisibleEngines()) {
+      if (hiddenList.indexOf(engine.name) != -1) {
+        continue;
+      }
       let uri = engine.getIconURLBySize(16, 16);
       state.engines.push({
         name: engine.name,
         iconBuffer: yield this._arrayBufferFromDataURI(uri),
       });
     }
     return state;
   }),
--- a/browser/themes/linux/jar.mn
+++ b/browser/themes/linux/jar.mn
@@ -88,16 +88,18 @@ browser.jar:
   skin/classic/browser/reload-stop-go@2x.png
   skin/classic/browser/searchbar.css
   skin/classic/browser/search-pref.png                      (../shared/search/search-pref.png)
   skin/classic/browser/search-indicator.png                 (../shared/search/search-indicator.png)
   skin/classic/browser/search-engine-placeholder.png        (../shared/search/search-engine-placeholder.png)
   skin/classic/browser/badge-add-engine.png                 (../shared/search/badge-add-engine.png)
   skin/classic/browser/search-indicator-badge-add.png       (../shared/search/search-indicator-badge-add.png)
   skin/classic/browser/search-history-icon.svg              (../shared/search/history-icon.svg)
+  skin/classic/browser/search-indicator-magnifying-glass.svg   (../shared/search/search-indicator-magnifying-glass.svg)
+  skin/classic/browser/search-arrow-go.svg                  (../shared/search/search-arrow-go.svg)
   skin/classic/browser/Security-broken.png
   skin/classic/browser/setDesktopBackground.css
   skin/classic/browser/slowStartup-16.png
   skin/classic/browser/theme-switcher-icon.png              (../shared/theme-switcher-icon.png)
   skin/classic/browser/Toolbar.png
   skin/classic/browser/Toolbar-inverted.png
   skin/classic/browser/Toolbar-small.png
   skin/classic/browser/undoCloseTab.png                        (../shared/undoCloseTab.png)
--- a/browser/themes/osx/jar.mn
+++ b/browser/themes/osx/jar.mn
@@ -119,16 +119,18 @@ browser.jar:
   skin/classic/browser/search-indicator@2x.png                 (../shared/search/search-indicator@2x.png)
   skin/classic/browser/search-engine-placeholder.png           (../shared/search/search-engine-placeholder.png)
   skin/classic/browser/search-engine-placeholder@2x.png        (../shared/search/search-engine-placeholder@2x.png)
   skin/classic/browser/badge-add-engine.png                    (../shared/search/badge-add-engine.png)
   skin/classic/browser/badge-add-engine@2x.png                 (../shared/search/badge-add-engine@2x.png)
   skin/classic/browser/search-indicator-badge-add.png          (../shared/search/search-indicator-badge-add.png)
   skin/classic/browser/search-indicator-badge-add@2x.png       (../shared/search/search-indicator-badge-add@2x.png)
   skin/classic/browser/search-history-icon.svg                 (../shared/search/history-icon.svg)
+  skin/classic/browser/search-indicator-magnifying-glass.svg   (../shared/search/search-indicator-magnifying-glass.svg)
+  skin/classic/browser/search-arrow-go.svg                     (../shared/search/search-arrow-go.svg)
   skin/classic/browser/slowStartup-16.png
   skin/classic/browser/theme-switcher-icon.png                 (../shared/theme-switcher-icon.png)
   skin/classic/browser/theme-switcher-icon@2x.png              (../shared/theme-switcher-icon@2x.png)
   skin/classic/browser/Toolbar.png
   skin/classic/browser/Toolbar@2x.png
   skin/classic/browser/Toolbar-inverted.png
   skin/classic/browser/Toolbar-inverted@2x.png
   skin/classic/browser/toolbarbutton-dropmarker.png
new file mode 100644
--- /dev/null
+++ b/browser/themes/shared/search/search-arrow-go.svg
@@ -0,0 +1,22 @@
+<?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/. -->
+<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="16" height="16" viewBox="0 0 16 16">
+  <style>
+    use:not(:target) {
+      display: none;
+    }
+    use {
+      fill: #616366;
+    }
+    use[id$="-inverted"] {
+      fill: #fff;
+    }
+  </style>
+  <defs>
+    <path id="search-arrow-go-glyph" d="M1,7v2.2C1,9.8,1.4,10,2,10h7.5l-3,3.1c-0.4,0.3-0.4,1,0,1.4l0.8,0.8 c0.4,0.4,1,0.4,1.4,0l6.6-6.6c0.4-0.4,0.4-1,0-1.4L8.7,0.7c-0.4-0.4-1-0.4-1.4,0L6.5,1.6C6.1,2,6.1,2.6,6.5,3l3,3H2C1.4,6,1,6.4,1,7z"/>
+  </defs>
+  <use id="search-arrow-go" xlink:href="#search-arrow-go-glyph"/>
+  <use id="search-arrow-go-inverted" xlink:href="#search-arrow-go-glyph"/>
+</svg>
new file mode 100644
--- /dev/null
+++ b/browser/themes/shared/search/search-indicator-magnifying-glass.svg
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="UTF-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/. -->
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
+  <path fill="#808080" d="M21.7,20.3l-1.4,1.4l-5.4-5.4c-1.3,1-3,1.7-4.9,1.7 c-4.4,0-8-3.6-8-8c0-4.4,3.6-8,8-8c4.4,0,8,3.6,8,8c0,1.8-0.6,3.5-1.7,4.9L21.7,20.3z M10,4c-3.3,0-6,2.7-6,6s2.7,6,6,6s6-2.7,6-6 S13.3,4,10,4z"/>
+</svg>
--- a/browser/themes/windows/jar.mn
+++ b/browser/themes/windows/jar.mn
@@ -111,16 +111,18 @@ browser.jar:
         skin/classic/browser/search-indicator@2x.png                 (../shared/search/search-indicator@2x.png)
         skin/classic/browser/search-engine-placeholder.png           (../shared/search/search-engine-placeholder.png)
         skin/classic/browser/search-engine-placeholder@2x.png        (../shared/search/search-engine-placeholder@2x.png)
         skin/classic/browser/badge-add-engine.png                    (../shared/search/badge-add-engine.png)
         skin/classic/browser/badge-add-engine@2x.png                 (../shared/search/badge-add-engine@2x.png)
         skin/classic/browser/search-indicator-badge-add.png          (../shared/search/search-indicator-badge-add.png)
         skin/classic/browser/search-indicator-badge-add@2x.png       (../shared/search/search-indicator-badge-add@2x.png)
         skin/classic/browser/search-history-icon.svg                 (../shared/search/history-icon.svg)
+        skin/classic/browser/search-indicator-magnifying-glass.svg   (../shared/search/search-indicator-magnifying-glass.svg)
+        skin/classic/browser/search-arrow-go.svg                     (../shared/search/search-arrow-go.svg)
         skin/classic/browser/setDesktopBackground.css
         skin/classic/browser/slowStartup-16.png
         skin/classic/browser/theme-switcher-icon.png                 (../shared/theme-switcher-icon.png)
         skin/classic/browser/Toolbar.png
         skin/classic/browser/Toolbar@2x.png
         skin/classic/browser/Toolbar-aero.png
         skin/classic/browser/Toolbar-aero@2x.png
         skin/classic/browser/Toolbar-inverted.png