Commit 5413a659 authored by David Tseng's avatar David Tseng Committed by Commit Bot

Centralize all pointer-type output in BackgroundMouseHandler

This is meant as a purely refactoring change and should have no functional user change.

R=akihiroota@chromium.org

Bug: none
Test: browser_tests --gtest_filter=ChromeVoxBackground*.*HitTest*
Change-Id: Id361780aeb90545e65f28f7ee3de09160ac8adcf
AX-Relnotes: n/a
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2270842Reviewed-by: default avatarAkihiro Ota <akihiroota@chromium.org>
Commit-Queue: David Tseng <dtseng@chromium.org>
Cr-Commit-Position: refs/heads/master@{#783680}
parent b1624781
...@@ -16,7 +16,7 @@ ChromeVoxBackgroundTest = class extends ChromeVoxNextE2ETest { ...@@ -16,7 +16,7 @@ ChromeVoxBackgroundTest = class extends ChromeVoxNextE2ETest {
window.EventType = chrome.automation.EventType; window.EventType = chrome.automation.EventType;
window.RoleType = chrome.automation.RoleType; window.RoleType = chrome.automation.RoleType;
window.doCmd = this.doCmd; window.doCmd = this.doCmd;
window.doHover = this.doHover; window.simulateHitTestResult = this.simulateHitTestResult;
window.press = this.press; window.press = this.press;
window.Mod = constants.ModifierFlag; window.Mod = constants.ModifierFlag;
...@@ -44,10 +44,9 @@ ChromeVoxBackgroundTest = class extends ChromeVoxNextE2ETest { ...@@ -44,10 +44,9 @@ ChromeVoxBackgroundTest = class extends ChromeVoxNextE2ETest {
}; };
} }
doHover(node) { simulateHitTestResult(node) {
return () => { return () => {
const event = new CustomAutomationEvent(EventType.HOVER, node, 'action'); BackgroundMouseHandler.instance.handleHitTestResult(node);
DesktopAutomationHandler.instance.onHover(event);
}; };
} }
...@@ -1937,7 +1936,7 @@ TEST_F('ChromeVoxBackgroundTest', 'HoverTargetsLeafNode', function() { ...@@ -1937,7 +1936,7 @@ TEST_F('ChromeVoxBackgroundTest', 'HoverTargetsLeafNode', function() {
assertNotNullNorUndefined(buttonP); assertNotNullNorUndefined(buttonP);
const buttonText = buttonP.firstChild; const buttonText = buttonP.firstChild;
assertNotNullNorUndefined(buttonText); assertNotNullNorUndefined(buttonText);
mockFeedback.call(doHover(buttonText)) mockFeedback.call(simulateHitTestResult(buttonText))
.expectSpeech('Jefferson') .expectSpeech('Jefferson')
.expectSpeech('Button') .expectSpeech('Button')
.replay(); .replay();
...@@ -2619,7 +2618,7 @@ TEST_F('ChromeVoxBackgroundTest', 'HitTestOnExoSurface', function() { ...@@ -2619,7 +2618,7 @@ TEST_F('ChromeVoxBackgroundTest', 'HitTestOnExoSurface', function() {
}); });
TEST_F('ChromeVoxBackgroundTest', 'HoverSkipsContainers', function() { TEST_F('ChromeVoxBackgroundTest', 'HoverSkipsContainers', function() {
DesktopAutomationHandler.MIN_HOVER_EXIT_SOUND_DELAY_MS = -1; BackgroundMouseHandler.MIN_HOVER_EXIT_SOUND_DELAY_MS = -1;
const mockFeedback = this.createMockFeedback(); const mockFeedback = this.createMockFeedback();
this.runWithLoadedTree( this.runWithLoadedTree(
` `
...@@ -2640,7 +2639,7 @@ TEST_F('ChromeVoxBackgroundTest', 'HoverSkipsContainers', function() { ...@@ -2640,7 +2639,7 @@ TEST_F('ChromeVoxBackgroundTest', 'HoverSkipsContainers', function() {
assertNotNullNorUndefined(button); assertNotNullNorUndefined(button);
const group = button.parent; const group = button.parent;
assertNotNullNorUndefined(group); assertNotNullNorUndefined(group);
mockFeedback.call(doHover(button)) mockFeedback.call(simulateHitTestResult(button))
.expectSpeech('Button') .expectSpeech('Button')
.call(() => { .call(() => {
// Override the role to simulate panes which are only found in // Override the role to simulate panes which are only found in
...@@ -2651,10 +2650,10 @@ TEST_F('ChromeVoxBackgroundTest', 'HoverSkipsContainers', function() { ...@@ -2651,10 +2650,10 @@ TEST_F('ChromeVoxBackgroundTest', 'HoverSkipsContainers', function() {
} }
}); });
}) })
.call(doHover(group)) .call(simulateHitTestResult(group))
.expectSpeech('range cleared!') .expectSpeech('range cleared!')
.expectEarcon(Earcon.TOUCH_EXIT) .expectEarcon(Earcon.TOUCH_EXIT)
.call(doHover(group)) .call(simulateHitTestResult(group))
.expectSpeech('range cleared!') .expectSpeech('range cleared!')
.expectEarcon(Earcon.TOUCH_EXIT) .expectEarcon(Earcon.TOUCH_EXIT)
.replay(); .replay();
......
...@@ -54,18 +54,11 @@ DesktopAutomationHandler = class extends BaseAutomationHandler { ...@@ -54,18 +54,11 @@ DesktopAutomationHandler = class extends BaseAutomationHandler {
/** @private {number?} */ /** @private {number?} */
this.delayedAttributeOutputId_; this.delayedAttributeOutputId_;
/** @private {!Date} */
this.lastHoverExit_ = new Date();
/** @private {!AutomationNode|undefined} */
this.lastHoverTarget_;
this.addListener_(EventType.ALERT, this.onAlert); this.addListener_(EventType.ALERT, this.onAlert);
this.addListener_(EventType.BLUR, this.onBlur); this.addListener_(EventType.BLUR, this.onBlur);
this.addListener_( this.addListener_(
EventType.DOCUMENT_SELECTION_CHANGED, this.onDocumentSelectionChanged); EventType.DOCUMENT_SELECTION_CHANGED, this.onDocumentSelectionChanged);
this.addListener_(EventType.FOCUS, this.onFocus); this.addListener_(EventType.FOCUS, this.onFocus);
this.addListener_(EventType.HOVER, this.onHover);
// Note that live region changes from views are really announcement // Note that live region changes from views are really announcement
// events. Their target nodes contain no live region semantics and have no // events. Their target nodes contain no live region semantics and have no
...@@ -99,14 +92,6 @@ DesktopAutomationHandler = class extends BaseAutomationHandler { ...@@ -99,14 +92,6 @@ DesktopAutomationHandler = class extends BaseAutomationHandler {
return this.textEditHandler_; return this.textEditHandler_;
} }
/**
* @return {!AutomationNode|undefined} The target of the last observed hover
* event.
*/
get lastHoverTarget() {
return this.lastHoverTarget_;
}
/** @override */ /** @override */
willHandleEvent_(evt) { willHandleEvent_(evt) {
return false; return false;
...@@ -153,75 +138,6 @@ DesktopAutomationHandler = class extends BaseAutomationHandler { ...@@ -153,75 +138,6 @@ DesktopAutomationHandler = class extends BaseAutomationHandler {
}); });
} }
/**
* @param {!ChromeVoxEvent} evt
*/
onHover(evt) {
if (!GestureCommandHandler.getEnabled()) {
return;
}
EventSourceState.set(EventSourceType.TOUCH_GESTURE);
// Save the last hover target for use by the gesture handler.
this.lastHoverTarget_ = evt.target;
let target = evt.target;
// If the target is in an ExoSurface, which hosts remote content, trigger a
// mouse move. This only occurs when we programmatically hit test content
// within ARC++ for now. Mouse moves automatically trigger Android to send
// hover events back.
if (target.role == RoleType.WINDOW &&
target.className.indexOf('ExoSurface') == 0) {
BackgroundMouseHandler.instance.synthesizeMouseMove();
return;
}
let targetLeaf = null;
let targetObject = null;
while (target && target != target.root) {
if (!targetObject && AutomationPredicate.touchObject(target)) {
targetObject = target;
}
if (AutomationPredicate.touchLeaf(target)) {
targetLeaf = target;
}
target = target.parent;
}
target = targetLeaf || targetObject;
if (!target) {
// This clears the anchor point in the TouchExplorationController (so
// things like double tap won't be directed to the previous target). It
// also ensures if a user touch explores back to the previous range, it
// will be announced again.
ChromeVoxState.instance.setCurrentRange(null);
// Play a earcon to let the user know they're in the middle of nowhere.
if ((new Date() - this.lastHoverExit_) >
DesktopAutomationHandler.MIN_HOVER_EXIT_SOUND_DELAY_MS) {
ChromeVox.earcons.playEarcon(Earcon.TOUCH_EXIT);
this.lastHoverExit_ = new Date();
}
chrome.tts.stop();
return;
}
if (ChromeVoxState.instance.currentRange &&
target == ChromeVoxState.instance.currentRange.start.node) {
return;
}
if (!this.createTextEditHandlerIfNeeded_(target)) {
this.textEditHandler_ = null;
}
Output.forceModeForNextSpeechUtterance(QueueMode.FLUSH);
this.onEventDefault(new CustomAutomationEvent(
evt.type, target, evt.eventFrom, evt.intents));
}
/** /**
* Makes an announcement without changing focus. * Makes an announcement without changing focus.
* @param {!ChromeVoxEvent} evt * @param {!ChromeVoxEvent} evt
...@@ -709,9 +625,6 @@ DesktopAutomationHandler.ATTRIBUTE_DELAY_MS = 1500; ...@@ -709,9 +625,6 @@ DesktopAutomationHandler.ATTRIBUTE_DELAY_MS = 1500;
*/ */
DesktopAutomationHandler.announceActions = false; DesktopAutomationHandler.announceActions = false;
/** @const {number} */
DesktopAutomationHandler.MIN_HOVER_EXIT_SOUND_DELAY_MS = 500;
/** /**
* Global instance. * Global instance.
* @type {DesktopAutomationHandler} * @type {DesktopAutomationHandler}
......
...@@ -72,7 +72,7 @@ GestureCommandHandler.onAccessibilityGesture_ = function(gesture, x, y) { ...@@ -72,7 +72,7 @@ GestureCommandHandler.onAccessibilityGesture_ = function(gesture, x, y) {
} }
if (!ChromeVoxState.instance.currentRange && commandData.shouldRecoverRange) { if (!ChromeVoxState.instance.currentRange && commandData.shouldRecoverRange) {
const recoverTo = DesktopAutomationHandler.instance.lastHoverTarget; const recoverTo = BackgroundMouseHandler.instance.lastHoverTarget;
if (recoverTo) { if (recoverTo) {
ChromeVoxState.instance.setCurrentRange( ChromeVoxState.instance.setCurrentRange(
cursors.Range.fromNode(recoverTo)); cursors.Range.fromNode(recoverTo));
......
...@@ -12,39 +12,34 @@ goog.require('BaseAutomationHandler'); ...@@ -12,39 +12,34 @@ goog.require('BaseAutomationHandler');
const AutomationEvent = chrome.automation.AutomationEvent; const AutomationEvent = chrome.automation.AutomationEvent;
const EventType = chrome.automation.EventType; const EventType = chrome.automation.EventType;
const RoleType = chrome.automation.RoleType;
const INTERVAL_MS_BETWEEN_HIT_TESTS = 50;
BackgroundMouseHandler = class extends BaseAutomationHandler { BackgroundMouseHandler = class extends BaseAutomationHandler {
constructor() { constructor() {
super(null); super(null);
/** @private {chrome.automation.AutomationNode} */
this.node_;
/**
*
* The desktop node.
*
* @private {!chrome.automation.AutomationNode}
*/
this.desktop_;
/** @private {boolean|undefined} */
this.isWaitingBeforeHitTest_;
/** @private {boolean|undefined} */ /** @private {boolean|undefined} */
this.hasPendingEvents_; this.hasPendingEvents_;
/** @private {number|undefined} */ /** @private {number|undefined} */
this.mouseX_; this.mouseX_;
/** @private {number|undefined} */ /** @private {number|undefined} */
this.mouseY_; this.mouseY_;
/** @private {!Date} */
this.lastHoverExit_ = new Date();
/** @private {!AutomationNode|undefined} */
this.lastHoverTarget_;
chrome.automation.getDesktop((desktop) => { chrome.automation.getDesktop((desktop) => {
this.node_ = desktop; this.node_ = desktop;
this.desktop_ = desktop;
this.addListener_(EventType.MOUSE_MOVED, this.onMouseMove); this.addListener_(EventType.MOUSE_MOVED, this.onMouseMove);
// When |this.isWaitingBeforeHitTest| is true, we should never run a // This is needed for ARC++ which sends back hovers when we send mouse
// hittest, and there should be a timer running so that it can be set to // moves.
// false. this.addListener_(EventType.HOVER, (evt) => {
this.isWaitingBeforeHitTest_ = false; this.handleHitTestResult(evt.target);
this.runHitTest();
});
this.hasPendingEvents_ = false; this.hasPendingEvents_ = false;
this.mouseX_ = 0; this.mouseX_ = 0;
this.mouseY_ = 0; this.mouseY_ = 0;
...@@ -56,39 +51,28 @@ BackgroundMouseHandler = class extends BaseAutomationHandler { ...@@ -56,39 +51,28 @@ BackgroundMouseHandler = class extends BaseAutomationHandler {
} }
/** /**
* Starts a timer which, when it finishes, calls runHitTest if an event was * Performs a hit test using the most recent mouse coordinates received in
* waiting to resolve to a node. * onMouseMove or onMove (a e.g. for touch explore).
*/
startTimer() {
this.isWaitingBeforeHitTest_ = true;
setTimeout(() => {
this.isWaitingBeforeHitTest_ = false;
if (this.hasPendingEvents_) {
this.runHitTest();
}
}, INTERVAL_MS_BETWEEN_HIT_TESTS);
}
/**
* Performs a hittest using the most recent mouse coordinates received in
* onMouseMove, and then starts a timer so that further hittests don't occur
* immediately after.
* *
* Note that runHitTest is only ever called when |isWaitingBeforeHitTest| is * Note that runHitTest only ever does a hit test when |hasPendingEvents| is
* false and |hasPendingEvents| is true. * true.
*/ */
runHitTest() { runHitTest() {
if (this.mouseX_ === undefined || this.mouseY_ === undefined) { if (this.mouseX_ === undefined || this.mouseY_ === undefined) {
return; return;
} }
this.desktop_.hitTest(this.mouseX_, this.mouseY_, EventType.HOVER); if (!this.hasPendingEvents_) {
return;
}
this.node_.hitTestWithReply(this.mouseX_, this.mouseY_, (target) => {
this.handleHitTestResult(target);
this.runHitTest();
});
this.hasPendingEvents_ = false; this.hasPendingEvents_ = false;
this.startTimer();
} }
/** /**
* Handles mouse move events. If the timer is running, it will perform a * Handles mouse move events.
* hittest on the most recent event; otherwise request a hittest immediately.
* @param {AutomationEvent} evt The mouse move event to process. * @param {AutomationEvent} evt The mouse move event to process.
*/ */
onMouseMove(evt) { onMouseMove(evt) {
...@@ -104,9 +88,7 @@ BackgroundMouseHandler = class extends BaseAutomationHandler { ...@@ -104,9 +88,7 @@ BackgroundMouseHandler = class extends BaseAutomationHandler {
this.mouseX_ = x; this.mouseX_ = x;
this.mouseY_ = y; this.mouseY_ = y;
this.hasPendingEvents_ = true; this.hasPendingEvents_ = true;
if (!this.isWaitingBeforeHitTest_) { this.runHitTest();
this.runHitTest();
}
} }
/** /**
...@@ -123,7 +105,82 @@ BackgroundMouseHandler = class extends BaseAutomationHandler { ...@@ -123,7 +105,82 @@ BackgroundMouseHandler = class extends BaseAutomationHandler {
y: this.mouseY_ y: this.mouseY_
}); });
} }
/**
* Handles the result of a test test e.g. speaking the node.
* @param {chrome.automation.AutomationNode} result
*/
handleHitTestResult(result) {
if (!result) {
return;
}
let target = result;
// Save the last hover target for use by the gesture handler.
this.lastHoverTarget_ = target;
// If the target is in an ExoSurface, which hosts remote content, trigger a
// mouse move. This only occurs when we programmatically hit test content
// within ARC++ for now. Mouse moves automatically trigger Android to send
// hover events back.
if (target.role == RoleType.WINDOW &&
target.className.indexOf('ExoSurface') == 0) {
this.synthesizeMouseMove();
return;
}
let targetLeaf = null;
let targetObject = null;
while (target && target != target.root) {
if (!targetObject && AutomationPredicate.touchObject(target)) {
targetObject = target;
}
if (AutomationPredicate.touchLeaf(target)) {
targetLeaf = target;
}
target = target.parent;
}
target = targetLeaf || targetObject;
if (!target) {
// This clears the anchor point in the TouchExplorationController (so
// things like double tap won't be directed to the previous target). It
// also ensures if a user touch explores back to the previous range, it
// will be announced again.
ChromeVoxState.instance.setCurrentRange(null);
// Play a earcon to let the user know they're in the middle of nowhere.
if ((new Date() - this.lastHoverExit_) >
BackgroundMouseHandler.MIN_HOVER_EXIT_SOUND_DELAY_MS) {
ChromeVox.earcons.playEarcon(Earcon.TOUCH_EXIT);
this.lastHoverExit_ = new Date();
}
chrome.tts.stop();
return;
}
if (ChromeVoxState.instance.currentRange &&
target == ChromeVoxState.instance.currentRange.start.node) {
return;
}
Output.forceModeForNextSpeechUtterance(QueueMode.FLUSH);
DesktopAutomationHandler.instance.onEventDefault(
new CustomAutomationEvent(EventType.HOVER, target, '', []));
}
/**
* @return {!AutomationNode|undefined} The target of the last observed hover
* event.
*/
get lastHoverTarget() {
return this.lastHoverTarget_;
}
}; };
/** @const {number} */
BackgroundMouseHandler.MIN_HOVER_EXIT_SOUND_DELAY_MS = 500;
/** @type {!BackgroundMouseHandler} */ /** @type {!BackgroundMouseHandler} */
BackgroundMouseHandler.instance = new BackgroundMouseHandler(); BackgroundMouseHandler.instance = new BackgroundMouseHandler();
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