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