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 55 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 66 return NULL; | 66 return NULL; |
| 67 return delegate_->AccessibilityGetAcceleratedWidget(); | 67 return delegate_->AccessibilityGetAcceleratedWidget(); |
| 68 } | 68 } |
| 69 | 69 |
| 70 IAccessible* BrowserAccessibilityManagerWin::GetParentIAccessible() { | 70 IAccessible* BrowserAccessibilityManagerWin::GetParentIAccessible() { |
| 71 if (!delegate_) | 71 if (!delegate_) |
| 72 return NULL; | 72 return NULL; |
| 73 return delegate_->AccessibilityGetNativeViewAccessible(); | 73 return delegate_->AccessibilityGetNativeViewAccessible(); |
| 74 } | 74 } |
| 75 | 75 |
| 76 void BrowserAccessibilityManagerWin::MaybeCallNotifyWinEvent(DWORD event, | 76 void BrowserAccessibilityManagerWin::MaybeCallNotifyWinEvent( |
| 77 LONG child_id) { | 77 DWORD event, BrowserAccessibility* node) { |
| 78 if (!delegate_) | 78 if (!delegate_) |
| 79 return; | 79 return; |
| 80 | 80 |
| 81 HWND hwnd = delegate_->AccessibilityGetAcceleratedWidget(); | 81 HWND hwnd = delegate_->AccessibilityGetAcceleratedWidget(); |
| 82 if (!hwnd) | 82 if (!hwnd) |
| 83 return; | 83 return; |
| 84 | 84 |
| 85 // Inline text boxes are an internal implementation detail, we don't | |
| 86 // expose them to Windows. | |
| 87 if (node->GetRole() == ui::AX_ROLE_INLINE_TEXT_BOX) | |
| 88 return; | |
| 89 | |
| 90 // It doesn't make sense to fire a REORDER event on a leaf node; that | |
| 91 // happens when the node has internal children line inline text boxes. | |
| 92 if (event == EVENT_OBJECT_REORDER && node->PlatformIsLeaf()) | |
| 93 return; | |
| 94 | |
| 95 // Don't fire focus, or load complete notifications if the | |
| 96 // window isn't focused, because that can confuse screen readers into | |
| 97 // entering their "browse" mode. | |
| 98 if ((event == EVENT_OBJECT_FOCUS || | |
| 99 event == IA2_EVENT_DOCUMENT_LOAD_COMPLETE) && | |
| 100 (!delegate_->AccessibilityViewHasFocus())) { | |
| 101 return; | |
| 102 } | |
| 103 | |
| 104 // NVDA gets confused if we focus the main document element when it hasn't | |
| 105 // finished loading and it has no children at all, so suppress that event. | |
| 106 if (event == EVENT_OBJECT_FOCUS && | |
| 107 node == GetRoot() && | |
| 108 node->PlatformChildCount() == 0 && | |
| 109 !node->HasState(ui::AX_STATE_BUSY) && | |
| 110 !node->GetBoolAttribute(ui::AX_ATTR_DOC_LOADED)) { | |
| 111 return; | |
| 112 } | |
| 113 | |
| 114 // If a focus event is needed on the root, fire that first before | |
| 115 // this event. | |
| 116 if (event == EVENT_OBJECT_FOCUS && node == GetRoot()) | |
| 117 focus_event_on_root_needed_ = false; | |
| 118 else if (focus_event_on_root_needed_) | |
| 119 OnWindowFocused(); | |
| 120 | |
| 121 LONG child_id = node->ToBrowserAccessibilityWin()->unique_id_win(); | |
|
aboxhall
2015/01/22 21:22:09
Why is (was) this called child_id rather than node
dmazzoni
2015/01/23 23:26:13
That's the name of the ::NotifyWinEvent parameter,
| |
| 85 ::NotifyWinEvent(event, hwnd, OBJID_CLIENT, child_id); | 122 ::NotifyWinEvent(event, hwnd, OBJID_CLIENT, child_id); |
| 86 } | 123 } |
| 87 | 124 |
| 88 | |
| 89 void BrowserAccessibilityManagerWin::OnNodeCreated(ui::AXNode* node) { | 125 void BrowserAccessibilityManagerWin::OnNodeCreated(ui::AXNode* node) { |
| 90 BrowserAccessibilityManager::OnNodeCreated(node); | 126 BrowserAccessibilityManager::OnNodeCreated(node); |
| 91 BrowserAccessibility* obj = GetFromAXNode(node); | 127 BrowserAccessibility* obj = GetFromAXNode(node); |
| 92 LONG unique_id_win = obj->ToBrowserAccessibilityWin()->unique_id_win(); | 128 LONG unique_id_win = obj->ToBrowserAccessibilityWin()->unique_id_win(); |
| 93 unique_id_to_ax_id_map_[unique_id_win] = obj->GetId(); | 129 unique_id_to_ax_id_map_[unique_id_win] = obj->GetId(); |
| 94 } | 130 } |
| 95 | 131 |
| 96 void BrowserAccessibilityManagerWin::OnNodeWillBeDeleted(ui::AXNode* node) { | 132 void BrowserAccessibilityManagerWin::OnNodeWillBeDeleted(ui::AXNode* node) { |
| 97 BrowserAccessibilityManager::OnNodeWillBeDeleted(node); | 133 BrowserAccessibilityManager::OnNodeWillBeDeleted(node); |
| 98 BrowserAccessibility* obj = GetFromAXNode(node); | 134 BrowserAccessibility* obj = GetFromAXNode(node); |
| (...skipping 23 matching lines...) Expand all Loading... | |
| 122 // Try to fire a focus event on the root first and then the focused node. | 158 // Try to fire a focus event on the root first and then the focused node. |
| 123 // This will clear focus_event_on_root_needed_ if successful. | 159 // This will clear focus_event_on_root_needed_ if successful. |
| 124 if (focus_ != tree_->GetRoot()) | 160 if (focus_ != tree_->GetRoot()) |
| 125 NotifyAccessibilityEvent(ui::AX_EVENT_FOCUS, GetRoot()); | 161 NotifyAccessibilityEvent(ui::AX_EVENT_FOCUS, GetRoot()); |
| 126 BrowserAccessibilityManager::OnWindowFocused(); | 162 BrowserAccessibilityManager::OnWindowFocused(); |
| 127 } | 163 } |
| 128 | 164 |
| 129 void BrowserAccessibilityManagerWin::NotifyAccessibilityEvent( | 165 void BrowserAccessibilityManagerWin::NotifyAccessibilityEvent( |
| 130 ui::AXEvent event_type, | 166 ui::AXEvent event_type, |
| 131 BrowserAccessibility* node) { | 167 BrowserAccessibility* node) { |
| 132 if (!delegate_ || !delegate_->AccessibilityGetAcceleratedWidget()) | |
| 133 return; | |
| 134 | |
| 135 // Inline text boxes are an internal implementation detail, we don't | |
| 136 // expose them to Windows. | |
| 137 if (node->GetRole() == ui::AX_ROLE_INLINE_TEXT_BOX) | |
| 138 return; | |
| 139 | |
| 140 // Don't fire focus, blur, or load complete notifications if the | |
| 141 // window isn't focused, because that can confuse screen readers into | |
| 142 // entering their "browse" mode. | |
| 143 if ((event_type == ui::AX_EVENT_FOCUS || | |
| 144 event_type == ui::AX_EVENT_BLUR || | |
| 145 event_type == ui::AX_EVENT_LOAD_COMPLETE) && | |
| 146 (!delegate_ || !delegate_->AccessibilityViewHasFocus())) { | |
| 147 return; | |
| 148 } | |
| 149 | |
| 150 // NVDA gets confused if we focus the main document element when it hasn't | |
| 151 // finished loading and it has no children at all, so suppress that event. | |
| 152 if (event_type == ui::AX_EVENT_FOCUS && | |
| 153 node == GetRoot() && | |
| 154 node->PlatformChildCount() == 0 && | |
| 155 !node->HasState(ui::AX_STATE_BUSY) && | |
| 156 !node->GetBoolAttribute(ui::AX_ATTR_DOC_LOADED)) { | |
| 157 return; | |
| 158 } | |
| 159 | |
| 160 // If a focus event is needed on the root, fire that first before | |
| 161 // this event. | |
| 162 if (event_type == ui::AX_EVENT_FOCUS && node == GetRoot()) | |
| 163 focus_event_on_root_needed_ = false; | |
| 164 else if (focus_event_on_root_needed_) | |
| 165 OnWindowFocused(); | |
| 166 | |
| 167 LONG event_id = EVENT_MIN; | 168 LONG event_id = EVENT_MIN; |
| 168 switch (event_type) { | 169 switch (event_type) { |
| 169 case ui::AX_EVENT_ACTIVEDESCENDANTCHANGED: | 170 case ui::AX_EVENT_ACTIVEDESCENDANTCHANGED: |
| 170 event_id = IA2_EVENT_ACTIVE_DESCENDANT_CHANGED; | 171 event_id = IA2_EVENT_ACTIVE_DESCENDANT_CHANGED; |
| 171 break; | 172 break; |
| 172 case ui::AX_EVENT_ALERT: | 173 case ui::AX_EVENT_ALERT: |
| 173 event_id = EVENT_SYSTEM_ALERT; | 174 event_id = EVENT_SYSTEM_ALERT; |
| 174 break; | 175 break; |
| 175 case ui::AX_EVENT_ARIA_ATTRIBUTE_CHANGED: | |
| 176 event_id = IA2_EVENT_OBJECT_ATTRIBUTE_CHANGED; | |
| 177 break; | |
| 178 case ui::AX_EVENT_AUTOCORRECTION_OCCURED: | 176 case ui::AX_EVENT_AUTOCORRECTION_OCCURED: |
| 179 event_id = IA2_EVENT_OBJECT_ATTRIBUTE_CHANGED; | 177 event_id = IA2_EVENT_OBJECT_ATTRIBUTE_CHANGED; |
| 180 break; | 178 break; |
| 181 case ui::AX_EVENT_BLUR: | 179 case ui::AX_EVENT_BLUR: |
| 182 // Equivalent to focus on the root. | 180 // Equivalent to focus on the root. |
| 183 event_id = EVENT_OBJECT_FOCUS; | 181 event_id = EVENT_OBJECT_FOCUS; |
| 184 node = GetRoot(); | 182 node = GetRoot(); |
| 185 break; | 183 break; |
| 186 case ui::AX_EVENT_CHECKED_STATE_CHANGED: | |
| 187 event_id = EVENT_OBJECT_STATECHANGE; | |
| 188 break; | |
| 189 case ui::AX_EVENT_CHILDREN_CHANGED: | 184 case ui::AX_EVENT_CHILDREN_CHANGED: |
| 190 event_id = EVENT_OBJECT_REORDER; | 185 event_id = EVENT_OBJECT_REORDER; |
| 191 break; | 186 break; |
| 192 case ui::AX_EVENT_FOCUS: | 187 case ui::AX_EVENT_FOCUS: |
| 193 event_id = EVENT_OBJECT_FOCUS; | 188 event_id = EVENT_OBJECT_FOCUS; |
| 194 break; | 189 break; |
| 195 case ui::AX_EVENT_INVALID_STATUS_CHANGED: | |
| 196 event_id = EVENT_OBJECT_STATECHANGE; | |
| 197 break; | |
| 198 case ui::AX_EVENT_LIVE_REGION_CHANGED: | 190 case ui::AX_EVENT_LIVE_REGION_CHANGED: |
| 199 if (node->GetBoolAttribute(ui::AX_ATTR_CONTAINER_LIVE_BUSY)) | 191 if (node->GetBoolAttribute(ui::AX_ATTR_CONTAINER_LIVE_BUSY)) |
| 200 return; | 192 return; |
| 201 event_id = EVENT_OBJECT_LIVEREGIONCHANGED; | 193 event_id = EVENT_OBJECT_LIVEREGIONCHANGED; |
| 202 break; | 194 break; |
| 203 case ui::AX_EVENT_LOAD_COMPLETE: | 195 case ui::AX_EVENT_LOAD_COMPLETE: |
| 204 event_id = IA2_EVENT_DOCUMENT_LOAD_COMPLETE; | 196 event_id = IA2_EVENT_DOCUMENT_LOAD_COMPLETE; |
| 205 break; | 197 break; |
| 206 case ui::AX_EVENT_MENU_LIST_ITEM_SELECTED: | 198 case ui::AX_EVENT_MENU_LIST_ITEM_SELECTED: |
| 207 event_id = EVENT_OBJECT_FOCUS; | 199 event_id = EVENT_OBJECT_FOCUS; |
| 208 break; | 200 break; |
| 209 case ui::AX_EVENT_MENU_LIST_VALUE_CHANGED: | 201 case ui::AX_EVENT_MENU_LIST_VALUE_CHANGED: |
| 210 event_id = EVENT_OBJECT_VALUECHANGE; | 202 event_id = EVENT_OBJECT_VALUECHANGE; |
| 211 break; | 203 break; |
| 212 case ui::AX_EVENT_HIDE: | |
| 213 event_id = EVENT_OBJECT_HIDE; | |
| 214 break; | |
| 215 case ui::AX_EVENT_SHOW: | |
| 216 event_id = EVENT_OBJECT_SHOW; | |
| 217 break; | |
| 218 case ui::AX_EVENT_SCROLL_POSITION_CHANGED: | 204 case ui::AX_EVENT_SCROLL_POSITION_CHANGED: |
| 219 event_id = EVENT_SYSTEM_SCROLLINGEND; | 205 event_id = EVENT_SYSTEM_SCROLLINGEND; |
| 220 break; | 206 break; |
| 221 case ui::AX_EVENT_SCROLLED_TO_ANCHOR: | 207 case ui::AX_EVENT_SCROLLED_TO_ANCHOR: |
| 222 event_id = EVENT_SYSTEM_SCROLLINGSTART; | 208 event_id = EVENT_SYSTEM_SCROLLINGSTART; |
| 223 break; | 209 break; |
| 224 case ui::AX_EVENT_SELECTED_CHILDREN_CHANGED: | 210 case ui::AX_EVENT_SELECTED_CHILDREN_CHANGED: |
| 225 event_id = EVENT_OBJECT_SELECTIONWITHIN; | 211 event_id = EVENT_OBJECT_SELECTIONWITHIN; |
| 226 break; | 212 break; |
| 227 case ui::AX_EVENT_TEXT_CHANGED: | |
| 228 event_id = EVENT_OBJECT_NAMECHANGE; | |
| 229 break; | |
| 230 case ui::AX_EVENT_TEXT_SELECTION_CHANGED: | 213 case ui::AX_EVENT_TEXT_SELECTION_CHANGED: |
| 231 event_id = IA2_EVENT_TEXT_CARET_MOVED; | 214 event_id = IA2_EVENT_TEXT_CARET_MOVED; |
| 232 break; | 215 break; |
| 233 case ui::AX_EVENT_VALUE_CHANGED: | |
| 234 event_id = EVENT_OBJECT_VALUECHANGE; | |
| 235 break; | |
| 236 default: | 216 default: |
| 237 // Not all WebKit accessibility events result in a Windows | 217 // Not all WebKit accessibility events result in a Windows |
| 238 // accessibility notification. | 218 // accessibility notification. |
| 239 break; | 219 break; |
| 240 } | 220 } |
| 241 | 221 |
| 242 if (event_id != EVENT_MIN) { | 222 if (event_id != EVENT_MIN) { |
| 243 // Pass the node's unique id in the |child_id| argument to NotifyWinEvent; | 223 // Pass the node's unique id in the |child_id| argument to NotifyWinEvent; |
| 244 // the AT client will then call get_accChild on the HWND's accessibility | 224 // the AT client will then call get_accChild on the HWND's accessibility |
| 245 // object and pass it that same id, which we can use to retrieve the | 225 // object and pass it that same id, which we can use to retrieve the |
| 246 // IAccessible for this node. | 226 // IAccessible for this node. |
| 247 LONG child_id = node->ToBrowserAccessibilityWin()->unique_id_win(); | 227 MaybeCallNotifyWinEvent(event_id, node); |
| 248 MaybeCallNotifyWinEvent(event_id, child_id); | |
| 249 } | 228 } |
| 250 | 229 |
| 251 // If this is a layout complete notification (sent when a container scrolls) | 230 // If this is a layout complete notification (sent when a container scrolls) |
| 252 // and there is a descendant tracked object, send a notification on it. | 231 // and there is a descendant tracked object, send a notification on it. |
| 253 // TODO(dmazzoni): remove once http://crbug.com/113483 is fixed. | 232 // TODO(dmazzoni): remove once http://crbug.com/113483 is fixed. |
| 254 if (event_type == ui::AX_EVENT_LAYOUT_COMPLETE && | 233 if (event_type == ui::AX_EVENT_LAYOUT_COMPLETE && |
| 255 tracked_scroll_object_ && | 234 tracked_scroll_object_ && |
| 256 tracked_scroll_object_->IsDescendantOf(node)) { | 235 tracked_scroll_object_->IsDescendantOf(node)) { |
| 257 MaybeCallNotifyWinEvent( | 236 MaybeCallNotifyWinEvent( |
| 258 IA2_EVENT_VISIBLE_DATA_CHANGED, | 237 IA2_EVENT_VISIBLE_DATA_CHANGED, tracked_scroll_object_); |
| 259 tracked_scroll_object_->ToBrowserAccessibilityWin()->unique_id_win()); | |
| 260 tracked_scroll_object_->Release(); | 238 tracked_scroll_object_->Release(); |
| 261 tracked_scroll_object_ = NULL; | 239 tracked_scroll_object_ = NULL; |
| 262 } | 240 } |
| 263 } | 241 } |
| 264 | 242 |
| 265 void BrowserAccessibilityManagerWin::OnAtomicUpdateFinished( | 243 void BrowserAccessibilityManagerWin::OnAtomicUpdateFinished( |
| 266 bool root_changed, | 244 bool root_changed, |
| 267 const std::vector<ui::AXTreeDelegate::Change>& changes) { | 245 const std::vector<ui::AXTreeDelegate::Change>& changes) { |
| 246 BrowserAccessibilityManager::OnAtomicUpdateFinished(root_changed, changes); | |
| 247 | |
| 268 if (root_changed) { | 248 if (root_changed) { |
| 269 // In order to make screen readers aware of the new accessibility root, | 249 // In order to make screen readers aware of the new accessibility root, |
| 270 // we need to fire a focus event on it. | 250 // we need to fire a focus event on it. |
| 271 OnWindowFocused(); | 251 OnWindowFocused(); |
| 272 } | 252 } |
| 253 | |
| 254 // Second pass. | |
|
aboxhall
2015/01/22 21:22:09
Why do we need to do a second pass?
dmazzoni
2015/01/23 23:26:13
Because updating IAccessibleText requires the chil
| |
| 255 for (size_t i = 0; i < changes.size(); ++i) { | |
| 256 BrowserAccessibility* obj = GetFromAXNode(changes[i].node); | |
| 257 if (obj) | |
| 258 obj->ToBrowserAccessibilityWin()->UpdateIAccessibleText(); | |
| 259 } | |
| 273 } | 260 } |
| 274 | 261 |
| 275 void BrowserAccessibilityManagerWin::TrackScrollingObject( | 262 void BrowserAccessibilityManagerWin::TrackScrollingObject( |
| 276 BrowserAccessibilityWin* node) { | 263 BrowserAccessibilityWin* node) { |
| 277 if (tracked_scroll_object_) | 264 if (tracked_scroll_object_) |
| 278 tracked_scroll_object_->Release(); | 265 tracked_scroll_object_->Release(); |
| 279 tracked_scroll_object_ = node; | 266 tracked_scroll_object_ = node; |
| 280 tracked_scroll_object_->AddRef(); | 267 tracked_scroll_object_->AddRef(); |
| 281 } | 268 } |
| 282 | 269 |
| 283 BrowserAccessibilityWin* BrowserAccessibilityManagerWin::GetFromUniqueIdWin( | 270 BrowserAccessibilityWin* BrowserAccessibilityManagerWin::GetFromUniqueIdWin( |
| 284 LONG unique_id_win) { | 271 LONG unique_id_win) { |
| 285 base::hash_map<LONG, int32>::iterator iter = | 272 base::hash_map<LONG, int32>::iterator iter = |
| 286 unique_id_to_ax_id_map_.find(unique_id_win); | 273 unique_id_to_ax_id_map_.find(unique_id_win); |
| 287 if (iter != unique_id_to_ax_id_map_.end()) { | 274 if (iter != unique_id_to_ax_id_map_.end()) { |
| 288 BrowserAccessibility* result = GetFromID(iter->second); | 275 BrowserAccessibility* result = GetFromID(iter->second); |
| 289 if (result) | 276 if (result) |
| 290 return result->ToBrowserAccessibilityWin(); | 277 return result->ToBrowserAccessibilityWin(); |
| 291 } | 278 } |
| 292 return NULL; | 279 return NULL; |
| 293 } | 280 } |
| 294 | 281 |
| 295 } // namespace content | 282 } // namespace content |
| OLD | NEW |