OLD | NEW |
| (Empty) |
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 | |
3 // found in the LICENSE file. | |
4 | |
5 #import <Cocoa/Cocoa.h> | |
6 | |
7 #include "base/memory/scoped_nsobject.h" | |
8 #include "base/test/test_timeouts.h" | |
9 #include "chrome/common/chrome_switches.h" | |
10 #include "chrome/test/ui/ui_test.h" | |
11 | |
12 // The following tests exercise the Chrome Mac accessibility implementation | |
13 // similarly to the way in which VoiceOver would. | |
14 // We achieve this by utilizing the same carbon API (HIServices) as do | |
15 // other assistive technologies. | |
16 // Note that the tests must be UITests since these API's only work if not | |
17 // called within the same process begin examined. | |
18 class AccessibilityMacUITest : public UITest { | |
19 public: | |
20 AccessibilityMacUITest() { | |
21 // TODO(dtseng): fake the VoiceOver defaults value? | |
22 launch_arguments_.AppendSwitch(switches::kForceRendererAccessibility); | |
23 } | |
24 | |
25 virtual void SetUp() { | |
26 UITest::SetUp(); | |
27 SetupObservedNotifications(); | |
28 Initialize(); | |
29 } | |
30 | |
31 // Called to insert an event for validation. | |
32 // This is a order sensitive expectation. | |
33 void AddExpectedEvent(NSString* notificationName) { | |
34 [expectedEvents_ addObject:notificationName]; | |
35 } | |
36 | |
37 // Assert that there are no remaining expected events. | |
38 // CFRunLoop necessary to receive AX callbacks. | |
39 // Assumes that there is at least one expected event. | |
40 // The runloop stops only if we receive all expected notifications. | |
41 void WaitAndAssertAllEventsObserved() { | |
42 ASSERT_GE([expectedEvents_ count], 1U); | |
43 CFRunLoopRunInMode( | |
44 kCFRunLoopDefaultMode, | |
45 TestTimeouts::action_max_timeout_ms() / 1000, false); | |
46 ASSERT_EQ(0U, [expectedEvents_ count]); | |
47 } | |
48 | |
49 // The Callback handler added to each AXUIElement. | |
50 static void EventReceiver( | |
51 AXObserverRef observerRef, | |
52 AXUIElementRef element, | |
53 CFStringRef notificationName, | |
54 void *refcon) { | |
55 AccessibilityMacUITest* this_pointer = | |
56 reinterpret_cast<AccessibilityMacUITest*>(refcon); | |
57 if ([[this_pointer->expectedEvents_ objectAtIndex:0] | |
58 isEqualToString:(NSString*)notificationName]) { | |
59 [this_pointer->expectedEvents_ removeObjectAtIndex:0]; | |
60 } | |
61 | |
62 if ([this_pointer->expectedEvents_ count] == 0) { | |
63 CFRunLoopStop(CFRunLoopGetCurrent()); | |
64 } | |
65 | |
66 // TODO(dtseng): currently refreshing on all notifications; scope later. | |
67 this_pointer->SetAllObserversOnDescendants(element, observerRef); | |
68 } | |
69 | |
70 private: | |
71 // Perform AX setup. | |
72 void Initialize() { | |
73 expectedEvents_.reset([[NSMutableArray alloc] init]); | |
74 | |
75 // Construct the Chrome AXUIElementRef. | |
76 ASSERT_NE(-1, browser_process_id()); | |
77 AXUIElementRef browserUiElement = | |
78 AXUIElementCreateApplication(browser_process_id()); | |
79 ASSERT_TRUE(browserUiElement); | |
80 | |
81 // Setup our callbacks. | |
82 AXObserverRef observerRef; | |
83 ASSERT_EQ(kAXErrorSuccess, | |
84 AXObserverCreate(browser_process_id(), | |
85 AccessibilityMacUITest::EventReceiver, | |
86 &observerRef)); | |
87 SetAllObserversOnDescendants(browserUiElement, observerRef); | |
88 | |
89 // Add the observer to the current message loop. | |
90 CFRunLoopAddSource( | |
91 [[NSRunLoop currentRunLoop] getCFRunLoop], | |
92 AXObserverGetRunLoopSource(observerRef), | |
93 kCFRunLoopDefaultMode); | |
94 } | |
95 | |
96 // Taken largely from AXNotificationConstants.h | |
97 // (substituted NSAccessibility* to avoid casting). | |
98 void SetupObservedNotifications() { | |
99 observedNotifications_.reset( | |
100 [[NSArray alloc] initWithObjects: | |
101 | |
102 // focus notifications | |
103 NSAccessibilityMainWindowChangedNotification, | |
104 NSAccessibilityFocusedWindowChangedNotification, | |
105 NSAccessibilityFocusedUIElementChangedNotification, | |
106 | |
107 // application notifications | |
108 NSAccessibilityApplicationActivatedNotification, | |
109 NSAccessibilityApplicationDeactivatedNotification, | |
110 NSAccessibilityApplicationHiddenNotification, | |
111 NSAccessibilityApplicationShownNotification, | |
112 | |
113 // window notifications | |
114 NSAccessibilityWindowCreatedNotification, | |
115 NSAccessibilityWindowMovedNotification, | |
116 NSAccessibilityWindowResizedNotification, | |
117 NSAccessibilityWindowMiniaturizedNotification, | |
118 NSAccessibilityWindowDeminiaturizedNotification, | |
119 | |
120 // new drawer, sheet, and help tag notifications | |
121 NSAccessibilityDrawerCreatedNotification, | |
122 NSAccessibilitySheetCreatedNotification, | |
123 NSAccessibilityHelpTagCreatedNotification, | |
124 | |
125 // element notifications | |
126 NSAccessibilityValueChangedNotification, | |
127 NSAccessibilityUIElementDestroyedNotification, | |
128 | |
129 // menu notifications | |
130 (NSString*)kAXMenuOpenedNotification, | |
131 (NSString*)kAXMenuClosedNotification, | |
132 (NSString*)kAXMenuItemSelectedNotification, | |
133 | |
134 // table/outline notifications | |
135 NSAccessibilityRowCountChangedNotification, | |
136 | |
137 // other notifications | |
138 NSAccessibilitySelectedChildrenChangedNotification, | |
139 NSAccessibilityResizedNotification, | |
140 NSAccessibilityMovedNotification, | |
141 NSAccessibilityCreatedNotification, | |
142 NSAccessibilitySelectedRowsChangedNotification, | |
143 NSAccessibilitySelectedColumnsChangedNotification, | |
144 NSAccessibilitySelectedTextChangedNotification, | |
145 NSAccessibilityTitleChangedNotification, | |
146 | |
147 // Webkit specific notifications. | |
148 @"AXLoadComplete", | |
149 nil]); | |
150 } | |
151 | |
152 // Observe AX notifications on element and all descendants. | |
153 void SetAllObserversOnDescendants( | |
154 AXUIElementRef element, | |
155 AXObserverRef observerRef) { | |
156 SetAllObservers(element, observerRef); | |
157 CFTypeRef childrenRef; | |
158 if ((AXUIElementCopyAttributeValue( | |
159 element, kAXChildrenAttribute, &childrenRef)) == kAXErrorSuccess) { | |
160 NSArray* children = (NSArray*)childrenRef; | |
161 for (uint32 i = 0; i < [children count]; ++i) { | |
162 SetAllObserversOnDescendants( | |
163 (AXUIElementRef)[children objectAtIndex:i], observerRef); | |
164 } | |
165 } | |
166 } | |
167 | |
168 // Add observers for all notifications we know about. | |
169 void SetAllObservers( | |
170 AXUIElementRef element, | |
171 AXObserverRef observerRef) { | |
172 for (NSString* notification in observedNotifications_.get()) { | |
173 AXObserverAddNotification( | |
174 observerRef, element, (CFStringRef)notification, this); | |
175 } | |
176 } | |
177 | |
178 // Used to keep track of events received during the lifetime of the tests. | |
179 scoped_nsobject<NSMutableArray> expectedEvents_; | |
180 // NSString collection of all AX notifications. | |
181 scoped_nsobject<NSArray> observedNotifications_; | |
182 }; | |
183 | |
184 // Timing out frequently. http://crbug.com/98388 | |
185 TEST_F(AccessibilityMacUITest, DISABLED_TestInitialPageNotifications) { | |
186 // Browse to a new page. | |
187 GURL tree_url( | |
188 "data:text/html,<html><head><title>Accessibility Mac Test</title></head>" | |
189 "<body><input type='button' value='push' /><input type='checkbox' />" | |
190 "</body></html>"); | |
191 NavigateToURLAsync(tree_url); | |
192 | |
193 // Test for navigation. | |
194 AddExpectedEvent(@"AXLoadComplete"); | |
195 | |
196 // Check all the expected Mac notifications. | |
197 WaitAndAssertAllEventsObserved(); | |
198 } | |
OLD | NEW |