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