Bug 1535701 - Focus GeckoView when interacting with TalkBack. r=geckoview-reviewers,snorp

When TalkBack receives a focus event, it redirects the accessibility focus (the green cursor) to the focused element. This is an important driver for the screen reader experience.

Since the focus mode of the GeckoView is "focusable in touch", the focused state of the view is very arbitrary when using TalkBack since the user never directly touches the view. The only way for the view to regain focus is if a control or link in the content is interacted with.

TalkBack user, who is explicitly interacting with the webview/geckoview would expect it to have focus, and to have the accessibility focus redirected in the page in the case of script-driven focus events.

Differential Revision: https://phabricator.services.mozilla.com/D23747

--HG--
extra : moz-landing-system : lando
This commit is contained in:
Eitan Isaacson 2019-03-20 16:48:40 +00:00
parent 2b6876f959
commit 65c8912aaf

View File

@ -178,6 +178,7 @@ public class SessionAccessibility {
return true;
case AccessibilityNodeInfo.ACTION_NEXT_HTML_ELEMENT:
case AccessibilityNodeInfo.ACTION_PREVIOUS_HTML_ELEMENT:
requestViewFocus();
if (arguments != null) {
data = new GeckoBundle(1);
data.putString("rule", arguments.getString(AccessibilityNodeInfo.ACTION_ARGUMENT_HTML_ELEMENT_STRING));
@ -491,6 +492,7 @@ public class SessionAccessibility {
final SparseArray<GeckoBundle> mFocusPathCache = new SparseArray<>();
// List of caches in descending order from last updated.
LinkedList<SparseArray<GeckoBundle>> mCaches = new LinkedList<>();
private boolean mViewFocusRequested = false;
/* package */ SessionAccessibility(final GeckoSession session) {
mSession = session;
@ -540,9 +542,30 @@ public class SessionAccessibility {
}
return mProvider;
}
@Override
public void sendAccessibilityEvent(View host, int eventType) {
if (eventType == AccessibilityEvent.TYPE_VIEW_FOCUSED) {
// We rely on the focus events sent from Gecko.
return;
}
super.sendAccessibilityEvent(host, eventType);
}
});
}
private boolean isInTest() {
return Build.VERSION.SDK_INT >= 17 && mView != null && mView.getDisplay() == null;
}
private void requestViewFocus() {
if (!mView.isFocused() && !isInTest()) {
mViewFocusRequested = true;
mView.requestFocus();
}
}
private static class Settings {
private static final String FORCE_ACCESSIBILITY_PREF = "accessibility.force_disabled";
@ -635,7 +658,7 @@ public class SessionAccessibility {
return false;
}
mView.requestFocus();
requestViewFocus();
final GeckoBundle data = new GeckoBundle(2);
data.putDoubleArray("coordinates", new double[] {event.getRawX(), event.getRawY()});
@ -649,7 +672,14 @@ public class SessionAccessibility {
return;
}
if (!Settings.isPlatformEnabled() && (Build.VERSION.SDK_INT < 17 || mView.getDisplay() != null)) {
if (mViewFocusRequested && className == CLASSNAME_WEBVIEW) {
// If the view was focused from an accessiblity action or
// explore-by-touch, we supress this focus event to avoid noise.
mViewFocusRequested = false;
return;
}
if (!Settings.isPlatformEnabled() && !isInTest()) {
// Accessibility could be activated in Gecko via xpcom, for example when using a11y
// devtools. Here we assure that either Android a11y is *really* enabled, or no
// display is attached and we must be in a junit test.
@ -721,6 +751,10 @@ public class SessionAccessibility {
break;
case AccessibilityEvent.TYPE_VIEW_FOCUSED:
mFocusedNode = sourceId;
if (!mView.isFocused() && !isInTest()) {
// Don't dispatch a focus event if the parent view is not focused
return;
}
break;
}