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