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