Re-subimission of https://codereview.chromium.org/1041213003/
[chromium-blink-merge.git] / content / browser / accessibility / browser_accessibility.cc
blob7a8c56020e5dcd761f5b3378e801c046a8a272a0
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_AREA:
56 case ui::AX_ROLE_TEXT_FIELD:
57 return true;
58 default:
59 return false;
63 uint32 BrowserAccessibility::PlatformChildCount() const {
64 return PlatformIsLeaf() ? 0 : InternalChildCount();
67 bool BrowserAccessibility::IsNative() const {
68 return false;
71 bool BrowserAccessibility::IsDescendantOf(
72 BrowserAccessibility* ancestor) {
73 if (this == ancestor) {
74 return true;
75 } else if (GetParent()) {
76 return GetParent()->IsDescendantOf(ancestor);
79 return false;
82 BrowserAccessibility* BrowserAccessibility::PlatformGetChild(
83 uint32 child_index) const {
84 DCHECK(child_index < InternalChildCount());
85 BrowserAccessibility* result = InternalGetChild(child_index);
87 if (result->HasBoolAttribute(ui::AX_ATTR_IS_AX_TREE_HOST)) {
88 BrowserAccessibilityManager* child_manager =
89 manager_->delegate()->AccessibilityGetChildFrame(result->GetId());
90 if (child_manager)
91 result = child_manager->GetRoot();
94 return result;
97 bool BrowserAccessibility::PlatformIsChildOfLeaf() const {
98 BrowserAccessibility* ancestor = GetParent();
99 while (ancestor) {
100 if (ancestor->PlatformIsLeaf())
101 return true;
102 ancestor = ancestor->GetParent();
105 return false;
108 BrowserAccessibility* BrowserAccessibility::GetPreviousSibling() {
109 if (GetParent() && GetIndexInParent() > 0)
110 return GetParent()->InternalGetChild(GetIndexInParent() - 1);
112 return NULL;
115 BrowserAccessibility* BrowserAccessibility::GetNextSibling() {
116 if (GetParent() &&
117 GetIndexInParent() >= 0 &&
118 GetIndexInParent() < static_cast<int>(
119 GetParent()->InternalChildCount() - 1)) {
120 return GetParent()->InternalGetChild(GetIndexInParent() + 1);
123 return NULL;
126 uint32 BrowserAccessibility::InternalChildCount() const {
127 if (!node_ || !manager_)
128 return 0;
129 return static_cast<uint32>(node_->child_count());
132 BrowserAccessibility* BrowserAccessibility::InternalGetChild(
133 uint32 child_index) const {
134 if (!node_ || !manager_)
135 return NULL;
136 return manager_->GetFromAXNode(node_->ChildAtIndex(child_index));
139 BrowserAccessibility* BrowserAccessibility::GetParent() const {
140 if (!node_ || !manager_)
141 return NULL;
142 ui::AXNode* parent = node_->parent();
143 if (parent)
144 return manager_->GetFromAXNode(parent);
146 if (!manager_->delegate())
147 return NULL;
149 BrowserAccessibility* host_node =
150 manager_->delegate()->AccessibilityGetParentFrame();
151 if (!host_node)
152 return NULL;
154 return host_node->GetParent();
157 int32 BrowserAccessibility::GetIndexInParent() const {
158 return node_ ? node_->index_in_parent() : -1;
161 int32 BrowserAccessibility::GetId() const {
162 return node_ ? node_->id() : -1;
165 const ui::AXNodeData& BrowserAccessibility::GetData() const {
166 CR_DEFINE_STATIC_LOCAL(ui::AXNodeData, empty_data, ());
167 if (node_)
168 return node_->data();
169 else
170 return empty_data;
173 gfx::Rect BrowserAccessibility::GetLocation() const {
174 return GetData().location;
177 int32 BrowserAccessibility::GetRole() const {
178 return GetData().role;
181 int32 BrowserAccessibility::GetState() const {
182 return GetData().state;
185 const BrowserAccessibility::HtmlAttributes&
186 BrowserAccessibility::GetHtmlAttributes() const {
187 return GetData().html_attributes;
190 gfx::Rect BrowserAccessibility::GetLocalBoundsRect() const {
191 gfx::Rect bounds = GetLocation();
192 return ElementBoundsToLocalBounds(bounds);
195 gfx::Rect BrowserAccessibility::GetGlobalBoundsRect() const {
196 gfx::Rect bounds = GetLocalBoundsRect();
198 // Adjust the bounds by the top left corner of the containing view's bounds
199 // in screen coordinates.
200 bounds.Offset(manager_->GetViewBounds().OffsetFromOrigin());
202 return bounds;
205 gfx::Rect BrowserAccessibility::GetLocalBoundsForRange(int start, int len)
206 const {
207 if (GetRole() != ui::AX_ROLE_STATIC_TEXT) {
208 // Apply recursively to all static text descendants. For example, if
209 // you call it on a div with two text node children, it just calls
210 // GetLocalBoundsForRange on each of the two children (adjusting
211 // |start| for each one) and unions the resulting rects.
212 gfx::Rect bounds;
213 for (size_t i = 0; i < InternalChildCount(); ++i) {
214 BrowserAccessibility* child = InternalGetChild(i);
215 int child_len = child->GetStaticTextLenRecursive();
216 if (start < child_len && start + len > 0) {
217 gfx::Rect child_rect = child->GetLocalBoundsForRange(start, len);
218 bounds.Union(child_rect);
220 start -= child_len;
222 return ElementBoundsToLocalBounds(bounds);
225 int end = start + len;
226 int child_start = 0;
227 int child_end = 0;
229 gfx::Rect bounds;
230 for (size_t i = 0; i < InternalChildCount() && child_end < start + len; ++i) {
231 BrowserAccessibility* child = InternalGetChild(i);
232 DCHECK_EQ(child->GetRole(), ui::AX_ROLE_INLINE_TEXT_BOX);
233 std::string child_text;
234 child->GetStringAttribute(ui::AX_ATTR_VALUE, &child_text);
235 int child_len = static_cast<int>(child_text.size());
236 child_start = child_end;
237 child_end += child_len;
239 if (child_end < start)
240 continue;
242 int overlap_start = std::max(start, child_start);
243 int overlap_end = std::min(end, child_end);
245 int local_start = overlap_start - child_start;
246 int local_end = overlap_end - child_start;
248 gfx::Rect child_rect = child->GetLocation();
249 int text_direction = child->GetIntAttribute(
250 ui::AX_ATTR_TEXT_DIRECTION);
251 const std::vector<int32>& character_offsets = child->GetIntListAttribute(
252 ui::AX_ATTR_CHARACTER_OFFSETS);
253 int start_pixel_offset =
254 local_start > 0 ? character_offsets[local_start - 1] : 0;
255 int end_pixel_offset =
256 local_end > 0 ? character_offsets[local_end - 1] : 0;
258 gfx::Rect child_overlap_rect;
259 switch (text_direction) {
260 case ui::AX_TEXT_DIRECTION_NONE:
261 case ui::AX_TEXT_DIRECTION_LR: {
262 int left = child_rect.x() + start_pixel_offset;
263 int right = child_rect.x() + end_pixel_offset;
264 child_overlap_rect = gfx::Rect(left, child_rect.y(),
265 right - left, child_rect.height());
266 break;
268 case ui::AX_TEXT_DIRECTION_RL: {
269 int right = child_rect.right() - start_pixel_offset;
270 int left = child_rect.right() - end_pixel_offset;
271 child_overlap_rect = gfx::Rect(left, child_rect.y(),
272 right - left, child_rect.height());
273 break;
275 case ui::AX_TEXT_DIRECTION_TB: {
276 int top = child_rect.y() + start_pixel_offset;
277 int bottom = child_rect.y() + end_pixel_offset;
278 child_overlap_rect = gfx::Rect(child_rect.x(), top,
279 child_rect.width(), bottom - top);
280 break;
282 case ui::AX_TEXT_DIRECTION_BT: {
283 int bottom = child_rect.bottom() - start_pixel_offset;
284 int top = child_rect.bottom() - end_pixel_offset;
285 child_overlap_rect = gfx::Rect(child_rect.x(), top,
286 child_rect.width(), bottom - top);
287 break;
289 default:
290 NOTREACHED();
293 if (bounds.width() == 0 && bounds.height() == 0)
294 bounds = child_overlap_rect;
295 else
296 bounds.Union(child_overlap_rect);
299 return ElementBoundsToLocalBounds(bounds);
302 gfx::Rect BrowserAccessibility::GetGlobalBoundsForRange(int start, int len)
303 const {
304 gfx::Rect bounds = GetLocalBoundsForRange(start, len);
306 // Adjust the bounds by the top left corner of the containing view's bounds
307 // in screen coordinates.
308 bounds.Offset(manager_->GetViewBounds().OffsetFromOrigin());
310 return bounds;
313 int BrowserAccessibility::GetWordStartBoundary(
314 int start, ui::TextBoundaryDirection direction) const {
315 int word_start = 0;
316 int prev_word_start = 0;
317 if (GetRole() != ui::AX_ROLE_STATIC_TEXT) {
318 for (size_t i = 0; i < InternalChildCount(); ++i) {
319 BrowserAccessibility* child = InternalGetChild(i);
320 int child_len = child->GetStaticTextLenRecursive();
321 int child_word_start = child->GetWordStartBoundary(start, direction);
322 word_start += child_word_start;
323 if (child_word_start != child_len)
324 break;
325 start -= child_len;
327 return word_start;
330 int child_start = 0;
331 int child_end = 0;
332 for (size_t i = 0; i < InternalChildCount(); ++i) {
333 // The next child starts where the previous one ended.
334 child_start = child_end;
335 BrowserAccessibility* child = InternalGetChild(i);
336 DCHECK_EQ(child->GetRole(), ui::AX_ROLE_INLINE_TEXT_BOX);
337 const std::string& child_text = child->GetStringAttribute(
338 ui::AX_ATTR_VALUE);
339 int child_len = static_cast<int>(child_text.size());
340 child_end += child_len; // End is one past the last character.
342 const std::vector<int32>& word_starts = child->GetIntListAttribute(
343 ui::AX_ATTR_WORD_STARTS);
344 if (word_starts.empty()) {
345 word_start = child_end;
346 continue;
349 int local_start = start - child_start;
350 std::vector<int32>::const_iterator iter = std::upper_bound(
351 word_starts.begin(), word_starts.end(), local_start);
352 if (iter != word_starts.end()) {
353 if (direction == ui::FORWARDS_DIRECTION) {
354 word_start = child_start + *iter;
355 } else if (direction == ui::BACKWARDS_DIRECTION) {
356 if (iter == word_starts.begin()) {
357 // Return the position of the last word in the previous child.
358 word_start = prev_word_start;
359 } else {
360 word_start = child_start + *(iter - 1);
362 } else {
363 NOTREACHED();
365 break;
368 // No word start that is >= to the requested offset has been 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();
379 return word_start;
382 BrowserAccessibility* BrowserAccessibility::BrowserAccessibilityForPoint(
383 const gfx::Point& point) {
384 // The best result found that's a child of this object.
385 BrowserAccessibility* child_result = NULL;
386 // The best result that's an indirect descendant like grandchild, etc.
387 BrowserAccessibility* descendant_result = NULL;
389 // Walk the children recursively looking for the BrowserAccessibility that
390 // most tightly encloses the specified point. Walk backwards so that in
391 // the absence of any other information, we assume the object that occurs
392 // later in the tree is on top of one that comes before it.
393 for (int i = static_cast<int>(PlatformChildCount()) - 1; i >= 0; --i) {
394 BrowserAccessibility* child = PlatformGetChild(i);
396 // Skip table columns because cells are only contained in rows,
397 // not columns.
398 if (child->GetRole() == ui::AX_ROLE_COLUMN)
399 continue;
401 if (child->GetGlobalBoundsRect().Contains(point)) {
402 BrowserAccessibility* result = child->BrowserAccessibilityForPoint(point);
403 if (result == child && !child_result)
404 child_result = result;
405 if (result != child && !descendant_result)
406 descendant_result = result;
409 if (child_result && descendant_result)
410 break;
413 // Explanation of logic: it's possible that this point overlaps more than
414 // one child of this object. If so, as a heuristic we prefer if the point
415 // overlaps a descendant of one of the two children and not the other.
416 // As an example, suppose you have two rows of buttons - the buttons don't
417 // overlap, but the rows do. Without this heuristic, we'd greedily only
418 // consider one of the containers.
419 if (descendant_result)
420 return descendant_result;
421 if (child_result)
422 return child_result;
424 return this;
427 void BrowserAccessibility::Destroy() {
428 // Allow the object to fire a TextRemoved notification.
429 manager_->NotifyAccessibilityEvent(ui::AX_EVENT_HIDE, this);
430 node_ = NULL;
431 manager_ = NULL;
433 NativeReleaseReference();
436 void BrowserAccessibility::NativeReleaseReference() {
437 delete this;
440 bool BrowserAccessibility::HasBoolAttribute(
441 ui::AXBoolAttribute attribute) const {
442 const ui::AXNodeData& data = GetData();
443 for (size_t i = 0; i < data.bool_attributes.size(); ++i) {
444 if (data.bool_attributes[i].first == attribute)
445 return true;
448 return false;
452 bool BrowserAccessibility::GetBoolAttribute(
453 ui::AXBoolAttribute attribute) const {
454 const ui::AXNodeData& data = GetData();
455 for (size_t i = 0; i < data.bool_attributes.size(); ++i) {
456 if (data.bool_attributes[i].first == attribute)
457 return data.bool_attributes[i].second;
460 return false;
463 bool BrowserAccessibility::GetBoolAttribute(
464 ui::AXBoolAttribute attribute, bool* value) const {
465 const ui::AXNodeData& data = GetData();
466 for (size_t i = 0; i < data.bool_attributes.size(); ++i) {
467 if (data.bool_attributes[i].first == attribute) {
468 *value = data.bool_attributes[i].second;
469 return true;
473 return false;
476 bool BrowserAccessibility::HasFloatAttribute(
477 ui::AXFloatAttribute attribute) const {
478 const ui::AXNodeData& data = GetData();
479 for (size_t i = 0; i < data.float_attributes.size(); ++i) {
480 if (data.float_attributes[i].first == attribute)
481 return true;
484 return false;
487 float BrowserAccessibility::GetFloatAttribute(
488 ui::AXFloatAttribute attribute) const {
489 const ui::AXNodeData& data = GetData();
490 for (size_t i = 0; i < data.float_attributes.size(); ++i) {
491 if (data.float_attributes[i].first == attribute)
492 return data.float_attributes[i].second;
495 return 0.0;
498 bool BrowserAccessibility::GetFloatAttribute(
499 ui::AXFloatAttribute attribute, float* value) const {
500 const ui::AXNodeData& data = GetData();
501 for (size_t i = 0; i < data.float_attributes.size(); ++i) {
502 if (data.float_attributes[i].first == attribute) {
503 *value = data.float_attributes[i].second;
504 return true;
508 return false;
511 bool BrowserAccessibility::HasIntAttribute(
512 ui::AXIntAttribute attribute) const {
513 const ui::AXNodeData& data = GetData();
514 for (size_t i = 0; i < data.int_attributes.size(); ++i) {
515 if (data.int_attributes[i].first == attribute)
516 return true;
519 return false;
522 int BrowserAccessibility::GetIntAttribute(ui::AXIntAttribute attribute) const {
523 const ui::AXNodeData& data = GetData();
524 for (size_t i = 0; i < data.int_attributes.size(); ++i) {
525 if (data.int_attributes[i].first == attribute)
526 return data.int_attributes[i].second;
529 return 0;
532 bool BrowserAccessibility::GetIntAttribute(
533 ui::AXIntAttribute attribute, int* value) const {
534 const ui::AXNodeData& data = GetData();
535 for (size_t i = 0; i < data.int_attributes.size(); ++i) {
536 if (data.int_attributes[i].first == attribute) {
537 *value = data.int_attributes[i].second;
538 return true;
542 return false;
545 bool BrowserAccessibility::HasStringAttribute(
546 ui::AXStringAttribute attribute) const {
547 const ui::AXNodeData& data = GetData();
548 for (size_t i = 0; i < data.string_attributes.size(); ++i) {
549 if (data.string_attributes[i].first == attribute)
550 return true;
553 return false;
556 const std::string& BrowserAccessibility::GetStringAttribute(
557 ui::AXStringAttribute attribute) const {
558 const ui::AXNodeData& data = GetData();
559 CR_DEFINE_STATIC_LOCAL(std::string, empty_string, ());
560 for (size_t i = 0; i < data.string_attributes.size(); ++i) {
561 if (data.string_attributes[i].first == attribute)
562 return data.string_attributes[i].second;
565 return empty_string;
568 bool BrowserAccessibility::GetStringAttribute(
569 ui::AXStringAttribute attribute, std::string* value) const {
570 const ui::AXNodeData& data = GetData();
571 for (size_t i = 0; i < data.string_attributes.size(); ++i) {
572 if (data.string_attributes[i].first == attribute) {
573 *value = data.string_attributes[i].second;
574 return true;
578 return false;
581 base::string16 BrowserAccessibility::GetString16Attribute(
582 ui::AXStringAttribute attribute) const {
583 std::string value_utf8;
584 if (!GetStringAttribute(attribute, &value_utf8))
585 return base::string16();
586 return base::UTF8ToUTF16(value_utf8);
589 bool BrowserAccessibility::GetString16Attribute(
590 ui::AXStringAttribute attribute,
591 base::string16* value) const {
592 std::string value_utf8;
593 if (!GetStringAttribute(attribute, &value_utf8))
594 return false;
595 *value = base::UTF8ToUTF16(value_utf8);
596 return true;
599 bool BrowserAccessibility::HasIntListAttribute(
600 ui::AXIntListAttribute attribute) const {
601 const ui::AXNodeData& data = GetData();
602 for (size_t i = 0; i < data.intlist_attributes.size(); ++i) {
603 if (data.intlist_attributes[i].first == attribute)
604 return true;
607 return false;
610 const std::vector<int32>& BrowserAccessibility::GetIntListAttribute(
611 ui::AXIntListAttribute attribute) const {
612 const ui::AXNodeData& data = GetData();
613 CR_DEFINE_STATIC_LOCAL(std::vector<int32>, empty_vector, ());
614 for (size_t i = 0; i < data.intlist_attributes.size(); ++i) {
615 if (data.intlist_attributes[i].first == attribute)
616 return data.intlist_attributes[i].second;
619 return empty_vector;
622 bool BrowserAccessibility::GetIntListAttribute(
623 ui::AXIntListAttribute attribute,
624 std::vector<int32>* value) const {
625 const ui::AXNodeData& data = GetData();
626 for (size_t i = 0; i < data.intlist_attributes.size(); ++i) {
627 if (data.intlist_attributes[i].first == attribute) {
628 *value = data.intlist_attributes[i].second;
629 return true;
633 return false;
636 bool BrowserAccessibility::GetHtmlAttribute(
637 const char* html_attr, std::string* value) const {
638 for (size_t i = 0; i < GetHtmlAttributes().size(); ++i) {
639 const std::string& attr = GetHtmlAttributes()[i].first;
640 if (LowerCaseEqualsASCII(attr, html_attr)) {
641 *value = GetHtmlAttributes()[i].second;
642 return true;
646 return false;
649 bool BrowserAccessibility::GetHtmlAttribute(
650 const char* html_attr, base::string16* value) const {
651 std::string value_utf8;
652 if (!GetHtmlAttribute(html_attr, &value_utf8))
653 return false;
654 *value = base::UTF8ToUTF16(value_utf8);
655 return true;
658 bool BrowserAccessibility::GetAriaTristate(
659 const char* html_attr,
660 bool* is_defined,
661 bool* is_mixed) const {
662 *is_defined = false;
663 *is_mixed = false;
665 base::string16 value;
666 if (!GetHtmlAttribute(html_attr, &value) ||
667 value.empty() ||
668 EqualsASCII(value, "undefined")) {
669 return false; // Not set (and *is_defined is also false)
672 *is_defined = true;
674 if (EqualsASCII(value, "true"))
675 return true;
677 if (EqualsASCII(value, "mixed"))
678 *is_mixed = true;
680 return false; // Not set
683 bool BrowserAccessibility::HasState(ui::AXState state_enum) const {
684 return (GetState() >> state_enum) & 1;
687 bool BrowserAccessibility::IsCellOrTableHeaderRole() const {
688 return (GetRole() == ui::AX_ROLE_CELL ||
689 GetRole() == ui::AX_ROLE_COLUMN_HEADER ||
690 GetRole() == ui::AX_ROLE_ROW_HEADER);
693 bool BrowserAccessibility::IsEditableText() const {
694 // These roles don't have readonly set, but they're not editable text.
695 if (GetRole() == ui::AX_ROLE_SCROLL_AREA ||
696 GetRole() == ui::AX_ROLE_COLUMN ||
697 GetRole() == ui::AX_ROLE_TABLE_HEADER_CONTAINER) {
698 return false;
701 // Note: WebAXStateReadonly being false means it's either a text control,
702 // or contenteditable. We also check for editable text roles to cover
703 // another element that has role=textbox set on it.
704 return (!HasState(ui::AX_STATE_READ_ONLY) ||
705 GetRole() == ui::AX_ROLE_TEXT_FIELD ||
706 GetRole() == ui::AX_ROLE_TEXT_AREA);
709 bool BrowserAccessibility::IsWebAreaForPresentationalIframe() const {
710 if (GetRole() != ui::AX_ROLE_WEB_AREA &&
711 GetRole() != ui::AX_ROLE_ROOT_WEB_AREA) {
712 return false;
715 BrowserAccessibility* parent = GetParent();
716 if (!parent)
717 return false;
719 BrowserAccessibility* grandparent = parent->GetParent();
720 if (!grandparent)
721 return false;
723 return grandparent->GetRole() == ui::AX_ROLE_IFRAME_PRESENTATIONAL;
726 int BrowserAccessibility::GetStaticTextLenRecursive() const {
727 if (GetRole() == ui::AX_ROLE_STATIC_TEXT)
728 return static_cast<int>(GetStringAttribute(ui::AX_ATTR_VALUE).size());
730 int len = 0;
731 for (size_t i = 0; i < InternalChildCount(); ++i)
732 len += InternalGetChild(i)->GetStaticTextLenRecursive();
733 return len;
736 BrowserAccessibility* BrowserAccessibility::GetParentForBoundsCalculation()
737 const {
738 if (!node_ || !manager_)
739 return NULL;
740 ui::AXNode* parent = node_->parent();
741 if (parent)
742 return manager_->GetFromAXNode(parent);
744 if (!manager_->delegate())
745 return NULL;
747 return manager_->delegate()->AccessibilityGetParentFrame();
750 gfx::Rect BrowserAccessibility::ElementBoundsToLocalBounds(gfx::Rect bounds)
751 const {
752 // Walk up the parent chain. Every time we encounter a Web Area, offset
753 // based on the scroll bars and then offset based on the origin of that
754 // nested web area.
755 BrowserAccessibility* parent = GetParentForBoundsCalculation();
756 bool need_to_offset_web_area =
757 (GetRole() == ui::AX_ROLE_WEB_AREA ||
758 GetRole() == ui::AX_ROLE_ROOT_WEB_AREA);
759 while (parent) {
760 if (need_to_offset_web_area &&
761 parent->GetLocation().width() > 0 &&
762 parent->GetLocation().height() > 0) {
763 bounds.Offset(parent->GetLocation().x(), parent->GetLocation().y());
764 need_to_offset_web_area = false;
767 // On some platforms, we don't want to take the root scroll offsets
768 // into account.
769 if (parent->GetRole() == ui::AX_ROLE_ROOT_WEB_AREA &&
770 !manager()->UseRootScrollOffsetsWhenComputingBounds()) {
771 break;
774 if (parent->GetRole() == ui::AX_ROLE_WEB_AREA ||
775 parent->GetRole() == ui::AX_ROLE_ROOT_WEB_AREA) {
776 int sx = 0;
777 int sy = 0;
778 if (parent->GetIntAttribute(ui::AX_ATTR_SCROLL_X, &sx) &&
779 parent->GetIntAttribute(ui::AX_ATTR_SCROLL_Y, &sy)) {
780 bounds.Offset(-sx, -sy);
782 need_to_offset_web_area = true;
784 parent = parent->GetParentForBoundsCalculation();
787 return bounds;
790 } // namespace content