Bug 1176437 - Don't show fallback "Search with" result when the awesomebar is empty. r=mak
authorDrew Willcoxon <adw@mozilla.com>
Fri, 07 Aug 2015 15:00:08 -0700
changeset 288567 907891e1c2112a703439aa885d3e47ac905878ff
parent 288566 152eafb9dc89a1d37ca5e24ee19d1987a66f1201
child 288568 2e33ac0bb341812efa0f1a7e49837c9eced1f5ed
push id5067
push userraliiev@mozilla.com
push dateMon, 21 Sep 2015 14:04:52 +0000
treeherdermozilla-beta@14221ffe5b2f [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmak
bugs1176437
milestone42.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1176437 - Don't show fallback "Search with" result when the awesomebar is empty. r=mak
browser/base/content/test/general/browser_action_keyword.js
browser/base/content/urlbarBindings.xml
toolkit/components/places/UnifiedComplete.js
toolkit/components/places/tests/unifiedcomplete/head_autocomplete.js
toolkit/components/places/tests/unifiedcomplete/test_autoFill_default_behavior.js
toolkit/components/places/tests/unifiedcomplete/test_dupe_urls.js
toolkit/components/places/tests/unifiedcomplete/test_empty_search.js
toolkit/components/places/tests/unifiedcomplete/test_keyword_search.js
toolkit/components/places/tests/unifiedcomplete/test_keyword_search_actions.js
toolkit/components/places/tests/unifiedcomplete/test_searchEngine_alias.js
toolkit/components/places/tests/unifiedcomplete/test_searchEngine_current.js
toolkit/components/places/tests/unifiedcomplete/test_tabmatches.js
toolkit/components/places/tests/unifiedcomplete/test_visiturl.js
toolkit/content/widgets/autocomplete.xml
--- a/browser/base/content/test/general/browser_action_keyword.js
+++ b/browser/base/content/test/general/browser_action_keyword.js
@@ -33,17 +33,19 @@ add_task(function*() {
                                                 url: "http://example.com/?q=%s",
                                                 title: "test" });
   yield PlacesUtils.keywords.insert({ keyword: "keyword",
                                       url: "http://example.com/?q=%s" });
 
   let result = yield promise_first_result("keyword something");
   isnot(result, null, "Expect a keyword result");
 
-  is(result.getAttribute("type"), "action keyword", "Expect correct  `type` attribute");
+  let types = new Set(result.getAttribute("type").split(/\s+/));
+  Assert.ok(types.has("action"));
+  Assert.ok(types.has("keyword"));
   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");
 
--- a/browser/base/content/urlbarBindings.xml
+++ b/browser/base/content/urlbarBindings.xml
@@ -763,18 +763,24 @@ file, You can obtain one at http://mozil
               }
             } else {
               val = losslessDecodeURI(uri);
             }
           }
 
           // Trim popup selected values, but never trim results coming from
           // autofill.
+          let styles = new Set(
+            this.popup.selectedIndex == -1 ? [] :
+            this.mController.getStyleAt(this.popup.selectedIndex).split(/\s+/)
+          );
           if (this.popup.selectedIndex == -1 ||
-              this.mController.getStyleAt(this.popup.selectedIndex) == "autofill") {
+              this.mController
+                  .getStyleAt(this.popup.selectedIndex)
+                  .split(/\s+/).indexOf("autofill") >= 0) {
             this._disableTrim = true;
           }
           this.value = val;
           this._disableTrim = false;
 
           // Completing a result should simulate the user typing the result, so
           // fire an input event.
           let evt = document.createEvent("UIEvents");
@@ -1581,17 +1587,23 @@ file, You can obtain one at http://mozil
 
           let newIndex = index + (reverse ? -1 : 1) * amount;
 
           // We only want to wrap if navigation is in any direction by one item,
           // otherwise we clamp to one end of the list.
           // ie, hitting page-down will only cause is to wrap if we're already
           // at one end of the list.
 
-          if (!Services.prefs.getBoolPref("browser.urlbar.unifiedcomplete")) {
+          // Do not allow the selection to be removed if UnifiedComplete is
+          // enabled and the popup's first result is a heuristic result.
+          if (!Services.prefs.getBoolPref("browser.urlbar.unifiedcomplete") ||
+              (this.input.mController.matchCount > 0 &&
+               this.input.mController
+                         .getStyleAt(0)
+                         .split(/\s+/).indexOf("heuristic") == -1)) {
             if (reverse && index == -1 || newIndex > maxRow && index != maxRow)
               newIndex = maxRow;
             else if (!reverse && index == -1 || newIndex < 0 && index != 0)
               newIndex = 0;
 
             if (newIndex < 0 && index == 0 || newIndex > maxRow && index == maxRow)
               newIndex = -1;
 
@@ -1787,45 +1799,48 @@ file, You can obtain one at http://mozil
           }
         ]]>
         </body>
       </method>
 
       <method name="createResultLabel">
         <parameter name="aTitle"/>
         <parameter name="aUrl"/>
-        <parameter name="aType"/>
+        <parameter name="aTypes"/>
         <body>
           <![CDATA[
             let label = aTitle + " " + aUrl;
-            // convert aType (ex: "ac-result-type-<aType>") to text to be spoke aloud
-            // by screen readers.  convert "tag" and "bookmark" to the localized versions,
-            // but don't do anything for "favicon" (the default)
+            let type = aTypes.find(v => v != "action" && v != "heuristic");
             try {
-              label += " " + this._bundle.GetStringFromName(aType + "ResultLabel");
-            } catch (e) {
-              // Undefined result label, do nothing.
-            }
+              // Some types intentionally do not map to strings, which is not
+              // an error.
+              label += " " + this._bundle.GetStringFromName(type + "ResultLabel");
+            } catch (e) {}
 
             return label;
           ]]>
         </body>
       </method>
 
       <method name="onResultsAdded">
         <body>
           <![CDATA[
             if (!Services.prefs.getBoolPref("browser.urlbar.unifiedcomplete"))
               return;
 
+            // If nothing is selected yet, select the first result if it is a
+            // pre-selected "heuristic" result.  (See UnifiedComplete.js.)
             if (this._matchCount > 0 && this.selectedIndex == -1) {
-              // Don't handle this as a user-initiated action.
-              this._ignoreNextSelect = true;
-              this.selectedIndex = 0;
-              this._ignoreNextSelect = false;
+              let styles = this.input.mController.getStyleAt(0).split(/\s+/);
+              if (styles.indexOf("heuristic") >= 0) {
+                // Don't handle this as a user-initiated action.
+                this._ignoreNextSelect = true;
+                this.selectedIndex = 0;
+                this._ignoreNextSelect = false;
+              }
             }
 
             this.input.gotResultForCurrentQuery = true;
             if (this.input.handleEnterWhenGotResult) {
               this.input.handleEnterWhenGotResult = false;
               this.input.mController.handleEnter(false);
             }
           ]]>
