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