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