Add basic layout test coverage for File Picker on iOS
[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         options.shouldHandleRunOpenPanel ? runOpenPanel : 0,
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 == "shouldHandleRunOpenPanel")
1320             testOptions.shouldHandleRunOpenPanel = parseBooleanTestHeaderValue(value);
1321         else if (key == "contentInset.top")
1322             testOptions.contentInsetTop = std::stod(value);
1323         else if (key == "ignoreSynchronousMessagingTimeouts")
1324             testOptions.contextOptions.ignoreSynchronousMessagingTimeouts = parseBooleanTestHeaderValue(value);
1325         pairStart = pairEnd + 1;
1326     }
1327 }
1328
1329 TestOptions TestController::testOptionsForTest(const TestCommand& command) const
1330 {
1331     TestOptions options(command.pathOrURL);
1332
1333     options.useRemoteLayerTree = m_shouldUseRemoteLayerTree;
1334     options.shouldShowWebView = m_shouldShowWebView;
1335
1336     updatePlatformSpecificTestOptionsForTest(options, command.pathOrURL);
1337     updateTestOptionsFromTestHeader(options, command.pathOrURL, command.absolutePath);
1338     platformAddTestOptions(options);
1339
1340     return options;
1341 }
1342
1343 void TestController::updateWebViewSizeForTest(const TestInvocation& test)
1344 {
1345     unsigned width = viewWidth;
1346     unsigned height = viewHeight;
1347     if (test.options().isSVGTest) {
1348         width = w3cSVGViewWidth;
1349         height = w3cSVGViewHeight;
1350     }
1351
1352     mainWebView()->resizeTo(width, height);
1353 }
1354
1355 void TestController::updateWindowScaleForTest(PlatformWebView* view, const TestInvocation& test)
1356 {
1357     view->changeWindowScaleIfNeeded(test.options().deviceScaleFactor);
1358 }
1359
1360 void TestController::configureViewForTest(const TestInvocation& test)
1361 {
1362     ensureViewSupportsOptionsForTest(test);
1363     updateWebViewSizeForTest(test);
1364     updateWindowScaleForTest(mainWebView(), test);
1365     configureContentExtensionForTest(test);
1366     platformConfigureViewForTest(test);
1367 }
1368
1369 #if ENABLE(CONTENT_EXTENSIONS) && !PLATFORM(COCOA)
1370 struct ContentExtensionStoreCallbackContext {
1371     explicit ContentExtensionStoreCallbackContext(TestController& controller)
1372         : testController(controller)
1373     {
1374     }
1375
1376     TestController& testController;
1377     uint32_t status { kWKUserContentExtensionStoreSuccess };
1378     WKRetainPtr<WKUserContentFilterRef> filter;
1379     bool done { false };
1380 };
1381
1382 static void contentExtensionStoreCallback(WKUserContentFilterRef filter, uint32_t status, void* userData)
1383 {
1384     auto* context = static_cast<ContentExtensionStoreCallbackContext*>(userData);
1385     context->status = status;
1386     context->filter = filter ? adoptWK(filter) : nullptr;
1387     context->done = true;
1388     context->testController.notifyDone();
1389 }
1390
1391 static std::string contentExtensionJSONPath(WKURLRef url)
1392 {
1393     auto path = testPath(url);
1394     if (path.length())
1395         return path + ".json";
1396
1397     auto p = adoptWK(WKURLCopyPath(url));
1398     auto buffer = std::vector<char>(WKStringGetMaximumUTF8CStringSize(p.get()));
1399     const auto length = WKStringGetUTF8CString(p.get(), buffer.data(), buffer.size());
1400     return std::string("LayoutTests/http/tests") + std::string(buffer.data(), length - 1) + ".json";
1401 }
1402 #endif
1403
1404 #if !PLATFORM(COCOA)
1405 #if ENABLE(CONTENT_EXTENSIONS)
1406 void TestController::configureContentExtensionForTest(const TestInvocation& test)
1407 {
1408     const char* contentExtensionsPath = libraryPathForTesting();
1409     if (!contentExtensionsPath)
1410         contentExtensionsPath = "/tmp/wktr-contentextensions";
1411
1412     if (!test.urlContains("contentextensions/")) {
1413         WKPageSetUserContentExtensionsEnabled(m_mainWebView->page(), false);
1414         return;
1415     }
1416
1417     std::string jsonFilePath(contentExtensionJSONPath(test.url()));
1418     std::ifstream jsonFile(jsonFilePath);
1419     if (!jsonFile.good()) {
1420         WTFLogAlways("Could not open file '%s'", jsonFilePath.c_str());
1421         return;
1422     }
1423
1424     std::string jsonFileContents {std::istreambuf_iterator<char>(jsonFile), std::istreambuf_iterator<char>()};
1425     auto jsonSource = adoptWK(WKStringCreateWithUTF8CString(jsonFileContents.c_str()));
1426
1427     auto storePath = adoptWK(WKStringCreateWithUTF8CString(contentExtensionsPath));
1428     auto extensionStore = adoptWK(WKUserContentExtensionStoreCreate(storePath.get()));
1429     ASSERT(extensionStore);
1430
1431     auto filterIdentifier = adoptWK(WKStringCreateWithUTF8CString("TestContentExtension"));
1432
1433     ContentExtensionStoreCallbackContext context(*this);
1434     WKUserContentExtensionStoreCompile(extensionStore.get(), filterIdentifier.get(), jsonSource.get(), &context, contentExtensionStoreCallback);
1435     runUntil(context.done, noTimeout);
1436     ASSERT(context.status == kWKUserContentExtensionStoreSuccess);
1437     ASSERT(context.filter);
1438
1439     WKPageSetUserContentExtensionsEnabled(mainWebView()->page(), true);
1440     WKUserContentControllerAddUserContentFilter(userContentController(), context.filter.get());
1441 }
1442
1443 void TestController::resetContentExtensions()
1444 {
1445     if (!mainWebView())
1446         return;
1447
1448     WKPageSetUserContentExtensionsEnabled(mainWebView()->page(), false);
1449
1450     const char* contentExtensionsPath = libraryPathForTesting();
1451     if (!contentExtensionsPath)
1452         return;
1453
1454     WKUserContentControllerRemoveAllUserContentFilters(userContentController());
1455
1456     auto storePath = adoptWK(WKStringCreateWithUTF8CString(contentExtensionsPath));
1457     auto extensionStore = adoptWK(WKUserContentExtensionStoreCreate(storePath.get()));
1458     ASSERT(extensionStore);
1459
1460     auto filterIdentifier = adoptWK(WKStringCreateWithUTF8CString("TestContentExtension"));
1461
1462     ContentExtensionStoreCallbackContext context(*this);
1463     WKUserContentExtensionStoreRemove(extensionStore.get(), filterIdentifier.get(), &context, contentExtensionStoreCallback);
1464     runUntil(context.done, noTimeout);
1465     ASSERT(!context.filter);
1466 }
1467 #else // ENABLE(CONTENT_EXTENSIONS)
1468 void TestController::configureContentExtensionForTest(const TestInvocation&)
1469 {
1470 }
1471
1472 void TestController::resetContentExtensions()
1473 {
1474 }
1475 #endif // ENABLE(CONTENT_EXTENSIONS)
1476 #endif // !PLATFORM(COCOA)
1477
1478 class CommandTokenizer {
1479 public:
1480     explicit CommandTokenizer(const std::string& input)
1481         : m_input(input)
1482         , m_posNextSeparator(0)
1483     {
1484         pump();
1485     }
1486
1487     bool hasNext() const;
1488     std::string next();
1489
1490 private:
1491     void pump();
1492     static const char kSeparator = '\'';
1493     const std::string& m_input;
1494     std::string m_next;
1495     size_t m_posNextSeparator;
1496 };
1497
1498 void CommandTokenizer::pump()
1499 {
1500     if (m_posNextSeparator == std::string::npos || m_posNextSeparator == m_input.size()) {
1501         m_next = std::string();
1502         return;
1503     }
1504     size_t start = m_posNextSeparator ? m_posNextSeparator + 1 : 0;
1505     m_posNextSeparator = m_input.find(kSeparator, start);
1506     size_t size = m_posNextSeparator == std::string::npos ? std::string::npos : m_posNextSeparator - start;
1507     m_next = std::string(m_input, start, size);
1508 }
1509
1510 std::string CommandTokenizer::next()
1511 {
1512     ASSERT(hasNext());
1513
1514     std::string oldNext = m_next;
1515     pump();
1516     return oldNext;
1517 }
1518
1519 bool CommandTokenizer::hasNext() const
1520 {
1521     return !m_next.empty();
1522 }
1523
1524 NO_RETURN static void die(const std::string& inputLine)
1525 {
1526     fprintf(stderr, "Unexpected input line: %s\n", inputLine.c_str());
1527     exit(1);
1528 }
1529
1530 static TestCommand parseInputLine(const std::string& inputLine)
1531 {
1532     TestCommand result;
1533     CommandTokenizer tokenizer(inputLine);
1534     if (!tokenizer.hasNext())
1535         die(inputLine);
1536
1537     std::string arg = tokenizer.next();
1538     result.pathOrURL = arg;
1539     while (tokenizer.hasNext()) {
1540         arg = tokenizer.next();
1541         if (arg == std::string("--timeout")) {
1542             std::string timeoutToken = tokenizer.next();
1543             result.timeout = Seconds::fromMilliseconds(atoi(timeoutToken.c_str()));
1544         } else if (arg == std::string("-p") || arg == std::string("--pixel-test")) {
1545             result.shouldDumpPixels = true;
1546             if (tokenizer.hasNext())
1547                 result.expectedPixelHash = tokenizer.next();
1548         } else if (arg == std::string("--dump-jsconsolelog-in-stderr"))
1549             result.dumpJSConsoleLogInStdErr = true;
1550         else if (arg == std::string("--absolutePath"))
1551             result.absolutePath = tokenizer.next();
1552         else
1553             die(inputLine);
1554     }
1555     return result;
1556 }
1557
1558 bool TestController::runTest(const char* inputLine)
1559 {
1560     AutodrainedPool pool;
1561     
1562     WKTextCheckerSetTestingMode(true);
1563     TestCommand command = parseInputLine(std::string(inputLine));
1564
1565     m_state = RunningTest;
1566     
1567     TestOptions options = testOptionsForTest(command);
1568
1569     WKRetainPtr<WKURLRef> wkURL(AdoptWK, createTestURL(command.pathOrURL.c_str()));
1570     m_currentInvocation = std::make_unique<TestInvocation>(wkURL.get(), options);
1571
1572     if (command.shouldDumpPixels || m_shouldDumpPixelsForAllTests)
1573         m_currentInvocation->setIsPixelTest(command.expectedPixelHash);
1574
1575     if (command.timeout > 0_s)
1576         m_currentInvocation->setCustomTimeout(command.timeout);
1577
1578     m_currentInvocation->setDumpJSConsoleLogInStdErr(command.dumpJSConsoleLogInStdErr || options.dumpJSConsoleLogInStdErr);
1579
1580     platformWillRunTest(*m_currentInvocation);
1581
1582     m_currentInvocation->invoke();
1583     m_currentInvocation = nullptr;
1584
1585     return true;
1586 }
1587
1588 bool TestController::waitForCompletion(const WTF::Function<void ()>& function, WTF::Seconds timeout)
1589 {
1590     m_doneResetting = false;
1591     function();
1592     runUntil(m_doneResetting, timeout);
1593     return !m_doneResetting;
1594 }
1595
1596 bool TestController::handleControlCommand(const char* command)
1597 {
1598     if (!strcmp("#CHECK FOR WORLD LEAKS", command)) {
1599         if (!m_checkForWorldLeaks) {
1600             WTFLogAlways("WebKitTestRunner asked to check for world leaks, but was not run with --world-leaks");
1601             return true;
1602         }
1603         findAndDumpWorldLeaks();
1604         return true;
1605     }
1606     return false;
1607 }
1608
1609 void TestController::runTestingServerLoop()
1610 {
1611     char filenameBuffer[2048];
1612     while (fgets(filenameBuffer, sizeof(filenameBuffer), stdin)) {
1613         char* newLineCharacter = strchr(filenameBuffer, '\n');
1614         if (newLineCharacter)
1615             *newLineCharacter = '\0';
1616
1617         if (strlen(filenameBuffer) == 0)
1618             continue;
1619
1620         if (handleControlCommand(filenameBuffer))
1621             continue;
1622
1623         if (!runTest(filenameBuffer))
1624             break;
1625     }
1626 }
1627
1628 void TestController::run()
1629 {
1630     if (m_usingServerMode)
1631         runTestingServerLoop();
1632     else {
1633         for (size_t i = 0; i < m_paths.size(); ++i) {
1634             if (!runTest(m_paths[i].c_str()))
1635                 break;
1636         }
1637         if (m_checkForWorldLeaks)
1638             findAndDumpWorldLeaks();
1639     }
1640 }
1641
1642 void TestController::runUntil(bool& done, WTF::Seconds timeout)
1643 {
1644     if (m_forceNoTimeout)
1645         timeout = noTimeout;
1646
1647     platformRunUntil(done, timeout);
1648 }
1649
1650 // WKContextInjectedBundleClient
1651
1652 void TestController::didReceiveMessageFromInjectedBundle(WKContextRef context, WKStringRef messageName, WKTypeRef messageBody, const void* clientInfo)
1653 {
1654     static_cast<TestController*>(const_cast<void*>(clientInfo))->didReceiveMessageFromInjectedBundle(messageName, messageBody);
1655 }
1656
1657 void TestController::didReceiveSynchronousMessageFromInjectedBundle(WKContextRef context, WKStringRef messageName, WKTypeRef messageBody, WKTypeRef* returnData, const void* clientInfo)
1658 {
1659     *returnData = static_cast<TestController*>(const_cast<void*>(clientInfo))->didReceiveSynchronousMessageFromInjectedBundle(messageName, messageBody).leakRef();
1660 }
1661
1662 WKTypeRef TestController::getInjectedBundleInitializationUserData(WKContextRef, const void* clientInfo)
1663 {
1664     return static_cast<TestController*>(const_cast<void*>(clientInfo))->getInjectedBundleInitializationUserData().leakRef();
1665 }
1666
1667 // WKPageInjectedBundleClient
1668
1669 void TestController::didReceivePageMessageFromInjectedBundle(WKPageRef page, WKStringRef messageName, WKTypeRef messageBody, const void* clientInfo)
1670 {
1671     static_cast<TestController*>(const_cast<void*>(clientInfo))->didReceiveMessageFromInjectedBundle(messageName, messageBody);
1672 }
1673
1674 void TestController::didReceiveSynchronousPageMessageFromInjectedBundle(WKPageRef page, WKStringRef messageName, WKTypeRef messageBody, WKTypeRef* returnData, const void* clientInfo)
1675 {
1676     *returnData = static_cast<TestController*>(const_cast<void*>(clientInfo))->didReceiveSynchronousMessageFromInjectedBundle(messageName, messageBody).leakRef();
1677 }
1678
1679 void TestController::networkProcessDidCrash(WKContextRef context, const void *clientInfo)
1680 {
1681     static_cast<TestController*>(const_cast<void*>(clientInfo))->networkProcessDidCrash();
1682 }
1683
1684 void TestController::didReceiveKeyDownMessageFromInjectedBundle(WKDictionaryRef messageBodyDictionary, bool synchronous)
1685 {
1686     WKRetainPtr<WKStringRef> keyKey = adoptWK(WKStringCreateWithUTF8CString("Key"));
1687     WKStringRef key = static_cast<WKStringRef>(WKDictionaryGetItemForKey(messageBodyDictionary, keyKey.get()));
1688
1689     WKRetainPtr<WKStringRef> modifiersKey = adoptWK(WKStringCreateWithUTF8CString("Modifiers"));
1690     WKEventModifiers modifiers = static_cast<WKEventModifiers>(WKUInt64GetValue(static_cast<WKUInt64Ref>(WKDictionaryGetItemForKey(messageBodyDictionary, modifiersKey.get()))));
1691
1692     WKRetainPtr<WKStringRef> locationKey = adoptWK(WKStringCreateWithUTF8CString("Location"));
1693     unsigned location = static_cast<unsigned>(WKUInt64GetValue(static_cast<WKUInt64Ref>(WKDictionaryGetItemForKey(messageBodyDictionary, locationKey.get()))));
1694
1695     m_eventSenderProxy->keyDown(key, modifiers, location);
1696 }
1697
1698 void TestController::didReceiveLiveDocumentsList(WKArrayRef liveDocumentList)
1699 {
1700     auto numDocuments = WKArrayGetSize(liveDocumentList);
1701
1702     HashMap<uint64_t, String> documentInfo;
1703     for (size_t i = 0; i < numDocuments; ++i) {
1704         WKTypeRef item = WKArrayGetItemAtIndex(liveDocumentList, i);
1705         if (item && WKGetTypeID(item) == WKDictionaryGetTypeID()) {
1706             WKDictionaryRef liveDocumentItem = static_cast<WKDictionaryRef>(item);
1707
1708             WKRetainPtr<WKStringRef> idKey(AdoptWK, WKStringCreateWithUTF8CString("id"));
1709             WKUInt64Ref documentID = static_cast<WKUInt64Ref>(WKDictionaryGetItemForKey(liveDocumentItem, idKey.get()));
1710
1711             WKRetainPtr<WKStringRef> urlKey(AdoptWK, WKStringCreateWithUTF8CString("url"));
1712             WKStringRef documentURL = static_cast<WKStringRef>(WKDictionaryGetItemForKey(liveDocumentItem, urlKey.get()));
1713
1714             documentInfo.add(WKUInt64GetValue(documentID), toWTFString(documentURL));
1715         }
1716     }
1717
1718     if (!documentInfo.size()) {
1719         m_abandonedDocumentInfo.clear();
1720         return;
1721     }
1722
1723     // Remove any documents which are no longer live.
1724     m_abandonedDocumentInfo.removeIf([&](auto& keyAndValue) {
1725         return !documentInfo.contains(keyAndValue.key);
1726     });
1727     
1728     // Add newly abandoned documents.
1729     String currentTestURL = m_currentInvocation ? toWTFString(adoptWK(WKURLCopyString(m_currentInvocation->url()))) : "no test";
1730     for (const auto& it : documentInfo)
1731         m_abandonedDocumentInfo.add(it.key, AbandonedDocumentInfo(currentTestURL, it.value));
1732 }
1733
1734 void TestController::didReceiveMessageFromInjectedBundle(WKStringRef messageName, WKTypeRef messageBody)
1735 {
1736     if (WKStringIsEqualToUTF8CString(messageName, "LiveDocuments")) {
1737         ASSERT(WKGetTypeID(messageBody) == WKArrayGetTypeID());
1738         didReceiveLiveDocumentsList(static_cast<WKArrayRef>(messageBody));
1739         AsyncTask::currentTask()->taskComplete();
1740         return;
1741     }
1742
1743     if (WKStringIsEqualToUTF8CString(messageName, "EventSender")) {
1744         if (m_state != RunningTest)
1745             return;
1746
1747         ASSERT(WKGetTypeID(messageBody) == WKDictionaryGetTypeID());
1748         WKDictionaryRef messageBodyDictionary = static_cast<WKDictionaryRef>(messageBody);
1749
1750         WKRetainPtr<WKStringRef> subMessageKey(AdoptWK, WKStringCreateWithUTF8CString("SubMessage"));
1751         WKStringRef subMessageName = static_cast<WKStringRef>(WKDictionaryGetItemForKey(messageBodyDictionary, subMessageKey.get()));
1752
1753         if (WKStringIsEqualToUTF8CString(subMessageName, "MouseDown") || WKStringIsEqualToUTF8CString(subMessageName, "MouseUp")) {
1754             WKRetainPtr<WKStringRef> buttonKey = adoptWK(WKStringCreateWithUTF8CString("Button"));
1755             unsigned button = static_cast<unsigned>(WKUInt64GetValue(static_cast<WKUInt64Ref>(WKDictionaryGetItemForKey(messageBodyDictionary, buttonKey.get()))));
1756
1757             WKRetainPtr<WKStringRef> modifiersKey = adoptWK(WKStringCreateWithUTF8CString("Modifiers"));
1758             WKEventModifiers modifiers = static_cast<WKEventModifiers>(WKUInt64GetValue(static_cast<WKUInt64Ref>(WKDictionaryGetItemForKey(messageBodyDictionary, modifiersKey.get()))));
1759
1760             // Forward to WebProcess
1761             if (WKStringIsEqualToUTF8CString(subMessageName, "MouseDown"))
1762                 m_eventSenderProxy->mouseDown(button, modifiers);
1763             else
1764                 m_eventSenderProxy->mouseUp(button, modifiers);
1765
1766             return;
1767         }
1768
1769         if (WKStringIsEqualToUTF8CString(subMessageName, "KeyDown")) {
1770             didReceiveKeyDownMessageFromInjectedBundle(messageBodyDictionary, false);
1771             return;
1772         }
1773
1774         if (WKStringIsEqualToUTF8CString(subMessageName, "MouseScrollBy")) {
1775             WKRetainPtr<WKStringRef> xKey = adoptWK(WKStringCreateWithUTF8CString("X"));
1776             double x = WKDoubleGetValue(static_cast<WKDoubleRef>(WKDictionaryGetItemForKey(messageBodyDictionary, xKey.get())));
1777
1778             WKRetainPtr<WKStringRef> yKey = adoptWK(WKStringCreateWithUTF8CString("Y"));
1779             double y = WKDoubleGetValue(static_cast<WKDoubleRef>(WKDictionaryGetItemForKey(messageBodyDictionary, yKey.get())));
1780
1781             // Forward to WebProcess
1782             m_eventSenderProxy->mouseScrollBy(x, y);
1783             return;
1784         }
1785
1786         if (WKStringIsEqualToUTF8CString(subMessageName, "MouseScrollByWithWheelAndMomentumPhases")) {
1787             WKRetainPtr<WKStringRef> xKey = adoptWK(WKStringCreateWithUTF8CString("X"));
1788             double x = WKDoubleGetValue(static_cast<WKDoubleRef>(WKDictionaryGetItemForKey(messageBodyDictionary, xKey.get())));
1789             
1790             WKRetainPtr<WKStringRef> yKey = adoptWK(WKStringCreateWithUTF8CString("Y"));
1791             double y = WKDoubleGetValue(static_cast<WKDoubleRef>(WKDictionaryGetItemForKey(messageBodyDictionary, yKey.get())));
1792             
1793             WKRetainPtr<WKStringRef> phaseKey = adoptWK(WKStringCreateWithUTF8CString("Phase"));
1794             int phase = static_cast<int>(WKUInt64GetValue(static_cast<WKUInt64Ref>(WKDictionaryGetItemForKey(messageBodyDictionary, phaseKey.get()))));
1795             WKRetainPtr<WKStringRef> momentumKey = adoptWK(WKStringCreateWithUTF8CString("Momentum"));
1796             int momentum = static_cast<int>(WKUInt64GetValue(static_cast<WKUInt64Ref>(WKDictionaryGetItemForKey(messageBodyDictionary, momentumKey.get()))));
1797             
1798             // Forward to WebProcess
1799             m_eventSenderProxy->mouseScrollByWithWheelAndMomentumPhases(x, y, phase, momentum);
1800
1801             return;
1802         }
1803
1804         ASSERT_NOT_REACHED();
1805     }
1806
1807     if (!m_currentInvocation)
1808         return;
1809
1810     m_currentInvocation->didReceiveMessageFromInjectedBundle(messageName, messageBody);
1811 }
1812
1813 WKRetainPtr<WKTypeRef> TestController::didReceiveSynchronousMessageFromInjectedBundle(WKStringRef messageName, WKTypeRef messageBody)
1814 {
1815     if (WKStringIsEqualToUTF8CString(messageName, "EventSender")) {
1816         if (m_state != RunningTest)
1817             return nullptr;
1818
1819         ASSERT(WKGetTypeID(messageBody) == WKDictionaryGetTypeID());
1820         WKDictionaryRef messageBodyDictionary = static_cast<WKDictionaryRef>(messageBody);
1821
1822         WKRetainPtr<WKStringRef> subMessageKey(AdoptWK, WKStringCreateWithUTF8CString("SubMessage"));
1823         WKStringRef subMessageName = static_cast<WKStringRef>(WKDictionaryGetItemForKey(messageBodyDictionary, subMessageKey.get()));
1824
1825         if (WKStringIsEqualToUTF8CString(subMessageName, "KeyDown")) {
1826             didReceiveKeyDownMessageFromInjectedBundle(messageBodyDictionary, true);
1827
1828             return 0;
1829         }
1830
1831         if (WKStringIsEqualToUTF8CString(subMessageName, "MouseDown") || WKStringIsEqualToUTF8CString(subMessageName, "MouseUp")) {
1832             WKRetainPtr<WKStringRef> buttonKey = adoptWK(WKStringCreateWithUTF8CString("Button"));
1833             unsigned button = static_cast<unsigned>(WKUInt64GetValue(static_cast<WKUInt64Ref>(WKDictionaryGetItemForKey(messageBodyDictionary, buttonKey.get()))));
1834
1835             WKRetainPtr<WKStringRef> modifiersKey = adoptWK(WKStringCreateWithUTF8CString("Modifiers"));
1836             WKEventModifiers modifiers = static_cast<WKEventModifiers>(WKUInt64GetValue(static_cast<WKUInt64Ref>(WKDictionaryGetItemForKey(messageBodyDictionary, modifiersKey.get()))));
1837
1838             // Forward to WebProcess
1839             if (WKStringIsEqualToUTF8CString(subMessageName, "MouseDown"))
1840                 m_eventSenderProxy->mouseDown(button, modifiers);
1841             else
1842                 m_eventSenderProxy->mouseUp(button, modifiers);
1843             return 0;
1844         }
1845
1846         if (WKStringIsEqualToUTF8CString(subMessageName, "MouseMoveTo")) {
1847             WKRetainPtr<WKStringRef> xKey = adoptWK(WKStringCreateWithUTF8CString("X"));
1848             double x = WKDoubleGetValue(static_cast<WKDoubleRef>(WKDictionaryGetItemForKey(messageBodyDictionary, xKey.get())));
1849
1850             WKRetainPtr<WKStringRef> yKey = adoptWK(WKStringCreateWithUTF8CString("Y"));
1851             double y = WKDoubleGetValue(static_cast<WKDoubleRef>(WKDictionaryGetItemForKey(messageBodyDictionary, yKey.get())));
1852
1853             // Forward to WebProcess
1854             m_eventSenderProxy->mouseMoveTo(x, y);
1855             return 0;
1856         }
1857
1858 #if PLATFORM(MAC)
1859         if (WKStringIsEqualToUTF8CString(subMessageName, "MouseForceClick")) {
1860             m_eventSenderProxy->mouseForceClick();
1861             return 0;
1862         }
1863
1864         if (WKStringIsEqualToUTF8CString(subMessageName, "StartAndCancelMouseForceClick")) {
1865             m_eventSenderProxy->startAndCancelMouseForceClick();
1866             return 0;
1867         }
1868
1869         if (WKStringIsEqualToUTF8CString(subMessageName, "MouseForceDown")) {
1870             m_eventSenderProxy->mouseForceDown();
1871             return 0;
1872         }
1873
1874         if (WKStringIsEqualToUTF8CString(subMessageName, "MouseForceUp")) {
1875             m_eventSenderProxy->mouseForceUp();
1876             return 0;
1877         }
1878
1879         if (WKStringIsEqualToUTF8CString(subMessageName, "MouseForceChanged")) {
1880             WKRetainPtr<WKStringRef> forceKey = adoptWK(WKStringCreateWithUTF8CString("Force"));
1881             double force = WKDoubleGetValue(static_cast<WKDoubleRef>(WKDictionaryGetItemForKey(messageBodyDictionary, forceKey.get())));
1882
1883             m_eventSenderProxy->mouseForceChanged(force);
1884             return 0;
1885         }
1886 #endif // PLATFORM(MAC)
1887
1888         if (WKStringIsEqualToUTF8CString(subMessageName, "ContinuousMouseScrollBy")) {
1889             WKRetainPtr<WKStringRef> xKey = adoptWK(WKStringCreateWithUTF8CString("X"));
1890             double x = WKDoubleGetValue(static_cast<WKDoubleRef>(WKDictionaryGetItemForKey(messageBodyDictionary, xKey.get())));
1891
1892             WKRetainPtr<WKStringRef> yKey = adoptWK(WKStringCreateWithUTF8CString("Y"));
1893             double y = WKDoubleGetValue(static_cast<WKDoubleRef>(WKDictionaryGetItemForKey(messageBodyDictionary, yKey.get())));
1894
1895             WKRetainPtr<WKStringRef> pagedKey = adoptWK(WKStringCreateWithUTF8CString("Paged"));
1896             bool paged = static_cast<bool>(WKUInt64GetValue(static_cast<WKUInt64Ref>(WKDictionaryGetItemForKey(messageBodyDictionary, pagedKey.get()))));
1897
1898             // Forward to WebProcess
1899             m_eventSenderProxy->continuousMouseScrollBy(x, y, paged);
1900             return 0;
1901         }
1902
1903         if (WKStringIsEqualToUTF8CString(subMessageName, "LeapForward")) {
1904             WKRetainPtr<WKStringRef> timeKey = adoptWK(WKStringCreateWithUTF8CString("TimeInMilliseconds"));
1905             unsigned time = static_cast<unsigned>(WKUInt64GetValue(static_cast<WKUInt64Ref>(WKDictionaryGetItemForKey(messageBodyDictionary, timeKey.get()))));
1906
1907             m_eventSenderProxy->leapForward(time);
1908             return 0;
1909         }
1910
1911 #if ENABLE(TOUCH_EVENTS)
1912         if (WKStringIsEqualToUTF8CString(subMessageName, "AddTouchPoint")) {
1913             WKRetainPtr<WKStringRef> xKey = adoptWK(WKStringCreateWithUTF8CString("X"));
1914             int x = static_cast<int>(WKUInt64GetValue(static_cast<WKUInt64Ref>(WKDictionaryGetItemForKey(messageBodyDictionary, xKey.get()))));
1915
1916             WKRetainPtr<WKStringRef> yKey = adoptWK(WKStringCreateWithUTF8CString("Y"));
1917             int y = static_cast<int>(WKUInt64GetValue(static_cast<WKUInt64Ref>(WKDictionaryGetItemForKey(messageBodyDictionary, yKey.get()))));
1918
1919             m_eventSenderProxy->addTouchPoint(x, y);
1920             return 0;
1921         }
1922
1923         if (WKStringIsEqualToUTF8CString(subMessageName, "UpdateTouchPoint")) {
1924             WKRetainPtr<WKStringRef> indexKey = adoptWK(WKStringCreateWithUTF8CString("Index"));
1925             int index = static_cast<int>(WKUInt64GetValue(static_cast<WKUInt64Ref>(WKDictionaryGetItemForKey(messageBodyDictionary, indexKey.get()))));
1926
1927             WKRetainPtr<WKStringRef> xKey = adoptWK(WKStringCreateWithUTF8CString("X"));
1928             int x = static_cast<int>(WKUInt64GetValue(static_cast<WKUInt64Ref>(WKDictionaryGetItemForKey(messageBodyDictionary, xKey.get()))));
1929
1930             WKRetainPtr<WKStringRef> yKey = adoptWK(WKStringCreateWithUTF8CString("Y"));
1931             int y = static_cast<int>(WKUInt64GetValue(static_cast<WKUInt64Ref>(WKDictionaryGetItemForKey(messageBodyDictionary, yKey.get()))));
1932
1933             m_eventSenderProxy->updateTouchPoint(index, x, y);
1934             return 0;
1935         }
1936
1937         if (WKStringIsEqualToUTF8CString(subMessageName, "SetTouchModifier")) {
1938             WKRetainPtr<WKStringRef> modifierKey = adoptWK(WKStringCreateWithUTF8CString("Modifier"));
1939             WKEventModifiers modifier = static_cast<WKEventModifiers>(WKUInt64GetValue(static_cast<WKUInt64Ref>(WKDictionaryGetItemForKey(messageBodyDictionary, modifierKey.get()))));
1940
1941             WKRetainPtr<WKStringRef> enableKey = adoptWK(WKStringCreateWithUTF8CString("Enable"));
1942             bool enable = static_cast<bool>(WKUInt64GetValue(static_cast<WKUInt64Ref>(WKDictionaryGetItemForKey(messageBodyDictionary, enableKey.get()))));
1943
1944             m_eventSenderProxy->setTouchModifier(modifier, enable);
1945             return 0;
1946         }
1947
1948         if (WKStringIsEqualToUTF8CString(subMessageName, "SetTouchPointRadius")) {
1949             WKRetainPtr<WKStringRef> xKey = adoptWK(WKStringCreateWithUTF8CString("RadiusX"));
1950             int x = static_cast<int>(WKUInt64GetValue(static_cast<WKUInt64Ref>(WKDictionaryGetItemForKey(messageBodyDictionary, xKey.get()))));
1951
1952             WKRetainPtr<WKStringRef> yKey = adoptWK(WKStringCreateWithUTF8CString("RadiusY"));
1953             int y = static_cast<int>(WKUInt64GetValue(static_cast<WKUInt64Ref>(WKDictionaryGetItemForKey(messageBodyDictionary, yKey.get()))));
1954
1955             m_eventSenderProxy->setTouchPointRadius(x, y);
1956             return 0;
1957         }
1958
1959         if (WKStringIsEqualToUTF8CString(subMessageName, "TouchStart")) {
1960             m_eventSenderProxy->touchStart();
1961             return 0;
1962         }
1963
1964         if (WKStringIsEqualToUTF8CString(subMessageName, "TouchMove")) {
1965             m_eventSenderProxy->touchMove();
1966             return 0;
1967         }
1968
1969         if (WKStringIsEqualToUTF8CString(subMessageName, "TouchEnd")) {
1970             m_eventSenderProxy->touchEnd();
1971             return 0;
1972         }
1973
1974         if (WKStringIsEqualToUTF8CString(subMessageName, "TouchCancel")) {
1975             m_eventSenderProxy->touchCancel();
1976             return 0;
1977         }
1978
1979         if (WKStringIsEqualToUTF8CString(subMessageName, "ClearTouchPoints")) {
1980             m_eventSenderProxy->clearTouchPoints();
1981             return 0;
1982         }
1983
1984         if (WKStringIsEqualToUTF8CString(subMessageName, "ReleaseTouchPoint")) {
1985             WKRetainPtr<WKStringRef> indexKey = adoptWK(WKStringCreateWithUTF8CString("Index"));
1986             int index = static_cast<int>(WKUInt64GetValue(static_cast<WKUInt64Ref>(WKDictionaryGetItemForKey(messageBodyDictionary, indexKey.get()))));
1987             m_eventSenderProxy->releaseTouchPoint(index);
1988             return 0;
1989         }
1990
1991         if (WKStringIsEqualToUTF8CString(subMessageName, "CancelTouchPoint")) {
1992             WKRetainPtr<WKStringRef> indexKey = adoptWK(WKStringCreateWithUTF8CString("Index"));
1993             int index = static_cast<int>(WKUInt64GetValue(static_cast<WKUInt64Ref>(WKDictionaryGetItemForKey(messageBodyDictionary, indexKey.get()))));
1994             m_eventSenderProxy->cancelTouchPoint(index);
1995             return 0;
1996         }
1997 #endif
1998         ASSERT_NOT_REACHED();
1999     }
2000     return m_currentInvocation->didReceiveSynchronousMessageFromInjectedBundle(messageName, messageBody);
2001 }
2002
2003 WKRetainPtr<WKTypeRef> TestController::getInjectedBundleInitializationUserData()
2004 {
2005     return nullptr;
2006 }
2007
2008 // WKContextClient
2009
2010 void TestController::networkProcessDidCrash()
2011 {
2012     pid_t pid = WKContextGetNetworkProcessIdentifier(m_context.get());
2013     fprintf(stderr, "#CRASHED - %s (pid %ld)\n", networkProcessName(), static_cast<long>(pid));
2014     exit(1);
2015 }
2016
2017 // WKPageNavigationClient
2018
2019 void TestController::didCommitNavigation(WKPageRef page, WKNavigationRef navigation, WKTypeRef, const void* clientInfo)
2020 {
2021     static_cast<TestController*>(const_cast<void*>(clientInfo))->didCommitNavigation(page, navigation);
2022 }
2023
2024 void TestController::didFinishNavigation(WKPageRef page, WKNavigationRef navigation, WKTypeRef, const void* clientInfo)
2025 {
2026     static_cast<TestController*>(const_cast<void*>(clientInfo))->didFinishNavigation(page, navigation);
2027 }
2028
2029 void TestController::didReceiveServerRedirectForProvisionalNavigation(WKPageRef page, WKNavigationRef navigation, WKTypeRef userData, const void* clientInfo)
2030 {
2031     static_cast<TestController*>(const_cast<void*>(clientInfo))->didReceiveServerRedirectForProvisionalNavigation(page, navigation, userData);
2032 }
2033
2034 bool TestController::canAuthenticateAgainstProtectionSpace(WKPageRef page, WKProtectionSpaceRef protectionSpace, const void* clientInfo)
2035 {
2036     return static_cast<TestController*>(const_cast<void*>(clientInfo))->canAuthenticateAgainstProtectionSpace(page, protectionSpace);
2037 }
2038
2039 void TestController::didReceiveAuthenticationChallenge(WKPageRef page, WKAuthenticationChallengeRef authenticationChallenge, const void *clientInfo)
2040 {
2041     static_cast<TestController*>(const_cast<void*>(clientInfo))->didReceiveAuthenticationChallenge(page, /*frame,*/ authenticationChallenge);
2042 }
2043
2044 void TestController::processDidCrash(WKPageRef page, const void* clientInfo)
2045 {
2046     static_cast<TestController*>(const_cast<void*>(clientInfo))->processDidCrash();
2047 }
2048
2049 void TestController::didBeginNavigationGesture(WKPageRef page, const void *clientInfo)
2050 {
2051     static_cast<TestController*>(const_cast<void*>(clientInfo))->didBeginNavigationGesture(page);
2052 }
2053
2054 void TestController::willEndNavigationGesture(WKPageRef page, WKBackForwardListItemRef backForwardListItem, const void *clientInfo)
2055 {
2056     static_cast<TestController*>(const_cast<void*>(clientInfo))->willEndNavigationGesture(page, backForwardListItem);
2057 }
2058
2059 void TestController::didEndNavigationGesture(WKPageRef page, WKBackForwardListItemRef backForwardListItem, const void *clientInfo)
2060 {
2061     static_cast<TestController*>(const_cast<void*>(clientInfo))->didEndNavigationGesture(page, backForwardListItem);
2062 }
2063
2064 void TestController::didRemoveNavigationGestureSnapshot(WKPageRef page, const void *clientInfo)
2065 {
2066     static_cast<TestController*>(const_cast<void*>(clientInfo))->didRemoveNavigationGestureSnapshot(page);
2067 }
2068
2069 WKPluginLoadPolicy TestController::decidePolicyForPluginLoad(WKPageRef page, WKPluginLoadPolicy currentPluginLoadPolicy, WKDictionaryRef pluginInformation, WKStringRef* unavailabilityDescription, const void* clientInfo)
2070 {
2071     return static_cast<TestController*>(const_cast<void*>(clientInfo))->decidePolicyForPluginLoad(page, currentPluginLoadPolicy, pluginInformation, unavailabilityDescription);
2072 }
2073
2074 WKPluginLoadPolicy TestController::decidePolicyForPluginLoad(WKPageRef, WKPluginLoadPolicy currentPluginLoadPolicy, WKDictionaryRef pluginInformation, WKStringRef* unavailabilityDescription)
2075 {
2076     if (m_shouldBlockAllPlugins)
2077         return kWKPluginLoadPolicyBlocked;
2078
2079 #if PLATFORM(MAC)
2080     WKStringRef bundleIdentifier = (WKStringRef)WKDictionaryGetItemForKey(pluginInformation, WKPluginInformationBundleIdentifierKey());
2081     if (!bundleIdentifier)
2082         return currentPluginLoadPolicy;
2083
2084     if (WKStringIsEqualToUTF8CString(bundleIdentifier, "com.apple.QuickTime Plugin.plugin"))
2085         return currentPluginLoadPolicy;
2086
2087     if (WKStringIsEqualToUTF8CString(bundleIdentifier, "com.apple.testnetscapeplugin"))
2088         return currentPluginLoadPolicy;
2089
2090     RELEASE_ASSERT_NOT_REACHED(); // Please don't use any other plug-ins in tests, as they will not be installed on all machines.
2091 #else
2092     return currentPluginLoadPolicy;
2093 #endif
2094 }
2095
2096 void TestController::setBlockAllPlugins(bool shouldBlock)
2097 {
2098     m_shouldBlockAllPlugins = shouldBlock;
2099
2100 #if PLATFORM(MAC)
2101     auto policy = shouldBlock ? kWKPluginLoadClientPolicyBlock : kWKPluginLoadClientPolicyAllow;
2102
2103     WKRetainPtr<WKStringRef> nameNetscape = adoptWK(WKStringCreateWithUTF8CString("com.apple.testnetscapeplugin"));
2104     WKRetainPtr<WKStringRef> nameFlash = adoptWK(WKStringCreateWithUTF8CString("com.macromedia.Flash Player.plugin"));
2105     WKRetainPtr<WKStringRef> emptyString = adoptWK(WKStringCreateWithUTF8CString(""));
2106     WKContextSetPluginLoadClientPolicy(m_context.get(), policy, emptyString.get(), nameNetscape.get(), emptyString.get());
2107     WKContextSetPluginLoadClientPolicy(m_context.get(), policy, emptyString.get(), nameFlash.get(), emptyString.get());
2108 #endif
2109 }
2110
2111 void TestController::setPluginSupportedMode(const String& mode)
2112 {
2113     if (m_unsupportedPluginMode == mode)
2114         return;
2115
2116     m_unsupportedPluginMode = mode;
2117     if (m_unsupportedPluginMode.isEmpty()) {
2118         WKContextClearSupportedPlugins(m_context.get());
2119         return;
2120     }
2121
2122     WKRetainPtr<WKMutableArrayRef> emptyArray = adoptWK(WKMutableArrayCreate());
2123     WKRetainPtr<WKStringRef> allOrigins = adoptWK(WKStringCreateWithUTF8CString(""));
2124     WKRetainPtr<WKStringRef> specificOrigin = adoptWK(WKStringCreateWithUTF8CString("localhost"));
2125
2126     WKRetainPtr<WKStringRef> pdfName = adoptWK(WKStringCreateWithUTF8CString("My personal PDF"));
2127     WKContextAddSupportedPlugin(m_context.get(), allOrigins.get(), pdfName.get(), emptyArray.get(), emptyArray.get());
2128
2129     WKRetainPtr<WKStringRef> nameNetscape = adoptWK(WKStringCreateWithUTF8CString("com.apple.testnetscapeplugin"));
2130     WKRetainPtr<WKStringRef> mimeTypeNetscape = adoptWK(WKStringCreateWithUTF8CString("application/x-webkit-test-netscape"));
2131     WKRetainPtr<WKMutableArrayRef> mimeTypesNetscape = adoptWK(WKMutableArrayCreate());
2132     WKArrayAppendItem(mimeTypesNetscape.get(), mimeTypeNetscape.get());
2133
2134     WKRetainPtr<WKStringRef> namePdf = adoptWK(WKStringCreateWithUTF8CString("WebKit built-in PDF"));
2135
2136     if (m_unsupportedPluginMode == "allOrigins") {
2137         WKContextAddSupportedPlugin(m_context.get(), allOrigins.get(), nameNetscape.get(), mimeTypesNetscape.get(), emptyArray.get());
2138         WKContextAddSupportedPlugin(m_context.get(), allOrigins.get(), namePdf.get(), emptyArray.get(), emptyArray.get());
2139         return;
2140     }
2141
2142     if (m_unsupportedPluginMode == "specificOrigin") {
2143         WKContextAddSupportedPlugin(m_context.get(), specificOrigin.get(), nameNetscape.get(), mimeTypesNetscape.get(), emptyArray.get());
2144         WKContextAddSupportedPlugin(m_context.get(), specificOrigin.get(), namePdf.get(), emptyArray.get(), emptyArray.get());
2145         return;
2146     }
2147 }
2148
2149 void TestController::didCommitNavigation(WKPageRef page, WKNavigationRef navigation)
2150 {
2151     mainWebView()->focus();
2152 }
2153
2154 void TestController::didReceiveServerRedirectForProvisionalNavigation(WKPageRef page, WKNavigationRef navigation, WKTypeRef userData)
2155 {
2156     m_didReceiveServerRedirectForProvisionalNavigation = true;
2157     return;
2158 }
2159
2160 static const char* toString(WKProtectionSpaceAuthenticationScheme scheme)
2161 {
2162     switch (scheme) {
2163     case kWKProtectionSpaceAuthenticationSchemeDefault:
2164         return "ProtectionSpaceAuthenticationSchemeDefault";
2165     case kWKProtectionSpaceAuthenticationSchemeHTTPBasic:
2166         return "ProtectionSpaceAuthenticationSchemeHTTPBasic";
2167     case kWKProtectionSpaceAuthenticationSchemeHTMLForm:
2168         return "ProtectionSpaceAuthenticationSchemeHTMLForm";
2169     case kWKProtectionSpaceAuthenticationSchemeNTLM:
2170         return "ProtectionSpaceAuthenticationSchemeNTLM";
2171     case kWKProtectionSpaceAuthenticationSchemeNegotiate:
2172         return "ProtectionSpaceAuthenticationSchemeNegotiate";
2173     case kWKProtectionSpaceAuthenticationSchemeClientCertificateRequested:
2174         return "ProtectionSpaceAuthenticationSchemeClientCertificateRequested";
2175     case kWKProtectionSpaceAuthenticationSchemeServerTrustEvaluationRequested:
2176         return "ProtectionSpaceAuthenticationSchemeServerTrustEvaluationRequested";
2177     case kWKProtectionSpaceAuthenticationSchemeOAuth:
2178         return "ProtectionSpaceAuthenticationSchemeOAuth";
2179     case kWKProtectionSpaceAuthenticationSchemeUnknown:
2180         return "ProtectionSpaceAuthenticationSchemeUnknown";
2181     }
2182     ASSERT_NOT_REACHED();
2183     return "ProtectionSpaceAuthenticationSchemeUnknown";
2184 }
2185
2186 bool TestController::canAuthenticateAgainstProtectionSpace(WKPageRef page, WKProtectionSpaceRef protectionSpace)
2187 {
2188     if (m_shouldLogCanAuthenticateAgainstProtectionSpace)
2189         m_currentInvocation->outputText("canAuthenticateAgainstProtectionSpace\n");
2190     WKProtectionSpaceAuthenticationScheme authenticationScheme = WKProtectionSpaceGetAuthenticationScheme(protectionSpace);
2191     
2192     if (authenticationScheme == kWKProtectionSpaceAuthenticationSchemeServerTrustEvaluationRequested) {
2193         std::string host = toSTD(adoptWK(WKProtectionSpaceCopyHost(protectionSpace)).get());
2194         return host == "localhost" || host == "127.0.0.1" || (m_allowAnyHTTPSCertificateForAllowedHosts && m_allowedHosts.find(host) != m_allowedHosts.end());
2195     }
2196     
2197     return authenticationScheme <= kWKProtectionSpaceAuthenticationSchemeHTTPDigest || authenticationScheme == kWKProtectionSpaceAuthenticationSchemeOAuth;
2198 }
2199
2200 void TestController::didFinishNavigation(WKPageRef page, WKNavigationRef navigation)
2201 {
2202     if (m_state != Resetting)
2203         return;
2204
2205     WKRetainPtr<WKURLRef> wkURL(AdoptWK, WKFrameCopyURL(WKPageGetMainFrame(page)));
2206     if (!WKURLIsEqual(wkURL.get(), blankURL()))
2207         return;
2208
2209     m_doneResetting = true;
2210     singleton().notifyDone();
2211 }
2212
2213 void TestController::didReceiveAuthenticationChallenge(WKPageRef page, WKAuthenticationChallengeRef authenticationChallenge)
2214 {
2215     WKProtectionSpaceRef protectionSpace = WKAuthenticationChallengeGetProtectionSpace(authenticationChallenge);
2216     WKAuthenticationDecisionListenerRef decisionListener = WKAuthenticationChallengeGetDecisionListener(authenticationChallenge);
2217     WKProtectionSpaceAuthenticationScheme authenticationScheme = WKProtectionSpaceGetAuthenticationScheme(protectionSpace);
2218
2219     if (authenticationScheme == kWKProtectionSpaceAuthenticationSchemeServerTrustEvaluationRequested) {
2220         // Any non-empty credential signals to accept the server trust. Since the cross-platform API
2221         // doesn't expose a way to create a credential from server trust, we use a password credential.
2222
2223         m_serverTrustEvaluationCallbackCallsCount++;
2224
2225         WKRetainPtr<WKCredentialRef> credential = adoptWK(WKCredentialCreate(toWK("accept server trust").get(), toWK("").get(), kWKCredentialPersistenceNone));
2226         WKAuthenticationDecisionListenerUseCredential(decisionListener, credential.get());
2227         return;
2228     }
2229
2230     if (m_rejectsProtectionSpaceAndContinueForAuthenticationChallenges) {
2231         m_currentInvocation->outputText("Simulating reject protection space and continue for authentication challenge\n");
2232         WKAuthenticationDecisionListenerRejectProtectionSpaceAndContinue(decisionListener);
2233         return;
2234     }
2235
2236     std::string host = toSTD(adoptWK(WKProtectionSpaceCopyHost(protectionSpace)).get());
2237     int port = WKProtectionSpaceGetPort(protectionSpace);
2238     String message = makeString(host.c_str(), ':', port, " - didReceiveAuthenticationChallenge - ", toString(authenticationScheme), " - ");
2239     if (!m_handlesAuthenticationChallenges)
2240         message.append("Simulating cancelled authentication sheet\n");
2241     else
2242         message.append("Responding with " + m_authenticationUsername + ":" + m_authenticationPassword + "\n");
2243     m_currentInvocation->outputText(message);
2244
2245     if (!m_handlesAuthenticationChallenges) {
2246         WKAuthenticationDecisionListenerUseCredential(decisionListener, 0);
2247         return;
2248     }
2249     WKRetainPtr<WKStringRef> username(AdoptWK, WKStringCreateWithUTF8CString(m_authenticationUsername.utf8().data()));
2250     WKRetainPtr<WKStringRef> password(AdoptWK, WKStringCreateWithUTF8CString(m_authenticationPassword.utf8().data()));
2251     WKRetainPtr<WKCredentialRef> credential(AdoptWK, WKCredentialCreate(username.get(), password.get(), kWKCredentialPersistenceForSession));
2252     WKAuthenticationDecisionListenerUseCredential(decisionListener, credential.get());
2253 }
2254
2255     
2256 // WKContextDownloadClient
2257
2258 void TestController::downloadDidStart(WKContextRef context, WKDownloadRef download, const void* clientInfo)
2259 {
2260     static_cast<TestController*>(const_cast<void*>(clientInfo))->downloadDidStart(context, download);
2261 }
2262     
2263 WKStringRef TestController::decideDestinationWithSuggestedFilename(WKContextRef context, WKDownloadRef download, WKStringRef filename, bool* allowOverwrite, const void* clientInfo)
2264 {
2265     return static_cast<TestController*>(const_cast<void*>(clientInfo))->decideDestinationWithSuggestedFilename(context, download, filename, allowOverwrite);
2266 }
2267
2268 void TestController::downloadDidFinish(WKContextRef context, WKDownloadRef download, const void* clientInfo)
2269 {
2270     static_cast<TestController*>(const_cast<void*>(clientInfo))->downloadDidFinish(context, download);
2271 }
2272
2273 void TestController::downloadDidFail(WKContextRef context, WKDownloadRef download, WKErrorRef error, const void* clientInfo)
2274 {
2275     static_cast<TestController*>(const_cast<void*>(clientInfo))->downloadDidFail(context, download, error);
2276 }
2277
2278 void TestController::downloadDidCancel(WKContextRef context, WKDownloadRef download, const void* clientInfo)
2279 {
2280     static_cast<TestController*>(const_cast<void*>(clientInfo))->downloadDidCancel(context, download);
2281 }
2282
2283 void TestController::downloadDidReceiveServerRedirectToURL(WKContextRef context, WKDownloadRef download, WKURLRef url, const void* clientInfo)
2284 {
2285     static_cast<TestController*>(const_cast<void*>(clientInfo))->downloadDidReceiveServerRedirectToURL(context, download, url);
2286 }
2287
2288 void TestController::downloadDidStart(WKContextRef context, WKDownloadRef download)
2289 {
2290     if (m_shouldLogDownloadCallbacks)
2291         m_currentInvocation->outputText("Download started.\n");
2292 }
2293
2294 WKStringRef TestController::decideDestinationWithSuggestedFilename(WKContextRef, WKDownloadRef, WKStringRef filename, bool*& allowOverwrite)
2295 {
2296     String suggestedFilename = toWTFString(filename);
2297
2298     if (m_shouldLogDownloadCallbacks) {
2299         StringBuilder builder;
2300         builder.append("Downloading URL with suggested filename \"");
2301         builder.append(suggestedFilename);
2302         builder.append("\"\n");
2303         m_currentInvocation->outputText(builder.toString());
2304     }
2305
2306     const char* dumpRenderTreeTemp = libraryPathForTesting();
2307     if (!dumpRenderTreeTemp)
2308         return nullptr;
2309
2310     *allowOverwrite = true;
2311     String temporaryFolder = String::fromUTF8(dumpRenderTreeTemp);
2312     if (suggestedFilename.isEmpty())
2313         suggestedFilename = "Unknown";
2314
2315     return toWK(temporaryFolder + "/" + suggestedFilename).leakRef();
2316 }
2317
2318 void TestController::downloadDidFinish(WKContextRef, WKDownloadRef)
2319 {
2320     if (m_shouldLogDownloadCallbacks)
2321         m_currentInvocation->outputText("Download completed.\n");
2322     m_currentInvocation->notifyDownloadDone();
2323 }
2324
2325 void TestController::downloadDidReceiveServerRedirectToURL(WKContextRef, WKDownloadRef, WKURLRef url)
2326 {
2327     if (m_shouldLogDownloadCallbacks) {
2328         StringBuilder builder;
2329         builder.appendLiteral("Download was redirected to \"");
2330         WKRetainPtr<WKStringRef> urlStringWK = adoptWK(WKURLCopyString(url));
2331         builder.append(toSTD(urlStringWK).c_str());
2332         builder.appendLiteral("\".\n");
2333         m_currentInvocation->outputText(builder.toString());
2334     }
2335 }
2336
2337 void TestController::downloadDidFail(WKContextRef, WKDownloadRef, WKErrorRef error)
2338 {
2339     if (m_shouldLogDownloadCallbacks) {
2340         m_currentInvocation->outputText("Download failed.\n"_s);
2341
2342         WKRetainPtr<WKStringRef> errorDomain = adoptWK(WKErrorCopyDomain(error));
2343         WKRetainPtr<WKStringRef> errorDescription = adoptWK(WKErrorCopyLocalizedDescription(error));
2344         int errorCode = WKErrorGetErrorCode(error);
2345
2346         StringBuilder errorBuilder;
2347         errorBuilder.append("Failed: ");
2348         errorBuilder.append(toWTFString(errorDomain));
2349         errorBuilder.append(", code=");
2350         errorBuilder.appendNumber(errorCode);
2351         errorBuilder.append(", description=");
2352         errorBuilder.append(toWTFString(errorDescription));
2353         errorBuilder.append("\n");
2354
2355         m_currentInvocation->outputText(errorBuilder.toString());
2356     }
2357     m_currentInvocation->notifyDownloadDone();
2358 }
2359
2360 void TestController::downloadDidCancel(WKContextRef, WKDownloadRef)
2361 {
2362     if (m_shouldLogDownloadCallbacks)
2363         m_currentInvocation->outputText("Download cancelled.\n");
2364     m_currentInvocation->notifyDownloadDone();
2365 }
2366
2367 void TestController::processDidCrash()
2368 {
2369     // This function can be called multiple times when crash logs are being saved on Windows, so
2370     // ensure we only print the crashed message once.
2371     if (!m_didPrintWebProcessCrashedMessage) {
2372         pid_t pid = WKPageGetProcessIdentifier(m_mainWebView->page());
2373         fprintf(stderr, "#CRASHED - %s (pid %ld)\n", webProcessName(), static_cast<long>(pid));
2374         fflush(stderr);
2375         m_didPrintWebProcessCrashedMessage = true;
2376     }
2377
2378     if (m_shouldExitWhenWebProcessCrashes)
2379         exit(1);
2380 }
2381
2382 void TestController::didBeginNavigationGesture(WKPageRef)
2383 {
2384     m_currentInvocation->didBeginSwipe();
2385 }
2386
2387 void TestController::willEndNavigationGesture(WKPageRef, WKBackForwardListItemRef)
2388 {
2389     m_currentInvocation->willEndSwipe();
2390 }
2391
2392 void TestController::didEndNavigationGesture(WKPageRef, WKBackForwardListItemRef)
2393 {
2394     m_currentInvocation->didEndSwipe();
2395 }
2396
2397 void TestController::didRemoveNavigationGestureSnapshot(WKPageRef)
2398 {
2399     m_currentInvocation->didRemoveSwipeSnapshot();
2400 }
2401
2402 void TestController::simulateWebNotificationClick(uint64_t notificationID)
2403 {
2404     m_webNotificationProvider.simulateWebNotificationClick(mainWebView()->page(), notificationID);
2405 }
2406
2407 void TestController::setGeolocationPermission(bool enabled)
2408 {
2409     m_isGeolocationPermissionSet = true;
2410     m_isGeolocationPermissionAllowed = enabled;
2411     decidePolicyForGeolocationPermissionRequestIfPossible();
2412 }
2413
2414 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)
2415 {
2416     m_geolocationProvider->setPosition(latitude, longitude, accuracy, providesAltitude, altitude, providesAltitudeAccuracy, altitudeAccuracy, providesHeading, heading, providesSpeed, speed, providesFloorLevel, floorLevel);
2417 }
2418
2419 void TestController::setMockGeolocationPositionUnavailableError(WKStringRef errorMessage)
2420 {
2421     m_geolocationProvider->setPositionUnavailableError(errorMessage);
2422 }
2423
2424 void TestController::handleGeolocationPermissionRequest(WKGeolocationPermissionRequestRef geolocationPermissionRequest)
2425 {
2426     m_geolocationPermissionRequests.append(geolocationPermissionRequest);
2427     decidePolicyForGeolocationPermissionRequestIfPossible();
2428 }
2429
2430 bool TestController::isGeolocationProviderActive() const
2431 {
2432     return m_geolocationProvider->isActive();
2433 }
2434
2435 static String originUserVisibleName(WKSecurityOriginRef origin)
2436 {
2437     if (!origin)
2438         return emptyString();
2439
2440     auto host = toWTFString(adoptWK(WKSecurityOriginCopyHost(origin)));
2441     auto protocol = toWTFString(adoptWK(WKSecurityOriginCopyProtocol(origin)));
2442
2443     if (host.isEmpty() || protocol.isEmpty())
2444         return emptyString();
2445
2446     if (int port = WKSecurityOriginGetPort(origin))
2447         return makeString(protocol, "://", host, ':', port);
2448
2449     return makeString(protocol, "://", host);
2450 }
2451
2452 static String userMediaOriginHash(WKSecurityOriginRef userMediaDocumentOrigin, WKSecurityOriginRef topLevelDocumentOrigin)
2453 {
2454     String userMediaDocumentOriginString = originUserVisibleName(userMediaDocumentOrigin);
2455     String topLevelDocumentOriginString = originUserVisibleName(topLevelDocumentOrigin);
2456
2457     if (topLevelDocumentOriginString.isEmpty())
2458         return userMediaDocumentOriginString;
2459
2460     return makeString(userMediaDocumentOriginString, '-', topLevelDocumentOriginString);
2461 }
2462
2463 static String userMediaOriginHash(WKStringRef userMediaDocumentOriginString, WKStringRef topLevelDocumentOriginString)
2464 {
2465     auto userMediaDocumentOrigin = adoptWK(WKSecurityOriginCreateFromString(userMediaDocumentOriginString));
2466     if (!WKStringGetLength(topLevelDocumentOriginString))
2467         return userMediaOriginHash(userMediaDocumentOrigin.get(), nullptr);
2468
2469     auto topLevelDocumentOrigin = adoptWK(WKSecurityOriginCreateFromString(topLevelDocumentOriginString));
2470     return userMediaOriginHash(userMediaDocumentOrigin.get(), topLevelDocumentOrigin.get());
2471 }
2472
2473 void TestController::setUserMediaPermission(bool enabled)
2474 {
2475     m_isUserMediaPermissionSet = true;
2476     m_isUserMediaPermissionAllowed = enabled;
2477     decidePolicyForUserMediaPermissionRequestIfPossible();
2478 }
2479
2480 void TestController::resetUserMediaPermission()
2481 {
2482     m_isUserMediaPermissionSet = false;
2483 }
2484
2485 void TestController::setShouldDismissJavaScriptAlertsAsynchronously(bool value)
2486 {
2487     m_shouldDismissJavaScriptAlertsAsynchronously = value;
2488 }
2489
2490 void TestController::handleJavaScriptAlert(WKPageRunJavaScriptAlertResultListenerRef listener)
2491 {
2492     if (!m_shouldDismissJavaScriptAlertsAsynchronously) {
2493         WKPageRunJavaScriptAlertResultListenerCall(listener);
2494         return;
2495     }
2496
2497     WKRetain(listener);
2498     callOnMainThread([listener] {
2499         WKPageRunJavaScriptAlertResultListenerCall(listener);
2500         WKRelease(listener);
2501     });
2502 }
2503
2504 class OriginSettings : public RefCounted<OriginSettings> {
2505 public:
2506     explicit OriginSettings()
2507     {
2508     }
2509
2510     bool persistentPermission() const { return m_persistentPermission; }
2511     void setPersistentPermission(bool permission) { m_persistentPermission = permission; }
2512
2513     String persistentSalt() const { return m_persistentSalt; }
2514     void setPersistentSalt(const String& salt) { m_persistentSalt = salt; }
2515
2516     HashMap<uint64_t, String>& ephemeralSalts() { return m_ephemeralSalts; }
2517
2518     void incrementRequestCount() { ++m_requestCount; }
2519     void resetRequestCount() { m_requestCount = 0; }
2520     unsigned requestCount() const { return m_requestCount; }
2521
2522 private:
2523     HashMap<uint64_t, String> m_ephemeralSalts;
2524     String m_persistentSalt;
2525     unsigned m_requestCount { 0 };
2526     bool m_persistentPermission { false };
2527 };
2528
2529 String TestController::saltForOrigin(WKFrameRef frame, String originHash)
2530 {
2531     auto& settings = settingsForOrigin(originHash);
2532     auto& ephemeralSalts = settings.ephemeralSalts();
2533     auto frameInfo = adoptWK(WKFrameCreateFrameInfo(frame));
2534     auto frameHandle = WKFrameInfoGetFrameHandleRef(frameInfo.get());
2535     uint64_t frameIdentifier = WKFrameHandleGetFrameID(frameHandle);
2536     String frameSalt = ephemeralSalts.get(frameIdentifier);
2537
2538     if (settings.persistentPermission()) {
2539         if (frameSalt.length())
2540             return frameSalt;
2541
2542         if (!settings.persistentSalt().length())
2543             settings.setPersistentSalt(createCanonicalUUIDString());
2544
2545         return settings.persistentSalt();
2546     }
2547
2548     if (!frameSalt.length()) {
2549         frameSalt = createCanonicalUUIDString();
2550         ephemeralSalts.add(frameIdentifier, frameSalt);
2551     }
2552
2553     return frameSalt;
2554 }
2555
2556 void TestController::setUserMediaPersistentPermissionForOrigin(bool permission, WKStringRef userMediaDocumentOriginString, WKStringRef topLevelDocumentOriginString)
2557 {
2558     auto originHash = userMediaOriginHash(userMediaDocumentOriginString, topLevelDocumentOriginString);
2559     auto& settings = settingsForOrigin(originHash);
2560     settings.setPersistentPermission(permission);
2561 }
2562
2563 void TestController::handleCheckOfUserMediaPermissionForOrigin(WKFrameRef frame, WKSecurityOriginRef userMediaDocumentOrigin, WKSecurityOriginRef topLevelDocumentOrigin, const WKUserMediaPermissionCheckRef& checkRequest)
2564 {
2565     auto originHash = userMediaOriginHash(userMediaDocumentOrigin, topLevelDocumentOrigin);
2566     auto salt = saltForOrigin(frame, originHash);
2567     WKRetainPtr<WKStringRef> saltString = adoptWK(WKStringCreateWithUTF8CString(salt.utf8().data()));
2568
2569     WKUserMediaPermissionCheckSetUserMediaAccessInfo(checkRequest, saltString.get(), settingsForOrigin(originHash).persistentPermission());
2570 }
2571
2572 bool TestController::handleDeviceOrientationAndMotionAccessRequest(WKSecurityOriginRef origin)
2573 {
2574     m_currentInvocation->outputText(makeString("Received device orientation & motion access request for security origin \"", originUserVisibleName(origin), "\".\n"));
2575     return m_shouldAllowDeviceOrientationAndMotionAccess;
2576 }
2577
2578 void TestController::handleUserMediaPermissionRequest(WKFrameRef frame, WKSecurityOriginRef userMediaDocumentOrigin, WKSecurityOriginRef topLevelDocumentOrigin, WKUserMediaPermissionRequestRef request)
2579 {
2580     auto originHash = userMediaOriginHash(userMediaDocumentOrigin, topLevelDocumentOrigin);
2581     m_userMediaPermissionRequests.append(std::make_pair(originHash, request));
2582     decidePolicyForUserMediaPermissionRequestIfPossible();
2583 }
2584
2585 OriginSettings& TestController::settingsForOrigin(const String& originHash)
2586 {
2587     RefPtr<OriginSettings> settings = m_cachedUserMediaPermissions.get(originHash);
2588     if (!settings) {
2589         settings = adoptRef(*new OriginSettings());
2590         m_cachedUserMediaPermissions.add(originHash, settings);
2591     }
2592
2593     return *settings;
2594 }
2595
2596 unsigned TestController::userMediaPermissionRequestCountForOrigin(WKStringRef userMediaDocumentOriginString, WKStringRef topLevelDocumentOriginString)
2597 {
2598     auto originHash = userMediaOriginHash(userMediaDocumentOriginString, topLevelDocumentOriginString);
2599     return settingsForOrigin(originHash).requestCount();
2600 }
2601
2602 void TestController::resetUserMediaPermissionRequestCountForOrigin(WKStringRef userMediaDocumentOriginString, WKStringRef topLevelDocumentOriginString)
2603 {
2604     auto originHash = userMediaOriginHash(userMediaDocumentOriginString, topLevelDocumentOriginString);
2605     settingsForOrigin(originHash).resetRequestCount();
2606 }
2607
2608 void TestController::decidePolicyForUserMediaPermissionRequestIfPossible()
2609 {
2610     if (!m_isUserMediaPermissionSet)
2611         return;
2612
2613     for (auto& pair : m_userMediaPermissionRequests) {
2614         auto originHash = pair.first;
2615         auto request = pair.second.get();
2616
2617         auto& settings = settingsForOrigin(originHash);
2618         settings.incrementRequestCount();
2619
2620         if (!m_isUserMediaPermissionAllowed && !settings.persistentPermission()) {
2621             WKUserMediaPermissionRequestDeny(request, kWKPermissionDenied);
2622             continue;
2623         }
2624
2625         WKRetainPtr<WKArrayRef> audioDeviceUIDs = adoptWK(WKUserMediaPermissionRequestAudioDeviceUIDs(request));
2626         WKRetainPtr<WKArrayRef> videoDeviceUIDs = adoptWK(WKUserMediaPermissionRequestVideoDeviceUIDs(request));
2627
2628         if (!WKArrayGetSize(videoDeviceUIDs.get()) && !WKArrayGetSize(audioDeviceUIDs.get())) {
2629             WKUserMediaPermissionRequestDeny(request, kWKNoConstraints);
2630             continue;
2631         }
2632
2633         WKRetainPtr<WKStringRef> videoDeviceUID;
2634         if (WKArrayGetSize(videoDeviceUIDs.get()))
2635             videoDeviceUID = reinterpret_cast<WKStringRef>(WKArrayGetItemAtIndex(videoDeviceUIDs.get(), 0));
2636         else
2637             videoDeviceUID = adoptWK(WKStringCreateWithUTF8CString(""));
2638
2639         WKRetainPtr<WKStringRef> audioDeviceUID;
2640         if (WKArrayGetSize(audioDeviceUIDs.get()))
2641             audioDeviceUID = reinterpret_cast<WKStringRef>(WKArrayGetItemAtIndex(audioDeviceUIDs.get(), 0));
2642         else
2643             audioDeviceUID = adoptWK(WKStringCreateWithUTF8CString(""));
2644
2645         WKUserMediaPermissionRequestAllow(request, audioDeviceUID.get(), videoDeviceUID.get());
2646     }
2647     m_userMediaPermissionRequests.clear();
2648 }
2649
2650 void TestController::setCustomPolicyDelegate(bool enabled, bool permissive)
2651 {
2652     m_policyDelegateEnabled = enabled;
2653     m_policyDelegatePermissive = permissive;
2654 }
2655
2656 void TestController::decidePolicyForGeolocationPermissionRequestIfPossible()
2657 {
2658     if (!m_isGeolocationPermissionSet)
2659         return;
2660
2661     for (size_t i = 0; i < m_geolocationPermissionRequests.size(); ++i) {
2662         WKGeolocationPermissionRequestRef permissionRequest = m_geolocationPermissionRequests[i].get();
2663         if (m_isGeolocationPermissionAllowed)
2664             WKGeolocationPermissionRequestAllow(permissionRequest);
2665         else
2666             WKGeolocationPermissionRequestDeny(permissionRequest);
2667     }
2668     m_geolocationPermissionRequests.clear();
2669 }
2670
2671 void TestController::decidePolicyForNotificationPermissionRequest(WKPageRef page, WKSecurityOriginRef origin, WKNotificationPermissionRequestRef request, const void*)
2672 {
2673     TestController::singleton().decidePolicyForNotificationPermissionRequest(page, origin, request);
2674 }
2675
2676 void TestController::decidePolicyForNotificationPermissionRequest(WKPageRef, WKSecurityOriginRef, WKNotificationPermissionRequestRef request)
2677 {
2678     WKNotificationPermissionRequestAllow(request);
2679 }
2680
2681 void TestController::unavailablePluginButtonClicked(WKPageRef, WKPluginUnavailabilityReason, WKDictionaryRef, const void*)
2682 {
2683     printf("MISSING PLUGIN BUTTON PRESSED\n");
2684 }
2685
2686 void TestController::decidePolicyForNavigationAction(WKPageRef, WKNavigationActionRef navigationAction, WKFramePolicyListenerRef listener, WKTypeRef, const void* clientInfo)
2687 {
2688     static_cast<TestController*>(const_cast<void*>(clientInfo))->decidePolicyForNavigationAction(navigationAction, listener);
2689 }
2690
2691 void TestController::decidePolicyForNavigationAction(WKNavigationActionRef navigationAction, WKFramePolicyListenerRef listener)
2692 {
2693     WKRetainPtr<WKFramePolicyListenerRef> retainedListener { listener };
2694     WKRetainPtr<WKNavigationActionRef> retainedNavigationAction { navigationAction };
2695     const bool shouldIgnore { m_policyDelegateEnabled && !m_policyDelegatePermissive };
2696     auto decisionFunction = [shouldIgnore, retainedListener, retainedNavigationAction]() {
2697         if (shouldIgnore)
2698             WKFramePolicyListenerIgnore(retainedListener.get());
2699         else if (WKNavigationActionShouldPerformDownload(retainedNavigationAction.get()))
2700             WKFramePolicyListenerDownload(retainedListener.get());
2701         else
2702             WKFramePolicyListenerUse(retainedListener.get());
2703     };
2704
2705     if (m_shouldDecideNavigationPolicyAfterDelay)
2706         RunLoop::main().dispatch(WTFMove(decisionFunction));
2707     else
2708         decisionFunction();
2709 }
2710
2711 void TestController::decidePolicyForNavigationResponse(WKPageRef, WKNavigationResponseRef navigationResponse, WKFramePolicyListenerRef listener, WKTypeRef, const void* clientInfo)
2712 {
2713     static_cast<TestController*>(const_cast<void*>(clientInfo))->decidePolicyForNavigationResponse(navigationResponse, listener);
2714 }
2715
2716 void TestController::decidePolicyForNavigationResponse(WKNavigationResponseRef navigationResponse, WKFramePolicyListenerRef listener)
2717 {
2718     WKRetainPtr<WKNavigationResponseRef> retainedNavigationResponse { navigationResponse };
2719     WKRetainPtr<WKFramePolicyListenerRef> retainedListener { listener };
2720
2721     bool shouldDownloadUndisplayableMIMETypes = m_shouldDownloadUndisplayableMIMETypes;
2722     auto decisionFunction = [shouldDownloadUndisplayableMIMETypes, retainedNavigationResponse, retainedListener]() {
2723         // Even though Response was already checked by WKBundlePagePolicyClient, the check did not include plugins
2724         // so we have to re-check again.
2725         if (WKNavigationResponseCanShowMIMEType(retainedNavigationResponse.get())) {
2726             WKFramePolicyListenerUse(retainedListener.get());
2727             return;
2728         }
2729
2730         if (shouldDownloadUndisplayableMIMETypes)
2731             WKFramePolicyListenerDownload(retainedListener.get());
2732         else
2733             WKFramePolicyListenerIgnore(retainedListener.get());
2734     };
2735
2736     if (m_shouldDecideResponsePolicyAfterDelay)
2737         RunLoop::main().dispatch(WTFMove(decisionFunction));
2738     else
2739         decisionFunction();
2740 }
2741
2742 void TestController::didNavigateWithNavigationData(WKContextRef, WKPageRef, WKNavigationDataRef navigationData, WKFrameRef frame, const void* clientInfo)
2743 {
2744     static_cast<TestController*>(const_cast<void*>(clientInfo))->didNavigateWithNavigationData(navigationData, frame);
2745 }
2746
2747 void TestController::didNavigateWithNavigationData(WKNavigationDataRef navigationData, WKFrameRef)
2748 {
2749     if (m_state != RunningTest)
2750         return;
2751
2752     if (!m_shouldLogHistoryClientCallbacks)
2753         return;
2754
2755     // URL
2756     auto url = adoptWK(WKNavigationDataCopyURL(navigationData));
2757     auto urlString = toWTFString(adoptWK(WKURLCopyString(url.get())));
2758     // Title
2759     auto title = toWTFString(adoptWK(WKNavigationDataCopyTitle(navigationData)));
2760     // HTTP method
2761     auto request = adoptWK(WKNavigationDataCopyOriginalRequest(navigationData));
2762     auto method = toWTFString(adoptWK(WKURLRequestCopyHTTPMethod(request.get())));
2763
2764     // FIXME: Determine whether the navigation was successful / a client redirect rather than hard-coding the message here.
2765     m_currentInvocation->outputText(makeString("WebView navigated to url \"", urlString, "\" with title \"", title, "\" with HTTP equivalent method \"", method,
2766         "\".  The navigation was successful and was not a client redirect.\n"));
2767 }
2768
2769 void TestController::didPerformClientRedirect(WKContextRef, WKPageRef, WKURLRef sourceURL, WKURLRef destinationURL, WKFrameRef frame, const void* clientInfo)
2770 {
2771     static_cast<TestController*>(const_cast<void*>(clientInfo))->didPerformClientRedirect(sourceURL, destinationURL, frame);
2772 }
2773
2774 void TestController::didPerformClientRedirect(WKURLRef sourceURL, WKURLRef destinationURL, WKFrameRef)
2775 {
2776     if (m_state != RunningTest)
2777         return;
2778
2779     if (!m_shouldLogHistoryClientCallbacks)
2780         return;
2781
2782     auto source = toWTFString(adoptWK(WKURLCopyString(sourceURL)));
2783     auto destination = toWTFString(adoptWK(WKURLCopyString(destinationURL)));
2784
2785     m_currentInvocation->outputText(makeString("WebView performed a client redirect from \"", source, "\" to \"", destination, "\".\n"));
2786 }
2787
2788 void TestController::didPerformServerRedirect(WKContextRef, WKPageRef, WKURLRef sourceURL, WKURLRef destinationURL, WKFrameRef frame, const void* clientInfo)
2789 {
2790     static_cast<TestController*>(const_cast<void*>(clientInfo))->didPerformServerRedirect(sourceURL, destinationURL, frame);
2791 }
2792
2793 void TestController::didPerformServerRedirect(WKURLRef sourceURL, WKURLRef destinationURL, WKFrameRef)
2794 {
2795     if (m_state != RunningTest)
2796         return;
2797
2798     if (!m_shouldLogHistoryClientCallbacks)
2799         return;
2800
2801     auto source = toWTFString(adoptWK(WKURLCopyString(sourceURL)));
2802     auto destination = toWTFString(adoptWK(WKURLCopyString(destinationURL)));
2803
2804     m_currentInvocation->outputText(makeString("WebView performed a server redirect from \"", source, "\" to \"", destination, "\".\n"));
2805 }
2806
2807 void TestController::didUpdateHistoryTitle(WKContextRef, WKPageRef, WKStringRef title, WKURLRef URL, WKFrameRef frame, const void* clientInfo)
2808 {
2809     static_cast<TestController*>(const_cast<void*>(clientInfo))->didUpdateHistoryTitle(title, URL, frame);
2810 }
2811
2812 void TestController::didUpdateHistoryTitle(WKStringRef title, WKURLRef URL, WKFrameRef)
2813 {
2814     if (m_state != RunningTest)
2815         return;
2816
2817     if (!m_shouldLogHistoryClientCallbacks)
2818         return;
2819
2820     auto urlString = toWTFString(adoptWK(WKURLCopyString(URL)));
2821     m_currentInvocation->outputText(makeString("WebView updated the title for history URL \"", urlString, "\" to \"", toWTFString(title), "\".\n"));
2822 }
2823
2824 void TestController::setNavigationGesturesEnabled(bool value)
2825 {
2826     m_mainWebView->setNavigationGesturesEnabled(value);
2827 }
2828
2829 void TestController::setIgnoresViewportScaleLimits(bool ignoresViewportScaleLimits)
2830 {
2831     WKPageSetIgnoresViewportScaleLimits(m_mainWebView->page(), ignoresViewportScaleLimits);
2832 }
2833
2834 void TestController::terminateNetworkProcess()
2835 {
2836     WKContextTerminateNetworkProcess(platformContext());
2837 }
2838
2839 void TestController::terminateServiceWorkerProcess()
2840 {
2841     WKContextTerminateServiceWorkerProcess(platformContext());
2842 }
2843
2844 #if !PLATFORM(COCOA)
2845 void TestController::platformWillRunTest(const TestInvocation&)
2846 {
2847 }
2848
2849 void TestController::platformCreateWebView(WKPageConfigurationRef configuration, const TestOptions& options)
2850 {
2851     m_mainWebView = std::make_unique<PlatformWebView>(configuration, options);
2852 }
2853
2854 PlatformWebView* TestController::platformCreateOtherPage(PlatformWebView* parentView, WKPageConfigurationRef configuration, const TestOptions& options)
2855 {
2856     return new PlatformWebView(configuration, options);
2857 }
2858
2859 WKContextRef TestController::platformAdjustContext(WKContextRef context, WKContextConfigurationRef contextConfiguration)
2860 {
2861     auto* dataStore = WKContextGetWebsiteDataStore(context);
2862     WKWebsiteDataStoreSetResourceLoadStatisticsEnabled(dataStore, true);
2863
2864     if (const char* dumpRenderTreeTemp = libraryPathForTesting()) {
2865         String temporaryFolder = String::fromUTF8(dumpRenderTreeTemp);
2866
2867         WKWebsiteDataStoreSetServiceWorkerRegistrationDirectory(dataStore, toWK(temporaryFolder + pathSeparator + "ServiceWorkers").get());
2868     }
2869
2870     return context;
2871 }
2872
2873 void TestController::platformResetStateToConsistentValues(const TestOptions&)
2874 {
2875 }
2876
2877 unsigned TestController::imageCountInGeneralPasteboard() const
2878 {
2879     return 0;
2880 }
2881
2882 void TestController::removeAllSessionCredentials()
2883 {
2884 }
2885
2886 void TestController::getAllStorageAccessEntries()
2887 {
2888 }
2889
2890 #endif
2891
2892 struct ClearServiceWorkerRegistrationsCallbackContext {
2893     explicit ClearServiceWorkerRegistrationsCallbackContext(TestController& controller)
2894         : testController(controller)
2895     {
2896     }
2897
2898     TestController& testController;
2899     bool done { false };
2900 };
2901
2902 static void clearServiceWorkerRegistrationsCallback(void* userData)
2903 {
2904     auto* context = static_cast<ClearServiceWorkerRegistrationsCallbackContext*>(userData);
2905     context->done = true;
2906     context->testController.notifyDone();
2907 }
2908
2909 void TestController::clearServiceWorkerRegistrations()
2910 {
2911     auto websiteDataStore = WKContextGetWebsiteDataStore(platformContext());
2912     ClearServiceWorkerRegistrationsCallbackContext context(*this);
2913
2914     WKWebsiteDataStoreRemoveAllServiceWorkerRegistrations(websiteDataStore, &context, clearServiceWorkerRegistrationsCallback);
2915     runUntil(context.done, noTimeout);
2916 }
2917
2918 struct ClearDOMCacheCallbackContext {
2919     explicit ClearDOMCacheCallbackContext(TestController& controller)
2920         : testController(controller)
2921     {
2922     }
2923
2924     TestController& testController;
2925     bool done { false };
2926 };
2927
2928 static void clearDOMCacheCallback(void* userData)
2929 {
2930     auto* context = static_cast<ClearDOMCacheCallbackContext*>(userData);
2931     context->done = true;
2932     context->testController.notifyDone();
2933 }
2934
2935 void TestController::clearDOMCache(WKStringRef origin)
2936 {
2937     auto websiteDataStore = WKContextGetWebsiteDataStore(platformContext());
2938     ClearDOMCacheCallbackContext context(*this);
2939
2940     auto cacheOrigin = adoptWK(WKSecurityOriginCreateFromString(origin));
2941     WKWebsiteDataStoreRemoveFetchCacheForOrigin(websiteDataStore, cacheOrigin.get(), &context, clearDOMCacheCallback);
2942     runUntil(context.done, noTimeout);
2943 }
2944
2945 void TestController::clearDOMCaches()
2946 {
2947     auto websiteDataStore = WKContextGetWebsiteDataStore(platformContext());
2948     ClearDOMCacheCallbackContext context(*this);
2949
2950     WKWebsiteDataStoreRemoveAllFetchCaches(websiteDataStore, &context, clearDOMCacheCallback);
2951     runUntil(context.done, noTimeout);
2952 }
2953
2954 void TestController::setIDBPerOriginQuota(uint64_t quota)
2955 {
2956     WKContextSetIDBPerOriginQuota(platformContext(), quota);
2957 }
2958
2959 struct RemoveAllIndexedDatabasesCallbackContext {
2960     explicit RemoveAllIndexedDatabasesCallbackContext(TestController& controller)
2961         : testController(controller)
2962     {
2963     }
2964
2965     TestController& testController;
2966     bool done { false };
2967 };
2968
2969 static void RemoveAllIndexedDatabasesCallback(void* userData)
2970 {
2971     auto* context = static_cast<RemoveAllIndexedDatabasesCallbackContext*>(userData);
2972     context->done = true;
2973     context->testController.notifyDone();
2974 }
2975
2976 void TestController::ClearIndexedDatabases()
2977 {
2978     auto websiteDataStore = WKContextGetWebsiteDataStore(platformContext());
2979     RemoveAllIndexedDatabasesCallbackContext context(*this);
2980     WKWebsiteDataStoreRemoveAllIndexedDatabases(websiteDataStore, &context, RemoveAllIndexedDatabasesCallback);
2981     runUntil(context.done, noTimeout);
2982 }
2983
2984 struct FetchCacheOriginsCallbackContext {
2985     FetchCacheOriginsCallbackContext(TestController& controller, WKStringRef origin)
2986         : testController(controller)
2987         , origin(origin)
2988     {
2989     }
2990
2991     TestController& testController;
2992     WKStringRef origin;
2993
2994     bool done { false };
2995     bool result { false };
2996 };
2997
2998 static void fetchCacheOriginsCallback(WKArrayRef origins, void* userData)
2999 {
3000     auto* context = static_cast<FetchCacheOriginsCallbackContext*>(userData);
3001     context->done = true;
3002
3003     auto size = WKArrayGetSize(origins);
3004     for (size_t index = 0; index < size && !context->result; ++index) {
3005         WKSecurityOriginRef securityOrigin = reinterpret_cast<WKSecurityOriginRef>(WKArrayGetItemAtIndex(origins, index));
3006         if (WKStringIsEqual(context->origin, adoptWK(WKSecurityOriginCopyToString(securityOrigin)).get()))
3007             context->result = true;
3008     }
3009     context->testController.notifyDone();
3010 }
3011
3012 bool TestController::hasDOMCache(WKStringRef origin)
3013 {
3014     auto* dataStore = WKContextGetWebsiteDataStore(platformContext());
3015     FetchCacheOriginsCallbackContext context(*this, origin);
3016     WKWebsiteDataStoreGetFetchCacheOrigins(dataStore, &context, fetchCacheOriginsCallback);
3017     runUntil(context.done, noTimeout);
3018     return context.result;
3019 }
3020
3021 struct FetchCacheSizeForOriginCallbackContext {
3022     explicit FetchCacheSizeForOriginCallbackContext(TestController& controller)
3023         : testController(controller)
3024     {
3025     }
3026
3027     TestController& testController;
3028
3029     bool done { false };
3030     uint64_t result { 0 };
3031 };
3032
3033 static void fetchCacheSizeForOriginCallback(uint64_t size, void* userData)
3034 {
3035     auto* context = static_cast<FetchCacheSizeForOriginCallbackContext*>(userData);
3036     context->done = true;
3037     context->result = size;
3038     context->testController.notifyDone();
3039 }
3040
3041 uint64_t TestController::domCacheSize(WKStringRef origin)
3042 {
3043     auto* dataStore = WKContextGetWebsiteDataStore(platformContext());
3044     FetchCacheSizeForOriginCallbackContext context(*this);
3045     WKWebsiteDataStoreGetFetchCacheSizeForOrigin(dataStore, origin, &context, fetchCacheSizeForOriginCallback);
3046     runUntil(context.done, noTimeout);
3047     return context.result;
3048 }
3049
3050 #if !PLATFORM(COCOA)
3051 void TestController::setAllowStorageQuotaIncrease(bool)
3052 {
3053     // FIXME: To implement.
3054 }
3055
3056 bool TestController::isDoingMediaCapture() const
3057 {
3058     return false;
3059 }
3060
3061 #endif
3062
3063 struct ResourceStatisticsCallbackContext {
3064     explicit ResourceStatisticsCallbackContext(TestController& controller)
3065         : testController(controller)
3066     {
3067     }
3068
3069     TestController& testController;
3070     bool done { false };
3071     bool result { false };
3072     WKRetainPtr<WKStringRef> resourceLoadStatisticsRepresentation;
3073 };
3074     
3075 static void resourceStatisticsStringResultCallback(WKStringRef resourceLoadStatisticsRepresentation, void* userData)
3076 {
3077     auto* context = static_cast<ResourceStatisticsCallbackContext*>(userData);
3078     context->resourceLoadStatisticsRepresentation = resourceLoadStatisticsRepresentation;
3079     context->done = true;
3080     context->testController.notifyDone();
3081 }
3082
3083 static void resourceStatisticsVoidResultCallback(void* userData)
3084 {
3085     auto* context = static_cast<ResourceStatisticsCallbackContext*>(userData);
3086     context->done = true;
3087     context->testController.notifyDone();
3088 }
3089
3090 static void resourceStatisticsBooleanResultCallback(bool result, void* userData)
3091 {
3092     auto* context = static_cast<ResourceStatisticsCallbackContext*>(userData);
3093     context->result = result;
3094     context->done = true;
3095     context->testController.notifyDone();
3096 }
3097
3098 void TestController::setStatisticsDebugMode(bool value)
3099 {
3100     auto* dataStore = WKContextGetWebsiteDataStore(platformContext());
3101     ResourceStatisticsCallbackContext context(*this);
3102     WKWebsiteDataStoreSetResourceLoadStatisticsDebugModeWithCompletionHandler(dataStore, value, &context, resourceStatisticsVoidResultCallback);
3103     runUntil(context.done, noTimeout);
3104     m_currentInvocation->didSetStatisticsDebugMode();
3105 }
3106
3107 void TestController::setStatisticsPrevalentResourceForDebugMode(WKStringRef hostName)
3108 {
3109     auto* dataStore = WKContextGetWebsiteDataStore(platformContext());
3110     ResourceStatisticsCallbackContext context(*this);
3111     WKWebsiteDataStoreSetResourceLoadStatisticsPrevalentResourceForDebugMode(dataStore, hostName, &context, resourceStatisticsVoidResultCallback);
3112     runUntil(context.done, noTimeout);
3113     m_currentInvocation->didSetPrevalentResourceForDebugMode();
3114 }
3115
3116 void TestController::setStatisticsLastSeen(WKStringRef host, double seconds)
3117 {
3118     auto* dataStore = WKContextGetWebsiteDataStore(platformContext());
3119     ResourceStatisticsCallbackContext context(*this);
3120     WKWebsiteDataStoreSetStatisticsLastSeen(dataStore, host, seconds, &context, resourceStatisticsVoidResultCallback);
3121     runUntil(context.done, noTimeout);
3122     m_currentInvocation->didSetLastSeen();
3123 }
3124
3125 void TestController::setStatisticsPrevalentResource(WKStringRef host, bool value)
3126 {
3127     auto* dataStore = WKContextGetWebsiteDataStore(platformContext());
3128     ResourceStatisticsCallbackContext context(*this);
3129     WKWebsiteDataStoreSetStatisticsPrevalentResource(dataStore, host, value, &context, resourceStatisticsVoidResultCallback);
3130     runUntil(context.done, noTimeout);
3131     m_currentInvocation->didSetPrevalentResource();
3132 }
3133
3134 void TestController::setStatisticsVeryPrevalentResource(WKStringRef host, bool value)
3135 {
3136     auto* dataStore = WKContextGetWebsiteDataStore(platformContext());
3137     ResourceStatisticsCallbackContext context(*this);
3138     WKWebsiteDataStoreSetStatisticsVeryPrevalentResource(dataStore, host, value, &context, resourceStatisticsVoidResultCallback);
3139     runUntil(context.done, noTimeout);
3140     m_currentInvocation->didSetVeryPrevalentResource();
3141 }
3142     
3143 String TestController::dumpResourceLoadStatistics()
3144 {
3145     auto* dataStore = WKContextGetWebsiteDataStore(platformContext());
3146     ResourceStatisticsCallbackContext context(*this);
3147     WKWebsiteDataStoreDumpResourceLoadStatistics(dataStore, &context, resourceStatisticsStringResultCallback);
3148     runUntil(context.done, noTimeout);
3149     return toWTFString(context.resourceLoadStatisticsRepresentation.get());
3150 }
3151
3152 bool TestController::isStatisticsPrevalentResource(WKStringRef host)
3153 {
3154     auto* dataStore = WKContextGetWebsiteDataStore(platformContext());
3155     ResourceStatisticsCallbackContext context(*this);
3156     WKWebsiteDataStoreIsStatisticsPrevalentResource(dataStore, host, &context, resourceStatisticsBooleanResultCallback);
3157     runUntil(context.done, noTimeout);
3158     return context.result;
3159 }
3160
3161 bool TestController::isStatisticsVeryPrevalentResource(WKStringRef host)
3162 {
3163     auto* dataStore = WKContextGetWebsiteDataStore(platformContext());
3164     ResourceStatisticsCallbackContext context(*this);
3165     WKWebsiteDataStoreIsStatisticsVeryPrevalentResource(dataStore, host, &context, resourceStatisticsBooleanResultCallback);
3166     runUntil(context.done, noTimeout);
3167     return context.result;
3168 }
3169
3170 bool TestController::isStatisticsRegisteredAsSubresourceUnder(WKStringRef subresourceHost, WKStringRef topFrameHost)
3171 {
3172     auto* dataStore = WKContextGetWebsiteDataStore(platformContext());
3173     ResourceStatisticsCallbackContext context(*this);
3174     WKWebsiteDataStoreIsStatisticsRegisteredAsSubresourceUnder(dataStore, subresourceHost, topFrameHost, &context, resourceStatisticsBooleanResultCallback);
3175     runUntil(context.done, noTimeout);
3176     return context.result;
3177 }
3178
3179 bool TestController::isStatisticsRegisteredAsSubFrameUnder(WKStringRef subFrameHost, WKStringRef topFrameHost)
3180 {
3181     auto* dataStore = WKContextGetWebsiteDataStore(platformContext());
3182     ResourceStatisticsCallbackContext context(*this);
3183     WKWebsiteDataStoreIsStatisticsRegisteredAsSubFrameUnder(dataStore, subFrameHost, topFrameHost, &context, resourceStatisticsBooleanResultCallback);
3184     runUntil(context.done, noTimeout);
3185     return context.result;
3186 }
3187
3188 bool TestController::isStatisticsRegisteredAsRedirectingTo(WKStringRef hostRedirectedFrom, WKStringRef hostRedirectedTo)
3189 {
3190     auto* dataStore = WKContextGetWebsiteDataStore(platformContext());
3191     ResourceStatisticsCallbackContext context(*this);
3192     WKWebsiteDataStoreIsStatisticsRegisteredAsRedirectingTo(dataStore, hostRedirectedFrom, hostRedirectedTo, &context, resourceStatisticsBooleanResultCallback);
3193     runUntil(context.done, noTimeout);
3194     return context.result;
3195 }
3196
3197 void TestController::setStatisticsHasHadUserInteraction(WKStringRef host, bool value)
3198 {
3199     auto* dataStore = WKContextGetWebsiteDataStore(platformContext());
3200     ResourceStatisticsCallbackContext context(*this);
3201     WKWebsiteDataStoreSetStatisticsHasHadUserInteraction(dataStore, host, value, &context, resourceStatisticsVoidResultCallback);
3202     runUntil(context.done, noTimeout);
3203     m_currentInvocation->didSetHasHadUserInteraction();
3204 }
3205
3206 bool TestController::isStatisticsHasHadUserInteraction(WKStringRef host)
3207 {
3208     auto* dataStore = WKContextGetWebsiteDataStore(platformContext());
3209     ResourceStatisticsCallbackContext context(*this);
3210     WKWebsiteDataStoreIsStatisticsHasHadUserInteraction(dataStore, host, &context, resourceStatisticsBooleanResultCallback);
3211     runUntil(context.done, noTimeout);
3212     return context.result;
3213 }
3214
3215 void TestController::setStatisticsGrandfathered(WKStringRef host, bool value)
3216 {
3217     auto* dataStore = WKContextGetWebsiteDataStore(platformContext());
3218     WKWebsiteDataStoreSetStatisticsGrandfathered(dataStore, host, value);
3219 }
3220
3221 bool TestController::isStatisticsGrandfathered(WKStringRef host)
3222 {
3223     auto* dataStore = WKContextGetWebsiteDataStore(platformContext());
3224     ResourceStatisticsCallbackContext context(*this);
3225     WKWebsiteDataStoreIsStatisticsGrandfathered(dataStore, host, &context, resourceStatisticsBooleanResultCallback);
3226     runUntil(context.done, noTimeout);
3227     return context.result;
3228 }
3229
3230 void TestController::setStatisticsSubframeUnderTopFrameOrigin(WKStringRef host, WKStringRef topFrameHost)
3231 {
3232     auto* dataStore = WKContextGetWebsiteDataStore(platformContext());
3233     WKWebsiteDataStoreSetStatisticsSubframeUnderTopFrameOrigin(dataStore, host, topFrameHost);
3234 }
3235
3236 void TestController::setStatisticsSubresourceUnderTopFrameOrigin(WKStringRef host, WKStringRef topFrameHost)
3237 {
3238     auto* dataStore = WKContextGetWebsiteDataStore(platformContext());
3239     WKWebsiteDataStoreSetStatisticsSubresourceUnderTopFrameOrigin(dataStore, host, topFrameHost);
3240 }
3241
3242 void TestController::setStatisticsSubresourceUniqueRedirectTo(WKStringRef host, WKStringRef hostRedirectedTo)
3243 {
3244     auto* dataStore = WKContextGetWebsiteDataStore(platformContext());
3245     WKWebsiteDataStoreSetStatisticsSubresourceUniqueRedirectTo(dataStore, host, hostRedirectedTo);
3246 }
3247
3248 void TestController::setStatisticsSubresourceUniqueRedirectFrom(WKStringRef host, WKStringRef hostRedirectedFrom)
3249 {
3250     auto* dataStore = WKContextGetWebsiteDataStore(platformContext());
3251     WKWebsiteDataStoreSetStatisticsSubresourceUniqueRedirectFrom(dataStore, host, hostRedirectedFrom);
3252 }
3253
3254 void TestController::setStatisticsTopFrameUniqueRedirectTo(WKStringRef host, WKStringRef hostRedirectedTo)
3255 {
3256     auto* dataStore = WKContextGetWebsiteDataStore(platformContext());
3257     WKWebsiteDataStoreSetStatisticsTopFrameUniqueRedirectTo(dataStore, host, hostRedirectedTo);
3258 }
3259
3260 void TestController::setStatisticsTopFrameUniqueRedirectFrom(WKStringRef host, WKStringRef hostRedirectedFrom)
3261 {
3262     auto* dataStore = WKContextGetWebsiteDataStore(platformContext());
3263     WKWebsiteDataStoreSetStatisticsTopFrameUniqueRedirectFrom(dataStore, host, hostRedirectedFrom);
3264 }
3265
3266 void TestController::setStatisticsCrossSiteLoadWithLinkDecoration(WKStringRef fromHost, WKStringRef toHost)
3267 {
3268     auto* dataStore = WKContextGetWebsiteDataStore(platformContext());
3269     ResourceStatisticsCallbackContext context(*this);
3270     WKWebsiteDataStoreSetStatisticsCrossSiteLoadWithLinkDecoration(dataStore, fromHost, toHost, &context, resourceStatisticsVoidResultCallback);
3271     runUntil(context.done, noTimeout);
3272 }
3273
3274 void TestController::setStatisticsTimeToLiveUserInteraction(double seconds)
3275 {
3276     auto* dataStore = WKContextGetWebsiteDataStore(platformContext());
3277     ResourceStatisticsCallbackContext context(*this);
3278     WKWebsiteDataStoreSetStatisticsTimeToLiveUserInteraction(dataStore, seconds, &context, resourceStatisticsVoidResultCallback);
3279     runUntil(context.done, noTimeout);
3280 }
3281
3282 void TestController::statisticsProcessStatisticsAndDataRecords()
3283 {
3284     auto* dataStore = WKContextGetWebsiteDataStore(platformContext());
3285     ResourceStatisticsCallbackContext context(*this);
3286     WKWebsiteDataStoreStatisticsProcessStatisticsAndDataRecords(dataStore, &context, resourceStatisticsVoidResultCallback);
3287     runUntil(context.done, noTimeout);
3288 }
3289
3290 void TestController::statisticsUpdateCookieBlocking()
3291 {
3292     auto* dataStore = WKContextGetWebsiteDataStore(platformContext());
3293     ResourceStatisticsCallbackContext context(*this);
3294     WKWebsiteDataStoreStatisticsUpdateCookieBlocking(dataStore, &context, resourceStatisticsVoidResultCallback);
3295     runUntil(context.done, noTimeout);
3296     m_currentInvocation->didSetBlockCookiesForHost();
3297 }
3298
3299 void TestController::statisticsSubmitTelemetry()
3300 {
3301     auto* dataStore = WKContextGetWebsiteDataStore(platformContext());
3302     WKWebsiteDataStoreStatisticsSubmitTelemetry(dataStore);
3303 }
3304
3305 void TestController::setStatisticsNotifyPagesWhenDataRecordsWereScanned(bool value)
3306 {
3307     auto* dataStore = WKContextGetWebsiteDataStore(platformContext());
3308     WKWebsiteDataStoreSetStatisticsNotifyPagesWhenDataRecordsWereScanned(dataStore, value);
3309 }
3310
3311 void TestController::setStatisticsIsRunningTest(bool value)
3312 {
3313     auto* dataStore = WKContextGetWebsiteDataStore(platformContext());
3314     ResourceStatisticsCallbackContext context(*this);
3315     WKWebsiteDataStoreSetStatisticsIsRunningTest(dataStore, value, &context, resourceStatisticsVoidResultCallback);
3316     runUntil(context.done, noTimeout);
3317 }
3318
3319 void TestController::setStatisticsShouldClassifyResourcesBeforeDataRecordsRemoval(bool value)
3320 {
3321     auto* dataStore = WKContextGetWebsiteDataStore(platformContext());
3322     WKWebsiteDataStoreSetStatisticsShouldClassifyResourcesBeforeDataRecordsRemoval(dataStore, value);
3323 }
3324
3325 void TestController::setStatisticsNotifyPagesWhenTelemetryWasCaptured(bool value)
3326 {
3327     auto* dataStore = WKContextGetWebsiteDataStore(platformContext());
3328     WKWebsiteDataStoreSetStatisticsNotifyPagesWhenTelemetryWasCaptured(dataStore, value);
3329 }
3330
3331 void TestController::setStatisticsMinimumTimeBetweenDataRecordsRemoval(double seconds)
3332 {
3333     auto* dataStore = WKContextGetWebsiteDataStore(platformContext());
3334     WKWebsiteDataStoreSetStatisticsMinimumTimeBetweenDataRecordsRemoval(dataStore, seconds);
3335 }
3336
3337 void TestController::setStatisticsGrandfatheringTime(double seconds)
3338 {
3339     auto* dataStore = WKContextGetWebsiteDataStore(platformContext());
3340     WKWebsiteDataStoreSetStatisticsGrandfatheringTime(dataStore, seconds);
3341 }
3342
3343 void TestController::setStatisticsMaxStatisticsEntries(unsigned entries)
3344 {
3345     auto* dataStore = WKContextGetWebsiteDataStore(platformContext());
3346     WKWebsiteDataStoreSetStatisticsMaxStatisticsEntries(dataStore, entries);
3347 }
3348
3349 void TestController::setStatisticsPruneEntriesDownTo(unsigned entries)
3350 {
3351     auto* dataStore = WKContextGetWebsiteDataStore(platformContext());
3352     WKWebsiteDataStoreSetStatisticsPruneEntriesDownTo(dataStore, entries);
3353 }
3354
3355 void TestController::statisticsClearInMemoryAndPersistentStore()
3356 {
3357     auto* dataStore = WKContextGetWebsiteDataStore(platformContext());
3358     ResourceStatisticsCallbackContext context(*this);
3359     WKWebsiteDataStoreStatisticsClearInMemoryAndPersistentStore(dataStore, &context, resourceStatisticsVoidResultCallback);
3360     runUntil(context.done, noTimeout);
3361     m_currentInvocation->didClearStatisticsThroughWebsiteDataRemoval();
3362 }
3363
3364 void TestController::statisticsClearInMemoryAndPersistentStoreModifiedSinceHours(unsigned hours)
3365 {
3366     auto* dataStore = WKContextGetWebsiteDataStore(platformContext());
3367     ResourceStatisticsCallbackContext context(*this);
3368     WKWebsiteDataStoreStatisticsClearInMemoryAndPersistentStoreModifiedSinceHours(dataStore, hours, &context, resourceStatisticsVoidResultCallback);
3369     runUntil(context.done, noTimeout);
3370     m_currentInvocation->didClearStatisticsThroughWebsiteDataRemoval();
3371 }
3372
3373 void TestController::statisticsClearThroughWebsiteDataRemoval()
3374 {
3375     auto* dataStore = WKContextGetWebsiteDataStore(platformContext());
3376     ResourceStatisticsCallbackContext context(*this);
3377     WKWebsiteDataStoreStatisticsClearThroughWebsiteDataRemoval(dataStore, &context, resourceStatisticsVoidResultCallback);
3378     runUntil(context.done, noTimeout);
3379     m_currentInvocation->didClearStatisticsThroughWebsiteDataRemoval();
3380 }
3381
3382 void TestController::statisticsDeleteCookiesForHost(WKStringRef host, bool includeHttpOnlyCookies)
3383 {
3384     auto* dataStore = WKContextGetWebsiteDataStore(platformContext());
3385     ResourceStatisticsCallbackContext context(*this);
3386     WKWebsiteDataStoreStatisticsDeleteCookiesForTesting(dataStore, host, includeHttpOnlyCookies, &context, resourceStatisticsVoidResultCallback);
3387     runUntil(context.done, noTimeout);
3388 }
3389
3390 void TestController::setStatisticsCacheMaxAgeCap(double seconds)
3391 {
3392     auto* dataStore = WKContextGetWebsiteDataStore(platformContext());
3393     ResourceStatisticsCallbackContext context(*this);
3394     WKWebsiteDataStoreSetStatisticsCacheMaxAgeCap(dataStore, seconds, &context, resourceStatisticsVoidResultCallback);
3395     runUntil(context.done, noTimeout);
3396 }
3397
3398 void TestController::statisticsResetToConsistentState()
3399 {
3400     auto* dataStore = WKContextGetWebsiteDataStore(platformContext());
3401     ResourceStatisticsCallbackContext context(*this);
3402     WKWebsiteDataStoreStatisticsResetToConsistentState(dataStore, &context, resourceStatisticsVoidResultCallback);
3403     runUntil(context.done, noTimeout);
3404     m_currentInvocation->didResetStatisticsToConsistentState();
3405 }
3406
3407 void TestController::addMockMediaDevice(WKStringRef persistentID, WKStringRef label, WKStringRef type)
3408 {
3409     WKAddMockMediaDevice(platformContext(), persistentID, label, type);
3410 }
3411
3412 void TestController::clearMockMediaDevices()
3413 {
3414     WKClearMockMediaDevices(platformContext());
3415 }
3416
3417 void TestController::removeMockMediaDevice(WKStringRef persistentID)
3418 {
3419     WKRemoveMockMediaDevice(platformContext(), persistentID);
3420 }
3421
3422 void TestController::resetMockMediaDevices()
3423 {
3424     WKResetMockMediaDevices(platformContext());
3425 }
3426
3427 #if !PLATFORM(COCOA)
3428 void TestController::platformAddTestOptions(TestOptions&) const
3429 {
3430 }
3431
3432 void TestController::injectUserScript(WKStringRef)
3433 {
3434 }
3435
3436 void TestController::addTestKeyToKeychain(const String&, const String&, const String&)
3437 {
3438 }
3439
3440 void TestController::cleanUpKeychain(const String&)
3441 {
3442 }
3443
3444 bool TestController::keyExistsInKeychain(const String&, const String&)
3445 {
3446     return false;
3447 }
3448
3449 bool TestController::canDoServerTrustEvaluationInNetworkProcess() const
3450 {
3451     return false;
3452 }
3453
3454 #endif
3455
3456 void TestController::sendDisplayConfigurationChangedMessageForTesting()
3457 {
3458     WKSendDisplayConfigurationChangedMessageForTesting(platformContext());
3459 }
3460
3461 void TestController::setWebAuthenticationMockConfiguration(WKDictionaryRef configuration)
3462 {
3463     WKWebsiteDataStoreSetWebAuthenticationMockConfiguration(WKContextGetWebsiteDataStore(platformContext()), configuration);
3464 }
3465
3466 struct AdClickAttributionStringResultCallbackContext {
3467     explicit AdClickAttributionStringResultCallbackContext(TestController& controller)
3468         : testController(controller)
3469     {
3470     }
3471     
3472     TestController& testController;
3473     bool done { false };
3474     WKRetainPtr<WKStringRef> adClickAttributionRepresentation;
3475 };
3476
3477 static void adClickAttributionStringResultCallback(WKStringRef adClickAttributionRepresentation, void* userData)
3478 {
3479     auto* context = static_cast<AdClickAttributionStringResultCallbackContext*>(userData);
3480     context->adClickAttributionRepresentation = adClickAttributionRepresentation;
3481     context->done = true;
3482     context->testController.notifyDone();
3483 }
3484
3485 String TestController::dumpAdClickAttribution()
3486 {
3487     AdClickAttributionStringResultCallbackContext callbackContext(*this);
3488     WKPageDumpAdClickAttribution(m_mainWebView->page(), adClickAttributionStringResultCallback, &callbackContext);
3489     runUntil(callbackContext.done, noTimeout);
3490     return toWTFString(callbackContext.adClickAttributionRepresentation.get());
3491 }
3492
3493 struct AdClickAttributionVoidCallbackContext {
3494     explicit AdClickAttributionVoidCallbackContext(TestController& controller)
3495         : testController(controller)
3496     {
3497     }
3498     
3499     TestController& testController;
3500     bool done { false };
3501 };
3502
3503 static void adClickAttributionVoidCallback(void* userData)
3504 {
3505     auto* context = static_cast<AdClickAttributionVoidCallbackContext*>(userData);
3506     context->done = true;
3507     context->testController.notifyDone();
3508 }
3509
3510 void TestController::clearAdClickAttribution()
3511 {
3512     AdClickAttributionVoidCallbackContext callbackContext(*this);
3513     WKPageClearAdClickAttribution(m_mainWebView->page(), adClickAttributionVoidCallback, &callbackContext);
3514     runUntil(callbackContext.done, noTimeout);
3515 }
3516
3517 } // namespace WTR