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