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