1 // Copyright 2014 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"
13 ui::AXTreeUpdate MakeAXTreeUpdate(
14 const ui::AXNodeData& node1,
15 const ui::AXNodeData& node2 /* = ui::AXNodeData() */,
16 const ui::AXNodeData& node3 /* = ui::AXNodeData() */,
17 const ui::AXNodeData& node4 /* = ui::AXNodeData() */,
18 const ui::AXNodeData& node5 /* = ui::AXNodeData() */,
19 const ui::AXNodeData& node6 /* = ui::AXNodeData() */,
20 const ui::AXNodeData& node7 /* = ui::AXNodeData() */,
21 const ui::AXNodeData& node8 /* = ui::AXNodeData() */,
22 const ui::AXNodeData& node9 /* = ui::AXNodeData() */) {
23 CR_DEFINE_STATIC_LOCAL(ui::AXNodeData, empty_data, ());
24 int32 no_id = empty_data.id;
26 ui::AXTreeUpdate update;
27 update.nodes.push_back(node1);
28 if (node2.id != no_id)
29 update.nodes.push_back(node2);
30 if (node3.id != no_id)
31 update.nodes.push_back(node3);
32 if (node4.id != no_id)
33 update.nodes.push_back(node4);
34 if (node5.id != no_id)
35 update.nodes.push_back(node5);
36 if (node6.id != no_id)
37 update.nodes.push_back(node6);
38 if (node7.id != no_id)
39 update.nodes.push_back(node7);
40 if (node8.id != no_id)
41 update.nodes.push_back(node8);
42 if (node9.id != no_id)
43 update.nodes.push_back(node9);
47 BrowserAccessibility* BrowserAccessibilityFactory::Create() {
48 return BrowserAccessibility::Create();
51 #if !defined(OS_MACOSX) && \
53 !defined(OS_ANDROID) \
54 // We have subclassess of BrowserAccessibilityManager on Mac, Win, and Android.
55 // These are the default implementations of these functions
58 BrowserAccessibilityManager* BrowserAccessibilityManager::Create(
59 const ui::AXTreeUpdate& initial_tree,
60 BrowserAccessibilityDelegate* delegate,
61 BrowserAccessibilityFactory* factory) {
62 return new BrowserAccessibilityManager(initial_tree, delegate, factory);
66 ui::AXTreeUpdate BrowserAccessibilityManager::GetEmptyDocument() {
67 ui::AXNodeData empty_document;
68 empty_document.id = 0;
69 empty_document.role = ui::AX_ROLE_ROOT_WEB_AREA;
70 ui::AXTreeUpdate update;
71 update.nodes.push_back(empty_document);
76 BrowserAccessibilityManager::BrowserAccessibilityManager(
77 BrowserAccessibilityDelegate* delegate,
78 BrowserAccessibilityFactory* factory)
79 : delegate_(delegate),
81 tree_(new ui::AXTree()),
83 osk_state_(OSK_ALLOWED) {
84 tree_->SetDelegate(this);
87 BrowserAccessibilityManager::BrowserAccessibilityManager(
88 const ui::AXTreeUpdate& initial_tree,
89 BrowserAccessibilityDelegate* delegate,
90 BrowserAccessibilityFactory* factory)
91 : delegate_(delegate),
93 tree_(new ui::AXTree()),
95 osk_state_(OSK_ALLOWED) {
96 tree_->SetDelegate(this);
97 Initialize(initial_tree);
100 BrowserAccessibilityManager::~BrowserAccessibilityManager() {
104 void BrowserAccessibilityManager::Initialize(
105 const ui::AXTreeUpdate& initial_tree) {
106 if (!tree_->Unserialize(initial_tree)) {
108 LOG(ERROR) << tree_->error();
109 delegate_->AccessibilityFatalError();
111 LOG(FATAL) << tree_->error();
116 SetFocus(tree_->GetRoot(), false);
119 BrowserAccessibility* BrowserAccessibilityManager::GetRoot() {
120 return GetFromAXNode(tree_->GetRoot());
123 BrowserAccessibility* BrowserAccessibilityManager::GetFromAXNode(
125 return GetFromID(node->id());
128 BrowserAccessibility* BrowserAccessibilityManager::GetFromID(int32 id) {
129 base::hash_map<int32, BrowserAccessibility*>::iterator iter =
130 id_wrapper_map_.find(id);
131 if (iter != id_wrapper_map_.end())
136 void BrowserAccessibilityManager::OnWindowFocused() {
138 NotifyAccessibilityEvent(ui::AX_EVENT_FOCUS, GetFromAXNode(focus_));
141 void BrowserAccessibilityManager::OnWindowBlurred() {
143 NotifyAccessibilityEvent(ui::AX_EVENT_BLUR, GetFromAXNode(focus_));
146 void BrowserAccessibilityManager::OnNavigation(bool is_reload) {
147 // Exit if we don't even have the first document loaded yet.
148 if (GetRoot()->GetId() == 0)
151 LOG(ERROR) << "AX: BrowserAccessibilityManager::OnNavigation " << is_reload;
152 // Create an update that replaces the current tree with an empty document
153 // (which should be in the "busy" state by default) and apply it.
154 ui::AXTreeUpdate update = GetEmptyDocument();
156 LOG(ERROR) << "AX: OnNavigation empty doc update state: "
157 << update.nodes[0].state;
159 update.nodes[0].id = GetRoot()->GetId();
161 LOG(ERROR) << "AX: State before unserializing the empty doc: "
162 << GetRoot()->GetState();
163 LOG(ERROR) << "AX: Root id before unserializing the empty doc: "
164 << GetRoot()->GetId();
165 LOG(ERROR) << "AX: Root children before: "
166 << GetRoot()->PlatformChildCount();
168 LOG(ERROR) << "AX: State of first node in update: "
169 << update.nodes[0].state;
171 CHECK(tree_->Unserialize(update));
172 LOG(ERROR) << "AX: State after unserializing the empty doc: "
173 << GetRoot()->GetState();
174 LOG(ERROR) << "AX: Root id after unserializing the empty doc: "
175 << GetRoot()->GetId();
176 LOG(ERROR) << "AX: Root children after: "
177 << GetRoot()->PlatformChildCount();
180 void BrowserAccessibilityManager::GotMouseDown() {
181 osk_state_ = OSK_ALLOWED_WITHIN_FOCUSED_OBJECT;
182 NotifyAccessibilityEvent(ui::AX_EVENT_FOCUS, GetFromAXNode(focus_));
185 bool BrowserAccessibilityManager::UseRootScrollOffsetsWhenComputingBounds() {
189 void BrowserAccessibilityManager::OnAccessibilityEvents(
190 const std::vector<AccessibilityHostMsg_EventParams>& params) {
191 bool should_send_initial_focus = false;
193 // Process all changes to the accessibility tree first.
194 for (uint32 index = 0; index < params.size(); index++) {
195 const AccessibilityHostMsg_EventParams& param = params[index];
196 if (!tree_->Unserialize(param.update)) {
198 LOG(ERROR) << tree_->error();
199 delegate_->AccessibilityFatalError();
201 CHECK(false) << tree_->error();
206 // Set focus to the root if it's not anywhere else.
208 SetFocus(tree_->GetRoot(), false);
209 should_send_initial_focus = true;
213 OnTreeUpdateFinished();
215 if (should_send_initial_focus &&
216 (!delegate_ || delegate_->AccessibilityViewHasFocus())) {
217 NotifyAccessibilityEvent(ui::AX_EVENT_FOCUS, GetFromAXNode(focus_));
220 // Now iterate over the events again and fire the events.
221 for (uint32 index = 0; index < params.size(); index++) {
222 const AccessibilityHostMsg_EventParams& param = params[index];
224 // Find the node corresponding to the id that's the target of the
225 // event (which may not be the root of the update tree).
226 ui::AXNode* node = tree_->GetFromId(param.id);
230 ui::AXEvent event_type = param.event_type;
231 if (event_type == ui::AX_EVENT_FOCUS ||
232 event_type == ui::AX_EVENT_BLUR) {
233 SetFocus(node, false);
235 if (osk_state_ != OSK_DISALLOWED_BECAUSE_TAB_HIDDEN &&
236 osk_state_ != OSK_DISALLOWED_BECAUSE_TAB_JUST_APPEARED)
237 osk_state_ = OSK_ALLOWED;
239 // Don't send a native focus event if the window itself doesn't
241 if (delegate_ && !delegate_->AccessibilityViewHasFocus())
245 // Send the event event to the operating system.
246 NotifyAccessibilityEvent(event_type, GetFromAXNode(node));
250 void BrowserAccessibilityManager::OnLocationChanges(
251 const std::vector<AccessibilityHostMsg_LocationChangeParams>& params) {
252 for (size_t i = 0; i < params.size(); ++i) {
253 BrowserAccessibility* obj = GetFromID(params[i].id);
256 ui::AXNode* node = obj->node();
257 node->SetLocation(params[i].new_location);
258 obj->OnLocationChanged();
262 BrowserAccessibility* BrowserAccessibilityManager::GetActiveDescendantFocus(
263 BrowserAccessibility* root) {
264 BrowserAccessibility* node = BrowserAccessibilityManager::GetFocus(root);
268 int active_descendant_id;
269 if (node->GetIntAttribute(ui::AX_ATTR_ACTIVEDESCENDANT_ID,
270 &active_descendant_id)) {
271 BrowserAccessibility* active_descendant =
272 node->manager()->GetFromID(active_descendant_id);
273 if (active_descendant)
274 return active_descendant;
279 BrowserAccessibility* BrowserAccessibilityManager::GetFocus(
280 BrowserAccessibility* root) {
281 if (focus_ && (!root || focus_->IsDescendantOf(root->node())))
282 return GetFromAXNode(focus_);
287 void BrowserAccessibilityManager::SetFocus(ui::AXNode* node, bool notify) {
291 if (notify && node && delegate_)
292 delegate_->AccessibilitySetFocus(node->id());
295 void BrowserAccessibilityManager::SetFocus(
296 BrowserAccessibility* obj, bool notify) {
298 SetFocus(obj->node(), notify);
301 void BrowserAccessibilityManager::DoDefaultAction(
302 const BrowserAccessibility& node) {
304 delegate_->AccessibilityDoDefaultAction(node.GetId());
307 void BrowserAccessibilityManager::ScrollToMakeVisible(
308 const BrowserAccessibility& node, gfx::Rect subfocus) {
310 delegate_->AccessibilityScrollToMakeVisible(node.GetId(), subfocus);
314 void BrowserAccessibilityManager::ScrollToPoint(
315 const BrowserAccessibility& node, gfx::Point point) {
317 delegate_->AccessibilityScrollToPoint(node.GetId(), point);
321 void BrowserAccessibilityManager::SetTextSelection(
322 const BrowserAccessibility& node, int start_offset, int end_offset) {
324 delegate_->AccessibilitySetTextSelection(
325 node.GetId(), start_offset, end_offset);
329 gfx::Rect BrowserAccessibilityManager::GetViewBounds() {
331 return delegate_->AccessibilityGetViewBounds();
335 BrowserAccessibility* BrowserAccessibilityManager::NextInTreeOrder(
336 BrowserAccessibility* node) {
340 if (node->PlatformChildCount() > 0)
341 return node->PlatformGetChild(0);
343 if (node->GetParent() &&
344 node->GetIndexInParent() <
345 static_cast<int>(node->GetParent()->PlatformChildCount()) - 1) {
346 return node->GetParent()->PlatformGetChild(node->GetIndexInParent() + 1);
348 node = node->GetParent();
354 BrowserAccessibility* BrowserAccessibilityManager::PreviousInTreeOrder(
355 BrowserAccessibility* node) {
359 if (node->GetParent() && node->GetIndexInParent() > 0) {
360 node = node->GetParent()->PlatformGetChild(node->GetIndexInParent() - 1);
361 while (node->PlatformChildCount() > 0)
362 node = node->PlatformGetChild(node->PlatformChildCount() - 1);
366 return node->GetParent();
369 void BrowserAccessibilityManager::OnNodeWillBeDeleted(ui::AXNode* node) {
370 if (node == focus_ && tree_) {
371 if (node != tree_->GetRoot())
372 SetFocus(tree_->GetRoot(), false);
376 if (id_wrapper_map_.find(node->id()) == id_wrapper_map_.end())
378 GetFromAXNode(node)->Destroy();
379 id_wrapper_map_.erase(node->id());
382 void BrowserAccessibilityManager::OnNodeCreated(ui::AXNode* node) {
383 BrowserAccessibility* wrapper = factory_->Create();
384 wrapper->Init(this, node);
385 id_wrapper_map_[node->id()] = wrapper;
386 wrapper->OnDataChanged();
389 void BrowserAccessibilityManager::OnNodeChanged(ui::AXNode* node) {
390 GetFromAXNode(node)->OnDataChanged();
393 void BrowserAccessibilityManager::OnNodeCreationFinished(ui::AXNode* node) {
394 GetFromAXNode(node)->OnUpdateFinished();
397 void BrowserAccessibilityManager::OnNodeChangeFinished(ui::AXNode* node) {
398 GetFromAXNode(node)->OnUpdateFinished();
401 } // namespace content