Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(30)

Side by Side Diff: content/browser/accessibility/browser_accessibility_manager_win.cc

Issue 848653002: Re-land: Send Windows accessibility events based on tree updates. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@implicit_1_tests
Patch Set: Remove logging Created 5 years, 11 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
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
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
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
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698