Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2012 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 "content/browser/accessibility/browser_accessibility_manager_win.h" | 5 #include "content/browser/accessibility/browser_accessibility_manager_win.h" |
| 6 | 6 |
| 7 #include "base/command_line.h" | 7 #include "base/command_line.h" |
| 8 #include "base/win/scoped_comptr.h" | 8 #include "base/win/scoped_comptr.h" |
| 9 #include "base/win/windows_version.h" | 9 #include "base/win/windows_version.h" |
| 10 #include "content/browser/accessibility/browser_accessibility_state_impl.h" | 10 #include "content/browser/accessibility/browser_accessibility_state_impl.h" |
| (...skipping 22 matching lines...) Expand all Loading... | |
| 33 BrowserAccessibilityManagerWin::BrowserAccessibilityManagerWin( | 33 BrowserAccessibilityManagerWin::BrowserAccessibilityManagerWin( |
| 34 LegacyRenderWidgetHostHWND* accessible_hwnd, | 34 LegacyRenderWidgetHostHWND* accessible_hwnd, |
| 35 IAccessible* parent_iaccessible, | 35 IAccessible* parent_iaccessible, |
| 36 const ui::AXTreeUpdate& initial_tree, | 36 const ui::AXTreeUpdate& initial_tree, |
| 37 BrowserAccessibilityDelegate* delegate, | 37 BrowserAccessibilityDelegate* delegate, |
| 38 BrowserAccessibilityFactory* factory) | 38 BrowserAccessibilityFactory* factory) |
| 39 : BrowserAccessibilityManager(initial_tree, delegate, factory), | 39 : BrowserAccessibilityManager(initial_tree, delegate, factory), |
| 40 parent_hwnd_(NULL), | 40 parent_hwnd_(NULL), |
| 41 parent_iaccessible_(parent_iaccessible), | 41 parent_iaccessible_(parent_iaccessible), |
| 42 tracked_scroll_object_(NULL), | 42 tracked_scroll_object_(NULL), |
| 43 accessible_hwnd_(accessible_hwnd) { | 43 accessible_hwnd_(accessible_hwnd), |
| 44 focus_event_on_root_needed_(false) { | |
| 44 ui::win::CreateATLModuleIfNeeded(); | 45 ui::win::CreateATLModuleIfNeeded(); |
| 45 if (accessible_hwnd_) { | 46 if (accessible_hwnd_) { |
| 46 accessible_hwnd_->set_browser_accessibility_manager(this); | 47 accessible_hwnd_->set_browser_accessibility_manager(this); |
| 47 parent_hwnd_ = accessible_hwnd_->GetParent(); | 48 parent_hwnd_ = accessible_hwnd_->GetParent(); |
| 48 } | 49 } |
| 49 } | 50 } |
| 50 | 51 |
| 51 BrowserAccessibilityManagerWin::~BrowserAccessibilityManagerWin() { | 52 BrowserAccessibilityManagerWin::~BrowserAccessibilityManagerWin() { |
| 52 if (tracked_scroll_object_) { | 53 if (tracked_scroll_object_) { |
| 53 tracked_scroll_object_->Release(); | 54 tracked_scroll_object_->Release(); |
| (...skipping 22 matching lines...) Expand all Loading... | |
| 76 LegacyRenderWidgetHostHWND* accessible_hwnd) { | 77 LegacyRenderWidgetHostHWND* accessible_hwnd) { |
| 77 accessible_hwnd_ = accessible_hwnd; | 78 accessible_hwnd_ = accessible_hwnd; |
| 78 if (accessible_hwnd_) { | 79 if (accessible_hwnd_) { |
| 79 accessible_hwnd_->set_browser_accessibility_manager(this); | 80 accessible_hwnd_->set_browser_accessibility_manager(this); |
| 80 parent_hwnd_ = accessible_hwnd_->GetParent(); | 81 parent_hwnd_ = accessible_hwnd_->GetParent(); |
| 81 } | 82 } |
| 82 } | 83 } |
| 83 | 84 |
| 84 void BrowserAccessibilityManagerWin::MaybeCallNotifyWinEvent(DWORD event, | 85 void BrowserAccessibilityManagerWin::MaybeCallNotifyWinEvent(DWORD event, |
| 85 LONG child_id) { | 86 LONG child_id) { |
| 87 // If on Win 7 and complete accessibility is enabled, use the fake child HWND | |
|
aboxhall
2014/06/18 18:25:29
Why has been moved above the check below?
The nam
dmazzoni
2014/06/18 22:58:33
The reason I moved it is because it didn't make se
aboxhall
2014/06/18 23:29:23
Sure, SGTM.
| |
| 88 // to use as the root of the accessibility tree. See comments above | |
| 89 // LegacyRenderWidgetHostHWND for details. | |
| 90 if (accessible_hwnd_ && | |
| 91 BrowserAccessibilityStateImpl::GetInstance()->IsAccessibleBrowser()) { | |
| 92 parent_hwnd_ = accessible_hwnd_->hwnd(); | |
| 93 parent_iaccessible_ = accessible_hwnd_->window_accessible(); | |
| 94 } | |
| 95 | |
| 86 // Don't fire events if this view isn't hooked up to its parent. | 96 // Don't fire events if this view isn't hooked up to its parent. |
| 87 if (!parent_iaccessible() || !parent_hwnd()) | 97 if (!parent_iaccessible() || !parent_hwnd()) |
|
aboxhall
2014/06/18 18:25:29
Not much point early outing here any more.
dmazzoni
2014/06/18 22:58:33
Done.
| |
| 88 return; | 98 return; |
| 89 | 99 |
| 90 // If on Win 7 and complete accessibility is enabled, use the fake child HWND | |
| 91 // to use as the root of the accessibility tree. See comments above | |
| 92 // LegacyRenderWidgetHostHWND for details. | |
| 93 if (BrowserAccessibilityStateImpl::GetInstance()->IsAccessibleBrowser()) { | |
| 94 DCHECK(accessible_hwnd_); | |
| 95 parent_hwnd_ = accessible_hwnd_->hwnd(); | |
| 96 parent_iaccessible_ = accessible_hwnd_->window_accessible(); | |
| 97 } | |
| 98 ::NotifyWinEvent(event, parent_hwnd(), OBJID_CLIENT, child_id); | 100 ::NotifyWinEvent(event, parent_hwnd(), OBJID_CLIENT, child_id); |
| 99 } | 101 } |
| 100 | 102 |
| 101 | 103 |
| 102 void BrowserAccessibilityManagerWin::OnNodeCreated(ui::AXNode* node) { | 104 void BrowserAccessibilityManagerWin::OnNodeCreated(ui::AXNode* node) { |
| 103 BrowserAccessibilityManager::OnNodeCreated(node); | 105 BrowserAccessibilityManager::OnNodeCreated(node); |
| 104 BrowserAccessibility* obj = GetFromAXNode(node); | 106 BrowserAccessibility* obj = GetFromAXNode(node); |
| 105 LONG unique_id_win = obj->ToBrowserAccessibilityWin()->unique_id_win(); | 107 LONG unique_id_win = obj->ToBrowserAccessibilityWin()->unique_id_win(); |
| 106 unique_id_to_ax_id_map_[unique_id_win] = obj->GetId(); | 108 unique_id_to_ax_id_map_[unique_id_win] = obj->GetId(); |
| 107 } | 109 } |
| 108 | 110 |
| 109 void BrowserAccessibilityManagerWin::OnNodeWillBeDeleted(ui::AXNode* node) { | 111 void BrowserAccessibilityManagerWin::OnNodeWillBeDeleted(ui::AXNode* node) { |
| 110 BrowserAccessibilityManager::OnNodeWillBeDeleted(node); | 112 BrowserAccessibilityManager::OnNodeWillBeDeleted(node); |
| 111 BrowserAccessibility* obj = GetFromAXNode(node); | 113 BrowserAccessibility* obj = GetFromAXNode(node); |
| 112 if (!obj) | 114 if (!obj) |
| 113 return; | 115 return; |
| 114 unique_id_to_ax_id_map_.erase( | 116 unique_id_to_ax_id_map_.erase( |
| 115 obj->ToBrowserAccessibilityWin()->unique_id_win()); | 117 obj->ToBrowserAccessibilityWin()->unique_id_win()); |
| 116 if (obj == tracked_scroll_object_) { | 118 if (obj == tracked_scroll_object_) { |
| 117 tracked_scroll_object_->Release(); | 119 tracked_scroll_object_->Release(); |
| 118 tracked_scroll_object_ = NULL; | 120 tracked_scroll_object_ = NULL; |
| 119 } | 121 } |
| 120 } | 122 } |
| 121 | 123 |
| 122 void BrowserAccessibilityManagerWin::OnWindowFocused() { | 124 void BrowserAccessibilityManagerWin::OnWindowFocused() { |
| 123 // Fire a focus event on the root first and then the focused node. | 125 // This is called either when this web frame gets focused, or when |
| 126 // the root of the accessibility tree changes. In both cases, we need | |
| 127 // to fire a focus event on the root and then on the focused element | |
| 128 // within the page, if different. | |
| 129 // | |
|
aboxhall
2014/06/18 18:25:29
Is the next comment a continuation of this one? It
dmazzoni
2014/06/18 22:58:33
You're right. Split into two comments: a top comme
| |
| 130 // Set this flag so that we'll keep trying to fire these focus events | |
| 131 // if they're not successful this time. | |
| 132 focus_event_on_root_needed_ = true; | |
| 133 | |
| 134 if (!delegate_ || !delegate_->AccessibilityViewHasFocus()) | |
| 135 return; | |
| 136 | |
| 137 // Try to fire a focus event on the root first and then the focused node. | |
| 138 // This will clear focus_event_on_root_needed_ if successful. | |
| 124 if (focus_ != tree_->GetRoot()) | 139 if (focus_ != tree_->GetRoot()) |
| 125 NotifyAccessibilityEvent(ui::AX_EVENT_FOCUS, GetRoot()); | 140 NotifyAccessibilityEvent(ui::AX_EVENT_FOCUS, GetRoot()); |
| 126 BrowserAccessibilityManager::OnWindowFocused(); | 141 BrowserAccessibilityManager::OnWindowFocused(); |
| 127 } | 142 } |
| 128 | 143 |
| 129 void BrowserAccessibilityManagerWin::OnWindowBlurred() { | 144 void BrowserAccessibilityManagerWin::OnWindowBlurred() { |
| 130 // Fire a blur event on the focused node first and then the root. | 145 // Windows doesn't have a specific blur event. |
|
aboxhall
2014/06/18 18:25:29
Inline this?
Was what was there previously wrong/
dmazzoni
2014/06/18 22:58:32
Done.
On the web, a blur event just means to focu
| |
| 131 BrowserAccessibilityManager::OnWindowBlurred(); | |
| 132 if (focus_ != tree_->GetRoot()) | |
| 133 NotifyAccessibilityEvent(ui::AX_EVENT_BLUR, GetRoot()); | |
| 134 } | 146 } |
| 135 | 147 |
| 136 void BrowserAccessibilityManagerWin::NotifyAccessibilityEvent( | 148 void BrowserAccessibilityManagerWin::NotifyAccessibilityEvent( |
| 137 ui::AXEvent event_type, | 149 ui::AXEvent event_type, |
| 138 BrowserAccessibility* node) { | 150 BrowserAccessibility* node) { |
| 139 if (node->GetRole() == ui::AX_ROLE_INLINE_TEXT_BOX) | 151 if (node->GetRole() == ui::AX_ROLE_INLINE_TEXT_BOX) |
| 140 return; | 152 return; |
| 141 | 153 |
| 154 // Don't fire focus, blur, or load complete notifications if the | |
| 155 // window isn't focused, because that can confuse screen readers into | |
| 156 // entering their "browse" mode. | |
| 157 if ((event_type == ui::AX_EVENT_FOCUS || | |
| 158 event_type == ui::AX_EVENT_BLUR || | |
| 159 event_type == ui::AX_EVENT_LOAD_COMPLETE) && | |
| 160 (!delegate_ || !delegate_->AccessibilityViewHasFocus())) { | |
| 161 return; | |
| 162 } | |
| 163 | |
| 142 // NVDA gets confused if we focus the main document element when it hasn't | 164 // NVDA gets confused if we focus the main document element when it hasn't |
| 143 // finished loading and it has no children at all, so suppress that event. | 165 // finished loading and it has no children at all, so suppress that event. |
| 144 if (event_type == ui::AX_EVENT_FOCUS && | 166 if (event_type == ui::AX_EVENT_FOCUS && |
| 145 node == GetRoot() && | 167 node == GetRoot() && |
| 146 node->PlatformChildCount() == 0 && | 168 node->PlatformChildCount() == 0 && |
| 169 !node->HasState(ui::AX_STATE_BUSY) && | |
| 147 !node->GetBoolAttribute(ui::AX_ATTR_DOC_LOADED)) { | 170 !node->GetBoolAttribute(ui::AX_ATTR_DOC_LOADED)) { |
| 148 return; | 171 return; |
| 149 } | 172 } |
| 150 | 173 |
| 174 // If a focus event is needed on the root, fire that first before | |
| 175 // this event. | |
| 176 if (event_type == ui::AX_EVENT_FOCUS && node == GetRoot()) | |
| 177 focus_event_on_root_needed_ = false; | |
| 178 else if (focus_event_on_root_needed_) | |
| 179 OnWindowFocused(); | |
| 180 | |
| 151 LONG event_id = EVENT_MIN; | 181 LONG event_id = EVENT_MIN; |
| 152 switch (event_type) { | 182 switch (event_type) { |
| 153 case ui::AX_EVENT_ACTIVEDESCENDANTCHANGED: | 183 case ui::AX_EVENT_ACTIVEDESCENDANTCHANGED: |
| 154 event_id = IA2_EVENT_ACTIVE_DESCENDANT_CHANGED; | 184 event_id = IA2_EVENT_ACTIVE_DESCENDANT_CHANGED; |
| 155 break; | 185 break; |
| 156 case ui::AX_EVENT_ALERT: | 186 case ui::AX_EVENT_ALERT: |
| 157 event_id = EVENT_SYSTEM_ALERT; | 187 event_id = EVENT_SYSTEM_ALERT; |
| 158 break; | 188 break; |
| 159 case ui::AX_EVENT_ARIA_ATTRIBUTE_CHANGED: | 189 case ui::AX_EVENT_ARIA_ATTRIBUTE_CHANGED: |
| 160 event_id = IA2_EVENT_OBJECT_ATTRIBUTE_CHANGED; | 190 event_id = IA2_EVENT_OBJECT_ATTRIBUTE_CHANGED; |
| (...skipping 67 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 228 // accessibility notification. | 258 // accessibility notification. |
| 229 break; | 259 break; |
| 230 } | 260 } |
| 231 | 261 |
| 232 if (event_id != EVENT_MIN) { | 262 if (event_id != EVENT_MIN) { |
| 233 // Pass the node's unique id in the |child_id| argument to NotifyWinEvent; | 263 // Pass the node's unique id in the |child_id| argument to NotifyWinEvent; |
| 234 // the AT client will then call get_accChild on the HWND's accessibility | 264 // the AT client will then call get_accChild on the HWND's accessibility |
| 235 // object and pass it that same id, which we can use to retrieve the | 265 // object and pass it that same id, which we can use to retrieve the |
| 236 // IAccessible for this node. | 266 // IAccessible for this node. |
| 237 LONG child_id = node->ToBrowserAccessibilityWin()->unique_id_win(); | 267 LONG child_id = node->ToBrowserAccessibilityWin()->unique_id_win(); |
| 238 | |
| 239 // Always send a focus before a load complete. | |
| 240 if (event_type == ui::AX_EVENT_LOAD_COMPLETE) | |
| 241 MaybeCallNotifyWinEvent(EVENT_OBJECT_FOCUS, child_id); | |
| 242 MaybeCallNotifyWinEvent(event_id, child_id); | 268 MaybeCallNotifyWinEvent(event_id, child_id); |
| 243 } | 269 } |
| 244 | 270 |
| 245 // If this is a layout complete notification (sent when a container scrolls) | 271 // If this is a layout complete notification (sent when a container scrolls) |
| 246 // and there is a descendant tracked object, send a notification on it. | 272 // and there is a descendant tracked object, send a notification on it. |
| 247 // TODO(dmazzoni): remove once http://crbug.com/113483 is fixed. | 273 // TODO(dmazzoni): remove once http://crbug.com/113483 is fixed. |
| 248 if (event_type == ui::AX_EVENT_LAYOUT_COMPLETE && | 274 if (event_type == ui::AX_EVENT_LAYOUT_COMPLETE && |
| 249 tracked_scroll_object_ && | 275 tracked_scroll_object_ && |
| 250 tracked_scroll_object_->IsDescendantOf(node)) { | 276 tracked_scroll_object_->IsDescendantOf(node)) { |
| 251 MaybeCallNotifyWinEvent( | 277 MaybeCallNotifyWinEvent( |
| 252 IA2_EVENT_VISIBLE_DATA_CHANGED, | 278 IA2_EVENT_VISIBLE_DATA_CHANGED, |
| 253 tracked_scroll_object_->ToBrowserAccessibilityWin()->unique_id_win()); | 279 tracked_scroll_object_->ToBrowserAccessibilityWin()->unique_id_win()); |
| 254 tracked_scroll_object_->Release(); | 280 tracked_scroll_object_->Release(); |
| 255 tracked_scroll_object_ = NULL; | 281 tracked_scroll_object_ = NULL; |
| 256 } | 282 } |
| 257 } | 283 } |
| 258 | 284 |
| 259 void BrowserAccessibilityManagerWin::OnRootChanged(ui::AXNode* new_root) { | 285 void BrowserAccessibilityManagerWin::OnRootChanged(ui::AXNode* new_root) { |
| 260 if (delegate_ && delegate_->AccessibilityViewHasFocus()) | 286 // In order to make screen readers aware of the new accessibility root, |
| 261 NotifyAccessibilityEvent(ui::AX_EVENT_FOCUS, GetRoot()); | 287 // we need to fire a focus event on it. |
| 288 OnWindowFocused(); | |
| 262 } | 289 } |
| 263 | 290 |
| 264 void BrowserAccessibilityManagerWin::TrackScrollingObject( | 291 void BrowserAccessibilityManagerWin::TrackScrollingObject( |
| 265 BrowserAccessibilityWin* node) { | 292 BrowserAccessibilityWin* node) { |
| 266 if (tracked_scroll_object_) | 293 if (tracked_scroll_object_) |
| 267 tracked_scroll_object_->Release(); | 294 tracked_scroll_object_->Release(); |
| 268 tracked_scroll_object_ = node; | 295 tracked_scroll_object_ = node; |
| 269 tracked_scroll_object_->AddRef(); | 296 tracked_scroll_object_->AddRef(); |
| 270 } | 297 } |
| 271 | 298 |
| (...skipping 14 matching lines...) Expand all Loading... | |
| 286 // |parent_iaccessible_| are no longer valid either, since they were | 313 // |parent_iaccessible_| are no longer valid either, since they were |
| 287 // derived from AccessibleHWND. We don't have to restore them to | 314 // derived from AccessibleHWND. We don't have to restore them to |
| 288 // previous values, though, because this should only happen | 315 // previous values, though, because this should only happen |
| 289 // during the destruct sequence for this window. | 316 // during the destruct sequence for this window. |
| 290 accessible_hwnd_ = NULL; | 317 accessible_hwnd_ = NULL; |
| 291 parent_hwnd_ = NULL; | 318 parent_hwnd_ = NULL; |
| 292 parent_iaccessible_ = NULL; | 319 parent_iaccessible_ = NULL; |
| 293 } | 320 } |
| 294 | 321 |
| 295 } // namespace content | 322 } // namespace content |
| OLD | NEW |