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

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

Powered by Google App Engine
This is Rietveld 408576698