AX: Implement word related text marker functions using TextIterator
[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     WKContextClearCachedCredentials(TestController::singleton().context());
718
719     // FIXME: This function should also ensure that there is only one page open.
720
721     // Reset the EventSender for each test.
722     m_eventSenderProxy = std::make_unique<EventSenderProxy>(this);
723
724     // FIXME: Is this needed? Nothing in TestController changes preferences during tests, and if there is
725     // some other code doing this, it should probably be responsible for cleanup too.
726     resetPreferencesToConsistentValues();
727
728 #if !PLATFORM(COCOA)
729     WKTextCheckerContinuousSpellCheckingEnabledStateChanged(true);
730 #endif
731
732     // In the case that a test using the chrome input field failed, be sure to clean up for the next test.
733     m_mainWebView->removeChromeInputField();
734     m_mainWebView->focus();
735
736     // Re-set to the default backing scale factor by setting the custom scale factor to 0.
737     WKPageSetCustomBackingScaleFactor(m_mainWebView->page(), 0);
738
739     WKPageClearWheelEventTestTrigger(m_mainWebView->page());
740
741 #if PLATFORM(EFL)
742     // EFL use a real window while other ports such as Qt don't.
743     // In EFL, we need to resize the window to the original size after calls to window.resizeTo.
744     WKRect rect = m_mainWebView->windowFrame();
745     m_mainWebView->setWindowFrame(WKRectMake(rect.origin.x, rect.origin.y, TestController::viewWidth, TestController::viewHeight));
746 #endif
747
748     // Reset notification permissions
749     m_webNotificationProvider.reset();
750
751     // Reset Geolocation permissions.
752     m_geolocationPermissionRequests.clear();
753     m_isGeolocationPermissionSet = false;
754     m_isGeolocationPermissionAllowed = false;
755
756     // Reset UserMedia permissions.
757     m_userMediaPermissionRequests.clear();
758     m_userMediaOriginPermissions = nullptr;
759     m_isUserMediaPermissionSet = false;
760     m_isUserMediaPermissionAllowed = false;
761
762     // Reset Custom Policy Delegate.
763     setCustomPolicyDelegate(false, false);
764
765     m_workQueueManager.clearWorkQueue();
766
767     m_handlesAuthenticationChallenges = false;
768     m_authenticationUsername = String();
769     m_authenticationPassword = String();
770
771     m_shouldBlockAllPlugins = false;
772
773     m_shouldLogHistoryClientCallbacks = false;
774
775     setHidden(false);
776
777     platformResetStateToConsistentValues();
778
779     // Reset main page back to about:blank
780     m_doneResetting = false;
781
782     m_shouldDecideNavigationPolicyAfterDelay = false;
783
784     setNavigationGesturesEnabled(false);
785
786     WKPageLoadURL(m_mainWebView->page(), blankURL());
787     runUntil(m_doneResetting, shortTimeout);
788     return m_doneResetting;
789 }
790
791 void TestController::terminateWebContentProcess()
792 {
793     WKPageTerminate(m_mainWebView->page());
794 }
795
796 void TestController::reattachPageToWebProcess()
797 {
798     // Loading a web page is the only way to reattach an existing page to a process.
799     m_doneResetting = false;
800     WKPageLoadURL(m_mainWebView->page(), blankURL());
801     runUntil(m_doneResetting, shortTimeout);
802 }
803
804 const char* TestController::webProcessName()
805 {
806     // FIXME: Find a way to not hardcode the process name.
807 #if PLATFORM(COCOA)
808     return "com.apple.WebKit.WebContent.Development";
809 #else
810     return "WebProcess";
811 #endif
812 }
813
814 const char* TestController::networkProcessName()
815 {
816     // FIXME: Find a way to not hardcode the process name.
817 #if PLATFORM(COCOA)
818     return "com.apple.WebKit.Networking.Development";
819 #else
820     return "NetworkProcess";
821 #endif
822 }
823
824 static std::string testPath(WKURLRef url)
825 {
826     auto scheme = adoptWK(WKURLCopyScheme(url));
827     if (WKStringIsEqualToUTF8CStringIgnoringCase(scheme.get(), "file")) {
828         auto path = adoptWK(WKURLCopyPath(url));
829         auto buffer = std::vector<char>(WKStringGetMaximumUTF8CStringSize(path.get()));
830         auto length = WKStringGetUTF8CString(path.get(), buffer.data(), buffer.size());
831         return std::string(buffer.data(), length);
832     }
833     return std::string();
834 }
835
836 static WKURLRef createTestURL(const char* pathOrURL)
837 {
838     if (strstr(pathOrURL, "http://") || strstr(pathOrURL, "https://") || strstr(pathOrURL, "file://"))
839         return WKURLCreateWithUTF8CString(pathOrURL);
840
841     // Creating from filesytem path.
842     size_t length = strlen(pathOrURL);
843     if (!length)
844         return 0;
845
846     const char separator = '/';
847     bool isAbsolutePath = pathOrURL[0] == separator;
848     const char* filePrefix = "file://";
849     static const size_t prefixLength = strlen(filePrefix);
850
851     std::unique_ptr<char[]> buffer;
852     if (isAbsolutePath) {
853         buffer = std::make_unique<char[]>(prefixLength + length + 1);
854         strcpy(buffer.get(), filePrefix);
855         strcpy(buffer.get() + prefixLength, pathOrURL);
856     } else {
857         buffer = std::make_unique<char[]>(prefixLength + PATH_MAX + length + 2); // 1 for the separator
858         strcpy(buffer.get(), filePrefix);
859         if (!getcwd(buffer.get() + prefixLength, PATH_MAX))
860             return 0;
861         size_t numCharacters = strlen(buffer.get());
862         buffer[numCharacters] = separator;
863         strcpy(buffer.get() + numCharacters + 1, pathOrURL);
864     }
865
866     return WKURLCreateWithUTF8CString(buffer.get());
867 }
868
869 static bool parseBooleanTestHeaderValue(const std::string& value)
870 {
871     if (value == "true")
872         return true;
873     if (value == "false")
874         return false;
875
876     LOG_ERROR("Found unexpected value '%s' for boolean option. Expected 'true' or 'false'.", value.c_str());
877     return false;
878 }
879
880 static void updateTestOptionsFromTestHeader(TestOptions& testOptions, const std::string& pathOrURL)
881 {
882     // Gross. Need to reduce conversions between all the string types and URLs.
883     WKRetainPtr<WKURLRef> wkURL(AdoptWK, createTestURL(pathOrURL.c_str()));
884     std::string filename = testPath(wkURL.get());
885     if (filename.empty())
886         return;
887
888     std::string options;
889     std::ifstream testFile(filename.data());
890     if (!testFile.good())
891         return;
892     getline(testFile, options);
893     std::string beginString("webkit-test-runner [ ");
894     std::string endString(" ]");
895     size_t beginLocation = options.find(beginString);
896     if (beginLocation == std::string::npos)
897         return;
898     size_t endLocation = options.find(endString, beginLocation);
899     if (endLocation == std::string::npos) {
900         LOG_ERROR("Could not find end of test header in %s", filename.c_str());
901         return;
902     }
903     std::string pairString = options.substr(beginLocation + beginString.size(), endLocation - (beginLocation + beginString.size()));
904     size_t pairStart = 0;
905     while (pairStart < pairString.size()) {
906         size_t pairEnd = pairString.find(" ", pairStart);
907         if (pairEnd == std::string::npos)
908             pairEnd = pairString.size();
909         size_t equalsLocation = pairString.find("=", pairStart);
910         if (equalsLocation == std::string::npos) {
911             LOG_ERROR("Malformed option in test header (could not find '=' character) in %s", filename.c_str());
912             break;
913         }
914         auto key = pairString.substr(pairStart, equalsLocation - pairStart);
915         auto value = pairString.substr(equalsLocation + 1, pairEnd - (equalsLocation + 1));
916         if (key == "language")
917             String(value.c_str()).split(",", false, testOptions.overrideLanguages);
918         if (key == "useThreadedScrolling")
919             testOptions.useThreadedScrolling = parseBooleanTestHeaderValue(value);
920         if (key == "useFlexibleViewport")
921             testOptions.useFlexibleViewport = parseBooleanTestHeaderValue(value);
922         if (key == "useDataDetection")
923             testOptions.useDataDetection = parseBooleanTestHeaderValue(value);
924         pairStart = pairEnd + 1;
925     }
926 }
927
928 TestOptions TestController::testOptionsForTest(const std::string& pathOrURL) const
929 {
930     TestOptions options(pathOrURL);
931
932     options.useRemoteLayerTree = m_shouldUseRemoteLayerTree;
933     options.shouldShowWebView = m_shouldShowWebView;
934
935     updatePlatformSpecificTestOptionsForTest(options, pathOrURL);
936     updateTestOptionsFromTestHeader(options, pathOrURL);
937
938     return options;
939 }
940
941 void TestController::updateWebViewSizeForTest(const TestInvocation& test)
942 {
943     unsigned width = viewWidth;
944     unsigned height = viewHeight;
945     if (test.options().isSVGTest) {
946         width = w3cSVGViewWidth;
947         height = w3cSVGViewHeight;
948     }
949
950     mainWebView()->resizeTo(width, height);
951 }
952
953 void TestController::updateWindowScaleForTest(PlatformWebView* view, const TestInvocation& test)
954 {
955     view->changeWindowScaleIfNeeded(test.options().isHiDPITest ? 2 : 1);
956 }
957
958 void TestController::configureViewForTest(const TestInvocation& test)
959 {
960     ensureViewSupportsOptionsForTest(test);
961     updateWebViewSizeForTest(test);
962     updateWindowScaleForTest(mainWebView(), test);
963
964     platformConfigureViewForTest(test);
965 }
966
967 struct TestCommand {
968     TestCommand() : shouldDumpPixels(false), timeout(0) { }
969
970     std::string pathOrURL;
971     bool shouldDumpPixels;
972     std::string expectedPixelHash;
973     int timeout;
974 };
975
976 class CommandTokenizer {
977 public:
978     explicit CommandTokenizer(const std::string& input)
979         : m_input(input)
980         , m_posNextSeparator(0)
981     {
982         pump();
983     }
984
985     bool hasNext() const;
986     std::string next();
987
988 private:
989     void pump();
990     static const char kSeparator = '\'';
991     const std::string& m_input;
992     std::string m_next;
993     size_t m_posNextSeparator;
994 };
995
996 void CommandTokenizer::pump()
997 {
998     if (m_posNextSeparator == std::string::npos || m_posNextSeparator == m_input.size()) {
999         m_next = std::string();
1000         return;
1001     }
1002     size_t start = m_posNextSeparator ? m_posNextSeparator + 1 : 0;
1003     m_posNextSeparator = m_input.find(kSeparator, start);
1004     size_t size = m_posNextSeparator == std::string::npos ? std::string::npos : m_posNextSeparator - start;
1005     m_next = std::string(m_input, start, size);
1006 }
1007
1008 std::string CommandTokenizer::next()
1009 {
1010     ASSERT(hasNext());
1011
1012     std::string oldNext = m_next;
1013     pump();
1014     return oldNext;
1015 }
1016
1017 bool CommandTokenizer::hasNext() const
1018 {
1019     return !m_next.empty();
1020 }
1021
1022 NO_RETURN static void die(const std::string& inputLine)
1023 {
1024     fprintf(stderr, "Unexpected input line: %s\n", inputLine.c_str());
1025     exit(1);
1026 }
1027
1028 TestCommand parseInputLine(const std::string& inputLine)
1029 {
1030     TestCommand result;
1031     CommandTokenizer tokenizer(inputLine);
1032     if (!tokenizer.hasNext())
1033         die(inputLine);
1034
1035     std::string arg = tokenizer.next();
1036     result.pathOrURL = arg;
1037     while (tokenizer.hasNext()) {
1038         arg = tokenizer.next();
1039         if (arg == std::string("--timeout")) {
1040             std::string timeoutToken = tokenizer.next();
1041             result.timeout = atoi(timeoutToken.c_str());
1042         } else if (arg == std::string("-p") || arg == std::string("--pixel-test")) {
1043             result.shouldDumpPixels = true;
1044             if (tokenizer.hasNext())
1045                 result.expectedPixelHash = tokenizer.next();
1046         } else
1047             die(inputLine);
1048     }
1049     return result;
1050 }
1051
1052 bool TestController::runTest(const char* inputLine)
1053 {
1054     TestCommand command = parseInputLine(std::string(inputLine));
1055
1056     m_state = RunningTest;
1057     
1058     TestOptions options = testOptionsForTest(command.pathOrURL);
1059
1060     WKRetainPtr<WKURLRef> wkURL(AdoptWK, createTestURL(command.pathOrURL.c_str()));
1061     m_currentInvocation = std::make_unique<TestInvocation>(wkURL.get(), options);
1062
1063     if (command.shouldDumpPixels || m_shouldDumpPixelsForAllTests)
1064         m_currentInvocation->setIsPixelTest(command.expectedPixelHash);
1065     if (command.timeout > 0)
1066         m_currentInvocation->setCustomTimeout(command.timeout);
1067
1068     platformWillRunTest(*m_currentInvocation);
1069
1070     m_currentInvocation->invoke();
1071     m_currentInvocation = nullptr;
1072
1073     return true;
1074 }
1075
1076 void TestController::runTestingServerLoop()
1077 {
1078     char filenameBuffer[2048];
1079     while (fgets(filenameBuffer, sizeof(filenameBuffer), stdin)) {
1080         char* newLineCharacter = strchr(filenameBuffer, '\n');
1081         if (newLineCharacter)
1082             *newLineCharacter = '\0';
1083
1084         if (strlen(filenameBuffer) == 0)
1085             continue;
1086
1087         if (!runTest(filenameBuffer))
1088             break;
1089     }
1090 }
1091
1092 void TestController::run()
1093 {
1094     if (m_usingServerMode)
1095         runTestingServerLoop();
1096     else {
1097         for (size_t i = 0; i < m_paths.size(); ++i) {
1098             if (!runTest(m_paths[i].c_str()))
1099                 break;
1100         }
1101     }
1102 }
1103
1104 void TestController::runUntil(bool& done, double timeout)
1105 {
1106     if (m_forceNoTimeout)
1107         timeout = noTimeout;
1108
1109     platformRunUntil(done, timeout);
1110 }
1111
1112 // WKContextInjectedBundleClient
1113
1114 void TestController::didReceiveMessageFromInjectedBundle(WKContextRef context, WKStringRef messageName, WKTypeRef messageBody, const void* clientInfo)
1115 {
1116     static_cast<TestController*>(const_cast<void*>(clientInfo))->didReceiveMessageFromInjectedBundle(messageName, messageBody);
1117 }
1118
1119 void TestController::didReceiveSynchronousMessageFromInjectedBundle(WKContextRef context, WKStringRef messageName, WKTypeRef messageBody, WKTypeRef* returnData, const void* clientInfo)
1120 {
1121     *returnData = static_cast<TestController*>(const_cast<void*>(clientInfo))->didReceiveSynchronousMessageFromInjectedBundle(messageName, messageBody).leakRef();
1122 }
1123
1124 // WKPageInjectedBundleClient
1125
1126 void TestController::didReceivePageMessageFromInjectedBundle(WKPageRef page, WKStringRef messageName, WKTypeRef messageBody, const void* clientInfo)
1127 {
1128     static_cast<TestController*>(const_cast<void*>(clientInfo))->didReceiveMessageFromInjectedBundle(messageName, messageBody);
1129 }
1130
1131 void TestController::didReceiveSynchronousPageMessageFromInjectedBundle(WKPageRef page, WKStringRef messageName, WKTypeRef messageBody, WKTypeRef* returnData, const void* clientInfo)
1132 {
1133     *returnData = static_cast<TestController*>(const_cast<void*>(clientInfo))->didReceiveSynchronousMessageFromInjectedBundle(messageName, messageBody).leakRef();
1134 }
1135
1136 void TestController::networkProcessDidCrash(WKContextRef context, const void *clientInfo)
1137 {
1138     static_cast<TestController*>(const_cast<void*>(clientInfo))->networkProcessDidCrash();
1139 }
1140
1141 void TestController::didReceiveKeyDownMessageFromInjectedBundle(WKDictionaryRef messageBodyDictionary, bool synchronous)
1142 {
1143     WKRetainPtr<WKStringRef> keyKey = adoptWK(WKStringCreateWithUTF8CString("Key"));
1144     WKStringRef key = static_cast<WKStringRef>(WKDictionaryGetItemForKey(messageBodyDictionary, keyKey.get()));
1145
1146     WKRetainPtr<WKStringRef> modifiersKey = adoptWK(WKStringCreateWithUTF8CString("Modifiers"));
1147     WKEventModifiers modifiers = static_cast<WKEventModifiers>(WKUInt64GetValue(static_cast<WKUInt64Ref>(WKDictionaryGetItemForKey(messageBodyDictionary, modifiersKey.get()))));
1148
1149     WKRetainPtr<WKStringRef> locationKey = adoptWK(WKStringCreateWithUTF8CString("Location"));
1150     unsigned location = static_cast<unsigned>(WKUInt64GetValue(static_cast<WKUInt64Ref>(WKDictionaryGetItemForKey(messageBodyDictionary, locationKey.get()))));
1151
1152     m_eventSenderProxy->keyDown(key, modifiers, location);
1153 }
1154
1155 void TestController::didReceiveMessageFromInjectedBundle(WKStringRef messageName, WKTypeRef messageBody)
1156 {
1157     if (WKStringIsEqualToUTF8CString(messageName, "EventSender")) {
1158         if (m_state != RunningTest)
1159             return;
1160
1161         ASSERT(WKGetTypeID(messageBody) == WKDictionaryGetTypeID());
1162         WKDictionaryRef messageBodyDictionary = static_cast<WKDictionaryRef>(messageBody);
1163
1164         WKRetainPtr<WKStringRef> subMessageKey(AdoptWK, WKStringCreateWithUTF8CString("SubMessage"));
1165         WKStringRef subMessageName = static_cast<WKStringRef>(WKDictionaryGetItemForKey(messageBodyDictionary, subMessageKey.get()));
1166
1167         if (WKStringIsEqualToUTF8CString(subMessageName, "MouseDown") || WKStringIsEqualToUTF8CString(subMessageName, "MouseUp")) {
1168             WKRetainPtr<WKStringRef> buttonKey = adoptWK(WKStringCreateWithUTF8CString("Button"));
1169             unsigned button = static_cast<unsigned>(WKUInt64GetValue(static_cast<WKUInt64Ref>(WKDictionaryGetItemForKey(messageBodyDictionary, buttonKey.get()))));
1170
1171             WKRetainPtr<WKStringRef> modifiersKey = adoptWK(WKStringCreateWithUTF8CString("Modifiers"));
1172             WKEventModifiers modifiers = static_cast<WKEventModifiers>(WKUInt64GetValue(static_cast<WKUInt64Ref>(WKDictionaryGetItemForKey(messageBodyDictionary, modifiersKey.get()))));
1173
1174             // Forward to WebProcess
1175             if (WKStringIsEqualToUTF8CString(subMessageName, "MouseDown"))
1176                 m_eventSenderProxy->mouseDown(button, modifiers);
1177             else
1178                 m_eventSenderProxy->mouseUp(button, modifiers);
1179
1180             return;
1181         }
1182
1183         if (WKStringIsEqualToUTF8CString(subMessageName, "KeyDown")) {
1184             didReceiveKeyDownMessageFromInjectedBundle(messageBodyDictionary, false);
1185
1186             return;
1187         }
1188
1189         if (WKStringIsEqualToUTF8CString(subMessageName, "MouseScrollBy")) {
1190             WKRetainPtr<WKStringRef> xKey = adoptWK(WKStringCreateWithUTF8CString("X"));
1191             double x = WKDoubleGetValue(static_cast<WKDoubleRef>(WKDictionaryGetItemForKey(messageBodyDictionary, xKey.get())));
1192
1193             WKRetainPtr<WKStringRef> yKey = adoptWK(WKStringCreateWithUTF8CString("Y"));
1194             double y = WKDoubleGetValue(static_cast<WKDoubleRef>(WKDictionaryGetItemForKey(messageBodyDictionary, yKey.get())));
1195
1196             // Forward to WebProcess
1197             m_eventSenderProxy->mouseScrollBy(x, y);
1198             return;
1199         }
1200
1201         if (WKStringIsEqualToUTF8CString(subMessageName, "MouseScrollByWithWheelAndMomentumPhases")) {
1202             WKRetainPtr<WKStringRef> xKey = adoptWK(WKStringCreateWithUTF8CString("X"));
1203             double x = WKDoubleGetValue(static_cast<WKDoubleRef>(WKDictionaryGetItemForKey(messageBodyDictionary, xKey.get())));
1204             
1205             WKRetainPtr<WKStringRef> yKey = adoptWK(WKStringCreateWithUTF8CString("Y"));
1206             double y = WKDoubleGetValue(static_cast<WKDoubleRef>(WKDictionaryGetItemForKey(messageBodyDictionary, yKey.get())));
1207             
1208             WKRetainPtr<WKStringRef> phaseKey = adoptWK(WKStringCreateWithUTF8CString("Phase"));
1209             int phase = static_cast<int>(WKUInt64GetValue(static_cast<WKUInt64Ref>(WKDictionaryGetItemForKey(messageBodyDictionary, phaseKey.get()))));
1210             WKRetainPtr<WKStringRef> momentumKey = adoptWK(WKStringCreateWithUTF8CString("Momentum"));
1211             int momentum = static_cast<int>(WKUInt64GetValue(static_cast<WKUInt64Ref>(WKDictionaryGetItemForKey(messageBodyDictionary, momentumKey.get()))));
1212             
1213             // Forward to WebProcess
1214             m_eventSenderProxy->mouseScrollByWithWheelAndMomentumPhases(x, y, phase, momentum);
1215
1216             return;
1217         }
1218
1219         if (WKStringIsEqualToUTF8CString(subMessageName, "SwipeGestureWithWheelAndMomentumPhases")) {
1220             WKRetainPtr<WKStringRef> xKey = adoptWK(WKStringCreateWithUTF8CString("X"));
1221             double x = WKDoubleGetValue(static_cast<WKDoubleRef>(WKDictionaryGetItemForKey(messageBodyDictionary, xKey.get())));
1222
1223             WKRetainPtr<WKStringRef> yKey = adoptWK(WKStringCreateWithUTF8CString("Y"));
1224             double y = WKDoubleGetValue(static_cast<WKDoubleRef>(WKDictionaryGetItemForKey(messageBodyDictionary, yKey.get())));
1225
1226             WKRetainPtr<WKStringRef> phaseKey = adoptWK(WKStringCreateWithUTF8CString("Phase"));
1227             int phase = static_cast<int>(WKUInt64GetValue(static_cast<WKUInt64Ref>(WKDictionaryGetItemForKey(messageBodyDictionary, phaseKey.get()))));
1228             WKRetainPtr<WKStringRef> momentumKey = adoptWK(WKStringCreateWithUTF8CString("Momentum"));
1229             int momentum = static_cast<int>(WKUInt64GetValue(static_cast<WKUInt64Ref>(WKDictionaryGetItemForKey(messageBodyDictionary, momentumKey.get()))));
1230
1231             m_eventSenderProxy->swipeGestureWithWheelAndMomentumPhases(x, y, phase, momentum);
1232
1233             return;
1234         }
1235
1236         ASSERT_NOT_REACHED();
1237     }
1238
1239     if (!m_currentInvocation)
1240         return;
1241
1242     m_currentInvocation->didReceiveMessageFromInjectedBundle(messageName, messageBody);
1243 }
1244
1245 WKRetainPtr<WKTypeRef> TestController::didReceiveSynchronousMessageFromInjectedBundle(WKStringRef messageName, WKTypeRef messageBody)
1246 {
1247     if (WKStringIsEqualToUTF8CString(messageName, "EventSender")) {
1248         if (m_state != RunningTest)
1249             return nullptr;
1250
1251         ASSERT(WKGetTypeID(messageBody) == WKDictionaryGetTypeID());
1252         WKDictionaryRef messageBodyDictionary = static_cast<WKDictionaryRef>(messageBody);
1253
1254         WKRetainPtr<WKStringRef> subMessageKey(AdoptWK, WKStringCreateWithUTF8CString("SubMessage"));
1255         WKStringRef subMessageName = static_cast<WKStringRef>(WKDictionaryGetItemForKey(messageBodyDictionary, subMessageKey.get()));
1256
1257         if (WKStringIsEqualToUTF8CString(subMessageName, "KeyDown")) {
1258             didReceiveKeyDownMessageFromInjectedBundle(messageBodyDictionary, true);
1259
1260             return 0;
1261         }
1262
1263         if (WKStringIsEqualToUTF8CString(subMessageName, "MouseDown") || WKStringIsEqualToUTF8CString(subMessageName, "MouseUp")) {
1264             WKRetainPtr<WKStringRef> buttonKey = adoptWK(WKStringCreateWithUTF8CString("Button"));
1265             unsigned button = static_cast<unsigned>(WKUInt64GetValue(static_cast<WKUInt64Ref>(WKDictionaryGetItemForKey(messageBodyDictionary, buttonKey.get()))));
1266
1267             WKRetainPtr<WKStringRef> modifiersKey = adoptWK(WKStringCreateWithUTF8CString("Modifiers"));
1268             WKEventModifiers modifiers = static_cast<WKEventModifiers>(WKUInt64GetValue(static_cast<WKUInt64Ref>(WKDictionaryGetItemForKey(messageBodyDictionary, modifiersKey.get()))));
1269
1270             // Forward to WebProcess
1271             if (WKStringIsEqualToUTF8CString(subMessageName, "MouseDown"))
1272                 m_eventSenderProxy->mouseDown(button, modifiers);
1273             else
1274                 m_eventSenderProxy->mouseUp(button, modifiers);
1275             return 0;
1276         }
1277
1278         if (WKStringIsEqualToUTF8CString(subMessageName, "MouseMoveTo")) {
1279             WKRetainPtr<WKStringRef> xKey = adoptWK(WKStringCreateWithUTF8CString("X"));
1280             double x = WKDoubleGetValue(static_cast<WKDoubleRef>(WKDictionaryGetItemForKey(messageBodyDictionary, xKey.get())));
1281
1282             WKRetainPtr<WKStringRef> yKey = adoptWK(WKStringCreateWithUTF8CString("Y"));
1283             double y = WKDoubleGetValue(static_cast<WKDoubleRef>(WKDictionaryGetItemForKey(messageBodyDictionary, yKey.get())));
1284
1285             // Forward to WebProcess
1286             m_eventSenderProxy->mouseMoveTo(x, y);
1287             return 0;
1288         }
1289
1290 #if PLATFORM(MAC)
1291         if (WKStringIsEqualToUTF8CString(subMessageName, "MouseForceClick")) {
1292             m_eventSenderProxy->mouseForceClick();
1293             return 0;
1294         }
1295
1296         if (WKStringIsEqualToUTF8CString(subMessageName, "StartAndCancelMouseForceClick")) {
1297             m_eventSenderProxy->startAndCancelMouseForceClick();
1298             return 0;
1299         }
1300
1301         if (WKStringIsEqualToUTF8CString(subMessageName, "MouseForceDown")) {
1302             m_eventSenderProxy->mouseForceDown();
1303             return 0;
1304         }
1305
1306         if (WKStringIsEqualToUTF8CString(subMessageName, "MouseForceUp")) {
1307             m_eventSenderProxy->mouseForceUp();
1308             return 0;
1309         }
1310
1311         if (WKStringIsEqualToUTF8CString(subMessageName, "MouseForceChanged")) {
1312             WKRetainPtr<WKStringRef> forceKey = adoptWK(WKStringCreateWithUTF8CString("Force"));
1313             double force = WKDoubleGetValue(static_cast<WKDoubleRef>(WKDictionaryGetItemForKey(messageBodyDictionary, forceKey.get())));
1314
1315             m_eventSenderProxy->mouseForceChanged(force);
1316             return 0;
1317         }
1318 #endif // PLATFORM(MAC)
1319
1320         if (WKStringIsEqualToUTF8CString(subMessageName, "ContinuousMouseScrollBy")) {
1321             WKRetainPtr<WKStringRef> xKey = adoptWK(WKStringCreateWithUTF8CString("X"));
1322             double x = WKDoubleGetValue(static_cast<WKDoubleRef>(WKDictionaryGetItemForKey(messageBodyDictionary, xKey.get())));
1323
1324             WKRetainPtr<WKStringRef> yKey = adoptWK(WKStringCreateWithUTF8CString("Y"));
1325             double y = WKDoubleGetValue(static_cast<WKDoubleRef>(WKDictionaryGetItemForKey(messageBodyDictionary, yKey.get())));
1326
1327             WKRetainPtr<WKStringRef> pagedKey = adoptWK(WKStringCreateWithUTF8CString("Paged"));
1328             bool paged = static_cast<bool>(WKUInt64GetValue(static_cast<WKUInt64Ref>(WKDictionaryGetItemForKey(messageBodyDictionary, pagedKey.get()))));
1329
1330             // Forward to WebProcess
1331             m_eventSenderProxy->continuousMouseScrollBy(x, y, paged);
1332             return 0;
1333         }
1334
1335         if (WKStringIsEqualToUTF8CString(subMessageName, "LeapForward")) {
1336             WKRetainPtr<WKStringRef> timeKey = adoptWK(WKStringCreateWithUTF8CString("TimeInMilliseconds"));
1337             unsigned time = static_cast<unsigned>(WKUInt64GetValue(static_cast<WKUInt64Ref>(WKDictionaryGetItemForKey(messageBodyDictionary, timeKey.get()))));
1338
1339             m_eventSenderProxy->leapForward(time);
1340             return 0;
1341         }
1342
1343 #if ENABLE(TOUCH_EVENTS)
1344         if (WKStringIsEqualToUTF8CString(subMessageName, "AddTouchPoint")) {
1345             WKRetainPtr<WKStringRef> xKey = adoptWK(WKStringCreateWithUTF8CString("X"));
1346             int x = static_cast<int>(WKUInt64GetValue(static_cast<WKUInt64Ref>(WKDictionaryGetItemForKey(messageBodyDictionary, xKey.get()))));
1347
1348             WKRetainPtr<WKStringRef> yKey = adoptWK(WKStringCreateWithUTF8CString("Y"));
1349             int y = static_cast<int>(WKUInt64GetValue(static_cast<WKUInt64Ref>(WKDictionaryGetItemForKey(messageBodyDictionary, yKey.get()))));
1350
1351             m_eventSenderProxy->addTouchPoint(x, y);
1352             return 0;
1353         }
1354
1355         if (WKStringIsEqualToUTF8CString(subMessageName, "UpdateTouchPoint")) {
1356             WKRetainPtr<WKStringRef> indexKey = adoptWK(WKStringCreateWithUTF8CString("Index"));
1357             int index = static_cast<int>(WKUInt64GetValue(static_cast<WKUInt64Ref>(WKDictionaryGetItemForKey(messageBodyDictionary, indexKey.get()))));
1358
1359             WKRetainPtr<WKStringRef> xKey = adoptWK(WKStringCreateWithUTF8CString("X"));
1360             int x = static_cast<int>(WKUInt64GetValue(static_cast<WKUInt64Ref>(WKDictionaryGetItemForKey(messageBodyDictionary, xKey.get()))));
1361
1362             WKRetainPtr<WKStringRef> yKey = adoptWK(WKStringCreateWithUTF8CString("Y"));
1363             int y = static_cast<int>(WKUInt64GetValue(static_cast<WKUInt64Ref>(WKDictionaryGetItemForKey(messageBodyDictionary, yKey.get()))));
1364
1365             m_eventSenderProxy->updateTouchPoint(index, x, y);
1366             return 0;
1367         }
1368
1369         if (WKStringIsEqualToUTF8CString(subMessageName, "SetTouchModifier")) {
1370             WKRetainPtr<WKStringRef> modifierKey = adoptWK(WKStringCreateWithUTF8CString("Modifier"));
1371             WKEventModifiers modifier = static_cast<WKEventModifiers>(WKUInt64GetValue(static_cast<WKUInt64Ref>(WKDictionaryGetItemForKey(messageBodyDictionary, modifierKey.get()))));
1372
1373             WKRetainPtr<WKStringRef> enableKey = adoptWK(WKStringCreateWithUTF8CString("Enable"));
1374             bool enable = static_cast<bool>(WKUInt64GetValue(static_cast<WKUInt64Ref>(WKDictionaryGetItemForKey(messageBodyDictionary, enableKey.get()))));
1375
1376             m_eventSenderProxy->setTouchModifier(modifier, enable);
1377             return 0;
1378         }
1379
1380         if (WKStringIsEqualToUTF8CString(subMessageName, "SetTouchPointRadius")) {
1381             WKRetainPtr<WKStringRef> xKey = adoptWK(WKStringCreateWithUTF8CString("RadiusX"));
1382             int x = static_cast<int>(WKUInt64GetValue(static_cast<WKUInt64Ref>(WKDictionaryGetItemForKey(messageBodyDictionary, xKey.get()))));
1383
1384             WKRetainPtr<WKStringRef> yKey = adoptWK(WKStringCreateWithUTF8CString("RadiusY"));
1385             int y = static_cast<int>(WKUInt64GetValue(static_cast<WKUInt64Ref>(WKDictionaryGetItemForKey(messageBodyDictionary, yKey.get()))));
1386
1387             m_eventSenderProxy->setTouchPointRadius(x, y);
1388             return 0;
1389         }
1390
1391         if (WKStringIsEqualToUTF8CString(subMessageName, "TouchStart")) {
1392             m_eventSenderProxy->touchStart();
1393             return 0;
1394         }
1395
1396         if (WKStringIsEqualToUTF8CString(subMessageName, "TouchMove")) {
1397             m_eventSenderProxy->touchMove();
1398             return 0;
1399         }
1400
1401         if (WKStringIsEqualToUTF8CString(subMessageName, "TouchEnd")) {
1402             m_eventSenderProxy->touchEnd();
1403             return 0;
1404         }
1405
1406         if (WKStringIsEqualToUTF8CString(subMessageName, "TouchCancel")) {
1407             m_eventSenderProxy->touchCancel();
1408             return 0;
1409         }
1410
1411         if (WKStringIsEqualToUTF8CString(subMessageName, "ClearTouchPoints")) {
1412             m_eventSenderProxy->clearTouchPoints();
1413             return 0;
1414         }
1415
1416         if (WKStringIsEqualToUTF8CString(subMessageName, "ReleaseTouchPoint")) {
1417             WKRetainPtr<WKStringRef> indexKey = adoptWK(WKStringCreateWithUTF8CString("Index"));
1418             int index = static_cast<int>(WKUInt64GetValue(static_cast<WKUInt64Ref>(WKDictionaryGetItemForKey(messageBodyDictionary, indexKey.get()))));
1419             m_eventSenderProxy->releaseTouchPoint(index);
1420             return 0;
1421         }
1422
1423         if (WKStringIsEqualToUTF8CString(subMessageName, "CancelTouchPoint")) {
1424             WKRetainPtr<WKStringRef> indexKey = adoptWK(WKStringCreateWithUTF8CString("Index"));
1425             int index = static_cast<int>(WKUInt64GetValue(static_cast<WKUInt64Ref>(WKDictionaryGetItemForKey(messageBodyDictionary, indexKey.get()))));
1426             m_eventSenderProxy->cancelTouchPoint(index);
1427             return 0;
1428         }
1429 #endif
1430         ASSERT_NOT_REACHED();
1431     }
1432     return m_currentInvocation->didReceiveSynchronousMessageFromInjectedBundle(messageName, messageBody);
1433 }
1434
1435 // WKContextClient
1436
1437 void TestController::networkProcessDidCrash()
1438 {
1439 #if PLATFORM(COCOA)
1440     pid_t pid = WKContextGetNetworkProcessIdentifier(m_context.get());
1441     fprintf(stderr, "#CRASHED - %s (pid %ld)\n", networkProcessName(), static_cast<long>(pid));
1442 #else
1443     fprintf(stderr, "#CRASHED - %s\n", networkProcessName());
1444 #endif
1445     exit(1);
1446 }
1447
1448 // WKPageNavigationClient
1449
1450 void TestController::didCommitNavigation(WKPageRef page, WKNavigationRef navigation, WKTypeRef, const void* clientInfo)
1451 {
1452     static_cast<TestController*>(const_cast<void*>(clientInfo))->didCommitNavigation(page, navigation);
1453 }
1454
1455 void TestController::didFinishNavigation(WKPageRef page, WKNavigationRef navigation, WKTypeRef, const void* clientInfo)
1456 {
1457     static_cast<TestController*>(const_cast<void*>(clientInfo))->didFinishNavigation(page, navigation);
1458 }
1459
1460 bool TestController::canAuthenticateAgainstProtectionSpace(WKPageRef, WKProtectionSpaceRef protectionSpace, const void*)
1461 {
1462     WKProtectionSpaceAuthenticationScheme authenticationScheme = WKProtectionSpaceGetAuthenticationScheme(protectionSpace);
1463
1464     if (authenticationScheme == kWKProtectionSpaceAuthenticationSchemeServerTrustEvaluationRequested) {
1465         std::string host = toSTD(adoptWK(WKProtectionSpaceCopyHost(protectionSpace)).get());
1466         return host == "localhost" || host == "127.0.0.1";
1467     }
1468
1469     return authenticationScheme <= kWKProtectionSpaceAuthenticationSchemeHTTPDigest;
1470 }
1471
1472 void TestController::didReceiveAuthenticationChallenge(WKPageRef page, WKAuthenticationChallengeRef authenticationChallenge, const void *clientInfo)
1473 {
1474     static_cast<TestController*>(const_cast<void*>(clientInfo))->didReceiveAuthenticationChallenge(page, /*frame,*/ authenticationChallenge);
1475 }
1476
1477 void TestController::processDidCrash(WKPageRef page, const void* clientInfo)
1478 {
1479     static_cast<TestController*>(const_cast<void*>(clientInfo))->processDidCrash();
1480 }
1481
1482 void TestController::didBeginNavigationGesture(WKPageRef page, const void *clientInfo)
1483 {
1484     static_cast<TestController*>(const_cast<void*>(clientInfo))->didBeginNavigationGesture(page);
1485 }
1486
1487 void TestController::willEndNavigationGesture(WKPageRef page, WKBackForwardListItemRef backForwardListItem, const void *clientInfo)
1488 {
1489     static_cast<TestController*>(const_cast<void*>(clientInfo))->willEndNavigationGesture(page, backForwardListItem);
1490 }
1491
1492 void TestController::didEndNavigationGesture(WKPageRef page, WKBackForwardListItemRef backForwardListItem, const void *clientInfo)
1493 {
1494     static_cast<TestController*>(const_cast<void*>(clientInfo))->didEndNavigationGesture(page, backForwardListItem);
1495 }
1496
1497 void TestController::didRemoveNavigationGestureSnapshot(WKPageRef page, const void *clientInfo)
1498 {
1499     static_cast<TestController*>(const_cast<void*>(clientInfo))->didRemoveNavigationGestureSnapshot(page);
1500 }
1501
1502 WKPluginLoadPolicy TestController::decidePolicyForPluginLoad(WKPageRef page, WKPluginLoadPolicy currentPluginLoadPolicy, WKDictionaryRef pluginInformation, WKStringRef* unavailabilityDescription, const void* clientInfo)
1503 {
1504     return static_cast<TestController*>(const_cast<void*>(clientInfo))->decidePolicyForPluginLoad(page, currentPluginLoadPolicy, pluginInformation, unavailabilityDescription);
1505 }
1506
1507 WKPluginLoadPolicy TestController::decidePolicyForPluginLoad(WKPageRef, WKPluginLoadPolicy currentPluginLoadPolicy, WKDictionaryRef pluginInformation, WKStringRef* unavailabilityDescription)
1508 {
1509     if (m_shouldBlockAllPlugins)
1510         return kWKPluginLoadPolicyBlocked;
1511
1512 #if PLATFORM(MAC)
1513     WKStringRef bundleIdentifier = (WKStringRef)WKDictionaryGetItemForKey(pluginInformation, WKPluginInformationBundleIdentifierKey());
1514     if (!bundleIdentifier)
1515         return currentPluginLoadPolicy;
1516
1517     if (WKStringIsEqualToUTF8CString(bundleIdentifier, "com.apple.QuickTime Plugin.plugin"))
1518         return currentPluginLoadPolicy;
1519
1520     if (WKStringIsEqualToUTF8CString(bundleIdentifier, "com.apple.testnetscapeplugin"))
1521         return currentPluginLoadPolicy;
1522
1523     RELEASE_ASSERT_NOT_REACHED(); // Please don't use any other plug-ins in tests, as they will not be installed on all machines.
1524 #else
1525     return currentPluginLoadPolicy;
1526 #endif
1527 }
1528
1529 void TestController::didCommitNavigation(WKPageRef page, WKNavigationRef navigation)
1530 {
1531     mainWebView()->focus();
1532 }
1533
1534 void TestController::didFinishNavigation(WKPageRef page, WKNavigationRef navigation)
1535 {
1536     if (m_state != Resetting)
1537         return;
1538
1539     WKRetainPtr<WKURLRef> wkURL(AdoptWK, WKFrameCopyURL(WKPageGetMainFrame(page)));
1540     if (!WKURLIsEqual(wkURL.get(), blankURL()))
1541         return;
1542
1543     m_doneResetting = true;
1544     singleton().notifyDone();
1545 }
1546
1547 void TestController::didReceiveAuthenticationChallenge(WKPageRef page, WKAuthenticationChallengeRef authenticationChallenge)
1548 {
1549     WKProtectionSpaceRef protectionSpace = WKAuthenticationChallengeGetProtectionSpace(authenticationChallenge);
1550     WKAuthenticationDecisionListenerRef decisionListener = WKAuthenticationChallengeGetDecisionListener(authenticationChallenge);
1551
1552     if (WKProtectionSpaceGetAuthenticationScheme(protectionSpace) == kWKProtectionSpaceAuthenticationSchemeServerTrustEvaluationRequested) {
1553         // Any non-empty credential signals to accept the server trust. Since the cross-platform API
1554         // doesn't expose a way to create a credential from server trust, we use a password credential.
1555
1556         WKRetainPtr<WKCredentialRef> credential = adoptWK(WKCredentialCreate(toWK("accept server trust").get(), toWK("").get(), kWKCredentialPersistenceNone));
1557         WKAuthenticationDecisionListenerUseCredential(decisionListener, credential.get());
1558         return;
1559     }
1560
1561     std::string host = toSTD(adoptWK(WKProtectionSpaceCopyHost(protectionSpace)).get());
1562     int port = WKProtectionSpaceGetPort(protectionSpace);
1563     String message = String::format("%s:%d - didReceiveAuthenticationChallenge - ", host.c_str(), port);
1564     if (!m_handlesAuthenticationChallenges)
1565         message.append("Simulating cancelled authentication sheet\n");
1566     else
1567         message.append(String::format("Responding with %s:%s\n", m_authenticationUsername.utf8().data(), m_authenticationPassword.utf8().data()));
1568     m_currentInvocation->outputText(message);
1569
1570     if (!m_handlesAuthenticationChallenges) {
1571         WKAuthenticationDecisionListenerUseCredential(decisionListener, 0);
1572         return;
1573     }
1574     WKRetainPtr<WKStringRef> username(AdoptWK, WKStringCreateWithUTF8CString(m_authenticationUsername.utf8().data()));
1575     WKRetainPtr<WKStringRef> password(AdoptWK, WKStringCreateWithUTF8CString(m_authenticationPassword.utf8().data()));
1576     WKRetainPtr<WKCredentialRef> credential(AdoptWK, WKCredentialCreate(username.get(), password.get(), kWKCredentialPersistenceForSession));
1577     WKAuthenticationDecisionListenerUseCredential(decisionListener, credential.get());
1578 }
1579
1580 void TestController::processDidCrash()
1581 {
1582     // This function can be called multiple times when crash logs are being saved on Windows, so
1583     // ensure we only print the crashed message once.
1584     if (!m_didPrintWebProcessCrashedMessage) {
1585 #if PLATFORM(COCOA)
1586         pid_t pid = WKPageGetProcessIdentifier(m_mainWebView->page());
1587         fprintf(stderr, "#CRASHED - %s (pid %ld)\n", webProcessName(), static_cast<long>(pid));
1588 #else
1589         fprintf(stderr, "#CRASHED - %s\n", webProcessName());
1590 #endif
1591         fflush(stderr);
1592         m_didPrintWebProcessCrashedMessage = true;
1593     }
1594
1595     if (m_shouldExitWhenWebProcessCrashes)
1596         exit(1);
1597 }
1598
1599 void TestController::didBeginNavigationGesture(WKPageRef)
1600 {
1601     m_currentInvocation->didBeginSwipe();
1602 }
1603
1604 void TestController::willEndNavigationGesture(WKPageRef, WKBackForwardListItemRef)
1605 {
1606     m_currentInvocation->willEndSwipe();
1607 }
1608
1609 void TestController::didEndNavigationGesture(WKPageRef, WKBackForwardListItemRef)
1610 {
1611     m_currentInvocation->didEndSwipe();
1612 }
1613
1614 void TestController::didRemoveNavigationGestureSnapshot(WKPageRef)
1615 {
1616     m_currentInvocation->didRemoveSwipeSnapshot();
1617 }
1618
1619 void TestController::simulateWebNotificationClick(uint64_t notificationID)
1620 {
1621     m_webNotificationProvider.simulateWebNotificationClick(notificationID);
1622 }
1623
1624 void TestController::setGeolocationPermission(bool enabled)
1625 {
1626     m_isGeolocationPermissionSet = true;
1627     m_isGeolocationPermissionAllowed = enabled;
1628     decidePolicyForGeolocationPermissionRequestIfPossible();
1629 }
1630
1631 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)
1632 {
1633     m_geolocationProvider->setPosition(latitude, longitude, accuracy, providesAltitude, altitude, providesAltitudeAccuracy, altitudeAccuracy, providesHeading, heading, providesSpeed, speed);
1634 }
1635
1636 void TestController::setMockGeolocationPositionUnavailableError(WKStringRef errorMessage)
1637 {
1638     m_geolocationProvider->setPositionUnavailableError(errorMessage);
1639 }
1640
1641 void TestController::handleGeolocationPermissionRequest(WKGeolocationPermissionRequestRef geolocationPermissionRequest)
1642 {
1643     m_geolocationPermissionRequests.append(geolocationPermissionRequest);
1644     decidePolicyForGeolocationPermissionRequestIfPossible();
1645 }
1646
1647 bool TestController::isGeolocationProviderActive() const
1648 {
1649     return m_geolocationProvider->isActive();
1650 }
1651
1652 static WKStringRef originUserVisibleName(WKSecurityOriginRef origin)
1653 {
1654     std::string host = toSTD(adoptWK(WKSecurityOriginCopyHost(origin))).c_str();
1655     std::string protocol = toSTD(adoptWK(WKSecurityOriginCopyProtocol(origin))).c_str();
1656     unsigned short port = WKSecurityOriginGetPort(origin);
1657
1658     String userVisibleName;
1659     if (port)
1660         userVisibleName = String::format("%s://%s:%d", protocol.c_str(), host.c_str(), port);
1661     else
1662         userVisibleName = String::format("%s://%s", protocol.c_str(), host.c_str());
1663
1664     return WKStringCreateWithUTF8CString(userVisibleName.utf8().data());
1665 }
1666
1667 void TestController::setUserMediaPermission(bool enabled)
1668 {
1669     m_isUserMediaPermissionSet = true;
1670     m_isUserMediaPermissionAllowed = enabled;
1671     decidePolicyForUserMediaPermissionRequestIfPossible();
1672 }
1673
1674 void TestController::setUserMediaPermissionForOrigin(bool permission, WKStringRef originString)
1675 {
1676     if (!m_userMediaOriginPermissions)
1677         m_userMediaOriginPermissions = adoptWK(WKMutableDictionaryCreate());
1678     WKRetainPtr<WKBooleanRef> allowed = adoptWK(WKBooleanCreate(permission));
1679     WKRetainPtr<WKSecurityOriginRef> origin = adoptWK(WKSecurityOriginCreateFromString(originString));
1680     WKDictionarySetItem(m_userMediaOriginPermissions.get(), originUserVisibleName(origin.get()), allowed.get());
1681 }
1682
1683 void TestController::handleCheckOfUserMediaPermissionForOrigin(WKSecurityOriginRef origin, const WKUserMediaPermissionCheckRef& checkRequest)
1684 {
1685     bool allowed = false;
1686
1687     if (m_userMediaOriginPermissions) {
1688         WKRetainPtr<WKStringRef> originString = originUserVisibleName(origin);
1689         WKBooleanRef value = static_cast<WKBooleanRef>(WKDictionaryGetItemForKey(m_userMediaOriginPermissions.get(), originString.get()));
1690         if (value && WKGetTypeID(value) == WKBooleanGetTypeID())
1691             allowed = WKBooleanGetValue(value);
1692     }
1693
1694     WKUserMediaPermissionCheckSetHasPersistentPermission(checkRequest, allowed);
1695 }
1696
1697 void TestController::handleUserMediaPermissionRequest(WKSecurityOriginRef origin, WKUserMediaPermissionRequestRef request)
1698 {
1699     m_userMediaPermissionRequests.append(std::make_pair(origin, request));
1700     decidePolicyForUserMediaPermissionRequestIfPossible();
1701 }
1702
1703 void TestController::decidePolicyForUserMediaPermissionRequestIfPossible()
1704 {
1705     if (!m_isUserMediaPermissionSet)
1706         return;
1707
1708     for (auto& pair : m_userMediaPermissionRequests) {
1709         auto request = pair.second.get();
1710         WKRetainPtr<WKArrayRef> audioDeviceUIDs = WKUserMediaPermissionRequestAudioDeviceUIDs(request);
1711         WKRetainPtr<WKArrayRef> videoDeviceUIDs = WKUserMediaPermissionRequestVideoDeviceUIDs(request);
1712
1713         if (m_isUserMediaPermissionAllowed && (WKArrayGetSize(videoDeviceUIDs.get()) || WKArrayGetSize(audioDeviceUIDs.get()))) {
1714             WKRetainPtr<WKStringRef> videoDeviceUID;
1715             if (WKArrayGetSize(videoDeviceUIDs.get()))
1716                 videoDeviceUID = reinterpret_cast<WKStringRef>(WKArrayGetItemAtIndex(videoDeviceUIDs.get(), 0));
1717             else
1718                 videoDeviceUID = WKStringCreateWithUTF8CString("");
1719
1720             WKRetainPtr<WKStringRef> audioDeviceUID;
1721             if (WKArrayGetSize(audioDeviceUIDs.get()))
1722                 audioDeviceUID = reinterpret_cast<WKStringRef>(WKArrayGetItemAtIndex(audioDeviceUIDs.get(), 0));
1723             else
1724                 audioDeviceUID = WKStringCreateWithUTF8CString("");
1725
1726             WKUserMediaPermissionRequestAllow(request, audioDeviceUID.get(), videoDeviceUID.get());
1727
1728         } else
1729             WKUserMediaPermissionRequestDeny(request);
1730     }
1731     m_userMediaPermissionRequests.clear();
1732 }
1733
1734 void TestController::setCustomPolicyDelegate(bool enabled, bool permissive)
1735 {
1736     m_policyDelegateEnabled = enabled;
1737     m_policyDelegatePermissive = permissive;
1738 }
1739
1740 void TestController::decidePolicyForGeolocationPermissionRequestIfPossible()
1741 {
1742     if (!m_isGeolocationPermissionSet)
1743         return;
1744
1745     for (size_t i = 0; i < m_geolocationPermissionRequests.size(); ++i) {
1746         WKGeolocationPermissionRequestRef permissionRequest = m_geolocationPermissionRequests[i].get();
1747         if (m_isGeolocationPermissionAllowed)
1748             WKGeolocationPermissionRequestAllow(permissionRequest);
1749         else
1750             WKGeolocationPermissionRequestDeny(permissionRequest);
1751     }
1752     m_geolocationPermissionRequests.clear();
1753 }
1754
1755 void TestController::decidePolicyForNotificationPermissionRequest(WKPageRef page, WKSecurityOriginRef origin, WKNotificationPermissionRequestRef request, const void*)
1756 {
1757     TestController::singleton().decidePolicyForNotificationPermissionRequest(page, origin, request);
1758 }
1759
1760 void TestController::decidePolicyForNotificationPermissionRequest(WKPageRef, WKSecurityOriginRef, WKNotificationPermissionRequestRef request)
1761 {
1762     WKNotificationPermissionRequestAllow(request);
1763 }
1764
1765 void TestController::unavailablePluginButtonClicked(WKPageRef, WKPluginUnavailabilityReason, WKDictionaryRef, const void*)
1766 {
1767     printf("MISSING PLUGIN BUTTON PRESSED\n");
1768 }
1769
1770 void TestController::decidePolicyForNavigationAction(WKPageRef, WKNavigationActionRef navigationAction, WKFramePolicyListenerRef listener, WKTypeRef, const void* clientInfo)
1771 {
1772     static_cast<TestController*>(const_cast<void*>(clientInfo))->decidePolicyForNavigationAction(listener);
1773 }
1774
1775 void TestController::decidePolicyForNavigationAction(WKFramePolicyListenerRef listener)
1776 {
1777     WKRetainPtr<WKFramePolicyListenerRef> retainedListener { listener };
1778     const bool shouldIgnore { m_policyDelegateEnabled && !m_policyDelegatePermissive };
1779     std::function<void()> decisionFunction = [shouldIgnore, retainedListener]() {
1780         if (shouldIgnore)
1781             WKFramePolicyListenerIgnore(retainedListener.get());
1782         else
1783             WKFramePolicyListenerUse(retainedListener.get());
1784     };
1785
1786     if (m_shouldDecideNavigationPolicyAfterDelay)
1787         RunLoop::main().dispatch(decisionFunction);
1788     else
1789         decisionFunction();
1790 }
1791
1792 void TestController::decidePolicyForNavigationResponse(WKPageRef, WKNavigationResponseRef navigationResponse, WKFramePolicyListenerRef listener, WKTypeRef, const void* clientInfo)
1793 {
1794     static_cast<TestController*>(const_cast<void*>(clientInfo))->decidePolicyForNavigationResponse(navigationResponse, listener);
1795 }
1796
1797 void TestController::decidePolicyForNavigationResponse(WKNavigationResponseRef navigationResponse, WKFramePolicyListenerRef listener)
1798 {
1799     // Even though Response was already checked by WKBundlePagePolicyClient, the check did not include plugins
1800     // so we have to re-check again.
1801     if (WKNavigationResponseCanShowMIMEType(navigationResponse)) {
1802         WKFramePolicyListenerUse(listener);
1803         return;
1804     }
1805
1806     WKFramePolicyListenerIgnore(listener);
1807 }
1808
1809 void TestController::didNavigateWithNavigationData(WKContextRef, WKPageRef, WKNavigationDataRef navigationData, WKFrameRef frame, const void* clientInfo)
1810 {
1811     static_cast<TestController*>(const_cast<void*>(clientInfo))->didNavigateWithNavigationData(navigationData, frame);
1812 }
1813
1814 void TestController::didNavigateWithNavigationData(WKNavigationDataRef navigationData, WKFrameRef)
1815 {
1816     if (m_state != RunningTest)
1817         return;
1818
1819     if (!m_shouldLogHistoryClientCallbacks)
1820         return;
1821
1822     // URL
1823     WKRetainPtr<WKURLRef> urlWK = adoptWK(WKNavigationDataCopyURL(navigationData));
1824     WKRetainPtr<WKStringRef> urlStringWK = adoptWK(WKURLCopyString(urlWK.get()));
1825     // Title
1826     WKRetainPtr<WKStringRef> titleWK = adoptWK(WKNavigationDataCopyTitle(navigationData));
1827     // HTTP method
1828     WKRetainPtr<WKURLRequestRef> requestWK = adoptWK(WKNavigationDataCopyOriginalRequest(navigationData));
1829     WKRetainPtr<WKStringRef> methodWK = adoptWK(WKURLRequestCopyHTTPMethod(requestWK.get()));
1830
1831     // FIXME: Determine whether the navigation was successful / a client redirect rather than hard-coding the message here.
1832     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",
1833         toSTD(urlStringWK).c_str(), toSTD(titleWK).c_str(), toSTD(methodWK).c_str()));
1834 }
1835
1836 void TestController::didPerformClientRedirect(WKContextRef, WKPageRef, WKURLRef sourceURL, WKURLRef destinationURL, WKFrameRef frame, const void* clientInfo)
1837 {
1838     static_cast<TestController*>(const_cast<void*>(clientInfo))->didPerformClientRedirect(sourceURL, destinationURL, frame);
1839 }
1840
1841 void TestController::didPerformClientRedirect(WKURLRef sourceURL, WKURLRef destinationURL, WKFrameRef)
1842 {
1843     if (m_state != RunningTest)
1844         return;
1845
1846     if (!m_shouldLogHistoryClientCallbacks)
1847         return;
1848
1849     WKRetainPtr<WKStringRef> sourceStringWK = adoptWK(WKURLCopyString(sourceURL));
1850     WKRetainPtr<WKStringRef> destinationStringWK = adoptWK(WKURLCopyString(destinationURL));
1851
1852     m_currentInvocation->outputText(String::format("WebView performed a client redirect from \"%s\" to \"%s\".\n", toSTD(sourceStringWK).c_str(), toSTD(destinationStringWK).c_str()));
1853 }
1854
1855 void TestController::didPerformServerRedirect(WKContextRef, WKPageRef, WKURLRef sourceURL, WKURLRef destinationURL, WKFrameRef frame, const void* clientInfo)
1856 {
1857     static_cast<TestController*>(const_cast<void*>(clientInfo))->didPerformServerRedirect(sourceURL, destinationURL, frame);
1858 }
1859
1860 void TestController::didPerformServerRedirect(WKURLRef sourceURL, WKURLRef destinationURL, WKFrameRef)
1861 {
1862     if (m_state != RunningTest)
1863         return;
1864
1865     if (!m_shouldLogHistoryClientCallbacks)
1866         return;
1867
1868     WKRetainPtr<WKStringRef> sourceStringWK = adoptWK(WKURLCopyString(sourceURL));
1869     WKRetainPtr<WKStringRef> destinationStringWK = adoptWK(WKURLCopyString(destinationURL));
1870
1871     m_currentInvocation->outputText(String::format("WebView performed a server redirect from \"%s\" to \"%s\".\n", toSTD(sourceStringWK).c_str(), toSTD(destinationStringWK).c_str()));
1872 }
1873
1874 void TestController::didUpdateHistoryTitle(WKContextRef, WKPageRef, WKStringRef title, WKURLRef URL, WKFrameRef frame, const void* clientInfo)
1875 {
1876     static_cast<TestController*>(const_cast<void*>(clientInfo))->didUpdateHistoryTitle(title, URL, frame);
1877 }
1878
1879 void TestController::didUpdateHistoryTitle(WKStringRef title, WKURLRef URL, WKFrameRef)
1880 {
1881     if (m_state != RunningTest)
1882         return;
1883
1884     if (!m_shouldLogHistoryClientCallbacks)
1885         return;
1886
1887     WKRetainPtr<WKStringRef> urlStringWK(AdoptWK, WKURLCopyString(URL));
1888     m_currentInvocation->outputText(String::format("WebView updated the title for history URL \"%s\" to \"%s\".\n", toSTD(urlStringWK).c_str(), toSTD(title).c_str()));
1889 }
1890
1891 void TestController::setNavigationGesturesEnabled(bool value)
1892 {
1893     m_mainWebView->setNavigationGesturesEnabled(value);
1894 }
1895
1896 #if !PLATFORM(COCOA)
1897 void TestController::platformWillRunTest(const TestInvocation&)
1898 {
1899 }
1900
1901 void TestController::platformCreateWebView(WKPageConfigurationRef configuration, const TestOptions& options)
1902 {
1903     m_mainWebView = std::make_unique<PlatformWebView>(configuration, options);
1904 }
1905
1906 PlatformWebView* TestController::platformCreateOtherPage(PlatformWebView* parentView, WKPageConfigurationRef configuration, const TestOptions& options)
1907 {
1908     return new PlatformWebView(configuration, options);
1909 }
1910
1911 WKContextRef TestController::platformAdjustContext(WKContextRef context, WKContextConfigurationRef contextConfiguration)
1912 {
1913     return context;
1914 }
1915
1916 void TestController::platformResetStateToConsistentValues()
1917 {
1918
1919 }
1920 #endif
1921
1922 } // namespace WTR