Battery Status API: add UMA logging for Linux.
[chromium-blink-merge.git] / content / browser / accessibility / browser_accessibility_manager_win.cc
blob4351ef46369987382235eff827e4b89eec0f35c9
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(initial_tree, delegate, factory),
36 tracked_scroll_object_(NULL),
37 focus_event_on_root_needed_(false) {
38 ui::win::CreateATLModuleIfNeeded();
41 BrowserAccessibilityManagerWin::~BrowserAccessibilityManagerWin() {
42 if (tracked_scroll_object_) {
43 tracked_scroll_object_->Release();
44 tracked_scroll_object_ = NULL;
48 // static
49 ui::AXTreeUpdate BrowserAccessibilityManagerWin::GetEmptyDocument() {
50 ui::AXNodeData empty_document;
51 empty_document.id = 0;
52 empty_document.role = ui::AX_ROLE_ROOT_WEB_AREA;
53 empty_document.state =
54 (1 << ui::AX_STATE_ENABLED) |
55 (1 << ui::AX_STATE_READ_ONLY) |
56 (1 << ui::AX_STATE_BUSY);
58 ui::AXTreeUpdate update;
59 update.nodes.push_back(empty_document);
60 return update;
63 HWND BrowserAccessibilityManagerWin::GetParentHWND() {
64 if (!delegate_)
65 return NULL;
66 return delegate_->AccessibilityGetAcceleratedWidget();
69 IAccessible* BrowserAccessibilityManagerWin::GetParentIAccessible() {
70 if (!delegate_)
71 return NULL;
72 return delegate_->AccessibilityGetNativeViewAccessible();
75 void BrowserAccessibilityManagerWin::MaybeCallNotifyWinEvent(DWORD event,
76 LONG child_id) {
77 if (!delegate_)
78 return;
80 HWND hwnd = delegate_->AccessibilityGetAcceleratedWidget();
81 if (!hwnd)
82 return;
84 ::NotifyWinEvent(event, hwnd, OBJID_CLIENT, child_id);
88 void BrowserAccessibilityManagerWin::OnNodeCreated(ui::AXNode* node) {
89 BrowserAccessibilityManager::OnNodeCreated(node);
90 BrowserAccessibility* obj = GetFromAXNode(node);
91 LONG unique_id_win = obj->ToBrowserAccessibilityWin()->unique_id_win();
92 unique_id_to_ax_id_map_[unique_id_win] = obj->GetId();
95 void BrowserAccessibilityManagerWin::OnNodeWillBeDeleted(ui::AXNode* node) {
96 BrowserAccessibilityManager::OnNodeWillBeDeleted(node);
97 BrowserAccessibility* obj = GetFromAXNode(node);
98 if (!obj)
99 return;
100 unique_id_to_ax_id_map_.erase(
101 obj->ToBrowserAccessibilityWin()->unique_id_win());
102 if (obj == tracked_scroll_object_) {
103 tracked_scroll_object_->Release();
104 tracked_scroll_object_ = NULL;
108 void BrowserAccessibilityManagerWin::OnWindowFocused() {
109 // This is called either when this web frame gets focused, or when
110 // the root of the accessibility tree changes. In both cases, we need
111 // to fire a focus event on the root and then on the focused element
112 // within the page, if different.
114 // Set this flag so that we'll keep trying to fire these focus events
115 // if they're not successful this time.
116 focus_event_on_root_needed_ = true;
118 if (!delegate_ || !delegate_->AccessibilityViewHasFocus())
119 return;
121 // Try to fire a focus event on the root first and then the focused node.
122 // This will clear focus_event_on_root_needed_ if successful.
123 if (focus_ != tree_->GetRoot())
124 NotifyAccessibilityEvent(ui::AX_EVENT_FOCUS, GetRoot());
125 BrowserAccessibilityManager::OnWindowFocused();
128 void BrowserAccessibilityManagerWin::NotifyAccessibilityEvent(
129 ui::AXEvent event_type,
130 BrowserAccessibility* node) {
131 if (!delegate_ || !delegate_->AccessibilityGetAcceleratedWidget())
132 return;
134 // Inline text boxes are an internal implementation detail, we don't
135 // expose them to Windows.
136 if (node->GetRole() == ui::AX_ROLE_INLINE_TEXT_BOX)
137 return;
139 // Don't fire focus, blur, or load complete notifications if the
140 // window isn't focused, because that can confuse screen readers into
141 // entering their "browse" mode.
142 if ((event_type == ui::AX_EVENT_FOCUS ||
143 event_type == ui::AX_EVENT_BLUR ||
144 event_type == ui::AX_EVENT_LOAD_COMPLETE) &&
145 (!delegate_ || !delegate_->AccessibilityViewHasFocus())) {
146 return;
149 // NVDA gets confused if we focus the main document element when it hasn't
150 // finished loading and it has no children at all, so suppress that event.
151 if (event_type == ui::AX_EVENT_FOCUS &&
152 node == GetRoot() &&
153 node->PlatformChildCount() == 0 &&
154 !node->HasState(ui::AX_STATE_BUSY) &&
155 !node->GetBoolAttribute(ui::AX_ATTR_DOC_LOADED)) {
156 return;
159 // If a focus event is needed on the root, fire that first before
160 // this event.
161 if (event_type == ui::AX_EVENT_FOCUS && node == GetRoot())
162 focus_event_on_root_needed_ = false;
163 else if (focus_event_on_root_needed_)
164 OnWindowFocused();
166 LONG event_id = EVENT_MIN;
167 switch (event_type) {
168 case ui::AX_EVENT_ACTIVEDESCENDANTCHANGED:
169 event_id = IA2_EVENT_ACTIVE_DESCENDANT_CHANGED;
170 break;
171 case ui::AX_EVENT_ALERT:
172 event_id = EVENT_SYSTEM_ALERT;
173 break;
174 case ui::AX_EVENT_ARIA_ATTRIBUTE_CHANGED:
175 event_id = IA2_EVENT_OBJECT_ATTRIBUTE_CHANGED;
176 break;
177 case ui::AX_EVENT_AUTOCORRECTION_OCCURED:
178 event_id = IA2_EVENT_OBJECT_ATTRIBUTE_CHANGED;
179 break;
180 case ui::AX_EVENT_BLUR:
181 // Equivalent to focus on the root.
182 event_id = EVENT_OBJECT_FOCUS;
183 node = GetRoot();
184 break;
185 case ui::AX_EVENT_CHECKED_STATE_CHANGED:
186 event_id = EVENT_OBJECT_STATECHANGE;
187 break;
188 case ui::AX_EVENT_CHILDREN_CHANGED:
189 event_id = EVENT_OBJECT_REORDER;
190 break;
191 case ui::AX_EVENT_FOCUS:
192 event_id = EVENT_OBJECT_FOCUS;
193 break;
194 case ui::AX_EVENT_INVALID_STATUS_CHANGED:
195 event_id = EVENT_OBJECT_STATECHANGE;
196 break;
197 case ui::AX_EVENT_LIVE_REGION_CHANGED:
198 if (node->GetBoolAttribute(ui::AX_ATTR_CONTAINER_LIVE_BUSY))
199 return;
200 event_id = EVENT_OBJECT_LIVEREGIONCHANGED;
201 break;
202 case ui::AX_EVENT_LOAD_COMPLETE:
203 event_id = IA2_EVENT_DOCUMENT_LOAD_COMPLETE;
204 break;
205 case ui::AX_EVENT_MENU_LIST_ITEM_SELECTED:
206 event_id = EVENT_OBJECT_FOCUS;
207 break;
208 case ui::AX_EVENT_MENU_LIST_VALUE_CHANGED:
209 event_id = EVENT_OBJECT_VALUECHANGE;
210 break;
211 case ui::AX_EVENT_HIDE:
212 event_id = EVENT_OBJECT_HIDE;
213 break;
214 case ui::AX_EVENT_SHOW:
215 event_id = EVENT_OBJECT_SHOW;
216 break;
217 case ui::AX_EVENT_SCROLL_POSITION_CHANGED:
218 event_id = EVENT_SYSTEM_SCROLLINGEND;
219 break;
220 case ui::AX_EVENT_SCROLLED_TO_ANCHOR:
221 event_id = EVENT_SYSTEM_SCROLLINGSTART;
222 break;
223 case ui::AX_EVENT_SELECTED_CHILDREN_CHANGED:
224 event_id = EVENT_OBJECT_SELECTIONWITHIN;
225 break;
226 case ui::AX_EVENT_TEXT_CHANGED:
227 event_id = EVENT_OBJECT_NAMECHANGE;
228 break;
229 case ui::AX_EVENT_TEXT_SELECTION_CHANGED:
230 event_id = IA2_EVENT_TEXT_CARET_MOVED;
231 break;
232 case ui::AX_EVENT_VALUE_CHANGED:
233 event_id = EVENT_OBJECT_VALUECHANGE;
234 break;
235 default:
236 // Not all WebKit accessibility events result in a Windows
237 // accessibility notification.
238 break;
241 if (event_id != EVENT_MIN) {
242 // Pass the node's unique id in the |child_id| argument to NotifyWinEvent;
243 // the AT client will then call get_accChild on the HWND's accessibility
244 // object and pass it that same id, which we can use to retrieve the
245 // IAccessible for this node.
246 LONG child_id = node->ToBrowserAccessibilityWin()->unique_id_win();
247 MaybeCallNotifyWinEvent(event_id, child_id);
250 // If this is a layout complete notification (sent when a container scrolls)
251 // and there is a descendant tracked object, send a notification on it.
252 // TODO(dmazzoni): remove once http://crbug.com/113483 is fixed.
253 if (event_type == ui::AX_EVENT_LAYOUT_COMPLETE &&
254 tracked_scroll_object_ &&
255 tracked_scroll_object_->IsDescendantOf(node)) {
256 MaybeCallNotifyWinEvent(
257 IA2_EVENT_VISIBLE_DATA_CHANGED,
258 tracked_scroll_object_->ToBrowserAccessibilityWin()->unique_id_win());
259 tracked_scroll_object_->Release();
260 tracked_scroll_object_ = NULL;
264 void BrowserAccessibilityManagerWin::OnRootChanged(ui::AXNode* new_root) {
265 // In order to make screen readers aware of the new accessibility root,
266 // we need to fire a focus event on it.
267 OnWindowFocused();
270 void BrowserAccessibilityManagerWin::TrackScrollingObject(
271 BrowserAccessibilityWin* node) {
272 if (tracked_scroll_object_)
273 tracked_scroll_object_->Release();
274 tracked_scroll_object_ = node;
275 tracked_scroll_object_->AddRef();
278 BrowserAccessibilityWin* BrowserAccessibilityManagerWin::GetFromUniqueIdWin(
279 LONG unique_id_win) {
280 base::hash_map<LONG, int32>::iterator iter =
281 unique_id_to_ax_id_map_.find(unique_id_win);
282 if (iter != unique_id_to_ax_id_map_.end()) {
283 BrowserAccessibility* result = GetFromID(iter->second);
284 if (result)
285 return result->ToBrowserAccessibilityWin();
287 return NULL;
290 } // namespace content