[Storage] Blob Storage Refactoring pt 1:
[chromium-blink-merge.git] / content / browser / accessibility / browser_accessibility_manager_win.cc
blobd3449df2d42dca40c526513600b21f375b75a5f5
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
3 // found in the LICENSE file.
5 #include "content/browser/accessibility/browser_accessibility_manager_win.h"
7 #include "base/command_line.h"
8 #include "base/win/scoped_comptr.h"
9 #include "base/win/windows_version.h"
10 #include "content/browser/accessibility/browser_accessibility_state_impl.h"
11 #include "content/browser/accessibility/browser_accessibility_win.h"
12 #include "content/browser/renderer_host/legacy_render_widget_host_win.h"
13 #include "content/common/accessibility_messages.h"
14 #include "ui/base/win/atl_module.h"
16 namespace content {
18 // static
19 BrowserAccessibilityManager* BrowserAccessibilityManager::Create(
20 const ui::AXTreeUpdate& initial_tree,
21 BrowserAccessibilityDelegate* delegate,
22 BrowserAccessibilityFactory* factory) {
23 return new BrowserAccessibilityManagerWin(initial_tree, delegate, factory);
26 BrowserAccessibilityManagerWin*
27 BrowserAccessibilityManager::ToBrowserAccessibilityManagerWin() {
28 return static_cast<BrowserAccessibilityManagerWin*>(this);
31 BrowserAccessibilityManagerWin::BrowserAccessibilityManagerWin(
32 const ui::AXTreeUpdate& initial_tree,
33 BrowserAccessibilityDelegate* delegate,
34 BrowserAccessibilityFactory* factory)
35 : BrowserAccessibilityManager(delegate, factory),
36 tracked_scroll_object_(NULL),
37 focus_event_on_root_needed_(false) {
38 ui::win::CreateATLModuleIfNeeded();
39 Initialize(initial_tree);
42 BrowserAccessibilityManagerWin::~BrowserAccessibilityManagerWin() {
43 if (tracked_scroll_object_) {
44 tracked_scroll_object_->Release();
45 tracked_scroll_object_ = NULL;
49 // static
50 ui::AXTreeUpdate BrowserAccessibilityManagerWin::GetEmptyDocument() {
51 ui::AXNodeData empty_document;
52 empty_document.id = 0;
53 empty_document.role = ui::AX_ROLE_ROOT_WEB_AREA;
54 empty_document.state =
55 (1 << ui::AX_STATE_ENABLED) |
56 (1 << ui::AX_STATE_READ_ONLY) |
57 (1 << ui::AX_STATE_BUSY);
59 ui::AXTreeUpdate update;
60 update.nodes.push_back(empty_document);
61 return update;
64 HWND BrowserAccessibilityManagerWin::GetParentHWND() {
65 if (!delegate_)
66 return NULL;
67 return delegate_->AccessibilityGetAcceleratedWidget();
70 IAccessible* BrowserAccessibilityManagerWin::GetParentIAccessible() {
71 if (!delegate_)
72 return NULL;
73 return delegate_->AccessibilityGetNativeViewAccessible();
76 void BrowserAccessibilityManagerWin::MaybeCallNotifyWinEvent(DWORD event,
77 LONG child_id) {
78 BrowserAccessibilityDelegate* delegate = GetDelegateFromRootManager();
79 if (!delegate)
80 return;
82 HWND hwnd = delegate->AccessibilityGetAcceleratedWidget();
83 if (!hwnd)
84 return;
86 ::NotifyWinEvent(event, hwnd, OBJID_CLIENT, child_id);
89 void BrowserAccessibilityManagerWin::OnNodeCreated(ui::AXNode* node) {
90 BrowserAccessibilityManager::OnNodeCreated(node);
91 BrowserAccessibility* obj = GetFromAXNode(node);
92 LONG unique_id_win = obj->ToBrowserAccessibilityWin()->unique_id_win();
93 unique_id_to_ax_id_map_[unique_id_win] = obj->GetId();
96 void BrowserAccessibilityManagerWin::OnNodeWillBeDeleted(ui::AXNode* node) {
97 BrowserAccessibilityManager::OnNodeWillBeDeleted(node);
98 BrowserAccessibility* obj = GetFromAXNode(node);
99 if (!obj)
100 return;
101 unique_id_to_ax_id_map_.erase(
102 obj->ToBrowserAccessibilityWin()->unique_id_win());
103 if (obj == tracked_scroll_object_) {
104 tracked_scroll_object_->Release();
105 tracked_scroll_object_ = NULL;
109 void BrowserAccessibilityManagerWin::OnWindowFocused() {
110 // This is called either when this web frame gets focused, or when
111 // the root of the accessibility tree changes. In both cases, we need
112 // to fire a focus event on the root and then on the focused element
113 // within the page, if different.
115 // Set this flag so that we'll keep trying to fire these focus events
116 // if they're not successful this time.
117 focus_event_on_root_needed_ = true;
119 if (!delegate_ || !delegate_->AccessibilityViewHasFocus())
120 return;
122 // 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.
124 if (focus_ != tree_->GetRoot())
125 NotifyAccessibilityEvent(ui::AX_EVENT_FOCUS, GetRoot());
126 BrowserAccessibilityManager::OnWindowFocused();
129 void BrowserAccessibilityManagerWin::NotifyAccessibilityEvent(
130 ui::AXEvent event_type,
131 BrowserAccessibility* node) {
132 BrowserAccessibilityDelegate* root_delegate = GetDelegateFromRootManager();
133 if (!root_delegate || !root_delegate->AccessibilityGetAcceleratedWidget())
134 return;
136 // Inline text boxes are an internal implementation detail, we don't
137 // expose them to Windows.
138 if (node->GetRole() == ui::AX_ROLE_INLINE_TEXT_BOX)
139 return;
141 // Don't fire focus, blur, or load complete notifications if the
142 // window isn't focused, because that can confuse screen readers into
143 // entering their "browse" mode.
144 if ((event_type == ui::AX_EVENT_FOCUS ||
145 event_type == ui::AX_EVENT_BLUR ||
146 event_type == ui::AX_EVENT_LOAD_COMPLETE) &&
147 !root_delegate->AccessibilityViewHasFocus()) {
148 return;
151 // NVDA gets confused if we focus the main document element when it hasn't
152 // finished loading and it has no children at all, so suppress that event.
153 if (event_type == ui::AX_EVENT_FOCUS &&
154 node == GetRoot() &&
155 node->PlatformChildCount() == 0 &&
156 !node->HasState(ui::AX_STATE_BUSY) &&
157 !node->GetBoolAttribute(ui::AX_ATTR_DOC_LOADED)) {
158 return;
161 // If a focus event is needed on the root, fire that first before
162 // this event.
163 if (event_type == ui::AX_EVENT_FOCUS && node == GetRoot())
164 focus_event_on_root_needed_ = false;
165 else if (focus_event_on_root_needed_)
166 OnWindowFocused();
168 LONG event_id = EVENT_MIN;
169 switch (event_type) {
170 case ui::AX_EVENT_ACTIVEDESCENDANTCHANGED:
171 event_id = IA2_EVENT_ACTIVE_DESCENDANT_CHANGED;
172 break;
173 case ui::AX_EVENT_ALERT:
174 event_id = EVENT_SYSTEM_ALERT;
175 break;
176 case ui::AX_EVENT_ARIA_ATTRIBUTE_CHANGED:
177 event_id = IA2_EVENT_OBJECT_ATTRIBUTE_CHANGED;
178 break;
179 case ui::AX_EVENT_AUTOCORRECTION_OCCURED:
180 event_id = IA2_EVENT_OBJECT_ATTRIBUTE_CHANGED;
181 break;
182 case ui::AX_EVENT_BLUR:
183 // Equivalent to focus on the root.
184 event_id = EVENT_OBJECT_FOCUS;
185 node = GetRoot();
186 break;
187 case ui::AX_EVENT_CHECKED_STATE_CHANGED:
188 event_id = EVENT_OBJECT_STATECHANGE;
189 break;
190 case ui::AX_EVENT_CHILDREN_CHANGED:
191 event_id = EVENT_OBJECT_REORDER;
192 break;
193 case ui::AX_EVENT_FOCUS:
194 event_id = EVENT_OBJECT_FOCUS;
195 break;
196 case ui::AX_EVENT_INVALID_STATUS_CHANGED:
197 event_id = EVENT_OBJECT_STATECHANGE;
198 break;
199 case ui::AX_EVENT_LIVE_REGION_CHANGED:
200 if (node->GetBoolAttribute(ui::AX_ATTR_CONTAINER_LIVE_BUSY))
201 return;
202 event_id = EVENT_OBJECT_LIVEREGIONCHANGED;
203 break;
204 case ui::AX_EVENT_LOAD_COMPLETE:
205 event_id = IA2_EVENT_DOCUMENT_LOAD_COMPLETE;
206 break;
207 case ui::AX_EVENT_MENU_LIST_ITEM_SELECTED:
208 event_id = EVENT_OBJECT_FOCUS;
209 break;
210 case ui::AX_EVENT_MENU_LIST_VALUE_CHANGED:
211 event_id = EVENT_OBJECT_VALUECHANGE;
212 break;
213 case ui::AX_EVENT_HIDE:
214 event_id = EVENT_OBJECT_HIDE;
215 break;
216 case ui::AX_EVENT_SHOW:
217 event_id = EVENT_OBJECT_SHOW;
218 break;
219 case ui::AX_EVENT_SCROLL_POSITION_CHANGED:
220 event_id = EVENT_SYSTEM_SCROLLINGEND;
221 break;
222 case ui::AX_EVENT_SCROLLED_TO_ANCHOR:
223 event_id = EVENT_SYSTEM_SCROLLINGSTART;
224 break;
225 case ui::AX_EVENT_SELECTED_CHILDREN_CHANGED:
226 event_id = EVENT_OBJECT_SELECTIONWITHIN;
227 break;
228 case ui::AX_EVENT_TEXT_CHANGED:
229 event_id = EVENT_OBJECT_NAMECHANGE;
230 break;
231 case ui::AX_EVENT_TEXT_SELECTION_CHANGED:
232 event_id = IA2_EVENT_TEXT_CARET_MOVED;
233 break;
234 case ui::AX_EVENT_VALUE_CHANGED:
235 event_id = EVENT_OBJECT_VALUECHANGE;
236 break;
237 default:
238 // Not all WebKit accessibility events result in a Windows
239 // accessibility notification.
240 break;
243 if (event_id != EVENT_MIN) {
244 // Pass the node's unique id in the |child_id| argument to NotifyWinEvent;
245 // the AT client will then call get_accChild on the HWND's accessibility
246 // object and pass it that same id, which we can use to retrieve the
247 // IAccessible for this node.
248 LONG child_id = node->ToBrowserAccessibilityWin()->unique_id_win();
249 MaybeCallNotifyWinEvent(event_id, child_id);
252 // If this is a layout complete notification (sent when a container scrolls)
253 // and there is a descendant tracked object, send a notification on it.
254 // TODO(dmazzoni): remove once http://crbug.com/113483 is fixed.
255 if (event_type == ui::AX_EVENT_LAYOUT_COMPLETE &&
256 tracked_scroll_object_ &&
257 tracked_scroll_object_->IsDescendantOf(node)) {
258 MaybeCallNotifyWinEvent(
259 IA2_EVENT_VISIBLE_DATA_CHANGED,
260 tracked_scroll_object_->ToBrowserAccessibilityWin()->unique_id_win());
261 tracked_scroll_object_->Release();
262 tracked_scroll_object_ = NULL;
266 void BrowserAccessibilityManagerWin::OnRootChanged(ui::AXNode* new_root) {
267 // In order to make screen readers aware of the new accessibility root,
268 // we need to fire a focus event on it.
269 OnWindowFocused();
272 void BrowserAccessibilityManagerWin::TrackScrollingObject(
273 BrowserAccessibilityWin* node) {
274 if (tracked_scroll_object_)
275 tracked_scroll_object_->Release();
276 tracked_scroll_object_ = node;
277 tracked_scroll_object_->AddRef();
280 BrowserAccessibilityWin* BrowserAccessibilityManagerWin::GetFromUniqueIdWin(
281 LONG unique_id_win) {
282 base::hash_map<LONG, int32>::iterator iter =
283 unique_id_to_ax_id_map_.find(unique_id_win);
284 if (iter != unique_id_to_ax_id_map_.end()) {
285 BrowserAccessibility* result = GetFromID(iter->second);
286 if (result)
287 return result->ToBrowserAccessibilityWin();
290 // Also search all child frames, such as out-of-process iframes or
291 // guest browser plugins.
292 if (delegate()) {
293 std::vector<BrowserAccessibilityManager*> child_frames;
294 delegate()->AccessibilityGetAllChildFrames(&child_frames);
295 for (size_t i = 0; i < child_frames.size(); ++i) {
296 BrowserAccessibilityManagerWin* child_manager =
297 child_frames[i]->ToBrowserAccessibilityManagerWin();
298 BrowserAccessibilityWin* result =
299 child_manager->GetFromUniqueIdWin(unique_id_win);
300 if (result)
301 return result;
305 return NULL;
308 } // namespace content