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