1 // Copyright 2013 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_android.h" 6 7 #include "base/strings/utf_string_conversions.h" 8 #include "content/browser/accessibility/browser_accessibility_manager_android.h" 9 #include "content/common/accessibility_messages.h" 10 11 namespace { 12 13 // These are enums from android.text.InputType in Java: 14 enum { 15 ANDROID_TEXT_INPUTTYPE_TYPE_NULL = 0, 16 ANDROID_TEXT_INPUTTYPE_TYPE_DATETIME = 0x4, 17 ANDROID_TEXT_INPUTTYPE_TYPE_DATETIME_DATE = 0x14, 18 ANDROID_TEXT_INPUTTYPE_TYPE_DATETIME_TIME = 0x24, 19 ANDROID_TEXT_INPUTTYPE_TYPE_NUMBER = 0x2, 20 ANDROID_TEXT_INPUTTYPE_TYPE_PHONE = 0x3, 21 ANDROID_TEXT_INPUTTYPE_TYPE_TEXT = 0x1, 22 ANDROID_TEXT_INPUTTYPE_TYPE_TEXT_URI = 0x11, 23 ANDROID_TEXT_INPUTTYPE_TYPE_TEXT_WEB_EDIT_TEXT = 0xa1, 24 ANDROID_TEXT_INPUTTYPE_TYPE_TEXT_WEB_EMAIL = 0xd1, 25 ANDROID_TEXT_INPUTTYPE_TYPE_TEXT_WEB_PASSWORD = 0xe1 26 }; 27 28 // These are enums from android.view.View in Java: 29 enum { 30 ANDROID_VIEW_VIEW_ACCESSIBILITY_LIVE_REGION_NONE = 0, 31 ANDROID_VIEW_VIEW_ACCESSIBILITY_LIVE_REGION_POLITE = 1, 32 ANDROID_VIEW_VIEW_ACCESSIBILITY_LIVE_REGION_ASSERTIVE = 2 33 }; 34 35 // These are enums from 36 // android.view.accessibility.AccessibilityNodeInfo.RangeInfo in Java: 37 enum { 38 ANDROID_VIEW_ACCESSIBILITY_RANGE_TYPE_FLOAT = 1 39 }; 40 41 } // namespace 42 43 namespace content { 44 45 // static 46 BrowserAccessibility* BrowserAccessibility::Create() { 47 return new BrowserAccessibilityAndroid(); 48 } 49 50 BrowserAccessibilityAndroid::BrowserAccessibilityAndroid() { 51 first_time_ = true; 52 } 53 54 bool BrowserAccessibilityAndroid::IsNative() const { 55 return true; 56 } 57 58 void BrowserAccessibilityAndroid::OnLocationChanged() { 59 manager()->NotifyAccessibilityEvent(ui::AX_EVENT_LOCATION_CHANGED, this); 60 } 61 62 bool BrowserAccessibilityAndroid::PlatformIsLeaf() const { 63 if (InternalChildCount() == 0) 64 return true; 65 66 // Iframes are always allowed to contain children. 67 if (IsIframe() || 68 GetRole() == ui::AX_ROLE_ROOT_WEB_AREA || 69 GetRole() == ui::AX_ROLE_WEB_AREA) { 70 return false; 71 } 72 73 // If it has a focusable child, we definitely can't leave out children. 74 if (HasFocusableChild()) 75 return false; 76 77 // Headings with text can drop their children. 78 base::string16 name = GetText(); 79 if (GetRole() == ui::AX_ROLE_HEADING && !name.empty()) 80 return true; 81 82 // Focusable nodes with text can drop their children. 83 if (HasState(ui::AX_STATE_FOCUSABLE) && !name.empty()) 84 return true; 85 86 // Nodes with only static text as children can drop their children. 87 if (HasOnlyStaticTextChildren()) 88 return true; 89 90 return BrowserAccessibility::PlatformIsLeaf(); 91 } 92 93 bool BrowserAccessibilityAndroid::IsCheckable() const { 94 bool checkable = false; 95 bool is_aria_pressed_defined; 96 bool is_mixed; 97 GetAriaTristate("aria-pressed", &is_aria_pressed_defined, &is_mixed); 98 if (GetRole() == ui::AX_ROLE_CHECK_BOX || 99 GetRole() == ui::AX_ROLE_RADIO_BUTTON || 100 is_aria_pressed_defined) { 101 checkable = true; 102 } 103 if (HasState(ui::AX_STATE_CHECKED)) 104 checkable = true; 105 return checkable; 106 } 107 108 bool BrowserAccessibilityAndroid::IsChecked() const { 109 return HasState(ui::AX_STATE_CHECKED); 110 } 111 112 bool BrowserAccessibilityAndroid::IsClickable() const { 113 return (PlatformIsLeaf() && !GetText().empty()); 114 } 115 116 bool BrowserAccessibilityAndroid::IsCollection() const { 117 return (GetRole() == ui::AX_ROLE_GRID || 118 GetRole() == ui::AX_ROLE_LIST || 119 GetRole() == ui::AX_ROLE_LIST_BOX || 120 GetRole() == ui::AX_ROLE_TABLE || 121 GetRole() == ui::AX_ROLE_TREE); 122 } 123 124 bool BrowserAccessibilityAndroid::IsCollectionItem() const { 125 return (GetRole() == ui::AX_ROLE_CELL || 126 GetRole() == ui::AX_ROLE_COLUMN_HEADER || 127 GetRole() == ui::AX_ROLE_DESCRIPTION_LIST_TERM || 128 GetRole() == ui::AX_ROLE_LIST_BOX_OPTION || 129 GetRole() == ui::AX_ROLE_LIST_ITEM || 130 GetRole() == ui::AX_ROLE_ROW_HEADER || 131 GetRole() == ui::AX_ROLE_TREE_ITEM); 132 } 133 134 bool BrowserAccessibilityAndroid::IsContentInvalid() const { 135 std::string invalid; 136 return GetHtmlAttribute("aria-invalid", &invalid); 137 } 138 139 bool BrowserAccessibilityAndroid::IsDismissable() const { 140 return false; // No concept of "dismissable" on the web currently. 141 } 142 143 bool BrowserAccessibilityAndroid::IsEnabled() const { 144 return HasState(ui::AX_STATE_ENABLED); 145 } 146 147 bool BrowserAccessibilityAndroid::IsFocusable() const { 148 bool focusable = HasState(ui::AX_STATE_FOCUSABLE); 149 if (IsIframe() || 150 GetRole() == ui::AX_ROLE_WEB_AREA) { 151 focusable = false; 152 } 153 return focusable; 154 } 155 156 bool BrowserAccessibilityAndroid::IsFocused() const { 157 return manager()->GetFocus(manager()->GetRoot()) == this; 158 } 159 160 bool BrowserAccessibilityAndroid::IsHeading() const { 161 return (GetRole() == ui::AX_ROLE_COLUMN_HEADER || 162 GetRole() == ui::AX_ROLE_HEADING || 163 GetRole() == ui::AX_ROLE_ROW_HEADER); 164 } 165 166 bool BrowserAccessibilityAndroid::IsHierarchical() const { 167 return (GetRole() == ui::AX_ROLE_LIST || 168 GetRole() == ui::AX_ROLE_TREE); 169 } 170 171 bool BrowserAccessibilityAndroid::IsLink() const { 172 return GetRole() == ui::AX_ROLE_LINK || 173 GetRole() == ui::AX_ROLE_IMAGE_MAP_LINK; 174 } 175 176 bool BrowserAccessibilityAndroid::IsMultiLine() const { 177 return GetRole() == ui::AX_ROLE_TEXT_AREA; 178 } 179 180 bool BrowserAccessibilityAndroid::IsPassword() const { 181 return HasState(ui::AX_STATE_PROTECTED); 182 } 183 184 bool BrowserAccessibilityAndroid::IsRangeType() const { 185 return (GetRole() == ui::AX_ROLE_PROGRESS_INDICATOR || 186 GetRole() == ui::AX_ROLE_SCROLL_BAR || 187 GetRole() == ui::AX_ROLE_SLIDER); 188 } 189 190 bool BrowserAccessibilityAndroid::IsScrollable() const { 191 int dummy; 192 return GetIntAttribute(ui::AX_ATTR_SCROLL_X_MAX, &dummy); 193 } 194 195 bool BrowserAccessibilityAndroid::IsSelected() const { 196 return HasState(ui::AX_STATE_SELECTED); 197 } 198 199 bool BrowserAccessibilityAndroid::IsVisibleToUser() const { 200 return !HasState(ui::AX_STATE_INVISIBLE); 201 } 202 203 bool BrowserAccessibilityAndroid::CanOpenPopup() const { 204 return HasState(ui::AX_STATE_HASPOPUP); 205 } 206 207 const char* BrowserAccessibilityAndroid::GetClassName() const { 208 const char* class_name = NULL; 209 210 switch(GetRole()) { 211 case ui::AX_ROLE_EDITABLE_TEXT: 212 case ui::AX_ROLE_SPIN_BUTTON: 213 case ui::AX_ROLE_TEXT_AREA: 214 case ui::AX_ROLE_TEXT_FIELD: 215 class_name = "android.widget.EditText"; 216 break; 217 case ui::AX_ROLE_SLIDER: 218 class_name = "android.widget.SeekBar"; 219 break; 220 case ui::AX_ROLE_COMBO_BOX: 221 class_name = "android.widget.Spinner"; 222 break; 223 case ui::AX_ROLE_BUTTON: 224 case ui::AX_ROLE_MENU_BUTTON: 225 case ui::AX_ROLE_POP_UP_BUTTON: 226 class_name = "android.widget.Button"; 227 break; 228 case ui::AX_ROLE_CHECK_BOX: 229 class_name = "android.widget.CheckBox"; 230 break; 231 case ui::AX_ROLE_RADIO_BUTTON: 232 class_name = "android.widget.RadioButton"; 233 break; 234 case ui::AX_ROLE_TOGGLE_BUTTON: 235 class_name = "android.widget.ToggleButton"; 236 break; 237 case ui::AX_ROLE_CANVAS: 238 case ui::AX_ROLE_IMAGE: 239 class_name = "android.widget.Image"; 240 break; 241 case ui::AX_ROLE_PROGRESS_INDICATOR: 242 class_name = "android.widget.ProgressBar"; 243 break; 244 case ui::AX_ROLE_TAB_LIST: 245 class_name = "android.widget.TabWidget"; 246 break; 247 case ui::AX_ROLE_GRID: 248 case ui::AX_ROLE_TABLE: 249 class_name = "android.widget.GridView"; 250 break; 251 case ui::AX_ROLE_LIST: 252 case ui::AX_ROLE_LIST_BOX: 253 class_name = "android.widget.ListView"; 254 break; 255 case ui::AX_ROLE_DIALOG: 256 class_name = "android.app.Dialog"; 257 break; 258 case ui::AX_ROLE_ROOT_WEB_AREA: 259 class_name = "android.webkit.WebView"; 260 break; 261 default: 262 class_name = "android.view.View"; 263 break; 264 } 265 266 return class_name; 267 } 268 269 base::string16 BrowserAccessibilityAndroid::GetText() const { 270 if (IsIframe() || 271 GetRole() == ui::AX_ROLE_WEB_AREA) { 272 return base::string16(); 273 } 274 275 // See comment in browser_accessibility_win.cc for details. 276 // The difference here is that we can only expose one accessible 277 // name on Android, not 2 or 3 like on Windows or Mac. 278 279 // First, always return the |value| attribute if this is an 280 // accessible text. 281 if (!value().empty() && 282 (GetRole() == ui::AX_ROLE_EDITABLE_TEXT || 283 GetRole() == ui::AX_ROLE_TEXT_AREA || 284 GetRole() == ui::AX_ROLE_TEXT_FIELD || 285 HasState(ui::AX_STATE_EDITABLE))) { 286 return base::UTF8ToUTF16(value()); 287 } 288 289 // If there's no text value, the basic rule is: prefer description 290 // (aria-labelledby or aria-label), then help (title), then name 291 // (inner text), then value (control value). However, if 292 // title_elem_id is set, that means there's a label element 293 // supplying the name and then name takes precedence over help. 294 // TODO(dmazzoni): clean this up by providing more granular labels in 295 // Blink, making the platform-specific mapping to accessible text simpler. 296 base::string16 description = GetString16Attribute(ui::AX_ATTR_DESCRIPTION); 297 base::string16 help = GetString16Attribute(ui::AX_ATTR_HELP); 298 int title_elem_id = GetIntAttribute( 299 ui::AX_ATTR_TITLE_UI_ELEMENT); 300 base::string16 text; 301 if (!description.empty()) 302 text = description; 303 else if (title_elem_id && !name().empty()) 304 text = base::UTF8ToUTF16(name()); 305 else if (!help.empty()) 306 text = help; 307 else if (!name().empty()) 308 text = base::UTF8ToUTF16(name()); 309 else if (!value().empty()) 310 text = base::UTF8ToUTF16(value()); 311 312 // This is called from PlatformIsLeaf, so don't call PlatformChildCount 313 // from within this! 314 if (text.empty() && HasOnlyStaticTextChildren()) { 315 for (uint32 i = 0; i < InternalChildCount(); i++) { 316 BrowserAccessibility* child = InternalGetChild(i); 317 text += static_cast<BrowserAccessibilityAndroid*>(child)->GetText(); 318 } 319 } 320 321 if (text.empty() && IsLink()) { 322 base::string16 url = GetString16Attribute(ui::AX_ATTR_URL); 323 // Given a url like http://foo.com/bar/baz.png, just return the 324 // base name, e.g., "baz". 325 int trailing_slashes = 0; 326 while (url.size() - trailing_slashes > 0 && 327 url[url.size() - trailing_slashes - 1] == '/') { 328 trailing_slashes++; 329 } 330 if (trailing_slashes) 331 url = url.substr(0, url.size() - trailing_slashes); 332 size_t slash_index = url.rfind('/'); 333 if (slash_index != std::string::npos) 334 url = url.substr(slash_index + 1); 335 size_t dot_index = url.rfind('.'); 336 if (dot_index != std::string::npos) 337 url = url.substr(0, dot_index); 338 text = url; 339 } 340 341 return text; 342 } 343 344 int BrowserAccessibilityAndroid::GetItemIndex() const { 345 int index = 0; 346 switch(GetRole()) { 347 case ui::AX_ROLE_LIST_ITEM: 348 case ui::AX_ROLE_LIST_BOX_OPTION: 349 case ui::AX_ROLE_TREE_ITEM: 350 index = GetIndexInParent(); 351 break; 352 case ui::AX_ROLE_SLIDER: 353 case ui::AX_ROLE_PROGRESS_INDICATOR: { 354 float value_for_range; 355 if (GetFloatAttribute( 356 ui::AX_ATTR_VALUE_FOR_RANGE, &value_for_range)) { 357 index = static_cast<int>(value_for_range); 358 } 359 break; 360 } 361 } 362 return index; 363 } 364 365 int BrowserAccessibilityAndroid::GetItemCount() const { 366 int count = 0; 367 switch(GetRole()) { 368 case ui::AX_ROLE_LIST: 369 case ui::AX_ROLE_LIST_BOX: 370 count = PlatformChildCount(); 371 break; 372 case ui::AX_ROLE_SLIDER: 373 case ui::AX_ROLE_PROGRESS_INDICATOR: { 374 float max_value_for_range; 375 if (GetFloatAttribute(ui::AX_ATTR_MAX_VALUE_FOR_RANGE, 376 &max_value_for_range)) { 377 count = static_cast<int>(max_value_for_range); 378 } 379 break; 380 } 381 } 382 return count; 383 } 384 385 int BrowserAccessibilityAndroid::GetScrollX() const { 386 int value = 0; 387 GetIntAttribute(ui::AX_ATTR_SCROLL_X, &value); 388 return value; 389 } 390 391 int BrowserAccessibilityAndroid::GetScrollY() const { 392 int value = 0; 393 GetIntAttribute(ui::AX_ATTR_SCROLL_Y, &value); 394 return value; 395 } 396 397 int BrowserAccessibilityAndroid::GetMaxScrollX() const { 398 int value = 0; 399 GetIntAttribute(ui::AX_ATTR_SCROLL_X_MAX, &value); 400 return value; 401 } 402 403 int BrowserAccessibilityAndroid::GetMaxScrollY() const { 404 int value = 0; 405 GetIntAttribute(ui::AX_ATTR_SCROLL_Y_MAX, &value); 406 return value; 407 } 408 409 int BrowserAccessibilityAndroid::GetTextChangeFromIndex() const { 410 size_t index = 0; 411 while (index < old_value_.length() && 412 index < new_value_.length() && 413 old_value_[index] == new_value_[index]) { 414 index++; 415 } 416 return index; 417 } 418 419 int BrowserAccessibilityAndroid::GetTextChangeAddedCount() const { 420 size_t old_len = old_value_.length(); 421 size_t new_len = new_value_.length(); 422 size_t left = 0; 423 while (left < old_len && 424 left < new_len && 425 old_value_[left] == new_value_[left]) { 426 left++; 427 } 428 size_t right = 0; 429 while (right < old_len && 430 right < new_len && 431 old_value_[old_len - right - 1] == new_value_[new_len - right - 1]) { 432 right++; 433 } 434 return (new_len - left - right); 435 } 436 437 int BrowserAccessibilityAndroid::GetTextChangeRemovedCount() const { 438 size_t old_len = old_value_.length(); 439 size_t new_len = new_value_.length(); 440 size_t left = 0; 441 while (left < old_len && 442 left < new_len && 443 old_value_[left] == new_value_[left]) { 444 left++; 445 } 446 size_t right = 0; 447 while (right < old_len && 448 right < new_len && 449 old_value_[old_len - right - 1] == new_value_[new_len - right - 1]) { 450 right++; 451 } 452 return (old_len - left - right); 453 } 454 455 base::string16 BrowserAccessibilityAndroid::GetTextChangeBeforeText() const { 456 return old_value_; 457 } 458 459 int BrowserAccessibilityAndroid::GetSelectionStart() const { 460 int sel_start = 0; 461 GetIntAttribute(ui::AX_ATTR_TEXT_SEL_START, &sel_start); 462 return sel_start; 463 } 464 465 int BrowserAccessibilityAndroid::GetSelectionEnd() const { 466 int sel_end = 0; 467 GetIntAttribute(ui::AX_ATTR_TEXT_SEL_END, &sel_end); 468 return sel_end; 469 } 470 471 int BrowserAccessibilityAndroid::GetEditableTextLength() const { 472 return value().length(); 473 } 474 475 int BrowserAccessibilityAndroid::AndroidInputType() const { 476 std::string html_tag = GetStringAttribute( 477 ui::AX_ATTR_HTML_TAG); 478 if (html_tag != "input") 479 return ANDROID_TEXT_INPUTTYPE_TYPE_NULL; 480 481 std::string type; 482 if (!GetHtmlAttribute("type", &type)) 483 return ANDROID_TEXT_INPUTTYPE_TYPE_TEXT; 484 485 if (type == "" || type == "text" || type == "search") 486 return ANDROID_TEXT_INPUTTYPE_TYPE_TEXT; 487 else if (type == "date") 488 return ANDROID_TEXT_INPUTTYPE_TYPE_DATETIME_DATE; 489 else if (type == "datetime" || type == "datetime-local") 490 return ANDROID_TEXT_INPUTTYPE_TYPE_DATETIME; 491 else if (type == "email") 492 return ANDROID_TEXT_INPUTTYPE_TYPE_TEXT_WEB_EMAIL; 493 else if (type == "month") 494 return ANDROID_TEXT_INPUTTYPE_TYPE_DATETIME_DATE; 495 else if (type == "number") 496 return ANDROID_TEXT_INPUTTYPE_TYPE_NUMBER; 497 else if (type == "password") 498 return ANDROID_TEXT_INPUTTYPE_TYPE_TEXT_WEB_PASSWORD; 499 else if (type == "tel") 500 return ANDROID_TEXT_INPUTTYPE_TYPE_PHONE; 501 else if (type == "time") 502 return ANDROID_TEXT_INPUTTYPE_TYPE_DATETIME_TIME; 503 else if (type == "url") 504 return ANDROID_TEXT_INPUTTYPE_TYPE_TEXT_URI; 505 else if (type == "week") 506 return ANDROID_TEXT_INPUTTYPE_TYPE_DATETIME; 507 508 return ANDROID_TEXT_INPUTTYPE_TYPE_NULL; 509 } 510 511 int BrowserAccessibilityAndroid::AndroidLiveRegionType() const { 512 std::string live = GetStringAttribute( 513 ui::AX_ATTR_LIVE_STATUS); 514 if (live == "polite") 515 return ANDROID_VIEW_VIEW_ACCESSIBILITY_LIVE_REGION_POLITE; 516 else if (live == "assertive") 517 return ANDROID_VIEW_VIEW_ACCESSIBILITY_LIVE_REGION_ASSERTIVE; 518 return ANDROID_VIEW_VIEW_ACCESSIBILITY_LIVE_REGION_NONE; 519 } 520 521 int BrowserAccessibilityAndroid::AndroidRangeType() const { 522 return ANDROID_VIEW_ACCESSIBILITY_RANGE_TYPE_FLOAT; 523 } 524 525 int BrowserAccessibilityAndroid::RowCount() const { 526 if (GetRole() == ui::AX_ROLE_GRID || 527 GetRole() == ui::AX_ROLE_TABLE) { 528 return CountChildrenWithRole(ui::AX_ROLE_ROW); 529 } 530 531 if (GetRole() == ui::AX_ROLE_LIST || 532 GetRole() == ui::AX_ROLE_LIST_BOX || 533 GetRole() == ui::AX_ROLE_TREE) { 534 return PlatformChildCount(); 535 } 536 537 return 0; 538 } 539 540 int BrowserAccessibilityAndroid::ColumnCount() const { 541 if (GetRole() == ui::AX_ROLE_GRID || 542 GetRole() == ui::AX_ROLE_TABLE) { 543 return CountChildrenWithRole(ui::AX_ROLE_COLUMN); 544 } 545 return 0; 546 } 547 548 int BrowserAccessibilityAndroid::RowIndex() const { 549 if (GetRole() == ui::AX_ROLE_LIST_ITEM || 550 GetRole() == ui::AX_ROLE_LIST_BOX_OPTION || 551 GetRole() == ui::AX_ROLE_TREE_ITEM) { 552 return GetIndexInParent(); 553 } 554 555 return GetIntAttribute(ui::AX_ATTR_TABLE_CELL_ROW_INDEX); 556 } 557 558 int BrowserAccessibilityAndroid::RowSpan() const { 559 return GetIntAttribute(ui::AX_ATTR_TABLE_CELL_ROW_SPAN); 560 } 561 562 int BrowserAccessibilityAndroid::ColumnIndex() const { 563 return GetIntAttribute(ui::AX_ATTR_TABLE_CELL_COLUMN_INDEX); 564 } 565 566 int BrowserAccessibilityAndroid::ColumnSpan() const { 567 return GetIntAttribute(ui::AX_ATTR_TABLE_CELL_COLUMN_SPAN); 568 } 569 570 float BrowserAccessibilityAndroid::RangeMin() const { 571 return GetFloatAttribute(ui::AX_ATTR_MIN_VALUE_FOR_RANGE); 572 } 573 574 float BrowserAccessibilityAndroid::RangeMax() const { 575 return GetFloatAttribute(ui::AX_ATTR_MAX_VALUE_FOR_RANGE); 576 } 577 578 float BrowserAccessibilityAndroid::RangeCurrentValue() const { 579 return GetFloatAttribute(ui::AX_ATTR_VALUE_FOR_RANGE); 580 } 581 582 bool BrowserAccessibilityAndroid::HasFocusableChild() const { 583 // This is called from PlatformIsLeaf, so don't call PlatformChildCount 584 // from within this! 585 for (uint32 i = 0; i < InternalChildCount(); i++) { 586 BrowserAccessibility* child = InternalGetChild(i); 587 if (child->HasState(ui::AX_STATE_FOCUSABLE)) 588 return true; 589 if (static_cast<BrowserAccessibilityAndroid*>(child)->HasFocusableChild()) 590 return true; 591 } 592 return false; 593 } 594 595 bool BrowserAccessibilityAndroid::HasOnlyStaticTextChildren() const { 596 // This is called from PlatformIsLeaf, so don't call PlatformChildCount 597 // from within this! 598 for (uint32 i = 0; i < InternalChildCount(); i++) { 599 BrowserAccessibility* child = InternalGetChild(i); 600 if (child->GetRole() != ui::AX_ROLE_STATIC_TEXT) 601 return false; 602 } 603 return true; 604 } 605 606 bool BrowserAccessibilityAndroid::IsIframe() const { 607 base::string16 html_tag = GetString16Attribute( 608 ui::AX_ATTR_HTML_TAG); 609 return html_tag == base::ASCIIToUTF16("iframe"); 610 } 611 612 void BrowserAccessibilityAndroid::OnDataChanged() { 613 BrowserAccessibility::OnDataChanged(); 614 615 if (IsEditableText()) { 616 if (base::UTF8ToUTF16(value()) != new_value_) { 617 old_value_ = new_value_; 618 new_value_ = base::UTF8ToUTF16(value()); 619 } 620 } 621 622 if (GetRole() == ui::AX_ROLE_ALERT && first_time_) 623 manager()->NotifyAccessibilityEvent(ui::AX_EVENT_ALERT, this); 624 625 base::string16 live; 626 if (GetString16Attribute( 627 ui::AX_ATTR_CONTAINER_LIVE_STATUS, &live)) { 628 NotifyLiveRegionUpdate(live); 629 } 630 631 first_time_ = false; 632 } 633 634 void BrowserAccessibilityAndroid::NotifyLiveRegionUpdate( 635 base::string16& aria_live) { 636 if (!EqualsASCII(aria_live, aria_strings::kAriaLivePolite) && 637 !EqualsASCII(aria_live, aria_strings::kAriaLiveAssertive)) 638 return; 639 640 base::string16 text = GetText(); 641 if (cached_text_ != text) { 642 if (!text.empty()) { 643 manager()->NotifyAccessibilityEvent(ui::AX_EVENT_SHOW, 644 this); 645 } 646 cached_text_ = text; 647 } 648 } 649 650 int BrowserAccessibilityAndroid::CountChildrenWithRole(ui::AXRole role) const { 651 int count = 0; 652 for (uint32 i = 0; i < PlatformChildCount(); i++) { 653 if (PlatformGetChild(i)->GetRole() == role) 654 count++; 655 } 656 return count; 657 } 658 659 } // namespace content 660