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 "content/browser/accessibility/browser_accessibility_manager.h"
     11 #include "content/common/accessibility_messages.h"
     12 
     13 namespace content {
     14 
     15 typedef AccessibilityNodeData::BoolAttribute BoolAttribute;
     16 typedef AccessibilityNodeData::FloatAttribute FloatAttribute;
     17 typedef AccessibilityNodeData::IntAttribute IntAttribute;
     18 typedef AccessibilityNodeData::StringAttribute StringAttribute;
     19 
     20 #if !defined(OS_MACOSX) && \
     21     !defined(OS_WIN) && \
     22     !defined(TOOLKIT_GTK) && \
     23     !defined(OS_ANDROID)
     24 // We have subclassess of BrowserAccessibility on Mac, Linux/GTK,
     25 // and Win. For any other platform, instantiate the base class.
     26 // static
     27 BrowserAccessibility* BrowserAccessibility::Create() {
     28   return new BrowserAccessibility();
     29 }
     30 #endif
     31 
     32 BrowserAccessibility::BrowserAccessibility()
     33     : manager_(NULL),
     34       parent_(NULL),
     35       index_in_parent_(0),
     36       renderer_id_(0),
     37       role_(0),
     38       state_(0),
     39       instance_active_(false) {
     40 }
     41 
     42 BrowserAccessibility::~BrowserAccessibility() {
     43 }
     44 
     45 void BrowserAccessibility::DetachTree(
     46     std::vector<BrowserAccessibility*>* nodes) {
     47   nodes->push_back(this);
     48   for (size_t i = 0; i < children_.size(); i++)
     49     children_[i]->DetachTree(nodes);
     50   children_.clear();
     51   parent_ = NULL;
     52 }
     53 
     54 void BrowserAccessibility::InitializeTreeStructure(
     55     BrowserAccessibilityManager* manager,
     56     BrowserAccessibility* parent,
     57     int32 renderer_id,
     58     int32 index_in_parent) {
     59   manager_ = manager;
     60   parent_ = parent;
     61   renderer_id_ = renderer_id;
     62   index_in_parent_ = index_in_parent;
     63 }
     64 
     65 void BrowserAccessibility::InitializeData(const AccessibilityNodeData& src) {
     66   DCHECK_EQ(renderer_id_, src.id);
     67   name_ = src.name;
     68   value_ = src.value;
     69   role_ = src.role;
     70   state_ = src.state;
     71   string_attributes_ = src.string_attributes;
     72   int_attributes_ = src.int_attributes;
     73   float_attributes_ = src.float_attributes;
     74   bool_attributes_ = src.bool_attributes;
     75   html_attributes_ = src.html_attributes;
     76   location_ = src.location;
     77   indirect_child_ids_ = src.indirect_child_ids;
     78   line_breaks_ = src.line_breaks;
     79   cell_ids_ = src.cell_ids;
     80   unique_cell_ids_ = src.unique_cell_ids;
     81   instance_active_ = true;
     82 
     83   PreInitialize();
     84 }
     85 
     86 bool BrowserAccessibility::IsNative() const {
     87   return false;
     88 }
     89 
     90 void BrowserAccessibility::SwapChildren(
     91     std::vector<BrowserAccessibility*>& children) {
     92   children.swap(children_);
     93 }
     94 
     95 void BrowserAccessibility::UpdateParent(BrowserAccessibility* parent,
     96                                         int index_in_parent) {
     97   parent_ = parent;
     98   index_in_parent_ = index_in_parent;
     99 }
    100 
    101 void BrowserAccessibility::SetLocation(const gfx::Rect& new_location) {
    102   location_ = new_location;
    103 }
    104 
    105 bool BrowserAccessibility::IsDescendantOf(
    106     BrowserAccessibility* ancestor) {
    107   if (this == ancestor) {
    108     return true;
    109   } else if (parent_) {
    110     return parent_->IsDescendantOf(ancestor);
    111   }
    112 
    113   return false;
    114 }
    115 
    116 BrowserAccessibility* BrowserAccessibility::GetChild(uint32 child_index) const {
    117   DCHECK(child_index < children_.size());
    118   return children_[child_index];
    119 }
    120 
    121 BrowserAccessibility* BrowserAccessibility::GetPreviousSibling() {
    122   if (parent_ && index_in_parent_ > 0)
    123     return parent_->children_[index_in_parent_ - 1];
    124 
    125   return NULL;
    126 }
    127 
    128 BrowserAccessibility* BrowserAccessibility::GetNextSibling() {
    129   if (parent_ &&
    130       index_in_parent_ >= 0 &&
    131       index_in_parent_ < static_cast<int>(parent_->children_.size() - 1)) {
    132     return parent_->children_[index_in_parent_ + 1];
    133   }
    134 
    135   return NULL;
    136 }
    137 
    138 gfx::Rect BrowserAccessibility::GetLocalBoundsRect() const {
    139   gfx::Rect bounds = location_;
    140 
    141   // Walk up the parent chain. Every time we encounter a Web Area, offset
    142   // based on the scroll bars and then offset based on the origin of that
    143   // nested web area.
    144   BrowserAccessibility* parent = parent_;
    145   bool need_to_offset_web_area =
    146       (role_ == AccessibilityNodeData::ROLE_WEB_AREA ||
    147        role_ == AccessibilityNodeData::ROLE_ROOT_WEB_AREA);
    148   while (parent) {
    149     if (need_to_offset_web_area &&
    150         parent->location().width() > 0 &&
    151         parent->location().height() > 0) {
    152       bounds.Offset(parent->location().x(), parent->location().y());
    153       need_to_offset_web_area = false;
    154     }
    155 
    156     // On some platforms, we don't want to take the root scroll offsets
    157     // into account.
    158     if (parent->role() == AccessibilityNodeData::ROLE_ROOT_WEB_AREA &&
    159         !manager()->UseRootScrollOffsetsWhenComputingBounds()) {
    160       break;
    161     }
    162 
    163     if (parent->role() == AccessibilityNodeData::ROLE_WEB_AREA ||
    164         parent->role() == AccessibilityNodeData::ROLE_ROOT_WEB_AREA) {
    165       int sx = 0;
    166       int sy = 0;
    167       if (parent->GetIntAttribute(AccessibilityNodeData::ATTR_SCROLL_X, &sx) &&
    168           parent->GetIntAttribute(AccessibilityNodeData::ATTR_SCROLL_Y, &sy)) {
    169         bounds.Offset(-sx, -sy);
    170       }
    171       need_to_offset_web_area = true;
    172     }
    173     parent = parent->parent();
    174   }
    175 
    176   return bounds;
    177 }
    178 
    179 gfx::Rect BrowserAccessibility::GetGlobalBoundsRect() const {
    180   gfx::Rect bounds = GetLocalBoundsRect();
    181 
    182   // Adjust the bounds by the top left corner of the containing view's bounds
    183   // in screen coordinates.
    184   bounds.Offset(manager_->GetViewBounds().OffsetFromOrigin());
    185 
    186   return bounds;
    187 }
    188 
    189 BrowserAccessibility* BrowserAccessibility::BrowserAccessibilityForPoint(
    190     const gfx::Point& point) {
    191   // Walk the children recursively looking for the BrowserAccessibility that
    192   // most tightly encloses the specified point.
    193   for (int i = children_.size() - 1; i >= 0; --i) {
    194     BrowserAccessibility* child = children_[i];
    195     if (child->GetGlobalBoundsRect().Contains(point))
    196       return child->BrowserAccessibilityForPoint(point);
    197   }
    198   return this;
    199 }
    200 
    201 void BrowserAccessibility::Destroy() {
    202   for (std::vector<BrowserAccessibility*>::iterator iter = children_.begin();
    203        iter != children_.end();
    204        ++iter) {
    205     (*iter)->Destroy();
    206   }
    207   children_.clear();
    208 
    209   // Allow the object to fire a TextRemoved notification.
    210   name_.clear();
    211   value_.clear();
    212   PostInitialize();
    213 
    214   manager_->NotifyAccessibilityEvent(
    215       AccessibilityNotificationObjectHide, this);
    216 
    217   instance_active_ = false;
    218   manager_->RemoveNode(this);
    219   NativeReleaseReference();
    220 }
    221 
    222 void BrowserAccessibility::NativeReleaseReference() {
    223   delete this;
    224 }
    225 
    226 bool BrowserAccessibility::GetBoolAttribute(
    227     BoolAttribute attribute, bool* value) const {
    228   BoolAttrMap::const_iterator iter = bool_attributes_.find(attribute);
    229   if (iter != bool_attributes_.end()) {
    230     *value = iter->second;
    231     return true;
    232   }
    233 
    234   return false;
    235 }
    236 
    237 bool BrowserAccessibility::GetFloatAttribute(
    238     FloatAttribute attribute, float* value) const {
    239   FloatAttrMap::const_iterator iter = float_attributes_.find(attribute);
    240   if (iter != float_attributes_.end()) {
    241     *value = iter->second;
    242     return true;
    243   }
    244 
    245   return false;
    246 }
    247 
    248 bool BrowserAccessibility::GetIntAttribute(
    249     IntAttribute attribute, int* value) const {
    250   IntAttrMap::const_iterator iter = int_attributes_.find(attribute);
    251   if (iter != int_attributes_.end()) {
    252     *value = iter->second;
    253     return true;
    254   }
    255 
    256   return false;
    257 }
    258 
    259 bool BrowserAccessibility::GetStringAttribute(
    260     StringAttribute attribute,
    261     string16* value) const {
    262   StringAttrMap::const_iterator iter = string_attributes_.find(attribute);
    263   if (iter != string_attributes_.end()) {
    264     *value = iter->second;
    265     return true;
    266   }
    267 
    268   return false;
    269 }
    270 
    271 bool BrowserAccessibility::GetHtmlAttribute(
    272     const char* html_attr, string16* value) const {
    273   for (size_t i = 0; i < html_attributes_.size(); i++) {
    274     const string16& attr = html_attributes_[i].first;
    275     if (LowerCaseEqualsASCII(attr, html_attr)) {
    276       *value = html_attributes_[i].second;
    277       return true;
    278     }
    279   }
    280 
    281   return false;
    282 }
    283 
    284 bool BrowserAccessibility::GetAriaTristate(
    285     const char* html_attr,
    286     bool* is_defined,
    287     bool* is_mixed) const {
    288   *is_defined = false;
    289   *is_mixed = false;
    290 
    291   string16 value;
    292   if (!GetHtmlAttribute(html_attr, &value) ||
    293       value.empty() ||
    294       EqualsASCII(value, "undefined")) {
    295     return false;  // Not set (and *is_defined is also false)
    296   }
    297 
    298   *is_defined = true;
    299 
    300   if (EqualsASCII(value, "true"))
    301     return true;
    302 
    303   if (EqualsASCII(value, "mixed"))
    304     *is_mixed = true;
    305 
    306   return false;  // Not set
    307 }
    308 
    309 bool BrowserAccessibility::HasState(
    310     AccessibilityNodeData::State state_enum) const {
    311   return (state_ >> state_enum) & 1;
    312 }
    313 
    314 bool BrowserAccessibility::IsEditableText() const {
    315   // These roles don't have readonly set, but they're not editable text.
    316   if (role_ == AccessibilityNodeData::ROLE_SCROLLAREA ||
    317       role_ == AccessibilityNodeData::ROLE_COLUMN ||
    318       role_ == AccessibilityNodeData::ROLE_TABLE_HEADER_CONTAINER) {
    319     return false;
    320   }
    321 
    322   // Note: STATE_READONLY being false means it's either a text control,
    323   // or contenteditable. We also check for editable text roles to cover
    324   // another element that has role=textbox set on it.
    325   return (!HasState(AccessibilityNodeData::STATE_READONLY) ||
    326           role_ == AccessibilityNodeData::ROLE_TEXT_FIELD ||
    327           role_ == AccessibilityNodeData::ROLE_TEXTAREA);
    328 }
    329 
    330 string16 BrowserAccessibility::GetTextRecursive() const {
    331   if (!name_.empty()) {
    332     return name_;
    333   }
    334 
    335   string16 result;
    336   for (size_t i = 0; i < children_.size(); ++i)
    337     result += children_[i]->GetTextRecursive();
    338   return result;
    339 }
    340 
    341 }  // namespace content
    342