REGRESSION: editing/async-clipboard/clipboard-interfaces.html is failing in WebKit1
[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     prefsPrivate->setAsyncClipboardAPIEnabled(options.enableAsyncClipboardAPI);
916     preferences->setPrivateBrowsingEnabled(options.useEphemeralSession);
917     preferences->setUsesPageCache(options.enablePageCache);
918 }
919
920 static String applicationId()
921 {
922     return makeString("com.apple.DumpRenderTree.", ::GetCurrentProcessId());
923 }
924
925 static void setApplicationId()
926 {
927     COMPtr<IWebPreferences> preferences;
928     if (SUCCEEDED(WebKitCreateInstance(CLSID_WebPreferences, 0, IID_IWebPreferences, (void**)&preferences))) {
929         COMPtr<IWebPreferencesPrivate4> prefsPrivate4(Query, preferences);
930         ASSERT(prefsPrivate4);
931         _bstr_t fileName = applicationId().wideCharacters().data();
932         prefsPrivate4->setApplicationId(fileName);
933     }
934 }
935
936 // Called once on DumpRenderTree startup.
937 static void setDefaultsToConsistentValuesForTesting()
938 {
939 #if USE(CF)
940     // Create separate preferences for each DRT instance
941     setApplicationId();
942
943     RetainPtr<CFStringRef> appId = applicationId().createCFString();
944
945     String libraryPath = libraryPathForDumpRenderTree();
946
947     // Set up these values before creating the WebView so that the various initializations will see these preferred values.
948     CFPreferencesSetAppValue(WebDatabaseDirectoryDefaultsKey, FileSystem::pathByAppendingComponent(libraryPath, "Databases").createCFString().get(), appId.get());
949     CFPreferencesSetAppValue(WebStorageDirectoryDefaultsKey, FileSystem::pathByAppendingComponent(libraryPath, "LocalStorage").createCFString().get(), appId.get());
950     CFPreferencesSetAppValue(WebKitLocalCacheDefaultsKey, FileSystem::pathByAppendingComponent(libraryPath, "LocalCache").createCFString().get(), appId.get());
951 #endif
952 }
953
954 static void setJSCOptions(const TestOptions& options)
955 {
956     static WTF::StringBuilder savedOptions;
957
958     if (!savedOptions.isEmpty()) {
959         JSC::Options::setOptions(savedOptions.toString().ascii().data());
960         savedOptions.clear();
961     }
962
963     if (options.jscOptions.length()) {
964         JSC::Options::dumpAllOptionsInALine(savedOptions);
965         JSC::Options::setOptions(options.jscOptions.c_str());
966     }
967 }
968
969 static void resetWebViewToConsistentStateBeforeTesting(const TestOptions& options)
970 {
971     setJSCOptions(options);
972
973     COMPtr<IWebView> webView;
974     if (FAILED(frame->webView(&webView))) 
975         return;
976
977     COMPtr<IWebViewEditing> viewEditing;
978     if (SUCCEEDED(webView->QueryInterface(&viewEditing)) && viewEditing) {
979
980         viewEditing->setEditable(FALSE);
981
982         COMPtr<IWebEditingDelegate> delegate;
983         if (SUCCEEDED(viewEditing->editingDelegate(&delegate)) && delegate) {
984             COMPtr<EditingDelegate> editingDelegate(Query, viewEditing.get());
985             if (editingDelegate)
986                 editingDelegate->setAcceptsEditing(TRUE);
987         }
988     }
989
990     COMPtr<IWebIBActions> webIBActions(Query, webView);
991     if (webIBActions) {
992         webIBActions->makeTextStandardSize(0);
993         webIBActions->resetPageZoom(0);
994     }
995
996     COMPtr<IWebViewPrivate2> webViewPrivate(Query, webView);
997     if (webViewPrivate) {
998         POINT origin = { 0, 0 };
999         webViewPrivate->scaleWebView(1.0, origin);
1000         webViewPrivate->setCustomBackingScaleFactor(0);
1001         webViewPrivate->setTabKeyCyclesThroughElements(TRUE);
1002     }
1003
1004     webView->setPolicyDelegate(DefaultPolicyDelegate::sharedInstance());
1005     policyDelegate->setPermissive(false);
1006     policyDelegate->setControllerToNotifyDone(nullptr);
1007     sharedFrameLoadDelegate->resetToConsistentState();
1008
1009     if (webViewPrivate) {
1010         webViewPrivate->clearMainFrameName();
1011         _bstr_t groupName;
1012         if (SUCCEEDED(webView->groupName(&groupName.GetBSTR())))
1013             webViewPrivate->removeAllUserContentFromGroup(groupName);
1014     }
1015
1016     COMPtr<IWebPreferences> preferences;
1017     if (SUCCEEDED(webView->preferences(&preferences))) {
1018         resetWebPreferencesToConsistentValues(preferences.get());
1019         setWebPreferencesForTestOptions(preferences.get(), options);
1020     }
1021
1022     TestRunner::setSerializeHTTPLoads(false);
1023
1024     setlocale(LC_ALL, "");
1025
1026     if (::gTestRunner) {
1027         JSGlobalContextRef context = frame->globalContext();
1028         WebCoreTestSupport::resetInternalsObject(context);
1029     }
1030
1031     if (preferences) {
1032         preferences->setContinuousSpellCheckingEnabled(TRUE);
1033         // Automatic Quote Subs
1034         // Automatic Link Detection
1035         // Autommatic Dash substitution
1036         // Automatic Spell Check
1037         preferences->setGrammarCheckingEnabled(TRUE);
1038         // Use Test Mode Focus Ring
1039     }
1040
1041     HWND viewWindow;
1042     if (webViewPrivate && SUCCEEDED(webViewPrivate->viewWindow(&viewWindow)) && viewWindow)
1043         ::SetFocus(viewWindow);
1044
1045     webViewPrivate->resetOriginAccessWhitelists();
1046
1047     sharedUIDelegate->resetUndoManager();
1048
1049     COMPtr<IWebFramePrivate> framePrivate;
1050     if (SUCCEEDED(frame->QueryInterface(&framePrivate)))
1051         framePrivate->clearOpener();
1052
1053     COMPtr<IWebViewPrivate5> webViewPrivate5(Query, webView);
1054     webViewPrivate5->exitFullscreenIfNeeded();
1055 }
1056
1057 static void sizeWebViewForCurrentTest()
1058 {
1059     bool isSVGW3CTest = (::gTestRunner->testURL().find("svg\\W3C-SVG-1.1") != string::npos)
1060         || (::gTestRunner->testURL().find("svg/W3C-SVG-1.1") != string::npos);
1061     unsigned width = isSVGW3CTest ? TestRunner::w3cSVGViewWidth : TestRunner::viewWidth;
1062     unsigned height = isSVGW3CTest ? TestRunner::w3cSVGViewHeight : TestRunner::viewHeight;
1063
1064     ::SetWindowPos(webViewWindow, 0, 0, 0, width, height, SWP_NOMOVE);
1065 }
1066
1067 static String findFontFallback(const char* pathOrUrl)
1068 {
1069     String pathToFontFallback = FileSystem::directoryName(pathOrUrl);
1070
1071     wchar_t fullPath[_MAX_PATH];
1072     if (!_wfullpath(fullPath, pathToFontFallback.wideCharacters().data(), _MAX_PATH))
1073         return emptyString();
1074
1075     if (!::PathIsDirectoryW(fullPath))
1076         return emptyString();
1077
1078     String pathToCheck = fullPath;
1079
1080     static const String layoutTests = "LayoutTests";
1081
1082     // Find the layout test root on the current path:
1083     size_t location = pathToCheck.find(layoutTests);
1084     if (WTF::notFound == location)
1085         return emptyString();
1086
1087     String pathToTest = pathToCheck.substring(location + layoutTests.length() + 1);
1088     String possiblePathToLogue = FileSystem::pathByAppendingComponent(pathToCheck.substring(0, location + layoutTests.length() + 1), "platform\\win");
1089
1090     Vector<String> possiblePaths;
1091     possiblePaths.append(FileSystem::pathByAppendingComponent(possiblePathToLogue, pathToTest));
1092
1093     size_t nextCandidateEnd = pathToTest.reverseFind('\\');
1094     while (nextCandidateEnd && nextCandidateEnd != WTF::notFound) {
1095         pathToTest = pathToTest.substring(0, nextCandidateEnd);
1096         possiblePaths.append(FileSystem::pathByAppendingComponent(possiblePathToLogue, pathToTest));
1097         nextCandidateEnd = pathToTest.reverseFind('\\');
1098     }
1099
1100     for (Vector<String>::iterator pos = possiblePaths.begin(); pos != possiblePaths.end(); ++pos) {
1101         pathToFontFallback = FileSystem::pathByAppendingComponent(*pos, "resources\\");
1102
1103         if (::PathIsDirectoryW(pathToFontFallback.wideCharacters().data()))
1104             return pathToFontFallback;
1105     }
1106
1107     return emptyString();
1108 }
1109
1110 static void addFontFallbackIfPresent(const String& fontFallbackPath)
1111 {
1112     if (fontFallbackPath.isEmpty())
1113         return;
1114
1115     String fontFallback = FileSystem::pathByAppendingComponent(fontFallbackPath, "Mac-compatible-font-fallback.css");
1116
1117     if (!::PathFileExistsW(fontFallback.wideCharacters().data()))
1118         return;
1119
1120     ::setPersistentUserStyleSheetLocation(fontFallback.createCFString().get());
1121 }
1122
1123 static void removeFontFallbackIfPresent(const String& fontFallbackPath)
1124 {
1125     if (fontFallbackPath.isEmpty())
1126         return;
1127
1128     String fontFallback = FileSystem::pathByAppendingComponent(fontFallbackPath, "Mac-compatible-font-fallback.css");
1129
1130     if (!::PathFileExistsW(fontFallback.wideCharacters().data()))
1131         return;
1132
1133     ::setPersistentUserStyleSheetLocation(nullptr);
1134 }
1135
1136 static bool handleControlCommand(const char* command)
1137 {
1138     if (!strcmp("#CHECK FOR ABANDONED DOCUMENTS", command)) {
1139         // DumpRenderTree does not support checking for abandonded documents.
1140         String result("\n");
1141         printf("Content-Type: text/plain\n");
1142         printf("Content-Length: %u\n", result.length());
1143         fwrite(result.utf8().data(), 1, result.length(), stdout);
1144         printf("#EOF\n");
1145         fprintf(stderr, "#EOF\n");
1146         fflush(stdout);
1147         fflush(stderr);
1148         return true;
1149     }
1150     return false;
1151 }
1152
1153 static void runTest(const string& inputLine)
1154 {
1155     ASSERT(!inputLine.empty());
1156
1157     TestCommand command = parseInputLine(inputLine);
1158     const string& pathOrURL = command.pathOrURL;
1159     dumpPixelsForCurrentTest = command.shouldDumpPixels || dumpPixelsForAllTests;
1160
1161     static _bstr_t methodBStr(TEXT("GET"));
1162
1163     CFStringRef str = CFStringCreateWithCString(0, pathOrURL.c_str(), kCFStringEncodingWindowsLatin1);
1164     if (!str) {
1165         fprintf(stderr, "Failed to parse \"%s\" as UTF-8\n", pathOrURL.c_str());
1166         return;
1167     }
1168
1169     CFURLRef url = CFURLCreateWithString(0, str, 0);
1170
1171     if (!url)
1172         url = CFURLCreateWithFileSystemPath(0, str, kCFURLWindowsPathStyle, false);
1173
1174     CFRelease(str);
1175
1176     if (!url) {
1177         fprintf(stderr, "Failed to parse \"%s\" as a URL\n", pathOrURL.c_str());
1178         return;
1179     }
1180
1181     String hostName = String(adoptCF(CFURLCopyHostName(url)).get());
1182
1183     String fallbackPath = findFontFallback(pathOrURL.c_str());
1184
1185     str = CFURLGetString(url);
1186
1187     CFIndex length = CFStringGetLength(str);
1188
1189     Vector<UniChar> buffer(length + 1, 0);
1190     CFStringGetCharacters(str, CFRangeMake(0, length), buffer.data());
1191
1192     _bstr_t urlBStr(reinterpret_cast<wchar_t*>(buffer.data()));
1193     ASSERT(urlBStr.length() == length);
1194
1195     CFIndex maximumURLLengthAsUTF8 = CFStringGetMaximumSizeForEncoding(length, kCFStringEncodingUTF8) + 1;
1196     Vector<char> testURL(maximumURLLengthAsUTF8 + 1, 0);
1197     CFStringGetCString(str, testURL.data(), maximumURLLengthAsUTF8, kCFStringEncodingUTF8);
1198
1199     CFRelease(url);
1200
1201     TestOptions options { command.pathOrURL, command.absolutePath };
1202
1203     resetWebViewToConsistentStateBeforeTesting(options);
1204
1205     ::gTestRunner = TestRunner::create(testURL.data(), command.expectedPixelHash);
1206     ::gTestRunner->setCustomTimeout(command.timeout);
1207     ::gTestRunner->setDumpJSConsoleLogInStdErr(command.dumpJSConsoleLogInStdErr || options.dumpJSConsoleLogInStdErr);
1208
1209     topLoadingFrame = nullptr;
1210     done = false;
1211
1212     addFontFallbackIfPresent(fallbackPath);
1213
1214     sizeWebViewForCurrentTest();
1215     ::gTestRunner->setIconDatabaseEnabled(false);
1216     ::gTestRunner->clearAllApplicationCaches();
1217
1218     if (shouldLogFrameLoadDelegates(pathOrURL.c_str()))
1219         ::gTestRunner->setDumpFrameLoadCallbacks(true);
1220
1221     COMPtr<IWebView> webView;
1222     if (SUCCEEDED(frame->webView(&webView))) {
1223         COMPtr<IWebViewPrivate2> viewPrivate;
1224         if (SUCCEEDED(webView->QueryInterface(&viewPrivate))) {
1225             if (shouldLogHistoryDelegates(pathOrURL.c_str())) {
1226                 ::gTestRunner->setDumpHistoryDelegateCallbacks(true);            
1227                 viewPrivate->setHistoryDelegate(sharedHistoryDelegate.get());
1228             } else
1229                 viewPrivate->setHistoryDelegate(nullptr);
1230         }
1231     }
1232
1233     if (shouldEnableDeveloperExtras(pathOrURL.c_str())) {
1234         ::gTestRunner->setDeveloperExtrasEnabled(true);
1235         if (shouldDumpAsText(pathOrURL.c_str())) {
1236             ::gTestRunner->setDumpAsText(true);
1237             ::gTestRunner->setGeneratePixelResults(false);
1238         }
1239     }
1240
1241     COMPtr<IWebHistory> history;
1242     if (SUCCEEDED(WebKitCreateInstance(CLSID_WebHistory, 0, __uuidof(history), reinterpret_cast<void**>(&history))))
1243         history->setOptionalSharedHistory(nullptr);
1244
1245     prevTestBFItem = nullptr;
1246     COMPtr<IWebBackForwardList> bfList;
1247     if (webView && SUCCEEDED(webView->backForwardList(&bfList)))
1248         bfList->currentItem(&prevTestBFItem);
1249
1250     auto& workQueue = DRT::WorkQueue::singleton();
1251     workQueue.clear();
1252     workQueue.setFrozen(false);
1253
1254     MSG msg { };
1255     HWND hostWindow;
1256     webView->hostWindow(&hostWindow);
1257
1258     COMPtr<IWebMutableURLRequest> request, emptyRequest;
1259     HRESULT hr = WebKitCreateInstance(CLSID_WebMutableURLRequest, 0, IID_IWebMutableURLRequest, (void**)&request);
1260     if (FAILED(hr))
1261         goto exit;
1262
1263     request->initWithURL(urlBStr, WebURLRequestUseProtocolCachePolicy, 60);
1264     request->setHTTPMethod(methodBStr);
1265     if (hostName == "localhost" || hostName == "127.0.0.1")
1266         request->setAllowsAnyHTTPSCertificate();
1267     frame->loadRequest(request.get());
1268
1269     while (!done) {
1270 #if USE(CF)
1271         CFRunLoopRunInMode(kCFRunLoopDefaultMode, 0, true);
1272 #endif
1273         if (!::GetMessage(&msg, 0, 0, 0))
1274             break;
1275
1276         // We get spurious WM_MOUSELEAVE events which make event handling machinery think that mouse button
1277         // is released during dragging (see e.g. fast\dynamic\layer-hit-test-crash.html).
1278         // Mouse can never leave WebView during normal DumpRenderTree operation, so we just ignore all such events.
1279         if (msg.message == WM_MOUSELEAVE)
1280             continue;
1281         ::TranslateMessage(&msg);
1282         ::DispatchMessage(&msg);
1283     }
1284
1285     // EventSendingController clearSavedEvents
1286     workQueue.clear();
1287
1288     // If the test page could have possibly opened the Web Inspector frontend,
1289     // then try to close it in case it was accidentally left open.
1290     if (shouldEnableDeveloperExtras(pathOrURL.c_str())) {
1291         ::gTestRunner->closeWebInspector();
1292         ::gTestRunner->setDeveloperExtrasEnabled(false);
1293     }
1294
1295     if (::gTestRunner->closeRemainingWindowsWhenComplete()) {
1296         Vector<HWND> windows = openWindows();
1297         unsigned size = windows.size();
1298         for (unsigned i = 0; i < size; i++) {
1299             HWND window = windows[i];
1300
1301             // Don't try to close the main window
1302             if (window == hostWindow)
1303                 continue;
1304
1305             ::DestroyWindow(window);
1306         }
1307     }
1308
1309     resetWebViewToConsistentStateBeforeTesting(options);
1310
1311     // Loading an empty request synchronously replaces the document with a blank one, which is necessary
1312     // to stop timers, WebSockets and other activity that could otherwise spill output into next test's results.
1313     if (SUCCEEDED(WebKitCreateInstance(CLSID_WebMutableURLRequest, 0, IID_IWebMutableURLRequest, (void**)&emptyRequest))) {
1314         _bstr_t emptyURL(L"");
1315         emptyRequest->initWithURL(emptyURL.GetBSTR(), WebURLRequestUseProtocolCachePolicy, 60);
1316         emptyRequest->setHTTPMethod(methodBStr);
1317         frame->loadRequest(emptyRequest.get());
1318     }
1319
1320     // We should only have our main window left open when we're done
1321     ASSERT(openWindows().size() == 1);
1322     ASSERT(openWindows()[0] == hostWindow);
1323
1324 exit:
1325     removeFontFallbackIfPresent(fallbackPath);
1326     ::gTestRunner->cleanup();
1327     ::gTestRunner = nullptr;
1328
1329     if (gcBetweenTests) {
1330         GCController gcController;
1331         gcController.collect();
1332     }
1333     JSC::waitForVMDestruction();
1334
1335     fputs("#EOF\n", stderr);
1336     fflush(stderr);
1337 }
1338
1339 Vector<HWND>& openWindows()
1340 {
1341     static NeverDestroyed<Vector<HWND>> vector;
1342     return vector;
1343 }
1344
1345 WindowToWebViewMap& windowToWebViewMap()
1346 {
1347     static NeverDestroyed<WindowToWebViewMap> map;
1348     return map;
1349 }
1350
1351 IWebView* createWebViewAndOffscreenWindow(HWND* webViewWindow)
1352 {
1353     int maxViewWidth = TestRunner::viewWidth;
1354     int maxViewHeight = TestRunner::viewHeight;
1355     HWND hostWindow = (showWebView) ? CreateWindowEx(WS_EX_TOOLWINDOW, kDumpRenderTreeClassName, TEXT("DumpRenderTree"), WS_POPUP, 100, 100, maxViewWidth, maxViewHeight, 0, 0, ::GetModuleHandle(0), nullptr)
1356         : CreateWindowEx(WS_EX_TOOLWINDOW, kDumpRenderTreeClassName, TEXT("DumpRenderTree"), WS_POPUP, -maxViewWidth, -maxViewHeight, maxViewWidth, maxViewHeight, 0, 0, ::GetModuleHandle(0), nullptr);
1357
1358     IWebView* webView = nullptr;
1359     HRESULT hr = WebKitCreateInstance(CLSID_WebView, 0, IID_IWebView, (void**)&webView);
1360     if (FAILED(hr)) {
1361         fprintf(stderr, "Failed to create CLSID_WebView instance, error 0x%lx\n", hr);
1362         return nullptr;
1363     }
1364
1365     if (FAILED(webView->setHostWindow(hostWindow)))
1366         return nullptr;
1367
1368     RECT clientRect;
1369     clientRect.bottom = clientRect.left = clientRect.top = clientRect.right = 0;
1370     _bstr_t groupName(L"org.webkit.DumpRenderTree");
1371     if (FAILED(webView->initWithFrame(clientRect, 0, groupName)))
1372         return nullptr;
1373
1374     COMPtr<IWebViewPrivate2> viewPrivate;
1375     if (FAILED(webView->QueryInterface(&viewPrivate)))
1376         return nullptr;
1377
1378     viewPrivate->setShouldApplyMacFontAscentHack(TRUE);
1379     viewPrivate->setAlwaysUsesComplexTextCodePath(forceComplexText);
1380     viewPrivate->setCustomBackingScaleFactor(1.0);
1381
1382     _bstr_t pluginPath = _bstr_t(exePath().data()) + TestPluginDir;
1383     if (FAILED(viewPrivate->addAdditionalPluginDirectory(pluginPath.GetBSTR())))
1384         return nullptr;
1385
1386     HWND viewWindow;
1387     if (FAILED(viewPrivate->viewWindow(&viewWindow)))
1388         return nullptr;
1389     if (webViewWindow)
1390         *webViewWindow = viewWindow;
1391
1392     ::SetWindowPos(viewWindow, 0, 0, 0, maxViewWidth, maxViewHeight, 0);
1393     ::ShowWindow(hostWindow, SW_SHOW);
1394
1395     if (FAILED(webView->setUIDelegate(sharedUIDelegate.get())))
1396         return nullptr;
1397
1398     if (FAILED(webView->setFrameLoadDelegate(sharedFrameLoadDelegate.get())))
1399         return nullptr;
1400
1401     if (FAILED(viewPrivate->setFrameLoadDelegatePrivate(sharedFrameLoadDelegate.get())))
1402         return nullptr;
1403
1404     COMPtr<IWebViewEditing> viewEditing;
1405     if (FAILED(webView->QueryInterface(&viewEditing)))
1406         return nullptr;
1407
1408     if (FAILED(viewEditing->setEditingDelegate(sharedEditingDelegate.get())))
1409         return nullptr;
1410
1411     if (FAILED(webView->setResourceLoadDelegate(resourceLoadDelegate.get())))
1412         return nullptr;
1413
1414     viewPrivate->setDefersCallbacks(FALSE);
1415
1416     openWindows().append(hostWindow);
1417     windowToWebViewMap().set(hostWindow, webView);
1418     return webView;
1419 }
1420
1421 #if USE(CFURLCONNECTION)
1422 RetainPtr<CFURLCacheRef> sharedCFURLCache()
1423 {
1424 #ifndef DEBUG_ALL
1425     HMODULE module = GetModuleHandle(TEXT("CFNetwork.dll"));
1426 #else
1427     HMODULE module = GetModuleHandle(TEXT("CFNetwork_debug.dll"));
1428 #endif
1429     if (!module)
1430         return nullptr;
1431
1432     typedef CFURLCacheRef (*CFURLCacheCopySharedURLCacheProcPtr)(void);
1433     if (CFURLCacheCopySharedURLCacheProcPtr copyCache = reinterpret_cast<CFURLCacheCopySharedURLCacheProcPtr>(GetProcAddress(module, "CFURLCacheCopySharedURLCache")))
1434         return adoptCF(copyCache());
1435
1436     typedef CFURLCacheRef (*CFURLCacheSharedURLCacheProcPtr)(void);
1437     if (CFURLCacheSharedURLCacheProcPtr sharedCache = reinterpret_cast<CFURLCacheSharedURLCacheProcPtr>(GetProcAddress(module, "CFURLCacheSharedURLCache")))
1438         return sharedCache();
1439
1440     return nullptr;
1441 }
1442 #endif
1443
1444 static Vector<const char*> initializeGlobalsFromCommandLineOptions(int argc, const char* argv[])
1445 {
1446     Vector<const char*> tests;
1447
1448     for (int i = 1; i < argc; ++i) {
1449         if (!stricmp(argv[i], "--notree")) {
1450             dumpTree = false;
1451             continue;
1452         }
1453
1454         if (!stricmp(argv[i], "--pixel-tests")) {
1455             dumpPixelsForAllTests = true;
1456             continue;
1457         }
1458
1459         if (!stricmp(argv[i], "--tree")) {
1460             dumpTree = true;
1461             continue;
1462         }
1463
1464         if (!stricmp(argv[i], "--threaded")) {
1465             threaded = true;
1466             continue;
1467         }
1468
1469         if (!stricmp(argv[i], "--complex-text")) {
1470             forceComplexText = true;
1471             continue;
1472         }
1473
1474         if (!stricmp(argv[i], "--accelerated-drawing")) {
1475             useAcceleratedDrawing = true;
1476             continue;
1477         }
1478
1479         if (!stricmp(argv[i], "--gc-between-tests")) {
1480             gcBetweenTests = true;
1481             continue;
1482         }
1483
1484         if (!stricmp(argv[i], "--no-timeout")) {
1485             useTimeoutWatchdog = false;
1486             continue;
1487         }
1488
1489         if (!stricmp(argv[i], "--dump-all-pixels")) {
1490             dumpAllPixels = true;
1491             continue;
1492         }
1493
1494         if (!stricmp(argv[i], "--show-webview")) {
1495             showWebView = true;
1496             continue;
1497         }
1498
1499         tests.append(argv[i]);
1500     }
1501
1502     return tests;
1503 }
1504
1505 static void allocateGlobalControllers()
1506 {
1507     sharedFrameLoadDelegate.adoptRef(new FrameLoadDelegate);
1508     sharedUIDelegate.adoptRef(new UIDelegate);
1509     sharedEditingDelegate.adoptRef(new EditingDelegate);
1510     resourceLoadDelegate.adoptRef(new ResourceLoadDelegate);
1511     policyDelegate = new PolicyDelegate();
1512     sharedHistoryDelegate.adoptRef(new HistoryDelegate);
1513     // storage delegate
1514     // policy delegate
1515 }
1516
1517 static void prepareConsistentTestingEnvironment(IWebPreferences* standardPreferences, IWebPreferencesPrivate* standardPreferencesPrivate)
1518 {
1519     ASSERT(standardPreferences);
1520     ASSERT(standardPreferencesPrivate);
1521     standardPreferences->setAutosaves(FALSE);
1522
1523 #if USE(CFURLCONNECTION)
1524     auto newCache = adoptCF(CFURLCacheCreate(kCFAllocatorDefault, 1024 * 1024, 0, nullptr));
1525     CFURLCacheSetSharedURLCache(newCache.get());
1526 #endif
1527
1528     COMPtr<IWebPreferencesPrivate4> prefsPrivate4(Query, standardPreferences);
1529     prefsPrivate4->switchNetworkLoaderToNewTestingSession();
1530
1531     standardPreferences->setJavaScriptEnabled(TRUE);
1532     standardPreferences->setDefaultFontSize(16);
1533 #if USE(CG)
1534     standardPreferences->setAcceleratedCompositingEnabled(TRUE);
1535     standardPreferences->setAVFoundationEnabled(TRUE);
1536 #endif
1537
1538     allocateGlobalControllers();
1539 }
1540
1541 int main(int argc, const char* argv[])
1542 {
1543     // Cygwin calls ::SetErrorMode(SEM_FAILCRITICALERRORS), which we will inherit. This is bad for
1544     // testing/debugging, as it causes the post-mortem debugger not to be invoked. We reset the
1545     // error mode here to work around Cygwin's behavior. See <http://webkit.org/b/55222>.
1546     ::SetErrorMode(0);
1547
1548     leakChecking = false;
1549
1550     _setmode(1, _O_BINARY);
1551     _setmode(2, _O_BINARY);
1552
1553     // Some tests are flaky because certain DLLs are writing to stdout, giving incorrect test results.
1554     // We work around that here by duplicating and redirecting stdout.
1555     int fdStdout = _dup(1);
1556     _setmode(fdStdout, _O_BINARY);
1557     testResult = fdopen(fdStdout, "a+b");
1558     // Redirect stdout to stderr.
1559     int result = _dup2(_fileno(stderr), 1);
1560     if (result)
1561         return -5;
1562
1563     // Tests involving the clipboard are flaky when running with multiple DRTs, since the clipboard is global.
1564     // We can fix this by assigning each DRT a separate window station (each window station has its own clipboard).
1565     DWORD processId = ::GetCurrentProcessId();
1566     String windowStationName = makeString("windowStation", processId);
1567     String desktopName = makeString("desktop", processId);
1568     HDESK desktop = nullptr;
1569
1570     auto windowsStation = ::CreateWindowStation(windowStationName.wideCharacters().data(), CWF_CREATE_ONLY, WINSTA_ALL_ACCESS, nullptr);
1571     if (windowsStation) {
1572         if (!::SetProcessWindowStation(windowsStation))
1573             fprintf(stderr, "SetProcessWindowStation failed with error %lu\n", ::GetLastError());
1574
1575         desktop = ::CreateDesktop(desktopName.wideCharacters().data(), nullptr, nullptr, 0, GENERIC_ALL, nullptr);
1576         if (!desktop)
1577             fprintf(stderr, "Failed to create desktop with error %lu\n", ::GetLastError());
1578     } else {
1579         DWORD error = ::GetLastError();
1580         fprintf(stderr, "Failed to create window station with error %lu\n", error);
1581         if (error == ERROR_ACCESS_DENIED)
1582             fprintf(stderr, "DumpRenderTree should be run as Administrator!\n");
1583     }
1584
1585     initialize();
1586
1587     setDefaultsToConsistentValuesForTesting();
1588
1589     Vector<const char*> tests = initializeGlobalsFromCommandLineOptions(argc, argv);
1590
1591     // FIXME - need to make DRT pass with Windows native controls <http://bugs.webkit.org/show_bug.cgi?id=25592>
1592     COMPtr<IWebPreferences> tmpPreferences;
1593     if (FAILED(WebKitCreateInstance(CLSID_WebPreferences, 0, IID_IWebPreferences, reinterpret_cast<void**>(&tmpPreferences))))
1594         return -1;
1595     COMPtr<IWebPreferences> standardPreferences;
1596     if (FAILED(tmpPreferences->standardPreferences(&standardPreferences)))
1597         return -2;
1598     COMPtr<IWebPreferencesPrivate> standardPreferencesPrivate;
1599     if (FAILED(standardPreferences->QueryInterface(&standardPreferencesPrivate)))
1600         return -3;
1601
1602     prepareConsistentTestingEnvironment(standardPreferences.get(), standardPreferencesPrivate.get());
1603
1604     COMPtr<IWebView> webView(AdoptCOM, createWebViewAndOffscreenWindow(&webViewWindow));
1605     if (!webView)
1606         return -4;
1607
1608     if (FAILED(webView->mainFrame(&frame)))
1609         return -7;
1610
1611 #if USE(CFURLCONNECTION)
1612     RetainPtr<CFURLCacheRef> urlCache = sharedCFURLCache();
1613     CFURLCacheRemoveAllCachedResponses(urlCache.get());
1614 #endif
1615
1616 #ifdef _DEBUG
1617     _CrtMemState entryToMainMemCheckpoint;
1618     if (leakChecking)
1619         _CrtMemCheckpoint(&entryToMainMemCheckpoint);
1620 #endif
1621
1622     if (threaded)
1623         startJavaScriptThreads();
1624
1625     if (tests.size() == 1 && !strcmp(tests[0], "-")) {
1626         char filenameBuffer[2048];
1627         printSeparators = true;
1628         while (fgets(filenameBuffer, sizeof(filenameBuffer), stdin)) {
1629             char* newLineCharacter = strchr(filenameBuffer, '\n');
1630             if (newLineCharacter)
1631                 *newLineCharacter = '\0';
1632             
1633             if (strlen(filenameBuffer) == 0)
1634                 continue;
1635
1636             if (handleControlCommand(filenameBuffer))
1637                 continue;
1638
1639             runTest(filenameBuffer);
1640         }
1641     } else {
1642         printSeparators = tests.size() > 1;
1643         for (int i = 0; i < tests.size(); i++)
1644             runTest(tests[i]);
1645     }
1646
1647     if (threaded)
1648         stopJavaScriptThreads();
1649     
1650     delete policyDelegate;
1651     frame->Release();
1652
1653 #ifdef _DEBUG
1654     if (leakChecking) {
1655         // dump leaks to stderr
1656         _CrtSetReportMode(_CRT_WARN, _CRTDBG_MODE_FILE);
1657         _CrtSetReportFile(_CRT_WARN, _CRTDBG_FILE_STDERR);
1658         _CrtMemDumpAllObjectsSince(&entryToMainMemCheckpoint);
1659     }
1660 #endif
1661
1662     shutDownWebKit();
1663 #ifdef _CRTDBG_MAP_ALLOC
1664     _CrtDumpMemoryLeaks();
1665 #endif
1666
1667     if (desktop)
1668         ::CloseDesktop(desktop);
1669     if (windowsStation)
1670         ::CloseWindowStation(windowsStation);
1671
1672     ::OleUninitialize();
1673
1674     return 0;
1675 }
1676
1677 extern "C" __declspec(dllexport) int WINAPI dllLauncherEntryPoint(int argc, const char* argv[])
1678 {
1679     return main(argc, argv);
1680 }