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