Commit 892ef7a4 authored by Moe Ahmadi's avatar Moe Ahmadi Committed by Commit Bot

Local NTP, Realbox: Remember pressing 'Enter' when results are not ready

This CL adds logic to "remember" the user pressing 'Enter' when
autocomplete matches are stale. This happens if the user types too fast
and presses 'Enter' immediately. Currently, the realbox either does nothing
or navigates to a stale result. After this change, the realbox does nothing
until up-to-date results have arrived. it then navigates to the default
match, if one exists, assuming that navigation to the default match was the
user intent, signaled by typing too fast and pressing 'Enter'.

Bug: 1024109
Change-Id: I67e434419e586d3488b1239b9fa37da8aaa4fec4
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/1928214
Commit-Queue: Moe Ahmadi <mahmadi@chromium.org>
Reviewed-by: default avatarDan Beam <dbeam@chromium.org>
Cr-Commit-Position: refs/heads/master@{#718302}
parent 4917d1d7
...@@ -41,9 +41,6 @@ const ACMatchClassificationStyle = { ...@@ -41,9 +41,6 @@ const ACMatchClassificationStyle = {
DIM: 1 << 2, DIM: 1 << 2,
}; };
/** @type {string} */
let lastInput;
/** @typedef {{inline: string, text: string}} */ /** @typedef {{inline: string, text: string}} */
let RealboxOutput; let RealboxOutput;
...@@ -239,8 +236,8 @@ const REALBOX_KEYDOWN_HANDLED_KEYS = [ ...@@ -239,8 +236,8 @@ const REALBOX_KEYDOWN_HANDLED_KEYS = [
// Local statics. // Local statics.
/** @type {!Array<!AutocompleteMatch>} */ /** @type {?AutocompleteResult} */
let autocompleteMatches = []; let autocompleteResult = null;
/** /**
* The currently visible notification element. Null if no notification is * The currently visible notification element. Null if no notification is
...@@ -256,6 +253,13 @@ let currNotification = null; ...@@ -256,6 +253,13 @@ let currNotification = null;
*/ */
let delayedHideNotification = null; let delayedHideNotification = null;
/**
* Whether 'Enter' was pressed but did not navigate to a match due to matches
* being stale.
* @type {boolean}
*/
let enterWasPressed = false;
/** /**
* True if dark mode is enabled. * True if dark mode is enabled.
* @type {boolean} * @type {boolean}
...@@ -275,8 +279,18 @@ let isDeletingInput = false; ...@@ -275,8 +279,18 @@ let isDeletingInput = false;
*/ */
let lastBlacklistedTile = null; let lastBlacklistedTile = null;
/** @type {?number} */ /**
let lastRealboxFocusTime = null; * The 'Enter' event that was ignored due to matches being stale. Will be used
* to navigate to the default match once up-to-date matches arrive.
* @type {?Event}
*/
let lastEnterEvent = null;
/**
* The last queried input.
* @type {string|undefined}
*/
let lastInput;
/** /**
* Last text/inline autocompletion shown in the realbox (either by user input or * Last text/inline autocompletion shown in the realbox (either by user input or
...@@ -285,6 +299,9 @@ let lastRealboxFocusTime = null; ...@@ -285,6 +299,9 @@ let lastRealboxFocusTime = null;
*/ */
let lastOutput = {text: '', inline: ''}; let lastOutput = {text: '', inline: ''};
/** @type {?number} */
let lastRealboxFocusTime = null;
/** /**
* The browser embeddedSearch.newTabPage object. * The browser embeddedSearch.newTabPage object.
* @type {Object} * @type {Object}
...@@ -1083,7 +1100,7 @@ function listen() { ...@@ -1083,7 +1100,7 @@ function listen() {
* @param {!Event} e * @param {!Event} e
*/ */
function navigateToMatch(match, e) { function navigateToMatch(match, e) {
const line = autocompleteMatches.indexOf(match); const line = autocompleteResult.matches.indexOf(match);
assert(line >= 0); assert(line >= 0);
assert(lastRealboxFocusTime); assert(lastRealboxFocusTime);
window.chrome.embeddedSearch.searchBox.openAutocompleteMatch( window.chrome.embeddedSearch.searchBox.openAutocompleteMatch(
...@@ -1158,6 +1175,7 @@ function autocompleteResultChanged(result) { ...@@ -1158,6 +1175,7 @@ function autocompleteResultChanged(result) {
} }
populateAutocompleteMatches(result.matches); populateAutocompleteMatches(result.matches);
autocompleteResult = result;
$(IDS.REALBOX).focus(); $(IDS.REALBOX).focus();
...@@ -1170,6 +1188,11 @@ function autocompleteResultChanged(result) { ...@@ -1170,6 +1188,11 @@ function autocompleteResultChanged(result) {
if (first && first.allowedToBeDefaultMatch) { if (first && first.allowedToBeDefaultMatch) {
selectMatchEl(assert($(IDS.REALBOX_MATCHES).firstElementChild)); selectMatchEl(assert($(IDS.REALBOX_MATCHES).firstElementChild));
updateRealboxOutput({inline: first.inlineAutocompletion}); updateRealboxOutput({inline: first.inlineAutocompletion});
if (enterWasPressed) {
assert(lastEnterEvent);
navigateToMatch(first, lastEnterEvent);
}
} }
} }
...@@ -1178,7 +1201,7 @@ function onRealboxCutCopy(e) { ...@@ -1178,7 +1201,7 @@ function onRealboxCutCopy(e) {
const realboxEl = $(IDS.REALBOX); const realboxEl = $(IDS.REALBOX);
if (!realboxEl.value || realboxEl.selectionStart !== 0 || if (!realboxEl.value || realboxEl.selectionStart !== 0 ||
realboxEl.selectionEnd !== realboxEl.value.length || realboxEl.selectionEnd !== realboxEl.value.length ||
autocompleteMatches.length === 0) { !autocompleteResult || autocompleteResult.matches.length === 0) {
// Only handle cut/copy when realbox has content and it's all selected. // Only handle cut/copy when realbox has content and it's all selected.
return; return;
} }
...@@ -1188,7 +1211,7 @@ function onRealboxCutCopy(e) { ...@@ -1188,7 +1211,7 @@ function onRealboxCutCopy(e) {
return matchEl.classList.contains(CLASSES.SELECTED); return matchEl.classList.contains(CLASSES.SELECTED);
}); });
const selectedMatch = autocompleteMatches[selected]; const selectedMatch = autocompleteResult.matches[selected];
if (selectedMatch && !selectedMatch.isSearchType) { if (selectedMatch && !selectedMatch.isSearchType) {
e.clipboardData.setData('text/plain', selectedMatch.destinationUrl); e.clipboardData.setData('text/plain', selectedMatch.destinationUrl);
e.preventDefault(); e.preventDefault();
...@@ -1235,7 +1258,7 @@ function onRealboxWrapperFocusIn(e) { ...@@ -1235,7 +1258,7 @@ function onRealboxWrapperFocusIn(e) {
// It doesn't really make sense to use fillFromMatch() here as the focus // It doesn't really make sense to use fillFromMatch() here as the focus
// change drops the selection (and is probably just noisy to // change drops the selection (and is probably just noisy to
// screenreaders). // screenreaders).
const newFill = autocompleteMatches[selectedIndex].fillIntoEdit; const newFill = autocompleteResult.matches[selectedIndex].fillIntoEdit;
updateRealboxOutput({moveCursorToEnd: true, inline: '', text: newFill}); updateRealboxOutput({moveCursorToEnd: true, inline: '', text: newFill});
} }
} }
...@@ -1302,11 +1325,22 @@ function onRealboxWrapperKeydown(e) { ...@@ -1302,11 +1325,22 @@ function onRealboxWrapperKeydown(e) {
return matchEl.classList.contains(CLASSES.SELECTED); return matchEl.classList.contains(CLASSES.SELECTED);
}); });
assert(autocompleteMatches.length === matchEls.length); assert(autocompleteResult.matches.length === matchEls.length);
if (key === 'Enter') { if (key === 'Enter') {
if (matchEls[selected] && matchEls.concat(realboxEl).includes(e.target)) { if (matchEls.concat(realboxEl).includes(e.target)) {
navigateToMatch(autocompleteMatches[selected], e); if (lastInput === autocompleteResult.input) {
if (autocompleteResult.matches[selected]) {
navigateToMatch(autocompleteResult.matches[selected], e);
}
} else {
// User typed and pressed 'Enter' too quickly. Ignore this for now
// because the matches are stale. Navigate to the default match (if one
// exists) once the up-to-date results arrive.
enterWasPressed = true;
lastEnterEvent = e;
e.preventDefault();
}
} }
return; return;
} }
...@@ -1324,7 +1358,7 @@ function onRealboxWrapperKeydown(e) { ...@@ -1324,7 +1358,7 @@ function onRealboxWrapperKeydown(e) {
if (key === 'Delete') { if (key === 'Delete') {
if (e.shiftKey && !e.altKey && !e.ctrlKey && !e.metaKey) { if (e.shiftKey && !e.altKey && !e.ctrlKey && !e.metaKey) {
const selectedMatch = autocompleteMatches[selected]; const selectedMatch = autocompleteResult.matches[selected];
if (selectedMatch && selectedMatch.supportsDeletion) { if (selectedMatch && selectedMatch.supportsDeletion) {
window.chrome.embeddedSearch.searchBox.deleteAutocompleteMatch( window.chrome.embeddedSearch.searchBox.deleteAutocompleteMatch(
selected); selected);
...@@ -1365,7 +1399,7 @@ function onRealboxWrapperKeydown(e) { ...@@ -1365,7 +1399,7 @@ function onRealboxWrapperKeydown(e) {
matchEls[newSelected].focus(); matchEls[newSelected].focus();
} }
const newMatch = autocompleteMatches[newSelected]; const newMatch = autocompleteResult.matches[newSelected];
const newFill = newMatch.fillIntoEdit; const newFill = newMatch.fillIntoEdit;
let newInline = ''; let newInline = '';
if (newMatch.allowedToBeDefaultMatch) { if (newMatch.allowedToBeDefaultMatch) {
...@@ -1557,7 +1591,6 @@ function populateAutocompleteMatches(matches) { ...@@ -1557,7 +1591,6 @@ function populateAutocompleteMatches(matches) {
const hasMatches = matches.length > 0; const hasMatches = matches.length > 0;
setRealboxMatchesVisible(hasMatches); setRealboxMatchesVisible(hasMatches);
setRealboxWrapperListenForKeydown(hasMatches); setRealboxWrapperListenForKeydown(hasMatches);
autocompleteMatches = matches;
} }
/** /**
...@@ -1866,9 +1899,8 @@ function setAttributionVisibility(show) { ...@@ -1866,9 +1899,8 @@ function setAttributionVisibility(show) {
$(IDS.ATTRIBUTION).style.display = show ? '' : 'none'; $(IDS.ATTRIBUTION).style.display = show ? '' : 'none';
} }
/** @suppress {checkTypes} */
function clearAutocompleteMatches() { function clearAutocompleteMatches() {
autocompleteMatches = []; autocompleteResult = null;
window.chrome.embeddedSearch.searchBox.stopAutocomplete( window.chrome.embeddedSearch.searchBox.stopAutocomplete(
/*clearResult=*/ true); /*clearResult=*/ true);
// Autocomplete sends updates once it is stopped. Invalidate those results // Autocomplete sends updates once it is stopped. Invalidate those results
......
...@@ -785,6 +785,53 @@ test.realbox2.testPressEnterOnSelectedMatch = function() { ...@@ -785,6 +785,53 @@ test.realbox2.testPressEnterOnSelectedMatch = function() {
assertEquals(1, test.realbox.opens.length); assertEquals(1, test.realbox.opens.length);
}; };
test.realbox2.testPressEnterTooQuickly = function() {
test.realbox.realboxEl.dispatchEvent(new Event('focusin', {bubbles: true}));
test.realbox.realboxEl.value = 'hello';
test.realbox.realboxEl.dispatchEvent(new CustomEvent('input'));
chrome.embeddedSearch.searchBox.autocompleteresultchanged({
input: test.realbox.realboxEl.value,
matches: [test.realbox.getSearchMatch({supportsDeletion: true})]
});
const matchEls1 = $(test.realbox.IDS.REALBOX_MATCHES).children;
assertEquals(1, matchEls1.length);
// First match is selected.
assertTrue(matchEls1[0].classList.contains(test.realbox.CLASSES.SELECTED));
test.realbox.realboxEl.value = 'hello world';
test.realbox.realboxEl.dispatchEvent(new CustomEvent('input'));
const shiftEnter = new KeyboardEvent('keydown', {
bubbles: true,
cancelable: true,
key: 'Enter',
target: test.realbox.realboxEl,
shiftKey: true,
});
test.realbox.realboxEl.dispatchEvent(shiftEnter);
// Did not navigate to the first match as it is stale.
assertEquals(0, test.realbox.opens.length);
const matches = [test.realbox.getUrlMatch({supportsDeletion: true})];
chrome.embeddedSearch.searchBox.autocompleteresultchanged(
{input: test.realbox.realboxEl.value, matches});
const matchEls2 = $(test.realbox.IDS.REALBOX_MATCHES).children;
assertEquals(1, matchEls2.length);
// First match is selected.
assertTrue(matchEls2[0].classList.contains(test.realbox.CLASSES.SELECTED));
// Navigated to the first new match.
assertEquals(1, test.realbox.opens.length);
assertEquals(0, test.realbox.opens[0].index);
assertEquals(matches[0].destinationUrl, test.realbox.opens[0].url);
};
test.realbox2.testPressEnterNoSelectedMatch = function() { test.realbox2.testPressEnterNoSelectedMatch = function() {
test.realbox.realboxEl.value = 'hello world'; test.realbox.realboxEl.value = 'hello world';
test.realbox.realboxEl.dispatchEvent(new CustomEvent('input')); test.realbox.realboxEl.dispatchEvent(new CustomEvent('input'));
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment