Bug 1538293 - Make autofilling the placeholder allow changes in case. r=mak

Differential Revision: https://phabricator.services.mozilla.com/D24593

--HG--
extra : moz-landing-system : lando
This commit is contained in:
Drew Willcoxon 2019-03-26 22:59:14 +00:00
parent 017147c0f4
commit c707227057
4 changed files with 207 additions and 17 deletions

View File

@ -709,26 +709,23 @@ class UrlbarInput {
!deletedAutofilledSubstring &&
this.selectionEnd == value.length;
// The autofill placeholder is a string that we autofill now, before we
// start waiting on the new search's first result, in order to prevent a
// flicker in the input caused by the previous autofilled substring
// disappearing and reappearing when the new first result arrives. Of
// course we can only autofill the placeholder if it starts with the new
// search string.
// Determine whether we can autofill the placeholder. The placeholder is a
// value that we autofill now, when the search starts and before we wait on
// its first result, in order to prevent a flicker in the input caused by
// the previous autofilled substring disappearing and reappearing when the
// first result arrives. Of course we can only autofill the placeholder if
// it starts with the new search string, and we shouldn't autofill anything
// if the caret isn't at the end of the input.
if (!allowAutofill ||
this._autofillPlaceholder.length <= value.length ||
!this._autofillPlaceholder.startsWith(value)) {
this._autofillPlaceholder = "";
}
// Don't ever autofill on input if the caret/selection isn't at the end, or
// if the placeholder doesn't start with what the user typed.
if (this._autofillPlaceholder &&
this.selectionEnd == this.value.length &&
this._autofillPlaceholder.toLocaleLowerCase()
!this._autofillPlaceholder.toLocaleLowerCase()
.startsWith(value.toLocaleLowerCase())) {
this._autofillValue(this._autofillPlaceholder, value.length,
this._autofillPlaceholder.length);
this._autofillPlaceholder = "";
} else if (this._autofillPlaceholder &&
this.selectionEnd == this.value.length) {
let autofillValue =
value + this._autofillPlaceholder.substring(value.length);
this._autofillValue(autofillValue, value.length, autofillValue.length);
}
return allowAutofill;

View File

@ -237,6 +237,14 @@ var UrlbarTestUtils = {
let urlbar = getUrlbarAbstraction(win);
return urlbar.promiseUserContextId();
},
/**
* Dispatches an input event to the input field.
* @param {object} win The browser window
*/
fireInputEvent(win) {
getUrlbarAbstraction(win).fireInputEvent();
},
};
/**

View File

@ -26,6 +26,7 @@ skip-if = os != "mac" # Mac only feature
[browser_autoFill_backspaced.js]
[browser_autoFill_canonize.js]
[browser_autoFill_caretNotAtEnd.js]
[browser_autoFill_placeholder.js]
[browser_autoFill_preserve.js]
[browser_autoFill_trimURLs.js]
[browser_autoFill_typed.js]

View File

@ -0,0 +1,184 @@
/* Any copyright is dedicated to the Public Domain.
* http://creativecommons.org/publicdomain/zero/1.0/ */
// This test makes sure that the autofill placeholder value is autofilled
// correctly. The placeholder is a string that we immediately autofill when a
// search starts and before its first result arrives in order to prevent text
// flicker in the input.
//
// Because this test specifically checks autofill *before* searches complete, we
// can't use promiseAutocompleteResultPopup() or other helpers that wait for
// searches to complete. Instead the test uses fireInputEvent() to trigger
// placeholder autofill and then immediately checks autofill status.
"use strict";
add_task(async function init() {
await cleanUp();
});
add_task(async function origin() {
await PlacesTestUtils.addVisits("http://example.com/");
// Do an initial search that triggers autofill so that the placeholder has an
// initial value.
await promiseAutocompleteResultPopup("ex", window, true);
let details = await UrlbarTestUtils.getDetailsOfResultAt(window, 0);
Assert.ok(details.autofill);
Assert.equal(gURLBar.value, "example.com/");
Assert.equal(gURLBar.selectionStart, "ex".length);
Assert.equal(gURLBar.selectionEnd, "example.com/".length);
await searchAndCheck("exa", "example.com/");
await searchAndCheck("EXAM", "EXAMple.com/");
await searchAndCheck("eXaMp", "eXaMple.com/");
await searchAndCheck("exampl", "example.com/");
await cleanUp();
});
add_task(async function tokenAlias() {
// We have built-in engine aliases that may conflict with the one we choose
// here in terms of autofill, so be careful and choose a weird alias.
await Services.search.addEngineWithDetails("Test", {
alias: "@__example",
template: "http://example.com/?search={searchTerms}",
});
registerCleanupFunction(async function() {
let engine = Services.search.getEngineByName("Test");
await Services.search.removeEngine(engine);
});
// Do an initial search that triggers autofill so that the placeholder has an
// initial value.
await promiseAutocompleteResultPopup("@__ex", window, true);
let details = await UrlbarTestUtils.getDetailsOfResultAt(window, 0);
Assert.ok(details.autofill);
Assert.equal(gURLBar.value, "@__example ");
Assert.equal(gURLBar.selectionStart, "@__ex".length);
Assert.equal(gURLBar.selectionEnd, "@__example ".length);
await searchAndCheck("@__exa", "@__example ");
await searchAndCheck("@__EXAM", "@__EXAMple ");
await searchAndCheck("@__eXaMp", "@__eXaMple ");
await searchAndCheck("@__exampl", "@__example ");
await cleanUp();
});
add_task(async function noMatch1() {
await PlacesTestUtils.addVisits("http://example.com/");
// Do an initial search that triggers autofill so that the placeholder has an
// initial value.
await promiseAutocompleteResultPopup("ex", window, true);
let details = await UrlbarTestUtils.getDetailsOfResultAt(window, 0);
Assert.ok(details.autofill);
Assert.equal(gURLBar.value, "example.com/");
Assert.equal(gURLBar.selectionStart, "ex".length);
Assert.equal(gURLBar.selectionEnd, "example.com/".length);
// Search with a string that does not match the placeholder. Placeholder
// autofill shouldn't happen.
gURLBar.value = "moz";
UrlbarTestUtils.fireInputEvent(window);
Assert.equal(gURLBar.value, "moz");
Assert.equal(gURLBar.selectionStart, "moz".length);
Assert.equal(gURLBar.selectionEnd, "moz".length);
await UrlbarTestUtils.promiseSearchComplete(window);
// Search for "ex" again. It should be autofilled. Placeholder autofill
// won't happen. It's not important for this test to check that.
await promiseAutocompleteResultPopup("ex", window, true);
details = await UrlbarTestUtils.getDetailsOfResultAt(window, 0);
Assert.ok(details.autofill);
Assert.equal(gURLBar.value, "example.com/");
Assert.equal(gURLBar.selectionStart, "ex".length);
Assert.equal(gURLBar.selectionEnd, "example.com/".length);
// Placeholder autofill should work again for example.com searches.
await searchAndCheck("exa", "example.com/");
await searchAndCheck("EXAM", "EXAMple.com/");
await searchAndCheck("eXaMp", "eXaMple.com/");
await searchAndCheck("exampl", "example.com/");
await cleanUp();
});
add_task(async function noMatch2() {
await PlacesTestUtils.addVisits([
"http://mozilla.org/",
"http://example.com/",
]);
// Do an initial search that triggers autofill so that the placeholder has an
// initial value.
await promiseAutocompleteResultPopup("moz", window, true);
let details = await UrlbarTestUtils.getDetailsOfResultAt(window, 0);
Assert.ok(details.autofill);
Assert.equal(gURLBar.value, "mozilla.org/");
Assert.equal(gURLBar.selectionStart, "moz".length);
Assert.equal(gURLBar.selectionEnd, "mozilla.org/".length);
// Search with a string that does not match the placeholder but does trigger
// autofill. Placeholder autofill shouldn't happen.
gURLBar.value = "ex";
UrlbarTestUtils.fireInputEvent(window);
Assert.equal(gURLBar.value, "ex");
Assert.equal(gURLBar.selectionStart, "ex".length);
Assert.equal(gURLBar.selectionEnd, "ex".length);
await UrlbarTestUtils.promiseSearchComplete(window);
Assert.equal(gURLBar.value, "example.com/");
Assert.equal(gURLBar.selectionStart, "ex".length);
Assert.equal(gURLBar.selectionEnd, "example.com/".length);
// Do some searches that should trigger placeholder autofill.
await searchAndCheck("exa", "example.com/");
await searchAndCheck("EXAm", "EXAmple.com/");
// Search for "moz" again. It should be autofilled. Placeholder autofill
// shouldn't happen.
gURLBar.value = "moz";
UrlbarTestUtils.fireInputEvent(window);
Assert.equal(gURLBar.value, "moz");
Assert.equal(gURLBar.selectionStart, "moz".length);
Assert.equal(gURLBar.selectionEnd, "moz".length);
await UrlbarTestUtils.promiseSearchComplete(window);
Assert.equal(gURLBar.value, "mozilla.org/");
Assert.equal(gURLBar.selectionStart, "moz".length);
Assert.equal(gURLBar.selectionEnd, "mozilla.org/".length);
// Do some searches that should trigger placeholder autofill.
await searchAndCheck("mozi", "mozilla.org/");
await searchAndCheck("MOZil", "MOZilla.org/");
await cleanUp();
});
async function searchAndCheck(searchString, expectedAutofillValue) {
gURLBar.value = searchString;
// Placeholder autofill is done on input, so fire an input event. As the
// comment at the top of this file says, we can't use
// promiseAutocompleteResultPopup() or other helpers that wait for searches to
// complete because we are specifically checking autofill before the search
// completes.
UrlbarTestUtils.fireInputEvent(window);
// Check the input value and selection immediately, before waiting on the
// search to complete.
Assert.equal(gURLBar.value, expectedAutofillValue);
Assert.equal(gURLBar.selectionStart, searchString.length);
Assert.equal(gURLBar.selectionEnd, expectedAutofillValue.length);
await UrlbarTestUtils.promiseSearchComplete(window);
}
async function cleanUp() {
await UrlbarTestUtils.promisePopupClose(window, () => {
EventUtils.synthesizeKey("KEY_Escape");
});
await PlacesUtils.bookmarks.eraseEverything();
await PlacesUtils.history.clear();
}