90df04a70c6cb46d7dc70faa503eff44cc6cfb8e
[WebKit-https.git] / Tools / WebKitTestRunner / TestController.cpp
1 /*
2  * Copyright (C) 2010, 2014-2017 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 <WebKit/WKArray.h>
36 #include <WebKit/WKAuthenticationChallenge.h>
37 #include <WebKit/WKAuthenticationDecisionListener.h>
38 #include <WebKit/WKContextConfigurationRef.h>
39 #include <WebKit/WKContextPrivate.h>
40 #include <WebKit/WKCookieManager.h>
41 #include <WebKit/WKCredential.h>
42 #include <WebKit/WKFrameHandleRef.h>
43 #include <WebKit/WKFrameInfoRef.h>
44 #include <WebKit/WKIconDatabase.h>
45 #include <WebKit/WKNavigationResponseRef.h>
46 #include <WebKit/WKNotification.h>
47 #include <WebKit/WKNotificationManager.h>
48 #include <WebKit/WKNotificationPermissionRequest.h>
49 #include <WebKit/WKNumber.h>
50 #include <WebKit/WKOpenPanelResultListener.h>
51 #include <WebKit/WKPageGroup.h>
52 #include <WebKit/WKPageInjectedBundleClient.h>
53 #include <WebKit/WKPagePrivate.h>
54 #include <WebKit/WKPluginInformation.h>
55 #include <WebKit/WKPreferencesRefPrivate.h>
56 #include <WebKit/WKProtectionSpace.h>
57 #include <WebKit/WKRetainPtr.h>
58 #include <WebKit/WKSecurityOriginRef.h>
59 #include <WebKit/WKTextChecker.h>
60 #include <WebKit/WKUserMediaPermissionCheck.h>
61 #include <WebKit/WKWebsiteDataStoreRef.h>
62 #include <algorithm>
63 #include <cstdio>
64 #include <ctype.h>
65 #include <fstream>
66 #include <runtime/InitializeThreading.h>
67 #include <stdlib.h>
68 #include <string>
69 #include <unistd.h>
70 #include <wtf/AutodrainedPool.h>
71 #include <wtf/CryptographicallyRandomNumber.h>
72 #include <wtf/HexNumber.h>
73 #include <wtf/MainThread.h>
74 #include <wtf/RefCounted.h>
75 #include <wtf/RunLoop.h>
76 #include <wtf/SetForScope.h>
77 #include <wtf/UUID.h>
78 #include <wtf/text/CString.h>
79 #include <wtf/text/WTFString.h>
80
81 #if PLATFORM(COCOA)
82 #include <WebKit/WKContextPrivateMac.h>
83 #include <WebKit/WKPagePrivateMac.h>
84 #endif
85
86 namespace WTR {
87
88 const unsigned TestController::viewWidth = 800;
89 const unsigned TestController::viewHeight = 600;
90
91 const unsigned TestController::w3cSVGViewWidth = 480;
92 const unsigned TestController::w3cSVGViewHeight = 360;
93
94 const double TestController::defaultShortTimeout = 5.0;
95
96 const double TestController::noTimeout = -1;
97
98 static WKURLRef blankURL()
99 {
100     static WKURLRef staticBlankURL = WKURLCreateWithUTF8CString("about:blank");
101     return staticBlankURL;
102 }
103
104 static WKDataRef copyWebCryptoMasterKey(WKPageRef, const void*)
105 {
106     // Any 128 bit key would do, all we need for testing is to implement the callback.
107     return WKDataCreate((const uint8_t*)"\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f", 16);
108 }
109
110 static TestController* controller;
111
112 TestController& TestController::singleton()
113 {
114     ASSERT(controller);
115     return *controller;
116 }
117
118 TestController::TestController(int argc, const char* argv[])
119 {
120     initialize(argc, argv);
121     controller = this;
122     run();
123     controller = 0;
124 }
125
126 TestController::~TestController()
127 {
128     // The context will be null if WebKitTestRunner was in server mode, but ran no tests.
129     if (m_context)
130         WKIconDatabaseClose(WKContextGetIconDatabase(m_context.get()));
131
132     platformDestroy();
133 }
134
135 static WKRect getWindowFrame(WKPageRef page, const void* clientInfo)
136 {
137     PlatformWebView* view = static_cast<PlatformWebView*>(const_cast<void*>(clientInfo));
138     return view->windowFrame();
139 }
140
141 static void setWindowFrame(WKPageRef page, WKRect frame, const void* clientInfo)
142 {
143     PlatformWebView* view = static_cast<PlatformWebView*>(const_cast<void*>(clientInfo));
144     view->setWindowFrame(frame);
145 }
146
147 static bool runBeforeUnloadConfirmPanel(WKPageRef page, WKStringRef message, WKFrameRef frame, const void*)
148 {
149     printf("CONFIRM NAVIGATION: %s\n", toSTD(message).c_str());
150     return TestController::singleton().beforeUnloadReturnValue();
151 }
152
153 static void runOpenPanel(WKPageRef page, WKFrameRef frame, WKOpenPanelParametersRef parameters, WKOpenPanelResultListenerRef resultListenerRef, const void*)
154 {
155     printf("OPEN FILE PANEL\n");
156     if (WKOpenPanelParametersGetAllowsDirectories(parameters))
157         printf("-> DIRECTORIES ARE ALLOWED\n");
158     WKArrayRef fileURLs = TestController::singleton().openPanelFileURLs();
159     if (!fileURLs || !WKArrayGetSize(fileURLs)) {
160         WKOpenPanelResultListenerCancel(resultListenerRef);
161         return;
162     }
163
164     if (WKOpenPanelParametersGetAllowsMultipleFiles(parameters)) {
165         WKOpenPanelResultListenerChooseFiles(resultListenerRef, fileURLs);
166         return;
167     }
168
169     WKTypeRef firstItem = WKArrayGetItemAtIndex(fileURLs, 0);
170     WKOpenPanelResultListenerChooseFiles(resultListenerRef, adoptWK(WKArrayCreate(&firstItem, 1)).get());
171 }
172
173 void TestController::runModal(WKPageRef page, const void* clientInfo)
174 {
175     PlatformWebView* view = static_cast<PlatformWebView*>(const_cast<void*>(clientInfo));
176     view->setWindowIsKey(false);
177     runModal(view);
178     view->setWindowIsKey(true);
179 }
180
181 static void closeOtherPage(WKPageRef page, const void* clientInfo)
182 {
183     WKPageClose(page);
184     PlatformWebView* view = static_cast<PlatformWebView*>(const_cast<void*>(clientInfo));
185     delete view;
186 }
187
188 static void focus(WKPageRef page, const void* clientInfo)
189 {
190     PlatformWebView* view = static_cast<PlatformWebView*>(const_cast<void*>(clientInfo));
191     view->focus();
192     view->setWindowIsKey(true);
193 }
194
195 static void unfocus(WKPageRef page, const void* clientInfo)
196 {
197     PlatformWebView* view = static_cast<PlatformWebView*>(const_cast<void*>(clientInfo));
198     view->setWindowIsKey(false);
199 }
200
201 static void decidePolicyForGeolocationPermissionRequest(WKPageRef, WKFrameRef, WKSecurityOriginRef, WKGeolocationPermissionRequestRef permissionRequest, const void* clientInfo)
202 {
203     TestController::singleton().handleGeolocationPermissionRequest(permissionRequest);
204 }
205
206 static void decidePolicyForUserMediaPermissionRequest(WKPageRef, WKFrameRef frame, WKSecurityOriginRef userMediaDocumentOrigin, WKSecurityOriginRef topLevelDocumentOrigin, WKUserMediaPermissionRequestRef permissionRequest, const void* clientInfo)
207 {
208     TestController::singleton().handleUserMediaPermissionRequest(frame, userMediaDocumentOrigin, topLevelDocumentOrigin, permissionRequest);
209 }
210
211 static void checkUserMediaPermissionForOrigin(WKPageRef, WKFrameRef frame, WKSecurityOriginRef userMediaDocumentOrigin, WKSecurityOriginRef topLevelDocumentOrigin, WKUserMediaPermissionCheckRef checkRequest, const void*)
212 {
213     TestController::singleton().handleCheckOfUserMediaPermissionForOrigin(frame, userMediaDocumentOrigin, topLevelDocumentOrigin, checkRequest);
214 }
215
216 static void requestPointerLock(WKPageRef page, const void*)
217 {
218     WKPageDidAllowPointerLock(page);
219 }
220
221 WKPageRef TestController::createOtherPage(WKPageRef oldPage, WKPageConfigurationRef configuration, WKNavigationActionRef navigationAction, WKWindowFeaturesRef windowFeatures, const void *clientInfo)
222 {
223     PlatformWebView* parentView = static_cast<PlatformWebView*>(const_cast<void*>(clientInfo));
224
225     PlatformWebView* view = platformCreateOtherPage(parentView, configuration, parentView->options());
226     WKPageRef newPage = view->page();
227
228     view->resizeTo(800, 600);
229
230     WKPageUIClientV8 otherPageUIClient = {
231         { 8, view },
232         0, // createNewPage_deprecatedForUseWithV0
233         0, // showPage
234         closeOtherPage,
235         0, // takeFocus
236         focus,
237         unfocus,
238         0, // runJavaScriptAlert_deprecatedForUseWithV0
239         0, // runJavaScriptAlert_deprecatedForUseWithV0
240         0, // runJavaScriptAlert_deprecatedForUseWithV0
241         0, // setStatusText
242         0, // mouseDidMoveOverElement_deprecatedForUseWithV0
243         0, // missingPluginButtonClicked
244         0, // didNotHandleKeyEvent
245         0, // didNotHandleWheelEvent
246         0, // toolbarsAreVisible
247         0, // setToolbarsAreVisible
248         0, // menuBarIsVisible
249         0, // setMenuBarIsVisible
250         0, // statusBarIsVisible
251         0, // setStatusBarIsVisible
252         0, // isResizable
253         0, // setIsResizable
254         getWindowFrame,
255         setWindowFrame,
256         runBeforeUnloadConfirmPanel,
257         0, // didDraw
258         0, // pageDidScroll
259         0, // exceededDatabaseQuota
260         runOpenPanel,
261         decidePolicyForGeolocationPermissionRequest,
262         0, // headerHeight
263         0, // footerHeight
264         0, // drawHeader
265         0, // drawFooter
266         0, // printFrame
267         runModal,
268         0, // didCompleteRubberBandForMainFrame
269         0, // saveDataToFileInDownloadsFolder
270         0, // shouldInterruptJavaScript
271         0, // createNewPage_deprecatedForUseWithV1
272         0, // mouseDidMoveOverElement
273         0, // decidePolicyForNotificationPermissionRequest
274         0, // unavailablePluginButtonClicked_deprecatedForUseWithV1
275         0, // showColorPicker
276         0, // hideColorPicker
277         0, // unavailablePluginButtonClicked
278         0, // pinnedStateDidChange
279         0, // didBeginTrackingPotentialLongMousePress
280         0, // didRecognizeLongMousePress
281         0, // didCancelTrackingPotentialLongMousePress
282         0, // isPlayingAudioDidChange
283         decidePolicyForUserMediaPermissionRequest,
284         0, // didClickAutofillButton
285         0, // runJavaScriptAlert
286         0, // runJavaScriptConfirm
287         0, // runJavaScriptPrompt
288         0, // mediaSessionMetadataDidChange
289         createOtherPage,
290         0, // runJavaScriptAlert
291         0, // runJavaScriptConfirm
292         0, // runJavaScriptPrompt
293         checkUserMediaPermissionForOrigin,
294         0, // runBeforeUnloadConfirmPanel
295         0, // fullscreenMayReturnToInline
296         requestPointerLock,
297         0,
298     };
299     WKPageSetPageUIClient(newPage, &otherPageUIClient.base);
300     
301     WKPageNavigationClientV0 pageNavigationClient = {
302         { 0, &TestController::singleton() },
303         decidePolicyForNavigationAction,
304         decidePolicyForNavigationResponse,
305         decidePolicyForPluginLoad,
306         0, // didStartProvisionalNavigation
307         0, // didReceiveServerRedirectForProvisionalNavigation
308         0, // didFailProvisionalNavigation
309         0, // didCommitNavigation
310         0, // didFinishNavigation
311         0, // didFailNavigation
312         0, // didFailProvisionalLoadInSubframe
313         0, // didFinishDocumentLoad
314         0, // didSameDocumentNavigation
315         0, // renderingProgressDidChange
316         canAuthenticateAgainstProtectionSpace,
317         didReceiveAuthenticationChallenge,
318         processDidCrash,
319         copyWebCryptoMasterKey,
320         didBeginNavigationGesture,
321         willEndNavigationGesture,
322         didEndNavigationGesture,
323         didRemoveNavigationGestureSnapshot
324     };
325     WKPageSetPageNavigationClient(newPage, &pageNavigationClient.base);
326
327     view->didInitializeClients();
328
329     TestController::singleton().updateWindowScaleForTest(view, *TestController::singleton().m_currentInvocation);
330
331     WKRetain(newPage);
332     return newPage;
333 }
334
335 const char* TestController::libraryPathForTesting()
336 {
337     // FIXME: This may not be sufficient to prevent interactions/crashes
338     // when running more than one copy of DumpRenderTree.
339     // See https://bugs.webkit.org/show_bug.cgi?id=10906
340     char* dumpRenderTreeTemp = getenv("DUMPRENDERTREE_TEMP");
341     if (dumpRenderTreeTemp)
342         return dumpRenderTreeTemp;
343     return platformLibraryPathForTesting();
344 }
345
346 void TestController::initialize(int argc, const char* argv[])
347 {
348     JSC::initializeThreading();
349     RunLoop::initializeMainRunLoop();
350
351     platformInitialize();
352
353     Options options;
354     OptionsHandler optionsHandler(options);
355
356     if (argc < 2) {
357         optionsHandler.printHelp();
358         exit(1);
359     }
360     if (!optionsHandler.parse(argc, argv))
361         exit(1);
362
363     m_useWaitToDumpWatchdogTimer = options.useWaitToDumpWatchdogTimer;
364     m_forceNoTimeout = options.forceNoTimeout;
365     m_verbose = options.verbose;
366     m_gcBetweenTests = options.gcBetweenTests;
367     m_shouldDumpPixelsForAllTests = options.shouldDumpPixelsForAllTests;
368     m_forceComplexText = options.forceComplexText;
369     m_shouldUseAcceleratedDrawing = options.shouldUseAcceleratedDrawing;
370     m_shouldUseRemoteLayerTree = options.shouldUseRemoteLayerTree;
371     m_paths = options.paths;
372     m_allowedHosts = options.allowedHosts;
373     m_shouldShowWebView = options.shouldShowWebView;
374     m_shouldShowTouches = options.shouldShowTouches;
375
376     if (options.printSupportedFeatures) {
377         // FIXME: On Windows, DumpRenderTree uses this to expose whether it supports 3d
378         // transforms and accelerated compositing. When we support those features, we
379         // should match DRT's behavior.
380         exit(0);
381     }
382
383     m_usingServerMode = (m_paths.size() == 1 && m_paths[0] == "-");
384     if (m_usingServerMode)
385         m_printSeparators = true;
386     else
387         m_printSeparators = m_paths.size() > 1;
388
389     initializeInjectedBundlePath();
390     initializeTestPluginDirectory();
391
392 #if PLATFORM(MAC)
393     WebCoreTestSupport::installMockGamepadProvider();
394 #endif
395
396     WKRetainPtr<WKStringRef> pageGroupIdentifier(AdoptWK, WKStringCreateWithUTF8CString("WebKitTestRunnerPageGroup"));
397     m_pageGroup.adopt(WKPageGroupCreateWithIdentifier(pageGroupIdentifier.get()));
398 }
399
400 WKRetainPtr<WKContextConfigurationRef> TestController::generateContextConfiguration() const
401 {
402     auto configuration = adoptWK(WKContextConfigurationCreate());
403     WKContextConfigurationSetInjectedBundlePath(configuration.get(), injectedBundlePath());
404     WKContextConfigurationSetFullySynchronousModeIsAllowedForTesting(configuration.get(), true);
405
406     if (const char* dumpRenderTreeTemp = libraryPathForTesting()) {
407         String temporaryFolder = String::fromUTF8(dumpRenderTreeTemp);
408
409         const char separator = '/';
410
411         WKContextConfigurationSetApplicationCacheDirectory(configuration.get(), toWK(temporaryFolder + separator + "ApplicationCache").get());
412         WKContextConfigurationSetDiskCacheDirectory(configuration.get(), toWK(temporaryFolder + separator + "Cache").get());
413         WKContextConfigurationSetIndexedDBDatabaseDirectory(configuration.get(), toWK(temporaryFolder + separator + "Databases" + separator + "IndexedDB").get());
414         WKContextConfigurationSetLocalStorageDirectory(configuration.get(), toWK(temporaryFolder + separator + "LocalStorage").get());
415         WKContextConfigurationSetWebSQLDatabaseDirectory(configuration.get(), toWK(temporaryFolder + separator + "Databases" + separator + "WebSQL").get());
416         WKContextConfigurationSetMediaKeysStorageDirectory(configuration.get(), toWK(temporaryFolder + separator + "MediaKeys").get());
417         WKContextConfigurationSetResourceLoadStatisticsDirectory(configuration.get(), toWK(temporaryFolder + separator + "ResourceLoadStatistics").get());
418     }
419     return configuration;
420 }
421
422 WKRetainPtr<WKPageConfigurationRef> TestController::generatePageConfiguration(WKContextConfigurationRef configuration)
423 {
424     m_context = platformAdjustContext(adoptWK(WKContextCreateWithConfiguration(configuration)).get(), configuration);
425
426     m_geolocationProvider = std::make_unique<GeolocationProviderMock>(m_context.get());
427
428     if (const char* dumpRenderTreeTemp = libraryPathForTesting()) {
429         String temporaryFolder = String::fromUTF8(dumpRenderTreeTemp);
430
431         // FIXME: This should be migrated to WKContextConfigurationRef.
432         // Disable icon database to avoid fetching <http://127.0.0.1:8000/favicon.ico> and making tests flaky.
433         // Invividual tests can enable it using testRunner.setIconDatabaseEnabled, although it's not currently supported in WebKitTestRunner.
434         WKContextSetIconDatabasePath(m_context.get(), toWK(emptyString()).get());
435     }
436
437     WKContextSetDiskCacheSpeculativeValidationEnabled(m_context.get(), true);
438     WKContextUseTestingNetworkSession(m_context.get());
439     WKContextSetCacheModel(m_context.get(), kWKCacheModelDocumentBrowser);
440
441     platformInitializeContext();
442
443     WKContextInjectedBundleClientV1 injectedBundleClient = {
444         { 1, this },
445         didReceiveMessageFromInjectedBundle,
446         didReceiveSynchronousMessageFromInjectedBundle,
447         getInjectedBundleInitializationUserData,
448     };
449     WKContextSetInjectedBundleClient(m_context.get(), &injectedBundleClient.base);
450
451     WKContextClientV2 contextClient = {
452         { 2, this },
453         0, // plugInAutoStartOriginHashesChanged
454         networkProcessDidCrash,
455         0, // plugInInformationBecameAvailable
456         0, // copyWebCryptoMasterKey
457         databaseProcessDidCrash,
458     };
459     WKContextSetClient(m_context.get(), &contextClient.base);
460
461     WKContextHistoryClientV0 historyClient = {
462         { 0, this },
463         didNavigateWithNavigationData,
464         didPerformClientRedirect,
465         didPerformServerRedirect,
466         didUpdateHistoryTitle,
467         0, // populateVisitedLinks
468     };
469     WKContextSetHistoryClient(m_context.get(), &historyClient.base);
470
471     WKNotificationManagerRef notificationManager = WKContextGetNotificationManager(m_context.get());
472     WKNotificationProviderV0 notificationKit = m_webNotificationProvider.provider();
473     WKNotificationManagerSetProvider(notificationManager, &notificationKit.base);
474
475     if (testPluginDirectory())
476         WKContextSetAdditionalPluginsDirectory(m_context.get(), testPluginDirectory());
477
478     if (m_forceComplexText)
479         WKContextSetAlwaysUsesComplexTextCodePath(m_context.get(), true);
480
481     auto pageConfiguration = adoptWK(WKPageConfigurationCreate());
482     WKPageConfigurationSetContext(pageConfiguration.get(), m_context.get());
483     WKPageConfigurationSetPageGroup(pageConfiguration.get(), m_pageGroup.get());
484     WKPageConfigurationSetUserContentController(pageConfiguration.get(), adoptWK(WKUserContentControllerCreate()).get());
485     return pageConfiguration;
486 }
487
488 void TestController::createWebViewWithOptions(const TestOptions& options)
489 {
490     auto contextConfiguration = generateContextConfiguration();
491
492     WKRetainPtr<WKMutableArrayRef> overrideLanguages = adoptWK(WKMutableArrayCreate());
493     for (auto& language : options.overrideLanguages)
494         WKArrayAppendItem(overrideLanguages.get(), adoptWK(WKStringCreateWithUTF8CString(language.utf8().data())).get());
495     WKContextConfigurationSetOverrideLanguages(contextConfiguration.get(), overrideLanguages.get());
496
497     auto configuration = generatePageConfiguration(contextConfiguration.get());
498
499     // Some preferences (notably mock scroll bars setting) currently cannot be re-applied to an existing view, so we need to set them now.
500     // FIXME: Migrate these preferences to WKContextConfigurationRef.
501     resetPreferencesToConsistentValues(options);
502
503     platformCreateWebView(configuration.get(), options);
504     WKPageUIClientV8 pageUIClient = {
505         { 8, m_mainWebView.get() },
506         0, // createNewPage_deprecatedForUseWithV0
507         0, // showPage
508         0, // close
509         0, // takeFocus
510         focus,
511         unfocus,
512         0, // runJavaScriptAlert_deprecatedForUseWithV0
513         0, // runJavaScriptAlert_deprecatedForUseWithV0
514         0, // runJavaScriptAlert_deprecatedForUseWithV0
515         0, // setStatusText
516         0, // mouseDidMoveOverElement_deprecatedForUseWithV0
517         0, // missingPluginButtonClicked
518         0, // didNotHandleKeyEvent
519         0, // didNotHandleWheelEvent
520         0, // toolbarsAreVisible
521         0, // setToolbarsAreVisible
522         0, // menuBarIsVisible
523         0, // setMenuBarIsVisible
524         0, // statusBarIsVisible
525         0, // setStatusBarIsVisible
526         0, // isResizable
527         0, // setIsResizable
528         getWindowFrame,
529         setWindowFrame,
530         runBeforeUnloadConfirmPanel,
531         0, // didDraw
532         0, // pageDidScroll
533         0, // exceededDatabaseQuota,
534         runOpenPanel,
535         decidePolicyForGeolocationPermissionRequest,
536         0, // headerHeight
537         0, // footerHeight
538         0, // drawHeader
539         0, // drawFooter
540         0, // printFrame
541         runModal,
542         0, // didCompleteRubberBandForMainFrame
543         0, // saveDataToFileInDownloadsFolder
544         0, // shouldInterruptJavaScript
545         0, // createNewPage_deprecatedForUseWithV1
546         0, // mouseDidMoveOverElement
547         decidePolicyForNotificationPermissionRequest, // decidePolicyForNotificationPermissionRequest
548         0, // unavailablePluginButtonClicked_deprecatedForUseWithV1
549         0, // showColorPicker
550         0, // hideColorPicker
551         unavailablePluginButtonClicked,
552         0, // pinnedStateDidChange
553         0, // didBeginTrackingPotentialLongMousePress
554         0, // didRecognizeLongMousePress
555         0, // didCancelTrackingPotentialLongMousePress
556         0, // isPlayingAudioDidChange
557         decidePolicyForUserMediaPermissionRequest,
558         0, // didClickAutofillButton
559         0, // runJavaScriptAlert
560         0, // runJavaScriptConfirm
561         0, // runJavaScriptPrompt
562         0, // mediaSessionMetadataDidChange
563         createOtherPage,
564         0, // runJavaScriptAlert
565         0, // runJavaScriptConfirm
566         0, // runJavaScriptPrompt
567         checkUserMediaPermissionForOrigin,
568         0, // runBeforeUnloadConfirmPanel
569         0, // fullscreenMayReturnToInline
570         requestPointerLock,
571         0,
572     };
573     WKPageSetPageUIClient(m_mainWebView->page(), &pageUIClient.base);
574
575     WKPageNavigationClientV0 pageNavigationClient = {
576         { 0, this },
577         decidePolicyForNavigationAction,
578         decidePolicyForNavigationResponse,
579         decidePolicyForPluginLoad,
580         0, // didStartProvisionalNavigation
581         0, // didReceiveServerRedirectForProvisionalNavigation
582         0, // didFailProvisionalNavigation
583         didCommitNavigation,
584         didFinishNavigation,
585         0, // didFailNavigation
586         0, // didFailProvisionalLoadInSubframe
587         0, // didFinishDocumentLoad
588         0, // didSameDocumentNavigation
589         0, // renderingProgressDidChange
590         canAuthenticateAgainstProtectionSpace,
591         didReceiveAuthenticationChallenge,
592         processDidCrash,
593         copyWebCryptoMasterKey,
594         didBeginNavigationGesture,
595         willEndNavigationGesture,
596         didEndNavigationGesture,
597         didRemoveNavigationGestureSnapshot
598     };
599     WKPageSetPageNavigationClient(m_mainWebView->page(), &pageNavigationClient.base);
600
601     WKContextDownloadClientV1 downloadClient = {
602         { 1, this },
603         downloadDidStart,
604         0, // didReceiveAuthenticationChallenge
605         0, // didReceiveResponse
606         0, // didReceiveData
607         0, // shouldDecodeSourceDataOfMIMEType
608         decideDestinationWithSuggestedFilename,
609         0, // didCreateDestination
610         downloadDidFinish,
611         downloadDidFail,
612         downloadDidCancel,
613         0, // processDidCrash;
614         downloadDidReceiveServerRedirectToURL
615     };
616     WKContextSetDownloadClient(context(), &downloadClient.base);
617     
618     // this should just be done on the page?
619     WKPageInjectedBundleClientV0 injectedBundleClient = {
620         { 0, this },
621         didReceivePageMessageFromInjectedBundle,
622         didReceiveSynchronousPageMessageFromInjectedBundle
623     };
624     WKPageSetPageInjectedBundleClient(m_mainWebView->page(), &injectedBundleClient.base);
625
626     m_mainWebView->didInitializeClients();
627
628     // Generally, the tests should default to running at 1x. updateWindowScaleForTest() will adjust the scale to
629     // something else for specific tests that need to run at a different window scale.
630     m_mainWebView->changeWindowScaleIfNeeded(1);
631 }
632
633 void TestController::ensureViewSupportsOptionsForTest(const TestInvocation& test)
634 {
635     auto options = test.options();
636
637     if (m_mainWebView) {
638         if (m_mainWebView->viewSupportsOptions(options))
639             return;
640
641         WKPageSetPageUIClient(m_mainWebView->page(), nullptr);
642         WKPageSetPageNavigationClient(m_mainWebView->page(), nullptr);
643         WKPageClose(m_mainWebView->page());
644
645         m_mainWebView = nullptr;
646     }
647
648     createWebViewWithOptions(options);
649
650     if (!resetStateToConsistentValues(options))
651         TestInvocation::dumpWebProcessUnresponsiveness("<unknown> - TestController::run - Failed to reset state to consistent values\n");
652 }
653
654 void TestController::resetPreferencesToConsistentValues(const TestOptions& options)
655 {
656     // Reset preferences
657     WKPreferencesRef preferences = platformPreferences();
658     WKPreferencesResetTestRunnerOverrides(preferences);
659     WKPreferencesEnableAllExperimentalFeatures(preferences);
660     WKPreferencesSetPageVisibilityBasedProcessSuppressionEnabled(preferences, false);
661     WKPreferencesSetOfflineWebApplicationCacheEnabled(preferences, true);
662     WKPreferencesSetFontSmoothingLevel(preferences, kWKFontSmoothingLevelNoSubpixelAntiAliasing);
663     WKPreferencesSetSubpixelAntialiasedLayerTextEnabled(preferences, false);
664     WKPreferencesSetXSSAuditorEnabled(preferences, false);
665     WKPreferencesSetWebAudioEnabled(preferences, true);
666     WKPreferencesSetMediaDevicesEnabled(preferences, true);
667     WKPreferencesSetWebRTCLegacyAPIEnabled(preferences, true);
668     WKPreferencesSetDeveloperExtrasEnabled(preferences, true);
669     WKPreferencesSetJavaScriptRuntimeFlags(preferences, kWKJavaScriptRuntimeFlagsAllEnabled);
670     WKPreferencesSetJavaScriptCanOpenWindowsAutomatically(preferences, true);
671     WKPreferencesSetJavaScriptCanAccessClipboard(preferences, true);
672     WKPreferencesSetDOMPasteAllowed(preferences, true);
673     WKPreferencesSetUniversalAccessFromFileURLsAllowed(preferences, true);
674     WKPreferencesSetFileAccessFromFileURLsAllowed(preferences, true);
675 #if ENABLE(FULLSCREEN_API)
676     WKPreferencesSetFullScreenEnabled(preferences, true);
677 #endif
678     WKPreferencesSetPageCacheEnabled(preferences, false);
679     WKPreferencesSetAsynchronousPluginInitializationEnabled(preferences, false);
680     WKPreferencesSetAsynchronousPluginInitializationEnabledForAllPlugins(preferences, false);
681     WKPreferencesSetArtificialPluginInitializationDelayEnabled(preferences, false);
682     WKPreferencesSetTabToLinksEnabled(preferences, false);
683     WKPreferencesSetInteractiveFormValidationEnabled(preferences, true);
684     WKPreferencesSetDisplayContentsEnabled(preferences, true);
685     WKPreferencesSetDataTransferItemsEnabled(preferences, true);
686     WKPreferencesSetCustomPasteboardDataEnabled(preferences, true);
687
688     WKPreferencesSetMockScrollbarsEnabled(preferences, options.useMockScrollbars);
689     WKPreferencesSetNeedsSiteSpecificQuirks(preferences, options.needsSiteSpecificQuirks);
690     WKPreferencesSetAttachmentElementEnabled(preferences, options.enableAttachmentElement);
691     WKPreferencesSetIntersectionObserverEnabled(preferences, options.enableIntersectionObserver);
692     WKPreferencesSetModernMediaControlsEnabled(preferences, options.enableModernMediaControls);
693     WKPreferencesSetCredentialManagementEnabled(preferences, options.enableCredentialManagement);
694     WKPreferencesSetIsSecureContextAttributeEnabled(preferences, options.enableIsSecureContextAttribute);
695
696     static WKStringRef defaultTextEncoding = WKStringCreateWithUTF8CString("ISO-8859-1");
697     WKPreferencesSetDefaultTextEncodingName(preferences, defaultTextEncoding);
698
699     static WKStringRef standardFontFamily = WKStringCreateWithUTF8CString("Times");
700     static WKStringRef cursiveFontFamily = WKStringCreateWithUTF8CString("Apple Chancery");
701     static WKStringRef fantasyFontFamily = WKStringCreateWithUTF8CString("Papyrus");
702     static WKStringRef fixedFontFamily = WKStringCreateWithUTF8CString("Courier");
703     static WKStringRef pictographFontFamily = WKStringCreateWithUTF8CString("Apple Color Emoji");
704     static WKStringRef sansSerifFontFamily = WKStringCreateWithUTF8CString("Helvetica");
705     static WKStringRef serifFontFamily = WKStringCreateWithUTF8CString("Times");
706
707     WKPreferencesSetMinimumFontSize(preferences, 0);
708     WKPreferencesSetStandardFontFamily(preferences, standardFontFamily);
709     WKPreferencesSetCursiveFontFamily(preferences, cursiveFontFamily);
710     WKPreferencesSetFantasyFontFamily(preferences, fantasyFontFamily);
711     WKPreferencesSetFixedFontFamily(preferences, fixedFontFamily);
712     WKPreferencesSetPictographFontFamily(preferences, pictographFontFamily);
713     WKPreferencesSetSansSerifFontFamily(preferences, sansSerifFontFamily);
714     WKPreferencesSetSerifFontFamily(preferences, serifFontFamily);
715     WKPreferencesSetAsynchronousSpellCheckingEnabled(preferences, false);
716 #if ENABLE(WEB_AUDIO)
717     WKPreferencesSetMediaSourceEnabled(preferences, true);
718 #endif
719
720     WKPreferencesSetHiddenPageDOMTimerThrottlingEnabled(preferences, false);
721     WKPreferencesSetHiddenPageCSSAnimationSuspensionEnabled(preferences, false);
722
723     WKPreferencesSetAcceleratedDrawingEnabled(preferences, m_shouldUseAcceleratedDrawing);
724     // FIXME: We should be testing the default.
725     WKPreferencesSetStorageBlockingPolicy(preferences, kWKAllowAllStorage);
726
727     WKPreferencesSetResourceTimingEnabled(preferences, true);
728     WKPreferencesSetUserTimingEnabled(preferences, true);
729     WKPreferencesSetMediaPreloadingEnabled(preferences, true);
730     WKPreferencesSetMediaPlaybackAllowsInline(preferences, true);
731     WKPreferencesSetInlineMediaPlaybackRequiresPlaysInlineAttribute(preferences, false);
732     WKPreferencesSetBeaconAPIEnabled(preferences, true);
733     WKPreferencesSetDirectoryUploadEnabled(preferences, true);
734
735     WKCookieManagerDeleteAllCookies(WKContextGetCookieManager(m_context.get()));
736
737     WKPreferencesSetMockCaptureDevicesEnabled(preferences, true);
738     
739     WKPreferencesSetLargeImageAsyncDecodingEnabled(preferences, false);
740
741     WKPreferencesSetInspectorAdditionsEnabled(preferences, options.enableInspectorAdditions);
742
743     WKPreferencesSetStorageAccessAPIEnabled(preferences, true);
744
745     platformResetPreferencesToConsistentValues();
746 }
747
748 bool TestController::resetStateToConsistentValues(const TestOptions& options)
749 {
750     SetForScope<State> changeState(m_state, Resetting);
751     m_beforeUnloadReturnValue = true;
752
753     // This setting differs between the antique and modern Mac WebKit2 API.
754     // For now, maintain the antique behavior, because some tests depend on it!
755     // FIXME: We should be testing the default.
756     WKPageSetBackgroundExtendsBeyondPage(m_mainWebView->page(), false);
757
758     WKRetainPtr<WKStringRef> messageName = adoptWK(WKStringCreateWithUTF8CString("Reset"));
759     WKRetainPtr<WKMutableDictionaryRef> resetMessageBody = adoptWK(WKMutableDictionaryCreate());
760
761     WKRetainPtr<WKStringRef> shouldGCKey = adoptWK(WKStringCreateWithUTF8CString("ShouldGC"));
762     WKRetainPtr<WKBooleanRef> shouldGCValue = adoptWK(WKBooleanCreate(m_gcBetweenTests));
763     WKDictionarySetItem(resetMessageBody.get(), shouldGCKey.get(), shouldGCValue.get());
764
765     WKRetainPtr<WKStringRef> allowedHostsKey = adoptWK(WKStringCreateWithUTF8CString("AllowedHosts"));
766     WKRetainPtr<WKMutableArrayRef> allowedHostsValue = adoptWK(WKMutableArrayCreate());
767     for (auto& host : m_allowedHosts) {
768         WKRetainPtr<WKStringRef> wkHost = adoptWK(WKStringCreateWithUTF8CString(host.c_str()));
769         WKArrayAppendItem(allowedHostsValue.get(), wkHost.get());
770     }
771     WKDictionarySetItem(resetMessageBody.get(), allowedHostsKey.get(), allowedHostsValue.get());
772
773     WKPagePostMessageToInjectedBundle(TestController::singleton().mainWebView()->page(), messageName.get(), resetMessageBody.get());
774
775     WKContextSetShouldUseFontSmoothing(TestController::singleton().context(), false);
776
777     WKContextSetCacheModel(TestController::singleton().context(), kWKCacheModelDocumentBrowser);
778
779     WKContextClearCachedCredentials(TestController::singleton().context());
780
781     WKWebsiteDataStoreRemoveAllServiceWorkerRegistrations(WKContextGetWebsiteDataStore(platformContext()));
782
783     clearDOMCaches();
784
785     WKContextSetAllowsAnySSLCertificateForServiceWorkerTesting(platformContext(), true);
786
787     // FIXME: This function should also ensure that there is only one page open.
788
789     // Reset the EventSender for each test.
790     m_eventSenderProxy = std::make_unique<EventSenderProxy>(this);
791
792     // FIXME: Is this needed? Nothing in TestController changes preferences during tests, and if there is
793     // some other code doing this, it should probably be responsible for cleanup too.
794     resetPreferencesToConsistentValues(options);
795
796 #if !PLATFORM(COCOA) && !PLATFORM(WPE)
797     WKTextCheckerContinuousSpellCheckingEnabledStateChanged(true);
798 #endif
799
800     // Make sure the view is in the window (a test can unparent it).
801     m_mainWebView->addToWindow();
802
803     // In the case that a test using the chrome input field failed, be sure to clean up for the next test.
804     m_mainWebView->removeChromeInputField();
805     m_mainWebView->focus();
806
807     // Re-set to the default backing scale factor by setting the custom scale factor to 0.
808     WKPageSetCustomBackingScaleFactor(m_mainWebView->page(), 0);
809
810     WKPageClearWheelEventTestTrigger(m_mainWebView->page());
811
812     WKPageSetMuted(m_mainWebView->page(), true);
813
814     WKPageClearUserMediaState(m_mainWebView->page());
815
816     // Reset notification permissions
817     m_webNotificationProvider.reset();
818
819     // Reset Geolocation permissions.
820     m_geolocationPermissionRequests.clear();
821     m_isGeolocationPermissionSet = false;
822     m_isGeolocationPermissionAllowed = false;
823
824     // Reset UserMedia permissions.
825     m_userMediaPermissionRequests.clear();
826     m_cachedUserMediaPermissions.clear();
827     setUserMediaPermission(true);
828
829     // Reset Custom Policy Delegate.
830     setCustomPolicyDelegate(false, false);
831
832     m_shouldDownloadUndisplayableMIMETypes = false;
833
834     m_workQueueManager.clearWorkQueue();
835
836     m_rejectsProtectionSpaceAndContinueForAuthenticationChallenges = false;
837     m_handlesAuthenticationChallenges = false;
838     m_authenticationUsername = String();
839     m_authenticationPassword = String();
840
841     m_shouldBlockAllPlugins = false;
842
843     m_shouldLogDownloadCallbacks = false;
844     m_shouldLogHistoryClientCallbacks = false;
845     m_shouldLogCanAuthenticateAgainstProtectionSpace = false;
846
847     setHidden(false);
848
849     platformResetStateToConsistentValues();
850
851     // Reset main page back to about:blank
852     m_doneResetting = false;
853
854     m_shouldDecideNavigationPolicyAfterDelay = false;
855
856     setNavigationGesturesEnabled(false);
857     
858     setIgnoresViewportScaleLimits(options.ignoresViewportScaleLimits);
859
860     m_openPanelFileURLs = nullptr;
861     
862     statisticsResetToConsistentState();
863
864     WKPageLoadURL(m_mainWebView->page(), blankURL());
865     runUntil(m_doneResetting, m_currentInvocation->shortTimeout());
866     return m_doneResetting;
867 }
868
869 void TestController::terminateWebContentProcess()
870 {
871     WKPageTerminate(m_mainWebView->page());
872 }
873
874 void TestController::reattachPageToWebProcess()
875 {
876     // Loading a web page is the only way to reattach an existing page to a process.
877     m_doneResetting = false;
878     WKPageLoadURL(m_mainWebView->page(), blankURL());
879     runUntil(m_doneResetting, m_currentInvocation->shortTimeout());
880 }
881
882 const char* TestController::webProcessName()
883 {
884     // FIXME: Find a way to not hardcode the process name.
885 #if PLATFORM(IOS) && !PLATFORM(IOS_SIMULATOR)
886     return "com.apple.WebKit.WebContent";
887 #elif PLATFORM(COCOA)
888     return "com.apple.WebKit.WebContent.Development";
889 #else
890     return "WebProcess";
891 #endif
892 }
893
894 const char* TestController::networkProcessName()
895 {
896     // FIXME: Find a way to not hardcode the process name.
897 #if PLATFORM(IOS) && !PLATFORM(IOS_SIMULATOR)
898     return "com.apple.WebKit.Networking";
899 #elif PLATFORM(COCOA)
900     return "com.apple.WebKit.Networking.Development";
901 #else
902     return "NetworkProcess";
903 #endif
904 }
905
906 const char* TestController::databaseProcessName()
907 {
908     // FIXME: Find a way to not hardcode the process name.
909 #if PLATFORM(IOS) && !PLATFORM(IOS_SIMULATOR)
910 #if !PLATFORM(WATCHOS) && !PLATFORM(APPLETV) && __IPHONE_OS_VERSION_MIN_REQUIRED < 110300
911     return "com.apple.WebKit.Databases";
912 #else
913     return "com.apple.WebKit.Storage";
914 #endif
915 #elif PLATFORM(COCOA)
916     return "com.apple.WebKit.Storage.Development";
917 #else
918     return "DatabaseProcess";
919 #endif
920 }
921
922 void TestController::setAllowsAnySSLCertificate(bool allows)
923 {
924     WKContextSetAllowsAnySSLCertificateForWebSocketTesting(platformContext(), allows);
925 }
926
927 static std::string testPath(WKURLRef url)
928 {
929     auto scheme = adoptWK(WKURLCopyScheme(url));
930     if (WKStringIsEqualToUTF8CStringIgnoringCase(scheme.get(), "file")) {
931         auto path = adoptWK(WKURLCopyPath(url));
932         auto buffer = std::vector<char>(WKStringGetMaximumUTF8CStringSize(path.get()));
933         auto length = WKStringGetUTF8CString(path.get(), buffer.data(), buffer.size());
934         return std::string(buffer.data(), length);
935     }
936     return std::string();
937 }
938
939 static WKURLRef createTestURL(const char* pathOrURL)
940 {
941     if (strstr(pathOrURL, "http://") || strstr(pathOrURL, "https://") || strstr(pathOrURL, "file://"))
942         return WKURLCreateWithUTF8CString(pathOrURL);
943
944     // Creating from filesytem path.
945     size_t length = strlen(pathOrURL);
946     if (!length)
947         return 0;
948
949     const char separator = '/';
950     bool isAbsolutePath = pathOrURL[0] == separator;
951     const char* filePrefix = "file://";
952     static const size_t prefixLength = strlen(filePrefix);
953
954     std::unique_ptr<char[]> buffer;
955     if (isAbsolutePath) {
956         buffer = std::make_unique<char[]>(prefixLength + length + 1);
957         strcpy(buffer.get(), filePrefix);
958         strcpy(buffer.get() + prefixLength, pathOrURL);
959     } else {
960         buffer = std::make_unique<char[]>(prefixLength + PATH_MAX + length + 2); // 1 for the separator
961         strcpy(buffer.get(), filePrefix);
962         if (!getcwd(buffer.get() + prefixLength, PATH_MAX))
963             return 0;
964         size_t numCharacters = strlen(buffer.get());
965         buffer[numCharacters] = separator;
966         strcpy(buffer.get() + numCharacters + 1, pathOrURL);
967     }
968
969     return WKURLCreateWithUTF8CString(buffer.get());
970 }
971
972 static bool parseBooleanTestHeaderValue(const std::string& value)
973 {
974     if (value == "true")
975         return true;
976     if (value == "false")
977         return false;
978
979     LOG_ERROR("Found unexpected value '%s' for boolean option. Expected 'true' or 'false'.", value.c_str());
980     return false;
981 }
982
983 static void updateTestOptionsFromTestHeader(TestOptions& testOptions, const std::string& pathOrURL, const std::string& absolutePath)
984 {
985     std::string filename = absolutePath;
986     if (filename.empty()) {
987         // Gross. Need to reduce conversions between all the string types and URLs.
988         WKRetainPtr<WKURLRef> wkURL(AdoptWK, createTestURL(pathOrURL.c_str()));
989         filename = testPath(wkURL.get());
990     }
991
992     if (filename.empty())
993         return;
994
995     std::string options;
996     std::ifstream testFile(filename.data());
997     if (!testFile.good())
998         return;
999     getline(testFile, options);
1000     std::string beginString("webkit-test-runner [ ");
1001     std::string endString(" ]");
1002     size_t beginLocation = options.find(beginString);
1003     if (beginLocation == std::string::npos)
1004         return;
1005     size_t endLocation = options.find(endString, beginLocation);
1006     if (endLocation == std::string::npos) {
1007         LOG_ERROR("Could not find end of test header in %s", filename.c_str());
1008         return;
1009     }
1010     std::string pairString = options.substr(beginLocation + beginString.size(), endLocation - (beginLocation + beginString.size()));
1011     size_t pairStart = 0;
1012     while (pairStart < pairString.size()) {
1013         size_t pairEnd = pairString.find(" ", pairStart);
1014         if (pairEnd == std::string::npos)
1015             pairEnd = pairString.size();
1016         size_t equalsLocation = pairString.find("=", pairStart);
1017         if (equalsLocation == std::string::npos) {
1018             LOG_ERROR("Malformed option in test header (could not find '=' character) in %s", filename.c_str());
1019             break;
1020         }
1021         auto key = pairString.substr(pairStart, equalsLocation - pairStart);
1022         auto value = pairString.substr(equalsLocation + 1, pairEnd - (equalsLocation + 1));
1023         if (key == "language")
1024             String(value.c_str()).split(",", false, testOptions.overrideLanguages);
1025         if (key == "useThreadedScrolling")
1026             testOptions.useThreadedScrolling = parseBooleanTestHeaderValue(value);
1027         if (key == "useFlexibleViewport")
1028             testOptions.useFlexibleViewport = parseBooleanTestHeaderValue(value);
1029         if (key == "useDataDetection")
1030             testOptions.useDataDetection = parseBooleanTestHeaderValue(value);
1031         if (key == "useMockScrollbars")
1032             testOptions.useMockScrollbars = parseBooleanTestHeaderValue(value);
1033         if (key == "needsSiteSpecificQuirks")
1034             testOptions.needsSiteSpecificQuirks = parseBooleanTestHeaderValue(value);
1035         if (key == "ignoresViewportScaleLimits")
1036             testOptions.ignoresViewportScaleLimits = parseBooleanTestHeaderValue(value);
1037         if (key == "useCharacterSelectionGranularity")
1038             testOptions.useCharacterSelectionGranularity = parseBooleanTestHeaderValue(value);
1039         if (key == "enableAttachmentElement")
1040             testOptions.enableAttachmentElement = parseBooleanTestHeaderValue(value);
1041         if (key == "enableIntersectionObserver")
1042             testOptions.enableIntersectionObserver = parseBooleanTestHeaderValue(value);
1043         if (key == "enableModernMediaControls")
1044             testOptions.enableModernMediaControls = parseBooleanTestHeaderValue(value);
1045         if (key == "enablePointerLock")
1046             testOptions.enablePointerLock = parseBooleanTestHeaderValue(value);
1047         if (key == "enableCredentialManagement")
1048             testOptions.enableCredentialManagement = parseBooleanTestHeaderValue(value);
1049         if (key == "enableIsSecureContextAttribute")
1050             testOptions.enableIsSecureContextAttribute = parseBooleanTestHeaderValue(value);
1051         if (key == "enableInspectorAdditions")
1052             testOptions.enableInspectorAdditions = parseBooleanTestHeaderValue(value);
1053         if (key == "dumpJSConsoleLogInStdErr")
1054             testOptions.dumpJSConsoleLogInStdErr = parseBooleanTestHeaderValue(value);
1055         pairStart = pairEnd + 1;
1056     }
1057 }
1058
1059 TestOptions TestController::testOptionsForTest(const TestCommand& command) const
1060 {
1061     TestOptions options(command.pathOrURL);
1062
1063     options.useRemoteLayerTree = m_shouldUseRemoteLayerTree;
1064     options.shouldShowWebView = m_shouldShowWebView;
1065
1066     updatePlatformSpecificTestOptionsForTest(options, command.pathOrURL);
1067     updateTestOptionsFromTestHeader(options, command.pathOrURL, command.absolutePath);
1068
1069     return options;
1070 }
1071
1072 void TestController::updateWebViewSizeForTest(const TestInvocation& test)
1073 {
1074     unsigned width = viewWidth;
1075     unsigned height = viewHeight;
1076     if (test.options().isSVGTest) {
1077         width = w3cSVGViewWidth;
1078         height = w3cSVGViewHeight;
1079     }
1080
1081     mainWebView()->resizeTo(width, height);
1082 }
1083
1084 void TestController::updateWindowScaleForTest(PlatformWebView* view, const TestInvocation& test)
1085 {
1086     view->changeWindowScaleIfNeeded(test.options().deviceScaleFactor);
1087 }
1088
1089 void TestController::configureViewForTest(const TestInvocation& test)
1090 {
1091     ensureViewSupportsOptionsForTest(test);
1092     updateWebViewSizeForTest(test);
1093     updateWindowScaleForTest(mainWebView(), test);
1094
1095     platformConfigureViewForTest(test);
1096 }
1097
1098 class CommandTokenizer {
1099 public:
1100     explicit CommandTokenizer(const std::string& input)
1101         : m_input(input)
1102         , m_posNextSeparator(0)
1103     {
1104         pump();
1105     }
1106
1107     bool hasNext() const;
1108     std::string next();
1109
1110 private:
1111     void pump();
1112     static const char kSeparator = '\'';
1113     const std::string& m_input;
1114     std::string m_next;
1115     size_t m_posNextSeparator;
1116 };
1117
1118 void CommandTokenizer::pump()
1119 {
1120     if (m_posNextSeparator == std::string::npos || m_posNextSeparator == m_input.size()) {
1121         m_next = std::string();
1122         return;
1123     }
1124     size_t start = m_posNextSeparator ? m_posNextSeparator + 1 : 0;
1125     m_posNextSeparator = m_input.find(kSeparator, start);
1126     size_t size = m_posNextSeparator == std::string::npos ? std::string::npos : m_posNextSeparator - start;
1127     m_next = std::string(m_input, start, size);
1128 }
1129
1130 std::string CommandTokenizer::next()
1131 {
1132     ASSERT(hasNext());
1133
1134     std::string oldNext = m_next;
1135     pump();
1136     return oldNext;
1137 }
1138
1139 bool CommandTokenizer::hasNext() const
1140 {
1141     return !m_next.empty();
1142 }
1143
1144 NO_RETURN static void die(const std::string& inputLine)
1145 {
1146     fprintf(stderr, "Unexpected input line: %s\n", inputLine.c_str());
1147     exit(1);
1148 }
1149
1150 TestCommand parseInputLine(const std::string& inputLine)
1151 {
1152     TestCommand result;
1153     CommandTokenizer tokenizer(inputLine);
1154     if (!tokenizer.hasNext())
1155         die(inputLine);
1156
1157     std::string arg = tokenizer.next();
1158     result.pathOrURL = arg;
1159     while (tokenizer.hasNext()) {
1160         arg = tokenizer.next();
1161         if (arg == std::string("--timeout")) {
1162             std::string timeoutToken = tokenizer.next();
1163             result.timeout = atoi(timeoutToken.c_str());
1164         } else if (arg == std::string("-p") || arg == std::string("--pixel-test")) {
1165             result.shouldDumpPixels = true;
1166             if (tokenizer.hasNext())
1167                 result.expectedPixelHash = tokenizer.next();
1168         } else if (arg == std::string("--dump-jsconsolelog-in-stderr"))
1169             result.dumpJSConsoleLogInStdErr = true;
1170         else if (arg == std::string("--absolutePath"))
1171             result.absolutePath = tokenizer.next();
1172         else
1173             die(inputLine);
1174     }
1175     return result;
1176 }
1177
1178 bool TestController::runTest(const char* inputLine)
1179 {
1180     AutodrainedPool pool;
1181     
1182     WKTextCheckerSetTestingMode(true);
1183     TestCommand command = parseInputLine(std::string(inputLine));
1184
1185     m_state = RunningTest;
1186     
1187     TestOptions options = testOptionsForTest(command);
1188
1189     WKRetainPtr<WKURLRef> wkURL(AdoptWK, createTestURL(command.pathOrURL.c_str()));
1190     m_currentInvocation = std::make_unique<TestInvocation>(wkURL.get(), options);
1191
1192     if (command.shouldDumpPixels || m_shouldDumpPixelsForAllTests)
1193         m_currentInvocation->setIsPixelTest(command.expectedPixelHash);
1194     if (command.timeout > 0)
1195         m_currentInvocation->setCustomTimeout(command.timeout);
1196     m_currentInvocation->setDumpJSConsoleLogInStdErr(command.dumpJSConsoleLogInStdErr || options.dumpJSConsoleLogInStdErr);
1197
1198     platformWillRunTest(*m_currentInvocation);
1199
1200     m_currentInvocation->invoke();
1201     m_currentInvocation = nullptr;
1202
1203     return true;
1204 }
1205
1206 void TestController::runTestingServerLoop()
1207 {
1208     char filenameBuffer[2048];
1209     while (fgets(filenameBuffer, sizeof(filenameBuffer), stdin)) {
1210         char* newLineCharacter = strchr(filenameBuffer, '\n');
1211         if (newLineCharacter)
1212             *newLineCharacter = '\0';
1213
1214         if (strlen(filenameBuffer) == 0)
1215             continue;
1216
1217         if (!runTest(filenameBuffer))
1218             break;
1219     }
1220 }
1221
1222 void TestController::run()
1223 {
1224     if (m_usingServerMode)
1225         runTestingServerLoop();
1226     else {
1227         for (size_t i = 0; i < m_paths.size(); ++i) {
1228             if (!runTest(m_paths[i].c_str()))
1229                 break;
1230         }
1231     }
1232 }
1233
1234 void TestController::runUntil(bool& done, double timeout)
1235 {
1236     if (m_forceNoTimeout)
1237         timeout = noTimeout;
1238
1239     platformRunUntil(done, timeout);
1240 }
1241
1242 // WKContextInjectedBundleClient
1243
1244 void TestController::didReceiveMessageFromInjectedBundle(WKContextRef context, WKStringRef messageName, WKTypeRef messageBody, const void* clientInfo)
1245 {
1246     static_cast<TestController*>(const_cast<void*>(clientInfo))->didReceiveMessageFromInjectedBundle(messageName, messageBody);
1247 }
1248
1249 void TestController::didReceiveSynchronousMessageFromInjectedBundle(WKContextRef context, WKStringRef messageName, WKTypeRef messageBody, WKTypeRef* returnData, const void* clientInfo)
1250 {
1251     *returnData = static_cast<TestController*>(const_cast<void*>(clientInfo))->didReceiveSynchronousMessageFromInjectedBundle(messageName, messageBody).leakRef();
1252 }
1253
1254 WKTypeRef TestController::getInjectedBundleInitializationUserData(WKContextRef, const void* clientInfo)
1255 {
1256     return static_cast<TestController*>(const_cast<void*>(clientInfo))->getInjectedBundleInitializationUserData().leakRef();
1257 }
1258
1259 // WKPageInjectedBundleClient
1260
1261 void TestController::didReceivePageMessageFromInjectedBundle(WKPageRef page, WKStringRef messageName, WKTypeRef messageBody, const void* clientInfo)
1262 {
1263     static_cast<TestController*>(const_cast<void*>(clientInfo))->didReceiveMessageFromInjectedBundle(messageName, messageBody);
1264 }
1265
1266 void TestController::didReceiveSynchronousPageMessageFromInjectedBundle(WKPageRef page, WKStringRef messageName, WKTypeRef messageBody, WKTypeRef* returnData, const void* clientInfo)
1267 {
1268     *returnData = static_cast<TestController*>(const_cast<void*>(clientInfo))->didReceiveSynchronousMessageFromInjectedBundle(messageName, messageBody).leakRef();
1269 }
1270
1271 void TestController::networkProcessDidCrash(WKContextRef context, const void *clientInfo)
1272 {
1273     static_cast<TestController*>(const_cast<void*>(clientInfo))->networkProcessDidCrash();
1274 }
1275
1276 void TestController::databaseProcessDidCrash(WKContextRef context, const void *clientInfo)
1277 {
1278     static_cast<TestController*>(const_cast<void*>(clientInfo))->databaseProcessDidCrash();
1279 }
1280
1281 void TestController::didReceiveKeyDownMessageFromInjectedBundle(WKDictionaryRef messageBodyDictionary, bool synchronous)
1282 {
1283     WKRetainPtr<WKStringRef> keyKey = adoptWK(WKStringCreateWithUTF8CString("Key"));
1284     WKStringRef key = static_cast<WKStringRef>(WKDictionaryGetItemForKey(messageBodyDictionary, keyKey.get()));
1285
1286     WKRetainPtr<WKStringRef> modifiersKey = adoptWK(WKStringCreateWithUTF8CString("Modifiers"));
1287     WKEventModifiers modifiers = static_cast<WKEventModifiers>(WKUInt64GetValue(static_cast<WKUInt64Ref>(WKDictionaryGetItemForKey(messageBodyDictionary, modifiersKey.get()))));
1288
1289     WKRetainPtr<WKStringRef> locationKey = adoptWK(WKStringCreateWithUTF8CString("Location"));
1290     unsigned location = static_cast<unsigned>(WKUInt64GetValue(static_cast<WKUInt64Ref>(WKDictionaryGetItemForKey(messageBodyDictionary, locationKey.get()))));
1291
1292     m_eventSenderProxy->keyDown(key, modifiers, location);
1293 }
1294
1295 void TestController::didReceiveMessageFromInjectedBundle(WKStringRef messageName, WKTypeRef messageBody)
1296 {
1297     if (WKStringIsEqualToUTF8CString(messageName, "EventSender")) {
1298         if (m_state != RunningTest)
1299             return;
1300
1301         ASSERT(WKGetTypeID(messageBody) == WKDictionaryGetTypeID());
1302         WKDictionaryRef messageBodyDictionary = static_cast<WKDictionaryRef>(messageBody);
1303
1304         WKRetainPtr<WKStringRef> subMessageKey(AdoptWK, WKStringCreateWithUTF8CString("SubMessage"));
1305         WKStringRef subMessageName = static_cast<WKStringRef>(WKDictionaryGetItemForKey(messageBodyDictionary, subMessageKey.get()));
1306
1307         if (WKStringIsEqualToUTF8CString(subMessageName, "MouseDown") || WKStringIsEqualToUTF8CString(subMessageName, "MouseUp")) {
1308             WKRetainPtr<WKStringRef> buttonKey = adoptWK(WKStringCreateWithUTF8CString("Button"));
1309             unsigned button = static_cast<unsigned>(WKUInt64GetValue(static_cast<WKUInt64Ref>(WKDictionaryGetItemForKey(messageBodyDictionary, buttonKey.get()))));
1310
1311             WKRetainPtr<WKStringRef> modifiersKey = adoptWK(WKStringCreateWithUTF8CString("Modifiers"));
1312             WKEventModifiers modifiers = static_cast<WKEventModifiers>(WKUInt64GetValue(static_cast<WKUInt64Ref>(WKDictionaryGetItemForKey(messageBodyDictionary, modifiersKey.get()))));
1313
1314             // Forward to WebProcess
1315             if (WKStringIsEqualToUTF8CString(subMessageName, "MouseDown"))
1316                 m_eventSenderProxy->mouseDown(button, modifiers);
1317             else
1318                 m_eventSenderProxy->mouseUp(button, modifiers);
1319
1320             return;
1321         }
1322
1323         if (WKStringIsEqualToUTF8CString(subMessageName, "KeyDown")) {
1324             didReceiveKeyDownMessageFromInjectedBundle(messageBodyDictionary, false);
1325
1326             return;
1327         }
1328
1329         if (WKStringIsEqualToUTF8CString(subMessageName, "MouseScrollBy")) {
1330             WKRetainPtr<WKStringRef> xKey = adoptWK(WKStringCreateWithUTF8CString("X"));
1331             double x = WKDoubleGetValue(static_cast<WKDoubleRef>(WKDictionaryGetItemForKey(messageBodyDictionary, xKey.get())));
1332
1333             WKRetainPtr<WKStringRef> yKey = adoptWK(WKStringCreateWithUTF8CString("Y"));
1334             double y = WKDoubleGetValue(static_cast<WKDoubleRef>(WKDictionaryGetItemForKey(messageBodyDictionary, yKey.get())));
1335
1336             // Forward to WebProcess
1337             m_eventSenderProxy->mouseScrollBy(x, y);
1338             return;
1339         }
1340
1341         if (WKStringIsEqualToUTF8CString(subMessageName, "MouseScrollByWithWheelAndMomentumPhases")) {
1342             WKRetainPtr<WKStringRef> xKey = adoptWK(WKStringCreateWithUTF8CString("X"));
1343             double x = WKDoubleGetValue(static_cast<WKDoubleRef>(WKDictionaryGetItemForKey(messageBodyDictionary, xKey.get())));
1344             
1345             WKRetainPtr<WKStringRef> yKey = adoptWK(WKStringCreateWithUTF8CString("Y"));
1346             double y = WKDoubleGetValue(static_cast<WKDoubleRef>(WKDictionaryGetItemForKey(messageBodyDictionary, yKey.get())));
1347             
1348             WKRetainPtr<WKStringRef> phaseKey = adoptWK(WKStringCreateWithUTF8CString("Phase"));
1349             int phase = static_cast<int>(WKUInt64GetValue(static_cast<WKUInt64Ref>(WKDictionaryGetItemForKey(messageBodyDictionary, phaseKey.get()))));
1350             WKRetainPtr<WKStringRef> momentumKey = adoptWK(WKStringCreateWithUTF8CString("Momentum"));
1351             int momentum = static_cast<int>(WKUInt64GetValue(static_cast<WKUInt64Ref>(WKDictionaryGetItemForKey(messageBodyDictionary, momentumKey.get()))));
1352             
1353             // Forward to WebProcess
1354             m_eventSenderProxy->mouseScrollByWithWheelAndMomentumPhases(x, y, phase, momentum);
1355
1356             return;
1357         }
1358
1359         ASSERT_NOT_REACHED();
1360     }
1361
1362     if (!m_currentInvocation)
1363         return;
1364
1365     m_currentInvocation->didReceiveMessageFromInjectedBundle(messageName, messageBody);
1366 }
1367
1368 WKRetainPtr<WKTypeRef> TestController::didReceiveSynchronousMessageFromInjectedBundle(WKStringRef messageName, WKTypeRef messageBody)
1369 {
1370     if (WKStringIsEqualToUTF8CString(messageName, "EventSender")) {
1371         if (m_state != RunningTest)
1372             return nullptr;
1373
1374         ASSERT(WKGetTypeID(messageBody) == WKDictionaryGetTypeID());
1375         WKDictionaryRef messageBodyDictionary = static_cast<WKDictionaryRef>(messageBody);
1376
1377         WKRetainPtr<WKStringRef> subMessageKey(AdoptWK, WKStringCreateWithUTF8CString("SubMessage"));
1378         WKStringRef subMessageName = static_cast<WKStringRef>(WKDictionaryGetItemForKey(messageBodyDictionary, subMessageKey.get()));
1379
1380         if (WKStringIsEqualToUTF8CString(subMessageName, "KeyDown")) {
1381             didReceiveKeyDownMessageFromInjectedBundle(messageBodyDictionary, true);
1382
1383             return 0;
1384         }
1385
1386         if (WKStringIsEqualToUTF8CString(subMessageName, "MouseDown") || WKStringIsEqualToUTF8CString(subMessageName, "MouseUp")) {
1387             WKRetainPtr<WKStringRef> buttonKey = adoptWK(WKStringCreateWithUTF8CString("Button"));
1388             unsigned button = static_cast<unsigned>(WKUInt64GetValue(static_cast<WKUInt64Ref>(WKDictionaryGetItemForKey(messageBodyDictionary, buttonKey.get()))));
1389
1390             WKRetainPtr<WKStringRef> modifiersKey = adoptWK(WKStringCreateWithUTF8CString("Modifiers"));
1391             WKEventModifiers modifiers = static_cast<WKEventModifiers>(WKUInt64GetValue(static_cast<WKUInt64Ref>(WKDictionaryGetItemForKey(messageBodyDictionary, modifiersKey.get()))));
1392
1393             // Forward to WebProcess
1394             if (WKStringIsEqualToUTF8CString(subMessageName, "MouseDown"))
1395                 m_eventSenderProxy->mouseDown(button, modifiers);
1396             else
1397                 m_eventSenderProxy->mouseUp(button, modifiers);
1398             return 0;
1399         }
1400
1401         if (WKStringIsEqualToUTF8CString(subMessageName, "MouseMoveTo")) {
1402             WKRetainPtr<WKStringRef> xKey = adoptWK(WKStringCreateWithUTF8CString("X"));
1403             double x = WKDoubleGetValue(static_cast<WKDoubleRef>(WKDictionaryGetItemForKey(messageBodyDictionary, xKey.get())));
1404
1405             WKRetainPtr<WKStringRef> yKey = adoptWK(WKStringCreateWithUTF8CString("Y"));
1406             double y = WKDoubleGetValue(static_cast<WKDoubleRef>(WKDictionaryGetItemForKey(messageBodyDictionary, yKey.get())));
1407
1408             // Forward to WebProcess
1409             m_eventSenderProxy->mouseMoveTo(x, y);
1410             return 0;
1411         }
1412
1413 #if PLATFORM(MAC)
1414         if (WKStringIsEqualToUTF8CString(subMessageName, "MouseForceClick")) {
1415             m_eventSenderProxy->mouseForceClick();
1416             return 0;
1417         }
1418
1419         if (WKStringIsEqualToUTF8CString(subMessageName, "StartAndCancelMouseForceClick")) {
1420             m_eventSenderProxy->startAndCancelMouseForceClick();
1421             return 0;
1422         }
1423
1424         if (WKStringIsEqualToUTF8CString(subMessageName, "MouseForceDown")) {
1425             m_eventSenderProxy->mouseForceDown();
1426             return 0;
1427         }
1428
1429         if (WKStringIsEqualToUTF8CString(subMessageName, "MouseForceUp")) {
1430             m_eventSenderProxy->mouseForceUp();
1431             return 0;
1432         }
1433
1434         if (WKStringIsEqualToUTF8CString(subMessageName, "MouseForceChanged")) {
1435             WKRetainPtr<WKStringRef> forceKey = adoptWK(WKStringCreateWithUTF8CString("Force"));
1436             double force = WKDoubleGetValue(static_cast<WKDoubleRef>(WKDictionaryGetItemForKey(messageBodyDictionary, forceKey.get())));
1437
1438             m_eventSenderProxy->mouseForceChanged(force);
1439             return 0;
1440         }
1441 #endif // PLATFORM(MAC)
1442
1443         if (WKStringIsEqualToUTF8CString(subMessageName, "ContinuousMouseScrollBy")) {
1444             WKRetainPtr<WKStringRef> xKey = adoptWK(WKStringCreateWithUTF8CString("X"));
1445             double x = WKDoubleGetValue(static_cast<WKDoubleRef>(WKDictionaryGetItemForKey(messageBodyDictionary, xKey.get())));
1446
1447             WKRetainPtr<WKStringRef> yKey = adoptWK(WKStringCreateWithUTF8CString("Y"));
1448             double y = WKDoubleGetValue(static_cast<WKDoubleRef>(WKDictionaryGetItemForKey(messageBodyDictionary, yKey.get())));
1449
1450             WKRetainPtr<WKStringRef> pagedKey = adoptWK(WKStringCreateWithUTF8CString("Paged"));
1451             bool paged = static_cast<bool>(WKUInt64GetValue(static_cast<WKUInt64Ref>(WKDictionaryGetItemForKey(messageBodyDictionary, pagedKey.get()))));
1452
1453             // Forward to WebProcess
1454             m_eventSenderProxy->continuousMouseScrollBy(x, y, paged);
1455             return 0;
1456         }
1457
1458         if (WKStringIsEqualToUTF8CString(subMessageName, "LeapForward")) {
1459             WKRetainPtr<WKStringRef> timeKey = adoptWK(WKStringCreateWithUTF8CString("TimeInMilliseconds"));
1460             unsigned time = static_cast<unsigned>(WKUInt64GetValue(static_cast<WKUInt64Ref>(WKDictionaryGetItemForKey(messageBodyDictionary, timeKey.get()))));
1461
1462             m_eventSenderProxy->leapForward(time);
1463             return 0;
1464         }
1465
1466 #if ENABLE(TOUCH_EVENTS)
1467         if (WKStringIsEqualToUTF8CString(subMessageName, "AddTouchPoint")) {
1468             WKRetainPtr<WKStringRef> xKey = adoptWK(WKStringCreateWithUTF8CString("X"));
1469             int x = static_cast<int>(WKUInt64GetValue(static_cast<WKUInt64Ref>(WKDictionaryGetItemForKey(messageBodyDictionary, xKey.get()))));
1470
1471             WKRetainPtr<WKStringRef> yKey = adoptWK(WKStringCreateWithUTF8CString("Y"));
1472             int y = static_cast<int>(WKUInt64GetValue(static_cast<WKUInt64Ref>(WKDictionaryGetItemForKey(messageBodyDictionary, yKey.get()))));
1473
1474             m_eventSenderProxy->addTouchPoint(x, y);
1475             return 0;
1476         }
1477
1478         if (WKStringIsEqualToUTF8CString(subMessageName, "UpdateTouchPoint")) {
1479             WKRetainPtr<WKStringRef> indexKey = adoptWK(WKStringCreateWithUTF8CString("Index"));
1480             int index = static_cast<int>(WKUInt64GetValue(static_cast<WKUInt64Ref>(WKDictionaryGetItemForKey(messageBodyDictionary, indexKey.get()))));
1481
1482             WKRetainPtr<WKStringRef> xKey = adoptWK(WKStringCreateWithUTF8CString("X"));
1483             int x = static_cast<int>(WKUInt64GetValue(static_cast<WKUInt64Ref>(WKDictionaryGetItemForKey(messageBodyDictionary, xKey.get()))));
1484
1485             WKRetainPtr<WKStringRef> yKey = adoptWK(WKStringCreateWithUTF8CString("Y"));
1486             int y = static_cast<int>(WKUInt64GetValue(static_cast<WKUInt64Ref>(WKDictionaryGetItemForKey(messageBodyDictionary, yKey.get()))));
1487
1488             m_eventSenderProxy->updateTouchPoint(index, x, y);
1489             return 0;
1490         }
1491
1492         if (WKStringIsEqualToUTF8CString(subMessageName, "SetTouchModifier")) {
1493             WKRetainPtr<WKStringRef> modifierKey = adoptWK(WKStringCreateWithUTF8CString("Modifier"));
1494             WKEventModifiers modifier = static_cast<WKEventModifiers>(WKUInt64GetValue(static_cast<WKUInt64Ref>(WKDictionaryGetItemForKey(messageBodyDictionary, modifierKey.get()))));
1495
1496             WKRetainPtr<WKStringRef> enableKey = adoptWK(WKStringCreateWithUTF8CString("Enable"));
1497             bool enable = static_cast<bool>(WKUInt64GetValue(static_cast<WKUInt64Ref>(WKDictionaryGetItemForKey(messageBodyDictionary, enableKey.get()))));
1498
1499             m_eventSenderProxy->setTouchModifier(modifier, enable);
1500             return 0;
1501         }
1502
1503         if (WKStringIsEqualToUTF8CString(subMessageName, "SetTouchPointRadius")) {
1504             WKRetainPtr<WKStringRef> xKey = adoptWK(WKStringCreateWithUTF8CString("RadiusX"));
1505             int x = static_cast<int>(WKUInt64GetValue(static_cast<WKUInt64Ref>(WKDictionaryGetItemForKey(messageBodyDictionary, xKey.get()))));
1506
1507             WKRetainPtr<WKStringRef> yKey = adoptWK(WKStringCreateWithUTF8CString("RadiusY"));
1508             int y = static_cast<int>(WKUInt64GetValue(static_cast<WKUInt64Ref>(WKDictionaryGetItemForKey(messageBodyDictionary, yKey.get()))));
1509
1510             m_eventSenderProxy->setTouchPointRadius(x, y);
1511             return 0;
1512         }
1513
1514         if (WKStringIsEqualToUTF8CString(subMessageName, "TouchStart")) {
1515             m_eventSenderProxy->touchStart();
1516             return 0;
1517         }
1518
1519         if (WKStringIsEqualToUTF8CString(subMessageName, "TouchMove")) {
1520             m_eventSenderProxy->touchMove();
1521             return 0;
1522         }
1523
1524         if (WKStringIsEqualToUTF8CString(subMessageName, "TouchEnd")) {
1525             m_eventSenderProxy->touchEnd();
1526             return 0;
1527         }
1528
1529         if (WKStringIsEqualToUTF8CString(subMessageName, "TouchCancel")) {
1530             m_eventSenderProxy->touchCancel();
1531             return 0;
1532         }
1533
1534         if (WKStringIsEqualToUTF8CString(subMessageName, "ClearTouchPoints")) {
1535             m_eventSenderProxy->clearTouchPoints();
1536             return 0;
1537         }
1538
1539         if (WKStringIsEqualToUTF8CString(subMessageName, "ReleaseTouchPoint")) {
1540             WKRetainPtr<WKStringRef> indexKey = adoptWK(WKStringCreateWithUTF8CString("Index"));
1541             int index = static_cast<int>(WKUInt64GetValue(static_cast<WKUInt64Ref>(WKDictionaryGetItemForKey(messageBodyDictionary, indexKey.get()))));
1542             m_eventSenderProxy->releaseTouchPoint(index);
1543             return 0;
1544         }
1545
1546         if (WKStringIsEqualToUTF8CString(subMessageName, "CancelTouchPoint")) {
1547             WKRetainPtr<WKStringRef> indexKey = adoptWK(WKStringCreateWithUTF8CString("Index"));
1548             int index = static_cast<int>(WKUInt64GetValue(static_cast<WKUInt64Ref>(WKDictionaryGetItemForKey(messageBodyDictionary, indexKey.get()))));
1549             m_eventSenderProxy->cancelTouchPoint(index);
1550             return 0;
1551         }
1552 #endif
1553         ASSERT_NOT_REACHED();
1554     }
1555     return m_currentInvocation->didReceiveSynchronousMessageFromInjectedBundle(messageName, messageBody);
1556 }
1557
1558 WKRetainPtr<WKTypeRef> TestController::getInjectedBundleInitializationUserData()
1559 {
1560     return nullptr;
1561 }
1562
1563 // WKContextClient
1564
1565 void TestController::networkProcessDidCrash()
1566 {
1567     pid_t pid = WKContextGetNetworkProcessIdentifier(m_context.get());
1568     fprintf(stderr, "#CRASHED - %s (pid %ld)\n", networkProcessName(), static_cast<long>(pid));
1569     exit(1);
1570 }
1571
1572 void TestController::databaseProcessDidCrash()
1573 {
1574     pid_t pid = WKContextGetDatabaseProcessIdentifier(m_context.get());
1575     fprintf(stderr, "#CRASHED - %s (pid %ld)\n", databaseProcessName(), static_cast<long>(pid));
1576     exit(1);
1577 }
1578
1579 // WKPageNavigationClient
1580
1581 void TestController::didCommitNavigation(WKPageRef page, WKNavigationRef navigation, WKTypeRef, const void* clientInfo)
1582 {
1583     static_cast<TestController*>(const_cast<void*>(clientInfo))->didCommitNavigation(page, navigation);
1584 }
1585
1586 void TestController::didFinishNavigation(WKPageRef page, WKNavigationRef navigation, WKTypeRef, const void* clientInfo)
1587 {
1588     static_cast<TestController*>(const_cast<void*>(clientInfo))->didFinishNavigation(page, navigation);
1589 }
1590
1591 bool TestController::canAuthenticateAgainstProtectionSpace(WKPageRef page, WKProtectionSpaceRef protectionSpace, const void* clientInfo)
1592 {
1593     return static_cast<TestController*>(const_cast<void*>(clientInfo))->canAuthenticateAgainstProtectionSpace(page, protectionSpace);
1594 }
1595
1596 void TestController::didReceiveAuthenticationChallenge(WKPageRef page, WKAuthenticationChallengeRef authenticationChallenge, const void *clientInfo)
1597 {
1598     static_cast<TestController*>(const_cast<void*>(clientInfo))->didReceiveAuthenticationChallenge(page, /*frame,*/ authenticationChallenge);
1599 }
1600
1601 void TestController::processDidCrash(WKPageRef page, const void* clientInfo)
1602 {
1603     static_cast<TestController*>(const_cast<void*>(clientInfo))->processDidCrash();
1604 }
1605
1606 void TestController::didBeginNavigationGesture(WKPageRef page, const void *clientInfo)
1607 {
1608     static_cast<TestController*>(const_cast<void*>(clientInfo))->didBeginNavigationGesture(page);
1609 }
1610
1611 void TestController::willEndNavigationGesture(WKPageRef page, WKBackForwardListItemRef backForwardListItem, const void *clientInfo)
1612 {
1613     static_cast<TestController*>(const_cast<void*>(clientInfo))->willEndNavigationGesture(page, backForwardListItem);
1614 }
1615
1616 void TestController::didEndNavigationGesture(WKPageRef page, WKBackForwardListItemRef backForwardListItem, const void *clientInfo)
1617 {
1618     static_cast<TestController*>(const_cast<void*>(clientInfo))->didEndNavigationGesture(page, backForwardListItem);
1619 }
1620
1621 void TestController::didRemoveNavigationGestureSnapshot(WKPageRef page, const void *clientInfo)
1622 {
1623     static_cast<TestController*>(const_cast<void*>(clientInfo))->didRemoveNavigationGestureSnapshot(page);
1624 }
1625
1626 WKPluginLoadPolicy TestController::decidePolicyForPluginLoad(WKPageRef page, WKPluginLoadPolicy currentPluginLoadPolicy, WKDictionaryRef pluginInformation, WKStringRef* unavailabilityDescription, const void* clientInfo)
1627 {
1628     return static_cast<TestController*>(const_cast<void*>(clientInfo))->decidePolicyForPluginLoad(page, currentPluginLoadPolicy, pluginInformation, unavailabilityDescription);
1629 }
1630
1631 WKPluginLoadPolicy TestController::decidePolicyForPluginLoad(WKPageRef, WKPluginLoadPolicy currentPluginLoadPolicy, WKDictionaryRef pluginInformation, WKStringRef* unavailabilityDescription)
1632 {
1633     if (m_shouldBlockAllPlugins)
1634         return kWKPluginLoadPolicyBlocked;
1635
1636 #if PLATFORM(MAC)
1637     WKStringRef bundleIdentifier = (WKStringRef)WKDictionaryGetItemForKey(pluginInformation, WKPluginInformationBundleIdentifierKey());
1638     if (!bundleIdentifier)
1639         return currentPluginLoadPolicy;
1640
1641     if (WKStringIsEqualToUTF8CString(bundleIdentifier, "com.apple.QuickTime Plugin.plugin"))
1642         return currentPluginLoadPolicy;
1643
1644     if (WKStringIsEqualToUTF8CString(bundleIdentifier, "com.apple.testnetscapeplugin"))
1645         return currentPluginLoadPolicy;
1646
1647     RELEASE_ASSERT_NOT_REACHED(); // Please don't use any other plug-ins in tests, as they will not be installed on all machines.
1648 #else
1649     return currentPluginLoadPolicy;
1650 #endif
1651 }
1652
1653 void TestController::didCommitNavigation(WKPageRef page, WKNavigationRef navigation)
1654 {
1655     mainWebView()->focus();
1656 }
1657
1658 bool TestController::canAuthenticateAgainstProtectionSpace(WKPageRef page, WKProtectionSpaceRef protectionSpace)
1659 {
1660     if (m_shouldLogCanAuthenticateAgainstProtectionSpace)
1661         m_currentInvocation->outputText("canAuthenticateAgainstProtectionSpace\n");
1662     WKProtectionSpaceAuthenticationScheme authenticationScheme = WKProtectionSpaceGetAuthenticationScheme(protectionSpace);
1663     
1664     if (authenticationScheme == kWKProtectionSpaceAuthenticationSchemeServerTrustEvaluationRequested) {
1665         std::string host = toSTD(adoptWK(WKProtectionSpaceCopyHost(protectionSpace)).get());
1666         return host == "localhost" || host == "127.0.0.1";
1667     }
1668     
1669     return authenticationScheme <= kWKProtectionSpaceAuthenticationSchemeHTTPDigest;
1670 }
1671
1672 void TestController::didFinishNavigation(WKPageRef page, WKNavigationRef navigation)
1673 {
1674     if (m_state != Resetting)
1675         return;
1676
1677     WKRetainPtr<WKURLRef> wkURL(AdoptWK, WKFrameCopyURL(WKPageGetMainFrame(page)));
1678     if (!WKURLIsEqual(wkURL.get(), blankURL()))
1679         return;
1680
1681     m_doneResetting = true;
1682     singleton().notifyDone();
1683 }
1684
1685 void TestController::didReceiveAuthenticationChallenge(WKPageRef page, WKAuthenticationChallengeRef authenticationChallenge)
1686 {
1687     WKProtectionSpaceRef protectionSpace = WKAuthenticationChallengeGetProtectionSpace(authenticationChallenge);
1688     WKAuthenticationDecisionListenerRef decisionListener = WKAuthenticationChallengeGetDecisionListener(authenticationChallenge);
1689
1690     if (WKProtectionSpaceGetAuthenticationScheme(protectionSpace) == kWKProtectionSpaceAuthenticationSchemeServerTrustEvaluationRequested) {
1691         // Any non-empty credential signals to accept the server trust. Since the cross-platform API
1692         // doesn't expose a way to create a credential from server trust, we use a password credential.
1693
1694         WKRetainPtr<WKCredentialRef> credential = adoptWK(WKCredentialCreate(toWK("accept server trust").get(), toWK("").get(), kWKCredentialPersistenceNone));
1695         WKAuthenticationDecisionListenerUseCredential(decisionListener, credential.get());
1696         return;
1697     }
1698
1699     if (m_rejectsProtectionSpaceAndContinueForAuthenticationChallenges) {
1700         m_currentInvocation->outputText("Simulating reject protection space and continue for authentication challenge\n");
1701         WKAuthenticationDecisionListenerRejectProtectionSpaceAndContinue(decisionListener);
1702         return;
1703     }
1704
1705     std::string host = toSTD(adoptWK(WKProtectionSpaceCopyHost(protectionSpace)).get());
1706     int port = WKProtectionSpaceGetPort(protectionSpace);
1707     String message = String::format("%s:%d - didReceiveAuthenticationChallenge - ", host.c_str(), port);
1708     if (!m_handlesAuthenticationChallenges)
1709         message.append("Simulating cancelled authentication sheet\n");
1710     else
1711         message.append(String::format("Responding with %s:%s\n", m_authenticationUsername.utf8().data(), m_authenticationPassword.utf8().data()));
1712     m_currentInvocation->outputText(message);
1713
1714     if (!m_handlesAuthenticationChallenges) {
1715         WKAuthenticationDecisionListenerUseCredential(decisionListener, 0);
1716         return;
1717     }
1718     WKRetainPtr<WKStringRef> username(AdoptWK, WKStringCreateWithUTF8CString(m_authenticationUsername.utf8().data()));
1719     WKRetainPtr<WKStringRef> password(AdoptWK, WKStringCreateWithUTF8CString(m_authenticationPassword.utf8().data()));
1720     WKRetainPtr<WKCredentialRef> credential(AdoptWK, WKCredentialCreate(username.get(), password.get(), kWKCredentialPersistenceForSession));
1721     WKAuthenticationDecisionListenerUseCredential(decisionListener, credential.get());
1722 }
1723
1724     
1725 // WKContextDownloadClient
1726
1727 void TestController::downloadDidStart(WKContextRef context, WKDownloadRef download, const void* clientInfo)
1728 {
1729     static_cast<TestController*>(const_cast<void*>(clientInfo))->downloadDidStart(context, download);
1730 }
1731     
1732 WKStringRef TestController::decideDestinationWithSuggestedFilename(WKContextRef context, WKDownloadRef download, WKStringRef filename, bool* allowOverwrite, const void* clientInfo)
1733 {
1734     return static_cast<TestController*>(const_cast<void*>(clientInfo))->decideDestinationWithSuggestedFilename(context, download, filename, allowOverwrite);
1735 }
1736
1737 void TestController::downloadDidFinish(WKContextRef context, WKDownloadRef download, const void* clientInfo)
1738 {
1739     static_cast<TestController*>(const_cast<void*>(clientInfo))->downloadDidFinish(context, download);
1740 }
1741
1742 void TestController::downloadDidFail(WKContextRef context, WKDownloadRef download, WKErrorRef error, const void* clientInfo)
1743 {
1744     static_cast<TestController*>(const_cast<void*>(clientInfo))->downloadDidFail(context, download, error);
1745 }
1746
1747 void TestController::downloadDidCancel(WKContextRef context, WKDownloadRef download, const void* clientInfo)
1748 {
1749     static_cast<TestController*>(const_cast<void*>(clientInfo))->downloadDidCancel(context, download);
1750 }
1751
1752 void TestController::downloadDidReceiveServerRedirectToURL(WKContextRef context, WKDownloadRef download, WKURLRef url, const void* clientInfo)
1753 {
1754     static_cast<TestController*>(const_cast<void*>(clientInfo))->downloadDidReceiveServerRedirectToURL(context, download, url);
1755 }
1756
1757 void TestController::downloadDidStart(WKContextRef context, WKDownloadRef download)
1758 {
1759     if (m_shouldLogDownloadCallbacks)
1760         m_currentInvocation->outputText("Download started.\n");
1761 }
1762
1763 WKStringRef TestController::decideDestinationWithSuggestedFilename(WKContextRef, WKDownloadRef, WKStringRef filename, bool*& allowOverwrite)
1764 {
1765     String suggestedFilename = toWTFString(filename);
1766
1767     if (m_shouldLogDownloadCallbacks) {
1768         StringBuilder builder;
1769         builder.append("Downloading URL with suggested filename \"");
1770         builder.append(suggestedFilename);
1771         builder.append("\"\n");
1772         m_currentInvocation->outputText(builder.toString());
1773     }
1774
1775     const char* dumpRenderTreeTemp = libraryPathForTesting();
1776     if (!dumpRenderTreeTemp)
1777         return nullptr;
1778
1779     *allowOverwrite = true;
1780     String temporaryFolder = String::fromUTF8(dumpRenderTreeTemp);
1781     if (suggestedFilename.isEmpty())
1782         suggestedFilename = "Unknown";
1783
1784     return toWK(temporaryFolder + "/" + suggestedFilename).leakRef();
1785 }
1786
1787 void TestController::downloadDidFinish(WKContextRef, WKDownloadRef)
1788 {
1789     if (m_shouldLogDownloadCallbacks)
1790         m_currentInvocation->outputText("Download completed.\n");
1791     m_currentInvocation->notifyDownloadDone();
1792 }
1793
1794 void TestController::downloadDidReceiveServerRedirectToURL(WKContextRef, WKDownloadRef, WKURLRef url)
1795 {
1796     if (m_shouldLogDownloadCallbacks) {
1797         StringBuilder builder;
1798         builder.appendLiteral("Download was redirected to \"");
1799         WKRetainPtr<WKStringRef> urlStringWK = adoptWK(WKURLCopyString(url));
1800         builder.append(toSTD(urlStringWK).c_str());
1801         builder.appendLiteral("\".\n");
1802         m_currentInvocation->outputText(builder.toString());
1803     }
1804 }
1805
1806 void TestController::downloadDidFail(WKContextRef, WKDownloadRef, WKErrorRef error)
1807 {
1808     if (m_shouldLogDownloadCallbacks) {
1809         String message = String::format("Download failed.\n");
1810         m_currentInvocation->outputText(message);
1811
1812         WKRetainPtr<WKStringRef> errorDomain = adoptWK(WKErrorCopyDomain(error));
1813         WKRetainPtr<WKStringRef> errorDescription = adoptWK(WKErrorCopyLocalizedDescription(error));
1814         int errorCode = WKErrorGetErrorCode(error);
1815
1816         StringBuilder errorBuilder;
1817         errorBuilder.append("Failed: ");
1818         errorBuilder.append(toWTFString(errorDomain));
1819         errorBuilder.append(", code=");
1820         errorBuilder.appendNumber(errorCode);
1821         errorBuilder.append(", description=");
1822         errorBuilder.append(toWTFString(errorDescription));
1823         errorBuilder.append("\n");
1824
1825         m_currentInvocation->outputText(errorBuilder.toString());
1826     }
1827     m_currentInvocation->notifyDownloadDone();
1828 }
1829
1830 void TestController::downloadDidCancel(WKContextRef, WKDownloadRef)
1831 {
1832     if (m_shouldLogDownloadCallbacks)
1833         m_currentInvocation->outputText("Download cancelled.\n");
1834     m_currentInvocation->notifyDownloadDone();
1835 }
1836
1837 void TestController::processDidCrash()
1838 {
1839     // This function can be called multiple times when crash logs are being saved on Windows, so
1840     // ensure we only print the crashed message once.
1841     if (!m_didPrintWebProcessCrashedMessage) {
1842         pid_t pid = WKPageGetProcessIdentifier(m_mainWebView->page());
1843         fprintf(stderr, "#CRASHED - %s (pid %ld)\n", webProcessName(), static_cast<long>(pid));
1844         fflush(stderr);
1845         m_didPrintWebProcessCrashedMessage = true;
1846     }
1847
1848     if (m_shouldExitWhenWebProcessCrashes)
1849         exit(1);
1850 }
1851
1852 void TestController::didBeginNavigationGesture(WKPageRef)
1853 {
1854     m_currentInvocation->didBeginSwipe();
1855 }
1856
1857 void TestController::willEndNavigationGesture(WKPageRef, WKBackForwardListItemRef)
1858 {
1859     m_currentInvocation->willEndSwipe();
1860 }
1861
1862 void TestController::didEndNavigationGesture(WKPageRef, WKBackForwardListItemRef)
1863 {
1864     m_currentInvocation->didEndSwipe();
1865 }
1866
1867 void TestController::didRemoveNavigationGestureSnapshot(WKPageRef)
1868 {
1869     m_currentInvocation->didRemoveSwipeSnapshot();
1870 }
1871
1872 void TestController::simulateWebNotificationClick(uint64_t notificationID)
1873 {
1874     m_webNotificationProvider.simulateWebNotificationClick(mainWebView()->page(), notificationID);
1875 }
1876
1877 void TestController::setGeolocationPermission(bool enabled)
1878 {
1879     m_isGeolocationPermissionSet = true;
1880     m_isGeolocationPermissionAllowed = enabled;
1881     decidePolicyForGeolocationPermissionRequestIfPossible();
1882 }
1883
1884 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)
1885 {
1886     m_geolocationProvider->setPosition(latitude, longitude, accuracy, providesAltitude, altitude, providesAltitudeAccuracy, altitudeAccuracy, providesHeading, heading, providesSpeed, speed, providesFloorLevel, floorLevel);
1887 }
1888
1889 void TestController::setMockGeolocationPositionUnavailableError(WKStringRef errorMessage)
1890 {
1891     m_geolocationProvider->setPositionUnavailableError(errorMessage);
1892 }
1893
1894 void TestController::handleGeolocationPermissionRequest(WKGeolocationPermissionRequestRef geolocationPermissionRequest)
1895 {
1896     m_geolocationPermissionRequests.append(geolocationPermissionRequest);
1897     decidePolicyForGeolocationPermissionRequestIfPossible();
1898 }
1899
1900 bool TestController::isGeolocationProviderActive() const
1901 {
1902     return m_geolocationProvider->isActive();
1903 }
1904
1905 static String originUserVisibleName(WKSecurityOriginRef origin)
1906 {
1907     if (!origin)
1908         return emptyString();
1909
1910     std::string host = toSTD(adoptWK(WKSecurityOriginCopyHost(origin))).c_str();
1911     std::string protocol = toSTD(adoptWK(WKSecurityOriginCopyProtocol(origin))).c_str();
1912
1913     if (!host.length() || !protocol.length())
1914         return emptyString();
1915
1916     unsigned short port = WKSecurityOriginGetPort(origin);
1917     if (port)
1918         return String::format("%s://%s:%d", protocol.c_str(), host.c_str(), port);
1919
1920     return String::format("%s://%s", protocol.c_str(), host.c_str());
1921 }
1922
1923 static String userMediaOriginHash(WKSecurityOriginRef userMediaDocumentOrigin, WKSecurityOriginRef topLevelDocumentOrigin)
1924 {
1925     String userMediaDocumentOriginString = originUserVisibleName(userMediaDocumentOrigin);
1926     String topLevelDocumentOriginString = originUserVisibleName(topLevelDocumentOrigin);
1927
1928     if (topLevelDocumentOriginString.isEmpty())
1929         return userMediaDocumentOriginString;
1930
1931     return String::format("%s-%s", userMediaDocumentOriginString.utf8().data(), topLevelDocumentOriginString.utf8().data());
1932 }
1933
1934 static String userMediaOriginHash(WKStringRef userMediaDocumentOriginString, WKStringRef topLevelDocumentOriginString)
1935 {
1936     auto userMediaDocumentOrigin = adoptWK(WKSecurityOriginCreateFromString(userMediaDocumentOriginString));
1937     if (!WKStringGetLength(topLevelDocumentOriginString))
1938         return userMediaOriginHash(userMediaDocumentOrigin.get(), nullptr);
1939
1940     auto topLevelDocumentOrigin = adoptWK(WKSecurityOriginCreateFromString(topLevelDocumentOriginString));
1941     return userMediaOriginHash(userMediaDocumentOrigin.get(), topLevelDocumentOrigin.get());
1942 }
1943
1944 void TestController::setUserMediaPermission(bool enabled)
1945 {
1946     m_isUserMediaPermissionSet = true;
1947     m_isUserMediaPermissionAllowed = enabled;
1948     decidePolicyForUserMediaPermissionRequestIfPossible();
1949 }
1950
1951 void TestController::resetUserMediaPermission()
1952 {
1953     m_isUserMediaPermissionSet = false;
1954 }
1955
1956 class OriginSettings : public RefCounted<OriginSettings> {
1957 public:
1958     explicit OriginSettings()
1959     {
1960     }
1961
1962     bool persistentPermission() const { return m_persistentPermission; }
1963     void setPersistentPermission(bool permission) { m_persistentPermission = permission; }
1964
1965     String persistentSalt() const { return m_persistentSalt; }
1966     void setPersistentSalt(const String& salt) { m_persistentSalt = salt; }
1967
1968     HashMap<uint64_t, String>& ephemeralSalts() { return m_ephemeralSalts; }
1969
1970     void incrementRequestCount() { ++m_requestCount; }
1971     void resetRequestCount() { m_requestCount = 0; }
1972     unsigned requestCount() const { return m_requestCount; }
1973
1974 private:
1975     HashMap<uint64_t, String> m_ephemeralSalts;
1976     String m_persistentSalt;
1977     unsigned m_requestCount { 0 };
1978     bool m_persistentPermission { false };
1979 };
1980
1981 String TestController::saltForOrigin(WKFrameRef frame, String originHash)
1982 {
1983     auto& settings = settingsForOrigin(originHash);
1984     auto& ephemeralSalts = settings.ephemeralSalts();
1985     auto frameInfo = adoptWK(WKFrameCreateFrameInfo(frame));
1986     auto frameHandle = WKFrameInfoGetFrameHandleRef(frameInfo.get());
1987     uint64_t frameIdentifier = WKFrameHandleGetFrameID(frameHandle);
1988     String frameSalt = ephemeralSalts.get(frameIdentifier);
1989
1990     if (settings.persistentPermission()) {
1991         if (frameSalt.length())
1992             return frameSalt;
1993
1994         if (!settings.persistentSalt().length())
1995             settings.setPersistentSalt(createCanonicalUUIDString());
1996
1997         return settings.persistentSalt();
1998     }
1999
2000     if (!frameSalt.length()) {
2001         frameSalt = createCanonicalUUIDString();
2002         ephemeralSalts.add(frameIdentifier, frameSalt);
2003     }
2004
2005     return frameSalt;
2006 }
2007
2008 void TestController::setUserMediaPersistentPermissionForOrigin(bool permission, WKStringRef userMediaDocumentOriginString, WKStringRef topLevelDocumentOriginString)
2009 {
2010     auto originHash = userMediaOriginHash(userMediaDocumentOriginString, topLevelDocumentOriginString);
2011     auto& settings = settingsForOrigin(originHash);
2012     settings.setPersistentPermission(permission);
2013 }
2014
2015 void TestController::handleCheckOfUserMediaPermissionForOrigin(WKFrameRef frame, WKSecurityOriginRef userMediaDocumentOrigin, WKSecurityOriginRef topLevelDocumentOrigin, const WKUserMediaPermissionCheckRef& checkRequest)
2016 {
2017     auto originHash = userMediaOriginHash(userMediaDocumentOrigin, topLevelDocumentOrigin);
2018     auto salt = saltForOrigin(frame, originHash);
2019     WKRetainPtr<WKStringRef> saltString = adoptWK(WKStringCreateWithUTF8CString(salt.utf8().data()));
2020
2021     WKUserMediaPermissionCheckSetUserMediaAccessInfo(checkRequest, saltString.get(), settingsForOrigin(originHash).persistentPermission());
2022 }
2023
2024 void TestController::handleUserMediaPermissionRequest(WKFrameRef frame, WKSecurityOriginRef userMediaDocumentOrigin, WKSecurityOriginRef topLevelDocumentOrigin, WKUserMediaPermissionRequestRef request)
2025 {
2026     auto originHash = userMediaOriginHash(userMediaDocumentOrigin, topLevelDocumentOrigin);
2027     m_userMediaPermissionRequests.append(std::make_pair(originHash, request));
2028     decidePolicyForUserMediaPermissionRequestIfPossible();
2029 }
2030
2031 OriginSettings& TestController::settingsForOrigin(const String& originHash)
2032 {
2033     RefPtr<OriginSettings> settings = m_cachedUserMediaPermissions.get(originHash);
2034     if (!settings) {
2035         settings = adoptRef(*new OriginSettings());
2036         m_cachedUserMediaPermissions.add(originHash, settings);
2037     }
2038
2039     return *settings;
2040 }
2041
2042 unsigned TestController::userMediaPermissionRequestCountForOrigin(WKStringRef userMediaDocumentOriginString, WKStringRef topLevelDocumentOriginString)
2043 {
2044     auto originHash = userMediaOriginHash(userMediaDocumentOriginString, topLevelDocumentOriginString);
2045     return settingsForOrigin(originHash).requestCount();
2046 }
2047
2048 void TestController::resetUserMediaPermissionRequestCountForOrigin(WKStringRef userMediaDocumentOriginString, WKStringRef topLevelDocumentOriginString)
2049 {
2050     auto originHash = userMediaOriginHash(userMediaDocumentOriginString, topLevelDocumentOriginString);
2051     settingsForOrigin(originHash).resetRequestCount();
2052 }
2053
2054 void TestController::decidePolicyForUserMediaPermissionRequestIfPossible()
2055 {
2056     if (!m_isUserMediaPermissionSet)
2057         return;
2058
2059     for (auto& pair : m_userMediaPermissionRequests) {
2060         auto originHash = pair.first;
2061         auto request = pair.second.get();
2062
2063         auto& settings = settingsForOrigin(originHash);
2064         settings.incrementRequestCount();
2065
2066         if (!m_isUserMediaPermissionAllowed && !settings.persistentPermission()) {
2067             WKUserMediaPermissionRequestDeny(request, kWKPermissionDenied);
2068             continue;
2069         }
2070
2071         WKRetainPtr<WKArrayRef> audioDeviceUIDs = adoptWK(WKUserMediaPermissionRequestAudioDeviceUIDs(request));
2072         WKRetainPtr<WKArrayRef> videoDeviceUIDs = adoptWK(WKUserMediaPermissionRequestVideoDeviceUIDs(request));
2073
2074         if (!WKArrayGetSize(videoDeviceUIDs.get()) && !WKArrayGetSize(audioDeviceUIDs.get())) {
2075             WKUserMediaPermissionRequestDeny(request, kWKNoConstraints);
2076             continue;
2077         }
2078
2079         WKRetainPtr<WKStringRef> videoDeviceUID;
2080         if (WKArrayGetSize(videoDeviceUIDs.get()))
2081             videoDeviceUID = reinterpret_cast<WKStringRef>(WKArrayGetItemAtIndex(videoDeviceUIDs.get(), 0));
2082         else
2083             videoDeviceUID = adoptWK(WKStringCreateWithUTF8CString(""));
2084
2085         WKRetainPtr<WKStringRef> audioDeviceUID;
2086         if (WKArrayGetSize(audioDeviceUIDs.get()))
2087             audioDeviceUID = reinterpret_cast<WKStringRef>(WKArrayGetItemAtIndex(audioDeviceUIDs.get(), 0));
2088         else
2089             audioDeviceUID = adoptWK(WKStringCreateWithUTF8CString(""));
2090
2091         WKUserMediaPermissionRequestAllow(request, audioDeviceUID.get(), videoDeviceUID.get());
2092     }
2093     m_userMediaPermissionRequests.clear();
2094 }
2095
2096 void TestController::setCustomPolicyDelegate(bool enabled, bool permissive)
2097 {
2098     m_policyDelegateEnabled = enabled;
2099     m_policyDelegatePermissive = permissive;
2100 }
2101
2102 void TestController::decidePolicyForGeolocationPermissionRequestIfPossible()
2103 {
2104     if (!m_isGeolocationPermissionSet)
2105         return;
2106
2107     for (size_t i = 0; i < m_geolocationPermissionRequests.size(); ++i) {
2108         WKGeolocationPermissionRequestRef permissionRequest = m_geolocationPermissionRequests[i].get();
2109         if (m_isGeolocationPermissionAllowed)
2110             WKGeolocationPermissionRequestAllow(permissionRequest);
2111         else
2112             WKGeolocationPermissionRequestDeny(permissionRequest);
2113     }
2114     m_geolocationPermissionRequests.clear();
2115 }
2116
2117 void TestController::decidePolicyForNotificationPermissionRequest(WKPageRef page, WKSecurityOriginRef origin, WKNotificationPermissionRequestRef request, const void*)
2118 {
2119     TestController::singleton().decidePolicyForNotificationPermissionRequest(page, origin, request);
2120 }
2121
2122 void TestController::decidePolicyForNotificationPermissionRequest(WKPageRef, WKSecurityOriginRef, WKNotificationPermissionRequestRef request)
2123 {
2124     WKNotificationPermissionRequestAllow(request);
2125 }
2126
2127 void TestController::unavailablePluginButtonClicked(WKPageRef, WKPluginUnavailabilityReason, WKDictionaryRef, const void*)
2128 {
2129     printf("MISSING PLUGIN BUTTON PRESSED\n");
2130 }
2131
2132 void TestController::decidePolicyForNavigationAction(WKPageRef, WKNavigationActionRef navigationAction, WKFramePolicyListenerRef listener, WKTypeRef, const void* clientInfo)
2133 {
2134     static_cast<TestController*>(const_cast<void*>(clientInfo))->decidePolicyForNavigationAction(listener);
2135 }
2136
2137 void TestController::decidePolicyForNavigationAction(WKFramePolicyListenerRef listener)
2138 {
2139     WKRetainPtr<WKFramePolicyListenerRef> retainedListener { listener };
2140     const bool shouldIgnore { m_policyDelegateEnabled && !m_policyDelegatePermissive };
2141     auto decisionFunction = [shouldIgnore, retainedListener]() {
2142         if (shouldIgnore)
2143             WKFramePolicyListenerIgnore(retainedListener.get());
2144         else
2145             WKFramePolicyListenerUse(retainedListener.get());
2146     };
2147
2148     if (m_shouldDecideNavigationPolicyAfterDelay)
2149         RunLoop::main().dispatch(WTFMove(decisionFunction));
2150     else
2151         decisionFunction();
2152 }
2153
2154 void TestController::decidePolicyForNavigationResponse(WKPageRef, WKNavigationResponseRef navigationResponse, WKFramePolicyListenerRef listener, WKTypeRef, const void* clientInfo)
2155 {
2156     static_cast<TestController*>(const_cast<void*>(clientInfo))->decidePolicyForNavigationResponse(navigationResponse, listener);
2157 }
2158
2159 void TestController::decidePolicyForNavigationResponse(WKNavigationResponseRef navigationResponse, WKFramePolicyListenerRef listener)
2160 {
2161     // Even though Response was already checked by WKBundlePagePolicyClient, the check did not include plugins
2162     // so we have to re-check again.
2163     if (WKNavigationResponseCanShowMIMEType(navigationResponse)) {
2164         WKFramePolicyListenerUse(listener);
2165         return;
2166     }
2167
2168     if (m_shouldDownloadUndisplayableMIMETypes)
2169         WKFramePolicyListenerDownload(listener);
2170     else
2171         WKFramePolicyListenerIgnore(listener);
2172 }
2173
2174 void TestController::didNavigateWithNavigationData(WKContextRef, WKPageRef, WKNavigationDataRef navigationData, WKFrameRef frame, const void* clientInfo)
2175 {
2176     static_cast<TestController*>(const_cast<void*>(clientInfo))->didNavigateWithNavigationData(navigationData, frame);
2177 }
2178
2179 void TestController::didNavigateWithNavigationData(WKNavigationDataRef navigationData, WKFrameRef)
2180 {
2181     if (m_state != RunningTest)
2182         return;
2183
2184     if (!m_shouldLogHistoryClientCallbacks)
2185         return;
2186
2187     // URL
2188     WKRetainPtr<WKURLRef> urlWK = adoptWK(WKNavigationDataCopyURL(navigationData));
2189     WKRetainPtr<WKStringRef> urlStringWK = adoptWK(WKURLCopyString(urlWK.get()));
2190     // Title
2191     WKRetainPtr<WKStringRef> titleWK = adoptWK(WKNavigationDataCopyTitle(navigationData));
2192     // HTTP method
2193     WKRetainPtr<WKURLRequestRef> requestWK = adoptWK(WKNavigationDataCopyOriginalRequest(navigationData));
2194     WKRetainPtr<WKStringRef> methodWK = adoptWK(WKURLRequestCopyHTTPMethod(requestWK.get()));
2195
2196     // FIXME: Determine whether the navigation was successful / a client redirect rather than hard-coding the message here.
2197     m_currentInvocation->outputText(String::format("WebView navigated to url \"%s\" with title \"%s\" with HTTP equivalent method \"%s\".  The navigation was successful and was not a client redirect.\n",
2198         toSTD(urlStringWK).c_str(), toSTD(titleWK).c_str(), toSTD(methodWK).c_str()));
2199 }
2200
2201 void TestController::didPerformClientRedirect(WKContextRef, WKPageRef, WKURLRef sourceURL, WKURLRef destinationURL, WKFrameRef frame, const void* clientInfo)
2202 {
2203     static_cast<TestController*>(const_cast<void*>(clientInfo))->didPerformClientRedirect(sourceURL, destinationURL, frame);
2204 }
2205
2206 void TestController::didPerformClientRedirect(WKURLRef sourceURL, WKURLRef destinationURL, WKFrameRef)
2207 {
2208     if (m_state != RunningTest)
2209         return;
2210
2211     if (!m_shouldLogHistoryClientCallbacks)
2212         return;
2213
2214     WKRetainPtr<WKStringRef> sourceStringWK = adoptWK(WKURLCopyString(sourceURL));
2215     WKRetainPtr<WKStringRef> destinationStringWK = adoptWK(WKURLCopyString(destinationURL));
2216
2217     m_currentInvocation->outputText(String::format("WebView performed a client redirect from \"%s\" to \"%s\".\n", toSTD(sourceStringWK).c_str(), toSTD(destinationStringWK).c_str()));
2218 }
2219
2220 void TestController::didPerformServerRedirect(WKContextRef, WKPageRef, WKURLRef sourceURL, WKURLRef destinationURL, WKFrameRef frame, const void* clientInfo)
2221 {
2222     static_cast<TestController*>(const_cast<void*>(clientInfo))->didPerformServerRedirect(sourceURL, destinationURL, frame);
2223 }
2224
2225 void TestController::didPerformServerRedirect(WKURLRef sourceURL, WKURLRef destinationURL, WKFrameRef)
2226 {
2227     if (m_state != RunningTest)
2228         return;
2229
2230     if (!m_shouldLogHistoryClientCallbacks)
2231         return;
2232
2233     WKRetainPtr<WKStringRef> sourceStringWK = adoptWK(WKURLCopyString(sourceURL));
2234     WKRetainPtr<WKStringRef> destinationStringWK = adoptWK(WKURLCopyString(destinationURL));
2235
2236     m_currentInvocation->outputText(String::format("WebView performed a server redirect from \"%s\" to \"%s\".\n", toSTD(sourceStringWK).c_str(), toSTD(destinationStringWK).c_str()));
2237 }
2238
2239 void TestController::didUpdateHistoryTitle(WKContextRef, WKPageRef, WKStringRef title, WKURLRef URL, WKFrameRef frame, const void* clientInfo)
2240 {
2241     static_cast<TestController*>(const_cast<void*>(clientInfo))->didUpdateHistoryTitle(title, URL, frame);
2242 }
2243
2244 void TestController::didUpdateHistoryTitle(WKStringRef title, WKURLRef URL, WKFrameRef)
2245 {
2246     if (m_state != RunningTest)
2247         return;
2248
2249     if (!m_shouldLogHistoryClientCallbacks)
2250         return;
2251
2252     WKRetainPtr<WKStringRef> urlStringWK(AdoptWK, WKURLCopyString(URL));
2253     m_currentInvocation->outputText(String::format("WebView updated the title for history URL \"%s\" to \"%s\".\n", toSTD(urlStringWK).c_str(), toSTD(title).c_str()));
2254 }
2255
2256 void TestController::setNavigationGesturesEnabled(bool value)
2257 {
2258     m_mainWebView->setNavigationGesturesEnabled(value);
2259 }
2260
2261 void TestController::setIgnoresViewportScaleLimits(bool ignoresViewportScaleLimits)
2262 {
2263     WKPageSetIgnoresViewportScaleLimits(m_mainWebView->page(), ignoresViewportScaleLimits);
2264 }
2265
2266 void TestController::terminateNetworkProcess()
2267 {
2268     WKContextTerminateNetworkProcess(platformContext());
2269 }
2270
2271 #if !PLATFORM(COCOA)
2272 void TestController::platformWillRunTest(const TestInvocation&)
2273 {
2274 }
2275
2276 void TestController::platformCreateWebView(WKPageConfigurationRef configuration, const TestOptions& options)
2277 {
2278     m_mainWebView = std::make_unique<PlatformWebView>(configuration, options);
2279 }
2280
2281 PlatformWebView* TestController::platformCreateOtherPage(PlatformWebView* parentView, WKPageConfigurationRef configuration, const TestOptions& options)
2282 {
2283     return new PlatformWebView(configuration, options);
2284 }
2285
2286 WKContextRef TestController::platformAdjustContext(WKContextRef context, WKContextConfigurationRef contextConfiguration)
2287 {
2288     auto* dataStore = WKContextGetWebsiteDataStore(context);
2289     WKWebsiteDataStoreSetResourceLoadStatisticsEnabled(dataStore, true);
2290     return context;
2291 }
2292
2293 void TestController::platformResetStateToConsistentValues()
2294 {
2295
2296 }
2297
2298 unsigned TestController::imageCountInGeneralPasteboard() const
2299 {
2300     return 0;
2301 }
2302
2303 void TestController::removeAllSessionCredentials()
2304 {
2305 }
2306
2307 #endif
2308
2309 #if PLATFORM(COCOA) && WK_API_ENABLED
2310 struct ClearDOMCacheCallbackContext {
2311     explicit ClearDOMCacheCallbackContext(TestController& controller)
2312         : testController(controller)
2313     {
2314     }
2315
2316     TestController& testController;
2317     bool done { false };
2318 };
2319
2320 static void clearDOMCacheCallback(void* userData)
2321 {
2322     auto* context = static_cast<ClearDOMCacheCallbackContext*>(userData);
2323     context->done = true;
2324     context->testController.notifyDone();
2325 }
2326 #endif
2327
2328 void TestController::clearDOMCache(WKStringRef origin)
2329 {
2330 #if PLATFORM(COCOA) && WK_API_ENABLED
2331     auto websiteDataStore = WKContextGetWebsiteDataStore(platformContext());
2332     ClearDOMCacheCallbackContext context(*this);
2333
2334     auto cacheOrigin = adoptWK(WKSecurityOriginCreateFromString(origin));
2335     WKWebsiteDataStoreRemoveFetchCacheForOrigin(websiteDataStore, cacheOrigin.get(), &context, clearDOMCacheCallback);
2336
2337     if (!context.done)
2338         runUntil(context.done, m_currentInvocation->shortTimeout());
2339 #endif
2340 }
2341
2342 void TestController::clearDOMCaches()
2343 {
2344 #if PLATFORM(COCOA) && WK_API_ENABLED
2345     auto websiteDataStore = WKContextGetWebsiteDataStore(platformContext());
2346     ClearDOMCacheCallbackContext context(*this);
2347
2348     WKWebsiteDataStoreRemoveAllFetchCaches(websiteDataStore, &context, clearDOMCacheCallback);
2349     if (!context.done)
2350         runUntil(context.done, m_currentInvocation->shortTimeout());
2351 #endif
2352 }
2353
2354 struct FetchCacheOriginsCallbackContext {
2355     FetchCacheOriginsCallbackContext(TestController& controller, WKStringRef origin)
2356         : testController(controller)
2357         , origin(origin)
2358     {
2359     }
2360
2361     TestController& testController;
2362     WKStringRef origin;
2363
2364     bool done { false };
2365     bool result { false };
2366 };
2367
2368 static void fetchCacheOriginsCallback(WKArrayRef origins, void* userData)
2369 {
2370     auto* context = static_cast<FetchCacheOriginsCallbackContext*>(userData);
2371     context->done = true;
2372
2373     auto size = WKArrayGetSize(origins);
2374     for (size_t index = 0; index < size && !context->result; ++index) {
2375         WKSecurityOriginRef securityOrigin = reinterpret_cast<WKSecurityOriginRef>(WKArrayGetItemAtIndex(origins, index));
2376         if (WKStringIsEqual(context->origin, adoptWK(WKSecurityOriginCopyToString(securityOrigin)).get()))
2377             context->result = true;
2378     }
2379     context->testController.notifyDone();
2380 }
2381
2382 bool TestController::hasDOMCache(WKStringRef origin)
2383 {
2384     auto* dataStore = WKContextGetWebsiteDataStore(platformContext());
2385     FetchCacheOriginsCallbackContext context(*this, origin);
2386     WKWebsiteDataStoreGetFetchCacheOrigins(dataStore, &context, fetchCacheOriginsCallback);
2387     if (!context.done)
2388         runUntil(context.done, m_currentInvocation->shortTimeout());
2389     return context.result;
2390 }
2391
2392 struct FetchCacheSizeForOriginCallbackContext {
2393     explicit FetchCacheSizeForOriginCallbackContext(TestController& controller)
2394         : testController(controller)
2395     {
2396     }
2397
2398     TestController& testController;
2399
2400     bool done { false };
2401     uint64_t result { 0 };
2402 };
2403
2404 static void fetchCacheSizeForOriginCallback(uint64_t size, void* userData)
2405 {
2406     auto* context = static_cast<FetchCacheSizeForOriginCallbackContext*>(userData);
2407     context->done = true;
2408     context->result = size;
2409     context->testController.notifyDone();
2410 }
2411
2412 uint64_t TestController::domCacheSize(WKStringRef origin)
2413 {
2414     auto* dataStore = WKContextGetWebsiteDataStore(platformContext());
2415     FetchCacheSizeForOriginCallbackContext context(*this);
2416     WKWebsiteDataStoreGetFetchCacheSizeForOrigin(dataStore, origin, &context, fetchCacheSizeForOriginCallback);
2417     if (!context.done)
2418         runUntil(context.done, m_currentInvocation->shortTimeout());
2419     return context.result;
2420 }
2421
2422 #if !PLATFORM(COCOA) || !WK_API_ENABLED
2423
2424 void TestController::setStatisticsLastSeen(WKStringRef host, double seconds)
2425 {
2426     auto* dataStore = WKContextGetWebsiteDataStore(platformContext());
2427     WKWebsiteDataStoreSetStatisticsLastSeen(dataStore, host, seconds);
2428 }
2429
2430 void TestController::setStatisticsPrevalentResource(WKStringRef host, bool value)
2431 {
2432     auto* dataStore = WKContextGetWebsiteDataStore(platformContext());
2433     WKWebsiteDataStoreSetStatisticsPrevalentResource(dataStore, host, value);
2434 }
2435
2436 struct ResourceStatisticsCallbackContext {
2437     explicit ResourceStatisticsCallbackContext(TestController& controller)
2438         : testController(controller)
2439     {
2440     }
2441
2442     TestController& testController;
2443     bool done { false };
2444     bool result { false };
2445 };
2446
2447 static void resourceStatisticsCallback(bool result, void* userData)
2448 {
2449     auto* context = static_cast<ResourceStatisticsCallbackContext*>(userData);
2450     context->result = result;
2451     context->done = true;
2452     context->testController.notifyDone();
2453 }
2454
2455 bool TestController::isStatisticsPrevalentResource(WKStringRef host)
2456 {
2457     auto* dataStore = WKContextGetWebsiteDataStore(platformContext());
2458     ResourceStatisticsCallbackContext context(*this);
2459     WKWebsiteDataStoreIsStatisticsPrevalentResource(dataStore, host, &context, resourceStatisticsCallback);
2460     if (!context.done)
2461         runUntil(context.done, m_currentInvocation->shortTimeout());
2462     return context.result;
2463 }
2464
2465 bool TestController::isStatisticsRegisteredAsSubFrameUnder(WKStringRef, WKStringRef)
2466 {
2467     return false;
2468 }
2469
2470 bool TestController::isStatisticsRegisteredAsRedirectingTo(WKStringRef, WKStringRef)
2471 {
2472     return false;
2473 }
2474
2475 void TestController::setStatisticsHasHadUserInteraction(WKStringRef host, bool value)
2476 {
2477     auto* dataStore = WKContextGetWebsiteDataStore(platformContext());
2478     WKWebsiteDataStoreSetStatisticsHasHadUserInteraction(dataStore, host, value);
2479 }
2480
2481 bool TestController::isStatisticsHasHadUserInteraction(WKStringRef host)
2482 {
2483     auto* dataStore = WKContextGetWebsiteDataStore(platformContext());
2484     ResourceStatisticsCallbackContext context(*this);
2485     WKWebsiteDataStoreIsStatisticsHasHadUserInteraction(dataStore, host, &context, resourceStatisticsCallback);
2486     if (!context.done)
2487         runUntil(context.done, m_currentInvocation->shortTimeout());
2488     return context.result;
2489 }
2490
2491 void TestController::setStatisticsGrandfathered(WKStringRef host, bool value)
2492 {
2493     auto* dataStore = WKContextGetWebsiteDataStore(platformContext());
2494     WKWebsiteDataStoreSetStatisticsGrandfathered(dataStore, host, value);
2495 }
2496
2497 bool TestController::isStatisticsGrandfathered(WKStringRef host)
2498 {
2499     auto* dataStore = WKContextGetWebsiteDataStore(platformContext());
2500     ResourceStatisticsCallbackContext context(*this);
2501     WKWebsiteDataStoreIsStatisticsGrandfathered(dataStore, host, &context, resourceStatisticsCallback);
2502     if (!context.done)
2503         runUntil(context.done, m_currentInvocation->shortTimeout());
2504     return context.result;
2505 }
2506
2507 void TestController::setStatisticsSubframeUnderTopFrameOrigin(WKStringRef host, WKStringRef topFrameHost)
2508 {
2509     auto* dataStore = WKContextGetWebsiteDataStore(platformContext());
2510     WKWebsiteDataStoreSetStatisticsSubframeUnderTopFrameOrigin(dataStore, host, topFrameHost);
2511 }
2512
2513 void TestController::setStatisticsSubresourceUnderTopFrameOrigin(WKStringRef host, WKStringRef topFrameHost)
2514 {
2515     auto* dataStore = WKContextGetWebsiteDataStore(platformContext());
2516     WKWebsiteDataStoreSetStatisticsSubresourceUnderTopFrameOrigin(dataStore, host, topFrameHost);
2517 }
2518
2519 void TestController::setStatisticsSubresourceUniqueRedirectTo(WKStringRef host, WKStringRef hostRedirectedTo)
2520 {
2521     auto* dataStore = WKContextGetWebsiteDataStore(platformContext());
2522     WKWebsiteDataStoreSetStatisticsSubresourceUniqueRedirectTo(dataStore, host, hostRedirectedTo);
2523 }
2524
2525 void TestController::setStatisticsTimeToLiveUserInteraction(double seconds)
2526 {
2527     auto* dataStore = WKContextGetWebsiteDataStore(platformContext());
2528     WKWebsiteDataStoreSetStatisticsTimeToLiveUserInteraction(dataStore, seconds);
2529 }
2530
2531 void TestController::setStatisticsTimeToLiveCookiePartitionFree(double seconds)
2532 {
2533     auto* dataStore = WKContextGetWebsiteDataStore(platformContext());
2534     WKWebsiteDataStoreSetStatisticsTimeToLiveCookiePartitionFree(dataStore, seconds);
2535 }
2536
2537 void TestController::statisticsProcessStatisticsAndDataRecords()
2538 {
2539     auto* dataStore = WKContextGetWebsiteDataStore(platformContext());
2540     WKWebsiteDataStoreStatisticsProcessStatisticsAndDataRecords(dataStore);
2541 }
2542
2543 void TestController::statisticsUpdateCookiePartitioning()
2544 {
2545     auto* dataStore = WKContextGetWebsiteDataStore(platformContext());
2546     WKWebsiteDataStoreStatisticsUpdateCookiePartitioning(dataStore);
2547 }
2548
2549 void TestController::statisticsSetShouldPartitionCookiesForHost(WKStringRef host, bool value)
2550 {
2551     auto* dataStore = WKContextGetWebsiteDataStore(platformContext());
2552     WKWebsiteDataStoreSetStatisticsShouldPartitionCookiesForHost(dataStore, host, value);
2553 }
2554
2555 void TestController::statisticsSubmitTelemetry()
2556 {
2557     auto* dataStore = WKContextGetWebsiteDataStore(platformContext());
2558     WKWebsiteDataStoreStatisticsSubmitTelemetry(dataStore);
2559 }
2560
2561 void TestController::setStatisticsNotifyPagesWhenDataRecordsWereScanned(bool value)
2562 {
2563     auto* dataStore = WKContextGetWebsiteDataStore(platformContext());
2564     WKWebsiteDataStoreSetStatisticsNotifyPagesWhenDataRecordsWereScanned(dataStore, value);
2565 }
2566
2567 void TestController::setStatisticsShouldClassifyResourcesBeforeDataRecordsRemoval(bool value)
2568 {
2569     auto* dataStore = WKContextGetWebsiteDataStore(platformContext());
2570     WKWebsiteDataStoreSetStatisticsShouldClassifyResourcesBeforeDataRecordsRemoval(dataStore, value);
2571 }
2572
2573 void TestController::setStatisticsNotifyPagesWhenTelemetryWasCaptured(bool value)
2574 {
2575     auto* dataStore = WKContextGetWebsiteDataStore(platformContext());
2576     WKWebsiteDataStoreSetStatisticsNotifyPagesWhenTelemetryWasCaptured(dataStore, value);
2577 }
2578
2579 void TestController::setStatisticsMinimumTimeBetweenDataRecordsRemoval(double seconds)
2580 {
2581     auto* dataStore = WKContextGetWebsiteDataStore(platformContext());
2582     WKWebsiteDataStoreSetStatisticsMinimumTimeBetweenDataRecordsRemoval(dataStore, seconds);
2583 }
2584
2585 void TestController::setStatisticsGrandfatheringTime(double seconds)
2586 {
2587     auto* dataStore = WKContextGetWebsiteDataStore(platformContext());
2588     WKWebsiteDataStoreSetStatisticsGrandfatheringTime(dataStore, seconds);
2589 }
2590
2591 void TestController::setStatisticsMaxStatisticsEntries(unsigned entries)
2592 {
2593     auto* dataStore = WKContextGetWebsiteDataStore(platformContext());
2594     WKWebsiteDataStoreSetStatisticsMaxStatisticsEntries(dataStore, entries);
2595 }
2596
2597 void TestController::setStatisticsPruneEntriesDownTo(unsigned entries)
2598 {
2599     auto* dataStore = WKContextGetWebsiteDataStore(platformContext());
2600     WKWebsiteDataStoreSetStatisticsPruneEntriesDownTo(dataStore, entries);
2601 }
2602
2603 void TestController::statisticsClearInMemoryAndPersistentStore()
2604 {
2605     auto* dataStore = WKContextGetWebsiteDataStore(platformContext());
2606     WKWebsiteDataStoreStatisticsClearInMemoryAndPersistentStore(dataStore);
2607 }
2608
2609 void TestController::statisticsClearInMemoryAndPersistentStoreModifiedSinceHours(unsigned hours)
2610 {
2611     auto* dataStore = WKContextGetWebsiteDataStore(platformContext());
2612     WKWebsiteDataStoreStatisticsClearInMemoryAndPersistentStoreModifiedSinceHours(dataStore, hours);
2613 }
2614
2615 void TestController::statisticsClearThroughWebsiteDataRemovalCallback(void* userData)
2616 {
2617     static_cast<TestController*>(userData)->m_currentInvocation->didClearStatisticsThroughWebsiteDataRemoval();
2618 }
2619
2620 void TestController::statisticsClearThroughWebsiteDataRemoval()
2621 {
2622     auto* dataStore = WKContextGetWebsiteDataStore(platformContext());
2623     WKWebsiteDataStoreStatisticsClearThroughWebsiteDataRemoval(dataStore, this, statisticsClearThroughWebsiteDataRemovalCallback);
2624 }
2625
2626 void TestController::statisticsResetToConsistentState()
2627 {
2628     auto* dataStore = WKContextGetWebsiteDataStore(platformContext());
2629     WKWebsiteDataStoreStatisticsResetToConsistentState(dataStore);
2630 }
2631
2632 #endif
2633
2634 } // namespace WTR