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