• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2005, 2006, 2007, 2008, 2009 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  *
8  * 1.  Redistributions of source code must retain the above copyright
9  *     notice, this list of conditions and the following disclaimer.
10  * 2.  Redistributions in binary form must reproduce the above copyright
11  *     notice, this list of conditions and the following disclaimer in the
12  *     documentation and/or other materials provided with the distribution.
13  * 3.  Neither the name of Apple Computer, Inc. ("Apple") nor the names of
14  *     its contributors may be used to endorse or promote products derived
15  *     from this software without specific prior written permission.
16  *
17  * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
18  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
19  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
20  * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
21  * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
22  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
23  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
24  * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27  */
28 
29 #include "config.h"
30 #include "DumpRenderTree.h"
31 
32 #include "EditingDelegate.h"
33 #include "FrameLoadDelegate.h"
34 #include "LayoutTestController.h"
35 #include "PixelDumpSupport.h"
36 #include "PolicyDelegate.h"
37 #include "ResourceLoadDelegate.h"
38 #include "UIDelegate.h"
39 #include "WorkQueueItem.h"
40 #include "WorkQueue.h"
41 
42 #include <fcntl.h>
43 #include <io.h>
44 #include <math.h>
45 #include <pthread.h>
46 #include <shlwapi.h>
47 #include <stdio.h>
48 #include <string.h>
49 #include <tchar.h>
50 #include <wtf/RetainPtr.h>
51 #include <wtf/Vector.h>
52 #include <windows.h>
53 #if PLATFORM(CFNETWORK)
54 #include <CFNetwork/CFURLCachePriv.h>
55 #endif
56 #include <CoreFoundation/CoreFoundation.h>
57 #include <JavaScriptCore/JavaScriptCore.h>
58 #include <WebKit/WebKit.h>
59 #include <WebKit/WebKitCOMAPI.h>
60 
61 using namespace std;
62 
63 #ifndef NDEBUG
64 const LPWSTR TestPluginDir = L"TestNetscapePlugin_Debug";
65 #else
66 const LPWSTR TestPluginDir = L"TestNetscapePlugin";
67 #endif
68 
69 static LPCWSTR fontsEnvironmentVariable = L"WEBKIT_TESTFONTS";
70 #define USE_MAC_FONTS
71 
72 const LPCWSTR kDumpRenderTreeClassName = L"DumpRenderTreeWindow";
73 
74 static bool dumpTree = true;
75 static bool dumpPixels;
76 static bool dumpAllPixels;
77 static bool printSeparators;
78 static bool leakChecking = false;
79 static bool threaded = false;
80 static bool forceComplexText = false;
81 static RetainPtr<CFStringRef> persistentUserStyleSheetLocation;
82 
83 volatile bool done;
84 // This is the topmost frame that is loading, during a given load, or nil when no load is
85 // in progress.  Usually this is the same as the main frame, but not always.  In the case
86 // where a frameset is loaded, and then new content is loaded into one of the child frames,
87 // that child frame is the "topmost frame that is loading".
88 IWebFrame* topLoadingFrame;     // !nil iff a load is in progress
89 static COMPtr<IWebHistoryItem> prevTestBFItem;  // current b/f item at the end of the previous test
90 PolicyDelegate* policyDelegate;
91 COMPtr<FrameLoadDelegate> sharedFrameLoadDelegate;
92 COMPtr<UIDelegate> sharedUIDelegate;
93 COMPtr<EditingDelegate> sharedEditingDelegate;
94 COMPtr<ResourceLoadDelegate> sharedResourceLoadDelegate;
95 
96 IWebFrame* frame;
97 HWND webViewWindow;
98 
99 LayoutTestController* gLayoutTestController = 0;
100 
101 UINT_PTR waitToDumpWatchdog = 0;
102 
103 const unsigned maxViewWidth = 800;
104 const unsigned maxViewHeight = 600;
105 
setPersistentUserStyleSheetLocation(CFStringRef url)106 void setPersistentUserStyleSheetLocation(CFStringRef url)
107 {
108     persistentUserStyleSheetLocation = url;
109 }
110 
urlSuitableForTestResult(const wstring & url)111 wstring urlSuitableForTestResult(const wstring& url)
112 {
113     if (!url.c_str() || url.find(L"file://") == wstring::npos)
114         return url;
115 
116     return PathFindFileNameW(url.c_str());
117 }
118 
DumpRenderTreeWndProc(HWND hWnd,UINT msg,WPARAM wParam,LPARAM lParam)119 static LRESULT CALLBACK DumpRenderTreeWndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
120 {
121     switch (msg) {
122         case WM_DESTROY:
123             for (unsigned i = openWindows().size() - 1; i >= 0; --i) {
124                 if (openWindows()[i] == hWnd) {
125                     openWindows().remove(i);
126                     windowToWebViewMap().remove(hWnd);
127                     break;
128                 }
129             }
130             return 0;
131             break;
132         default:
133             return DefWindowProc(hWnd, msg, wParam, lParam);
134     }
135 }
136 
exePath()137 static const wstring& exePath()
138 {
139     static wstring path;
140     static bool initialized;
141 
142     if (initialized)
143         return path;
144     initialized = true;
145 
146     TCHAR buffer[MAX_PATH];
147     GetModuleFileName(GetModuleHandle(0), buffer, ARRAYSIZE(buffer));
148     path = buffer;
149     int lastSlash = path.rfind('\\');
150     if (lastSlash != -1 && lastSlash + 1 < path.length())
151         path = path.substr(0, lastSlash + 1);
152 
153     return path;
154 }
155 
fontsPath()156 static const wstring& fontsPath()
157 {
158     static wstring path;
159     static bool initialized;
160 
161     if (initialized)
162         return path;
163     initialized = true;
164 
165     DWORD size = GetEnvironmentVariable(fontsEnvironmentVariable, 0, 0);
166     Vector<TCHAR> buffer(size);
167     if (GetEnvironmentVariable(fontsEnvironmentVariable, buffer.data(), buffer.size())) {
168         path = buffer.data();
169         if (path[path.length() - 1] != '\\')
170             path.append(L"\\");
171         return path;
172     }
173 
174     path = exePath() + TEXT("DumpRenderTree.resources\\");
175     return path;
176 }
177 
178 #ifdef DEBUG_WEBKIT_HAS_SUFFIX
179 #define WEBKITDLL TEXT("WebKit_debug.dll")
180 #else
181 #define WEBKITDLL TEXT("WebKit.dll")
182 #endif
183 
initialize()184 static void initialize()
185 {
186     if (HMODULE webKitModule = LoadLibrary(WEBKITDLL))
187         if (FARPROC dllRegisterServer = GetProcAddress(webKitModule, "DllRegisterServer"))
188             dllRegisterServer();
189 
190     // Init COM
191     OleInitialize(0);
192 
193     static LPCTSTR fontsToInstall[] = {
194         TEXT("AHEM____.ttf"),
195         TEXT("Apple Chancery.ttf"),
196         TEXT("Courier Bold.ttf"),
197         TEXT("Courier.ttf"),
198         TEXT("Helvetica Bold Oblique.ttf"),
199         TEXT("Helvetica Bold.ttf"),
200         TEXT("Helvetica Oblique.ttf"),
201         TEXT("Helvetica.ttf"),
202         TEXT("Helvetica Neue Bold Italic.ttf"),
203         TEXT("Helvetica Neue Bold.ttf"),
204         TEXT("Helvetica Neue Condensed Black.ttf"),
205         TEXT("Helvetica Neue Condensed Bold.ttf"),
206         TEXT("Helvetica Neue Italic.ttf"),
207         TEXT("Helvetica Neue Light Italic.ttf"),
208         TEXT("Helvetica Neue Light.ttf"),
209         TEXT("Helvetica Neue UltraLight Italic.ttf"),
210         TEXT("Helvetica Neue UltraLight.ttf"),
211         TEXT("Helvetica Neue.ttf"),
212         TEXT("Lucida Grande.ttf"),
213         TEXT("Lucida Grande Bold.ttf"),
214         TEXT("Monaco.ttf"),
215         TEXT("Papyrus.ttf"),
216         TEXT("Times Bold Italic.ttf"),
217         TEXT("Times Bold.ttf"),
218         TEXT("Times Italic.ttf"),
219         TEXT("Times Roman.ttf"),
220         TEXT("WebKit Layout Tests 2.ttf"),
221         TEXT("WebKit Layout Tests.ttf"),
222         TEXT("WebKitWeightWatcher100.ttf"),
223         TEXT("WebKitWeightWatcher200.ttf"),
224         TEXT("WebKitWeightWatcher300.ttf"),
225         TEXT("WebKitWeightWatcher400.ttf"),
226         TEXT("WebKitWeightWatcher500.ttf"),
227         TEXT("WebKitWeightWatcher600.ttf"),
228         TEXT("WebKitWeightWatcher700.ttf"),
229         TEXT("WebKitWeightWatcher800.ttf"),
230         TEXT("WebKitWeightWatcher900.ttf")
231     };
232 
233     wstring resourcesPath = fontsPath();
234 
235     COMPtr<IWebTextRenderer> textRenderer;
236     if (SUCCEEDED(WebKitCreateInstance(CLSID_WebTextRenderer, 0, IID_IWebTextRenderer, (void**)&textRenderer)))
237         for (int i = 0; i < ARRAYSIZE(fontsToInstall); ++i)
238             textRenderer->registerPrivateFont(wstring(resourcesPath + fontsToInstall[i]).c_str());
239 
240     // Register a host window
241     WNDCLASSEX wcex;
242 
243     wcex.cbSize = sizeof(WNDCLASSEX);
244 
245     wcex.style         = CS_HREDRAW | CS_VREDRAW;
246     wcex.lpfnWndProc   = DumpRenderTreeWndProc;
247     wcex.cbClsExtra    = 0;
248     wcex.cbWndExtra    = 0;
249     wcex.hInstance     = GetModuleHandle(0);
250     wcex.hIcon         = 0;
251     wcex.hCursor       = LoadCursor(0, IDC_ARROW);
252     wcex.hbrBackground = 0;
253     wcex.lpszMenuName  = 0;
254     wcex.lpszClassName = kDumpRenderTreeClassName;
255     wcex.hIconSm       = 0;
256 
257     RegisterClassEx(&wcex);
258 }
259 
displayWebView()260 void displayWebView()
261 {
262     ::InvalidateRect(webViewWindow, 0, TRUE);
263     ::UpdateWindow(webViewWindow);
264 }
265 
dumpFrameScrollPosition(IWebFrame * frame)266 void dumpFrameScrollPosition(IWebFrame* frame)
267 {
268     if (!frame)
269         return;
270 
271     COMPtr<IWebFramePrivate> framePrivate;
272     if (FAILED(frame->QueryInterface(&framePrivate)))
273         return;
274 
275     SIZE scrollPosition;
276     if (FAILED(framePrivate->scrollOffset(&scrollPosition)))
277         return;
278 
279     if (abs(scrollPosition.cx) > 0.00000001 || abs(scrollPosition.cy) > 0.00000001) {
280         COMPtr<IWebFrame> parent;
281         if (FAILED(frame->parentFrame(&parent)))
282             return;
283         if (parent) {
284             BSTR name;
285             if (FAILED(frame->name(&name)))
286                 return;
287             printf("frame '%S' ", name ? name : L"");
288             SysFreeString(name);
289         }
290         printf("scrolled to %.f,%.f\n", (double)scrollPosition.cx, (double)scrollPosition.cy);
291     }
292 
293     if (::gLayoutTestController->dumpChildFrameScrollPositions()) {
294         COMPtr<IEnumVARIANT> enumKids;
295         if (FAILED(frame->childFrames(&enumKids)))
296             return;
297         VARIANT var;
298         VariantInit(&var);
299         while (enumKids->Next(1, &var, 0) == S_OK) {
300             ASSERT(V_VT(&var) == VT_UNKNOWN);
301             COMPtr<IWebFrame> framePtr;
302             V_UNKNOWN(&var)->QueryInterface(IID_IWebFrame, (void**)&framePtr);
303             dumpFrameScrollPosition(framePtr.get());
304             VariantClear(&var);
305         }
306     }
307 }
308 
dumpFramesAsText(IWebFrame * frame)309 static wstring dumpFramesAsText(IWebFrame* frame)
310 {
311     if (!frame)
312         return L"";
313 
314     COMPtr<IDOMDocument> document;
315     if (FAILED(frame->DOMDocument(&document)))
316         return L"";
317 
318     COMPtr<IDOMElement> documentElement;
319     if (FAILED(document->documentElement(&documentElement)))
320         return L"";
321 
322     wstring result;
323 
324     // Add header for all but the main frame.
325     COMPtr<IWebFrame> parent;
326     if (FAILED(frame->parentFrame(&parent)))
327         return L"";
328     if (parent) {
329         BSTR name = L"";
330         if (FAILED(frame->name(&name)))
331             return L"";
332 
333         result.append(L"\n--------\nFrame: '");
334         result.append(name ? name : L"", SysStringLen(name));
335         result.append(L"'\n--------\n");
336 
337         SysFreeString(name);
338     }
339 
340     BSTR innerText = 0;
341     COMPtr<IDOMElementPrivate> docPrivate;
342     if (SUCCEEDED(documentElement->QueryInterface(&docPrivate)))
343         docPrivate->innerText(&innerText);
344 
345     result.append(innerText ? innerText : L"", SysStringLen(innerText));
346     result.append(L"\n");
347 
348     SysFreeString(innerText);
349 
350     if (::gLayoutTestController->dumpChildFramesAsText()) {
351         COMPtr<IEnumVARIANT> enumKids;
352         if (FAILED(frame->childFrames(&enumKids)))
353             return L"";
354         VARIANT var;
355         VariantInit(&var);
356         while (enumKids->Next(1, &var, 0) == S_OK) {
357             ASSERT(V_VT(&var) == VT_UNKNOWN);
358             COMPtr<IWebFrame> framePtr;
359             V_UNKNOWN(&var)->QueryInterface(IID_IWebFrame, (void**)&framePtr);
360             result.append(dumpFramesAsText(framePtr.get()));
361             VariantClear(&var);
362         }
363     }
364 
365     return result;
366 }
367 
compareHistoryItems(const void * item1,const void * item2)368 static int compareHistoryItems(const void* item1, const void* item2)
369 {
370     COMPtr<IWebHistoryItemPrivate> itemA;
371     if (FAILED((*(COMPtr<IUnknown>*)item1)->QueryInterface(&itemA)))
372         return 0;
373 
374     COMPtr<IWebHistoryItemPrivate> itemB;
375     if (FAILED((*(COMPtr<IUnknown>*)item2)->QueryInterface(&itemB)))
376         return 0;
377 
378     BSTR targetA;
379     if (FAILED(itemA->target(&targetA)))
380         return 0;
381 
382     BSTR targetB;
383     if (FAILED(itemB->target(&targetB))) {
384         SysFreeString(targetA);
385         return 0;
386     }
387 
388     int result = wcsicmp(wstring(targetA, SysStringLen(targetA)).c_str(), wstring(targetB, SysStringLen(targetB)).c_str());
389     SysFreeString(targetA);
390     SysFreeString(targetB);
391     return result;
392 }
393 
dumpHistoryItem(IWebHistoryItem * item,int indent,bool current)394 static void dumpHistoryItem(IWebHistoryItem* item, int indent, bool current)
395 {
396     assert(item);
397 
398     int start = 0;
399     if (current) {
400         printf("curr->");
401         start = 6;
402     }
403     for (int i = start; i < indent; i++)
404         putchar(' ');
405 
406     BSTR url;
407     if (FAILED(item->URLString(&url)))
408         return;
409 
410     if (wcsstr(url, L"file:/") == url) {
411         static wchar_t* layoutTestsString = L"/LayoutTests/";
412         static wchar_t* fileTestString = L"(file test):";
413 
414         wchar_t* result = wcsstr(url, layoutTestsString);
415         if (result == NULL)
416             return;
417         wchar_t* start = result + wcslen(layoutTestsString);
418 
419         BSTR newURL = SysAllocStringLen(NULL, SysStringLen(url));
420         wcscpy(newURL, fileTestString);
421         wcscpy(newURL + wcslen(fileTestString), start);
422 
423         SysFreeString(url);
424         url = newURL;
425     }
426 
427     printf("%S", url ? url : L"");
428     SysFreeString(url);
429 
430     COMPtr<IWebHistoryItemPrivate> itemPrivate;
431     if (FAILED(item->QueryInterface(&itemPrivate)))
432         return;
433 
434     BSTR target;
435     if (FAILED(itemPrivate->target(&target)))
436         return;
437     if (SysStringLen(target))
438         printf(" (in frame \"%S\")", target);
439     SysFreeString(target);
440     BOOL isTargetItem = FALSE;
441     if (FAILED(itemPrivate->isTargetItem(&isTargetItem)))
442         return;
443     if (isTargetItem)
444         printf("  **nav target**");
445     putchar('\n');
446 
447     unsigned kidsCount;
448     SAFEARRAY* arrPtr;
449     if (FAILED(itemPrivate->children(&kidsCount, &arrPtr)) || !kidsCount)
450         return;
451 
452     Vector<COMPtr<IUnknown> > kidsVector;
453 
454     LONG lowerBound;
455     if (FAILED(::SafeArrayGetLBound(arrPtr, 1, &lowerBound)))
456         goto exit;
457 
458     LONG upperBound;
459     if (FAILED(::SafeArrayGetUBound(arrPtr, 1, &upperBound)))
460         goto exit;
461 
462     LONG length = upperBound - lowerBound + 1;
463     if (!length)
464         goto exit;
465     ASSERT(length == kidsCount);
466 
467     IUnknown** safeArrayData;
468     if (FAILED(::SafeArrayAccessData(arrPtr, (void**)&safeArrayData)))
469         goto exit;
470 
471     for (int i = 0; i < length; ++i)
472         kidsVector.append(safeArrayData[i]);
473     ::SafeArrayUnaccessData(arrPtr);
474 
475     // must sort to eliminate arbitrary result ordering which defeats reproducible testing
476     qsort(kidsVector.data(), kidsCount, sizeof(kidsVector[0]), compareHistoryItems);
477 
478     for (unsigned i = 0; i < kidsCount; ++i) {
479         COMPtr<IWebHistoryItem> item;
480         kidsVector[i]->QueryInterface(&item);
481         dumpHistoryItem(item.get(), indent + 4, false);
482     }
483 
484 exit:
485     if (arrPtr && SUCCEEDED(::SafeArrayUnlock(arrPtr)))
486         ::SafeArrayDestroy(arrPtr);
487 }
488 
dumpBackForwardList(IWebView * webView)489 static void dumpBackForwardList(IWebView* webView)
490 {
491     ASSERT(webView);
492 
493     printf("\n============== Back Forward List ==============\n");
494 
495     COMPtr<IWebBackForwardList> bfList;
496     if (FAILED(webView->backForwardList(&bfList)))
497         return;
498 
499     // Print out all items in the list after prevTestBFItem, which was from the previous test
500     // Gather items from the end of the list, the print them out from oldest to newest
501 
502     Vector<COMPtr<IUnknown> > itemsToPrint;
503 
504     int forwardListCount;
505     if (FAILED(bfList->forwardListCount(&forwardListCount)))
506         return;
507 
508     for (int i = forwardListCount; i > 0; --i) {
509         COMPtr<IWebHistoryItem> item;
510         if (FAILED(bfList->itemAtIndex(i, &item)))
511             return;
512         // something is wrong if the item from the last test is in the forward part of the b/f list
513         assert(item != prevTestBFItem);
514         COMPtr<IUnknown> itemUnknown;
515         item->QueryInterface(&itemUnknown);
516         itemsToPrint.append(itemUnknown);
517     }
518 
519     COMPtr<IWebHistoryItem> currentItem;
520     if (FAILED(bfList->currentItem(&currentItem)))
521         return;
522 
523     assert(currentItem != prevTestBFItem);
524     COMPtr<IUnknown> currentItemUnknown;
525     currentItem->QueryInterface(&currentItemUnknown);
526     itemsToPrint.append(currentItemUnknown);
527     int currentItemIndex = itemsToPrint.size() - 1;
528 
529     int backListCount;
530     if (FAILED(bfList->backListCount(&backListCount)))
531         return;
532 
533     for (int i = -1; i >= -backListCount; --i) {
534         COMPtr<IWebHistoryItem> item;
535         if (FAILED(bfList->itemAtIndex(i, &item)))
536             return;
537         if (item == prevTestBFItem)
538             break;
539         COMPtr<IUnknown> itemUnknown;
540         item->QueryInterface(&itemUnknown);
541         itemsToPrint.append(itemUnknown);
542     }
543 
544     for (int i = itemsToPrint.size() - 1; i >= 0; --i) {
545         COMPtr<IWebHistoryItem> historyItemToPrint;
546         itemsToPrint[i]->QueryInterface(&historyItemToPrint);
547         dumpHistoryItem(historyItemToPrint.get(), 8, i == currentItemIndex);
548     }
549 
550     printf("===============================================\n");
551 }
552 
dumpBackForwardListForAllWindows()553 static void dumpBackForwardListForAllWindows()
554 {
555     unsigned count = openWindows().size();
556     for (unsigned i = 0; i < count; i++) {
557         HWND window = openWindows()[i];
558         IWebView* webView = windowToWebViewMap().get(window).get();
559         dumpBackForwardList(webView);
560     }
561 }
562 
invalidateAnyPreviousWaitToDumpWatchdog()563 static void invalidateAnyPreviousWaitToDumpWatchdog()
564 {
565     if (!waitToDumpWatchdog)
566         return;
567 
568     KillTimer(0, waitToDumpWatchdog);
569     waitToDumpWatchdog = 0;
570 }
571 
dump()572 void dump()
573 {
574     invalidateAnyPreviousWaitToDumpWatchdog();
575 
576     COMPtr<IWebDataSource> dataSource;
577     if (SUCCEEDED(frame->dataSource(&dataSource))) {
578         COMPtr<IWebURLResponse> response;
579         if (SUCCEEDED(dataSource->response(&response)) && response) {
580             BSTR mimeType;
581             if (SUCCEEDED(response->MIMEType(&mimeType)))
582                 ::gLayoutTestController->setDumpAsText(::gLayoutTestController->dumpAsText() | !_tcscmp(mimeType, TEXT("text/plain")));
583             SysFreeString(mimeType);
584         }
585     }
586 
587     BSTR resultString = 0;
588 
589     if (dumpTree) {
590         if (::gLayoutTestController->dumpAsText()) {
591             ::InvalidateRect(webViewWindow, 0, TRUE);
592             ::SendMessage(webViewWindow, WM_PAINT, 0, 0);
593             wstring result = dumpFramesAsText(frame);
594             resultString = SysAllocStringLen(result.data(), result.size());
595         } else {
596             bool isSVGW3CTest = (gLayoutTestController->testPathOrURL().find("svg\\W3C-SVG-1.1") != string::npos);
597             unsigned width;
598             unsigned height;
599             if (isSVGW3CTest) {
600                 width = 480;
601                 height = 360;
602             } else {
603                 width = maxViewWidth;
604                 height = maxViewHeight;
605             }
606 
607             ::SetWindowPos(webViewWindow, 0, 0, 0, width, height, SWP_NOMOVE);
608             ::InvalidateRect(webViewWindow, 0, TRUE);
609             ::SendMessage(webViewWindow, WM_PAINT, 0, 0);
610 
611             COMPtr<IWebFramePrivate> framePrivate;
612             if (FAILED(frame->QueryInterface(&framePrivate)))
613                 goto fail;
614             framePrivate->renderTreeAsExternalRepresentation(&resultString);
615         }
616 
617         if (!resultString)
618             printf("ERROR: nil result from %s", ::gLayoutTestController->dumpAsText() ? "IDOMElement::innerText" : "IFrameViewPrivate::renderTreeAsExternalRepresentation");
619         else {
620             unsigned stringLength = SysStringLen(resultString);
621             int bufferSize = ::WideCharToMultiByte(CP_UTF8, 0, resultString, stringLength, 0, 0, 0, 0);
622             char* buffer = (char*)malloc(bufferSize + 1);
623             ::WideCharToMultiByte(CP_UTF8, 0, resultString, stringLength, buffer, bufferSize + 1, 0, 0);
624             fwrite(buffer, 1, bufferSize, stdout);
625             free(buffer);
626             if (!::gLayoutTestController->dumpAsText())
627                 dumpFrameScrollPosition(frame);
628         }
629         if (::gLayoutTestController->dumpBackForwardList())
630             dumpBackForwardListForAllWindows();
631     }
632 
633     if (printSeparators) {
634         puts("#EOF");   // terminate the content block
635         fputs("#EOF\n", stderr);
636         fflush(stdout);
637         fflush(stderr);
638     }
639 
640     if (dumpPixels) {
641         if (!gLayoutTestController->dumpAsText() && !gLayoutTestController->dumpDOMAsWebArchive() && !gLayoutTestController->dumpSourceAsWebArchive())
642             dumpWebViewAsPixelsAndCompareWithExpected(gLayoutTestController->expectedPixelHash());
643     }
644 
645     printf("#EOF\n");   // terminate the (possibly empty) pixels block
646     fflush(stdout);
647 
648 fail:
649     SysFreeString(resultString);
650     // This will exit from our message loop.
651     PostQuitMessage(0);
652     done = true;
653 }
654 
shouldLogFrameLoadDelegates(const char * pathOrURL)655 static bool shouldLogFrameLoadDelegates(const char* pathOrURL)
656 {
657     return strstr(pathOrURL, "/loading/") || strstr(pathOrURL, "\\loading\\");
658 }
659 
resetWebViewToConsistentStateBeforeTesting()660 static void resetWebViewToConsistentStateBeforeTesting()
661 {
662     COMPtr<IWebView> webView;
663     if (FAILED(frame->webView(&webView)))
664         return;
665 
666     webView->setPolicyDelegate(0);
667     policyDelegate->setPermissive(false);
668     policyDelegate->setControllerToNotifyDone(0);
669 
670     COMPtr<IWebIBActions> webIBActions(Query, webView);
671     if (webIBActions) {
672         webIBActions->makeTextStandardSize(0);
673         webIBActions->resetPageZoom(0);
674     }
675 
676     COMPtr<IWebPreferences> preferences;
677     if (SUCCEEDED(webView->preferences(&preferences))) {
678         preferences->setPrivateBrowsingEnabled(FALSE);
679         preferences->setJavaScriptCanOpenWindowsAutomatically(TRUE);
680         preferences->setLoadsImagesAutomatically(TRUE);
681 
682         if (persistentUserStyleSheetLocation) {
683             Vector<wchar_t> urlCharacters(CFStringGetLength(persistentUserStyleSheetLocation.get()));
684             CFStringGetCharacters(persistentUserStyleSheetLocation.get(), CFRangeMake(0, CFStringGetLength(persistentUserStyleSheetLocation.get())), (UniChar *)urlCharacters.data());
685             BSTR url = SysAllocStringLen(urlCharacters.data(), urlCharacters.size());
686             preferences->setUserStyleSheetLocation(url);
687             SysFreeString(url);
688             preferences->setUserStyleSheetEnabled(TRUE);
689         } else
690             preferences->setUserStyleSheetEnabled(FALSE);
691 
692         COMPtr<IWebPreferencesPrivate> prefsPrivate(Query, preferences);
693         if (prefsPrivate) {
694             prefsPrivate->setAuthorAndUserStylesEnabled(TRUE);
695             prefsPrivate->setDeveloperExtrasEnabled(FALSE);
696             prefsPrivate->setShouldPaintNativeControls(FALSE); // FIXME - need to make DRT pass with Windows native controls <http://bugs.webkit.org/show_bug.cgi?id=25592>
697             prefsPrivate->setXSSAuditorEnabled(FALSE);
698         }
699     }
700 
701     COMPtr<IWebViewEditing> viewEditing;
702     if (SUCCEEDED(webView->QueryInterface(&viewEditing)))
703         viewEditing->setSmartInsertDeleteEnabled(TRUE);
704 
705     COMPtr<IWebViewPrivate> webViewPrivate(Query, webView);
706     if (!webViewPrivate)
707         return;
708 
709     COMPtr<IWebInspector> inspector;
710     if (SUCCEEDED(webViewPrivate->inspector(&inspector)))
711         inspector->setJavaScriptProfilingEnabled(FALSE);
712 
713     HWND viewWindow;
714     if (SUCCEEDED(webViewPrivate->viewWindow(reinterpret_cast<OLE_HANDLE*>(&viewWindow))) && viewWindow)
715         SetFocus(viewWindow);
716 
717     webViewPrivate->clearMainFrameName();
718 
719     sharedUIDelegate->resetUndoManager();
720 }
721 
runTest(const string & testPathOrURL)722 static void runTest(const string& testPathOrURL)
723 {
724     static BSTR methodBStr = SysAllocString(TEXT("GET"));
725 
726     // Look for "'" as a separator between the path or URL, and the pixel dump hash that follows.
727     string pathOrURL(testPathOrURL);
728     string expectedPixelHash;
729 
730     size_t separatorPos = pathOrURL.find("'");
731     if (separatorPos != string::npos) {
732         pathOrURL = string(testPathOrURL, 0, separatorPos);
733         expectedPixelHash = string(testPathOrURL, separatorPos + 1);
734     }
735 
736     BSTR urlBStr;
737 
738     CFStringRef str = CFStringCreateWithCString(0, pathOrURL.c_str(), kCFStringEncodingWindowsLatin1);
739     CFURLRef url = CFURLCreateWithString(0, str, 0);
740 
741     if (!url)
742         url = CFURLCreateWithFileSystemPath(0, str, kCFURLWindowsPathStyle, false);
743 
744     CFRelease(str);
745 
746     str = CFURLGetString(url);
747 
748     CFIndex length = CFStringGetLength(str);
749     UniChar* buffer = new UniChar[length];
750 
751     CFStringGetCharacters(str, CFRangeMake(0, length), buffer);
752     urlBStr = SysAllocStringLen((OLECHAR*)buffer, length);
753     delete[] buffer;
754 
755     CFRelease(url);
756 
757     ::gLayoutTestController = new LayoutTestController(pathOrURL, expectedPixelHash);
758     done = false;
759     topLoadingFrame = 0;
760 
761     gLayoutTestController->setIconDatabaseEnabled(false);
762 
763     if (shouldLogFrameLoadDelegates(pathOrURL.c_str()))
764         gLayoutTestController->setDumpFrameLoadCallbacks(true);
765 
766     COMPtr<IWebHistory> history;
767     if (SUCCEEDED(WebKitCreateInstance(CLSID_WebHistory, 0, __uuidof(history), reinterpret_cast<void**>(&history))))
768         history->setOptionalSharedHistory(0);
769 
770     resetWebViewToConsistentStateBeforeTesting();
771 
772     prevTestBFItem = 0;
773     COMPtr<IWebView> webView;
774     if (SUCCEEDED(frame->webView(&webView))) {
775         COMPtr<IWebBackForwardList> bfList;
776         if (SUCCEEDED(webView->backForwardList(&bfList)))
777             bfList->currentItem(&prevTestBFItem);
778     }
779 
780     WorkQueue::shared()->clear();
781     WorkQueue::shared()->setFrozen(false);
782 
783     HWND hostWindow;
784     webView->hostWindow(reinterpret_cast<OLE_HANDLE*>(&hostWindow));
785 
786     COMPtr<IWebMutableURLRequest> request;
787     HRESULT hr = WebKitCreateInstance(CLSID_WebMutableURLRequest, 0, IID_IWebMutableURLRequest, (void**)&request);
788     if (FAILED(hr))
789         goto exit;
790 
791     request->initWithURL(urlBStr, WebURLRequestUseProtocolCachePolicy, 60);
792 
793     request->setHTTPMethod(methodBStr);
794     frame->loadRequest(request.get());
795 
796     MSG msg;
797     while (GetMessage(&msg, 0, 0, 0)) {
798         // We get spurious WM_MOUSELEAVE events which make event handling machinery think that mouse button
799         // is released during dragging (see e.g. fast\dynamic\layer-hit-test-crash.html).
800         // Mouse can never leave WebView during normal DumpRenderTree operation, so we just ignore all such events.
801         if (msg.message == WM_MOUSELEAVE)
802             continue;
803         TranslateMessage(&msg);
804         DispatchMessage(&msg);
805     }
806 
807     resetWebViewToConsistentStateBeforeTesting();
808 
809     frame->stopLoading();
810 
811     if (::gLayoutTestController->closeRemainingWindowsWhenComplete()) {
812         Vector<HWND> windows = openWindows();
813         unsigned size = windows.size();
814         for (unsigned i = 0; i < size; i++) {
815             HWND window = windows[i];
816 
817             // Don't try to close the main window
818             if (window == hostWindow)
819                 continue;
820 
821             DestroyWindow(window);
822         }
823     }
824 
825 exit:
826     SysFreeString(urlBStr);
827     ::gLayoutTestController->deref();
828     ::gLayoutTestController = 0;
829 
830     return;
831 }
832 
initializePreferences(IWebPreferences * preferences)833 static void initializePreferences(IWebPreferences* preferences)
834 {
835 #ifdef USE_MAC_FONTS
836     BSTR standardFamily = SysAllocString(TEXT("Times"));
837     BSTR fixedFamily = SysAllocString(TEXT("Courier"));
838     BSTR sansSerifFamily = SysAllocString(TEXT("Helvetica"));
839     BSTR cursiveFamily = SysAllocString(TEXT("Apple Chancery"));
840     BSTR fantasyFamily = SysAllocString(TEXT("Papyrus"));
841 #else
842     BSTR standardFamily = SysAllocString(TEXT("Times New Roman"));
843     BSTR fixedFamily = SysAllocString(TEXT("Courier New"));
844     BSTR sansSerifFamily = SysAllocString(TEXT("Arial"));
845     BSTR cursiveFamily = SysAllocString(TEXT("Comic Sans MS")); // Not actually cursive, but it's what IE and Firefox use.
846     BSTR fantasyFamily = SysAllocString(TEXT("Times New Roman"));
847 #endif
848 
849     preferences->setStandardFontFamily(standardFamily);
850     preferences->setFixedFontFamily(fixedFamily);
851     preferences->setSerifFontFamily(standardFamily);
852     preferences->setSansSerifFontFamily(sansSerifFamily);
853     preferences->setCursiveFontFamily(cursiveFamily);
854     preferences->setFantasyFontFamily(fantasyFamily);
855 
856     preferences->setAutosaves(FALSE);
857     preferences->setJavaEnabled(FALSE);
858     preferences->setPlugInsEnabled(TRUE);
859     preferences->setDOMPasteAllowed(TRUE);
860     preferences->setEditableLinkBehavior(WebKitEditableLinkOnlyLiveWithShiftKey);
861     preferences->setFontSmoothing(FontSmoothingTypeStandard);
862     preferences->setUsesPageCache(FALSE);
863 
864     SysFreeString(standardFamily);
865     SysFreeString(fixedFamily);
866     SysFreeString(sansSerifFamily);
867     SysFreeString(cursiveFamily);
868     SysFreeString(fantasyFamily);
869 }
870 
pthreadEqualCallback(const void * value1,const void * value2)871 static Boolean pthreadEqualCallback(const void* value1, const void* value2)
872 {
873     return (Boolean)pthread_equal(*(pthread_t*)value1, *(pthread_t*)value2);
874 }
875 
876 static CFDictionaryKeyCallBacks pthreadKeyCallbacks = { 0, 0, 0, 0, pthreadEqualCallback, 0 };
877 
878 static pthread_mutex_t javaScriptThreadsMutex = PTHREAD_MUTEX_INITIALIZER;
879 static bool javaScriptThreadsShouldTerminate;
880 
881 static const int javaScriptThreadsCount = 4;
javaScriptThreads()882 static CFMutableDictionaryRef javaScriptThreads()
883 {
884     assert(pthread_mutex_trylock(&javaScriptThreadsMutex) == EBUSY);
885     static CFMutableDictionaryRef staticJavaScriptThreads;
886     if (!staticJavaScriptThreads)
887         staticJavaScriptThreads = CFDictionaryCreateMutable(0, 0, &pthreadKeyCallbacks, 0);
888     return staticJavaScriptThreads;
889 }
890 
891 // Loops forever, running a script and randomly respawning, until
892 // javaScriptThreadsShouldTerminate becomes true.
runJavaScriptThread(void * arg)893 void* runJavaScriptThread(void* arg)
894 {
895     const char* const script =
896     " \
897     var array = []; \
898     for (var i = 0; i < 10; i++) { \
899         array.push(String(i)); \
900     } \
901     ";
902 
903     while (true) {
904         JSGlobalContextRef ctx = JSGlobalContextCreate(0);
905         JSStringRef scriptRef = JSStringCreateWithUTF8CString(script);
906 
907         JSValueRef exception = 0;
908         JSEvaluateScript(ctx, scriptRef, 0, 0, 1, &exception);
909         assert(!exception);
910 
911         JSGlobalContextRelease(ctx);
912         JSStringRelease(scriptRef);
913 
914         JSGarbageCollect(ctx);
915 
916         pthread_mutex_lock(&javaScriptThreadsMutex);
917 
918         // Check for cancellation.
919         if (javaScriptThreadsShouldTerminate) {
920             pthread_mutex_unlock(&javaScriptThreadsMutex);
921             return 0;
922         }
923 
924         // Respawn probabilistically.
925         if (rand() % 5 == 0) {
926             pthread_t pthread;
927             pthread_create(&pthread, 0, &runJavaScriptThread, 0);
928             pthread_detach(pthread);
929 
930             pthread_t self = pthread_self();
931             CFDictionaryRemoveValue(javaScriptThreads(), self.p);
932             CFDictionaryAddValue(javaScriptThreads(), pthread.p, 0);
933 
934             pthread_mutex_unlock(&javaScriptThreadsMutex);
935             return 0;
936         }
937 
938         pthread_mutex_unlock(&javaScriptThreadsMutex);
939     }
940 }
941 
startJavaScriptThreads(void)942 static void startJavaScriptThreads(void)
943 {
944     pthread_mutex_lock(&javaScriptThreadsMutex);
945 
946     for (int i = 0; i < javaScriptThreadsCount; i++) {
947         pthread_t pthread;
948         pthread_create(&pthread, 0, &runJavaScriptThread, 0);
949         pthread_detach(pthread);
950         CFDictionaryAddValue(javaScriptThreads(), pthread.p, 0);
951     }
952 
953     pthread_mutex_unlock(&javaScriptThreadsMutex);
954 }
955 
stopJavaScriptThreads(void)956 static void stopJavaScriptThreads(void)
957 {
958     pthread_mutex_lock(&javaScriptThreadsMutex);
959 
960     javaScriptThreadsShouldTerminate = true;
961 
962     pthread_t* pthreads[javaScriptThreadsCount] = {0};
963     int threadDictCount = CFDictionaryGetCount(javaScriptThreads());
964     assert(threadDictCount == javaScriptThreadsCount);
965     CFDictionaryGetKeysAndValues(javaScriptThreads(), (const void**)pthreads, 0);
966 
967     pthread_mutex_unlock(&javaScriptThreadsMutex);
968 
969     for (int i = 0; i < javaScriptThreadsCount; i++) {
970         pthread_t* pthread = pthreads[i];
971         pthread_join(*pthread, 0);
972         free(pthread);
973     }
974 }
975 
openWindows()976 Vector<HWND>& openWindows()
977 {
978     static Vector<HWND> vector;
979     return vector;
980 }
981 
windowToWebViewMap()982 WindowToWebViewMap& windowToWebViewMap()
983 {
984     static WindowToWebViewMap map;
985     return map;
986 }
987 
createWebViewAndOffscreenWindow(HWND * webViewWindow)988 IWebView* createWebViewAndOffscreenWindow(HWND* webViewWindow)
989 {
990     HWND hostWindow = CreateWindowEx(WS_EX_TOOLWINDOW, kDumpRenderTreeClassName, TEXT("DumpRenderTree"), WS_POPUP,
991       -maxViewWidth, -maxViewHeight, maxViewWidth, maxViewHeight, 0, 0, GetModuleHandle(0), 0);
992 
993     IWebView* webView;
994 
995     HRESULT hr = WebKitCreateInstance(CLSID_WebView, 0, IID_IWebView, (void**)&webView);
996     if (FAILED(hr)) {
997         fprintf(stderr, "Failed to create CLSID_WebView instance, error 0x%x\n", hr);
998         return 0;
999     }
1000 
1001     if (FAILED(webView->setHostWindow((OLE_HANDLE)(ULONG64)hostWindow)))
1002         return 0;
1003 
1004     RECT clientRect;
1005     clientRect.bottom = clientRect.left = clientRect.top = clientRect.right = 0;
1006     BSTR groupName = SysAllocString(L"org.webkit.DumpRenderTree");
1007     bool failed = FAILED(webView->initWithFrame(clientRect, 0, groupName));
1008     SysFreeString(groupName);
1009     if (failed)
1010         return 0;
1011 
1012     COMPtr<IWebViewPrivate> viewPrivate;
1013     if (FAILED(webView->QueryInterface(&viewPrivate)))
1014         return 0;
1015 
1016     viewPrivate->setShouldApplyMacFontAscentHack(TRUE);
1017     viewPrivate->setAlwaysUsesComplexTextCodePath(forceComplexText);
1018 
1019     BSTR pluginPath = SysAllocStringLen(0, exePath().length() + _tcslen(TestPluginDir));
1020     _tcscpy(pluginPath, exePath().c_str());
1021     _tcscat(pluginPath, TestPluginDir);
1022     failed = FAILED(viewPrivate->addAdditionalPluginDirectory(pluginPath));
1023     SysFreeString(pluginPath);
1024     if (failed)
1025         return 0;
1026 
1027     HWND viewWindow;
1028     if (FAILED(viewPrivate->viewWindow(reinterpret_cast<OLE_HANDLE*>(&viewWindow))))
1029         return 0;
1030     if (webViewWindow)
1031         *webViewWindow = viewWindow;
1032 
1033     SetWindowPos(viewWindow, 0, 0, 0, maxViewWidth, maxViewHeight, 0);
1034     ShowWindow(hostWindow, SW_SHOW);
1035 
1036     if (FAILED(webView->setFrameLoadDelegate(sharedFrameLoadDelegate.get())))
1037         return 0;
1038 
1039     if (FAILED(viewPrivate->setFrameLoadDelegatePrivate(sharedFrameLoadDelegate.get())))
1040         return 0;
1041 
1042     if (FAILED(webView->setUIDelegate(sharedUIDelegate.get())))
1043         return 0;
1044 
1045     COMPtr<IWebViewEditing> viewEditing;
1046     if (FAILED(webView->QueryInterface(&viewEditing)))
1047         return 0;
1048 
1049     if (FAILED(viewEditing->setEditingDelegate(sharedEditingDelegate.get())))
1050         return 0;
1051 
1052     if (FAILED(webView->setResourceLoadDelegate(sharedResourceLoadDelegate.get())))
1053         return 0;
1054 
1055     COMPtr<IWebPreferences> preferences;
1056     if (FAILED(webView->preferences(&preferences)))
1057         return 0;
1058 
1059     initializePreferences(preferences.get());
1060 
1061     openWindows().append(hostWindow);
1062     windowToWebViewMap().set(hostWindow, webView);
1063     return webView;
1064 }
1065 
1066 #if PLATFORM(CFNETWORK)
sharedCFURLCache()1067 RetainPtr<CFURLCacheRef> sharedCFURLCache()
1068 {
1069     HMODULE module = GetModuleHandle(TEXT("CFNetwork_debug.dll"));
1070     if (!module)
1071         module = GetModuleHandle(TEXT("CFNetwork.dll"));
1072     if (!module)
1073         return 0;
1074 
1075     typedef CFURLCacheRef (*CFURLCacheCopySharedURLCacheProcPtr)(void);
1076     if (CFURLCacheCopySharedURLCacheProcPtr copyCache = reinterpret_cast<CFURLCacheCopySharedURLCacheProcPtr>(GetProcAddress(module, "CFURLCacheCopySharedURLCache")))
1077         return RetainPtr<CFURLCacheRef>(AdoptCF, copyCache());
1078 
1079     typedef CFURLCacheRef (*CFURLCacheSharedURLCacheProcPtr)(void);
1080     if (CFURLCacheSharedURLCacheProcPtr sharedCache = reinterpret_cast<CFURLCacheSharedURLCacheProcPtr>(GetProcAddress(module, "CFURLCacheSharedURLCache")))
1081         return sharedCache();
1082 
1083     return 0;
1084 }
1085 #endif
1086 
main(int argc,char * argv[])1087 int main(int argc, char* argv[])
1088 {
1089     leakChecking = false;
1090 
1091     _setmode(1, _O_BINARY);
1092     _setmode(2, _O_BINARY);
1093 
1094     initialize();
1095 
1096     Vector<const char*> tests;
1097 
1098     for (int i = 1; i < argc; ++i) {
1099         if (!stricmp(argv[i], "--threaded")) {
1100             threaded = true;
1101             continue;
1102         }
1103 
1104         if (!stricmp(argv[i], "--dump-all-pixels")) {
1105             dumpAllPixels = true;
1106             continue;
1107         }
1108 
1109         if (!stricmp(argv[i], "--pixel-tests")) {
1110             dumpPixels = true;
1111             continue;
1112         }
1113 
1114         if (!stricmp(argv[i], "--complex-text")) {
1115             forceComplexText = true;
1116             continue;
1117         }
1118 
1119         tests.append(argv[i]);
1120     }
1121 
1122     policyDelegate = new PolicyDelegate();
1123     sharedFrameLoadDelegate.adoptRef(new FrameLoadDelegate);
1124     sharedUIDelegate.adoptRef(new UIDelegate);
1125     sharedEditingDelegate.adoptRef(new EditingDelegate);
1126     sharedResourceLoadDelegate.adoptRef(new ResourceLoadDelegate);
1127 
1128     // FIXME - need to make DRT pass with Windows native controls <http://bugs.webkit.org/show_bug.cgi?id=25592>
1129     COMPtr<IWebPreferences> tmpPreferences;
1130     if (FAILED(WebKitCreateInstance(CLSID_WebPreferences, 0, IID_IWebPreferences, reinterpret_cast<void**>(&tmpPreferences))))
1131         return -1;
1132     COMPtr<IWebPreferences> standardPreferences;
1133     if (FAILED(tmpPreferences->standardPreferences(&standardPreferences)))
1134         return -1;
1135     COMPtr<IWebPreferencesPrivate> standardPreferencesPrivate;
1136     if (FAILED(standardPreferences->QueryInterface(&standardPreferencesPrivate)))
1137         return -1;
1138     standardPreferencesPrivate->setShouldPaintNativeControls(FALSE);
1139     standardPreferences->setJavaScriptEnabled(TRUE);
1140     standardPreferences->setDefaultFontSize(16);
1141 
1142     COMPtr<IWebView> webView(AdoptCOM, createWebViewAndOffscreenWindow(&webViewWindow));
1143     if (!webView)
1144         return -1;
1145 
1146     COMPtr<IWebIconDatabase> iconDatabase;
1147     COMPtr<IWebIconDatabase> tmpIconDatabase;
1148     if (FAILED(WebKitCreateInstance(CLSID_WebIconDatabase, 0, IID_IWebIconDatabase, (void**)&tmpIconDatabase)))
1149         return -1;
1150     if (FAILED(tmpIconDatabase->sharedIconDatabase(&iconDatabase)))
1151         return -1;
1152 
1153     if (FAILED(webView->mainFrame(&frame)))
1154         return -1;
1155 
1156 #if PLATFORM(CFNETWORK)
1157     RetainPtr<CFURLCacheRef> urlCache = sharedCFURLCache();
1158     CFURLCacheRemoveAllCachedResponses(urlCache.get());
1159 #endif
1160 
1161 #ifdef _DEBUG
1162     _CrtMemState entryToMainMemCheckpoint;
1163     if (leakChecking)
1164         _CrtMemCheckpoint(&entryToMainMemCheckpoint);
1165 #endif
1166 
1167     if (threaded)
1168         startJavaScriptThreads();
1169 
1170     if (tests.size() == 1 && !strcmp(tests[0], "-")) {
1171         char filenameBuffer[2048];
1172         printSeparators = true;
1173         while (fgets(filenameBuffer, sizeof(filenameBuffer), stdin)) {
1174             char* newLineCharacter = strchr(filenameBuffer, '\n');
1175             if (newLineCharacter)
1176                 *newLineCharacter = '\0';
1177 
1178             if (strlen(filenameBuffer) == 0)
1179                 continue;
1180 
1181             runTest(filenameBuffer);
1182         }
1183     } else {
1184         printSeparators = tests.size() > 1;
1185         for (int i = 0; i < tests.size(); i++)
1186             runTest(tests[i]);
1187     }
1188 
1189     if (threaded)
1190         stopJavaScriptThreads();
1191 
1192     delete policyDelegate;
1193     frame->Release();
1194 
1195 #ifdef _DEBUG
1196     if (leakChecking) {
1197         // dump leaks to stderr
1198         _CrtSetReportMode(_CRT_WARN, _CRTDBG_MODE_FILE);
1199         _CrtSetReportFile(_CRT_WARN, _CRTDBG_FILE_STDERR);
1200         _CrtMemDumpAllObjectsSince(&entryToMainMemCheckpoint);
1201     }
1202 #endif
1203 
1204     shutDownWebKit();
1205 
1206     return 0;
1207 }
1208