Home | History | Annotate | Download | only in accessibility
      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