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