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"
14 ui::AXTreeUpdate
MakeAXTreeUpdate(
15 const ui::AXNodeData
& node1
,
16 const ui::AXNodeData
& node2
/* = ui::AXNodeData() */,
17 const ui::AXNodeData
& node3
/* = ui::AXNodeData() */,
18 const ui::AXNodeData
& node4
/* = ui::AXNodeData() */,
19 const ui::AXNodeData
& node5
/* = ui::AXNodeData() */,
20 const ui::AXNodeData
& node6
/* = ui::AXNodeData() */,
21 const ui::AXNodeData
& node7
/* = ui::AXNodeData() */,
22 const ui::AXNodeData
& node8
/* = ui::AXNodeData() */,
23 const ui::AXNodeData
& node9
/* = ui::AXNodeData() */) {
24 CR_DEFINE_STATIC_LOCAL(ui::AXNodeData
, empty_data
, ());
25 int32 no_id
= empty_data
.id
;
27 ui::AXTreeUpdate update
;
28 update
.nodes
.push_back(node1
);
29 if (node2
.id
!= no_id
)
30 update
.nodes
.push_back(node2
);
31 if (node3
.id
!= no_id
)
32 update
.nodes
.push_back(node3
);
33 if (node4
.id
!= no_id
)
34 update
.nodes
.push_back(node4
);
35 if (node5
.id
!= no_id
)
36 update
.nodes
.push_back(node5
);
37 if (node6
.id
!= no_id
)
38 update
.nodes
.push_back(node6
);
39 if (node7
.id
!= no_id
)
40 update
.nodes
.push_back(node7
);
41 if (node8
.id
!= no_id
)
42 update
.nodes
.push_back(node8
);
43 if (node9
.id
!= no_id
)
44 update
.nodes
.push_back(node9
);
48 BrowserAccessibility
* BrowserAccessibilityFactory::Create() {
49 return BrowserAccessibility::Create();
52 BrowserAccessibilityFindInPageInfo::BrowserAccessibilityFindInPageInfo()
59 active_request_id(-1) {}
61 #if !defined(OS_WIN) && \
62 !defined(OS_MACOSX) && \
63 !defined(OS_ANDROID) && \
64 !(defined(OS_LINUX) && !defined(OS_CHROMEOS) && defined(USE_X11))
65 // We have subclassess of BrowserAccessibility on some platforms.
66 // For other platforms, instantiate the base class.
68 BrowserAccessibilityManager
* BrowserAccessibilityManager::Create(
69 const ui::AXTreeUpdate
& initial_tree
,
70 BrowserAccessibilityDelegate
* delegate
,
71 BrowserAccessibilityFactory
* factory
) {
72 return new BrowserAccessibilityManager(initial_tree
, delegate
, factory
);
76 BrowserAccessibilityManager::BrowserAccessibilityManager(
77 BrowserAccessibilityDelegate
* delegate
,
78 BrowserAccessibilityFactory
* factory
)
79 : delegate_(delegate
),
81 tree_(new ui::AXSerializableTree()),
83 user_is_navigating_away_(false),
84 osk_state_(OSK_ALLOWED
) {
85 tree_
->SetDelegate(this);
88 BrowserAccessibilityManager::BrowserAccessibilityManager(
89 const ui::AXTreeUpdate
& initial_tree
,
90 BrowserAccessibilityDelegate
* delegate
,
91 BrowserAccessibilityFactory
* factory
)
92 : delegate_(delegate
),
94 tree_(new ui::AXSerializableTree()),
96 user_is_navigating_away_(false),
97 osk_state_(OSK_ALLOWED
) {
98 tree_
->SetDelegate(this);
99 Initialize(initial_tree
);
102 BrowserAccessibilityManager::~BrowserAccessibilityManager() {
106 void BrowserAccessibilityManager::Initialize(
107 const ui::AXTreeUpdate
& initial_tree
) {
108 if (!tree_
->Unserialize(initial_tree
)) {
110 LOG(ERROR
) << tree_
->error();
111 delegate_
->AccessibilityFatalError();
113 LOG(FATAL
) << tree_
->error();
118 SetFocus(tree_
->root(), false);
122 ui::AXTreeUpdate
BrowserAccessibilityManager::GetEmptyDocument() {
123 ui::AXNodeData empty_document
;
124 empty_document
.id
= 0;
125 empty_document
.role
= ui::AX_ROLE_ROOT_WEB_AREA
;
126 ui::AXTreeUpdate update
;
127 update
.nodes
.push_back(empty_document
);
131 BrowserAccessibility
* BrowserAccessibilityManager::GetRoot() {
132 // tree_->root() can be null during AXTreeDelegate callbacks.
133 ui::AXNode
* root
= tree_
->root();
134 return root
? GetFromAXNode(root
) : nullptr;
137 BrowserAccessibility
* BrowserAccessibilityManager::GetFromAXNode(
139 return GetFromID(node
->id());
142 BrowserAccessibility
* BrowserAccessibilityManager::GetFromID(int32 id
) {
143 base::hash_map
<int32
, BrowserAccessibility
*>::iterator iter
=
144 id_wrapper_map_
.find(id
);
145 if (iter
!= id_wrapper_map_
.end())
150 void BrowserAccessibilityManager::OnWindowFocused() {
152 NotifyAccessibilityEvent(ui::AX_EVENT_FOCUS
, GetFromAXNode(focus_
));
155 void BrowserAccessibilityManager::OnWindowBlurred() {
157 NotifyAccessibilityEvent(ui::AX_EVENT_BLUR
, GetFromAXNode(focus_
));
160 void BrowserAccessibilityManager::UserIsNavigatingAway() {
161 user_is_navigating_away_
= true;
164 void BrowserAccessibilityManager::UserIsReloading() {
165 user_is_navigating_away_
= true;
168 void BrowserAccessibilityManager::NavigationSucceeded() {
169 user_is_navigating_away_
= false;
172 void BrowserAccessibilityManager::NavigationFailed() {
173 user_is_navigating_away_
= false;
176 void BrowserAccessibilityManager::GotMouseDown() {
177 osk_state_
= OSK_ALLOWED_WITHIN_FOCUSED_OBJECT
;
178 NotifyAccessibilityEvent(ui::AX_EVENT_FOCUS
, GetFromAXNode(focus_
));
181 bool BrowserAccessibilityManager::UseRootScrollOffsetsWhenComputingBounds() {
185 void BrowserAccessibilityManager::OnAccessibilityEvents(
186 const std::vector
<AccessibilityHostMsg_EventParams
>& params
) {
187 bool should_send_initial_focus
= false;
189 // Process all changes to the accessibility tree first.
190 for (uint32 index
= 0; index
< params
.size(); index
++) {
191 const AccessibilityHostMsg_EventParams
& param
= params
[index
];
192 if (!tree_
->Unserialize(param
.update
)) {
194 LOG(ERROR
) << tree_
->error();
195 delegate_
->AccessibilityFatalError();
197 CHECK(false) << tree_
->error();
202 // Set focus to the root if it's not anywhere else.
204 SetFocus(tree_
->root(), false);
205 should_send_initial_focus
= true;
209 if (should_send_initial_focus
&&
210 (!delegate_
|| delegate_
->AccessibilityViewHasFocus())) {
211 NotifyAccessibilityEvent(ui::AX_EVENT_FOCUS
, GetFromAXNode(focus_
));
214 // Now iterate over the events again and fire the events.
215 for (uint32 index
= 0; index
< params
.size(); index
++) {
216 const AccessibilityHostMsg_EventParams
& param
= params
[index
];
218 // Find the node corresponding to the id that's the target of the
219 // event (which may not be the root of the update tree).
220 ui::AXNode
* node
= tree_
->GetFromId(param
.id
);
224 ui::AXEvent event_type
= param
.event_type
;
225 if (event_type
== ui::AX_EVENT_FOCUS
||
226 event_type
== ui::AX_EVENT_BLUR
) {
227 SetFocus(node
, false);
229 if (osk_state_
!= OSK_DISALLOWED_BECAUSE_TAB_HIDDEN
&&
230 osk_state_
!= OSK_DISALLOWED_BECAUSE_TAB_JUST_APPEARED
)
231 osk_state_
= OSK_ALLOWED
;
233 // Don't send a native focus event if the window itself doesn't
235 if (delegate_
&& !delegate_
->AccessibilityViewHasFocus())
239 // Send the event event to the operating system.
240 NotifyAccessibilityEvent(event_type
, GetFromAXNode(node
));
244 void BrowserAccessibilityManager::OnLocationChanges(
245 const std::vector
<AccessibilityHostMsg_LocationChangeParams
>& params
) {
246 for (size_t i
= 0; i
< params
.size(); ++i
) {
247 BrowserAccessibility
* obj
= GetFromID(params
[i
].id
);
250 ui::AXNode
* node
= obj
->node();
251 node
->SetLocation(params
[i
].new_location
);
252 obj
->OnLocationChanged();
256 void BrowserAccessibilityManager::OnFindInPageResult(
257 int request_id
, int match_index
, int start_id
, int start_offset
,
258 int end_id
, int end_offset
) {
259 find_in_page_info_
.request_id
= request_id
;
260 find_in_page_info_
.match_index
= match_index
;
261 find_in_page_info_
.start_id
= start_id
;
262 find_in_page_info_
.start_offset
= start_offset
;
263 find_in_page_info_
.end_id
= end_id
;
264 find_in_page_info_
.end_offset
= end_offset
;
266 if (find_in_page_info_
.active_request_id
== request_id
)
267 ActivateFindInPageResult(request_id
);
270 void BrowserAccessibilityManager::ActivateFindInPageResult(
272 find_in_page_info_
.active_request_id
= request_id
;
273 if (find_in_page_info_
.request_id
!= request_id
)
276 BrowserAccessibility
* node
= GetFromID(find_in_page_info_
.start_id
);
280 // If an ancestor of this node is a leaf node, fire the notification on that.
281 BrowserAccessibility
* ancestor
= node
->GetParent();
282 while (ancestor
&& ancestor
!= GetRoot()) {
283 if (ancestor
->PlatformIsLeaf())
285 ancestor
= ancestor
->GetParent();
288 // The "scrolled to anchor" notification is a great way to get a
289 // screen reader to jump directly to a specific location in a document.
290 NotifyAccessibilityEvent(ui::AX_EVENT_SCROLLED_TO_ANCHOR
, node
);
293 BrowserAccessibility
* BrowserAccessibilityManager::GetActiveDescendantFocus(
294 BrowserAccessibility
* root
) {
295 BrowserAccessibility
* node
= BrowserAccessibilityManager::GetFocus(root
);
299 int active_descendant_id
;
300 if (node
->GetIntAttribute(ui::AX_ATTR_ACTIVEDESCENDANT_ID
,
301 &active_descendant_id
)) {
302 BrowserAccessibility
* active_descendant
=
303 node
->manager()->GetFromID(active_descendant_id
);
304 if (active_descendant
)
305 return active_descendant
;
310 BrowserAccessibility
* BrowserAccessibilityManager::GetFocus(
311 BrowserAccessibility
* root
) {
315 if (root
&& !focus_
->IsDescendantOf(root
->node()))
318 BrowserAccessibility
* obj
= GetFromAXNode(focus_
);
319 if (delegate() && obj
->HasBoolAttribute(ui::AX_ATTR_IS_AX_TREE_HOST
)) {
320 BrowserAccessibilityManager
* child_manager
=
321 delegate()->AccessibilityGetChildFrame(obj
->GetId());
323 return child_manager
->GetFocus(child_manager
->GetRoot());
329 void BrowserAccessibilityManager::SetFocus(ui::AXNode
* node
, bool notify
) {
333 if (notify
&& node
&& delegate_
)
334 delegate_
->AccessibilitySetFocus(node
->id());
337 void BrowserAccessibilityManager::SetFocus(
338 BrowserAccessibility
* obj
, bool notify
) {
340 SetFocus(obj
->node(), notify
);
343 void BrowserAccessibilityManager::DoDefaultAction(
344 const BrowserAccessibility
& node
) {
346 delegate_
->AccessibilityDoDefaultAction(node
.GetId());
349 void BrowserAccessibilityManager::ScrollToMakeVisible(
350 const BrowserAccessibility
& node
, gfx::Rect subfocus
) {
352 delegate_
->AccessibilityScrollToMakeVisible(node
.GetId(), subfocus
);
356 void BrowserAccessibilityManager::ScrollToPoint(
357 const BrowserAccessibility
& node
, gfx::Point point
) {
359 delegate_
->AccessibilityScrollToPoint(node
.GetId(), point
);
363 void BrowserAccessibilityManager::SetScrollOffset(
364 const BrowserAccessibility
& node
, gfx::Point offset
) {
366 delegate_
->AccessibilitySetScrollOffset(node
.GetId(), offset
);
370 void BrowserAccessibilityManager::SetValue(
371 const BrowserAccessibility
& node
,
372 const base::string16
& value
) {
374 delegate_
->AccessibilitySetValue(node
.GetId(), value
);
377 void BrowserAccessibilityManager::SetTextSelection(
378 const BrowserAccessibility
& node
,
382 delegate_
->AccessibilitySetTextSelection(
383 node
.GetId(), start_offset
, end_offset
);
387 gfx::Rect
BrowserAccessibilityManager::GetViewBounds() {
388 BrowserAccessibilityDelegate
* delegate
= GetDelegateFromRootManager();
390 return delegate
->AccessibilityGetViewBounds();
394 BrowserAccessibility
* BrowserAccessibilityManager::NextInTreeOrder(
395 BrowserAccessibility
* node
) {
399 if (node
->PlatformChildCount() > 0)
400 return node
->PlatformGetChild(0);
402 if (node
->GetParent() &&
403 node
->GetIndexInParent() <
404 static_cast<int>(node
->GetParent()->PlatformChildCount()) - 1) {
405 return node
->GetParent()->PlatformGetChild(node
->GetIndexInParent() + 1);
407 node
= node
->GetParent();
413 BrowserAccessibility
* BrowserAccessibilityManager::PreviousInTreeOrder(
414 BrowserAccessibility
* node
) {
418 if (node
->GetParent() && node
->GetIndexInParent() > 0) {
419 node
= node
->GetParent()->PlatformGetChild(node
->GetIndexInParent() - 1);
420 while (node
->PlatformChildCount() > 0)
421 node
= node
->PlatformGetChild(node
->PlatformChildCount() - 1);
425 return node
->GetParent();
428 void BrowserAccessibilityManager::OnNodeWillBeDeleted(ui::AXTree
* tree
,
430 if (node
== focus_
&& tree_
) {
431 if (node
!= tree_
->root())
432 SetFocus(tree_
->root(), false);
436 if (id_wrapper_map_
.find(node
->id()) == id_wrapper_map_
.end())
438 GetFromAXNode(node
)->Destroy();
439 id_wrapper_map_
.erase(node
->id());
442 void BrowserAccessibilityManager::OnSubtreeWillBeDeleted(ui::AXTree
* tree
,
444 BrowserAccessibility
* obj
= GetFromAXNode(node
);
446 obj
->OnSubtreeWillBeDeleted();
449 void BrowserAccessibilityManager::OnNodeCreated(ui::AXTree
* tree
,
451 BrowserAccessibility
* wrapper
= factory_
->Create();
452 wrapper
->Init(this, node
);
453 id_wrapper_map_
[node
->id()] = wrapper
;
454 wrapper
->OnDataChanged();
457 void BrowserAccessibilityManager::OnNodeChanged(ui::AXTree
* tree
,
459 GetFromAXNode(node
)->OnDataChanged();
462 void BrowserAccessibilityManager::OnAtomicUpdateFinished(
465 const std::vector
<ui::AXTreeDelegate::Change
>& changes
) {
468 BrowserAccessibilityDelegate
*
469 BrowserAccessibilityManager::GetDelegateFromRootManager() {
470 BrowserAccessibilityManager
* manager
= this;
471 while (manager
->delegate()) {
472 BrowserAccessibility
* host_node_in_parent_frame
=
473 manager
->delegate()->AccessibilityGetParentFrame();
474 if (!host_node_in_parent_frame
)
476 manager
= host_node_in_parent_frame
->manager();
478 return manager
->delegate();
481 ui::AXTreeUpdate
BrowserAccessibilityManager::SnapshotAXTreeForTesting() {
482 scoped_ptr
<ui::AXTreeSource
<const ui::AXNode
*> > tree_source(
483 tree_
->CreateTreeSource());
484 ui::AXTreeSerializer
<const ui::AXNode
*> serializer(tree_source
.get());
485 ui::AXTreeUpdate update
;
486 serializer
.SerializeChanges(tree_
->root(), &update
);
490 } // namespace content