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 "ResourceLoadDelegate.h"
37 #include "UIDelegate.h"
38 #include "WorkQueueItem.h"
39 #include "WorkQueue.h"
40 #include <wtf/Vector.h>
41 #include <WebCore/COMPtr.h>
42 #include <CoreFoundation/CoreFoundation.h>
43 #include <JavaScriptCore/JavaScriptCore.h>
48 #include <WebKit/DOMPrivate.h>
49 #include <WebKit/IWebFramePrivate.h>
50 #include <WebKit/IWebHistoryItem.h>
51 #include <WebKit/IWebHistoryItemPrivate.h>
52 #include <WebKit/IWebPreferencesPrivate.h>
53 #include <WebKit/IWebURLResponse.h>
54 #include <WebKit/IWebViewPrivate.h>
55 #include <WebKit/WebKit.h>
65 const LPWSTR TestPluginDir = L"TestNetscapePlugin_Debug";
67 const LPWSTR TestPluginDir = L"TestNetscapePlugin";
70 static LPCWSTR fontsEnvironmentVariable = L"WEBKIT_TESTFONTS";
73 const LPCWSTR kDumpRenderTreeClassName = L"DumpRenderTreeWindow";
75 static bool dumpTree = true;
76 static bool dumpPixels;
77 static bool dumpAllPixels;
78 static bool printSeparators;
79 static bool leakChecking = false;
80 static bool timedOut = false;
81 static bool threaded = false;
83 static const char* currentTest;
86 // This is the topmost frame that is loading, during a given load, or nil when no load is
87 // in progress. Usually this is the same as the main frame, but not always. In the case
88 // where a frameset is loaded, and then new content is loaded into one of the child frames,
89 // that child frame is the "topmost frame that is loading".
90 IWebFrame* topLoadingFrame; // !nil iff a load is in progress
91 static COMPtr<IWebHistoryItem> prevTestBFItem; // current b/f item at the end of the previous test
92 IWebPolicyDelegate* policyDelegate;
96 static HWND hostWindow;
98 LayoutTestController* layoutTestController = 0;
99 CFRunLoopTimerRef waitToDumpWatchdog = 0;
101 static const unsigned timeoutValue = 60000;
102 static const unsigned timeoutId = 10;
104 const unsigned maxViewWidth = 800;
105 const unsigned maxViewHeight = 600;
107 wstring urlSuitableForTestResult(const wstring& url)
109 if (!url.c_str() || !url.find(L"file://"))
112 return PathFindFileNameW(url.c_str());
115 static LRESULT CALLBACK DumpRenderTreeWndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
119 // The test ran long enough to time out
125 return DefWindowProc(hWnd, msg, wParam, lParam);
129 static const wstring& exePath()
132 static bool initialized;
138 TCHAR buffer[MAX_PATH];
139 GetModuleFileName(GetModuleHandle(0), buffer, ARRAYSIZE(buffer));
141 int lastSlash = path.rfind('\\');
142 if (lastSlash != -1 && lastSlash + 1 < path.length())
143 path = path.substr(0, lastSlash + 1);
148 static const wstring& fontsPath()
151 static bool initialized;
157 DWORD size = GetEnvironmentVariable(fontsEnvironmentVariable, 0, 0);
158 Vector<TCHAR> buffer(size);
159 if (GetEnvironmentVariable(fontsEnvironmentVariable, buffer.data(), buffer.size())) {
160 path = buffer.data();
161 if (path[path.length() - 1] != '\\')
166 path = exePath() + TEXT("DumpRenderTree.resources\\");
170 #ifdef DEBUG_WEBKIT_HAS_SUFFIX
171 #define WEBKITDLL TEXT("WebKit_debug.dll")
173 #define WEBKITDLL TEXT("WebKit.dll")
176 static void initialize(HMODULE hModule)
178 if (HMODULE webKitModule = LoadLibrary(WEBKITDLL))
179 if (FARPROC dllRegisterServer = GetProcAddress(webKitModule, "DllRegisterServer"))
185 static LPCTSTR fontsToInstall[] = {
186 TEXT("AHEM____.ttf"),
187 TEXT("Apple Chancery.ttf"),
188 TEXT("Courier Bold.ttf"),
190 TEXT("Helvetica Bold Oblique.ttf"),
191 TEXT("Helvetica Bold.ttf"),
192 TEXT("Helvetica Oblique.ttf"),
193 TEXT("Helvetica.ttf"),
194 TEXT("Helvetica Neue Bold Italic.ttf"),
195 TEXT("Helvetica Neue Bold.ttf"),
196 TEXT("Helvetica Neue Condensed Black.ttf"),
197 TEXT("Helvetica Neue Condensed Bold.ttf"),
198 TEXT("Helvetica Neue Italic.ttf"),
199 TEXT("Helvetica Neue Light Italic.ttf"),
200 TEXT("Helvetica Neue Light.ttf"),
201 TEXT("Helvetica Neue UltraLight Italic.ttf"),
202 TEXT("Helvetica Neue UltraLight.ttf"),
203 TEXT("Helvetica Neue.ttf"),
204 TEXT("Lucida Grande.ttf"),
205 TEXT("Lucida Grande Bold.ttf"),
208 TEXT("Times Bold Italic.ttf"),
209 TEXT("Times Bold.ttf"),
210 TEXT("Times Italic.ttf"),
211 TEXT("Times Roman.ttf")
214 wstring resourcesPath = fontsPath();
216 COMPtr<IWebTextRenderer> textRenderer;
217 if (SUCCEEDED(CoCreateInstance(CLSID_WebTextRenderer, 0, CLSCTX_ALL, IID_IWebTextRenderer, (void**)&textRenderer)))
218 for (int i = 0; i < ARRAYSIZE(fontsToInstall); ++i)
219 textRenderer->registerPrivateFont(wstring(resourcesPath + fontsToInstall[i]).c_str());
221 // Register a host window
224 wcex.cbSize = sizeof(WNDCLASSEX);
226 wcex.style = CS_HREDRAW | CS_VREDRAW;
227 wcex.lpfnWndProc = DumpRenderTreeWndProc;
230 wcex.hInstance = hModule;
232 wcex.hCursor = LoadCursor(0, IDC_ARROW);
233 wcex.hbrBackground = 0;
234 wcex.lpszMenuName = 0;
235 wcex.lpszClassName = kDumpRenderTreeClassName;
238 RegisterClassEx(&wcex);
240 hostWindow = CreateWindowEx(WS_EX_TOOLWINDOW, kDumpRenderTreeClassName, TEXT("DumpRenderTree"), WS_POPUP,
241 -maxViewWidth, -maxViewHeight, maxViewWidth, maxViewHeight, 0, 0, hModule, 0);
244 void displayWebView()
246 ::InvalidateRect(webViewWindow, 0, TRUE);
247 ::UpdateWindow(webViewWindow);
250 void dumpFrameScrollPosition(IWebFrame* frame)
255 COMPtr<IWebFramePrivate> framePrivate;
256 if (FAILED(frame->QueryInterface(&framePrivate)))
260 if (FAILED(framePrivate->scrollOffset(&scrollPosition)))
263 if (abs(scrollPosition.cx) > 0.00000001 || abs(scrollPosition.cy) > 0.00000001) {
264 COMPtr<IWebFrame> parent;
265 if (FAILED(frame->parentFrame(&parent)))
269 if (FAILED(frame->name(&name)))
271 printf("frame '%S' ", name ? name : L"");
274 printf("scrolled to %.f,%.f\n", (double)scrollPosition.cx, (double)scrollPosition.cy);
277 if (::layoutTestController->dumpChildFrameScrollPositions()) {
278 COMPtr<IEnumVARIANT> enumKids;
279 if (FAILED(frame->childFrames(&enumKids)))
283 while (enumKids->Next(1, &var, 0) == S_OK) {
284 ASSERT(V_VT(&var) == VT_UNKNOWN);
285 COMPtr<IWebFrame> framePtr;
286 V_UNKNOWN(&var)->QueryInterface(IID_IWebFrame, (void**)&framePtr);
287 dumpFrameScrollPosition(framePtr.get());
293 static wstring dumpFramesAsText(IWebFrame* frame)
298 COMPtr<IDOMDocument> document;
299 if (FAILED(frame->DOMDocument(&document)))
302 COMPtr<IDOMElement> documentElement;
303 if (FAILED(document->documentElement(&documentElement)))
308 // Add header for all but the main frame.
309 COMPtr<IWebFrame> parent;
310 if (FAILED(frame->parentFrame(&parent)))
314 if (FAILED(frame->name(&name)))
317 result.append(L"\n--------\nFrame: '");
318 result.append(name ? name : L"");
319 result.append(L"'\n--------\n");
325 COMPtr<IDOMElementPrivate> docPrivate;
326 if (SUCCEEDED(documentElement->QueryInterface(&docPrivate)))
327 docPrivate->innerText(&innerText);
329 result.append(innerText ? innerText : L"");
330 result.append(L"\n");
332 SysFreeString(innerText);
334 if (::layoutTestController->dumpChildFramesAsText()) {
335 COMPtr<IEnumVARIANT> enumKids;
336 if (FAILED(frame->childFrames(&enumKids)))
340 while (enumKids->Next(1, &var, 0) == S_OK) {
341 ASSERT(V_VT(&var) == VT_UNKNOWN);
342 COMPtr<IWebFrame> framePtr;
343 V_UNKNOWN(&var)->QueryInterface(IID_IWebFrame, (void**)&framePtr);
344 result.append(dumpFramesAsText(framePtr.get()));
352 static int compareHistoryItems(const void* item1, const void* item2)
354 COMPtr<IWebHistoryItemPrivate> itemA;
355 if (FAILED((*(COMPtr<IUnknown>*)item1)->QueryInterface(&itemA)))
358 COMPtr<IWebHistoryItemPrivate> itemB;
359 if (FAILED((*(COMPtr<IUnknown>*)item2)->QueryInterface(&itemB)))
363 if (FAILED(itemA->target(&targetA)))
367 if (FAILED(itemB->target(&targetB))) {
368 SysFreeString(targetA);
372 int result = wcsicmp(wstring(targetA, SysStringLen(targetA)).c_str(), wstring(targetB, SysStringLen(targetB)).c_str());
373 SysFreeString(targetA);
374 SysFreeString(targetB);
378 static void dumpHistoryItem(IWebHistoryItem* item, int indent, bool current)
387 for (int i = start; i < indent; i++)
391 if (FAILED(item->URLString(&url)))
393 printf("%S", url ? url : L"");
396 COMPtr<IWebHistoryItemPrivate> itemPrivate;
397 if (FAILED(item->QueryInterface(&itemPrivate)))
401 if (FAILED(itemPrivate->target(&target)))
403 if (SysStringLen(target))
404 printf(" (in frame \"%S\")", target);
405 SysFreeString(target);
406 BOOL isTargetItem = FALSE;
407 if (FAILED(itemPrivate->isTargetItem(&isTargetItem)))
410 printf(" **nav target**");
415 if (FAILED(itemPrivate->children(&kidsCount, &arrPtr)) || !kidsCount)
418 Vector<COMPtr<IUnknown> > kidsVector;
421 if (FAILED(::SafeArrayGetLBound(arrPtr, 1, &lowerBound)))
425 if (FAILED(::SafeArrayGetUBound(arrPtr, 1, &upperBound)))
428 LONG length = upperBound - lowerBound + 1;
431 ASSERT(length == kidsCount);
433 IUnknown** safeArrayData;
434 if (FAILED(::SafeArrayAccessData(arrPtr, (void**)&safeArrayData)))
437 for (int i = 0; i < length; ++i)
438 kidsVector.append(safeArrayData[i]);
439 ::SafeArrayUnaccessData(arrPtr);
441 // must sort to eliminate arbitrary result ordering which defeats reproducible testing
442 qsort(kidsVector.data(), kidsCount, sizeof(kidsVector[0]), compareHistoryItems);
444 for (unsigned i = 0; i < kidsCount; ++i) {
445 COMPtr<IWebHistoryItem> item;
446 kidsVector[i]->QueryInterface(&item);
447 dumpHistoryItem(item.get(), indent + 4, false);
451 if (arrPtr && SUCCEEDED(::SafeArrayUnlock(arrPtr)))
452 ::SafeArrayDestroy(arrPtr);
455 static void dumpBackForwardList(IWebFrame* frame)
459 printf("\n============== Back Forward List ==============\n");
460 COMPtr<IWebView> webView;
461 if (FAILED(frame->webView(&webView)))
464 COMPtr<IWebBackForwardList> bfList;
465 if (FAILED(webView->backForwardList(&bfList)))
468 // Print out all items in the list after prevTestBFItem, which was from the previous test
469 // Gather items from the end of the list, the print them out from oldest to newest
471 Vector<COMPtr<IUnknown> > itemsToPrint;
473 int forwardListCount;
474 if (FAILED(bfList->forwardListCount(&forwardListCount)))
477 for (int i = forwardListCount; i > 0; --i) {
478 COMPtr<IWebHistoryItem> item;
479 if (FAILED(bfList->itemAtIndex(i, &item)))
481 // something is wrong if the item from the last test is in the forward part of the b/f list
482 assert(item != prevTestBFItem);
483 COMPtr<IUnknown> itemUnknown;
484 item->QueryInterface(&itemUnknown);
485 itemsToPrint.append(itemUnknown);
488 COMPtr<IWebHistoryItem> currentItem;
489 if (FAILED(bfList->currentItem(¤tItem)))
492 assert(currentItem != prevTestBFItem);
493 COMPtr<IUnknown> currentItemUnknown;
494 currentItem->QueryInterface(¤tItemUnknown);
495 itemsToPrint.append(currentItemUnknown);
496 int currentItemIndex = itemsToPrint.size() - 1;
499 if (FAILED(bfList->backListCount(&backListCount)))
502 for (int i = -1; i >= -backListCount; --i) {
503 COMPtr<IWebHistoryItem> item;
504 if (FAILED(bfList->itemAtIndex(i, &item)))
506 if (item == prevTestBFItem)
508 COMPtr<IUnknown> itemUnknown;
509 item->QueryInterface(&itemUnknown);
510 itemsToPrint.append(itemUnknown);
513 for (int i = itemsToPrint.size() - 1; i >= 0; --i) {
514 COMPtr<IWebHistoryItem> historyItemToPrint;
515 itemsToPrint[i]->QueryInterface(&historyItemToPrint);
516 dumpHistoryItem(historyItemToPrint.get(), 8, i == currentItemIndex);
519 printf("===============================================\n");
524 COMPtr<IWebDataSource> dataSource;
525 if (SUCCEEDED(frame->dataSource(&dataSource))) {
526 COMPtr<IWebURLResponse> response;
527 if (SUCCEEDED(dataSource->response(&response)) && response) {
529 if (SUCCEEDED(response->MIMEType(&mimeType)))
530 ::layoutTestController->setDumpAsText(::layoutTestController->dumpAsText() | !_tcscmp(mimeType, TEXT("text/plain")));
531 SysFreeString(mimeType);
535 BSTR resultString = 0;
538 if (::layoutTestController->dumpAsText()) {
539 ::InvalidateRect(webViewWindow, 0, TRUE);
540 ::SendMessage(webViewWindow, WM_PAINT, 0, 0);
541 wstring result = dumpFramesAsText(frame);
542 resultString = SysAllocStringLen(result.data(), result.size());
544 bool isSVGW3CTest = strstr(currentTest, "svg\\W3C-SVG-1.1");
551 width = maxViewWidth;
552 height = maxViewHeight;
555 ::SetWindowPos(webViewWindow, 0, 0, 0, width, height, SWP_NOMOVE);
556 ::InvalidateRect(webViewWindow, 0, TRUE);
557 ::SendMessage(webViewWindow, WM_PAINT, 0, 0);
559 COMPtr<IWebFramePrivate> framePrivate;
560 if (FAILED(frame->QueryInterface(&framePrivate)))
562 framePrivate->renderTreeAsExternalRepresentation(&resultString);
566 printf("ERROR: nil result from %s", ::layoutTestController->dumpAsText() ? "IDOMElement::innerText" : "IFrameViewPrivate::renderTreeAsExternalRepresentation");
568 unsigned stringLength = SysStringLen(resultString);
569 int bufferSize = ::WideCharToMultiByte(CP_UTF8, 0, resultString, stringLength, 0, 0, 0, 0);
570 char* buffer = (char*)malloc(bufferSize + 1);
571 int result = ::WideCharToMultiByte(CP_UTF8, 0, resultString, stringLength, buffer, bufferSize + 1, 0, 0);
572 buffer[bufferSize] = '\0';
573 printf("%s", buffer);
575 if (!::layoutTestController->dumpAsText())
576 dumpFrameScrollPosition(frame);
578 if (::layoutTestController->dumpBackForwardList())
579 dumpBackForwardList(frame);
586 if (layoutTestController->dumpAsText() || layoutTestController->dumpDOMAsWebArchive() || layoutTestController->dumpSourceAsWebArchive())
589 dumpWebViewAsPixelsAndCompareWithExpected(currentTest, dumpAllPixels);
593 SysFreeString(resultString);
594 // This will exit from our message loop
599 static bool shouldLogFrameLoadDelegates(const char* pathOrURL)
601 return strstr(pathOrURL, "loading/");
604 static void runTest(const char* pathOrURL)
606 static BSTR methodBStr = SysAllocString(TEXT("GET"));
610 CFStringRef str = CFStringCreateWithCString(0, pathOrURL, kCFStringEncodingWindowsLatin1);
611 CFURLRef url = CFURLCreateWithString(0, str, 0);
614 url = CFURLCreateWithFileSystemPath(0, str, kCFURLWindowsPathStyle, false);
618 str = CFURLGetString(url);
620 CFIndex length = CFStringGetLength(str);
621 UniChar* buffer = new UniChar[length];
623 CFStringGetCharacters(str, CFRangeMake(0, length), buffer);
624 urlBStr = SysAllocStringLen((OLECHAR*)buffer, length);
629 currentTest = pathOrURL;
631 ::layoutTestController = new LayoutTestController(false, false);
636 if (shouldLogFrameLoadDelegates(pathOrURL))
637 layoutTestController->setDumpFrameLoadCallbacks(true);
639 COMPtr<IWebHistory> history(Create, CLSID_WebHistory);
641 history->setOptionalSharedHistory(0);
644 COMPtr<IWebView> webView;
645 if (SUCCEEDED(frame->webView(&webView))) {
646 COMPtr<IWebBackForwardList> bfList;
647 if (SUCCEEDED(webView->backForwardList(&bfList)))
648 bfList->currentItem(&prevTestBFItem);
650 webView->setPolicyDelegate(NULL);
652 COMPtr<IWebIBActions> webIBActions;
653 if (SUCCEEDED(webView->QueryInterface(IID_IWebIBActions, (void**)&webIBActions)))
654 webIBActions->makeTextStandardSize(0);
656 COMPtr<IWebPreferences> preferences;
657 if (SUCCEEDED(webView->preferences(&preferences))) {
658 preferences->setPrivateBrowsingEnabled(FALSE);
659 COMPtr<IWebPreferencesPrivate> prefsPrivate(Query, preferences);
661 prefsPrivate->setAuthorAndUserStylesEnabled(TRUE);
665 WorkQueue::shared()->clear();
666 WorkQueue::shared()->setFrozen(false);
668 // Set the test timeout timer
669 SetTimer(hostWindow, timeoutId, timeoutValue, 0);
671 COMPtr<IWebMutableURLRequest> request;
672 HRESULT hr = CoCreateInstance(CLSID_WebMutableURLRequest, 0, CLSCTX_ALL, IID_IWebMutableURLRequest, (void**)&request);
676 request->initWithURL(urlBStr, WebURLRequestUseProtocolCachePolicy, 0);
678 request->setHTTPMethod(methodBStr);
679 frame->loadRequest(request.get());
682 while (GetMessage(&msg, 0, 0, 0)) {
683 TranslateMessage(&msg);
684 DispatchMessage(&msg);
686 KillTimer(hostWindow, timeoutId);
689 fprintf(stderr, "ERROR: Timed out running %s\n", pathOrURL);
690 printf("ERROR: Timed out loading page\n");
696 frame->stopLoading();
699 SysFreeString(urlBStr);
700 delete ::layoutTestController;
705 static void initializePreferences(IWebPreferences* preferences)
708 BSTR standardFamily = SysAllocString(TEXT("Times"));
709 BSTR fixedFamily = SysAllocString(TEXT("Courier"));
710 BSTR sansSerifFamily = SysAllocString(TEXT("Helvetica"));
711 BSTR cursiveFamily = SysAllocString(TEXT("Apple Chancery"));
712 BSTR fantasyFamily = SysAllocString(TEXT("Papyrus"));
714 BSTR standardFamily = SysAllocString(TEXT("Times New Roman"));
715 BSTR fixedFamily = SysAllocString(TEXT("Courier New"));
716 BSTR sansSerifFamily = SysAllocString(TEXT("Arial"));
717 BSTR cursiveFamily = SysAllocString(TEXT("Comic Sans MS")); // Not actually cursive, but it's what IE and Firefox use.
718 BSTR fantasyFamily = SysAllocString(TEXT("Times New Roman"));
721 preferences->setStandardFontFamily(standardFamily);
722 preferences->setFixedFontFamily(fixedFamily);
723 preferences->setSerifFontFamily(standardFamily);
724 preferences->setSansSerifFontFamily(sansSerifFamily);
725 preferences->setCursiveFontFamily(cursiveFamily);
726 preferences->setFantasyFontFamily(fantasyFamily);
728 preferences->setAutosaves(FALSE);
729 preferences->setJavaEnabled(FALSE);
730 preferences->setPlugInsEnabled(TRUE);
731 preferences->setDOMPasteAllowed(TRUE);
732 preferences->setEditableLinkBehavior(WebKitEditableLinkOnlyLiveWithShiftKey);
733 preferences->setFontSmoothing(FontSmoothingTypeStandard);
735 SysFreeString(standardFamily);
736 SysFreeString(fixedFamily);
737 SysFreeString(sansSerifFamily);
738 SysFreeString(cursiveFamily);
739 SysFreeString(fantasyFamily);
742 static Boolean pthreadEqualCallback(const void* value1, const void* value2)
744 return (Boolean)pthread_equal(*(pthread_t*)value1, *(pthread_t*)value2);
747 static CFDictionaryKeyCallBacks pthreadKeyCallbacks = { 0, 0, 0, 0, pthreadEqualCallback, 0 };
749 static pthread_mutex_t javaScriptThreadsMutex = PTHREAD_MUTEX_INITIALIZER;
750 static bool javaScriptThreadsShouldTerminate;
752 static const int javaScriptThreadsCount = 4;
753 static CFMutableDictionaryRef javaScriptThreads()
755 assert(pthread_mutex_trylock(&javaScriptThreadsMutex) == EBUSY);
756 static CFMutableDictionaryRef staticJavaScriptThreads;
757 if (!staticJavaScriptThreads)
758 staticJavaScriptThreads = CFDictionaryCreateMutable(0, 0, &pthreadKeyCallbacks, 0);
759 return staticJavaScriptThreads;
762 // Loops forever, running a script and randomly respawning, until
763 // javaScriptThreadsShouldTerminate becomes true.
764 void* runJavaScriptThread(void* arg)
766 const char* const script =
769 for (var i = 0; i < 10; i++) { \
770 array.push(String(i)); \
775 JSGlobalContextRef ctx = JSGlobalContextCreate(0);
776 JSStringRef scriptRef = JSStringCreateWithUTF8CString(script);
778 JSValueRef exception = 0;
779 JSEvaluateScript(ctx, scriptRef, 0, 0, 0, &exception);
782 JSGlobalContextRelease(ctx);
783 JSStringRelease(scriptRef);
785 JSGarbageCollect(ctx);
787 pthread_mutex_lock(&javaScriptThreadsMutex);
789 // Check for cancellation.
790 if (javaScriptThreadsShouldTerminate) {
791 pthread_mutex_unlock(&javaScriptThreadsMutex);
795 // Respawn probabilistically.
796 if (rand() % 5 == 0) {
798 pthread_create(&pthread, 0, &runJavaScriptThread, 0);
799 pthread_detach(pthread);
801 pthread_t self = pthread_self();
802 CFDictionaryRemoveValue(javaScriptThreads(), self.p);
803 CFDictionaryAddValue(javaScriptThreads(), pthread.p, 0);
805 pthread_mutex_unlock(&javaScriptThreadsMutex);
809 pthread_mutex_unlock(&javaScriptThreadsMutex);
813 static void startJavaScriptThreads(void)
815 pthread_mutex_lock(&javaScriptThreadsMutex);
817 for (int i = 0; i < javaScriptThreadsCount; i++) {
819 pthread_create(&pthread, 0, &runJavaScriptThread, 0);
820 pthread_detach(pthread);
821 CFDictionaryAddValue(javaScriptThreads(), pthread.p, 0);
824 pthread_mutex_unlock(&javaScriptThreadsMutex);
827 static void stopJavaScriptThreads(void)
829 pthread_mutex_lock(&javaScriptThreadsMutex);
831 javaScriptThreadsShouldTerminate = true;
833 pthread_t* pthreads[javaScriptThreadsCount] = {0};
834 int threadDictCount = CFDictionaryGetCount(javaScriptThreads());
835 assert(threadDictCount == javaScriptThreadsCount);
836 CFDictionaryGetKeysAndValues(javaScriptThreads(), (const void**)pthreads, 0);
838 pthread_mutex_unlock(&javaScriptThreadsMutex);
840 for (int i = 0; i < javaScriptThreadsCount; i++) {
841 pthread_t* pthread = pthreads[i];
842 pthread_join(*pthread, 0);
847 int main(int argc, char* argv[])
849 leakChecking = false;
851 _setmode(1, _O_BINARY);
853 initialize(GetModuleHandle(0));
855 Vector<const char*> tests;
857 for (int i = 1; i < argc; ++i) {
858 if (!stricmp(argv[i], "--threaded")) {
863 if (!stricmp(argv[i], "--dump-all-pixels")) {
864 dumpAllPixels = true;
868 if (!stricmp(argv[i], "--pixel-tests")) {
873 tests.append(argv[i]);
876 COMPtr<IWebView> webView;
877 HRESULT hr = CoCreateInstance(CLSID_WebView, 0, CLSCTX_ALL, IID_IWebView, (void**)&webView);
879 fprintf(stderr, "Failed to create CLSID_WebView instance, error 0x%x\n", hr);
883 if (FAILED(webView->setHostWindow((OLE_HANDLE)(ULONG64)hostWindow)))
887 clientRect.bottom = clientRect.left = clientRect.top = clientRect.right = 0;
888 BSTR groupName = SysAllocString(L"org.webkit.DumpRenderTree");
889 bool failed = FAILED(webView->initWithFrame(clientRect, 0, groupName));
890 SysFreeString(groupName);
894 COMPtr<IWebViewPrivate> viewPrivate;
895 if (FAILED(webView->QueryInterface(&viewPrivate)))
899 viewPrivate->setShouldApplyMacFontAscentHack(TRUE);
901 BSTR pluginPath = SysAllocStringLen(0, exePath().length() + _tcslen(TestPluginDir));
902 _tcscpy(pluginPath, exePath().c_str());
903 _tcscat(pluginPath, TestPluginDir);
904 failed = FAILED(viewPrivate->addAdditionalPluginPath(pluginPath));
905 SysFreeString(pluginPath);
909 if (FAILED(viewPrivate->viewWindow((OLE_HANDLE*)&webViewWindow)))
912 SetWindowPos(webViewWindow, 0, 0, 0, maxViewWidth, maxViewHeight, 0);
913 ShowWindow(hostWindow, SW_SHOW);
915 COMPtr<FrameLoadDelegate> frameLoadDelegate;
916 frameLoadDelegate.adoptRef(new FrameLoadDelegate);
917 if (FAILED(webView->setFrameLoadDelegate(frameLoadDelegate.get())))
919 if (FAILED(viewPrivate->setFrameLoadDelegatePrivate(frameLoadDelegate.get())))
922 policyDelegate = new PolicyDelegate();
924 COMPtr<UIDelegate> uiDelegate;
925 uiDelegate.adoptRef(new UIDelegate);
926 if (FAILED(webView->setUIDelegate(uiDelegate.get())))
929 COMPtr<IWebViewEditing> viewEditing;
930 if (FAILED(webView->QueryInterface(&viewEditing)))
934 COMPtr<EditingDelegate> editingDelegate;
935 editingDelegate.adoptRef(new EditingDelegate);
936 if (FAILED(viewEditing->setEditingDelegate(editingDelegate.get())))
939 COMPtr<ResourceLoadDelegate> resourceLoadDelegate(AdoptCOM, new ResourceLoadDelegate);
940 if (FAILED(webView->setResourceLoadDelegate(resourceLoadDelegate.get())))
943 COMPtr<IWebPreferences> preferences;
944 if (FAILED(webView->preferences(&preferences)))
947 initializePreferences(preferences.get());
949 COMPtr<IWebIconDatabase> iconDatabase;
950 COMPtr<IWebIconDatabase> tmpIconDatabase;
951 if (FAILED(CoCreateInstance(CLSID_WebIconDatabase, 0, CLSCTX_ALL, IID_IWebIconDatabase, (void**)&tmpIconDatabase)))
953 if (FAILED(tmpIconDatabase->sharedIconDatabase(&iconDatabase)))
956 if (FAILED(webView->mainFrame(&frame)))
960 _CrtMemState entryToMainMemCheckpoint;
962 _CrtMemCheckpoint(&entryToMainMemCheckpoint);
966 startJavaScriptThreads();
968 if (tests.size() == 1 && !strcmp(tests[0], "-")) {
969 char filenameBuffer[2048];
970 printSeparators = true;
971 while (fgets(filenameBuffer, sizeof(filenameBuffer), stdin)) {
972 char* newLineCharacter = strchr(filenameBuffer, '\n');
973 if (newLineCharacter)
974 *newLineCharacter = '\0';
976 if (strlen(filenameBuffer) == 0)
979 runTest(filenameBuffer);
983 printSeparators = tests.size() > 1;
984 for (int i = 0; i < tests.size(); i++)
989 stopJavaScriptThreads();
991 delete policyDelegate;
996 // dump leaks to stderr
997 _CrtSetReportMode(_CRT_WARN, _CRTDBG_MODE_FILE);
998 _CrtSetReportFile(_CRT_WARN, _CRTDBG_FILE_STDERR);
999 _CrtMemDumpAllObjectsSince(&entryToMainMemCheckpoint);