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