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"
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"
19 #if !defined(OS_MACOSX) && \
22 // We have subclassess of BrowserAccessibility on Mac and Win. For any other
23 // platform, instantiate the base class.
25 BrowserAccessibility
* BrowserAccessibility::Create() {
26 return new BrowserAccessibility();
30 BrowserAccessibility::BrowserAccessibility()
35 BrowserAccessibility::~BrowserAccessibility() {
38 void BrowserAccessibility::Init(BrowserAccessibilityManager
* manager
,
44 bool BrowserAccessibility::PlatformIsLeaf() const {
45 if (InternalChildCount() == 0)
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.
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
:
62 uint32
BrowserAccessibility::PlatformChildCount() const {
63 return PlatformIsLeaf() ? 0 : InternalChildCount();
66 bool BrowserAccessibility::IsNative() const {
70 bool BrowserAccessibility::IsDescendantOf(
71 BrowserAccessibility
* ancestor
) {
72 if (this == ancestor
) {
74 } else if (GetParent()) {
75 return GetParent()->IsDescendantOf(ancestor
);
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());
90 result
= child_manager
->GetRoot();
96 bool BrowserAccessibility::PlatformIsChildOfLeaf() const {
97 BrowserAccessibility
* ancestor
= GetParent();
99 if (ancestor
->PlatformIsLeaf())
101 ancestor
= ancestor
->GetParent();
107 BrowserAccessibility
* BrowserAccessibility::GetPreviousSibling() {
108 if (GetParent() && GetIndexInParent() > 0)
109 return GetParent()->InternalGetChild(GetIndexInParent() - 1);
114 BrowserAccessibility
* BrowserAccessibility::GetNextSibling() {
116 GetIndexInParent() >= 0 &&
117 GetIndexInParent() < static_cast<int>(
118 GetParent()->InternalChildCount() - 1)) {
119 return GetParent()->InternalGetChild(GetIndexInParent() + 1);
125 uint32
BrowserAccessibility::InternalChildCount() const {
126 if (!node_
|| !manager_
)
128 return static_cast<uint32
>(node_
->child_count());
131 BrowserAccessibility
* BrowserAccessibility::InternalGetChild(
132 uint32 child_index
) const {
133 if (!node_
|| !manager_
)
135 return manager_
->GetFromAXNode(node_
->ChildAtIndex(child_index
));
138 BrowserAccessibility
* BrowserAccessibility::GetParent() const {
139 if (!node_
|| !manager_
)
141 ui::AXNode
* parent
= node_
->parent();
143 return manager_
->GetFromAXNode(parent
);
145 if (!manager_
->delegate())
148 BrowserAccessibility
* host_node
=
149 manager_
->delegate()->AccessibilityGetParentFrame();
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
, ());
167 return node_
->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());
204 gfx::Rect
BrowserAccessibility::GetLocalBoundsForRange(int start
, int len
)
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.
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
);
221 return ElementBoundsToLocalBounds(bounds
);
224 int end
= start
+ len
;
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.";
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
)
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());
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());
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
);
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
);
297 if (bounds
.width() == 0 && bounds
.height() == 0)
298 bounds
= child_overlap_rect
;
300 bounds
.Union(child_overlap_rect
);
303 return ElementBoundsToLocalBounds(bounds
);
306 gfx::Rect
BrowserAccessibility::GetGlobalBoundsForRange(int start
, int len
)
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());
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
;
325 case ui::AX_ROLE_STATIC_TEXT
: {
326 int prev_word_start
= word_start_not_found
;
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(
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
;
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
;
359 word_start
= child_start
+ *(iter
- 1);
367 // No word start that is greater than the requested offset has been
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
;
381 case ui::AX_ROLE_LINE_BREAK
:
382 // Words never start at a line break.
383 return word_start_not_found
;
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
;
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
)) {
409 child_start
+= child_len
;
410 if (start
>= child_len
)
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,
435 if (child
->GetRole() == ui::AX_ROLE_COLUMN
)
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
)
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
;
464 void BrowserAccessibility::Destroy() {
465 // Allow the object to fire a TextRemoved notification.
466 manager_
->NotifyAccessibilityEvent(ui::AX_EVENT_HIDE
, this);
470 NativeReleaseReference();
473 void BrowserAccessibility::NativeReleaseReference() {
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
)
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
;
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
;
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
)
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
;
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
;
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
)
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
;
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
;
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
)
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
;
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
;
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
))
632 *value
= base::UTF8ToUTF16(value_utf8
);
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
)
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
;
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
;
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
;
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
))
691 *value
= base::UTF8ToUTF16(value_utf8
);
695 bool BrowserAccessibility::GetAriaTristate(
696 const char* html_attr
,
698 bool* is_mixed
) const {
702 base::string16 value
;
703 if (!GetHtmlAttribute(html_attr
, &value
) ||
705 EqualsASCII(value
, "undefined")) {
706 return false; // Not set (and *is_defined is also false)
711 if (EqualsASCII(value
, "true"))
714 if (EqualsASCII(value
, "mixed"))
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
) {
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
) {
751 BrowserAccessibility
* parent
= GetParent();
755 BrowserAccessibility
* grandparent
= parent
->GetParent();
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());
769 for (size_t i
= 0; i
< InternalChildCount(); ++i
)
770 len
+= InternalGetChild(i
)->GetStaticTextLenRecursive();
774 BrowserAccessibility
* BrowserAccessibility::GetParentForBoundsCalculation()
776 if (!node_
|| !manager_
)
778 ui::AXNode
* parent
= node_
->parent();
780 return manager_
->GetFromAXNode(parent
);
782 if (!manager_
->delegate())
785 return manager_
->delegate()->AccessibilityGetParentFrame();
788 gfx::Rect
BrowserAccessibility::ElementBoundsToLocalBounds(gfx::Rect bounds
)
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
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
);
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
807 if (parent
->GetRole() == ui::AX_ROLE_ROOT_WEB_AREA
&&
808 !manager()->UseRootScrollOffsetsWhenComputingBounds()) {
812 if (parent
->GetRole() == ui::AX_ROLE_WEB_AREA
||
813 parent
->GetRole() == ui::AX_ROLE_ROOT_WEB_AREA
) {
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();
828 } // namespace content