[Win] Implement the "--show-webview" option for Windows
[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 bool showWebView = false;
100 static RetainPtr<CFStringRef> persistentUserStyleSheetLocation;
101
102 volatile bool done;
103 // This is the topmost frame that is loading, during a given load, or nil when no load is 
104 // in progress.  Usually this is the same as the main frame, but not always.  In the case
105 // where a frameset is loaded, and then new content is loaded into one of the child frames,
106 // that child frame is the "topmost frame that is loading".
107 IWebFrame* topLoadingFrame;     // !nil iff a load is in progress
108 static COMPtr<IWebHistoryItem> prevTestBFItem;  // current b/f item at the end of the previous test
109 PolicyDelegate* policyDelegate; 
110 COMPtr<FrameLoadDelegate> sharedFrameLoadDelegate;
111 COMPtr<UIDelegate> sharedUIDelegate;
112 COMPtr<EditingDelegate> sharedEditingDelegate;
113 COMPtr<ResourceLoadDelegate> resourceLoadDelegate;
114 COMPtr<HistoryDelegate> sharedHistoryDelegate;
115
116 IWebFrame* frame;
117 HWND webViewWindow;
118
119 RefPtr<TestRunner> gTestRunner;
120
121 UINT_PTR waitToDumpWatchdog = 0;
122
123 void setPersistentUserStyleSheetLocation(CFStringRef url)
124 {
125     persistentUserStyleSheetLocation = url;
126 }
127
128 bool setAlwaysAcceptCookies(bool)
129 {
130     // FIXME: Implement this by making the Windows port use the testing network storage session and
131     // modify its cookie storage policy.
132     return false;
133 }
134
135 static RetainPtr<CFStringRef> substringFromIndex(CFStringRef string, CFIndex index)
136 {
137     return adoptCF(CFStringCreateWithSubstring(kCFAllocatorDefault, string, CFRangeMake(index, CFStringGetLength(string) - index)));
138 }
139
140 static wstring lastPathComponentAsWString(CFURLRef url)
141 {
142     RetainPtr<CFStringRef> lastPathComponent = adoptCF(CFURLCopyLastPathComponent(url));
143     return cfStringRefToWString(lastPathComponent.get());
144 }
145
146 wstring urlSuitableForTestResult(const wstring& urlString)
147 {
148     if (urlString.empty())
149         return urlString;
150
151     RetainPtr<CFURLRef> url = adoptCF(CFURLCreateWithBytes(kCFAllocatorDefault, reinterpret_cast<const UInt8*>(urlString.c_str()), urlString.length() * sizeof(wstring::value_type), kCFStringEncodingUTF16, 0));
152
153     RetainPtr<CFStringRef> scheme = adoptCF(CFURLCopyScheme(url.get()));
154     if (scheme && CFStringCompare(scheme.get(), CFSTR("file"), kCFCompareCaseInsensitive) != kCFCompareEqualTo)
155         return urlString;
156
157     COMPtr<IWebDataSource> dataSource;
158     if (FAILED(frame->dataSource(&dataSource))) {
159         if (FAILED(frame->provisionalDataSource(&dataSource)))
160             return lastPathComponentAsWString(url.get());
161     }
162
163     COMPtr<IWebMutableURLRequest> request;
164     if (FAILED(dataSource->request(&request)))
165         return lastPathComponentAsWString(url.get());
166
167     _bstr_t requestURLString;
168     if (FAILED(request->URL(requestURLString.GetAddress())))
169         return lastPathComponentAsWString(url.get());
170
171     RetainPtr<CFURLRef> requestURL = adoptCF(CFURLCreateWithBytes(kCFAllocatorDefault, reinterpret_cast<const UInt8*>(requestURLString.GetBSTR()), requestURLString.length() * sizeof(OLECHAR), kCFStringEncodingUTF16, 0));
172     RetainPtr<CFURLRef> baseURL = adoptCF(CFURLCreateCopyDeletingLastPathComponent(kCFAllocatorDefault, requestURL.get()));
173
174     RetainPtr<CFStringRef> basePath = adoptCF(CFURLCopyPath(baseURL.get()));
175     RetainPtr<CFStringRef> path = adoptCF(CFURLCopyPath(url.get()));
176
177     if (basePath.get() && CFStringHasPrefix(path.get(), basePath.get()))
178         return cfStringRefToWString(substringFromIndex(path.get(), CFStringGetLength(basePath.get())).get());
179
180     return lastPathComponentAsWString(url.get());
181 }
182
183 wstring lastPathComponent(const wstring& urlString)
184 {
185     if (urlString.empty())
186         return urlString;
187
188     RetainPtr<CFURLRef> url = adoptCF(CFURLCreateWithBytes(kCFAllocatorDefault, reinterpret_cast<const UInt8*>(urlString.c_str()), urlString.length() * sizeof(wstring::value_type), kCFStringEncodingUTF16, 0));
189     RetainPtr<CFStringRef> lastPathComponent = adoptCF(CFURLCopyLastPathComponent(url.get()));
190
191     return cfStringRefToWString(lastPathComponent.get());
192 }
193
194 static string toUTF8(const wchar_t* wideString, size_t length)
195 {
196     int result = WideCharToMultiByte(CP_UTF8, 0, wideString, length + 1, 0, 0, 0, 0);
197     Vector<char> utf8Vector(result);
198     result = WideCharToMultiByte(CP_UTF8, 0, wideString, length + 1, utf8Vector.data(), result, 0, 0);
199     if (!result)
200         return string();
201
202     return string(utf8Vector.data(), utf8Vector.size() - 1);
203 }
204
205 #if USE(CF)
206 static String libraryPathForDumpRenderTree()
207 {
208     DWORD size = ::GetEnvironmentVariable(dumpRenderTreeTemp, 0, 0);
209     Vector<TCHAR> buffer(size);
210     if (::GetEnvironmentVariable(dumpRenderTreeTemp, buffer.data(), buffer.size())) {
211         wstring path = buffer.data();
212         if (!path.empty() && (path[path.length() - 1] != L'\\'))
213             path.append(L"\\");
214         return String (path.data(), path.length());
215     }
216
217     return WebCore::localUserSpecificStorageDirectory();
218 }
219 #endif
220
221 string toUTF8(BSTR bstr)
222 {
223     return toUTF8(bstr, SysStringLen(bstr));
224 }
225
226 string toUTF8(const wstring& wideString)
227 {
228     return toUTF8(wideString.c_str(), wideString.length());
229 }
230
231 wstring cfStringRefToWString(CFStringRef cfStr)
232 {
233     Vector<wchar_t> v(CFStringGetLength(cfStr));
234     CFStringGetCharacters(cfStr, CFRangeMake(0, CFStringGetLength(cfStr)), (UniChar *)v.data());
235
236     return wstring(v.data(), v.size());
237 }
238
239 static LRESULT CALLBACK DumpRenderTreeWndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
240 {
241     switch (msg) {
242         case WM_DESTROY:
243             for (long i = openWindows().size() - 1; i >= 0; --i) {
244                 if (openWindows()[i] == hWnd) {
245                     openWindows().remove(i);
246                     windowToWebViewMap().remove(hWnd);
247                     break;
248                 }
249             }
250             return 0;
251             break;
252         default:
253             return DefWindowProc(hWnd, msg, wParam, lParam);
254     }
255 }
256
257 static const wstring& exePath()
258 {
259     static wstring path;
260     static bool initialized;
261
262     if (initialized)
263         return path;
264     initialized = true;
265
266     TCHAR buffer[MAX_PATH];
267     GetModuleFileName(GetModuleHandle(0), buffer, ARRAYSIZE(buffer));
268     path = buffer;
269     int lastSlash = path.rfind('\\');
270     if (lastSlash != -1 && lastSlash + 1 < path.length())
271         path = path.substr(0, lastSlash + 1);
272
273     return path;
274 }
275
276 static const wstring& fontsPath()
277 {
278     static wstring path;
279     static bool initialized;
280
281     if (initialized)
282         return path;
283     initialized = true;
284
285     DWORD size = GetEnvironmentVariable(fontsEnvironmentVariable, 0, 0);
286     Vector<TCHAR> buffer(size);
287     if (GetEnvironmentVariable(fontsEnvironmentVariable, buffer.data(), buffer.size())) {
288         path = buffer.data();
289         if (path[path.length() - 1] != '\\')
290             path.append(L"\\");
291         return path;
292     }
293
294     path = exePath() + TEXT("DumpRenderTree.resources\\");
295     return path;
296 }
297
298 #ifdef DEBUG_ALL
299 #define WEBKITDLL TEXT("WebKit_debug.dll")
300 #else
301 #define WEBKITDLL TEXT("WebKit.dll")
302 #endif
303
304 static void initialize()
305 {
306     if (HMODULE webKitModule = LoadLibrary(WEBKITDLL))
307         if (FARPROC dllRegisterServer = GetProcAddress(webKitModule, "DllRegisterServer"))
308             dllRegisterServer();
309
310     // Init COM
311     OleInitialize(nullptr);
312
313     static LPCTSTR fontsToInstall[] = {
314         TEXT("AHEM____.ttf"),
315         TEXT("Apple Chancery.ttf"),
316         TEXT("Courier Bold.ttf"),
317         TEXT("Courier.ttf"),
318         TEXT("Helvetica Bold Oblique.ttf"),
319         TEXT("Helvetica Bold.ttf"),
320         TEXT("Helvetica Oblique.ttf"),
321         TEXT("Helvetica.ttf"),
322         TEXT("Helvetica Neue Bold Italic.ttf"),
323         TEXT("Helvetica Neue Bold.ttf"),
324         TEXT("Helvetica Neue Condensed Black.ttf"),
325         TEXT("Helvetica Neue Condensed Bold.ttf"),
326         TEXT("Helvetica Neue Italic.ttf"),
327         TEXT("Helvetica Neue Light Italic.ttf"),
328         TEXT("Helvetica Neue Light.ttf"),
329         TEXT("Helvetica Neue UltraLight Italic.ttf"),
330         TEXT("Helvetica Neue UltraLight.ttf"),
331         TEXT("Helvetica Neue.ttf"),
332         TEXT("Lucida Grande.ttf"),
333         TEXT("Lucida Grande Bold.ttf"),
334         TEXT("Monaco.ttf"),
335         TEXT("Papyrus.ttf"),
336         TEXT("Times Bold Italic.ttf"),
337         TEXT("Times Bold.ttf"),
338         TEXT("Times Italic.ttf"),
339         TEXT("Times Roman.ttf"),
340         TEXT("WebKit Layout Tests 2.ttf"),
341         TEXT("WebKit Layout Tests.ttf"),
342         TEXT("WebKitWeightWatcher100.ttf"),
343         TEXT("WebKitWeightWatcher200.ttf"),
344         TEXT("WebKitWeightWatcher300.ttf"),
345         TEXT("WebKitWeightWatcher400.ttf"),
346         TEXT("WebKitWeightWatcher500.ttf"),
347         TEXT("WebKitWeightWatcher600.ttf"),
348         TEXT("WebKitWeightWatcher700.ttf"),
349         TEXT("WebKitWeightWatcher800.ttf"),
350         TEXT("WebKitWeightWatcher900.ttf")
351     };
352
353     wstring resourcesPath = fontsPath();
354
355     COMPtr<IWebTextRenderer> textRenderer;
356     if (SUCCEEDED(WebKitCreateInstance(CLSID_WebTextRenderer, 0, IID_IWebTextRenderer, (void**)&textRenderer)))
357         for (int i = 0; i < ARRAYSIZE(fontsToInstall); ++i)
358             textRenderer->registerPrivateFont(wstring(resourcesPath + fontsToInstall[i]).c_str());
359
360     // Register a host window
361     WNDCLASSEX wcex;
362
363     wcex.cbSize = sizeof(WNDCLASSEX);
364
365     wcex.style         = CS_HREDRAW | CS_VREDRAW;
366     wcex.lpfnWndProc   = DumpRenderTreeWndProc;
367     wcex.cbClsExtra    = 0;
368     wcex.cbWndExtra    = 0;
369     wcex.hInstance     = GetModuleHandle(0);
370     wcex.hIcon         = 0;
371     wcex.hCursor       = LoadCursor(0, IDC_ARROW);
372     wcex.hbrBackground = 0;
373     wcex.lpszMenuName  = 0;
374     wcex.lpszClassName = kDumpRenderTreeClassName;
375     wcex.hIconSm       = 0;
376
377     RegisterClassEx(&wcex);
378 }
379
380 void displayWebView()
381 {
382     ::InvalidateRect(webViewWindow, 0, TRUE);
383     ::SendMessage(webViewWindow, WM_PAINT, 0, 0);
384 }
385
386 void dumpFrameScrollPosition(IWebFrame* frame)
387 {
388     if (!frame)
389         return;
390
391     COMPtr<IWebFramePrivate> framePrivate;
392     if (FAILED(frame->QueryInterface(&framePrivate)))
393         return;
394
395     SIZE scrollPosition;
396     if (FAILED(framePrivate->scrollOffset(&scrollPosition)))
397         return;
398
399     if (abs(scrollPosition.cx) > 0.00000001 || abs(scrollPosition.cy) > 0.00000001) {
400         COMPtr<IWebFrame> parent;
401         if (FAILED(frame->parentFrame(&parent)))
402             return;
403         if (parent) {
404             _bstr_t name;
405             if (FAILED(frame->name(&name.GetBSTR())))
406                 return;
407             printf("frame '%S' ", static_cast<wchar_t*>(name));
408         }
409         printf("scrolled to %.f,%.f\n", (double)scrollPosition.cx, (double)scrollPosition.cy);
410     }
411
412     if (::gTestRunner->dumpChildFrameScrollPositions()) {
413         COMPtr<IEnumVARIANT> enumKids;
414         if (FAILED(frame->childFrames(&enumKids)))
415             return;
416         _variant_t var;
417         while (enumKids->Next(1, &var.GetVARIANT(), nullptr) == S_OK) {
418             ASSERT(V_VT(&var) == VT_UNKNOWN);
419             COMPtr<IWebFrame> framePtr;
420             V_UNKNOWN(&var)->QueryInterface(IID_IWebFrame, (void**)&framePtr);
421             dumpFrameScrollPosition(framePtr.get());
422         }
423     }
424 }
425
426 static wstring dumpFramesAsText(IWebFrame* frame)
427 {
428     if (!frame)
429         return L"";
430
431     COMPtr<IDOMDocument> document;
432     if (FAILED(frame->DOMDocument(&document)))
433         return L"";
434
435     COMPtr<IDOMElement> documentElement;
436     if (FAILED(document->documentElement(&documentElement)))
437         return L"";
438
439     wstring result;
440
441     // Add header for all but the main frame.
442     COMPtr<IWebFrame> parent;
443     if (FAILED(frame->parentFrame(&parent)))
444         return L"";
445
446     if (parent) {
447         _bstr_t name;
448         if (FAILED(frame->name(&name.GetBSTR())))
449             return L"";
450
451         result.append(L"\n--------\nFrame: '");
452         result.append(static_cast<wchar_t*>(name), name.length());
453         result.append(L"'\n--------\n");
454     }
455
456     _bstr_t innerText;
457     COMPtr<IDOMElementPrivate> docPrivate;
458     if (SUCCEEDED(documentElement->QueryInterface(&docPrivate)))
459         docPrivate->innerText(&innerText.GetBSTR());
460
461     result.append(static_cast<wchar_t*>(innerText), innerText.length());
462     result.append(L"\n");
463
464     if (::gTestRunner->dumpChildFramesAsText()) {
465         COMPtr<IEnumVARIANT> enumKids;
466         if (FAILED(frame->childFrames(&enumKids)))
467             return L"";
468
469         _variant_t var;
470         while (enumKids->Next(1, &var.GetVARIANT(), nullptr) == S_OK) {
471             ASSERT(V_VT(&var) == VT_UNKNOWN);
472             COMPtr<IWebFrame> framePtr;
473             V_UNKNOWN(&var)->QueryInterface(IID_IWebFrame, (void**)&framePtr);
474             result.append(dumpFramesAsText(framePtr.get()));
475         }
476     }
477
478     return result;
479 }
480
481 static int compareHistoryItems(const void* item1, const void* item2)
482 {
483     COMPtr<IWebHistoryItemPrivate> itemA;
484     if (FAILED((*(COMPtr<IUnknown>*)item1)->QueryInterface(&itemA)))
485         return 0;
486
487     COMPtr<IWebHistoryItemPrivate> itemB;
488     if (FAILED((*(COMPtr<IUnknown>*)item2)->QueryInterface(&itemB)))
489         return 0;
490
491     _bstr_t targetA;
492     if (FAILED(itemA->target(&targetA.GetBSTR())))
493         return 0;
494
495     _bstr_t targetB;
496     if (FAILED(itemB->target(&targetB.GetBSTR())))
497         return 0;
498
499     return wcsicmp(static_cast<wchar_t*>(targetA), static_cast<wchar_t*>(targetB));
500 }
501
502 static void dumpHistoryItem(IWebHistoryItem* item, int indent, bool current)
503 {
504     ASSERT(item);
505
506     int start = 0;
507     if (current) {
508         printf("curr->");
509         start = 6;
510     }
511     for (int i = start; i < indent; i++)
512         putchar(' ');
513
514     _bstr_t url;
515     if (FAILED(item->URLString(&url.GetBSTR())))
516         return;
517
518     if (wcsstr(static_cast<wchar_t*>(url), L"file:/") == static_cast<wchar_t*>(url)) {
519         static wchar_t* layoutTestsStringUnixPath = L"/LayoutTests/";
520         static wchar_t* layoutTestsStringDOSPath = L"\\LayoutTests\\";
521         
522         wchar_t* result = wcsstr(static_cast<wchar_t*>(url), layoutTestsStringUnixPath);
523         if (!result)
524             result = wcsstr(static_cast<wchar_t*>(url), layoutTestsStringDOSPath);
525         if (!result)
526             return;
527
528         wchar_t* start = result + wcslen(layoutTestsStringUnixPath);
529
530         url = _bstr_t(L"(file test):") + _bstr_t(start);
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);
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     preferences->setUsesPageCache(FALSE);
813
814     preferences->setPrivateBrowsingEnabled(FALSE);
815     prefsPrivate->setAuthorAndUserStylesEnabled(TRUE);
816     // Shrinks standalone images to fit: YES
817     preferences->setJavaScriptCanOpenWindowsAutomatically(TRUE);
818     prefsPrivate->setJavaScriptCanAccessClipboard(TRUE);
819     prefsPrivate->setOfflineWebApplicationCacheEnabled(TRUE);
820     prefsPrivate->setDeveloperExtrasEnabled(FALSE);
821     prefsPrivate->setJavaScriptRuntimeFlags(WebKitJavaScriptRuntimeFlagsAllEnabled);
822     // Set JS experiments enabled: YES
823     preferences->setLoadsImagesAutomatically(TRUE);
824     prefsPrivate->setLoadsSiteIconsIgnoringImageLoadingPreference(FALSE);
825     prefsPrivate->setFrameFlatteningEnabled(FALSE);
826     // Set spatial navigation enabled: NO
827     if (persistentUserStyleSheetLocation) {
828         size_t stringLength = CFStringGetLength(persistentUserStyleSheetLocation.get());
829         Vector<UniChar> urlCharacters(stringLength + 1, 0);
830         CFStringGetCharacters(persistentUserStyleSheetLocation.get(), CFRangeMake(0, stringLength), urlCharacters.data());
831         _bstr_t url(reinterpret_cast<wchar_t*>(urlCharacters.data()));
832         preferences->setUserStyleSheetLocation(url);
833         preferences->setUserStyleSheetEnabled(TRUE);
834     } else
835         preferences->setUserStyleSheetEnabled(FALSE);
836
837 #if USE(CG)
838     prefsPrivate->setAcceleratedCompositingEnabled(TRUE);
839 #endif
840     // Set WebGL Enabled: NO
841     preferences->setCSSRegionsEnabled(TRUE);
842     // Set uses HTML5 parser quirks: NO
843     // Async spellcheck: NO
844     prefsPrivate->setMockScrollbarsEnabled(TRUE);
845
846     preferences->setFontSmoothing(FontSmoothingTypeStandard);
847
848     setAlwaysAcceptCookies(false);
849 }
850
851 // Called once on DumpRenderTree startup.
852 static void setDefaultsToConsistentValuesForTesting()
853 {
854 #if USE(CF)
855     String libraryPath = libraryPathForDumpRenderTree();
856
857     // Set up these values before creating the WebView so that the various initializations will see these preferred values.
858     CFPreferencesSetAppValue(WebDatabaseDirectoryDefaultsKey, WebCore::pathByAppendingComponent(libraryPath, "Databases").createCFString().get(), kCFPreferencesCurrentApplication);
859     CFPreferencesSetAppValue(WebStorageDirectoryDefaultsKey, WebCore::pathByAppendingComponent(libraryPath, "LocalStorage").createCFString().get(), kCFPreferencesCurrentApplication);
860     CFPreferencesSetAppValue(WebKitLocalCacheDefaultsKey, WebCore::pathByAppendingComponent(libraryPath, "LocalCache").createCFString().get(), kCFPreferencesCurrentApplication);
861 #endif
862 }
863
864 static void resetWebViewToConsistentStateBeforeTesting()
865 {
866     COMPtr<IWebView> webView;
867     if (FAILED(frame->webView(&webView))) 
868         return;
869
870     COMPtr<IWebViewEditing> viewEditing;
871     if (SUCCEEDED(webView->QueryInterface(&viewEditing)) && viewEditing) {
872
873         viewEditing->setEditable(FALSE);
874
875         COMPtr<IWebEditingDelegate> delegate;
876         if (SUCCEEDED(viewEditing->editingDelegate(&delegate)) && delegate) {
877             COMPtr<EditingDelegate> editingDelegate(Query, viewEditing.get());
878             if (editingDelegate)
879                 editingDelegate->setAcceptsEditing(TRUE);
880         }
881     }
882
883     COMPtr<IWebIBActions> webIBActions(Query, webView);
884     if (webIBActions) {
885         webIBActions->makeTextStandardSize(0);
886         webIBActions->resetPageZoom(0);
887     }
888
889     COMPtr<IWebViewPrivate> webViewPrivate(Query, webView);
890     if (webViewPrivate)
891         webViewPrivate->setTabKeyCyclesThroughElements(TRUE);
892
893     webView->setPolicyDelegate(nullptr);
894     policyDelegate->setPermissive(false);
895     policyDelegate->setControllerToNotifyDone(nullptr);
896     sharedFrameLoadDelegate->resetToConsistentState();
897
898     if (webViewPrivate) {
899         webViewPrivate->clearMainFrameName();
900         _bstr_t groupName;
901         if (SUCCEEDED(webView->groupName(&groupName.GetBSTR())))
902             webViewPrivate->removeAllUserContentFromGroup(groupName);
903     }
904
905     COMPtr<IWebPreferences> preferences;
906     if (SUCCEEDED(webView->preferences(&preferences)))
907         resetWebPreferencesToConsistentValues(preferences.get());
908
909     TestRunner::setSerializeHTTPLoads(false);
910
911     setlocale(LC_ALL, "");
912
913     if (::gTestRunner) {
914         JSGlobalContextRef context = frame->globalContext();
915         WebCoreTestSupport::resetInternalsObject(context);
916     }
917
918     if (preferences) {
919         preferences->setContinuousSpellCheckingEnabled(TRUE);
920         // Automatic Quote Subs
921         // Automatic Link Detection
922         // Autommatic Dash substitution
923         // Automatic Spell Check
924         preferences->setGrammarCheckingEnabled(TRUE);
925         // Use Test Mode Focus Ring
926     }
927
928     HWND viewWindow;
929     if (webViewPrivate && SUCCEEDED(webViewPrivate->viewWindow(&viewWindow)) && viewWindow)
930         ::SetFocus(viewWindow);
931
932     webViewPrivate->resetOriginAccessWhitelists();
933
934     sharedUIDelegate->resetUndoManager();
935
936     COMPtr<IWebFramePrivate> framePrivate;
937     if (SUCCEEDED(frame->QueryInterface(&framePrivate)))
938         framePrivate->clearOpener();
939 }
940
941 static void sizeWebViewForCurrentTest()
942 {
943     bool isSVGW3CTest = (::gTestRunner->testURL().find("svg\\W3C-SVG-1.1") != string::npos)
944         || (::gTestRunner->testURL().find("svg/W3C-SVG-1.1") != string::npos);
945     unsigned width = isSVGW3CTest ? TestRunner::w3cSVGViewWidth : TestRunner::viewWidth;
946     unsigned height = isSVGW3CTest ? TestRunner::w3cSVGViewHeight : TestRunner::viewHeight;
947
948     ::SetWindowPos(webViewWindow, 0, 0, 0, width, height, SWP_NOMOVE);
949 }
950
951 static String findFontFallback(const char* pathOrUrl)
952 {
953     String pathToFontFallback = WebCore::directoryName(pathOrUrl);
954
955     wchar_t fullPath[_MAX_PATH];
956     if (!_wfullpath(fullPath, pathToFontFallback.charactersWithNullTermination().data(), _MAX_PATH))
957         return emptyString();
958
959     if (!::PathIsDirectoryW(fullPath))
960         return emptyString();
961
962     String pathToCheck = fullPath;
963
964     static const String layoutTests = "LayoutTests";
965
966     // Find the layout test root on the current path:
967     size_t location = pathToCheck.find(layoutTests);
968     if (WTF::notFound == location)
969         return emptyString();
970
971     String pathToTest = pathToCheck.substring(location + layoutTests.length() + 1);
972     String possiblePathToLogue = WebCore::pathByAppendingComponent(pathToCheck.substring(0, location + layoutTests.length() + 1), "platform\\win");
973
974     Vector<String> possiblePaths;
975     possiblePaths.append(WebCore::pathByAppendingComponent(possiblePathToLogue, pathToTest));
976
977     size_t nextCandidateEnd = pathToTest.reverseFind('\\');
978     while (nextCandidateEnd && nextCandidateEnd != WTF::notFound) {
979         pathToTest = pathToTest.substring(0, nextCandidateEnd);
980         possiblePaths.append(WebCore::pathByAppendingComponent(possiblePathToLogue, pathToTest));
981         nextCandidateEnd = pathToTest.reverseFind('\\');
982     }
983
984     for (Vector<String>::iterator pos = possiblePaths.begin(); pos != possiblePaths.end(); ++pos) {
985         pathToFontFallback = WebCore::pathByAppendingComponent(*pos, "resources\\"); 
986
987         if (::PathIsDirectoryW(pathToFontFallback.charactersWithNullTermination().data()))
988             return pathToFontFallback;
989     }
990
991     return emptyString();
992 }
993
994 static void addFontFallbackIfPresent(const String& fontFallbackPath)
995 {
996     if (fontFallbackPath.isEmpty())
997         return;
998
999     String fontFallback = WebCore::pathByAppendingComponent(fontFallbackPath, "Mac-compatible-font-fallback.css");
1000
1001     if (!::PathFileExistsW(fontFallback.charactersWithNullTermination().data()))
1002         return;
1003
1004     ::setPersistentUserStyleSheetLocation(fontFallback.createCFString().get());
1005 }
1006
1007 static void removeFontFallbackIfPresent(const String& fontFallbackPath)
1008 {
1009     if (fontFallbackPath.isEmpty())
1010         return;
1011
1012     String fontFallback = WebCore::pathByAppendingComponent(fontFallbackPath, "Mac-compatible-font-fallback.css");
1013
1014     if (!::PathFileExistsW(fontFallback.charactersWithNullTermination().data()))
1015         return;
1016
1017     ::setPersistentUserStyleSheetLocation(nullptr);
1018 }
1019
1020
1021 static void runTest(const string& inputLine)
1022 {
1023     ASSERT(!inputLine.empty());
1024
1025     TestCommand command = parseInputLine(inputLine);
1026     const string& pathOrURL = command.pathOrURL;
1027     dumpPixelsForCurrentTest = command.shouldDumpPixels || dumpPixelsForAllTests;
1028
1029     static _bstr_t methodBStr(TEXT("GET"));
1030
1031     CFStringRef str = CFStringCreateWithCString(0, pathOrURL.c_str(), kCFStringEncodingWindowsLatin1);
1032     if (!str) {
1033         fprintf(stderr, "Failed to parse \"%s\" as UTF-8\n", pathOrURL.c_str());
1034         return;
1035     }
1036
1037     CFURLRef url = CFURLCreateWithString(0, str, 0);
1038
1039     if (!url)
1040         url = CFURLCreateWithFileSystemPath(0, str, kCFURLWindowsPathStyle, false);
1041
1042     CFRelease(str);
1043
1044     if (!url) {
1045         fprintf(stderr, "Failed to parse \"%s\" as a URL\n", pathOrURL.c_str());
1046         return;
1047     }
1048
1049     String fallbackPath = findFontFallback(pathOrURL.c_str());
1050
1051     str = CFURLGetString(url);
1052
1053     CFIndex length = CFStringGetLength(str);
1054
1055     Vector<UniChar> buffer(length + 1, 0);
1056     CFStringGetCharacters(str, CFRangeMake(0, length), buffer.data());
1057
1058     _bstr_t urlBStr(reinterpret_cast<wchar_t*>(buffer.data()));
1059     ASSERT(urlBStr.length() == length);
1060
1061     CFIndex maximumURLLengthAsUTF8 = CFStringGetMaximumSizeForEncoding(length, kCFStringEncodingUTF8) + 1;
1062     Vector<char> testURL(maximumURLLengthAsUTF8 + 1, 0);
1063     CFStringGetCString(str, testURL.data(), maximumURLLengthAsUTF8, kCFStringEncodingUTF8);
1064
1065     CFRelease(url);
1066
1067     resetWebViewToConsistentStateBeforeTesting();
1068
1069     ::gTestRunner = TestRunner::create(testURL.data(), command.expectedPixelHash);
1070     ::gTestRunner->setCustomTimeout(command.timeout);
1071     topLoadingFrame = nullptr;
1072     done = false;
1073
1074     addFontFallbackIfPresent(fallbackPath);
1075
1076     sizeWebViewForCurrentTest();
1077     ::gTestRunner->setIconDatabaseEnabled(false);
1078     ::gTestRunner->clearAllApplicationCaches();
1079
1080     if (shouldLogFrameLoadDelegates(pathOrURL.c_str()))
1081         ::gTestRunner->setDumpFrameLoadCallbacks(true);
1082
1083     COMPtr<IWebView> webView;
1084     if (SUCCEEDED(frame->webView(&webView))) {
1085         COMPtr<IWebViewPrivate> viewPrivate;
1086         if (SUCCEEDED(webView->QueryInterface(&viewPrivate))) {
1087             if (shouldLogHistoryDelegates(pathOrURL.c_str())) {
1088                 ::gTestRunner->setDumpHistoryDelegateCallbacks(true);            
1089                 viewPrivate->setHistoryDelegate(sharedHistoryDelegate.get());
1090             } else
1091                 viewPrivate->setHistoryDelegate(nullptr);
1092         }
1093     }
1094
1095     if (shouldEnableDeveloperExtras(pathOrURL.c_str())) {
1096         ::gTestRunner->setDeveloperExtrasEnabled(true);
1097         if (shouldDumpAsText(pathOrURL.c_str())) {
1098             ::gTestRunner->setDumpAsText(true);
1099             ::gTestRunner->setGeneratePixelResults(false);
1100         }
1101     }
1102
1103     COMPtr<IWebHistory> history;
1104     if (SUCCEEDED(WebKitCreateInstance(CLSID_WebHistory, 0, __uuidof(history), reinterpret_cast<void**>(&history))))
1105         history->setOptionalSharedHistory(nullptr);
1106
1107     prevTestBFItem = nullptr;
1108     COMPtr<IWebBackForwardList> bfList;
1109     if (webView && SUCCEEDED(webView->backForwardList(&bfList)))
1110         bfList->currentItem(&prevTestBFItem);
1111
1112     auto& workQueue = WorkQueue::singleton();
1113     workQueue.clear();
1114     workQueue.setFrozen(false);
1115
1116     MSG msg = { 0 };
1117     HWND hostWindow;
1118     webView->hostWindow(&hostWindow);
1119
1120     COMPtr<IWebMutableURLRequest> request, emptyRequest;
1121     HRESULT hr = WebKitCreateInstance(CLSID_WebMutableURLRequest, 0, IID_IWebMutableURLRequest, (void**)&request);
1122     if (FAILED(hr))
1123         goto exit;
1124
1125     request->initWithURL(urlBStr, WebURLRequestUseProtocolCachePolicy, 60);
1126     request->setHTTPMethod(methodBStr);
1127     frame->loadRequest(request.get());
1128
1129     while (true) {
1130 #if USE(CF)
1131         CFRunLoopRunInMode(kCFRunLoopDefaultMode, 0, true);
1132 #endif
1133         if (!::GetMessage(&msg, 0, 0, 0))
1134             break;
1135
1136         // We get spurious WM_MOUSELEAVE events which make event handling machinery think that mouse button
1137         // is released during dragging (see e.g. fast\dynamic\layer-hit-test-crash.html).
1138         // Mouse can never leave WebView during normal DumpRenderTree operation, so we just ignore all such events.
1139         if (msg.message == WM_MOUSELEAVE)
1140             continue;
1141         ::TranslateMessage(&msg);
1142         ::DispatchMessage(&msg);
1143     }
1144
1145     // EventSendingController clearSavedEvents
1146     workQueue.clear();
1147
1148     if (::gTestRunner->closeRemainingWindowsWhenComplete()) {
1149         Vector<HWND> windows = openWindows();
1150         unsigned size = windows.size();
1151         for (unsigned i = 0; i < size; i++) {
1152             HWND window = windows[i];
1153
1154             // Don't try to close the main window
1155             if (window == hostWindow)
1156                 continue;
1157
1158             ::DestroyWindow(window);
1159         }
1160     }
1161
1162     if (shouldEnableDeveloperExtras(pathOrURL.c_str())) {
1163         ::gTestRunner->closeWebInspector();
1164         ::gTestRunner->setDeveloperExtrasEnabled(false);
1165     }
1166
1167     resetWebViewToConsistentStateBeforeTesting();
1168
1169     // Loading an empty request synchronously replaces the document with a blank one, which is necessary
1170     // to stop timers, WebSockets and other activity that could otherwise spill output into next test's results.
1171     if (SUCCEEDED(WebKitCreateInstance(CLSID_WebMutableURLRequest, 0, IID_IWebMutableURLRequest, (void**)&emptyRequest))) {
1172         _bstr_t emptyURL(L"");
1173         emptyRequest->initWithURL(emptyURL.GetBSTR(), WebURLRequestUseProtocolCachePolicy, 60);
1174         emptyRequest->setHTTPMethod(methodBStr);
1175         frame->loadRequest(request.get());
1176     }
1177
1178     frame->stopLoading();
1179
1180     // We should only have our main window left open when we're done
1181     ASSERT(openWindows().size() == 1);
1182     ASSERT(openWindows()[0] == hostWindow);
1183
1184 exit:
1185     removeFontFallbackIfPresent(fallbackPath);
1186     ::gTestRunner.clear();
1187
1188     fputs("#EOF\n", stderr);
1189     fflush(stderr);
1190 }
1191
1192 Vector<HWND>& openWindows()
1193 {
1194     static NeverDestroyed<Vector<HWND>> vector;
1195     return vector;
1196 }
1197
1198 WindowToWebViewMap& windowToWebViewMap()
1199 {
1200     static NeverDestroyed<WindowToWebViewMap> map;
1201     return map;
1202 }
1203
1204 IWebView* createWebViewAndOffscreenWindow(HWND* webViewWindow)
1205 {
1206     int maxViewWidth = TestRunner::viewWidth;
1207     int maxViewHeight = TestRunner::viewHeight;
1208     HWND hostWindow = (showWebView) ? CreateWindowEx(WS_EX_TOOLWINDOW, kDumpRenderTreeClassName, TEXT("DumpRenderTree"), WS_POPUP, 100, 100, maxViewWidth, maxViewHeight, 0, 0, ::GetModuleHandle(0), nullptr)
1209         : CreateWindowEx(WS_EX_TOOLWINDOW, kDumpRenderTreeClassName, TEXT("DumpRenderTree"), WS_POPUP, -maxViewWidth, -maxViewHeight, maxViewWidth, maxViewHeight, 0, 0, ::GetModuleHandle(0), nullptr);
1210
1211     IWebView* webView = nullptr;
1212     HRESULT hr = WebKitCreateInstance(CLSID_WebView, 0, IID_IWebView, (void**)&webView);
1213     if (FAILED(hr)) {
1214         fprintf(stderr, "Failed to create CLSID_WebView instance, error 0x%x\n", hr);
1215         return nullptr;
1216     }
1217
1218     if (FAILED(webView->setHostWindow(hostWindow)))
1219         return nullptr;
1220
1221     RECT clientRect;
1222     clientRect.bottom = clientRect.left = clientRect.top = clientRect.right = 0;
1223     _bstr_t groupName(L"org.webkit.DumpRenderTree");
1224     if (FAILED(webView->initWithFrame(clientRect, 0, groupName)))
1225         return nullptr;
1226
1227     COMPtr<IWebViewPrivate> viewPrivate;
1228     if (FAILED(webView->QueryInterface(&viewPrivate)))
1229         return nullptr;
1230
1231     viewPrivate->setShouldApplyMacFontAscentHack(TRUE);
1232     viewPrivate->setAlwaysUsesComplexTextCodePath(forceComplexText);
1233
1234     _bstr_t pluginPath = _bstr_t(exePath().data()) + TestPluginDir;
1235     if (FAILED(viewPrivate->addAdditionalPluginDirectory(pluginPath.GetBSTR())))
1236         return nullptr;
1237
1238     HWND viewWindow;
1239     if (FAILED(viewPrivate->viewWindow(&viewWindow)))
1240         return nullptr;
1241     if (webViewWindow)
1242         *webViewWindow = viewWindow;
1243
1244     ::SetWindowPos(viewWindow, 0, 0, 0, maxViewWidth, maxViewHeight, 0);
1245     ::ShowWindow(hostWindow, SW_SHOW);
1246
1247     if (FAILED(webView->setUIDelegate(sharedUIDelegate.get())))
1248         return nullptr;
1249
1250     if (FAILED(webView->setFrameLoadDelegate(sharedFrameLoadDelegate.get())))
1251         return nullptr;
1252
1253     if (FAILED(viewPrivate->setFrameLoadDelegatePrivate(sharedFrameLoadDelegate.get())))
1254         return nullptr;
1255
1256     COMPtr<IWebViewEditing> viewEditing;
1257     if (FAILED(webView->QueryInterface(&viewEditing)))
1258         return nullptr;
1259
1260     if (FAILED(viewEditing->setEditingDelegate(sharedEditingDelegate.get())))
1261         return nullptr;
1262
1263     if (FAILED(webView->setResourceLoadDelegate(resourceLoadDelegate.get())))
1264         return nullptr;
1265
1266     viewPrivate->setDefersCallbacks(FALSE);
1267
1268     openWindows().append(hostWindow);
1269     windowToWebViewMap().set(hostWindow, webView);
1270     return webView;
1271 }
1272
1273 #if USE(CFNETWORK)
1274 RetainPtr<CFURLCacheRef> sharedCFURLCache()
1275 {
1276 #ifndef DEBUG_ALL
1277     HMODULE module = GetModuleHandle(TEXT("CFNetwork.dll"));
1278 #else
1279     HMODULE module = GetModuleHandle(TEXT("CFNetwork_debug.dll"));
1280 #endif
1281     if (!module)
1282         return nullptr;
1283
1284     typedef CFURLCacheRef (*CFURLCacheCopySharedURLCacheProcPtr)(void);
1285     if (CFURLCacheCopySharedURLCacheProcPtr copyCache = reinterpret_cast<CFURLCacheCopySharedURLCacheProcPtr>(GetProcAddress(module, "CFURLCacheCopySharedURLCache")))
1286         return adoptCF(copyCache());
1287
1288     typedef CFURLCacheRef (*CFURLCacheSharedURLCacheProcPtr)(void);
1289     if (CFURLCacheSharedURLCacheProcPtr sharedCache = reinterpret_cast<CFURLCacheSharedURLCacheProcPtr>(GetProcAddress(module, "CFURLCacheSharedURLCache")))
1290         return sharedCache();
1291
1292     return nullptr;
1293 }
1294 #endif
1295
1296 static LONG WINAPI exceptionFilter(EXCEPTION_POINTERS*)
1297 {
1298     fputs("#CRASHED\n", stderr);
1299     fflush(stderr);
1300     return EXCEPTION_CONTINUE_SEARCH;
1301 }
1302
1303 static Vector<const char*> initializeGlobalsFromCommandLineOptions(int argc, const char* argv[])
1304 {
1305     Vector<const char*> tests;
1306
1307     for (int i = 1; i < argc; ++i) {
1308         if (!stricmp(argv[i], "--notree")) {
1309             dumpTree = false;
1310             continue;
1311         }
1312
1313         if (!stricmp(argv[i], "--pixel-tests")) {
1314             dumpPixelsForAllTests = true;
1315             continue;
1316         }
1317
1318         if (!stricmp(argv[i], "--tree")) {
1319             dumpTree = true;
1320             continue;
1321         }
1322
1323         if (!stricmp(argv[i], "--threaded")) {
1324             threaded = true;
1325             continue;
1326         }
1327
1328         if (!stricmp(argv[i], "--complex-text")) {
1329             forceComplexText = true;
1330             continue;
1331         }
1332
1333         if (!stricmp(argv[i], "--accelerated-drawing")) {
1334             useAcceleratedDrawing = true;
1335             continue;
1336         }
1337
1338         if (!stricmp(argv[i], "--gc-between-tests")) {
1339             gcBetweenTests = true;
1340             continue;
1341         }
1342
1343         if (!stricmp(argv[i], "--no-timeout")) {
1344             useTimeoutWatchdog = false;
1345             continue;
1346         }
1347
1348         if (!stricmp(argv[i], "--dump-all-pixels")) {
1349             dumpAllPixels = true;
1350             continue;
1351         }
1352
1353         if (!stricmp(argv[i], "--print-supported-features")) {
1354             printSupportedFeatures = true;
1355             continue;
1356         }
1357
1358         if (!stricmp(argv[i], "--show-webview")) {
1359             showWebView = true;
1360             continue;
1361         }
1362
1363         tests.append(argv[i]);
1364     }
1365
1366     return tests;
1367 }
1368
1369 static void allocateGlobalControllers()
1370 {
1371     sharedFrameLoadDelegate.adoptRef(new FrameLoadDelegate);
1372     sharedUIDelegate.adoptRef(new UIDelegate);
1373     sharedEditingDelegate.adoptRef(new EditingDelegate);
1374     resourceLoadDelegate.adoptRef(new ResourceLoadDelegate);
1375     policyDelegate = new PolicyDelegate();
1376     sharedHistoryDelegate.adoptRef(new HistoryDelegate);
1377     // storage delegate
1378     // policy delegate
1379 }
1380
1381 static void prepareConsistentTestingEnvironment(IWebPreferences* standardPreferences, IWebPreferencesPrivate* standardPreferencesPrivate)
1382 {
1383     ASSERT(standardPreferences);
1384     ASSERT(standardPreferencesPrivate);
1385     standardPreferences->setAutosaves(FALSE);
1386
1387     standardPreferences->setJavaScriptEnabled(TRUE);
1388     standardPreferences->setDefaultFontSize(16);
1389 #if USE(CG)
1390     standardPreferences->setAcceleratedCompositingEnabled(TRUE);
1391     standardPreferences->setAVFoundationEnabled(TRUE);
1392 #endif
1393
1394     allocateGlobalControllers();
1395 }
1396
1397 int main(int argc, const char* argv[])
1398 {
1399 #if defined(_M_X64) || defined(__x86_64__)
1400     // The VS2013 runtime has a bug where it mis-detects AVX-capable processors
1401     // if the feature has been disabled in firmware. This causes us to crash
1402     // in some of the math functions. For now, we disable those optimizations
1403     // because Microsoft is not going to fix the problem in VS2013.
1404     // FIXME: http://webkit.org/b/141449: Remove this workaround when we switch to VS2015+.
1405     _set_FMA3_enable(0);
1406 #endif
1407
1408     // Cygwin calls ::SetErrorMode(SEM_FAILCRITICALERRORS), which we will inherit. This is bad for
1409     // testing/debugging, as it causes the post-mortem debugger not to be invoked. We reset the
1410     // error mode here to work around Cygwin's behavior. See <http://webkit.org/b/55222>.
1411     ::SetErrorMode(0);
1412
1413     ::SetUnhandledExceptionFilter(exceptionFilter);
1414
1415     leakChecking = false;
1416
1417     _setmode(1, _O_BINARY);
1418     _setmode(2, _O_BINARY);
1419
1420     initialize();
1421
1422     setDefaultsToConsistentValuesForTesting();
1423
1424     Vector<const char*> tests = initializeGlobalsFromCommandLineOptions(argc, argv);
1425
1426     // FIXME - need to make DRT pass with Windows native controls <http://bugs.webkit.org/show_bug.cgi?id=25592>
1427     COMPtr<IWebPreferences> tmpPreferences;
1428     if (FAILED(WebKitCreateInstance(CLSID_WebPreferences, 0, IID_IWebPreferences, reinterpret_cast<void**>(&tmpPreferences))))
1429         return -1;
1430     COMPtr<IWebPreferences> standardPreferences;
1431     if (FAILED(tmpPreferences->standardPreferences(&standardPreferences)))
1432         return -2;
1433     COMPtr<IWebPreferencesPrivate> standardPreferencesPrivate;
1434     if (FAILED(standardPreferences->QueryInterface(&standardPreferencesPrivate)))
1435         return -3;
1436
1437     prepareConsistentTestingEnvironment(standardPreferences.get(), standardPreferencesPrivate.get());
1438
1439     if (printSupportedFeatures) {
1440         BOOL acceleratedCompositingAvailable;
1441         standardPreferences->acceleratedCompositingEnabled(&acceleratedCompositingAvailable);
1442
1443 #if ENABLE(3D_TRANSFORMS)
1444         // In theory, we could have a software-based 3D rendering implementation that we use when
1445         // hardware-acceleration is not available. But we don't have any such software
1446         // implementation, so 3D rendering is only available when hardware-acceleration is.
1447         BOOL threeDTransformsAvailable = acceleratedCompositingAvailable;
1448 #else
1449         BOOL threeDTransformsAvailable = FALSE;
1450 #endif
1451
1452         printf("SupportedFeatures:%s %s\n", acceleratedCompositingAvailable ? "AcceleratedCompositing" : "", threeDTransformsAvailable ? "3DTransforms" : "");
1453         return 0;
1454     }
1455
1456     COMPtr<IWebView> webView(AdoptCOM, createWebViewAndOffscreenWindow(&webViewWindow));
1457     if (!webView)
1458         return -4;
1459
1460     COMPtr<IWebIconDatabase> iconDatabase;
1461     COMPtr<IWebIconDatabase> tmpIconDatabase;
1462     if (FAILED(WebKitCreateInstance(CLSID_WebIconDatabase, 0, IID_IWebIconDatabase, (void**)&tmpIconDatabase)))
1463         return -5;
1464     if (FAILED(tmpIconDatabase->sharedIconDatabase(&iconDatabase)))
1465         return -6;
1466     if (FAILED(webView->mainFrame(&frame)))
1467         return -7;
1468
1469 #if USE(CFNETWORK)
1470     RetainPtr<CFURLCacheRef> urlCache = sharedCFURLCache();
1471     CFURLCacheRemoveAllCachedResponses(urlCache.get());
1472 #endif
1473
1474 #ifdef _DEBUG
1475     _CrtMemState entryToMainMemCheckpoint;
1476     if (leakChecking)
1477         _CrtMemCheckpoint(&entryToMainMemCheckpoint);
1478 #endif
1479
1480     if (threaded)
1481         startJavaScriptThreads();
1482
1483     if (tests.size() == 1 && !strcmp(tests[0], "-")) {
1484         char filenameBuffer[2048];
1485         printSeparators = true;
1486         while (fgets(filenameBuffer, sizeof(filenameBuffer), stdin)) {
1487             char* newLineCharacter = strchr(filenameBuffer, '\n');
1488             if (newLineCharacter)
1489                 *newLineCharacter = '\0';
1490             
1491             if (strlen(filenameBuffer) == 0)
1492                 continue;
1493
1494             runTest(filenameBuffer);
1495         }
1496     } else {
1497         printSeparators = tests.size() > 1;
1498         for (int i = 0; i < tests.size(); i++)
1499             runTest(tests[i]);
1500     }
1501
1502     if (threaded)
1503         stopJavaScriptThreads();
1504     
1505     delete policyDelegate;
1506     frame->Release();
1507
1508 #ifdef _DEBUG
1509     if (leakChecking) {
1510         // dump leaks to stderr
1511         _CrtSetReportMode(_CRT_WARN, _CRTDBG_MODE_FILE);
1512         _CrtSetReportFile(_CRT_WARN, _CRTDBG_FILE_STDERR);
1513         _CrtMemDumpAllObjectsSince(&entryToMainMemCheckpoint);
1514     }
1515 #endif
1516
1517     shutDownWebKit();
1518 #ifdef _CRTDBG_MAP_ALLOC
1519     _CrtDumpMemoryLeaks();
1520 #endif
1521
1522     ::OleUninitialize();
1523
1524     return 0;
1525 }
1526
1527 extern "C" __declspec(dllexport) int WINAPI dllLauncherEntryPoint(int argc, const char* argv[])
1528 {
1529     return main(argc, argv);
1530 }