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"
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(
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);
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);
45 BrowserAccessibility* BrowserAccessibilityManagerMac::GetFocus(
46 BrowserAccessibility* root) {
47 BrowserAccessibility* node = GetActiveDescendantFocus(root);
51 void BrowserAccessibilityManagerMac::NotifyAccessibilityEvent(
52 ui::AXEvent event_type,
53 BrowserAccessibility* node) {
54 if (!node->IsNative())
57 // Refer to AXObjectCache.mm (webkit).
58 NSString* event_id = @"";
60 case ui::AX_EVENT_ACTIVEDESCENDANTCHANGED:
61 if (node->GetRole() == ui::AX_ROLE_TREE) {
62 event_id = NSAccessibilitySelectedRowsChangedNotification;
64 event_id = NSAccessibilityFocusedUIElementChangedNotification;
65 BrowserAccessibility* active_descendant_focus =
66 GetActiveDescendantFocus(GetRoot());
67 if (active_descendant_focus)
68 node = active_descendant_focus;
72 case ui::AX_EVENT_ALERT:
75 case ui::AX_EVENT_BLUR:
78 case ui::AX_EVENT_CHECKED_STATE_CHANGED:
81 case ui::AX_EVENT_CHILDREN_CHANGED:
82 // TODO(dtseng): no clear equivalent on Mac.
84 case ui::AX_EVENT_FOCUS:
85 event_id = NSAccessibilityFocusedUIElementChangedNotification;
87 case ui::AX_EVENT_LAYOUT_COMPLETE:
88 event_id = @"AXLayoutComplete";
90 case ui::AX_EVENT_LIVE_REGION_CHANGED:
91 event_id = @"AXLiveRegionChanged";
93 case ui::AX_EVENT_LOAD_COMPLETE:
94 event_id = @"AXLoadComplete";
96 case ui::AX_EVENT_MENU_LIST_VALUE_CHANGED:
99 case ui::AX_EVENT_ROW_COUNT_CHANGED:
100 event_id = NSAccessibilityRowCountChangedNotification;
102 case ui::AX_EVENT_ROW_COLLAPSED:
103 event_id = @"AXRowCollapsed";
105 case ui::AX_EVENT_ROW_EXPANDED:
106 event_id = @"AXRowExpanded";
108 case ui::AX_EVENT_SCROLLED_TO_ANCHOR:
111 case ui::AX_EVENT_SELECTED_CHILDREN_CHANGED:
112 event_id = NSAccessibilitySelectedChildrenChangedNotification;
114 case ui::AX_EVENT_TEXT_SELECTION_CHANGED:
115 event_id = NSAccessibilitySelectedTextChangedNotification;
117 case ui::AX_EVENT_VALUE_CHANGED:
118 event_id = NSAccessibilityValueChangedNotification;
120 case ui::AX_EVENT_ARIA_ATTRIBUTE_CHANGED:
123 case ui::AX_EVENT_AUTOCORRECTION_OCCURED:
126 case ui::AX_EVENT_INVALID_STATUS_CHANGED:
129 case ui::AX_EVENT_LOCATION_CHANGED:
132 case ui::AX_EVENT_MENU_LIST_ITEM_SELECTED:
135 case ui::AX_EVENT_TEXT_CHANGED:
139 LOG(WARNING) << "Unknown accessibility event: " << event_type;
142 BrowserAccessibilityCocoa* native_node = node->ToBrowserAccessibilityCocoa();
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)
155 BrowserAccessibility* obj = GetFromAXNode(changes[i].node);
156 if (obj && obj->HasStringAttribute(ui::AX_ATTR_LIVE_STATUS)) {
157 created_live_region = true;
162 if (!created_live_region)
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