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