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 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