Bug 1181078 - Implement new awesomebar popup design. r=mak
authorDrew Willcoxon <adw@mozilla.com>
Tue, 05 Apr 2016 18:56:54 -0700
changeset 291841 061165ac1ff9e076ec51ea268878efa751173511
parent 291840 5f46f7e70d1f8565ce22d6eb32cb7c7873fecce5
child 291842 7f54e6fc8add68c07c781e788182c4f9427aaa86
push id30147
push usercbook@mozilla.com
push dateWed, 06 Apr 2016 09:59:43 +0000
treeherdermozilla-central@68c0b7d6f16c [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmak
bugs1181078
milestone48.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 1181078 - Implement new awesomebar popup design. r=mak Based on an earlier patch by Marco Bonardo <mbonardo@mozilla.com>. MozReview-Commit-ID: 7S0OoTGivbC
browser/base/content/browser.css
browser/base/content/browser.xul
browser/base/content/test/general/browser_action_keyword.js
browser/base/content/test/general/browser_action_keyword_override.js
browser/base/content/test/general/browser_autocomplete_no_title.js
browser/base/content/test/general/browser_autocomplete_tag_star_visibility.js
browser/base/content/test/general/browser_search_favicon.js
browser/base/content/test/general/browser_urlbarDecode.js
browser/base/content/urlbarBindings.xml
browser/themes/linux/browser.css
browser/themes/linux/devedition.css
browser/themes/osx/browser.css
browser/themes/osx/devedition.css
browser/themes/shared/jar.inc.mn
browser/themes/shared/urlbar-star.svg
browser/themes/shared/urlbar-tab.svg
browser/themes/windows/browser.css
browser/themes/windows/devedition.css
toolkit/components/places/UnifiedComplete.js
toolkit/components/places/tests/unifiedcomplete/head_autocomplete.js
toolkit/components/places/tests/unifiedcomplete/test_searchEngine_restyle.js
toolkit/content/autocomplete.css
toolkit/content/tests/chrome/test_autocomplete_emphasis.xul
toolkit/content/widgets/autocomplete.xml
toolkit/content/xul.css
toolkit/locales/en-US/chrome/global/autocomplete.properties
toolkit/themes/linux/global/autocomplete.css
toolkit/themes/osx/global/autocomplete.css
toolkit/themes/windows/global/autocomplete.css
toolkit/themes/windows/global/jar.mn
--- a/browser/base/content/browser.css
+++ b/browser/base/content/browser.css
@@ -442,35 +442,39 @@ toolbar:not(#TabsToolbar) > #personal-bo
   display: none;
 }
 
 /* ::::: location bar ::::: */
 #urlbar {
   -moz-binding: url(chrome://browser/content/urlbarBindings.xml#urlbar);
 }
 
-.ac-url-text:-moz-locale-dir(rtl),
-.ac-title:-moz-locale-dir(rtl) > description {
+/* Always show URLs LTR. */
+.ac-url-text:-moz-locale-dir(rtl) {
   direction: ltr !important;
 }
 
-/* For results that are actions, their description text is shown instead of
-   the URL - this needs to follow the locale's direction, unlike URLs. */
-panel:not([noactions]) > richlistbox > richlistitem.overridable-action:-moz-locale-dir(rtl) > .ac-url-box {
-  direction: rtl;
-}
-
-panel[noactions] > richlistbox > richlistitem.overridable-action > .ac-url-box > .ac-url > .ac-action-text,
-panel[noactions] > richlistbox > richlistitem.overridable-action > .ac-url-box > .ac-action-icon {
+/* For non-action items, hide the action text; for action items, hide the URL
+   text. */
+.ac-url[actiontype],
+.ac-action:not([actiontype]) {
   visibility: collapse;
 }
 
-panel[noactions] > richlistbox > richlistitem.overridable-action > .ac-url-box > .ac-url > .ac-url-text {
+/* For action items in a noactions popup, show the URL text and hide the action
+   text and type icon. */
+#PopupAutoCompleteRichResult[noactions] > richlistbox > richlistitem.overridable-action > .ac-url {
   visibility: visible;
 }
+#PopupAutoCompleteRichResult[noactions] > richlistbox > richlistitem.overridable-action > .ac-action {
+  visibility: collapse;
+}
+#PopupAutoCompleteRichResult[noactions] > richlistbox > richlistitem.overridable-action > .ac-type-icon {
+  list-style-image: none;
+}
 
 #urlbar:not([actiontype="switchtab"]) > #urlbar-display-box {
   display: none;
 }
 
 #PopupAutoComplete {
   -moz-binding: url("chrome://browser/content/urlbarBindings.xml#browser-autocomplete-result-popup");
 }
--- a/browser/base/content/browser.xul
+++ b/browser/base/content/browser.xul
@@ -135,17 +135,17 @@
 
     <!-- for search and content formfill/pw manager -->
     <panel type="autocomplete" id="PopupAutoComplete" noautofocus="true" hidden="true"/>
 
     <!-- for search with one-off buttons -->
     <panel type="autocomplete" id="PopupSearchAutoComplete" noautofocus="true" hidden="true"/>
 
     <!-- for url bar autocomplete -->
-    <panel type="autocomplete-richlistbox" id="PopupAutoCompleteRichResult" noautofocus="true" hidden="true">
+    <panel type="autocomplete-richlistbox" id="PopupAutoCompleteRichResult" noautofocus="true" hidden="true" flip="none">
 #ifdef NIGHTLY_BUILD
       <hbox id="urlbar-search-footer" flex="1" align="stretch" pack="end">
         <button id="urlbar-search-settings" label="&changeSearchSettings.button;"
                 oncommand="BrowserUITelemetry.countSearchSettingsEvent('urlbar'); openPreferences('paneSearch')"/>
       </hbox>
 #endif
     </panel>
 
--- a/browser/base/content/test/general/browser_action_keyword.js
+++ b/browser/base/content/test/general/browser_action_keyword.js
@@ -44,30 +44,29 @@ add_task(function*() {
   is(result.getAttribute("actiontype"), "keyword", "Expect correct `actiontype` attribute");
   is(result.getAttribute("title"), "example.com", "Expect correct title");
 
   // We need to make a real URI out of this to ensure it's normalised for
   // comparison.
   let uri = NetUtil.newURI(result.getAttribute("url"));
   is(uri.spec, makeActionURI("keyword", {url: "http://example.com/?q=something", input: "keyword something"}).spec, "Expect correct url");
 
-  is_element_visible(result._title, "Title element should be visible");
-  is(result._title.childNodes.length, 1, "Title element should have 1 child");
-  is(result._title.childNodes[0].nodeName, "#text", "That child should be a text node");
-  is(result._title.childNodes[0].data, "example.com", "Node should contain the name of the bookmark");
+  let titleHbox = result._titleText.parentNode.parentNode;
+  ok(titleHbox.classList.contains("ac-title"), "Title hbox element sanity check");
+  is_element_visible(titleHbox, "Title element should be visible");
+  is(result._titleText.textContent, "example.com: something", "Node should contain the name of the bookmark and query");
 
-  is_element_visible(result._extraBox, "Extra element should be visible");
-  is(result._extra.childNodes.length, 1, "Title element should have 1 child");
-  is(result._extra.childNodes[0].nodeName, "span", "That child should be a span node");
-  let span = result._extra.childNodes[0];
-  is(span.childNodes.length, 1, "span element should have 1 child");
-  is(span.childNodes[0].nodeName, "#text", "That child should be a text node");
-  is(span.childNodes[0].data, "something", "Node should contain the query for the keyword");
+  let urlHbox = result._urlText.parentNode.parentNode;
+  ok(urlHbox.classList.contains("ac-url"), "URL hbox element sanity check");
+  is_element_hidden(urlHbox, "URL element should be hidden");
 
-  is_element_hidden(result._url, "URL element should be hidden");
+  let actionHbox = result._actionText.parentNode.parentNode;
+  ok(actionHbox.classList.contains("ac-action"), "Action hbox element sanity check");
+  is_element_visible(actionHbox, "Action element should be visible");
+  is(result._actionText.textContent, "", "Action text should be empty");
 
   // Click on the result
   info("Normal click on result");
   let tabPromise = promiseTabLoadEvent(tab);
   EventUtils.synthesizeMouseAtCenter(result, {});
   yield tabPromise;
   is(tab.linkedBrowser.currentURI.spec, "http://example.com/?q=something", "Tab should have loaded from clicking on result");
 
--- a/browser/base/content/test/general/browser_action_keyword_override.js
+++ b/browser/base/content/test/general/browser_action_keyword_override.js
@@ -15,21 +15,33 @@ add_task(function*() {
   registerCleanupFunction(function* () {
     yield PlacesUtils.bookmarks.remove(bm);
   });
 
   yield promiseAutocompleteResultPopup("keyword search");
   let result = gURLBar.popup.richlistbox.children[0];
 
   info("Before override");
-  is_element_hidden(result._url, "URL element should be hidden");
-  is_element_visible(result._extraBox, "Extra element should be visible");
+  let titleHbox = result._titleText.parentNode.parentNode;
+  ok(titleHbox.classList.contains("ac-title"), "Title hbox element sanity check");
+  is_element_visible(titleHbox, "Title element should be visible");
+
+  let urlHbox = result._urlText.parentNode.parentNode;
+  ok(urlHbox.classList.contains("ac-url"), "URL hbox element sanity check");
+  is_element_hidden(urlHbox, "URL element should be hidden");
+
+  let actionHbox = result._actionText.parentNode.parentNode;
+  ok(actionHbox.classList.contains("ac-action"), "Action hbox element sanity check");
+  is_element_visible(actionHbox, "Action element should be visible");
+  is(result._actionText.textContent, "", "Action text should be empty");
 
   info("During override");
   EventUtils.synthesizeKey("VK_SHIFT" , { type: "keydown" });
-  is_element_hidden(result._url, "URL element should be hidden");
-  is_element_visible(result._extraBox, "Extra element should be visible");
+  is_element_visible(titleHbox, "Title element should be visible");
+  is_element_hidden(urlHbox, "URL element should be hidden");
+  is_element_visible(actionHbox, "Action element should be visible");
+  is(result._actionText.textContent, "", "Action text should be empty");
 
   EventUtils.synthesizeKey("VK_SHIFT" , { type: "keyup" });
 
   gURLBar.popup.hidePopup();
   yield promisePopupHidden(gURLBar.popup);
 });
--- a/browser/base/content/test/general/browser_autocomplete_no_title.js
+++ b/browser/base/content/test/general/browser_autocomplete_no_title.js
@@ -13,14 +13,14 @@ add_task(function*() {
   yield promiseTabLoaded(tab);
 
   let uri = NetUtil.newURI("http://bug1060642.example.com/beards/are/pretty/great");
   yield PlacesTestUtils.addVisits([{uri: uri, title: ""}]);
 
   yield promiseAutocompleteResultPopup("bug1060642");
   ok(gURLBar.popup.richlistbox.children.length > 1, "Should get at least 2 results");
   let result = gURLBar.popup.richlistbox.children[1];
-  is(result._title.textContent, "bug1060642.example.com", "Result title should be as expected");
+  is(result._titleText.textContent, "bug1060642.example.com", "Result title should be as expected");
 
   gURLBar.popup.hidePopup();
   yield promisePopupHidden(gURLBar.popup);
   gBrowser.removeTab(tab);
 });
--- a/browser/base/content/test/general/browser_autocomplete_tag_star_visibility.js
+++ b/browser/base/content/test/general/browser_autocomplete_tag_star_visibility.js
@@ -74,32 +74,34 @@ add_task(function*() {
     },
     input: "^ tagtest5",
     expected: {
       type: "tag",
       typeImageVisible: false,
     },
   }];
 
-
   for (let testcase of testcases) {
     info(`Test case: ${testcase.description}`);
 
     yield addTagItem(testcase.tagName);
     for (let prefName of Object.keys(testcase.prefs)) {
       Services.prefs.setBoolPref(`browser.urlbar.${prefName}`, testcase.prefs[prefName]);
     }
 
     yield promiseAutocompleteResultPopup(testcase.input);
     let result = gURLBar.popup.richlistbox.children[1];
     ok(result && !result.collasped, "Should have result");
 
     is(result.getAttribute("type"), testcase.expected.type, "Result should have expected type");
+
+    let typeIconStyle = window.getComputedStyle(result._typeIcon);
+    let imageURL = typeIconStyle.listStyleImage;
     if (testcase.expected.typeImageVisible) {
-      is_element_visible(result._typeImage, "Type image should be visible");
+      ok(/^url\(.+\)$/.test(imageURL), "Type image should be visible");
     } else {
-      is_element_hidden(result._typeImage, "Type image should be hidden");
+      is(imageURL, "none", "Type image should be hidden");
     }
 
     gURLBar.popup.hidePopup();
     yield promisePopupHidden(gURLBar.popup);
   }
 });
