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