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
Create()28 BrowserAccessibility* BrowserAccessibility::Create() {
29 return new BrowserAccessibility();
30 }
31 #endif
32
BrowserAccessibility()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
~BrowserAccessibility()43 BrowserAccessibility::~BrowserAccessibility() {
44 }
45
PlatformIsLeaf() const46 bool BrowserAccessibility::PlatformIsLeaf() const {
47 return role_ == blink::WebAXRoleStaticText || child_count() == 0;
48 }
49
PlatformChildCount() const50 uint32 BrowserAccessibility::PlatformChildCount() const {
51 return PlatformIsLeaf() ? 0 : children_.size();
52 }
53
DetachTree(std::vector<BrowserAccessibility * > * nodes)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
InitializeTreeStructure(BrowserAccessibilityManager * manager,BrowserAccessibility * parent,int32 renderer_id,int32 index_in_parent)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
InitializeData(const AccessibilityNodeData & src)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
IsNative() const93 bool BrowserAccessibility::IsNative() const {
94 return false;
95 }
96
SwapChildren(std::vector<BrowserAccessibility * > & children)97 void BrowserAccessibility::SwapChildren(
98 std::vector<BrowserAccessibility*>& children) {
99 children.swap(children_);
100 }
101
UpdateParent(BrowserAccessibility * parent,int index_in_parent)102 void BrowserAccessibility::UpdateParent(BrowserAccessibility* parent,
103 int index_in_parent) {
104 parent_ = parent;
105 index_in_parent_ = index_in_parent;
106 }
107
SetLocation(const gfx::Rect & new_location)108 void BrowserAccessibility::SetLocation(const gfx::Rect& new_location) {
109 location_ = new_location;
110 }
111
IsDescendantOf(BrowserAccessibility * ancestor)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
PlatformGetChild(uint32 child_index) const123 BrowserAccessibility* BrowserAccessibility::PlatformGetChild(
124 uint32 child_index) const {
125 DCHECK(child_index < children_.size());
126 return children_[child_index];
127 }
128
GetPreviousSibling()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
GetNextSibling()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
GetLocalBoundsRect() const146 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_ == blink::WebAXRoleWebArea ||
155 role_ == blink::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() == blink::WebAXRoleRootWebArea &&
167 !manager()->UseRootScrollOffsetsWhenComputingBounds()) {
168 break;
169 }
170
171 if (parent->role() == blink::WebAXRoleWebArea ||
172 parent->role() == blink::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
GetGlobalBoundsRect() const187 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
GetLocalBoundsForRange(int start,int len) const197 gfx::Rect BrowserAccessibility::GetLocalBoundsForRange(int start, int len)
198 const {
199 DCHECK_EQ(role_, blink::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(), blink::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 blink::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 blink::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 blink::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 blink::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
GetGlobalBoundsForRange(int start,int len) const276 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
BrowserAccessibilityForPoint(const gfx::Point & point)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 = static_cast<int>(PlatformChildCount()) - 1; i >= 0; --i) {
292 BrowserAccessibility* child = PlatformGetChild(i);
293 if (child->GetGlobalBoundsRect().Contains(point))
294 return child->BrowserAccessibilityForPoint(point);
295 }
296 return this;
297 }
298
Destroy()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 blink::WebAXEventHide, this);
314
315 instance_active_ = false;
316 manager_->RemoveNode(this);
317 NativeReleaseReference();
318 }
319
NativeReleaseReference()320 void BrowserAccessibility::NativeReleaseReference() {
321 delete this;
322 }
323
HasBoolAttribute(BoolAttribute attribute) const324 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
GetBoolAttribute(BoolAttribute attribute) const334 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
GetBoolAttribute(BoolAttribute attribute,bool * value) const343 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
HasFloatAttribute(FloatAttribute attribute) const355 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
GetFloatAttribute(FloatAttribute attribute) const364 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
GetFloatAttribute(FloatAttribute attribute,float * value) const373 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
HasIntAttribute(IntAttribute attribute) const385 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
GetIntAttribute(IntAttribute attribute) const394 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
GetIntAttribute(IntAttribute attribute,int * value) const403 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
HasStringAttribute(StringAttribute attribute) const415 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
GetStringAttribute(StringAttribute attribute) const424 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
GetStringAttribute(StringAttribute attribute,std::string * value) const435 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
GetString16Attribute(StringAttribute attribute) const447 base::string16 BrowserAccessibility::GetString16Attribute(
448 StringAttribute attribute) const {
449 std::string value_utf8;
450 if (!GetStringAttribute(attribute, &value_utf8))
451 return base::string16();
452 return UTF8ToUTF16(value_utf8);
453 }
454
GetString16Attribute(StringAttribute attribute,base::string16 * value) const455 bool BrowserAccessibility::GetString16Attribute(
456 StringAttribute attribute,
457 base::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
SetStringAttribute(StringAttribute attribute,const std::string & value)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
HasIntListAttribute(AccessibilityNodeData::IntListAttribute attribute) const477 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
GetIntListAttribute(AccessibilityNodeData::IntListAttribute attribute) const487 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
GetIntListAttribute(AccessibilityNodeData::IntListAttribute attribute,std::vector<int32> * value) const498 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
GetHtmlAttribute(const char * html_attr,std::string * value) const511 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
GetHtmlAttribute(const char * html_attr,base::string16 * value) const524 bool BrowserAccessibility::GetHtmlAttribute(
525 const char* html_attr, base::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
GetAriaTristate(const char * html_attr,bool * is_defined,bool * is_mixed) const533 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 base::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
HasState(blink::WebAXState state_enum) const558 bool BrowserAccessibility::HasState(blink::WebAXState state_enum) const {
559 return (state_ >> state_enum) & 1;
560 }
561
IsEditableText() const562 bool BrowserAccessibility::IsEditableText() const {
563 // These roles don't have readonly set, but they're not editable text.
564 if (role_ == blink::WebAXRoleScrollArea ||
565 role_ == blink::WebAXRoleColumn ||
566 role_ == blink::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(blink::WebAXStateReadonly) ||
574 role_ == blink::WebAXRoleTextField ||
575 role_ == blink::WebAXRoleTextArea);
576 }
577
GetTextRecursive() const578 std::string BrowserAccessibility::GetTextRecursive() const {
579 if (!name_.empty()) {
580 return name_;
581 }
582
583 std::string result;
584 for (uint32 i = 0; i < PlatformChildCount(); ++i)
585 result += PlatformGetChild(i)->GetTextRecursive();
586 return result;
587 }
588
589 } // namespace content
590