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