Home | History | Annotate | Download | only in accessibility
      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 #if !defined(OS_MACOSX) && \
     17     !defined(OS_WIN) && \
     18     !defined(OS_ANDROID)
     19 // We have subclassess of BrowserAccessibility on Mac and Win. For any other
     20 // platform, instantiate the base class.
     21 // static
     22 BrowserAccessibility* BrowserAccessibility::Create() {
     23   return new BrowserAccessibility();
     24 }
     25 #endif
     26 
     27 BrowserAccessibility::BrowserAccessibility()
     28     : manager_(NULL),
     29       node_(NULL) {
     30 }
     31 
     32 BrowserAccessibility::~BrowserAccessibility() {
     33 }
     34 
     35 void BrowserAccessibility::Init(BrowserAccessibilityManager* manager,
     36     ui::AXNode* node) {
     37   manager_ = manager;
     38   node_ = node;
     39 }
     40 
     41 void BrowserAccessibility::OnDataChanged() {
     42   GetStringAttribute(ui::AX_ATTR_NAME, &name_);
     43   GetStringAttribute(ui::AX_ATTR_VALUE, &value_);
     44 }
     45 
     46 bool BrowserAccessibility::PlatformIsLeaf() const {
     47   if (InternalChildCount() == 0)
     48     return true;
     49 
     50   // All of these roles may have children that we use as internal
     51   // implementation details, but we want to expose them as leaves
     52   // to platform accessibility APIs.
     53   switch (GetRole()) {
     54     case ui::AX_ROLE_EDITABLE_TEXT:
     55     case ui::AX_ROLE_SLIDER:
     56     case ui::AX_ROLE_STATIC_TEXT:
     57     case ui::AX_ROLE_TEXT_AREA:
     58     case ui::AX_ROLE_TEXT_FIELD:
     59       return true;
     60     default:
     61       return false;
     62   }
     63 }
     64 
     65 uint32 BrowserAccessibility::PlatformChildCount() const {
     66   return PlatformIsLeaf() ? 0 : InternalChildCount();
     67 }
     68 
     69 bool BrowserAccessibility::IsNative() const {
     70   return false;
     71 }
     72 
     73 bool BrowserAccessibility::IsDescendantOf(
     74     BrowserAccessibility* ancestor) {
     75   if (this == ancestor) {
     76     return true;
     77   } else if (GetParent()) {
     78     return GetParent()->IsDescendantOf(ancestor);
     79   }
     80 
     81   return false;
     82 }
     83 
     84 BrowserAccessibility* BrowserAccessibility::PlatformGetChild(
     85     uint32 child_index) const {
     86   DCHECK(child_index < InternalChildCount());
     87   return InternalGetChild(child_index);
     88 }
     89 
     90 BrowserAccessibility* BrowserAccessibility::GetPreviousSibling() {
     91   if (GetParent() && GetIndexInParent() > 0)
     92     return GetParent()->InternalGetChild(GetIndexInParent() - 1);
     93 
     94   return NULL;
     95 }
     96 
     97 BrowserAccessibility* BrowserAccessibility::GetNextSibling() {
     98   if (GetParent() &&
     99       GetIndexInParent() >= 0 &&
    100       GetIndexInParent() < static_cast<int>(
    101           GetParent()->InternalChildCount() - 1)) {
    102     return GetParent()->InternalGetChild(GetIndexInParent() + 1);
    103   }
    104 
    105   return NULL;
    106 }
    107 
    108 uint32 BrowserAccessibility::InternalChildCount() const {
    109   if (!node_ || !manager_)
    110     return 0;
    111   return static_cast<uint32>(node_->child_count());
    112 }
    113 
    114 BrowserAccessibility* BrowserAccessibility::InternalGetChild(
    115     uint32 child_index) const {
    116   if (!node_ || !manager_)
    117     return NULL;
    118   return manager_->GetFromAXNode(node_->children()[child_index]);
    119 }
    120 
    121 BrowserAccessibility* BrowserAccessibility::GetParent() const {
    122   if (!node_ || !manager_)
    123     return NULL;
    124   ui::AXNode* parent = node_->parent();
    125   return parent ? manager_->GetFromAXNode(parent) : NULL;
    126 }
    127 
    128 int32 BrowserAccessibility::GetIndexInParent() const {
    129   return node_ ? node_->index_in_parent() : -1;
    130 }
    131 
    132 int32 BrowserAccessibility::GetId() const {
    133   return node_ ? node_->id() : -1;
    134 }
    135 
    136 const ui::AXNodeData& BrowserAccessibility::GetData() const {
    137   CR_DEFINE_STATIC_LOCAL(ui::AXNodeData, empty_data, ());
    138   if (node_)
    139     return node_->data();
    140   else
    141     return empty_data;
    142 }
    143 
    144 gfx::Rect BrowserAccessibility::GetLocation() const {
    145   return GetData().location;
    146 }
    147 
    148 int32 BrowserAccessibility::GetRole() const {
    149   return GetData().role;
    150 }
    151 
    152 int32 BrowserAccessibility::GetState() const {
    153   return GetData().state;
    154 }
    155 
    156 const std::vector<std::pair<std::string, std::string> >&
    157 BrowserAccessibility::GetHtmlAttributes() const {
    158   return GetData().html_attributes;
    159 }
    160 
    161 gfx::Rect BrowserAccessibility::GetLocalBoundsRect() const {
    162   gfx::Rect bounds = GetLocation();
    163 
    164   // Walk up the parent chain. Every time we encounter a Web Area, offset
    165   // based on the scroll bars and then offset based on the origin of that
    166   // nested web area.
    167   BrowserAccessibility* parent = GetParent();
    168   bool need_to_offset_web_area =
    169       (GetRole() == ui::AX_ROLE_WEB_AREA ||
    170        GetRole() == ui::AX_ROLE_ROOT_WEB_AREA);
    171   while (parent) {
    172     if (need_to_offset_web_area &&
    173         parent->GetLocation().width() > 0 &&
    174         parent->GetLocation().height() > 0) {
    175       bounds.Offset(parent->GetLocation().x(), parent->GetLocation().y());
    176       need_to_offset_web_area = false;
    177     }
    178 
    179     // On some platforms, we don't want to take the root scroll offsets
    180     // into account.
    181     if (parent->GetRole() == ui::AX_ROLE_ROOT_WEB_AREA &&
    182         !manager()->UseRootScrollOffsetsWhenComputingBounds()) {
    183       break;
    184     }
    185 
    186     if (parent->GetRole() == ui::AX_ROLE_WEB_AREA ||
    187         parent->GetRole() == ui::AX_ROLE_ROOT_WEB_AREA) {
    188       int sx = 0;
    189       int sy = 0;
    190       if (parent->GetIntAttribute(ui::AX_ATTR_SCROLL_X, &sx) &&
    191           parent->GetIntAttribute(ui::AX_ATTR_SCROLL_Y, &sy)) {
    192         bounds.Offset(-sx, -sy);
    193       }
    194       need_to_offset_web_area = true;
    195     }
    196     parent = parent->GetParent();
    197   }
    198 
    199   return bounds;
    200 }
    201 
    202 gfx::Rect BrowserAccessibility::GetGlobalBoundsRect() const {
    203   gfx::Rect bounds = GetLocalBoundsRect();
    204 
    205   // Adjust the bounds by the top left corner of the containing view's bounds
    206   // in screen coordinates.
    207   bounds.Offset(manager_->GetViewBounds().OffsetFromOrigin());
    208 
    209   return bounds;
    210 }
    211 
    212 gfx::Rect BrowserAccessibility::GetLocalBoundsForRange(int start, int len)
    213     const {
    214   if (GetRole() != ui::AX_ROLE_STATIC_TEXT) {
    215     // Apply recursively to all static text descendants. For example, if
    216     // you call it on a div with two text node children, it just calls
    217     // GetLocalBoundsForRange on each of the two children (adjusting
    218     // |start| for each one) and unions the resulting rects.
    219     gfx::Rect bounds;
    220     for (size_t i = 0; i < InternalChildCount(); ++i) {
    221       BrowserAccessibility* child = InternalGetChild(i);
    222       int child_len = child->GetStaticTextLenRecursive();
    223       if (start < child_len && start + len > 0) {
    224         gfx::Rect child_rect = child->GetLocalBoundsForRange(start, len);
    225         bounds.Union(child_rect);
    226       }
    227       start -= child_len;
    228     }
    229     return bounds;
    230   }
    231 
    232   int end = start + len;
    233   int child_start = 0;
    234   int child_end = 0;
    235 
    236   gfx::Rect bounds;
    237   for (size_t i = 0; i < InternalChildCount() && child_end < start + len; ++i) {
    238     BrowserAccessibility* child = InternalGetChild(i);
    239     DCHECK_EQ(child->GetRole(), ui::AX_ROLE_INLINE_TEXT_BOX);
    240     std::string child_text;
    241     child->GetStringAttribute(ui::AX_ATTR_VALUE, &child_text);
    242     int child_len = static_cast<int>(child_text.size());
    243     child_start = child_end;
    244     child_end += child_len;
    245 
    246     if (child_end < start)
    247       continue;
    248 
    249     int overlap_start = std::max(start, child_start);
    250     int overlap_end = std::min(end, child_end);
    251 
    252     int local_start = overlap_start - child_start;
    253     int local_end = overlap_end - child_start;
    254 
    255     gfx::Rect child_rect = child->GetLocation();
    256     int text_direction = child->GetIntAttribute(
    257         ui::AX_ATTR_TEXT_DIRECTION);
    258     const std::vector<int32>& character_offsets = child->GetIntListAttribute(
    259         ui::AX_ATTR_CHARACTER_OFFSETS);
    260     int start_pixel_offset =
    261         local_start > 0 ? character_offsets[local_start - 1] : 0;
    262     int end_pixel_offset =
    263         local_end > 0 ? character_offsets[local_end - 1] : 0;
    264 
    265     gfx::Rect child_overlap_rect;
    266     switch (text_direction) {
    267       case ui::AX_TEXT_DIRECTION_NONE:
    268       case ui::AX_TEXT_DIRECTION_LR: {
    269         int left = child_rect.x() + start_pixel_offset;
    270         int right = child_rect.x() + end_pixel_offset;
    271         child_overlap_rect = gfx::Rect(left, child_rect.y(),
    272                                        right - left, child_rect.height());
    273         break;
    274       }
    275       case ui::AX_TEXT_DIRECTION_RL: {
    276         int right = child_rect.right() - start_pixel_offset;
    277         int left = child_rect.right() - end_pixel_offset;
    278         child_overlap_rect = gfx::Rect(left, child_rect.y(),
    279                                        right - left, child_rect.height());
    280         break;
    281       }
    282       case ui::AX_TEXT_DIRECTION_TB: {
    283         int top = child_rect.y() + start_pixel_offset;
    284         int bottom = child_rect.y() + end_pixel_offset;
    285         child_overlap_rect = gfx::Rect(child_rect.x(), top,
    286                                        child_rect.width(), bottom - top);
    287         break;
    288       }
    289       case ui::AX_TEXT_DIRECTION_BT: {
    290         int bottom = child_rect.bottom() - start_pixel_offset;
    291         int top = child_rect.bottom() - end_pixel_offset;
    292         child_overlap_rect = gfx::Rect(child_rect.x(), top,
    293                                        child_rect.width(), bottom - top);
    294         break;
    295       }
    296       default:
    297         NOTREACHED();
    298     }
    299 
    300     if (bounds.width() == 0 && bounds.height() == 0)
    301       bounds = child_overlap_rect;
    302     else
    303       bounds.Union(child_overlap_rect);
    304   }
    305 
    306   return bounds;
    307 }
    308 
    309 gfx::Rect BrowserAccessibility::GetGlobalBoundsForRange(int start, int len)
    310     const {
    311   gfx::Rect bounds = GetLocalBoundsForRange(start, len);
    312 
    313   // Adjust the bounds by the top left corner of the containing view's bounds
    314   // in screen coordinates.
    315   bounds.Offset(manager_->GetViewBounds().OffsetFromOrigin());
    316 
    317   return bounds;
    318 }
    319 
    320 BrowserAccessibility* BrowserAccessibility::BrowserAccessibilityForPoint(
    321     const gfx::Point& point) {
    322   // The best result found that's a child of this object.
    323   BrowserAccessibility* child_result = NULL;
    324   // The best result that's an indirect descendant like grandchild, etc.
    325   BrowserAccessibility* descendant_result = NULL;
    326 
    327   // Walk the children recursively looking for the BrowserAccessibility that
    328   // most tightly encloses the specified point. Walk backwards so that in
    329   // the absence of any other information, we assume the object that occurs
    330   // later in the tree is on top of one that comes before it.
    331   for (int i = static_cast<int>(PlatformChildCount()) - 1; i >= 0; --i) {
    332     BrowserAccessibility* child = PlatformGetChild(i);
    333 
    334     // Skip table columns because cells are only contained in rows,
    335     // not columns.
    336     if (child->GetRole() == ui::AX_ROLE_COLUMN)
    337       continue;
    338 
    339     if (child->GetGlobalBoundsRect().Contains(point)) {
    340       BrowserAccessibility* result = child->BrowserAccessibilityForPoint(point);
    341       if (result == child && !child_result)
    342         child_result = result;
    343       if (result != child && !descendant_result)
    344         descendant_result = result;
    345     }
    346 
    347     if (child_result && descendant_result)
    348       break;
    349   }
    350 
    351   // Explanation of logic: it's possible that this point overlaps more than
    352   // one child of this object. If so, as a heuristic we prefer if the point
    353   // overlaps a descendant of one of the two children and not the other.
    354   // As an example, suppose you have two rows of buttons - the buttons don't
    355   // overlap, but the rows do. Without this heuristic, we'd greedily only
    356   // consider one of the containers.
    357   if (descendant_result)
    358     return descendant_result;
    359   if (child_result)
    360     return child_result;
    361 
    362   return this;
    363 }
    364 
    365 void BrowserAccessibility::Destroy() {
    366   // Allow the object to fire a TextRemoved notification.
    367   name_.clear();
    368   value_.clear();
    369 
    370   manager_->NotifyAccessibilityEvent(ui::AX_EVENT_HIDE, this);
    371   node_ = NULL;
    372   manager_ = NULL;
    373 
    374   NativeReleaseReference();
    375 }
    376 
    377 void BrowserAccessibility::NativeReleaseReference() {
    378   delete this;
    379 }
    380 
    381 bool BrowserAccessibility::HasBoolAttribute(
    382     ui::AXBoolAttribute attribute) const {
    383   const ui::AXNodeData& data = GetData();
    384   for (size_t i = 0; i < data.bool_attributes.size(); ++i) {
    385     if (data.bool_attributes[i].first == attribute)
    386       return true;
    387   }
    388 
    389   return false;
    390 }
    391 
    392 
    393 bool BrowserAccessibility::GetBoolAttribute(
    394     ui::AXBoolAttribute attribute) const {
    395   const ui::AXNodeData& data = GetData();
    396   for (size_t i = 0; i < data.bool_attributes.size(); ++i) {
    397     if (data.bool_attributes[i].first == attribute)
    398       return data.bool_attributes[i].second;
    399   }
    400 
    401   return false;
    402 }
    403 
    404 bool BrowserAccessibility::GetBoolAttribute(
    405     ui::AXBoolAttribute attribute, bool* value) const {
    406   const ui::AXNodeData& data = GetData();
    407   for (size_t i = 0; i < data.bool_attributes.size(); ++i) {
    408     if (data.bool_attributes[i].first == attribute) {
    409       *value = data.bool_attributes[i].second;
    410       return true;
    411     }
    412   }
    413 
    414   return false;
    415 }
    416 
    417 bool BrowserAccessibility::HasFloatAttribute(
    418     ui::AXFloatAttribute attribute) const {
    419   const ui::AXNodeData& data = GetData();
    420   for (size_t i = 0; i < data.float_attributes.size(); ++i) {
    421     if (data.float_attributes[i].first == attribute)
    422       return true;
    423   }
    424 
    425   return false;
    426 }
    427 
    428 float BrowserAccessibility::GetFloatAttribute(
    429     ui::AXFloatAttribute attribute) const {
    430   const ui::AXNodeData& data = GetData();
    431   for (size_t i = 0; i < data.float_attributes.size(); ++i) {
    432     if (data.float_attributes[i].first == attribute)
    433       return data.float_attributes[i].second;
    434   }
    435 
    436   return 0.0;
    437 }
    438 
    439 bool BrowserAccessibility::GetFloatAttribute(
    440     ui::AXFloatAttribute attribute, float* value) const {
    441   const ui::AXNodeData& data = GetData();
    442   for (size_t i = 0; i < data.float_attributes.size(); ++i) {
    443     if (data.float_attributes[i].first == attribute) {
    444       *value = data.float_attributes[i].second;
    445       return true;
    446     }
    447   }
    448 
    449   return false;
    450 }
    451 
    452 bool BrowserAccessibility::HasIntAttribute(
    453     ui::AXIntAttribute attribute) const {
    454   const ui::AXNodeData& data = GetData();
    455   for (size_t i = 0; i < data.int_attributes.size(); ++i) {
    456     if (data.int_attributes[i].first == attribute)
    457       return true;
    458   }
    459 
    460   return false;
    461 }
    462 
    463 int BrowserAccessibility::GetIntAttribute(ui::AXIntAttribute attribute) const {
    464   const ui::AXNodeData& data = GetData();
    465   for (size_t i = 0; i < data.int_attributes.size(); ++i) {
    466     if (data.int_attributes[i].first == attribute)
    467       return data.int_attributes[i].second;
    468   }
    469 
    470   return 0;
    471 }
    472 
    473 bool BrowserAccessibility::GetIntAttribute(
    474     ui::AXIntAttribute attribute, int* value) const {
    475   const ui::AXNodeData& data = GetData();
    476   for (size_t i = 0; i < data.int_attributes.size(); ++i) {
    477     if (data.int_attributes[i].first == attribute) {
    478       *value = data.int_attributes[i].second;
    479       return true;
    480     }
    481   }
    482 
    483   return false;
    484 }
    485 
    486 bool BrowserAccessibility::HasStringAttribute(
    487     ui::AXStringAttribute attribute) const {
    488   const ui::AXNodeData& data = GetData();
    489   for (size_t i = 0; i < data.string_attributes.size(); ++i) {
    490     if (data.string_attributes[i].first == attribute)
    491       return true;
    492   }
    493 
    494   return false;
    495 }
    496 
    497 const std::string& BrowserAccessibility::GetStringAttribute(
    498     ui::AXStringAttribute attribute) const {
    499   const ui::AXNodeData& data = GetData();
    500   CR_DEFINE_STATIC_LOCAL(std::string, empty_string, ());
    501   for (size_t i = 0; i < data.string_attributes.size(); ++i) {
    502     if (data.string_attributes[i].first == attribute)
    503       return data.string_attributes[i].second;
    504   }
    505 
    506   return empty_string;
    507 }
    508 
    509 bool BrowserAccessibility::GetStringAttribute(
    510     ui::AXStringAttribute attribute, std::string* value) const {
    511   const ui::AXNodeData& data = GetData();
    512   for (size_t i = 0; i < data.string_attributes.size(); ++i) {
    513     if (data.string_attributes[i].first == attribute) {
    514       *value = data.string_attributes[i].second;
    515       return true;
    516     }
    517   }
    518 
    519   return false;
    520 }
    521 
    522 base::string16 BrowserAccessibility::GetString16Attribute(
    523     ui::AXStringAttribute attribute) const {
    524   std::string value_utf8;
    525   if (!GetStringAttribute(attribute, &value_utf8))
    526     return base::string16();
    527   return base::UTF8ToUTF16(value_utf8);
    528 }
    529 
    530 bool BrowserAccessibility::GetString16Attribute(
    531     ui::AXStringAttribute attribute,
    532     base::string16* value) const {
    533   std::string value_utf8;
    534   if (!GetStringAttribute(attribute, &value_utf8))
    535     return false;
    536   *value = base::UTF8ToUTF16(value_utf8);
    537   return true;
    538 }
    539 
    540 void BrowserAccessibility::SetStringAttribute(
    541     ui::AXStringAttribute attribute, const std::string& value) {
    542   if (!node_)
    543     return;
    544   ui::AXNodeData data = GetData();
    545   for (size_t i = 0; i < data.string_attributes.size(); ++i) {
    546     if (data.string_attributes[i].first == attribute) {
    547       data.string_attributes[i].second = value;
    548       node_->SetData(data);
    549       return;
    550     }
    551   }
    552   if (!value.empty()) {
    553     data.string_attributes.push_back(std::make_pair(attribute, value));
    554     node_->SetData(data);
    555   }
    556 }
    557 
    558 bool BrowserAccessibility::HasIntListAttribute(
    559     ui::AXIntListAttribute attribute) const {
    560   const ui::AXNodeData& data = GetData();
    561   for (size_t i = 0; i < data.intlist_attributes.size(); ++i) {
    562     if (data.intlist_attributes[i].first == attribute)
    563       return true;
    564   }
    565 
    566   return false;
    567 }
    568 
    569 const std::vector<int32>& BrowserAccessibility::GetIntListAttribute(
    570     ui::AXIntListAttribute attribute) const {
    571   const ui::AXNodeData& data = GetData();
    572   CR_DEFINE_STATIC_LOCAL(std::vector<int32>, empty_vector, ());
    573   for (size_t i = 0; i < data.intlist_attributes.size(); ++i) {
    574     if (data.intlist_attributes[i].first == attribute)
    575       return data.intlist_attributes[i].second;
    576   }
    577 
    578   return empty_vector;
    579 }
    580 
    581 bool BrowserAccessibility::GetIntListAttribute(
    582     ui::AXIntListAttribute attribute,
    583     std::vector<int32>* value) const {
    584   const ui::AXNodeData& data = GetData();
    585   for (size_t i = 0; i < data.intlist_attributes.size(); ++i) {
    586     if (data.intlist_attributes[i].first == attribute) {
    587       *value = data.intlist_attributes[i].second;
    588       return true;
    589     }
    590   }
    591 
    592   return false;
    593 }
    594 
    595 bool BrowserAccessibility::GetHtmlAttribute(
    596     const char* html_attr, std::string* value) const {
    597   for (size_t i = 0; i < GetHtmlAttributes().size(); ++i) {
    598     const std::string& attr = GetHtmlAttributes()[i].first;
    599     if (LowerCaseEqualsASCII(attr, html_attr)) {
    600       *value = GetHtmlAttributes()[i].second;
    601       return true;
    602     }
    603   }
    604 
    605   return false;
    606 }
    607 
    608 bool BrowserAccessibility::GetHtmlAttribute(
    609     const char* html_attr, base::string16* value) const {
    610   std::string value_utf8;
    611   if (!GetHtmlAttribute(html_attr, &value_utf8))
    612     return false;
    613   *value = base::UTF8ToUTF16(value_utf8);
    614   return true;
    615 }
    616 
    617 bool BrowserAccessibility::GetAriaTristate(
    618     const char* html_attr,
    619     bool* is_defined,
    620     bool* is_mixed) const {
    621   *is_defined = false;
    622   *is_mixed = false;
    623 
    624   base::string16 value;
    625   if (!GetHtmlAttribute(html_attr, &value) ||
    626       value.empty() ||
    627       EqualsASCII(value, "undefined")) {
    628     return false;  // Not set (and *is_defined is also false)
    629   }
    630 
    631   *is_defined = true;
    632 
    633   if (EqualsASCII(value, "true"))
    634     return true;
    635 
    636   if (EqualsASCII(value, "mixed"))
    637     *is_mixed = true;
    638 
    639   return false;  // Not set
    640 }
    641 
    642 bool BrowserAccessibility::HasState(ui::AXState state_enum) const {
    643   return (GetState() >> state_enum) & 1;
    644 }
    645 
    646 bool BrowserAccessibility::IsEditableText() const {
    647   // These roles don't have readonly set, but they're not editable text.
    648   if (GetRole() == ui::AX_ROLE_SCROLL_AREA ||
    649       GetRole() == ui::AX_ROLE_COLUMN ||
    650       GetRole() == ui::AX_ROLE_TABLE_HEADER_CONTAINER) {
    651     return false;
    652   }
    653 
    654   // Note: WebAXStateReadonly being false means it's either a text control,
    655   // or contenteditable. We also check for editable text roles to cover
    656   // another element that has role=textbox set on it.
    657   return (!HasState(ui::AX_STATE_READ_ONLY) ||
    658           GetRole() == ui::AX_ROLE_TEXT_FIELD ||
    659           GetRole() == ui::AX_ROLE_TEXT_AREA);
    660 }
    661 
    662 std::string BrowserAccessibility::GetTextRecursive() const {
    663   if (!name_.empty()) {
    664     return name_;
    665   }
    666 
    667   std::string result;
    668   for (uint32 i = 0; i < PlatformChildCount(); ++i)
    669     result += PlatformGetChild(i)->GetTextRecursive();
    670   return result;
    671 }
    672 
    673 int BrowserAccessibility::GetStaticTextLenRecursive() const {
    674   if (GetRole() == ui::AX_ROLE_STATIC_TEXT)
    675     return static_cast<int>(GetStringAttribute(ui::AX_ATTR_VALUE).size());
    676 
    677   int len = 0;
    678   for (size_t i = 0; i < InternalChildCount(); ++i)
    679     len += InternalGetChild(i)->GetStaticTextLenRecursive();
    680   return len;
    681 }
    682 
    683 }  // namespace content
    684