Merge Chromium + Blink git repositories
[chromium-blink-merge.git] / content / browser / accessibility / browser_accessibility_manager_mac.mm
blob8d57b692b66dba06453168c47a78222e2abca87f
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.
5 #include "content/browser/accessibility/browser_accessibility_manager_mac.h"
7 #import "base/logging.h"
8 #import "content/browser/accessibility/browser_accessibility_cocoa.h"
9 #import "content/browser/accessibility/browser_accessibility_mac.h"
10 #include "content/common/accessibility_messages.h"
12 namespace content {
14 // static
15 BrowserAccessibilityManager* BrowserAccessibilityManager::Create(
16     const SimpleAXTreeUpdate& initial_tree,
17     BrowserAccessibilityDelegate* delegate,
18     BrowserAccessibilityFactory* factory) {
19   return new BrowserAccessibilityManagerMac(
20       NULL, initial_tree, delegate, factory);
23 BrowserAccessibilityManagerMac::BrowserAccessibilityManagerMac(
24     NSView* parent_view,
25     const SimpleAXTreeUpdate& initial_tree,
26     BrowserAccessibilityDelegate* delegate,
27     BrowserAccessibilityFactory* factory)
28     : BrowserAccessibilityManager(delegate, factory),
29       parent_view_(parent_view) {
30   Initialize(initial_tree);
33 // static
34 SimpleAXTreeUpdate
35     BrowserAccessibilityManagerMac::GetEmptyDocument() {
36   ui::AXNodeData empty_document;
37   empty_document.id = 0;
38   empty_document.role = ui::AX_ROLE_ROOT_WEB_AREA;
39   empty_document.state =
40       1 << ui::AX_STATE_READ_ONLY;
41   SimpleAXTreeUpdate update;
42   update.nodes.push_back(empty_document);
43   return update;
46 BrowserAccessibility* BrowserAccessibilityManagerMac::GetFocus(
47     BrowserAccessibility* root) {
48   // On Mac, list boxes should always get focus on the whole list, otherwise
49   // information about the number of selected items will never be reported.
50   BrowserAccessibility* node = BrowserAccessibilityManager::GetFocus(root);
51   if (node && node->GetRole() == ui::AX_ROLE_LIST_BOX)
52     return node;
54   // For other roles, follow the active descendant.
55   return GetActiveDescendantFocus(root);
58 void BrowserAccessibilityManagerMac::NotifyAccessibilityEvent(
59     ui::AXEvent event_type,
60     BrowserAccessibility* node) {
61   if (!node->IsNative())
62     return;
64   if (event_type == ui::AX_EVENT_FOCUS &&
65       node->GetRole() == ui::AX_ROLE_LIST_BOX_OPTION &&
66       node->HasState(ui::AX_STATE_SELECTED) &&
67       node->GetParent() &&
68       node->GetParent()->GetRole() == ui::AX_ROLE_LIST_BOX) {
69     node = node->GetParent();
70     SetFocus(node, false);
71   }
73   // Refer to AXObjectCache.mm (webkit).
74   NSString* event_id = @"";
75   switch (event_type) {
76     case ui::AX_EVENT_ACTIVEDESCENDANTCHANGED:
77       if (node->GetRole() == ui::AX_ROLE_TREE) {
78         event_id = NSAccessibilitySelectedRowsChangedNotification;
79       } else {
80         event_id = NSAccessibilityFocusedUIElementChangedNotification;
81         BrowserAccessibility* active_descendant_focus =
82             GetActiveDescendantFocus(GetRoot());
83         if (active_descendant_focus)
84           node = active_descendant_focus;
85       }
87       break;
88     case ui::AX_EVENT_ALERT:
89       // Not used on Mac.
90       return;
91     case ui::AX_EVENT_BLUR:
92       // A no-op on Mac.
93       return;
94     case ui::AX_EVENT_CHECKED_STATE_CHANGED:
95       // Not used on Mac.
96       return;
97     case ui::AX_EVENT_CHILDREN_CHANGED:
98       // TODO(dtseng): no clear equivalent on Mac.
99       return;
100     case ui::AX_EVENT_FOCUS:
101       event_id = NSAccessibilityFocusedUIElementChangedNotification;
102       break;
103     case ui::AX_EVENT_LAYOUT_COMPLETE:
104       event_id = @"AXLayoutComplete";
105       break;
106     case ui::AX_EVENT_LIVE_REGION_CHANGED:
107       event_id = @"AXLiveRegionChanged";
108       break;
109     case ui::AX_EVENT_LOAD_COMPLETE:
110       event_id = @"AXLoadComplete";
111       break;
112     case ui::AX_EVENT_MENU_LIST_VALUE_CHANGED:
113       // Not used on Mac.
114       return;
115     case ui::AX_EVENT_ROW_COUNT_CHANGED:
116       event_id = NSAccessibilityRowCountChangedNotification;
117       break;
118     case ui::AX_EVENT_ROW_COLLAPSED:
119       event_id = @"AXRowCollapsed";
120       break;
121     case ui::AX_EVENT_ROW_EXPANDED:
122       event_id = @"AXRowExpanded";
123       break;
124     case ui::AX_EVENT_SCROLLED_TO_ANCHOR:
125       // Not used on Mac.
126       return;
127     case ui::AX_EVENT_SELECTED_CHILDREN_CHANGED:
128       event_id = NSAccessibilitySelectedChildrenChangedNotification;
129       break;
130     case ui::AX_EVENT_TEXT_SELECTION_CHANGED:
131       event_id = NSAccessibilitySelectedTextChangedNotification;
132       break;
133     case ui::AX_EVENT_VALUE_CHANGED:
134       event_id = NSAccessibilityValueChangedNotification;
135       break;
136     case ui::AX_EVENT_ARIA_ATTRIBUTE_CHANGED:
137       // Not used on Mac.
138       return;
139     case ui::AX_EVENT_AUTOCORRECTION_OCCURED:
140       // Not used on Mac.
141       return;
142     case ui::AX_EVENT_INVALID_STATUS_CHANGED:
143       // Not used on Mac.
144       return;
145     case ui::AX_EVENT_LOCATION_CHANGED:
146       // Not used on Mac.
147       return;
148     case ui::AX_EVENT_MENU_LIST_ITEM_SELECTED:
149       // Not used on Mac.
150       return;
151     case ui::AX_EVENT_TEXT_CHANGED:
152       // Not used on Mac.
153       return;
154     default:
155       LOG(WARNING) << "Unknown accessibility event: " << event_type;
156       return;
157   }
159   BrowserAccessibilityCocoa* native_node = node->ToBrowserAccessibilityCocoa();
160   DCHECK(native_node);
161   NSAccessibilityPostNotification(native_node, event_id);
164 void BrowserAccessibilityManagerMac::OnAtomicUpdateFinished(
165     ui::AXTree* tree,
166     bool root_changed,
167     const std::vector<ui::AXTreeDelegate::Change>& changes) {
168   BrowserAccessibilityManager::OnAtomicUpdateFinished(
169       tree, root_changed, changes);
171   bool created_live_region = false;
172   for (size_t i = 0; i < changes.size(); ++i) {
173     if (changes[i].type != NODE_CREATED && changes[i].type != SUBTREE_CREATED)
174       continue;
175     BrowserAccessibility* obj = GetFromAXNode(changes[i].node);
176     if (obj && obj->HasStringAttribute(ui::AX_ATTR_LIVE_STATUS)) {
177       created_live_region = true;
178       break;
179     }
180   }
182   if (!created_live_region)
183     return;
185   // This code is to work around a bug in VoiceOver, where a new live
186   // region that gets added is ignored. VoiceOver seems to only scan the
187   // page for live regions once. By recreating the NSAccessibility
188   // object for the root of the tree, we force VoiceOver to clear out its
189   // internal state and find newly-added live regions this time.
190   BrowserAccessibilityMac* root =
191       static_cast<BrowserAccessibilityMac*>(GetRoot());
192   if (root) {
193     root->RecreateNativeObject();
194     NotifyAccessibilityEvent(ui::AX_EVENT_CHILDREN_CHANGED, root);
195   }
198 }  // namespace content