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