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