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