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 "base/logging.h"
8 #include "base/strings/string_number_conversions.h"
9 #include "base/strings/string_util.h"
10 #include "base/strings/utf_string_conversions.h"
11 #include "content/browser/accessibility/browser_accessibility_manager.h"
12 #include "content/common/accessibility_messages.h"
16 #if !defined(OS_MACOSX) && \
19 // We have subclassess of BrowserAccessibility on Mac and Win. For any other
20 // platform, instantiate the base class.
22 BrowserAccessibility
* BrowserAccessibility::Create() {
23 return new BrowserAccessibility();
27 BrowserAccessibility::BrowserAccessibility()
32 BrowserAccessibility::~BrowserAccessibility() {
35 void BrowserAccessibility::Init(BrowserAccessibilityManager
* manager
,
41 void BrowserAccessibility::OnDataChanged() {
42 GetStringAttribute(ui::AX_ATTR_NAME
, &name_
);
43 GetStringAttribute(ui::AX_ATTR_VALUE
, &value_
);
46 bool BrowserAccessibility::PlatformIsLeaf() const {
47 if (InternalChildCount() == 0)
50 // All of these roles may have children that we use as internal
51 // implementation details, but we want to expose them as leaves
52 // to platform accessibility APIs.
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_AREA
:
58 case ui::AX_ROLE_TEXT_FIELD
:
65 uint32
BrowserAccessibility::PlatformChildCount() const {
66 return PlatformIsLeaf() ? 0 : InternalChildCount();
69 bool BrowserAccessibility::IsNative() const {
73 bool BrowserAccessibility::IsDescendantOf(
74 BrowserAccessibility
* ancestor
) {
75 if (this == ancestor
) {
77 } else if (GetParent()) {
78 return GetParent()->IsDescendantOf(ancestor
);
84 BrowserAccessibility
* BrowserAccessibility::PlatformGetChild(
85 uint32 child_index
) const {
86 DCHECK(child_index
< InternalChildCount());
87 BrowserAccessibility
* result
= InternalGetChild(child_index
);
89 if (result
->HasBoolAttribute(ui::AX_ATTR_IS_AX_TREE_HOST
)) {
90 BrowserAccessibilityManager
* child_manager
=
91 manager_
->delegate()->AccessibilityGetChildFrame(result
->GetId());
93 result
= child_manager
->GetRoot();
99 BrowserAccessibility
* BrowserAccessibility::GetPreviousSibling() {
100 if (GetParent() && GetIndexInParent() > 0)
101 return GetParent()->InternalGetChild(GetIndexInParent() - 1);
106 BrowserAccessibility
* BrowserAccessibility::GetNextSibling() {
108 GetIndexInParent() >= 0 &&
109 GetIndexInParent() < static_cast<int>(
110 GetParent()->InternalChildCount() - 1)) {
111 return GetParent()->InternalGetChild(GetIndexInParent() + 1);
117 uint32
BrowserAccessibility::InternalChildCount() const {
118 if (!node_
|| !manager_
)
120 return static_cast<uint32
>(node_
->child_count());
123 BrowserAccessibility
* BrowserAccessibility::InternalGetChild(
124 uint32 child_index
) const {
125 if (!node_
|| !manager_
)
127 return manager_
->GetFromAXNode(node_
->children()[child_index
]);
130 BrowserAccessibility
* BrowserAccessibility::GetParent() const {
131 if (!node_
|| !manager_
)
133 ui::AXNode
* parent
= node_
->parent();
135 return manager_
->GetFromAXNode(parent
);
137 if (!manager_
->delegate())
140 BrowserAccessibility
* host_node
=
141 manager_
->delegate()->AccessibilityGetParentFrame();
145 return host_node
->GetParent();
148 int32
BrowserAccessibility::GetIndexInParent() const {
149 return node_
? node_
->index_in_parent() : -1;
152 int32
BrowserAccessibility::GetId() const {
153 return node_
? node_
->id() : -1;
156 const ui::AXNodeData
& BrowserAccessibility::GetData() const {
157 CR_DEFINE_STATIC_LOCAL(ui::AXNodeData
, empty_data
, ());
159 return node_
->data();
164 gfx::Rect
BrowserAccessibility::GetLocation() const {
165 return GetData().location
;
168 int32
BrowserAccessibility::GetRole() const {
169 return GetData().role
;
172 int32
BrowserAccessibility::GetState() const {
173 return GetData().state
;
176 const BrowserAccessibility::HtmlAttributes
&
177 BrowserAccessibility::GetHtmlAttributes() const {
178 return GetData().html_attributes
;
181 gfx::Rect
BrowserAccessibility::GetLocalBoundsRect() const {
182 gfx::Rect bounds
= GetLocation();
184 // Walk up the parent chain. Every time we encounter a Web Area, offset
185 // based on the scroll bars and then offset based on the origin of that
187 BrowserAccessibility
* parent
= GetParentForBoundsCalculation();
188 bool need_to_offset_web_area
=
189 (GetRole() == ui::AX_ROLE_WEB_AREA
||
190 GetRole() == ui::AX_ROLE_ROOT_WEB_AREA
);
192 if (need_to_offset_web_area
&&
193 parent
->GetLocation().width() > 0 &&
194 parent
->GetLocation().height() > 0) {
195 bounds
.Offset(parent
->GetLocation().x(), parent
->GetLocation().y());
196 need_to_offset_web_area
= false;
199 // On some platforms, we don't want to take the root scroll offsets
201 if (parent
->GetRole() == ui::AX_ROLE_ROOT_WEB_AREA
&&
202 !manager()->UseRootScrollOffsetsWhenComputingBounds()) {
206 if (parent
->GetRole() == ui::AX_ROLE_WEB_AREA
||
207 parent
->GetRole() == ui::AX_ROLE_ROOT_WEB_AREA
) {
210 if (parent
->GetIntAttribute(ui::AX_ATTR_SCROLL_X
, &sx
) &&
211 parent
->GetIntAttribute(ui::AX_ATTR_SCROLL_Y
, &sy
)) {
212 bounds
.Offset(-sx
, -sy
);
214 need_to_offset_web_area
= true;
216 parent
= parent
->GetParentForBoundsCalculation();
222 gfx::Rect
BrowserAccessibility::GetGlobalBoundsRect() const {
223 gfx::Rect bounds
= GetLocalBoundsRect();
225 // Adjust the bounds by the top left corner of the containing view's bounds
226 // in screen coordinates.
227 bounds
.Offset(manager_
->GetViewBounds().OffsetFromOrigin());
232 gfx::Rect
BrowserAccessibility::GetLocalBoundsForRange(int start
, int len
)
234 if (GetRole() != ui::AX_ROLE_STATIC_TEXT
) {
235 // Apply recursively to all static text descendants. For example, if
236 // you call it on a div with two text node children, it just calls
237 // GetLocalBoundsForRange on each of the two children (adjusting
238 // |start| for each one) and unions the resulting rects.
240 for (size_t i
= 0; i
< InternalChildCount(); ++i
) {
241 BrowserAccessibility
* child
= InternalGetChild(i
);
242 int child_len
= child
->GetStaticTextLenRecursive();
243 if (start
< child_len
&& start
+ len
> 0) {
244 gfx::Rect child_rect
= child
->GetLocalBoundsForRange(start
, len
);
245 bounds
.Union(child_rect
);
252 int end
= start
+ len
;
257 for (size_t i
= 0; i
< InternalChildCount() && child_end
< start
+ len
; ++i
) {
258 BrowserAccessibility
* child
= InternalGetChild(i
);
259 DCHECK_EQ(child
->GetRole(), ui::AX_ROLE_INLINE_TEXT_BOX
);
260 std::string child_text
;
261 child
->GetStringAttribute(ui::AX_ATTR_VALUE
, &child_text
);
262 int child_len
= static_cast<int>(child_text
.size());
263 child_start
= child_end
;
264 child_end
+= child_len
;
266 if (child_end
< start
)
269 int overlap_start
= std::max(start
, child_start
);
270 int overlap_end
= std::min(end
, child_end
);
272 int local_start
= overlap_start
- child_start
;
273 int local_end
= overlap_end
- child_start
;
275 gfx::Rect child_rect
= child
->GetLocation();
276 int text_direction
= child
->GetIntAttribute(
277 ui::AX_ATTR_TEXT_DIRECTION
);
278 const std::vector
<int32
>& character_offsets
= child
->GetIntListAttribute(
279 ui::AX_ATTR_CHARACTER_OFFSETS
);
280 int start_pixel_offset
=
281 local_start
> 0 ? character_offsets
[local_start
- 1] : 0;
282 int end_pixel_offset
=
283 local_end
> 0 ? character_offsets
[local_end
- 1] : 0;
285 gfx::Rect child_overlap_rect
;
286 switch (text_direction
) {
287 case ui::AX_TEXT_DIRECTION_NONE
:
288 case ui::AX_TEXT_DIRECTION_LR
: {
289 int left
= child_rect
.x() + start_pixel_offset
;
290 int right
= child_rect
.x() + end_pixel_offset
;
291 child_overlap_rect
= gfx::Rect(left
, child_rect
.y(),
292 right
- left
, child_rect
.height());
295 case ui::AX_TEXT_DIRECTION_RL
: {
296 int right
= child_rect
.right() - start_pixel_offset
;
297 int left
= child_rect
.right() - end_pixel_offset
;
298 child_overlap_rect
= gfx::Rect(left
, child_rect
.y(),
299 right
- left
, child_rect
.height());
302 case ui::AX_TEXT_DIRECTION_TB
: {
303 int top
= child_rect
.y() + start_pixel_offset
;
304 int bottom
= child_rect
.y() + end_pixel_offset
;
305 child_overlap_rect
= gfx::Rect(child_rect
.x(), top
,
306 child_rect
.width(), bottom
- top
);
309 case ui::AX_TEXT_DIRECTION_BT
: {
310 int bottom
= child_rect
.bottom() - start_pixel_offset
;
311 int top
= child_rect
.bottom() - end_pixel_offset
;
312 child_overlap_rect
= gfx::Rect(child_rect
.x(), top
,
313 child_rect
.width(), bottom
- top
);
320 if (bounds
.width() == 0 && bounds
.height() == 0)
321 bounds
= child_overlap_rect
;
323 bounds
.Union(child_overlap_rect
);
329 gfx::Rect
BrowserAccessibility::GetGlobalBoundsForRange(int start
, int len
)
331 gfx::Rect bounds
= GetLocalBoundsForRange(start
, len
);
333 // Adjust the bounds by the top left corner of the containing view's bounds
334 // in screen coordinates.
335 bounds
.Offset(manager_
->GetViewBounds().OffsetFromOrigin());
340 BrowserAccessibility
* BrowserAccessibility::BrowserAccessibilityForPoint(
341 const gfx::Point
& point
) {
342 // The best result found that's a child of this object.
343 BrowserAccessibility
* child_result
= NULL
;
344 // The best result that's an indirect descendant like grandchild, etc.
345 BrowserAccessibility
* descendant_result
= NULL
;
347 // Walk the children recursively looking for the BrowserAccessibility that
348 // most tightly encloses the specified point. Walk backwards so that in
349 // the absence of any other information, we assume the object that occurs
350 // later in the tree is on top of one that comes before it.
351 for (int i
= static_cast<int>(PlatformChildCount()) - 1; i
>= 0; --i
) {
352 BrowserAccessibility
* child
= PlatformGetChild(i
);
354 // Skip table columns because cells are only contained in rows,
356 if (child
->GetRole() == ui::AX_ROLE_COLUMN
)
359 if (child
->GetGlobalBoundsRect().Contains(point
)) {
360 BrowserAccessibility
* result
= child
->BrowserAccessibilityForPoint(point
);
361 if (result
== child
&& !child_result
)
362 child_result
= result
;
363 if (result
!= child
&& !descendant_result
)
364 descendant_result
= result
;
367 if (child_result
&& descendant_result
)
371 // Explanation of logic: it's possible that this point overlaps more than
372 // one child of this object. If so, as a heuristic we prefer if the point
373 // overlaps a descendant of one of the two children and not the other.
374 // As an example, suppose you have two rows of buttons - the buttons don't
375 // overlap, but the rows do. Without this heuristic, we'd greedily only
376 // consider one of the containers.
377 if (descendant_result
)
378 return descendant_result
;
385 void BrowserAccessibility::Destroy() {
386 // Allow the object to fire a TextRemoved notification.
390 manager_
->NotifyAccessibilityEvent(ui::AX_EVENT_HIDE
, this);
394 NativeReleaseReference();
397 void BrowserAccessibility::NativeReleaseReference() {
401 bool BrowserAccessibility::HasBoolAttribute(
402 ui::AXBoolAttribute attribute
) const {
403 const ui::AXNodeData
& data
= GetData();
404 for (size_t i
= 0; i
< data
.bool_attributes
.size(); ++i
) {
405 if (data
.bool_attributes
[i
].first
== attribute
)
413 bool BrowserAccessibility::GetBoolAttribute(
414 ui::AXBoolAttribute attribute
) const {
415 const ui::AXNodeData
& data
= GetData();
416 for (size_t i
= 0; i
< data
.bool_attributes
.size(); ++i
) {
417 if (data
.bool_attributes
[i
].first
== attribute
)
418 return data
.bool_attributes
[i
].second
;
424 bool BrowserAccessibility::GetBoolAttribute(
425 ui::AXBoolAttribute attribute
, bool* value
) const {
426 const ui::AXNodeData
& data
= GetData();
427 for (size_t i
= 0; i
< data
.bool_attributes
.size(); ++i
) {
428 if (data
.bool_attributes
[i
].first
== attribute
) {
429 *value
= data
.bool_attributes
[i
].second
;
437 bool BrowserAccessibility::HasFloatAttribute(
438 ui::AXFloatAttribute attribute
) const {
439 const ui::AXNodeData
& data
= GetData();
440 for (size_t i
= 0; i
< data
.float_attributes
.size(); ++i
) {
441 if (data
.float_attributes
[i
].first
== attribute
)
448 float BrowserAccessibility::GetFloatAttribute(
449 ui::AXFloatAttribute attribute
) const {
450 const ui::AXNodeData
& data
= GetData();
451 for (size_t i
= 0; i
< data
.float_attributes
.size(); ++i
) {
452 if (data
.float_attributes
[i
].first
== attribute
)
453 return data
.float_attributes
[i
].second
;
459 bool BrowserAccessibility::GetFloatAttribute(
460 ui::AXFloatAttribute attribute
, float* value
) const {
461 const ui::AXNodeData
& data
= GetData();
462 for (size_t i
= 0; i
< data
.float_attributes
.size(); ++i
) {
463 if (data
.float_attributes
[i
].first
== attribute
) {
464 *value
= data
.float_attributes
[i
].second
;
472 bool BrowserAccessibility::HasIntAttribute(
473 ui::AXIntAttribute attribute
) const {
474 const ui::AXNodeData
& data
= GetData();
475 for (size_t i
= 0; i
< data
.int_attributes
.size(); ++i
) {
476 if (data
.int_attributes
[i
].first
== attribute
)
483 int BrowserAccessibility::GetIntAttribute(ui::AXIntAttribute attribute
) const {
484 const ui::AXNodeData
& data
= GetData();
485 for (size_t i
= 0; i
< data
.int_attributes
.size(); ++i
) {
486 if (data
.int_attributes
[i
].first
== attribute
)
487 return data
.int_attributes
[i
].second
;
493 bool BrowserAccessibility::GetIntAttribute(
494 ui::AXIntAttribute attribute
, int* value
) const {
495 const ui::AXNodeData
& data
= GetData();
496 for (size_t i
= 0; i
< data
.int_attributes
.size(); ++i
) {
497 if (data
.int_attributes
[i
].first
== attribute
) {
498 *value
= data
.int_attributes
[i
].second
;
506 bool BrowserAccessibility::HasStringAttribute(
507 ui::AXStringAttribute attribute
) const {
508 const ui::AXNodeData
& data
= GetData();
509 for (size_t i
= 0; i
< data
.string_attributes
.size(); ++i
) {
510 if (data
.string_attributes
[i
].first
== attribute
)
517 const std::string
& BrowserAccessibility::GetStringAttribute(
518 ui::AXStringAttribute attribute
) const {
519 const ui::AXNodeData
& data
= GetData();
520 CR_DEFINE_STATIC_LOCAL(std::string
, empty_string
, ());
521 for (size_t i
= 0; i
< data
.string_attributes
.size(); ++i
) {
522 if (data
.string_attributes
[i
].first
== attribute
)
523 return data
.string_attributes
[i
].second
;
529 bool BrowserAccessibility::GetStringAttribute(
530 ui::AXStringAttribute attribute
, std::string
* value
) const {
531 const ui::AXNodeData
& data
= GetData();
532 for (size_t i
= 0; i
< data
.string_attributes
.size(); ++i
) {
533 if (data
.string_attributes
[i
].first
== attribute
) {
534 *value
= data
.string_attributes
[i
].second
;
542 base::string16
BrowserAccessibility::GetString16Attribute(
543 ui::AXStringAttribute attribute
) const {
544 std::string value_utf8
;
545 if (!GetStringAttribute(attribute
, &value_utf8
))
546 return base::string16();
547 return base::UTF8ToUTF16(value_utf8
);
550 bool BrowserAccessibility::GetString16Attribute(
551 ui::AXStringAttribute attribute
,
552 base::string16
* value
) const {
553 std::string value_utf8
;
554 if (!GetStringAttribute(attribute
, &value_utf8
))
556 *value
= base::UTF8ToUTF16(value_utf8
);
560 void BrowserAccessibility::SetStringAttribute(
561 ui::AXStringAttribute attribute
, const std::string
& value
) {
564 ui::AXNodeData data
= GetData();
565 for (size_t i
= 0; i
< data
.string_attributes
.size(); ++i
) {
566 if (data
.string_attributes
[i
].first
== attribute
) {
567 data
.string_attributes
[i
].second
= value
;
568 node_
->SetData(data
);
572 if (!value
.empty()) {
573 data
.string_attributes
.push_back(std::make_pair(attribute
, value
));
574 node_
->SetData(data
);
578 bool BrowserAccessibility::HasIntListAttribute(
579 ui::AXIntListAttribute attribute
) const {
580 const ui::AXNodeData
& data
= GetData();
581 for (size_t i
= 0; i
< data
.intlist_attributes
.size(); ++i
) {
582 if (data
.intlist_attributes
[i
].first
== attribute
)
589 const std::vector
<int32
>& BrowserAccessibility::GetIntListAttribute(
590 ui::AXIntListAttribute attribute
) const {
591 const ui::AXNodeData
& data
= GetData();
592 CR_DEFINE_STATIC_LOCAL(std::vector
<int32
>, empty_vector
, ());
593 for (size_t i
= 0; i
< data
.intlist_attributes
.size(); ++i
) {
594 if (data
.intlist_attributes
[i
].first
== attribute
)
595 return data
.intlist_attributes
[i
].second
;
601 bool BrowserAccessibility::GetIntListAttribute(
602 ui::AXIntListAttribute attribute
,
603 std::vector
<int32
>* value
) const {
604 const ui::AXNodeData
& data
= GetData();
605 for (size_t i
= 0; i
< data
.intlist_attributes
.size(); ++i
) {
606 if (data
.intlist_attributes
[i
].first
== attribute
) {
607 *value
= data
.intlist_attributes
[i
].second
;
615 bool BrowserAccessibility::GetHtmlAttribute(
616 const char* html_attr
, std::string
* value
) const {
617 for (size_t i
= 0; i
< GetHtmlAttributes().size(); ++i
) {
618 const std::string
& attr
= GetHtmlAttributes()[i
].first
;
619 if (LowerCaseEqualsASCII(attr
, html_attr
)) {
620 *value
= GetHtmlAttributes()[i
].second
;
628 bool BrowserAccessibility::GetHtmlAttribute(
629 const char* html_attr
, base::string16
* value
) const {
630 std::string value_utf8
;
631 if (!GetHtmlAttribute(html_attr
, &value_utf8
))
633 *value
= base::UTF8ToUTF16(value_utf8
);
637 bool BrowserAccessibility::GetAriaTristate(
638 const char* html_attr
,
640 bool* is_mixed
) const {
644 base::string16 value
;
645 if (!GetHtmlAttribute(html_attr
, &value
) ||
647 EqualsASCII(value
, "undefined")) {
648 return false; // Not set (and *is_defined is also false)
653 if (EqualsASCII(value
, "true"))
656 if (EqualsASCII(value
, "mixed"))
659 return false; // Not set
662 bool BrowserAccessibility::HasState(ui::AXState state_enum
) const {
663 return (GetState() >> state_enum
) & 1;
666 bool BrowserAccessibility::IsCellOrTableHeaderRole() const {
667 return (GetRole() == ui::AX_ROLE_CELL
||
668 GetRole() == ui::AX_ROLE_COLUMN_HEADER
||
669 GetRole() == ui::AX_ROLE_ROW_HEADER
);
672 bool BrowserAccessibility::IsEditableText() const {
673 // These roles don't have readonly set, but they're not editable text.
674 if (GetRole() == ui::AX_ROLE_SCROLL_AREA
||
675 GetRole() == ui::AX_ROLE_COLUMN
||
676 GetRole() == ui::AX_ROLE_TABLE_HEADER_CONTAINER
) {
680 // Note: WebAXStateReadonly being false means it's either a text control,
681 // or contenteditable. We also check for editable text roles to cover
682 // another element that has role=textbox set on it.
683 return (!HasState(ui::AX_STATE_READ_ONLY
) ||
684 GetRole() == ui::AX_ROLE_TEXT_FIELD
||
685 GetRole() == ui::AX_ROLE_TEXT_AREA
);
688 bool BrowserAccessibility::IsWebAreaForPresentationalIframe() const {
689 if (GetRole() != ui::AX_ROLE_WEB_AREA
&&
690 GetRole() != ui::AX_ROLE_ROOT_WEB_AREA
) {
694 BrowserAccessibility
* parent
= GetParent();
698 BrowserAccessibility
* grandparent
= parent
->GetParent();
702 return grandparent
->GetRole() == ui::AX_ROLE_IFRAME_PRESENTATIONAL
;
705 std::string
BrowserAccessibility::GetTextRecursive() const {
706 if (!name_
.empty()) {
711 for (uint32 i
= 0; i
< PlatformChildCount(); ++i
)
712 result
+= PlatformGetChild(i
)->GetTextRecursive();
716 int BrowserAccessibility::GetStaticTextLenRecursive() const {
717 if (GetRole() == ui::AX_ROLE_STATIC_TEXT
)
718 return static_cast<int>(GetStringAttribute(ui::AX_ATTR_VALUE
).size());
721 for (size_t i
= 0; i
< InternalChildCount(); ++i
)
722 len
+= InternalGetChild(i
)->GetStaticTextLenRecursive();
726 BrowserAccessibility
* BrowserAccessibility::GetParentForBoundsCalculation()
728 if (!node_
|| !manager_
)
730 ui::AXNode
* parent
= node_
->parent();
732 return manager_
->GetFromAXNode(parent
);
734 if (!manager_
->delegate())
737 return manager_
->delegate()->AccessibilityGetParentFrame();
740 } // namespace content