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