Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 // Copyright (c) 2011 The Chromium Authors. All rights reserved. | 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 | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #include "content/browser/accessibility/browser_accessibility_manager.h" | 5 #include "content/browser/accessibility/browser_accessibility_manager.h" |
| 6 | 6 |
| 7 #include "base/logging.h" | 7 #include "base/logging.h" |
| 8 #include "content/browser/accessibility/browser_accessibility.h" | 8 #include "content/browser/accessibility/browser_accessibility.h" |
| 9 #include "content/common/view_messages.h" | 9 #include "content/common/view_messages.h" |
| 10 | 10 |
| (...skipping 43 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 54 | 54 |
| 55 BrowserAccessibilityManager::BrowserAccessibilityManager( | 55 BrowserAccessibilityManager::BrowserAccessibilityManager( |
| 56 gfx::NativeView parent_view, | 56 gfx::NativeView parent_view, |
| 57 const WebAccessibility& src, | 57 const WebAccessibility& src, |
| 58 BrowserAccessibilityDelegate* delegate, | 58 BrowserAccessibilityDelegate* delegate, |
| 59 BrowserAccessibilityFactory* factory) | 59 BrowserAccessibilityFactory* factory) |
| 60 : parent_view_(parent_view), | 60 : parent_view_(parent_view), |
| 61 delegate_(delegate), | 61 delegate_(delegate), |
| 62 factory_(factory), | 62 factory_(factory), |
| 63 focus_(NULL) { | 63 focus_(NULL) { |
| 64 root_ = CreateAccessibilityTree(NULL, src, 0); | 64 root_ = CreateAccessibilityTree(NULL, src, 0, false); |
| 65 if (!focus_) | 65 if (!focus_) |
| 66 SetFocus(root_, false); | 66 SetFocus(root_, false); |
| 67 } | 67 } |
| 68 | 68 |
| 69 // static | 69 // static |
| 70 int32 BrowserAccessibilityManager::GetNextChildID() { | 70 int32 BrowserAccessibilityManager::GetNextChildID() { |
| 71 // Get the next child ID, and wrap around when we get near the end | 71 // Get the next child ID, and wrap around when we get near the end |
| 72 // of a 32-bit integer range. It's okay to wrap around; we just want | 72 // of a 32-bit integer range. It's okay to wrap around; we just want |
| 73 // to avoid it as long as possible because clients may cache the ID of | 73 // to avoid it as long as possible because clients may cache the ID of |
| 74 // an object for a while to determine if they've seen it before. | 74 // an object for a while to determine if they've seen it before. |
| (...skipping 25 matching lines...) Expand all Loading... | |
| 100 return iter->second; | 100 return iter->second; |
| 101 } else { | 101 } else { |
| 102 return NULL; | 102 return NULL; |
| 103 } | 103 } |
| 104 } | 104 } |
| 105 | 105 |
| 106 BrowserAccessibility* BrowserAccessibilityManager::GetFromRendererID( | 106 BrowserAccessibility* BrowserAccessibilityManager::GetFromRendererID( |
| 107 int32 renderer_id) { | 107 int32 renderer_id) { |
| 108 base::hash_map<int32, int32>::iterator iter = | 108 base::hash_map<int32, int32>::iterator iter = |
| 109 renderer_id_to_child_id_map_.find(renderer_id); | 109 renderer_id_to_child_id_map_.find(renderer_id); |
| 110 if (iter == renderer_id_to_child_id_map_.end()) | 110 if (iter == renderer_id_to_child_id_map_.end()) { |
|
David Tseng
2011/08/26 16:14:37
nit: Do we need the braces?
dmazzoni
2011/08/29 18:08:51
Done.
| |
| 111 return NULL; | 111 return NULL; |
| 112 } | |
| 112 | 113 |
| 113 int32 child_id = iter->second; | 114 int32 child_id = iter->second; |
| 114 return GetFromChildID(child_id); | 115 return GetFromChildID(child_id); |
| 115 } | 116 } |
| 116 | 117 |
| 117 void BrowserAccessibilityManager::Remove(int32 child_id, int32 renderer_id) { | 118 void BrowserAccessibilityManager::Remove(int32 child_id, int32 renderer_id) { |
| 118 child_id_map_.erase(child_id); | 119 child_id_map_.erase(child_id); |
| 119 | 120 |
| 120 // TODO(ctguil): Investigate if hit. We should never have a newer entry. | 121 // TODO(ctguil): Investigate if hit. We should never have a newer entry. |
| 121 DCHECK(renderer_id_to_child_id_map_[renderer_id] == child_id); | 122 DCHECK(renderer_id_to_child_id_map_[renderer_id] == child_id); |
| 122 // Make sure we don't overwrite a newer entry (see UpdateNode for a possible | 123 // Make sure we don't overwrite a newer entry (see UpdateNode for a possible |
| 123 // corner case). | 124 // corner case). |
| 124 if (renderer_id_to_child_id_map_[renderer_id] == child_id) | 125 if (renderer_id_to_child_id_map_[renderer_id] == child_id) |
| 125 renderer_id_to_child_id_map_.erase(renderer_id); | 126 renderer_id_to_child_id_map_.erase(renderer_id); |
| 126 } | 127 } |
| 127 | 128 |
| 128 void BrowserAccessibilityManager::OnAccessibilityNotifications( | 129 void BrowserAccessibilityManager::OnAccessibilityNotifications( |
| 129 const std::vector<ViewHostMsg_AccessibilityNotification_Params>& params) { | 130 const std::vector<ViewHostMsg_AccessibilityNotification_Params>& params) { |
| 130 for (uint32 index = 0; index < params.size(); index++) { | 131 for (uint32 index = 0; index < params.size(); index++) { |
| 131 const ViewHostMsg_AccessibilityNotification_Params& param = params[index]; | 132 const ViewHostMsg_AccessibilityNotification_Params& param = params[index]; |
| 132 | 133 |
| 133 switch (param.notification_type) { | 134 switch (param.notification_type) { |
| 134 case ViewHostMsg_AccessibilityNotification_Type:: | 135 case ViewHostMsg_AccessibilityNotification_Type:: |
| 135 NOTIFICATION_TYPE_CHECK_STATE_CHANGED: | 136 NOTIFICATION_TYPE_LIVE_REGION_CHANGED: |
|
David Tseng
2011/08/26 16:14:37
nit: sort alphabetically.
dmazzoni
2011/08/29 18:08:51
Done.
| |
| 136 OnAccessibilityObjectStateChange(param.acc_obj); | |
| 137 break; | |
| 138 case ViewHostMsg_AccessibilityNotification_Type:: | 137 case ViewHostMsg_AccessibilityNotification_Type:: |
| 139 NOTIFICATION_TYPE_CHILDREN_CHANGED: | 138 NOTIFICATION_TYPE_CHILDREN_CHANGED: |
| 140 OnAccessibilityObjectChildrenChange(param.acc_obj); | 139 // Notifications where children are included. |
| 140 OnSimpleAccessibilityNotification( | |
| 141 param.acc_obj, param.notification_type, true); | |
| 141 break; | 142 break; |
| 142 case ViewHostMsg_AccessibilityNotification_Type:: | 143 case ViewHostMsg_AccessibilityNotification_Type:: |
| 143 NOTIFICATION_TYPE_FOCUS_CHANGED: | 144 NOTIFICATION_TYPE_FOCUS_CHANGED: |
| 144 OnAccessibilityObjectFocusChange(param.acc_obj); | 145 OnAccessibilityObjectFocusChange(param.acc_obj); |
| 145 break; | 146 break; |
| 146 case ViewHostMsg_AccessibilityNotification_Type:: | 147 case ViewHostMsg_AccessibilityNotification_Type:: |
| 147 NOTIFICATION_TYPE_LOAD_COMPLETE: | 148 NOTIFICATION_TYPE_LOAD_COMPLETE: |
| 148 OnAccessibilityObjectLoadComplete(param.acc_obj); | 149 OnAccessibilityObjectLoadComplete(param.acc_obj); |
| 149 break; | 150 break; |
| 150 case ViewHostMsg_AccessibilityNotification_Type:: | |
| 151 NOTIFICATION_TYPE_VALUE_CHANGED: | |
| 152 OnAccessibilityObjectValueChange(param.acc_obj); | |
| 153 break; | |
| 154 case ViewHostMsg_AccessibilityNotification_Type:: | |
| 155 NOTIFICATION_TYPE_SELECTED_TEXT_CHANGED: | |
| 156 OnAccessibilityObjectTextChange(param.acc_obj); | |
| 157 break; | |
| 158 default: | 151 default: |
| 159 DCHECK(0); | 152 // For all other notifications, children are not included. |
|
David Tseng
2011/08/26 16:14:37
Either remove the DCHECK above or explicitly list
dmazzoni
2011/08/29 18:08:51
Done.
| |
| 153 OnSimpleAccessibilityNotification( | |
| 154 param.acc_obj, param.notification_type, false); | |
| 160 break; | 155 break; |
| 161 } | 156 } |
| 162 } | 157 } |
| 163 } | 158 } |
| 164 | 159 |
| 165 void BrowserAccessibilityManager::OnAccessibilityObjectStateChange( | 160 void BrowserAccessibilityManager::OnSimpleAccessibilityNotification( |
| 166 const WebAccessibility& acc_obj) { | 161 const WebAccessibility& acc_obj, |
| 167 BrowserAccessibility* new_browser_acc = UpdateNode(acc_obj, false); | 162 int notification_type, |
| 163 bool include_children) { | |
| 164 BrowserAccessibility* new_browser_acc = | |
| 165 UpdateNode(acc_obj, include_children); | |
| 168 if (!new_browser_acc) | 166 if (!new_browser_acc) |
| 169 return; | 167 return; |
| 170 | 168 |
| 171 NotifyAccessibilityEvent( | 169 NotifyAccessibilityEvent(notification_type, new_browser_acc); |
| 172 ViewHostMsg_AccessibilityNotification_Type:: | |
| 173 NOTIFICATION_TYPE_CHECK_STATE_CHANGED, | |
| 174 new_browser_acc); | |
| 175 } | |
| 176 | |
| 177 void BrowserAccessibilityManager::OnAccessibilityObjectChildrenChange( | |
| 178 const WebAccessibility& acc_obj) { | |
| 179 BrowserAccessibility* new_browser_acc = UpdateNode(acc_obj, true); | |
| 180 if (!new_browser_acc) | |
| 181 return; | |
| 182 | |
| 183 NotifyAccessibilityEvent( | |
| 184 ViewHostMsg_AccessibilityNotification_Type:: | |
| 185 NOTIFICATION_TYPE_CHILDREN_CHANGED, | |
| 186 new_browser_acc); | |
| 187 } | 170 } |
| 188 | 171 |
| 189 void BrowserAccessibilityManager::OnAccessibilityObjectFocusChange( | 172 void BrowserAccessibilityManager::OnAccessibilityObjectFocusChange( |
| 190 const WebAccessibility& acc_obj) { | 173 const WebAccessibility& acc_obj) { |
| 191 BrowserAccessibility* new_browser_acc = UpdateNode(acc_obj, false); | 174 BrowserAccessibility* new_browser_acc = UpdateNode(acc_obj, false); |
| 192 if (!new_browser_acc) | 175 if (!new_browser_acc) |
| 193 return; | 176 return; |
| 194 | 177 |
| 195 SetFocus(new_browser_acc, false); | 178 SetFocus(new_browser_acc, false); |
| 196 if (delegate_ && delegate_->HasFocus()) { | 179 if (delegate_ && delegate_->HasFocus()) { |
| (...skipping 16 matching lines...) Expand all Loading... | |
| 213 SetFocus(root_, false); | 196 SetFocus(root_, false); |
| 214 | 197 |
| 215 NotifyAccessibilityEvent( | 198 NotifyAccessibilityEvent( |
| 216 ViewHostMsg_AccessibilityNotification_Type:: | 199 ViewHostMsg_AccessibilityNotification_Type:: |
| 217 NOTIFICATION_TYPE_LOAD_COMPLETE, | 200 NOTIFICATION_TYPE_LOAD_COMPLETE, |
| 218 root_); | 201 root_); |
| 219 if (delegate_ && delegate_->HasFocus()) | 202 if (delegate_ && delegate_->HasFocus()) |
| 220 GotFocus(); | 203 GotFocus(); |
| 221 } | 204 } |
| 222 | 205 |
| 223 void BrowserAccessibilityManager::OnAccessibilityObjectValueChange( | |
| 224 const WebAccessibility& acc_obj) { | |
| 225 BrowserAccessibility* new_browser_acc = UpdateNode(acc_obj, false); | |
| 226 if (!new_browser_acc) | |
| 227 return; | |
| 228 | |
| 229 NotifyAccessibilityEvent( | |
| 230 ViewHostMsg_AccessibilityNotification_Type:: | |
| 231 NOTIFICATION_TYPE_VALUE_CHANGED, | |
| 232 new_browser_acc); | |
| 233 } | |
| 234 | |
| 235 void BrowserAccessibilityManager::OnAccessibilityObjectTextChange( | |
| 236 const WebAccessibility& acc_obj) { | |
| 237 BrowserAccessibility* new_browser_acc = UpdateNode(acc_obj, false); | |
| 238 if (!new_browser_acc) | |
| 239 return; | |
| 240 | |
| 241 NotifyAccessibilityEvent( | |
| 242 ViewHostMsg_AccessibilityNotification_Type:: | |
| 243 NOTIFICATION_TYPE_SELECTED_TEXT_CHANGED, | |
| 244 new_browser_acc); | |
| 245 } | |
| 246 | |
| 247 void BrowserAccessibilityManager::GotFocus() { | 206 void BrowserAccessibilityManager::GotFocus() { |
| 248 // TODO(ctguil): Remove when tree update logic handles focus changes. | 207 // TODO(ctguil): Remove when tree update logic handles focus changes. |
| 249 if (!focus_) | 208 if (!focus_) |
| 250 return; | 209 return; |
| 251 | 210 |
| 252 NotifyAccessibilityEvent( | 211 NotifyAccessibilityEvent( |
| 253 ViewHostMsg_AccessibilityNotification_Type:: | 212 ViewHostMsg_AccessibilityNotification_Type:: |
| 254 NOTIFICATION_TYPE_FOCUS_CHANGED, | 213 NOTIFICATION_TYPE_FOCUS_CHANGED, |
| 255 focus_); | 214 focus_); |
| 256 } | 215 } |
| (...skipping 50 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 307 return NULL; | 266 return NULL; |
| 308 | 267 |
| 309 if (!include_children) { | 268 if (!include_children) { |
| 310 DCHECK_EQ(0U, src.children.size()); | 269 DCHECK_EQ(0U, src.children.size()); |
| 311 current->Initialize( | 270 current->Initialize( |
| 312 this, | 271 this, |
| 313 current->parent(), | 272 current->parent(), |
| 314 current->child_id(), | 273 current->child_id(), |
| 315 current->index_in_parent(), | 274 current->index_in_parent(), |
| 316 src); | 275 src); |
| 276 current->SendNodeUpdateEvents(); | |
| 317 return current; | 277 return current; |
| 318 } | 278 } |
| 319 | 279 |
| 320 BrowserAccessibility* current_parent = current->parent(); | 280 BrowserAccessibility* current_parent = current->parent(); |
| 321 int current_index_in_parent = current->index_in_parent(); | 281 int current_index_in_parent = current->index_in_parent(); |
| 322 | 282 |
| 323 // Detach all of the nodes in the old tree and get a single flat vector | 283 // Detach all of the nodes in the old tree and get a single flat vector |
| 324 // of all node pointers. | 284 // of all node pointers. |
| 325 std::vector<BrowserAccessibility*> old_tree_nodes; | 285 std::vector<BrowserAccessibility*> old_tree_nodes; |
| 326 current->DetachTree(&old_tree_nodes); | 286 current->DetachTree(&old_tree_nodes); |
| 327 | 287 |
| 328 // Build a new tree, reusing old nodes if possible. Each node that's | 288 // Build a new tree, reusing old nodes if possible. Each node that's |
| 329 // reused will have its reference count incremented by one. | 289 // reused will have its reference count incremented by one. |
| 330 current = | 290 current = CreateAccessibilityTree( |
| 331 CreateAccessibilityTree(current_parent, src, current_index_in_parent); | 291 current_parent, src, current_index_in_parent, true); |
| 332 | 292 |
| 333 // Decrement the reference count of all nodes in the old tree, which will | 293 // Decrement the reference count of all nodes in the old tree, which will |
| 334 // delete any nodes no longer needed. | 294 // delete any nodes no longer needed. |
| 335 for (int i = 0; i < static_cast<int>(old_tree_nodes.size()); i++) | 295 for (int i = 0; i < static_cast<int>(old_tree_nodes.size()); i++) { |
|
David Tseng
2011/08/26 16:14:37
nit: braces necessary?
dmazzoni
2011/08/29 18:08:51
Done.
| |
| 336 old_tree_nodes[i]->InternalReleaseReference(false); | 296 old_tree_nodes[i]->InternalReleaseReference(false); |
| 297 } | |
| 337 | 298 |
| 338 if (!focus_ || !focus_->instance_active()) | 299 if (!focus_ || !focus_->instance_active()) |
| 339 SetFocus(root_, false); | 300 SetFocus(root_, false); |
| 340 | 301 |
| 341 return current; | 302 return current; |
| 342 } | 303 } |
| 343 | 304 |
| 344 BrowserAccessibility* BrowserAccessibilityManager::CreateAccessibilityTree( | 305 BrowserAccessibility* BrowserAccessibilityManager::CreateAccessibilityTree( |
| 345 BrowserAccessibility* parent, | 306 BrowserAccessibility* parent, |
| 346 const WebAccessibility& src, | 307 const WebAccessibility& src, |
| 347 int index_in_parent) { | 308 int index_in_parent, |
| 309 bool send_show_events) { | |
| 348 BrowserAccessibility* instance = NULL; | 310 BrowserAccessibility* instance = NULL; |
| 349 int32 child_id = 0; | 311 int32 child_id = 0; |
| 312 bool children_can_send_show_events = send_show_events; | |
| 350 base::hash_map<int32, int32>::iterator iter = | 313 base::hash_map<int32, int32>::iterator iter = |
| 351 renderer_id_to_child_id_map_.find(src.id); | 314 renderer_id_to_child_id_map_.find(src.id); |
| 352 | 315 |
| 353 // If a BrowserAccessibility instance for this ID already exists, add a | 316 // If a BrowserAccessibility instance for this ID already exists, add a |
| 354 // new reference to it and retrieve its children vector. | 317 // new reference to it and retrieve its children vector. |
| 355 if (iter != renderer_id_to_child_id_map_.end()) { | 318 if (iter != renderer_id_to_child_id_map_.end()) { |
| 356 child_id = iter->second; | 319 child_id = iter->second; |
| 357 instance = GetFromChildID(child_id); | 320 instance = GetFromChildID(child_id); |
| 358 } | 321 } |
| 359 | 322 |
| 360 // If the node has changed roles, don't reuse a BrowserAccessibility | 323 // If the node has changed roles, don't reuse a BrowserAccessibility |
| 361 // object, that could confuse a screen reader. | 324 // object, that could confuse a screen reader. |
| 362 // TODO(dtseng): Investigate when this gets hit; See crbug.com/93095. | 325 // TODO(dtseng): Investigate when this gets hit; See crbug.com/93095. |
| 363 DCHECK(!instance || instance->role() == src.role); | 326 DCHECK(!instance || instance->role() == src.role); |
| 364 | 327 |
| 365 // If we're reusing a node, it should already be detached from a parent | 328 // If we're reusing a node, it should already be detached from a parent |
| 366 // and any children. If not, that means we have a serious bug somewhere, | 329 // and any children. If not, that means we have a serious bug somewhere, |
| 367 // like the same child is reachable from two places in the same tree. | 330 // like the same child is reachable from two places in the same tree. |
| 368 if (instance && (instance->parent() != NULL || instance->child_count() > 0)) { | 331 if (instance && (instance->parent() != NULL || instance->child_count() > 0)) { |
| 369 NOTREACHED(); | 332 NOTREACHED(); |
| 370 instance = NULL; | 333 instance = NULL; |
| 371 } | 334 } |
| 372 | 335 |
| 373 if (instance) { | 336 if (instance) { |
| 374 // If we're reusing a node, update its parent and increment its | 337 // If we're reusing a node, update its parent and increment its |
| 375 // reference count. | 338 // reference count. |
| 376 instance->UpdateParent(parent, index_in_parent); | 339 instance->UpdateParent(parent, index_in_parent); |
| 377 instance->InternalAddReference(); | 340 instance->InternalAddReference(); |
| 341 send_show_events = false; | |
| 378 } else { | 342 } else { |
| 379 // Otherwise, create a new instance. | 343 // Otherwise, create a new instance. |
| 380 instance = factory_->Create(); | 344 instance = factory_->Create(); |
| 381 child_id = GetNextChildID(); | 345 child_id = GetNextChildID(); |
| 346 children_can_send_show_events = false; | |
| 382 } | 347 } |
| 383 | 348 |
| 384 instance->Initialize(this, parent, child_id, index_in_parent, src); | 349 instance->Initialize(this, parent, child_id, index_in_parent, src); |
| 385 child_id_map_[child_id] = instance; | 350 child_id_map_[child_id] = instance; |
| 386 renderer_id_to_child_id_map_[src.id] = child_id; | 351 renderer_id_to_child_id_map_[src.id] = child_id; |
| 387 if ((src.state >> WebAccessibility::STATE_FOCUSED) & 1) | 352 if ((src.state >> WebAccessibility::STATE_FOCUSED) & 1) |
| 388 SetFocus(instance, false); | 353 SetFocus(instance, false); |
| 389 for (int i = 0; i < static_cast<int>(src.children.size()); ++i) { | 354 for (int i = 0; i < static_cast<int>(src.children.size()); ++i) { |
| 390 BrowserAccessibility* child = CreateAccessibilityTree( | 355 BrowserAccessibility* child = CreateAccessibilityTree( |
| 391 instance, src.children[i], i); | 356 instance, src.children[i], i, children_can_send_show_events); |
| 392 instance->AddChild(child); | 357 instance->AddChild(child); |
| 393 } | 358 } |
| 359 if (send_show_events) { | |
|
David Tseng
2011/08/26 16:14:37
Any chance we could move this up to UpdateNode?
dmazzoni
2011/08/29 18:08:51
It'd be pretty hard, we need to update children_ca
| |
| 360 NotifyAccessibilityEvent( | |
| 361 ViewHostMsg_AccessibilityNotification_Type:: | |
| 362 NOTIFICATION_TYPE_OBJECT_SHOW, | |
| 363 instance); | |
|
David Tseng
2011/08/26 16:14:37
nit: align with "ViewHostMsg_AccessibilityNotifica
dmazzoni
2011/08/29 18:08:51
Done.
| |
| 364 } | |
| 365 instance->SendNodeUpdateEvents(); | |
| 394 | 366 |
| 395 return instance; | 367 return instance; |
| 396 } | 368 } |
| OLD | NEW |