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