Chromium Code Reviews| 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) { | |
|
tapted
2017/04/05 06:03:33
Would would happen if we did something like
(ax_p
Patti Lor
2017/05/24 07:29:11
So the invisible stuff works, but I think since th
| |
| 61 NativeViewAccessibilityBase* nva = GetForView(view); | |
| 62 if (nva) | |
| 63 return nva->GetData().role == ui::AX_ROLE_IGNORED; | |
| 64 return false; | |
|
tapted
2017/04/04 08:13:13
Comment here like
// If the native accessibility
Patti Lor
2017/04/11 05:40:45
Done.
| |
| 65 } | |
| 66 | |
| 17 NativeViewAccessibilityBase::NativeViewAccessibilityBase(View* view) | 67 NativeViewAccessibilityBase::NativeViewAccessibilityBase(View* view) |
| 18 : view_(view), | 68 : view_(view), |
| 19 parent_widget_(nullptr), | 69 parent_widget_(nullptr), |
| 20 ax_node_(ui::AXPlatformNode::Create(this)) { | 70 ax_node_(ui::AXPlatformNode::Create(this)) { |
| 21 DCHECK(ax_node_); | 71 DCHECK(ax_node_); |
| 22 } | 72 } |
| 23 | 73 |
| 24 NativeViewAccessibilityBase::~NativeViewAccessibilityBase() { | 74 NativeViewAccessibilityBase::~NativeViewAccessibilityBase() { |
| 25 ax_node_->Destroy(); | 75 ax_node_->Destroy(); |
| 26 if (parent_widget_) | 76 if (parent_widget_) |
| (...skipping 43 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 70 | 120 |
| 71 if (view_->IsAccessibilityFocusable()) | 121 if (view_->IsAccessibilityFocusable()) |
| 72 data_.state |= (1 << ui::AX_STATE_FOCUSABLE); | 122 data_.state |= (1 << ui::AX_STATE_FOCUSABLE); |
| 73 | 123 |
| 74 if (!view_->enabled()) | 124 if (!view_->enabled()) |
| 75 data_.state |= (1 << ui::AX_STATE_DISABLED); | 125 data_.state |= (1 << ui::AX_STATE_DISABLED); |
| 76 | 126 |
| 77 if (!view_->IsDrawn()) | 127 if (!view_->IsDrawn()) |
| 78 data_.state |= (1 << ui::AX_STATE_INVISIBLE); | 128 data_.state |= (1 << ui::AX_STATE_INVISIBLE); |
| 79 | 129 |
| 130 // Make sure this element is excluded from the a11y tree if there's a | |
| 131 // focusable parent. All keyboard focusable elements should be leaf nodes. | |
| 132 // Exceptions to this rule will themselves be accessibility focusable. | |
| 133 if (IsViewUnfocusableChildOfFocusableAncestor(view_)) | |
| 134 data_.role = ui::AX_ROLE_IGNORED; | |
| 80 return data_; | 135 return data_; |
| 81 } | 136 } |
| 82 | 137 |
| 83 int NativeViewAccessibilityBase::GetChildCount() { | 138 int NativeViewAccessibilityBase::GetChildCount() { |
| 84 int child_count = view_->child_count(); | 139 int child_count = GetUnignoredA11yChildren(view_).size(); |
| 85 | 140 |
| 86 std::vector<Widget*> child_widgets; | 141 std::vector<Widget*> child_widgets; |
| 87 PopulateChildWidgetVector(&child_widgets); | 142 PopulateChildWidgetVector(&child_widgets); |
| 88 child_count += child_widgets.size(); | 143 child_count += child_widgets.size(); |
| 89 | 144 |
| 90 return child_count; | 145 return child_count; |
| 91 } | 146 } |
| 92 | 147 |
| 93 gfx::NativeViewAccessible NativeViewAccessibilityBase::ChildAtIndex(int index) { | 148 gfx::NativeViewAccessible NativeViewAccessibilityBase::ChildAtIndex(int index) { |
| 94 // If this is a root view, our widget might have child widgets. Include | 149 std::vector<gfx::NativeViewAccessible> a11y_children = |
| 150 GetUnignoredA11yChildren(view_); | |
| 151 // Include the child widgets that may be present if this is a RootView. | |
| 95 std::vector<Widget*> child_widgets; | 152 std::vector<Widget*> child_widgets; |
| 96 PopulateChildWidgetVector(&child_widgets); | 153 PopulateChildWidgetVector(&child_widgets); |
| 97 int child_widget_count = static_cast<int>(child_widgets.size()); | |
| 98 | 154 |
| 99 if (index < view_->child_count()) { | 155 if (index >= static_cast<int>(a11y_children.size() + child_widgets.size())) |
| 100 return view_->child_at(index)->GetNativeViewAccessible(); | 156 return nullptr; |
| 101 } else if (index < view_->child_count() + child_widget_count) { | 157 if (index < static_cast<int>(a11y_children.size())) |
| 102 Widget* child_widget = child_widgets[index - view_->child_count()]; | 158 return a11y_children[index]; |
| 103 return child_widget->GetRootView()->GetNativeViewAccessible(); | |
| 104 } | |
| 105 | 159 |
| 106 return nullptr; | 160 Widget* child_widget = child_widgets[index - a11y_children.size()]; |
| 161 return child_widget->GetRootView()->GetNativeViewAccessible(); | |
| 107 } | 162 } |
| 108 | 163 |
| 109 gfx::NativeWindow NativeViewAccessibilityBase::GetTopLevelWidget() { | 164 gfx::NativeWindow NativeViewAccessibilityBase::GetTopLevelWidget() { |
| 110 if (view_->GetWidget()) | 165 if (view_->GetWidget()) |
| 111 return view_->GetWidget()->GetTopLevelWidget()->GetNativeWindow(); | 166 return view_->GetWidget()->GetTopLevelWidget()->GetNativeWindow(); |
| 112 return nullptr; | 167 return nullptr; |
| 113 } | 168 } |
| 114 | 169 |
| 115 gfx::NativeViewAccessible NativeViewAccessibilityBase::GetParent() { | 170 gfx::NativeViewAccessible NativeViewAccessibilityBase::GetParent() { |
| 116 if (view_->parent()) | 171 View* parent_view = view_->parent(); |
| 117 return view_->parent()->GetNativeViewAccessible(); | 172 while (parent_view && IsIgnoredView(parent_view)) |
| 118 | 173 parent_view = parent_view->parent(); |
| 119 if (parent_widget_) | 174 return parent_view ? parent_view->GetNativeViewAccessible() |
| 120 return parent_widget_->GetRootView()->GetNativeViewAccessible(); | 175 : GetNativeViewAccessibleForWidget(); |
| 121 | |
| 122 return nullptr; | |
| 123 } | 176 } |
| 124 | 177 |
| 125 gfx::Vector2d NativeViewAccessibilityBase::GetGlobalCoordinateOffset() { | 178 gfx::Vector2d NativeViewAccessibilityBase::GetGlobalCoordinateOffset() { |
| 126 return gfx::Vector2d(0, 0); // location is already in screen coordinates. | 179 return gfx::Vector2d(0, 0); // Location is already in screen coordinates. |
| 127 } | 180 } |
| 128 | 181 |
| 129 gfx::NativeViewAccessible NativeViewAccessibilityBase::HitTestSync(int x, | 182 gfx::NativeViewAccessible NativeViewAccessibilityBase::HitTestSync(int x, |
| 130 int y) { | 183 int y) { |
| 131 if (!view_ || !view_->GetWidget()) | 184 if (!view_ || !view_->GetWidget()) |
| 132 return nullptr; | 185 return nullptr; |
| 133 | 186 |
| 134 // Search child widgets first, since they're on top in the z-order. | 187 // Search child widgets first, since they're on top in the z-order. |
| 135 std::vector<Widget*> child_widgets; | 188 std::vector<Widget*> child_widgets; |
| 136 PopulateChildWidgetVector(&child_widgets); | 189 PopulateChildWidgetVector(&child_widgets); |
| 137 for (Widget* child_widget : child_widgets) { | 190 for (Widget* child_widget : child_widgets) { |
| 138 View* child_root_view = child_widget->GetRootView(); | 191 View* child_root_view = child_widget->GetRootView(); |
| 139 gfx::Point point(x, y); | 192 gfx::Point point(x, y); |
| 140 View::ConvertPointFromScreen(child_root_view, &point); | 193 View::ConvertPointFromScreen(child_root_view, &point); |
| 141 if (child_root_view->HitTestPoint(point)) | 194 if (child_root_view->HitTestPoint(point)) |
| 142 return child_root_view->GetNativeViewAccessible(); | 195 return child_root_view->GetNativeViewAccessible(); |
| 143 } | 196 } |
| 144 | 197 |
| 198 // Check if the given point is inside |view_|. | |
| 145 gfx::Point point(x, y); | 199 gfx::Point point(x, y); |
| 146 View::ConvertPointFromScreen(view_, &point); | 200 View::ConvertPointFromScreen(view_, &point); |
| 147 if (!view_->HitTestPoint(point)) | 201 if (!view_->HitTestPoint(point)) |
| 148 return nullptr; | 202 return nullptr; |
| 149 | 203 |
| 150 // Check if the point is within any of the immediate children of this | 204 // 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 | 205 if (GetChildCount() == 0) |
| 152 // do a recursive hit test if we return anything other than |this| or NULL. | 206 return IsIgnoredView(view_) ? nullptr : GetNativeObject(); |
| 207 | |
| 208 // Check if the point is within any of the immediate children of this view. We | |
| 209 // don't have to search further because AXPlatformNodeWin will do a recursive | |
| 210 // hit test if we return anything other than GetNativeObject() or nullptr. | |
| 211 // The exception to this is if the recursion needs to travel up the tree, | |
| 212 // which may return a non-direct ancestor of the given node. | |
| 153 for (int i = view_->child_count() - 1; i >= 0; --i) { | 213 for (int i = view_->child_count() - 1; i >= 0; --i) { |
| 154 View* child_view = view_->child_at(i); | 214 View* child_view = view_->child_at(i); |
| 155 if (!child_view->visible()) | 215 NativeViewAccessibilityBase* child_nva = GetForView(child_view); |
| 156 continue; | 216 if (child_nva) { |
|
tapted
2017/04/05 06:03:33
I think this means that any webview child can't be
| |
| 157 | 217 gfx::Point point_in_child_coords(point); |
| 158 gfx::Point point_in_child_coords(point); | 218 view_->ConvertPointToTarget(view_, child_view, &point_in_child_coords); |
| 159 view_->ConvertPointToTarget(view_, child_view, &point_in_child_coords); | 219 if (!child_nva->GetData().HasStateFlag(ui::AX_STATE_INVISIBLE) && |
| 160 if (child_view->HitTestPoint(point_in_child_coords)) | 220 child_view->HitTestPoint(point_in_child_coords)) { |
| 161 return child_view->GetNativeViewAccessible(); | 221 if (child_nva->GetChildCount() == 0 && IsIgnoredView(child_view)) { |
| 222 // Multiple levels might be jumped here. | |
| 223 return child_nva->GetParent(); | |
| 224 } | |
| 225 return child_nva->GetNativeObject(); | |
| 226 } | |
| 227 } | |
| 162 } | 228 } |
| 163 | 229 |
| 164 // If it's not inside any of our children, it's inside this view. | 230 // If it's not inside any of our children, it's inside this view. |
| 165 return GetNativeObject(); | 231 return GetNativeObject(); |
| 166 } | 232 } |
| 167 | 233 |
| 168 gfx::NativeViewAccessible NativeViewAccessibilityBase::GetFocus() { | 234 gfx::NativeViewAccessible NativeViewAccessibilityBase::GetFocus() { |
| 169 FocusManager* focus_manager = view_->GetFocusManager(); | 235 FocusManager* focus_manager = view_->GetFocusManager(); |
| 170 View* focused_view = | 236 View* focused_view = |
| 171 focus_manager ? focus_manager->GetFocusedView() : nullptr; | 237 focus_manager ? focus_manager->GetFocusedView() : nullptr; |
| (...skipping 46 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 218 } | 284 } |
| 219 } | 285 } |
| 220 | 286 |
| 221 void NativeViewAccessibilityBase::SetParentWidget(Widget* parent_widget) { | 287 void NativeViewAccessibilityBase::SetParentWidget(Widget* parent_widget) { |
| 222 if (parent_widget_) | 288 if (parent_widget_) |
| 223 parent_widget_->RemoveObserver(this); | 289 parent_widget_->RemoveObserver(this); |
| 224 parent_widget_ = parent_widget; | 290 parent_widget_ = parent_widget; |
| 225 parent_widget_->AddObserver(this); | 291 parent_widget_->AddObserver(this); |
| 226 } | 292 } |
| 227 | 293 |
| 294 // static | |
| 295 NativeViewAccessibilityBase* NativeViewAccessibilityBase::GetForView( | |
| 296 View* view) { | |
| 297 // Retrieving the gfx::NativeViewAccessible also ensures that a | |
| 298 // NativeViewAccessibility exists for the given View. | |
| 299 gfx::NativeViewAccessible native_object = view->GetNativeViewAccessible(); | |
| 300 ui::AXPlatformNode* node = | |
| 301 ui::AXPlatformNode::FromNativeViewAccessible(native_object); | |
| 302 // WebViews with web contents have their own NativeViewAccessible | |
| 303 // implementation, so |node| will be null if |view| is a WebView. | |
| 304 if (node) | |
| 305 return static_cast<NativeViewAccessibilityBase*>(node->GetDelegate()); | |
| 306 return nullptr; | |
| 307 } | |
| 308 | |
| 309 gfx::NativeViewAccessible | |
| 310 NativeViewAccessibilityBase::GetNativeViewAccessibleForWidget() { | |
|
tapted
2017/04/05 06:03:33
I think this needs an override in NativeViewAccess
Patti Lor
2017/04/11 05:40:45
I'm confused, can you explain why? A call to NVAWi
| |
| 311 if (parent_widget_) | |
| 312 return parent_widget_->GetRootView()->GetNativeViewAccessible(); | |
| 313 return nullptr; | |
| 314 } | |
| 315 | |
| 228 gfx::RectF NativeViewAccessibilityBase::GetBoundsInScreen() const { | 316 gfx::RectF NativeViewAccessibilityBase::GetBoundsInScreen() const { |
| 229 return gfx::RectF(view_->GetBoundsInScreen()); | 317 return gfx::RectF(view_->GetBoundsInScreen()); |
| 230 } | 318 } |
| 231 | 319 |
| 232 void NativeViewAccessibilityBase::PopulateChildWidgetVector( | 320 void NativeViewAccessibilityBase::PopulateChildWidgetVector( |
| 233 std::vector<Widget*>* result_child_widgets) { | 321 std::vector<Widget*>* result_child_widgets) { |
| 234 // Only attach child widgets to the root view. | 322 // Only attach child widgets to the root view. |
| 235 Widget* widget = view_->GetWidget(); | 323 Widget* widget = view_->GetWidget(); |
| 236 // Note that during window close, a Widget may exist in a state where it has | 324 // Note that during window close, a Widget may exist in a state where it has |
| 237 // no NativeView, but hasn't yet torn down its view hierarchy. | 325 // no NativeView, but hasn't yet torn down its view hierarchy. |
| 238 if (!widget || !widget->GetNativeView() || widget->GetRootView() != view_) | 326 if (!widget || !widget->GetNativeView() || widget->GetRootView() != view_) |
| 239 return; | 327 return; |
| 240 | 328 |
| 241 std::set<Widget*> child_widgets; | 329 std::set<Widget*> child_widgets; |
| 242 Widget::GetAllOwnedWidgets(widget->GetNativeView(), &child_widgets); | 330 Widget::GetAllOwnedWidgets(widget->GetNativeView(), &child_widgets); |
| 243 for (auto iter = child_widgets.begin(); iter != child_widgets.end(); ++iter) { | 331 for (auto iter = child_widgets.begin(); iter != child_widgets.end(); ++iter) { |
| 244 Widget* child_widget = *iter; | 332 Widget* child_widget = *iter; |
| 245 DCHECK_NE(widget, child_widget); | 333 DCHECK_NE(widget, child_widget); |
| 246 | 334 |
| 247 if (!child_widget->IsVisible()) | 335 if (!child_widget->IsVisible()) |
| 248 continue; | 336 continue; |
| 249 | 337 |
| 250 if (widget->GetNativeWindowProperty(kWidgetNativeViewHostKey)) | 338 if (widget->GetNativeWindowProperty(kWidgetNativeViewHostKey)) |
| 251 continue; | 339 continue; |
| 252 | 340 |
| 253 gfx::NativeViewAccessible child_widget_accessible = | 341 NativeViewAccessibilityBase* child_widget_view_accessibility = |
| 254 child_widget->GetRootView()->GetNativeViewAccessible(); | 342 GetForView(child_widget->GetRootView()); |
| 255 ui::AXPlatformNode* child_widget_platform_node = | 343 if (child_widget_view_accessibility && |
| 256 ui::AXPlatformNode::FromNativeViewAccessible(child_widget_accessible); | 344 child_widget_view_accessibility->parent_widget() != widget) |
| 257 if (child_widget_platform_node) { | 345 child_widget_view_accessibility->SetParentWidget(widget); |
| 258 NativeViewAccessibilityBase* child_widget_view_accessibility = | |
| 259 static_cast<NativeViewAccessibilityBase*>( | |
| 260 child_widget_platform_node->GetDelegate()); | |
| 261 if (child_widget_view_accessibility->parent_widget() != widget) | |
| 262 child_widget_view_accessibility->SetParentWidget(widget); | |
| 263 } | |
| 264 | 346 |
| 265 result_child_widgets->push_back(child_widget); | 347 result_child_widgets->push_back(child_widget); |
| 266 } | 348 } |
| 267 } | 349 } |
| 268 | 350 |
| 269 } // namespace views | 351 } // namespace views |
| OLD | NEW |