Battery Status API: add UMA logging for Linux.
[chromium-blink-merge.git] / content / browser / accessibility / browser_accessibility.cc
blobfe71fef965aa95dfdd32a16d0c22d738e9430e99
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_EDITABLE_TEXT:
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:
59 return true;
60 default:
61 return false;
65 uint32 BrowserAccessibility::PlatformChildCount() const {
66 return PlatformIsLeaf() ? 0 : InternalChildCount();
69 bool BrowserAccessibility::IsNative() const {
70 return false;
73 bool BrowserAccessibility::IsDescendantOf(
74 BrowserAccessibility* ancestor) {
75 if (this == ancestor) {
76 return true;
77 } else if (GetParent()) {
78 return GetParent()->IsDescendantOf(ancestor);
81 return false;
84 BrowserAccessibility* BrowserAccessibility::PlatformGetChild(
85 uint32 child_index) const {
86 DCHECK(child_index < InternalChildCount());
87 return InternalGetChild(child_index);
90 BrowserAccessibility* BrowserAccessibility::GetPreviousSibling() {
91 if (GetParent() && GetIndexInParent() > 0)
92 return GetParent()->InternalGetChild(GetIndexInParent() - 1);
94 return NULL;
97 BrowserAccessibility* BrowserAccessibility::GetNextSibling() {
98 if (GetParent() &&
99 GetIndexInParent() >= 0 &&
100 GetIndexInParent() < static_cast<int>(
101 GetParent()->InternalChildCount() - 1)) {
102 return GetParent()->InternalGetChild(GetIndexInParent() + 1);
105 return NULL;
108 uint32 BrowserAccessibility::InternalChildCount() const {
109 if (!node_ || !manager_)
110 return 0;
111 return static_cast<uint32>(node_->child_count());
114 BrowserAccessibility* BrowserAccessibility::InternalGetChild(
115 uint32 child_index) const {
116 if (!node_ || !manager_)
117 return NULL;
118 return manager_->GetFromAXNode(node_->children()[child_index]);
121 BrowserAccessibility* BrowserAccessibility::GetParent() const {
122 if (!node_ || !manager_)
123 return NULL;
124 ui::AXNode* parent = node_->parent();
125 return parent ? manager_->GetFromAXNode(parent) : NULL;
128 int32 BrowserAccessibility::GetIndexInParent() const {
129 return node_ ? node_->index_in_parent() : -1;
132 int32 BrowserAccessibility::GetId() const {
133 return node_ ? node_->id() : -1;
136 const ui::AXNodeData& BrowserAccessibility::GetData() const {
137 CR_DEFINE_STATIC_LOCAL(ui::AXNodeData, empty_data, ());
138 if (node_)
139 return node_->data();
140 else
141 return empty_data;
144 gfx::Rect BrowserAccessibility::GetLocation() const {
145 return GetData().location;
148 int32 BrowserAccessibility::GetRole() const {
149 return GetData().role;
152 int32 BrowserAccessibility::GetState() const {
153 return GetData().state;
156 const std::vector<std::pair<std::string, std::string> >&
157 BrowserAccessibility::GetHtmlAttributes() const {
158 return GetData().html_attributes;
161 gfx::Rect BrowserAccessibility::GetLocalBoundsRect() const {
162 gfx::Rect bounds = GetLocation();
164 // Walk up the parent chain. Every time we encounter a Web Area, offset
165 // based on the scroll bars and then offset based on the origin of that
166 // nested web area.
167 BrowserAccessibility* parent = GetParent();
168 bool need_to_offset_web_area =
169 (GetRole() == ui::AX_ROLE_WEB_AREA ||
170 GetRole() == ui::AX_ROLE_ROOT_WEB_AREA);
171 while (parent) {
172 if (need_to_offset_web_area &&
173 parent->GetLocation().width() > 0 &&
174 parent->GetLocation().height() > 0) {
175 bounds.Offset(parent->GetLocation().x(), parent->GetLocation().y());
176 need_to_offset_web_area = false;
179 // On some platforms, we don't want to take the root scroll offsets
180 // into account.
181 if (parent->GetRole() == ui::AX_ROLE_ROOT_WEB_AREA &&
182 !manager()->UseRootScrollOffsetsWhenComputingBounds()) {
183 break;
186 if (parent->GetRole() == ui::AX_ROLE_WEB_AREA ||
187 parent->GetRole() == ui::AX_ROLE_ROOT_WEB_AREA) {
188 int sx = 0;
189 int sy = 0;
190 if (parent->GetIntAttribute(ui::AX_ATTR_SCROLL_X, &sx) &&
191 parent->GetIntAttribute(ui::AX_ATTR_SCROLL_Y, &sy)) {
192 bounds.Offset(-sx, -sy);
194 need_to_offset_web_area = true;
196 parent = parent->GetParent();
199 return bounds;
202 gfx::Rect BrowserAccessibility::GetGlobalBoundsRect() const {
203 gfx::Rect bounds = GetLocalBoundsRect();
205 // Adjust the bounds by the top left corner of the containing view's bounds
206 // in screen coordinates.
207 bounds.Offset(manager_->GetViewBounds().OffsetFromOrigin());
209 return bounds;
212 gfx::Rect BrowserAccessibility::GetLocalBoundsForRange(int start, int len)
213 const {
214 if (GetRole() != ui::AX_ROLE_STATIC_TEXT) {
215 // Apply recursively to all static text descendants. For example, if
216 // you call it on a div with two text node children, it just calls
217 // GetLocalBoundsForRange on each of the two children (adjusting
218 // |start| for each one) and unions the resulting rects.
219 gfx::Rect bounds;
220 for (size_t i = 0; i < InternalChildCount(); ++i) {
221 BrowserAccessibility* child = InternalGetChild(i);
222 int child_len = child->GetStaticTextLenRecursive();
223 if (start < child_len && start + len > 0) {
224 gfx::Rect child_rect = child->GetLocalBoundsForRange(start, len);
225 bounds.Union(child_rect);
227 start -= child_len;
229 return bounds;
232 int end = start + len;
233 int child_start = 0;
234 int child_end = 0;
236 gfx::Rect bounds;
237 for (size_t i = 0; i < InternalChildCount() && child_end < start + len; ++i) {
238 BrowserAccessibility* child = InternalGetChild(i);
239 DCHECK_EQ(child->GetRole(), ui::AX_ROLE_INLINE_TEXT_BOX);
240 std::string child_text;
241 child->GetStringAttribute(ui::AX_ATTR_VALUE, &child_text);
242 int child_len = static_cast<int>(child_text.size());
243 child_start = child_end;
244 child_end += child_len;
246 if (child_end < start)
247 continue;
249 int overlap_start = std::max(start, child_start);
250 int overlap_end = std::min(end, child_end);
252 int local_start = overlap_start - child_start;
253 int local_end = overlap_end - child_start;
255 gfx::Rect child_rect = child->GetLocation();
256 int text_direction = child->GetIntAttribute(
257 ui::AX_ATTR_TEXT_DIRECTION);
258 const std::vector<int32>& character_offsets = child->GetIntListAttribute(
259 ui::AX_ATTR_CHARACTER_OFFSETS);
260 int start_pixel_offset =
261 local_start > 0 ? character_offsets[local_start - 1] : 0;
262 int end_pixel_offset =
263 local_end > 0 ? character_offsets[local_end - 1] : 0;
265 gfx::Rect child_overlap_rect;
266 switch (text_direction) {
267 case ui::AX_TEXT_DIRECTION_NONE:
268 case ui::AX_TEXT_DIRECTION_LR: {
269 int left = child_rect.x() + start_pixel_offset;
270 int right = child_rect.x() + end_pixel_offset;
271 child_overlap_rect = gfx::Rect(left, child_rect.y(),
272 right - left, child_rect.height());
273 break;
275 case ui::AX_TEXT_DIRECTION_RL: {
276 int right = child_rect.right() - start_pixel_offset;
277 int left = child_rect.right() - end_pixel_offset;
278 child_overlap_rect = gfx::Rect(left, child_rect.y(),
279 right - left, child_rect.height());
280 break;
282 case ui::AX_TEXT_DIRECTION_TB: {
283 int top = child_rect.y() + start_pixel_offset;
284 int bottom = child_rect.y() + end_pixel_offset;
285 child_overlap_rect = gfx::Rect(child_rect.x(), top,
286 child_rect.width(), bottom - top);
287 break;
289 case ui::AX_TEXT_DIRECTION_BT: {
290 int bottom = child_rect.bottom() - start_pixel_offset;
291 int top = child_rect.bottom() - end_pixel_offset;
292 child_overlap_rect = gfx::Rect(child_rect.x(), top,
293 child_rect.width(), bottom - top);
294 break;
296 default:
297 NOTREACHED();
300 if (bounds.width() == 0 && bounds.height() == 0)
301 bounds = child_overlap_rect;
302 else
303 bounds.Union(child_overlap_rect);
306 return bounds;
309 gfx::Rect BrowserAccessibility::GetGlobalBoundsForRange(int start, int len)
310 const {
311 gfx::Rect bounds = GetLocalBoundsForRange(start, len);
313 // Adjust the bounds by the top left corner of the containing view's bounds
314 // in screen coordinates.
315 bounds.Offset(manager_->GetViewBounds().OffsetFromOrigin());
317 return bounds;
320 BrowserAccessibility* BrowserAccessibility::BrowserAccessibilityForPoint(
321 const gfx::Point& point) {
322 // The best result found that's a child of this object.
323 BrowserAccessibility* child_result = NULL;
324 // The best result that's an indirect descendant like grandchild, etc.
325 BrowserAccessibility* descendant_result = NULL;
327 // Walk the children recursively looking for the BrowserAccessibility that
328 // most tightly encloses the specified point. Walk backwards so that in
329 // the absence of any other information, we assume the object that occurs
330 // later in the tree is on top of one that comes before it.
331 for (int i = static_cast<int>(PlatformChildCount()) - 1; i >= 0; --i) {
332 BrowserAccessibility* child = PlatformGetChild(i);
334 // Skip table columns because cells are only contained in rows,
335 // not columns.
336 if (child->GetRole() == ui::AX_ROLE_COLUMN)
337 continue;
339 if (child->GetGlobalBoundsRect().Contains(point)) {
340 BrowserAccessibility* result = child->BrowserAccessibilityForPoint(point);
341 if (result == child && !child_result)
342 child_result = result;
343 if (result != child && !descendant_result)
344 descendant_result = result;
347 if (child_result && descendant_result)
348 break;
351 // Explanation of logic: it's possible that this point overlaps more than
352 // one child of this object. If so, as a heuristic we prefer if the point
353 // overlaps a descendant of one of the two children and not the other.
354 // As an example, suppose you have two rows of buttons - the buttons don't
355 // overlap, but the rows do. Without this heuristic, we'd greedily only
356 // consider one of the containers.
357 if (descendant_result)
358 return descendant_result;
359 if (child_result)
360 return child_result;
362 return this;
365 void BrowserAccessibility::Destroy() {
366 // Allow the object to fire a TextRemoved notification.
367 name_.clear();
368 value_.clear();
370 manager_->NotifyAccessibilityEvent(ui::AX_EVENT_HIDE, this);
371 node_ = NULL;
372 manager_ = NULL;
374 NativeReleaseReference();
377 void BrowserAccessibility::NativeReleaseReference() {
378 delete this;
381 bool BrowserAccessibility::HasBoolAttribute(
382 ui::AXBoolAttribute attribute) const {
383 const ui::AXNodeData& data = GetData();
384 for (size_t i = 0; i < data.bool_attributes.size(); ++i) {
385 if (data.bool_attributes[i].first == attribute)
386 return true;
389 return false;
393 bool BrowserAccessibility::GetBoolAttribute(
394 ui::AXBoolAttribute attribute) const {
395 const ui::AXNodeData& data = GetData();
396 for (size_t i = 0; i < data.bool_attributes.size(); ++i) {
397 if (data.bool_attributes[i].first == attribute)
398 return data.bool_attributes[i].second;
401 return false;
404 bool BrowserAccessibility::GetBoolAttribute(
405 ui::AXBoolAttribute attribute, bool* value) const {
406 const ui::AXNodeData& data = GetData();
407 for (size_t i = 0; i < data.bool_attributes.size(); ++i) {
408 if (data.bool_attributes[i].first == attribute) {
409 *value = data.bool_attributes[i].second;
410 return true;
414 return false;
417 bool BrowserAccessibility::HasFloatAttribute(
418 ui::AXFloatAttribute attribute) const {
419 const ui::AXNodeData& data = GetData();
420 for (size_t i = 0; i < data.float_attributes.size(); ++i) {
421 if (data.float_attributes[i].first == attribute)
422 return true;
425 return false;
428 float BrowserAccessibility::GetFloatAttribute(
429 ui::AXFloatAttribute attribute) const {
430 const ui::AXNodeData& data = GetData();
431 for (size_t i = 0; i < data.float_attributes.size(); ++i) {
432 if (data.float_attributes[i].first == attribute)
433 return data.float_attributes[i].second;
436 return 0.0;
439 bool BrowserAccessibility::GetFloatAttribute(
440 ui::AXFloatAttribute attribute, float* value) const {
441 const ui::AXNodeData& data = GetData();
442 for (size_t i = 0; i < data.float_attributes.size(); ++i) {
443 if (data.float_attributes[i].first == attribute) {
444 *value = data.float_attributes[i].second;
445 return true;
449 return false;
452 bool BrowserAccessibility::HasIntAttribute(
453 ui::AXIntAttribute attribute) const {
454 const ui::AXNodeData& data = GetData();
455 for (size_t i = 0; i < data.int_attributes.size(); ++i) {
456 if (data.int_attributes[i].first == attribute)
457 return true;
460 return false;
463 int BrowserAccessibility::GetIntAttribute(ui::AXIntAttribute attribute) const {
464 const ui::AXNodeData& data = GetData();
465 for (size_t i = 0; i < data.int_attributes.size(); ++i) {
466 if (data.int_attributes[i].first == attribute)
467 return data.int_attributes[i].second;
470 return 0;
473 bool BrowserAccessibility::GetIntAttribute(
474 ui::AXIntAttribute attribute, int* value) const {
475 const ui::AXNodeData& data = GetData();
476 for (size_t i = 0; i < data.int_attributes.size(); ++i) {
477 if (data.int_attributes[i].first == attribute) {
478 *value = data.int_attributes[i].second;
479 return true;
483 return false;
486 bool BrowserAccessibility::HasStringAttribute(
487 ui::AXStringAttribute attribute) const {
488 const ui::AXNodeData& data = GetData();
489 for (size_t i = 0; i < data.string_attributes.size(); ++i) {
490 if (data.string_attributes[i].first == attribute)
491 return true;
494 return false;
497 const std::string& BrowserAccessibility::GetStringAttribute(
498 ui::AXStringAttribute attribute) const {
499 const ui::AXNodeData& data = GetData();
500 CR_DEFINE_STATIC_LOCAL(std::string, empty_string, ());
501 for (size_t i = 0; i < data.string_attributes.size(); ++i) {
502 if (data.string_attributes[i].first == attribute)
503 return data.string_attributes[i].second;
506 return empty_string;
509 bool BrowserAccessibility::GetStringAttribute(
510 ui::AXStringAttribute attribute, std::string* value) const {
511 const ui::AXNodeData& data = GetData();
512 for (size_t i = 0; i < data.string_attributes.size(); ++i) {
513 if (data.string_attributes[i].first == attribute) {
514 *value = data.string_attributes[i].second;
515 return true;
519 return false;
522 base::string16 BrowserAccessibility::GetString16Attribute(
523 ui::AXStringAttribute attribute) const {
524 std::string value_utf8;
525 if (!GetStringAttribute(attribute, &value_utf8))
526 return base::string16();
527 return base::UTF8ToUTF16(value_utf8);
530 bool BrowserAccessibility::GetString16Attribute(
531 ui::AXStringAttribute attribute,
532 base::string16* value) const {
533 std::string value_utf8;
534 if (!GetStringAttribute(attribute, &value_utf8))
535 return false;
536 *value = base::UTF8ToUTF16(value_utf8);
537 return true;
540 void BrowserAccessibility::SetStringAttribute(
541 ui::AXStringAttribute attribute, const std::string& value) {
542 if (!node_)
543 return;
544 ui::AXNodeData data = GetData();
545 for (size_t i = 0; i < data.string_attributes.size(); ++i) {
546 if (data.string_attributes[i].first == attribute) {
547 data.string_attributes[i].second = value;
548 node_->SetData(data);
549 return;
552 if (!value.empty()) {
553 data.string_attributes.push_back(std::make_pair(attribute, value));
554 node_->SetData(data);
558 bool BrowserAccessibility::HasIntListAttribute(
559 ui::AXIntListAttribute attribute) const {
560 const ui::AXNodeData& data = GetData();
561 for (size_t i = 0; i < data.intlist_attributes.size(); ++i) {
562 if (data.intlist_attributes[i].first == attribute)
563 return true;
566 return false;
569 const std::vector<int32>& BrowserAccessibility::GetIntListAttribute(
570 ui::AXIntListAttribute attribute) const {
571 const ui::AXNodeData& data = GetData();
572 CR_DEFINE_STATIC_LOCAL(std::vector<int32>, empty_vector, ());
573 for (size_t i = 0; i < data.intlist_attributes.size(); ++i) {
574 if (data.intlist_attributes[i].first == attribute)
575 return data.intlist_attributes[i].second;
578 return empty_vector;
581 bool BrowserAccessibility::GetIntListAttribute(
582 ui::AXIntListAttribute attribute,
583 std::vector<int32>* value) const {
584 const ui::AXNodeData& data = GetData();
585 for (size_t i = 0; i < data.intlist_attributes.size(); ++i) {
586 if (data.intlist_attributes[i].first == attribute) {
587 *value = data.intlist_attributes[i].second;
588 return true;
592 return false;
595 bool BrowserAccessibility::GetHtmlAttribute(
596 const char* html_attr, std::string* value) const {
597 for (size_t i = 0; i < GetHtmlAttributes().size(); ++i) {
598 const std::string& attr = GetHtmlAttributes()[i].first;
599 if (LowerCaseEqualsASCII(attr, html_attr)) {
600 *value = GetHtmlAttributes()[i].second;
601 return true;
605 return false;
608 bool BrowserAccessibility::GetHtmlAttribute(
609 const char* html_attr, base::string16* value) const {
610 std::string value_utf8;
611 if (!GetHtmlAttribute(html_attr, &value_utf8))
612 return false;
613 *value = base::UTF8ToUTF16(value_utf8);
614 return true;
617 bool BrowserAccessibility::GetAriaTristate(
618 const char* html_attr,
619 bool* is_defined,
620 bool* is_mixed) const {
621 *is_defined = false;
622 *is_mixed = false;
624 base::string16 value;
625 if (!GetHtmlAttribute(html_attr, &value) ||
626 value.empty() ||
627 EqualsASCII(value, "undefined")) {
628 return false; // Not set (and *is_defined is also false)
631 *is_defined = true;
633 if (EqualsASCII(value, "true"))
634 return true;
636 if (EqualsASCII(value, "mixed"))
637 *is_mixed = true;
639 return false; // Not set
642 bool BrowserAccessibility::HasState(ui::AXState state_enum) const {
643 return (GetState() >> state_enum) & 1;
646 bool BrowserAccessibility::IsEditableText() const {
647 // These roles don't have readonly set, but they're not editable text.
648 if (GetRole() == ui::AX_ROLE_SCROLL_AREA ||
649 GetRole() == ui::AX_ROLE_COLUMN ||
650 GetRole() == ui::AX_ROLE_TABLE_HEADER_CONTAINER) {
651 return false;
654 // Note: WebAXStateReadonly being false means it's either a text control,
655 // or contenteditable. We also check for editable text roles to cover
656 // another element that has role=textbox set on it.
657 return (!HasState(ui::AX_STATE_READ_ONLY) ||
658 GetRole() == ui::AX_ROLE_TEXT_FIELD ||
659 GetRole() == ui::AX_ROLE_TEXT_AREA);
662 std::string BrowserAccessibility::GetTextRecursive() const {
663 if (!name_.empty()) {
664 return name_;
667 std::string result;
668 for (uint32 i = 0; i < PlatformChildCount(); ++i)
669 result += PlatformGetChild(i)->GetTextRecursive();
670 return result;
673 int BrowserAccessibility::GetStaticTextLenRecursive() const {
674 if (GetRole() == ui::AX_ROLE_STATIC_TEXT)
675 return static_cast<int>(GetStringAttribute(ui::AX_ATTR_VALUE).size());
677 int len = 0;
678 for (size_t i = 0; i < InternalChildCount(); ++i)
679 len += InternalGetChild(i)->GetStaticTextLenRecursive();
680 return len;
683 } // namespace content