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