diff --git a/browser/base/content/test/general/browser.ini b/browser/base/content/test/general/browser.ini index 8ec115527f0..1197328ad5f 100644 --- a/browser/base/content/test/general/browser.ini +++ b/browser/base/content/test/general/browser.ini @@ -105,6 +105,8 @@ skip-if = e10s # Bug ?????? - no about:home support yet skip-if = os == "linux" # Bug 1073339 - Investigate autocomplete test unreliability on Linux [browser_action_searchengine.js] skip-if = os == "linux" # Bug 1073339 - Investigate autocomplete test unreliability on Linux +[browser_action_searchengine_alias.js] +skip-if = os == "linux" # Bug 1073339 - Investigate autocomplete test unreliability on Linux [browser_addKeywordSearch.js] skip-if = e10s [browser_alltabslistener.js] diff --git a/browser/base/content/test/general/browser_action_searchengine_alias.js b/browser/base/content/test/general/browser_action_searchengine_alias.js new file mode 100644 index 00000000000..f1f77088aa9 --- /dev/null +++ b/browser/base/content/test/general/browser_action_searchengine_alias.js @@ -0,0 +1,42 @@ +/** + * Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ + **/ + + let gOriginalEngine; + +add_task(function* () { + // This test is only relevant if UnifiedComplete is enabled. + if (!Services.prefs.getBoolPref("browser.urlbar.unifiedcomplete")) + return; + + Services.search.addEngineWithDetails("MozSearch", "", "moz", "", "GET", + "http://example.com/?q={searchTerms}"); + let engine = Services.search.getEngineByName("MozSearch"); + gOriginalEngine = Services.search.currentEngine; + Services.search.currentEngine = engine; + + let tab = gBrowser.selectedTab = gBrowser.addTab(); + + registerCleanupFunction(() => { + Services.search.currentEngine = gOriginalEngine; + let engine = Services.search.getEngineByName("MozSearch"); + Services.search.removeEngine(engine); + + try { + gBrowser.removeTab(tab); + } catch(ex) { /* tab may have already been closed in case of failure */ } + + return promiseClearHistory(); + }); + + gURLBar.focus(); + gURLBar.value = "moz open a searc"; + EventUtils.synthesizeKey("h" , {}); + yield promiseSearchComplete(); + + EventUtils.synthesizeKey("VK_RETURN" , { }); + yield promiseTabLoaded(gBrowser.selectedTab); + + is(gBrowser.selectedBrowser.currentURI.spec, "http://example.com/?q=open+a+search"); +}); diff --git a/toolkit/components/places/PlacesSearchAutocompleteProvider.jsm b/toolkit/components/places/PlacesSearchAutocompleteProvider.jsm index a7707e5e94a..6914cd5a692 100644 --- a/toolkit/components/places/PlacesSearchAutocompleteProvider.jsm +++ b/toolkit/components/places/PlacesSearchAutocompleteProvider.jsm @@ -25,6 +25,11 @@ const SearchAutocompleteProviderInternal = { */ priorityMatches: null, + /** + * Array of objects in the format returned by findMatchByAlias. + */ + aliasMatches: null, + /** * Object for the default search match. **/ @@ -65,6 +70,7 @@ const SearchAutocompleteProviderInternal = { _refresh: function () { this.priorityMatches = []; + this.aliasMatches = []; this.defaultMatch = null; let currentEngine = Services.search.currentEngine; @@ -82,19 +88,25 @@ const SearchAutocompleteProviderInternal = { }, _addEngine: function (engine) { - let token = engine.getResultDomain(); - if (!token) { - return; + if (engine.alias) { + this.aliasMatches.push({ + alias: engine.alias, + engineName: engine.name, + iconUrl: engine.iconURI ? engine.iconURI.spec : null, + }); } - this.priorityMatches.push({ - token: token, - // The searchForm property returns a simple URL for the search engine, but - // we may need an URL which includes an affiliate code (bug 990799). - url: engine.searchForm, - engineName: engine.name, - iconUrl: engine.iconURI ? engine.iconURI.spec : null, - }); + let domain = engine.getResultDomain(); + if (domain) { + this.priorityMatches.push({ + token: domain, + // The searchForm property returns a simple URL for the search engine, but + // we may need an URL which includes an affiliate code (bug 990799). + url: engine.searchForm, + engineName: engine.name, + iconUrl: engine.iconURI ? engine.iconURI.spec : null, + }); + } }, QueryInterface: XPCOMUtils.generateQI([Ci.nsIObserver, @@ -138,7 +150,30 @@ this.PlacesSearchAutocompleteProvider = Object.freeze({ // Match at the beginning for now. In the future, an "options" argument may // allow the matching behavior to be tuned. return SearchAutocompleteProviderInternal.priorityMatches - .find(m => m.token.startsWith(searchToken)); + .find(m => m.token.startsWith(searchToken)); + }), + + /** + * Matches a given search string to an item that should be included by + * components wishing to search using search engine aliases, like + * autocomple. + * + * @param searchToken + * Search string to match exactly a search engine alias. + * + * @return An object with the following properties, or undefined if the token + * does not match any relevant URL: + * { + * alias: The matched search engine's alias. + * engineName: The display name of the search engine. + * iconUrl: Icon associated to the match, or null if not available. + * } + */ + findMatchByAlias: Task.async(function* (searchToken) { + yield this.ensureInitialized(); + + return SearchAutocompleteProviderInternal.aliasMatches + .find(m => m.alias == searchToken); }), getDefaultMatch: Task.async(function* () { diff --git a/toolkit/components/places/UnifiedComplete.js b/toolkit/components/places/UnifiedComplete.js index a81fb715900..355db1f813f 100644 --- a/toolkit/components/places/UnifiedComplete.js +++ b/toolkit/components/places/UnifiedComplete.js @@ -706,6 +706,12 @@ Search.prototype = { hasFirstResult = true; } + if (this._enableActions && !hasFirstResult) { + // If it's not a bookmarked 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) { // Or it may look like a URL we know about from search engines. @@ -833,6 +839,21 @@ Search.prototype = { return true; }, + _matchSearchEngineAlias: function* () { + if (this._searchTokens.length < 2) + return false; + + let match = yield PlacesSearchAutocompleteProvider.findMatchByAlias( + this._searchTokens[0]); + if (!match) + return false; + + let query = this._searchTokens.slice(1).join(" "); + + yield this._addSearchEngineMatch(match, query); + return true; + }, + _matchCurrentSearchEngine: function* () { let match = yield PlacesSearchAutocompleteProvider.getDefaultMatch(); if (!match) diff --git a/toolkit/components/places/tests/unifiedcomplete/test_searchEngine_alias.js b/toolkit/components/places/tests/unifiedcomplete/test_searchEngine_alias.js new file mode 100644 index 00000000000..80430dd1e9c --- /dev/null +++ b/toolkit/components/places/tests/unifiedcomplete/test_searchEngine_alias.js @@ -0,0 +1,33 @@ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ + + +add_task(function*() { + Services.search.addEngineWithDetails("MozSearch", "", "", "", "GET", + "http://s.example.com/search"); + let engine = Services.search.getEngineByName("MozSearch"); + Services.search.currentEngine = engine; + Services.search.addEngineWithDetails("AliasedMozSearch", "", "doit", "", + "GET", "http://s.example.com/search"); + + + yield check_autocomplete({ + search: "doit", + searchParam: "enable-actions", + matches: [ { uri: makeActionURI("searchengine", {engineName: "MozSearch", input: "doit", searchQuery: "doit"}), title: "MozSearch" }, ] + }); + + yield check_autocomplete({ + search: "doit mozilla", + searchParam: "enable-actions", + matches: [ { uri: makeActionURI("searchengine", {engineName: "AliasedMozSearch", input: "doit mozilla", searchQuery: "mozilla"}), title: "AliasedMozSearch" }, ] + }); + + yield check_autocomplete({ + search: "doit mozzarella mozilla", + searchParam: "enable-actions", + matches: [ { uri: makeActionURI("searchengine", {engineName: "AliasedMozSearch", input: "doit mozzarella mozilla", searchQuery: "mozzarella mozilla"}), title: "AliasedMozSearch" }, ] + }); + + yield cleanup(); +}); diff --git a/toolkit/components/places/tests/unifiedcomplete/xpcshell.ini b/toolkit/components/places/tests/unifiedcomplete/xpcshell.ini index bea512368e9..66bf29b625d 100644 --- a/toolkit/components/places/tests/unifiedcomplete/xpcshell.ini +++ b/toolkit/components/places/tests/unifiedcomplete/xpcshell.ini @@ -26,6 +26,7 @@ tail = [test_match_beginning.js] [test_multi_word_search.js] [test_queryurl.js] +[test_searchEngine_alias.js] [test_searchEngine_current.js] [test_searchEngine_host.js] [test_searchEngine_restyle.js] diff --git a/toolkit/components/places/tests/unit/test_PlacesSearchAutocompleteProvider.js b/toolkit/components/places/tests/unit/test_PlacesSearchAutocompleteProvider.js index 2e6628449ba..3b2df432a0e 100644 --- a/toolkit/components/places/tests/unit/test_PlacesSearchAutocompleteProvider.js +++ b/toolkit/components/places/tests/unit/test_PlacesSearchAutocompleteProvider.js @@ -34,7 +34,7 @@ add_task(function* hide_search_engine_nomatch() { add_task(function* add_search_engine_match() { let promiseTopic = promiseSearchTopic("engine-added"); do_check_eq(null, yield PlacesSearchAutocompleteProvider.findMatchByToken("bacon")); - Services.search.addEngineWithDetails("bacon", "", "bacon", "Search Bacon", + Services.search.addEngineWithDetails("bacon", "", "pork", "Search Bacon", "GET", "http://www.bacon.moz/?search={searchTerms}"); yield promiseSearchTopic; let match = yield PlacesSearchAutocompleteProvider.findMatchByToken("bacon"); @@ -43,6 +43,15 @@ add_task(function* add_search_engine_match() { do_check_eq(match.iconUrl, null); }); +add_task(function* test_aliased_search_engine_match() { + do_check_eq(null, yield PlacesSearchAutocompleteProvider.findMatchByAlias("sober")); + + let match = yield PlacesSearchAutocompleteProvider.findMatchByAlias("pork"); + do_check_eq(match.engineName, "bacon"); + do_check_eq(match.alias, "pork"); + do_check_eq(match.iconUrl, null); +}); + add_task(function* remove_search_engine_nomatch() { let engine = Services.search.getEngineByName("bacon"); let promiseTopic = promiseSearchTopic("engine-removed");