Swipe gesture tests don't work on macOS Sierra
[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         ASSERT_NOT_REACHED();
1319     }
1320
1321     if (!m_currentInvocation)
1322         return;
1323
1324     m_currentInvocation->didReceiveMessageFromInjectedBundle(messageName, messageBody);
1325 }
1326
1327 WKRetainPtr<WKTypeRef> TestController::didReceiveSynchronousMessageFromInjectedBundle(WKStringRef messageName, WKTypeRef messageBody)
1328 {
1329     if (WKStringIsEqualToUTF8CString(messageName, "EventSender")) {
1330         if (m_state != RunningTest)
1331             return nullptr;
1332
1333         ASSERT(WKGetTypeID(messageBody) == WKDictionaryGetTypeID());
1334         WKDictionaryRef messageBodyDictionary = static_cast<WKDictionaryRef>(messageBody);
1335
1336         WKRetainPtr<WKStringRef> subMessageKey(AdoptWK, WKStringCreateWithUTF8CString("SubMessage"));
1337         WKStringRef subMessageName = static_cast<WKStringRef>(WKDictionaryGetItemForKey(messageBodyDictionary, subMessageKey.get()));
1338
1339         if (WKStringIsEqualToUTF8CString(subMessageName, "KeyDown")) {
1340             didReceiveKeyDownMessageFromInjectedBundle(messageBodyDictionary, true);
1341
1342             return 0;
1343         }
1344
1345         if (WKStringIsEqualToUTF8CString(subMessageName, "MouseDown") || WKStringIsEqualToUTF8CString(subMessageName, "MouseUp")) {
1346             WKRetainPtr<WKStringRef> buttonKey = adoptWK(WKStringCreateWithUTF8CString("Button"));
1347             unsigned button = static_cast<unsigned>(WKUInt64GetValue(static_cast<WKUInt64Ref>(WKDictionaryGetItemForKey(messageBodyDictionary, buttonKey.get()))));
1348
1349             WKRetainPtr<WKStringRef> modifiersKey = adoptWK(WKStringCreateWithUTF8CString("Modifiers"));
1350             WKEventModifiers modifiers = static_cast<WKEventModifiers>(WKUInt64GetValue(static_cast<WKUInt64Ref>(WKDictionaryGetItemForKey(messageBodyDictionary, modifiersKey.get()))));
1351
1352             // Forward to WebProcess
1353             if (WKStringIsEqualToUTF8CString(subMessageName, "MouseDown"))
1354                 m_eventSenderProxy->mouseDown(button, modifiers);
1355             else
1356                 m_eventSenderProxy->mouseUp(button, modifiers);
1357             return 0;
1358         }
1359
1360         if (WKStringIsEqualToUTF8CString(subMessageName, "MouseMoveTo")) {
1361             WKRetainPtr<WKStringRef> xKey = adoptWK(WKStringCreateWithUTF8CString("X"));
1362             double x = WKDoubleGetValue(static_cast<WKDoubleRef>(WKDictionaryGetItemForKey(messageBodyDictionary, xKey.get())));
1363
1364             WKRetainPtr<WKStringRef> yKey = adoptWK(WKStringCreateWithUTF8CString("Y"));
1365             double y = WKDoubleGetValue(static_cast<WKDoubleRef>(WKDictionaryGetItemForKey(messageBodyDictionary, yKey.get())));
1366
1367             // Forward to WebProcess
1368             m_eventSenderProxy->mouseMoveTo(x, y);
1369             return 0;
1370         }
1371
1372 #if PLATFORM(MAC)
1373         if (WKStringIsEqualToUTF8CString(subMessageName, "MouseForceClick")) {
1374             m_eventSenderProxy->mouseForceClick();
1375             return 0;
1376         }
1377
1378         if (WKStringIsEqualToUTF8CString(subMessageName, "StartAndCancelMouseForceClick")) {
1379             m_eventSenderProxy->startAndCancelMouseForceClick();
1380             return 0;
1381         }
1382
1383         if (WKStringIsEqualToUTF8CString(subMessageName, "MouseForceDown")) {
1384             m_eventSenderProxy->mouseForceDown();
1385             return 0;
1386         }
1387
1388         if (WKStringIsEqualToUTF8CString(subMessageName, "MouseForceUp")) {
1389             m_eventSenderProxy->mouseForceUp();
1390             return 0;
1391         }
1392
1393         if (WKStringIsEqualToUTF8CString(subMessageName, "MouseForceChanged")) {
1394             WKRetainPtr<WKStringRef> forceKey = adoptWK(WKStringCreateWithUTF8CString("Force"));
1395             double force = WKDoubleGetValue(static_cast<WKDoubleRef>(WKDictionaryGetItemForKey(messageBodyDictionary, forceKey.get())));
1396
1397             m_eventSenderProxy->mouseForceChanged(force);
1398             return 0;
1399         }
1400 #endif // PLATFORM(MAC)
1401
1402         if (WKStringIsEqualToUTF8CString(subMessageName, "ContinuousMouseScrollBy")) {
1403             WKRetainPtr<WKStringRef> xKey = adoptWK(WKStringCreateWithUTF8CString("X"));
1404             double x = WKDoubleGetValue(static_cast<WKDoubleRef>(WKDictionaryGetItemForKey(messageBodyDictionary, xKey.get())));
1405
1406             WKRetainPtr<WKStringRef> yKey = adoptWK(WKStringCreateWithUTF8CString("Y"));
1407             double y = WKDoubleGetValue(static_cast<WKDoubleRef>(WKDictionaryGetItemForKey(messageBodyDictionary, yKey.get())));
1408
1409             WKRetainPtr<WKStringRef> pagedKey = adoptWK(WKStringCreateWithUTF8CString("Paged"));
1410             bool paged = static_cast<bool>(WKUInt64GetValue(static_cast<WKUInt64Ref>(WKDictionaryGetItemForKey(messageBodyDictionary, pagedKey.get()))));
1411
1412             // Forward to WebProcess
1413             m_eventSenderProxy->continuousMouseScrollBy(x, y, paged);
1414             return 0;
1415         }
1416
1417         if (WKStringIsEqualToUTF8CString(subMessageName, "LeapForward")) {
1418             WKRetainPtr<WKStringRef> timeKey = adoptWK(WKStringCreateWithUTF8CString("TimeInMilliseconds"));
1419             unsigned time = static_cast<unsigned>(WKUInt64GetValue(static_cast<WKUInt64Ref>(WKDictionaryGetItemForKey(messageBodyDictionary, timeKey.get()))));
1420
1421             m_eventSenderProxy->leapForward(time);
1422             return 0;
1423         }
1424
1425 #if ENABLE(TOUCH_EVENTS)
1426         if (WKStringIsEqualToUTF8CString(subMessageName, "AddTouchPoint")) {
1427             WKRetainPtr<WKStringRef> xKey = adoptWK(WKStringCreateWithUTF8CString("X"));
1428             int x = static_cast<int>(WKUInt64GetValue(static_cast<WKUInt64Ref>(WKDictionaryGetItemForKey(messageBodyDictionary, xKey.get()))));
1429
1430             WKRetainPtr<WKStringRef> yKey = adoptWK(WKStringCreateWithUTF8CString("Y"));
1431             int y = static_cast<int>(WKUInt64GetValue(static_cast<WKUInt64Ref>(WKDictionaryGetItemForKey(messageBodyDictionary, yKey.get()))));
1432
1433             m_eventSenderProxy->addTouchPoint(x, y);
1434             return 0;
1435         }
1436
1437         if (WKStringIsEqualToUTF8CString(subMessageName, "UpdateTouchPoint")) {
1438             WKRetainPtr<WKStringRef> indexKey = adoptWK(WKStringCreateWithUTF8CString("Index"));
1439             int index = static_cast<int>(WKUInt64GetValue(static_cast<WKUInt64Ref>(WKDictionaryGetItemForKey(messageBodyDictionary, indexKey.get()))));
1440
1441             WKRetainPtr<WKStringRef> xKey = adoptWK(WKStringCreateWithUTF8CString("X"));
1442             int x = static_cast<int>(WKUInt64GetValue(static_cast<WKUInt64Ref>(WKDictionaryGetItemForKey(messageBodyDictionary, xKey.get()))));
1443
1444             WKRetainPtr<WKStringRef> yKey = adoptWK(WKStringCreateWithUTF8CString("Y"));
1445             int y = static_cast<int>(WKUInt64GetValue(static_cast<WKUInt64Ref>(WKDictionaryGetItemForKey(messageBodyDictionary, yKey.get()))));
1446
1447             m_eventSenderProxy->updateTouchPoint(index, x, y);
1448             return 0;
1449         }
1450
1451         if (WKStringIsEqualToUTF8CString(subMessageName, "SetTouchModifier")) {
1452             WKRetainPtr<WKStringRef> modifierKey = adoptWK(WKStringCreateWithUTF8CString("Modifier"));
1453             WKEventModifiers modifier = static_cast<WKEventModifiers>(WKUInt64GetValue(static_cast<WKUInt64Ref>(WKDictionaryGetItemForKey(messageBodyDictionary, modifierKey.get()))));
1454
1455             WKRetainPtr<WKStringRef> enableKey = adoptWK(WKStringCreateWithUTF8CString("Enable"));
1456             bool enable = static_cast<bool>(WKUInt64GetValue(static_cast<WKUInt64Ref>(WKDictionaryGetItemForKey(messageBodyDictionary, enableKey.get()))));
1457
1458             m_eventSenderProxy->setTouchModifier(modifier, enable);
1459             return 0;
1460         }
1461
1462         if (WKStringIsEqualToUTF8CString(subMessageName, "SetTouchPointRadius")) {
1463             WKRetainPtr<WKStringRef> xKey = adoptWK(WKStringCreateWithUTF8CString("RadiusX"));
1464             int x = static_cast<int>(WKUInt64GetValue(static_cast<WKUInt64Ref>(WKDictionaryGetItemForKey(messageBodyDictionary, xKey.get()))));
1465
1466             WKRetainPtr<WKStringRef> yKey = adoptWK(WKStringCreateWithUTF8CString("RadiusY"));
1467             int y = static_cast<int>(WKUInt64GetValue(static_cast<WKUInt64Ref>(WKDictionaryGetItemForKey(messageBodyDictionary, yKey.get()))));
1468
1469             m_eventSenderProxy->setTouchPointRadius(x, y);
1470             return 0;
1471         }
1472
1473         if (WKStringIsEqualToUTF8CString(subMessageName, "TouchStart")) {
1474             m_eventSenderProxy->touchStart();
1475             return 0;
1476         }
1477
1478         if (WKStringIsEqualToUTF8CString(subMessageName, "TouchMove")) {
1479             m_eventSenderProxy->touchMove();
1480             return 0;
1481         }
1482
1483         if (WKStringIsEqualToUTF8CString(subMessageName, "TouchEnd")) {
1484             m_eventSenderProxy->touchEnd();
1485             return 0;
1486         }
1487
1488         if (WKStringIsEqualToUTF8CString(subMessageName, "TouchCancel")) {
1489             m_eventSenderProxy->touchCancel();
1490             return 0;
1491         }
1492
1493         if (WKStringIsEqualToUTF8CString(subMessageName, "ClearTouchPoints")) {
1494             m_eventSenderProxy->clearTouchPoints();
1495             return 0;
1496         }
1497
1498         if (WKStringIsEqualToUTF8CString(subMessageName, "ReleaseTouchPoint")) {
1499             WKRetainPtr<WKStringRef> indexKey = adoptWK(WKStringCreateWithUTF8CString("Index"));
1500             int index = static_cast<int>(WKUInt64GetValue(static_cast<WKUInt64Ref>(WKDictionaryGetItemForKey(messageBodyDictionary, indexKey.get()))));
1501             m_eventSenderProxy->releaseTouchPoint(index);
1502             return 0;
1503         }
1504
1505         if (WKStringIsEqualToUTF8CString(subMessageName, "CancelTouchPoint")) {
1506             WKRetainPtr<WKStringRef> indexKey = adoptWK(WKStringCreateWithUTF8CString("Index"));
1507             int index = static_cast<int>(WKUInt64GetValue(static_cast<WKUInt64Ref>(WKDictionaryGetItemForKey(messageBodyDictionary, indexKey.get()))));
1508             m_eventSenderProxy->cancelTouchPoint(index);
1509             return 0;
1510         }
1511 #endif
1512         ASSERT_NOT_REACHED();
1513     }
1514     return m_currentInvocation->didReceiveSynchronousMessageFromInjectedBundle(messageName, messageBody);
1515 }
1516
1517 WKRetainPtr<WKTypeRef> TestController::getInjectedBundleInitializationUserData()
1518 {
1519     return nullptr;
1520 }
1521
1522 // WKContextClient
1523
1524 void TestController::networkProcessDidCrash()
1525 {
1526     pid_t pid = WKContextGetNetworkProcessIdentifier(m_context.get());
1527     fprintf(stderr, "#CRASHED - %s (pid %ld)\n", networkProcessName(), static_cast<long>(pid));
1528     exit(1);
1529 }
1530
1531 void TestController::databaseProcessDidCrash()
1532 {
1533     pid_t pid = WKContextGetDatabaseProcessIdentifier(m_context.get());
1534     fprintf(stderr, "#CRASHED - %s (pid %ld)\n", databaseProcessName(), static_cast<long>(pid));
1535     exit(1);
1536 }
1537
1538 // WKPageNavigationClient
1539
1540 void TestController::didCommitNavigation(WKPageRef page, WKNavigationRef navigation, WKTypeRef, const void* clientInfo)
1541 {
1542     static_cast<TestController*>(const_cast<void*>(clientInfo))->didCommitNavigation(page, navigation);
1543 }
1544
1545 void TestController::didFinishNavigation(WKPageRef page, WKNavigationRef navigation, WKTypeRef, const void* clientInfo)
1546 {
1547     static_cast<TestController*>(const_cast<void*>(clientInfo))->didFinishNavigation(page, navigation);
1548 }
1549
1550 bool TestController::canAuthenticateAgainstProtectionSpace(WKPageRef page, WKProtectionSpaceRef protectionSpace, const void* clientInfo)
1551 {
1552     return static_cast<TestController*>(const_cast<void*>(clientInfo))->canAuthenticateAgainstProtectionSpace(page, protectionSpace);
1553 }
1554
1555 void TestController::didReceiveAuthenticationChallenge(WKPageRef page, WKAuthenticationChallengeRef authenticationChallenge, const void *clientInfo)
1556 {
1557     static_cast<TestController*>(const_cast<void*>(clientInfo))->didReceiveAuthenticationChallenge(page, /*frame,*/ authenticationChallenge);
1558 }
1559
1560 void TestController::processDidCrash(WKPageRef page, const void* clientInfo)
1561 {
1562     static_cast<TestController*>(const_cast<void*>(clientInfo))->processDidCrash();
1563 }
1564
1565 void TestController::didBeginNavigationGesture(WKPageRef page, const void *clientInfo)
1566 {
1567     static_cast<TestController*>(const_cast<void*>(clientInfo))->didBeginNavigationGesture(page);
1568 }
1569
1570 void TestController::willEndNavigationGesture(WKPageRef page, WKBackForwardListItemRef backForwardListItem, const void *clientInfo)
1571 {
1572     static_cast<TestController*>(const_cast<void*>(clientInfo))->willEndNavigationGesture(page, backForwardListItem);
1573 }
1574
1575 void TestController::didEndNavigationGesture(WKPageRef page, WKBackForwardListItemRef backForwardListItem, const void *clientInfo)
1576 {
1577     static_cast<TestController*>(const_cast<void*>(clientInfo))->didEndNavigationGesture(page, backForwardListItem);
1578 }
1579
1580 void TestController::didRemoveNavigationGestureSnapshot(WKPageRef page, const void *clientInfo)
1581 {
1582     static_cast<TestController*>(const_cast<void*>(clientInfo))->didRemoveNavigationGestureSnapshot(page);
1583 }
1584
1585 WKPluginLoadPolicy TestController::decidePolicyForPluginLoad(WKPageRef page, WKPluginLoadPolicy currentPluginLoadPolicy, WKDictionaryRef pluginInformation, WKStringRef* unavailabilityDescription, const void* clientInfo)
1586 {
1587     return static_cast<TestController*>(const_cast<void*>(clientInfo))->decidePolicyForPluginLoad(page, currentPluginLoadPolicy, pluginInformation, unavailabilityDescription);
1588 }
1589
1590 WKPluginLoadPolicy TestController::decidePolicyForPluginLoad(WKPageRef, WKPluginLoadPolicy currentPluginLoadPolicy, WKDictionaryRef pluginInformation, WKStringRef* unavailabilityDescription)
1591 {
1592     if (m_shouldBlockAllPlugins)
1593         return kWKPluginLoadPolicyBlocked;
1594
1595 #if PLATFORM(MAC)
1596     WKStringRef bundleIdentifier = (WKStringRef)WKDictionaryGetItemForKey(pluginInformation, WKPluginInformationBundleIdentifierKey());
1597     if (!bundleIdentifier)
1598         return currentPluginLoadPolicy;
1599
1600     if (WKStringIsEqualToUTF8CString(bundleIdentifier, "com.apple.QuickTime Plugin.plugin"))
1601         return currentPluginLoadPolicy;
1602
1603     if (WKStringIsEqualToUTF8CString(bundleIdentifier, "com.apple.testnetscapeplugin"))
1604         return currentPluginLoadPolicy;
1605
1606     RELEASE_ASSERT_NOT_REACHED(); // Please don't use any other plug-ins in tests, as they will not be installed on all machines.
1607 #else
1608     return currentPluginLoadPolicy;
1609 #endif
1610 }
1611
1612 void TestController::didCommitNavigation(WKPageRef page, WKNavigationRef navigation)
1613 {
1614     mainWebView()->focus();
1615 }
1616
1617 bool TestController::canAuthenticateAgainstProtectionSpace(WKPageRef page, WKProtectionSpaceRef protectionSpace)
1618 {
1619     if (m_shouldLogCanAuthenticateAgainstProtectionSpace)
1620         m_currentInvocation->outputText("canAuthenticateAgainstProtectionSpace\n");
1621     WKProtectionSpaceAuthenticationScheme authenticationScheme = WKProtectionSpaceGetAuthenticationScheme(protectionSpace);
1622     
1623     if (authenticationScheme == kWKProtectionSpaceAuthenticationSchemeServerTrustEvaluationRequested) {
1624         std::string host = toSTD(adoptWK(WKProtectionSpaceCopyHost(protectionSpace)).get());
1625         return host == "localhost" || host == "127.0.0.1";
1626     }
1627     
1628     return authenticationScheme <= kWKProtectionSpaceAuthenticationSchemeHTTPDigest;
1629 }
1630
1631 void TestController::didFinishNavigation(WKPageRef page, WKNavigationRef navigation)
1632 {
1633     if (m_state != Resetting)
1634         return;
1635
1636     WKRetainPtr<WKURLRef> wkURL(AdoptWK, WKFrameCopyURL(WKPageGetMainFrame(page)));
1637     if (!WKURLIsEqual(wkURL.get(), blankURL()))
1638         return;
1639
1640     m_doneResetting = true;
1641     singleton().notifyDone();
1642 }
1643
1644 void TestController::didReceiveAuthenticationChallenge(WKPageRef page, WKAuthenticationChallengeRef authenticationChallenge)
1645 {
1646     WKProtectionSpaceRef protectionSpace = WKAuthenticationChallengeGetProtectionSpace(authenticationChallenge);
1647     WKAuthenticationDecisionListenerRef decisionListener = WKAuthenticationChallengeGetDecisionListener(authenticationChallenge);
1648
1649     if (WKProtectionSpaceGetAuthenticationScheme(protectionSpace) == kWKProtectionSpaceAuthenticationSchemeServerTrustEvaluationRequested) {
1650         // Any non-empty credential signals to accept the server trust. Since the cross-platform API
1651         // doesn't expose a way to create a credential from server trust, we use a password credential.
1652
1653         WKRetainPtr<WKCredentialRef> credential = adoptWK(WKCredentialCreate(toWK("accept server trust").get(), toWK("").get(), kWKCredentialPersistenceNone));
1654         WKAuthenticationDecisionListenerUseCredential(decisionListener, credential.get());
1655         return;
1656     }
1657
1658     if (m_rejectsProtectionSpaceAndContinueForAuthenticationChallenges) {
1659         m_currentInvocation->outputText("Simulating reject protection space and continue for authentication challenge\n");
1660         WKAuthenticationDecisionListenerRejectProtectionSpaceAndContinue(decisionListener);
1661         return;
1662     }
1663
1664     std::string host = toSTD(adoptWK(WKProtectionSpaceCopyHost(protectionSpace)).get());
1665     int port = WKProtectionSpaceGetPort(protectionSpace);
1666     String message = String::format("%s:%d - didReceiveAuthenticationChallenge - ", host.c_str(), port);
1667     if (!m_handlesAuthenticationChallenges)
1668         message.append("Simulating cancelled authentication sheet\n");
1669     else
1670         message.append(String::format("Responding with %s:%s\n", m_authenticationUsername.utf8().data(), m_authenticationPassword.utf8().data()));
1671     m_currentInvocation->outputText(message);
1672
1673     if (!m_handlesAuthenticationChallenges) {
1674         WKAuthenticationDecisionListenerUseCredential(decisionListener, 0);
1675         return;
1676     }
1677     WKRetainPtr<WKStringRef> username(AdoptWK, WKStringCreateWithUTF8CString(m_authenticationUsername.utf8().data()));
1678     WKRetainPtr<WKStringRef> password(AdoptWK, WKStringCreateWithUTF8CString(m_authenticationPassword.utf8().data()));
1679     WKRetainPtr<WKCredentialRef> credential(AdoptWK, WKCredentialCreate(username.get(), password.get(), kWKCredentialPersistenceForSession));
1680     WKAuthenticationDecisionListenerUseCredential(decisionListener, credential.get());
1681 }
1682
1683     
1684 // WKContextDownloadClient
1685
1686 void TestController::downloadDidStart(WKContextRef context, WKDownloadRef download, const void* clientInfo)
1687 {
1688     static_cast<TestController*>(const_cast<void*>(clientInfo))->downloadDidStart(context, download);
1689 }
1690     
1691 WKStringRef TestController::decideDestinationWithSuggestedFilename(WKContextRef context, WKDownloadRef download, WKStringRef filename, bool* allowOverwrite, const void* clientInfo)
1692 {
1693     return static_cast<TestController*>(const_cast<void*>(clientInfo))->decideDestinationWithSuggestedFilename(context, download, filename, allowOverwrite);
1694 }
1695
1696 void TestController::downloadDidFinish(WKContextRef context, WKDownloadRef download, const void* clientInfo)
1697 {
1698     static_cast<TestController*>(const_cast<void*>(clientInfo))->downloadDidFinish(context, download);
1699 }
1700
1701 void TestController::downloadDidFail(WKContextRef context, WKDownloadRef download, WKErrorRef error, const void* clientInfo)
1702 {
1703     static_cast<TestController*>(const_cast<void*>(clientInfo))->downloadDidFail(context, download, error);
1704 }
1705
1706 void TestController::downloadDidCancel(WKContextRef context, WKDownloadRef download, const void* clientInfo)
1707 {
1708     static_cast<TestController*>(const_cast<void*>(clientInfo))->downloadDidCancel(context, download);
1709 }
1710
1711 void TestController::downloadDidStart(WKContextRef context, WKDownloadRef download)
1712 {
1713     m_currentInvocation->outputText("Download started.\n");
1714 }
1715
1716 WKStringRef TestController::decideDestinationWithSuggestedFilename(WKContextRef, WKDownloadRef, WKStringRef filename, bool*& allowOverwrite)
1717 {
1718     String suggestedFilename = toWTFString(filename);
1719
1720     StringBuilder builder;
1721     builder.append("Downloading URL with suggested filename \"");
1722     builder.append(suggestedFilename);
1723     builder.append("\"\n");
1724
1725     m_currentInvocation->outputText(builder.toString());
1726
1727     const char* dumpRenderTreeTemp = libraryPathForTesting();
1728     if (!dumpRenderTreeTemp)
1729         return nullptr;
1730
1731     *allowOverwrite = true;
1732     String temporaryFolder = String::fromUTF8(dumpRenderTreeTemp);
1733     if (suggestedFilename.isEmpty())
1734         suggestedFilename = "Unknown";
1735
1736     return toWK(temporaryFolder + "/" + suggestedFilename).leakRef();
1737 }
1738
1739 void TestController::downloadDidFinish(WKContextRef, WKDownloadRef)
1740 {
1741     m_currentInvocation->outputText("Download completed.\n");
1742     m_currentInvocation->notifyDownloadDone();
1743 }
1744
1745 void TestController::downloadDidFail(WKContextRef, WKDownloadRef, WKErrorRef error)
1746 {
1747     String message = String::format("Download failed.\n");
1748     m_currentInvocation->outputText(message);
1749     
1750     WKRetainPtr<WKStringRef> errorDomain = adoptWK(WKErrorCopyDomain(error));
1751     WKRetainPtr<WKStringRef> errorDescription = adoptWK(WKErrorCopyLocalizedDescription(error));
1752     int errorCode = WKErrorGetErrorCode(error);
1753
1754     StringBuilder errorBuilder;
1755     errorBuilder.append("Failed: ");
1756     errorBuilder.append(toWTFString(errorDomain));
1757     errorBuilder.append(", code=");
1758     errorBuilder.appendNumber(errorCode);
1759     errorBuilder.append(", description=");
1760     errorBuilder.append(toWTFString(errorDescription));
1761     errorBuilder.append("\n");
1762
1763     m_currentInvocation->outputText(errorBuilder.toString());
1764     m_currentInvocation->notifyDownloadDone();
1765 }
1766
1767 void TestController::downloadDidCancel(WKContextRef, WKDownloadRef)
1768 {
1769     m_currentInvocation->outputText("Download cancelled.\n");
1770     m_currentInvocation->notifyDownloadDone();
1771 }
1772
1773 void TestController::processDidCrash()
1774 {
1775     // This function can be called multiple times when crash logs are being saved on Windows, so
1776     // ensure we only print the crashed message once.
1777     if (!m_didPrintWebProcessCrashedMessage) {
1778         pid_t pid = WKPageGetProcessIdentifier(m_mainWebView->page());
1779         fprintf(stderr, "#CRASHED - %s (pid %ld)\n", webProcessName(), static_cast<long>(pid));
1780         fflush(stderr);
1781         m_didPrintWebProcessCrashedMessage = true;
1782     }
1783
1784     if (m_shouldExitWhenWebProcessCrashes)
1785         exit(1);
1786 }
1787
1788 void TestController::didBeginNavigationGesture(WKPageRef)
1789 {
1790     m_currentInvocation->didBeginSwipe();
1791 }
1792
1793 void TestController::willEndNavigationGesture(WKPageRef, WKBackForwardListItemRef)
1794 {
1795     m_currentInvocation->willEndSwipe();
1796 }
1797
1798 void TestController::didEndNavigationGesture(WKPageRef, WKBackForwardListItemRef)
1799 {
1800     m_currentInvocation->didEndSwipe();
1801 }
1802
1803 void TestController::didRemoveNavigationGestureSnapshot(WKPageRef)
1804 {
1805     m_currentInvocation->didRemoveSwipeSnapshot();
1806 }
1807
1808 void TestController::simulateWebNotificationClick(uint64_t notificationID)
1809 {
1810     m_webNotificationProvider.simulateWebNotificationClick(mainWebView()->page(), notificationID);
1811 }
1812
1813 void TestController::setGeolocationPermission(bool enabled)
1814 {
1815     m_isGeolocationPermissionSet = true;
1816     m_isGeolocationPermissionAllowed = enabled;
1817     decidePolicyForGeolocationPermissionRequestIfPossible();
1818 }
1819
1820 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)
1821 {
1822     m_geolocationProvider->setPosition(latitude, longitude, accuracy, providesAltitude, altitude, providesAltitudeAccuracy, altitudeAccuracy, providesHeading, heading, providesSpeed, speed);
1823 }
1824
1825 void TestController::setMockGeolocationPositionUnavailableError(WKStringRef errorMessage)
1826 {
1827     m_geolocationProvider->setPositionUnavailableError(errorMessage);
1828 }
1829
1830 void TestController::handleGeolocationPermissionRequest(WKGeolocationPermissionRequestRef geolocationPermissionRequest)
1831 {
1832     m_geolocationPermissionRequests.append(geolocationPermissionRequest);
1833     decidePolicyForGeolocationPermissionRequestIfPossible();
1834 }
1835
1836 bool TestController::isGeolocationProviderActive() const
1837 {
1838     return m_geolocationProvider->isActive();
1839 }
1840
1841 static String originUserVisibleName(WKSecurityOriginRef origin)
1842 {
1843     if (!origin)
1844         return emptyString();
1845
1846     std::string host = toSTD(adoptWK(WKSecurityOriginCopyHost(origin))).c_str();
1847     std::string protocol = toSTD(adoptWK(WKSecurityOriginCopyProtocol(origin))).c_str();
1848
1849     if (!host.length() || !protocol.length())
1850         return emptyString();
1851
1852     unsigned short port = WKSecurityOriginGetPort(origin);
1853     if (port)
1854         return String::format("%s://%s:%d", protocol.c_str(), host.c_str(), port);
1855
1856     return String::format("%s://%s", protocol.c_str(), host.c_str());
1857 }
1858
1859 static String userMediaOriginHash(WKSecurityOriginRef userMediaDocumentOrigin, WKSecurityOriginRef topLevelDocumentOrigin)
1860 {
1861     String userMediaDocumentOriginString = originUserVisibleName(userMediaDocumentOrigin);
1862     String topLevelDocumentOriginString = originUserVisibleName(topLevelDocumentOrigin);
1863
1864     if (topLevelDocumentOriginString.isEmpty())
1865         return userMediaDocumentOriginString;
1866
1867     return String::format("%s-%s", userMediaDocumentOriginString.utf8().data(), topLevelDocumentOriginString.utf8().data());
1868 }
1869
1870 static String userMediaOriginHash(WKStringRef userMediaDocumentOriginString, WKStringRef topLevelDocumentOriginString)
1871 {
1872     auto userMediaDocumentOrigin = adoptWK(WKSecurityOriginCreateFromString(userMediaDocumentOriginString));
1873     if (!WKStringGetLength(topLevelDocumentOriginString))
1874         return userMediaOriginHash(userMediaDocumentOrigin.get(), nullptr);
1875
1876     auto topLevelDocumentOrigin = adoptWK(WKSecurityOriginCreateFromString(topLevelDocumentOriginString));
1877     return userMediaOriginHash(userMediaDocumentOrigin.get(), topLevelDocumentOrigin.get());
1878 }
1879
1880 void TestController::setUserMediaPermission(bool enabled)
1881 {
1882     m_isUserMediaPermissionSet = true;
1883     m_isUserMediaPermissionAllowed = enabled;
1884     decidePolicyForUserMediaPermissionRequestIfPossible();
1885 }
1886
1887 static String createCanonicalUUIDString()
1888 {
1889     unsigned randomData[4];
1890     cryptographicallyRandomValues(reinterpret_cast<unsigned char*>(randomData), sizeof(randomData));
1891
1892     // Format as Version 4 UUID.
1893     StringBuilder builder;
1894     builder.reserveCapacity(36);
1895     appendUnsignedAsHexFixedSize(randomData[0], builder, 8, Lowercase);
1896     builder.append('-');
1897     appendUnsignedAsHexFixedSize(randomData[1] >> 16, builder, 4, Lowercase);
1898     builder.appendLiteral("-4");
1899     appendUnsignedAsHexFixedSize(randomData[1] & 0x00000fff, builder, 3, Lowercase);
1900     builder.append('-');
1901     appendUnsignedAsHexFixedSize((randomData[2] >> 30) | 0x8, builder, 1, Lowercase);
1902     appendUnsignedAsHexFixedSize((randomData[2] >> 16) & 0x00000fff, builder, 3, Lowercase);
1903     builder.append('-');
1904     appendUnsignedAsHexFixedSize(randomData[2] & 0x0000ffff, builder, 4, Lowercase);
1905     appendUnsignedAsHexFixedSize(randomData[3], builder, 8, Lowercase);
1906     return builder.toString();
1907 }
1908
1909 class OriginSettings : public RefCounted<OriginSettings> {
1910 public:
1911     explicit OriginSettings()
1912     {
1913     }
1914
1915     bool persistentPermission() const { return m_persistentPermission; }
1916     void setPersistentPermission(bool permission) { m_persistentPermission = permission; }
1917
1918     String persistentSalt() const { return m_persistentSalt; }
1919     void setPersistentSalt(const String& salt) { m_persistentSalt = salt; }
1920
1921     HashMap<uint64_t, String>& ephemeralSalts() { return m_ephemeralSalts; }
1922
1923     void incrementRequestCount() { ++m_requestCount; }
1924     void resetRequestCount() { m_requestCount = 0; }
1925     unsigned requestCount() const { return m_requestCount; }
1926
1927 private:
1928     HashMap<uint64_t, String> m_ephemeralSalts;
1929     String m_persistentSalt;
1930     unsigned m_requestCount { 0 };
1931     bool m_persistentPermission { false };
1932 };
1933
1934 String TestController::saltForOrigin(WKFrameRef frame, String originHash)
1935 {
1936     auto& settings = settingsForOrigin(originHash);
1937     auto& ephemeralSalts = settings.ephemeralSalts();
1938     auto frameInfo = adoptWK(WKFrameCreateFrameInfo(frame));
1939     auto frameHandle = WKFrameInfoGetFrameHandleRef(frameInfo.get());
1940     uint64_t frameIdentifier = WKFrameHandleGetFrameID(frameHandle);
1941     String frameSalt = ephemeralSalts.get(frameIdentifier);
1942
1943     if (settings.persistentPermission()) {
1944         if (frameSalt.length())
1945             return frameSalt;
1946
1947         if (!settings.persistentSalt().length())
1948             settings.setPersistentSalt(createCanonicalUUIDString());
1949
1950         return settings.persistentSalt();
1951     }
1952
1953     if (!frameSalt.length()) {
1954         frameSalt = createCanonicalUUIDString();
1955         ephemeralSalts.add(frameIdentifier, frameSalt);
1956     }
1957
1958     return frameSalt;
1959 }
1960
1961 void TestController::setUserMediaPersistentPermissionForOrigin(bool permission, WKStringRef userMediaDocumentOriginString, WKStringRef topLevelDocumentOriginString)
1962 {
1963     auto originHash = userMediaOriginHash(userMediaDocumentOriginString, topLevelDocumentOriginString);
1964     auto& settings = settingsForOrigin(originHash);
1965     settings.setPersistentPermission(permission);
1966 }
1967
1968 void TestController::handleCheckOfUserMediaPermissionForOrigin(WKFrameRef frame, WKSecurityOriginRef userMediaDocumentOrigin, WKSecurityOriginRef topLevelDocumentOrigin, const WKUserMediaPermissionCheckRef& checkRequest)
1969 {
1970     auto originHash = userMediaOriginHash(userMediaDocumentOrigin, topLevelDocumentOrigin);
1971     auto salt = saltForOrigin(frame, originHash);
1972     WKRetainPtr<WKStringRef> saltString = adoptWK(WKStringCreateWithUTF8CString(salt.utf8().data()));
1973
1974     WKUserMediaPermissionCheckSetUserMediaAccessInfo(checkRequest, saltString.get(), settingsForOrigin(originHash).persistentPermission());
1975 }
1976
1977 void TestController::handleUserMediaPermissionRequest(WKFrameRef frame, WKSecurityOriginRef userMediaDocumentOrigin, WKSecurityOriginRef topLevelDocumentOrigin, WKUserMediaPermissionRequestRef request)
1978 {
1979     auto originHash = userMediaOriginHash(userMediaDocumentOrigin, topLevelDocumentOrigin);
1980     m_userMediaPermissionRequests.append(std::make_pair(originHash, request));
1981     decidePolicyForUserMediaPermissionRequestIfPossible();
1982 }
1983
1984 OriginSettings& TestController::settingsForOrigin(const String& originHash)
1985 {
1986     RefPtr<OriginSettings> settings = m_cachedUserMediaPermissions.get(originHash);
1987     if (!settings) {
1988         settings = adoptRef(*new OriginSettings());
1989         m_cachedUserMediaPermissions.add(originHash, settings);
1990     }
1991
1992     return *settings;
1993 }
1994
1995 unsigned TestController::userMediaPermissionRequestCountForOrigin(WKStringRef userMediaDocumentOriginString, WKStringRef topLevelDocumentOriginString)
1996 {
1997     auto originHash = userMediaOriginHash(userMediaDocumentOriginString, topLevelDocumentOriginString);
1998     return settingsForOrigin(originHash).requestCount();
1999 }
2000
2001 void TestController::resetUserMediaPermissionRequestCountForOrigin(WKStringRef userMediaDocumentOriginString, WKStringRef topLevelDocumentOriginString)
2002 {
2003     auto originHash = userMediaOriginHash(userMediaDocumentOriginString, topLevelDocumentOriginString);
2004     settingsForOrigin(originHash).resetRequestCount();
2005 }
2006
2007 void TestController::decidePolicyForUserMediaPermissionRequestIfPossible()
2008 {
2009     if (!m_isUserMediaPermissionSet)
2010         return;
2011
2012     for (auto& pair : m_userMediaPermissionRequests) {
2013         auto originHash = pair.first;
2014         auto request = pair.second.get();
2015
2016         auto& settings = settingsForOrigin(originHash);
2017         settings.incrementRequestCount();
2018
2019         if (!m_isUserMediaPermissionAllowed && !settings.persistentPermission()) {
2020             WKUserMediaPermissionRequestDeny(request, kWKPermissionDenied);
2021             continue;
2022         }
2023
2024         WKRetainPtr<WKArrayRef> audioDeviceUIDs = adoptWK(WKUserMediaPermissionRequestAudioDeviceUIDs(request));
2025         WKRetainPtr<WKArrayRef> videoDeviceUIDs = adoptWK(WKUserMediaPermissionRequestVideoDeviceUIDs(request));
2026
2027         if (!WKArrayGetSize(videoDeviceUIDs.get()) && !WKArrayGetSize(audioDeviceUIDs.get())) {
2028             WKUserMediaPermissionRequestDeny(request, kWKNoConstraints);
2029             continue;
2030         }
2031
2032         WKRetainPtr<WKStringRef> videoDeviceUID;
2033         if (WKArrayGetSize(videoDeviceUIDs.get()))
2034             videoDeviceUID = reinterpret_cast<WKStringRef>(WKArrayGetItemAtIndex(videoDeviceUIDs.get(), 0));
2035         else
2036             videoDeviceUID = adoptWK(WKStringCreateWithUTF8CString(""));
2037
2038         WKRetainPtr<WKStringRef> audioDeviceUID;
2039         if (WKArrayGetSize(audioDeviceUIDs.get()))
2040             audioDeviceUID = reinterpret_cast<WKStringRef>(WKArrayGetItemAtIndex(audioDeviceUIDs.get(), 0));
2041         else
2042             audioDeviceUID = adoptWK(WKStringCreateWithUTF8CString(""));
2043
2044         WKUserMediaPermissionRequestAllow(request, audioDeviceUID.get(), videoDeviceUID.get());
2045     }
2046     m_userMediaPermissionRequests.clear();
2047 }
2048
2049 void TestController::setCustomPolicyDelegate(bool enabled, bool permissive)
2050 {
2051     m_policyDelegateEnabled = enabled;
2052     m_policyDelegatePermissive = permissive;
2053 }
2054
2055 void TestController::decidePolicyForGeolocationPermissionRequestIfPossible()
2056 {
2057     if (!m_isGeolocationPermissionSet)
2058         return;
2059
2060     for (size_t i = 0; i < m_geolocationPermissionRequests.size(); ++i) {
2061         WKGeolocationPermissionRequestRef permissionRequest = m_geolocationPermissionRequests[i].get();
2062         if (m_isGeolocationPermissionAllowed)
2063             WKGeolocationPermissionRequestAllow(permissionRequest);
2064         else
2065             WKGeolocationPermissionRequestDeny(permissionRequest);
2066     }
2067     m_geolocationPermissionRequests.clear();
2068 }
2069
2070 void TestController::decidePolicyForNotificationPermissionRequest(WKPageRef page, WKSecurityOriginRef origin, WKNotificationPermissionRequestRef request, const void*)
2071 {
2072     TestController::singleton().decidePolicyForNotificationPermissionRequest(page, origin, request);
2073 }
2074
2075 void TestController::decidePolicyForNotificationPermissionRequest(WKPageRef, WKSecurityOriginRef, WKNotificationPermissionRequestRef request)
2076 {
2077     WKNotificationPermissionRequestAllow(request);
2078 }
2079
2080 void TestController::unavailablePluginButtonClicked(WKPageRef, WKPluginUnavailabilityReason, WKDictionaryRef, const void*)
2081 {
2082     printf("MISSING PLUGIN BUTTON PRESSED\n");
2083 }
2084
2085 void TestController::decidePolicyForNavigationAction(WKPageRef, WKNavigationActionRef navigationAction, WKFramePolicyListenerRef listener, WKTypeRef, const void* clientInfo)
2086 {
2087     static_cast<TestController*>(const_cast<void*>(clientInfo))->decidePolicyForNavigationAction(listener);
2088 }
2089
2090 void TestController::decidePolicyForNavigationAction(WKFramePolicyListenerRef listener)
2091 {
2092     WKRetainPtr<WKFramePolicyListenerRef> retainedListener { listener };
2093     const bool shouldIgnore { m_policyDelegateEnabled && !m_policyDelegatePermissive };
2094     auto decisionFunction = [shouldIgnore, retainedListener]() {
2095         if (shouldIgnore)
2096             WKFramePolicyListenerIgnore(retainedListener.get());
2097         else
2098             WKFramePolicyListenerUse(retainedListener.get());
2099     };
2100
2101     if (m_shouldDecideNavigationPolicyAfterDelay)
2102         RunLoop::main().dispatch(WTFMove(decisionFunction));
2103     else
2104         decisionFunction();
2105 }
2106
2107 void TestController::decidePolicyForNavigationResponse(WKPageRef, WKNavigationResponseRef navigationResponse, WKFramePolicyListenerRef listener, WKTypeRef, const void* clientInfo)
2108 {
2109     static_cast<TestController*>(const_cast<void*>(clientInfo))->decidePolicyForNavigationResponse(navigationResponse, listener);
2110 }
2111
2112 void TestController::decidePolicyForNavigationResponse(WKNavigationResponseRef navigationResponse, WKFramePolicyListenerRef listener)
2113 {
2114     // Even though Response was already checked by WKBundlePagePolicyClient, the check did not include plugins
2115     // so we have to re-check again.
2116     if (WKNavigationResponseCanShowMIMEType(navigationResponse)) {
2117         WKFramePolicyListenerUse(listener);
2118         return;
2119     }
2120
2121     if (m_shouldDownloadUndisplayableMIMETypes)
2122         WKFramePolicyListenerDownload(listener);
2123     else
2124         WKFramePolicyListenerIgnore(listener);
2125 }
2126
2127 void TestController::didNavigateWithNavigationData(WKContextRef, WKPageRef, WKNavigationDataRef navigationData, WKFrameRef frame, const void* clientInfo)
2128 {
2129     static_cast<TestController*>(const_cast<void*>(clientInfo))->didNavigateWithNavigationData(navigationData, frame);
2130 }
2131
2132 void TestController::didNavigateWithNavigationData(WKNavigationDataRef navigationData, WKFrameRef)
2133 {
2134     if (m_state != RunningTest)
2135         return;
2136
2137     if (!m_shouldLogHistoryClientCallbacks)
2138         return;
2139
2140     // URL
2141     WKRetainPtr<WKURLRef> urlWK = adoptWK(WKNavigationDataCopyURL(navigationData));
2142     WKRetainPtr<WKStringRef> urlStringWK = adoptWK(WKURLCopyString(urlWK.get()));
2143     // Title
2144     WKRetainPtr<WKStringRef> titleWK = adoptWK(WKNavigationDataCopyTitle(navigationData));
2145     // HTTP method
2146     WKRetainPtr<WKURLRequestRef> requestWK = adoptWK(WKNavigationDataCopyOriginalRequest(navigationData));
2147     WKRetainPtr<WKStringRef> methodWK = adoptWK(WKURLRequestCopyHTTPMethod(requestWK.get()));
2148
2149     // FIXME: Determine whether the navigation was successful / a client redirect rather than hard-coding the message here.
2150     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",
2151         toSTD(urlStringWK).c_str(), toSTD(titleWK).c_str(), toSTD(methodWK).c_str()));
2152 }
2153
2154 void TestController::didPerformClientRedirect(WKContextRef, WKPageRef, WKURLRef sourceURL, WKURLRef destinationURL, WKFrameRef frame, const void* clientInfo)
2155 {
2156     static_cast<TestController*>(const_cast<void*>(clientInfo))->didPerformClientRedirect(sourceURL, destinationURL, frame);
2157 }
2158
2159 void TestController::didPerformClientRedirect(WKURLRef sourceURL, WKURLRef destinationURL, WKFrameRef)
2160 {
2161     if (m_state != RunningTest)
2162         return;
2163
2164     if (!m_shouldLogHistoryClientCallbacks)
2165         return;
2166
2167     WKRetainPtr<WKStringRef> sourceStringWK = adoptWK(WKURLCopyString(sourceURL));
2168     WKRetainPtr<WKStringRef> destinationStringWK = adoptWK(WKURLCopyString(destinationURL));
2169
2170     m_currentInvocation->outputText(String::format("WebView performed a client redirect from \"%s\" to \"%s\".\n", toSTD(sourceStringWK).c_str(), toSTD(destinationStringWK).c_str()));
2171 }
2172
2173 void TestController::didPerformServerRedirect(WKContextRef, WKPageRef, WKURLRef sourceURL, WKURLRef destinationURL, WKFrameRef frame, const void* clientInfo)
2174 {
2175     static_cast<TestController*>(const_cast<void*>(clientInfo))->didPerformServerRedirect(sourceURL, destinationURL, frame);
2176 }
2177
2178 void TestController::didPerformServerRedirect(WKURLRef sourceURL, WKURLRef destinationURL, WKFrameRef)
2179 {
2180     if (m_state != RunningTest)
2181         return;
2182
2183     if (!m_shouldLogHistoryClientCallbacks)
2184         return;
2185
2186     WKRetainPtr<WKStringRef> sourceStringWK = adoptWK(WKURLCopyString(sourceURL));
2187     WKRetainPtr<WKStringRef> destinationStringWK = adoptWK(WKURLCopyString(destinationURL));
2188
2189     m_currentInvocation->outputText(String::format("WebView performed a server redirect from \"%s\" to \"%s\".\n", toSTD(sourceStringWK).c_str(), toSTD(destinationStringWK).c_str()));
2190 }
2191
2192 void TestController::didUpdateHistoryTitle(WKContextRef, WKPageRef, WKStringRef title, WKURLRef URL, WKFrameRef frame, const void* clientInfo)
2193 {
2194     static_cast<TestController*>(const_cast<void*>(clientInfo))->didUpdateHistoryTitle(title, URL, frame);
2195 }
2196
2197 void TestController::didUpdateHistoryTitle(WKStringRef title, WKURLRef URL, WKFrameRef)
2198 {
2199     if (m_state != RunningTest)
2200         return;
2201
2202     if (!m_shouldLogHistoryClientCallbacks)
2203         return;
2204
2205     WKRetainPtr<WKStringRef> urlStringWK(AdoptWK, WKURLCopyString(URL));
2206     m_currentInvocation->outputText(String::format("WebView updated the title for history URL \"%s\" to \"%s\".\n", toSTD(urlStringWK).c_str(), toSTD(title).c_str()));
2207 }
2208
2209 void TestController::setNavigationGesturesEnabled(bool value)
2210 {
2211     m_mainWebView->setNavigationGesturesEnabled(value);
2212 }
2213
2214 void TestController::setIgnoresViewportScaleLimits(bool ignoresViewportScaleLimits)
2215 {
2216     WKPageSetIgnoresViewportScaleLimits(m_mainWebView->page(), ignoresViewportScaleLimits);
2217 }
2218
2219 void TestController::setStatisticsPrevalentResource(WKStringRef hostName, bool value)
2220 {
2221     WKResourceLoadStatisticsManagerSetPrevalentResource(hostName, value);
2222 }
2223
2224 bool TestController::isStatisticsPrevalentResource(WKStringRef hostName)
2225 {
2226     return WKResourceLoadStatisticsManagerIsPrevalentResource(hostName);
2227 }
2228
2229 void TestController::setStatisticsHasHadUserInteraction(WKStringRef hostName, bool value)
2230 {
2231     WKResourceLoadStatisticsManagerSetHasHadUserInteraction(hostName, value);
2232 }
2233
2234 bool TestController::isStatisticsHasHadUserInteraction(WKStringRef hostName)
2235 {
2236     return WKResourceLoadStatisticsManagerIsHasHadUserInteraction(hostName);
2237 }
2238
2239 void TestController::setStatisticsSubframeUnderTopFrameOrigin(WKStringRef hostName, WKStringRef topFrameHostName)
2240 {
2241     WKResourceLoadStatisticsManagerSetSubframeUnderTopFrameOrigin(hostName, topFrameHostName);
2242 }
2243
2244 void TestController::setStatisticsSubresourceUnderTopFrameOrigin(WKStringRef hostName, WKStringRef topFrameHostName)
2245 {
2246     WKResourceLoadStatisticsManagerSetSubresourceUnderTopFrameOrigin(hostName, topFrameHostName);
2247 }
2248     
2249 void TestController::setStatisticsSubresourceUniqueRedirectTo(WKStringRef hostName, WKStringRef hostNameRedirectedTo)
2250 {
2251     WKResourceLoadStatisticsManagerSetSubresourceUniqueRedirectTo(hostName, hostNameRedirectedTo);
2252 }
2253
2254 void TestController::setStatisticsTimeToLiveUserInteraction(double seconds)
2255 {
2256     WKResourceLoadStatisticsManagerSetTimeToLiveUserInteraction(seconds);
2257 }
2258
2259 void TestController::statisticsFireDataModificationHandler()
2260 {
2261     WKResourceLoadStatisticsManagerFireDataModificationHandler();
2262 }
2263     
2264 void TestController::statisticsFireShouldPartitionCookiesHandler(WKStringRef hostName, bool value)
2265 {
2266     WKResourceLoadStatisticsManagerFireShouldPartitionCookiesHandler(hostName, value);
2267 }
2268
2269 void TestController::setStatisticsNotifyPagesWhenDataRecordsWereScanned(bool value)
2270 {
2271     WKResourceLoadStatisticsManagerSetNotifyPagesWhenDataRecordsWereScanned(value);
2272 }
2273     
2274 void TestController::setStatisticsShouldClassifyResourcesBeforeDataRecordsRemoval(bool value)
2275 {
2276     WKResourceLoadStatisticsManagerSetShouldClassifyResourcesBeforeDataRecordsRemoval(value);
2277 }
2278
2279 void TestController::setStatisticsMinimumTimeBetweeenDataRecordsRemoval(double seconds)
2280 {
2281     WKResourceLoadStatisticsManagerSetMinimumTimeBetweeenDataRecordsRemoval(seconds);
2282 }
2283
2284 void TestController::statisticsClearInMemoryAndPersistentStore()
2285 {
2286     WKResourceLoadStatisticsManagerClearInMemoryAndPersistentStore();
2287 }
2288
2289 void TestController::statisticsResetToConsistentState()
2290 {
2291     WKResourceLoadStatisticsManagerResetToConsistentState();
2292 }
2293
2294 void TestController::terminateNetworkProcess()
2295 {
2296     WKContextTerminateNetworkProcess(platformContext());
2297 }
2298
2299 #if !PLATFORM(COCOA)
2300 void TestController::platformWillRunTest(const TestInvocation&)
2301 {
2302 }
2303
2304 void TestController::platformCreateWebView(WKPageConfigurationRef configuration, const TestOptions& options)
2305 {
2306     m_mainWebView = std::make_unique<PlatformWebView>(configuration, options);
2307 }
2308
2309 PlatformWebView* TestController::platformCreateOtherPage(PlatformWebView* parentView, WKPageConfigurationRef configuration, const TestOptions& options)
2310 {
2311     return new PlatformWebView(configuration, options);
2312 }
2313
2314 WKContextRef TestController::platformAdjustContext(WKContextRef context, WKContextConfigurationRef contextConfiguration)
2315 {
2316     return context;
2317 }
2318
2319 void TestController::platformResetStateToConsistentValues()
2320 {
2321
2322 }
2323
2324 unsigned TestController::imageCountInGeneralPasteboard() const
2325 {
2326     return 0;
2327 }
2328
2329 #endif
2330
2331 } // namespace WTR