--- a/browser/base/content/test/general/browser_search_favicon.js
+++ b/browser/base/content/test/general/browser_search_favicon.js
@@ -35,25 +35,26 @@ add_task(function*() {
   yield promiseTabLoaded(gBrowser.selectedTab);
 
   // The first autocomplete result has the action searchengine, while
   // the second result is the "search favicon" element.
   yield promiseAutocompleteResultPopup("foo");
   let result = gURLBar.popup.richlistbox.children[1];
 
   isnot(result, null, "Expect a search result");
-  is(result.getAttribute("type"), "search favicon", "Expect correct `type` attribute");
+  is(result.getAttribute("type"), "action searchengine favicon", "Expect correct `type` attribute");
 
-  is_element_visible(result._title, "Title element should be visible");
-  is_element_visible(result._extraBox, "Extra box element should be visible");
+  let titleHbox = result._titleText.parentNode.parentNode;
+  ok(titleHbox.classList.contains("ac-title"), "Title hbox sanity check");
+  is_element_visible(titleHbox, "Title element should be visible");
 
-  is(result._extraBox.pack, "start", "Extra box element should start after the title");
-  let iconElem = result._extraBox.nextSibling;
-  is_element_visible(iconElem,
-                     "The element containing the magnifying glass icon should be visible");
-  ok(iconElem.classList.contains("ac-result-type-keyword"),
-     "That icon should have the same class use for `keyword` results");
+  let urlHbox = result._urlText.parentNode.parentNode;
+  ok(urlHbox.classList.contains("ac-url"), "URL hbox sanity check");
+  is_element_hidden(urlHbox, "URL element should be hidden");
 
-  is_element_visible(result._url, "URL element should be visible");
-  is(result._url.textContent, "Search with SearchEngine");
+  let actionHbox = result._actionText.parentNode.parentNode;
+  ok(actionHbox.classList.contains("ac-action"), "Action hbox sanity check");
+  is_element_hidden(actionHbox, "Action element should be hidden because it is not selected");
+  // \u2014 == em dash
+  is(result._actionText.textContent, "\u2014Search with SearchEngine", "Action text should be as expected");
 
   gBrowser.removeCurrentTab();
 });
--- a/browser/base/content/test/general/browser_urlbarDecode.js
+++ b/browser/base/content/test/general/browser_urlbarDecode.js
@@ -88,10 +88,12 @@ function* checkInput(inputStr) {
   Assert.equal(item.getAttribute("text"), inputStr, "text");
 
   let itemTypeStr = item.getAttribute("type");
   let itemTypes = itemTypeStr.split(" ").sort();
   Assert.equal(itemTypes.toString(),
                ["action", "heuristic", "visiturl"].toString(),
                "type");
 
-  Assert.equal(item._title.textContent, "Visit " + inputStr.replace("\\","/"), "Visible title");
+  Assert.equal(item._titleText.textContent, inputStr.replace("\\","/"), "Visible title");
+  // \u2014 == em dash
+  Assert.equal(item._actionText.textContent, "\u2014Visit", "Visible action");
 }
--- a/browser/base/content/urlbarBindings.xml
+++ b/browser/base/content/urlbarBindings.xml
@@ -1334,16 +1334,51 @@ file, You can obtain one at http://mozil
             this._showSearchSuggestionsNotification();
           }
 
           this._openAutocompletePopup(aInput, aElement);
           ]]>
         </body>
       </method>
 
+      <method name="_openAutocompletePopup">
+        <parameter name="aInput"/>
+        <parameter name="aElement"/>
+        <body><![CDATA[
+          if (this.mPopupOpen) {
+            return;
+          }
+
+          this.mInput = aInput;
+          this.selectedIndex = -1;
+          this.view = aInput.controller.QueryInterface(Components.interfaces.nsITreeView);
+          this.invalidate();
+
+          var rect = window.document.documentElement.getBoundingClientRect();
+          var width = rect.right - rect.left;
+          this.setAttribute("width", width);
+
+          // Adjust the direction of the autocomplete popup list based on the textbox direction, bug 649840
+          var popupDirection = aElement.ownerDocument.defaultView.getComputedStyle(aElement).direction;
+          this.style.direction = popupDirection;
+
+          // Move left margin to the window border.
+          let elementRect = aElement.getBoundingClientRect();
+          this.style.marginLeft = "-" + (elementRect.left - rect.left) + "px";
+
+          // Position the popup below the navbar.  To get the y-coordinate,
+          // which is an offset from the bottom of the input, subtract the
+          // bottom of the navbar from the buttom of the input.
+          let yOffset =
+            document.getElementById("nav-bar").getBoundingClientRect().bottom -
+            aInput.getBoundingClientRect().bottom;
+          this.openPopup(aElement, "after_start", 0, yOffset, false, false);
+        ]]></body>
+      </method>
+
       <method name="_updateFooterVisibility">
         <body>
           <![CDATA[
           this.footer.collapsed = this._matchCount == 0;
           ]]>
         </body>
       </method>
 
--- a/browser/themes/linux/browser.css
+++ b/browser/themes/linux/browser.css
@@ -1091,75 +1091,114 @@ notification[value="translation"] button
 notification[value="translation"] menulist > .menulist-dropmarker {
   display: block;
 }
 
 #treecolAutoCompleteImage {
   max-width : 36px;
 }
 
-.ac-result-type-bookmark,
+#PopupAutoCompleteRichResult {
+  /* The awesomebar popup should open just below the navbar bottom border. */
+  margin-top: 1px;
+}
+
+.autocomplete-richlistbox {
+  padding: 4px;
+}
+
+.autocomplete-richlistitem {
+  height: 30px;
+  min-height: 30px;
+  font: message-box;
+  border-radius: 2px;
+  border: 1px solid transparent;
+}
+
+.autocomplete-richlistitem[selected=true] {
+  background-color: Highlight;
+}
+
+.ac-title {
+  font-size: 14px;
+}
+
+.ac-tags {
+  font-size: 12px;
+}
+
+html|span.ac-tag {
+  background-color: MenuText;
+  color: Menu;
+  border-radius: 2px;
+  border: 1px solid transparent;
+  padding: 0 1px;
+}
+
+.ac-url,
+.ac-action {
+  font-size: 12px;
+  color: -moz-nativehyperlinktext;
+}
+
+.ac-title[selected=true],
+.ac-url[selected=true],
+.ac-action[selected=true] {
+  color: inherit !important;
+}
+
+.ac-tags-text[selected] > html|span.ac-tag {
+  background-color: HighlightText;
+  color: Highlight;
+}
+
+.ac-type-icon[type=bookmark] {
+  list-style-image: url("chrome://browser/skin/urlbar-star.svg#star");
+}
+
+.ac-type-icon[type=bookmark][selected][current] {
+  list-style-image: url("chrome://browser/skin/urlbar-star.svg#star-inverted");
+}
+
 .autocomplete-treebody::-moz-tree-image(bookmark, treecolAutoCompleteImage) {
   list-style-image: url("chrome://browser/skin/places/autocomplete-star.png");
   width: 16px;
   height: 16px;
 }
 
