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

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. 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. E.g.,
26 // LabelButtons contain Labels, but a11y should just show that there's a button.
27 bool IsViewUnfocusableChildOfFocusableAncestor(View* view) {
28 if (IsAccessibilityFocusableWhenEnabled(view))
29 return false;
30
31 while (view->parent()) {
32 view = view->parent();
33 if (IsAccessibilityFocusableWhenEnabled(view))
34 return true;
35 }
36 return false;
37 }
38
39 // Recursively gets a list of unignored gfx::NativeViewAccessible children of a
40 // given View. If a child is ignored, its children will be used instead.
41 std::vector<gfx::NativeViewAccessible> GetUnignoredA11yChildren(View* view) {
42 std::vector<gfx::NativeViewAccessible> children;
43 for (int i = 0; i < view->child_count(); ++i) {
44 View* child = view->child_at(i);
45 if (NativeViewAccessibilityBase::IsIgnoredView(child)) {
46 std::vector<gfx::NativeViewAccessible> grandchildren =
47 GetUnignoredA11yChildren(child);
48 children.insert(children.end(), grandchildren.begin(),
49 grandchildren.end());
50 } else {
51 children.push_back(child->GetNativeViewAccessible());
52 }
53 }
54 return children;
55 }
56
57 } // namespace
58
59 // static
60 bool NativeViewAccessibilityBase::IsIgnoredView(View* view) {
61 NativeViewAccessibilityBase* nva = GetForView(view);
62 if (nva)
63 return nva->GetData().role == ui::AX_ROLE_IGNORED;
64 // If the native accessibility object isn't backed by NativeViewAccessibility
65 // (e.g. when the View wraps a native object), assume it shouldn't be ignored.
66 return false;
67 }
68
17 NativeViewAccessibilityBase::NativeViewAccessibilityBase(View* view) 69 NativeViewAccessibilityBase::NativeViewAccessibilityBase(View* view)
18 : view_(view), 70 : view_(view),
19 parent_widget_(nullptr), 71 parent_widget_(nullptr),
20 ax_node_(ui::AXPlatformNode::Create(this)) { 72 ax_node_(ui::AXPlatformNode::Create(this)) {
21 DCHECK(ax_node_); 73 DCHECK(ax_node_);
22 } 74 }
23 75
24 NativeViewAccessibilityBase::~NativeViewAccessibilityBase() { 76 NativeViewAccessibilityBase::~NativeViewAccessibilityBase() {
25 ax_node_->Destroy(); 77 ax_node_->Destroy();
26 if (parent_widget_) 78 if (parent_widget_)
(...skipping 32 matching lines...) Expand 10 before | Expand all | Expand 10 after
59 111
60 if (view_->IsAccessibilityFocusable()) 112 if (view_->IsAccessibilityFocusable())
61 data_.state |= (1 << ui::AX_STATE_FOCUSABLE); 113 data_.state |= (1 << ui::AX_STATE_FOCUSABLE);
62 114
63 if (!view_->enabled()) 115 if (!view_->enabled())
64 data_.state |= (1 << ui::AX_STATE_DISABLED); 116 data_.state |= (1 << ui::AX_STATE_DISABLED);
65 117
66 if (!view_->IsDrawn()) 118 if (!view_->IsDrawn())
67 data_.state |= (1 << ui::AX_STATE_INVISIBLE); 119 data_.state |= (1 << ui::AX_STATE_INVISIBLE);
68 120
121 // Make sure this element is excluded from the a11y tree if there's a
122 // focusable parent. All keyboard focusable elements should be leaf nodes.
123 // Exceptions to this rule will themselves be accessibility focusable.
124 if (IsViewUnfocusableChildOfFocusableAncestor(view_))
125 data_.role = ui::AX_ROLE_IGNORED;
69 return data_; 126 return data_;
70 } 127 }
71 128
72 int NativeViewAccessibilityBase::GetChildCount() { 129 int NativeViewAccessibilityBase::GetChildCount() {
73 int child_count = view_->child_count(); 130 int child_count = GetUnignoredA11yChildren(view_).size();
74 131
75 std::vector<Widget*> child_widgets; 132 std::vector<Widget*> child_widgets;
76 PopulateChildWidgetVector(&child_widgets); 133 PopulateChildWidgetVector(&child_widgets);
77 child_count += child_widgets.size(); 134 child_count += child_widgets.size();
78 135
79 return child_count; 136 return child_count;
80 } 137 }
81 138
82 gfx::NativeViewAccessible NativeViewAccessibilityBase::ChildAtIndex(int index) { 139 gfx::NativeViewAccessible NativeViewAccessibilityBase::ChildAtIndex(int index) {
83 // If this is a root view, our widget might have child widgets. Include 140 std::vector<gfx::NativeViewAccessible> a11y_children =
141 GetUnignoredA11yChildren(view_);
142 // Include the child widgets that may be present if this is a RootView.
84 std::vector<Widget*> child_widgets; 143 std::vector<Widget*> child_widgets;
85 PopulateChildWidgetVector(&child_widgets); 144 PopulateChildWidgetVector(&child_widgets);
86 int child_widget_count = static_cast<int>(child_widgets.size());
87 145
88 if (index < view_->child_count()) { 146 if (index >= static_cast<int>(a11y_children.size() + child_widgets.size()))
89 return view_->child_at(index)->GetNativeViewAccessible(); 147 return nullptr;
90 } else if (index < view_->child_count() + child_widget_count) { 148 if (index < static_cast<int>(a11y_children.size()))
91 Widget* child_widget = child_widgets[index - view_->child_count()]; 149 return a11y_children[index];
92 return child_widget->GetRootView()->GetNativeViewAccessible();
93 }
94 150
95 return nullptr; 151 Widget* child_widget = child_widgets[index - a11y_children.size()];
152 return child_widget->GetRootView()->GetNativeViewAccessible();
96 } 153 }
97 154
98 gfx::NativeWindow NativeViewAccessibilityBase::GetTopLevelWidget() { 155 gfx::NativeWindow NativeViewAccessibilityBase::GetTopLevelWidget() {
99 if (view_->GetWidget()) 156 if (view_->GetWidget())
100 return view_->GetWidget()->GetTopLevelWidget()->GetNativeWindow(); 157 return view_->GetWidget()->GetTopLevelWidget()->GetNativeWindow();
101 return nullptr; 158 return nullptr;
102 } 159 }
103 160
104 gfx::NativeViewAccessible NativeViewAccessibilityBase::GetParent() { 161 gfx::NativeViewAccessible NativeViewAccessibilityBase::GetParent() {
105 if (view_->parent()) 162 View* parent_view = view_->parent();
106 return view_->parent()->GetNativeViewAccessible(); 163 while (parent_view && IsIgnoredView(parent_view))
107 164 parent_view = parent_view->parent();
108 if (parent_widget_) 165 return parent_view ? parent_view->GetNativeViewAccessible()
109 return parent_widget_->GetRootView()->GetNativeViewAccessible(); 166 : GetNativeViewAccessibleForWidget();
110
111 return nullptr;
112 } 167 }
113 168
114 gfx::Rect NativeViewAccessibilityBase::GetScreenBoundsRect() const { 169 gfx::Rect NativeViewAccessibilityBase::GetScreenBoundsRect() const {
115 return view_->GetBoundsInScreen(); 170 return view_->GetBoundsInScreen();
116 } 171 }
117 172
118 gfx::NativeViewAccessible NativeViewAccessibilityBase::HitTestSync(int x, 173 gfx::NativeViewAccessible NativeViewAccessibilityBase::HitTestSync(int x,
119 int y) { 174 int y) {
120 if (!view_ || !view_->GetWidget()) 175 if (!view_ || !view_->GetWidget())
121 return nullptr; 176 return nullptr;
122 177
123 // Search child widgets first, since they're on top in the z-order. 178 // Search child widgets first, since they're on top in the z-order.
124 std::vector<Widget*> child_widgets; 179 std::vector<Widget*> child_widgets;
125 PopulateChildWidgetVector(&child_widgets); 180 PopulateChildWidgetVector(&child_widgets);
126 for (Widget* child_widget : child_widgets) { 181 for (Widget* child_widget : child_widgets) {
127 View* child_root_view = child_widget->GetRootView(); 182 View* child_root_view = child_widget->GetRootView();
128 gfx::Point point(x, y); 183 gfx::Point point(x, y);
129 View::ConvertPointFromScreen(child_root_view, &point); 184 View::ConvertPointFromScreen(child_root_view, &point);
130 if (child_root_view->HitTestPoint(point)) 185 if (child_root_view->HitTestPoint(point))
131 return child_root_view->GetNativeViewAccessible(); 186 return child_root_view->GetNativeViewAccessible();
132 } 187 }
133 188
189 // Check if the given point is inside |view_|.
134 gfx::Point point(x, y); 190 gfx::Point point(x, y);
135 View::ConvertPointFromScreen(view_, &point); 191 View::ConvertPointFromScreen(view_, &point);
136 if (!view_->HitTestPoint(point)) 192 if (!view_->HitTestPoint(point))
137 return nullptr; 193 return nullptr;
138 194
139 // Check if the point is within any of the immediate children of this 195 // Early return if this node is a leaf, depending on whether it's ignored.
140 // view. We don't have to search further because AXPlatformNode will 196 if (GetChildCount() == 0)
141 // do a recursive hit test if we return anything other than |this| or NULL. 197 return IsIgnoredView(view_) ? nullptr : GetNativeObject();
198
199 // Check if the point is within any of the immediate children of this view. We
200 // don't have to search further because AXPlatformNodeWin will do a recursive
201 // hit test if we return anything other than GetNativeObject() or nullptr.
202 // The exception to this is if the recursion needs to travel up the tree,
203 // which may return a non-direct ancestor of the given node.
142 for (int i = view_->child_count() - 1; i >= 0; --i) { 204 for (int i = view_->child_count() - 1; i >= 0; --i) {
143 View* child_view = view_->child_at(i); 205 View* child_view = view_->child_at(i);
144 if (!child_view->visible()) 206 NativeViewAccessibilityBase* child_nva = GetForView(child_view);
145 continue; 207 if (child_nva) {
146 208 gfx::Point point_in_child_coords(point);
147 gfx::Point point_in_child_coords(point); 209 view_->ConvertPointToTarget(view_, child_view, &point_in_child_coords);
148 view_->ConvertPointToTarget(view_, child_view, &point_in_child_coords); 210 if (!child_nva->GetData().HasStateFlag(ui::AX_STATE_INVISIBLE) &&
149 if (child_view->HitTestPoint(point_in_child_coords)) 211 child_view->HitTestPoint(point_in_child_coords)) {
150 return child_view->GetNativeViewAccessible(); 212 if (child_nva->GetChildCount() == 0 && IsIgnoredView(child_view)) {
213 // Multiple levels might be jumped here.
214 return child_nva->GetParent();
215 }
216 return child_nva->GetNativeObject();
217 }
218 }
151 } 219 }
152 220
153 // If it's not inside any of our children, it's inside this view. 221 // If it's not inside any of our children, it's inside this view.
154 return GetNativeObject(); 222 return GetNativeObject();
155 } 223 }
156 224
157 gfx::NativeViewAccessible NativeViewAccessibilityBase::GetFocus() { 225 gfx::NativeViewAccessible NativeViewAccessibilityBase::GetFocus() {
158 FocusManager* focus_manager = view_->GetFocusManager(); 226 FocusManager* focus_manager = view_->GetFocusManager();
159 View* focused_view = 227 View* focused_view =
160 focus_manager ? focus_manager->GetFocusedView() : nullptr; 228 focus_manager ? focus_manager->GetFocusedView() : nullptr;
(...skipping 17 matching lines...) Expand all
178 } 246 }
179 } 247 }
180 248
181 void NativeViewAccessibilityBase::SetParentWidget(Widget* parent_widget) { 249 void NativeViewAccessibilityBase::SetParentWidget(Widget* parent_widget) {
182 if (parent_widget_) 250 if (parent_widget_)
183 parent_widget_->RemoveObserver(this); 251 parent_widget_->RemoveObserver(this);
184 parent_widget_ = parent_widget; 252 parent_widget_ = parent_widget;
185 parent_widget_->AddObserver(this); 253 parent_widget_->AddObserver(this);
186 } 254 }
187 255
256 // static
257 NativeViewAccessibilityBase* NativeViewAccessibilityBase::GetForView(
258 View* view) {
259 // Retrieving the gfx::NativeViewAccessible also ensures that a
260 // NativeViewAccessibility exists for the given View.
261 gfx::NativeViewAccessible native_object = view->GetNativeViewAccessible();
262 ui::AXPlatformNode* node =
263 ui::AXPlatformNode::FromNativeViewAccessible(native_object);
264 // WebViews with web contents have their own NativeViewAccessible
265 // implementation, so |node| will be null if |view| is a WebView.
266 if (node)
267 return static_cast<NativeViewAccessibilityBase*>(node->GetDelegate());
268 return nullptr;
269 }
270
271 gfx::NativeViewAccessible
272 NativeViewAccessibilityBase::GetNativeViewAccessibleForWidget() {
273 if (parent_widget_)
274 return parent_widget_->GetRootView()->GetNativeViewAccessible();
275 return nullptr;
276 }
277
188 gfx::RectF NativeViewAccessibilityBase::GetBoundsInScreen() const { 278 gfx::RectF NativeViewAccessibilityBase::GetBoundsInScreen() const {
189 return gfx::RectF(view_->GetBoundsInScreen()); 279 return gfx::RectF(view_->GetBoundsInScreen());
190 } 280 }
191 281
192 void NativeViewAccessibilityBase::PopulateChildWidgetVector( 282 void NativeViewAccessibilityBase::PopulateChildWidgetVector(
193 std::vector<Widget*>* result_child_widgets) { 283 std::vector<Widget*>* result_child_widgets) {
194 // Only attach child widgets to the root view. 284 // Only attach child widgets to the root view.
195 Widget* widget = view_->GetWidget(); 285 Widget* widget = view_->GetWidget();
196 // Note that during window close, a Widget may exist in a state where it has 286 // Note that during window close, a Widget may exist in a state where it has
197 // no NativeView, but hasn't yet torn down its view hierarchy. 287 // no NativeView, but hasn't yet torn down its view hierarchy.
198 if (!widget || !widget->GetNativeView() || widget->GetRootView() != view_) 288 if (!widget || !widget->GetNativeView() || widget->GetRootView() != view_)
199 return; 289 return;
200 290
201 std::set<Widget*> child_widgets; 291 std::set<Widget*> child_widgets;
202 Widget::GetAllOwnedWidgets(widget->GetNativeView(), &child_widgets); 292 Widget::GetAllOwnedWidgets(widget->GetNativeView(), &child_widgets);
203 for (auto iter = child_widgets.begin(); iter != child_widgets.end(); ++iter) { 293 for (auto iter = child_widgets.begin(); iter != child_widgets.end(); ++iter) {
204 Widget* child_widget = *iter; 294 Widget* child_widget = *iter;
205 DCHECK_NE(widget, child_widget); 295 DCHECK_NE(widget, child_widget);
206 296
207 if (!child_widget->IsVisible()) 297 if (!child_widget->IsVisible())
208 continue; 298 continue;
209 299
210 if (widget->GetNativeWindowProperty(kWidgetNativeViewHostKey)) 300 if (widget->GetNativeWindowProperty(kWidgetNativeViewHostKey))
211 continue; 301 continue;
212 302
213 gfx::NativeViewAccessible child_widget_accessible = 303 NativeViewAccessibilityBase* child_widget_view_accessibility =
214 child_widget->GetRootView()->GetNativeViewAccessible(); 304 GetForView(child_widget->GetRootView());
215 ui::AXPlatformNode* child_widget_platform_node = 305 if (child_widget_view_accessibility &&
216 ui::AXPlatformNode::FromNativeViewAccessible(child_widget_accessible); 306 child_widget_view_accessibility->parent_widget() != widget)
217 if (child_widget_platform_node) { 307 child_widget_view_accessibility->SetParentWidget(widget);
218 NativeViewAccessibilityBase* child_widget_view_accessibility =
219 static_cast<NativeViewAccessibilityBase*>(
220 child_widget_platform_node->GetDelegate());
221 if (child_widget_view_accessibility->parent_widget() != widget)
222 child_widget_view_accessibility->SetParentWidget(widget);
223 }
224 308
225 result_child_widgets->push_back(child_widget); 309 result_child_widgets->push_back(child_widget);
226 } 310 }
227 } 311 }
228 312
229 } // namespace views 313 } // namespace views
OLDNEW
« no previous file with comments | « ui/views/accessibility/native_view_accessibility_base.h ('k') | ui/views/accessibility/native_view_accessibility_mac.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698