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