- add sources.
[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 typedef AccessibilityNodeData::BoolAttribute BoolAttribute;
17 typedef AccessibilityNodeData::FloatAttribute FloatAttribute;
18 typedef AccessibilityNodeData::IntAttribute IntAttribute;
19 typedef AccessibilityNodeData::StringAttribute StringAttribute;
20
21 #if !defined(OS_MACOSX) && \
22     !defined(OS_WIN) && \
23     !defined(TOOLKIT_GTK) && \
24     !defined(OS_ANDROID)
25 // We have subclassess of BrowserAccessibility on Mac, Linux/GTK,
26 // and Win. For any other platform, instantiate the base class.
27 // static
28 BrowserAccessibility* BrowserAccessibility::Create() {
29   return new BrowserAccessibility();
30 }
31 #endif
32
33 BrowserAccessibility::BrowserAccessibility()
34     : manager_(NULL),
35       parent_(NULL),
36       index_in_parent_(0),
37       renderer_id_(0),
38       role_(0),
39       state_(0),
40       instance_active_(false) {
41 }
42
43 BrowserAccessibility::~BrowserAccessibility() {
44 }
45
46 bool BrowserAccessibility::PlatformIsLeaf() const {
47   return role_ == WebKit::WebAXRoleStaticText || child_count() == 0;
48 }
49
50 uint32 BrowserAccessibility::PlatformChildCount() const {
51   return PlatformIsLeaf() ? 0 : children_.size();
52 }
53
54 void BrowserAccessibility::DetachTree(
55     std::vector<BrowserAccessibility*>* nodes) {
56   nodes->push_back(this);
57   for (size_t i = 0; i < children_.size(); ++i)
58     children_[i]->DetachTree(nodes);
59   children_.clear();
60   parent_ = NULL;
61 }
62
63 void BrowserAccessibility::InitializeTreeStructure(
64     BrowserAccessibilityManager* manager,
65     BrowserAccessibility* parent,
66     int32 renderer_id,
67     int32 index_in_parent) {
68   manager_ = manager;
69   parent_ = parent;
70   renderer_id_ = renderer_id;
71   index_in_parent_ = index_in_parent;
72 }
73
74 void BrowserAccessibility::InitializeData(const AccessibilityNodeData& src) {
75   DCHECK_EQ(renderer_id_, src.id);
76   role_ = src.role;
77   state_ = src.state;
78   string_attributes_ = src.string_attributes;
79   int_attributes_ = src.int_attributes;
80   float_attributes_ = src.float_attributes;
81   bool_attributes_ = src.bool_attributes;
82   intlist_attributes_ = src.intlist_attributes;
83   html_attributes_ = src.html_attributes;
84   location_ = src.location;
85   instance_active_ = true;
86
87   GetStringAttribute(AccessibilityNodeData::ATTR_NAME, &name_);
88   GetStringAttribute(AccessibilityNodeData::ATTR_VALUE, &value_);
89
90   PreInitialize();
91 }
92
93 bool BrowserAccessibility::IsNative() const {
94   return false;
95 }
96
97 void BrowserAccessibility::SwapChildren(
98     std::vector<BrowserAccessibility*>& children) {
99   children.swap(children_);
100 }
101
102 void BrowserAccessibility::UpdateParent(BrowserAccessibility* parent,
103                                         int index_in_parent) {
104   parent_ = parent;
105   index_in_parent_ = index_in_parent;
106 }
107
108 void BrowserAccessibility::SetLocation(const gfx::Rect& new_location) {
109   location_ = new_location;
110 }
111
112 bool BrowserAccessibility::IsDescendantOf(
113     BrowserAccessibility* ancestor) {
114   if (this == ancestor) {
115     return true;
116   } else if (parent_) {
117     return parent_->IsDescendantOf(ancestor);
118   }
119
120   return false;
121 }
122
123 BrowserAccessibility* BrowserAccessibility::PlatformGetChild(
124     uint32 child_index) const {
125   DCHECK(child_index < children_.size());
126   return children_[child_index];
127 }
128
129 BrowserAccessibility* BrowserAccessibility::GetPreviousSibling() {
130   if (parent_ && index_in_parent_ > 0)
131     return parent_->children_[index_in_parent_ - 1];
132
133   return NULL;
134 }
135
136 BrowserAccessibility* BrowserAccessibility::GetNextSibling() {
137   if (parent_ &&
138       index_in_parent_ >= 0 &&
139       index_in_parent_ < static_cast<int>(parent_->children_.size() - 1)) {
140     return parent_->children_[index_in_parent_ + 1];
141   }
142
143   return NULL;
144 }
145
146 gfx::Rect BrowserAccessibility::GetLocalBoundsRect() const {
147   gfx::Rect bounds = location_;
148
149   // Walk up the parent chain. Every time we encounter a Web Area, offset
150   // based on the scroll bars and then offset based on the origin of that
151   // nested web area.
152   BrowserAccessibility* parent = parent_;
153   bool need_to_offset_web_area =
154       (role_ == WebKit::WebAXRoleWebArea ||
155        role_ == WebKit::WebAXRoleRootWebArea);
156   while (parent) {
157     if (need_to_offset_web_area &&
158         parent->location().width() > 0 &&
159         parent->location().height() > 0) {
160       bounds.Offset(parent->location().x(), parent->location().y());
161       need_to_offset_web_area = false;
162     }
163
164     // On some platforms, we don't want to take the root scroll offsets
165     // into account.
166     if (parent->role() == WebKit::WebAXRoleRootWebArea &&
167         !manager()->UseRootScrollOffsetsWhenComputingBounds()) {
168       break;
169     }
170
171     if (parent->role() == WebKit::WebAXRoleWebArea ||
172         parent->role() == WebKit::WebAXRoleRootWebArea) {
173       int sx = 0;
174       int sy = 0;
175       if (parent->GetIntAttribute(AccessibilityNodeData::ATTR_SCROLL_X, &sx) &&
176           parent->GetIntAttribute(AccessibilityNodeData::ATTR_SCROLL_Y, &sy)) {
177         bounds.Offset(-sx, -sy);
178       }
179       need_to_offset_web_area = true;
180     }
181     parent = parent->parent();
182   }
183
184   return bounds;
185 }
186
187 gfx::Rect BrowserAccessibility::GetGlobalBoundsRect() const {
188   gfx::Rect bounds = GetLocalBoundsRect();
189
190   // Adjust the bounds by the top left corner of the containing view's bounds
191   // in screen coordinates.
192   bounds.Offset(manager_->GetViewBounds().OffsetFromOrigin());
193
194   return bounds;
195 }
196
197 gfx::Rect BrowserAccessibility::GetLocalBoundsForRange(int start, int len)
198     const {
199   DCHECK_EQ(role_, WebKit::WebAXRoleStaticText);
200   int end = start + len;
201   int child_start = 0;
202   int child_end = 0;
203
204   gfx::Rect bounds;
205   for (size_t i = 0; i < children_.size() && child_end < start + len; ++i) {
206     BrowserAccessibility* child = children_[i];
207     DCHECK_EQ(child->role(), WebKit::WebAXRoleInlineTextBox);
208     std::string child_text;
209     child->GetStringAttribute(AccessibilityNodeData::ATTR_VALUE, &child_text);
210     int child_len = static_cast<int>(child_text.size());
211     child_start = child_end;
212     child_end += child_len;
213
214     if (child_end < start)
215       continue;
216
217     int overlap_start = std::max(start, child_start);
218     int overlap_end = std::min(end, child_end);
219
220     int local_start = overlap_start - child_start;
221     int local_end = overlap_end - child_start;
222
223     gfx::Rect child_rect = child->location();
224     int text_direction = child->GetIntAttribute(
225         AccessibilityNodeData::ATTR_TEXT_DIRECTION);
226     const std::vector<int32>& character_offsets = child->GetIntListAttribute(
227         AccessibilityNodeData::ATTR_CHARACTER_OFFSETS);
228     int start_pixel_offset =
229         local_start > 0 ? character_offsets[local_start - 1] : 0;
230     int end_pixel_offset =
231         local_end > 0 ? character_offsets[local_end - 1] : 0;
232
233     gfx::Rect child_overlap_rect;
234     switch (text_direction) {
235       case WebKit::WebAXTextDirectionLR: {
236         int left = child_rect.x() + start_pixel_offset;
237         int right = child_rect.x() + end_pixel_offset;
238         child_overlap_rect = gfx::Rect(left, child_rect.y(),
239                                        right - left, child_rect.height());
240         break;
241       }
242       case WebKit::WebAXTextDirectionRL: {
243         int right = child_rect.right() - start_pixel_offset;
244         int left = child_rect.right() - end_pixel_offset;
245         child_overlap_rect = gfx::Rect(left, child_rect.y(),
246                                        right - left, child_rect.height());
247         break;
248       }
249       case WebKit::WebAXTextDirectionTB: {
250         int top = child_rect.y() + start_pixel_offset;
251         int bottom = child_rect.y() + end_pixel_offset;
252         child_overlap_rect = gfx::Rect(child_rect.x(), top,
253                                        child_rect.width(), bottom - top);
254         break;
255       }
256       case WebKit::WebAXTextDirectionBT: {
257         int bottom = child_rect.bottom() - start_pixel_offset;
258         int top = child_rect.bottom() - end_pixel_offset;
259         child_overlap_rect = gfx::Rect(child_rect.x(), top,
260                                        child_rect.width(), bottom - top);
261         break;
262       }
263       default:
264         NOTREACHED();
265     }
266
267     if (bounds.width() == 0 && bounds.height() == 0)
268       bounds = child_overlap_rect;
269     else
270       bounds.Union(child_overlap_rect);
271   }
272
273   return bounds;
274 }
275
276 gfx::Rect BrowserAccessibility::GetGlobalBoundsForRange(int start, int len)
277     const {
278   gfx::Rect bounds = GetLocalBoundsForRange(start, len);
279
280   // Adjust the bounds by the top left corner of the containing view's bounds
281   // in screen coordinates.
282   bounds.Offset(manager_->GetViewBounds().OffsetFromOrigin());
283
284   return bounds;
285 }
286
287 BrowserAccessibility* BrowserAccessibility::BrowserAccessibilityForPoint(
288     const gfx::Point& point) {
289   // Walk the children recursively looking for the BrowserAccessibility that
290   // most tightly encloses the specified point.
291   for (int i = children_.size() - 1; i >= 0; --i) {
292     BrowserAccessibility* child = children_[i];
293     if (child->GetGlobalBoundsRect().Contains(point))
294       return child->BrowserAccessibilityForPoint(point);
295   }
296   return this;
297 }
298
299 void BrowserAccessibility::Destroy() {
300   for (std::vector<BrowserAccessibility*>::iterator iter = children_.begin();
301        iter != children_.end();
302        ++iter) {
303     (*iter)->Destroy();
304   }
305   children_.clear();
306
307   // Allow the object to fire a TextRemoved notification.
308   name_.clear();
309   value_.clear();
310   PostInitialize();
311
312   manager_->NotifyAccessibilityEvent(
313       WebKit::WebAXEventHide, this);
314
315   instance_active_ = false;
316   manager_->RemoveNode(this);
317   NativeReleaseReference();
318 }
319
320 void BrowserAccessibility::NativeReleaseReference() {
321   delete this;
322 }
323
324 bool BrowserAccessibility::HasBoolAttribute(BoolAttribute attribute) const {
325   for (size_t i = 0; i < bool_attributes_.size(); ++i) {
326     if (bool_attributes_[i].first == attribute)
327       return true;
328   }
329
330   return false;
331 }
332
333
334 bool BrowserAccessibility::GetBoolAttribute(BoolAttribute attribute) const {
335   for (size_t i = 0; i < bool_attributes_.size(); ++i) {
336     if (bool_attributes_[i].first == attribute)
337       return bool_attributes_[i].second;
338   }
339
340   return false;
341 }
342
343 bool BrowserAccessibility::GetBoolAttribute(
344     BoolAttribute attribute, bool* value) const {
345   for (size_t i = 0; i < bool_attributes_.size(); ++i) {
346     if (bool_attributes_[i].first == attribute) {
347       *value = bool_attributes_[i].second;
348       return true;
349     }
350   }
351
352   return false;
353 }
354
355 bool BrowserAccessibility::HasFloatAttribute(FloatAttribute attribute) const {
356   for (size_t i = 0; i < float_attributes_.size(); ++i) {
357     if (float_attributes_[i].first == attribute)
358       return true;
359   }
360
361   return false;
362 }
363
364 float BrowserAccessibility::GetFloatAttribute(FloatAttribute attribute) const {
365   for (size_t i = 0; i < float_attributes_.size(); ++i) {
366     if (float_attributes_[i].first == attribute)
367       return float_attributes_[i].second;
368   }
369
370   return 0.0;
371 }
372
373 bool BrowserAccessibility::GetFloatAttribute(
374     FloatAttribute attribute, float* value) const {
375   for (size_t i = 0; i < float_attributes_.size(); ++i) {
376     if (float_attributes_[i].first == attribute) {
377       *value = float_attributes_[i].second;
378       return true;
379     }
380   }
381
382   return false;
383 }
384
385 bool BrowserAccessibility::HasIntAttribute(IntAttribute attribute) const {
386   for (size_t i = 0; i < int_attributes_.size(); ++i) {
387     if (int_attributes_[i].first == attribute)
388       return true;
389   }
390
391   return false;
392 }
393
394 int BrowserAccessibility::GetIntAttribute(IntAttribute attribute) const {
395   for (size_t i = 0; i < int_attributes_.size(); ++i) {
396     if (int_attributes_[i].first == attribute)
397       return int_attributes_[i].second;
398   }
399
400   return 0;
401 }
402
403 bool BrowserAccessibility::GetIntAttribute(
404     IntAttribute attribute, int* value) const {
405   for (size_t i = 0; i < int_attributes_.size(); ++i) {
406     if (int_attributes_[i].first == attribute) {
407       *value = int_attributes_[i].second;
408       return true;
409     }
410   }
411
412   return false;
413 }
414
415 bool BrowserAccessibility::HasStringAttribute(StringAttribute attribute) const {
416   for (size_t i = 0; i < string_attributes_.size(); ++i) {
417     if (string_attributes_[i].first == attribute)
418       return true;
419   }
420
421   return false;
422 }
423
424 const std::string& BrowserAccessibility::GetStringAttribute(
425     StringAttribute attribute) const {
426   CR_DEFINE_STATIC_LOCAL(std::string, empty_string, ());
427   for (size_t i = 0; i < string_attributes_.size(); ++i) {
428     if (string_attributes_[i].first == attribute)
429       return string_attributes_[i].second;
430   }
431
432   return empty_string;
433 }
434
435 bool BrowserAccessibility::GetStringAttribute(
436     StringAttribute attribute, std::string* value) const {
437   for (size_t i = 0; i < string_attributes_.size(); ++i) {
438     if (string_attributes_[i].first == attribute) {
439       *value = string_attributes_[i].second;
440       return true;
441     }
442   }
443
444   return false;
445 }
446
447 string16 BrowserAccessibility::GetString16Attribute(
448     StringAttribute attribute) const {
449   std::string value_utf8;
450   if (!GetStringAttribute(attribute, &value_utf8))
451     return string16();
452   return UTF8ToUTF16(value_utf8);
453 }
454
455 bool BrowserAccessibility::GetString16Attribute(
456     StringAttribute attribute,
457     string16* value) const {
458   std::string value_utf8;
459   if (!GetStringAttribute(attribute, &value_utf8))
460     return false;
461   *value = UTF8ToUTF16(value_utf8);
462   return true;
463 }
464
465 void BrowserAccessibility::SetStringAttribute(
466     StringAttribute attribute, const std::string& value) {
467   for (size_t i = 0; i < string_attributes_.size(); ++i) {
468     if (string_attributes_[i].first == attribute) {
469       string_attributes_[i].second = value;
470       return;
471     }
472   }
473   if (!value.empty())
474     string_attributes_.push_back(std::make_pair(attribute, value));
475 }
476
477 bool BrowserAccessibility::HasIntListAttribute(
478     AccessibilityNodeData::IntListAttribute attribute) const {
479   for (size_t i = 0; i < intlist_attributes_.size(); ++i) {
480     if (intlist_attributes_[i].first == attribute)
481       return true;
482   }
483
484   return false;
485 }
486
487 const std::vector<int32>& BrowserAccessibility::GetIntListAttribute(
488     AccessibilityNodeData::IntListAttribute attribute) const {
489   CR_DEFINE_STATIC_LOCAL(std::vector<int32>, empty_vector, ());
490   for (size_t i = 0; i < intlist_attributes_.size(); ++i) {
491     if (intlist_attributes_[i].first == attribute)
492       return intlist_attributes_[i].second;
493   }
494
495   return empty_vector;
496 }
497
498 bool BrowserAccessibility::GetIntListAttribute(
499     AccessibilityNodeData::IntListAttribute attribute,
500     std::vector<int32>* value) const {
501   for (size_t i = 0; i < intlist_attributes_.size(); ++i) {
502     if (intlist_attributes_[i].first == attribute) {
503       *value = intlist_attributes_[i].second;
504       return true;
505     }
506   }
507
508   return false;
509 }
510
511 bool BrowserAccessibility::GetHtmlAttribute(
512     const char* html_attr, std::string* value) const {
513   for (size_t i = 0; i < html_attributes_.size(); ++i) {
514     const std::string& attr = html_attributes_[i].first;
515     if (LowerCaseEqualsASCII(attr, html_attr)) {
516       *value = html_attributes_[i].second;
517       return true;
518     }
519   }
520
521   return false;
522 }
523
524 bool BrowserAccessibility::GetHtmlAttribute(
525     const char* html_attr, string16* value) const {
526   std::string value_utf8;
527   if (!GetHtmlAttribute(html_attr, &value_utf8))
528     return false;
529   *value = UTF8ToUTF16(value_utf8);
530   return true;
531 }
532
533 bool BrowserAccessibility::GetAriaTristate(
534     const char* html_attr,
535     bool* is_defined,
536     bool* is_mixed) const {
537   *is_defined = false;
538   *is_mixed = false;
539
540   string16 value;
541   if (!GetHtmlAttribute(html_attr, &value) ||
542       value.empty() ||
543       EqualsASCII(value, "undefined")) {
544     return false;  // Not set (and *is_defined is also false)
545   }
546
547   *is_defined = true;
548
549   if (EqualsASCII(value, "true"))
550     return true;
551
552   if (EqualsASCII(value, "mixed"))
553     *is_mixed = true;
554
555   return false;  // Not set
556 }
557
558 bool BrowserAccessibility::HasState(WebKit::WebAXState state_enum) const {
559   return (state_ >> state_enum) & 1;
560 }
561
562 bool BrowserAccessibility::IsEditableText() const {
563   // These roles don't have readonly set, but they're not editable text.
564   if (role_ == WebKit::WebAXRoleScrollArea ||
565       role_ == WebKit::WebAXRoleColumn ||
566       role_ == WebKit::WebAXRoleTableHeaderContainer) {
567     return false;
568   }
569
570   // Note: WebAXStateReadonly being false means it's either a text control,
571   // or contenteditable. We also check for editable text roles to cover
572   // another element that has role=textbox set on it.
573   return (!HasState(WebKit::WebAXStateReadonly) ||
574           role_ == WebKit::WebAXRoleTextField ||
575           role_ == WebKit::WebAXRoleTextArea);
576 }
577
578 std::string BrowserAccessibility::GetTextRecursive() const {
579   if (!name_.empty()) {
580     return name_;
581   }
582
583   std::string result;
584   for (size_t i = 0; i < children_.size(); ++i)
585     result += children_[i]->GetTextRecursive();
586   return result;
587 }
588
589 }  // namespace content