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_MACOSX) && \
63 !defined(OS_ANDROID) \
64 // We have subclassess of BrowserAccessibilityManager on Mac, and Win. For any
65 // other platform, instantiate the base class.
67 BrowserAccessibilityManager* BrowserAccessibilityManager::Create(
68 const ui::AXTreeUpdate& initial_tree,
69 BrowserAccessibilityDelegate* delegate,
70 BrowserAccessibilityFactory* factory) {
71 return new BrowserAccessibilityManager(initial_tree, delegate, factory);
75 BrowserAccessibilityManager::BrowserAccessibilityManager(
76 BrowserAccessibilityDelegate* delegate,
77 BrowserAccessibilityFactory* factory)
78 : delegate_(delegate),
80 tree_(new ui::AXSerializableTree()),
82 osk_state_(OSK_ALLOWED) {
83 tree_->SetDelegate(this);
86 BrowserAccessibilityManager::BrowserAccessibilityManager(
87 const ui::AXTreeUpdate& initial_tree,
88 BrowserAccessibilityDelegate* delegate,
89 BrowserAccessibilityFactory* factory)
90 : delegate_(delegate),
92 tree_(new ui::AXSerializableTree()),
94 osk_state_(OSK_ALLOWED) {
95 tree_->SetDelegate(this);
96 Initialize(initial_tree);
99 BrowserAccessibilityManager::~BrowserAccessibilityManager() {
103 void BrowserAccessibilityManager::Initialize(
104 const ui::AXTreeUpdate& initial_tree) {
105 if (!tree_->Unserialize(initial_tree)) {
107 LOG(ERROR) << tree_->error();
108 delegate_->AccessibilityFatalError();
110 LOG(FATAL) << tree_->error();
115 SetFocus(tree_->GetRoot(), false);
119 ui::AXTreeUpdate BrowserAccessibilityManager::GetEmptyDocument() {
120 ui::AXNodeData empty_document;
121 empty_document.id = 0;
122 empty_document.role = ui::AX_ROLE_ROOT_WEB_AREA;
123 ui::AXTreeUpdate update;
124 update.nodes.push_back(empty_document);
128 BrowserAccessibility* BrowserAccessibilityManager::GetRoot() {
129 return GetFromAXNode(tree_->GetRoot());
132 BrowserAccessibility* BrowserAccessibilityManager::GetFromAXNode(
134 return GetFromID(node->id());
137 BrowserAccessibility* BrowserAccessibilityManager::GetFromID(int32 id) {
138 base::hash_map<int32, BrowserAccessibility*>::iterator iter =
139 id_wrapper_map_.find(id);
140 if (iter != id_wrapper_map_.end())
145 void BrowserAccessibilityManager::OnWindowFocused() {
147 NotifyAccessibilityEvent(ui::AX_EVENT_FOCUS, GetFromAXNode(focus_));
150 void BrowserAccessibilityManager::OnWindowBlurred() {
152 NotifyAccessibilityEvent(ui::AX_EVENT_BLUR, GetFromAXNode(focus_));
155 void BrowserAccessibilityManager::GotMouseDown() {
156 osk_state_ = OSK_ALLOWED_WITHIN_FOCUSED_OBJECT;
157 NotifyAccessibilityEvent(ui::AX_EVENT_FOCUS, GetFromAXNode(focus_));
160 bool BrowserAccessibilityManager::UseRootScrollOffsetsWhenComputingBounds() {
164 void BrowserAccessibilityManager::OnAccessibilityEvents(
165 const std::vector<AccessibilityHostMsg_EventParams>& params) {
166 bool should_send_initial_focus = false;
168 // Process all changes to the accessibility tree first.
169 for (uint32 index = 0; index < params.size(); index++) {
170 const AccessibilityHostMsg_EventParams& param = params[index];
171 if (!tree_->Unserialize(param.update)) {
173 LOG(ERROR) << tree_->error();
174 delegate_->AccessibilityFatalError();
176 CHECK(false) << tree_->error();
181 // Set focus to the root if it's not anywhere else.
183 SetFocus(tree_->GetRoot(), false);
184 should_send_initial_focus = true;
188 OnTreeUpdateFinished();
190 if (should_send_initial_focus &&
191 (!delegate_ || delegate_->AccessibilityViewHasFocus())) {
192 NotifyAccessibilityEvent(ui::AX_EVENT_FOCUS, GetFromAXNode(focus_));
195 // Now iterate over the events again and fire the events.
196 for (uint32 index = 0; index < params.size(); index++) {
197 const AccessibilityHostMsg_EventParams& param = params[index];
199 // Find the node corresponding to the id that's the target of the
200 // event (which may not be the root of the update tree).
201 ui::AXNode* node = tree_->GetFromId(param.id);
205 ui::AXEvent event_type = param.event_type;
206 if (event_type == ui::AX_EVENT_FOCUS ||
207 event_type == ui::AX_EVENT_BLUR) {
208 SetFocus(node, false);
210 if (osk_state_ != OSK_DISALLOWED_BECAUSE_TAB_HIDDEN &&
211 osk_state_ != OSK_DISALLOWED_BECAUSE_TAB_JUST_APPEARED)
212 osk_state_ = OSK_ALLOWED;
214 // Don't send a native focus event if the window itself doesn't
216 if (delegate_ && !delegate_->AccessibilityViewHasFocus())
220 // Send the event event to the operating system.
221 NotifyAccessibilityEvent(event_type, GetFromAXNode(node));
225 void BrowserAccessibilityManager::OnLocationChanges(
226 const std::vector<AccessibilityHostMsg_LocationChangeParams>& params) {
227 for (size_t i = 0; i < params.size(); ++i) {
228 BrowserAccessibility* obj = GetFromID(params[i].id);
231 ui::AXNode* node = obj->node();
232 node->SetLocation(params[i].new_location);
233 obj->OnLocationChanged();
237 void BrowserAccessibilityManager::OnFindInPageResult(
238 int request_id, int match_index, int start_id, int start_offset,
239 int end_id, int end_offset) {
240 find_in_page_info_.request_id = request_id;
241 find_in_page_info_.match_index = match_index;
242 find_in_page_info_.start_id = start_id;
243 find_in_page_info_.start_offset = start_offset;
244 find_in_page_info_.end_id = end_id;
245 find_in_page_info_.end_offset = end_offset;
247 if (find_in_page_info_.active_request_id == request_id)
248 ActivateFindInPageResult(request_id);
251 void BrowserAccessibilityManager::ActivateFindInPageResult(
253 find_in_page_info_.active_request_id = request_id;
254 if (find_in_page_info_.request_id != request_id)
257 BrowserAccessibility* node = GetFromID(find_in_page_info_.start_id);
261 // If an ancestor of this node is a leaf node, fire the notification on that.
262 BrowserAccessibility* ancestor = node->GetParent();
263 while (ancestor && ancestor != GetRoot()) {
264 if (ancestor->PlatformIsLeaf())
266 ancestor = ancestor->GetParent();
269 // The "scrolled to anchor" notification is a great way to get a
270 // screen reader to jump directly to a specific location in a document.
271 NotifyAccessibilityEvent(ui::AX_EVENT_SCROLLED_TO_ANCHOR, node);
274 BrowserAccessibility* BrowserAccessibilityManager::GetActiveDescendantFocus(
275 BrowserAccessibility* root) {
276 BrowserAccessibility* node = BrowserAccessibilityManager::GetFocus(root);
280 int active_descendant_id;
281 if (node->GetIntAttribute(ui::AX_ATTR_ACTIVEDESCENDANT_ID,
282 &active_descendant_id)) {
283 BrowserAccessibility* active_descendant =
284 node->manager()->GetFromID(active_descendant_id);
285 if (active_descendant)
286 return active_descendant;
291 BrowserAccessibility* BrowserAccessibilityManager::GetFocus(
292 BrowserAccessibility* root) {
293 if (focus_ && (!root || focus_->IsDescendantOf(root->node())))
294 return GetFromAXNode(focus_);
299 void BrowserAccessibilityManager::SetFocus(ui::AXNode* node, bool notify) {
303 if (notify && node && delegate_)
304 delegate_->AccessibilitySetFocus(node->id());
307 void BrowserAccessibilityManager::SetFocus(
308 BrowserAccessibility* obj, bool notify) {
310 SetFocus(obj->node(), notify);
313 void BrowserAccessibilityManager::DoDefaultAction(
314 const BrowserAccessibility& node) {
316 delegate_->AccessibilityDoDefaultAction(node.GetId());
319 void BrowserAccessibilityManager::ScrollToMakeVisible(
320 const BrowserAccessibility& node, gfx::Rect subfocus) {
322 delegate_->AccessibilityScrollToMakeVisible(node.GetId(), subfocus);
326 void BrowserAccessibilityManager::ScrollToPoint(
327 const BrowserAccessibility& node, gfx::Point point) {
329 delegate_->AccessibilityScrollToPoint(node.GetId(), point);
333 void BrowserAccessibilityManager::SetValue(
334 const BrowserAccessibility& node,
335 const base::string16& value) {
337 delegate_->AccessibilitySetValue(node.GetId(), value);
340 void BrowserAccessibilityManager::SetTextSelection(
341 const BrowserAccessibility& node,
345 delegate_->AccessibilitySetTextSelection(
346 node.GetId(), start_offset, end_offset);
350 gfx::Rect BrowserAccessibilityManager::GetViewBounds() {
352 return delegate_->AccessibilityGetViewBounds();
356 BrowserAccessibility* BrowserAccessibilityManager::NextInTreeOrder(
357 BrowserAccessibility* node) {
361 if (node->PlatformChildCount() > 0)
362 return node->PlatformGetChild(0);
364 if (node->GetParent() &&
365 node->GetIndexInParent() <
366 static_cast<int>(node->GetParent()->PlatformChildCount()) - 1) {
367 return node->GetParent()->PlatformGetChild(node->GetIndexInParent() + 1);
369 node = node->GetParent();
375 BrowserAccessibility* BrowserAccessibilityManager::PreviousInTreeOrder(
376 BrowserAccessibility* node) {
380 if (node->GetParent() && node->GetIndexInParent() > 0) {
381 node = node->GetParent()->PlatformGetChild(node->GetIndexInParent() - 1);
382 while (node->PlatformChildCount() > 0)
383 node = node->PlatformGetChild(node->PlatformChildCount() - 1);
387 return node->GetParent();
390 void BrowserAccessibilityManager::OnNodeWillBeDeleted(ui::AXNode* node) {
391 if (node == focus_ && tree_) {
392 if (node != tree_->GetRoot())
393 SetFocus(tree_->GetRoot(), false);
397 if (id_wrapper_map_.find(node->id()) == id_wrapper_map_.end())
399 GetFromAXNode(node)->Destroy();
400 id_wrapper_map_.erase(node->id());
403 void BrowserAccessibilityManager::OnNodeCreated(ui::AXNode* node) {
404 BrowserAccessibility* wrapper = factory_->Create();
405 wrapper->Init(this, node);
406 id_wrapper_map_[node->id()] = wrapper;
407 wrapper->OnDataChanged();
410 void BrowserAccessibilityManager::OnNodeChanged(ui::AXNode* node) {
411 GetFromAXNode(node)->OnDataChanged();
414 void BrowserAccessibilityManager::OnNodeCreationFinished(ui::AXNode* node) {
415 GetFromAXNode(node)->OnUpdateFinished();
418 void BrowserAccessibilityManager::OnNodeChangeFinished(ui::AXNode* node) {
419 GetFromAXNode(node)->OnUpdateFinished();
422 ui::AXTreeUpdate BrowserAccessibilityManager::SnapshotAXTreeForTesting() {
423 scoped_ptr<ui::AXTreeSource<const ui::AXNode*> > tree_source(
424 tree_->CreateTreeSource());
425 ui::AXTreeSerializer<const ui::AXNode*> serializer(tree_source.get());
426 ui::AXTreeUpdate update;
427 serializer.SerializeChanges(tree_->GetRoot(), &update);
431 } // namespace content