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