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