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 "FrameLoaderDelegate.h"
33 #include "LayoutTestController.h"
34 #include "PolicyDelegate.h"
35 #include "UIDelegate.h"
36 #include "WorkQueueItem.h"
37 #include "WorkQueue.h"
38 #include <wtf/Vector.h>
39 #include <WebCore/COMPtr.h>
40 #include <CoreFoundation/CoreFoundation.h>
41 #include <JavaScriptCore/JavaScriptCore.h>
46 #include <WebKit/DOMPrivate.h>
47 #include <WebKit/IWebFramePrivate.h>
48 #include <WebKit/IWebHistoryItem.h>
49 #include <WebKit/IWebHistoryItemPrivate.h>
50 #include <WebKit/IWebURLResponse.h>
51 #include <WebKit/IWebViewPrivate.h>
52 #include <WebKit/WebKit.h>
59 const LPWSTR TestPluginDir = L"TestNetscapePlugin_Debug";
61 const LPWSTR TestPluginDir = L"TestNetscapePlugin";
64 static LPCWSTR fontsEnvironmentVariable = L"WEBKIT_TESTFONTS";
67 const LPCWSTR kDumpRenderTreeClassName = L"DumpRenderTreeWindow";
69 static bool dumpTree = true;
70 static bool printSeparators;
71 static bool leakChecking = false;
72 static bool timedOut = false;
73 static bool threaded = false;
75 static const char* currentTest;
78 // This is the topmost frame that is loading, during a given load, or nil when no load is
79 // in progress. Usually this is the same as the main frame, but not always. In the case
80 // where a frameset is loaded, and then new content is loaded into one of the child frames,
81 // that child frame is the "topmost frame that is loading".
82 IWebFrame* topLoadingFrame; // !nil iff a load is in progress
83 static COMPtr<IWebHistoryItem> prevTestBFItem; // current b/f item at the end of the previous test
84 IWebPolicyDelegate* policyDelegate;
88 static HWND hostWindow;
90 LayoutTestController* layoutTestController = 0;
91 CFRunLoopTimerRef waitToDumpWatchdog = 0;
93 static const unsigned timeoutValue = 60000;
94 static const unsigned timeoutId = 10;
96 const unsigned maxViewWidth = 800;
97 const unsigned maxViewHeight = 600;
99 static LRESULT CALLBACK DumpRenderTreeWndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
103 // The test ran long enough to time out
109 return DefWindowProc(hWnd, msg, wParam, lParam);
113 static const wstring& exePath()
116 static bool initialized;
122 TCHAR buffer[MAX_PATH];
123 GetModuleFileName(GetModuleHandle(0), buffer, ARRAYSIZE(buffer));
125 int lastSlash = path.rfind('\\');
126 if (lastSlash != -1 && lastSlash + 1 < path.length())
127 path = path.substr(0, lastSlash + 1);
132 static const wstring& fontsPath()
135 static bool initialized;
141 DWORD size = GetEnvironmentVariable(fontsEnvironmentVariable, 0, 0);
142 Vector<TCHAR> buffer(size);
143 if (GetEnvironmentVariable(fontsEnvironmentVariable, buffer.data(), buffer.size())) {
144 path = buffer.data();
145 if (path[path.length() - 1] != '\\')
150 path = exePath() + TEXT("DumpRenderTree.resources\\");
154 #ifdef DEBUG_WEBKIT_HAS_SUFFIX
155 #define WEBKITDLL TEXT("WebKit_debug.dll")
157 #define WEBKITDLL TEXT("WebKit.dll")
160 static void initialize(HMODULE hModule)
162 if (HMODULE webKitModule = LoadLibrary(WEBKITDLL))
163 if (FARPROC dllRegisterServer = GetProcAddress(webKitModule, "DllRegisterServer"))
169 static LPCTSTR fontsToInstall[] = {
170 TEXT("AHEM____.ttf"),
171 TEXT("Apple Chancery.ttf"),
172 TEXT("Courier Bold.ttf"),
174 TEXT("Helvetica Bold.ttf"),
175 TEXT("Helvetica.ttf"),
176 TEXT("Helvetica Neue Bold Italic.ttf"),
177 TEXT("Helvetica Neue Bold.ttf"),
178 TEXT("Helvetica Neue Condensed Black.ttf"),
179 TEXT("Helvetica Neue Condensed Bold.ttf"),
180 TEXT("Helvetica Neue Italic.ttf"),
181 TEXT("Helvetica Neue Light Italic.ttf"),
182 TEXT("Helvetica Neue Light.ttf"),
183 TEXT("Helvetica Neue UltraLight Italic.ttf"),
184 TEXT("Helvetica Neue UltraLight.ttf"),
185 TEXT("Helvetica Neue.ttf"),
186 TEXT("Lucida Grande.ttf"),
187 TEXT("Lucida Grande Bold.ttf"),
190 TEXT("Times Bold Italic.ttf"),
191 TEXT("Times Bold.ttf"),
192 TEXT("Times Italic.ttf"),
193 TEXT("Times Roman.ttf")
196 wstring resourcesPath = fontsPath();
198 COMPtr<IWebTextRenderer> textRenderer;
199 if (SUCCEEDED(CoCreateInstance(CLSID_WebTextRenderer, 0, CLSCTX_ALL, IID_IWebTextRenderer, (void**)&textRenderer)))
200 for (int i = 0; i < ARRAYSIZE(fontsToInstall); ++i)
201 textRenderer->registerPrivateFont(wstring(resourcesPath + fontsToInstall[i]).c_str());
203 // Register a host window
206 wcex.cbSize = sizeof(WNDCLASSEX);
208 wcex.style = CS_HREDRAW | CS_VREDRAW;
209 wcex.lpfnWndProc = DumpRenderTreeWndProc;
212 wcex.hInstance = hModule;
214 wcex.hCursor = LoadCursor(0, IDC_ARROW);
215 wcex.hbrBackground = 0;
216 wcex.lpszMenuName = 0;
217 wcex.lpszClassName = kDumpRenderTreeClassName;
220 RegisterClassEx(&wcex);
222 hostWindow = CreateWindowEx(WS_EX_TOOLWINDOW, kDumpRenderTreeClassName, TEXT("DumpRenderTree"), WS_POPUP,
223 -maxViewWidth, -maxViewHeight, maxViewWidth, maxViewHeight, 0, 0, hModule, 0);
226 void displayWebView()
228 ::InvalidateRect(webViewWindow, 0, TRUE);
229 ::UpdateWindow(webViewWindow);
232 void dumpFrameScrollPosition(IWebFrame* frame)
237 COMPtr<IWebFramePrivate> framePrivate;
238 if (FAILED(frame->QueryInterface(&framePrivate)))
242 if (FAILED(framePrivate->scrollOffset(&scrollPosition)))
245 if (abs(scrollPosition.cx) > 0.00000001 || abs(scrollPosition.cy) > 0.00000001) {
246 COMPtr<IWebFrame> parent;
247 if (FAILED(frame->parentFrame(&parent)))
251 if (FAILED(frame->name(&name)))
253 printf("frame '%S' ", name ? name : L"");
256 printf("scrolled to %.f,%.f\n", (double)scrollPosition.cx, (double)scrollPosition.cy);
259 if (::layoutTestController->dumpChildFrameScrollPositions()) {
260 COMPtr<IEnumVARIANT> enumKids;
261 if (FAILED(frame->childFrames(&enumKids)))
265 while (enumKids->Next(1, &var, 0) == S_OK) {
266 ASSERT(V_VT(&var) == VT_UNKNOWN);
267 COMPtr<IWebFrame> framePtr;
268 V_UNKNOWN(&var)->QueryInterface(IID_IWebFrame, (void**)&framePtr);
269 dumpFrameScrollPosition(framePtr.get());
275 static wstring dumpFramesAsText(IWebFrame* frame)
280 COMPtr<IDOMDocument> document;
281 if (FAILED(frame->DOMDocument(&document)))
284 COMPtr<IDOMElement> documentElement;
285 if (FAILED(document->documentElement(&documentElement)))
290 // Add header for all but the main frame.
291 COMPtr<IWebFrame> parent;
292 if (FAILED(frame->parentFrame(&parent)))
296 if (FAILED(frame->name(&name)))
299 result.append(L"\n--------\nFrame: '");
300 result.append(name ? name : L"");
301 result.append(L"'\n--------\n");
307 COMPtr<IDOMElementPrivate> docPrivate;
308 if (SUCCEEDED(documentElement->QueryInterface(&docPrivate)))
309 docPrivate->innerText(&innerText);
311 result.append(innerText ? innerText : L"");
312 result.append(L"\n");
314 SysFreeString(innerText);
316 if (::layoutTestController->dumpChildFramesAsText()) {
317 COMPtr<IEnumVARIANT> enumKids;
318 if (FAILED(frame->childFrames(&enumKids)))
322 while (enumKids->Next(1, &var, 0) == S_OK) {
323 ASSERT(V_VT(&var) == VT_UNKNOWN);
324 COMPtr<IWebFrame> framePtr;
325 V_UNKNOWN(&var)->QueryInterface(IID_IWebFrame, (void**)&framePtr);
326 result.append(dumpFramesAsText(framePtr.get()));
334 static int compareHistoryItems(const void* item1, const void* item2)
336 COMPtr<IWebHistoryItemPrivate> itemA;
337 if (FAILED((*(COMPtr<IUnknown>*)item1)->QueryInterface(&itemA)))
340 COMPtr<IWebHistoryItemPrivate> itemB;
341 if (FAILED((*(COMPtr<IUnknown>*)item2)->QueryInterface(&itemB)))
345 if (FAILED(itemA->target(&targetA)))
349 if (FAILED(itemB->target(&targetB))) {
350 SysFreeString(targetA);
354 int result = wcsicmp(wstring(targetA, SysStringLen(targetA)).c_str(), wstring(targetB, SysStringLen(targetB)).c_str());
355 SysFreeString(targetA);
356 SysFreeString(targetB);
360 static void dumpHistoryItem(IWebHistoryItem* item, int indent, bool current)
369 for (int i = start; i < indent; i++)
373 if (FAILED(item->URLString(&url)))
375 printf("%S", url ? url : L"");
378 COMPtr<IWebHistoryItemPrivate> itemPrivate;
379 if (FAILED(item->QueryInterface(&itemPrivate)))
383 if (FAILED(itemPrivate->target(&target)))
385 if (SysStringLen(target))
386 printf(" (in frame \"%S\")", target);
387 SysFreeString(target);
388 BOOL isTargetItem = FALSE;
389 if (FAILED(itemPrivate->isTargetItem(&isTargetItem)))
392 printf(" **nav target**");
397 if (FAILED(itemPrivate->children(&kidsCount, &arrPtr)) || !kidsCount)
400 Vector<COMPtr<IUnknown> > kidsVector;
403 if (FAILED(::SafeArrayGetLBound(arrPtr, 1, &lowerBound)))
407 if (FAILED(::SafeArrayGetUBound(arrPtr, 1, &upperBound)))
410 LONG length = upperBound - lowerBound + 1;
413 ASSERT(length == kidsCount);
415 IUnknown** safeArrayData;
416 if (FAILED(::SafeArrayAccessData(arrPtr, (void**)&safeArrayData)))
419 for (int i = 0; i < length; ++i)
420 kidsVector.append(safeArrayData[i]);
421 ::SafeArrayUnaccessData(arrPtr);
423 // must sort to eliminate arbitrary result ordering which defeats reproducible testing
424 qsort(kidsVector.data(), kidsCount, sizeof(kidsVector[0]), compareHistoryItems);
426 for (unsigned i = 0; i < kidsCount; ++i) {
427 COMPtr<IWebHistoryItem> item;
428 kidsVector[i]->QueryInterface(&item);
429 dumpHistoryItem(item.get(), indent + 4, false);
433 if (arrPtr && SUCCEEDED(::SafeArrayUnlock(arrPtr)))
434 ::SafeArrayDestroy(arrPtr);
437 static void dumpBackForwardList(IWebFrame* frame)
441 printf("\n============== Back Forward List ==============\n");
442 COMPtr<IWebView> webView;
443 if (FAILED(frame->webView(&webView)))
446 COMPtr<IWebBackForwardList> bfList;
447 if (FAILED(webView->backForwardList(&bfList)))
450 // Print out all items in the list after prevTestBFItem, which was from the previous test
451 // Gather items from the end of the list, the print them out from oldest to newest
453 Vector<COMPtr<IUnknown> > itemsToPrint;
455 int forwardListCount;
456 if (FAILED(bfList->forwardListCount(&forwardListCount)))
459 for (int i = forwardListCount; i > 0; --i) {
460 COMPtr<IWebHistoryItem> item;
461 if (FAILED(bfList->itemAtIndex(i, &item)))
463 // something is wrong if the item from the last test is in the forward part of the b/f list
464 assert(item != prevTestBFItem);
465 COMPtr<IUnknown> itemUnknown;
466 item->QueryInterface(&itemUnknown);
467 itemsToPrint.append(itemUnknown);
470 COMPtr<IWebHistoryItem> currentItem;
471 if (FAILED(bfList->currentItem(¤tItem)))
474 assert(currentItem != prevTestBFItem);
475 COMPtr<IUnknown> currentItemUnknown;
476 currentItem->QueryInterface(¤tItemUnknown);
477 itemsToPrint.append(currentItemUnknown);
478 int currentItemIndex = itemsToPrint.size() - 1;
481 if (FAILED(bfList->backListCount(&backListCount)))
484 for (int i = -1; i >= -backListCount; --i) {
485 COMPtr<IWebHistoryItem> item;
486 if (FAILED(bfList->itemAtIndex(i, &item)))
488 if (item == prevTestBFItem)
490 COMPtr<IUnknown> itemUnknown;
491 item->QueryInterface(&itemUnknown);
492 itemsToPrint.append(itemUnknown);
495 for (int i = itemsToPrint.size() - 1; i >= 0; --i) {
496 COMPtr<IWebHistoryItem> historyItemToPrint;
497 itemsToPrint[i]->QueryInterface(&historyItemToPrint);
498 dumpHistoryItem(historyItemToPrint.get(), 8, i == currentItemIndex);
501 printf("===============================================\n");
506 COMPtr<IWebDataSource> dataSource;
507 if (SUCCEEDED(frame->dataSource(&dataSource))) {
508 COMPtr<IWebURLResponse> response;
509 if (SUCCEEDED(dataSource->response(&response)) && response) {
511 if (SUCCEEDED(response->MIMEType(&mimeType)))
512 ::layoutTestController->setDumpAsText(::layoutTestController->dumpAsText() | !_tcscmp(mimeType, TEXT("text/plain")));
513 SysFreeString(mimeType);
517 BSTR resultString = 0;
520 if (::layoutTestController->dumpAsText()) {
521 ::InvalidateRect(webViewWindow, 0, TRUE);
522 ::SendMessage(webViewWindow, WM_PAINT, 0, 0);
523 wstring result = dumpFramesAsText(frame);
524 resultString = SysAllocStringLen(result.data(), result.size());
526 bool isSVGW3CTest = strstr(currentTest, "svg\\W3C-SVG-1.1");
533 width = maxViewWidth;
534 height = maxViewHeight;
537 ::SetWindowPos(webViewWindow, 0, 0, 0, width, height, SWP_NOMOVE);
538 ::InvalidateRect(webViewWindow, 0, TRUE);
539 ::SendMessage(webViewWindow, WM_PAINT, 0, 0);
541 COMPtr<IWebFramePrivate> framePrivate;
542 if (FAILED(frame->QueryInterface(&framePrivate)))
544 framePrivate->renderTreeAsExternalRepresentation(&resultString);
548 printf("ERROR: nil result from %s", ::layoutTestController->dumpAsText() ? "IDOMElement::innerText" : "IFrameViewPrivate::renderTreeAsExternalRepresentation");
550 unsigned stringLength = SysStringLen(resultString);
551 int bufferSize = ::WideCharToMultiByte(CP_UTF8, 0, resultString, stringLength, 0, 0, 0, 0);
552 char* buffer = (char*)malloc(bufferSize + 1);
553 int result = ::WideCharToMultiByte(CP_UTF8, 0, resultString, stringLength, buffer, bufferSize + 1, 0, 0);
554 buffer[bufferSize] = '\0';
555 printf("%s", buffer);
557 if (!::layoutTestController->dumpAsText())
558 dumpFrameScrollPosition(frame);
560 if (::layoutTestController->dumpBackForwardList())
561 dumpBackForwardList(frame);
567 SysFreeString(resultString);
568 // This will exit from our message loop
573 static void runTest(const char* pathOrURL)
575 static BSTR methodBStr = SysAllocString(TEXT("GET"));
579 CFStringRef str = CFStringCreateWithCString(0, pathOrURL, kCFStringEncodingWindowsLatin1);
580 CFURLRef url = CFURLCreateWithString(0, str, 0);
583 url = CFURLCreateWithFileSystemPath(0, str, kCFURLWindowsPathStyle, false);
587 str = CFURLGetString(url);
589 CFIndex length = CFStringGetLength(str);
590 UniChar* buffer = new UniChar[length];
592 CFStringGetCharacters(str, CFRangeMake(0, length), buffer);
593 urlBStr = SysAllocStringLen((OLECHAR*)buffer, length);
598 currentTest = pathOrURL;
600 ::layoutTestController = new LayoutTestController(false, false);
606 COMPtr<IWebView> webView;
607 if (SUCCEEDED(frame->webView(&webView))) {
608 COMPtr<IWebBackForwardList> bfList;
609 if (SUCCEEDED(webView->backForwardList(&bfList)))
610 bfList->currentItem(&prevTestBFItem);
612 webView->setPolicyDelegate(NULL);
614 COMPtr<IWebIBActions> webIBActions;
615 if (SUCCEEDED(webView->QueryInterface(IID_IWebIBActions, (void**)&webIBActions)))
616 webIBActions->makeTextStandardSize(0);
619 WorkQueue::shared()->clear();
620 WorkQueue::shared()->setFrozen(false);
622 // Set the test timeout timer
623 SetTimer(hostWindow, timeoutId, timeoutValue, 0);
625 COMPtr<IWebMutableURLRequest> request;
626 HRESULT hr = CoCreateInstance(CLSID_WebMutableURLRequest, 0, CLSCTX_ALL, IID_IWebMutableURLRequest, (void**)&request);
630 request->initWithURL(urlBStr, WebURLRequestUseProtocolCachePolicy, 0);
632 request->setHTTPMethod(methodBStr);
633 frame->loadRequest(request.get());
636 while (GetMessage(&msg, 0, 0, 0)) {
637 TranslateMessage(&msg);
638 DispatchMessage(&msg);
640 KillTimer(hostWindow, timeoutId);
643 fprintf(stderr, "ERROR: Timed out running %s\n", pathOrURL);
644 printf("ERROR: Timed out loading page\n");
650 SysFreeString(urlBStr);
651 delete ::layoutTestController;
656 static void initializePreferences(IWebPreferences* preferences)
659 BSTR standardFamily = SysAllocString(TEXT("Times"));
660 BSTR fixedFamily = SysAllocString(TEXT("Courier"));
661 BSTR sansSerifFamily = SysAllocString(TEXT("Helvetica"));
662 BSTR cursiveFamily = SysAllocString(TEXT("Apple Chancery"));
663 BSTR fantasyFamily = SysAllocString(TEXT("Papyrus"));
665 BSTR standardFamily = SysAllocString(TEXT("Times New Roman"));
666 BSTR fixedFamily = SysAllocString(TEXT("Courier New"));
667 BSTR sansSerifFamily = SysAllocString(TEXT("Arial"));
668 BSTR cursiveFamily = SysAllocString(TEXT("Comic Sans MS")); // Not actually cursive, but it's what IE and Firefox use.
669 BSTR fantasyFamily = SysAllocString(TEXT("Times New Roman"));
672 preferences->setStandardFontFamily(standardFamily);
673 preferences->setFixedFontFamily(fixedFamily);
674 preferences->setSerifFontFamily(standardFamily);
675 preferences->setSansSerifFontFamily(sansSerifFamily);
676 preferences->setCursiveFontFamily(cursiveFamily);
677 preferences->setFantasyFontFamily(fantasyFamily);
679 preferences->setAutosaves(FALSE);
680 preferences->setJavaEnabled(FALSE);
681 preferences->setPlugInsEnabled(TRUE);
682 preferences->setDOMPasteAllowed(TRUE);
683 preferences->setEditableLinkBehavior(WebKitEditableLinkOnlyLiveWithShiftKey);
685 SysFreeString(standardFamily);
686 SysFreeString(fixedFamily);
687 SysFreeString(sansSerifFamily);
688 SysFreeString(cursiveFamily);
689 SysFreeString(fantasyFamily);
692 static Boolean pthreadEqualCallback(const void* value1, const void* value2)
694 return (Boolean)pthread_equal(*(pthread_t*)value1, *(pthread_t*)value2);
697 static CFDictionaryKeyCallBacks pthreadKeyCallbacks = { 0, 0, 0, 0, pthreadEqualCallback, 0 };
699 static pthread_mutex_t javaScriptThreadsMutex = PTHREAD_MUTEX_INITIALIZER;
700 static bool javaScriptThreadsShouldTerminate;
702 static const int javaScriptThreadsCount = 4;
703 static CFMutableDictionaryRef javaScriptThreads()
705 assert(pthread_mutex_trylock(&javaScriptThreadsMutex) == EBUSY);
706 static CFMutableDictionaryRef staticJavaScriptThreads;
707 if (!staticJavaScriptThreads)
708 staticJavaScriptThreads = CFDictionaryCreateMutable(0, 0, &pthreadKeyCallbacks, 0);
709 return staticJavaScriptThreads;
712 // Loops forever, running a script and randomly respawning, until
713 // javaScriptThreadsShouldTerminate becomes true.
714 void* runJavaScriptThread(void* arg)
716 const char* const script =
719 for (var i = 0; i < 10; i++) { \
720 array.push(String(i)); \
725 JSGlobalContextRef ctx = JSGlobalContextCreate(0);
726 JSStringRef scriptRef = JSStringCreateWithUTF8CString(script);
728 JSValueRef exception = 0;
729 JSEvaluateScript(ctx, scriptRef, 0, 0, 0, &exception);
732 JSGlobalContextRelease(ctx);
733 JSStringRelease(scriptRef);
735 JSGarbageCollect(ctx);
737 pthread_mutex_lock(&javaScriptThreadsMutex);
739 // Check for cancellation.
740 if (javaScriptThreadsShouldTerminate) {
741 pthread_mutex_unlock(&javaScriptThreadsMutex);
745 // Respawn probabilistically.
746 if (rand() % 5 == 0) {
748 pthread_create(&pthread, 0, &runJavaScriptThread, 0);
749 pthread_detach(pthread);
751 pthread_t self = pthread_self();
752 CFDictionaryRemoveValue(javaScriptThreads(), self.p);
753 CFDictionaryAddValue(javaScriptThreads(), pthread.p, 0);
755 pthread_mutex_unlock(&javaScriptThreadsMutex);
759 pthread_mutex_unlock(&javaScriptThreadsMutex);
763 static void startJavaScriptThreads(void)
765 pthread_mutex_lock(&javaScriptThreadsMutex);
767 for (int i = 0; i < javaScriptThreadsCount; i++) {
769 pthread_create(&pthread, 0, &runJavaScriptThread, 0);
770 pthread_detach(pthread);
771 CFDictionaryAddValue(javaScriptThreads(), pthread.p, 0);
774 pthread_mutex_unlock(&javaScriptThreadsMutex);
777 static void stopJavaScriptThreads(void)
779 pthread_mutex_lock(&javaScriptThreadsMutex);
781 javaScriptThreadsShouldTerminate = true;
783 pthread_t* pthreads[javaScriptThreadsCount] = {0};
784 int threadDictCount = CFDictionaryGetCount(javaScriptThreads());
785 assert(threadDictCount == javaScriptThreadsCount);
786 CFDictionaryGetKeysAndValues(javaScriptThreads(), (const void**)pthreads, 0);
788 pthread_mutex_unlock(&javaScriptThreadsMutex);
790 for (int i = 0; i < javaScriptThreadsCount; i++) {
791 pthread_t* pthread = pthreads[i];
792 pthread_join(*pthread, 0);
797 int main(int argc, char* argv[])
799 leakChecking = false;
801 initialize(GetModuleHandle(0));
805 COMPtr<IWebView> webView;
806 HRESULT hr = CoCreateInstance(CLSID_WebView, 0, CLSCTX_ALL, IID_IWebView, (void**)&webView);
808 fprintf(stderr, "Failed to create CLSID_WebView instance, error 0x%x\n", hr);
812 if (FAILED(webView->setHostWindow((OLE_HANDLE)(ULONG64)hostWindow)))
816 clientRect.bottom = clientRect.left = clientRect.top = clientRect.right = 0;
817 BSTR groupName = SysAllocString(L"org.webkit.DumpRenderTree");
818 bool failed = FAILED(webView->initWithFrame(clientRect, 0, groupName));
819 SysFreeString(groupName);
823 COMPtr<IWebViewPrivate> viewPrivate;
824 if (FAILED(webView->QueryInterface(&viewPrivate)))
829 BSTR pluginPath = SysAllocStringLen(0, exePath().length() + _tcslen(TestPluginDir));
830 _tcscpy(pluginPath, exePath().c_str());
831 _tcscat(pluginPath, TestPluginDir);
832 failed = FAILED(viewPrivate->addAdditionalPluginPath(pluginPath));
833 SysFreeString(pluginPath);
837 if (FAILED(viewPrivate->viewWindow((OLE_HANDLE*)&webViewWindow)))
840 SetWindowPos(webViewWindow, 0, 0, 0, maxViewWidth, maxViewHeight, 0);
841 ShowWindow(hostWindow, SW_SHOW);
843 COMPtr<FrameLoadDelegate> frameLoadDelegate;
844 frameLoadDelegate.adoptRef(new FrameLoadDelegate);
845 if (FAILED(webView->setFrameLoadDelegate(frameLoadDelegate.get())))
848 policyDelegate = new PolicyDelegate();
850 COMPtr<UIDelegate> uiDelegate;
851 uiDelegate.adoptRef(new UIDelegate);
852 if (FAILED(webView->setUIDelegate(uiDelegate.get())))
855 COMPtr<IWebViewEditing> viewEditing;
856 if (FAILED(webView->QueryInterface(&viewEditing)))
860 COMPtr<EditingDelegate> editingDelegate;
861 editingDelegate.adoptRef(new EditingDelegate);
862 if (FAILED(viewEditing->setEditingDelegate(editingDelegate.get())))
865 COMPtr<IWebPreferences> preferences;
866 if (FAILED(webView->preferences(&preferences)))
869 initializePreferences(preferences.get());
871 COMPtr<IWebIconDatabase> iconDatabase;
872 COMPtr<IWebIconDatabase> tmpIconDatabase;
873 if (FAILED(CoCreateInstance(CLSID_WebIconDatabase, 0, CLSCTX_ALL, IID_IWebIconDatabase, (void**)&tmpIconDatabase)))
875 if (FAILED(tmpIconDatabase->sharedIconDatabase(&iconDatabase)))
878 if (FAILED(webView->mainFrame(&frame)))
881 _CrtMemState entryToMainMemCheckpoint;
883 _CrtMemCheckpoint(&entryToMainMemCheckpoint);
885 for (int i = 0; i < argc; ++i)
886 if (!stricmp(argv[i], "--threaded")) {
887 argv[i] = argv[argc - 1];
894 startJavaScriptThreads();
896 if (argc == 2 && strcmp(argv[1], "-") == 0) {
897 char filenameBuffer[2048];
898 printSeparators = true;
899 while (fgets(filenameBuffer, sizeof(filenameBuffer), stdin)) {
900 char* newLineCharacter = strchr(filenameBuffer, '\n');
901 if (newLineCharacter)
902 *newLineCharacter = '\0';
904 if (strlen(filenameBuffer) == 0)
907 runTest(filenameBuffer);
911 printSeparators = argc > 2;
912 for (int i = 1; i != argc; i++)
917 stopJavaScriptThreads();
919 delete policyDelegate;
923 // dump leaks to stderr
924 _CrtSetReportMode(_CRT_WARN, _CRTDBG_MODE_FILE);
925 _CrtSetReportFile(_CRT_WARN, _CRTDBG_FILE_STDERR);
926 _CrtMemDumpAllObjectsSince(&entryToMainMemCheckpoint);