| 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 <stddef.h> | 7 #include <stddef.h> |
| 8 #include <stdint.h> | 8 #include <stdint.h> |
| 9 | 9 |
| 10 #include <vector> | 10 #include <vector> |
| 11 | 11 |
| 12 #include "base/command_line.h" | 12 #include "base/command_line.h" |
| 13 #include "base/win/scoped_comptr.h" | 13 #include "base/win/scoped_comptr.h" |
| 14 #include "base/win/windows_version.h" | 14 #include "base/win/windows_version.h" |
| 15 #include "content/browser/accessibility/browser_accessibility_event_win.h" |
| 15 #include "content/browser/accessibility/browser_accessibility_state_impl.h" | 16 #include "content/browser/accessibility/browser_accessibility_state_impl.h" |
| 16 #include "content/browser/accessibility/browser_accessibility_win.h" | 17 #include "content/browser/accessibility/browser_accessibility_win.h" |
| 17 #include "content/browser/renderer_host/legacy_render_widget_host_win.h" | 18 #include "content/browser/renderer_host/legacy_render_widget_host_win.h" |
| 18 #include "content/common/accessibility_messages.h" | 19 #include "content/common/accessibility_messages.h" |
| 19 #include "ui/base/win/atl_module.h" | 20 #include "ui/base/win/atl_module.h" |
| 20 | 21 |
| 21 namespace content { | 22 namespace content { |
| 22 | 23 |
| 23 // static | 24 // static |
| 24 BrowserAccessibilityManager* BrowserAccessibilityManager::Create( | 25 BrowserAccessibilityManager* BrowserAccessibilityManager::Create( |
| (...skipping 56 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 81 return delegate->AccessibilityGetAcceleratedWidget(); | 82 return delegate->AccessibilityGetAcceleratedWidget(); |
| 82 } | 83 } |
| 83 | 84 |
| 84 IAccessible* BrowserAccessibilityManagerWin::GetParentIAccessible() { | 85 IAccessible* BrowserAccessibilityManagerWin::GetParentIAccessible() { |
| 85 BrowserAccessibilityDelegate* delegate = GetDelegateFromRootManager(); | 86 BrowserAccessibilityDelegate* delegate = GetDelegateFromRootManager(); |
| 86 if (!delegate) | 87 if (!delegate) |
| 87 return NULL; | 88 return NULL; |
| 88 return delegate->AccessibilityGetNativeViewAccessible(); | 89 return delegate->AccessibilityGetNativeViewAccessible(); |
| 89 } | 90 } |
| 90 | 91 |
| 91 void BrowserAccessibilityManagerWin::MaybeCallNotifyWinEvent( | |
| 92 DWORD event, BrowserAccessibility* node) { | |
| 93 BrowserAccessibilityDelegate* delegate = | |
| 94 node->manager()->GetDelegateFromRootManager(); | |
| 95 if (!delegate) { | |
| 96 // This line and other LOG(WARNING) lines are temporary, to debug | |
| 97 // flaky failures in DumpAccessibilityEvent* tests. | |
| 98 // http://crbug.com/440579 | |
| 99 DLOG(WARNING) << "Not firing AX event because of no delegate"; | |
| 100 return; | |
| 101 } | |
| 102 | |
| 103 if (!node->IsNative()) | |
| 104 return; | |
| 105 | |
| 106 HWND hwnd = delegate->AccessibilityGetAcceleratedWidget(); | |
| 107 if (!hwnd) { | |
| 108 DLOG(WARNING) << "Not firing AX event because of no hwnd"; | |
| 109 return; | |
| 110 } | |
| 111 | |
| 112 // Inline text boxes are an internal implementation detail, we don't | |
| 113 // expose them to Windows. | |
| 114 if (node->GetRole() == ui::AX_ROLE_INLINE_TEXT_BOX) | |
| 115 return; | |
| 116 | |
| 117 // It doesn't make sense to fire a REORDER event on a leaf node; that | |
| 118 // happens when the node has internal children line inline text boxes. | |
| 119 if (event == EVENT_OBJECT_REORDER && node->PlatformChildCount() == 0) | |
| 120 return; | |
| 121 | |
| 122 // Pass the negation of this node's unique id in the |child_id| | |
| 123 // argument to NotifyWinEvent; the AT client will then call get_accChild | |
| 124 // on the HWND's accessibility object and pass it that same id, which | |
| 125 // we can use to retrieve the IAccessible for this node. | |
| 126 LONG child_id = -node->unique_id(); | |
| 127 ::NotifyWinEvent(event, hwnd, OBJID_CLIENT, child_id); | |
| 128 } | |
| 129 | |
| 130 void BrowserAccessibilityManagerWin::OnIAccessible2Used() { | 92 void BrowserAccessibilityManagerWin::OnIAccessible2Used() { |
| 131 BrowserAccessibilityStateImpl::GetInstance()->OnScreenReaderDetected(); | 93 BrowserAccessibilityStateImpl::GetInstance()->OnScreenReaderDetected(); |
| 132 } | 94 } |
| 133 | 95 |
| 134 void BrowserAccessibilityManagerWin::UserIsReloading() { | 96 void BrowserAccessibilityManagerWin::UserIsReloading() { |
| 135 if (GetRoot()) | 97 if (GetRoot()) { |
| 136 MaybeCallNotifyWinEvent(IA2_EVENT_DOCUMENT_RELOAD, GetRoot()); | 98 (new BrowserAccessibilityEventWin( |
| 99 BrowserAccessibilityEvent::FromRenderFrameHost, |
| 100 ui::AX_EVENT_NONE, |
| 101 IA2_EVENT_DOCUMENT_RELOAD, |
| 102 GetRoot()))->Fire(); |
| 103 } |
| 137 } | 104 } |
| 138 | 105 |
| 139 void BrowserAccessibilityManagerWin::NotifyAccessibilityEvent( | 106 void BrowserAccessibilityManagerWin::NotifyAccessibilityEvent( |
| 107 BrowserAccessibilityEvent::Source source, |
| 140 ui::AXEvent event_type, | 108 ui::AXEvent event_type, |
| 141 BrowserAccessibility* node) { | 109 BrowserAccessibility* node) { |
| 110 if (event_type == ui::AX_EVENT_BLUR) { |
| 111 // Equivalent to focus on the root. |
| 112 event_type = ui::AX_EVENT_FOCUS; |
| 113 node = GetRoot(); |
| 114 } |
| 115 |
| 116 if (event_type == ui::AX_EVENT_DOCUMENT_SELECTION_CHANGED) { |
| 117 // Fire the event on the object where the focus of the selection is. |
| 118 int32_t focus_id = GetTreeData().sel_focus_object_id; |
| 119 BrowserAccessibility* focus_object = GetFromID(focus_id); |
| 120 if (focus_object) { |
| 121 (new BrowserAccessibilityEventWin( |
| 122 source, |
| 123 ui::AX_EVENT_NONE, |
| 124 IA2_EVENT_TEXT_CARET_MOVED, |
| 125 focus_object))->Fire(); |
| 126 return; |
| 127 } |
| 128 } |
| 129 |
| 130 BrowserAccessibilityManager::NotifyAccessibilityEvent( |
| 131 source, event_type, node); |
| 132 } |
| 133 |
| 134 BrowserAccessibilityEvent::Result |
| 135 BrowserAccessibilityManagerWin::FireWinAccessibilityEvent( |
| 136 BrowserAccessibilityEventWin* event) { |
| 137 const BrowserAccessibility* target = event->target(); |
| 138 ui::AXEvent event_type = event->event_type(); |
| 139 LONG win_event_type = event->win_event_type(); |
| 140 |
| 142 BrowserAccessibilityDelegate* root_delegate = GetDelegateFromRootManager(); | 141 BrowserAccessibilityDelegate* root_delegate = GetDelegateFromRootManager(); |
| 143 if (!root_delegate || !root_delegate->AccessibilityGetAcceleratedWidget()) { | 142 if (!root_delegate) |
| 144 DLOG(WARNING) << "Not firing AX event because of no root_delegate or hwnd"; | 143 return BrowserAccessibilityEvent::FailedBecauseFrameIsDetached; |
| 145 return; | 144 |
| 146 } | 145 HWND hwnd = root_delegate->AccessibilityGetAcceleratedWidget(); |
| 146 if (!hwnd) |
| 147 return BrowserAccessibilityEvent::FailedBecauseNoWindow; |
| 147 | 148 |
| 148 // Don't fire events when this document might be stale as the user has | 149 // Don't fire events when this document might be stale as the user has |
| 149 // started navigating to a new document. | 150 // started navigating to a new document. |
| 150 if (user_is_navigating_away_) | 151 if (user_is_navigating_away_) |
| 151 return; | 152 return BrowserAccessibilityEvent::DiscardedBecauseUserNavigatingAway; |
| 152 | 153 |
| 153 // Inline text boxes are an internal implementation detail, we don't | 154 // Inline text boxes are an internal implementation detail, we don't |
| 154 // expose them to Windows. | 155 // expose them to Windows. |
| 155 if (node->GetRole() == ui::AX_ROLE_INLINE_TEXT_BOX) | 156 if (target->GetRole() == ui::AX_ROLE_INLINE_TEXT_BOX) |
| 156 return; | 157 return BrowserAccessibilityEvent::NotNeededOnThisPlatform; |
| 157 | 158 |
| 158 LONG event_id = EVENT_MIN; | 159 if (event_type == ui::AX_EVENT_LIVE_REGION_CHANGED && |
| 159 switch (event_type) { | 160 target->GetBoolAttribute(ui::AX_ATTR_CONTAINER_LIVE_BUSY)) |
| 160 case ui::AX_EVENT_ACTIVEDESCENDANTCHANGED: | 161 return BrowserAccessibilityEvent::DiscardedBecauseLiveRegionBusy; |
| 161 event_id = IA2_EVENT_ACTIVE_DESCENDANT_CHANGED; | 162 |
| 162 break; | 163 if (!target) |
| 163 case ui::AX_EVENT_ALERT: | 164 return BrowserAccessibilityEvent::FailedBecauseNoFocus; |
| 164 event_id = EVENT_SYSTEM_ALERT; | 165 |
| 165 break; | 166 event->set_target(target); |
| 166 case ui::AX_EVENT_AUTOCORRECTION_OCCURED: | 167 |
| 167 event_id = IA2_EVENT_OBJECT_ATTRIBUTE_CHANGED; | 168 // It doesn't make sense to fire a REORDER event on a leaf node; that |
| 168 break; | 169 // happens when the target has internal children inline text boxes. |
| 169 case ui::AX_EVENT_BLUR: | 170 if (win_event_type == EVENT_OBJECT_REORDER && |
| 170 // Equivalent to focus on the root. | 171 target->PlatformChildCount() == 0) { |
| 171 event_id = EVENT_OBJECT_FOCUS; | 172 return BrowserAccessibilityEvent::NotNeededOnThisPlatform; |
| 172 node = GetRoot(); | |
| 173 break; | |
| 174 case ui::AX_EVENT_CHILDREN_CHANGED: | |
| 175 event_id = EVENT_OBJECT_REORDER; | |
| 176 break; | |
| 177 case ui::AX_EVENT_FOCUS: | |
| 178 event_id = EVENT_OBJECT_FOCUS; | |
| 179 break; | |
| 180 case ui::AX_EVENT_LIVE_REGION_CHANGED: | |
| 181 if (node->GetBoolAttribute(ui::AX_ATTR_CONTAINER_LIVE_BUSY)) | |
| 182 return; | |
| 183 event_id = EVENT_OBJECT_LIVEREGIONCHANGED; | |
| 184 break; | |
| 185 case ui::AX_EVENT_LOAD_COMPLETE: | |
| 186 event_id = IA2_EVENT_DOCUMENT_LOAD_COMPLETE; | |
| 187 break; | |
| 188 case ui::AX_EVENT_SCROLL_POSITION_CHANGED: | |
| 189 event_id = EVENT_SYSTEM_SCROLLINGEND; | |
| 190 break; | |
| 191 case ui::AX_EVENT_SCROLLED_TO_ANCHOR: | |
| 192 event_id = EVENT_SYSTEM_SCROLLINGSTART; | |
| 193 break; | |
| 194 case ui::AX_EVENT_SELECTED_CHILDREN_CHANGED: | |
| 195 event_id = EVENT_OBJECT_SELECTIONWITHIN; | |
| 196 break; | |
| 197 case ui::AX_EVENT_DOCUMENT_SELECTION_CHANGED: { | |
| 198 // Fire the event on the object where the focus of the selection is. | |
| 199 int32_t focus_id = GetTreeData().sel_focus_object_id; | |
| 200 BrowserAccessibility* focus_object = GetFromID(focus_id); | |
| 201 if (focus_object) | |
| 202 node = focus_object; | |
| 203 event_id = IA2_EVENT_TEXT_CARET_MOVED; | |
| 204 break; | |
| 205 } | |
| 206 default: | |
| 207 // Not all WebKit accessibility events result in a Windows | |
| 208 // accessibility notification. | |
| 209 break; | |
| 210 } | 173 } |
| 211 | 174 |
| 212 if (!node) | 175 // Pass the negation of this node's unique id in the |child_id| |
| 213 return; | 176 // argument to NotifyWinEvent; the AT client will then call get_accChild |
| 214 | 177 // on the HWND's accessibility object and pass it that same id, which |
| 215 if (event_id != EVENT_MIN) | 178 // we can use to retrieve the IAccessible for this node. |
| 216 MaybeCallNotifyWinEvent(event_id, node); | 179 LONG child_id = -target->unique_id(); |
| 180 ::NotifyWinEvent(win_event_type, hwnd, OBJID_CLIENT, child_id); |
| 217 | 181 |
| 218 // If this is a layout complete notification (sent when a container scrolls) | 182 // If this is a layout complete notification (sent when a container scrolls) |
| 219 // and there is a descendant tracked object, send a notification on it. | 183 // and there is a descendant tracked object, send a notification on it. |
| 220 // TODO(dmazzoni): remove once http://crbug.com/113483 is fixed. | 184 // TODO(dmazzoni): remove once http://crbug.com/113483 is fixed. |
| 221 if (event_type == ui::AX_EVENT_LAYOUT_COMPLETE && | 185 if (event_type == ui::AX_EVENT_LAYOUT_COMPLETE && |
| 222 tracked_scroll_object_ && | 186 tracked_scroll_object_ && |
| 223 tracked_scroll_object_->IsDescendantOf(node)) { | 187 tracked_scroll_object_->IsDescendantOf(target)) { |
| 224 MaybeCallNotifyWinEvent( | 188 (new BrowserAccessibilityEventWin( |
| 225 IA2_EVENT_VISIBLE_DATA_CHANGED, tracked_scroll_object_); | 189 BrowserAccessibilityEvent::FromScroll, |
| 190 ui::AX_EVENT_NONE, |
| 191 IA2_EVENT_VISIBLE_DATA_CHANGED, |
| 192 tracked_scroll_object_))->Fire(); |
| 226 tracked_scroll_object_->Release(); | 193 tracked_scroll_object_->Release(); |
| 227 tracked_scroll_object_ = NULL; | 194 tracked_scroll_object_ = NULL; |
| 228 } | 195 } |
| 196 |
| 197 return BrowserAccessibilityEvent::Sent; |
| 229 } | 198 } |
| 230 | 199 |
| 231 bool BrowserAccessibilityManagerWin::CanFireEvents() { | 200 bool BrowserAccessibilityManagerWin::CanFireEvents() { |
| 232 BrowserAccessibilityDelegate* root_delegate = GetDelegateFromRootManager(); | 201 BrowserAccessibilityDelegate* root_delegate = GetDelegateFromRootManager(); |
| 233 if (!root_delegate) | 202 if (!root_delegate) |
| 234 return false; | 203 return false; |
| 235 HWND hwnd = root_delegate->AccessibilityGetAcceleratedWidget(); | 204 HWND hwnd = root_delegate->AccessibilityGetAcceleratedWidget(); |
| 236 return hwnd != nullptr; | 205 return hwnd != nullptr; |
| 237 } | 206 } |
| 238 | 207 |
| 239 void BrowserAccessibilityManagerWin::FireFocusEvent( | 208 void BrowserAccessibilityManagerWin::FireFocusEvent( |
| 209 BrowserAccessibilityEvent::Source source, |
| 240 BrowserAccessibility* node) { | 210 BrowserAccessibility* node) { |
| 241 // On Windows, we always fire a FOCUS event on the root of a frame before | 211 // On Windows, we always fire a FOCUS event on the root of a frame before |
| 242 // firing a focus event within that frame. | 212 // firing a focus event within that frame. |
| 243 if (node->manager() != last_focused_manager_ && | 213 if (node->manager() != last_focused_manager_ && |
| 244 node != node->manager()->GetRoot()) { | 214 node != node->manager()->GetRoot()) { |
| 245 NotifyAccessibilityEvent(ui::AX_EVENT_FOCUS, node->manager()->GetRoot()); | 215 BrowserAccessibilityEvent::Create(source, |
| 216 ui::AX_EVENT_FOCUS, |
| 217 node->manager()->GetRoot())->Fire(); |
| 246 } | 218 } |
| 247 | 219 |
| 248 BrowserAccessibilityManager::FireFocusEvent(node); | 220 BrowserAccessibilityManager::FireFocusEvent(source, node); |
| 249 } | 221 } |
| 250 | 222 |
| 251 void BrowserAccessibilityManagerWin::OnNodeCreated(ui::AXTree* tree, | 223 void BrowserAccessibilityManagerWin::OnNodeCreated(ui::AXTree* tree, |
| 252 ui::AXNode* node) { | 224 ui::AXNode* node) { |
| 253 DCHECK(node); | 225 DCHECK(node); |
| 254 BrowserAccessibilityManager::OnNodeCreated(tree, node); | 226 BrowserAccessibilityManager::OnNodeCreated(tree, node); |
| 255 BrowserAccessibility* obj = GetFromAXNode(node); | 227 BrowserAccessibility* obj = GetFromAXNode(node); |
| 256 if (!obj) | 228 if (!obj) |
| 257 return; | 229 return; |
| 258 if (!obj->IsNative()) | 230 if (!obj->IsNative()) |
| (...skipping 67 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 326 | 298 |
| 327 void BrowserAccessibilityManagerWin::TrackScrollingObject( | 299 void BrowserAccessibilityManagerWin::TrackScrollingObject( |
| 328 BrowserAccessibilityWin* node) { | 300 BrowserAccessibilityWin* node) { |
| 329 if (tracked_scroll_object_) | 301 if (tracked_scroll_object_) |
| 330 tracked_scroll_object_->Release(); | 302 tracked_scroll_object_->Release(); |
| 331 tracked_scroll_object_ = node; | 303 tracked_scroll_object_ = node; |
| 332 tracked_scroll_object_->AddRef(); | 304 tracked_scroll_object_->AddRef(); |
| 333 } | 305 } |
| 334 | 306 |
| 335 } // namespace content | 307 } // namespace content |
| OLD | NEW |