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