Address NSWindow sometimes using WebKitTestRunnerEvent too early
[WebKit-https.git] / Tools / WebKitTestRunner / TestController.cpp
1 /*
2  * Copyright (C) 2010-2018 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 #include "config.h"
27 #include "TestController.h"
28
29 #include "EventSenderProxy.h"
30 #include "Options.h"
31 #include "PlatformWebView.h"
32 #include "StringFunctions.h"
33 #include "TestInvocation.h"
34 #include "WebCoreTestSupport.h"
35 #include <JavaScriptCore/InitializeThreading.h>
36 #include <WebKit/WKArray.h>
37 #include <WebKit/WKAuthenticationChallenge.h>
38 #include <WebKit/WKAuthenticationDecisionListener.h>
39 #include <WebKit/WKContextConfigurationRef.h>
40 #include <WebKit/WKContextPrivate.h>
41 #include <WebKit/WKCookieManager.h>
42 #include <WebKit/WKCredential.h>
43 #include <WebKit/WKFrameHandleRef.h>
44 #include <WebKit/WKFrameInfoRef.h>
45 #include <WebKit/WKIconDatabase.h>
46 #include <WebKit/WKMockDisplay.h>
47 #include <WebKit/WKMockMediaDevice.h>
48 #include <WebKit/WKNavigationActionRef.h>
49 #include <WebKit/WKNavigationResponseRef.h>
50 #include <WebKit/WKNotification.h>
51 #include <WebKit/WKNotificationManager.h>
52 #include <WebKit/WKNotificationPermissionRequest.h>
53 #include <WebKit/WKNumber.h>
54 #include <WebKit/WKOpenPanelResultListener.h>
55 #include <WebKit/WKPageGroup.h>
56 #include <WebKit/WKPageInjectedBundleClient.h>
57 #include <WebKit/WKPagePrivate.h>
58 #include <WebKit/WKPluginInformation.h>
59 #include <WebKit/WKPreferencesRefPrivate.h>
60 #include <WebKit/WKProtectionSpace.h>
61 #include <WebKit/WKRetainPtr.h>
62 #include <WebKit/WKSecurityOriginRef.h>
63 #include <WebKit/WKTextChecker.h>
64 #include <WebKit/WKUserContentControllerRef.h>
65 #include <WebKit/WKUserContentExtensionStoreRef.h>
66 #include <WebKit/WKUserMediaPermissionCheck.h>
67 #include <WebKit/WKWebsiteDataStoreRef.h>
68 #include <algorithm>
69 #include <cstdio>
70 #include <ctype.h>
71 #include <fstream>
72 #include <stdlib.h>
73 #include <string>
74 #include <wtf/AutodrainedPool.h>
75 #include <wtf/CryptographicallyRandomNumber.h>
76 #include <wtf/MainThread.h>
77 #include <wtf/ProcessPrivilege.h>
78 #include <wtf/RefCounted.h>
79 #include <wtf/RunLoop.h>
80 #include <wtf/SetForScope.h>
81 #include <wtf/UUID.h>
82 #include <wtf/text/CString.h>
83 #include <wtf/text/StringConcatenateNumbers.h>
84
85 #if PLATFORM(COCOA)
86 #include <WebKit/WKContextPrivateMac.h>
87 #include <WebKit/WKPagePrivateMac.h>
88 #endif
89
90 #if PLATFORM(WIN)
91 #include <direct.h>
92 #define getcwd _getcwd
93 #define PATH_MAX _MAX_PATH
94 #else
95 #include <unistd.h>
96 #endif
97
98 namespace WTR {
99
100 #if OS(WINDOWS)
101 static constexpr auto pathSeparator = '\\';
102 #else
103 static constexpr auto pathSeparator = '/';
104 #endif
105
106 const unsigned TestController::viewWidth = 800;
107 const unsigned TestController::viewHeight = 600;
108
109 const unsigned TestController::w3cSVGViewWidth = 480;
110 const unsigned TestController::w3cSVGViewHeight = 360;
111
112 const WTF::Seconds TestController::defaultShortTimeout = 5_s;
113 const WTF::Seconds TestController::noTimeout = -1_s;
114
115 static WKURLRef blankURL()
116 {
117     static WKURLRef staticBlankURL = WKURLCreateWithUTF8CString("about:blank");
118     return staticBlankURL;
119 }
120
121 static WKDataRef copyWebCryptoMasterKey(WKPageRef, const void*)
122 {
123     // Any 128 bit key would do, all we need for testing is to implement the callback.
124     return WKDataCreate((const uint8_t*)"\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f", 16);
125 }
126
127 static WKStringRef copySignedPublicKeyAndChallengeString(WKPageRef, const void*)
128 {
129     // Any fake response would do, all we need for testing is to implement the callback.
130     return WKStringCreateWithUTF8CString("MIHFMHEwXDANBgkqhkiG9w0BAQEFAANLADBIAkEAnX0TILJrOMUue%2BPtwBRE6XfV%0AWtKQbsshxk5ZhcUwcwyvcnIq9b82QhJdoACdD34rqfCAIND46fXKQUnb0mvKzQID%0AAQABFhFNb3ppbGxhSXNNeUZyaWVuZDANBgkqhkiG9w0BAQQFAANBAAKv2Eex2n%2FS%0Ar%2F7iJNroWlSzSMtTiQTEB%2BADWHGj9u1xrUrOilq%2Fo2cuQxIfZcNZkYAkWP4DubqW%0Ai0%2F%2FrgBvmco%3D");
131 }
132
133 AsyncTask* AsyncTask::m_currentTask;
134
135 bool AsyncTask::run()
136 {
137     m_currentTask = this;
138     m_task();
139     TestController::singleton().runUntil(m_taskDone, m_timeout);
140     m_currentTask = nullptr;
141     return m_taskDone;
142 }
143
144 AsyncTask* AsyncTask::currentTask()
145 {
146     return m_currentTask;
147 }
148
149 static TestController* controller;
150
151 TestController& TestController::singleton()
152 {
153     ASSERT(controller);
154     return *controller;
155 }
156
157 TestController::TestController(int argc, const char* argv[])
158 {
159     initialize(argc, argv);
160     controller = this;
161     run();
162     controller = nullptr;
163 }
164
165 TestController::~TestController()
166 {
167     // The context will be null if WebKitTestRunner was in server mode, but ran no tests.
168     if (m_context)
169         WKIconDatabaseClose(WKContextGetIconDatabase(m_context.get()));
170
171     platformDestroy();
172 }
173
174 static WKRect getWindowFrame(WKPageRef page, const void* clientInfo)
175 {
176     PlatformWebView* view = static_cast<PlatformWebView*>(const_cast<void*>(clientInfo));
177     return view->windowFrame();
178 }
179
180 static void setWindowFrame(WKPageRef page, WKRect frame, const void* clientInfo)
181 {
182     PlatformWebView* view = static_cast<PlatformWebView*>(const_cast<void*>(clientInfo));
183     view->setWindowFrame(frame);
184 }
185
186 static bool runBeforeUnloadConfirmPanel(WKPageRef page, WKStringRef message, WKFrameRef frame, const void*)
187 {
188     printf("CONFIRM NAVIGATION: %s\n", toSTD(message).c_str());
189     return TestController::singleton().beforeUnloadReturnValue();
190 }
191
192 static void runOpenPanel(WKPageRef page, WKFrameRef frame, WKOpenPanelParametersRef parameters, WKOpenPanelResultListenerRef resultListenerRef, const void*)
193 {
194     printf("OPEN FILE PANEL\n");
195     if (WKOpenPanelParametersGetAllowsDirectories(parameters))
196         printf("-> DIRECTORIES ARE ALLOWED\n");
197     WKArrayRef fileURLs = TestController::singleton().openPanelFileURLs();
198     if (!fileURLs || !WKArrayGetSize(fileURLs)) {
199         WKOpenPanelResultListenerCancel(resultListenerRef);
200         return;
201     }
202
203     if (WKOpenPanelParametersGetAllowsMultipleFiles(parameters)) {
204         WKOpenPanelResultListenerChooseFiles(resultListenerRef, fileURLs);
205         return;
206     }
207
208     WKTypeRef firstItem = WKArrayGetItemAtIndex(fileURLs, 0);
209     WKOpenPanelResultListenerChooseFiles(resultListenerRef, adoptWK(WKArrayCreate(&firstItem, 1)).get());
210 }
211
212 void TestController::runModal(WKPageRef page, const void* clientInfo)
213 {
214     PlatformWebView* view = static_cast<PlatformWebView*>(const_cast<void*>(clientInfo));
215     view->setWindowIsKey(false);
216     runModal(view);
217     view->setWindowIsKey(true);
218 }
219
220 static void closeOtherPage(WKPageRef page, const void* clientInfo)
221 {
222     WKPageClose(page);
223     PlatformWebView* view = static_cast<PlatformWebView*>(const_cast<void*>(clientInfo));
224     delete view;
225 }
226
227 static void focus(WKPageRef page, const void* clientInfo)
228 {
229     PlatformWebView* view = static_cast<PlatformWebView*>(const_cast<void*>(clientInfo));
230     view->focus();
231     view->setWindowIsKey(true);
232 }
233
234 static void unfocus(WKPageRef page, const void* clientInfo)
235 {
236     PlatformWebView* view = static_cast<PlatformWebView*>(const_cast<void*>(clientInfo));
237     view->setWindowIsKey(false);
238 }
239
240 static void decidePolicyForGeolocationPermissionRequest(WKPageRef, WKFrameRef, WKSecurityOriginRef, WKGeolocationPermissionRequestRef permissionRequest, const void* clientInfo)
241 {
242     TestController::singleton().handleGeolocationPermissionRequest(permissionRequest);
243 }
244
245 static void decidePolicyForUserMediaPermissionRequest(WKPageRef, WKFrameRef frame, WKSecurityOriginRef userMediaDocumentOrigin, WKSecurityOriginRef topLevelDocumentOrigin, WKUserMediaPermissionRequestRef permissionRequest, const void* clientInfo)
246 {
247     TestController::singleton().handleUserMediaPermissionRequest(frame, userMediaDocumentOrigin, topLevelDocumentOrigin, permissionRequest);
248 }
249
250 static void runJavaScriptAlert(WKPageRef page, WKStringRef alertText, WKFrameRef frame, WKSecurityOriginRef securityOrigin, WKPageRunJavaScriptAlertResultListenerRef listener, const void *clientInfo)
251 {
252     TestController::singleton().handleJavaScriptAlert(listener);
253 }
254
255 static void checkUserMediaPermissionForOrigin(WKPageRef, WKFrameRef frame, WKSecurityOriginRef userMediaDocumentOrigin, WKSecurityOriginRef topLevelDocumentOrigin, WKUserMediaPermissionCheckRef checkRequest, const void*)
256 {
257     TestController::singleton().handleCheckOfUserMediaPermissionForOrigin(frame, userMediaDocumentOrigin, topLevelDocumentOrigin, checkRequest);
258 }
259
260 static void requestPointerLock(WKPageRef page, const void*)
261 {
262     WKPageDidAllowPointerLock(page);
263 }
264
265 static bool shouldAllowDeviceOrientationAndMotionAccess(WKPageRef, WKSecurityOriginRef origin, const void*)
266 {
267     return TestController::singleton().handleDeviceOrientationAndMotionAccessRequest(origin);
268 }
269
270 WKPageRef TestController::createOtherPage(WKPageRef, WKPageConfigurationRef configuration, WKNavigationActionRef navigationAction, WKWindowFeaturesRef windowFeatures, const void *clientInfo)
271 {
272     PlatformWebView* parentView = static_cast<PlatformWebView*>(const_cast<void*>(clientInfo));
273     return TestController::singleton().createOtherPage(parentView, configuration, navigationAction, windowFeatures);
274 }
275
276 WKPageRef TestController::createOtherPage(PlatformWebView* parentView, WKPageConfigurationRef configuration, WKNavigationActionRef navigationAction, WKWindowFeaturesRef windowFeatures)
277 {
278     // The test needs to call testRunner.setCanOpenWindows() to open new windows.
279     if (!m_currentInvocation->canOpenWindows())
280         return nullptr;
281
282     PlatformWebView* view = platformCreateOtherPage(parentView, configuration, parentView->options());
283     WKPageRef newPage = view->page();
284
285     view->resizeTo(800, 600);
286
287     WKPageUIClientV8 otherPageUIClient = {
288         { 8, view },
289         0, // createNewPage_deprecatedForUseWithV0
290         0, // showPage
291         closeOtherPage,
292         0, // takeFocus
293         focus,
294         unfocus,
295         0, // runJavaScriptAlert_deprecatedForUseWithV0
296         0, // runJavaScriptAlert_deprecatedForUseWithV0
297         0, // runJavaScriptAlert_deprecatedForUseWithV0
298         0, // setStatusText
299         0, // mouseDidMoveOverElement_deprecatedForUseWithV0
300         0, // missingPluginButtonClicked
301         0, // didNotHandleKeyEvent
302         0, // didNotHandleWheelEvent
303         0, // toolbarsAreVisible
304         0, // setToolbarsAreVisible
305         0, // menuBarIsVisible
306         0, // setMenuBarIsVisible
307         0, // statusBarIsVisible
308         0, // setStatusBarIsVisible
309         0, // isResizable
310         0, // setIsResizable
311         getWindowFrame,
312         setWindowFrame,
313         runBeforeUnloadConfirmPanel,
314         0, // didDraw
315         0, // pageDidScroll
316         0, // exceededDatabaseQuota
317         runOpenPanel,
318         decidePolicyForGeolocationPermissionRequest,
319         0, // headerHeight
320         0, // footerHeight
321         0, // drawHeader
322         0, // drawFooter
323         0, // printFrame
324         runModal,
325         0, // didCompleteRubberBandForMainFrame
326         0, // saveDataToFileInDownloadsFolder
327         0, // shouldInterruptJavaScript
328         0, // createNewPage_deprecatedForUseWithV1
329         0, // mouseDidMoveOverElement
330         0, // decidePolicyForNotificationPermissionRequest
331         0, // unavailablePluginButtonClicked_deprecatedForUseWithV1
332         0, // showColorPicker
333         0, // hideColorPicker
334         0, // unavailablePluginButtonClicked
335         0, // pinnedStateDidChange
336         0, // didBeginTrackingPotentialLongMousePress
337         0, // didRecognizeLongMousePress
338         0, // didCancelTrackingPotentialLongMousePress
339         0, // isPlayingAudioDidChange
340         decidePolicyForUserMediaPermissionRequest,
341         0, // didClickAutofillButton
342         0, // runJavaScriptAlert
343         0, // runJavaScriptConfirm
344         0, // runJavaScriptPrompt
345         0, // mediaSessionMetadataDidChange
346         createOtherPage,
347         runJavaScriptAlert,
348         0, // runJavaScriptConfirm
349         0, // runJavaScriptPrompt
350         checkUserMediaPermissionForOrigin,
351         0, // runBeforeUnloadConfirmPanel
352         0, // fullscreenMayReturnToInline
353         requestPointerLock,
354         0,
355     };
356     WKPageSetPageUIClient(newPage, &otherPageUIClient.base);
357     
358     WKPageNavigationClientV3 pageNavigationClient = {
359         { 3, &TestController::singleton() },
360         decidePolicyForNavigationAction,
361         decidePolicyForNavigationResponse,
362         decidePolicyForPluginLoad,
363         0, // didStartProvisionalNavigation
364         didReceiveServerRedirectForProvisionalNavigation,
365         0, // didFailProvisionalNavigation
366         0, // didCommitNavigation
367         0, // didFinishNavigation
368         0, // didFailNavigation
369         0, // didFailProvisionalLoadInSubframe
370         0, // didFinishDocumentLoad
371         0, // didSameDocumentNavigation
372         0, // renderingProgressDidChange
373         canAuthenticateAgainstProtectionSpace,
374         didReceiveAuthenticationChallenge,
375         processDidCrash,
376         copyWebCryptoMasterKey,
377         didBeginNavigationGesture,
378         willEndNavigationGesture,
379         didEndNavigationGesture,
380         didRemoveNavigationGestureSnapshot,
381         0, // webProcessDidTerminate
382         0, // contentRuleListNotification
383         copySignedPublicKeyAndChallengeString
384     };
385     WKPageSetPageNavigationClient(newPage, &pageNavigationClient.base);
386
387     view->didInitializeClients();
388
389     TestController::singleton().updateWindowScaleForTest(view, *TestController::singleton().m_currentInvocation);
390
391     WKRetain(newPage);
392     return newPage;
393 }
394
395 const char* TestController::libraryPathForTesting()
396 {
397     // FIXME: This may not be sufficient to prevent interactions/crashes
398     // when running more than one copy of DumpRenderTree.
399     // See https://bugs.webkit.org/show_bug.cgi?id=10906
400     char* dumpRenderTreeTemp = getenv("DUMPRENDERTREE_TEMP");
401     if (dumpRenderTreeTemp)
402         return dumpRenderTreeTemp;
403     return platformLibraryPathForTesting();
404 }
405
406 void TestController::initialize(int argc, const char* argv[])
407 {
408     JSC::initializeThreading();
409     RunLoop::initializeMainRunLoop();
410     WTF::setProcessPrivileges(allPrivileges());
411
412     platformInitialize();
413
414     Options options;
415     OptionsHandler optionsHandler(options);
416
417     if (argc < 2) {
418         optionsHandler.printHelp();
419         exit(1);
420     }
421     if (!optionsHandler.parse(argc, argv))
422         exit(1);
423
424     m_useWaitToDumpWatchdogTimer = options.useWaitToDumpWatchdogTimer;
425     m_forceNoTimeout = options.forceNoTimeout;
426     m_verbose = options.verbose;
427     m_gcBetweenTests = options.gcBetweenTests;
428     m_shouldDumpPixelsForAllTests = options.shouldDumpPixelsForAllTests;
429     m_forceComplexText = options.forceComplexText;
430     m_shouldUseAcceleratedDrawing = options.shouldUseAcceleratedDrawing;
431     m_shouldUseRemoteLayerTree = options.shouldUseRemoteLayerTree;
432     m_paths = options.paths;
433     m_allowedHosts = options.allowedHosts;
434     m_shouldShowWebView = options.shouldShowWebView;
435     m_shouldShowTouches = options.shouldShowTouches;
436     m_checkForWorldLeaks = options.checkForWorldLeaks;
437     m_allowAnyHTTPSCertificateForAllowedHosts = options.allowAnyHTTPSCertificateForAllowedHosts;
438
439     if (options.printSupportedFeatures) {
440         // FIXME: On Windows, DumpRenderTree uses this to expose whether it supports 3d
441         // transforms and accelerated compositing. When we support those features, we
442         // should match DRT's behavior.
443         exit(0);
444     }
445
446     m_usingServerMode = (m_paths.size() == 1 && m_paths[0] == "-");
447     if (m_usingServerMode)
448         m_printSeparators = true;
449     else
450         m_printSeparators = m_paths.size() > 1;
451
452     initializeInjectedBundlePath();
453     initializeTestPluginDirectory();
454
455 #if PLATFORM(MAC)
456     WebCoreTestSupport::installMockGamepadProvider();
457 #endif
458
459     WKRetainPtr<WKStringRef> pageGroupIdentifier(AdoptWK, WKStringCreateWithUTF8CString("WebKitTestRunnerPageGroup"));
460     m_pageGroup.adopt(WKPageGroupCreateWithIdentifier(pageGroupIdentifier.get()));
461
462     m_eventSenderProxy = std::make_unique<EventSenderProxy>(this);
463 }
464
465 WKRetainPtr<WKContextConfigurationRef> TestController::generateContextConfiguration(const TestOptions::ContextOptions& options) const
466 {
467     auto configuration = adoptWK(WKContextConfigurationCreate());
468     WKContextConfigurationSetInjectedBundlePath(configuration.get(), injectedBundlePath());
469     WKContextConfigurationSetFullySynchronousModeIsAllowedForTesting(configuration.get(), true);
470     WKContextConfigurationSetIgnoreSynchronousMessagingTimeoutsForTesting(configuration.get(), options.ignoreSynchronousMessagingTimeouts);
471
472     WKRetainPtr<WKMutableArrayRef> overrideLanguages = adoptWK(WKMutableArrayCreate());
473     for (auto& language : options.overrideLanguages)
474         WKArrayAppendItem(overrideLanguages.get(), adoptWK(WKStringCreateWithUTF8CString(language.utf8().data())).get());
475     WKContextConfigurationSetOverrideLanguages(configuration.get(), overrideLanguages.get());
476
477     if (options.shouldEnableProcessSwapOnNavigation()) {
478         WKContextConfigurationSetProcessSwapsOnNavigation(configuration.get(), true);
479         if (options.enableProcessSwapOnWindowOpen)
480             WKContextConfigurationSetProcessSwapsOnWindowOpenWithOpener(configuration.get(), true);
481     }
482
483     if (const char* dumpRenderTreeTemp = libraryPathForTesting()) {
484         String temporaryFolder = String::fromUTF8(dumpRenderTreeTemp);
485
486         WKContextConfigurationSetApplicationCacheDirectory(configuration.get(), toWK(temporaryFolder + pathSeparator + "ApplicationCache").get());
487         WKContextConfigurationSetDiskCacheDirectory(configuration.get(), toWK(temporaryFolder + pathSeparator + "Cache").get());
488         WKContextConfigurationSetIndexedDBDatabaseDirectory(configuration.get(), toWK(temporaryFolder + pathSeparator + "Databases" + pathSeparator + "IndexedDB").get());
489         WKContextConfigurationSetLocalStorageDirectory(configuration.get(), toWK(temporaryFolder + pathSeparator + "LocalStorage").get());
490         WKContextConfigurationSetWebSQLDatabaseDirectory(configuration.get(), toWK(temporaryFolder + pathSeparator + "Databases" + pathSeparator + "WebSQL").get());
491         WKContextConfigurationSetMediaKeysStorageDirectory(configuration.get(), toWK(temporaryFolder + pathSeparator + "MediaKeys").get());
492         WKContextConfigurationSetResourceLoadStatisticsDirectory(configuration.get(), toWK(temporaryFolder + pathSeparator + "ResourceLoadStatistics").get());
493     }
494     return configuration;
495 }
496
497 WKRetainPtr<WKPageConfigurationRef> TestController::generatePageConfiguration(const TestOptions& options)
498 {
499     if (!m_context || !m_contextOptions->hasSameInitializationOptions(options.contextOptions)) {
500         auto contextConfiguration = generateContextConfiguration(options.contextOptions);
501         m_context = platformAdjustContext(adoptWK(WKContextCreateWithConfiguration(contextConfiguration.get())).get(), contextConfiguration.get());
502         m_contextOptions = options.contextOptions;
503
504         m_geolocationProvider = std::make_unique<GeolocationProviderMock>(m_context.get());
505
506         if (const char* dumpRenderTreeTemp = libraryPathForTesting()) {
507             String temporaryFolder = String::fromUTF8(dumpRenderTreeTemp);
508
509             // FIXME: This should be migrated to WKContextConfigurationRef.
510             // Disable icon database to avoid fetching <http://127.0.0.1:8000/favicon.ico> and making tests flaky.
511             // Invividual tests can enable it using testRunner.setIconDatabaseEnabled, although it's not currently supported in WebKitTestRunner.
512             WKContextSetIconDatabasePath(m_context.get(), toWK(emptyString()).get());
513         }
514
515         WKContextSetDiskCacheSpeculativeValidationEnabled(m_context.get(), true);
516         WKContextUseTestingNetworkSession(m_context.get());
517         WKContextSetCacheModel(m_context.get(), kWKCacheModelDocumentBrowser);
518
519         auto* websiteDataStore = WKContextGetWebsiteDataStore(m_context.get());
520         WKWebsiteDataStoreSetCacheStoragePerOriginQuota(websiteDataStore, 400 * 1024);
521
522         platformInitializeContext();
523     }
524
525     WKContextInjectedBundleClientV1 injectedBundleClient = {
526         { 1, this },
527         didReceiveMessageFromInjectedBundle,
528         didReceiveSynchronousMessageFromInjectedBundle,
529         getInjectedBundleInitializationUserData,
530     };
531     WKContextSetInjectedBundleClient(m_context.get(), &injectedBundleClient.base);
532
533     WKContextClientV2 contextClient = {
534         { 2, this },
535         0, // plugInAutoStartOriginHashesChanged
536         networkProcessDidCrash,
537         0, // plugInInformationBecameAvailable
538         0, // copyWebCryptoMasterKey
539     };
540     WKContextSetClient(m_context.get(), &contextClient.base);
541
542     WKContextHistoryClientV0 historyClient = {
543         { 0, this },
544         didNavigateWithNavigationData,
545         didPerformClientRedirect,
546         didPerformServerRedirect,
547         didUpdateHistoryTitle,
548         0, // populateVisitedLinks
549     };
550     WKContextSetHistoryClient(m_context.get(), &historyClient.base);
551
552     WKNotificationManagerRef notificationManager = WKContextGetNotificationManager(m_context.get());
553     WKNotificationProviderV0 notificationKit = m_webNotificationProvider.provider();
554     WKNotificationManagerSetProvider(notificationManager, &notificationKit.base);
555
556     if (testPluginDirectory())
557         WKContextSetAdditionalPluginsDirectory(m_context.get(), testPluginDirectory());
558
559     if (m_forceComplexText)
560         WKContextSetAlwaysUsesComplexTextCodePath(m_context.get(), true);
561
562     auto pageConfiguration = adoptWK(WKPageConfigurationCreate());
563     WKPageConfigurationSetContext(pageConfiguration.get(), m_context.get());
564     WKPageConfigurationSetPageGroup(pageConfiguration.get(), m_pageGroup.get());
565
566     m_userContentController = adoptWK(WKUserContentControllerCreate());
567     WKPageConfigurationSetUserContentController(pageConfiguration.get(), userContentController());
568     return pageConfiguration;
569 }
570
571 void TestController::createWebViewWithOptions(const TestOptions& options)
572 {
573     auto configuration = generatePageConfiguration(options);
574
575     // Some preferences (notably mock scroll bars setting) currently cannot be re-applied to an existing view, so we need to set them now.
576     // FIXME: Migrate these preferences to WKContextConfigurationRef.
577     resetPreferencesToConsistentValues(options);
578
579     platformCreateWebView(configuration.get(), options);
580     WKPageUIClientV13 pageUIClient = {
581         { 13, m_mainWebView.get() },
582         0, // createNewPage_deprecatedForUseWithV0
583         0, // showPage
584         0, // close
585         0, // takeFocus
586         focus,
587         unfocus,
588         0, // runJavaScriptAlert_deprecatedForUseWithV0
589         0, // runJavaScriptAlert_deprecatedForUseWithV0
590         0, // runJavaScriptAlert_deprecatedForUseWithV0
591         0, // setStatusText
592         0, // mouseDidMoveOverElement_deprecatedForUseWithV0
593         0, // missingPluginButtonClicked
594         0, // didNotHandleKeyEvent
595         0, // didNotHandleWheelEvent
596         0, // toolbarsAreVisible
597         0, // setToolbarsAreVisible
598         0, // menuBarIsVisible
599         0, // setMenuBarIsVisible
600         0, // statusBarIsVisible
601         0, // setStatusBarIsVisible
602         0, // isResizable
603         0, // setIsResizable
604         getWindowFrame,
605         setWindowFrame,
606         runBeforeUnloadConfirmPanel,
607         0, // didDraw
608         0, // pageDidScroll
609         0, // exceededDatabaseQuota,
610         runOpenPanel,
611         decidePolicyForGeolocationPermissionRequest,
612         0, // headerHeight
613         0, // footerHeight
614         0, // drawHeader
615         0, // drawFooter
616         0, // printFrame
617         runModal,
618         0, // didCompleteRubberBandForMainFrame
619         0, // saveDataToFileInDownloadsFolder
620         0, // shouldInterruptJavaScript
621         0, // createNewPage_deprecatedForUseWithV1
622         0, // mouseDidMoveOverElement
623         decidePolicyForNotificationPermissionRequest, // decidePolicyForNotificationPermissionRequest
624         0, // unavailablePluginButtonClicked_deprecatedForUseWithV1
625         0, // showColorPicker
626         0, // hideColorPicker
627         unavailablePluginButtonClicked,
628         0, // pinnedStateDidChange
629         0, // didBeginTrackingPotentialLongMousePress
630         0, // didRecognizeLongMousePress
631         0, // didCancelTrackingPotentialLongMousePress
632         0, // isPlayingAudioDidChange
633         decidePolicyForUserMediaPermissionRequest,
634         0, // didClickAutofillButton
635         0, // runJavaScriptAlert
636         0, // runJavaScriptConfirm
637         0, // runJavaScriptPrompt
638         0, // mediaSessionMetadataDidChange
639         createOtherPage,
640         runJavaScriptAlert,
641         0, // runJavaScriptConfirm
642         0, // runJavaScriptPrompt
643         checkUserMediaPermissionForOrigin,
644         0, // runBeforeUnloadConfirmPanel
645         0, // fullscreenMayReturnToInline
646         requestPointerLock,
647         0, // didLosePointerLock
648         0, // handleAutoplayEvent
649         0, // hasVideoInPictureInPictureDidChange
650         0, // didExceedBackgroundResourceLimitWhileInForeground
651         0, // didResignInputElementStrongPasswordAppearance
652         0, // requestStorageAccessConfirm
653         shouldAllowDeviceOrientationAndMotionAccess
654     };
655     WKPageSetPageUIClient(m_mainWebView->page(), &pageUIClient.base);
656
657     WKPageNavigationClientV3 pageNavigationClient = {
658         { 3, this },
659         decidePolicyForNavigationAction,
660         decidePolicyForNavigationResponse,
661         decidePolicyForPluginLoad,
662         0, // didStartProvisionalNavigation
663         didReceiveServerRedirectForProvisionalNavigation,
664         0, // didFailProvisionalNavigation
665         didCommitNavigation,
666         didFinishNavigation,
667         0, // didFailNavigation
668         0, // didFailProvisionalLoadInSubframe
669         0, // didFinishDocumentLoad
670         0, // didSameDocumentNavigation
671         0, // renderingProgressDidChange
672         canAuthenticateAgainstProtectionSpace,
673         didReceiveAuthenticationChallenge,
674         processDidCrash,
675         copyWebCryptoMasterKey,
676         didBeginNavigationGesture,
677         willEndNavigationGesture,
678         didEndNavigationGesture,
679         didRemoveNavigationGestureSnapshot,
680         0, // webProcessDidTerminate
681         0, // contentRuleListNotification
682         copySignedPublicKeyAndChallengeString
683     };
684     WKPageSetPageNavigationClient(m_mainWebView->page(), &pageNavigationClient.base);
685
686     WKContextDownloadClientV1 downloadClient = {
687         { 1, this },
688         downloadDidStart,
689         0, // didReceiveAuthenticationChallenge
690         0, // didReceiveResponse
691         0, // didReceiveData
692         0, // shouldDecodeSourceDataOfMIMEType
693         decideDestinationWithSuggestedFilename,
694         0, // didCreateDestination
695         downloadDidFinish,
696         downloadDidFail,
697         downloadDidCancel,
698         0, // processDidCrash;
699         downloadDidReceiveServerRedirectToURL
700     };
701     WKContextSetDownloadClient(context(), &downloadClient.base);
702     
703     // this should just be done on the page?
704     WKPageInjectedBundleClientV0 injectedBundleClient = {
705         { 0, this },
706         didReceivePageMessageFromInjectedBundle,
707         didReceiveSynchronousPageMessageFromInjectedBundle
708     };
709     WKPageSetPageInjectedBundleClient(m_mainWebView->page(), &injectedBundleClient.base);
710
711     m_mainWebView->didInitializeClients();
712
713     // Generally, the tests should default to running at 1x. updateWindowScaleForTest() will adjust the scale to
714     // something else for specific tests that need to run at a different window scale.
715     m_mainWebView->changeWindowScaleIfNeeded(1);
716 }
717
718 void TestController::ensureViewSupportsOptionsForTest(const TestInvocation& test)
719 {
720     auto options = test.options();
721
722     if (m_mainWebView) {
723         if (m_mainWebView->viewSupportsOptions(options))
724             return;
725
726         willDestroyWebView();
727
728         WKPageSetPageUIClient(m_mainWebView->page(), nullptr);
729         WKPageSetPageNavigationClient(m_mainWebView->page(), nullptr);
730         WKPageClose(m_mainWebView->page());
731
732         m_mainWebView = nullptr;
733     }
734
735     createWebViewWithOptions(options);
736
737     if (!resetStateToConsistentValues(options, ResetStage::BeforeTest))
738         TestInvocation::dumpWebProcessUnresponsiveness("<unknown> - TestController::run - Failed to reset state to consistent values\n");
739 }
740
741 void TestController::resetPreferencesToConsistentValues(const TestOptions& options)
742 {
743     // Reset preferences
744     WKPreferencesRef preferences = platformPreferences();
745     WKPreferencesResetTestRunnerOverrides(preferences);
746
747     WKPreferencesEnableAllExperimentalFeatures(preferences);
748     for (const auto& experimentalFeature : options.experimentalFeatures)
749         WKPreferencesSetExperimentalFeatureForKey(preferences, experimentalFeature.value, toWK(experimentalFeature.key).get());
750
751     WKPreferencesResetAllInternalDebugFeatures(preferences);
752     // Set internal features that have different default values for testing.
753     static WKStringRef asyncOverflowScrollingFeature = WKStringCreateWithUTF8CString("AsyncOverflowScrollingEnabled");
754     WKPreferencesSetInternalDebugFeatureForKey(preferences, false, asyncOverflowScrollingFeature);
755
756     for (const auto& internalDebugFeature : options.internalDebugFeatures)
757         WKPreferencesSetInternalDebugFeatureForKey(preferences, internalDebugFeature.value, toWK(internalDebugFeature.key).get());
758
759     WKPreferencesSetProcessSwapOnNavigationEnabled(preferences, options.contextOptions.shouldEnableProcessSwapOnNavigation());
760     WKPreferencesSetPageVisibilityBasedProcessSuppressionEnabled(preferences, false);
761     WKPreferencesSetOfflineWebApplicationCacheEnabled(preferences, true);
762     WKPreferencesSetSubpixelAntialiasedLayerTextEnabled(preferences, false);
763     WKPreferencesSetXSSAuditorEnabled(preferences, false);
764     WKPreferencesSetWebAudioEnabled(preferences, true);
765     WKPreferencesSetMediaDevicesEnabled(preferences, true);
766     WKPreferencesSetWebRTCMDNSICECandidatesEnabled(preferences, false);
767     WKPreferencesSetDeveloperExtrasEnabled(preferences, true);
768     WKPreferencesSetJavaScriptRuntimeFlags(preferences, kWKJavaScriptRuntimeFlagsAllEnabled);
769     WKPreferencesSetJavaScriptCanOpenWindowsAutomatically(preferences, true);
770     WKPreferencesSetJavaScriptCanAccessClipboard(preferences, true);
771     WKPreferencesSetDOMPasteAllowed(preferences, options.domPasteAllowed);
772     WKPreferencesSetUniversalAccessFromFileURLsAllowed(preferences, true);
773     WKPreferencesSetFileAccessFromFileURLsAllowed(preferences, true);
774 #if ENABLE(FULLSCREEN_API)
775     WKPreferencesSetFullScreenEnabled(preferences, true);
776 #endif
777     WKPreferencesSetPageCacheEnabled(preferences, false);
778     WKPreferencesSetAsynchronousPluginInitializationEnabled(preferences, false);
779     WKPreferencesSetAsynchronousPluginInitializationEnabledForAllPlugins(preferences, false);
780     WKPreferencesSetArtificialPluginInitializationDelayEnabled(preferences, false);
781     WKPreferencesSetTabToLinksEnabled(preferences, false);
782     WKPreferencesSetInteractiveFormValidationEnabled(preferences, true);
783     WKPreferencesSetDataTransferItemsEnabled(preferences, true);
784     WKPreferencesSetCustomPasteboardDataEnabled(preferences, true);
785
786     WKPreferencesSetMockScrollbarsEnabled(preferences, options.useMockScrollbars);
787     WKPreferencesSetNeedsSiteSpecificQuirks(preferences, options.needsSiteSpecificQuirks);
788     WKPreferencesSetAttachmentElementEnabled(preferences, options.enableAttachmentElement);
789     WKPreferencesSetMenuItemElementEnabled(preferences, options.enableMenuItemElement);
790     WKPreferencesSetModernMediaControlsEnabled(preferences, options.enableModernMediaControls);
791     WKPreferencesSetWebAuthenticationEnabled(preferences, options.enableWebAuthentication);
792     WKPreferencesSetWebAuthenticationLocalAuthenticatorEnabled(preferences, options.enableWebAuthenticationLocalAuthenticator);
793     WKPreferencesSetIsSecureContextAttributeEnabled(preferences, options.enableIsSecureContextAttribute);
794     WKPreferencesSetAllowCrossOriginSubresourcesToAskForCredentials(preferences, options.allowCrossOriginSubresourcesToAskForCredentials);
795     WKPreferencesSetColorFilterEnabled(preferences, options.enableColorFilter);
796     WKPreferencesSetPunchOutWhiteBackgroundsInDarkMode(preferences, options.punchOutWhiteBackgroundsInDarkMode);
797
798     static WKStringRef defaultTextEncoding = WKStringCreateWithUTF8CString("ISO-8859-1");
799     WKPreferencesSetDefaultTextEncodingName(preferences, defaultTextEncoding);
800
801     static WKStringRef standardFontFamily = WKStringCreateWithUTF8CString("Times");
802     static WKStringRef cursiveFontFamily = WKStringCreateWithUTF8CString("Apple Chancery");
803     static WKStringRef fantasyFontFamily = WKStringCreateWithUTF8CString("Papyrus");
804     static WKStringRef fixedFontFamily = WKStringCreateWithUTF8CString("Courier");
805     static WKStringRef pictographFontFamily = WKStringCreateWithUTF8CString("Apple Color Emoji");
806     static WKStringRef sansSerifFontFamily = WKStringCreateWithUTF8CString("Helvetica");
807     static WKStringRef serifFontFamily = WKStringCreateWithUTF8CString("Times");
808
809     WKPreferencesSetMinimumFontSize(preferences, 0);
810     WKPreferencesSetStandardFontFamily(preferences, standardFontFamily);
811     WKPreferencesSetCursiveFontFamily(preferences, cursiveFontFamily);
812     WKPreferencesSetFantasyFontFamily(preferences, fantasyFontFamily);
813     WKPreferencesSetFixedFontFamily(preferences, fixedFontFamily);
814     WKPreferencesSetPictographFontFamily(preferences, pictographFontFamily);
815     WKPreferencesSetSansSerifFontFamily(preferences, sansSerifFontFamily);
816     WKPreferencesSetSerifFontFamily(preferences, serifFontFamily);
817     WKPreferencesSetAsynchronousSpellCheckingEnabled(preferences, false);
818 #if ENABLE(MEDIA_SOURCE)
819     WKPreferencesSetMediaSourceEnabled(preferences, true);
820     WKPreferencesSetSourceBufferChangeTypeEnabled(preferences, true);
821 #endif
822
823     WKPreferencesSetHiddenPageDOMTimerThrottlingEnabled(preferences, false);
824     WKPreferencesSetHiddenPageCSSAnimationSuspensionEnabled(preferences, false);
825
826     WKPreferencesSetAcceleratedDrawingEnabled(preferences, m_shouldUseAcceleratedDrawing || options.useAcceleratedDrawing);
827     // FIXME: We should be testing the default.
828     WKPreferencesSetStorageBlockingPolicy(preferences, kWKAllowAllStorage);
829
830     WKPreferencesSetFetchAPIKeepAliveEnabled(preferences, true);
831     WKPreferencesSetResourceTimingEnabled(preferences, true);
832     WKPreferencesSetUserTimingEnabled(preferences, true);
833     WKPreferencesSetMediaPreloadingEnabled(preferences, true);
834     WKPreferencesSetMediaPlaybackAllowsInline(preferences, true);
835     WKPreferencesSetInlineMediaPlaybackRequiresPlaysInlineAttribute(preferences, false);
836     WKPreferencesSetBeaconAPIEnabled(preferences, true);
837     WKPreferencesSetDirectoryUploadEnabled(preferences, true);
838
839     WKCookieManagerDeleteAllCookies(WKContextGetCookieManager(m_context.get()));
840
841     WKPreferencesSetMockCaptureDevicesEnabled(preferences, true);
842     
843     WKPreferencesSetLargeImageAsyncDecodingEnabled(preferences, false);
844
845     WKPreferencesSetInspectorAdditionsEnabled(preferences, options.enableInspectorAdditions);
846
847     WKPreferencesSetStorageAccessAPIEnabled(preferences, true);
848     
849     WKPreferencesSetAccessibilityObjectModelEnabled(preferences, true);
850     WKPreferencesSetCSSOMViewScrollingAPIEnabled(preferences, true);
851     WKPreferencesSetMediaCapabilitiesEnabled(preferences, true);
852
853     WKPreferencesSetRestrictedHTTPResponseAccess(preferences, true);
854
855     WKPreferencesSetServerTimingEnabled(preferences, true);
856
857     WKPreferencesSetWebSQLDisabled(preferences, false);
858
859     platformResetPreferencesToConsistentValues();
860 }
861
862 bool TestController::resetStateToConsistentValues(const TestOptions& options, ResetStage resetStage)
863 {
864     SetForScope<State> changeState(m_state, Resetting);
865     m_beforeUnloadReturnValue = true;
866
867     // This setting differs between the antique and modern Mac WebKit2 API.
868     // For now, maintain the antique behavior, because some tests depend on it!
869     // FIXME: We should be testing the default.
870     WKPageSetBackgroundExtendsBeyondPage(m_mainWebView->page(), false);
871
872     WKPageSetCustomUserAgent(m_mainWebView->page(), nullptr);
873
874     WKRetainPtr<WKStringRef> messageName = adoptWK(WKStringCreateWithUTF8CString("Reset"));
875     WKRetainPtr<WKMutableDictionaryRef> resetMessageBody = adoptWK(WKMutableDictionaryCreate());
876
877     WKRetainPtr<WKStringRef> shouldGCKey = adoptWK(WKStringCreateWithUTF8CString("ShouldGC"));
878     WKRetainPtr<WKBooleanRef> shouldGCValue = adoptWK(WKBooleanCreate(m_gcBetweenTests));
879     WKDictionarySetItem(resetMessageBody.get(), shouldGCKey.get(), shouldGCValue.get());
880
881     WKRetainPtr<WKStringRef> allowedHostsKey = adoptWK(WKStringCreateWithUTF8CString("AllowedHosts"));
882     WKRetainPtr<WKMutableArrayRef> allowedHostsValue = adoptWK(WKMutableArrayCreate());
883     for (auto& host : m_allowedHosts) {
884         WKRetainPtr<WKStringRef> wkHost = adoptWK(WKStringCreateWithUTF8CString(host.c_str()));
885         WKArrayAppendItem(allowedHostsValue.get(), wkHost.get());
886     }
887     WKDictionarySetItem(resetMessageBody.get(), allowedHostsKey.get(), allowedHostsValue.get());
888
889     if (options.jscOptions.length()) {
890         WKRetainPtr<WKStringRef> jscOptionsKey = adoptWK(WKStringCreateWithUTF8CString("JSCOptions"));
891         WKRetainPtr<WKStringRef> jscOptionsValue = adoptWK(WKStringCreateWithUTF8CString(options.jscOptions.c_str()));
892         WKDictionarySetItem(resetMessageBody.get(), jscOptionsKey.get(), jscOptionsValue.get());
893     }
894
895     WKPagePostMessageToInjectedBundle(TestController::singleton().mainWebView()->page(), messageName.get(), resetMessageBody.get());
896
897     WKContextSetShouldUseFontSmoothing(TestController::singleton().context(), false);
898
899     WKContextSetCacheModel(TestController::singleton().context(), kWKCacheModelDocumentBrowser);
900
901     WKContextClearCachedCredentials(TestController::singleton().context());
902
903     ClearIndexedDatabases();
904     setIDBPerOriginQuota(50 * MB);
905
906     clearServiceWorkerRegistrations();
907     clearDOMCaches();
908
909     WKContextSetAllowsAnySSLCertificateForServiceWorkerTesting(platformContext(), true);
910
911     WKContextClearCurrentModifierStateForTesting(TestController::singleton().context());
912
913     // FIXME: This function should also ensure that there is only one page open.
914
915     // Reset the EventSender for each test.
916     m_eventSenderProxy = std::make_unique<EventSenderProxy>(this);
917
918     // FIXME: Is this needed? Nothing in TestController changes preferences during tests, and if there is
919     // some other code doing this, it should probably be responsible for cleanup too.
920     resetPreferencesToConsistentValues(options);
921
922 #if PLATFORM(GTK)
923     WKTextCheckerContinuousSpellCheckingEnabledStateChanged(true);
924 #endif
925
926     // Make sure the view is in the window (a test can unparent it).
927     m_mainWebView->addToWindow();
928
929     // In the case that a test using the chrome input field failed, be sure to clean up for the next test.
930     m_mainWebView->removeChromeInputField();
931     m_mainWebView->focus();
932
933     // Re-set to the default backing scale factor by setting the custom scale factor to 0.
934     WKPageSetCustomBackingScaleFactor(m_mainWebView->page(), 0);
935
936     WKPageClearWheelEventTestTrigger(m_mainWebView->page());
937
938     WKPageSetMuted(m_mainWebView->page(), true);
939
940     WKPageClearUserMediaState(m_mainWebView->page());
941
942     // Reset notification permissions
943     m_webNotificationProvider.reset();
944
945     // Reset Geolocation permissions.
946     m_geolocationPermissionRequests.clear();
947     m_isGeolocationPermissionSet = false;
948     m_isGeolocationPermissionAllowed = false;
949
950     // Reset UserMedia permissions.
951     m_userMediaPermissionRequests.clear();
952     m_cachedUserMediaPermissions.clear();
953     setUserMediaPermission(true);
954
955     // Reset Custom Policy Delegate.
956     setCustomPolicyDelegate(false, false);
957
958     // Reset Content Extensions.
959     resetContentExtensions();
960
961     m_shouldDownloadUndisplayableMIMETypes = false;
962
963     m_shouldAllowDeviceOrientationAndMotionAccess = false;
964
965     m_workQueueManager.clearWorkQueue();
966
967     m_rejectsProtectionSpaceAndContinueForAuthenticationChallenges = false;
968     m_handlesAuthenticationChallenges = false;
969     m_authenticationUsername = String();
970     m_authenticationPassword = String();
971
972     setBlockAllPlugins(false);
973     setPluginSupportedMode({ });
974
975     m_shouldLogDownloadCallbacks = false;
976     m_shouldLogHistoryClientCallbacks = false;
977     m_shouldLogCanAuthenticateAgainstProtectionSpace = false;
978
979     setHidden(false);
980
981     platformResetStateToConsistentValues(options);
982
983     m_shouldDecideNavigationPolicyAfterDelay = false;
984     m_shouldDecideResponsePolicyAfterDelay = false;
985
986     setNavigationGesturesEnabled(false);
987     
988     setIgnoresViewportScaleLimits(options.ignoresViewportScaleLimits);
989
990     m_openPanelFileURLs = nullptr;
991     
992     statisticsResetToConsistentState();
993     
994     clearAdClickAttribution();
995
996     m_didReceiveServerRedirectForProvisionalNavigation = false;
997     m_serverTrustEvaluationCallbackCallsCount = 0;
998     m_shouldDismissJavaScriptAlertsAsynchronously = false;
999
1000     // Reset main page back to about:blank
1001     m_doneResetting = false;
1002     WKPageLoadURL(m_mainWebView->page(), blankURL());
1003     runUntil(m_doneResetting, m_currentInvocation->shortTimeout());
1004     if (!m_doneResetting)
1005         return false;
1006     
1007     if (resetStage == ResetStage::AfterTest)
1008         updateLiveDocumentsAfterTest();
1009
1010     return m_doneResetting;
1011 }
1012
1013 void TestController::updateLiveDocumentsAfterTest()
1014 {
1015     if (!m_checkForWorldLeaks)
1016         return;
1017
1018     AsyncTask([]() {
1019         // After each test, we update the list of live documents so that we can detect when an abandoned document first showed up.
1020         WKRetainPtr<WKStringRef> messageName = adoptWK(WKStringCreateWithUTF8CString("GetLiveDocuments"));
1021         WKPagePostMessageToInjectedBundle(TestController::singleton().mainWebView()->page(), messageName.get(), nullptr);
1022     }, 5_s).run();
1023 }
1024
1025 void TestController::checkForWorldLeaks()
1026 {
1027     if (!m_checkForWorldLeaks || !TestController::singleton().mainWebView())
1028         return;
1029
1030     AsyncTask([]() {
1031         // This runs at the end of a series of tests. It clears caches, runs a GC and then fetches the list of documents.
1032         WKRetainPtr<WKStringRef> messageName = adoptWK(WKStringCreateWithUTF8CString("CheckForWorldLeaks"));
1033         WKPagePostMessageToInjectedBundle(TestController::singleton().mainWebView()->page(), messageName.get(), nullptr);
1034     }, 20_s).run();
1035 }
1036
1037 void TestController::findAndDumpWorldLeaks()
1038 {
1039     if (!m_checkForWorldLeaks)
1040         return;
1041
1042     checkForWorldLeaks();
1043
1044     StringBuilder builder;
1045     
1046     if (m_abandonedDocumentInfo.size()) {
1047         for (const auto& it : m_abandonedDocumentInfo) {
1048             auto documentURL = it.value.abandonedDocumentURL;
1049             if (documentURL.isEmpty())
1050                 documentURL = "(no url)";
1051             builder.append("TEST: ");
1052             builder.append(it.value.testURL);
1053             builder.append('\n');
1054             builder.append("ABANDONED DOCUMENT: ");
1055             builder.append(documentURL);
1056             builder.append('\n');
1057         }
1058     } else
1059         builder.append("no abandoned documents");
1060
1061     String result = builder.toString();
1062     printf("Content-Type: text/plain\n");
1063     printf("Content-Length: %u\n", result.length());
1064     fwrite(result.utf8().data(), 1, result.length(), stdout);
1065     printf("#EOF\n");
1066     fprintf(stderr, "#EOF\n");
1067     fflush(stdout);
1068     fflush(stderr);
1069 }
1070
1071 void TestController::willDestroyWebView()
1072 {
1073     // Before we kill the web view, look for abandoned documents before that web process goes away.
1074     checkForWorldLeaks();
1075 }
1076
1077 void TestController::terminateWebContentProcess()
1078 {
1079     WKPageTerminate(m_mainWebView->page());
1080 }
1081
1082 void TestController::reattachPageToWebProcess()
1083 {
1084     // Loading a web page is the only way to reattach an existing page to a process.
1085     SetForScope<State> changeState(m_state, Resetting);
1086     m_doneResetting = false;
1087     WKPageLoadURL(m_mainWebView->page(), blankURL());
1088     runUntil(m_doneResetting, noTimeout);
1089 }
1090
1091 const char* TestController::webProcessName()
1092 {
1093     // FIXME: Find a way to not hardcode the process name.
1094 #if PLATFORM(IOS_FAMILY) && !PLATFORM(IOS_FAMILY_SIMULATOR)
1095     return "com.apple.WebKit.WebContent";
1096 #elif PLATFORM(COCOA)
1097     return "com.apple.WebKit.WebContent.Development";
1098 #elif PLATFORM(GTK)
1099     return "WebKitWebProcess";
1100 #elif PLATFORM(WPE)
1101     return "WPEWebProcess";
1102 #else
1103     return "WebProcess";
1104 #endif
1105 }
1106
1107 const char* TestController::networkProcessName()
1108 {
1109     // FIXME: Find a way to not hardcode the process name.
1110 #if PLATFORM(IOS_FAMILY) && !PLATFORM(IOS_FAMILY_SIMULATOR)
1111     return "com.apple.WebKit.Networking";
1112 #elif PLATFORM(COCOA)
1113     return "com.apple.WebKit.Networking.Development";
1114 #elif PLATFORM(GTK)
1115     return "WebKitNetworkProcess";
1116 #elif PLATFORM(WPE)
1117     return "WPENetworkProcess";
1118 #else
1119     return "NetworkProcess";
1120 #endif
1121 }
1122
1123 void TestController::setAllowsAnySSLCertificate(bool allows)
1124 {
1125     WKContextSetAllowsAnySSLCertificateForWebSocketTesting(platformContext(), allows);
1126 }
1127
1128 static std::string testPath(WKURLRef url)
1129 {
1130     auto scheme = adoptWK(WKURLCopyScheme(url));
1131     if (WKStringIsEqualToUTF8CStringIgnoringCase(scheme.get(), "file")) {
1132         auto path = adoptWK(WKURLCopyPath(url));
1133         auto buffer = std::vector<char>(WKStringGetMaximumUTF8CStringSize(path.get()));
1134         auto length = WKStringGetUTF8CString(path.get(), buffer.data(), buffer.size());
1135         RELEASE_ASSERT(length > 0);
1136 #if OS(WINDOWS)
1137         // Remove the first '/' if it starts with something like "/C:/".
1138         if (length >= 4 && buffer[0] == '/' && buffer[2] == ':' && buffer[3] == '/')
1139             return std::string(buffer.data() + 1, length - 1);
1140 #endif
1141         return std::string(buffer.data(), length - 1);
1142     }
1143     return std::string();
1144 }
1145
1146 static WKURLRef createTestURL(const char* pathOrURL)
1147 {
1148     if (strstr(pathOrURL, "http://") || strstr(pathOrURL, "https://") || strstr(pathOrURL, "file://"))
1149         return WKURLCreateWithUTF8CString(pathOrURL);
1150
1151     // Creating from filesytem path.
1152     size_t length = strlen(pathOrURL);
1153     if (!length)
1154         return 0;
1155
1156 #if PLATFORM(WIN)
1157     bool isAbsolutePath = false;
1158     if (strlen(pathOrURL) >= 3 && pathOrURL[1] == ':' && pathOrURL[2] == pathSeparator)
1159         isAbsolutePath = true;
1160 #else
1161     bool isAbsolutePath = pathOrURL[0] == pathSeparator;
1162 #endif
1163     const char* filePrefix = "file://";
1164     static const size_t prefixLength = strlen(filePrefix);
1165
1166     std::unique_ptr<char[]> buffer;
1167     if (isAbsolutePath) {
1168         buffer = std::make_unique<char[]>(prefixLength + length + 1);
1169         strcpy(buffer.get(), filePrefix);
1170         strcpy(buffer.get() + prefixLength, pathOrURL);
1171     } else {
1172         buffer = std::make_unique<char[]>(prefixLength + PATH_MAX + length + 2); // 1 for the pathSeparator
1173         strcpy(buffer.get(), filePrefix);
1174         if (!getcwd(buffer.get() + prefixLength, PATH_MAX))
1175             return 0;
1176         size_t numCharacters = strlen(buffer.get());
1177         buffer[numCharacters] = pathSeparator;
1178         strcpy(buffer.get() + numCharacters + 1, pathOrURL);
1179     }
1180
1181     return WKURLCreateWithUTF8CString(buffer.get());
1182 }
1183
1184 static bool parseBooleanTestHeaderValue(const std::string& value)
1185 {
1186     if (value == "true")
1187         return true;
1188     if (value == "false")
1189         return false;
1190
1191     LOG_ERROR("Found unexpected value '%s' for boolean option. Expected 'true' or 'false'.", value.c_str());
1192     return false;
1193 }
1194
1195 static std::string parseStringTestHeaderValueAsRelativePath(const std::string& value, const std::string& pathOrURL)
1196 {
1197     WKRetainPtr<WKURLRef> baseURL(AdoptWK, createTestURL(pathOrURL.c_str()));
1198     WKRetainPtr<WKURLRef> relativeURL(AdoptWK, WKURLCreateWithBaseURL(baseURL.get(), value.c_str()));
1199     return toSTD(adoptWK(WKURLCopyPath(relativeURL.get())));
1200 }
1201
1202 static void updateTestOptionsFromTestHeader(TestOptions& testOptions, const std::string& pathOrURL, const std::string& absolutePath)
1203 {
1204     std::string filename = absolutePath;
1205     if (filename.empty()) {
1206         // Gross. Need to reduce conversions between all the string types and URLs.
1207         WKRetainPtr<WKURLRef> wkURL(AdoptWK, createTestURL(pathOrURL.c_str()));
1208         filename = testPath(wkURL.get());
1209     }
1210
1211     if (filename.empty())
1212         return;
1213
1214     std::string options;
1215     std::ifstream testFile(filename.data());
1216     if (!testFile.good())
1217         return;
1218     getline(testFile, options);
1219     std::string beginString("webkit-test-runner [ ");
1220     std::string endString(" ]");
1221     size_t beginLocation = options.find(beginString);
1222     if (beginLocation == std::string::npos)
1223         return;
1224     size_t endLocation = options.find(endString, beginLocation);
1225     if (endLocation == std::string::npos) {
1226         LOG_ERROR("Could not find end of test header in %s", filename.c_str());
1227         return;
1228     }
1229     std::string pairString = options.substr(beginLocation + beginString.size(), endLocation - (beginLocation + beginString.size()));
1230     size_t pairStart = 0;
1231     while (pairStart < pairString.size()) {
1232         size_t pairEnd = pairString.find(" ", pairStart);
1233         if (pairEnd == std::string::npos)
1234             pairEnd = pairString.size();
1235         size_t equalsLocation = pairString.find("=", pairStart);
1236         if (equalsLocation == std::string::npos) {
1237             LOG_ERROR("Malformed option in test header (could not find '=' character) in %s", filename.c_str());
1238             break;
1239         }
1240         auto key = pairString.substr(pairStart, equalsLocation - pairStart);
1241         auto value = pairString.substr(equalsLocation + 1, pairEnd - (equalsLocation + 1));
1242
1243         if (!key.rfind("experimental:")) {
1244             key = key.substr(13);
1245             testOptions.experimentalFeatures.add(String(key.c_str()), parseBooleanTestHeaderValue(value));
1246         }
1247
1248         if (!key.rfind("internal:")) {
1249             key = key.substr(9);
1250             testOptions.internalDebugFeatures.add(String(key.c_str()), parseBooleanTestHeaderValue(value));
1251         }
1252
1253         if (key == "language")
1254             testOptions.contextOptions.overrideLanguages = String(value.c_str()).split(',');
1255         else if (key == "useThreadedScrolling")
1256             testOptions.useThreadedScrolling = parseBooleanTestHeaderValue(value);
1257         else if (key == "useAcceleratedDrawing")
1258             testOptions.useAcceleratedDrawing = parseBooleanTestHeaderValue(value);
1259         else if (key == "useFlexibleViewport")
1260             testOptions.useFlexibleViewport = parseBooleanTestHeaderValue(value);
1261         else if (key == "useDataDetection")
1262             testOptions.useDataDetection = parseBooleanTestHeaderValue(value);
1263         else if (key == "useMockScrollbars")
1264             testOptions.useMockScrollbars = parseBooleanTestHeaderValue(value);
1265         else if (key == "needsSiteSpecificQuirks")
1266             testOptions.needsSiteSpecificQuirks = parseBooleanTestHeaderValue(value);
1267         else if (key == "ignoresViewportScaleLimits")
1268             testOptions.ignoresViewportScaleLimits = parseBooleanTestHeaderValue(value);
1269         else if (key == "useCharacterSelectionGranularity")
1270             testOptions.useCharacterSelectionGranularity = parseBooleanTestHeaderValue(value);
1271         else if (key == "enableAttachmentElement")
1272             testOptions.enableAttachmentElement = parseBooleanTestHeaderValue(value);
1273         else if (key == "enableIntersectionObserver")
1274             testOptions.enableIntersectionObserver = parseBooleanTestHeaderValue(value);
1275         else if (key == "enableMenuItemElement")
1276             testOptions.enableMenuItemElement = parseBooleanTestHeaderValue(value);
1277         else if (key == "enableModernMediaControls")
1278             testOptions.enableModernMediaControls = parseBooleanTestHeaderValue(value);
1279         else if (key == "enablePointerLock")
1280             testOptions.enablePointerLock = parseBooleanTestHeaderValue(value);
1281         else if (key == "enableWebAuthentication")
1282             testOptions.enableWebAuthentication = parseBooleanTestHeaderValue(value);
1283         else if (key == "enableWebAuthenticationLocalAuthenticator")
1284             testOptions.enableWebAuthenticationLocalAuthenticator = parseBooleanTestHeaderValue(value);
1285         else if (key == "enableIsSecureContextAttribute")
1286             testOptions.enableIsSecureContextAttribute = parseBooleanTestHeaderValue(value);
1287         else if (key == "enableInspectorAdditions")
1288             testOptions.enableInspectorAdditions = parseBooleanTestHeaderValue(value);
1289         else if (key == "dumpJSConsoleLogInStdErr")
1290             testOptions.dumpJSConsoleLogInStdErr = parseBooleanTestHeaderValue(value);
1291         else if (key == "applicationManifest")
1292             testOptions.applicationManifest = parseStringTestHeaderValueAsRelativePath(value, pathOrURL);
1293         else if (key == "allowCrossOriginSubresourcesToAskForCredentials")
1294             testOptions.allowCrossOriginSubresourcesToAskForCredentials = parseBooleanTestHeaderValue(value);
1295         else if (key == "domPasteAllowed")
1296             testOptions.domPasteAllowed = parseBooleanTestHeaderValue(value);
1297         else if (key == "enableProcessSwapOnNavigation")
1298             testOptions.contextOptions.enableProcessSwapOnNavigation = parseBooleanTestHeaderValue(value);
1299         else if (key == "enableProcessSwapOnWindowOpen")
1300             testOptions.contextOptions.enableProcessSwapOnWindowOpen = parseBooleanTestHeaderValue(value);
1301         else if (key == "enableColorFilter")
1302             testOptions.enableColorFilter = parseBooleanTestHeaderValue(value);
1303         else if (key == "punchOutWhiteBackgroundsInDarkMode")
1304             testOptions.punchOutWhiteBackgroundsInDarkMode = parseBooleanTestHeaderValue(value);
1305         else if (key == "jscOptions")
1306             testOptions.jscOptions = value;
1307         else if (key == "runSingly")
1308             testOptions.runSingly = parseBooleanTestHeaderValue(value);
1309         else if (key == "shouldIgnoreMetaViewport")
1310             testOptions.shouldIgnoreMetaViewport = parseBooleanTestHeaderValue(value);
1311         else if (key == "spellCheckingDots")
1312             testOptions.shouldShowSpellCheckingDots = parseBooleanTestHeaderValue(value);
1313         else if (key == "enableEditableImages")
1314             testOptions.enableEditableImages = parseBooleanTestHeaderValue(value);
1315         else if (key == "editable")
1316             testOptions.editable = parseBooleanTestHeaderValue(value);
1317         else if (key == "enableUndoManagerAPI")
1318             testOptions.enableUndoManagerAPI = parseBooleanTestHeaderValue(value);
1319         else if (key == "contentInset.top")
1320             testOptions.contentInsetTop = std::stod(value);
1321         else if (key == "ignoreSynchronousMessagingTimeouts")
1322             testOptions.contextOptions.ignoreSynchronousMessagingTimeouts = parseBooleanTestHeaderValue(value);
1323         pairStart = pairEnd + 1;
1324     }
1325 }
1326
1327 TestOptions TestController::testOptionsForTest(const TestCommand& command) const
1328 {
1329     TestOptions options(command.pathOrURL);
1330
1331     options.useRemoteLayerTree = m_shouldUseRemoteLayerTree;
1332     options.shouldShowWebView = m_shouldShowWebView;
1333
1334     updatePlatformSpecificTestOptionsForTest(options, command.pathOrURL);
1335     updateTestOptionsFromTestHeader(options, command.pathOrURL, command.absolutePath);
1336     platformAddTestOptions(options);
1337
1338     return options;
1339 }
1340
1341 void TestController::updateWebViewSizeForTest(const TestInvocation& test)
1342 {
1343     unsigned width = viewWidth;
1344     unsigned height = viewHeight;
1345     if (test.options().isSVGTest) {
1346         width = w3cSVGViewWidth;
1347         height = w3cSVGViewHeight;
1348     }
1349
1350     mainWebView()->resizeTo(width, height);
1351 }
1352
1353 void TestController::updateWindowScaleForTest(PlatformWebView* view, const TestInvocation& test)
1354 {
1355     view->changeWindowScaleIfNeeded(test.options().deviceScaleFactor);
1356 }
1357
1358 void TestController::configureViewForTest(const TestInvocation& test)
1359 {
1360     ensureViewSupportsOptionsForTest(test);
1361     updateWebViewSizeForTest(test);
1362     updateWindowScaleForTest(mainWebView(), test);
1363     configureContentExtensionForTest(test);
1364     platformConfigureViewForTest(test);
1365 }
1366
1367 #if ENABLE(CONTENT_EXTENSIONS) && !PLATFORM(COCOA)
1368 struct ContentExtensionStoreCallbackContext {
1369     explicit ContentExtensionStoreCallbackContext(TestController& controller)
1370         : testController(controller)
1371     {
1372     }
1373
1374     TestController& testController;
1375     uint32_t status { kWKUserContentExtensionStoreSuccess };
1376     WKRetainPtr<WKUserContentFilterRef> filter;
1377     bool done { false };
1378 };
1379
1380 static void contentExtensionStoreCallback(WKUserContentFilterRef filter, uint32_t status, void* userData)
1381 {
1382     auto* context = static_cast<ContentExtensionStoreCallbackContext*>(userData);
1383     context->status = status;
1384     context->filter = filter ? adoptWK(filter) : nullptr;
1385     context->done = true;
1386     context->testController.notifyDone();
1387 }
1388
1389 static std::string contentExtensionJSONPath(WKURLRef url)
1390 {
1391     auto path = testPath(url);
1392     if (path.length())
1393         return path + ".json";
1394
1395     auto p = adoptWK(WKURLCopyPath(url));
1396     auto buffer = std::vector<char>(WKStringGetMaximumUTF8CStringSize(p.get()));
1397     const auto length = WKStringGetUTF8CString(p.get(), buffer.data(), buffer.size());
1398     return std::string("LayoutTests/http/tests") + std::string(buffer.data(), length - 1) + ".json";
1399 }
1400 #endif
1401
1402 #if !PLATFORM(COCOA)
1403 #if ENABLE(CONTENT_EXTENSIONS)
1404 void TestController::configureContentExtensionForTest(const TestInvocation& test)
1405 {
1406     const char* contentExtensionsPath = libraryPathForTesting();
1407     if (!contentExtensionsPath)
1408         contentExtensionsPath = "/tmp/wktr-contentextensions";
1409
1410     if (!test.urlContains("contentextensions/")) {
1411         WKPageSetUserContentExtensionsEnabled(m_mainWebView->page(), false);
1412         return;
1413     }
1414
1415     std::string jsonFilePath(contentExtensionJSONPath(test.url()));
1416     std::ifstream jsonFile(jsonFilePath);
1417     if (!jsonFile.good()) {
1418         WTFLogAlways("Could not open file '%s'", jsonFilePath.c_str());
1419         return;
1420     }
1421
1422     std::string jsonFileContents {std::istreambuf_iterator<char>(jsonFile), std::istreambuf_iterator<char>()};
1423     auto jsonSource = adoptWK(WKStringCreateWithUTF8CString(jsonFileContents.c_str()));
1424
1425     auto storePath = adoptWK(WKStringCreateWithUTF8CString(contentExtensionsPath));
1426     auto extensionStore = adoptWK(WKUserContentExtensionStoreCreate(storePath.get()));
1427     ASSERT(extensionStore);
1428
1429     auto filterIdentifier = adoptWK(WKStringCreateWithUTF8CString("TestContentExtension"));
1430
1431     ContentExtensionStoreCallbackContext context(*this);
1432     WKUserContentExtensionStoreCompile(extensionStore.get(), filterIdentifier.get(), jsonSource.get(), &context, contentExtensionStoreCallback);
1433     runUntil(context.done, noTimeout);
1434     ASSERT(context.status == kWKUserContentExtensionStoreSuccess);
1435     ASSERT(context.filter);
1436
1437     WKPageSetUserContentExtensionsEnabled(mainWebView()->page(), true);
1438     WKUserContentControllerAddUserContentFilter(userContentController(), context.filter.get());
1439 }
1440
1441 void TestController::resetContentExtensions()
1442 {
1443     if (!mainWebView())
1444         return;
1445
1446     WKPageSetUserContentExtensionsEnabled(mainWebView()->page(), false);
1447
1448     const char* contentExtensionsPath = libraryPathForTesting();
1449     if (!contentExtensionsPath)
1450         return;
1451
1452     WKUserContentControllerRemoveAllUserContentFilters(userContentController());
1453
1454     auto storePath = adoptWK(WKStringCreateWithUTF8CString(contentExtensionsPath));
1455     auto extensionStore = adoptWK(WKUserContentExtensionStoreCreate(storePath.get()));
1456     ASSERT(extensionStore);
1457
1458     auto filterIdentifier = adoptWK(WKStringCreateWithUTF8CString("TestContentExtension"));
1459
1460     ContentExtensionStoreCallbackContext context(*this);
1461     WKUserContentExtensionStoreRemove(extensionStore.get(), filterIdentifier.get(), &context, contentExtensionStoreCallback);
1462     runUntil(context.done, noTimeout);
1463     ASSERT(!context.filter);
1464 }
1465 #else // ENABLE(CONTENT_EXTENSIONS)
1466 void TestController::configureContentExtensionForTest(const TestInvocation&)
1467 {
1468 }
1469
1470 void TestController::resetContentExtensions()
1471 {
1472 }
1473 #endif // ENABLE(CONTENT_EXTENSIONS)
1474 #endif // !PLATFORM(COCOA)
1475
1476 class CommandTokenizer {
1477 public:
1478     explicit CommandTokenizer(const std::string& input)
1479         : m_input(input)
1480         , m_posNextSeparator(0)
1481     {
1482         pump();
1483     }
1484
1485     bool hasNext() const;
1486     std::string next();
1487
1488 private:
1489     void pump();
1490     static const char kSeparator = '\'';
1491     const std::string& m_input;
1492     std::string m_next;
1493     size_t m_posNextSeparator;
1494 };
1495
1496 void CommandTokenizer::pump()
1497 {
1498     if (m_posNextSeparator == std::string::npos || m_posNextSeparator == m_input.size()) {
1499         m_next = std::string();
1500         return;
1501     }
1502     size_t start = m_posNextSeparator ? m_posNextSeparator + 1 : 0;
1503     m_posNextSeparator = m_input.find(kSeparator, start);
1504     size_t size = m_posNextSeparator == std::string::npos ? std::string::npos : m_posNextSeparator - start;
1505     m_next = std::string(m_input, start, size);
1506 }
1507
1508 std::string CommandTokenizer::next()
1509 {
1510     ASSERT(hasNext());
1511
1512     std::string oldNext = m_next;
1513     pump();
1514     return oldNext;
1515 }
1516
1517 bool CommandTokenizer::hasNext() const
1518 {
1519     return !m_next.empty();
1520 }
1521
1522 NO_RETURN static void die(const std::string& inputLine)
1523 {
1524     fprintf(stderr, "Unexpected input line: %s\n", inputLine.c_str());
1525     exit(1);
1526 }
1527
1528 static TestCommand parseInputLine(const std::string& inputLine)
1529 {
1530     TestCommand result;
1531     CommandTokenizer tokenizer(inputLine);
1532     if (!tokenizer.hasNext())
1533         die(inputLine);
1534
1535     std::string arg = tokenizer.next();
1536     result.pathOrURL = arg;
1537     while (tokenizer.hasNext()) {
1538         arg = tokenizer.next();
1539         if (arg == std::string("--timeout")) {
1540             std::string timeoutToken = tokenizer.next();
1541             result.timeout = Seconds::fromMilliseconds(atoi(timeoutToken.c_str()));
1542         } else if (arg == std::string("-p") || arg == std::string("--pixel-test")) {
1543             result.shouldDumpPixels = true;
1544             if (tokenizer.hasNext())
1545                 result.expectedPixelHash = tokenizer.next();
1546         } else if (arg == std::string("--dump-jsconsolelog-in-stderr"))
1547             result.dumpJSConsoleLogInStdErr = true;
1548         else if (arg == std::string("--absolutePath"))
1549             result.absolutePath = tokenizer.next();
1550         else
1551             die(inputLine);
1552     }
1553     return result;
1554 }
1555
1556 bool TestController::runTest(const char* inputLine)
1557 {
1558     AutodrainedPool pool;
1559     
1560     WKTextCheckerSetTestingMode(true);
1561     TestCommand command = parseInputLine(std::string(inputLine));
1562
1563     m_state = RunningTest;
1564     
1565     TestOptions options = testOptionsForTest(command);
1566
1567     WKRetainPtr<WKURLRef> wkURL(AdoptWK, createTestURL(command.pathOrURL.c_str()));
1568     m_currentInvocation = std::make_unique<TestInvocation>(wkURL.get(), options);
1569
1570     if (command.shouldDumpPixels || m_shouldDumpPixelsForAllTests)
1571         m_currentInvocation->setIsPixelTest(command.expectedPixelHash);
1572
1573     if (command.timeout > 0_s)
1574         m_currentInvocation->setCustomTimeout(command.timeout);
1575
1576     m_currentInvocation->setDumpJSConsoleLogInStdErr(command.dumpJSConsoleLogInStdErr || options.dumpJSConsoleLogInStdErr);
1577
1578     platformWillRunTest(*m_currentInvocation);
1579
1580     m_currentInvocation->invoke();
1581     m_currentInvocation = nullptr;
1582
1583     return true;
1584 }
1585
1586 bool TestController::waitForCompletion(const WTF::Function<void ()>& function, WTF::Seconds timeout)
1587 {
1588     m_doneResetting = false;
1589     function();
1590     runUntil(m_doneResetting, timeout);
1591     return !m_doneResetting;
1592 }
1593
1594 bool TestController::handleControlCommand(const char* command)
1595 {
1596     if (!strcmp("#CHECK FOR WORLD LEAKS", command)) {
1597         if (!m_checkForWorldLeaks) {
1598             WTFLogAlways("WebKitTestRunner asked to check for world leaks, but was not run with --world-leaks");
1599             return true;
1600         }
1601         findAndDumpWorldLeaks();
1602         return true;
1603     }
1604     return false;
1605 }
1606
1607 void TestController::runTestingServerLoop()
1608 {
1609     char filenameBuffer[2048];
1610     while (fgets(filenameBuffer, sizeof(filenameBuffer), stdin)) {
1611         char* newLineCharacter = strchr(filenameBuffer, '\n');
1612         if (newLineCharacter)
1613             *newLineCharacter = '\0';
1614
1615         if (strlen(filenameBuffer) == 0)
1616             continue;
1617
1618         if (handleControlCommand(filenameBuffer))
1619             continue;
1620
1621         if (!runTest(filenameBuffer))
1622             break;
1623     }
1624 }
1625
1626 void TestController::run()
1627 {
1628     if (m_usingServerMode)
1629         runTestingServerLoop();
1630     else {
1631         for (size_t i = 0; i < m_paths.size(); ++i) {
1632             if (!runTest(m_paths[i].c_str()))
1633                 break;
1634         }
1635         if (m_checkForWorldLeaks)
1636             findAndDumpWorldLeaks();
1637     }
1638 }
1639
1640 void TestController::runUntil(bool& done, WTF::Seconds timeout)
1641 {
1642     if (m_forceNoTimeout)
1643         timeout = noTimeout;
1644
1645     platformRunUntil(done, timeout);
1646 }
1647
1648 // WKContextInjectedBundleClient
1649
1650 void TestController::didReceiveMessageFromInjectedBundle(WKContextRef context, WKStringRef messageName, WKTypeRef messageBody, const void* clientInfo)
1651 {
1652     static_cast<TestController*>(const_cast<void*>(clientInfo))->didReceiveMessageFromInjectedBundle(messageName, messageBody);
1653 }
1654
1655 void TestController::didReceiveSynchronousMessageFromInjectedBundle(WKContextRef context, WKStringRef messageName, WKTypeRef messageBody, WKTypeRef* returnData, const void* clientInfo)
1656 {
1657     *returnData = static_cast<TestController*>(const_cast<void*>(clientInfo))->didReceiveSynchronousMessageFromInjectedBundle(messageName, messageBody).leakRef();
1658 }
1659
1660 WKTypeRef TestController::getInjectedBundleInitializationUserData(WKContextRef, const void* clientInfo)
1661 {
1662     return static_cast<TestController*>(const_cast<void*>(clientInfo))->getInjectedBundleInitializationUserData().leakRef();
1663 }
1664
1665 // WKPageInjectedBundleClient
1666
1667 void TestController::didReceivePageMessageFromInjectedBundle(WKPageRef page, WKStringRef messageName, WKTypeRef messageBody, const void* clientInfo)
1668 {
1669     static_cast<TestController*>(const_cast<void*>(clientInfo))->didReceiveMessageFromInjectedBundle(messageName, messageBody);
1670 }
1671
1672 void TestController::didReceiveSynchronousPageMessageFromInjectedBundle(WKPageRef page, WKStringRef messageName, WKTypeRef messageBody, WKTypeRef* returnData, const void* clientInfo)
1673 {
1674     *returnData = static_cast<TestController*>(const_cast<void*>(clientInfo))->didReceiveSynchronousMessageFromInjectedBundle(messageName, messageBody).leakRef();
1675 }
1676
1677 void TestController::networkProcessDidCrash(WKContextRef context, const void *clientInfo)
1678 {
1679     static_cast<TestController*>(const_cast<void*>(clientInfo))->networkProcessDidCrash();
1680 }
1681
1682 void TestController::didReceiveKeyDownMessageFromInjectedBundle(WKDictionaryRef messageBodyDictionary, bool synchronous)
1683 {
1684     WKRetainPtr<WKStringRef> keyKey = adoptWK(WKStringCreateWithUTF8CString("Key"));
1685     WKStringRef key = static_cast<WKStringRef>(WKDictionaryGetItemForKey(messageBodyDictionary, keyKey.get()));
1686
1687     WKRetainPtr<WKStringRef> modifiersKey = adoptWK(WKStringCreateWithUTF8CString("Modifiers"));
1688     WKEventModifiers modifiers = static_cast<WKEventModifiers>(WKUInt64GetValue(static_cast<WKUInt64Ref>(WKDictionaryGetItemForKey(messageBodyDictionary, modifiersKey.get()))));
1689
1690     WKRetainPtr<WKStringRef> locationKey = adoptWK(WKStringCreateWithUTF8CString("Location"));
1691     unsigned location = static_cast<unsigned>(WKUInt64GetValue(static_cast<WKUInt64Ref>(WKDictionaryGetItemForKey(messageBodyDictionary, locationKey.get()))));
1692
1693     m_eventSenderProxy->keyDown(key, modifiers, location);
1694 }
1695
1696 void TestController::didReceiveLiveDocumentsList(WKArrayRef liveDocumentList)
1697 {
1698     auto numDocuments = WKArrayGetSize(liveDocumentList);
1699
1700     HashMap<uint64_t, String> documentInfo;
1701     for (size_t i = 0; i < numDocuments; ++i) {
1702         WKTypeRef item = WKArrayGetItemAtIndex(liveDocumentList, i);
1703         if (item && WKGetTypeID(item) == WKDictionaryGetTypeID()) {
1704             WKDictionaryRef liveDocumentItem = static_cast<WKDictionaryRef>(item);
1705
1706             WKRetainPtr<WKStringRef> idKey(AdoptWK, WKStringCreateWithUTF8CString("id"));
1707             WKUInt64Ref documentID = static_cast<WKUInt64Ref>(WKDictionaryGetItemForKey(liveDocumentItem, idKey.get()));
1708
1709             WKRetainPtr<WKStringRef> urlKey(AdoptWK, WKStringCreateWithUTF8CString("url"));
1710             WKStringRef documentURL = static_cast<WKStringRef>(WKDictionaryGetItemForKey(liveDocumentItem, urlKey.get()));
1711
1712             documentInfo.add(WKUInt64GetValue(documentID), toWTFString(documentURL));
1713         }
1714     }
1715
1716     if (!documentInfo.size()) {
1717         m_abandonedDocumentInfo.clear();
1718         return;
1719     }
1720
1721     // Remove any documents which are no longer live.
1722     m_abandonedDocumentInfo.removeIf([&](auto& keyAndValue) {
1723         return !documentInfo.contains(keyAndValue.key);
1724     });
1725     
1726     // Add newly abandoned documents.
1727     String currentTestURL = m_currentInvocation ? toWTFString(adoptWK(WKURLCopyString(m_currentInvocation->url()))) : "no test";
1728     for (const auto& it : documentInfo)
1729         m_abandonedDocumentInfo.add(it.key, AbandonedDocumentInfo(currentTestURL, it.value));
1730 }
1731
1732 void TestController::didReceiveMessageFromInjectedBundle(WKStringRef messageName, WKTypeRef messageBody)
1733 {
1734     if (WKStringIsEqualToUTF8CString(messageName, "LiveDocuments")) {
1735         ASSERT(WKGetTypeID(messageBody) == WKArrayGetTypeID());
1736         didReceiveLiveDocumentsList(static_cast<WKArrayRef>(messageBody));
1737         AsyncTask::currentTask()->taskComplete();
1738         return;
1739     }
1740
1741     if (WKStringIsEqualToUTF8CString(messageName, "EventSender")) {
1742         if (m_state != RunningTest)
1743             return;
1744
1745         ASSERT(WKGetTypeID(messageBody) == WKDictionaryGetTypeID());
1746         WKDictionaryRef messageBodyDictionary = static_cast<WKDictionaryRef>(messageBody);
1747
1748         WKRetainPtr<WKStringRef> subMessageKey(AdoptWK, WKStringCreateWithUTF8CString("SubMessage"));
1749         WKStringRef subMessageName = static_cast<WKStringRef>(WKDictionaryGetItemForKey(messageBodyDictionary, subMessageKey.get()));
1750
1751         if (WKStringIsEqualToUTF8CString(subMessageName, "MouseDown") || WKStringIsEqualToUTF8CString(subMessageName, "MouseUp")) {
1752             WKRetainPtr<WKStringRef> buttonKey = adoptWK(WKStringCreateWithUTF8CString("Button"));
1753             unsigned button = static_cast<unsigned>(WKUInt64GetValue(static_cast<WKUInt64Ref>(WKDictionaryGetItemForKey(messageBodyDictionary, buttonKey.get()))));
1754
1755             WKRetainPtr<WKStringRef> modifiersKey = adoptWK(WKStringCreateWithUTF8CString("Modifiers"));
1756             WKEventModifiers modifiers = static_cast<WKEventModifiers>(WKUInt64GetValue(static_cast<WKUInt64Ref>(WKDictionaryGetItemForKey(messageBodyDictionary, modifiersKey.get()))));
1757
1758             // Forward to WebProcess
1759             if (WKStringIsEqualToUTF8CString(subMessageName, "MouseDown"))
1760                 m_eventSenderProxy->mouseDown(button, modifiers);
1761             else
1762                 m_eventSenderProxy->mouseUp(button, modifiers);
1763
1764             return;
1765         }
1766
1767         if (WKStringIsEqualToUTF8CString(subMessageName, "KeyDown")) {
1768             didReceiveKeyDownMessageFromInjectedBundle(messageBodyDictionary, false);
1769             return;
1770         }
1771
1772         if (WKStringIsEqualToUTF8CString(subMessageName, "MouseScrollBy")) {
1773             WKRetainPtr<WKStringRef> xKey = adoptWK(WKStringCreateWithUTF8CString("X"));
1774             double x = WKDoubleGetValue(static_cast<WKDoubleRef>(WKDictionaryGetItemForKey(messageBodyDictionary, xKey.get())));
1775
1776             WKRetainPtr<WKStringRef> yKey = adoptWK(WKStringCreateWithUTF8CString("Y"));
1777             double y = WKDoubleGetValue(static_cast<WKDoubleRef>(WKDictionaryGetItemForKey(messageBodyDictionary, yKey.get())));
1778
1779             // Forward to WebProcess
1780             m_eventSenderProxy->mouseScrollBy(x, y);
1781             return;
1782         }
1783
1784         if (WKStringIsEqualToUTF8CString(subMessageName, "MouseScrollByWithWheelAndMomentumPhases")) {
1785             WKRetainPtr<WKStringRef> xKey = adoptWK(WKStringCreateWithUTF8CString("X"));
1786             double x = WKDoubleGetValue(static_cast<WKDoubleRef>(WKDictionaryGetItemForKey(messageBodyDictionary, xKey.get())));
1787             
1788             WKRetainPtr<WKStringRef> yKey = adoptWK(WKStringCreateWithUTF8CString("Y"));
1789             double y = WKDoubleGetValue(static_cast<WKDoubleRef>(WKDictionaryGetItemForKey(messageBodyDictionary, yKey.get())));
1790             
1791             WKRetainPtr<WKStringRef> phaseKey = adoptWK(WKStringCreateWithUTF8CString("Phase"));
1792             int phase = static_cast<int>(WKUInt64GetValue(static_cast<WKUInt64Ref>(WKDictionaryGetItemForKey(messageBodyDictionary, phaseKey.get()))));
1793             WKRetainPtr<WKStringRef> momentumKey = adoptWK(WKStringCreateWithUTF8CString("Momentum"));
1794             int momentum = static_cast<int>(WKUInt64GetValue(static_cast<WKUInt64Ref>(WKDictionaryGetItemForKey(messageBodyDictionary, momentumKey.get()))));
1795             
1796             // Forward to WebProcess
1797             m_eventSenderProxy->mouseScrollByWithWheelAndMomentumPhases(x, y, phase, momentum);
1798
1799             return;
1800         }
1801
1802         ASSERT_NOT_REACHED();
1803     }
1804
1805     if (!m_currentInvocation)
1806         return;
1807
1808     m_currentInvocation->didReceiveMessageFromInjectedBundle(messageName, messageBody);
1809 }
1810
1811 WKRetainPtr<WKTypeRef> TestController::didReceiveSynchronousMessageFromInjectedBundle(WKStringRef messageName, WKTypeRef messageBody)
1812 {
1813     if (WKStringIsEqualToUTF8CString(messageName, "EventSender")) {
1814         if (m_state != RunningTest)
1815             return nullptr;
1816
1817         ASSERT(WKGetTypeID(messageBody) == WKDictionaryGetTypeID());
1818         WKDictionaryRef messageBodyDictionary = static_cast<WKDictionaryRef>(messageBody);
1819
1820         WKRetainPtr<WKStringRef> subMessageKey(AdoptWK, WKStringCreateWithUTF8CString("SubMessage"));
1821         WKStringRef subMessageName = static_cast<WKStringRef>(WKDictionaryGetItemForKey(messageBodyDictionary, subMessageKey.get()));
1822
1823         if (WKStringIsEqualToUTF8CString(subMessageName, "KeyDown")) {
1824             didReceiveKeyDownMessageFromInjectedBundle(messageBodyDictionary, true);
1825
1826             return 0;
1827         }
1828
1829         if (WKStringIsEqualToUTF8CString(subMessageName, "MouseDown") || WKStringIsEqualToUTF8CString(subMessageName, "MouseUp")) {
1830             WKRetainPtr<WKStringRef> buttonKey = adoptWK(WKStringCreateWithUTF8CString("Button"));
1831             unsigned button = static_cast<unsigned>(WKUInt64GetValue(static_cast<WKUInt64Ref>(WKDictionaryGetItemForKey(messageBodyDictionary, buttonKey.get()))));
1832
1833             WKRetainPtr<WKStringRef> modifiersKey = adoptWK(WKStringCreateWithUTF8CString("Modifiers"));
1834             WKEventModifiers modifiers = static_cast<WKEventModifiers>(WKUInt64GetValue(static_cast<WKUInt64Ref>(WKDictionaryGetItemForKey(messageBodyDictionary, modifiersKey.get()))));
1835
1836             // Forward to WebProcess
1837             if (WKStringIsEqualToUTF8CString(subMessageName, "MouseDown"))
1838                 m_eventSenderProxy->mouseDown(button, modifiers);
1839             else
1840                 m_eventSenderProxy->mouseUp(button, modifiers);
1841             return 0;
1842         }
1843
1844         if (WKStringIsEqualToUTF8CString(subMessageName, "MouseMoveTo")) {
1845             WKRetainPtr<WKStringRef> xKey = adoptWK(WKStringCreateWithUTF8CString("X"));
1846             double x = WKDoubleGetValue(static_cast<WKDoubleRef>(WKDictionaryGetItemForKey(messageBodyDictionary, xKey.get())));
1847
1848             WKRetainPtr<WKStringRef> yKey = adoptWK(WKStringCreateWithUTF8CString("Y"));
1849             double y = WKDoubleGetValue(static_cast<WKDoubleRef>(WKDictionaryGetItemForKey(messageBodyDictionary, yKey.get())));
1850
1851             // Forward to WebProcess
1852             m_eventSenderProxy->mouseMoveTo(x, y);
1853             return 0;
1854         }
1855
1856 #if PLATFORM(MAC)
1857         if (WKStringIsEqualToUTF8CString(subMessageName, "MouseForceClick")) {
1858             m_eventSenderProxy->mouseForceClick();
1859             return 0;
1860         }
1861
1862         if (WKStringIsEqualToUTF8CString(subMessageName, "StartAndCancelMouseForceClick")) {
1863             m_eventSenderProxy->startAndCancelMouseForceClick();
1864             return 0;
1865         }
1866
1867         if (WKStringIsEqualToUTF8CString(subMessageName, "MouseForceDown")) {
1868             m_eventSenderProxy->mouseForceDown();
1869             return 0;
1870         }
1871
1872         if (WKStringIsEqualToUTF8CString(subMessageName, "MouseForceUp")) {
1873             m_eventSenderProxy->mouseForceUp();
1874             return 0;
1875         }
1876
1877         if (WKStringIsEqualToUTF8CString(subMessageName, "MouseForceChanged")) {
1878             WKRetainPtr<WKStringRef> forceKey = adoptWK(WKStringCreateWithUTF8CString("Force"));
1879             double force = WKDoubleGetValue(static_cast<WKDoubleRef>(WKDictionaryGetItemForKey(messageBodyDictionary, forceKey.get())));
1880
1881             m_eventSenderProxy->mouseForceChanged(force);
1882             return 0;
1883         }
1884 #endif // PLATFORM(MAC)
1885
1886         if (WKStringIsEqualToUTF8CString(subMessageName, "ContinuousMouseScrollBy")) {
1887             WKRetainPtr<WKStringRef> xKey = adoptWK(WKStringCreateWithUTF8CString("X"));
1888             double x = WKDoubleGetValue(static_cast<WKDoubleRef>(WKDictionaryGetItemForKey(messageBodyDictionary, xKey.get())));
1889
1890             WKRetainPtr<WKStringRef> yKey = adoptWK(WKStringCreateWithUTF8CString("Y"));
1891             double y = WKDoubleGetValue(static_cast<WKDoubleRef>(WKDictionaryGetItemForKey(messageBodyDictionary, yKey.get())));
1892
1893             WKRetainPtr<WKStringRef> pagedKey = adoptWK(WKStringCreateWithUTF8CString("Paged"));
1894             bool paged = static_cast<bool>(WKUInt64GetValue(static_cast<WKUInt64Ref>(WKDictionaryGetItemForKey(messageBodyDictionary, pagedKey.get()))));
1895
1896             // Forward to WebProcess
1897             m_eventSenderProxy->continuousMouseScrollBy(x, y, paged);
1898             return 0;
1899         }
1900
1901         if (WKStringIsEqualToUTF8CString(subMessageName, "LeapForward")) {
1902             WKRetainPtr<WKStringRef> timeKey = adoptWK(WKStringCreateWithUTF8CString("TimeInMilliseconds"));
1903             unsigned time = static_cast<unsigned>(WKUInt64GetValue(static_cast<WKUInt64Ref>(WKDictionaryGetItemForKey(messageBodyDictionary, timeKey.get()))));
1904
1905             m_eventSenderProxy->leapForward(time);
1906             return 0;
1907         }
1908
1909 #if ENABLE(TOUCH_EVENTS)
1910         if (WKStringIsEqualToUTF8CString(subMessageName, "AddTouchPoint")) {
1911             WKRetainPtr<WKStringRef> xKey = adoptWK(WKStringCreateWithUTF8CString("X"));
1912             int x = static_cast<int>(WKUInt64GetValue(static_cast<WKUInt64Ref>(WKDictionaryGetItemForKey(messageBodyDictionary, xKey.get()))));
1913
1914             WKRetainPtr<WKStringRef> yKey = adoptWK(WKStringCreateWithUTF8CString("Y"));
1915             int y = static_cast<int>(WKUInt64GetValue(static_cast<WKUInt64Ref>(WKDictionaryGetItemForKey(messageBodyDictionary, yKey.get()))));
1916
1917             m_eventSenderProxy->addTouchPoint(x, y);
1918             return 0;
1919         }
1920
1921         if (WKStringIsEqualToUTF8CString(subMessageName, "UpdateTouchPoint")) {
1922             WKRetainPtr<WKStringRef> indexKey = adoptWK(WKStringCreateWithUTF8CString("Index"));
1923             int index = static_cast<int>(WKUInt64GetValue(static_cast<WKUInt64Ref>(WKDictionaryGetItemForKey(messageBodyDictionary, indexKey.get()))));
1924
1925             WKRetainPtr<WKStringRef> xKey = adoptWK(WKStringCreateWithUTF8CString("X"));
1926             int x = static_cast<int>(WKUInt64GetValue(static_cast<WKUInt64Ref>(WKDictionaryGetItemForKey(messageBodyDictionary, xKey.get()))));
1927
1928             WKRetainPtr<WKStringRef> yKey = adoptWK(WKStringCreateWithUTF8CString("Y"));
1929             int y = static_cast<int>(WKUInt64GetValue(static_cast<WKUInt64Ref>(WKDictionaryGetItemForKey(messageBodyDictionary, yKey.get()))));
1930
1931             m_eventSenderProxy->updateTouchPoint(index, x, y);
1932             return 0;
1933         }
1934
1935         if (WKStringIsEqualToUTF8CString(subMessageName, "SetTouchModifier")) {
1936             WKRetainPtr<WKStringRef> modifierKey = adoptWK(WKStringCreateWithUTF8CString("Modifier"));
1937             WKEventModifiers modifier = static_cast<WKEventModifiers>(WKUInt64GetValue(static_cast<WKUInt64Ref>(WKDictionaryGetItemForKey(messageBodyDictionary, modifierKey.get()))));
1938
1939             WKRetainPtr<WKStringRef> enableKey = adoptWK(WKStringCreateWithUTF8CString("Enable"));
1940             bool enable = static_cast<bool>(WKUInt64GetValue(static_cast<WKUInt64Ref>(WKDictionaryGetItemForKey(messageBodyDictionary, enableKey.get()))));
1941
1942             m_eventSenderProxy->setTouchModifier(modifier, enable);
1943             return 0;
1944         }
1945
1946         if (WKStringIsEqualToUTF8CString(subMessageName, "SetTouchPointRadius")) {
1947             WKRetainPtr<WKStringRef> xKey = adoptWK(WKStringCreateWithUTF8CString("RadiusX"));
1948             int x = static_cast<int>(WKUInt64GetValue(static_cast<WKUInt64Ref>(WKDictionaryGetItemForKey(messageBodyDictionary, xKey.get()))));
1949
1950             WKRetainPtr<WKStringRef> yKey = adoptWK(WKStringCreateWithUTF8CString("RadiusY"));
1951             int y = static_cast<int>(WKUInt64GetValue(static_cast<WKUInt64Ref>(WKDictionaryGetItemForKey(messageBodyDictionary, yKey.get()))));
1952
1953             m_eventSenderProxy->setTouchPointRadius(x, y);
1954             return 0;
1955         }
1956
1957         if (WKStringIsEqualToUTF8CString(subMessageName, "TouchStart")) {
1958             m_eventSenderProxy->touchStart();
1959             return 0;
1960         }
1961
1962         if (WKStringIsEqualToUTF8CString(subMessageName, "TouchMove")) {
1963             m_eventSenderProxy->touchMove();
1964             return 0;
1965         }
1966
1967         if (WKStringIsEqualToUTF8CString(subMessageName, "TouchEnd")) {
1968             m_eventSenderProxy->touchEnd();
1969             return 0;
1970         }
1971
1972         if (WKStringIsEqualToUTF8CString(subMessageName, "TouchCancel")) {
1973             m_eventSenderProxy->touchCancel();
1974             return 0;
1975         }
1976
1977         if (WKStringIsEqualToUTF8CString(subMessageName, "ClearTouchPoints")) {
1978             m_eventSenderProxy->clearTouchPoints();
1979             return 0;
1980         }
1981
1982         if (WKStringIsEqualToUTF8CString(subMessageName, "ReleaseTouchPoint")) {
1983             WKRetainPtr<WKStringRef> indexKey = adoptWK(WKStringCreateWithUTF8CString("Index"));
1984             int index = static_cast<int>(WKUInt64GetValue(static_cast<WKUInt64Ref>(WKDictionaryGetItemForKey(messageBodyDictionary, indexKey.get()))));
1985             m_eventSenderProxy->releaseTouchPoint(index);
1986             return 0;
1987         }
1988
1989         if (WKStringIsEqualToUTF8CString(subMessageName, "CancelTouchPoint")) {
1990             WKRetainPtr<WKStringRef> indexKey = adoptWK(WKStringCreateWithUTF8CString("Index"));
1991             int index = static_cast<int>(WKUInt64GetValue(static_cast<WKUInt64Ref>(WKDictionaryGetItemForKey(messageBodyDictionary, indexKey.get()))));
1992             m_eventSenderProxy->cancelTouchPoint(index);
1993             return 0;
1994         }
1995 #endif
1996         ASSERT_NOT_REACHED();
1997     }
1998     return m_currentInvocation->didReceiveSynchronousMessageFromInjectedBundle(messageName, messageBody);
1999 }
2000
2001 WKRetainPtr<WKTypeRef> TestController::getInjectedBundleInitializationUserData()
2002 {
2003     return nullptr;
2004 }
2005
2006 // WKContextClient
2007
2008 void TestController::networkProcessDidCrash()
2009 {
2010     pid_t pid = WKContextGetNetworkProcessIdentifier(m_context.get());
2011     fprintf(stderr, "#CRASHED - %s (pid %ld)\n", networkProcessName(), static_cast<long>(pid));
2012     exit(1);
2013 }
2014
2015 // WKPageNavigationClient
2016
2017 void TestController::didCommitNavigation(WKPageRef page, WKNavigationRef navigation, WKTypeRef, const void* clientInfo)
2018 {
2019     static_cast<TestController*>(const_cast<void*>(clientInfo))->didCommitNavigation(page, navigation);
2020 }
2021
2022 void TestController::didFinishNavigation(WKPageRef page, WKNavigationRef navigation, WKTypeRef, const void* clientInfo)
2023 {
2024     static_cast<TestController*>(const_cast<void*>(clientInfo))->didFinishNavigation(page, navigation);
2025 }
2026
2027 void TestController::didReceiveServerRedirectForProvisionalNavigation(WKPageRef page, WKNavigationRef navigation, WKTypeRef userData, const void* clientInfo)
2028 {
2029     static_cast<TestController*>(const_cast<void*>(clientInfo))->didReceiveServerRedirectForProvisionalNavigation(page, navigation, userData);
2030 }
2031
2032 bool TestController::canAuthenticateAgainstProtectionSpace(WKPageRef page, WKProtectionSpaceRef protectionSpace, const void* clientInfo)
2033 {
2034     return static_cast<TestController*>(const_cast<void*>(clientInfo))->canAuthenticateAgainstProtectionSpace(page, protectionSpace);
2035 }
2036
2037 void TestController::didReceiveAuthenticationChallenge(WKPageRef page, WKAuthenticationChallengeRef authenticationChallenge, const void *clientInfo)
2038 {
2039     static_cast<TestController*>(const_cast<void*>(clientInfo))->didReceiveAuthenticationChallenge(page, /*frame,*/ authenticationChallenge);
2040 }
2041
2042 void TestController::processDidCrash(WKPageRef page, const void* clientInfo)
2043 {
2044     static_cast<TestController*>(const_cast<void*>(clientInfo))->processDidCrash();
2045 }
2046
2047 void TestController::didBeginNavigationGesture(WKPageRef page, const void *clientInfo)
2048 {
2049     static_cast<TestController*>(const_cast<void*>(clientInfo))->didBeginNavigationGesture(page);
2050 }
2051
2052 void TestController::willEndNavigationGesture(WKPageRef page, WKBackForwardListItemRef backForwardListItem, const void *clientInfo)
2053 {
2054     static_cast<TestController*>(const_cast<void*>(clientInfo))->willEndNavigationGesture(page, backForwardListItem);
2055 }
2056
2057 void TestController::didEndNavigationGesture(WKPageRef page, WKBackForwardListItemRef backForwardListItem, const void *clientInfo)
2058 {
2059     static_cast<TestController*>(const_cast<void*>(clientInfo))->didEndNavigationGesture(page, backForwardListItem);
2060 }
2061
2062 void TestController::didRemoveNavigationGestureSnapshot(WKPageRef page, const void *clientInfo)
2063 {
2064     static_cast<TestController*>(const_cast<void*>(clientInfo))->didRemoveNavigationGestureSnapshot(page);
2065 }
2066
2067 WKPluginLoadPolicy TestController::decidePolicyForPluginLoad(WKPageRef page, WKPluginLoadPolicy currentPluginLoadPolicy, WKDictionaryRef pluginInformation, WKStringRef* unavailabilityDescription, const void* clientInfo)
2068 {
2069     return static_cast<TestController*>(const_cast<void*>(clientInfo))->decidePolicyForPluginLoad(page, currentPluginLoadPolicy, pluginInformation, unavailabilityDescription);
2070 }
2071
2072 WKPluginLoadPolicy TestController::decidePolicyForPluginLoad(WKPageRef, WKPluginLoadPolicy currentPluginLoadPolicy, WKDictionaryRef pluginInformation, WKStringRef* unavailabilityDescription)
2073 {
2074     if (m_shouldBlockAllPlugins)
2075         return kWKPluginLoadPolicyBlocked;
2076
2077 #if PLATFORM(MAC)
2078     WKStringRef bundleIdentifier = (WKStringRef)WKDictionaryGetItemForKey(pluginInformation, WKPluginInformationBundleIdentifierKey());
2079     if (!bundleIdentifier)
2080         return currentPluginLoadPolicy;
2081
2082     if (WKStringIsEqualToUTF8CString(bundleIdentifier, "com.apple.QuickTime Plugin.plugin"))
2083         return currentPluginLoadPolicy;
2084
2085     if (WKStringIsEqualToUTF8CString(bundleIdentifier, "com.apple.testnetscapeplugin"))
2086         return currentPluginLoadPolicy;
2087
2088     RELEASE_ASSERT_NOT_REACHED(); // Please don't use any other plug-ins in tests, as they will not be installed on all machines.
2089 #else
2090     return currentPluginLoadPolicy;
2091 #endif
2092 }
2093
2094 void TestController::setBlockAllPlugins(bool shouldBlock)
2095 {
2096     m_shouldBlockAllPlugins = shouldBlock;
2097
2098 #if PLATFORM(MAC)
2099     auto policy = shouldBlock ? kWKPluginLoadClientPolicyBlock : kWKPluginLoadClientPolicyAllow;
2100
2101     WKRetainPtr<WKStringRef> nameNetscape = adoptWK(WKStringCreateWithUTF8CString("com.apple.testnetscapeplugin"));
2102     WKRetainPtr<WKStringRef> nameFlash = adoptWK(WKStringCreateWithUTF8CString("com.macromedia.Flash Player.plugin"));
2103     WKRetainPtr<WKStringRef> emptyString = adoptWK(WKStringCreateWithUTF8CString(""));
2104     WKContextSetPluginLoadClientPolicy(m_context.get(), policy, emptyString.get(), nameNetscape.get(), emptyString.get());
2105     WKContextSetPluginLoadClientPolicy(m_context.get(), policy, emptyString.get(), nameFlash.get(), emptyString.get());
2106 #endif
2107 }
2108
2109 void TestController::setPluginSupportedMode(const String& mode)
2110 {
2111     if (m_unsupportedPluginMode == mode)
2112         return;
2113
2114     m_unsupportedPluginMode = mode;
2115     if (m_unsupportedPluginMode.isEmpty()) {
2116         WKContextClearSupportedPlugins(m_context.get());
2117         return;
2118     }
2119
2120     WKRetainPtr<WKMutableArrayRef> emptyArray = adoptWK(WKMutableArrayCreate());
2121     WKRetainPtr<WKStringRef> allOrigins = adoptWK(WKStringCreateWithUTF8CString(""));
2122     WKRetainPtr<WKStringRef> specificOrigin = adoptWK(WKStringCreateWithUTF8CString("localhost"));
2123
2124     WKRetainPtr<WKStringRef> pdfName = adoptWK(WKStringCreateWithUTF8CString("My personal PDF"));
2125     WKContextAddSupportedPlugin(m_context.get(), allOrigins.get(), pdfName.get(), emptyArray.get(), emptyArray.get());
2126
2127     WKRetainPtr<WKStringRef> nameNetscape = adoptWK(WKStringCreateWithUTF8CString("com.apple.testnetscapeplugin"));
2128     WKRetainPtr<WKStringRef> mimeTypeNetscape = adoptWK(WKStringCreateWithUTF8CString("application/x-webkit-test-netscape"));
2129     WKRetainPtr<WKMutableArrayRef> mimeTypesNetscape = adoptWK(WKMutableArrayCreate());
2130     WKArrayAppendItem(mimeTypesNetscape.get(), mimeTypeNetscape.get());
2131
2132     WKRetainPtr<WKStringRef> namePdf = adoptWK(WKStringCreateWithUTF8CString("WebKit built-in PDF"));
2133
2134     if (m_unsupportedPluginMode == "allOrigins") {
2135         WKContextAddSupportedPlugin(m_context.get(), allOrigins.get(), nameNetscape.get(), mimeTypesNetscape.get(), emptyArray.get());
2136         WKContextAddSupportedPlugin(m_context.get(), allOrigins.get(), namePdf.get(), emptyArray.get(), emptyArray.get());
2137         return;
2138     }
2139
2140     if (m_unsupportedPluginMode == "specificOrigin") {
2141         WKContextAddSupportedPlugin(m_context.get(), specificOrigin.get(), nameNetscape.get(), mimeTypesNetscape.get(), emptyArray.get());
2142         WKContextAddSupportedPlugin(m_context.get(), specificOrigin.get(), namePdf.get(), emptyArray.get(), emptyArray.get());
2143         return;
2144     }
2145 }
2146
2147 void TestController::didCommitNavigation(WKPageRef page, WKNavigationRef navigation)
2148 {
2149     mainWebView()->focus();
2150 }
2151
2152 void TestController::didReceiveServerRedirectForProvisionalNavigation(WKPageRef page, WKNavigationRef navigation, WKTypeRef userData)
2153 {
2154     m_didReceiveServerRedirectForProvisionalNavigation = true;
2155     return;
2156 }
2157
2158 static const char* toString(WKProtectionSpaceAuthenticationScheme scheme)
2159 {
2160     switch (scheme) {
2161     case kWKProtectionSpaceAuthenticationSchemeDefault:
2162         return "ProtectionSpaceAuthenticationSchemeDefault";
2163     case kWKProtectionSpaceAuthenticationSchemeHTTPBasic:
2164         return "ProtectionSpaceAuthenticationSchemeHTTPBasic";
2165     case kWKProtectionSpaceAuthenticationSchemeHTMLForm:
2166         return "ProtectionSpaceAuthenticationSchemeHTMLForm";
2167     case kWKProtectionSpaceAuthenticationSchemeNTLM:
2168         return "ProtectionSpaceAuthenticationSchemeNTLM";
2169     case kWKProtectionSpaceAuthenticationSchemeNegotiate:
2170         return "ProtectionSpaceAuthenticationSchemeNegotiate";
2171     case kWKProtectionSpaceAuthenticationSchemeClientCertificateRequested:
2172         return "ProtectionSpaceAuthenticationSchemeClientCertificateRequested";
2173     case kWKProtectionSpaceAuthenticationSchemeServerTrustEvaluationRequested:
2174         return "ProtectionSpaceAuthenticationSchemeServerTrustEvaluationRequested";
2175     case kWKProtectionSpaceAuthenticationSchemeOAuth:
2176         return "ProtectionSpaceAuthenticationSchemeOAuth";
2177     case kWKProtectionSpaceAuthenticationSchemeUnknown:
2178         return "ProtectionSpaceAuthenticationSchemeUnknown";
2179     }
2180     ASSERT_NOT_REACHED();
2181     return "ProtectionSpaceAuthenticationSchemeUnknown";
2182 }
2183
2184 bool TestController::canAuthenticateAgainstProtectionSpace(WKPageRef page, WKProtectionSpaceRef protectionSpace)
2185 {
2186     if (m_shouldLogCanAuthenticateAgainstProtectionSpace)
2187         m_currentInvocation->outputText("canAuthenticateAgainstProtectionSpace\n");
2188     WKProtectionSpaceAuthenticationScheme authenticationScheme = WKProtectionSpaceGetAuthenticationScheme(protectionSpace);
2189     
2190     if (authenticationScheme == kWKProtectionSpaceAuthenticationSchemeServerTrustEvaluationRequested) {
2191         std::string host = toSTD(adoptWK(WKProtectionSpaceCopyHost(protectionSpace)).get());
2192         return host == "localhost" || host == "127.0.0.1" || (m_allowAnyHTTPSCertificateForAllowedHosts && m_allowedHosts.find(host) != m_allowedHosts.end());
2193     }
2194     
2195     return authenticationScheme <= kWKProtectionSpaceAuthenticationSchemeHTTPDigest || authenticationScheme == kWKProtectionSpaceAuthenticationSchemeOAuth;
2196 }
2197
2198 void TestController::didFinishNavigation(WKPageRef page, WKNavigationRef navigation)
2199 {
2200     if (m_state != Resetting)
2201         return;
2202
2203     WKRetainPtr<WKURLRef> wkURL(AdoptWK, WKFrameCopyURL(WKPageGetMainFrame(page)));
2204     if (!WKURLIsEqual(wkURL.get(), blankURL()))
2205         return;
2206
2207     m_doneResetting = true;
2208     singleton().notifyDone();
2209 }
2210
2211 void TestController::didReceiveAuthenticationChallenge(WKPageRef page, WKAuthenticationChallengeRef authenticationChallenge)
2212 {
2213     WKProtectionSpaceRef protectionSpace = WKAuthenticationChallengeGetProtectionSpace(authenticationChallenge);
2214     WKAuthenticationDecisionListenerRef decisionListener = WKAuthenticationChallengeGetDecisionListener(authenticationChallenge);
2215     WKProtectionSpaceAuthenticationScheme authenticationScheme = WKProtectionSpaceGetAuthenticationScheme(protectionSpace);
2216
2217     if (authenticationScheme == kWKProtectionSpaceAuthenticationSchemeServerTrustEvaluationRequested) {
2218         // Any non-empty credential signals to accept the server trust. Since the cross-platform API
2219         // doesn't expose a way to create a credential from server trust, we use a password credential.
2220
2221         m_serverTrustEvaluationCallbackCallsCount++;
2222
2223         WKRetainPtr<WKCredentialRef> credential = adoptWK(WKCredentialCreate(toWK("accept server trust").get(), toWK("").get(), kWKCredentialPersistenceNone));
2224         WKAuthenticationDecisionListenerUseCredential(decisionListener, credential.get());
2225         return;
2226     }
2227
2228     if (m_rejectsProtectionSpaceAndContinueForAuthenticationChallenges) {
2229         m_currentInvocation->outputText("Simulating reject protection space and continue for authentication challenge\n");
2230         WKAuthenticationDecisionListenerRejectProtectionSpaceAndContinue(decisionListener);
2231         return;
2232     }
2233
2234     std::string host = toSTD(adoptWK(WKProtectionSpaceCopyHost(protectionSpace)).get());
2235     int port = WKProtectionSpaceGetPort(protectionSpace);
2236     String message = makeString(host.c_str(), ':', port, " - didReceiveAuthenticationChallenge - ", toString(authenticationScheme), " - ");
2237     if (!m_handlesAuthenticationChallenges)
2238         message.append("Simulating cancelled authentication sheet\n");
2239     else
2240         message.append("Responding with " + m_authenticationUsername + ":" + m_authenticationPassword + "\n");
2241     m_currentInvocation->outputText(message);
2242
2243     if (!m_handlesAuthenticationChallenges) {
2244         WKAuthenticationDecisionListenerUseCredential(decisionListener, 0);
2245         return;
2246     }
2247     WKRetainPtr<WKStringRef> username(AdoptWK, WKStringCreateWithUTF8CString(m_authenticationUsername.utf8().data()));
2248     WKRetainPtr<WKStringRef> password(AdoptWK, WKStringCreateWithUTF8CString(m_authenticationPassword.utf8().data()));
2249     WKRetainPtr<WKCredentialRef> credential(AdoptWK, WKCredentialCreate(username.get(), password.get(), kWKCredentialPersistenceForSession));
2250     WKAuthenticationDecisionListenerUseCredential(decisionListener, credential.get());
2251 }
2252
2253     
2254 // WKContextDownloadClient
2255
2256 void TestController::downloadDidStart(WKContextRef context, WKDownloadRef download, const void* clientInfo)
2257 {
2258     static_cast<TestController*>(const_cast<void*>(clientInfo))->downloadDidStart(context, download);
2259 }
2260     
2261 WKStringRef TestController::decideDestinationWithSuggestedFilename(WKContextRef context, WKDownloadRef download, WKStringRef filename, bool* allowOverwrite, const void* clientInfo)
2262 {
2263     return static_cast<TestController*>(const_cast<void*>(clientInfo))->decideDestinationWithSuggestedFilename(context, download, filename, allowOverwrite);
2264 }
2265
2266 void TestController::downloadDidFinish(WKContextRef context, WKDownloadRef download, const void* clientInfo)
2267 {
2268     static_cast<TestController*>(const_cast<void*>(clientInfo))->downloadDidFinish(context, download);
2269 }
2270
2271 void TestController::downloadDidFail(WKContextRef context, WKDownloadRef download, WKErrorRef error, const void* clientInfo)
2272 {
2273     static_cast<TestController*>(const_cast<void*>(clientInfo))->downloadDidFail(context, download, error);
2274 }
2275
2276 void TestController::downloadDidCancel(WKContextRef context, WKDownloadRef download, const void* clientInfo)
2277 {
2278     static_cast<TestController*>(const_cast<void*>(clientInfo))->downloadDidCancel(context, download);
2279 }
2280
2281 void TestController::downloadDidReceiveServerRedirectToURL(WKContextRef context, WKDownloadRef download, WKURLRef url, const void* clientInfo)
2282 {
2283     static_cast<TestController*>(const_cast<void*>(clientInfo))->downloadDidReceiveServerRedirectToURL(context, download, url);
2284 }
2285
2286 void TestController::downloadDidStart(WKContextRef context, WKDownloadRef download)
2287 {
2288     if (m_shouldLogDownloadCallbacks)
2289         m_currentInvocation->outputText("Download started.\n");
2290 }
2291
2292 WKStringRef TestController::decideDestinationWithSuggestedFilename(WKContextRef, WKDownloadRef, WKStringRef filename, bool*& allowOverwrite)
2293 {
2294     String suggestedFilename = toWTFString(filename);
2295
2296     if (m_shouldLogDownloadCallbacks) {
2297         StringBuilder builder;
2298         builder.append("Downloading URL with suggested filename \"");
2299         builder.append(suggestedFilename);
2300         builder.append("\"\n");
2301         m_currentInvocation->outputText(builder.toString());
2302     }
2303
2304     const char* dumpRenderTreeTemp = libraryPathForTesting();
2305     if (!dumpRenderTreeTemp)
2306         return nullptr;
2307
2308     *allowOverwrite = true;
2309     String temporaryFolder = String::fromUTF8(dumpRenderTreeTemp);
2310     if (suggestedFilename.isEmpty())
2311         suggestedFilename = "Unknown";
2312
2313     return toWK(temporaryFolder + "/" + suggestedFilename).leakRef();
2314 }
2315
2316 void TestController::downloadDidFinish(WKContextRef, WKDownloadRef)
2317 {
2318     if (m_shouldLogDownloadCallbacks)
2319         m_currentInvocation->outputText("Download completed.\n");
2320     m_currentInvocation->notifyDownloadDone();
2321 }
2322
2323 void TestController::downloadDidReceiveServerRedirectToURL(WKContextRef, WKDownloadRef, WKURLRef url)
2324 {
2325     if (m_shouldLogDownloadCallbacks) {
2326         StringBuilder builder;
2327         builder.appendLiteral("Download was redirected to \"");
2328         WKRetainPtr<WKStringRef> urlStringWK = adoptWK(WKURLCopyString(url));
2329         builder.append(toSTD(urlStringWK).c_str());
2330         builder.appendLiteral("\".\n");
2331         m_currentInvocation->outputText(builder.toString());
2332     }
2333 }
2334
2335 void TestController::downloadDidFail(WKContextRef, WKDownloadRef, WKErrorRef error)
2336 {
2337     if (m_shouldLogDownloadCallbacks) {
2338         m_currentInvocation->outputText("Download failed.\n"_s);
2339
2340         WKRetainPtr<WKStringRef> errorDomain = adoptWK(WKErrorCopyDomain(error));
2341         WKRetainPtr<WKStringRef> errorDescription = adoptWK(WKErrorCopyLocalizedDescription(error));
2342         int errorCode = WKErrorGetErrorCode(error);
2343
2344         StringBuilder errorBuilder;
2345         errorBuilder.append("Failed: ");
2346         errorBuilder.append(toWTFString(errorDomain));
2347         errorBuilder.append(", code=");
2348         errorBuilder.appendNumber(errorCode);
2349         errorBuilder.append(", description=");
2350         errorBuilder.append(toWTFString(errorDescription));
2351         errorBuilder.append("\n");
2352
2353         m_currentInvocation->outputText(errorBuilder.toString());
2354     }
2355     m_currentInvocation->notifyDownloadDone();
2356 }
2357
2358 void TestController::downloadDidCancel(WKContextRef, WKDownloadRef)
2359 {
2360     if (m_shouldLogDownloadCallbacks)
2361         m_currentInvocation->outputText("Download cancelled.\n");
2362     m_currentInvocation->notifyDownloadDone();
2363 }
2364
2365 void TestController::processDidCrash()
2366 {
2367     // This function can be called multiple times when crash logs are being saved on Windows, so
2368     // ensure we only print the crashed message once.
2369     if (!m_didPrintWebProcessCrashedMessage) {
2370         pid_t pid = WKPageGetProcessIdentifier(m_mainWebView->page());
2371         fprintf(stderr, "#CRASHED - %s (pid %ld)\n", webProcessName(), static_cast<long>(pid));
2372         fflush(stderr);
2373         m_didPrintWebProcessCrashedMessage = true;
2374     }
2375
2376     if (m_shouldExitWhenWebProcessCrashes)
2377         exit(1);
2378 }
2379
2380 void TestController::didBeginNavigationGesture(WKPageRef)
2381 {
2382     m_currentInvocation->didBeginSwipe();
2383 }
2384
2385 void TestController::willEndNavigationGesture(WKPageRef, WKBackForwardListItemRef)
2386 {
2387     m_currentInvocation->willEndSwipe();
2388 }
2389
2390 void TestController::didEndNavigationGesture(WKPageRef, WKBackForwardListItemRef)
2391 {
2392     m_currentInvocation->didEndSwipe();
2393 }
2394
2395 void TestController::didRemoveNavigationGestureSnapshot(WKPageRef)
2396 {
2397     m_currentInvocation->didRemoveSwipeSnapshot();
2398 }
2399
2400 void TestController::simulateWebNotificationClick(uint64_t notificationID)
2401 {
2402     m_webNotificationProvider.simulateWebNotificationClick(mainWebView()->page(), notificationID);
2403 }
2404
2405 void TestController::setGeolocationPermission(bool enabled)
2406 {
2407     m_isGeolocationPermissionSet = true;
2408     m_isGeolocationPermissionAllowed = enabled;
2409     decidePolicyForGeolocationPermissionRequestIfPossible();
2410 }
2411
2412 void TestController::setMockGeolocationPosition(double latitude, double longitude, double accuracy, bool providesAltitude, double altitude, bool providesAltitudeAccuracy, double altitudeAccuracy, bool providesHeading, double heading, bool providesSpeed, double speed, bool providesFloorLevel, double floorLevel)
2413 {
2414     m_geolocationProvider->setPosition(latitude, longitude, accuracy, providesAltitude, altitude, providesAltitudeAccuracy, altitudeAccuracy, providesHeading, heading, providesSpeed, speed, providesFloorLevel, floorLevel);
2415 }
2416
2417 void TestController::setMockGeolocationPositionUnavailableError(WKStringRef errorMessage)
2418 {
2419     m_geolocationProvider->setPositionUnavailableError(errorMessage);
2420 }
2421
2422 void TestController::handleGeolocationPermissionRequest(WKGeolocationPermissionRequestRef geolocationPermissionRequest)
2423 {
2424     m_geolocationPermissionRequests.append(geolocationPermissionRequest);
2425     decidePolicyForGeolocationPermissionRequestIfPossible();
2426 }
2427
2428 bool TestController::isGeolocationProviderActive() const
2429 {
2430     return m_geolocationProvider->isActive();
2431 }
2432
2433 static String originUserVisibleName(WKSecurityOriginRef origin)
2434 {
2435     if (!origin)
2436         return emptyString();
2437
2438     auto host = toWTFString(adoptWK(WKSecurityOriginCopyHost(origin)));
2439     auto protocol = toWTFString(adoptWK(WKSecurityOriginCopyProtocol(origin)));
2440
2441     if (host.isEmpty() || protocol.isEmpty())
2442         return emptyString();
2443
2444     if (int port = WKSecurityOriginGetPort(origin))
2445         return makeString(protocol, "://", host, ':', port);
2446
2447     return makeString(protocol, "://", host);
2448 }
2449
2450 static String userMediaOriginHash(WKSecurityOriginRef userMediaDocumentOrigin, WKSecurityOriginRef topLevelDocumentOrigin)
2451 {
2452     String userMediaDocumentOriginString = originUserVisibleName(userMediaDocumentOrigin);
2453     String topLevelDocumentOriginString = originUserVisibleName(topLevelDocumentOrigin);
2454
2455     if (topLevelDocumentOriginString.isEmpty())
2456         return userMediaDocumentOriginString;
2457
2458     return makeString(userMediaDocumentOriginString, '-', topLevelDocumentOriginString);
2459 }
2460
2461 static String userMediaOriginHash(WKStringRef userMediaDocumentOriginString, WKStringRef topLevelDocumentOriginString)
2462 {
2463     auto userMediaDocumentOrigin = adoptWK(WKSecurityOriginCreateFromString(userMediaDocumentOriginString));
2464     if (!WKStringGetLength(topLevelDocumentOriginString))
2465         return userMediaOriginHash(userMediaDocumentOrigin.get(), nullptr);
2466
2467     auto topLevelDocumentOrigin = adoptWK(WKSecurityOriginCreateFromString(topLevelDocumentOriginString));
2468     return userMediaOriginHash(userMediaDocumentOrigin.get(), topLevelDocumentOrigin.get());
2469 }
2470
2471 void TestController::setUserMediaPermission(bool enabled)
2472 {
2473     m_isUserMediaPermissionSet = true;
2474     m_isUserMediaPermissionAllowed = enabled;
2475     decidePolicyForUserMediaPermissionRequestIfPossible();
2476 }
2477
2478 void TestController::resetUserMediaPermission()
2479 {
2480     m_isUserMediaPermissionSet = false;
2481 }
2482
2483 void TestController::setShouldDismissJavaScriptAlertsAsynchronously(bool value)
2484 {
2485     m_shouldDismissJavaScriptAlertsAsynchronously = value;
2486 }
2487
2488 void TestController::handleJavaScriptAlert(WKPageRunJavaScriptAlertResultListenerRef listener)
2489 {
2490     if (!m_shouldDismissJavaScriptAlertsAsynchronously) {
2491         WKPageRunJavaScriptAlertResultListenerCall(listener);
2492         return;
2493     }
2494
2495     WKRetain(listener);
2496     callOnMainThread([listener] {
2497         WKPageRunJavaScriptAlertResultListenerCall(listener);
2498         WKRelease(listener);
2499     });
2500 }
2501
2502 class OriginSettings : public RefCounted<OriginSettings> {
2503 public:
2504     explicit OriginSettings()
2505     {
2506     }
2507
2508     bool persistentPermission() const { return m_persistentPermission; }
2509     void setPersistentPermission(bool permission) { m_persistentPermission = permission; }
2510
2511     String persistentSalt() const { return m_persistentSalt; }
2512     void setPersistentSalt(const String& salt) { m_persistentSalt = salt; }
2513
2514     HashMap<uint64_t, String>& ephemeralSalts() { return m_ephemeralSalts; }
2515
2516     void incrementRequestCount() { ++m_requestCount; }
2517     void resetRequestCount() { m_requestCount = 0; }
2518     unsigned requestCount() const { return m_requestCount; }
2519
2520 private:
2521     HashMap<uint64_t, String> m_ephemeralSalts;
2522     String m_persistentSalt;
2523     unsigned m_requestCount { 0 };
2524     bool m_persistentPermission { false };
2525 };
2526
2527 String TestController::saltForOrigin(WKFrameRef frame, String originHash)
2528 {
2529     auto& settings = settingsForOrigin(originHash);
2530     auto& ephemeralSalts = settings.ephemeralSalts();
2531     auto frameInfo = adoptWK(WKFrameCreateFrameInfo(frame));
2532     auto frameHandle = WKFrameInfoGetFrameHandleRef(frameInfo.get());
2533     uint64_t frameIdentifier = WKFrameHandleGetFrameID(frameHandle);
2534     String frameSalt = ephemeralSalts.get(frameIdentifier);
2535
2536     if (settings.persistentPermission()) {
2537         if (frameSalt.length())
2538             return frameSalt;
2539
2540         if (!settings.persistentSalt().length())
2541             settings.setPersistentSalt(createCanonicalUUIDString());
2542
2543         return settings.persistentSalt();
2544     }
2545
2546     if (!frameSalt.length()) {
2547         frameSalt = createCanonicalUUIDString();
2548         ephemeralSalts.add(frameIdentifier, frameSalt);
2549     }
2550
2551     return frameSalt;
2552 }
2553
2554 void TestController::setUserMediaPersistentPermissionForOrigin(bool permission, WKStringRef userMediaDocumentOriginString, WKStringRef topLevelDocumentOriginString)
2555 {
2556     auto originHash = userMediaOriginHash(userMediaDocumentOriginString, topLevelDocumentOriginString);
2557     auto& settings = settingsForOrigin(originHash);
2558     settings.setPersistentPermission(permission);
2559 }
2560
2561 void TestController::handleCheckOfUserMediaPermissionForOrigin(WKFrameRef frame, WKSecurityOriginRef userMediaDocumentOrigin, WKSecurityOriginRef topLevelDocumentOrigin, const WKUserMediaPermissionCheckRef& checkRequest)
2562 {
2563     auto originHash = userMediaOriginHash(userMediaDocumentOrigin, topLevelDocumentOrigin);
2564     auto salt = saltForOrigin(frame, originHash);
2565     WKRetainPtr<WKStringRef> saltString = adoptWK(WKStringCreateWithUTF8CString(salt.utf8().data()));
2566
2567     WKUserMediaPermissionCheckSetUserMediaAccessInfo(checkRequest, saltString.get(), settingsForOrigin(originHash).persistentPermission());
2568 }
2569
2570 bool TestController::handleDeviceOrientationAndMotionAccessRequest(WKSecurityOriginRef origin)
2571 {
2572     m_currentInvocation->outputText(makeString("Received device orientation & motion access request for security origin \"", originUserVisibleName(origin), "\".\n"));
2573     return m_shouldAllowDeviceOrientationAndMotionAccess;
2574 }
2575
2576 void TestController::handleUserMediaPermissionRequest(WKFrameRef frame, WKSecurityOriginRef userMediaDocumentOrigin, WKSecurityOriginRef topLevelDocumentOrigin, WKUserMediaPermissionRequestRef request)
2577 {
2578     auto originHash = userMediaOriginHash(userMediaDocumentOrigin, topLevelDocumentOrigin);
2579     m_userMediaPermissionRequests.append(std::make_pair(originHash, request));
2580     decidePolicyForUserMediaPermissionRequestIfPossible();
2581 }
2582
2583 OriginSettings& TestController::settingsForOrigin(const String& originHash)
2584 {
2585     RefPtr<OriginSettings> settings = m_cachedUserMediaPermissions.get(originHash);
2586     if (!settings) {
2587         settings = adoptRef(*new OriginSettings());
2588         m_cachedUserMediaPermissions.add(originHash, settings);
2589     }
2590
2591     return *settings;
2592 }
2593
2594 unsigned TestController::userMediaPermissionRequestCountForOrigin(WKStringRef userMediaDocumentOriginString, WKStringRef topLevelDocumentOriginString)
2595 {
2596     auto originHash = userMediaOriginHash(userMediaDocumentOriginString, topLevelDocumentOriginString);
2597     return settingsForOrigin(originHash).requestCount();
2598 }
2599
2600 void TestController::resetUserMediaPermissionRequestCountForOrigin(WKStringRef userMediaDocumentOriginString, WKStringRef topLevelDocumentOriginString)
2601 {
2602     auto originHash = userMediaOriginHash(userMediaDocumentOriginString, topLevelDocumentOriginString);
2603     settingsForOrigin(originHash).resetRequestCount();
2604 }
2605
2606 void TestController::decidePolicyForUserMediaPermissionRequestIfPossible()
2607 {
2608     if (!m_isUserMediaPermissionSet)
2609         return;
2610
2611     for (auto& pair : m_userMediaPermissionRequests) {
2612         auto originHash = pair.first;
2613         auto request = pair.second.get();
2614
2615         auto& settings = settingsForOrigin(originHash);
2616         settings.incrementRequestCount();
2617
2618         if (!m_isUserMediaPermissionAllowed && !settings.persistentPermission()) {
2619             WKUserMediaPermissionRequestDeny(request, kWKPermissionDenied);
2620             continue;
2621         }
2622
2623         WKRetainPtr<WKArrayRef> audioDeviceUIDs = adoptWK(WKUserMediaPermissionRequestAudioDeviceUIDs(request));
2624         WKRetainPtr<WKArrayRef> videoDeviceUIDs = adoptWK(WKUserMediaPermissionRequestVideoDeviceUIDs(request));
2625
2626         if (!WKArrayGetSize(videoDeviceUIDs.get()) && !WKArrayGetSize(audioDeviceUIDs.get())) {
2627             WKUserMediaPermissionRequestDeny(request, kWKNoConstraints);
2628             continue;
2629         }
2630
2631         WKRetainPtr<WKStringRef> videoDeviceUID;
2632         if (WKArrayGetSize(videoDeviceUIDs.get()))
2633             videoDeviceUID = reinterpret_cast<WKStringRef>(WKArrayGetItemAtIndex(videoDeviceUIDs.get(), 0));
2634         else
2635             videoDeviceUID = adoptWK(WKStringCreateWithUTF8CString(""));
2636
2637         WKRetainPtr<WKStringRef> audioDeviceUID;
2638         if (WKArrayGetSize(audioDeviceUIDs.get()))
2639             audioDeviceUID = reinterpret_cast<WKStringRef>(WKArrayGetItemAtIndex(audioDeviceUIDs.get(), 0));
2640         else
2641             audioDeviceUID = adoptWK(WKStringCreateWithUTF8CString(""));
2642
2643         WKUserMediaPermissionRequestAllow(request, audioDeviceUID.get(), videoDeviceUID.get());
2644     }
2645     m_userMediaPermissionRequests.clear();
2646 }
2647
2648 void TestController::setCustomPolicyDelegate(bool enabled, bool permissive)
2649 {
2650     m_policyDelegateEnabled = enabled;
2651     m_policyDelegatePermissive = permissive;
2652 }
2653
2654 void TestController::decidePolicyForGeolocationPermissionRequestIfPossible()
2655 {
2656     if (!m_isGeolocationPermissionSet)
2657         return;
2658
2659     for (size_t i = 0; i < m_geolocationPermissionRequests.size(); ++i) {
2660         WKGeolocationPermissionRequestRef permissionRequest = m_geolocationPermissionRequests[i].get();
2661         if (m_isGeolocationPermissionAllowed)
2662             WKGeolocationPermissionRequestAllow(permissionRequest);
2663         else
2664             WKGeolocationPermissionRequestDeny(permissionRequest);
2665     }
2666     m_geolocationPermissionRequests.clear();
2667 }
2668
2669 void TestController::decidePolicyForNotificationPermissionRequest(WKPageRef page, WKSecurityOriginRef origin, WKNotificationPermissionRequestRef request, const void*)
2670 {
2671     TestController::singleton().decidePolicyForNotificationPermissionRequest(page, origin, request);
2672 }
2673
2674 void TestController::decidePolicyForNotificationPermissionRequest(WKPageRef, WKSecurityOriginRef, WKNotificationPermissionRequestRef request)
2675 {
2676     WKNotificationPermissionRequestAllow(request);
2677 }
2678
2679 void TestController::unavailablePluginButtonClicked(WKPageRef, WKPluginUnavailabilityReason, WKDictionaryRef, const void*)
2680 {
2681     printf("MISSING PLUGIN BUTTON PRESSED\n");
2682 }
2683
2684 void TestController::decidePolicyForNavigationAction(WKPageRef, WKNavigationActionRef navigationAction, WKFramePolicyListenerRef listener, WKTypeRef, const void* clientInfo)
2685 {
2686     static_cast<TestController*>(const_cast<void*>(clientInfo))->decidePolicyForNavigationAction(navigationAction, listener);
2687 }
2688
2689 void TestController::decidePolicyForNavigationAction(WKNavigationActionRef navigationAction, WKFramePolicyListenerRef listener)
2690 {
2691     WKRetainPtr<WKFramePolicyListenerRef> retainedListener { listener };
2692     WKRetainPtr<WKNavigationActionRef> retainedNavigationAction { navigationAction };
2693     const bool shouldIgnore { m_policyDelegateEnabled && !m_policyDelegatePermissive };
2694     auto decisionFunction = [shouldIgnore, retainedListener, retainedNavigationAction]() {
2695         if (shouldIgnore)
2696             WKFramePolicyListenerIgnore(retainedListener.get());
2697         else if (WKNavigationActionShouldPerformDownload(retainedNavigationAction.get()))
2698             WKFramePolicyListenerDownload(retainedListener.get());
2699         else
2700             WKFramePolicyListenerUse(retainedListener.get());
2701     };
2702
2703     if (m_shouldDecideNavigationPolicyAfterDelay)
2704         RunLoop::main().dispatch(WTFMove(decisionFunction));
2705     else
2706         decisionFunction();
2707 }
2708
2709 void TestController::decidePolicyForNavigationResponse(WKPageRef, WKNavigationResponseRef navigationResponse, WKFramePolicyListenerRef listener, WKTypeRef, const void* clientInfo)
2710 {
2711     static_cast<TestController*>(const_cast<void*>(clientInfo))->decidePolicyForNavigationResponse(navigationResponse, listener);
2712 }
2713
2714 void TestController::decidePolicyForNavigationResponse(WKNavigationResponseRef navigationResponse, WKFramePolicyListenerRef listener)
2715 {
2716     WKRetainPtr<WKNavigationResponseRef> retainedNavigationResponse { navigationResponse };
2717     WKRetainPtr<WKFramePolicyListenerRef> retainedListener { listener };
2718
2719     bool shouldDownloadUndisplayableMIMETypes = m_shouldDownloadUndisplayableMIMETypes;
2720     auto decisionFunction = [shouldDownloadUndisplayableMIMETypes, retainedNavigationResponse, retainedListener]() {
2721         // Even though Response was already checked by WKBundlePagePolicyClient, the check did not include plugins
2722         // so we have to re-check again.
2723         if (WKNavigationResponseCanShowMIMEType(retainedNavigationResponse.get())) {
2724             WKFramePolicyListenerUse(retainedListener.get());
2725             return;
2726         }
2727
2728         if (shouldDownloadUndisplayableMIMETypes)
2729             WKFramePolicyListenerDownload(retainedListener.get());
2730         else
2731             WKFramePolicyListenerIgnore(retainedListener.get());
2732     };
2733
2734     if (m_shouldDecideResponsePolicyAfterDelay)
2735         RunLoop::main().dispatch(WTFMove(decisionFunction));
2736     else
2737         decisionFunction();
2738 }
2739
2740 void TestController::didNavigateWithNavigationData(WKContextRef, WKPageRef, WKNavigationDataRef navigationData, WKFrameRef frame, const void* clientInfo)
2741 {
2742     static_cast<TestController*>(const_cast<void*>(clientInfo))->didNavigateWithNavigationData(navigationData, frame);
2743 }
2744
2745 void TestController::didNavigateWithNavigationData(WKNavigationDataRef navigationData, WKFrameRef)
2746 {
2747     if (m_state != RunningTest)
2748         return;
2749
2750     if (!m_shouldLogHistoryClientCallbacks)
2751         return;
2752
2753     // URL
2754     auto url = adoptWK(WKNavigationDataCopyURL(navigationData));
2755     auto urlString = toWTFString(adoptWK(WKURLCopyString(url.get())));
2756     // Title
2757     auto title = toWTFString(adoptWK(WKNavigationDataCopyTitle(navigationData)));
2758     // HTTP method
2759     auto request = adoptWK(WKNavigationDataCopyOriginalRequest(navigationData));
2760     auto method = toWTFString(adoptWK(WKURLRequestCopyHTTPMethod(request.get())));
2761
2762     // FIXME: Determine whether the navigation was successful / a client redirect rather than hard-coding the message here.
2763     m_currentInvocation->outputText(makeString("WebView navigated to url \"", urlString, "\" with title \"", title, "\" with HTTP equivalent method \"", method,
2764         "\".  The navigation was successful and was not a client redirect.\n"));
2765 }
2766
2767 void TestController::didPerformClientRedirect(WKContextRef, WKPageRef, WKURLRef sourceURL, WKURLRef destinationURL, WKFrameRef frame, const void* clientInfo)
2768 {
2769     static_cast<TestController*>(const_cast<void*>(clientInfo))->didPerformClientRedirect(sourceURL, destinationURL, frame);
2770 }
2771
2772 void TestController::didPerformClientRedirect(WKURLRef sourceURL, WKURLRef destinationURL, WKFrameRef)
2773 {
2774     if (m_state != RunningTest)
2775         return;
2776
2777     if (!m_shouldLogHistoryClientCallbacks)
2778         return;
2779
2780     auto source = toWTFString(adoptWK(WKURLCopyString(sourceURL)));
2781     auto destination = toWTFString(adoptWK(WKURLCopyString(destinationURL)));
2782
2783     m_currentInvocation->outputText(makeString("WebView performed a client redirect from \"", source, "\" to \"", destination, "\".\n"));
2784 }
2785
2786 void TestController::didPerformServerRedirect(WKContextRef, WKPageRef, WKURLRef sourceURL, WKURLRef destinationURL, WKFrameRef frame, const void* clientInfo)
2787 {
2788     static_cast<TestController*>(const_cast<void*>(clientInfo))->didPerformServerRedirect(sourceURL, destinationURL, frame);
2789 }
2790
2791 void TestController::didPerformServerRedirect(WKURLRef sourceURL, WKURLRef destinationURL, WKFrameRef)
2792 {
2793     if (m_state != RunningTest)
2794         return;
2795
2796     if (!m_shouldLogHistoryClientCallbacks)
2797         return;
2798
2799     auto source = toWTFString(adoptWK(WKURLCopyString(sourceURL)));
2800     auto destination = toWTFString(adoptWK(WKURLCopyString(destinationURL)));
2801
2802     m_currentInvocation->outputText(makeString("WebView performed a server redirect from \"", source, "\" to \"", destination, "\".\n"));
2803 }
2804
2805 void TestController::didUpdateHistoryTitle(WKContextRef, WKPageRef, WKStringRef title, WKURLRef URL, WKFrameRef frame, const void* clientInfo)
2806 {
2807     static_cast<TestController*>(const_cast<void*>(clientInfo))->didUpdateHistoryTitle(title, URL, frame);
2808 }
2809
2810 void TestController::didUpdateHistoryTitle(WKStringRef title, WKURLRef URL, WKFrameRef)
2811 {
2812     if (m_state != RunningTest)
2813         return;
2814
2815     if (!m_shouldLogHistoryClientCallbacks)
2816         return;
2817
2818     auto urlString = toWTFString(adoptWK(WKURLCopyString(URL)));
2819     m_currentInvocation->outputText(makeString("WebView updated the title for history URL \"", urlString, "\" to \"", toWTFString(title), "\".\n"));
2820 }
2821
2822 void TestController::setNavigationGesturesEnabled(bool value)
2823 {
2824     m_mainWebView->setNavigationGesturesEnabled(value);
2825 }
2826
2827 void TestController::setIgnoresViewportScaleLimits(bool ignoresViewportScaleLimits)
2828 {
2829     WKPageSetIgnoresViewportScaleLimits(m_mainWebView->page(), ignoresViewportScaleLimits);
2830 }
2831
2832 void TestController::terminateNetworkProcess()
2833 {
2834     WKContextTerminateNetworkProcess(platformContext());
2835 }
2836
2837 void TestController::terminateServiceWorkerProcess()
2838 {
2839     WKContextTerminateServiceWorkerProcess(platformContext());
2840 }
2841
2842 #if !PLATFORM(COCOA)
2843 void TestController::platformWillRunTest(const TestInvocation&)
2844 {
2845 }
2846
2847 void TestController::platformCreateWebView(WKPageConfigurationRef configuration, const TestOptions& options)
2848 {
2849     m_mainWebView = std::make_unique<PlatformWebView>(configuration, options);
2850 }
2851
2852 PlatformWebView* TestController::platformCreateOtherPage(PlatformWebView* parentView, WKPageConfigurationRef configuration, const TestOptions& options)
2853 {
2854     return new PlatformWebView(configuration, options);
2855 }
2856
2857 WKContextRef TestController::platformAdjustContext(WKContextRef context, WKContextConfigurationRef contextConfiguration)
2858 {
2859     auto* dataStore = WKContextGetWebsiteDataStore(context);
2860     WKWebsiteDataStoreSetResourceLoadStatisticsEnabled(dataStore, true);
2861
2862     if (const char* dumpRenderTreeTemp = libraryPathForTesting()) {
2863         String temporaryFolder = String::fromUTF8(dumpRenderTreeTemp);
2864
2865         WKWebsiteDataStoreSetServiceWorkerRegistrationDirectory(dataStore, toWK(temporaryFolder + pathSeparator + "ServiceWorkers").get());
2866     }
2867
2868     return context;
2869 }
2870
2871 void TestController::platformResetStateToConsistentValues(const TestOptions&)
2872 {
2873 }
2874
2875 unsigned TestController::imageCountInGeneralPasteboard() const
2876 {
2877     return 0;
2878 }
2879
2880 void TestController::removeAllSessionCredentials()
2881 {
2882 }
2883
2884 void TestController::getAllStorageAccessEntries()
2885 {
2886 }
2887
2888 #endif
2889
2890 struct ClearServiceWorkerRegistrationsCallbackContext {
2891     explicit ClearServiceWorkerRegistrationsCallbackContext(TestController& controller)
2892         : testController(controller)
2893     {
2894     }
2895
2896     TestController& testController;
2897     bool done { false };
2898 };
2899
2900 static void clearServiceWorkerRegistrationsCallback(void* userData)
2901 {
2902     auto* context = static_cast<ClearServiceWorkerRegistrationsCallbackContext*>(userData);
2903     context->done = true;
2904     context->testController.notifyDone();
2905 }
2906
2907 void TestController::clearServiceWorkerRegistrations()
2908 {
2909     auto websiteDataStore = WKContextGetWebsiteDataStore(platformContext());
2910     ClearServiceWorkerRegistrationsCallbackContext context(*this);
2911
2912     WKWebsiteDataStoreRemoveAllServiceWorkerRegistrations(websiteDataStore, &context, clearServiceWorkerRegistrationsCallback);
2913     runUntil(context.done, noTimeout);
2914 }
2915
2916 struct ClearDOMCacheCallbackContext {
2917     explicit ClearDOMCacheCallbackContext(TestController& controller)
2918         : testController(controller)
2919     {
2920     }
2921
2922     TestController& testController;
2923     bool done { false };
2924 };
2925
2926 static void clearDOMCacheCallback(void* userData)
2927 {
2928     auto* context = static_cast<ClearDOMCacheCallbackContext*>(userData);
2929     context->done = true;
2930     context->testController.notifyDone();
2931 }
2932
2933 void TestController::clearDOMCache(WKStringRef origin)
2934 {
2935     auto websiteDataStore = WKContextGetWebsiteDataStore(platformContext());
2936     ClearDOMCacheCallbackContext context(*this);
2937
2938     auto cacheOrigin = adoptWK(WKSecurityOriginCreateFromString(origin));
2939     WKWebsiteDataStoreRemoveFetchCacheForOrigin(websiteDataStore, cacheOrigin.get(), &context, clearDOMCacheCallback);
2940     runUntil(context.done, noTimeout);
2941 }
2942
2943 void TestController::clearDOMCaches()
2944 {
2945     auto websiteDataStore = WKContextGetWebsiteDataStore(platformContext());
2946     ClearDOMCacheCallbackContext context(*this);
2947
2948     WKWebsiteDataStoreRemoveAllFetchCaches(websiteDataStore, &context, clearDOMCacheCallback);
2949     runUntil(context.done, noTimeout);
2950 }
2951
2952 void TestController::setIDBPerOriginQuota(uint64_t quota)
2953 {
2954     WKContextSetIDBPerOriginQuota(platformContext(), quota);
2955 }
2956
2957 struct RemoveAllIndexedDatabasesCallbackContext {
2958     explicit RemoveAllIndexedDatabasesCallbackContext(TestController& controller)
2959         : testController(controller)
2960     {
2961     }
2962
2963     TestController& testController;
2964     bool done { false };
2965 };
2966
2967 static void RemoveAllIndexedDatabasesCallback(void* userData)
2968 {
2969     auto* context = static_cast<RemoveAllIndexedDatabasesCallbackContext*>(userData);
2970     context->done = true;
2971     context->testController.notifyDone();
2972 }
2973
2974 void TestController::ClearIndexedDatabases()
2975 {
2976     auto websiteDataStore = WKContextGetWebsiteDataStore(platformContext());
2977     RemoveAllIndexedDatabasesCallbackContext context(*this);
2978     WKWebsiteDataStoreRemoveAllIndexedDatabases(websiteDataStore, &context, RemoveAllIndexedDatabasesCallback);
2979     runUntil(context.done, noTimeout);
2980 }
2981
2982 struct FetchCacheOriginsCallbackContext {
2983     FetchCacheOriginsCallbackContext(TestController& controller, WKStringRef origin)
2984         : testController(controller)
2985         , origin(origin)
2986     {
2987     }
2988
2989     TestController& testController;
2990     WKStringRef origin;
2991
2992     bool done { false };
2993     bool result { false };
2994 };
2995
2996 static void fetchCacheOriginsCallback(WKArrayRef origins, void* userData)
2997 {
2998     auto* context = static_cast<FetchCacheOriginsCallbackContext*>(userData);
2999     context->done = true;
3000
3001     auto size = WKArrayGetSize(origins);
3002     for (size_t index = 0; index < size && !context->result; ++index) {
3003         WKSecurityOriginRef securityOrigin = reinterpret_cast<WKSecurityOriginRef>(WKArrayGetItemAtIndex(origins, index));
3004         if (WKStringIsEqual(context->origin, adoptWK(WKSecurityOriginCopyToString(securityOrigin)).get()))
3005             context->result = true;
3006     }
3007     context->testController.notifyDone();
3008 }
3009
3010 bool TestController::hasDOMCache(WKStringRef origin)
3011 {
3012     auto* dataStore = WKContextGetWebsiteDataStore(platformContext());
3013     FetchCacheOriginsCallbackContext context(*this, origin);
3014     WKWebsiteDataStoreGetFetchCacheOrigins(dataStore, &context, fetchCacheOriginsCallback);
3015     runUntil(context.done, noTimeout);
3016     return context.result;
3017 }
3018
3019 struct FetchCacheSizeForOriginCallbackContext {
3020     explicit FetchCacheSizeForOriginCallbackContext(TestController& controller)
3021         : testController(controller)
3022     {
3023     }
3024
3025     TestController& testController;
3026
3027     bool done { false };
3028     uint64_t result { 0 };
3029 };
3030
3031 static void fetchCacheSizeForOriginCallback(uint64_t size, void* userData)
3032 {
3033     auto* context = static_cast<FetchCacheSizeForOriginCallbackContext*>(userData);
3034     context->done = true;
3035     context->result = size;
3036     context->testController.notifyDone();
3037 }
3038
3039 uint64_t TestController::domCacheSize(WKStringRef origin)
3040 {
3041     auto* dataStore = WKContextGetWebsiteDataStore(platformContext());
3042     FetchCacheSizeForOriginCallbackContext context(*this);
3043     WKWebsiteDataStoreGetFetchCacheSizeForOrigin(dataStore, origin, &context, fetchCacheSizeForOriginCallback);
3044     runUntil(context.done, noTimeout);
3045     return context.result;
3046 }
3047
3048 #if !PLATFORM(COCOA)
3049 void TestController::setAllowStorageQuotaIncrease(bool)
3050 {
3051     // FIXME: To implement.
3052 }
3053
3054 bool TestController::isDoingMediaCapture() const
3055 {
3056     return false;
3057 }
3058
3059 #endif
3060
3061 struct ResourceStatisticsCallbackContext {
3062     explicit ResourceStatisticsCallbackContext(TestController& controller)
3063         : testController(controller)
3064     {
3065     }
3066
3067     TestController& testController;
3068     bool done { false };
3069     bool result { false };
3070     WKRetainPtr<WKStringRef> resourceLoadStatisticsRepresentation;
3071 };
3072     
3073 static void resourceStatisticsStringResultCallback(WKStringRef resourceLoadStatisticsRepresentation, void* userData)
3074 {
3075     auto* context = static_cast<ResourceStatisticsCallbackContext*>(userData);
3076     context->resourceLoadStatisticsRepresentation = resourceLoadStatisticsRepresentation;
3077     context->done = true;
3078     context->testController.notifyDone();
3079 }
3080
3081 static void resourceStatisticsVoidResultCallback(void* userData)
3082 {
3083     auto* context = static_cast<ResourceStatisticsCallbackContext*>(userData);
3084     context->done = true;
3085     context->testController.notifyDone();
3086 }
3087
3088 static void resourceStatisticsBooleanResultCallback(bool result, void* userData)
3089 {
3090     auto* context = static_cast<ResourceStatisticsCallbackContext*>(userData);
3091     context->result = result;
3092     context->done = true;
3093     context->testController.notifyDone();
3094 }
3095
3096 void TestController::setStatisticsDebugMode(bool value)
3097 {
3098     auto* dataStore = WKContextGetWebsiteDataStore(platformContext());
3099     ResourceStatisticsCallbackContext context(*this);
3100     WKWebsiteDataStoreSetResourceLoadStatisticsDebugModeWithCompletionHandler(dataStore, value, &context, resourceStatisticsVoidResultCallback);
3101     runUntil(context.done, noTimeout);
3102     m_currentInvocation->didSetStatisticsDebugMode();
3103 }
3104
3105 void TestController::setStatisticsPrevalentResourceForDebugMode(WKStringRef hostName)
3106 {
3107     auto* dataStore = WKContextGetWebsiteDataStore(platformContext());
3108     ResourceStatisticsCallbackContext context(*this);
3109     WKWebsiteDataStoreSetResourceLoadStatisticsPrevalentResourceForDebugMode(dataStore, hostName, &context, resourceStatisticsVoidResultCallback);
3110     runUntil(context.done, noTimeout);
3111     m_currentInvocation->didSetPrevalentResourceForDebugMode();
3112 }
3113
3114 void TestController::setStatisticsLastSeen(WKStringRef host, double seconds)
3115 {
3116     auto* dataStore = WKContextGetWebsiteDataStore(platformContext());
3117     ResourceStatisticsCallbackContext context(*this);
3118     WKWebsiteDataStoreSetStatisticsLastSeen(dataStore, host, seconds, &context, resourceStatisticsVoidResultCallback);
3119     runUntil(context.done, noTimeout);
3120     m_currentInvocation->didSetLastSeen();
3121 }
3122
3123 void TestController::setStatisticsPrevalentResource(WKStringRef host, bool value)
3124 {
3125     auto* dataStore = WKContextGetWebsiteDataStore(platformContext());
3126     ResourceStatisticsCallbackContext context(*this);
3127     WKWebsiteDataStoreSetStatisticsPrevalentResource(dataStore, host, value, &context, resourceStatisticsVoidResultCallback);
3128     runUntil(context.done, noTimeout);
3129     m_currentInvocation->didSetPrevalentResource();
3130 }
3131
3132 void TestController::setStatisticsVeryPrevalentResource(WKStringRef host, bool value)
3133 {
3134     auto* dataStore = WKContextGetWebsiteDataStore(platformContext());
3135     ResourceStatisticsCallbackContext context(*this);
3136     WKWebsiteDataStoreSetStatisticsVeryPrevalentResource(dataStore, host, value, &context, resourceStatisticsVoidResultCallback);
3137     runUntil(context.done, noTimeout);
3138     m_currentInvocation->didSetVeryPrevalentResource();
3139 }
3140     
3141 String TestController::dumpResourceLoadStatistics()
3142 {
3143     auto* dataStore = WKContextGetWebsiteDataStore(platformContext());
3144     ResourceStatisticsCallbackContext context(*this);
3145     WKWebsiteDataStoreDumpResourceLoadStatistics(dataStore, &context, resourceStatisticsStringResultCallback);
3146     runUntil(context.done, noTimeout);
3147     return toWTFString(context.resourceLoadStatisticsRepresentation.get());
3148 }
3149
3150 bool TestController::isStatisticsPrevalentResource(WKStringRef host)
3151 {
3152     auto* dataStore = WKContextGetWebsiteDataStore(platformContext());
3153     ResourceStatisticsCallbackContext context(*this);
3154     WKWebsiteDataStoreIsStatisticsPrevalentResource(dataStore, host, &context, resourceStatisticsBooleanResultCallback);
3155     runUntil(context.done, noTimeout);
3156     return context.result;
3157 }
3158
3159 bool TestController::isStatisticsVeryPrevalentResource(WKStringRef host)
3160 {
3161     auto* dataStore = WKContextGetWebsiteDataStore(platformContext());
3162     ResourceStatisticsCallbackContext context(*this);
3163     WKWebsiteDataStoreIsStatisticsVeryPrevalentResource(dataStore, host, &context, resourceStatisticsBooleanResultCallback);
3164     runUntil(context.done, noTimeout);
3165     return context.result;
3166 }
3167
3168 bool TestController::isStatisticsRegisteredAsSubresourceUnder(WKStringRef subresourceHost, WKStringRef topFrameHost)
3169 {
3170     auto* dataStore = WKContextGetWebsiteDataStore(platformContext());
3171     ResourceStatisticsCallbackContext context(*this);
3172     WKWebsiteDataStoreIsStatisticsRegisteredAsSubresourceUnder(dataStore, subresourceHost, topFrameHost, &context, resourceStatisticsBooleanResultCallback);
3173     runUntil(context.done, noTimeout);
3174     return context.result;
3175 }
3176
3177 bool TestController::isStatisticsRegisteredAsSubFrameUnder(WKStringRef subFrameHost, WKStringRef topFrameHost)
3178 {
3179     auto* dataStore = WKContextGetWebsiteDataStore(platformContext());
3180     ResourceStatisticsCallbackContext context(*this);
3181     WKWebsiteDataStoreIsStatisticsRegisteredAsSubFrameUnder(dataStore, subFrameHost, topFrameHost, &context, resourceStatisticsBooleanResultCallback);
3182     runUntil(context.done, noTimeout);
3183     return context.result;
3184 }
3185
3186 bool TestController::isStatisticsRegisteredAsRedirectingTo(WKStringRef hostRedirectedFrom, WKStringRef hostRedirectedTo)
3187 {
3188     auto* dataStore = WKContextGetWebsiteDataStore(platformContext());
3189     ResourceStatisticsCallbackContext context(*this);
3190     WKWebsiteDataStoreIsStatisticsRegisteredAsRedirectingTo(dataStore, hostRedirectedFrom, hostRedirectedTo, &context, resourceStatisticsBooleanResultCallback);
3191     runUntil(context.done, noTimeout);
3192     return context.result;
3193 }
3194
3195 void TestController::setStatisticsHasHadUserInteraction(WKStringRef host, bool value)
3196 {
3197     auto* dataStore = WKContextGetWebsiteDataStore(platformContext());
3198     ResourceStatisticsCallbackContext context(*this);
3199     WKWebsiteDataStoreSetStatisticsHasHadUserInteraction(dataStore, host, value, &context, resourceStatisticsVoidResultCallback);
3200     runUntil(context.done, noTimeout);
3201     m_currentInvocation->didSetHasHadUserInteraction();
3202 }
3203
3204 bool TestController::isStatisticsHasHadUserInteraction(WKStringRef host)
3205 {
3206     auto* dataStore = WKContextGetWebsiteDataStore(platformContext());
3207     ResourceStatisticsCallbackContext context(*this);
3208     WKWebsiteDataStoreIsStatisticsHasHadUserInteraction(dataStore, host, &context, resourceStatisticsBooleanResultCallback);
3209     runUntil(context.done, noTimeout);
3210     return context.result;
3211 }
3212
3213 void TestController::setStatisticsGrandfathered(WKStringRef host, bool value)
3214 {
3215     auto* dataStore = WKContextGetWebsiteDataStore(platformContext());
3216     WKWebsiteDataStoreSetStatisticsGrandfathered(dataStore, host, value);
3217 }
3218
3219 bool TestController::isStatisticsGrandfathered(WKStringRef host)
3220 {
3221     auto* dataStore = WKContextGetWebsiteDataStore(platformContext());
3222     ResourceStatisticsCallbackContext context(*this);
3223     WKWebsiteDataStoreIsStatisticsGrandfathered(dataStore, host, &context, resourceStatisticsBooleanResultCallback);
3224     runUntil(context.done, noTimeout);
3225     return context.result;
3226 }
3227
3228 void TestController::setStatisticsSubframeUnderTopFrameOrigin(WKStringRef host, WKStringRef topFrameHost)
3229 {
3230     auto* dataStore = WKContextGetWebsiteDataStore(platformContext());
3231     WKWebsiteDataStoreSetStatisticsSubframeUnderTopFrameOrigin(dataStore, host, topFrameHost);
3232 }
3233
3234 void TestController::setStatisticsSubresourceUnderTopFrameOrigin(WKStringRef host, WKStringRef topFrameHost)
3235 {
3236     auto* dataStore = WKContextGetWebsiteDataStore(platformContext());
3237     WKWebsiteDataStoreSetStatisticsSubresourceUnderTopFrameOrigin(dataStore, host, topFrameHost);
3238 }
3239
3240 void TestController::setStatisticsSubresourceUniqueRedirectTo(WKStringRef host, WKStringRef hostRedirectedTo)
3241 {
3242     auto* dataStore = WKContextGetWebsiteDataStore(platformContext());
3243     WKWebsiteDataStoreSetStatisticsSubresourceUniqueRedirectTo(dataStore, host, hostRedirectedTo);
3244 }
3245
3246 void TestController::setStatisticsSubresourceUniqueRedirectFrom(WKStringRef host, WKStringRef hostRedirectedFrom)
3247 {
3248     auto* dataStore = WKContextGetWebsiteDataStore(platformContext());
3249     WKWebsiteDataStoreSetStatisticsSubresourceUniqueRedirectFrom(dataStore, host, hostRedirectedFrom);
3250 }
3251
3252 void TestController::setStatisticsTopFrameUniqueRedirectTo(WKStringRef host, WKStringRef hostRedirectedTo)
3253 {
3254     auto* dataStore = WKContextGetWebsiteDataStore(platformContext());
3255     WKWebsiteDataStoreSetStatisticsTopFrameUniqueRedirectTo(dataStore, host, hostRedirectedTo);
3256 }
3257
3258 void TestController::setStatisticsTopFrameUniqueRedirectFrom(WKStringRef host, WKStringRef hostRedirectedFrom)
3259 {
3260     auto* dataStore = WKContextGetWebsiteDataStore(platformContext());
3261     WKWebsiteDataStoreSetStatisticsTopFrameUniqueRedirectFrom(dataStore, host, hostRedirectedFrom);
3262 }
3263
3264 void TestController::setStatisticsCrossSiteLoadWithLinkDecoration(WKStringRef fromHost, WKStringRef toHost)
3265 {
3266     auto* dataStore = WKContextGetWebsiteDataStore(platformContext());
3267     ResourceStatisticsCallbackContext context(*this);
3268     WKWebsiteDataStoreSetStatisticsCrossSiteLoadWithLinkDecoration(dataStore, fromHost, toHost, &context, resourceStatisticsVoidResultCallback);
3269     runUntil(context.done, noTimeout);
3270 }
3271
3272 void TestController::setStatisticsTimeToLiveUserInteraction(double seconds)
3273 {
3274     auto* dataStore = WKContextGetWebsiteDataStore(platformContext());
3275     ResourceStatisticsCallbackContext context(*this);
3276     WKWebsiteDataStoreSetStatisticsTimeToLiveUserInteraction(dataStore, seconds, &context, resourceStatisticsVoidResultCallback);
3277     runUntil(context.done, noTimeout);
3278 }
3279
3280 void TestController::statisticsProcessStatisticsAndDataRecords()
3281 {
3282     auto* dataStore = WKContextGetWebsiteDataStore(platformContext());
3283     ResourceStatisticsCallbackContext context(*this);
3284     WKWebsiteDataStoreStatisticsProcessStatisticsAndDataRecords(dataStore, &context, resourceStatisticsVoidResultCallback);
3285     runUntil(context.done, noTimeout);
3286 }
3287
3288 void TestController::statisticsUpdateCookieBlocking()
3289 {
3290     auto* dataStore = WKContextGetWebsiteDataStore(platformContext());
3291     ResourceStatisticsCallbackContext context(*this);
3292     WKWebsiteDataStoreStatisticsUpdateCookieBlocking(dataStore, &context, resourceStatisticsVoidResultCallback);
3293     runUntil(context.done, noTimeout);
3294     m_currentInvocation->didSetBlockCookiesForHost();
3295 }
3296
3297 void TestController::statisticsSubmitTelemetry()
3298 {
3299     auto* dataStore = WKContextGetWebsiteDataStore(platformContext());
3300     WKWebsiteDataStoreStatisticsSubmitTelemetry(dataStore);
3301 }
3302
3303 void TestController::setStatisticsNotifyPagesWhenDataRecordsWereScanned(bool value)
3304 {
3305     auto* dataStore = WKContextGetWebsiteDataStore(platformContext());
3306     WKWebsiteDataStoreSetStatisticsNotifyPagesWhenDataRecordsWereScanned(dataStore, value);
3307 }
3308
3309 void TestController::setStatisticsIsRunningTest(bool value)
3310 {
3311     auto* dataStore = WKContextGetWebsiteDataStore(platformContext());
3312     ResourceStatisticsCallbackContext context(*this);
3313     WKWebsiteDataStoreSetStatisticsIsRunningTest(dataStore, value, &context, resourceStatisticsVoidResultCallback);
3314     runUntil(context.done, noTimeout);
3315 }
3316
3317 void TestController::setStatisticsShouldClassifyResourcesBeforeDataRecordsRemoval(bool value)
3318 {
3319     auto* dataStore = WKContextGetWebsiteDataStore(platformContext());
3320     WKWebsiteDataStoreSetStatisticsShouldClassifyResourcesBeforeDataRecordsRemoval(dataStore, value);
3321 }
3322
3323 void TestController::setStatisticsNotifyPagesWhenTelemetryWasCaptured(bool value)
3324 {
3325     auto* dataStore = WKContextGetWebsiteDataStore(platformContext());
3326     WKWebsiteDataStoreSetStatisticsNotifyPagesWhenTelemetryWasCaptured(dataStore, value);
3327 }
3328
3329 void TestController::setStatisticsMinimumTimeBetweenDataRecordsRemoval(double seconds)
3330 {
3331     auto* dataStore = WKContextGetWebsiteDataStore(platformContext());
3332     WKWebsiteDataStoreSetStatisticsMinimumTimeBetweenDataRecordsRemoval(dataStore, seconds);
3333 }
3334
3335 void TestController::setStatisticsGrandfatheringTime(double seconds)
3336 {
3337     auto* dataStore = WKContextGetWebsiteDataStore(platformContext());
3338     WKWebsiteDataStoreSetStatisticsGrandfatheringTime(dataStore, seconds);
3339 }
3340
3341 void TestController::setStatisticsMaxStatisticsEntries(unsigned entries)
3342 {
3343     auto* dataStore = WKContextGetWebsiteDataStore(platformContext());
3344     WKWebsiteDataStoreSetStatisticsMaxStatisticsEntries(dataStore, entries);
3345 }
3346
3347 void TestController::setStatisticsPruneEntriesDownTo(unsigned entries)
3348 {
3349     auto* dataStore = WKContextGetWebsiteDataStore(platformContext());
3350     WKWebsiteDataStoreSetStatisticsPruneEntriesDownTo(dataStore, entries);
3351 }
3352
3353 void TestController::statisticsClearInMemoryAndPersistentStore()
3354 {
3355     auto* dataStore = WKContextGetWebsiteDataStore(platformContext());
3356     ResourceStatisticsCallbackContext context(*this);
3357     WKWebsiteDataStoreStatisticsClearInMemoryAndPersistentStore(dataStore, &context, resourceStatisticsVoidResultCallback);
3358     runUntil(context.done, noTimeout);
3359     m_currentInvocation->didClearStatisticsThroughWebsiteDataRemoval();
3360 }
3361
3362 void TestController::statisticsClearInMemoryAndPersistentStoreModifiedSinceHours(unsigned hours)
3363 {
3364     auto* dataStore = WKContextGetWebsiteDataStore(platformContext());
3365     ResourceStatisticsCallbackContext context(*this);
3366     WKWebsiteDataStoreStatisticsClearInMemoryAndPersistentStoreModifiedSinceHours(dataStore, hours, &context, resourceStatisticsVoidResultCallback);
3367     runUntil(context.done, noTimeout);
3368     m_currentInvocation->didClearStatisticsThroughWebsiteDataRemoval();
3369 }
3370
3371 void TestController::statisticsClearThroughWebsiteDataRemoval()
3372 {
3373     auto* dataStore = WKContextGetWebsiteDataStore(platformContext());
3374     ResourceStatisticsCallbackContext context(*this);
3375     WKWebsiteDataStoreStatisticsClearThroughWebsiteDataRemoval(dataStore, &context, resourceStatisticsVoidResultCallback);
3376     runUntil(context.done, noTimeout);
3377     m_currentInvocation->didClearStatisticsThroughWebsiteDataRemoval();
3378 }
3379
3380 void TestController::statisticsDeleteCookiesForHost(WKStringRef host, bool includeHttpOnlyCookies)
3381 {
3382     auto* dataStore = WKContextGetWebsiteDataStore(platformContext());
3383     ResourceStatisticsCallbackContext context(*this);
3384     WKWebsiteDataStoreStatisticsDeleteCookiesForTesting(dataStore, host, includeHttpOnlyCookies, &context, resourceStatisticsVoidResultCallback);
3385     runUntil(context.done, noTimeout);
3386 }
3387
3388 void TestController::setStatisticsCacheMaxAgeCap(double seconds)
3389 {
3390     auto* dataStore = WKContextGetWebsiteDataStore(platformContext());
3391     ResourceStatisticsCallbackContext context(*this);
3392     WKWebsiteDataStoreSetStatisticsCacheMaxAgeCap(dataStore, seconds, &context, resourceStatisticsVoidResultCallback);
3393     runUntil(context.done, noTimeout);
3394 }
3395
3396 void TestController::statisticsResetToConsistentState()
3397 {
3398     auto* dataStore = WKContextGetWebsiteDataStore(platformContext());
3399     ResourceStatisticsCallbackContext context(*this);
3400     WKWebsiteDataStoreStatisticsResetToConsistentState(dataStore, &context, resourceStatisticsVoidResultCallback);
3401     runUntil(context.done, noTimeout);
3402     m_currentInvocation->didResetStatisticsToConsistentState();
3403 }
3404
3405 void TestController::addMockMediaDevice(WKStringRef persistentID, WKStringRef label, WKStringRef type)
3406 {
3407     WKAddMockMediaDevice(platformContext(), persistentID, label, type);
3408 }
3409
3410 void TestController::clearMockMediaDevices()
3411 {
3412     WKClearMockMediaDevices(platformContext());
3413 }
3414
3415 void TestController::removeMockMediaDevice(WKStringRef persistentID)
3416 {
3417     WKRemoveMockMediaDevice(platformContext(), persistentID);
3418 }
3419
3420 void TestController::resetMockMediaDevices()
3421 {
3422     WKResetMockMediaDevices(platformContext());
3423 }
3424
3425 #if !PLATFORM(COCOA)
3426 void TestController::platformAddTestOptions(TestOptions&) const
3427 {
3428 }
3429
3430 void TestController::injectUserScript(WKStringRef)
3431 {
3432 }
3433
3434 void TestController::addTestKeyToKeychain(const String&, const String&, const String&)
3435 {
3436 }
3437
3438 void TestController::cleanUpKeychain(const String&)
3439 {
3440 }
3441
3442 bool TestController::keyExistsInKeychain(const String&, const String&)
3443 {
3444     return false;
3445 }
3446
3447 bool TestController::canDoServerTrustEvaluationInNetworkProcess() const
3448 {
3449     return false;
3450 }
3451
3452 #endif
3453
3454 void TestController::sendDisplayConfigurationChangedMessageForTesting()
3455 {
3456     WKSendDisplayConfigurationChangedMessageForTesting(platformContext());
3457 }
3458
3459 void TestController::setWebAuthenticationMockConfiguration(WKDictionaryRef configuration)
3460 {
3461     WKWebsiteDataStoreSetWebAuthenticationMockConfiguration(WKContextGetWebsiteDataStore(platformContext()), configuration);
3462 }
3463
3464 struct AdClickAttributionStringResultCallbackContext {
3465     explicit AdClickAttributionStringResultCallbackContext(TestController& controller)
3466         : testController(controller)
3467     {
3468     }
3469     
3470     TestController& testController;
3471     bool done { false };
3472     WKRetainPtr<WKStringRef> adClickAttributionRepresentation;
3473 };
3474
3475 static void adClickAttributionStringResultCallback(WKStringRef adClickAttributionRepresentation, void* userData)
3476 {
3477     auto* context = static_cast<AdClickAttributionStringResultCallbackContext*>(userData);
3478     context->adClickAttributionRepresentation = adClickAttributionRepresentation;
3479     context->done = true;
3480     context->testController.notifyDone();
3481 }
3482
3483 String TestController::dumpAdClickAttribution()
3484 {
3485     AdClickAttributionStringResultCallbackContext callbackContext(*this);
3486     WKPageDumpAdClickAttribution(m_mainWebView->page(), adClickAttributionStringResultCallback, &callbackContext);
3487     runUntil(callbackContext.done, noTimeout);
3488     return toWTFString(callbackContext.adClickAttributionRepresentation.get());
3489 }
3490
3491 struct AdClickAttributionVoidCallbackContext {
3492     explicit AdClickAttributionVoidCallbackContext(TestController& controller)
3493         : testController(controller)
3494     {
3495     }
3496     
3497     TestController& testController;
3498     bool done { false };
3499 };
3500
3501 static void adClickAttributionVoidCallback(void* userData)
3502 {
3503     auto* context = static_cast<AdClickAttributionVoidCallbackContext*>(userData);
3504     context->done = true;
3505     context->testController.notifyDone();
3506 }
3507
3508 void TestController::clearAdClickAttribution()
3509 {
3510     AdClickAttributionVoidCallbackContext callbackContext(*this);
3511     WKPageClearAdClickAttribution(m_mainWebView->page(), adClickAttributionVoidCallback, &callbackContext);
3512     runUntil(callbackContext.done, noTimeout);
3513 }
3514
3515 } // namespace WTR