Add ICU message format support
[chromium-blink-merge.git] / content / browser / accessibility / browser_accessibility.cc
blob6b32ef2c9202eff19bd0df8a3f4b177c930f0476
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_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.
25 // static
26 BrowserAccessibility* BrowserAccessibility::Create() {
27 return new BrowserAccessibility();
29 #endif
31 BrowserAccessibility::BrowserAccessibility()
32 : manager_(NULL),
33 node_(NULL) {
36 BrowserAccessibility::~BrowserAccessibility() {
39 void BrowserAccessibility::Init(BrowserAccessibilityManager* manager,
40 ui::AXNode* node) {
41 manager_ = manager;
42 node_ = node;
45 bool BrowserAccessibility::PlatformIsLeaf() const {
46 if (InternalChildCount() == 0)
47 return true;
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.
52 switch (GetRole()) {
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:
57 return true;
58 default:
59 return false;
63 uint32 BrowserAccessibility::PlatformChildCount() const {
64 if (HasIntAttribute(ui::AX_ATTR_CHILD_TREE_ID)) {
65 BrowserAccessibilityManager* child_manager =
66 BrowserAccessibilityManager::FromID(
67 GetIntAttribute(ui::AX_ATTR_CHILD_TREE_ID));
68 if (child_manager)
69 return 1;
71 return 0;
74 return PlatformIsLeaf() ? 0 : InternalChildCount();
77 bool BrowserAccessibility::IsNative() const {
78 return false;
81 bool BrowserAccessibility::IsDescendantOf(
82 BrowserAccessibility* ancestor) {
83 if (this == ancestor) {
84 return true;
85 } else if (GetParent()) {
86 return GetParent()->IsDescendantOf(ancestor);
89 return false;
92 BrowserAccessibility* BrowserAccessibility::PlatformGetChild(
93 uint32 child_index) const {
94 DCHECK(child_index < PlatformChildCount());
95 BrowserAccessibility* result = nullptr;
97 if (HasIntAttribute(ui::AX_ATTR_CHILD_TREE_ID)) {
98 BrowserAccessibilityManager* child_manager =
99 BrowserAccessibilityManager::FromID(
100 GetIntAttribute(ui::AX_ATTR_CHILD_TREE_ID));
101 if (child_manager)
102 result = child_manager->GetRoot();
103 } else {
104 result = InternalGetChild(child_index);
107 return result;
110 bool BrowserAccessibility::PlatformIsChildOfLeaf() const {
111 BrowserAccessibility* ancestor = GetParent();
112 while (ancestor) {
113 if (ancestor->PlatformIsLeaf())
114 return true;
115 ancestor = ancestor->GetParent();
118 return false;
121 BrowserAccessibility* BrowserAccessibility::GetPreviousSibling() {
122 if (GetParent() && GetIndexInParent() > 0)
123 return GetParent()->InternalGetChild(GetIndexInParent() - 1);
125 return NULL;
128 BrowserAccessibility* BrowserAccessibility::GetNextSibling() {
129 if (GetParent() &&
130 GetIndexInParent() >= 0 &&
131 GetIndexInParent() < static_cast<int>(
132 GetParent()->InternalChildCount() - 1)) {
133 return GetParent()->InternalGetChild(GetIndexInParent() + 1);
136 return NULL;
139 uint32 BrowserAccessibility::InternalChildCount() const {
140 if (!node_ || !manager_)
141 return 0;
142 return static_cast<uint32>(node_->child_count());
145 BrowserAccessibility* BrowserAccessibility::InternalGetChild(
146 uint32 child_index) const {
147 if (!node_ || !manager_)
148 return NULL;
149 return manager_->GetFromAXNode(node_->ChildAtIndex(child_index));
152 BrowserAccessibility* BrowserAccessibility::GetParent() const {
153 if (!node_ || !manager_)
154 return NULL;
155 ui::AXNode* parent = node_->parent();
156 if (parent)
157 return manager_->GetFromAXNode(parent);
159 return manager_->GetParentNodeFromParentTree();
162 int32 BrowserAccessibility::GetIndexInParent() const {
163 return node_ ? node_->index_in_parent() : -1;
166 int32 BrowserAccessibility::GetId() const {
167 return node_ ? node_->id() : -1;
170 const ui::AXNodeData& BrowserAccessibility::GetData() const {
171 CR_DEFINE_STATIC_LOCAL(ui::AXNodeData, empty_data, ());
172 if (node_)
173 return node_->data();
174 else
175 return empty_data;
178 gfx::Rect BrowserAccessibility::GetLocation() const {
179 return GetData().location;
182 int32 BrowserAccessibility::GetRole() const {
183 return GetData().role;
186 int32 BrowserAccessibility::GetState() const {
187 return GetData().state;
190 const BrowserAccessibility::HtmlAttributes&
191 BrowserAccessibility::GetHtmlAttributes() const {
192 return GetData().html_attributes;
195 gfx::Rect BrowserAccessibility::GetLocalBoundsRect() const {
196 gfx::Rect bounds = GetLocation();
197 FixEmptyBounds(&bounds);
198 return ElementBoundsToLocalBounds(bounds);
201 gfx::Rect BrowserAccessibility::GetGlobalBoundsRect() const {
202 gfx::Rect bounds = GetLocalBoundsRect();
204 // Adjust the bounds by the top left corner of the containing view's bounds
205 // in screen coordinates.
206 bounds.Offset(manager_->GetViewBounds().OffsetFromOrigin());
208 return bounds;
211 gfx::Rect BrowserAccessibility::GetLocalBoundsForRange(int start, int len)
212 const {
213 if (GetRole() != ui::AX_ROLE_STATIC_TEXT) {
214 // Apply recursively to all static text descendants. For example, if
215 // you call it on a div with two text node children, it just calls
216 // GetLocalBoundsForRange on each of the two children (adjusting
217 // |start| for each one) and unions the resulting rects.
218 gfx::Rect bounds;
219 for (size_t i = 0; i < InternalChildCount(); ++i) {
220 BrowserAccessibility* child = InternalGetChild(i);
221 int child_len = child->GetStaticTextLenRecursive();
222 if (start < child_len && start + len > 0) {
223 gfx::Rect child_rect = child->GetLocalBoundsForRange(start, len);
224 bounds.Union(child_rect);
226 start -= child_len;
228 return ElementBoundsToLocalBounds(bounds);
231 int end = start + len;
232 int child_start = 0;
233 int child_end = 0;
235 gfx::Rect bounds;
236 for (size_t i = 0; i < InternalChildCount() && child_end < start + len; ++i) {
237 BrowserAccessibility* child = InternalGetChild(i);
238 if (child->GetRole() != ui::AX_ROLE_INLINE_TEXT_BOX) {
239 DLOG(WARNING) << "BrowserAccessibility objects with role STATIC_TEXT " <<
240 "should have children of role INLINE_TEXT_BOX.";
241 continue;
244 std::string child_text;
245 child->GetStringAttribute(ui::AX_ATTR_VALUE, &child_text);
246 int child_len = static_cast<int>(child_text.size());
247 child_start = child_end;
248 child_end += child_len;
250 if (child_end < start)
251 continue;
253 int overlap_start = std::max(start, child_start);
254 int overlap_end = std::min(end, child_end);
256 int local_start = overlap_start - child_start;
257 int local_end = overlap_end - child_start;
259 gfx::Rect child_rect = child->GetLocation();
260 int text_direction = child->GetIntAttribute(
261 ui::AX_ATTR_TEXT_DIRECTION);
262 const std::vector<int32>& character_offsets = child->GetIntListAttribute(
263 ui::AX_ATTR_CHARACTER_OFFSETS);
264 int start_pixel_offset =
265 local_start > 0 ? character_offsets[local_start - 1] : 0;
266 int end_pixel_offset =
267 local_end > 0 ? character_offsets[local_end - 1] : 0;
269 gfx::Rect child_overlap_rect;
270 switch (text_direction) {
271 case ui::AX_TEXT_DIRECTION_NONE:
272 case ui::AX_TEXT_DIRECTION_LTR: {
273 int left = child_rect.x() + start_pixel_offset;
274 int right = child_rect.x() + end_pixel_offset;
275 child_overlap_rect = gfx::Rect(left, child_rect.y(),
276 right - left, child_rect.height());
277 break;
279 case ui::AX_TEXT_DIRECTION_RTL: {
280 int right = child_rect.right() - start_pixel_offset;
281 int left = child_rect.right() - end_pixel_offset;
282 child_overlap_rect = gfx::Rect(left, child_rect.y(),
283 right - left, child_rect.height());
284 break;
286 case ui::AX_TEXT_DIRECTION_TTB: {
287 int top = child_rect.y() + start_pixel_offset;
288 int bottom = child_rect.y() + end_pixel_offset;
289 child_overlap_rect = gfx::Rect(child_rect.x(), top,
290 child_rect.width(), bottom - top);
291 break;
293 case ui::AX_TEXT_DIRECTION_BTT: {
294 int bottom = child_rect.bottom() - start_pixel_offset;
295 int top = child_rect.bottom() - end_pixel_offset;
296 child_overlap_rect = gfx::Rect(child_rect.x(), top,
297 child_rect.width(), bottom - top);
298 break;
300 default:
301 NOTREACHED();
304 if (bounds.width() == 0 && bounds.height() == 0)
305 bounds = child_overlap_rect;
306 else
307 bounds.Union(child_overlap_rect);
310 return ElementBoundsToLocalBounds(bounds);
313 gfx::Rect BrowserAccessibility::GetGlobalBoundsForRange(int start, int len)
314 const {
315 gfx::Rect bounds = GetLocalBoundsForRange(start, len);
317 // Adjust the bounds by the top left corner of the containing view's bounds
318 // in screen coordinates.
319 bounds.Offset(manager_->GetViewBounds().OffsetFromOrigin());
321 return bounds;
324 int BrowserAccessibility::GetWordStartBoundary(
325 int start, ui::TextBoundaryDirection direction) const {
326 DCHECK_GE(start, -1);
327 // Special offset that indicates that a word boundary has not been found.
328 int word_start_not_found = GetStaticTextLenRecursive();
329 int word_start = word_start_not_found;
331 switch (GetRole()) {
332 case ui::AX_ROLE_STATIC_TEXT: {
333 int prev_word_start = word_start_not_found;
334 int child_start = 0;
335 int child_end = 0;
337 // Go through the inline text boxes.
338 for (size_t i = 0; i < InternalChildCount(); ++i) {
339 // The next child starts where the previous one ended.
340 child_start = child_end;
341 BrowserAccessibility* child = InternalGetChild(i);
342 DCHECK_EQ(child->GetRole(), ui::AX_ROLE_INLINE_TEXT_BOX);
343 const std::string& child_text = child->GetStringAttribute(
344 ui::AX_ATTR_VALUE);
345 int child_len = static_cast<int>(child_text.size());
346 child_end += child_len; // End is one past the last character.
348 const std::vector<int32>& word_starts = child->GetIntListAttribute(
349 ui::AX_ATTR_WORD_STARTS);
350 if (word_starts.empty()) {
351 word_start = child_end;
352 continue;
355 int local_start = start - child_start;
356 std::vector<int32>::const_iterator iter = std::upper_bound(
357 word_starts.begin(), word_starts.end(), local_start);
358 if (iter != word_starts.end()) {
359 if (direction == ui::FORWARDS_DIRECTION) {
360 word_start = child_start + *iter;
361 } else if (direction == ui::BACKWARDS_DIRECTION) {
362 if (iter == word_starts.begin()) {
363 // Return the position of the last word in the previous child.
364 word_start = prev_word_start;
365 } else {
366 word_start = child_start + *(iter - 1);
368 } else {
369 NOTREACHED();
371 break;
374 // No word start that is greater than the requested offset has been
375 // found.
376 prev_word_start = child_start + *(iter - 1);
377 if (direction == ui::FORWARDS_DIRECTION) {
378 word_start = child_end;
379 } else if (direction == ui::BACKWARDS_DIRECTION) {
380 word_start = prev_word_start;
381 } else {
382 NOTREACHED();
385 return word_start;
388 case ui::AX_ROLE_LINE_BREAK:
389 // Words never start at a line break.
390 return word_start_not_found;
392 default:
393 // If there are no children, the word start boundary is still unknown or
394 // found previously depending on the direction.
395 if (!InternalChildCount())
396 return word_start_not_found;
398 int child_start = 0;
399 for (size_t i = 0; i < InternalChildCount(); ++i) {
400 BrowserAccessibility* child = InternalGetChild(i);
401 int child_len = child->GetStaticTextLenRecursive();
402 int child_word_start = child->GetWordStartBoundary(start, direction);
403 if (child_word_start < child_len) {
404 // We have found a possible word boundary.
405 word_start = child_start + child_word_start;
408 // Decide when to stop searching.
409 if ((word_start != word_start_not_found &&
410 direction == ui::FORWARDS_DIRECTION) ||
411 (start < child_len &&
412 direction == ui::BACKWARDS_DIRECTION)) {
413 break;
416 child_start += child_len;
417 if (start >= child_len)
418 start -= child_len;
419 else
420 start = -1;
422 return word_start;
426 BrowserAccessibility* BrowserAccessibility::BrowserAccessibilityForPoint(
427 const gfx::Point& point) {
428 // The best result found that's a child of this object.
429 BrowserAccessibility* child_result = NULL;
430 // The best result that's an indirect descendant like grandchild, etc.
431 BrowserAccessibility* descendant_result = NULL;
433 // Walk the children recursively looking for the BrowserAccessibility that
434 // most tightly encloses the specified point. Walk backwards so that in
435 // the absence of any other information, we assume the object that occurs
436 // later in the tree is on top of one that comes before it.
437 for (int i = static_cast<int>(PlatformChildCount()) - 1; i >= 0; --i) {
438 BrowserAccessibility* child = PlatformGetChild(i);
440 // Skip table columns because cells are only contained in rows,
441 // not columns.
442 if (child->GetRole() == ui::AX_ROLE_COLUMN)
443 continue;
445 if (child->GetGlobalBoundsRect().Contains(point)) {
446 BrowserAccessibility* result = child->BrowserAccessibilityForPoint(point);
447 if (result == child && !child_result)
448 child_result = result;
449 if (result != child && !descendant_result)
450 descendant_result = result;
453 if (child_result && descendant_result)
454 break;
457 // Explanation of logic: it's possible that this point overlaps more than
458 // one child of this object. If so, as a heuristic we prefer if the point
459 // overlaps a descendant of one of the two children and not the other.
460 // As an example, suppose you have two rows of buttons - the buttons don't
461 // overlap, but the rows do. Without this heuristic, we'd greedily only
462 // consider one of the containers.
463 if (descendant_result)
464 return descendant_result;
465 if (child_result)
466 return child_result;
468 return this;
471 void BrowserAccessibility::Destroy() {
472 // Allow the object to fire a TextRemoved notification.
473 manager_->NotifyAccessibilityEvent(ui::AX_EVENT_HIDE, this);
474 node_ = NULL;
475 manager_ = NULL;
477 NativeReleaseReference();
480 void BrowserAccessibility::NativeReleaseReference() {
481 delete this;
484 bool BrowserAccessibility::HasBoolAttribute(
485 ui::AXBoolAttribute attribute) const {
486 return GetData().HasBoolAttribute(attribute);
489 bool BrowserAccessibility::GetBoolAttribute(
490 ui::AXBoolAttribute attribute) const {
491 return GetData().GetBoolAttribute(attribute);
494 bool BrowserAccessibility::GetBoolAttribute(
495 ui::AXBoolAttribute attribute, bool* value) const {
496 return GetData().GetBoolAttribute(attribute, value);
499 bool BrowserAccessibility::HasFloatAttribute(
500 ui::AXFloatAttribute attribute) const {
501 return GetData().HasFloatAttribute(attribute);
504 float BrowserAccessibility::GetFloatAttribute(
505 ui::AXFloatAttribute attribute) const {
506 return GetData().GetFloatAttribute(attribute);
509 bool BrowserAccessibility::GetFloatAttribute(
510 ui::AXFloatAttribute attribute, float* value) const {
511 return GetData().GetFloatAttribute(attribute, value);
514 bool BrowserAccessibility::HasIntAttribute(
515 ui::AXIntAttribute attribute) const {
516 return GetData().HasIntAttribute(attribute);
519 int BrowserAccessibility::GetIntAttribute(ui::AXIntAttribute attribute) const {
520 return GetData().GetIntAttribute(attribute);
523 bool BrowserAccessibility::GetIntAttribute(
524 ui::AXIntAttribute attribute, int* value) const {
525 return GetData().GetIntAttribute(attribute, value);
528 bool BrowserAccessibility::HasStringAttribute(
529 ui::AXStringAttribute attribute) const {
530 return GetData().HasStringAttribute(attribute);
533 const std::string& BrowserAccessibility::GetStringAttribute(
534 ui::AXStringAttribute attribute) const {
535 return GetData().GetStringAttribute(attribute);
538 bool BrowserAccessibility::GetStringAttribute(
539 ui::AXStringAttribute attribute, std::string* value) const {
540 return GetData().GetStringAttribute(attribute, value);
543 base::string16 BrowserAccessibility::GetString16Attribute(
544 ui::AXStringAttribute attribute) const {
545 return GetData().GetString16Attribute(attribute);
548 bool BrowserAccessibility::GetString16Attribute(
549 ui::AXStringAttribute attribute,
550 base::string16* value) const {
551 return GetData().GetString16Attribute(attribute, value);
554 bool BrowserAccessibility::HasIntListAttribute(
555 ui::AXIntListAttribute attribute) const {
556 return GetData().HasIntListAttribute(attribute);
559 const std::vector<int32>& BrowserAccessibility::GetIntListAttribute(
560 ui::AXIntListAttribute attribute) const {
561 return GetData().GetIntListAttribute(attribute);
564 bool BrowserAccessibility::GetIntListAttribute(
565 ui::AXIntListAttribute attribute,
566 std::vector<int32>* value) const {
567 return GetData().GetIntListAttribute(attribute, value);
570 bool BrowserAccessibility::GetHtmlAttribute(
571 const char* html_attr, std::string* value) const {
572 return GetData().GetHtmlAttribute(html_attr, value);
575 bool BrowserAccessibility::GetHtmlAttribute(
576 const char* html_attr, base::string16* value) const {
577 return GetData().GetHtmlAttribute(html_attr, value);
580 bool BrowserAccessibility::GetAriaTristate(
581 const char* html_attr,
582 bool* is_defined,
583 bool* is_mixed) const {
584 *is_defined = false;
585 *is_mixed = false;
587 base::string16 value;
588 if (!GetHtmlAttribute(html_attr, &value) ||
589 value.empty() ||
590 base::EqualsASCII(value, "undefined")) {
591 return false; // Not set (and *is_defined is also false)
594 *is_defined = true;
596 if (base::EqualsASCII(value, "true"))
597 return true;
599 if (base::EqualsASCII(value, "mixed"))
600 *is_mixed = true;
602 return false; // Not set
605 bool BrowserAccessibility::HasState(ui::AXState state_enum) const {
606 return (GetState() >> state_enum) & 1;
609 bool BrowserAccessibility::IsCellOrTableHeaderRole() const {
610 return (GetRole() == ui::AX_ROLE_CELL ||
611 GetRole() == ui::AX_ROLE_COLUMN_HEADER ||
612 GetRole() == ui::AX_ROLE_ROW_HEADER);
615 bool BrowserAccessibility::IsEditableText() const {
616 // These roles don't have readonly set, but they're not editable text.
617 if (GetRole() == ui::AX_ROLE_SCROLL_AREA ||
618 GetRole() == ui::AX_ROLE_COLUMN ||
619 GetRole() == ui::AX_ROLE_TABLE_HEADER_CONTAINER) {
620 return false;
623 // Note: WebAXStateReadonly being false means it's either a text control,
624 // or contenteditable. We also check for editable text roles to cover
625 // another element that has role=textbox set on it.
626 return (!HasState(ui::AX_STATE_READ_ONLY) ||
627 GetRole() == ui::AX_ROLE_TEXT_FIELD);
630 bool BrowserAccessibility::IsWebAreaForPresentationalIframe() const {
631 if (GetRole() != ui::AX_ROLE_WEB_AREA &&
632 GetRole() != ui::AX_ROLE_ROOT_WEB_AREA) {
633 return false;
636 BrowserAccessibility* parent = GetParent();
637 if (!parent)
638 return false;
640 BrowserAccessibility* grandparent = parent->GetParent();
641 if (!grandparent)
642 return false;
644 return grandparent->GetRole() == ui::AX_ROLE_IFRAME_PRESENTATIONAL;
647 bool BrowserAccessibility::IsControl() const {
648 switch (GetRole()) {
649 case ui::AX_ROLE_BUTTON:
650 case ui::AX_ROLE_BUTTON_DROP_DOWN:
651 case ui::AX_ROLE_CHECK_BOX:
652 case ui::AX_ROLE_COLOR_WELL:
653 case ui::AX_ROLE_COMBO_BOX:
654 case ui::AX_ROLE_DISCLOSURE_TRIANGLE:
655 case ui::AX_ROLE_LIST_BOX:
656 case ui::AX_ROLE_MENU_BAR:
657 case ui::AX_ROLE_MENU_BUTTON:
658 case ui::AX_ROLE_MENU_ITEM:
659 case ui::AX_ROLE_MENU_ITEM_CHECK_BOX:
660 case ui::AX_ROLE_MENU_ITEM_RADIO:
661 case ui::AX_ROLE_MENU:
662 case ui::AX_ROLE_POP_UP_BUTTON:
663 case ui::AX_ROLE_RADIO_BUTTON:
664 case ui::AX_ROLE_SCROLL_BAR:
665 case ui::AX_ROLE_SEARCH_BOX:
666 case ui::AX_ROLE_SLIDER:
667 case ui::AX_ROLE_SPIN_BUTTON:
668 case ui::AX_ROLE_SWITCH:
669 case ui::AX_ROLE_TAB:
670 case ui::AX_ROLE_TEXT_FIELD:
671 case ui::AX_ROLE_TOGGLE_BUTTON:
672 case ui::AX_ROLE_TREE:
673 return true;
674 default:
675 return false;
679 int BrowserAccessibility::GetStaticTextLenRecursive() const {
680 if (GetRole() == ui::AX_ROLE_STATIC_TEXT ||
681 GetRole() == ui::AX_ROLE_LINE_BREAK) {
682 return static_cast<int>(GetStringAttribute(ui::AX_ATTR_VALUE).size());
685 int len = 0;
686 for (size_t i = 0; i < InternalChildCount(); ++i)
687 len += InternalGetChild(i)->GetStaticTextLenRecursive();
688 return len;
691 void BrowserAccessibility::FixEmptyBounds(gfx::Rect* bounds) const
693 if (bounds->width() > 0 && bounds->height() > 0)
694 return;
696 for (size_t i = 0; i < InternalChildCount(); ++i) {
697 // Compute the bounds of each child - this calls FixEmptyBounds
698 // recursively if necessary.
699 BrowserAccessibility* child = InternalGetChild(i);
700 gfx::Rect child_bounds = child->GetLocalBoundsRect();
702 // Ignore children that don't have valid bounds themselves.
703 if (child_bounds.width() == 0 || child_bounds.height() == 0)
704 continue;
706 // For the first valid child, just set the bounds to that child's bounds.
707 if (bounds->width() == 0 || bounds->height() == 0) {
708 *bounds = child_bounds;
709 continue;
712 // Union each additional child's bounds.
713 bounds->Union(child_bounds);
717 gfx::Rect BrowserAccessibility::ElementBoundsToLocalBounds(gfx::Rect bounds)
718 const {
719 // Walk up the parent chain. Every time we encounter a Web Area, offset
720 // based on the scroll bars and then offset based on the origin of that
721 // nested web area.
722 BrowserAccessibility* parent = GetParent();
723 bool need_to_offset_web_area =
724 (GetRole() == ui::AX_ROLE_WEB_AREA ||
725 GetRole() == ui::AX_ROLE_ROOT_WEB_AREA);
726 while (parent) {
727 if (need_to_offset_web_area &&
728 parent->GetLocation().width() > 0 &&
729 parent->GetLocation().height() > 0) {
730 bounds.Offset(parent->GetLocation().x(), parent->GetLocation().y());
731 need_to_offset_web_area = false;
734 // On some platforms, we don't want to take the root scroll offsets
735 // into account.
736 if (parent->GetRole() == ui::AX_ROLE_ROOT_WEB_AREA &&
737 !manager()->UseRootScrollOffsetsWhenComputingBounds()) {
738 break;
741 if (parent->GetRole() == ui::AX_ROLE_WEB_AREA ||
742 parent->GetRole() == ui::AX_ROLE_ROOT_WEB_AREA) {
743 int sx = 0;
744 int sy = 0;
745 if (parent->GetIntAttribute(ui::AX_ATTR_SCROLL_X, &sx) &&
746 parent->GetIntAttribute(ui::AX_ATTR_SCROLL_Y, &sy)) {
747 bounds.Offset(-sx, -sy);
749 need_to_offset_web_area = true;
751 parent = parent->GetParent();
754 return bounds;
757 } // namespace content