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_ == blink::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_ == 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 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_, 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 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 = 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 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 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 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 455 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 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, 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 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 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 558 bool BrowserAccessibility::HasState(blink::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_ == 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 578 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