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