--- a/toolkit/components/places/UnifiedComplete.js
+++ b/toolkit/components/places/UnifiedComplete.js
@@ -842,62 +842,22 @@ Search.prototype = {
 
     // "openpage" behavior is supported by the default query.
     // _switchToTabQuery instead returns only pages not supported by history.
     if (this.hasBehavior("openpage")) {
       queries.push(this._switchToTabQuery);
     }
     queries.push(this._searchQuery);
 
-    // When actions are enabled, we run a series of heuristics to determine what
-    // the first result should be - which is always a special result.
-    // |hasFirstResult| is used to keep track of whether we've obtained such a
-    // result yet, so we can skip further heuristics and not add any additional
-    // special results.
-    let hasFirstResult = false;
-
-    if (this._searchTokens.length > 0) {
-      // This may be a Places keyword.
-      hasFirstResult = yield this._matchPlacesKeyword();
-    }
-
-    if (this.pending && this._enableActions && !hasFirstResult) {
-      // If it's not a Places keyword, then it may be a search engine
-      // with an alias - which works like a keyword.
-      hasFirstResult = yield this._matchSearchEngineAlias();
-    }
-
-    let shouldAutofill = this._shouldAutofill;
-    if (this.pending && !hasFirstResult && shouldAutofill) {
-      // It may also look like a URL we know from the database.
-      hasFirstResult = yield this._matchKnownUrl(conn);
-    }
-
-    if (this.pending && !hasFirstResult && shouldAutofill) {
-      // Or it may look like a URL we know about from search engines.
-      hasFirstResult = yield this._matchSearchEngineUrl();
-    }
-
-    if (this.pending && this._enableActions && !hasFirstResult) {
-      // If we don't have a result that matches what we know about, then
-      // we use a fallback for things we don't know about.
-
-      // We may not have auto-filled, but this may still look like a URL.
-      // However, even if the input is a valid URL, we may not want to use
-      // it as such. This can happen if the host would require whitelisting,
-      // but isn't in the whitelist.
-      hasFirstResult = yield this._matchUnknownUrl();
-    }
-
-    if (this.pending && this._enableActions && !hasFirstResult) {
-      // When all else fails, we search using the current search engine.
-      hasFirstResult = yield this._matchCurrentSearchEngine();
-    }
-
-    // IMPORTANT: No other first result heuristics should run after this point.
+    // Add the first heuristic result, if any.  Set _addingHeuristicFirstMatch
+    // to true so that when the result is added, "heuristic" can be included in
+    // its style.
+    this._addingHeuristicFirstMatch = true;
+    yield this._matchFirstHeuristicResult(conn);
+    this._addingHeuristicFirstMatch = false;
 
     yield this._sleep(Prefs.delay);
     if (!this.pending)
       return;
 
     yield this._matchSearchSuggestions();
     if (!this.pending)
       return;
@@ -921,16 +881,78 @@ Search.prototype = {
           return;
       }
     }
 
     // Ensure to fill any remaining space.
     yield Promise.all(this._remoteMatchesPromises);
   }),
 
