browser_accessibility_manager.cc revision 731df977c0511bca2206b5f333555b1205ff1f43
1// Copyright (c) 2010 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 "chrome/browser/accessibility/browser_accessibility.h" 8 9using webkit_glue::WebAccessibility; 10 11// Start child IDs at -1 and decrement each time, because clients use 12// child IDs of 1, 2, 3, ... to access the children of an object by 13// index, so we use negative IDs to clearly distinguish between indices 14// and unique IDs. 15// static 16int32 BrowserAccessibilityManager::next_child_id_ = -1; 17 18BrowserAccessibilityManager::BrowserAccessibilityManager( 19 gfx::NativeView parent_view, 20 const WebAccessibility& src, 21 BrowserAccessibilityDelegate* delegate, 22 BrowserAccessibilityFactory* factory) 23 : parent_view_(parent_view), 24 delegate_(delegate), 25 factory_(factory), 26 focus_(NULL) { 27 root_ = CreateAccessibilityTree(NULL, GetNextChildID(), src, 0); 28 if (!focus_) 29 focus_ = root_; 30} 31 32// static 33int32 BrowserAccessibilityManager::GetNextChildID() { 34 // Get the next child ID, and wrap around when we get near the end 35 // of a 32-bit integer range. It's okay to wrap around; we just want 36 // to avoid it as long as possible because clients may cache the ID of 37 // an object for a while to determine if they've seen it before. 38 next_child_id_--; 39 if (next_child_id_ == -2000000000) 40 next_child_id_ = -1; 41 42 return next_child_id_; 43} 44 45BrowserAccessibilityManager::~BrowserAccessibilityManager() { 46 // Clients could still hold references to some nodes of the tree, so 47 // calling Inactivate will make sure that as many nodes as possible are 48 // released now, and remaining nodes are marked as inactive so that 49 // calls to any methods on them will return E_FAIL; 50 root_->ReleaseTree(); 51 root_->ReleaseReference(); 52} 53 54BrowserAccessibility* BrowserAccessibilityManager::GetRoot() { 55 return root_; 56} 57 58BrowserAccessibility* BrowserAccessibilityManager::GetFromChildID( 59 int32 child_id) { 60 base::hash_map<int32, BrowserAccessibility*>::iterator iter = 61 child_id_map_.find(child_id); 62 if (iter != child_id_map_.end()) { 63 return iter->second; 64 } else { 65 return NULL; 66 } 67} 68 69void BrowserAccessibilityManager::Remove(int32 child_id) { 70 child_id_map_.erase(child_id); 71} 72 73void BrowserAccessibilityManager::OnAccessibilityNotifications( 74 const std::vector<ViewHostMsg_AccessibilityNotification_Params>& params) { 75 for (uint32 index = 0; index < params.size(); index++) { 76 const ViewHostMsg_AccessibilityNotification_Params& param = params[index]; 77 78 switch (param.notification_type) { 79 case ViewHostMsg_AccessibilityNotification_Params:: 80 NOTIFICATION_TYPE_CHECK_STATE_CHANGED: 81 OnAccessibilityObjectStateChange(param.acc_obj); 82 break; 83 case ViewHostMsg_AccessibilityNotification_Params:: 84 NOTIFICATION_TYPE_CHILDREN_CHANGED: 85 OnAccessibilityObjectChildrenChange(param.acc_obj); 86 break; 87 case ViewHostMsg_AccessibilityNotification_Params:: 88 NOTIFICATION_TYPE_FOCUS_CHANGED: 89 OnAccessibilityObjectFocusChange(param.acc_obj); 90 break; 91 case ViewHostMsg_AccessibilityNotification_Params:: 92 NOTIFICATION_TYPE_LOAD_COMPLETE: 93 OnAccessibilityObjectLoadComplete(param.acc_obj); 94 break; 95 case ViewHostMsg_AccessibilityNotification_Params:: 96 NOTIFICATION_TYPE_VALUE_CHANGED: 97 OnAccessibilityObjectValueChange(param.acc_obj); 98 break; 99 case ViewHostMsg_AccessibilityNotification_Params:: 100 NOTIFICATION_TYPE_SELECTED_TEXT_CHANGED: 101 OnAccessibilityObjectTextChange(param.acc_obj); 102 break; 103 default: 104 DCHECK(0); 105 break; 106 } 107 } 108} 109 110void BrowserAccessibilityManager::OnAccessibilityObjectStateChange( 111 const WebAccessibility& acc_obj) { 112 BrowserAccessibility* new_browser_acc = UpdateTree(acc_obj); 113 if (!new_browser_acc) 114 return; 115 116 NotifyAccessibilityEvent( 117 ViewHostMsg_AccessibilityNotification_Params:: 118 NOTIFICATION_TYPE_CHECK_STATE_CHANGED, 119 new_browser_acc); 120} 121 122void BrowserAccessibilityManager::OnAccessibilityObjectChildrenChange( 123 const WebAccessibility& acc_obj) { 124 BrowserAccessibility* new_browser_acc = UpdateTree(acc_obj); 125 if (!new_browser_acc) 126 return; 127 128 NotifyAccessibilityEvent( 129 ViewHostMsg_AccessibilityNotification_Params:: 130 NOTIFICATION_TYPE_CHILDREN_CHANGED, 131 new_browser_acc); 132} 133 134void BrowserAccessibilityManager::OnAccessibilityObjectFocusChange( 135 const WebAccessibility& acc_obj) { 136 BrowserAccessibility* new_browser_acc = UpdateTree(acc_obj); 137 if (!new_browser_acc) 138 return; 139 140 focus_ = new_browser_acc; 141 if (delegate_ && delegate_->HasFocus()) 142 GotFocus(); 143 // Mac currently does not have a BrowserAccessibilityDelegate. 144 else if (!delegate_) { 145 NotifyAccessibilityEvent( 146 ViewHostMsg_AccessibilityNotification_Params:: 147 NOTIFICATION_TYPE_FOCUS_CHANGED, 148 focus_); 149 } 150} 151 152void BrowserAccessibilityManager::OnAccessibilityObjectLoadComplete( 153 const WebAccessibility& acc_obj) { 154 root_->ReleaseTree(); 155 root_->ReleaseReference(); 156 focus_ = NULL; 157 158 root_ = CreateAccessibilityTree(NULL, GetNextChildID(), acc_obj, 0); 159 if (!focus_) 160 focus_ = root_; 161 162 NotifyAccessibilityEvent( 163 ViewHostMsg_AccessibilityNotification_Params:: 164 NOTIFICATION_TYPE_LOAD_COMPLETE, 165 root_); 166 if (delegate_ && delegate_->HasFocus()) 167 GotFocus(); 168} 169 170void BrowserAccessibilityManager::OnAccessibilityObjectValueChange( 171 const WebAccessibility& acc_obj) { 172 BrowserAccessibility* new_browser_acc = UpdateTree(acc_obj); 173 if (!new_browser_acc) 174 return; 175 176 NotifyAccessibilityEvent( 177 ViewHostMsg_AccessibilityNotification_Params:: 178 NOTIFICATION_TYPE_VALUE_CHANGED, 179 root_); 180} 181 182void BrowserAccessibilityManager::OnAccessibilityObjectTextChange( 183 const WebAccessibility& acc_obj) { 184 BrowserAccessibility* new_browser_acc = UpdateTree(acc_obj); 185 if (!new_browser_acc) 186 return; 187 188 NotifyAccessibilityEvent( 189 ViewHostMsg_AccessibilityNotification_Params:: 190 NOTIFICATION_TYPE_SELECTED_TEXT_CHANGED, 191 root_); 192} 193 194void BrowserAccessibilityManager::GotFocus() { 195 // TODO(ctguil): Remove when tree update logic handles focus changes. 196 if (!focus_) 197 return; 198 199 NotifyAccessibilityEvent( 200 ViewHostMsg_AccessibilityNotification_Params:: 201 NOTIFICATION_TYPE_FOCUS_CHANGED, 202 focus_); 203} 204 205gfx::NativeView BrowserAccessibilityManager::GetParentView() { 206 return parent_view_; 207} 208 209BrowserAccessibility* BrowserAccessibilityManager::GetFocus( 210 BrowserAccessibility* root) { 211 if (focus_ && (!root || focus_->IsDescendantOf(root))) 212 return focus_; 213 214 return NULL; 215} 216 217void BrowserAccessibilityManager::SetFocus( 218 const BrowserAccessibility& node) { 219 if (delegate_) 220 delegate_->SetAccessibilityFocus(node.renderer_id()); 221} 222 223void BrowserAccessibilityManager::DoDefaultAction( 224 const BrowserAccessibility& node) { 225 if (delegate_) 226 delegate_->AccessibilityDoDefaultAction(node.renderer_id()); 227} 228 229bool BrowserAccessibilityManager::CanModifyTreeInPlace( 230 BrowserAccessibility* current_root, 231 const WebAccessibility& new_root) { 232 if (current_root->renderer_id() != new_root.id) 233 return false; 234 if (current_root->GetChildCount() != new_root.children.size()) 235 return false; 236 for (unsigned int i = 0; i < current_root->GetChildCount(); i++) { 237 if (!CanModifyTreeInPlace(current_root->GetChild(i), 238 new_root.children[i])) { 239 return false; 240 } 241 } 242 return true; 243} 244 245void BrowserAccessibilityManager::ModifyTreeInPlace( 246 BrowserAccessibility* current_root, 247 const WebAccessibility& new_root) { 248 DCHECK_EQ(current_root->renderer_id(), new_root.id); 249 DCHECK_EQ(current_root->GetChildCount(), new_root.children.size()); 250 for (unsigned int i = 0; i < current_root->GetChildCount(); i++) 251 ModifyTreeInPlace(current_root->GetChild(i), new_root.children[i]); 252 current_root->Initialize( 253 this, 254 current_root->GetParent(), 255 current_root->child_id(), 256 current_root->index_in_parent(), 257 new_root); 258} 259 260 261BrowserAccessibility* BrowserAccessibilityManager::UpdateTree( 262 const WebAccessibility& acc_obj) { 263 base::hash_map<int, int32>::iterator iter = 264 renderer_id_to_child_id_map_.find(acc_obj.id); 265 if (iter == renderer_id_to_child_id_map_.end()) 266 return NULL; 267 268 int32 child_id = iter->second; 269 BrowserAccessibility* old_browser_acc = GetFromChildID(child_id); 270 if (!old_browser_acc) 271 return NULL; 272 273 if (CanModifyTreeInPlace(old_browser_acc, acc_obj)) { 274 ModifyTreeInPlace(old_browser_acc, acc_obj); 275 return old_browser_acc; 276 } 277 278 BrowserAccessibility* new_browser_acc = CreateAccessibilityTree( 279 old_browser_acc->GetParent(), 280 child_id, 281 acc_obj, 282 old_browser_acc->index_in_parent()); 283 284 if (old_browser_acc->GetParent()) { 285 old_browser_acc->GetParent()->ReplaceChild( 286 old_browser_acc, 287 new_browser_acc); 288 } else { 289 DCHECK_EQ(old_browser_acc, root_); 290 root_ = new_browser_acc; 291 } 292 old_browser_acc->ReleaseTree(); 293 old_browser_acc->ReleaseReference(); 294 child_id_map_[child_id] = new_browser_acc; 295 296 return new_browser_acc; 297} 298 299BrowserAccessibility* BrowserAccessibilityManager::CreateAccessibilityTree( 300 BrowserAccessibility* parent, 301 int child_id, 302 const WebAccessibility& src, 303 int index_in_parent) { 304 BrowserAccessibility* instance = factory_->Create(); 305 306 instance->Initialize(this, parent, child_id, index_in_parent, src); 307 child_id_map_[child_id] = instance; 308 renderer_id_to_child_id_map_[src.id] = child_id; 309 if ((src.state >> WebAccessibility::STATE_FOCUSED) & 1) 310 focus_ = instance; 311 for (int i = 0; i < static_cast<int>(src.children.size()); ++i) { 312 BrowserAccessibility* child = CreateAccessibilityTree( 313 instance, GetNextChildID(), src.children[i], i); 314 instance->AddChild(child); 315 } 316 317 return instance; 318} 319