Add long running gmail memory benchmark for background tab.
[chromium-blink-merge.git] / content / browser / accessibility / browser_accessibility_manager_win.cc
blob526e998d5c6a7b9f6d818de6e2b4cc3ade08586c
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 <vector>
9 #include "base/command_line.h"
10 #include "base/win/scoped_comptr.h"
11 #include "base/win/windows_version.h"
12 #include "content/browser/accessibility/browser_accessibility_state_impl.h"
13 #include "content/browser/accessibility/browser_accessibility_win.h"
14 #include "content/browser/renderer_host/legacy_render_widget_host_win.h"
15 #include "content/common/accessibility_messages.h"
16 #include "ui/base/win/atl_module.h"
18 namespace content {
20 // static
21 BrowserAccessibilityManager* BrowserAccessibilityManager::Create(
22 const ui::AXTreeUpdate& initial_tree,
23 BrowserAccessibilityDelegate* delegate,
24 BrowserAccessibilityFactory* factory) {
25 return new BrowserAccessibilityManagerWin(initial_tree, delegate, factory);
28 BrowserAccessibilityManagerWin*
29 BrowserAccessibilityManager::ToBrowserAccessibilityManagerWin() {
30 return static_cast<BrowserAccessibilityManagerWin*>(this);
33 BrowserAccessibilityManagerWin::BrowserAccessibilityManagerWin(
34 const ui::AXTreeUpdate& initial_tree,
35 BrowserAccessibilityDelegate* delegate,
36 BrowserAccessibilityFactory* factory)
37 : BrowserAccessibilityManager(delegate, factory),
38 tracked_scroll_object_(NULL),
39 focus_event_on_root_needed_(false),
40 inside_on_window_focused_(false) {
41 ui::win::CreateATLModuleIfNeeded();
42 Initialize(initial_tree);
45 BrowserAccessibilityManagerWin::~BrowserAccessibilityManagerWin() {
46 if (tracked_scroll_object_) {
47 tracked_scroll_object_->Release();
48 tracked_scroll_object_ = NULL;
52 // static
53 ui::AXTreeUpdate BrowserAccessibilityManagerWin::GetEmptyDocument() {
54 ui::AXNodeData empty_document;
55 empty_document.id = 0;
56 empty_document.role = ui::AX_ROLE_ROOT_WEB_AREA;
57 empty_document.state =
58 (1 << ui::AX_STATE_ENABLED) |
59 (1 << ui::AX_STATE_READ_ONLY) |
60 (1 << ui::AX_STATE_BUSY);
62 ui::AXTreeUpdate update;
63 update.nodes.push_back(empty_document);
64 return update;
67 HWND BrowserAccessibilityManagerWin::GetParentHWND() {
68 if (!delegate_)
69 return NULL;
70 return delegate_->AccessibilityGetAcceleratedWidget();
73 IAccessible* BrowserAccessibilityManagerWin::GetParentIAccessible() {
74 if (!delegate_)
75 return NULL;
76 return delegate_->AccessibilityGetNativeViewAccessible();
79 void BrowserAccessibilityManagerWin::MaybeCallNotifyWinEvent(
80 DWORD event, BrowserAccessibility* node) {
81 BrowserAccessibilityDelegate* delegate = GetDelegateFromRootManager();
82 if (!delegate) {
83 // This line and other LOG(WARNING) lines are temporary, to debug
84 // flaky failures in DumpAccessibilityEvent* tests.
85 // http://crbug.com/440579
86 LOG(WARNING) << "Not firing AX event because of no delegate";
87 return;
90 if (!node->IsNative())
91 return;
93 HWND hwnd = delegate->AccessibilityGetAcceleratedWidget();
94 if (!hwnd) {
95 LOG(WARNING) << "Not firing AX event because of no hwnd";
96 return;
99 // Inline text boxes are an internal implementation detail, we don't
100 // expose them to Windows.
101 if (node->GetRole() == ui::AX_ROLE_INLINE_TEXT_BOX)
102 return;
104 // It doesn't make sense to fire a REORDER event on a leaf node; that
105 // happens when the node has internal children line inline text boxes.
106 if (event == EVENT_OBJECT_REORDER && node->PlatformIsLeaf())
107 return;
109 // Don't fire focus, or load complete notifications if the
110 // window isn't focused, because that can confuse screen readers into
111 // entering their "browse" mode.
112 if ((event == EVENT_OBJECT_FOCUS ||
113 event == IA2_EVENT_DOCUMENT_LOAD_COMPLETE) &&
114 (!delegate_->AccessibilityViewHasFocus())) {
115 return;
118 // NVDA gets confused if we focus the main document element when it hasn't
119 // finished loading and it has no children at all, so suppress that event.
120 if (event == EVENT_OBJECT_FOCUS &&
121 node == GetRoot() &&
122 node->PlatformChildCount() == 0 &&
123 !node->HasState(ui::AX_STATE_BUSY) &&
124 !node->GetBoolAttribute(ui::AX_ATTR_DOC_LOADED)) {
125 return;
128 // If a focus event is needed on the root, fire that first before
129 // this event.
130 if (event == EVENT_OBJECT_FOCUS && node == GetRoot())
131 focus_event_on_root_needed_ = false;
132 else if (focus_event_on_root_needed_)
133 OnWindowFocused();
135 LONG child_id = node->ToBrowserAccessibilityWin()->unique_id_win();
136 ::NotifyWinEvent(event, hwnd, OBJID_CLIENT, child_id);
139 void BrowserAccessibilityManagerWin::OnWindowFocused() {
140 // Make sure we don't call this recursively.
141 if (inside_on_window_focused_)
142 return;
143 inside_on_window_focused_ = true;
145 // This is called either when this web frame gets focused, or when
146 // the root of the accessibility tree changes. In both cases, we need
147 // to fire a focus event on the root and then on the focused element
148 // within the page, if different.
150 // Set this flag so that we'll keep trying to fire these focus events
151 // if they're not successful this time.
152 focus_event_on_root_needed_ = true;
154 if (!delegate_ || !delegate_->AccessibilityViewHasFocus()) {
155 inside_on_window_focused_ = false;
156 return;
159 // Try to fire a focus event on the root first and then the focused node.
160 // This will clear focus_event_on_root_needed_ if successful.
161 if (focus_ != tree_->root() && GetRoot())
162 NotifyAccessibilityEvent(ui::AX_EVENT_FOCUS, GetRoot());
163 BrowserAccessibilityManager::OnWindowFocused();
164 inside_on_window_focused_ = false;
167 void BrowserAccessibilityManagerWin::UserIsReloading() {
168 if (GetRoot())
169 MaybeCallNotifyWinEvent(IA2_EVENT_DOCUMENT_RELOAD, GetRoot());
172 void BrowserAccessibilityManagerWin::NotifyAccessibilityEvent(
173 ui::AXEvent event_type,
174 BrowserAccessibility* node) {
175 BrowserAccessibilityDelegate* root_delegate = GetDelegateFromRootManager();
176 if (!root_delegate || !root_delegate->AccessibilityGetAcceleratedWidget()) {
177 LOG(WARNING) << "Not firing AX event because of no root_delegate or hwnd";
178 return;
181 // Don't fire events when this document might be stale as the user has
182 // started navigating to a new document.
183 if (user_is_navigating_away_)
184 return;
186 // Inline text boxes are an internal implementation detail, we don't
187 // expose them to Windows.
188 if (node->GetRole() == ui::AX_ROLE_INLINE_TEXT_BOX)
189 return;
191 // Don't fire focus, blur, or load complete notifications if the
192 // window isn't focused, because that can confuse screen readers into
193 // entering their "browse" mode.
194 if ((event_type == ui::AX_EVENT_FOCUS ||
195 event_type == ui::AX_EVENT_BLUR ||
196 event_type == ui::AX_EVENT_LOAD_COMPLETE) &&
197 !root_delegate->AccessibilityViewHasFocus()) {
198 return;
201 // NVDA gets confused if we focus the main document element when it hasn't
202 // finished loading and it has no children at all, so suppress that event.
203 if (event_type == ui::AX_EVENT_FOCUS &&
204 node == GetRoot() &&
205 node->PlatformChildCount() == 0 &&
206 !node->HasState(ui::AX_STATE_BUSY) &&
207 !node->GetBoolAttribute(ui::AX_ATTR_DOC_LOADED)) {
208 return;
211 // If a focus event is needed on the root, fire that first before
212 // this event.
213 if (event_type == ui::AX_EVENT_FOCUS && node == GetRoot())
214 focus_event_on_root_needed_ = false;
215 else if (focus_event_on_root_needed_)
216 OnWindowFocused();
218 LONG event_id = EVENT_MIN;
219 switch (event_type) {
220 case ui::AX_EVENT_ACTIVEDESCENDANTCHANGED:
221 event_id = IA2_EVENT_ACTIVE_DESCENDANT_CHANGED;
222 break;
223 case ui::AX_EVENT_ALERT:
224 event_id = EVENT_SYSTEM_ALERT;
225 break;
226 case ui::AX_EVENT_AUTOCORRECTION_OCCURED:
227 event_id = IA2_EVENT_OBJECT_ATTRIBUTE_CHANGED;
228 break;
229 case ui::AX_EVENT_BLUR:
230 // Equivalent to focus on the root.
231 event_id = EVENT_OBJECT_FOCUS;
232 node = GetRoot();
233 break;
234 case ui::AX_EVENT_CHILDREN_CHANGED:
235 event_id = EVENT_OBJECT_REORDER;
236 break;
237 case ui::AX_EVENT_FOCUS:
238 event_id = EVENT_OBJECT_FOCUS;
239 break;
240 case ui::AX_EVENT_LIVE_REGION_CHANGED:
241 if (node->GetBoolAttribute(ui::AX_ATTR_CONTAINER_LIVE_BUSY))
242 return;
243 event_id = EVENT_OBJECT_LIVEREGIONCHANGED;
244 break;
245 case ui::AX_EVENT_LOAD_COMPLETE:
246 event_id = IA2_EVENT_DOCUMENT_LOAD_COMPLETE;
247 break;
248 case ui::AX_EVENT_SCROLL_POSITION_CHANGED:
249 event_id = EVENT_SYSTEM_SCROLLINGEND;
250 break;
251 case ui::AX_EVENT_SCROLLED_TO_ANCHOR:
252 event_id = EVENT_SYSTEM_SCROLLINGSTART;
253 break;
254 case ui::AX_EVENT_SELECTED_CHILDREN_CHANGED:
255 event_id = EVENT_OBJECT_SELECTIONWITHIN;
256 break;
257 case ui::AX_EVENT_TEXT_SELECTION_CHANGED:
258 event_id = IA2_EVENT_TEXT_CARET_MOVED;
259 break;
260 default:
261 // Not all WebKit accessibility events result in a Windows
262 // accessibility notification.
263 break;
266 if (!node)
267 return;
269 if (event_id != EVENT_MIN) {
270 // Pass the node's unique id in the |child_id| argument to NotifyWinEvent;
271 // the AT client will then call get_accChild on the HWND's accessibility
272 // object and pass it that same id, which we can use to retrieve the
273 // IAccessible for this node.
274 MaybeCallNotifyWinEvent(event_id, node);
277 // If this is a layout complete notification (sent when a container scrolls)
278 // and there is a descendant tracked object, send a notification on it.
279 // TODO(dmazzoni): remove once http://crbug.com/113483 is fixed.
280 if (event_type == ui::AX_EVENT_LAYOUT_COMPLETE &&
281 tracked_scroll_object_ &&
282 tracked_scroll_object_->IsDescendantOf(node)) {
283 MaybeCallNotifyWinEvent(
284 IA2_EVENT_VISIBLE_DATA_CHANGED, tracked_scroll_object_);
285 tracked_scroll_object_->Release();
286 tracked_scroll_object_ = NULL;
290 void BrowserAccessibilityManagerWin::OnNodeCreated(ui::AXTree* tree,
291 ui::AXNode* node) {
292 BrowserAccessibilityManager::OnNodeCreated(tree, node);
293 BrowserAccessibility* obj = GetFromAXNode(node);
294 if (!obj)
295 return;
296 if (!obj->IsNative())
297 return;
298 LONG unique_id_win = obj->ToBrowserAccessibilityWin()->unique_id_win();
299 unique_id_to_ax_id_map_[unique_id_win] = obj->GetId();
302 void BrowserAccessibilityManagerWin::OnNodeWillBeDeleted(ui::AXTree* tree,
303 ui::AXNode* node) {
304 BrowserAccessibilityManager::OnNodeWillBeDeleted(tree, node);
305 BrowserAccessibility* obj = GetFromAXNode(node);
306 if (!obj)
307 return;
308 if (!obj->IsNative())
309 return;
310 unique_id_to_ax_id_map_.erase(
311 obj->ToBrowserAccessibilityWin()->unique_id_win());
312 if (obj == tracked_scroll_object_) {
313 tracked_scroll_object_->Release();
314 tracked_scroll_object_ = NULL;
318 void BrowserAccessibilityManagerWin::OnAtomicUpdateFinished(
319 ui::AXTree* tree,
320 bool root_changed,
321 const std::vector<ui::AXTreeDelegate::Change>& changes) {
322 BrowserAccessibilityManager::OnAtomicUpdateFinished(
323 tree, root_changed, changes);
325 if (root_changed) {
326 // In order to make screen readers aware of the new accessibility root,
327 // we need to fire a focus event on it.
328 OnWindowFocused();
331 // Do a sequence of Windows-specific updates on each node. Each one is
332 // done in a single pass that must complete before the next step starts.
333 // The first step moves win_attributes_ to old_win_attributes_ and then
334 // recomputes all of win_attributes_ other than IAccessibleText.
335 for (size_t i = 0; i < changes.size(); ++i) {
336 BrowserAccessibility* obj = GetFromAXNode(changes[i].node);
337 if (obj && obj->IsNative() && !obj->PlatformIsChildOfLeaf())
338 obj->ToBrowserAccessibilityWin()->UpdateStep1ComputeWinAttributes();
341 // The next step updates the hypertext of each node, which is a
342 // concatenation of all of its child text nodes, so it can't run until
343 // the text of all of the nodes was computed in the previous step.
344 for (size_t i = 0; i < changes.size(); ++i) {
345 BrowserAccessibility* obj = GetFromAXNode(changes[i].node);
346 if (obj && obj->IsNative() && !obj->PlatformIsChildOfLeaf())
347 obj->ToBrowserAccessibilityWin()->UpdateStep2ComputeHypertext();
350 // The third step fires events on nodes based on what's changed - like
351 // if the name, value, or description changed, or if the hypertext had
352 // text inserted or removed. It's able to figure out exactly what changed
353 // because we still have old_win_attributes_ populated.
354 // This step has to run after the previous two steps complete because the
355 // client may walk the tree when it receives any of these events.
356 // At the end, it deletes old_win_attributes_ since they're not needed
357 // anymore.
358 for (size_t i = 0; i < changes.size(); ++i) {
359 BrowserAccessibility* obj = GetFromAXNode(changes[i].node);
360 if (obj && obj->IsNative() && !obj->PlatformIsChildOfLeaf()) {
361 obj->ToBrowserAccessibilityWin()->UpdateStep3FireEvents(
362 changes[i].type == AXTreeDelegate::SUBTREE_CREATED);
367 void BrowserAccessibilityManagerWin::TrackScrollingObject(
368 BrowserAccessibilityWin* node) {
369 if (tracked_scroll_object_)
370 tracked_scroll_object_->Release();
371 tracked_scroll_object_ = node;
372 tracked_scroll_object_->AddRef();
375 BrowserAccessibilityWin* BrowserAccessibilityManagerWin::GetFromUniqueIdWin(
376 LONG unique_id_win) {
377 base::hash_map<LONG, int32>::iterator iter =
378 unique_id_to_ax_id_map_.find(unique_id_win);
379 if (iter != unique_id_to_ax_id_map_.end()) {
380 BrowserAccessibility* result = GetFromID(iter->second);
381 if (result && result->IsNative())
382 return result->ToBrowserAccessibilityWin();
385 // Also search all child frames, such as out-of-process iframes or
386 // guest browser plugins.
387 if (delegate()) {
388 std::vector<BrowserAccessibilityManager*> child_frames;
389 delegate()->AccessibilityGetAllChildFrames(&child_frames);
390 for (size_t i = 0; i < child_frames.size(); ++i) {
391 BrowserAccessibilityManagerWin* child_manager =
392 child_frames[i]->ToBrowserAccessibilityManagerWin();
393 BrowserAccessibilityWin* result =
394 child_manager->GetFromUniqueIdWin(unique_id_win);
395 if (result)
396 return result;
400 return NULL;
403 } // namespace content