author | Drew Willcoxon <adw@mozilla.com> |
Fri, 07 Aug 2015 15:00:08 -0700 | |
changeset 256917 | 907891e1c2112a703439aa885d3e47ac905878ff |
parent 256916 | 152eafb9dc89a1d37ca5e24ee19d1987a66f1201 |
child 256918 | 2e33ac0bb341812efa0f1a7e49837c9eced1f5ed |
push id | 29195 |
push user | philringnalda@gmail.com |
push date | Sun, 09 Aug 2015 01:37:55 +0000 |
treeherder | mozilla-central@fd63d8ed9d2e [default view] [failures only] |
perfherder | [talos] [build metrics] [platform microbench] (compared to previous push) |
reviewers | mak |
bugs | 1176437 |
milestone | 42.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
|
--- 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;