Home | History | Annotate | Download | only in win
      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 
     68 using namespace std;
     69 
     70 #ifdef DEBUG_ALL
     71 const LPWSTR TestPluginDir = L"TestNetscapePlugin_Debug";
     72 #else
     73 const LPWSTR TestPluginDir = L"TestNetscapePlugin";
     74 #endif
     75 
     76 static LPCWSTR fontsEnvironmentVariable = L"WEBKIT_TESTFONTS";
     77 #define USE_MAC_FONTS
     78 
     79 const LPCWSTR kDumpRenderTreeClassName = L"DumpRenderTreeWindow";
     80 
     81 static bool dumpTree = true;
     82 static bool dumpPixels;
     83 static bool dumpAllPixels;
     84 static bool printSeparators;
     85 static bool leakChecking = false;
     86 static bool threaded = false;
     87 static bool forceComplexText = false;
     88 static bool printSupportedFeatures = false;
     89 static RetainPtr<CFStringRef> persistentUserStyleSheetLocation;
     90 
     91 volatile 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".
     96 IWebFrame* topLoadingFrame;     // !nil iff a load is in progress
     97 static COMPtr<IWebHistoryItem> prevTestBFItem;  // current b/f item at the end of the previous test
     98 PolicyDelegate* policyDelegate;
     99 COMPtr<FrameLoadDelegate> sharedFrameLoadDelegate;
    100 COMPtr<UIDelegate> sharedUIDelegate;
    101 COMPtr<EditingDelegate> sharedEditingDelegate;
    102 COMPtr<HistoryDelegate> sharedHistoryDelegate;
    103 
    104 IWebFrame* frame;
    105 HWND webViewWindow;
    106 
    107 RefPtr<LayoutTestController> gLayoutTestController;
    108 
    109 UINT_PTR waitToDumpWatchdog = 0;
    110 
    111 void setPersistentUserStyleSheetLocation(CFStringRef url)
    112 {
    113     persistentUserStyleSheetLocation = url;
    114 }
    115 
    116 bool 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 
    135 static RetainPtr<CFStringRef> substringFromIndex(CFStringRef string, CFIndex index)
    136 {
    137     return RetainPtr<CFStringRef>(AdoptCF, CFStringCreateWithSubstring(kCFAllocatorDefault, string, CFRangeMake(index, CFStringGetLength(string) - index)));
    138 }
    139 
    140 wstring 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 
    171 wstring 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 
    182 static 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 
    193 string toUTF8(BSTR bstr)
    194 {
    195     return toUTF8(bstr, SysStringLen(bstr));
    196 }
    197 
    198 string toUTF8(const wstring& wideString)
    199 {
    200     return toUTF8(wideString.c_str(), wideString.length());
    201 }
    202 
    203 wstring 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 
    211 static 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 
    229 static 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 
    248 static 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 
    270 static 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 
    313 static 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 
    393 void displayWebView()
    394 {
    395     ::InvalidateRect(webViewWindow, 0, TRUE);
    396     ::SendMessage(webViewWindow, WM_PAINT, 0, 0);
    397 }
    398 
    399 void 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 
    442 static 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 
    501 static 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 
    527 static 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 
    617 exit:
    618     if (arrPtr && SUCCEEDED(::SafeArrayUnlock(arrPtr)))
    619         ::SafeArrayDestroy(arrPtr);
    620 }
    621 
    622 static 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 
    686 static 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 
    696 static void invalidateAnyPreviousWaitToDumpWatchdog()
    697 {
    698     if (!waitToDumpWatchdog)
    699         return;
    700 
    701     KillTimer(0, waitToDumpWatchdog);
    702     waitToDumpWatchdog = 0;
    703 }
    704 
    705 void 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 
    784 fail:
    785     SysFreeString(resultString);
    786     // This will exit from our message loop.
    787     PostQuitMessage(0);
    788     done = true;
    789 }
    790 
    791 static bool shouldLogFrameLoadDelegates(const char* pathOrURL)
    792 {
    793     return strstr(pathOrURL, "/loading/") || strstr(pathOrURL, "\\loading\\");
    794 }
    795 
    796 static bool shouldLogHistoryDelegates(const char* pathOrURL)
    797 {
    798     return strstr(pathOrURL, "/globalhistory/") || strstr(pathOrURL, "\\globalhistory\\");
    799 }
    800 
    801 static bool shouldOpenWebInspector(const char* pathOrURL)
    802 {
    803     return strstr(pathOrURL, "/inspector/") || strstr(pathOrURL, "\\inspector\\");
    804 }
    805 
    806 static bool shouldDumpAsText(const char* pathOrURL)
    807 {
    808     return strstr(pathOrURL, "/dumpAsText/") || strstr(pathOrURL, "\\dumpAsText\\");
    809 }
    810 
    811 static bool shouldEnableDeveloperExtras(const char* pathOrURL)
    812 {
    813     return true;
    814 }
    815 
    816 static 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 
    886 static 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 
    945 static 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 
   1073 exit:
   1074     SysFreeString(urlBStr);
   1075     ::gLayoutTestController.clear();
   1076 
   1077     return;
   1078 }
   1079 
   1080 static Boolean pthreadEqualCallback(const void* value1, const void* value2)
   1081 {
   1082     return (Boolean)pthread_equal(*(pthread_t*)value1, *(pthread_t*)value2);
   1083 }
   1084 
   1085 static CFDictionaryKeyCallBacks pthreadKeyCallbacks = { 0, 0, 0, 0, pthreadEqualCallback, 0 };
   1086 
   1087 static pthread_mutex_t javaScriptThreadsMutex = PTHREAD_MUTEX_INITIALIZER;
   1088 static bool javaScriptThreadsShouldTerminate;
   1089 
   1090 static const int javaScriptThreadsCount = 4;
   1091 static 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.
   1102 void* 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 
   1151 static 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 
   1165 static 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 
   1185 Vector<HWND>& openWindows()
   1186 {
   1187     static Vector<HWND> vector;
   1188     return vector;
   1189 }
   1190 
   1191 WindowToWebViewMap& windowToWebViewMap()
   1192 {
   1193     static WindowToWebViewMap map;
   1194     return map;
   1195 }
   1196 
   1197 IWebView* 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)
   1275 RetainPtr<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 
   1297 static LONG WINAPI exceptionFilter(EXCEPTION_POINTERS*)
   1298 {
   1299     fputs("#CRASHED\n", stderr);
   1300     fflush(stderr);
   1301     return EXCEPTION_CONTINUE_SEARCH;
   1302 }
   1303 
   1304 int 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