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