2 * Copyright (C) 2005, 2006, 2007 Apple Inc. All rights reserved.
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
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.
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.
29 #include "DumpRenderTree.h"
31 #include "EditingDelegate.h"
32 #include "FrameLoadDelegate.h"
33 #include "LayoutTestController.h"
34 #include "PixelDumpSupport.h"
35 #include "PolicyDelegate.h"
36 #include "UIDelegate.h"
37 #include "WorkQueueItem.h"
38 #include "WorkQueue.h"
39 #include <wtf/Vector.h>
40 #include <WebCore/COMPtr.h>
41 #include <CoreFoundation/CoreFoundation.h>
42 #include <JavaScriptCore/JavaScriptCore.h>
47 #include <WebKit/DOMPrivate.h>
48 #include <WebKit/IWebFramePrivate.h>
49 #include <WebKit/IWebHistoryItem.h>
50 #include <WebKit/IWebHistoryItemPrivate.h>
51 #include <WebKit/IWebPreferencesPrivate.h>
52 #include <WebKit/IWebURLResponse.h>
53 #include <WebKit/IWebViewPrivate.h>
54 #include <WebKit/WebKit.h>
63 const LPWSTR TestPluginDir = L"TestNetscapePlugin_Debug";
65 const LPWSTR TestPluginDir = L"TestNetscapePlugin";
68 static LPCWSTR fontsEnvironmentVariable = L"WEBKIT_TESTFONTS";
71 const LPCWSTR kDumpRenderTreeClassName = L"DumpRenderTreeWindow";
73 static bool dumpTree = true;
74 static bool dumpPixels;
75 static bool dumpAllPixels;
76 static bool printSeparators;
77 static bool leakChecking = false;
78 static bool timedOut = false;
79 static bool threaded = false;
81 static const char* currentTest;
84 // This is the topmost frame that is loading, during a given load, or nil when no load is
85 // in progress. Usually this is the same as the main frame, but not always. In the case
86 // where a frameset is loaded, and then new content is loaded into one of the child frames,
87 // that child frame is the "topmost frame that is loading".
88 IWebFrame* topLoadingFrame; // !nil iff a load is in progress
89 static COMPtr<IWebHistoryItem> prevTestBFItem; // current b/f item at the end of the previous test
90 IWebPolicyDelegate* policyDelegate;
94 static HWND hostWindow;
96 LayoutTestController* layoutTestController = 0;
97 CFRunLoopTimerRef waitToDumpWatchdog = 0;
99 static const unsigned timeoutValue = 60000;
100 static const unsigned timeoutId = 10;
102 const unsigned maxViewWidth = 800;
103 const unsigned maxViewHeight = 600;
105 static LRESULT CALLBACK DumpRenderTreeWndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
109 // The test ran long enough to time out
115 return DefWindowProc(hWnd, msg, wParam, lParam);
119 static const wstring& exePath()
122 static bool initialized;
128 TCHAR buffer[MAX_PATH];
129 GetModuleFileName(GetModuleHandle(0), buffer, ARRAYSIZE(buffer));
131 int lastSlash = path.rfind('\\');
132 if (lastSlash != -1 && lastSlash + 1 < path.length())
133 path = path.substr(0, lastSlash + 1);
138 static const wstring& fontsPath()
141 static bool initialized;
147 DWORD size = GetEnvironmentVariable(fontsEnvironmentVariable, 0, 0);
148 Vector<TCHAR> buffer(size);
149 if (GetEnvironmentVariable(fontsEnvironmentVariable, buffer.data(), buffer.size())) {
150 path = buffer.data();
151 if (path[path.length() - 1] != '\\')
156 path = exePath() + TEXT("DumpRenderTree.resources\\");
160 #ifdef DEBUG_WEBKIT_HAS_SUFFIX
161 #define WEBKITDLL TEXT("WebKit_debug.dll")
163 #define WEBKITDLL TEXT("WebKit.dll")
166 static void initialize(HMODULE hModule)
168 if (HMODULE webKitModule = LoadLibrary(WEBKITDLL))
169 if (FARPROC dllRegisterServer = GetProcAddress(webKitModule, "DllRegisterServer"))
175 static LPCTSTR fontsToInstall[] = {
176 TEXT("AHEM____.ttf"),
177 TEXT("Apple Chancery.ttf"),
178 TEXT("Courier Bold.ttf"),
180 TEXT("Helvetica Bold.ttf"),
181 TEXT("Helvetica.ttf"),
182 TEXT("Helvetica Neue Bold Italic.ttf"),
183 TEXT("Helvetica Neue Bold.ttf"),
184 TEXT("Helvetica Neue Condensed Black.ttf"),
185 TEXT("Helvetica Neue Condensed Bold.ttf"),
186 TEXT("Helvetica Neue Italic.ttf"),
187 TEXT("Helvetica Neue Light Italic.ttf"),
188 TEXT("Helvetica Neue Light.ttf"),
189 TEXT("Helvetica Neue UltraLight Italic.ttf"),
190 TEXT("Helvetica Neue UltraLight.ttf"),
191 TEXT("Helvetica Neue.ttf"),
192 TEXT("Lucida Grande.ttf"),
193 TEXT("Lucida Grande Bold.ttf"),
196 TEXT("Times Bold Italic.ttf"),
197 TEXT("Times Bold.ttf"),
198 TEXT("Times Italic.ttf"),
199 TEXT("Times Roman.ttf")
202 wstring resourcesPath = fontsPath();
204 COMPtr<IWebTextRenderer> textRenderer;
205 if (SUCCEEDED(CoCreateInstance(CLSID_WebTextRenderer, 0, CLSCTX_ALL, IID_IWebTextRenderer, (void**)&textRenderer)))
206 for (int i = 0; i < ARRAYSIZE(fontsToInstall); ++i)
207 textRenderer->registerPrivateFont(wstring(resourcesPath + fontsToInstall[i]).c_str());
209 // Register a host window
212 wcex.cbSize = sizeof(WNDCLASSEX);
214 wcex.style = CS_HREDRAW | CS_VREDRAW;
215 wcex.lpfnWndProc = DumpRenderTreeWndProc;
218 wcex.hInstance = hModule;
220 wcex.hCursor = LoadCursor(0, IDC_ARROW);
221 wcex.hbrBackground = 0;
222 wcex.lpszMenuName = 0;
223 wcex.lpszClassName = kDumpRenderTreeClassName;
226 RegisterClassEx(&wcex);
228 hostWindow = CreateWindowEx(WS_EX_TOOLWINDOW, kDumpRenderTreeClassName, TEXT("DumpRenderTree"), WS_POPUP,
229 -maxViewWidth, -maxViewHeight, maxViewWidth, maxViewHeight, 0, 0, hModule, 0);
232 void displayWebView()
234 ::InvalidateRect(webViewWindow, 0, TRUE);
235 ::UpdateWindow(webViewWindow);
238 void dumpFrameScrollPosition(IWebFrame* frame)
243 COMPtr<IWebFramePrivate> framePrivate;
244 if (FAILED(frame->QueryInterface(&framePrivate)))
248 if (FAILED(framePrivate->scrollOffset(&scrollPosition)))
251 if (abs(scrollPosition.cx) > 0.00000001 || abs(scrollPosition.cy) > 0.00000001) {
252 COMPtr<IWebFrame> parent;
253 if (FAILED(frame->parentFrame(&parent)))
257 if (FAILED(frame->name(&name)))
259 printf("frame '%S' ", name ? name : L"");
262 printf("scrolled to %.f,%.f\n", (double)scrollPosition.cx, (double)scrollPosition.cy);
265 if (::layoutTestController->dumpChildFrameScrollPositions()) {
266 COMPtr<IEnumVARIANT> enumKids;
267 if (FAILED(frame->childFrames(&enumKids)))
271 while (enumKids->Next(1, &var, 0) == S_OK) {
272 ASSERT(V_VT(&var) == VT_UNKNOWN);
273 COMPtr<IWebFrame> framePtr;
274 V_UNKNOWN(&var)->QueryInterface(IID_IWebFrame, (void**)&framePtr);
275 dumpFrameScrollPosition(framePtr.get());
281 static wstring dumpFramesAsText(IWebFrame* frame)
286 COMPtr<IDOMDocument> document;
287 if (FAILED(frame->DOMDocument(&document)))
290 COMPtr<IDOMElement> documentElement;
291 if (FAILED(document->documentElement(&documentElement)))
296 // Add header for all but the main frame.
297 COMPtr<IWebFrame> parent;
298 if (FAILED(frame->parentFrame(&parent)))
302 if (FAILED(frame->name(&name)))
305 result.append(L"\n--------\nFrame: '");
306 result.append(name ? name : L"");
307 result.append(L"'\n--------\n");
313 COMPtr<IDOMElementPrivate> docPrivate;
314 if (SUCCEEDED(documentElement->QueryInterface(&docPrivate)))
315 docPrivate->innerText(&innerText);
317 result.append(innerText ? innerText : L"");
318 result.append(L"\n");
320 SysFreeString(innerText);
322 if (::layoutTestController->dumpChildFramesAsText()) {
323 COMPtr<IEnumVARIANT> enumKids;
324 if (FAILED(frame->childFrames(&enumKids)))
328 while (enumKids->Next(1, &var, 0) == S_OK) {
329 ASSERT(V_VT(&var) == VT_UNKNOWN);
330 COMPtr<IWebFrame> framePtr;
331 V_UNKNOWN(&var)->QueryInterface(IID_IWebFrame, (void**)&framePtr);
332 result.append(dumpFramesAsText(framePtr.get()));
340 static int compareHistoryItems(const void* item1, const void* item2)
342 COMPtr<IWebHistoryItemPrivate> itemA;
343 if (FAILED((*(COMPtr<IUnknown>*)item1)->QueryInterface(&itemA)))
346 COMPtr<IWebHistoryItemPrivate> itemB;
347 if (FAILED((*(COMPtr<IUnknown>*)item2)->QueryInterface(&itemB)))
351 if (FAILED(itemA->target(&targetA)))
355 if (FAILED(itemB->target(&targetB))) {
356 SysFreeString(targetA);
360 int result = wcsicmp(wstring(targetA, SysStringLen(targetA)).c_str(), wstring(targetB, SysStringLen(targetB)).c_str());
361 SysFreeString(targetA);
362 SysFreeString(targetB);
366 static void dumpHistoryItem(IWebHistoryItem* item, int indent, bool current)
375 for (int i = start; i < indent; i++)
379 if (FAILED(item->URLString(&url)))
381 printf("%S", url ? url : L"");
384 COMPtr<IWebHistoryItemPrivate> itemPrivate;
385 if (FAILED(item->QueryInterface(&itemPrivate)))
389 if (FAILED(itemPrivate->target(&target)))
391 if (SysStringLen(target))
392 printf(" (in frame \"%S\")", target);
393 SysFreeString(target);
394 BOOL isTargetItem = FALSE;
395 if (FAILED(itemPrivate->isTargetItem(&isTargetItem)))
398 printf(" **nav target**");
403 if (FAILED(itemPrivate->children(&kidsCount, &arrPtr)) || !kidsCount)
406 Vector<COMPtr<IUnknown> > kidsVector;
409 if (FAILED(::SafeArrayGetLBound(arrPtr, 1, &lowerBound)))
413 if (FAILED(::SafeArrayGetUBound(arrPtr, 1, &upperBound)))
416 LONG length = upperBound - lowerBound + 1;
419 ASSERT(length == kidsCount);
421 IUnknown** safeArrayData;
422 if (FAILED(::SafeArrayAccessData(arrPtr, (void**)&safeArrayData)))
425 for (int i = 0; i < length; ++i)
426 kidsVector.append(safeArrayData[i]);
427 ::SafeArrayUnaccessData(arrPtr);
429 // must sort to eliminate arbitrary result ordering which defeats reproducible testing
430 qsort(kidsVector.data(), kidsCount, sizeof(kidsVector[0]), compareHistoryItems);
432 for (unsigned i = 0; i < kidsCount; ++i) {
433 COMPtr<IWebHistoryItem> item;
434 kidsVector[i]->QueryInterface(&item);
435 dumpHistoryItem(item.get(), indent + 4, false);
439 if (arrPtr && SUCCEEDED(::SafeArrayUnlock(arrPtr)))
440 ::SafeArrayDestroy(arrPtr);
443 static void dumpBackForwardList(IWebFrame* frame)
447 printf("\n============== Back Forward List ==============\n");
448 COMPtr<IWebView> webView;
449 if (FAILED(frame->webView(&webView)))
452 COMPtr<IWebBackForwardList> bfList;
453 if (FAILED(webView->backForwardList(&bfList)))
456 // Print out all items in the list after prevTestBFItem, which was from the previous test
457 // Gather items from the end of the list, the print them out from oldest to newest
459 Vector<COMPtr<IUnknown> > itemsToPrint;
461 int forwardListCount;
462 if (FAILED(bfList->forwardListCount(&forwardListCount)))
465 for (int i = forwardListCount; i > 0; --i) {
466 COMPtr<IWebHistoryItem> item;
467 if (FAILED(bfList->itemAtIndex(i, &item)))
469 // something is wrong if the item from the last test is in the forward part of the b/f list
470 assert(item != prevTestBFItem);
471 COMPtr<IUnknown> itemUnknown;
472 item->QueryInterface(&itemUnknown);
473 itemsToPrint.append(itemUnknown);
476 COMPtr<IWebHistoryItem> currentItem;
477 if (FAILED(bfList->currentItem(¤tItem)))
480 assert(currentItem != prevTestBFItem);
481 COMPtr<IUnknown> currentItemUnknown;
482 currentItem->QueryInterface(¤tItemUnknown);
483 itemsToPrint.append(currentItemUnknown);
484 int currentItemIndex = itemsToPrint.size() - 1;
487 if (FAILED(bfList->backListCount(&backListCount)))
490 for (int i = -1; i >= -backListCount; --i) {
491 COMPtr<IWebHistoryItem> item;
492 if (FAILED(bfList->itemAtIndex(i, &item)))
494 if (item == prevTestBFItem)
496 COMPtr<IUnknown> itemUnknown;
497 item->QueryInterface(&itemUnknown);
498 itemsToPrint.append(itemUnknown);
501 for (int i = itemsToPrint.size() - 1; i >= 0; --i) {
502 COMPtr<IWebHistoryItem> historyItemToPrint;
503 itemsToPrint[i]->QueryInterface(&historyItemToPrint);
504 dumpHistoryItem(historyItemToPrint.get(), 8, i == currentItemIndex);
507 printf("===============================================\n");
512 COMPtr<IWebDataSource> dataSource;
513 if (SUCCEEDED(frame->dataSource(&dataSource))) {
514 COMPtr<IWebURLResponse> response;
515 if (SUCCEEDED(dataSource->response(&response)) && response) {
517 if (SUCCEEDED(response->MIMEType(&mimeType)))
518 ::layoutTestController->setDumpAsText(::layoutTestController->dumpAsText() | !_tcscmp(mimeType, TEXT("text/plain")));
519 SysFreeString(mimeType);
523 BSTR resultString = 0;
526 if (::layoutTestController->dumpAsText()) {
527 ::InvalidateRect(webViewWindow, 0, TRUE);
528 ::SendMessage(webViewWindow, WM_PAINT, 0, 0);
529 wstring result = dumpFramesAsText(frame);
530 resultString = SysAllocStringLen(result.data(), result.size());
532 bool isSVGW3CTest = strstr(currentTest, "svg\\W3C-SVG-1.1");
539 width = maxViewWidth;
540 height = maxViewHeight;
543 ::SetWindowPos(webViewWindow, 0, 0, 0, width, height, SWP_NOMOVE);
544 ::InvalidateRect(webViewWindow, 0, TRUE);
545 ::SendMessage(webViewWindow, WM_PAINT, 0, 0);
547 COMPtr<IWebFramePrivate> framePrivate;
548 if (FAILED(frame->QueryInterface(&framePrivate)))
550 framePrivate->renderTreeAsExternalRepresentation(&resultString);
554 printf("ERROR: nil result from %s", ::layoutTestController->dumpAsText() ? "IDOMElement::innerText" : "IFrameViewPrivate::renderTreeAsExternalRepresentation");
556 unsigned stringLength = SysStringLen(resultString);
557 int bufferSize = ::WideCharToMultiByte(CP_UTF8, 0, resultString, stringLength, 0, 0, 0, 0);
558 char* buffer = (char*)malloc(bufferSize + 1);
559 int result = ::WideCharToMultiByte(CP_UTF8, 0, resultString, stringLength, buffer, bufferSize + 1, 0, 0);
560 buffer[bufferSize] = '\0';
561 printf("%s", buffer);
563 if (!::layoutTestController->dumpAsText())
564 dumpFrameScrollPosition(frame);
566 if (::layoutTestController->dumpBackForwardList())
567 dumpBackForwardList(frame);
574 if (layoutTestController->dumpAsText() || layoutTestController->dumpDOMAsWebArchive() || layoutTestController->dumpSourceAsWebArchive())
577 dumpWebViewAsPixelsAndCompareWithExpected(currentTest, dumpAllPixels);
581 SysFreeString(resultString);
582 // This will exit from our message loop
587 static bool shouldLogFrameLoadDelegates(const char* pathOrURL)
589 return strstr(pathOrURL, "loading/");
592 static void runTest(const char* pathOrURL)
594 static BSTR methodBStr = SysAllocString(TEXT("GET"));
598 CFStringRef str = CFStringCreateWithCString(0, pathOrURL, kCFStringEncodingWindowsLatin1);
599 CFURLRef url = CFURLCreateWithString(0, str, 0);
602 url = CFURLCreateWithFileSystemPath(0, str, kCFURLWindowsPathStyle, false);
606 str = CFURLGetString(url);
608 CFIndex length = CFStringGetLength(str);
609 UniChar* buffer = new UniChar[length];
611 CFStringGetCharacters(str, CFRangeMake(0, length), buffer);
612 urlBStr = SysAllocStringLen((OLECHAR*)buffer, length);
617 currentTest = pathOrURL;
619 ::layoutTestController = new LayoutTestController(false, false);
624 if (shouldLogFrameLoadDelegates(pathOrURL))
625 layoutTestController->setDumpFrameLoadCallbacks(true);
627 COMPtr<IWebHistory> history(Create, CLSID_WebHistory);
629 history->setOptionalSharedHistory(0);
632 COMPtr<IWebView> webView;
633 if (SUCCEEDED(frame->webView(&webView))) {
634 COMPtr<IWebBackForwardList> bfList;
635 if (SUCCEEDED(webView->backForwardList(&bfList)))
636 bfList->currentItem(&prevTestBFItem);
638 webView->setPolicyDelegate(NULL);
640 COMPtr<IWebIBActions> webIBActions;
641 if (SUCCEEDED(webView->QueryInterface(IID_IWebIBActions, (void**)&webIBActions)))
642 webIBActions->makeTextStandardSize(0);
644 COMPtr<IWebPreferences> preferences;
645 if (SUCCEEDED(webView->preferences(&preferences))) {
646 preferences->setPrivateBrowsingEnabled(FALSE);
647 COMPtr<IWebPreferencesPrivate> prefsPrivate(Query, preferences);
649 prefsPrivate->setAuthorAndUserStylesEnabled(TRUE);
653 WorkQueue::shared()->clear();
654 WorkQueue::shared()->setFrozen(false);
656 // Set the test timeout timer
657 SetTimer(hostWindow, timeoutId, timeoutValue, 0);
659 COMPtr<IWebMutableURLRequest> request;
660 HRESULT hr = CoCreateInstance(CLSID_WebMutableURLRequest, 0, CLSCTX_ALL, IID_IWebMutableURLRequest, (void**)&request);
664 request->initWithURL(urlBStr, WebURLRequestUseProtocolCachePolicy, 0);
666 request->setHTTPMethod(methodBStr);
667 frame->loadRequest(request.get());
670 while (GetMessage(&msg, 0, 0, 0)) {
671 TranslateMessage(&msg);
672 DispatchMessage(&msg);
674 KillTimer(hostWindow, timeoutId);
677 fprintf(stderr, "ERROR: Timed out running %s\n", pathOrURL);
678 printf("ERROR: Timed out loading page\n");
684 frame->stopLoading();
687 SysFreeString(urlBStr);
688 delete ::layoutTestController;
693 static void initializePreferences(IWebPreferences* preferences)
696 BSTR standardFamily = SysAllocString(TEXT("Times"));
697 BSTR fixedFamily = SysAllocString(TEXT("Courier"));
698 BSTR sansSerifFamily = SysAllocString(TEXT("Helvetica"));
699 BSTR cursiveFamily = SysAllocString(TEXT("Apple Chancery"));
700 BSTR fantasyFamily = SysAllocString(TEXT("Papyrus"));
702 BSTR standardFamily = SysAllocString(TEXT("Times New Roman"));
703 BSTR fixedFamily = SysAllocString(TEXT("Courier New"));
704 BSTR sansSerifFamily = SysAllocString(TEXT("Arial"));
705 BSTR cursiveFamily = SysAllocString(TEXT("Comic Sans MS")); // Not actually cursive, but it's what IE and Firefox use.
706 BSTR fantasyFamily = SysAllocString(TEXT("Times New Roman"));
709 preferences->setStandardFontFamily(standardFamily);
710 preferences->setFixedFontFamily(fixedFamily);
711 preferences->setSerifFontFamily(standardFamily);
712 preferences->setSansSerifFontFamily(sansSerifFamily);
713 preferences->setCursiveFontFamily(cursiveFamily);
714 preferences->setFantasyFontFamily(fantasyFamily);
716 preferences->setAutosaves(FALSE);
717 preferences->setJavaEnabled(FALSE);
718 preferences->setPlugInsEnabled(TRUE);
719 preferences->setDOMPasteAllowed(TRUE);
720 preferences->setEditableLinkBehavior(WebKitEditableLinkOnlyLiveWithShiftKey);
721 preferences->setFontSmoothing(FontSmoothingTypeStandard);
723 SysFreeString(standardFamily);
724 SysFreeString(fixedFamily);
725 SysFreeString(sansSerifFamily);
726 SysFreeString(cursiveFamily);
727 SysFreeString(fantasyFamily);
730 static Boolean pthreadEqualCallback(const void* value1, const void* value2)
732 return (Boolean)pthread_equal(*(pthread_t*)value1, *(pthread_t*)value2);
735 static CFDictionaryKeyCallBacks pthreadKeyCallbacks = { 0, 0, 0, 0, pthreadEqualCallback, 0 };
737 static pthread_mutex_t javaScriptThreadsMutex = PTHREAD_MUTEX_INITIALIZER;
738 static bool javaScriptThreadsShouldTerminate;
740 static const int javaScriptThreadsCount = 4;
741 static CFMutableDictionaryRef javaScriptThreads()
743 assert(pthread_mutex_trylock(&javaScriptThreadsMutex) == EBUSY);
744 static CFMutableDictionaryRef staticJavaScriptThreads;
745 if (!staticJavaScriptThreads)
746 staticJavaScriptThreads = CFDictionaryCreateMutable(0, 0, &pthreadKeyCallbacks, 0);
747 return staticJavaScriptThreads;
750 // Loops forever, running a script and randomly respawning, until
751 // javaScriptThreadsShouldTerminate becomes true.
752 void* runJavaScriptThread(void* arg)
754 const char* const script =
757 for (var i = 0; i < 10; i++) { \
758 array.push(String(i)); \
763 JSGlobalContextRef ctx = JSGlobalContextCreate(0);
764 JSStringRef scriptRef = JSStringCreateWithUTF8CString(script);
766 JSValueRef exception = 0;
767 JSEvaluateScript(ctx, scriptRef, 0, 0, 0, &exception);
770 JSGlobalContextRelease(ctx);
771 JSStringRelease(scriptRef);
773 JSGarbageCollect(ctx);
775 pthread_mutex_lock(&javaScriptThreadsMutex);
777 // Check for cancellation.
778 if (javaScriptThreadsShouldTerminate) {
779 pthread_mutex_unlock(&javaScriptThreadsMutex);
783 // Respawn probabilistically.
784 if (rand() % 5 == 0) {
786 pthread_create(&pthread, 0, &runJavaScriptThread, 0);
787 pthread_detach(pthread);
789 pthread_t self = pthread_self();
790 CFDictionaryRemoveValue(javaScriptThreads(), self.p);
791 CFDictionaryAddValue(javaScriptThreads(), pthread.p, 0);
793 pthread_mutex_unlock(&javaScriptThreadsMutex);
797 pthread_mutex_unlock(&javaScriptThreadsMutex);
801 static void startJavaScriptThreads(void)
803 pthread_mutex_lock(&javaScriptThreadsMutex);
805 for (int i = 0; i < javaScriptThreadsCount; i++) {
807 pthread_create(&pthread, 0, &runJavaScriptThread, 0);
808 pthread_detach(pthread);
809 CFDictionaryAddValue(javaScriptThreads(), pthread.p, 0);
812 pthread_mutex_unlock(&javaScriptThreadsMutex);
815 static void stopJavaScriptThreads(void)
817 pthread_mutex_lock(&javaScriptThreadsMutex);
819 javaScriptThreadsShouldTerminate = true;
821 pthread_t* pthreads[javaScriptThreadsCount] = {0};
822 int threadDictCount = CFDictionaryGetCount(javaScriptThreads());
823 assert(threadDictCount == javaScriptThreadsCount);
824 CFDictionaryGetKeysAndValues(javaScriptThreads(), (const void**)pthreads, 0);
826 pthread_mutex_unlock(&javaScriptThreadsMutex);
828 for (int i = 0; i < javaScriptThreadsCount; i++) {
829 pthread_t* pthread = pthreads[i];
830 pthread_join(*pthread, 0);
835 int main(int argc, char* argv[])
837 leakChecking = false;
839 _setmode(1, _O_BINARY);
841 initialize(GetModuleHandle(0));
843 Vector<const char*> tests;
845 for (int i = 1; i < argc; ++i) {
846 if (!stricmp(argv[i], "--threaded")) {
851 if (!stricmp(argv[i], "--dump-all-pixels")) {
852 dumpAllPixels = true;
856 if (!stricmp(argv[i], "--pixel-tests")) {
861 tests.append(argv[i]);
864 COMPtr<IWebView> webView;
865 HRESULT hr = CoCreateInstance(CLSID_WebView, 0, CLSCTX_ALL, IID_IWebView, (void**)&webView);
867 fprintf(stderr, "Failed to create CLSID_WebView instance, error 0x%x\n", hr);
871 if (FAILED(webView->setHostWindow((OLE_HANDLE)(ULONG64)hostWindow)))
875 clientRect.bottom = clientRect.left = clientRect.top = clientRect.right = 0;
876 BSTR groupName = SysAllocString(L"org.webkit.DumpRenderTree");
877 bool failed = FAILED(webView->initWithFrame(clientRect, 0, groupName));
878 SysFreeString(groupName);
882 COMPtr<IWebViewPrivate> viewPrivate;
883 if (FAILED(webView->QueryInterface(&viewPrivate)))
887 viewPrivate->setShouldApplyMacFontAscentHack(TRUE);
889 BSTR pluginPath = SysAllocStringLen(0, exePath().length() + _tcslen(TestPluginDir));
890 _tcscpy(pluginPath, exePath().c_str());
891 _tcscat(pluginPath, TestPluginDir);
892 failed = FAILED(viewPrivate->addAdditionalPluginPath(pluginPath));
893 SysFreeString(pluginPath);
897 if (FAILED(viewPrivate->viewWindow((OLE_HANDLE*)&webViewWindow)))
900 SetWindowPos(webViewWindow, 0, 0, 0, maxViewWidth, maxViewHeight, 0);
901 ShowWindow(hostWindow, SW_SHOW);
903 COMPtr<FrameLoadDelegate> frameLoadDelegate;
904 frameLoadDelegate.adoptRef(new FrameLoadDelegate);
905 if (FAILED(webView->setFrameLoadDelegate(frameLoadDelegate.get())))
907 if (FAILED(viewPrivate->setFrameLoadDelegatePrivate(frameLoadDelegate.get())))
910 policyDelegate = new PolicyDelegate();
912 COMPtr<UIDelegate> uiDelegate;
913 uiDelegate.adoptRef(new UIDelegate);
914 if (FAILED(webView->setUIDelegate(uiDelegate.get())))
917 COMPtr<IWebViewEditing> viewEditing;
918 if (FAILED(webView->QueryInterface(&viewEditing)))
922 COMPtr<EditingDelegate> editingDelegate;
923 editingDelegate.adoptRef(new EditingDelegate);
924 if (FAILED(viewEditing->setEditingDelegate(editingDelegate.get())))
927 COMPtr<IWebPreferences> preferences;
928 if (FAILED(webView->preferences(&preferences)))
931 initializePreferences(preferences.get());
933 COMPtr<IWebIconDatabase> iconDatabase;
934 COMPtr<IWebIconDatabase> tmpIconDatabase;
935 if (FAILED(CoCreateInstance(CLSID_WebIconDatabase, 0, CLSCTX_ALL, IID_IWebIconDatabase, (void**)&tmpIconDatabase)))
937 if (FAILED(tmpIconDatabase->sharedIconDatabase(&iconDatabase)))
940 if (FAILED(webView->mainFrame(&frame)))
944 _CrtMemState entryToMainMemCheckpoint;
946 _CrtMemCheckpoint(&entryToMainMemCheckpoint);
950 startJavaScriptThreads();
952 if (tests.size() == 1 && !strcmp(tests[0], "-")) {
953 char filenameBuffer[2048];
954 printSeparators = true;
955 while (fgets(filenameBuffer, sizeof(filenameBuffer), stdin)) {
956 char* newLineCharacter = strchr(filenameBuffer, '\n');
957 if (newLineCharacter)
958 *newLineCharacter = '\0';
960 if (strlen(filenameBuffer) == 0)
963 runTest(filenameBuffer);
967 printSeparators = tests.size() > 1;
968 for (int i = 0; i < tests.size(); i++)
973 stopJavaScriptThreads();
975 delete policyDelegate;
980 // dump leaks to stderr
981 _CrtSetReportMode(_CRT_WARN, _CRTDBG_MODE_FILE);
982 _CrtSetReportFile(_CRT_WARN, _CRTDBG_FILE_STDERR);
983 _CrtMemDumpAllObjectsSince(&entryToMainMemCheckpoint);