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_LINE_BREAK
:
54 case ui::AX_ROLE_SLIDER
:
55 case ui::AX_ROLE_STATIC_TEXT
:
56 case ui::AX_ROLE_TEXT_FIELD
:
63 uint32
BrowserAccessibility::PlatformChildCount() const {
64 if (GetBoolAttribute(ui::AX_ATTR_IS_AX_TREE_HOST
)) {
65 // Check if the child frame currently exists.
66 if (manager_
->delegate()->AccessibilityGetChildFrame(GetId()))
72 return PlatformIsLeaf() ? 0 : InternalChildCount();
75 bool BrowserAccessibility::IsNative() const {
79 bool BrowserAccessibility::IsDescendantOf(
80 BrowserAccessibility
* ancestor
) {
81 if (this == ancestor
) {
83 } else if (GetParent()) {
84 return GetParent()->IsDescendantOf(ancestor
);
90 BrowserAccessibility
* BrowserAccessibility::PlatformGetChild(
91 uint32 child_index
) const {
92 DCHECK(child_index
< PlatformChildCount());
93 BrowserAccessibility
* result
= nullptr;
95 if (GetBoolAttribute(ui::AX_ATTR_IS_AX_TREE_HOST
)) {
96 BrowserAccessibilityManager
* child_manager
=
97 manager_
->delegate()->AccessibilityGetChildFrame(GetId());
99 result
= child_manager
->GetRoot();
101 result
= InternalGetChild(child_index
);
107 bool BrowserAccessibility::PlatformIsChildOfLeaf() const {
108 BrowserAccessibility
* ancestor
= GetParent();
110 if (ancestor
->PlatformIsLeaf())
112 ancestor
= ancestor
->GetParent();
118 BrowserAccessibility
* BrowserAccessibility::GetPreviousSibling() {
119 if (GetParent() && GetIndexInParent() > 0)
120 return GetParent()->InternalGetChild(GetIndexInParent() - 1);
125 BrowserAccessibility
* BrowserAccessibility::GetNextSibling() {
127 GetIndexInParent() >= 0 &&
128 GetIndexInParent() < static_cast<int>(
129 GetParent()->InternalChildCount() - 1)) {
130 return GetParent()->InternalGetChild(GetIndexInParent() + 1);
136 uint32
BrowserAccessibility::InternalChildCount() const {
137 if (!node_
|| !manager_
)
139 return static_cast<uint32
>(node_
->child_count());
142 BrowserAccessibility
* BrowserAccessibility::InternalGetChild(
143 uint32 child_index
) const {
144 if (!node_
|| !manager_
)
146 return manager_
->GetFromAXNode(node_
->ChildAtIndex(child_index
));
149 BrowserAccessibility
* BrowserAccessibility::GetParent() const {
150 if (!node_
|| !manager_
)
152 ui::AXNode
* parent
= node_
->parent();
154 return manager_
->GetFromAXNode(parent
);
156 if (!manager_
->delegate())
159 BrowserAccessibility
* host_node
=
160 manager_
->delegate()->AccessibilityGetParentFrame();
167 int32
BrowserAccessibility::GetIndexInParent() const {
168 return node_
? node_
->index_in_parent() : -1;
171 int32
BrowserAccessibility::GetId() const {
172 return node_
? node_
->id() : -1;
175 const ui::AXNodeData
& BrowserAccessibility::GetData() const {
176 CR_DEFINE_STATIC_LOCAL(ui::AXNodeData
, empty_data
, ());
178 return node_
->data();
183 gfx::Rect
BrowserAccessibility::GetLocation() const {
184 return GetData().location
;
187 int32
BrowserAccessibility::GetRole() const {
188 return GetData().role
;
191 int32
BrowserAccessibility::GetState() const {
192 return GetData().state
;
195 const BrowserAccessibility::HtmlAttributes
&
196 BrowserAccessibility::GetHtmlAttributes() const {
197 return GetData().html_attributes
;
200 gfx::Rect
BrowserAccessibility::GetLocalBoundsRect() const {
201 gfx::Rect bounds
= GetLocation();
202 FixEmptyBounds(&bounds
);
203 return ElementBoundsToLocalBounds(bounds
);
206 gfx::Rect
BrowserAccessibility::GetGlobalBoundsRect() const {
207 gfx::Rect bounds
= GetLocalBoundsRect();
209 // Adjust the bounds by the top left corner of the containing view's bounds
210 // in screen coordinates.
211 bounds
.Offset(manager_
->GetViewBounds().OffsetFromOrigin());
216 gfx::Rect
BrowserAccessibility::GetLocalBoundsForRange(int start
, int len
)
218 if (GetRole() != ui::AX_ROLE_STATIC_TEXT
) {
219 // Apply recursively to all static text descendants. For example, if
220 // you call it on a div with two text node children, it just calls
221 // GetLocalBoundsForRange on each of the two children (adjusting
222 // |start| for each one) and unions the resulting rects.
224 for (size_t i
= 0; i
< InternalChildCount(); ++i
) {
225 BrowserAccessibility
* child
= InternalGetChild(i
);
226 int child_len
= child
->GetStaticTextLenRecursive();
227 if (start
< child_len
&& start
+ len
> 0) {
228 gfx::Rect child_rect
= child
->GetLocalBoundsForRange(start
, len
);
229 bounds
.Union(child_rect
);
233 return ElementBoundsToLocalBounds(bounds
);
236 int end
= start
+ len
;
241 for (size_t i
= 0; i
< InternalChildCount() && child_end
< start
+ len
; ++i
) {
242 BrowserAccessibility
* child
= InternalGetChild(i
);
243 if (child
->GetRole() != ui::AX_ROLE_INLINE_TEXT_BOX
) {
244 DLOG(WARNING
) << "BrowserAccessibility objects with role STATIC_TEXT " <<
245 "should have children of role INLINE_TEXT_BOX.";
249 std::string child_text
;
250 child
->GetStringAttribute(ui::AX_ATTR_VALUE
, &child_text
);
251 int child_len
= static_cast<int>(child_text
.size());
252 child_start
= child_end
;
253 child_end
+= child_len
;
255 if (child_end
< start
)
258 int overlap_start
= std::max(start
, child_start
);
259 int overlap_end
= std::min(end
, child_end
);
261 int local_start
= overlap_start
- child_start
;
262 int local_end
= overlap_end
- child_start
;
264 gfx::Rect child_rect
= child
->GetLocation();
265 int text_direction
= child
->GetIntAttribute(
266 ui::AX_ATTR_TEXT_DIRECTION
);
267 const std::vector
<int32
>& character_offsets
= child
->GetIntListAttribute(
268 ui::AX_ATTR_CHARACTER_OFFSETS
);
269 int start_pixel_offset
=
270 local_start
> 0 ? character_offsets
[local_start
- 1] : 0;
271 int end_pixel_offset
=
272 local_end
> 0 ? character_offsets
[local_end
- 1] : 0;
274 gfx::Rect child_overlap_rect
;
275 switch (text_direction
) {
276 case ui::AX_TEXT_DIRECTION_NONE
:
277 case ui::AX_TEXT_DIRECTION_LTR
: {
278 int left
= child_rect
.x() + start_pixel_offset
;
279 int right
= child_rect
.x() + end_pixel_offset
;
280 child_overlap_rect
= gfx::Rect(left
, child_rect
.y(),
281 right
- left
, child_rect
.height());
284 case ui::AX_TEXT_DIRECTION_RTL
: {
285 int right
= child_rect
.right() - start_pixel_offset
;
286 int left
= child_rect
.right() - end_pixel_offset
;
287 child_overlap_rect
= gfx::Rect(left
, child_rect
.y(),
288 right
- left
, child_rect
.height());
291 case ui::AX_TEXT_DIRECTION_TTB
: {
292 int top
= child_rect
.y() + start_pixel_offset
;
293 int bottom
= child_rect
.y() + end_pixel_offset
;
294 child_overlap_rect
= gfx::Rect(child_rect
.x(), top
,
295 child_rect
.width(), bottom
- top
);
298 case ui::AX_TEXT_DIRECTION_BTT
: {
299 int bottom
= child_rect
.bottom() - start_pixel_offset
;
300 int top
= child_rect
.bottom() - end_pixel_offset
;
301 child_overlap_rect
= gfx::Rect(child_rect
.x(), top
,
302 child_rect
.width(), bottom
- top
);
309 if (bounds
.width() == 0 && bounds
.height() == 0)
310 bounds
= child_overlap_rect
;
312 bounds
.Union(child_overlap_rect
);
315 return ElementBoundsToLocalBounds(bounds
);
318 gfx::Rect
BrowserAccessibility::GetGlobalBoundsForRange(int start
, int len
)
320 gfx::Rect bounds
= GetLocalBoundsForRange(start
, len
);
322 // Adjust the bounds by the top left corner of the containing view's bounds
323 // in screen coordinates.
324 bounds
.Offset(manager_
->GetViewBounds().OffsetFromOrigin());
329 int BrowserAccessibility::GetWordStartBoundary(
330 int start
, ui::TextBoundaryDirection direction
) const {
331 DCHECK_GE(start
, -1);
332 // Special offset that indicates that a word boundary has not been found.
333 int word_start_not_found
= GetStaticTextLenRecursive();
334 int word_start
= word_start_not_found
;
337 case ui::AX_ROLE_STATIC_TEXT
: {
338 int prev_word_start
= word_start_not_found
;
342 // Go through the inline text boxes.
343 for (size_t i
= 0; i
< InternalChildCount(); ++i
) {
344 // The next child starts where the previous one ended.
345 child_start
= child_end
;
346 BrowserAccessibility
* child
= InternalGetChild(i
);
347 DCHECK_EQ(child
->GetRole(), ui::AX_ROLE_INLINE_TEXT_BOX
);
348 const std::string
& child_text
= child
->GetStringAttribute(
350 int child_len
= static_cast<int>(child_text
.size());
351 child_end
+= child_len
; // End is one past the last character.
353 const std::vector
<int32
>& word_starts
= child
->GetIntListAttribute(
354 ui::AX_ATTR_WORD_STARTS
);
355 if (word_starts
.empty()) {
356 word_start
= child_end
;
360 int local_start
= start
- child_start
;
361 std::vector
<int32
>::const_iterator iter
= std::upper_bound(
362 word_starts
.begin(), word_starts
.end(), local_start
);
363 if (iter
!= word_starts
.end()) {
364 if (direction
== ui::FORWARDS_DIRECTION
) {
365 word_start
= child_start
+ *iter
;
366 } else if (direction
== ui::BACKWARDS_DIRECTION
) {
367 if (iter
== word_starts
.begin()) {
368 // Return the position of the last word in the previous child.
369 word_start
= prev_word_start
;
371 word_start
= child_start
+ *(iter
- 1);
379 // No word start that is greater than the requested offset has been
381 prev_word_start
= child_start
+ *(iter
- 1);
382 if (direction
== ui::FORWARDS_DIRECTION
) {
383 word_start
= child_end
;
384 } else if (direction
== ui::BACKWARDS_DIRECTION
) {
385 word_start
= prev_word_start
;
393 case ui::AX_ROLE_LINE_BREAK
:
394 // Words never start at a line break.
395 return word_start_not_found
;
398 // If there are no children, the word start boundary is still unknown or
399 // found previously depending on the direction.
400 if (!InternalChildCount())
401 return word_start_not_found
;
404 for (size_t i
= 0; i
< InternalChildCount(); ++i
) {
405 BrowserAccessibility
* child
= InternalGetChild(i
);
406 int child_len
= child
->GetStaticTextLenRecursive();
407 int child_word_start
= child
->GetWordStartBoundary(start
, direction
);
408 if (child_word_start
< child_len
) {
409 // We have found a possible word boundary.
410 word_start
= child_start
+ child_word_start
;
413 // Decide when to stop searching.
414 if ((word_start
!= word_start_not_found
&&
415 direction
== ui::FORWARDS_DIRECTION
) ||
416 (start
< child_len
&&
417 direction
== ui::BACKWARDS_DIRECTION
)) {
421 child_start
+= child_len
;
422 if (start
>= child_len
)
431 BrowserAccessibility
* BrowserAccessibility::BrowserAccessibilityForPoint(
432 const gfx::Point
& point
) {
433 // The best result found that's a child of this object.
434 BrowserAccessibility
* child_result
= NULL
;
435 // The best result that's an indirect descendant like grandchild, etc.
436 BrowserAccessibility
* descendant_result
= NULL
;
438 // Walk the children recursively looking for the BrowserAccessibility that
439 // most tightly encloses the specified point. Walk backwards so that in
440 // the absence of any other information, we assume the object that occurs
441 // later in the tree is on top of one that comes before it.
442 for (int i
= static_cast<int>(PlatformChildCount()) - 1; i
>= 0; --i
) {
443 BrowserAccessibility
* child
= PlatformGetChild(i
);
445 // Skip table columns because cells are only contained in rows,
447 if (child
->GetRole() == ui::AX_ROLE_COLUMN
)
450 if (child
->GetGlobalBoundsRect().Contains(point
)) {
451 BrowserAccessibility
* result
= child
->BrowserAccessibilityForPoint(point
);
452 if (result
== child
&& !child_result
)
453 child_result
= result
;
454 if (result
!= child
&& !descendant_result
)
455 descendant_result
= result
;
458 if (child_result
&& descendant_result
)
462 // Explanation of logic: it's possible that this point overlaps more than
463 // one child of this object. If so, as a heuristic we prefer if the point
464 // overlaps a descendant of one of the two children and not the other.
465 // As an example, suppose you have two rows of buttons - the buttons don't
466 // overlap, but the rows do. Without this heuristic, we'd greedily only
467 // consider one of the containers.
468 if (descendant_result
)
469 return descendant_result
;
476 void BrowserAccessibility::Destroy() {
477 // Allow the object to fire a TextRemoved notification.
478 manager_
->NotifyAccessibilityEvent(ui::AX_EVENT_HIDE
, this);
482 NativeReleaseReference();
485 void BrowserAccessibility::NativeReleaseReference() {
489 bool BrowserAccessibility::HasBoolAttribute(
490 ui::AXBoolAttribute attribute
) const {
491 return GetData().HasBoolAttribute(attribute
);
494 bool BrowserAccessibility::GetBoolAttribute(
495 ui::AXBoolAttribute attribute
) const {
496 return GetData().GetBoolAttribute(attribute
);
499 bool BrowserAccessibility::GetBoolAttribute(
500 ui::AXBoolAttribute attribute
, bool* value
) const {
501 return GetData().GetBoolAttribute(attribute
, value
);
504 bool BrowserAccessibility::HasFloatAttribute(
505 ui::AXFloatAttribute attribute
) const {
506 return GetData().HasFloatAttribute(attribute
);
509 float BrowserAccessibility::GetFloatAttribute(
510 ui::AXFloatAttribute attribute
) const {
511 return GetData().GetFloatAttribute(attribute
);
514 bool BrowserAccessibility::GetFloatAttribute(
515 ui::AXFloatAttribute attribute
, float* value
) const {
516 return GetData().GetFloatAttribute(attribute
, value
);
519 bool BrowserAccessibility::HasIntAttribute(
520 ui::AXIntAttribute attribute
) const {
521 return GetData().HasIntAttribute(attribute
);
524 int BrowserAccessibility::GetIntAttribute(ui::AXIntAttribute attribute
) const {
525 return GetData().GetIntAttribute(attribute
);
528 bool BrowserAccessibility::GetIntAttribute(
529 ui::AXIntAttribute attribute
, int* value
) const {
530 return GetData().GetIntAttribute(attribute
, value
);
533 bool BrowserAccessibility::HasStringAttribute(
534 ui::AXStringAttribute attribute
) const {
535 return GetData().HasStringAttribute(attribute
);
538 const std::string
& BrowserAccessibility::GetStringAttribute(
539 ui::AXStringAttribute attribute
) const {
540 return GetData().GetStringAttribute(attribute
);
543 bool BrowserAccessibility::GetStringAttribute(
544 ui::AXStringAttribute attribute
, std::string
* value
) const {
545 return GetData().GetStringAttribute(attribute
, value
);
548 base::string16
BrowserAccessibility::GetString16Attribute(
549 ui::AXStringAttribute attribute
) const {
550 return GetData().GetString16Attribute(attribute
);
553 bool BrowserAccessibility::GetString16Attribute(
554 ui::AXStringAttribute attribute
,
555 base::string16
* value
) const {
556 return GetData().GetString16Attribute(attribute
, value
);
559 bool BrowserAccessibility::HasIntListAttribute(
560 ui::AXIntListAttribute attribute
) const {
561 return GetData().HasIntListAttribute(attribute
);
564 const std::vector
<int32
>& BrowserAccessibility::GetIntListAttribute(
565 ui::AXIntListAttribute attribute
) const {
566 return GetData().GetIntListAttribute(attribute
);
569 bool BrowserAccessibility::GetIntListAttribute(
570 ui::AXIntListAttribute attribute
,
571 std::vector
<int32
>* value
) const {
572 return GetData().GetIntListAttribute(attribute
, value
);
575 bool BrowserAccessibility::GetHtmlAttribute(
576 const char* html_attr
, std::string
* value
) const {
577 return GetData().GetHtmlAttribute(html_attr
, value
);
580 bool BrowserAccessibility::GetHtmlAttribute(
581 const char* html_attr
, base::string16
* value
) const {
582 return GetData().GetHtmlAttribute(html_attr
, value
);
585 bool BrowserAccessibility::GetAriaTristate(
586 const char* html_attr
,
588 bool* is_mixed
) const {
592 base::string16 value
;
593 if (!GetHtmlAttribute(html_attr
, &value
) ||
595 base::EqualsASCII(value
, "undefined")) {
596 return false; // Not set (and *is_defined is also false)
601 if (base::EqualsASCII(value
, "true"))
604 if (base::EqualsASCII(value
, "mixed"))
607 return false; // Not set
610 bool BrowserAccessibility::HasState(ui::AXState state_enum
) const {
611 return (GetState() >> state_enum
) & 1;
614 bool BrowserAccessibility::IsCellOrTableHeaderRole() const {
615 return (GetRole() == ui::AX_ROLE_CELL
||
616 GetRole() == ui::AX_ROLE_COLUMN_HEADER
||
617 GetRole() == ui::AX_ROLE_ROW_HEADER
);
620 bool BrowserAccessibility::IsEditableText() const {
621 // These roles don't have readonly set, but they're not editable text.
622 if (GetRole() == ui::AX_ROLE_SCROLL_AREA
||
623 GetRole() == ui::AX_ROLE_COLUMN
||
624 GetRole() == ui::AX_ROLE_TABLE_HEADER_CONTAINER
) {
628 // Note: WebAXStateReadonly being false means it's either a text control,
629 // or contenteditable. We also check for editable text roles to cover
630 // another element that has role=textbox set on it.
631 return (!HasState(ui::AX_STATE_READ_ONLY
) ||
632 GetRole() == ui::AX_ROLE_TEXT_FIELD
);
635 bool BrowserAccessibility::IsWebAreaForPresentationalIframe() const {
636 if (GetRole() != ui::AX_ROLE_WEB_AREA
&&
637 GetRole() != ui::AX_ROLE_ROOT_WEB_AREA
) {
641 BrowserAccessibility
* parent
= GetParent();
645 BrowserAccessibility
* grandparent
= parent
->GetParent();
649 return grandparent
->GetRole() == ui::AX_ROLE_IFRAME_PRESENTATIONAL
;
652 bool BrowserAccessibility::IsControl() const {
654 case ui::AX_ROLE_BUTTON
:
655 case ui::AX_ROLE_BUTTON_DROP_DOWN
:
656 case ui::AX_ROLE_CHECK_BOX
:
657 case ui::AX_ROLE_COLOR_WELL
:
658 case ui::AX_ROLE_COMBO_BOX
:
659 case ui::AX_ROLE_DISCLOSURE_TRIANGLE
:
660 case ui::AX_ROLE_LIST_BOX
:
661 case ui::AX_ROLE_MENU_BAR
:
662 case ui::AX_ROLE_MENU_BUTTON
:
663 case ui::AX_ROLE_MENU_ITEM
:
664 case ui::AX_ROLE_MENU_ITEM_CHECK_BOX
:
665 case ui::AX_ROLE_MENU_ITEM_RADIO
:
666 case ui::AX_ROLE_MENU
:
667 case ui::AX_ROLE_POP_UP_BUTTON
:
668 case ui::AX_ROLE_RADIO_BUTTON
:
669 case ui::AX_ROLE_SCROLL_BAR
:
670 case ui::AX_ROLE_SEARCH_BOX
:
671 case ui::AX_ROLE_SLIDER
:
672 case ui::AX_ROLE_SPIN_BUTTON
:
673 case ui::AX_ROLE_SWITCH
:
674 case ui::AX_ROLE_TAB
:
675 case ui::AX_ROLE_TEXT_FIELD
:
676 case ui::AX_ROLE_TOGGLE_BUTTON
:
677 case ui::AX_ROLE_TREE
:
684 int BrowserAccessibility::GetStaticTextLenRecursive() const {
685 if (GetRole() == ui::AX_ROLE_STATIC_TEXT
||
686 GetRole() == ui::AX_ROLE_LINE_BREAK
) {
687 return static_cast<int>(GetStringAttribute(ui::AX_ATTR_VALUE
).size());
691 for (size_t i
= 0; i
< InternalChildCount(); ++i
)
692 len
+= InternalGetChild(i
)->GetStaticTextLenRecursive();
696 BrowserAccessibility
* BrowserAccessibility::GetParentForBoundsCalculation()
698 if (!node_
|| !manager_
)
700 ui::AXNode
* parent
= node_
->parent();
702 return manager_
->GetFromAXNode(parent
);
704 if (!manager_
->delegate())
707 return manager_
->delegate()->AccessibilityGetParentFrame();
710 void BrowserAccessibility::FixEmptyBounds(gfx::Rect
* bounds
) const
712 if (bounds
->width() > 0 && bounds
->height() > 0)
715 for (size_t i
= 0; i
< InternalChildCount(); ++i
) {
716 // Compute the bounds of each child - this calls FixEmptyBounds
717 // recursively if necessary.
718 BrowserAccessibility
* child
= InternalGetChild(i
);
719 gfx::Rect child_bounds
= child
->GetLocalBoundsRect();
721 // Ignore children that don't have valid bounds themselves.
722 if (child_bounds
.width() == 0 || child_bounds
.height() == 0)
725 // For the first valid child, just set the bounds to that child's bounds.
726 if (bounds
->width() == 0 || bounds
->height() == 0) {
727 *bounds
= child_bounds
;
731 // Union each additional child's bounds.
732 bounds
->Union(child_bounds
);
736 gfx::Rect
BrowserAccessibility::ElementBoundsToLocalBounds(gfx::Rect bounds
)
738 // Walk up the parent chain. Every time we encounter a Web Area, offset
739 // based on the scroll bars and then offset based on the origin of that
741 BrowserAccessibility
* parent
= GetParentForBoundsCalculation();
742 bool need_to_offset_web_area
=
743 (GetRole() == ui::AX_ROLE_WEB_AREA
||
744 GetRole() == ui::AX_ROLE_ROOT_WEB_AREA
);
746 if (need_to_offset_web_area
&&
747 parent
->GetLocation().width() > 0 &&
748 parent
->GetLocation().height() > 0) {
749 bounds
.Offset(parent
->GetLocation().x(), parent
->GetLocation().y());
750 need_to_offset_web_area
= false;
753 // On some platforms, we don't want to take the root scroll offsets
755 if (parent
->GetRole() == ui::AX_ROLE_ROOT_WEB_AREA
&&
756 !manager()->UseRootScrollOffsetsWhenComputingBounds()) {
760 if (parent
->GetRole() == ui::AX_ROLE_WEB_AREA
||
761 parent
->GetRole() == ui::AX_ROLE_ROOT_WEB_AREA
) {
764 if (parent
->GetIntAttribute(ui::AX_ATTR_SCROLL_X
, &sx
) &&
765 parent
->GetIntAttribute(ui::AX_ATTR_SCROLL_Y
, &sy
)) {
766 bounds
.Offset(-sx
, -sy
);
768 need_to_offset_web_area
= true;
770 parent
= parent
->GetParentForBoundsCalculation();
776 } // namespace content