Merge "[Cherry-pick][[Text Autosizing] Refactoring to eliminate boolean parameter...
[framework/web/webkit-efl.git] / Tools / WebKitTestRunner / TestController.cpp
1 /*
2  * Copyright (C) 2010 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 "PlatformWebView.h"
30 #include "StringFunctions.h"
31 #include "TestInvocation.h"
32 #include <WebKit2/WKContextPrivate.h>
33 #include <WebKit2/WKNumber.h>
34 #include <WebKit2/WKPageGroup.h>
35 #include <WebKit2/WKPagePrivate.h>
36 #include <WebKit2/WKPreferencesPrivate.h>
37 #include <WebKit2/WKRetainPtr.h>
38 #include <cstdio>
39 #include <wtf/PassOwnPtr.h>
40
41 #if PLATFORM(MAC)
42 #include <WebKit2/WKPagePrivateMac.h>
43 #endif
44
45 #if PLATFORM(MAC) || PLATFORM(QT) || PLATFORM(GTK) || PLATFORM(EFL)
46 #include "EventSenderProxy.h"
47 #endif
48
49 #if !PLATFORM(MAC)
50 #include <WebKit2/WKTextChecker.h>
51 #endif
52
53 namespace WTR {
54
55 static const double defaultLongTimeout = 30;
56 static const double defaultShortTimeout = 15;
57 static const double defaultNoTimeout = -1;
58
59 static WKURLRef blankURL()
60 {
61     static WKURLRef staticBlankURL = WKURLCreateWithUTF8CString("about:blank");
62     return staticBlankURL;
63 }
64
65 static TestController* controller;
66
67 TestController& TestController::shared()
68 {
69     ASSERT(controller);
70     return *controller;
71 }
72
73 TestController::TestController(int argc, const char* argv[])
74     : m_dumpPixelsForAllTests(false)
75     , m_verbose(false)
76     , m_printSeparators(false)
77     , m_usingServerMode(false)
78     , m_gcBetweenTests(false)
79     , m_state(Initial)
80     , m_doneResetting(false)
81     , m_longTimeout(defaultLongTimeout)
82     , m_shortTimeout(defaultShortTimeout)
83     , m_noTimeout(defaultNoTimeout)
84     , m_useWaitToDumpWatchdogTimer(true)
85     , m_forceNoTimeout(false)
86     , m_didPrintWebProcessCrashedMessage(false)
87     , m_shouldExitWhenWebProcessCrashes(true)
88     , m_beforeUnloadReturnValue(true)
89 #if PLATFORM(MAC) || PLATFORM(QT) || PLATFORM(GTK) || PLATFORM(EFL)
90     , m_eventSenderProxy(new EventSenderProxy(this))
91 #endif
92 {
93     initialize(argc, argv);
94     controller = this;
95     run();
96     controller = 0;
97 }
98
99 TestController::~TestController()
100 {
101 }
102
103 static WKRect getWindowFrameMainPage(WKPageRef page, const void* clientInfo)
104 {
105     PlatformWebView* view = static_cast<TestController*>(const_cast<void*>(clientInfo))->mainWebView();
106     return view->windowFrame();
107 }
108
109 static void setWindowFrameMainPage(WKPageRef page, WKRect frame, const void* clientInfo)
110 {
111     PlatformWebView* view = static_cast<TestController*>(const_cast<void*>(clientInfo))->mainWebView();
112     view->setWindowFrame(frame);
113 }
114
115 static WKRect getWindowFrameOtherPage(WKPageRef page, const void* clientInfo)
116 {
117     PlatformWebView* view = static_cast<PlatformWebView*>(const_cast<void*>(clientInfo));
118     return view->windowFrame();
119 }
120
121 static void setWindowFrameOtherPage(WKPageRef page, WKRect frame, const void* clientInfo)
122 {
123     PlatformWebView* view = static_cast<PlatformWebView*>(const_cast<void*>(clientInfo));
124     view->setWindowFrame(frame);
125 }
126
127 static bool runBeforeUnloadConfirmPanel(WKPageRef page, WKStringRef message, WKFrameRef frame, const void *clientInfo)
128 {
129     TestController* testController = static_cast<TestController*>(const_cast<void*>(clientInfo));
130     printf("CONFIRM NAVIGATION: %s\n", toSTD(message).c_str());
131     return testController->beforeUnloadReturnValue();
132 }
133
134 #if ENABLE(TIZEN_SQL_DATABASE)
135 static bool exceededDatabaseQuota(WKPageRef, WKFrameRef, WKSecurityOriginRef, WKStringRef, unsigned long long, const void*)
136 {
137     return true;
138 }
139 #else
140 static unsigned long long exceededDatabaseQuota(WKPageRef, WKFrameRef, WKSecurityOriginRef, WKStringRef, WKStringRef, unsigned long long, unsigned long long, unsigned long long, unsigned long long, const void*)
141 {
142     static const unsigned long long defaultQuota = 5 * 1024 * 1024;
143     return defaultQuota;
144 }
145 #endif
146
147 void TestController::runModal(WKPageRef page, const void* clientInfo)
148 {
149     PlatformWebView* view = static_cast<PlatformWebView*>(const_cast<void*>(clientInfo));
150     view->setWindowIsKey(false);
151     runModal(view);
152     view->setWindowIsKey(true);
153 }
154
155 static void closeOtherPage(WKPageRef page, const void* clientInfo)
156 {
157     WKPageClose(page);
158     PlatformWebView* view = static_cast<PlatformWebView*>(const_cast<void*>(clientInfo));
159     delete view;
160 }
161
162 static void focus(WKPageRef page, const void* clientInfo)
163 {
164     PlatformWebView* view = static_cast<PlatformWebView*>(const_cast<void*>(clientInfo));
165     view->setWindowIsKey(true);
166 }
167
168 static void unfocus(WKPageRef page, const void* clientInfo)
169 {
170     PlatformWebView* view = static_cast<PlatformWebView*>(const_cast<void*>(clientInfo));
171     view->setWindowIsKey(false);
172 }
173
174 WKPageRef TestController::createOtherPage(WKPageRef oldPage, WKURLRequestRef, WKDictionaryRef, WKEventModifiers, WKEventMouseButton, const void*)
175 {
176     PlatformWebView* view = new PlatformWebView(WKPageGetContext(oldPage), WKPageGetPageGroup(oldPage));
177     WKPageRef newPage = view->page();
178
179     view->resizeTo(800, 600);
180
181     WKPageUIClient otherPageUIClient = {
182         kWKPageUIClientCurrentVersion,
183         view,
184         0, // createNewPage_deprecatedForUseWithV0
185         0, // showPage
186         closeOtherPage,
187         0, // takeFocus
188         focus,
189         unfocus,
190         0, // runJavaScriptAlert
191         0, // runJavaScriptConfirm
192         0, // runJavaScriptPrompt
193         0, // setStatusText
194         0, // mouseDidMoveOverElement_deprecatedForUseWithV0
195         0, // missingPluginButtonClicked
196         0, // didNotHandleKeyEvent
197         0, // didNotHandleWheelEvent
198         0, // toolbarsAreVisible
199         0, // setToolbarsAreVisible
200         0, // menuBarIsVisible
201         0, // setMenuBarIsVisible
202         0, // statusBarIsVisible
203         0, // setStatusBarIsVisible
204         0, // isResizable
205         0, // setIsResizable
206         getWindowFrameOtherPage,
207         setWindowFrameOtherPage,
208         runBeforeUnloadConfirmPanel,
209         0, // didDraw
210         0, // pageDidScroll
211         exceededDatabaseQuota,
212         0, // runOpenPanel
213         0, // decidePolicyForGeolocationPermissionRequest
214         0, // headerHeight
215         0, // footerHeight
216         0, // drawHeader
217         0, // drawFooter
218         0, // printFrame
219         runModal,
220         0, // didCompleteRubberBandForMainFrame
221         0, // saveDataToFileInDownloadsFolder
222         0, // shouldInterruptJavaScript
223         createOtherPage,
224         0, // mouseDidMoveOverElement
225         0, // decidePolicyForNotificationPermissionRequest
226         0, // unavailablePluginButtonClicked
227     };
228     WKPageSetPageUIClient(newPage, &otherPageUIClient);
229
230     WKRetain(newPage);
231     return newPage;
232 }
233
234 const char* TestController::libraryPathForTesting()
235 {
236     // FIXME: This may not be sufficient to prevent interactions/crashes
237     // when running more than one copy of DumpRenderTree.
238     // See https://bugs.webkit.org/show_bug.cgi?id=10906
239     char* dumpRenderTreeTemp = getenv("DUMPRENDERTREE_TEMP");
240     if (dumpRenderTreeTemp)
241         return dumpRenderTreeTemp;
242     return platformLibraryPathForTesting();
243 }
244
245
246 void TestController::initialize(int argc, const char* argv[])
247 {
248     platformInitialize();
249
250     if (argc < 2) {
251         fputs("Usage: WebKitTestRunner [options] filename [filename2..n]\n", stderr);
252         // FIXME: Refactor option parsing to allow us to print
253         // an auto-generated list of options.
254         exit(1);
255     }
256
257     bool printSupportedFeatures = false;
258
259     for (int i = 1; i < argc; ++i) {
260         std::string argument(argv[i]);
261
262         if (argument == "--timeout" && i + 1 < argc) {
263             m_longTimeout = atoi(argv[++i]);
264             // Scale up the short timeout to match.
265             m_shortTimeout = defaultShortTimeout * m_longTimeout / defaultLongTimeout;
266             continue;
267         }
268
269         if (argument == "--no-timeout") {
270             m_useWaitToDumpWatchdogTimer = false;
271             continue;
272         }
273
274         if (argument == "--no-timeout-at-all") {
275             m_useWaitToDumpWatchdogTimer = false;
276             m_forceNoTimeout = true;
277             continue;
278         }
279
280         if (argument == "--pixel-tests") {
281             m_dumpPixelsForAllTests = true;
282             continue;
283         }
284         if (argument == "--verbose") {
285             m_verbose = true;
286             continue;
287         }
288         if (argument == "--gc-between-tests") {
289             m_gcBetweenTests = true;
290             continue;
291         }
292         if (argument == "--print-supported-features") {
293             printSupportedFeatures = true;
294             break;
295         }
296
297         // Skip any other arguments that begin with '--'.
298         if (argument.length() >= 2 && argument[0] == '-' && argument[1] == '-')
299             continue;
300
301         m_paths.push_back(argument);
302     }
303
304     if (printSupportedFeatures) {
305         // FIXME: On Windows, DumpRenderTree uses this to expose whether it supports 3d
306         // transforms and accelerated compositing. When we support those features, we
307         // should match DRT's behavior.
308         exit(0);
309     }
310
311     m_usingServerMode = (m_paths.size() == 1 && m_paths[0] == "-");
312     if (m_usingServerMode)
313         m_printSeparators = true;
314     else
315         m_printSeparators = m_paths.size() > 1;
316
317     initializeInjectedBundlePath();
318     initializeTestPluginDirectory();
319
320     WKRetainPtr<WKStringRef> pageGroupIdentifier(AdoptWK, WKStringCreateWithUTF8CString("WebKitTestRunnerPageGroup"));
321     m_pageGroup.adopt(WKPageGroupCreateWithIdentifier(pageGroupIdentifier.get()));
322
323     m_context.adopt(WKContextCreateWithInjectedBundlePath(injectedBundlePath()));
324
325     const char* path = libraryPathForTesting();
326     if (path) {
327         Vector<char> databaseDirectory(strlen(path) + strlen("/Databases") + 1);
328         sprintf(databaseDirectory.data(), "%s%s", path, "/Databases");
329         WKRetainPtr<WKStringRef> databaseDirectoryWK(AdoptWK, WKStringCreateWithUTF8CString(databaseDirectory.data()));
330         WKContextSetDatabaseDirectory(m_context.get(), databaseDirectoryWK.get());
331     }
332
333     platformInitializeContext();
334
335     WKContextInjectedBundleClient injectedBundleClient = {
336         kWKContextInjectedBundleClientCurrentVersion,
337         this,
338         didReceiveMessageFromInjectedBundle,
339         didReceiveSynchronousMessageFromInjectedBundle,
340         0 // getInjectedBundleInitializationUserData
341     };
342     WKContextSetInjectedBundleClient(m_context.get(), &injectedBundleClient);
343
344     if (testPluginDirectory())
345         WKContextSetAdditionalPluginsDirectory(m_context.get(), testPluginDirectory());
346
347     m_mainWebView = adoptPtr(new PlatformWebView(m_context.get(), m_pageGroup.get()));
348
349     WKPageUIClient pageUIClient = {
350         kWKPageUIClientCurrentVersion,
351         this,
352         0, // createNewPage_deprecatedForUseWithV0
353         0, // showPage
354         0, // close
355         0, // takeFocus
356         0, // focus
357         0, // unfocus
358         0, // runJavaScriptAlert
359         0, // runJavaScriptConfirm
360         0, // runJavaScriptPrompt
361         0, // setStatusText
362         0, // mouseDidMoveOverElement_deprecatedForUseWithV0
363         0, // missingPluginButtonClicked
364         0, // didNotHandleKeyEvent
365         0, // didNotHandleWheelEvent
366         0, // toolbarsAreVisible
367         0, // setToolbarsAreVisible
368         0, // menuBarIsVisible
369         0, // setMenuBarIsVisible
370         0, // statusBarIsVisible
371         0, // setStatusBarIsVisible
372         0, // isResizable
373         0, // setIsResizable
374         getWindowFrameMainPage,
375         setWindowFrameMainPage,
376         runBeforeUnloadConfirmPanel,
377         0, // didDraw
378         0, // pageDidScroll
379         exceededDatabaseQuota,
380         0, // runOpenPanel
381         0, // decidePolicyForGeolocationPermissionRequest
382         0, // headerHeight
383         0, // footerHeight
384         0, // drawHeader
385         0, // drawFooter
386         0, // printFrame
387         runModal,
388         0, // didCompleteRubberBandForMainFrame
389         0, // saveDataToFileInDownloadsFolder
390         0, // shouldInterruptJavaScript
391         createOtherPage,
392         0, // mouseDidMoveOverElement
393         0, // decidePolicyForNotificationPermissionRequest
394         0, // unavailablePluginButtonClicked
395     };
396     WKPageSetPageUIClient(m_mainWebView->page(), &pageUIClient);
397
398     WKPageLoaderClient pageLoaderClient = {
399         kWKPageLoaderClientCurrentVersion,
400         this,
401         0, // didStartProvisionalLoadForFrame
402         0, // didReceiveServerRedirectForProvisionalLoadForFrame
403         0, // didFailProvisionalLoadWithErrorForFrame
404         didCommitLoadForFrame,
405         0, // didFinishDocumentLoadForFrame
406         didFinishLoadForFrame,
407         0, // didFailLoadWithErrorForFrame
408         0, // didSameDocumentNavigationForFrame
409         0, // didReceiveTitleForFrame
410         0, // didFirstLayoutForFrame
411         0, // didFirstVisuallyNonEmptyLayoutForFrame
412         0, // didRemoveFrameFromHierarchy
413         0, // didFailToInitializePlugin
414         0, // didDisplayInsecureContentForFrame
415         0, // canAuthenticateAgainstProtectionSpaceInFrame
416         0, // didReceiveAuthenticationChallengeInFrame
417         0, // didStartProgress
418         0, // didChangeProgress
419         0, // didFinishProgress
420         0, // didBecomeUnresponsive
421         0, // didBecomeResponsive
422         processDidCrash,
423         0, // didChangeBackForwardList
424         0, // shouldGoToBackForwardListItem
425         0, // didRunInsecureContentForFrame
426         0, // didDetectXSSForFrame
427         0, // didNewFirstVisuallyNonEmptyLayout
428         0, // willGoToBackForwardListItem
429         0, // interactionOccurredWhileProcessUnresponsive
430         0, // pluginDidFail
431         0, // didReceiveIntentForFrame
432         0, // registerIntentServiceForFrame
433     };
434     WKPageSetPageLoaderClient(m_mainWebView->page(), &pageLoaderClient);
435 }
436
437 bool TestController::resetStateToConsistentValues()
438 {
439     m_state = Resetting;
440
441     m_beforeUnloadReturnValue = true;
442
443     WKRetainPtr<WKStringRef> messageName = adoptWK(WKStringCreateWithUTF8CString("Reset"));
444     WKRetainPtr<WKMutableDictionaryRef> resetMessageBody = adoptWK(WKMutableDictionaryCreate());
445
446     WKRetainPtr<WKStringRef> shouldGCKey = adoptWK(WKStringCreateWithUTF8CString("ShouldGC"));
447     WKRetainPtr<WKBooleanRef> shouldGCValue = adoptWK(WKBooleanCreate(m_gcBetweenTests));
448     WKDictionaryAddItem(resetMessageBody.get(), shouldGCKey.get(), shouldGCValue.get());
449
450     WKContextPostMessageToInjectedBundle(TestController::shared().context(), messageName.get(), resetMessageBody.get());
451
452     WKContextSetShouldUseFontSmoothing(TestController::shared().context(), false);
453
454     WKContextSetCacheModel(TestController::shared().context(), kWKCacheModelDocumentBrowser);
455
456     // FIXME: This function should also ensure that there is only one page open.
457
458     // Reset preferences
459     WKPreferencesRef preferences = WKPageGroupGetPreferences(m_pageGroup.get());
460     WKPreferencesResetTestRunnerOverrides(preferences);
461     WKPreferencesSetOfflineWebApplicationCacheEnabled(preferences, true);
462     WKPreferencesSetFontSmoothingLevel(preferences, kWKFontSmoothingLevelNoSubpixelAntiAliasing);
463     WKPreferencesSetXSSAuditorEnabled(preferences, false);
464     WKPreferencesSetWebAudioEnabled(preferences, true);
465     WKPreferencesSetDeveloperExtrasEnabled(preferences, true);
466     WKPreferencesSetJavaScriptExperimentsEnabled(preferences, true);
467     WKPreferencesSetJavaScriptCanOpenWindowsAutomatically(preferences, true);
468     WKPreferencesSetJavaScriptCanAccessClipboard(preferences, true);
469     WKPreferencesSetDOMPasteAllowed(preferences, true);
470     WKPreferencesSetUniversalAccessFromFileURLsAllowed(preferences, true);
471     WKPreferencesSetFileAccessFromFileURLsAllowed(preferences, true);
472 #if ENABLE(FULLSCREEN_API)
473     WKPreferencesSetFullScreenEnabled(preferences, true);
474 #endif
475     WKPreferencesSetPageCacheEnabled(preferences, false);
476
477 // [Qt][WK2]REGRESSION(r104881):It broke hundreds of tests
478 // FIXME: https://bugs.webkit.org/show_bug.cgi?id=76247
479 #if !PLATFORM(QT)
480     WKPreferencesSetMockScrollbarsEnabled(preferences, true);
481 #endif
482
483 #if !PLATFORM(QT)
484     static WKStringRef standardFontFamily = WKStringCreateWithUTF8CString("Times");
485     static WKStringRef cursiveFontFamily = WKStringCreateWithUTF8CString("Apple Chancery");
486     static WKStringRef fantasyFontFamily = WKStringCreateWithUTF8CString("Papyrus");
487     static WKStringRef fixedFontFamily = WKStringCreateWithUTF8CString("Courier");
488     static WKStringRef pictographFontFamily = WKStringCreateWithUTF8CString("Apple Color Emoji");
489     static WKStringRef sansSerifFontFamily = WKStringCreateWithUTF8CString("Helvetica");
490     static WKStringRef serifFontFamily = WKStringCreateWithUTF8CString("Times");
491
492     WKPreferencesSetStandardFontFamily(preferences, standardFontFamily);
493     WKPreferencesSetCursiveFontFamily(preferences, cursiveFontFamily);
494     WKPreferencesSetFantasyFontFamily(preferences, fantasyFontFamily);
495     WKPreferencesSetFixedFontFamily(preferences, fixedFontFamily);
496     WKPreferencesSetPictographFontFamily(preferences, pictographFontFamily);
497     WKPreferencesSetSansSerifFontFamily(preferences, sansSerifFontFamily);
498     WKPreferencesSetSerifFontFamily(preferences, serifFontFamily);
499 #endif
500     WKPreferencesSetInspectorUsesWebKitUserInterface(preferences, true);
501 #if !PLATFORM(MAC)
502     WKTextCheckerContinuousSpellCheckingEnabledStateChanged(true);
503 #endif
504
505 #if ENABLE(TIZEN_WEBKIT2_ASYNCHRONOUS_SPELLCHECKING)
506     WKPreferencesSetAsynchronousSpellCheckingEnabled(preferences, false);
507 #endif
508
509     // in the case that a test using the chrome input field failed, be sure to clean up for the next test
510     m_mainWebView->removeChromeInputField();
511     m_mainWebView->focus();
512
513     // Re-set to the default backing scale factor by setting the custom scale factor to 0.
514     WKPageSetCustomBackingScaleFactor(m_mainWebView->page(), 0);
515
516     // Reset main page back to about:blank
517     m_doneResetting = false;
518
519     WKPageLoadURL(m_mainWebView->page(), blankURL());
520     runUntil(m_doneResetting, ShortTimeout);
521     return m_doneResetting;
522 }
523
524 bool TestController::runTest(const char* test)
525 {
526     if (!resetStateToConsistentValues()) {
527 #if PLATFORM(MAC)
528         pid_t pid = WKPageGetProcessIdentifier(m_mainWebView->page());
529         fprintf(stderr, "#PROCESS UNRESPONSIVE - WebProcess (pid %ld)\n", static_cast<long>(pid));
530 #else
531         fputs("#PROCESS UNRESPONSIVE - WebProcess\n", stderr);
532 #endif
533         fflush(stderr);
534         return false;
535     }
536
537     bool dumpPixelsTest = m_dumpPixelsForAllTests;
538     std::string command(test);
539     std::string pathOrURL = command;
540     std::string expectedPixelHash;
541     size_t firstSeparatorPos = command.find_first_of('\'');
542     size_t secondSeparatorPos = command.find_first_of('\'', firstSeparatorPos + 1);
543     if (firstSeparatorPos != std::string::npos) {
544         pathOrURL = std::string(command, 0, firstSeparatorPos);
545         size_t pixelHashPos = firstSeparatorPos + 1;
546
547         // NRWT passes --pixel-test if we should dump pixels for the test.
548         const std::string expectedPixelTestArg("--pixel-test");
549         std::string argTest = std::string(command, firstSeparatorPos + 1, expectedPixelTestArg.size());
550         if (argTest == expectedPixelTestArg) {
551             dumpPixelsTest = true;
552             pixelHashPos = secondSeparatorPos == std::string::npos ? std::string::npos : secondSeparatorPos + 1;
553         }
554         if (pixelHashPos != std::string::npos && pixelHashPos < command.size())
555             expectedPixelHash = std::string(command, pixelHashPos);
556     }
557
558     m_state = RunningTest;
559
560     m_currentInvocation = adoptPtr(new TestInvocation(pathOrURL));
561     if (dumpPixelsTest)
562         m_currentInvocation->setIsPixelTest(expectedPixelHash);
563
564     m_currentInvocation->invoke();
565     m_currentInvocation.clear();
566
567     return true;
568 }
569
570 void TestController::runTestingServerLoop()
571 {
572     char filenameBuffer[2048];
573     while (fgets(filenameBuffer, sizeof(filenameBuffer), stdin)) {
574         char* newLineCharacter = strchr(filenameBuffer, '\n');
575         if (newLineCharacter)
576             *newLineCharacter = '\0';
577
578         if (strlen(filenameBuffer) == 0)
579             continue;
580
581         if (!runTest(filenameBuffer))
582             break;
583     }
584 }
585
586 void TestController::run()
587 {
588     if (m_usingServerMode)
589         runTestingServerLoop();
590     else {
591         for (size_t i = 0; i < m_paths.size(); ++i) {
592             if (!runTest(m_paths[i].c_str()))
593                 break;
594         }
595     }
596 }
597
598 void TestController::runUntil(bool& done, TimeoutDuration timeoutDuration)
599 {
600     double timeout = m_noTimeout;
601     if (!m_forceNoTimeout) {
602         switch (timeoutDuration) {
603         case ShortTimeout:
604             timeout = m_shortTimeout;
605             break;
606         case LongTimeout:
607             timeout = m_longTimeout;
608             break;
609         case NoTimeout:
610         default:
611             timeout = m_noTimeout;
612             break;
613         }
614     }
615
616     platformRunUntil(done, timeout);
617 }
618
619 // WKContextInjectedBundleClient
620
621 void TestController::didReceiveMessageFromInjectedBundle(WKContextRef context, WKStringRef messageName, WKTypeRef messageBody, const void* clientInfo)
622 {
623     static_cast<TestController*>(const_cast<void*>(clientInfo))->didReceiveMessageFromInjectedBundle(messageName, messageBody);
624 }
625
626 void TestController::didReceiveSynchronousMessageFromInjectedBundle(WKContextRef context, WKStringRef messageName, WKTypeRef messageBody, WKTypeRef* returnData, const void* clientInfo)
627 {
628     *returnData = static_cast<TestController*>(const_cast<void*>(clientInfo))->didReceiveSynchronousMessageFromInjectedBundle(messageName, messageBody).leakRef();
629 }
630
631 void TestController::didReceiveMessageFromInjectedBundle(WKStringRef messageName, WKTypeRef messageBody)
632 {
633     if (!m_currentInvocation)
634         return;
635     m_currentInvocation->didReceiveMessageFromInjectedBundle(messageName, messageBody);
636 }
637
638 WKRetainPtr<WKTypeRef> TestController::didReceiveSynchronousMessageFromInjectedBundle(WKStringRef messageName, WKTypeRef messageBody)
639 {
640 #if PLATFORM(MAC) || PLATFORM(QT) || PLATFORM(GTK) || PLATFORM(EFL)
641     if (WKStringIsEqualToUTF8CString(messageName, "EventSender")) {
642         ASSERT(WKGetTypeID(messageBody) == WKDictionaryGetTypeID());
643         WKDictionaryRef messageBodyDictionary = static_cast<WKDictionaryRef>(messageBody);
644
645         WKRetainPtr<WKStringRef> subMessageKey(AdoptWK, WKStringCreateWithUTF8CString("SubMessage"));
646         WKStringRef subMessageName = static_cast<WKStringRef>(WKDictionaryGetItemForKey(messageBodyDictionary, subMessageKey.get()));
647
648         if (WKStringIsEqualToUTF8CString(subMessageName, "KeyDown")) {
649             WKRetainPtr<WKStringRef> keyKey = adoptWK(WKStringCreateWithUTF8CString("Key"));
650             WKStringRef key = static_cast<WKStringRef>(WKDictionaryGetItemForKey(messageBodyDictionary, keyKey.get()));
651
652             WKRetainPtr<WKStringRef> modifiersKey = adoptWK(WKStringCreateWithUTF8CString("Modifiers"));
653             WKEventModifiers modifiers = static_cast<WKEventModifiers>(WKUInt64GetValue(static_cast<WKUInt64Ref>(WKDictionaryGetItemForKey(messageBodyDictionary, modifiersKey.get()))));
654
655             WKRetainPtr<WKStringRef> locationKey = adoptWK(WKStringCreateWithUTF8CString("Location"));
656             unsigned location = static_cast<unsigned>(WKUInt64GetValue(static_cast<WKUInt64Ref>(WKDictionaryGetItemForKey(messageBodyDictionary, locationKey.get()))));
657
658             // Forward to WebProcess
659             WKPageSetShouldSendEventsSynchronously(mainWebView()->page(), true);
660             m_eventSenderProxy->keyDown(key, modifiers, location);
661             WKPageSetShouldSendEventsSynchronously(mainWebView()->page(), false);
662             return 0;
663         }
664
665         if (WKStringIsEqualToUTF8CString(subMessageName, "MouseDown") || WKStringIsEqualToUTF8CString(subMessageName, "MouseUp")) {
666             WKRetainPtr<WKStringRef> buttonKey = adoptWK(WKStringCreateWithUTF8CString("Button"));
667             unsigned button = static_cast<unsigned>(WKUInt64GetValue(static_cast<WKUInt64Ref>(WKDictionaryGetItemForKey(messageBodyDictionary, buttonKey.get()))));
668
669             WKRetainPtr<WKStringRef> modifiersKey = adoptWK(WKStringCreateWithUTF8CString("Modifiers"));
670             WKEventModifiers modifiers = static_cast<WKEventModifiers>(WKUInt64GetValue(static_cast<WKUInt64Ref>(WKDictionaryGetItemForKey(messageBodyDictionary, modifiersKey.get()))));
671
672             // Forward to WebProcess
673             WKPageSetShouldSendEventsSynchronously(mainWebView()->page(), true);
674             if (WKStringIsEqualToUTF8CString(subMessageName, "MouseDown"))
675                 m_eventSenderProxy->mouseDown(button, modifiers);
676             else
677                 m_eventSenderProxy->mouseUp(button, modifiers);
678             WKPageSetShouldSendEventsSynchronously(mainWebView()->page(), false);
679             return 0;
680         }
681
682         if (WKStringIsEqualToUTF8CString(subMessageName, "MouseMoveTo")) {
683             WKRetainPtr<WKStringRef> xKey = adoptWK(WKStringCreateWithUTF8CString("X"));
684             double x = WKDoubleGetValue(static_cast<WKDoubleRef>(WKDictionaryGetItemForKey(messageBodyDictionary, xKey.get())));
685
686             WKRetainPtr<WKStringRef> yKey = adoptWK(WKStringCreateWithUTF8CString("Y"));
687             double y = WKDoubleGetValue(static_cast<WKDoubleRef>(WKDictionaryGetItemForKey(messageBodyDictionary, yKey.get())));
688
689             // Forward to WebProcess
690             WKPageSetShouldSendEventsSynchronously(mainWebView()->page(), true);
691             m_eventSenderProxy->mouseMoveTo(x, y);
692             WKPageSetShouldSendEventsSynchronously(mainWebView()->page(), false);
693             return 0;
694         }
695
696         if (WKStringIsEqualToUTF8CString(subMessageName, "MouseScrollBy")) {
697             WKRetainPtr<WKStringRef> xKey = adoptWK(WKStringCreateWithUTF8CString("X"));
698             double x = WKDoubleGetValue(static_cast<WKDoubleRef>(WKDictionaryGetItemForKey(messageBodyDictionary, xKey.get())));
699
700             WKRetainPtr<WKStringRef> yKey = adoptWK(WKStringCreateWithUTF8CString("Y"));
701             double y = WKDoubleGetValue(static_cast<WKDoubleRef>(WKDictionaryGetItemForKey(messageBodyDictionary, yKey.get())));
702
703             // Forward to WebProcess
704             WKPageSetShouldSendEventsSynchronously(mainWebView()->page(), true);
705             m_eventSenderProxy->mouseScrollBy(x, y);
706             WKPageSetShouldSendEventsSynchronously(mainWebView()->page(), false);
707             return 0;
708         }
709
710         if (WKStringIsEqualToUTF8CString(subMessageName, "LeapForward")) {
711             WKRetainPtr<WKStringRef> timeKey = adoptWK(WKStringCreateWithUTF8CString("TimeInMilliseconds"));
712             unsigned time = static_cast<unsigned>(WKUInt64GetValue(static_cast<WKUInt64Ref>(WKDictionaryGetItemForKey(messageBodyDictionary, timeKey.get()))));
713
714             m_eventSenderProxy->leapForward(time);
715             return 0;
716         }
717
718 #if ENABLE(TOUCH_EVENTS)
719         if (WKStringIsEqualToUTF8CString(subMessageName, "AddTouchPoint")) {
720             WKRetainPtr<WKStringRef> xKey = adoptWK(WKStringCreateWithUTF8CString("X"));
721             int x = static_cast<int>(WKUInt64GetValue(static_cast<WKUInt64Ref>(WKDictionaryGetItemForKey(messageBodyDictionary, xKey.get()))));
722
723             WKRetainPtr<WKStringRef> yKey = adoptWK(WKStringCreateWithUTF8CString("Y"));
724             int y = static_cast<int>(WKUInt64GetValue(static_cast<WKUInt64Ref>(WKDictionaryGetItemForKey(messageBodyDictionary, yKey.get()))));
725
726             m_eventSenderProxy->addTouchPoint(x, y);
727             return 0;
728         }
729
730         if (WKStringIsEqualToUTF8CString(subMessageName, "UpdateTouchPoint")) {
731             WKRetainPtr<WKStringRef> indexKey = adoptWK(WKStringCreateWithUTF8CString("Index"));
732             int index = static_cast<int>(WKUInt64GetValue(static_cast<WKUInt64Ref>(WKDictionaryGetItemForKey(messageBodyDictionary, indexKey.get()))));
733
734             WKRetainPtr<WKStringRef> xKey = adoptWK(WKStringCreateWithUTF8CString("X"));
735             int x = static_cast<int>(WKUInt64GetValue(static_cast<WKUInt64Ref>(WKDictionaryGetItemForKey(messageBodyDictionary, xKey.get()))));
736
737             WKRetainPtr<WKStringRef> yKey = adoptWK(WKStringCreateWithUTF8CString("Y"));
738             int y = static_cast<int>(WKUInt64GetValue(static_cast<WKUInt64Ref>(WKDictionaryGetItemForKey(messageBodyDictionary, yKey.get()))));
739
740             m_eventSenderProxy->updateTouchPoint(index, x, y);
741             return 0;
742         }
743
744         if (WKStringIsEqualToUTF8CString(subMessageName, "SetTouchModifier")) {
745             WKRetainPtr<WKStringRef> modifierKey = adoptWK(WKStringCreateWithUTF8CString("Modifier"));
746             WKEventModifiers modifier = static_cast<WKEventModifiers>(WKUInt64GetValue(static_cast<WKUInt64Ref>(WKDictionaryGetItemForKey(messageBodyDictionary, modifierKey.get()))));
747
748             WKRetainPtr<WKStringRef> enableKey = adoptWK(WKStringCreateWithUTF8CString("Enable"));
749             bool enable = static_cast<bool>(WKUInt64GetValue(static_cast<WKUInt64Ref>(WKDictionaryGetItemForKey(messageBodyDictionary, enableKey.get()))));
750
751             m_eventSenderProxy->setTouchModifier(modifier, enable);
752             return 0;
753         }
754
755         if (WKStringIsEqualToUTF8CString(subMessageName, "SetTouchPointRadius")) {
756             WKRetainPtr<WKStringRef> xKey = adoptWK(WKStringCreateWithUTF8CString("RadiusX"));
757             int x = static_cast<int>(WKUInt64GetValue(static_cast<WKUInt64Ref>(WKDictionaryGetItemForKey(messageBodyDictionary, xKey.get()))));
758
759             WKRetainPtr<WKStringRef> yKey = adoptWK(WKStringCreateWithUTF8CString("RadiusY"));
760             int y = static_cast<int>(WKUInt64GetValue(static_cast<WKUInt64Ref>(WKDictionaryGetItemForKey(messageBodyDictionary, yKey.get()))));
761
762             m_eventSenderProxy->setTouchPointRadius(x, y);
763             return 0;
764         }
765
766         if (WKStringIsEqualToUTF8CString(subMessageName, "TouchStart")) {
767             WKPageSetShouldSendEventsSynchronously(mainWebView()->page(), true);
768             m_eventSenderProxy->touchStart();
769             WKPageSetShouldSendEventsSynchronously(mainWebView()->page(), false);
770             return 0;
771         }
772
773         if (WKStringIsEqualToUTF8CString(subMessageName, "TouchMove")) {
774             WKPageSetShouldSendEventsSynchronously(mainWebView()->page(), true);
775             m_eventSenderProxy->touchMove();
776             WKPageSetShouldSendEventsSynchronously(mainWebView()->page(), false);
777             return 0;
778         }
779
780         if (WKStringIsEqualToUTF8CString(subMessageName, "TouchEnd")) {
781             WKPageSetShouldSendEventsSynchronously(mainWebView()->page(), true);
782             m_eventSenderProxy->touchEnd();
783             WKPageSetShouldSendEventsSynchronously(mainWebView()->page(), false);
784             return 0;
785         }
786
787         if (WKStringIsEqualToUTF8CString(subMessageName, "TouchCancel")) {
788             WKPageSetShouldSendEventsSynchronously(mainWebView()->page(), true);
789             m_eventSenderProxy->touchCancel();
790             WKPageSetShouldSendEventsSynchronously(mainWebView()->page(), false);
791             return 0;
792         }
793
794         if (WKStringIsEqualToUTF8CString(subMessageName, "ClearTouchPoints")) {
795             m_eventSenderProxy->clearTouchPoints();
796             return 0;
797         }
798
799         if (WKStringIsEqualToUTF8CString(subMessageName, "ReleaseTouchPoint")) {
800             WKRetainPtr<WKStringRef> indexKey = adoptWK(WKStringCreateWithUTF8CString("Index"));
801             int index = static_cast<int>(WKUInt64GetValue(static_cast<WKUInt64Ref>(WKDictionaryGetItemForKey(messageBodyDictionary, indexKey.get()))));
802             m_eventSenderProxy->releaseTouchPoint(index);
803             return 0;
804         }
805
806         if (WKStringIsEqualToUTF8CString(subMessageName, "CancelTouchPoint")) {
807             WKRetainPtr<WKStringRef> indexKey = adoptWK(WKStringCreateWithUTF8CString("Index"));
808             int index = static_cast<int>(WKUInt64GetValue(static_cast<WKUInt64Ref>(WKDictionaryGetItemForKey(messageBodyDictionary, indexKey.get()))));
809             m_eventSenderProxy->cancelTouchPoint(index);
810             return 0;
811         }
812 #endif
813         ASSERT_NOT_REACHED();
814     }
815 #endif
816     return m_currentInvocation->didReceiveSynchronousMessageFromInjectedBundle(messageName, messageBody);
817 }
818
819 // WKPageLoaderClient
820
821 void TestController::didCommitLoadForFrame(WKPageRef page, WKFrameRef frame, WKTypeRef, const void* clientInfo)
822 {
823     static_cast<TestController*>(const_cast<void*>(clientInfo))->didCommitLoadForFrame(page, frame);
824 }
825
826 void TestController::didFinishLoadForFrame(WKPageRef page, WKFrameRef frame, WKTypeRef, const void* clientInfo)
827 {
828     static_cast<TestController*>(const_cast<void*>(clientInfo))->didFinishLoadForFrame(page, frame);
829 }
830
831 void TestController::processDidCrash(WKPageRef page, const void* clientInfo)
832 {
833     static_cast<TestController*>(const_cast<void*>(clientInfo))->processDidCrash();
834 }
835
836 void TestController::didCommitLoadForFrame(WKPageRef page, WKFrameRef frame)
837 {
838     if (!WKFrameIsMainFrame(frame))
839         return;
840
841     mainWebView()->focus();
842 }
843
844 void TestController::didFinishLoadForFrame(WKPageRef page, WKFrameRef frame)
845 {
846     if (m_state != Resetting)
847         return;
848
849     if (!WKFrameIsMainFrame(frame))
850         return;
851
852     WKRetainPtr<WKURLRef> wkURL(AdoptWK, WKFrameCopyURL(frame));
853     if (!WKURLIsEqual(wkURL.get(), blankURL()))
854         return;
855
856     m_doneResetting = true;
857     shared().notifyDone();
858 }
859
860 void TestController::processDidCrash()
861 {
862     // This function can be called multiple times when crash logs are being saved on Windows, so
863     // ensure we only print the crashed message once.
864     if (!m_didPrintWebProcessCrashedMessage) {
865 #if PLATFORM(MAC)
866         pid_t pid = WKPageGetProcessIdentifier(m_mainWebView->page());
867         fprintf(stderr, "#CRASHED - WebProcess (pid %ld)\n", static_cast<long>(pid));
868 #else
869         fputs("#CRASHED - WebProcess\n", stderr);
870 #endif
871         fflush(stderr);
872         m_didPrintWebProcessCrashedMessage = true;
873     }
874
875     if (m_shouldExitWhenWebProcessCrashes)
876         exit(1);
877 }
878
879 } // namespace WTR