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

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: Rebase on top of 2704263002. 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 #if defined(PLATFORM_HAS_NATIVE_VIEW_ACCESSIBILITY_IMPL)
73 // Retrieving the gfx::NativeViewAccessible also ensures that a
74 // NativeViewAccessibility exists for the given View.
75 gfx::NativeViewAccessible native_object = view->GetNativeViewAccessible();
76 ui::AXPlatformNode* node =
77 ui::AXPlatformNode::FromNativeViewAccessible(native_object);
78 DCHECK(node);
79 return static_cast<NativeViewAccessibility*>(node->GetDelegate());
80 #else
81 // Platforms (currently ChromeOS) without a native implementation also have no
82 // gfx::NativeViewAccessible, so just create a new instance.
83 return Create(view);
tapted 2017/02/21 06:01:11 I think this means it will be leaked. I think we n
Patti Lor 2017/02/27 05:31:04 Done.
84 #endif
85 }
22 86
23 NativeViewAccessibility::NativeViewAccessibility(View* view) 87 NativeViewAccessibility::NativeViewAccessibility(View* view)
24 : view_(view), 88 : view_(view),
25 parent_widget_(nullptr), 89 parent_widget_(nullptr),
26 ax_node_(nullptr) { 90 ax_node_(nullptr) {
27 ax_node_ = ui::AXPlatformNode::Create(this); 91 ax_node_ = ui::AXPlatformNode::Create(this);
28 } 92 }
29 93
30 NativeViewAccessibility::~NativeViewAccessibility() { 94 NativeViewAccessibility::~NativeViewAccessibility() {
31 if (ax_node_) 95 if (ax_node_)
32 ax_node_->Destroy(); 96 ax_node_->Destroy();
33 if (parent_widget_) 97 if (parent_widget_)
34 parent_widget_->RemoveObserver(this); 98 parent_widget_->RemoveObserver(this);
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 std::vector<gfx::NativeViewAccessible> a11y_children =
172 GetUnignoredA11yChildren(view_);
173 // Include the child widgets that may be present if this is a RootView.
106 std::vector<Widget*> child_widgets; 174 std::vector<Widget*> child_widgets;
107 PopulateChildWidgetVector(&child_widgets); 175 PopulateChildWidgetVector(&child_widgets);
108 int child_widget_count = static_cast<int>(child_widgets.size());
109 176
110 if (index < view_->child_count()) { 177 if (index >= static_cast<int>(a11y_children.size() + child_widgets.size()))
111 return view_->child_at(index)->GetNativeViewAccessible(); 178 return nullptr;
112 } else if (index < view_->child_count() + child_widget_count) { 179 if (index < static_cast<int>(a11y_children.size()))
113 Widget* child_widget = child_widgets[index - view_->child_count()]; 180 return a11y_children[index];
114 return child_widget->GetRootView()->GetNativeViewAccessible();
115 }
116 181
117 return nullptr; 182 Widget* child_widget = child_widgets[index - a11y_children.size()];
183 return child_widget->GetRootView()->GetNativeViewAccessible();
118 } 184 }
119 185
120 gfx::NativeWindow NativeViewAccessibility::GetTopLevelWidget() { 186 gfx::NativeWindow NativeViewAccessibility::GetTopLevelWidget() {
121 if (view_->GetWidget()) 187 if (view_->GetWidget())
122 return view_->GetWidget()->GetTopLevelWidget()->GetNativeWindow(); 188 return view_->GetWidget()->GetTopLevelWidget()->GetNativeWindow();
123 return nullptr; 189 return nullptr;
124 } 190 }
125 191
126 gfx::NativeViewAccessible NativeViewAccessibility::GetParent() { 192 gfx::NativeViewAccessible NativeViewAccessibility::GetParent() {
127 if (view_->parent()) 193 View* parent_view = view_->parent();
128 return view_->parent()->GetNativeViewAccessible(); 194 if (parent_view) {
195 if (IsViewA11yIgnored(parent_view))
196 return GetForView(parent_view)->GetParent();
197 return parent_view->GetNativeViewAccessible();
198 }
129 199
130 if (parent_widget_) 200 if (parent_widget_)
131 return parent_widget_->GetRootView()->GetNativeViewAccessible(); 201 return parent_widget_->GetRootView()->GetNativeViewAccessible();
132 202
133 return nullptr; 203 return nullptr;
134 } 204 }
135 205
136 gfx::Vector2d NativeViewAccessibility::GetGlobalCoordinateOffset() { 206 gfx::Vector2d NativeViewAccessibility::GetGlobalCoordinateOffset() {
137 return gfx::Vector2d(0, 0); // location is already in screen coordinates. 207 return gfx::Vector2d(0, 0); // Location is already in screen coordinates.
138 } 208 }
139 209
140 gfx::NativeViewAccessible NativeViewAccessibility::HitTestSync(int x, int y) { 210 gfx::NativeViewAccessible NativeViewAccessibility::HitTestSync(int x, int y) {
141 if (!view_ || !view_->GetWidget()) 211 if (!view_ || !view_->GetWidget())
142 return nullptr; 212 return nullptr;
143 213
144 // Search child widgets first, since they're on top in the z-order. 214 // Search child widgets first, since they're on top in the z-order.
145 std::vector<Widget*> child_widgets; 215 std::vector<Widget*> child_widgets;
146 PopulateChildWidgetVector(&child_widgets); 216 PopulateChildWidgetVector(&child_widgets);
147 for (Widget* child_widget : child_widgets) { 217 for (Widget* child_widget : child_widgets) {
148 View* child_root_view = child_widget->GetRootView(); 218 View* child_root_view = child_widget->GetRootView();
149 gfx::Point point(x, y); 219 gfx::Point point(x, y);
150 View::ConvertPointFromScreen(child_root_view, &point); 220 View::ConvertPointFromScreen(child_root_view, &point);
151 if (child_root_view->HitTestPoint(point)) 221 if (child_root_view->HitTestPoint(point))
152 return child_root_view->GetNativeViewAccessible(); 222 return child_root_view->GetNativeViewAccessible();
153 } 223 }
154 224
225 if (GetChildCount() == 0) {
226 if (IsViewA11yIgnored(view_))
227 return nullptr;
228 return GetNativeObject();
229 }
tapted 2017/02/21 06:01:11 Can you comment about this - I'm not sure it looks
Patti Lor 2017/02/27 05:31:04 Yeah, it's kinda confusing (I had to work through
230
155 gfx::Point point(x, y); 231 gfx::Point point(x, y);
156 View::ConvertPointFromScreen(view_, &point); 232 View::ConvertPointFromScreen(view_, &point);
157 if (!view_->HitTestPoint(point)) 233 if (!view_->HitTestPoint(point))
158 return nullptr; 234 return nullptr;
159 235
160 // Check if the point is within any of the immediate children of this 236 // Check if the point is within any of the immediate children of this view. We
161 // view. We don't have to search further because AXPlatformNode will 237 // don't have to search further because AXPlatformNodeWin will do a recursive
tapted 2017/02/21 06:01:11 was there a comment earlier about how this applies
Patti Lor 2017/02/27 05:31:04 Yeah, see https://codereview.chromium.org/21194130
162 // do a recursive hit test if we return anything other than |this| or NULL. 238 // hit test if we return anything other than GetNativeObject() or nullptr.
163 for (int i = view_->child_count() - 1; i >= 0; --i) { 239 for (int i = view_->child_count() - 1; i >= 0; --i) {
164 View* child_view = view_->child_at(i); 240 View* child_view = view_->child_at(i);
165 if (!child_view->visible()) 241 if (!child_view->visible())
166 continue; 242 continue;
167 243
168 gfx::Point point_in_child_coords(point); 244 gfx::Point point_in_child_coords(point);
169 view_->ConvertPointToTarget(view_, child_view, &point_in_child_coords); 245 view_->ConvertPointToTarget(view_, child_view, &point_in_child_coords);
170 if (child_view->HitTestPoint(point_in_child_coords)) 246 if (child_view->HitTestPoint(point_in_child_coords))
171 return child_view->GetNativeViewAccessible(); 247 return child_view->GetNativeViewAccessible();
172 } 248 }
(...skipping 61 matching lines...) Expand 10 before | Expand all | Expand 10 after
234 } 310 }
235 } 311 }
236 312
237 void NativeViewAccessibility::SetParentWidget(Widget* parent_widget) { 313 void NativeViewAccessibility::SetParentWidget(Widget* parent_widget) {
238 if (parent_widget_) 314 if (parent_widget_)
239 parent_widget_->RemoveObserver(this); 315 parent_widget_->RemoveObserver(this);
240 parent_widget_ = parent_widget; 316 parent_widget_ = parent_widget;
241 parent_widget_->AddObserver(this); 317 parent_widget_->AddObserver(this);
242 } 318 }
243 319
320 #if !defined(PLATFORM_HAS_NATIVE_VIEW_ACCESSIBILITY_IMPL)
321 // static
322 NativeViewAccessibility* NativeViewAccessibility::Create(View* view) {
323 return new NativeViewAccessibility(view);
324 }
325 #endif // !defined(PLATFORM_HAS_NATIVE_VIEW_ACCESSIBILITY_IMPL)
326
244 void NativeViewAccessibility::PopulateChildWidgetVector( 327 void NativeViewAccessibility::PopulateChildWidgetVector(
245 std::vector<Widget*>* result_child_widgets) { 328 std::vector<Widget*>* result_child_widgets) {
246 // Only attach child widgets to the root view. 329 // Only attach child widgets to the root view.
247 Widget* widget = view_->GetWidget(); 330 Widget* widget = view_->GetWidget();
248 if (!widget || widget->GetRootView() != view_) 331 if (!widget || widget->GetRootView() != view_)
249 return; 332 return;
250 333
251 std::set<Widget*> child_widgets; 334 std::set<Widget*> child_widgets;
252 Widget::GetAllOwnedWidgets(widget->GetNativeView(), &child_widgets); 335 Widget::GetAllOwnedWidgets(widget->GetNativeView(), &child_widgets);
253 for (auto iter = child_widgets.begin(); iter != child_widgets.end(); ++iter) { 336 for (auto iter = child_widgets.begin(); iter != child_widgets.end(); ++iter) {
(...skipping 16 matching lines...) Expand all
270 child_widget_platform_node->GetDelegate()); 353 child_widget_platform_node->GetDelegate());
271 if (child_widget_view_accessibility->parent_widget() != widget) 354 if (child_widget_view_accessibility->parent_widget() != widget)
272 child_widget_view_accessibility->SetParentWidget(widget); 355 child_widget_view_accessibility->SetParentWidget(widget);
273 } 356 }
274 357
275 result_child_widgets->push_back(child_widget); 358 result_child_widgets->push_back(child_widget);
276 } 359 }
277 } 360 }
278 361
279 } // namespace views 362 } // namespace views
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698