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