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 typedef AccessibilityNodeData::BoolAttribute BoolAttribute;
     17 typedef AccessibilityNodeData::FloatAttribute FloatAttribute;
     18 typedef AccessibilityNodeData::IntAttribute IntAttribute;
     19 typedef AccessibilityNodeData::StringAttribute StringAttribute;
     20 
     21 #if !defined(OS_MACOSX) && \
     22     !defined(OS_WIN) && \
     23     !defined(TOOLKIT_GTK) && \
     24     !defined(OS_ANDROID)
     25 // We have subclassess of BrowserAccessibility on Mac, Linux/GTK,
     26 // and Win. For any other platform, instantiate the base class.
     27 // static
     28 BrowserAccessibility* BrowserAccessibility::Create() {
     29   return new BrowserAccessibility();
     30 }
     31 #endif
     32 
     33 BrowserAccessibility::BrowserAccessibility()
     34     : manager_(NULL),
     35       parent_(NULL),
     36       index_in_parent_(0),
     37       renderer_id_(0),
     38       role_(0),
     39       state_(0),
     40       instance_active_(false) {
     41 }
     42 
     43 BrowserAccessibility::~BrowserAccessibility() {
     44 }
     45 
     46 bool BrowserAccessibility::PlatformIsLeaf() const {
     47   return role_ == blink::WebAXRoleStaticText || child_count() == 0;
     48 }
     49 
     50 uint32 BrowserAccessibility::PlatformChildCount() const {
     51   return PlatformIsLeaf() ? 0 : children_.size();
     52 }
     53 
     54 void BrowserAccessibility::DetachTree(
     55     std::vector<BrowserAccessibility*>* nodes) {
     56   nodes->push_back(this);
     57   for (size_t i = 0; i < children_.size(); ++i)
     58     children_[i]->DetachTree(nodes);
     59   children_.clear();
     60   parent_ = NULL;
     61 }
     62 
     63 void BrowserAccessibility::InitializeTreeStructure(
     64     BrowserAccessibilityManager* manager,
     65     BrowserAccessibility* parent,
     66     int32 renderer_id,
     67     int32 index_in_parent) {
     68   manager_ = manager;
     69   parent_ = parent;
     70   renderer_id_ = renderer_id;
     71   index_in_parent_ = index_in_parent;
     72 }
     73 
     74 void BrowserAccessibility::InitializeData(const AccessibilityNodeData& src) {
     75   DCHECK_EQ(renderer_id_, src.id);
     76   role_ = src.role;
     77   state_ = src.state;
     78   string_attributes_ = src.string_attributes;
     79   int_attributes_ = src.int_attributes;
     80   float_attributes_ = src.float_attributes;
     81   bool_attributes_ = src.bool_attributes;
     82   intlist_attributes_ = src.intlist_attributes;
     83   html_attributes_ = src.html_attributes;
     84   location_ = src.location;
     85   instance_active_ = true;
     86 
     87   GetStringAttribute(AccessibilityNodeData::ATTR_NAME, &name_);
     88   GetStringAttribute(AccessibilityNodeData::ATTR_VALUE, &value_);
     89 
     90   PreInitialize();
     91 }
     92 
     93 bool BrowserAccessibility::IsNative() const {
     94   return false;
     95 }
     96 
     97 void BrowserAccessibility::SwapChildren(
     98     std::vector<BrowserAccessibility*>& children) {
     99   children.swap(children_);
    100 }
    101 
    102 void BrowserAccessibility::UpdateParent(BrowserAccessibility* parent,
    103                                         int index_in_parent) {
    104   parent_ = parent;
    105   index_in_parent_ = index_in_parent;
    106 }
    107 
    108 void BrowserAccessibility::SetLocation(const gfx::Rect& new_location) {
    109   location_ = new_location;
    110 }
    111 
    112 bool BrowserAccessibility::IsDescendantOf(
    113     BrowserAccessibility* ancestor) {
    114   if (this == ancestor) {
    115     return true;
    116   } else if (parent_) {
    117     return parent_->IsDescendantOf(ancestor);
    118   }
    119 
    120   return false;
    121 }
    122 
    123 BrowserAccessibility* BrowserAccessibility::PlatformGetChild(
    124     uint32 child_index) const {
    125   DCHECK(child_index < children_.size());
    126   return children_[child_index];
    127 }
    128 
    129 BrowserAccessibility* BrowserAccessibility::GetPreviousSibling() {
    130   if (parent_ && index_in_parent_ > 0)
    131     return parent_->children_[index_in_parent_ - 1];
    132 
    133   return NULL;
    134 }
    135 
    136 BrowserAccessibility* BrowserAccessibility::GetNextSibling() {
    137   if (parent_ &&
    138       index_in_parent_ >= 0 &&
    139       index_in_parent_ < static_cast<int>(parent_->children_.size() - 1)) {
    140     return parent_->children_[index_in_parent_ + 1];
    141   }
    142 
    143   return NULL;
    144 }
    145 
    146 gfx::Rect BrowserAccessibility::GetLocalBoundsRect() const {
    147   gfx::Rect bounds = location_;
    148 
    149   // Walk up the parent chain. Every time we encounter a Web Area, offset
    150   // based on the scroll bars and then offset based on the origin of that
    151   // nested web area.
    152   BrowserAccessibility* parent = parent_;
    153   bool need_to_offset_web_area =
    154       (role_ == blink::WebAXRoleWebArea ||
    155        role_ == blink::WebAXRoleRootWebArea);
    156   while (parent) {
    157     if (need_to_offset_web_area &&
    158         parent->location().width() > 0 &&
    159         parent->location().height() > 0) {
    160       bounds.Offset(parent->location().x(), parent->location().y());
    161       need_to_offset_web_area = false;
    162     }
    163 
    164     // On some platforms, we don't want to take the root scroll offsets
    165     // into account.
    166     if (parent->role() == blink::WebAXRoleRootWebArea &&
    167         !manager()->UseRootScrollOffsetsWhenComputingBounds()) {
    168       break;
    169     }
    170 
    171     if (parent->role() == blink::WebAXRoleWebArea ||
    172         parent->role() == blink::WebAXRoleRootWebArea) {
    173       int sx = 0;
    174       int sy = 0;
    175       if (parent->GetIntAttribute(AccessibilityNodeData::ATTR_SCROLL_X, &sx) &&
    176           parent->GetIntAttribute(AccessibilityNodeData::ATTR_SCROLL_Y, &sy)) {
    177         bounds.Offset(-sx, -sy);
    178       }
    179       need_to_offset_web_area = true;
    180     }
    181     parent = parent->parent();
    182   }
    183 
    184   return bounds;
    185 }
    186 
    187 gfx::Rect BrowserAccessibility::GetGlobalBoundsRect() const {
    188   gfx::Rect bounds = GetLocalBoundsRect();
    189 
    190   // Adjust the bounds by the top left corner of the containing view's bounds
    191   // in screen coordinates.
    192   bounds.Offset(manager_->GetViewBounds().OffsetFromOrigin());
    193 
    194   return bounds;
    195 }
    196 
    197 gfx::Rect BrowserAccessibility::GetLocalBoundsForRange(int start, int len)
    198     const {
    199   DCHECK_EQ(role_, blink::WebAXRoleStaticText);
    200   int end = start + len;
    201   int child_start = 0;
    202   int child_end = 0;
    203 
    204   gfx::Rect bounds;
    205   for (size_t i = 0; i < children_.size() && child_end < start + len; ++i) {
    206     BrowserAccessibility* child = children_[i];
    207     DCHECK_EQ(child->role(), blink::WebAXRoleInlineTextBox);
    208     std::string child_text;
    209     child->GetStringAttribute(AccessibilityNodeData::ATTR_VALUE, &child_text);
    210     int child_len = static_cast<int>(child_text.size());
    211     child_start = child_end;
    212     child_end += child_len;
    213 
    214     if (child_end < start)
    215       continue;
    216 
    217     int overlap_start = std::max(start, child_start);
    218     int overlap_end = std::min(end, child_end);
    219 
    220     int local_start = overlap_start - child_start;
    221     int local_end = overlap_end - child_start;
    222 
    223     gfx::Rect child_rect = child->location();
    224     int text_direction = child->GetIntAttribute(
    225         AccessibilityNodeData::ATTR_TEXT_DIRECTION);
    226     const std::vector<int32>& character_offsets = child->GetIntListAttribute(
    227         AccessibilityNodeData::ATTR_CHARACTER_OFFSETS);
    228     int start_pixel_offset =
    229         local_start > 0 ? character_offsets[local_start - 1] : 0;
    230     int end_pixel_offset =
    231         local_end > 0 ? character_offsets[local_end - 1] : 0;
    232 
    233     gfx::Rect child_overlap_rect;
    234     switch (text_direction) {
    235       case blink::WebAXTextDirectionLR: {
    236         int left = child_rect.x() + start_pixel_offset;
    237         int right = child_rect.x() + end_pixel_offset;
    238         child_overlap_rect = gfx::Rect(left, child_rect.y(),
    239                                        right - left, child_rect.height());
    240         break;
    241       }
    242       case blink::WebAXTextDirectionRL: {
    243         int right = child_rect.right() - start_pixel_offset;
    244         int left = child_rect.right() - end_pixel_offset;
    245         child_overlap_rect = gfx::Rect(left, child_rect.y(),
    246                                        right - left, child_rect.height());
    247         break;
    248       }
    249       case blink::WebAXTextDirectionTB: {
    250         int top = child_rect.y() + start_pixel_offset;
    251         int bottom = child_rect.y() + end_pixel_offset;
    252         child_overlap_rect = gfx::Rect(child_rect.x(), top,
    253                                        child_rect.width(), bottom - top);
    254         break;
    255       }
    256       case blink::WebAXTextDirectionBT: {
    257         int bottom = child_rect.bottom() - start_pixel_offset;
    258         int top = child_rect.bottom() - end_pixel_offset;
    259         child_overlap_rect = gfx::Rect(child_rect.x(), top,
    260                                        child_rect.width(), bottom - top);
    261         break;
    262       }
    263       default:
    264         NOTREACHED();
    265     }
    266 
    267     if (bounds.width() == 0 && bounds.height() == 0)
    268       bounds = child_overlap_rect;
    269     else
    270       bounds.Union(child_overlap_rect);
    271   }
    272 
    273   return bounds;
    274 }
    275 
    276 gfx::Rect BrowserAccessibility::GetGlobalBoundsForRange(int start, int len)
    277     const {
    278   gfx::Rect bounds = GetLocalBoundsForRange(start, len);
    279 
    280   // Adjust the bounds by the top left corner of the containing view's bounds
    281   // in screen coordinates.
    282   bounds.Offset(manager_->GetViewBounds().OffsetFromOrigin());
    283 
    284   return bounds;
    285 }
    286 
    287 BrowserAccessibility* BrowserAccessibility::BrowserAccessibilityForPoint(
    288     const gfx::Point& point) {
    289   // Walk the children recursively looking for the BrowserAccessibility that
    290   // most tightly encloses the specified point.
    291   for (int i = static_cast<int>(PlatformChildCount()) - 1; i >= 0; --i) {
    292     BrowserAccessibility* child = PlatformGetChild(i);
    293     if (child->GetGlobalBoundsRect().Contains(point))
    294       return child->BrowserAccessibilityForPoint(point);
    295   }
    296   return this;
    297 }
    298 
    299 void BrowserAccessibility::Destroy() {
    300   for (std::vector<BrowserAccessibility*>::iterator iter = children_.begin();
    301        iter != children_.end();
    302        ++iter) {
    303     (*iter)->Destroy();
    304   }
    305   children_.clear();
    306 
    307   // Allow the object to fire a TextRemoved notification.
    308   name_.clear();
    309   value_.clear();
    310   PostInitialize();
    311 
    312   manager_->NotifyAccessibilityEvent(
    313       blink::WebAXEventHide, this);
    314 
    315   instance_active_ = false;
    316   manager_->RemoveNode(this);
    317   NativeReleaseReference();
    318 }
    319 
    320 void BrowserAccessibility::NativeReleaseReference() {
    321   delete this;
    322 }
    323 
    324 bool BrowserAccessibility::HasBoolAttribute(BoolAttribute attribute) const {
    325   for (size_t i = 0; i < bool_attributes_.size(); ++i) {
    326     if (bool_attributes_[i].first == attribute)
    327       return true;
    328   }
    329 
    330   return false;
    331 }
    332 
    333 
    334 bool BrowserAccessibility::GetBoolAttribute(BoolAttribute attribute) const {
    335   for (size_t i = 0; i < bool_attributes_.size(); ++i) {
    336     if (bool_attributes_[i].first == attribute)
    337       return bool_attributes_[i].second;
    338   }
    339 
    340   return false;
    341 }
    342 
    343 bool BrowserAccessibility::GetBoolAttribute(
    344     BoolAttribute attribute, bool* value) const {
    345   for (size_t i = 0; i < bool_attributes_.size(); ++i) {
    346     if (bool_attributes_[i].first == attribute) {
    347       *value = bool_attributes_[i].second;
    348       return true;
    349     }
    350   }
    351 
    352   return false;
    353 }
    354 
    355 bool BrowserAccessibility::HasFloatAttribute(FloatAttribute attribute) const {
    356   for (size_t i = 0; i < float_attributes_.size(); ++i) {
    357     if (float_attributes_[i].first == attribute)
    358       return true;
    359   }
    360 
    361   return false;
    362 }
    363 
    364 float BrowserAccessibility::GetFloatAttribute(FloatAttribute attribute) const {
    365   for (size_t i = 0; i < float_attributes_.size(); ++i) {
    366     if (float_attributes_[i].first == attribute)
    367       return float_attributes_[i].second;
    368   }
    369 
    370   return 0.0;
    371 }
    372 
    373 bool BrowserAccessibility::GetFloatAttribute(
    374     FloatAttribute attribute, float* value) const {
    375   for (size_t i = 0; i < float_attributes_.size(); ++i) {
    376     if (float_attributes_[i].first == attribute) {
    377       *value = float_attributes_[i].second;
    378       return true;
    379     }
    380   }
    381 
    382   return false;
    383 }
    384 
    385 bool BrowserAccessibility::HasIntAttribute(IntAttribute attribute) const {
    386   for (size_t i = 0; i < int_attributes_.size(); ++i) {
    387     if (int_attributes_[i].first == attribute)
    388       return true;
    389   }
    390 
    391   return false;
    392 }
    393 
    394 int BrowserAccessibility::GetIntAttribute(IntAttribute attribute) const {
    395   for (size_t i = 0; i < int_attributes_.size(); ++i) {
    396     if (int_attributes_[i].first == attribute)
    397       return int_attributes_[i].second;
    398   }
    399 
    400   return 0;
    401 }
    402 
    403 bool BrowserAccessibility::GetIntAttribute(
    404     IntAttribute attribute, int* value) const {
    405   for (size_t i = 0; i < int_attributes_.size(); ++i) {
    406     if (int_attributes_[i].first == attribute) {
    407       *value = int_attributes_[i].second;
    408       return true;
    409     }
    410   }
    411 
    412   return false;
    413 }
    414 
    415 bool BrowserAccessibility::HasStringAttribute(StringAttribute attribute) const {
    416   for (size_t i = 0; i < string_attributes_.size(); ++i) {
    417     if (string_attributes_[i].first == attribute)
    418       return true;
    419   }
    420 
    421   return false;
    422 }
    423 
    424 const std::string& BrowserAccessibility::GetStringAttribute(
    425     StringAttribute attribute) const {
    426   CR_DEFINE_STATIC_LOCAL(std::string, empty_string, ());
    427   for (size_t i = 0; i < string_attributes_.size(); ++i) {
    428     if (string_attributes_[i].first == attribute)
    429       return string_attributes_[i].second;
    430   }
    431 
    432   return empty_string;
    433 }
    434 
    435 bool BrowserAccessibility::GetStringAttribute(
    436     StringAttribute attribute, std::string* value) const {
    437   for (size_t i = 0; i < string_attributes_.size(); ++i) {
    438     if (string_attributes_[i].first == attribute) {
    439       *value = string_attributes_[i].second;
    440       return true;
    441     }
    442   }
    443 
    444   return false;
    445 }
    446 
    447 base::string16 BrowserAccessibility::GetString16Attribute(
    448     StringAttribute attribute) const {
    449   std::string value_utf8;
    450   if (!GetStringAttribute(attribute, &value_utf8))
    451     return base::string16();
    452   return UTF8ToUTF16(value_utf8);
    453 }
    454 
    455 bool BrowserAccessibility::GetString16Attribute(
    456     StringAttribute attribute,
    457     base::string16* value) const {
    458   std::string value_utf8;
    459   if (!GetStringAttribute(attribute, &value_utf8))
    460     return false;
    461   *value = UTF8ToUTF16(value_utf8);
    462   return true;
    463 }
    464 
    465 void BrowserAccessibility::SetStringAttribute(
    466     StringAttribute attribute, const std::string& value) {
    467   for (size_t i = 0; i < string_attributes_.size(); ++i) {
    468     if (string_attributes_[i].first == attribute) {
    469       string_attributes_[i].second = value;
    470       return;
    471     }
    472   }
    473   if (!value.empty())
    474     string_attributes_.push_back(std::make_pair(attribute, value));
    475 }
    476 
    477 bool BrowserAccessibility::HasIntListAttribute(
    478     AccessibilityNodeData::IntListAttribute attribute) const {
    479   for (size_t i = 0; i < intlist_attributes_.size(); ++i) {
    480     if (intlist_attributes_[i].first == attribute)
    481       return true;
    482   }
    483 
    484   return false;
    485 }
    486 
    487 const std::vector<int32>& BrowserAccessibility::GetIntListAttribute(
    488     AccessibilityNodeData::IntListAttribute attribute) const {
    489   CR_DEFINE_STATIC_LOCAL(std::vector<int32>, empty_vector, ());
    490   for (size_t i = 0; i < intlist_attributes_.size(); ++i) {
    491     if (intlist_attributes_[i].first == attribute)
    492       return intlist_attributes_[i].second;
    493   }
    494 
    495   return empty_vector;
    496 }
    497 
    498 bool BrowserAccessibility::GetIntListAttribute(
    499     AccessibilityNodeData::IntListAttribute attribute,
    500     std::vector<int32>* value) const {
    501   for (size_t i = 0; i < intlist_attributes_.size(); ++i) {
    502     if (intlist_attributes_[i].first == attribute) {
    503       *value = intlist_attributes_[i].second;
    504       return true;
    505     }
    506   }
    507 
    508   return false;
    509 }
    510 
    511 bool BrowserAccessibility::GetHtmlAttribute(
    512     const char* html_attr, std::string* value) const {
    513   for (size_t i = 0; i < html_attributes_.size(); ++i) {
    514     const std::string& attr = html_attributes_[i].first;
    515     if (LowerCaseEqualsASCII(attr, html_attr)) {
    516       *value = html_attributes_[i].second;
    517       return true;
    518     }
    519   }
    520 
    521   return false;
    522 }
    523 
    524 bool BrowserAccessibility::GetHtmlAttribute(
    525     const char* html_attr, base::string16* value) const {
    526   std::string value_utf8;
    527   if (!GetHtmlAttribute(html_attr, &value_utf8))
    528     return false;
    529   *value = UTF8ToUTF16(value_utf8);
    530   return true;
    531 }
    532 
    533 bool BrowserAccessibility::GetAriaTristate(
    534     const char* html_attr,
    535     bool* is_defined,
    536     bool* is_mixed) const {
    537   *is_defined = false;
    538   *is_mixed = false;
    539 
    540   base::string16 value;
    541   if (!GetHtmlAttribute(html_attr, &value) ||
    542       value.empty() ||
    543       EqualsASCII(value, "undefined")) {
    544     return false;  // Not set (and *is_defined is also false)
    545   }
    546 
    547   *is_defined = true;
    548 
    549   if (EqualsASCII(value, "true"))
    550     return true;
    551 
    552   if (EqualsASCII(value, "mixed"))
    553     *is_mixed = true;
    554 
    555   return false;  // Not set
    556 }
    557 
    558 bool BrowserAccessibility::HasState(blink::WebAXState state_enum) const {
    559   return (state_ >> state_enum) & 1;
    560 }
    561 
    562 bool BrowserAccessibility::IsEditableText() const {
    563   // These roles don't have readonly set, but they're not editable text.
    564   if (role_ == blink::WebAXRoleScrollArea ||
    565       role_ == blink::WebAXRoleColumn ||
    566       role_ == blink::WebAXRoleTableHeaderContainer) {
    567     return false;
    568   }
    569 
    570   // Note: WebAXStateReadonly being false means it's either a text control,
    571   // or contenteditable. We also check for editable text roles to cover
    572   // another element that has role=textbox set on it.
    573   return (!HasState(blink::WebAXStateReadonly) ||
    574           role_ == blink::WebAXRoleTextField ||
    575           role_ == blink::WebAXRoleTextArea);
    576 }
    577 
    578 std::string BrowserAccessibility::GetTextRecursive() const {
    579   if (!name_.empty()) {
    580     return name_;
    581   }
    582 
    583   std::string result;
    584   for (uint32 i = 0; i < PlatformChildCount(); ++i)
    585     result += PlatformGetChild(i)->GetTextRecursive();
    586   return result;
    587 }
    588 
    589 }  // namespace content
    590