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) { |
86 // Don't fire events if this view isn't hooked up to its parent. | |
87 if (!parent_iaccessible() || !parent_hwnd()) | |
88 return; | |
89 | |
90 // If on Win 7 and complete accessibility is enabled, use the fake child HWND | 87 // 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 | 88 // to use as the root of the accessibility tree. See comments above |
92 // LegacyRenderWidgetHostHWND for details. | 89 // LegacyRenderWidgetHostHWND for details. |
93 if (BrowserAccessibilityStateImpl::GetInstance()->IsAccessibleBrowser()) { | 90 if (accessible_hwnd_ && |
94 DCHECK(accessible_hwnd_); | 91 BrowserAccessibilityStateImpl::GetInstance()->IsAccessibleBrowser()) { |
95 parent_hwnd_ = accessible_hwnd_->hwnd(); | 92 parent_hwnd_ = accessible_hwnd_->hwnd(); |
96 parent_iaccessible_ = accessible_hwnd_->window_accessible(); | 93 parent_iaccessible_ = accessible_hwnd_->window_accessible(); |
97 } | 94 } |
98 ::NotifyWinEvent(event, parent_hwnd(), OBJID_CLIENT, child_id); | 95 |
| 96 // Only fire events if this view is hooked up to its parent. |
| 97 if (parent_iaccessible() && parent_hwnd()) |
| 98 ::NotifyWinEvent(event, parent_hwnd(), OBJID_CLIENT, child_id); |
99 } | 99 } |
100 | 100 |
101 | 101 |
102 void BrowserAccessibilityManagerWin::OnNodeCreated(ui::AXNode* node) { | 102 void BrowserAccessibilityManagerWin::OnNodeCreated(ui::AXNode* node) { |
103 BrowserAccessibilityManager::OnNodeCreated(node); | 103 BrowserAccessibilityManager::OnNodeCreated(node); |
104 BrowserAccessibility* obj = GetFromAXNode(node); | 104 BrowserAccessibility* obj = GetFromAXNode(node); |
105 LONG unique_id_win = obj->ToBrowserAccessibilityWin()->unique_id_win(); | 105 LONG unique_id_win = obj->ToBrowserAccessibilityWin()->unique_id_win(); |
106 unique_id_to_ax_id_map_[unique_id_win] = obj->GetId(); | 106 unique_id_to_ax_id_map_[unique_id_win] = obj->GetId(); |
107 } | 107 } |
108 | 108 |
109 void BrowserAccessibilityManagerWin::OnNodeWillBeDeleted(ui::AXNode* node) { | 109 void BrowserAccessibilityManagerWin::OnNodeWillBeDeleted(ui::AXNode* node) { |
110 BrowserAccessibilityManager::OnNodeWillBeDeleted(node); | 110 BrowserAccessibilityManager::OnNodeWillBeDeleted(node); |
111 BrowserAccessibility* obj = GetFromAXNode(node); | 111 BrowserAccessibility* obj = GetFromAXNode(node); |
112 if (!obj) | 112 if (!obj) |
113 return; | 113 return; |
114 unique_id_to_ax_id_map_.erase( | 114 unique_id_to_ax_id_map_.erase( |
115 obj->ToBrowserAccessibilityWin()->unique_id_win()); | 115 obj->ToBrowserAccessibilityWin()->unique_id_win()); |
116 if (obj == tracked_scroll_object_) { | 116 if (obj == tracked_scroll_object_) { |
117 tracked_scroll_object_->Release(); | 117 tracked_scroll_object_->Release(); |
118 tracked_scroll_object_ = NULL; | 118 tracked_scroll_object_ = NULL; |
119 } | 119 } |
120 } | 120 } |
121 | 121 |
122 void BrowserAccessibilityManagerWin::OnWindowFocused() { | 122 void BrowserAccessibilityManagerWin::OnWindowFocused() { |
123 // Fire a focus event on the root first and then the focused node. | 123 // This is called either when this web frame gets focused, or when |
| 124 // the root of the accessibility tree changes. In both cases, we need |
| 125 // to fire a focus event on the root and then on the focused element |
| 126 // within the page, if different. |
| 127 |
| 128 // Set this flag so that we'll keep trying to fire these focus events |
| 129 // if they're not successful this time. |
| 130 focus_event_on_root_needed_ = true; |
| 131 |
| 132 if (!delegate_ || !delegate_->AccessibilityViewHasFocus()) |
| 133 return; |
| 134 |
| 135 // Try to fire a focus event on the root first and then the focused node. |
| 136 // This will clear focus_event_on_root_needed_ if successful. |
124 if (focus_ != tree_->GetRoot()) | 137 if (focus_ != tree_->GetRoot()) |
125 NotifyAccessibilityEvent(ui::AX_EVENT_FOCUS, GetRoot()); | 138 NotifyAccessibilityEvent(ui::AX_EVENT_FOCUS, GetRoot()); |
126 BrowserAccessibilityManager::OnWindowFocused(); | 139 BrowserAccessibilityManager::OnWindowFocused(); |
127 } | 140 } |
128 | 141 |
129 void BrowserAccessibilityManagerWin::OnWindowBlurred() { | |
130 // Fire a blur event on the focused node first and then the root. | |
131 BrowserAccessibilityManager::OnWindowBlurred(); | |
132 if (focus_ != tree_->GetRoot()) | |
133 NotifyAccessibilityEvent(ui::AX_EVENT_BLUR, GetRoot()); | |
134 } | |
135 | |
136 void BrowserAccessibilityManagerWin::NotifyAccessibilityEvent( | 142 void BrowserAccessibilityManagerWin::NotifyAccessibilityEvent( |
137 ui::AXEvent event_type, | 143 ui::AXEvent event_type, |
138 BrowserAccessibility* node) { | 144 BrowserAccessibility* node) { |
139 if (node->GetRole() == ui::AX_ROLE_INLINE_TEXT_BOX) | 145 if (node->GetRole() == ui::AX_ROLE_INLINE_TEXT_BOX) |
140 return; | 146 return; |
141 | 147 |
| 148 // Don't fire focus, blur, or load complete notifications if the |
| 149 // window isn't focused, because that can confuse screen readers into |
| 150 // entering their "browse" mode. |
| 151 if ((event_type == ui::AX_EVENT_FOCUS || |
| 152 event_type == ui::AX_EVENT_BLUR || |
| 153 event_type == ui::AX_EVENT_LOAD_COMPLETE) && |
| 154 (!delegate_ || !delegate_->AccessibilityViewHasFocus())) { |
| 155 return; |
| 156 } |
| 157 |
142 // NVDA gets confused if we focus the main document element when it hasn't | 158 // 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. | 159 // finished loading and it has no children at all, so suppress that event. |
144 if (event_type == ui::AX_EVENT_FOCUS && | 160 if (event_type == ui::AX_EVENT_FOCUS && |
145 node == GetRoot() && | 161 node == GetRoot() && |
146 node->PlatformChildCount() == 0 && | 162 node->PlatformChildCount() == 0 && |
| 163 !node->HasState(ui::AX_STATE_BUSY) && |
147 !node->GetBoolAttribute(ui::AX_ATTR_DOC_LOADED)) { | 164 !node->GetBoolAttribute(ui::AX_ATTR_DOC_LOADED)) { |
148 return; | 165 return; |
149 } | 166 } |
150 | 167 |
| 168 // If a focus event is needed on the root, fire that first before |
| 169 // this event. |
| 170 if (event_type == ui::AX_EVENT_FOCUS && node == GetRoot()) |
| 171 focus_event_on_root_needed_ = false; |
| 172 else if (focus_event_on_root_needed_) |
| 173 OnWindowFocused(); |
| 174 |
151 LONG event_id = EVENT_MIN; | 175 LONG event_id = EVENT_MIN; |
152 switch (event_type) { | 176 switch (event_type) { |
153 case ui::AX_EVENT_ACTIVEDESCENDANTCHANGED: | 177 case ui::AX_EVENT_ACTIVEDESCENDANTCHANGED: |
154 event_id = IA2_EVENT_ACTIVE_DESCENDANT_CHANGED; | 178 event_id = IA2_EVENT_ACTIVE_DESCENDANT_CHANGED; |
155 break; | 179 break; |
156 case ui::AX_EVENT_ALERT: | 180 case ui::AX_EVENT_ALERT: |
157 event_id = EVENT_SYSTEM_ALERT; | 181 event_id = EVENT_SYSTEM_ALERT; |
158 break; | 182 break; |
159 case ui::AX_EVENT_ARIA_ATTRIBUTE_CHANGED: | 183 case ui::AX_EVENT_ARIA_ATTRIBUTE_CHANGED: |
160 event_id = IA2_EVENT_OBJECT_ATTRIBUTE_CHANGED; | 184 event_id = IA2_EVENT_OBJECT_ATTRIBUTE_CHANGED; |
(...skipping 67 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
228 // accessibility notification. | 252 // accessibility notification. |
229 break; | 253 break; |
230 } | 254 } |
231 | 255 |
232 if (event_id != EVENT_MIN) { | 256 if (event_id != EVENT_MIN) { |
233 // Pass the node's unique id in the |child_id| argument to NotifyWinEvent; | 257 // 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 | 258 // 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 | 259 // object and pass it that same id, which we can use to retrieve the |
236 // IAccessible for this node. | 260 // IAccessible for this node. |
237 LONG child_id = node->ToBrowserAccessibilityWin()->unique_id_win(); | 261 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); | 262 MaybeCallNotifyWinEvent(event_id, child_id); |
243 } | 263 } |
244 | 264 |
245 // If this is a layout complete notification (sent when a container scrolls) | 265 // 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. | 266 // and there is a descendant tracked object, send a notification on it. |
247 // TODO(dmazzoni): remove once http://crbug.com/113483 is fixed. | 267 // TODO(dmazzoni): remove once http://crbug.com/113483 is fixed. |
248 if (event_type == ui::AX_EVENT_LAYOUT_COMPLETE && | 268 if (event_type == ui::AX_EVENT_LAYOUT_COMPLETE && |
249 tracked_scroll_object_ && | 269 tracked_scroll_object_ && |
250 tracked_scroll_object_->IsDescendantOf(node)) { | 270 tracked_scroll_object_->IsDescendantOf(node)) { |
251 MaybeCallNotifyWinEvent( | 271 MaybeCallNotifyWinEvent( |
252 IA2_EVENT_VISIBLE_DATA_CHANGED, | 272 IA2_EVENT_VISIBLE_DATA_CHANGED, |
253 tracked_scroll_object_->ToBrowserAccessibilityWin()->unique_id_win()); | 273 tracked_scroll_object_->ToBrowserAccessibilityWin()->unique_id_win()); |
254 tracked_scroll_object_->Release(); | 274 tracked_scroll_object_->Release(); |
255 tracked_scroll_object_ = NULL; | 275 tracked_scroll_object_ = NULL; |
256 } | 276 } |
257 } | 277 } |
258 | 278 |
259 void BrowserAccessibilityManagerWin::OnRootChanged(ui::AXNode* new_root) { | 279 void BrowserAccessibilityManagerWin::OnRootChanged(ui::AXNode* new_root) { |
260 if (delegate_ && delegate_->AccessibilityViewHasFocus()) | 280 // In order to make screen readers aware of the new accessibility root, |
261 NotifyAccessibilityEvent(ui::AX_EVENT_FOCUS, GetRoot()); | 281 // we need to fire a focus event on it. |
| 282 OnWindowFocused(); |
262 } | 283 } |
263 | 284 |
264 void BrowserAccessibilityManagerWin::TrackScrollingObject( | 285 void BrowserAccessibilityManagerWin::TrackScrollingObject( |
265 BrowserAccessibilityWin* node) { | 286 BrowserAccessibilityWin* node) { |
266 if (tracked_scroll_object_) | 287 if (tracked_scroll_object_) |
267 tracked_scroll_object_->Release(); | 288 tracked_scroll_object_->Release(); |
268 tracked_scroll_object_ = node; | 289 tracked_scroll_object_ = node; |
269 tracked_scroll_object_->AddRef(); | 290 tracked_scroll_object_->AddRef(); |
270 } | 291 } |
271 | 292 |
(...skipping 14 matching lines...) Expand all Loading... |
286 // |parent_iaccessible_| are no longer valid either, since they were | 307 // |parent_iaccessible_| are no longer valid either, since they were |
287 // derived from AccessibleHWND. We don't have to restore them to | 308 // derived from AccessibleHWND. We don't have to restore them to |
288 // previous values, though, because this should only happen | 309 // previous values, though, because this should only happen |
289 // during the destruct sequence for this window. | 310 // during the destruct sequence for this window. |
290 accessible_hwnd_ = NULL; | 311 accessible_hwnd_ = NULL; |
291 parent_hwnd_ = NULL; | 312 parent_hwnd_ = NULL; |
292 parent_iaccessible_ = NULL; | 313 parent_iaccessible_ = NULL; |
293 } | 314 } |
294 | 315 |
295 } // namespace content | 316 } // namespace content |
OLD | NEW |