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