зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1545916 - Make quantumbar match highlighting case insensitive. r=dao
Differential Revision: https://phabricator.services.mozilla.com/D28751 --HG-- extra : moz-landing-system : lando
This commit is contained in:
Родитель
3c8ae0e869
Коммит
441baf366e
|
@ -272,6 +272,7 @@ function filterTokens(tokens) {
|
|||
let token = tokens[i];
|
||||
let tokenObj = {
|
||||
value: token,
|
||||
lowerCaseValue: token.toLocaleLowerCase(),
|
||||
type: UrlbarTokenizer.TYPE.TEXT,
|
||||
};
|
||||
let restrictionType = CHAR_TO_TYPE_MAP.get(token);
|
||||
|
|
|
@ -228,9 +228,10 @@ var UrlbarUtils = {
|
|||
},
|
||||
|
||||
/**
|
||||
* Returns a list of all the token substring matches in a string. Each match
|
||||
* in the list is a tuple: [matchIndex, matchLength]. matchIndex is the index
|
||||
* in the string of the match, and matchLength is the length of the match.
|
||||
* Returns a list of all the token substring matches in a string. Matching is
|
||||
* case insensitive. Each match in the returned list is a tuple: [matchIndex,
|
||||
* matchLength]. matchIndex is the index in the string of the match, and
|
||||
* matchLength is the length of the match.
|
||||
*
|
||||
* @param {array} tokens The tokens to search for.
|
||||
* @param {string} str The string to match against.
|
||||
|
@ -243,14 +244,15 @@ var UrlbarUtils = {
|
|||
* The array is sorted by match indexes ascending.
|
||||
*/
|
||||
getTokenMatches(tokens, str) {
|
||||
str = str.toLocaleLowerCase();
|
||||
// To generate non-overlapping ranges, we start from a 0-filled array with
|
||||
// the same length of the string, and use it as a collision marker, setting
|
||||
// 1 where a token matches.
|
||||
let hits = new Array(str.length).fill(0);
|
||||
for (let token of tokens) {
|
||||
for (let { lowerCaseValue } of tokens) {
|
||||
// Ideally we should never hit the empty token case, but just in case
|
||||
// the value check protects us from an infinite loop.
|
||||
for (let index = 0, needle = token.value; index >= 0 && needle;) {
|
||||
// the `needle` check protects us from an infinite loop.
|
||||
for (let index = 0, needle = lowerCaseValue; index >= 0 && needle;) {
|
||||
index = str.indexOf(needle, index);
|
||||
if (index >= 0) {
|
||||
hits.fill(1, index, index + needle.length);
|
||||
|
|
|
@ -20,12 +20,13 @@ add_task(async function setup() {
|
|||
async function testResult(input, expected) {
|
||||
const ESCAPED_URL = encodeURI(input.url);
|
||||
|
||||
await PlacesUtils.history.clear();
|
||||
await PlacesTestUtils.addVisits({
|
||||
uri: input.url,
|
||||
title: input.title,
|
||||
});
|
||||
|
||||
await promiseAutocompleteResultPopup("\u6e2C\u8a66");
|
||||
await promiseAutocompleteResultPopup(input.query);
|
||||
|
||||
let result = await UrlbarTestUtils.getDetailsOfResultAt(window, 1);
|
||||
Assert.equal(result.url, ESCAPED_URL,
|
||||
|
@ -100,3 +101,145 @@ add_task(async function test_url_result_no_trimming() {
|
|||
|
||||
Services.prefs.clearUserPref("browser.urlbar.trimURLs");
|
||||
});
|
||||
|
||||
add_task(async function test_case_insensitive_highlights_1() {
|
||||
await testResult({
|
||||
query: "exam",
|
||||
title: "The examPLE URL EXAMple",
|
||||
url: "http://example.com/ExAm",
|
||||
}, {
|
||||
displayedUrl: "example.com/ExAm",
|
||||
highlightedTitle: [
|
||||
["The ", false],
|
||||
["exam", true],
|
||||
["PLE URL ", false],
|
||||
["EXAM", true],
|
||||
["ple", false],
|
||||
],
|
||||
highlightedUrl: [
|
||||
["exam", true],
|
||||
["ple.com/", false],
|
||||
["ExAm", true],
|
||||
],
|
||||
});
|
||||
});
|
||||
|
||||
add_task(async function test_case_insensitive_highlights_2() {
|
||||
await testResult({
|
||||
query: "EXAM",
|
||||
title: "The examPLE URL EXAMple",
|
||||
url: "http://example.com/ExAm",
|
||||
}, {
|
||||
displayedUrl: "example.com/ExAm",
|
||||
highlightedTitle: [
|
||||
["The ", false],
|
||||
["exam", true],
|
||||
["PLE URL ", false],
|
||||
["EXAM", true],
|
||||
["ple", false],
|
||||
],
|
||||
highlightedUrl: [
|
||||
["exam", true],
|
||||
["ple.com/", false],
|
||||
["ExAm", true],
|
||||
],
|
||||
});
|
||||
});
|
||||
|
||||
add_task(async function test_case_insensitive_highlights_3() {
|
||||
await testResult({
|
||||
query: "eXaM",
|
||||
title: "The examPLE URL EXAMple",
|
||||
url: "http://example.com/ExAm",
|
||||
}, {
|
||||
displayedUrl: "example.com/ExAm",
|
||||
highlightedTitle: [
|
||||
["The ", false],
|
||||
["exam", true],
|
||||
["PLE URL ", false],
|
||||
["EXAM", true],
|
||||
["ple", false],
|
||||
],
|
||||
highlightedUrl: [
|
||||
["exam", true],
|
||||
["ple.com/", false],
|
||||
["ExAm", true],
|
||||
],
|
||||
});
|
||||
});
|
||||
|
||||
add_task(async function test_case_insensitive_highlights_4() {
|
||||
await testResult({
|
||||
query: "ExAm",
|
||||
title: "The examPLE URL EXAMple",
|
||||
url: "http://example.com/ExAm",
|
||||
}, {
|
||||
displayedUrl: "example.com/ExAm",
|
||||
highlightedTitle: [
|
||||
["The ", false],
|
||||
["exam", true],
|
||||
["PLE URL ", false],
|
||||
["EXAM", true],
|
||||
["ple", false],
|
||||
],
|
||||
highlightedUrl: [
|
||||
["exam", true],
|
||||
["ple.com/", false],
|
||||
["ExAm", true],
|
||||
],
|
||||
});
|
||||
});
|
||||
|
||||
add_task(async function test_case_insensitive_highlights_5() {
|
||||
await testResult({
|
||||
query: "exam foo",
|
||||
title: "The examPLE URL foo EXAMple FOO",
|
||||
url: "http://example.com/ExAm/fOo",
|
||||
}, {
|
||||
displayedUrl: "example.com/ExAm/fOo",
|
||||
highlightedTitle: [
|
||||
["The ", false],
|
||||
["exam", true],
|
||||
["PLE URL ", false],
|
||||
["foo", true],
|
||||
[" ", false],
|
||||
["EXAM", true],
|
||||
["ple ", false],
|
||||
["FOO", true],
|
||||
],
|
||||
highlightedUrl: [
|
||||
["exam", true],
|
||||
["ple.com/", false],
|
||||
["ExAm", true],
|
||||
["/", false],
|
||||
["fOo", true],
|
||||
],
|
||||
});
|
||||
});
|
||||
|
||||
add_task(async function test_case_insensitive_highlights_6() {
|
||||
await testResult({
|
||||
query: "EXAM FOO",
|
||||
title: "The examPLE URL foo EXAMple FOO",
|
||||
url: "http://example.com/ExAm/fOo",
|
||||
}, {
|
||||
displayedUrl: "example.com/ExAm/fOo",
|
||||
highlightedTitle: [
|
||||
["The ", false],
|
||||
["exam", true],
|
||||
["PLE URL ", false],
|
||||
["foo", true],
|
||||
[" ", false],
|
||||
["EXAM", true],
|
||||
["ple ", false],
|
||||
["FOO", true],
|
||||
],
|
||||
highlightedUrl: [
|
||||
["exam", true],
|
||||
["ple.com/", false],
|
||||
["ExAm", true],
|
||||
["/", false],
|
||||
["fOo", true],
|
||||
],
|
||||
});
|
||||
});
|
||||
|
|
|
@ -14,11 +14,41 @@ add_task(function test() {
|
|||
phrase: "mozilla is for the Open Web",
|
||||
expected: [[0, 7], [8, 2]],
|
||||
},
|
||||
{
|
||||
tokens: ["mozilla", "is", "i"],
|
||||
phrase: "MOZILLA IS for the Open Web",
|
||||
expected: [[0, 7], [8, 2]],
|
||||
},
|
||||
{
|
||||
tokens: ["mozilla", "is", "i"],
|
||||
phrase: "MoZiLlA Is for the Open Web",
|
||||
expected: [[0, 7], [8, 2]],
|
||||
},
|
||||
{
|
||||
tokens: ["MOZILLA", "IS", "I"],
|
||||
phrase: "mozilla is for the Open Web",
|
||||
expected: [[0, 7], [8, 2]],
|
||||
},
|
||||
{
|
||||
tokens: ["MoZiLlA", "Is", "I"],
|
||||
phrase: "mozilla is for the Open Web",
|
||||
expected: [[0, 7], [8, 2]],
|
||||
},
|
||||
{
|
||||
tokens: ["mo", "b"],
|
||||
phrase: "mozilla is for the Open Web",
|
||||
expected: [[0, 2], [26, 1]],
|
||||
},
|
||||
{
|
||||
tokens: ["mo", "b"],
|
||||
phrase: "MOZILLA is for the OPEN WEB",
|
||||
expected: [[0, 2], [26, 1]],
|
||||
},
|
||||
{
|
||||
tokens: ["MO", "B"],
|
||||
phrase: "mozilla is for the Open Web",
|
||||
expected: [[0, 2], [26, 1]],
|
||||
},
|
||||
{
|
||||
tokens: ["mo", ""],
|
||||
phrase: "mozilla is for the Open Web",
|
||||
|
@ -29,6 +59,36 @@ add_task(function test() {
|
|||
phrase: "mozilla",
|
||||
expected: [[0, 7]],
|
||||
},
|
||||
{
|
||||
tokens: ["mozilla"],
|
||||
phrase: "MOZILLA",
|
||||
expected: [[0, 7]],
|
||||
},
|
||||
{
|
||||
tokens: ["mozilla"],
|
||||
phrase: "MoZiLlA",
|
||||
expected: [[0, 7]],
|
||||
},
|
||||
{
|
||||
tokens: ["mozilla"],
|
||||
phrase: "mOzIlLa",
|
||||
expected: [[0, 7]],
|
||||
},
|
||||
{
|
||||
tokens: ["MOZILLA"],
|
||||
phrase: "mozilla",
|
||||
expected: [[0, 7]],
|
||||
},
|
||||
{
|
||||
tokens: ["MoZiLlA"],
|
||||
phrase: "mozilla",
|
||||
expected: [[0, 7]],
|
||||
},
|
||||
{
|
||||
tokens: ["mOzIlLa"],
|
||||
phrase: "mozilla",
|
||||
expected: [[0, 7]],
|
||||
},
|
||||
{
|
||||
tokens: ["\u9996"],
|
||||
phrase: "Test \u9996\u9875 Test",
|
||||
|
@ -39,6 +99,31 @@ add_task(function test() {
|
|||
phrase: "mozilla",
|
||||
expected: [[0, 7]],
|
||||
},
|
||||
{
|
||||
tokens: ["mo", "zilla"],
|
||||
phrase: "MOZILLA",
|
||||
expected: [[0, 7]],
|
||||
},
|
||||
{
|
||||
tokens: ["mo", "zilla"],
|
||||
phrase: "MoZiLlA",
|
||||
expected: [[0, 7]],
|
||||
},
|
||||
{
|
||||
tokens: ["mo", "zilla"],
|
||||
phrase: "mOzIlLa",
|
||||
expected: [[0, 7]],
|
||||
},
|
||||
{
|
||||
tokens: ["MO", "ZILLA"],
|
||||
phrase: "mozilla",
|
||||
expected: [[0, 7]],
|
||||
},
|
||||
{
|
||||
tokens: ["Mo", "Zilla"],
|
||||
phrase: "mozilla",
|
||||
expected: [[0, 7]],
|
||||
},
|
||||
{
|
||||
tokens: ["moz", "zilla"],
|
||||
phrase: "mozilla",
|
||||
|
@ -54,9 +139,23 @@ add_task(function test() {
|
|||
phrase: "mozilla mozzarella momo",
|
||||
expected: [[0, 2], [8, 2], [19, 4]],
|
||||
},
|
||||
{
|
||||
tokens: ["mo", "om"],
|
||||
phrase: "MOZILLA MOZZARELLA MOMO",
|
||||
expected: [[0, 2], [8, 2], [19, 4]],
|
||||
},
|
||||
{
|
||||
tokens: ["MO", "OM"],
|
||||
phrase: "mozilla mozzarella momo",
|
||||
expected: [[0, 2], [8, 2], [19, 4]],
|
||||
},
|
||||
];
|
||||
for (let {tokens, phrase, expected} of tests) {
|
||||
tokens = tokens.map(t => ({value: t}));
|
||||
let mockContext = {
|
||||
searchString: tokens.join(" "),
|
||||
};
|
||||
UrlbarTokenizer.tokenize(mockContext);
|
||||
tokens = mockContext.tokens;
|
||||
Assert.deepEqual(UrlbarUtils.getTokenMatches(tokens, phrase), expected,
|
||||
`Match "${tokens.map(t => t.value).join(", ")}" on "${phrase}"`);
|
||||
}
|
||||
|
|
|
@ -78,7 +78,7 @@ It is augmented as it progresses through the system, with various information:
|
|||
preselected; // {boolean} whether the first result should be preselected.
|
||||
results; // {array} list of UrlbarResult objects.
|
||||
tokens; // {array} tokens extracted from the searchString, each token is an
|
||||
// object in the form {type, value}.
|
||||
// object in the form {type, value, lowerCaseValue}.
|
||||
}
|
||||
|
||||
|
||||
|
|
Загрузка…
Ссылка в новой задаче