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