Commit ec018e32 authored by David Tseng's avatar David Tseng Committed by Commit Bot

Utilize focus recovery in more code paths

1. alt+left/right

Chrome only fires focus events in this case. This change tries to
recover focus whenever a focus event gets fired on a root web area. The
challenge here is the root node gets re-used. Explicit tracking of the
docurl gets introduced as a result.

Forum discussion:
https://groups.google.com/forum/#!topic/axs-chrome-discuss/u9gcqZxDISM

2. toggle on ChromeVox

In this case, ChromeVox tries to always recover focus. Detected by
checking there is no |currentRange|.

3. ctrl+r

In this case, ChromeVox receives a load complete. Only recover if the
|focus| is not already inside of the page (i.e. |focus| is the top level
root).

restores its range appropriately.

Test: exercise each of the above examples; verify that ChromeVox
Bug: 
Cq-Include-Trybots: master.tryserver.chromium.linux:closure_compilation
Change-Id: I6e427105555b2663dca1100a02b8eb6b00cdc08a
Reviewed-on: https://chromium-review.googlesource.com/806759
Commit-Queue: David Tseng <dtseng@chromium.org>
Reviewed-by: default avatarDominic Mazzoni <dmazzoni@chromium.org>
Cr-Commit-Position: refs/heads/master@{#521909}
parent 174f0b5e
......@@ -324,7 +324,7 @@ AutomationPredicate.root = function(node) {
// to be crossed when performing traversals up the ancestry chain.
return false;
}
return !node.parent ||
return !node.parent || !node.parent.root ||
(node.parent.root.role == Role.DESKTOP &&
node.parent.role == Role.WEB_VIEW);
default:
......
......@@ -377,12 +377,12 @@ Background.prototype = {
var start = this.currentRange_.start.node;
start.makeVisible();
var root = start.root;
var root = AutomationUtil.getTopLevelRoot(start);
if (!root || root.role == RoleType.DESKTOP)
return;
var position = {};
var loc = start.location;
var loc = start.unclippedLocation;
position.x = loc.left + loc.width / 2;
position.y = loc.top + loc.height / 2;
var url = root.docUrl;
......
......@@ -525,13 +525,13 @@ TEST_F('BackgroundTest', 'ModeSwitching', function() {
fakeWebRoot.parent = fakeDesktop;
fakeWebRoot.role = RoleType.ROOT_WEB_AREA;
fakeWebRoot.makeVisible = function() {};
fakeWebRoot.location = {left: 1, top: 1, width: 1, height: 1};
fakeWebRoot.unclippedLocation = {left: 1, top: 1, width: 1, height: 1};
var fakeSubRoot = {};
fakeSubRoot.root = fakeSubRoot;
fakeSubRoot.parent = fakeWebRoot;
fakeSubRoot.role = RoleType.ROOT_WEB_AREA;
fakeSubRoot.makeVisible = function() {};
fakeSubRoot.location = {left: 1, top: 1, width: 1, height: 1};
fakeSubRoot.unclippedLocation = {left: 1, top: 1, width: 1, height: 1};
var bk = ChromeVoxState.instance;
// Tests default to force next mode.
......
......@@ -47,6 +47,9 @@ DesktopAutomationHandler = function(node) {
/** @private {AutomationNode} */
this.lastValueTarget_ = null;
/** @private {string} */
this.lastRootUrl_ = '';
this.addListener_(
EventType.ACTIVEDESCENDANTCHANGED, this.onActiveDescendantChanged);
this.addListener_(EventType.ALERT, this.onAlert);
......@@ -296,6 +299,11 @@ DesktopAutomationHandler.prototype = {
* @param {!AutomationEvent} evt
*/
onFocus: function(evt) {
if (evt.target.role == RoleType.ROOT_WEB_AREA) {
this.maybeRecoverFocusAndOutput_(evt);
return;
}
// Invalidate any previous editable text handler state.
if (!this.createTextEditHandlerIfNeeded_(evt.target))
this.textEditHandler_ = null;
......@@ -361,33 +369,7 @@ DesktopAutomationHandler.prototype = {
return;
}
// If initial focus was already placed on this page (e.g. if a user starts
// tabbing before load complete), then don't move ChromeVox's position on
// the page.
if (ChromeVoxState.instance.currentRange &&
ChromeVoxState.instance.currentRange.start.node.root == focus.root)
return;
var o = new Output();
if (focus.role == RoleType.ROOT_WEB_AREA) {
// Restore to previous position.
var url = focus.docUrl;
url = url.substring(0, url.indexOf('#')) || url;
var pos = cvox.ChromeVox.position[url];
if (pos) {
focus = AutomationUtil.hitTest(focus.root, pos) || focus;
if (focus != focus.root)
o.format('$name', focus.root);
}
}
ChromeVoxState.instance.setCurrentRange(cursors.Range.fromNode(focus));
if (!this.shouldOutput_(evt))
return;
Output.forceModeForNextSpeechUtterance(cvox.QueueMode.FLUSH);
o.withRichSpeechAndBraille(
ChromeVoxState.instance.currentRange, null, evt.type)
.go();
this.maybeRecoverFocusAndOutput_(evt);
}.bind(this));
},
......@@ -602,6 +584,60 @@ DesktopAutomationHandler.prototype = {
return evt.target.root.role == RoleType.DESKTOP ||
(mode == ChromeVoxMode.NEXT || mode == ChromeVoxMode.FORCE_NEXT ||
mode == ChromeVoxMode.CLASSIC_COMPAT);
},
/**
* @param {AutomationEvent} evt
* @private
*/
maybeRecoverFocusAndOutput_: function(evt) {
chrome.automation.getFocus(function(focus) {
var focusedRoot = AutomationUtil.getTopLevelRoot(focus);
if (!focusedRoot)
return;
var curRoot;
if (ChromeVoxState.instance.currentRange) {
curRoot = AutomationUtil.getTopLevelRoot(
ChromeVoxState.instance.currentRange.start.node);
}
// If initial focus was already placed inside this page (e.g. if a user
// starts tabbing before load complete), then don't move ChromeVox's
// position on the page.
if (curRoot && focusedRoot == curRoot &&
this.lastRootUrl_ == focusedRoot.docUrl && focus != focusedRoot)
return;
this.lastRootUrl_ = focusedRoot.docUrl || '';
var o = new Output();
// Restore to previous position.
var url = focusedRoot.docUrl;
url = url.substring(0, url.indexOf('#')) || url;
var pos = cvox.ChromeVox.position[url];
if (pos) {
focus = AutomationUtil.hitTest(focusedRoot, pos) || focus;
if (focus != focusedRoot)
o.format('$name', focusedRoot);
} else {
// This catches initial focus (i.e. on startup).
if (!curRoot && focus != focusedRoot)
o.format('$name', focusedRoot);
}
if (ChromeVoxState.instance.currentRange &&
focus == ChromeVoxState.instance.currentRange.start.node)
return;
ChromeVoxState.instance.setCurrentRange(cursors.Range.fromNode(focus));
if (!this.shouldOutput_(evt))
return;
Output.forceModeForNextSpeechUtterance(cvox.QueueMode.FLUSH);
o.withRichSpeechAndBraille(
ChromeVoxState.instance.currentRange, null, evt.type)
.go();
}.bind(this));
}
};
......
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