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