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