1 // Copyright (c) 2011 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 "chrome/browser/accessibility/browser_accessibility_manager.h" 6 7 #include "base/logging.h" 8 #include "chrome/browser/accessibility/browser_accessibility.h" 9 #include "content/common/view_messages.h" 10 11 using webkit_glue::WebAccessibility; 12 13 BrowserAccessibility* BrowserAccessibilityFactory::Create() { 14 return BrowserAccessibility::Create(); 15 } 16 17 // Start child IDs at -1 and decrement each time, because clients use 18 // child IDs of 1, 2, 3, ... to access the children of an object by 19 // index, so we use negative IDs to clearly distinguish between indices 20 // and unique IDs. 21 // static 22 int32 BrowserAccessibilityManager::next_child_id_ = -1; 23 24 #if defined(OS_LINUX) 25 // There's no OS-specific implementation of BrowserAccessibilityManager 26 // on Linux, so just instantiate the base class. 27 // static 28 BrowserAccessibilityManager* BrowserAccessibilityManager::Create( 29 gfx::NativeView parent_view, 30 const WebAccessibility& src, 31 BrowserAccessibilityDelegate* delegate, 32 BrowserAccessibilityFactory* factory) { 33 return new BrowserAccessibilityManager( 34 parent_view, src, delegate, factory); 35 } 36 #endif 37 38 BrowserAccessibilityManager::BrowserAccessibilityManager( 39 gfx::NativeView parent_view, 40 const WebAccessibility& src, 41 BrowserAccessibilityDelegate* delegate, 42 BrowserAccessibilityFactory* factory) 43 : parent_view_(parent_view), 44 delegate_(delegate), 45 factory_(factory), 46 focus_(NULL) { 47 root_ = CreateAccessibilityTree(NULL, src, 0); 48 if (!focus_) 49 SetFocus(root_, false); 50 } 51 52 // static 53 int32 BrowserAccessibilityManager::GetNextChildID() { 54 // Get the next child ID, and wrap around when we get near the end 55 // of a 32-bit integer range. It's okay to wrap around; we just want 56 // to avoid it as long as possible because clients may cache the ID of 57 // an object for a while to determine if they've seen it before. 58 next_child_id_--; 59 if (next_child_id_ == -2000000000) 60 next_child_id_ = -1; 61 62 return next_child_id_; 63 } 64 65 BrowserAccessibilityManager::~BrowserAccessibilityManager() { 66 // Clients could still hold references to some nodes of the tree, so 67 // calling InternalReleaseReference will make sure that as many nodes 68 // as possible are released now, and remaining nodes are marked as 69 // inactive so that calls to any methods on them will fail gracefully. 70 focus_->InternalReleaseReference(false); 71 root_->InternalReleaseReference(true); 72 } 73 74 BrowserAccessibility* BrowserAccessibilityManager::GetRoot() { 75 return root_; 76 } 77 78 BrowserAccessibility* BrowserAccessibilityManager::GetFromChildID( 79 int32 child_id) { 80 base::hash_map<int32, BrowserAccessibility*>::iterator iter = 81 child_id_map_.find(child_id); 82 if (iter != child_id_map_.end()) { 83 return iter->second; 84 } else { 85 return NULL; 86 } 87 } 88 89 void BrowserAccessibilityManager::Remove(int32 child_id, int32 renderer_id) { 90 child_id_map_.erase(child_id); 91 renderer_id_to_child_id_map_.erase(renderer_id); 92 } 93 94 void BrowserAccessibilityManager::OnAccessibilityNotifications( 95 const std::vector<ViewHostMsg_AccessibilityNotification_Params>& params) { 96 for (uint32 index = 0; index < params.size(); index++) { 97 const ViewHostMsg_AccessibilityNotification_Params& param = params[index]; 98 99 switch (param.notification_type) { 100 case ViewHostMsg_AccessibilityNotification_Type:: 101 NOTIFICATION_TYPE_CHECK_STATE_CHANGED: 102 OnAccessibilityObjectStateChange(param.acc_obj); 103 break; 104 case ViewHostMsg_AccessibilityNotification_Type:: 105 NOTIFICATION_TYPE_CHILDREN_CHANGED: 106 OnAccessibilityObjectChildrenChange(param.acc_obj); 107 break; 108 case ViewHostMsg_AccessibilityNotification_Type:: 109 NOTIFICATION_TYPE_FOCUS_CHANGED: 110 OnAccessibilityObjectFocusChange(param.acc_obj); 111 break; 112 case ViewHostMsg_AccessibilityNotification_Type:: 113 NOTIFICATION_TYPE_LOAD_COMPLETE: 114 OnAccessibilityObjectLoadComplete(param.acc_obj); 115 break; 116 case ViewHostMsg_AccessibilityNotification_Type:: 117 NOTIFICATION_TYPE_VALUE_CHANGED: 118 OnAccessibilityObjectValueChange(param.acc_obj); 119 break; 120 case ViewHostMsg_AccessibilityNotification_Type:: 121 NOTIFICATION_TYPE_SELECTED_TEXT_CHANGED: 122 OnAccessibilityObjectTextChange(param.acc_obj); 123 break; 124 default: 125 DCHECK(0); 126 break; 127 } 128 } 129 } 130 131 void BrowserAccessibilityManager::OnAccessibilityObjectStateChange( 132 const WebAccessibility& acc_obj) { 133 BrowserAccessibility* new_browser_acc = UpdateNode(acc_obj, false); 134 if (!new_browser_acc) 135 return; 136 137 NotifyAccessibilityEvent( 138 ViewHostMsg_AccessibilityNotification_Type:: 139 NOTIFICATION_TYPE_CHECK_STATE_CHANGED, 140 new_browser_acc); 141 } 142 143 void BrowserAccessibilityManager::OnAccessibilityObjectChildrenChange( 144 const WebAccessibility& acc_obj) { 145 BrowserAccessibility* new_browser_acc = UpdateNode(acc_obj, true); 146 if (!new_browser_acc) 147 return; 148 149 NotifyAccessibilityEvent( 150 ViewHostMsg_AccessibilityNotification_Type:: 151 NOTIFICATION_TYPE_CHILDREN_CHANGED, 152 new_browser_acc); 153 } 154 155 void BrowserAccessibilityManager::OnAccessibilityObjectFocusChange( 156 const WebAccessibility& acc_obj) { 157 BrowserAccessibility* new_browser_acc = UpdateNode(acc_obj, false); 158 if (!new_browser_acc) 159 return; 160 161 SetFocus(new_browser_acc, false); 162 if (delegate_ && delegate_->HasFocus()) { 163 GotFocus(); 164 } else if (!delegate_) { 165 // Mac currently does not have a BrowserAccessibilityDelegate. 166 NotifyAccessibilityEvent( 167 ViewHostMsg_AccessibilityNotification_Type:: 168 NOTIFICATION_TYPE_FOCUS_CHANGED, 169 focus_); 170 } 171 } 172 173 void BrowserAccessibilityManager::OnAccessibilityObjectLoadComplete( 174 const WebAccessibility& acc_obj) { 175 SetFocus(NULL, false); 176 root_->InternalReleaseReference(true); 177 178 root_ = CreateAccessibilityTree(NULL, acc_obj, 0); 179 if (!focus_) 180 SetFocus(root_, false); 181 182 NotifyAccessibilityEvent( 183 ViewHostMsg_AccessibilityNotification_Type:: 184 NOTIFICATION_TYPE_LOAD_COMPLETE, 185 root_); 186 if (delegate_ && delegate_->HasFocus()) 187 GotFocus(); 188 } 189 190 void BrowserAccessibilityManager::OnAccessibilityObjectValueChange( 191 const WebAccessibility& acc_obj) { 192 BrowserAccessibility* new_browser_acc = UpdateNode(acc_obj, false); 193 if (!new_browser_acc) 194 return; 195 196 NotifyAccessibilityEvent( 197 ViewHostMsg_AccessibilityNotification_Type:: 198 NOTIFICATION_TYPE_VALUE_CHANGED, 199 new_browser_acc); 200 } 201 202 void BrowserAccessibilityManager::OnAccessibilityObjectTextChange( 203 const WebAccessibility& acc_obj) { 204 BrowserAccessibility* new_browser_acc = UpdateNode(acc_obj, false); 205 if (!new_browser_acc) 206 return; 207 208 NotifyAccessibilityEvent( 209 ViewHostMsg_AccessibilityNotification_Type:: 210 NOTIFICATION_TYPE_SELECTED_TEXT_CHANGED, 211 new_browser_acc); 212 } 213 214 void BrowserAccessibilityManager::GotFocus() { 215 // TODO(ctguil): Remove when tree update logic handles focus changes. 216 if (!focus_) 217 return; 218 219 NotifyAccessibilityEvent( 220 ViewHostMsg_AccessibilityNotification_Type:: 221 NOTIFICATION_TYPE_FOCUS_CHANGED, 222 focus_); 223 } 224 225 gfx::NativeView BrowserAccessibilityManager::GetParentView() { 226 return parent_view_; 227 } 228 229 BrowserAccessibility* BrowserAccessibilityManager::GetFocus( 230 BrowserAccessibility* root) { 231 if (focus_ && (!root || focus_->IsDescendantOf(root))) 232 return focus_; 233 234 return NULL; 235 } 236 237 void BrowserAccessibilityManager::SetFocus( 238 BrowserAccessibility* node, bool notify) { 239 if (focus_) 240 focus_->InternalReleaseReference(false); 241 focus_ = node; 242 if (focus_) 243 focus_->InternalAddReference(); 244 245 if (notify && node && delegate_) 246 delegate_->SetAccessibilityFocus(node->renderer_id()); 247 } 248 249 void BrowserAccessibilityManager::DoDefaultAction( 250 const BrowserAccessibility& node) { 251 if (delegate_) 252 delegate_->AccessibilityDoDefaultAction(node.renderer_id()); 253 } 254 255 gfx::Rect BrowserAccessibilityManager::GetViewBounds() { 256 if (delegate_) 257 return delegate_->GetViewBounds(); 258 return gfx::Rect(); 259 } 260 261 BrowserAccessibility* BrowserAccessibilityManager::UpdateNode( 262 const WebAccessibility& src, 263 bool include_children) { 264 base::hash_map<int32, int32>::iterator iter = 265 renderer_id_to_child_id_map_.find(src.id); 266 if (iter == renderer_id_to_child_id_map_.end()) 267 return NULL; 268 269 int32 child_id = iter->second; 270 BrowserAccessibility* current = GetFromChildID(child_id); 271 if (!current) 272 return NULL; 273 274 if (!include_children) { 275 DCHECK_EQ(0U, src.children.size()); 276 current->Initialize( 277 this, 278 current->parent(), 279 current->child_id(), 280 current->index_in_parent(), 281 src); 282 return current; 283 } 284 285 // Detach all of the nodes in the old tree and get a single flat vector 286 // of all node pointers. 287 std::vector<BrowserAccessibility*> old_tree_nodes; 288 current->DetachTree(&old_tree_nodes); 289 290 // Build a new tree, reusing old nodes if possible. Each node that's 291 // reused will have its reference count incremented by one. 292 current = CreateAccessibilityTree(NULL, src, -1); 293 294 // Decrement the reference count of all nodes in the old tree, which will 295 // delete any nodes no longer needed. 296 for (int i = 0; i < static_cast<int>(old_tree_nodes.size()); i++) 297 old_tree_nodes[i]->InternalReleaseReference(false); 298 299 DCHECK(focus_); 300 if (!focus_->instance_active()) 301 SetFocus(root_, false); 302 303 return current; 304 } 305 306 BrowserAccessibility* BrowserAccessibilityManager::CreateAccessibilityTree( 307 BrowserAccessibility* parent, 308 const WebAccessibility& src, 309 int index_in_parent) { 310 BrowserAccessibility* instance = NULL; 311 int32 child_id = 0; 312 base::hash_map<int32, int32>::iterator iter = 313 renderer_id_to_child_id_map_.find(src.id); 314 315 // If a BrowserAccessibility instance for this ID already exists, add a 316 // new reference to it and retrieve its children vector. 317 if (iter != renderer_id_to_child_id_map_.end()) { 318 child_id = iter->second; 319 instance = GetFromChildID(child_id); 320 } 321 322 // If the node has changed roles, don't reuse a BrowserAccessibility 323 // object, that could confuse a screen reader. 324 if (instance && instance->role() != src.role) 325 instance = NULL; 326 327 if (instance) { 328 // If we're reusing a node, it should already be detached from a parent 329 // and any children. If not, that means we have a serious bug somewhere, 330 // like the same child is reachable from two places in the same tree. 331 DCHECK_EQ(static_cast<BrowserAccessibility*>(NULL), instance->parent()); 332 DCHECK_EQ(0U, instance->child_count()); 333 334 // If we're reusing a node, update its parent and increment its 335 // reference count. 336 instance->UpdateParent(parent, index_in_parent); 337 instance->InternalAddReference(); 338 } else { 339 // Otherwise, create a new instance. 340 instance = factory_->Create(); 341 child_id = GetNextChildID(); 342 } 343 344 instance->Initialize(this, parent, child_id, index_in_parent, src); 345 child_id_map_[child_id] = instance; 346 renderer_id_to_child_id_map_[src.id] = child_id; 347 if ((src.state >> WebAccessibility::STATE_FOCUSED) & 1) 348 SetFocus(instance, false); 349 for (int i = 0; i < static_cast<int>(src.children.size()); ++i) { 350 BrowserAccessibility* child = CreateAccessibilityTree( 351 instance, src.children[i], i); 352 instance->AddChild(child); 353 } 354 355 return instance; 356 } 357