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

Side by Side Diff: ui/views/accessibility/native_view_accessibility.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: Fix CrOS. Created 3 years, 10 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.h" 5 #include "ui/views/accessibility/native_view_accessibility.h"
6 6
7 #include "base/strings/utf_string_conversions.h" 7 #include "base/strings/utf_string_conversions.h"
8 #include "ui/accessibility/platform/ax_platform_node.h"
8 #include "ui/events/event_utils.h" 9 #include "ui/events/event_utils.h"
9 #include "ui/gfx/native_widget_types.h" 10 #include "ui/gfx/native_widget_types.h"
10 #include "ui/views/controls/native/native_view_host.h" 11 #include "ui/views/controls/native/native_view_host.h"
11 #include "ui/views/view.h" 12 #include "ui/views/view.h"
12 #include "ui/views/widget/widget.h" 13 #include "ui/views/widget/widget.h"
13 14
14 namespace views { 15 namespace views {
15 16
16 #if !defined(PLATFORM_HAS_NATIVE_VIEW_ACCESSIBILITY_IMPL) 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.
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 // Convenience method for checking if a View should be ignored by a11y.
39 bool IsViewA11yIgnored(View* view) {
40 return NativeViewAccessibility::GetForView(view)->GetData().role ==
41 ui::AX_ROLE_IGNORED;
42 }
43
44 // Recursively gets a list of unignored gfx::NativeViewAccessible children of a
45 // given View. If a child is ignored, its children will be used instead.
46 std::vector<gfx::NativeViewAccessible> GetUnignoredA11yChildren(View* view) {
47 std::vector<gfx::NativeViewAccessible> children;
48 for (int i = 0; i < view->child_count(); ++i) {
49 View* child = view->child_at(i);
50 if (IsViewA11yIgnored(child)) {
51 std::vector<gfx::NativeViewAccessible> grandchildren =
52 GetUnignoredA11yChildren(child);
53 children.insert(children.end(), grandchildren.begin(),
54 grandchildren.end());
55 } else {
56 children.push_back(child->GetNativeViewAccessible());
57 }
58 }
59 return children;
60 }
61
62 } // namespace
63
17 // static 64 // static
18 NativeViewAccessibility* NativeViewAccessibility::Create(View* view) { 65 std::unique_ptr<NativeViewAccessibility> NativeViewAccessibility::CreateForView(
19 return new NativeViewAccessibility(view); 66 View* view) {
67 return base::WrapUnique(Create(view));
20 } 68 }
21 #endif // !defined(PLATFORM_HAS_NATIVE_VIEW_ACCESSIBILITY_IMPL) 69
70 // static
71 NativeViewAccessibility* NativeViewAccessibility::GetForView(View* view) {
72 // Retrieving the gfx::NativeViewAccessible also ensures that a
73 // NativeViewAccessibility exists for the given View.
74 gfx::NativeViewAccessible native_object = view->GetNativeViewAccessible();
75 ui::AXPlatformNode* node =
76 ui::AXPlatformNode::FromNativeViewAccessible(native_object);
77 DCHECK(node);
78 return static_cast<NativeViewAccessibility*>(node->GetDelegate());
79 }
22 80
23 NativeViewAccessibility::NativeViewAccessibility(View* view) 81 NativeViewAccessibility::NativeViewAccessibility(View* view)
24 : view_(view), 82 : view_(view),
25 parent_widget_(nullptr), 83 parent_widget_(nullptr),
26 ax_node_(nullptr) { 84 ax_node_(nullptr) {
27 ax_node_ = ui::AXPlatformNode::Create(this); 85 ax_node_ = ui::AXPlatformNode::Create(this);
28 } 86 }
29 87
30 NativeViewAccessibility::~NativeViewAccessibility() { 88 NativeViewAccessibility::~NativeViewAccessibility() {
31 if (ax_node_) 89 if (ax_node_)
32 ax_node_->Destroy(); 90 ax_node_->Destroy();
33 if (parent_widget_) 91 if (parent_widget_)
34 parent_widget_->RemoveObserver(this); 92 parent_widget_->RemoveObserver(this);
35 } 93 }
36 94
37 gfx::NativeViewAccessible NativeViewAccessibility::GetNativeObject() { 95 gfx::NativeViewAccessible NativeViewAccessibility::GetNativeObject() {
38 return ax_node_ ? ax_node_->GetNativeViewAccessible() : nullptr; 96 return ax_node_ ? ax_node_->GetNativeViewAccessible() : nullptr;
39 } 97 }
40 98
41 void NativeViewAccessibility::Destroy() {
42 delete this;
43 }
44
45 void NativeViewAccessibility::NotifyAccessibilityEvent(ui::AXEvent event_type) { 99 void NativeViewAccessibility::NotifyAccessibilityEvent(ui::AXEvent event_type) {
46 if (ax_node_) 100 if (ax_node_)
47 ax_node_->NotifyAccessibilityEvent(event_type); 101 ax_node_->NotifyAccessibilityEvent(event_type);
48 } 102 }
49 103
50 bool NativeViewAccessibility::SetFocused(bool focused) { 104 bool NativeViewAccessibility::SetFocused(bool focused) {
51 if (!ui::AXNodeData::IsFlagSet(GetData().state, ui::AX_STATE_FOCUSABLE)) 105 if (!ui::AXNodeData::IsFlagSet(GetData().state, ui::AX_STATE_FOCUSABLE))
52 return false; 106 return false;
53 107
54 if (focused) 108 if (focused)
(...skipping 18 matching lines...) Expand all
73 } 127 }
74 128
75 view_->GetAccessibleNodeData(&data_); 129 view_->GetAccessibleNodeData(&data_);
76 data_.location = gfx::RectF(view_->GetBoundsInScreen()); 130 data_.location = gfx::RectF(view_->GetBoundsInScreen());
77 base::string16 description; 131 base::string16 description;
78 view_->GetTooltipText(gfx::Point(), &description); 132 view_->GetTooltipText(gfx::Point(), &description);
79 data_.AddStringAttribute(ui::AX_ATTR_DESCRIPTION, 133 data_.AddStringAttribute(ui::AX_ATTR_DESCRIPTION,
80 base::UTF16ToUTF8(description)); 134 base::UTF16ToUTF8(description));
81 135
82 if (view_->IsAccessibilityFocusable()) 136 if (view_->IsAccessibilityFocusable())
83 data_.state |= (1 << ui::AX_STATE_FOCUSABLE); 137 data_.AddStateFlag(ui::AX_STATE_FOCUSABLE);
84 138
85 if (!view_->enabled()) 139 if (!view_->enabled())
86 data_.state |= (1 << ui::AX_STATE_DISABLED); 140 data_.AddStateFlag(ui::AX_STATE_DISABLED);
87 141
88 if (!view_->IsDrawn()) 142 if (!view_->IsDrawn())
89 data_.state |= (1 << ui::AX_STATE_INVISIBLE); 143 data_.AddStateFlag(ui::AX_STATE_INVISIBLE);
144
145 // Make sure this element is excluded from the a11y tree if there's a
146 // focusable parent. All keyboard focusable elements should be leaf nodes.
147 // Exceptions to this rule will themselves be accessibility focusable.
148 if (IsViewUnfocusableChildOfFocusableAncestor(view_))
149 data_.role = ui::AX_ROLE_IGNORED;
90 150
91 return data_; 151 return data_;
92 } 152 }
93 153
94 int NativeViewAccessibility::GetChildCount() { 154 int NativeViewAccessibility::GetChildCount() {
95 int child_count = view_->child_count(); 155 int child_count = GetUnignoredA11yChildren(view_).size();
96 156
97 std::vector<Widget*> child_widgets; 157 std::vector<Widget*> child_widgets;
98 PopulateChildWidgetVector(&child_widgets); 158 PopulateChildWidgetVector(&child_widgets);
99 child_count += child_widgets.size(); 159 child_count += child_widgets.size();
100 160
101 return child_count; 161 return child_count;
102 } 162 }
103 163
104 gfx::NativeViewAccessible NativeViewAccessibility::ChildAtIndex(int index) { 164 gfx::NativeViewAccessible NativeViewAccessibility::ChildAtIndex(int index) {
105 // If this is a root view, our widget might have child widgets. Include 165 std::vector<gfx::NativeViewAccessible> a11y_children =
166 GetUnignoredA11yChildren(view_);
167 int total_child_count = a11y_children.size();
168
169 if (index >= total_child_count)
170 return nullptr;
171
172 // Include the child widgets that may be present if this is a RootView.
106 std::vector<Widget*> child_widgets; 173 std::vector<Widget*> child_widgets;
107 PopulateChildWidgetVector(&child_widgets); 174 PopulateChildWidgetVector(&child_widgets);
108 int child_widget_count = static_cast<int>(child_widgets.size());
109 175
110 if (index < view_->child_count()) { 176 int child_view_count = total_child_count - child_widgets.size();
111 return view_->child_at(index)->GetNativeViewAccessible(); 177 if (index < child_view_count) {
112 } else if (index < view_->child_count() + child_widget_count) { 178 return a11y_children[index];
113 Widget* child_widget = child_widgets[index - view_->child_count()];
114 return child_widget->GetRootView()->GetNativeViewAccessible();
115 } 179 }
116 180
117 return nullptr; 181 Widget* child_widget = child_widgets[index - child_view_count];
182 return child_widget->GetRootView()->GetNativeViewAccessible();
118 } 183 }
119 184
120 gfx::NativeWindow NativeViewAccessibility::GetTopLevelWidget() { 185 gfx::NativeWindow NativeViewAccessibility::GetTopLevelWidget() {
121 if (view_->GetWidget()) 186 if (view_->GetWidget())
122 return view_->GetWidget()->GetTopLevelWidget()->GetNativeWindow(); 187 return view_->GetWidget()->GetTopLevelWidget()->GetNativeWindow();
123 return nullptr; 188 return nullptr;
124 } 189 }
125 190
126 gfx::NativeViewAccessible NativeViewAccessibility::GetParent() { 191 gfx::NativeViewAccessible NativeViewAccessibility::GetParent() {
tapted 2017/02/15 02:10:45 yeah NativeViewAccessible is `typedef void*` on Ch
Patti Lor 2017/02/21 03:29:17 Yep - so I ended up just returning a new instance
127 if (view_->parent()) 192 View* parent_view = view_->parent();
128 return view_->parent()->GetNativeViewAccessible(); 193 if (parent_view) {
194 if (IsViewA11yIgnored(parent_view))
195 return GetForView(parent_view)->GetParent();
196 return parent_view->GetNativeViewAccessible();
197 }
129 198
130 // TODO: move this to NativeViewAccessibilityMac. 199 // TODO: move this to NativeViewAccessibilityMac.
131 #if defined(OS_MACOSX) 200 #if defined(OS_MACOSX)
132 if (view_->GetWidget()) 201 if (view_->GetWidget())
133 return view_->GetWidget()->GetNativeView(); 202 return (gfx::NativeViewAccessible)view_->GetWidget()->GetNativeView();
tapted 2017/02/15 02:10:45 can the c-style cast be removed?
Patti Lor 2017/02/21 03:29:17 Yep! Originally, I tried a static_cast instead, bu
134 #endif 203 #endif
135 204
136 if (parent_widget_) 205 if (parent_widget_)
137 return parent_widget_->GetRootView()->GetNativeViewAccessible(); 206 return parent_widget_->GetRootView()->GetNativeViewAccessible();
138 207
139 return nullptr; 208 return nullptr;
140 } 209 }
141 210
142 gfx::Vector2d NativeViewAccessibility::GetGlobalCoordinateOffset() { 211 gfx::Vector2d NativeViewAccessibility::GetGlobalCoordinateOffset() {
143 return gfx::Vector2d(0, 0); // location is already in screen coordinates. 212 return gfx::Vector2d(0, 0); // Location is already in screen coordinates.
144 } 213 }
145 214
146 gfx::NativeViewAccessible NativeViewAccessibility::HitTestSync(int x, int y) { 215 gfx::NativeViewAccessible NativeViewAccessibility::HitTestSync(int x, int y) {
147 if (!view_ || !view_->GetWidget()) 216 if (!view_ || !view_->GetWidget())
148 return nullptr; 217 return nullptr;
149 218
150 // Search child widgets first, since they're on top in the z-order. 219 // Search child widgets first, since they're on top in the z-order.
151 std::vector<Widget*> child_widgets; 220 std::vector<Widget*> child_widgets;
152 PopulateChildWidgetVector(&child_widgets); 221 PopulateChildWidgetVector(&child_widgets);
153 for (Widget* child_widget : child_widgets) { 222 for (Widget* child_widget : child_widgets) {
154 View* child_root_view = child_widget->GetRootView(); 223 View* child_root_view = child_widget->GetRootView();
155 gfx::Point point(x, y); 224 gfx::Point point(x, y);
156 View::ConvertPointFromScreen(child_root_view, &point); 225 View::ConvertPointFromScreen(child_root_view, &point);
157 if (child_root_view->HitTestPoint(point)) 226 if (child_root_view->HitTestPoint(point))
158 return child_root_view->GetNativeViewAccessible(); 227 return child_root_view->GetNativeViewAccessible();
159 } 228 }
160 229
230 if (GetChildCount() == 0) {
231 if (IsViewA11yIgnored(view_))
232 return nullptr;
233 return GetNativeObject();
234 }
235
161 gfx::Point point(x, y); 236 gfx::Point point(x, y);
162 View::ConvertPointFromScreen(view_, &point); 237 View::ConvertPointFromScreen(view_, &point);
163 if (!view_->HitTestPoint(point)) 238 if (!view_->HitTestPoint(point))
164 return nullptr; 239 return nullptr;
165 240
166 // Check if the point is within any of the immediate children of this 241 // Check if the point is within any of the immediate children of this view. We
167 // view. We don't have to search further because AXPlatformNode will 242 // don't have to search further because AXPlatformNodeWin will do a recursive
168 // do a recursive hit test if we return anything other than |this| or NULL. 243 // hit test if we return anything other than GetNativeObject() or nullptr.
169 for (int i = view_->child_count() - 1; i >= 0; --i) { 244 for (int i = view_->child_count() - 1; i >= 0; --i) {
170 View* child_view = view_->child_at(i); 245 View* child_view = view_->child_at(i);
171 if (!child_view->visible()) 246 if (!child_view->visible())
172 continue; 247 continue;
173 248
174 gfx::Point point_in_child_coords(point); 249 gfx::Point point_in_child_coords(point);
175 view_->ConvertPointToTarget(view_, child_view, &point_in_child_coords); 250 view_->ConvertPointToTarget(view_, child_view, &point_in_child_coords);
176 if (child_view->HitTestPoint(point_in_child_coords)) 251 if (child_view->HitTestPoint(point_in_child_coords))
177 return child_view->GetNativeViewAccessible(); 252 return child_view->GetNativeViewAccessible();
178 } 253 }
(...skipping 61 matching lines...) Expand 10 before | Expand all | Expand 10 after
240 } 315 }
241 } 316 }
242 317
243 void NativeViewAccessibility::SetParentWidget(Widget* parent_widget) { 318 void NativeViewAccessibility::SetParentWidget(Widget* parent_widget) {
244 if (parent_widget_) 319 if (parent_widget_)
245 parent_widget_->RemoveObserver(this); 320 parent_widget_->RemoveObserver(this);
246 parent_widget_ = parent_widget; 321 parent_widget_ = parent_widget;
247 parent_widget_->AddObserver(this); 322 parent_widget_->AddObserver(this);
248 } 323 }
249 324
325 #if !defined(PLATFORM_HAS_NATIVE_VIEW_ACCESSIBILITY_IMPL)
326 // static
327 NativeViewAccessibility* NativeViewAccessibility::Create(View* view) {
328 return new NativeViewAccessibility(view);
329 }
330 #endif // !defined(PLATFORM_HAS_NATIVE_VIEW_ACCESSIBILITY_IMPL)
331
250 void NativeViewAccessibility::PopulateChildWidgetVector( 332 void NativeViewAccessibility::PopulateChildWidgetVector(
251 std::vector<Widget*>* result_child_widgets) { 333 std::vector<Widget*>* result_child_widgets) {
252 // Only attach child widgets to the root view. 334 // Only attach child widgets to the root view.
253 Widget* widget = view_->GetWidget(); 335 Widget* widget = view_->GetWidget();
254 if (!widget || widget->GetRootView() != view_) 336 if (!widget || widget->GetRootView() != view_)
255 return; 337 return;
256 338
257 std::set<Widget*> child_widgets; 339 std::set<Widget*> child_widgets;
258 Widget::GetAllOwnedWidgets(widget->GetNativeView(), &child_widgets); 340 Widget::GetAllOwnedWidgets(widget->GetNativeView(), &child_widgets);
259 for (auto iter = child_widgets.begin(); iter != child_widgets.end(); ++iter) { 341 for (auto iter = child_widgets.begin(); iter != child_widgets.end(); ++iter) {
(...skipping 16 matching lines...) Expand all
276 child_widget_platform_node->GetDelegate()); 358 child_widget_platform_node->GetDelegate());
277 if (child_widget_view_accessibility->parent_widget() != widget) 359 if (child_widget_view_accessibility->parent_widget() != widget)
278 child_widget_view_accessibility->SetParentWidget(widget); 360 child_widget_view_accessibility->SetParentWidget(widget);
279 } 361 }
280 362
281 result_child_widgets->push_back(child_widget); 363 result_child_widgets->push_back(child_widget);
282 } 364 }
283 } 365 }
284 366
285 } // namespace views 367 } // namespace views
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698