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);
605 COMPtr<IWebHistory> history(Create, CLSID_WebHistory);
607 history->setOptionalSharedHistory(0);
610 COMPtr<IWebView> webView;
611 if (SUCCEEDED(frame->webView(&webView))) {
612 COMPtr<IWebBackForwardList> bfList;
613 if (SUCCEEDED(webView->backForwardList(&bfList)))
614 bfList->currentItem(&prevTestBFItem);
616 webView->setPolicyDelegate(NULL);
618 COMPtr<IWebIBActions> webIBActions;
619 if (SUCCEEDED(webView->QueryInterface(IID_IWebIBActions, (void**)&webIBActions)))
620 webIBActions->makeTextStandardSize(0);
623 WorkQueue::shared()->clear();
624 WorkQueue::shared()->setFrozen(false);
626 // Set the test timeout timer
627 SetTimer(hostWindow, timeoutId, timeoutValue, 0);
629 COMPtr<IWebMutableURLRequest> request;
630 HRESULT hr = CoCreateInstance(CLSID_WebMutableURLRequest, 0, CLSCTX_ALL, IID_IWebMutableURLRequest, (void**)&request);
634 request->initWithURL(urlBStr, WebURLRequestUseProtocolCachePolicy, 0);
636 request->setHTTPMethod(methodBStr);
637 frame->loadRequest(request.get());
640 while (GetMessage(&msg, 0, 0, 0)) {
641 TranslateMessage(&msg);
642 DispatchMessage(&msg);
644 KillTimer(hostWindow, timeoutId);
647 fprintf(stderr, "ERROR: Timed out running %s\n", pathOrURL);
648 printf("ERROR: Timed out loading page\n");
654 SysFreeString(urlBStr);
655 delete ::layoutTestController;
660 static void initializePreferences(IWebPreferences* preferences)
663 BSTR standardFamily = SysAllocString(TEXT("Times"));
664 BSTR fixedFamily = SysAllocString(TEXT("Courier"));
665 BSTR sansSerifFamily = SysAllocString(TEXT("Helvetica"));
666 BSTR cursiveFamily = SysAllocString(TEXT("Apple Chancery"));
667 BSTR fantasyFamily = SysAllocString(TEXT("Papyrus"));
669 BSTR standardFamily = SysAllocString(TEXT("Times New Roman"));
670 BSTR fixedFamily = SysAllocString(TEXT("Courier New"));
671 BSTR sansSerifFamily = SysAllocString(TEXT("Arial"));
672 BSTR cursiveFamily = SysAllocString(TEXT("Comic Sans MS")); // Not actually cursive, but it's what IE and Firefox use.
673 BSTR fantasyFamily = SysAllocString(TEXT("Times New Roman"));
676 preferences->setStandardFontFamily(standardFamily);
677 preferences->setFixedFontFamily(fixedFamily);
678 preferences->setSerifFontFamily(standardFamily);
679 preferences->setSansSerifFontFamily(sansSerifFamily);
680 preferences->setCursiveFontFamily(cursiveFamily);
681 preferences->setFantasyFontFamily(fantasyFamily);
683 preferences->setAutosaves(FALSE);
684 preferences->setJavaEnabled(FALSE);
685 preferences->setPlugInsEnabled(TRUE);
686 preferences->setDOMPasteAllowed(TRUE);
687 preferences->setEditableLinkBehavior(WebKitEditableLinkOnlyLiveWithShiftKey);
689 SysFreeString(standardFamily);
690 SysFreeString(fixedFamily);
691 SysFreeString(sansSerifFamily);
692 SysFreeString(cursiveFamily);
693 SysFreeString(fantasyFamily);
696 static Boolean pthreadEqualCallback(const void* value1, const void* value2)
698 return (Boolean)pthread_equal(*(pthread_t*)value1, *(pthread_t*)value2);
701 static CFDictionaryKeyCallBacks pthreadKeyCallbacks = { 0, 0, 0, 0, pthreadEqualCallback, 0 };
703 static pthread_mutex_t javaScriptThreadsMutex = PTHREAD_MUTEX_INITIALIZER;
704 static bool javaScriptThreadsShouldTerminate;
706 static const int javaScriptThreadsCount = 4;
707 static CFMutableDictionaryRef javaScriptThreads()
709 assert(pthread_mutex_trylock(&javaScriptThreadsMutex) == EBUSY);
710 static CFMutableDictionaryRef staticJavaScriptThreads;
711 if (!staticJavaScriptThreads)
712 staticJavaScriptThreads = CFDictionaryCreateMutable(0, 0, &pthreadKeyCallbacks, 0);
713 return staticJavaScriptThreads;
716 // Loops forever, running a script and randomly respawning, until
717 // javaScriptThreadsShouldTerminate becomes true.
718 void* runJavaScriptThread(void* arg)
720 const char* const script =
723 for (var i = 0; i < 10; i++) { \
724 array.push(String(i)); \
729 JSGlobalContextRef ctx = JSGlobalContextCreate(0);
730 JSStringRef scriptRef = JSStringCreateWithUTF8CString(script);
732 JSValueRef exception = 0;
733 JSEvaluateScript(ctx, scriptRef, 0, 0, 0, &exception);
736 JSGlobalContextRelease(ctx);
737 JSStringRelease(scriptRef);
739 JSGarbageCollect(ctx);
741 pthread_mutex_lock(&javaScriptThreadsMutex);
743 // Check for cancellation.
744 if (javaScriptThreadsShouldTerminate) {
745 pthread_mutex_unlock(&javaScriptThreadsMutex);
749 // Respawn probabilistically.
750 if (rand() % 5 == 0) {
752 pthread_create(&pthread, 0, &runJavaScriptThread, 0);
753 pthread_detach(pthread);
755 pthread_t self = pthread_self();
756 CFDictionaryRemoveValue(javaScriptThreads(), self.p);
757 CFDictionaryAddValue(javaScriptThreads(), pthread.p, 0);
759 pthread_mutex_unlock(&javaScriptThreadsMutex);
763 pthread_mutex_unlock(&javaScriptThreadsMutex);
767 static void startJavaScriptThreads(void)
769 pthread_mutex_lock(&javaScriptThreadsMutex);
771 for (int i = 0; i < javaScriptThreadsCount; i++) {
773 pthread_create(&pthread, 0, &runJavaScriptThread, 0);
774 pthread_detach(pthread);
775 CFDictionaryAddValue(javaScriptThreads(), pthread.p, 0);
778 pthread_mutex_unlock(&javaScriptThreadsMutex);
781 static void stopJavaScriptThreads(void)
783 pthread_mutex_lock(&javaScriptThreadsMutex);
785 javaScriptThreadsShouldTerminate = true;
787 pthread_t* pthreads[javaScriptThreadsCount] = {0};
788 int threadDictCount = CFDictionaryGetCount(javaScriptThreads());
789 assert(threadDictCount == javaScriptThreadsCount);
790 CFDictionaryGetKeysAndValues(javaScriptThreads(), (const void**)pthreads, 0);
792 pthread_mutex_unlock(&javaScriptThreadsMutex);
794 for (int i = 0; i < javaScriptThreadsCount; i++) {
795 pthread_t* pthread = pthreads[i];
796 pthread_join(*pthread, 0);
801 int main(int argc, char* argv[])
803 leakChecking = false;
805 initialize(GetModuleHandle(0));
809 COMPtr<IWebView> webView;
810 HRESULT hr = CoCreateInstance(CLSID_WebView, 0, CLSCTX_ALL, IID_IWebView, (void**)&webView);
812 fprintf(stderr, "Failed to create CLSID_WebView instance, error 0x%x\n", hr);
816 if (FAILED(webView->setHostWindow((OLE_HANDLE)(ULONG64)hostWindow)))
820 clientRect.bottom = clientRect.left = clientRect.top = clientRect.right = 0;
821 BSTR groupName = SysAllocString(L"org.webkit.DumpRenderTree");
822 bool failed = FAILED(webView->initWithFrame(clientRect, 0, groupName));
823 SysFreeString(groupName);
827 COMPtr<IWebViewPrivate> viewPrivate;
828 if (FAILED(webView->QueryInterface(&viewPrivate)))
833 BSTR pluginPath = SysAllocStringLen(0, exePath().length() + _tcslen(TestPluginDir));
834 _tcscpy(pluginPath, exePath().c_str());
835 _tcscat(pluginPath, TestPluginDir);
836 failed = FAILED(viewPrivate->addAdditionalPluginPath(pluginPath));
837 SysFreeString(pluginPath);
841 if (FAILED(viewPrivate->viewWindow((OLE_HANDLE*)&webViewWindow)))
844 SetWindowPos(webViewWindow, 0, 0, 0, maxViewWidth, maxViewHeight, 0);
845 ShowWindow(hostWindow, SW_SHOW);
847 COMPtr<FrameLoadDelegate> frameLoadDelegate;
848 frameLoadDelegate.adoptRef(new FrameLoadDelegate);
849 if (FAILED(webView->setFrameLoadDelegate(frameLoadDelegate.get())))
852 policyDelegate = new PolicyDelegate();
854 COMPtr<UIDelegate> uiDelegate;
855 uiDelegate.adoptRef(new UIDelegate);
856 if (FAILED(webView->setUIDelegate(uiDelegate.get())))
859 COMPtr<IWebViewEditing> viewEditing;
860 if (FAILED(webView->QueryInterface(&viewEditing)))
864 COMPtr<EditingDelegate> editingDelegate;
865 editingDelegate.adoptRef(new EditingDelegate);
866 if (FAILED(viewEditing->setEditingDelegate(editingDelegate.get())))
869 COMPtr<IWebPreferences> preferences;
870 if (FAILED(webView->preferences(&preferences)))
873 initializePreferences(preferences.get());
875 COMPtr<IWebIconDatabase> iconDatabase;
876 COMPtr<IWebIconDatabase> tmpIconDatabase;
877 if (FAILED(CoCreateInstance(CLSID_WebIconDatabase, 0, CLSCTX_ALL, IID_IWebIconDatabase, (void**)&tmpIconDatabase)))
879 if (FAILED(tmpIconDatabase->sharedIconDatabase(&iconDatabase)))
882 if (FAILED(webView->mainFrame(&frame)))
885 _CrtMemState entryToMainMemCheckpoint;
887 _CrtMemCheckpoint(&entryToMainMemCheckpoint);
889 for (int i = 0; i < argc; ++i)
890 if (!stricmp(argv[i], "--threaded")) {
891 argv[i] = argv[argc - 1];
898 startJavaScriptThreads();
900 if (argc == 2 && strcmp(argv[1], "-") == 0) {
901 char filenameBuffer[2048];
902 printSeparators = true;
903 while (fgets(filenameBuffer, sizeof(filenameBuffer), stdin)) {
904 char* newLineCharacter = strchr(filenameBuffer, '\n');
905 if (newLineCharacter)
906 *newLineCharacter = '\0';
908 if (strlen(filenameBuffer) == 0)
911 runTest(filenameBuffer);
915 printSeparators = argc > 2;
916 for (int i = 1; i != argc; i++)
921 stopJavaScriptThreads();
923 delete policyDelegate;
927 // dump leaks to stderr
928 _CrtSetReportMode(_CRT_WARN, _CRTDBG_MODE_FILE);
929 _CrtSetReportFile(_CRT_WARN, _CRTDBG_FILE_STDERR);
930 _CrtMemDumpAllObjectsSince(&entryToMainMemCheckpoint);