Anchor target should be ignored on activation when the download attribute is set
[WebKit-https.git] / Tools / WebKitTestRunner / TestController.cpp
1 /*
2  * Copyright (C) 2010-2018 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/WKCookieManager.h>
42 #include <WebKit/WKCredential.h>
43 #include <WebKit/WKFrameHandleRef.h>
44 #include <WebKit/WKFrameInfoRef.h>
45 #include <WebKit/WKIconDatabase.h>
46 #include <WebKit/WKMockDisplay.h>
47 #include <WebKit/WKMockMediaDevice.h>
48 #include <WebKit/WKNavigationResponseRef.h>
49 #include <WebKit/WKNotification.h>
50 #include <WebKit/WKNotificationManager.h>
51 #include <WebKit/WKNotificationPermissionRequest.h>
52 #include <WebKit/WKNumber.h>
53 #include <WebKit/WKOpenPanelResultListener.h>
54 #include <WebKit/WKPageGroup.h>
55 #include <WebKit/WKPageInjectedBundleClient.h>
56 #include <WebKit/WKPagePrivate.h>
57 #include <WebKit/WKPluginInformation.h>
58 #include <WebKit/WKPreferencesRefPrivate.h>
59 #include <WebKit/WKProtectionSpace.h>
60 #include <WebKit/WKRetainPtr.h>
61 #include <WebKit/WKSecurityOriginRef.h>
62 #include <WebKit/WKTextChecker.h>
63 #include <WebKit/WKUserMediaPermissionCheck.h>
64 #include <WebKit/WKWebsiteDataStoreRef.h>
65 #include <algorithm>
66 #include <cstdio>
67 #include <ctype.h>
68 #include <fstream>
69 #include <stdlib.h>
70 #include <string>
71 #include <unistd.h>
72 #include <wtf/AutodrainedPool.h>
73 #include <wtf/CryptographicallyRandomNumber.h>
74 #include <wtf/HexNumber.h>
75 #include <wtf/MainThread.h>
76 #include <wtf/ProcessPrivilege.h>
77 #include <wtf/RefCounted.h>
78 #include <wtf/RunLoop.h>
79 #include <wtf/SetForScope.h>
80 #include <wtf/UUID.h>
81 #include <wtf/text/CString.h>
82 #include <wtf/text/WTFString.h>
83
84 #if PLATFORM(COCOA)
85 #include <WebKit/WKContextPrivateMac.h>
86 #include <WebKit/WKPagePrivateMac.h>
87 #endif
88
89 namespace WTR {
90
91 const unsigned TestController::viewWidth = 800;
92 const unsigned TestController::viewHeight = 600;
93
94 const unsigned TestController::w3cSVGViewWidth = 480;
95 const unsigned TestController::w3cSVGViewHeight = 360;
96
97 const WTF::Seconds TestController::defaultShortTimeout = 5_s;
98 const WTF::Seconds TestController::noTimeout = -1_s;
99
100 static WKURLRef blankURL()
101 {
102     static WKURLRef staticBlankURL = WKURLCreateWithUTF8CString("about:blank");
103     return staticBlankURL;
104 }
105
106 static WKDataRef copyWebCryptoMasterKey(WKPageRef, const void*)
107 {
108     // Any 128 bit key would do, all we need for testing is to implement the callback.
109     return WKDataCreate((const uint8_t*)"\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f", 16);
110 }
111
112 static WKStringRef copySignedPublicKeyAndChallengeString(WKPageRef, const void*)
113 {
114     // Any fake response would do, all we need for testing is to implement the callback.
115     return WKStringCreateWithUTF8CString("MIHFMHEwXDANBgkqhkiG9w0BAQEFAANLADBIAkEAnX0TILJrOMUue%2BPtwBRE6XfV%0AWtKQbsshxk5ZhcUwcwyvcnIq9b82QhJdoACdD34rqfCAIND46fXKQUnb0mvKzQID%0AAQABFhFNb3ppbGxhSXNNeUZyaWVuZDANBgkqhkiG9w0BAQQFAANBAAKv2Eex2n%2FS%0Ar%2F7iJNroWlSzSMtTiQTEB%2BADWHGj9u1xrUrOilq%2Fo2cuQxIfZcNZkYAkWP4DubqW%0Ai0%2F%2FrgBvmco%3D");
116 }
117
118 AsyncTask* AsyncTask::m_currentTask;
119
120 bool AsyncTask::run()
121 {
122     m_currentTask = this;
123     m_task();
124     TestController::singleton().runUntil(m_taskDone, m_timeout);
125     m_currentTask = nullptr;
126     return m_taskDone;
127 }
128
129 AsyncTask* AsyncTask::currentTask()
130 {
131     return m_currentTask;
132 }
133
134 static TestController* controller;
135
136 TestController& TestController::singleton()
137 {
138     ASSERT(controller);
139     return *controller;
140 }
141
142 TestController::TestController(int argc, const char* argv[])
143 {
144     initialize(argc, argv);
145     controller = this;
146     run();
147     controller = nullptr;
148 }
149
150 TestController::~TestController()
151 {
152     // The context will be null if WebKitTestRunner was in server mode, but ran no tests.
153     if (m_context)
154         WKIconDatabaseClose(WKContextGetIconDatabase(m_context.get()));
155
156     platformDestroy();
157 }
158
159 static WKRect getWindowFrame(WKPageRef page, const void* clientInfo)
160 {
161     PlatformWebView* view = static_cast<PlatformWebView*>(const_cast<void*>(clientInfo));
162     return view->windowFrame();
163 }
164
165 static void setWindowFrame(WKPageRef page, WKRect frame, const void* clientInfo)
166 {
167     PlatformWebView* view = static_cast<PlatformWebView*>(const_cast<void*>(clientInfo));
168     view->setWindowFrame(frame);
169 }
170
171 static bool runBeforeUnloadConfirmPanel(WKPageRef page, WKStringRef message, WKFrameRef frame, const void*)
172 {
173     printf("CONFIRM NAVIGATION: %s\n", toSTD(message).c_str());
174     return TestController::singleton().beforeUnloadReturnValue();
175 }
176
177 static void runOpenPanel(WKPageRef page, WKFrameRef frame, WKOpenPanelParametersRef parameters, WKOpenPanelResultListenerRef resultListenerRef, const void*)
178 {
179     printf("OPEN FILE PANEL\n");
180     if (WKOpenPanelParametersGetAllowsDirectories(parameters))
181         printf("-> DIRECTORIES ARE ALLOWED\n");
182     WKArrayRef fileURLs = TestController::singleton().openPanelFileURLs();
183     if (!fileURLs || !WKArrayGetSize(fileURLs)) {
184         WKOpenPanelResultListenerCancel(resultListenerRef);
185         return;
186     }
187
188     if (WKOpenPanelParametersGetAllowsMultipleFiles(parameters)) {
189         WKOpenPanelResultListenerChooseFiles(resultListenerRef, fileURLs);
190         return;
191     }
192
193     WKTypeRef firstItem = WKArrayGetItemAtIndex(fileURLs, 0);
194     WKOpenPanelResultListenerChooseFiles(resultListenerRef, adoptWK(WKArrayCreate(&firstItem, 1)).get());
195 }
196
197 void TestController::runModal(WKPageRef page, const void* clientInfo)
198 {
199     PlatformWebView* view = static_cast<PlatformWebView*>(const_cast<void*>(clientInfo));
200     view->setWindowIsKey(false);
201     runModal(view);
202     view->setWindowIsKey(true);
203 }
204
205 static void closeOtherPage(WKPageRef page, const void* clientInfo)
206 {
207     WKPageClose(page);
208     PlatformWebView* view = static_cast<PlatformWebView*>(const_cast<void*>(clientInfo));
209     delete view;
210 }
211
212 static void focus(WKPageRef page, const void* clientInfo)
213 {
214     PlatformWebView* view = static_cast<PlatformWebView*>(const_cast<void*>(clientInfo));
215     view->focus();
216     view->setWindowIsKey(true);
217 }
218
219 static void unfocus(WKPageRef page, const void* clientInfo)
220 {
221     PlatformWebView* view = static_cast<PlatformWebView*>(const_cast<void*>(clientInfo));
222     view->setWindowIsKey(false);
223 }
224
225 static void decidePolicyForGeolocationPermissionRequest(WKPageRef, WKFrameRef, WKSecurityOriginRef, WKGeolocationPermissionRequestRef permissionRequest, const void* clientInfo)
226 {
227     TestController::singleton().handleGeolocationPermissionRequest(permissionRequest);
228 }
229
230 static void decidePolicyForUserMediaPermissionRequest(WKPageRef, WKFrameRef frame, WKSecurityOriginRef userMediaDocumentOrigin, WKSecurityOriginRef topLevelDocumentOrigin, WKUserMediaPermissionRequestRef permissionRequest, const void* clientInfo)
231 {
232     TestController::singleton().handleUserMediaPermissionRequest(frame, userMediaDocumentOrigin, topLevelDocumentOrigin, permissionRequest);
233 }
234
235 static void checkUserMediaPermissionForOrigin(WKPageRef, WKFrameRef frame, WKSecurityOriginRef userMediaDocumentOrigin, WKSecurityOriginRef topLevelDocumentOrigin, WKUserMediaPermissionCheckRef checkRequest, const void*)
236 {
237     TestController::singleton().handleCheckOfUserMediaPermissionForOrigin(frame, userMediaDocumentOrigin, topLevelDocumentOrigin, checkRequest);
238 }
239
240 static void requestPointerLock(WKPageRef page, const void*)
241 {
242     WKPageDidAllowPointerLock(page);
243 }
244
245 WKPageRef TestController::createOtherPage(WKPageRef, WKPageConfigurationRef configuration, WKNavigationActionRef navigationAction, WKWindowFeaturesRef windowFeatures, const void *clientInfo)
246 {
247     PlatformWebView* parentView = static_cast<PlatformWebView*>(const_cast<void*>(clientInfo));
248     return TestController::singleton().createOtherPage(parentView, configuration, navigationAction, windowFeatures);
249 }
250
251 WKPageRef TestController::createOtherPage(PlatformWebView* parentView, WKPageConfigurationRef configuration, WKNavigationActionRef navigationAction, WKWindowFeaturesRef windowFeatures)
252 {
253     // The test needs to call testRunner.setCanOpenWindows() to open new windows.
254     if (!m_currentInvocation->canOpenWindows())
255         return nullptr;
256
257     PlatformWebView* view = platformCreateOtherPage(parentView, configuration, parentView->options());
258     WKPageRef newPage = view->page();
259
260     view->resizeTo(800, 600);
261
262     WKPageUIClientV8 otherPageUIClient = {
263         { 8, view },
264         0, // createNewPage_deprecatedForUseWithV0
265         0, // showPage
266         closeOtherPage,
267         0, // takeFocus
268         focus,
269         unfocus,
270         0, // runJavaScriptAlert_deprecatedForUseWithV0
271         0, // runJavaScriptAlert_deprecatedForUseWithV0
272         0, // runJavaScriptAlert_deprecatedForUseWithV0
273         0, // setStatusText
274         0, // mouseDidMoveOverElement_deprecatedForUseWithV0
275         0, // missingPluginButtonClicked
276         0, // didNotHandleKeyEvent
277         0, // didNotHandleWheelEvent
278         0, // toolbarsAreVisible
279         0, // setToolbarsAreVisible
280         0, // menuBarIsVisible
281         0, // setMenuBarIsVisible
282         0, // statusBarIsVisible
283         0, // setStatusBarIsVisible
284         0, // isResizable
285         0, // setIsResizable
286         getWindowFrame,
287         setWindowFrame,
288         runBeforeUnloadConfirmPanel,
289         0, // didDraw
290         0, // pageDidScroll
291         0, // exceededDatabaseQuota
292         runOpenPanel,
293         decidePolicyForGeolocationPermissionRequest,
294         0, // headerHeight
295         0, // footerHeight
296         0, // drawHeader
297         0, // drawFooter
298         0, // printFrame
299         runModal,
300         0, // didCompleteRubberBandForMainFrame
301         0, // saveDataToFileInDownloadsFolder
302         0, // shouldInterruptJavaScript
303         0, // createNewPage_deprecatedForUseWithV1
304         0, // mouseDidMoveOverElement
305         0, // decidePolicyForNotificationPermissionRequest
306         0, // unavailablePluginButtonClicked_deprecatedForUseWithV1
307         0, // showColorPicker
308         0, // hideColorPicker
309         0, // unavailablePluginButtonClicked
310         0, // pinnedStateDidChange
311         0, // didBeginTrackingPotentialLongMousePress
312         0, // didRecognizeLongMousePress
313         0, // didCancelTrackingPotentialLongMousePress
314         0, // isPlayingAudioDidChange
315         decidePolicyForUserMediaPermissionRequest,
316         0, // didClickAutofillButton
317         0, // runJavaScriptAlert
318         0, // runJavaScriptConfirm
319         0, // runJavaScriptPrompt
320         0, // mediaSessionMetadataDidChange
321         createOtherPage,
322         0, // runJavaScriptAlert
323         0, // runJavaScriptConfirm
324         0, // runJavaScriptPrompt
325         checkUserMediaPermissionForOrigin,
326         0, // runBeforeUnloadConfirmPanel
327         0, // fullscreenMayReturnToInline
328         requestPointerLock,
329         0,
330     };
331     WKPageSetPageUIClient(newPage, &otherPageUIClient.base);
332     
333     WKPageNavigationClientV3 pageNavigationClient = {
334         { 3, &TestController::singleton() },
335         decidePolicyForNavigationAction,
336         decidePolicyForNavigationResponse,
337         decidePolicyForPluginLoad,
338         0, // didStartProvisionalNavigation
339         didReceiveServerRedirectForProvisionalNavigation,
340         0, // didFailProvisionalNavigation
341         0, // didCommitNavigation
342         0, // didFinishNavigation
343         0, // didFailNavigation
344         0, // didFailProvisionalLoadInSubframe
345         0, // didFinishDocumentLoad
346         0, // didSameDocumentNavigation
347         0, // renderingProgressDidChange
348         canAuthenticateAgainstProtectionSpace,
349         didReceiveAuthenticationChallenge,
350         processDidCrash,
351         copyWebCryptoMasterKey,
352         didBeginNavigationGesture,
353         willEndNavigationGesture,
354         didEndNavigationGesture,
355         didRemoveNavigationGestureSnapshot,
356         0, // webProcessDidTerminate
357         0, // contentRuleListNotification
358         copySignedPublicKeyAndChallengeString
359     };
360     WKPageSetPageNavigationClient(newPage, &pageNavigationClient.base);
361
362     view->didInitializeClients();
363
364     TestController::singleton().updateWindowScaleForTest(view, *TestController::singleton().m_currentInvocation);
365
366     WKRetain(newPage);
367     return newPage;
368 }
369
370 const char* TestController::libraryPathForTesting()
371 {
372     // FIXME: This may not be sufficient to prevent interactions/crashes
373     // when running more than one copy of DumpRenderTree.
374     // See https://bugs.webkit.org/show_bug.cgi?id=10906
375     char* dumpRenderTreeTemp = getenv("DUMPRENDERTREE_TEMP");
376     if (dumpRenderTreeTemp)
377         return dumpRenderTreeTemp;
378     return platformLibraryPathForTesting();
379 }
380
381 void TestController::initialize(int argc, const char* argv[])
382 {
383     JSC::initializeThreading();
384     RunLoop::initializeMainRunLoop();
385     WTF::setProcessPrivileges(allPrivileges());
386
387     platformInitialize();
388
389     Options options;
390     OptionsHandler optionsHandler(options);
391
392     if (argc < 2) {
393         optionsHandler.printHelp();
394         exit(1);
395     }
396     if (!optionsHandler.parse(argc, argv))
397         exit(1);
398
399     m_useWaitToDumpWatchdogTimer = options.useWaitToDumpWatchdogTimer;
400     m_forceNoTimeout = options.forceNoTimeout;
401     m_verbose = options.verbose;
402     m_gcBetweenTests = options.gcBetweenTests;
403     m_shouldDumpPixelsForAllTests = options.shouldDumpPixelsForAllTests;
404     m_forceComplexText = options.forceComplexText;
405     m_shouldUseAcceleratedDrawing = options.shouldUseAcceleratedDrawing;
406     m_shouldUseRemoteLayerTree = options.shouldUseRemoteLayerTree;
407     m_paths = options.paths;
408     m_allowedHosts = options.allowedHosts;
409     m_shouldShowWebView = options.shouldShowWebView;
410     m_shouldShowTouches = options.shouldShowTouches;
411     m_checkForWorldLeaks = options.checkForWorldLeaks;
412     m_allowAnyHTTPSCertificateForAllowedHosts = options.allowAnyHTTPSCertificateForAllowedHosts;
413
414     if (options.printSupportedFeatures) {
415         // FIXME: On Windows, DumpRenderTree uses this to expose whether it supports 3d
416         // transforms and accelerated compositing. When we support those features, we
417         // should match DRT's behavior.
418         exit(0);
419     }
420
421     m_usingServerMode = (m_paths.size() == 1 && m_paths[0] == "-");
422     if (m_usingServerMode)
423         m_printSeparators = true;
424     else
425         m_printSeparators = m_paths.size() > 1;
426
427     initializeInjectedBundlePath();
428     initializeTestPluginDirectory();
429
430 #if PLATFORM(MAC)
431     WebCoreTestSupport::installMockGamepadProvider();
432 #endif
433
434     WKRetainPtr<WKStringRef> pageGroupIdentifier(AdoptWK, WKStringCreateWithUTF8CString("WebKitTestRunnerPageGroup"));
435     m_pageGroup.adopt(WKPageGroupCreateWithIdentifier(pageGroupIdentifier.get()));
436 }
437
438 WKRetainPtr<WKContextConfigurationRef> TestController::generateContextConfiguration() const
439 {
440     auto configuration = adoptWK(WKContextConfigurationCreate());
441     WKContextConfigurationSetInjectedBundlePath(configuration.get(), injectedBundlePath());
442     WKContextConfigurationSetFullySynchronousModeIsAllowedForTesting(configuration.get(), true);
443
444     if (const char* dumpRenderTreeTemp = libraryPathForTesting()) {
445         String temporaryFolder = String::fromUTF8(dumpRenderTreeTemp);
446
447         const char separator = '/';
448
449         WKContextConfigurationSetApplicationCacheDirectory(configuration.get(), toWK(temporaryFolder + separator + "ApplicationCache").get());
450         WKContextConfigurationSetDiskCacheDirectory(configuration.get(), toWK(temporaryFolder + separator + "Cache").get());
451         WKContextConfigurationSetIndexedDBDatabaseDirectory(configuration.get(), toWK(temporaryFolder + separator + "Databases" + separator + "IndexedDB").get());
452         WKContextConfigurationSetLocalStorageDirectory(configuration.get(), toWK(temporaryFolder + separator + "LocalStorage").get());
453         WKContextConfigurationSetWebSQLDatabaseDirectory(configuration.get(), toWK(temporaryFolder + separator + "Databases" + separator + "WebSQL").get());
454         WKContextConfigurationSetMediaKeysStorageDirectory(configuration.get(), toWK(temporaryFolder + separator + "MediaKeys").get());
455         WKContextConfigurationSetResourceLoadStatisticsDirectory(configuration.get(), toWK(temporaryFolder + separator + "ResourceLoadStatistics").get());
456     }
457     return configuration;
458 }
459
460 WKRetainPtr<WKPageConfigurationRef> TestController::generatePageConfiguration(WKContextConfigurationRef configuration)
461 {
462     m_context = platformAdjustContext(adoptWK(WKContextCreateWithConfiguration(configuration)).get(), configuration);
463
464     m_geolocationProvider = std::make_unique<GeolocationProviderMock>(m_context.get());
465
466     if (const char* dumpRenderTreeTemp = libraryPathForTesting()) {
467         String temporaryFolder = String::fromUTF8(dumpRenderTreeTemp);
468
469         // FIXME: This should be migrated to WKContextConfigurationRef.
470         // Disable icon database to avoid fetching <http://127.0.0.1:8000/favicon.ico> and making tests flaky.
471         // Invividual tests can enable it using testRunner.setIconDatabaseEnabled, although it's not currently supported in WebKitTestRunner.
472         WKContextSetIconDatabasePath(m_context.get(), toWK(emptyString()).get());
473     }
474
475     WKContextSetDiskCacheSpeculativeValidationEnabled(m_context.get(), true);
476     WKContextUseTestingNetworkSession(m_context.get());
477     WKContextSetCacheModel(m_context.get(), kWKCacheModelDocumentBrowser);
478
479     platformInitializeContext();
480
481     WKContextInjectedBundleClientV1 injectedBundleClient = {
482         { 1, this },
483         didReceiveMessageFromInjectedBundle,
484         didReceiveSynchronousMessageFromInjectedBundle,
485         getInjectedBundleInitializationUserData,
486     };
487     WKContextSetInjectedBundleClient(m_context.get(), &injectedBundleClient.base);
488
489     WKContextClientV2 contextClient = {
490         { 2, this },
491         0, // plugInAutoStartOriginHashesChanged
492         networkProcessDidCrash,
493         0, // plugInInformationBecameAvailable
494         0, // copyWebCryptoMasterKey
495     };
496     WKContextSetClient(m_context.get(), &contextClient.base);
497
498     WKContextHistoryClientV0 historyClient = {
499         { 0, this },
500         didNavigateWithNavigationData,
501         didPerformClientRedirect,
502         didPerformServerRedirect,
503         didUpdateHistoryTitle,
504         0, // populateVisitedLinks
505     };
506     WKContextSetHistoryClient(m_context.get(), &historyClient.base);
507
508     WKNotificationManagerRef notificationManager = WKContextGetNotificationManager(m_context.get());
509     WKNotificationProviderV0 notificationKit = m_webNotificationProvider.provider();
510     WKNotificationManagerSetProvider(notificationManager, &notificationKit.base);
511
512     if (testPluginDirectory())
513         WKContextSetAdditionalPluginsDirectory(m_context.get(), testPluginDirectory());
514
515     if (m_forceComplexText)
516         WKContextSetAlwaysUsesComplexTextCodePath(m_context.get(), true);
517
518     auto pageConfiguration = adoptWK(WKPageConfigurationCreate());
519     WKPageConfigurationSetContext(pageConfiguration.get(), m_context.get());
520     WKPageConfigurationSetPageGroup(pageConfiguration.get(), m_pageGroup.get());
521     WKPageConfigurationSetUserContentController(pageConfiguration.get(), adoptWK(WKUserContentControllerCreate()).get());
522     return pageConfiguration;
523 }
524
525 void TestController::createWebViewWithOptions(const TestOptions& options)
526 {
527     auto contextConfiguration = generateContextConfiguration();
528
529     WKRetainPtr<WKMutableArrayRef> overrideLanguages = adoptWK(WKMutableArrayCreate());
530     for (auto& language : options.overrideLanguages)
531         WKArrayAppendItem(overrideLanguages.get(), adoptWK(WKStringCreateWithUTF8CString(language.utf8().data())).get());
532     WKContextConfigurationSetOverrideLanguages(contextConfiguration.get(), overrideLanguages.get());
533
534     if (options.shouldEnableProcessSwapOnNavigation()) {
535         WKContextConfigurationSetProcessSwapsOnNavigation(contextConfiguration.get(), true);
536         if (options.enableProcessSwapOnWindowOpen)
537             WKContextConfigurationSetProcessSwapsOnWindowOpenWithOpener(contextConfiguration.get(), true);
538     }
539
540     auto configuration = generatePageConfiguration(contextConfiguration.get());
541
542     // Some preferences (notably mock scroll bars setting) currently cannot be re-applied to an existing view, so we need to set them now.
543     // FIXME: Migrate these preferences to WKContextConfigurationRef.
544     resetPreferencesToConsistentValues(options);
545
546     platformCreateWebView(configuration.get(), options);
547     WKPageUIClientV8 pageUIClient = {
548         { 8, m_mainWebView.get() },
549         0, // createNewPage_deprecatedForUseWithV0
550         0, // showPage
551         0, // close
552         0, // takeFocus
553         focus,
554         unfocus,
555         0, // runJavaScriptAlert_deprecatedForUseWithV0
556         0, // runJavaScriptAlert_deprecatedForUseWithV0
557         0, // runJavaScriptAlert_deprecatedForUseWithV0
558         0, // setStatusText
559         0, // mouseDidMoveOverElement_deprecatedForUseWithV0
560         0, // missingPluginButtonClicked
561         0, // didNotHandleKeyEvent
562         0, // didNotHandleWheelEvent
563         0, // toolbarsAreVisible
564         0, // setToolbarsAreVisible
565         0, // menuBarIsVisible
566         0, // setMenuBarIsVisible
567         0, // statusBarIsVisible
568         0, // setStatusBarIsVisible
569         0, // isResizable
570         0, // setIsResizable
571         getWindowFrame,
572         setWindowFrame,
573         runBeforeUnloadConfirmPanel,
574         0, // didDraw
575         0, // pageDidScroll
576         0, // exceededDatabaseQuota,
577         runOpenPanel,
578         decidePolicyForGeolocationPermissionRequest,
579         0, // headerHeight
580         0, // footerHeight
581         0, // drawHeader
582         0, // drawFooter
583         0, // printFrame
584         runModal,
585         0, // didCompleteRubberBandForMainFrame
586         0, // saveDataToFileInDownloadsFolder
587         0, // shouldInterruptJavaScript
588         0, // createNewPage_deprecatedForUseWithV1
589         0, // mouseDidMoveOverElement
590         decidePolicyForNotificationPermissionRequest, // decidePolicyForNotificationPermissionRequest
591         0, // unavailablePluginButtonClicked_deprecatedForUseWithV1
592         0, // showColorPicker
593         0, // hideColorPicker
594         unavailablePluginButtonClicked,
595         0, // pinnedStateDidChange
596         0, // didBeginTrackingPotentialLongMousePress
597         0, // didRecognizeLongMousePress
598         0, // didCancelTrackingPotentialLongMousePress
599         0, // isPlayingAudioDidChange
600         decidePolicyForUserMediaPermissionRequest,
601         0, // didClickAutofillButton
602         0, // runJavaScriptAlert
603         0, // runJavaScriptConfirm
604         0, // runJavaScriptPrompt
605         0, // mediaSessionMetadataDidChange
606         createOtherPage,
607         0, // runJavaScriptAlert
608         0, // runJavaScriptConfirm
609         0, // runJavaScriptPrompt
610         checkUserMediaPermissionForOrigin,
611         0, // runBeforeUnloadConfirmPanel
612         0, // fullscreenMayReturnToInline
613         requestPointerLock,
614         0,
615     };
616     WKPageSetPageUIClient(m_mainWebView->page(), &pageUIClient.base);
617
618     WKPageNavigationClientV3 pageNavigationClient = {
619         { 3, this },
620         decidePolicyForNavigationAction,
621         decidePolicyForNavigationResponse,
622         decidePolicyForPluginLoad,
623         0, // didStartProvisionalNavigation
624         didReceiveServerRedirectForProvisionalNavigation,
625         0, // didFailProvisionalNavigation
626         didCommitNavigation,
627         didFinishNavigation,
628         0, // didFailNavigation
629         0, // didFailProvisionalLoadInSubframe
630         0, // didFinishDocumentLoad
631         0, // didSameDocumentNavigation
632         0, // renderingProgressDidChange
633         canAuthenticateAgainstProtectionSpace,
634         didReceiveAuthenticationChallenge,
635         processDidCrash,
636         copyWebCryptoMasterKey,
637         didBeginNavigationGesture,
638         willEndNavigationGesture,
639         didEndNavigationGesture,
640         didRemoveNavigationGestureSnapshot,
641         0, // webProcessDidTerminate
642         0, // contentRuleListNotification
643         copySignedPublicKeyAndChallengeString
644     };
645     WKPageSetPageNavigationClient(m_mainWebView->page(), &pageNavigationClient.base);
646
647     WKContextDownloadClientV1 downloadClient = {
648         { 1, this },
649         downloadDidStart,
650         0, // didReceiveAuthenticationChallenge
651         0, // didReceiveResponse
652         0, // didReceiveData
653         0, // shouldDecodeSourceDataOfMIMEType
654         decideDestinationWithSuggestedFilename,
655         0, // didCreateDestination
656         downloadDidFinish,
657         downloadDidFail,
658         downloadDidCancel,
659         0, // processDidCrash;
660         downloadDidReceiveServerRedirectToURL
661     };
662     WKContextSetDownloadClient(context(), &downloadClient.base);
663     
664     // this should just be done on the page?
665     WKPageInjectedBundleClientV0 injectedBundleClient = {
666         { 0, this },
667         didReceivePageMessageFromInjectedBundle,
668         didReceiveSynchronousPageMessageFromInjectedBundle
669     };
670     WKPageSetPageInjectedBundleClient(m_mainWebView->page(), &injectedBundleClient.base);
671
672     m_mainWebView->didInitializeClients();
673
674     // Generally, the tests should default to running at 1x. updateWindowScaleForTest() will adjust the scale to
675     // something else for specific tests that need to run at a different window scale.
676     m_mainWebView->changeWindowScaleIfNeeded(1);
677 }
678
679 void TestController::ensureViewSupportsOptionsForTest(const TestInvocation& test)
680 {
681     auto options = test.options();
682
683     if (m_mainWebView) {
684         if (m_mainWebView->viewSupportsOptions(options))
685             return;
686
687         willDestroyWebView();
688
689         WKPageSetPageUIClient(m_mainWebView->page(), nullptr);
690         WKPageSetPageNavigationClient(m_mainWebView->page(), nullptr);
691         WKPageClose(m_mainWebView->page());
692
693         m_mainWebView = nullptr;
694     }
695
696     createWebViewWithOptions(options);
697
698     if (!resetStateToConsistentValues(options, ResetStage::BeforeTest))
699         TestInvocation::dumpWebProcessUnresponsiveness("<unknown> - TestController::run - Failed to reset state to consistent values\n");
700 }
701
702 void TestController::resetPreferencesToConsistentValues(const TestOptions& options)
703 {
704     // Reset preferences
705     WKPreferencesRef preferences = platformPreferences();
706     WKPreferencesResetTestRunnerOverrides(preferences);
707
708     WKPreferencesEnableAllExperimentalFeatures(preferences);
709     for (const auto& experimentalFeature : options.experimentalFeatures)
710         WKPreferencesSetExperimentalFeatureForKey(preferences, experimentalFeature.value, toWK(experimentalFeature.key).get());
711
712     WKPreferencesResetAllInternalDebugFeatures(preferences);
713     for (const auto& internalDebugFeature : options.internalDebugFeatures)
714         WKPreferencesSetInternalDebugFeatureForKey(preferences, internalDebugFeature.value, toWK(internalDebugFeature.key).get());
715
716     WKPreferencesSetProcessSwapOnNavigationEnabled(preferences, options.shouldEnableProcessSwapOnNavigation());
717     WKPreferencesSetPageVisibilityBasedProcessSuppressionEnabled(preferences, false);
718     WKPreferencesSetOfflineWebApplicationCacheEnabled(preferences, true);
719     WKPreferencesSetFontSmoothingLevel(preferences, kWKFontSmoothingLevelNoSubpixelAntiAliasing);
720     WKPreferencesSetSubpixelAntialiasedLayerTextEnabled(preferences, false);
721     WKPreferencesSetXSSAuditorEnabled(preferences, false);
722     WKPreferencesSetWebAudioEnabled(preferences, true);
723     WKPreferencesSetMediaDevicesEnabled(preferences, true);
724     WKPreferencesSetWebRTCMDNSICECandidatesEnabled(preferences, false);
725     WKPreferencesSetDeveloperExtrasEnabled(preferences, true);
726     WKPreferencesSetJavaScriptRuntimeFlags(preferences, kWKJavaScriptRuntimeFlagsAllEnabled);
727     WKPreferencesSetJavaScriptCanOpenWindowsAutomatically(preferences, true);
728     WKPreferencesSetJavaScriptCanAccessClipboard(preferences, true);
729     WKPreferencesSetDOMPasteAllowed(preferences, true);
730     WKPreferencesSetUniversalAccessFromFileURLsAllowed(preferences, true);
731     WKPreferencesSetFileAccessFromFileURLsAllowed(preferences, true);
732 #if ENABLE(FULLSCREEN_API)
733     WKPreferencesSetFullScreenEnabled(preferences, true);
734 #endif
735     WKPreferencesSetPageCacheEnabled(preferences, false);
736     WKPreferencesSetAsynchronousPluginInitializationEnabled(preferences, false);
737     WKPreferencesSetAsynchronousPluginInitializationEnabledForAllPlugins(preferences, false);
738     WKPreferencesSetArtificialPluginInitializationDelayEnabled(preferences, false);
739     WKPreferencesSetTabToLinksEnabled(preferences, false);
740     WKPreferencesSetInteractiveFormValidationEnabled(preferences, true);
741     WKPreferencesSetDataTransferItemsEnabled(preferences, true);
742     WKPreferencesSetCustomPasteboardDataEnabled(preferences, true);
743
744     WKPreferencesSetMockScrollbarsEnabled(preferences, options.useMockScrollbars);
745     WKPreferencesSetNeedsSiteSpecificQuirks(preferences, options.needsSiteSpecificQuirks);
746     WKPreferencesSetAttachmentElementEnabled(preferences, options.enableAttachmentElement);
747     WKPreferencesSetMenuItemElementEnabled(preferences, options.enableMenuItemElement);
748     WKPreferencesSetModernMediaControlsEnabled(preferences, options.enableModernMediaControls);
749     WKPreferencesSetWebAuthenticationEnabled(preferences, options.enableWebAuthentication);
750     WKPreferencesSetIsSecureContextAttributeEnabled(preferences, options.enableIsSecureContextAttribute);
751     WKPreferencesSetAllowCrossOriginSubresourcesToAskForCredentials(preferences, options.allowCrossOriginSubresourcesToAskForCredentials);
752     WKPreferencesSetColorFilterEnabled(preferences, options.enableColorFilter);
753     WKPreferencesSetPunchOutWhiteBackgroundsInDarkMode(preferences, options.punchOutWhiteBackgroundsInDarkMode);
754
755     static WKStringRef defaultTextEncoding = WKStringCreateWithUTF8CString("ISO-8859-1");
756     WKPreferencesSetDefaultTextEncodingName(preferences, defaultTextEncoding);
757
758     static WKStringRef standardFontFamily = WKStringCreateWithUTF8CString("Times");
759     static WKStringRef cursiveFontFamily = WKStringCreateWithUTF8CString("Apple Chancery");
760     static WKStringRef fantasyFontFamily = WKStringCreateWithUTF8CString("Papyrus");
761     static WKStringRef fixedFontFamily = WKStringCreateWithUTF8CString("Courier");
762     static WKStringRef pictographFontFamily = WKStringCreateWithUTF8CString("Apple Color Emoji");
763     static WKStringRef sansSerifFontFamily = WKStringCreateWithUTF8CString("Helvetica");
764     static WKStringRef serifFontFamily = WKStringCreateWithUTF8CString("Times");
765
766     WKPreferencesSetMinimumFontSize(preferences, 0);
767     WKPreferencesSetStandardFontFamily(preferences, standardFontFamily);
768     WKPreferencesSetCursiveFontFamily(preferences, cursiveFontFamily);
769     WKPreferencesSetFantasyFontFamily(preferences, fantasyFontFamily);
770     WKPreferencesSetFixedFontFamily(preferences, fixedFontFamily);
771     WKPreferencesSetPictographFontFamily(preferences, pictographFontFamily);
772     WKPreferencesSetSansSerifFontFamily(preferences, sansSerifFontFamily);
773     WKPreferencesSetSerifFontFamily(preferences, serifFontFamily);
774     WKPreferencesSetAsynchronousSpellCheckingEnabled(preferences, false);
775 #if ENABLE(MEDIA_SOURCE)
776     WKPreferencesSetMediaSourceEnabled(preferences, true);
777     WKPreferencesSetSourceBufferChangeTypeEnabled(preferences, true);
778 #endif
779
780     WKPreferencesSetHiddenPageDOMTimerThrottlingEnabled(preferences, false);
781     WKPreferencesSetHiddenPageCSSAnimationSuspensionEnabled(preferences, false);
782
783     WKPreferencesSetAcceleratedDrawingEnabled(preferences, m_shouldUseAcceleratedDrawing || options.useAcceleratedDrawing);
784     // FIXME: We should be testing the default.
785     WKPreferencesSetStorageBlockingPolicy(preferences, kWKAllowAllStorage);
786
787     WKPreferencesSetFetchAPIKeepAliveEnabled(preferences, true);
788     WKPreferencesSetResourceTimingEnabled(preferences, true);
789     WKPreferencesSetUserTimingEnabled(preferences, true);
790     WKPreferencesSetMediaPreloadingEnabled(preferences, true);
791     WKPreferencesSetMediaPlaybackAllowsInline(preferences, true);
792     WKPreferencesSetInlineMediaPlaybackRequiresPlaysInlineAttribute(preferences, false);
793     WKPreferencesSetBeaconAPIEnabled(preferences, true);
794     WKPreferencesSetDirectoryUploadEnabled(preferences, true);
795
796     WKCookieManagerDeleteAllCookies(WKContextGetCookieManager(m_context.get()));
797
798     WKPreferencesSetMockCaptureDevicesEnabled(preferences, true);
799     
800     WKPreferencesSetLargeImageAsyncDecodingEnabled(preferences, false);
801
802     WKPreferencesSetInspectorAdditionsEnabled(preferences, options.enableInspectorAdditions);
803
804     WKPreferencesSetStorageAccessAPIEnabled(preferences, true);
805     
806     WKPreferencesSetAccessibilityObjectModelEnabled(preferences, true);
807     WKPreferencesSetAriaReflectionEnabled(preferences, true);
808     WKPreferencesSetCSSOMViewScrollingAPIEnabled(preferences, true);
809     WKPreferencesSetMediaCapabilitiesEnabled(preferences, true);
810
811     WKPreferencesSetRestrictedHTTPResponseAccess(preferences, true);
812
813     WKPreferencesSetServerTimingEnabled(preferences, true);
814
815     platformResetPreferencesToConsistentValues();
816 }
817
818 bool TestController::resetStateToConsistentValues(const TestOptions& options, ResetStage resetStage)
819 {
820     SetForScope<State> changeState(m_state, Resetting);
821     m_beforeUnloadReturnValue = true;
822
823     // This setting differs between the antique and modern Mac WebKit2 API.
824     // For now, maintain the antique behavior, because some tests depend on it!
825     // FIXME: We should be testing the default.
826     WKPageSetBackgroundExtendsBeyondPage(m_mainWebView->page(), false);
827
828     WKPageSetCustomUserAgent(m_mainWebView->page(), nullptr);
829
830     WKRetainPtr<WKStringRef> messageName = adoptWK(WKStringCreateWithUTF8CString("Reset"));
831     WKRetainPtr<WKMutableDictionaryRef> resetMessageBody = adoptWK(WKMutableDictionaryCreate());
832
833     WKRetainPtr<WKStringRef> shouldGCKey = adoptWK(WKStringCreateWithUTF8CString("ShouldGC"));
834     WKRetainPtr<WKBooleanRef> shouldGCValue = adoptWK(WKBooleanCreate(m_gcBetweenTests));
835     WKDictionarySetItem(resetMessageBody.get(), shouldGCKey.get(), shouldGCValue.get());
836
837     WKRetainPtr<WKStringRef> allowedHostsKey = adoptWK(WKStringCreateWithUTF8CString("AllowedHosts"));
838     WKRetainPtr<WKMutableArrayRef> allowedHostsValue = adoptWK(WKMutableArrayCreate());
839     for (auto& host : m_allowedHosts) {
840         WKRetainPtr<WKStringRef> wkHost = adoptWK(WKStringCreateWithUTF8CString(host.c_str()));
841         WKArrayAppendItem(allowedHostsValue.get(), wkHost.get());
842     }
843     WKDictionarySetItem(resetMessageBody.get(), allowedHostsKey.get(), allowedHostsValue.get());
844
845     if (options.jscOptions.length()) {
846         WKRetainPtr<WKStringRef> jscOptionsKey = adoptWK(WKStringCreateWithUTF8CString("JSCOptions"));
847         WKRetainPtr<WKStringRef> jscOptionsValue = adoptWK(WKStringCreateWithUTF8CString(options.jscOptions.c_str()));
848         WKDictionarySetItem(resetMessageBody.get(), jscOptionsKey.get(), jscOptionsValue.get());
849     }
850
851     WKPagePostMessageToInjectedBundle(TestController::singleton().mainWebView()->page(), messageName.get(), resetMessageBody.get());
852
853     WKContextSetShouldUseFontSmoothing(TestController::singleton().context(), false);
854
855     WKContextSetCacheModel(TestController::singleton().context(), kWKCacheModelDocumentBrowser);
856
857     WKContextClearCachedCredentials(TestController::singleton().context());
858
859     clearServiceWorkerRegistrations();
860     clearDOMCaches();
861
862     WKContextSetAllowsAnySSLCertificateForServiceWorkerTesting(platformContext(), true);
863
864     // FIXME: This function should also ensure that there is only one page open.
865
866     // Reset the EventSender for each test.
867     m_eventSenderProxy = std::make_unique<EventSenderProxy>(this);
868
869     // FIXME: Is this needed? Nothing in TestController changes preferences during tests, and if there is
870     // some other code doing this, it should probably be responsible for cleanup too.
871     resetPreferencesToConsistentValues(options);
872
873 #if !PLATFORM(COCOA) && !PLATFORM(WPE)
874     WKTextCheckerContinuousSpellCheckingEnabledStateChanged(true);
875 #endif
876
877     // Make sure the view is in the window (a test can unparent it).
878     m_mainWebView->addToWindow();
879
880     // In the case that a test using the chrome input field failed, be sure to clean up for the next test.
881     m_mainWebView->removeChromeInputField();
882     m_mainWebView->focus();
883
884     // Re-set to the default backing scale factor by setting the custom scale factor to 0.
885     WKPageSetCustomBackingScaleFactor(m_mainWebView->page(), 0);
886
887     WKPageClearWheelEventTestTrigger(m_mainWebView->page());
888
889     WKPageSetMuted(m_mainWebView->page(), true);
890
891     WKPageClearUserMediaState(m_mainWebView->page());
892
893     // Reset notification permissions
894     m_webNotificationProvider.reset();
895
896     // Reset Geolocation permissions.
897     m_geolocationPermissionRequests.clear();
898     m_isGeolocationPermissionSet = false;
899     m_isGeolocationPermissionAllowed = false;
900
901     // Reset UserMedia permissions.
902     m_userMediaPermissionRequests.clear();
903     m_cachedUserMediaPermissions.clear();
904     setUserMediaPermission(true);
905
906     // Reset Custom Policy Delegate.
907     setCustomPolicyDelegate(false, false);
908
909     m_shouldDownloadUndisplayableMIMETypes = false;
910
911     m_workQueueManager.clearWorkQueue();
912
913     m_rejectsProtectionSpaceAndContinueForAuthenticationChallenges = false;
914     m_handlesAuthenticationChallenges = false;
915     m_authenticationUsername = String();
916     m_authenticationPassword = String();
917
918     setBlockAllPlugins(false);
919     setPluginSupportedMode({ });
920
921     m_shouldLogDownloadCallbacks = false;
922     m_shouldLogHistoryClientCallbacks = false;
923     m_shouldLogCanAuthenticateAgainstProtectionSpace = false;
924
925     setHidden(false);
926
927     platformResetStateToConsistentValues();
928
929     m_shouldDecideNavigationPolicyAfterDelay = false;
930     m_shouldDecideResponsePolicyAfterDelay = false;
931
932     setNavigationGesturesEnabled(false);
933     
934     setIgnoresViewportScaleLimits(options.ignoresViewportScaleLimits);
935
936     m_openPanelFileURLs = nullptr;
937     
938     statisticsResetToConsistentState();
939
940     m_didReceiveServerRedirectForProvisionalNavigation = false;
941
942     // Reset main page back to about:blank
943     m_doneResetting = false;
944     WKPageLoadURL(m_mainWebView->page(), blankURL());
945     runUntil(m_doneResetting, m_currentInvocation->shortTimeout());
946     if (!m_doneResetting)
947         return false;
948     
949     if (resetStage == ResetStage::AfterTest)
950         updateLiveDocumentsAfterTest();
951
952     return m_doneResetting;
953 }
954
955 void TestController::updateLiveDocumentsAfterTest()
956 {
957     if (!m_checkForWorldLeaks)
958         return;
959
960     AsyncTask([]() {
961         // After each test, we update the list of live documents so that we can detect when an abandoned document first showed up.
962         WKRetainPtr<WKStringRef> messageName = adoptWK(WKStringCreateWithUTF8CString("GetLiveDocuments"));
963         WKPagePostMessageToInjectedBundle(TestController::singleton().mainWebView()->page(), messageName.get(), nullptr);
964     }, 5_s).run();
965 }
966
967 void TestController::checkForWorldLeaks()
968 {
969     if (!m_checkForWorldLeaks || !TestController::singleton().mainWebView())
970         return;
971
972     AsyncTask([]() {
973         // This runs at the end of a series of tests. It clears caches, runs a GC and then fetches the list of documents.
974         WKRetainPtr<WKStringRef> messageName = adoptWK(WKStringCreateWithUTF8CString("CheckForWorldLeaks"));
975         WKPagePostMessageToInjectedBundle(TestController::singleton().mainWebView()->page(), messageName.get(), nullptr);
976     }, 20_s).run();
977 }
978
979 void TestController::findAndDumpWorldLeaks()
980 {
981     if (!m_checkForWorldLeaks)
982         return;
983
984     checkForWorldLeaks();
985
986     StringBuilder builder;
987     
988     if (m_abandonedDocumentInfo.size()) {
989         for (const auto& it : m_abandonedDocumentInfo) {
990             auto documentURL = it.value.abandonedDocumentURL;
991             if (documentURL.isEmpty())
992                 documentURL = "(no url)";
993             builder.append("TEST: ");
994             builder.append(it.value.testURL);
995             builder.append('\n');
996             builder.append("ABANDONED DOCUMENT: ");
997             builder.append(documentURL);
998             builder.append('\n');
999         }
1000     } else
1001         builder.append("no abandoned documents");
1002
1003     String result = builder.toString();
1004     printf("Content-Type: text/plain\n");
1005     printf("Content-Length: %u\n", result.length());
1006     fwrite(result.utf8().data(), 1, result.length(), stdout);
1007     printf("#EOF\n");
1008     fprintf(stderr, "#EOF\n");
1009     fflush(stdout);
1010     fflush(stderr);
1011 }
1012
1013 void TestController::willDestroyWebView()
1014 {
1015     // Before we kill the web view, look for abandoned documents before that web process goes away.
1016     checkForWorldLeaks();
1017 }
1018
1019 void TestController::terminateWebContentProcess()
1020 {
1021     WKPageTerminate(m_mainWebView->page());
1022 }
1023
1024 void TestController::reattachPageToWebProcess()
1025 {
1026     // Loading a web page is the only way to reattach an existing page to a process.
1027     SetForScope<State> changeState(m_state, Resetting);
1028     m_doneResetting = false;
1029     WKPageLoadURL(m_mainWebView->page(), blankURL());
1030     runUntil(m_doneResetting, noTimeout);
1031 }
1032
1033 const char* TestController::webProcessName()
1034 {
1035     // FIXME: Find a way to not hardcode the process name.
1036 #if PLATFORM(IOS) && !PLATFORM(IOS_SIMULATOR)
1037     return "com.apple.WebKit.WebContent";
1038 #elif PLATFORM(COCOA)
1039     return "com.apple.WebKit.WebContent.Development";
1040 #elif PLATFORM(GTK)
1041     return "WebKitWebProcess";
1042 #elif PLATFORM(WPE)
1043     return "WPEWebProcess";
1044 #else
1045     return "WebProcess";
1046 #endif
1047 }
1048
1049 const char* TestController::networkProcessName()
1050 {
1051     // FIXME: Find a way to not hardcode the process name.
1052 #if PLATFORM(IOS) && !PLATFORM(IOS_SIMULATOR)
1053     return "com.apple.WebKit.Networking";
1054 #elif PLATFORM(COCOA)
1055     return "com.apple.WebKit.Networking.Development";
1056 #elif PLATFORM(GTK)
1057     return "WebKitNetworkProcess";
1058 #elif PLATFORM(WPE)
1059     return "WPENetworkProcess";
1060 #else
1061     return "NetworkProcess";
1062 #endif
1063 }
1064
1065 void TestController::setAllowsAnySSLCertificate(bool allows)
1066 {
1067     WKContextSetAllowsAnySSLCertificateForWebSocketTesting(platformContext(), allows);
1068 }
1069
1070 static std::string testPath(WKURLRef url)
1071 {
1072     auto scheme = adoptWK(WKURLCopyScheme(url));
1073     if (WKStringIsEqualToUTF8CStringIgnoringCase(scheme.get(), "file")) {
1074         auto path = adoptWK(WKURLCopyPath(url));
1075         auto buffer = std::vector<char>(WKStringGetMaximumUTF8CStringSize(path.get()));
1076         auto length = WKStringGetUTF8CString(path.get(), buffer.data(), buffer.size());
1077         return std::string(buffer.data(), length);
1078     }
1079     return std::string();
1080 }
1081
1082 static WKURLRef createTestURL(const char* pathOrURL)
1083 {
1084     if (strstr(pathOrURL, "http://") || strstr(pathOrURL, "https://") || strstr(pathOrURL, "file://"))
1085         return WKURLCreateWithUTF8CString(pathOrURL);
1086
1087     // Creating from filesytem path.
1088     size_t length = strlen(pathOrURL);
1089     if (!length)
1090         return 0;
1091
1092     const char separator = '/';
1093     bool isAbsolutePath = pathOrURL[0] == separator;
1094     const char* filePrefix = "file://";
1095     static const size_t prefixLength = strlen(filePrefix);
1096
1097     std::unique_ptr<char[]> buffer;
1098     if (isAbsolutePath) {
1099         buffer = std::make_unique<char[]>(prefixLength + length + 1);
1100         strcpy(buffer.get(), filePrefix);
1101         strcpy(buffer.get() + prefixLength, pathOrURL);
1102     } else {
1103         buffer = std::make_unique<char[]>(prefixLength + PATH_MAX + length + 2); // 1 for the separator
1104         strcpy(buffer.get(), filePrefix);
1105         if (!getcwd(buffer.get() + prefixLength, PATH_MAX))
1106             return 0;
1107         size_t numCharacters = strlen(buffer.get());
1108         buffer[numCharacters] = separator;
1109         strcpy(buffer.get() + numCharacters + 1, pathOrURL);
1110     }
1111
1112     return WKURLCreateWithUTF8CString(buffer.get());
1113 }
1114
1115 static bool parseBooleanTestHeaderValue(const std::string& value)
1116 {
1117     if (value == "true")
1118         return true;
1119     if (value == "false")
1120         return false;
1121
1122     LOG_ERROR("Found unexpected value '%s' for boolean option. Expected 'true' or 'false'.", value.c_str());
1123     return false;
1124 }
1125
1126 static std::string parseStringTestHeaderValueAsRelativePath(const std::string& value, const std::string& pathOrURL)
1127 {
1128     WKRetainPtr<WKURLRef> baseURL(AdoptWK, createTestURL(pathOrURL.c_str()));
1129     WKRetainPtr<WKURLRef> relativeURL(AdoptWK, WKURLCreateWithBaseURL(baseURL.get(), value.c_str()));
1130     return toSTD(adoptWK(WKURLCopyPath(relativeURL.get())));
1131 }
1132
1133 static void updateTestOptionsFromTestHeader(TestOptions& testOptions, const std::string& pathOrURL, const std::string& absolutePath)
1134 {
1135     std::string filename = absolutePath;
1136     if (filename.empty()) {
1137         // Gross. Need to reduce conversions between all the string types and URLs.
1138         WKRetainPtr<WKURLRef> wkURL(AdoptWK, createTestURL(pathOrURL.c_str()));
1139         filename = testPath(wkURL.get());
1140     }
1141
1142     if (filename.empty())
1143         return;
1144
1145     std::string options;
1146     std::ifstream testFile(filename.data());
1147     if (!testFile.good())
1148         return;
1149     getline(testFile, options);
1150     std::string beginString("webkit-test-runner [ ");
1151     std::string endString(" ]");
1152     size_t beginLocation = options.find(beginString);
1153     if (beginLocation == std::string::npos)
1154         return;
1155     size_t endLocation = options.find(endString, beginLocation);
1156     if (endLocation == std::string::npos) {
1157         LOG_ERROR("Could not find end of test header in %s", filename.c_str());
1158         return;
1159     }
1160     std::string pairString = options.substr(beginLocation + beginString.size(), endLocation - (beginLocation + beginString.size()));
1161     size_t pairStart = 0;
1162     while (pairStart < pairString.size()) {
1163         size_t pairEnd = pairString.find(" ", pairStart);
1164         if (pairEnd == std::string::npos)
1165             pairEnd = pairString.size();
1166         size_t equalsLocation = pairString.find("=", pairStart);
1167         if (equalsLocation == std::string::npos) {
1168             LOG_ERROR("Malformed option in test header (could not find '=' character) in %s", filename.c_str());
1169             break;
1170         }
1171         auto key = pairString.substr(pairStart, equalsLocation - pairStart);
1172         auto value = pairString.substr(equalsLocation + 1, pairEnd - (equalsLocation + 1));
1173
1174         if (!key.rfind("experimental:")) {
1175             key = key.substr(13);
1176             testOptions.experimentalFeatures.add(String(key.c_str()), parseBooleanTestHeaderValue(value));
1177         }
1178
1179         if (!key.rfind("internal:")) {
1180             key = key.substr(9);
1181             testOptions.internalDebugFeatures.add(String(key.c_str()), parseBooleanTestHeaderValue(value));
1182         }
1183
1184         if (key == "language")
1185             testOptions.overrideLanguages = String(value.c_str()).split(',');
1186         else if (key == "useThreadedScrolling")
1187             testOptions.useThreadedScrolling = parseBooleanTestHeaderValue(value);
1188         else if (key == "useAcceleratedDrawing")
1189             testOptions.useAcceleratedDrawing = parseBooleanTestHeaderValue(value);
1190         else if (key == "useFlexibleViewport")
1191             testOptions.useFlexibleViewport = parseBooleanTestHeaderValue(value);
1192         else if (key == "useDataDetection")
1193             testOptions.useDataDetection = parseBooleanTestHeaderValue(value);
1194         else if (key == "useMockScrollbars")
1195             testOptions.useMockScrollbars = parseBooleanTestHeaderValue(value);
1196         else if (key == "needsSiteSpecificQuirks")
1197             testOptions.needsSiteSpecificQuirks = parseBooleanTestHeaderValue(value);
1198         else if (key == "ignoresViewportScaleLimits")
1199             testOptions.ignoresViewportScaleLimits = parseBooleanTestHeaderValue(value);
1200         else if (key == "useCharacterSelectionGranularity")
1201             testOptions.useCharacterSelectionGranularity = parseBooleanTestHeaderValue(value);
1202         else if (key == "enableAttachmentElement")
1203             testOptions.enableAttachmentElement = parseBooleanTestHeaderValue(value);
1204         else if (key == "enableIntersectionObserver")
1205             testOptions.enableIntersectionObserver = parseBooleanTestHeaderValue(value);
1206         else if (key == "enableMenuItemElement")
1207             testOptions.enableMenuItemElement = parseBooleanTestHeaderValue(value);
1208         else if (key == "enableModernMediaControls")
1209             testOptions.enableModernMediaControls = parseBooleanTestHeaderValue(value);
1210         else if (key == "enablePointerLock")
1211             testOptions.enablePointerLock = parseBooleanTestHeaderValue(value);
1212         else if (key == "enableWebAuthentication")
1213             testOptions.enableWebAuthentication = parseBooleanTestHeaderValue(value);
1214         else if (key == "enableIsSecureContextAttribute")
1215             testOptions.enableIsSecureContextAttribute = parseBooleanTestHeaderValue(value);
1216         else if (key == "enableInspectorAdditions")
1217             testOptions.enableInspectorAdditions = parseBooleanTestHeaderValue(value);
1218         else if (key == "dumpJSConsoleLogInStdErr")
1219             testOptions.dumpJSConsoleLogInStdErr = parseBooleanTestHeaderValue(value);
1220         else if (key == "applicationManifest")
1221             testOptions.applicationManifest = parseStringTestHeaderValueAsRelativePath(value, pathOrURL);
1222         else if (key == "allowCrossOriginSubresourcesToAskForCredentials")
1223             testOptions.allowCrossOriginSubresourcesToAskForCredentials = parseBooleanTestHeaderValue(value);
1224         else if (key == "enableProcessSwapOnNavigation")
1225             testOptions.enableProcessSwapOnNavigation = parseBooleanTestHeaderValue(value);
1226         else if (key == "enableProcessSwapOnWindowOpen")
1227             testOptions.enableProcessSwapOnWindowOpen = parseBooleanTestHeaderValue(value);
1228         else if (key == "enableColorFilter")
1229             testOptions.enableColorFilter = parseBooleanTestHeaderValue(value);
1230         else if (key == "punchOutWhiteBackgroundsInDarkMode")
1231             testOptions.punchOutWhiteBackgroundsInDarkMode = parseBooleanTestHeaderValue(value);
1232         else if (key == "jscOptions")
1233             testOptions.jscOptions = value;
1234         else if (key == "runSingly")
1235             testOptions.runSingly = parseBooleanTestHeaderValue(value);
1236         else if (key == "shouldIgnoreMetaViewport")
1237             testOptions.shouldIgnoreMetaViewport = parseBooleanTestHeaderValue(value);
1238         pairStart = pairEnd + 1;
1239     }
1240 }
1241
1242 TestOptions TestController::testOptionsForTest(const TestCommand& command) const
1243 {
1244     TestOptions options(command.pathOrURL);
1245
1246     options.useRemoteLayerTree = m_shouldUseRemoteLayerTree;
1247     options.shouldShowWebView = m_shouldShowWebView;
1248
1249     updatePlatformSpecificTestOptionsForTest(options, command.pathOrURL);
1250     updateTestOptionsFromTestHeader(options, command.pathOrURL, command.absolutePath);
1251     platformAddTestOptions(options);
1252
1253     return options;
1254 }
1255
1256 void TestController::updateWebViewSizeForTest(const TestInvocation& test)
1257 {
1258     unsigned width = viewWidth;
1259     unsigned height = viewHeight;
1260     if (test.options().isSVGTest) {
1261         width = w3cSVGViewWidth;
1262         height = w3cSVGViewHeight;
1263     }
1264
1265     mainWebView()->resizeTo(width, height);
1266 }
1267
1268 void TestController::updateWindowScaleForTest(PlatformWebView* view, const TestInvocation& test)
1269 {
1270     view->changeWindowScaleIfNeeded(test.options().deviceScaleFactor);
1271 }
1272
1273 void TestController::configureViewForTest(const TestInvocation& test)
1274 {
1275     ensureViewSupportsOptionsForTest(test);
1276     updateWebViewSizeForTest(test);
1277     updateWindowScaleForTest(mainWebView(), test);
1278
1279     platformConfigureViewForTest(test);
1280 }
1281
1282 class CommandTokenizer {
1283 public:
1284     explicit CommandTokenizer(const std::string& input)
1285         : m_input(input)
1286         , m_posNextSeparator(0)
1287     {
1288         pump();
1289     }
1290
1291     bool hasNext() const;
1292     std::string next();
1293
1294 private:
1295     void pump();
1296     static const char kSeparator = '\'';
1297     const std::string& m_input;
1298     std::string m_next;
1299     size_t m_posNextSeparator;
1300 };
1301
1302 void CommandTokenizer::pump()
1303 {
1304     if (m_posNextSeparator == std::string::npos || m_posNextSeparator == m_input.size()) {
1305         m_next = std::string();
1306         return;
1307     }
1308     size_t start = m_posNextSeparator ? m_posNextSeparator + 1 : 0;
1309     m_posNextSeparator = m_input.find(kSeparator, start);
1310     size_t size = m_posNextSeparator == std::string::npos ? std::string::npos : m_posNextSeparator - start;
1311     m_next = std::string(m_input, start, size);
1312 }
1313
1314 std::string CommandTokenizer::next()
1315 {
1316     ASSERT(hasNext());
1317
1318     std::string oldNext = m_next;
1319     pump();
1320     return oldNext;
1321 }
1322
1323 bool CommandTokenizer::hasNext() const
1324 {
1325     return !m_next.empty();
1326 }
1327
1328 NO_RETURN static void die(const std::string& inputLine)
1329 {
1330     fprintf(stderr, "Unexpected input line: %s\n", inputLine.c_str());
1331     exit(1);
1332 }
1333
1334 static TestCommand parseInputLine(const std::string& inputLine)
1335 {
1336     TestCommand result;
1337     CommandTokenizer tokenizer(inputLine);
1338     if (!tokenizer.hasNext())
1339         die(inputLine);
1340
1341     std::string arg = tokenizer.next();
1342     result.pathOrURL = arg;
1343     while (tokenizer.hasNext()) {
1344         arg = tokenizer.next();
1345         if (arg == std::string("--timeout")) {
1346             std::string timeoutToken = tokenizer.next();
1347             result.timeout = Seconds::fromMilliseconds(atoi(timeoutToken.c_str()));
1348         } else if (arg == std::string("-p") || arg == std::string("--pixel-test")) {
1349             result.shouldDumpPixels = true;
1350             if (tokenizer.hasNext())
1351                 result.expectedPixelHash = tokenizer.next();
1352         } else if (arg == std::string("--dump-jsconsolelog-in-stderr"))
1353             result.dumpJSConsoleLogInStdErr = true;
1354         else if (arg == std::string("--absolutePath"))
1355             result.absolutePath = tokenizer.next();
1356         else
1357             die(inputLine);
1358     }
1359     return result;
1360 }
1361
1362 bool TestController::runTest(const char* inputLine)
1363 {
1364     AutodrainedPool pool;
1365     
1366     WKTextCheckerSetTestingMode(true);
1367     TestCommand command = parseInputLine(std::string(inputLine));
1368
1369     m_state = RunningTest;
1370     
1371     TestOptions options = testOptionsForTest(command);
1372
1373     WKRetainPtr<WKURLRef> wkURL(AdoptWK, createTestURL(command.pathOrURL.c_str()));
1374     m_currentInvocation = std::make_unique<TestInvocation>(wkURL.get(), options);
1375
1376     if (command.shouldDumpPixels || m_shouldDumpPixelsForAllTests)
1377         m_currentInvocation->setIsPixelTest(command.expectedPixelHash);
1378
1379     if (command.timeout > 0_s)
1380         m_currentInvocation->setCustomTimeout(command.timeout);
1381
1382     m_currentInvocation->setDumpJSConsoleLogInStdErr(command.dumpJSConsoleLogInStdErr || options.dumpJSConsoleLogInStdErr);
1383
1384     platformWillRunTest(*m_currentInvocation);
1385
1386     m_currentInvocation->invoke();
1387     m_currentInvocation = nullptr;
1388
1389     return true;
1390 }
1391
1392 bool TestController::waitForCompletion(const WTF::Function<void ()>& function, WTF::Seconds timeout)
1393 {
1394     m_doneResetting = false;
1395     function();
1396     runUntil(m_doneResetting, timeout);
1397     return !m_doneResetting;
1398 }
1399
1400 bool TestController::handleControlCommand(const char* command)
1401 {
1402     if (!strcmp("#CHECK FOR WORLD LEAKS", command)) {
1403         if (!m_checkForWorldLeaks) {
1404             WTFLogAlways("WebKitTestRunner asked to check for world leaks, but was not run with --world-leaks");
1405             return true;
1406         }
1407         findAndDumpWorldLeaks();
1408         return true;
1409     }
1410     return false;
1411 }
1412
1413 void TestController::runTestingServerLoop()
1414 {
1415     char filenameBuffer[2048];
1416     while (fgets(filenameBuffer, sizeof(filenameBuffer), stdin)) {
1417         char* newLineCharacter = strchr(filenameBuffer, '\n');
1418         if (newLineCharacter)
1419             *newLineCharacter = '\0';
1420
1421         if (strlen(filenameBuffer) == 0)
1422             continue;
1423
1424         if (handleControlCommand(filenameBuffer))
1425             continue;
1426
1427         if (!runTest(filenameBuffer))
1428             break;
1429     }
1430 }
1431
1432 void TestController::run()
1433 {
1434     if (m_usingServerMode)
1435         runTestingServerLoop();
1436     else {
1437         for (size_t i = 0; i < m_paths.size(); ++i) {
1438             if (!runTest(m_paths[i].c_str()))
1439                 break;
1440         }
1441         if (m_checkForWorldLeaks)
1442             findAndDumpWorldLeaks();
1443     }
1444 }
1445
1446 void TestController::runUntil(bool& done, WTF::Seconds timeout)
1447 {
1448     if (m_forceNoTimeout)
1449         timeout = noTimeout;
1450
1451     platformRunUntil(done, timeout);
1452 }
1453
1454 // WKContextInjectedBundleClient
1455
1456 void TestController::didReceiveMessageFromInjectedBundle(WKContextRef context, WKStringRef messageName, WKTypeRef messageBody, const void* clientInfo)
1457 {
1458     static_cast<TestController*>(const_cast<void*>(clientInfo))->didReceiveMessageFromInjectedBundle(messageName, messageBody);
1459 }
1460
1461 void TestController::didReceiveSynchronousMessageFromInjectedBundle(WKContextRef context, WKStringRef messageName, WKTypeRef messageBody, WKTypeRef* returnData, const void* clientInfo)
1462 {
1463     *returnData = static_cast<TestController*>(const_cast<void*>(clientInfo))->didReceiveSynchronousMessageFromInjectedBundle(messageName, messageBody).leakRef();
1464 }
1465
1466 WKTypeRef TestController::getInjectedBundleInitializationUserData(WKContextRef, const void* clientInfo)
1467 {
1468     return static_cast<TestController*>(const_cast<void*>(clientInfo))->getInjectedBundleInitializationUserData().leakRef();
1469 }
1470
1471 // WKPageInjectedBundleClient
1472
1473 void TestController::didReceivePageMessageFromInjectedBundle(WKPageRef page, WKStringRef messageName, WKTypeRef messageBody, const void* clientInfo)
1474 {
1475     static_cast<TestController*>(const_cast<void*>(clientInfo))->didReceiveMessageFromInjectedBundle(messageName, messageBody);
1476 }
1477
1478 void TestController::didReceiveSynchronousPageMessageFromInjectedBundle(WKPageRef page, WKStringRef messageName, WKTypeRef messageBody, WKTypeRef* returnData, const void* clientInfo)
1479 {
1480     *returnData = static_cast<TestController*>(const_cast<void*>(clientInfo))->didReceiveSynchronousMessageFromInjectedBundle(messageName, messageBody).leakRef();
1481 }
1482
1483 void TestController::networkProcessDidCrash(WKContextRef context, const void *clientInfo)
1484 {
1485     static_cast<TestController*>(const_cast<void*>(clientInfo))->networkProcessDidCrash();
1486 }
1487
1488 void TestController::didReceiveKeyDownMessageFromInjectedBundle(WKDictionaryRef messageBodyDictionary, bool synchronous)
1489 {
1490     WKRetainPtr<WKStringRef> keyKey = adoptWK(WKStringCreateWithUTF8CString("Key"));
1491     WKStringRef key = static_cast<WKStringRef>(WKDictionaryGetItemForKey(messageBodyDictionary, keyKey.get()));
1492
1493     WKRetainPtr<WKStringRef> modifiersKey = adoptWK(WKStringCreateWithUTF8CString("Modifiers"));
1494     WKEventModifiers modifiers = static_cast<WKEventModifiers>(WKUInt64GetValue(static_cast<WKUInt64Ref>(WKDictionaryGetItemForKey(messageBodyDictionary, modifiersKey.get()))));
1495
1496     WKRetainPtr<WKStringRef> locationKey = adoptWK(WKStringCreateWithUTF8CString("Location"));
1497     unsigned location = static_cast<unsigned>(WKUInt64GetValue(static_cast<WKUInt64Ref>(WKDictionaryGetItemForKey(messageBodyDictionary, locationKey.get()))));
1498
1499     m_eventSenderProxy->keyDown(key, modifiers, location);
1500 }
1501
1502 void TestController::didReceiveLiveDocumentsList(WKArrayRef liveDocumentList)
1503 {
1504     auto numDocuments = WKArrayGetSize(liveDocumentList);
1505
1506     HashMap<uint64_t, String> documentInfo;
1507     for (size_t i = 0; i < numDocuments; ++i) {
1508         WKTypeRef item = WKArrayGetItemAtIndex(liveDocumentList, i);
1509         if (item && WKGetTypeID(item) == WKDictionaryGetTypeID()) {
1510             WKDictionaryRef liveDocumentItem = static_cast<WKDictionaryRef>(item);
1511
1512             WKRetainPtr<WKStringRef> idKey(AdoptWK, WKStringCreateWithUTF8CString("id"));
1513             WKUInt64Ref documentID = static_cast<WKUInt64Ref>(WKDictionaryGetItemForKey(liveDocumentItem, idKey.get()));
1514
1515             WKRetainPtr<WKStringRef> urlKey(AdoptWK, WKStringCreateWithUTF8CString("url"));
1516             WKStringRef documentURL = static_cast<WKStringRef>(WKDictionaryGetItemForKey(liveDocumentItem, urlKey.get()));
1517
1518             documentInfo.add(WKUInt64GetValue(documentID), toWTFString(documentURL));
1519         }
1520     }
1521
1522     if (!documentInfo.size()) {
1523         m_abandonedDocumentInfo.clear();
1524         return;
1525     }
1526
1527     // Remove any documents which are no longer live.
1528     m_abandonedDocumentInfo.removeIf([&](auto& keyAndValue) {
1529         return !documentInfo.contains(keyAndValue.key);
1530     });
1531     
1532     // Add newly abandoned documents.
1533     String currentTestURL = m_currentInvocation ? toWTFString(adoptWK(WKURLCopyString(m_currentInvocation->url()))) : "no test";
1534     for (const auto& it : documentInfo)
1535         m_abandonedDocumentInfo.add(it.key, AbandonedDocumentInfo(currentTestURL, it.value));
1536 }
1537
1538 void TestController::didReceiveMessageFromInjectedBundle(WKStringRef messageName, WKTypeRef messageBody)
1539 {
1540     if (WKStringIsEqualToUTF8CString(messageName, "LiveDocuments")) {
1541         ASSERT(WKGetTypeID(messageBody) == WKArrayGetTypeID());
1542         didReceiveLiveDocumentsList(static_cast<WKArrayRef>(messageBody));
1543         AsyncTask::currentTask()->taskComplete();
1544         return;
1545     }
1546
1547     if (WKStringIsEqualToUTF8CString(messageName, "EventSender")) {
1548         if (m_state != RunningTest)
1549             return;
1550
1551         ASSERT(WKGetTypeID(messageBody) == WKDictionaryGetTypeID());
1552         WKDictionaryRef messageBodyDictionary = static_cast<WKDictionaryRef>(messageBody);
1553
1554         WKRetainPtr<WKStringRef> subMessageKey(AdoptWK, WKStringCreateWithUTF8CString("SubMessage"));
1555         WKStringRef subMessageName = static_cast<WKStringRef>(WKDictionaryGetItemForKey(messageBodyDictionary, subMessageKey.get()));
1556
1557         if (WKStringIsEqualToUTF8CString(subMessageName, "MouseDown") || WKStringIsEqualToUTF8CString(subMessageName, "MouseUp")) {
1558             WKRetainPtr<WKStringRef> buttonKey = adoptWK(WKStringCreateWithUTF8CString("Button"));
1559             unsigned button = static_cast<unsigned>(WKUInt64GetValue(static_cast<WKUInt64Ref>(WKDictionaryGetItemForKey(messageBodyDictionary, buttonKey.get()))));
1560
1561             WKRetainPtr<WKStringRef> modifiersKey = adoptWK(WKStringCreateWithUTF8CString("Modifiers"));
1562             WKEventModifiers modifiers = static_cast<WKEventModifiers>(WKUInt64GetValue(static_cast<WKUInt64Ref>(WKDictionaryGetItemForKey(messageBodyDictionary, modifiersKey.get()))));
1563
1564             // Forward to WebProcess
1565             if (WKStringIsEqualToUTF8CString(subMessageName, "MouseDown"))
1566                 m_eventSenderProxy->mouseDown(button, modifiers);
1567             else
1568                 m_eventSenderProxy->mouseUp(button, modifiers);
1569
1570             return;
1571         }
1572
1573         if (WKStringIsEqualToUTF8CString(subMessageName, "KeyDown")) {
1574             didReceiveKeyDownMessageFromInjectedBundle(messageBodyDictionary, false);
1575             return;
1576         }
1577
1578         if (WKStringIsEqualToUTF8CString(subMessageName, "MouseScrollBy")) {
1579             WKRetainPtr<WKStringRef> xKey = adoptWK(WKStringCreateWithUTF8CString("X"));
1580             double x = WKDoubleGetValue(static_cast<WKDoubleRef>(WKDictionaryGetItemForKey(messageBodyDictionary, xKey.get())));
1581
1582             WKRetainPtr<WKStringRef> yKey = adoptWK(WKStringCreateWithUTF8CString("Y"));
1583             double y = WKDoubleGetValue(static_cast<WKDoubleRef>(WKDictionaryGetItemForKey(messageBodyDictionary, yKey.get())));
1584
1585             // Forward to WebProcess
1586             m_eventSenderProxy->mouseScrollBy(x, y);
1587             return;
1588         }
1589
1590         if (WKStringIsEqualToUTF8CString(subMessageName, "MouseScrollByWithWheelAndMomentumPhases")) {
1591             WKRetainPtr<WKStringRef> xKey = adoptWK(WKStringCreateWithUTF8CString("X"));
1592             double x = WKDoubleGetValue(static_cast<WKDoubleRef>(WKDictionaryGetItemForKey(messageBodyDictionary, xKey.get())));
1593             
1594             WKRetainPtr<WKStringRef> yKey = adoptWK(WKStringCreateWithUTF8CString("Y"));
1595             double y = WKDoubleGetValue(static_cast<WKDoubleRef>(WKDictionaryGetItemForKey(messageBodyDictionary, yKey.get())));
1596             
1597             WKRetainPtr<WKStringRef> phaseKey = adoptWK(WKStringCreateWithUTF8CString("Phase"));
1598             int phase = static_cast<int>(WKUInt64GetValue(static_cast<WKUInt64Ref>(WKDictionaryGetItemForKey(messageBodyDictionary, phaseKey.get()))));
1599             WKRetainPtr<WKStringRef> momentumKey = adoptWK(WKStringCreateWithUTF8CString("Momentum"));
1600             int momentum = static_cast<int>(WKUInt64GetValue(static_cast<WKUInt64Ref>(WKDictionaryGetItemForKey(messageBodyDictionary, momentumKey.get()))));
1601             
1602             // Forward to WebProcess
1603             m_eventSenderProxy->mouseScrollByWithWheelAndMomentumPhases(x, y, phase, momentum);
1604
1605             return;
1606         }
1607
1608         ASSERT_NOT_REACHED();
1609     }
1610
1611     if (!m_currentInvocation)
1612         return;
1613
1614     m_currentInvocation->didReceiveMessageFromInjectedBundle(messageName, messageBody);
1615 }
1616
1617 WKRetainPtr<WKTypeRef> TestController::didReceiveSynchronousMessageFromInjectedBundle(WKStringRef messageName, WKTypeRef messageBody)
1618 {
1619     if (WKStringIsEqualToUTF8CString(messageName, "EventSender")) {
1620         if (m_state != RunningTest)
1621             return nullptr;
1622
1623         ASSERT(WKGetTypeID(messageBody) == WKDictionaryGetTypeID());
1624         WKDictionaryRef messageBodyDictionary = static_cast<WKDictionaryRef>(messageBody);
1625
1626         WKRetainPtr<WKStringRef> subMessageKey(AdoptWK, WKStringCreateWithUTF8CString("SubMessage"));
1627         WKStringRef subMessageName = static_cast<WKStringRef>(WKDictionaryGetItemForKey(messageBodyDictionary, subMessageKey.get()));
1628
1629         if (WKStringIsEqualToUTF8CString(subMessageName, "KeyDown")) {
1630             didReceiveKeyDownMessageFromInjectedBundle(messageBodyDictionary, true);
1631
1632             return 0;
1633         }
1634
1635         if (WKStringIsEqualToUTF8CString(subMessageName, "MouseDown") || WKStringIsEqualToUTF8CString(subMessageName, "MouseUp")) {
1636             WKRetainPtr<WKStringRef> buttonKey = adoptWK(WKStringCreateWithUTF8CString("Button"));
1637             unsigned button = static_cast<unsigned>(WKUInt64GetValue(static_cast<WKUInt64Ref>(WKDictionaryGetItemForKey(messageBodyDictionary, buttonKey.get()))));
1638
1639             WKRetainPtr<WKStringRef> modifiersKey = adoptWK(WKStringCreateWithUTF8CString("Modifiers"));
1640             WKEventModifiers modifiers = static_cast<WKEventModifiers>(WKUInt64GetValue(static_cast<WKUInt64Ref>(WKDictionaryGetItemForKey(messageBodyDictionary, modifiersKey.get()))));
1641
1642             // Forward to WebProcess
1643             if (WKStringIsEqualToUTF8CString(subMessageName, "MouseDown"))
1644                 m_eventSenderProxy->mouseDown(button, modifiers);
1645             else
1646                 m_eventSenderProxy->mouseUp(button, modifiers);
1647             return 0;
1648         }
1649
1650         if (WKStringIsEqualToUTF8CString(subMessageName, "MouseMoveTo")) {
1651             WKRetainPtr<WKStringRef> xKey = adoptWK(WKStringCreateWithUTF8CString("X"));
1652             double x = WKDoubleGetValue(static_cast<WKDoubleRef>(WKDictionaryGetItemForKey(messageBodyDictionary, xKey.get())));
1653
1654             WKRetainPtr<WKStringRef> yKey = adoptWK(WKStringCreateWithUTF8CString("Y"));
1655             double y = WKDoubleGetValue(static_cast<WKDoubleRef>(WKDictionaryGetItemForKey(messageBodyDictionary, yKey.get())));
1656
1657             // Forward to WebProcess
1658             m_eventSenderProxy->mouseMoveTo(x, y);
1659             return 0;
1660         }
1661
1662 #if PLATFORM(MAC)
1663         if (WKStringIsEqualToUTF8CString(subMessageName, "MouseForceClick")) {
1664             m_eventSenderProxy->mouseForceClick();
1665             return 0;
1666         }
1667
1668         if (WKStringIsEqualToUTF8CString(subMessageName, "StartAndCancelMouseForceClick")) {
1669             m_eventSenderProxy->startAndCancelMouseForceClick();
1670             return 0;
1671         }
1672
1673         if (WKStringIsEqualToUTF8CString(subMessageName, "MouseForceDown")) {
1674             m_eventSenderProxy->mouseForceDown();
1675             return 0;
1676         }
1677
1678         if (WKStringIsEqualToUTF8CString(subMessageName, "MouseForceUp")) {
1679             m_eventSenderProxy->mouseForceUp();
1680             return 0;
1681         }
1682
1683         if (WKStringIsEqualToUTF8CString(subMessageName, "MouseForceChanged")) {
1684             WKRetainPtr<WKStringRef> forceKey = adoptWK(WKStringCreateWithUTF8CString("Force"));
1685             double force = WKDoubleGetValue(static_cast<WKDoubleRef>(WKDictionaryGetItemForKey(messageBodyDictionary, forceKey.get())));
1686
1687             m_eventSenderProxy->mouseForceChanged(force);
1688             return 0;
1689         }
1690 #endif // PLATFORM(MAC)
1691
1692         if (WKStringIsEqualToUTF8CString(subMessageName, "ContinuousMouseScrollBy")) {
1693             WKRetainPtr<WKStringRef> xKey = adoptWK(WKStringCreateWithUTF8CString("X"));
1694             double x = WKDoubleGetValue(static_cast<WKDoubleRef>(WKDictionaryGetItemForKey(messageBodyDictionary, xKey.get())));
1695
1696             WKRetainPtr<WKStringRef> yKey = adoptWK(WKStringCreateWithUTF8CString("Y"));
1697             double y = WKDoubleGetValue(static_cast<WKDoubleRef>(WKDictionaryGetItemForKey(messageBodyDictionary, yKey.get())));
1698
1699             WKRetainPtr<WKStringRef> pagedKey = adoptWK(WKStringCreateWithUTF8CString("Paged"));
1700             bool paged = static_cast<bool>(WKUInt64GetValue(static_cast<WKUInt64Ref>(WKDictionaryGetItemForKey(messageBodyDictionary, pagedKey.get()))));
1701
1702             // Forward to WebProcess
1703             m_eventSenderProxy->continuousMouseScrollBy(x, y, paged);
1704             return 0;
1705         }
1706
1707         if (WKStringIsEqualToUTF8CString(subMessageName, "LeapForward")) {
1708             WKRetainPtr<WKStringRef> timeKey = adoptWK(WKStringCreateWithUTF8CString("TimeInMilliseconds"));
1709             unsigned time = static_cast<unsigned>(WKUInt64GetValue(static_cast<WKUInt64Ref>(WKDictionaryGetItemForKey(messageBodyDictionary, timeKey.get()))));
1710
1711             m_eventSenderProxy->leapForward(time);
1712             return 0;
1713         }
1714
1715 #if ENABLE(TOUCH_EVENTS)
1716         if (WKStringIsEqualToUTF8CString(subMessageName, "AddTouchPoint")) {
1717             WKRetainPtr<WKStringRef> xKey = adoptWK(WKStringCreateWithUTF8CString("X"));
1718             int x = static_cast<int>(WKUInt64GetValue(static_cast<WKUInt64Ref>(WKDictionaryGetItemForKey(messageBodyDictionary, xKey.get()))));
1719
1720             WKRetainPtr<WKStringRef> yKey = adoptWK(WKStringCreateWithUTF8CString("Y"));
1721             int y = static_cast<int>(WKUInt64GetValue(static_cast<WKUInt64Ref>(WKDictionaryGetItemForKey(messageBodyDictionary, yKey.get()))));
1722
1723             m_eventSenderProxy->addTouchPoint(x, y);
1724             return 0;
1725         }
1726
1727         if (WKStringIsEqualToUTF8CString(subMessageName, "UpdateTouchPoint")) {
1728             WKRetainPtr<WKStringRef> indexKey = adoptWK(WKStringCreateWithUTF8CString("Index"));
1729             int index = static_cast<int>(WKUInt64GetValue(static_cast<WKUInt64Ref>(WKDictionaryGetItemForKey(messageBodyDictionary, indexKey.get()))));
1730
1731             WKRetainPtr<WKStringRef> xKey = adoptWK(WKStringCreateWithUTF8CString("X"));
1732             int x = static_cast<int>(WKUInt64GetValue(static_cast<WKUInt64Ref>(WKDictionaryGetItemForKey(messageBodyDictionary, xKey.get()))));
1733
1734             WKRetainPtr<WKStringRef> yKey = adoptWK(WKStringCreateWithUTF8CString("Y"));
1735             int y = static_cast<int>(WKUInt64GetValue(static_cast<WKUInt64Ref>(WKDictionaryGetItemForKey(messageBodyDictionary, yKey.get()))));
1736
1737             m_eventSenderProxy->updateTouchPoint(index, x, y);
1738             return 0;
1739         }
1740
1741         if (WKStringIsEqualToUTF8CString(subMessageName, "SetTouchModifier")) {
1742             WKRetainPtr<WKStringRef> modifierKey = adoptWK(WKStringCreateWithUTF8CString("Modifier"));
1743             WKEventModifiers modifier = static_cast<WKEventModifiers>(WKUInt64GetValue(static_cast<WKUInt64Ref>(WKDictionaryGetItemForKey(messageBodyDictionary, modifierKey.get()))));
1744
1745             WKRetainPtr<WKStringRef> enableKey = adoptWK(WKStringCreateWithUTF8CString("Enable"));
1746             bool enable = static_cast<bool>(WKUInt64GetValue(static_cast<WKUInt64Ref>(WKDictionaryGetItemForKey(messageBodyDictionary, enableKey.get()))));
1747
1748             m_eventSenderProxy->setTouchModifier(modifier, enable);
1749             return 0;
1750         }
1751
1752         if (WKStringIsEqualToUTF8CString(subMessageName, "SetTouchPointRadius")) {
1753             WKRetainPtr<WKStringRef> xKey = adoptWK(WKStringCreateWithUTF8CString("RadiusX"));
1754             int x = static_cast<int>(WKUInt64GetValue(static_cast<WKUInt64Ref>(WKDictionaryGetItemForKey(messageBodyDictionary, xKey.get()))));
1755
1756             WKRetainPtr<WKStringRef> yKey = adoptWK(WKStringCreateWithUTF8CString("RadiusY"));
1757             int y = static_cast<int>(WKUInt64GetValue(static_cast<WKUInt64Ref>(WKDictionaryGetItemForKey(messageBodyDictionary, yKey.get()))));
1758
1759             m_eventSenderProxy->setTouchPointRadius(x, y);
1760             return 0;
1761         }
1762
1763         if (WKStringIsEqualToUTF8CString(subMessageName, "TouchStart")) {
1764             m_eventSenderProxy->touchStart();
1765             return 0;
1766         }
1767
1768         if (WKStringIsEqualToUTF8CString(subMessageName, "TouchMove")) {
1769             m_eventSenderProxy->touchMove();
1770             return 0;
1771         }
1772
1773         if (WKStringIsEqualToUTF8CString(subMessageName, "TouchEnd")) {
1774             m_eventSenderProxy->touchEnd();
1775             return 0;
1776         }
1777
1778         if (WKStringIsEqualToUTF8CString(subMessageName, "TouchCancel")) {
1779             m_eventSenderProxy->touchCancel();
1780             return 0;
1781         }
1782
1783         if (WKStringIsEqualToUTF8CString(subMessageName, "ClearTouchPoints")) {
1784             m_eventSenderProxy->clearTouchPoints();
1785             return 0;
1786         }
1787
1788         if (WKStringIsEqualToUTF8CString(subMessageName, "ReleaseTouchPoint")) {
1789             WKRetainPtr<WKStringRef> indexKey = adoptWK(WKStringCreateWithUTF8CString("Index"));
1790             int index = static_cast<int>(WKUInt64GetValue(static_cast<WKUInt64Ref>(WKDictionaryGetItemForKey(messageBodyDictionary, indexKey.get()))));
1791             m_eventSenderProxy->releaseTouchPoint(index);
1792             return 0;
1793         }
1794
1795         if (WKStringIsEqualToUTF8CString(subMessageName, "CancelTouchPoint")) {
1796             WKRetainPtr<WKStringRef> indexKey = adoptWK(WKStringCreateWithUTF8CString("Index"));
1797             int index = static_cast<int>(WKUInt64GetValue(static_cast<WKUInt64Ref>(WKDictionaryGetItemForKey(messageBodyDictionary, indexKey.get()))));
1798             m_eventSenderProxy->cancelTouchPoint(index);
1799             return 0;
1800         }
1801 #endif
1802         ASSERT_NOT_REACHED();
1803     }
1804     return m_currentInvocation->didReceiveSynchronousMessageFromInjectedBundle(messageName, messageBody);
1805 }
1806
1807 WKRetainPtr<WKTypeRef> TestController::getInjectedBundleInitializationUserData()
1808 {
1809     return nullptr;
1810 }
1811
1812 // WKContextClient
1813
1814 void TestController::networkProcessDidCrash()
1815 {
1816     pid_t pid = WKContextGetNetworkProcessIdentifier(m_context.get());
1817     fprintf(stderr, "#CRASHED - %s (pid %ld)\n", networkProcessName(), static_cast<long>(pid));
1818     exit(1);
1819 }
1820
1821 // WKPageNavigationClient
1822
1823 void TestController::didCommitNavigation(WKPageRef page, WKNavigationRef navigation, WKTypeRef, const void* clientInfo)
1824 {
1825     static_cast<TestController*>(const_cast<void*>(clientInfo))->didCommitNavigation(page, navigation);
1826 }
1827
1828 void TestController::didFinishNavigation(WKPageRef page, WKNavigationRef navigation, WKTypeRef, const void* clientInfo)
1829 {
1830     static_cast<TestController*>(const_cast<void*>(clientInfo))->didFinishNavigation(page, navigation);
1831 }
1832
1833 void TestController::didReceiveServerRedirectForProvisionalNavigation(WKPageRef page, WKNavigationRef navigation, WKTypeRef userData, const void* clientInfo)
1834 {
1835     static_cast<TestController*>(const_cast<void*>(clientInfo))->didReceiveServerRedirectForProvisionalNavigation(page, navigation, userData);
1836 }
1837
1838 bool TestController::canAuthenticateAgainstProtectionSpace(WKPageRef page, WKProtectionSpaceRef protectionSpace, const void* clientInfo)
1839 {
1840     return static_cast<TestController*>(const_cast<void*>(clientInfo))->canAuthenticateAgainstProtectionSpace(page, protectionSpace);
1841 }
1842
1843 void TestController::didReceiveAuthenticationChallenge(WKPageRef page, WKAuthenticationChallengeRef authenticationChallenge, const void *clientInfo)
1844 {
1845     static_cast<TestController*>(const_cast<void*>(clientInfo))->didReceiveAuthenticationChallenge(page, /*frame,*/ authenticationChallenge);
1846 }
1847
1848 void TestController::processDidCrash(WKPageRef page, const void* clientInfo)
1849 {
1850     static_cast<TestController*>(const_cast<void*>(clientInfo))->processDidCrash();
1851 }
1852
1853 void TestController::didBeginNavigationGesture(WKPageRef page, const void *clientInfo)
1854 {
1855     static_cast<TestController*>(const_cast<void*>(clientInfo))->didBeginNavigationGesture(page);
1856 }
1857
1858 void TestController::willEndNavigationGesture(WKPageRef page, WKBackForwardListItemRef backForwardListItem, const void *clientInfo)
1859 {
1860     static_cast<TestController*>(const_cast<void*>(clientInfo))->willEndNavigationGesture(page, backForwardListItem);
1861 }
1862
1863 void TestController::didEndNavigationGesture(WKPageRef page, WKBackForwardListItemRef backForwardListItem, const void *clientInfo)
1864 {
1865     static_cast<TestController*>(const_cast<void*>(clientInfo))->didEndNavigationGesture(page, backForwardListItem);
1866 }
1867
1868 void TestController::didRemoveNavigationGestureSnapshot(WKPageRef page, const void *clientInfo)
1869 {
1870     static_cast<TestController*>(const_cast<void*>(clientInfo))->didRemoveNavigationGestureSnapshot(page);
1871 }
1872
1873 WKPluginLoadPolicy TestController::decidePolicyForPluginLoad(WKPageRef page, WKPluginLoadPolicy currentPluginLoadPolicy, WKDictionaryRef pluginInformation, WKStringRef* unavailabilityDescription, const void* clientInfo)
1874 {
1875     return static_cast<TestController*>(const_cast<void*>(clientInfo))->decidePolicyForPluginLoad(page, currentPluginLoadPolicy, pluginInformation, unavailabilityDescription);
1876 }
1877
1878 WKPluginLoadPolicy TestController::decidePolicyForPluginLoad(WKPageRef, WKPluginLoadPolicy currentPluginLoadPolicy, WKDictionaryRef pluginInformation, WKStringRef* unavailabilityDescription)
1879 {
1880     if (m_shouldBlockAllPlugins)
1881         return kWKPluginLoadPolicyBlocked;
1882
1883 #if PLATFORM(MAC)
1884     WKStringRef bundleIdentifier = (WKStringRef)WKDictionaryGetItemForKey(pluginInformation, WKPluginInformationBundleIdentifierKey());
1885     if (!bundleIdentifier)
1886         return currentPluginLoadPolicy;
1887
1888     if (WKStringIsEqualToUTF8CString(bundleIdentifier, "com.apple.QuickTime Plugin.plugin"))
1889         return currentPluginLoadPolicy;
1890
1891     if (WKStringIsEqualToUTF8CString(bundleIdentifier, "com.apple.testnetscapeplugin"))
1892         return currentPluginLoadPolicy;
1893
1894     RELEASE_ASSERT_NOT_REACHED(); // Please don't use any other plug-ins in tests, as they will not be installed on all machines.
1895 #else
1896     return currentPluginLoadPolicy;
1897 #endif
1898 }
1899
1900 void TestController::setBlockAllPlugins(bool shouldBlock)
1901 {
1902     m_shouldBlockAllPlugins = shouldBlock;
1903
1904 #if PLATFORM(MAC)
1905     auto policy = shouldBlock ? kWKPluginLoadClientPolicyBlock : kWKPluginLoadClientPolicyAllow;
1906
1907     WKRetainPtr<WKStringRef> nameNetscape = adoptWK(WKStringCreateWithUTF8CString("com.apple.testnetscapeplugin"));
1908     WKRetainPtr<WKStringRef> nameFlash = adoptWK(WKStringCreateWithUTF8CString("com.macromedia.Flash Player.plugin"));
1909     WKRetainPtr<WKStringRef> emptyString = adoptWK(WKStringCreateWithUTF8CString(""));
1910     WKContextSetPluginLoadClientPolicy(m_context.get(), policy, emptyString.get(), nameNetscape.get(), emptyString.get());
1911     WKContextSetPluginLoadClientPolicy(m_context.get(), policy, emptyString.get(), nameFlash.get(), emptyString.get());
1912 #endif
1913 }
1914
1915 void TestController::setPluginSupportedMode(const String& mode)
1916 {
1917     if (m_unsupportedPluginMode == mode)
1918         return;
1919
1920     m_unsupportedPluginMode = mode;
1921     if (m_unsupportedPluginMode.isEmpty()) {
1922         WKContextClearSupportedPlugins(m_context.get());
1923         return;
1924     }
1925
1926     WKRetainPtr<WKMutableArrayRef> emptyArray = adoptWK(WKMutableArrayCreate());
1927     WKRetainPtr<WKStringRef> allOrigins = adoptWK(WKStringCreateWithUTF8CString(""));
1928     WKRetainPtr<WKStringRef> specificOrigin = adoptWK(WKStringCreateWithUTF8CString("localhost"));
1929
1930     WKRetainPtr<WKStringRef> pdfName = adoptWK(WKStringCreateWithUTF8CString("My personal PDF"));
1931     WKContextAddSupportedPlugin(m_context.get(), allOrigins.get(), pdfName.get(), emptyArray.get(), emptyArray.get());
1932
1933     WKRetainPtr<WKStringRef> nameNetscape = adoptWK(WKStringCreateWithUTF8CString("com.apple.testnetscapeplugin"));
1934     WKRetainPtr<WKStringRef> mimeTypeNetscape = adoptWK(WKStringCreateWithUTF8CString("application/x-webkit-test-netscape"));
1935     WKRetainPtr<WKMutableArrayRef> mimeTypesNetscape = adoptWK(WKMutableArrayCreate());
1936     WKArrayAppendItem(mimeTypesNetscape.get(), mimeTypeNetscape.get());
1937
1938     WKRetainPtr<WKStringRef> namePdf = adoptWK(WKStringCreateWithUTF8CString("WebKit built-in PDF"));
1939
1940     if (m_unsupportedPluginMode == "allOrigins") {
1941         WKContextAddSupportedPlugin(m_context.get(), allOrigins.get(), nameNetscape.get(), mimeTypesNetscape.get(), emptyArray.get());
1942         WKContextAddSupportedPlugin(m_context.get(), allOrigins.get(), namePdf.get(), emptyArray.get(), emptyArray.get());
1943         return;
1944     }
1945
1946     if (m_unsupportedPluginMode == "specificOrigin") {
1947         WKContextAddSupportedPlugin(m_context.get(), specificOrigin.get(), nameNetscape.get(), mimeTypesNetscape.get(), emptyArray.get());
1948         WKContextAddSupportedPlugin(m_context.get(), specificOrigin.get(), namePdf.get(), emptyArray.get(), emptyArray.get());
1949         return;
1950     }
1951 }
1952
1953 void TestController::didCommitNavigation(WKPageRef page, WKNavigationRef navigation)
1954 {
1955     mainWebView()->focus();
1956 }
1957
1958 void TestController::didReceiveServerRedirectForProvisionalNavigation(WKPageRef page, WKNavigationRef navigation, WKTypeRef userData)
1959 {
1960     m_didReceiveServerRedirectForProvisionalNavigation = true;
1961     return;
1962 }
1963
1964 static const char* toString(WKProtectionSpaceAuthenticationScheme scheme)
1965 {
1966     switch (scheme) {
1967     case kWKProtectionSpaceAuthenticationSchemeDefault:
1968         return "ProtectionSpaceAuthenticationSchemeDefault";
1969     case kWKProtectionSpaceAuthenticationSchemeHTTPBasic:
1970         return "ProtectionSpaceAuthenticationSchemeHTTPBasic";
1971     case kWKProtectionSpaceAuthenticationSchemeHTMLForm:
1972         return "ProtectionSpaceAuthenticationSchemeHTMLForm";
1973     case kWKProtectionSpaceAuthenticationSchemeNTLM:
1974         return "ProtectionSpaceAuthenticationSchemeNTLM";
1975     case kWKProtectionSpaceAuthenticationSchemeNegotiate:
1976         return "ProtectionSpaceAuthenticationSchemeNegotiate";
1977     case kWKProtectionSpaceAuthenticationSchemeClientCertificateRequested:
1978         return "ProtectionSpaceAuthenticationSchemeClientCertificateRequested";
1979     case kWKProtectionSpaceAuthenticationSchemeServerTrustEvaluationRequested:
1980         return "ProtectionSpaceAuthenticationSchemeServerTrustEvaluationRequested";
1981     case kWKProtectionSpaceAuthenticationSchemeOAuth:
1982         return "ProtectionSpaceAuthenticationSchemeOAuth";
1983     case kWKProtectionSpaceAuthenticationSchemeUnknown:
1984         return "ProtectionSpaceAuthenticationSchemeUnknown";
1985     }
1986     ASSERT_NOT_REACHED();
1987     return "ProtectionSpaceAuthenticationSchemeUnknown";
1988 }
1989
1990 bool TestController::canAuthenticateAgainstProtectionSpace(WKPageRef page, WKProtectionSpaceRef protectionSpace)
1991 {
1992     if (m_shouldLogCanAuthenticateAgainstProtectionSpace)
1993         m_currentInvocation->outputText("canAuthenticateAgainstProtectionSpace\n");
1994     WKProtectionSpaceAuthenticationScheme authenticationScheme = WKProtectionSpaceGetAuthenticationScheme(protectionSpace);
1995     
1996     if (authenticationScheme == kWKProtectionSpaceAuthenticationSchemeServerTrustEvaluationRequested) {
1997         std::string host = toSTD(adoptWK(WKProtectionSpaceCopyHost(protectionSpace)).get());
1998         return host == "localhost" || host == "127.0.0.1" || (m_allowAnyHTTPSCertificateForAllowedHosts && m_allowedHosts.find(host) != m_allowedHosts.end());
1999     }
2000     
2001     return authenticationScheme <= kWKProtectionSpaceAuthenticationSchemeHTTPDigest || authenticationScheme == kWKProtectionSpaceAuthenticationSchemeOAuth;
2002 }
2003
2004 void TestController::didFinishNavigation(WKPageRef page, WKNavigationRef navigation)
2005 {
2006     if (m_state != Resetting)
2007         return;
2008
2009     WKRetainPtr<WKURLRef> wkURL(AdoptWK, WKFrameCopyURL(WKPageGetMainFrame(page)));
2010     if (!WKURLIsEqual(wkURL.get(), blankURL()))
2011         return;
2012
2013     m_doneResetting = true;
2014     singleton().notifyDone();
2015 }
2016
2017 void TestController::didReceiveAuthenticationChallenge(WKPageRef page, WKAuthenticationChallengeRef authenticationChallenge)
2018 {
2019     WKProtectionSpaceRef protectionSpace = WKAuthenticationChallengeGetProtectionSpace(authenticationChallenge);
2020     WKAuthenticationDecisionListenerRef decisionListener = WKAuthenticationChallengeGetDecisionListener(authenticationChallenge);
2021     WKProtectionSpaceAuthenticationScheme authenticationScheme = WKProtectionSpaceGetAuthenticationScheme(protectionSpace);
2022
2023     if (authenticationScheme == kWKProtectionSpaceAuthenticationSchemeServerTrustEvaluationRequested) {
2024         // Any non-empty credential signals to accept the server trust. Since the cross-platform API
2025         // doesn't expose a way to create a credential from server trust, we use a password credential.
2026
2027         WKRetainPtr<WKCredentialRef> credential = adoptWK(WKCredentialCreate(toWK("accept server trust").get(), toWK("").get(), kWKCredentialPersistenceNone));
2028         WKAuthenticationDecisionListenerUseCredential(decisionListener, credential.get());
2029         return;
2030     }
2031
2032     if (m_rejectsProtectionSpaceAndContinueForAuthenticationChallenges) {
2033         m_currentInvocation->outputText("Simulating reject protection space and continue for authentication challenge\n");
2034         WKAuthenticationDecisionListenerRejectProtectionSpaceAndContinue(decisionListener);
2035         return;
2036     }
2037
2038     std::string host = toSTD(adoptWK(WKProtectionSpaceCopyHost(protectionSpace)).get());
2039     int port = WKProtectionSpaceGetPort(protectionSpace);
2040     String message = String::format("%s:%d - didReceiveAuthenticationChallenge - %s - ", host.c_str(), port, toString(authenticationScheme));
2041     if (!m_handlesAuthenticationChallenges)
2042         message.append("Simulating cancelled authentication sheet\n");
2043     else
2044         message.append(String::format("Responding with %s:%s\n", m_authenticationUsername.utf8().data(), m_authenticationPassword.utf8().data()));
2045     m_currentInvocation->outputText(message);
2046
2047     if (!m_handlesAuthenticationChallenges) {
2048         WKAuthenticationDecisionListenerUseCredential(decisionListener, 0);
2049         return;
2050     }
2051     WKRetainPtr<WKStringRef> username(AdoptWK, WKStringCreateWithUTF8CString(m_authenticationUsername.utf8().data()));
2052     WKRetainPtr<WKStringRef> password(AdoptWK, WKStringCreateWithUTF8CString(m_authenticationPassword.utf8().data()));
2053     WKRetainPtr<WKCredentialRef> credential(AdoptWK, WKCredentialCreate(username.get(), password.get(), kWKCredentialPersistenceForSession));
2054     WKAuthenticationDecisionListenerUseCredential(decisionListener, credential.get());
2055 }
2056
2057     
2058 // WKContextDownloadClient
2059
2060 void TestController::downloadDidStart(WKContextRef context, WKDownloadRef download, const void* clientInfo)
2061 {
2062     static_cast<TestController*>(const_cast<void*>(clientInfo))->downloadDidStart(context, download);
2063 }
2064     
2065 WKStringRef TestController::decideDestinationWithSuggestedFilename(WKContextRef context, WKDownloadRef download, WKStringRef filename, bool* allowOverwrite, const void* clientInfo)
2066 {
2067     return static_cast<TestController*>(const_cast<void*>(clientInfo))->decideDestinationWithSuggestedFilename(context, download, filename, allowOverwrite);
2068 }
2069
2070 void TestController::downloadDidFinish(WKContextRef context, WKDownloadRef download, const void* clientInfo)
2071 {
2072     static_cast<TestController*>(const_cast<void*>(clientInfo))->downloadDidFinish(context, download);
2073 }
2074
2075 void TestController::downloadDidFail(WKContextRef context, WKDownloadRef download, WKErrorRef error, const void* clientInfo)
2076 {
2077     static_cast<TestController*>(const_cast<void*>(clientInfo))->downloadDidFail(context, download, error);
2078 }
2079
2080 void TestController::downloadDidCancel(WKContextRef context, WKDownloadRef download, const void* clientInfo)
2081 {
2082     static_cast<TestController*>(const_cast<void*>(clientInfo))->downloadDidCancel(context, download);
2083 }
2084
2085 void TestController::downloadDidReceiveServerRedirectToURL(WKContextRef context, WKDownloadRef download, WKURLRef url, const void* clientInfo)
2086 {
2087     static_cast<TestController*>(const_cast<void*>(clientInfo))->downloadDidReceiveServerRedirectToURL(context, download, url);
2088 }
2089
2090 void TestController::downloadDidStart(WKContextRef context, WKDownloadRef download)
2091 {
2092     if (m_shouldLogDownloadCallbacks)
2093         m_currentInvocation->outputText("Download started.\n");
2094 }
2095
2096 WKStringRef TestController::decideDestinationWithSuggestedFilename(WKContextRef, WKDownloadRef, WKStringRef filename, bool*& allowOverwrite)
2097 {
2098     String suggestedFilename = toWTFString(filename);
2099
2100     if (m_shouldLogDownloadCallbacks) {
2101         StringBuilder builder;
2102         builder.append("Downloading URL with suggested filename \"");
2103         builder.append(suggestedFilename);
2104         builder.append("\"\n");
2105         m_currentInvocation->outputText(builder.toString());
2106     }
2107
2108     const char* dumpRenderTreeTemp = libraryPathForTesting();
2109     if (!dumpRenderTreeTemp)
2110         return nullptr;
2111
2112     *allowOverwrite = true;
2113     String temporaryFolder = String::fromUTF8(dumpRenderTreeTemp);
2114     if (suggestedFilename.isEmpty())
2115         suggestedFilename = "Unknown";
2116
2117     return toWK(temporaryFolder + "/" + suggestedFilename).leakRef();
2118 }
2119
2120 void TestController::downloadDidFinish(WKContextRef, WKDownloadRef)
2121 {
2122     if (m_shouldLogDownloadCallbacks)
2123         m_currentInvocation->outputText("Download completed.\n");
2124     m_currentInvocation->notifyDownloadDone();
2125 }
2126
2127 void TestController::downloadDidReceiveServerRedirectToURL(WKContextRef, WKDownloadRef, WKURLRef url)
2128 {
2129     if (m_shouldLogDownloadCallbacks) {
2130         StringBuilder builder;
2131         builder.appendLiteral("Download was redirected to \"");
2132         WKRetainPtr<WKStringRef> urlStringWK = adoptWK(WKURLCopyString(url));
2133         builder.append(toSTD(urlStringWK).c_str());
2134         builder.appendLiteral("\".\n");
2135         m_currentInvocation->outputText(builder.toString());
2136     }
2137 }
2138
2139 void TestController::downloadDidFail(WKContextRef, WKDownloadRef, WKErrorRef error)
2140 {
2141     if (m_shouldLogDownloadCallbacks) {
2142         String message = String::format("Download failed.\n");
2143         m_currentInvocation->outputText(message);
2144
2145         WKRetainPtr<WKStringRef> errorDomain = adoptWK(WKErrorCopyDomain(error));
2146         WKRetainPtr<WKStringRef> errorDescription = adoptWK(WKErrorCopyLocalizedDescription(error));
2147         int errorCode = WKErrorGetErrorCode(error);
2148
2149         StringBuilder errorBuilder;
2150         errorBuilder.append("Failed: ");
2151         errorBuilder.append(toWTFString(errorDomain));
2152         errorBuilder.append(", code=");
2153         errorBuilder.appendNumber(errorCode);
2154         errorBuilder.append(", description=");
2155         errorBuilder.append(toWTFString(errorDescription));
2156         errorBuilder.append("\n");
2157
2158         m_currentInvocation->outputText(errorBuilder.toString());
2159     }
2160     m_currentInvocation->notifyDownloadDone();
2161 }
2162
2163 void TestController::downloadDidCancel(WKContextRef, WKDownloadRef)
2164 {
2165     if (m_shouldLogDownloadCallbacks)
2166         m_currentInvocation->outputText("Download cancelled.\n");
2167     m_currentInvocation->notifyDownloadDone();
2168 }
2169
2170 void TestController::processDidCrash()
2171 {
2172     // This function can be called multiple times when crash logs are being saved on Windows, so
2173     // ensure we only print the crashed message once.
2174     if (!m_didPrintWebProcessCrashedMessage) {
2175         pid_t pid = WKPageGetProcessIdentifier(m_mainWebView->page());
2176         fprintf(stderr, "#CRASHED - %s (pid %ld)\n", webProcessName(), static_cast<long>(pid));
2177         fflush(stderr);
2178         m_didPrintWebProcessCrashedMessage = true;
2179     }
2180
2181     if (m_shouldExitWhenWebProcessCrashes)
2182         exit(1);
2183 }
2184
2185 void TestController::didBeginNavigationGesture(WKPageRef)
2186 {
2187     m_currentInvocation->didBeginSwipe();
2188 }
2189
2190 void TestController::willEndNavigationGesture(WKPageRef, WKBackForwardListItemRef)
2191 {
2192     m_currentInvocation->willEndSwipe();
2193 }
2194
2195 void TestController::didEndNavigationGesture(WKPageRef, WKBackForwardListItemRef)
2196 {
2197     m_currentInvocation->didEndSwipe();
2198 }
2199
2200 void TestController::didRemoveNavigationGestureSnapshot(WKPageRef)
2201 {
2202     m_currentInvocation->didRemoveSwipeSnapshot();
2203 }
2204
2205 void TestController::simulateWebNotificationClick(uint64_t notificationID)
2206 {
2207     m_webNotificationProvider.simulateWebNotificationClick(mainWebView()->page(), notificationID);
2208 }
2209
2210 void TestController::setGeolocationPermission(bool enabled)
2211 {
2212     m_isGeolocationPermissionSet = true;
2213     m_isGeolocationPermissionAllowed = enabled;
2214     decidePolicyForGeolocationPermissionRequestIfPossible();
2215 }
2216
2217 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)
2218 {
2219     m_geolocationProvider->setPosition(latitude, longitude, accuracy, providesAltitude, altitude, providesAltitudeAccuracy, altitudeAccuracy, providesHeading, heading, providesSpeed, speed, providesFloorLevel, floorLevel);
2220 }
2221
2222 void TestController::setMockGeolocationPositionUnavailableError(WKStringRef errorMessage)
2223 {
2224     m_geolocationProvider->setPositionUnavailableError(errorMessage);
2225 }
2226
2227 void TestController::handleGeolocationPermissionRequest(WKGeolocationPermissionRequestRef geolocationPermissionRequest)
2228 {
2229     m_geolocationPermissionRequests.append(geolocationPermissionRequest);
2230     decidePolicyForGeolocationPermissionRequestIfPossible();
2231 }
2232
2233 bool TestController::isGeolocationProviderActive() const
2234 {
2235     return m_geolocationProvider->isActive();
2236 }
2237
2238 static String originUserVisibleName(WKSecurityOriginRef origin)
2239 {
2240     if (!origin)
2241         return emptyString();
2242
2243     std::string host = toSTD(adoptWK(WKSecurityOriginCopyHost(origin))).c_str();
2244     std::string protocol = toSTD(adoptWK(WKSecurityOriginCopyProtocol(origin))).c_str();
2245
2246     if (!host.length() || !protocol.length())
2247         return emptyString();
2248
2249     unsigned short port = WKSecurityOriginGetPort(origin);
2250     if (port)
2251         return String::format("%s://%s:%d", protocol.c_str(), host.c_str(), port);
2252
2253     return String::format("%s://%s", protocol.c_str(), host.c_str());
2254 }
2255
2256 static String userMediaOriginHash(WKSecurityOriginRef userMediaDocumentOrigin, WKSecurityOriginRef topLevelDocumentOrigin)
2257 {
2258     String userMediaDocumentOriginString = originUserVisibleName(userMediaDocumentOrigin);
2259     String topLevelDocumentOriginString = originUserVisibleName(topLevelDocumentOrigin);
2260
2261     if (topLevelDocumentOriginString.isEmpty())
2262         return userMediaDocumentOriginString;
2263
2264     return String::format("%s-%s", userMediaDocumentOriginString.utf8().data(), topLevelDocumentOriginString.utf8().data());
2265 }
2266
2267 static String userMediaOriginHash(WKStringRef userMediaDocumentOriginString, WKStringRef topLevelDocumentOriginString)
2268 {
2269     auto userMediaDocumentOrigin = adoptWK(WKSecurityOriginCreateFromString(userMediaDocumentOriginString));
2270     if (!WKStringGetLength(topLevelDocumentOriginString))
2271         return userMediaOriginHash(userMediaDocumentOrigin.get(), nullptr);
2272
2273     auto topLevelDocumentOrigin = adoptWK(WKSecurityOriginCreateFromString(topLevelDocumentOriginString));
2274     return userMediaOriginHash(userMediaDocumentOrigin.get(), topLevelDocumentOrigin.get());
2275 }
2276
2277 void TestController::setUserMediaPermission(bool enabled)
2278 {
2279     m_isUserMediaPermissionSet = true;
2280     m_isUserMediaPermissionAllowed = enabled;
2281     decidePolicyForUserMediaPermissionRequestIfPossible();
2282 }
2283
2284 void TestController::resetUserMediaPermission()
2285 {
2286     m_isUserMediaPermissionSet = false;
2287 }
2288
2289 class OriginSettings : public RefCounted<OriginSettings> {
2290 public:
2291     explicit OriginSettings()
2292     {
2293     }
2294
2295     bool persistentPermission() const { return m_persistentPermission; }
2296     void setPersistentPermission(bool permission) { m_persistentPermission = permission; }
2297
2298     String persistentSalt() const { return m_persistentSalt; }
2299     void setPersistentSalt(const String& salt) { m_persistentSalt = salt; }
2300
2301     HashMap<uint64_t, String>& ephemeralSalts() { return m_ephemeralSalts; }
2302
2303     void incrementRequestCount() { ++m_requestCount; }
2304     void resetRequestCount() { m_requestCount = 0; }
2305     unsigned requestCount() const { return m_requestCount; }
2306
2307 private:
2308     HashMap<uint64_t, String> m_ephemeralSalts;
2309     String m_persistentSalt;
2310     unsigned m_requestCount { 0 };
2311     bool m_persistentPermission { false };
2312 };
2313
2314 String TestController::saltForOrigin(WKFrameRef frame, String originHash)
2315 {
2316     auto& settings = settingsForOrigin(originHash);
2317     auto& ephemeralSalts = settings.ephemeralSalts();
2318     auto frameInfo = adoptWK(WKFrameCreateFrameInfo(frame));
2319     auto frameHandle = WKFrameInfoGetFrameHandleRef(frameInfo.get());
2320     uint64_t frameIdentifier = WKFrameHandleGetFrameID(frameHandle);
2321     String frameSalt = ephemeralSalts.get(frameIdentifier);
2322
2323     if (settings.persistentPermission()) {
2324         if (frameSalt.length())
2325             return frameSalt;
2326
2327         if (!settings.persistentSalt().length())
2328             settings.setPersistentSalt(createCanonicalUUIDString());
2329
2330         return settings.persistentSalt();
2331     }
2332
2333     if (!frameSalt.length()) {
2334         frameSalt = createCanonicalUUIDString();
2335         ephemeralSalts.add(frameIdentifier, frameSalt);
2336     }
2337
2338     return frameSalt;
2339 }
2340
2341 void TestController::setUserMediaPersistentPermissionForOrigin(bool permission, WKStringRef userMediaDocumentOriginString, WKStringRef topLevelDocumentOriginString)
2342 {
2343     auto originHash = userMediaOriginHash(userMediaDocumentOriginString, topLevelDocumentOriginString);
2344     auto& settings = settingsForOrigin(originHash);
2345     settings.setPersistentPermission(permission);
2346 }
2347
2348 void TestController::handleCheckOfUserMediaPermissionForOrigin(WKFrameRef frame, WKSecurityOriginRef userMediaDocumentOrigin, WKSecurityOriginRef topLevelDocumentOrigin, const WKUserMediaPermissionCheckRef& checkRequest)
2349 {
2350     auto originHash = userMediaOriginHash(userMediaDocumentOrigin, topLevelDocumentOrigin);
2351     auto salt = saltForOrigin(frame, originHash);
2352     WKRetainPtr<WKStringRef> saltString = adoptWK(WKStringCreateWithUTF8CString(salt.utf8().data()));
2353
2354     WKUserMediaPermissionCheckSetUserMediaAccessInfo(checkRequest, saltString.get(), settingsForOrigin(originHash).persistentPermission());
2355 }
2356
2357 void TestController::handleUserMediaPermissionRequest(WKFrameRef frame, WKSecurityOriginRef userMediaDocumentOrigin, WKSecurityOriginRef topLevelDocumentOrigin, WKUserMediaPermissionRequestRef request)
2358 {
2359     auto originHash = userMediaOriginHash(userMediaDocumentOrigin, topLevelDocumentOrigin);
2360     m_userMediaPermissionRequests.append(std::make_pair(originHash, request));
2361     decidePolicyForUserMediaPermissionRequestIfPossible();
2362 }
2363
2364 OriginSettings& TestController::settingsForOrigin(const String& originHash)
2365 {
2366     RefPtr<OriginSettings> settings = m_cachedUserMediaPermissions.get(originHash);
2367     if (!settings) {
2368         settings = adoptRef(*new OriginSettings());
2369         m_cachedUserMediaPermissions.add(originHash, settings);
2370     }
2371
2372     return *settings;
2373 }
2374
2375 unsigned TestController::userMediaPermissionRequestCountForOrigin(WKStringRef userMediaDocumentOriginString, WKStringRef topLevelDocumentOriginString)
2376 {
2377     auto originHash = userMediaOriginHash(userMediaDocumentOriginString, topLevelDocumentOriginString);
2378     return settingsForOrigin(originHash).requestCount();
2379 }
2380
2381 void TestController::resetUserMediaPermissionRequestCountForOrigin(WKStringRef userMediaDocumentOriginString, WKStringRef topLevelDocumentOriginString)
2382 {
2383     auto originHash = userMediaOriginHash(userMediaDocumentOriginString, topLevelDocumentOriginString);
2384     settingsForOrigin(originHash).resetRequestCount();
2385 }
2386
2387 void TestController::decidePolicyForUserMediaPermissionRequestIfPossible()
2388 {
2389     if (!m_isUserMediaPermissionSet)
2390         return;
2391
2392     for (auto& pair : m_userMediaPermissionRequests) {
2393         auto originHash = pair.first;
2394         auto request = pair.second.get();
2395
2396         auto& settings = settingsForOrigin(originHash);
2397         settings.incrementRequestCount();
2398
2399         if (!m_isUserMediaPermissionAllowed && !settings.persistentPermission()) {
2400             WKUserMediaPermissionRequestDeny(request, kWKPermissionDenied);
2401             continue;
2402         }
2403
2404         WKRetainPtr<WKArrayRef> audioDeviceUIDs = adoptWK(WKUserMediaPermissionRequestAudioDeviceUIDs(request));
2405         WKRetainPtr<WKArrayRef> videoDeviceUIDs = adoptWK(WKUserMediaPermissionRequestVideoDeviceUIDs(request));
2406
2407         if (!WKArrayGetSize(videoDeviceUIDs.get()) && !WKArrayGetSize(audioDeviceUIDs.get())) {
2408             WKUserMediaPermissionRequestDeny(request, kWKNoConstraints);
2409             continue;
2410         }
2411
2412         WKRetainPtr<WKStringRef> videoDeviceUID;
2413         if (WKArrayGetSize(videoDeviceUIDs.get()))
2414             videoDeviceUID = reinterpret_cast<WKStringRef>(WKArrayGetItemAtIndex(videoDeviceUIDs.get(), 0));
2415         else
2416             videoDeviceUID = adoptWK(WKStringCreateWithUTF8CString(""));
2417
2418         WKRetainPtr<WKStringRef> audioDeviceUID;
2419         if (WKArrayGetSize(audioDeviceUIDs.get()))
2420             audioDeviceUID = reinterpret_cast<WKStringRef>(WKArrayGetItemAtIndex(audioDeviceUIDs.get(), 0));
2421         else
2422             audioDeviceUID = adoptWK(WKStringCreateWithUTF8CString(""));
2423
2424         WKUserMediaPermissionRequestAllow(request, audioDeviceUID.get(), videoDeviceUID.get());
2425     }
2426     m_userMediaPermissionRequests.clear();
2427 }
2428
2429 void TestController::setCustomPolicyDelegate(bool enabled, bool permissive)
2430 {
2431     m_policyDelegateEnabled = enabled;
2432     m_policyDelegatePermissive = permissive;
2433 }
2434
2435 void TestController::decidePolicyForGeolocationPermissionRequestIfPossible()
2436 {
2437     if (!m_isGeolocationPermissionSet)
2438         return;
2439
2440     for (size_t i = 0; i < m_geolocationPermissionRequests.size(); ++i) {
2441         WKGeolocationPermissionRequestRef permissionRequest = m_geolocationPermissionRequests[i].get();
2442         if (m_isGeolocationPermissionAllowed)
2443             WKGeolocationPermissionRequestAllow(permissionRequest);
2444         else
2445             WKGeolocationPermissionRequestDeny(permissionRequest);
2446     }
2447     m_geolocationPermissionRequests.clear();
2448 }
2449
2450 void TestController::decidePolicyForNotificationPermissionRequest(WKPageRef page, WKSecurityOriginRef origin, WKNotificationPermissionRequestRef request, const void*)
2451 {
2452     TestController::singleton().decidePolicyForNotificationPermissionRequest(page, origin, request);
2453 }
2454
2455 void TestController::decidePolicyForNotificationPermissionRequest(WKPageRef, WKSecurityOriginRef, WKNotificationPermissionRequestRef request)
2456 {
2457     WKNotificationPermissionRequestAllow(request);
2458 }
2459
2460 void TestController::unavailablePluginButtonClicked(WKPageRef, WKPluginUnavailabilityReason, WKDictionaryRef, const void*)
2461 {
2462     printf("MISSING PLUGIN BUTTON PRESSED\n");
2463 }
2464
2465 void TestController::decidePolicyForNavigationAction(WKPageRef, WKNavigationActionRef navigationAction, WKFramePolicyListenerRef listener, WKTypeRef, const void* clientInfo)
2466 {
2467     static_cast<TestController*>(const_cast<void*>(clientInfo))->decidePolicyForNavigationAction(listener);
2468 }
2469
2470 void TestController::decidePolicyForNavigationAction(WKFramePolicyListenerRef listener)
2471 {
2472     WKRetainPtr<WKFramePolicyListenerRef> retainedListener { listener };
2473     const bool shouldIgnore { m_policyDelegateEnabled && !m_policyDelegatePermissive };
2474     auto decisionFunction = [shouldIgnore, retainedListener]() {
2475         if (shouldIgnore)
2476             WKFramePolicyListenerIgnore(retainedListener.get());
2477         else
2478             WKFramePolicyListenerUse(retainedListener.get());
2479     };
2480
2481     if (m_shouldDecideNavigationPolicyAfterDelay)
2482         RunLoop::main().dispatch(WTFMove(decisionFunction));
2483     else
2484         decisionFunction();
2485 }
2486
2487 void TestController::decidePolicyForNavigationResponse(WKPageRef, WKNavigationResponseRef navigationResponse, WKFramePolicyListenerRef listener, WKTypeRef, const void* clientInfo)
2488 {
2489     static_cast<TestController*>(const_cast<void*>(clientInfo))->decidePolicyForNavigationResponse(navigationResponse, listener);
2490 }
2491
2492 void TestController::decidePolicyForNavigationResponse(WKNavigationResponseRef navigationResponse, WKFramePolicyListenerRef listener)
2493 {
2494     WKRetainPtr<WKNavigationResponseRef> retainedNavigationResponse { navigationResponse };
2495     WKRetainPtr<WKFramePolicyListenerRef> retainedListener { listener };
2496
2497     bool shouldDownloadUndisplayableMIMETypes = m_shouldDownloadUndisplayableMIMETypes;
2498     auto decisionFunction = [shouldDownloadUndisplayableMIMETypes, retainedNavigationResponse, retainedListener]() {
2499         // Even though Response was already checked by WKBundlePagePolicyClient, the check did not include plugins
2500         // so we have to re-check again.
2501         if (WKNavigationResponseCanShowMIMEType(retainedNavigationResponse.get())) {
2502             WKFramePolicyListenerUse(retainedListener.get());
2503             return;
2504         }
2505
2506         if (shouldDownloadUndisplayableMIMETypes)
2507             WKFramePolicyListenerDownload(retainedListener.get());
2508         else
2509             WKFramePolicyListenerIgnore(retainedListener.get());
2510     };
2511
2512     if (m_shouldDecideResponsePolicyAfterDelay)
2513         RunLoop::main().dispatch(WTFMove(decisionFunction));
2514     else
2515         decisionFunction();
2516 }
2517
2518 void TestController::didNavigateWithNavigationData(WKContextRef, WKPageRef, WKNavigationDataRef navigationData, WKFrameRef frame, const void* clientInfo)
2519 {
2520     static_cast<TestController*>(const_cast<void*>(clientInfo))->didNavigateWithNavigationData(navigationData, frame);
2521 }
2522
2523 void TestController::didNavigateWithNavigationData(WKNavigationDataRef navigationData, WKFrameRef)
2524 {
2525     if (m_state != RunningTest)
2526         return;
2527
2528     if (!m_shouldLogHistoryClientCallbacks)
2529         return;
2530
2531     // URL
2532     WKRetainPtr<WKURLRef> urlWK = adoptWK(WKNavigationDataCopyURL(navigationData));
2533     WKRetainPtr<WKStringRef> urlStringWK = adoptWK(WKURLCopyString(urlWK.get()));
2534     // Title
2535     WKRetainPtr<WKStringRef> titleWK = adoptWK(WKNavigationDataCopyTitle(navigationData));
2536     // HTTP method
2537     WKRetainPtr<WKURLRequestRef> requestWK = adoptWK(WKNavigationDataCopyOriginalRequest(navigationData));
2538     WKRetainPtr<WKStringRef> methodWK = adoptWK(WKURLRequestCopyHTTPMethod(requestWK.get()));
2539
2540     // FIXME: Determine whether the navigation was successful / a client redirect rather than hard-coding the message here.
2541     m_currentInvocation->outputText(String::format("WebView navigated to url \"%s\" with title \"%s\" with HTTP equivalent method \"%s\".  The navigation was successful and was not a client redirect.\n",
2542         toSTD(urlStringWK).c_str(), toSTD(titleWK).c_str(), toSTD(methodWK).c_str()));
2543 }
2544
2545 void TestController::didPerformClientRedirect(WKContextRef, WKPageRef, WKURLRef sourceURL, WKURLRef destinationURL, WKFrameRef frame, const void* clientInfo)
2546 {
2547     static_cast<TestController*>(const_cast<void*>(clientInfo))->didPerformClientRedirect(sourceURL, destinationURL, frame);
2548 }
2549
2550 void TestController::didPerformClientRedirect(WKURLRef sourceURL, WKURLRef destinationURL, WKFrameRef)
2551 {
2552     if (m_state != RunningTest)
2553         return;
2554
2555     if (!m_shouldLogHistoryClientCallbacks)
2556         return;
2557
2558     WKRetainPtr<WKStringRef> sourceStringWK = adoptWK(WKURLCopyString(sourceURL));
2559     WKRetainPtr<WKStringRef> destinationStringWK = adoptWK(WKURLCopyString(destinationURL));
2560
2561     m_currentInvocation->outputText(String::format("WebView performed a client redirect from \"%s\" to \"%s\".\n", toSTD(sourceStringWK).c_str(), toSTD(destinationStringWK).c_str()));
2562 }
2563
2564 void TestController::didPerformServerRedirect(WKContextRef, WKPageRef, WKURLRef sourceURL, WKURLRef destinationURL, WKFrameRef frame, const void* clientInfo)
2565 {
2566     static_cast<TestController*>(const_cast<void*>(clientInfo))->didPerformServerRedirect(sourceURL, destinationURL, frame);
2567 }
2568
2569 void TestController::didPerformServerRedirect(WKURLRef sourceURL, WKURLRef destinationURL, WKFrameRef)
2570 {
2571     if (m_state != RunningTest)
2572         return;
2573
2574     if (!m_shouldLogHistoryClientCallbacks)
2575         return;
2576
2577     WKRetainPtr<WKStringRef> sourceStringWK = adoptWK(WKURLCopyString(sourceURL));
2578     WKRetainPtr<WKStringRef> destinationStringWK = adoptWK(WKURLCopyString(destinationURL));
2579
2580     m_currentInvocation->outputText(String::format("WebView performed a server redirect from \"%s\" to \"%s\".\n", toSTD(sourceStringWK).c_str(), toSTD(destinationStringWK).c_str()));
2581 }
2582
2583 void TestController::didUpdateHistoryTitle(WKContextRef, WKPageRef, WKStringRef title, WKURLRef URL, WKFrameRef frame, const void* clientInfo)
2584 {
2585     static_cast<TestController*>(const_cast<void*>(clientInfo))->didUpdateHistoryTitle(title, URL, frame);
2586 }
2587
2588 void TestController::didUpdateHistoryTitle(WKStringRef title, WKURLRef URL, WKFrameRef)
2589 {
2590     if (m_state != RunningTest)
2591         return;
2592
2593     if (!m_shouldLogHistoryClientCallbacks)
2594         return;
2595
2596     WKRetainPtr<WKStringRef> urlStringWK(AdoptWK, WKURLCopyString(URL));
2597     m_currentInvocation->outputText(String::format("WebView updated the title for history URL \"%s\" to \"%s\".\n", toSTD(urlStringWK).c_str(), toSTD(title).c_str()));
2598 }
2599
2600 void TestController::setNavigationGesturesEnabled(bool value)
2601 {
2602     m_mainWebView->setNavigationGesturesEnabled(value);
2603 }
2604
2605 void TestController::setIgnoresViewportScaleLimits(bool ignoresViewportScaleLimits)
2606 {
2607     WKPageSetIgnoresViewportScaleLimits(m_mainWebView->page(), ignoresViewportScaleLimits);
2608 }
2609
2610 void TestController::terminateNetworkProcess()
2611 {
2612     WKContextTerminateNetworkProcess(platformContext());
2613 }
2614
2615 void TestController::terminateServiceWorkerProcess()
2616 {
2617     WKContextTerminateServiceWorkerProcess(platformContext());
2618 }
2619
2620 #if !PLATFORM(COCOA)
2621 void TestController::platformWillRunTest(const TestInvocation&)
2622 {
2623 }
2624
2625 void TestController::platformCreateWebView(WKPageConfigurationRef configuration, const TestOptions& options)
2626 {
2627     m_mainWebView = std::make_unique<PlatformWebView>(configuration, options);
2628 }
2629
2630 PlatformWebView* TestController::platformCreateOtherPage(PlatformWebView* parentView, WKPageConfigurationRef configuration, const TestOptions& options)
2631 {
2632     return new PlatformWebView(configuration, options);
2633 }
2634
2635 WKContextRef TestController::platformAdjustContext(WKContextRef context, WKContextConfigurationRef contextConfiguration)
2636 {
2637     auto* dataStore = WKContextGetWebsiteDataStore(context);
2638     WKWebsiteDataStoreSetResourceLoadStatisticsEnabled(dataStore, true);
2639
2640     if (const char* dumpRenderTreeTemp = libraryPathForTesting()) {
2641         String temporaryFolder = String::fromUTF8(dumpRenderTreeTemp);
2642         const char separator = '/';
2643
2644         WKWebsiteDataStoreSetServiceWorkerRegistrationDirectory(dataStore, toWK(temporaryFolder + separator + "ServiceWorkers").get());
2645     }
2646
2647     return context;
2648 }
2649
2650 void TestController::platformResetStateToConsistentValues()
2651 {
2652 }
2653
2654 unsigned TestController::imageCountInGeneralPasteboard() const
2655 {
2656     return 0;
2657 }
2658
2659 void TestController::removeAllSessionCredentials()
2660 {
2661 }
2662
2663 void TestController::getAllStorageAccessEntries()
2664 {
2665 }
2666
2667 #endif
2668
2669 struct ClearServiceWorkerRegistrationsCallbackContext {
2670     explicit ClearServiceWorkerRegistrationsCallbackContext(TestController& controller)
2671         : testController(controller)
2672     {
2673     }
2674
2675     TestController& testController;
2676     bool done { false };
2677 };
2678
2679 static void clearServiceWorkerRegistrationsCallback(void* userData)
2680 {
2681     auto* context = static_cast<ClearServiceWorkerRegistrationsCallbackContext*>(userData);
2682     context->done = true;
2683     context->testController.notifyDone();
2684 }
2685
2686 void TestController::clearServiceWorkerRegistrations()
2687 {
2688     auto websiteDataStore = WKContextGetWebsiteDataStore(platformContext());
2689     ClearServiceWorkerRegistrationsCallbackContext context(*this);
2690
2691     WKWebsiteDataStoreRemoveAllServiceWorkerRegistrations(websiteDataStore, &context, clearServiceWorkerRegistrationsCallback);
2692     runUntil(context.done, noTimeout);
2693 }
2694
2695 struct ClearDOMCacheCallbackContext {
2696     explicit ClearDOMCacheCallbackContext(TestController& controller)
2697         : testController(controller)
2698     {
2699     }
2700
2701     TestController& testController;
2702     bool done { false };
2703 };
2704
2705 static void clearDOMCacheCallback(void* userData)
2706 {
2707     auto* context = static_cast<ClearDOMCacheCallbackContext*>(userData);
2708     context->done = true;
2709     context->testController.notifyDone();
2710 }
2711
2712 void TestController::clearDOMCache(WKStringRef origin)
2713 {
2714     auto websiteDataStore = WKContextGetWebsiteDataStore(platformContext());
2715     ClearDOMCacheCallbackContext context(*this);
2716
2717     auto cacheOrigin = adoptWK(WKSecurityOriginCreateFromString(origin));
2718     WKWebsiteDataStoreRemoveFetchCacheForOrigin(websiteDataStore, cacheOrigin.get(), &context, clearDOMCacheCallback);
2719     runUntil(context.done, noTimeout);
2720 }
2721
2722 void TestController::clearDOMCaches()
2723 {
2724     auto websiteDataStore = WKContextGetWebsiteDataStore(platformContext());
2725     ClearDOMCacheCallbackContext context(*this);
2726
2727     WKWebsiteDataStoreRemoveAllFetchCaches(websiteDataStore, &context, clearDOMCacheCallback);
2728     runUntil(context.done, noTimeout);
2729 }
2730
2731 struct FetchCacheOriginsCallbackContext {
2732     FetchCacheOriginsCallbackContext(TestController& controller, WKStringRef origin)
2733         : testController(controller)
2734         , origin(origin)
2735     {
2736     }
2737
2738     TestController& testController;
2739     WKStringRef origin;
2740
2741     bool done { false };
2742     bool result { false };
2743 };
2744
2745 static void fetchCacheOriginsCallback(WKArrayRef origins, void* userData)
2746 {
2747     auto* context = static_cast<FetchCacheOriginsCallbackContext*>(userData);
2748     context->done = true;
2749
2750     auto size = WKArrayGetSize(origins);
2751     for (size_t index = 0; index < size && !context->result; ++index) {
2752         WKSecurityOriginRef securityOrigin = reinterpret_cast<WKSecurityOriginRef>(WKArrayGetItemAtIndex(origins, index));
2753         if (WKStringIsEqual(context->origin, adoptWK(WKSecurityOriginCopyToString(securityOrigin)).get()))
2754             context->result = true;
2755     }
2756     context->testController.notifyDone();
2757 }
2758
2759 bool TestController::hasDOMCache(WKStringRef origin)
2760 {
2761     auto* dataStore = WKContextGetWebsiteDataStore(platformContext());
2762     FetchCacheOriginsCallbackContext context(*this, origin);
2763     WKWebsiteDataStoreGetFetchCacheOrigins(dataStore, &context, fetchCacheOriginsCallback);
2764     runUntil(context.done, noTimeout);
2765     return context.result;
2766 }
2767
2768 struct FetchCacheSizeForOriginCallbackContext {
2769     explicit FetchCacheSizeForOriginCallbackContext(TestController& controller)
2770         : testController(controller)
2771     {
2772     }
2773
2774     TestController& testController;
2775
2776     bool done { false };
2777     uint64_t result { 0 };
2778 };
2779
2780 static void fetchCacheSizeForOriginCallback(uint64_t size, void* userData)
2781 {
2782     auto* context = static_cast<FetchCacheSizeForOriginCallbackContext*>(userData);
2783     context->done = true;
2784     context->result = size;
2785     context->testController.notifyDone();
2786 }
2787
2788 uint64_t TestController::domCacheSize(WKStringRef origin)
2789 {
2790     auto* dataStore = WKContextGetWebsiteDataStore(platformContext());
2791     FetchCacheSizeForOriginCallbackContext context(*this);
2792     WKWebsiteDataStoreGetFetchCacheSizeForOrigin(dataStore, origin, &context, fetchCacheSizeForOriginCallback);
2793     runUntil(context.done, noTimeout);
2794     return context.result;
2795 }
2796
2797 struct ResourceStatisticsCallbackContext {
2798     explicit ResourceStatisticsCallbackContext(TestController& controller)
2799         : testController(controller)
2800     {
2801     }
2802
2803     TestController& testController;
2804     bool done { false };
2805     bool result { false };
2806     WKRetainPtr<WKStringRef> resourceLoadStatisticsRepresentation;
2807 };
2808     
2809 static void resourceStatisticsStringResultCallback(WKStringRef resourceLoadStatisticsRepresentation, void* userData)
2810 {
2811     auto* context = static_cast<ResourceStatisticsCallbackContext*>(userData);
2812     context->resourceLoadStatisticsRepresentation = resourceLoadStatisticsRepresentation;
2813     context->done = true;
2814     context->testController.notifyDone();
2815 }
2816
2817 static void resourceStatisticsVoidResultCallback(void* userData)
2818 {
2819     auto* context = static_cast<ResourceStatisticsCallbackContext*>(userData);
2820     context->done = true;
2821     context->testController.notifyDone();
2822 }
2823
2824 static void resourceStatisticsBooleanResultCallback(bool result, void* userData)
2825 {
2826     auto* context = static_cast<ResourceStatisticsCallbackContext*>(userData);
2827     context->result = result;
2828     context->done = true;
2829     context->testController.notifyDone();
2830 }
2831
2832 void TestController::setStatisticsDebugMode(bool value)
2833 {
2834     auto* dataStore = WKContextGetWebsiteDataStore(platformContext());
2835     ResourceStatisticsCallbackContext context(*this);
2836     WKWebsiteDataStoreSetResourceLoadStatisticsDebugModeWithCompletionHandler(dataStore, value, &context, resourceStatisticsVoidResultCallback);
2837     runUntil(context.done, noTimeout);
2838     m_currentInvocation->didSetStatisticsDebugMode();
2839 }
2840
2841 void TestController::setStatisticsPrevalentResourceForDebugMode(WKStringRef hostName)
2842 {
2843     auto* dataStore = WKContextGetWebsiteDataStore(platformContext());
2844     ResourceStatisticsCallbackContext context(*this);
2845     WKWebsiteDataStoreSetResourceLoadStatisticsPrevalentResourceForDebugMode(dataStore, hostName, &context, resourceStatisticsVoidResultCallback);
2846     runUntil(context.done, noTimeout);
2847     m_currentInvocation->didSetPrevalentResourceForDebugMode();
2848 }
2849
2850 void TestController::setStatisticsLastSeen(WKStringRef host, double seconds)
2851 {
2852     auto* dataStore = WKContextGetWebsiteDataStore(platformContext());
2853     ResourceStatisticsCallbackContext context(*this);
2854     WKWebsiteDataStoreSetStatisticsLastSeen(dataStore, host, seconds, &context, resourceStatisticsVoidResultCallback);
2855     runUntil(context.done, noTimeout);
2856     m_currentInvocation->didSetLastSeen();
2857 }
2858
2859 void TestController::setStatisticsPrevalentResource(WKStringRef host, bool value)
2860 {
2861     auto* dataStore = WKContextGetWebsiteDataStore(platformContext());
2862     ResourceStatisticsCallbackContext context(*this);
2863     WKWebsiteDataStoreSetStatisticsPrevalentResource(dataStore, host, value, &context, resourceStatisticsVoidResultCallback);
2864     runUntil(context.done, noTimeout);
2865     m_currentInvocation->didSetPrevalentResource();
2866 }
2867
2868 void TestController::setStatisticsVeryPrevalentResource(WKStringRef host, bool value)
2869 {
2870     auto* dataStore = WKContextGetWebsiteDataStore(platformContext());
2871     ResourceStatisticsCallbackContext context(*this);
2872     WKWebsiteDataStoreSetStatisticsVeryPrevalentResource(dataStore, host, value, &context, resourceStatisticsVoidResultCallback);
2873     runUntil(context.done, noTimeout);
2874     m_currentInvocation->didSetVeryPrevalentResource();
2875 }
2876     
2877 String TestController::dumpResourceLoadStatistics()
2878 {
2879     auto* dataStore = WKContextGetWebsiteDataStore(platformContext());
2880     ResourceStatisticsCallbackContext context(*this);
2881     WKWebsiteDataStoreDumpResourceLoadStatistics(dataStore, &context, resourceStatisticsStringResultCallback);
2882     runUntil(context.done, noTimeout);
2883     return toWTFString(context.resourceLoadStatisticsRepresentation.get());
2884 }
2885
2886 bool TestController::isStatisticsPrevalentResource(WKStringRef host)
2887 {
2888     auto* dataStore = WKContextGetWebsiteDataStore(platformContext());
2889     ResourceStatisticsCallbackContext context(*this);
2890     WKWebsiteDataStoreIsStatisticsPrevalentResource(dataStore, host, &context, resourceStatisticsBooleanResultCallback);
2891     runUntil(context.done, noTimeout);
2892     return context.result;
2893 }
2894
2895 bool TestController::isStatisticsVeryPrevalentResource(WKStringRef host)
2896 {
2897     auto* dataStore = WKContextGetWebsiteDataStore(platformContext());
2898     ResourceStatisticsCallbackContext context(*this);
2899     WKWebsiteDataStoreIsStatisticsVeryPrevalentResource(dataStore, host, &context, resourceStatisticsBooleanResultCallback);
2900     runUntil(context.done, noTimeout);
2901     return context.result;
2902 }
2903
2904 bool TestController::isStatisticsRegisteredAsSubresourceUnder(WKStringRef subresourceHost, WKStringRef topFrameHost)
2905 {
2906     auto* dataStore = WKContextGetWebsiteDataStore(platformContext());
2907     ResourceStatisticsCallbackContext context(*this);
2908     WKWebsiteDataStoreIsStatisticsRegisteredAsSubresourceUnder(dataStore, subresourceHost, topFrameHost, &context, resourceStatisticsBooleanResultCallback);
2909     runUntil(context.done, noTimeout);
2910     return context.result;
2911 }
2912
2913 bool TestController::isStatisticsRegisteredAsSubFrameUnder(WKStringRef subFrameHost, WKStringRef topFrameHost)
2914 {
2915     auto* dataStore = WKContextGetWebsiteDataStore(platformContext());
2916     ResourceStatisticsCallbackContext context(*this);
2917     WKWebsiteDataStoreIsStatisticsRegisteredAsSubFrameUnder(dataStore, subFrameHost, topFrameHost, &context, resourceStatisticsBooleanResultCallback);
2918     runUntil(context.done, noTimeout);
2919     return context.result;
2920 }
2921
2922 bool TestController::isStatisticsRegisteredAsRedirectingTo(WKStringRef hostRedirectedFrom, WKStringRef hostRedirectedTo)
2923 {
2924     auto* dataStore = WKContextGetWebsiteDataStore(platformContext());
2925     ResourceStatisticsCallbackContext context(*this);
2926     WKWebsiteDataStoreIsStatisticsRegisteredAsRedirectingTo(dataStore, hostRedirectedFrom, hostRedirectedTo, &context, resourceStatisticsBooleanResultCallback);
2927     runUntil(context.done, noTimeout);
2928     return context.result;
2929 }
2930
2931 void TestController::setStatisticsHasHadUserInteraction(WKStringRef host, bool value)
2932 {
2933     auto* dataStore = WKContextGetWebsiteDataStore(platformContext());
2934     ResourceStatisticsCallbackContext context(*this);
2935     WKWebsiteDataStoreSetStatisticsHasHadUserInteraction(dataStore, host, value, &context, resourceStatisticsVoidResultCallback);
2936     runUntil(context.done, noTimeout);
2937     m_currentInvocation->didSetHasHadUserInteraction();
2938 }
2939
2940 bool TestController::isStatisticsHasHadUserInteraction(WKStringRef host)
2941 {
2942     auto* dataStore = WKContextGetWebsiteDataStore(platformContext());
2943     ResourceStatisticsCallbackContext context(*this);
2944     WKWebsiteDataStoreIsStatisticsHasHadUserInteraction(dataStore, host, &context, resourceStatisticsBooleanResultCallback);
2945     runUntil(context.done, noTimeout);
2946     return context.result;
2947 }
2948
2949 void TestController::setStatisticsGrandfathered(WKStringRef host, bool value)
2950 {
2951     auto* dataStore = WKContextGetWebsiteDataStore(platformContext());
2952     WKWebsiteDataStoreSetStatisticsGrandfathered(dataStore, host, value);
2953 }
2954
2955 bool TestController::isStatisticsGrandfathered(WKStringRef host)
2956 {
2957     auto* dataStore = WKContextGetWebsiteDataStore(platformContext());
2958     ResourceStatisticsCallbackContext context(*this);
2959     WKWebsiteDataStoreIsStatisticsGrandfathered(dataStore, host, &context, resourceStatisticsBooleanResultCallback);
2960     runUntil(context.done, noTimeout);
2961     return context.result;
2962 }
2963
2964 void TestController::setStatisticsSubframeUnderTopFrameOrigin(WKStringRef host, WKStringRef topFrameHost)
2965 {
2966     auto* dataStore = WKContextGetWebsiteDataStore(platformContext());
2967     WKWebsiteDataStoreSetStatisticsSubframeUnderTopFrameOrigin(dataStore, host, topFrameHost);
2968 }
2969
2970 void TestController::setStatisticsSubresourceUnderTopFrameOrigin(WKStringRef host, WKStringRef topFrameHost)
2971 {
2972     auto* dataStore = WKContextGetWebsiteDataStore(platformContext());
2973     WKWebsiteDataStoreSetStatisticsSubresourceUnderTopFrameOrigin(dataStore, host, topFrameHost);
2974 }
2975
2976 void TestController::setStatisticsSubresourceUniqueRedirectTo(WKStringRef host, WKStringRef hostRedirectedTo)
2977 {
2978     auto* dataStore = WKContextGetWebsiteDataStore(platformContext());
2979     WKWebsiteDataStoreSetStatisticsSubresourceUniqueRedirectTo(dataStore, host, hostRedirectedTo);
2980 }
2981
2982 void TestController::setStatisticsSubresourceUniqueRedirectFrom(WKStringRef host, WKStringRef hostRedirectedFrom)
2983 {
2984     auto* dataStore = WKContextGetWebsiteDataStore(platformContext());
2985     WKWebsiteDataStoreSetStatisticsSubresourceUniqueRedirectFrom(dataStore, host, hostRedirectedFrom);
2986 }
2987
2988 void TestController::setStatisticsTopFrameUniqueRedirectTo(WKStringRef host, WKStringRef hostRedirectedTo)
2989 {
2990     auto* dataStore = WKContextGetWebsiteDataStore(platformContext());
2991     WKWebsiteDataStoreSetStatisticsTopFrameUniqueRedirectTo(dataStore, host, hostRedirectedTo);
2992 }
2993
2994 void TestController::setStatisticsTopFrameUniqueRedirectFrom(WKStringRef host, WKStringRef hostRedirectedFrom)
2995 {
2996     auto* dataStore = WKContextGetWebsiteDataStore(platformContext());
2997     WKWebsiteDataStoreSetStatisticsTopFrameUniqueRedirectFrom(dataStore, host, hostRedirectedFrom);
2998 }
2999
3000 void TestController::setStatisticsTimeToLiveUserInteraction(double seconds)
3001 {
3002     auto* dataStore = WKContextGetWebsiteDataStore(platformContext());
3003     WKWebsiteDataStoreSetStatisticsTimeToLiveUserInteraction(dataStore, seconds);
3004 }
3005
3006 void TestController::statisticsProcessStatisticsAndDataRecords()
3007 {
3008     auto* dataStore = WKContextGetWebsiteDataStore(platformContext());
3009     WKWebsiteDataStoreStatisticsProcessStatisticsAndDataRecords(dataStore);
3010 }
3011
3012 void TestController::statisticsUpdateCookieBlocking()
3013 {
3014     auto* dataStore = WKContextGetWebsiteDataStore(platformContext());
3015     ResourceStatisticsCallbackContext context(*this);
3016     WKWebsiteDataStoreStatisticsUpdateCookieBlocking(dataStore, &context, resourceStatisticsVoidResultCallback);
3017     runUntil(context.done, noTimeout);
3018     m_currentInvocation->didSetBlockCookiesForHost();
3019 }
3020
3021 void TestController::statisticsSubmitTelemetry()
3022 {
3023     auto* dataStore = WKContextGetWebsiteDataStore(platformContext());
3024     WKWebsiteDataStoreStatisticsSubmitTelemetry(dataStore);
3025 }
3026
3027 void TestController::setStatisticsNotifyPagesWhenDataRecordsWereScanned(bool value)
3028 {
3029     auto* dataStore = WKContextGetWebsiteDataStore(platformContext());
3030     WKWebsiteDataStoreSetStatisticsNotifyPagesWhenDataRecordsWereScanned(dataStore, value);
3031 }
3032
3033 void TestController::setStatisticsShouldClassifyResourcesBeforeDataRecordsRemoval(bool value)
3034 {
3035     auto* dataStore = WKContextGetWebsiteDataStore(platformContext());
3036     WKWebsiteDataStoreSetStatisticsShouldClassifyResourcesBeforeDataRecordsRemoval(dataStore, value);
3037 }
3038
3039 void TestController::setStatisticsNotifyPagesWhenTelemetryWasCaptured(bool value)
3040 {
3041     auto* dataStore = WKContextGetWebsiteDataStore(platformContext());
3042     WKWebsiteDataStoreSetStatisticsNotifyPagesWhenTelemetryWasCaptured(dataStore, value);
3043 }
3044
3045 void TestController::setStatisticsMinimumTimeBetweenDataRecordsRemoval(double seconds)
3046 {
3047     auto* dataStore = WKContextGetWebsiteDataStore(platformContext());
3048     WKWebsiteDataStoreSetStatisticsMinimumTimeBetweenDataRecordsRemoval(dataStore, seconds);
3049 }
3050
3051 void TestController::setStatisticsGrandfatheringTime(double seconds)
3052 {
3053     auto* dataStore = WKContextGetWebsiteDataStore(platformContext());
3054     WKWebsiteDataStoreSetStatisticsGrandfatheringTime(dataStore, seconds);
3055 }
3056
3057 void TestController::setStatisticsMaxStatisticsEntries(unsigned entries)
3058 {
3059     auto* dataStore = WKContextGetWebsiteDataStore(platformContext());
3060     WKWebsiteDataStoreSetStatisticsMaxStatisticsEntries(dataStore, entries);
3061 }
3062
3063 void TestController::setStatisticsPruneEntriesDownTo(unsigned entries)
3064 {
3065     auto* dataStore = WKContextGetWebsiteDataStore(platformContext());
3066     WKWebsiteDataStoreSetStatisticsPruneEntriesDownTo(dataStore, entries);
3067 }
3068
3069 void TestController::statisticsClearInMemoryAndPersistentStore()
3070 {
3071     auto* dataStore = WKContextGetWebsiteDataStore(platformContext());
3072     ResourceStatisticsCallbackContext context(*this);
3073     WKWebsiteDataStoreStatisticsClearInMemoryAndPersistentStore(dataStore, &context, resourceStatisticsVoidResultCallback);
3074     runUntil(context.done, noTimeout);
3075     m_currentInvocation->didClearStatisticsThroughWebsiteDataRemoval();
3076 }
3077
3078 void TestController::statisticsClearInMemoryAndPersistentStoreModifiedSinceHours(unsigned hours)
3079 {
3080     auto* dataStore = WKContextGetWebsiteDataStore(platformContext());
3081     ResourceStatisticsCallbackContext context(*this);
3082     WKWebsiteDataStoreStatisticsClearInMemoryAndPersistentStoreModifiedSinceHours(dataStore, hours, &context, resourceStatisticsVoidResultCallback);
3083     runUntil(context.done, noTimeout);
3084     m_currentInvocation->didClearStatisticsThroughWebsiteDataRemoval();
3085 }
3086
3087 void TestController::statisticsClearThroughWebsiteDataRemoval()
3088 {
3089     auto* dataStore = WKContextGetWebsiteDataStore(platformContext());
3090     ResourceStatisticsCallbackContext context(*this);
3091     WKWebsiteDataStoreStatisticsClearThroughWebsiteDataRemoval(dataStore, &context, resourceStatisticsVoidResultCallback);
3092     runUntil(context.done, noTimeout);
3093     m_currentInvocation->didClearStatisticsThroughWebsiteDataRemoval();
3094 }
3095
3096 void TestController::setStatisticsCacheMaxAgeCap(double seconds)
3097 {
3098     auto* dataStore = WKContextGetWebsiteDataStore(platformContext());
3099     ResourceStatisticsCallbackContext context(*this);
3100     WKWebsiteDataStoreSetStatisticsCacheMaxAgeCap(dataStore, seconds, &context, resourceStatisticsVoidResultCallback);
3101     runUntil(context.done, noTimeout);
3102 }
3103
3104 void TestController::statisticsResetToConsistentState()
3105 {
3106     auto* dataStore = WKContextGetWebsiteDataStore(platformContext());
3107     ResourceStatisticsCallbackContext context(*this);
3108     WKWebsiteDataStoreStatisticsResetToConsistentState(dataStore, &context, resourceStatisticsVoidResultCallback);
3109     runUntil(context.done, noTimeout);
3110     m_currentInvocation->didResetStatisticsToConsistentState();
3111 }
3112
3113 void TestController::addMockMediaDevice(WKStringRef persistentID, WKStringRef label, WKStringRef type)
3114 {
3115     WKAddMockMediaDevice(platformContext(), persistentID, label, type);
3116 }
3117
3118 void TestController::clearMockMediaDevices()
3119 {
3120     WKClearMockMediaDevices(platformContext());
3121 }
3122
3123 void TestController::removeMockMediaDevice(WKStringRef persistentID)
3124 {
3125     WKRemoveMockMediaDevice(platformContext(), persistentID);
3126 }
3127
3128 void TestController::resetMockMediaDevices()
3129 {
3130     WKResetMockMediaDevices(platformContext());
3131 }
3132
3133 #if !PLATFORM(COCOA)
3134 void TestController::platformAddTestOptions(TestOptions&) const
3135 {
3136 }
3137
3138 void TestController::injectUserScript(WKStringRef)
3139 {
3140 }
3141
3142 void TestController::addTestKeyToKeychain(const String&, const String&, const String&)
3143 {
3144 }
3145
3146 void TestController::cleanUpKeychain(const String&)
3147 {
3148 }
3149
3150 bool TestController::keyExistsInKeychain(const String&, const String&)
3151 {
3152     return false;
3153 }
3154 #endif
3155
3156 void TestController::sendDisplayConfigurationChangedMessageForTesting()
3157 {
3158     WKSendDisplayConfigurationChangedMessageForTesting(platformContext());
3159 }
3160
3161 void TestController::setWebAuthenticationMockConfiguration(WKDictionaryRef configuration)
3162 {
3163     WKWebsiteDataStoreSetWebAuthenticationMockConfiguration(WKContextGetWebsiteDataStore(platformContext()), configuration);
3164 }
3165
3166 } // namespace WTR