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 #include "ui/accessibility/ax_tree_serializer.h" 11 12 namespace content { 13 14 ui::AXTreeUpdate MakeAXTreeUpdate( 15 const ui::AXNodeData& node1, 16 const ui::AXNodeData& node2 /* = ui::AXNodeData() */, 17 const ui::AXNodeData& node3 /* = ui::AXNodeData() */, 18 const ui::AXNodeData& node4 /* = ui::AXNodeData() */, 19 const ui::AXNodeData& node5 /* = ui::AXNodeData() */, 20 const ui::AXNodeData& node6 /* = ui::AXNodeData() */, 21 const ui::AXNodeData& node7 /* = ui::AXNodeData() */, 22 const ui::AXNodeData& node8 /* = ui::AXNodeData() */, 23 const ui::AXNodeData& node9 /* = ui::AXNodeData() */) { 24 CR_DEFINE_STATIC_LOCAL(ui::AXNodeData, empty_data, ()); 25 int32 no_id = empty_data.id; 26 27 ui::AXTreeUpdate update; 28 update.nodes.push_back(node1); 29 if (node2.id != no_id) 30 update.nodes.push_back(node2); 31 if (node3.id != no_id) 32 update.nodes.push_back(node3); 33 if (node4.id != no_id) 34 update.nodes.push_back(node4); 35 if (node5.id != no_id) 36 update.nodes.push_back(node5); 37 if (node6.id != no_id) 38 update.nodes.push_back(node6); 39 if (node7.id != no_id) 40 update.nodes.push_back(node7); 41 if (node8.id != no_id) 42 update.nodes.push_back(node8); 43 if (node9.id != no_id) 44 update.nodes.push_back(node9); 45 return update; 46 } 47 48 BrowserAccessibility* BrowserAccessibilityFactory::Create() { 49 return BrowserAccessibility::Create(); 50 } 51 52 #if !defined(OS_MACOSX) && \ 53 !defined(OS_WIN) && \ 54 !defined(OS_ANDROID) \ 55 // We have subclassess of BrowserAccessibilityManager on Mac, and Win. For any 56 // other platform, instantiate the base class. 57 // static 58 BrowserAccessibilityManager* BrowserAccessibilityManager::Create( 59 const ui::AXTreeUpdate& initial_tree, 60 BrowserAccessibilityDelegate* delegate, 61 BrowserAccessibilityFactory* factory) { 62 return new BrowserAccessibilityManager(initial_tree, delegate, factory); 63 } 64 #endif 65 66 BrowserAccessibilityManager::BrowserAccessibilityManager( 67 BrowserAccessibilityDelegate* delegate, 68 BrowserAccessibilityFactory* factory) 69 : delegate_(delegate), 70 factory_(factory), 71 tree_(new ui::AXSerializableTree()), 72 focus_(NULL), 73 osk_state_(OSK_ALLOWED) { 74 tree_->SetDelegate(this); 75 } 76 77 BrowserAccessibilityManager::BrowserAccessibilityManager( 78 const ui::AXTreeUpdate& initial_tree, 79 BrowserAccessibilityDelegate* delegate, 80 BrowserAccessibilityFactory* factory) 81 : delegate_(delegate), 82 factory_(factory), 83 tree_(new ui::AXSerializableTree()), 84 focus_(NULL), 85 osk_state_(OSK_ALLOWED) { 86 tree_->SetDelegate(this); 87 Initialize(initial_tree); 88 } 89 90 BrowserAccessibilityManager::~BrowserAccessibilityManager() { 91 tree_.reset(NULL); 92 } 93 94 void BrowserAccessibilityManager::Initialize( 95 const ui::AXTreeUpdate& initial_tree) { 96 if (!tree_->Unserialize(initial_tree)) { 97 if (delegate_) { 98 LOG(ERROR) << tree_->error(); 99 delegate_->AccessibilityFatalError(); 100 } else { 101 LOG(FATAL) << tree_->error(); 102 } 103 } 104 105 if (!focus_) 106 SetFocus(tree_->GetRoot(), false); 107 } 108 109 // static 110 ui::AXTreeUpdate BrowserAccessibilityManager::GetEmptyDocument() { 111 ui::AXNodeData empty_document; 112 empty_document.id = 0; 113 empty_document.role = ui::AX_ROLE_ROOT_WEB_AREA; 114 ui::AXTreeUpdate update; 115 update.nodes.push_back(empty_document); 116 return update; 117 } 118 119 BrowserAccessibility* BrowserAccessibilityManager::GetRoot() { 120 return GetFromAXNode(tree_->GetRoot()); 121 } 122 123 BrowserAccessibility* BrowserAccessibilityManager::GetFromAXNode( 124 ui::AXNode* node) { 125 return GetFromID(node->id()); 126 } 127 128 BrowserAccessibility* BrowserAccessibilityManager::GetFromID(int32 id) { 129 base::hash_map<int32, BrowserAccessibility*>::iterator iter = 130 id_wrapper_map_.find(id); 131 if (iter != id_wrapper_map_.end()) 132 return iter->second; 133 return NULL; 134 } 135 136 void BrowserAccessibilityManager::OnWindowFocused() { 137 if (focus_) 138 NotifyAccessibilityEvent(ui::AX_EVENT_FOCUS, GetFromAXNode(focus_)); 139 } 140 141 void BrowserAccessibilityManager::OnWindowBlurred() { 142 if (focus_) 143 NotifyAccessibilityEvent(ui::AX_EVENT_BLUR, GetFromAXNode(focus_)); 144 } 145 146 void BrowserAccessibilityManager::GotMouseDown() { 147 osk_state_ = OSK_ALLOWED_WITHIN_FOCUSED_OBJECT; 148 NotifyAccessibilityEvent(ui::AX_EVENT_FOCUS, GetFromAXNode(focus_)); 149 } 150 151 bool BrowserAccessibilityManager::UseRootScrollOffsetsWhenComputingBounds() { 152 return true; 153 } 154 155 void BrowserAccessibilityManager::OnAccessibilityEvents( 156 const std::vector<AccessibilityHostMsg_EventParams>& params) { 157 bool should_send_initial_focus = false; 158 159 // Process all changes to the accessibility tree first. 160 for (uint32 index = 0; index < params.size(); index++) { 161 const AccessibilityHostMsg_EventParams& param = params[index]; 162 if (!tree_->Unserialize(param.update)) { 163 if (delegate_) { 164 LOG(ERROR) << tree_->error(); 165 delegate_->AccessibilityFatalError(); 166 } else { 167 CHECK(false) << tree_->error(); 168 } 169 return; 170 } 171 172 // Set focus to the root if it's not anywhere else. 173 if (!focus_) { 174 SetFocus(tree_->GetRoot(), false); 175 should_send_initial_focus = true; 176 } 177 } 178 179 OnTreeUpdateFinished(); 180 181 if (should_send_initial_focus && 182 (!delegate_ || delegate_->AccessibilityViewHasFocus())) { 183 NotifyAccessibilityEvent(ui::AX_EVENT_FOCUS, GetFromAXNode(focus_)); 184 } 185 186 // Now iterate over the events again and fire the events. 187 for (uint32 index = 0; index < params.size(); index++) { 188 const AccessibilityHostMsg_EventParams& param = params[index]; 189 190 // Find the node corresponding to the id that's the target of the 191 // event (which may not be the root of the update tree). 192 ui::AXNode* node = tree_->GetFromId(param.id); 193 if (!node) 194 continue; 195 196 ui::AXEvent event_type = param.event_type; 197 if (event_type == ui::AX_EVENT_FOCUS || 198 event_type == ui::AX_EVENT_BLUR) { 199 SetFocus(node, false); 200 201 if (osk_state_ != OSK_DISALLOWED_BECAUSE_TAB_HIDDEN && 202 osk_state_ != OSK_DISALLOWED_BECAUSE_TAB_JUST_APPEARED) 203 osk_state_ = OSK_ALLOWED; 204 205 // Don't send a native focus event if the window itself doesn't 206 // have focus. 207 if (delegate_ && !delegate_->AccessibilityViewHasFocus()) 208 continue; 209 } 210 211 // Send the event event to the operating system. 212 NotifyAccessibilityEvent(event_type, GetFromAXNode(node)); 213 } 214 } 215 216 void BrowserAccessibilityManager::OnLocationChanges( 217 const std::vector<AccessibilityHostMsg_LocationChangeParams>& params) { 218 for (size_t i = 0; i < params.size(); ++i) { 219 BrowserAccessibility* obj = GetFromID(params[i].id); 220 if (!obj) 221 continue; 222 ui::AXNode* node = obj->node(); 223 node->SetLocation(params[i].new_location); 224 obj->OnLocationChanged(); 225 } 226 } 227 228 BrowserAccessibility* BrowserAccessibilityManager::GetActiveDescendantFocus( 229 BrowserAccessibility* root) { 230 BrowserAccessibility* node = BrowserAccessibilityManager::GetFocus(root); 231 if (!node) 232 return NULL; 233 234 int active_descendant_id; 235 if (node->GetIntAttribute(ui::AX_ATTR_ACTIVEDESCENDANT_ID, 236 &active_descendant_id)) { 237 BrowserAccessibility* active_descendant = 238 node->manager()->GetFromID(active_descendant_id); 239 if (active_descendant) 240 return active_descendant; 241 } 242 return node; 243 } 244 245 BrowserAccessibility* BrowserAccessibilityManager::GetFocus( 246 BrowserAccessibility* root) { 247 if (focus_ && (!root || focus_->IsDescendantOf(root->node()))) 248 return GetFromAXNode(focus_); 249 250 return NULL; 251 } 252 253 void BrowserAccessibilityManager::SetFocus(ui::AXNode* node, bool notify) { 254 if (focus_ != node) 255 focus_ = node; 256 257 if (notify && node && delegate_) 258 delegate_->AccessibilitySetFocus(node->id()); 259 } 260 261 void BrowserAccessibilityManager::SetFocus( 262 BrowserAccessibility* obj, bool notify) { 263 if (obj->node()) 264 SetFocus(obj->node(), notify); 265 } 266 267 void BrowserAccessibilityManager::DoDefaultAction( 268 const BrowserAccessibility& node) { 269 if (delegate_) 270 delegate_->AccessibilityDoDefaultAction(node.GetId()); 271 } 272 273 void BrowserAccessibilityManager::ScrollToMakeVisible( 274 const BrowserAccessibility& node, gfx::Rect subfocus) { 275 if (delegate_) { 276 delegate_->AccessibilityScrollToMakeVisible(node.GetId(), subfocus); 277 } 278 } 279 280 void BrowserAccessibilityManager::ScrollToPoint( 281 const BrowserAccessibility& node, gfx::Point point) { 282 if (delegate_) { 283 delegate_->AccessibilityScrollToPoint(node.GetId(), point); 284 } 285 } 286 287 void BrowserAccessibilityManager::SetTextSelection( 288 const BrowserAccessibility& node, int start_offset, int end_offset) { 289 if (delegate_) { 290 delegate_->AccessibilitySetTextSelection( 291 node.GetId(), start_offset, end_offset); 292 } 293 } 294 295 gfx::Rect BrowserAccessibilityManager::GetViewBounds() { 296 if (delegate_) 297 return delegate_->AccessibilityGetViewBounds(); 298 return gfx::Rect(); 299 } 300 301 BrowserAccessibility* BrowserAccessibilityManager::NextInTreeOrder( 302 BrowserAccessibility* node) { 303 if (!node) 304 return NULL; 305 306 if (node->PlatformChildCount() > 0) 307 return node->PlatformGetChild(0); 308 while (node) { 309 if (node->GetParent() && 310 node->GetIndexInParent() < 311 static_cast<int>(node->GetParent()->PlatformChildCount()) - 1) { 312 return node->GetParent()->PlatformGetChild(node->GetIndexInParent() + 1); 313 } 314 node = node->GetParent(); 315 } 316 317 return NULL; 318 } 319 320 BrowserAccessibility* BrowserAccessibilityManager::PreviousInTreeOrder( 321 BrowserAccessibility* node) { 322 if (!node) 323 return NULL; 324 325 if (node->GetParent() && node->GetIndexInParent() > 0) { 326 node = node->GetParent()->PlatformGetChild(node->GetIndexInParent() - 1); 327 while (node->PlatformChildCount() > 0) 328 node = node->PlatformGetChild(node->PlatformChildCount() - 1); 329 return node; 330 } 331 332 return node->GetParent(); 333 } 334 335 void BrowserAccessibilityManager::OnNodeWillBeDeleted(ui::AXNode* node) { 336 if (node == focus_ && tree_) { 337 if (node != tree_->GetRoot()) 338 SetFocus(tree_->GetRoot(), false); 339 else 340 focus_ = NULL; 341 } 342 if (id_wrapper_map_.find(node->id()) == id_wrapper_map_.end()) 343 return; 344 GetFromAXNode(node)->Destroy(); 345 id_wrapper_map_.erase(node->id()); 346 } 347 348 void BrowserAccessibilityManager::OnNodeCreated(ui::AXNode* node) { 349 BrowserAccessibility* wrapper = factory_->Create(); 350 wrapper->Init(this, node); 351 id_wrapper_map_[node->id()] = wrapper; 352 wrapper->OnDataChanged(); 353 } 354 355 void BrowserAccessibilityManager::OnNodeChanged(ui::AXNode* node) { 356 GetFromAXNode(node)->OnDataChanged(); 357 } 358 359 void BrowserAccessibilityManager::OnNodeCreationFinished(ui::AXNode* node) { 360 GetFromAXNode(node)->OnUpdateFinished(); 361 } 362 363 void BrowserAccessibilityManager::OnNodeChangeFinished(ui::AXNode* node) { 364 GetFromAXNode(node)->OnUpdateFinished(); 365 } 366 367 ui::AXTreeUpdate BrowserAccessibilityManager::SnapshotAXTreeForTesting() { 368 scoped_ptr<ui::AXTreeSource<const ui::AXNode*> > tree_source( 369 tree_->CreateTreeSource()); 370 ui::AXTreeSerializer<const ui::AXNode*> serializer(tree_source.get()); 371 ui::AXTreeUpdate update; 372 serializer.SerializeChanges(tree_->GetRoot(), &update); 373 return update; 374 } 375 376 } // namespace content 377