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_manager.h" 6 7 #include "base/logging.h" 8 #include "content/browser/accessibility/browser_accessibility.h" 9 #include "content/common/accessibility_messages.h" 10 11 namespace content { 12 13 BrowserAccessibility* BrowserAccessibilityFactory::Create() { 14 return BrowserAccessibility::Create(); 15 } 16 17 #if !defined(OS_MACOSX) && \ 18 !defined(OS_WIN) && \ 19 !defined(TOOLKIT_GTK) && \ 20 !defined(OS_ANDROID) \ 21 // We have subclassess of BrowserAccessibilityManager on Mac, Linux/GTK, 22 // and Win. For any other platform, instantiate the base class. 23 // static 24 BrowserAccessibilityManager* BrowserAccessibilityManager::Create( 25 const AccessibilityNodeData& src, 26 BrowserAccessibilityDelegate* delegate, 27 BrowserAccessibilityFactory* factory) { 28 return new BrowserAccessibilityManager(src, delegate, factory); 29 } 30 #endif 31 32 BrowserAccessibilityManager::BrowserAccessibilityManager( 33 BrowserAccessibilityDelegate* delegate, 34 BrowserAccessibilityFactory* factory) 35 : delegate_(delegate), 36 factory_(factory), 37 root_(NULL), 38 focus_(NULL), 39 osk_state_(OSK_ALLOWED) { 40 } 41 42 BrowserAccessibilityManager::BrowserAccessibilityManager( 43 const AccessibilityNodeData& src, 44 BrowserAccessibilityDelegate* delegate, 45 BrowserAccessibilityFactory* factory) 46 : delegate_(delegate), 47 factory_(factory), 48 root_(NULL), 49 focus_(NULL), 50 osk_state_(OSK_ALLOWED) { 51 Initialize(src); 52 } 53 54 BrowserAccessibilityManager::~BrowserAccessibilityManager() { 55 if (root_) 56 root_->Destroy(); 57 } 58 59 void BrowserAccessibilityManager::Initialize(const AccessibilityNodeData src) { 60 std::vector<AccessibilityNodeData> nodes; 61 nodes.push_back(src); 62 if (!UpdateNodes(nodes)) 63 return; 64 if (!focus_) 65 SetFocus(root_, false); 66 } 67 68 // static 69 AccessibilityNodeData BrowserAccessibilityManager::GetEmptyDocument() { 70 AccessibilityNodeData empty_document; 71 empty_document.id = 0; 72 empty_document.role = blink::WebAXRoleRootWebArea; 73 return empty_document; 74 } 75 76 BrowserAccessibility* BrowserAccessibilityManager::GetRoot() { 77 return root_; 78 } 79 80 BrowserAccessibility* BrowserAccessibilityManager::GetFromRendererID( 81 int32 renderer_id) { 82 base::hash_map<int32, BrowserAccessibility*>::iterator iter = 83 renderer_id_map_.find(renderer_id); 84 if (iter != renderer_id_map_.end()) 85 return iter->second; 86 return NULL; 87 } 88 89 void BrowserAccessibilityManager::GotFocus(bool touch_event_context) { 90 if (!touch_event_context) 91 osk_state_ = OSK_DISALLOWED_BECAUSE_TAB_JUST_APPEARED; 92 93 if (!focus_) 94 return; 95 96 NotifyAccessibilityEvent(blink::WebAXEventFocus, focus_); 97 } 98 99 void BrowserAccessibilityManager::WasHidden() { 100 osk_state_ = OSK_DISALLOWED_BECAUSE_TAB_HIDDEN; 101 } 102 103 void BrowserAccessibilityManager::GotMouseDown() { 104 osk_state_ = OSK_ALLOWED_WITHIN_FOCUSED_OBJECT; 105 NotifyAccessibilityEvent(blink::WebAXEventFocus, focus_); 106 } 107 108 bool BrowserAccessibilityManager::IsOSKAllowed(const gfx::Rect& bounds) { 109 if (!delegate_ || !delegate_->HasFocus()) 110 return false; 111 112 gfx::Point touch_point = delegate_->GetLastTouchEventLocation(); 113 return bounds.Contains(touch_point); 114 } 115 116 bool BrowserAccessibilityManager::UseRootScrollOffsetsWhenComputingBounds() { 117 return true; 118 } 119 120 void BrowserAccessibilityManager::RemoveNode(BrowserAccessibility* node) { 121 if (node == focus_) 122 SetFocus(root_, false); 123 int renderer_id = node->renderer_id(); 124 renderer_id_map_.erase(renderer_id); 125 } 126 127 void BrowserAccessibilityManager::OnAccessibilityEvents( 128 const std::vector<AccessibilityHostMsg_EventParams>& params) { 129 bool should_send_initial_focus = false; 130 131 // Process all changes to the accessibility tree first. 132 for (uint32 index = 0; index < params.size(); index++) { 133 const AccessibilityHostMsg_EventParams& param = params[index]; 134 if (!UpdateNodes(param.nodes)) 135 return; 136 137 // Set initial focus when a page is loaded. 138 blink::WebAXEvent event_type = param.event_type; 139 if (event_type == blink::WebAXEventLoadComplete) { 140 if (!focus_) { 141 SetFocus(root_, false); 142 should_send_initial_focus = true; 143 } 144 } 145 } 146 147 if (should_send_initial_focus && 148 (!delegate_ || delegate_->HasFocus())) { 149 NotifyAccessibilityEvent(blink::WebAXEventFocus, focus_); 150 } 151 152 // Now iterate over the events again and fire the events. 153 for (uint32 index = 0; index < params.size(); index++) { 154 const AccessibilityHostMsg_EventParams& param = params[index]; 155 156 // Find the node corresponding to the id that's the target of the 157 // event (which may not be the root of the update tree). 158 BrowserAccessibility* node = GetFromRendererID(param.id); 159 if (!node) 160 continue; 161 162 blink::WebAXEvent event_type = param.event_type; 163 if (event_type == blink::WebAXEventFocus || 164 event_type == blink::WebAXEventBlur) { 165 SetFocus(node, false); 166 167 if (osk_state_ != OSK_DISALLOWED_BECAUSE_TAB_HIDDEN && 168 osk_state_ != OSK_DISALLOWED_BECAUSE_TAB_JUST_APPEARED) 169 osk_state_ = OSK_ALLOWED; 170 171 // Don't send a native focus event if the window itself doesn't 172 // have focus. 173 if (delegate_ && !delegate_->HasFocus()) 174 continue; 175 } 176 177 // Send the event event to the operating system. 178 NotifyAccessibilityEvent(event_type, node); 179 } 180 } 181 182 BrowserAccessibility* BrowserAccessibilityManager::GetFocus( 183 BrowserAccessibility* root) { 184 if (focus_ && (!root || focus_->IsDescendantOf(root))) 185 return focus_; 186 187 return NULL; 188 } 189 190 void BrowserAccessibilityManager::SetFocus( 191 BrowserAccessibility* node, bool notify) { 192 if (focus_ != node) 193 focus_ = node; 194 195 if (notify && node && delegate_) 196 delegate_->SetAccessibilityFocus(node->renderer_id()); 197 } 198 199 void BrowserAccessibilityManager::SetRoot(BrowserAccessibility* node) { 200 root_ = node; 201 NotifyRootChanged(); 202 } 203 204 void BrowserAccessibilityManager::DoDefaultAction( 205 const BrowserAccessibility& node) { 206 if (delegate_) 207 delegate_->AccessibilityDoDefaultAction(node.renderer_id()); 208 } 209 210 void BrowserAccessibilityManager::ScrollToMakeVisible( 211 const BrowserAccessibility& node, gfx::Rect subfocus) { 212 if (delegate_) { 213 delegate_->AccessibilityScrollToMakeVisible(node.renderer_id(), subfocus); 214 } 215 } 216 217 void BrowserAccessibilityManager::ScrollToPoint( 218 const BrowserAccessibility& node, gfx::Point point) { 219 if (delegate_) { 220 delegate_->AccessibilityScrollToPoint(node.renderer_id(), point); 221 } 222 } 223 224 void BrowserAccessibilityManager::SetTextSelection( 225 const BrowserAccessibility& node, int start_offset, int end_offset) { 226 if (delegate_) { 227 delegate_->AccessibilitySetTextSelection( 228 node.renderer_id(), start_offset, end_offset); 229 } 230 } 231 232 gfx::Rect BrowserAccessibilityManager::GetViewBounds() { 233 if (delegate_) 234 return delegate_->GetViewBounds(); 235 return gfx::Rect(); 236 } 237 238 void BrowserAccessibilityManager::UpdateNodesForTesting( 239 const AccessibilityNodeData& node1, 240 const AccessibilityNodeData& node2 /* = AccessibilityNodeData() */, 241 const AccessibilityNodeData& node3 /* = AccessibilityNodeData() */, 242 const AccessibilityNodeData& node4 /* = AccessibilityNodeData() */, 243 const AccessibilityNodeData& node5 /* = AccessibilityNodeData() */, 244 const AccessibilityNodeData& node6 /* = AccessibilityNodeData() */, 245 const AccessibilityNodeData& node7 /* = AccessibilityNodeData() */) { 246 std::vector<AccessibilityNodeData> nodes; 247 nodes.push_back(node1); 248 if (node2.id != AccessibilityNodeData().id) 249 nodes.push_back(node2); 250 if (node3.id != AccessibilityNodeData().id) 251 nodes.push_back(node3); 252 if (node4.id != AccessibilityNodeData().id) 253 nodes.push_back(node4); 254 if (node5.id != AccessibilityNodeData().id) 255 nodes.push_back(node5); 256 if (node6.id != AccessibilityNodeData().id) 257 nodes.push_back(node6); 258 if (node7.id != AccessibilityNodeData().id) 259 nodes.push_back(node7); 260 UpdateNodes(nodes); 261 } 262 263 bool BrowserAccessibilityManager::UpdateNodes( 264 const std::vector<AccessibilityNodeData>& nodes) { 265 bool success = true; 266 267 // First, update all of the nodes in the tree. 268 for (size_t i = 0; i < nodes.size() && success; i++) { 269 if (!UpdateNode(nodes[i])) 270 success = false; 271 } 272 273 // In a second pass, call PostInitialize on each one - this must 274 // be called after all of each node's children are initialized too. 275 for (size_t i = 0; i < nodes.size() && success; i++) { 276 // Note: it's not a bug for nodes[i].id to not be found in the tree. 277 // Consider this example: 278 // Before: 279 // A 280 // B 281 // C 282 // D 283 // E 284 // F 285 // After: 286 // A 287 // B 288 // C 289 // F 290 // D 291 // In this example, F is being reparented. The renderer scans the tree 292 // in order. If can't update "C" to add "F" as a child, when "F" is still 293 // a child of "E". So it first updates "E", to remove "F" as a child. 294 // Later, it ends up deleting "E". So when we get here, "E" was updated as 295 // part of this sequence but it no longer exists in the final tree, so 296 // there's nothing to postinitialize. 297 BrowserAccessibility* instance = GetFromRendererID(nodes[i].id); 298 if (instance) 299 instance->PostInitialize(); 300 } 301 302 if (!success) { 303 // A bad accessibility tree could lead to memory corruption. 304 // Ask the delegate to crash the renderer, or if not available, 305 // crash the browser. 306 if (delegate_) 307 delegate_->FatalAccessibilityTreeError(); 308 else 309 CHECK(false); 310 } 311 312 return success; 313 } 314 315 BrowserAccessibility* BrowserAccessibilityManager::CreateNode( 316 BrowserAccessibility* parent, 317 int32 renderer_id, 318 int32 index_in_parent) { 319 BrowserAccessibility* node = factory_->Create(); 320 node->InitializeTreeStructure( 321 this, parent, renderer_id, index_in_parent); 322 AddNodeToMap(node); 323 return node; 324 } 325 326 void BrowserAccessibilityManager::AddNodeToMap(BrowserAccessibility* node) { 327 renderer_id_map_[node->renderer_id()] = node; 328 } 329 330 bool BrowserAccessibilityManager::UpdateNode(const AccessibilityNodeData& src) { 331 // This method updates one node in the tree based on serialized data 332 // received from the renderer. 333 334 // Create a set of child ids in |src| for fast lookup. If a duplicate id is 335 // found, exit now with a fatal error before changing anything else. 336 std::set<int32> new_child_ids; 337 for (size_t i = 0; i < src.child_ids.size(); ++i) { 338 if (new_child_ids.find(src.child_ids[i]) != new_child_ids.end()) 339 return false; 340 new_child_ids.insert(src.child_ids[i]); 341 } 342 343 // Look up the node by id. If it's not found, then either the root 344 // of the tree is being swapped, or we're out of sync with the renderer 345 // and this is a serious error. 346 BrowserAccessibility* instance = GetFromRendererID(src.id); 347 if (!instance) { 348 if (src.role != blink::WebAXRoleRootWebArea) 349 return false; 350 instance = CreateNode(NULL, src.id, 0); 351 } 352 353 // TODO(dmazzoni): avoid a linear scan here. 354 for (size_t i = 0; i < src.bool_attributes.size(); i++) { 355 if (src.bool_attributes[i].first == 356 AccessibilityNodeData::ATTR_UPDATE_LOCATION_ONLY) { 357 instance->SetLocation(src.location); 358 return true; 359 } 360 } 361 362 // Update all of the node-specific data, like its role, state, name, etc. 363 instance->InitializeData(src); 364 365 // 366 // Update the children in three steps: 367 // 368 // 1. Iterate over the old children and delete nodes that are no longer 369 // in the tree. 370 // 2. Build up a vector of new children, reusing children that haven't 371 // changed (but may have been reordered) and adding new empty 372 // objects for new children. 373 // 3. Swap in the new children vector for the old one. 374 375 // Delete any previous children of this instance that are no longer 376 // children first. We make a deletion-only pass first to prevent a 377 // node that's being reparented from being the child of both its old 378 // parent and new parent, which could lead to a double-free. 379 // If a node is reparented, the renderer will always send us a fresh 380 // copy of the node. 381 const std::vector<BrowserAccessibility*>& old_children = instance->children(); 382 for (size_t i = 0; i < old_children.size(); ++i) { 383 int old_id = old_children[i]->renderer_id(); 384 if (new_child_ids.find(old_id) == new_child_ids.end()) 385 old_children[i]->Destroy(); 386 } 387 388 // Now build a vector of new children, reusing objects that were already 389 // children of this node before. 390 std::vector<BrowserAccessibility*> new_children; 391 bool success = true; 392 for (size_t i = 0; i < src.child_ids.size(); i++) { 393 int32 child_renderer_id = src.child_ids[i]; 394 int32 index_in_parent = static_cast<int32>(i); 395 BrowserAccessibility* child = GetFromRendererID(child_renderer_id); 396 if (child) { 397 if (child->parent() != instance) { 398 // This is a serious error - nodes should never be reparented. 399 // If this case occurs, continue so this node isn't left in an 400 // inconsistent state, but return failure at the end. 401 success = false; 402 continue; 403 } 404 child->UpdateParent(instance, index_in_parent); 405 } else { 406 child = CreateNode(instance, child_renderer_id, index_in_parent); 407 } 408 new_children.push_back(child); 409 } 410 411 // Finally, swap in the new children vector for the old. 412 instance->SwapChildren(new_children); 413 414 // Handle the case where this node is the new root of the tree. 415 if (src.role == blink::WebAXRoleRootWebArea && 416 (!root_ || root_->renderer_id() != src.id)) { 417 if (root_) 418 root_->Destroy(); 419 if (focus_ == root_) 420 SetFocus(instance, false); 421 SetRoot(instance); 422 } 423 424 // Keep track of what node is focused. 425 if (src.role != blink::WebAXRoleRootWebArea && 426 src.role != blink::WebAXRoleWebArea && 427 (src.state >> blink::WebAXStateFocused & 1)) { 428 SetFocus(instance, false); 429 } 430 return success; 431 } 432 433 } // namespace content 434