Use native radiobutton for Default Search Engine picker dialog.
[chromium-blink-merge.git] / content / browser / accessibility / browser_accessibility.cc
blob0fdad54c91c4ef45eac37bfd034aba1e3c644049
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 <algorithm>
9 #include "base/logging.h"
10 #include "base/strings/string_number_conversions.h"
11 #include "base/strings/string_util.h"
12 #include "base/strings/utf_string_conversions.h"
13 #include "content/browser/accessibility/browser_accessibility_manager.h"
14 #include "content/common/accessibility_messages.h"
15 #include "ui/accessibility/ax_text_utils.h"
17 namespace content {
19 #if !defined(OS_MACOSX) && \
20 !defined(OS_WIN) && \
21 !defined(OS_ANDROID)
22 // We have subclassess of BrowserAccessibility on Mac and Win. For any other
23 // platform, instantiate the base class.
24 // static
25 BrowserAccessibility* BrowserAccessibility::Create() {
26 return new BrowserAccessibility();
28 #endif
30 BrowserAccessibility::BrowserAccessibility()
31 : manager_(NULL),
32 node_(NULL) {
35 BrowserAccessibility::~BrowserAccessibility() {
38 void BrowserAccessibility::Init(BrowserAccessibilityManager* manager,
39 ui::AXNode* node) {
40 manager_ = manager;
41 node_ = node;
44 bool BrowserAccessibility::PlatformIsLeaf() const {
45 if (InternalChildCount() == 0)
46 return true;
48 // All of these roles may have children that we use as internal
49 // implementation details, but we want to expose them as leaves
50 // to platform accessibility APIs.
51 switch (GetRole()) {
52 case ui::AX_ROLE_LINE_BREAK:
53 case ui::AX_ROLE_SLIDER:
54 case ui::AX_ROLE_STATIC_TEXT:
55 case ui::AX_ROLE_TEXT_FIELD:
56 return true;
57 default:
58 return false;
62 uint32 BrowserAccessibility::PlatformChildCount() const {
63 return PlatformIsLeaf() ? 0 : InternalChildCount();
66 bool BrowserAccessibility::IsNative() const {
67 return false;
70 bool BrowserAccessibility::IsDescendantOf(
71 BrowserAccessibility* ancestor) {
72 if (this == ancestor) {
73 return true;
74 } else if (GetParent()) {
75 return GetParent()->IsDescendantOf(ancestor);
78 return false;
81 BrowserAccessibility* BrowserAccessibility::PlatformGetChild(
82 uint32 child_index) const {
83 DCHECK(child_index < InternalChildCount());
84 BrowserAccessibility* result = InternalGetChild(child_index);
86 if (result->HasBoolAttribute(ui::AX_ATTR_IS_AX_TREE_HOST)) {
87 BrowserAccessibilityManager* child_manager =
88 manager_->delegate()->AccessibilityGetChildFrame(result->GetId());
89 if (child_manager)
90 result = child_manager->GetRoot();
93 return result;
96 bool BrowserAccessibility::PlatformIsChildOfLeaf() const {
97 BrowserAccessibility* ancestor = GetParent();
98 while (ancestor) {
99 if (ancestor->PlatformIsLeaf())
100 return true;
101 ancestor = ancestor->GetParent();
104 return false;
107 BrowserAccessibility* BrowserAccessibility::GetPreviousSibling() {
108 if (GetParent() && GetIndexInParent() > 0)
109 return GetParent()->InternalGetChild(GetIndexInParent() - 1);
111 return NULL;
114 BrowserAccessibility* BrowserAccessibility::GetNextSibling() {
115 if (GetParent() &&
116 GetIndexInParent() >= 0 &&
117 GetIndexInParent() < static_cast<int>(
118 GetParent()->InternalChildCount() - 1)) {
119 return GetParent()->InternalGetChild(GetIndexInParent() + 1);
122 return NULL;
125 uint32 BrowserAccessibility::InternalChildCount() const {
126 if (!node_ || !manager_)
127 return 0;
128 return static_cast<uint32>(node_->child_count());
131 BrowserAccessibility* BrowserAccessibility::InternalGetChild(
132 uint32 child_index) const {
133 if (!node_ || !manager_)
134 return NULL;
135 return manager_->GetFromAXNode(node_->ChildAtIndex(child_index));
138 BrowserAccessibility* BrowserAccessibility::GetParent() const {
139 if (!node_ || !manager_)
140 return NULL;
141 ui::AXNode* parent = node_->parent();
142 if (parent)
143 return manager_->GetFromAXNode(parent);
145 if (!manager_->delegate())
146 return NULL;
148 BrowserAccessibility* host_node =
149 manager_->delegate()->AccessibilityGetParentFrame();
150 if (!host_node)
151 return NULL;
153 return host_node->GetParent();
156 int32 BrowserAccessibility::GetIndexInParent() const {
157 return node_ ? node_->index_in_parent() : -1;
160 int32 BrowserAccessibility::GetId() const {
161 return node_ ? node_->id() : -1;
164 const ui::AXNodeData& BrowserAccessibility::GetData() const {
165 CR_DEFINE_STATIC_LOCAL(ui::AXNodeData, empty_data, ());
166 if (node_)
167 return node_->data();
168 else
169 return empty_data;
172 gfx::Rect BrowserAccessibility::GetLocation() const {
173 return GetData().location;
176 int32 BrowserAccessibility::GetRole() const {
177 return GetData().role;
180 int32 BrowserAccessibility::GetState() const {
181 return GetData().state;
184 const BrowserAccessibility::HtmlAttributes&
185 BrowserAccessibility::GetHtmlAttributes() const {
186 return GetData().html_attributes;
189 gfx::Rect BrowserAccessibility::GetLocalBoundsRect() const {
190 gfx::Rect bounds = GetLocation();
191 return ElementBoundsToLocalBounds(bounds);
194 gfx::Rect BrowserAccessibility::GetGlobalBoundsRect() const {
195 gfx::Rect bounds = GetLocalBoundsRect();
197 // Adjust the bounds by the top left corner of the containing view's bounds
198 // in screen coordinates.
199 bounds.Offset(manager_->GetViewBounds().OffsetFromOrigin());
201 return bounds;
204 gfx::Rect BrowserAccessibility::GetLocalBoundsForRange(int start, int len)
205 const {
206 if (GetRole() != ui::AX_ROLE_STATIC_TEXT) {
207 // Apply recursively to all static text descendants. For example, if
208 // you call it on a div with two text node children, it just calls
209 // GetLocalBoundsForRange on each of the two children (adjusting
210 // |start| for each one) and unions the resulting rects.
211 gfx::Rect bounds;
212 for (size_t i = 0; i < InternalChildCount(); ++i) {
213 BrowserAccessibility* child = InternalGetChild(i);
214 int child_len = child->GetStaticTextLenRecursive();
215 if (start < child_len && start + len > 0) {
216 gfx::Rect child_rect = child->GetLocalBoundsForRange(start, len);
217 bounds.Union(child_rect);
219 start -= child_len;
221 return ElementBoundsToLocalBounds(bounds);
224 int end = start + len;
225 int child_start = 0;
226 int child_end = 0;
228 gfx::Rect bounds;
229 for (size_t i = 0; i < InternalChildCount() && child_end < start + len; ++i) {
230 BrowserAccessibility* child = InternalGetChild(i);
231 if (child->GetRole() != ui::AX_ROLE_INLINE_TEXT_BOX) {
232 DLOG(WARNING) << "BrowserAccessibility objects with role STATIC_TEXT " <<
233 "should have children of role INLINE_TEXT_BOX.";
234 continue;
237 std::string child_text;
238 child->GetStringAttribute(ui::AX_ATTR_VALUE, &child_text);
239 int child_len = static_cast<int>(child_text.size());
240 child_start = child_end;
241 child_end += child_len;
243 if (child_end < start)
244 continue;
246 int overlap_start = std::max(start, child_start);
247 int overlap_end = std::min(end, child_end);
249 int local_start = overlap_start - child_start;
250 int local_end = overlap_end - child_start;
252 gfx::Rect child_rect = child->GetLocation();
253 int text_direction = child->GetIntAttribute(
254 ui::AX_ATTR_TEXT_DIRECTION);
255 const std::vector<int32>& character_offsets = child->GetIntListAttribute(
256 ui::AX_ATTR_CHARACTER_OFFSETS);
257 int start_pixel_offset =
258 local_start > 0 ? character_offsets[local_start - 1] : 0;
259 int end_pixel_offset =
260 local_end > 0 ? character_offsets[local_end - 1] : 0;
262 gfx::Rect child_overlap_rect;
263 switch (text_direction) {
264 case ui::AX_TEXT_DIRECTION_NONE:
265 case ui::AX_TEXT_DIRECTION_LR: {
266 int left = child_rect.x() + start_pixel_offset;
267 int right = child_rect.x() + end_pixel_offset;
268 child_overlap_rect = gfx::Rect(left, child_rect.y(),
269 right - left, child_rect.height());
270 break;
272 case ui::AX_TEXT_DIRECTION_RL: {
273 int right = child_rect.right() - start_pixel_offset;
274 int left = child_rect.right() - end_pixel_offset;
275 child_overlap_rect = gfx::Rect(left, child_rect.y(),
276 right - left, child_rect.height());
277 break;
279 case ui::AX_TEXT_DIRECTION_TB: {
280 int top = child_rect.y() + start_pixel_offset;
281 int bottom = child_rect.y() + end_pixel_offset;
282 child_overlap_rect = gfx::Rect(child_rect.x(), top,
283 child_rect.width(), bottom - top);
284 break;
286 case ui::AX_TEXT_DIRECTION_BT: {
287 int bottom = child_rect.bottom() - start_pixel_offset;
288 int top = child_rect.bottom() - end_pixel_offset;
289 child_overlap_rect = gfx::Rect(child_rect.x(), top,
290 child_rect.width(), bottom - top);
291 break;
293 default:
294 NOTREACHED();
297 if (bounds.width() == 0 && bounds.height() == 0)
298 bounds = child_overlap_rect;
299 else
300 bounds.Union(child_overlap_rect);
303 return ElementBoundsToLocalBounds(bounds);
306 gfx::Rect BrowserAccessibility::GetGlobalBoundsForRange(int start, int len)
307 const {
308 gfx::Rect bounds = GetLocalBoundsForRange(start, len);
310 // Adjust the bounds by the top left corner of the containing view's bounds
311 // in screen coordinates.
312 bounds.Offset(manager_->GetViewBounds().OffsetFromOrigin());
314 return bounds;
317 int BrowserAccessibility::GetWordStartBoundary(
318 int start, ui::TextBoundaryDirection direction) const {
319 DCHECK_GE(start, -1);
320 // Special offset that indicates that a word boundary has not been found.
321 int word_start_not_found = GetStaticTextLenRecursive();
322 int word_start = word_start_not_found;
324 switch (GetRole()) {
325 case ui::AX_ROLE_STATIC_TEXT: {
326 int prev_word_start = word_start_not_found;
327 int child_start = 0;
328 int child_end = 0;
330 // Go through the inline text boxes.
331 for (size_t i = 0; i < InternalChildCount(); ++i) {
332 // The next child starts where the previous one ended.
333 child_start = child_end;
334 BrowserAccessibility* child = InternalGetChild(i);
335 DCHECK_EQ(child->GetRole(), ui::AX_ROLE_INLINE_TEXT_BOX);
336 const std::string& child_text = child->GetStringAttribute(
337 ui::AX_ATTR_VALUE);
338 int child_len = static_cast<int>(child_text.size());
339 child_end += child_len; // End is one past the last character.
341 const std::vector<int32>& word_starts = child->GetIntListAttribute(
342 ui::AX_ATTR_WORD_STARTS);
343 if (word_starts.empty()) {
344 word_start = child_end;
345 continue;
348 int local_start = start - child_start;
349 std::vector<int32>::const_iterator iter = std::upper_bound(
350 word_starts.begin(), word_starts.end(), local_start);
351 if (iter != word_starts.end()) {
352 if (direction == ui::FORWARDS_DIRECTION) {
353 word_start = child_start + *iter;
354 } else if (direction == ui::BACKWARDS_DIRECTION) {
355 if (iter == word_starts.begin()) {
356 // Return the position of the last word in the previous child.
357 word_start = prev_word_start;
358 } else {
359 word_start = child_start + *(iter - 1);
361 } else {
362 NOTREACHED();
364 break;
367 // No word start that is greater than the requested offset has been
368 // found.
369 prev_word_start = child_start + *(iter - 1);
370 if (direction == ui::FORWARDS_DIRECTION) {
371 word_start = child_end;
372 } else if (direction == ui::BACKWARDS_DIRECTION) {
373 word_start = prev_word_start;
374 } else {
375 NOTREACHED();
378 return word_start;
381 case ui::AX_ROLE_LINE_BREAK:
382 // Words never start at a line break.
383 return word_start_not_found;
385 default:
386 // If there are no children, the word start boundary is still unknown or
387 // found previously depending on the direction.
388 if (!InternalChildCount())
389 return word_start_not_found;
391 int child_start = 0;
392 for (size_t i = 0; i < InternalChildCount(); ++i) {
393 BrowserAccessibility* child = InternalGetChild(i);
394 int child_len = child->GetStaticTextLenRecursive();
395 int child_word_start = child->GetWordStartBoundary(start, direction);
396 if (child_word_start < child_len) {
397 // We have found a possible word boundary.
398 word_start = child_start + child_word_start;
401 // Decide when to stop searching.
402 if ((word_start != word_start_not_found &&
403 direction == ui::FORWARDS_DIRECTION) ||
404 (start < child_len &&
405 direction == ui::BACKWARDS_DIRECTION)) {
406 break;
409 child_start += child_len;
410 if (start >= child_len)
411 start -= child_len;
412 else
413 start = -1;
415 return word_start;
419 BrowserAccessibility* BrowserAccessibility::BrowserAccessibilityForPoint(
420 const gfx::Point& point) {
421 // The best result found that's a child of this object.
422 BrowserAccessibility* child_result = NULL;
423 // The best result that's an indirect descendant like grandchild, etc.
424 BrowserAccessibility* descendant_result = NULL;
426 // Walk the children recursively looking for the BrowserAccessibility that
427 // most tightly encloses the specified point. Walk backwards so that in
428 // the absence of any other information, we assume the object that occurs
429 // later in the tree is on top of one that comes before it.
430 for (int i = static_cast<int>(PlatformChildCount()) - 1; i >= 0; --i) {
431 BrowserAccessibility* child = PlatformGetChild(i);
433 // Skip table columns because cells are only contained in rows,
434 // not columns.
435 if (child->GetRole() == ui::AX_ROLE_COLUMN)
436 continue;
438 if (child->GetGlobalBoundsRect().Contains(point)) {
439 BrowserAccessibility* result = child->BrowserAccessibilityForPoint(point);
440 if (result == child && !child_result)
441 child_result = result;
442 if (result != child && !descendant_result)
443 descendant_result = result;
446 if (child_result && descendant_result)
447 break;
450 // Explanation of logic: it's possible that this point overlaps more than
451 // one child of this object. If so, as a heuristic we prefer if the point
452 // overlaps a descendant of one of the two children and not the other.
453 // As an example, suppose you have two rows of buttons - the buttons don't
454 // overlap, but the rows do. Without this heuristic, we'd greedily only
455 // consider one of the containers.
456 if (descendant_result)
457 return descendant_result;
458 if (child_result)
459 return child_result;
461 return this;
464 void BrowserAccessibility::Destroy() {
465 // Allow the object to fire a TextRemoved notification.
466 manager_->NotifyAccessibilityEvent(ui::AX_EVENT_HIDE, this);
467 node_ = NULL;
468 manager_ = NULL;
470 NativeReleaseReference();
473 void BrowserAccessibility::NativeReleaseReference() {
474 delete this;
477 bool BrowserAccessibility::HasBoolAttribute(
478 ui::AXBoolAttribute attribute) const {
479 const ui::AXNodeData& data = GetData();
480 for (size_t i = 0; i < data.bool_attributes.size(); ++i) {
481 if (data.bool_attributes[i].first == attribute)
482 return true;
485 return false;
489 bool BrowserAccessibility::GetBoolAttribute(
490 ui::AXBoolAttribute attribute) const {
491 const ui::AXNodeData& data = GetData();
492 for (size_t i = 0; i < data.bool_attributes.size(); ++i) {
493 if (data.bool_attributes[i].first == attribute)
494 return data.bool_attributes[i].second;
497 return false;
500 bool BrowserAccessibility::GetBoolAttribute(
501 ui::AXBoolAttribute attribute, bool* value) const {
502 const ui::AXNodeData& data = GetData();
503 for (size_t i = 0; i < data.bool_attributes.size(); ++i) {
504 if (data.bool_attributes[i].first == attribute) {
505 *value = data.bool_attributes[i].second;
506 return true;
510 return false;
513 bool BrowserAccessibility::HasFloatAttribute(
514 ui::AXFloatAttribute attribute) const {
515 const ui::AXNodeData& data = GetData();
516 for (size_t i = 0; i < data.float_attributes.size(); ++i) {
517 if (data.float_attributes[i].first == attribute)
518 return true;
521 return false;
524 float BrowserAccessibility::GetFloatAttribute(
525 ui::AXFloatAttribute attribute) const {
526 const ui::AXNodeData& data = GetData();
527 for (size_t i = 0; i < data.float_attributes.size(); ++i) {
528 if (data.float_attributes[i].first == attribute)
529 return data.float_attributes[i].second;
532 return 0.0;
535 bool BrowserAccessibility::GetFloatAttribute(
536 ui::AXFloatAttribute attribute, float* value) const {
537 const ui::AXNodeData& data = GetData();
538 for (size_t i = 0; i < data.float_attributes.size(); ++i) {
539 if (data.float_attributes[i].first == attribute) {
540 *value = data.float_attributes[i].second;
541 return true;
545 return false;
548 bool BrowserAccessibility::HasIntAttribute(
549 ui::AXIntAttribute attribute) const {
550 const ui::AXNodeData& data = GetData();
551 for (size_t i = 0; i < data.int_attributes.size(); ++i) {
552 if (data.int_attributes[i].first == attribute)
553 return true;
556 return false;
559 int BrowserAccessibility::GetIntAttribute(ui::AXIntAttribute attribute) const {
560 const ui::AXNodeData& data = GetData();
561 for (size_t i = 0; i < data.int_attributes.size(); ++i) {
562 if (data.int_attributes[i].first == attribute)
563 return data.int_attributes[i].second;
566 return 0;
569 bool BrowserAccessibility::GetIntAttribute(
570 ui::AXIntAttribute attribute, int* value) const {
571 const ui::AXNodeData& data = GetData();
572 for (size_t i = 0; i < data.int_attributes.size(); ++i) {
573 if (data.int_attributes[i].first == attribute) {
574 *value = data.int_attributes[i].second;
575 return true;
579 return false;
582 bool BrowserAccessibility::HasStringAttribute(
583 ui::AXStringAttribute attribute) const {
584 const ui::AXNodeData& data = GetData();
585 for (size_t i = 0; i < data.string_attributes.size(); ++i) {
586 if (data.string_attributes[i].first == attribute)
587 return true;
590 return false;
593 const std::string& BrowserAccessibility::GetStringAttribute(
594 ui::AXStringAttribute attribute) const {
595 const ui::AXNodeData& data = GetData();
596 CR_DEFINE_STATIC_LOCAL(std::string, empty_string, ());
597 for (size_t i = 0; i < data.string_attributes.size(); ++i) {
598 if (data.string_attributes[i].first == attribute)
599 return data.string_attributes[i].second;
602 return empty_string;
605 bool BrowserAccessibility::GetStringAttribute(
606 ui::AXStringAttribute attribute, std::string* value) const {
607 const ui::AXNodeData& data = GetData();
608 for (size_t i = 0; i < data.string_attributes.size(); ++i) {
609 if (data.string_attributes[i].first == attribute) {
610 *value = data.string_attributes[i].second;
611 return true;
615 return false;
618 base::string16 BrowserAccessibility::GetString16Attribute(
619 ui::AXStringAttribute attribute) const {
620 std::string value_utf8;
621 if (!GetStringAttribute(attribute, &value_utf8))
622 return base::string16();
623 return base::UTF8ToUTF16(value_utf8);
626 bool BrowserAccessibility::GetString16Attribute(
627 ui::AXStringAttribute attribute,
628 base::string16* value) const {
629 std::string value_utf8;
630 if (!GetStringAttribute(attribute, &value_utf8))
631 return false;
632 *value = base::UTF8ToUTF16(value_utf8);
633 return true;
636 bool BrowserAccessibility::HasIntListAttribute(
637 ui::AXIntListAttribute attribute) const {
638 const ui::AXNodeData& data = GetData();
639 for (size_t i = 0; i < data.intlist_attributes.size(); ++i) {
640 if (data.intlist_attributes[i].first == attribute)
641 return true;
644 return false;
647 const std::vector<int32>& BrowserAccessibility::GetIntListAttribute(
648 ui::AXIntListAttribute attribute) const {
649 const ui::AXNodeData& data = GetData();
650 CR_DEFINE_STATIC_LOCAL(std::vector<int32>, empty_vector, ());
651 for (size_t i = 0; i < data.intlist_attributes.size(); ++i) {
652 if (data.intlist_attributes[i].first == attribute)
653 return data.intlist_attributes[i].second;
656 return empty_vector;
659 bool BrowserAccessibility::GetIntListAttribute(
660 ui::AXIntListAttribute attribute,
661 std::vector<int32>* value) const {
662 const ui::AXNodeData& data = GetData();
663 for (size_t i = 0; i < data.intlist_attributes.size(); ++i) {
664 if (data.intlist_attributes[i].first == attribute) {
665 *value = data.intlist_attributes[i].second;
666 return true;
670 return false;
673 bool BrowserAccessibility::GetHtmlAttribute(
674 const char* html_attr, std::string* value) const {
675 for (size_t i = 0; i < GetHtmlAttributes().size(); ++i) {
676 const std::string& attr = GetHtmlAttributes()[i].first;
677 if (LowerCaseEqualsASCII(attr, html_attr)) {
678 *value = GetHtmlAttributes()[i].second;
679 return true;
683 return false;
686 bool BrowserAccessibility::GetHtmlAttribute(
687 const char* html_attr, base::string16* value) const {
688 std::string value_utf8;
689 if (!GetHtmlAttribute(html_attr, &value_utf8))
690 return false;
691 *value = base::UTF8ToUTF16(value_utf8);
692 return true;
695 bool BrowserAccessibility::GetAriaTristate(
696 const char* html_attr,
697 bool* is_defined,
698 bool* is_mixed) const {
699 *is_defined = false;
700 *is_mixed = false;
702 base::string16 value;
703 if (!GetHtmlAttribute(html_attr, &value) ||
704 value.empty() ||
705 EqualsASCII(value, "undefined")) {
706 return false; // Not set (and *is_defined is also false)
709 *is_defined = true;
711 if (EqualsASCII(value, "true"))
712 return true;
714 if (EqualsASCII(value, "mixed"))
715 *is_mixed = true;
717 return false; // Not set
720 bool BrowserAccessibility::HasState(ui::AXState state_enum) const {
721 return (GetState() >> state_enum) & 1;
724 bool BrowserAccessibility::IsCellOrTableHeaderRole() const {
725 return (GetRole() == ui::AX_ROLE_CELL ||
726 GetRole() == ui::AX_ROLE_COLUMN_HEADER ||
727 GetRole() == ui::AX_ROLE_ROW_HEADER);
730 bool BrowserAccessibility::IsEditableText() const {
731 // These roles don't have readonly set, but they're not editable text.
732 if (GetRole() == ui::AX_ROLE_SCROLL_AREA ||
733 GetRole() == ui::AX_ROLE_COLUMN ||
734 GetRole() == ui::AX_ROLE_TABLE_HEADER_CONTAINER) {
735 return false;
738 // Note: WebAXStateReadonly being false means it's either a text control,
739 // or contenteditable. We also check for editable text roles to cover
740 // another element that has role=textbox set on it.
741 return (!HasState(ui::AX_STATE_READ_ONLY) ||
742 GetRole() == ui::AX_ROLE_TEXT_FIELD);
745 bool BrowserAccessibility::IsWebAreaForPresentationalIframe() const {
746 if (GetRole() != ui::AX_ROLE_WEB_AREA &&
747 GetRole() != ui::AX_ROLE_ROOT_WEB_AREA) {
748 return false;
751 BrowserAccessibility* parent = GetParent();
752 if (!parent)
753 return false;
755 BrowserAccessibility* grandparent = parent->GetParent();
756 if (!grandparent)
757 return false;
759 return grandparent->GetRole() == ui::AX_ROLE_IFRAME_PRESENTATIONAL;
762 int BrowserAccessibility::GetStaticTextLenRecursive() const {
763 if (GetRole() == ui::AX_ROLE_STATIC_TEXT ||
764 GetRole() == ui::AX_ROLE_LINE_BREAK) {
765 return static_cast<int>(GetStringAttribute(ui::AX_ATTR_VALUE).size());
768 int len = 0;
769 for (size_t i = 0; i < InternalChildCount(); ++i)
770 len += InternalGetChild(i)->GetStaticTextLenRecursive();
771 return len;
774 BrowserAccessibility* BrowserAccessibility::GetParentForBoundsCalculation()
775 const {
776 if (!node_ || !manager_)
777 return NULL;
778 ui::AXNode* parent = node_->parent();
779 if (parent)
780 return manager_->GetFromAXNode(parent);
782 if (!manager_->delegate())
783 return NULL;
785 return manager_->delegate()->AccessibilityGetParentFrame();
788 gfx::Rect BrowserAccessibility::ElementBoundsToLocalBounds(gfx::Rect bounds)
789 const {
790 // Walk up the parent chain. Every time we encounter a Web Area, offset
791 // based on the scroll bars and then offset based on the origin of that
792 // nested web area.
793 BrowserAccessibility* parent = GetParentForBoundsCalculation();
794 bool need_to_offset_web_area =
795 (GetRole() == ui::AX_ROLE_WEB_AREA ||
796 GetRole() == ui::AX_ROLE_ROOT_WEB_AREA);
797 while (parent) {
798 if (need_to_offset_web_area &&
799 parent->GetLocation().width() > 0 &&
800 parent->GetLocation().height() > 0) {
801 bounds.Offset(parent->GetLocation().x(), parent->GetLocation().y());
802 need_to_offset_web_area = false;
805 // On some platforms, we don't want to take the root scroll offsets
806 // into account.
807 if (parent->GetRole() == ui::AX_ROLE_ROOT_WEB_AREA &&
808 !manager()->UseRootScrollOffsetsWhenComputingBounds()) {
809 break;
812 if (parent->GetRole() == ui::AX_ROLE_WEB_AREA ||
813 parent->GetRole() == ui::AX_ROLE_ROOT_WEB_AREA) {
814 int sx = 0;
815 int sy = 0;
816 if (parent->GetIntAttribute(ui::AX_ATTR_SCROLL_X, &sx) &&
817 parent->GetIntAttribute(ui::AX_ATTR_SCROLL_Y, &sy)) {
818 bounds.Offset(-sx, -sy);
820 need_to_offset_web_area = true;
822 parent = parent->GetParentForBoundsCalculation();
825 return bounds;
828 } // namespace content