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