Bug 1438329 - Problem(s) with delayed key handling in urlbar. r=mak

MozReview-Commit-ID: CwvnsLJGgG6

--HG--
extra : rebase_source : 02d640bc1108b2bb976dcee0f52a099614c1bc43
This commit is contained in:
Drew Willcoxon 2018-02-16 11:13:21 -08:00
Родитель 40c81820ad
Коммит 6e1df9fafb
2 изменённых файлов: 124 добавлений и 52 удалений

Просмотреть файл

@ -18,14 +18,6 @@ add_task(async function setup() {
});
add_task(async function test_keyword() {
// This is set because we see (undiagnosed) test timeouts without it
let timerPrecision = Preferences.get("privacy.reduceTimerPrecision");
Preferences.set("privacy.reduceTimerPrecision", false);
registerCleanupFunction(function() {
Preferences.set("privacy.reduceTimerPrecision", timerPrecision);
});
await promiseAutocompleteResultPopup("keyword bear");
gURLBar.focus();
EventUtils.synthesizeKey("d", {});
@ -108,17 +100,8 @@ add_task(async function test_delay() {
// Set a large delay.
let delay = Preferences.get("browser.urlbar.delay");
Preferences.set("browser.urlbar.delay", TIMEOUT);
// This may be a real test regression on Bug 1283329, if we can get it to
// fail at a realistic 2ms.
let timerPrecision = Preferences.get("privacy.reduceTimerPrecision");
Preferences.set("privacy.reduceTimerPrecision", true);
let timerPrecisionUSec = Preferences.get("privacy.resistFingerprinting.reduceTimerPrecision.microseconds");
Preferences.set("privacy.resistFingerprinting.reduceTimerPrecision.microseconds", 2000);
registerCleanupFunction(function() {
Preferences.set("browser.urlbar.delay", delay);
Preferences.set("privacy.reduceTimerPrecision", timerPrecision);
Preferences.set("privacy.resistFingerprinting.reduceTimerPrecision.microseconds", timerPrecisionUSec);
});
// This is needed to clear the current value, otherwise autocomplete may think

Просмотреть файл

@ -271,8 +271,7 @@ file, You can obtain one at http://mozilla.org/MPL/2.0/.
break;
}
if (!this.popup.disableKeyNavigation) {
if (this._keyCodesToDefer.has(aEvent.keyCode) &&
this._shouldDeferKeyEvent(aEvent)) {
if (this._shouldDeferKeyEvent(aEvent)) {
this._deferKeyEvent(aEvent, "onKeyPress");
return false;
}
@ -296,33 +295,86 @@ file, You can obtain one at http://mozilla.org/MPL/2.0/.
keys are buffered and deferred until more results arrive, at which time
they're replayed.
@param event
The key event that should maybe be deferred. You can pass null
or undefined if you don't have one to see whether the next key
event in general should be deferred.
@param event
The key event that should maybe be deferred.
@return True if the event should be deferred, false if not.
-->
<method name="_shouldDeferKeyEvent">
<parameter name="event"/>
<body><![CDATA[
let waitedLongEnough =
this._searchStartDate + this._deferredKeyEventTimeoutMs < Date.now();
if (waitedLongEnough && !this._deferredKeyEventTimeout) {
return false;
}
if (event && event.keyCode == KeyEvent.DOM_VK_TAB && !this.popupOpen) {
// In this case, the popup is closed and the user pressed the Tab
// key. The focus should move out of the urlbar immediately.
return false;
}
if (!this.gotResultForCurrentQuery || !this.popupOpen) {
// If any event has been deferred for this search, then defer all
// subsequent events so that the user does not experience any
// keypresses out of order. All events will be replayed when this
// timeout fires.
if (this._deferredKeyEventTimeout) {
return true;
}
// At this point, no events have been deferred for this search, and we
// need to decide whether `event` is the first one that should be.
if (!this._keyCodesToDefer.has(event.keyCode)) {
// Not a key that should trigger deferring.
return false;
}
let waitedLongEnough =
this._searchStartDate + this._deferredKeyEventTimeoutMs <= Cu.now();
if (waitedLongEnough) {
// This is a key that we would defer, but enough time has passed
// since the start of the search that we don't want to block the
// user's keypresses anymore.
return false;
}
if (event.keyCode == KeyEvent.DOM_VK_TAB && !this.popupOpen) {
// The popup is closed and the user pressed the Tab key. The
// focus should move out of the urlbar immediately.
return false;
}
return !this._safeToPlayDeferredKeyEvent(event);
]]></body>
</method>
<!--
Returns true if the given deferred key event can be played now without
possibly surprising the user. This depends on the state of the popup,
its results, and the type of keypress. Use this method only after
determining that the event should be deferred, or after it's already
been deferred and you want to know if it can be played now.
@param event
The key event.
@return True if the event can be played, false if not.
-->
<method name="_safeToPlayDeferredKeyEvent">
<parameter name="event"/>
<body><![CDATA[
if (!this.gotResultForCurrentQuery || !this.popupOpen) {
// We're still waiting on the first result, or the popup hasn't
// opened yet, so not safe.
return false;
}
let maxResultsRemaining =
this.popup.maxResults - this.popup.matchCount;
let lastResultSelected =
this.popup.selectedIndex + 1 == this.popup.matchCount;
return maxResultsRemaining > 0 && lastResultSelected;
if (maxResultsRemaining == 0) {
// The popup can't possibly have any more results, so there's no
// need to defer any event now.
return true;
}
if (event.keyCode == KeyEvent.DOM_VK_DOWN) {
// Don't play the event if the last result is selected so that the
// user doesn't accidentally arrow down into the one-off buttons
// when they didn't mean to.
let lastResultSelected =
this.popup.selectedIndex + 1 == this.popup.matchCount;
return !lastResultSelected;
}
return true;
]]></body>
</method>
@ -354,10 +406,22 @@ file, You can obtain one at http://mozilla.org/MPL/2.0/.
});
if (!this._deferredKeyEventTimeout) {
this._deferredKeyEventTimeout = setTimeout(() => {
this._deferredKeyEventTimeout = null;
this.maybeReplayDeferredKeyEvents();
}, this._deferredKeyEventTimeoutMs);
// Start the timeout that will unconditionally replay all deferred
// events when it fires so that, after a certain point, we don't
// keep blocking the user's keypresses when nothing else has caused
// the events to be replayed. Do not check whether it's safe to
// replay the events because otherwise it may look like we ignored
// the user's input.
let elapsed = Cu.now() - this._searchStartDate;
let remaining = this._deferredKeyEventTimeoutMs - elapsed;
if (remaining <= 0) {
this.replayAllDeferredKeyEvents();
} else {
this._deferredKeyEventTimeout = setTimeout(() => {
this.replayAllDeferredKeyEvents();
this._deferredKeyEventTimeout = null;
}, remaining);
}
}
]]></body>
</method>
@ -372,24 +436,49 @@ file, You can obtain one at http://mozilla.org/MPL/2.0/.
<field name="_deferredKeyEventTimeoutMs">200</field>
<field name="_searchStartDate">0</field>
<method name="maybeReplayDeferredKeyEvents">
<method name="replaySafeDeferredKeyEvents">
<body><![CDATA[
if (!this._deferredKeyEventQueue.length ||
this._shouldDeferKeyEvent()) {
if (!this._deferredKeyEventQueue.length) {
return;
}
if (this._deferredKeyEventTimeout) {
clearTimeout(this._deferredKeyEventTimeout);
this._deferredKeyEventTimeout = null;
let instance = this._deferredKeyEventQueue[0];
if (!this._safeToPlayDeferredKeyEvent(instance.event)) {
return;
}
this._deferredKeyEventQueue.shift();
this._replayKeyEventInstance(instance);
Services.tm.dispatchToMainThread(() => {
this.replaySafeDeferredKeyEvents();
});
]]></body>
</method>
<!--
Unconditionally replays all deferred key events. This does not check
whether it's safe to replay the events; use replaySafeDeferredKeyEvents
for that. Use this method when you must replay all events so that it
does not appear that we ignored the user's input.
-->
<method name="replayAllDeferredKeyEvents">
<body><![CDATA[
let instance = this._deferredKeyEventQueue.shift();
if (!instance) {
return;
}
this._replayKeyEventInstance(instance);
Services.tm.dispatchToMainThread(() => {
this.replayAllDeferredKeyEvents();
});
]]></body>
</method>
<method name="_replayKeyEventInstance">
<parameter name="instance"/>
<body><![CDATA[
// Safety check: handle only if the search string didn't change.
if (this.mController.searchString == instance.searchString) {
this[instance.methodName](instance.event);
}
setTimeout(() => {
this.maybeReplayDeferredKeyEvents();
});
]]></body>
</method>
@ -1363,7 +1452,7 @@ file, You can obtain one at http://mozilla.org/MPL/2.0/.
// a new search and we won't get a result.
if (this.mController.handleText()) {
this.gotResultForCurrentQuery = false;
this._searchStartDate = Date.now();
this._searchStartDate = Cu.now();
this._deferredKeyEventQueue = [];
if (this._deferredKeyEventTimeout) {
clearTimeout(this._deferredKeyEventTimeout);
@ -2217,7 +2306,7 @@ file, You can obtain one at http://mozilla.org/MPL/2.0/.
this.input.tabScrolling = true;
this.input.gotResultForCurrentQuery = true;
this.input.maybeReplayDeferredKeyEvents();
this.input.replaySafeDeferredKeyEvents();
]]>
</body>
</method>