--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/webengine/osswebengine/WebKitTools/DumpRenderTree/win/DumpRenderTree.cpp Mon Mar 30 12:54:55 2009 +0300
@@ -0,0 +1,891 @@
+/*
+ * Copyright (C) 2005, 2006, 2007 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of
+ * its contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "DumpRenderTree.h"
+
+#include "EditingDelegate.h"
+#include "FrameLoaderDelegate.h"
+#include "LayoutTestController.h"
+#include "UIDelegate.h"
+#include "WorkQueueItem.h"
+#include "WorkQueue.h"
+#include <wtf/Vector.h>
+#include <WebCore/COMPtr.h>
+#include <CoreFoundation/CoreFoundation.h>
+#include <JavaScriptCore/JavaScriptCore.h>
+#include <math.h>
+#include <pthread.h>
+#include <string>
+#include <tchar.h>
+#include <WebKit/DOMPrivate.h>
+#include <WebKit/IWebFramePrivate.h>
+#include <WebKit/IWebHistoryItem.h>
+#include <WebKit/IWebHistoryItemPrivate.h>
+#include <WebKit/IWebURLResponse.h>
+#include <WebKit/IWebViewPrivate.h>
+#include <WebKit/WebKit.h>
+#include <windows.h>
+#include <stdio.h>
+
+using std::wstring;
+
+#ifdef _DEBUG
+const LPWSTR TestPluginDir = L"TestNetscapePlugin_Debug";
+#else
+const LPWSTR TestPluginDir = L"TestNetscapePlugin";
+#endif
+
+#define USE_MAC_FONTS
+
+const LPCWSTR kDumpRenderTreeClassName = L"DumpRenderTreeWindow";
+
+static bool dumpTree = true;
+static bool printSeparators;
+static bool leakChecking = false;
+static bool timedOut = false;
+static bool threaded = false;
+
+static const char* currentTest;
+
+volatile bool done;
+// This is the topmost frame that is loading, during a given load, or nil when no load is
+// in progress. Usually this is the same as the main frame, but not always. In the case
+// where a frameset is loaded, and then new content is loaded into one of the child frames,
+// that child frame is the "topmost frame that is loading".
+IWebFrame* topLoadingFrame; // !nil iff a load is in progress
+static COMPtr<IWebHistoryItem> prevTestBFItem; // current b/f item at the end of the previous test
+
+IWebFrame* frame;
+HWND webViewWindow;
+static HWND hostWindow;
+
+LayoutTestController* layoutTestController = 0;
+CFRunLoopTimerRef waitToDumpWatchdog = 0;
+
+static const unsigned timeoutValue = 60000;
+static const unsigned timeoutId = 10;
+
+const unsigned maxViewWidth = 800;
+const unsigned maxViewHeight = 600;
+
+static LRESULT CALLBACK DumpRenderTreeWndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
+{
+ switch (msg) {
+ case WM_TIMER:
+ // The test ran long enough to time out
+ timedOut = true;
+ PostQuitMessage(0);
+ return 0;
+ break;
+ default:
+ return DefWindowProc(hWnd, msg, wParam, lParam);
+ }
+}
+
+extern "C" BOOL InitializeCoreGraphics();
+
+#ifdef DEBUG_WEBKIT_HAS_SUFFIX
+#define WEBKITDLL TEXT("WebKit_debug.dll")
+#else
+#define WEBKITDLL TEXT("WebKit.dll")
+#endif
+
+static wstring initialize(HMODULE hModule)
+{
+ if (HMODULE webKitModule = LoadLibrary(WEBKITDLL))
+ if (FARPROC dllRegisterServer = GetProcAddress(webKitModule, "DllRegisterServer"))
+ dllRegisterServer();
+
+ static LPCTSTR fontsToInstall[] = {
+ TEXT("AHEM____.ttf"),
+ TEXT("Apple Chancery.ttf"),
+ TEXT("Courier Bold.ttf"),
+ TEXT("Courier.ttf"),
+ TEXT("Helvetica Bold.ttf"),
+ TEXT("Helvetica.ttf"),
+ TEXT("Helvetica Neue Bold Italic.ttf"),
+ TEXT("Helvetica Neue Bold.ttf"),
+ TEXT("Helvetica Neue Condensed Black.ttf"),
+ TEXT("Helvetica Neue Condensed Bold.ttf"),
+ TEXT("Helvetica Neue Italic.ttf"),
+ TEXT("Helvetica Neue Light Italic.ttf"),
+ TEXT("Helvetica Neue Light.ttf"),
+ TEXT("Helvetica Neue UltraLight Italic.ttf"),
+ TEXT("Helvetica Neue UltraLight.ttf"),
+ TEXT("Helvetica Neue.ttf"),
+ TEXT("Lucida Grande.ttf"),
+ TEXT("Lucida Grande Bold.ttf"),
+ TEXT("Monaco.ttf"),
+ TEXT("Papyrus.ttf"),
+ TEXT("Times Bold Italic.ttf"),
+ TEXT("Times Bold.ttf"),
+ TEXT("Times Italic.ttf"),
+ TEXT("Times Roman.ttf")
+ };
+
+ TCHAR buffer[MAX_PATH];
+ GetModuleFileName(hModule, buffer, ARRAYSIZE(buffer));
+ wstring exePath(buffer);
+ int lastSlash = exePath.rfind('\\');
+ if (lastSlash != -1 && lastSlash + 1< exePath.length())
+ exePath = exePath.substr(0, lastSlash + 1);
+
+ wstring resourcesPath(exePath + TEXT("DumpRenderTree.resources\\"));
+
+ for (int i = 0; i < ARRAYSIZE(fontsToInstall); ++i)
+ AddFontResourceEx(wstring(resourcesPath + fontsToInstall[i]).c_str(), FR_PRIVATE, 0);
+
+ // Init COM
+ OleInitialize(0);
+
+ // Initialize CG
+ InitializeCoreGraphics();
+
+ // Register a host window
+ WNDCLASSEX wcex;
+
+ wcex.cbSize = sizeof(WNDCLASSEX);
+
+ wcex.style = CS_HREDRAW | CS_VREDRAW;
+ wcex.lpfnWndProc = DumpRenderTreeWndProc;
+ wcex.cbClsExtra = 0;
+ wcex.cbWndExtra = 0;
+ wcex.hInstance = hModule;
+ wcex.hIcon = 0;
+ wcex.hCursor = LoadCursor(0, IDC_ARROW);
+ wcex.hbrBackground = 0;
+ wcex.lpszMenuName = 0;
+ wcex.lpszClassName = kDumpRenderTreeClassName;
+ wcex.hIconSm = 0;
+
+ RegisterClassEx(&wcex);
+
+ hostWindow = CreateWindowEx(WS_EX_TOOLWINDOW, kDumpRenderTreeClassName, TEXT("DumpRenderTree"), WS_POPUP,
+ -maxViewWidth, -maxViewHeight, maxViewWidth, maxViewHeight, 0, 0, hModule, 0);
+
+ return exePath;
+}
+
+void displayWebView()
+{
+ ::InvalidateRect(webViewWindow, 0, TRUE);
+ ::UpdateWindow(webViewWindow);
+}
+
+void dumpFrameScrollPosition(IWebFrame* frame)
+{
+ if (!frame)
+ return;
+
+ COMPtr<IWebFramePrivate> framePrivate;
+ if (FAILED(frame->QueryInterface(&framePrivate)))
+ return;
+
+ SIZE scrollPosition;
+ if (FAILED(framePrivate->scrollOffset(&scrollPosition)))
+ return;
+
+ if (abs(scrollPosition.cx) > 0.00000001 || abs(scrollPosition.cy) > 0.00000001) {
+ COMPtr<IWebFrame> parent;
+ if (FAILED(frame->parentFrame(&parent)))
+ return;
+ if (parent) {
+ BSTR name;
+ if (FAILED(frame->name(&name)))
+ return;
+ printf("frame '%S' ", name ? name : L"");
+ SysFreeString(name);
+ }
+ printf("scrolled to %.f,%.f\n", (double)scrollPosition.cx, (double)scrollPosition.cy);
+ }
+
+ if (::layoutTestController->dumpChildFrameScrollPositions()) {
+ COMPtr<IEnumVARIANT> enumKids;
+ if (FAILED(frame->childFrames(&enumKids)))
+ return;
+ VARIANT var;
+ VariantInit(&var);
+ while (enumKids->Next(1, &var, 0) == S_OK) {
+ ASSERT(V_VT(&var) == VT_UNKNOWN);
+ COMPtr<IWebFrame> framePtr;
+ V_UNKNOWN(&var)->QueryInterface(IID_IWebFrame, (void**)&framePtr);
+ dumpFrameScrollPosition(framePtr.get());
+ VariantClear(&var);
+ }
+ }
+}
+
+static wstring dumpFramesAsText(IWebFrame* frame)
+{
+ if (!frame)
+ return L"";
+
+ COMPtr<IDOMDocument> document;
+ if (FAILED(frame->DOMDocument(&document)))
+ return L"";
+
+ COMPtr<IDOMElement> documentElement;
+ if (FAILED(document->documentElement(&documentElement)))
+ return L"";
+
+ wstring result;
+
+ // Add header for all but the main frame.
+ COMPtr<IWebFrame> parent;
+ if (FAILED(frame->parentFrame(&parent)))
+ return L"";
+ if (parent) {
+ BSTR name = L"";
+ if (FAILED(frame->name(&name)))
+ return L"";
+
+ result.append(L"\n--------\nFrame: '");
+ result.append(name ? name : L"");
+ result.append(L"'\n--------\n");
+
+ SysFreeString(name);
+ }
+
+ BSTR innerText = 0;
+ COMPtr<IDOMElementPrivate> docPrivate;
+ if (SUCCEEDED(documentElement->QueryInterface(&docPrivate)))
+ docPrivate->innerText(&innerText);
+
+ result.append(innerText ? innerText : L"");
+ result.append(L"\n");
+
+ SysFreeString(innerText);
+
+ if (::layoutTestController->dumpChildFramesAsText()) {
+ COMPtr<IEnumVARIANT> enumKids;
+ if (FAILED(frame->childFrames(&enumKids)))
+ return L"";
+ VARIANT var;
+ VariantInit(&var);
+ while (enumKids->Next(1, &var, 0) == S_OK) {
+ ASSERT(V_VT(&var) == VT_UNKNOWN);
+ COMPtr<IWebFrame> framePtr;
+ V_UNKNOWN(&var)->QueryInterface(IID_IWebFrame, (void**)&framePtr);
+ result.append(dumpFramesAsText(framePtr.get()));
+ VariantClear(&var);
+ }
+ }
+
+ return result;
+}
+
+static int compareHistoryItems(const void* item1, const void* item2)
+{
+ COMPtr<IWebHistoryItemPrivate> itemA;
+ if (FAILED((*(COMPtr<IUnknown>*)item1)->QueryInterface(&itemA)))
+ return 0;
+
+ COMPtr<IWebHistoryItemPrivate> itemB;
+ if (FAILED((*(COMPtr<IUnknown>*)item2)->QueryInterface(&itemB)))
+ return 0;
+
+ BSTR targetA;
+ if (FAILED(itemA->target(&targetA)))
+ return 0;
+
+ BSTR targetB;
+ if (FAILED(itemB->target(&targetB))) {
+ SysFreeString(targetA);
+ return 0;
+ }
+
+ int result = wcsicmp(wstring(targetA, SysStringLen(targetA)).c_str(), wstring(targetB, SysStringLen(targetB)).c_str());
+ SysFreeString(targetA);
+ SysFreeString(targetB);
+ return result;
+}
+
+static void dumpHistoryItem(IWebHistoryItem* item, int indent, bool current)
+{
+ assert(item);
+
+ int start = 0;
+ if (current) {
+ printf("curr->");
+ start = 6;
+ }
+ for (int i = start; i < indent; i++)
+ putchar(' ');
+
+ BSTR url;
+ if (FAILED(item->URLString(&url)))
+ return;
+ printf("%S", url ? url : L"");
+ SysFreeString(url);
+
+ COMPtr<IWebHistoryItemPrivate> itemPrivate;
+ if (FAILED(item->QueryInterface(&itemPrivate)))
+ return;
+
+ BSTR target;
+ if (FAILED(itemPrivate->target(&target)))
+ return;
+ if (SysStringLen(target))
+ printf(" (in frame \"%S\")", target);
+ SysFreeString(target);
+ BOOL isTargetItem = FALSE;
+ if (FAILED(itemPrivate->isTargetItem(&isTargetItem)))
+ return;
+ if (isTargetItem)
+ printf(" **nav target**");
+ putchar('\n');
+
+ unsigned kidsCount;
+ SAFEARRAY* arrPtr;
+ if (FAILED(itemPrivate->children(&kidsCount, &arrPtr)) || !kidsCount)
+ return;
+
+ Vector<COMPtr<IUnknown> > kidsVector;
+
+ LONG lowerBound;
+ if (FAILED(::SafeArrayGetLBound(arrPtr, 1, &lowerBound)))
+ goto exit;
+
+ LONG upperBound;
+ if (FAILED(::SafeArrayGetUBound(arrPtr, 1, &upperBound)))
+ goto exit;
+
+ LONG length = upperBound - lowerBound + 1;
+ if (!length)
+ goto exit;
+ ASSERT(length == kidsCount);
+
+ IUnknown** safeArrayData;
+ if (FAILED(::SafeArrayAccessData(arrPtr, (void**)&safeArrayData)))
+ goto exit;
+
+ for (int i = 0; i < length; ++i)
+ kidsVector.append(safeArrayData[i]);
+ ::SafeArrayUnaccessData(arrPtr);
+
+ // must sort to eliminate arbitrary result ordering which defeats reproducible testing
+ qsort(kidsVector.data(), kidsCount, sizeof(kidsVector[0]), compareHistoryItems);
+
+ for (unsigned i = 0; i < kidsCount; ++i) {
+ COMPtr<IWebHistoryItem> item;
+ kidsVector[i]->QueryInterface(&item);
+ dumpHistoryItem(item.get(), indent + 4, false);
+ }
+
+exit:
+ if (arrPtr && SUCCEEDED(::SafeArrayUnlock(arrPtr)))
+ ::SafeArrayDestroy(arrPtr);
+}
+
+static void dumpBackForwardList(IWebFrame* frame)
+{
+ assert(frame);
+
+ printf("\n============== Back Forward List ==============\n");
+ COMPtr<IWebView> webView;
+ if (FAILED(frame->webView(&webView)))
+ return;
+
+ COMPtr<IWebBackForwardList> bfList;
+ if (FAILED(webView->backForwardList(&bfList)))
+ return;
+
+ // Print out all items in the list after prevTestBFItem, which was from the previous test
+ // Gather items from the end of the list, the print them out from oldest to newest
+
+ Vector<COMPtr<IUnknown> > itemsToPrint;
+
+ int forwardListCount;
+ if (FAILED(bfList->forwardListCount(&forwardListCount)))
+ return;
+
+ for (int i = forwardListCount; i > 0; --i) {
+ COMPtr<IWebHistoryItem> item;
+ if (FAILED(bfList->itemAtIndex(i, &item)))
+ return;
+ // something is wrong if the item from the last test is in the forward part of the b/f list
+ assert(item != prevTestBFItem);
+ COMPtr<IUnknown> itemUnknown;
+ item->QueryInterface(&itemUnknown);
+ itemsToPrint.append(itemUnknown);
+ }
+
+ COMPtr<IWebHistoryItem> currentItem;
+ if (FAILED(bfList->currentItem(¤tItem)))
+ return;
+
+ assert(currentItem != prevTestBFItem);
+ COMPtr<IUnknown> currentItemUnknown;
+ currentItem->QueryInterface(¤tItemUnknown);
+ itemsToPrint.append(currentItemUnknown);
+ int currentItemIndex = itemsToPrint.size() - 1;
+
+ int backListCount;
+ if (FAILED(bfList->backListCount(&backListCount)))
+ return;
+
+ for (int i = -1; i >= -backListCount; --i) {
+ COMPtr<IWebHistoryItem> item;
+ if (FAILED(bfList->itemAtIndex(i, &item)))
+ return;
+ if (item == prevTestBFItem)
+ break;
+ COMPtr<IUnknown> itemUnknown;
+ item->QueryInterface(&itemUnknown);
+ itemsToPrint.append(itemUnknown);
+ }
+
+ for (int i = itemsToPrint.size() - 1; i >= 0; --i) {
+ COMPtr<IWebHistoryItem> historyItemToPrint;
+ itemsToPrint[i]->QueryInterface(&historyItemToPrint);
+ dumpHistoryItem(historyItemToPrint.get(), 8, i == currentItemIndex);
+ }
+
+ printf("===============================================\n");
+}
+
+void dump()
+{
+ COMPtr<IWebDataSource> dataSource;
+ if (SUCCEEDED(frame->dataSource(&dataSource))) {
+ COMPtr<IWebURLResponse> response;
+ if (SUCCEEDED(dataSource->response(&response)) && response) {
+ BSTR mimeType;
+ if (SUCCEEDED(response->MIMEType(&mimeType)))
+ ::layoutTestController->setDumpAsText(::layoutTestController->dumpAsText() | !_tcscmp(mimeType, TEXT("text/plain")));
+ SysFreeString(mimeType);
+ }
+ }
+
+ BSTR resultString = 0;
+
+ if (dumpTree) {
+ if (::layoutTestController->dumpAsText()) {
+ ::InvalidateRect(webViewWindow, 0, TRUE);
+ ::SendMessage(webViewWindow, WM_PAINT, 0, 0);
+ wstring result = dumpFramesAsText(frame);
+ resultString = SysAllocStringLen(result.data(), result.size());
+ } else {
+ bool isSVGW3CTest = strstr(currentTest, "svg\\W3C-SVG-1.1");
+ unsigned width;
+ unsigned height;
+ if (isSVGW3CTest) {
+ width = 480;
+ height = 360;
+ } else {
+ width = maxViewWidth;
+ height = maxViewHeight;
+ }
+
+ ::SetWindowPos(webViewWindow, 0, 0, 0, width, height, SWP_NOMOVE);
+ ::InvalidateRect(webViewWindow, 0, TRUE);
+ ::SendMessage(webViewWindow, WM_PAINT, 0, 0);
+
+ COMPtr<IWebFramePrivate> framePrivate;
+ if (FAILED(frame->QueryInterface(&framePrivate)))
+ goto fail;
+ framePrivate->renderTreeAsExternalRepresentation(&resultString);
+ }
+
+ if (!resultString)
+ printf("ERROR: nil result from %s", ::layoutTestController->dumpAsText() ? "IDOMElement::innerText" : "IFrameViewPrivate::renderTreeAsExternalRepresentation");
+ else {
+ unsigned stringLength = SysStringLen(resultString);
+ int bufferSize = ::WideCharToMultiByte(CP_UTF8, 0, resultString, stringLength, 0, 0, 0, 0);
+ char* buffer = (char*)malloc(bufferSize + 1);
+ int result = ::WideCharToMultiByte(CP_UTF8, 0, resultString, stringLength, buffer, bufferSize + 1, 0, 0);
+ buffer[bufferSize] = '\0';
+ printf("%s", buffer);
+ free(buffer);
+ if (!::layoutTestController->dumpAsText())
+ dumpFrameScrollPosition(frame);
+ }
+ if (::layoutTestController->dumpBackForwardList())
+ dumpBackForwardList(frame);
+ }
+
+ if (printSeparators)
+ puts("#EOF");
+fail:
+ SysFreeString(resultString);
+ // This will exit from our message loop
+ PostQuitMessage(0);
+ done = true;
+}
+
+static void runTest(const char* pathOrURL)
+{
+ static BSTR methodBStr = SysAllocString(TEXT("GET"));
+
+ BSTR urlBStr;
+
+ CFStringRef str = CFStringCreateWithCString(0, pathOrURL, kCFStringEncodingWindowsLatin1);
+ CFURLRef url = CFURLCreateWithString(0, str, 0);
+
+ if (!url)
+ url = CFURLCreateWithFileSystemPath(0, str, kCFURLWindowsPathStyle, false);
+
+ CFRelease(str);
+
+ str = CFURLGetString(url);
+
+ CFIndex length = CFStringGetLength(str);
+ UniChar* buffer = new UniChar[length];
+
+ CFStringGetCharacters(str, CFRangeMake(0, length), buffer);
+ urlBStr = SysAllocStringLen((OLECHAR*)buffer, length);
+ delete[] buffer;
+
+ CFRelease(url);
+
+ currentTest = pathOrURL;
+
+ ::layoutTestController = new LayoutTestController(false, false);
+ done = false;
+ topLoadingFrame = 0;
+ timedOut = false;
+
+ prevTestBFItem = 0;
+ COMPtr<IWebView> webView;
+ if (SUCCEEDED(frame->webView(&webView))) {
+ COMPtr<IWebBackForwardList> bfList;
+ if (SUCCEEDED(webView->backForwardList(&bfList)))
+ bfList->currentItem(&prevTestBFItem);
+
+ COMPtr<IWebIBActions> webIBActions;
+ if (SUCCEEDED(webView->QueryInterface(IID_IWebIBActions, (void**)&webIBActions)))
+ webIBActions->makeTextStandardSize(0);
+ }
+
+ WorkQueue::shared()->clear();
+ WorkQueue::shared()->setFrozen(false);
+
+ // Set the test timeout timer
+ SetTimer(hostWindow, timeoutId, timeoutValue, 0);
+
+ COMPtr<IWebMutableURLRequest> request;
+ HRESULT hr = CoCreateInstance(CLSID_WebMutableURLRequest, 0, CLSCTX_ALL, IID_IWebMutableURLRequest, (void**)&request);
+ if (FAILED(hr))
+ goto exit;
+
+ request->initWithURL(urlBStr, WebURLRequestUseProtocolCachePolicy, 0);
+
+ request->setHTTPMethod(methodBStr);
+ frame->loadRequest(request.get());
+
+ MSG msg;
+ while (GetMessage(&msg, 0, 0, 0)) {
+ TranslateMessage(&msg);
+ DispatchMessage(&msg);
+ }
+ KillTimer(hostWindow, timeoutId);
+
+ if (timedOut) {
+ fprintf(stderr, "ERROR: Timed out running %s\n", pathOrURL);
+ printf("ERROR: Timed out loading page\n");
+
+ if (printSeparators)
+ puts("#EOF");
+ }
+exit:
+ SysFreeString(urlBStr);
+ return;
+}
+
+static void initializePreferences(IWebPreferences* preferences)
+{
+#ifdef USE_MAC_FONTS
+ BSTR standardFamily = SysAllocString(TEXT("Times"));
+ BSTR fixedFamily = SysAllocString(TEXT("Courier"));
+ BSTR sansSerifFamily = SysAllocString(TEXT("Helvetica"));
+ BSTR cursiveFamily = SysAllocString(TEXT("Apple Chancery"));
+ BSTR fantasyFamily = SysAllocString(TEXT("Papyrus"));
+#else
+ BSTR standardFamily = SysAllocString(TEXT("Times New Roman"));
+ BSTR fixedFamily = SysAllocString(TEXT("Courier New"));
+ BSTR sansSerifFamily = SysAllocString(TEXT("Arial"));
+ BSTR cursiveFamily = SysAllocString(TEXT("Comic Sans MS")); // Not actually cursive, but it's what IE and Firefox use.
+ BSTR fantasyFamily = SysAllocString(TEXT("Times New Roman"));
+#endif
+
+ preferences->setStandardFontFamily(standardFamily);
+ preferences->setFixedFontFamily(fixedFamily);
+ preferences->setSerifFontFamily(standardFamily);
+ preferences->setSansSerifFontFamily(sansSerifFamily);
+ preferences->setCursiveFontFamily(cursiveFamily);
+ preferences->setFantasyFontFamily(fantasyFamily);
+
+ preferences->setAutosaves(FALSE);
+ preferences->setJavaEnabled(FALSE);
+ preferences->setPlugInsEnabled(TRUE);
+ preferences->setDOMPasteAllowed(TRUE);
+ preferences->setEditableLinkBehavior(WebKitEditableLinkOnlyLiveWithShiftKey);
+
+ SysFreeString(standardFamily);
+ SysFreeString(fixedFamily);
+ SysFreeString(sansSerifFamily);
+ SysFreeString(cursiveFamily);
+ SysFreeString(fantasyFamily);
+}
+
+static Boolean pthreadEqualCallback(const void* value1, const void* value2)
+{
+ return (Boolean)pthread_equal(*(pthread_t*)value1, *(pthread_t*)value2);
+}
+
+static CFDictionaryKeyCallBacks pthreadKeyCallbacks = { 0, 0, 0, 0, pthreadEqualCallback, 0 };
+
+static pthread_mutex_t javaScriptThreadsMutex = PTHREAD_MUTEX_INITIALIZER;
+static bool javaScriptThreadsShouldTerminate;
+
+static const int javaScriptThreadsCount = 4;
+static CFMutableDictionaryRef javaScriptThreads()
+{
+ assert(pthread_mutex_trylock(&javaScriptThreadsMutex) == EBUSY);
+ static CFMutableDictionaryRef staticJavaScriptThreads;
+ if (!staticJavaScriptThreads)
+ staticJavaScriptThreads = CFDictionaryCreateMutable(0, 0, &pthreadKeyCallbacks, 0);
+ return staticJavaScriptThreads;
+}
+
+// Loops forever, running a script and randomly respawning, until
+// javaScriptThreadsShouldTerminate becomes true.
+void* runJavaScriptThread(void* arg)
+{
+ const char* const script =
+ " \
+ var array = []; \
+ for (var i = 0; i < 10; i++) { \
+ array.push(String(i)); \
+ } \
+ ";
+
+ while (true) {
+ JSGlobalContextRef ctx = JSGlobalContextCreate(0);
+ JSStringRef scriptRef = JSStringCreateWithUTF8CString(script);
+
+ JSValueRef exception = 0;
+ JSEvaluateScript(ctx, scriptRef, 0, 0, 0, &exception);
+ assert(!exception);
+
+ JSGlobalContextRelease(ctx);
+ JSStringRelease(scriptRef);
+
+ JSGarbageCollect(ctx);
+
+ pthread_mutex_lock(&javaScriptThreadsMutex);
+
+ // Check for cancellation.
+ if (javaScriptThreadsShouldTerminate) {
+ pthread_mutex_unlock(&javaScriptThreadsMutex);
+ return 0;
+ }
+
+ // Respawn probabilistically.
+ if (rand() % 5 == 0) {
+ pthread_t pthread;
+ pthread_create(&pthread, 0, &runJavaScriptThread, 0);
+ pthread_detach(pthread);
+
+ pthread_t self = pthread_self();
+ CFDictionaryRemoveValue(javaScriptThreads(), self.p);
+ CFDictionaryAddValue(javaScriptThreads(), pthread.p, 0);
+
+ pthread_mutex_unlock(&javaScriptThreadsMutex);
+ return 0;
+ }
+
+ pthread_mutex_unlock(&javaScriptThreadsMutex);
+ }
+}
+
+static void startJavaScriptThreads(void)
+{
+ pthread_mutex_lock(&javaScriptThreadsMutex);
+
+ for (int i = 0; i < javaScriptThreadsCount; i++) {
+ pthread_t pthread;
+ pthread_create(&pthread, 0, &runJavaScriptThread, 0);
+ pthread_detach(pthread);
+ CFDictionaryAddValue(javaScriptThreads(), pthread.p, 0);
+ }
+
+ pthread_mutex_unlock(&javaScriptThreadsMutex);
+}
+
+static void stopJavaScriptThreads(void)
+{
+ pthread_mutex_lock(&javaScriptThreadsMutex);
+
+ javaScriptThreadsShouldTerminate = true;
+
+ pthread_t* pthreads[javaScriptThreadsCount] = {0};
+ int threadDictCount = CFDictionaryGetCount(javaScriptThreads());
+ assert(threadDictCount == javaScriptThreadsCount);
+ CFDictionaryGetKeysAndValues(javaScriptThreads(), (const void**)pthreads, 0);
+
+ pthread_mutex_unlock(&javaScriptThreadsMutex);
+
+ for (int i = 0; i < javaScriptThreadsCount; i++) {
+ pthread_t* pthread = pthreads[i];
+ pthread_join(*pthread, 0);
+ free(pthread);
+ }
+}
+
+int main(int argc, char* argv[])
+{
+ leakChecking = false;
+
+ wstring exePath = initialize(GetModuleHandle(0));
+
+ // FIXME: options
+
+ COMPtr<IWebView> webView;
+ HRESULT hr = CoCreateInstance(CLSID_WebView, 0, CLSCTX_ALL, IID_IWebView, (void**)&webView);
+ if (FAILED(hr)) {
+ fprintf(stderr, "Failed to create CLSID_WebView instance, error 0x%x\n", hr);
+ return -1;
+ }
+
+ if (FAILED(webView->setHostWindow((OLE_HANDLE)(ULONG64)hostWindow)))
+ return -1;
+
+ RECT clientRect;
+ clientRect.bottom = clientRect.left = clientRect.top = clientRect.right = 0;
+ BSTR groupName = SysAllocString(L"org.webkit.DumpRenderTree");
+ bool failed = FAILED(webView->initWithFrame(clientRect, 0, groupName));
+ SysFreeString(groupName);
+ if (failed)
+ return -1;
+
+ COMPtr<IWebViewPrivate> viewPrivate;
+ if (FAILED(webView->QueryInterface(&viewPrivate)))
+ return -1;
+ webView->Release();
+
+
+ BSTR pluginPath = SysAllocStringLen(0, exePath.length() + _tcslen(TestPluginDir));
+ _tcscpy(pluginPath, exePath.c_str());
+ _tcscat(pluginPath, TestPluginDir);
+ failed = FAILED(viewPrivate->addAdditionalPluginPath(pluginPath));
+ SysFreeString(pluginPath);
+ if (failed)
+ return -1;
+
+ if (FAILED(viewPrivate->viewWindow((OLE_HANDLE*)&webViewWindow)))
+ return -1;
+
+ SetWindowPos(webViewWindow, 0, 0, 0, maxViewWidth, maxViewHeight, 0);
+ ShowWindow(hostWindow, SW_SHOW);
+
+ COMPtr<FrameLoadDelegate> frameLoadDelegate;
+ frameLoadDelegate.adoptRef(new FrameLoadDelegate);
+ if (FAILED(webView->setFrameLoadDelegate(frameLoadDelegate.get())))
+ return -1;
+
+ COMPtr<UIDelegate> uiDelegate;
+ uiDelegate.adoptRef(new UIDelegate);
+ if (FAILED(webView->setUIDelegate(uiDelegate.get())))
+ return -1;
+
+ COMPtr<IWebViewEditing> viewEditing;
+ if (FAILED(webView->QueryInterface(&viewEditing)))
+ return -1;
+ webView->Release();
+
+ COMPtr<EditingDelegate> editingDelegate;
+ editingDelegate.adoptRef(new EditingDelegate);
+ if (FAILED(viewEditing->setEditingDelegate(editingDelegate.get())))
+ return -1;
+
+ COMPtr<IWebPreferences> preferences;
+ if (FAILED(webView->preferences(&preferences)))
+ return -1;
+
+ initializePreferences(preferences.get());
+
+ COMPtr<IWebIconDatabase> iconDatabase;
+ COMPtr<IWebIconDatabase> tmpIconDatabase;
+ if (FAILED(CoCreateInstance(CLSID_WebIconDatabase, 0, CLSCTX_ALL, IID_IWebIconDatabase, (void**)&tmpIconDatabase)))
+ return -1;
+ if (FAILED(tmpIconDatabase->sharedIconDatabase(&iconDatabase)))
+ return -1;
+
+ if (FAILED(webView->mainFrame(&frame)))
+ return -1;
+
+ _CrtMemState entryToMainMemCheckpoint;
+ if (leakChecking)
+ _CrtMemCheckpoint(&entryToMainMemCheckpoint);
+
+ for (int i = 0; i < argc; ++i)
+ if (!stricmp(argv[i], "--threaded")) {
+ argv[i] = argv[argc - 1];
+ argc--;
+ threaded = true;
+ break;
+ }
+
+ if (threaded)
+ startJavaScriptThreads();
+
+ if (argc == 2 && strcmp(argv[1], "-") == 0) {
+ char filenameBuffer[2048];
+ printSeparators = true;
+ while (fgets(filenameBuffer, sizeof(filenameBuffer), stdin)) {
+ char* newLineCharacter = strchr(filenameBuffer, '\n');
+ if (newLineCharacter)
+ *newLineCharacter = '\0';
+
+ if (strlen(filenameBuffer) == 0)
+ continue;
+
+ runTest(filenameBuffer);
+ fflush(stdout);
+ }
+ } else {
+ printSeparators = argc > 2;
+ for (int i = 1; i != argc; i++)
+ runTest(argv[i]);
+ }
+
+ if (threaded)
+ stopJavaScriptThreads();
+
+ frame->Release();
+
+ if (leakChecking) {
+ // dump leaks to stderr
+ _CrtSetReportMode(_CRT_WARN, _CRTDBG_MODE_FILE);
+ _CrtSetReportFile(_CRT_WARN, _CRTDBG_FILE_STDERR);
+ _CrtMemDumpAllObjectsSince(&entryToMainMemCheckpoint);
+ }
+
+ return 0;
+}