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) && \
18 !defined(TOOLKIT_GTK) && \
20 // We have subclassess of BrowserAccessibility on Mac, Linux/GTK,
21 // and Win. For any other platform, instantiate the base class.
23 BrowserAccessibility
* BrowserAccessibility::Create() {
24 return new BrowserAccessibility();
28 BrowserAccessibility::BrowserAccessibility()
35 instance_active_(false) {
38 BrowserAccessibility::~BrowserAccessibility() {
41 bool BrowserAccessibility::PlatformIsLeaf() const {
42 if (child_count() == 0)
45 // All of these roles may have children that we use as internal
46 // implementation details, but we want to expose them as leaves
47 // to platform accessibility APIs.
49 case ui::AX_ROLE_EDITABLE_TEXT
:
50 case ui::AX_ROLE_SLIDER
:
51 case ui::AX_ROLE_STATIC_TEXT
:
52 case ui::AX_ROLE_TEXT_AREA
:
53 case ui::AX_ROLE_TEXT_FIELD
:
60 uint32
BrowserAccessibility::PlatformChildCount() const {
61 return PlatformIsLeaf() ? 0 : children_
.size();
64 void BrowserAccessibility::DetachTree(
65 std::vector
<BrowserAccessibility
*>* nodes
) {
66 nodes
->push_back(this);
67 for (size_t i
= 0; i
< children_
.size(); ++i
)
68 children_
[i
]->DetachTree(nodes
);
73 void BrowserAccessibility::InitializeTreeStructure(
74 BrowserAccessibilityManager
* manager
,
75 BrowserAccessibility
* parent
,
77 int32 index_in_parent
) {
80 renderer_id_
= renderer_id
;
81 index_in_parent_
= index_in_parent
;
84 void BrowserAccessibility::InitializeData(const ui::AXNodeData
& src
) {
85 DCHECK_EQ(renderer_id_
, src
.id
);
88 string_attributes_
= src
.string_attributes
;
89 int_attributes_
= src
.int_attributes
;
90 float_attributes_
= src
.float_attributes
;
91 bool_attributes_
= src
.bool_attributes
;
92 intlist_attributes_
= src
.intlist_attributes
;
93 html_attributes_
= src
.html_attributes
;
94 location_
= src
.location
;
95 instance_active_
= true;
97 GetStringAttribute(ui::AX_ATTR_NAME
, &name_
);
98 GetStringAttribute(ui::AX_ATTR_VALUE
, &value_
);
103 bool BrowserAccessibility::IsNative() const {
107 void BrowserAccessibility::SwapChildren(
108 std::vector
<BrowserAccessibility
*>& children
) {
109 children
.swap(children_
);
112 void BrowserAccessibility::UpdateParent(BrowserAccessibility
* parent
,
113 int index_in_parent
) {
115 index_in_parent_
= index_in_parent
;
118 void BrowserAccessibility::SetLocation(const gfx::Rect
& new_location
) {
119 location_
= new_location
;
122 bool BrowserAccessibility::IsDescendantOf(
123 BrowserAccessibility
* ancestor
) {
124 if (this == ancestor
) {
126 } else if (parent_
) {
127 return parent_
->IsDescendantOf(ancestor
);
133 BrowserAccessibility
* BrowserAccessibility::PlatformGetChild(
134 uint32 child_index
) const {
135 DCHECK(child_index
< children_
.size());
136 return children_
[child_index
];
139 BrowserAccessibility
* BrowserAccessibility::GetPreviousSibling() {
140 if (parent_
&& index_in_parent_
> 0)
141 return parent_
->children_
[index_in_parent_
- 1];
146 BrowserAccessibility
* BrowserAccessibility::GetNextSibling() {
148 index_in_parent_
>= 0 &&
149 index_in_parent_
< static_cast<int>(parent_
->children_
.size() - 1)) {
150 return parent_
->children_
[index_in_parent_
+ 1];
156 gfx::Rect
BrowserAccessibility::GetLocalBoundsRect() const {
157 gfx::Rect bounds
= location_
;
159 // Walk up the parent chain. Every time we encounter a Web Area, offset
160 // based on the scroll bars and then offset based on the origin of that
162 BrowserAccessibility
* parent
= parent_
;
163 bool need_to_offset_web_area
=
164 (role_
== ui::AX_ROLE_WEB_AREA
||
165 role_
== ui::AX_ROLE_ROOT_WEB_AREA
);
167 if (need_to_offset_web_area
&&
168 parent
->location().width() > 0 &&
169 parent
->location().height() > 0) {
170 bounds
.Offset(parent
->location().x(), parent
->location().y());
171 need_to_offset_web_area
= false;
174 // On some platforms, we don't want to take the root scroll offsets
176 if (parent
->role() == ui::AX_ROLE_ROOT_WEB_AREA
&&
177 !manager()->UseRootScrollOffsetsWhenComputingBounds()) {
181 if (parent
->role() == ui::AX_ROLE_WEB_AREA
||
182 parent
->role() == ui::AX_ROLE_ROOT_WEB_AREA
) {
185 if (parent
->GetIntAttribute(ui::AX_ATTR_SCROLL_X
, &sx
) &&
186 parent
->GetIntAttribute(ui::AX_ATTR_SCROLL_Y
, &sy
)) {
187 bounds
.Offset(-sx
, -sy
);
189 need_to_offset_web_area
= true;
191 parent
= parent
->parent();
197 gfx::Rect
BrowserAccessibility::GetGlobalBoundsRect() const {
198 gfx::Rect bounds
= GetLocalBoundsRect();
200 // Adjust the bounds by the top left corner of the containing view's bounds
201 // in screen coordinates.
202 bounds
.Offset(manager_
->GetViewBounds().OffsetFromOrigin());
207 gfx::Rect
BrowserAccessibility::GetLocalBoundsForRange(int start
, int len
)
209 if (role() != ui::AX_ROLE_STATIC_TEXT
) {
210 // Apply recursively to all static text descendants. For example, if
211 // you call it on a div with two text node children, it just calls
212 // GetLocalBoundsForRange on each of the two children (adjusting
213 // |start| for each one) and unions the resulting rects.
215 for (size_t i
= 0; i
< children_
.size(); ++i
) {
216 BrowserAccessibility
* child
= children_
[i
];
217 int child_len
= child
->GetStaticTextLenRecursive();
218 if (start
< child_len
&& start
+ len
> 0) {
219 gfx::Rect child_rect
= child
->GetLocalBoundsForRange(start
, len
);
220 bounds
.Union(child_rect
);
227 int end
= start
+ len
;
232 for (size_t i
= 0; i
< children_
.size() && child_end
< start
+ len
; ++i
) {
233 BrowserAccessibility
* child
= children_
[i
];
234 DCHECK_EQ(child
->role(), ui::AX_ROLE_INLINE_TEXT_BOX
);
235 std::string child_text
;
236 child
->GetStringAttribute(ui::AX_ATTR_VALUE
, &child_text
);
237 int child_len
= static_cast<int>(child_text
.size());
238 child_start
= child_end
;
239 child_end
+= child_len
;
241 if (child_end
< start
)
244 int overlap_start
= std::max(start
, child_start
);
245 int overlap_end
= std::min(end
, child_end
);
247 int local_start
= overlap_start
- child_start
;
248 int local_end
= overlap_end
- child_start
;
250 gfx::Rect child_rect
= child
->location();
251 int text_direction
= child
->GetIntAttribute(
252 ui::AX_ATTR_TEXT_DIRECTION
);
253 const std::vector
<int32
>& character_offsets
= child
->GetIntListAttribute(
254 ui::AX_ATTR_CHARACTER_OFFSETS
);
255 int start_pixel_offset
=
256 local_start
> 0 ? character_offsets
[local_start
- 1] : 0;
257 int end_pixel_offset
=
258 local_end
> 0 ? character_offsets
[local_end
- 1] : 0;
260 gfx::Rect child_overlap_rect
;
261 switch (text_direction
) {
262 case ui::AX_TEXT_DIRECTION_LR
: {
263 int left
= child_rect
.x() + start_pixel_offset
;
264 int right
= child_rect
.x() + end_pixel_offset
;
265 child_overlap_rect
= gfx::Rect(left
, child_rect
.y(),
266 right
- left
, child_rect
.height());
269 case ui::AX_TEXT_DIRECTION_RL
: {
270 int right
= child_rect
.right() - start_pixel_offset
;
271 int left
= child_rect
.right() - end_pixel_offset
;
272 child_overlap_rect
= gfx::Rect(left
, child_rect
.y(),
273 right
- left
, child_rect
.height());
276 case ui::AX_TEXT_DIRECTION_TB
: {
277 int top
= child_rect
.y() + start_pixel_offset
;
278 int bottom
= child_rect
.y() + end_pixel_offset
;
279 child_overlap_rect
= gfx::Rect(child_rect
.x(), top
,
280 child_rect
.width(), bottom
- top
);
283 case ui::AX_TEXT_DIRECTION_BT
: {
284 int bottom
= child_rect
.bottom() - start_pixel_offset
;
285 int top
= child_rect
.bottom() - end_pixel_offset
;
286 child_overlap_rect
= gfx::Rect(child_rect
.x(), top
,
287 child_rect
.width(), bottom
- top
);
294 if (bounds
.width() == 0 && bounds
.height() == 0)
295 bounds
= child_overlap_rect
;
297 bounds
.Union(child_overlap_rect
);
303 gfx::Rect
BrowserAccessibility::GetGlobalBoundsForRange(int start
, int len
)
305 gfx::Rect bounds
= GetLocalBoundsForRange(start
, len
);
307 // Adjust the bounds by the top left corner of the containing view's bounds
308 // in screen coordinates.
309 bounds
.Offset(manager_
->GetViewBounds().OffsetFromOrigin());
314 BrowserAccessibility
* BrowserAccessibility::BrowserAccessibilityForPoint(
315 const gfx::Point
& point
) {
316 // Walk the children recursively looking for the BrowserAccessibility that
317 // most tightly encloses the specified point.
318 for (int i
= static_cast<int>(PlatformChildCount()) - 1; i
>= 0; --i
) {
319 BrowserAccessibility
* child
= PlatformGetChild(i
);
320 if (child
->GetGlobalBoundsRect().Contains(point
))
321 return child
->BrowserAccessibilityForPoint(point
);
326 void BrowserAccessibility::Destroy() {
327 for (std::vector
<BrowserAccessibility
*>::iterator iter
= children_
.begin();
328 iter
!= children_
.end();
334 // Allow the object to fire a TextRemoved notification.
339 manager_
->NotifyAccessibilityEvent(ui::AX_EVENT_HIDE
, this);
341 instance_active_
= false;
342 manager_
->RemoveNode(this);
343 NativeReleaseReference();
346 void BrowserAccessibility::NativeReleaseReference() {
350 bool BrowserAccessibility::HasBoolAttribute(
351 ui::AXBoolAttribute attribute
) const {
352 for (size_t i
= 0; i
< bool_attributes_
.size(); ++i
) {
353 if (bool_attributes_
[i
].first
== attribute
)
361 bool BrowserAccessibility::GetBoolAttribute(
362 ui::AXBoolAttribute attribute
) const {
363 for (size_t i
= 0; i
< bool_attributes_
.size(); ++i
) {
364 if (bool_attributes_
[i
].first
== attribute
)
365 return bool_attributes_
[i
].second
;
371 bool BrowserAccessibility::GetBoolAttribute(
372 ui::AXBoolAttribute attribute
, bool* value
) const {
373 for (size_t i
= 0; i
< bool_attributes_
.size(); ++i
) {
374 if (bool_attributes_
[i
].first
== attribute
) {
375 *value
= bool_attributes_
[i
].second
;
383 bool BrowserAccessibility::HasFloatAttribute(
384 ui::AXFloatAttribute attribute
) const {
385 for (size_t i
= 0; i
< float_attributes_
.size(); ++i
) {
386 if (float_attributes_
[i
].first
== attribute
)
393 float BrowserAccessibility::GetFloatAttribute(
394 ui::AXFloatAttribute attribute
) const {
395 for (size_t i
= 0; i
< float_attributes_
.size(); ++i
) {
396 if (float_attributes_
[i
].first
== attribute
)
397 return float_attributes_
[i
].second
;
403 bool BrowserAccessibility::GetFloatAttribute(
404 ui::AXFloatAttribute attribute
, float* value
) const {
405 for (size_t i
= 0; i
< float_attributes_
.size(); ++i
) {
406 if (float_attributes_
[i
].first
== attribute
) {
407 *value
= float_attributes_
[i
].second
;
415 bool BrowserAccessibility::HasIntAttribute(
416 ui::AXIntAttribute attribute
) const {
417 for (size_t i
= 0; i
< int_attributes_
.size(); ++i
) {
418 if (int_attributes_
[i
].first
== attribute
)
425 int BrowserAccessibility::GetIntAttribute(ui::AXIntAttribute attribute
) const {
426 for (size_t i
= 0; i
< int_attributes_
.size(); ++i
) {
427 if (int_attributes_
[i
].first
== attribute
)
428 return int_attributes_
[i
].second
;
434 bool BrowserAccessibility::GetIntAttribute(
435 ui::AXIntAttribute attribute
, int* value
) const {
436 for (size_t i
= 0; i
< int_attributes_
.size(); ++i
) {
437 if (int_attributes_
[i
].first
== attribute
) {
438 *value
= int_attributes_
[i
].second
;
446 bool BrowserAccessibility::HasStringAttribute(
447 ui::AXStringAttribute attribute
) const {
448 for (size_t i
= 0; i
< string_attributes_
.size(); ++i
) {
449 if (string_attributes_
[i
].first
== attribute
)
456 const std::string
& BrowserAccessibility::GetStringAttribute(
457 ui::AXStringAttribute attribute
) const {
458 CR_DEFINE_STATIC_LOCAL(std::string
, empty_string
, ());
459 for (size_t i
= 0; i
< string_attributes_
.size(); ++i
) {
460 if (string_attributes_
[i
].first
== attribute
)
461 return string_attributes_
[i
].second
;
467 bool BrowserAccessibility::GetStringAttribute(
468 ui::AXStringAttribute attribute
, std::string
* value
) const {
469 for (size_t i
= 0; i
< string_attributes_
.size(); ++i
) {
470 if (string_attributes_
[i
].first
== attribute
) {
471 *value
= string_attributes_
[i
].second
;
479 base::string16
BrowserAccessibility::GetString16Attribute(
480 ui::AXStringAttribute attribute
) const {
481 std::string value_utf8
;
482 if (!GetStringAttribute(attribute
, &value_utf8
))
483 return base::string16();
484 return base::UTF8ToUTF16(value_utf8
);
487 bool BrowserAccessibility::GetString16Attribute(
488 ui::AXStringAttribute attribute
,
489 base::string16
* value
) const {
490 std::string value_utf8
;
491 if (!GetStringAttribute(attribute
, &value_utf8
))
493 *value
= base::UTF8ToUTF16(value_utf8
);
497 void BrowserAccessibility::SetStringAttribute(
498 ui::AXStringAttribute attribute
, const std::string
& value
) {
499 for (size_t i
= 0; i
< string_attributes_
.size(); ++i
) {
500 if (string_attributes_
[i
].first
== attribute
) {
501 string_attributes_
[i
].second
= value
;
506 string_attributes_
.push_back(std::make_pair(attribute
, value
));
509 bool BrowserAccessibility::HasIntListAttribute(
510 ui::AXIntListAttribute attribute
) const {
511 for (size_t i
= 0; i
< intlist_attributes_
.size(); ++i
) {
512 if (intlist_attributes_
[i
].first
== attribute
)
519 const std::vector
<int32
>& BrowserAccessibility::GetIntListAttribute(
520 ui::AXIntListAttribute attribute
) const {
521 CR_DEFINE_STATIC_LOCAL(std::vector
<int32
>, empty_vector
, ());
522 for (size_t i
= 0; i
< intlist_attributes_
.size(); ++i
) {
523 if (intlist_attributes_
[i
].first
== attribute
)
524 return intlist_attributes_
[i
].second
;
530 bool BrowserAccessibility::GetIntListAttribute(
531 ui::AXIntListAttribute attribute
,
532 std::vector
<int32
>* value
) const {
533 for (size_t i
= 0; i
< intlist_attributes_
.size(); ++i
) {
534 if (intlist_attributes_
[i
].first
== attribute
) {
535 *value
= intlist_attributes_
[i
].second
;
543 bool BrowserAccessibility::GetHtmlAttribute(
544 const char* html_attr
, std::string
* value
) const {
545 for (size_t i
= 0; i
< html_attributes_
.size(); ++i
) {
546 const std::string
& attr
= html_attributes_
[i
].first
;
547 if (LowerCaseEqualsASCII(attr
, html_attr
)) {
548 *value
= html_attributes_
[i
].second
;
556 bool BrowserAccessibility::GetHtmlAttribute(
557 const char* html_attr
, base::string16
* value
) const {
558 std::string value_utf8
;
559 if (!GetHtmlAttribute(html_attr
, &value_utf8
))
561 *value
= base::UTF8ToUTF16(value_utf8
);
565 bool BrowserAccessibility::GetAriaTristate(
566 const char* html_attr
,
568 bool* is_mixed
) const {
572 base::string16 value
;
573 if (!GetHtmlAttribute(html_attr
, &value
) ||
575 EqualsASCII(value
, "undefined")) {
576 return false; // Not set (and *is_defined is also false)
581 if (EqualsASCII(value
, "true"))
584 if (EqualsASCII(value
, "mixed"))
587 return false; // Not set
590 bool BrowserAccessibility::HasState(ui::AXState state_enum
) const {
591 return (state_
>> state_enum
) & 1;
594 bool BrowserAccessibility::IsEditableText() const {
595 // These roles don't have readonly set, but they're not editable text.
596 if (role_
== ui::AX_ROLE_SCROLL_AREA
||
597 role_
== ui::AX_ROLE_COLUMN
||
598 role_
== ui::AX_ROLE_TABLE_HEADER_CONTAINER
) {
602 // Note: WebAXStateReadonly being false means it's either a text control,
603 // or contenteditable. We also check for editable text roles to cover
604 // another element that has role=textbox set on it.
605 return (!HasState(ui::AX_STATE_READONLY
) ||
606 role_
== ui::AX_ROLE_TEXT_FIELD
||
607 role_
== ui::AX_ROLE_TEXT_AREA
);
610 std::string
BrowserAccessibility::GetTextRecursive() const {
611 if (!name_
.empty()) {
616 for (uint32 i
= 0; i
< PlatformChildCount(); ++i
)
617 result
+= PlatformGetChild(i
)->GetTextRecursive();
621 int BrowserAccessibility::GetStaticTextLenRecursive() const {
622 if (role_
== blink::WebAXRoleStaticText
)
623 return static_cast<int>(GetStringAttribute(ui::AX_ATTR_VALUE
).size());
626 for (size_t i
= 0; i
< children_
.size(); ++i
)
627 len
+= children_
[i
]->GetStaticTextLenRecursive();
631 } // namespace content