Revert of Update mojo sdk to rev 8af2ccff2eee4bfca1043015abee30482a030b30 (patchset...
[chromium-blink-merge.git] / content / browser / accessibility / browser_accessibility.cc
blob13402f5b03c9b37b9278e770d15322df0e9d34c2
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"
14 namespace content {
16 #if !defined(OS_MACOSX) && \
17 !defined(OS_WIN) && \
18 !defined(OS_ANDROID)
19 // We have subclassess of BrowserAccessibility on Mac and Win. For any other
20 // platform, instantiate the base class.
21 // static
22 BrowserAccessibility* BrowserAccessibility::Create() {
23 return new BrowserAccessibility();
25 #endif
27 BrowserAccessibility::BrowserAccessibility()
28 : manager_(NULL),
29 node_(NULL) {
32 BrowserAccessibility::~BrowserAccessibility() {
35 void BrowserAccessibility::Init(BrowserAccessibilityManager* manager,
36 ui::AXNode* node) {
37 manager_ = manager;
38 node_ = node;
41 bool BrowserAccessibility::PlatformIsLeaf() const {
42 if (InternalChildCount() == 0)
43 return true;
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.
48 switch (GetRole()) {
49 case ui::AX_ROLE_LINE_BREAK:
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:
54 return true;
55 default:
56 return false;
60 uint32 BrowserAccessibility::PlatformChildCount() const {
61 return PlatformIsLeaf() ? 0 : InternalChildCount();
64 bool BrowserAccessibility::IsNative() const {
65 return false;
68 bool BrowserAccessibility::IsDescendantOf(
69 BrowserAccessibility* ancestor) {
70 if (this == ancestor) {
71 return true;
72 } else if (GetParent()) {
73 return GetParent()->IsDescendantOf(ancestor);
76 return false;
79 BrowserAccessibility* BrowserAccessibility::PlatformGetChild(
80 uint32 child_index) const {
81 DCHECK(child_index < InternalChildCount());
82 BrowserAccessibility* result = InternalGetChild(child_index);
84 if (result->HasBoolAttribute(ui::AX_ATTR_IS_AX_TREE_HOST)) {
85 BrowserAccessibilityManager* child_manager =
86 manager_->delegate()->AccessibilityGetChildFrame(result->GetId());
87 if (child_manager)
88 result = child_manager->GetRoot();
91 return result;
94 bool BrowserAccessibility::PlatformIsChildOfLeaf() const {
95 BrowserAccessibility* ancestor = GetParent();
96 while (ancestor) {
97 if (ancestor->PlatformIsLeaf())
98 return true;
99 ancestor = ancestor->GetParent();
102 return false;
105 BrowserAccessibility* BrowserAccessibility::GetPreviousSibling() {
106 if (GetParent() && GetIndexInParent() > 0)
107 return GetParent()->InternalGetChild(GetIndexInParent() - 1);
109 return NULL;
112 BrowserAccessibility* BrowserAccessibility::GetNextSibling() {
113 if (GetParent() &&
114 GetIndexInParent() >= 0 &&
115 GetIndexInParent() < static_cast<int>(
116 GetParent()->InternalChildCount() - 1)) {
117 return GetParent()->InternalGetChild(GetIndexInParent() + 1);
120 return NULL;
123 uint32 BrowserAccessibility::InternalChildCount() const {
124 if (!node_ || !manager_)
125 return 0;
126 return static_cast<uint32>(node_->child_count());
129 BrowserAccessibility* BrowserAccessibility::InternalGetChild(
130 uint32 child_index) const {
131 if (!node_ || !manager_)
132 return NULL;
133 return manager_->GetFromAXNode(node_->children()[child_index]);
136 BrowserAccessibility* BrowserAccessibility::GetParent() const {
137 if (!node_ || !manager_)
138 return NULL;
139 ui::AXNode* parent = node_->parent();
140 if (parent)
141 return manager_->GetFromAXNode(parent);
143 if (!manager_->delegate())
144 return NULL;
146 BrowserAccessibility* host_node =
147 manager_->delegate()->AccessibilityGetParentFrame();
148 if (!host_node)
149 return NULL;
151 return host_node->GetParent();
154 int32 BrowserAccessibility::GetIndexInParent() const {
155 return node_ ? node_->index_in_parent() : -1;
158 int32 BrowserAccessibility::GetId() const {
159 return node_ ? node_->id() : -1;
162 const ui::AXNodeData& BrowserAccessibility::GetData() const {
163 CR_DEFINE_STATIC_LOCAL(ui::AXNodeData, empty_data, ());
164 if (node_)
165 return node_->data();
166 else
167 return empty_data;
170 gfx::Rect BrowserAccessibility::GetLocation() const {
171 return GetData().location;
174 int32 BrowserAccessibility::GetRole() const {
175 return GetData().role;
178 int32 BrowserAccessibility::GetState() const {
179 return GetData().state;
182 const BrowserAccessibility::HtmlAttributes&
183 BrowserAccessibility::GetHtmlAttributes() const {
184 return GetData().html_attributes;
187 gfx::Rect BrowserAccessibility::GetLocalBoundsRect() const {
188 gfx::Rect bounds = GetLocation();
189 return ElementBoundsToLocalBounds(bounds);
192 gfx::Rect BrowserAccessibility::GetGlobalBoundsRect() const {
193 gfx::Rect bounds = GetLocalBoundsRect();
195 // Adjust the bounds by the top left corner of the containing view's bounds
196 // in screen coordinates.
197 bounds.Offset(manager_->GetViewBounds().OffsetFromOrigin());
199 return bounds;
202 gfx::Rect BrowserAccessibility::GetLocalBoundsForRange(int start, int len)
203 const {
204 if (GetRole() != ui::AX_ROLE_STATIC_TEXT) {
205 // Apply recursively to all static text descendants. For example, if
206 // you call it on a div with two text node children, it just calls
207 // GetLocalBoundsForRange on each of the two children (adjusting
208 // |start| for each one) and unions the resulting rects.
209 gfx::Rect bounds;
210 for (size_t i = 0; i < InternalChildCount(); ++i) {
211 BrowserAccessibility* child = InternalGetChild(i);
212 int child_len = child->GetStaticTextLenRecursive();
213 if (start < child_len && start + len > 0) {
214 gfx::Rect child_rect = child->GetLocalBoundsForRange(start, len);
215 bounds.Union(child_rect);
217 start -= child_len;
219 return ElementBoundsToLocalBounds(bounds);
222 int end = start + len;
223 int child_start = 0;
224 int child_end = 0;
226 gfx::Rect bounds;
227 for (size_t i = 0; i < InternalChildCount() && child_end < start + len; ++i) {
228 BrowserAccessibility* child = InternalGetChild(i);
229 DCHECK_EQ(child->GetRole(), ui::AX_ROLE_INLINE_TEXT_BOX);
230 std::string child_text;
231 child->GetStringAttribute(ui::AX_ATTR_VALUE, &child_text);
232 int child_len = static_cast<int>(child_text.size());
233 child_start = child_end;
234 child_end += child_len;
236 if (child_end < start)
237 continue;
239 int overlap_start = std::max(start, child_start);
240 int overlap_end = std::min(end, child_end);
242 int local_start = overlap_start - child_start;
243 int local_end = overlap_end - child_start;
245 gfx::Rect child_rect = child->GetLocation();
246 int text_direction = child->GetIntAttribute(
247 ui::AX_ATTR_TEXT_DIRECTION);
248 const std::vector<int32>& character_offsets = child->GetIntListAttribute(
249 ui::AX_ATTR_CHARACTER_OFFSETS);
250 int start_pixel_offset =
251 local_start > 0 ? character_offsets[local_start - 1] : 0;
252 int end_pixel_offset =
253 local_end > 0 ? character_offsets[local_end - 1] : 0;
255 gfx::Rect child_overlap_rect;
256 switch (text_direction) {
257 case ui::AX_TEXT_DIRECTION_NONE:
258 case ui::AX_TEXT_DIRECTION_LR: {
259 int left = child_rect.x() + start_pixel_offset;
260 int right = child_rect.x() + end_pixel_offset;
261 child_overlap_rect = gfx::Rect(left, child_rect.y(),
262 right - left, child_rect.height());
263 break;
265 case ui::AX_TEXT_DIRECTION_RL: {
266 int right = child_rect.right() - start_pixel_offset;
267 int left = child_rect.right() - end_pixel_offset;
268 child_overlap_rect = gfx::Rect(left, child_rect.y(),
269 right - left, child_rect.height());
270 break;
272 case ui::AX_TEXT_DIRECTION_TB: {
273 int top = child_rect.y() + start_pixel_offset;
274 int bottom = child_rect.y() + end_pixel_offset;
275 child_overlap_rect = gfx::Rect(child_rect.x(), top,
276 child_rect.width(), bottom - top);
277 break;
279 case ui::AX_TEXT_DIRECTION_BT: {
280 int bottom = child_rect.bottom() - start_pixel_offset;
281 int top = child_rect.bottom() - end_pixel_offset;
282 child_overlap_rect = gfx::Rect(child_rect.x(), top,
283 child_rect.width(), bottom - top);
284 break;
286 default:
287 NOTREACHED();
290 if (bounds.width() == 0 && bounds.height() == 0)
291 bounds = child_overlap_rect;
292 else
293 bounds.Union(child_overlap_rect);
296 return ElementBoundsToLocalBounds(bounds);
299 gfx::Rect BrowserAccessibility::GetGlobalBoundsForRange(int start, int len)
300 const {
301 gfx::Rect bounds = GetLocalBoundsForRange(start, len);
303 // Adjust the bounds by the top left corner of the containing view's bounds
304 // in screen coordinates.
305 bounds.Offset(manager_->GetViewBounds().OffsetFromOrigin());
307 return bounds;
310 BrowserAccessibility* BrowserAccessibility::BrowserAccessibilityForPoint(
311 const gfx::Point& point) {
312 // The best result found that's a child of this object.
313 BrowserAccessibility* child_result = NULL;
314 // The best result that's an indirect descendant like grandchild, etc.
315 BrowserAccessibility* descendant_result = NULL;
317 // Walk the children recursively looking for the BrowserAccessibility that
318 // most tightly encloses the specified point. Walk backwards so that in
319 // the absence of any other information, we assume the object that occurs
320 // later in the tree is on top of one that comes before it.
321 for (int i = static_cast<int>(PlatformChildCount()) - 1; i >= 0; --i) {
322 BrowserAccessibility* child = PlatformGetChild(i);
324 // Skip table columns because cells are only contained in rows,
325 // not columns.
326 if (child->GetRole() == ui::AX_ROLE_COLUMN)
327 continue;
329 if (child->GetGlobalBoundsRect().Contains(point)) {
330 BrowserAccessibility* result = child->BrowserAccessibilityForPoint(point);
331 if (result == child && !child_result)
332 child_result = result;
333 if (result != child && !descendant_result)
334 descendant_result = result;
337 if (child_result && descendant_result)
338 break;
341 // Explanation of logic: it's possible that this point overlaps more than
342 // one child of this object. If so, as a heuristic we prefer if the point
343 // overlaps a descendant of one of the two children and not the other.
344 // As an example, suppose you have two rows of buttons - the buttons don't
345 // overlap, but the rows do. Without this heuristic, we'd greedily only
346 // consider one of the containers.
347 if (descendant_result)
348 return descendant_result;
349 if (child_result)
350 return child_result;
352 return this;
355 void BrowserAccessibility::Destroy() {
356 // Allow the object to fire a TextRemoved notification.
357 manager_->NotifyAccessibilityEvent(ui::AX_EVENT_HIDE, this);
358 node_ = NULL;
359 manager_ = NULL;
361 NativeReleaseReference();
364 void BrowserAccessibility::NativeReleaseReference() {
365 delete this;
368 bool BrowserAccessibility::HasBoolAttribute(
369 ui::AXBoolAttribute attribute) const {
370 const ui::AXNodeData& data = GetData();
371 for (size_t i = 0; i < data.bool_attributes.size(); ++i) {
372 if (data.bool_attributes[i].first == attribute)
373 return true;
376 return false;
380 bool BrowserAccessibility::GetBoolAttribute(
381 ui::AXBoolAttribute attribute) const {
382 const ui::AXNodeData& data = GetData();
383 for (size_t i = 0; i < data.bool_attributes.size(); ++i) {
384 if (data.bool_attributes[i].first == attribute)
385 return data.bool_attributes[i].second;
388 return false;
391 bool BrowserAccessibility::GetBoolAttribute(
392 ui::AXBoolAttribute attribute, bool* value) const {
393 const ui::AXNodeData& data = GetData();
394 for (size_t i = 0; i < data.bool_attributes.size(); ++i) {
395 if (data.bool_attributes[i].first == attribute) {
396 *value = data.bool_attributes[i].second;
397 return true;
401 return false;
404 bool BrowserAccessibility::HasFloatAttribute(
405 ui::AXFloatAttribute attribute) const {
406 const ui::AXNodeData& data = GetData();
407 for (size_t i = 0; i < data.float_attributes.size(); ++i) {
408 if (data.float_attributes[i].first == attribute)
409 return true;
412 return false;
415 float BrowserAccessibility::GetFloatAttribute(
416 ui::AXFloatAttribute attribute) const {
417 const ui::AXNodeData& data = GetData();
418 for (size_t i = 0; i < data.float_attributes.size(); ++i) {
419 if (data.float_attributes[i].first == attribute)
420 return data.float_attributes[i].second;
423 return 0.0;
426 bool BrowserAccessibility::GetFloatAttribute(
427 ui::AXFloatAttribute attribute, float* value) const {
428 const ui::AXNodeData& data = GetData();
429 for (size_t i = 0; i < data.float_attributes.size(); ++i) {
430 if (data.float_attributes[i].first == attribute) {
431 *value = data.float_attributes[i].second;
432 return true;
436 return false;
439 bool BrowserAccessibility::HasIntAttribute(
440 ui::AXIntAttribute attribute) const {
441 const ui::AXNodeData& data = GetData();
442 for (size_t i = 0; i < data.int_attributes.size(); ++i) {
443 if (data.int_attributes[i].first == attribute)
444 return true;
447 return false;
450 int BrowserAccessibility::GetIntAttribute(ui::AXIntAttribute attribute) const {
451 const ui::AXNodeData& data = GetData();
452 for (size_t i = 0; i < data.int_attributes.size(); ++i) {
453 if (data.int_attributes[i].first == attribute)
454 return data.int_attributes[i].second;
457 return 0;
460 bool BrowserAccessibility::GetIntAttribute(
461 ui::AXIntAttribute attribute, int* value) const {
462 const ui::AXNodeData& data = GetData();
463 for (size_t i = 0; i < data.int_attributes.size(); ++i) {
464 if (data.int_attributes[i].first == attribute) {
465 *value = data.int_attributes[i].second;
466 return true;
470 return false;
473 bool BrowserAccessibility::HasStringAttribute(
474 ui::AXStringAttribute attribute) const {
475 const ui::AXNodeData& data = GetData();
476 for (size_t i = 0; i < data.string_attributes.size(); ++i) {
477 if (data.string_attributes[i].first == attribute)
478 return true;
481 return false;
484 const std::string& BrowserAccessibility::GetStringAttribute(
485 ui::AXStringAttribute attribute) const {
486 const ui::AXNodeData& data = GetData();
487 CR_DEFINE_STATIC_LOCAL(std::string, empty_string, ());
488 for (size_t i = 0; i < data.string_attributes.size(); ++i) {
489 if (data.string_attributes[i].first == attribute)
490 return data.string_attributes[i].second;
493 return empty_string;
496 bool BrowserAccessibility::GetStringAttribute(
497 ui::AXStringAttribute attribute, std::string* value) const {
498 const ui::AXNodeData& data = GetData();
499 for (size_t i = 0; i < data.string_attributes.size(); ++i) {
500 if (data.string_attributes[i].first == attribute) {
501 *value = data.string_attributes[i].second;
502 return true;
506 return false;
509 base::string16 BrowserAccessibility::GetString16Attribute(
510 ui::AXStringAttribute attribute) const {
511 std::string value_utf8;
512 if (!GetStringAttribute(attribute, &value_utf8))
513 return base::string16();
514 return base::UTF8ToUTF16(value_utf8);
517 bool BrowserAccessibility::GetString16Attribute(
518 ui::AXStringAttribute attribute,
519 base::string16* value) const {
520 std::string value_utf8;
521 if (!GetStringAttribute(attribute, &value_utf8))
522 return false;
523 *value = base::UTF8ToUTF16(value_utf8);
524 return true;
527 bool BrowserAccessibility::HasIntListAttribute(
528 ui::AXIntListAttribute attribute) const {
529 const ui::AXNodeData& data = GetData();
530 for (size_t i = 0; i < data.intlist_attributes.size(); ++i) {
531 if (data.intlist_attributes[i].first == attribute)
532 return true;
535 return false;
538 const std::vector<int32>& BrowserAccessibility::GetIntListAttribute(
539 ui::AXIntListAttribute attribute) const {
540 const ui::AXNodeData& data = GetData();
541 CR_DEFINE_STATIC_LOCAL(std::vector<int32>, empty_vector, ());
542 for (size_t i = 0; i < data.intlist_attributes.size(); ++i) {
543 if (data.intlist_attributes[i].first == attribute)
544 return data.intlist_attributes[i].second;
547 return empty_vector;
550 bool BrowserAccessibility::GetIntListAttribute(
551 ui::AXIntListAttribute attribute,
552 std::vector<int32>* value) const {
553 const ui::AXNodeData& data = GetData();
554 for (size_t i = 0; i < data.intlist_attributes.size(); ++i) {
555 if (data.intlist_attributes[i].first == attribute) {
556 *value = data.intlist_attributes[i].second;
557 return true;
561 return false;
564 bool BrowserAccessibility::GetHtmlAttribute(
565 const char* html_attr, std::string* value) const {
566 for (size_t i = 0; i < GetHtmlAttributes().size(); ++i) {
567 const std::string& attr = GetHtmlAttributes()[i].first;
568 if (LowerCaseEqualsASCII(attr, html_attr)) {
569 *value = GetHtmlAttributes()[i].second;
570 return true;
574 return false;
577 bool BrowserAccessibility::GetHtmlAttribute(
578 const char* html_attr, base::string16* value) const {
579 std::string value_utf8;
580 if (!GetHtmlAttribute(html_attr, &value_utf8))
581 return false;
582 *value = base::UTF8ToUTF16(value_utf8);
583 return true;
586 bool BrowserAccessibility::GetAriaTristate(
587 const char* html_attr,
588 bool* is_defined,
589 bool* is_mixed) const {
590 *is_defined = false;
591 *is_mixed = false;
593 base::string16 value;
594 if (!GetHtmlAttribute(html_attr, &value) ||
595 value.empty() ||
596 EqualsASCII(value, "undefined")) {
597 return false; // Not set (and *is_defined is also false)
600 *is_defined = true;
602 if (EqualsASCII(value, "true"))
603 return true;
605 if (EqualsASCII(value, "mixed"))
606 *is_mixed = true;
608 return false; // Not set
611 bool BrowserAccessibility::HasState(ui::AXState state_enum) const {
612 return (GetState() >> state_enum) & 1;
615 bool BrowserAccessibility::IsCellOrTableHeaderRole() const {
616 return (GetRole() == ui::AX_ROLE_CELL ||
617 GetRole() == ui::AX_ROLE_COLUMN_HEADER ||
618 GetRole() == ui::AX_ROLE_ROW_HEADER);
621 bool BrowserAccessibility::IsEditableText() const {
622 // These roles don't have readonly set, but they're not editable text.
623 if (GetRole() == ui::AX_ROLE_SCROLL_AREA ||
624 GetRole() == ui::AX_ROLE_COLUMN ||
625 GetRole() == ui::AX_ROLE_TABLE_HEADER_CONTAINER) {
626 return false;
629 // Note: WebAXStateReadonly being false means it's either a text control,
630 // or contenteditable. We also check for editable text roles to cover
631 // another element that has role=textbox set on it.
632 return (!HasState(ui::AX_STATE_READ_ONLY) ||
633 GetRole() == ui::AX_ROLE_TEXT_FIELD ||
634 GetRole() == ui::AX_ROLE_TEXT_AREA);
637 bool BrowserAccessibility::IsWebAreaForPresentationalIframe() const {
638 if (GetRole() != ui::AX_ROLE_WEB_AREA &&
639 GetRole() != ui::AX_ROLE_ROOT_WEB_AREA) {
640 return false;
643 BrowserAccessibility* parent = GetParent();
644 if (!parent)
645 return false;
647 BrowserAccessibility* grandparent = parent->GetParent();
648 if (!grandparent)
649 return false;
651 return grandparent->GetRole() == ui::AX_ROLE_IFRAME_PRESENTATIONAL;
654 int BrowserAccessibility::GetStaticTextLenRecursive() const {
655 if (GetRole() == ui::AX_ROLE_STATIC_TEXT)
656 return static_cast<int>(GetStringAttribute(ui::AX_ATTR_VALUE).size());
658 int len = 0;
659 for (size_t i = 0; i < InternalChildCount(); ++i)
660 len += InternalGetChild(i)->GetStaticTextLenRecursive();
661 return len;
664 BrowserAccessibility* BrowserAccessibility::GetParentForBoundsCalculation()
665 const {
666 if (!node_ || !manager_)
667 return NULL;
668 ui::AXNode* parent = node_->parent();
669 if (parent)
670 return manager_->GetFromAXNode(parent);
672 if (!manager_->delegate())
673 return NULL;
675 return manager_->delegate()->AccessibilityGetParentFrame();
678 gfx::Rect BrowserAccessibility::ElementBoundsToLocalBounds(gfx::Rect bounds)
679 const {
680 // Walk up the parent chain. Every time we encounter a Web Area, offset
681 // based on the scroll bars and then offset based on the origin of that
682 // nested web area.
683 BrowserAccessibility* parent = GetParentForBoundsCalculation();
684 bool need_to_offset_web_area =
685 (GetRole() == ui::AX_ROLE_WEB_AREA ||
686 GetRole() == ui::AX_ROLE_ROOT_WEB_AREA);
687 while (parent) {
688 if (need_to_offset_web_area &&
689 parent->GetLocation().width() > 0 &&
690 parent->GetLocation().height() > 0) {
691 bounds.Offset(parent->GetLocation().x(), parent->GetLocation().y());
692 need_to_offset_web_area = false;
695 // On some platforms, we don't want to take the root scroll offsets
696 // into account.
697 if (parent->GetRole() == ui::AX_ROLE_ROOT_WEB_AREA &&
698 !manager()->UseRootScrollOffsetsWhenComputingBounds()) {
699 break;
702 if (parent->GetRole() == ui::AX_ROLE_WEB_AREA ||
703 parent->GetRole() == ui::AX_ROLE_ROOT_WEB_AREA) {
704 int sx = 0;
705 int sy = 0;
706 if (parent->GetIntAttribute(ui::AX_ATTR_SCROLL_X, &sx) &&
707 parent->GetIntAttribute(ui::AX_ATTR_SCROLL_Y, &sy)) {
708 bounds.Offset(-sx, -sy);
710 need_to_offset_web_area = true;
712 parent = parent->GetParentForBoundsCalculation();
715 return bounds;
718 } // namespace content