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