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