This sets up API to release OutputSurface from LTHClient.
[chromium-blink-merge.git] / content / browser / accessibility / browser_accessibility_manager.cc
blob76ab1e74925e5ef7e59905e13bf9e2e36a4692a3
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.h"
7 #include "base/logging.h"
8 #include "content/browser/accessibility/browser_accessibility.h"
9 #include "content/common/accessibility_messages.h"
10 #include "ui/accessibility/ax_tree_serializer.h"
12 namespace content {
14 namespace {
16 // Search the tree recursively from |node| and return any node that has
17 // a child tree ID of |ax_tree_id|.
18 BrowserAccessibility* FindNodeWithChildTreeId(BrowserAccessibility* node,
19 int ax_tree_id) {
20 if (!node)
21 return nullptr;
23 if (node->GetIntAttribute(ui::AX_ATTR_CHILD_TREE_ID) == ax_tree_id)
24 return node;
26 for (unsigned int i = 0; i < node->InternalChildCount(); ++i) {
27 BrowserAccessibility* child = node->InternalGetChild(i);
28 BrowserAccessibility* result = FindNodeWithChildTreeId(child, ax_tree_id);
29 if (result)
30 return result;
33 return nullptr;
36 } // namespace
38 // Map from AXTreeID to BrowserAccessibilityManager
39 using AXTreeIDMap =
40 base::hash_map<AXTreeIDRegistry::AXTreeID, BrowserAccessibilityManager*>;
41 base::LazyInstance<AXTreeIDMap> g_ax_tree_id_map = LAZY_INSTANCE_INITIALIZER;
43 SimpleAXTreeUpdate MakeAXTreeUpdate(
44 const ui::AXNodeData& node1,
45 const ui::AXNodeData& node2 /* = ui::AXNodeData() */,
46 const ui::AXNodeData& node3 /* = ui::AXNodeData() */,
47 const ui::AXNodeData& node4 /* = ui::AXNodeData() */,
48 const ui::AXNodeData& node5 /* = ui::AXNodeData() */,
49 const ui::AXNodeData& node6 /* = ui::AXNodeData() */,
50 const ui::AXNodeData& node7 /* = ui::AXNodeData() */,
51 const ui::AXNodeData& node8 /* = ui::AXNodeData() */,
52 const ui::AXNodeData& node9 /* = ui::AXNodeData() */) {
53 CR_DEFINE_STATIC_LOCAL(ui::AXNodeData, empty_data, ());
54 int32 no_id = empty_data.id;
56 SimpleAXTreeUpdate update;
57 update.nodes.push_back(node1);
58 if (node2.id != no_id)
59 update.nodes.push_back(node2);
60 if (node3.id != no_id)
61 update.nodes.push_back(node3);
62 if (node4.id != no_id)
63 update.nodes.push_back(node4);
64 if (node5.id != no_id)
65 update.nodes.push_back(node5);
66 if (node6.id != no_id)
67 update.nodes.push_back(node6);
68 if (node7.id != no_id)
69 update.nodes.push_back(node7);
70 if (node8.id != no_id)
71 update.nodes.push_back(node8);
72 if (node9.id != no_id)
73 update.nodes.push_back(node9);
74 return update;
77 BrowserAccessibility* BrowserAccessibilityFactory::Create() {
78 return BrowserAccessibility::Create();
81 BrowserAccessibilityFindInPageInfo::BrowserAccessibilityFindInPageInfo()
82 : request_id(-1),
83 match_index(-1),
84 start_id(-1),
85 start_offset(0),
86 end_id(-1),
87 end_offset(-1),
88 active_request_id(-1) {}
90 #if !defined(OS_WIN) && \
91 !defined(OS_MACOSX) && \
92 !defined(OS_ANDROID) && \
93 !(defined(OS_LINUX) && !defined(OS_CHROMEOS) && defined(USE_X11))
94 // We have subclassess of BrowserAccessibility on some platforms.
95 // For other platforms, instantiate the base class.
96 // static
97 BrowserAccessibilityManager* BrowserAccessibilityManager::Create(
98 const SimpleAXTreeUpdate& initial_tree,
99 BrowserAccessibilityDelegate* delegate,
100 BrowserAccessibilityFactory* factory) {
101 return new BrowserAccessibilityManager(initial_tree, delegate, factory);
103 #endif
105 // static
106 BrowserAccessibilityManager* BrowserAccessibilityManager::FromID(
107 AXTreeIDRegistry::AXTreeID ax_tree_id) {
108 AXTreeIDMap* ax_tree_id_map = g_ax_tree_id_map.Pointer();
109 auto iter = ax_tree_id_map->find(ax_tree_id);
110 return iter == ax_tree_id_map->end() ? nullptr : iter->second;
113 BrowserAccessibilityManager::BrowserAccessibilityManager(
114 BrowserAccessibilityDelegate* delegate,
115 BrowserAccessibilityFactory* factory)
116 : delegate_(delegate),
117 factory_(factory),
118 tree_(new ui::AXSerializableTree()),
119 focus_(NULL),
120 user_is_navigating_away_(false),
121 osk_state_(OSK_ALLOWED),
122 ax_tree_id_(AXTreeIDRegistry::kNoAXTreeID),
123 parent_node_id_from_parent_tree_(0) {
124 tree_->SetDelegate(this);
127 BrowserAccessibilityManager::BrowserAccessibilityManager(
128 const SimpleAXTreeUpdate& initial_tree,
129 BrowserAccessibilityDelegate* delegate,
130 BrowserAccessibilityFactory* factory)
131 : delegate_(delegate),
132 factory_(factory),
133 tree_(new ui::AXSerializableTree()),
134 focus_(NULL),
135 user_is_navigating_away_(false),
136 osk_state_(OSK_ALLOWED),
137 ax_tree_id_(AXTreeIDRegistry::kNoAXTreeID),
138 parent_node_id_from_parent_tree_(0) {
139 tree_->SetDelegate(this);
140 Initialize(initial_tree);
143 BrowserAccessibilityManager::~BrowserAccessibilityManager() {
144 tree_.reset(NULL);
147 void BrowserAccessibilityManager::Initialize(
148 const SimpleAXTreeUpdate& initial_tree) {
149 if (!tree_->Unserialize(initial_tree)) {
150 if (delegate_) {
151 LOG(ERROR) << tree_->error();
152 delegate_->AccessibilityFatalError();
153 } else {
154 LOG(FATAL) << tree_->error();
158 if (!focus_)
159 SetFocus(tree_->root(), false);
162 // static
163 SimpleAXTreeUpdate
164 BrowserAccessibilityManager::GetEmptyDocument() {
165 ui::AXNodeData empty_document;
166 empty_document.id = 0;
167 empty_document.role = ui::AX_ROLE_ROOT_WEB_AREA;
168 SimpleAXTreeUpdate update;
169 update.nodes.push_back(empty_document);
170 return update;
173 BrowserAccessibility* BrowserAccessibilityManager::GetRoot() {
174 // tree_->root() can be null during AXTreeDelegate callbacks.
175 ui::AXNode* root = tree_->root();
176 return root ? GetFromAXNode(root) : nullptr;
179 BrowserAccessibility* BrowserAccessibilityManager::GetFromAXNode(
180 ui::AXNode* node) {
181 return GetFromID(node->id());
184 BrowserAccessibility* BrowserAccessibilityManager::GetFromID(int32 id) {
185 base::hash_map<int32, BrowserAccessibility*>::iterator iter =
186 id_wrapper_map_.find(id);
187 if (iter != id_wrapper_map_.end())
188 return iter->second;
189 return NULL;
192 BrowserAccessibility*
193 BrowserAccessibilityManager::GetParentNodeFromParentTree() {
194 if (!GetRoot())
195 return nullptr;
197 int parent_tree_id = GetRoot()->GetIntAttribute(ui::AX_ATTR_PARENT_TREE_ID);
198 BrowserAccessibilityManager* parent_manager =
199 BrowserAccessibilityManager::FromID(parent_tree_id);
200 if (!parent_manager)
201 return nullptr;
203 // Try to use the cached parent node from the most recent time this
204 // was called.
205 if (parent_node_id_from_parent_tree_) {
206 BrowserAccessibility* parent_node = parent_manager->GetFromID(
207 parent_node_id_from_parent_tree_);
208 if (parent_node) {
209 int parent_child_tree_id =
210 parent_node->GetIntAttribute(ui::AX_ATTR_CHILD_TREE_ID);
211 if (parent_child_tree_id == ax_tree_id_)
212 return parent_node;
216 // If that fails, search for it and cache it for next time.
217 BrowserAccessibility* parent_node = FindNodeWithChildTreeId(
218 parent_manager->GetRoot(), ax_tree_id_);
219 if (parent_node) {
220 parent_node_id_from_parent_tree_ = parent_node->GetId();
221 return parent_node;
224 return nullptr;
227 void BrowserAccessibilityManager::OnWindowFocused() {
228 if (focus_)
229 NotifyAccessibilityEvent(ui::AX_EVENT_FOCUS, GetFromAXNode(focus_));
232 void BrowserAccessibilityManager::OnWindowBlurred() {
233 if (focus_)
234 NotifyAccessibilityEvent(ui::AX_EVENT_BLUR, GetFromAXNode(focus_));
237 void BrowserAccessibilityManager::UserIsNavigatingAway() {
238 user_is_navigating_away_ = true;
241 void BrowserAccessibilityManager::UserIsReloading() {
242 user_is_navigating_away_ = true;
245 void BrowserAccessibilityManager::NavigationSucceeded() {
246 user_is_navigating_away_ = false;
249 void BrowserAccessibilityManager::NavigationFailed() {
250 user_is_navigating_away_ = false;
253 void BrowserAccessibilityManager::GotMouseDown() {
254 osk_state_ = OSK_ALLOWED_WITHIN_FOCUSED_OBJECT;
255 NotifyAccessibilityEvent(ui::AX_EVENT_FOCUS, GetFromAXNode(focus_));
258 bool BrowserAccessibilityManager::UseRootScrollOffsetsWhenComputingBounds() {
259 return true;
262 void BrowserAccessibilityManager::OnAccessibilityEvents(
263 const std::vector<AXEventNotificationDetails>& details) {
264 bool should_send_initial_focus = false;
266 // Process all changes to the accessibility tree first.
267 for (uint32 index = 0; index < details.size(); ++index) {
268 const AXEventNotificationDetails& detail = details[index];
269 if (!tree_->Unserialize(detail.update)) {
270 if (delegate_) {
271 LOG(ERROR) << tree_->error();
272 delegate_->AccessibilityFatalError();
273 } else {
274 CHECK(false) << tree_->error();
276 return;
279 // Set focus to the root if it's not anywhere else.
280 if (!focus_) {
281 SetFocus(tree_->root(), false);
282 should_send_initial_focus = true;
286 if (should_send_initial_focus &&
287 (!delegate_ || delegate_->AccessibilityViewHasFocus())) {
288 NotifyAccessibilityEvent(ui::AX_EVENT_FOCUS, GetFromAXNode(focus_));
291 // Now iterate over the events again and fire the events.
292 for (uint32 index = 0; index < details.size(); index++) {
293 const AXEventNotificationDetails& detail = details[index];
295 // Find the node corresponding to the id that's the target of the
296 // event (which may not be the root of the update tree).
297 ui::AXNode* node = tree_->GetFromId(detail.id);
298 if (!node)
299 continue;
301 ui::AXEvent event_type = detail.event_type;
302 if (event_type == ui::AX_EVENT_FOCUS ||
303 event_type == ui::AX_EVENT_BLUR) {
304 SetFocus(node, false);
306 if (osk_state_ != OSK_DISALLOWED_BECAUSE_TAB_HIDDEN &&
307 osk_state_ != OSK_DISALLOWED_BECAUSE_TAB_JUST_APPEARED)
308 osk_state_ = OSK_ALLOWED;
310 // Don't send a native focus event if the window itself doesn't
311 // have focus.
312 if (delegate_ && !delegate_->AccessibilityViewHasFocus())
313 continue;
316 // Send the event event to the operating system.
317 NotifyAccessibilityEvent(event_type, GetFromAXNode(node));
321 void BrowserAccessibilityManager::OnLocationChanges(
322 const std::vector<AccessibilityHostMsg_LocationChangeParams>& params) {
323 for (size_t i = 0; i < params.size(); ++i) {
324 BrowserAccessibility* obj = GetFromID(params[i].id);
325 if (!obj)
326 continue;
327 ui::AXNode* node = obj->node();
328 node->SetLocation(params[i].new_location);
329 obj->OnLocationChanged();
333 void BrowserAccessibilityManager::OnFindInPageResult(
334 int request_id, int match_index, int start_id, int start_offset,
335 int end_id, int end_offset) {
336 find_in_page_info_.request_id = request_id;
337 find_in_page_info_.match_index = match_index;
338 find_in_page_info_.start_id = start_id;
339 find_in_page_info_.start_offset = start_offset;
340 find_in_page_info_.end_id = end_id;
341 find_in_page_info_.end_offset = end_offset;
343 if (find_in_page_info_.active_request_id == request_id)
344 ActivateFindInPageResult(request_id);
347 void BrowserAccessibilityManager::ActivateFindInPageResult(
348 int request_id) {
349 find_in_page_info_.active_request_id = request_id;
350 if (find_in_page_info_.request_id != request_id)
351 return;
353 BrowserAccessibility* node = GetFromID(find_in_page_info_.start_id);
354 if (!node)
355 return;
357 // If an ancestor of this node is a leaf node, fire the notification on that.
358 BrowserAccessibility* ancestor = node->GetParent();
359 while (ancestor && ancestor != GetRoot()) {
360 if (ancestor->PlatformIsLeaf())
361 node = ancestor;
362 ancestor = ancestor->GetParent();
365 // The "scrolled to anchor" notification is a great way to get a
366 // screen reader to jump directly to a specific location in a document.
367 NotifyAccessibilityEvent(ui::AX_EVENT_SCROLLED_TO_ANCHOR, node);
370 BrowserAccessibility* BrowserAccessibilityManager::GetActiveDescendantFocus(
371 BrowserAccessibility* root) {
372 BrowserAccessibility* node = BrowserAccessibilityManager::GetFocus(root);
373 if (!node)
374 return NULL;
376 int active_descendant_id;
377 if (node->GetIntAttribute(ui::AX_ATTR_ACTIVEDESCENDANT_ID,
378 &active_descendant_id)) {
379 BrowserAccessibility* active_descendant =
380 node->manager()->GetFromID(active_descendant_id);
381 if (active_descendant)
382 return active_descendant;
384 return node;
387 BrowserAccessibility* BrowserAccessibilityManager::GetFocus(
388 BrowserAccessibility* root) {
389 if (!focus_)
390 return NULL;
392 if (root && !focus_->IsDescendantOf(root->node()))
393 return NULL;
395 BrowserAccessibility* obj = GetFromAXNode(focus_);
396 if (obj->HasIntAttribute(ui::AX_ATTR_CHILD_TREE_ID)) {
397 BrowserAccessibilityManager* child_manager =
398 BrowserAccessibilityManager::FromID(
399 obj->GetIntAttribute(ui::AX_ATTR_CHILD_TREE_ID));
400 if (child_manager)
401 return child_manager->GetFocus(child_manager->GetRoot());
404 return obj;
407 void BrowserAccessibilityManager::SetFocus(ui::AXNode* node, bool notify) {
408 if (focus_ != node)
409 focus_ = node;
411 if (notify && node && delegate_)
412 delegate_->AccessibilitySetFocus(node->id());
415 void BrowserAccessibilityManager::SetFocus(
416 BrowserAccessibility* obj, bool notify) {
417 if (obj->node())
418 SetFocus(obj->node(), notify);
421 void BrowserAccessibilityManager::DoDefaultAction(
422 const BrowserAccessibility& node) {
423 if (delegate_)
424 delegate_->AccessibilityDoDefaultAction(node.GetId());
427 void BrowserAccessibilityManager::ScrollToMakeVisible(
428 const BrowserAccessibility& node, gfx::Rect subfocus) {
429 if (delegate_) {
430 delegate_->AccessibilityScrollToMakeVisible(node.GetId(), subfocus);
434 void BrowserAccessibilityManager::ScrollToPoint(
435 const BrowserAccessibility& node, gfx::Point point) {
436 if (delegate_) {
437 delegate_->AccessibilityScrollToPoint(node.GetId(), point);
441 void BrowserAccessibilityManager::SetScrollOffset(
442 const BrowserAccessibility& node, gfx::Point offset) {
443 if (delegate_) {
444 delegate_->AccessibilitySetScrollOffset(node.GetId(), offset);
448 void BrowserAccessibilityManager::SetValue(
449 const BrowserAccessibility& node,
450 const base::string16& value) {
451 if (delegate_)
452 delegate_->AccessibilitySetValue(node.GetId(), value);
455 void BrowserAccessibilityManager::SetTextSelection(
456 const BrowserAccessibility& node,
457 int start_offset,
458 int end_offset) {
459 if (delegate_) {
460 delegate_->AccessibilitySetTextSelection(
461 node.GetId(), start_offset, end_offset);
465 gfx::Rect BrowserAccessibilityManager::GetViewBounds() {
466 BrowserAccessibilityDelegate* delegate = GetDelegateFromRootManager();
467 if (delegate)
468 return delegate->AccessibilityGetViewBounds();
469 return gfx::Rect();
472 BrowserAccessibility* BrowserAccessibilityManager::NextInTreeOrder(
473 BrowserAccessibility* node) const {
474 if (!node)
475 return nullptr;
477 if (node->PlatformChildCount())
478 return node->PlatformGetChild(0);
480 while (node) {
481 auto sibling = node->GetNextSibling();
482 if (sibling)
483 return sibling;
485 node = node->GetParent();
488 return nullptr;
491 BrowserAccessibility* BrowserAccessibilityManager::PreviousInTreeOrder(
492 BrowserAccessibility* node) const {
493 if (!node)
494 return nullptr;
496 auto sibling = node->GetPreviousSibling();
497 if (!sibling)
498 return node->GetParent();
500 if (sibling->PlatformChildCount())
501 return sibling->PlatformDeepestLastChild();
503 return sibling;
506 BrowserAccessibility* BrowserAccessibilityManager::PreviousTextOnlyObject(
507 BrowserAccessibility* node) const {
508 BrowserAccessibility* previous_node = PreviousInTreeOrder(node);
509 while (previous_node && !previous_node->IsTextOnlyObject())
510 previous_node = PreviousInTreeOrder(previous_node);
512 return previous_node;
515 BrowserAccessibility* BrowserAccessibilityManager::NextTextOnlyObject(
516 BrowserAccessibility* node) const {
517 BrowserAccessibility* next_node = NextInTreeOrder(node);
518 while (next_node && !next_node->IsTextOnlyObject())
519 next_node = NextInTreeOrder(next_node);
521 return next_node;
524 void BrowserAccessibilityManager::OnNodeWillBeDeleted(ui::AXTree* tree,
525 ui::AXNode* node) {
526 if (node == focus_ && tree_) {
527 if (node != tree_->root())
528 SetFocus(tree_->root(), false);
529 else
530 focus_ = NULL;
532 if (id_wrapper_map_.find(node->id()) == id_wrapper_map_.end())
533 return;
534 GetFromAXNode(node)->Destroy();
535 id_wrapper_map_.erase(node->id());
538 void BrowserAccessibilityManager::OnSubtreeWillBeDeleted(ui::AXTree* tree,
539 ui::AXNode* node) {
540 BrowserAccessibility* obj = GetFromAXNode(node);
541 if (obj)
542 obj->OnSubtreeWillBeDeleted();
545 void BrowserAccessibilityManager::OnNodeCreated(ui::AXTree* tree,
546 ui::AXNode* node) {
547 BrowserAccessibility* wrapper = factory_->Create();
548 wrapper->Init(this, node);
549 id_wrapper_map_[node->id()] = wrapper;
550 wrapper->OnDataChanged();
553 void BrowserAccessibilityManager::OnNodeChanged(ui::AXTree* tree,
554 ui::AXNode* node) {
555 GetFromAXNode(node)->OnDataChanged();
558 void BrowserAccessibilityManager::OnAtomicUpdateFinished(
559 ui::AXTree* tree,
560 bool root_changed,
561 const std::vector<ui::AXTreeDelegate::Change>& changes) {
562 if (GetRoot()->HasIntAttribute(ui::AX_ATTR_TREE_ID) &&
563 GetRoot()->GetIntAttribute(ui::AX_ATTR_TREE_ID) != ax_tree_id_) {
564 g_ax_tree_id_map.Get().erase(ax_tree_id_);
565 ax_tree_id_ = GetRoot()->GetIntAttribute(ui::AX_ATTR_TREE_ID);
566 g_ax_tree_id_map.Get().insert(std::make_pair(ax_tree_id_, this));
570 BrowserAccessibilityDelegate*
571 BrowserAccessibilityManager::GetDelegateFromRootManager() {
572 if (!GetRoot())
573 return nullptr;
574 int parent_tree_id = GetRoot()->GetIntAttribute(ui::AX_ATTR_PARENT_TREE_ID);
575 BrowserAccessibilityManager* parent_manager =
576 BrowserAccessibilityManager::FromID(parent_tree_id);
577 if (parent_manager)
578 return parent_manager->GetDelegateFromRootManager();
579 return delegate();
582 SimpleAXTreeUpdate
583 BrowserAccessibilityManager::SnapshotAXTreeForTesting() {
584 scoped_ptr<ui::AXTreeSource<const ui::AXNode*, ui::AXNodeData> > tree_source(
585 tree_->CreateTreeSource());
586 ui::AXTreeSerializer<const ui::AXNode*, ui::AXNodeData> serializer(
587 tree_source.get());
588 SimpleAXTreeUpdate update;
589 serializer.SerializeChanges(tree_->root(), &update);
590 return update;
593 } // namespace content