diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-28 14:29:10 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-28 14:29:10 +0000 |
commit | 2aa4a82499d4becd2284cdb482213d541b8804dd (patch) | |
tree | b80bf8bf13c3766139fbacc530efd0dd9d54394c /toolkit/components/places/tests/unifiedcomplete | |
parent | Initial commit. (diff) | |
download | firefox-2aa4a82499d4becd2284cdb482213d541b8804dd.tar.xz firefox-2aa4a82499d4becd2284cdb482213d541b8804dd.zip |
Adding upstream version 86.0.1.upstream/86.0.1upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'toolkit/components/places/tests/unifiedcomplete')
28 files changed, 4215 insertions, 0 deletions
diff --git a/toolkit/components/places/tests/unifiedcomplete/.eslintrc.js b/toolkit/components/places/tests/unifiedcomplete/.eslintrc.js new file mode 100644 index 0000000000..69e89d0054 --- /dev/null +++ b/toolkit/components/places/tests/unifiedcomplete/.eslintrc.js @@ -0,0 +1,5 @@ +"use strict"; + +module.exports = { + extends: ["plugin:mozilla/xpcshell-test"], +}; diff --git a/toolkit/components/places/tests/unifiedcomplete/data/engine-rel-searchform.xml b/toolkit/components/places/tests/unifiedcomplete/data/engine-rel-searchform.xml new file mode 100644 index 0000000000..f4baad28ab --- /dev/null +++ b/toolkit/components/places/tests/unifiedcomplete/data/engine-rel-searchform.xml @@ -0,0 +1,5 @@ +<?xml version="1.0" encoding="UTF-8"?> +<SearchPlugin xmlns="http://www.mozilla.org/2006/browser/search/"> +<ShortName>engine-rel-searchform.xml</ShortName> +<Url type="text/html" method="GET" template="http://example.com/?search" rel="searchform"/> +</SearchPlugin> diff --git a/toolkit/components/places/tests/unifiedcomplete/data/engine-suggestions.xml b/toolkit/components/places/tests/unifiedcomplete/data/engine-suggestions.xml new file mode 100644 index 0000000000..a322a7c86e --- /dev/null +++ b/toolkit/components/places/tests/unifiedcomplete/data/engine-suggestions.xml @@ -0,0 +1,14 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- Any copyright is dedicated to the Public Domain. + - http://creativecommons.org/publicdomain/zero/1.0/ --> + +<SearchPlugin xmlns="http://www.mozilla.org/2006/browser/search/"> +<ShortName>engine-suggestions.xml</ShortName> +<Url type="application/x-suggestions+json" + method="GET" + template="http://localhost:9000/suggest?{searchTerms}"/> +<Url type="text/html" + method="GET" + template="http://localhost:9000/search" + rel="searchform"/> +</SearchPlugin> diff --git a/toolkit/components/places/tests/unifiedcomplete/head_autocomplete.js b/toolkit/components/places/tests/unifiedcomplete/head_autocomplete.js new file mode 100644 index 0000000000..0fb763095a --- /dev/null +++ b/toolkit/components/places/tests/unifiedcomplete/head_autocomplete.js @@ -0,0 +1,578 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. */ + +const FRECENCY_DEFAULT = 10000; + +var { ObjectUtils } = ChromeUtils.import( + "resource://gre/modules/ObjectUtils.jsm" +); +var { Services } = ChromeUtils.import("resource://gre/modules/Services.jsm"); +var { + HTTP_400, + HTTP_401, + HTTP_402, + HTTP_403, + HTTP_404, + HTTP_405, + HTTP_406, + HTTP_407, + HTTP_408, + HTTP_409, + HTTP_410, + HTTP_411, + HTTP_412, + HTTP_413, + HTTP_414, + HTTP_415, + HTTP_417, + HTTP_500, + HTTP_501, + HTTP_502, + HTTP_503, + HTTP_504, + HTTP_505, + HttpError, + HttpServer, +} = ChromeUtils.import("resource://testing-common/httpd.js"); + +// Import common head. +{ + /* import-globals-from ../head_common.js */ + let commonFile = do_get_file("../head_common.js", false); + let uri = Services.io.newFileURI(commonFile); + Services.scriptloader.loadSubScript(uri.spec, this); +} + +// Put any other stuff relative to this test folder below. + +XPCOMUtils.defineLazyModuleGetters(this, { + UrlbarPrefs: "resource:///modules/UrlbarPrefs.jsm", + UrlbarProviderOpenTabs: "resource:///modules/UrlbarProviderOpenTabs.jsm", + UrlbarTokenizer: "resource:///modules/UrlbarTokenizer.jsm", + UrlbarUtils: "resource:///modules/UrlbarUtils.jsm", +}); + +const { AddonTestUtils } = ChromeUtils.import( + "resource://testing-common/AddonTestUtils.jsm" +); +AddonTestUtils.init(this, false); +AddonTestUtils.createAppInfo( + "xpcshell@tests.mozilla.org", + "XPCShell", + "42", + "42" +); + +add_task(async function setup() { + await AddonTestUtils.promiseStartupManager(); +}); + +async function cleanup() { + Services.prefs.clearUserPref("browser.urlbar.autoFill"); + Services.prefs.clearUserPref("browser.urlbar.autoFill.searchEngines"); + let suggestPrefs = ["history", "bookmark", "openpage", "searches"]; + for (let type of suggestPrefs) { + Services.prefs.clearUserPref("browser.urlbar.suggest." + type); + } + Services.prefs.clearUserPref("browser.search.suggest.enabled"); + await PlacesUtils.bookmarks.eraseEverything(); + await PlacesUtils.history.clear(); +} +registerCleanupFunction(cleanup); + +/** + * @param {Array} aSearches Array of AutoCompleteSearch names. + */ +function AutoCompleteInput(aSearches) { + this.searches = aSearches; +} +AutoCompleteInput.prototype = { + popup: { + selectedIndex: -1, + invalidate() {}, + QueryInterface: ChromeUtils.generateQI(["nsIAutoCompletePopup"]), + }, + popupOpen: false, + + disableAutoComplete: false, + completeDefaultIndex: true, + completeSelectedIndex: true, + forceComplete: false, + + minResultsForPopup: 0, + maxRows: 0, + + timeout: 10, + searchParam: "", + + get searchCount() { + return this.searches.length; + }, + getSearchAt(aIndex) { + return this.searches[aIndex]; + }, + + textValue: "", + // Text selection range + _selStart: 0, + _selEnd: 0, + get selectionStart() { + return this._selStart; + }, + get selectionEnd() { + return this._selEnd; + }, + selectTextRange(aStart, aEnd) { + this._selStart = aStart; + this._selEnd = aEnd; + }, + + onSearchBegin() {}, + onSearchComplete() {}, + + onTextEntered: () => false, + onTextReverted: () => false, + + QueryInterface: ChromeUtils.generateQI(["nsIAutoCompleteInput"]), +}; + +/** + * A helper for check_autocomplete to check a specific match against data from + * the controller. + * + * @param {Object} match The expected match for the result, in the following form: + * { + * uri: {String|nsIURI} The expected uri. Note: nsIURI should be considered + * deprecated. + * title: {String} The title of the entry. + * tags: {String} The tags for the entry. + * style: {Array} The style of the entry. + * } + * @param {Object} result The result to compare the result against with the same + * properties as the match param. + * @returns {boolean} Returns true if the result matches. + */ +async function _check_autocomplete_matches(match, result) { + let { uri, tags, style } = match; + if (uri instanceof Ci.nsIURI) { + uri = uri.spec; + } + let title = match.comment || match.title; + + if (tags) { + title += UrlbarUtils.TITLE_TAGS_SEPARATOR + tags.sort().join(", "); + } + if (style) { + style = style.sort(); + } else { + style = ["favicon"]; + } + + let actual = { value: result.value, comment: result.comment }; + let expected = { value: match.value || uri, comment: title }; + info( + `Checking match: ` + + `actual=${JSON.stringify(actual)} ... ` + + `expected=${JSON.stringify(expected)}` + ); + + let actualAction = PlacesUtils.parseActionUrl(actual.value); + let expectedAction = PlacesUtils.parseActionUrl(expected.value); + if (actualAction && expectedAction) { + if (!ObjectUtils.deepEqual(actualAction, expectedAction)) { + return false; + } + } else if (actual.value != expected.value) { + return false; + } + + if (actual.comment != expected.comment) { + return false; + } + + let actualStyle = result.style.split(/\s+/).sort(); + if (style) { + Assert.equal( + actualStyle.toString(), + style.toString(), + "Match should have expected style" + ); + } + if (uri && uri.startsWith("moz-action:")) { + Assert.ok( + actualStyle.includes("action"), + "moz-action results should always have 'action' in their style" + ); + } + + if (match.icon) { + await compareFavicons( + result.image, + match.icon, + "Match should have the expected icon" + ); + } + + return true; +} + +/** + * Helper function to test an autocomplete entry and check the resultant matches. + * + * @param {Object} test An object representing the test to run, in the following form: + * { + * search: {String} The string to enter for autocompleting. + * searchParam: {String} The search parameters to apply to the + * autocomplete search. + * matches: {Object[]} The expected results in match format. see + * _check_autocomplete_matches. + * } + */ +async function check_autocomplete(test) { + // At this point frecency could still be updating due to latest pages + // updates. + // This is not a problem in real life, but autocomplete tests should + // return reliable resultsets, thus we have to wait. + await PlacesTestUtils.promiseAsyncUpdates(); + + // Make an AutoCompleteInput that uses our searches and confirms results. + let input = test.input || new AutoCompleteInput(["unifiedcomplete"]); + input.textValue = test.search; + + if (test.searchParam) { + input.searchParam = test.searchParam; + } + + // Caret must be at the end for autoFill to happen. + let strLen = test.search.length; + input.selectTextRange(strLen, strLen); + Assert.equal(input.selectionStart, strLen, "Selection starts at end"); + Assert.equal(input.selectionEnd, strLen, "Selection ends at the end"); + + let controller = Cc["@mozilla.org/autocomplete/controller;1"].getService( + Ci.nsIAutoCompleteController + ); + controller.input = input; + + let numSearchesStarted = 0; + input.onSearchBegin = () => { + info("onSearchBegin received"); + numSearchesStarted++; + }; + let searchCompletePromise = new Promise(resolve => { + input.onSearchComplete = () => { + info("onSearchComplete received"); + resolve(); + }; + }); + let expectedSearches = 1; + + info("Searching for: '" + test.search + "'"); + controller.startSearch(test.search); + await searchCompletePromise; + + Assert.equal(numSearchesStarted, expectedSearches, "All searches started"); + + // Check to see the expected uris and titles match up. If 'enable-actions' + // is specified, we check that the first specified match is the first + // controller value (as this is the "special" always selected item), but the + // rest can match in any order. + // If 'enable-actions' is not specified, they can match in any order. + if (test.matches) { + // Do not modify the test original matches. + let matches = test.matches.slice(); + + if (matches.length) { + let firstIndexToCheck = 0; + if (test.searchParam && test.searchParam.includes("enable-actions")) { + let result = { + value: controller.getValueAt(0), + comment: controller.getCommentAt(0), + style: controller.getStyleAt(0), + image: controller.getImageAt(0), + }; + // We only care about the positioning of the first result if it is + // heuristic. + if (result.style.includes("heuristic")) { + info("Checking first match is first autocomplete entry"); + info(`First match is "${result.value}", "${result.comment}"`); + Assert.ok( + await _check_autocomplete_matches(matches[0], result), + "first item is correct" + ); + info("Checking rest of the matches"); + firstIndexToCheck = 1; + } + } + + for (let i = firstIndexToCheck; i < controller.matchCount; i++) { + let result = { + value: controller.getValueAt(i), + comment: controller.getCommentAt(i), + style: controller.getStyleAt(i), + image: controller.getImageAt(i), + }; + info(`Actual result at index ${i}: ${JSON.stringify(result)}`); + let lowerBound = test.checkSorting ? i : firstIndexToCheck; + let upperBound = test.checkSorting ? i + 1 : matches.length; + let found = false; + for (let j = lowerBound; j < upperBound; ++j) { + // Skip processed expected results + if (matches[j] == undefined) { + continue; + } + if (await _check_autocomplete_matches(matches[j], result)) { + info("Got a match at index " + j + "!"); + // Make it undefined so we don't process it again + matches[j] = undefined; + found = true; + break; + } + } + + if (!found) { + do_throw( + `Didn't find the current result ("${result.value}", "${result.comment}") in matches` + ); + } // ' (Emacs syntax highlighting fix) + } + } + + Assert.equal( + controller.matchCount, + matches.length, + "Got as many results as expected" + ); + + // If we expect results, make sure we got matches. + Assert.equal( + controller.searchStatus, + matches.length + ? Ci.nsIAutoCompleteController.STATUS_COMPLETE_MATCH + : Ci.nsIAutoCompleteController.STATUS_COMPLETE_NO_MATCH + ); + } + + if (test.autofilled) { + // Check the autoFilled result. + Assert.equal( + input.textValue, + test.autofilled, + "Autofilled value is correct" + ); + + // Now force completion and check correct casing of the result. + // This ensures the controller is able to do its magic case-preserving + // stuff and correct replacement of the user's casing with result's one. + controller.handleEnter(false); + Assert.equal(input.textValue, test.completed, "Completed value is correct"); + } + return input; +} + +async function addOpenPages(aUri, aCount = 1, aUserContextId = 0) { + for (let i = 0; i < aCount; i++) { + await UrlbarProviderOpenTabs.registerOpenTab(aUri.spec, aUserContextId); + } +} + +async function removeOpenPages(aUri, aCount = 1, aUserContextId = 0) { + for (let i = 0; i < aCount; i++) { + await UrlbarProviderOpenTabs.unregisterOpenTab(aUri.spec, aUserContextId); + } +} + +/** + * Strip prefixes from the URI that we don't care about for searching. + * + * @param {String} spec + * The text to modify. + * @return {String} the modified spec. + */ +function stripPrefix(spec) { + ["http://", "https://", "ftp://"].some(scheme => { + if (spec.startsWith(scheme)) { + spec = spec.slice(scheme.length); + return true; + } + return false; + }); + + if (spec.startsWith("www.")) { + spec = spec.slice(4); + } + return spec; +} + +function makeActionURI(action, params) { + let encodedParams = {}; + for (let key in params) { + encodedParams[key] = encodeURIComponent(params[key]); + } + let url = "moz-action:" + action + "," + JSON.stringify(encodedParams); + return Services.io.newURI(url); +} + +// Creates a full "match" entry for a search result, suitable for passing as +// an entry to check_autocomplete. +function makeSearchMatch(input, extra = {}) { + let params = { + engineName: extra.engineName || "MozSearch", + input, + searchQuery: "searchQuery" in extra ? extra.searchQuery : input, + }; + let style = ["action", "searchengine"]; + if ("style" in extra && Array.isArray(extra.style)) { + style.push(...extra.style); + } + if (extra.heuristic) { + style.push("heuristic"); + } + if ("alias" in extra) { + params.alias = extra.alias; + style.push("alias"); + } + if ("searchSuggestion" in extra) { + params.searchSuggestion = extra.searchSuggestion; + style.push("suggestion"); + } + if ("isSearchHistory" in extra) { + params.isSearchHistory = extra.isSearchHistory; + } + return { + uri: makeActionURI("searchengine", params), + title: params.engineName, + 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"); + } + let displaySpec = Services.textToSubURI.unEscapeURIForUI(url); + return { + uri: makeActionURI("visiturl", params), + title: extra.title || displaySpec, + style, + }; +} + +function makeSwitchToTabMatch(url, extra = {}) { + let displaySpec = Services.textToSubURI.unEscapeURIForUI(url); + return { + uri: makeActionURI("switchtab", { url }), + title: extra.title || displaySpec, + style: ["action", "switchtab"], + }; +} + +function makeExtensionMatch(extra = {}) { + let style = ["action", "extension"]; + if (extra.heuristic) { + style.push("heuristic"); + } + + return { + uri: makeActionURI("extension", { + content: extra.content, + keyword: extra.keyword, + }), + title: extra.description, + style, + }; +} + +function makeTestServer(port = -1) { + let httpServer = new HttpServer(); + httpServer.start(port); + registerCleanupFunction(() => httpServer.stop(() => {})); + return httpServer; +} + +function addTestEngine(basename, httpServer = undefined) { + httpServer = httpServer || makeTestServer(); + httpServer.registerDirectory("/", do_get_cwd()); + let dataUrl = + "http://localhost:" + httpServer.identity.primaryPort + "/data/"; + + info("Adding engine: " + basename); + return new Promise(resolve => { + Services.obs.addObserver(function obs(subject, topic, data) { + let engine = subject.QueryInterface(Ci.nsISearchEngine); + info("Observed " + data + " for " + engine.name); + if (data != "engine-added" || engine.name != basename) { + return; + } + + Services.obs.removeObserver(obs, "browser-search-engine-modified"); + registerCleanupFunction(() => Services.search.removeEngine(engine)); + resolve(engine); + }, "browser-search-engine-modified"); + + info("Adding engine from URL: " + dataUrl + basename); + Services.search.addOpenSearchEngine(dataUrl + basename, null); + }); +} + +/** + * Sets up a search engine that provides some suggestions by appending strings + * onto the search query. + * + * @param {function} suggestionsFn + * A function that returns an array of suggestion strings given a + * search string. If not given, a default function is used. + * @returns {nsISearchEngine} The new engine. + */ +async function addTestSuggestionsEngine(suggestionsFn = null) { + // This port number should match the number in engine-suggestions.xml. + let server = makeTestServer(9000); + server.registerPathHandler("/suggest", (req, resp) => { + // URL query params are x-www-form-urlencoded, which converts spaces into + // plus signs, so un-convert any plus signs back to spaces. + let searchStr = decodeURIComponent(req.queryString.replace(/\+/g, " ")); + let suggestions = suggestionsFn + ? suggestionsFn(searchStr) + : [searchStr].concat(["foo", "bar"].map(s => searchStr + " " + s)); + let data = [searchStr, suggestions]; + resp.setHeader("Content-Type", "application/json", false); + resp.write(JSON.stringify(data)); + }); + let engine = await addTestEngine("engine-suggestions.xml", server); + return engine; +} + +// Ensure we have a default search engine and the keyword.enabled preference +// set. +add_task(async function ensure_search_engine() { + // keyword.enabled is necessary for the tests to see keyword searches. + Services.prefs.setBoolPref("keyword.enabled", true); + + // Before initializing the search service, set the geo IP url pref to a dummy + // string. When the search service is initialized, it contacts the URI named + // in this pref, causing unnecessary error logs. + let geoPref = "browser.search.geoip.url"; + Services.prefs.setCharPref(geoPref, ""); + registerCleanupFunction(() => Services.prefs.clearUserPref(geoPref)); + // Remove any existing engines before adding ours. + for (let engine of await Services.search.getEngines()) { + await Services.search.removeEngine(engine); + } + await Services.search.addEngineWithDetails("MozSearch", { + method: "GET", + template: "http://s.example.com/search", + }); + let engine = Services.search.getEngineByName("MozSearch"); + await Services.search.setDefault(engine); +}); diff --git a/toolkit/components/places/tests/unifiedcomplete/test_416211.js b/toolkit/components/places/tests/unifiedcomplete/test_416211.js new file mode 100644 index 0000000000..06fea799aa --- /dev/null +++ b/toolkit/components/places/tests/unifiedcomplete/test_416211.js @@ -0,0 +1,31 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +/* + * Test bug 416211 to make sure results that match the tag show the bookmark + * title instead of the page title. + */ + +add_task(async function test_tag_match_has_bookmark_title() { + info("Make sure the tag match gives the bookmark title"); + let uri = NetUtil.newURI("http://theuri/"); + await PlacesTestUtils.addVisits({ uri, title: "Page title" }); + await PlacesTestUtils.addBookmarkWithDetails({ + uri, + title: "Bookmark title", + tags: ["superTag"], + }); + await check_autocomplete({ + search: "superTag", + matches: [ + { + uri, + title: "Bookmark title", + tags: ["superTag"], + style: ["bookmark-tag"], + }, + ], + }); + await cleanup(); +}); diff --git a/toolkit/components/places/tests/unifiedcomplete/test_416214.js b/toolkit/components/places/tests/unifiedcomplete/test_416214.js new file mode 100644 index 0000000000..117767d93e --- /dev/null +++ b/toolkit/components/places/tests/unifiedcomplete/test_416214.js @@ -0,0 +1,57 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +/* + * Test autocomplete for non-English URLs that match the tag bug 416214. Also + * test bug 417441 by making sure escaped ascii characters like "+" remain + * escaped. + * + * - add a visit for a page with a non-English URL + * - add a tag for the page + * - search for the tag + * - test number of matches (should be exactly one) + * - make sure the url is decoded + */ + +add_task(async function test_tag_match_url() { + info( + "Make sure tag matches return the right url as well as '+' remain escaped" + ); + let uri1 = NetUtil.newURI("http://escaped/ユニコード"); + let uri2 = NetUtil.newURI("http://asciiescaped/blocking-firefox3%2B"); + await PlacesTestUtils.addVisits([ + { uri: uri1, title: "title" }, + { uri: uri2, title: "title" }, + ]); + await PlacesTestUtils.addBookmarkWithDetails({ + uri: uri1, + title: "title", + tags: ["superTag"], + style: ["bookmark-tag"], + }); + await PlacesTestUtils.addBookmarkWithDetails({ + uri: uri2, + title: "title", + tags: ["superTag"], + style: ["bookmark-tag"], + }); + await check_autocomplete({ + search: "superTag", + matches: [ + { + uri: uri1, + title: "title", + tags: ["superTag"], + style: ["bookmark-tag"], + }, + { + uri: uri2, + title: "title", + tags: ["superTag"], + style: ["bookmark-tag"], + }, + ], + }); + await cleanup(); +}); diff --git a/toolkit/components/places/tests/unifiedcomplete/test_417798.js b/toolkit/components/places/tests/unifiedcomplete/test_417798.js new file mode 100644 index 0000000000..37d0a544fa --- /dev/null +++ b/toolkit/components/places/tests/unifiedcomplete/test_417798.js @@ -0,0 +1,71 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +/** + * Test for bug 417798 to make sure javascript: URIs don't show up unless the + * user searches for javascript: explicitly. + */ + +add_task(async function test_javascript_match() { + Services.prefs.setBoolPref("browser.urlbar.autoFill.searchEngines", false); + + let uri1 = NetUtil.newURI("http://abc/def"); + let uri2 = NetUtil.newURI("javascript:5"); + await PlacesTestUtils.addVisits([ + { uri: uri1, title: "Title with javascript:" }, + ]); + await PlacesTestUtils.addBookmarkWithDetails({ + uri: uri2, + title: "Title with javascript:", + }); + + info("Match non-javascript: with plain search"); + await check_autocomplete({ + search: "a", + matches: [{ uri: uri1, title: "Title with javascript:" }], + }); + + info("Match non-javascript: with 'javascript'"); + await check_autocomplete({ + search: "javascript", + matches: [{ uri: uri1, title: "Title with javascript:" }], + }); + + info("Match non-javascript with 'javascript:'"); + await check_autocomplete({ + search: "javascript:", + matches: [{ uri: uri1, title: "Title with javascript:" }], + }); + + info("Match nothing with '5 javascript:'"); + await check_autocomplete({ + search: "5 javascript:", + matches: [], + }); + + info("Match non-javascript: with 'a javascript:'"); + await check_autocomplete({ + search: "a javascript:", + matches: [{ uri: uri1, title: "Title with javascript:" }], + }); + + info("Match non-javascript: and javascript: with 'javascript: a'"); + await check_autocomplete({ + search: "javascript: a", + matches: [ + { uri: uri1, title: "Title with javascript:" }, + { uri: uri2, title: "Title with javascript:", style: ["bookmark"] }, + ], + }); + + info("Match javascript: with 'javascript: 5'"); + await check_autocomplete({ + search: "javascript: 5", + matches: [ + { uri: uri2, title: "Title with javascript:", style: ["bookmark"] }, + ], + }); + + await cleanup(); +}); diff --git a/toolkit/components/places/tests/unifiedcomplete/test_418257.js b/toolkit/components/places/tests/unifiedcomplete/test_418257.js new file mode 100644 index 0000000000..bbb05eaa5c --- /dev/null +++ b/toolkit/components/places/tests/unifiedcomplete/test_418257.js @@ -0,0 +1,112 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +/** + * Test bug 418257 by making sure tags are returned with the title as part of + * the "comment" if there are tags even if we didn't match in the tags. They + * are separated from the title by a endash. + */ + +add_task(async function test() { + Services.prefs.setBoolPref("browser.urlbar.autoFill", false); + + let uri1 = NetUtil.newURI("http://page1"); + let uri2 = NetUtil.newURI("http://page2"); + let uri3 = NetUtil.newURI("http://page3"); + let uri4 = NetUtil.newURI("http://page4"); + await PlacesTestUtils.addVisits([ + { uri: uri1, title: "tagged" }, + { uri: uri2, title: "tagged" }, + { uri: uri3, title: "tagged" }, + { uri: uri4, title: "tagged" }, + ]); + await PlacesTestUtils.addBookmarkWithDetails({ + uri: uri1, + title: "tagged", + tags: ["tag1"], + }); + await PlacesTestUtils.addBookmarkWithDetails({ + uri: uri2, + title: "tagged", + tags: ["tag1", "tag2"], + }); + await PlacesTestUtils.addBookmarkWithDetails({ + uri: uri3, + title: "tagged", + tags: ["tag1", "tag3"], + }); + await PlacesTestUtils.addBookmarkWithDetails({ + uri: uri4, + title: "tagged", + tags: ["tag1", "tag2", "tag3"], + }); + + info("Make sure tags come back in the title when matching tags"); + await check_autocomplete({ + search: "page1 tag", + matches: [ + { uri: uri1, title: "tagged", tags: ["tag1"], style: ["bookmark-tag"] }, + ], + }); + + info("Check tags in title for page2"); + await check_autocomplete({ + search: "page2 tag", + matches: [ + { + uri: uri2, + title: "tagged", + tags: ["tag1", "tag2"], + style: ["bookmark-tag"], + }, + ], + }); + + info("Make sure tags appear even when not matching the tag"); + await check_autocomplete({ + search: "page3", + matches: [ + { + uri: uri3, + title: "tagged", + tags: ["tag1", "tag3"], + style: ["bookmark-tag"], + }, + ], + }); + + info("Multiple tags come in commas for page4"); + await check_autocomplete({ + search: "page4", + matches: [ + { + uri: uri4, + title: "tagged", + tags: ["tag1", "tag2", "tag3"], + style: ["bookmark-tag"], + }, + ], + }); + + info("Extra test just to make sure we match the title"); + await check_autocomplete({ + search: "tag2", + matches: [ + { + uri: uri2, + title: "tagged", + tags: ["tag1", "tag2"], + style: ["bookmark-tag"], + }, + { + uri: uri4, + title: "tagged", + tags: ["tag1", "tag2", "tag3"], + style: ["bookmark-tag"], + }, + ], + }); + + await cleanup(); +}); diff --git a/toolkit/components/places/tests/unifiedcomplete/test_422277.js b/toolkit/components/places/tests/unifiedcomplete/test_422277.js new file mode 100644 index 0000000000..b85c1d1c44 --- /dev/null +++ b/toolkit/components/places/tests/unifiedcomplete/test_422277.js @@ -0,0 +1,21 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +/** + * Test bug 422277 to make sure bad escaped uris don't get escaped. This makes + * sure we don't hit an assertion for "not a UTF8 string". + */ + +add_task(async function test() { + Services.prefs.setBoolPref("browser.urlbar.autoFill", false); + + info("Bad escaped uri stays escaped"); + let uri1 = NetUtil.newURI("http://site/%EAid"); + await PlacesTestUtils.addVisits([{ uri: uri1, title: "title" }]); + await check_autocomplete({ + search: "site", + matches: [{ uri: uri1, title: "title" }], + }); + await cleanup(); +}); diff --git a/toolkit/components/places/tests/unifiedcomplete/test_autocomplete_stopSearch_no_throw.js b/toolkit/components/places/tests/unifiedcomplete/test_autocomplete_stopSearch_no_throw.js new file mode 100644 index 0000000000..1d3cff6248 --- /dev/null +++ b/toolkit/components/places/tests/unifiedcomplete/test_autocomplete_stopSearch_no_throw.js @@ -0,0 +1,22 @@ +/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */ +/* vim:set ts=2 sw=2 sts=2 et: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +/** + * Added with bug 508102 to make sure that calling stopSearch on our + * AutoComplete implementation does not throw. + */ + +var ac = Cc[ + "@mozilla.org/autocomplete/search;1?name=unifiedcomplete" +].getService(Ci.nsIAutoCompleteSearch); + +add_task(async function test_stopSearch() { + try { + ac.stopSearch(); + } catch (e) { + do_throw("we should not have caught anything!"); + } +}); diff --git a/toolkit/components/places/tests/unifiedcomplete/test_do_not_trim.js b/toolkit/components/places/tests/unifiedcomplete/test_do_not_trim.js new file mode 100644 index 0000000000..d7f5f14841 --- /dev/null +++ b/toolkit/components/places/tests/unifiedcomplete/test_do_not_trim.js @@ -0,0 +1,90 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. */ + +// Inline should never return matches shorter than the search string, since +// that largely confuses completeDefaultIndex + +add_task(async function test_not_autofill_ws_1() { + info("Do not autofill whitespaced entry 1"); + await PlacesTestUtils.addVisits({ + uri: NetUtil.newURI("http://mozilla.org/link/"), + transition: TRANSITION_TYPED, + }); + await check_autocomplete({ + search: "mozilla.org ", + autofilled: "mozilla.org ", + completed: "mozilla.org ", + }); + await cleanup(); +}); + +add_task(async function test_not_autofill_ws_2() { + info("Do not autofill whitespaced entry 2"); + await PlacesTestUtils.addVisits({ + uri: NetUtil.newURI("http://mozilla.org/link/"), + transition: TRANSITION_TYPED, + }); + await check_autocomplete({ + search: "mozilla.org/ ", + autofilled: "mozilla.org/ ", + completed: "mozilla.org/ ", + }); + await cleanup(); +}); + +add_task(async function test_not_autofill_ws_3() { + info("Do not autofill whitespaced entry 3"); + await PlacesTestUtils.addVisits({ + uri: NetUtil.newURI("http://mozilla.org/link/"), + transition: TRANSITION_TYPED, + }); + await check_autocomplete({ + search: "mozilla.org/link ", + autofilled: "mozilla.org/link ", + completed: "mozilla.org/link ", + }); + await cleanup(); +}); + +add_task(async function test_not_autofill_ws_4() { + info("Do not autofill whitespaced entry 4"); + await PlacesTestUtils.addVisits({ + uri: NetUtil.newURI("http://mozilla.org/link/"), + transition: TRANSITION_TYPED, + }); + await check_autocomplete({ + search: "mozilla.org/link/ ", + autofilled: "mozilla.org/link/ ", + completed: "mozilla.org/link/ ", + }); + await cleanup(); +}); + +add_task(async function test_not_autofill_ws_5() { + info("Do not autofill whitespaced entry 5"); + await PlacesTestUtils.addVisits({ + uri: NetUtil.newURI("http://mozilla.org/link/"), + transition: TRANSITION_TYPED, + }); + await check_autocomplete({ + search: "moz illa ", + autofilled: "moz illa ", + completed: "moz illa ", + }); + await cleanup(); +}); + +add_task(async function test_not_autofill_ws_6() { + info("Do not autofill whitespaced entry 6"); + await PlacesTestUtils.addVisits({ + uri: NetUtil.newURI("http://mozilla.org/link/"), + transition: TRANSITION_TYPED, + }); + await check_autocomplete({ + search: " mozilla", + autofilled: " mozilla", + completed: " mozilla", + }); + await cleanup(); +}); diff --git a/toolkit/components/places/tests/unifiedcomplete/test_download_embed_bookmarks.js b/toolkit/components/places/tests/unifiedcomplete/test_download_embed_bookmarks.js new file mode 100644 index 0000000000..824cb9c431 --- /dev/null +++ b/toolkit/components/places/tests/unifiedcomplete/test_download_embed_bookmarks.js @@ -0,0 +1,77 @@ +/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- + * vim:set ts=2 sw=2 sts=2 et: + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +/** + * Tests bug 449406 to ensure that TRANSITION_DOWNLOAD, TRANSITION_EMBED and + * TRANSITION_FRAMED_LINK bookmarked uri's show up in the location bar. + */ + +add_task(async function test_download_embed_bookmarks() { + let uri1 = NetUtil.newURI("http://download/bookmarked"); + let uri2 = NetUtil.newURI("http://embed/bookmarked"); + let uri3 = NetUtil.newURI("http://framed/bookmarked"); + let uri4 = NetUtil.newURI("http://download"); + let uri5 = NetUtil.newURI("http://embed"); + let uri6 = NetUtil.newURI("http://framed"); + await PlacesTestUtils.addVisits([ + { uri: uri1, title: "download-bookmark", transition: TRANSITION_DOWNLOAD }, + { uri: uri2, title: "embed-bookmark", transition: TRANSITION_EMBED }, + { uri: uri3, title: "framed-bookmark", transition: TRANSITION_FRAMED_LINK }, + { uri: uri4, title: "download2", transition: TRANSITION_DOWNLOAD }, + { uri: uri5, title: "embed2", transition: TRANSITION_EMBED }, + { uri: uri6, title: "framed2", transition: TRANSITION_FRAMED_LINK }, + ]); + await PlacesTestUtils.addBookmarkWithDetails({ + uri: uri1, + title: "download-bookmark", + }); + await PlacesTestUtils.addBookmarkWithDetails({ + uri: uri2, + title: "embed-bookmark", + }); + await PlacesTestUtils.addBookmarkWithDetails({ + uri: uri3, + title: "framed-bookmark", + }); + + info("Searching for bookmarked download uri matches"); + await check_autocomplete({ + search: "download-bookmark", + matches: [{ uri: uri1, title: "download-bookmark", style: ["bookmark"] }], + }); + + info("Searching for bookmarked embed uri matches"); + await check_autocomplete({ + search: "embed-bookmark", + matches: [{ uri: uri2, title: "embed-bookmark", style: ["bookmark"] }], + }); + + info("Searching for bookmarked framed uri matches"); + await check_autocomplete({ + search: "framed-bookmark", + matches: [{ uri: uri3, title: "framed-bookmark", style: ["bookmark"] }], + }); + + info("Searching for download uri does not match"); + await check_autocomplete({ + search: "download2", + matches: [], + }); + + info("Searching for embed uri does not match"); + await check_autocomplete({ + search: "embed2", + matches: [], + }); + + info("Searching for framed uri does not match"); + await check_autocomplete({ + search: "framed2", + matches: [], + }); + + await cleanup(); +}); diff --git a/toolkit/components/places/tests/unifiedcomplete/test_empty_search.js b/toolkit/components/places/tests/unifiedcomplete/test_empty_search.js new file mode 100644 index 0000000000..733a12dd3b --- /dev/null +++ b/toolkit/components/places/tests/unifiedcomplete/test_empty_search.js @@ -0,0 +1,104 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +/** + * Test for bug 426864 that makes sure the empty search (drop down list) only + * shows typed pages from history. + */ + +add_task(async function test_javascript_match() { + let uri1 = NetUtil.newURI("http://t.foo/0"); + let uri2 = NetUtil.newURI("http://t.foo/1"); + let uri3 = NetUtil.newURI("http://t.foo/2"); + let uri4 = NetUtil.newURI("http://t.foo/3"); + let uri5 = NetUtil.newURI("http://t.foo/4"); + let uri6 = NetUtil.newURI("http://t.foo/5"); + let uri7 = NetUtil.newURI("http://t.foo/6"); + + await PlacesTestUtils.addVisits([ + { uri: uri1, title: "title" }, + { uri: uri2, title: "title" }, + { uri: uri3, title: "title" }, + { uri: uri4, title: "title" }, + { uri: uri6, title: "title" }, + { uri: uri7, title: "title" }, + ]); + + await PlacesTestUtils.addBookmarkWithDetails({ uri: uri2, title: "title" }); + await PlacesTestUtils.addBookmarkWithDetails({ uri: uri4, title: "title" }); + await PlacesTestUtils.addBookmarkWithDetails({ uri: uri5, title: "title" }); + await PlacesTestUtils.addBookmarkWithDetails({ uri: uri6, title: "title" }); + + await addOpenPages(uri7, 1); + + // Now remove page 6 from history, so it is an unvisited bookmark. + await PlacesUtils.history.remove(uri6); + + info("Match everything"); + await check_autocomplete({ + search: "foo", + searchParam: "enable-actions", + matches: [ + { 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" }), + ], + }); + + // Note the next few tests do *not* get a search result as enable-actions + // isn't specified. + info("Match only history"); + await check_autocomplete({ + search: `foo ${UrlbarTokenizer.RESTRICT.HISTORY}`, + matches: [ + { uri: uri1, title: "title" }, + { uri: uri2, title: "title" }, + { uri: uri3, title: "title" }, + { uri: uri4, title: "title" }, + { uri: uri7, title: "title" }, + ], + }); + + info("Drop-down empty search matches history sorted by frecency desc"); + await check_autocomplete({ + search: "", + matches: [ + { uri: uri7, title: "title" }, + { uri: uri4, title: "title" }, + { uri: uri3, title: "title" }, + { uri: uri2, title: "title" }, + { uri: uri1, title: "title" }, + ], + }); + + info("Drop-down empty search matches only bookmarks"); + Services.prefs.setBoolPref("browser.urlbar.suggest.history", false); + Services.prefs.setBoolPref("browser.urlbar.suggest.bookmark", true); + await check_autocomplete({ + search: "", + matches: [ + { uri: uri2, title: "title", style: ["bookmark"] }, + { uri: uri4, title: "title", style: ["bookmark"] }, + { uri: uri5, title: "title", style: ["bookmark"] }, + { uri: uri6, title: "title", style: ["bookmark"] }, + ], + }); + + info("Drop-down empty search matches only open tabs"); + Services.prefs.setBoolPref("browser.urlbar.suggest.bookmark", false); + await check_autocomplete({ + search: "", + searchParam: "enable-actions", + matches: [makeSwitchToTabMatch("http://t.foo/6", { title: "title" })], + }); + + Services.prefs.clearUserPref("browser.urlbar.suggest.history"); + Services.prefs.clearUserPref("browser.urlbar.suggest.bookmark"); + + await cleanup(); +}); diff --git a/toolkit/components/places/tests/unifiedcomplete/test_escape_self.js b/toolkit/components/places/tests/unifiedcomplete/test_escape_self.js new file mode 100644 index 0000000000..7359c9540f --- /dev/null +++ b/toolkit/components/places/tests/unifiedcomplete/test_escape_self.js @@ -0,0 +1,33 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +/** + * Test bug 422698 to make sure searches with urls from the location bar + * correctly match itself when it contains escaped characters. + */ + +add_task(async function test_escape() { + Services.prefs.setBoolPref("browser.urlbar.autoFill", false); + + let uri1 = NetUtil.newURI("http://unescapeduri/"); + let uri2 = NetUtil.newURI("http://escapeduri/%40/"); + await PlacesTestUtils.addVisits([ + { uri: uri1, title: "title" }, + { uri: uri2, title: "title" }, + ]); + + info("Unescaped location matches itself"); + await check_autocomplete({ + search: "http://unescapeduri/", + matches: [{ uri: uri1, title: "title" }], + }); + + info("Escaped location matches itself"); + await check_autocomplete({ + search: "http://escapeduri/%40/", + matches: [{ uri: uri2, title: "title" }], + }); + + await cleanup(); +}); diff --git a/toolkit/components/places/tests/unifiedcomplete/test_history_autocomplete_tags.js b/toolkit/components/places/tests/unifiedcomplete/test_history_autocomplete_tags.js new file mode 100644 index 0000000000..2cbe150bfa --- /dev/null +++ b/toolkit/components/places/tests/unifiedcomplete/test_history_autocomplete_tags.js @@ -0,0 +1,162 @@ +/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */ +/* vim:set ts=2 sw=2 sts=2 et: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +var current_test = 0; + +function AutoCompleteInput(aSearches) { + this.searches = aSearches; +} +AutoCompleteInput.prototype = { + constructor: AutoCompleteInput, + + searches: null, + + minResultsForPopup: 0, + timeout: 10, + searchParam: "", + textValue: "", + disableAutoComplete: false, + completeDefaultIndex: false, + + get searchCount() { + return this.searches.length; + }, + + getSearchAt(aIndex) { + return this.searches[aIndex]; + }, + + onSearchBegin() {}, + onSearchComplete() {}, + + popupOpen: false, + + popup: { + setSelectedIndex(aIndex) {}, + invalidate() {}, + + // nsISupports implementation + QueryInterface: ChromeUtils.generateQI(["nsIAutoCompletePopup"]), + }, + + // nsISupports implementation + QueryInterface: ChromeUtils.generateQI(["nsIAutoCompleteInput"]), +}; + +async function ensure_tag_results(uris, searchTerm) { + print("Searching for '" + searchTerm + "'"); + var controller = Cc["@mozilla.org/autocomplete/controller;1"].getService( + Ci.nsIAutoCompleteController + ); + + // Make an AutoCompleteInput that uses our searches + // and confirms results on search complete + var input = new AutoCompleteInput(["unifiedcomplete"]); + + controller.input = input; + + return new Promise(resolve => { + var numSearchesStarted = 0; + input.onSearchBegin = function() { + numSearchesStarted++; + Assert.equal(numSearchesStarted, 1); + }; + + input.onSearchComplete = function() { + Assert.equal(numSearchesStarted, 1); + Assert.equal( + controller.searchStatus, + uris.length + ? Ci.nsIAutoCompleteController.STATUS_COMPLETE_MATCH + : Ci.nsIAutoCompleteController.STATUS_COMPLETE_NO_MATCH + ); + Assert.equal(controller.matchCount, uris.length); + let vals = []; + for (let i = 0; i < controller.matchCount; i++) { + // Keep the URL for later because order of tag results is undefined + vals.push(controller.getValueAt(i)); + Assert.equal(controller.getStyleAt(i), "bookmark-tag"); + } + // Sort the results then check if we have the right items + vals.sort().forEach((val, i) => Assert.equal(val, uris[i])); + + resolve(); + }; + + controller.startSearch(searchTerm); + }); +} + +var uri1 = "http://site.tld/1/aaa"; +var uri2 = "http://site.tld/2/bbb"; +var uri3 = "http://site.tld/3/aaa"; +var uri4 = "http://site.tld/4/bbb"; +var uri5 = "http://site.tld/5/aaa"; +var uri6 = "http://site.tld/6/bbb"; + +var tests = [ + () => ensure_tag_results([uri1, uri4, uri6], "foo"), + () => ensure_tag_results([uri1], "foo aaa"), + () => ensure_tag_results([uri4, uri6], "foo bbb"), + () => ensure_tag_results([uri2, uri4, uri5, uri6], "bar"), + () => ensure_tag_results([uri5], "bar aaa"), + () => ensure_tag_results([uri2, uri4, uri6], "bar bbb"), + () => ensure_tag_results([uri3, uri5, uri6], "cheese"), + () => ensure_tag_results([uri3, uri5], "chees aaa"), + () => ensure_tag_results([uri6], "chees bbb"), + () => ensure_tag_results([uri4, uri6], "fo bar"), + () => ensure_tag_results([], "fo bar aaa"), + () => ensure_tag_results([uri4, uri6], "fo bar bbb"), + () => ensure_tag_results([uri4, uri6], "ba foo"), + () => ensure_tag_results([], "ba foo aaa"), + () => ensure_tag_results([uri4, uri6], "ba foo bbb"), + () => ensure_tag_results([uri5, uri6], "ba chee"), + () => ensure_tag_results([uri5], "ba chee aaa"), + () => ensure_tag_results([uri6], "ba chee bbb"), + () => ensure_tag_results([uri5, uri6], "cheese bar"), + () => ensure_tag_results([uri5], "cheese bar aaa"), + () => ensure_tag_results([uri6], "chees bar bbb"), + () => ensure_tag_results([uri6], "cheese bar foo"), + () => ensure_tag_results([], "foo bar cheese aaa"), + () => ensure_tag_results([uri6], "foo bar cheese bbb"), +]; + +/** + * Properly tags a uri adding it to bookmarks. + * + * @param url + * The nsIURI to tag. + * @param tags + * The tags to add. + */ +async function tagURI(url, tags) { + await PlacesUtils.bookmarks.insert({ + parentGuid: PlacesUtils.bookmarks.unfiledGuid, + url, + title: "A title", + }); + PlacesUtils.tagging.tagURI(uri(url), tags); +} + +/** + * Test history autocomplete + */ +add_task(async function test_history_autocomplete_tags() { + // always search in history + bookmarks, no matter what the default is + Services.prefs.setIntPref("browser.urlbar.search.sources", 3); + Services.prefs.setIntPref("browser.urlbar.default.behavior", 0); + + await tagURI(uri1, ["foo"]); + await tagURI(uri2, ["bar"]); + await tagURI(uri3, ["cheese"]); + await tagURI(uri4, ["foo bar"]); + await tagURI(uri5, ["bar cheese"]); + await tagURI(uri6, ["foo bar cheese"]); + + for (let tagTest of tests) { + await tagTest(); + } +}); diff --git a/toolkit/components/places/tests/unifiedcomplete/test_ignore_protocol.js b/toolkit/components/places/tests/unifiedcomplete/test_ignore_protocol.js new file mode 100644 index 0000000000..8140cc211a --- /dev/null +++ b/toolkit/components/places/tests/unifiedcomplete/test_ignore_protocol.js @@ -0,0 +1,26 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +/** + * Test bug 424509 to make sure searching for "h" doesn't match "http" of urls. + */ + +add_task(async function test_escape() { + Services.prefs.setBoolPref("browser.urlbar.autoFill", false); + + let uri1 = NetUtil.newURI("http://site/"); + let uri2 = NetUtil.newURI("http://happytimes/"); + await PlacesTestUtils.addVisits([ + { uri: uri1, title: "title" }, + { uri: uri2, title: "title" }, + ]); + + info("Searching for h matches site and not http://"); + await check_autocomplete({ + search: "h", + matches: [{ uri: uri2, title: "title" }], + }); + + await cleanup(); +}); diff --git a/toolkit/components/places/tests/unifiedcomplete/test_keyword_search.js b/toolkit/components/places/tests/unifiedcomplete/test_keyword_search.js new file mode 100644 index 0000000000..1e889da3fd --- /dev/null +++ b/toolkit/components/places/tests/unifiedcomplete/test_keyword_search.js @@ -0,0 +1,178 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +/** + * Test for bug 392143 that puts keyword results into the autocomplete. Makes + * sure that multiple parameter queries get spaces converted to +, + converted + * to %2B, non-ascii become escaped, and pages in history that match the + * keyword uses the page's title. + * + * Also test for bug 249468 by making sure multiple keyword bookmarks with the + * same keyword appear in the list. + */ + +add_task(async function test_keyword_search() { + let uri1 = NetUtil.newURI("http://abc/?search=%s"); + let uri2 = NetUtil.newURI("http://abc/?search=ThisPageIsInHistory"); + let uri3 = NetUtil.newURI("http://somedomain.example/key"); + await PlacesTestUtils.addVisits([ + { uri: uri1, title: "Generic page title" }, + { uri: uri2, title: "Generic page title" }, + { uri: uri3, title: "This page uri contains the keyword" }, + ]); + await PlacesTestUtils.addBookmarkWithDetails({ + uri: uri1, + title: "Bookmark title", + keyword: "key", + }); + + info("Plain keyword query"); + await check_autocomplete({ + search: "key term", + matches: [ + { + uri: NetUtil.newURI("http://abc/?search=term"), + title: "abc", + style: ["keyword", "heuristic"], + }, + ], + }); + + info("Plain keyword UC"); + await check_autocomplete({ + search: "key TERM", + matches: [ + { + uri: NetUtil.newURI("http://abc/?search=TERM"), + title: "abc", + style: ["keyword", "heuristic"], + }, + ], + }); + + info("Multi-word keyword query"); + await check_autocomplete({ + search: "key multi word", + matches: [ + { + uri: NetUtil.newURI("http://abc/?search=multi%20word"), + title: "abc", + style: ["keyword", "heuristic"], + }, + ], + }); + + info("Keyword query with +"); + await check_autocomplete({ + search: "key blocking+", + matches: [ + { + uri: NetUtil.newURI("http://abc/?search=blocking%2B"), + title: "abc", + style: ["keyword", "heuristic"], + }, + ], + }); + + info("Unescaped term in query"); + await check_autocomplete({ + search: "key ユニコード", + matches: [ + { + uri: NetUtil.newURI("http://abc/?search=ユニコード"), + title: "abc", + style: ["keyword", "heuristic"], + }, + ], + }); + + info("Keyword with query that happens to match a page"); + await check_autocomplete({ + search: "key ThisPageIsInHistory", + matches: [ + { + uri: NetUtil.newURI("http://abc/?search=ThisPageIsInHistory"), + title: "abc", + style: ["keyword", "heuristic"], + }, + ], + }); + + info("Keyword with query that partially matches a page"); + await check_autocomplete({ + search: "key ThisPage", + matches: [ + { + uri: NetUtil.newURI("http://abc/?search=ThisPage"), + title: "abc", + style: ["keyword", "heuristic"], + }, + { + uri: NetUtil.newURI("http://abc/?search=ThisPageIsInHistory"), + title: "Generic page title", + }, + ], + }); + + info("Keyword without query (without space)"); + await check_autocomplete({ + search: "key", + matches: [ + { + uri: NetUtil.newURI("http://abc/?search="), + style: ["keyword", "heuristic"], + }, + { + uri: NetUtil.newURI("http://abc/?search=%s"), + title: "Bookmark title", + style: ["bookmark"], + }, + { + uri: NetUtil.newURI("http://abc/?search=ThisPageIsInHistory"), + title: "Generic page title", + }, + ], + }); + + info("Keyword without query (with space)"); + await check_autocomplete({ + search: "key ", + matches: [ + { + uri: NetUtil.newURI("http://abc/?search="), + style: ["keyword", "heuristic"], + }, + { + uri: NetUtil.newURI("http://abc/?search=%s"), + title: "Bookmark title", + style: ["bookmark"], + }, + { + uri: NetUtil.newURI("http://abc/?search=ThisPageIsInHistory"), + title: "Generic page title", + }, + ], + }); + + info("Bug 1228111 - Keyword with a space in front"); + await check_autocomplete({ + search: " key test", + matches: [ + { + uri: NetUtil.newURI("http://abc/?search=test"), + title: "abc", + style: ["keyword", "heuristic"], + }, + ], + }); + + info("Bug 1481319 - Keyword with a prefix in front"); + await check_autocomplete({ + search: "http://key", + matches: [{ uri: uri3, title: "This page uri contains the keyword" }], + completed: "http://key", + }); + + await cleanup(); +}); diff --git a/toolkit/components/places/tests/unifiedcomplete/test_keyword_search_actions.js b/toolkit/components/places/tests/unifiedcomplete/test_keyword_search_actions.js new file mode 100644 index 0000000000..35be972a8a --- /dev/null +++ b/toolkit/components/places/tests/unifiedcomplete/test_keyword_search_actions.js @@ -0,0 +1,357 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +/** + * Test for bug 392143 that puts keyword results into the autocomplete. Makes + * sure that multiple parameter queries get spaces converted to +, + converted + * to %2B, non-ascii become escaped, and pages in history that match the + * keyword uses the page's title. + * + * Also test for bug 249468 by making sure multiple keyword bookmarks with the + * same keyword appear in the list. + */ + +add_task(async function test_keyword_search() { + let uri1 = "http://abc/?search=%s"; + let uri2 = "http://abc/?search=ThisPageIsInHistory"; + let uri3 = "http://abc/?search=%s&raw=%S"; + let uri4 = "http://abc/?search=%s&raw=%S&mozcharset=ISO-8859-1"; + let uri5 = "http://def/?search=%s"; + let uri6 = "http://ghi/?search=%s&raw=%S"; + await PlacesTestUtils.addVisits([ + { uri: uri1 }, + { uri: uri2 }, + { uri: uri3 }, + { uri: uri6 }, + ]); + await PlacesTestUtils.addBookmarkWithDetails({ + uri: uri1, + title: "Keyword", + keyword: "key", + }); + await PlacesTestUtils.addBookmarkWithDetails({ + uri: uri1, + title: "Post", + keyword: "post", + postData: "post_search=%s", + }); + await PlacesTestUtils.addBookmarkWithDetails({ + uri: uri3, + title: "Encoded", + keyword: "encoded", + }); + await PlacesTestUtils.addBookmarkWithDetails({ + uri: uri4, + title: "Charset", + keyword: "charset", + }); + await PlacesTestUtils.addBookmarkWithDetails({ + uri: uri2, + title: "Noparam", + keyword: "noparam", + }); + await PlacesTestUtils.addBookmarkWithDetails({ + uri: uri2, + title: "Noparam-Post", + keyword: "post_noparam", + postData: "noparam=1", + }); + await PlacesTestUtils.addBookmarkWithDetails({ + uri: uri5, + title: "Keyword", + keyword: "key2", + }); + await PlacesTestUtils.addBookmarkWithDetails({ + uri: uri6, + title: "Charset-history", + keyword: "charset_history", + }); + + await PlacesUtils.history.update({ + url: uri6, + annotations: new Map([[PlacesUtils.CHARSET_ANNO, "ISO-8859-1"]]), + }); + + info("Plain keyword query"); + await check_autocomplete({ + search: "key term", + searchParam: "enable-actions", + matches: [ + { + uri: makeActionURI("keyword", { + url: "http://abc/?search=term", + keyword: "key", + input: "key term", + }), + title: "abc", + style: ["action", "keyword", "heuristic"], + }, + ], + }); + + info("Plain keyword UC"); + await check_autocomplete({ + search: "key TERM", + matches: [ + { + uri: "http://abc/?search=TERM", + title: "abc", + style: ["keyword", "heuristic"], + }, + ], + }); + + info("Multi-word keyword query"); + await check_autocomplete({ + search: "key multi word", + searchParam: "enable-actions", + matches: [ + { + uri: makeActionURI("keyword", { + url: "http://abc/?search=multi%20word", + keyword: "key", + input: "key multi word", + }), + title: "abc", + style: ["action", "keyword", "heuristic"], + }, + ], + }); + + info("Keyword query with +"); + await check_autocomplete({ + search: "key blocking+", + searchParam: "enable-actions", + matches: [ + { + uri: makeActionURI("keyword", { + url: "http://abc/?search=blocking%2B", + keyword: "key", + input: "key blocking+", + }), + title: "abc", + style: ["action", "keyword", "heuristic"], + }, + ], + }); + + info("Unescaped term in query"); + // ... but note that UnifiedComplete calls encodeURIComponent() on the query + // string when it builds the URL, so the expected result will have the + // ユニコード substring encoded in the URL. + await check_autocomplete({ + search: "key ユニコード", + searchParam: "enable-actions", + matches: [ + { + uri: makeActionURI("keyword", { + url: "http://abc/?search=" + encodeURIComponent("ユニコード"), + keyword: "key", + input: "key ユニコード", + }), + title: "abc", + style: ["action", "keyword", "heuristic"], + }, + ], + }); + + info("Keyword that happens to match a page"); + await check_autocomplete({ + search: "key ThisPageIsInHistory", + searchParam: "enable-actions", + matches: [ + { + uri: makeActionURI("keyword", { + url: "http://abc/?search=ThisPageIsInHistory", + keyword: "key", + input: "key ThisPageIsInHistory", + }), + title: "abc", + style: ["action", "keyword", "heuristic"], + }, + ], + }); + + info("Keyword with partial page match"); + await check_autocomplete({ + search: "key ThisPage", + searchParam: "enable-actions", + matches: [ + { + uri: makeActionURI("keyword", { + url: "http://abc/?search=ThisPage", + keyword: "key", + input: "key ThisPage", + }), + title: "abc", + style: ["action", "keyword", "heuristic"], + }, + // Only the most recent bookmark for the URL: + { + value: "http://abc/?search=ThisPageIsInHistory", + title: "Noparam-Post", + style: ["bookmark"], + }, + ], + }); + + // For the keyword with no query terms (with or without space after), the + // domain is different from the other tests because otherwise all the other + // test bookmarks and history entries would be matches. + info("Keyword without query (without space)"); + await check_autocomplete({ + search: "key2", + searchParam: "enable-actions", + matches: [ + { + uri: makeActionURI("keyword", { + url: "http://def/?search=", + keyword: "key2", + input: "key2", + }), + style: ["action", "keyword", "heuristic"], + }, + { uri: uri5, title: "Keyword", style: ["bookmark"] }, + ], + }); + + info("Keyword without query (with space)"); + await check_autocomplete({ + search: "key2 ", + searchParam: "enable-actions", + matches: [ + { + uri: makeActionURI("keyword", { + url: "http://def/?search=", + keyword: "key2", + input: "key2 ", + }), + style: ["action", "keyword", "heuristic"], + }, + { uri: uri5, title: "Keyword", style: ["bookmark"] }, + ], + }); + + info("POST Keyword"); + await check_autocomplete({ + search: "post foo", + searchParam: "enable-actions", + matches: [ + { + uri: makeActionURI("keyword", { + url: "http://abc/?search=foo", + keyword: "post", + input: "post foo", + postData: "post_search=foo", + }), + title: "abc", + style: ["action", "keyword", "heuristic"], + }, + ], + }); + + info("escaping with default UTF-8 charset"); + await check_autocomplete({ + search: "encoded foé", + searchParam: "enable-actions", + matches: [ + { + uri: makeActionURI("keyword", { + url: "http://abc/?search=fo%C3%A9&raw=foé", + keyword: "encoded", + input: "encoded foé", + }), + title: "abc", + style: ["action", "keyword", "heuristic"], + }, + ], + }); + + info("escaping with forced ISO-8859-1 charset"); + await check_autocomplete({ + search: "charset foé", + searchParam: "enable-actions", + matches: [ + { + uri: makeActionURI("keyword", { + url: "http://abc/?search=fo%E9&raw=foé", + keyword: "charset", + input: "charset foé", + }), + title: "abc", + style: ["action", "keyword", "heuristic"], + }, + ], + }); + + info("escaping with ISO-8859-1 charset annotated in history"); + await check_autocomplete({ + search: "charset_history foé", + searchParam: "enable-actions", + matches: [ + { + uri: makeActionURI("keyword", { + url: "http://ghi/?search=fo%E9&raw=foé", + keyword: "charset_history", + input: "charset_history foé", + }), + title: "ghi", + style: ["action", "keyword", "heuristic"], + }, + ], + }); + + info("Bug 359809: escaping +, / and @ with default UTF-8 charset"); + await check_autocomplete({ + search: "encoded +/@", + searchParam: "enable-actions", + matches: [ + { + uri: makeActionURI("keyword", { + url: "http://abc/?search=%2B%2F%40&raw=+/@", + keyword: "encoded", + input: "encoded +/@", + }), + title: "abc", + style: ["action", "keyword", "heuristic"], + }, + ], + }); + + info("Bug 359809: escaping +, / and @ with forced ISO-8859-1 charset"); + await check_autocomplete({ + search: "charset +/@", + searchParam: "enable-actions", + matches: [ + { + uri: makeActionURI("keyword", { + url: "http://abc/?search=%2B%2F%40&raw=+/@", + keyword: "charset", + input: "charset +/@", + }), + title: "abc", + style: ["action", "keyword", "heuristic"], + }, + ], + }); + + info("Bug 1228111 - Keyword with a space in front"); + await check_autocomplete({ + search: " key test", + searchParam: "enable-actions", + matches: [ + { + uri: makeActionURI("keyword", { + url: "http://abc/?search=test", + keyword: "key", + input: " key test", + }), + title: "abc", + style: ["action", "keyword", "heuristic"], + }, + ], + }); + + await cleanup(); +}); diff --git a/toolkit/components/places/tests/unifiedcomplete/test_multi_word_search.js b/toolkit/components/places/tests/unifiedcomplete/test_multi_word_search.js new file mode 100644 index 0000000000..4187641c15 --- /dev/null +++ b/toolkit/components/places/tests/unifiedcomplete/test_multi_word_search.js @@ -0,0 +1,80 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +/** + * Test for bug 401869 to allow multiple words separated by spaces to match in + * the page title, page url, or bookmark title to be considered a match. All + * terms must match but not all terms need to be in the title, etc. + * + * Test bug 424216 by making sure bookmark titles are always shown if one is + * available. Also bug 425056 makes sure matches aren't found partially in the + * page title and partially in the bookmark. + */ + +add_task(async function test_match_beginning() { + let uri1 = NetUtil.newURI("http://a.b.c/d-e_f/h/t/p"); + let uri2 = NetUtil.newURI("http://d.e.f/g-h_i/h/t/p"); + let uri3 = NetUtil.newURI("http://g.h.i/j-k_l/h/t/p"); + let uri4 = NetUtil.newURI("http://j.k.l/m-n_o/h/t/p"); + await PlacesTestUtils.addVisits([ + { uri: uri1, title: "f(o)o b<a>r" }, + { uri: uri2, title: "b(a)r b<a>z" }, + { uri: uri3, title: "f(o)o b<a>r" }, + { uri: uri4, title: "f(o)o b<a>r" }, + ]); + await PlacesTestUtils.addBookmarkWithDetails({ + uri: uri3, + title: "f(o)o b<a>r", + }); + await PlacesTestUtils.addBookmarkWithDetails({ + uri: uri4, + title: "b(a)r b<a>z", + }); + + info("Match 2 terms all in url"); + await check_autocomplete({ + search: "c d", + matches: [{ uri: uri1, title: "f(o)o b<a>r" }], + }); + + info("Match 1 term in url and 1 term in title"); + await check_autocomplete({ + search: "b e", + matches: [ + { uri: uri1, title: "f(o)o b<a>r" }, + { uri: uri2, title: "b(a)r b<a>z" }, + ], + }); + + info("Match 3 terms all in title; display bookmark title if matched"); + await check_autocomplete({ + search: "b a z", + matches: [ + { uri: uri2, title: "b(a)r b<a>z" }, + { uri: uri4, title: "b(a)r b<a>z", style: ["bookmark"] }, + ], + }); + + info( + "Match 2 terms in url and 1 in title; make sure bookmark title is used for search" + ); + await check_autocomplete({ + search: "k f t", + matches: [{ uri: uri3, title: "f(o)o b<a>r", style: ["bookmark"] }], + }); + + info("Match 3 terms in url and 1 in title"); + await check_autocomplete({ + search: "d i g z", + matches: [{ uri: uri2, title: "b(a)r b<a>z" }], + }); + + info("Match nothing"); + await check_autocomplete({ + search: "m o z i", + matches: [], + }); + + await cleanup(); +}); diff --git a/toolkit/components/places/tests/unifiedcomplete/test_preloaded_sites.js b/toolkit/components/places/tests/unifiedcomplete/test_preloaded_sites.js new file mode 100644 index 0000000000..62c12ad79c --- /dev/null +++ b/toolkit/components/places/tests/unifiedcomplete/test_preloaded_sites.js @@ -0,0 +1,322 @@ +/** + * Test for bug 1211726 - preload list of top web sites for better + * autocompletion on empty profiles. + */ + +const PREF_FEATURE_ENABLED = "browser.urlbar.usepreloadedtopurls.enabled"; +const PREF_FEATURE_EXPIRE_DAYS = + "browser.urlbar.usepreloadedtopurls.expire_days"; + +const autocompleteObject = Cc[ + "@mozilla.org/autocomplete/search;1?name=unifiedcomplete" +].getService(Ci.mozIPlacesAutoComplete); + +Cu.importGlobalProperties(["fetch"]); + +// With or without trailing slash - no matter. URI.spec does have it always. +// Then, check_autocomplete() doesn't cut it off (uses stripPrefix()). +let yahoooURI = NetUtil.newURI("https://yahooo.com/"); +let gooogleURI = NetUtil.newURI("https://gooogle.com/"); + +autocompleteObject.populatePreloadedSiteStorage([ + [yahoooURI.spec, "Yahooo"], + [gooogleURI.spec, "Gooogle"], +]); + +async function assert_feature_works(condition) { + info("List Results do appear " + condition); + await check_autocomplete({ + search: "ooo", + matches: [ + { uri: yahoooURI, title: "Yahooo", style: ["preloaded-top-site"] }, + { uri: gooogleURI, title: "Gooogle", style: ["preloaded-top-site"] }, + ], + }); + + info("Autofill does appear " + condition); + await check_autocomplete({ + search: "gooo", + autofilled: "gooogle.com/", // Will fail without trailing slash + completed: "https://gooogle.com/", + }); +} + +async function assert_feature_does_not_appear(condition) { + info("List Results don't appear " + condition); + await check_autocomplete({ + search: "ooo", + matches: [], + }); + + info("Autofill doesn't appear " + condition); + // "search" is what you type, + // "autofilled" is what you get in response in the url bar, + // "completed" is what you get there when you hit enter. + // So if they are all equal - it's the proof there was no autofill + // (knowing we have a suitable preloaded top site). + await check_autocomplete({ + search: "gooo", + autofilled: "gooo", + completed: "gooo", + }); +} + +add_task(async function test_it_works() { + // Not expired but OFF + Services.prefs.setIntPref(PREF_FEATURE_EXPIRE_DAYS, 14); + Services.prefs.setBoolPref(PREF_FEATURE_ENABLED, false); + await assert_feature_does_not_appear("when OFF by prefs"); + + // Now turn it ON + Services.prefs.setBoolPref(PREF_FEATURE_ENABLED, true); + await assert_feature_works("when ON by prefs"); + + // And expire + Services.prefs.setIntPref(PREF_FEATURE_EXPIRE_DAYS, 0); + await assert_feature_does_not_appear("when expired"); + + await cleanup(); +}); + +add_task(async function test_sorting_against_bookmark() { + let boookmarkURI = NetUtil.newURI("https://boookmark.com"); + await PlacesTestUtils.addBookmarkWithDetails({ + uri: boookmarkURI, + title: "Boookmark", + }); + + Services.prefs.setBoolPref(PREF_FEATURE_ENABLED, true); + Services.prefs.setIntPref(PREF_FEATURE_EXPIRE_DAYS, 14); + + info("Preloaded Top Sites are placed lower than Bookmarks"); + await check_autocomplete({ + checkSorting: true, + search: "ooo", + matches: [ + { uri: boookmarkURI, title: "Boookmark", style: ["bookmark"] }, + { uri: yahoooURI, title: "Yahooo", style: ["preloaded-top-site"] }, + { uri: gooogleURI, title: "Gooogle", style: ["preloaded-top-site"] }, + ], + }); + + await cleanup(); +}); + +add_task(async function test_sorting_against_history() { + let histoooryURI = NetUtil.newURI("https://histooory.com"); + await PlacesTestUtils.addVisits({ uri: histoooryURI, title: "Histooory" }); + + Services.prefs.setBoolPref(PREF_FEATURE_ENABLED, true); + Services.prefs.setIntPref(PREF_FEATURE_EXPIRE_DAYS, 14); + + info("Preloaded Top Sites are placed lower than History entries"); + await check_autocomplete({ + checkSorting: true, + search: "ooo", + matches: [ + { uri: histoooryURI, title: "Histooory" }, + { uri: yahoooURI, title: "Yahooo", style: ["preloaded-top-site"] }, + { uri: gooogleURI, title: "Gooogle", style: ["preloaded-top-site"] }, + ], + }); + + await cleanup(); +}); + +add_task(async function test_scheme_and_www() { + // Order is important to check sorting + let sites = [ + ["https://www.ooops-https-www.com/", "Ooops"], + ["https://ooops-https.com/", "Ooops"], + ["HTTP://ooops-HTTP.com/", "Ooops"], + ["HTTP://www.ooops-HTTP-www.com/", "Ooops"], + ["https://foo.com/", "Title with www"], + ["https://www.bar.com/", "Tile"], + ]; + + let titlesMap = new Map(sites); + + autocompleteObject.populatePreloadedSiteStorage(sites); + + let tests = [ + // User typed, + // Inline autofill (`autofilled`), + // Substitute after enter is pressed (`completed`), + // [List matches, with sorting] + // not tested if omitted + // !!! first one is always an autofill entry !!! + + [ + // Protocol by itself doesn't match anything + "https://", + "https://", + "https://", + [], + ], + + [ + "www.", + "www.ooops-https-www.com/", + "https://www.ooops-https-www.com/", + [ + ["www.ooops-https-www.com/", "https://www.ooops-https-www.com"], + "HTTP://www.ooops-HTTP-www.com/", + "https://www.bar.com/", + ], + ], + + [ + "http://www.", + "http://www.ooops-http-www.com/", + "http://www.ooops-http-www.com/", + [["http://www.ooops-http-www.com/", "www.ooops-http-www.com"]], + ], + + ["ftp://ooops", "ftp://ooops", "ftp://ooops", []], + + [ + "ww", + "www.ooops-https-www.com/", + "https://www.ooops-https-www.com/", + [ + ["www.ooops-https-www.com/", "https://www.ooops-https-www.com"], + "HTTP://www.ooops-HTTP-www.com/", + ["https://foo.com/", "Title with www", ["preloaded-top-site"]], + "https://www.bar.com/", + ], + ], + + [ + "ooops", + "ooops-https-www.com/", + "https://www.ooops-https-www.com/", + [ + ["ooops-https-www.com/", "https://www.ooops-https-www.com"], + "https://ooops-https.com/", + "HTTP://ooops-HTTP.com/", + "HTTP://www.ooops-HTTP-www.com/", + ], + ], + + [ + "www.ooops", + "www.ooops-https-www.com/", + "https://www.ooops-https-www.com/", + [ + ["www.ooops-https-www.com/", "https://www.ooops-https-www.com"], + "HTTP://www.ooops-HTTP-www.com/", + ], + ], + + [ + "ooops-https-www", + "ooops-https-www.com/", + "https://www.ooops-https-www.com/", + [["ooops-https-www.com/", "https://www.ooops-https-www.com"]], + ], + + ["www.ooops-https.", "www.ooops-https.", "www.ooops-https.", []], + + [ + "https://ooops", + "https://ooops-https-www.com/", + "https://www.ooops-https-www.com/", + [ + ["https://ooops-https-www.com/", "https://www.ooops-https-www.com"], + "https://ooops-https.com/", + ], + ], + + [ + "https://www.ooops", + "https://www.ooops-https-www.com/", + "https://www.ooops-https-www.com/", + [["https://www.ooops-https-www.com/", "https://www.ooops-https-www.com"]], + ], + + [ + "http://www.ooops-http.", + "http://www.ooops-http.", + "http://www.ooops-http.", + [], + ], + + ["http://ooops-https", "http://ooops-https", "http://ooops-https", []], + ]; + + function toMatch(entry, index) { + if (Array.isArray(entry)) { + return { + value: entry[0], + comment: entry[1], + style: entry[2] || ["autofill", "heuristic", "preloaded-top-site"], + }; + } + return { + uri: NetUtil.newURI(entry), + title: titlesMap.get(entry), + style: ["preloaded-top-site"], + }; + } + + for (let test of tests) { + let matches = test[3] ? test[3].map(toMatch) : null; + info("User types: " + test[0]); + await check_autocomplete({ + checkSorting: true, + search: test[0], + autofilled: test[1].toLowerCase(), + completed: test[2].toLowerCase(), + matches, + }); + } + + await cleanup(); +}); + +add_task(async function test_data_file() { + let response = await fetch( + "chrome://global/content/unifiedcomplete-top-urls.json" + ); + + info("Source file is supplied and fetched OK"); + Assert.ok(response.ok); + + info("The JSON is parsed"); + let sites = await response.json(); + + info("Storage is populated"); + autocompleteObject.populatePreloadedSiteStorage(sites); + + let lastSite = sites.pop(); + let uri = NetUtil.newURI(lastSite[0]); + + info("Storage is populated from JSON correctly"); + await check_autocomplete({ + search: uri.host, + autofilled: uri.host + "/", + completed: uri.spec, + }); + + await cleanup(); +}); + +add_task(async function test_partial_scheme() { + // "tt" should not result in a match of "ttps://whatever.com/". + autocompleteObject.populatePreloadedSiteStorage([ + ["http://www.ttt.com/", "Test"], + ]); + await check_autocomplete({ + search: "tt", + autofilled: "ttt.com/", + completed: "http://www.ttt.com/", + matches: [ + { + value: "ttt.com/", + comment: "www.ttt.com", + style: ["autofill", "heuristic", "preloaded-top-site"], + }, + ], + }); + await cleanup(); +}); diff --git a/toolkit/components/places/tests/unifiedcomplete/test_remote_tab_matches.js b/toolkit/components/places/tests/unifiedcomplete/test_remote_tab_matches.js new file mode 100644 index 0000000000..c157506538 --- /dev/null +++ b/toolkit/components/places/tests/unifiedcomplete/test_remote_tab_matches.js @@ -0,0 +1,408 @@ +/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- + * vim:set ts=2 sw=2 sts=2 et: + */ +"use strict"; + +const { Weave } = ChromeUtils.import("resource://services-sync/main.js"); + +Services.prefs.setCharPref("services.sync.username", "someone@somewhere.com"); +Services.prefs.setCharPref("services.sync.registerEngines", ""); + +// A mock "Tabs" engine which autocomplete will use instead of the real +// engine. We pass a constructor that Sync creates. +function MockTabsEngine() { + this.clients = null; // We'll set this dynamically +} + +MockTabsEngine.prototype = { + name: "tabs", + + startTracking() {}, + getAllClients() { + return this.clients; + }, +}; + +// A clients engine that doesn't need to be a constructor. +let MockClientsEngine = { + getClientType(guid) { + Assert.ok(guid.endsWith("desktop") || guid.endsWith("mobile")); + return guid.endsWith("mobile") ? "phone" : "desktop"; + }, + remoteClientExists(id) { + return true; + }, + getClientName(id) { + return id.endsWith("mobile") ? "My Phone" : "My Desktop"; + }, +}; + +// Tell Sync about the mocks. +Weave.Service.engineManager.register(MockTabsEngine); + +// Tell the Sync XPCOM service it is initialized. +let weaveXPCService = Cc["@mozilla.org/weave/service;1"].getService( + Ci.nsISupports +).wrappedJSObject; +weaveXPCService.ready = true; + +// Configure the singleton engine for a test. +function configureEngine(clients) { + // Configure the instance Sync created. + let engine = Weave.Service.engineManager.get("tabs"); + engine.clients = clients; + Weave.Service.clientsEngine = MockClientsEngine; + // Send an observer that pretends the engine just finished a sync. + Services.obs.notifyObservers(null, "weave:engine:sync:finish", "tabs"); +} + +// Make a match object suitable for passing to check_autocomplete. +function makeRemoteTabMatch(url, deviceName, extra = {}) { + return { + uri: makeActionURI("remotetab", { url, deviceName }), + title: extra.title || url, + style: ["action", "remotetab"], + icon: extra.icon, + }; +} + +add_task(async function test_minimal() { + // The minimal client and tabs info we can get away with. + configureEngine({ + guid_desktop: { + id: "desktop", + tabs: [ + { + urlHistory: ["http://example.com/"], + }, + ], + }, + }); + + await check_autocomplete({ + search: "ex", + searchParam: "enable-actions", + matches: [makeRemoteTabMatch("http://example.com/", "My Desktop")], + }); +}); + +add_task(async function test_maximal() { + // Every field that could possibly exist on a remote record. + configureEngine({ + guid_mobile: { + id: "mobile", + tabs: [ + { + urlHistory: ["http://example.com/"], + title: "An Example", + icon: "http://favicon", + }, + ], + }, + }); + + await check_autocomplete({ + search: "ex", + searchParam: "enable-actions", + matches: [ + makeRemoteTabMatch("http://example.com/", "My Phone", { + title: "An Example", + icon: "moz-anno:favicon:http://favicon/", + }), + ], + }); +}); + +add_task(async function test_noShowIcons() { + Services.prefs.setBoolPref("services.sync.syncedTabs.showRemoteIcons", false); + configureEngine({ + guid_mobile: { + id: "mobile", + tabs: [ + { + urlHistory: ["http://example.com/"], + title: "An Example", + icon: "http://favicon", + }, + ], + }, + }); + + await check_autocomplete({ + search: "ex", + searchParam: "enable-actions", + matches: [ + makeRemoteTabMatch("http://example.com/", "My Phone", { + title: "An Example", + // expecting the default favicon due to that pref. + icon: "", + }), + ], + }); + Services.prefs.clearUserPref("services.sync.syncedTabs.showRemoteIcons"); +}); + +add_task(async function test_dontMatchSyncedTabs() { + Services.prefs.setBoolPref("services.sync.syncedTabs.showRemoteTabs", false); + configureEngine({ + guid_mobile: { + id: "mobile", + tabs: [ + { + urlHistory: ["http://example.com/"], + title: "An Example", + icon: "http://favicon", + }, + ], + }, + }); + + await check_autocomplete({ + search: "ex", + searchParam: "enable-actions", + matches: [], + }); + Services.prefs.clearUserPref("services.sync.syncedTabs.showRemoteTabs"); +}); + +add_task(async function test_matches_title() { + // URL doesn't match search expression, should still match the title. + configureEngine({ + guid_mobile: { + id: "mobile", + tabs: [ + { + urlHistory: ["http://foo.com/"], + title: "An Example", + }, + ], + }, + }); + + await check_autocomplete({ + search: "ex", + searchParam: "enable-actions", + matches: [ + makeRemoteTabMatch("http://foo.com/", "My Phone", { + title: "An Example", + }), + ], + }); +}); + +add_task(async function test_localtab_matches_override() { + // We have an open tab to the same page on a remote device, only "switch to + // tab" should appear as duplicate detection removed the remote one. + + // First setup Sync to have the page as a remote tab. + configureEngine({ + guid_mobile: { + id: "mobile", + tabs: [ + { + urlHistory: ["http://foo.com/"], + title: "An Example", + }, + ], + }, + }); + + // Setup Places to think the tab is open locally. + let uri = NetUtil.newURI("http://foo.com/"); + await PlacesTestUtils.addVisits([{ uri, title: "An Example" }]); + await addOpenPages(uri, 1); + + await check_autocomplete({ + search: "ex", + searchParam: "enable-actions", + matches: [makeSwitchToTabMatch("http://foo.com/", { title: "An Example" })], + }); + await removeOpenPages(uri, 1); +}); + +add_task(async function test_remotetab_matches_override() { + // If We have an history result to the same page, we should only get the + // remote tab match. + let url = "http://foo.remote.com/"; + // First setup Sync to have the page as a remote tab. + configureEngine({ + guid_mobile: { + id: "mobile", + tabs: [ + { + urlHistory: [url], + title: "An Example", + }, + ], + }, + }); + + // Setup Places to think the tab is open locally. + await PlacesTestUtils.addVisits(url); + + await check_autocomplete({ + search: "rem", + searchParam: "enable-actions", + matches: [ + makeRemoteTabMatch("http://foo.remote.com/", "My Phone", { + title: "An Example", + }), + ], + }); +}); + +add_task(async function test_many_remotetab_matches() { + await PlacesUtils.history.clear(); + + // In case we have many results, the most recent ones should be added on top, + // while others should be appended. + let url = "http://foo.remote.com/"; + // First setup Sync to have the page as a remote tab. + configureEngine({ + guid_mobile: { + id: "mobile", + tabs: Array(5) + .fill(0) + .map((e, i) => ({ + urlHistory: [`${url}${i}`], + title: "A title", + lastUsed: Date.now() / 1000 - i * 86400, // i days ago. + })), + }, + }); + + // Also add a local history result. + let historyUrl = url + "history/"; + await PlacesTestUtils.addVisits(historyUrl); + + await check_autocomplete({ + search: "rem", + searchParam: "enable-actions", + checkSorting: true, + matches: [ + makeRemoteTabMatch("http://foo.remote.com/0", "My Phone", { + title: "A title", + }), + makeRemoteTabMatch("http://foo.remote.com/1", "My Phone", { + title: "A title", + }), + makeRemoteTabMatch("http://foo.remote.com/2", "My Phone", { + title: "A title", + }), + { + uri: Services.io.newURI(historyUrl), + title: "test visit for " + historyUrl, + }, + makeRemoteTabMatch("http://foo.remote.com/3", "My Phone", { + title: "A title", + }), + makeRemoteTabMatch("http://foo.remote.com/4", "My Phone", { + title: "A title", + }), + ], + }); +}); + +add_task(async function test_maxResults() { + await PlacesUtils.history.clear(); + + let url = "http://foo.remote.com/"; + + // Add 10 mock remote tabs. Important: We set `lastUsed` so that these tabs + // are newer than RECENT_REMOTE_TAB_THRESHOLD_MS in UnifiedComplete. That way + // they are added immediately in _matchRemoteTabs rather than being pushed + // onto _extraRemoteTabRows and then added later in execute(). The former + // does not check maxResults, but the latter does, and in this test, we want + // to verify that PlacesRemoteTabsAutocompleteProvider.getMatches returns at + // most ceil(maxResults / 2) tabs. + configureEngine({ + guid_mobile: { + id: "mobile", + tabs: Array(10) + .fill(0) + .map((e, i) => ({ + urlHistory: [`${url}${i}`], + title: "A title", + lastUsed: Date.now() - i, + })), + }, + }); + + // Set maxResults to 4 in our search. + await check_autocomplete({ + search: "rem", + searchParam: "enable-actions max-results:4", + checkSorting: true, + matches: [ + makeRemoteTabMatch("http://foo.remote.com/0", "My Phone", { + title: "A title", + }), + makeRemoteTabMatch("http://foo.remote.com/1", "My Phone", { + title: "A title", + }), + makeRemoteTabMatch("http://foo.remote.com/2", "My Phone", { + title: "A title", + }), + makeRemoteTabMatch("http://foo.remote.com/3", "My Phone", { + title: "A title", + }), + ], + }); +}); + +add_task(async function test_restrictionCharacter() { + await PlacesUtils.history.clear(); + + let url = "http://foo.remote.com/"; + + configureEngine({ + guid_mobile: { + id: "mobile", + tabs: Array(10) + .fill(0) + .map((e, i) => ({ + urlHistory: [`${url}${i}`], + title: "A title", + lastUsed: Date.now() - i, + })), + }, + }); + + // Also add an open page. + let uri = Services.io.newURI(`${url}openpage/`); + await PlacesTestUtils.addVisits([{ uri, title: "An Example" }]); + await addOpenPages(uri, 1); + + // Set maxResults to 7 in our search. 7 results should be returned: + // ceil(maxResults / 2) remote tabs, then the open tab, then 2 more remote tab + // results to get to 7 total. + await check_autocomplete({ + search: UrlbarTokenizer.RESTRICT.OPENPAGE, + searchParam: "enable-actions max-results:7", + checkSorting: true, + matches: [ + makeRemoteTabMatch("http://foo.remote.com/0", "My Phone", { + title: "A title", + }), + makeRemoteTabMatch("http://foo.remote.com/1", "My Phone", { + title: "A title", + }), + makeRemoteTabMatch("http://foo.remote.com/2", "My Phone", { + title: "A title", + }), + makeRemoteTabMatch("http://foo.remote.com/3", "My Phone", { + title: "A title", + }), + makeSwitchToTabMatch("http://foo.remote.com/openpage/", { + title: "An Example", + }), + makeRemoteTabMatch("http://foo.remote.com/4", "My Phone", { + title: "A title", + }), + makeRemoteTabMatch("http://foo.remote.com/5", "My Phone", { + title: "A title", + }), + ], + }); + await removeOpenPages(uri, 1); +}); diff --git a/toolkit/components/places/tests/unifiedcomplete/test_search_engine_alias.js b/toolkit/components/places/tests/unifiedcomplete/test_search_engine_alias.js new file mode 100644 index 0000000000..772334bb6d --- /dev/null +++ b/toolkit/components/places/tests/unifiedcomplete/test_search_engine_alias.js @@ -0,0 +1,108 @@ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ + +const SUGGESTIONS_ENGINE_NAME = "engine-suggestions.xml"; + +/** + * Tests search engine aliases. See + * browser/components/urlbar/tests/browser/browser_tokenAlias.js for tests of + * the token alias list (i.e. showing all aliased engines on a "@" query). + */ +// Basic test that uses two engines, a GET engine and a POST engine, neither +// providing search suggestions. +add_task(async function basicGetAndPost() { + // Note that head_autocomplete.js has already added a MozSearch engine. + // Here we add another engine with a search alias. + await Services.search.addEngineWithDetails("AliasedGETMozSearch", { + alias: "get", + method: "GET", + template: "http://s.example.com/search", + }); + await Services.search.addEngineWithDetails("AliasedPOSTMozSearch", { + alias: "post", + method: "POST", + template: "http://s.example.com/search", + }); + + await PlacesTestUtils.addVisits("http://s.example.com/search?q=firefox"); + let historyMatch = { + value: "http://s.example.com/search?q=firefox", + comment: "test visit for http://s.example.com/search?q=firefox", + }; + + for (let alias of ["get", "post"]) { + await check_autocomplete({ + search: alias, + searchParam: "enable-actions", + matches: [], + }); + + await check_autocomplete({ + search: `${alias} `, + searchParam: "enable-actions", + matches: [ + makeSearchMatch(`${alias} `, { + engineName: `Aliased${alias.toUpperCase()}MozSearch`, + searchQuery: "", + alias, + heuristic: true, + }), + historyMatch, + ], + }); + + await check_autocomplete({ + search: `${alias} fire`, + searchParam: "enable-actions", + matches: [ + makeSearchMatch(`${alias} fire`, { + engineName: `Aliased${alias.toUpperCase()}MozSearch`, + searchQuery: "fire", + alias, + heuristic: true, + }), + historyMatch, + ], + }); + + await check_autocomplete({ + search: `${alias} mozilla`, + searchParam: "enable-actions", + matches: [ + makeSearchMatch(`${alias} mozilla`, { + engineName: `Aliased${alias.toUpperCase()}MozSearch`, + searchQuery: "mozilla", + alias, + heuristic: true, + }), + ], + }); + + await check_autocomplete({ + search: `${alias} MoZiLlA`, + searchParam: "enable-actions", + matches: [ + makeSearchMatch(`${alias} MoZiLlA`, { + engineName: `Aliased${alias.toUpperCase()}MozSearch`, + searchQuery: "MoZiLlA", + alias, + heuristic: true, + }), + ], + }); + + await check_autocomplete({ + search: `${alias} mozzarella mozilla`, + searchParam: "enable-actions", + matches: [ + makeSearchMatch(`${alias} mozzarella mozilla`, { + engineName: `Aliased${alias.toUpperCase()}MozSearch`, + searchQuery: "mozzarella mozilla", + alias, + heuristic: true, + }), + ], + }); + } + await cleanup(); +}); diff --git a/toolkit/components/places/tests/unifiedcomplete/test_search_engine_restyle.js b/toolkit/components/places/tests/unifiedcomplete/test_search_engine_restyle.js new file mode 100644 index 0000000000..a9d4e8e95f --- /dev/null +++ b/toolkit/components/places/tests/unifiedcomplete/test_search_engine_restyle.js @@ -0,0 +1,90 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +const engineDomain = "s.example.com"; +add_task(async function setup() { + let engine = await Services.search.addEngineWithDetails("SearchEngine", { + method: "GET", + template: `http://${engineDomain}/search`, + searchGetParams: "q={searchTerms}", + }); + Services.prefs.setBoolPref("browser.urlbar.restyleSearches", true); + registerCleanupFunction(async () => { + Services.prefs.clearUserPref("browser.urlbar.restyleSearches"); + Services.search.removeEngine(engine); + }); +}); +add_task(async function test_searchEngine() { + let uri = Services.io.newURI(`http://${engineDomain}/search?q=Terms`); + await PlacesTestUtils.addVisits({ + uri, + title: "Terms - SearchEngine Search", + }); + + info("Past search terms should be styled."); + + await check_autocomplete({ + search: "term", + matches: [ + makeSearchMatch("Terms", { + engineName: "SearchEngine", + searchSuggestion: "Terms", + isSearchHistory: true, + style: ["favicon"], + }), + ], + }); + + info( + "Searching for a superset of the search string in history should not restyle." + ); + await check_autocomplete({ + search: "Terms Foo", + }); + + info("Bookmarked past searches should not be restyled"); + await PlacesTestUtils.addBookmarkWithDetails({ + uri, + title: "Terms - SearchEngine Search", + }); + + await check_autocomplete({ + search: "term", + matches: [ + { + uri, + title: "Terms - SearchEngine Search", + style: ["bookmark"], + }, + ], + }); + + await PlacesUtils.bookmarks.eraseEverything(); + + info("Past search terms should not be styled if restyling is disabled"); + Services.prefs.setBoolPref("browser.urlbar.restyleSearches", false); + await check_autocomplete({ + search: "term", + matches: [{ uri, title: "Terms - SearchEngine Search" }], + }); + Services.prefs.setBoolPref("browser.urlbar.restyleSearches", true); + + await cleanup(); +}); + +add_task(async function test_extraneousParameters() { + info("SERPs in history with extraneous parameters should not be restyled."); + let uri = Services.io.newURI( + `http://${engineDomain}/search?q=Terms&p=2&type=img` + ); + await PlacesTestUtils.addVisits({ + uri, + title: "Terms - SearchEngine Search", + }); + + await check_autocomplete({ + search: "term", + matches: [{ uri, title: "Terms - SearchEngine Search" }], + }); +}); diff --git a/toolkit/components/places/tests/unifiedcomplete/test_special_search.js b/toolkit/components/places/tests/unifiedcomplete/test_special_search.js new file mode 100644 index 0000000000..cee143c79c --- /dev/null +++ b/toolkit/components/places/tests/unifiedcomplete/test_special_search.js @@ -0,0 +1,539 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +/** + * Test for bug 395161 that allows special searches that restrict results to + * history/bookmark/tagged items and title/url matches. + * + * Test 485122 by making sure results don't have tags when restricting result + * to just history either by default behavior or dynamic query restrict. + */ + +function setSuggestPrefsToFalse() { + Services.prefs.setBoolPref("browser.urlbar.suggest.history", false); + Services.prefs.setBoolPref("browser.urlbar.suggest.bookmark", false); +} + +add_task(async function test_special_searches() { + let uri1 = NetUtil.newURI("http://url/"); + let uri2 = NetUtil.newURI("http://url/2"); + let uri3 = NetUtil.newURI("http://foo.bar/"); + let uri4 = NetUtil.newURI("http://foo.bar/2"); + let uri5 = NetUtil.newURI("http://url/star"); + let uri6 = NetUtil.newURI("http://url/star/2"); + let uri7 = NetUtil.newURI("http://foo.bar/star"); + let uri8 = NetUtil.newURI("http://foo.bar/star/2"); + let uri9 = NetUtil.newURI("http://url/tag"); + let uri10 = NetUtil.newURI("http://url/tag/2"); + let uri11 = NetUtil.newURI("http://foo.bar/tag"); + let uri12 = NetUtil.newURI("http://foo.bar/tag/2"); + await PlacesTestUtils.addVisits([ + { uri: uri1, title: "title", transition: TRANSITION_TYPED }, + { uri: uri2, title: "foo.bar" }, + { uri: uri3, title: "title" }, + { uri: uri4, title: "foo.bar", transition: TRANSITION_TYPED }, + { uri: uri6, title: "foo.bar" }, + { uri: uri11, title: "title", transition: TRANSITION_TYPED }, + ]); + await PlacesTestUtils.addBookmarkWithDetails({ uri: uri5, title: "title" }); + await PlacesTestUtils.addBookmarkWithDetails({ uri: uri6, title: "foo.bar" }); + await PlacesTestUtils.addBookmarkWithDetails({ uri: uri7, title: "title" }); + await PlacesTestUtils.addBookmarkWithDetails({ uri: uri8, title: "foo.bar" }); + await PlacesTestUtils.addBookmarkWithDetails({ + uri: uri9, + title: "title", + tags: ["foo.bar"], + }); + await PlacesTestUtils.addBookmarkWithDetails({ + uri: uri10, + title: "foo.bar", + tags: ["foo.bar"], + }); + await PlacesTestUtils.addBookmarkWithDetails({ + uri: uri11, + title: "title", + tags: ["foo.bar"], + }); + await PlacesTestUtils.addBookmarkWithDetails({ + uri: uri12, + title: "foo.bar", + tags: ["foo.bar"], + }); + + // Test restricting searches. + + info("History restrict"); + await check_autocomplete({ + search: UrlbarTokenizer.RESTRICT.HISTORY, + matches: [ + { uri: uri1, title: "title" }, + { uri: uri2, title: "foo.bar" }, + { uri: uri3, title: "title" }, + { uri: uri4, title: "foo.bar" }, + { uri: uri6, title: "foo.bar" }, + { uri: uri11, title: "title", tags: ["foo.bar"], style: ["tag"] }, + ], + }); + + info("Star restrict"); + await check_autocomplete({ + search: UrlbarTokenizer.RESTRICT.BOOKMARK, + matches: [ + { uri: uri5, title: "title", style: ["bookmark"] }, + { uri: uri6, title: "foo.bar", style: ["bookmark"] }, + { uri: uri7, title: "title", style: ["bookmark"] }, + { uri: uri8, title: "foo.bar", style: ["bookmark"] }, + { uri: uri9, title: "title", tags: ["foo.bar"], style: ["bookmark-tag"] }, + { + uri: uri10, + title: "foo.bar", + tags: ["foo.bar"], + style: ["bookmark-tag"], + }, + { + uri: uri11, + title: "title", + tags: ["foo.bar"], + style: ["bookmark-tag"], + }, + { + uri: uri12, + title: "foo.bar", + tags: ["foo.bar"], + style: ["bookmark-tag"], + }, + ], + }); + + info("Tag restrict"); + await check_autocomplete({ + search: UrlbarTokenizer.RESTRICT.TAG, + matches: [ + { uri: uri9, title: "title", tags: ["foo.bar"], style: ["bookmark-tag"] }, + { + uri: uri10, + title: "foo.bar", + tags: ["foo.bar"], + style: ["bookmark-tag"], + }, + { + uri: uri11, + title: "title", + tags: ["foo.bar"], + style: ["bookmark-tag"], + }, + { + uri: uri12, + title: "foo.bar", + tags: ["foo.bar"], + style: ["bookmark-tag"], + }, + ], + }); + + info("Special as first word"); + await check_autocomplete({ + search: `${UrlbarTokenizer.RESTRICT.HISTORY} foo bar`, + matches: [ + { uri: uri2, title: "foo.bar" }, + { uri: uri3, title: "title" }, + { uri: uri4, title: "foo.bar" }, + { uri: uri6, title: "foo.bar" }, + { uri: uri11, title: "title", tags: ["foo.bar"], style: ["tag"] }, + ], + }); + + info("Special as last word"); + await check_autocomplete({ + search: `foo bar ${UrlbarTokenizer.RESTRICT.HISTORY}`, + matches: [ + { uri: uri2, title: "foo.bar" }, + { uri: uri3, title: "title" }, + { uri: uri4, title: "foo.bar" }, + { uri: uri6, title: "foo.bar" }, + { uri: uri11, title: "title", tags: ["foo.bar"], style: ["tag"] }, + ], + }); + + // Test restricting and matching searches with a term. + + info(`foo ${UrlbarTokenizer.RESTRICT.HISTORY} -> history`); + await check_autocomplete({ + search: `foo ${UrlbarTokenizer.RESTRICT.HISTORY}`, + matches: [ + { uri: uri2, title: "foo.bar" }, + { uri: uri3, title: "title" }, + { uri: uri4, title: "foo.bar" }, + { uri: uri6, title: "foo.bar" }, + { uri: uri11, title: "title", tags: ["foo.bar"], style: ["tag"] }, + ], + }); + + info(`foo ${UrlbarTokenizer.RESTRICT.BOOKMARK} -> is star`); + await check_autocomplete({ + search: `foo ${UrlbarTokenizer.RESTRICT.BOOKMARK}`, + matches: [ + { uri: uri6, title: "foo.bar", style: ["bookmark"] }, + { uri: uri7, title: "title", style: ["bookmark"] }, + { uri: uri8, title: "foo.bar", style: ["bookmark"] }, + { uri: uri9, title: "title", tags: ["foo.bar"], style: ["bookmark-tag"] }, + { + uri: uri10, + title: "foo.bar", + tags: ["foo.bar"], + style: ["bookmark-tag"], + }, + { + uri: uri11, + title: "title", + tags: ["foo.bar"], + style: ["bookmark-tag"], + }, + { + uri: uri12, + title: "foo.bar", + tags: ["foo.bar"], + style: ["bookmark-tag"], + }, + ], + }); + + info(`foo ${UrlbarTokenizer.RESTRICT.TITLE} -> in title`); + await check_autocomplete({ + search: `foo ${UrlbarTokenizer.RESTRICT.TITLE}`, + matches: [ + { uri: uri2, title: "foo.bar" }, + { uri: uri4, title: "foo.bar" }, + { uri: uri6, title: "foo.bar", style: ["bookmark"] }, + { uri: uri8, title: "foo.bar", style: ["bookmark"] }, + { uri: uri9, title: "title", tags: ["foo.bar"], style: ["tag"] }, + { uri: uri10, title: "foo.bar", tags: ["foo.bar"], style: ["tag"] }, + { uri: uri11, title: "title", tags: ["foo.bar"], style: ["tag"] }, + { uri: uri12, title: "foo.bar", tags: ["foo.bar"], style: ["tag"] }, + ], + }); + + info(`foo ${UrlbarTokenizer.RESTRICT.URL} -> in url`); + await check_autocomplete({ + search: `foo ${UrlbarTokenizer.RESTRICT.URL}`, + matches: [ + { uri: uri3, title: "title" }, + { uri: uri4, title: "foo.bar" }, + { uri: uri7, title: "title", style: ["bookmark"] }, + { uri: uri8, title: "foo.bar", style: ["bookmark"] }, + { uri: uri11, title: "title", tags: ["foo.bar"], style: ["tag"] }, + { uri: uri12, title: "foo.bar", tags: ["foo.bar"], style: ["tag"] }, + ], + }); + + info(`foo ${UrlbarTokenizer.RESTRICT.TAG} -> is tag`); + await check_autocomplete({ + search: `foo ${UrlbarTokenizer.RESTRICT.TAG}`, + matches: [ + { uri: uri9, title: "title", tags: ["foo.bar"], style: ["bookmark-tag"] }, + { + uri: uri10, + title: "foo.bar", + tags: ["foo.bar"], + style: ["bookmark-tag"], + }, + { + uri: uri11, + title: "title", + tags: ["foo.bar"], + style: ["bookmark-tag"], + }, + { + uri: uri12, + title: "foo.bar", + tags: ["foo.bar"], + style: ["bookmark-tag"], + }, + ], + }); + + // Test various pairs of special searches + + info( + `foo ${UrlbarTokenizer.RESTRICT.HISTORY} ${UrlbarTokenizer.RESTRICT.TITLE} -> history, in title` + ); + await check_autocomplete({ + search: `foo ${UrlbarTokenizer.RESTRICT.HISTORY} ${UrlbarTokenizer.RESTRICT.TITLE}`, + matches: [ + { uri: uri2, title: "foo.bar" }, + { uri: uri4, title: "foo.bar" }, + { uri: uri6, title: "foo.bar" }, + { uri: uri11, title: "title", tags: ["foo.bar"], style: ["tag"] }, + ], + }); + + info( + `foo ${UrlbarTokenizer.RESTRICT.HISTORY} ${UrlbarTokenizer.RESTRICT.URL} -> history, in url` + ); + await check_autocomplete({ + search: `foo ${UrlbarTokenizer.RESTRICT.HISTORY} ${UrlbarTokenizer.RESTRICT.URL}`, + matches: [ + { uri: uri3, title: "title" }, + { uri: uri4, title: "foo.bar" }, + { uri: uri11, title: "title", tags: ["foo.bar"], style: ["tag"] }, + ], + }); + + info( + `foo ${UrlbarTokenizer.RESTRICT.BOOKMARK} ${UrlbarTokenizer.RESTRICT.TITLE} -> is star, in title` + ); + await check_autocomplete({ + search: `foo ${UrlbarTokenizer.RESTRICT.BOOKMARK} ${UrlbarTokenizer.RESTRICT.TITLE}`, + matches: [ + { uri: uri6, title: "foo.bar", style: ["bookmark"] }, + { uri: uri8, title: "foo.bar", style: ["bookmark"] }, + { uri: uri9, title: "title", tags: ["foo.bar"], style: ["bookmark-tag"] }, + { + uri: uri10, + title: "foo.bar", + tags: ["foo.bar"], + style: ["bookmark-tag"], + }, + { + uri: uri11, + title: "title", + tags: ["foo.bar"], + style: ["bookmark-tag"], + }, + { + uri: uri12, + title: "foo.bar", + tags: ["foo.bar"], + style: ["bookmark-tag"], + }, + ], + }); + + info( + `foo ${UrlbarTokenizer.RESTRICT.BOOKMARK} ${UrlbarTokenizer.RESTRICT.URL} -> is star, in url` + ); + await check_autocomplete({ + search: `foo ${UrlbarTokenizer.RESTRICT.BOOKMARK} ${UrlbarTokenizer.RESTRICT.URL}`, + matches: [ + { uri: uri7, title: "title", style: ["bookmark"] }, + { uri: uri8, title: "foo.bar", style: ["bookmark"] }, + { + uri: uri11, + title: "title", + tags: ["foo.bar"], + style: ["bookmark-tag"], + }, + { + uri: uri12, + title: "foo.bar", + tags: ["foo.bar"], + style: ["bookmark-tag"], + }, + ], + }); + + info( + `foo ${UrlbarTokenizer.RESTRICT.TITLE} ${UrlbarTokenizer.RESTRICT.TAG} -> in title, is tag` + ); + await check_autocomplete({ + search: `foo ${UrlbarTokenizer.RESTRICT.TITLE} ${UrlbarTokenizer.RESTRICT.TAG}`, + matches: [ + { uri: uri9, title: "title", tags: ["foo.bar"], style: ["bookmark-tag"] }, + { + uri: uri10, + title: "foo.bar", + tags: ["foo.bar"], + style: ["bookmark-tag"], + }, + { + uri: uri11, + title: "title", + tags: ["foo.bar"], + style: ["bookmark-tag"], + }, + { + uri: uri12, + title: "foo.bar", + tags: ["foo.bar"], + style: ["bookmark-tag"], + }, + ], + }); + + info( + `foo ${UrlbarTokenizer.RESTRICT.URL} ${UrlbarTokenizer.RESTRICT.TAG} -> in url, is tag` + ); + await check_autocomplete({ + search: `foo ${UrlbarTokenizer.RESTRICT.URL} ${UrlbarTokenizer.RESTRICT.TAG}`, + matches: [ + { + uri: uri11, + title: "title", + tags: ["foo.bar"], + style: ["bookmark-tag"], + }, + { + uri: uri12, + title: "foo.bar", + tags: ["foo.bar"], + style: ["bookmark-tag"], + }, + ], + }); + + // Test conflicting restrictions. + + info( + `conflict ${UrlbarTokenizer.RESTRICT.TITLE} ${UrlbarTokenizer.RESTRICT.URL} -> url wins` + ); + await PlacesTestUtils.addVisits([ + { + uri: `http://conflict.com/${UrlbarTokenizer.RESTRICT.TITLE}`, + title: "test", + }, + { + uri: "http://conflict.com/", + title: `test${UrlbarTokenizer.RESTRICT.TITLE}`, + }, + ]); + await check_autocomplete({ + search: `conflict ${UrlbarTokenizer.RESTRICT.TITLE} ${UrlbarTokenizer.RESTRICT.URL}`, + matches: [ + { + uri: `http://conflict.com/${UrlbarTokenizer.RESTRICT.TITLE}`, + title: "test", + }, + ], + }); + + info( + `conflict ${UrlbarTokenizer.RESTRICT.HISTORY} ${UrlbarTokenizer.RESTRICT.BOOKMARK} -> bookmark wins` + ); + await PlacesTestUtils.addBookmarkWithDetails({ + uri: "http://bookmark.conflict.com/", + title: `conflict ${UrlbarTokenizer.RESTRICT.HISTORY}`, + }); + await check_autocomplete({ + search: `conflict ${UrlbarTokenizer.RESTRICT.HISTORY} ${UrlbarTokenizer.RESTRICT.BOOKMARK}`, + matches: [ + { + uri: "http://bookmark.conflict.com/", + title: `conflict ${UrlbarTokenizer.RESTRICT.HISTORY}`, + style: ["bookmark"], + }, + ], + }); + + info( + `conflict ${UrlbarTokenizer.RESTRICT.BOOKMARK} ${UrlbarTokenizer.RESTRICT.TAG} -> tag wins` + ); + await PlacesTestUtils.addBookmarkWithDetails({ + uri: "http://tag.conflict.com/", + title: `conflict ${UrlbarTokenizer.RESTRICT.BOOKMARK}`, + tags: ["one"], + }); + await PlacesTestUtils.addBookmarkWithDetails({ + uri: "http://nontag.conflict.com/", + title: `conflict ${UrlbarTokenizer.RESTRICT.BOOKMARK}`, + }); + await check_autocomplete({ + search: `conflict ${UrlbarTokenizer.RESTRICT.BOOKMARK} ${UrlbarTokenizer.RESTRICT.TAG}`, + matches: [ + { + uri: "http://tag.conflict.com/", + title: `conflict ${UrlbarTokenizer.RESTRICT.BOOKMARK}`, + tags: ["one"], + style: ["bookmark-tag"], + }, + ], + }); + + // Disable autoFill for the next tests, see test_autoFill_default_behavior.js + // for specific tests. + Services.prefs.setBoolPref("browser.urlbar.autoFill", false); + + // Test default usage by setting certain browser.urlbar.suggest.* prefs + info("foo -> default history"); + setSuggestPrefsToFalse(); + Services.prefs.setBoolPref("browser.urlbar.suggest.history", true); + await check_autocomplete({ + search: "foo", + matches: [ + { uri: uri2, title: "foo.bar" }, + { uri: uri3, title: "title" }, + { uri: uri4, title: "foo.bar" }, + { uri: uri6, title: "foo.bar" }, + { uri: uri11, title: "title", tags: ["foo.bar"], style: ["tag"] }, + ], + }); + + info("foo -> default history, is star"); + setSuggestPrefsToFalse(); + Services.prefs.setBoolPref("browser.urlbar.suggest.history", true); + Services.prefs.setBoolPref("browser.urlbar.suggest.bookmark", true); + await check_autocomplete({ + search: "foo", + matches: [ + { uri: uri2, title: "foo.bar" }, + { uri: uri3, title: "title" }, + { uri: uri4, title: "foo.bar" }, + { uri: uri6, title: "foo.bar", style: ["bookmark"] }, + { uri: uri7, title: "title", style: ["bookmark"] }, + { uri: uri8, title: "foo.bar", style: ["bookmark"] }, + { uri: uri9, title: "title", tags: ["foo.bar"], style: ["bookmark-tag"] }, + { + uri: uri10, + title: "foo.bar", + tags: ["foo.bar"], + style: ["bookmark-tag"], + }, + { + uri: uri11, + title: "title", + tags: ["foo.bar"], + style: ["bookmark-tag"], + }, + { + uri: uri12, + title: "foo.bar", + tags: ["foo.bar"], + style: ["bookmark-tag"], + }, + ], + }); + + info("foo -> is star"); + setSuggestPrefsToFalse(); + Services.prefs.setBoolPref("browser.urlbar.suggest.history", false); + Services.prefs.setBoolPref("browser.urlbar.suggest.bookmark", true); + await check_autocomplete({ + search: "foo", + matches: [ + { uri: uri6, title: "foo.bar", style: ["bookmark"] }, + { uri: uri7, title: "title", style: ["bookmark"] }, + { uri: uri8, title: "foo.bar", style: ["bookmark"] }, + { uri: uri9, title: "title", tags: ["foo.bar"], style: ["bookmark-tag"] }, + { + uri: uri10, + title: "foo.bar", + tags: ["foo.bar"], + style: ["bookmark-tag"], + }, + { + uri: uri11, + title: "title", + tags: ["foo.bar"], + style: ["bookmark-tag"], + }, + { + uri: uri12, + title: "foo.bar", + tags: ["foo.bar"], + style: ["bookmark-tag"], + }, + ], + }); + + await cleanup(); +}); diff --git a/toolkit/components/places/tests/unifiedcomplete/test_swap_protocol.js b/toolkit/components/places/tests/unifiedcomplete/test_swap_protocol.js new file mode 100644 index 0000000000..92927d6f2b --- /dev/null +++ b/toolkit/components/places/tests/unifiedcomplete/test_swap_protocol.js @@ -0,0 +1,163 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +/** + * Test bug 424717 to make sure searching with an existing location like + * http://site/ also matches https://site/ or ftp://site/. Same thing for + * ftp://site/ and https://site/. + * + * Test bug 461483 to make sure a search for "w" doesn't match the "www." from + * site subdomains. + */ + +add_task(async function test_swap_protocol() { + let uri1 = NetUtil.newURI("http://www.site/"); + let uri2 = NetUtil.newURI("http://site/"); + let uri3 = NetUtil.newURI("ftp://ftp.site/"); + let uri4 = NetUtil.newURI("ftp://site/"); + let uri5 = NetUtil.newURI("https://www.site/"); + let uri6 = NetUtil.newURI("https://site/"); + let uri7 = NetUtil.newURI("http://woohoo/"); + let uri8 = NetUtil.newURI("http://wwwwwwacko/"); + await PlacesTestUtils.addVisits([ + { uri: uri1, title: "title" }, + { uri: uri2, title: "title" }, + { uri: uri3, title: "title" }, + { uri: uri4, title: "title" }, + { uri: uri5, title: "title" }, + { uri: uri6, title: "title" }, + { uri: uri7, title: "title" }, + { uri: uri8, title: "title" }, + ]); + + // uri1 and uri2 won't appear since they are lower-ranked duplicates of uri6. + let allMatches = [ + { uri: uri3, title: "title" }, + { uri: uri4, title: "title" }, + { uri: uri5, title: "title" }, + { uri: uri6, title: "title" }, + ]; + + // Disable autoFill to avoid handling the first result. + Services.prefs.setBoolPref("browser.urlbar.autoFill", false); + Services.prefs.setBoolPref("browser.urlbar.autoFill.searchEngines", false); + + info("http://www.site matches 'www.site' pages"); + await check_autocomplete({ + search: "http://www.site", + matches: [{ uri: uri5, title: "title" }], + }); + + info("http://site matches all site"); + await check_autocomplete({ + search: "http://site", + matches: allMatches, + }); + + info("ftp://ftp.site matches itself"); + await check_autocomplete({ + search: "ftp://ftp.site", + matches: [{ uri: uri3, title: "title" }], + }); + + info("ftp://site matches all site"); + await check_autocomplete({ + search: "ftp://site", + matches: allMatches, + }); + + info("https://www.site matches all site"); + await check_autocomplete({ + search: "https://www.site", + matches: [{ uri: uri5, title: "title" }], + }); + + info("https://site matches all site"); + await check_autocomplete({ + search: "https://site", + matches: allMatches, + }); + + info("www.site matches 'www.site' pages"); + await check_autocomplete({ + search: "www.site", + matches: [{ uri: uri5, title: "title" }], + }); + + info("w matches 'w' pages, including 'www'"); + await check_autocomplete({ + search: "w", + matches: [ + { uri: uri5, title: "title" }, + { uri: uri7, title: "title" }, + { uri: uri8, title: "title" }, + ], + }); + + info("http://w matches 'w' pages, including 'www'"); + await check_autocomplete({ + search: "http://w", + matches: [ + { uri: uri5, title: "title" }, + { uri: uri7, title: "title" }, + { uri: uri8, title: "title" }, + ], + }); + + info("http://www.w matches nothing"); + await check_autocomplete({ + search: "http://www.w", + matches: [], + }); + + info("ww matches no 'ww' pages, including 'www'"); + await check_autocomplete({ + search: "ww", + matches: [ + { uri: uri5, title: "title" }, + { uri: uri8, title: "title" }, + ], + }); + + info("http://ww matches no 'ww' pages, including 'www'"); + await check_autocomplete({ + search: "http://ww", + matches: [ + { uri: uri5, title: "title" }, + { uri: uri8, title: "title" }, + ], + }); + + info("http://www.ww matches nothing"); + await check_autocomplete({ + search: "http://www.ww", + matches: [], + }); + + info("www matches 'www' pages"); + await check_autocomplete({ + search: "www", + matches: [ + { uri: uri5, title: "title" }, + { uri: uri8, title: "title" }, + ], + }); + + info("http://www matches 'www' pages"); + await check_autocomplete({ + search: "http://www", + matches: [ + { uri: uri5, title: "title" }, + { uri: uri8, title: "title" }, + ], + }); + + info("http://www.www matches nothing"); + await check_autocomplete({ + search: "http://www.www", + matches: [], + }); + + await cleanup(); +}); diff --git a/toolkit/components/places/tests/unifiedcomplete/test_tab_matches.js b/toolkit/components/places/tests/unifiedcomplete/test_tab_matches.js new file mode 100644 index 0000000000..5cb4d790b1 --- /dev/null +++ b/toolkit/components/places/tests/unifiedcomplete/test_tab_matches.js @@ -0,0 +1,263 @@ +/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- + * vim:set ts=2 sw=2 sts=2 et: + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +add_task(async function test_tab_matches() { + Services.prefs.setBoolPref("browser.urlbar.autoFill", false); + + let uri1 = NetUtil.newURI("http://abc.com/"); + let uri2 = NetUtil.newURI("http://xyz.net/"); + let uri3 = NetUtil.newURI("about:mozilla"); + let uri4 = NetUtil.newURI("data:text/html,test"); + let uri5 = NetUtil.newURI("http://foobar.org"); + await PlacesTestUtils.addVisits([ + { uri: uri1, title: "ABC rocks" }, + { uri: uri2, title: "xyz.net - we're better than ABC" }, + { + uri: uri5, + title: "foobar.org - much better than ABC, definitely better than XYZ", + }, + ]); + await addOpenPages(uri1, 1); + // Pages that cannot be registered in history. + await addOpenPages(uri3, 1); + await addOpenPages(uri4, 1); + + info("basic tab match"); + await check_autocomplete({ + search: "abc.com", + searchParam: "enable-actions", + matches: [makeSwitchToTabMatch("http://abc.com/", { title: "ABC rocks" })], + }); + + info("three results, one tab match"); + await check_autocomplete({ + search: "abc", + searchParam: "enable-actions", + matches: [ + makeSwitchToTabMatch("http://abc.com/", { title: "ABC rocks" }), + { + uri: uri2, + title: "xyz.net - we're better than ABC", + style: ["favicon"], + }, + { + uri: uri5, + title: "foobar.org - much better than ABC, definitely better than XYZ", + style: ["favicon"], + }, + ], + }); + + info("three results, both normal results are tab matches"); + await addOpenPages(uri2, 1); + await check_autocomplete({ + search: "abc", + searchParam: "enable-actions", + matches: [ + makeSwitchToTabMatch("http://abc.com/", { title: "ABC rocks" }), + makeSwitchToTabMatch("http://xyz.net/", { + title: "xyz.net - we're better than ABC", + }), + { + uri: uri5, + title: "foobar.org - much better than ABC, definitely better than XYZ", + style: ["favicon"], + }, + ], + }); + + info("a container tab is not visible in 'switch to tab'"); + await addOpenPages(uri5, 1, /* userContextId: */ 3); + await check_autocomplete({ + search: "abc", + searchParam: "enable-actions", + matches: [ + makeSwitchToTabMatch("http://abc.com/", { title: "ABC rocks" }), + makeSwitchToTabMatch("http://xyz.net/", { + title: "xyz.net - we're better than ABC", + }), + { + uri: uri5, + title: "foobar.org - much better than ABC, definitely better than XYZ", + style: ["favicon"], + }, + ], + }); + + info( + "a container tab should not see 'switch to tab' for other container tabs" + ); + await check_autocomplete({ + search: "abc", + searchParam: "enable-actions user-context-id:3", + matches: [ + makeSwitchToTabMatch("http://foobar.org/", { + title: "foobar.org - much better than ABC, definitely better than XYZ", + }), + { uri: uri1, title: "ABC rocks", style: ["favicon"] }, + { + uri: uri2, + title: "xyz.net - we're better than ABC", + style: ["favicon"], + }, + ], + }); + + info("a different container tab should not see any 'switch to tab'"); + await check_autocomplete({ + search: "abc", + searchParam: "enable-actions user-context-id:2", + matches: [ + { uri: uri1, title: "ABC rocks", style: ["favicon"] }, + { + uri: uri2, + title: "xyz.net - we're better than ABC", + style: ["favicon"], + }, + { + uri: uri5, + title: "foobar.org - much better than ABC, definitely better than XYZ", + style: ["favicon"], + }, + ], + }); + + info( + "three results, both normal results are tab matches, one has multiple tabs" + ); + await addOpenPages(uri2, 5); + await check_autocomplete({ + search: "abc", + searchParam: "enable-actions", + matches: [ + makeSwitchToTabMatch("http://abc.com/", { title: "ABC rocks" }), + makeSwitchToTabMatch("http://xyz.net/", { + title: "xyz.net - we're better than ABC", + }), + { + uri: uri5, + title: "foobar.org - much better than ABC, definitely better than XYZ", + style: ["favicon"], + }, + ], + }); + + info("three results, no tab matches (disable-private-actions)"); + await check_autocomplete({ + search: "abc", + searchParam: "enable-actions disable-private-actions", + matches: [ + { uri: uri1, title: "ABC rocks", style: ["favicon"] }, + { + uri: uri2, + title: "xyz.net - we're better than ABC", + style: ["favicon"], + }, + { + uri: uri5, + title: "foobar.org - much better than ABC, definitely better than XYZ", + style: ["favicon"], + }, + ], + }); + + info("two results (actions disabled)"); + await check_autocomplete({ + search: "abc", + searchParam: "", + matches: [ + { uri: uri1, title: "ABC rocks", style: ["favicon"] }, + { + uri: uri2, + title: "xyz.net - we're better than ABC", + style: ["favicon"], + }, + { + uri: uri5, + title: "foobar.org - much better than ABC, definitely better than XYZ", + style: ["favicon"], + }, + ], + }); + + info("three results, no tab matches"); + await removeOpenPages(uri1, 1); + await removeOpenPages(uri2, 6); + await check_autocomplete({ + search: "abc", + searchParam: "enable-actions", + matches: [ + { uri: uri1, title: "ABC rocks", style: ["favicon"] }, + { + uri: uri2, + title: "xyz.net - we're better than ABC", + style: ["favicon"], + }, + { + uri: uri5, + title: "foobar.org - much better than ABC, definitely better than XYZ", + style: ["favicon"], + }, + ], + }); + + info("tab match search with restriction character"); + await addOpenPages(uri1, 1); + await check_autocomplete({ + search: UrlbarTokenizer.RESTRICT.OPENPAGE + " abc", + searchParam: "enable-actions", + matches: [makeSwitchToTabMatch("http://abc.com/", { title: "ABC rocks" })], + }); + + info("tab match with not-addable pages"); + await check_autocomplete({ + search: "mozilla", + searchParam: "enable-actions", + matches: [makeSwitchToTabMatch("about:mozilla")], + }); + + info("tab match with not-addable pages, no boundary search"); + await check_autocomplete({ + search: "ut:mo", + searchParam: "enable-actions", + matches: [makeSwitchToTabMatch("about:mozilla")], + }); + + info("tab match with not-addable pages and restriction character"); + await check_autocomplete({ + search: UrlbarTokenizer.RESTRICT.OPENPAGE + " mozilla", + searchParam: "enable-actions", + matches: [makeSwitchToTabMatch("about:mozilla")], + }); + + info("tab match with not-addable pages and only restriction character"); + await check_autocomplete({ + search: UrlbarTokenizer.RESTRICT.OPENPAGE, + searchParam: "enable-actions", + matches: [ + makeSwitchToTabMatch("http://abc.com/", { title: "ABC rocks" }), + makeSwitchToTabMatch("about:mozilla"), + makeSwitchToTabMatch("data:text/html,test"), + ], + }); + + info("tab match should not return tags as part of the title"); + // Bookmark one of the pages, and add tags to it, to check they don't appear + // in the title. + let bm = await PlacesUtils.bookmarks.insert({ + url: uri1, + parentGuid: PlacesUtils.bookmarks.toolbarGuid, + }); + PlacesUtils.tagging.tagURI(uri1, ["test-tag"]); + await check_autocomplete({ + search: "abc.com", + searchParam: "enable-actions", + matches: [makeSwitchToTabMatch("http://abc.com/", { title: "ABC rocks" })], + }); + await PlacesUtils.bookmarks.remove(bm); + + await cleanup(); +}); diff --git a/toolkit/components/places/tests/unifiedcomplete/test_word_boundary_search.js b/toolkit/components/places/tests/unifiedcomplete/test_word_boundary_search.js new file mode 100644 index 0000000000..6d417e758d --- /dev/null +++ b/toolkit/components/places/tests/unifiedcomplete/test_word_boundary_search.js @@ -0,0 +1,266 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +/** + * Test to make sure matches against the url, title, tags are first made on word + * boundaries, instead of in the middle of words, and later are extended to the + * whole words. For this test it is critical to check sorting of the matches. + * + * Make sure we don't try matching one after a CamelCase because the upper-case + * isn't really a word boundary. (bug 429498) + */ + +var katakana = ["\u30a8", "\u30c9"]; // E, Do +var ideograph = ["\u4efb", "\u5929", "\u5802"]; // Nin Ten Do + +add_task(async function test_escape() { + Services.prefs.setBoolPref("browser.urlbar.autoFill.searchEngines", false); + Services.prefs.setBoolPref("browser.urlbar.autoFill", false); + + await PlacesTestUtils.addVisits([ + { uri: "http://matchme/", title: "title1" }, + { uri: "http://dontmatchme/", title: "title1" }, + { uri: "http://title/1", title: "matchme2" }, + { uri: "http://title/2", title: "dontmatchme3" }, + { uri: "http://tag/1", title: "title1" }, + { uri: "http://tag/2", title: "title1" }, + { uri: "http://crazytitle/", title: "!@#$%^&*()_+{}|:<>?word" }, + { uri: "http://katakana/", title: katakana.join("") }, + { uri: "http://ideograph/", title: ideograph.join("") }, + { uri: "http://camel/pleaseMatchMe/", title: "title1" }, + ]); + await PlacesTestUtils.addBookmarkWithDetails({ + uri: "http://tag/1", + title: "title1", + tags: ["matchme2"], + }); + await PlacesTestUtils.addBookmarkWithDetails({ + uri: "http://tag/2", + title: "title1", + tags: ["dontmatchme3"], + }); + + info("Match 'match' at the beginning or after / or on a CamelCase"); + await check_autocomplete({ + search: "match", + checkSorting: true, + matches: [ + { + uri: "http://tag/1", + title: "title1", + tags: ["matchme2"], + style: ["bookmark-tag"], + }, + { uri: "http://camel/pleaseMatchMe/", title: "title1" }, + { uri: "http://title/1", title: "matchme2" }, + { uri: "http://matchme/", title: "title1" }, + { + uri: "http://tag/2", + title: "title1", + tags: ["dontmatchme3"], + style: ["bookmark-tag"], + }, + { uri: "http://title/2", title: "dontmatchme3" }, + { uri: "http://dontmatchme/", title: "title1" }, + ], + }); + + info("Match 'dont' at the beginning or after /"); + await check_autocomplete({ + search: "dont", + checkSorting: true, + matches: [ + { + uri: "http://tag/2", + title: "title1", + tags: ["dontmatchme3"], + style: ["bookmark-tag"], + }, + { uri: "http://title/2", title: "dontmatchme3" }, + { uri: "http://dontmatchme/", title: "title1" }, + ], + }); + + info("Match 'match' at the beginning or after / or on a CamelCase"); + await check_autocomplete({ + search: "2", + checkSorting: true, + matches: [ + { + uri: "http://tag/2", + title: "title1", + tags: ["dontmatchme3"], + style: ["bookmark-tag"], + }, + { + uri: "http://tag/1", + title: "title1", + tags: ["matchme2"], + style: ["bookmark-tag"], + }, + { uri: "http://title/2", title: "dontmatchme3" }, + { uri: "http://title/1", title: "matchme2" }, + ], + }); + + info("Match 't' at the beginning or after /"); + await check_autocomplete({ + search: "t", + checkSorting: true, + matches: [ + { + uri: "http://tag/2", + title: "title1", + tags: ["dontmatchme3"], + style: ["bookmark-tag"], + }, + { + uri: "http://tag/1", + title: "title1", + tags: ["matchme2"], + style: ["bookmark-tag"], + }, + { uri: "http://camel/pleaseMatchMe/", title: "title1" }, + { uri: "http://title/2", title: "dontmatchme3" }, + { uri: "http://title/1", title: "matchme2" }, + { uri: "http://dontmatchme/", title: "title1" }, + { uri: "http://matchme/", title: "title1" }, + { uri: "http://katakana/", title: katakana.join("") }, + { uri: "http://crazytitle/", title: "!@#$%^&*()_+{}|:<>?word" }, + ], + }); + + info("Match 'word' after many consecutive word boundaries"); + await check_autocomplete({ + search: "word", + checkSorting: true, + matches: [{ uri: "http://crazytitle/", title: "!@#$%^&*()_+{}|:<>?word" }], + }); + + info("Match a word boundary '/' for everything"); + await check_autocomplete({ + search: "/", + checkSorting: true, + matches: [ + { + uri: "http://tag/2", + title: "title1", + tags: ["dontmatchme3"], + style: ["bookmark-tag"], + }, + { + uri: "http://tag/1", + title: "title1", + tags: ["matchme2"], + style: ["bookmark-tag"], + }, + { uri: "http://camel/pleaseMatchMe/", title: "title1" }, + { uri: "http://ideograph/", title: ideograph.join("") }, + { uri: "http://katakana/", title: katakana.join("") }, + { uri: "http://crazytitle/", title: "!@#$%^&*()_+{}|:<>?word" }, + { uri: "http://title/2", title: "dontmatchme3" }, + { uri: "http://title/1", title: "matchme2" }, + { uri: "http://dontmatchme/", title: "title1" }, + { uri: "http://matchme/", title: "title1" }, + ], + }); + + info("Match word boundaries '()_' that are among word boundaries"); + await check_autocomplete({ + search: "()_", + checkSorting: true, + matches: [{ uri: "http://crazytitle/", title: "!@#$%^&*()_+{}|:<>?word" }], + }); + + info("Katakana characters form a string, so match the beginning"); + await check_autocomplete({ + search: katakana[0], + checkSorting: true, + matches: [{ uri: "http://katakana/", title: katakana.join("") }], + }); + + /* + info("Middle of a katakana word shouldn't be matched"); + await check_autocomplete({ + search: katakana[1], + matches: [ ], + }); +*/ + + info("Ideographs are treated as words so 'nin' is one word"); + await check_autocomplete({ + search: ideograph[0], + checkSorting: true, + matches: [{ uri: "http://ideograph/", title: ideograph.join("") }], + }); + + info("Ideographs are treated as words so 'ten' is another word"); + await check_autocomplete({ + search: ideograph[1], + checkSorting: true, + matches: [{ uri: "http://ideograph/", title: ideograph.join("") }], + }); + + info("Ideographs are treated as words so 'do' is yet another word"); + await check_autocomplete({ + search: ideograph[2], + checkSorting: true, + matches: [{ uri: "http://ideograph/", title: ideograph.join("") }], + }); + + info("Match in the middle. Should just be sorted by frecency."); + await check_autocomplete({ + search: "ch", + checkSorting: true, + matches: [ + { + uri: "http://tag/2", + title: "title1", + tags: ["dontmatchme3"], + style: ["bookmark-tag"], + }, + { + uri: "http://tag/1", + title: "title1", + tags: ["matchme2"], + style: ["bookmark-tag"], + }, + { uri: "http://camel/pleaseMatchMe/", title: "title1" }, + { uri: "http://title/2", title: "dontmatchme3" }, + { uri: "http://title/1", title: "matchme2" }, + { uri: "http://dontmatchme/", title: "title1" }, + { uri: "http://matchme/", title: "title1" }, + ], + }); + + // Also this test should just be sorted by frecency. + info( + "Don't match one character after a camel-case word boundary (bug 429498). Should just be sorted by frecency." + ); + await check_autocomplete({ + search: "atch", + checkSorting: true, + matches: [ + { + uri: "http://tag/2", + title: "title1", + tags: ["dontmatchme3"], + style: ["bookmark-tag"], + }, + { + uri: "http://tag/1", + title: "title1", + tags: ["matchme2"], + style: ["bookmark-tag"], + }, + { uri: "http://camel/pleaseMatchMe/", title: "title1" }, + { uri: "http://title/2", title: "dontmatchme3" }, + { uri: "http://title/1", title: "matchme2" }, + { uri: "http://dontmatchme/", title: "title1" }, + { uri: "http://matchme/", title: "title1" }, + ], + }); + + await cleanup(); +}); diff --git a/toolkit/components/places/tests/unifiedcomplete/xpcshell.ini b/toolkit/components/places/tests/unifiedcomplete/xpcshell.ini new file mode 100644 index 0000000000..cf80015682 --- /dev/null +++ b/toolkit/components/places/tests/unifiedcomplete/xpcshell.ini @@ -0,0 +1,33 @@ +[DEFAULT] +head = head_autocomplete.js +skip-if = toolkit == "android" || appname == "thunderbird" +firefox-appdir = browser +support-files = + data/engine-rel-searchform.xml + data/engine-suggestions.xml + !/toolkit/components/places/tests/favicons/favicon-normal16.png + +[test_416211.js] +[test_416214.js] +[test_417798.js] +[test_418257.js] +[test_422277.js] +[test_autocomplete_stopSearch_no_throw.js] +[test_do_not_trim.js] +[test_download_embed_bookmarks.js] +[test_empty_search.js] +[test_escape_self.js] +[test_history_autocomplete_tags.js] +[test_ignore_protocol.js] +[test_keyword_search.js] +[test_keyword_search_actions.js] +[test_multi_word_search.js] +[test_preloaded_sites.js] +[test_remote_tab_matches.js] +skip-if = !sync +[test_search_engine_alias.js] +[test_search_engine_restyle.js] +[test_special_search.js] +[test_swap_protocol.js] +[test_tab_matches.js] +[test_word_boundary_search.js] |