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 |