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 @@ ...@@ -23,6 +23,7 @@
#include "content/public/browser/notification_details.h" #include "content/public/browser/notification_details.h"
#include "content/public/browser/notification_service.h" #include "content/public/browser/notification_service.h"
#include "content/public/browser/notification_source.h" #include "content/public/browser/notification_source.h"
#include "ui/accessibility/accessibility_switches.h"
#include "ui/accessibility/ax_role_properties.h" #include "ui/accessibility/ax_role_properties.h"
#include "ui/views/accessibility/ax_event_manager.h" #include "ui/views/accessibility/ax_event_manager.h"
#include "ui/views/accessibility/view_accessibility.h" #include "ui/views/accessibility/view_accessibility.h"
...@@ -342,6 +343,10 @@ void MagnificationManager::HandleFocusChangedInPage( ...@@ -342,6 +343,10 @@ void MagnificationManager::HandleFocusChangedInPage(
void MagnificationManager::HandleFocusChanged(const gfx::Rect& bounds_in_screen, void MagnificationManager::HandleFocusChanged(const gfx::Rect& bounds_in_screen,
bool is_editable) { bool is_editable) {
if (::switches::
IsExperimentalAccessibilityMagnifierNewFocusFollowingEnabled())
return;
if (bounds_in_screen.IsEmpty()) if (bounds_in_screen.IsEmpty())
return; return;
......
...@@ -8,6 +8,9 @@ ...@@ -8,6 +8,9 @@
class Magnifier { class Magnifier {
constructor() { constructor() {
/** @private {!EventHandler} */ /** @private {!EventHandler} */
this.focusHandler_ = new EventHandler(
[], chrome.automation.EventType.FOCUS, this.onFocus_.bind(this));
this.activeDescendantHandler_ = new EventHandler( this.activeDescendantHandler_ = new EventHandler(
[], chrome.automation.EventType.ACTIVE_DESCENDANT_CHANGED, [], chrome.automation.EventType.ACTIVE_DESCENDANT_CHANGED,
this.onActiveDescendantChanged_.bind(this)); this.onActiveDescendantChanged_.bind(this));
...@@ -17,6 +20,7 @@ class Magnifier { ...@@ -17,6 +20,7 @@ class Magnifier {
/** Destructor to remove listener. */ /** Destructor to remove listener. */
onMagnifierDisabled() { onMagnifierDisabled() {
this.focusHandler_.stop();
this.activeDescendantHandler_.stop(); this.activeDescendantHandler_.stop();
} }
...@@ -26,11 +30,36 @@ class Magnifier { ...@@ -26,11 +30,36 @@ class Magnifier {
*/ */
init_() { init_() {
chrome.automation.getDesktop(desktop => { chrome.automation.getDesktop(desktop => {
this.focusHandler_.setNodes(desktop);
this.focusHandler_.start();
this.activeDescendantHandler_.setNodes(desktop); this.activeDescendantHandler_.setNodes(desktop);
this.activeDescendantHandler_.start(); 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 * Listener for when active descendant is changed. Moves magnifier to include
* active descendant in viewport. * active descendant in viewport.
......
...@@ -16,11 +16,18 @@ MagnifierE2ETest = class extends E2ETestBase { ...@@ -16,11 +16,18 @@ MagnifierE2ETest = class extends E2ETestBase {
chrome.accessibilityPrivate = this.mockAccessibilityPrivate; chrome.accessibilityPrivate = this.mockAccessibilityPrivate;
window.RoleType = chrome.automation.RoleType; window.RoleType = chrome.automation.RoleType;
window.getNextMagnifierLocation = this.getNextMagnifierLocation;
// Re-initialize AccessibilityCommon with mock AccessibilityPrivate API. // Re-initialize AccessibilityCommon with mock AccessibilityPrivate API.
window.accessibilityCommon = new AccessibilityCommon(); window.accessibilityCommon = new AccessibilityCommon();
} }
async getNextMagnifierLocation() {
return new Promise(resolve => {
chrome.accessibilityPrivate.registerMoveMagnifierToRectCallback(resolve);
});
}
/** @override */ /** @override */
testGenCppIncludes() { testGenCppIncludes() {
super.testGenCppIncludes(); super.testGenCppIncludes();
...@@ -44,6 +51,27 @@ MagnifierE2ETest = class extends E2ETestBase { ...@@ -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( TEST_F(
'MagnifierE2ETest', 'MovesScreenMagnifierToActiveDescendant', function() { 'MagnifierE2ETest', 'MovesScreenMagnifierToActiveDescendant', function() {
const site = ` const site = `
...@@ -64,14 +92,11 @@ TEST_F( ...@@ -64,14 +92,11 @@ TEST_F(
parent.doDefault(); parent.doDefault();
// Register and wait for rect from magnifier. // Register and wait for rect from magnifier.
const rect = await new Promise(resolve => { const rect = await getNextMagnifierLocation();
this.mockAccessibilityPrivate.registerMoveMagnifierToRectCallback(
resolve);
});
// Validate rect from magnifier is rect of banana. // Validate rect from magnifier is rect of banana.
const bananaNode = const bananaNode =
root.find({role: RoleType.TREE_ITEM, attributes: {name: 'Banana'}}); root.find({role: RoleType.TREE_ITEM, attributes: {name: 'Banana'}});
assertTrue(RectUtil.equal(rect, bananaNode.location)); assertTrue(RectUtil.equal(rect, bananaNode.location));
}, {returnPage: true}); });
}); });
...@@ -57,7 +57,9 @@ var MockAccessibilityPrivate = { ...@@ -57,7 +57,9 @@ var MockAccessibilityPrivate = {
* @param {!chrome.accessibilityPrivate.ScreenRect} rect * @param {!chrome.accessibilityPrivate.ScreenRect} rect
*/ */
moveMagnifierToRect: (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