Chromium Code Reviews| Index: content/browser/accessibility/browser_accessibility_manager.cc |
| diff --git a/content/browser/accessibility/browser_accessibility_manager.cc b/content/browser/accessibility/browser_accessibility_manager.cc |
| index c6b65e6b732e25d227c21c411d989b6b473f1076..ebf3a99e8a5f65aba3acd77bdae4b813c64838bd 100644 |
| --- a/content/browser/accessibility/browser_accessibility_manager.cc |
| +++ b/content/browser/accessibility/browser_accessibility_manager.cc |
| @@ -125,6 +125,8 @@ BrowserAccessibilityManager::BrowserAccessibilityManager( |
| tree_(new ui::AXSerializableTree()), |
| user_is_navigating_away_(false), |
| osk_state_(OSK_ALLOWED), |
| + last_focused_node_(nullptr), |
| + last_focused_manager_(nullptr), |
| ax_tree_id_(AXTreeIDRegistry::kNoAXTreeID), |
| parent_node_id_from_parent_tree_(0) { |
| tree_->SetDelegate(this); |
| @@ -173,6 +175,40 @@ BrowserAccessibilityManager::GetEmptyDocument() { |
| return update; |
| } |
| +void BrowserAccessibilityManager::FireFocusEventsIfNeeded() { |
| + BrowserAccessibility* focus = GetFocus(); |
| + if (delegate_ && !delegate_->AccessibilityViewHasFocus()) |
| + focus = nullptr; |
| + |
| + if (!CanFireEvents()) |
| + focus = nullptr; |
| + |
| + // Don't allow the document to be focused if it has no children and |
| + // hasn't finished loading yet. Wait for at least a tiny bit of content, |
| + // or for the document to actually finish loading. |
| + if (focus && |
| + focus == focus->manager()->GetRoot() && |
| + focus->PlatformChildCount() == 0 && |
| + !focus->HasState(ui::AX_STATE_BUSY) && |
| + !focus->manager()->GetTreeData().loaded) { |
|
David Tseng
2016/03/04 16:50:20
Why do we want to exclude focus for this case?
dmazzoni
2016/03/07 21:35:32
I moved this from Windows-specific code to cross-p
|
| + focus = nullptr; |
| + } |
| + |
| + if (focus && focus != last_focused_node_) |
| + FireFocusEvent(focus); |
| + |
| + last_focused_node_ = focus; |
|
David Tseng
2016/03/04 16:50:20
This would set nullptr for the conditions above. I
dmazzoni
2016/03/07 21:35:32
Yes, definitely intentional. Every single event we
|
| + last_focused_manager_ = focus ? focus->manager() : nullptr; |
|
David Tseng
2016/03/04 16:50:20
Why not remove the FocusManager member pointer and
dmazzoni
2016/03/07 21:35:32
Because we don't have an easy way of clearing last
|
| +} |
| + |
| +bool BrowserAccessibilityManager::CanFireEvents() { |
| + return true; |
| +} |
| + |
| +void BrowserAccessibilityManager::FireFocusEvent(BrowserAccessibility* node) { |
| + NotifyAccessibilityEvent(ui::AX_EVENT_FOCUS, node); |
| +} |
| + |
| BrowserAccessibility* BrowserAccessibilityManager::GetRoot() { |
| // tree_ can be null during destruction. |
| if (!tree_) |
| @@ -238,15 +274,15 @@ const ui::AXTreeData& BrowserAccessibilityManager::GetTreeData() { |
| } |
| void BrowserAccessibilityManager::OnWindowFocused() { |
| - BrowserAccessibility* focus = GetFocus(); |
| - if (focus) |
| - NotifyAccessibilityEvent(ui::AX_EVENT_FOCUS, focus); |
| + if (this == GetRootManager()) |
| + FireFocusEventsIfNeeded(); |
| } |
| void BrowserAccessibilityManager::OnWindowBlurred() { |
| - BrowserAccessibility* focus = GetFocus(); |
| - if (focus) |
| - NotifyAccessibilityEvent(ui::AX_EVENT_BLUR, focus); |
| + if (this == GetRootManager()) { |
| + last_focused_node_ = nullptr; |
| + last_focused_manager_ = nullptr; |
| + } |
| } |
| void BrowserAccessibilityManager::UserIsNavigatingAway() { |
| @@ -294,7 +330,14 @@ void BrowserAccessibilityManager::OnAccessibilityEvents( |
| } |
| } |
| - // Now iterate over the events again and fire the events. |
| + // Based on the changes to the tree, first fire focus events if needed. |
| + // Screen readers might not do the right thing if they're not aware of what |
| + // has focus, so always try that first. Nothing will be fired if the window |
| + // itself isn't focused or if focus hasn't changed. |
| + GetRootManager()->FireFocusEventsIfNeeded(); |
| + |
| + // Now iterate over the events again and fire the events other than focus |
| + // events. |
| for (uint32_t index = 0; index < details.size(); index++) { |
| const AXEventNotificationDetails& detail = details[index]; |
| @@ -311,13 +354,18 @@ void BrowserAccessibilityManager::OnAccessibilityEvents( |
| osk_state_ != OSK_DISALLOWED_BECAUSE_TAB_JUST_APPEARED) |
| osk_state_ = OSK_ALLOWED; |
| - // Don't send a native focus event if the window itself doesn't |
| - // have focus. |
| - if (!NativeViewHasFocus()) |
| + bool is_menu_list_option = |
| + node->data().role == ui::AX_ROLE_MENU_LIST_OPTION; |
| + |
| + // Skip all focus events other than ones on menu list options; |
| + // we've already handled them, above. Menu list options are a weird |
| + // exception because the menu list itself has focus but we need to fire |
| + // focus events on the individual options. |
| + if (!is_menu_list_option) |
| continue; |
| } |
| - // Send the event event to the operating system. |
| + // Fire the native event. |
| NotifyAccessibilityEvent(event_type, GetFromAXNode(node)); |
| } |
| } |
| @@ -395,10 +443,21 @@ bool BrowserAccessibilityManager::NativeViewHasFocus() { |
| } |
| BrowserAccessibility* BrowserAccessibilityManager::GetFocus() { |
| - int32_t focus_id = GetTreeData().focus_id; |
| - BrowserAccessibility* obj = GetFromID(focus_id); |
| + BrowserAccessibilityManager* root_manager = GetRootManager(); |
| + if (!root_manager) |
| + root_manager = this; |
| + int32_t focused_tree_id = root_manager->GetTreeData().focused_tree_id; |
| + |
| + BrowserAccessibilityManager* focused_manager = nullptr; |
| + if (focused_tree_id) |
| + focused_manager =BrowserAccessibilityManager::FromID(focused_tree_id); |
| + if (!focused_manager) |
| + focused_manager = root_manager; |
| + |
| + int32_t focus_id = focused_manager->GetTreeData().focus_id; |
| + BrowserAccessibility* obj = focused_manager->GetFromID(focus_id); |
| if (!obj) |
| - return GetRoot(); |
| + return focused_manager->GetRoot(); |
| if (obj->HasIntAttribute(ui::AX_ATTR_CHILD_TREE_ID)) { |
| BrowserAccessibilityManager* child_manager = |