WebKitTestRunner bundleID should not be cleared before every test
[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(WKPageCopyPageConfiguration(m_mainWebView->page()));
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
796 void TestController::ensureViewSupportsOptionsForTest(const TestInvocation& test)
797 {
798     auto options = test.options();
799
800     if (m_mainWebView) {
801         // Having created another page (via window.open()) prevents process swapping on navigation and it may therefore
802         // cause flakiness to reuse the view.
803         if (!m_createdOtherPage && m_mainWebView->viewSupportsOptions(options))
804             return;
805
806         willDestroyWebView();
807
808         WKPageSetPageUIClient(m_mainWebView->page(), nullptr);
809         WKPageSetPageNavigationClient(m_mainWebView->page(), nullptr);
810         WKPageClose(m_mainWebView->page());
811
812         m_mainWebView = nullptr;
813         m_createdOtherPage = false;
814     }
815
816
817     createWebViewWithOptions(options);
818
819     if (!resetStateToConsistentValues(options, ResetStage::BeforeTest))
820         TestInvocation::dumpWebProcessUnresponsiveness("<unknown> - TestController::run - Failed to reset state to consistent values\n");
821 }
822
823 void TestController::resetPreferencesToConsistentValues(const TestOptions& options)
824 {
825     // Reset preferences
826     WKPreferencesRef preferences = platformPreferences();
827     WKPreferencesResetTestRunnerOverrides(preferences);
828
829     WKPreferencesEnableAllExperimentalFeatures(preferences);
830     for (const auto& experimentalFeature : options.experimentalFeatures)
831         WKPreferencesSetExperimentalFeatureForKey(preferences, experimentalFeature.value, toWK(experimentalFeature.key).get());
832
833     WKPreferencesResetAllInternalDebugFeatures(preferences);
834
835     // Set internal features that have different default values for testing.
836     static WKStringRef asyncOverflowScrollingFeature = WKStringCreateWithUTF8CString("AsyncOverflowScrollingEnabled");
837     WKPreferencesSetInternalDebugFeatureForKey(preferences, false, asyncOverflowScrollingFeature);
838
839     static WKStringRef asyncFrameScrollingFeature = WKStringCreateWithUTF8CString("AsyncFrameScrollingEnabled");
840     WKPreferencesSetInternalDebugFeatureForKey(preferences, false, asyncFrameScrollingFeature);
841
842     for (const auto& internalDebugFeature : options.internalDebugFeatures)
843         WKPreferencesSetInternalDebugFeatureForKey(preferences, internalDebugFeature.value, toWK(internalDebugFeature.key).get());
844
845 #if PLATFORM(COCOA)
846     WKPreferencesSetCaptureVideoInUIProcessEnabled(preferences, options.enableCaptureVideoInUIProcess);
847     WKPreferencesSetCaptureVideoInGPUProcessEnabled(preferences, options.enableCaptureVideoInGPUProcess);
848     WKPreferencesSetCaptureAudioInGPUProcessEnabled(preferences, options.enableCaptureAudioInGPUProcess);
849 #endif
850     WKPreferencesSetProcessSwapOnNavigationEnabled(preferences, options.contextOptions.shouldEnableProcessSwapOnNavigation());
851     WKPreferencesSetPageVisibilityBasedProcessSuppressionEnabled(preferences, options.enableAppNap);
852     WKPreferencesSetOfflineWebApplicationCacheEnabled(preferences, true);
853     WKPreferencesSetSubpixelAntialiasedLayerTextEnabled(preferences, false);
854     WKPreferencesSetXSSAuditorEnabled(preferences, false);
855     WKPreferencesSetWebAudioEnabled(preferences, true);
856     WKPreferencesSetMediaDevicesEnabled(preferences, true);
857     WKPreferencesSetWebRTCMDNSICECandidatesEnabled(preferences, false);
858     WKPreferencesSetDeveloperExtrasEnabled(preferences, true);
859     WKPreferencesSetJavaScriptRuntimeFlags(preferences, kWKJavaScriptRuntimeFlagsAllEnabled);
860     WKPreferencesSetJavaScriptCanOpenWindowsAutomatically(preferences, true);
861     WKPreferencesSetJavaScriptCanAccessClipboard(preferences, true);
862     WKPreferencesSetDOMPasteAllowed(preferences, options.domPasteAllowed);
863     WKPreferencesSetUniversalAccessFromFileURLsAllowed(preferences, true);
864     WKPreferencesSetFileAccessFromFileURLsAllowed(preferences, true);
865     WKPreferencesSetTopNavigationToDataURLsAllowed(preferences, options.allowTopNavigationToDataURLs);
866 #if ENABLE(FULLSCREEN_API)
867     WKPreferencesSetFullScreenEnabled(preferences, true);
868 #endif
869     WKPreferencesSetAsynchronousPluginInitializationEnabled(preferences, false);
870     WKPreferencesSetAsynchronousPluginInitializationEnabledForAllPlugins(preferences, false);
871     WKPreferencesSetArtificialPluginInitializationDelayEnabled(preferences, false);
872     WKPreferencesSetTabToLinksEnabled(preferences, false);
873     WKPreferencesSetInteractiveFormValidationEnabled(preferences, true);
874     WKPreferencesSetDataTransferItemsEnabled(preferences, true);
875     WKPreferencesSetCustomPasteboardDataEnabled(preferences, true);
876     WKPreferencesSetDialogElementEnabled(preferences, true);
877
878     WKPreferencesSetMockScrollbarsEnabled(preferences, options.useMockScrollbars);
879     WKPreferencesSetNeedsSiteSpecificQuirks(preferences, options.needsSiteSpecificQuirks);
880     WKPreferencesSetAttachmentElementEnabled(preferences, options.enableAttachmentElement);
881     WKPreferencesSetMenuItemElementEnabled(preferences, options.enableMenuItemElement);
882     WKPreferencesSetKeygenElementEnabled(preferences, options.enableKeygenElement);
883     WKPreferencesSetModernMediaControlsEnabled(preferences, options.enableModernMediaControls);
884     WKPreferencesSetWebAuthenticationEnabled(preferences, options.enableWebAuthentication);
885     WKPreferencesSetWebAuthenticationLocalAuthenticatorEnabled(preferences, options.enableWebAuthenticationLocalAuthenticator);
886     WKPreferencesSetIsSecureContextAttributeEnabled(preferences, options.enableIsSecureContextAttribute);
887     WKPreferencesSetAllowCrossOriginSubresourcesToAskForCredentials(preferences, options.allowCrossOriginSubresourcesToAskForCredentials);
888     WKPreferencesSetColorFilterEnabled(preferences, options.enableColorFilter);
889     WKPreferencesSetPunchOutWhiteBackgroundsInDarkMode(preferences, options.punchOutWhiteBackgroundsInDarkMode);
890     WKPreferencesSetPageCacheEnabled(preferences, options.enableBackForwardCache);
891     WKPreferencesSetLazyImageLoadingEnabled(preferences, options.enableLazyImageLoading);
892
893     static WKStringRef defaultTextEncoding = WKStringCreateWithUTF8CString("ISO-8859-1");
894     WKPreferencesSetDefaultTextEncodingName(preferences, defaultTextEncoding);
895
896     static WKStringRef standardFontFamily = WKStringCreateWithUTF8CString("Times");
897     static WKStringRef cursiveFontFamily = WKStringCreateWithUTF8CString("Apple Chancery");
898     static WKStringRef fantasyFontFamily = WKStringCreateWithUTF8CString("Papyrus");
899     static WKStringRef fixedFontFamily = WKStringCreateWithUTF8CString("Courier");
900     static WKStringRef pictographFontFamily = WKStringCreateWithUTF8CString("Apple Color Emoji");
901     static WKStringRef sansSerifFontFamily = WKStringCreateWithUTF8CString("Helvetica");
902     static WKStringRef serifFontFamily = WKStringCreateWithUTF8CString("Times");
903
904     WKPreferencesSetMinimumFontSize(preferences, 0);
905     WKPreferencesSetStandardFontFamily(preferences, standardFontFamily);
906     WKPreferencesSetCursiveFontFamily(preferences, cursiveFontFamily);
907     WKPreferencesSetFantasyFontFamily(preferences, fantasyFontFamily);
908     WKPreferencesSetFixedFontFamily(preferences, fixedFontFamily);
909     WKPreferencesSetPictographFontFamily(preferences, pictographFontFamily);
910     WKPreferencesSetSansSerifFontFamily(preferences, sansSerifFontFamily);
911     WKPreferencesSetSerifFontFamily(preferences, serifFontFamily);
912     WKPreferencesSetAsynchronousSpellCheckingEnabled(preferences, false);
913 #if ENABLE(MEDIA_SOURCE)
914     WKPreferencesSetMediaSourceEnabled(preferences, true);
915     WKPreferencesSetSourceBufferChangeTypeEnabled(preferences, true);
916 #endif
917     WKPreferencesSetHighlightAPIEnabled(preferences, true);
918
919     WKPreferencesSetHiddenPageDOMTimerThrottlingEnabled(preferences, false);
920     WKPreferencesSetHiddenPageCSSAnimationSuspensionEnabled(preferences, false);
921
922     WKPreferencesSetAcceleratedDrawingEnabled(preferences, m_shouldUseAcceleratedDrawing || options.useAcceleratedDrawing);
923     // FIXME: We should be testing the default.
924     WKPreferencesSetStorageBlockingPolicy(preferences, kWKAllowAllStorage);
925
926     WKPreferencesSetIsNSURLSessionWebSocketEnabled(preferences, false);
927
928     WKPreferencesSetFetchAPIKeepAliveEnabled(preferences, true);
929     WKPreferencesSetResourceTimingEnabled(preferences, true);
930     WKPreferencesSetUserTimingEnabled(preferences, true);
931     WKPreferencesSetMediaPreloadingEnabled(preferences, true);
932     WKPreferencesSetMediaPlaybackAllowsInline(preferences, true);
933     WKPreferencesSetInlineMediaPlaybackRequiresPlaysInlineAttribute(preferences, false);
934     WKPreferencesSetRemotePlaybackEnabled(preferences, true);
935     WKPreferencesSetBeaconAPIEnabled(preferences, true);
936     WKPreferencesSetDirectoryUploadEnabled(preferences, true);
937
938     WKHTTPCookieStoreDeleteAllCookies(WKWebsiteDataStoreGetHTTPCookieStore(TestController::defaultWebsiteDataStore()), nullptr, nullptr);
939
940     WKPreferencesSetMockCaptureDevicesEnabled(preferences, true);
941     
942     WKPreferencesSetLargeImageAsyncDecodingEnabled(preferences, false);
943
944     WKPreferencesSetInspectorAdditionsEnabled(preferences, options.enableInspectorAdditions);
945
946     WKPreferencesSetStorageAccessAPIEnabled(preferences, true);
947     
948     WKPreferencesSetAccessibilityObjectModelEnabled(preferences, true);
949     WKPreferencesSetCSSOMViewScrollingAPIEnabled(preferences, true);
950     WKPreferencesSetMediaCapabilitiesEnabled(preferences, true);
951
952     WKPreferencesSetRestrictedHTTPResponseAccess(preferences, true);
953
954     WKPreferencesSetServerTimingEnabled(preferences, true);
955
956     WKPreferencesSetWebSQLDisabled(preferences, false);
957
958     WKPreferencesSetMediaPlaybackRequiresUserGesture(preferences, false);
959     WKPreferencesSetVideoPlaybackRequiresUserGesture(preferences, false);
960     WKPreferencesSetAudioPlaybackRequiresUserGesture(preferences, false);
961
962     WKPreferencesSetShouldUseServiceWorkerShortTimeout(preferences, options.contextOptions.useServiceWorkerShortTimeout);
963
964 #if ENABLE(ACCESSIBILITY_ISOLATED_TREE)
965     WKPreferencesSetIsAccessibilityIsolatedTreeEnabled(preferences, accessibilityIsolatedTreeMode());
966 #endif
967     
968     platformResetPreferencesToConsistentValues();
969 }
970
971 bool TestController::resetStateToConsistentValues(const TestOptions& options, ResetStage resetStage)
972 {
973     SetForScope<State> changeState(m_state, Resetting);
974     m_beforeUnloadReturnValue = true;
975
976     // This setting differs between the antique and modern Mac WebKit2 API.
977     // For now, maintain the antique behavior, because some tests depend on it!
978     // FIXME: We should be testing the default.
979     WKPageSetBackgroundExtendsBeyondPage(m_mainWebView->page(), false);
980
981     WKPageSetCustomUserAgent(m_mainWebView->page(), nullptr);
982
983     WKRetainPtr<WKStringRef> messageName = adoptWK(WKStringCreateWithUTF8CString("Reset"));
984     WKRetainPtr<WKMutableDictionaryRef> resetMessageBody = adoptWK(WKMutableDictionaryCreate());
985
986     WKRetainPtr<WKStringRef> shouldGCKey = adoptWK(WKStringCreateWithUTF8CString("ShouldGC"));
987     WKRetainPtr<WKBooleanRef> shouldGCValue = adoptWK(WKBooleanCreate(m_gcBetweenTests));
988     WKDictionarySetItem(resetMessageBody.get(), shouldGCKey.get(), shouldGCValue.get());
989
990     WKRetainPtr<WKStringRef> allowedHostsKey = adoptWK(WKStringCreateWithUTF8CString("AllowedHosts"));
991     WKRetainPtr<WKMutableArrayRef> allowedHostsValue = adoptWK(WKMutableArrayCreate());
992     for (auto& host : m_allowedHosts) {
993         WKRetainPtr<WKStringRef> wkHost = adoptWK(WKStringCreateWithUTF8CString(host.c_str()));
994         WKArrayAppendItem(allowedHostsValue.get(), wkHost.get());
995     }
996     WKDictionarySetItem(resetMessageBody.get(), allowedHostsKey.get(), allowedHostsValue.get());
997
998 #if ENABLE(ACCESSIBILITY_ISOLATED_TREE)
999     WKRetainPtr<WKStringRef> axIsolatedModeKey = adoptWK(WKStringCreateWithUTF8CString("AccessibilityIsolatedTree"));
1000     WKRetainPtr<WKBooleanRef> axIsolatedModeValue = adoptWK(WKBooleanCreate(m_accessibilityIsolatedTreeMode));
1001     WKDictionarySetItem(resetMessageBody.get(), axIsolatedModeKey.get(), axIsolatedModeValue.get());
1002 #endif
1003     
1004     if (options.jscOptions.length()) {
1005         WKRetainPtr<WKStringRef> jscOptionsKey = adoptWK(WKStringCreateWithUTF8CString("JSCOptions"));
1006         WKRetainPtr<WKStringRef> jscOptionsValue = adoptWK(WKStringCreateWithUTF8CString(options.jscOptions.c_str()));
1007         WKDictionarySetItem(resetMessageBody.get(), jscOptionsKey.get(), jscOptionsValue.get());
1008     }
1009
1010 #if PLATFORM(COCOA)
1011     WebCoreTestSupport::setAdditionalSupportedImageTypesForTesting(options.additionalSupportedImageTypes.c_str());
1012 #endif
1013
1014     WKPagePostMessageToInjectedBundle(TestController::singleton().mainWebView()->page(), messageName.get(), resetMessageBody.get());
1015
1016     WKContextSetShouldUseFontSmoothing(TestController::singleton().context(), false);
1017
1018     WKContextSetCacheModel(TestController::singleton().context(), kWKCacheModelDocumentBrowser);
1019
1020     WKContextClearCachedCredentials(TestController::singleton().context());
1021
1022     WKContextResetServiceWorkerFetchTimeoutForTesting(TestController::singleton().context());
1023
1024     WKWebsiteDataStoreClearAllDeviceOrientationPermissions(websiteDataStore());
1025     WKWebsiteDataStoreClearAllDeviceOrientationPermissions(TestController::defaultWebsiteDataStore());
1026
1027     clearIndexedDatabases();
1028     clearLocalStorage();
1029
1030     clearServiceWorkerRegistrations();
1031     clearDOMCaches();
1032
1033     resetQuota();
1034
1035     WKContextSetAllowsAnySSLCertificateForServiceWorkerTesting(platformContext(), true);
1036
1037     WKContextClearCurrentModifierStateForTesting(TestController::singleton().context());
1038
1039     WKContextSetUseSeparateServiceWorkerProcess(TestController::singleton().context(), false);
1040
1041     WKPageSetMockCameraOrientation(m_mainWebView->page(), 0);
1042
1043     // FIXME: This function should also ensure that there is only one page open.
1044
1045     // Reset the EventSender for each test.
1046     m_eventSenderProxy = makeUnique<EventSenderProxy>(this);
1047
1048     // FIXME: Is this needed? Nothing in TestController changes preferences during tests, and if there is
1049     // some other code doing this, it should probably be responsible for cleanup too.
1050     resetPreferencesToConsistentValues(options);
1051
1052 #if PLATFORM(GTK)
1053     WKTextCheckerContinuousSpellCheckingEnabledStateChanged(true);
1054 #endif
1055
1056     // Make sure the view is in the window (a test can unparent it).
1057     m_mainWebView->addToWindow();
1058
1059     // In the case that a test using the chrome input field failed, be sure to clean up for the next test.
1060     m_mainWebView->removeChromeInputField();
1061     m_mainWebView->focus();
1062
1063     // Re-set to the default backing scale factor by setting the custom scale factor to 0.
1064     WKPageSetCustomBackingScaleFactor(m_mainWebView->page(), 0);
1065
1066     WKPageClearWheelEventTestMonitor(m_mainWebView->page());
1067
1068     WKPageSetMuted(m_mainWebView->page(), true);
1069
1070     WKPageClearUserMediaState(m_mainWebView->page());
1071
1072     // Reset notification permissions
1073     m_webNotificationProvider.reset();
1074
1075     // Reset Geolocation permissions.
1076     m_geolocationPermissionRequests.clear();
1077     m_isGeolocationPermissionSet = false;
1078     m_isGeolocationPermissionAllowed = false;
1079
1080     // Reset UserMedia permissions.
1081     m_userMediaPermissionRequests.clear();
1082     m_cachedUserMediaPermissions.clear();
1083     setUserMediaPermission(true);
1084
1085     // Reset Custom Policy Delegate.
1086     setCustomPolicyDelegate(false, false);
1087
1088     // Reset Content Extensions.
1089     resetContentExtensions();
1090
1091     m_shouldDownloadUndisplayableMIMETypes = false;
1092
1093     m_shouldAllowDeviceOrientationAndMotionAccess = false;
1094
1095     m_workQueueManager.clearWorkQueue();
1096
1097     m_rejectsProtectionSpaceAndContinueForAuthenticationChallenges = false;
1098     m_handlesAuthenticationChallenges = false;
1099     m_authenticationUsername = String();
1100     m_authenticationPassword = String();
1101
1102     setBlockAllPlugins(false);
1103     setPluginSupportedMode({ });
1104
1105     m_shouldLogDownloadCallbacks = false;
1106     m_shouldLogHistoryClientCallbacks = false;
1107     m_shouldLogCanAuthenticateAgainstProtectionSpace = false;
1108
1109     setHidden(false);
1110
1111     if (!platformResetStateToConsistentValues(options))
1112         return false;
1113
1114     m_shouldDecideNavigationPolicyAfterDelay = false;
1115     m_shouldDecideResponsePolicyAfterDelay = false;
1116
1117     setNavigationGesturesEnabled(false);
1118     
1119     setIgnoresViewportScaleLimits(options.ignoresViewportScaleLimits);
1120
1121     m_openPanelFileURLs = nullptr;
1122 #if PLATFORM(IOS_FAMILY)
1123     m_openPanelFileURLsMediaIcon = nullptr;
1124 #endif
1125
1126     setAllowsAnySSLCertificate(true);
1127
1128     statisticsResetToConsistentState();
1129     clearPrevalentDomains();
1130
1131     clearAdClickAttribution();
1132
1133     m_didReceiveServerRedirectForProvisionalNavigation = false;
1134     m_serverTrustEvaluationCallbackCallsCount = 0;
1135     m_shouldDismissJavaScriptAlertsAsynchronously = false;
1136
1137     // Reset main page back to about:blank
1138     m_doneResetting = false;
1139     WKPageLoadURL(m_mainWebView->page(), blankURL());
1140     runUntil(m_doneResetting, m_currentInvocation->shortTimeout());
1141     if (!m_doneResetting)
1142         return false;
1143     
1144     if (resetStage == ResetStage::AfterTest)
1145         updateLiveDocumentsAfterTest();
1146
1147     return m_doneResetting;
1148 }
1149
1150 void TestController::updateLiveDocumentsAfterTest()
1151 {
1152     if (!m_checkForWorldLeaks)
1153         return;
1154
1155     AsyncTask([]() {
1156         // After each test, we update the list of live documents so that we can detect when an abandoned document first showed up.
1157         WKRetainPtr<WKStringRef> messageName = adoptWK(WKStringCreateWithUTF8CString("GetLiveDocuments"));
1158         WKPagePostMessageToInjectedBundle(TestController::singleton().mainWebView()->page(), messageName.get(), nullptr);
1159     }, 5_s).run();
1160 }
1161
1162 void TestController::checkForWorldLeaks()
1163 {
1164     if (!m_checkForWorldLeaks || !TestController::singleton().mainWebView())
1165         return;
1166
1167     AsyncTask([]() {
1168         // This runs at the end of a series of tests. It clears caches, runs a GC and then fetches the list of documents.
1169         WKRetainPtr<WKStringRef> messageName = adoptWK(WKStringCreateWithUTF8CString("CheckForWorldLeaks"));
1170         WKPagePostMessageToInjectedBundle(TestController::singleton().mainWebView()->page(), messageName.get(), nullptr);
1171     }, 20_s).run();
1172 }
1173
1174 void TestController::dumpResponse(const String& result)
1175 {
1176     unsigned resultLength = result.length();
1177     printf("Content-Type: text/plain\n");
1178     printf("Content-Length: %u\n", resultLength);
1179     fwrite(result.utf8().data(), 1, resultLength, stdout);
1180     printf("#EOF\n");
1181     fprintf(stderr, "#EOF\n");
1182     fflush(stdout);
1183     fflush(stderr);
1184 }
1185
1186 void TestController::findAndDumpWebKitProcessIdentifiers()
1187 {
1188 #if PLATFORM(COCOA)
1189     dumpResponse(makeString(TestController::webProcessName(), ": ",
1190         WKPageGetProcessIdentifier(TestController::singleton().mainWebView()->page()), '\n',
1191         TestController::networkProcessName(), ": ",
1192         WKContextGetNetworkProcessIdentifier(m_context.get()), '\n'));
1193 #else
1194     dumpResponse("\n"_s);
1195 #endif
1196 }
1197
1198 void TestController::findAndDumpWorldLeaks()
1199 {
1200     if (!m_checkForWorldLeaks)
1201         return;
1202
1203     checkForWorldLeaks();
1204
1205     StringBuilder builder;
1206     
1207     if (m_abandonedDocumentInfo.size()) {
1208         for (const auto& it : m_abandonedDocumentInfo) {
1209             auto documentURL = it.value.abandonedDocumentURL;
1210             if (documentURL.isEmpty())
1211                 documentURL = "(no url)";
1212             builder.append("TEST: ");
1213             builder.append(it.value.testURL);
1214             builder.append('\n');
1215             builder.append("ABANDONED DOCUMENT: ");
1216             builder.append(documentURL);
1217             builder.append('\n');
1218         }
1219     } else
1220         builder.append("no abandoned documents\n");
1221
1222     dumpResponse(builder.toString());
1223 }
1224
1225 void TestController::willDestroyWebView()
1226 {
1227     // Before we kill the web view, look for abandoned documents before that web process goes away.
1228     checkForWorldLeaks();
1229 }
1230
1231 void TestController::terminateWebContentProcess()
1232 {
1233     WKPageTerminate(m_mainWebView->page());
1234 }
1235
1236 void TestController::reattachPageToWebProcess()
1237 {
1238     // Loading a web page is the only way to reattach an existing page to a process.
1239     SetForScope<State> changeState(m_state, Resetting);
1240     m_doneResetting = false;
1241     WKPageLoadURL(m_mainWebView->page(), blankURL());
1242     runUntil(m_doneResetting, noTimeout);
1243 }
1244
1245 const char* TestController::webProcessName()
1246 {
1247     // FIXME: Find a way to not hardcode the process name.
1248 #if PLATFORM(IOS_FAMILY) && !PLATFORM(IOS_FAMILY_SIMULATOR)
1249     return "com.apple.WebKit.WebContent";
1250 #elif PLATFORM(COCOA)
1251     return "com.apple.WebKit.WebContent.Development";
1252 #elif PLATFORM(GTK)
1253     return "WebKitWebProcess";
1254 #elif PLATFORM(WPE)
1255     return "WPEWebProcess";
1256 #else
1257     return "WebProcess";
1258 #endif
1259 }
1260
1261 const char* TestController::networkProcessName()
1262 {
1263     // FIXME: Find a way to not hardcode the process name.
1264 #if PLATFORM(IOS_FAMILY) && !PLATFORM(IOS_FAMILY_SIMULATOR)
1265     return "com.apple.WebKit.Networking";
1266 #elif PLATFORM(COCOA)
1267     return "com.apple.WebKit.Networking.Development";
1268 #elif PLATFORM(GTK)
1269     return "WebKitNetworkProcess";
1270 #elif PLATFORM(WPE)
1271     return "WPENetworkProcess";
1272 #else
1273     return "NetworkProcess";
1274 #endif
1275 }
1276
1277 #if !PLATFORM(COCOA)
1278 void TestController::setAllowsAnySSLCertificate(bool allows)
1279 {
1280     m_allowsAnySSLCertificate = allows;
1281     WKContextSetAllowsAnySSLCertificateForWebSocketTesting(platformContext(), allows);
1282 }
1283 #endif
1284
1285 static std::string testPath(WKURLRef url)
1286 {
1287     auto scheme = adoptWK(WKURLCopyScheme(url));
1288     if (WKStringIsEqualToUTF8CStringIgnoringCase(scheme.get(), "file")) {
1289         auto path = adoptWK(WKURLCopyPath(url));
1290         auto buffer = std::vector<char>(WKStringGetMaximumUTF8CStringSize(path.get()));
1291         auto length = WKStringGetUTF8CString(path.get(), buffer.data(), buffer.size());
1292         RELEASE_ASSERT(length > 0);
1293 #if OS(WINDOWS)
1294         // Remove the first '/' if it starts with something like "/C:/".
1295         if (length >= 4 && buffer[0] == '/' && buffer[2] == ':' && buffer[3] == '/')
1296             return std::string(buffer.data() + 1, length - 1);
1297 #endif
1298         return std::string(buffer.data(), length - 1);
1299     }
1300     return std::string();
1301 }
1302
1303 static WKURLRef createTestURL(const char* pathOrURL)
1304 {
1305     if (strstr(pathOrURL, "http://") || strstr(pathOrURL, "https://") || strstr(pathOrURL, "file://"))
1306         return WKURLCreateWithUTF8CString(pathOrURL);
1307
1308     // Creating from filesytem path.
1309     size_t length = strlen(pathOrURL);
1310     if (!length)
1311         return 0;
1312
1313 #if PLATFORM(WIN)
1314     bool isAbsolutePath = false;
1315     if (strlen(pathOrURL) >= 3 && pathOrURL[1] == ':' && pathOrURL[2] == pathSeparator)
1316         isAbsolutePath = true;
1317 #else
1318     bool isAbsolutePath = pathOrURL[0] == pathSeparator;
1319 #endif
1320     const char* filePrefix = "file://";
1321     static const size_t prefixLength = strlen(filePrefix);
1322
1323     UniqueArray<char> buffer;
1324     if (isAbsolutePath) {
1325         buffer = makeUniqueArray<char>(prefixLength + length + 1);
1326         strcpy(buffer.get(), filePrefix);
1327         strcpy(buffer.get() + prefixLength, pathOrURL);
1328     } else {
1329         buffer = makeUniqueArray<char>(prefixLength + PATH_MAX + length + 2); // 1 for the pathSeparator
1330         strcpy(buffer.get(), filePrefix);
1331         if (!getcwd(buffer.get() + prefixLength, PATH_MAX))
1332             return 0;
1333         size_t numCharacters = strlen(buffer.get());
1334         buffer[numCharacters] = pathSeparator;
1335         strcpy(buffer.get() + numCharacters + 1, pathOrURL);
1336     }
1337
1338     return WKURLCreateWithUTF8CString(buffer.get());
1339 }
1340
1341 static bool parseBooleanTestHeaderValue(const std::string& value)
1342 {
1343     if (value == "true")
1344         return true;
1345     if (value == "false")
1346         return false;
1347
1348     LOG_ERROR("Found unexpected value '%s' for boolean option. Expected 'true' or 'false'.", value.c_str());
1349     return false;
1350 }
1351
1352 static std::string parseStringTestHeaderValueAsRelativePath(const std::string& value, const std::string& pathOrURL)
1353 {
1354     WKRetainPtr<WKURLRef> baseURL = adoptWK(createTestURL(pathOrURL.c_str()));
1355     WKRetainPtr<WKURLRef> relativeURL = adoptWK(WKURLCreateWithBaseURL(baseURL.get(), value.c_str()));
1356     return toSTD(adoptWK(WKURLCopyPath(relativeURL.get())));
1357 }
1358
1359 static void updateTestOptionsFromTestHeader(TestOptions& testOptions, const std::string& pathOrURL, const std::string& absolutePath)
1360 {
1361     std::string filename = absolutePath;
1362     if (filename.empty()) {
1363         // Gross. Need to reduce conversions between all the string types and URLs.
1364         WKRetainPtr<WKURLRef> wkURL = adoptWK(createTestURL(pathOrURL.c_str()));
1365         filename = testPath(wkURL.get());
1366     }
1367
1368     if (filename.empty())
1369         return;
1370
1371     std::string options;
1372     std::ifstream testFile(filename.data());
1373     if (!testFile.good())
1374         return;
1375     getline(testFile, options);
1376     std::string beginString("webkit-test-runner [ ");
1377     std::string endString(" ]");
1378     size_t beginLocation = options.find(beginString);
1379     if (beginLocation == std::string::npos)
1380         return;
1381     size_t endLocation = options.find(endString, beginLocation);
1382     if (endLocation == std::string::npos) {
1383         LOG_ERROR("Could not find end of test header in %s", filename.c_str());
1384         return;
1385     }
1386     std::string pairString = options.substr(beginLocation + beginString.size(), endLocation - (beginLocation + beginString.size()));
1387     size_t pairStart = 0;
1388     while (pairStart < pairString.size()) {
1389         size_t pairEnd = pairString.find(" ", pairStart);
1390         if (pairEnd == std::string::npos)
1391             pairEnd = pairString.size();
1392         size_t equalsLocation = pairString.find("=", pairStart);
1393         if (equalsLocation == std::string::npos) {
1394             LOG_ERROR("Malformed option in test header (could not find '=' character) in %s", filename.c_str());
1395             break;
1396         }
1397         auto key = pairString.substr(pairStart, equalsLocation - pairStart);
1398         auto value = pairString.substr(equalsLocation + 1, pairEnd - (equalsLocation + 1));
1399
1400         if (!key.rfind("experimental:")) {
1401             key = key.substr(13);
1402             testOptions.experimentalFeatures.add(String(key.c_str()), parseBooleanTestHeaderValue(value));
1403         }
1404
1405         if (!key.rfind("internal:")) {
1406             key = key.substr(9);
1407             testOptions.internalDebugFeatures.add(String(key.c_str()), parseBooleanTestHeaderValue(value));
1408         }
1409
1410         if (key == "language")
1411             testOptions.contextOptions.overrideLanguages = String(value.c_str()).split(',');
1412         else if (key == "useThreadedScrolling")
1413             testOptions.useThreadedScrolling = parseBooleanTestHeaderValue(value);
1414         else if (key == "useAcceleratedDrawing")
1415             testOptions.useAcceleratedDrawing = parseBooleanTestHeaderValue(value);
1416         else if (key == "useFlexibleViewport")
1417             testOptions.useFlexibleViewport = parseBooleanTestHeaderValue(value);
1418         else if (key == "useDataDetection")
1419             testOptions.useDataDetection = parseBooleanTestHeaderValue(value);
1420         else if (key == "useMockScrollbars")
1421             testOptions.useMockScrollbars = parseBooleanTestHeaderValue(value);
1422         else if (key == "needsSiteSpecificQuirks")
1423             testOptions.needsSiteSpecificQuirks = parseBooleanTestHeaderValue(value);
1424         else if (key == "ignoresViewportScaleLimits")
1425             testOptions.ignoresViewportScaleLimits = parseBooleanTestHeaderValue(value);
1426         else if (key == "useCharacterSelectionGranularity")
1427             testOptions.useCharacterSelectionGranularity = parseBooleanTestHeaderValue(value);
1428         else if (key == "enableAttachmentElement")
1429             testOptions.enableAttachmentElement = parseBooleanTestHeaderValue(value);
1430         else if (key == "enableIntersectionObserver")
1431             testOptions.enableIntersectionObserver = parseBooleanTestHeaderValue(value);
1432         else if (key == "useEphemeralSession")
1433             testOptions.useEphemeralSession = parseBooleanTestHeaderValue(value);
1434         else if (key == "enableMenuItemElement")
1435             testOptions.enableMenuItemElement = parseBooleanTestHeaderValue(value);
1436         else if (key == "enableKeygenElement")
1437             testOptions.enableKeygenElement = parseBooleanTestHeaderValue(value);
1438         else if (key == "enableModernMediaControls")
1439             testOptions.enableModernMediaControls = parseBooleanTestHeaderValue(value);
1440         else if (key == "enablePointerLock")
1441             testOptions.enablePointerLock = parseBooleanTestHeaderValue(value);
1442         else if (key == "enableWebAuthentication")
1443             testOptions.enableWebAuthentication = parseBooleanTestHeaderValue(value);
1444         else if (key == "enableWebAuthenticationLocalAuthenticator")
1445             testOptions.enableWebAuthenticationLocalAuthenticator = parseBooleanTestHeaderValue(value);
1446         else if (key == "enableIsSecureContextAttribute")
1447             testOptions.enableIsSecureContextAttribute = parseBooleanTestHeaderValue(value);
1448         else if (key == "enableInspectorAdditions")
1449             testOptions.enableInspectorAdditions = parseBooleanTestHeaderValue(value);
1450         else if (key == "dumpJSConsoleLogInStdErr")
1451             testOptions.dumpJSConsoleLogInStdErr = parseBooleanTestHeaderValue(value);
1452         else if (key == "applicationManifest")
1453             testOptions.applicationManifest = parseStringTestHeaderValueAsRelativePath(value, pathOrURL);
1454         else if (key == "allowCrossOriginSubresourcesToAskForCredentials")
1455             testOptions.allowCrossOriginSubresourcesToAskForCredentials = parseBooleanTestHeaderValue(value);
1456         else if (key == "domPasteAllowed")
1457             testOptions.domPasteAllowed = parseBooleanTestHeaderValue(value);
1458         else if (key == "enableProcessSwapOnNavigation")
1459             testOptions.contextOptions.enableProcessSwapOnNavigation = parseBooleanTestHeaderValue(value);
1460         else if (key == "enableProcessSwapOnWindowOpen")
1461             testOptions.contextOptions.enableProcessSwapOnWindowOpen = parseBooleanTestHeaderValue(value);
1462         else if (key == "useServiceWorkerShortTimeout")
1463             testOptions.contextOptions.useServiceWorkerShortTimeout = parseBooleanTestHeaderValue(value);
1464         else if (key == "enableColorFilter")
1465             testOptions.enableColorFilter = parseBooleanTestHeaderValue(value);
1466         else if (key == "punchOutWhiteBackgroundsInDarkMode")
1467             testOptions.punchOutWhiteBackgroundsInDarkMode = parseBooleanTestHeaderValue(value);
1468         else if (key == "jscOptions")
1469             testOptions.jscOptions = value;
1470         else if (key == "additionalSupportedImageTypes")
1471             testOptions.additionalSupportedImageTypes = value;
1472         else if (key == "runSingly")
1473             testOptions.runSingly = parseBooleanTestHeaderValue(value);
1474         else if (key == "shouldIgnoreMetaViewport")
1475             testOptions.shouldIgnoreMetaViewport = parseBooleanTestHeaderValue(value);
1476         else if (key == "spellCheckingDots")
1477             testOptions.shouldShowSpellCheckingDots = parseBooleanTestHeaderValue(value);
1478         else if (key == "enableServiceControls")
1479             testOptions.enableServiceControls = parseBooleanTestHeaderValue(value);
1480         else if (key == "enableEditableImages")
1481             testOptions.enableEditableImages = parseBooleanTestHeaderValue(value);
1482         else if (key == "editable")
1483             testOptions.editable = parseBooleanTestHeaderValue(value);
1484         else if (key == "enableUndoManagerAPI")
1485             testOptions.enableUndoManagerAPI = parseBooleanTestHeaderValue(value);
1486         else if (key == "shouldHandleRunOpenPanel")
1487             testOptions.shouldHandleRunOpenPanel = parseBooleanTestHeaderValue(value);
1488         else if (key == "shouldPresentPopovers")
1489             testOptions.shouldPresentPopovers = parseBooleanTestHeaderValue(value);
1490         else if (key == "contentInset.top")
1491             testOptions.contentInsetTop = std::stod(value);
1492         else if (key == "ignoreSynchronousMessagingTimeouts")
1493             testOptions.contextOptions.ignoreSynchronousMessagingTimeouts = parseBooleanTestHeaderValue(value);
1494         else if (key == "contentMode")
1495             testOptions.contentMode = { value.c_str() };
1496         else if (key == "applicationBundleIdentifier")
1497             testOptions.applicationBundleIdentifier = { value.c_str() };
1498         else if (key == "enableAppNap")
1499             testOptions.enableAppNap = parseBooleanTestHeaderValue(value);
1500         else if (key == "enableBackForwardCache")
1501             testOptions.enableBackForwardCache = parseBooleanTestHeaderValue(value);
1502         else if (key == "enableLazyImageLoading")
1503             testOptions.enableLazyImageLoading = parseBooleanTestHeaderValue(value);
1504         else if (key == "allowsLinkPreview")
1505             testOptions.allowsLinkPreview = parseBooleanTestHeaderValue(value);
1506         else if (key == "enableCaptureVideoInUIProcess")
1507             testOptions.enableCaptureVideoInUIProcess = parseBooleanTestHeaderValue(value);
1508         else if (key == "enableCaptureVideoInGPUProcess")
1509             testOptions.enableCaptureVideoInGPUProcess = parseBooleanTestHeaderValue(value);
1510         else if (key == "enableCaptureAudioInGPUProcess")
1511             testOptions.enableCaptureAudioInGPUProcess = parseBooleanTestHeaderValue(value);
1512         else if (key == "allowTopNavigationToDataURLs")
1513             testOptions.allowTopNavigationToDataURLs = parseBooleanTestHeaderValue(value);
1514         
1515         pairStart = pairEnd + 1;
1516     }
1517 }
1518
1519 TestOptions TestController::testOptionsForTest(const TestCommand& command) const
1520 {
1521     TestOptions options(command.pathOrURL);
1522
1523     options.useRemoteLayerTree = m_shouldUseRemoteLayerTree;
1524     options.shouldShowWebView = m_shouldShowWebView;
1525
1526     for (auto& feature : m_internalFeatures)
1527         options.internalDebugFeatures.add(feature.key, feature.value);
1528     for (auto& feature : m_experimentalFeatures)
1529         options.experimentalFeatures.add(feature.key, feature.value);
1530
1531     updatePlatformSpecificTestOptionsForTest(options, command.pathOrURL);
1532     updateTestOptionsFromTestHeader(options, command.pathOrURL, command.absolutePath);
1533     platformAddTestOptions(options);
1534
1535     return options;
1536 }
1537
1538 void TestController::updateWebViewSizeForTest(const TestInvocation& test)
1539 {
1540     unsigned width = viewWidth;
1541     unsigned height = viewHeight;
1542     if (test.options().isSVGTest) {
1543         width = w3cSVGViewWidth;
1544         height = w3cSVGViewHeight;
1545     }
1546
1547     mainWebView()->resizeTo(width, height);
1548 }
1549
1550 void TestController::updateWindowScaleForTest(PlatformWebView* view, const TestInvocation& test)
1551 {
1552     view->changeWindowScaleIfNeeded(test.options().deviceScaleFactor);
1553 }
1554
1555 void TestController::configureViewForTest(const TestInvocation& test)
1556 {
1557     ensureViewSupportsOptionsForTest(test);
1558     updateWebViewSizeForTest(test);
1559     updateWindowScaleForTest(mainWebView(), test);
1560     configureContentExtensionForTest(test);
1561     platformConfigureViewForTest(test);
1562 }
1563
1564 #if ENABLE(CONTENT_EXTENSIONS) && !PLATFORM(COCOA)
1565 struct ContentExtensionStoreCallbackContext {
1566     explicit ContentExtensionStoreCallbackContext(TestController& controller)
1567         : testController(controller)
1568     {
1569     }
1570
1571     TestController& testController;
1572     uint32_t status { kWKUserContentExtensionStoreSuccess };
1573     WKRetainPtr<WKUserContentFilterRef> filter;
1574     bool done { false };
1575 };
1576
1577 static void contentExtensionStoreCallback(WKUserContentFilterRef filter, uint32_t status, void* userData)
1578 {
1579     auto* context = static_cast<ContentExtensionStoreCallbackContext*>(userData);
1580     context->status = status;
1581     context->filter = filter ? adoptWK(filter) : nullptr;
1582     context->done = true;
1583     context->testController.notifyDone();
1584 }
1585
1586 static std::string contentExtensionJSONPath(WKURLRef url)
1587 {
1588     auto path = testPath(url);
1589     if (path.length())
1590         return path + ".json";
1591
1592     auto p = adoptWK(WKURLCopyPath(url));
1593     auto buffer = std::vector<char>(WKStringGetMaximumUTF8CStringSize(p.get()));
1594     const auto length = WKStringGetUTF8CString(p.get(), buffer.data(), buffer.size());
1595     return std::string("LayoutTests/http/tests") + std::string(buffer.data(), length - 1) + ".json";
1596 }
1597 #endif
1598
1599 #if !PLATFORM(COCOA)
1600 #if ENABLE(CONTENT_EXTENSIONS)
1601 void TestController::configureContentExtensionForTest(const TestInvocation& test)
1602 {
1603     const char* contentExtensionsPath = libraryPathForTesting();
1604     if (!contentExtensionsPath)
1605         contentExtensionsPath = "/tmp/wktr-contentextensions";
1606
1607     if (!test.urlContains("contentextensions/")) {
1608         WKPageSetUserContentExtensionsEnabled(m_mainWebView->page(), false);
1609         return;
1610     }
1611
1612     std::string jsonFilePath(contentExtensionJSONPath(test.url()));
1613     std::ifstream jsonFile(jsonFilePath);
1614     if (!jsonFile.good()) {
1615         WTFLogAlways("Could not open file '%s'", jsonFilePath.c_str());
1616         return;
1617     }
1618
1619     std::string jsonFileContents {std::istreambuf_iterator<char>(jsonFile), std::istreambuf_iterator<char>()};
1620     auto jsonSource = adoptWK(WKStringCreateWithUTF8CString(jsonFileContents.c_str()));
1621
1622     auto storePath = adoptWK(WKStringCreateWithUTF8CString(contentExtensionsPath));
1623     auto extensionStore = adoptWK(WKUserContentExtensionStoreCreate(storePath.get()));
1624     ASSERT(extensionStore);
1625
1626     auto filterIdentifier = adoptWK(WKStringCreateWithUTF8CString("TestContentExtension"));
1627
1628     ContentExtensionStoreCallbackContext context(*this);
1629     WKUserContentExtensionStoreCompile(extensionStore.get(), filterIdentifier.get(), jsonSource.get(), &context, contentExtensionStoreCallback);
1630     runUntil(context.done, noTimeout);
1631     ASSERT(context.status == kWKUserContentExtensionStoreSuccess);
1632     ASSERT(context.filter);
1633
1634     WKPageSetUserContentExtensionsEnabled(mainWebView()->page(), true);
1635     WKUserContentControllerAddUserContentFilter(userContentController(), context.filter.get());
1636 }
1637
1638 void TestController::resetContentExtensions()
1639 {
1640     if (!mainWebView())
1641         return;
1642
1643     WKPageSetUserContentExtensionsEnabled(mainWebView()->page(), false);
1644
1645     const char* contentExtensionsPath = libraryPathForTesting();
1646     if (!contentExtensionsPath)
1647         return;
1648
1649     WKUserContentControllerRemoveAllUserContentFilters(userContentController());
1650
1651     auto storePath = adoptWK(WKStringCreateWithUTF8CString(contentExtensionsPath));
1652     auto extensionStore = adoptWK(WKUserContentExtensionStoreCreate(storePath.get()));
1653     ASSERT(extensionStore);
1654
1655     auto filterIdentifier = adoptWK(WKStringCreateWithUTF8CString("TestContentExtension"));
1656
1657     ContentExtensionStoreCallbackContext context(*this);
1658     WKUserContentExtensionStoreRemove(extensionStore.get(), filterIdentifier.get(), &context, contentExtensionStoreCallback);
1659     runUntil(context.done, noTimeout);
1660     ASSERT(!context.filter);
1661 }
1662 #else // ENABLE(CONTENT_EXTENSIONS)
1663 void TestController::configureContentExtensionForTest(const TestInvocation&)
1664 {
1665 }
1666
1667 void TestController::resetContentExtensions()
1668 {
1669 }
1670 #endif // ENABLE(CONTENT_EXTENSIONS)
1671 #endif // !PLATFORM(COCOA)
1672
1673 class CommandTokenizer {
1674 public:
1675     explicit CommandTokenizer(const std::string& input)
1676         : m_input(input)
1677         , m_posNextSeparator(0)
1678     {
1679         pump();
1680     }
1681
1682     bool hasNext() const;
1683     std::string next();
1684
1685 private:
1686     void pump();
1687     static const char kSeparator = '\'';
1688     const std::string& m_input;
1689     std::string m_next;
1690     size_t m_posNextSeparator;
1691 };
1692
1693 void CommandTokenizer::pump()
1694 {
1695     if (m_posNextSeparator == std::string::npos || m_posNextSeparator == m_input.size()) {
1696         m_next = std::string();
1697         return;
1698     }
1699     size_t start = m_posNextSeparator ? m_posNextSeparator + 1 : 0;
1700     m_posNextSeparator = m_input.find(kSeparator, start);
1701     size_t size = m_posNextSeparator == std::string::npos ? std::string::npos : m_posNextSeparator - start;
1702     m_next = std::string(m_input, start, size);
1703 }
1704
1705 std::string CommandTokenizer::next()
1706 {
1707     ASSERT(hasNext());
1708
1709     std::string oldNext = m_next;
1710     pump();
1711     return oldNext;
1712 }
1713
1714 bool CommandTokenizer::hasNext() const
1715 {
1716     return !m_next.empty();
1717 }
1718
1719 NO_RETURN static void die(const std::string& inputLine)
1720 {
1721     fprintf(stderr, "Unexpected input line: %s\n", inputLine.c_str());
1722     exit(1);
1723 }
1724
1725 static TestCommand parseInputLine(const std::string& inputLine)
1726 {
1727     TestCommand result;
1728     CommandTokenizer tokenizer(inputLine);
1729     if (!tokenizer.hasNext())
1730         die(inputLine);
1731
1732     std::string arg = tokenizer.next();
1733     result.pathOrURL = arg;
1734     while (tokenizer.hasNext()) {
1735         arg = tokenizer.next();
1736         if (arg == std::string("--timeout")) {
1737             std::string timeoutToken = tokenizer.next();
1738             result.timeout = Seconds::fromMilliseconds(atoi(timeoutToken.c_str()));
1739         } else if (arg == std::string("-p") || arg == std::string("--pixel-test")) {
1740             result.shouldDumpPixels = true;
1741             if (tokenizer.hasNext())
1742                 result.expectedPixelHash = tokenizer.next();
1743         } else if (arg == std::string("--dump-jsconsolelog-in-stderr"))
1744             result.dumpJSConsoleLogInStdErr = true;
1745         else if (arg == std::string("--absolutePath"))
1746             result.absolutePath = tokenizer.next();
1747         else
1748             die(inputLine);
1749     }
1750     return result;
1751 }
1752
1753 bool TestController::runTest(const char* inputLine)
1754 {
1755     AutodrainedPool pool;
1756     
1757     WKTextCheckerSetTestingMode(true);
1758     TestCommand command = parseInputLine(std::string(inputLine));
1759
1760     m_state = RunningTest;
1761     
1762     TestOptions options = testOptionsForTest(command);
1763
1764     WKRetainPtr<WKURLRef> wkURL = adoptWK(createTestURL(command.pathOrURL.c_str()));
1765     m_currentInvocation = makeUnique<TestInvocation>(wkURL.get(), options);
1766
1767     if (command.shouldDumpPixels || m_shouldDumpPixelsForAllTests)
1768         m_currentInvocation->setIsPixelTest(command.expectedPixelHash);
1769
1770     if (command.timeout > 0_s)
1771         m_currentInvocation->setCustomTimeout(command.timeout);
1772
1773     m_currentInvocation->setDumpJSConsoleLogInStdErr(command.dumpJSConsoleLogInStdErr || options.dumpJSConsoleLogInStdErr);
1774
1775     platformWillRunTest(*m_currentInvocation);
1776
1777     m_currentInvocation->invoke();
1778     m_currentInvocation = nullptr;
1779
1780     return true;
1781 }
1782
1783 bool TestController::waitForCompletion(const WTF::Function<void ()>& function, WTF::Seconds timeout)
1784 {
1785     m_doneResetting = false;
1786     function();
1787     runUntil(m_doneResetting, timeout);
1788     return !m_doneResetting;
1789 }
1790
1791 bool TestController::handleControlCommand(const char* command)
1792 {
1793     if (!strncmp("#CHECK FOR WORLD LEAKS", command, 22)) {
1794         if (m_checkForWorldLeaks)
1795             findAndDumpWorldLeaks();
1796         else
1797             WTFLogAlways("WebKitTestRunner asked to check for world leaks, but was not run with --world-leaks");
1798         return true;
1799     }
1800
1801     if (!strncmp("#LIST CHILD PROCESSES", command, 21)) {
1802         findAndDumpWebKitProcessIdentifiers();
1803         return true;
1804     }
1805
1806     return false;
1807 }
1808
1809 void TestController::runTestingServerLoop()
1810 {
1811     char filenameBuffer[2048];
1812     while (fgets(filenameBuffer, sizeof(filenameBuffer), stdin)) {
1813         char* newLineCharacter = strchr(filenameBuffer, '\n');
1814         if (newLineCharacter)
1815             *newLineCharacter = '\0';
1816
1817         if (strlen(filenameBuffer) == 0)
1818             continue;
1819
1820         if (handleControlCommand(filenameBuffer))
1821             continue;
1822
1823         if (!runTest(filenameBuffer))
1824             break;
1825     }
1826 }
1827
1828 void TestController::run()
1829 {
1830     if (m_usingServerMode)
1831         runTestingServerLoop();
1832     else {
1833         for (size_t i = 0; i < m_paths.size(); ++i) {
1834             if (!runTest(m_paths[i].c_str()))
1835                 break;
1836         }
1837         if (m_checkForWorldLeaks)
1838             findAndDumpWorldLeaks();
1839     }
1840 }
1841
1842 void TestController::runUntil(bool& done, WTF::Seconds timeout)
1843 {
1844     if (m_forceNoTimeout)
1845         timeout = noTimeout;
1846
1847     platformRunUntil(done, timeout);
1848 }
1849
1850 // WKContextInjectedBundleClient
1851
1852 void TestController::didReceiveMessageFromInjectedBundle(WKContextRef context, WKStringRef messageName, WKTypeRef messageBody, const void* clientInfo)
1853 {
1854     static_cast<TestController*>(const_cast<void*>(clientInfo))->didReceiveMessageFromInjectedBundle(messageName, messageBody);
1855 }
1856
1857 void TestController::didReceiveSynchronousMessageFromInjectedBundleWithListener(WKContextRef context, WKStringRef messageName, WKTypeRef messageBody, WKMessageListenerRef listener, const void* clientInfo)
1858 {
1859     static_cast<TestController*>(const_cast<void*>(clientInfo))->didReceiveSynchronousMessageFromInjectedBundle(messageName, messageBody, listener);
1860 }
1861
1862 WKTypeRef TestController::getInjectedBundleInitializationUserData(WKContextRef, const void* clientInfo)
1863 {
1864     return static_cast<TestController*>(const_cast<void*>(clientInfo))->getInjectedBundleInitializationUserData().leakRef();
1865 }
1866
1867 // WKPageInjectedBundleClient
1868
1869 void TestController::didReceivePageMessageFromInjectedBundle(WKPageRef page, WKStringRef messageName, WKTypeRef messageBody, const void* clientInfo)
1870 {
1871     static_cast<TestController*>(const_cast<void*>(clientInfo))->didReceiveMessageFromInjectedBundle(messageName, messageBody);
1872 }
1873
1874 void TestController::didReceiveSynchronousPageMessageFromInjectedBundleWithListener(WKPageRef page, WKStringRef messageName, WKTypeRef messageBody, WKMessageListenerRef listener, const void* clientInfo)
1875 {
1876     static_cast<TestController*>(const_cast<void*>(clientInfo))->didReceiveSynchronousMessageFromInjectedBundle(messageName, messageBody, listener);
1877 }
1878
1879 void TestController::networkProcessDidCrash(WKContextRef context, const void *clientInfo)
1880 {
1881     static_cast<TestController*>(const_cast<void*>(clientInfo))->networkProcessDidCrash();
1882 }
1883
1884 void TestController::serviceWorkerProcessDidCrash(WKContextRef context, WKProcessID processID, const void *clientInfo)
1885 {
1886     static_cast<TestController*>(const_cast<void*>(clientInfo))->serviceWorkerProcessDidCrash(processID);
1887 }
1888
1889 void TestController::gpuProcessDidCrash(WKContextRef context, WKProcessID processID, const void *clientInfo)
1890 {
1891     static_cast<TestController*>(const_cast<void*>(clientInfo))->gpuProcessDidCrash(processID);
1892 }
1893
1894 void TestController::didReceiveKeyDownMessageFromInjectedBundle(WKDictionaryRef messageBodyDictionary, bool synchronous)
1895 {
1896     WKRetainPtr<WKStringRef> keyKey = adoptWK(WKStringCreateWithUTF8CString("Key"));
1897     WKStringRef key = static_cast<WKStringRef>(WKDictionaryGetItemForKey(messageBodyDictionary, keyKey.get()));
1898
1899     WKRetainPtr<WKStringRef> modifiersKey = adoptWK(WKStringCreateWithUTF8CString("Modifiers"));
1900     WKEventModifiers modifiers = static_cast<WKEventModifiers>(WKUInt64GetValue(static_cast<WKUInt64Ref>(WKDictionaryGetItemForKey(messageBodyDictionary, modifiersKey.get()))));
1901
1902     WKRetainPtr<WKStringRef> locationKey = adoptWK(WKStringCreateWithUTF8CString("Location"));
1903     unsigned location = static_cast<unsigned>(WKUInt64GetValue(static_cast<WKUInt64Ref>(WKDictionaryGetItemForKey(messageBodyDictionary, locationKey.get()))));
1904
1905     m_eventSenderProxy->keyDown(key, modifiers, location);
1906 }
1907
1908 void TestController::didReceiveLiveDocumentsList(WKArrayRef liveDocumentList)
1909 {
1910     auto numDocuments = WKArrayGetSize(liveDocumentList);
1911
1912     HashMap<uint64_t, String> documentInfo;
1913     for (size_t i = 0; i < numDocuments; ++i) {
1914         WKTypeRef item = WKArrayGetItemAtIndex(liveDocumentList, i);
1915         if (item && WKGetTypeID(item) == WKDictionaryGetTypeID()) {
1916             WKDictionaryRef liveDocumentItem = static_cast<WKDictionaryRef>(item);
1917
1918             WKRetainPtr<WKStringRef> idKey = adoptWK(WKStringCreateWithUTF8CString("id"));
1919             WKUInt64Ref documentID = static_cast<WKUInt64Ref>(WKDictionaryGetItemForKey(liveDocumentItem, idKey.get()));
1920
1921             WKRetainPtr<WKStringRef> urlKey = adoptWK(WKStringCreateWithUTF8CString("url"));
1922             WKStringRef documentURL = static_cast<WKStringRef>(WKDictionaryGetItemForKey(liveDocumentItem, urlKey.get()));
1923
1924             documentInfo.add(WKUInt64GetValue(documentID), toWTFString(documentURL));
1925         }
1926     }
1927
1928     if (!documentInfo.size()) {
1929         m_abandonedDocumentInfo.clear();
1930         return;
1931     }
1932
1933     // Remove any documents which are no longer live.
1934     m_abandonedDocumentInfo.removeIf([&](auto& keyAndValue) {
1935         return !documentInfo.contains(keyAndValue.key);
1936     });
1937     
1938     // Add newly abandoned documents.
1939     String currentTestURL = m_currentInvocation ? toWTFString(adoptWK(WKURLCopyString(m_currentInvocation->url()))) : "no test";
1940     for (const auto& it : documentInfo)
1941         m_abandonedDocumentInfo.add(it.key, AbandonedDocumentInfo(currentTestURL, it.value));
1942 }
1943
1944 void TestController::didReceiveMessageFromInjectedBundle(WKStringRef messageName, WKTypeRef messageBody)
1945 {
1946     if (WKStringIsEqualToUTF8CString(messageName, "LiveDocuments")) {
1947         ASSERT(WKGetTypeID(messageBody) == WKArrayGetTypeID());
1948         didReceiveLiveDocumentsList(static_cast<WKArrayRef>(messageBody));
1949         AsyncTask::currentTask()->taskComplete();
1950         return;
1951     }
1952
1953     if (WKStringIsEqualToUTF8CString(messageName, "EventSender")) {
1954         if (m_state != RunningTest)
1955             return;
1956
1957         ASSERT(WKGetTypeID(messageBody) == WKDictionaryGetTypeID());
1958         WKDictionaryRef messageBodyDictionary = static_cast<WKDictionaryRef>(messageBody);
1959
1960         WKRetainPtr<WKStringRef> subMessageKey = adoptWK(WKStringCreateWithUTF8CString("SubMessage"));
1961         WKStringRef subMessageName = static_cast<WKStringRef>(WKDictionaryGetItemForKey(messageBodyDictionary, subMessageKey.get()));
1962
1963         if (WKStringIsEqualToUTF8CString(subMessageName, "MouseDown") || WKStringIsEqualToUTF8CString(subMessageName, "MouseUp")) {
1964             WKRetainPtr<WKStringRef> buttonKey = adoptWK(WKStringCreateWithUTF8CString("Button"));
1965             unsigned button = static_cast<unsigned>(WKUInt64GetValue(static_cast<WKUInt64Ref>(WKDictionaryGetItemForKey(messageBodyDictionary, buttonKey.get()))));
1966
1967             WKRetainPtr<WKStringRef> modifiersKey = adoptWK(WKStringCreateWithUTF8CString("Modifiers"));
1968             WKEventModifiers modifiers = static_cast<WKEventModifiers>(WKUInt64GetValue(static_cast<WKUInt64Ref>(WKDictionaryGetItemForKey(messageBodyDictionary, modifiersKey.get()))));
1969
1970             // Forward to WebProcess
1971             if (WKStringIsEqualToUTF8CString(subMessageName, "MouseDown"))
1972                 m_eventSenderProxy->mouseDown(button, modifiers);
1973             else
1974                 m_eventSenderProxy->mouseUp(button, modifiers);
1975
1976             return;
1977         }
1978
1979         if (WKStringIsEqualToUTF8CString(subMessageName, "KeyDown")) {
1980             didReceiveKeyDownMessageFromInjectedBundle(messageBodyDictionary, false);
1981             return;
1982         }
1983
1984         if (WKStringIsEqualToUTF8CString(subMessageName, "MouseScrollBy")) {
1985             WKRetainPtr<WKStringRef> xKey = adoptWK(WKStringCreateWithUTF8CString("X"));
1986             double x = WKDoubleGetValue(static_cast<WKDoubleRef>(WKDictionaryGetItemForKey(messageBodyDictionary, xKey.get())));
1987
1988             WKRetainPtr<WKStringRef> yKey = adoptWK(WKStringCreateWithUTF8CString("Y"));
1989             double y = WKDoubleGetValue(static_cast<WKDoubleRef>(WKDictionaryGetItemForKey(messageBodyDictionary, yKey.get())));
1990
1991             // Forward to WebProcess
1992             m_eventSenderProxy->mouseScrollBy(x, y);
1993             return;
1994         }
1995
1996         if (WKStringIsEqualToUTF8CString(subMessageName, "MouseScrollByWithWheelAndMomentumPhases")) {
1997             WKRetainPtr<WKStringRef> xKey = adoptWK(WKStringCreateWithUTF8CString("X"));
1998             double x = WKDoubleGetValue(static_cast<WKDoubleRef>(WKDictionaryGetItemForKey(messageBodyDictionary, xKey.get())));
1999             
2000             WKRetainPtr<WKStringRef> yKey = adoptWK(WKStringCreateWithUTF8CString("Y"));
2001             double y = WKDoubleGetValue(static_cast<WKDoubleRef>(WKDictionaryGetItemForKey(messageBodyDictionary, yKey.get())));
2002             
2003             WKRetainPtr<WKStringRef> phaseKey = adoptWK(WKStringCreateWithUTF8CString("Phase"));
2004             int phase = static_cast<int>(WKUInt64GetValue(static_cast<WKUInt64Ref>(WKDictionaryGetItemForKey(messageBodyDictionary, phaseKey.get()))));
2005             WKRetainPtr<WKStringRef> momentumKey = adoptWK(WKStringCreateWithUTF8CString("Momentum"));
2006             int momentum = static_cast<int>(WKUInt64GetValue(static_cast<WKUInt64Ref>(WKDictionaryGetItemForKey(messageBodyDictionary, momentumKey.get()))));
2007             
2008             // Forward to WebProcess
2009             m_eventSenderProxy->mouseScrollByWithWheelAndMomentumPhases(x, y, phase, momentum);
2010
2011             return;
2012         }
2013
2014         ASSERT_NOT_REACHED();
2015     }
2016
2017     if (!m_currentInvocation)
2018         return;
2019
2020     m_currentInvocation->didReceiveMessageFromInjectedBundle(messageName, messageBody);
2021 }
2022
2023 void TestController::didReceiveSynchronousMessageFromInjectedBundle(WKStringRef messageName, WKTypeRef messageBody, WKMessageListenerRef listener)
2024 {
2025     auto completionHandler = [listener = retainWK(listener)] (WKTypeRef reply) {
2026         WKMessageListenerSendReply(listener.get(), reply);
2027     };
2028
2029     if (WKStringIsEqualToUTF8CString(messageName, "EventSender")) {
2030         if (m_state != RunningTest)
2031             return completionHandler(nullptr);
2032
2033         ASSERT(WKGetTypeID(messageBody) == WKDictionaryGetTypeID());
2034         WKDictionaryRef messageBodyDictionary = static_cast<WKDictionaryRef>(messageBody);
2035
2036         WKRetainPtr<WKStringRef> subMessageKey = adoptWK(WKStringCreateWithUTF8CString("SubMessage"));
2037         WKStringRef subMessageName = static_cast<WKStringRef>(WKDictionaryGetItemForKey(messageBodyDictionary, subMessageKey.get()));
2038
2039         if (WKStringIsEqualToUTF8CString(subMessageName, "KeyDown")) {
2040             didReceiveKeyDownMessageFromInjectedBundle(messageBodyDictionary, true);
2041
2042             return completionHandler(nullptr);
2043         }
2044
2045         if (WKStringIsEqualToUTF8CString(subMessageName, "MouseDown") || WKStringIsEqualToUTF8CString(subMessageName, "MouseUp")) {
2046             WKRetainPtr<WKStringRef> buttonKey = adoptWK(WKStringCreateWithUTF8CString("Button"));
2047             unsigned button = static_cast<unsigned>(WKUInt64GetValue(static_cast<WKUInt64Ref>(WKDictionaryGetItemForKey(messageBodyDictionary, buttonKey.get()))));
2048
2049             WKRetainPtr<WKStringRef> modifiersKey = adoptWK(WKStringCreateWithUTF8CString("Modifiers"));
2050             WKEventModifiers modifiers = static_cast<WKEventModifiers>(WKUInt64GetValue(static_cast<WKUInt64Ref>(WKDictionaryGetItemForKey(messageBodyDictionary, modifiersKey.get()))));
2051
2052             // Forward to WebProcess
2053             if (WKStringIsEqualToUTF8CString(subMessageName, "MouseDown"))
2054                 m_eventSenderProxy->mouseDown(button, modifiers);
2055             else
2056                 m_eventSenderProxy->mouseUp(button, modifiers);
2057             return completionHandler(nullptr);
2058         }
2059
2060         if (WKStringIsEqualToUTF8CString(subMessageName, "MouseMoveTo")) {
2061             WKRetainPtr<WKStringRef> xKey = adoptWK(WKStringCreateWithUTF8CString("X"));
2062             double x = WKDoubleGetValue(static_cast<WKDoubleRef>(WKDictionaryGetItemForKey(messageBodyDictionary, xKey.get())));
2063
2064             WKRetainPtr<WKStringRef> yKey = adoptWK(WKStringCreateWithUTF8CString("Y"));
2065             double y = WKDoubleGetValue(static_cast<WKDoubleRef>(WKDictionaryGetItemForKey(messageBodyDictionary, yKey.get())));
2066
2067             // Forward to WebProcess
2068             m_eventSenderProxy->mouseMoveTo(x, y);
2069             return completionHandler(nullptr);
2070         }
2071
2072 #if PLATFORM(MAC)
2073         if (WKStringIsEqualToUTF8CString(subMessageName, "MouseForceClick")) {
2074             m_eventSenderProxy->mouseForceClick();
2075             return completionHandler(nullptr);
2076         }
2077
2078         if (WKStringIsEqualToUTF8CString(subMessageName, "StartAndCancelMouseForceClick")) {
2079             m_eventSenderProxy->startAndCancelMouseForceClick();
2080             return completionHandler(nullptr);
2081         }
2082
2083         if (WKStringIsEqualToUTF8CString(subMessageName, "MouseForceDown")) {
2084             m_eventSenderProxy->mouseForceDown();
2085             return completionHandler(nullptr);
2086         }
2087
2088         if (WKStringIsEqualToUTF8CString(subMessageName, "MouseForceUp")) {
2089             m_eventSenderProxy->mouseForceUp();
2090             return completionHandler(nullptr);
2091         }
2092
2093         if (WKStringIsEqualToUTF8CString(subMessageName, "MouseForceChanged")) {
2094             WKRetainPtr<WKStringRef> forceKey = adoptWK(WKStringCreateWithUTF8CString("Force"));
2095             double force = WKDoubleGetValue(static_cast<WKDoubleRef>(WKDictionaryGetItemForKey(messageBodyDictionary, forceKey.get())));
2096
2097             m_eventSenderProxy->mouseForceChanged(force);
2098             return completionHandler(nullptr);
2099         }
2100 #endif // PLATFORM(MAC)
2101
2102         if (WKStringIsEqualToUTF8CString(subMessageName, "ContinuousMouseScrollBy")) {
2103             WKRetainPtr<WKStringRef> xKey = adoptWK(WKStringCreateWithUTF8CString("X"));
2104             double x = WKDoubleGetValue(static_cast<WKDoubleRef>(WKDictionaryGetItemForKey(messageBodyDictionary, xKey.get())));
2105
2106             WKRetainPtr<WKStringRef> yKey = adoptWK(WKStringCreateWithUTF8CString("Y"));
2107             double y = WKDoubleGetValue(static_cast<WKDoubleRef>(WKDictionaryGetItemForKey(messageBodyDictionary, yKey.get())));
2108
2109             WKRetainPtr<WKStringRef> pagedKey = adoptWK(WKStringCreateWithUTF8CString("Paged"));
2110             bool paged = static_cast<bool>(WKUInt64GetValue(static_cast<WKUInt64Ref>(WKDictionaryGetItemForKey(messageBodyDictionary, pagedKey.get()))));
2111
2112             // Forward to WebProcess
2113             m_eventSenderProxy->continuousMouseScrollBy(x, y, paged);
2114             return completionHandler(nullptr);
2115         }
2116
2117         if (WKStringIsEqualToUTF8CString(subMessageName, "LeapForward")) {
2118             WKRetainPtr<WKStringRef> timeKey = adoptWK(WKStringCreateWithUTF8CString("TimeInMilliseconds"));
2119             unsigned time = static_cast<unsigned>(WKUInt64GetValue(static_cast<WKUInt64Ref>(WKDictionaryGetItemForKey(messageBodyDictionary, timeKey.get()))));
2120
2121             m_eventSenderProxy->leapForward(time);
2122             return completionHandler(nullptr);
2123         }
2124
2125 #if ENABLE(TOUCH_EVENTS)
2126         if (WKStringIsEqualToUTF8CString(subMessageName, "AddTouchPoint")) {
2127             WKRetainPtr<WKStringRef> xKey = adoptWK(WKStringCreateWithUTF8CString("X"));
2128             int x = static_cast<int>(WKUInt64GetValue(static_cast<WKUInt64Ref>(WKDictionaryGetItemForKey(messageBodyDictionary, xKey.get()))));
2129
2130             WKRetainPtr<WKStringRef> yKey = adoptWK(WKStringCreateWithUTF8CString("Y"));
2131             int y = static_cast<int>(WKUInt64GetValue(static_cast<WKUInt64Ref>(WKDictionaryGetItemForKey(messageBodyDictionary, yKey.get()))));
2132
2133             m_eventSenderProxy->addTouchPoint(x, y);
2134             return completionHandler(nullptr);
2135         }
2136
2137         if (WKStringIsEqualToUTF8CString(subMessageName, "UpdateTouchPoint")) {
2138             WKRetainPtr<WKStringRef> indexKey = adoptWK(WKStringCreateWithUTF8CString("Index"));
2139             int index = static_cast<int>(WKUInt64GetValue(static_cast<WKUInt64Ref>(WKDictionaryGetItemForKey(messageBodyDictionary, indexKey.get()))));
2140
2141             WKRetainPtr<WKStringRef> xKey = adoptWK(WKStringCreateWithUTF8CString("X"));
2142             int x = static_cast<int>(WKUInt64GetValue(static_cast<WKUInt64Ref>(WKDictionaryGetItemForKey(messageBodyDictionary, xKey.get()))));
2143
2144             WKRetainPtr<WKStringRef> yKey = adoptWK(WKStringCreateWithUTF8CString("Y"));
2145             int y = static_cast<int>(WKUInt64GetValue(static_cast<WKUInt64Ref>(WKDictionaryGetItemForKey(messageBodyDictionary, yKey.get()))));
2146
2147             m_eventSenderProxy->updateTouchPoint(index, x, y);
2148             return completionHandler(nullptr);
2149         }
2150
2151         if (WKStringIsEqualToUTF8CString(subMessageName, "SetTouchModifier")) {
2152             WKRetainPtr<WKStringRef> modifierKey = adoptWK(WKStringCreateWithUTF8CString("Modifier"));
2153             WKEventModifiers modifier = static_cast<WKEventModifiers>(WKUInt64GetValue(static_cast<WKUInt64Ref>(WKDictionaryGetItemForKey(messageBodyDictionary, modifierKey.get()))));
2154
2155             WKRetainPtr<WKStringRef> enableKey = adoptWK(WKStringCreateWithUTF8CString("Enable"));
2156             bool enable = static_cast<bool>(WKUInt64GetValue(static_cast<WKUInt64Ref>(WKDictionaryGetItemForKey(messageBodyDictionary, enableKey.get()))));
2157
2158             m_eventSenderProxy->setTouchModifier(modifier, enable);
2159             return completionHandler(nullptr);
2160         }
2161
2162         if (WKStringIsEqualToUTF8CString(subMessageName, "SetTouchPointRadius")) {
2163             WKRetainPtr<WKStringRef> xKey = adoptWK(WKStringCreateWithUTF8CString("RadiusX"));
2164             int x = static_cast<int>(WKUInt64GetValue(static_cast<WKUInt64Ref>(WKDictionaryGetItemForKey(messageBodyDictionary, xKey.get()))));
2165
2166             WKRetainPtr<WKStringRef> yKey = adoptWK(WKStringCreateWithUTF8CString("RadiusY"));
2167             int y = static_cast<int>(WKUInt64GetValue(static_cast<WKUInt64Ref>(WKDictionaryGetItemForKey(messageBodyDictionary, yKey.get()))));
2168
2169             m_eventSenderProxy->setTouchPointRadius(x, y);
2170             return completionHandler(nullptr);
2171         }
2172
2173         if (WKStringIsEqualToUTF8CString(subMessageName, "TouchStart")) {
2174             m_eventSenderProxy->touchStart();
2175             return completionHandler(nullptr);
2176         }
2177
2178         if (WKStringIsEqualToUTF8CString(subMessageName, "TouchMove")) {
2179             m_eventSenderProxy->touchMove();
2180             return completionHandler(nullptr);
2181         }
2182
2183         if (WKStringIsEqualToUTF8CString(subMessageName, "TouchEnd")) {
2184             m_eventSenderProxy->touchEnd();
2185             return completionHandler(nullptr);
2186         }
2187
2188         if (WKStringIsEqualToUTF8CString(subMessageName, "TouchCancel")) {
2189             m_eventSenderProxy->touchCancel();
2190             return completionHandler(nullptr);
2191         }
2192
2193         if (WKStringIsEqualToUTF8CString(subMessageName, "ClearTouchPoints")) {
2194             m_eventSenderProxy->clearTouchPoints();
2195             return completionHandler(nullptr);
2196         }
2197
2198         if (WKStringIsEqualToUTF8CString(subMessageName, "ReleaseTouchPoint")) {
2199             WKRetainPtr<WKStringRef> indexKey = adoptWK(WKStringCreateWithUTF8CString("Index"));
2200             int index = static_cast<int>(WKUInt64GetValue(static_cast<WKUInt64Ref>(WKDictionaryGetItemForKey(messageBodyDictionary, indexKey.get()))));
2201             m_eventSenderProxy->releaseTouchPoint(index);
2202             return completionHandler(nullptr);
2203         }
2204
2205         if (WKStringIsEqualToUTF8CString(subMessageName, "CancelTouchPoint")) {
2206             WKRetainPtr<WKStringRef> indexKey = adoptWK(WKStringCreateWithUTF8CString("Index"));
2207             int index = static_cast<int>(WKUInt64GetValue(static_cast<WKUInt64Ref>(WKDictionaryGetItemForKey(messageBodyDictionary, indexKey.get()))));
2208             m_eventSenderProxy->cancelTouchPoint(index);
2209             return completionHandler(nullptr);
2210         }
2211 #endif
2212         ASSERT_NOT_REACHED();
2213     }
2214
2215     auto setHTTPCookieAcceptPolicy = [&] (WKHTTPCookieAcceptPolicy policy, CompletionHandler<void(WKTypeRef)>&& completionHandler) {
2216         auto context = new CompletionHandler<void(WKTypeRef)>(WTFMove(completionHandler));
2217         WKHTTPCookieStoreSetHTTPCookieAcceptPolicy(WKWebsiteDataStoreGetHTTPCookieStore(TestController::defaultWebsiteDataStore()), policy, context, [] (void* context) {
2218             auto completionHandlerPointer = static_cast<CompletionHandler<void(WKTypeRef)>*>(context);
2219             (*completionHandlerPointer)(nullptr);
2220             delete completionHandlerPointer;
2221         });
2222     };
2223
2224     if (WKStringIsEqualToUTF8CString(messageName, "SetAlwaysAcceptCookies")) {
2225         WKBooleanRef accept = static_cast<WKBooleanRef>(messageBody);
2226         WKHTTPCookieAcceptPolicy policy = WKBooleanGetValue(accept) ? kWKHTTPCookieAcceptPolicyAlways : kWKHTTPCookieAcceptPolicyOnlyFromMainDocumentDomain;
2227         return setHTTPCookieAcceptPolicy(policy, WTFMove(completionHandler));
2228     }
2229
2230     if (WKStringIsEqualToUTF8CString(messageName, "SetOnlyAcceptFirstPartyCookies")) {
2231         WKBooleanRef accept = static_cast<WKBooleanRef>(messageBody);
2232         WKHTTPCookieAcceptPolicy policy = WKBooleanGetValue(accept) ? kWKHTTPCookieAcceptPolicyExclusivelyFromMainDocumentDomain : kWKHTTPCookieAcceptPolicyOnlyFromMainDocumentDomain;
2233         return setHTTPCookieAcceptPolicy(policy, WTFMove(completionHandler));
2234     }
2235
2236     completionHandler(m_currentInvocation->didReceiveSynchronousMessageFromInjectedBundle(messageName, messageBody).get());
2237 }
2238
2239 WKRetainPtr<WKTypeRef> TestController::getInjectedBundleInitializationUserData()
2240 {
2241     return nullptr;
2242 }
2243
2244 // WKContextClient
2245
2246 void TestController::networkProcessDidCrash()
2247 {
2248     pid_t pid = WKContextGetNetworkProcessIdentifier(m_context.get());
2249     fprintf(stderr, "#CRASHED - %s (pid %ld)\n", networkProcessName(), static_cast<long>(pid));
2250     exit(1);
2251 }
2252
2253 void TestController::serviceWorkerProcessDidCrash(WKProcessID processID)
2254 {
2255     fprintf(stderr, "#CRASHED - ServiceWorkerProcess (pid %ld)\n", static_cast<long>(processID));
2256     if (m_shouldExitWhenWebProcessCrashes)
2257         exit(1);
2258 }
2259
2260 void TestController::gpuProcessDidCrash(WKProcessID processID)
2261 {
2262     fprintf(stderr, "#CRASHED - GPUProcess (pid %ld)\n", static_cast<long>(processID));
2263     if (m_shouldExitWhenWebProcessCrashes)
2264         exit(1);
2265 }
2266
2267 // WKPageNavigationClient
2268
2269 void TestController::didCommitNavigation(WKPageRef page, WKNavigationRef navigation, WKTypeRef, const void* clientInfo)
2270 {
2271     static_cast<TestController*>(const_cast<void*>(clientInfo))->didCommitNavigation(page, navigation);
2272 }
2273
2274 void TestController::didFinishNavigation(WKPageRef page, WKNavigationRef navigation, WKTypeRef, const void* clientInfo)
2275 {
2276     static_cast<TestController*>(const_cast<void*>(clientInfo))->didFinishNavigation(page, navigation);
2277 }
2278
2279 void TestController::didReceiveServerRedirectForProvisionalNavigation(WKPageRef page, WKNavigationRef navigation, WKTypeRef userData, const void* clientInfo)
2280 {
2281     static_cast<TestController*>(const_cast<void*>(clientInfo))->didReceiveServerRedirectForProvisionalNavigation(page, navigation, userData);
2282 }
2283
2284 bool TestController::canAuthenticateAgainstProtectionSpace(WKPageRef page, WKProtectionSpaceRef protectionSpace, const void* clientInfo)
2285 {
2286     return static_cast<TestController*>(const_cast<void*>(clientInfo))->canAuthenticateAgainstProtectionSpace(page, protectionSpace);
2287 }
2288
2289 void TestController::didReceiveAuthenticationChallenge(WKPageRef page, WKAuthenticationChallengeRef authenticationChallenge, const void *clientInfo)
2290 {
2291     static_cast<TestController*>(const_cast<void*>(clientInfo))->didReceiveAuthenticationChallenge(page, /*frame,*/ authenticationChallenge);
2292 }
2293
2294 void TestController::processDidCrash(WKPageRef page, const void* clientInfo)
2295 {
2296     static_cast<TestController*>(const_cast<void*>(clientInfo))->processDidCrash();
2297 }
2298
2299 void TestController::didBeginNavigationGesture(WKPageRef page, const void *clientInfo)
2300 {
2301     static_cast<TestController*>(const_cast<void*>(clientInfo))->didBeginNavigationGesture(page);
2302 }
2303
2304 void TestController::willEndNavigationGesture(WKPageRef page, WKBackForwardListItemRef backForwardListItem, const void *clientInfo)
2305 {
2306     static_cast<TestController*>(const_cast<void*>(clientInfo))->willEndNavigationGesture(page, backForwardListItem);
2307 }
2308
2309 void TestController::didEndNavigationGesture(WKPageRef page, WKBackForwardListItemRef backForwardListItem, const void *clientInfo)
2310 {
2311     static_cast<TestController*>(const_cast<void*>(clientInfo))->didEndNavigationGesture(page, backForwardListItem);
2312 }
2313
2314 void TestController::didRemoveNavigationGestureSnapshot(WKPageRef page, const void *clientInfo)
2315 {
2316     static_cast<TestController*>(const_cast<void*>(clientInfo))->didRemoveNavigationGestureSnapshot(page);
2317 }
2318
2319 WKPluginLoadPolicy TestController::decidePolicyForPluginLoad(WKPageRef page, WKPluginLoadPolicy currentPluginLoadPolicy, WKDictionaryRef pluginInformation, WKStringRef* unavailabilityDescription, const void* clientInfo)
2320 {
2321     return static_cast<TestController*>(const_cast<void*>(clientInfo))->decidePolicyForPluginLoad(page, currentPluginLoadPolicy, pluginInformation, unavailabilityDescription);
2322 }
2323
2324 WKPluginLoadPolicy TestController::decidePolicyForPluginLoad(WKPageRef, WKPluginLoadPolicy currentPluginLoadPolicy, WKDictionaryRef pluginInformation, WKStringRef* unavailabilityDescription)
2325 {
2326     if (m_shouldBlockAllPlugins)
2327         return kWKPluginLoadPolicyBlocked;
2328
2329 #if PLATFORM(MAC)
2330     WKStringRef bundleIdentifier = (WKStringRef)WKDictionaryGetItemForKey(pluginInformation, WKPluginInformationBundleIdentifierKey());
2331     if (!bundleIdentifier)
2332         return currentPluginLoadPolicy;
2333
2334     if (WKStringIsEqualToUTF8CString(bundleIdentifier, "com.apple.QuickTime Plugin.plugin"))
2335         return currentPluginLoadPolicy;
2336
2337     if (WKStringIsEqualToUTF8CString(bundleIdentifier, "com.apple.testnetscapeplugin"))
2338         return currentPluginLoadPolicy;
2339
2340     // Please don't use any other plug-ins in tests, as they will not be installed on all machines.
2341     RELEASE_ASSERT_NOT_REACHED_WITH_MESSAGE("Unexpected plugin bundle identifier: %s", toSTD(bundleIdentifier).c_str());
2342 #else
2343     return currentPluginLoadPolicy;
2344 #endif
2345 }
2346
2347 void TestController::setBlockAllPlugins(bool shouldBlock)
2348 {
2349     m_shouldBlockAllPlugins = shouldBlock;
2350
2351 #if PLATFORM(MAC)
2352     auto policy = shouldBlock ? kWKPluginLoadClientPolicyBlock : kWKPluginLoadClientPolicyAllow;
2353
2354     WKRetainPtr<WKStringRef> nameNetscape = adoptWK(WKStringCreateWithUTF8CString("com.apple.testnetscapeplugin"));
2355     WKRetainPtr<WKStringRef> nameFlash = adoptWK(WKStringCreateWithUTF8CString("com.macromedia.Flash Player.plugin"));
2356     WKRetainPtr<WKStringRef> emptyString = adoptWK(WKStringCreateWithUTF8CString(""));
2357     WKContextSetPluginLoadClientPolicy(m_context.get(), policy, emptyString.get(), nameNetscape.get(), emptyString.get());
2358     WKContextSetPluginLoadClientPolicy(m_context.get(), policy, emptyString.get(), nameFlash.get(), emptyString.get());
2359 #endif
2360 }
2361
2362 void TestController::setPluginSupportedMode(const String& mode)
2363 {
2364     if (m_unsupportedPluginMode == mode)
2365         return;
2366
2367     m_unsupportedPluginMode = mode;
2368     if (m_unsupportedPluginMode.isEmpty()) {
2369         WKContextClearSupportedPlugins(m_context.get());
2370         return;
2371     }
2372
2373     WKRetainPtr<WKMutableArrayRef> emptyArray = adoptWK(WKMutableArrayCreate());
2374     WKRetainPtr<WKStringRef> allOrigins = adoptWK(WKStringCreateWithUTF8CString(""));
2375     WKRetainPtr<WKStringRef> specificOrigin = adoptWK(WKStringCreateWithUTF8CString("localhost"));
2376
2377     WKRetainPtr<WKStringRef> pdfName = adoptWK(WKStringCreateWithUTF8CString("My personal PDF"));
2378     WKContextAddSupportedPlugin(m_context.get(), allOrigins.get(), pdfName.get(), emptyArray.get(), emptyArray.get());
2379
2380     WKRetainPtr<WKStringRef> nameNetscape = adoptWK(WKStringCreateWithUTF8CString("com.apple.testnetscapeplugin"));
2381     WKRetainPtr<WKStringRef> mimeTypeNetscape = adoptWK(WKStringCreateWithUTF8CString("application/x-webkit-test-netscape"));
2382     WKRetainPtr<WKMutableArrayRef> mimeTypesNetscape = adoptWK(WKMutableArrayCreate());
2383     WKArrayAppendItem(mimeTypesNetscape.get(), mimeTypeNetscape.get());
2384
2385     WKRetainPtr<WKStringRef> namePdf = adoptWK(WKStringCreateWithUTF8CString("WebKit built-in PDF"));
2386
2387     if (m_unsupportedPluginMode == "allOrigins") {
2388         WKContextAddSupportedPlugin(m_context.get(), allOrigins.get(), nameNetscape.get(), mimeTypesNetscape.get(), emptyArray.get());
2389         WKContextAddSupportedPlugin(m_context.get(), allOrigins.get(), namePdf.get(), emptyArray.get(), emptyArray.get());
2390         return;
2391     }
2392
2393     if (m_unsupportedPluginMode == "specificOrigin") {
2394         WKContextAddSupportedPlugin(m_context.get(), specificOrigin.get(), nameNetscape.get(), mimeTypesNetscape.get(), emptyArray.get());
2395         WKContextAddSupportedPlugin(m_context.get(), specificOrigin.get(), namePdf.get(), emptyArray.get(), emptyArray.get());
2396         return;
2397     }
2398 }
2399
2400 void TestController::didCommitNavigation(WKPageRef page, WKNavigationRef navigation)
2401 {
2402     mainWebView()->focus();
2403 }
2404
2405 void TestController::didReceiveServerRedirectForProvisionalNavigation(WKPageRef page, WKNavigationRef navigation, WKTypeRef userData)
2406 {
2407     m_didReceiveServerRedirectForProvisionalNavigation = true;
2408     return;
2409 }
2410
2411 static const char* toString(WKProtectionSpaceAuthenticationScheme scheme)
2412 {
2413     switch (scheme) {
2414     case kWKProtectionSpaceAuthenticationSchemeDefault:
2415         return "ProtectionSpaceAuthenticationSchemeDefault";
2416     case kWKProtectionSpaceAuthenticationSchemeHTTPBasic:
2417         return "ProtectionSpaceAuthenticationSchemeHTTPBasic";
2418     case kWKProtectionSpaceAuthenticationSchemeHTMLForm:
2419         return "ProtectionSpaceAuthenticationSchemeHTMLForm";
2420     case kWKProtectionSpaceAuthenticationSchemeNTLM:
2421         return "ProtectionSpaceAuthenticationSchemeNTLM";
2422     case kWKProtectionSpaceAuthenticationSchemeNegotiate:
2423         return "ProtectionSpaceAuthenticationSchemeNegotiate";
2424     case kWKProtectionSpaceAuthenticationSchemeClientCertificateRequested:
2425         return "ProtectionSpaceAuthenticationSchemeClientCertificateRequested";
2426     case kWKProtectionSpaceAuthenticationSchemeServerTrustEvaluationRequested:
2427         return "ProtectionSpaceAuthenticationSchemeServerTrustEvaluationRequested";
2428     case kWKProtectionSpaceAuthenticationSchemeOAuth:
2429         return "ProtectionSpaceAuthenticationSchemeOAuth";
2430     case kWKProtectionSpaceAuthenticationSchemeUnknown:
2431         return "ProtectionSpaceAuthenticationSchemeUnknown";
2432     }
2433     ASSERT_NOT_REACHED();
2434     return "ProtectionSpaceAuthenticationSchemeUnknown";
2435 }
2436
2437 bool TestController::canAuthenticateAgainstProtectionSpace(WKPageRef page, WKProtectionSpaceRef protectionSpace)
2438 {
2439     if (m_shouldLogCanAuthenticateAgainstProtectionSpace)
2440         m_currentInvocation->outputText("canAuthenticateAgainstProtectionSpace\n");
2441     WKProtectionSpaceAuthenticationScheme authenticationScheme = WKProtectionSpaceGetAuthenticationScheme(protectionSpace);
2442     
2443     if (authenticationScheme == kWKProtectionSpaceAuthenticationSchemeServerTrustEvaluationRequested) {
2444         std::string host = toSTD(adoptWK(WKProtectionSpaceCopyHost(protectionSpace)).get());
2445         return host == "localhost" || host == "127.0.0.1" || (m_allowAnyHTTPSCertificateForAllowedHosts && m_allowedHosts.find(host) != m_allowedHosts.end());
2446     }
2447     
2448     return authenticationScheme <= kWKProtectionSpaceAuthenticationSchemeHTTPDigest || authenticationScheme == kWKProtectionSpaceAuthenticationSchemeOAuth;
2449 }
2450
2451 void TestController::didFinishNavigation(WKPageRef page, WKNavigationRef navigation)
2452 {
2453     if (m_state != Resetting)
2454         return;
2455
2456     WKRetainPtr<WKURLRef> wkURL = adoptWK(WKFrameCopyURL(WKPageGetMainFrame(page)));
2457     if (!WKURLIsEqual(wkURL.get(), blankURL()))
2458         return;
2459
2460     m_doneResetting = true;
2461     singleton().notifyDone();
2462 }
2463
2464 void TestController::didReceiveAuthenticationChallenge(WKPageRef page, WKAuthenticationChallengeRef authenticationChallenge)
2465 {
2466     WKProtectionSpaceRef protectionSpace = WKAuthenticationChallengeGetProtectionSpace(authenticationChallenge);
2467     WKAuthenticationDecisionListenerRef decisionListener = WKAuthenticationChallengeGetDecisionListener(authenticationChallenge);
2468     WKProtectionSpaceAuthenticationScheme authenticationScheme = WKProtectionSpaceGetAuthenticationScheme(protectionSpace);
2469
2470     if (authenticationScheme == kWKProtectionSpaceAuthenticationSchemeServerTrustEvaluationRequested) {
2471         // Any non-empty credential signals to accept the server trust. Since the cross-platform API
2472         // doesn't expose a way to create a credential from server trust, we use a password credential.
2473
2474         m_serverTrustEvaluationCallbackCallsCount++;
2475
2476         if (m_allowsAnySSLCertificate) {
2477             WKRetainPtr<WKCredentialRef> credential = adoptWK(WKCredentialCreate(toWK("accept server trust").get(), toWK("").get(), kWKCredentialPersistenceNone));
2478             WKAuthenticationDecisionListenerUseCredential(decisionListener, credential.get());
2479             return;
2480         }
2481         WKAuthenticationDecisionListenerRejectProtectionSpaceAndContinue(decisionListener);
2482         return;
2483     }
2484
2485     if (m_rejectsProtectionSpaceAndContinueForAuthenticationChallenges) {
2486         m_currentInvocation->outputText("Simulating reject protection space and continue for authentication challenge\n");
2487         WKAuthenticationDecisionListenerRejectProtectionSpaceAndContinue(decisionListener);
2488         return;
2489     }
2490
2491     std::string host = toSTD(adoptWK(WKProtectionSpaceCopyHost(protectionSpace)).get());
2492     int port = WKProtectionSpaceGetPort(protectionSpace);
2493     String message = makeString(host.c_str(), ':', port, " - didReceiveAuthenticationChallenge - ", toString(authenticationScheme), " - ");
2494     if (!m_handlesAuthenticationChallenges)
2495         message.append("Simulating cancelled authentication sheet\n");
2496     else
2497         message.append("Responding with " + m_authenticationUsername + ":" + m_authenticationPassword + "\n");
2498     m_currentInvocation->outputText(message);
2499
2500     if (!m_handlesAuthenticationChallenges) {
2501         WKAuthenticationDecisionListenerUseCredential(decisionListener, 0);
2502         return;
2503     }
2504     WKRetainPtr<WKStringRef> username = adoptWK(WKStringCreateWithUTF8CString(m_authenticationUsername.utf8().data()));
2505     WKRetainPtr<WKStringRef> password = adoptWK(WKStringCreateWithUTF8CString(m_authenticationPassword.utf8().data()));
2506     WKRetainPtr<WKCredentialRef> credential = adoptWK(WKCredentialCreate(username.get(), password.get(), kWKCredentialPersistenceForSession));
2507     WKAuthenticationDecisionListenerUseCredential(decisionListener, credential.get());
2508 }
2509
2510     
2511 // WKContextDownloadClient
2512
2513 void TestController::downloadDidStart(WKContextRef context, WKDownloadRef download, const void* clientInfo)
2514 {
2515     static_cast<TestController*>(const_cast<void*>(clientInfo))->downloadDidStart(context, download);
2516 }
2517     
2518 WKStringRef TestController::decideDestinationWithSuggestedFilename(WKContextRef context, WKDownloadRef download, WKStringRef filename, bool* allowOverwrite, const void* clientInfo)
2519 {
2520     return static_cast<TestController*>(const_cast<void*>(clientInfo))->decideDestinationWithSuggestedFilename(context, download, filename, allowOverwrite);
2521 }
2522
2523 void TestController::downloadDidFinish(WKContextRef context, WKDownloadRef download, const void* clientInfo)
2524 {
2525     static_cast<TestController*>(const_cast<void*>(clientInfo))->downloadDidFinish(context, download);
2526 }
2527
2528 void TestController::downloadDidFail(WKContextRef context, WKDownloadRef download, WKErrorRef error, const void* clientInfo)
2529 {
2530     static_cast<TestController*>(const_cast<void*>(clientInfo))->downloadDidFail(context, download, error);
2531 }
2532
2533 void TestController::downloadDidCancel(WKContextRef context, WKDownloadRef download, const void* clientInfo)
2534 {
2535     static_cast<TestController*>(const_cast<void*>(clientInfo))->downloadDidCancel(context, download);
2536 }
2537
2538 void TestController::downloadDidReceiveServerRedirectToURL(WKContextRef context, WKDownloadRef download, WKURLRef url, const void* clientInfo)
2539 {
2540     static_cast<TestController*>(const_cast<void*>(clientInfo))->downloadDidReceiveServerRedirectToURL(context, download, url);
2541 }
2542
2543 void TestController::downloadDidStart(WKContextRef context, WKDownloadRef download)
2544 {
2545     if (m_shouldLogDownloadCallbacks)
2546         m_currentInvocation->outputText("Download started.\n");
2547 }
2548
2549 WKStringRef TestController::decideDestinationWithSuggestedFilename(WKContextRef, WKDownloadRef, WKStringRef filename, bool*& allowOverwrite)
2550 {
2551     String suggestedFilename = toWTFString(filename);
2552
2553     if (m_shouldLogDownloadCallbacks) {
2554         StringBuilder builder;
2555         builder.append("Downloading URL with suggested filename \"");
2556         builder.append(suggestedFilename);
2557         builder.append("\"\n");
2558         m_currentInvocation->outputText(builder.toString());
2559     }
2560
2561     const char* dumpRenderTreeTemp = libraryPathForTesting();
2562     if (!dumpRenderTreeTemp)
2563         return nullptr;
2564
2565     *allowOverwrite = true;
2566     String temporaryFolder = String::fromUTF8(dumpRenderTreeTemp);
2567     if (suggestedFilename.isEmpty())
2568         suggestedFilename = "Unknown";
2569
2570     return toWK(temporaryFolder + "/" + suggestedFilename).leakRef();
2571 }
2572
2573 void TestController::downloadDidFinish(WKContextRef, WKDownloadRef)
2574 {
2575     if (m_shouldLogDownloadCallbacks)
2576         m_currentInvocation->outputText("Download completed.\n");
2577     m_currentInvocation->notifyDownloadDone();
2578 }
2579
2580 void TestController::downloadDidReceiveServerRedirectToURL(WKContextRef, WKDownloadRef, WKURLRef url)
2581 {
2582     if (m_shouldLogDownloadCallbacks)
2583         m_currentInvocation->outputText(makeString("Download was redirected to \"", toWTFString(adoptWK(WKURLCopyString(url))), "\".\n"));
2584 }
2585
2586 void TestController::downloadDidFail(WKContextRef, WKDownloadRef, WKErrorRef error)
2587 {
2588     if (m_shouldLogDownloadCallbacks) {
2589         m_currentInvocation->outputText("Download failed.\n"_s);
2590
2591         auto domain = toWTFString(adoptWK(WKErrorCopyDomain(error)));
2592         auto description = toWTFString(adoptWK(WKErrorCopyLocalizedDescription(error)));
2593         int code = WKErrorGetErrorCode(error);
2594
2595         m_currentInvocation->outputText(makeString("Failed: ", domain, ", code=", code, ", description=", description, "\n"));
2596     }
2597     m_currentInvocation->notifyDownloadDone();
2598 }
2599
2600 void TestController::downloadDidCancel(WKContextRef, WKDownloadRef)
2601 {
2602     if (m_shouldLogDownloadCallbacks)
2603         m_currentInvocation->outputText("Download cancelled.\n");
2604     m_currentInvocation->notifyDownloadDone();
2605 }
2606
2607 void TestController::processDidCrash()
2608 {
2609     // This function can be called multiple times when crash logs are being saved on Windows, so
2610     // ensure we only print the crashed message once.
2611     if (!m_didPrintWebProcessCrashedMessage) {
2612         pid_t pid = WKPageGetProcessIdentifier(m_mainWebView->page());
2613         fprintf(stderr, "#CRASHED - %s (pid %ld)\n", webProcessName(), static_cast<long>(pid));
2614         fflush(stderr);
2615         m_didPrintWebProcessCrashedMessage = true;
2616     }
2617
2618     if (m_shouldExitWhenWebProcessCrashes)
2619         exit(1);
2620 }
2621
2622 void TestController::didBeginNavigationGesture(WKPageRef)
2623 {
2624     m_currentInvocation->didBeginSwipe();
2625 }
2626
2627 void TestController::willEndNavigationGesture(WKPageRef, WKBackForwardListItemRef)
2628 {
2629     m_currentInvocation->willEndSwipe();
2630 }
2631
2632 void TestController::didEndNavigationGesture(WKPageRef, WKBackForwardListItemRef)
2633 {
2634     m_currentInvocation->didEndSwipe();
2635 }
2636
2637 void TestController::didRemoveNavigationGestureSnapshot(WKPageRef)
2638 {
2639     m_currentInvocation->didRemoveSwipeSnapshot();
2640 }
2641
2642 void TestController::simulateWebNotificationClick(uint64_t notificationID)
2643 {
2644     m_webNotificationProvider.simulateWebNotificationClick(mainWebView()->page(), notificationID);
2645 }
2646
2647 void TestController::setGeolocationPermission(bool enabled)
2648 {
2649     m_isGeolocationPermissionSet = true;
2650     m_isGeolocationPermissionAllowed = enabled;
2651     decidePolicyForGeolocationPermissionRequestIfPossible();
2652 }
2653
2654 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)
2655 {
2656     m_geolocationProvider->setPosition(latitude, longitude, accuracy, providesAltitude, altitude, providesAltitudeAccuracy, altitudeAccuracy, providesHeading, heading, providesSpeed, speed, providesFloorLevel, floorLevel);
2657 }
2658
2659 void TestController::setMockGeolocationPositionUnavailableError(WKStringRef errorMessage)
2660 {
2661     m_geolocationProvider->setPositionUnavailableError(errorMessage);
2662 }
2663
2664 void TestController::handleGeolocationPermissionRequest(WKGeolocationPermissionRequestRef geolocationPermissionRequest)
2665 {
2666     m_geolocationPermissionRequests.append(geolocationPermissionRequest);
2667     decidePolicyForGeolocationPermissionRequestIfPossible();
2668 }
2669
2670 bool TestController::isGeolocationProviderActive() const
2671 {
2672     return m_geolocationProvider->isActive();
2673 }
2674
2675 static String originUserVisibleName(WKSecurityOriginRef origin)
2676 {
2677     if (!origin)
2678         return emptyString();
2679
2680     auto host = toWTFString(adoptWK(WKSecurityOriginCopyHost(origin)));
2681     auto protocol = toWTFString(adoptWK(WKSecurityOriginCopyProtocol(origin)));
2682
2683     if (host.isEmpty() || protocol.isEmpty())
2684         return emptyString();
2685
2686     if (int port = WKSecurityOriginGetPort(origin))
2687         return makeString(protocol, "://", host, ':', port);
2688
2689     return makeString(protocol, "://", host);
2690 }
2691
2692 static String userMediaOriginHash(WKSecurityOriginRef userMediaDocumentOrigin, WKSecurityOriginRef topLevelDocumentOrigin)
2693 {
2694     String userMediaDocumentOriginString = originUserVisibleName(userMediaDocumentOrigin);
2695     String topLevelDocumentOriginString = originUserVisibleName(topLevelDocumentOrigin);
2696
2697     if (topLevelDocumentOriginString.isEmpty())
2698         return userMediaDocumentOriginString;
2699
2700     return makeString(userMediaDocumentOriginString, '-', topLevelDocumentOriginString);
2701 }
2702
2703 static String userMediaOriginHash(WKStringRef userMediaDocumentOriginString, WKStringRef topLevelDocumentOriginString)
2704 {
2705     auto userMediaDocumentOrigin = adoptWK(WKSecurityOriginCreateFromString(userMediaDocumentOriginString));
2706     if (!WKStringGetLength(topLevelDocumentOriginString))
2707         return userMediaOriginHash(userMediaDocumentOrigin.get(), nullptr);
2708
2709     auto topLevelDocumentOrigin = adoptWK(WKSecurityOriginCreateFromString(topLevelDocumentOriginString));
2710     return userMediaOriginHash(userMediaDocumentOrigin.get(), topLevelDocumentOrigin.get());
2711 }
2712
2713 void TestController::setUserMediaPermission(bool enabled)
2714 {
2715     m_isUserMediaPermissionSet = true;
2716     m_isUserMediaPermissionAllowed = enabled;
2717     decidePolicyForUserMediaPermissionRequestIfPossible();
2718 }
2719
2720 void TestController::resetUserMediaPermission()
2721 {
2722     m_isUserMediaPermissionSet = false;
2723 }
2724
2725 void TestController::setShouldDismissJavaScriptAlertsAsynchronously(bool value)
2726 {
2727     m_shouldDismissJavaScriptAlertsAsynchronously = value;
2728 }
2729
2730 void TestController::handleJavaScriptAlert(WKPageRunJavaScriptAlertResultListenerRef listener)
2731 {
2732     if (!m_shouldDismissJavaScriptAlertsAsynchronously) {
2733         WKPageRunJavaScriptAlertResultListenerCall(listener);
2734         return;
2735     }
2736
2737     WKRetain(listener);
2738     callOnMainThread([listener] {
2739         WKPageRunJavaScriptAlertResultListenerCall(listener);
2740         WKRelease(listener);
2741     });
2742 }
2743
2744 class OriginSettings : public RefCounted<OriginSettings> {
2745 public:
2746     explicit OriginSettings()
2747     {
2748     }
2749
2750     bool persistentPermission() const { return m_persistentPermission; }
2751     void setPersistentPermission(bool permission) { m_persistentPermission = permission; }
2752
2753     String persistentSalt() const { return m_persistentSalt; }
2754     void setPersistentSalt(const String& salt) { m_persistentSalt = salt; }
2755
2756     HashMap<uint64_t, String>& ephemeralSalts() { return m_ephemeralSalts; }
2757
2758     void incrementRequestCount() { ++m_requestCount; }
2759     void resetRequestCount() { m_requestCount = 0; }
2760     unsigned requestCount() const { return m_requestCount; }
2761
2762 private:
2763     HashMap<uint64_t, String> m_ephemeralSalts;
2764     String m_persistentSalt;
2765     unsigned m_requestCount { 0 };
2766     bool m_persistentPermission { false };
2767 };
2768
2769 String TestController::saltForOrigin(WKFrameRef frame, String originHash)
2770 {
2771     auto& settings = settingsForOrigin(originHash);
2772     auto& ephemeralSalts = settings.ephemeralSalts();
2773     auto frameHandle = adoptWK(WKFrameCreateFrameHandle(frame));
2774     uint64_t frameIdentifier = WKFrameHandleGetFrameID(frameHandle.get());
2775     String frameSalt = ephemeralSalts.get(frameIdentifier);
2776
2777     if (settings.persistentPermission()) {
2778         if (frameSalt.length())
2779             return frameSalt;
2780
2781         if (!settings.persistentSalt().length())
2782             settings.setPersistentSalt(createCanonicalUUIDString());
2783
2784         return settings.persistentSalt();
2785     }
2786
2787     if (!frameSalt.length()) {
2788         frameSalt = createCanonicalUUIDString();
2789         ephemeralSalts.add(frameIdentifier, frameSalt);
2790     }
2791
2792     return frameSalt;
2793 }
2794
2795 void TestController::setUserMediaPersistentPermissionForOrigin(bool permission, WKStringRef userMediaDocumentOriginString, WKStringRef topLevelDocumentOriginString)
2796 {
2797     auto originHash = userMediaOriginHash(userMediaDocumentOriginString, topLevelDocumentOriginString);
2798     auto& settings = settingsForOrigin(originHash);
2799     settings.setPersistentPermission(permission);
2800 }
2801
2802 void TestController::handleCheckOfUserMediaPermissionForOrigin(WKFrameRef frame, WKSecurityOriginRef userMediaDocumentOrigin, WKSecurityOriginRef topLevelDocumentOrigin, const WKUserMediaPermissionCheckRef& checkRequest)
2803 {
2804     auto originHash = userMediaOriginHash(userMediaDocumentOrigin, topLevelDocumentOrigin);
2805     auto salt = saltForOrigin(frame, originHash);
2806     WKRetainPtr<WKStringRef> saltString = adoptWK(WKStringCreateWithUTF8CString(salt.utf8().data()));
2807
2808     WKUserMediaPermissionCheckSetUserMediaAccessInfo(checkRequest, saltString.get(), settingsForOrigin(originHash).persistentPermission());
2809 }
2810
2811 bool TestController::handleDeviceOrientationAndMotionAccessRequest(WKSecurityOriginRef origin)
2812 {
2813     m_currentInvocation->outputText(makeString("Received device orientation & motion access request for security origin \"", originUserVisibleName(origin), "\".\n"));
2814     return m_shouldAllowDeviceOrientationAndMotionAccess;
2815 }
2816
2817 void TestController::handleUserMediaPermissionRequest(WKFrameRef frame, WKSecurityOriginRef userMediaDocumentOrigin, WKSecurityOriginRef topLevelDocumentOrigin, WKUserMediaPermissionRequestRef request)
2818 {
2819     auto originHash = userMediaOriginHash(userMediaDocumentOrigin, topLevelDocumentOrigin);
2820     m_userMediaPermissionRequests.append(std::make_pair(originHash, request));
2821     decidePolicyForUserMediaPermissionRequestIfPossible();
2822 }
2823
2824 OriginSettings& TestController::settingsForOrigin(const String& originHash)
2825 {
2826     RefPtr<OriginSettings> settings = m_cachedUserMediaPermissions.get(originHash);
2827     if (!settings) {
2828         settings = adoptRef(*new OriginSettings());
2829         m_cachedUserMediaPermissions.add(originHash, settings);
2830     }
2831
2832     return *settings;
2833 }
2834
2835 unsigned TestController::userMediaPermissionRequestCountForOrigin(WKStringRef userMediaDocumentOriginString, WKStringRef topLevelDocumentOriginString)
2836 {
2837     auto originHash = userMediaOriginHash(userMediaDocumentOriginString, topLevelDocumentOriginString);
2838     return settingsForOrigin(originHash).requestCount();
2839 }
2840
2841 void TestController::resetUserMediaPermissionRequestCountForOrigin(WKStringRef userMediaDocumentOriginString, WKStringRef topLevelDocumentOriginString)
2842 {
2843     auto originHash = userMediaOriginHash(userMediaDocumentOriginString, topLevelDocumentOriginString);
2844     settingsForOrigin(originHash).resetRequestCount();
2845 }
2846
2847 void TestController::decidePolicyForUserMediaPermissionRequestIfPossible()
2848 {
2849     if (!m_isUserMediaPermissionSet)
2850         return;
2851
2852     for (auto& pair : m_userMediaPermissionRequests) {
2853         auto originHash = pair.first;
2854         auto request = pair.second.get();
2855
2856         auto& settings = settingsForOrigin(originHash);
2857         settings.incrementRequestCount();
2858
2859         if (!m_isUserMediaPermissionAllowed && !settings.persistentPermission()) {
2860             WKUserMediaPermissionRequestDeny(request, kWKPermissionDenied);
2861             continue;
2862         }
2863
2864         WKRetainPtr<WKArrayRef> audioDeviceUIDs = adoptWK(WKUserMediaPermissionRequestAudioDeviceUIDs(request));
2865         WKRetainPtr<WKArrayRef> videoDeviceUIDs = adoptWK(WKUserMediaPermissionRequestVideoDeviceUIDs(request));
2866
2867         if (!WKArrayGetSize(videoDeviceUIDs.get()) && !WKArrayGetSize(audioDeviceUIDs.get())) {
2868             WKUserMediaPermissionRequestDeny(request, kWKNoConstraints);
2869             continue;
2870         }
2871
2872         WKRetainPtr<WKStringRef> videoDeviceUID;
2873         if (WKArrayGetSize(videoDeviceUIDs.get()))
2874             videoDeviceUID = reinterpret_cast<WKStringRef>(WKArrayGetItemAtIndex(videoDeviceUIDs.get(), 0));
2875         else
2876             videoDeviceUID = adoptWK(WKStringCreateWithUTF8CString(""));
2877
2878         WKRetainPtr<WKStringRef> audioDeviceUID;
2879         if (WKArrayGetSize(audioDeviceUIDs.get()))
2880             audioDeviceUID = reinterpret_cast<WKStringRef>(WKArrayGetItemAtIndex(audioDeviceUIDs.get(), 0));
2881         else
2882             audioDeviceUID = adoptWK(WKStringCreateWithUTF8CString(""));
2883
2884         WKUserMediaPermissionRequestAllow(request, audioDeviceUID.get(), videoDeviceUID.get());
2885     }
2886     m_userMediaPermissionRequests.clear();
2887 }
2888
2889 void TestController::setCustomPolicyDelegate(bool enabled, bool permissive)
2890 {
2891     m_policyDelegateEnabled = enabled;
2892     m_policyDelegatePermissive = permissive;
2893 }
2894
2895 void TestController::decidePolicyForGeolocationPermissionRequestIfPossible()
2896 {
2897     if (!m_isGeolocationPermissionSet)
2898         return;
2899
2900     for (size_t i = 0; i < m_geolocationPermissionRequests.size(); ++i) {
2901         WKGeolocationPermissionRequestRef permissionRequest = m_geolocationPermissionRequests[i].get();
2902         if (m_isGeolocationPermissionAllowed)
2903             WKGeolocationPermissionRequestAllow(permissionRequest);
2904         else
2905             WKGeolocationPermissionRequestDeny(permissionRequest);
2906     }
2907     m_geolocationPermissionRequests.clear();
2908 }
2909
2910 void TestController::decidePolicyForNotificationPermissionRequest(WKPageRef page, WKSecurityOriginRef origin, WKNotificationPermissionRequestRef request, const void*)
2911 {
2912     TestController::singleton().decidePolicyForNotificationPermissionRequest(page, origin, request);
2913 }
2914
2915 void TestController::decidePolicyForNotificationPermissionRequest(WKPageRef, WKSecurityOriginRef, WKNotificationPermissionRequestRef request)
2916 {
2917     WKNotificationPermissionRequestAllow(request);
2918 }
2919
2920 void TestController::unavailablePluginButtonClicked(WKPageRef, WKPluginUnavailabilityReason, WKDictionaryRef, const void*)
2921 {
2922     printf("MISSING PLUGIN BUTTON PRESSED\n");
2923 }
2924
2925 void TestController::decidePolicyForNavigationAction(WKPageRef, WKNavigationActionRef navigationAction, WKFramePolicyListenerRef listener, WKTypeRef, const void* clientInfo)
2926 {
2927     static_cast<TestController*>(const_cast<void*>(clientInfo))->decidePolicyForNavigationAction(navigationAction, listener);
2928 }
2929
2930 void TestController::decidePolicyForNavigationAction(WKNavigationActionRef navigationAction, WKFramePolicyListenerRef listener)
2931 {
2932     WKRetainPtr<WKFramePolicyListenerRef> retainedListener { listener };
2933     WKRetainPtr<WKNavigationActionRef> retainedNavigationAction { navigationAction };
2934     const bool shouldIgnore { m_policyDelegateEnabled && !m_policyDelegatePermissive };
2935     auto decisionFunction = [shouldIgnore, retainedListener, retainedNavigationAction, shouldSwapToEphemeralSessionOnNextNavigation = m_shouldSwapToEphemeralSessionOnNextNavigation, shouldSwapToDefaultSessionOnNextNavigation = m_shouldSwapToDefaultSessionOnNextNavigation]() {
2936         if (shouldIgnore)
2937             WKFramePolicyListenerIgnore(retainedListener.get());
2938         else if (WKNavigationActionShouldPerformDownload(retainedNavigationAction.get()))
2939             WKFramePolicyListenerDownload(retainedListener.get());
2940         else {
2941             if (shouldSwapToEphemeralSessionOnNextNavigation || shouldSwapToDefaultSessionOnNextNavigation) {
2942                 ASSERT(shouldSwapToEphemeralSessionOnNextNavigation != shouldSwapToDefaultSessionOnNextNavigation);
2943                 auto policies = adoptWK(WKWebsitePoliciesCreate());
2944                 WKRetainPtr<WKWebsiteDataStoreRef> newSession = TestController::defaultWebsiteDataStore();
2945                 if (shouldSwapToEphemeralSessionOnNextNavigation)
2946                     newSession = adoptWK(WKWebsiteDataStoreCreateNonPersistentDataStore());
2947                 WKWebsitePoliciesSetDataStore(policies.get(), newSession.get());
2948                 WKFramePolicyListenerUseWithPolicies(retainedListener.get(), policies.get());
2949             } else
2950                 WKFramePolicyListenerUse(retainedListener.get());
2951         }
2952     };
2953     m_shouldSwapToEphemeralSessionOnNextNavigation = false;
2954     m_shouldSwapToDefaultSessionOnNextNavigation = false;
2955
2956     if (m_shouldDecideNavigationPolicyAfterDelay)
2957         RunLoop::main().dispatch(WTFMove(decisionFunction));
2958     else
2959         decisionFunction();
2960 }
2961
2962 void TestController::decidePolicyForNavigationResponse(WKPageRef, WKNavigationResponseRef navigationResponse, WKFramePolicyListenerRef listener, WKTypeRef, const void* clientInfo)
2963 {
2964     static_cast<TestController*>(const_cast<void*>(clientInfo))->decidePolicyForNavigationResponse(navigationResponse, listener);
2965 }
2966
2967 void TestController::decidePolicyForNavigationResponse(WKNavigationResponseRef navigationResponse, WKFramePolicyListenerRef listener)
2968 {
2969     WKRetainPtr<WKNavigationResponseRef> retainedNavigationResponse { navigationResponse };
2970     WKRetainPtr<WKFramePolicyListenerRef> retainedListener { listener };
2971
2972     bool shouldDownloadUndisplayableMIMETypes = m_shouldDownloadUndisplayableMIMETypes;
2973     auto decisionFunction = [shouldDownloadUndisplayableMIMETypes, retainedNavigationResponse, retainedListener]() {
2974         // Even though Response was already checked by WKBundlePagePolicyClient, the check did not include plugins
2975         // so we have to re-check again.
2976         if (WKNavigationResponseCanShowMIMEType(retainedNavigationResponse.get())) {
2977             WKFramePolicyListenerUse(retainedListener.get());
2978             return;
2979         }
2980
2981         if (shouldDownloadUndisplayableMIMETypes)
2982             WKFramePolicyListenerDownload(retainedListener.get());
2983         else
2984             WKFramePolicyListenerIgnore(retainedListener.get());
2985     };
2986
2987     if (m_shouldDecideResponsePolicyAfterDelay)
2988         RunLoop::main().dispatch(WTFMove(decisionFunction));
2989     else
2990         decisionFunction();
2991 }
2992
2993 void TestController::didNavigateWithNavigationData(WKContextRef, WKPageRef, WKNavigationDataRef navigationData, WKFrameRef frame, const void* clientInfo)
2994 {
2995     static_cast<TestController*>(const_cast<void*>(clientInfo))->didNavigateWithNavigationData(navigationData, frame);
2996 }
2997
2998 void TestController::didNavigateWithNavigationData(WKNavigationDataRef navigationData, WKFrameRef)
2999 {
3000     if (m_state != RunningTest)
3001         return;
3002
3003     if (!m_shouldLogHistoryClientCallbacks)
3004         return;
3005
3006     // URL
3007     auto url = adoptWK(WKNavigationDataCopyURL(navigationData));
3008     auto urlString = toWTFString(adoptWK(WKURLCopyString(url.get())));
3009     // Title
3010     auto title = toWTFString(adoptWK(WKNavigationDataCopyTitle(navigationData)));
3011     // HTTP method
3012     auto request = adoptWK(WKNavigationDataCopyOriginalRequest(navigationData));
3013     auto method = toWTFString(adoptWK(WKURLRequestCopyHTTPMethod(request.get())));
3014
3015     // FIXME: Determine whether the navigation was successful / a client redirect rather than hard-coding the message here.
3016     m_currentInvocation->outputText(makeString("WebView navigated to url \"", urlString, "\" with title \"", title, "\" with HTTP equivalent method \"", method,
3017         "\".  The navigation was successful and was not a client redirect.\n"));
3018 }
3019
3020 void TestController::didPerformClientRedirect(WKContextRef, WKPageRef, WKURLRef sourceURL, WKURLRef destinationURL, WKFrameRef frame, const void* clientInfo)
3021 {
3022     static_cast<TestController*>(const_cast<void*>(clientInfo))->didPerformClientRedirect(sourceURL, destinationURL, frame);
3023 }
3024
3025 void TestController::didPerformClientRedirect(WKURLRef sourceURL, WKURLRef destinationURL, WKFrameRef)
3026 {
3027     if (m_state != RunningTest)
3028         return;
3029
3030     if (!m_shouldLogHistoryClientCallbacks)
3031         return;
3032
3033     auto source = toWTFString(adoptWK(WKURLCopyString(sourceURL)));
3034     auto destination = toWTFString(adoptWK(WKURLCopyString(destinationURL)));
3035
3036     m_currentInvocation->outputText(makeString("WebView performed a client redirect from \"", source, "\" to \"", destination, "\".\n"));
3037 }
3038
3039 void TestController::didPerformServerRedirect(WKContextRef, WKPageRef, WKURLRef sourceURL, WKURLRef destinationURL, WKFrameRef frame, const void* clientInfo)
3040 {
3041     static_cast<TestController*>(const_cast<void*>(clientInfo))->didPerformServerRedirect(sourceURL, destinationURL, frame);
3042 }
3043
3044 void TestController::didPerformServerRedirect(WKURLRef sourceURL, WKURLRef destinationURL, WKFrameRef)
3045 {
3046     if (m_state != RunningTest)
3047         return;
3048
3049     if (!m_shouldLogHistoryClientCallbacks)
3050         return;
3051
3052     auto source = toWTFString(adoptWK(WKURLCopyString(sourceURL)));
3053     auto destination = toWTFString(adoptWK(WKURLCopyString(destinationURL)));
3054
3055     m_currentInvocation->outputText(makeString("WebView performed a server redirect from \"", source, "\" to \"", destination, "\".\n"));
3056 }
3057
3058 void TestController::didUpdateHistoryTitle(WKContextRef, WKPageRef, WKStringRef title, WKURLRef URL, WKFrameRef frame, const void* clientInfo)
3059 {
3060     static_cast<TestController*>(const_cast<void*>(clientInfo))->didUpdateHistoryTitle(title, URL, frame);
3061 }
3062
3063 void TestController::didUpdateHistoryTitle(WKStringRef title, WKURLRef URL, WKFrameRef)
3064 {
3065     if (m_state != RunningTest)
3066         return;
3067
3068     if (!m_shouldLogHistoryClientCallbacks)
3069         return;
3070
3071     auto urlString = toWTFString(adoptWK(WKURLCopyString(URL)));
3072     m_currentInvocation->outputText(makeString("WebView updated the title for history URL \"", urlString, "\" to \"", toWTFString(title), "\".\n"));
3073 }
3074
3075 void TestController::setNavigationGesturesEnabled(bool value)
3076 {
3077     m_mainWebView->setNavigationGesturesEnabled(value);
3078 }
3079
3080 void TestController::setIgnoresViewportScaleLimits(bool ignoresViewportScaleLimits)
3081 {
3082     WKPageSetIgnoresViewportScaleLimits(m_mainWebView->page(), ignoresViewportScaleLimits);
3083 }
3084
3085 void TestController::terminateNetworkProcess()
3086 {
3087     WKContextTerminateNetworkProcess(platformContext());
3088 }
3089
3090 void TestController::terminateServiceWorkers()
3091 {
3092     WKContextTerminateServiceWorkers(platformContext());
3093 }
3094
3095 #if !PLATFORM(COCOA)
3096 void TestController::platformWillRunTest(const TestInvocation&)
3097 {
3098 }
3099
3100 void TestController::platformCreateWebView(WKPageConfigurationRef configuration, const TestOptions& options)
3101 {
3102     m_mainWebView = makeUnique<PlatformWebView>(configuration, options);
3103 }
3104
3105 PlatformWebView* TestController::platformCreateOtherPage(PlatformWebView* parentView, WKPageConfigurationRef configuration, const TestOptions& options)
3106 {
3107     return new PlatformWebView(configuration, options);
3108 }
3109
3110 WKContextRef TestController::platformAdjustContext(WKContextRef context, WKContextConfigurationRef contextConfiguration)
3111 {
3112     return context;
3113 }
3114
3115 bool TestController::platformResetStateToConsistentValues(const TestOptions&)
3116 {
3117     return true;
3118 }
3119
3120 unsigned TestController::imageCountInGeneralPasteboard() const
3121 {
3122     return 0;
3123 }
3124
3125 void TestController::removeAllSessionCredentials()
3126 {
3127 }
3128
3129 void TestController::getAllStorageAccessEntries()
3130 {
3131 }
3132
3133 void TestController::getPrevalentDomains()
3134 {
3135 }
3136
3137 void TestController::clearPrevalentDomains()
3138 {
3139 }
3140
3141 void TestController::getWebViewCategory()
3142 {
3143 }
3144
3145 #endif
3146
3147 struct ClearServiceWorkerRegistrationsCallbackContext {
3148     explicit ClearServiceWorkerRegistrationsCallbackContext(TestController& controller)
3149         : testController(controller)
3150     {
3151     }
3152
3153     TestController& testController;
3154     bool done { false };
3155 };
3156
3157 static void clearServiceWorkerRegistrationsCallback(void* userData)
3158 {
3159     auto* context = static_cast<ClearServiceWorkerRegistrationsCallbackContext*>(userData);
3160     context->done = true;
3161     context->testController.notifyDone();
3162 }
3163
3164 void TestController::clearServiceWorkerRegistrations()
3165 {
3166     ClearServiceWorkerRegistrationsCallbackContext context(*this);
3167
3168     WKWebsiteDataStoreRemoveAllServiceWorkerRegistrations(TestController::defaultWebsiteDataStore(), &context, clearServiceWorkerRegistrationsCallback);
3169     runUntil(context.done, noTimeout);
3170 }
3171
3172 struct ClearDOMCacheCallbackContext {
3173     explicit ClearDOMCacheCallbackContext(TestController& controller)
3174         : testController(controller)
3175     {
3176     }
3177
3178     TestController& testController;
3179     bool done { false };
3180 };
3181
3182 static void clearDOMCacheCallback(void* userData)
3183 {
3184     auto* context = static_cast<ClearDOMCacheCallbackContext*>(userData);
3185     context->done = true;
3186     context->testController.notifyDone();
3187 }
3188
3189 void TestController::clearDOMCache(WKStringRef origin)
3190 {
3191     ClearDOMCacheCallbackContext context(*this);
3192
3193     auto cacheOrigin = adoptWK(WKSecurityOriginCreateFromString(origin));
3194     WKWebsiteDataStoreRemoveFetchCacheForOrigin(TestController::defaultWebsiteDataStore(), cacheOrigin.get(), &context, clearDOMCacheCallback);
3195     runUntil(context.done, noTimeout);
3196 }
3197
3198 void TestController::clearDOMCaches()
3199 {
3200     ClearDOMCacheCallbackContext context(*this);
3201
3202     WKWebsiteDataStoreRemoveAllFetchCaches(TestController::defaultWebsiteDataStore(), &context, clearDOMCacheCallback);
3203     runUntil(context.done, noTimeout);
3204 }
3205
3206 struct StorageVoidCallbackContext {
3207     explicit StorageVoidCallbackContext(TestController& controller)
3208         : testController(controller)
3209     {
3210     }
3211
3212     TestController& testController;
3213     bool done { false };
3214 };
3215
3216 static void StorageVoidCallback(void* userData)
3217 {
3218     auto* context = static_cast<StorageVoidCallbackContext*>(userData);
3219     context->done = true;
3220     context->testController.notifyDone();
3221 }
3222
3223 void TestController::clearIndexedDatabases()
3224 {
3225     StorageVoidCallbackContext context(*this);
3226     WKWebsiteDataStoreRemoveAllIndexedDatabases(websiteDataStore(), &context, StorageVoidCallback);
3227     runUntil(context.done, noTimeout);
3228 }
3229
3230 void TestController::clearLocalStorage()
3231 {
3232     StorageVoidCallbackContext context(*this);
3233     WKWebsiteDataStoreRemoveLocalStorage(websiteDataStore(), &context, StorageVoidCallback);
3234     runUntil(context.done, noTimeout);
3235
3236     StorageVoidCallbackContext legacyContext(*this);
3237     WKContextClearLegacyPrivateBrowsingLocalStorage(platformContext(), &legacyContext, StorageVoidCallback);
3238     runUntil(legacyContext.done, noTimeout);
3239 }
3240
3241 void TestController::syncLocalStorage()
3242 {
3243     StorageVoidCallbackContext context(*this);
3244     WKContextSyncLocalStorage(platformContext(), &context, StorageVoidCallback);
3245     runUntil(context.done, noTimeout);
3246 }
3247
3248 void TestController::resetQuota()
3249 {
3250     StorageVoidCallbackContext context(*this);
3251     WKWebsiteDataStoreResetQuota(TestController::websiteDataStore(), &context, StorageVoidCallback);
3252     runUntil(context.done, noTimeout);
3253 }
3254
3255 struct FetchCacheOriginsCallbackContext {
3256     FetchCacheOriginsCallbackContext(TestController& controller, WKStringRef origin)
3257         : testController(controller)
3258         , origin(origin)
3259     {
3260     }
3261
3262     TestController& testController;
3263     WKStringRef origin;
3264
3265     bool done { false };
3266     bool result { false };
3267 };
3268
3269 static void fetchCacheOriginsCallback(WKArrayRef origins, void* userData)
3270 {
3271     auto* context = static_cast<FetchCacheOriginsCallbackContext*>(userData);
3272     context->done = true;
3273
3274     auto size = WKArrayGetSize(origins);
3275     for (size_t index = 0; index < size && !context->result; ++index) {
3276         WKSecurityOriginRef securityOrigin = reinterpret_cast<WKSecurityOriginRef>(WKArrayGetItemAtIndex(origins, index));
3277         if (WKStringIsEqual(context->origin, adoptWK(WKSecurityOriginCopyToString(securityOrigin)).get()))
3278             context->result = true;
3279     }
3280     context->testController.notifyDone();
3281 }
3282
3283 bool TestController::hasDOMCache(WKStringRef origin)
3284 {
3285     FetchCacheOriginsCallbackContext context(*this, origin);
3286     WKWebsiteDataStoreGetFetchCacheOrigins(TestController::defaultWebsiteDataStore(), &context, fetchCacheOriginsCallback);
3287     runUntil(context.done, noTimeout);
3288     return context.result;
3289 }
3290
3291 struct FetchCacheSizeForOriginCallbackContext {
3292     explicit FetchCacheSizeForOriginCallbackContext(TestController& controller)
3293         : testController(controller)
3294     {
3295     }
3296
3297     TestController& testController;
3298
3299     bool done { false };
3300     uint64_t result { 0 };
3301 };
3302
3303 static void fetchCacheSizeForOriginCallback(uint64_t size, void* userData)
3304 {
3305     auto* context = static_cast<FetchCacheSizeForOriginCallbackContext*>(userData);
3306     context->done = true;
3307     context->result = size;
3308     context->testController.notifyDone();
3309 }
3310
3311 uint64_t TestController::domCacheSize(WKStringRef origin)
3312 {
3313     FetchCacheSizeForOriginCallbackContext context(*this);
3314     WKWebsiteDataStoreGetFetchCacheSizeForOrigin(TestController::defaultWebsiteDataStore(), origin, &context, fetchCacheSizeForOriginCallback);
3315     runUntil(context.done, noTimeout);
3316     return context.result;
3317 }
3318
3319 #if !PLATFORM(COCOA)
3320 void TestController::setAllowStorageQuotaIncrease(bool)
3321 {
3322     // FIXME: To implement.
3323 }
3324
3325 bool TestController::isDoingMediaCapture() const
3326 {
3327     return false;
3328 }
3329
3330 #endif
3331
3332 struct ResourceStatisticsCallbackContext {
3333     explicit ResourceStatisticsCallbackContext(TestController& controller)
3334         : testController(controller)
3335     {
3336     }
3337
3338     TestController& testController;
3339     bool done { false };
3340     bool result { false };
3341     WKRetainPtr<WKStringRef> resourceLoadStatisticsRepresentation;
3342 };
3343     
3344 static void resourceStatisticsStringResultCallback(WKStringRef resourceLoadStatisticsRepresentation, void* userData)
3345 {
3346     auto* context = static_cast<ResourceStatisticsCallbackContext*>(userData);
3347     context->resourceLoadStatisticsRepresentation = resourceLoadStatisticsRepresentation;
3348     context->done = true;
3349     context->testController.notifyDone();
3350 }
3351
3352 static void resourceStatisticsVoidResultCallback(void* userData)
3353 {
3354     auto* context = static_cast<ResourceStatisticsCallbackContext*>(userData);
3355     context->done = true;
3356     context->testController.notifyDone();
3357 }
3358
3359 static void resourceStatisticsBooleanResultCallback(bool result, void* userData)
3360 {
3361     auto* context = static_cast<ResourceStatisticsCallbackContext*>(userData);
3362     context->result = result;
3363     context->done = true;
3364     context->testController.notifyDone();
3365 }
3366
3367 void TestController::setStatisticsEnabled(bool value)
3368 {
3369     WKWebsiteDataStoreSetResourceLoadStatisticsEnabled(websiteDataStore(), value);
3370 }
3371
3372 bool TestController::isStatisticsEphemeral()
3373 {
3374     ResourceStatisticsCallbackContext context(*this);
3375     WKWebsiteDataStoreIsStatisticsEphemeral(websiteDataStore(), &context, resourceStatisticsBooleanResultCallback);
3376     runUntil(context.done, noTimeout);
3377     return context.result;
3378 }
3379
3380 void TestController::setStatisticsDebugMode(bool value)
3381 {
3382     ResourceStatisticsCallbackContext context(*this);
3383     WKWebsiteDataStoreSetResourceLoadStatisticsDebugModeWithCompletionHandler(websiteDataStore(), value, &context, resourceStatisticsVoidResultCallback);
3384     runUntil(context.done, noTimeout);
3385     m_currentInvocation->didSetStatisticsDebugMode();
3386 }
3387
3388 void TestController::setStatisticsPrevalentResourceForDebugMode(WKStringRef hostName)
3389 {
3390     ResourceStatisticsCallbackContext context(*this);
3391     WKWebsiteDataStoreSetResourceLoadStatisticsPrevalentResourceForDebugMode(websiteDataStore(), hostName, &context, resourceStatisticsVoidResultCallback);
3392     runUntil(context.done, noTimeout);
3393     m_currentInvocation->didSetPrevalentResourceForDebugMode();
3394 }
3395
3396 void TestController::setStatisticsLastSeen(WKStringRef host, double seconds)
3397 {
3398     ResourceStatisticsCallbackContext context(*this);
3399     WKWebsiteDataStoreSetStatisticsLastSeen(websiteDataStore(), host, seconds, &context, resourceStatisticsVoidResultCallback);
3400     runUntil(context.done, noTimeout);
3401     m_currentInvocation->didSetLastSeen();
3402 }
3403
3404 void TestController::setStatisticsMergeStatistic(WKStringRef host, WKStringRef topFrameDomain1, WKStringRef topFrameDomain2, double lastSeen, bool hadUserInteraction, double mostRecentUserInteraction, bool isGrandfathered, bool isPrevalent, bool isVeryPrevalent, int dataRecordsRemoved)
3405 {
3406     ResourceStatisticsCallbackContext context(*this);
3407     WKWebsiteDataStoreSetStatisticsMergeStatistic(websiteDataStore(), host, topFrameDomain1, topFrameDomain2, lastSeen, hadUserInteraction, mostRecentUserInteraction, isGrandfathered, isPrevalent, isVeryPrevalent, dataRecordsRemoved, &context, resourceStatisticsVoidResultCallback);
3408     runUntil(context.done, noTimeout);
3409     m_currentInvocation->didMergeStatistic();
3410 }
3411
3412 void TestController::setStatisticsPrevalentResource(WKStringRef host, bool value)
3413 {
3414     ResourceStatisticsCallbackContext context(*this);
3415     WKWebsiteDataStoreSetStatisticsPrevalentResource(websiteDataStore(), host, value, &context, resourceStatisticsVoidResultCallback);
3416     runUntil(context.done, noTimeout);
3417     m_currentInvocation->didSetPrevalentResource();
3418 }
3419
3420 void TestController::setStatisticsVeryPrevalentResource(WKStringRef host, bool value)
3421 {
3422     ResourceStatisticsCallbackContext context(*this);
3423     WKWebsiteDataStoreSetStatisticsVeryPrevalentResource(websiteDataStore(), host, value, &context, resourceStatisticsVoidResultCallback);
3424     runUntil(context.done, noTimeout);
3425     m_currentInvocation->didSetVeryPrevalentResource();
3426 }
3427     
3428 String TestController::dumpResourceLoadStatistics()
3429 {
3430     ResourceStatisticsCallbackContext context(*this);
3431     WKWebsiteDataStoreDumpResourceLoadStatistics(websiteDataStore(), &context, resourceStatisticsStringResultCallback);
3432     runUntil(context.done, noTimeout);
3433     return toWTFString(context.resourceLoadStatisticsRepresentation.get());
3434 }
3435
3436 bool TestController::isStatisticsPrevalentResource(WKStringRef host)
3437 {
3438     ResourceStatisticsCallbackContext context(*this);
3439     WKWebsiteDataStoreIsStatisticsPrevalentResource(websiteDataStore(), host, &context, resourceStatisticsBooleanResultCallback);
3440     runUntil(context.done, noTimeout);