ColorTypes are no longer constexpr in debug builds due to std::isnan() in range assertion
[WebKit-https.git] / Tools / WebKitTestRunner / InjectedBundle / mac / AccessibilityControllerMac.mm
1 /*
2  * Copyright (C) 2011 Google Inc. All rights reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions are
6  * met:
7  *
8  *     * Redistributions of source code must retain the above copyright
9  * notice, this list of conditions and the following disclaimer.
10  *     * Redistributions in binary form must reproduce the above
11  * copyright notice, this list of conditions and the following disclaimer
12  * in the documentation and/or other materials provided with the
13  * distribution.
14  *     * Neither the name of Google Inc. nor the names of its
15  * contributors may be used to endorse or promote products derived from
16  * this software without specific prior written permission.
17  *
18  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
19  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
20  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
21  * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
22  * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
23  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
24  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
25  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
26  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
28  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29  */
30
31 #import "config.h"
32 #import "AccessibilityController.h"
33
34 #import "AccessibilityCommonMac.h"
35 #import "AccessibilityNotificationHandler.h"
36 #import "InjectedBundle.h"
37 #import "InjectedBundlePage.h"
38 #import "JSBasics.h"
39 #import <JavaScriptCore/JSStringRefCF.h>
40 #import <WebKit/WKBundle.h>
41 #import <WebKit/WKBundlePage.h>
42 #import <WebKit/WKBundlePagePrivate.h>
43
44 #if ENABLE(ACCESSIBILITY_ISOLATED_TREE)
45 #import <pal/spi/cocoa/AccessibilitySupportSPI.h>
46 #import <pal/spi/mac/HIServicesSPI.h>
47 #endif
48
49 namespace WTR {
50
51 bool AccessibilityController::addNotificationListener(JSValueRef functionCallback)
52 {
53     if (!functionCallback)
54         return false;
55
56     if (m_globalNotificationHandler)
57         return false;
58
59     m_globalNotificationHandler = adoptNS([[AccessibilityNotificationHandler alloc] init]);
60     [m_globalNotificationHandler.get() setCallback:functionCallback];
61     [m_globalNotificationHandler.get() startObserving];
62
63     return true;
64 }
65
66 bool AccessibilityController::removeNotificationListener()
67 {
68     ASSERT(m_globalNotificationHandler);
69     
70     [m_globalNotificationHandler.get() stopObserving];
71     m_globalNotificationHandler.clear();
72
73     return true;
74 }
75
76 void AccessibilityController::resetToConsistentState()
77 {
78     if (m_globalNotificationHandler)
79         removeNotificationListener();
80 }
81
82 static id findAccessibleObjectById(id obj, NSString *idAttribute)
83 {
84     BEGIN_AX_OBJC_EXCEPTIONS
85     id objIdAttribute = [obj accessibilityAttributeValue:@"AXDOMIdentifier"];
86     if ([objIdAttribute isKindOfClass:[NSString class]] && [objIdAttribute isEqualToString:idAttribute])
87         return obj;
88     END_AX_OBJC_EXCEPTIONS
89
90     BEGIN_AX_OBJC_EXCEPTIONS
91     NSArray *children = [obj accessibilityAttributeValue:NSAccessibilityChildrenAttribute];
92     NSUInteger childrenCount = [children count];
93     for (NSUInteger i = 0; i < childrenCount; ++i) {
94         id result = findAccessibleObjectById([children objectAtIndex:i], idAttribute);
95         if (result)
96             return result;
97     }
98     END_AX_OBJC_EXCEPTIONS
99
100     return nil;
101 }
102
103 void AccessibilityController::injectAccessibilityPreference(JSStringRef domain, JSStringRef key, JSStringRef value)
104 {
105     auto page = InjectedBundle::singleton().page()->page();
106     NSNumber *numberValue = @([[NSString stringWithJSStringRef:value] integerValue]);
107     NSData *encodedData = [NSKeyedArchiver archivedDataWithRootObject:numberValue requiringSecureCoding:YES error:nil];
108     NSString *encodedString = [encodedData base64EncodedStringWithOptions:0];
109     WKAccessibilityTestingInjectPreference(page, toWK(domain).get(), toWK(key).get(), toWK(encodedString).get());
110 }
111
112 RefPtr<AccessibilityUIElement> AccessibilityController::accessibleElementById(JSStringRef idAttribute)
113 {
114     auto page = InjectedBundle::singleton().page()->page();
115     PlatformUIElement root = static_cast<PlatformUIElement>(WKAccessibilityRootObject(page));
116
117     RetainPtr<id> result;
118     executeOnAXThreadAndWait([&root, &idAttribute, &result] {
119         result = findAccessibleObjectById(root, [NSString stringWithJSStringRef:idAttribute]);
120     });
121
122     if (result)
123         return AccessibilityUIElement::create(result.get());
124     return nullptr;
125 }
126
127 JSRetainPtr<JSStringRef> AccessibilityController::platformName()
128 {
129     return WTR::createJSString("mac");
130 }
131
132 #if ENABLE(ACCESSIBILITY_ISOLATED_TREE)
133 void AccessibilityController::updateIsolatedTreeMode()
134 {
135     // Override to set identifier to VoiceOver so that requests are handled in isolated mode.
136     _AXSetClientIdentificationOverride(m_accessibilityIsolatedTreeMode ? (AXClientType)kAXClientTypeWebKitTesting : kAXClientTypeNoActiveRequestFound);
137     _AXSSetIsolatedTreeMode(m_accessibilityIsolatedTreeMode ? AXSIsolatedTreeModeSecondaryThread : AXSIsolatedTreeModeOff);
138     m_useMockAXThread = WKAccessibilityCanUseSecondaryAXThread(InjectedBundle::singleton().page()->page());
139 }
140 #endif
141
142 // AXThread implementation
143
144 void AXThread::initializeRunLoop()
145 {
146     // Initialize the run loop.
147     {
148         Locker locker { m_initializeRunLoopMutex };
149
150         m_threadRunLoop = CFRunLoopGetCurrent();
151
152         CFRunLoopSourceContext context = { 0, this, 0, 0, 0, 0, 0, 0, 0, threadRunLoopSourceCallback };
153         m_threadRunLoopSource = adoptCF(CFRunLoopSourceCreate(0, 0, &context));
154         CFRunLoopAddSource(CFRunLoopGetCurrent(), m_threadRunLoopSource.get(), kCFRunLoopDefaultMode);
155
156         m_initializeRunLoopConditionVariable.notifyAll();
157     }
158
159     ASSERT(isCurrentThread());
160
161     CFRunLoopRun();
162 }
163
164 void AXThread::wakeUpRunLoop()
165 {
166     CFRunLoopSourceSignal(m_threadRunLoopSource.get());
167     CFRunLoopWakeUp(m_threadRunLoop.get());
168 }
169
170 void AXThread::threadRunLoopSourceCallback(void* axThread)
171 {
172     static_cast<AXThread*>(axThread)->threadRunLoopSourceCallback();
173 }
174
175 void AXThread::threadRunLoopSourceCallback()
176 {
177     @autoreleasepool {
178         dispatchFunctionsFromAXThread();
179     }
180 }
181
182 } // namespace WTR