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_WIN) && \
20 !defined(OS_MACOSX) && \
21 !defined(OS_ANDROID) && \
22 !(defined(OS_LINUX) && !defined(OS_CHROMEOS) && defined(USE_X11))
23 // We have subclassess of BrowserAccessibility on some platforms.
24 // On other platforms, instantiate the base class.
26 BrowserAccessibility
* BrowserAccessibility::Create() {
27 return new BrowserAccessibility();
31 BrowserAccessibility::BrowserAccessibility()
36 BrowserAccessibility::~BrowserAccessibility() {
39 void BrowserAccessibility::Init(BrowserAccessibilityManager
* manager
,
45 bool BrowserAccessibility::PlatformIsLeaf() const {
46 if (InternalChildCount() == 0)
49 // All of these roles may have children that we use as internal
50 // implementation details, but we want to expose them as leaves
51 // to platform accessibility APIs.
53 case ui::AX_ROLE_COMBO_BOX
:
54 case ui::AX_ROLE_LINE_BREAK
:
55 case ui::AX_ROLE_SLIDER
:
56 case ui::AX_ROLE_STATIC_TEXT
:
57 case ui::AX_ROLE_TEXT_FIELD
:
64 uint32
BrowserAccessibility::PlatformChildCount() const {
65 if (HasIntAttribute(ui::AX_ATTR_CHILD_TREE_ID
)) {
66 BrowserAccessibilityManager
* child_manager
=
67 BrowserAccessibilityManager::FromID(
68 GetIntAttribute(ui::AX_ATTR_CHILD_TREE_ID
));
75 return PlatformIsLeaf() ? 0 : InternalChildCount();
78 bool BrowserAccessibility::IsNative() const {
82 bool BrowserAccessibility::IsDescendantOf(
83 const BrowserAccessibility
* ancestor
) const {
84 if (this == ancestor
) {
86 } else if (GetParent()) {
87 return GetParent()->IsDescendantOf(ancestor
);
93 bool BrowserAccessibility::IsTextOnlyObject() const {
94 return GetRole() == ui::AX_ROLE_STATIC_TEXT
||
95 GetRole() == ui::AX_ROLE_LINE_BREAK
;
98 BrowserAccessibility
* BrowserAccessibility::PlatformGetChild(
99 uint32 child_index
) const {
100 DCHECK(child_index
< PlatformChildCount());
101 BrowserAccessibility
* result
= nullptr;
103 if (HasIntAttribute(ui::AX_ATTR_CHILD_TREE_ID
)) {
104 BrowserAccessibilityManager
* child_manager
=
105 BrowserAccessibilityManager::FromID(
106 GetIntAttribute(ui::AX_ATTR_CHILD_TREE_ID
));
108 result
= child_manager
->GetRoot();
110 result
= InternalGetChild(child_index
);
116 bool BrowserAccessibility::PlatformIsChildOfLeaf() const {
117 BrowserAccessibility
* ancestor
= GetParent();
119 if (ancestor
->PlatformIsLeaf())
121 ancestor
= ancestor
->GetParent();
127 BrowserAccessibility
* BrowserAccessibility::GetPreviousSibling() const {
128 if (GetParent() && GetIndexInParent() > 0)
129 return GetParent()->InternalGetChild(GetIndexInParent() - 1);
134 BrowserAccessibility
* BrowserAccessibility::GetNextSibling() const {
136 GetIndexInParent() >= 0 &&
137 GetIndexInParent() < static_cast<int>(
138 GetParent()->InternalChildCount() - 1)) {
139 return GetParent()->InternalGetChild(GetIndexInParent() + 1);
145 BrowserAccessibility
* BrowserAccessibility::PlatformDeepestFirstChild() const {
146 if (!PlatformChildCount())
149 auto deepest_child
= PlatformGetChild(0);
150 while (deepest_child
->PlatformChildCount())
151 deepest_child
= deepest_child
->PlatformGetChild(0);
153 return deepest_child
;
156 BrowserAccessibility
* BrowserAccessibility::PlatformDeepestLastChild() const {
157 if (!PlatformChildCount())
160 auto deepest_child
= PlatformGetChild(PlatformChildCount() - 1);
161 while (deepest_child
->PlatformChildCount())
162 deepest_child
= deepest_child
->PlatformGetChild(PlatformChildCount() - 1);
164 return deepest_child
;
167 uint32
BrowserAccessibility::InternalChildCount() const {
168 if (!node_
|| !manager_
)
170 return static_cast<uint32
>(node_
->child_count());
173 BrowserAccessibility
* BrowserAccessibility::InternalGetChild(
174 uint32 child_index
) const {
175 if (!node_
|| !manager_
)
177 return manager_
->GetFromAXNode(node_
->ChildAtIndex(child_index
));
180 BrowserAccessibility
* BrowserAccessibility::GetParent() const {
181 if (!node_
|| !manager_
)
183 ui::AXNode
* parent
= node_
->parent();
185 return manager_
->GetFromAXNode(parent
);
187 return manager_
->GetParentNodeFromParentTree();
190 int32
BrowserAccessibility::GetIndexInParent() const {
191 return node_
? node_
->index_in_parent() : -1;
194 int32
BrowserAccessibility::GetId() const {
195 return node_
? node_
->id() : -1;
198 const ui::AXNodeData
& BrowserAccessibility::GetData() const {
199 CR_DEFINE_STATIC_LOCAL(ui::AXNodeData
, empty_data
, ());
201 return node_
->data();
206 gfx::Rect
BrowserAccessibility::GetLocation() const {
207 return GetData().location
;
210 int32
BrowserAccessibility::GetRole() const {
211 return GetData().role
;
214 int32
BrowserAccessibility::GetState() const {
215 return GetData().state
;
218 const BrowserAccessibility::HtmlAttributes
&
219 BrowserAccessibility::GetHtmlAttributes() const {
220 return GetData().html_attributes
;
223 gfx::Rect
BrowserAccessibility::GetLocalBoundsRect() const {
224 gfx::Rect bounds
= GetLocation();
225 FixEmptyBounds(&bounds
);
226 return ElementBoundsToLocalBounds(bounds
);
229 gfx::Rect
BrowserAccessibility::GetGlobalBoundsRect() const {
230 gfx::Rect bounds
= GetLocalBoundsRect();
232 // Adjust the bounds by the top left corner of the containing view's bounds
233 // in screen coordinates.
234 bounds
.Offset(manager_
->GetViewBounds().OffsetFromOrigin());
239 gfx::Rect
BrowserAccessibility::GetLocalBoundsForRange(int start
, int len
)
241 if (GetRole() != ui::AX_ROLE_STATIC_TEXT
) {
242 // Apply recursively to all static text descendants. For example, if
243 // you call it on a div with two text node children, it just calls
244 // GetLocalBoundsForRange on each of the two children (adjusting
245 // |start| for each one) and unions the resulting rects.
247 for (size_t i
= 0; i
< InternalChildCount(); ++i
) {
248 BrowserAccessibility
* child
= InternalGetChild(i
);
249 int child_len
= child
->GetStaticTextLenRecursive();
250 if (start
< child_len
&& start
+ len
> 0) {
251 gfx::Rect child_rect
= child
->GetLocalBoundsForRange(start
, len
);
252 bounds
.Union(child_rect
);
256 return ElementBoundsToLocalBounds(bounds
);
259 int end
= start
+ len
;
264 for (size_t i
= 0; i
< InternalChildCount() && child_end
< start
+ len
; ++i
) {
265 BrowserAccessibility
* child
= InternalGetChild(i
);
266 if (child
->GetRole() != ui::AX_ROLE_INLINE_TEXT_BOX
) {
267 DLOG(WARNING
) << "BrowserAccessibility objects with role STATIC_TEXT " <<
268 "should have children of role INLINE_TEXT_BOX.";
272 std::string child_text
;
273 child
->GetStringAttribute(ui::AX_ATTR_VALUE
, &child_text
);
274 int child_len
= static_cast<int>(child_text
.size());
275 child_start
= child_end
;
276 child_end
+= child_len
;
278 if (child_end
< start
)
281 int overlap_start
= std::max(start
, child_start
);
282 int overlap_end
= std::min(end
, child_end
);
284 int local_start
= overlap_start
- child_start
;
285 int local_end
= overlap_end
- child_start
;
287 gfx::Rect child_rect
= child
->GetLocation();
288 int text_direction
= child
->GetIntAttribute(
289 ui::AX_ATTR_TEXT_DIRECTION
);
290 const std::vector
<int32
>& character_offsets
= child
->GetIntListAttribute(
291 ui::AX_ATTR_CHARACTER_OFFSETS
);
292 int start_pixel_offset
=
293 local_start
> 0 ? character_offsets
[local_start
- 1] : 0;
294 int end_pixel_offset
=
295 local_end
> 0 ? character_offsets
[local_end
- 1] : 0;
297 gfx::Rect child_overlap_rect
;
298 switch (text_direction
) {
299 case ui::AX_TEXT_DIRECTION_NONE
:
300 case ui::AX_TEXT_DIRECTION_LTR
: {
301 int left
= child_rect
.x() + start_pixel_offset
;
302 int right
= child_rect
.x() + end_pixel_offset
;
303 child_overlap_rect
= gfx::Rect(left
, child_rect
.y(),
304 right
- left
, child_rect
.height());
307 case ui::AX_TEXT_DIRECTION_RTL
: {
308 int right
= child_rect
.right() - start_pixel_offset
;
309 int left
= child_rect
.right() - end_pixel_offset
;
310 child_overlap_rect
= gfx::Rect(left
, child_rect
.y(),
311 right
- left
, child_rect
.height());
314 case ui::AX_TEXT_DIRECTION_TTB
: {
315 int top
= child_rect
.y() + start_pixel_offset
;
316 int bottom
= child_rect
.y() + end_pixel_offset
;
317 child_overlap_rect
= gfx::Rect(child_rect
.x(), top
,
318 child_rect
.width(), bottom
- top
);
321 case ui::AX_TEXT_DIRECTION_BTT
: {
322 int bottom
= child_rect
.bottom() - start_pixel_offset
;
323 int top
= child_rect
.bottom() - end_pixel_offset
;
324 child_overlap_rect
= gfx::Rect(child_rect
.x(), top
,
325 child_rect
.width(), bottom
- top
);
332 if (bounds
.width() == 0 && bounds
.height() == 0)
333 bounds
= child_overlap_rect
;
335 bounds
.Union(child_overlap_rect
);
338 return ElementBoundsToLocalBounds(bounds
);
341 gfx::Rect
BrowserAccessibility::GetGlobalBoundsForRange(int start
, int len
)
343 gfx::Rect bounds
= GetLocalBoundsForRange(start
, len
);
345 // Adjust the bounds by the top left corner of the containing view's bounds
346 // in screen coordinates.
347 bounds
.Offset(manager_
->GetViewBounds().OffsetFromOrigin());
352 int BrowserAccessibility::GetWordStartBoundary(
353 int start
, ui::TextBoundaryDirection direction
) const {
354 DCHECK_GE(start
, -1);
355 // Special offset that indicates that a word boundary has not been found.
356 int word_start_not_found
= GetStaticTextLenRecursive();
357 int word_start
= word_start_not_found
;
360 case ui::AX_ROLE_STATIC_TEXT
: {
361 int prev_word_start
= word_start_not_found
;
365 // Go through the inline text boxes.
366 for (size_t i
= 0; i
< InternalChildCount(); ++i
) {
367 // The next child starts where the previous one ended.
368 child_start
= child_end
;
369 BrowserAccessibility
* child
= InternalGetChild(i
);
370 DCHECK_EQ(child
->GetRole(), ui::AX_ROLE_INLINE_TEXT_BOX
);
371 const std::string
& child_text
= child
->GetStringAttribute(
373 int child_len
= static_cast<int>(child_text
.size());
374 child_end
+= child_len
; // End is one past the last character.
376 const std::vector
<int32
>& word_starts
= child
->GetIntListAttribute(
377 ui::AX_ATTR_WORD_STARTS
);
378 if (word_starts
.empty()) {
379 word_start
= child_end
;
383 int local_start
= start
- child_start
;
384 std::vector
<int32
>::const_iterator iter
= std::upper_bound(
385 word_starts
.begin(), word_starts
.end(), local_start
);
386 if (iter
!= word_starts
.end()) {
387 if (direction
== ui::FORWARDS_DIRECTION
) {
388 word_start
= child_start
+ *iter
;
389 } else if (direction
== ui::BACKWARDS_DIRECTION
) {
390 if (iter
== word_starts
.begin()) {
391 // Return the position of the last word in the previous child.
392 word_start
= prev_word_start
;
394 word_start
= child_start
+ *(iter
- 1);
402 // No word start that is greater than the requested offset has been
404 prev_word_start
= child_start
+ *(iter
- 1);
405 if (direction
== ui::FORWARDS_DIRECTION
) {
406 word_start
= child_end
;
407 } else if (direction
== ui::BACKWARDS_DIRECTION
) {
408 word_start
= prev_word_start
;
416 case ui::AX_ROLE_LINE_BREAK
:
417 // Words never start at a line break.
418 return word_start_not_found
;
421 // If there are no children, the word start boundary is still unknown or
422 // found previously depending on the direction.
423 if (!InternalChildCount())
424 return word_start_not_found
;
427 for (size_t i
= 0; i
< InternalChildCount(); ++i
) {
428 BrowserAccessibility
* child
= InternalGetChild(i
);
429 int child_len
= child
->GetStaticTextLenRecursive();
430 int child_word_start
= child
->GetWordStartBoundary(start
, direction
);
431 if (child_word_start
< child_len
) {
432 // We have found a possible word boundary.
433 word_start
= child_start
+ child_word_start
;
436 // Decide when to stop searching.
437 if ((word_start
!= word_start_not_found
&&
438 direction
== ui::FORWARDS_DIRECTION
) ||
439 (start
< child_len
&&
440 direction
== ui::BACKWARDS_DIRECTION
)) {
444 child_start
+= child_len
;
445 if (start
>= child_len
)
454 BrowserAccessibility
* BrowserAccessibility::BrowserAccessibilityForPoint(
455 const gfx::Point
& point
) {
456 // The best result found that's a child of this object.
457 BrowserAccessibility
* child_result
= NULL
;
458 // The best result that's an indirect descendant like grandchild, etc.
459 BrowserAccessibility
* descendant_result
= NULL
;
461 // Walk the children recursively looking for the BrowserAccessibility that
462 // most tightly encloses the specified point. Walk backwards so that in
463 // the absence of any other information, we assume the object that occurs
464 // later in the tree is on top of one that comes before it.
465 for (int i
= static_cast<int>(PlatformChildCount()) - 1; i
>= 0; --i
) {
466 BrowserAccessibility
* child
= PlatformGetChild(i
);
468 // Skip table columns because cells are only contained in rows,
470 if (child
->GetRole() == ui::AX_ROLE_COLUMN
)
473 if (child
->GetGlobalBoundsRect().Contains(point
)) {
474 BrowserAccessibility
* result
= child
->BrowserAccessibilityForPoint(point
);
475 if (result
== child
&& !child_result
)
476 child_result
= result
;
477 if (result
!= child
&& !descendant_result
)
478 descendant_result
= result
;
481 if (child_result
&& descendant_result
)
485 // Explanation of logic: it's possible that this point overlaps more than
486 // one child of this object. If so, as a heuristic we prefer if the point
487 // overlaps a descendant of one of the two children and not the other.
488 // As an example, suppose you have two rows of buttons - the buttons don't
489 // overlap, but the rows do. Without this heuristic, we'd greedily only
490 // consider one of the containers.
491 if (descendant_result
)
492 return descendant_result
;
499 void BrowserAccessibility::Destroy() {
500 // Allow the object to fire a TextRemoved notification.
501 manager_
->NotifyAccessibilityEvent(ui::AX_EVENT_HIDE
, this);
505 NativeReleaseReference();
508 void BrowserAccessibility::NativeReleaseReference() {
512 bool BrowserAccessibility::HasBoolAttribute(
513 ui::AXBoolAttribute attribute
) const {
514 return GetData().HasBoolAttribute(attribute
);
517 bool BrowserAccessibility::GetBoolAttribute(
518 ui::AXBoolAttribute attribute
) const {
519 return GetData().GetBoolAttribute(attribute
);
522 bool BrowserAccessibility::GetBoolAttribute(
523 ui::AXBoolAttribute attribute
, bool* value
) const {
524 return GetData().GetBoolAttribute(attribute
, value
);
527 bool BrowserAccessibility::HasFloatAttribute(
528 ui::AXFloatAttribute attribute
) const {
529 return GetData().HasFloatAttribute(attribute
);
532 float BrowserAccessibility::GetFloatAttribute(
533 ui::AXFloatAttribute attribute
) const {
534 return GetData().GetFloatAttribute(attribute
);
537 bool BrowserAccessibility::GetFloatAttribute(
538 ui::AXFloatAttribute attribute
, float* value
) const {
539 return GetData().GetFloatAttribute(attribute
, value
);
542 bool BrowserAccessibility::HasIntAttribute(
543 ui::AXIntAttribute attribute
) const {
544 return GetData().HasIntAttribute(attribute
);
547 int BrowserAccessibility::GetIntAttribute(ui::AXIntAttribute attribute
) const {
548 return GetData().GetIntAttribute(attribute
);
551 bool BrowserAccessibility::GetIntAttribute(
552 ui::AXIntAttribute attribute
, int* value
) const {
553 return GetData().GetIntAttribute(attribute
, value
);
556 bool BrowserAccessibility::HasStringAttribute(
557 ui::AXStringAttribute attribute
) const {
558 return GetData().HasStringAttribute(attribute
);
561 const std::string
& BrowserAccessibility::GetStringAttribute(
562 ui::AXStringAttribute attribute
) const {
563 return GetData().GetStringAttribute(attribute
);
566 bool BrowserAccessibility::GetStringAttribute(
567 ui::AXStringAttribute attribute
, std::string
* value
) const {
568 return GetData().GetStringAttribute(attribute
, value
);
571 base::string16
BrowserAccessibility::GetString16Attribute(
572 ui::AXStringAttribute attribute
) const {
573 return GetData().GetString16Attribute(attribute
);
576 bool BrowserAccessibility::GetString16Attribute(
577 ui::AXStringAttribute attribute
,
578 base::string16
* value
) const {
579 return GetData().GetString16Attribute(attribute
, value
);
582 bool BrowserAccessibility::HasIntListAttribute(
583 ui::AXIntListAttribute attribute
) const {
584 return GetData().HasIntListAttribute(attribute
);
587 const std::vector
<int32
>& BrowserAccessibility::GetIntListAttribute(
588 ui::AXIntListAttribute attribute
) const {
589 return GetData().GetIntListAttribute(attribute
);
592 bool BrowserAccessibility::GetIntListAttribute(
593 ui::AXIntListAttribute attribute
,
594 std::vector
<int32
>* value
) const {
595 return GetData().GetIntListAttribute(attribute
, value
);
598 bool BrowserAccessibility::GetHtmlAttribute(
599 const char* html_attr
, std::string
* value
) const {
600 return GetData().GetHtmlAttribute(html_attr
, value
);
603 bool BrowserAccessibility::GetHtmlAttribute(
604 const char* html_attr
, base::string16
* value
) const {
605 return GetData().GetHtmlAttribute(html_attr
, value
);
608 bool BrowserAccessibility::GetAriaTristate(
609 const char* html_attr
,
611 bool* is_mixed
) const {
615 base::string16 value
;
616 if (!GetHtmlAttribute(html_attr
, &value
) ||
618 base::EqualsASCII(value
, "undefined")) {
619 return false; // Not set (and *is_defined is also false)
624 if (base::EqualsASCII(value
, "true"))
627 if (base::EqualsASCII(value
, "mixed"))
630 return false; // Not set
633 bool BrowserAccessibility::HasState(ui::AXState state_enum
) const {
634 return (GetState() >> state_enum
) & 1;
637 bool BrowserAccessibility::IsCellOrTableHeaderRole() const {
638 return (GetRole() == ui::AX_ROLE_CELL
||
639 GetRole() == ui::AX_ROLE_COLUMN_HEADER
||
640 GetRole() == ui::AX_ROLE_ROW_HEADER
);
643 bool BrowserAccessibility::IsEditableText() const {
644 return HasState(ui::AX_STATE_EDITABLE
);
647 bool BrowserAccessibility::IsWebAreaForPresentationalIframe() const {
648 if (GetRole() != ui::AX_ROLE_WEB_AREA
&&
649 GetRole() != ui::AX_ROLE_ROOT_WEB_AREA
) {
653 BrowserAccessibility
* parent
= GetParent();
657 BrowserAccessibility
* grandparent
= parent
->GetParent();
661 return grandparent
->GetRole() == ui::AX_ROLE_IFRAME_PRESENTATIONAL
;
664 bool BrowserAccessibility::IsControl() const {
666 case ui::AX_ROLE_BUTTON
:
667 case ui::AX_ROLE_BUTTON_DROP_DOWN
:
668 case ui::AX_ROLE_CHECK_BOX
:
669 case ui::AX_ROLE_COLOR_WELL
:
670 case ui::AX_ROLE_COMBO_BOX
:
671 case ui::AX_ROLE_DISCLOSURE_TRIANGLE
:
672 case ui::AX_ROLE_LIST_BOX
:
673 case ui::AX_ROLE_MENU_BAR
:
674 case ui::AX_ROLE_MENU_BUTTON
:
675 case ui::AX_ROLE_MENU_ITEM
:
676 case ui::AX_ROLE_MENU_ITEM_CHECK_BOX
:
677 case ui::AX_ROLE_MENU_ITEM_RADIO
:
678 case ui::AX_ROLE_MENU
:
679 case ui::AX_ROLE_POP_UP_BUTTON
:
680 case ui::AX_ROLE_RADIO_BUTTON
:
681 case ui::AX_ROLE_SCROLL_BAR
:
682 case ui::AX_ROLE_SEARCH_BOX
:
683 case ui::AX_ROLE_SLIDER
:
684 case ui::AX_ROLE_SPIN_BUTTON
:
685 case ui::AX_ROLE_SWITCH
:
686 case ui::AX_ROLE_TAB
:
687 case ui::AX_ROLE_TEXT_FIELD
:
688 case ui::AX_ROLE_TOGGLE_BUTTON
:
689 case ui::AX_ROLE_TREE
:
696 int BrowserAccessibility::GetStaticTextLenRecursive() const {
697 if (GetRole() == ui::AX_ROLE_STATIC_TEXT
||
698 GetRole() == ui::AX_ROLE_LINE_BREAK
) {
699 return static_cast<int>(GetStringAttribute(ui::AX_ATTR_VALUE
).size());
703 for (size_t i
= 0; i
< InternalChildCount(); ++i
)
704 len
+= InternalGetChild(i
)->GetStaticTextLenRecursive();
708 void BrowserAccessibility::FixEmptyBounds(gfx::Rect
* bounds
) const
710 if (bounds
->width() > 0 && bounds
->height() > 0)
713 for (size_t i
= 0; i
< InternalChildCount(); ++i
) {
714 // Compute the bounds of each child - this calls FixEmptyBounds
715 // recursively if necessary.
716 BrowserAccessibility
* child
= InternalGetChild(i
);
717 gfx::Rect child_bounds
= child
->GetLocalBoundsRect();
719 // Ignore children that don't have valid bounds themselves.
720 if (child_bounds
.width() == 0 || child_bounds
.height() == 0)
723 // For the first valid child, just set the bounds to that child's bounds.
724 if (bounds
->width() == 0 || bounds
->height() == 0) {
725 *bounds
= child_bounds
;
729 // Union each additional child's bounds.
730 bounds
->Union(child_bounds
);
734 gfx::Rect
BrowserAccessibility::ElementBoundsToLocalBounds(gfx::Rect bounds
)
736 // Walk up the parent chain. Every time we encounter a Web Area, offset
737 // based on the scroll bars and then offset based on the origin of that
739 BrowserAccessibility
* parent
= GetParent();
740 bool need_to_offset_web_area
=
741 (GetRole() == ui::AX_ROLE_WEB_AREA
||
742 GetRole() == ui::AX_ROLE_ROOT_WEB_AREA
);
744 if (need_to_offset_web_area
&&
745 parent
->GetLocation().width() > 0 &&
746 parent
->GetLocation().height() > 0) {
747 bounds
.Offset(parent
->GetLocation().x(), parent
->GetLocation().y());
748 need_to_offset_web_area
= false;
751 // On some platforms, we don't want to take the root scroll offsets
753 if (parent
->GetRole() == ui::AX_ROLE_ROOT_WEB_AREA
&&
754 !manager()->UseRootScrollOffsetsWhenComputingBounds()) {
758 if (parent
->GetRole() == ui::AX_ROLE_WEB_AREA
||
759 parent
->GetRole() == ui::AX_ROLE_ROOT_WEB_AREA
) {
762 if (parent
->GetIntAttribute(ui::AX_ATTR_SCROLL_X
, &sx
) &&
763 parent
->GetIntAttribute(ui::AX_ATTR_SCROLL_Y
, &sy
)) {
764 bounds
.Offset(-sx
, -sy
);
766 need_to_offset_web_area
= true;
768 parent
= parent
->GetParent();
774 } // namespace content