+  *_matchFirstHeuristicResult(conn) {
+    // We always try to make the first result a special "heuristic" result.  The
+    // heuristics below determine what type of result it will be, if any.
+
+    if (this._searchTokens.length > 0) {
+      // This may be a Places keyword.
+      let matched = yield this._matchPlacesKeyword();
+      if (matched) {
+        return;
+      }
+    }
+
+    if (this.pending && this._enableActions) {
+      // If it's not a Places keyword, then it may be a search engine
+      // with an alias - which works like a keyword.
+      let matched = yield this._matchSearchEngineAlias();
+      if (matched) {
+        return;
+      }
+    }
+
+    let shouldAutofill = this._shouldAutofill;
+    if (this.pending && shouldAutofill) {
+      // It may also look like a URL we know from the database.
+      let matched = yield this._matchKnownUrl(conn);
+      if (matched) {
+        return;
+      }
+    }
+
+    if (this.pending && shouldAutofill) {
+      // Or it may look like a URL we know about from search engines.
+      let matched = yield this._matchSearchEngineUrl();
+      if (matched) {
+        return;
+      }
+    }
+
+    if (this.pending && this._enableActions) {
+      // If we don't have a result that matches what we know about, then
+      // we use a fallback for things we don't know about.
+
+      // We may not have auto-filled, but this may still look like a URL.
+      // However, even if the input is a valid URL, we may not want to use
+      // it as such. This can happen if the host would require whitelisting,
+      // but isn't in the whitelist.
+      let matched = yield this._matchUnknownUrl();
+      if (matched) {
+        return;
+      }
+    }
+
+    if (this.pending && this._enableActions && this._originalSearchString) {
+      // When all else fails, and the search string is non-empty, we search
+      // using the current search engine.
+      let matched = yield this._matchCurrentSearchEngine();
+      if (matched) {
+        return;
+      }
+    }
+  },
+
   *_matchSearchSuggestions() {
     // Limit the string sent for search suggestions to a maximum length.
     let searchString = this._searchTokens.join(" ")
                            .substr(0, Prefs.maxCharsForSearchSuggestions);
     // Avoid fetching suggestions if they are not required, private browsing
     // mode is enabled, or the search string may expose sensitive information.
     if (!this.hasBehavior("searches") || this._inPrivateWindow ||
         this._prohibitSearchSuggestionsFor(searchString)) {
@@ -1293,16 +1315,20 @@ Search.prototype = {
 
     match.style = match.style || "favicon";
 
     // Restyle past searches, unless they are bookmarks or special results.
     if (Prefs.restyleSearches && match.style == "favicon") {
       this._maybeRestyleSearchMatch(match);
     }
 
+    if (this._addingHeuristicFirstMatch) {
+      match.style += " heuristic";
+    }
+
     match.icon = match.icon || PlacesUtils.favicons.defaultFavicon.spec;
     match.finalCompleteValue = match.finalCompleteValue || "";
 
     this._result.insertMatchAt(this._getInsertIndexForMatch(match),
                                match.value,
                                match.comment,
                                match.icon,
                                match.style,
@@ -1571,17 +1597,17 @@ Search.prototype = {
   /**
    * Whether we should try to autoFill.
    */
   get _shouldAutofill() {
     // First of all, check for the autoFill pref.
     if (!Prefs.autofill)
       return false;
 
-    if (!this._searchTokens.length == 1)
+    if (this._searchTokens.length != 1)
       return false;
 
     // autoFill can only cope with history or bookmarks entries.
     if (!this.hasBehavior("history") &&
         !this.hasBehavior("bookmark"))
       return false;
 
     // autoFill doesn't search titles or tags.
--- a/toolkit/components/places/tests/unifiedcomplete/head_autocomplete.js
+++ b/toolkit/components/places/tests/unifiedcomplete/head_autocomplete.js
@@ -357,37 +357,45 @@ function makeSearchMatch(input, extra = 
   // in the object passed to makeActionURI is important for check_autocomplete
   // to match them :(
   let params = {
     engineName: extra.engineName || "MozSearch",
     input,
     searchQuery: "searchQuery" in extra ? extra.searchQuery : input,
     alias: extra.alias, // may be undefined which is expected.
   }
+  let style = [ "action", "searchengine" ];
+  if (extra.heuristic) {
+    style.push("heuristic");
+  }
   return {
     uri: makeActionURI("searchengine", params),
     title: params.engineName,
-    style: [ "action", "searchengine" ],
+    style,
   }
 }
 
 // Creates a full "match" entry for a search result, suitable for passing as
 // an entry to check_autocomplete.
 function makeVisitMatch(input, url, extra = {}) {
   // Note that counter-intuitively, the order the object properties are defined
   // in the object passed to makeActionURI is important for check_autocomplete
   // to match them :(
   let params = {
     url,
     input,
   }
+  let style = [ "action", "visiturl" ];
+  if (extra.heuristic) {
+    style.push("heuristic");
+  }
   return {
     uri: makeActionURI("visiturl", params),
     title: extra.title || url,
-    style: [ "action", "visiturl" ],
+    style,
   }
 }
 
 function makeSwitchToTabMatch(url, extra = {}) {
   return {
     uri: makeActionURI("switchtab", {url}),
     title: extra.title || url,
     style: [ "action", "switchtab" ],
--- a/toolkit/components/places/tests/unifiedcomplete/test_autoFill_default_behavior.js
+++ b/toolkit/components/places/tests/unifiedcomplete/test_autoFill_default_behavior.js
@@ -36,17 +36,17 @@ add_task(function* test_default_behavior
     matches: [ { uri: uri2, title: "visited" } ],
     autofilled: "vi",
     completed: "vi"
   });
 
   do_print("Restrict history, typed visit, should autoFill");
   yield check_autocomplete({
     search: "ty",
-    matches: [ { uri: uri1, title: "typed", style: [ "autofill" ],
+    matches: [ { uri: uri1, title: "typed", style: [ "autofill", "heuristic" ],
                  icon: "chrome://global/skin/icons/information-16.png" } ],
     autofilled: "typed/",
     completed: "typed/"
   });
 
   // Don't autoFill this one cause it's not typed.
   do_print("Restrict history, bookmark, should not autoFill");
   yield check_autocomplete({
@@ -55,39 +55,39 @@ add_task(function* test_default_behavior
     autofilled: "bo",
     completed: "bo"
   });
 
   // Note we don't show this one cause it's not typed.
   do_print("Restrict history, typed bookmark, should autoFill");
   yield check_autocomplete({
     search: "tp",
-    matches: [ { uri: uri4, title: "tpbk", style: [ "autofill" ] } ],
+    matches: [ { uri: uri4, title: "tpbk", style: [ "autofill", "heuristic" ] } ],
     autofilled: "tpbk/",
     completed: "tpbk/"
   });
 
   Services.prefs.setBoolPref("browser.urlbar.autoFill.typed", false);
 
   // We are not restricting on typed, so we autoFill the bookmark even if we
   // are restricted to history.  We accept that cause not doing that
   // would be a perf hit and the privacy implications are very weak.
   do_print("Restrict history, bookmark, autoFill.typed = false, should autoFill");
   yield check_autocomplete({
     search: "bo",
-    matches: [ { uri: uri3, title: "bookmarked", style: [ "bookmark" ], style: [ "autofill" ],
+    matches: [ { uri: uri3, title: "bookmarked", style: [ "bookmark" ], style: [ "autofill", "heuristic" ],
                  icon: "chrome://global/skin/icons/error-16.png" } ],
     autofilled: "bookmarked/",
     completed: "bookmarked/"
   });
 
   do_print("Restrict history, common visit, autoFill.typed = false, should autoFill");
   yield check_autocomplete({
     search: "vi",
-    matches: [ { uri: uri2, title: "visited", style: [ "autofill" ] } ],
+    matches: [ { uri: uri2, title: "visited", style: [ "autofill", "heuristic" ] } ],
     autofilled: "visited/",
     completed: "visited/"
   });
 
   // RESTRICT TO TYPED.
   // This should basically ignore autoFill.typed and acts as if it would be set.
   Services.prefs.setBoolPref("browser.urlbar.suggest.history.onlyTyped", true);
 
@@ -98,34 +98,34 @@ add_task(function* test_default_behavior
     matches: [ ],
     autofilled: "vi",
     completed: "vi"
   });
 
   do_print("Restrict typed, typed visit, autofill.typed = false, should autoFill");
   yield check_autocomplete({
     search: "ty",
-    matches: [ { uri: uri1, title: "typed", style: [ "autofill" ],
+    matches: [ { uri: uri1, title: "typed", style: [ "autofill", "heuristic" ],
                  icon: "chrome://global/skin/icons/information-16.png"} ],
     autofilled: "typed/",
     completed: "typed/"
   });
 
   do_print("Restrict typed, bookmark, autofill.typed = false, should not autoFill");
   yield check_autocomplete({
     search: "bo",
     matches: [ ],
     autofilled: "bo",
     completed: "bo"
   });
 
   do_print("Restrict typed, typed bookmark, autofill.typed = false, should autoFill");
   yield check_autocomplete({
     search: "tp",
-    matches: [ { uri: uri4, title: "tpbk", style: [ "autofill" ] } ],
+    matches: [ { uri: uri4, title: "tpbk", style: [ "autofill", "heuristic" ] } ],
     autofilled: "tpbk/",
     completed: "tpbk/"
   });
 
   // RESTRICT BOOKMARKS.
   Services.prefs.setBoolPref("browser.urlbar.suggest.history", false);
   Services.prefs.setBoolPref("browser.urlbar.suggest.bookmark", true);
   Services.prefs.setBoolPref("browser.urlbar.autoFill.typed", true);
@@ -155,27 +155,27 @@ add_task(function* test_default_behavior
     autofilled: "bo",
     completed: "bo"
   });
 
   // Note we don't show this one cause it's not typed.
   do_print("Restrict bookmarks, typed bookmark, should autoFill");
   yield check_autocomplete({
     search: "tp",
-    matches: [ { uri: uri4, title: "tpbk", style: [ "autofill" ] } ],
+    matches: [ { uri: uri4, title: "tpbk", style: [ "autofill", "heuristic" ] } ],
     autofilled: "tpbk/",
     completed: "tpbk/"
   });
 
   Services.prefs.setBoolPref("browser.urlbar.autoFill.typed", false);
 
   do_print("Restrict bookmarks, bookmark, autofill.typed = false, should autoFill");
   yield check_autocomplete({
     search: "bo",
-    matches: [ { uri: uri3, title: "bookmarked", style: [ "autofill" ],
+    matches: [ { uri: uri3, title: "bookmarked", style: [ "autofill", "heuristic" ],
                  icon: "chrome://global/skin/icons/error-16.png" } ],
     autofilled: "bookmarked/",
     completed: "bookmarked/"
   });
 
   // Don't autofill because it's a title.
   do_print("Restrict bookmarks, title, autofill.typed = false, should not autoFill");
   yield check_autocomplete({
@@ -227,17 +227,17 @@ add_task(function* test_default_behavior
     matches: [ { uri: uri2, title: "visited" } ],
     autofilled: "visited/v",
     completed: "visited/v"
   });
 
   do_print("URL: Restrict history, typed visit, should autoFill");
   yield check_autocomplete({
     search: "typed/t",
-    matches: [ { uri: uri1, title: "typed/ty/", style: [ "autofill" ],
+    matches: [ { uri: uri1, title: "typed/ty/", style: [ "autofill", "heuristic" ],
                  icon: "chrome://global/skin/icons/information-16.png"} ],
     autofilled: "typed/ty/",
     completed: "http://typed/ty/"
   });
 
   // Don't autoFill this one cause it's not typed.
   do_print("URL: Restrict history, bookmark, should not autoFill");
   yield check_autocomplete({
@@ -246,17 +246,17 @@ add_task(function* test_default_behavior
     autofilled: "bookmarked/b",
     completed: "bookmarked/b"
   });
 
   // Note we don't show this one cause it's not typed.
   do_print("URL: Restrict history, typed bookmark, should autoFill");
   yield check_autocomplete({
     search: "tpbk/t",
-    matches: [ { uri: uri4, title: "tpbk/tp/", style: [ "autofill" ] } ],
+    matches: [ { uri: uri4, title: "tpbk/tp/", style: [ "autofill", "heuristic" ] } ],
     autofilled: "tpbk/tp/",
     completed: "http://tpbk/tp/"
   });
 
   // RESTRICT BOOKMARKS.
   Services.prefs.setBoolPref("browser.urlbar.suggest.history", false);
   Services.prefs.setBoolPref("browser.urlbar.suggest.bookmark", true);
 
@@ -285,26 +285,26 @@ add_task(function* test_default_behavior
     autofilled: "bookmarked/b",
     completed: "bookmarked/b"
   });
 
   // Note we don't show this one cause it's not typed.
   do_print("URL: Restrict bookmarks, typed bookmark, should autoFill");
   yield check_autocomplete({
     search: "tpbk/t",
-    matches: [ { uri: uri4, title: "tpbk/tp/", style: [ "autofill" ] } ],
+    matches: [ { uri: uri4, title: "tpbk/tp/", style: [ "autofill", "heuristic" ] } ],
     autofilled: "tpbk/tp/",
     completed: "http://tpbk/tp/"
   });
 
   Services.prefs.setBoolPref("browser.urlbar.autoFill.typed", false);
 
   do_print("URL: Restrict bookmarks, bookmark, autofill.typed = false, should autoFill");
   yield check_autocomplete({
     search: "bookmarked/b",
-    matches: [ { uri: uri3, title: "bookmarked/bo/", style: [ "autofill" ],
+    matches: [ { uri: uri3, title: "bookmarked/bo/", style: [ "autofill", "heuristic" ],
                  icon: "chrome://global/skin/icons/error-16.png" } ],
     autofilled: "bookmarked/bo/",
     completed: "http://bookmarked/bo/"
   });
 
   yield cleanup();
 });
--- a/toolkit/components/places/tests/unifiedcomplete/test_dupe_urls.js
+++ b/toolkit/components/places/tests/unifiedcomplete/test_dupe_urls.js
@@ -12,12 +12,12 @@ add_task(function* test_dupe_urls() {
     uri: NetUtil.newURI("http://mozilla.org/?")
   });
   yield check_autocomplete({
     search: "moz",
     autofilled: "mozilla.org/",
     completed:  "mozilla.org/",
     matches: [ { uri: NetUtil.newURI("http://mozilla.org/"),
                  title: "mozilla.org",
-                 style: [ "autofill" ] } ]
+                 style: [ "autofill", "heuristic" ] } ]
   });
   yield cleanup();
 });
--- a/toolkit/components/places/tests/unifiedcomplete/test_empty_search.js
+++ b/toolkit/components/places/tests/unifiedcomplete/test_empty_search.js
@@ -38,17 +38,17 @@ add_task(function* test_javascript_match
 
   // Now remove page 6 from history, so it is an unvisited bookmark.
   PlacesUtils.history.removePage(uri6);
 
   do_print("Match everything");
   yield check_autocomplete({
     search: "foo",
     searchParam: "enable-actions",
-    matches: [ makeSearchMatch("foo"),
+    matches: [ makeSearchMatch("foo", { heuristic: true }),
                { uri: uri1, title: "title" },
                { uri: uri2, title: "title", style: ["bookmark"] },
                { uri: uri3, title: "title" },
                { uri: uri4, title: "title", style: ["bookmark"] },
                { uri: uri5, title: "title", style: ["bookmark"] },
                { uri: uri6, title: "title", style: ["bookmark"] },
                makeSwitchToTabMatch("http://t.foo/6", { title: "title" }),
              ]
@@ -82,17 +82,16 @@ add_task(function* test_javascript_match
   });
 
   do_print("Drop-down empty search matches only open tabs");
   Services.prefs.setBoolPref("browser.urlbar.suggest.bookmark", false);
   yield check_autocomplete({
     search: "",
     searchParam: "enable-actions",
     matches: [
-               makeSearchMatch(""),
                makeSwitchToTabMatch("http://t.foo/6", { title: "title" }),
              ]
   });
 
   Services.prefs.clearUserPref("browser.urlbar.suggest.history");
   Services.prefs.clearUserPref("browser.urlbar.suggest.bookmark");
 
   yield cleanup();
--- a/toolkit/components/places/tests/unifiedcomplete/test_keyword_search.js
+++ b/toolkit/components/places/tests/unifiedcomplete/test_keyword_search.js
@@ -19,49 +19,49 @@ add_task(function* test_keyword_searc() 
     { uri: uri1, title: "Generic page title" },
     { uri: uri2, title: "Generic page title" }
   ]);
   yield addBookmark({ uri: uri1, title: "Bookmark title", keyword: "key"});
 
   do_print("Plain keyword query");
   yield check_autocomplete({
     search: "key term",
-    matches: [ { uri: NetUtil.newURI("http://abc/?search=term"), title: "abc", style: ["keyword"] } ]
+    matches: [ { uri: NetUtil.newURI("http://abc/?search=term"), title: "abc", style: ["keyword", "heuristic"] } ]
   });
 
   do_print("Multi-word keyword query");
   yield check_autocomplete({
     search: "key multi word",
-    matches: [ { uri: NetUtil.newURI("http://abc/?search=multi+word"), title: "abc", style: ["keyword"] } ]
+    matches: [ { uri: NetUtil.newURI("http://abc/?search=multi+word"), title: "abc", style: ["keyword", "heuristic"] } ]
   });
 
   do_print("Keyword query with +");
   yield check_autocomplete({
     search: "key blocking+",
-    matches: [ { uri: NetUtil.newURI("http://abc/?search=blocking%2B"), title: "abc", style: ["keyword"] } ]
+    matches: [ { uri: NetUtil.newURI("http://abc/?search=blocking%2B"), title: "abc", style: ["keyword", "heuristic"] } ]
   });
 
   do_print("Unescaped term in query");
   yield check_autocomplete({
     search: "key ユニコード",
-    matches: [ { uri: NetUtil.newURI("http://abc/?search=ユニコード"), title: "abc", style: ["keyword"] } ]
+    matches: [ { uri: NetUtil.newURI("http://abc/?search=ユニコード"), title: "abc", style: ["keyword", "heuristic"] } ]
   });
 
   do_print("Keyword that happens to match a page");
   yield check_autocomplete({
     search: "key ThisPageIsInHistory",
-    matches: [ { uri: NetUtil.newURI("http://abc/?search=ThisPageIsInHistory"), title: "abc", style: ["keyword"] } ]
+    matches: [ { uri: NetUtil.newURI("http://abc/?search=ThisPageIsInHistory"), title: "abc", style: ["keyword", "heuristic"] } ]
   });
 
   do_print("Keyword without query (without space)");
   yield check_autocomplete({
     search: "key",
-    matches: [ { uri: NetUtil.newURI("http://abc/?search="), title: "abc", style: ["keyword"] } ]
+    matches: [ { uri: NetUtil.newURI("http://abc/?search="), title: "abc", style: ["keyword", "heuristic"] } ]
   });
 
   do_print("Keyword without query (with space)");
   yield check_autocomplete({
     search: "key ",
-    matches: [ { uri: NetUtil.newURI("http://abc/?search="), title: "abc", style: ["keyword"] } ]
+    matches: [ { uri: NetUtil.newURI("http://abc/?search="), title: "abc", style: ["keyword", "heuristic"] } ]
   });
 
   yield cleanup();
 });
--- a/toolkit/components/places/tests/unifiedcomplete/test_keyword_search_actions.js
+++ b/toolkit/components/places/tests/unifiedcomplete/test_keyword_search_actions.js
@@ -20,55 +20,55 @@ add_task(function* test_keyword_search()
     { uri: uri2, title: "Generic page title" }
   ]);
   yield addBookmark({ uri: uri1, title: "Bookmark title", keyword: "key"});
 
   do_print("Plain keyword query");
   yield check_autocomplete({
     search: "key term",
     searchParam: "enable-actions",
-    matches: [ { uri: makeActionURI("keyword", {url: "http://abc/?search=term", input: "key term"}), title: "abc", style: [ "action", "keyword" ] } ]
+    matches: [ { uri: makeActionURI("keyword", {url: "http://abc/?search=term", input: "key term"}), title: "abc", style: [ "action", "keyword", "heuristic" ] } ]
   });
 
   do_print("Multi-word keyword query");
   yield check_autocomplete({
     search: "key multi word",
     searchParam: "enable-actions",
-    matches: [ { uri: makeActionURI("keyword", {url: "http://abc/?search=multi+word", input: "key multi word"}), title: "abc", style: [ "action", "keyword" ] } ]
+    matches: [ { uri: makeActionURI("keyword", {url: "http://abc/?search=multi+word", input: "key multi word"}), title: "abc", style: [ "action", "keyword", "heuristic" ] } ]
   });
 
   do_print("Keyword query with +");
   yield check_autocomplete({
     search: "key blocking+",
     searchParam: "enable-actions",
-    matches: [ { uri: makeActionURI("keyword", {url: "http://abc/?search=blocking%2B", input: "key blocking+"}), title: "abc", style: [ "action", "keyword" ] } ]
+    matches: [ { uri: makeActionURI("keyword", {url: "http://abc/?search=blocking%2B", input: "key blocking+"}), title: "abc", style: [ "action", "keyword", "heuristic" ] } ]
   });
 
   do_print("Unescaped term in query");
   yield check_autocomplete({
     search: "key ユニコード",
     searchParam: "enable-actions",
-    matches: [ { uri: makeActionURI("keyword", {url: "http://abc/?search=ユニコード", input: "key ユニコード"}), title: "abc", style: [ "action", "keyword" ] } ]
+    matches: [ { uri: makeActionURI("keyword", {url: "http://abc/?search=ユニコード", input: "key ユニコード"}), title: "abc", style: [ "action", "keyword", "heuristic" ] } ]
   });
 
   do_print("Keyword that happens to match a page");
   yield check_autocomplete({
     search: "key ThisPageIsInHistory",
     searchParam: "enable-actions",
-    matches: [ { uri: makeActionURI("keyword", {url: "http://abc/?search=ThisPageIsInHistory", input: "key ThisPageIsInHistory"}), title: "abc", style: [ "action", "keyword" ] } ]
+    matches: [ { uri: makeActionURI("keyword", {url: "http://abc/?search=ThisPageIsInHistory", input: "key ThisPageIsInHistory"}), title: "abc", style: [ "action", "keyword", "heuristic" ] } ]
   });
 
   do_print("Keyword without query (without space)");
   yield check_autocomplete({
     search: "key",
     searchParam: "enable-actions",
-    matches: [ { uri: makeActionURI("keyword", {url: "http://abc/?search=", input: "key"}), title: "abc", style: [ "action", "keyword" ] } ]
+    matches: [ { uri: makeActionURI("keyword", {url: "http://abc/?search=", input: "key"}), title: "abc", style: [ "action", "keyword", "heuristic" ] } ]
   });
 
   do_print("Keyword without query (with space)");
   yield check_autocomplete({
     search: "key ",
     searchParam: "enable-actions",
-    matches: [ { uri: makeActionURI("keyword", {url: "http://abc/?search=", input: "key "}), title: "abc", style: [ "action", "keyword" ] } ]
+    matches: [ { uri: makeActionURI("keyword", {url: "http://abc/?search=", input: "key "}), title: "abc", style: [ "action", "keyword", "heuristic" ] } ]
   });
 
   yield cleanup();
 });
--- a/toolkit/components/places/tests/unifiedcomplete/test_searchEngine_alias.js
+++ b/toolkit/components/places/tests/unifiedcomplete/test_searchEngine_alias.js
@@ -7,31 +7,31 @@ add_task(function*() {
   // Here we add another engine with a search alias.
   Services.search.addEngineWithDetails("AliasedMozSearch", "", "doit", "",
                                        "GET", "http://s.example.com/search");
 
 
   yield check_autocomplete({
     search: "doit",
     searchParam: "enable-actions",
-    matches: [ makeSearchMatch("doit", { engineName: "AliasedMozSearch", searchQuery: "", alias: "doit" }) ]
+    matches: [ makeSearchMatch("doit", { engineName: "AliasedMozSearch", searchQuery: "", alias: "doit", heuristic: true }) ]
   });
 
   yield check_autocomplete({
     search: "doit ",
     searchParam: "enable-actions",
-    matches: [ makeSearchMatch("doit ", { engineName: "AliasedMozSearch", searchQuery: "", alias: "doit" }) ]
+    matches: [ makeSearchMatch("doit ", { engineName: "AliasedMozSearch", searchQuery: "", alias: "doit", heuristic: true }) ]
   });
 
   yield check_autocomplete({
     search: "doit mozilla",
     searchParam: "enable-actions",
-    matches: [ makeSearchMatch("doit mozilla", { engineName: "AliasedMozSearch", searchQuery: "mozilla", alias: "doit" }) ]
+        matches: [ makeSearchMatch("doit mozilla", { engineName: "AliasedMozSearch", searchQuery: "mozilla", alias: "doit", heuristic: true }) ]
   });
 
   yield check_autocomplete({
     search: "doit mozzarella mozilla",
     searchParam: "enable-actions",
-    matches: [ makeSearchMatch("doit mozzarella mozilla", { engineName: "AliasedMozSearch", searchQuery: "mozzarella mozilla", alias: "doit" }) ]
+        matches: [ makeSearchMatch("doit mozzarella mozilla", { engineName: "AliasedMozSearch", searchQuery: "mozzarella mozilla", alias: "doit", heuristic: true }) ]
   });
 
   yield cleanup();
 });
--- a/toolkit/components/places/tests/unifiedcomplete/test_searchEngine_current.js
+++ b/toolkit/components/places/tests/unifiedcomplete/test_searchEngine_current.js
@@ -7,39 +7,39 @@ add_task(function*() {
   // Here we add another engine with a search alias.
   Services.search.addEngineWithDetails("AliasedMozSearch", "", "doit", "",
                                        "GET", "http://s.example.com/search");
 
   do_print("search engine");
   yield check_autocomplete({
     search: "mozilla",
     searchParam: "enable-actions",
-    matches: [ makeSearchMatch("mozilla") ]
+    matches: [ makeSearchMatch("mozilla", { heuristic: true }) ]
   });
 
   do_print("search engine, uri-like input");
   yield check_autocomplete({
     search: "http:///",
     searchParam: "enable-actions",
-    matches: [ makeSearchMatch("http:///") ]
+    matches: [ makeSearchMatch("http:///", { heuristic: true }) ]
   });
 
   do_print("search engine, multiple words");
   yield check_autocomplete({
     search: "mozzarella cheese",
     searchParam: "enable-actions",
-    matches: [ makeSearchMatch("mozzarella cheese") ]
+    matches: [ makeSearchMatch("mozzarella cheese", { heuristic: true }) ]
   });
 
   do_print("search engine, after current engine has changed");
   Services.search.addEngineWithDetails("MozSearch2", "", "", "", "GET",
                                        "http://s.example.com/search2");
   engine = Services.search.getEngineByName("MozSearch2");
   notEqual(Services.search.currentEngine, engine, "New engine shouldn't be the current engine yet");
   Services.search.currentEngine = engine;
   yield check_autocomplete({
     search: "mozilla",
     searchParam: "enable-actions",
-    matches: [ makeSearchMatch("mozilla", { engineName: "MozSearch2" }) ]
+    matches: [ makeSearchMatch("mozilla", { engineName: "MozSearch2", heuristic: true }) ]
   });
 
   yield cleanup();
 });
--- a/toolkit/components/places/tests/unifiedcomplete/test_tabmatches.js
+++ b/toolkit/components/places/tests/unifiedcomplete/test_tabmatches.js
@@ -19,54 +19,54 @@ add_task(function* test_tab_matches() {
   // Pages that cannot be registered in history.
   addOpenPages(uri3, 1);
   addOpenPages(uri4, 1);
 
   do_print("two results, normal result is a tab match");
   yield check_autocomplete({
     search: "abc.com",
     searchParam: "enable-actions",
-    matches: [ makeVisitMatch("abc.com", "http://abc.com/"),
+    matches: [ makeVisitMatch("abc.com", "http://abc.com/", { heuristic: true }),
                makeSwitchToTabMatch("http://abc.com/", { title: "ABC rocks" }) ]
   });
 
   do_print("three results, one tab match");
   yield check_autocomplete({
     search: "abc",
     searchParam: "enable-actions",
-    matches: [ makeSearchMatch("abc"),
+    matches: [ makeSearchMatch("abc", { heuristic: true }),
                makeSwitchToTabMatch("http://abc.com/", { title: "ABC rocks" }),
                { uri: uri2, title: "xyz.net - we're better than ABC", style: [ "favicon" ] } ]
   });
 
   do_print("three results, both normal results are tab matches");
   addOpenPages(uri2, 1);
   yield check_autocomplete({
     search: "abc",
     searchParam: "enable-actions",
-    matches: [ makeSearchMatch("abc"),
+    matches: [ makeSearchMatch("abc", { heuristic: true }),
                makeSwitchToTabMatch("http://abc.com/", { title: "ABC rocks" }),
                makeSwitchToTabMatch("http://xyz.net/", { title: "xyz.net - we're better than ABC" }) ]
   });
 
   do_print("three results, both normal results are tab matches, one has multiple tabs");
   addOpenPages(uri2, 5);
   yield check_autocomplete({
     search: "abc",
     searchParam: "enable-actions",
-    matches: [ makeSearchMatch("abc"),
+    matches: [ makeSearchMatch("abc", { heuristic: true }),
                makeSwitchToTabMatch("http://abc.com/", { title: "ABC rocks" }),
                makeSwitchToTabMatch("http://xyz.net/", { title: "xyz.net - we're better than ABC" }) ]
   });
 
   do_print("three results, no tab matches (disable-private-actions)");
   yield check_autocomplete({
     search: "abc",
     searchParam: "enable-actions disable-private-actions",
-    matches: [ makeSearchMatch("abc"),
+    matches: [ makeSearchMatch("abc", { heuristic: true }),
                { uri: uri1, title: "ABC rocks", style: [ "favicon" ] },
                { uri: uri2, title: "xyz.net - we're better than ABC", style: [ "favicon" ] } ]
   });
 
   do_print("two results (actions disabled)");
   yield check_autocomplete({
     search: "abc",
     searchParam: "",
@@ -75,50 +75,50 @@ add_task(function* test_tab_matches() {
   });
 
   do_print("three results, no tab matches");
   removeOpenPages(uri1, 1);
   removeOpenPages(uri2, 6);
   yield check_autocomplete({
     search: "abc",
     searchParam: "enable-actions",
-    matches: [ makeSearchMatch("abc"),
+    matches: [ makeSearchMatch("abc", { heuristic: true }),
                { uri: uri1, title: "ABC rocks", style: [ "favicon" ] },
                { uri: uri2, title: "xyz.net - we're better than ABC", style: [ "favicon" ] } ]
   });
 
   do_print("tab match search with restriction character");
   addOpenPages(uri1, 1);
   yield check_autocomplete({
     search: gTabRestrictChar + " abc",
     searchParam: "enable-actions",
-    matches: [ makeSearchMatch(gTabRestrictChar + " abc"),
+    matches: [ makeSearchMatch(gTabRestrictChar + " abc", { heuristic: true }),
                makeSwitchToTabMatch("http://abc.com/", { title: "ABC rocks" }) ]
   });
 
   do_print("tab match with not-addable pages");
   yield check_autocomplete({
     search: "mozilla",
     searchParam: "enable-actions",
-    matches: [ makeSearchMatch("mozilla"),
+    matches: [ makeSearchMatch("mozilla", { heuristic: true }),
                makeSwitchToTabMatch("about:mozilla") ]
   });
 
   do_print("tab match with not-addable pages and restriction character");
   yield check_autocomplete({
     search: gTabRestrictChar + " mozilla",
     searchParam: "enable-actions",
-    matches: [ makeSearchMatch(gTabRestrictChar + " mozilla"),
+    matches: [ makeSearchMatch(gTabRestrictChar + " mozilla", { heuristic: true }),
                makeSwitchToTabMatch("about:mozilla") ]
   });
 
   do_print("tab match with not-addable pages and only restriction character");
   yield check_autocomplete({
     search: gTabRestrictChar,
     searchParam: "enable-actions",
-    matches: [ makeSearchMatch(gTabRestrictChar),
+    matches: [ makeSearchMatch(gTabRestrictChar, { heuristic: true }),
                makeSwitchToTabMatch("http://abc.com/", { title: "ABC rocks" }),
                makeSwitchToTabMatch("about:mozilla"),
                makeSwitchToTabMatch("data:text/html,test") ]
   });
 
   yield cleanup();
 });
--- a/toolkit/components/places/tests/unifiedcomplete/test_visiturl.js
+++ b/toolkit/components/places/tests/unifiedcomplete/test_visiturl.js
@@ -2,75 +2,75 @@
  * http://creativecommons.org/publicdomain/zero/1.0/ */
 
 
 add_task(function*() {
   do_print("visit url, no protocol");
   yield check_autocomplete({
     search: "mozilla.org",
     searchParam: "enable-actions",
-    matches: [ { uri: makeActionURI("visiturl", {url: "http://mozilla.org/", input: "mozilla.org"}), title: "http://mozilla.org/", style: [ "action", "visiturl" ] } ]
+    matches: [ { uri: makeActionURI("visiturl", {url: "http://mozilla.org/", input: "mozilla.org"}), title: "http://mozilla.org/", style: [ "action", "visiturl", "heuristic" ] } ]
   });
 
   do_print("visit url, with protocol");
   yield check_autocomplete({
     search: "https://mozilla.org",
     searchParam: "enable-actions",
-    matches: [ { uri: makeActionURI("visiturl", {url: "https://mozilla.org/", input: "https://mozilla.org"}), title: "https://mozilla.org/", style: [ "action", "visiturl" ] } ]
+    matches: [ { uri: makeActionURI("visiturl", {url: "https://mozilla.org/", input: "https://mozilla.org"}), title: "https://mozilla.org/", style: [ "action", "visiturl", "heuristic" ] } ]
   });
 
   do_print("visit url, about: protocol (no host)");
   yield check_autocomplete({
     search: "about:config",
     searchParam: "enable-actions",
-    matches: [ { uri: makeActionURI("visiturl", {url: "about:config", input: "about:config"}), title: "about:config", style: [ "action", "visiturl" ] } ]
+    matches: [ { uri: makeActionURI("visiturl", {url: "about:config", input: "about:config"}), title: "about:config", style: [ "action", "visiturl", "heuristic" ] } ]
   });
 
   // This is distinct because of how we predict being able to url autofill via
   // host lookups.
   do_print("visit url, host matching visited host but not visited url");
   yield PlacesTestUtils.addVisits([
     { uri: NetUtil.newURI("http://mozilla.org/wine/"), title: "Mozilla Wine", transition: TRANSITION_TYPED },
   ]);
   yield check_autocomplete({
     search: "mozilla.org/rum",
     searchParam: "enable-actions",
-    matches: [ makeVisitMatch("mozilla.org/rum", "http://mozilla.org/rum") ]
+    matches: [ makeVisitMatch("mozilla.org/rum", "http://mozilla.org/rum", { heuristic: true }) ]
   });
 
   // And hosts with no dot in them are special, due to requiring whitelisting.
   do_print("visit url, host matching visited host but not visited url, non-whitelisted host");
   yield PlacesTestUtils.addVisits([
     { uri: NetUtil.newURI("http://mozilla/bourbon/"), title: "Mozilla Bourbon", transition: TRANSITION_TYPED },
   ]);
   yield check_autocomplete({
     search: "mozilla/rum",
     searchParam: "enable-actions",
-    matches: [ makeSearchMatch("mozilla/rum") ]
+    matches: [ makeSearchMatch("mozilla/rum", { heuristic: true }) ]
   });
 
   // ipv4 and ipv6 literal addresses should offer to visit.
   do_print("visit url, ipv4 literal");
   yield check_autocomplete({
     search: "127.0.0.1",
     searchParam: "enable-actions",
-    matches: [ makeVisitMatch("127.0.0.1", "http://127.0.0.1/") ]
+    matches: [ makeVisitMatch("127.0.0.1", "http://127.0.0.1/", { heuristic: true }) ]
   });
 
   do_print("visit url, ipv6 literal");
   yield check_autocomplete({
     search: "[2001:db8::1]",
     searchParam: "enable-actions",
-    matches: [ makeVisitMatch("[2001:db8::1]", "http://[2001:db8::1]/") ]
+    matches: [ makeVisitMatch("[2001:db8::1]", "http://[2001:db8::1]/", { heuristic: true }) ]
   });
 
   // Setting keyword.enabled to false should always try to visit.
   Services.prefs.setBoolPref("keyword.enabled", false);
   do_register_cleanup(() => {
     Services.prefs.clearUserPref("keyword.enabled");
   });
   do_print("visit url, keyword.enabled = false");
   yield check_autocomplete({
     search: "bacon",
     searchParam: "enable-actions",
-    matches: [ makeVisitMatch("bacon", "http://bacon/") ]
+    matches: [ makeVisitMatch("bacon", "http://bacon/", { heuristic: true }) ]
   });
 });
--- a/toolkit/content/widgets/autocomplete.xml
+++ b/toolkit/content/widgets/autocomplete.xml
@@ -1378,33 +1378,30 @@ extends="chrome://global/content/binding
 
             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.
+
             var title = this.getAttribute("title");
             var url = this.getAttribute("url");
             var panel = this.parentNode.parentNode;
 
             // allow consumers that have extended popups to override
             // the label values for the richlistitems
             if (panel.createResultLabel) {
               let types = this.getAttribute("type").split(/\s+/);
-              // We only want one type for createResultLabel, so get the first
-              // that isn't "action".
-              let type = types.find(v => v != "action");
-
-              return panel.createResultLabel(title, url, type);
+              return panel.createResultLabel(title, url, types);
             }
 
-            // aType (ex: "ac-result-type-<aType>") is related to the class of the image,
-            // and is not "visible" text so don't use it for the label (for accessibility).
             return title + " " + url;
           ]]>
         </getter>
       </property>
 
       <property name="_stringBundle">
         <getter><![CDATA[
           if (!this.__stringBundle) {
@@ -1686,20 +1683,28 @@ extends="chrome://global/content/binding
 
           // 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 (types.has("action")) {
+          if (initialTypes.has("action")) {
             let action = this._parseActionUrl(url);
             this.setAttribute("actiontype", action.type);
 
             if (action.type == "switchtab") {
               this.classList.add("overridable-action");
               displayUrl = action.params.url;
               let desc = this._stringBundle.GetStringFromName("switchToTab");
               this._setUpDescription(this._action, desc, true);
@@ -1759,50 +1764,39 @@ extends="chrome://global/content/binding
               displayUrl = action.params.url;
 
               let sourceStr = this._stringBundle.GetStringFromName("visitURL");
               title = this._generateEmphasisPairs(sourceStr, [
                                                     [displayUrl, "match"],
                                                   ]);
             }
 
-            // Remove the "action" substring so that the correct style, if any,
-            // is applied below.
-            types.delete("action");
           }
 
           // Check if we have a search engine name
-          if (types.has("search")) {
+          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);
-
-            // Remove the "search" substring so that the correct style, if any,
-            // is applied below.
-            types.delete("search");
           }
 
           // Check if we have an auto-fill URL
-          if (types.has("autofill")) {
+          if (initialTypes.has("autofill")) {
             emphasiseUrl = false;
 
             let sourceStr = this._stringBundle.GetStringFromName("visitURL");
             title = this._generateEmphasisPairs(sourceStr, [
                                                  [displayUrl, "match"],
                                                 ]);
-
-            types.delete("autofill");
           }
 
-          type = [...types].join(" ");
-
           // 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;