<https://webkit.org/b/42704> WebKitTestRunner needs to print history delegate information
[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 <WebKit2/WKAuthenticationChallenge.h>
35 #include <WebKit2/WKAuthenticationDecisionListener.h>
36 #include <WebKit2/WKContextPrivate.h>
37 #include <WebKit2/WKCredential.h>
38 #include <WebKit2/WKIconDatabase.h>
39 #include <WebKit2/WKNotification.h>
40 #include <WebKit2/WKNotificationManager.h>
41 #include <WebKit2/WKNotificationPermissionRequest.h>
42 #include <WebKit2/WKNumber.h>
43 #include <WebKit2/WKPageGroup.h>
44 #include <WebKit2/WKPagePrivate.h>
45 #include <WebKit2/WKPreferencesRefPrivate.h>
46 #include <WebKit2/WKRetainPtr.h>
47 #include <algorithm>
48 #include <cstdio>
49 #include <ctype.h>
50 #include <stdlib.h>
51 #include <string>
52 #include <wtf/PassOwnPtr.h>
53 #include <wtf/text/CString.h>
54
55 #if PLATFORM(COCOA)
56 #include <WebKit2/WKPagePrivateMac.h>
57 #endif
58
59 #if !PLATFORM(COCOA)
60 #include <WebKit2/WKTextChecker.h>
61 #endif
62
63 namespace WTR {
64
65 const unsigned TestController::viewWidth = 800;
66 const unsigned TestController::viewHeight = 600;
67
68 const unsigned TestController::w3cSVGViewWidth = 480;
69 const unsigned TestController::w3cSVGViewHeight = 360;
70
71 // defaultLongTimeout + defaultShortTimeout should be less than 80,
72 // the default timeout value of the test harness so we can detect an
73 // unresponsive web process.
74 static const double defaultLongTimeout = 60;
75 static const double defaultShortTimeout = 15;
76 static const double defaultNoTimeout = -1;
77
78 static WKURLRef blankURL()
79 {
80     static WKURLRef staticBlankURL = WKURLCreateWithUTF8CString("about:blank");
81     return staticBlankURL;
82 }
83
84 static WKDataRef copyWebCryptoMasterKey(WKContextRef, const void*)
85 {
86     // Any 128 bit key would do, all we need for testing is to implement the callback.
87     return WKDataCreate((const uint8_t*)"\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f", 16);
88 }
89
90 static TestController* controller;
91
92 TestController& TestController::shared()
93 {
94     ASSERT(controller);
95     return *controller;
96 }
97
98 TestController::TestController(int argc, const char* argv[])
99     : m_verbose(false)
100     , m_printSeparators(false)
101     , m_usingServerMode(false)
102     , m_gcBetweenTests(false)
103     , m_shouldDumpPixelsForAllTests(false)
104     , m_state(Initial)
105     , m_doneResetting(false)
106     , m_longTimeout(defaultLongTimeout)
107     , m_shortTimeout(defaultShortTimeout)
108     , m_noTimeout(defaultNoTimeout)
109     , m_useWaitToDumpWatchdogTimer(true)
110     , m_forceNoTimeout(false)
111     , m_timeout(0)
112     , m_didPrintWebProcessCrashedMessage(false)
113     , m_shouldExitWhenWebProcessCrashes(true)
114     , m_beforeUnloadReturnValue(true)
115     , m_isGeolocationPermissionSet(false)
116     , m_isGeolocationPermissionAllowed(false)
117     , m_policyDelegateEnabled(false)
118     , m_policyDelegatePermissive(false)
119     , m_handlesAuthenticationChallenges(false)
120     , m_shouldBlockAllPlugins(false)
121     , m_forceComplexText(false)
122     , m_shouldUseAcceleratedDrawing(false)
123     , m_shouldUseRemoteLayerTree(false)
124     , m_shouldLogHistoryClientCallbacks(false)
125 {
126
127 #if PLATFORM(IOS)
128     int infd = open("/tmp/WebKitTestRunner_IN", O_RDWR);
129     dup2(infd, STDIN_FILENO);
130     int outfd = open("/tmp/WebKitTestRunner_OUT", O_RDWR);
131     dup2(outfd, STDOUT_FILENO);
132     int errfd = open("/tmp/WebKitTestRunner_ERROR", O_RDWR | O_NONBLOCK);
133     dup2(errfd, STDERR_FILENO);
134 #endif
135
136     initialize(argc, argv);
137     controller = this;
138     run();
139     controller = 0;
140 }
141
142 TestController::~TestController()
143 {
144     WKIconDatabaseClose(WKContextGetIconDatabase(m_context.get()));
145
146     platformDestroy();
147 }
148
149 static WKRect getWindowFrame(WKPageRef page, const void* clientInfo)
150 {
151     PlatformWebView* view = static_cast<PlatformWebView*>(const_cast<void*>(clientInfo));
152     return view->windowFrame();
153 }
154
155 static void setWindowFrame(WKPageRef page, WKRect frame, const void* clientInfo)
156 {
157     PlatformWebView* view = static_cast<PlatformWebView*>(const_cast<void*>(clientInfo));
158     view->setWindowFrame(frame);
159 }
160
161 static bool runBeforeUnloadConfirmPanel(WKPageRef page, WKStringRef message, WKFrameRef frame, const void*)
162 {
163     printf("CONFIRM NAVIGATION: %s\n", toSTD(message).c_str());
164     return TestController::shared().beforeUnloadReturnValue();
165 }
166
167 void TestController::runModal(WKPageRef page, const void* clientInfo)
168 {
169     PlatformWebView* view = static_cast<PlatformWebView*>(const_cast<void*>(clientInfo));
170     view->setWindowIsKey(false);
171     runModal(view);
172     view->setWindowIsKey(true);
173 }
174
175 static void closeOtherPage(WKPageRef page, const void* clientInfo)
176 {
177     WKPageClose(page);
178     PlatformWebView* view = static_cast<PlatformWebView*>(const_cast<void*>(clientInfo));
179     delete view;
180 }
181
182 static void focus(WKPageRef page, const void* clientInfo)
183 {
184     PlatformWebView* view = static_cast<PlatformWebView*>(const_cast<void*>(clientInfo));
185     view->focus();
186     view->setWindowIsKey(true);
187 }
188
189 static void unfocus(WKPageRef page, const void* clientInfo)
190 {
191     PlatformWebView* view = static_cast<PlatformWebView*>(const_cast<void*>(clientInfo));
192     view->setWindowIsKey(false);
193 }
194
195 static void decidePolicyForGeolocationPermissionRequest(WKPageRef, WKFrameRef, WKSecurityOriginRef, WKGeolocationPermissionRequestRef permissionRequest, const void* clientInfo)
196 {
197     TestController::shared().handleGeolocationPermissionRequest(permissionRequest);
198 }
199
200 int TestController::getCustomTimeout()
201 {
202     return m_timeout;
203 }
204
205 WKPageRef TestController::createOtherPage(WKPageRef oldPage, WKURLRequestRef, WKDictionaryRef, WKEventModifiers, WKEventMouseButton, const void* clientInfo)
206 {
207     PlatformWebView* parentView = static_cast<PlatformWebView*>(const_cast<void*>(clientInfo));
208
209     PlatformWebView* view = new PlatformWebView(WKPageGetContext(oldPage), WKPageGetPageGroup(oldPage), oldPage, parentView->options());
210     WKPageRef newPage = view->page();
211
212     view->resizeTo(800, 600);
213
214     WKPageUIClientV2 otherPageUIClient = {
215         { 2, view },
216         0, // createNewPage_deprecatedForUseWithV0
217         0, // showPage
218         closeOtherPage,
219         0, // takeFocus
220         focus,
221         unfocus,
222         0, // runJavaScriptAlert
223         0, // runJavaScriptConfirm
224         0, // runJavaScriptPrompt
225         0, // setStatusText
226         0, // mouseDidMoveOverElement_deprecatedForUseWithV0
227         0, // missingPluginButtonClicked
228         0, // didNotHandleKeyEvent
229         0, // didNotHandleWheelEvent
230         0, // toolbarsAreVisible
231         0, // setToolbarsAreVisible
232         0, // menuBarIsVisible
233         0, // setMenuBarIsVisible
234         0, // statusBarIsVisible
235         0, // setStatusBarIsVisible
236         0, // isResizable
237         0, // setIsResizable
238         getWindowFrame,
239         setWindowFrame,
240         runBeforeUnloadConfirmPanel,
241         0, // didDraw
242         0, // pageDidScroll
243         0, // exceededDatabaseQuota
244         0, // runOpenPanel
245         decidePolicyForGeolocationPermissionRequest,
246         0, // headerHeight
247         0, // footerHeight
248         0, // drawHeader
249         0, // drawFooter
250         0, // printFrame
251         runModal,
252         0, // didCompleteRubberBandForMainFrame
253         0, // saveDataToFileInDownloadsFolder
254         0, // shouldInterruptJavaScript
255         createOtherPage,
256         0, // mouseDidMoveOverElement
257         0, // decidePolicyForNotificationPermissionRequest
258         0, // unavailablePluginButtonClicked_deprecatedForUseWithV1
259         0, // showColorPicker
260         0, // hideColorPicker
261         0, // unavailablePluginButtonClicked
262     };
263     WKPageSetPageUIClient(newPage, &otherPageUIClient.base);
264
265     view->didInitializeClients();
266
267     WKRetain(newPage);
268     return newPage;
269 }
270
271 const char* TestController::libraryPathForTesting()
272 {
273     // FIXME: This may not be sufficient to prevent interactions/crashes
274     // when running more than one copy of DumpRenderTree.
275     // See https://bugs.webkit.org/show_bug.cgi?id=10906
276     char* dumpRenderTreeTemp = getenv("DUMPRENDERTREE_TEMP");
277     if (dumpRenderTreeTemp)
278         return dumpRenderTreeTemp;
279     return platformLibraryPathForTesting();
280 }
281
282
283 void TestController::initialize(int argc, const char* argv[])
284 {
285     platformInitialize();
286
287     Options options(defaultLongTimeout, defaultShortTimeout);
288     OptionsHandler optionsHandler(options);
289
290     if (argc < 2) {
291         optionsHandler.printHelp();
292         exit(1);
293     }
294     if (!optionsHandler.parse(argc, argv))
295         exit(1);
296
297     m_longTimeout = options.longTimeout;
298     m_shortTimeout = options.shortTimeout;
299     m_useWaitToDumpWatchdogTimer = options.useWaitToDumpWatchdogTimer;
300     m_forceNoTimeout = options.forceNoTimeout;
301     m_verbose = options.verbose;
302     m_gcBetweenTests = options.gcBetweenTests;
303     m_shouldDumpPixelsForAllTests = options.shouldDumpPixelsForAllTests;
304     m_forceComplexText = options.forceComplexText;
305     m_shouldUseAcceleratedDrawing = options.shouldUseAcceleratedDrawing;
306     m_shouldUseRemoteLayerTree = options.shouldUseRemoteLayerTree;
307     m_paths = options.paths;
308
309     if (options.printSupportedFeatures) {
310         // FIXME: On Windows, DumpRenderTree uses this to expose whether it supports 3d
311         // transforms and accelerated compositing. When we support those features, we
312         // should match DRT's behavior.
313         exit(0);
314     }
315
316     m_usingServerMode = (m_paths.size() == 1 && m_paths[0] == "-");
317     if (m_usingServerMode)
318         m_printSeparators = true;
319     else
320         m_printSeparators = m_paths.size() > 1;
321
322     initializeInjectedBundlePath();
323     initializeTestPluginDirectory();
324
325     WKRetainPtr<WKStringRef> pageGroupIdentifier(AdoptWK, WKStringCreateWithUTF8CString("WebKitTestRunnerPageGroup"));
326     m_pageGroup.adopt(WKPageGroupCreateWithIdentifier(pageGroupIdentifier.get()));
327
328     m_context.adopt(WKContextCreateWithInjectedBundlePath(injectedBundlePath()));
329     m_geolocationProvider = adoptPtr(new GeolocationProviderMock(m_context.get()));
330
331 #if PLATFORM(IOS) || (PLATFORM(MAC) && __MAC_OS_X_VERSION_MIN_REQUIRED > 1080)
332     WKContextSetUsesNetworkProcess(m_context.get(), true);
333     WKContextSetProcessModel(m_context.get(), kWKProcessModelMultipleSecondaryProcesses);
334 #endif
335
336     if (const char* dumpRenderTreeTemp = libraryPathForTesting()) {
337         String temporaryFolder = String::fromUTF8(dumpRenderTreeTemp);
338
339         // WebCore::pathByAppendingComponent is not used here because of the namespace,
340         // which leads us to this ugly #ifdef and file path concatenation.
341         const char separator = '/';
342
343         WKContextSetApplicationCacheDirectory(m_context.get(), toWK(temporaryFolder + separator + "ApplicationCache").get());
344         WKContextSetDatabaseDirectory(m_context.get(), toWK(temporaryFolder + separator + "Databases").get());
345         WKContextSetLocalStorageDirectory(m_context.get(), toWK(temporaryFolder + separator + "LocalStorage").get());
346         WKContextSetDiskCacheDirectory(m_context.get(), toWK(temporaryFolder + separator + "Cache").get());
347         WKContextSetCookieStorageDirectory(m_context.get(), toWK(temporaryFolder + separator + "Cookies").get());
348         WKContextSetIconDatabasePath(m_context.get(), toWK(temporaryFolder + separator + "IconDatabase" + separator + "WebpageIcons.db").get());
349     }
350
351     WKContextUseTestingNetworkSession(m_context.get());
352     WKContextSetCacheModel(m_context.get(), kWKCacheModelDocumentBrowser);
353
354     platformInitializeContext();
355
356     WKContextClientV1 contextClient = {
357         { 1, this },
358         nullptr, // plugInAutoStartOriginHashesChanged
359         nullptr, // networkProcessDidCrash,
360         nullptr, // plugInInformationBecameAvailable,
361         copyWebCryptoMasterKey
362     };
363     WKContextSetClient(m_context.get(), &contextClient.base);
364
365     WKContextInjectedBundleClientV1 injectedBundleClient = {
366         { 1, this },
367         didReceiveMessageFromInjectedBundle,
368         didReceiveSynchronousMessageFromInjectedBundle,
369         0 // getInjectedBundleInitializationUserData
370     };
371     WKContextSetInjectedBundleClient(m_context.get(), &injectedBundleClient.base);
372
373     WKContextHistoryClientV0 historyClient = {
374         { 0, this },
375         didNavigateWithNavigationData,
376         didPerformClientRedirect,
377         didPerformServerRedirect,
378         didUpdateHistoryTitle,
379         0, // populateVisitedLinks
380     };
381     WKContextSetHistoryClient(m_context.get(), &historyClient.base);
382
383     WKNotificationManagerRef notificationManager = WKContextGetNotificationManager(m_context.get());
384     WKNotificationProviderV0 notificationKit = m_webNotificationProvider.provider();
385     WKNotificationManagerSetProvider(notificationManager, &notificationKit.base);
386
387     if (testPluginDirectory())
388         WKContextSetAdditionalPluginsDirectory(m_context.get(), testPluginDirectory());
389
390     if (m_forceComplexText)
391         WKContextSetAlwaysUsesComplexTextCodePath(m_context.get(), true);
392
393     // Some preferences (notably mock scroll bars setting) currently cannot be re-applied to an existing view, so we need to set them now.
394     resetPreferencesToConsistentValues();
395
396     WKRetainPtr<WKMutableDictionaryRef> viewOptions;
397     if (m_shouldUseRemoteLayerTree) {
398         viewOptions = adoptWK(WKMutableDictionaryCreate());
399         WKRetainPtr<WKStringRef> useRemoteLayerTreeKey = adoptWK(WKStringCreateWithUTF8CString("RemoteLayerTree"));
400         WKRetainPtr<WKBooleanRef> useRemoteLayerTreeValue = adoptWK(WKBooleanCreate(m_shouldUseRemoteLayerTree));
401         WKDictionarySetItem(viewOptions.get(), useRemoteLayerTreeKey.get(), useRemoteLayerTreeValue.get());
402     }
403
404     createWebViewWithOptions(viewOptions.get());
405 }
406
407 void TestController::createWebViewWithOptions(WKDictionaryRef options)
408 {
409     m_mainWebView = adoptPtr(new PlatformWebView(m_context.get(), m_pageGroup.get(), 0, options));
410     WKPageUIClientV2 pageUIClient = {
411         { 2, m_mainWebView.get() },
412         0, // createNewPage_deprecatedForUseWithV0
413         0, // showPage
414         0, // close
415         0, // takeFocus
416         focus,
417         unfocus,
418         0, // runJavaScriptAlert
419         0, // runJavaScriptConfirm
420         0, // runJavaScriptPrompt
421         0, // setStatusText
422         0, // mouseDidMoveOverElement_deprecatedForUseWithV0
423         0, // missingPluginButtonClicked
424         0, // didNotHandleKeyEvent
425         0, // didNotHandleWheelEvent
426         0, // toolbarsAreVisible
427         0, // setToolbarsAreVisible
428         0, // menuBarIsVisible
429         0, // setMenuBarIsVisible
430         0, // statusBarIsVisible
431         0, // setStatusBarIsVisible
432         0, // isResizable
433         0, // setIsResizable
434         getWindowFrame,
435         setWindowFrame,
436         runBeforeUnloadConfirmPanel,
437         0, // didDraw
438         0, // pageDidScroll
439         0, // exceededDatabaseQuota,
440         0, // runOpenPanel
441         decidePolicyForGeolocationPermissionRequest,
442         0, // headerHeight
443         0, // footerHeight
444         0, // drawHeader
445         0, // drawFooter
446         0, // printFrame
447         runModal,
448         0, // didCompleteRubberBandForMainFrame
449         0, // saveDataToFileInDownloadsFolder
450         0, // shouldInterruptJavaScript
451         createOtherPage,
452         0, // mouseDidMoveOverElement
453         decidePolicyForNotificationPermissionRequest, // decidePolicyForNotificationPermissionRequest
454         0, // unavailablePluginButtonClicked_deprecatedForUseWithV1
455         0, // showColorPicker
456         0, // hideColorPicker
457         unavailablePluginButtonClicked,
458     };
459     WKPageSetPageUIClient(m_mainWebView->page(), &pageUIClient.base);
460
461     WKPageLoaderClientV4 pageLoaderClient = {
462         { 4, this },
463         0, // didStartProvisionalLoadForFrame
464         0, // didReceiveServerRedirectForProvisionalLoadForFrame
465         0, // didFailProvisionalLoadWithErrorForFrame
466         didCommitLoadForFrame,
467         0, // didFinishDocumentLoadForFrame
468         didFinishLoadForFrame,
469         0, // didFailLoadWithErrorForFrame
470         0, // didSameDocumentNavigationForFrame
471         0, // didReceiveTitleForFrame
472         0, // didFirstLayoutForFrame
473         0, // didFirstVisuallyNonEmptyLayoutForFrame
474         0, // didRemoveFrameFromHierarchy
475         0, // didFailToInitializePlugin
476         0, // didDisplayInsecureContentForFrame
477         0, // canAuthenticateAgainstProtectionSpaceInFrame
478         didReceiveAuthenticationChallengeInFrame, // didReceiveAuthenticationChallengeInFrame
479         0, // didStartProgress
480         0, // didChangeProgress
481         0, // didFinishProgress
482         0, // didBecomeUnresponsive
483         0, // didBecomeResponsive
484         processDidCrash,
485         0, // didChangeBackForwardList
486         0, // shouldGoToBackForwardListItem
487         0, // didRunInsecureContentForFrame
488         0, // didDetectXSSForFrame
489         0, // didNewFirstVisuallyNonEmptyLayout_unavailable
490         0, // willGoToBackForwardListItem
491         0, // interactionOccurredWhileProcessUnresponsive
492         0, // pluginDidFail_deprecatedForUseWithV1
493         0, // didReceiveIntentForFrame
494         0, // registerIntentServiceForFrame
495         0, // didLayout
496         0, // pluginLoadPolicy_deprecatedForUseWithV2
497         0, // pluginDidFail
498         pluginLoadPolicy, // pluginLoadPolicy
499         0, // webGLLoadPolicy
500         0, // resolveWebGLLoadPolicy
501     };
502     WKPageSetPageLoaderClient(m_mainWebView->page(), &pageLoaderClient.base);
503
504     WKPagePolicyClientV1 pagePolicyClient = {
505         { 1, this },
506         0, // decidePolicyForNavigationAction_deprecatedForUseWithV0
507         0, // decidePolicyForNewWindowAction
508         0, // decidePolicyForResponse_deprecatedForUseWithV0
509         0, // unableToImplementPolicy
510         decidePolicyForNavigationAction,
511         decidePolicyForResponse,
512     };
513     WKPageSetPagePolicyClient(m_mainWebView->page(), &pagePolicyClient.base);
514
515     m_mainWebView->didInitializeClients();
516 }
517
518 void TestController::ensureViewSupportsOptions(WKDictionaryRef options)
519 {
520     if (m_mainWebView && !m_mainWebView->viewSupportsOptions(options)) {
521         WKPageSetPageUIClient(m_mainWebView->page(), 0);
522         WKPageSetPageLoaderClient(m_mainWebView->page(), 0);
523         WKPageSetPagePolicyClient(m_mainWebView->page(), 0);
524         WKPageClose(m_mainWebView->page());
525         
526         m_mainWebView = nullptr;
527
528         createWebViewWithOptions(options);
529         resetStateToConsistentValues();
530     }
531 }
532
533 void TestController::resetPreferencesToConsistentValues()
534 {
535     // Reset preferences
536     WKPreferencesRef preferences = WKPageGroupGetPreferences(m_pageGroup.get());
537     WKPreferencesResetTestRunnerOverrides(preferences);
538     WKPreferencesSetOfflineWebApplicationCacheEnabled(preferences, true);
539     WKPreferencesSetFontSmoothingLevel(preferences, kWKFontSmoothingLevelNoSubpixelAntiAliasing);
540     WKPreferencesSetXSSAuditorEnabled(preferences, false);
541     WKPreferencesSetWebAudioEnabled(preferences, true);
542     WKPreferencesSetDeveloperExtrasEnabled(preferences, true);
543     WKPreferencesSetJavaScriptExperimentsEnabled(preferences, true);
544     WKPreferencesSetJavaScriptCanOpenWindowsAutomatically(preferences, true);
545     WKPreferencesSetJavaScriptCanAccessClipboard(preferences, true);
546     WKPreferencesSetDOMPasteAllowed(preferences, true);
547     WKPreferencesSetUniversalAccessFromFileURLsAllowed(preferences, true);
548     WKPreferencesSetFileAccessFromFileURLsAllowed(preferences, true);
549 #if ENABLE(FULLSCREEN_API)
550     WKPreferencesSetFullScreenEnabled(preferences, true);
551 #endif
552     WKPreferencesSetPageCacheEnabled(preferences, false);
553     WKPreferencesSetAsynchronousPluginInitializationEnabled(preferences, false);
554     WKPreferencesSetAsynchronousPluginInitializationEnabledForAllPlugins(preferences, false);
555     WKPreferencesSetArtificialPluginInitializationDelayEnabled(preferences, false);
556     WKPreferencesSetTabToLinksEnabled(preferences, false);
557     WKPreferencesSetInteractiveFormValidationEnabled(preferences, true);
558     WKPreferencesSetMockScrollbarsEnabled(preferences, true);
559
560     static WKStringRef standardFontFamily = WKStringCreateWithUTF8CString("Times");
561     static WKStringRef cursiveFontFamily = WKStringCreateWithUTF8CString("Apple Chancery");
562     static WKStringRef fantasyFontFamily = WKStringCreateWithUTF8CString("Papyrus");
563     static WKStringRef fixedFontFamily = WKStringCreateWithUTF8CString("Courier");
564     static WKStringRef pictographFontFamily = WKStringCreateWithUTF8CString("Apple Color Emoji");
565     static WKStringRef sansSerifFontFamily = WKStringCreateWithUTF8CString("Helvetica");
566     static WKStringRef serifFontFamily = WKStringCreateWithUTF8CString("Times");
567
568     WKPreferencesSetStandardFontFamily(preferences, standardFontFamily);
569     WKPreferencesSetCursiveFontFamily(preferences, cursiveFontFamily);
570     WKPreferencesSetFantasyFontFamily(preferences, fantasyFontFamily);
571     WKPreferencesSetFixedFontFamily(preferences, fixedFontFamily);
572     WKPreferencesSetPictographFontFamily(preferences, pictographFontFamily);
573     WKPreferencesSetSansSerifFontFamily(preferences, sansSerifFontFamily);
574     WKPreferencesSetSerifFontFamily(preferences, serifFontFamily);
575     WKPreferencesSetScreenFontSubstitutionEnabled(preferences, true);
576     WKPreferencesSetAsynchronousSpellCheckingEnabled(preferences, false);
577 #if ENABLE(WEB_AUDIO)
578     WKPreferencesSetMediaSourceEnabled(preferences, true);
579 #endif
580
581     WKPreferencesSetAcceleratedDrawingEnabled(preferences, m_shouldUseAcceleratedDrawing);
582 }
583
584 bool TestController::resetStateToConsistentValues()
585 {
586     m_state = Resetting;
587
588     m_beforeUnloadReturnValue = true;
589
590     WKRetainPtr<WKStringRef> messageName = adoptWK(WKStringCreateWithUTF8CString("Reset"));
591     WKRetainPtr<WKMutableDictionaryRef> resetMessageBody = adoptWK(WKMutableDictionaryCreate());
592
593     WKRetainPtr<WKStringRef> shouldGCKey = adoptWK(WKStringCreateWithUTF8CString("ShouldGC"));
594     WKRetainPtr<WKBooleanRef> shouldGCValue = adoptWK(WKBooleanCreate(m_gcBetweenTests));
595     WKDictionarySetItem(resetMessageBody.get(), shouldGCKey.get(), shouldGCValue.get());
596
597     WKContextPostMessageToInjectedBundle(TestController::shared().context(), messageName.get(), resetMessageBody.get());
598
599     WKContextSetShouldUseFontSmoothing(TestController::shared().context(), false);
600
601     WKContextSetCacheModel(TestController::shared().context(), kWKCacheModelDocumentBrowser);
602
603     // FIXME: This function should also ensure that there is only one page open.
604
605     // Reset the EventSender for each test.
606     m_eventSenderProxy = adoptPtr(new EventSenderProxy(this));
607
608     // FIXME: Is this needed? Nothing in TestController changes preferences during tests, and if there is
609     // some other code doing this, it should probably be responsible for cleanup too.
610     resetPreferencesToConsistentValues();
611
612 #if !PLATFORM(COCOA)
613     WKTextCheckerContinuousSpellCheckingEnabledStateChanged(true);
614 #endif
615
616     // in the case that a test using the chrome input field failed, be sure to clean up for the next test
617     m_mainWebView->removeChromeInputField();
618     m_mainWebView->focus();
619
620     // Re-set to the default backing scale factor by setting the custom scale factor to 0.
621     WKPageSetCustomBackingScaleFactor(m_mainWebView->page(), 0);
622
623 #if PLATFORM(EFL)
624     // EFL use a real window while other ports such as Qt don't.
625     // In EFL, we need to resize the window to the original size after calls to window.resizeTo.
626     WKRect rect = m_mainWebView->windowFrame();
627     m_mainWebView->setWindowFrame(WKRectMake(rect.origin.x, rect.origin.y, TestController::viewWidth, TestController::viewHeight));
628 #endif
629
630     // Reset notification permissions
631     m_webNotificationProvider.reset();
632
633     // Reset Geolocation permissions.
634     m_geolocationPermissionRequests.clear();
635     m_isGeolocationPermissionSet = false;
636     m_isGeolocationPermissionAllowed = false;
637
638     // Reset Custom Policy Delegate.
639     setCustomPolicyDelegate(false, false);
640
641     m_workQueueManager.clearWorkQueue();
642
643     m_handlesAuthenticationChallenges = false;
644     m_authenticationUsername = String();
645     m_authenticationPassword = String();
646
647     m_shouldBlockAllPlugins = false;
648
649     m_shouldLogHistoryClientCallbacks = false;
650
651     // Reset main page back to about:blank
652     m_doneResetting = false;
653
654     WKPageLoadURL(m_mainWebView->page(), blankURL());
655     runUntil(m_doneResetting, ShortTimeout);
656     return m_doneResetting;
657 }
658
659 struct TestCommand {
660     TestCommand() : shouldDumpPixels(false), timeout(0) { }
661
662     std::string pathOrURL;
663     bool shouldDumpPixels;
664     std::string expectedPixelHash;
665     int timeout;
666 };
667
668 class CommandTokenizer {
669 public:
670     explicit CommandTokenizer(const std::string& input)
671         : m_input(input)
672         , m_posNextSeparator(0)
673     {
674         pump();
675     }
676
677     bool hasNext() const;
678     std::string next();
679
680 private:
681     void pump();
682     static const char kSeparator = '\'';
683     const std::string& m_input;
684     std::string m_next;
685     size_t m_posNextSeparator;
686 };
687
688 void CommandTokenizer::pump()
689 {
690     if (m_posNextSeparator == std::string::npos || m_posNextSeparator == m_input.size()) {
691         m_next = std::string();
692         return;
693     }
694     size_t start = m_posNextSeparator ? m_posNextSeparator + 1 : 0;
695     m_posNextSeparator = m_input.find(kSeparator, start);
696     size_t size = m_posNextSeparator == std::string::npos ? std::string::npos : m_posNextSeparator - start;
697     m_next = std::string(m_input, start, size);
698 }
699
700 std::string CommandTokenizer::next()
701 {
702     ASSERT(hasNext());
703
704     std::string oldNext = m_next;
705     pump();
706     return oldNext;
707 }
708
709 bool CommandTokenizer::hasNext() const
710 {
711     return !m_next.empty();
712 }
713
714 NO_RETURN static void die(const std::string& inputLine)
715 {
716     fprintf(stderr, "Unexpected input line: %s\n", inputLine.c_str());
717     exit(1);
718 }
719
720 TestCommand parseInputLine(const std::string& inputLine)
721 {
722     TestCommand result;
723     CommandTokenizer tokenizer(inputLine);
724     if (!tokenizer.hasNext())
725         die(inputLine);
726
727     std::string arg = tokenizer.next();
728     result.pathOrURL = arg;
729     while (tokenizer.hasNext()) {
730         arg = tokenizer.next();
731         if (arg == std::string("--timeout")) {
732             std::string timeoutToken = tokenizer.next();
733             result.timeout = atoi(timeoutToken.c_str());
734         } else if (arg == std::string("-p") || arg == std::string("--pixel-test")) {
735             result.shouldDumpPixels = true;
736             if (tokenizer.hasNext())
737                 result.expectedPixelHash = tokenizer.next();
738         } else
739             die(inputLine);
740     }
741     return result;
742 }
743
744 bool TestController::runTest(const char* inputLine)
745 {
746     TestCommand command = parseInputLine(std::string(inputLine));
747
748     m_state = RunningTest;
749
750     m_currentInvocation = adoptPtr(new TestInvocation(command.pathOrURL));
751     if (command.shouldDumpPixels || m_shouldDumpPixelsForAllTests)
752         m_currentInvocation->setIsPixelTest(command.expectedPixelHash);
753     if (command.timeout > 0)
754         m_currentInvocation->setCustomTimeout(command.timeout);
755
756     m_currentInvocation->invoke();
757     m_currentInvocation.clear();
758
759     return true;
760 }
761
762 void TestController::runTestingServerLoop()
763 {
764     char filenameBuffer[2048];
765     while (fgets(filenameBuffer, sizeof(filenameBuffer), stdin)) {
766         char* newLineCharacter = strchr(filenameBuffer, '\n');
767         if (newLineCharacter)
768             *newLineCharacter = '\0';
769
770         if (strlen(filenameBuffer) == 0)
771             continue;
772
773         if (!runTest(filenameBuffer))
774             break;
775     }
776 }
777
778 void TestController::run()
779 {
780     if (!resetStateToConsistentValues()) {
781         TestInvocation::dumpWebProcessUnresponsiveness("<unknown> - TestController::run - Failed to reset state to consistent values\n");
782         return;
783     }
784
785     if (m_usingServerMode)
786         runTestingServerLoop();
787     else {
788         for (size_t i = 0; i < m_paths.size(); ++i) {
789             if (!runTest(m_paths[i].c_str()))
790                 break;
791         }
792     }
793 }
794
795 void TestController::runUntil(bool& done, TimeoutDuration timeoutDuration)
796 {
797     double timeout = m_noTimeout;
798     if (!m_forceNoTimeout) {
799         switch (timeoutDuration) {
800         case ShortTimeout:
801             timeout = m_shortTimeout;
802             break;
803         case LongTimeout:
804             timeout = m_longTimeout;
805             break;
806         case CustomTimeout:
807             timeout = m_timeout;
808             break;
809         case NoTimeout:
810         default:
811             timeout = m_noTimeout;
812             break;
813         }
814     }
815
816     platformRunUntil(done, timeout);
817 }
818
819 // WKContextInjectedBundleClient
820
821 void TestController::didReceiveMessageFromInjectedBundle(WKContextRef context, WKStringRef messageName, WKTypeRef messageBody, const void* clientInfo)
822 {
823     static_cast<TestController*>(const_cast<void*>(clientInfo))->didReceiveMessageFromInjectedBundle(messageName, messageBody);
824 }
825
826 void TestController::didReceiveSynchronousMessageFromInjectedBundle(WKContextRef context, WKStringRef messageName, WKTypeRef messageBody, WKTypeRef* returnData, const void* clientInfo)
827 {
828     *returnData = static_cast<TestController*>(const_cast<void*>(clientInfo))->didReceiveSynchronousMessageFromInjectedBundle(messageName, messageBody).leakRef();
829 }
830
831 void TestController::didReceiveKeyDownMessageFromInjectedBundle(WKDictionaryRef messageBodyDictionary, bool synchronous)
832 {
833     WKRetainPtr<WKStringRef> keyKey = adoptWK(WKStringCreateWithUTF8CString("Key"));
834     WKStringRef key = static_cast<WKStringRef>(WKDictionaryGetItemForKey(messageBodyDictionary, keyKey.get()));
835
836     WKRetainPtr<WKStringRef> modifiersKey = adoptWK(WKStringCreateWithUTF8CString("Modifiers"));
837     WKEventModifiers modifiers = static_cast<WKEventModifiers>(WKUInt64GetValue(static_cast<WKUInt64Ref>(WKDictionaryGetItemForKey(messageBodyDictionary, modifiersKey.get()))));
838
839     WKRetainPtr<WKStringRef> locationKey = adoptWK(WKStringCreateWithUTF8CString("Location"));
840     unsigned location = static_cast<unsigned>(WKUInt64GetValue(static_cast<WKUInt64Ref>(WKDictionaryGetItemForKey(messageBodyDictionary, locationKey.get()))));
841
842     if (synchronous)
843         WKPageSetShouldSendEventsSynchronously(mainWebView()->page(), true);
844
845     m_eventSenderProxy->keyDown(key, modifiers, location);
846
847     if (synchronous)
848         WKPageSetShouldSendEventsSynchronously(mainWebView()->page(), false);
849 }
850
851 void TestController::didReceiveMessageFromInjectedBundle(WKStringRef messageName, WKTypeRef messageBody)
852 {
853     if (WKStringIsEqualToUTF8CString(messageName, "EventSender")) {
854         ASSERT(WKGetTypeID(messageBody) == WKDictionaryGetTypeID());
855         WKDictionaryRef messageBodyDictionary = static_cast<WKDictionaryRef>(messageBody);
856
857         WKRetainPtr<WKStringRef> subMessageKey(AdoptWK, WKStringCreateWithUTF8CString("SubMessage"));
858         WKStringRef subMessageName = static_cast<WKStringRef>(WKDictionaryGetItemForKey(messageBodyDictionary, subMessageKey.get()));
859
860         if (WKStringIsEqualToUTF8CString(subMessageName, "MouseDown") || WKStringIsEqualToUTF8CString(subMessageName, "MouseUp")) {
861             WKRetainPtr<WKStringRef> buttonKey = adoptWK(WKStringCreateWithUTF8CString("Button"));
862             unsigned button = static_cast<unsigned>(WKUInt64GetValue(static_cast<WKUInt64Ref>(WKDictionaryGetItemForKey(messageBodyDictionary, buttonKey.get()))));
863
864             WKRetainPtr<WKStringRef> modifiersKey = adoptWK(WKStringCreateWithUTF8CString("Modifiers"));
865             WKEventModifiers modifiers = static_cast<WKEventModifiers>(WKUInt64GetValue(static_cast<WKUInt64Ref>(WKDictionaryGetItemForKey(messageBodyDictionary, modifiersKey.get()))));
866
867             // Forward to WebProcess
868             WKPageSetShouldSendEventsSynchronously(mainWebView()->page(), false);
869             if (WKStringIsEqualToUTF8CString(subMessageName, "MouseDown"))
870                 m_eventSenderProxy->mouseDown(button, modifiers);
871             else
872                 m_eventSenderProxy->mouseUp(button, modifiers);
873
874             return;
875         }
876
877         if (WKStringIsEqualToUTF8CString(subMessageName, "KeyDown")) {
878             didReceiveKeyDownMessageFromInjectedBundle(messageBodyDictionary, false);
879
880             return;
881         }
882
883         if (WKStringIsEqualToUTF8CString(subMessageName, "MouseScrollByWithWheelAndMomentumPhases")) {
884             WKRetainPtr<WKStringRef> xKey = adoptWK(WKStringCreateWithUTF8CString("X"));
885             double x = WKDoubleGetValue(static_cast<WKDoubleRef>(WKDictionaryGetItemForKey(messageBodyDictionary, xKey.get())));
886             
887             WKRetainPtr<WKStringRef> yKey = adoptWK(WKStringCreateWithUTF8CString("Y"));
888             double y = WKDoubleGetValue(static_cast<WKDoubleRef>(WKDictionaryGetItemForKey(messageBodyDictionary, yKey.get())));
889             
890             WKRetainPtr<WKStringRef> phaseKey = adoptWK(WKStringCreateWithUTF8CString("Phase"));
891             int phase = static_cast<int>(WKUInt64GetValue(static_cast<WKUInt64Ref>(WKDictionaryGetItemForKey(messageBodyDictionary, phaseKey.get()))));
892             WKRetainPtr<WKStringRef> momentumKey = adoptWK(WKStringCreateWithUTF8CString("Momentum"));
893             int momentum = static_cast<int>(WKUInt64GetValue(static_cast<WKUInt64Ref>(WKDictionaryGetItemForKey(messageBodyDictionary, momentumKey.get()))));
894             
895             // Forward to WebProcess
896             WKPageSetShouldSendEventsSynchronously(mainWebView()->page(), false);
897             m_eventSenderProxy->mouseScrollByWithWheelAndMomentumPhases(x, y, phase, momentum);
898
899             return;
900         }
901
902         ASSERT_NOT_REACHED();
903     }
904
905     if (!m_currentInvocation)
906         return;
907
908     m_currentInvocation->didReceiveMessageFromInjectedBundle(messageName, messageBody);
909 }
910
911 WKRetainPtr<WKTypeRef> TestController::didReceiveSynchronousMessageFromInjectedBundle(WKStringRef messageName, WKTypeRef messageBody)
912 {
913     if (WKStringIsEqualToUTF8CString(messageName, "EventSender")) {
914         ASSERT(WKGetTypeID(messageBody) == WKDictionaryGetTypeID());
915         WKDictionaryRef messageBodyDictionary = static_cast<WKDictionaryRef>(messageBody);
916
917         WKRetainPtr<WKStringRef> subMessageKey(AdoptWK, WKStringCreateWithUTF8CString("SubMessage"));
918         WKStringRef subMessageName = static_cast<WKStringRef>(WKDictionaryGetItemForKey(messageBodyDictionary, subMessageKey.get()));
919
920         if (WKStringIsEqualToUTF8CString(subMessageName, "KeyDown")) {
921             didReceiveKeyDownMessageFromInjectedBundle(messageBodyDictionary, true);
922
923             return 0;
924         }
925
926         if (WKStringIsEqualToUTF8CString(subMessageName, "MouseDown") || WKStringIsEqualToUTF8CString(subMessageName, "MouseUp")) {
927             WKRetainPtr<WKStringRef> buttonKey = adoptWK(WKStringCreateWithUTF8CString("Button"));
928             unsigned button = static_cast<unsigned>(WKUInt64GetValue(static_cast<WKUInt64Ref>(WKDictionaryGetItemForKey(messageBodyDictionary, buttonKey.get()))));
929
930             WKRetainPtr<WKStringRef> modifiersKey = adoptWK(WKStringCreateWithUTF8CString("Modifiers"));
931             WKEventModifiers modifiers = static_cast<WKEventModifiers>(WKUInt64GetValue(static_cast<WKUInt64Ref>(WKDictionaryGetItemForKey(messageBodyDictionary, modifiersKey.get()))));
932
933             // Forward to WebProcess
934             WKPageSetShouldSendEventsSynchronously(mainWebView()->page(), true);
935             if (WKStringIsEqualToUTF8CString(subMessageName, "MouseDown"))
936                 m_eventSenderProxy->mouseDown(button, modifiers);
937             else
938                 m_eventSenderProxy->mouseUp(button, modifiers);
939             WKPageSetShouldSendEventsSynchronously(mainWebView()->page(), false);
940             return 0;
941         }
942
943         if (WKStringIsEqualToUTF8CString(subMessageName, "MouseMoveTo")) {
944             WKRetainPtr<WKStringRef> xKey = adoptWK(WKStringCreateWithUTF8CString("X"));
945             double x = WKDoubleGetValue(static_cast<WKDoubleRef>(WKDictionaryGetItemForKey(messageBodyDictionary, xKey.get())));
946
947             WKRetainPtr<WKStringRef> yKey = adoptWK(WKStringCreateWithUTF8CString("Y"));
948             double y = WKDoubleGetValue(static_cast<WKDoubleRef>(WKDictionaryGetItemForKey(messageBodyDictionary, yKey.get())));
949
950             // Forward to WebProcess
951             WKPageSetShouldSendEventsSynchronously(mainWebView()->page(), true);
952             m_eventSenderProxy->mouseMoveTo(x, y);
953             WKPageSetShouldSendEventsSynchronously(mainWebView()->page(), false);
954             return 0;
955         }
956
957         if (WKStringIsEqualToUTF8CString(subMessageName, "MouseScrollBy")) {
958             WKRetainPtr<WKStringRef> xKey = adoptWK(WKStringCreateWithUTF8CString("X"));
959             double x = WKDoubleGetValue(static_cast<WKDoubleRef>(WKDictionaryGetItemForKey(messageBodyDictionary, xKey.get())));
960
961             WKRetainPtr<WKStringRef> yKey = adoptWK(WKStringCreateWithUTF8CString("Y"));
962             double y = WKDoubleGetValue(static_cast<WKDoubleRef>(WKDictionaryGetItemForKey(messageBodyDictionary, yKey.get())));
963
964             // Forward to WebProcess
965             WKPageSetShouldSendEventsSynchronously(mainWebView()->page(), true);
966             m_eventSenderProxy->mouseScrollBy(x, y);
967             WKPageSetShouldSendEventsSynchronously(mainWebView()->page(), false);
968             return 0;
969         }
970
971         if (WKStringIsEqualToUTF8CString(subMessageName, "MouseScrollByWithWheelAndMomentumPhases")) {
972             WKRetainPtr<WKStringRef> xKey = adoptWK(WKStringCreateWithUTF8CString("X"));
973             double x = WKDoubleGetValue(static_cast<WKDoubleRef>(WKDictionaryGetItemForKey(messageBodyDictionary, xKey.get())));
974             
975             WKRetainPtr<WKStringRef> yKey = adoptWK(WKStringCreateWithUTF8CString("Y"));
976             double y = WKDoubleGetValue(static_cast<WKDoubleRef>(WKDictionaryGetItemForKey(messageBodyDictionary, yKey.get())));
977             
978             WKRetainPtr<WKStringRef> phaseKey = adoptWK(WKStringCreateWithUTF8CString("Phase"));
979             int phase = static_cast<int>(WKUInt64GetValue(static_cast<WKUInt64Ref>(WKDictionaryGetItemForKey(messageBodyDictionary, phaseKey.get()))));
980             WKRetainPtr<WKStringRef> momentumKey = adoptWK(WKStringCreateWithUTF8CString("Momentum"));
981             int momentum = static_cast<int>(WKUInt64GetValue(static_cast<WKUInt64Ref>(WKDictionaryGetItemForKey(messageBodyDictionary, momentumKey.get()))));
982
983             // Forward to WebProcess
984             WKPageSetShouldSendEventsSynchronously(mainWebView()->page(), true);
985             m_eventSenderProxy->mouseScrollByWithWheelAndMomentumPhases(x, y, phase, momentum);
986             WKPageSetShouldSendEventsSynchronously(mainWebView()->page(), false);
987             return 0;
988         }
989         
990         if (WKStringIsEqualToUTF8CString(subMessageName, "ContinuousMouseScrollBy")) {
991             WKRetainPtr<WKStringRef> xKey = adoptWK(WKStringCreateWithUTF8CString("X"));
992             double x = WKDoubleGetValue(static_cast<WKDoubleRef>(WKDictionaryGetItemForKey(messageBodyDictionary, xKey.get())));
993
994             WKRetainPtr<WKStringRef> yKey = adoptWK(WKStringCreateWithUTF8CString("Y"));
995             double y = WKDoubleGetValue(static_cast<WKDoubleRef>(WKDictionaryGetItemForKey(messageBodyDictionary, yKey.get())));
996
997             WKRetainPtr<WKStringRef> pagedKey = adoptWK(WKStringCreateWithUTF8CString("Paged"));
998             bool paged = static_cast<bool>(WKUInt64GetValue(static_cast<WKUInt64Ref>(WKDictionaryGetItemForKey(messageBodyDictionary, pagedKey.get()))));
999
1000             // Forward to WebProcess
1001             WKPageSetShouldSendEventsSynchronously(mainWebView()->page(), true);
1002             m_eventSenderProxy->continuousMouseScrollBy(x, y, paged);
1003             WKPageSetShouldSendEventsSynchronously(mainWebView()->page(), false);
1004             return 0;
1005         }
1006
1007         if (WKStringIsEqualToUTF8CString(subMessageName, "LeapForward")) {
1008             WKRetainPtr<WKStringRef> timeKey = adoptWK(WKStringCreateWithUTF8CString("TimeInMilliseconds"));
1009             unsigned time = static_cast<unsigned>(WKUInt64GetValue(static_cast<WKUInt64Ref>(WKDictionaryGetItemForKey(messageBodyDictionary, timeKey.get()))));
1010
1011             m_eventSenderProxy->leapForward(time);
1012             return 0;
1013         }
1014
1015 #if ENABLE(TOUCH_EVENTS)
1016         if (WKStringIsEqualToUTF8CString(subMessageName, "AddTouchPoint")) {
1017             WKRetainPtr<WKStringRef> xKey = adoptWK(WKStringCreateWithUTF8CString("X"));
1018             int x = static_cast<int>(WKUInt64GetValue(static_cast<WKUInt64Ref>(WKDictionaryGetItemForKey(messageBodyDictionary, xKey.get()))));
1019
1020             WKRetainPtr<WKStringRef> yKey = adoptWK(WKStringCreateWithUTF8CString("Y"));
1021             int y = static_cast<int>(WKUInt64GetValue(static_cast<WKUInt64Ref>(WKDictionaryGetItemForKey(messageBodyDictionary, yKey.get()))));
1022
1023             m_eventSenderProxy->addTouchPoint(x, y);
1024             return 0;
1025         }
1026
1027         if (WKStringIsEqualToUTF8CString(subMessageName, "UpdateTouchPoint")) {
1028             WKRetainPtr<WKStringRef> indexKey = adoptWK(WKStringCreateWithUTF8CString("Index"));
1029             int index = static_cast<int>(WKUInt64GetValue(static_cast<WKUInt64Ref>(WKDictionaryGetItemForKey(messageBodyDictionary, indexKey.get()))));
1030
1031             WKRetainPtr<WKStringRef> xKey = adoptWK(WKStringCreateWithUTF8CString("X"));
1032             int x = static_cast<int>(WKUInt64GetValue(static_cast<WKUInt64Ref>(WKDictionaryGetItemForKey(messageBodyDictionary, xKey.get()))));
1033
1034             WKRetainPtr<WKStringRef> yKey = adoptWK(WKStringCreateWithUTF8CString("Y"));
1035             int y = static_cast<int>(WKUInt64GetValue(static_cast<WKUInt64Ref>(WKDictionaryGetItemForKey(messageBodyDictionary, yKey.get()))));
1036
1037             m_eventSenderProxy->updateTouchPoint(index, x, y);
1038             return 0;
1039         }
1040
1041         if (WKStringIsEqualToUTF8CString(subMessageName, "SetTouchModifier")) {
1042             WKRetainPtr<WKStringRef> modifierKey = adoptWK(WKStringCreateWithUTF8CString("Modifier"));
1043             WKEventModifiers modifier = static_cast<WKEventModifiers>(WKUInt64GetValue(static_cast<WKUInt64Ref>(WKDictionaryGetItemForKey(messageBodyDictionary, modifierKey.get()))));
1044
1045             WKRetainPtr<WKStringRef> enableKey = adoptWK(WKStringCreateWithUTF8CString("Enable"));
1046             bool enable = static_cast<bool>(WKUInt64GetValue(static_cast<WKUInt64Ref>(WKDictionaryGetItemForKey(messageBodyDictionary, enableKey.get()))));
1047
1048             m_eventSenderProxy->setTouchModifier(modifier, enable);
1049             return 0;
1050         }
1051
1052         if (WKStringIsEqualToUTF8CString(subMessageName, "SetTouchPointRadius")) {
1053             WKRetainPtr<WKStringRef> xKey = adoptWK(WKStringCreateWithUTF8CString("RadiusX"));
1054             int x = static_cast<int>(WKUInt64GetValue(static_cast<WKUInt64Ref>(WKDictionaryGetItemForKey(messageBodyDictionary, xKey.get()))));
1055
1056             WKRetainPtr<WKStringRef> yKey = adoptWK(WKStringCreateWithUTF8CString("RadiusY"));
1057             int y = static_cast<int>(WKUInt64GetValue(static_cast<WKUInt64Ref>(WKDictionaryGetItemForKey(messageBodyDictionary, yKey.get()))));
1058
1059             m_eventSenderProxy->setTouchPointRadius(x, y);
1060             return 0;
1061         }
1062
1063         if (WKStringIsEqualToUTF8CString(subMessageName, "TouchStart")) {
1064             WKPageSetShouldSendEventsSynchronously(mainWebView()->page(), true);
1065             m_eventSenderProxy->touchStart();
1066             WKPageSetShouldSendEventsSynchronously(mainWebView()->page(), false);
1067             return 0;
1068         }
1069
1070         if (WKStringIsEqualToUTF8CString(subMessageName, "TouchMove")) {
1071             WKPageSetShouldSendEventsSynchronously(mainWebView()->page(), true);
1072             m_eventSenderProxy->touchMove();
1073             WKPageSetShouldSendEventsSynchronously(mainWebView()->page(), false);
1074             return 0;
1075         }
1076
1077         if (WKStringIsEqualToUTF8CString(subMessageName, "TouchEnd")) {
1078             WKPageSetShouldSendEventsSynchronously(mainWebView()->page(), true);
1079             m_eventSenderProxy->touchEnd();
1080             WKPageSetShouldSendEventsSynchronously(mainWebView()->page(), false);
1081             return 0;
1082         }
1083
1084         if (WKStringIsEqualToUTF8CString(subMessageName, "TouchCancel")) {
1085             WKPageSetShouldSendEventsSynchronously(mainWebView()->page(), true);
1086             m_eventSenderProxy->touchCancel();
1087             WKPageSetShouldSendEventsSynchronously(mainWebView()->page(), false);
1088             return 0;
1089         }
1090
1091         if (WKStringIsEqualToUTF8CString(subMessageName, "ClearTouchPoints")) {
1092             m_eventSenderProxy->clearTouchPoints();
1093             return 0;
1094         }
1095
1096         if (WKStringIsEqualToUTF8CString(subMessageName, "ReleaseTouchPoint")) {
1097             WKRetainPtr<WKStringRef> indexKey = adoptWK(WKStringCreateWithUTF8CString("Index"));
1098             int index = static_cast<int>(WKUInt64GetValue(static_cast<WKUInt64Ref>(WKDictionaryGetItemForKey(messageBodyDictionary, indexKey.get()))));
1099             m_eventSenderProxy->releaseTouchPoint(index);
1100             return 0;
1101         }
1102
1103         if (WKStringIsEqualToUTF8CString(subMessageName, "CancelTouchPoint")) {
1104             WKRetainPtr<WKStringRef> indexKey = adoptWK(WKStringCreateWithUTF8CString("Index"));
1105             int index = static_cast<int>(WKUInt64GetValue(static_cast<WKUInt64Ref>(WKDictionaryGetItemForKey(messageBodyDictionary, indexKey.get()))));
1106             m_eventSenderProxy->cancelTouchPoint(index);
1107             return 0;
1108         }
1109 #endif
1110         ASSERT_NOT_REACHED();
1111     }
1112     return m_currentInvocation->didReceiveSynchronousMessageFromInjectedBundle(messageName, messageBody);
1113 }
1114
1115 // WKPageLoaderClient
1116
1117 void TestController::didCommitLoadForFrame(WKPageRef page, WKFrameRef frame, WKTypeRef, const void* clientInfo)
1118 {
1119     static_cast<TestController*>(const_cast<void*>(clientInfo))->didCommitLoadForFrame(page, frame);
1120 }
1121
1122 void TestController::didFinishLoadForFrame(WKPageRef page, WKFrameRef frame, WKTypeRef, const void* clientInfo)
1123 {
1124     static_cast<TestController*>(const_cast<void*>(clientInfo))->didFinishLoadForFrame(page, frame);
1125 }
1126
1127 void TestController::didReceiveAuthenticationChallengeInFrame(WKPageRef page, WKFrameRef frame, WKAuthenticationChallengeRef authenticationChallenge, const void *clientInfo)
1128 {
1129     static_cast<TestController*>(const_cast<void*>(clientInfo))->didReceiveAuthenticationChallengeInFrame(page, frame, authenticationChallenge);
1130 }
1131
1132 void TestController::processDidCrash(WKPageRef page, const void* clientInfo)
1133 {
1134     static_cast<TestController*>(const_cast<void*>(clientInfo))->processDidCrash();
1135 }
1136
1137 WKPluginLoadPolicy TestController::pluginLoadPolicy(WKPageRef page, WKPluginLoadPolicy currentPluginLoadPolicy, WKDictionaryRef pluginInformation, WKStringRef* unavailabilityDescription, const void* clientInfo)
1138 {
1139     return static_cast<TestController*>(const_cast<void*>(clientInfo))->pluginLoadPolicy(page, currentPluginLoadPolicy, pluginInformation, unavailabilityDescription);
1140 }
1141
1142 WKPluginLoadPolicy TestController::pluginLoadPolicy(WKPageRef, WKPluginLoadPolicy currentPluginLoadPolicy, WKDictionaryRef pluginInformation, WKStringRef* unavailabilityDescription)
1143 {
1144     if (m_shouldBlockAllPlugins)
1145         return kWKPluginLoadPolicyBlocked;
1146     return currentPluginLoadPolicy;
1147 }
1148
1149 void TestController::didCommitLoadForFrame(WKPageRef page, WKFrameRef frame)
1150 {
1151     if (!WKFrameIsMainFrame(frame))
1152         return;
1153
1154     mainWebView()->focus();
1155 }
1156
1157 void TestController::didFinishLoadForFrame(WKPageRef page, WKFrameRef frame)
1158 {
1159     if (m_state != Resetting)
1160         return;
1161
1162     if (!WKFrameIsMainFrame(frame))
1163         return;
1164
1165     WKRetainPtr<WKURLRef> wkURL(AdoptWK, WKFrameCopyURL(frame));
1166     if (!WKURLIsEqual(wkURL.get(), blankURL()))
1167         return;
1168
1169     m_doneResetting = true;
1170     shared().notifyDone();
1171 }
1172
1173 void TestController::didReceiveAuthenticationChallengeInFrame(WKPageRef page, WKFrameRef frame, WKAuthenticationChallengeRef authenticationChallenge)
1174 {
1175     String message;
1176     if (!m_handlesAuthenticationChallenges)
1177         message = "<unknown> - didReceiveAuthenticationChallenge - Simulating cancelled authentication sheet\n";
1178     else
1179         message = String::format("<unknown> - didReceiveAuthenticationChallenge - Responding with %s:%s\n", m_authenticationUsername.utf8().data(), m_authenticationPassword.utf8().data());
1180     m_currentInvocation->outputText(message);
1181
1182     WKAuthenticationDecisionListenerRef decisionListener = WKAuthenticationChallengeGetDecisionListener(authenticationChallenge);
1183     if (!m_handlesAuthenticationChallenges) {
1184         WKAuthenticationDecisionListenerUseCredential(decisionListener, 0);
1185         return;
1186     }
1187     WKRetainPtr<WKStringRef> username(AdoptWK, WKStringCreateWithUTF8CString(m_authenticationUsername.utf8().data()));
1188     WKRetainPtr<WKStringRef> password(AdoptWK, WKStringCreateWithUTF8CString(m_authenticationPassword.utf8().data()));
1189     WKRetainPtr<WKCredentialRef> credential(AdoptWK, WKCredentialCreate(username.get(), password.get(), kWKCredentialPersistenceForSession));
1190     WKAuthenticationDecisionListenerUseCredential(decisionListener, credential.get());
1191 }
1192
1193 void TestController::processDidCrash()
1194 {
1195     // This function can be called multiple times when crash logs are being saved on Windows, so
1196     // ensure we only print the crashed message once.
1197     if (!m_didPrintWebProcessCrashedMessage) {
1198 #if PLATFORM(COCOA)
1199         pid_t pid = WKPageGetProcessIdentifier(m_mainWebView->page());
1200         fprintf(stderr, "#CRASHED - WebProcess (pid %ld)\n", static_cast<long>(pid));
1201 #else
1202         fputs("#CRASHED - WebProcess\n", stderr);
1203 #endif
1204         fflush(stderr);
1205         m_didPrintWebProcessCrashedMessage = true;
1206     }
1207
1208     if (m_shouldExitWhenWebProcessCrashes)
1209         exit(1);
1210 }
1211
1212 void TestController::simulateWebNotificationClick(uint64_t notificationID)
1213 {
1214     m_webNotificationProvider.simulateWebNotificationClick(notificationID);
1215 }
1216
1217 void TestController::setGeolocationPermission(bool enabled)
1218 {
1219     m_isGeolocationPermissionSet = true;
1220     m_isGeolocationPermissionAllowed = enabled;
1221     decidePolicyForGeolocationPermissionRequestIfPossible();
1222 }
1223
1224 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)
1225 {
1226     m_geolocationProvider->setPosition(latitude, longitude, accuracy, providesAltitude, altitude, providesAltitudeAccuracy, altitudeAccuracy, providesHeading, heading, providesSpeed, speed);
1227 }
1228
1229 void TestController::setMockGeolocationPositionUnavailableError(WKStringRef errorMessage)
1230 {
1231     m_geolocationProvider->setPositionUnavailableError(errorMessage);
1232 }
1233
1234 void TestController::handleGeolocationPermissionRequest(WKGeolocationPermissionRequestRef geolocationPermissionRequest)
1235 {
1236     m_geolocationPermissionRequests.append(geolocationPermissionRequest);
1237     decidePolicyForGeolocationPermissionRequestIfPossible();
1238 }
1239
1240 void TestController::setCustomPolicyDelegate(bool enabled, bool permissive)
1241 {
1242     m_policyDelegateEnabled = enabled;
1243     m_policyDelegatePermissive = permissive;
1244 }
1245
1246 void TestController::decidePolicyForGeolocationPermissionRequestIfPossible()
1247 {
1248     if (!m_isGeolocationPermissionSet)
1249         return;
1250
1251     for (size_t i = 0; i < m_geolocationPermissionRequests.size(); ++i) {
1252         WKGeolocationPermissionRequestRef permissionRequest = m_geolocationPermissionRequests[i].get();
1253         if (m_isGeolocationPermissionAllowed)
1254             WKGeolocationPermissionRequestAllow(permissionRequest);
1255         else
1256             WKGeolocationPermissionRequestDeny(permissionRequest);
1257     }
1258     m_geolocationPermissionRequests.clear();
1259 }
1260
1261 void TestController::decidePolicyForNotificationPermissionRequest(WKPageRef page, WKSecurityOriginRef origin, WKNotificationPermissionRequestRef request, const void*)
1262 {
1263     TestController::shared().decidePolicyForNotificationPermissionRequest(page, origin, request);
1264 }
1265
1266 void TestController::decidePolicyForNotificationPermissionRequest(WKPageRef, WKSecurityOriginRef, WKNotificationPermissionRequestRef request)
1267 {
1268     WKNotificationPermissionRequestAllow(request);
1269 }
1270
1271 void TestController::unavailablePluginButtonClicked(WKPageRef, WKPluginUnavailabilityReason, WKDictionaryRef, const void*)
1272 {
1273     printf("MISSING PLUGIN BUTTON PRESSED\n");
1274 }
1275
1276 void TestController::decidePolicyForNavigationAction(WKPageRef, WKFrameRef, WKFrameNavigationType, WKEventModifiers, WKEventMouseButton, WKFrameRef, WKURLRequestRef, WKFramePolicyListenerRef listener, WKTypeRef, const void* clientInfo)
1277 {
1278     static_cast<TestController*>(const_cast<void*>(clientInfo))->decidePolicyForNavigationAction(listener);
1279 }
1280
1281 void TestController::decidePolicyForNavigationAction(WKFramePolicyListenerRef listener)
1282 {
1283     if (m_policyDelegateEnabled && !m_policyDelegatePermissive) {
1284         WKFramePolicyListenerIgnore(listener);
1285         return;
1286     }
1287
1288     WKFramePolicyListenerUse(listener);
1289 }
1290
1291 void TestController::decidePolicyForResponse(WKPageRef, WKFrameRef frame, WKURLResponseRef response, WKURLRequestRef, bool canShowMIMEType, WKFramePolicyListenerRef listener, WKTypeRef, const void* clientInfo)
1292 {
1293     static_cast<TestController*>(const_cast<void*>(clientInfo))->decidePolicyForResponse(frame, response, listener);
1294 }
1295
1296 void TestController::decidePolicyForResponse(WKFrameRef frame, WKURLResponseRef response, WKFramePolicyListenerRef listener)
1297 {
1298     // Even though Response was already checked by WKBundlePagePolicyClient, the check did not include plugins
1299     // so we have to re-check again.
1300     WKRetainPtr<WKStringRef> wkMIMEType(AdoptWK, WKURLResponseCopyMIMEType(response));
1301     if (WKFrameCanShowMIMEType(frame, wkMIMEType.get())) {
1302         WKFramePolicyListenerUse(listener);
1303         return;
1304     }
1305
1306     WKFramePolicyListenerIgnore(listener);
1307 }
1308
1309 void TestController::didNavigateWithNavigationData(WKContextRef, WKPageRef, WKNavigationDataRef navigationData, WKFrameRef frame, const void* clientInfo)
1310 {
1311     static_cast<TestController*>(const_cast<void*>(clientInfo))->didNavigateWithNavigationData(navigationData, frame);
1312 }
1313
1314 void TestController::didNavigateWithNavigationData(WKNavigationDataRef navigationData, WKFrameRef)
1315 {
1316     if (m_state != RunningTest)
1317         return;
1318
1319     if (!m_shouldLogHistoryClientCallbacks)
1320         return;
1321
1322     // URL
1323     WKRetainPtr<WKURLRef> urlWK = adoptWK(WKNavigationDataCopyURL(navigationData));
1324     WKRetainPtr<WKStringRef> urlStringWK = adoptWK(WKURLCopyString(urlWK.get()));
1325     // Title
1326     WKRetainPtr<WKStringRef> titleWK = adoptWK(WKNavigationDataCopyTitle(navigationData));
1327     // HTTP method
1328     WKRetainPtr<WKURLRequestRef> requestWK = adoptWK(WKNavigationDataCopyOriginalRequest(navigationData));
1329     WKRetainPtr<WKStringRef> methodWK = adoptWK(WKURLRequestCopyHTTPMethod(requestWK.get()));
1330
1331     // FIXME: Determine whether the navigation was successful / a client redirect rather than hard-coding the message here.
1332     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",
1333         toSTD(urlStringWK).c_str(), toSTD(titleWK).c_str(), toSTD(methodWK).c_str()));
1334 }
1335
1336 void TestController::didPerformClientRedirect(WKContextRef, WKPageRef, WKURLRef sourceURL, WKURLRef destinationURL, WKFrameRef frame, const void* clientInfo)
1337 {
1338     static_cast<TestController*>(const_cast<void*>(clientInfo))->didPerformClientRedirect(sourceURL, destinationURL, frame);
1339 }
1340
1341 void TestController::didPerformClientRedirect(WKURLRef sourceURL, WKURLRef destinationURL, WKFrameRef)
1342 {
1343     if (m_state != RunningTest)
1344         return;
1345
1346     if (!m_shouldLogHistoryClientCallbacks)
1347         return;
1348
1349     WKRetainPtr<WKStringRef> sourceStringWK = adoptWK(WKURLCopyString(sourceURL));
1350     WKRetainPtr<WKStringRef> destinationStringWK = adoptWK(WKURLCopyString(destinationURL));
1351
1352     m_currentInvocation->outputText(String::format("WebView performed a client redirect from \"%s\" to \"%s\".\n", toSTD(sourceStringWK).c_str(), toSTD(destinationStringWK).c_str()));
1353 }
1354
1355 void TestController::didPerformServerRedirect(WKContextRef, WKPageRef, WKURLRef sourceURL, WKURLRef destinationURL, WKFrameRef frame, const void* clientInfo)
1356 {
1357     static_cast<TestController*>(const_cast<void*>(clientInfo))->didPerformServerRedirect(sourceURL, destinationURL, frame);
1358 }
1359
1360 void TestController::didPerformServerRedirect(WKURLRef sourceURL, WKURLRef destinationURL, WKFrameRef)
1361 {
1362     if (m_state != RunningTest)
1363         return;
1364
1365     if (!m_shouldLogHistoryClientCallbacks)
1366         return;
1367
1368     WKRetainPtr<WKStringRef> sourceStringWK = adoptWK(WKURLCopyString(sourceURL));
1369     WKRetainPtr<WKStringRef> destinationStringWK = adoptWK(WKURLCopyString(destinationURL));
1370
1371     m_currentInvocation->outputText(String::format("WebView performed a server redirect from \"%s\" to \"%s\".\n", toSTD(sourceStringWK).c_str(), toSTD(destinationStringWK).c_str()));
1372 }
1373
1374 void TestController::didUpdateHistoryTitle(WKContextRef, WKPageRef, WKStringRef title, WKURLRef URL, WKFrameRef frame, const void* clientInfo)
1375 {
1376     static_cast<TestController*>(const_cast<void*>(clientInfo))->didUpdateHistoryTitle(title, URL, frame);
1377 }
1378
1379 void TestController::didUpdateHistoryTitle(WKStringRef title, WKURLRef URL, WKFrameRef)
1380 {
1381     if (m_state != RunningTest)
1382         return;
1383
1384     if (!m_shouldLogHistoryClientCallbacks)
1385         return;
1386
1387     WKRetainPtr<WKStringRef> urlStringWK(AdoptWK, WKURLCopyString(URL));
1388     m_currentInvocation->outputText(String::format("WebView updated the title for history URL \"%s\" to \"%s\".\n", toSTD(urlStringWK).c_str(), toSTD(title).c_str()));
1389 }
1390
1391 } // namespace WTR