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"
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
,
23 if (node
->GetIntAttribute(ui::AX_ATTR_CHILD_TREE_ID
) == ax_tree_id
)
26 for (unsigned int i
= 0; i
< node
->InternalChildCount(); ++i
) {
27 BrowserAccessibility
* child
= node
->InternalGetChild(i
);
28 BrowserAccessibility
* result
= FindNodeWithChildTreeId(child
, ax_tree_id
);
38 // Map from AXTreeID to BrowserAccessibilityManager
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
);
77 BrowserAccessibility
* BrowserAccessibilityFactory::Create() {
78 return BrowserAccessibility::Create();
81 BrowserAccessibilityFindInPageInfo::BrowserAccessibilityFindInPageInfo()
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.
97 BrowserAccessibilityManager
* BrowserAccessibilityManager::Create(
98 const SimpleAXTreeUpdate
& initial_tree
,
99 BrowserAccessibilityDelegate
* delegate
,
100 BrowserAccessibilityFactory
* factory
) {
101 return new BrowserAccessibilityManager(initial_tree
, delegate
, factory
);
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
),
118 tree_(new ui::AXSerializableTree()),
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
),
133 tree_(new ui::AXSerializableTree()),
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() {
147 void BrowserAccessibilityManager::Initialize(
148 const SimpleAXTreeUpdate
& initial_tree
) {
149 if (!tree_
->Unserialize(initial_tree
)) {
151 LOG(ERROR
) << tree_
->error();
152 delegate_
->AccessibilityFatalError();
154 LOG(FATAL
) << tree_
->error();
159 SetFocus(tree_
->root(), false);
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
);
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(
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())
192 BrowserAccessibility
*
193 BrowserAccessibilityManager::GetParentNodeFromParentTree() {
197 int parent_tree_id
= GetRoot()->GetIntAttribute(ui::AX_ATTR_PARENT_TREE_ID
);
198 BrowserAccessibilityManager
* parent_manager
=
199 BrowserAccessibilityManager::FromID(parent_tree_id
);
203 // Try to use the cached parent node from the most recent time this
205 if (parent_node_id_from_parent_tree_
) {
206 BrowserAccessibility
* parent_node
= parent_manager
->GetFromID(
207 parent_node_id_from_parent_tree_
);
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_
)
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_
);
220 parent_node_id_from_parent_tree_
= parent_node
->GetId();
227 void BrowserAccessibilityManager::OnWindowFocused() {
229 NotifyAccessibilityEvent(ui::AX_EVENT_FOCUS
, GetFromAXNode(focus_
));
232 void BrowserAccessibilityManager::OnWindowBlurred() {
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() {
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
)) {
271 LOG(ERROR
) << tree_
->error();
272 delegate_
->AccessibilityFatalError();
274 CHECK(false) << tree_
->error();
279 // Set focus to the root if it's not anywhere else.
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
);
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
312 if (delegate_
&& !delegate_
->AccessibilityViewHasFocus())
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
);
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(
349 find_in_page_info_
.active_request_id
= request_id
;
350 if (find_in_page_info_
.request_id
!= request_id
)
353 BrowserAccessibility
* node
= GetFromID(find_in_page_info_
.start_id
);
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())
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
);
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
;
387 BrowserAccessibility
* BrowserAccessibilityManager::GetFocus(
388 BrowserAccessibility
* root
) {
392 if (root
&& !focus_
->IsDescendantOf(root
->node()))
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
));
401 return child_manager
->GetFocus(child_manager
->GetRoot());
407 void BrowserAccessibilityManager::SetFocus(ui::AXNode
* node
, bool notify
) {
411 if (notify
&& node
&& delegate_
)
412 delegate_
->AccessibilitySetFocus(node
->id());
415 void BrowserAccessibilityManager::SetFocus(
416 BrowserAccessibility
* obj
, bool notify
) {
418 SetFocus(obj
->node(), notify
);
421 void BrowserAccessibilityManager::DoDefaultAction(
422 const BrowserAccessibility
& node
) {
424 delegate_
->AccessibilityDoDefaultAction(node
.GetId());
427 void BrowserAccessibilityManager::ScrollToMakeVisible(
428 const BrowserAccessibility
& node
, gfx::Rect subfocus
) {
430 delegate_
->AccessibilityScrollToMakeVisible(node
.GetId(), subfocus
);
434 void BrowserAccessibilityManager::ScrollToPoint(
435 const BrowserAccessibility
& node
, gfx::Point point
) {
437 delegate_
->AccessibilityScrollToPoint(node
.GetId(), point
);
441 void BrowserAccessibilityManager::SetScrollOffset(
442 const BrowserAccessibility
& node
, gfx::Point offset
) {
444 delegate_
->AccessibilitySetScrollOffset(node
.GetId(), offset
);
448 void BrowserAccessibilityManager::SetValue(
449 const BrowserAccessibility
& node
,
450 const base::string16
& value
) {
452 delegate_
->AccessibilitySetValue(node
.GetId(), value
);
455 void BrowserAccessibilityManager::SetTextSelection(
456 const BrowserAccessibility
& node
,
460 delegate_
->AccessibilitySetTextSelection(
461 node
.GetId(), start_offset
, end_offset
);
465 gfx::Rect
BrowserAccessibilityManager::GetViewBounds() {
466 BrowserAccessibilityDelegate
* delegate
= GetDelegateFromRootManager();
468 return delegate
->AccessibilityGetViewBounds();
472 BrowserAccessibility
* BrowserAccessibilityManager::NextInTreeOrder(
473 BrowserAccessibility
* node
) const {
477 if (node
->PlatformChildCount())
478 return node
->PlatformGetChild(0);
481 auto sibling
= node
->GetNextSibling();
485 node
= node
->GetParent();
491 BrowserAccessibility
* BrowserAccessibilityManager::PreviousInTreeOrder(
492 BrowserAccessibility
* node
) const {
496 auto sibling
= node
->GetPreviousSibling();
498 return node
->GetParent();
500 if (sibling
->PlatformChildCount())
501 return sibling
->PlatformDeepestLastChild();
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
);
524 void BrowserAccessibilityManager::OnNodeWillBeDeleted(ui::AXTree
* tree
,
526 if (node
== focus_
&& tree_
) {
527 if (node
!= tree_
->root())
528 SetFocus(tree_
->root(), false);
532 if (id_wrapper_map_
.find(node
->id()) == id_wrapper_map_
.end())
534 GetFromAXNode(node
)->Destroy();
535 id_wrapper_map_
.erase(node
->id());
538 void BrowserAccessibilityManager::OnSubtreeWillBeDeleted(ui::AXTree
* tree
,
540 BrowserAccessibility
* obj
= GetFromAXNode(node
);
542 obj
->OnSubtreeWillBeDeleted();
545 void BrowserAccessibilityManager::OnNodeCreated(ui::AXTree
* tree
,
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
,
555 GetFromAXNode(node
)->OnDataChanged();
558 void BrowserAccessibilityManager::OnAtomicUpdateFinished(
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() {
574 int parent_tree_id
= GetRoot()->GetIntAttribute(ui::AX_ATTR_PARENT_TREE_ID
);
575 BrowserAccessibilityManager
* parent_manager
=
576 BrowserAccessibilityManager::FromID(parent_tree_id
);
578 return parent_manager
->GetDelegateFromRootManager();
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(
588 SimpleAXTreeUpdate update
;
589 serializer
.SerializeChanges(tree_
->root(), &update
);
593 } // namespace content