Commit 9b2d35e2 authored by Josiah K's avatar Josiah K Committed by Commit Bot

Add new Javascript-based Magnifier focus following

Focus following should work in more cases than handled previously, since we're now listening for all high-level chrome.automation focus updates, instead of low-level views::View AXEvent and related focus updates.

AX-Relnotes: New Javascript-based magnifier focus following behind flag - focus following should work in more cases than handled previously.
Bug: 1131153
Change-Id: I1e34255000a320df942462690c6a7ea1b6d0612e
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2466622
Commit-Queue: Josiah Krutz <josiahk@google.com>
Reviewed-by: default avatarDavid Tseng <dtseng@chromium.org>
Cr-Commit-Position: refs/heads/master@{#818175}
parent 994866f4
......@@ -23,6 +23,7 @@
#include "content/public/browser/notification_details.h"
#include "content/public/browser/notification_service.h"
#include "content/public/browser/notification_source.h"
#include "ui/accessibility/accessibility_switches.h"
#include "ui/accessibility/ax_role_properties.h"
#include "ui/views/accessibility/ax_event_manager.h"
#include "ui/views/accessibility/view_accessibility.h"
......@@ -342,6 +343,10 @@ void MagnificationManager::HandleFocusChangedInPage(
void MagnificationManager::HandleFocusChanged(const gfx::Rect& bounds_in_screen,
bool is_editable) {
if (::switches::
IsExperimentalAccessibilityMagnifierNewFocusFollowingEnabled())
return;
if (bounds_in_screen.IsEmpty())
return;
......
......@@ -8,6 +8,9 @@
class Magnifier {
constructor() {
/** @private {!EventHandler} */
this.focusHandler_ = new EventHandler(
[], chrome.automation.EventType.FOCUS, this.onFocus_.bind(this));
this.activeDescendantHandler_ = new EventHandler(
[], chrome.automation.EventType.ACTIVE_DESCENDANT_CHANGED,
this.onActiveDescendantChanged_.bind(this));
......@@ -17,6 +20,7 @@ class Magnifier {
/** Destructor to remove listener. */
onMagnifierDisabled() {
this.focusHandler_.stop();
this.activeDescendantHandler_.stop();
}
......@@ -26,11 +30,36 @@ class Magnifier {
*/
init_() {
chrome.automation.getDesktop(desktop => {
this.focusHandler_.setNodes(desktop);
this.focusHandler_.start();
this.activeDescendantHandler_.setNodes(desktop);
this.activeDescendantHandler_.start();
});
}
/**
* Listener for when focus is updated. Moves magnifier to include focused
* element in viewport.
*
* TODO(accessibility): There is a bit of magnifier shakiness on arrow down in
* omnibox - probably focus following fighting with caret following - maybe
* add timer for last focus event so that fast-following caret updates don't
* shake screen.
* TODO(accessibility): On page load, sometimes viewport moves to center of
* webpage instead of spotlighting first focusable page element.
*
* @param {!chrome.automation.AutomationEvent} event
* @private
*/
onFocus_(event) {
const {location} = event.target;
if (!location) {
return;
}
chrome.accessibilityPrivate.moveMagnifierToRect(location);
}
/**
* Listener for when active descendant is changed. Moves magnifier to include
* active descendant in viewport.
......
......@@ -16,11 +16,18 @@ MagnifierE2ETest = class extends E2ETestBase {
chrome.accessibilityPrivate = this.mockAccessibilityPrivate;
window.RoleType = chrome.automation.RoleType;
window.getNextMagnifierLocation = this.getNextMagnifierLocation;
// Re-initialize AccessibilityCommon with mock AccessibilityPrivate API.
window.accessibilityCommon = new AccessibilityCommon();
}
async getNextMagnifierLocation() {
return new Promise(resolve => {
chrome.accessibilityPrivate.registerMoveMagnifierToRectCallback(resolve);
});
}
/** @override */
testGenCppIncludes() {
super.testGenCppIncludes();
......@@ -44,6 +51,27 @@ MagnifierE2ETest = class extends E2ETestBase {
}
};
TEST_F('MagnifierE2ETest', 'MovesScreenMagnifierToFocusedElement', function() {
const site = `
<button id="apple">Apple</button>
<button id="banana">Banana</button>
`;
this.runWithLoadedTree(site, async function(root) {
// Validate magnifier wants to move to root.
const rootLocation = await getNextMagnifierLocation();
assertTrue(RectUtil.equal(rootLocation, root.location));
// Focus banana node.
const banana =
root.find({role: RoleType.BUTTON, attributes: {name: 'Banana'}});
banana.focus();
// Validate magnifier wants to move to banana.
const bananaLocation = await getNextMagnifierLocation();
assertTrue(RectUtil.equal(bananaLocation, banana.location));
});
});
TEST_F(
'MagnifierE2ETest', 'MovesScreenMagnifierToActiveDescendant', function() {
const site = `
......@@ -64,14 +92,11 @@ TEST_F(
parent.doDefault();
// Register and wait for rect from magnifier.
const rect = await new Promise(resolve => {
this.mockAccessibilityPrivate.registerMoveMagnifierToRectCallback(
resolve);
});
const rect = await getNextMagnifierLocation();
// Validate rect from magnifier is rect of banana.
const bananaNode =
root.find({role: RoleType.TREE_ITEM, attributes: {name: 'Banana'}});
assertTrue(RectUtil.equal(rect, bananaNode.location));
}, {returnPage: true});
});
});
......@@ -57,7 +57,9 @@ var MockAccessibilityPrivate = {
* @param {!chrome.accessibilityPrivate.ScreenRect} rect
*/
moveMagnifierToRect: (rect) => {
MockAccessibilityPrivate.moveMagnifierToRectCallback_(rect);
if (MockAccessibilityPrivate.moveMagnifierToRectCallback_) {
MockAccessibilityPrivate.moveMagnifierToRectCallback_(rect);
}
},
/**
......
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