Fix accessibility objects with zero width and height.
[chromium-blink-merge.git] / content / browser / accessibility / browser_accessibility.cc
blob9d4fe101afc599be1f15caf17a87cdfc0a74103b
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 FixEmptyBounds(&bounds);
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 if (child->GetRole() != ui::AX_ROLE_INLINE_TEXT_BOX) {
233 DLOG(WARNING) << "BrowserAccessibility objects with role STATIC_TEXT " <<
234 "should have children of role INLINE_TEXT_BOX.";
235 continue;
238 std::string child_text;
239 child->GetStringAttribute(ui::AX_ATTR_VALUE, &child_text);
240 int child_len = static_cast<int>(child_text.size());
241 child_start = child_end;
242 child_end += child_len;
244 if (child_end < start)
245 continue;
247 int overlap_start = std::max(start, child_start);
248 int overlap_end = std::min(end, child_end);
250 int local_start = overlap_start - child_start;
251 int local_end = overlap_end - child_start;
253 gfx::Rect child_rect = child->GetLocation();
254 int text_direction = child->GetIntAttribute(
255 ui::AX_ATTR_TEXT_DIRECTION);
256 const std::vector<int32>& character_offsets = child->GetIntListAttribute(
257 ui::AX_ATTR_CHARACTER_OFFSETS);
258 int start_pixel_offset =
259 local_start > 0 ? character_offsets[local_start - 1] : 0;
260 int end_pixel_offset =
261 local_end > 0 ? character_offsets[local_end - 1] : 0;
263 gfx::Rect child_overlap_rect;
264 switch (text_direction) {
265 case ui::AX_TEXT_DIRECTION_NONE:
266 case ui::AX_TEXT_DIRECTION_LTR: {
267 int left = child_rect.x() + start_pixel_offset;
268 int right = child_rect.x() + end_pixel_offset;
269 child_overlap_rect = gfx::Rect(left, child_rect.y(),
270 right - left, child_rect.height());
271 break;
273 case ui::AX_TEXT_DIRECTION_RTL: {
274 int right = child_rect.right() - start_pixel_offset;
275 int left = child_rect.right() - end_pixel_offset;
276 child_overlap_rect = gfx::Rect(left, child_rect.y(),
277 right - left, child_rect.height());
278 break;
280 case ui::AX_TEXT_DIRECTION_TTB: {
281 int top = child_rect.y() + start_pixel_offset;
282 int bottom = child_rect.y() + end_pixel_offset;
283 child_overlap_rect = gfx::Rect(child_rect.x(), top,
284 child_rect.width(), bottom - top);
285 break;
287 case ui::AX_TEXT_DIRECTION_BTT: {
288 int bottom = child_rect.bottom() - start_pixel_offset;
289 int top = child_rect.bottom() - end_pixel_offset;
290 child_overlap_rect = gfx::Rect(child_rect.x(), top,
291 child_rect.width(), bottom - top);
292 break;
294 default:
295 NOTREACHED();
298 if (bounds.width() == 0 && bounds.height() == 0)
299 bounds = child_overlap_rect;
300 else
301 bounds.Union(child_overlap_rect);
304 return ElementBoundsToLocalBounds(bounds);
307 gfx::Rect BrowserAccessibility::GetGlobalBoundsForRange(int start, int len)
308 const {
309 gfx::Rect bounds = GetLocalBoundsForRange(start, len);
311 // Adjust the bounds by the top left corner of the containing view's bounds
312 // in screen coordinates.
313 bounds.Offset(manager_->GetViewBounds().OffsetFromOrigin());
315 return bounds;
318 int BrowserAccessibility::GetWordStartBoundary(
319 int start, ui::TextBoundaryDirection direction) const {
320 DCHECK_GE(start, -1);
321 // Special offset that indicates that a word boundary has not been found.
322 int word_start_not_found = GetStaticTextLenRecursive();
323 int word_start = word_start_not_found;
325 switch (GetRole()) {
326 case ui::AX_ROLE_STATIC_TEXT: {
327 int prev_word_start = word_start_not_found;
328 int child_start = 0;
329 int child_end = 0;
331 // Go through the inline text boxes.
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 greater than the requested offset has been
369 // found.
370 prev_word_start = child_start + *(iter - 1);
371 if (direction == ui::FORWARDS_DIRECTION) {
372 word_start = child_end;
373 } else if (direction == ui::BACKWARDS_DIRECTION) {
374 word_start = prev_word_start;
375 } else {
376 NOTREACHED();
379 return word_start;
382 case ui::AX_ROLE_LINE_BREAK:
383 // Words never start at a line break.
384 return word_start_not_found;
386 default:
387 // If there are no children, the word start boundary is still unknown or
388 // found previously depending on the direction.
389 if (!InternalChildCount())
390 return word_start_not_found;
392 int child_start = 0;
393 for (size_t i = 0; i < InternalChildCount(); ++i) {
394 BrowserAccessibility* child = InternalGetChild(i);
395 int child_len = child->GetStaticTextLenRecursive();
396 int child_word_start = child->GetWordStartBoundary(start, direction);
397 if (child_word_start < child_len) {
398 // We have found a possible word boundary.
399 word_start = child_start + child_word_start;
402 // Decide when to stop searching.
403 if ((word_start != word_start_not_found &&
404 direction == ui::FORWARDS_DIRECTION) ||
405 (start < child_len &&
406 direction == ui::BACKWARDS_DIRECTION)) {
407 break;
410 child_start += child_len;
411 if (start >= child_len)
412 start -= child_len;
413 else
414 start = -1;
416 return word_start;
420 BrowserAccessibility* BrowserAccessibility::BrowserAccessibilityForPoint(
421 const gfx::Point& point) {
422 // The best result found that's a child of this object.
423 BrowserAccessibility* child_result = NULL;
424 // The best result that's an indirect descendant like grandchild, etc.
425 BrowserAccessibility* descendant_result = NULL;
427 // Walk the children recursively looking for the BrowserAccessibility that
428 // most tightly encloses the specified point. Walk backwards so that in
429 // the absence of any other information, we assume the object that occurs
430 // later in the tree is on top of one that comes before it.
431 for (int i = static_cast<int>(PlatformChildCount()) - 1; i >= 0; --i) {
432 BrowserAccessibility* child = PlatformGetChild(i);
434 // Skip table columns because cells are only contained in rows,
435 // not columns.
436 if (child->GetRole() == ui::AX_ROLE_COLUMN)
437 continue;
439 if (child->GetGlobalBoundsRect().Contains(point)) {
440 BrowserAccessibility* result = child->BrowserAccessibilityForPoint(point);
441 if (result == child && !child_result)
442 child_result = result;
443 if (result != child && !descendant_result)
444 descendant_result = result;
447 if (child_result && descendant_result)
448 break;
451 // Explanation of logic: it's possible that this point overlaps more than
452 // one child of this object. If so, as a heuristic we prefer if the point
453 // overlaps a descendant of one of the two children and not the other.
454 // As an example, suppose you have two rows of buttons - the buttons don't
455 // overlap, but the rows do. Without this heuristic, we'd greedily only
456 // consider one of the containers.
457 if (descendant_result)
458 return descendant_result;
459 if (child_result)
460 return child_result;
462 return this;
465 void BrowserAccessibility::Destroy() {
466 // Allow the object to fire a TextRemoved notification.
467 manager_->NotifyAccessibilityEvent(ui::AX_EVENT_HIDE, this);
468 node_ = NULL;
469 manager_ = NULL;
471 NativeReleaseReference();
474 void BrowserAccessibility::NativeReleaseReference() {
475 delete this;
478 bool BrowserAccessibility::HasBoolAttribute(
479 ui::AXBoolAttribute attribute) const {
480 return GetData().HasBoolAttribute(attribute);
483 bool BrowserAccessibility::GetBoolAttribute(
484 ui::AXBoolAttribute attribute) const {
485 return GetData().GetBoolAttribute(attribute);
488 bool BrowserAccessibility::GetBoolAttribute(
489 ui::AXBoolAttribute attribute, bool* value) const {
490 return GetData().GetBoolAttribute(attribute, value);
493 bool BrowserAccessibility::HasFloatAttribute(
494 ui::AXFloatAttribute attribute) const {
495 return GetData().HasFloatAttribute(attribute);
498 float BrowserAccessibility::GetFloatAttribute(
499 ui::AXFloatAttribute attribute) const {
500 return GetData().GetFloatAttribute(attribute);
503 bool BrowserAccessibility::GetFloatAttribute(
504 ui::AXFloatAttribute attribute, float* value) const {
505 return GetData().GetFloatAttribute(attribute, value);
508 bool BrowserAccessibility::HasIntAttribute(
509 ui::AXIntAttribute attribute) const {
510 return GetData().HasIntAttribute(attribute);
513 int BrowserAccessibility::GetIntAttribute(ui::AXIntAttribute attribute) const {
514 return GetData().GetIntAttribute(attribute);
517 bool BrowserAccessibility::GetIntAttribute(
518 ui::AXIntAttribute attribute, int* value) const {
519 return GetData().GetIntAttribute(attribute, value);
522 bool BrowserAccessibility::HasStringAttribute(
523 ui::AXStringAttribute attribute) const {
524 return GetData().HasStringAttribute(attribute);
527 const std::string& BrowserAccessibility::GetStringAttribute(
528 ui::AXStringAttribute attribute) const {
529 return GetData().GetStringAttribute(attribute);
532 bool BrowserAccessibility::GetStringAttribute(
533 ui::AXStringAttribute attribute, std::string* value) const {
534 return GetData().GetStringAttribute(attribute, value);
537 base::string16 BrowserAccessibility::GetString16Attribute(
538 ui::AXStringAttribute attribute) const {
539 return GetData().GetString16Attribute(attribute);
542 bool BrowserAccessibility::GetString16Attribute(
543 ui::AXStringAttribute attribute,
544 base::string16* value) const {
545 return GetData().GetString16Attribute(attribute, value);
548 bool BrowserAccessibility::HasIntListAttribute(
549 ui::AXIntListAttribute attribute) const {
550 return GetData().HasIntListAttribute(attribute);
553 const std::vector<int32>& BrowserAccessibility::GetIntListAttribute(
554 ui::AXIntListAttribute attribute) const {
555 return GetData().GetIntListAttribute(attribute);
558 bool BrowserAccessibility::GetIntListAttribute(
559 ui::AXIntListAttribute attribute,
560 std::vector<int32>* value) const {
561 return GetData().GetIntListAttribute(attribute, value);
564 bool BrowserAccessibility::GetHtmlAttribute(
565 const char* html_attr, std::string* value) const {
566 return GetData().GetHtmlAttribute(html_attr, value);
569 bool BrowserAccessibility::GetHtmlAttribute(
570 const char* html_attr, base::string16* value) const {
571 return GetData().GetHtmlAttribute(html_attr, value);
574 bool BrowserAccessibility::GetAriaTristate(
575 const char* html_attr,
576 bool* is_defined,
577 bool* is_mixed) const {
578 *is_defined = false;
579 *is_mixed = false;
581 base::string16 value;
582 if (!GetHtmlAttribute(html_attr, &value) ||
583 value.empty() ||
584 base::EqualsASCII(value, "undefined")) {
585 return false; // Not set (and *is_defined is also false)
588 *is_defined = true;
590 if (base::EqualsASCII(value, "true"))
591 return true;
593 if (base::EqualsASCII(value, "mixed"))
594 *is_mixed = true;
596 return false; // Not set
599 bool BrowserAccessibility::HasState(ui::AXState state_enum) const {
600 return (GetState() >> state_enum) & 1;
603 bool BrowserAccessibility::IsCellOrTableHeaderRole() const {
604 return (GetRole() == ui::AX_ROLE_CELL ||
605 GetRole() == ui::AX_ROLE_COLUMN_HEADER ||
606 GetRole() == ui::AX_ROLE_ROW_HEADER);
609 bool BrowserAccessibility::IsEditableText() const {
610 // These roles don't have readonly set, but they're not editable text.
611 if (GetRole() == ui::AX_ROLE_SCROLL_AREA ||
612 GetRole() == ui::AX_ROLE_COLUMN ||
613 GetRole() == ui::AX_ROLE_TABLE_HEADER_CONTAINER) {
614 return false;
617 // Note: WebAXStateReadonly being false means it's either a text control,
618 // or contenteditable. We also check for editable text roles to cover
619 // another element that has role=textbox set on it.
620 return (!HasState(ui::AX_STATE_READ_ONLY) ||
621 GetRole() == ui::AX_ROLE_TEXT_FIELD);
624 bool BrowserAccessibility::IsWebAreaForPresentationalIframe() const {
625 if (GetRole() != ui::AX_ROLE_WEB_AREA &&
626 GetRole() != ui::AX_ROLE_ROOT_WEB_AREA) {
627 return false;
630 BrowserAccessibility* parent = GetParent();
631 if (!parent)
632 return false;
634 BrowserAccessibility* grandparent = parent->GetParent();
635 if (!grandparent)
636 return false;
638 return grandparent->GetRole() == ui::AX_ROLE_IFRAME_PRESENTATIONAL;
641 bool BrowserAccessibility::IsControl() const {
642 switch (GetRole()) {
643 case ui::AX_ROLE_BUTTON:
644 case ui::AX_ROLE_BUTTON_DROP_DOWN:
645 case ui::AX_ROLE_CHECK_BOX:
646 case ui::AX_ROLE_COLOR_WELL:
647 case ui::AX_ROLE_COMBO_BOX:
648 case ui::AX_ROLE_DISCLOSURE_TRIANGLE:
649 case ui::AX_ROLE_LIST_BOX:
650 case ui::AX_ROLE_MENU_BAR:
651 case ui::AX_ROLE_MENU_BUTTON:
652 case ui::AX_ROLE_MENU_ITEM:
653 case ui::AX_ROLE_MENU_ITEM_CHECK_BOX:
654 case ui::AX_ROLE_MENU_ITEM_RADIO:
655 case ui::AX_ROLE_MENU:
656 case ui::AX_ROLE_POP_UP_BUTTON:
657 case ui::AX_ROLE_RADIO_BUTTON:
658 case ui::AX_ROLE_SCROLL_BAR:
659 case ui::AX_ROLE_SEARCH_BOX:
660 case ui::AX_ROLE_SLIDER:
661 case ui::AX_ROLE_SPIN_BUTTON:
662 case ui::AX_ROLE_SWITCH:
663 case ui::AX_ROLE_TAB:
664 case ui::AX_ROLE_TEXT_FIELD:
665 case ui::AX_ROLE_TOGGLE_BUTTON:
666 case ui::AX_ROLE_TREE:
667 return true;
668 default:
669 return false;
673 int BrowserAccessibility::GetStaticTextLenRecursive() const {
674 if (GetRole() == ui::AX_ROLE_STATIC_TEXT ||
675 GetRole() == ui::AX_ROLE_LINE_BREAK) {
676 return static_cast<int>(GetStringAttribute(ui::AX_ATTR_VALUE).size());
679 int len = 0;
680 for (size_t i = 0; i < InternalChildCount(); ++i)
681 len += InternalGetChild(i)->GetStaticTextLenRecursive();
682 return len;
685 BrowserAccessibility* BrowserAccessibility::GetParentForBoundsCalculation()
686 const {
687 if (!node_ || !manager_)
688 return NULL;
689 ui::AXNode* parent = node_->parent();
690 if (parent)
691 return manager_->GetFromAXNode(parent);
693 if (!manager_->delegate())
694 return NULL;
696 return manager_->delegate()->AccessibilityGetParentFrame();
699 void BrowserAccessibility::FixEmptyBounds(gfx::Rect* bounds) const
701 if (bounds->width() > 0 && bounds->height() > 0)
702 return;
704 for (size_t i = 0; i < InternalChildCount(); ++i) {
705 // Compute the bounds of each child - this calls FixEmptyBounds
706 // recursively if necessary.
707 BrowserAccessibility* child = InternalGetChild(i);
708 gfx::Rect child_bounds = child->GetLocalBoundsRect();
710 // Ignore children that don't have valid bounds themselves.
711 if (child_bounds.width() == 0 || child_bounds.height() == 0)
712 continue;
714 // For the first valid child, just set the bounds to that child's bounds.
715 if (bounds->width() == 0 || bounds->height() == 0) {
716 *bounds = child_bounds;
717 continue;
720 // Union each additional child's bounds.
721 bounds->Union(child_bounds);
725 gfx::Rect BrowserAccessibility::ElementBoundsToLocalBounds(gfx::Rect bounds)
726 const {
727 // Walk up the parent chain. Every time we encounter a Web Area, offset
728 // based on the scroll bars and then offset based on the origin of that
729 // nested web area.
730 BrowserAccessibility* parent = GetParentForBoundsCalculation();
731 bool need_to_offset_web_area =
732 (GetRole() == ui::AX_ROLE_WEB_AREA ||
733 GetRole() == ui::AX_ROLE_ROOT_WEB_AREA);
734 while (parent) {
735 if (need_to_offset_web_area &&
736 parent->GetLocation().width() > 0 &&
737 parent->GetLocation().height() > 0) {
738 bounds.Offset(parent->GetLocation().x(), parent->GetLocation().y());
739 need_to_offset_web_area = false;
742 // On some platforms, we don't want to take the root scroll offsets
743 // into account.
744 if (parent->GetRole() == ui::AX_ROLE_ROOT_WEB_AREA &&
745 !manager()->UseRootScrollOffsetsWhenComputingBounds()) {
746 break;
749 if (parent->GetRole() == ui::AX_ROLE_WEB_AREA ||
750 parent->GetRole() == ui::AX_ROLE_ROOT_WEB_AREA) {
751 int sx = 0;
752 int sy = 0;
753 if (parent->GetIntAttribute(ui::AX_ATTR_SCROLL_X, &sx) &&
754 parent->GetIntAttribute(ui::AX_ATTR_SCROLL_Y, &sy)) {
755 bounds.Offset(-sx, -sy);
757 need_to_offset_web_area = true;
759 parent = parent->GetParentForBoundsCalculation();
762 return bounds;
765 } // namespace content