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.h"
6
7#include "base/logging.h"
8#include "base/strings/string_number_conversions.h"
9#include "base/strings/string_util.h"
10#include "content/browser/accessibility/browser_accessibility_manager.h"
11#include "content/common/accessibility_messages.h"
12
13namespace content {
14
15typedef AccessibilityNodeData::BoolAttribute BoolAttribute;
16typedef AccessibilityNodeData::FloatAttribute FloatAttribute;
17typedef AccessibilityNodeData::IntAttribute IntAttribute;
18typedef AccessibilityNodeData::StringAttribute StringAttribute;
19
20#if !defined(OS_MACOSX) && \
21    !defined(OS_WIN) && \
22    !defined(TOOLKIT_GTK) && \
23    !defined(OS_ANDROID)
24// We have subclassess of BrowserAccessibility on Mac, Linux/GTK,
25// and Win. For any other platform, instantiate the base class.
26// static
27BrowserAccessibility* BrowserAccessibility::Create() {
28  return new BrowserAccessibility();
29}
30#endif
31
32BrowserAccessibility::BrowserAccessibility()
33    : manager_(NULL),
34      parent_(NULL),
35      index_in_parent_(0),
36      renderer_id_(0),
37      role_(0),
38      state_(0),
39      instance_active_(false) {
40}
41
42BrowserAccessibility::~BrowserAccessibility() {
43}
44
45void BrowserAccessibility::DetachTree(
46    std::vector<BrowserAccessibility*>* nodes) {
47  nodes->push_back(this);
48  for (size_t i = 0; i < children_.size(); i++)
49    children_[i]->DetachTree(nodes);
50  children_.clear();
51  parent_ = NULL;
52}
53
54void BrowserAccessibility::InitializeTreeStructure(
55    BrowserAccessibilityManager* manager,
56    BrowserAccessibility* parent,
57    int32 renderer_id,
58    int32 index_in_parent) {
59  manager_ = manager;
60  parent_ = parent;
61  renderer_id_ = renderer_id;
62  index_in_parent_ = index_in_parent;
63}
64
65void BrowserAccessibility::InitializeData(const AccessibilityNodeData& src) {
66  DCHECK_EQ(renderer_id_, src.id);
67  name_ = src.name;
68  value_ = src.value;
69  role_ = src.role;
70  state_ = src.state;
71  string_attributes_ = src.string_attributes;
72  int_attributes_ = src.int_attributes;
73  float_attributes_ = src.float_attributes;
74  bool_attributes_ = src.bool_attributes;
75  html_attributes_ = src.html_attributes;
76  location_ = src.location;
77  indirect_child_ids_ = src.indirect_child_ids;
78  line_breaks_ = src.line_breaks;
79  cell_ids_ = src.cell_ids;
80  unique_cell_ids_ = src.unique_cell_ids;
81  instance_active_ = true;
82
83  PreInitialize();
84}
85
86bool BrowserAccessibility::IsNative() const {
87  return false;
88}
89
90void BrowserAccessibility::SwapChildren(
91    std::vector<BrowserAccessibility*>& children) {
92  children.swap(children_);
93}
94
95void BrowserAccessibility::UpdateParent(BrowserAccessibility* parent,
96                                        int index_in_parent) {
97  parent_ = parent;
98  index_in_parent_ = index_in_parent;
99}
100
101void BrowserAccessibility::SetLocation(const gfx::Rect& new_location) {
102  location_ = new_location;
103}
104
105bool BrowserAccessibility::IsDescendantOf(
106    BrowserAccessibility* ancestor) {
107  if (this == ancestor) {
108    return true;
109  } else if (parent_) {
110    return parent_->IsDescendantOf(ancestor);
111  }
112
113  return false;
114}
115
116BrowserAccessibility* BrowserAccessibility::GetChild(uint32 child_index) const {
117  DCHECK(child_index < children_.size());
118  return children_[child_index];
119}
120
121BrowserAccessibility* BrowserAccessibility::GetPreviousSibling() {
122  if (parent_ && index_in_parent_ > 0)
123    return parent_->children_[index_in_parent_ - 1];
124
125  return NULL;
126}
127
128BrowserAccessibility* BrowserAccessibility::GetNextSibling() {
129  if (parent_ &&
130      index_in_parent_ >= 0 &&
131      index_in_parent_ < static_cast<int>(parent_->children_.size() - 1)) {
132    return parent_->children_[index_in_parent_ + 1];
133  }
134
135  return NULL;
136}
137
138gfx::Rect BrowserAccessibility::GetLocalBoundsRect() const {
139  gfx::Rect bounds = location_;
140
141  // Walk up the parent chain. Every time we encounter a Web Area, offset
142  // based on the scroll bars and then offset based on the origin of that
143  // nested web area.
144  BrowserAccessibility* parent = parent_;
145  bool need_to_offset_web_area =
146      (role_ == AccessibilityNodeData::ROLE_WEB_AREA ||
147       role_ == AccessibilityNodeData::ROLE_ROOT_WEB_AREA);
148  while (parent) {
149    if (need_to_offset_web_area &&
150        parent->location().width() > 0 &&
151        parent->location().height() > 0) {
152      bounds.Offset(parent->location().x(), parent->location().y());
153      need_to_offset_web_area = false;
154    }
155
156    // On some platforms, we don't want to take the root scroll offsets
157    // into account.
158    if (parent->role() == AccessibilityNodeData::ROLE_ROOT_WEB_AREA &&
159        !manager()->UseRootScrollOffsetsWhenComputingBounds()) {
160      break;
161    }
162
163    if (parent->role() == AccessibilityNodeData::ROLE_WEB_AREA ||
164        parent->role() == AccessibilityNodeData::ROLE_ROOT_WEB_AREA) {
165      int sx = 0;
166      int sy = 0;
167      if (parent->GetIntAttribute(AccessibilityNodeData::ATTR_SCROLL_X, &sx) &&
168          parent->GetIntAttribute(AccessibilityNodeData::ATTR_SCROLL_Y, &sy)) {
169        bounds.Offset(-sx, -sy);
170      }
171      need_to_offset_web_area = true;
172    }
173    parent = parent->parent();
174  }
175
176  return bounds;
177}
178
179gfx::Rect BrowserAccessibility::GetGlobalBoundsRect() const {
180  gfx::Rect bounds = GetLocalBoundsRect();
181
182  // Adjust the bounds by the top left corner of the containing view's bounds
183  // in screen coordinates.
184  bounds.Offset(manager_->GetViewBounds().OffsetFromOrigin());
185
186  return bounds;
187}
188
189BrowserAccessibility* BrowserAccessibility::BrowserAccessibilityForPoint(
190    const gfx::Point& point) {
191  // Walk the children recursively looking for the BrowserAccessibility that
192  // most tightly encloses the specified point.
193  for (int i = children_.size() - 1; i >= 0; --i) {
194    BrowserAccessibility* child = children_[i];
195    if (child->GetGlobalBoundsRect().Contains(point))
196      return child->BrowserAccessibilityForPoint(point);
197  }
198  return this;
199}
200
201void BrowserAccessibility::Destroy() {
202  for (std::vector<BrowserAccessibility*>::iterator iter = children_.begin();
203       iter != children_.end();
204       ++iter) {
205    (*iter)->Destroy();
206  }
207  children_.clear();
208
209  // Allow the object to fire a TextRemoved notification.
210  name_.clear();
211  value_.clear();
212  PostInitialize();
213
214  manager_->NotifyAccessibilityEvent(
215      AccessibilityNotificationObjectHide, this);
216
217  instance_active_ = false;
218  manager_->RemoveNode(this);
219  NativeReleaseReference();
220}
221
222void BrowserAccessibility::NativeReleaseReference() {
223  delete this;
224}
225
226bool BrowserAccessibility::GetBoolAttribute(
227    BoolAttribute attribute, bool* value) const {
228  BoolAttrMap::const_iterator iter = bool_attributes_.find(attribute);
229  if (iter != bool_attributes_.end()) {
230    *value = iter->second;
231    return true;
232  }
233
234  return false;
235}
236
237bool BrowserAccessibility::GetFloatAttribute(
238    FloatAttribute attribute, float* value) const {
239  FloatAttrMap::const_iterator iter = float_attributes_.find(attribute);
240  if (iter != float_attributes_.end()) {
241    *value = iter->second;
242    return true;
243  }
244
245  return false;
246}
247
248bool BrowserAccessibility::GetIntAttribute(
249    IntAttribute attribute, int* value) const {
250  IntAttrMap::const_iterator iter = int_attributes_.find(attribute);
251  if (iter != int_attributes_.end()) {
252    *value = iter->second;
253    return true;
254  }
255
256  return false;
257}
258
259bool BrowserAccessibility::GetStringAttribute(
260    StringAttribute attribute,
261    string16* value) const {
262  StringAttrMap::const_iterator iter = string_attributes_.find(attribute);
263  if (iter != string_attributes_.end()) {
264    *value = iter->second;
265    return true;
266  }
267
268  return false;
269}
270
271bool BrowserAccessibility::GetHtmlAttribute(
272    const char* html_attr, string16* value) const {
273  for (size_t i = 0; i < html_attributes_.size(); i++) {
274    const string16& attr = html_attributes_[i].first;
275    if (LowerCaseEqualsASCII(attr, html_attr)) {
276      *value = html_attributes_[i].second;
277      return true;
278    }
279  }
280
281  return false;
282}
283
284bool BrowserAccessibility::GetAriaTristate(
285    const char* html_attr,
286    bool* is_defined,
287    bool* is_mixed) const {
288  *is_defined = false;
289  *is_mixed = false;
290
291  string16 value;
292  if (!GetHtmlAttribute(html_attr, &value) ||
293      value.empty() ||
294      EqualsASCII(value, "undefined")) {
295    return false;  // Not set (and *is_defined is also false)
296  }
297
298  *is_defined = true;
299
300  if (EqualsASCII(value, "true"))
301    return true;
302
303  if (EqualsASCII(value, "mixed"))
304    *is_mixed = true;
305
306  return false;  // Not set
307}
308
309bool BrowserAccessibility::HasState(
310    AccessibilityNodeData::State state_enum) const {
311  return (state_ >> state_enum) & 1;
312}
313
314bool BrowserAccessibility::IsEditableText() const {
315  // These roles don't have readonly set, but they're not editable text.
316  if (role_ == AccessibilityNodeData::ROLE_SCROLLAREA ||
317      role_ == AccessibilityNodeData::ROLE_COLUMN ||
318      role_ == AccessibilityNodeData::ROLE_TABLE_HEADER_CONTAINER) {
319    return false;
320  }
321
322  // Note: STATE_READONLY being false means it's either a text control,
323  // or contenteditable. We also check for editable text roles to cover
324  // another element that has role=textbox set on it.
325  return (!HasState(AccessibilityNodeData::STATE_READONLY) ||
326          role_ == AccessibilityNodeData::ROLE_TEXT_FIELD ||
327          role_ == AccessibilityNodeData::ROLE_TEXTAREA);
328}
329
330string16 BrowserAccessibility::GetTextRecursive() const {
331  if (!name_.empty()) {
332    return name_;
333  }
334
335  string16 result;
336  for (size_t i = 0; i < children_.size(); ++i)
337    result += children_[i]->GetTextRecursive();
338  return result;
339}
340
341}  // namespace content
342