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