Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(196)

Side by Side Diff: ui/views/accessibility/native_view_accessibility_base.cc

Issue 2119413004: a11y: Exclude children of nested keyboard accessible controls from a11y tree. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Rebase again. Created 3 years, 8 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
1 // Copyright (c) 2013 The Chromium Authors. All rights reserved. 1 // Copyright (c) 2013 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 #include "ui/views/accessibility/native_view_accessibility_base.h" 5 #include "ui/views/accessibility/native_view_accessibility_base.h"
6 6
7 #include "base/memory/ptr_util.h" 7 #include "base/memory/ptr_util.h"
8 #include "base/strings/utf_string_conversions.h" 8 #include "base/strings/utf_string_conversions.h"
9 #include "ui/events/event_utils.h" 9 #include "ui/events/event_utils.h"
10 #include "ui/gfx/native_widget_types.h" 10 #include "ui/gfx/native_widget_types.h"
11 #include "ui/views/controls/native/native_view_host.h" 11 #include "ui/views/controls/native/native_view_host.h"
12 #include "ui/views/view.h" 12 #include "ui/views/view.h"
13 #include "ui/views/widget/widget.h" 13 #include "ui/views/widget/widget.h"
14 14
15 namespace views { 15 namespace views {
16 16
17 namespace {
18
19 bool IsAccessibilityFocusableWhenEnabled(View* view) {
20 return view->focus_behavior() != View::FocusBehavior::NEVER &&
21 view->IsDrawn();
22 }
23
24 // Used to determine if a View should be ignored by accessibility clients by
25 // being a non-keyboard-focusable child of a keyboard-focusable ancestor.
tapted 2017/04/03 05:50:36 An example might be good here in the comment to cl
Patti Lor 2017/04/04 02:46:28 Done.
26 bool IsViewUnfocusableChildOfFocusableAncestor(View* view) {
27 if (IsAccessibilityFocusableWhenEnabled(view))
28 return false;
29
30 while (view->parent()) {
31 view = view->parent();
32 if (IsAccessibilityFocusableWhenEnabled(view))
33 return true;
34 }
35 return false;
36 }
37
38 // Recursively gets a list of unignored gfx::NativeViewAccessible children of a
39 // given View. If a child is ignored, its children will be used instead.
40 std::vector<gfx::NativeViewAccessible> GetUnignoredA11yChildren(View* view) {
41 std::vector<gfx::NativeViewAccessible> children;
42 for (int i = 0; i < view->child_count(); ++i) {
43 View* child = view->child_at(i);
44 if (NativeViewAccessibilityBase::Ignored(child)) {
45 std::vector<gfx::NativeViewAccessible> grandchildren =
46 GetUnignoredA11yChildren(child);
47 children.insert(children.end(), grandchildren.begin(),
48 grandchildren.end());
49 } else {
50 children.push_back(child->GetNativeViewAccessible());
51 }
52 }
53 return children;
54 }
55
56 } // namespace
57
58 // static
59 bool NativeViewAccessibilityBase::Ignored(View* view) {
60 return GetForView(view)->GetData().role == ui::AX_ROLE_IGNORED;
61 }
62
17 NativeViewAccessibilityBase::NativeViewAccessibilityBase(View* view) 63 NativeViewAccessibilityBase::NativeViewAccessibilityBase(View* view)
18 : view_(view), 64 : view_(view),
19 parent_widget_(nullptr), 65 parent_widget_(nullptr),
20 ax_node_(ui::AXPlatformNode::Create(this)) { 66 ax_node_(ui::AXPlatformNode::Create(this)) {
21 DCHECK(ax_node_); 67 DCHECK(ax_node_);
22 } 68 }
23 69
24 NativeViewAccessibilityBase::~NativeViewAccessibilityBase() { 70 NativeViewAccessibilityBase::~NativeViewAccessibilityBase() {
25 ax_node_->Destroy(); 71 ax_node_->Destroy();
26 if (parent_widget_) 72 if (parent_widget_)
(...skipping 43 matching lines...) Expand 10 before | Expand all | Expand 10 after
70 116
71 if (view_->IsAccessibilityFocusable()) 117 if (view_->IsAccessibilityFocusable())
72 data_.state |= (1 << ui::AX_STATE_FOCUSABLE); 118 data_.state |= (1 << ui::AX_STATE_FOCUSABLE);
73 119
74 if (!view_->enabled()) 120 if (!view_->enabled())
75 data_.state |= (1 << ui::AX_STATE_DISABLED); 121 data_.state |= (1 << ui::AX_STATE_DISABLED);
76 122
77 if (!view_->IsDrawn()) 123 if (!view_->IsDrawn())
78 data_.state |= (1 << ui::AX_STATE_INVISIBLE); 124 data_.state |= (1 << ui::AX_STATE_INVISIBLE);
79 125
126 // Make sure this element is excluded from the a11y tree if there's a
127 // focusable parent. All keyboard focusable elements should be leaf nodes.
128 // Exceptions to this rule will themselves be accessibility focusable.
129 if (IsViewUnfocusableChildOfFocusableAncestor(view_))
130 data_.role = ui::AX_ROLE_IGNORED;
80 return data_; 131 return data_;
81 } 132 }
82 133
83 int NativeViewAccessibilityBase::GetChildCount() { 134 int NativeViewAccessibilityBase::GetChildCount() {
84 int child_count = view_->child_count(); 135 int child_count = GetUnignoredA11yChildren(view_).size();
85 136
86 std::vector<Widget*> child_widgets; 137 std::vector<Widget*> child_widgets;
87 PopulateChildWidgetVector(&child_widgets); 138 PopulateChildWidgetVector(&child_widgets);
88 child_count += child_widgets.size(); 139 child_count += child_widgets.size();
89 140
90 return child_count; 141 return child_count;
91 } 142 }
92 143
93 gfx::NativeViewAccessible NativeViewAccessibilityBase::ChildAtIndex(int index) { 144 gfx::NativeViewAccessible NativeViewAccessibilityBase::ChildAtIndex(int index) {
94 // If this is a root view, our widget might have child widgets. Include 145 std::vector<gfx::NativeViewAccessible> a11y_children =
146 GetUnignoredA11yChildren(view_);
147 // Include the child widgets that may be present if this is a RootView.
95 std::vector<Widget*> child_widgets; 148 std::vector<Widget*> child_widgets;
96 PopulateChildWidgetVector(&child_widgets); 149 PopulateChildWidgetVector(&child_widgets);
97 int child_widget_count = static_cast<int>(child_widgets.size());
98 150
99 if (index < view_->child_count()) { 151 if (index >= static_cast<int>(a11y_children.size() + child_widgets.size()))
100 return view_->child_at(index)->GetNativeViewAccessible(); 152 return nullptr;
101 } else if (index < view_->child_count() + child_widget_count) { 153 if (index < static_cast<int>(a11y_children.size()))
102 Widget* child_widget = child_widgets[index - view_->child_count()]; 154 return a11y_children[index];
103 return child_widget->GetRootView()->GetNativeViewAccessible();
104 }
105 155
106 return nullptr; 156 Widget* child_widget = child_widgets[index - a11y_children.size()];
157 return child_widget->GetRootView()->GetNativeViewAccessible();
107 } 158 }
108 159
109 gfx::NativeWindow NativeViewAccessibilityBase::GetTopLevelWidget() { 160 gfx::NativeWindow NativeViewAccessibilityBase::GetTopLevelWidget() {
110 if (view_->GetWidget()) 161 if (view_->GetWidget())
111 return view_->GetWidget()->GetTopLevelWidget()->GetNativeWindow(); 162 return view_->GetWidget()->GetTopLevelWidget()->GetNativeWindow();
112 return nullptr; 163 return nullptr;
113 } 164 }
114 165
115 gfx::NativeViewAccessible NativeViewAccessibilityBase::GetParent() { 166 gfx::NativeViewAccessible NativeViewAccessibilityBase::GetParent() {
116 if (view_->parent()) 167 View* parent_view = view_->parent();
117 return view_->parent()->GetNativeViewAccessible(); 168 if (parent_view) {
118 169 if (Ignored(parent_view))
119 if (parent_widget_) 170 return GetForView(parent_view)->GetParent();
tapted 2017/04/03 05:50:36 (assuming it works..) things might be simpler if w
Patti Lor 2017/04/04 02:46:28 Oh, that's way better. It works, thanks!
120 return parent_widget_->GetRootView()->GetNativeViewAccessible(); 171 return parent_view->GetNativeViewAccessible();
121 172 }
122 return nullptr; 173 return GetNativeViewAccessibleForWidget();
123 } 174 }
124 175
125 gfx::Vector2d NativeViewAccessibilityBase::GetGlobalCoordinateOffset() { 176 gfx::Vector2d NativeViewAccessibilityBase::GetGlobalCoordinateOffset() {
126 return gfx::Vector2d(0, 0); // location is already in screen coordinates. 177 return gfx::Vector2d(0, 0); // Location is already in screen coordinates.
127 } 178 }
128 179
129 gfx::NativeViewAccessible NativeViewAccessibilityBase::HitTestSync(int x, 180 gfx::NativeViewAccessible NativeViewAccessibilityBase::HitTestSync(int x,
130 int y) { 181 int y) {
131 if (!view_ || !view_->GetWidget()) 182 if (!view_ || !view_->GetWidget())
132 return nullptr; 183 return nullptr;
133 184
134 // Search child widgets first, since they're on top in the z-order. 185 // Search child widgets first, since they're on top in the z-order.
135 std::vector<Widget*> child_widgets; 186 std::vector<Widget*> child_widgets;
136 PopulateChildWidgetVector(&child_widgets); 187 PopulateChildWidgetVector(&child_widgets);
137 for (Widget* child_widget : child_widgets) { 188 for (Widget* child_widget : child_widgets) {
138 View* child_root_view = child_widget->GetRootView(); 189 View* child_root_view = child_widget->GetRootView();
139 gfx::Point point(x, y); 190 gfx::Point point(x, y);
140 View::ConvertPointFromScreen(child_root_view, &point); 191 View::ConvertPointFromScreen(child_root_view, &point);
141 if (child_root_view->HitTestPoint(point)) 192 if (child_root_view->HitTestPoint(point))
142 return child_root_view->GetNativeViewAccessible(); 193 return child_root_view->GetNativeViewAccessible();
143 } 194 }
144 195
196 // Check if the given point is inside |view_|.
145 gfx::Point point(x, y); 197 gfx::Point point(x, y);
146 View::ConvertPointFromScreen(view_, &point); 198 View::ConvertPointFromScreen(view_, &point);
147 if (!view_->HitTestPoint(point)) 199 if (!view_->HitTestPoint(point))
148 return nullptr; 200 return nullptr;
149 201
150 // Check if the point is within any of the immediate children of this 202 // Early return if this node is a leaf, depending on whether it's ignored.
151 // view. We don't have to search further because AXPlatformNode will 203 if (GetChildCount() == 0)
152 // do a recursive hit test if we return anything other than |this| or NULL. 204 return Ignored(view_) ? nullptr : GetNativeObject();
205
206 // Check if the point is within any of the immediate children of this view. We
207 // don't have to search further because AXPlatformNodeWin will do a recursive
208 // hit test if we return anything other than GetNativeObject() or nullptr.
209 // The exception to this is if the recursion needs to travel up the tree,
210 // which may return a non-direct ancestor of the given node.
153 for (int i = view_->child_count() - 1; i >= 0; --i) { 211 for (int i = view_->child_count() - 1; i >= 0; --i) {
154 View* child_view = view_->child_at(i); 212 View* child_view = view_->child_at(i);
155 if (!child_view->visible()) 213 NativeViewAccessibilityBase* child_nva = GetForView(child_view);
156 continue;
157 214
158 gfx::Point point_in_child_coords(point); 215 gfx::Point point_in_child_coords(point);
159 view_->ConvertPointToTarget(view_, child_view, &point_in_child_coords); 216 view_->ConvertPointToTarget(view_, child_view, &point_in_child_coords);
160 if (child_view->HitTestPoint(point_in_child_coords)) 217 if (!child_nva->GetData().HasStateFlag(ui::AX_STATE_INVISIBLE) &&
161 return child_view->GetNativeViewAccessible(); 218 child_view->HitTestPoint(point_in_child_coords)) {
219 if (child_nva->GetChildCount() == 0 && Ignored(child_view)) {
220 // Multiple levels might be jumped here.
221 return child_nva->GetParent();
tapted 2017/04/03 05:50:36 Can this get stuck in a loop? The comment above s
Patti Lor 2017/04/04 02:46:28 I think this is covered in the tests - if you look
tapted 2017/04/04 08:13:13 Acknowledged.
222 }
223 return child_nva->GetNativeObject();
224 }
162 } 225 }
163 226
164 // If it's not inside any of our children, it's inside this view. 227 // If it's not inside any of our children, it's inside this view.
165 return GetNativeObject(); 228 return GetNativeObject();
166 } 229 }
167 230
168 gfx::NativeViewAccessible NativeViewAccessibilityBase::GetFocus() { 231 gfx::NativeViewAccessible NativeViewAccessibilityBase::GetFocus() {
169 FocusManager* focus_manager = view_->GetFocusManager(); 232 FocusManager* focus_manager = view_->GetFocusManager();
170 View* focused_view = 233 View* focused_view =
171 focus_manager ? focus_manager->GetFocusedView() : nullptr; 234 focus_manager ? focus_manager->GetFocusedView() : nullptr;
(...skipping 46 matching lines...) Expand 10 before | Expand all | Expand 10 after
218 } 281 }
219 } 282 }
220 283
221 void NativeViewAccessibilityBase::SetParentWidget(Widget* parent_widget) { 284 void NativeViewAccessibilityBase::SetParentWidget(Widget* parent_widget) {
222 if (parent_widget_) 285 if (parent_widget_)
223 parent_widget_->RemoveObserver(this); 286 parent_widget_->RemoveObserver(this);
224 parent_widget_ = parent_widget; 287 parent_widget_ = parent_widget;
225 parent_widget_->AddObserver(this); 288 parent_widget_->AddObserver(this);
226 } 289 }
227 290
291 // static
292 NativeViewAccessibilityBase* NativeViewAccessibilityBase::GetForView(
293 View* view) {
294 // Retrieving the gfx::NativeViewAccessible also ensures that a
295 // NativeViewAccessibility exists for the given View.
296 gfx::NativeViewAccessible native_object = view->GetNativeViewAccessible();
297 ui::AXPlatformNode* node =
298 ui::AXPlatformNode::FromNativeViewAccessible(native_object);
299 DCHECK(node);
300 return static_cast<NativeViewAccessibilityBase*>(node->GetDelegate());
301 }
302
303 gfx::NativeViewAccessible
304 NativeViewAccessibilityBase::GetNativeViewAccessibleForWidget() {
305 if (parent_widget_)
306 return parent_widget_->GetRootView()->GetNativeViewAccessible();
307 return nullptr;
308 }
309
228 void NativeViewAccessibilityBase::PopulateChildWidgetVector( 310 void NativeViewAccessibilityBase::PopulateChildWidgetVector(
229 std::vector<Widget*>* result_child_widgets) { 311 std::vector<Widget*>* result_child_widgets) {
230 // Only attach child widgets to the root view. 312 // Only attach child widgets to the root view.
231 Widget* widget = view_->GetWidget(); 313 Widget* widget = view_->GetWidget();
232 // Note that during window close, a Widget may exist in a state where it has 314 // Note that during window close, a Widget may exist in a state where it has
233 // no NativeView, but hasn't yet torn down its view hierarchy. 315 // no NativeView, but hasn't yet torn down its view hierarchy.
234 if (!widget || !widget->GetNativeView() || widget->GetRootView() != view_) 316 if (!widget || !widget->GetNativeView() || widget->GetRootView() != view_)
235 return; 317 return;
236 318
237 std::set<Widget*> child_widgets; 319 std::set<Widget*> child_widgets;
(...skipping 18 matching lines...) Expand all
256 child_widget_platform_node->GetDelegate()); 338 child_widget_platform_node->GetDelegate());
257 if (child_widget_view_accessibility->parent_widget() != widget) 339 if (child_widget_view_accessibility->parent_widget() != widget)
258 child_widget_view_accessibility->SetParentWidget(widget); 340 child_widget_view_accessibility->SetParentWidget(widget);
259 } 341 }
260 342
261 result_child_widgets->push_back(child_widget); 343 result_child_widgets->push_back(child_widget);
262 } 344 }
263 } 345 }
264 346
265 } // namespace views 347 } // namespace views
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698