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