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