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