a7dff05f17ee72fff6fa42a4d7eafc63329a3d4b
[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     // FIXME: ModernMediaControls
778     // FIXME: InputEvents
779     // FIXME: SubtleCrypto
780     prefsPrivate4->setUserTimingEnabled(TRUE);
781     prefsPrivate4->setWebAnimationsEnabled(TRUE);
782     // FIXME: WebGL2
783 }
784
785 static void resetWebPreferencesToConsistentValues(IWebPreferences* preferences)
786 {
787     ASSERT(preferences);
788
789     enableExperimentalFeatures(preferences);
790
791     preferences->setAutosaves(FALSE);
792
793     COMPtr<IWebPreferencesPrivate> prefsPrivate(Query, preferences);
794     ASSERT(prefsPrivate);
795     prefsPrivate->setFullScreenEnabled(TRUE);
796
797 #ifdef USE_MAC_FONTS
798     static _bstr_t standardFamily(TEXT("Times"));
799     static _bstr_t fixedFamily(TEXT("Courier"));
800     static _bstr_t sansSerifFamily(TEXT("Helvetica"));
801     static _bstr_t cursiveFamily(TEXT("Apple Chancery"));
802     static _bstr_t fantasyFamily(TEXT("Papyrus"));
803     static _bstr_t pictographFamily(TEXT("Apple Color Emoji"));
804 #else
805     static _bstr_t standardFamily(TEXT("Times New Roman"));
806     static _bstr_t fixedFamily(TEXT("Courier New"));
807     static _bstr_t sansSerifFamily(TEXT("Arial"));
808     static _bstr_t cursiveFamily(TEXT("Comic Sans MS")); // Not actually cursive, but it's what IE and Firefox use.
809     static _bstr_t fantasyFamily(TEXT("Times New Roman"));
810     static _bstr_t pictographFamily(TEXT("Segoe UI Symbol"));
811 #endif
812
813     prefsPrivate->setAllowUniversalAccessFromFileURLs(TRUE);
814     prefsPrivate->setAllowFileAccessFromFileURLs(TRUE);
815     preferences->setStandardFontFamily(standardFamily);
816     preferences->setFixedFontFamily(fixedFamily);
817     preferences->setSerifFontFamily(standardFamily);
818     preferences->setSansSerifFontFamily(sansSerifFamily);
819     preferences->setCursiveFontFamily(cursiveFamily);
820     preferences->setFantasyFontFamily(fantasyFamily);
821     preferences->setPictographFontFamily(pictographFamily);
822     preferences->setDefaultFontSize(16);
823     preferences->setDefaultFixedFontSize(13);
824     preferences->setMinimumFontSize(0);
825     preferences->setDefaultTextEncodingName(L"ISO-8859-1");
826     preferences->setJavaEnabled(FALSE);
827     preferences->setJavaScriptEnabled(TRUE);
828     preferences->setEditableLinkBehavior(WebKitEditableLinkOnlyLiveWithShiftKey);
829     preferences->setTabsToLinks(FALSE);
830     preferences->setDOMPasteAllowed(TRUE);
831     preferences->setShouldPrintBackgrounds(TRUE);
832     preferences->setCacheModel(WebCacheModelDocumentBrowser);
833     prefsPrivate->setXSSAuditorEnabled(FALSE);
834     prefsPrivate->setExperimentalNotificationsEnabled(FALSE);
835     preferences->setPlugInsEnabled(TRUE);
836     preferences->setTextAreasAreResizable(TRUE);
837     preferences->setUsesPageCache(FALSE);
838
839     preferences->setPrivateBrowsingEnabled(FALSE);
840     prefsPrivate->setAuthorAndUserStylesEnabled(TRUE);
841     // Shrinks standalone images to fit: YES
842     preferences->setJavaScriptCanOpenWindowsAutomatically(TRUE);
843     prefsPrivate->setJavaScriptCanAccessClipboard(TRUE);
844     prefsPrivate->setOfflineWebApplicationCacheEnabled(TRUE);
845     prefsPrivate->setDeveloperExtrasEnabled(FALSE);
846     COMPtr<IWebPreferencesPrivate2> prefsPrivate2(Query, preferences);
847     if (prefsPrivate2)
848         prefsPrivate2->setJavaScriptRuntimeFlags(WebKitJavaScriptRuntimeFlagsAllEnabled);
849     // Set JS experiments enabled: YES
850     preferences->setLoadsImagesAutomatically(TRUE);
851     prefsPrivate->setLoadsSiteIconsIgnoringImageLoadingPreference(FALSE);
852     prefsPrivate->setFrameFlatteningEnabled(FALSE);
853     // Set spatial navigation enabled: NO
854     if (persistentUserStyleSheetLocation) {
855         size_t stringLength = CFStringGetLength(persistentUserStyleSheetLocation.get());
856         Vector<UniChar> urlCharacters(stringLength + 1, 0);
857         CFStringGetCharacters(persistentUserStyleSheetLocation.get(), CFRangeMake(0, stringLength), urlCharacters.data());
858         _bstr_t url(reinterpret_cast<wchar_t*>(urlCharacters.data()));
859         preferences->setUserStyleSheetLocation(url);
860         preferences->setUserStyleSheetEnabled(TRUE);
861     } else
862         preferences->setUserStyleSheetEnabled(FALSE);
863
864 #if USE(CG)
865     prefsPrivate->setAcceleratedCompositingEnabled(TRUE);
866 #endif
867     // Set WebGL Enabled: NO
868     preferences->setCSSRegionsEnabled(TRUE);
869     // Set uses HTML5 parser quirks: NO
870     // Async spellcheck: NO
871     prefsPrivate->setMockScrollbarsEnabled(TRUE);
872
873     preferences->setFontSmoothing(FontSmoothingTypeStandard);
874
875     COMPtr<IWebPreferencesPrivate4> prefsPrivate4(Query, preferences);
876     ASSERT(prefsPrivate4);
877     prefsPrivate4->setFetchAPIEnabled(TRUE);
878     prefsPrivate4->setShadowDOMEnabled(TRUE);
879     prefsPrivate4->setCustomElementsEnabled(TRUE);
880     prefsPrivate4->setModernMediaControlsEnabled(FALSE);
881     prefsPrivate4->setResourceTimingEnabled(TRUE);
882     prefsPrivate4->setLinkPreloadEnabled(TRUE);
883
884     setAlwaysAcceptCookies(false);
885 }
886
887 static String applicationId()
888 {
889     DWORD processId = ::GetCurrentProcessId();
890     return String::format("com.apple.DumpRenderTree.%d", processId);
891 }
892
893 static void setApplicationId()
894 {
895     COMPtr<IWebPreferences> preferences;
896     if (SUCCEEDED(WebKitCreateInstance(CLSID_WebPreferences, 0, IID_IWebPreferences, (void**)&preferences))) {
897         COMPtr<IWebPreferencesPrivate4> prefsPrivate4(Query, preferences);
898         ASSERT(prefsPrivate4);
899         _bstr_t fileName = applicationId().charactersWithNullTermination().data();
900         prefsPrivate4->setApplicationId(fileName);
901     }
902 }
903
904 static void setCacheFolder()
905 {
906     String libraryPath = libraryPathForDumpRenderTree();
907
908     COMPtr<IWebCache> webCache;
909     if (SUCCEEDED(WebKitCreateInstance(CLSID_WebCache, 0, IID_IWebCache, (void**)&webCache))) {
910         _bstr_t cacheFolder = WebCore::pathByAppendingComponent(libraryPath, "LocalCache").utf8().data();
911         webCache->setCacheFolder(cacheFolder);
912     }
913 }
914
915 // Called once on DumpRenderTree startup.
916 static void setDefaultsToConsistentValuesForTesting()
917 {
918 #if USE(CF)
919     // Create separate preferences for each DRT instance
920     setApplicationId();
921
922     RetainPtr<CFStringRef> appId = applicationId().createCFString();
923
924     String libraryPath = libraryPathForDumpRenderTree();
925
926     // Set up these values before creating the WebView so that the various initializations will see these preferred values.
927     CFPreferencesSetAppValue(WebDatabaseDirectoryDefaultsKey, WebCore::pathByAppendingComponent(libraryPath, "Databases").createCFString().get(), appId.get());
928     CFPreferencesSetAppValue(WebStorageDirectoryDefaultsKey, WebCore::pathByAppendingComponent(libraryPath, "LocalStorage").createCFString().get(), appId.get());
929     CFPreferencesSetAppValue(WebKitLocalCacheDefaultsKey, WebCore::pathByAppendingComponent(libraryPath, "LocalCache").createCFString().get(), appId.get());
930
931     // Create separate cache for each DRT instance
932     setCacheFolder();
933 #endif
934 }
935
936 static void resetWebViewToConsistentStateBeforeTesting()
937 {
938     COMPtr<IWebView> webView;
939     if (FAILED(frame->webView(&webView))) 
940         return;
941
942     COMPtr<IWebViewEditing> viewEditing;
943     if (SUCCEEDED(webView->QueryInterface(&viewEditing)) && viewEditing) {
944
945         viewEditing->setEditable(FALSE);
946
947         COMPtr<IWebEditingDelegate> delegate;
948         if (SUCCEEDED(viewEditing->editingDelegate(&delegate)) && delegate) {
949             COMPtr<EditingDelegate> editingDelegate(Query, viewEditing.get());
950             if (editingDelegate)
951                 editingDelegate->setAcceptsEditing(TRUE);
952         }
953     }
954
955     COMPtr<IWebIBActions> webIBActions(Query, webView);
956     if (webIBActions) {
957         webIBActions->makeTextStandardSize(0);
958         webIBActions->resetPageZoom(0);
959     }
960
961     COMPtr<IWebViewPrivate2> webViewPrivate(Query, webView);
962     if (webViewPrivate) {
963         POINT origin = { 0, 0 };
964         webViewPrivate->scaleWebView(1.0, origin);
965         webViewPrivate->setCustomBackingScaleFactor(0);
966         webViewPrivate->setTabKeyCyclesThroughElements(TRUE);
967     }
968
969     webView->setPolicyDelegate(nullptr);
970     policyDelegate->setPermissive(false);
971     policyDelegate->setControllerToNotifyDone(nullptr);
972     sharedFrameLoadDelegate->resetToConsistentState();
973
974     if (webViewPrivate) {
975         webViewPrivate->clearMainFrameName();
976         _bstr_t groupName;
977         if (SUCCEEDED(webView->groupName(&groupName.GetBSTR())))
978             webViewPrivate->removeAllUserContentFromGroup(groupName);
979     }
980
981     COMPtr<IWebPreferences> preferences;
982     if (SUCCEEDED(webView->preferences(&preferences)))
983         resetWebPreferencesToConsistentValues(preferences.get());
984
985     TestRunner::setSerializeHTTPLoads(false);
986
987     setlocale(LC_ALL, "");
988
989     if (::gTestRunner) {
990         JSGlobalContextRef context = frame->globalContext();
991         WebCoreTestSupport::resetInternalsObject(context);
992     }
993
994     if (preferences) {
995         preferences->setContinuousSpellCheckingEnabled(TRUE);
996         // Automatic Quote Subs
997         // Automatic Link Detection
998         // Autommatic Dash substitution
999         // Automatic Spell Check
1000         preferences->setGrammarCheckingEnabled(TRUE);
1001         // Use Test Mode Focus Ring
1002     }
1003
1004     HWND viewWindow;
1005     if (webViewPrivate && SUCCEEDED(webViewPrivate->viewWindow(&viewWindow)) && viewWindow)
1006         ::SetFocus(viewWindow);
1007
1008     webViewPrivate->resetOriginAccessWhitelists();
1009
1010     sharedUIDelegate->resetUndoManager();
1011
1012     COMPtr<IWebFramePrivate> framePrivate;
1013     if (SUCCEEDED(frame->QueryInterface(&framePrivate)))
1014         framePrivate->clearOpener();
1015 }
1016
1017 static void sizeWebViewForCurrentTest()
1018 {
1019     bool isSVGW3CTest = (::gTestRunner->testURL().find("svg\\W3C-SVG-1.1") != string::npos)
1020         || (::gTestRunner->testURL().find("svg/W3C-SVG-1.1") != string::npos);
1021     unsigned width = isSVGW3CTest ? TestRunner::w3cSVGViewWidth : TestRunner::viewWidth;
1022     unsigned height = isSVGW3CTest ? TestRunner::w3cSVGViewHeight : TestRunner::viewHeight;
1023
1024     ::SetWindowPos(webViewWindow, 0, 0, 0, width, height, SWP_NOMOVE);
1025 }
1026
1027 static String findFontFallback(const char* pathOrUrl)
1028 {
1029     String pathToFontFallback = WebCore::directoryName(pathOrUrl);
1030
1031     wchar_t fullPath[_MAX_PATH];
1032     if (!_wfullpath(fullPath, pathToFontFallback.charactersWithNullTermination().data(), _MAX_PATH))
1033         return emptyString();
1034
1035     if (!::PathIsDirectoryW(fullPath))
1036         return emptyString();
1037
1038     String pathToCheck = fullPath;
1039
1040     static const String layoutTests = "LayoutTests";
1041
1042     // Find the layout test root on the current path:
1043     size_t location = pathToCheck.find(layoutTests);
1044     if (WTF::notFound == location)
1045         return emptyString();
1046
1047     String pathToTest = pathToCheck.substring(location + layoutTests.length() + 1);
1048     String possiblePathToLogue = WebCore::pathByAppendingComponent(pathToCheck.substring(0, location + layoutTests.length() + 1), "platform\\win");
1049
1050     Vector<String> possiblePaths;
1051     possiblePaths.append(WebCore::pathByAppendingComponent(possiblePathToLogue, pathToTest));
1052
1053     size_t nextCandidateEnd = pathToTest.reverseFind('\\');
1054     while (nextCandidateEnd && nextCandidateEnd != WTF::notFound) {
1055         pathToTest = pathToTest.substring(0, nextCandidateEnd);
1056         possiblePaths.append(WebCore::pathByAppendingComponent(possiblePathToLogue, pathToTest));
1057         nextCandidateEnd = pathToTest.reverseFind('\\');
1058     }
1059
1060     for (Vector<String>::iterator pos = possiblePaths.begin(); pos != possiblePaths.end(); ++pos) {
1061         pathToFontFallback = WebCore::pathByAppendingComponent(*pos, "resources\\"); 
1062
1063         if (::PathIsDirectoryW(pathToFontFallback.charactersWithNullTermination().data()))
1064             return pathToFontFallback;
1065     }
1066
1067     return emptyString();
1068 }
1069
1070 static void addFontFallbackIfPresent(const String& fontFallbackPath)
1071 {
1072     if (fontFallbackPath.isEmpty())
1073         return;
1074
1075     String fontFallback = WebCore::pathByAppendingComponent(fontFallbackPath, "Mac-compatible-font-fallback.css");
1076
1077     if (!::PathFileExistsW(fontFallback.charactersWithNullTermination().data()))
1078         return;
1079
1080     ::setPersistentUserStyleSheetLocation(fontFallback.createCFString().get());
1081 }
1082
1083 static void removeFontFallbackIfPresent(const String& fontFallbackPath)
1084 {
1085     if (fontFallbackPath.isEmpty())
1086         return;
1087
1088     String fontFallback = WebCore::pathByAppendingComponent(fontFallbackPath, "Mac-compatible-font-fallback.css");
1089
1090     if (!::PathFileExistsW(fontFallback.charactersWithNullTermination().data()))
1091         return;
1092
1093     ::setPersistentUserStyleSheetLocation(nullptr);
1094 }
1095
1096
1097 static void runTest(const string& inputLine)
1098 {
1099     ASSERT(!inputLine.empty());
1100
1101     TestCommand command = parseInputLine(inputLine);
1102     const string& pathOrURL = command.pathOrURL;
1103     dumpPixelsForCurrentTest = command.shouldDumpPixels || dumpPixelsForAllTests;
1104
1105     static _bstr_t methodBStr(TEXT("GET"));
1106
1107     CFStringRef str = CFStringCreateWithCString(0, pathOrURL.c_str(), kCFStringEncodingWindowsLatin1);
1108     if (!str) {
1109         fprintf(stderr, "Failed to parse \"%s\" as UTF-8\n", pathOrURL.c_str());
1110         return;
1111     }
1112
1113     CFURLRef url = CFURLCreateWithString(0, str, 0);
1114
1115     if (!url)
1116         url = CFURLCreateWithFileSystemPath(0, str, kCFURLWindowsPathStyle, false);
1117
1118     CFRelease(str);
1119
1120     if (!url) {
1121         fprintf(stderr, "Failed to parse \"%s\" as a URL\n", pathOrURL.c_str());
1122         return;
1123     }
1124
1125     String fallbackPath = findFontFallback(pathOrURL.c_str());
1126
1127     str = CFURLGetString(url);
1128
1129     CFIndex length = CFStringGetLength(str);
1130
1131     Vector<UniChar> buffer(length + 1, 0);
1132     CFStringGetCharacters(str, CFRangeMake(0, length), buffer.data());
1133
1134     _bstr_t urlBStr(reinterpret_cast<wchar_t*>(buffer.data()));
1135     ASSERT(urlBStr.length() == length);
1136
1137     // Check that test has not already run
1138     static HashSet<String> testUrls;
1139     if (testUrls.contains(String(inputLine.c_str())))
1140         fprintf(stderr, "Test has already run \"%s\"\n", inputLine.c_str());
1141     testUrls.add(String(inputLine.c_str()));
1142
1143     CFIndex maximumURLLengthAsUTF8 = CFStringGetMaximumSizeForEncoding(length, kCFStringEncodingUTF8) + 1;
1144     Vector<char> testURL(maximumURLLengthAsUTF8 + 1, 0);
1145     CFStringGetCString(str, testURL.data(), maximumURLLengthAsUTF8, kCFStringEncodingUTF8);
1146
1147     CFRelease(url);
1148
1149     resetWebViewToConsistentStateBeforeTesting();
1150
1151     ::gTestRunner = TestRunner::create(testURL.data(), command.expectedPixelHash);
1152     ::gTestRunner->setCustomTimeout(command.timeout);
1153     ::gTestRunner->setDumpJSConsoleLogInStdErr(command.dumpJSConsoleLogInStdErr);
1154
1155     topLoadingFrame = nullptr;
1156     done = false;
1157
1158     addFontFallbackIfPresent(fallbackPath);
1159
1160     sizeWebViewForCurrentTest();
1161     ::gTestRunner->setIconDatabaseEnabled(false);
1162     ::gTestRunner->clearAllApplicationCaches();
1163
1164     if (shouldLogFrameLoadDelegates(pathOrURL.c_str()))
1165         ::gTestRunner->setDumpFrameLoadCallbacks(true);
1166
1167     COMPtr<IWebView> webView;
1168     if (SUCCEEDED(frame->webView(&webView))) {
1169         COMPtr<IWebViewPrivate2> viewPrivate;
1170         if (SUCCEEDED(webView->QueryInterface(&viewPrivate))) {
1171             if (shouldLogHistoryDelegates(pathOrURL.c_str())) {
1172                 ::gTestRunner->setDumpHistoryDelegateCallbacks(true);            
1173                 viewPrivate->setHistoryDelegate(sharedHistoryDelegate.get());
1174             } else
1175                 viewPrivate->setHistoryDelegate(nullptr);
1176         }
1177     }
1178
1179     if (shouldEnableDeveloperExtras(pathOrURL.c_str())) {
1180         ::gTestRunner->setDeveloperExtrasEnabled(true);
1181         if (shouldDumpAsText(pathOrURL.c_str())) {
1182             ::gTestRunner->setDumpAsText(true);
1183             ::gTestRunner->setGeneratePixelResults(false);
1184         }
1185     }
1186
1187     COMPtr<IWebHistory> history;
1188     if (SUCCEEDED(WebKitCreateInstance(CLSID_WebHistory, 0, __uuidof(history), reinterpret_cast<void**>(&history))))
1189         history->setOptionalSharedHistory(nullptr);
1190
1191     prevTestBFItem = nullptr;
1192     COMPtr<IWebBackForwardList> bfList;
1193     if (webView && SUCCEEDED(webView->backForwardList(&bfList)))
1194         bfList->currentItem(&prevTestBFItem);
1195
1196     auto& workQueue = WorkQueue::singleton();
1197     workQueue.clear();
1198     workQueue.setFrozen(false);
1199
1200     MSG msg = { 0 };
1201     HWND hostWindow;
1202     webView->hostWindow(&hostWindow);
1203
1204     COMPtr<IWebMutableURLRequest> request, emptyRequest;
1205     HRESULT hr = WebKitCreateInstance(CLSID_WebMutableURLRequest, 0, IID_IWebMutableURLRequest, (void**)&request);
1206     if (FAILED(hr))
1207         goto exit;
1208
1209     request->initWithURL(urlBStr, WebURLRequestUseProtocolCachePolicy, 60);
1210     request->setHTTPMethod(methodBStr);
1211     frame->loadRequest(request.get());
1212
1213     while (true) {
1214 #if USE(CF)
1215         CFRunLoopRunInMode(kCFRunLoopDefaultMode, 0, true);
1216 #endif
1217         if (!::GetMessage(&msg, 0, 0, 0))
1218             break;
1219
1220         // We get spurious WM_MOUSELEAVE events which make event handling machinery think that mouse button
1221         // is released during dragging (see e.g. fast\dynamic\layer-hit-test-crash.html).
1222         // Mouse can never leave WebView during normal DumpRenderTree operation, so we just ignore all such events.
1223         if (msg.message == WM_MOUSELEAVE)
1224             continue;
1225         ::TranslateMessage(&msg);
1226         ::DispatchMessage(&msg);
1227     }
1228
1229     // EventSendingController clearSavedEvents
1230     workQueue.clear();
1231
1232     // If the test page could have possibly opened the Web Inspector frontend,
1233     // then try to close it in case it was accidentally left open.
1234     if (shouldEnableDeveloperExtras(pathOrURL.c_str())) {
1235         ::gTestRunner->closeWebInspector();
1236         ::gTestRunner->setDeveloperExtrasEnabled(false);
1237     }
1238
1239     if (::gTestRunner->closeRemainingWindowsWhenComplete()) {
1240         Vector<HWND> windows = openWindows();
1241         unsigned size = windows.size();
1242         for (unsigned i = 0; i < size; i++) {
1243             HWND window = windows[i];
1244
1245             // Don't try to close the main window
1246             if (window == hostWindow)
1247                 continue;
1248
1249             ::DestroyWindow(window);
1250         }
1251     }
1252
1253     resetWebViewToConsistentStateBeforeTesting();
1254
1255     // Loading an empty request synchronously replaces the document with a blank one, which is necessary
1256     // to stop timers, WebSockets and other activity that could otherwise spill output into next test's results.
1257     if (SUCCEEDED(WebKitCreateInstance(CLSID_WebMutableURLRequest, 0, IID_IWebMutableURLRequest, (void**)&emptyRequest))) {
1258         _bstr_t emptyURL(L"");
1259         emptyRequest->initWithURL(emptyURL.GetBSTR(), WebURLRequestUseProtocolCachePolicy, 60);
1260         emptyRequest->setHTTPMethod(methodBStr);
1261         frame->loadRequest(emptyRequest.get());
1262     }
1263
1264     // We should only have our main window left open when we're done
1265     ASSERT(openWindows().size() == 1);
1266     ASSERT(openWindows()[0] == hostWindow);
1267
1268 exit:
1269     removeFontFallbackIfPresent(fallbackPath);
1270     ::gTestRunner->cleanup();
1271     ::gTestRunner = nullptr;
1272
1273     fputs("#EOF\n", stderr);
1274     fflush(stderr);
1275 }
1276
1277 Vector<HWND>& openWindows()
1278 {
1279     static NeverDestroyed<Vector<HWND>> vector;
1280     return vector;
1281 }
1282
1283 WindowToWebViewMap& windowToWebViewMap()
1284 {
1285     static NeverDestroyed<WindowToWebViewMap> map;
1286     return map;
1287 }
1288
1289 IWebView* createWebViewAndOffscreenWindow(HWND* webViewWindow)
1290 {
1291     int maxViewWidth = TestRunner::viewWidth;
1292     int maxViewHeight = TestRunner::viewHeight;
1293     HWND hostWindow = (showWebView) ? CreateWindowEx(WS_EX_TOOLWINDOW, kDumpRenderTreeClassName, TEXT("DumpRenderTree"), WS_POPUP, 100, 100, maxViewWidth, maxViewHeight, 0, 0, ::GetModuleHandle(0), nullptr)
1294         : CreateWindowEx(WS_EX_TOOLWINDOW, kDumpRenderTreeClassName, TEXT("DumpRenderTree"), WS_POPUP, -maxViewWidth, -maxViewHeight, maxViewWidth, maxViewHeight, 0, 0, ::GetModuleHandle(0), nullptr);
1295
1296     IWebView* webView = nullptr;
1297     HRESULT hr = WebKitCreateInstance(CLSID_WebView, 0, IID_IWebView, (void**)&webView);
1298     if (FAILED(hr)) {
1299         fprintf(stderr, "Failed to create CLSID_WebView instance, error 0x%x\n", hr);
1300         return nullptr;
1301     }
1302
1303     if (FAILED(webView->setHostWindow(hostWindow)))
1304         return nullptr;
1305
1306     RECT clientRect;
1307     clientRect.bottom = clientRect.left = clientRect.top = clientRect.right = 0;
1308     _bstr_t groupName(L"org.webkit.DumpRenderTree");
1309     if (FAILED(webView->initWithFrame(clientRect, 0, groupName)))
1310         return nullptr;
1311
1312     COMPtr<IWebViewPrivate2> viewPrivate;
1313     if (FAILED(webView->QueryInterface(&viewPrivate)))
1314         return nullptr;
1315
1316     viewPrivate->setShouldApplyMacFontAscentHack(TRUE);
1317     viewPrivate->setAlwaysUsesComplexTextCodePath(forceComplexText);
1318     viewPrivate->setCustomBackingScaleFactor(1.0);
1319
1320     _bstr_t pluginPath = _bstr_t(exePath().data()) + TestPluginDir;
1321     if (FAILED(viewPrivate->addAdditionalPluginDirectory(pluginPath.GetBSTR())))
1322         return nullptr;
1323
1324     HWND viewWindow;
1325     if (FAILED(viewPrivate->viewWindow(&viewWindow)))
1326         return nullptr;
1327     if (webViewWindow)
1328         *webViewWindow = viewWindow;
1329
1330     ::SetWindowPos(viewWindow, 0, 0, 0, maxViewWidth, maxViewHeight, 0);
1331     ::ShowWindow(hostWindow, SW_SHOW);
1332
1333     if (FAILED(webView->setUIDelegate(sharedUIDelegate.get())))
1334         return nullptr;
1335
1336     if (FAILED(webView->setFrameLoadDelegate(sharedFrameLoadDelegate.get())))
1337         return nullptr;
1338
1339     if (FAILED(viewPrivate->setFrameLoadDelegatePrivate(sharedFrameLoadDelegate.get())))
1340         return nullptr;
1341
1342     COMPtr<IWebViewEditing> viewEditing;
1343     if (FAILED(webView->QueryInterface(&viewEditing)))
1344         return nullptr;
1345
1346     if (FAILED(viewEditing->setEditingDelegate(sharedEditingDelegate.get())))
1347         return nullptr;
1348
1349     if (FAILED(webView->setResourceLoadDelegate(resourceLoadDelegate.get())))
1350         return nullptr;
1351
1352     viewPrivate->setDefersCallbacks(FALSE);
1353
1354     openWindows().append(hostWindow);
1355     windowToWebViewMap().set(hostWindow, webView);
1356     return webView;
1357 }
1358
1359 #if USE(CFURLCONNECTION)
1360 RetainPtr<CFURLCacheRef> sharedCFURLCache()
1361 {
1362 #ifndef DEBUG_ALL
1363     HMODULE module = GetModuleHandle(TEXT("CFNetwork.dll"));
1364 #else
1365     HMODULE module = GetModuleHandle(TEXT("CFNetwork_debug.dll"));
1366 #endif
1367     if (!module)
1368         return nullptr;
1369
1370     typedef CFURLCacheRef (*CFURLCacheCopySharedURLCacheProcPtr)(void);
1371     if (CFURLCacheCopySharedURLCacheProcPtr copyCache = reinterpret_cast<CFURLCacheCopySharedURLCacheProcPtr>(GetProcAddress(module, "CFURLCacheCopySharedURLCache")))
1372         return adoptCF(copyCache());
1373
1374     typedef CFURLCacheRef (*CFURLCacheSharedURLCacheProcPtr)(void);
1375     if (CFURLCacheSharedURLCacheProcPtr sharedCache = reinterpret_cast<CFURLCacheSharedURLCacheProcPtr>(GetProcAddress(module, "CFURLCacheSharedURLCache")))
1376         return sharedCache();
1377
1378     return nullptr;
1379 }
1380 #endif
1381
1382 static Vector<const char*> initializeGlobalsFromCommandLineOptions(int argc, const char* argv[])
1383 {
1384     Vector<const char*> tests;
1385
1386     for (int i = 1; i < argc; ++i) {
1387         if (!stricmp(argv[i], "--notree")) {
1388             dumpTree = false;
1389             continue;
1390         }
1391
1392         if (!stricmp(argv[i], "--pixel-tests")) {
1393             dumpPixelsForAllTests = true;
1394             continue;
1395         }
1396
1397         if (!stricmp(argv[i], "--tree")) {
1398             dumpTree = true;
1399             continue;
1400         }
1401
1402         if (!stricmp(argv[i], "--threaded")) {
1403             threaded = true;
1404             continue;
1405         }
1406
1407         if (!stricmp(argv[i], "--complex-text")) {
1408             forceComplexText = true;
1409             continue;
1410         }
1411
1412         if (!stricmp(argv[i], "--accelerated-drawing")) {
1413             useAcceleratedDrawing = true;
1414             continue;
1415         }
1416
1417         if (!stricmp(argv[i], "--gc-between-tests")) {
1418             gcBetweenTests = true;
1419             continue;
1420         }
1421
1422         if (!stricmp(argv[i], "--no-timeout")) {
1423             useTimeoutWatchdog = false;
1424             continue;
1425         }
1426
1427         if (!stricmp(argv[i], "--dump-all-pixels")) {
1428             dumpAllPixels = true;
1429             continue;
1430         }
1431
1432         if (!stricmp(argv[i], "--print-supported-features")) {
1433             printSupportedFeatures = true;
1434             continue;
1435         }
1436
1437         if (!stricmp(argv[i], "--show-webview")) {
1438             showWebView = true;
1439             continue;
1440         }
1441
1442         tests.append(argv[i]);
1443     }
1444
1445     return tests;
1446 }
1447
1448 static void allocateGlobalControllers()
1449 {
1450     sharedFrameLoadDelegate.adoptRef(new FrameLoadDelegate);
1451     sharedUIDelegate.adoptRef(new UIDelegate);
1452     sharedEditingDelegate.adoptRef(new EditingDelegate);
1453     resourceLoadDelegate.adoptRef(new ResourceLoadDelegate);
1454     policyDelegate = new PolicyDelegate();
1455     sharedHistoryDelegate.adoptRef(new HistoryDelegate);
1456     // storage delegate
1457     // policy delegate
1458 }
1459
1460 static void prepareConsistentTestingEnvironment(IWebPreferences* standardPreferences, IWebPreferencesPrivate* standardPreferencesPrivate)
1461 {
1462     ASSERT(standardPreferences);
1463     ASSERT(standardPreferencesPrivate);
1464     standardPreferences->setAutosaves(FALSE);
1465
1466     standardPreferences->setJavaScriptEnabled(TRUE);
1467     standardPreferences->setDefaultFontSize(16);
1468 #if USE(CG)
1469     standardPreferences->setAcceleratedCompositingEnabled(TRUE);
1470     standardPreferences->setAVFoundationEnabled(TRUE);
1471 #endif
1472
1473     allocateGlobalControllers();
1474 }
1475
1476 int main(int argc, const char* argv[])
1477 {
1478     // Cygwin calls ::SetErrorMode(SEM_FAILCRITICALERRORS), which we will inherit. This is bad for
1479     // testing/debugging, as it causes the post-mortem debugger not to be invoked. We reset the
1480     // error mode here to work around Cygwin's behavior. See <http://webkit.org/b/55222>.
1481     ::SetErrorMode(0);
1482
1483     leakChecking = false;
1484
1485     _setmode(1, _O_BINARY);
1486     _setmode(2, _O_BINARY);
1487
1488     // Some tests are flaky because certain DLLs are writing to stdout, giving incorrect test results.
1489     // We work around that here by duplicating and redirecting stdout.
1490     int fdStdout = _dup(1);
1491     _setmode(fdStdout, _O_BINARY);
1492     testResult = fdopen(fdStdout, "a+b");
1493     // Redirect stdout to stderr.
1494     int result = _dup2(_fileno(stderr), 1);
1495
1496     // Tests involving the clipboard are flaky when running with multiple DRTs, since the clipboard is global.
1497     // We can fix this by assigning each DRT a separate window station (each window station has its own clipboard).
1498     DWORD processId = ::GetCurrentProcessId();
1499     String windowStationName = String::format("windowStation%d", processId);
1500     String desktopName = String::format("desktop%d", processId);
1501     HDESK desktop = nullptr;
1502
1503     auto windowsStation = ::CreateWindowStation(windowStationName.charactersWithNullTermination().data(), CWF_CREATE_ONLY, WINSTA_ALL_ACCESS, nullptr);
1504     if (windowsStation) {
1505         if (!::SetProcessWindowStation(windowsStation))
1506             fprintf(stderr, "SetProcessWindowStation failed with error %d\n", ::GetLastError());
1507
1508         desktop = ::CreateDesktop(desktopName.charactersWithNullTermination().data(), nullptr, nullptr, 0, GENERIC_ALL, nullptr);
1509         if (!desktop)
1510             fprintf(stderr, "Failed to create desktop with error %d\n", ::GetLastError());
1511     } else {
1512         DWORD error = ::GetLastError();
1513         fprintf(stderr, "Failed to create window station with error %d\n", error);
1514         if (error == ERROR_ACCESS_DENIED)
1515             fprintf(stderr, "DumpRenderTree should be run as Administrator!\n");
1516     }
1517
1518     initialize();
1519
1520     setDefaultsToConsistentValuesForTesting();
1521
1522     Vector<const char*> tests = initializeGlobalsFromCommandLineOptions(argc, argv);
1523
1524     // FIXME - need to make DRT pass with Windows native controls <http://bugs.webkit.org/show_bug.cgi?id=25592>
1525     COMPtr<IWebPreferences> tmpPreferences;
1526     if (FAILED(WebKitCreateInstance(CLSID_WebPreferences, 0, IID_IWebPreferences, reinterpret_cast<void**>(&tmpPreferences))))
1527         return -1;
1528     COMPtr<IWebPreferences> standardPreferences;
1529     if (FAILED(tmpPreferences->standardPreferences(&standardPreferences)))
1530         return -2;
1531     COMPtr<IWebPreferencesPrivate> standardPreferencesPrivate;
1532     if (FAILED(standardPreferences->QueryInterface(&standardPreferencesPrivate)))
1533         return -3;
1534
1535     prepareConsistentTestingEnvironment(standardPreferences.get(), standardPreferencesPrivate.get());
1536
1537     if (printSupportedFeatures) {
1538         BOOL acceleratedCompositingAvailable = FALSE;
1539         standardPreferences->acceleratedCompositingEnabled(&acceleratedCompositingAvailable);
1540
1541 #if ENABLE(3D_TRANSFORMS)
1542         // In theory, we could have a software-based 3D rendering implementation that we use when
1543         // hardware-acceleration is not available. But we don't have any such software
1544         // implementation, so 3D rendering is only available when hardware-acceleration is.
1545         BOOL threeDTransformsAvailable = acceleratedCompositingAvailable;
1546 #else
1547         BOOL threeDTransformsAvailable = FALSE;
1548 #endif
1549
1550         fprintf(testResult, "SupportedFeatures:%s %s\n", acceleratedCompositingAvailable ? "AcceleratedCompositing" : "", threeDTransformsAvailable ? "3DTransforms" : "");
1551         return 0;
1552     }
1553
1554     COMPtr<IWebView> webView(AdoptCOM, createWebViewAndOffscreenWindow(&webViewWindow));
1555     if (!webView)
1556         return -4;
1557
1558     COMPtr<IWebIconDatabase> iconDatabase;
1559     COMPtr<IWebIconDatabase> tmpIconDatabase;
1560     if (FAILED(WebKitCreateInstance(CLSID_WebIconDatabase, 0, IID_IWebIconDatabase, (void**)&tmpIconDatabase)))
1561         return -5;
1562     if (FAILED(tmpIconDatabase->sharedIconDatabase(&iconDatabase)))
1563         return -6;
1564     if (FAILED(webView->mainFrame(&frame)))
1565         return -7;
1566
1567 #if USE(CFURLCONNECTION)
1568     RetainPtr<CFURLCacheRef> urlCache = sharedCFURLCache();
1569     CFURLCacheRemoveAllCachedResponses(urlCache.get());
1570 #endif
1571
1572 #ifdef _DEBUG
1573     _CrtMemState entryToMainMemCheckpoint;
1574     if (leakChecking)
1575         _CrtMemCheckpoint(&entryToMainMemCheckpoint);
1576 #endif
1577
1578     if (threaded)
1579         startJavaScriptThreads();
1580
1581     if (tests.size() == 1 && !strcmp(tests[0], "-")) {
1582         char filenameBuffer[2048];
1583         printSeparators = true;
1584         while (fgets(filenameBuffer, sizeof(filenameBuffer), stdin)) {
1585             char* newLineCharacter = strchr(filenameBuffer, '\n');
1586             if (newLineCharacter)
1587                 *newLineCharacter = '\0';
1588             
1589             if (strlen(filenameBuffer) == 0)
1590                 continue;
1591
1592             runTest(filenameBuffer);
1593         }
1594     } else {
1595         printSeparators = tests.size() > 1;
1596         for (int i = 0; i < tests.size(); i++)
1597             runTest(tests[i]);
1598     }
1599
1600     if (threaded)
1601         stopJavaScriptThreads();
1602     
1603     delete policyDelegate;
1604     frame->Release();
1605
1606 #ifdef _DEBUG
1607     if (leakChecking) {
1608         // dump leaks to stderr
1609         _CrtSetReportMode(_CRT_WARN, _CRTDBG_MODE_FILE);
1610         _CrtSetReportFile(_CRT_WARN, _CRTDBG_FILE_STDERR);
1611         _CrtMemDumpAllObjectsSince(&entryToMainMemCheckpoint);
1612     }
1613 #endif
1614
1615     shutDownWebKit();
1616 #ifdef _CRTDBG_MAP_ALLOC
1617     _CrtDumpMemoryLeaks();
1618 #endif
1619
1620     if (desktop)
1621         ::CloseDesktop(desktop);
1622     if (windowsStation)
1623         ::CloseWindowStation(windowsStation);
1624
1625     ::OleUninitialize();
1626
1627     return 0;
1628 }
1629
1630 extern "C" __declspec(dllexport) int WINAPI dllLauncherEntryPoint(int argc, const char* argv[])
1631 {
1632     return main(argc, argv);
1633 }