initial import
[vuplus_webkit] / 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/WKPreferencesPrivate.h>
36 #include <WebKit2/WKRetainPtr.h>
37 #include <cstdio>
38 #include <wtf/PassOwnPtr.h>
39
40 #if PLATFORM(MAC)
41 #include "EventSenderProxy.h"
42 #endif
43
44 namespace WTR {
45
46 static const double defaultLongTimeout = 30;
47 static const double defaultShortTimeout = 5;
48
49 static WKURLRef blankURL()
50 {
51     static WKURLRef staticBlankURL = WKURLCreateWithUTF8CString("about:blank");
52     return staticBlankURL;
53 }
54
55 static TestController* controller;
56
57 TestController& TestController::shared()
58 {
59     ASSERT(controller);
60     return *controller;
61 }
62
63 TestController::TestController(int argc, const char* argv[])
64     : m_dumpPixels(false)
65     , m_verbose(false)
66     , m_printSeparators(false)
67     , m_usingServerMode(false)
68     , m_gcBetweenTests(false)
69     , m_state(Initial)
70     , m_doneResetting(false)
71     , m_longTimeout(defaultLongTimeout)
72     , m_shortTimeout(defaultShortTimeout)
73     , m_didPrintWebProcessCrashedMessage(false)
74     , m_shouldExitWhenWebProcessCrashes(true)
75     , m_beforeUnloadReturnValue(true)
76 #if PLATFORM(MAC)
77     , m_eventSenderProxy(new EventSenderProxy(this))
78 #endif
79 {
80     initialize(argc, argv);
81     controller = this;
82     run();
83     controller = 0;
84 }
85
86 TestController::~TestController()
87 {
88 }
89
90 static WKRect getWindowFrameMainPage(WKPageRef page, const void* clientInfo)
91 {
92     PlatformWebView* view = static_cast<TestController*>(const_cast<void*>(clientInfo))->mainWebView();
93     return view->windowFrame();
94 }
95
96 static void setWindowFrameMainPage(WKPageRef page, WKRect frame, const void* clientInfo)
97 {
98     PlatformWebView* view = static_cast<TestController*>(const_cast<void*>(clientInfo))->mainWebView();
99     view->setWindowFrame(frame);
100 }
101
102 static WKRect getWindowFrameOtherPage(WKPageRef page, const void* clientInfo)
103 {
104     PlatformWebView* view = static_cast<PlatformWebView*>(const_cast<void*>(clientInfo));
105     return view->windowFrame();
106 }
107
108 static void setWindowFrameOtherPage(WKPageRef page, WKRect frame, const void* clientInfo)
109 {
110     PlatformWebView* view = static_cast<PlatformWebView*>(const_cast<void*>(clientInfo));
111     view->setWindowFrame(frame);
112 }
113
114 static bool runBeforeUnloadConfirmPanel(WKPageRef page, WKStringRef message, WKFrameRef frame, const void *clientInfo)
115 {
116     TestController* testController = static_cast<TestController*>(const_cast<void*>(clientInfo));
117     printf("CONFIRM NAVIGATION: %s\n", toSTD(message).c_str());
118     return testController->beforeUnloadReturnValue();
119 }
120
121 static unsigned long long exceededDatabaseQuota(WKPageRef, WKFrameRef, WKSecurityOriginRef, WKStringRef, WKStringRef, unsigned long long, unsigned long long, unsigned long long, unsigned long long, const void*)
122 {
123     static const unsigned long long defaultQuota = 5 * 1024 * 1024;    
124     return defaultQuota;
125 }
126
127
128 void TestController::runModal(WKPageRef page, const void* clientInfo)
129 {
130     runModal(static_cast<PlatformWebView*>(const_cast<void*>(clientInfo)));
131 }
132
133 static void closeOtherPage(WKPageRef page, const void* clientInfo)
134 {
135     WKPageClose(page);
136     const PlatformWebView* view = static_cast<const PlatformWebView*>(clientInfo);
137     delete view;
138 }
139
140 WKPageRef TestController::createOtherPage(WKPageRef oldPage, WKURLRequestRef, WKDictionaryRef, WKEventModifiers, WKEventMouseButton, const void*)
141 {
142     PlatformWebView* view = new PlatformWebView(WKPageGetContext(oldPage), WKPageGetPageGroup(oldPage));
143     WKPageRef newPage = view->page();
144
145     view->resizeTo(800, 600);
146
147     WKPageUIClient otherPageUIClient = {
148         kWKPageUIClientCurrentVersion,
149         view,
150         0, // createNewPage_deprecatedForUseWithV0
151         0, // showPage
152         closeOtherPage,
153         0, // takeFocus
154         0, // focus
155         0, // unfocus
156         0, // runJavaScriptAlert        
157         0, // runJavaScriptConfirm
158         0, // runJavaScriptPrompt
159         0, // setStatusText
160         0, // mouseDidMoveOverElement
161         0, // missingPluginButtonClicked
162         0, // didNotHandleKeyEvent
163         0, // didNotHandleWheelEvent
164         0, // toolbarsAreVisible
165         0, // setToolbarsAreVisible
166         0, // menuBarIsVisible
167         0, // setMenuBarIsVisible
168         0, // statusBarIsVisible
169         0, // setStatusBarIsVisible
170         0, // isResizable
171         0, // setIsResizable
172         getWindowFrameOtherPage,
173         setWindowFrameOtherPage,
174         runBeforeUnloadConfirmPanel,
175         0, // didDraw
176         0, // pageDidScroll
177         exceededDatabaseQuota,
178         0, // runOpenPanel
179         0, // decidePolicyForGeolocationPermissionRequest
180         0, // headerHeight
181         0, // footerHeight
182         0, // drawHeader
183         0, // drawFooter
184         0, // printFrame
185         runModal,
186         0, // didCompleteRubberBandForMainFrame
187         0, // saveDataToFileInDownloadsFolder
188         0, // shouldInterruptJavaScript
189         createOtherPage,
190     };
191     WKPageSetPageUIClient(newPage, &otherPageUIClient);
192
193     WKRetain(newPage);
194     return newPage;
195 }
196
197 const char* TestController::libraryPathForTesting()
198 {
199     // FIXME: This may not be sufficient to prevent interactions/crashes
200     // when running more than one copy of DumpRenderTree.
201     // See https://bugs.webkit.org/show_bug.cgi?id=10906
202     char* dumpRenderTreeTemp = getenv("DUMPRENDERTREE_TEMP");
203     if (dumpRenderTreeTemp)
204         return dumpRenderTreeTemp;
205     return platformLibraryPathForTesting();
206 }
207
208
209 void TestController::initialize(int argc, const char* argv[])
210 {
211     platformInitialize();
212
213     bool printSupportedFeatures = false;
214
215     for (int i = 1; i < argc; ++i) {
216         std::string argument(argv[i]);
217
218         if (argument == "--timeout" && i + 1 < argc) {
219             m_longTimeout = atoi(argv[++i]);
220             // Scale up the short timeout to match.
221             m_shortTimeout = defaultShortTimeout * m_longTimeout / defaultLongTimeout;
222             continue;
223         }
224         if (argument == "--pixel-tests") {
225             m_dumpPixels = true;
226             continue;
227         }
228         if (argument == "--verbose") {
229             m_verbose = true;
230             continue;
231         }
232         if (argument == "--gc-between-tests") {
233             m_gcBetweenTests = true;
234             continue;
235         }
236         if (argument == "--print-supported-features") {
237             printSupportedFeatures = true;
238             break;
239         }
240
241         // Skip any other arguments that begin with '--'.
242         if (argument.length() >= 2 && argument[0] == '-' && argument[1] == '-')
243             continue;
244
245         m_paths.push_back(argument);
246     }
247
248     if (printSupportedFeatures) {
249         // FIXME: On Windows, DumpRenderTree uses this to expose whether it supports 3d
250         // transforms and accelerated compositing. When we support those features, we
251         // should match DRT's behavior.
252         exit(0);
253     }
254
255     m_usingServerMode = (m_paths.size() == 1 && m_paths[0] == "-");
256     if (m_usingServerMode)
257         m_printSeparators = true;
258     else
259         m_printSeparators = m_paths.size() > 1;
260
261     initializeInjectedBundlePath();
262     initializeTestPluginDirectory();
263
264     WKRetainPtr<WKStringRef> pageGroupIdentifier(AdoptWK, WKStringCreateWithUTF8CString("WebKitTestRunnerPageGroup"));
265     m_pageGroup.adopt(WKPageGroupCreateWithIdentifier(pageGroupIdentifier.get()));
266
267     m_context.adopt(WKContextCreateWithInjectedBundlePath(injectedBundlePath()));
268
269     const char* path = libraryPathForTesting();
270     if (path) {
271         Vector<char> databaseDirectory(strlen(path) + strlen("/Databases") + 1);
272         sprintf(databaseDirectory.data(), "%s%s", path, "/Databases");
273         WKRetainPtr<WKStringRef> databaseDirectoryWK(AdoptWK, WKStringCreateWithUTF8CString(databaseDirectory.data()));
274         WKContextSetDatabaseDirectory(m_context.get(), databaseDirectoryWK.get());
275     }
276
277     platformInitializeContext();
278
279     WKContextInjectedBundleClient injectedBundleClient = {
280         kWKContextInjectedBundleClientCurrentVersion,
281         this,
282         didReceiveMessageFromInjectedBundle,
283         didReceiveSynchronousMessageFromInjectedBundle
284     };
285     WKContextSetInjectedBundleClient(m_context.get(), &injectedBundleClient);
286
287     _WKContextSetAdditionalPluginsDirectory(m_context.get(), testPluginDirectory());
288
289     m_mainWebView = adoptPtr(new PlatformWebView(m_context.get(), m_pageGroup.get()));
290
291     WKPageUIClient pageUIClient = {
292         kWKPageUIClientCurrentVersion,
293         this,
294         0, // createNewPage_deprecatedForUseWithV0
295         0, // showPage
296         0, // close
297         0, // takeFocus
298         0, // focus
299         0, // unfocus
300         0, // runJavaScriptAlert        
301         0, // runJavaScriptConfirm
302         0, // runJavaScriptPrompt
303         0, // setStatusText
304         0, // mouseDidMoveOverElement
305         0, // missingPluginButtonClicked
306         0, // didNotHandleKeyEvent
307         0, // didNotHandleWheelEvent
308         0, // toolbarsAreVisible
309         0, // setToolbarsAreVisible
310         0, // menuBarIsVisible
311         0, // setMenuBarIsVisible
312         0, // statusBarIsVisible
313         0, // setStatusBarIsVisible
314         0, // isResizable
315         0, // setIsResizable
316         getWindowFrameMainPage,
317         setWindowFrameMainPage,
318         runBeforeUnloadConfirmPanel,
319         0, // didDraw
320         0, // pageDidScroll
321         exceededDatabaseQuota,
322         0, // runOpenPanel
323         0, // decidePolicyForGeolocationPermissionRequest
324         0, // headerHeight
325         0, // footerHeight
326         0, // drawHeader
327         0, // drawFooter
328         0, // printFrame
329         runModal,
330         0, // didCompleteRubberBandForMainFrame
331         0, // saveDataToFileInDownloadsFolder
332         0, // shouldInterruptJavaScript
333         createOtherPage,
334     };
335     WKPageSetPageUIClient(m_mainWebView->page(), &pageUIClient);
336
337     WKPageLoaderClient pageLoaderClient = {
338         kWKPageLoaderClientCurrentVersion,
339         this,
340         0, // didStartProvisionalLoadForFrame
341         0, // didReceiveServerRedirectForProvisionalLoadForFrame
342         0, // didFailProvisionalLoadWithErrorForFrame
343         0, // didCommitLoadForFrame
344         0, // didFinishDocumentLoadForFrame
345         didFinishLoadForFrame,
346         0, // didFailLoadWithErrorForFrame
347         0, // didSameDocumentNavigationForFrame
348         0, // didReceiveTitleForFrame
349         0, // didFirstLayoutForFrame
350         0, // didFirstVisuallyNonEmptyLayoutForFrame
351         0, // didRemoveFrameFromHierarchy
352         0, // didDisplayInsecureContentForFrame
353         0, // didRunInsecureContentForFrame
354         0, // canAuthenticateAgainstProtectionSpaceInFrame
355         0, // didReceiveAuthenticationChallengeInFrame
356         0, // didStartProgress
357         0, // didChangeProgress
358         0, // didFinishProgress
359         0, // didBecomeUnresponsive
360         0, // didBecomeResponsive
361         processDidCrash,
362         0, // didChangeBackForwardList
363         0, // shouldGoToBackForwardListItem
364         0  // didFailToInitializePlugin
365     };
366     WKPageSetPageLoaderClient(m_mainWebView->page(), &pageLoaderClient);
367 }
368
369 bool TestController::resetStateToConsistentValues()
370 {
371     m_state = Resetting;
372     
373     m_beforeUnloadReturnValue = true;
374
375     WKRetainPtr<WKStringRef> messageName = adoptWK(WKStringCreateWithUTF8CString("Reset"));
376     WKRetainPtr<WKMutableDictionaryRef> resetMessageBody = adoptWK(WKMutableDictionaryCreate());
377
378     WKRetainPtr<WKStringRef> shouldGCKey = adoptWK(WKStringCreateWithUTF8CString("ShouldGC"));
379     WKRetainPtr<WKBooleanRef> shouldGCValue = adoptWK(WKBooleanCreate(m_gcBetweenTests));
380     WKDictionaryAddItem(resetMessageBody.get(), shouldGCKey.get(), shouldGCValue.get());
381
382     WKContextPostMessageToInjectedBundle(TestController::shared().context(), messageName.get(), resetMessageBody.get());
383
384     // FIXME: This function should also ensure that there is only one page open.
385
386     // Reset preferences
387     WKPreferencesRef preferences = WKPageGroupGetPreferences(m_pageGroup.get());
388     WKPreferencesSetOfflineWebApplicationCacheEnabled(preferences, true);
389     WKPreferencesSetFontSmoothingLevel(preferences, kWKFontSmoothingLevelNoSubpixelAntiAliasing);
390     WKPreferencesSetXSSAuditorEnabled(preferences, false);
391     WKPreferencesSetDeveloperExtrasEnabled(preferences, true);
392     WKPreferencesSetJavaScriptCanOpenWindowsAutomatically(preferences, true);
393     WKPreferencesSetJavaScriptCanAccessClipboard(preferences, true);
394     WKPreferencesSetDOMPasteAllowed(preferences, true);
395     WKPreferencesSetUniversalAccessFromFileURLsAllowed(preferences, true);
396     WKPreferencesSetFileAccessFromFileURLsAllowed(preferences, true);
397 #if ENABLE(FULLSCREEN_API)
398     WKPreferencesSetFullScreenEnabled(preferences, true);
399 #endif
400
401 #if !PLATFORM(QT)
402     static WKStringRef standardFontFamily = WKStringCreateWithUTF8CString("Times");
403     static WKStringRef cursiveFontFamily = WKStringCreateWithUTF8CString("Apple Chancery");
404     static WKStringRef fantasyFontFamily = WKStringCreateWithUTF8CString("Papyrus");
405     static WKStringRef fixedFontFamily = WKStringCreateWithUTF8CString("Courier");
406     static WKStringRef pictographFontFamily = WKStringCreateWithUTF8CString("Apple Color Emoji");
407     static WKStringRef sansSerifFontFamily = WKStringCreateWithUTF8CString("Helvetica");
408     static WKStringRef serifFontFamily = WKStringCreateWithUTF8CString("Times");
409
410     WKPreferencesSetStandardFontFamily(preferences, standardFontFamily);
411     WKPreferencesSetCursiveFontFamily(preferences, cursiveFontFamily);
412     WKPreferencesSetFantasyFontFamily(preferences, fantasyFontFamily);
413     WKPreferencesSetFixedFontFamily(preferences, fixedFontFamily);
414     WKPreferencesSetPictographFontFamily(preferences, pictographFontFamily);
415     WKPreferencesSetSansSerifFontFamily(preferences, sansSerifFontFamily);
416     WKPreferencesSetSerifFontFamily(preferences, serifFontFamily);
417 #endif
418
419     m_mainWebView->focus();
420
421     // Reset main page back to about:blank
422     m_doneResetting = false;
423
424     WKPageLoadURL(m_mainWebView->page(), blankURL());
425     runUntil(m_doneResetting, ShortTimeout);
426     return m_doneResetting;
427 }
428
429 bool TestController::runTest(const char* test)
430 {
431     if (!resetStateToConsistentValues()) {
432         fputs("#CRASHED - WebProcess\n", stderr);
433         fflush(stderr);
434         return false;
435     }
436
437     std::string pathOrURL(test);
438     std::string expectedPixelHash;
439     size_t separatorPos = pathOrURL.find("'");
440     if (separatorPos != std::string::npos) {
441         pathOrURL = std::string(std::string(test), 0, separatorPos);
442         expectedPixelHash = std::string(std::string(test), separatorPos + 1);
443     }
444
445     m_state = RunningTest;
446
447     m_currentInvocation = adoptPtr(new TestInvocation(pathOrURL));
448     if (m_dumpPixels)
449         m_currentInvocation->setIsPixelTest(expectedPixelHash);    
450
451     m_currentInvocation->invoke();
452     m_currentInvocation.clear();
453
454     return true;
455 }
456
457 void TestController::runTestingServerLoop()
458 {
459     char filenameBuffer[2048];
460     while (fgets(filenameBuffer, sizeof(filenameBuffer), stdin)) {
461         char* newLineCharacter = strchr(filenameBuffer, '\n');
462         if (newLineCharacter)
463             *newLineCharacter = '\0';
464
465         if (strlen(filenameBuffer) == 0)
466             continue;
467
468         if (!runTest(filenameBuffer))
469             break;
470     }
471 }
472
473 void TestController::run()
474 {
475     if (m_usingServerMode)
476         runTestingServerLoop();
477     else {
478         for (size_t i = 0; i < m_paths.size(); ++i) {
479             if (!runTest(m_paths[i].c_str()))
480                 break;
481         }
482     }
483 }
484
485 void TestController::runUntil(bool& done, TimeoutDuration timeoutDuration)
486 {
487     platformRunUntil(done, timeoutDuration == ShortTimeout ? m_shortTimeout : m_longTimeout);
488 }
489
490 // WKContextInjectedBundleClient
491
492 void TestController::didReceiveMessageFromInjectedBundle(WKContextRef context, WKStringRef messageName, WKTypeRef messageBody, const void* clientInfo)
493 {
494     static_cast<TestController*>(const_cast<void*>(clientInfo))->didReceiveMessageFromInjectedBundle(messageName, messageBody);
495 }
496
497 void TestController::didReceiveSynchronousMessageFromInjectedBundle(WKContextRef context, WKStringRef messageName, WKTypeRef messageBody, WKTypeRef* returnData, const void* clientInfo)
498 {
499     *returnData = static_cast<TestController*>(const_cast<void*>(clientInfo))->didReceiveSynchronousMessageFromInjectedBundle(messageName, messageBody).leakRef();
500 }
501
502 void TestController::didReceiveMessageFromInjectedBundle(WKStringRef messageName, WKTypeRef messageBody)
503 {
504     if (!m_currentInvocation)
505         return;
506     m_currentInvocation->didReceiveMessageFromInjectedBundle(messageName, messageBody);
507 }
508
509 WKRetainPtr<WKTypeRef> TestController::didReceiveSynchronousMessageFromInjectedBundle(WKStringRef messageName, WKTypeRef messageBody)
510 {
511 #if PLATFORM(MAC)
512     if (WKStringIsEqualToUTF8CString(messageName, "EventSender")) {
513         ASSERT(WKGetTypeID(messageBody) == WKDictionaryGetTypeID());
514         WKDictionaryRef messageBodyDictionary = static_cast<WKDictionaryRef>(messageBody);
515
516         WKRetainPtr<WKStringRef> subMessageKey(AdoptWK, WKStringCreateWithUTF8CString("SubMessage"));
517         WKStringRef subMessageName = static_cast<WKStringRef>(WKDictionaryGetItemForKey(messageBodyDictionary, subMessageKey.get()));
518
519         if (WKStringIsEqualToUTF8CString(subMessageName, "KeyDown")) {
520             WKRetainPtr<WKStringRef> keyKey = adoptWK(WKStringCreateWithUTF8CString("Key"));
521             WKStringRef key = static_cast<WKStringRef>(WKDictionaryGetItemForKey(messageBodyDictionary, keyKey.get()));
522
523             WKRetainPtr<WKStringRef> modifiersKey = adoptWK(WKStringCreateWithUTF8CString("Modifiers"));
524             WKEventModifiers modifiers = static_cast<WKEventModifiers>(WKUInt64GetValue(static_cast<WKUInt64Ref>(WKDictionaryGetItemForKey(messageBodyDictionary, modifiersKey.get()))));
525
526             WKRetainPtr<WKStringRef> locationKey = adoptWK(WKStringCreateWithUTF8CString("Location"));
527             unsigned location = static_cast<unsigned>(WKUInt64GetValue(static_cast<WKUInt64Ref>(WKDictionaryGetItemForKey(messageBodyDictionary, locationKey.get()))));
528
529             WKRetainPtr<WKStringRef> timestampKey = adoptWK(WKStringCreateWithUTF8CString("Timestamp"));
530             double timestamp = WKDoubleGetValue(static_cast<WKDoubleRef>(WKDictionaryGetItemForKey(messageBodyDictionary, timestampKey.get())));
531
532             // Forward to WebProcess
533             m_eventSenderProxy->keyDown(key, modifiers, location, timestamp);
534             return 0;
535         }
536         ASSERT_NOT_REACHED();
537     }
538 #endif
539     return m_currentInvocation->didReceiveSynchronousMessageFromInjectedBundle(messageName, messageBody);
540 }
541
542 // WKPageLoaderClient
543
544 void TestController::didFinishLoadForFrame(WKPageRef page, WKFrameRef frame, WKTypeRef, const void* clientInfo)
545 {
546     static_cast<TestController*>(const_cast<void*>(clientInfo))->didFinishLoadForFrame(page, frame);
547 }
548
549 void TestController::processDidCrash(WKPageRef page, const void* clientInfo)
550 {
551     static_cast<TestController*>(const_cast<void*>(clientInfo))->processDidCrash();
552 }
553
554 void TestController::didFinishLoadForFrame(WKPageRef page, WKFrameRef frame)
555 {
556     if (m_state != Resetting)
557         return;
558
559     if (!WKFrameIsMainFrame(frame))
560         return;
561
562     WKRetainPtr<WKURLRef> wkURL(AdoptWK, WKFrameCopyURL(frame));
563     if (!WKURLIsEqual(wkURL.get(), blankURL()))
564         return;
565
566     m_doneResetting = true;
567     shared().notifyDone();
568 }
569
570 void TestController::processDidCrash()
571 {
572     // This function can be called multiple times when crash logs are being saved on Windows, so
573     // ensure we only print the crashed message once.
574     if (!m_didPrintWebProcessCrashedMessage) {
575         fputs("#CRASHED - WebProcess\n", stderr);
576         fflush(stderr);
577         m_didPrintWebProcessCrashedMessage = true;
578     }
579
580     if (m_shouldExitWhenWebProcessCrashes)
581         exit(1);
582 }
583
584 } // namespace WTR