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