Upstream version 5.34.92.0
[platform/framework/web/crosswalk.git] / src / content / browser / accessibility / browser_accessibility.cc
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.
4
5 #include "content/browser/accessibility/browser_accessibility.h"
6
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"
13
14 namespace content {
15
16 #if !defined(OS_MACOSX) && \
17     !defined(OS_WIN) && \
18     !defined(TOOLKIT_GTK) && \
19     !defined(OS_ANDROID)
20 // We have subclassess of BrowserAccessibility on Mac, Linux/GTK,
21 // and Win. For any other platform, instantiate the base class.
22 // static
23 BrowserAccessibility* BrowserAccessibility::Create() {
24   return new BrowserAccessibility();
25 }
26 #endif
27
28 BrowserAccessibility::BrowserAccessibility()
29     : manager_(NULL),
30       parent_(NULL),
31       index_in_parent_(0),
32       renderer_id_(0),
33       role_(0),
34       state_(0),
35       instance_active_(false) {
36 }
37
38 BrowserAccessibility::~BrowserAccessibility() {
39 }
40
41 bool BrowserAccessibility::PlatformIsLeaf() const {
42   if (child_count() == 0)
43     return true;
44
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 (role_) {
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:
54       return true;
55     default:
56       return false;
57   }
58 }
59
60 uint32 BrowserAccessibility::PlatformChildCount() const {
61   return PlatformIsLeaf() ? 0 : children_.size();
62 }
63
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);
69   children_.clear();
70   parent_ = NULL;
71 }
72
73 void BrowserAccessibility::InitializeTreeStructure(
74     BrowserAccessibilityManager* manager,
75     BrowserAccessibility* parent,
76     int32 renderer_id,
77     int32 index_in_parent) {
78   manager_ = manager;
79   parent_ = parent;
80   renderer_id_ = renderer_id;
81   index_in_parent_ = index_in_parent;
82 }
83
84 void BrowserAccessibility::InitializeData(const ui::AXNodeData& src) {
85   DCHECK_EQ(renderer_id_, src.id);
86   role_ = src.role;
87   state_ = src.state;
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;
96
97   GetStringAttribute(ui::AX_ATTR_NAME, &name_);
98   GetStringAttribute(ui::AX_ATTR_VALUE, &value_);
99
100   PreInitialize();
101 }
102
103 bool BrowserAccessibility::IsNative() const {
104   return false;
105 }
106
107 void BrowserAccessibility::SwapChildren(
108     std::vector<BrowserAccessibility*>& children) {
109   children.swap(children_);
110 }
111
112 void BrowserAccessibility::UpdateParent(BrowserAccessibility* parent,
113                                         int index_in_parent) {
114   parent_ = parent;
115   index_in_parent_ = index_in_parent;
116 }
117
118 void BrowserAccessibility::SetLocation(const gfx::Rect& new_location) {
119   location_ = new_location;
120 }
121
122 bool BrowserAccessibility::IsDescendantOf(
123     BrowserAccessibility* ancestor) {
124   if (this == ancestor) {
125     return true;
126   } else if (parent_) {
127     return parent_->IsDescendantOf(ancestor);
128   }
129
130   return false;
131 }
132
133 BrowserAccessibility* BrowserAccessibility::PlatformGetChild(
134     uint32 child_index) const {
135   DCHECK(child_index < children_.size());
136   return children_[child_index];
137 }
138
139 BrowserAccessibility* BrowserAccessibility::GetPreviousSibling() {
140   if (parent_ && index_in_parent_ > 0)
141     return parent_->children_[index_in_parent_ - 1];
142
143   return NULL;
144 }
145
146 BrowserAccessibility* BrowserAccessibility::GetNextSibling() {
147   if (parent_ &&
148       index_in_parent_ >= 0 &&
149       index_in_parent_ < static_cast<int>(parent_->children_.size() - 1)) {
150     return parent_->children_[index_in_parent_ + 1];
151   }
152
153   return NULL;
154 }
155
156 gfx::Rect BrowserAccessibility::GetLocalBoundsRect() const {
157   gfx::Rect bounds = location_;
158
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
161   // nested web area.
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);
166   while (parent) {
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;
172     }
173
174     // On some platforms, we don't want to take the root scroll offsets
175     // into account.
176     if (parent->role() == ui::AX_ROLE_ROOT_WEB_AREA &&
177         !manager()->UseRootScrollOffsetsWhenComputingBounds()) {
178       break;
179     }
180
181     if (parent->role() == ui::AX_ROLE_WEB_AREA ||
182         parent->role() == ui::AX_ROLE_ROOT_WEB_AREA) {
183       int sx = 0;
184       int sy = 0;
185       if (parent->GetIntAttribute(ui::AX_ATTR_SCROLL_X, &sx) &&
186           parent->GetIntAttribute(ui::AX_ATTR_SCROLL_Y, &sy)) {
187         bounds.Offset(-sx, -sy);
188       }
189       need_to_offset_web_area = true;
190     }
191     parent = parent->parent();
192   }
193
194   return bounds;
195 }
196
197 gfx::Rect BrowserAccessibility::GetGlobalBoundsRect() const {
198   gfx::Rect bounds = GetLocalBoundsRect();
199
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());
203
204   return bounds;
205 }
206
207 gfx::Rect BrowserAccessibility::GetLocalBoundsForRange(int start, int len)
208     const {
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.
214     gfx::Rect bounds;
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);
221       }
222       start -= child_len;
223     }
224     return bounds;
225   }
226
227   int end = start + len;
228   int child_start = 0;
229   int child_end = 0;
230
231   gfx::Rect bounds;
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;
240
241     if (child_end < start)
242       continue;
243
244     int overlap_start = std::max(start, child_start);
245     int overlap_end = std::min(end, child_end);
246
247     int local_start = overlap_start - child_start;
248     int local_end = overlap_end - child_start;
249
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;
259
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());
267         break;
268       }
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());
274         break;
275       }
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);
281         break;
282       }
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);
288         break;
289       }
290       default:
291         NOTREACHED();
292     }
293
294     if (bounds.width() == 0 && bounds.height() == 0)
295       bounds = child_overlap_rect;
296     else
297       bounds.Union(child_overlap_rect);
298   }
299
300   return bounds;
301 }
302
303 gfx::Rect BrowserAccessibility::GetGlobalBoundsForRange(int start, int len)
304     const {
305   gfx::Rect bounds = GetLocalBoundsForRange(start, len);
306
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());
310
311   return bounds;
312 }
313
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);
322   }
323   return this;
324 }
325
326 void BrowserAccessibility::Destroy() {
327   for (std::vector<BrowserAccessibility*>::iterator iter = children_.begin();
328        iter != children_.end();
329        ++iter) {
330     (*iter)->Destroy();
331   }
332   children_.clear();
333
334   // Allow the object to fire a TextRemoved notification.
335   name_.clear();
336   value_.clear();
337   PostInitialize();
338
339   manager_->NotifyAccessibilityEvent(ui::AX_EVENT_HIDE, this);
340
341   instance_active_ = false;
342   manager_->RemoveNode(this);
343   NativeReleaseReference();
344 }
345
346 void BrowserAccessibility::NativeReleaseReference() {
347   delete this;
348 }
349
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)
354       return true;
355   }
356
357   return false;
358 }
359
360
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;
366   }
367
368   return false;
369 }
370
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;
376       return true;
377     }
378   }
379
380   return false;
381 }
382
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)
387       return true;
388   }
389
390   return false;
391 }
392
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;
398   }
399
400   return 0.0;
401 }
402
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;
408       return true;
409     }
410   }
411
412   return false;
413 }
414
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)
419       return true;
420   }
421
422   return false;
423 }
424
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;
429   }
430
431   return 0;
432 }
433
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;
439       return true;
440     }
441   }
442
443   return false;
444 }
445
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)
450       return true;
451   }
452
453   return false;
454 }
455
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;
462   }
463
464   return empty_string;
465 }
466
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;
472       return true;
473     }
474   }
475
476   return false;
477 }
478
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);
485 }
486
487 bool BrowserAccessibility::GetString16Attribute(
488     ui::AXStringAttribute attribute,
489     base::string16* value) const {
490   std::string value_utf8;
491   if (!GetStringAttribute(attribute, &value_utf8))
492     return false;
493   *value = base::UTF8ToUTF16(value_utf8);
494   return true;
495 }
496
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;
502       return;
503     }
504   }
505   if (!value.empty())
506     string_attributes_.push_back(std::make_pair(attribute, value));
507 }
508
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)
513       return true;
514   }
515
516   return false;
517 }
518
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;
525   }
526
527   return empty_vector;
528 }
529
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;
536       return true;
537     }
538   }
539
540   return false;
541 }
542
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;
549       return true;
550     }
551   }
552
553   return false;
554 }
555
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))
560     return false;
561   *value = base::UTF8ToUTF16(value_utf8);
562   return true;
563 }
564
565 bool BrowserAccessibility::GetAriaTristate(
566     const char* html_attr,
567     bool* is_defined,
568     bool* is_mixed) const {
569   *is_defined = false;
570   *is_mixed = false;
571
572   base::string16 value;
573   if (!GetHtmlAttribute(html_attr, &value) ||
574       value.empty() ||
575       EqualsASCII(value, "undefined")) {
576     return false;  // Not set (and *is_defined is also false)
577   }
578
579   *is_defined = true;
580
581   if (EqualsASCII(value, "true"))
582     return true;
583
584   if (EqualsASCII(value, "mixed"))
585     *is_mixed = true;
586
587   return false;  // Not set
588 }
589
590 bool BrowserAccessibility::HasState(ui::AXState state_enum) const {
591   return (state_ >> state_enum) & 1;
592 }
593
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) {
599     return false;
600   }
601
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);
608 }
609
610 std::string BrowserAccessibility::GetTextRecursive() const {
611   if (!name_.empty()) {
612     return name_;
613   }
614
615   std::string result;
616   for (uint32 i = 0; i < PlatformChildCount(); ++i)
617     result += PlatformGetChild(i)->GetTextRecursive();
618   return result;
619 }
620
621 int BrowserAccessibility::GetStaticTextLenRecursive() const {
622   if (role_ == blink::WebAXRoleStaticText)
623     return static_cast<int>(GetStringAttribute(ui::AX_ATTR_VALUE).size());
624
625   int len = 0;
626   for (size_t i = 0; i < children_.size(); ++i)
627     len += children_[i]->GetStaticTextLenRecursive();
628   return len;
629 }
630
631 }  // namespace content