84dc6d73fe234db29b6ae0f701ccaccf0985c532
[WebKit-https.git] / WebKitTools / DumpRenderTree / win / DumpRenderTree.cpp
1 /*
2  * Copyright (C) 2005, 2006, 2007 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 "DumpRenderTree.h"
30
31 #include "EditingDelegate.h"
32 #include "FrameLoaderDelegate.h"
33 #include "LayoutTestController.h"
34 #include "PixelDumpSupport.h"
35 #include "PolicyDelegate.h"
36 #include "UIDelegate.h"
37 #include "WorkQueueItem.h"
38 #include "WorkQueue.h"
39 #include <wtf/Vector.h>
40 #include <WebCore/COMPtr.h>
41 #include <CoreFoundation/CoreFoundation.h>
42 #include <JavaScriptCore/JavaScriptCore.h>
43 #include <math.h>
44 #include <pthread.h>
45 #include <string>
46 #include <tchar.h>
47 #include <WebKit/DOMPrivate.h>
48 #include <WebKit/IWebFramePrivate.h>
49 #include <WebKit/IWebHistoryItem.h>
50 #include <WebKit/IWebHistoryItemPrivate.h>
51 #include <WebKit/IWebURLResponse.h>
52 #include <WebKit/IWebViewPrivate.h>
53 #include <WebKit/WebKit.h>
54 #include <fcntl.h>
55 #include <io.h>
56 #include <windows.h>
57 #include <stdio.h>
58
59 using std::wstring;
60
61 #ifdef _DEBUG
62 const LPWSTR TestPluginDir = L"TestNetscapePlugin_Debug";
63 #else
64 const LPWSTR TestPluginDir = L"TestNetscapePlugin";
65 #endif
66
67 static LPCWSTR fontsEnvironmentVariable = L"WEBKIT_TESTFONTS";
68 #define USE_MAC_FONTS
69
70 const LPCWSTR kDumpRenderTreeClassName = L"DumpRenderTreeWindow";
71
72 static bool dumpTree = true;
73 static bool dumpPixels;
74 static bool dumpAllPixels;
75 static bool printSeparators;
76 static bool leakChecking = false;
77 static bool timedOut = false;
78 static bool threaded = false;
79
80 static const char* currentTest;
81
82 volatile bool done;
83 // This is the topmost frame that is loading, during a given load, or nil when no load is 
84 // in progress.  Usually this is the same as the main frame, but not always.  In the case
85 // where a frameset is loaded, and then new content is loaded into one of the child frames,
86 // that child frame is the "topmost frame that is loading".
87 IWebFrame* topLoadingFrame;     // !nil iff a load is in progress
88 static COMPtr<IWebHistoryItem> prevTestBFItem;  // current b/f item at the end of the previous test
89 IWebPolicyDelegate* policyDelegate; 
90
91 IWebFrame* frame;
92 HWND webViewWindow;
93 static HWND hostWindow;
94
95 LayoutTestController* layoutTestController = 0;
96 CFRunLoopTimerRef waitToDumpWatchdog = 0; 
97
98 static const unsigned timeoutValue = 60000;
99 static const unsigned timeoutId = 10;
100
101 const unsigned maxViewWidth = 800;
102 const unsigned maxViewHeight = 600;
103
104 static LRESULT CALLBACK DumpRenderTreeWndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
105 {
106     switch (msg) {
107         case WM_TIMER:
108             // The test ran long enough to time out
109             timedOut = true;
110             PostQuitMessage(0);
111             return 0;
112             break;
113         default:
114             return DefWindowProc(hWnd, msg, wParam, lParam);
115     }
116 }
117
118 static const wstring& exePath()
119 {
120     static wstring path;
121     static bool initialized;
122
123     if (initialized)
124         return path;
125     initialized = true;
126
127     TCHAR buffer[MAX_PATH];
128     GetModuleFileName(GetModuleHandle(0), buffer, ARRAYSIZE(buffer));
129     path = buffer;
130     int lastSlash = path.rfind('\\');
131     if (lastSlash != -1 && lastSlash + 1 < path.length())
132         path = path.substr(0, lastSlash + 1);
133
134     return path;
135 }
136
137 static const wstring& fontsPath()
138 {
139     static wstring path;
140     static bool initialized;
141
142     if (initialized)
143         return path;
144     initialized = true;
145
146     DWORD size = GetEnvironmentVariable(fontsEnvironmentVariable, 0, 0);
147     Vector<TCHAR> buffer(size);
148     if (GetEnvironmentVariable(fontsEnvironmentVariable, buffer.data(), buffer.size())) {
149         path = buffer.data();
150         if (path[path.length() - 1] != '\\')
151             path.append(L"\\");
152         return path;
153     }
154
155     path = exePath() + TEXT("DumpRenderTree.resources\\");
156     return path;
157 }
158
159 #ifdef DEBUG_WEBKIT_HAS_SUFFIX
160 #define WEBKITDLL TEXT("WebKit_debug.dll")
161 #else
162 #define WEBKITDLL TEXT("WebKit.dll")
163 #endif
164
165 static void initialize(HMODULE hModule)
166 {
167     if (HMODULE webKitModule = LoadLibrary(WEBKITDLL))
168         if (FARPROC dllRegisterServer = GetProcAddress(webKitModule, "DllRegisterServer"))
169             dllRegisterServer();
170
171     // Init COM
172     OleInitialize(0);
173
174     static LPCTSTR fontsToInstall[] = {
175         TEXT("AHEM____.ttf"),
176         TEXT("Apple Chancery.ttf"),
177         TEXT("Courier Bold.ttf"),
178         TEXT("Courier.ttf"),
179         TEXT("Helvetica Bold.ttf"),
180         TEXT("Helvetica.ttf"),
181         TEXT("Helvetica Neue Bold Italic.ttf"),
182         TEXT("Helvetica Neue Bold.ttf"),
183         TEXT("Helvetica Neue Condensed Black.ttf"),
184         TEXT("Helvetica Neue Condensed Bold.ttf"),
185         TEXT("Helvetica Neue Italic.ttf"),
186         TEXT("Helvetica Neue Light Italic.ttf"),
187         TEXT("Helvetica Neue Light.ttf"),
188         TEXT("Helvetica Neue UltraLight Italic.ttf"),
189         TEXT("Helvetica Neue UltraLight.ttf"),
190         TEXT("Helvetica Neue.ttf"),
191         TEXT("Lucida Grande.ttf"),
192         TEXT("Lucida Grande Bold.ttf"),
193         TEXT("Monaco.ttf"),
194         TEXT("Papyrus.ttf"),
195         TEXT("Times Bold Italic.ttf"),
196         TEXT("Times Bold.ttf"),
197         TEXT("Times Italic.ttf"),
198         TEXT("Times Roman.ttf")
199     };
200
201     wstring resourcesPath = fontsPath();
202
203     COMPtr<IWebTextRenderer> textRenderer;
204     if (SUCCEEDED(CoCreateInstance(CLSID_WebTextRenderer, 0, CLSCTX_ALL, IID_IWebTextRenderer, (void**)&textRenderer)))
205         for (int i = 0; i < ARRAYSIZE(fontsToInstall); ++i)
206             textRenderer->registerPrivateFont(wstring(resourcesPath + fontsToInstall[i]).c_str());
207
208     // Register a host window
209     WNDCLASSEX wcex;
210
211     wcex.cbSize = sizeof(WNDCLASSEX);
212
213     wcex.style         = CS_HREDRAW | CS_VREDRAW;
214     wcex.lpfnWndProc   = DumpRenderTreeWndProc;
215     wcex.cbClsExtra    = 0;
216     wcex.cbWndExtra    = 0;
217     wcex.hInstance     = hModule;
218     wcex.hIcon         = 0;
219     wcex.hCursor       = LoadCursor(0, IDC_ARROW);
220     wcex.hbrBackground = 0;
221     wcex.lpszMenuName  = 0;
222     wcex.lpszClassName = kDumpRenderTreeClassName;
223     wcex.hIconSm       = 0;
224
225     RegisterClassEx(&wcex);
226
227     hostWindow = CreateWindowEx(WS_EX_TOOLWINDOW, kDumpRenderTreeClassName, TEXT("DumpRenderTree"), WS_POPUP,
228       -maxViewWidth, -maxViewHeight, maxViewWidth, maxViewHeight, 0, 0, hModule, 0);
229 }
230
231 void displayWebView()
232 {
233     ::InvalidateRect(webViewWindow, 0, TRUE);
234     ::UpdateWindow(webViewWindow);
235 }
236
237 void dumpFrameScrollPosition(IWebFrame* frame)
238 {
239     if (!frame)
240         return;
241
242     COMPtr<IWebFramePrivate> framePrivate;
243     if (FAILED(frame->QueryInterface(&framePrivate)))
244         return;
245
246     SIZE scrollPosition;
247     if (FAILED(framePrivate->scrollOffset(&scrollPosition)))
248         return;
249
250     if (abs(scrollPosition.cx) > 0.00000001 || abs(scrollPosition.cy) > 0.00000001) {
251         COMPtr<IWebFrame> parent;
252         if (FAILED(frame->parentFrame(&parent)))
253             return;
254         if (parent) {
255             BSTR name;
256             if (FAILED(frame->name(&name)))
257                 return;
258             printf("frame '%S' ", name ? name : L"");
259             SysFreeString(name);
260         }
261         printf("scrolled to %.f,%.f\n", (double)scrollPosition.cx, (double)scrollPosition.cy);
262     }
263
264     if (::layoutTestController->dumpChildFrameScrollPositions()) {
265         COMPtr<IEnumVARIANT> enumKids;
266         if (FAILED(frame->childFrames(&enumKids)))
267             return;
268         VARIANT var;
269         VariantInit(&var);
270         while (enumKids->Next(1, &var, 0) == S_OK) {
271             ASSERT(V_VT(&var) == VT_UNKNOWN);
272             COMPtr<IWebFrame> framePtr;
273             V_UNKNOWN(&var)->QueryInterface(IID_IWebFrame, (void**)&framePtr);
274             dumpFrameScrollPosition(framePtr.get());
275             VariantClear(&var);
276         }
277     }
278 }
279
280 static wstring dumpFramesAsText(IWebFrame* frame)
281 {
282     if (!frame)
283         return L"";
284
285     COMPtr<IDOMDocument> document;
286     if (FAILED(frame->DOMDocument(&document)))
287         return L"";
288
289     COMPtr<IDOMElement> documentElement;
290     if (FAILED(document->documentElement(&documentElement)))
291         return L"";
292
293     wstring result;
294
295     // Add header for all but the main frame.
296     COMPtr<IWebFrame> parent;
297     if (FAILED(frame->parentFrame(&parent)))
298         return L"";
299     if (parent) {
300         BSTR name = L"";
301         if (FAILED(frame->name(&name)))
302             return L"";
303
304         result.append(L"\n--------\nFrame: '");
305         result.append(name ? name : L"");
306         result.append(L"'\n--------\n");
307
308         SysFreeString(name);
309     }
310
311     BSTR innerText = 0;
312     COMPtr<IDOMElementPrivate> docPrivate;
313     if (SUCCEEDED(documentElement->QueryInterface(&docPrivate)))
314         docPrivate->innerText(&innerText);
315
316     result.append(innerText ? innerText : L"");
317     result.append(L"\n");
318
319     SysFreeString(innerText);
320
321     if (::layoutTestController->dumpChildFramesAsText()) {
322         COMPtr<IEnumVARIANT> enumKids;
323         if (FAILED(frame->childFrames(&enumKids)))
324             return L"";
325         VARIANT var;
326         VariantInit(&var);
327         while (enumKids->Next(1, &var, 0) == S_OK) {
328             ASSERT(V_VT(&var) == VT_UNKNOWN);
329             COMPtr<IWebFrame> framePtr;
330             V_UNKNOWN(&var)->QueryInterface(IID_IWebFrame, (void**)&framePtr);
331             result.append(dumpFramesAsText(framePtr.get()));
332             VariantClear(&var);
333         }
334     }
335
336     return result;
337 }
338
339 static int compareHistoryItems(const void* item1, const void* item2)
340 {
341     COMPtr<IWebHistoryItemPrivate> itemA;
342     if (FAILED((*(COMPtr<IUnknown>*)item1)->QueryInterface(&itemA)))
343         return 0;
344
345     COMPtr<IWebHistoryItemPrivate> itemB;
346     if (FAILED((*(COMPtr<IUnknown>*)item2)->QueryInterface(&itemB)))
347         return 0;
348
349     BSTR targetA;
350     if (FAILED(itemA->target(&targetA)))
351         return 0;
352
353     BSTR targetB;
354     if (FAILED(itemB->target(&targetB))) {
355         SysFreeString(targetA);
356         return 0;
357     }
358
359     int result = wcsicmp(wstring(targetA, SysStringLen(targetA)).c_str(), wstring(targetB, SysStringLen(targetB)).c_str());
360     SysFreeString(targetA);
361     SysFreeString(targetB);
362     return result;
363 }
364
365 static void dumpHistoryItem(IWebHistoryItem* item, int indent, bool current)
366 {
367     assert(item);
368
369     int start = 0;
370     if (current) {
371         printf("curr->");
372         start = 6;
373     }
374     for (int i = start; i < indent; i++)
375         putchar(' ');
376
377     BSTR url;
378     if (FAILED(item->URLString(&url)))
379         return;
380     printf("%S", url ? url : L"");
381     SysFreeString(url);
382
383     COMPtr<IWebHistoryItemPrivate> itemPrivate;
384     if (FAILED(item->QueryInterface(&itemPrivate)))
385         return;
386
387     BSTR target;
388     if (FAILED(itemPrivate->target(&target)))
389         return;
390     if (SysStringLen(target))
391         printf(" (in frame \"%S\")", target);
392     SysFreeString(target);
393     BOOL isTargetItem = FALSE;
394     if (FAILED(itemPrivate->isTargetItem(&isTargetItem)))
395         return;
396     if (isTargetItem)
397         printf("  **nav target**");
398     putchar('\n');
399
400     unsigned kidsCount;
401     SAFEARRAY* arrPtr;
402     if (FAILED(itemPrivate->children(&kidsCount, &arrPtr)) || !kidsCount)
403         return;
404
405     Vector<COMPtr<IUnknown> > kidsVector;
406
407     LONG lowerBound;
408     if (FAILED(::SafeArrayGetLBound(arrPtr, 1, &lowerBound)))
409         goto exit;
410
411     LONG upperBound;
412     if (FAILED(::SafeArrayGetUBound(arrPtr, 1, &upperBound)))
413         goto exit;
414
415     LONG length = upperBound - lowerBound + 1;
416     if (!length)
417         goto exit;
418     ASSERT(length == kidsCount);
419
420     IUnknown** safeArrayData;
421     if (FAILED(::SafeArrayAccessData(arrPtr, (void**)&safeArrayData)))
422         goto exit;
423
424     for (int i = 0; i < length; ++i)
425         kidsVector.append(safeArrayData[i]);
426     ::SafeArrayUnaccessData(arrPtr);
427
428     // must sort to eliminate arbitrary result ordering which defeats reproducible testing
429     qsort(kidsVector.data(), kidsCount, sizeof(kidsVector[0]), compareHistoryItems);
430
431     for (unsigned i = 0; i < kidsCount; ++i) {
432         COMPtr<IWebHistoryItem> item;
433         kidsVector[i]->QueryInterface(&item);
434         dumpHistoryItem(item.get(), indent + 4, false);
435     }
436
437 exit:
438     if (arrPtr && SUCCEEDED(::SafeArrayUnlock(arrPtr)))
439         ::SafeArrayDestroy(arrPtr);
440 }
441
442 static void dumpBackForwardList(IWebFrame* frame)
443 {
444     assert(frame);
445
446     printf("\n============== Back Forward List ==============\n");
447     COMPtr<IWebView> webView;
448     if (FAILED(frame->webView(&webView)))
449         return;
450
451     COMPtr<IWebBackForwardList> bfList;
452     if (FAILED(webView->backForwardList(&bfList)))
453         return;
454
455     // Print out all items in the list after prevTestBFItem, which was from the previous test
456     // Gather items from the end of the list, the print them out from oldest to newest
457
458     Vector<COMPtr<IUnknown> > itemsToPrint;
459
460     int forwardListCount;
461     if (FAILED(bfList->forwardListCount(&forwardListCount)))
462         return;
463
464     for (int i = forwardListCount; i > 0; --i) {
465         COMPtr<IWebHistoryItem> item;
466         if (FAILED(bfList->itemAtIndex(i, &item)))
467             return;
468         // something is wrong if the item from the last test is in the forward part of the b/f list
469         assert(item != prevTestBFItem);
470         COMPtr<IUnknown> itemUnknown;
471         item->QueryInterface(&itemUnknown);
472         itemsToPrint.append(itemUnknown);
473     }
474     
475     COMPtr<IWebHistoryItem> currentItem;
476     if (FAILED(bfList->currentItem(&currentItem)))
477         return;
478
479     assert(currentItem != prevTestBFItem);
480     COMPtr<IUnknown> currentItemUnknown;
481     currentItem->QueryInterface(&currentItemUnknown);
482     itemsToPrint.append(currentItemUnknown);
483     int currentItemIndex = itemsToPrint.size() - 1;
484
485     int backListCount;
486     if (FAILED(bfList->backListCount(&backListCount)))
487         return;
488
489     for (int i = -1; i >= -backListCount; --i) {
490         COMPtr<IWebHistoryItem> item;
491         if (FAILED(bfList->itemAtIndex(i, &item)))
492             return;
493         if (item == prevTestBFItem)
494             break;
495         COMPtr<IUnknown> itemUnknown;
496         item->QueryInterface(&itemUnknown);
497         itemsToPrint.append(itemUnknown);
498     }
499
500     for (int i = itemsToPrint.size() - 1; i >= 0; --i) {
501         COMPtr<IWebHistoryItem> historyItemToPrint;
502         itemsToPrint[i]->QueryInterface(&historyItemToPrint);
503         dumpHistoryItem(historyItemToPrint.get(), 8, i == currentItemIndex);
504     }
505
506     printf("===============================================\n");
507 }
508
509 void dump()
510 {
511     COMPtr<IWebDataSource> dataSource;
512     if (SUCCEEDED(frame->dataSource(&dataSource))) {
513         COMPtr<IWebURLResponse> response;
514         if (SUCCEEDED(dataSource->response(&response)) && response) {
515             BSTR mimeType;
516             if (SUCCEEDED(response->MIMEType(&mimeType)))
517                 ::layoutTestController->setDumpAsText(::layoutTestController->dumpAsText() | !_tcscmp(mimeType, TEXT("text/plain")));
518             SysFreeString(mimeType);
519         }
520     }
521
522     BSTR resultString = 0;
523
524     if (dumpTree) {
525         if (::layoutTestController->dumpAsText()) {
526             ::InvalidateRect(webViewWindow, 0, TRUE);
527             ::SendMessage(webViewWindow, WM_PAINT, 0, 0);
528             wstring result = dumpFramesAsText(frame);
529             resultString = SysAllocStringLen(result.data(), result.size());
530         } else {
531             bool isSVGW3CTest = strstr(currentTest, "svg\\W3C-SVG-1.1");
532             unsigned width;
533             unsigned height;
534             if (isSVGW3CTest) {
535                 width = 480;
536                 height = 360;
537             } else {
538                 width = maxViewWidth;
539                 height = maxViewHeight;
540             }
541
542             ::SetWindowPos(webViewWindow, 0, 0, 0, width, height, SWP_NOMOVE);
543             ::InvalidateRect(webViewWindow, 0, TRUE);
544             ::SendMessage(webViewWindow, WM_PAINT, 0, 0);
545
546             COMPtr<IWebFramePrivate> framePrivate;
547             if (FAILED(frame->QueryInterface(&framePrivate)))
548                 goto fail;
549             framePrivate->renderTreeAsExternalRepresentation(&resultString);
550         }
551         
552         if (!resultString)
553             printf("ERROR: nil result from %s", ::layoutTestController->dumpAsText() ? "IDOMElement::innerText" : "IFrameViewPrivate::renderTreeAsExternalRepresentation");
554         else {
555             unsigned stringLength = SysStringLen(resultString);
556             int bufferSize = ::WideCharToMultiByte(CP_UTF8, 0, resultString, stringLength, 0, 0, 0, 0);
557             char* buffer = (char*)malloc(bufferSize + 1);
558             int result = ::WideCharToMultiByte(CP_UTF8, 0, resultString, stringLength, buffer, bufferSize + 1, 0, 0);
559             buffer[bufferSize] = '\0';
560             printf("%s", buffer);
561             free(buffer);
562             if (!::layoutTestController->dumpAsText())
563                 dumpFrameScrollPosition(frame);
564         }
565         if (::layoutTestController->dumpBackForwardList())
566             dumpBackForwardList(frame);
567     }
568
569     if (printSeparators)
570         puts("#EOF");
571
572     if (dumpPixels) {
573         if (layoutTestController->dumpAsText() || layoutTestController->dumpDOMAsWebArchive() || layoutTestController->dumpSourceAsWebArchive())
574             printf("#EOF\n");
575         else
576             dumpWebViewAsPixelsAndCompareWithExpected(currentTest, dumpAllPixels);
577     }
578
579 fail:
580     SysFreeString(resultString);
581     // This will exit from our message loop
582     PostQuitMessage(0);
583     done = true;
584 }
585
586 static void runTest(const char* pathOrURL)
587 {
588     static BSTR methodBStr = SysAllocString(TEXT("GET"));
589
590     BSTR urlBStr;
591  
592     CFStringRef str = CFStringCreateWithCString(0, pathOrURL, kCFStringEncodingWindowsLatin1);
593     CFURLRef url = CFURLCreateWithString(0, str, 0);
594
595     if (!url)
596         url = CFURLCreateWithFileSystemPath(0, str, kCFURLWindowsPathStyle, false);
597
598     CFRelease(str);
599
600     str = CFURLGetString(url);
601
602     CFIndex length = CFStringGetLength(str);
603     UniChar* buffer = new UniChar[length];
604
605     CFStringGetCharacters(str, CFRangeMake(0, length), buffer);
606     urlBStr = SysAllocStringLen((OLECHAR*)buffer, length);
607     delete[] buffer;
608
609     CFRelease(url);
610
611     currentTest = pathOrURL;
612
613     ::layoutTestController = new LayoutTestController(false, false);
614     done = false;
615     topLoadingFrame = 0;
616     timedOut = false;
617
618     COMPtr<IWebHistory> history(Create, CLSID_WebHistory);
619     if (history)
620         history->setOptionalSharedHistory(0);
621
622     prevTestBFItem = 0;
623     COMPtr<IWebView> webView;
624     if (SUCCEEDED(frame->webView(&webView))) {
625         COMPtr<IWebBackForwardList> bfList;
626         if (SUCCEEDED(webView->backForwardList(&bfList)))
627             bfList->currentItem(&prevTestBFItem);
628
629         webView->setPolicyDelegate(NULL);
630
631         COMPtr<IWebIBActions> webIBActions;
632         if (SUCCEEDED(webView->QueryInterface(IID_IWebIBActions, (void**)&webIBActions)))
633             webIBActions->makeTextStandardSize(0);
634
635         COMPtr<IWebPreferences> preferences;
636         if (SUCCEEDED(webView->preferences(&preferences)))
637             preferences->setPrivateBrowsingEnabled(FALSE);
638     }
639
640     WorkQueue::shared()->clear();
641     WorkQueue::shared()->setFrozen(false);
642
643     // Set the test timeout timer
644     SetTimer(hostWindow, timeoutId, timeoutValue, 0);
645
646     COMPtr<IWebMutableURLRequest> request;
647     HRESULT hr = CoCreateInstance(CLSID_WebMutableURLRequest, 0, CLSCTX_ALL, IID_IWebMutableURLRequest, (void**)&request);
648     if (FAILED(hr))
649         goto exit;
650
651     request->initWithURL(urlBStr, WebURLRequestUseProtocolCachePolicy, 0);
652
653     request->setHTTPMethod(methodBStr);
654     frame->loadRequest(request.get());
655
656     MSG msg;
657     while (GetMessage(&msg, 0, 0, 0)) {
658         TranslateMessage(&msg);
659         DispatchMessage(&msg);
660     }
661     KillTimer(hostWindow, timeoutId);
662
663     if (timedOut) {
664         fprintf(stderr, "ERROR: Timed out running %s\n", pathOrURL);
665         printf("ERROR: Timed out loading page\n");
666
667         if (printSeparators)
668             puts("#EOF");
669     }
670 exit:
671     SysFreeString(urlBStr);
672     delete ::layoutTestController;
673
674     return;
675 }
676
677 static void initializePreferences(IWebPreferences* preferences)
678 {
679 #ifdef USE_MAC_FONTS
680     BSTR standardFamily = SysAllocString(TEXT("Times"));
681     BSTR fixedFamily = SysAllocString(TEXT("Courier"));
682     BSTR sansSerifFamily = SysAllocString(TEXT("Helvetica"));
683     BSTR cursiveFamily = SysAllocString(TEXT("Apple Chancery"));
684     BSTR fantasyFamily = SysAllocString(TEXT("Papyrus"));
685 #else
686     BSTR standardFamily = SysAllocString(TEXT("Times New Roman"));
687     BSTR fixedFamily = SysAllocString(TEXT("Courier New"));
688     BSTR sansSerifFamily = SysAllocString(TEXT("Arial"));
689     BSTR cursiveFamily = SysAllocString(TEXT("Comic Sans MS")); // Not actually cursive, but it's what IE and Firefox use.
690     BSTR fantasyFamily = SysAllocString(TEXT("Times New Roman"));
691 #endif
692
693     preferences->setStandardFontFamily(standardFamily);
694     preferences->setFixedFontFamily(fixedFamily);
695     preferences->setSerifFontFamily(standardFamily);
696     preferences->setSansSerifFontFamily(sansSerifFamily);
697     preferences->setCursiveFontFamily(cursiveFamily);
698     preferences->setFantasyFontFamily(fantasyFamily);
699
700     preferences->setAutosaves(FALSE);
701     preferences->setJavaEnabled(FALSE);
702     preferences->setPlugInsEnabled(TRUE);
703     preferences->setDOMPasteAllowed(TRUE);
704     preferences->setEditableLinkBehavior(WebKitEditableLinkOnlyLiveWithShiftKey);
705
706     SysFreeString(standardFamily);
707     SysFreeString(fixedFamily);
708     SysFreeString(sansSerifFamily);
709     SysFreeString(cursiveFamily);
710     SysFreeString(fantasyFamily);
711 }
712
713 static Boolean pthreadEqualCallback(const void* value1, const void* value2)
714 {
715     return (Boolean)pthread_equal(*(pthread_t*)value1, *(pthread_t*)value2);
716 }
717
718 static CFDictionaryKeyCallBacks pthreadKeyCallbacks = { 0, 0, 0, 0, pthreadEqualCallback, 0 };
719
720 static pthread_mutex_t javaScriptThreadsMutex = PTHREAD_MUTEX_INITIALIZER;
721 static bool javaScriptThreadsShouldTerminate;
722
723 static const int javaScriptThreadsCount = 4;
724 static CFMutableDictionaryRef javaScriptThreads()
725 {
726     assert(pthread_mutex_trylock(&javaScriptThreadsMutex) == EBUSY);
727     static CFMutableDictionaryRef staticJavaScriptThreads;
728     if (!staticJavaScriptThreads)
729         staticJavaScriptThreads = CFDictionaryCreateMutable(0, 0, &pthreadKeyCallbacks, 0);
730     return staticJavaScriptThreads;
731 }
732
733 // Loops forever, running a script and randomly respawning, until 
734 // javaScriptThreadsShouldTerminate becomes true.
735 void* runJavaScriptThread(void* arg)
736 {
737     const char* const script =
738     " \
739     var array = []; \
740     for (var i = 0; i < 10; i++) { \
741         array.push(String(i)); \
742     } \
743     ";
744
745     while (true) {
746         JSGlobalContextRef ctx = JSGlobalContextCreate(0);
747         JSStringRef scriptRef = JSStringCreateWithUTF8CString(script);
748
749         JSValueRef exception = 0;
750         JSEvaluateScript(ctx, scriptRef, 0, 0, 0, &exception);
751         assert(!exception);
752         
753         JSGlobalContextRelease(ctx);
754         JSStringRelease(scriptRef);
755         
756         JSGarbageCollect(ctx);
757
758         pthread_mutex_lock(&javaScriptThreadsMutex);
759
760         // Check for cancellation.
761         if (javaScriptThreadsShouldTerminate) {
762             pthread_mutex_unlock(&javaScriptThreadsMutex);
763             return 0;
764         }
765
766         // Respawn probabilistically.
767         if (rand() % 5 == 0) {
768             pthread_t pthread;
769             pthread_create(&pthread, 0, &runJavaScriptThread, 0);
770             pthread_detach(pthread);
771
772             pthread_t self = pthread_self();
773             CFDictionaryRemoveValue(javaScriptThreads(), self.p);
774             CFDictionaryAddValue(javaScriptThreads(), pthread.p, 0);
775
776             pthread_mutex_unlock(&javaScriptThreadsMutex);
777             return 0;
778         }
779
780         pthread_mutex_unlock(&javaScriptThreadsMutex);
781     }
782 }
783
784 static void startJavaScriptThreads(void)
785 {
786     pthread_mutex_lock(&javaScriptThreadsMutex);
787
788     for (int i = 0; i < javaScriptThreadsCount; i++) {
789         pthread_t pthread;
790         pthread_create(&pthread, 0, &runJavaScriptThread, 0);
791         pthread_detach(pthread);
792         CFDictionaryAddValue(javaScriptThreads(), pthread.p, 0);
793     }
794
795     pthread_mutex_unlock(&javaScriptThreadsMutex);
796 }
797
798 static void stopJavaScriptThreads(void)
799 {
800     pthread_mutex_lock(&javaScriptThreadsMutex);
801
802     javaScriptThreadsShouldTerminate = true;
803
804     pthread_t* pthreads[javaScriptThreadsCount] = {0};
805     int threadDictCount = CFDictionaryGetCount(javaScriptThreads());
806     assert(threadDictCount == javaScriptThreadsCount);
807     CFDictionaryGetKeysAndValues(javaScriptThreads(), (const void**)pthreads, 0);
808
809     pthread_mutex_unlock(&javaScriptThreadsMutex);
810
811     for (int i = 0; i < javaScriptThreadsCount; i++) {
812         pthread_t* pthread = pthreads[i];
813         pthread_join(*pthread, 0);
814         free(pthread);
815     }
816 }
817
818 int main(int argc, char* argv[])
819 {
820     leakChecking = false;
821
822     _setmode(1, _O_BINARY);
823
824     initialize(GetModuleHandle(0));
825
826     Vector<const char*> tests;
827
828     for (int i = 1; i < argc; ++i) {
829         if (!stricmp(argv[i], "--threaded")) {
830             threaded = true;
831             continue;
832         }
833
834         if (!stricmp(argv[i], "--dump-all-pixels")) {
835             dumpAllPixels = true;
836             continue;
837         }
838
839         if (!stricmp(argv[i], "--pixel-tests")) {
840             dumpPixels = true;
841             continue;
842         }
843
844         tests.append(argv[i]);
845     }
846
847     COMPtr<IWebView> webView;
848     HRESULT hr = CoCreateInstance(CLSID_WebView, 0, CLSCTX_ALL, IID_IWebView, (void**)&webView);
849     if (FAILED(hr)) {
850         fprintf(stderr, "Failed to create CLSID_WebView instance, error 0x%x\n", hr);
851         return -1;
852     }
853
854     if (FAILED(webView->setHostWindow((OLE_HANDLE)(ULONG64)hostWindow)))
855         return -1;
856
857     RECT clientRect;
858     clientRect.bottom = clientRect.left = clientRect.top = clientRect.right = 0;
859     BSTR groupName = SysAllocString(L"org.webkit.DumpRenderTree");
860     bool failed = FAILED(webView->initWithFrame(clientRect, 0, groupName));
861     SysFreeString(groupName);
862     if (failed)
863         return -1;
864
865     COMPtr<IWebViewPrivate> viewPrivate;
866     if (FAILED(webView->QueryInterface(&viewPrivate)))
867         return -1;
868     webView->Release();
869
870
871     BSTR pluginPath = SysAllocStringLen(0, exePath().length() + _tcslen(TestPluginDir));
872     _tcscpy(pluginPath, exePath().c_str());
873     _tcscat(pluginPath, TestPluginDir);
874     failed = FAILED(viewPrivate->addAdditionalPluginPath(pluginPath));
875     SysFreeString(pluginPath);
876     if (failed)
877         return -1;
878
879     if (FAILED(viewPrivate->viewWindow((OLE_HANDLE*)&webViewWindow)))
880         return -1;
881
882     SetWindowPos(webViewWindow, 0, 0, 0, maxViewWidth, maxViewHeight, 0);
883     ShowWindow(hostWindow, SW_SHOW);
884
885     COMPtr<FrameLoadDelegate> frameLoadDelegate;
886     frameLoadDelegate.adoptRef(new FrameLoadDelegate);
887     if (FAILED(webView->setFrameLoadDelegate(frameLoadDelegate.get())))
888         return -1;
889
890     policyDelegate = new PolicyDelegate();
891
892     COMPtr<UIDelegate> uiDelegate;
893     uiDelegate.adoptRef(new UIDelegate);
894     if (FAILED(webView->setUIDelegate(uiDelegate.get())))
895         return -1;
896
897     COMPtr<IWebViewEditing> viewEditing;
898     if (FAILED(webView->QueryInterface(&viewEditing)))
899         return -1;
900     webView->Release();
901
902     COMPtr<EditingDelegate> editingDelegate;
903     editingDelegate.adoptRef(new EditingDelegate);
904     if (FAILED(viewEditing->setEditingDelegate(editingDelegate.get())))
905         return -1;
906
907     COMPtr<IWebPreferences> preferences;
908     if (FAILED(webView->preferences(&preferences)))
909         return -1;
910
911     initializePreferences(preferences.get());
912
913     COMPtr<IWebIconDatabase> iconDatabase;
914     COMPtr<IWebIconDatabase> tmpIconDatabase;
915     if (FAILED(CoCreateInstance(CLSID_WebIconDatabase, 0, CLSCTX_ALL, IID_IWebIconDatabase, (void**)&tmpIconDatabase)))
916         return -1;
917     if (FAILED(tmpIconDatabase->sharedIconDatabase(&iconDatabase)))
918         return -1;
919         
920     if (FAILED(webView->mainFrame(&frame)))
921         return -1;
922
923 #ifdef _DEBUG
924     _CrtMemState entryToMainMemCheckpoint;
925     if (leakChecking)
926         _CrtMemCheckpoint(&entryToMainMemCheckpoint);
927 #endif
928
929     if (threaded)
930         startJavaScriptThreads();
931
932     if (tests.size() == 1 && !strcmp(tests[0], "-")) {
933         char filenameBuffer[2048];
934         printSeparators = true;
935         while (fgets(filenameBuffer, sizeof(filenameBuffer), stdin)) {
936             char* newLineCharacter = strchr(filenameBuffer, '\n');
937             if (newLineCharacter)
938                 *newLineCharacter = '\0';
939             
940             if (strlen(filenameBuffer) == 0)
941                 continue;
942
943             runTest(filenameBuffer);
944             fflush(stdout);
945         }
946     } else {
947         printSeparators = tests.size() > 1;
948         for (int i = 0; i < tests.size(); i++)
949             runTest(tests[i]);
950     }
951
952     if (threaded)
953         stopJavaScriptThreads();
954     
955     delete policyDelegate;
956     frame->Release();
957
958 #ifdef _DEBUG
959     if (leakChecking) {
960         // dump leaks to stderr
961         _CrtSetReportMode(_CRT_WARN, _CRTDBG_MODE_FILE);
962         _CrtSetReportFile(_CRT_WARN, _CRTDBG_FILE_STDERR);
963         _CrtMemDumpAllObjectsSince(&entryToMainMemCheckpoint);
964     }
965 #endif
966
967     return 0;
968 }