d86e8e199abbd5040deedecdb135708db7f74b22
[WebKit-https.git] / Tools / WebKitTestRunner / TestInvocation.cpp
1 /*
2  * Copyright (C) 2010-2016 Apple Inc. All rights reserved.
3  * Copyright (C) 2012 Intel Corporation. All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  *
14  * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
15  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
16  * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
17  * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
18  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
19  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
20  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
21  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
22  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
23  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
24  * THE POSSIBILITY OF SUCH DAMAGE.
25  */
26
27 #include "config.h"
28 #include "TestInvocation.h"
29
30 #include "PlatformWebView.h"
31 #include "StringFunctions.h"
32 #include "TestController.h"
33 #include "UIScriptController.h"
34 #include <WebKit/WKContextPrivate.h>
35 #include <WebKit/WKCookieManager.h>
36 #include <WebKit/WKData.h>
37 #include <WebKit/WKDictionary.h>
38 #include <WebKit/WKInspector.h>
39 #include <WebKit/WKPagePrivate.h>
40 #include <WebKit/WKRetainPtr.h>
41 #include <WebKit/WKWebsiteDataStoreRef.h>
42 #include <climits>
43 #include <cstdio>
44 #include <wtf/StdLibExtras.h>
45 #include <wtf/text/CString.h>
46
47 #if PLATFORM(MAC) && !PLATFORM(IOS)
48 #include <Carbon/Carbon.h>
49 #endif
50
51 #if PLATFORM(COCOA)
52 #include <WebKit/WKPagePrivateMac.h>
53 #endif
54
55 using namespace JSC;
56 using namespace WebKit;
57 using namespace std;
58
59 namespace WTR {
60
61 TestInvocation::TestInvocation(WKURLRef url, const TestOptions& options)
62     : m_options(options)
63     , m_url(url)
64 {
65     WKRetainPtr<WKStringRef> urlString = adoptWK(WKURLCopyString(m_url.get()));
66
67     size_t stringLength = WKStringGetLength(urlString.get());
68
69     Vector<char> urlVector;
70     urlVector.resize(stringLength + 1);
71
72     WKStringGetUTF8CString(urlString.get(), urlVector.data(), stringLength + 1);
73
74     m_urlString = String(urlVector.data(), stringLength);
75 }
76
77 TestInvocation::~TestInvocation()
78 {
79     if (m_pendingUIScriptInvocationData)
80         m_pendingUIScriptInvocationData->testInvocation = nullptr;
81 }
82
83 WKURLRef TestInvocation::url() const
84 {
85     return m_url.get();
86 }
87
88 bool TestInvocation::urlContains(const char* searchString) const
89 {
90     return m_urlString.contains(searchString, false);
91 }
92
93 void TestInvocation::setIsPixelTest(const std::string& expectedPixelHash)
94 {
95     m_dumpPixels = true;
96     m_expectedPixelHash = expectedPixelHash;
97 }
98
99 bool TestInvocation::shouldLogFrameLoadDelegates() const
100 {
101     return urlContains("loading/");
102 }
103
104 bool TestInvocation::shouldLogHistoryClientCallbacks() const
105 {
106     return urlContains("globalhistory/");
107 }
108
109 void TestInvocation::invoke()
110 {
111     TestController::singleton().configureViewForTest(*this);
112
113     WKPageSetAddsVisitedLinks(TestController::singleton().mainWebView()->page(), false);
114
115     m_textOutput.clear();
116
117     TestController::singleton().setShouldLogHistoryClientCallbacks(shouldLogHistoryClientCallbacks());
118
119     WKCookieManagerSetHTTPCookieAcceptPolicy(WKContextGetCookieManager(TestController::singleton().context()), kWKHTTPCookieAcceptPolicyOnlyFromMainDocumentDomain);
120
121     // FIXME: We should clear out visited links here.
122
123     WKRetainPtr<WKStringRef> messageName = adoptWK(WKStringCreateWithUTF8CString("BeginTest"));
124     WKRetainPtr<WKMutableDictionaryRef> beginTestMessageBody = adoptWK(WKMutableDictionaryCreate());
125
126     WKRetainPtr<WKStringRef> dumpFrameLoadDelegatesKey = adoptWK(WKStringCreateWithUTF8CString("DumpFrameLoadDelegates"));
127     WKRetainPtr<WKBooleanRef> dumpFrameLoadDelegatesValue = adoptWK(WKBooleanCreate(shouldLogFrameLoadDelegates()));
128     WKDictionarySetItem(beginTestMessageBody.get(), dumpFrameLoadDelegatesKey.get(), dumpFrameLoadDelegatesValue.get());
129
130     WKRetainPtr<WKStringRef> useFlexibleViewportKey = adoptWK(WKStringCreateWithUTF8CString("UseFlexibleViewport"));
131     WKRetainPtr<WKBooleanRef> useFlexibleViewportValue = adoptWK(WKBooleanCreate(options().useFlexibleViewport));
132     WKDictionarySetItem(beginTestMessageBody.get(), useFlexibleViewportKey.get(), useFlexibleViewportValue.get());
133
134     WKRetainPtr<WKStringRef> dumpPixelsKey = adoptWK(WKStringCreateWithUTF8CString("DumpPixels"));
135     WKRetainPtr<WKBooleanRef> dumpPixelsValue = adoptWK(WKBooleanCreate(m_dumpPixels));
136     WKDictionarySetItem(beginTestMessageBody.get(), dumpPixelsKey.get(), dumpPixelsValue.get());
137
138     WKRetainPtr<WKStringRef> useWaitToDumpWatchdogTimerKey = adoptWK(WKStringCreateWithUTF8CString("UseWaitToDumpWatchdogTimer"));
139     WKRetainPtr<WKBooleanRef> useWaitToDumpWatchdogTimerValue = adoptWK(WKBooleanCreate(TestController::singleton().useWaitToDumpWatchdogTimer()));
140     WKDictionarySetItem(beginTestMessageBody.get(), useWaitToDumpWatchdogTimerKey.get(), useWaitToDumpWatchdogTimerValue.get());
141
142     WKRetainPtr<WKStringRef> timeoutKey = adoptWK(WKStringCreateWithUTF8CString("Timeout"));
143     WKRetainPtr<WKUInt64Ref> timeoutValue = adoptWK(WKUInt64Create(m_timeout));
144     WKDictionarySetItem(beginTestMessageBody.get(), timeoutKey.get(), timeoutValue.get());
145
146     WKPagePostMessageToInjectedBundle(TestController::singleton().mainWebView()->page(), messageName.get(), beginTestMessageBody.get());
147
148     bool shouldOpenExternalURLs = false;
149
150     TestController::singleton().runUntil(m_gotInitialResponse, TestController::shortTimeout);
151     if (!m_gotInitialResponse) {
152         m_errorMessage = "Timed out waiting for initial response from web process\n";
153         m_webProcessIsUnresponsive = true;
154         goto end;
155     }
156     if (m_error)
157         goto end;
158
159     WKPageLoadURLWithShouldOpenExternalURLsPolicy(TestController::singleton().mainWebView()->page(), m_url.get(), shouldOpenExternalURLs);
160
161     TestController::singleton().runUntil(m_gotFinalMessage, TestController::noTimeout);
162     if (m_error)
163         goto end;
164
165     dumpResults();
166
167 end:
168 #if !PLATFORM(IOS)
169     if (m_gotInitialResponse)
170         WKInspectorClose(WKPageGetInspector(TestController::singleton().mainWebView()->page()));
171 #endif // !PLATFORM(IOS)
172
173     if (m_webProcessIsUnresponsive)
174         dumpWebProcessUnresponsiveness();
175     else if (TestController::singleton().resetStateToConsistentValues(m_options))
176         return;
177     else
178         dumpWebProcessUnresponsiveness("TestController::resetStateToConsistentValues failed\n");
179
180     // The process is unresponsive, so let's start a new one.
181     TestController::singleton().terminateWebContentProcess();
182     // Make sure that we have a process, as invoke() will need one to send bundle messages for the next test.
183     TestController::singleton().reattachPageToWebProcess();
184 }
185
186 void TestInvocation::dumpWebProcessUnresponsiveness()
187 {
188     dumpWebProcessUnresponsiveness(m_errorMessage.c_str());
189 }
190
191 void TestInvocation::dumpWebProcessUnresponsiveness(const char* errorMessage)
192 {
193     fprintf(stderr, "%s", errorMessage);
194     char errorMessageToStderr[1024];
195 #if PLATFORM(COCOA)
196     pid_t pid = WKPageGetProcessIdentifier(TestController::singleton().mainWebView()->page());
197     sprintf(errorMessageToStderr, "#PROCESS UNRESPONSIVE - %s (pid %ld)\n", TestController::webProcessName(), static_cast<long>(pid));
198 #else
199     sprintf(errorMessageToStderr, "#PROCESS UNRESPONSIVE - %s", TestController::webProcessName());
200 #endif
201
202     dump(errorMessage, errorMessageToStderr, true);
203 }
204
205 void TestInvocation::dump(const char* textToStdout, const char* textToStderr, bool seenError)
206 {
207     printf("Content-Type: text/plain\n");
208     if (textToStdout)
209         fputs(textToStdout, stdout);
210     if (textToStderr)
211         fputs(textToStderr, stderr);
212
213     fputs("#EOF\n", stdout);
214     fputs("#EOF\n", stderr);
215     if (seenError)
216         fputs("#EOF\n", stdout);
217     fflush(stdout);
218     fflush(stderr);
219 }
220
221 void TestInvocation::forceRepaintDoneCallback(WKErrorRef error, void* context)
222 {
223     // The context may not be valid any more, e.g. if WebKit is invalidating callbacks at process exit.
224     if (error)
225         return;
226
227     TestInvocation* testInvocation = static_cast<TestInvocation*>(context);
228     RELEASE_ASSERT(TestController::singleton().isCurrentInvocation(testInvocation));
229
230     testInvocation->m_gotRepaint = true;
231     TestController::singleton().notifyDone();
232 }
233
234 void TestInvocation::dumpResults()
235 {
236     if (m_textOutput.length() || !m_audioResult)
237         dump(m_textOutput.toString().utf8().data());
238     else
239         dumpAudio(m_audioResult.get());
240
241     if (m_dumpPixels) {
242         if (m_pixelResult)
243             dumpPixelsAndCompareWithExpected(m_pixelResult.get(), m_repaintRects.get(), TestInvocation::SnapshotResultType::WebContents);
244         else if (m_pixelResultIsPending) {
245             m_gotRepaint = false;
246             WKPageForceRepaint(TestController::singleton().mainWebView()->page(), this, TestInvocation::forceRepaintDoneCallback);
247             TestController::singleton().runUntil(m_gotRepaint, TestController::shortTimeout);
248             if (!m_gotRepaint) {
249                 m_errorMessage = "Timed out waiting for pre-pixel dump repaint\n";
250                 m_webProcessIsUnresponsive = true;
251                 return;
252             }
253             WKRetainPtr<WKImageRef> windowSnapshot = TestController::singleton().mainWebView()->windowSnapshotImage();
254             ASSERT(windowSnapshot);
255             dumpPixelsAndCompareWithExpected(windowSnapshot.get(), m_repaintRects.get(), TestInvocation::SnapshotResultType::WebView);
256         }
257     }
258
259     fputs("#EOF\n", stdout);
260     fflush(stdout);
261     fflush(stderr);
262 }
263
264 void TestInvocation::dumpAudio(WKDataRef audioData)
265 {
266     size_t length = WKDataGetSize(audioData);
267     if (!length)
268         return;
269
270     const unsigned char* data = WKDataGetBytes(audioData);
271
272     printf("Content-Type: audio/wav\n");
273     printf("Content-Length: %lu\n", static_cast<unsigned long>(length));
274
275     fwrite(data, 1, length, stdout);
276     printf("#EOF\n");
277     fprintf(stderr, "#EOF\n");
278 }
279
280 bool TestInvocation::compareActualHashToExpectedAndDumpResults(const char actualHash[33])
281 {
282     // Compute the hash of the bitmap context pixels
283     fprintf(stdout, "\nActualHash: %s\n", actualHash);
284
285     if (!m_expectedPixelHash.length())
286         return false;
287
288     ASSERT(m_expectedPixelHash.length() == 32);
289     fprintf(stdout, "\nExpectedHash: %s\n", m_expectedPixelHash.c_str());
290
291     // FIXME: Do case insensitive compare.
292     return m_expectedPixelHash == actualHash;
293 }
294
295 void TestInvocation::didReceiveMessageFromInjectedBundle(WKStringRef messageName, WKTypeRef messageBody)
296 {
297     if (WKStringIsEqualToUTF8CString(messageName, "Error")) {
298         // Set all states to true to stop spinning the runloop.
299         m_gotInitialResponse = true;
300         m_gotFinalMessage = true;
301         m_error = true;
302         m_errorMessage = "FAIL\n";
303         TestController::singleton().notifyDone();
304         return;
305     }
306
307     if (WKStringIsEqualToUTF8CString(messageName, "Ack")) {
308         ASSERT(WKGetTypeID(messageBody) == WKStringGetTypeID());
309         WKStringRef messageBodyString = static_cast<WKStringRef>(messageBody);
310         if (WKStringIsEqualToUTF8CString(messageBodyString, "BeginTest")) {
311             m_gotInitialResponse = true;
312             TestController::singleton().notifyDone();
313             return;
314         }
315
316         ASSERT_NOT_REACHED();
317     }
318
319     if (WKStringIsEqualToUTF8CString(messageName, "Done")) {
320         ASSERT(WKGetTypeID(messageBody) == WKDictionaryGetTypeID());
321         WKDictionaryRef messageBodyDictionary = static_cast<WKDictionaryRef>(messageBody);
322
323         WKRetainPtr<WKStringRef> pixelResultIsPendingKey = adoptWK(WKStringCreateWithUTF8CString("PixelResultIsPending"));
324         WKBooleanRef pixelResultIsPending = static_cast<WKBooleanRef>(WKDictionaryGetItemForKey(messageBodyDictionary, pixelResultIsPendingKey.get()));
325         m_pixelResultIsPending = WKBooleanGetValue(pixelResultIsPending);
326
327         if (!m_pixelResultIsPending) {
328             WKRetainPtr<WKStringRef> pixelResultKey = adoptWK(WKStringCreateWithUTF8CString("PixelResult"));
329             m_pixelResult = static_cast<WKImageRef>(WKDictionaryGetItemForKey(messageBodyDictionary, pixelResultKey.get()));
330             ASSERT(!m_pixelResult || m_dumpPixels);
331         }
332
333         WKRetainPtr<WKStringRef> repaintRectsKey = adoptWK(WKStringCreateWithUTF8CString("RepaintRects"));
334         m_repaintRects = static_cast<WKArrayRef>(WKDictionaryGetItemForKey(messageBodyDictionary, repaintRectsKey.get()));
335
336         WKRetainPtr<WKStringRef> audioResultKey =  adoptWK(WKStringCreateWithUTF8CString("AudioResult"));
337         m_audioResult = static_cast<WKDataRef>(WKDictionaryGetItemForKey(messageBodyDictionary, audioResultKey.get()));
338
339         m_gotFinalMessage = true;
340         TestController::singleton().notifyDone();
341         return;
342     }
343
344     if (WKStringIsEqualToUTF8CString(messageName, "TextOutput")) {
345         ASSERT(WKGetTypeID(messageBody) == WKStringGetTypeID());
346         WKStringRef textOutput = static_cast<WKStringRef>(messageBody);
347         m_textOutput.append(toWTFString(textOutput));
348         return;
349     }
350
351     if (WKStringIsEqualToUTF8CString(messageName, "BeforeUnloadReturnValue")) {
352         ASSERT(WKGetTypeID(messageBody) == WKBooleanGetTypeID());
353         WKBooleanRef beforeUnloadReturnValue = static_cast<WKBooleanRef>(messageBody);
354         TestController::singleton().setBeforeUnloadReturnValue(WKBooleanGetValue(beforeUnloadReturnValue));
355         return;
356     }
357     
358     if (WKStringIsEqualToUTF8CString(messageName, "AddChromeInputField")) {
359         TestController::singleton().mainWebView()->addChromeInputField();
360         WKRetainPtr<WKStringRef> messageName = adoptWK(WKStringCreateWithUTF8CString("CallAddChromeInputFieldCallback"));
361         WKPagePostMessageToInjectedBundle(TestController::singleton().mainWebView()->page(), messageName.get(), 0);
362         return;
363     }
364
365     if (WKStringIsEqualToUTF8CString(messageName, "RemoveChromeInputField")) {
366         TestController::singleton().mainWebView()->removeChromeInputField();
367         WKRetainPtr<WKStringRef> messageName = adoptWK(WKStringCreateWithUTF8CString("CallRemoveChromeInputFieldCallback"));
368         WKPagePostMessageToInjectedBundle(TestController::singleton().mainWebView()->page(), messageName.get(), 0);
369         return;
370     }
371     
372     if (WKStringIsEqualToUTF8CString(messageName, "FocusWebView")) {
373         TestController::singleton().mainWebView()->makeWebViewFirstResponder();
374         WKRetainPtr<WKStringRef> messageName = adoptWK(WKStringCreateWithUTF8CString("CallFocusWebViewCallback"));
375         WKPagePostMessageToInjectedBundle(TestController::singleton().mainWebView()->page(), messageName.get(), 0);
376         return;
377     }
378
379     if (WKStringIsEqualToUTF8CString(messageName, "SetBackingScaleFactor")) {
380         ASSERT(WKGetTypeID(messageBody) == WKDoubleGetTypeID());
381         double backingScaleFactor = WKDoubleGetValue(static_cast<WKDoubleRef>(messageBody));
382         WKPageSetCustomBackingScaleFactor(TestController::singleton().mainWebView()->page(), backingScaleFactor);
383
384         WKRetainPtr<WKStringRef> messageName = adoptWK(WKStringCreateWithUTF8CString("CallSetBackingScaleFactorCallback"));
385         WKPagePostMessageToInjectedBundle(TestController::singleton().mainWebView()->page(), messageName.get(), 0);
386         return;
387     }
388
389     if (WKStringIsEqualToUTF8CString(messageName, "SimulateWebNotificationClick")) {
390         ASSERT(WKGetTypeID(messageBody) == WKUInt64GetTypeID());
391         uint64_t notificationID = WKUInt64GetValue(static_cast<WKUInt64Ref>(messageBody));
392         TestController::singleton().simulateWebNotificationClick(notificationID);
393         return;
394     }
395
396     if (WKStringIsEqualToUTF8CString(messageName, "SetAddsVisitedLinks")) {
397         ASSERT(WKGetTypeID(messageBody) == WKBooleanGetTypeID());
398         WKBooleanRef enabledWK = static_cast<WKBooleanRef>(messageBody);
399         WKPageSetAddsVisitedLinks(TestController::singleton().mainWebView()->page(), WKBooleanGetValue(enabledWK));
400         return;
401     }
402
403     if (WKStringIsEqualToUTF8CString(messageName, "SetGeolocationPermission")) {
404         ASSERT(WKGetTypeID(messageBody) == WKBooleanGetTypeID());
405         WKBooleanRef enabledWK = static_cast<WKBooleanRef>(messageBody);
406         TestController::singleton().setGeolocationPermission(WKBooleanGetValue(enabledWK));
407         return;
408     }
409
410     if (WKStringIsEqualToUTF8CString(messageName, "SetMockGeolocationPosition")) {
411         ASSERT(WKGetTypeID(messageBody) == WKDictionaryGetTypeID());
412         WKDictionaryRef messageBodyDictionary = static_cast<WKDictionaryRef>(messageBody);
413
414         WKRetainPtr<WKStringRef> latitudeKeyWK(AdoptWK, WKStringCreateWithUTF8CString("latitude"));
415         WKDoubleRef latitudeWK = static_cast<WKDoubleRef>(WKDictionaryGetItemForKey(messageBodyDictionary, latitudeKeyWK.get()));
416         double latitude = WKDoubleGetValue(latitudeWK);
417
418         WKRetainPtr<WKStringRef> longitudeKeyWK(AdoptWK, WKStringCreateWithUTF8CString("longitude"));
419         WKDoubleRef longitudeWK = static_cast<WKDoubleRef>(WKDictionaryGetItemForKey(messageBodyDictionary, longitudeKeyWK.get()));
420         double longitude = WKDoubleGetValue(longitudeWK);
421
422         WKRetainPtr<WKStringRef> accuracyKeyWK(AdoptWK, WKStringCreateWithUTF8CString("accuracy"));
423         WKDoubleRef accuracyWK = static_cast<WKDoubleRef>(WKDictionaryGetItemForKey(messageBodyDictionary, accuracyKeyWK.get()));
424         double accuracy = WKDoubleGetValue(accuracyWK);
425
426         WKRetainPtr<WKStringRef> providesAltitudeKeyWK(AdoptWK, WKStringCreateWithUTF8CString("providesAltitude"));
427         WKBooleanRef providesAltitudeWK = static_cast<WKBooleanRef>(WKDictionaryGetItemForKey(messageBodyDictionary, providesAltitudeKeyWK.get()));
428         bool providesAltitude = WKBooleanGetValue(providesAltitudeWK);
429
430         WKRetainPtr<WKStringRef> altitudeKeyWK(AdoptWK, WKStringCreateWithUTF8CString("altitude"));
431         WKDoubleRef altitudeWK = static_cast<WKDoubleRef>(WKDictionaryGetItemForKey(messageBodyDictionary, altitudeKeyWK.get()));
432         double altitude = WKDoubleGetValue(altitudeWK);
433
434         WKRetainPtr<WKStringRef> providesAltitudeAccuracyKeyWK(AdoptWK, WKStringCreateWithUTF8CString("providesAltitudeAccuracy"));
435         WKBooleanRef providesAltitudeAccuracyWK = static_cast<WKBooleanRef>(WKDictionaryGetItemForKey(messageBodyDictionary, providesAltitudeAccuracyKeyWK.get()));
436         bool providesAltitudeAccuracy = WKBooleanGetValue(providesAltitudeAccuracyWK);
437
438         WKRetainPtr<WKStringRef> altitudeAccuracyKeyWK(AdoptWK, WKStringCreateWithUTF8CString("altitudeAccuracy"));
439         WKDoubleRef altitudeAccuracyWK = static_cast<WKDoubleRef>(WKDictionaryGetItemForKey(messageBodyDictionary, altitudeAccuracyKeyWK.get()));
440         double altitudeAccuracy = WKDoubleGetValue(altitudeAccuracyWK);
441
442         WKRetainPtr<WKStringRef> providesHeadingKeyWK(AdoptWK, WKStringCreateWithUTF8CString("providesHeading"));
443         WKBooleanRef providesHeadingWK = static_cast<WKBooleanRef>(WKDictionaryGetItemForKey(messageBodyDictionary, providesHeadingKeyWK.get()));
444         bool providesHeading = WKBooleanGetValue(providesHeadingWK);
445
446         WKRetainPtr<WKStringRef> headingKeyWK(AdoptWK, WKStringCreateWithUTF8CString("heading"));
447         WKDoubleRef headingWK = static_cast<WKDoubleRef>(WKDictionaryGetItemForKey(messageBodyDictionary, headingKeyWK.get()));
448         double heading = WKDoubleGetValue(headingWK);
449
450         WKRetainPtr<WKStringRef> providesSpeedKeyWK(AdoptWK, WKStringCreateWithUTF8CString("providesSpeed"));
451         WKBooleanRef providesSpeedWK = static_cast<WKBooleanRef>(WKDictionaryGetItemForKey(messageBodyDictionary, providesSpeedKeyWK.get()));
452         bool providesSpeed = WKBooleanGetValue(providesSpeedWK);
453
454         WKRetainPtr<WKStringRef> speedKeyWK(AdoptWK, WKStringCreateWithUTF8CString("speed"));
455         WKDoubleRef speedWK = static_cast<WKDoubleRef>(WKDictionaryGetItemForKey(messageBodyDictionary, speedKeyWK.get()));
456         double speed = WKDoubleGetValue(speedWK);
457
458         TestController::singleton().setMockGeolocationPosition(latitude, longitude, accuracy, providesAltitude, altitude, providesAltitudeAccuracy, altitudeAccuracy, providesHeading, heading, providesSpeed, speed);
459         return;
460     }
461
462     if (WKStringIsEqualToUTF8CString(messageName, "SetMockGeolocationPositionUnavailableError")) {
463         ASSERT(WKGetTypeID(messageBody) == WKStringGetTypeID());
464         WKStringRef errorMessage = static_cast<WKStringRef>(messageBody);
465         TestController::singleton().setMockGeolocationPositionUnavailableError(errorMessage);
466         return;
467     }
468
469     if (WKStringIsEqualToUTF8CString(messageName, "SetUserMediaPermission")) {
470         ASSERT(WKGetTypeID(messageBody) == WKBooleanGetTypeID());
471         WKBooleanRef enabledWK = static_cast<WKBooleanRef>(messageBody);
472         TestController::singleton().setUserMediaPermission(WKBooleanGetValue(enabledWK));
473         return;
474     }
475
476     if (WKStringIsEqualToUTF8CString(messageName, "SetUserMediaPermissionForOrigin")) {
477         ASSERT(WKGetTypeID(messageBody) == WKDictionaryGetTypeID());
478         WKDictionaryRef messageBodyDictionary = static_cast<WKDictionaryRef>(messageBody);
479
480         WKRetainPtr<WKStringRef> permissionKeyWK(AdoptWK, WKStringCreateWithUTF8CString("permission"));
481         WKBooleanRef permissionWK = static_cast<WKBooleanRef>(WKDictionaryGetItemForKey(messageBodyDictionary, permissionKeyWK.get()));
482         bool permission = WKBooleanGetValue(permissionWK);
483
484         WKRetainPtr<WKStringRef> originKey(AdoptWK, WKStringCreateWithUTF8CString("origin"));
485         WKStringRef originWK = static_cast<WKStringRef>(WKDictionaryGetItemForKey(messageBodyDictionary, originKey.get()));
486
487         WKRetainPtr<WKStringRef> parentOriginKey(AdoptWK, WKStringCreateWithUTF8CString("parentOrigin"));
488         WKStringRef parentOriginWK = static_cast<WKStringRef>(WKDictionaryGetItemForKey(messageBodyDictionary, parentOriginKey.get()));
489
490         TestController::singleton().setUserMediaPermissionForOrigin(permission, originWK, parentOriginWK);
491         return;
492     }
493
494     if (WKStringIsEqualToUTF8CString(messageName, "SetCacheModel")) {
495         ASSERT(WKGetTypeID(messageBody) == WKUInt64GetTypeID());
496         uint64_t model = WKUInt64GetValue(static_cast<WKUInt64Ref>(messageBody));
497         WKContextSetCacheModel(TestController::singleton().context(), model);
498         return;
499     }
500
501     if (WKStringIsEqualToUTF8CString(messageName, "SetCustomPolicyDelegate")) {
502         ASSERT(WKGetTypeID(messageBody) == WKDictionaryGetTypeID());
503         WKDictionaryRef messageBodyDictionary = static_cast<WKDictionaryRef>(messageBody);
504
505         WKRetainPtr<WKStringRef> enabledKeyWK(AdoptWK, WKStringCreateWithUTF8CString("enabled"));
506         WKBooleanRef enabledWK = static_cast<WKBooleanRef>(WKDictionaryGetItemForKey(messageBodyDictionary, enabledKeyWK.get()));
507         bool enabled = WKBooleanGetValue(enabledWK);
508
509         WKRetainPtr<WKStringRef> permissiveKeyWK(AdoptWK, WKStringCreateWithUTF8CString("permissive"));
510         WKBooleanRef permissiveWK = static_cast<WKBooleanRef>(WKDictionaryGetItemForKey(messageBodyDictionary, permissiveKeyWK.get()));
511         bool permissive = WKBooleanGetValue(permissiveWK);
512
513         TestController::singleton().setCustomPolicyDelegate(enabled, permissive);
514         return;
515     }
516
517     if (WKStringIsEqualToUTF8CString(messageName, "SetHidden")) {
518         ASSERT(WKGetTypeID(messageBody) == WKDictionaryGetTypeID());
519         WKDictionaryRef messageBodyDictionary = static_cast<WKDictionaryRef>(messageBody);
520
521         WKRetainPtr<WKStringRef> isInitialKeyWK(AdoptWK, WKStringCreateWithUTF8CString("hidden"));
522         WKBooleanRef hiddenWK = static_cast<WKBooleanRef>(WKDictionaryGetItemForKey(messageBodyDictionary, isInitialKeyWK.get()));
523         bool hidden = WKBooleanGetValue(hiddenWK);
524
525         TestController::singleton().setHidden(hidden);
526         return;
527     }
528
529     if (WKStringIsEqualToUTF8CString(messageName, "ProcessWorkQueue")) {
530         if (TestController::singleton().workQueueManager().processWorkQueue()) {
531             WKRetainPtr<WKStringRef> messageName = adoptWK(WKStringCreateWithUTF8CString("WorkQueueProcessedCallback"));
532             WKPagePostMessageToInjectedBundle(TestController::singleton().mainWebView()->page(), messageName.get(), 0);
533         }
534         return;
535     }
536
537     if (WKStringIsEqualToUTF8CString(messageName, "QueueBackNavigation")) {
538         ASSERT(WKGetTypeID(messageBody) == WKUInt64GetTypeID());
539         uint64_t stepCount = WKUInt64GetValue(static_cast<WKUInt64Ref>(messageBody));
540         TestController::singleton().workQueueManager().queueBackNavigation(stepCount);
541         return;
542     }
543
544     if (WKStringIsEqualToUTF8CString(messageName, "QueueForwardNavigation")) {
545         ASSERT(WKGetTypeID(messageBody) == WKUInt64GetTypeID());
546         uint64_t stepCount = WKUInt64GetValue(static_cast<WKUInt64Ref>(messageBody));
547         TestController::singleton().workQueueManager().queueForwardNavigation(stepCount);
548         return;
549     }
550
551     if (WKStringIsEqualToUTF8CString(messageName, "QueueLoad")) {
552         ASSERT(WKGetTypeID(messageBody) == WKDictionaryGetTypeID());
553         WKDictionaryRef loadDataDictionary = static_cast<WKDictionaryRef>(messageBody);
554
555         WKRetainPtr<WKStringRef> urlKey(AdoptWK, WKStringCreateWithUTF8CString("url"));
556         WKStringRef urlWK = static_cast<WKStringRef>(WKDictionaryGetItemForKey(loadDataDictionary, urlKey.get()));
557
558         WKRetainPtr<WKStringRef> targetKey(AdoptWK, WKStringCreateWithUTF8CString("target"));
559         WKStringRef targetWK = static_cast<WKStringRef>(WKDictionaryGetItemForKey(loadDataDictionary, targetKey.get()));
560
561         WKRetainPtr<WKStringRef> shouldOpenExternalURLsKey(AdoptWK, WKStringCreateWithUTF8CString("shouldOpenExternalURLs"));
562         WKBooleanRef shouldOpenExternalURLsValueWK = static_cast<WKBooleanRef>(WKDictionaryGetItemForKey(loadDataDictionary, shouldOpenExternalURLsKey.get()));
563
564         TestController::singleton().workQueueManager().queueLoad(toWTFString(urlWK), toWTFString(targetWK), WKBooleanGetValue(shouldOpenExternalURLsValueWK));
565         return;
566     }
567
568     if (WKStringIsEqualToUTF8CString(messageName, "QueueLoadHTMLString")) {
569         ASSERT(WKGetTypeID(messageBody) == WKDictionaryGetTypeID());
570         WKDictionaryRef loadDataDictionary = static_cast<WKDictionaryRef>(messageBody);
571
572         WKRetainPtr<WKStringRef> contentKey(AdoptWK, WKStringCreateWithUTF8CString("content"));
573         WKStringRef contentWK = static_cast<WKStringRef>(WKDictionaryGetItemForKey(loadDataDictionary, contentKey.get()));
574
575         WKRetainPtr<WKStringRef> baseURLKey(AdoptWK, WKStringCreateWithUTF8CString("baseURL"));
576         WKStringRef baseURLWK = static_cast<WKStringRef>(WKDictionaryGetItemForKey(loadDataDictionary, baseURLKey.get()));
577
578         WKRetainPtr<WKStringRef> unreachableURLKey(AdoptWK, WKStringCreateWithUTF8CString("unreachableURL"));
579         WKStringRef unreachableURLWK = static_cast<WKStringRef>(WKDictionaryGetItemForKey(loadDataDictionary, unreachableURLKey.get()));
580
581         TestController::singleton().workQueueManager().queueLoadHTMLString(toWTFString(contentWK), baseURLWK ? toWTFString(baseURLWK) : String(), unreachableURLWK ? toWTFString(unreachableURLWK) : String());
582         return;
583     }
584
585     if (WKStringIsEqualToUTF8CString(messageName, "QueueReload")) {
586         TestController::singleton().workQueueManager().queueReload();
587         return;
588     }
589
590     if (WKStringIsEqualToUTF8CString(messageName, "QueueLoadingScript")) {
591         ASSERT(WKGetTypeID(messageBody) == WKStringGetTypeID());
592         WKStringRef script = static_cast<WKStringRef>(messageBody);
593         TestController::singleton().workQueueManager().queueLoadingScript(toWTFString(script));
594         return;
595     }
596
597     if (WKStringIsEqualToUTF8CString(messageName, "QueueNonLoadingScript")) {
598         ASSERT(WKGetTypeID(messageBody) == WKStringGetTypeID());
599         WKStringRef script = static_cast<WKStringRef>(messageBody);
600         TestController::singleton().workQueueManager().queueNonLoadingScript(toWTFString(script));
601         return;
602     }
603
604     if (WKStringIsEqualToUTF8CString(messageName, "SetHandlesAuthenticationChallenge")) {
605         ASSERT(WKGetTypeID(messageBody) == WKBooleanGetTypeID());
606         WKBooleanRef value = static_cast<WKBooleanRef>(messageBody);
607         TestController::singleton().setHandlesAuthenticationChallenges(WKBooleanGetValue(value));
608         return;
609     }
610
611     if (WKStringIsEqualToUTF8CString(messageName, "SetAuthenticationUsername")) {
612         ASSERT(WKGetTypeID(messageBody) == WKStringGetTypeID());
613         WKStringRef username = static_cast<WKStringRef>(messageBody);
614         TestController::singleton().setAuthenticationUsername(toWTFString(username));
615         return;
616     }
617
618     if (WKStringIsEqualToUTF8CString(messageName, "SetAuthenticationPassword")) {
619         ASSERT(WKGetTypeID(messageBody) == WKStringGetTypeID());
620         WKStringRef password = static_cast<WKStringRef>(messageBody);
621         TestController::singleton().setAuthenticationPassword(toWTFString(password));
622         return;
623     }
624
625     if (WKStringIsEqualToUTF8CString(messageName, "SetBlockAllPlugins")) {
626         ASSERT(WKGetTypeID(messageBody) == WKBooleanGetTypeID());
627         WKBooleanRef shouldBlock = static_cast<WKBooleanRef>(messageBody);
628         TestController::singleton().setBlockAllPlugins(WKBooleanGetValue(shouldBlock));
629         return;
630     }
631
632     if (WKStringIsEqualToUTF8CString(messageName, "SetShouldDecideNavigationPolicyAfterDelay")) {
633         ASSERT(WKGetTypeID(messageBody) == WKBooleanGetTypeID());
634         WKBooleanRef value = static_cast<WKBooleanRef>(messageBody);
635         TestController::singleton().setShouldDecideNavigationPolicyAfterDelay(WKBooleanGetValue(value));
636         return;
637     }
638
639     if (WKStringIsEqualToUTF8CString(messageName, "SetNavigationGesturesEnabled")) {
640         ASSERT(WKGetTypeID(messageBody) == WKBooleanGetTypeID());
641         WKBooleanRef value = static_cast<WKBooleanRef>(messageBody);
642         TestController::singleton().setNavigationGesturesEnabled(WKBooleanGetValue(value));
643         return;
644     }
645
646     if (WKStringIsEqualToUTF8CString(messageName, "RunUIProcessScript")) {
647         WKDictionaryRef messageBodyDictionary = static_cast<WKDictionaryRef>(messageBody);
648         WKRetainPtr<WKStringRef> scriptKey(AdoptWK, WKStringCreateWithUTF8CString("Script"));
649         WKRetainPtr<WKStringRef> callbackIDKey(AdoptWK, WKStringCreateWithUTF8CString("CallbackID"));
650
651         UIScriptInvocationData* invocationData = new UIScriptInvocationData();
652         invocationData->testInvocation = this;
653         invocationData->callbackID = (unsigned)WKUInt64GetValue(static_cast<WKUInt64Ref>(WKDictionaryGetItemForKey(messageBodyDictionary, callbackIDKey.get())));
654         invocationData->scriptString = static_cast<WKStringRef>(WKDictionaryGetItemForKey(messageBodyDictionary, scriptKey.get()));
655         m_pendingUIScriptInvocationData = invocationData;
656         WKPageCallAfterNextPresentationUpdate(TestController::singleton().mainWebView()->page(), invocationData, runUISideScriptAfterUpdateCallback);
657         return;
658     }
659
660     ASSERT_NOT_REACHED();
661 }
662
663 WKRetainPtr<WKTypeRef> TestInvocation::didReceiveSynchronousMessageFromInjectedBundle(WKStringRef messageName, WKTypeRef messageBody)
664 {
665     if (WKStringIsEqualToUTF8CString(messageName, "SetWindowIsKey")) {
666         ASSERT(WKGetTypeID(messageBody) == WKBooleanGetTypeID());
667         WKBooleanRef isKeyValue = static_cast<WKBooleanRef>(messageBody);
668         TestController::singleton().mainWebView()->setWindowIsKey(WKBooleanGetValue(isKeyValue));
669         return nullptr;
670     }
671
672     if (WKStringIsEqualToUTF8CString(messageName, "SetViewSize")) {
673         ASSERT(WKGetTypeID(messageBody) == WKDictionaryGetTypeID());
674
675         WKDictionaryRef messageBodyDictionary = static_cast<WKDictionaryRef>(messageBody);
676         WKRetainPtr<WKStringRef> widthKey(AdoptWK, WKStringCreateWithUTF8CString("width"));
677         WKRetainPtr<WKStringRef> heightKey(AdoptWK, WKStringCreateWithUTF8CString("height"));
678
679         WKDoubleRef widthWK = static_cast<WKDoubleRef>(WKDictionaryGetItemForKey(messageBodyDictionary, widthKey.get()));
680         WKDoubleRef heightWK = static_cast<WKDoubleRef>(WKDictionaryGetItemForKey(messageBodyDictionary, heightKey.get()));
681
682         TestController::singleton().mainWebView()->resizeTo(WKDoubleGetValue(widthWK), WKDoubleGetValue(heightWK));
683         return nullptr;
684     }
685
686     if (WKStringIsEqualToUTF8CString(messageName, "IsGeolocationClientActive")) {
687         bool isActive = TestController::singleton().isGeolocationProviderActive();
688         WKRetainPtr<WKTypeRef> result(AdoptWK, WKBooleanCreate(isActive));
689         return result;
690     }
691
692     if (WKStringIsEqualToUTF8CString(messageName, "IsWorkQueueEmpty")) {
693         bool isEmpty = TestController::singleton().workQueueManager().isWorkQueueEmpty();
694         WKRetainPtr<WKTypeRef> result(AdoptWK, WKBooleanCreate(isEmpty));
695         return result;
696     }
697
698     if (WKStringIsEqualToUTF8CString(messageName, "SecureEventInputIsEnabled")) {
699 #if PLATFORM(MAC) && !PLATFORM(IOS)
700         WKRetainPtr<WKBooleanRef> result(AdoptWK, WKBooleanCreate(IsSecureEventInputEnabled()));
701 #else
702         WKRetainPtr<WKBooleanRef> result(AdoptWK, WKBooleanCreate(false));
703 #endif
704         return result;
705     }
706
707     if (WKStringIsEqualToUTF8CString(messageName, "SetAlwaysAcceptCookies")) {
708         WKBooleanRef accept = static_cast<WKBooleanRef>(messageBody);
709         WKHTTPCookieAcceptPolicy policy = WKBooleanGetValue(accept) ? kWKHTTPCookieAcceptPolicyAlways : kWKHTTPCookieAcceptPolicyOnlyFromMainDocumentDomain;
710         // FIXME: This updates the policy in WebProcess and in NetworkProcess asynchronously, which might break some tests' expectations.
711         WKCookieManagerSetHTTPCookieAcceptPolicy(WKContextGetCookieManager(TestController::singleton().context()), policy);
712         return nullptr;
713     }
714
715     if (WKStringIsEqualToUTF8CString(messageName, "ImageCountInGeneralPasteboard")) {
716         unsigned count = TestController::singleton().imageCountInGeneralPasteboard();
717         WKRetainPtr<WKUInt64Ref> result(AdoptWK, WKUInt64Create(count));
718         return result;
719     }
720     
721     if (WKStringIsEqualToUTF8CString(messageName, "DeleteAllIndexedDatabases")) {
722         WKWebsiteDataStoreRemoveAllIndexedDatabases(WKContextGetWebsiteDataStore(TestController::singleton().context()));
723         return nullptr;
724     }
725
726     ASSERT_NOT_REACHED();
727     return nullptr;
728 }
729
730 void TestInvocation::runUISideScriptAfterUpdateCallback(WKErrorRef, void* context)
731 {
732     UIScriptInvocationData* data = static_cast<UIScriptInvocationData*>(context);
733     if (TestInvocation* invocation = data->testInvocation) {
734         RELEASE_ASSERT(TestController::singleton().isCurrentInvocation(invocation));
735         invocation->runUISideScript(data->scriptString.get(), data->callbackID);
736     }
737     delete data;
738 }
739
740 void TestInvocation::runUISideScript(WKStringRef script, unsigned scriptCallbackID)
741 {
742     m_pendingUIScriptInvocationData = nullptr;
743
744     if (!m_UIScriptContext)
745         m_UIScriptContext = std::make_unique<UIScriptContext>(*this);
746     
747     m_UIScriptContext->runUIScript(script, scriptCallbackID);
748 }
749
750 void TestInvocation::uiScriptDidComplete(WKStringRef result, unsigned scriptCallbackID)
751 {
752     WKRetainPtr<WKStringRef> messageName = adoptWK(WKStringCreateWithUTF8CString("CallUISideScriptCallback"));
753
754     WKRetainPtr<WKMutableDictionaryRef> messageBody(AdoptWK, WKMutableDictionaryCreate());
755     WKRetainPtr<WKStringRef> resultKey(AdoptWK, WKStringCreateWithUTF8CString("Result"));
756     WKRetainPtr<WKStringRef> callbackIDKey(AdoptWK, WKStringCreateWithUTF8CString("CallbackID"));
757     WKRetainPtr<WKUInt64Ref> callbackIDValue = adoptWK(WKUInt64Create(scriptCallbackID));
758
759     WKDictionarySetItem(messageBody.get(), resultKey.get(), result);
760     WKDictionarySetItem(messageBody.get(), callbackIDKey.get(), callbackIDValue.get());
761
762     WKPagePostMessageToInjectedBundle(TestController::singleton().mainWebView()->page(), messageName.get(), messageBody.get());
763 }
764
765 void TestInvocation::outputText(const WTF::String& text)
766 {
767     m_textOutput.append(text);
768 }
769
770 void TestInvocation::didBeginSwipe()
771 {
772     WKRetainPtr<WKStringRef> messageName = adoptWK(WKStringCreateWithUTF8CString("CallDidBeginSwipeCallback"));
773     WKPagePostMessageToInjectedBundle(TestController::singleton().mainWebView()->page(), messageName.get(), 0);
774 }
775
776 void TestInvocation::willEndSwipe()
777 {
778     WKRetainPtr<WKStringRef> messageName = adoptWK(WKStringCreateWithUTF8CString("CallWillEndSwipeCallback"));
779     WKPagePostMessageToInjectedBundle(TestController::singleton().mainWebView()->page(), messageName.get(), 0);
780 }
781
782 void TestInvocation::didEndSwipe()
783 {
784     WKRetainPtr<WKStringRef> messageName = adoptWK(WKStringCreateWithUTF8CString("CallDidEndSwipeCallback"));
785     WKPagePostMessageToInjectedBundle(TestController::singleton().mainWebView()->page(), messageName.get(), 0);
786 }
787
788 void TestInvocation::didRemoveSwipeSnapshot()
789 {
790     WKRetainPtr<WKStringRef> messageName = adoptWK(WKStringCreateWithUTF8CString("CallDidRemoveSwipeSnapshotCallback"));
791     WKPagePostMessageToInjectedBundle(TestController::singleton().mainWebView()->page(), messageName.get(), 0);
792 }
793
794 void TestInvocation::notifyDownloadDone()
795 {
796     WKRetainPtr<WKStringRef> messageName = adoptWK(WKStringCreateWithUTF8CString("NotifyDownloadDone"));
797     WKPagePostMessageToInjectedBundle(TestController::singleton().mainWebView()->page(), messageName.get(), 0);
798 }
799
800 } // namespace WTR