Skip to content

Commit

Permalink
Expose Android accessibility focus to Blink accessibility internals
Browse files Browse the repository at this point in the history
Bug: 841218
TBR=michaelpg@chromium.org

Cq-Include-Trybots: master.tryserver.chromium.linux:closure_compilation
Change-Id: Iaea99a1a18c1eea7d1480b68b4a1aa71448d09d1
Reviewed-on: https://chromium-review.googlesource.com/1050498
Commit-Queue: Dominic Mazzoni <dmazzoni@chromium.org>
Reviewed-by: Mike West <mkwst@chromium.org>
Reviewed-by: David Tseng <dtseng@chromium.org>
Reviewed-by: Mounir Lamouri <mlamouri@chromium.org>
Cr-Commit-Position: refs/heads/master@{#559203}
  • Loading branch information
minorninth authored and Commit Bot committed May 16, 2018
1 parent c613fec commit af5f52b
Show file tree
Hide file tree
Showing 17 changed files with 134 additions and 23 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -442,6 +442,14 @@ void ArcAccessibilityHelperBridge::OnAction(
arc::mojom::AccessibilityActionType::CUSTOM_ACTION;
action_data->custom_action_id = data.custom_action_id;
break;
case ax::mojom::Action::kSetAccessibilityFocus:
action_data->action_type =
arc::mojom::AccessibilityActionType::ACCESSIBILITY_FOCUS;
break;
case ax::mojom::Action::kClearAccessibilityFocus:
action_data->action_type =
arc::mojom::AccessibilityActionType::CLEAR_ACCESSIBILITY_FOCUS;
break;
default:
return;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -345,6 +345,9 @@ AutomationInternalPerformActionFunction::ConvertToAXActionData(
case api::automation::ACTION_TYPE_BLUR:
action->action = ax::mojom::Action::kBlur;
break;
case api::automation::ACTION_TYPE_CLEARACCESSIBILITYFOCUS:
action->action = ax::mojom::Action::kClearAccessibilityFocus;
break;
case api::automation::ACTION_TYPE_DECREMENT:
action->action = ax::mojom::Action::kDecrement;
break;
Expand Down Expand Up @@ -383,6 +386,9 @@ AutomationInternalPerformActionFunction::ConvertToAXActionData(
case api::automation::ACTION_TYPE_LOADINLINETEXTBOXES:
action->action = ax::mojom::Action::kLoadInlineTextBoxes;
break;
case api::automation::ACTION_TYPE_SETACCESSIBILITYFOCUS:
action->action = ax::mojom::Action::kSetAccessibilityFocus;
break;
case api::automation::ACTION_TYPE_SCROLLTOMAKEVISIBLE:
action->action = ax::mojom::Action::kScrollToMakeVisible;
break;
Expand Down
2 changes: 2 additions & 0 deletions chrome/common/extensions/api/automation.idl
Original file line number Diff line number Diff line change
Expand Up @@ -278,6 +278,7 @@
// All possible actions that can be performed on automation nodes.
enum ActionType {
blur,
clearAccessibilityFocus,
customAction,
decrement,
doDefault,
Expand All @@ -295,6 +296,7 @@
scrollToMakeVisible,
scrollToPoint,
scrollUp,
setAccessibilityFocus,
setScrollOffset,
setSelection,
setSequentialFocusNavigationStartingPoint,
Expand Down
22 changes: 22 additions & 0 deletions content/browser/accessibility/browser_accessibility_manager.cc
Original file line number Diff line number Diff line change
Expand Up @@ -711,6 +711,28 @@ void BrowserAccessibilityManager::LoadInlineTextBoxes(
delegate_->AccessibilityPerformAction(action_data);
}

void BrowserAccessibilityManager::SetAccessibilityFocus(
const BrowserAccessibility& node) {
if (!delegate_)
return;

ui::AXActionData action_data;
action_data.action = ax::mojom::Action::kSetAccessibilityFocus;
action_data.target_node_id = node.GetId();
delegate_->AccessibilityPerformAction(action_data);
}

void BrowserAccessibilityManager::ClearAccessibilityFocus(
const BrowserAccessibility& node) {
if (!delegate_)
return;

ui::AXActionData action_data;
action_data.action = ax::mojom::Action::kClearAccessibilityFocus;
action_data.target_node_id = node.GetId();
delegate_->AccessibilityPerformAction(action_data);
}

void BrowserAccessibilityManager::HitTest(const gfx::Point& point) {
if (!delegate_)
return;
Expand Down
2 changes: 2 additions & 0 deletions content/browser/accessibility/browser_accessibility_manager.h
Original file line number Diff line number Diff line change
Expand Up @@ -203,6 +203,7 @@ class CONTENT_EXPORT BrowserAccessibilityManager : public ui::AXEventGenerator {
// by sending a message to the renderer to perform the respective action
// on the given node. See the definition of |ui::AXActionData| for more
// information about each of these actions.
void ClearAccessibilityFocus(const BrowserAccessibility& node);
void Decrement(const BrowserAccessibility& node);
void DoDefaultAction(const BrowserAccessibility& node);
void GetImageData(const BrowserAccessibility& node,
Expand All @@ -214,6 +215,7 @@ class CONTENT_EXPORT BrowserAccessibilityManager : public ui::AXEventGenerator {
const BrowserAccessibility& node, gfx::Rect subfocus);
void ScrollToPoint(
const BrowserAccessibility& node, gfx::Point point);
void SetAccessibilityFocus(const BrowserAccessibility& node);
void SetFocus(const BrowserAccessibility& node);
void SetScrollOffset(const BrowserAccessibility& node, gfx::Point offset);
void SetValue(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1043,15 +1043,25 @@ jboolean WebContentsAccessibilityAndroid::PreviousAtGranularity(
return false;
}

void WebContentsAccessibilityAndroid::SetAccessibilityFocus(
void WebContentsAccessibilityAndroid::MoveAccessibilityFocus(
JNIEnv* env,
const JavaParamRef<jobject>& obj,
jint unique_id) {
jint old_unique_id,
jint new_unique_id) {
BrowserAccessibilityAndroid* old_node = GetAXFromUniqueID(old_unique_id);
if (old_node)
old_node->manager()->ClearAccessibilityFocus(*old_node);

BrowserAccessibilityAndroid* node = GetAXFromUniqueID(new_unique_id);
if (!node)
return;
node->manager()->SetAccessibilityFocus(*node);

// When Android sets accessibility focus to a node, we load inline text
// boxes for that node so that subsequent requests for character bounding
// boxes will succeed.
BrowserAccessibilityAndroid* node = GetAXFromUniqueID(unique_id);
if (node)
// boxes will succeed. However, don't do that for the root of the tree,
// as that will result in loading inline text boxes for the whole tree.
if (node != node->manager()->GetRoot())
node->manager()->LoadInlineTextBoxes(*node);
}

Expand Down
18 changes: 12 additions & 6 deletions content/browser/accessibility/web_contents_accessibility_android.h
Original file line number Diff line number Diff line change
Expand Up @@ -151,12 +151,18 @@ class CONTENT_EXPORT WebContentsAccessibilityAndroid
jint id,
jint cursor_index);

// Set accessibility focus. This sends a message to the renderer to
// asynchronously load inline text boxes for this node only, enabling more
// accurate movement by granularities on this node.
void SetAccessibilityFocus(JNIEnv* env,
const base::android::JavaParamRef<jobject>& obj,
jint id);
// Move accessibility focus. This sends a message to the renderer to
// clear accessibility focus on the previous node and set accessibility
// focus on the current node. This isn't exposed to the open web, but used
// internally.
//
// In addition, when a node gets accessibility focus we asynchronously
// load inline text boxes for this node only, enabling more accurate
// movement by granularities on this node.
void MoveAccessibilityFocus(JNIEnv* env,
const base::android::JavaParamRef<jobject>& obj,
jint old_unique_id,
jint new_unique_id);

// Returns true if the object is a slider.
bool IsSlider(JNIEnv* env,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -433,6 +433,7 @@ public boolean performAction(int virtualViewId, int action, Bundle arguments) {
sendAccessibilityEvent(
virtualViewId, AccessibilityEvent.TYPE_VIEW_ACCESSIBILITY_FOCUS_CLEARED);
if (mAccessibilityFocusId == virtualViewId) {
nativeMoveAccessibilityFocus(mNativeObj, mAccessibilityFocusId, View.NO_ID);
mAccessibilityFocusId = View.NO_ID;
mAccessibilityFocusRect = null;
}
Expand Down Expand Up @@ -753,6 +754,8 @@ private boolean scrollBackward(int virtualViewId) {
private boolean moveAccessibilityFocusToId(int newAccessibilityFocusId) {
if (newAccessibilityFocusId == mAccessibilityFocusId) return false;

nativeMoveAccessibilityFocus(mNativeObj, mAccessibilityFocusId, newAccessibilityFocusId);

mAccessibilityFocusId = newAccessibilityFocusId;
mAccessibilityFocusRect = null;
// Used to store the node (edit text field) that has input focus but not a11y focus.
Expand All @@ -763,17 +766,8 @@ private boolean moveAccessibilityFocusToId(int newAccessibilityFocusId) {
mSelectionStartIndex = -1;
mSelectionEndIndex = nativeGetTextLength(mNativeObj, newAccessibilityFocusId);

// Calling nativeSetAccessibilityFocus will asynchronously load inline text boxes for
// this node and its subtree. If accessibility focus is on anything other than
// the root, do it - otherwise set it to -1 so we don't load inline text boxes
// for the whole subtree of the root.
if (mAccessibilityFocusId == mCurrentRootId) {
nativeSetAccessibilityFocus(mNativeObj, -1);
} else if (nativeIsAutofillPopupNode(mNativeObj, mAccessibilityFocusId)) {
if (nativeIsAutofillPopupNode(mNativeObj, mAccessibilityFocusId))
mAutofillPopupView.requestFocus();
} else {
nativeSetAccessibilityFocus(mNativeObj, mAccessibilityFocusId);
}

sendAccessibilityEvent(
mAccessibilityFocusId, AccessibilityEvent.TYPE_VIEW_ACCESSIBILITY_FOCUSED);
Expand Down Expand Up @@ -1499,8 +1493,8 @@ private native boolean nativePreviousAtGranularity(long nativeWebContentsAccessi
int selectionGranularity, boolean extendSelection, int id, int cursorIndex);
private native boolean nativeAdjustSlider(
long nativeWebContentsAccessibilityAndroid, int id, boolean increment);
private native void nativeSetAccessibilityFocus(
long nativeWebContentsAccessibilityAndroid, int id);
private native void nativeMoveAccessibilityFocus(
long nativeWebContentsAccessibilityAndroid, int oldId, int newId);
private native boolean nativeIsSlider(long nativeWebContentsAccessibilityAndroid, int id);
private native boolean nativeScroll(
long nativeWebContentsAccessibilityAndroid, int id, int direction);
Expand Down
6 changes: 6 additions & 0 deletions content/renderer/accessibility/render_accessibility_impl.cc
Original file line number Diff line number Diff line change
Expand Up @@ -566,6 +566,9 @@ void RenderAccessibilityImpl::OnPerformAction(
case ax::mojom::Action::kBlur:
root.Focus();
break;
case ax::mojom::Action::kClearAccessibilityFocus:
target.ClearAccessibilityFocus();
break;
case ax::mojom::Action::kDecrement:
target.Decrement();
break;
Expand Down Expand Up @@ -598,6 +601,9 @@ void RenderAccessibilityImpl::OnPerformAction(
case ax::mojom::Action::kFocus:
target.Focus();
break;
case ax::mojom::Action::kSetAccessibilityFocus:
target.SetAccessibilityFocus();
break;
case ax::mojom::Action::kSetScrollOffset:
target.SetScrollOffset(
WebPoint(data.target_point.x(), data.target_point.y()));
Expand Down
2 changes: 2 additions & 0 deletions third_party/blink/public/web/web_ax_object.h
Original file line number Diff line number Diff line change
Expand Up @@ -264,10 +264,12 @@ class WebAXObject {

// Actions. Return true if handled.
BLINK_EXPORT WebAXDefaultActionVerb Action() const;
BLINK_EXPORT bool ClearAccessibilityFocus() const;
BLINK_EXPORT bool Click() const;
BLINK_EXPORT bool Decrement() const;
BLINK_EXPORT bool Increment() const;
BLINK_EXPORT bool Focus() const;
BLINK_EXPORT bool SetAccessibilityFocus() const;
BLINK_EXPORT bool SetSelected(bool) const;
BLINK_EXPORT bool SetSelection(const WebAXObject& anchor_object,
int anchor_offset,
Expand Down
10 changes: 10 additions & 0 deletions third_party/blink/renderer/modules/accessibility/ax_object.cc
Original file line number Diff line number Diff line change
Expand Up @@ -2513,6 +2513,16 @@ bool AXObject::RequestShowContextMenuAction() {
return OnNativeShowContextMenuAction();
}

bool AXObject::InternalClearAccessibilityFocusAction() {
// TODO(mlamouri): implement
return false;
}

bool AXObject::InternalSetAccessibilityFocusAction() {
// TODO(mlamouri): implement
return false;
}

bool AXObject::OnNativeScrollToMakeVisibleAction() const {
Node* node = GetNode();
LayoutObject* layout_object = node ? node->GetLayoutObject() : nullptr;
Expand Down
7 changes: 7 additions & 0 deletions third_party/blink/renderer/modules/accessibility/ax_object.h
Original file line number Diff line number Diff line change
Expand Up @@ -734,6 +734,13 @@ class MODULES_EXPORT AXObject : public GarbageCollectedFinalized<AXObject> {
bool RequestSetValueAction(const String&);
bool RequestShowContextMenuAction();

// These are actions, just like the actions above, and they allow us
// to keep track of nodes that gain or lose accessibility focus, but
// this isn't exposed to the open web so they're explicitly marked as
// internal so it's clear that these should not dispatch DOM events.
virtual bool InternalClearAccessibilityFocusAction();
virtual bool InternalSetAccessibilityFocusAction();

// Native implementations of actions that aren't handled by AOM
// event listeners. These all return true if handled.
virtual bool OnNativeDecrementAction();
Expand Down
14 changes: 14 additions & 0 deletions third_party/blink/renderer/modules/exported/web_ax_object.cc
Original file line number Diff line number Diff line change
Expand Up @@ -684,6 +684,13 @@ WebString WebAXObject::Language() const {
return private_->Language();
}

bool WebAXObject::ClearAccessibilityFocus() const {
if (IsDetached())
return false;

return private_->InternalClearAccessibilityFocusAction();
}

bool WebAXObject::Click() const {
if (IsDetached())
return false;
Expand Down Expand Up @@ -766,6 +773,13 @@ void WebAXObject::Selection(WebAXObject& anchor_object,
return;
}

bool WebAXObject::SetAccessibilityFocus() const {
if (IsDetached())
return false;

return private_->InternalSetAccessibilityFocusAction();
}

bool WebAXObject::SetSelected(bool selected) const {
if (IsDetached())
return false;
Expand Down
2 changes: 2 additions & 0 deletions third_party/closure_compiler/externs/automation.js
Original file line number Diff line number Diff line change
Expand Up @@ -285,6 +285,7 @@ chrome.automation.StateType = {
*/
chrome.automation.ActionType = {
BLUR: 'blur',
CLEAR_ACCESSIBILITY_FOCUS: 'clearAccessibilityFocus',
CUSTOM_ACTION: 'customAction',
DECREMENT: 'decrement',
DO_DEFAULT: 'doDefault',
Expand All @@ -302,6 +303,7 @@ chrome.automation.ActionType = {
SCROLL_TO_MAKE_VISIBLE: 'scrollToMakeVisible',
SCROLL_TO_POINT: 'scrollToPoint',
SCROLL_UP: 'scrollUp',
SET_ACCESSIBILITY_FOCUS: 'setAccessibilityFocus',
SET_SCROLL_OFFSET: 'setScrollOffset',
SET_SELECTION: 'setSelection',
SET_SEQUENTIAL_FOCUS_NAVIGATION_STARTING_POINT: 'setSequentialFocusNavigationStartingPoint',
Expand Down
8 changes: 8 additions & 0 deletions ui/accessibility/ax_enum_util.cc
Original file line number Diff line number Diff line change
Expand Up @@ -927,6 +927,8 @@ const char* ToString(ax::mojom::Action action) {
return "none";
case ax::mojom::Action::kBlur:
return "blur";
case ax::mojom::Action::kClearAccessibilityFocus:
return "clearAccessibilityFocus";
case ax::mojom::Action::kCustomAction:
return "customAction";
case ax::mojom::Action::kDecrement:
Expand Down Expand Up @@ -961,6 +963,8 @@ const char* ToString(ax::mojom::Action action) {
return "scrollToMakeVisible";
case ax::mojom::Action::kScrollToPoint:
return "scrollToPoint";
case ax::mojom::Action::kSetAccessibilityFocus:
return "setAccessibilityFocus";
case ax::mojom::Action::kSetScrollOffset:
return "setScrollOffset";
case ax::mojom::Action::kSetSelection:
Expand All @@ -981,6 +985,8 @@ ax::mojom::Action ParseAction(const char* action) {
return ax::mojom::Action::kNone;
if (0 == strcmp(action, "blur"))
return ax::mojom::Action::kBlur;
if (0 == strcmp(action, "clearAccessibilityFocus"))
return ax::mojom::Action::kClearAccessibilityFocus;
if (0 == strcmp(action, "customAction"))
return ax::mojom::Action::kCustomAction;
if (0 == strcmp(action, "decrement"))
Expand Down Expand Up @@ -1015,6 +1021,8 @@ ax::mojom::Action ParseAction(const char* action) {
return ax::mojom::Action::kScrollToMakeVisible;
if (0 == strcmp(action, "scrollToPoint"))
return ax::mojom::Action::kScrollToPoint;
if (0 == strcmp(action, "setAccessibilityFocus"))
return ax::mojom::Action::kSetAccessibilityFocus;
if (0 == strcmp(action, "setScrollOffset"))
return ax::mojom::Action::kSetScrollOffset;
if (0 == strcmp(action, "setSelection"))
Expand Down
10 changes: 10 additions & 0 deletions ui/accessibility/ax_enums.mojom
Original file line number Diff line number Diff line change
Expand Up @@ -305,6 +305,11 @@ enum Action {
kNone,
kBlur,

// Notifies a node that it no longer has accessibility focus.
// Currently used only on Android and only internally, it's not
// exposed to the open web. See kSetAccessibilityFocus, below.
kClearAccessibilityFocus,

kCustomAction,

// Decrement a slider or range control by one step value.
Expand Down Expand Up @@ -352,6 +357,11 @@ enum Action {
// global screen coordinates. Pass a point in AXActionData.target_point.
kScrollToPoint,

// Notifies a node that it has accessibility focus.
// Currently used only on Android and only internally, it's not
// exposed to the open web. See kClearAccessibilityFocus, above.
kSetAccessibilityFocus,

kSetScrollOffset,
kSetSelection,

Expand Down
2 changes: 2 additions & 0 deletions ui/accessibility/ax_node_data.cc
Original file line number Diff line number Diff line change
Expand Up @@ -537,6 +537,7 @@ void AXNodeData::AddAction(ax::mojom::Action action_enum) {
: ax::mojom::Action::kBlur;
DCHECK(HasAction(excluded_action));
} break;
case ax::mojom::Action::kClearAccessibilityFocus:
case ax::mojom::Action::kCustomAction:
case ax::mojom::Action::kDecrement:
case ax::mojom::Action::kDoDefault:
Expand All @@ -547,6 +548,7 @@ void AXNodeData::AddAction(ax::mojom::Action action_enum) {
case ax::mojom::Action::kReplaceSelectedText:
case ax::mojom::Action::kScrollToMakeVisible:
case ax::mojom::Action::kScrollToPoint:
case ax::mojom::Action::kSetAccessibilityFocus:
case ax::mojom::Action::kSetScrollOffset:
case ax::mojom::Action::kSetSelection:
case ax::mojom::Action::kSetSequentialFocusNavigationStartingPoint:
Expand Down

0 comments on commit af5f52b

Please sign in to comment.