Re-subimission of https://codereview.chromium.org/1041213003/
[chromium-blink-merge.git] / content / browser / accessibility / browser_accessibility_manager_mac.mm
blob1361164af50e99f5959471dad1462b147ec576d6
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 ui::AXTreeUpdate& 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 ui::AXTreeUpdate& initial_tree,
26     BrowserAccessibilityDelegate* delegate,
27     BrowserAccessibilityFactory* factory)
28     : BrowserAccessibilityManager(delegate, factory),
29       parent_view_(parent_view) {
30   Initialize(initial_tree);
33 // static
34 ui::AXTreeUpdate BrowserAccessibilityManagerMac::GetEmptyDocument() {
35   ui::AXNodeData empty_document;
36   empty_document.id = 0;
37   empty_document.role = ui::AX_ROLE_ROOT_WEB_AREA;
38   empty_document.state =
39       1 << ui::AX_STATE_READ_ONLY;
40   ui::AXTreeUpdate update;
41   update.nodes.push_back(empty_document);
42   return update;
45 BrowserAccessibility* BrowserAccessibilityManagerMac::GetFocus(
46     BrowserAccessibility* root) {
47   BrowserAccessibility* node = GetActiveDescendantFocus(root);
48   return node;
51 void BrowserAccessibilityManagerMac::NotifyAccessibilityEvent(
52     ui::AXEvent event_type,
53     BrowserAccessibility* node) {
54   if (!node->IsNative())
55     return;
57   // Refer to AXObjectCache.mm (webkit).
58   NSString* event_id = @"";
59   switch (event_type) {
60     case ui::AX_EVENT_ACTIVEDESCENDANTCHANGED:
61       if (node->GetRole() == ui::AX_ROLE_TREE) {
62         event_id = NSAccessibilitySelectedRowsChangedNotification;
63       } else {
64         event_id = NSAccessibilityFocusedUIElementChangedNotification;
65         BrowserAccessibility* active_descendant_focus =
66             GetActiveDescendantFocus(GetRoot());
67         if (active_descendant_focus)
68           node = active_descendant_focus;
69       }
71       break;
72     case ui::AX_EVENT_ALERT:
73       // Not used on Mac.
74       return;
75     case ui::AX_EVENT_BLUR:
76       // A no-op on Mac.
77       return;
78     case ui::AX_EVENT_CHECKED_STATE_CHANGED:
79       // Not used on Mac.
80       return;
81     case ui::AX_EVENT_CHILDREN_CHANGED:
82       // TODO(dtseng): no clear equivalent on Mac.
83       return;
84     case ui::AX_EVENT_FOCUS:
85       event_id = NSAccessibilityFocusedUIElementChangedNotification;
86       break;
87     case ui::AX_EVENT_LAYOUT_COMPLETE:
88       event_id = @"AXLayoutComplete";
89       break;
90     case ui::AX_EVENT_LIVE_REGION_CHANGED:
91       event_id = @"AXLiveRegionChanged";
92       break;
93     case ui::AX_EVENT_LOAD_COMPLETE:
94       event_id = @"AXLoadComplete";
95       break;
96     case ui::AX_EVENT_MENU_LIST_VALUE_CHANGED:
97       // Not used on Mac.
98       return;
99     case ui::AX_EVENT_ROW_COUNT_CHANGED:
100       event_id = NSAccessibilityRowCountChangedNotification;
101       break;
102     case ui::AX_EVENT_ROW_COLLAPSED:
103       event_id = @"AXRowCollapsed";
104       break;
105     case ui::AX_EVENT_ROW_EXPANDED:
106       event_id = @"AXRowExpanded";
107       break;
108     case ui::AX_EVENT_SCROLLED_TO_ANCHOR:
109       // Not used on Mac.
110       return;
111     case ui::AX_EVENT_SELECTED_CHILDREN_CHANGED:
112       event_id = NSAccessibilitySelectedChildrenChangedNotification;
113       break;
114     case ui::AX_EVENT_TEXT_SELECTION_CHANGED:
115       event_id = NSAccessibilitySelectedTextChangedNotification;
116       break;
117     case ui::AX_EVENT_VALUE_CHANGED:
118       event_id = NSAccessibilityValueChangedNotification;
119       break;
120     case ui::AX_EVENT_ARIA_ATTRIBUTE_CHANGED:
121       // Not used on Mac.
122       return;
123     case ui::AX_EVENT_AUTOCORRECTION_OCCURED:
124       // Not used on Mac.
125       return;
126     case ui::AX_EVENT_INVALID_STATUS_CHANGED:
127       // Not used on Mac.
128       return;
129     case ui::AX_EVENT_LOCATION_CHANGED:
130       // Not used on Mac.
131       return;
132     case ui::AX_EVENT_MENU_LIST_ITEM_SELECTED:
133       // Not used on Mac.
134       return;
135     case ui::AX_EVENT_TEXT_CHANGED:
136       // Not used on Mac.
137       return;
138     default:
139       LOG(WARNING) << "Unknown accessibility event: " << event_type;
140       return;
141   }
142   BrowserAccessibilityCocoa* native_node = node->ToBrowserAccessibilityCocoa();
143   DCHECK(native_node);
144   NSAccessibilityPostNotification(native_node, event_id);
147 void BrowserAccessibilityManagerMac::OnAtomicUpdateFinished(
148     bool root_changed, const std::vector<ui::AXTreeDelegate::Change>& changes) {
149   BrowserAccessibilityManager::OnAtomicUpdateFinished(root_changed, changes);
151   bool created_live_region = false;
152   for (size_t i = 0; i < changes.size(); ++i) {
153     if (changes[i].type != NODE_CREATED && changes[i].type != SUBTREE_CREATED)
154       continue;
155     BrowserAccessibility* obj = GetFromAXNode(changes[i].node);
156     if (obj && obj->HasStringAttribute(ui::AX_ATTR_LIVE_STATUS)) {
157       created_live_region = true;
158       break;
159     }
160   }
162   if (!created_live_region)
163     return;
165   // This code is to work around a bug in VoiceOver, where a new live
166   // region that gets added is ignored. VoiceOver seems to only scan the
167   // page for live regions once. By recreating the NSAccessibility
168   // object for the root of the tree, we force VoiceOver to clear out its
169   // internal state and find newly-added live regions this time.
170   BrowserAccessibilityMac* root =
171       static_cast<BrowserAccessibilityMac*>(GetRoot());
172   root->RecreateNativeObject();
173   NotifyAccessibilityEvent(ui::AX_EVENT_CHILDREN_CHANGED, root);
176 }  // namespace content