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_AREA
:
56 case ui::AX_ROLE_TEXT_FIELD
:
63 uint32
BrowserAccessibility::PlatformChildCount() const {
64 return PlatformIsLeaf() ? 0 : InternalChildCount();
67 bool BrowserAccessibility::IsNative() const {
71 bool BrowserAccessibility::IsDescendantOf(
72 BrowserAccessibility
* ancestor
) {
73 if (this == ancestor
) {
75 } else if (GetParent()) {
76 return GetParent()->IsDescendantOf(ancestor
);
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());
91 result
= child_manager
->GetRoot();
97 bool BrowserAccessibility::PlatformIsChildOfLeaf() const {
98 BrowserAccessibility
* ancestor
= GetParent();
100 if (ancestor
->PlatformIsLeaf())
102 ancestor
= ancestor
->GetParent();
108 BrowserAccessibility
* BrowserAccessibility::GetPreviousSibling() {
109 if (GetParent() && GetIndexInParent() > 0)
110 return GetParent()->InternalGetChild(GetIndexInParent() - 1);
115 BrowserAccessibility
* BrowserAccessibility::GetNextSibling() {
117 GetIndexInParent() >= 0 &&
118 GetIndexInParent() < static_cast<int>(
119 GetParent()->InternalChildCount() - 1)) {
120 return GetParent()->InternalGetChild(GetIndexInParent() + 1);
126 uint32
BrowserAccessibility::InternalChildCount() const {
127 if (!node_
|| !manager_
)
129 return static_cast<uint32
>(node_
->child_count());
132 BrowserAccessibility
* BrowserAccessibility::InternalGetChild(
133 uint32 child_index
) const {
134 if (!node_
|| !manager_
)
136 return manager_
->GetFromAXNode(node_
->ChildAtIndex(child_index
));
139 BrowserAccessibility
* BrowserAccessibility::GetParent() const {
140 if (!node_
|| !manager_
)
142 ui::AXNode
* parent
= node_
->parent();
144 return manager_
->GetFromAXNode(parent
);
146 if (!manager_
->delegate())
149 BrowserAccessibility
* host_node
=
150 manager_
->delegate()->AccessibilityGetParentFrame();
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
, ());
168 return node_
->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());
205 gfx::Rect
BrowserAccessibility::GetLocalBoundsForRange(int start
, int len
)
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.
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
);
222 return ElementBoundsToLocalBounds(bounds
);
225 int end
= start
+ len
;
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
)
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());
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());
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
);
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
);
293 if (bounds
.width() == 0 && bounds
.height() == 0)
294 bounds
= child_overlap_rect
;
296 bounds
.Union(child_overlap_rect
);
299 return ElementBoundsToLocalBounds(bounds
);
302 gfx::Rect
BrowserAccessibility::GetGlobalBoundsForRange(int start
, int len
)
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());
313 int BrowserAccessibility::GetWordStartBoundary(
314 int start
, ui::TextBoundaryDirection direction
) const {
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
)
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(
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
;
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
;
360 word_start
= child_start
+ *(iter
- 1);
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
;
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,
398 if (child
->GetRole() == ui::AX_ROLE_COLUMN
)
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
)
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
;
427 void BrowserAccessibility::Destroy() {
428 // Allow the object to fire a TextRemoved notification.
429 manager_
->NotifyAccessibilityEvent(ui::AX_EVENT_HIDE
, this);
433 NativeReleaseReference();
436 void BrowserAccessibility::NativeReleaseReference() {
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
)
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
;
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
;
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
)
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
;
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
;
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
)
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
;
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
;
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
)
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
;
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
;
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
))
595 *value
= base::UTF8ToUTF16(value_utf8
);
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
)
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
;
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
;
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
;
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
))
654 *value
= base::UTF8ToUTF16(value_utf8
);
658 bool BrowserAccessibility::GetAriaTristate(
659 const char* html_attr
,
661 bool* is_mixed
) const {
665 base::string16 value
;
666 if (!GetHtmlAttribute(html_attr
, &value
) ||
668 EqualsASCII(value
, "undefined")) {
669 return false; // Not set (and *is_defined is also false)
674 if (EqualsASCII(value
, "true"))
677 if (EqualsASCII(value
, "mixed"))
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
) {
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
) {
715 BrowserAccessibility
* parent
= GetParent();
719 BrowserAccessibility
* grandparent
= parent
->GetParent();
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());
731 for (size_t i
= 0; i
< InternalChildCount(); ++i
)
732 len
+= InternalGetChild(i
)->GetStaticTextLenRecursive();
736 BrowserAccessibility
* BrowserAccessibility::GetParentForBoundsCalculation()
738 if (!node_
|| !manager_
)
740 ui::AXNode
* parent
= node_
->parent();
742 return manager_
->GetFromAXNode(parent
);
744 if (!manager_
->delegate())
747 return manager_
->delegate()->AccessibilityGetParentFrame();
750 gfx::Rect
BrowserAccessibility::ElementBoundsToLocalBounds(gfx::Rect bounds
)
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
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
);
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
769 if (parent
->GetRole() == ui::AX_ROLE_ROOT_WEB_AREA
&&
770 !manager()->UseRootScrollOffsetsWhenComputingBounds()) {
774 if (parent
->GetRole() == ui::AX_ROLE_WEB_AREA
||
775 parent
->GetRole() == ui::AX_ROLE_ROOT_WEB_AREA
) {
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();
790 } // namespace content