[Win] Various DumpRenderTree Fixes.
[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_t name;
406             if (FAILED(frame->name(&name.GetBSTR())))
407                 return;
408             printf("frame '%S' ", static_cast<wchar_t*>(name));
409         }
410         printf("scrolled to %.f,%.f\n", (double)scrollPosition.cx, (double)scrollPosition.cy);
411     }
412
413     if (::gTestRunner->dumpChildFrameScrollPositions()) {
414         COMPtr<IEnumVARIANT> enumKids;
415         if (FAILED(frame->childFrames(&enumKids)))
416             return;
417         VARIANT var;
418         VariantInit(&var);
419         while (enumKids->Next(1, &var, 0) == S_OK) {
420             ASSERT(V_VT(&var) == VT_UNKNOWN);
421             COMPtr<IWebFrame> framePtr;
422             V_UNKNOWN(&var)->QueryInterface(IID_IWebFrame, (void**)&framePtr);
423             dumpFrameScrollPosition(framePtr.get());
424             VariantClear(&var);
425         }
426     }
427 }
428
429 static wstring dumpFramesAsText(IWebFrame* frame)
430 {
431     if (!frame)
432         return L"";
433
434     COMPtr<IDOMDocument> document;
435     if (FAILED(frame->DOMDocument(&document)))
436         return L"";
437
438     COMPtr<IDOMElement> documentElement;
439     if (FAILED(document->documentElement(&documentElement)))
440         return L"";
441
442     wstring result;
443
444     // Add header for all but the main frame.
445     COMPtr<IWebFrame> parent;
446     if (FAILED(frame->parentFrame(&parent)))
447         return L"";
448     if (parent) {
449         _bstr_t name;
450         if (FAILED(frame->name(&name.GetBSTR())))
451             return L"";
452
453         result.append(L"\n--------\nFrame: '");
454         result.append(static_cast<wchar_t*>(name), name.length());
455         result.append(L"'\n--------\n");
456     }
457
458     _bstr_t innerText;
459     COMPtr<IDOMElementPrivate> docPrivate;
460     if (SUCCEEDED(documentElement->QueryInterface(&docPrivate)))
461         docPrivate->innerText(&innerText.GetBSTR());
462
463     result.append(static_cast<wchar_t*>(innerText), innerText.length());
464     result.append(L"\n");
465
466     if (::gTestRunner->dumpChildFramesAsText()) {
467         COMPtr<IEnumVARIANT> enumKids;
468         if (FAILED(frame->childFrames(&enumKids)))
469             return L"";
470         VARIANT var;
471         VariantInit(&var);
472         while (enumKids->Next(1, &var, 0) == S_OK) {
473             ASSERT(V_VT(&var) == VT_UNKNOWN);
474             COMPtr<IWebFrame> framePtr;
475             V_UNKNOWN(&var)->QueryInterface(IID_IWebFrame, (void**)&framePtr);
476             result.append(dumpFramesAsText(framePtr.get()));
477             VariantClear(&var);
478         }
479     }
480
481     return result;
482 }
483
484 static int compareHistoryItems(const void* item1, const void* item2)
485 {
486     COMPtr<IWebHistoryItemPrivate> itemA;
487     if (FAILED((*(COMPtr<IUnknown>*)item1)->QueryInterface(&itemA)))
488         return 0;
489
490     COMPtr<IWebHistoryItemPrivate> itemB;
491     if (FAILED((*(COMPtr<IUnknown>*)item2)->QueryInterface(&itemB)))
492         return 0;
493
494     _bstr_t targetA;
495     if (FAILED(itemA->target(&targetA.GetBSTR())))
496         return 0;
497
498     _bstr_t targetB;
499     if (FAILED(itemB->target(&targetB.GetBSTR())))
500         return 0;
501
502     return wcsicmp(static_cast<wchar_t*>(targetA), static_cast<wchar_t*>(targetB));
503 }
504
505 static void dumpHistoryItem(IWebHistoryItem* item, int indent, bool current)
506 {
507     ASSERT(item);
508
509     int start = 0;
510     if (current) {
511         printf("curr->");
512         start = 6;
513     }
514     for (int i = start; i < indent; i++)
515         putchar(' ');
516
517     _bstr_t url;
518     if (FAILED(item->URLString(&url.GetBSTR())))
519         return;
520
521     if (wcsstr(static_cast<wchar_t*>(url), L"file:/") == static_cast<wchar_t*>(url)) {
522         static wchar_t* layoutTestsString = L"/LayoutTests/";
523         static wchar_t* fileTestString = L"(file test):";
524         
525         wchar_t* result = wcsstr(static_cast<wchar_t*>(url), layoutTestsString);
526         if (result == NULL)
527             return;
528         wchar_t* start = result + wcslen(layoutTestsString);
529
530         _bstr_t newURL(SysAllocStringLen(0, SysStringLen(url)), false);
531         wcscpy(static_cast<wchar_t*>(newURL), fileTestString);
532         wcscpy(static_cast<wchar_t*>(newURL) + wcslen(fileTestString), start);
533
534         url = newURL;
535     }
536
537     printf("%S", static_cast<wchar_t*>(url));
538
539     COMPtr<IWebHistoryItemPrivate> itemPrivate;
540     if (FAILED(item->QueryInterface(&itemPrivate)))
541         return;
542
543     _bstr_t target;
544     if (FAILED(itemPrivate->target(&target.GetBSTR())))
545         return;
546     if (target.length())
547         printf(" (in frame \"%S\")", static_cast<wchar_t*>(target));
548     BOOL isTargetItem = FALSE;
549     if (FAILED(itemPrivate->isTargetItem(&isTargetItem)))
550         return;
551     if (isTargetItem)
552         printf("  **nav target**");
553     putchar('\n');
554
555     unsigned kidsCount;
556     SAFEARRAY* arrPtr;
557     if (FAILED(itemPrivate->children(&kidsCount, &arrPtr)) || !kidsCount)
558         return;
559
560     Vector<COMPtr<IUnknown> > kidsVector;
561
562     LONG lowerBound;
563     if (FAILED(::SafeArrayGetLBound(arrPtr, 1, &lowerBound)))
564         goto exit;
565
566     LONG upperBound;
567     if (FAILED(::SafeArrayGetUBound(arrPtr, 1, &upperBound)))
568         goto exit;
569
570     LONG length = upperBound - lowerBound + 1;
571     if (!length)
572         goto exit;
573     ASSERT(length == kidsCount);
574
575     IUnknown** safeArrayData;
576     if (FAILED(::SafeArrayAccessData(arrPtr, (void**)&safeArrayData)))
577         goto exit;
578
579     for (int i = 0; i < length; ++i)
580         kidsVector.append(safeArrayData[i]);
581     ::SafeArrayUnaccessData(arrPtr);
582
583     // must sort to eliminate arbitrary result ordering which defeats reproducible testing
584     qsort(kidsVector.data(), kidsCount, sizeof(kidsVector[0]), compareHistoryItems);
585
586     for (unsigned i = 0; i < kidsCount; ++i) {
587         COMPtr<IWebHistoryItem> item;
588         kidsVector[i]->QueryInterface(&item);
589         dumpHistoryItem(item.get(), indent + 4, false);
590     }
591
592 exit:
593     if (arrPtr && SUCCEEDED(::SafeArrayUnlock(arrPtr)))
594         ::SafeArrayDestroy(arrPtr);
595 }
596
597 static void dumpBackForwardList(IWebView* webView)
598 {
599     ASSERT(webView);
600
601     printf("\n============== Back Forward List ==============\n");
602
603     COMPtr<IWebBackForwardList> bfList;
604     if (FAILED(webView->backForwardList(&bfList)))
605         return;
606
607     // Print out all items in the list after prevTestBFItem, which was from the previous test
608     // Gather items from the end of the list, the print them out from oldest to newest
609
610     Vector<COMPtr<IUnknown> > itemsToPrint;
611
612     int forwardListCount;
613     if (FAILED(bfList->forwardListCount(&forwardListCount)))
614         return;
615
616     for (int i = forwardListCount; i > 0; --i) {
617         COMPtr<IWebHistoryItem> item;
618         if (FAILED(bfList->itemAtIndex(i, &item)))
619             return;
620         // something is wrong if the item from the last test is in the forward part of the b/f list
621         ASSERT(item != prevTestBFItem);
622         COMPtr<IUnknown> itemUnknown;
623         item->QueryInterface(&itemUnknown);
624         itemsToPrint.append(itemUnknown);
625     }
626
627     COMPtr<IWebHistoryItem> currentItem;
628     if (FAILED(bfList->currentItem(&currentItem)))
629         return;
630
631     ASSERT(currentItem != prevTestBFItem);
632     COMPtr<IUnknown> currentItemUnknown;
633     currentItem->QueryInterface(&currentItemUnknown);
634     itemsToPrint.append(currentItemUnknown);
635     int currentItemIndex = itemsToPrint.size() - 1;
636
637     int backListCount;
638     if (FAILED(bfList->backListCount(&backListCount)))
639         return;
640
641     for (int i = -1; i >= -backListCount; --i) {
642         COMPtr<IWebHistoryItem> item;
643         if (FAILED(bfList->itemAtIndex(i, &item)))
644             return;
645         if (item == prevTestBFItem)
646             break;
647         COMPtr<IUnknown> itemUnknown;
648         item->QueryInterface(&itemUnknown);
649         itemsToPrint.append(itemUnknown);
650     }
651
652     for (int i = itemsToPrint.size() - 1; i >= 0; --i) {
653         COMPtr<IWebHistoryItem> historyItemToPrint;
654         itemsToPrint[i]->QueryInterface(&historyItemToPrint);
655         dumpHistoryItem(historyItemToPrint.get(), 8, i == currentItemIndex);
656     }
657
658     printf("===============================================\n");
659 }
660
661 static void dumpBackForwardListForAllWindows()
662 {
663     unsigned count = openWindows().size();
664     for (unsigned i = 0; i < count; i++) {
665         HWND window = openWindows()[i];
666         IWebView* webView = windowToWebViewMap().get(window).get();
667         dumpBackForwardList(webView);
668     }
669 }
670
671 static void invalidateAnyPreviousWaitToDumpWatchdog()
672 {
673     if (!waitToDumpWatchdog)
674         return;
675
676     KillTimer(0, waitToDumpWatchdog);
677     waitToDumpWatchdog = 0;
678 }
679
680 void dump()
681 {
682     invalidateAnyPreviousWaitToDumpWatchdog();
683
684     COMPtr<IWebDataSource> dataSource;
685     if (SUCCEEDED(frame->dataSource(&dataSource))) {
686         COMPtr<IWebURLResponse> response;
687         if (SUCCEEDED(dataSource->response(&response)) && response) {
688             _bstr_t mimeType;
689             if (SUCCEEDED(response->MIMEType(&mimeType.GetBSTR())) && !_tcscmp(static_cast<TCHAR*>(mimeType), TEXT("text/plain"))) {
690                 ::gTestRunner->setDumpAsText(true);
691                 ::gTestRunner->setGeneratePixelResults(false);
692             }
693         }
694     }
695
696     _bstr_t resultString;
697
698     if (dumpTree) {
699         ::InvalidateRect(webViewWindow, 0, TRUE);
700         ::SendMessage(webViewWindow, WM_PAINT, 0, 0);
701
702         if (::gTestRunner->dumpAsText()) {
703             resultString = dumpFramesAsText(frame).data();
704         } else {
705             COMPtr<IWebFramePrivate> framePrivate;
706             if (FAILED(frame->QueryInterface(&framePrivate)))
707                 goto fail;
708             framePrivate->renderTreeAsExternalRepresentation(gTestRunner->isPrinting(), &resultString.GetBSTR());
709         }
710
711         if (resultString.length()) {
712             unsigned stringLength = resultString.length();
713             int bufferSize = ::WideCharToMultiByte(CP_UTF8, 0, resultString, stringLength, 0, 0, 0, 0);
714             char* buffer = (char*)malloc(bufferSize + 1);
715             ::WideCharToMultiByte(CP_UTF8, 0, resultString, stringLength, buffer, bufferSize + 1, 0, 0);
716             fwrite(buffer, 1, bufferSize, stdout);
717             free(buffer);
718
719             if (!::gTestRunner->dumpAsText() && !::gTestRunner->dumpDOMAsWebArchive() && !::gTestRunner->dumpSourceAsWebArchive() && !::gTestRunner->dumpAsAudio())
720                 dumpFrameScrollPosition(frame);
721
722             if (::gTestRunner->dumpBackForwardList())
723                 dumpBackForwardListForAllWindows();
724         } else
725             printf("ERROR: nil result from %s", ::gTestRunner->dumpAsText() ? "IDOMElement::innerText" : "IFrameViewPrivate::renderTreeAsExternalRepresentation");
726
727         if (printSeparators) {
728             puts("#EOF"); // terminate the content block
729             fputs("#EOF\n", stderr);
730         }
731     }
732
733     if (dumpPixelsForCurrentTest
734      && gTestRunner->generatePixelResults()
735      && !gTestRunner->dumpDOMAsWebArchive()
736      && !gTestRunner->dumpSourceAsWebArchive())
737         dumpWebViewAsPixelsAndCompareWithExpected(gTestRunner->expectedPixelHash());
738
739     printf("#EOF\n");   // terminate the (possibly empty) pixels block
740     fflush(stdout);
741     fflush(stderr);
742
743 fail:
744     SysFreeString(resultString);
745     // This will exit from our message loop.
746     PostQuitMessage(0);
747     done = true;
748 }
749
750 static bool shouldLogFrameLoadDelegates(const char* pathOrURL)
751 {
752     return strstr(pathOrURL, "/loading/") || strstr(pathOrURL, "\\loading\\");
753 }
754
755 static bool shouldLogHistoryDelegates(const char* pathOrURL)
756 {
757     return strstr(pathOrURL, "/globalhistory/") || strstr(pathOrURL, "\\globalhistory\\");
758 }
759
760 static bool shouldDumpAsText(const char* pathOrURL)
761 {
762     return strstr(pathOrURL, "/dumpAsText/") || strstr(pathOrURL, "\\dumpAsText\\");
763 }
764
765 static bool shouldEnableDeveloperExtras(const char* pathOrURL)
766 {
767     return true;
768 }
769
770 static void resetDefaultsToConsistentValues(IWebPreferences* preferences)
771 {
772 #ifdef USE_MAC_FONTS
773     static _bstr_t standardFamily(TEXT("Times"));
774     static _bstr_t fixedFamily(TEXT("Courier"));
775     static _bstr_t sansSerifFamily(TEXT("Helvetica"));
776     static _bstr_t cursiveFamily(TEXT("Apple Chancery"));
777     static _bstr_t fantasyFamily(TEXT("Papyrus"));
778     static _bstr_t pictographFamily(TEXT("Apple Color Emoji"));
779 #else
780     static _bstr_t standardFamily(TEXT("Times New Roman"));
781     static _bstr_t fixedFamily(TEXT("Courier New"));
782     static _bstr_t sansSerifFamily(TEXT("Arial"));
783     static _bstr_t cursiveFamily(TEXT("Comic Sans MS")); // Not actually cursive, but it's what IE and Firefox use.
784     static _bstr_t fantasyFamily(TEXT("Times New Roman"));
785     static _bstr_t pictographFamily(TEXT("Times New Roman"));
786 #endif
787
788     preferences->setStandardFontFamily(standardFamily);
789     preferences->setFixedFontFamily(fixedFamily);
790     preferences->setSerifFontFamily(standardFamily);
791     preferences->setSansSerifFontFamily(sansSerifFamily);
792     preferences->setCursiveFontFamily(cursiveFamily);
793     preferences->setFantasyFontFamily(fantasyFamily);
794     preferences->setPictographFontFamily(pictographFamily);
795
796     preferences->setAutosaves(FALSE);
797     preferences->setDefaultFontSize(16);
798     preferences->setDefaultFixedFontSize(13);
799     preferences->setMinimumFontSize(0);
800     preferences->setDefaultTextEncodingName(L"ISO-8859-1");
801     preferences->setJavaEnabled(FALSE);
802     preferences->setPlugInsEnabled(TRUE);
803     preferences->setDOMPasteAllowed(TRUE);
804     preferences->setEditableLinkBehavior(WebKitEditableLinkOnlyLiveWithShiftKey);
805     preferences->setFontSmoothing(FontSmoothingTypeStandard);
806     preferences->setUsesPageCache(FALSE);
807     preferences->setPrivateBrowsingEnabled(FALSE);
808     preferences->setJavaScriptCanOpenWindowsAutomatically(TRUE);
809     preferences->setJavaScriptEnabled(TRUE);
810     preferences->setTabsToLinks(FALSE);
811     preferences->setShouldPrintBackgrounds(TRUE);
812     preferences->setCacheModel(WebCacheModelDocumentBrowser);
813     preferences->setLoadsImagesAutomatically(TRUE);
814     preferences->setTextAreasAreResizable(TRUE);
815     preferences->setCSSRegionsEnabled(TRUE);
816
817     if (persistentUserStyleSheetLocation) {
818         Vector<wchar_t> urlCharacters(CFStringGetLength(persistentUserStyleSheetLocation.get()));
819         CFStringGetCharacters(persistentUserStyleSheetLocation.get(), CFRangeMake(0, CFStringGetLength(persistentUserStyleSheetLocation.get())), (UniChar *)urlCharacters.data());
820         _bstr_t url(urlCharacters.data());
821         preferences->setUserStyleSheetLocation(url);
822         preferences->setUserStyleSheetEnabled(TRUE);
823     } else
824         preferences->setUserStyleSheetEnabled(FALSE);
825
826     COMPtr<IWebPreferencesPrivate> prefsPrivate(Query, preferences);
827     if (prefsPrivate) {
828         prefsPrivate->setAllowUniversalAccessFromFileURLs(TRUE);
829         prefsPrivate->setAllowFileAccessFromFileURLs(TRUE);
830         prefsPrivate->setAuthorAndUserStylesEnabled(TRUE);
831         prefsPrivate->setDeveloperExtrasEnabled(FALSE);
832         prefsPrivate->setExperimentalNotificationsEnabled(TRUE);
833         prefsPrivate->setShouldPaintNativeControls(FALSE); // FIXME - need to make DRT pass with Windows native controls <http://bugs.webkit.org/show_bug.cgi?id=25592>
834         prefsPrivate->setJavaScriptCanAccessClipboard(TRUE);
835         prefsPrivate->setXSSAuditorEnabled(FALSE);
836         prefsPrivate->setOfflineWebApplicationCacheEnabled(TRUE);
837         prefsPrivate->setLoadsSiteIconsIgnoringImageLoadingPreference(FALSE);
838         prefsPrivate->setFrameFlatteningEnabled(FALSE);
839         prefsPrivate->setFullScreenEnabled(TRUE);
840 #if USE(CG)
841         prefsPrivate->setAcceleratedCompositingEnabled(TRUE);
842 #endif
843         prefsPrivate->setMockScrollbarsEnabled(TRUE);
844         prefsPrivate->setScreenFontSubstitutionEnabled(TRUE);
845     }
846     setAlwaysAcceptCookies(false);
847
848     setlocale(LC_ALL, "");
849 }
850
851 static void resetWebViewToConsistentStateBeforeTesting()
852 {
853     COMPtr<IWebView> webView;
854     if (FAILED(frame->webView(&webView))) 
855         return;
856
857     webView->setPolicyDelegate(0);
858     policyDelegate->setPermissive(false);
859     policyDelegate->setControllerToNotifyDone(0);
860
861     COMPtr<IWebIBActions> webIBActions(Query, webView);
862     if (webIBActions) {
863         webIBActions->makeTextStandardSize(0);
864         webIBActions->resetPageZoom(0);
865     }
866
867
868     COMPtr<IWebPreferences> preferences;
869     if (SUCCEEDED(webView->preferences(&preferences)))
870         resetDefaultsToConsistentValues(preferences.get());
871
872     if (gTestRunner) {
873         JSGlobalContextRef context = frame->globalContext();
874         WebCoreTestSupport::resetInternalsObject(context);
875     }
876
877     COMPtr<IWebViewPrivate> webViewPrivate(Query, webView);
878     if (!webViewPrivate)
879         return;
880
881     HWND viewWindow;
882     if (SUCCEEDED(webViewPrivate->viewWindow(reinterpret_cast<OLE_HANDLE*>(&viewWindow))) && viewWindow)
883         SetFocus(viewWindow);
884
885     webViewPrivate->clearMainFrameName();
886     webViewPrivate->resetOriginAccessWhitelists();
887
888     _bstr_t groupName;
889     if (SUCCEEDED(webView->groupName(&groupName.GetBSTR())))
890         webViewPrivate->removeAllUserContentFromGroup(groupName);
891
892     sharedUIDelegate->resetUndoManager();
893
894     sharedFrameLoadDelegate->resetToConsistentState();
895
896     COMPtr<IWebFramePrivate> framePrivate;
897     if (SUCCEEDED(frame->QueryInterface(&framePrivate)))
898         framePrivate->clearOpener();
899 }
900
901 static void sizeWebViewForCurrentTest()
902 {
903     bool isSVGW3CTest = (gTestRunner->testPathOrURL().find("svg\\W3C-SVG-1.1") != string::npos);
904     unsigned width;
905     unsigned height;
906     if (isSVGW3CTest) {
907         width = TestRunner::w3cSVGViewWidth;
908         height = TestRunner::w3cSVGViewHeight;
909     } else {
910         width = TestRunner::viewWidth;
911         height = TestRunner::viewHeight;
912     }
913
914     ::SetWindowPos(webViewWindow, 0, 0, 0, width, height, SWP_NOMOVE);
915 }
916
917 static String findFontFallback(const char* pathOrUrl)
918 {
919     String pathToFontFallback = WebCore::directoryName(pathOrUrl);
920
921     wchar_t fullPath[_MAX_PATH];
922     if (!_wfullpath(fullPath, pathToFontFallback.charactersWithNullTermination().data(), _MAX_PATH))
923         return emptyString();
924
925     if (!::PathIsDirectoryW(fullPath))
926         return emptyString();
927
928     String pathToCheck = fullPath;
929
930     static const String layoutTests = "LayoutTests";
931
932     // Find the layout test root on the current path:
933     size_t location = pathToCheck.find(layoutTests);
934     if (WTF::notFound == location)
935         return emptyString();
936
937     String pathToTest = pathToCheck.substring(location + layoutTests.length() + 1);
938     String possiblePathToLogue = WebCore::pathByAppendingComponent(pathToCheck.substring(0, location + layoutTests.length() + 1), "platform\\win");
939
940     Vector<String> possiblePaths;
941     possiblePaths.append(WebCore::pathByAppendingComponent(possiblePathToLogue, pathToTest));
942
943     size_t nextCandidateEnd = pathToTest.reverseFind('\\');
944     while (nextCandidateEnd && nextCandidateEnd != WTF::notFound) {
945         pathToTest = pathToTest.substring(0, nextCandidateEnd);
946         possiblePaths.append(WebCore::pathByAppendingComponent(possiblePathToLogue, pathToTest));
947         nextCandidateEnd = pathToTest.reverseFind('\\');
948     }
949
950     for (Vector<String>::iterator pos = possiblePaths.begin(); pos != possiblePaths.end(); ++pos) {
951         pathToFontFallback = WebCore::pathByAppendingComponent(*pos, "resources\\"); 
952
953         if (::PathIsDirectoryW(pathToFontFallback.charactersWithNullTermination().data()))
954             return pathToFontFallback;
955     }
956
957     return emptyString();
958 }
959
960 static void addFontFallbackIfPresent(const String& fontFallbackPath)
961 {
962     if (fontFallbackPath.isEmpty())
963         return;
964
965     String fontFallback = WebCore::pathByAppendingComponent(fontFallbackPath, "Mac-compatible-font-fallback.css");
966
967     if (!::PathFileExistsW(fontFallback.charactersWithNullTermination().data()))
968         return;
969
970     ::setPersistentUserStyleSheetLocation(fontFallback.createCFString().get());
971 }
972
973 static void removeFontFallbackIfPresent(const String& fontFallbackPath)
974 {
975     if (fontFallbackPath.isEmpty())
976         return;
977
978     String fontFallback = WebCore::pathByAppendingComponent(fontFallbackPath, "Mac-compatible-font-fallback.css");
979
980     if (!::PathFileExistsW(fontFallback.charactersWithNullTermination().data()))
981         return;
982
983     ::setPersistentUserStyleSheetLocation(0);
984 }
985
986
987 static void runTest(const string& inputLine)
988 {
989     ASSERT(!inputLine.empty());
990
991     TestCommand command = parseInputLine(inputLine);
992     const string& pathOrURL = command.pathOrURL;
993     dumpPixelsForCurrentTest = command.shouldDumpPixels || dumpPixelsForAllTests;
994
995     static _bstr_t methodBStr(TEXT("GET"));
996
997     CFStringRef str = CFStringCreateWithCString(0, pathOrURL.c_str(), kCFStringEncodingWindowsLatin1);
998     CFURLRef url = CFURLCreateWithString(0, str, 0);
999
1000     if (!url)
1001         url = CFURLCreateWithFileSystemPath(0, str, kCFURLWindowsPathStyle, false);
1002
1003     CFRelease(str);
1004
1005     String fallbackPath = findFontFallback(pathOrURL.c_str());
1006
1007     str = CFURLGetString(url);
1008
1009     CFIndex length = CFStringGetLength(str);
1010     UniChar* buffer = new UniChar[length + 1];
1011
1012     CFStringGetCharacters(str, CFRangeMake(0, length), buffer);
1013     buffer[length] = 0;
1014
1015     _bstr_t urlBStr((OLECHAR*)buffer);
1016     ASSERT(urlBStr.length() == length);
1017     delete[] buffer;
1018
1019     CFRelease(url);
1020
1021     ::gTestRunner = TestRunner::create(pathOrURL, command.expectedPixelHash);
1022     topLoadingFrame = 0;
1023     done = false;
1024
1025     addFontFallbackIfPresent(fallbackPath);
1026
1027     sizeWebViewForCurrentTest();
1028     gTestRunner->setIconDatabaseEnabled(false);
1029
1030     if (shouldLogFrameLoadDelegates(pathOrURL.c_str()))
1031         gTestRunner->setDumpFrameLoadCallbacks(true);
1032
1033     COMPtr<IWebView> webView;
1034     if (SUCCEEDED(frame->webView(&webView))) {
1035         COMPtr<IWebViewPrivate> viewPrivate;
1036         if (SUCCEEDED(webView->QueryInterface(&viewPrivate))) {
1037             if (shouldLogHistoryDelegates(pathOrURL.c_str())) {
1038                 gTestRunner->setDumpHistoryDelegateCallbacks(true);            
1039                 viewPrivate->setHistoryDelegate(sharedHistoryDelegate.get());
1040             } else
1041                 viewPrivate->setHistoryDelegate(0);
1042         }
1043     }
1044     COMPtr<IWebHistory> history;
1045     if (SUCCEEDED(WebKitCreateInstance(CLSID_WebHistory, 0, __uuidof(history), reinterpret_cast<void**>(&history))))
1046         history->setOptionalSharedHistory(0);
1047
1048     resetWebViewToConsistentStateBeforeTesting();
1049
1050     if (shouldEnableDeveloperExtras(pathOrURL.c_str())) {
1051         gTestRunner->setDeveloperExtrasEnabled(true);
1052         if (shouldDumpAsText(pathOrURL.c_str())) {
1053             gTestRunner->setDumpAsText(true);
1054             gTestRunner->setGeneratePixelResults(false);
1055         }
1056     }
1057
1058     prevTestBFItem = 0;
1059     if (webView) {
1060         COMPtr<IWebBackForwardList> bfList;
1061         if (SUCCEEDED(webView->backForwardList(&bfList)))
1062             bfList->currentItem(&prevTestBFItem);
1063     }
1064
1065     WorkQueue::shared()->clear();
1066     WorkQueue::shared()->setFrozen(false);
1067
1068     HWND hostWindow;
1069     webView->hostWindow(reinterpret_cast<OLE_HANDLE*>(&hostWindow));
1070
1071     COMPtr<IWebMutableURLRequest> request;
1072     HRESULT hr = WebKitCreateInstance(CLSID_WebMutableURLRequest, 0, IID_IWebMutableURLRequest, (void**)&request);
1073     if (FAILED(hr))
1074         goto exit;
1075
1076     request->initWithURL(urlBStr, WebURLRequestUseProtocolCachePolicy, 60);
1077
1078     request->setHTTPMethod(methodBStr);
1079     frame->loadRequest(request.get());
1080
1081     MSG msg;
1082     while (true) {
1083 #if USE(CF)
1084         CFRunLoopRunInMode(kCFRunLoopDefaultMode, 0, true);
1085 #endif
1086         if (!GetMessage(&msg, 0, 0, 0))
1087             break;
1088
1089         // We get spurious WM_MOUSELEAVE events which make event handling machinery think that mouse button
1090         // is released during dragging (see e.g. fast\dynamic\layer-hit-test-crash.html).
1091         // Mouse can never leave WebView during normal DumpRenderTree operation, so we just ignore all such events.
1092         if (msg.message == WM_MOUSELEAVE)
1093             continue;
1094         TranslateMessage(&msg);
1095         DispatchMessage(&msg);
1096     }
1097
1098     if (shouldEnableDeveloperExtras(pathOrURL.c_str())) {
1099         gTestRunner->closeWebInspector();
1100         gTestRunner->setDeveloperExtrasEnabled(false);
1101     }
1102
1103     resetWebViewToConsistentStateBeforeTesting();
1104
1105     frame->stopLoading();
1106
1107     if (::gTestRunner->closeRemainingWindowsWhenComplete()) {
1108         Vector<HWND> windows = openWindows();
1109         unsigned size = windows.size();
1110         for (unsigned i = 0; i < size; i++) {
1111             HWND window = windows[i];
1112
1113             // Don't try to close the main window
1114             if (window == hostWindow)
1115                 continue;
1116
1117             DestroyWindow(window);
1118         }
1119     }
1120
1121 exit:
1122     removeFontFallbackIfPresent(fallbackPath);
1123     ::gTestRunner.clear();
1124
1125     return;
1126 }
1127
1128 Vector<HWND>& openWindows()
1129 {
1130     static Vector<HWND> vector;
1131     return vector;
1132 }
1133
1134 WindowToWebViewMap& windowToWebViewMap()
1135 {
1136     static WindowToWebViewMap map;
1137     return map;
1138 }
1139
1140 IWebView* createWebViewAndOffscreenWindow(HWND* webViewWindow)
1141 {
1142     int maxViewWidth = TestRunner::viewWidth;
1143     int maxViewHeight = TestRunner::viewHeight;
1144     HWND hostWindow = CreateWindowEx(WS_EX_TOOLWINDOW, kDumpRenderTreeClassName, TEXT("DumpRenderTree"), WS_POPUP,
1145       -maxViewWidth, -maxViewHeight, maxViewWidth, maxViewHeight, 0, 0, GetModuleHandle(0), 0);
1146
1147     IWebView* webView;
1148
1149     HRESULT hr = WebKitCreateInstance(CLSID_WebView, 0, IID_IWebView, (void**)&webView);
1150     if (FAILED(hr)) {
1151         fprintf(stderr, "Failed to create CLSID_WebView instance, error 0x%x\n", hr);
1152         return 0;
1153     }
1154
1155     if (FAILED(webView->setHostWindow((OLE_HANDLE)(ULONG64)hostWindow)))
1156         return 0;
1157
1158     RECT clientRect;
1159     clientRect.bottom = clientRect.left = clientRect.top = clientRect.right = 0;
1160     _bstr_t groupName(L"org.webkit.DumpRenderTree");
1161     if (FAILED(webView->initWithFrame(clientRect, 0, groupName)))
1162         return 0;
1163
1164     COMPtr<IWebViewPrivate> viewPrivate;
1165     if (FAILED(webView->QueryInterface(&viewPrivate)))
1166         return 0;
1167
1168     viewPrivate->setShouldApplyMacFontAscentHack(TRUE);
1169     viewPrivate->setAlwaysUsesComplexTextCodePath(forceComplexText);
1170
1171     _bstr_t pluginPath(SysAllocStringLen(0, exePath().length() + _tcslen(TestPluginDir)), false);
1172     _tcscpy(static_cast<TCHAR*>(pluginPath), exePath().c_str());
1173     _tcscat(static_cast<TCHAR*>(pluginPath), TestPluginDir);
1174     if (FAILED(viewPrivate->addAdditionalPluginDirectory(pluginPath)))
1175         return 0;
1176
1177     HWND viewWindow;
1178     if (FAILED(viewPrivate->viewWindow(reinterpret_cast<OLE_HANDLE*>(&viewWindow))))
1179         return 0;
1180     if (webViewWindow)
1181         *webViewWindow = viewWindow;
1182
1183     SetWindowPos(viewWindow, 0, 0, 0, maxViewWidth, maxViewHeight, 0);
1184     ShowWindow(hostWindow, SW_SHOW);
1185
1186     if (FAILED(webView->setFrameLoadDelegate(sharedFrameLoadDelegate.get())))
1187         return 0;
1188
1189     if (FAILED(viewPrivate->setFrameLoadDelegatePrivate(sharedFrameLoadDelegate.get())))
1190         return 0;
1191
1192     if (FAILED(webView->setUIDelegate(sharedUIDelegate.get())))
1193         return 0;
1194
1195     COMPtr<IWebViewEditing> viewEditing;
1196     if (FAILED(webView->QueryInterface(&viewEditing)))
1197         return 0;
1198
1199     if (FAILED(viewEditing->setEditingDelegate(sharedEditingDelegate.get())))
1200         return 0;
1201
1202     ResourceLoadDelegate* resourceLoadDelegate = new ResourceLoadDelegate();
1203     HRESULT result = webView->setResourceLoadDelegate(resourceLoadDelegate);
1204     resourceLoadDelegate->Release(); // The delegate is owned by the WebView, so release our reference to it.
1205     if (FAILED(result))
1206         return 0;
1207
1208     openWindows().append(hostWindow);
1209     windowToWebViewMap().set(hostWindow, webView);
1210     return webView;
1211 }
1212
1213 #if USE(CFNETWORK)
1214 RetainPtr<CFURLCacheRef> sharedCFURLCache()
1215 {
1216 #ifndef DEBUG_ALL
1217     HMODULE module = GetModuleHandle(TEXT("CFNetwork.dll"));
1218 #else
1219     HMODULE module = GetModuleHandle(TEXT("CFNetwork_debug.dll"));
1220 #endif
1221     if (!module)
1222         return 0;
1223
1224     typedef CFURLCacheRef (*CFURLCacheCopySharedURLCacheProcPtr)(void);
1225     if (CFURLCacheCopySharedURLCacheProcPtr copyCache = reinterpret_cast<CFURLCacheCopySharedURLCacheProcPtr>(GetProcAddress(module, "CFURLCacheCopySharedURLCache")))
1226         return adoptCF(copyCache());
1227
1228     typedef CFURLCacheRef (*CFURLCacheSharedURLCacheProcPtr)(void);
1229     if (CFURLCacheSharedURLCacheProcPtr sharedCache = reinterpret_cast<CFURLCacheSharedURLCacheProcPtr>(GetProcAddress(module, "CFURLCacheSharedURLCache")))
1230         return sharedCache();
1231
1232     return 0;
1233 }
1234 #endif
1235
1236 static LONG WINAPI exceptionFilter(EXCEPTION_POINTERS*)
1237 {
1238     fputs("#CRASHED\n", stderr);
1239     fflush(stderr);
1240     return EXCEPTION_CONTINUE_SEARCH;
1241 }
1242
1243 int main(int argc, const char* argv[])
1244 {
1245     // Cygwin calls ::SetErrorMode(SEM_FAILCRITICALERRORS), which we will inherit. This is bad for
1246     // testing/debugging, as it causes the post-mortem debugger not to be invoked. We reset the
1247     // error mode here to work around Cygwin's behavior. See <http://webkit.org/b/55222>.
1248     ::SetErrorMode(0);
1249
1250     ::SetUnhandledExceptionFilter(exceptionFilter);
1251
1252     leakChecking = false;
1253
1254     _setmode(1, _O_BINARY);
1255     _setmode(2, _O_BINARY);
1256
1257     initialize();
1258
1259     Vector<const char*> tests;
1260
1261     for (int i = 1; i < argc; ++i) {
1262         if (!stricmp(argv[i], "--threaded")) {
1263             threaded = true;
1264             continue;
1265         }
1266
1267         if (!stricmp(argv[i], "--dump-all-pixels")) {
1268             dumpAllPixels = true;
1269             continue;
1270         }
1271
1272         if (!stricmp(argv[i], "--complex-text")) {
1273             forceComplexText = true;
1274             continue;
1275         }
1276
1277         if (!stricmp(argv[i], "--print-supported-features")) {
1278             printSupportedFeatures = true;
1279             continue;
1280         }
1281
1282         if (!stricmp(argv[i], "--pixel-tests")) {
1283             dumpPixelsForAllTests = true;
1284             continue;
1285         }
1286
1287         tests.append(argv[i]);
1288     }
1289
1290     policyDelegate = new PolicyDelegate();
1291     sharedFrameLoadDelegate.adoptRef(new FrameLoadDelegate);
1292     sharedUIDelegate.adoptRef(new UIDelegate);
1293     sharedEditingDelegate.adoptRef(new EditingDelegate);
1294     sharedHistoryDelegate.adoptRef(new HistoryDelegate);
1295
1296     // FIXME - need to make DRT pass with Windows native controls <http://bugs.webkit.org/show_bug.cgi?id=25592>
1297     COMPtr<IWebPreferences> tmpPreferences;
1298     if (FAILED(WebKitCreateInstance(CLSID_WebPreferences, 0, IID_IWebPreferences, reinterpret_cast<void**>(&tmpPreferences))))
1299         return -1;
1300     COMPtr<IWebPreferences> standardPreferences;
1301     if (FAILED(tmpPreferences->standardPreferences(&standardPreferences)))
1302         return -1;
1303     COMPtr<IWebPreferencesPrivate> standardPreferencesPrivate;
1304     if (FAILED(standardPreferences->QueryInterface(&standardPreferencesPrivate)))
1305         return -1;
1306     standardPreferencesPrivate->setShouldPaintNativeControls(FALSE);
1307     standardPreferences->setJavaScriptEnabled(TRUE);
1308     standardPreferences->setDefaultFontSize(16);
1309 #if USE(CG)
1310     standardPreferences->setAcceleratedCompositingEnabled(TRUE);
1311     standardPreferences->setAVFoundationEnabled(TRUE);
1312 #endif
1313     standardPreferences->setContinuousSpellCheckingEnabled(TRUE);
1314
1315     if (printSupportedFeatures) {
1316         BOOL acceleratedCompositingAvailable;
1317         standardPreferences->acceleratedCompositingEnabled(&acceleratedCompositingAvailable);
1318
1319 #if ENABLE(3D_RENDERING)
1320         // In theory, we could have a software-based 3D rendering implementation that we use when
1321         // hardware-acceleration is not available. But we don't have any such software
1322         // implementation, so 3D rendering is only available when hardware-acceleration is.
1323         BOOL threeDRenderingAvailable = acceleratedCompositingAvailable;
1324 #else
1325         BOOL threeDRenderingAvailable = FALSE;
1326 #endif
1327
1328         printf("SupportedFeatures:%s %s\n", acceleratedCompositingAvailable ? "AcceleratedCompositing" : "", threeDRenderingAvailable ? "3DRendering" : "");
1329         return 0;
1330     }
1331
1332 #if USE(CF)
1333     // Set up these values before creating the WebView so that the various initializations will see these preferred values.
1334     String path = libraryPathForDumpRenderTree();
1335     CFPreferencesSetAppValue(WebDatabaseDirectoryDefaultsKey, WebCore::pathByAppendingComponent(path, "Databases").createCFString().get(), kCFPreferencesCurrentApplication);
1336     CFPreferencesSetAppValue(WebStorageDirectoryDefaultsKey, WebCore::pathByAppendingComponent(path, "LocalStorage").createCFString().get(), kCFPreferencesCurrentApplication);
1337     CFPreferencesSetAppValue(WebKitLocalCacheDefaultsKey, WebCore::pathByAppendingComponent(path, "LocalCache").createCFString().get(), kCFPreferencesCurrentApplication);
1338 #endif
1339
1340     COMPtr<IWebView> webView(AdoptCOM, createWebViewAndOffscreenWindow(&webViewWindow));
1341     if (!webView)
1342         return -1;
1343
1344     COMPtr<IWebIconDatabase> iconDatabase;
1345     COMPtr<IWebIconDatabase> tmpIconDatabase;
1346     if (FAILED(WebKitCreateInstance(CLSID_WebIconDatabase, 0, IID_IWebIconDatabase, (void**)&tmpIconDatabase)))
1347         return -1;
1348     if (FAILED(tmpIconDatabase->sharedIconDatabase(&iconDatabase)))
1349         return -1;
1350         
1351     if (FAILED(webView->mainFrame(&frame)))
1352         return -1;
1353
1354 #if USE(CFNETWORK)
1355     RetainPtr<CFURLCacheRef> urlCache = sharedCFURLCache();
1356     CFURLCacheRemoveAllCachedResponses(urlCache.get());
1357 #endif
1358
1359 #ifdef _DEBUG
1360     _CrtMemState entryToMainMemCheckpoint;
1361     if (leakChecking)
1362         _CrtMemCheckpoint(&entryToMainMemCheckpoint);
1363 #endif
1364
1365     if (threaded)
1366         startJavaScriptThreads();
1367
1368     if (tests.size() == 1 && !strcmp(tests[0], "-")) {
1369         char filenameBuffer[2048];
1370         printSeparators = true;
1371         while (fgets(filenameBuffer, sizeof(filenameBuffer), stdin)) {
1372             char* newLineCharacter = strchr(filenameBuffer, '\n');
1373             if (newLineCharacter)
1374                 *newLineCharacter = '\0';
1375             
1376             if (strlen(filenameBuffer) == 0)
1377                 continue;
1378
1379             runTest(filenameBuffer);
1380         }
1381     } else {
1382         printSeparators = tests.size() > 1;
1383         for (int i = 0; i < tests.size(); i++)
1384             runTest(tests[i]);
1385     }
1386
1387     if (threaded)
1388         stopJavaScriptThreads();
1389     
1390     delete policyDelegate;
1391     frame->Release();
1392
1393 #ifdef _DEBUG
1394     if (leakChecking) {
1395         // dump leaks to stderr
1396         _CrtSetReportMode(_CRT_WARN, _CRTDBG_MODE_FILE);
1397         _CrtSetReportFile(_CRT_WARN, _CRTDBG_FILE_STDERR);
1398         _CrtMemDumpAllObjectsSince(&entryToMainMemCheckpoint);
1399     }
1400 #endif
1401
1402     shutDownWebKit();
1403
1404     return 0;
1405 }
1406
1407 extern "C" __declspec(dllexport) int WINAPI dllLauncherEntryPoint(int argc, const char* argv[])
1408 {
1409     return main(argc, argv);
1410 }