webengine/osswebengine/WebKitTools/DumpRenderTree/win/DumpRenderTree.cpp
changeset 0 dd21522fd290
equal deleted inserted replaced
-1:000000000000 0:dd21522fd290
       
     1 /*
       
     2  * Copyright (C) 2005, 2006, 2007 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 "DumpRenderTree.h"
       
    30 
       
    31 #include "EditingDelegate.h"
       
    32 #include "FrameLoaderDelegate.h"
       
    33 #include "LayoutTestController.h"
       
    34 #include "UIDelegate.h"
       
    35 #include "WorkQueueItem.h"
       
    36 #include "WorkQueue.h"
       
    37 #include <wtf/Vector.h>
       
    38 #include <WebCore/COMPtr.h>
       
    39 #include <CoreFoundation/CoreFoundation.h>
       
    40 #include <JavaScriptCore/JavaScriptCore.h>
       
    41 #include <math.h>
       
    42 #include <pthread.h>
       
    43 #include <string>
       
    44 #include <tchar.h>
       
    45 #include <WebKit/DOMPrivate.h>
       
    46 #include <WebKit/IWebFramePrivate.h>
       
    47 #include <WebKit/IWebHistoryItem.h>
       
    48 #include <WebKit/IWebHistoryItemPrivate.h>
       
    49 #include <WebKit/IWebURLResponse.h>
       
    50 #include <WebKit/IWebViewPrivate.h>
       
    51 #include <WebKit/WebKit.h>
       
    52 #include <windows.h>
       
    53 #include <stdio.h>
       
    54 
       
    55 using std::wstring;
       
    56 
       
    57 #ifdef _DEBUG
       
    58 const LPWSTR TestPluginDir = L"TestNetscapePlugin_Debug";
       
    59 #else
       
    60 const LPWSTR TestPluginDir = L"TestNetscapePlugin";
       
    61 #endif
       
    62 
       
    63 #define USE_MAC_FONTS
       
    64 
       
    65 const LPCWSTR kDumpRenderTreeClassName = L"DumpRenderTreeWindow";
       
    66 
       
    67 static bool dumpTree = true;
       
    68 static bool printSeparators;
       
    69 static bool leakChecking = false;
       
    70 static bool timedOut = false;
       
    71 static bool threaded = false;
       
    72 
       
    73 static const char* currentTest;
       
    74 
       
    75 volatile bool done;
       
    76 // This is the topmost frame that is loading, during a given load, or nil when no load is 
       
    77 // in progress.  Usually this is the same as the main frame, but not always.  In the case
       
    78 // where a frameset is loaded, and then new content is loaded into one of the child frames,
       
    79 // that child frame is the "topmost frame that is loading".
       
    80 IWebFrame* topLoadingFrame;     // !nil iff a load is in progress
       
    81 static COMPtr<IWebHistoryItem> prevTestBFItem;  // current b/f item at the end of the previous test
       
    82 
       
    83 IWebFrame* frame;
       
    84 HWND webViewWindow;
       
    85 static HWND hostWindow;
       
    86 
       
    87 LayoutTestController* layoutTestController = 0;
       
    88 CFRunLoopTimerRef waitToDumpWatchdog = 0; 
       
    89 
       
    90 static const unsigned timeoutValue = 60000;
       
    91 static const unsigned timeoutId = 10;
       
    92 
       
    93 const unsigned maxViewWidth = 800;
       
    94 const unsigned maxViewHeight = 600;
       
    95 
       
    96 static LRESULT CALLBACK DumpRenderTreeWndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
       
    97 {
       
    98     switch (msg) {
       
    99         case WM_TIMER:
       
   100             // The test ran long enough to time out
       
   101             timedOut = true;
       
   102             PostQuitMessage(0);
       
   103             return 0;
       
   104             break;
       
   105         default:
       
   106             return DefWindowProc(hWnd, msg, wParam, lParam);
       
   107     }
       
   108 }
       
   109 
       
   110 extern "C" BOOL InitializeCoreGraphics();
       
   111 
       
   112 #ifdef DEBUG_WEBKIT_HAS_SUFFIX
       
   113 #define WEBKITDLL TEXT("WebKit_debug.dll")
       
   114 #else
       
   115 #define WEBKITDLL TEXT("WebKit.dll")
       
   116 #endif
       
   117 
       
   118 static wstring initialize(HMODULE hModule)
       
   119 {
       
   120     if (HMODULE webKitModule = LoadLibrary(WEBKITDLL))
       
   121         if (FARPROC dllRegisterServer = GetProcAddress(webKitModule, "DllRegisterServer"))
       
   122             dllRegisterServer();
       
   123 
       
   124     static LPCTSTR fontsToInstall[] = {
       
   125         TEXT("AHEM____.ttf"),
       
   126         TEXT("Apple Chancery.ttf"),
       
   127         TEXT("Courier Bold.ttf"),
       
   128         TEXT("Courier.ttf"),
       
   129         TEXT("Helvetica Bold.ttf"),
       
   130         TEXT("Helvetica.ttf"),
       
   131         TEXT("Helvetica Neue Bold Italic.ttf"),
       
   132         TEXT("Helvetica Neue Bold.ttf"),
       
   133         TEXT("Helvetica Neue Condensed Black.ttf"),
       
   134         TEXT("Helvetica Neue Condensed Bold.ttf"),
       
   135         TEXT("Helvetica Neue Italic.ttf"),
       
   136         TEXT("Helvetica Neue Light Italic.ttf"),
       
   137         TEXT("Helvetica Neue Light.ttf"),
       
   138         TEXT("Helvetica Neue UltraLight Italic.ttf"),
       
   139         TEXT("Helvetica Neue UltraLight.ttf"),
       
   140         TEXT("Helvetica Neue.ttf"),
       
   141         TEXT("Lucida Grande.ttf"),
       
   142         TEXT("Lucida Grande Bold.ttf"),
       
   143         TEXT("Monaco.ttf"),
       
   144         TEXT("Papyrus.ttf"),
       
   145         TEXT("Times Bold Italic.ttf"),
       
   146         TEXT("Times Bold.ttf"),
       
   147         TEXT("Times Italic.ttf"),
       
   148         TEXT("Times Roman.ttf")
       
   149     };
       
   150 
       
   151     TCHAR buffer[MAX_PATH];
       
   152     GetModuleFileName(hModule, buffer, ARRAYSIZE(buffer));
       
   153     wstring exePath(buffer);
       
   154     int lastSlash = exePath.rfind('\\');
       
   155     if (lastSlash != -1 && lastSlash + 1< exePath.length())
       
   156         exePath = exePath.substr(0, lastSlash + 1);
       
   157     
       
   158     wstring resourcesPath(exePath + TEXT("DumpRenderTree.resources\\"));
       
   159 
       
   160     for (int i = 0; i < ARRAYSIZE(fontsToInstall); ++i)
       
   161         AddFontResourceEx(wstring(resourcesPath + fontsToInstall[i]).c_str(), FR_PRIVATE, 0);
       
   162 
       
   163     // Init COM
       
   164     OleInitialize(0);
       
   165 
       
   166     // Initialize CG
       
   167     InitializeCoreGraphics();
       
   168 
       
   169     // Register a host window
       
   170     WNDCLASSEX wcex;
       
   171 
       
   172     wcex.cbSize = sizeof(WNDCLASSEX);
       
   173 
       
   174     wcex.style         = CS_HREDRAW | CS_VREDRAW;
       
   175     wcex.lpfnWndProc   = DumpRenderTreeWndProc;
       
   176     wcex.cbClsExtra    = 0;
       
   177     wcex.cbWndExtra    = 0;
       
   178     wcex.hInstance     = hModule;
       
   179     wcex.hIcon         = 0;
       
   180     wcex.hCursor       = LoadCursor(0, IDC_ARROW);
       
   181     wcex.hbrBackground = 0;
       
   182     wcex.lpszMenuName  = 0;
       
   183     wcex.lpszClassName = kDumpRenderTreeClassName;
       
   184     wcex.hIconSm       = 0;
       
   185 
       
   186     RegisterClassEx(&wcex);
       
   187 
       
   188     hostWindow = CreateWindowEx(WS_EX_TOOLWINDOW, kDumpRenderTreeClassName, TEXT("DumpRenderTree"), WS_POPUP,
       
   189       -maxViewWidth, -maxViewHeight, maxViewWidth, maxViewHeight, 0, 0, hModule, 0);
       
   190 
       
   191     return exePath;
       
   192 }
       
   193 
       
   194 void displayWebView()
       
   195 {
       
   196     ::InvalidateRect(webViewWindow, 0, TRUE);
       
   197     ::UpdateWindow(webViewWindow);
       
   198 }
       
   199 
       
   200 void dumpFrameScrollPosition(IWebFrame* frame)
       
   201 {
       
   202     if (!frame)
       
   203         return;
       
   204 
       
   205     COMPtr<IWebFramePrivate> framePrivate;
       
   206     if (FAILED(frame->QueryInterface(&framePrivate)))
       
   207         return;
       
   208 
       
   209     SIZE scrollPosition;
       
   210     if (FAILED(framePrivate->scrollOffset(&scrollPosition)))
       
   211         return;
       
   212 
       
   213     if (abs(scrollPosition.cx) > 0.00000001 || abs(scrollPosition.cy) > 0.00000001) {
       
   214         COMPtr<IWebFrame> parent;
       
   215         if (FAILED(frame->parentFrame(&parent)))
       
   216             return;
       
   217         if (parent) {
       
   218             BSTR name;
       
   219             if (FAILED(frame->name(&name)))
       
   220                 return;
       
   221             printf("frame '%S' ", name ? name : L"");
       
   222             SysFreeString(name);
       
   223         }
       
   224         printf("scrolled to %.f,%.f\n", (double)scrollPosition.cx, (double)scrollPosition.cy);
       
   225     }
       
   226 
       
   227     if (::layoutTestController->dumpChildFrameScrollPositions()) {
       
   228         COMPtr<IEnumVARIANT> enumKids;
       
   229         if (FAILED(frame->childFrames(&enumKids)))
       
   230             return;
       
   231         VARIANT var;
       
   232         VariantInit(&var);
       
   233         while (enumKids->Next(1, &var, 0) == S_OK) {
       
   234             ASSERT(V_VT(&var) == VT_UNKNOWN);
       
   235             COMPtr<IWebFrame> framePtr;
       
   236             V_UNKNOWN(&var)->QueryInterface(IID_IWebFrame, (void**)&framePtr);
       
   237             dumpFrameScrollPosition(framePtr.get());
       
   238             VariantClear(&var);
       
   239         }
       
   240     }
       
   241 }
       
   242 
       
   243 static wstring dumpFramesAsText(IWebFrame* frame)
       
   244 {
       
   245     if (!frame)
       
   246         return L"";
       
   247 
       
   248     COMPtr<IDOMDocument> document;
       
   249     if (FAILED(frame->DOMDocument(&document)))
       
   250         return L"";
       
   251 
       
   252     COMPtr<IDOMElement> documentElement;
       
   253     if (FAILED(document->documentElement(&documentElement)))
       
   254         return L"";
       
   255 
       
   256     wstring result;
       
   257 
       
   258     // Add header for all but the main frame.
       
   259     COMPtr<IWebFrame> parent;
       
   260     if (FAILED(frame->parentFrame(&parent)))
       
   261         return L"";
       
   262     if (parent) {
       
   263         BSTR name = L"";
       
   264         if (FAILED(frame->name(&name)))
       
   265             return L"";
       
   266 
       
   267         result.append(L"\n--------\nFrame: '");
       
   268         result.append(name ? name : L"");
       
   269         result.append(L"'\n--------\n");
       
   270 
       
   271         SysFreeString(name);
       
   272     }
       
   273 
       
   274     BSTR innerText = 0;
       
   275     COMPtr<IDOMElementPrivate> docPrivate;
       
   276     if (SUCCEEDED(documentElement->QueryInterface(&docPrivate)))
       
   277         docPrivate->innerText(&innerText);
       
   278 
       
   279     result.append(innerText ? innerText : L"");
       
   280     result.append(L"\n");
       
   281 
       
   282     SysFreeString(innerText);
       
   283 
       
   284     if (::layoutTestController->dumpChildFramesAsText()) {
       
   285         COMPtr<IEnumVARIANT> enumKids;
       
   286         if (FAILED(frame->childFrames(&enumKids)))
       
   287             return L"";
       
   288         VARIANT var;
       
   289         VariantInit(&var);
       
   290         while (enumKids->Next(1, &var, 0) == S_OK) {
       
   291             ASSERT(V_VT(&var) == VT_UNKNOWN);
       
   292             COMPtr<IWebFrame> framePtr;
       
   293             V_UNKNOWN(&var)->QueryInterface(IID_IWebFrame, (void**)&framePtr);
       
   294             result.append(dumpFramesAsText(framePtr.get()));
       
   295             VariantClear(&var);
       
   296         }
       
   297     }
       
   298 
       
   299     return result;
       
   300 }
       
   301 
       
   302 static int compareHistoryItems(const void* item1, const void* item2)
       
   303 {
       
   304     COMPtr<IWebHistoryItemPrivate> itemA;
       
   305     if (FAILED((*(COMPtr<IUnknown>*)item1)->QueryInterface(&itemA)))
       
   306         return 0;
       
   307 
       
   308     COMPtr<IWebHistoryItemPrivate> itemB;
       
   309     if (FAILED((*(COMPtr<IUnknown>*)item2)->QueryInterface(&itemB)))
       
   310         return 0;
       
   311 
       
   312     BSTR targetA;
       
   313     if (FAILED(itemA->target(&targetA)))
       
   314         return 0;
       
   315 
       
   316     BSTR targetB;
       
   317     if (FAILED(itemB->target(&targetB))) {
       
   318         SysFreeString(targetA);
       
   319         return 0;
       
   320     }
       
   321 
       
   322     int result = wcsicmp(wstring(targetA, SysStringLen(targetA)).c_str(), wstring(targetB, SysStringLen(targetB)).c_str());
       
   323     SysFreeString(targetA);
       
   324     SysFreeString(targetB);
       
   325     return result;
       
   326 }
       
   327 
       
   328 static void dumpHistoryItem(IWebHistoryItem* item, int indent, bool current)
       
   329 {
       
   330     assert(item);
       
   331 
       
   332     int start = 0;
       
   333     if (current) {
       
   334         printf("curr->");
       
   335         start = 6;
       
   336     }
       
   337     for (int i = start; i < indent; i++)
       
   338         putchar(' ');
       
   339 
       
   340     BSTR url;
       
   341     if (FAILED(item->URLString(&url)))
       
   342         return;
       
   343     printf("%S", url ? url : L"");
       
   344     SysFreeString(url);
       
   345 
       
   346     COMPtr<IWebHistoryItemPrivate> itemPrivate;
       
   347     if (FAILED(item->QueryInterface(&itemPrivate)))
       
   348         return;
       
   349 
       
   350     BSTR target;
       
   351     if (FAILED(itemPrivate->target(&target)))
       
   352         return;
       
   353     if (SysStringLen(target))
       
   354         printf(" (in frame \"%S\")", target);
       
   355     SysFreeString(target);
       
   356     BOOL isTargetItem = FALSE;
       
   357     if (FAILED(itemPrivate->isTargetItem(&isTargetItem)))
       
   358         return;
       
   359     if (isTargetItem)
       
   360         printf("  **nav target**");
       
   361     putchar('\n');
       
   362 
       
   363     unsigned kidsCount;
       
   364     SAFEARRAY* arrPtr;
       
   365     if (FAILED(itemPrivate->children(&kidsCount, &arrPtr)) || !kidsCount)
       
   366         return;
       
   367 
       
   368     Vector<COMPtr<IUnknown> > kidsVector;
       
   369 
       
   370     LONG lowerBound;
       
   371     if (FAILED(::SafeArrayGetLBound(arrPtr, 1, &lowerBound)))
       
   372         goto exit;
       
   373 
       
   374     LONG upperBound;
       
   375     if (FAILED(::SafeArrayGetUBound(arrPtr, 1, &upperBound)))
       
   376         goto exit;
       
   377 
       
   378     LONG length = upperBound - lowerBound + 1;
       
   379     if (!length)
       
   380         goto exit;
       
   381     ASSERT(length == kidsCount);
       
   382 
       
   383     IUnknown** safeArrayData;
       
   384     if (FAILED(::SafeArrayAccessData(arrPtr, (void**)&safeArrayData)))
       
   385         goto exit;
       
   386 
       
   387     for (int i = 0; i < length; ++i)
       
   388         kidsVector.append(safeArrayData[i]);
       
   389     ::SafeArrayUnaccessData(arrPtr);
       
   390 
       
   391     // must sort to eliminate arbitrary result ordering which defeats reproducible testing
       
   392     qsort(kidsVector.data(), kidsCount, sizeof(kidsVector[0]), compareHistoryItems);
       
   393 
       
   394     for (unsigned i = 0; i < kidsCount; ++i) {
       
   395         COMPtr<IWebHistoryItem> item;
       
   396         kidsVector[i]->QueryInterface(&item);
       
   397         dumpHistoryItem(item.get(), indent + 4, false);
       
   398     }
       
   399 
       
   400 exit:
       
   401     if (arrPtr && SUCCEEDED(::SafeArrayUnlock(arrPtr)))
       
   402         ::SafeArrayDestroy(arrPtr);
       
   403 }
       
   404 
       
   405 static void dumpBackForwardList(IWebFrame* frame)
       
   406 {
       
   407     assert(frame);
       
   408 
       
   409     printf("\n============== Back Forward List ==============\n");
       
   410     COMPtr<IWebView> webView;
       
   411     if (FAILED(frame->webView(&webView)))
       
   412         return;
       
   413 
       
   414     COMPtr<IWebBackForwardList> bfList;
       
   415     if (FAILED(webView->backForwardList(&bfList)))
       
   416         return;
       
   417 
       
   418     // Print out all items in the list after prevTestBFItem, which was from the previous test
       
   419     // Gather items from the end of the list, the print them out from oldest to newest
       
   420 
       
   421     Vector<COMPtr<IUnknown> > itemsToPrint;
       
   422 
       
   423     int forwardListCount;
       
   424     if (FAILED(bfList->forwardListCount(&forwardListCount)))
       
   425         return;
       
   426 
       
   427     for (int i = forwardListCount; i > 0; --i) {
       
   428         COMPtr<IWebHistoryItem> item;
       
   429         if (FAILED(bfList->itemAtIndex(i, &item)))
       
   430             return;
       
   431         // something is wrong if the item from the last test is in the forward part of the b/f list
       
   432         assert(item != prevTestBFItem);
       
   433         COMPtr<IUnknown> itemUnknown;
       
   434         item->QueryInterface(&itemUnknown);
       
   435         itemsToPrint.append(itemUnknown);
       
   436     }
       
   437     
       
   438     COMPtr<IWebHistoryItem> currentItem;
       
   439     if (FAILED(bfList->currentItem(&currentItem)))
       
   440         return;
       
   441 
       
   442     assert(currentItem != prevTestBFItem);
       
   443     COMPtr<IUnknown> currentItemUnknown;
       
   444     currentItem->QueryInterface(&currentItemUnknown);
       
   445     itemsToPrint.append(currentItemUnknown);
       
   446     int currentItemIndex = itemsToPrint.size() - 1;
       
   447 
       
   448     int backListCount;
       
   449     if (FAILED(bfList->backListCount(&backListCount)))
       
   450         return;
       
   451 
       
   452     for (int i = -1; i >= -backListCount; --i) {
       
   453         COMPtr<IWebHistoryItem> item;
       
   454         if (FAILED(bfList->itemAtIndex(i, &item)))
       
   455             return;
       
   456         if (item == prevTestBFItem)
       
   457             break;
       
   458         COMPtr<IUnknown> itemUnknown;
       
   459         item->QueryInterface(&itemUnknown);
       
   460         itemsToPrint.append(itemUnknown);
       
   461     }
       
   462 
       
   463     for (int i = itemsToPrint.size() - 1; i >= 0; --i) {
       
   464         COMPtr<IWebHistoryItem> historyItemToPrint;
       
   465         itemsToPrint[i]->QueryInterface(&historyItemToPrint);
       
   466         dumpHistoryItem(historyItemToPrint.get(), 8, i == currentItemIndex);
       
   467     }
       
   468 
       
   469     printf("===============================================\n");
       
   470 }
       
   471 
       
   472 void dump()
       
   473 {
       
   474     COMPtr<IWebDataSource> dataSource;
       
   475     if (SUCCEEDED(frame->dataSource(&dataSource))) {
       
   476         COMPtr<IWebURLResponse> response;
       
   477         if (SUCCEEDED(dataSource->response(&response)) && response) {
       
   478             BSTR mimeType;
       
   479             if (SUCCEEDED(response->MIMEType(&mimeType)))
       
   480                 ::layoutTestController->setDumpAsText(::layoutTestController->dumpAsText() | !_tcscmp(mimeType, TEXT("text/plain")));
       
   481             SysFreeString(mimeType);
       
   482         }
       
   483     }
       
   484 
       
   485     BSTR resultString = 0;
       
   486 
       
   487     if (dumpTree) {
       
   488         if (::layoutTestController->dumpAsText()) {
       
   489             ::InvalidateRect(webViewWindow, 0, TRUE);
       
   490             ::SendMessage(webViewWindow, WM_PAINT, 0, 0);
       
   491             wstring result = dumpFramesAsText(frame);
       
   492             resultString = SysAllocStringLen(result.data(), result.size());
       
   493         } else {
       
   494             bool isSVGW3CTest = strstr(currentTest, "svg\\W3C-SVG-1.1");
       
   495             unsigned width;
       
   496             unsigned height;
       
   497             if (isSVGW3CTest) {
       
   498                 width = 480;
       
   499                 height = 360;
       
   500             } else {
       
   501                 width = maxViewWidth;
       
   502                 height = maxViewHeight;
       
   503             }
       
   504 
       
   505             ::SetWindowPos(webViewWindow, 0, 0, 0, width, height, SWP_NOMOVE);
       
   506             ::InvalidateRect(webViewWindow, 0, TRUE);
       
   507             ::SendMessage(webViewWindow, WM_PAINT, 0, 0);
       
   508 
       
   509             COMPtr<IWebFramePrivate> framePrivate;
       
   510             if (FAILED(frame->QueryInterface(&framePrivate)))
       
   511                 goto fail;
       
   512             framePrivate->renderTreeAsExternalRepresentation(&resultString);
       
   513         }
       
   514         
       
   515         if (!resultString)
       
   516             printf("ERROR: nil result from %s", ::layoutTestController->dumpAsText() ? "IDOMElement::innerText" : "IFrameViewPrivate::renderTreeAsExternalRepresentation");
       
   517         else {
       
   518             unsigned stringLength = SysStringLen(resultString);
       
   519             int bufferSize = ::WideCharToMultiByte(CP_UTF8, 0, resultString, stringLength, 0, 0, 0, 0);
       
   520             char* buffer = (char*)malloc(bufferSize + 1);
       
   521             int result = ::WideCharToMultiByte(CP_UTF8, 0, resultString, stringLength, buffer, bufferSize + 1, 0, 0);
       
   522             buffer[bufferSize] = '\0';
       
   523             printf("%s", buffer);
       
   524             free(buffer);
       
   525             if (!::layoutTestController->dumpAsText())
       
   526                 dumpFrameScrollPosition(frame);
       
   527         }
       
   528         if (::layoutTestController->dumpBackForwardList())
       
   529             dumpBackForwardList(frame);
       
   530     }
       
   531 
       
   532     if (printSeparators)
       
   533         puts("#EOF");
       
   534 fail:
       
   535     SysFreeString(resultString);
       
   536     // This will exit from our message loop
       
   537     PostQuitMessage(0);
       
   538     done = true;
       
   539 }
       
   540 
       
   541 static void runTest(const char* pathOrURL)
       
   542 {
       
   543     static BSTR methodBStr = SysAllocString(TEXT("GET"));
       
   544 
       
   545     BSTR urlBStr;
       
   546  
       
   547     CFStringRef str = CFStringCreateWithCString(0, pathOrURL, kCFStringEncodingWindowsLatin1);
       
   548     CFURLRef url = CFURLCreateWithString(0, str, 0);
       
   549 
       
   550     if (!url)
       
   551         url = CFURLCreateWithFileSystemPath(0, str, kCFURLWindowsPathStyle, false);
       
   552 
       
   553     CFRelease(str);
       
   554 
       
   555     str = CFURLGetString(url);
       
   556 
       
   557     CFIndex length = CFStringGetLength(str);
       
   558     UniChar* buffer = new UniChar[length];
       
   559 
       
   560     CFStringGetCharacters(str, CFRangeMake(0, length), buffer);
       
   561     urlBStr = SysAllocStringLen((OLECHAR*)buffer, length);
       
   562     delete[] buffer;
       
   563 
       
   564     CFRelease(url);
       
   565 
       
   566     currentTest = pathOrURL;
       
   567 
       
   568     ::layoutTestController = new LayoutTestController(false, false);
       
   569     done = false;
       
   570     topLoadingFrame = 0;
       
   571     timedOut = false;
       
   572 
       
   573     prevTestBFItem = 0;
       
   574     COMPtr<IWebView> webView;
       
   575     if (SUCCEEDED(frame->webView(&webView))) {
       
   576         COMPtr<IWebBackForwardList> bfList;
       
   577         if (SUCCEEDED(webView->backForwardList(&bfList)))
       
   578             bfList->currentItem(&prevTestBFItem);
       
   579 
       
   580         COMPtr<IWebIBActions> webIBActions;
       
   581         if (SUCCEEDED(webView->QueryInterface(IID_IWebIBActions, (void**)&webIBActions)))
       
   582             webIBActions->makeTextStandardSize(0);
       
   583     }
       
   584 
       
   585     WorkQueue::shared()->clear();
       
   586     WorkQueue::shared()->setFrozen(false);
       
   587 
       
   588     // Set the test timeout timer
       
   589     SetTimer(hostWindow, timeoutId, timeoutValue, 0);
       
   590 
       
   591     COMPtr<IWebMutableURLRequest> request;
       
   592     HRESULT hr = CoCreateInstance(CLSID_WebMutableURLRequest, 0, CLSCTX_ALL, IID_IWebMutableURLRequest, (void**)&request);
       
   593     if (FAILED(hr))
       
   594         goto exit;
       
   595 
       
   596     request->initWithURL(urlBStr, WebURLRequestUseProtocolCachePolicy, 0);
       
   597 
       
   598     request->setHTTPMethod(methodBStr);
       
   599     frame->loadRequest(request.get());
       
   600 
       
   601     MSG msg;
       
   602     while (GetMessage(&msg, 0, 0, 0)) {
       
   603         TranslateMessage(&msg);
       
   604         DispatchMessage(&msg);
       
   605     }
       
   606     KillTimer(hostWindow, timeoutId);
       
   607 
       
   608     if (timedOut) {
       
   609         fprintf(stderr, "ERROR: Timed out running %s\n", pathOrURL);
       
   610         printf("ERROR: Timed out loading page\n");
       
   611 
       
   612         if (printSeparators)
       
   613             puts("#EOF");
       
   614     }
       
   615 exit:
       
   616     SysFreeString(urlBStr);
       
   617     return;
       
   618 }
       
   619 
       
   620 static void initializePreferences(IWebPreferences* preferences)
       
   621 {
       
   622 #ifdef USE_MAC_FONTS
       
   623     BSTR standardFamily = SysAllocString(TEXT("Times"));
       
   624     BSTR fixedFamily = SysAllocString(TEXT("Courier"));
       
   625     BSTR sansSerifFamily = SysAllocString(TEXT("Helvetica"));
       
   626     BSTR cursiveFamily = SysAllocString(TEXT("Apple Chancery"));
       
   627     BSTR fantasyFamily = SysAllocString(TEXT("Papyrus"));
       
   628 #else
       
   629     BSTR standardFamily = SysAllocString(TEXT("Times New Roman"));
       
   630     BSTR fixedFamily = SysAllocString(TEXT("Courier New"));
       
   631     BSTR sansSerifFamily = SysAllocString(TEXT("Arial"));
       
   632     BSTR cursiveFamily = SysAllocString(TEXT("Comic Sans MS")); // Not actually cursive, but it's what IE and Firefox use.
       
   633     BSTR fantasyFamily = SysAllocString(TEXT("Times New Roman"));
       
   634 #endif
       
   635 
       
   636     preferences->setStandardFontFamily(standardFamily);
       
   637     preferences->setFixedFontFamily(fixedFamily);
       
   638     preferences->setSerifFontFamily(standardFamily);
       
   639     preferences->setSansSerifFontFamily(sansSerifFamily);
       
   640     preferences->setCursiveFontFamily(cursiveFamily);
       
   641     preferences->setFantasyFontFamily(fantasyFamily);
       
   642 
       
   643     preferences->setAutosaves(FALSE);
       
   644     preferences->setJavaEnabled(FALSE);
       
   645     preferences->setPlugInsEnabled(TRUE);
       
   646     preferences->setDOMPasteAllowed(TRUE);
       
   647     preferences->setEditableLinkBehavior(WebKitEditableLinkOnlyLiveWithShiftKey);
       
   648 
       
   649     SysFreeString(standardFamily);
       
   650     SysFreeString(fixedFamily);
       
   651     SysFreeString(sansSerifFamily);
       
   652     SysFreeString(cursiveFamily);
       
   653     SysFreeString(fantasyFamily);
       
   654 }
       
   655 
       
   656 static Boolean pthreadEqualCallback(const void* value1, const void* value2)
       
   657 {
       
   658     return (Boolean)pthread_equal(*(pthread_t*)value1, *(pthread_t*)value2);
       
   659 }
       
   660 
       
   661 static CFDictionaryKeyCallBacks pthreadKeyCallbacks = { 0, 0, 0, 0, pthreadEqualCallback, 0 };
       
   662 
       
   663 static pthread_mutex_t javaScriptThreadsMutex = PTHREAD_MUTEX_INITIALIZER;
       
   664 static bool javaScriptThreadsShouldTerminate;
       
   665 
       
   666 static const int javaScriptThreadsCount = 4;
       
   667 static CFMutableDictionaryRef javaScriptThreads()
       
   668 {
       
   669     assert(pthread_mutex_trylock(&javaScriptThreadsMutex) == EBUSY);
       
   670     static CFMutableDictionaryRef staticJavaScriptThreads;
       
   671     if (!staticJavaScriptThreads)
       
   672         staticJavaScriptThreads = CFDictionaryCreateMutable(0, 0, &pthreadKeyCallbacks, 0);
       
   673     return staticJavaScriptThreads;
       
   674 }
       
   675 
       
   676 // Loops forever, running a script and randomly respawning, until 
       
   677 // javaScriptThreadsShouldTerminate becomes true.
       
   678 void* runJavaScriptThread(void* arg)
       
   679 {
       
   680     const char* const script =
       
   681     " \
       
   682     var array = []; \
       
   683     for (var i = 0; i < 10; i++) { \
       
   684         array.push(String(i)); \
       
   685     } \
       
   686     ";
       
   687 
       
   688     while (true) {
       
   689         JSGlobalContextRef ctx = JSGlobalContextCreate(0);
       
   690         JSStringRef scriptRef = JSStringCreateWithUTF8CString(script);
       
   691 
       
   692         JSValueRef exception = 0;
       
   693         JSEvaluateScript(ctx, scriptRef, 0, 0, 0, &exception);
       
   694         assert(!exception);
       
   695         
       
   696         JSGlobalContextRelease(ctx);
       
   697         JSStringRelease(scriptRef);
       
   698         
       
   699         JSGarbageCollect(ctx);
       
   700 
       
   701         pthread_mutex_lock(&javaScriptThreadsMutex);
       
   702 
       
   703         // Check for cancellation.
       
   704         if (javaScriptThreadsShouldTerminate) {
       
   705             pthread_mutex_unlock(&javaScriptThreadsMutex);
       
   706             return 0;
       
   707         }
       
   708 
       
   709         // Respawn probabilistically.
       
   710         if (rand() % 5 == 0) {
       
   711             pthread_t pthread;
       
   712             pthread_create(&pthread, 0, &runJavaScriptThread, 0);
       
   713             pthread_detach(pthread);
       
   714 
       
   715             pthread_t self = pthread_self();
       
   716             CFDictionaryRemoveValue(javaScriptThreads(), self.p);
       
   717             CFDictionaryAddValue(javaScriptThreads(), pthread.p, 0);
       
   718 
       
   719             pthread_mutex_unlock(&javaScriptThreadsMutex);
       
   720             return 0;
       
   721         }
       
   722 
       
   723         pthread_mutex_unlock(&javaScriptThreadsMutex);
       
   724     }
       
   725 }
       
   726 
       
   727 static void startJavaScriptThreads(void)
       
   728 {
       
   729     pthread_mutex_lock(&javaScriptThreadsMutex);
       
   730 
       
   731     for (int i = 0; i < javaScriptThreadsCount; i++) {
       
   732         pthread_t pthread;
       
   733         pthread_create(&pthread, 0, &runJavaScriptThread, 0);
       
   734         pthread_detach(pthread);
       
   735         CFDictionaryAddValue(javaScriptThreads(), pthread.p, 0);
       
   736     }
       
   737 
       
   738     pthread_mutex_unlock(&javaScriptThreadsMutex);
       
   739 }
       
   740 
       
   741 static void stopJavaScriptThreads(void)
       
   742 {
       
   743     pthread_mutex_lock(&javaScriptThreadsMutex);
       
   744 
       
   745     javaScriptThreadsShouldTerminate = true;
       
   746 
       
   747     pthread_t* pthreads[javaScriptThreadsCount] = {0};
       
   748     int threadDictCount = CFDictionaryGetCount(javaScriptThreads());
       
   749     assert(threadDictCount == javaScriptThreadsCount);
       
   750     CFDictionaryGetKeysAndValues(javaScriptThreads(), (const void**)pthreads, 0);
       
   751 
       
   752     pthread_mutex_unlock(&javaScriptThreadsMutex);
       
   753 
       
   754     for (int i = 0; i < javaScriptThreadsCount; i++) {
       
   755         pthread_t* pthread = pthreads[i];
       
   756         pthread_join(*pthread, 0);
       
   757         free(pthread);
       
   758     }
       
   759 }
       
   760 
       
   761 int main(int argc, char* argv[])
       
   762 {
       
   763     leakChecking = false;
       
   764 
       
   765     wstring exePath = initialize(GetModuleHandle(0));
       
   766 
       
   767     // FIXME: options
       
   768 
       
   769     COMPtr<IWebView> webView;
       
   770     HRESULT hr = CoCreateInstance(CLSID_WebView, 0, CLSCTX_ALL, IID_IWebView, (void**)&webView);
       
   771     if (FAILED(hr)) {
       
   772         fprintf(stderr, "Failed to create CLSID_WebView instance, error 0x%x\n", hr);
       
   773         return -1;
       
   774     }
       
   775 
       
   776     if (FAILED(webView->setHostWindow((OLE_HANDLE)(ULONG64)hostWindow)))
       
   777         return -1;
       
   778 
       
   779     RECT clientRect;
       
   780     clientRect.bottom = clientRect.left = clientRect.top = clientRect.right = 0;
       
   781     BSTR groupName = SysAllocString(L"org.webkit.DumpRenderTree");
       
   782     bool failed = FAILED(webView->initWithFrame(clientRect, 0, groupName));
       
   783     SysFreeString(groupName);
       
   784     if (failed)
       
   785         return -1;
       
   786 
       
   787     COMPtr<IWebViewPrivate> viewPrivate;
       
   788     if (FAILED(webView->QueryInterface(&viewPrivate)))
       
   789         return -1;
       
   790     webView->Release();
       
   791 
       
   792 
       
   793     BSTR pluginPath = SysAllocStringLen(0, exePath.length() + _tcslen(TestPluginDir));
       
   794     _tcscpy(pluginPath, exePath.c_str());
       
   795     _tcscat(pluginPath, TestPluginDir);
       
   796     failed = FAILED(viewPrivate->addAdditionalPluginPath(pluginPath));
       
   797     SysFreeString(pluginPath);
       
   798     if (failed)
       
   799         return -1;
       
   800 
       
   801     if (FAILED(viewPrivate->viewWindow((OLE_HANDLE*)&webViewWindow)))
       
   802         return -1;
       
   803 
       
   804     SetWindowPos(webViewWindow, 0, 0, 0, maxViewWidth, maxViewHeight, 0);
       
   805     ShowWindow(hostWindow, SW_SHOW);
       
   806 
       
   807     COMPtr<FrameLoadDelegate> frameLoadDelegate;
       
   808     frameLoadDelegate.adoptRef(new FrameLoadDelegate);
       
   809     if (FAILED(webView->setFrameLoadDelegate(frameLoadDelegate.get())))
       
   810         return -1;
       
   811 
       
   812     COMPtr<UIDelegate> uiDelegate;
       
   813     uiDelegate.adoptRef(new UIDelegate);
       
   814     if (FAILED(webView->setUIDelegate(uiDelegate.get())))
       
   815         return -1;
       
   816 
       
   817     COMPtr<IWebViewEditing> viewEditing;
       
   818     if (FAILED(webView->QueryInterface(&viewEditing)))
       
   819         return -1;
       
   820     webView->Release();
       
   821 
       
   822     COMPtr<EditingDelegate> editingDelegate;
       
   823     editingDelegate.adoptRef(new EditingDelegate);
       
   824     if (FAILED(viewEditing->setEditingDelegate(editingDelegate.get())))
       
   825         return -1;
       
   826 
       
   827     COMPtr<IWebPreferences> preferences;
       
   828     if (FAILED(webView->preferences(&preferences)))
       
   829         return -1;
       
   830 
       
   831     initializePreferences(preferences.get());
       
   832 
       
   833     COMPtr<IWebIconDatabase> iconDatabase;
       
   834     COMPtr<IWebIconDatabase> tmpIconDatabase;
       
   835     if (FAILED(CoCreateInstance(CLSID_WebIconDatabase, 0, CLSCTX_ALL, IID_IWebIconDatabase, (void**)&tmpIconDatabase)))
       
   836         return -1;
       
   837     if (FAILED(tmpIconDatabase->sharedIconDatabase(&iconDatabase)))
       
   838         return -1;
       
   839         
       
   840     if (FAILED(webView->mainFrame(&frame)))
       
   841         return -1;
       
   842 
       
   843     _CrtMemState entryToMainMemCheckpoint;
       
   844     if (leakChecking)
       
   845         _CrtMemCheckpoint(&entryToMainMemCheckpoint);
       
   846 
       
   847     for (int i = 0; i < argc; ++i)
       
   848         if (!stricmp(argv[i], "--threaded")) {
       
   849             argv[i] = argv[argc - 1];
       
   850             argc--;
       
   851             threaded = true;
       
   852             break;
       
   853         }
       
   854 
       
   855     if (threaded)
       
   856         startJavaScriptThreads();
       
   857 
       
   858     if (argc == 2 && strcmp(argv[1], "-") == 0) {
       
   859         char filenameBuffer[2048];
       
   860         printSeparators = true;
       
   861         while (fgets(filenameBuffer, sizeof(filenameBuffer), stdin)) {
       
   862             char* newLineCharacter = strchr(filenameBuffer, '\n');
       
   863             if (newLineCharacter)
       
   864                 *newLineCharacter = '\0';
       
   865             
       
   866             if (strlen(filenameBuffer) == 0)
       
   867                 continue;
       
   868 
       
   869             runTest(filenameBuffer);
       
   870             fflush(stdout);
       
   871         }
       
   872     } else {
       
   873         printSeparators = argc > 2;
       
   874         for (int i = 1; i != argc; i++)
       
   875             runTest(argv[i]);
       
   876     }
       
   877 
       
   878     if (threaded)
       
   879         stopJavaScriptThreads();
       
   880     
       
   881     frame->Release();
       
   882 
       
   883     if (leakChecking) {
       
   884         // dump leaks to stderr
       
   885         _CrtSetReportMode(_CRT_WARN, _CRTDBG_MODE_FILE);
       
   886         _CrtSetReportFile(_CRT_WARN, _CRTDBG_FILE_STDERR);
       
   887         _CrtMemDumpAllObjectsSince(&entryToMainMemCheckpoint);
       
   888     }
       
   889 
       
   890     return 0;
       
   891 }