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