2011-06-29 Yuta Kitamura <yutak@chromium.org>
[WebKit-https.git] / 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 #else
825     static BSTR standardFamily = SysAllocString(TEXT("Times New Roman"));
826     static BSTR fixedFamily = SysAllocString(TEXT("Courier New"));
827     static BSTR sansSerifFamily = SysAllocString(TEXT("Arial"));
828     static BSTR cursiveFamily = SysAllocString(TEXT("Comic Sans MS")); // Not actually cursive, but it's what IE and Firefox use.
829     static BSTR fantasyFamily = SysAllocString(TEXT("Times New Roman"));
830 #endif
831
832     preferences->setStandardFontFamily(standardFamily);
833     preferences->setFixedFontFamily(fixedFamily);
834     preferences->setSerifFontFamily(standardFamily);
835     preferences->setSansSerifFontFamily(sansSerifFamily);
836     preferences->setCursiveFontFamily(cursiveFamily);
837     preferences->setFantasyFontFamily(fantasyFamily);
838
839     preferences->setAutosaves(FALSE);
840     preferences->setDefaultFontSize(16);
841     preferences->setDefaultFixedFontSize(13);
842     preferences->setMinimumFontSize(0);
843     preferences->setJavaEnabled(FALSE);
844     preferences->setPlugInsEnabled(TRUE);
845     preferences->setDOMPasteAllowed(TRUE);
846     preferences->setEditableLinkBehavior(WebKitEditableLinkOnlyLiveWithShiftKey);
847     preferences->setFontSmoothing(FontSmoothingTypeStandard);
848     preferences->setUsesPageCache(FALSE);
849     preferences->setPrivateBrowsingEnabled(FALSE);
850     preferences->setJavaScriptCanOpenWindowsAutomatically(TRUE);
851     preferences->setJavaScriptEnabled(TRUE);
852     preferences->setTabsToLinks(FALSE);
853     preferences->setShouldPrintBackgrounds(TRUE);
854     preferences->setLoadsImagesAutomatically(TRUE);
855     preferences->setEditingBehavior(WebKitEditingWinBehavior);
856
857     if (persistentUserStyleSheetLocation) {
858         Vector<wchar_t> urlCharacters(CFStringGetLength(persistentUserStyleSheetLocation.get()));
859         CFStringGetCharacters(persistentUserStyleSheetLocation.get(), CFRangeMake(0, CFStringGetLength(persistentUserStyleSheetLocation.get())), (UniChar *)urlCharacters.data());
860         BSTR url = SysAllocStringLen(urlCharacters.data(), urlCharacters.size());
861         preferences->setUserStyleSheetLocation(url);
862         SysFreeString(url);
863         preferences->setUserStyleSheetEnabled(TRUE);
864     } else
865         preferences->setUserStyleSheetEnabled(FALSE);
866
867     COMPtr<IWebPreferencesPrivate> prefsPrivate(Query, preferences);
868     if (prefsPrivate) {
869         prefsPrivate->setAllowUniversalAccessFromFileURLs(TRUE);
870         prefsPrivate->setAllowFileAccessFromFileURLs(TRUE);
871         prefsPrivate->setAuthorAndUserStylesEnabled(TRUE);
872         prefsPrivate->setDeveloperExtrasEnabled(FALSE);
873         prefsPrivate->setExperimentalNotificationsEnabled(TRUE);
874         prefsPrivate->setShouldPaintNativeControls(FALSE); // FIXME - need to make DRT pass with Windows native controls <http://bugs.webkit.org/show_bug.cgi?id=25592>
875         prefsPrivate->setJavaScriptCanAccessClipboard(TRUE);
876         prefsPrivate->setXSSAuditorEnabled(FALSE);
877         prefsPrivate->setFrameFlatteningEnabled(FALSE);
878         prefsPrivate->setOfflineWebApplicationCacheEnabled(TRUE);
879         prefsPrivate->setLoadsSiteIconsIgnoringImageLoadingPreference(FALSE);
880         prefsPrivate->setHixie76WebSocketProtocolEnabled(TRUE);
881     }
882     setAlwaysAcceptCookies(false);
883
884     setlocale(LC_ALL, "");
885 }
886
887 static void resetWebViewToConsistentStateBeforeTesting()
888 {
889     COMPtr<IWebView> webView;
890     if (FAILED(frame->webView(&webView))) 
891         return;
892
893     webView->setPolicyDelegate(0);
894     policyDelegate->setPermissive(false);
895     policyDelegate->setControllerToNotifyDone(0);
896
897     COMPtr<IWebIBActions> webIBActions(Query, webView);
898     if (webIBActions) {
899         webIBActions->makeTextStandardSize(0);
900         webIBActions->resetPageZoom(0);
901     }
902
903
904     COMPtr<IWebPreferences> preferences;
905     if (SUCCEEDED(webView->preferences(&preferences)))
906         resetDefaultsToConsistentValues(preferences.get());
907
908     COMPtr<IWebViewEditing> viewEditing;
909     if (SUCCEEDED(webView->QueryInterface(&viewEditing)))
910         viewEditing->setSmartInsertDeleteEnabled(TRUE);
911
912     COMPtr<IWebViewPrivate> webViewPrivate(Query, webView);
913     if (!webViewPrivate)
914         return;
915
916     double minimumInterval = 0;
917     if (SUCCEEDED(webViewPrivate->defaultMinimumTimerInterval(&minimumInterval)))
918         webViewPrivate->setMinimumTimerInterval(minimumInterval);
919
920     COMPtr<IWebInspector> inspector;
921     if (SUCCEEDED(webViewPrivate->inspector(&inspector)))
922         inspector->setJavaScriptProfilingEnabled(FALSE);
923
924     HWND viewWindow;
925     if (SUCCEEDED(webViewPrivate->viewWindow(reinterpret_cast<OLE_HANDLE*>(&viewWindow))) && viewWindow)
926         SetFocus(viewWindow);
927
928     webViewPrivate->clearMainFrameName();
929     webViewPrivate->resetOriginAccessWhitelists();
930
931     BSTR groupName;
932     if (SUCCEEDED(webView->groupName(&groupName))) {
933         webViewPrivate->removeAllUserContentFromGroup(groupName);
934         SysFreeString(groupName);
935     }
936
937     sharedUIDelegate->resetUndoManager();
938
939     sharedFrameLoadDelegate->resetToConsistentState();
940
941     COMPtr<IWebFramePrivate> framePrivate;
942     if (SUCCEEDED(frame->QueryInterface(&framePrivate)))
943         framePrivate->clearOpener();
944 }
945
946 static void runTest(const string& testPathOrURL)
947 {
948     static BSTR methodBStr = SysAllocString(TEXT("GET"));
949
950     // Look for "'" as a separator between the path or URL, and the pixel dump hash that follows.
951     string pathOrURL(testPathOrURL);
952     string expectedPixelHash;
953     
954     size_t separatorPos = pathOrURL.find("'");
955     if (separatorPos != string::npos) {
956         pathOrURL = string(testPathOrURL, 0, separatorPos);
957         expectedPixelHash = string(testPathOrURL, separatorPos + 1);
958     }
959     
960     BSTR urlBStr;
961  
962     CFStringRef str = CFStringCreateWithCString(0, pathOrURL.c_str(), kCFStringEncodingWindowsLatin1);
963     CFURLRef url = CFURLCreateWithString(0, str, 0);
964
965     if (!url)
966         url = CFURLCreateWithFileSystemPath(0, str, kCFURLWindowsPathStyle, false);
967
968     CFRelease(str);
969
970     str = CFURLGetString(url);
971
972     CFIndex length = CFStringGetLength(str);
973     UniChar* buffer = new UniChar[length];
974
975     CFStringGetCharacters(str, CFRangeMake(0, length), buffer);
976     urlBStr = SysAllocStringLen((OLECHAR*)buffer, length);
977     delete[] buffer;
978
979     CFRelease(url);
980
981     ::gLayoutTestController = LayoutTestController::create(pathOrURL, expectedPixelHash);
982     done = false;
983     topLoadingFrame = 0;
984
985     gLayoutTestController->setIconDatabaseEnabled(false);
986
987     if (shouldLogFrameLoadDelegates(pathOrURL.c_str()))
988         gLayoutTestController->setDumpFrameLoadCallbacks(true);
989
990     COMPtr<IWebView> webView;
991     if (SUCCEEDED(frame->webView(&webView))) {
992         COMPtr<IWebViewPrivate> viewPrivate;
993         if (SUCCEEDED(webView->QueryInterface(&viewPrivate))) {
994             if (shouldLogHistoryDelegates(pathOrURL.c_str())) {
995                 gLayoutTestController->setDumpHistoryDelegateCallbacks(true);            
996                 viewPrivate->setHistoryDelegate(sharedHistoryDelegate.get());
997             } else
998                 viewPrivate->setHistoryDelegate(0);
999         }
1000     }
1001     COMPtr<IWebHistory> history;
1002     if (SUCCEEDED(WebKitCreateInstance(CLSID_WebHistory, 0, __uuidof(history), reinterpret_cast<void**>(&history))))
1003         history->setOptionalSharedHistory(0);
1004
1005     resetWebViewToConsistentStateBeforeTesting();
1006
1007     if (shouldEnableDeveloperExtras(pathOrURL.c_str())) {
1008         gLayoutTestController->setDeveloperExtrasEnabled(true);
1009         if (shouldOpenWebInspector(pathOrURL.c_str()))
1010             gLayoutTestController->showWebInspector();
1011     }
1012     if (shouldDumpAsText(pathOrURL.c_str())) {
1013         gLayoutTestController->setDumpAsText(true);
1014         gLayoutTestController->setGeneratePixelResults(false);
1015     }
1016
1017     prevTestBFItem = 0;
1018     if (webView) {
1019         COMPtr<IWebBackForwardList> bfList;
1020         if (SUCCEEDED(webView->backForwardList(&bfList)))
1021             bfList->currentItem(&prevTestBFItem);
1022     }
1023
1024     WorkQueue::shared()->clear();
1025     WorkQueue::shared()->setFrozen(false);
1026
1027     HWND hostWindow;
1028     webView->hostWindow(reinterpret_cast<OLE_HANDLE*>(&hostWindow));
1029
1030     COMPtr<IWebMutableURLRequest> request;
1031     HRESULT hr = WebKitCreateInstance(CLSID_WebMutableURLRequest, 0, IID_IWebMutableURLRequest, (void**)&request);
1032     if (FAILED(hr))
1033         goto exit;
1034
1035     request->initWithURL(urlBStr, WebURLRequestUseProtocolCachePolicy, 60);
1036
1037     request->setHTTPMethod(methodBStr);
1038     frame->loadRequest(request.get());
1039
1040     MSG msg;
1041     while (GetMessage(&msg, 0, 0, 0)) {
1042         // We get spurious WM_MOUSELEAVE events which make event handling machinery think that mouse button
1043         // is released during dragging (see e.g. fast\dynamic\layer-hit-test-crash.html).
1044         // Mouse can never leave WebView during normal DumpRenderTree operation, so we just ignore all such events.
1045         if (msg.message == WM_MOUSELEAVE)
1046             continue;
1047         TranslateMessage(&msg);
1048         DispatchMessage(&msg);
1049     }
1050
1051     if (shouldEnableDeveloperExtras(pathOrURL.c_str())) {
1052         gLayoutTestController->closeWebInspector();
1053         gLayoutTestController->setDeveloperExtrasEnabled(false);
1054     }
1055
1056     resetWebViewToConsistentStateBeforeTesting();
1057
1058     frame->stopLoading();
1059
1060     if (::gLayoutTestController->closeRemainingWindowsWhenComplete()) {
1061         Vector<HWND> windows = openWindows();
1062         unsigned size = windows.size();
1063         for (unsigned i = 0; i < size; i++) {
1064             HWND window = windows[i];
1065
1066             // Don't try to close the main window
1067             if (window == hostWindow)
1068                 continue;
1069
1070             DestroyWindow(window);
1071         }
1072     }
1073
1074 exit:
1075     SysFreeString(urlBStr);
1076     ::gLayoutTestController.clear();
1077
1078     return;
1079 }
1080
1081 static Boolean pthreadEqualCallback(const void* value1, const void* value2)
1082 {
1083     return (Boolean)pthread_equal(*(pthread_t*)value1, *(pthread_t*)value2);
1084 }
1085
1086 static CFDictionaryKeyCallBacks pthreadKeyCallbacks = { 0, 0, 0, 0, pthreadEqualCallback, 0 };
1087
1088 static pthread_mutex_t javaScriptThreadsMutex = PTHREAD_MUTEX_INITIALIZER;
1089 static bool javaScriptThreadsShouldTerminate;
1090
1091 static const int javaScriptThreadsCount = 4;
1092 static CFMutableDictionaryRef javaScriptThreads()
1093 {
1094     assert(pthread_mutex_trylock(&javaScriptThreadsMutex) == EBUSY);
1095     static CFMutableDictionaryRef staticJavaScriptThreads;
1096     if (!staticJavaScriptThreads)
1097         staticJavaScriptThreads = CFDictionaryCreateMutable(0, 0, &pthreadKeyCallbacks, 0);
1098     return staticJavaScriptThreads;
1099 }
1100
1101 // Loops forever, running a script and randomly respawning, until 
1102 // javaScriptThreadsShouldTerminate becomes true.
1103 void* runJavaScriptThread(void* arg)
1104 {
1105     const char* const script =
1106     " \
1107     var array = []; \
1108     for (var i = 0; i < 10; i++) { \
1109         array.push(String(i)); \
1110     } \
1111     ";
1112
1113     while (true) {
1114         JSGlobalContextRef ctx = JSGlobalContextCreate(0);
1115         JSStringRef scriptRef = JSStringCreateWithUTF8CString(script);
1116
1117         JSValueRef exception = 0;
1118         JSEvaluateScript(ctx, scriptRef, 0, 0, 1, &exception);
1119         assert(!exception);
1120         
1121         JSGlobalContextRelease(ctx);
1122         JSStringRelease(scriptRef);
1123         
1124         JSGarbageCollect(ctx);
1125
1126         pthread_mutex_lock(&javaScriptThreadsMutex);
1127
1128         // Check for cancellation.
1129         if (javaScriptThreadsShouldTerminate) {
1130             pthread_mutex_unlock(&javaScriptThreadsMutex);
1131             return 0;
1132         }
1133
1134         // Respawn probabilistically.
1135         if (rand() % 5 == 0) {
1136             pthread_t pthread;
1137             pthread_create(&pthread, 0, &runJavaScriptThread, 0);
1138             pthread_detach(pthread);
1139
1140             pthread_t self = pthread_self();
1141             CFDictionaryRemoveValue(javaScriptThreads(), self.p);
1142             CFDictionaryAddValue(javaScriptThreads(), pthread.p, 0);
1143
1144             pthread_mutex_unlock(&javaScriptThreadsMutex);
1145             return 0;
1146         }
1147
1148         pthread_mutex_unlock(&javaScriptThreadsMutex);
1149     }
1150 }
1151
1152 static void startJavaScriptThreads(void)
1153 {
1154     pthread_mutex_lock(&javaScriptThreadsMutex);
1155
1156     for (int i = 0; i < javaScriptThreadsCount; i++) {
1157         pthread_t pthread;
1158         pthread_create(&pthread, 0, &runJavaScriptThread, 0);
1159         pthread_detach(pthread);
1160         CFDictionaryAddValue(javaScriptThreads(), pthread.p, 0);
1161     }
1162
1163     pthread_mutex_unlock(&javaScriptThreadsMutex);
1164 }
1165
1166 static void stopJavaScriptThreads(void)
1167 {
1168     pthread_mutex_lock(&javaScriptThreadsMutex);
1169
1170     javaScriptThreadsShouldTerminate = true;
1171
1172     pthread_t* pthreads[javaScriptThreadsCount] = {0};
1173     int threadDictCount = CFDictionaryGetCount(javaScriptThreads());
1174     assert(threadDictCount == javaScriptThreadsCount);
1175     CFDictionaryGetKeysAndValues(javaScriptThreads(), (const void**)pthreads, 0);
1176
1177     pthread_mutex_unlock(&javaScriptThreadsMutex);
1178
1179     for (int i = 0; i < javaScriptThreadsCount; i++) {
1180         pthread_t* pthread = pthreads[i];
1181         pthread_join(*pthread, 0);
1182         free(pthread);
1183     }
1184 }
1185
1186 Vector<HWND>& openWindows()
1187 {
1188     static Vector<HWND> vector;
1189     return vector;
1190 }
1191
1192 WindowToWebViewMap& windowToWebViewMap()
1193 {
1194     static WindowToWebViewMap map;
1195     return map;
1196 }
1197
1198 IWebView* createWebViewAndOffscreenWindow(HWND* webViewWindow)
1199 {
1200     unsigned maxViewWidth = LayoutTestController::maxViewWidth;
1201     unsigned maxViewHeight = LayoutTestController::maxViewHeight;
1202     HWND hostWindow = CreateWindowEx(WS_EX_TOOLWINDOW, kDumpRenderTreeClassName, TEXT("DumpRenderTree"), WS_POPUP,
1203       -maxViewWidth, -maxViewHeight, maxViewWidth, maxViewHeight, 0, 0, GetModuleHandle(0), 0);
1204
1205     IWebView* webView;
1206
1207     HRESULT hr = WebKitCreateInstance(CLSID_WebView, 0, IID_IWebView, (void**)&webView);
1208     if (FAILED(hr)) {
1209         fprintf(stderr, "Failed to create CLSID_WebView instance, error 0x%x\n", hr);
1210         return 0;
1211     }
1212
1213     if (FAILED(webView->setHostWindow((OLE_HANDLE)(ULONG64)hostWindow)))
1214         return 0;
1215
1216     RECT clientRect;
1217     clientRect.bottom = clientRect.left = clientRect.top = clientRect.right = 0;
1218     BSTR groupName = SysAllocString(L"org.webkit.DumpRenderTree");
1219     bool failed = FAILED(webView->initWithFrame(clientRect, 0, groupName));
1220     SysFreeString(groupName);
1221     if (failed)
1222         return 0;
1223
1224     COMPtr<IWebViewPrivate> viewPrivate;
1225     if (FAILED(webView->QueryInterface(&viewPrivate)))
1226         return 0;
1227
1228     viewPrivate->setShouldApplyMacFontAscentHack(TRUE);
1229     viewPrivate->setAlwaysUsesComplexTextCodePath(forceComplexText);
1230
1231     BSTR pluginPath = SysAllocStringLen(0, exePath().length() + _tcslen(TestPluginDir));
1232     _tcscpy(pluginPath, exePath().c_str());
1233     _tcscat(pluginPath, TestPluginDir);
1234     failed = FAILED(viewPrivate->addAdditionalPluginDirectory(pluginPath));
1235     SysFreeString(pluginPath);
1236     if (failed)
1237         return 0;
1238
1239     HWND viewWindow;
1240     if (FAILED(viewPrivate->viewWindow(reinterpret_cast<OLE_HANDLE*>(&viewWindow))))
1241         return 0;
1242     if (webViewWindow)
1243         *webViewWindow = viewWindow;
1244
1245     SetWindowPos(viewWindow, 0, 0, 0, maxViewWidth, maxViewHeight, 0);
1246     ShowWindow(hostWindow, SW_SHOW);
1247
1248     if (FAILED(webView->setFrameLoadDelegate(sharedFrameLoadDelegate.get())))
1249         return 0;
1250
1251     if (FAILED(viewPrivate->setFrameLoadDelegatePrivate(sharedFrameLoadDelegate.get())))
1252         return 0;
1253
1254     if (FAILED(webView->setUIDelegate(sharedUIDelegate.get())))
1255         return 0;
1256
1257     COMPtr<IWebViewEditing> viewEditing;
1258     if (FAILED(webView->QueryInterface(&viewEditing)))
1259         return 0;
1260
1261     if (FAILED(viewEditing->setEditingDelegate(sharedEditingDelegate.get())))
1262         return 0;
1263
1264     ResourceLoadDelegate* resourceLoadDelegate = new ResourceLoadDelegate();
1265     HRESULT result = webView->setResourceLoadDelegate(resourceLoadDelegate);
1266     resourceLoadDelegate->Release(); // The delegate is owned by the WebView, so release our reference to it.
1267     if (FAILED(result))
1268         return 0;
1269
1270     openWindows().append(hostWindow);
1271     windowToWebViewMap().set(hostWindow, webView);
1272     return webView;
1273 }
1274
1275 #if USE(CFNETWORK)
1276 RetainPtr<CFURLCacheRef> sharedCFURLCache()
1277 {
1278 #ifndef DEBUG_ALL
1279     HMODULE module = GetModuleHandle(TEXT("CFNetwork.dll"));
1280 #else
1281     HMODULE module = GetModuleHandle(TEXT("CFNetwork_debug.dll"));
1282 #endif
1283     if (!module)
1284         return 0;
1285
1286     typedef CFURLCacheRef (*CFURLCacheCopySharedURLCacheProcPtr)(void);
1287     if (CFURLCacheCopySharedURLCacheProcPtr copyCache = reinterpret_cast<CFURLCacheCopySharedURLCacheProcPtr>(GetProcAddress(module, "CFURLCacheCopySharedURLCache")))
1288         return RetainPtr<CFURLCacheRef>(AdoptCF, copyCache());
1289
1290     typedef CFURLCacheRef (*CFURLCacheSharedURLCacheProcPtr)(void);
1291     if (CFURLCacheSharedURLCacheProcPtr sharedCache = reinterpret_cast<CFURLCacheSharedURLCacheProcPtr>(GetProcAddress(module, "CFURLCacheSharedURLCache")))
1292         return sharedCache();
1293
1294     return 0;
1295 }
1296 #endif
1297
1298 static LONG WINAPI exceptionFilter(EXCEPTION_POINTERS*)
1299 {
1300     fputs("#CRASHED\n", stderr);
1301     fflush(stderr);
1302     return EXCEPTION_CONTINUE_SEARCH;
1303 }
1304
1305 int main(int argc, char* argv[])
1306 {
1307     // Cygwin calls ::SetErrorMode(SEM_FAILCRITICALERRORS), which we will inherit. This is bad for
1308     // testing/debugging, as it causes the post-mortem debugger not to be invoked. We reset the
1309     // error mode here to work around Cygwin's behavior. See <http://webkit.org/b/55222>.
1310     ::SetErrorMode(0);
1311
1312     ::SetUnhandledExceptionFilter(exceptionFilter);
1313
1314     leakChecking = false;
1315
1316     _setmode(1, _O_BINARY);
1317     _setmode(2, _O_BINARY);
1318
1319     initialize();
1320
1321     Vector<const char*> tests;
1322
1323     for (int i = 1; i < argc; ++i) {
1324         if (!stricmp(argv[i], "--threaded")) {
1325             threaded = true;
1326             continue;
1327         }
1328
1329         if (!stricmp(argv[i], "--dump-all-pixels")) {
1330             dumpAllPixels = true;
1331             continue;
1332         }
1333
1334         if (!stricmp(argv[i], "--pixel-tests")) {
1335             dumpPixels = true;
1336             continue;
1337         }
1338
1339         if (!stricmp(argv[i], "--complex-text")) {
1340             forceComplexText = true;
1341             continue;
1342         }
1343
1344         if (!stricmp(argv[i], "--print-supported-features")) {
1345             printSupportedFeatures = true;
1346             continue;
1347         }
1348
1349         tests.append(argv[i]);
1350     }
1351
1352     policyDelegate = new PolicyDelegate();
1353     sharedFrameLoadDelegate.adoptRef(new FrameLoadDelegate);
1354     sharedUIDelegate.adoptRef(new UIDelegate);
1355     sharedEditingDelegate.adoptRef(new EditingDelegate);
1356     sharedHistoryDelegate.adoptRef(new HistoryDelegate);
1357
1358     // FIXME - need to make DRT pass with Windows native controls <http://bugs.webkit.org/show_bug.cgi?id=25592>
1359     COMPtr<IWebPreferences> tmpPreferences;
1360     if (FAILED(WebKitCreateInstance(CLSID_WebPreferences, 0, IID_IWebPreferences, reinterpret_cast<void**>(&tmpPreferences))))
1361         return -1;
1362     COMPtr<IWebPreferences> standardPreferences;
1363     if (FAILED(tmpPreferences->standardPreferences(&standardPreferences)))
1364         return -1;
1365     COMPtr<IWebPreferencesPrivate> standardPreferencesPrivate;
1366     if (FAILED(standardPreferences->QueryInterface(&standardPreferencesPrivate)))
1367         return -1;
1368     standardPreferencesPrivate->setShouldPaintNativeControls(FALSE);
1369     standardPreferences->setJavaScriptEnabled(TRUE);
1370     standardPreferences->setDefaultFontSize(16);
1371     standardPreferences->setAcceleratedCompositingEnabled(true);
1372     standardPreferences->setContinuousSpellCheckingEnabled(TRUE);
1373
1374     if (printSupportedFeatures) {
1375         BOOL acceleratedCompositingAvailable;
1376         standardPreferences->acceleratedCompositingEnabled(&acceleratedCompositingAvailable);
1377
1378 #if ENABLE(3D_RENDERING)
1379         // In theory, we could have a software-based 3D rendering implementation that we use when
1380         // hardware-acceleration is not available. But we don't have any such software
1381         // implementation, so 3D rendering is only available when hardware-acceleration is.
1382         BOOL threeDRenderingAvailable = acceleratedCompositingAvailable;
1383 #else
1384         BOOL threeDRenderingAvailable = FALSE;
1385 #endif
1386
1387         printf("SupportedFeatures:%s %s\n", acceleratedCompositingAvailable ? "AcceleratedCompositing" : "", threeDRenderingAvailable ? "3DRendering" : "");
1388         return 0;
1389     }
1390
1391     COMPtr<IWebView> webView(AdoptCOM, createWebViewAndOffscreenWindow(&webViewWindow));
1392     if (!webView)
1393         return -1;
1394
1395     COMPtr<IWebIconDatabase> iconDatabase;
1396     COMPtr<IWebIconDatabase> tmpIconDatabase;
1397     if (FAILED(WebKitCreateInstance(CLSID_WebIconDatabase, 0, IID_IWebIconDatabase, (void**)&tmpIconDatabase)))
1398         return -1;
1399     if (FAILED(tmpIconDatabase->sharedIconDatabase(&iconDatabase)))
1400         return -1;
1401         
1402     if (FAILED(webView->mainFrame(&frame)))
1403         return -1;
1404
1405 #if USE(CFNETWORK)
1406     RetainPtr<CFURLCacheRef> urlCache = sharedCFURLCache();
1407     CFURLCacheRemoveAllCachedResponses(urlCache.get());
1408 #endif
1409
1410 #ifdef _DEBUG
1411     _CrtMemState entryToMainMemCheckpoint;
1412     if (leakChecking)
1413         _CrtMemCheckpoint(&entryToMainMemCheckpoint);
1414 #endif
1415
1416     if (threaded)
1417         startJavaScriptThreads();
1418
1419     if (tests.size() == 1 && !strcmp(tests[0], "-")) {
1420         char filenameBuffer[2048];
1421         printSeparators = true;
1422         while (fgets(filenameBuffer, sizeof(filenameBuffer), stdin)) {
1423             char* newLineCharacter = strchr(filenameBuffer, '\n');
1424             if (newLineCharacter)
1425                 *newLineCharacter = '\0';
1426             
1427             if (strlen(filenameBuffer) == 0)
1428                 continue;
1429
1430             runTest(filenameBuffer);
1431         }
1432     } else {
1433         printSeparators = tests.size() > 1;
1434         for (int i = 0; i < tests.size(); i++)
1435             runTest(tests[i]);
1436     }
1437
1438     if (threaded)
1439         stopJavaScriptThreads();
1440     
1441     delete policyDelegate;
1442     frame->Release();
1443
1444 #ifdef _DEBUG
1445     if (leakChecking) {
1446         // dump leaks to stderr
1447         _CrtSetReportMode(_CRT_WARN, _CRTDBG_MODE_FILE);
1448         _CrtSetReportFile(_CRT_WARN, _CRTDBG_FILE_STDERR);
1449         _CrtMemDumpAllObjectsSince(&entryToMainMemCheckpoint);
1450     }
1451 #endif
1452
1453     shutDownWebKit();
1454
1455     return 0;
1456 }