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