Source/ThirdParty:
[WebKit-https.git] / Tools / WebKitTestRunner / mac / UIScriptControllerMac.mm
1 /*
2  * Copyright (C) 2015 Apple 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
6  * are met:
7  * 1. Redistributions of source code must retain the above copyright
8  *    notice, this list of conditions and the following disclaimer.
9  * 2. Redistributions in binary form must reproduce the above copyright
10  *    notice, this list of conditions and the following disclaimer in the
11  *    documentation and/or other materials provided with the distribution.
12  *
13  * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
14  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
15  * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16  * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
17  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
18  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
19  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
20  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
21  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
22  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
23  * THE POSSIBILITY OF SUCH DAMAGE.
24  */
25
26 #import "config.h"
27 #import "UIScriptController.h"
28
29 #import "EventSerializerMac.h"
30 #import "PlatformWebView.h"
31 #import "SharedEventStreamsMac.h"
32 #import "TestController.h"
33 #import "PlatformWebView.h"
34 #import "StringFunctions.h"
35 #import "TestController.h"
36 #import "TestRunnerWKWebView.h"
37 #import "UIScriptContext.h"
38 #import <JavaScriptCore/JSContext.h>
39 #import <JavaScriptCore/JSStringRefCF.h>
40 #import <JavaScriptCore/JSValue.h>
41 #import <JavaScriptCore/JavaScriptCore.h>
42 #import <JavaScriptCore/OpaqueJSString.h>
43 #import <WebKit/WKWebViewPrivate.h>
44
45 namespace WTR {
46
47 NSString *nsString(JSStringRef string)
48 {
49     return CFBridgingRelease(JSStringCopyCFString(kCFAllocatorDefault, string));
50 }
51
52 void UIScriptController::doAsyncTask(JSValueRef callback)
53 {
54     unsigned callbackID = m_context->prepareForAsyncTask(callback, CallbackTypeNonPersistent);
55
56     dispatch_async(dispatch_get_main_queue(), ^{
57         if (!m_context)
58             return;
59         m_context->asyncTaskComplete(callbackID);
60     });
61 }
62
63 void UIScriptController::doAfterPresentationUpdate(JSValueRef callback)
64 {
65     return doAsyncTask(callback);
66 }
67
68 void UIScriptController::doAfterNextStablePresentationUpdate(JSValueRef callback)
69 {
70     doAsyncTask(callback);
71 }
72
73 void UIScriptController::doAfterVisibleContentRectUpdate(JSValueRef callback)
74 {
75     doAsyncTask(callback);
76 }
77
78 void UIScriptController::replaceTextAtRange(JSStringRef text, int location, int length)
79 {
80 #if WK_API_ENABLED
81     auto* webView = TestController::singleton().mainWebView()->platformView();
82     [webView _insertText:nsString(text) replacementRange:NSMakeRange(location == -1 ? NSNotFound : location, length)];
83 #else
84     UNUSED_PARAM(text);
85     UNUSED_PARAM(location);
86     UNUSED_PARAM(length);
87 #endif
88 }
89
90 void UIScriptController::zoomToScale(double scale, JSValueRef callback)
91 {
92 #if WK_API_ENABLED
93     unsigned callbackID = m_context->prepareForAsyncTask(callback, CallbackTypeNonPersistent);
94
95     auto* webView = TestController::singleton().mainWebView()->platformView();
96     [webView _setPageScale:scale withOrigin:CGPointZero];
97
98     [webView _doAfterNextPresentationUpdate: ^ {
99         if (!m_context)
100             return;
101         m_context->asyncTaskComplete(callbackID);
102     }];
103 #else
104     UNUSED_PARAM(scale);
105     UNUSED_PARAM(callback);
106 #endif
107 }
108
109 void UIScriptController::simulateAccessibilitySettingsChangeNotification(JSValueRef callback)
110 {
111 #if WK_API_ENABLED
112     unsigned callbackID = m_context->prepareForAsyncTask(callback, CallbackTypeNonPersistent);
113
114     auto* webView = TestController::singleton().mainWebView()->platformView();
115     NSNotificationCenter *center = [[NSWorkspace sharedWorkspace] notificationCenter];
116     [center postNotificationName:NSWorkspaceAccessibilityDisplayOptionsDidChangeNotification object:webView];
117
118     [webView _doAfterNextPresentationUpdate: ^{
119         if (!m_context)
120             return;
121         m_context->asyncTaskComplete(callbackID);
122     }];
123 #else
124     UNUSED_PARAM(callback);
125 #endif
126 }
127
128 JSObjectRef UIScriptController::contentsOfUserInterfaceItem(JSStringRef interfaceItem) const
129 {
130 #if WK_API_ENABLED
131     TestRunnerWKWebView *webView = TestController::singleton().mainWebView()->platformView();
132     NSDictionary *contentDictionary = [webView _contentsOfUserInterfaceItem:toWTFString(toWK(interfaceItem))];
133     return JSValueToObject(m_context->jsContext(), [JSValue valueWithObject:contentDictionary inContext:[JSContext contextWithJSGlobalContextRef:m_context->jsContext()]].JSValueRef, nullptr);
134 #else
135     UNUSED_PARAM(interfaceItem);
136     return nullptr;
137 #endif
138 }
139
140 void UIScriptController::overridePreference(JSStringRef preferenceRef, JSStringRef valueRef)
141 {
142 #if WK_API_ENABLED
143     TestRunnerWKWebView *webView = TestController::singleton().mainWebView()->platformView();
144     WKPreferences *preferences = webView.configuration.preferences;
145
146     String preference = toWTFString(toWK(preferenceRef));
147     String value = toWTFString(toWK(valueRef));
148     if (preference == "WebKitMinimumFontSize")
149         preferences.minimumFontSize = value.toDouble();
150 #else
151     UNUSED_PARAM(preferenceRef);
152     UNUSED_PARAM(valueRef);
153 #endif
154 }
155
156 void UIScriptController::simulateRotation(DeviceOrientation*, JSValueRef)
157 {
158 }
159
160 void UIScriptController::simulateRotationLikeSafari(DeviceOrientation*, JSValueRef)
161 {
162 }
163
164 void UIScriptController::findString(JSStringRef, unsigned long options, unsigned long maxCount)
165 {
166 }
167
168 bool UIScriptController::isShowingDataListSuggestions() const
169 {
170 #if WK_API_ENABLED
171     TestRunnerWKWebView *webView = TestController::singleton().mainWebView()->platformView();
172     for (NSWindow *childWindow in webView.window.childWindows) {
173         if ([childWindow isKindOfClass:NSClassFromString(@"WKDataListSuggestionWindow")])
174             return true;
175     }
176 #endif
177     return false;
178 }
179
180 void UIScriptController::removeViewFromWindow(JSValueRef callback)
181 {
182 #if WK_API_ENABLED
183     unsigned callbackID = m_context->prepareForAsyncTask(callback, CallbackTypeNonPersistent);
184
185     auto* mainWebView = TestController::singleton().mainWebView();
186     mainWebView->removeFromWindow();
187
188     [mainWebView->platformView() _doAfterNextPresentationUpdate: ^ {
189         if (!m_context)
190             return;
191         m_context->asyncTaskComplete(callbackID);
192     }];
193 #else
194     UNUSED_PARAM(callback);
195 #endif
196 }
197
198 void UIScriptController::addViewToWindow(JSValueRef callback)
199 {
200 #if WK_API_ENABLED
201     unsigned callbackID = m_context->prepareForAsyncTask(callback, CallbackTypeNonPersistent);
202
203     auto* mainWebView = TestController::singleton().mainWebView();
204     mainWebView->addToWindow();
205
206     [mainWebView->platformView() _doAfterNextPresentationUpdate: ^ {
207         if (!m_context)
208             return;
209         m_context->asyncTaskComplete(callbackID);
210     }];
211 #else
212     UNUSED_PARAM(callback);
213 #endif
214 }
215
216 static void playBackEvents(UIScriptContext *context, NSString *eventStream, JSValueRef callback)
217 {
218     NSError *error = nil;
219     NSArray *eventDicts = [NSJSONSerialization JSONObjectWithData:[eventStream dataUsingEncoding:NSUTF8StringEncoding] options:0 error:&error];
220
221     if (error) {
222         NSLog(@"ERROR: %@", error);
223         return;
224     }
225
226     unsigned callbackID = context->prepareForAsyncTask(callback, CallbackTypeNonPersistent);
227
228     NSWindow *window = [TestController::singleton().mainWebView()->platformView() window];
229
230     [EventStreamPlayer playStream:eventDicts window:window completionHandler:^ {
231         context->asyncTaskComplete(callbackID);
232     }];
233 }
234
235 void UIScriptController::beginBackSwipe(JSValueRef callback)
236 {
237     playBackEvents(m_context, beginSwipeBackEventStream(), callback);
238 }
239
240 void UIScriptController::completeBackSwipe(JSValueRef callback)
241 {
242     playBackEvents(m_context, completeSwipeBackEventStream(), callback);
243 }
244
245 void UIScriptController::platformPlayBackEventStream(JSStringRef eventStream, JSValueRef callback)
246 {
247     RetainPtr<CFStringRef> stream = adoptCF(JSStringCopyCFString(kCFAllocatorDefault, eventStream));
248     playBackEvents(m_context, (__bridge NSString *)stream.get(), callback);
249 }
250
251 void UIScriptController::firstResponderSuppressionForWebView(bool shouldSuppress)
252 {
253 #if WK_API_ENABLED
254     auto* webView = TestController::singleton().mainWebView()->platformView();
255     [webView _setShouldSuppressFirstResponderChanges:shouldSuppress];
256 #else
257     UNUSED_PARAM(shouldSuppress);
258 #endif
259 }
260
261 void UIScriptController::makeWindowContentViewFirstResponder()
262 {
263     NSWindow *window = [TestController::singleton().mainWebView()->platformView() window];
264     [window makeFirstResponder:[window contentView]];
265 }
266
267 bool UIScriptController::isWindowContentViewFirstResponder() const
268 {
269     NSWindow *window = [TestController::singleton().mainWebView()->platformView() window];
270     return [window firstResponder] == [window contentView];
271 }
272
273 } // namespace WTR