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