Add long running gmail memory benchmark for background tab.
[chromium-blink-merge.git] / content / browser / accessibility / browser_accessibility.cc
blobf3cf3779f9f2909d7afcc9495d01950f45a0145f
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 (GetBoolAttribute(ui::AX_ATTR_IS_AX_TREE_HOST)) {
65 // Check if the child frame currently exists.
66 if (manager_->delegate()->AccessibilityGetChildFrame(GetId()))
67 return 1;
69 return 0;
72 return PlatformIsLeaf() ? 0 : InternalChildCount();
75 bool BrowserAccessibility::IsNative() const {
76 return false;
79 bool BrowserAccessibility::IsDescendantOf(
80 BrowserAccessibility* ancestor) {
81 if (this == ancestor) {
82 return true;
83 } else if (GetParent()) {
84 return GetParent()->IsDescendantOf(ancestor);
87 return false;
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());
98 if (child_manager)
99 result = child_manager->GetRoot();
100 } else {
101 result = InternalGetChild(child_index);
104 return result;
107 bool BrowserAccessibility::PlatformIsChildOfLeaf() const {
108 BrowserAccessibility* ancestor = GetParent();
109 while (ancestor) {
110 if (ancestor->PlatformIsLeaf())
111 return true;
112 ancestor = ancestor->GetParent();
115 return false;
118 BrowserAccessibility* BrowserAccessibility::GetPreviousSibling() {
119 if (GetParent() && GetIndexInParent() > 0)
120 return GetParent()->InternalGetChild(GetIndexInParent() - 1);
122 return NULL;
125 BrowserAccessibility* BrowserAccessibility::GetNextSibling() {
126 if (GetParent() &&
127 GetIndexInParent() >= 0 &&
128 GetIndexInParent() < static_cast<int>(
129 GetParent()->InternalChildCount() - 1)) {
130 return GetParent()->InternalGetChild(GetIndexInParent() + 1);
133 return NULL;
136 uint32 BrowserAccessibility::InternalChildCount() const {
137 if (!node_ || !manager_)
138 return 0;
139 return static_cast<uint32>(node_->child_count());
142 BrowserAccessibility* BrowserAccessibility::InternalGetChild(
143 uint32 child_index) const {
144 if (!node_ || !manager_)
145 return NULL;
146 return manager_->GetFromAXNode(node_->ChildAtIndex(child_index));
149 BrowserAccessibility* BrowserAccessibility::GetParent() const {
150 if (!node_ || !manager_)
151 return NULL;
152 ui::AXNode* parent = node_->parent();
153 if (parent)
154 return manager_->GetFromAXNode(parent);
156 if (!manager_->delegate())
157 return NULL;
159 BrowserAccessibility* host_node =
160 manager_->delegate()->AccessibilityGetParentFrame();
161 if (!host_node)
162 return NULL;
164 return host_node;
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, ());
177 if (node_)
178 return node_->data();
179 else
180 return empty_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());
213 return bounds;
216 gfx::Rect BrowserAccessibility::GetLocalBoundsForRange(int start, int len)
217 const {
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.
223 gfx::Rect bounds;
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);
231 start -= child_len;
233 return ElementBoundsToLocalBounds(bounds);
236 int end = start + len;
237 int child_start = 0;
238 int child_end = 0;
240 gfx::Rect bounds;
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.";
246 continue;
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)
256 continue;
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());
282 break;
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());
289 break;
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);
296 break;
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);
303 break;
305 default:
306 NOTREACHED();
309 if (bounds.width() == 0 && bounds.height() == 0)
310 bounds = child_overlap_rect;
311 else
312 bounds.Union(child_overlap_rect);
315 return ElementBoundsToLocalBounds(bounds);
318 gfx::Rect BrowserAccessibility::GetGlobalBoundsForRange(int start, int len)
319 const {
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());
326 return bounds;
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;
336 switch (GetRole()) {
337 case ui::AX_ROLE_STATIC_TEXT: {
338 int prev_word_start = word_start_not_found;
339 int child_start = 0;
340 int child_end = 0;
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(
349 ui::AX_ATTR_VALUE);
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;
357 continue;
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;
370 } else {
371 word_start = child_start + *(iter - 1);
373 } else {
374 NOTREACHED();
376 break;
379 // No word start that is greater than the requested offset has been
380 // found.
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;
386 } else {
387 NOTREACHED();
390 return word_start;
393 case ui::AX_ROLE_LINE_BREAK:
394 // Words never start at a line break.
395 return word_start_not_found;
397 default:
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;
403 int child_start = 0;
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)) {
418 break;
421 child_start += child_len;
422 if (start >= child_len)
423 start -= child_len;
424 else
425 start = -1;
427 return word_start;
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,
446 // not columns.
447 if (child->GetRole() == ui::AX_ROLE_COLUMN)
448 continue;
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)
459 break;
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;
470 if (child_result)
471 return child_result;
473 return this;
476 void BrowserAccessibility::Destroy() {
477 // Allow the object to fire a TextRemoved notification.
478 manager_->NotifyAccessibilityEvent(ui::AX_EVENT_HIDE, this);
479 node_ = NULL;
480 manager_ = NULL;
482 NativeReleaseReference();
485 void BrowserAccessibility::NativeReleaseReference() {
486 delete this;
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,
587 bool* is_defined,
588 bool* is_mixed) const {
589 *is_defined = false;
590 *is_mixed = false;
592 base::string16 value;
593 if (!GetHtmlAttribute(html_attr, &value) ||
594 value.empty() ||
595 base::EqualsASCII(value, "undefined")) {
596 return false; // Not set (and *is_defined is also false)
599 *is_defined = true;
601 if (base::EqualsASCII(value, "true"))
602 return true;
604 if (base::EqualsASCII(value, "mixed"))
605 *is_mixed = true;
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) {
625 return false;
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) {
638 return false;
641 BrowserAccessibility* parent = GetParent();
642 if (!parent)
643 return false;
645 BrowserAccessibility* grandparent = parent->GetParent();
646 if (!grandparent)
647 return false;
649 return grandparent->GetRole() == ui::AX_ROLE_IFRAME_PRESENTATIONAL;
652 bool BrowserAccessibility::IsControl() const {
653 switch (GetRole()) {
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:
678 return true;
679 default:
680 return false;
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());
690 int len = 0;
691 for (size_t i = 0; i < InternalChildCount(); ++i)
692 len += InternalGetChild(i)->GetStaticTextLenRecursive();
693 return len;
696 BrowserAccessibility* BrowserAccessibility::GetParentForBoundsCalculation()
697 const {
698 if (!node_ || !manager_)
699 return NULL;
700 ui::AXNode* parent = node_->parent();
701 if (parent)
702 return manager_->GetFromAXNode(parent);
704 if (!manager_->delegate())
705 return NULL;
707 return manager_->delegate()->AccessibilityGetParentFrame();
710 void BrowserAccessibility::FixEmptyBounds(gfx::Rect* bounds) const
712 if (bounds->width() > 0 && bounds->height() > 0)
713 return;
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)
723 continue;
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;
728 continue;
731 // Union each additional child's bounds.
732 bounds->Union(child_bounds);
736 gfx::Rect BrowserAccessibility::ElementBoundsToLocalBounds(gfx::Rect bounds)
737 const {
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
740 // nested web area.
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);
745 while (parent) {
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
754 // into account.
755 if (parent->GetRole() == ui::AX_ROLE_ROOT_WEB_AREA &&
756 !manager()->UseRootScrollOffsetsWhenComputingBounds()) {
757 break;
760 if (parent->GetRole() == ui::AX_ROLE_WEB_AREA ||
761 parent->GetRole() == ui::AX_ROLE_ROOT_WEB_AREA) {
762 int sx = 0;
763 int sy = 0;
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();
773 return bounds;
776 } // namespace content