Commit c0dd4e06 authored by Joel Riley's avatar Joel Riley Committed by Chromium LUCI CQ

Fix bug where focus ring disappeared sometimes when quickly navigating.

Timing issue that can happen when user performs navigation before 'start' event is received. Simplified interruption handler which was putting STS into 'inactive' state when it was not necessary.

Bug: 1165284
Change-Id: Ia9818b29fcd57e0aaa5584579851ad093d9f89d4
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2622748
Commit-Queue: Joel Riley <joelriley@google.com>
Reviewed-by: default avatarKatie Dektar <katie@chromium.org>
Reviewed-by: default avatarDominic Mazzoni <dmazzoni@chromium.org>
Cr-Commit-Position: refs/heads/master@{#843742}
parent 41d26edb
......@@ -37,6 +37,20 @@ var MockTts = function() {
* @private
*/
this.options_ = null;
/**
* Whether TTS events will be queued instead of sent immediately.
* @private {boolean}
*/
this.waitToSendEvents_ = false;
/**
* TTS engine events waiting to be sent. Each entry in the array is an array
* of two elements: [options, event], where |options| is the TTS options
* during the time of that event, and |event| is the event isself.
* @private {!Array<!Array<!Object>>}
*/
this.pendingEvents_ = [];
};
MockTts.prototype = {
......@@ -47,7 +61,7 @@ MockTts.prototype = {
this.currentlySpeaking_ = true;
if (options && options.onEvent) {
this.options_ = options;
this.options_.onEvent({type: 'start', charIndex: 0});
this.sendEvent({type: 'start', charIndex: 0});
}
if (this.speechCallbackStack_.length > 0) {
this.speechCallbackStack_.pop()(utterance);
......@@ -57,7 +71,7 @@ MockTts.prototype = {
this.pendingUtterances_ = [];
this.currentlySpeaking_ = false;
if (this.options_) {
this.options_.onEvent({type: 'end'});
this.sendEvent({type: 'end'});
}
},
/**
......@@ -68,14 +82,14 @@ MockTts.prototype = {
speakUntilCharIndex(nextStartIndex) {
this.currentlySpeaking_ = true;
if (this.options_) {
this.options_.onEvent({type: 'word', charIndex: nextStartIndex});
this.sendEvent({type: 'word', charIndex: nextStartIndex});
}
},
stop() {
this.pendingUtterances_ = [];
this.currentlySpeaking_ = false;
if (this.options_) {
this.options_.onEvent({type: 'cancelled'});
this.sendEvent({type: 'cancelled'});
this.options_ = null;
}
},
......@@ -97,5 +111,25 @@ MockTts.prototype = {
},
getOptions() {
return this.options_;
},
sendEvent(event) {
if (!this.options_) {
return;
}
if (this.waitToSendEvents_) {
// Queue event if set to wait on events.
this.pendingEvents_.push([this.options_, event]);
return;
}
this.options_.onEvent(event);
},
setWaitToSendEvents(waitToSendEvents) {
this.waitToSendEvents_ = waitToSendEvents;
},
sendPendingEvents() {
while (this.pendingEvents_.length > 0) {
const [options, event] = this.pendingEvents_.pop();
options.onEvent(event);
}
}
};
......@@ -1518,6 +1518,7 @@ export class SelectToSpeak {
} else if (event.type === 'interrupted' || event.type === 'cancelled') {
if (!this.shouldShowNavigationControls_()) {
this.onStateChanged_(SelectToSpeakState.INACTIVE);
return;
}
if (this.state_ === SelectToSpeakState.SELECTING) {
// Do not go into inactive state if navigation controls are enabled
......@@ -1525,14 +1526,10 @@ export class SelectToSpeak {
// to select new nodes while STS is active without first exiting.
return;
}
if (!this.pauseCompleteCallback_) {
// Auto dismiss when navigation control is not enabled. In addition,
// if the interrupted or cancelled events are not triggered by
// |this.pause_| (e.g., from stopAll_), we should leave STS as
// INACTIVE. Currently, we check |this.pauseCompleteCallback_| as a
if (this.pauseCompleteCallback_) {
// Set to paused state if interruption/cancelled event triggered from
// a pause. Currently, we check |this.pauseCompleteCallback_| as a
// proxy to see if the interrupted events are from |this.pause_|.
this.onStateChanged_(SelectToSpeakState.INACTIVE);
} else {
this.updatePauseStatusFromTtsEvent_(true /* shouldPause */);
}
} else if (event.type === 'end') {
......
......@@ -716,4 +716,34 @@ TEST_F(
// Should auto-dismiss.
assertEquals(selectToSpeak.state_, SelectToSpeakState.INACTIVE);
});
});
TEST_F(
'SelectToSpeakNavigationControlTest', 'NavigatesToNextParagraphQuickly',
function() {
const bodyHtml = `
<p id="p1">Paragraph 1</p>
<p id="p2">Paragraph 2</p>'
`;
this.runWithLoadedTree(
this.generateHtmlWithSelectedElement('p1', bodyHtml), () => {
// Have mock TTS engine wait to send events so we can simulate a
// delayed 'start' event.
this.mockTts.setWaitToSendEvents(true);
this.triggerReadSelectedText();
const speakOptions = this.mockTts.getOptions();
// Navigate to next paragraph before speech begins.
selectToSpeak.onSelectToSpeakPanelAction_(
chrome.accessibilityPrivate.SelectToSpeakPanelAction
.NEXT_PARAGRAPH);
this.waitOneEventLoop(() => {
// Manually triggered delayed events.
this.mockTts.sendPendingEvents();
// Should remain in speaking state.
assertEquals(selectToSpeak.state_, SelectToSpeakState.SPEAKING);
});
});
});
\ No newline at end of file
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