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