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