OLD | NEW |
---|---|
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 Loading... | |
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 Loading... | |
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 Loading... | |
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 |
OLD | NEW |