Bug 1181078 - Implement new awesomebar design. r=mak draft
authorDrew Willcoxon <adw@mozilla.com>
Tue, 05 Apr 2016 14:56:39 -0700
changeset 347816 6ce7ceff183163dd48399782f6b531a7a56f6d20
parent 347619 7eebd9f0738af3900c95d8583ab54d6166a9b1d2
child 517716 016c96dff84cb2323b8954d619f50d876bbfd0cb
push id14675
push userdwillcoxon@mozilla.com
push dateTue, 05 Apr 2016 22:02:39 +0000
reviewersmak
bugs1181078
milestone48.0a1
Bug 1181078 - Implement new awesomebar design. r=mak Based on an earlier patch by Marco Bonardo <mbonardo@mozilla.com>. MozReview-Commit-ID: K7VsLIB4IyL
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/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/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