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