9bde273593c5653a6859aa48b65d8fa1e1ea009b
[WebKit-https.git] / Tools / WebKitTestRunner / TestController.cpp
1 /*
2  * Copyright (C) 2010-2020 Apple Inc. All rights reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions
6  * are met:
7  * 1. Redistributions of source code must retain the above copyright
8  *    notice, this list of conditions and the following disclaimer.
9  * 2. Redistributions in binary form must reproduce the above copyright
10  *    notice, this list of conditions and the following disclaimer in the
11  *    documentation and/or other materials provided with the distribution.
12  *
13  * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
14  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
15  * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16  * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
17  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
18  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
19  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
20  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
21  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
22  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
23  * THE POSSIBILITY OF SUCH DAMAGE.
24  */
25
26 #include "config.h"
27 #include "TestController.h"
28
29 #include "EventSenderProxy.h"
30 #include "Options.h"
31 #include "PlatformWebView.h"
32 #include "StringFunctions.h"
33 #include "TestInvocation.h"
34 #include "WebCoreTestSupport.h"
35 #include <JavaScriptCore/InitializeThreading.h>
36 #include <WebKit/WKArray.h>
37 #include <WebKit/WKAuthenticationChallenge.h>
38 #include <WebKit/WKAuthenticationDecisionListener.h>
39 #include <WebKit/WKContextConfigurationRef.h>
40 #include <WebKit/WKContextPrivate.h>
41 #include <WebKit/WKCredential.h>
42 #include <WebKit/WKFrameHandleRef.h>
43 #include <WebKit/WKFrameInfoRef.h>
44 #include <WebKit/WKHTTPCookieStoreRef.h>
45 #include <WebKit/WKIconDatabase.h>
46 #include <WebKit/WKMessageListener.h>
47 #include <WebKit/WKMockDisplay.h>
48 #include <WebKit/WKMockMediaDevice.h>
49 #include <WebKit/WKNavigationActionRef.h>
50 #include <WebKit/WKNavigationResponseRef.h>
51 #include <WebKit/WKNotification.h>
52 #include <WebKit/WKNotificationManager.h>
53 #include <WebKit/WKNotificationPermissionRequest.h>
54 #include <WebKit/WKNumber.h>
55 #include <WebKit/WKOpenPanelResultListener.h>
56 #include <WebKit/WKPageGroup.h>
57 #include <WebKit/WKPageInjectedBundleClient.h>
58 #include <WebKit/WKPagePrivate.h>
59 #include <WebKit/WKPluginInformation.h>
60 #include <WebKit/WKPreferencesRefPrivate.h>
61 #include <WebKit/WKProtectionSpace.h>
62 #include <WebKit/WKRetainPtr.h>
63 #include <WebKit/WKSecurityOriginRef.h>
64 #include <WebKit/WKTextChecker.h>
65 #include <WebKit/WKURL.h>
66 #include <WebKit/WKUserContentControllerRef.h>
67 #include <WebKit/WKUserContentExtensionStoreRef.h>
68 #include <WebKit/WKUserMediaPermissionCheck.h>
69 #include <WebKit/WKWebsiteDataStoreConfigurationRef.h>
70 #include <WebKit/WKWebsiteDataStoreRef.h>
71 #include <WebKit/WKWebsitePolicies.h>
72 #include <algorithm>
73 #include <cstdio>
74 #include <ctype.h>
75 #include <fstream>
76 #include <stdlib.h>
77 #include <string>
78 #include <wtf/AutodrainedPool.h>
79 #include <wtf/CompletionHandler.h>
80 #include <wtf/CryptographicallyRandomNumber.h>
81 #include <wtf/MainThread.h>
82 #include <wtf/ProcessPrivilege.h>
83 #include <wtf/RefCounted.h>
84 #include <wtf/RunLoop.h>
85 #include <wtf/SetForScope.h>
86 #include <wtf/UUID.h>
87 #include <wtf/UniqueArray.h>
88 #include <wtf/text/CString.h>
89 #include <wtf/text/StringConcatenateNumbers.h>
90
91 #if PLATFORM(COCOA)
92 #include <WebKit/WKContextPrivateMac.h>
93 #include <WebKit/WKPagePrivateMac.h>
94 #endif
95
96 #if PLATFORM(WIN)
97 #include <direct.h>
98 #define getcwd _getcwd
99 #define PATH_MAX _MAX_PATH
100 #else
101 #include <unistd.h>
102 #endif
103
104 namespace WTR {
105
106 #if OS(WINDOWS)
107 static constexpr auto pathSeparator = '\\';
108 #else
109 static constexpr auto pathSeparator = '/';
110 #endif
111
112 const unsigned TestController::viewWidth = 800;
113 const unsigned TestController::viewHeight = 600;
114
115 const unsigned TestController::w3cSVGViewWidth = 480;
116 const unsigned TestController::w3cSVGViewHeight = 360;
117
118 const WTF::Seconds TestController::defaultShortTimeout = 5_s;
119 const WTF::Seconds TestController::noTimeout = -1_s;
120
121 static WKURLRef blankURL()
122 {
123     static WKURLRef staticBlankURL = WKURLCreateWithUTF8CString("about:blank");
124     return staticBlankURL;
125 }
126
127 static WKDataRef copyWebCryptoMasterKey(WKPageRef, const void*)
128 {
129     // Any 128 bit key would do, all we need for testing is to implement the callback.
130     return WKDataCreate((const uint8_t*)"\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f", 16);
131 }
132
133 static WKStringRef copySignedPublicKeyAndChallengeString(WKPageRef, const void*)
134 {
135     // Any fake response would do, all we need for testing is to implement the callback.
136     return WKStringCreateWithUTF8CString("MIHFMHEwXDANBgkqhkiG9w0BAQEFAANLADBIAkEAnX0TILJrOMUue%2BPtwBRE6XfV%0AWtKQbsshxk5ZhcUwcwyvcnIq9b82QhJdoACdD34rqfCAIND46fXKQUnb0mvKzQID%0AAQABFhFNb3ppbGxhSXNNeUZyaWVuZDANBgkqhkiG9w0BAQQFAANBAAKv2Eex2n%2FS%0Ar%2F7iJNroWlSzSMtTiQTEB%2BADWHGj9u1xrUrOilq%2Fo2cuQxIfZcNZkYAkWP4DubqW%0Ai0%2F%2FrgBvmco%3D");
137 }
138
139 AsyncTask* AsyncTask::m_currentTask;
140
141 bool AsyncTask::run()
142 {
143     m_currentTask = this;
144     m_task();
145     TestController::singleton().runUntil(m_taskDone, m_timeout);
146     m_currentTask = nullptr;
147     return m_taskDone;
148 }
149
150 AsyncTask* AsyncTask::currentTask()
151 {
152     return m_currentTask;
153 }
154
155 static TestController* controller;
156
157 TestController& TestController::singleton()
158 {
159     ASSERT(controller);
160     return *controller;
161 }
162
163 TestController::TestController(int argc, const char* argv[])
164 {
165     initialize(argc, argv);
166     controller = this;
167     run();
168     controller = nullptr;
169 }
170
171 TestController::~TestController()
172 {
173     // The context will be null if WebKitTestRunner was in server mode, but ran no tests.
174     if (m_context)
175         WKIconDatabaseClose(WKContextGetIconDatabase(m_context.get()));
176
177     platformDestroy();
178 }
179
180 static WKRect getWindowFrame(WKPageRef page, const void* clientInfo)
181 {
182     PlatformWebView* view = static_cast<PlatformWebView*>(const_cast<void*>(clientInfo));
183     return view->windowFrame();
184 }
185
186 static void setWindowFrame(WKPageRef page, WKRect frame, const void* clientInfo)
187 {
188     PlatformWebView* view = static_cast<PlatformWebView*>(const_cast<void*>(clientInfo));
189     view->setWindowFrame(frame);
190 }
191
192 static bool runBeforeUnloadConfirmPanel(WKPageRef page, WKStringRef message, WKFrameRef frame, const void*)
193 {
194     printf("CONFIRM NAVIGATION: %s\n", toSTD(message).c_str());
195     return TestController::singleton().beforeUnloadReturnValue();
196 }
197
198 static void runOpenPanel(WKPageRef page, WKFrameRef frame, WKOpenPanelParametersRef parameters, WKOpenPanelResultListenerRef resultListenerRef, const void*)
199 {
200     printf("OPEN FILE PANEL\n");
201     if (WKOpenPanelParametersGetAllowsDirectories(parameters))
202         printf("-> DIRECTORIES ARE ALLOWED\n");
203     WKArrayRef fileURLs = TestController::singleton().openPanelFileURLs();
204     if (!fileURLs || !WKArrayGetSize(fileURLs)) {
205         WKOpenPanelResultListenerCancel(resultListenerRef);
206         return;
207     }
208     
209     WKTypeRef firstItem = WKArrayGetItemAtIndex(fileURLs, 0);
210     
211 #if PLATFORM(IOS_FAMILY)
212     auto displayString = adoptWK(WKURLCopyLastPathComponent(static_cast<WKURLRef>(firstItem)));
213     WKDataRef mediaIcon = TestController::singleton().openPanelFileURLsMediaIcon();
214     
215     if (mediaIcon) {
216         if (WKOpenPanelParametersGetAllowsMultipleFiles(parameters)) {
217             WKOpenPanelResultListenerChooseMediaFiles(resultListenerRef, fileURLs, displayString.get(), mediaIcon);
218             return;
219         }
220         
221         WKOpenPanelResultListenerChooseMediaFiles(resultListenerRef, adoptWK(WKArrayCreate(&firstItem, 1)).get(), displayString.get(), mediaIcon);
222         return;
223     }
224 #endif
225
226     if (WKOpenPanelParametersGetAllowsMultipleFiles(parameters)) {
227         WKOpenPanelResultListenerChooseFiles(resultListenerRef, fileURLs);
228         return;
229     }
230
231     WKOpenPanelResultListenerChooseFiles(resultListenerRef, adoptWK(WKArrayCreate(&firstItem, 1)).get());
232 }
233
234 void TestController::runModal(WKPageRef page, const void* clientInfo)
235 {
236     PlatformWebView* view = static_cast<PlatformWebView*>(const_cast<void*>(clientInfo));
237     TestController::singleton().mainWebView()->setWindowIsKey(false);
238     runModal(view);
239     TestController::singleton().mainWebView()->setWindowIsKey(true);
240 }
241
242 static void closeOtherPage(WKPageRef page, const void* clientInfo)
243 {
244     WKPageClose(page);
245     PlatformWebView* view = static_cast<PlatformWebView*>(const_cast<void*>(clientInfo));
246     delete view;
247 }
248
249 static void focus(WKPageRef page, const void* clientInfo)
250 {
251     PlatformWebView* view = static_cast<PlatformWebView*>(const_cast<void*>(clientInfo));
252     view->focus();
253     view->setWindowIsKey(true);
254 }
255
256 static void unfocus(WKPageRef page, const void* clientInfo)
257 {
258     PlatformWebView* view = static_cast<PlatformWebView*>(const_cast<void*>(clientInfo));
259     view->setWindowIsKey(false);
260 }
261
262 static void decidePolicyForGeolocationPermissionRequest(WKPageRef, WKFrameRef, WKSecurityOriginRef, WKGeolocationPermissionRequestRef permissionRequest, const void* clientInfo)
263 {
264     TestController::singleton().handleGeolocationPermissionRequest(permissionRequest);
265 }
266
267 static void decidePolicyForUserMediaPermissionRequest(WKPageRef, WKFrameRef frame, WKSecurityOriginRef userMediaDocumentOrigin, WKSecurityOriginRef topLevelDocumentOrigin, WKUserMediaPermissionRequestRef permissionRequest, const void* clientInfo)
268 {
269     TestController::singleton().handleUserMediaPermissionRequest(frame, userMediaDocumentOrigin, topLevelDocumentOrigin, permissionRequest);
270 }
271
272 static void runJavaScriptAlert(WKPageRef page, WKStringRef alertText, WKFrameRef frame, WKSecurityOriginRef securityOrigin, WKPageRunJavaScriptAlertResultListenerRef listener, const void *clientInfo)
273 {
274     TestController::singleton().handleJavaScriptAlert(listener);
275 }
276
277 static void checkUserMediaPermissionForOrigin(WKPageRef, WKFrameRef frame, WKSecurityOriginRef userMediaDocumentOrigin, WKSecurityOriginRef topLevelDocumentOrigin, WKUserMediaPermissionCheckRef checkRequest, const void*)
278 {
279     TestController::singleton().handleCheckOfUserMediaPermissionForOrigin(frame, userMediaDocumentOrigin, topLevelDocumentOrigin, checkRequest);
280 }
281
282 static void requestPointerLock(WKPageRef page, const void*)
283 {
284     WKPageDidAllowPointerLock(page);
285 }
286
287 static void printFrame(WKPageRef page, WKFrameRef frame, const void*)
288 {
289     WKPageBeginPrinting(page, frame, WKPrintInfo { 1, 21, 29.7f });
290     WKPageEndPrinting(page);
291 }
292
293 static bool shouldAllowDeviceOrientationAndMotionAccess(WKPageRef, WKSecurityOriginRef origin, const void*)
294 {
295     return TestController::singleton().handleDeviceOrientationAndMotionAccessRequest(origin);
296 }
297
298 // A placeholder to tell WebKit the client is WebKitTestRunner.
299 static void runWebAuthenticationPanel()
300 {
301 }
302
303 WKPageRef TestController::createOtherPage(WKPageRef, WKPageConfigurationRef configuration, WKNavigationActionRef navigationAction, WKWindowFeaturesRef windowFeatures, const void *clientInfo)
304 {
305     PlatformWebView* parentView = static_cast<PlatformWebView*>(const_cast<void*>(clientInfo));
306     return TestController::singleton().createOtherPage(parentView, configuration, navigationAction, windowFeatures);
307 }
308
309 WKPageRef TestController::createOtherPage(PlatformWebView* parentView, WKPageConfigurationRef configuration, WKNavigationActionRef navigationAction, WKWindowFeaturesRef windowFeatures)
310 {
311     m_currentInvocation->willCreateNewPage();
312
313     // The test needs to call testRunner.setCanOpenWindows() to open new windows.
314     if (!m_currentInvocation->canOpenWindows())
315         return nullptr;
316
317     m_createdOtherPage = true;
318
319     PlatformWebView* view = platformCreateOtherPage(parentView, configuration, parentView->options());
320     WKPageRef newPage = view->page();
321
322     view->resizeTo(800, 600);
323
324     WKPageUIClientV8 otherPageUIClient = {
325         { 8, view },
326         0, // createNewPage_deprecatedForUseWithV0
327         0, // showPage
328         closeOtherPage,
329         0, // takeFocus
330         focus,
331         unfocus,
332         0, // runJavaScriptAlert_deprecatedForUseWithV0
333         0, // runJavaScriptAlert_deprecatedForUseWithV0
334         0, // runJavaScriptAlert_deprecatedForUseWithV0
335         0, // setStatusText
336         0, // mouseDidMoveOverElement_deprecatedForUseWithV0
337         0, // missingPluginButtonClicked
338         0, // didNotHandleKeyEvent
339         0, // didNotHandleWheelEvent
340         0, // toolbarsAreVisible
341         0, // setToolbarsAreVisible
342         0, // menuBarIsVisible
343         0, // setMenuBarIsVisible
344         0, // statusBarIsVisible
345         0, // setStatusBarIsVisible
346         0, // isResizable
347         0, // setIsResizable
348         getWindowFrame,
349         setWindowFrame,
350         runBeforeUnloadConfirmPanel,
351         0, // didDraw
352         0, // pageDidScroll
353         0, // exceededDatabaseQuota
354         runOpenPanel,
355         decidePolicyForGeolocationPermissionRequest,
356         0, // headerHeight
357         0, // footerHeight
358         0, // drawHeader
359         0, // drawFooter
360         printFrame,
361         runModal,
362         0, // didCompleteRubberBandForMainFrame
363         0, // saveDataToFileInDownloadsFolder
364         0, // shouldInterruptJavaScript
365         0, // createNewPage_deprecatedForUseWithV1
366         0, // mouseDidMoveOverElement
367         0, // decidePolicyForNotificationPermissionRequest
368         0, // unavailablePluginButtonClicked_deprecatedForUseWithV1
369         0, // showColorPicker
370         0, // hideColorPicker
371         0, // unavailablePluginButtonClicked
372         0, // pinnedStateDidChange
373         0, // didBeginTrackingPotentialLongMousePress
374         0, // didRecognizeLongMousePress
375         0, // didCancelTrackingPotentialLongMousePress
376         0, // isPlayingAudioDidChange
377         decidePolicyForUserMediaPermissionRequest,
378         0, // didClickAutofillButton
379         0, // runJavaScriptAlert
380         0, // runJavaScriptConfirm
381         0, // runJavaScriptPrompt
382         0, // mediaSessionMetadataDidChange
383         createOtherPage,
384         runJavaScriptAlert,
385         0, // runJavaScriptConfirm
386         0, // runJavaScriptPrompt
387         checkUserMediaPermissionForOrigin,
388         0, // runBeforeUnloadConfirmPanel
389         0, // fullscreenMayReturnToInline
390         requestPointerLock,
391         0,
392     };
393     WKPageSetPageUIClient(newPage, &otherPageUIClient.base);
394     
395     WKPageNavigationClientV3 pageNavigationClient = {
396         { 3, &TestController::singleton() },
397         decidePolicyForNavigationAction,
398         decidePolicyForNavigationResponse,
399         decidePolicyForPluginLoad,
400         0, // didStartProvisionalNavigation
401         didReceiveServerRedirectForProvisionalNavigation,
402         0, // didFailProvisionalNavigation
403         0, // didCommitNavigation
404         0, // didFinishNavigation
405         0, // didFailNavigation
406         0, // didFailProvisionalLoadInSubframe
407         0, // didFinishDocumentLoad
408         0, // didSameDocumentNavigation
409         0, // renderingProgressDidChange
410         canAuthenticateAgainstProtectionSpace,
411         didReceiveAuthenticationChallenge,
412         processDidCrash,
413         copyWebCryptoMasterKey,
414         didBeginNavigationGesture,
415         willEndNavigationGesture,
416         didEndNavigationGesture,
417         didRemoveNavigationGestureSnapshot,
418         0, // webProcessDidTerminate
419         0, // contentRuleListNotification
420         copySignedPublicKeyAndChallengeString
421     };
422     WKPageSetPageNavigationClient(newPage, &pageNavigationClient.base);
423
424     view->didInitializeClients();
425
426     TestController::singleton().updateWindowScaleForTest(view, *TestController::singleton().m_currentInvocation);
427
428     WKRetain(newPage);
429     return newPage;
430 }
431
432 const char* TestController::libraryPathForTesting()
433 {
434     // FIXME: This may not be sufficient to prevent interactions/crashes
435     // when running more than one copy of DumpRenderTree.
436     // See https://bugs.webkit.org/show_bug.cgi?id=10906
437     char* dumpRenderTreeTemp = getenv("DUMPRENDERTREE_TEMP");
438     if (dumpRenderTreeTemp)
439         return dumpRenderTreeTemp;
440     return platformLibraryPathForTesting();
441 }
442
443 void TestController::initialize(int argc, const char* argv[])
444 {
445     AutodrainedPool pool;
446
447     JSC::initializeThreading();
448     RunLoop::initializeMainRunLoop();
449     WTF::setProcessPrivileges(allPrivileges());
450
451     platformInitialize();
452
453     Options options;
454     OptionsHandler optionsHandler(options);
455
456     if (argc < 2) {
457         optionsHandler.printHelp();
458         exit(1);
459     }
460     if (!optionsHandler.parse(argc, argv))
461         exit(1);
462
463     m_useWaitToDumpWatchdogTimer = options.useWaitToDumpWatchdogTimer;
464     m_forceNoTimeout = options.forceNoTimeout;
465     m_verbose = options.verbose;
466     m_gcBetweenTests = options.gcBetweenTests;
467     m_shouldDumpPixelsForAllTests = options.shouldDumpPixelsForAllTests;
468     m_forceComplexText = options.forceComplexText;
469     m_shouldUseAcceleratedDrawing = options.shouldUseAcceleratedDrawing;
470     m_shouldUseRemoteLayerTree = options.shouldUseRemoteLayerTree;
471     m_paths = options.paths;
472     m_allowedHosts = options.allowedHosts;
473     m_shouldShowWebView = options.shouldShowWebView;
474     m_shouldShowTouches = options.shouldShowTouches;
475     m_checkForWorldLeaks = options.checkForWorldLeaks;
476     m_allowAnyHTTPSCertificateForAllowedHosts = options.allowAnyHTTPSCertificateForAllowedHosts;
477     m_internalFeatures = options.internalFeatures;
478     m_experimentalFeatures = options.experimentalFeatures;
479 #if ENABLE(ACCESSIBILITY_ISOLATED_TREE)
480     m_accessibilityIsolatedTreeMode = options.accessibilityIsolatedTreeMode;
481 #endif
482     m_usingServerMode = (m_paths.size() == 1 && m_paths[0] == "-");
483     if (m_usingServerMode)
484         m_printSeparators = true;
485     else
486         m_printSeparators = m_paths.size() > 1;
487
488     initializeInjectedBundlePath();
489     initializeTestPluginDirectory();
490
491 #if PLATFORM(MAC)
492     WebCoreTestSupport::installMockGamepadProvider();
493 #endif
494
495     WKRetainPtr<WKStringRef> pageGroupIdentifier = adoptWK(WKStringCreateWithUTF8CString("WebKitTestRunnerPageGroup"));
496     m_pageGroup.adopt(WKPageGroupCreateWithIdentifier(pageGroupIdentifier.get()));
497
498     m_eventSenderProxy = makeUnique<EventSenderProxy>(this);
499 }
500
501 WKRetainPtr<WKContextConfigurationRef> TestController::generateContextConfiguration(const TestOptions::ContextOptions& options) const
502 {
503     auto configuration = adoptWK(WKContextConfigurationCreate());
504     WKContextConfigurationSetInjectedBundlePath(configuration.get(), injectedBundlePath());
505     WKContextConfigurationSetFullySynchronousModeIsAllowedForTesting(configuration.get(), true);
506     WKContextConfigurationSetIgnoreSynchronousMessagingTimeoutsForTesting(configuration.get(), options.ignoreSynchronousMessagingTimeouts);
507
508     WKRetainPtr<WKMutableArrayRef> overrideLanguages = adoptWK(WKMutableArrayCreate());
509     for (auto& language : options.overrideLanguages)
510         WKArrayAppendItem(overrideLanguages.get(), adoptWK(WKStringCreateWithUTF8CString(language.utf8().data())).get());
511     WKContextConfigurationSetOverrideLanguages(configuration.get(), overrideLanguages.get());
512
513     if (options.shouldEnableProcessSwapOnNavigation()) {
514         WKContextConfigurationSetProcessSwapsOnNavigation(configuration.get(), true);
515         if (options.enableProcessSwapOnWindowOpen)
516             WKContextConfigurationSetProcessSwapsOnWindowOpenWithOpener(configuration.get(), true);
517     }
518
519     WKContextConfigurationSetShouldConfigureJSCForTesting(configuration.get(), true);
520
521     return configuration;
522 }
523
524 WKWebsiteDataStoreRef TestController::defaultWebsiteDataStore()
525 {
526     static WKWebsiteDataStoreRef dataStore = nullptr;
527     if (!dataStore) {
528         auto configuration = adoptWK(WKWebsiteDataStoreConfigurationCreate());
529         if (const char* dumpRenderTreeTemp = libraryPathForTesting()) {
530             String temporaryFolder = String::fromUTF8(dumpRenderTreeTemp);
531
532             WKWebsiteDataStoreConfigurationSetApplicationCacheDirectory(configuration.get(), toWK(temporaryFolder + pathSeparator + "ApplicationCache").get());
533             WKWebsiteDataStoreConfigurationSetNetworkCacheDirectory(configuration.get(), toWK(temporaryFolder + pathSeparator + "Cache").get());
534             WKWebsiteDataStoreConfigurationSetCacheStorageDirectory(configuration.get(), toWK(temporaryFolder + pathSeparator + "CacheStorage").get());
535             WKWebsiteDataStoreConfigurationSetIndexedDBDatabaseDirectory(configuration.get(), toWK(temporaryFolder + pathSeparator + "Databases" + pathSeparator + "IndexedDB").get());
536             WKWebsiteDataStoreConfigurationSetLocalStorageDirectory(configuration.get(), toWK(temporaryFolder + pathSeparator + "LocalStorage").get());
537             WKWebsiteDataStoreConfigurationSetWebSQLDatabaseDirectory(configuration.get(), toWK(temporaryFolder + pathSeparator + "Databases" + pathSeparator + "WebSQL").get());
538             WKWebsiteDataStoreConfigurationSetMediaKeysStorageDirectory(configuration.get(), toWK(temporaryFolder + pathSeparator + "MediaKeys").get());
539             WKWebsiteDataStoreConfigurationSetResourceLoadStatisticsDirectory(configuration.get(), toWK(temporaryFolder + pathSeparator + "ResourceLoadStatistics").get());
540             WKWebsiteDataStoreConfigurationSetServiceWorkerRegistrationDirectory(configuration.get(), toWK(temporaryFolder + pathSeparator + "ServiceWorkers").get());
541             WKWebsiteDataStoreConfigurationSetPerOriginStorageQuota(configuration.get(), 400 * 1024);
542             WKWebsiteDataStoreConfigurationSetNetworkCacheSpeculativeValidationEnabled(configuration.get(), true);
543             WKWebsiteDataStoreConfigurationSetStaleWhileRevalidateEnabled(configuration.get(), true);
544             WKWebsiteDataStoreConfigurationSetTestingSessionEnabled(configuration.get(), true);
545         }
546         dataStore = WKWebsiteDataStoreCreateWithConfiguration(configuration.get());
547     }
548     return dataStore;
549 }
550
551 WKWebsiteDataStoreRef TestController::websiteDataStore()
552 {
553     return WKPageConfigurationGetWebsiteDataStore(adoptWK(WKPageCopyPageConfiguration(m_mainWebView->page())).get());
554 }
555
556 WKRetainPtr<WKPageConfigurationRef> TestController::generatePageConfiguration(const TestOptions& options)
557 {
558     if (!m_context || !m_contextOptions->hasSameInitializationOptions(options.contextOptions)) {
559         auto contextConfiguration = generateContextConfiguration(options.contextOptions);
560         m_context = platformAdjustContext(adoptWK(WKContextCreateWithConfiguration(contextConfiguration.get())).get(), contextConfiguration.get());
561         m_contextOptions = options.contextOptions;
562
563         m_geolocationProvider = makeUnique<GeolocationProviderMock>(m_context.get());
564
565         if (const char* dumpRenderTreeTemp = libraryPathForTesting()) {
566             String temporaryFolder = String::fromUTF8(dumpRenderTreeTemp);
567
568             // FIXME: This should be migrated to WKContextConfigurationRef.
569             // Disable icon database to avoid fetching <http://127.0.0.1:8000/favicon.ico> and making tests flaky.
570             // Invividual tests can enable it using testRunner.setIconDatabaseEnabled, although it's not currently supported in WebKitTestRunner.
571             WKContextSetIconDatabasePath(m_context.get(), toWK(emptyString()).get());
572         }
573
574         WKContextUseTestingNetworkSession(m_context.get());
575         WKContextSetCacheModel(m_context.get(), kWKCacheModelDocumentBrowser);
576
577         platformInitializeContext();
578     }
579
580     WKContextInjectedBundleClientV2 injectedBundleClient = {
581         { 2, this },
582         didReceiveMessageFromInjectedBundle,
583         nullptr,
584         getInjectedBundleInitializationUserData,
585         didReceiveSynchronousMessageFromInjectedBundleWithListener,
586     };
587     WKContextSetInjectedBundleClient(m_context.get(), &injectedBundleClient.base);
588
589     WKContextClientV3 contextClient = {
590         { 3, this },
591         0, // plugInAutoStartOriginHashesChanged
592         networkProcessDidCrash,
593         0, // plugInInformationBecameAvailable
594         0, // copyWebCryptoMasterKey
595         serviceWorkerProcessDidCrash,
596         gpuProcessDidCrash
597     };
598     WKContextSetClient(m_context.get(), &contextClient.base);
599
600     WKContextHistoryClientV0 historyClient = {
601         { 0, this },
602         didNavigateWithNavigationData,
603         didPerformClientRedirect,
604         didPerformServerRedirect,
605         didUpdateHistoryTitle,
606         0, // populateVisitedLinks
607     };
608     WKContextSetHistoryClient(m_context.get(), &historyClient.base);
609
610     WKNotificationManagerRef notificationManager = WKContextGetNotificationManager(m_context.get());
611     WKNotificationProviderV0 notificationKit = m_webNotificationProvider.provider();
612     WKNotificationManagerSetProvider(notificationManager, &notificationKit.base);
613
614     if (testPluginDirectory())
615         WKContextSetAdditionalPluginsDirectory(m_context.get(), testPluginDirectory());
616
617     if (m_forceComplexText)
618         WKContextSetAlwaysUsesComplexTextCodePath(m_context.get(), true);
619
620     auto pageConfiguration = adoptWK(WKPageConfigurationCreate());
621     WKPageConfigurationSetContext(pageConfiguration.get(), m_context.get());
622     WKPageConfigurationSetPageGroup(pageConfiguration.get(), m_pageGroup.get());
623     
624     if (options.useEphemeralSession) {
625         auto ephemeralDataStore = adoptWK(WKWebsiteDataStoreCreateNonPersistentDataStore());
626         WKWebsiteDataStoreSetResourceLoadStatisticsEnabled(ephemeralDataStore.get(), true);
627         WKPageConfigurationSetWebsiteDataStore(pageConfiguration.get(), ephemeralDataStore.get());
628     }
629
630     m_userContentController = adoptWK(WKUserContentControllerCreate());
631     WKPageConfigurationSetUserContentController(pageConfiguration.get(), userContentController());
632     return pageConfiguration;
633 }
634
635 void TestController::createWebViewWithOptions(const TestOptions& options)
636 {
637 #if PLATFORM(COCOA)
638     if (m_hasSetApplicationBundleIdentifier) {
639         // Exit if the application bundle identifier has already been set, since it can only be set once.
640         exit(1);
641     }
642     if (!options.applicationBundleIdentifier.isEmpty()) {
643         clearApplicationBundleIdentifierTestingOverride();
644         setApplicationBundleIdentifier(options.applicationBundleIdentifier);
645         m_hasSetApplicationBundleIdentifier = true;
646     }
647 #endif
648
649     auto configuration = generatePageConfiguration(options);
650
651     // Some preferences (notably mock scroll bars setting) currently cannot be re-applied to an existing view, so we need to set them now.
652     // FIXME: Migrate these preferences to WKContextConfigurationRef.
653     resetPreferencesToConsistentValues(options);
654
655     platformCreateWebView(configuration.get(), options);
656     WKPageUIClientV14 pageUIClient = {
657         { 14, m_mainWebView.get() },
658         0, // createNewPage_deprecatedForUseWithV0
659         0, // showPage
660         0, // close
661         0, // takeFocus
662         focus,
663         unfocus,
664         0, // runJavaScriptAlert_deprecatedForUseWithV0
665         0, // runJavaScriptAlert_deprecatedForUseWithV0
666         0, // runJavaScriptAlert_deprecatedForUseWithV0
667         0, // setStatusText
668         0, // mouseDidMoveOverElement_deprecatedForUseWithV0
669         0, // missingPluginButtonClicked
670         0, // didNotHandleKeyEvent
671         0, // didNotHandleWheelEvent
672         0, // toolbarsAreVisible
673         0, // setToolbarsAreVisible
674         0, // menuBarIsVisible
675         0, // setMenuBarIsVisible
676         0, // statusBarIsVisible
677         0, // setStatusBarIsVisible
678         0, // isResizable
679         0, // setIsResizable
680         getWindowFrame,
681         setWindowFrame,
682         runBeforeUnloadConfirmPanel,
683         0, // didDraw
684         0, // pageDidScroll
685         0, // exceededDatabaseQuota,
686         options.shouldHandleRunOpenPanel ? runOpenPanel : 0,
687         decidePolicyForGeolocationPermissionRequest,
688         0, // headerHeight
689         0, // footerHeight
690         0, // drawHeader
691         0, // drawFooter
692         printFrame,
693         runModal,
694         0, // didCompleteRubberBandForMainFrame
695         0, // saveDataToFileInDownloadsFolder
696         0, // shouldInterruptJavaScript
697         0, // createNewPage_deprecatedForUseWithV1
698         0, // mouseDidMoveOverElement
699         decidePolicyForNotificationPermissionRequest, // decidePolicyForNotificationPermissionRequest
700         0, // unavailablePluginButtonClicked_deprecatedForUseWithV1
701         0, // showColorPicker
702         0, // hideColorPicker
703         unavailablePluginButtonClicked,
704         0, // pinnedStateDidChange
705         0, // didBeginTrackingPotentialLongMousePress
706         0, // didRecognizeLongMousePress
707         0, // didCancelTrackingPotentialLongMousePress
708         0, // isPlayingAudioDidChange
709         decidePolicyForUserMediaPermissionRequest,
710         0, // didClickAutofillButton
711         0, // runJavaScriptAlert
712         0, // runJavaScriptConfirm
713         0, // runJavaScriptPrompt
714         0, // mediaSessionMetadataDidChange
715         createOtherPage,
716         runJavaScriptAlert,
717         0, // runJavaScriptConfirm
718         0, // runJavaScriptPrompt
719         checkUserMediaPermissionForOrigin,
720         0, // runBeforeUnloadConfirmPanel
721         0, // fullscreenMayReturnToInline
722         requestPointerLock,
723         0, // didLosePointerLock
724         0, // handleAutoplayEvent
725         0, // hasVideoInPictureInPictureDidChange
726         0, // didExceedBackgroundResourceLimitWhileInForeground
727         0, // didResignInputElementStrongPasswordAppearance
728         0, // requestStorageAccessConfirm
729         shouldAllowDeviceOrientationAndMotionAccess,
730         runWebAuthenticationPanel
731     };
732     WKPageSetPageUIClient(m_mainWebView->page(), &pageUIClient.base);
733
734     WKPageNavigationClientV3 pageNavigationClient = {
735         { 3, this },
736         decidePolicyForNavigationAction,
737         decidePolicyForNavigationResponse,
738         decidePolicyForPluginLoad,
739         0, // didStartProvisionalNavigation
740         didReceiveServerRedirectForProvisionalNavigation,
741         0, // didFailProvisionalNavigation
742         didCommitNavigation,
743         didFinishNavigation,
744         0, // didFailNavigation
745         0, // didFailProvisionalLoadInSubframe
746         0, // didFinishDocumentLoad
747         0, // didSameDocumentNavigation
748         0, // renderingProgressDidChange
749         canAuthenticateAgainstProtectionSpace,
750         didReceiveAuthenticationChallenge,
751         processDidCrash,
752         copyWebCryptoMasterKey,
753         didBeginNavigationGesture,
754         willEndNavigationGesture,
755         didEndNavigationGesture,
756         didRemoveNavigationGestureSnapshot,
757         0, // webProcessDidTerminate
758         0, // contentRuleListNotification
759         copySignedPublicKeyAndChallengeString
760     };
761     WKPageSetPageNavigationClient(m_mainWebView->page(), &pageNavigationClient.base);
762
763     WKContextDownloadClientV1 downloadClient = {
764         { 1, this },
765         downloadDidStart,
766         0, // didReceiveAuthenticationChallenge
767         0, // didReceiveResponse
768         0, // didReceiveData
769         0, // shouldDecodeSourceDataOfMIMEType
770         decideDestinationWithSuggestedFilename,
771         0, // didCreateDestination
772         downloadDidFinish,
773         downloadDidFail,
774         downloadDidCancel,
775         0, // processDidCrash;
776         downloadDidReceiveServerRedirectToURL
777     };
778     WKContextSetDownloadClient(context(), &downloadClient.base);
779     
780     // this should just be done on the page?
781     WKPageInjectedBundleClientV1 injectedBundleClient = {
782         { 1, this },
783         didReceivePageMessageFromInjectedBundle,
784         nullptr,
785         didReceiveSynchronousPageMessageFromInjectedBundleWithListener,
786     };
787     WKPageSetPageInjectedBundleClient(m_mainWebView->page(), &injectedBundleClient.base);
788
789     m_mainWebView->didInitializeClients();
790
791     // Generally, the tests should default to running at 1x. updateWindowScaleForTest() will adjust the scale to
792     // something else for specific tests that need to run at a different window scale.
793     m_mainWebView->changeWindowScaleIfNeeded(1);
794     
795     if (!options.applicationBundleIdentifier.isEmpty())
796         reinitializeAppBoundDomains();
797 }
798
799 void TestController::ensureViewSupportsOptionsForTest(const TestInvocation& test)
800 {
801     auto options = test.options();
802
803     if (m_mainWebView) {
804         // Having created another page (via window.open()) prevents process swapping on navigation and it may therefore
805         // cause flakiness to reuse the view.
806         if (!m_createdOtherPage && m_mainWebView->viewSupportsOptions(options))
807             return;
808
809         willDestroyWebView();
810
811         WKPageSetPageUIClient(m_mainWebView->page(), nullptr);
812         WKPageSetPageNavigationClient(m_mainWebView->page(), nullptr);
813         WKPageClose(m_mainWebView->page());
814
815         m_mainWebView = nullptr;
816         m_createdOtherPage = false;
817     }
818
819
820     createWebViewWithOptions(options);
821
822     if (!resetStateToConsistentValues(options, ResetStage::BeforeTest))
823         TestInvocation::dumpWebProcessUnresponsiveness("<unknown> - TestController::run - Failed to reset state to consistent values\n");
824 }
825
826 void TestController::resetPreferencesToConsistentValues(const TestOptions& options)
827 {
828     // Reset preferences
829     WKPreferencesRef preferences = platformPreferences();
830     WKPreferencesResetTestRunnerOverrides(preferences);
831
832     WKPreferencesEnableAllExperimentalFeatures(preferences);
833     for (const auto& experimentalFeature : options.experimentalFeatures)
834         WKPreferencesSetExperimentalFeatureForKey(preferences, experimentalFeature.value, toWK(experimentalFeature.key).get());
835
836     WKPreferencesResetAllInternalDebugFeatures(preferences);
837
838     // Set internal features that have different default values for testing.
839     static WKStringRef asyncOverflowScrollingFeature = WKStringCreateWithUTF8CString("AsyncOverflowScrollingEnabled");
840     WKPreferencesSetInternalDebugFeatureForKey(preferences, false, asyncOverflowScrollingFeature);
841
842     static WKStringRef asyncFrameScrollingFeature = WKStringCreateWithUTF8CString("AsyncFrameScrollingEnabled");
843     WKPreferencesSetInternalDebugFeatureForKey(preferences, false, asyncFrameScrollingFeature);
844
845     for (const auto& internalDebugFeature : options.internalDebugFeatures)
846         WKPreferencesSetInternalDebugFeatureForKey(preferences, internalDebugFeature.value, toWK(internalDebugFeature.key).get());
847
848 #if PLATFORM(COCOA)
849     WKPreferencesSetCaptureVideoInUIProcessEnabled(preferences, options.enableCaptureVideoInUIProcess);
850     WKPreferencesSetCaptureVideoInGPUProcessEnabled(preferences, options.enableCaptureVideoInGPUProcess);
851     WKPreferencesSetCaptureAudioInGPUProcessEnabled(preferences, options.enableCaptureAudioInGPUProcess);
852 #endif
853     WKPreferencesSetProcessSwapOnNavigationEnabled(preferences, options.contextOptions.shouldEnableProcessSwapOnNavigation());
854     WKPreferencesSetPageVisibilityBasedProcessSuppressionEnabled(preferences, options.enableAppNap);
855     WKPreferencesSetOfflineWebApplicationCacheEnabled(preferences, true);
856     WKPreferencesSetSubpixelAntialiasedLayerTextEnabled(preferences, false);
857     WKPreferencesSetXSSAuditorEnabled(preferences, false);
858     WKPreferencesSetWebAudioEnabled(preferences, true);
859     WKPreferencesSetMediaDevicesEnabled(preferences, true);
860     WKPreferencesSetWebRTCMDNSICECandidatesEnabled(preferences, false);
861     WKPreferencesSetDeveloperExtrasEnabled(preferences, true);
862     WKPreferencesSetJavaScriptRuntimeFlags(preferences, kWKJavaScriptRuntimeFlagsAllEnabled);
863     WKPreferencesSetJavaScriptCanOpenWindowsAutomatically(preferences, true);
864     WKPreferencesSetJavaScriptCanAccessClipboard(preferences, true);
865     WKPreferencesSetDOMPasteAllowed(preferences, options.domPasteAllowed);
866     WKPreferencesSetUniversalAccessFromFileURLsAllowed(preferences, true);
867     WKPreferencesSetFileAccessFromFileURLsAllowed(preferences, true);
868     WKPreferencesSetTopNavigationToDataURLsAllowed(preferences, options.allowTopNavigationToDataURLs);
869 #if ENABLE(FULLSCREEN_API)
870     WKPreferencesSetFullScreenEnabled(preferences, true);
871 #endif
872     WKPreferencesSetAsynchronousPluginInitializationEnabled(preferences, false);
873     WKPreferencesSetAsynchronousPluginInitializationEnabledForAllPlugins(preferences, false);
874     WKPreferencesSetArtificialPluginInitializationDelayEnabled(preferences, false);
875     WKPreferencesSetTabToLinksEnabled(preferences, false);
876     WKPreferencesSetInteractiveFormValidationEnabled(preferences, true);
877     WKPreferencesSetDataTransferItemsEnabled(preferences, true);
878     WKPreferencesSetCustomPasteboardDataEnabled(preferences, true);
879     WKPreferencesSetDialogElementEnabled(preferences, true);
880
881     WKPreferencesSetMockScrollbarsEnabled(preferences, options.useMockScrollbars);
882     WKPreferencesSetNeedsSiteSpecificQuirks(preferences, options.needsSiteSpecificQuirks);
883     WKPreferencesSetAttachmentElementEnabled(preferences, options.enableAttachmentElement);
884     WKPreferencesSetMenuItemElementEnabled(preferences, options.enableMenuItemElement);
885     WKPreferencesSetKeygenElementEnabled(preferences, options.enableKeygenElement);
886     WKPreferencesSetModernMediaControlsEnabled(preferences, options.enableModernMediaControls);
887     WKPreferencesSetWebAuthenticationEnabled(preferences, options.enableWebAuthentication);
888     WKPreferencesSetWebAuthenticationLocalAuthenticatorEnabled(preferences, options.enableWebAuthenticationLocalAuthenticator);
889     WKPreferencesSetIsSecureContextAttributeEnabled(preferences, options.enableIsSecureContextAttribute);
890     WKPreferencesSetAllowCrossOriginSubresourcesToAskForCredentials(preferences, options.allowCrossOriginSubresourcesToAskForCredentials);
891     WKPreferencesSetColorFilterEnabled(preferences, options.enableColorFilter);
892     WKPreferencesSetPunchOutWhiteBackgroundsInDarkMode(preferences, options.punchOutWhiteBackgroundsInDarkMode);
893     WKPreferencesSetPageCacheEnabled(preferences, options.enableBackForwardCache);
894     WKPreferencesSetLazyImageLoadingEnabled(preferences, options.enableLazyImageLoading);
895
896     static WKStringRef defaultTextEncoding = WKStringCreateWithUTF8CString("ISO-8859-1");
897     WKPreferencesSetDefaultTextEncodingName(preferences, defaultTextEncoding);
898
899     static WKStringRef standardFontFamily = WKStringCreateWithUTF8CString("Times");
900     static WKStringRef cursiveFontFamily = WKStringCreateWithUTF8CString("Apple Chancery");
901     static WKStringRef fantasyFontFamily = WKStringCreateWithUTF8CString("Papyrus");
902     static WKStringRef fixedFontFamily = WKStringCreateWithUTF8CString("Courier");
903     static WKStringRef pictographFontFamily = WKStringCreateWithUTF8CString("Apple Color Emoji");
904     static WKStringRef sansSerifFontFamily = WKStringCreateWithUTF8CString("Helvetica");
905     static WKStringRef serifFontFamily = WKStringCreateWithUTF8CString("Times");
906
907     WKPreferencesSetMinimumFontSize(preferences, 0);
908     WKPreferencesSetStandardFontFamily(preferences, standardFontFamily);
909     WKPreferencesSetCursiveFontFamily(preferences, cursiveFontFamily);
910     WKPreferencesSetFantasyFontFamily(preferences, fantasyFontFamily);
911     WKPreferencesSetFixedFontFamily(preferences, fixedFontFamily);
912     WKPreferencesSetPictographFontFamily(preferences, pictographFontFamily);
913     WKPreferencesSetSansSerifFontFamily(preferences, sansSerifFontFamily);
914     WKPreferencesSetSerifFontFamily(preferences, serifFontFamily);
915     WKPreferencesSetAsynchronousSpellCheckingEnabled(preferences, false);
916 #if ENABLE(MEDIA_SOURCE)
917     WKPreferencesSetMediaSourceEnabled(preferences, true);
918     WKPreferencesSetSourceBufferChangeTypeEnabled(preferences, true);
919 #endif
920     WKPreferencesSetHighlightAPIEnabled(preferences, true);
921
922     WKPreferencesSetHiddenPageDOMTimerThrottlingEnabled(preferences, false);
923     WKPreferencesSetHiddenPageCSSAnimationSuspensionEnabled(preferences, false);
924
925     WKPreferencesSetAcceleratedDrawingEnabled(preferences, m_shouldUseAcceleratedDrawing || options.useAcceleratedDrawing);
926     // FIXME: We should be testing the default.
927     WKPreferencesSetStorageBlockingPolicy(preferences, kWKAllowAllStorage);
928
929     WKPreferencesSetIsNSURLSessionWebSocketEnabled(preferences, false);
930
931     WKPreferencesSetFetchAPIKeepAliveEnabled(preferences, true);
932     WKPreferencesSetResourceTimingEnabled(preferences, true);
933     WKPreferencesSetUserTimingEnabled(preferences, true);
934     WKPreferencesSetMediaPreloadingEnabled(preferences, true);
935     WKPreferencesSetMediaPlaybackAllowsInline(preferences, true);
936     WKPreferencesSetInlineMediaPlaybackRequiresPlaysInlineAttribute(preferences, false);
937     WKPreferencesSetRemotePlaybackEnabled(preferences, true);
938     WKPreferencesSetBeaconAPIEnabled(preferences, true);
939     WKPreferencesSetDirectoryUploadEnabled(preferences, true);
940
941     WKHTTPCookieStoreDeleteAllCookies(WKWebsiteDataStoreGetHTTPCookieStore(TestController::defaultWebsiteDataStore()), nullptr, nullptr);
942
943     WKPreferencesSetMockCaptureDevicesEnabled(preferences, true);
944     
945     WKPreferencesSetLargeImageAsyncDecodingEnabled(preferences, false);
946
947     WKPreferencesSetInspectorAdditionsEnabled(preferences, options.enableInspectorAdditions);
948
949     WKPreferencesSetStorageAccessAPIEnabled(preferences, true);
950     
951     WKPreferencesSetAccessibilityObjectModelEnabled(preferences, true);
952     WKPreferencesSetCSSOMViewScrollingAPIEnabled(preferences, true);
953     WKPreferencesSetMediaCapabilitiesEnabled(preferences, true);
954
955     WKPreferencesSetRestrictedHTTPResponseAccess(preferences, true);
956
957     WKPreferencesSetServerTimingEnabled(preferences, true);
958
959     WKPreferencesSetWebSQLDisabled(preferences, false);
960
961     WKPreferencesSetMediaPlaybackRequiresUserGesture(preferences, false);
962     WKPreferencesSetVideoPlaybackRequiresUserGesture(preferences, false);
963     WKPreferencesSetAudioPlaybackRequiresUserGesture(preferences, false);
964
965     WKPreferencesSetShouldUseServiceWorkerShortTimeout(preferences, options.contextOptions.useServiceWorkerShortTimeout);
966
967 #if ENABLE(ACCESSIBILITY_ISOLATED_TREE)
968     WKPreferencesSetIsAccessibilityIsolatedTreeEnabled(preferences, accessibilityIsolatedTreeMode());
969 #endif
970     
971     platformResetPreferencesToConsistentValues();
972 }
973
974 bool TestController::resetStateToConsistentValues(const TestOptions& options, ResetStage resetStage)
975 {
976     SetForScope<State> changeState(m_state, Resetting);
977     m_beforeUnloadReturnValue = true;
978
979     // This setting differs between the antique and modern Mac WebKit2 API.
980     // For now, maintain the antique behavior, because some tests depend on it!
981     // FIXME: We should be testing the default.
982     WKPageSetBackgroundExtendsBeyondPage(m_mainWebView->page(), false);
983
984     WKPageSetCustomUserAgent(m_mainWebView->page(), nullptr);
985
986     WKRetainPtr<WKStringRef> messageName = adoptWK(WKStringCreateWithUTF8CString("Reset"));
987     WKRetainPtr<WKMutableDictionaryRef> resetMessageBody = adoptWK(WKMutableDictionaryCreate());
988
989     WKRetainPtr<WKStringRef> shouldGCKey = adoptWK(WKStringCreateWithUTF8CString("ShouldGC"));
990     WKRetainPtr<WKBooleanRef> shouldGCValue = adoptWK(WKBooleanCreate(m_gcBetweenTests));
991     WKDictionarySetItem(resetMessageBody.get(), shouldGCKey.get(), shouldGCValue.get());
992
993     WKRetainPtr<WKStringRef> allowedHostsKey = adoptWK(WKStringCreateWithUTF8CString("AllowedHosts"));
994     WKRetainPtr<WKMutableArrayRef> allowedHostsValue = adoptWK(WKMutableArrayCreate());
995     for (auto& host : m_allowedHosts) {
996         WKRetainPtr<WKStringRef> wkHost = adoptWK(WKStringCreateWithUTF8CString(host.c_str()));
997         WKArrayAppendItem(allowedHostsValue.get(), wkHost.get());
998     }
999     WKDictionarySetItem(resetMessageBody.get(), allowedHostsKey.get(), allowedHostsValue.get());
1000
1001 #if ENABLE(ACCESSIBILITY_ISOLATED_TREE)
1002     WKRetainPtr<WKStringRef> axIsolatedModeKey = adoptWK(WKStringCreateWithUTF8CString("AccessibilityIsolatedTree"));
1003     WKRetainPtr<WKBooleanRef> axIsolatedModeValue = adoptWK(WKBooleanCreate(m_accessibilityIsolatedTreeMode));
1004     WKDictionarySetItem(resetMessageBody.get(), axIsolatedModeKey.get(), axIsolatedModeValue.get());
1005 #endif
1006     
1007     if (options.jscOptions.length()) {
1008         WKRetainPtr<WKStringRef> jscOptionsKey = adoptWK(WKStringCreateWithUTF8CString("JSCOptions"));
1009         WKRetainPtr<WKStringRef> jscOptionsValue = adoptWK(WKStringCreateWithUTF8CString(options.jscOptions.c_str()));
1010         WKDictionarySetItem(resetMessageBody.get(), jscOptionsKey.get(), jscOptionsValue.get());
1011     }
1012
1013 #if PLATFORM(COCOA)
1014     WebCoreTestSupport::setAdditionalSupportedImageTypesForTesting(options.additionalSupportedImageTypes.c_str());
1015 #endif
1016
1017     WKPagePostMessageToInjectedBundle(TestController::singleton().mainWebView()->page(), messageName.get(), resetMessageBody.get());
1018
1019     WKContextSetShouldUseFontSmoothing(TestController::singleton().context(), false);
1020
1021     WKContextSetCacheModel(TestController::singleton().context(), kWKCacheModelDocumentBrowser);
1022
1023     WKContextClearCachedCredentials(TestController::singleton().context());
1024
1025     WKContextResetServiceWorkerFetchTimeoutForTesting(TestController::singleton().context());
1026
1027     WKWebsiteDataStoreClearAllDeviceOrientationPermissions(websiteDataStore());
1028     WKWebsiteDataStoreClearAllDeviceOrientationPermissions(TestController::defaultWebsiteDataStore());
1029
1030     clearIndexedDatabases();
1031     clearLocalStorage();
1032
1033     clearServiceWorkerRegistrations();
1034     clearDOMCaches();
1035
1036     resetQuota();
1037
1038     WKContextSetAllowsAnySSLCertificateForServiceWorkerTesting(platformContext(), true);
1039
1040     WKContextClearCurrentModifierStateForTesting(TestController::singleton().context());
1041
1042     WKContextSetUseSeparateServiceWorkerProcess(TestController::singleton().context(), false);
1043
1044     WKPageSetMockCameraOrientation(m_mainWebView->page(), 0);
1045     resetMockMediaDevices();
1046
1047     // FIXME: This function should also ensure that there is only one page open.
1048
1049     // Reset the EventSender for each test.
1050     m_eventSenderProxy = makeUnique<EventSenderProxy>(this);
1051
1052     // FIXME: Is this needed? Nothing in TestController changes preferences during tests, and if there is
1053     // some other code doing this, it should probably be responsible for cleanup too.
1054     resetPreferencesToConsistentValues(options);
1055
1056 #if PLATFORM(GTK)
1057     WKTextCheckerContinuousSpellCheckingEnabledStateChanged(true);
1058 #endif
1059
1060     // Make sure the view is in the window (a test can unparent it).
1061     m_mainWebView->addToWindow();
1062
1063     // In the case that a test using the chrome input field failed, be sure to clean up for the next test.
1064     m_mainWebView->removeChromeInputField();
1065     m_mainWebView->focus();
1066
1067     // Re-set to the default backing scale factor by setting the custom scale factor to 0.
1068     WKPageSetCustomBackingScaleFactor(m_mainWebView->page(), 0);
1069
1070     WKPageClearWheelEventTestMonitor(m_mainWebView->page());
1071
1072     WKPageSetMuted(m_mainWebView->page(), true);
1073
1074     WKPageClearUserMediaState(m_mainWebView->page());
1075
1076     // Reset notification permissions
1077     m_webNotificationProvider.reset();
1078
1079     // Reset Geolocation permissions.
1080     m_geolocationPermissionRequests.clear();
1081     m_isGeolocationPermissionSet = false;
1082     m_isGeolocationPermissionAllowed = false;
1083
1084     // Reset UserMedia permissions.
1085     m_userMediaPermissionRequests.clear();
1086     m_cachedUserMediaPermissions.clear();
1087     setUserMediaPermission(true);
1088
1089     // Reset Custom Policy Delegate.
1090     setCustomPolicyDelegate(false, false);
1091
1092     // Reset Content Extensions.
1093     resetContentExtensions();
1094
1095     m_shouldDownloadUndisplayableMIMETypes = false;
1096
1097     m_shouldAllowDeviceOrientationAndMotionAccess = false;
1098
1099     m_workQueueManager.clearWorkQueue();
1100
1101     m_rejectsProtectionSpaceAndContinueForAuthenticationChallenges = false;
1102     m_handlesAuthenticationChallenges = false;
1103     m_authenticationUsername = String();
1104     m_authenticationPassword = String();
1105
1106     setBlockAllPlugins(false);
1107     setPluginSupportedMode({ });
1108
1109     m_shouldLogDownloadCallbacks = false;
1110     m_shouldLogHistoryClientCallbacks = false;
1111     m_shouldLogCanAuthenticateAgainstProtectionSpace = false;
1112
1113     setHidden(false);
1114
1115     if (!platformResetStateToConsistentValues(options))
1116         return false;
1117
1118     m_shouldDecideNavigationPolicyAfterDelay = false;
1119     m_shouldDecideResponsePolicyAfterDelay = false;
1120
1121     setNavigationGesturesEnabled(false);
1122     
1123     setIgnoresViewportScaleLimits(options.ignoresViewportScaleLimits);
1124
1125     m_openPanelFileURLs = nullptr;
1126 #if PLATFORM(IOS_FAMILY)
1127     m_openPanelFileURLsMediaIcon = nullptr;
1128 #endif
1129
1130     setAllowsAnySSLCertificate(true);
1131
1132     statisticsResetToConsistentState();
1133     clearLoadedThirdPartyDomains();
1134
1135     clearAdClickAttribution();
1136
1137     m_didReceiveServerRedirectForProvisionalNavigation = false;
1138     m_serverTrustEvaluationCallbackCallsCount = 0;
1139     m_shouldDismissJavaScriptAlertsAsynchronously = false;
1140
1141     // Reset main page back to about:blank
1142     m_doneResetting = false;
1143     WKPageLoadURL(m_mainWebView->page(), blankURL());
1144     runUntil(m_doneResetting, m_currentInvocation->shortTimeout());
1145     if (!m_doneResetting)
1146         return false;
1147     
1148     if (resetStage == ResetStage::AfterTest)
1149         updateLiveDocumentsAfterTest();
1150
1151     return m_doneResetting;
1152 }
1153
1154 void TestController::updateLiveDocumentsAfterTest()
1155 {
1156     if (!m_checkForWorldLeaks)
1157         return;
1158
1159     AsyncTask([]() {
1160         // After each test, we update the list of live documents so that we can detect when an abandoned document first showed up.
1161         WKRetainPtr<WKStringRef> messageName = adoptWK(WKStringCreateWithUTF8CString("GetLiveDocuments"));
1162         WKPagePostMessageToInjectedBundle(TestController::singleton().mainWebView()->page(), messageName.get(), nullptr);
1163     }, 5_s).run();
1164 }
1165
1166 void TestController::checkForWorldLeaks()
1167 {
1168     if (!m_checkForWorldLeaks || !TestController::singleton().mainWebView())
1169         return;
1170
1171     AsyncTask([]() {
1172         // This runs at the end of a series of tests. It clears caches, runs a GC and then fetches the list of documents.
1173         WKRetainPtr<WKStringRef> messageName = adoptWK(WKStringCreateWithUTF8CString("CheckForWorldLeaks"));
1174         WKPagePostMessageToInjectedBundle(TestController::singleton().mainWebView()->page(), messageName.get(), nullptr);
1175     }, 20_s).run();
1176 }
1177
1178 void TestController::dumpResponse(const String& result)
1179 {
1180     unsigned resultLength = result.length();
1181     printf("Content-Type: text/plain\n");
1182     printf("Content-Length: %u\n", resultLength);
1183     fwrite(result.utf8().data(), 1, resultLength, stdout);
1184     printf("#EOF\n");
1185     fprintf(stderr, "#EOF\n");
1186     fflush(stdout);
1187     fflush(stderr);
1188 }
1189
1190 void TestController::findAndDumpWebKitProcessIdentifiers()
1191 {
1192 #if PLATFORM(COCOA)
1193     dumpResponse(makeString(TestController::webProcessName(), ": ",
1194         WKPageGetProcessIdentifier(TestController::singleton().mainWebView()->page()), '\n',
1195         TestController::networkProcessName(), ": ",
1196         WKContextGetNetworkProcessIdentifier(m_context.get()), '\n'));
1197 #else
1198     dumpResponse("\n"_s);
1199 #endif
1200 }
1201
1202 void TestController::findAndDumpWorldLeaks()
1203 {
1204     if (!m_checkForWorldLeaks)
1205         return;
1206
1207     checkForWorldLeaks();
1208
1209     StringBuilder builder;
1210     
1211     if (m_abandonedDocumentInfo.size()) {
1212         for (const auto& it : m_abandonedDocumentInfo) {
1213             auto documentURL = it.value.abandonedDocumentURL;
1214             if (documentURL.isEmpty())
1215                 documentURL = "(no url)";
1216             builder.append("TEST: ");
1217             builder.append(it.value.testURL);
1218             builder.append('\n');
1219             builder.append("ABANDONED DOCUMENT: ");
1220             builder.append(documentURL);
1221             builder.append('\n');
1222         }
1223     } else
1224         builder.append("no abandoned documents\n");
1225
1226     dumpResponse(builder.toString());
1227 }
1228
1229 void TestController::willDestroyWebView()
1230 {
1231     // Before we kill the web view, look for abandoned documents before that web process goes away.
1232     checkForWorldLeaks();
1233 }
1234
1235 void TestController::terminateWebContentProcess()
1236 {
1237     WKPageTerminate(m_mainWebView->page());
1238 }
1239
1240 void TestController::reattachPageToWebProcess()
1241 {
1242     // Loading a web page is the only way to reattach an existing page to a process.
1243     SetForScope<State> changeState(m_state, Resetting);
1244     m_doneResetting = false;
1245     WKPageLoadURL(m_mainWebView->page(), blankURL());
1246     runUntil(m_doneResetting, noTimeout);
1247 }
1248
1249 const char* TestController::webProcessName()
1250 {
1251     // FIXME: Find a way to not hardcode the process name.
1252 #if PLATFORM(IOS_FAMILY) && !PLATFORM(IOS_FAMILY_SIMULATOR)
1253     return "com.apple.WebKit.WebContent";
1254 #elif PLATFORM(COCOA)
1255     return "com.apple.WebKit.WebContent.Development";
1256 #elif PLATFORM(GTK)
1257     return "WebKitWebProcess";
1258 #elif PLATFORM(WPE)
1259     return "WPEWebProcess";
1260 #else
1261     return "WebProcess";
1262 #endif
1263 }
1264
1265 const char* TestController::networkProcessName()
1266 {
1267     // FIXME: Find a way to not hardcode the process name.
1268 #if PLATFORM(IOS_FAMILY) && !PLATFORM(IOS_FAMILY_SIMULATOR)
1269     return "com.apple.WebKit.Networking";
1270 #elif PLATFORM(COCOA)
1271     return "com.apple.WebKit.Networking.Development";
1272 #elif PLATFORM(GTK)
1273     return "WebKitNetworkProcess";
1274 #elif PLATFORM(WPE)
1275     return "WPENetworkProcess";
1276 #else
1277     return "NetworkProcess";
1278 #endif
1279 }
1280
1281 #if !PLATFORM(COCOA)
1282 void TestController::setAllowsAnySSLCertificate(bool allows)
1283 {
1284     m_allowsAnySSLCertificate = allows;
1285     WKContextSetAllowsAnySSLCertificateForWebSocketTesting(platformContext(), allows);
1286 }
1287 #endif
1288
1289 static std::string testPath(WKURLRef url)
1290 {
1291     auto scheme = adoptWK(WKURLCopyScheme(url));
1292     if (WKStringIsEqualToUTF8CStringIgnoringCase(scheme.get(), "file")) {
1293         auto path = adoptWK(WKURLCopyPath(url));
1294         auto buffer = std::vector<char>(WKStringGetMaximumUTF8CStringSize(path.get()));
1295         auto length = WKStringGetUTF8CString(path.get(), buffer.data(), buffer.size());
1296         RELEASE_ASSERT(length > 0);
1297 #if OS(WINDOWS)
1298         // Remove the first '/' if it starts with something like "/C:/".
1299         if (length >= 4 && buffer[0] == '/' && buffer[2] == ':' && buffer[3] == '/')
1300             return std::string(buffer.data() + 1, length - 1);
1301 #endif
1302         return std::string(buffer.data(), length - 1);
1303     }
1304     return std::string();
1305 }
1306
1307 static WKURLRef createTestURL(const char* pathOrURL)
1308 {
1309     if (strstr(pathOrURL, "http://") || strstr(pathOrURL, "https://") || strstr(pathOrURL, "file://"))
1310         return WKURLCreateWithUTF8CString(pathOrURL);
1311
1312     // Creating from filesytem path.
1313     size_t length = strlen(pathOrURL);
1314     if (!length)
1315         return 0;
1316
1317 #if PLATFORM(WIN)
1318     bool isAbsolutePath = false;
1319     if (strlen(pathOrURL) >= 3 && pathOrURL[1] == ':' && pathOrURL[2] == pathSeparator)
1320         isAbsolutePath = true;
1321 #else
1322     bool isAbsolutePath = pathOrURL[0] == pathSeparator;
1323 #endif
1324     const char* filePrefix = "file://";
1325     static const size_t prefixLength = strlen(filePrefix);
1326
1327     UniqueArray<char> buffer;
1328     if (isAbsolutePath) {
1329         buffer = makeUniqueArray<char>(prefixLength + length + 1);
1330         strcpy(buffer.get(), filePrefix);
1331         strcpy(buffer.get() + prefixLength, pathOrURL);
1332     } else {
1333         buffer = makeUniqueArray<char>(prefixLength + PATH_MAX + length + 2); // 1 for the pathSeparator
1334         strcpy(buffer.get(), filePrefix);
1335         if (!getcwd(buffer.get() + prefixLength, PATH_MAX))
1336             return 0;
1337         size_t numCharacters = strlen(buffer.get());
1338         buffer[numCharacters] = pathSeparator;
1339         strcpy(buffer.get() + numCharacters + 1, pathOrURL);
1340     }
1341
1342     return WKURLCreateWithUTF8CString(buffer.get());
1343 }
1344
1345 static bool parseBooleanTestHeaderValue(const std::string& value)
1346 {
1347     if (value == "true")
1348         return true;
1349     if (value == "false")
1350         return false;
1351
1352     LOG_ERROR("Found unexpected value '%s' for boolean option. Expected 'true' or 'false'.", value.c_str());
1353     return false;
1354 }
1355
1356 static std::string parseStringTestHeaderValueAsRelativePath(const std::string& value, const std::string& pathOrURL)
1357 {
1358     WKRetainPtr<WKURLRef> baseURL = adoptWK(createTestURL(pathOrURL.c_str()));
1359     WKRetainPtr<WKURLRef> relativeURL = adoptWK(WKURLCreateWithBaseURL(baseURL.get(), value.c_str()));
1360     return toSTD(adoptWK(WKURLCopyPath(relativeURL.get())));
1361 }
1362
1363 static void updateTestOptionsFromTestHeader(TestOptions& testOptions, const std::string& pathOrURL, const std::string& absolutePath)
1364 {
1365     std::string filename = absolutePath;
1366     if (filename.empty()) {
1367         // Gross. Need to reduce conversions between all the string types and URLs.
1368         WKRetainPtr<WKURLRef> wkURL = adoptWK(createTestURL(pathOrURL.c_str()));
1369         filename = testPath(wkURL.get());
1370     }
1371
1372     if (filename.empty())
1373         return;
1374
1375     std::string options;
1376     std::ifstream testFile(filename.data());
1377     if (!testFile.good())
1378         return;
1379     getline(testFile, options);
1380     std::string beginString("webkit-test-runner [ ");
1381     std::string endString(" ]");
1382     size_t beginLocation = options.find(beginString);
1383     if (beginLocation == std::string::npos)
1384         return;
1385     size_t endLocation = options.find(endString, beginLocation);
1386     if (endLocation == std::string::npos) {
1387         LOG_ERROR("Could not find end of test header in %s", filename.c_str());
1388         return;
1389     }
1390     std::string pairString = options.substr(beginLocation + beginString.size(), endLocation - (beginLocation + beginString.size()));
1391     size_t pairStart = 0;
1392     while (pairStart < pairString.size()) {
1393         size_t pairEnd = pairString.find(" ", pairStart);
1394         if (pairEnd == std::string::npos)
1395             pairEnd = pairString.size();
1396         size_t equalsLocation = pairString.find("=", pairStart);
1397         if (equalsLocation == std::string::npos) {
1398             LOG_ERROR("Malformed option in test header (could not find '=' character) in %s", filename.c_str());
1399             break;
1400         }
1401         auto key = pairString.substr(pairStart, equalsLocation - pairStart);
1402         auto value = pairString.substr(equalsLocation + 1, pairEnd - (equalsLocation + 1));
1403
1404         if (!key.rfind("experimental:")) {
1405             key = key.substr(13);
1406             testOptions.experimentalFeatures.add(String(key.c_str()), parseBooleanTestHeaderValue(value));
1407         }
1408
1409         if (!key.rfind("internal:")) {
1410             key = key.substr(9);
1411             testOptions.internalDebugFeatures.add(String(key.c_str()), parseBooleanTestHeaderValue(value));
1412         }
1413
1414         if (key == "language")
1415             testOptions.contextOptions.overrideLanguages = String(value.c_str()).split(',');
1416         else if (key == "useThreadedScrolling")
1417             testOptions.useThreadedScrolling = parseBooleanTestHeaderValue(value);
1418         else if (key == "useAcceleratedDrawing")
1419             testOptions.useAcceleratedDrawing = parseBooleanTestHeaderValue(value);
1420         else if (key == "useFlexibleViewport")
1421             testOptions.useFlexibleViewport = parseBooleanTestHeaderValue(value);
1422         else if (key == "useDataDetection")
1423             testOptions.useDataDetection = parseBooleanTestHeaderValue(value);
1424         else if (key == "useMockScrollbars")
1425             testOptions.useMockScrollbars = parseBooleanTestHeaderValue(value);
1426         else if (key == "needsSiteSpecificQuirks")
1427             testOptions.needsSiteSpecificQuirks = parseBooleanTestHeaderValue(value);
1428         else if (key == "ignoresViewportScaleLimits")
1429             testOptions.ignoresViewportScaleLimits = parseBooleanTestHeaderValue(value);
1430         else if (key == "useCharacterSelectionGranularity")
1431             testOptions.useCharacterSelectionGranularity = parseBooleanTestHeaderValue(value);
1432         else if (key == "enableAttachmentElement")
1433             testOptions.enableAttachmentElement = parseBooleanTestHeaderValue(value);
1434         else if (key == "enableIntersectionObserver")
1435             testOptions.enableIntersectionObserver = parseBooleanTestHeaderValue(value);
1436         else if (key == "useEphemeralSession")
1437             testOptions.useEphemeralSession = parseBooleanTestHeaderValue(value);
1438         else if (key == "enableMenuItemElement")
1439             testOptions.enableMenuItemElement = parseBooleanTestHeaderValue(value);
1440         else if (key == "enableKeygenElement")
1441             testOptions.enableKeygenElement = parseBooleanTestHeaderValue(value);
1442         else if (key == "enableModernMediaControls")
1443             testOptions.enableModernMediaControls = parseBooleanTestHeaderValue(value);
1444         else if (key == "enablePointerLock")
1445             testOptions.enablePointerLock = parseBooleanTestHeaderValue(value);
1446         else if (key == "enableWebAuthentication")
1447             testOptions.enableWebAuthentication = parseBooleanTestHeaderValue(value);
1448         else if (key == "enableWebAuthenticationLocalAuthenticator")
1449             testOptions.enableWebAuthenticationLocalAuthenticator = parseBooleanTestHeaderValue(value);
1450         else if (key == "enableIsSecureContextAttribute")
1451             testOptions.enableIsSecureContextAttribute = parseBooleanTestHeaderValue(value);
1452         else if (key == "enableInspectorAdditions")
1453             testOptions.enableInspectorAdditions = parseBooleanTestHeaderValue(value);
1454         else if (key == "dumpJSConsoleLogInStdErr")
1455             testOptions.dumpJSConsoleLogInStdErr = parseBooleanTestHeaderValue(value);
1456         else if (key == "applicationManifest")
1457             testOptions.applicationManifest = parseStringTestHeaderValueAsRelativePath(value, pathOrURL);
1458         else if (key == "allowCrossOriginSubresourcesToAskForCredentials")
1459             testOptions.allowCrossOriginSubresourcesToAskForCredentials = parseBooleanTestHeaderValue(value);
1460         else if (key == "domPasteAllowed")
1461             testOptions.domPasteAllowed = parseBooleanTestHeaderValue(value);
1462         else if (key == "enableProcessSwapOnNavigation")
1463             testOptions.contextOptions.enableProcessSwapOnNavigation = parseBooleanTestHeaderValue(value);
1464         else if (key == "enableProcessSwapOnWindowOpen")
1465             testOptions.contextOptions.enableProcessSwapOnWindowOpen = parseBooleanTestHeaderValue(value);
1466         else if (key == "useServiceWorkerShortTimeout")
1467             testOptions.contextOptions.useServiceWorkerShortTimeout = parseBooleanTestHeaderValue(value);
1468         else if (key == "enableColorFilter")
1469             testOptions.enableColorFilter = parseBooleanTestHeaderValue(value);
1470         else if (key == "punchOutWhiteBackgroundsInDarkMode")
1471             testOptions.punchOutWhiteBackgroundsInDarkMode = parseBooleanTestHeaderValue(value);
1472         else if (key == "jscOptions")
1473             testOptions.jscOptions = value;
1474         else if (key == "additionalSupportedImageTypes")
1475             testOptions.additionalSupportedImageTypes = value;
1476         else if (key == "runSingly")
1477             testOptions.runSingly = parseBooleanTestHeaderValue(value);
1478         else if (key == "shouldIgnoreMetaViewport")
1479             testOptions.shouldIgnoreMetaViewport = parseBooleanTestHeaderValue(value);
1480         else if (key == "spellCheckingDots")
1481             testOptions.shouldShowSpellCheckingDots = parseBooleanTestHeaderValue(value);
1482         else if (key == "enableServiceControls")
1483             testOptions.enableServiceControls = parseBooleanTestHeaderValue(value);
1484         else if (key == "enableEditableImages")
1485             testOptions.enableEditableImages = parseBooleanTestHeaderValue(value);
1486         else if (key == "editable")
1487             testOptions.editable = parseBooleanTestHeaderValue(value);
1488         else if (key == "shouldHandleRunOpenPanel")
1489             testOptions.shouldHandleRunOpenPanel = parseBooleanTestHeaderValue(value);
1490         else if (key == "shouldPresentPopovers")
1491             testOptions.shouldPresentPopovers = parseBooleanTestHeaderValue(value);
1492         else if (key == "contentInset.top")
1493             testOptions.contentInsetTop = std::stod(value);
1494         else if (key == "ignoreSynchronousMessagingTimeouts")
1495             testOptions.contextOptions.ignoreSynchronousMessagingTimeouts = parseBooleanTestHeaderValue(value);
1496         else if (key == "contentMode")
1497             testOptions.contentMode = { value.c_str() };
1498         else if (key == "applicationBundleIdentifier")
1499             testOptions.applicationBundleIdentifier = { value.c_str() };
1500         else if (key == "enableAppNap")
1501             testOptions.enableAppNap = parseBooleanTestHeaderValue(value);
1502         else if (key == "enableBackForwardCache")
1503             testOptions.enableBackForwardCache = parseBooleanTestHeaderValue(value);
1504         else if (key == "enableLazyImageLoading")
1505             testOptions.enableLazyImageLoading = parseBooleanTestHeaderValue(value);
1506         else if (key == "allowsLinkPreview")
1507             testOptions.allowsLinkPreview = parseBooleanTestHeaderValue(value);
1508         else if (key == "enableCaptureVideoInUIProcess")
1509             testOptions.enableCaptureVideoInUIProcess = parseBooleanTestHeaderValue(value);
1510         else if (key == "enableCaptureVideoInGPUProcess")
1511             testOptions.enableCaptureVideoInGPUProcess = parseBooleanTestHeaderValue(value);
1512         else if (key == "enableCaptureAudioInGPUProcess")
1513             testOptions.enableCaptureAudioInGPUProcess = parseBooleanTestHeaderValue(value);
1514         else if (key == "allowTopNavigationToDataURLs")
1515             testOptions.allowTopNavigationToDataURLs = parseBooleanTestHeaderValue(value);
1516         
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     WKWebsiteDataStoreSetResourceLoadStatisticsEnabled(WKContextGetWebsiteDataStore(context), true);
3115     return context;
3116 }
3117
3118 bool TestController::platformResetStateToConsistentValues(const TestOptions&)
3119 {
3120     return true;
3121 }
3122
3123 unsigned TestController::imageCountInGeneralPasteboard() const
3124 {
3125     return 0;
3126 }
3127
3128 void TestController::removeAllSessionCredentials()
3129 {
3130 }
3131
3132 void TestController::getAllStorageAccessEntries()
3133 {
3134 }
3135
3136 void TestController::loadedThirdPartyDomains()
3137 {
3138 }
3139
3140 void TestController::clearLoadedThirdPartyDomains()
3141 {
3142 }
3143
3144 void TestController::getWebViewCategory()
3145 {
3146 }
3147
3148 #endif
3149
3150 struct ClearServiceWorkerRegistrationsCallbackContext {
3151     explicit ClearServiceWorkerRegistrationsCallbackContext(TestController& controller)
3152         : testController(controller)
3153     {
3154     }
3155
3156     TestController& testController;
3157     bool done { false };
3158 };
3159
3160 static void clearServiceWorkerRegistrationsCallback(void* userData)
3161 {
3162     auto* context = static_cast<ClearServiceWorkerRegistrationsCallbackContext*>(userData);
3163     context->done = true;
3164     context->testController.notifyDone();
3165 }
3166
3167 void TestController::clearServiceWorkerRegistrations()
3168 {
3169     ClearServiceWorkerRegistrationsCallbackContext context(*this);
3170
3171     WKWebsiteDataStoreRemoveAllServiceWorkerRegistrations(TestController::defaultWebsiteDataStore(), &context, clearServiceWorkerRegistrationsCallback);
3172     runUntil(context.done, noTimeout);
3173 }
3174
3175 struct ClearDOMCacheCallbackContext {
3176     explicit ClearDOMCacheCallbackContext(TestController& controller)
3177         : testController(controller)
3178     {
3179     }
3180
3181     TestController& testController;
3182     bool done { false };
3183 };
3184
3185 static void clearDOMCacheCallback(void* userData)
3186 {
3187     auto* context = static_cast<ClearDOMCacheCallbackContext*>(userData);
3188     context->done = true;
3189     context->testController.notifyDone();
3190 }
3191
3192 void TestController::clearDOMCache(WKStringRef origin)
3193 {
3194     ClearDOMCacheCallbackContext context(*this);
3195
3196     auto cacheOrigin = adoptWK(WKSecurityOriginCreateFromString(origin));
3197     WKWebsiteDataStoreRemoveFetchCacheForOrigin(TestController::defaultWebsiteDataStore(), cacheOrigin.get(), &context, clearDOMCacheCallback);
3198     runUntil(context.done, noTimeout);
3199 }
3200
3201 void TestController::clearDOMCaches()
3202 {
3203     ClearDOMCacheCallbackContext context(*this);
3204
3205     WKWebsiteDataStoreRemoveAllFetchCaches(TestController::defaultWebsiteDataStore(), &context, clearDOMCacheCallback);
3206     runUntil(context.done, noTimeout);
3207 }
3208
3209 struct StorageVoidCallbackContext {
3210     explicit StorageVoidCallbackContext(TestController& controller)
3211         : testController(controller)
3212     {
3213     }
3214
3215     TestController& testController;
3216     bool done { false };
3217 };
3218
3219 static void StorageVoidCallback(void* userData)
3220 {
3221     auto* context = static_cast<StorageVoidCallbackContext*>(userData);
3222     context->done = true;
3223     context->testController.notifyDone();
3224 }
3225
3226 void TestController::clearIndexedDatabases()
3227 {
3228     StorageVoidCallbackContext context(*this);
3229     WKWebsiteDataStoreRemoveAllIndexedDatabases(websiteDataStore(), &context, StorageVoidCallback);
3230     runUntil(context.done, noTimeout);
3231 }
3232
3233 void TestController::clearLocalStorage()
3234 {
3235     StorageVoidCallbackContext context(*this);
3236     WKWebsiteDataStoreRemoveLocalStorage(websiteDataStore(), &context, StorageVoidCallback);
3237     runUntil(context.done, noTimeout);
3238
3239     StorageVoidCallbackContext legacyContext(*this);
3240     WKContextClearLegacyPrivateBrowsingLocalStorage(platformContext(), &legacyContext, StorageVoidCallback);
3241     runUntil(legacyContext.done, noTimeout);
3242 }
3243
3244 void TestController::syncLocalStorage()
3245 {
3246     StorageVoidCallbackContext context(*this);
3247     WKContextSyncLocalStorage(platformContext(), &context, StorageVoidCallback);
3248     runUntil(context.done, noTimeout);
3249 }
3250
3251 void TestController::resetQuota()
3252 {
3253     StorageVoidCallbackContext context(*this);
3254     WKWebsiteDataStoreResetQuota(TestController::websiteDataStore(), &context, StorageVoidCallback);
3255     runUntil(context.done, noTimeout);
3256 }
3257
3258 struct FetchCacheOriginsCallbackContext {
3259     FetchCacheOriginsCallbackContext(TestController& controller, WKStringRef origin)
3260         : testController(controller)
3261         , origin(origin)
3262     {
3263     }
3264
3265     TestController& testController;
3266     WKStringRef origin;
3267
3268     bool done { false };
3269     bool result { false };
3270 };
3271
3272 static void fetchCacheOriginsCallback(WKArrayRef origins, void* userData)
3273 {
3274     auto* context = static_cast<FetchCacheOriginsCallbackContext*>(userData);
3275     context->done = true;
3276
3277     auto size = WKArrayGetSize(origins);
3278     for (size_t index = 0; index < size && !context->result; ++index) {
3279         WKSecurityOriginRef securityOrigin = reinterpret_cast<WKSecurityOriginRef>(WKArrayGetItemAtIndex(origins, index));
3280         if (WKStringIsEqual(context->origin, adoptWK(WKSecurityOriginCopyToString(securityOrigin)).get()))
3281             context->result = true;
3282     }
3283     context->testController.notifyDone();
3284 }
3285
3286 bool TestController::hasDOMCache(WKStringRef origin)
3287 {
3288     FetchCacheOriginsCallbackContext context(*this, origin);
3289     WKWebsiteDataStoreGetFetchCacheOrigins(TestController::defaultWebsiteDataStore(), &context, fetchCacheOriginsCallback);
3290     runUntil(context.done, noTimeout);
3291     return context.result;
3292 }
3293
3294 struct FetchCacheSizeForOriginCallbackContext {
3295     explicit FetchCacheSizeForOriginCallbackContext(TestController& controller)
3296         : testController(controller)
3297     {
3298     }
3299
3300     TestController& testController;
3301
3302     bool done { false };
3303     uint64_t result { 0 };
3304 };
3305
3306 static void fetchCacheSizeForOriginCallback(uint64_t size, void* userData)
3307 {
3308     auto* context = static_cast<FetchCacheSizeForOriginCallbackContext*>(userData);
3309     context->done = true;
3310     context->result = size;
3311     context->testController.notifyDone();
3312 }
3313
3314 uint64_t TestController::domCacheSize(WKStringRef origin)
3315 {
3316     FetchCacheSizeForOriginCallbackContext context(*this);
3317     WKWebsiteDataStoreGetFetchCacheSizeForOrigin(TestController::defaultWebsiteDataStore(), origin, &context, fetchCacheSizeForOriginCallback);
3318     runUntil(context.done, noTimeout);
3319     return context.result;
3320 }
3321
3322 #if !PLATFORM(COCOA)
3323 void TestController::setAllowStorageQuotaIncrease(bool)
3324 {
3325     // FIXME: To implement.
3326 }
3327
3328 bool TestController::isDoingMediaCapture() const
3329 {
3330     return false;
3331 }
3332
3333 #endif
3334
3335 struct ResourceStatisticsCallbackContext {
3336     explicit ResourceStatisticsCallbackContext(TestController& controller)
3337         : testController(controller)
3338     {
3339     }
3340
3341     TestController& testController;
3342     bool done { false };
3343     bool result { false };
3344     WKRetainPtr<WKStringRef> resourceLoadStatisticsRepresentation;
3345 };
3346     
3347 static void resourceStatisticsStringResultCallback(WKStringRef resourceLoadStatisticsRepresentation, void* userData)
3348 {
3349     auto* context = static_cast<ResourceStatisticsCallbackContext*>(userData);
3350     context->resourceLoadStatisticsRepresentation = resourceLoadStatisticsRepresentation;
3351     context->done = true;
3352     context->testController.notifyDone();
3353 }
3354
3355 static void resourceStatisticsVoidResultCallback(void* userData)
3356 {
3357     auto* context = static_cast<ResourceStatisticsCallbackContext*>(userData);
3358     context->done = true;
3359     context->testController.notifyDone();
3360 }
3361
3362 static void resourceStatisticsBooleanResultCallback(bool result, void* userData)
3363 {
3364     auto* context = static_cast<ResourceStatisticsCallbackContext*>(userData);
3365     context->result = result;
3366     context->done = true;
3367     context->testController.notifyDone();
3368 }
3369
3370 void TestController::setStatisticsEnabled(bool value)
3371 {
3372     WKWebsiteDataStoreSetResourceLoadStatisticsEnabled(websiteDataStore(), value);
3373 }
3374
3375 bool TestController::isStatisticsEphemeral()
3376 {
3377     ResourceStatisticsCallbackContext context(*this);
3378     WKWebsiteDataStoreIsStatisticsEphemeral(websiteDataStore(), &context, resourceStatisticsBooleanResultCallback);
3379     runUntil(context.done, noTimeout);
3380     return context.result;
3381 }
3382
3383 void TestController::setStatisticsDebugMode(bool value)
3384 {
3385     ResourceStatisticsCallbackContext context(*this);
3386     WKWebsiteDataStoreSetResourceLoadStatisticsDebugModeWithCompletionHandler(websiteDataStore(), value, &context, resourceStatisticsVoidResultCallback);
3387     runUntil(context.done, noTimeout);
3388     m_currentInvocation->didSetStatisticsDebugMode();
3389 }
3390
3391 void TestController::setStatisticsPrevalentResourceForDebugMode(WKStringRef hostName)
3392 {
3393     ResourceStatisticsCallbackContext context(*this);
3394     WKWebsiteDataStoreSetResourceLoadStatisticsPrevalentResourceForDebugMode(websiteDataStore(), hostName, &context, resourceStatisticsVoidResultCallback);
3395     runUntil(context.done, noTimeout);
3396     m_currentInvocation->didSetPrevalentResourceForDebugMode();
3397 }
3398
3399 void TestController::setStatisticsLastSeen(WKStringRef host, double seconds)
3400 {
3401     ResourceStatisticsCallbackContext context(*this);
3402     WKWebsiteDataStoreSetStatisticsLastSeen(websiteDataStore(), host, seconds, &context, resourceStatisticsVoidResultCallback);
3403     runUntil(context.done, noTimeout);
3404     m_currentInvocation->didSetLastSeen();
3405 }
3406
3407 void TestController::setStatisticsMergeStatistic(WKStringRef host, WKStringRef topFrameDomain1, WKStringRef topFrameDomain2, double lastSeen, bool hadUserInteraction, double mostRecentUserInteraction, bool isGrandfathered, bool isPrevalent, bool isVeryPrevalent, int dataRecordsRemoved)
3408 {
3409     ResourceStatisticsCallbackContext context(*this);
3410     WKWebsiteDataStoreSetStatisticsMergeStatistic(websiteDataStore(), host, topFrameDomain1, topFrameDomain2, lastSeen, hadUserInteraction, mostRecentUserInteraction, isGrandfathered, isPrevalent, isVeryPrevalent, dataRecordsRemoved, &context, resourceStatisticsVoidResultCallback);
3411     runUntil(context.done, noTimeout);
3412     m_currentInvocation->didMergeStatistic();
3413 }
3414
3415 void TestController::setStatisticsPrevalentResource(WKStringRef host, bool value)
3416 {
3417     ResourceStatisticsCallbackContext context(*this);
3418     WKWebsiteDataStoreSetStatisticsPrevalentResource(websiteDataStore(), host, value, &context, resourceStatisticsVoidResultCallback);
3419     runUntil(context.done, noTimeout);
3420     m_currentInvocation->didSetPrevalentResource();
3421 }
3422
3423 void TestController::setStatisticsVeryPrevalentResource(WKStringRef host, bool value)
3424 {
3425     ResourceStatisticsCallbackContext context(*this);
3426     WKWebsiteDataStoreSetStatisticsVeryPrevalentResource(websiteDataStore(), host, value, &context, resourceStatisticsVoidResultCallback);
3427     runUntil(context.done, noTimeout);
3428     m_currentInvocation->didSetVeryPrevalentResource();
3429 }
3430     
3431 String TestController::dumpResourceLoadStatistics()
3432 {
3433     ResourceStatisticsCallbackContext context(*this);
3434     WKWebsiteDataStoreDumpResourceLoadStatistics(websiteDataStore(), &context, resourceStatisticsStringResultCallback);
3435     runUntil(context.done, noTimeout);
3436     return toWTFString(context.resourceLoadStatisticsRepresentation.get());
3437 }
3438
3439 bool TestController::isStatisticsPrevalentResource(WKStringRef host)
3440 {
3441     ResourceStatisticsCallbackContext context(*this);
3442     WKWebsiteDataStoreIsStatisticsPrevalentResource(websiteDataStore(), host, &context, resourceStatisticsBooleanResultCallback);
3443     runUntil(context.done, noTimeout);
3444     return context.result;
3445 }
3446
3447 bool TestController::isStatisticsVeryPrevalentResource(WKStringRef host)
3448 {
3449     ResourceStatisticsCallbackContext context(*this);
3450     WKWebsiteDataStoreIsStatisticsVeryPrevalentResource(websiteDataStore(), host, &context, resourceStatisticsBooleanResultCallback);
3451     runUntil(context.done, noTimeout);
3452     return context.result;
3453 }
3454
3455 bool TestController::isStatisticsRegisteredAsSubresourceUnder(WKStringRef subresourceHost, WKStringRef topFrameHost)
3456 {
3457     ResourceStatisticsCallbackContext context(*this);
3458     WKWebsiteDataStoreIsStatisticsRegisteredAsSubresourceUnder(websiteDataStore(), subresourceHost, topFrameHost, &context, resourceStatisticsBooleanResultCallback);
3459     runUntil(context.done, noTimeout);
3460     return context.result;
3461 }
3462
3463 bool TestController::isStatisticsRegisteredAsSubFrameUnder(WKStringRef subFrameHost, WKStringRef topFrameHost)
3464 {
3465     ResourceStatisticsCallbackContext context(*this);
3466     WKWebsiteDataStoreIsStatisticsRegisteredAsSubFrameUnder(websiteDataStore(), subFrameHost, topFrameHost, &context, resourceStatisticsBooleanResultCallback);
3467     runUntil(context.done, noTimeout);
3468     return context.result;
3469 }
3470
3471 bool TestController::isStatisticsRegisteredAsRedirectingTo(WKStringRef hostRedirectedFrom, WKStringRef hostRedirectedTo)
3472 {
3473     ResourceStatisticsCallbackContext context(*this);
3474     WKWebsiteDataStoreIsStatisticsRegisteredAsRedirectingTo(websiteDataStore(), hostRedirectedFrom, hostRedirectedTo, &context, resourceStatisticsBooleanResultCallback);
3475     runUntil(context.done, noTimeout);
3476     return context.result;
3477 }
3478
3479 void TestController::setStatisticsHasHadUserInteraction(WKStringRef host, bool value)
3480 {
3481     ResourceStatisticsCallbackContext context(*this);
3482     WKWebsiteDataStoreSetStatisticsHasHadUserInteraction(websiteDataStore(), host, value, &context, resourceStatisticsVoidResultCallback);
3483     runUntil(context.done, noTimeout);
3484     m_currentInvocation->didSetHasHadUserInteraction();
3485 }
3486
3487 bool TestController::isStatisticsHasHadUserInteraction(WKStringRef host)
3488 {
3489     ResourceStatisticsCallbackContext context(*this);
3490     WKWebsiteDataStoreIsStatisticsHasHadUserInteraction(websiteDataStore(), host, &context, resourceStatisticsBooleanResultCallback);
3491     runUntil(context.done, noTimeout);
3492     return context.result;
3493 }
3494
3495 bool TestController::isStatisticsOnlyInDatabaseOnce(WKStringRef subHost, WKStringRef topHost)
3496 {
3497     ResourceStatisticsCallbackContext context(*this);
3498     WKWebsiteDataStoreIsStatisticsOnlyInDatabaseOnce(websiteDataStore(), subHost, topHost, &context, resourceStatisticsBooleanResultCallback);
3499     runUntil(context.done, noTimeout);
3500     return context.result;
3501 }
3502
3503 void TestController::setStatisticsGrandfathered(WKStringRef host, bool value)
3504 {
3505     WKWebsiteDataStoreSetStatisticsGrandfathered(websiteDataStore(), host, value);
3506 }
3507
3508 bool TestController::isStatisticsGrandfathered(WKStringRef host)
3509 {
3510     ResourceStatisticsCallbackContext context(*this);
3511     WKWebsiteDataStoreIsStatisticsGrandfathered(websiteDataStore(), host, &context, resourceStatisticsBooleanResultCallback);
3512     runUntil(context.done, noTimeout);
3513     return context.result;
3514 }
3515
3516 void TestController::setUseITPDatabase(bool value)
3517 {
3518     ResourceStatisticsCallbackContext context(*this);
3519     WKWebsiteDataStoreSetUseITPDatabase(websiteDataStore(), value, &context, resourceStatisticsVoidResultCallback);
3520     runUntil(context.done, noTimeout);
3521 }
3522
3523 void TestController::setStatisticsSubframeUnderTopFrameOrigin(WKStringRef host, WKStringRef topFrameHost)
3524 {
3525     WKWebsiteDataStoreSetStatisticsSubframeUnderTopFrameOrigin(websiteDataStore(), host, topFrameHost);
3526 }
3527
3528 void TestController::setStatisticsSubresourceUnderTopFrameOrigin(WKStringRef host, WKStringRef topFrameHost)
3529 {
3530     WKWebsiteDataStoreSetStatisticsSubresourceUnderTopFrameOrigin(websiteDataStore(), host, topFrameHost);
3531 }
3532
3533 void TestController::setStatisticsSubresourceUniqueRedirectTo(WKStringRef host, WKStringRef hostRedirectedTo)
3534 {
3535     WKWebsiteDataStoreSetStatisticsSubresourceUniqueRedirectTo(websiteDataStore(), host, hostRedirectedTo);
3536 }
3537
3538 void TestController::setStatisticsSubresourceUniqueRedirectFrom(WKStringRef host, WKStringRef hostRedirectedFrom)
3539 {
3540     WKWebsiteDataStoreSetStatisticsSubresourceUniqueRedirectFrom(websiteDataStore(), host, hostRedirectedFrom);
3541 }
3542
3543 void TestController::setStatisticsTopFrameUniqueRedirectTo(WKStringRef host, WKStringRef hostRedirectedTo)
3544 {
3545     WKWebsiteDataStoreSetStatisticsTopFrameUniqueRedirectTo(websiteDataStore(), host, hostRedirectedTo);
3546 }
3547
3548 void TestController::setStatisticsTopFrameUniqueRedirectFrom(WKStringRef host, WKStringRef hostRedirectedFrom)
3549 {
3550     WKWebsiteDataStoreSetStatisticsTopFrameUniqueRedirectFrom(websiteDataStore(), host, hostRedirectedFrom);
3551 }
3552
3553 void TestController::setStatisticsCrossSiteLoadWithLinkDecoration(WKStringRef fromHost, WKStringRef toHost)
3554 {
3555     ResourceStatisticsCallbackContext context(*this);
3556     WKWebsiteDataStoreSetStatisticsCrossSiteLoadWithLinkDecoration(websiteDataStore(), fromHost, toHost, &context, resourceStatisticsVoidResultCallback);
3557     runUntil(context.done, noTimeout);
3558 }
3559
3560 void TestController::setStatisticsTimeToLiveUserInteraction(double seconds)
3561 {
3562     ResourceStatisticsCallbackContext context(*this);
3563     WKWebsiteDataStoreSetStatisticsTimeToLiveUserInteraction(websiteDataStore(), seconds, &context, resourceStatisticsVoidResultCallback);
3564     runUntil(context.done, noTimeout);
3565 }
3566
3567 void TestController::statisticsProcessStatisticsAndDataRecords()
3568 {
3569     ResourceStatisticsCallbackContext context(*this);
3570     WKWebsiteDataStoreStatisticsProcessStatisticsAndDataRecords(websiteDataStore(), &context, resourceStatisticsVoidResultCallback);
3571     runUntil(context.done, noTimeout);
3572 }
3573
3574 void TestController::statisticsUpdateCookieBlocking()
3575 {
3576     ResourceStatisticsCallbackContext context(*this);
3577     WKWebsiteDataStoreStatisticsUpdateCookieBlocking(websiteDataStore(), &context, resourceStatisticsVoidResultCallback);
3578     runUntil(context.done, noTimeout);
3579     m_currentInvocation->didSetBlockCookiesForHost();
3580 }
3581
3582 void TestController::statisticsSubmitTelemetry()
3583 {
3584     WKWebsiteDataStoreStatisticsSubmitTelemetry(websiteDataStore());
3585 }
3586
3587 void TestController::setStatisticsNotifyPagesWhenDataRecordsWereScanned(bool value)
3588 {
3589     WKWebsiteDataStoreSetStatisticsNotifyPagesWhenDataRecordsWereScanned(websiteDataStore(), value);
3590 }
3591
3592 void TestController::setStatisticsIsRunningTest(bool value)
3593 {
3594     ResourceStatisticsCallbackContext context(*this);
3595     WKWebsiteDataStoreSetStatisticsIsRunningTest(websiteDataStore(), value, &context, resourceStatisticsVoidResultCallback);
3596     runUntil(context.done, noTimeout);
3597 }
3598
3599 void TestController::setStatisticsShouldClassifyResourcesBeforeDataRecordsRemoval(bool value)
3600 {
3601     WKWebsiteDataStoreSetStatisticsShouldClassifyResourcesBeforeDataRecordsRemoval(websiteDataStore(), value);
3602 }
3603
3604 void TestController::setStatisticsNotifyPagesWhenTelemetryWasCaptured(bool value)
3605 {
3606     WKWebsiteDataStoreSetStatisticsNotifyPagesWhenTelemetryWasCaptured(websiteDataStore(), value);
3607 }
3608
3609 void TestController::setStatisticsMinimumTimeBetweenDataRecordsRemoval(double seconds)
3610 {
3611     WKWebsiteDataStoreSetStatisticsMinimumTimeBetweenDataRecordsRemoval(websiteDataStore(), seconds);
3612 }
3613
3614 void TestController::setStatisticsGrandfatheringTime(double seconds)
3615 {
3616     WKWebsiteDataStoreSetStatisticsGrandfatheringTime(websiteDataStore(), seconds);
3617 }
3618
3619 void TestController::setStatisticsMaxStatisticsEntries(unsigned entries)
3620 {
3621     WKWebsiteDataStoreSetStatisticsMaxStatisticsEntries(websiteDataStore(), entries);
3622 }
3623
3624 void TestController::setStatisticsPruneEntriesDownTo(unsigned entries)
3625 {
3626     WKWebsiteDataStoreSetStatisticsPruneEntriesDownTo(websiteDataStore(), entries);
3627 }
3628
3629 void TestController::statisticsClearInMemoryAndPersistentStore()
3630 {
3631     ResourceStatisticsCallbackContext context(*this);
3632     WKWebsiteDataStoreStatisticsClearInMemoryAndPersistentStore(websiteDataStore(), &context, resourceStatisticsVoidResultCallback);
3633     runUntil(context.done, noTimeout);
3634     m_currentInvocation->didClearStatisticsInMemoryAndPersistentStore();
3635 }
3636
3637 void TestController::statisticsClearInMemoryAndPersistentStoreModifiedSinceHours(unsigned hours)
3638 {
3639     ResourceStatisticsCallbackContext context(*this);
3640     WKWebsiteDataStoreStatisticsClearInMemoryAndPersistentStoreModifiedSinceHours(websiteDataStore(), hours, &context, resourceStatisticsVoidResultCallback);
3641     runUntil(context.done, noTimeout);
3642     m_currentInvocation->didClearStatisticsInMemoryAndPersistentStore();
3643 }
3644
3645 void TestController::statisticsClearThroughWebsiteDataRemoval()
3646 {
3647     ResourceStatisticsCallbackContext context(*this);
3648     WKWebsiteDataStoreStatisticsClearThroughWebsiteDataRemoval(websiteDataStore(), &context, resourceStatisticsVoidResultCallback);
3649     runUntil(context.done, noTimeout);
3650     m_currentInvocation->didClearStatisticsThroughWebsiteDataRemoval();
3651 }
3652
3653 void TestController::statisticsDeleteCookiesForHost(WKStringRef host, bool includeHttpOnlyCookies)
3654 {
3655     ResourceStatisticsCallbackContext context(*this);
3656     WKWebsiteDataStoreStatisticsDeleteCookiesForTesting(websiteDataStore(), host, includeHttpOnlyCookies, &context, resourceStatisticsVoidResultCallback);
3657     runUntil(context.done, noTimeout);
3658 }
3659
3660 bool TestController::isStatisticsHasLocalStorage(WKStringRef host)
3661 {
3662     ResourceStatisticsCallbackContext context(*this);
3663     WKWebsiteDataStoreStatisticsHasLocalStorage(websiteDataStore(), host, &context, resourceStatisticsBooleanResultCallback);
3664     runUntil(context.done, noTimeout);
3665     return context.result;
3666 }
3667
3668 void TestController::setStatisticsCacheMaxAgeCap(double seconds)
3669 {
3670     ResourceStatisticsCallbackContext context(*this);
3671     WKWebsiteDataStoreSetStatisticsCacheMaxAgeCap(websiteDataStore(), seconds, &context, resourceStatisticsVoidResultCallback);
3672     runUntil(context.done, noTimeout);
3673 }
3674
3675 bool TestController::hasStatisticsIsolatedSession(WKStringRef host)
3676 {
3677     ResourceStatisticsCallbackContext context(*this);
3678     WKWebsiteDataStoreStatisticsHasIsolatedSession(websiteDataStore(), host, &context, resourceStatisticsBooleanResultCallback);
3679     runUntil(context.done, noTimeout);
3680     return context.result;
3681 }
3682
3683 void TestController::setStatisticsShouldDowngradeReferrer(bool value)
3684 {
3685     ResourceStatisticsCallbackContext context(*this);
3686     WKWebsiteDataStoreSetResourceLoadStatisticsShouldDowngradeReferrerForTesting(websiteDataStore(), value, &context, resourceStatisticsVoidResultCallback);
3687     runUntil(context.done, noTimeout);
3688     m_currentInvocation->didSetShouldDowngradeReferrer();
3689 }
3690
3691 void TestController::setStatisticsShouldBlockThirdPartyCookies(bool value, bool onlyOnSitesWithoutUserInteraction)
3692 {
3693     ResourceStatisticsCallbackContext context(*this);
3694     WKWebsiteDataStoreSetResourceLoadStatisticsShouldBlockThirdPartyCookiesForTesting(websiteDataStore(), value, onlyOnSitesWithoutUserInteraction, &context, resourceStatisticsVoidResultCallback);
3695     runUntil(context.done, noTimeout);
3696     m_currentInvocation->didSetShouldBlockThirdPartyCookies();
3697 }
3698
3699 void TestController::setStatisticsFirstPartyWebsiteDataRemovalMode(bool value)
3700 {
3701     ResourceStatisticsCallbackContext context(*this);
3702     WKWebsiteDataStoreSetResourceLoadStatisticsFirstPartyWebsiteDataRemovalModeForTesting(websiteDataStore(), value, &context, resourceStatisticsVoidResultCallback);
3703     runUntil(context.done, noTimeout);
3704     m_currentInvocation->didSetFirstPartyWebsiteDataRemovalMode();
3705 }
3706
3707 void TestController::setStatisticsToSameSiteStrictCookies(WKStringRef hostName)
3708 {
3709     ResourceStatisticsCallbackContext context(*this);
3710     WKWebsiteDataStoreSetResourceLoadStatisticsToSameSiteStrictCookiesForTesting(websiteDataStore(), hostName, &context, resourceStatisticsVoidResultCallback);
3711     runUntil(context.done, noTimeout);
3712     m_currentInvocation->didSetToSameSiteStrictCookies();
3713 }
3714
3715 void TestController::statisticsResetToConsistentState()
3716 {
3717     ResourceStatisticsCallbackContext context(*this);
3718     WKWebsiteDataStoreStatisticsResetToConsistentState(websiteDataStore(), &context, resourceStatisticsVoidResultCallback);
3719     runUntil(context.done, noTimeout);
3720     m_currentInvocation->didResetStatisticsToConsistentState();
3721 }
3722
3723 void TestController::addMockMediaDevice(WKStringRef persistentID, WKStringRef label, WKStringRef type)
3724 {
3725     WKAddMockMediaDevice(platformContext(), persistentID, label, type);
3726 }
3727
3728 void TestController::clearMockMediaDevices()
3729 {
3730     WKClearMockMediaDevices(platformContext());
3731 }
3732
3733 void TestController::removeMockMediaDevice(WKStringRef persistentID)
3734 {
3735     WKRemoveMockMediaDevice(platformContext(), persistentID);
3736 }
3737
3738 void TestController::resetMockMediaDevices()
3739 {
3740     WKResetMockMediaDevices(platformContext());
3741 }
3742
3743 void TestController::setMockCameraOrientation(uint64_t orientation)
3744 {
3745     WKPageSetMockCameraOrientation(m_mainWebView->page(), orientation);
3746 }
3747
3748 bool TestController::isMockRealtimeMediaSourceCenterEnabled() const
3749 {
3750     return WKPageIsMockRealtimeMediaSourceCenterEnabled(m_mainWebView->page());
3751 }
3752
3753 struct InAppBrowserPrivacyCallbackContext {
3754     explicit InAppBrowserPrivacyCallbackContext(TestController& controller)
3755         : testController(controller)
3756     {
3757     }
3758
3759     TestController& testController;
3760     bool done { false };
3761     bool result { false };
3762 };
3763
3764 static void inAppBrowserPrivacyBooleanResultCallback(bool result, void* userData)
3765 {
3766     auto* context = static_cast<InAppBrowserPrivacyCallbackContext*>(userData);
3767     context->result = result;
3768     context->done = true;
3769     context->testController.notifyDone();
3770 }
3771
3772 static void inAppBrowserPrivacyVoidResultCallback(void* userData)
3773 {
3774     auto* context = static_cast<InAppBrowserPrivacyCallbackContext*>(userData);
3775     context->done = true;
3776     context->testController.notifyDone();
3777 }
3778
3779 bool TestController::hasAppBoundSession()
3780 {
3781     InAppBrowserPrivacyCallbackContext context(*this);
3782     WKWebsiteDataStoreHasAppBoundSession(TestController::websiteDataStore(), &context, inAppBrowserPrivacyBooleanResultCallback);
3783     runUntil(context.done, noTimeout);
3784     return context.result;
3785 }
3786
3787
3788 void TestController::setInAppBrowserPrivacyEnabled(bool value)
3789 {
3790     InAppBrowserPrivacyCallbackContext context(*this);
3791     WKWebsiteDataStoreSetInAppBrowserPrivacyEnabled(TestController::websiteDataStore(), value, &context, inAppBrowserPrivacyVoidResultCallback);
3792     runUntil(context.done, noTimeout);
3793     m_currentInvocation->didSetInAppBrowserPrivacyEnabled();
3794 }
3795
3796 void TestController::reinitializeAppBoundDomains()
3797 {
3798     WKWebsiteDataStoreReinitializeAppBoundDomains(TestController::websiteDataStore());
3799 }
3800
3801 #if !PLATFORM(COCOA)
3802 void TestController::platformAddTestOptions(TestOptions&) const
3803 {
3804 }
3805
3806 void TestController::injectUserScript(WKStringRef)
3807 {
3808 }
3809
3810 void TestController::addTestKeyToKeychain(const String&, const String&, const String&)
3811 {
3812 }
3813
3814 void TestController::cleanUpKeychain(const String&, const String&)
3815 {
3816 }
3817
3818 bool TestController::keyExistsInKeychain(const String&, const String&)
3819 {
3820     return false;
3821 }
3822
3823 void TestController::installCustomMenuAction(const String&, bool)
3824 {
3825 }
3826
3827 void TestController::setAllowedMenuActions(const Vector<String>&)
3828 {
3829 }
3830
3831 #endif
3832
3833 void TestController::sendDisplayConfigurationChangedMessageForTesting()
3834 {
3835     WKSendDisplayConfigurationChangedMessageForTesting(platformContext());
3836 }
3837
3838 void TestController::setServiceWorkerFetchTimeoutForTesting(double seconds)
3839 {
3840     WKContextSetServiceWorkerFetchTimeoutForTesting(platformContext(), seconds);
3841 }
3842
3843 struct AdClickAttributionStringResultCallbackContext {
3844     explicit AdClickAttributionStringResultCallbackContext(TestController& controller)
3845         : testController(controller)
3846     {
3847     }
3848     
3849     TestController& testController;
3850     bool done { false };
3851     WKRetainPtr<WKStringRef> adClickAttributionRepresentation;
3852 };
3853
3854 static void adClickAttributionStringResultCallback(WKStringRef adClickAttributionRepresentation, void* userData)
3855 {
3856     auto* context = static_cast<AdClickAttributionStringResultCallbackContext*>(userData);
3857     context->adClickAttributionRepresentation = adClickAttributionRepresentation;
3858     context->done = true;
3859     context->testController.notifyDone();
3860 }
3861
3862 String TestController::dumpAdClickAttribution()
3863 {
3864     AdClickAttributionStringResultCallbackContext callbackContext(*this);
3865     WKPageDumpAdClickAttribution(m_mainWebView->page(), adClickAttributionStringResultCallback, &callbackContext);
3866     runUntil(callbackContext.done, noTimeout);
3867     return toWTFString(callbackContext.adClickAttributionRepresentation.get());
3868 }
3869
3870 struct AdClickAttributionVoidCallbackContext {
3871     explicit AdClickAttributionVoidCallbackContext(TestController& controller)
3872         : testController(controller)
3873     {
3874     }
3875     
3876     TestController& testController;
3877     bool done { false };
3878 };
3879
3880 static void adClickAttributionVoidCallback(void* userData)
3881 {
3882     auto* context = static_cast<AdClickAttributionVoidCallbackContext*>(userData);
3883     context->done = true;
3884     context->testController.notifyDone();
3885 }
3886
3887 void TestController::clearAdClickAttribution()
3888 {
3889     AdClickAttributionVoidCallbackContext callbackContext(*this);
3890     WKPageClearAdClickAttribution(m_mainWebView->page(), adClickAttributionVoidCallback, &callbackContext);
3891     runUntil(callbackContext.done, noTimeout);
3892 }
3893
3894 void TestController::clearAdClickAttributionsThroughWebsiteDataRemoval()
3895 {
3896     AdClickAttributionVoidCallbackContext callbackContext(*this);
3897     WKWebsiteDataStoreClearAdClickAttributionsThroughWebsiteDataRemoval(websiteDataStore(), &callbackContext, adClickAttributionVoidCallback);
3898     runUntil(callbackContext.done, noTimeout);
3899 }
3900
3901 void TestController::setAdClickAttributionOverrideTimerForTesting(bool value)
3902 {
3903     AdClickAttributionVoidCallbackContext callbackContext(*this);
3904     WKPageSetAdClickAttributionOverrideTimerForTesting(m_mainWebView->page(), value, adClickAttributionVoidCallback, &callbackContext);
3905     runUntil(callbackContext.done, noTimeout);
3906 }
3907
3908 void TestController::setAdClickAttributionConversionURLForTesting(WKURLRef url)
3909 {
3910     AdClickAttributionVoidCallbackContext callbackContext(*this);
3911     WKPageSetAdClickAttributionConversionURLForTesting(m_mainWebView->page(), url, adClickAttributionVoidCallback, &callbackContext);
3912     runUntil(callbackContext.done, noTimeout);
3913 }
3914
3915 void TestController::markAdClickAttributionsAsExpiredForTesting()
3916 {
3917     AdClickAttributionVoidCallbackContext callbackContext(*this);
3918     WKPageMarkAdClickAttributionsAsExpiredForTesting(m_mainWebView->page(), adClickAttributionVoidCallback, &callbackContext);
3919     runUntil(callbackContext.done, noTimeout);
3920 }
3921
3922 } // namespace WTR