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.
4
5#include "content/browser/accessibility/browser_accessibility_manager.h"
6
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"
11
12namespace content {
13
14ui::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;
26
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);
45  return update;
46}
47
48BrowserAccessibility* BrowserAccessibilityFactory::Create() {
49  return BrowserAccessibility::Create();
50}
51
52#if !defined(OS_MACOSX) && \
53    !defined(OS_WIN) && \
54    !defined(OS_ANDROID) \
55// We have subclassess of BrowserAccessibilityManager on Mac, and Win. For any
56// other platform, instantiate the base class.
57// static
58BrowserAccessibilityManager* BrowserAccessibilityManager::Create(
59    const ui::AXTreeUpdate& initial_tree,
60    BrowserAccessibilityDelegate* delegate,
61    BrowserAccessibilityFactory* factory) {
62  return new BrowserAccessibilityManager(initial_tree, delegate, factory);
63}
64#endif
65
66BrowserAccessibilityManager::BrowserAccessibilityManager(
67    BrowserAccessibilityDelegate* delegate,
68    BrowserAccessibilityFactory* factory)
69    : delegate_(delegate),
70      factory_(factory),
71      tree_(new ui::AXSerializableTree()),
72      focus_(NULL),
73      osk_state_(OSK_ALLOWED) {
74  tree_->SetDelegate(this);
75}
76
77BrowserAccessibilityManager::BrowserAccessibilityManager(
78    const ui::AXTreeUpdate& initial_tree,
79    BrowserAccessibilityDelegate* delegate,
80    BrowserAccessibilityFactory* factory)
81    : delegate_(delegate),
82      factory_(factory),
83      tree_(new ui::AXSerializableTree()),
84      focus_(NULL),
85      osk_state_(OSK_ALLOWED) {
86  tree_->SetDelegate(this);
87  Initialize(initial_tree);
88}
89
90BrowserAccessibilityManager::~BrowserAccessibilityManager() {
91  tree_.reset(NULL);
92}
93
94void BrowserAccessibilityManager::Initialize(
95    const ui::AXTreeUpdate& initial_tree) {
96  if (!tree_->Unserialize(initial_tree)) {
97    if (delegate_) {
98      LOG(ERROR) << tree_->error();
99      delegate_->AccessibilityFatalError();
100    } else {
101      LOG(FATAL) << tree_->error();
102    }
103  }
104
105  if (!focus_)
106    SetFocus(tree_->GetRoot(), false);
107}
108
109// static
110ui::AXTreeUpdate BrowserAccessibilityManager::GetEmptyDocument() {
111  ui::AXNodeData empty_document;
112  empty_document.id = 0;
113  empty_document.role = ui::AX_ROLE_ROOT_WEB_AREA;
114  ui::AXTreeUpdate update;
115  update.nodes.push_back(empty_document);
116  return update;
117}
118
119BrowserAccessibility* BrowserAccessibilityManager::GetRoot() {
120  return GetFromAXNode(tree_->GetRoot());
121}
122
123BrowserAccessibility* BrowserAccessibilityManager::GetFromAXNode(
124    ui::AXNode* node) {
125  return GetFromID(node->id());
126}
127
128BrowserAccessibility* 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())
132    return iter->second;
133  return NULL;
134}
135
136void BrowserAccessibilityManager::OnWindowFocused() {
137  if (focus_)
138    NotifyAccessibilityEvent(ui::AX_EVENT_FOCUS, GetFromAXNode(focus_));
139}
140
141void BrowserAccessibilityManager::OnWindowBlurred() {
142  if (focus_)
143    NotifyAccessibilityEvent(ui::AX_EVENT_BLUR, GetFromAXNode(focus_));
144}
145
146void BrowserAccessibilityManager::GotMouseDown() {
147  osk_state_ = OSK_ALLOWED_WITHIN_FOCUSED_OBJECT;
148  NotifyAccessibilityEvent(ui::AX_EVENT_FOCUS, GetFromAXNode(focus_));
149}
150
151bool BrowserAccessibilityManager::UseRootScrollOffsetsWhenComputingBounds() {
152  return true;
153}
154
155void BrowserAccessibilityManager::OnAccessibilityEvents(
156    const std::vector<AccessibilityHostMsg_EventParams>& params) {
157  bool should_send_initial_focus = false;
158
159  // Process all changes to the accessibility tree first.
160  for (uint32 index = 0; index < params.size(); index++) {
161    const AccessibilityHostMsg_EventParams& param = params[index];
162    if (!tree_->Unserialize(param.update)) {
163      if (delegate_) {
164        LOG(ERROR) << tree_->error();
165        delegate_->AccessibilityFatalError();
166      } else {
167        CHECK(false) << tree_->error();
168      }
169      return;
170    }
171
172    // Set focus to the root if it's not anywhere else.
173    if (!focus_) {
174      SetFocus(tree_->GetRoot(), false);
175      should_send_initial_focus = true;
176    }
177  }
178
179  OnTreeUpdateFinished();
180
181  if (should_send_initial_focus &&
182      (!delegate_ || delegate_->AccessibilityViewHasFocus())) {
183    NotifyAccessibilityEvent(ui::AX_EVENT_FOCUS, GetFromAXNode(focus_));
184  }
185
186  // Now iterate over the events again and fire the events.
187  for (uint32 index = 0; index < params.size(); index++) {
188    const AccessibilityHostMsg_EventParams& param = params[index];
189
190    // Find the node corresponding to the id that's the target of the
191    // event (which may not be the root of the update tree).
192    ui::AXNode* node = tree_->GetFromId(param.id);
193    if (!node)
194      continue;
195
196    ui::AXEvent event_type = param.event_type;
197    if (event_type == ui::AX_EVENT_FOCUS ||
198        event_type == ui::AX_EVENT_BLUR) {
199      SetFocus(node, false);
200
201      if (osk_state_ != OSK_DISALLOWED_BECAUSE_TAB_HIDDEN &&
202          osk_state_ != OSK_DISALLOWED_BECAUSE_TAB_JUST_APPEARED)
203        osk_state_ = OSK_ALLOWED;
204
205      // Don't send a native focus event if the window itself doesn't
206      // have focus.
207      if (delegate_ && !delegate_->AccessibilityViewHasFocus())
208        continue;
209    }
210
211    // Send the event event to the operating system.
212    NotifyAccessibilityEvent(event_type, GetFromAXNode(node));
213  }
214}
215
216void BrowserAccessibilityManager::OnLocationChanges(
217    const std::vector<AccessibilityHostMsg_LocationChangeParams>& params) {
218  for (size_t i = 0; i < params.size(); ++i) {
219    BrowserAccessibility* obj = GetFromID(params[i].id);
220    if (!obj)
221      continue;
222    ui::AXNode* node = obj->node();
223    node->SetLocation(params[i].new_location);
224    obj->OnLocationChanged();
225  }
226}
227
228BrowserAccessibility* BrowserAccessibilityManager::GetActiveDescendantFocus(
229    BrowserAccessibility* root) {
230  BrowserAccessibility* node = BrowserAccessibilityManager::GetFocus(root);
231  if (!node)
232    return NULL;
233
234  int active_descendant_id;
235  if (node->GetIntAttribute(ui::AX_ATTR_ACTIVEDESCENDANT_ID,
236                            &active_descendant_id)) {
237    BrowserAccessibility* active_descendant =
238        node->manager()->GetFromID(active_descendant_id);
239    if (active_descendant)
240      return active_descendant;
241  }
242  return node;
243}
244
245BrowserAccessibility* BrowserAccessibilityManager::GetFocus(
246    BrowserAccessibility* root) {
247  if (focus_ && (!root || focus_->IsDescendantOf(root->node())))
248    return GetFromAXNode(focus_);
249
250  return NULL;
251}
252
253void BrowserAccessibilityManager::SetFocus(ui::AXNode* node, bool notify) {
254  if (focus_ != node)
255    focus_ = node;
256
257  if (notify && node && delegate_)
258    delegate_->AccessibilitySetFocus(node->id());
259}
260
261void BrowserAccessibilityManager::SetFocus(
262    BrowserAccessibility* obj, bool notify) {
263  if (obj->node())
264    SetFocus(obj->node(), notify);
265}
266
267void BrowserAccessibilityManager::DoDefaultAction(
268    const BrowserAccessibility& node) {
269  if (delegate_)
270    delegate_->AccessibilityDoDefaultAction(node.GetId());
271}
272
273void BrowserAccessibilityManager::ScrollToMakeVisible(
274    const BrowserAccessibility& node, gfx::Rect subfocus) {
275  if (delegate_) {
276    delegate_->AccessibilityScrollToMakeVisible(node.GetId(), subfocus);
277  }
278}
279
280void BrowserAccessibilityManager::ScrollToPoint(
281    const BrowserAccessibility& node, gfx::Point point) {
282  if (delegate_) {
283    delegate_->AccessibilityScrollToPoint(node.GetId(), point);
284  }
285}
286
287void BrowserAccessibilityManager::SetTextSelection(
288    const BrowserAccessibility& node, int start_offset, int end_offset) {
289  if (delegate_) {
290    delegate_->AccessibilitySetTextSelection(
291        node.GetId(), start_offset, end_offset);
292  }
293}
294
295gfx::Rect BrowserAccessibilityManager::GetViewBounds() {
296  if (delegate_)
297    return delegate_->AccessibilityGetViewBounds();
298  return gfx::Rect();
299}
300
301BrowserAccessibility* BrowserAccessibilityManager::NextInTreeOrder(
302    BrowserAccessibility* node) {
303  if (!node)
304    return NULL;
305
306  if (node->PlatformChildCount() > 0)
307    return node->PlatformGetChild(0);
308  while (node) {
309    if (node->GetParent() &&
310        node->GetIndexInParent() <
311            static_cast<int>(node->GetParent()->PlatformChildCount()) - 1) {
312      return node->GetParent()->PlatformGetChild(node->GetIndexInParent() + 1);
313    }
314    node = node->GetParent();
315  }
316
317  return NULL;
318}
319
320BrowserAccessibility* BrowserAccessibilityManager::PreviousInTreeOrder(
321    BrowserAccessibility* node) {
322  if (!node)
323    return NULL;
324
325  if (node->GetParent() && node->GetIndexInParent() > 0) {
326    node = node->GetParent()->PlatformGetChild(node->GetIndexInParent() - 1);
327    while (node->PlatformChildCount() > 0)
328      node = node->PlatformGetChild(node->PlatformChildCount() - 1);
329    return node;
330  }
331
332  return node->GetParent();
333}
334
335void BrowserAccessibilityManager::OnNodeWillBeDeleted(ui::AXNode* node) {
336  if (node == focus_ && tree_) {
337    if (node != tree_->GetRoot())
338      SetFocus(tree_->GetRoot(), false);
339    else
340      focus_ = NULL;
341  }
342  if (id_wrapper_map_.find(node->id()) == id_wrapper_map_.end())
343    return;
344  GetFromAXNode(node)->Destroy();
345  id_wrapper_map_.erase(node->id());
346}
347
348void BrowserAccessibilityManager::OnNodeCreated(ui::AXNode* node) {
349  BrowserAccessibility* wrapper = factory_->Create();
350  wrapper->Init(this, node);
351  id_wrapper_map_[node->id()] = wrapper;
352  wrapper->OnDataChanged();
353}
354
355void BrowserAccessibilityManager::OnNodeChanged(ui::AXNode* node) {
356  GetFromAXNode(node)->OnDataChanged();
357}
358
359void BrowserAccessibilityManager::OnNodeCreationFinished(ui::AXNode* node) {
360  GetFromAXNode(node)->OnUpdateFinished();
361}
362
363void BrowserAccessibilityManager::OnNodeChangeFinished(ui::AXNode* node) {
364  GetFromAXNode(node)->OnUpdateFinished();
365}
366
367ui::AXTreeUpdate BrowserAccessibilityManager::SnapshotAXTreeForTesting() {
368  scoped_ptr<ui::AXTreeSource<const ui::AXNode*> > tree_source(
369      tree_->CreateTreeSource());
370  ui::AXTreeSerializer<const ui::AXNode*> serializer(tree_source.get());
371  ui::AXTreeUpdate update;
372  serializer.SerializeChanges(tree_->GetRoot(), &update);
373  return update;
374}
375
376}  // namespace content
377