-.ac-result-type-keyword,
-.autocomplete-treebody::-moz-tree-image(keyword, treecolAutoCompleteImage),
-richlistitem[type~="action"][actiontype="searchengine"] > .ac-title-box > .ac-site-icon {
+.ac-type-icon[type=keyword],
+.ac-site-icon[type=searchengine],
+.autocomplete-treebody::-moz-tree-image(keyword, treecolAutoCompleteImage) {
   list-style-image: url(chrome://global/skin/icons/autocomplete-search.svg#search-icon);
-  width: 16px;
-  height: 16px;
 }
 
-.ac-result-type-keyword[selected="true"],
-.autocomplete-treebody::-moz-tree-image(keyword, treecolAutoCompleteImage, selected),
-richlistitem[type~="action"][actiontype="searchengine"][selected="true"] > .ac-title-box > .ac-site-icon {
+.ac-type-icon[type=keyword][selected],
+.ac-site-icon[type=searchengine][selected],
+.autocomplete-treebody::-moz-tree-image(keyword, treecolAutoCompleteImage, selected) {
   list-style-image: url(chrome://global/skin/icons/autocomplete-search.svg#search-icon-inverted);
 }
 
-.ac-result-type-tag,
 .autocomplete-treebody::-moz-tree-image(tag, treecolAutoCompleteImage) {
   list-style-image: url("chrome://browser/skin/places/tag.png");
   width: 16px;
   height: 16px;
 }
 
-.ac-comment,
-#PopupAutoCompleteRichResult > hbox[anonid="search-suggestions-notification"] > description,
-#PopupAutoCompleteRichResult > hbox[anonid="search-suggestions-notification"] > button {
-  font-size: 1.05em;
-}
-
-.ac-extra > .ac-comment {
-  font-size: inherit;
+.ac-type-icon[type=switchtab] {
+  list-style-image: url("chrome://browser/skin/urlbar-tab.svg#tab");
 }
 
-.ac-url-text,
-.ac-action-text {
-  color: -moz-nativehyperlinktext;
-  font-size: 0.9em;
-}
-
-richlistitem[type~="action"][actiontype$="tab"] > .ac-url-box > .ac-action-icon {
-  list-style-image: url("chrome://browser/skin/actionicon-tab.png");
-  padding: 0 3px;
+.ac-type-icon[type=switchtab][selected] {
+  list-style-image: url("chrome://browser/skin/urlbar-tab.svg#tab-inverted");
 }
 
 .autocomplete-treebody::-moz-tree-cell-text(treecolAutoCompleteComment) {
   color: GrayText;
 }
 
-.ac-comment[selected="true"],
-.ac-url-text[selected="true"],
-.ac-action-text[selected="true"] {
-  color: inherit !important;
-}
-
 .autocomplete-treebody::-moz-tree-cell-text(suggesthint, treecolAutoCompleteComment),
 .autocomplete-treebody::-moz-tree-cell-text(suggestfirst, treecolAutoCompleteComment) {
   color: GrayText;
   font-size: smaller;
 }
 
 .autocomplete-treebody::-moz-tree-cell(suggesthint) {
   border-top: 1px solid GrayText;
--- a/browser/themes/linux/devedition.css
+++ b/browser/themes/linux/devedition.css
@@ -94,8 +94,13 @@
 }
 
 /* Fix the bad-looking text-shadow in the sidebar header: */
 .sidebar-header,
 #sidebar-header {
   text-shadow: none;
 }
 
+.ac-type-icon {
+  /* Left-align the type icon in awesomebar popup results with the icon in the
+     urlbar. */
+  -moz-margin-start: 11px;
+}
--- a/browser/themes/osx/browser.css
+++ b/browser/themes/osx/browser.css
@@ -1707,116 +1707,148 @@ toolbar .toolbarbutton-1 > .toolbarbutto
 }
 
 .urlbar-display {
   margin-top: 0;
   margin-bottom: 0;
   color: GrayText;
 }
 
-#PopupAutoCompleteRichResult {
-  margin-top: 2px;
-}
-
 %include ../shared/urlbarSearchSuggestionsNotification.inc.css
 
 /* ----- AUTOCOMPLETE ----- */
 
 #treecolAutoCompleteImage {
   max-width: 36px;
 }
 
-.ac-result-type-bookmark,
+.autocomplete-richlistbox {
+  padding: 4px;
+}
+
+.autocomplete-richlistitem {
+  height: 30px;
+  min-height: 30px;
+  font: message-box;
+  border-radius: 2px;
+  border: 1px solid transparent;
+}
+
+.autocomplete-richlistitem[selected] {
+  background-color: hsl(210, 80%, 52%);
+}
+
+.ac-title {
+  font-size: 14px;
+  color: hsl(0, 0%, 0%);
+}
+
+html|span.ac-emphasize-text-title {
+  color: hsl(0, 0%, 50%);
+}
+
+.ac-tags {
+  font-size: 12px;
+}
+
+html|span.ac-tag {
+  background-color: hsl(216, 0%, 88%);
+  color: hsl(0, 0%, 0%);
+  border-radius: 2px;
+  border: 1px solid transparent;
+  padding: 0 1px;
+}
+
+html|span.ac-emphasize-text-tag {
+  color: hsl(0, 0%, 50%);
+}
+
+.ac-url {
+  font-size: 12px;
+  color: hsl(210, 77%, 47%);
+}
+
+html|span.ac-emphasize-text-url {
+  color: hsl(210, 86%, 64%);
+}
+
+.ac-action {
+  font-size: 12px;
+  color: hsl(178, 100%, 28%);
+}
+
+html|span.ac-title-urlaction-separator {
+  color: hsl(0, 0%, 50%);
+}
+
+.ac-title[selected],
+.ac-url[selected],
+.ac-action[selected],
+.ac-title-text[selected] > html|span.ac-emphasize-text,
+.ac-url-text[selected] > html|span.ac-emphasize-text,
+.ac-action-text[selected] > html|span.ac-emphasize-text,
+.ac-url-text[selected] > html|span.ac-title-urlaction-separator,
+.ac-action-text[selected] > html|span.ac-title-urlaction-separator {
+  color: hsl(0, 0%, 100%);
+}
+
+.ac-tags-text[selected] > html|span.ac-tag {
+  background-color: hsl(0, 0%, 100%);
+  color: hsl(210, 80%, 40%);
+}
+
+.ac-tags-text[selected] > html|span.ac-tag > html|span.ac-emphasize-text-tag {
+  color: hsl(210, 80%, 52%);
+}
+
+.ac-type-icon[type=bookmark] {
+  list-style-image: url("chrome://browser/skin/urlbar-star.svg#star");
+}
+
 .autocomplete-treebody::-moz-tree-image(bookmark, treecolAutoCompleteImage) {
   list-style-image: url("chrome://browser/skin/places/autocomplete-star.png");
   -moz-image-region: rect(0, 16px, 16px, 0);
 }
 
-richlistitem[selected="true"][current="true"] > .ac-title-box > .ac-result-type-bookmark,
 .autocomplete-treebody::-moz-tree-image(selected, current, bookmark, treecolAutoCompleteImage) {
   -moz-image-region: rect(0, 32px, 16px, 16px);
 }
 
-.ac-result-type-keyword,
-.autocomplete-treebody::-moz-tree-image(keyword, treecolAutoCompleteImage),
-richlistitem[type~="action"][actiontype="searchengine"] > .ac-title-box > .ac-site-icon {
+.ac-type-icon[type=bookmark][selected][current] {
+  list-style-image: url("chrome://browser/skin/urlbar-star.svg#star-inverted");
+}
+
+.ac-type-icon[type=keyword],
+.ac-site-icon[type=searchengine],
+.autocomplete-treebody::-moz-tree-image(keyword, treecolAutoCompleteImage) {
   list-style-image: url(chrome://global/skin/icons/autocomplete-search.svg#search-icon);
-  width: 16px;
-  height: 16px;
-}
-
-.ac-result-type-keyword[selected="true"],
-.autocomplete-treebody::-moz-tree-image(keyword, treecolAutoCompleteImage, selected),
-richlistitem[type~="action"][actiontype="searchengine"][selected="true"] > .ac-title-box > .ac-site-icon {
+}
+
+.ac-type-icon[type=keyword][selected],
+.ac-site-icon[type=searchengine][selected],
+.autocomplete-treebody::-moz-tree-image(keyword, treecolAutoCompleteImage, selected) {
   list-style-image: url(chrome://global/skin/icons/autocomplete-search.svg#search-icon-inverted);
 }
 
-.ac-result-type-tag,
 .autocomplete-treebody::-moz-tree-image(tag, treecolAutoCompleteImage) {
   list-style-image: url("chrome://browser/skin/places/tag.png");
-  width: 16px;
-  height: 16px;
-}
-
-.ac-extra > .ac-comment {
-  font-size: inherit;
-}
-
-.ac-url-text,
-.ac-action-text {
-  font: message-box;
-  color: -moz-nativehyperlinktext;
-}
-
-richlistitem[type~="action"][actiontype$="tab"] > .ac-url-box > .ac-action-icon {
-  list-style-image: url("chrome://browser/skin/actionicon-tab.png");
-  -moz-image-region: rect(0, 16px, 11px, 0);
-  padding: 0 3px;
-}
-
-richlistitem[type~="action"][actiontype$="tab"][selected="true"] > .ac-url-box > .ac-action-icon {
-  -moz-image-region: rect(11px, 16px, 22px, 0);
-}
-
-@media (min-resolution: 1.1dppx) {
-  .ac-result-type-bookmark {
-    list-style-image: url("chrome://browser/skin/places/autocomplete-star@2x.png");
-    -moz-image-region: rect(0, 32px, 32px, 0);
-  }
-
-  richlistitem[selected="true"][current="true"] > .ac-title-box > .ac-result-type-bookmark {
-    list-style-image: url("chrome://browser/skin/places/autocomplete-star@2x.png");
-    -moz-image-region: rect(0, 64px, 32px, 32px);
-  }
-
-  .ac-result-type-tag {
-    list-style-image: url("chrome://browser/skin/places/tag@2x.png");
-  }
-
-  richlistitem[type~="action"][actiontype$="tab"] > .ac-url-box > .ac-action-icon {
-    list-style-image: url("chrome://browser/skin/actionicon-tab@2x.png");
-    -moz-image-region: rect(0, 32px, 22px, 0);
-    width: 22px;
-  }
-
-  richlistitem[type~="action"][actiontype$="tab"][selected="true"] > .ac-url-box > .ac-action-icon {
-    -moz-image-region: rect(22px, 32px, 44px, 0);
-  }
+}
+
+.ac-type-icon[type=switchtab] {
+  list-style-image: url("chrome://browser/skin/urlbar-tab.svg#tab");
+}
+
+.ac-type-icon[type=switchtab][selected] {
+  list-style-image: url("chrome://browser/skin/urlbar-tab.svg#tab-inverted");
 }
 
 .autocomplete-treebody::-moz-tree-cell-text(treecolAutoCompleteComment) {
   color: GrayText;
 }
 
-.ac-comment[selected="true"],
-.ac-url-text[selected="true"],
-.ac-action-text[selected="true"] {
-  color: inherit !important;
-}
-
 .autocomplete-treebody::-moz-tree-cell-text(suggesthint, treecolAutoCompleteComment),
 .autocomplete-treebody::-moz-tree-cell-text(suggestfirst, treecolAutoCompleteComment)
 {
   color: GrayText;
   font-size: smaller;
 }
 
 .autocomplete-treebody::-moz-tree-cell(suggesthint) {
--- a/browser/themes/osx/devedition.css
+++ b/browser/themes/osx/devedition.css
@@ -116,8 +116,14 @@
   -moz-image-region: rect(0, 64px, 16px, 48px);
 }
 @media (min-resolution: 2dppx) {
   :root[devtoolstheme="dark"] .findbar-closebutton:not(:hover),
   .tab-close-button[visuallyselected=true]:not(:hover) {
     -moz-image-region: rect(0, 128px, 32px, 96px);
   }
 }
+
+.ac-type-icon {
+  /* Left-align the type icon in awesomebar popup results with the icon in the
+     urlbar. */
+  -moz-margin-start: 14px;
+}
--- a/browser/themes/shared/jar.inc.mn
+++ b/browser/themes/shared/jar.inc.mn
@@ -162,12 +162,14 @@
   skin/classic/browser/privatebrowsing/check.png               (../shared/privatebrowsing/check.png)
   skin/classic/browser/privatebrowsing/check@2x.png            (../shared/privatebrowsing/check@2x.png)
   skin/classic/browser/privatebrowsing/mask.svg                (../shared/privatebrowsing/mask.svg)
   skin/classic/browser/privatebrowsing/shield-page.png         (../shared/privatebrowsing/shield-page.png)
   skin/classic/browser/privatebrowsing/shield-page@2x.png      (../shared/privatebrowsing/shield-page@2x.png)
   skin/classic/browser/devedition/urlbar-history-dropmarker.svg (../shared/devedition/urlbar-history-dropmarker.svg)
   skin/classic/browser/devedition/urlbar-arrow.png             (../shared/devedition/urlbar-arrow.png)
   skin/classic/browser/devedition/urlbar-arrow@2x.png          (../shared/devedition/urlbar-arrow@2x.png)
+  skin/classic/browser/urlbar-star.svg                         (../shared/urlbar-star.svg)
+  skin/classic/browser/urlbar-tab.svg                          (../shared/urlbar-tab.svg)
   skin/classic/browser/usercontext/personal.svg                (../shared/usercontext/personal.svg)
   skin/classic/browser/usercontext/work.svg                    (../shared/usercontext/work.svg)
   skin/classic/browser/usercontext/banking.svg                 (../shared/usercontext/banking.svg)
   skin/classic/browser/usercontext/shopping.svg                (../shared/usercontext/shopping.svg)
new file mode 100644
--- /dev/null
+++ b/browser/themes/shared/urlbar-star.svg
@@ -0,0 +1,20 @@
+<?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>
+    path:not(:target) {
+      display: none;
+    }
+    path {
+      fill: #b2b2b2;
+    }
+    path[id$="-inverted"] {
+      fill: #fff;
+    }
+  </style>
+
+	<path id="star" d="M8.7,0.5l2,4.3l4.6,0.7c0.6,0.1,0.9,0.9,0.4,1.4l-3.3,3.4l0.8,4.8c0.1,0.7-0.6,1.2-1.1,0.9L8,13.7l-4.1,2.3 c-0.6,0.3-1.2-0.2-1.1-0.9l0.8-4.8L0.2,6.9C-0.2,6.4,0,5.6,0.7,5.5l4.6-0.7l2-4.3C7.6-0.1,8.4-0.1,8.7,0.5z"/>
+	<path id="star-inverted" d="M8.7,0.5l2,4.3l4.6,0.7c0.6,0.1,0.9,0.9,0.4,1.4l-3.3,3.4l0.8,4.8c0.1,0.7-0.6,1.2-1.1,0.9L8,13.7l-4.1,2.3 c-0.6,0.3-1.2-0.2-1.1-0.9l0.8-4.8L0.2,6.9C-0.2,6.4,0,5.6,0.7,5.5l4.6-0.7l2-4.3C7.6-0.1,8.4-0.1,8.7,0.5z"/>
+</svg>
new file mode 100644
--- /dev/null
+++ b/browser/themes/shared/urlbar-tab.svg
@@ -0,0 +1,21 @@
+<?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>
+    path:not(:target) {
+      display: none;
+    }
+    path {
+      fill: #b2b2b2;
+    }
+    path[id$="-inverted"] {
+      fill: #fff;
+    }
+  </style>
+
+  <path id="tab" d="M14,9.5V6c0-1.7-1.3-3-3-3H5C3.3,3,2,4.3,2,6v3.5C2,10.3,1.3,11,0.5,11h0C0.2,11,0,11.2,0,11.5v1 C0,12.8,0.2,13,0.5,13h15c0.3,0,0.5-0.2,0.5-0.5v-1c0-0.3-0.2-0.5-0.5-0.5h0C14.7,11,14,10.3,14,9.5z"/>
+  <path id="tab-inverted" d="M14,9.5V6c0-1.7-1.3-3-3-3H5C3.3,3,2,4.3,2,6v3.5C2,10.3,1.3,11,0.5,11h0C0.2,11,0,11.2,0,11.5v1 C0,12.8,0.2,13,0.5,13h15c0.3,0,0.5-0.2,0.5-0.5v-1c0-0.3-0.2-0.5-0.5-0.5h0C14.7,11,14,10.3,14,9.5z"/>
+
+</svg>
--- a/browser/themes/windows/browser.css
+++ b/browser/themes/windows/browser.css
@@ -1133,21 +1133,16 @@ toolbar[brighttext] .toolbarbutton-1 > .
   border-color: var(--urlbar-border-color);
 }
 
 #urlbar:hover,
 .searchbar-textbox:hover {
   border-color: var(--urlbar-border-color-hover);
 }
 
-/* overlap the urlbar's border */
-#PopupAutoCompleteRichResult {
-  margin-top: -1px;
-}
-
 @media (-moz-windows-default-theme) {
   #urlbar,
   .searchbar-textbox {
     border-radius: 1px;
   }
 
   @media (-moz-os-version: windows-vista),
          (-moz-os-version: windows-win7),
@@ -1408,135 +1403,189 @@ html|*.urlbar-input:-moz-lwtheme::-moz-p
 %include ../shared/identity-block/identity-block.inc.css
 
 /* autocomplete */
 
 #treecolAutoCompleteImage {
   max-width: 36px;
 }
 
-.ac-result-type-bookmark,
+.autocomplete-richlistbox {
+  padding: 4px;
+}
+
+.autocomplete-richlistitem {
+  height: 30px;
+  min-height: 30px;
+  font: message-box;
+  border-radius: 2px;
+  border: 1px solid transparent;
+}
+
+.autocomplete-richlistitem[selected=true] {
+  background-color: hsl(210, 80%, 52%);
+}
+
+.ac-title {
+  font-size: 14px;
+  color: hsl(0, 0%, 0%);
+}
+
+html|span.ac-emphasize-text-title {
+  color: hsl(0, 0%, 50%);
+}
+
+.ac-tags {
+  font-size: 12px;
+}
+
+html|span.ac-tag {
+  background-color: hsl(216, 0%, 88%);
+  color: hsl(0, 0%, 0%);
+  border-radius: 2px;
+  border: 1px solid transparent;
+  padding: 0 1px;
+}
+
+html|span.ac-emphasize-text-tag {
+  color: hsl(0, 0%, 50%);
+}
+
+.ac-url {
+  font-size: 12px;
+  color: hsl(210, 77%, 47%);
+}
+
+html|span.ac-emphasize-text-url {
+  color: hsl(210, 86%, 64%);
+}
+
+.ac-action {
+  font-size: 12px;
+  color: hsl(178, 100%, 28%);
+}
+
+html|span.ac-title-urlaction-separator {
+  color: hsl(0, 0%, 50%);
+}
+
+.ac-title[selected=true],
+.ac-url[selected=true],
+.ac-action[selected=true],
+.ac-title-text[selected=true] > html|span.ac-emphasize-text,
+.ac-url-text[selected=true] > html|span.ac-emphasize-text,
+.ac-action-text[selected=true] > html|span.ac-emphasize-text,
+.ac-url-text[selected=true] > html|span.ac-title-urlaction-separator,
+.ac-action-text[selected=true] > html|span.ac-title-urlaction-separator {
+  color: hsl(0, 0%, 100%);
+}
+
+.ac-tags-text[selected] > html|span.ac-tag {
+  background-color: hsl(0, 0%, 100%);
+  color: hsl(210, 80%, 40%);
+}
+
+.ac-tags-text[selected] > html|span.ac-tag > html|span.ac-emphasize-text-tag {
+  color: hsl(210, 80%, 52%);
+}
+
+@media not all and (-moz-windows-default-theme) {
+  .autocomplete-richlistitem[selected=true] {
+    background-color: Highlight;
+  }
+
+  .ac-title,
+  html|span.ac-emphasize-text-title {
+    color: inherit;
+  }
+
+  html|span.ac-tag,
+  html|span.ac-emphasize-text-tag {
+    background-color: -moz-FieldText;
+    color: -moz-Field;
+  }
+
+  .ac-url,
+  .ac-action,
+  html|span.ac-emphasize-text-url,
+  html|span.ac-title-urlaction-separator {
+    color: -moz-nativehyperlinktext;
+  }
+
+  .ac-tags-text[selected] > html|span.ac-tag,
+  .ac-tags-text[selected] > html|span.ac-tag > html|span.ac-emphasize-text-tag {
+    background-color: HighlightText;
+    color: Highlight;
+  }
+}
+
+.ac-type-icon[type=bookmark] {
+  list-style-image: url("chrome://browser/skin/urlbar-star.svg#star");
+}
+
+.ac-type-icon[type=bookmark][selected][current] {
+  list-style-image: url("chrome://browser/skin/urlbar-star.svg#star-inverted");
+}
+
 .autocomplete-treebody::-moz-tree-image(bookmark, treecolAutoCompleteImage) {
   list-style-image: url("chrome://browser/skin/places/autocomplete-star.png");
   -moz-image-region: rect(0 16px 16px 0);
   width: 16px;
   height: 16px;
 }
 
 @media (min-resolution: 1.1dppx) {
-  .ac-result-type-bookmark,
   .autocomplete-treebody::-moz-tree-image(bookmark, treecolAutoCompleteImage) {
     list-style-image: url("chrome://browser/skin/places/autocomplete-star@2x.png");
     -moz-image-region: rect(0 32px 32px 0);
   }
 }
 
-.ac-result-type-keyword,
-.autocomplete-treebody::-moz-tree-image(keyword, treecolAutoCompleteImage),
-richlistitem[type~="action"][actiontype="searchengine"] > .ac-title-box > .ac-site-icon {
-  list-style-image: url(chrome://global/skin/icons/autocomplete-search.svg#search-icon);
-  width: 16px;
-  height: 16px;
-}
-
 @media not all and (-moz-os-version: windows-vista) and (-moz-windows-default-theme) {
   @media not all and (-moz-os-version: windows-win7) and (-moz-windows-default-theme) {
-    richlistitem[selected="true"][current="true"] > .ac-title-box > .ac-result-type-bookmark,
     .autocomplete-treebody::-moz-tree-image(selected, current, bookmark, treecolAutoCompleteImage) {
       -moz-image-region: rect(0 32px 16px 16px);
     }
 
     @media (min-resolution: 1.1dppx) {
-      richlistitem[selected="true"][current="true"] > .ac-title-box > .ac-result-type-bookmark,
       .autocomplete-treebody::-moz-tree-image(selected, current, bookmark, treecolAutoCompleteImage) {
         -moz-image-region: rect(0 64px 32px 32px);
       }
     }
 
-    .ac-result-type-keyword[selected="true"],
-    .autocomplete-treebody::-moz-tree-image(keyword, treecolAutoCompleteImage, selected),
-    richlistitem[type~="action"][actiontype="searchengine"][selected="true"] > .ac-title-box > .ac-site-icon {
+    .autocomplete-treebody::-moz-tree-image(keyword, treecolAutoCompleteImage, selected) {
       list-style-image: url(chrome://global/skin/icons/autocomplete-search.svg#search-icon-inverted);
     }
   }
 }
 
-.ac-result-type-tag,
+.ac-type-icon[type=keyword],
+.ac-site-icon[type=searchengine],
+.autocomplete-treebody::-moz-tree-image(keyword, treecolAutoCompleteImage) {
+  list-style-image: url(chrome://global/skin/icons/autocomplete-search.svg#search-icon);
+}
+
+.ac-type-icon[type=keyword][selected],
+.ac-site-icon[type=searchengine][selected],
+.autocomplete-treebody::-moz-tree-image(keyword, treecolAutoCompleteImage, selected) {
+  list-style-image: url(chrome://global/skin/icons/autocomplete-search.svg#search-icon-inverted);
+}
+
 .autocomplete-treebody::-moz-tree-image(tag, treecolAutoCompleteImage) {
   list-style-image: url("chrome://browser/skin/places/tag.png");
   width: 16px;
   height: 16px;
 }
 
-.ac-comment,
-#PopupAutoCompleteRichResult > hbox[anonid="search-suggestions-notification"] > description,
-#PopupAutoCompleteRichResult > hbox[anonid="search-suggestions-notification"] > button {
-  font-size: 1.06em;
-}
-
-.ac-extra > .ac-comment,
-.ac-url-text,
-.ac-action-text {
-  font-size: 1em;
-}
-
-.ac-url-text,
-.ac-action-text {
-  color: -moz-nativehyperlinktext;
-}
-
-@media (-moz-os-version: windows-xp) and (-moz-windows-default-theme) {
-  .ac-url-text:not([selected="true"]),
-  .ac-action-text:not([selected="true"]) {
-    color: #008800;
-  }
-}
-
-@media (-moz-os-version: windows-win10) and (-moz-windows-default-theme) {
-  .ac-url-text:not([selected="true"]),
-  .ac-action-text:not([selected="true"]) {
-    color: Highlight;
-  }
-}
-
-richlistitem[type~="action"][actiontype$="tab"] > .ac-url-box > .ac-action-icon {
-  list-style-image: url("chrome://browser/skin/actionicon-tab.png");
-  -moz-image-region: rect(0, 16px, 11px, 0);
-  padding: 0 3px;
-  width: 22px;
-  height: 11px;
-}
-
-@media (min-resolution: 1.1dppx) {
-  richlistitem[type~="action"][actiontype$="tab"] > .ac-url-box > .ac-action-icon {
-    list-style-image: url("chrome://browser/skin/actionicon-tab@2x.png");
-    -moz-image-region: rect(0, 32px, 22px, 0);
-  }
-}
-
-@media not all and (-moz-os-version: windows-vista),
-       not all and (-moz-windows-default-theme) {
-  @media not all and (-moz-os-version: windows-win7),
-         not all and (-moz-windows-default-theme) {
-    richlistitem[type~="action"][actiontype$="tab"][selected="true"] > .ac-url-box > .ac-action-icon {
-      -moz-image-region: rect(11px, 16px, 22px, 0);
-    }
-
-    @media (min-resolution: 1.1dppx) {
-      richlistitem[type~="action"][actiontype$="tab"][selected="true"] > .ac-url-box > .ac-action-icon {
-        -moz-image-region: rect(22px, 32px, 44px, 0);
-      }
-    }
-
-    .ac-comment[selected="true"],
-    .ac-url-text[selected="true"],
-    .ac-action-text[selected="true"] {
-      color: inherit !important;
-    }
-  }
+.ac-type-icon[type=switchtab] {
+  list-style-image: url("chrome://browser/skin/urlbar-tab.svg#tab");
+}
+
+.ac-type-icon[type=switchtab][selected] {
+  list-style-image: url("chrome://browser/skin/urlbar-tab.svg#tab-inverted");
 }
 
 .autocomplete-treebody::-moz-tree-cell-text(treecolAutoCompleteComment) {
   color: GrayText;
 }
 
 .autocomplete-treebody::-moz-tree-cell-text(suggesthint, treecolAutoCompleteComment),
 .autocomplete-treebody::-moz-tree-cell-text(suggestfirst, treecolAutoCompleteComment)
--- a/browser/themes/windows/devedition.css
+++ b/browser/themes/windows/devedition.css
@@ -319,8 +319,14 @@
   :root[devtoolstheme="light"] #titlebar-close {
     list-style-image: url(chrome://browser/skin/caption-buttons.svg#close);
   }
 
   :root[devtoolstheme="light"] #titlebar-close:hover {
     list-style-image: url(chrome://browser/skin/caption-buttons.svg#close-white);
   }
 }
+
+.ac-type-icon {
+  /* Left-align the type icon in awesomebar popup results with the icon in the
+     urlbar. */
+  -moz-margin-start: 13px;
+}
--- a/toolkit/components/places/UnifiedComplete.js
+++ b/toolkit/components/places/UnifiedComplete.js
@@ -54,19 +54,16 @@ const QUERYTYPE_FILTERED            = 0;
 const QUERYTYPE_AUTOFILL_HOST       = 1;
 const QUERYTYPE_AUTOFILL_URL        = 2;
 
 // This separator is used as an RTL-friendly way to split the title and tags.
 // It can also be used by an nsIAutoCompleteResult consumer to re-split the
 // "comment" back into the title and the tag.
 const TITLE_TAGS_SEPARATOR = " \u2013 ";
 
-// This separator identifies the search engine name in the title.
-const TITLE_SEARCH_ENGINE_SEPARATOR = " \u00B7\u2013\u00B7 ";
-
 // Telemetry probes.
 const TELEMETRY_1ST_RESULT = "PLACES_AUTOCOMPLETE_1ST_RESULT_TIME_MS";
 const TELEMETRY_6_FIRST_RESULTS = "PLACES_AUTOCOMPLETE_6_FIRST_RESULTS_TIME_MS";
 // The default frecency value used when inserting matches with unknown frecency.
 const FRECENCY_DEFAULT = 1000;
 
 // Remote matches are appended when local matches are below a given frecency
 // threshold (FRECENCY_DEFAULT) as soon as they arrive.  However we'll
@@ -1380,20 +1377,25 @@ Search.prototype = {
     // URL. For example, "https://www.google.com/search?q=terms&client=firefox"
     // when searching for "Firefox".
     let terms = parseResult.terms.toLowerCase();
     if (this._searchTokens.length > 0 &&
         this._searchTokens.every(token => !terms.includes(token))) {
       return;
     }
 
-    // Use the special separator that the binding will use to style the item.
-    match.style = "search " + match.style;
-    match.comment = parseResult.terms + TITLE_SEARCH_ENGINE_SEPARATOR +
-                    parseResult.engineName;
+    // Turn the match into a searchengine action with a favicon.
+    match.value = makeActionURL("searchengine", {
+      engineName: parseResult.engineName,
+      input: parseResult.terms,
+      searchQuery: parseResult.terms,
+    });
+    match.comment = parseResult.engineName;
+    match.icon = match.icon || match.iconUrl;
+    match.style = "action searchengine favicon";
   },
 
   _addMatch(match) {
     // A search could be canceled between a query start and its completion,
     // in such a case ensure we won't notify any result for it.
     if (!this.pending)
       return;
 
--- a/toolkit/components/places/tests/unifiedcomplete/head_autocomplete.js
+++ b/toolkit/components/places/tests/unifiedcomplete/head_autocomplete.js
@@ -109,18 +109,16 @@ AutoCompleteInput.prototype = {
 }
 
 // A helper for check_autocomplete to check a specific match against data from
 // the controller.
 function _check_autocomplete_matches(match, result) {
   let { uri, title, tags, searchEngine, style } = match;
   if (tags)
     title += " \u2013 " + tags.sort().join(", ");
-  if (searchEngine)
-    title += TITLE_SEARCH_ENGINE_SEPARATOR + searchEngine;
   if (style)
     style = style.sort();
   else
     style = ["favicon"];
 
   do_print("Checking against expected '" + uri.spec + "', '" + title + "'...");
   // Got a match on both uri and title?
   if (stripPrefix(uri.spec) != stripPrefix(result.value) || title != result.comment) {
@@ -366,16 +364,19 @@ function makeSearchMatch(input, extra = 
     searchQuery: "searchQuery" in extra ? extra.searchQuery : input,
   };
   if ("alias" in extra) {
     // May be undefined, which is expected, but in that case make sure it's not
     // included in the params of the moz-action URL.
     params.alias = extra.alias;
   }
   let style = [ "action", "searchengine" ];
+  if (Array.isArray(extra.style)) {
+    style.push(...extra.style);
+  }
   if (extra.heuristic) {
     style.push("heuristic");
   }
   return {
     uri: makeActionURI("searchengine", params),
     title: params.engineName,
     style,
   }
--- a/toolkit/components/places/tests/unifiedcomplete/test_searchEngine_restyle.js
+++ b/toolkit/components/places/tests/unifiedcomplete/test_searchEngine_restyle.js
@@ -13,18 +13,27 @@ add_task(function* test_searchEngine() {
   let uri2 = NetUtil.newURI("http://s.example.com/search?q=Terms&client=2");
   yield PlacesTestUtils.addVisits({ uri: uri1, title: "Terms - SearchEngine Search" });
   yield addBookmark({ uri: uri2, title: "Terms - SearchEngine Search" });
 
   do_print("Past search terms should be styled, unless bookmarked");
   Services.prefs.setBoolPref("browser.urlbar.restyleSearches", true);
   yield check_autocomplete({
     search: "term",
-    matches: [ { uri: uri1, title: "Terms", searchEngine: "SearchEngine", style: ["favicon", "search"] },
-               { uri: uri2, title: "Terms - SearchEngine Search", style: ["bookmark"] } ]
+    matches: [
+      makeSearchMatch("Terms", {
+        engineName: "SearchEngine",
+        style: ["favicon"]
+      }),
+      {
+        uri: uri2,
+        title: "Terms - SearchEngine Search",
+        style: ["bookmark"]
+      }
+    ]
   });
 
   do_print("Past search terms should not be styled if restyling is disabled");
   Services.prefs.setBoolPref("browser.urlbar.restyleSearches", false);
   yield check_autocomplete({
     search: "term",
     matches: [ { uri: uri1, title: "Terms - SearchEngine Search" },
                { uri: uri2, title: "Terms - SearchEngine Search", style: ["bookmark"] } ]
--- a/toolkit/content/autocomplete.css
+++ b/toolkit/content/autocomplete.css
@@ -7,11 +7,29 @@
 
 /* Apply crisp rendering for favicons at exactly 2dppx resolution */
 @media (resolution: 2dppx) {
   .ac-site-icon {
     image-rendering: -moz-crisp-edges;
   }
 }
 
-richlistitem > .ac-title-box > .ac-title > .ac-comment:not([selected]) > html|span.ac-selected-text {
+richlistitem {
+  -moz-box-orient: horizontal;
+  overflow: hidden;
+}
+
+.ac-title-text,
+.ac-tags-text,
+.ac-url-text,
+.ac-action-text {
+  overflow: hidden;
+  text-overflow: ellipsis;
+  white-space: nowrap;
+}
+
+.ac-tags[empty] {
   display: none;
 }
+
+.ac-action[actiontype=searchengine]:not([selected]) {
+  display: none;
+}
--- a/toolkit/content/tests/chrome/test_autocomplete_emphasis.xul
+++ b/toolkit/content/tests/chrome/test_autocomplete_emphasis.xul
@@ -133,25 +133,39 @@ function nextTest() {
   autocomplete.value = currentTest.search;
   synthesizeKey("VK_DOWN", {});
 }
 
 function checkSearchCompleted() {
   let autocomplete = $("richautocomplete");
   let result = autocomplete.popup.richlistbox.firstChild;
 
-  for (let attribute of [result._title, result._url]) {
-    is(attribute.childNodes.length, currentTest.emphasis.length, "The element should have the expected number of children.");
+  for (let attribute of [result._titleText, result._urlText]) {
+
+    let numChildren = currentTest.emphasis.length;
+    let childNodeStart = 0;
+    if (attribute == result._urlText) {
+      // For the URL description, the first child is the em dash separator that
+      // visually separates the the title string from the URL string.
+      numChildren++;
+      childNodeStart = 1;
+      let node = attribute.childNodes[0];
+      ok(node.classList.contains("ac-title-urlaction-separator"),
+         "First child of URL text should be separator");
+    }
+
+    is(attribute.childNodes.length, numChildren, "The element should have the expected number of children.");
+
     for (let i = 0; i < currentTest.emphasis.length; i++) {
-      let node = attribute.childNodes[i];
+      let node = attribute.childNodes[childNodeStart + i];
       // Emphasized parts strictly alternate.
       if ((i % 2 == 0) == currentTest.emphasizeFirst) {
         // Check that this part is correctly emphasized.
         is(node.nodeName, "span", ". That child should be a span node");
-        is(node.className, "ac-emphasize-text", ". That child should be emphasized");
+        ok(node.classList.contains("ac-emphasize-text"), ". That child should be emphasized");
         is(node.textContent, currentTest.emphasis[i], ". That emphasis should be as expected.");
       } else {
         // Check that this part is _not_ emphasized.
         is(node.nodeName, "#text", ". That child should be a text node");
         is(node.textContent, currentTest.emphasis[i], ". That text should be as expected.");
       }
     }
   }
--- a/toolkit/content/widgets/autocomplete.xml
+++ b/toolkit/content/widgets/autocomplete.xml
@@ -1164,26 +1164,32 @@ extends="chrome://global/content/binding
 
           // Default the height to 0 if we have no rows to show
           let height = 0;
           if (numRows) {
             if (!this._rowHeight) {
               let firstRowRect = rows[0].getBoundingClientRect();
               this._rowHeight = firstRowRect.height;
 
-              let transition =
-                window.getComputedStyle(this.richlistbox).transitionProperty;
+              let style = window.getComputedStyle(this.richlistbox);
+
+              let transition = style.transitionProperty;
               this._rlbAnimated = transition && transition != "none";
 
+              let paddingTop = parseInt(style.paddingTop) || 0;
+              let paddingBottom = parseInt(style.paddingBottom) || 0;
+              this._rlbPadding = paddingTop + paddingBottom;
+
               // Set a fixed max-height to avoid flicker when growing the panel.
-              this.richlistbox.style.maxHeight = (this._rowHeight * this.maxRows) + "px";
+              this.richlistbox.style.maxHeight =
+                ((this._rowHeight * this.maxRows) + this._rlbPadding) + "px";
             }
 
             // Calculate the height to have the first row to last row shown
-            height = this._rowHeight * numRows;
+            height = (this._rowHeight * numRows) + this._rlbPadding;
           }
 
           let animate = this._rlbAnimated &&
                         this.getAttribute("dontanimate") != "true";
           let currentHeight = this.richlistbox.getBoundingClientRect().height;
           if (height > currentHeight) {
             // Grow immediately.
             if (animate) {
@@ -1318,82 +1324,91 @@ extends="chrome://global/content/binding
       <property name="view"
                 onget="return this.mInput.controller;"
                 onset="return val;"/>
 
     </implementation>
   </binding>
 
   <binding id="autocomplete-richlistitem" extends="chrome://global/content/bindings/richlistbox.xml#richlistitem">
-    <content>
-      <xul:hbox align="center" class="ac-title-box">
-        <xul:image xbl:inherits="src=image" class="ac-site-icon"/>
-        <xul:hbox anonid="title-box" class="ac-title" flex="1"
-                  onunderflow="_doUnderflow('_title');"
-                  onoverflow="_doOverflow('_title');">
-          <xul:description anonid="title" class="ac-normal-text ac-comment" xbl:inherits="selected"/>
-        </xul:hbox>
-        <xul:label anonid="title-overflow-ellipsis" xbl:inherits="selected"
-                   class="ac-ellipsis-after ac-comment"/>
-        <xul:hbox anonid="extra-box" class="ac-extra" align="center" hidden="true">
-          <xul:image class="ac-result-type-tag"/>
-          <xul:label class="ac-normal-text ac-comment" xbl:inherits="selected" value=":"/>
-          <xul:description anonid="extra" class="ac-normal-text ac-comment" xbl:inherits="selected"/>
-        </xul:hbox>
-        <xul:image anonid="type-image" class="ac-type-icon" xbl:inherits="selected"/>
+
+    <content align="center"
+             onoverflow="this._onOverflow();"
+             onunderflow="this._onUnderflow();">
+      <xul:image anonid="type-icon"
+                 class="ac-type-icon"
+                 xbl:inherits="selected,current"/>
+      <xul:image anonid="site-icon"
+                 class="ac-site-icon"
+                 xbl:inherits="src=image,selected"/>
+      <xul:hbox class="ac-title"
+                align="center"
+                xbl:inherits="selected">
+        <xul:description class="ac-text-overflow-container">
+          <xul:description anonid="title-text"
+                           class="ac-title-text"
+                           xbl:inherits="selected"/>
+        </xul:description>
       </xul:hbox>
-      <xul:hbox align="center" class="ac-url-box">
-        <xul:spacer class="ac-site-icon"/>
-        <xul:image class="ac-action-icon"/>
-        <xul:hbox anonid="url-box" class="ac-url" flex="1"
-                  onunderflow="_doUnderflow('_url');"
-                  onoverflow="_doOverflow('_url');">
-          <xul:description anonid="url" class="ac-normal-text ac-url-text"
-                           xbl:inherits="selected type"/>
-          <xul:description anonid="action" class="ac-normal-text ac-action-text"
-                           xbl:inherits="selected type"/>
-        </xul:hbox>
-        <xul:label anonid="url-overflow-ellipsis" xbl:inherits="selected"
-                   class="ac-ellipsis-after ac-url-text"/>
-        <xul:spacer class="ac-type-icon"/>
+      <xul:hbox anonid="tags"
+                class="ac-tags"
+                align="center"
+                xbl:inherits="selected">
+        <xul:description class="ac-text-overflow-container">
+          <xul:description anonid="tags-text"
+                           class="ac-tags-text"
+                           xbl:inherits="selected"/>
+        </xul:description>
+      </xul:hbox>
+      <xul:hbox class="ac-url"
+                align="center"
+                xbl:inherits="selected,actiontype">
+        <xul:description class="ac-text-overflow-container">
+          <xul:description anonid="url-text"
+                           class="ac-url-text"
+                           xbl:inherits="selected"/>
+        </xul:description>
+      </xul:hbox>
+      <xul:hbox class="ac-action"
+                align="center"
+                xbl:inherits="selected,actiontype">
+        <xul:description class="ac-text-overflow-container">
+          <xul:description anonid="action-text"
+                           class="ac-action-text"
+                           xbl:inherits="selected"/>
+        </xul:description>
       </xul:hbox>
     </content>
+
     <implementation implements="nsIDOMXULSelectControlItemElement">
       <constructor>
         <![CDATA[
-            let ellipsis = "\u2026";
-            try {
-              ellipsis = Components.classes["@mozilla.org/preferences-service;1"].
-                getService(Components.interfaces.nsIPrefBranch).
-                getComplexValue("intl.ellipsis",
-                  Components.interfaces.nsIPrefLocalizedString).data;
-            } catch (ex) {
-              // Do nothing.. we already have a default
-            }
-
-            this._urlOverflowEllipsis = document.getAnonymousElementByAttribute(this, "anonid", "url-overflow-ellipsis");
-            this._titleOverflowEllipsis = document.getAnonymousElementByAttribute(this, "anonid", "title-overflow-ellipsis");
-
-            this._urlOverflowEllipsis.value = ellipsis;
-            this._titleOverflowEllipsis.value = ellipsis;
-
-            this._typeImage = document.getAnonymousElementByAttribute(this, "anonid", "type-image");
-
-            this._urlBox = document.getAnonymousElementByAttribute(this, "anonid", "url-box");
-            this._url = document.getAnonymousElementByAttribute(this, "anonid", "url");
-            this._action = document.getAnonymousElementByAttribute(this, "anonid", "action");
-
-            this._titleBox = document.getAnonymousElementByAttribute(this, "anonid", "title-box");
-            this._title = document.getAnonymousElementByAttribute(this, "anonid", "title");
-
-            this._extraBox = document.getAnonymousElementByAttribute(this, "anonid", "extra-box");
-            this._extra = document.getAnonymousElementByAttribute(this, "anonid", "extra");
-
-            this._adjustAcItem();
-          ]]>
+          this._typeIcon = document.getAnonymousElementByAttribute(
+            this, "anonid", "type-icon"
+          );
+          this._siteIcon = document.getAnonymousElementByAttribute(
+            this, "anonid", "site-icon"
+          );
+          this._titleText = document.getAnonymousElementByAttribute(
+            this, "anonid", "title-text"
+          );
+          this._tags = document.getAnonymousElementByAttribute(
+            this, "anonid", "tags"
+          );
+          this._tagsText = document.getAnonymousElementByAttribute(
+            this, "anonid", "tags-text"
+          );
+          this._urlText = document.getAnonymousElementByAttribute(
+            this, "anonid", "url-text"
+          );
+          this._actionText = document.getAnonymousElementByAttribute(
+            this, "anonid", "action-text"
+          );
+          this._adjustAcItem();
+        ]]>
       </constructor>
 
       <property name="label" readonly="true">
         <getter>
           <![CDATA[
             // This property is a string that is read aloud by screen readers,
             // so it must not contain anything that should not be user-facing.
 
@@ -1435,16 +1450,36 @@ extends="chrome://global/content/binding
               getService(Components.interfaces.nsIPrefBranch).
               getIntPref("toolkit.autocomplete.richBoundaryCutoff");
           }
           return this._boundaryCutoff;
           ]]>
         </getter>
       </property>
 
+      <field name="_inOverflow">false</field>
+
+      <method name="_onOverflow">
+        <body>
+          <![CDATA[
+          this._inOverflow = true;
+          this._handleOverflow();
+          ]]>
+        </body>
+      </method>
+
+      <method name="_onUnderflow">
+        <body>
+          <![CDATA[
+          this._inOverflow = false;
+          this._handleOverflow();
+          ]]>
+        </body>
+      </method>
+
       <method name="_getBoundaryIndices">
         <parameter name="aText"/>
         <parameter name="aSearchTokens"/>
         <body>
           <![CDATA[
           // Short circuit for empty search ([""] == "")
           if (aSearchTokens == "")
             return [0, aText.length];
@@ -1519,46 +1554,133 @@ extends="chrome://global/content/binding
         <parameter name="aText"/>
         <parameter name="aNoEmphasis"/>
         <body>
           <![CDATA[
           // Get rid of all previous text
           while (aDescriptionElement.hasChildNodes())
             aDescriptionElement.removeChild(aDescriptionElement.firstChild);
 
+          // Add a separator to the front of the URL and action.
+          if (aText &&
+              (aDescriptionElement == this._urlText ||
+               aDescriptionElement == this._actionText)) {
+            let span =
+              document.createElementNS("http://www.w3.org/1999/xhtml", "span");
+            aDescriptionElement.appendChild(span);
+            span.className = "ac-title-urlaction-separator";
+            span.textContent = "—";
+          }
+
           // If aNoEmphasis is specified, don't add any emphasis
           if (aNoEmphasis) {
             aDescriptionElement.appendChild(document.createTextNode(aText));
             return;
           }
 
           // Get the indices that separate match and non-match text
           let search = this.getAttribute("text");
           let tokens = this._getSearchTokens(search);
           let indices = this._getBoundaryIndices(aText, tokens);
 
+          this._appendDescriptionSpans(indices, aText, aDescriptionElement,
+                                       aDescriptionElement);
+          ]]>
+        </body>
+      </method>
+
+      <method name="_appendDescriptionSpans">
+        <parameter name="indices"/>
+        <parameter name="text"/>
+        <parameter name="spansParentElement"/>
+        <parameter name="descriptionElement"/>
+        <body>
+          <![CDATA[
           let next;
           let start = 0;
           let len = indices.length;
           // Even indexed boundaries are matches, so skip the 0th if it's empty
           for (let i = indices[0] == 0 ? 1 : 0; i < len; i++) {
             next = indices[i];
-            let text = aText.substr(start, next - start);
+            let spanText = text.substr(start, next - start);
             start = next;
 
             if (i % 2 == 0) {
               // Emphasize the text for even indices
-              let span = aDescriptionElement.appendChild(
+              let span = spansParentElement.appendChild(
                 document.createElementNS("http://www.w3.org/1999/xhtml", "span"));
-              span.className = "ac-emphasize-text";
-              span.textContent = text;
+              this._setUpEmphasisSpan(span, descriptionElement);
+              span.textContent = spanText;
             } else {
               // Otherwise, it's plain text
-              aDescriptionElement.appendChild(document.createTextNode(text));
+              spansParentElement.appendChild(document.createTextNode(spanText));
+            }
+          }
+          ]]>
+        </body>
+      </method>
+
+      <method name="_setUpTags">
+        <parameter name="tags"/>
+        <body>
+          <![CDATA[
+          while (this._tagsText.hasChildNodes()) {
+            this._tagsText.firstChild.remove();
+          }
+
+          let anyTagsMatch = false;
+
+          // Include only tags that match the search string.
+          for (let tag of tags) {
+            // Check if the tag matches the search string.
+            let search = this.getAttribute("text");
+            let tokens = this._getSearchTokens(search);
+            let indices = this._getBoundaryIndices(tag, tokens);
+
+            if (indices.length == 2 &&
+                indices[0] == 0 &&
+                indices[1] == tag.length) {
+              // The tag doesn't match the search string, so don't include it.
+              continue;
             }
+
+            anyTagsMatch = true;
+
+            let tagSpan =
+              document.createElementNS("http://www.w3.org/1999/xhtml", "span");
+            tagSpan.classList.add("ac-tag");
+            this._tagsText.appendChild(tagSpan);
+
+            this._appendDescriptionSpans(indices, tag, tagSpan, this._tagsText);
+          }
+
+          return anyTagsMatch;
+          ]]>
+        </body>
+      </method>
+
+      <method name="_setUpEmphasisSpan">
+        <parameter name="aSpan"/>
+        <parameter name="aDescriptionElement"/>
+        <body>
+          <![CDATA[
+          aSpan.classList.add("ac-emphasize-text");
+          switch (aDescriptionElement) {
+            case this._titleText:
+              aSpan.classList.add("ac-emphasize-text-title");
+              break;
+            case this._tagsText:
+              aSpan.classList.add("ac-emphasize-text-tag");
+              break;
+            case this._urlText:
+              aSpan.classList.add("ac-emphasize-text-url");
+              break;
+            case this._actionText:
+              aSpan.classList.add("ac-emphasize-text-action");
+              break;
           }
           ]]>
         </body>
       </method>
 
       <!--
         This will generate an array of emphasis pairs for use with
         _setUpEmphasisedSections(). Each pair is a tuple (array) that
@@ -1647,20 +1769,17 @@ extends="chrome://global/content/binding
 
           for (let [text, emphasise] of aTextPairs) {
             if (emphasise) {
               let span = aDescriptionElement.appendChild(
                 document.createElementNS("http://www.w3.org/1999/xhtml", "span"));
               span.textContent = text;
               switch(emphasise) {
                 case "match":
-                  span.className = "ac-emphasize-text";
-                  break;
-                case "selected":
-                  span.className = "ac-selected-text";
+                  this._setUpEmphasisSpan(span, aDescriptionElement);
                   break;
               }
             } else {
               aDescriptionElement.appendChild(document.createTextNode(text));
             }
           }
           ]]>
         </body>
@@ -1679,76 +1798,78 @@ extends="chrome://global/content/binding
           return this._textToSubURI.unEscapeURIForUI("UTF-8", url);
           ]]>
         </body>
       </method>
 
       <method name="_adjustAcItem">
         <body>
           <![CDATA[
-          let originalUrl = this.getAttribute("url");
+          this._titleText.style.removeProperty("max-width");
+          this._tagsText.style.removeProperty("max-width");
+          this._urlText.style.removeProperty("max-width");
+          this._actionText.style.removeProperty("max-width");
+
           let title = this.getAttribute("title");
-          let type = this.getAttribute("type");
 
           let displayUrl;
-          let emphasiseTitle = true;
+          let originalUrl = this.getAttribute("url");
           let emphasiseUrl = true;
 
-          // Hide the title's extra box by default, until we find out later if
-          // we need extra stuff.
-          this._extraBox.hidden = true;
-          this._titleBox.flex = 1;
-          this._typeImage.hidden = false;
+          let type = this.getAttribute("type");
+          let types = new Set(type.split(/\s+/));
+          let initialTypes = new Set(types);
+          // Remove types that should ultimately not be in the `type` string.
+          types.delete("action");
+          types.delete("autofill");
+          types.delete("heuristic");
+          type = [...types][0] || "";
+
+          let action;
+
+          if (initialTypes.has("autofill")) {
+            // Treat autofills as visiturl actions.
+            action = {
+              type: "visiturl",
+              params: {
+                url: originalUrl,
+              },
+            };
+          }
 
           this.removeAttribute("actiontype");
           this.classList.remove("overridable-action");
 
-          // The ellipses are hidden via their visibility so that they always
-          // take up space and don't pop in on top of text when shown.  For
-          // keyword searches, however, the title ellipsis should not take up
-          // space when hidden.  Setting the hidden property accomplishes that.
-          this._titleOverflowEllipsis.hidden = false;
-
-          let types = new Set(type.split(/\s+/));
-
-          // Remove types that should ultimately not be in the `type` string.
-          let initialTypes = new Set(types);
-          types.delete("action");
-          types.delete("autofill");
-          types.delete("heuristic");
-          types.delete("search");
-
-          type = [...types][0] || "";
-
           // If the type includes an action, set up the item appropriately.
-          if (initialTypes.has("action")) {
-            let action = this._parseActionUrl(originalUrl);
+          if (initialTypes.has("action") || action) {
+            action = action || this._parseActionUrl(originalUrl);
             this.setAttribute("actiontype", action.type);
 
             if (action.type == "switchtab") {
               this.classList.add("overridable-action");
               displayUrl = this._unescapeUrl(action.params.url);
-              let desc = this._stringBundle.GetStringFromName("switchToTab");
-              this._setUpDescription(this._action, desc, true);
+              let desc = this._stringBundle.GetStringFromName("switchToTab2");
+              this._setUpDescription(this._actionText, desc, true);
             } else if (action.type == "remotetab") {
               displayUrl = this._unescapeUrl(action.params.url);
               let desc = action.params.deviceName;
-              this._setUpDescription(this._action, desc, true);
+              this._setUpDescription(this._actionText, desc, true);
             } else if (action.type == "searchengine") {
               emphasiseUrl = false;
 
               // The order here is not localizable, we default to appending
               // "- Search with Engine" to the search string, to be able to
               // properly generate emphasis pairs. That said, no localization
               // changed the order while it was possible, so doesn't look like
               // there's a strong need for that.
               let {engineName, searchSuggestion, searchQuery} = action.params;
-              let engineStr = " - " +
+              let engineStr =
                 this._stringBundle.formatStringFromName("searchWithEngine",
                                                         [engineName], 1);
+              this._setUpDescription(this._actionText, engineStr, true);
 
               // Make the title by generating an array of pairs and its
               // corresponding interpolation string (e.g., "%1$S") to pass to
               // _generateEmphasisPairs.
               let pairs;
               if (searchSuggestion) {
                 // Check if the search query appears in the suggestion.  It may
                 // not.  If it does, then emphasize the query in the suggestion
@@ -1765,164 +1886,182 @@ extends="chrome://global/content/binding
                     [searchSuggestion, ""],
                   ];
                 }
               } else {
                 pairs = [
                   [searchQuery, ""],
                 ];
               }
-              pairs.push([engineStr, "selected"]);
               let interpStr = pairs.map((pair, i) => `%${i + 1}$S`).join("");
               title = this._generateEmphasisPairs(interpStr, pairs);
 
               // If this is a default search match, we remove the image so we
               // can style it ourselves with a generic search icon.
               // We don't do this when matching an aliased search engine,
               // because the icon helps with recognising which engine will be
               // used (when using the default engine, we don't need that
               // recognition).
-              if (!action.params.alias) {
+              if (!action.params.alias && !initialTypes.has("favicon")) {
                 this.removeAttribute("image");
               }
             } else if (action.type == "visiturl") {
               emphasiseUrl = false;
               displayUrl = this._unescapeUrl(action.params.url);
-              let sourceStr = this._stringBundle.GetStringFromName("visitURL");
-              title = this._generateEmphasisPairs(sourceStr, [
-                                                    [displayUrl, "match"],
-                                                  ]);
+              title = displayUrl;
+              let visitStr = this._stringBundle.GetStringFromName("visit");
+              this._setUpDescription(this._actionText, visitStr, true);
             }
           }
 
-          // Check if we have a search engine name
-          if (initialTypes.has("search")) {
-            emphasiseUrl = false;
-
-            const TITLE_SEARCH_ENGINE_SEPARATOR = " \u00B7\u2013\u00B7 ";
-
-            let searchEngine = "";
-            [title, searchEngine] = title.split(TITLE_SEARCH_ENGINE_SEPARATOR);
-            displayUrl = this._stringBundle.formatStringFromName("searchWithEngine", [searchEngine], 1);
-          }
-
           if (!displayUrl) {
             let input = this.parentNode.parentNode.input;
             let url = typeof(input.trimValue) == "function" ?
                       input.trimValue(originalUrl) :
                       originalUrl;
             displayUrl = this._unescapeUrl(url);
           }
           this.setAttribute("displayurl", displayUrl);
 
-          // Check if we have an auto-fill URL
-          if (initialTypes.has("autofill")) {
-            emphasiseUrl = false;
-
-            let sourceStr = this._stringBundle.GetStringFromName("visitURL");
-            title = this._generateEmphasisPairs(sourceStr, [
-                                                 [displayUrl, "match"],
-                                                ]);
-          }
-
-          // If we have a tag match, show the tags and icon
-          if (type == "tag" || type == "bookmark-tag") {
-            // Configure the extra box for tags display
-            this._extraBox.hidden = false;
-            this._extraBox.childNodes[0].hidden = false;
-            this._extraBox.childNodes[1].hidden = true;
-            this._extraBox.pack = "end";
-            this._titleBox.flex = 1;
-
-            // The title is separated from the tags by an endash
-            let tags;
-            [, title, tags] = title.match(/^(.+) \u2013 (.+)$/);
-
-            // Each tag is split by a comma in an undefined order, so sort it
-            let sortedTags = tags.split(",").sort().join(", ");
-
-            // Emphasize the matching text in the tags
-            this._setUpDescription(this._extra, sortedTags);
-
-            // If we're suggesting bookmarks, then treat tagged matches as
-            // bookmarks for the star.
-            if (type == "bookmark-tag") {
-              type = "bookmark";
-            } else {
-              this._typeImage.hidden = true;
-            }
-          // keyword and favicon type results for search engines
-          // have an extra magnifying glass icon after them
-          } else if (type == "keyword" || (initialTypes.has("search") &&
-              initialTypes.has("favicon"))) {
-            // Configure the extra box for keyword display
-            this._extraBox.hidden = false;
-            this._extraBox.childNodes[0].hidden = true;
-            // The second child node is ":" and it should be hidden for non keyword types
-            this._extraBox.childNodes[1].hidden = type == "keyword" ? false : true;
-            this._extraBox.pack = "start";
-            this._titleBox.flex = 0;
-
-            // Hide the ellipsis so it doesn't take up space.
-            this._titleOverflowEllipsis.hidden = true;
-
-            if (type == "keyword") {
-              // Put the parameters next to the title if we have any
-              let search = this.getAttribute("text");
-              let params = "";
-              let paramsIndex = search.indexOf(" ");
-              if (paramsIndex != -1)
-                params = search.substr(paramsIndex + 1);
-
-              // Emphasize the keyword parameters
-              this._setUpDescription(this._extra, params);
-
-              // Don't emphasize keyword searches in the title or url
-              emphasiseUrl = false;
-              emphasiseTitle = false;
-            } else {
-              // Don't show any description for non keyword types.
-              this._setUpDescription(this._extra, "", true);
-            }
-            // If the result has the type favicon and a known search provider,
-            // customize it the same way as a keyword result.
-            type = "keyword";
-          }
-
-          // Give the image the icon style and a special one for the type
-          this._typeImage.className = "ac-type-icon" +
-            (type ? " ac-result-type-" + type : "");
-
           // Show the domain as the title if we don't have a title.
-          if (title == "") {
+          if (!title) {
             title = displayUrl;
             try {
               let uri = Services.io.newURI(originalUrl, null, null);
               // Not all valid URLs have a domain.
               if (uri.host)
                 title = uri.host;
             } catch (e) {}
           }
 
-          // Emphasize the matching search terms for the description
-          if (Array.isArray(title))
-            this._setUpEmphasisedSections(this._title, title);
-          else
-            this._setUpDescription(this._title, title, !emphasiseTitle);
+          this._tags.setAttribute("empty", "true");
+
+          if (type == "tag" || type == "bookmark-tag") {
+            // The title is separated from the tags by an endash
+            let tags;
+            [, title, tags] = title.match(/^(.+) \u2013 (.+)$/);
+
+            // Each tag is split by a comma in an undefined order, so sort it
+            let sortedTags = tags.split(/\s*,\s*/).sort((a, b) => {
+              return a.localeCompare(a);
+            });
 
-          this._setUpDescription(this._url, displayUrl, !emphasiseUrl);
+            let anyTagsMatch = this._setUpTags(sortedTags);
+            if (anyTagsMatch) {
+              this._tags.removeAttribute("empty");
+            }
+            if (type == "bookmark-tag") {
+              type = "bookmark";
+            }
+          } else if (type == "keyword") {
+            // Note that this is a moz-action with action.type == keyword.
+            emphasiseUrl = false;
+            let keywordArg = this.getAttribute("text").replace(/^[^\s]+\s*/, "");
+            if (!keywordArg) {
+              // Treat keyword searches without arguments as visiturl actions.
+              type = "visiturl";
+              this.setAttribute("actiontype", "visiturl");
+              let visitStr = this._stringBundle.GetStringFromName("visit");
+              this._setUpDescription(this._actionText, visitStr, true);
+            } else {
+              let pairs = [[title, ""], [keywordArg, "match"]];
+              let interpStr =
+                this._stringBundle.GetStringFromName("bookmarkKeywordSearch");
+              title = this._generateEmphasisPairs(interpStr, pairs);
+              // The action box will be visible since this is a moz-action, but
+              // we want it to appear as if it were not visible, so set its text
+              // to the empty string.
+              this._setUpDescription(this._actionText, "", false);
+            }
+          }
 
-          // Set up overflow on a timeout because the contents of the box
-          // might not have a width yet even though we just changed them
-          setTimeout(this._setUpOverflow, 0, this._titleBox, this._titleOverflowEllipsis);
-          setTimeout(this._setUpOverflow, 0, this._urlBox, this._urlOverflowEllipsis);
+          this._typeIcon.setAttribute("type", type);
+          this._siteIcon.setAttribute("type", type);
+
+          if (Array.isArray(title)) {
+            this._setUpEmphasisedSections(this._titleText, title);
+          } else {
+            this._setUpDescription(this._titleText, title, false);
+          }
+          this._setUpDescription(this._urlText, displayUrl, !emphasiseUrl);
+
+          if (this._inOverflow) {
+            this._handleOverflow();
+          }
           ]]>
         </body>
       </method>
 
+      <!-- This method truncates the displayed strings as necessary. -->
+      <method name="_handleOverflow">
+        <body><![CDATA[
+          let titleRect = this._titleText.getBoundingClientRect();
+          let tagsRect = this._tagsText.getBoundingClientRect();
+          let urlRect = this._urlText.getBoundingClientRect();
+          let actionRect = this._actionText.getBoundingClientRect();
+          let urlActionWidth = Math.max(urlRect.width, actionRect.width);
+
+          // Total width for the title and URL/action is the width of the item
+          // minus the start of the title text minus a little extra padding.
+          // This extra padding amount is basically arbitrary but balances out
+          // the listbox's padding on the left side.
+          let extraPadding = 30;
+          let itemWidth =
+            this.parentNode.getBoundingClientRect().width -
+            this._titleText.getBoundingClientRect().left -
+            extraPadding;
+
+          if (this._tags.hasAttribute("empty")) {
+            // The tags box is not displayed in this case.
+            tagsRect.width = 0;
+          }
+
+          let titleTagsWidth = titleRect.width + tagsRect.width;
+          if (titleTagsWidth + urlActionWidth > itemWidth) {
+            // Title + tags + URL/action overflows the item width.
+
+            // The percentage of the item width allocated to the title and tags.
+            let titleTagsPct = 0.66;
+
+            let titleTagsAvailable = itemWidth - urlActionWidth;
+            let titleTagsMaxWidth = Math.max(
+              titleTagsAvailable,
+              itemWidth * titleTagsPct
+            );
+            if (titleTagsWidth > titleTagsMaxWidth) {
+              // Title + tags overflows the max title + tags width.
+
+              // The percentage of the title + tags width allocated to the
+              // title.
+              let titlePct = 0.33;
+
+              let titleAvailable = titleTagsMaxWidth - tagsRect.width;
+              let titleMaxWidth = Math.max(
+                titleAvailable,
+                titleTagsMaxWidth * titlePct
+              );
+              let tagsAvailable = titleTagsMaxWidth - titleRect.width;
+              let tagsMaxWidth = Math.max(
+                tagsAvailable,
+                titleTagsMaxWidth * (1 - titlePct)
+              );
+              this._titleText.style.maxWidth = titleMaxWidth + "px";
+              this._tagsText.style.maxWidth = tagsMaxWidth + "px";
+            }
+            let urlActionAvailable = itemWidth - titleTagsWidth;
+            let urlActionMaxWidth = Math.max(
+              urlActionAvailable,
+              itemWidth * (1 - titleTagsPct)
+            );
+            this._urlText.style.maxWidth = urlActionMaxWidth + "px";
+            this._actionText.style.maxWidth = urlActionMaxWidth + "px";
+          }
+        ]]></body>
+      </method>
+
       <method name="_parseActionUrl">
         <parameter name="aUrl"/>
         <body><![CDATA[
           if (!aUrl.startsWith("moz-action:"))
             return null;
 
           // URL is in the format moz-action:ACTION,PARAMS
           // Where PARAMS is a JSON encoded object.
@@ -1945,78 +2084,16 @@ extends="chrome://global/content/binding
             action.params = {
               url: params,
             }
           }
 
           return action;
         ]]></body>
       </method>
-
-      <method name="_setUpOverflow">
-        <parameter name="aParentBox"/>
-        <parameter name="aEllipsis"/>
-        <body>
-          <![CDATA[
-          // Hide the ellipsis incase there's just enough to not underflow
-          aEllipsis.style.visibility = "hidden";
-
-          // Start with the parent's width and subtract off its children
-          let tooltip = [];
-          let children = aParentBox.childNodes;
-          let widthDiff = aParentBox.boxObject.width;
-
-          for (let i = 0; i < children.length; i++) {
-            // Only consider a child if it actually takes up space
-            let childWidth = children[i].boxObject.width;
-            if (childWidth > 0) {
-              // Subtract a little less to account for subpixel rounding
-              widthDiff -= childWidth - .5;
-
-              // Add to the tooltip if it's not hidden and has text
-              let childText = children[i].textContent;
-              if (childText)
-                tooltip.push(childText);
-            }
-          }
-
-          // If the children take up more space than the parent.. overflow!
-          if (widthDiff < 0) {
-            // Re-show the ellipsis now that we know it's needed
-            aEllipsis.style.visibility = "visible";
-
-            // Separate text components with a ndash --
-            aParentBox.tooltipText = tooltip.join(" \u2013 ");
-          }
-          ]]>
-        </body>
-      </method>
-
-      <method name="_doUnderflow">
-        <parameter name="aName"/>
-        <body>
-          <![CDATA[
-          // Hide the ellipsis right when we know we're underflowing instead of
-          // waiting for the timeout to trigger the _setUpOverflow calculations
-          this[aName + "Box"].tooltipText = "";
-          this[aName + "OverflowEllipsis"].style.visibility = "hidden";
-          ]]>
-        </body>
-      </method>
-
-      <method name="_doOverflow">
-        <parameter name="aName"/>
-        <body>
-          <![CDATA[
-          this._setUpOverflow(this[aName + "Box"],
-                              this[aName + "OverflowEllipsis"]);
-          ]]>
-        </body>
-      </method>
-
     </implementation>
   </binding>
 
   <binding id="autocomplete-tree" extends="chrome://global/content/bindings/tree.xml#tree">
     <content>
       <children includes="treecols"/>
       <xul:treerows class="autocomplete-treerows tree-rows" xbl:inherits="hidescrollbar" flex="1">
         <children/>
--- a/toolkit/content/xul.css
+++ b/toolkit/content/xul.css
@@ -887,25 +887,16 @@ panel[type="autocomplete-richlistbox"] {
   display: none;
 }
 
 .autocomplete-history-dropmarker[enablehistory="true"] {
   display: -moz-box;
   -moz-binding: url("chrome://global/content/bindings/autocomplete.xml#history-dropmarker");
 }
 
-.ac-ellipsis-after {
-  visibility: hidden;
-}
-
-.ac-url-text[type~="action"],
-.ac-action-text:not([type~="action"]) {
-  visibility: collapse;
-}
-
 %endif
 
 
 
 /* the C++ implementation of widgets is too eager to make popups visible.
    this causes problems (bug 120155 and others), thus this workaround: */
 popup[type="autocomplete"][hidden="true"] {
   visibility: hidden;
--- a/toolkit/locales/en-US/chrome/global/autocomplete.properties
+++ b/toolkit/locales/en-US/chrome/global/autocomplete.properties
@@ -1,12 +1,23 @@
 # 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/.
 
 # LOCALIZATION NOTE (searchWithEngine): %S will be replaced with
 # the search engine provider's name. This format was chosen because
 # the provider can also end with "Search" (e.g.: MSN Search).
 searchWithEngine = Search with %S
-switchToTab = Switch to tab
-# LOCALIZATION NOTE (visitURL):
-# %S is the URL to visit.
-visitURL = Visit %S
+
+# LOCALIZATION NOTE (switchToTab2): This is the same as the older switchToTab
+# string that it's replacing, except it uses title case, so "Switch" and "Tab"
+# are capitalized.
+switchToTab2 = Switch to Tab
+
+# LOCALIZATION NOTE (visit): This is shown next to autocomplete entries that are
+# simple URLs or sites, which will be visited when the user selects them.
+visit = Visit
+
+# LOCALIZATION NOTE (bookmarkKeywordSearch): This is the title of autocomplete
+# entries that are bookmark keyword searches.  %1$S will be replaced with the
+# domain name of the bookmark, and %2$S will be replaced with the keyword
+# search text that the user is typing.  %2$S will not be empty.
+bookmarkKeywordSearch = %1$S: %2$S
--- a/toolkit/themes/linux/global/autocomplete.css
+++ b/toolkit/themes/linux/global/autocomplete.css
@@ -102,106 +102,76 @@ treechildren.autocomplete-treebody::-moz
 /* ::::: richlistbox autocomplete ::::: */
 
 .autocomplete-richlistbox {
   -moz-appearance: none;
   margin: 1px;
   background-color: transparent;
 }
 
-.autocomplete-richlistitem[selected="true"] {
-  background-color: Highlight;
-  color: HighlightText;
-}
-
 .autocomplete-richlistitem {
-  padding: 6px 2px;
   color: MenuText;
 }
 
-.ac-url-box {
-  /* When setting a vertical margin here, half of that needs to be added
-     .ac-title-box's translateY for when .ac-url-box is hidden (see below). */
-  margin-top: 1px;
-}
-
-.autocomplete-richlistitem[actiontype="keyword"] .ac-url-box,
-.autocomplete-richlistitem[actiontype="searchengine"] .ac-url-box,
-.autocomplete-richlistitem[actiontype="visiturl"] .ac-url-box,
-.autocomplete-richlistitem[type~="autofill"] .ac-url-box {
-  visibility: hidden;
-}
-
-.autocomplete-richlistitem[actiontype="keyword"] .ac-title-box,
-.autocomplete-richlistitem[actiontype="searchengine"] .ac-title-box,
-.autocomplete-richlistitem[actiontype="visiturl"] .ac-title-box,
-.autocomplete-richlistitem[type~="autofill"] .ac-title-box {
-  /* Center the title by moving it down by half of .ac-url-box's height,
-     including vertical margins (if any). */
-  transform: translateY(.5em);
-}
-
-.ac-site-icon {
-  width: 16px; 
-  height: 16px;
-  margin-bottom: -2px;
-  -moz-margin-start: 3px;
-  -moz-margin-end: 6px;
+.autocomplete-richlistitem[selected] {
+  color: HighlightText;
 }
 
 .ac-type-icon {
-  width: 16px; 
+  width: 16px;
+  height: 16px;
+  max-width: 16px;
+  max-height: 16px;
+  -moz-margin-start: 13px;
+  -moz-margin-end: 6px;
+}
+
+.ac-site-icon {
+  width: 16px;
   height: 16px;
-  -moz-margin-start: 6px;
+  max-width: 16px;
+  max-height: 16px;
+  -moz-margin-start: 0;
+  -moz-margin-end: 11px;
+}
+
+.ac-title {
+  -moz-margin-start: 0;
+  -moz-margin-end: 6px;
+}
+
+html|span.ac-tag {
+  -moz-margin-start: 0;
+  -moz-margin-end: 2px;
+}
+
+.ac-tags {
+  -moz-margin-start: 0;
   -moz-margin-end: 4px;
 }
 
-.ac-extra > .ac-result-type-tag {
-  margin: 0 4px;
-}
-
-.ac-extra > .ac-comment {
-  padding-right: 4px;
-}
-
-.ac-ellipsis-after {
-  margin: 0 !important;
-  padding: 0; 
-  min-width: 1em;
-}
-
-.ac-normal-text {
-  margin: 0 !important;
-  padding: 0;
+html|span.ac-title-urlaction-separator {
+  padding-left: 0;
+  padding-right: 6px;
 }
 
-.ac-normal-text > html|span {
-  margin: 0 !important;
-  padding: 0;
-}
-
-html|span.ac-emphasize-text {
-  box-shadow: inset 0 0 1px 1px rgba(0,0,0,0.1);
-  background-color: rgba(0,0,0,0.05);
-  border-radius: 2px;
-  text-shadow: 0 0 currentColor; /*faux bold effect*/
+/* Better align the URL/action with the title. */
+.ac-tags,
+.ac-url,
+.ac-action {
+  margin-bottom: -2px;
 }
 
-.ac-url-text > html|span.ac-emphasize-text,
-.ac-action-text > html|span.ac-emphasize-text {
-  box-shadow: none;
-}
-
-.ac-normal-text[selected="true"] > html|span.ac-emphasize-text {
-  box-shadow: inset 0 0 1px 1px rgba(255,255,255,0.3);
-  background-color: rgba(255,255,255,0.2);
-}
-
-.ac-title, .ac-url {
-  overflow: hidden;
+.ac-title-text,
+.ac-tags-text,
+.ac-url-text,
+.ac-action-text,
+.ac-text-overflow-container {
+  padding: 0 !important;
+  margin: 0 !important;
 }
 
 /* ::::: textboxes inside toolbarpaletteitems ::::: */
 
 toolbarpaletteitem > toolbaritem > textbox > hbox > hbox > html|*.textbox-input {
   visibility: hidden;
 }
 
--- a/toolkit/themes/osx/global/autocomplete.css
+++ b/toolkit/themes/osx/global/autocomplete.css
@@ -88,108 +88,68 @@ treechildren.autocomplete-treebody::-moz
 
 /* ::::: richlistbox autocomplete ::::: */
 
 .autocomplete-richlistbox {
   -moz-appearance: none;
   margin: 0;
 }
 
-.autocomplete-richlistitem[selected="true"] {
-  background-color: Highlight;
-  color: HighlightText;
-  background-image: linear-gradient(rgba(255,255,255,0.3), transparent);
-}
-
-.autocomplete-richlistitem {
-  padding: 5px 2px;
-}
-
-.ac-url-box {
-  /* When setting a vertical margin here, half of that needs to be added
-     .ac-title-box's translateY for when .ac-url-box is hidden (see below). */
-  margin-top: 1px;
-}
-
-.autocomplete-richlistitem[actiontype="keyword"] .ac-url-box,
-.autocomplete-richlistitem[actiontype="searchengine"] .ac-url-box,
-.autocomplete-richlistitem[actiontype="visiturl"] .ac-url-box,
-.autocomplete-richlistitem[type~="autofill"] .ac-url-box {
-  visibility: hidden;
-}
-
-.autocomplete-richlistitem[actiontype="keyword"] .ac-title-box,
-.autocomplete-richlistitem[actiontype="searchengine"] .ac-title-box,
-.autocomplete-richlistitem[actiontype="visiturl"] .ac-title-box,
-.autocomplete-richlistitem[type~="autofill"] .ac-title-box {
-  /* Center the title by moving it down by half of .ac-url-box's height,
-     including vertical margins (if any). */
-  transform: translateY(.5em);
-}
-
-.ac-site-icon {
-  width: 16px; 
-  height: 16px;
-  margin-bottom: -1px;
-  -moz-margin-start: 7px;
-  -moz-margin-end: 5px;
-}
-
 .ac-type-icon {
   width: 16px;
   height: 16px;
-  -moz-margin-start: 6px;
+  max-width: 16px;
+  max-height: 16px;
+  -moz-margin-start: 16px;
+  -moz-margin-end: 6px;
+}
+
+.ac-site-icon {
+  width: 16px;
+  height: 16px;
+  max-width: 16px;
+  max-height: 16px;
+  -moz-margin-start: 0;
+  -moz-margin-end: 11px;
+}
+
+.ac-title {
+  -moz-margin-start: 0;
+  -moz-margin-end: 6px;
+}
+
+html|span.ac-tag {
+  -moz-margin-start: 0;
+  -moz-margin-end: 2px;
+}
+
+.ac-tags {
+  -moz-margin-start: 0;
   -moz-margin-end: 4px;
 }
 
-.ac-url-box > .ac-site-icon,
-.ac-url-box > .ac-type-icon {
-  /* Otherwise the spacer is big enough to stretch its container */
-  height: auto;
-}
-
-.ac-extra > .ac-result-type-tag {
-  margin: 0 4px;
-}
-
-.ac-extra > .ac-comment {
-  padding-right: 4px;
-}
-
-.ac-ellipsis-after {
-  margin: 0 !important;
-  padding: 0; 
-  min-width: 1.1em;
+html|span.ac-title-urlaction-separator {
+  -moz-margin-start: 0;
+  -moz-margin-end: 6px;
 }
 
-.ac-normal-text {
-  margin: 0 !important;
-  padding: 0;
-}
-
-.ac-normal-text > html|span {
-  margin: 0 !important;
-  padding: 0;
+/* Better align the URL/action with the title. */
+.ac-tags,
+.ac-url,
+.ac-action {
+  margin-bottom: -2px;
 }
 
-html|span.ac-emphasize-text {
-  box-shadow: inset 0 0 1px 1px rgba(208,208,208,0.4);
-  background-color: rgba(208,208,208,0.2);
-  border-radius: 2px;
-  text-shadow: 0 0 currentColor;
-}
-
-.ac-url-text > html|span.ac-emphasize-text,
-.ac-action-text > html|span.ac-emphasize-text {
-  box-shadow: inset 0 0 1px 1px rgba(183,210,226,0.4);
-  background-color: rgba(183,210,226,0.3);
-}
-
-.ac-title, .ac-url {
-  overflow: hidden;
+.ac-title-text,
+.ac-tags-text,
+.ac-url-text,
+.ac-action-text,
+.ac-text-overflow-container {
+  padding: 0 !important;
+  margin: 0 !important;
 }
 
 /* ::::: textboxes inside toolbarpaletteitems ::::: */
 
 toolbarpaletteitem > toolbaritem > textbox > hbox > hbox > html|*.textbox-input {
   visibility: hidden;
 }
 
--- a/toolkit/themes/windows/global/autocomplete.css
+++ b/toolkit/themes/windows/global/autocomplete.css
@@ -92,143 +92,68 @@ treechildren.autocomplete-treebody::-moz
 
 /* ::::: richlistbox autocomplete ::::: */
 
 .autocomplete-richlistbox {
   -moz-appearance: none;
   margin: 0;
 }
 
-.autocomplete-richlistitem {
-  padding: 1px;
-}
-
-.autocomplete-richlistitem[selected="true"] {
-  background-color: Highlight;
-  color: HighlightText;
-}
-
-%ifdef XP_WIN
-@media (-moz-os-version: windows-vista) and (-moz-windows-default-theme),
-       (-moz-os-version: windows-win7) and (-moz-windows-default-theme) {
-  .autocomplete-richlistitem[selected="true"] {
-    color: inherit;
-    background-color: transparent;
-    /* four gradients for the bevel highlights on each edge, one for blue background */
-    background-image:
-      linear-gradient(to bottom, rgba(255,255,255,0.9) 3px, transparent 3px),
-      linear-gradient(to right, rgba(255,255,255,0.5) 3px, transparent 3px),
-      linear-gradient(to left, rgba(255,255,255,0.5) 3px, transparent 3px),
-      linear-gradient(to top, rgba(255,255,255,0.4) 3px, transparent 3px),
-      linear-gradient(to bottom, rgba(163,196,247,0.3), rgba(122,180,246,0.3));
-    background-clip: content-box;
-    border-radius: 6px;
-    outline: 1px solid rgb(124,163,206);
-    -moz-outline-radius: 3px;
-    outline-offset: -2px;
-  }
-}
-%endif
-
-.ac-title-box {
-  margin-top: 4px;
-}
-
-.ac-url-box {
-  /* When setting a vertical margin here, half of that needs to be added
-     .ac-title-box's translateY for when .ac-url-box is hidden (see below). */
-  margin: 1px 0 4px;
-}
-
-.autocomplete-richlistitem[actiontype="keyword"] .ac-url-box,
-.autocomplete-richlistitem[actiontype="searchengine"] .ac-url-box,
-.autocomplete-richlistitem[actiontype="visiturl"] .ac-url-box,
-.autocomplete-richlistitem[type~="autofill"] .ac-url-box {
-  visibility: hidden;
-}
-
-.autocomplete-richlistitem[actiontype="keyword"] .ac-title-box,
-.autocomplete-richlistitem[actiontype="searchengine"] .ac-title-box,
-.autocomplete-richlistitem[actiontype="visiturl"] .ac-title-box,
-.autocomplete-richlistitem[type~="autofill"] .ac-title-box {
-  /* Center the title by moving it down by half of .ac-url-box's height,
-     including vertical margins (if any). */
-  transform: translateY(calc(.5em + 2px));
+.ac-type-icon {
+  width: 16px;
+  height: 16px;
+  max-width: 16px;
+  max-height: 16px;
+  -moz-margin-start: 14px;
+  -moz-margin-end: 6px;
 }
 
 .ac-site-icon {
-  width: 16px; 
+  width: 16px;
   height: 16px;
-  margin: 0 5px -2px;
-}
-
-.ac-type-icon {
-  width: 16px; 
-  height: 16px;
-  -moz-margin-start: 6px;
-  -moz-margin-end: 4px;
-  margin-bottom: -1px;
+  max-width: 16px;
+  max-height: 16px;
+  -moz-margin-start: 0;
+  -moz-margin-end: 11px;
 }
 
-.ac-url-box > .ac-site-icon,
-.ac-url-box > .ac-type-icon {
-  /* Otherwise the spacer is big enough to stretch its container */
-  height: auto;
+.ac-title {
+  -moz-margin-start: 0;
+  -moz-margin-end: 6px;
 }
 
-.ac-extra > .ac-result-type-tag {
-  margin: 0 4px;
-}
-
-.ac-extra > .ac-comment {
-  padding-right: 4px;
-}
-
-.ac-ellipsis-after {
-  margin: 0 !important;
-  padding: 0; 
-  min-width: 1em;
+html|span.ac-tag {
+  -moz-margin-start: 0;
+  -moz-margin-end: 2px;
 }
 
-.ac-normal-text {
-  margin: 0 !important;
-  padding: 0;
+.ac-tags {
+  -moz-margin-start: 0;
+  -moz-margin-end: 4px;
 }
 
-.ac-normal-text > html|span {
-  margin: 0 !important;
-  padding: 0;
-}
-
-html|span.ac-emphasize-text {
-  box-shadow: inset 0 0 1px 1px rgba(208,208,208,0.5);
-  background-color: rgba(208,208,208,0.3);
-  border-radius: 2px;
-  text-shadow: 0 0 currentColor;
+html|span.ac-title-urlaction-separator {
+  padding-left: 0;
+  padding-right: 6px;
 }
 
-@media (-moz-windows-default-theme) {
-  @media not all and (-moz-os-version: windows-xp) {
-    html|span.ac-emphasize-text {
-      box-shadow: inset 0 0 1px 1px rgba(0,0,0,0.1);
-      background-color: rgba(0,0,0,0.05);
-    }
-  }
-
-  @media (-moz-os-version: windows-xp) {
-    .ac-url-text > html|span.ac-emphasize-text,
-    .ac-action-text > html|span.ac-emphasize-text {
-      box-shadow: inset 0 0 1px 1px rgba(202,214,201,0.3);
-      background-color: rgba(202,214,201,0.2);
-    }
-  }
+/* Better align the URL/action with the title. */
+.ac-tags,
+.ac-url,
+.ac-action {
+  margin-bottom: -2px;
 }
 
-.ac-title, .ac-url {
-  overflow: hidden;
+.ac-title-text,
+.ac-tags-text,
+.ac-url-text,
+.ac-action-text,
+.ac-text-overflow-container {
+  padding: 0 !important;
+  margin: 0 !important;
 }
 
 /* ::::: textboxes inside toolbarpaletteitems ::::: */
 
 toolbarpaletteitem > toolbaritem > textbox > hbox > hbox > html|*.textbox-input {
   visibility: hidden;
 }
 
--- a/toolkit/themes/windows/global/jar.mn
+++ b/toolkit/themes/windows/global/jar.mn
@@ -1,16 +1,16 @@
 # 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/.
 
 #include ../../shared/non-mac.jar.inc.mn
 
 toolkit.jar:
-* skin/classic/global/autocomplete.css
+  skin/classic/global/autocomplete.css
 #ifndef MOZ_THEME_FASTSTRIPE
   skin/classic/global/button.css
   skin/classic/global/checkbox.css
   skin/classic/global/dropmarker.css
   skin/classic/global/groupbox.css
 * skin/classic/global/menu.css
   skin/classic/global/menulist.css
 * skin/classic/global/popup.css