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