Support presentational iframes and make use of them in the uber frame.
[chromium-blink-merge.git] / content / browser / accessibility / browser_accessibility.cc
blobfb533aa85e5380b9a514b1228376785251dd4b0d
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 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)
48 return true;
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.
53 switch (GetRole()) {
54 case ui::AX_ROLE_SLIDER:
55 case ui::AX_ROLE_STATIC_TEXT:
56 case ui::AX_ROLE_TEXT_AREA:
57 case ui::AX_ROLE_TEXT_FIELD:
58 return true;
59 default:
60 return false;
64 uint32 BrowserAccessibility::PlatformChildCount() const {
65 return PlatformIsLeaf() ? 0 : InternalChildCount();
68 bool BrowserAccessibility::IsNative() const {
69 return false;
72 bool BrowserAccessibility::IsDescendantOf(
73 BrowserAccessibility* ancestor) {
74 if (this == ancestor) {
75 return true;
76 } else if (GetParent()) {
77 return GetParent()->IsDescendantOf(ancestor);
80 return false;
83 BrowserAccessibility* BrowserAccessibility::PlatformGetChild(
84 uint32 child_index) const {
85 DCHECK(child_index < InternalChildCount());
86 BrowserAccessibility* result = InternalGetChild(child_index);
88 if (result->HasBoolAttribute(ui::AX_ATTR_IS_AX_TREE_HOST)) {
89 BrowserAccessibilityManager* child_manager =
90 manager_->delegate()->AccessibilityGetChildFrame(result->GetId());
91 if (child_manager)
92 result = child_manager->GetRoot();
95 return result;
98 BrowserAccessibility* BrowserAccessibility::GetPreviousSibling() {
99 if (GetParent() && GetIndexInParent() > 0)
100 return GetParent()->InternalGetChild(GetIndexInParent() - 1);
102 return NULL;
105 BrowserAccessibility* BrowserAccessibility::GetNextSibling() {
106 if (GetParent() &&
107 GetIndexInParent() >= 0 &&
108 GetIndexInParent() < static_cast<int>(
109 GetParent()->InternalChildCount() - 1)) {
110 return GetParent()->InternalGetChild(GetIndexInParent() + 1);
113 return NULL;
116 uint32 BrowserAccessibility::InternalChildCount() const {
117 if (!node_ || !manager_)
118 return 0;
119 return static_cast<uint32>(node_->child_count());
122 BrowserAccessibility* BrowserAccessibility::InternalGetChild(
123 uint32 child_index) const {
124 if (!node_ || !manager_)
125 return NULL;
126 return manager_->GetFromAXNode(node_->children()[child_index]);
129 BrowserAccessibility* BrowserAccessibility::GetParent() const {
130 if (!node_ || !manager_)
131 return NULL;
132 ui::AXNode* parent = node_->parent();
133 if (parent)
134 return manager_->GetFromAXNode(parent);
136 if (!manager_->delegate())
137 return NULL;
139 BrowserAccessibility* host_node =
140 manager_->delegate()->AccessibilityGetParentFrame();
141 if (!host_node)
142 return NULL;
144 return host_node->GetParent();
147 int32 BrowserAccessibility::GetIndexInParent() const {
148 return node_ ? node_->index_in_parent() : -1;
151 int32 BrowserAccessibility::GetId() const {
152 return node_ ? node_->id() : -1;
155 const ui::AXNodeData& BrowserAccessibility::GetData() const {
156 CR_DEFINE_STATIC_LOCAL(ui::AXNodeData, empty_data, ());
157 if (node_)
158 return node_->data();
159 else
160 return empty_data;
163 gfx::Rect BrowserAccessibility::GetLocation() const {
164 return GetData().location;
167 int32 BrowserAccessibility::GetRole() const {
168 return GetData().role;
171 int32 BrowserAccessibility::GetState() const {
172 return GetData().state;
175 const BrowserAccessibility::HtmlAttributes&
176 BrowserAccessibility::GetHtmlAttributes() const {
177 return GetData().html_attributes;
180 gfx::Rect BrowserAccessibility::GetLocalBoundsRect() const {
181 gfx::Rect bounds = GetLocation();
183 // Walk up the parent chain. Every time we encounter a Web Area, offset
184 // based on the scroll bars and then offset based on the origin of that
185 // nested web area.
186 BrowserAccessibility* parent = GetParent();
187 bool need_to_offset_web_area =
188 (GetRole() == ui::AX_ROLE_WEB_AREA ||
189 GetRole() == ui::AX_ROLE_ROOT_WEB_AREA);
190 while (parent) {
191 if (need_to_offset_web_area &&
192 parent->GetLocation().width() > 0 &&
193 parent->GetLocation().height() > 0) {
194 bounds.Offset(parent->GetLocation().x(), parent->GetLocation().y());
195 need_to_offset_web_area = false;
198 // On some platforms, we don't want to take the root scroll offsets
199 // into account.
200 if (parent->GetRole() == ui::AX_ROLE_ROOT_WEB_AREA &&
201 !manager()->UseRootScrollOffsetsWhenComputingBounds()) {
202 break;
205 if (parent->GetRole() == ui::AX_ROLE_WEB_AREA ||
206 parent->GetRole() == ui::AX_ROLE_ROOT_WEB_AREA) {
207 int sx = 0;
208 int sy = 0;
209 if (parent->GetIntAttribute(ui::AX_ATTR_SCROLL_X, &sx) &&
210 parent->GetIntAttribute(ui::AX_ATTR_SCROLL_Y, &sy)) {
211 bounds.Offset(-sx, -sy);
213 need_to_offset_web_area = true;
215 parent = parent->GetParent();
218 return bounds;
221 gfx::Rect BrowserAccessibility::GetGlobalBoundsRect() const {
222 gfx::Rect bounds = GetLocalBoundsRect();
224 // Adjust the bounds by the top left corner of the containing view's bounds
225 // in screen coordinates.
226 bounds.Offset(manager_->GetViewBounds().OffsetFromOrigin());
228 return bounds;
231 gfx::Rect BrowserAccessibility::GetLocalBoundsForRange(int start, int len)
232 const {
233 if (GetRole() != ui::AX_ROLE_STATIC_TEXT) {
234 // Apply recursively to all static text descendants. For example, if
235 // you call it on a div with two text node children, it just calls
236 // GetLocalBoundsForRange on each of the two children (adjusting
237 // |start| for each one) and unions the resulting rects.
238 gfx::Rect bounds;
239 for (size_t i = 0; i < InternalChildCount(); ++i) {
240 BrowserAccessibility* child = InternalGetChild(i);
241 int child_len = child->GetStaticTextLenRecursive();
242 if (start < child_len && start + len > 0) {
243 gfx::Rect child_rect = child->GetLocalBoundsForRange(start, len);
244 bounds.Union(child_rect);
246 start -= child_len;
248 return bounds;
251 int end = start + len;
252 int child_start = 0;
253 int child_end = 0;
255 gfx::Rect bounds;
256 for (size_t i = 0; i < InternalChildCount() && child_end < start + len; ++i) {
257 BrowserAccessibility* child = InternalGetChild(i);
258 DCHECK_EQ(child->GetRole(), ui::AX_ROLE_INLINE_TEXT_BOX);
259 std::string child_text;
260 child->GetStringAttribute(ui::AX_ATTR_VALUE, &child_text);
261 int child_len = static_cast<int>(child_text.size());
262 child_start = child_end;
263 child_end += child_len;
265 if (child_end < start)
266 continue;
268 int overlap_start = std::max(start, child_start);
269 int overlap_end = std::min(end, child_end);
271 int local_start = overlap_start - child_start;
272 int local_end = overlap_end - child_start;
274 gfx::Rect child_rect = child->GetLocation();
275 int text_direction = child->GetIntAttribute(
276 ui::AX_ATTR_TEXT_DIRECTION);
277 const std::vector<int32>& character_offsets = child->GetIntListAttribute(
278 ui::AX_ATTR_CHARACTER_OFFSETS);
279 int start_pixel_offset =
280 local_start > 0 ? character_offsets[local_start - 1] : 0;
281 int end_pixel_offset =
282 local_end > 0 ? character_offsets[local_end - 1] : 0;
284 gfx::Rect child_overlap_rect;
285 switch (text_direction) {
286 case ui::AX_TEXT_DIRECTION_NONE:
287 case ui::AX_TEXT_DIRECTION_LR: {
288 int left = child_rect.x() + start_pixel_offset;
289 int right = child_rect.x() + end_pixel_offset;
290 child_overlap_rect = gfx::Rect(left, child_rect.y(),
291 right - left, child_rect.height());
292 break;
294 case ui::AX_TEXT_DIRECTION_RL: {
295 int right = child_rect.right() - start_pixel_offset;
296 int left = child_rect.right() - end_pixel_offset;
297 child_overlap_rect = gfx::Rect(left, child_rect.y(),
298 right - left, child_rect.height());
299 break;
301 case ui::AX_TEXT_DIRECTION_TB: {
302 int top = child_rect.y() + start_pixel_offset;
303 int bottom = child_rect.y() + end_pixel_offset;
304 child_overlap_rect = gfx::Rect(child_rect.x(), top,
305 child_rect.width(), bottom - top);
306 break;
308 case ui::AX_TEXT_DIRECTION_BT: {
309 int bottom = child_rect.bottom() - start_pixel_offset;
310 int top = child_rect.bottom() - end_pixel_offset;
311 child_overlap_rect = gfx::Rect(child_rect.x(), top,
312 child_rect.width(), bottom - top);
313 break;
315 default:
316 NOTREACHED();
319 if (bounds.width() == 0 && bounds.height() == 0)
320 bounds = child_overlap_rect;
321 else
322 bounds.Union(child_overlap_rect);
325 return bounds;
328 gfx::Rect BrowserAccessibility::GetGlobalBoundsForRange(int start, int len)
329 const {
330 gfx::Rect bounds = GetLocalBoundsForRange(start, len);
332 // Adjust the bounds by the top left corner of the containing view's bounds
333 // in screen coordinates.
334 bounds.Offset(manager_->GetViewBounds().OffsetFromOrigin());
336 return bounds;
339 BrowserAccessibility* BrowserAccessibility::BrowserAccessibilityForPoint(
340 const gfx::Point& point) {
341 // The best result found that's a child of this object.
342 BrowserAccessibility* child_result = NULL;
343 // The best result that's an indirect descendant like grandchild, etc.
344 BrowserAccessibility* descendant_result = NULL;
346 // Walk the children recursively looking for the BrowserAccessibility that
347 // most tightly encloses the specified point. Walk backwards so that in
348 // the absence of any other information, we assume the object that occurs
349 // later in the tree is on top of one that comes before it.
350 for (int i = static_cast<int>(PlatformChildCount()) - 1; i >= 0; --i) {
351 BrowserAccessibility* child = PlatformGetChild(i);
353 // Skip table columns because cells are only contained in rows,
354 // not columns.
355 if (child->GetRole() == ui::AX_ROLE_COLUMN)
356 continue;
358 if (child->GetGlobalBoundsRect().Contains(point)) {
359 BrowserAccessibility* result = child->BrowserAccessibilityForPoint(point);
360 if (result == child && !child_result)
361 child_result = result;
362 if (result != child && !descendant_result)
363 descendant_result = result;
366 if (child_result && descendant_result)
367 break;
370 // Explanation of logic: it's possible that this point overlaps more than
371 // one child of this object. If so, as a heuristic we prefer if the point
372 // overlaps a descendant of one of the two children and not the other.
373 // As an example, suppose you have two rows of buttons - the buttons don't
374 // overlap, but the rows do. Without this heuristic, we'd greedily only
375 // consider one of the containers.
376 if (descendant_result)
377 return descendant_result;
378 if (child_result)
379 return child_result;
381 return this;
384 void BrowserAccessibility::Destroy() {
385 // Allow the object to fire a TextRemoved notification.
386 name_.clear();
387 value_.clear();
389 manager_->NotifyAccessibilityEvent(ui::AX_EVENT_HIDE, this);
390 node_ = NULL;
391 manager_ = NULL;
393 NativeReleaseReference();
396 void BrowserAccessibility::NativeReleaseReference() {
397 delete this;
400 bool BrowserAccessibility::HasBoolAttribute(
401 ui::AXBoolAttribute attribute) const {
402 const ui::AXNodeData& data = GetData();
403 for (size_t i = 0; i < data.bool_attributes.size(); ++i) {
404 if (data.bool_attributes[i].first == attribute)
405 return true;
408 return false;
412 bool BrowserAccessibility::GetBoolAttribute(
413 ui::AXBoolAttribute attribute) const {
414 const ui::AXNodeData& data = GetData();
415 for (size_t i = 0; i < data.bool_attributes.size(); ++i) {
416 if (data.bool_attributes[i].first == attribute)
417 return data.bool_attributes[i].second;
420 return false;
423 bool BrowserAccessibility::GetBoolAttribute(
424 ui::AXBoolAttribute attribute, bool* value) const {
425 const ui::AXNodeData& data = GetData();
426 for (size_t i = 0; i < data.bool_attributes.size(); ++i) {
427 if (data.bool_attributes[i].first == attribute) {
428 *value = data.bool_attributes[i].second;
429 return true;
433 return false;
436 bool BrowserAccessibility::HasFloatAttribute(
437 ui::AXFloatAttribute attribute) const {
438 const ui::AXNodeData& data = GetData();
439 for (size_t i = 0; i < data.float_attributes.size(); ++i) {
440 if (data.float_attributes[i].first == attribute)
441 return true;
444 return false;
447 float BrowserAccessibility::GetFloatAttribute(
448 ui::AXFloatAttribute attribute) const {
449 const ui::AXNodeData& data = GetData();
450 for (size_t i = 0; i < data.float_attributes.size(); ++i) {
451 if (data.float_attributes[i].first == attribute)
452 return data.float_attributes[i].second;
455 return 0.0;
458 bool BrowserAccessibility::GetFloatAttribute(
459 ui::AXFloatAttribute attribute, float* value) const {
460 const ui::AXNodeData& data = GetData();
461 for (size_t i = 0; i < data.float_attributes.size(); ++i) {
462 if (data.float_attributes[i].first == attribute) {
463 *value = data.float_attributes[i].second;
464 return true;
468 return false;
471 bool BrowserAccessibility::HasIntAttribute(
472 ui::AXIntAttribute attribute) const {
473 const ui::AXNodeData& data = GetData();
474 for (size_t i = 0; i < data.int_attributes.size(); ++i) {
475 if (data.int_attributes[i].first == attribute)
476 return true;
479 return false;
482 int BrowserAccessibility::GetIntAttribute(ui::AXIntAttribute attribute) const {
483 const ui::AXNodeData& data = GetData();
484 for (size_t i = 0; i < data.int_attributes.size(); ++i) {
485 if (data.int_attributes[i].first == attribute)
486 return data.int_attributes[i].second;
489 return 0;
492 bool BrowserAccessibility::GetIntAttribute(
493 ui::AXIntAttribute attribute, int* value) const {
494 const ui::AXNodeData& data = GetData();
495 for (size_t i = 0; i < data.int_attributes.size(); ++i) {
496 if (data.int_attributes[i].first == attribute) {
497 *value = data.int_attributes[i].second;
498 return true;
502 return false;
505 bool BrowserAccessibility::HasStringAttribute(
506 ui::AXStringAttribute attribute) const {
507 const ui::AXNodeData& data = GetData();
508 for (size_t i = 0; i < data.string_attributes.size(); ++i) {
509 if (data.string_attributes[i].first == attribute)
510 return true;
513 return false;
516 const std::string& BrowserAccessibility::GetStringAttribute(
517 ui::AXStringAttribute attribute) const {
518 const ui::AXNodeData& data = GetData();
519 CR_DEFINE_STATIC_LOCAL(std::string, empty_string, ());
520 for (size_t i = 0; i < data.string_attributes.size(); ++i) {
521 if (data.string_attributes[i].first == attribute)
522 return data.string_attributes[i].second;
525 return empty_string;
528 bool BrowserAccessibility::GetStringAttribute(
529 ui::AXStringAttribute attribute, std::string* value) const {
530 const ui::AXNodeData& data = GetData();
531 for (size_t i = 0; i < data.string_attributes.size(); ++i) {
532 if (data.string_attributes[i].first == attribute) {
533 *value = data.string_attributes[i].second;
534 return true;
538 return false;
541 base::string16 BrowserAccessibility::GetString16Attribute(
542 ui::AXStringAttribute attribute) const {
543 std::string value_utf8;
544 if (!GetStringAttribute(attribute, &value_utf8))
545 return base::string16();
546 return base::UTF8ToUTF16(value_utf8);
549 bool BrowserAccessibility::GetString16Attribute(
550 ui::AXStringAttribute attribute,
551 base::string16* value) const {
552 std::string value_utf8;
553 if (!GetStringAttribute(attribute, &value_utf8))
554 return false;
555 *value = base::UTF8ToUTF16(value_utf8);
556 return true;
559 void BrowserAccessibility::SetStringAttribute(
560 ui::AXStringAttribute attribute, const std::string& value) {
561 if (!node_)
562 return;
563 ui::AXNodeData data = GetData();
564 for (size_t i = 0; i < data.string_attributes.size(); ++i) {
565 if (data.string_attributes[i].first == attribute) {
566 data.string_attributes[i].second = value;
567 node_->SetData(data);
568 return;
571 if (!value.empty()) {
572 data.string_attributes.push_back(std::make_pair(attribute, value));
573 node_->SetData(data);
577 bool BrowserAccessibility::HasIntListAttribute(
578 ui::AXIntListAttribute attribute) const {
579 const ui::AXNodeData& data = GetData();
580 for (size_t i = 0; i < data.intlist_attributes.size(); ++i) {
581 if (data.intlist_attributes[i].first == attribute)
582 return true;
585 return false;
588 const std::vector<int32>& BrowserAccessibility::GetIntListAttribute(
589 ui::AXIntListAttribute attribute) const {
590 const ui::AXNodeData& data = GetData();
591 CR_DEFINE_STATIC_LOCAL(std::vector<int32>, empty_vector, ());
592 for (size_t i = 0; i < data.intlist_attributes.size(); ++i) {
593 if (data.intlist_attributes[i].first == attribute)
594 return data.intlist_attributes[i].second;
597 return empty_vector;
600 bool BrowserAccessibility::GetIntListAttribute(
601 ui::AXIntListAttribute attribute,
602 std::vector<int32>* value) const {
603 const ui::AXNodeData& data = GetData();
604 for (size_t i = 0; i < data.intlist_attributes.size(); ++i) {
605 if (data.intlist_attributes[i].first == attribute) {
606 *value = data.intlist_attributes[i].second;
607 return true;
611 return false;
614 bool BrowserAccessibility::GetHtmlAttribute(
615 const char* html_attr, std::string* value) const {
616 for (size_t i = 0; i < GetHtmlAttributes().size(); ++i) {
617 const std::string& attr = GetHtmlAttributes()[i].first;
618 if (LowerCaseEqualsASCII(attr, html_attr)) {
619 *value = GetHtmlAttributes()[i].second;
620 return true;
624 return false;
627 bool BrowserAccessibility::GetHtmlAttribute(
628 const char* html_attr, base::string16* value) const {
629 std::string value_utf8;
630 if (!GetHtmlAttribute(html_attr, &value_utf8))
631 return false;
632 *value = base::UTF8ToUTF16(value_utf8);
633 return true;
636 bool BrowserAccessibility::GetAriaTristate(
637 const char* html_attr,
638 bool* is_defined,
639 bool* is_mixed) const {
640 *is_defined = false;
641 *is_mixed = false;
643 base::string16 value;
644 if (!GetHtmlAttribute(html_attr, &value) ||
645 value.empty() ||
646 EqualsASCII(value, "undefined")) {
647 return false; // Not set (and *is_defined is also false)
650 *is_defined = true;
652 if (EqualsASCII(value, "true"))
653 return true;
655 if (EqualsASCII(value, "mixed"))
656 *is_mixed = true;
658 return false; // Not set
661 bool BrowserAccessibility::HasState(ui::AXState state_enum) const {
662 return (GetState() >> state_enum) & 1;
665 bool BrowserAccessibility::IsEditableText() const {
666 // These roles don't have readonly set, but they're not editable text.
667 if (GetRole() == ui::AX_ROLE_SCROLL_AREA ||
668 GetRole() == ui::AX_ROLE_COLUMN ||
669 GetRole() == ui::AX_ROLE_TABLE_HEADER_CONTAINER) {
670 return false;
673 // Note: WebAXStateReadonly being false means it's either a text control,
674 // or contenteditable. We also check for editable text roles to cover
675 // another element that has role=textbox set on it.
676 return (!HasState(ui::AX_STATE_READ_ONLY) ||
677 GetRole() == ui::AX_ROLE_TEXT_FIELD ||
678 GetRole() == ui::AX_ROLE_TEXT_AREA);
681 bool BrowserAccessibility::IsWebAreaForPresentationalIframe() const {
682 if (GetRole() != ui::AX_ROLE_WEB_AREA &&
683 GetRole() != ui::AX_ROLE_ROOT_WEB_AREA) {
684 return false;
687 BrowserAccessibility* parent = GetParent();
688 if (!parent)
689 return false;
691 BrowserAccessibility* grandparent = parent->GetParent();
692 if (!grandparent)
693 return false;
695 return grandparent->GetRole() == ui::AX_ROLE_IFRAME_PRESENTATIONAL;
698 std::string BrowserAccessibility::GetTextRecursive() const {
699 if (!name_.empty()) {
700 return name_;
703 std::string result;
704 for (uint32 i = 0; i < PlatformChildCount(); ++i)
705 result += PlatformGetChild(i)->GetTextRecursive();
706 return result;
709 int BrowserAccessibility::GetStaticTextLenRecursive() const {
710 if (GetRole() == ui::AX_ROLE_STATIC_TEXT)
711 return static_cast<int>(GetStringAttribute(ui::AX_ATTR_VALUE).size());
713 int len = 0;
714 for (size_t i = 0; i < InternalChildCount(); ++i)
715 len += InternalGetChild(i)->GetStaticTextLenRecursive();
716 return len;
719 } // namespace content