Merge Chromium + Blink git repositories
[chromium-blink-merge.git] / content / browser / accessibility / browser_accessibility.cc
blob150d0f4301f99a4204517ded1dc48e097263dfae
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_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:
58 return true;
59 default:
60 return false;
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));
69 if (child_manager)
70 return 1;
72 return 0;
75 return PlatformIsLeaf() ? 0 : InternalChildCount();
78 bool BrowserAccessibility::IsNative() const {
79 return false;
82 bool BrowserAccessibility::IsDescendantOf(
83 const BrowserAccessibility* ancestor) const {
84 if (this == ancestor) {
85 return true;
86 } else if (GetParent()) {
87 return GetParent()->IsDescendantOf(ancestor);
90 return false;
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));
107 if (child_manager)
108 result = child_manager->GetRoot();
109 } else {
110 result = InternalGetChild(child_index);
113 return result;
116 bool BrowserAccessibility::PlatformIsChildOfLeaf() const {
117 BrowserAccessibility* ancestor = GetParent();
118 while (ancestor) {
119 if (ancestor->PlatformIsLeaf())
120 return true;
121 ancestor = ancestor->GetParent();
124 return false;
127 BrowserAccessibility* BrowserAccessibility::GetPreviousSibling() const {
128 if (GetParent() && GetIndexInParent() > 0)
129 return GetParent()->InternalGetChild(GetIndexInParent() - 1);
131 return nullptr;
134 BrowserAccessibility* BrowserAccessibility::GetNextSibling() const {
135 if (GetParent() &&
136 GetIndexInParent() >= 0 &&
137 GetIndexInParent() < static_cast<int>(
138 GetParent()->InternalChildCount() - 1)) {
139 return GetParent()->InternalGetChild(GetIndexInParent() + 1);
142 return nullptr;
145 BrowserAccessibility* BrowserAccessibility::PlatformDeepestFirstChild() const {
146 if (!PlatformChildCount())
147 return nullptr;
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())
158 return nullptr;
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_)
169 return 0;
170 return static_cast<uint32>(node_->child_count());
173 BrowserAccessibility* BrowserAccessibility::InternalGetChild(
174 uint32 child_index) const {
175 if (!node_ || !manager_)
176 return NULL;
177 return manager_->GetFromAXNode(node_->ChildAtIndex(child_index));
180 BrowserAccessibility* BrowserAccessibility::GetParent() const {
181 if (!node_ || !manager_)
182 return NULL;
183 ui::AXNode* parent = node_->parent();
184 if (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, ());
200 if (node_)
201 return node_->data();
202 else
203 return empty_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());
236 return bounds;
239 gfx::Rect BrowserAccessibility::GetLocalBoundsForRange(int start, int len)
240 const {
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.
246 gfx::Rect bounds;
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);
254 start -= child_len;
256 return ElementBoundsToLocalBounds(bounds);
259 int end = start + len;
260 int child_start = 0;
261 int child_end = 0;
263 gfx::Rect bounds;
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.";
269 continue;
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)
279 continue;
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());
305 break;
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());
312 break;
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);
319 break;
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);
326 break;
328 default:
329 NOTREACHED();
332 if (bounds.width() == 0 && bounds.height() == 0)
333 bounds = child_overlap_rect;
334 else
335 bounds.Union(child_overlap_rect);
338 return ElementBoundsToLocalBounds(bounds);
341 gfx::Rect BrowserAccessibility::GetGlobalBoundsForRange(int start, int len)
342 const {
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());
349 return bounds;
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;
359 switch (GetRole()) {
360 case ui::AX_ROLE_STATIC_TEXT: {
361 int prev_word_start = word_start_not_found;
362 int child_start = 0;
363 int child_end = 0;
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(
372 ui::AX_ATTR_VALUE);
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;
380 continue;
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;
393 } else {
394 word_start = child_start + *(iter - 1);
396 } else {
397 NOTREACHED();
399 break;
402 // No word start that is greater than the requested offset has been
403 // found.
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;
409 } else {
410 NOTREACHED();
413 return word_start;
416 case ui::AX_ROLE_LINE_BREAK:
417 // Words never start at a line break.
418 return word_start_not_found;
420 default:
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;
426 int child_start = 0;
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)) {
441 break;
444 child_start += child_len;
445 if (start >= child_len)
446 start -= child_len;
447 else
448 start = -1;
450 return word_start;
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,
469 // not columns.
470 if (child->GetRole() == ui::AX_ROLE_COLUMN)
471 continue;
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)
482 break;
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;
493 if (child_result)
494 return child_result;
496 return this;
499 void BrowserAccessibility::Destroy() {
500 // Allow the object to fire a TextRemoved notification.
501 manager_->NotifyAccessibilityEvent(ui::AX_EVENT_HIDE, this);
502 node_ = NULL;
503 manager_ = NULL;
505 NativeReleaseReference();
508 void BrowserAccessibility::NativeReleaseReference() {
509 delete this;
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,
610 bool* is_defined,
611 bool* is_mixed) const {
612 *is_defined = false;
613 *is_mixed = false;
615 base::string16 value;
616 if (!GetHtmlAttribute(html_attr, &value) ||
617 value.empty() ||
618 base::EqualsASCII(value, "undefined")) {
619 return false; // Not set (and *is_defined is also false)
622 *is_defined = true;
624 if (base::EqualsASCII(value, "true"))
625 return true;
627 if (base::EqualsASCII(value, "mixed"))
628 *is_mixed = true;
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) {
650 return false;
653 BrowserAccessibility* parent = GetParent();
654 if (!parent)
655 return false;
657 BrowserAccessibility* grandparent = parent->GetParent();
658 if (!grandparent)
659 return false;
661 return grandparent->GetRole() == ui::AX_ROLE_IFRAME_PRESENTATIONAL;
664 bool BrowserAccessibility::IsControl() const {
665 switch (GetRole()) {
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:
690 return true;
691 default:
692 return false;
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());
702 int len = 0;
703 for (size_t i = 0; i < InternalChildCount(); ++i)
704 len += InternalGetChild(i)->GetStaticTextLenRecursive();
705 return len;
708 void BrowserAccessibility::FixEmptyBounds(gfx::Rect* bounds) const
710 if (bounds->width() > 0 && bounds->height() > 0)
711 return;
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)
721 continue;
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;
726 continue;
729 // Union each additional child's bounds.
730 bounds->Union(child_bounds);
734 gfx::Rect BrowserAccessibility::ElementBoundsToLocalBounds(gfx::Rect bounds)
735 const {
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
738 // nested web area.
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);
743 while (parent) {
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
752 // into account.
753 if (parent->GetRole() == ui::AX_ROLE_ROOT_WEB_AREA &&
754 !manager()->UseRootScrollOffsetsWhenComputingBounds()) {
755 break;
758 if (parent->GetRole() == ui::AX_ROLE_WEB_AREA ||
759 parent->GetRole() == ui::AX_ROLE_ROOT_WEB_AREA) {
760 int sx = 0;
761 int sy = 0;
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();
771 return bounds;
774 } // namespace content