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.h"
7 #include "base/logging.h"
8 #include "base/strings/string_number_conversions.h"
9 #include "base/strings/string_util.h"
10 #include "base/strings/utf_string_conversions.h"
11 #include "content/browser/accessibility/browser_accessibility_manager.h"
12 #include "content/common/accessibility_messages.h"
16 typedef AccessibilityNodeData::BoolAttribute BoolAttribute;
17 typedef AccessibilityNodeData::FloatAttribute FloatAttribute;
18 typedef AccessibilityNodeData::IntAttribute IntAttribute;
19 typedef AccessibilityNodeData::StringAttribute StringAttribute;
21 #if !defined(OS_MACOSX) && \
23 !defined(TOOLKIT_GTK) && \
25 // We have subclassess of BrowserAccessibility on Mac, Linux/GTK,
26 // and Win. For any other platform, instantiate the base class.
28 BrowserAccessibility* BrowserAccessibility::Create() {
29 return new BrowserAccessibility();
33 BrowserAccessibility::BrowserAccessibility()
40 instance_active_(false) {
43 BrowserAccessibility::~BrowserAccessibility() {
46 bool BrowserAccessibility::PlatformIsLeaf() const {
47 return role_ == WebKit::WebAXRoleStaticText || child_count() == 0;
50 uint32 BrowserAccessibility::PlatformChildCount() const {
51 return PlatformIsLeaf() ? 0 : children_.size();
54 void BrowserAccessibility::DetachTree(
55 std::vector<BrowserAccessibility*>* nodes) {
56 nodes->push_back(this);
57 for (size_t i = 0; i < children_.size(); ++i)
58 children_[i]->DetachTree(nodes);
63 void BrowserAccessibility::InitializeTreeStructure(
64 BrowserAccessibilityManager* manager,
65 BrowserAccessibility* parent,
67 int32 index_in_parent) {
70 renderer_id_ = renderer_id;
71 index_in_parent_ = index_in_parent;
74 void BrowserAccessibility::InitializeData(const AccessibilityNodeData& src) {
75 DCHECK_EQ(renderer_id_, src.id);
78 string_attributes_ = src.string_attributes;
79 int_attributes_ = src.int_attributes;
80 float_attributes_ = src.float_attributes;
81 bool_attributes_ = src.bool_attributes;
82 intlist_attributes_ = src.intlist_attributes;
83 html_attributes_ = src.html_attributes;
84 location_ = src.location;
85 instance_active_ = true;
87 GetStringAttribute(AccessibilityNodeData::ATTR_NAME, &name_);
88 GetStringAttribute(AccessibilityNodeData::ATTR_VALUE, &value_);
93 bool BrowserAccessibility::IsNative() const {
97 void BrowserAccessibility::SwapChildren(
98 std::vector<BrowserAccessibility*>& children) {
99 children.swap(children_);
102 void BrowserAccessibility::UpdateParent(BrowserAccessibility* parent,
103 int index_in_parent) {
105 index_in_parent_ = index_in_parent;
108 void BrowserAccessibility::SetLocation(const gfx::Rect& new_location) {
109 location_ = new_location;
112 bool BrowserAccessibility::IsDescendantOf(
113 BrowserAccessibility* ancestor) {
114 if (this == ancestor) {
116 } else if (parent_) {
117 return parent_->IsDescendantOf(ancestor);
123 BrowserAccessibility* BrowserAccessibility::PlatformGetChild(
124 uint32 child_index) const {
125 DCHECK(child_index < children_.size());
126 return children_[child_index];
129 BrowserAccessibility* BrowserAccessibility::GetPreviousSibling() {
130 if (parent_ && index_in_parent_ > 0)
131 return parent_->children_[index_in_parent_ - 1];
136 BrowserAccessibility* BrowserAccessibility::GetNextSibling() {
138 index_in_parent_ >= 0 &&
139 index_in_parent_ < static_cast<int>(parent_->children_.size() - 1)) {
140 return parent_->children_[index_in_parent_ + 1];
146 gfx::Rect BrowserAccessibility::GetLocalBoundsRect() const {
147 gfx::Rect bounds = location_;
149 // Walk up the parent chain. Every time we encounter a Web Area, offset
150 // based on the scroll bars and then offset based on the origin of that
152 BrowserAccessibility* parent = parent_;
153 bool need_to_offset_web_area =
154 (role_ == WebKit::WebAXRoleWebArea ||
155 role_ == WebKit::WebAXRoleRootWebArea);
157 if (need_to_offset_web_area &&
158 parent->location().width() > 0 &&
159 parent->location().height() > 0) {
160 bounds.Offset(parent->location().x(), parent->location().y());
161 need_to_offset_web_area = false;
164 // On some platforms, we don't want to take the root scroll offsets
166 if (parent->role() == WebKit::WebAXRoleRootWebArea &&
167 !manager()->UseRootScrollOffsetsWhenComputingBounds()) {
171 if (parent->role() == WebKit::WebAXRoleWebArea ||
172 parent->role() == WebKit::WebAXRoleRootWebArea) {
175 if (parent->GetIntAttribute(AccessibilityNodeData::ATTR_SCROLL_X, &sx) &&
176 parent->GetIntAttribute(AccessibilityNodeData::ATTR_SCROLL_Y, &sy)) {
177 bounds.Offset(-sx, -sy);
179 need_to_offset_web_area = true;
181 parent = parent->parent();
187 gfx::Rect BrowserAccessibility::GetGlobalBoundsRect() const {
188 gfx::Rect bounds = GetLocalBoundsRect();
190 // Adjust the bounds by the top left corner of the containing view's bounds
191 // in screen coordinates.
192 bounds.Offset(manager_->GetViewBounds().OffsetFromOrigin());
197 gfx::Rect BrowserAccessibility::GetLocalBoundsForRange(int start, int len)
199 DCHECK_EQ(role_, WebKit::WebAXRoleStaticText);
200 int end = start + len;
205 for (size_t i = 0; i < children_.size() && child_end < start + len; ++i) {
206 BrowserAccessibility* child = children_[i];
207 DCHECK_EQ(child->role(), WebKit::WebAXRoleInlineTextBox);
208 std::string child_text;
209 child->GetStringAttribute(AccessibilityNodeData::ATTR_VALUE, &child_text);
210 int child_len = static_cast<int>(child_text.size());
211 child_start = child_end;
212 child_end += child_len;
214 if (child_end < start)
217 int overlap_start = std::max(start, child_start);
218 int overlap_end = std::min(end, child_end);
220 int local_start = overlap_start - child_start;
221 int local_end = overlap_end - child_start;
223 gfx::Rect child_rect = child->location();
224 int text_direction = child->GetIntAttribute(
225 AccessibilityNodeData::ATTR_TEXT_DIRECTION);
226 const std::vector<int32>& character_offsets = child->GetIntListAttribute(
227 AccessibilityNodeData::ATTR_CHARACTER_OFFSETS);
228 int start_pixel_offset =
229 local_start > 0 ? character_offsets[local_start - 1] : 0;
230 int end_pixel_offset =
231 local_end > 0 ? character_offsets[local_end - 1] : 0;
233 gfx::Rect child_overlap_rect;
234 switch (text_direction) {
235 case WebKit::WebAXTextDirectionLR: {
236 int left = child_rect.x() + start_pixel_offset;
237 int right = child_rect.x() + end_pixel_offset;
238 child_overlap_rect = gfx::Rect(left, child_rect.y(),
239 right - left, child_rect.height());
242 case WebKit::WebAXTextDirectionRL: {
243 int right = child_rect.right() - start_pixel_offset;
244 int left = child_rect.right() - end_pixel_offset;
245 child_overlap_rect = gfx::Rect(left, child_rect.y(),
246 right - left, child_rect.height());
249 case WebKit::WebAXTextDirectionTB: {
250 int top = child_rect.y() + start_pixel_offset;
251 int bottom = child_rect.y() + end_pixel_offset;
252 child_overlap_rect = gfx::Rect(child_rect.x(), top,
253 child_rect.width(), bottom - top);
256 case WebKit::WebAXTextDirectionBT: {
257 int bottom = child_rect.bottom() - start_pixel_offset;
258 int top = child_rect.bottom() - end_pixel_offset;
259 child_overlap_rect = gfx::Rect(child_rect.x(), top,
260 child_rect.width(), bottom - top);
267 if (bounds.width() == 0 && bounds.height() == 0)
268 bounds = child_overlap_rect;
270 bounds.Union(child_overlap_rect);
276 gfx::Rect BrowserAccessibility::GetGlobalBoundsForRange(int start, int len)
278 gfx::Rect bounds = GetLocalBoundsForRange(start, len);
280 // Adjust the bounds by the top left corner of the containing view's bounds
281 // in screen coordinates.
282 bounds.Offset(manager_->GetViewBounds().OffsetFromOrigin());
287 BrowserAccessibility* BrowserAccessibility::BrowserAccessibilityForPoint(
288 const gfx::Point& point) {
289 // Walk the children recursively looking for the BrowserAccessibility that
290 // most tightly encloses the specified point.
291 for (int i = children_.size() - 1; i >= 0; --i) {
292 BrowserAccessibility* child = children_[i];
293 if (child->GetGlobalBoundsRect().Contains(point))
294 return child->BrowserAccessibilityForPoint(point);
299 void BrowserAccessibility::Destroy() {
300 for (std::vector<BrowserAccessibility*>::iterator iter = children_.begin();
301 iter != children_.end();
307 // Allow the object to fire a TextRemoved notification.
312 manager_->NotifyAccessibilityEvent(
313 WebKit::WebAXEventHide, this);
315 instance_active_ = false;
316 manager_->RemoveNode(this);
317 NativeReleaseReference();
320 void BrowserAccessibility::NativeReleaseReference() {
324 bool BrowserAccessibility::HasBoolAttribute(BoolAttribute attribute) const {
325 for (size_t i = 0; i < bool_attributes_.size(); ++i) {
326 if (bool_attributes_[i].first == attribute)
334 bool BrowserAccessibility::GetBoolAttribute(BoolAttribute attribute) const {
335 for (size_t i = 0; i < bool_attributes_.size(); ++i) {
336 if (bool_attributes_[i].first == attribute)
337 return bool_attributes_[i].second;
343 bool BrowserAccessibility::GetBoolAttribute(
344 BoolAttribute attribute, bool* value) const {
345 for (size_t i = 0; i < bool_attributes_.size(); ++i) {
346 if (bool_attributes_[i].first == attribute) {
347 *value = bool_attributes_[i].second;
355 bool BrowserAccessibility::HasFloatAttribute(FloatAttribute attribute) const {
356 for (size_t i = 0; i < float_attributes_.size(); ++i) {
357 if (float_attributes_[i].first == attribute)
364 float BrowserAccessibility::GetFloatAttribute(FloatAttribute attribute) const {
365 for (size_t i = 0; i < float_attributes_.size(); ++i) {
366 if (float_attributes_[i].first == attribute)
367 return float_attributes_[i].second;
373 bool BrowserAccessibility::GetFloatAttribute(
374 FloatAttribute attribute, float* value) const {
375 for (size_t i = 0; i < float_attributes_.size(); ++i) {
376 if (float_attributes_[i].first == attribute) {
377 *value = float_attributes_[i].second;
385 bool BrowserAccessibility::HasIntAttribute(IntAttribute attribute) const {
386 for (size_t i = 0; i < int_attributes_.size(); ++i) {
387 if (int_attributes_[i].first == attribute)
394 int BrowserAccessibility::GetIntAttribute(IntAttribute attribute) const {
395 for (size_t i = 0; i < int_attributes_.size(); ++i) {
396 if (int_attributes_[i].first == attribute)
397 return int_attributes_[i].second;
403 bool BrowserAccessibility::GetIntAttribute(
404 IntAttribute attribute, int* value) const {
405 for (size_t i = 0; i < int_attributes_.size(); ++i) {
406 if (int_attributes_[i].first == attribute) {
407 *value = int_attributes_[i].second;
415 bool BrowserAccessibility::HasStringAttribute(StringAttribute attribute) const {
416 for (size_t i = 0; i < string_attributes_.size(); ++i) {
417 if (string_attributes_[i].first == attribute)
424 const std::string& BrowserAccessibility::GetStringAttribute(
425 StringAttribute attribute) const {
426 CR_DEFINE_STATIC_LOCAL(std::string, empty_string, ());
427 for (size_t i = 0; i < string_attributes_.size(); ++i) {
428 if (string_attributes_[i].first == attribute)
429 return string_attributes_[i].second;
435 bool BrowserAccessibility::GetStringAttribute(
436 StringAttribute attribute, std::string* value) const {
437 for (size_t i = 0; i < string_attributes_.size(); ++i) {
438 if (string_attributes_[i].first == attribute) {
439 *value = string_attributes_[i].second;
447 string16 BrowserAccessibility::GetString16Attribute(
448 StringAttribute attribute) const {
449 std::string value_utf8;
450 if (!GetStringAttribute(attribute, &value_utf8))
452 return UTF8ToUTF16(value_utf8);
455 bool BrowserAccessibility::GetString16Attribute(
456 StringAttribute attribute,
457 string16* value) const {
458 std::string value_utf8;
459 if (!GetStringAttribute(attribute, &value_utf8))
461 *value = UTF8ToUTF16(value_utf8);
465 void BrowserAccessibility::SetStringAttribute(
466 StringAttribute attribute, const std::string& value) {
467 for (size_t i = 0; i < string_attributes_.size(); ++i) {
468 if (string_attributes_[i].first == attribute) {
469 string_attributes_[i].second = value;
474 string_attributes_.push_back(std::make_pair(attribute, value));
477 bool BrowserAccessibility::HasIntListAttribute(
478 AccessibilityNodeData::IntListAttribute attribute) const {
479 for (size_t i = 0; i < intlist_attributes_.size(); ++i) {
480 if (intlist_attributes_[i].first == attribute)
487 const std::vector<int32>& BrowserAccessibility::GetIntListAttribute(
488 AccessibilityNodeData::IntListAttribute attribute) const {
489 CR_DEFINE_STATIC_LOCAL(std::vector<int32>, empty_vector, ());
490 for (size_t i = 0; i < intlist_attributes_.size(); ++i) {
491 if (intlist_attributes_[i].first == attribute)
492 return intlist_attributes_[i].second;
498 bool BrowserAccessibility::GetIntListAttribute(
499 AccessibilityNodeData::IntListAttribute attribute,
500 std::vector<int32>* value) const {
501 for (size_t i = 0; i < intlist_attributes_.size(); ++i) {
502 if (intlist_attributes_[i].first == attribute) {
503 *value = intlist_attributes_[i].second;
511 bool BrowserAccessibility::GetHtmlAttribute(
512 const char* html_attr, std::string* value) const {
513 for (size_t i = 0; i < html_attributes_.size(); ++i) {
514 const std::string& attr = html_attributes_[i].first;
515 if (LowerCaseEqualsASCII(attr, html_attr)) {
516 *value = html_attributes_[i].second;
524 bool BrowserAccessibility::GetHtmlAttribute(
525 const char* html_attr, string16* value) const {
526 std::string value_utf8;
527 if (!GetHtmlAttribute(html_attr, &value_utf8))
529 *value = UTF8ToUTF16(value_utf8);
533 bool BrowserAccessibility::GetAriaTristate(
534 const char* html_attr,
536 bool* is_mixed) const {
541 if (!GetHtmlAttribute(html_attr, &value) ||
543 EqualsASCII(value, "undefined")) {
544 return false; // Not set (and *is_defined is also false)
549 if (EqualsASCII(value, "true"))
552 if (EqualsASCII(value, "mixed"))
555 return false; // Not set
558 bool BrowserAccessibility::HasState(WebKit::WebAXState state_enum) const {
559 return (state_ >> state_enum) & 1;
562 bool BrowserAccessibility::IsEditableText() const {
563 // These roles don't have readonly set, but they're not editable text.
564 if (role_ == WebKit::WebAXRoleScrollArea ||
565 role_ == WebKit::WebAXRoleColumn ||
566 role_ == WebKit::WebAXRoleTableHeaderContainer) {
570 // Note: WebAXStateReadonly being false means it's either a text control,
571 // or contenteditable. We also check for editable text roles to cover
572 // another element that has role=textbox set on it.
573 return (!HasState(WebKit::WebAXStateReadonly) ||
574 role_ == WebKit::WebAXRoleTextField ||
575 role_ == WebKit::WebAXRoleTextArea);
578 std::string BrowserAccessibility::GetTextRecursive() const {
579 if (!name_.empty()) {
584 for (size_t i = 0; i < children_.size(); ++i)
585 result += children_[i]->GetTextRecursive();
589 } // namespace content