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