Windows build fixes
[WebKit-https.git] / WebKitTools / DumpRenderTree / win / DumpRenderTree.cpp
1 /*
2  * Copyright (C) 2005, 2006, 2007, 2008 Apple Inc.  All rights reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions
6  * are met:
7  *
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. 
16  *
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.
27  */
28
29 #include "DumpRenderTree.h"
30
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/RetainPtr.h>
41 #include <wtf/Vector.h>
42 #include <WebCore/COMPtr.h>
43 #include <CoreFoundation/CoreFoundation.h>
44 #include <CFNetwork/CFURLCachePriv.h>
45 #include <JavaScriptCore/JavaScriptCore.h>
46 #include <math.h>
47 #include <pthread.h>
48 #include <string>
49 #include <tchar.h>
50 #include <WebKit/WebKit.h>
51 #include <fcntl.h>
52 #include <io.h>
53 #include <windows.h>
54 #include <stdio.h>
55 #include <shlwapi.h>
56
57 using std::wstring;
58
59 #ifndef NDEBUG
60 const LPWSTR TestPluginDir = L"TestNetscapePlugin_Debug";
61 #else
62 const LPWSTR TestPluginDir = L"TestNetscapePlugin";
63 #endif
64
65 static LPCWSTR fontsEnvironmentVariable = L"WEBKIT_TESTFONTS";
66 #define USE_MAC_FONTS
67
68 const LPCWSTR kDumpRenderTreeClassName = L"DumpRenderTreeWindow";
69
70 static bool dumpTree = true;
71 static bool dumpPixels;
72 static bool dumpAllPixels;
73 static bool printSeparators;
74 static bool leakChecking = false;
75 static bool threaded = false;
76 static RetainPtr<CFStringRef> persistentUserStyleSheetLocation;
77
78 static const char* currentTest;
79
80 volatile bool done;
81 // This is the topmost frame that is loading, during a given load, or nil when no load is 
82 // in progress.  Usually this is the same as the main frame, but not always.  In the case
83 // where a frameset is loaded, and then new content is loaded into one of the child frames,
84 // that child frame is the "topmost frame that is loading".
85 IWebFrame* topLoadingFrame;     // !nil iff a load is in progress
86 static COMPtr<IWebHistoryItem> prevTestBFItem;  // current b/f item at the end of the previous test
87 IWebPolicyDelegate* policyDelegate; 
88 COMPtr<FrameLoadDelegate> sharedFrameLoadDelegate;
89 COMPtr<UIDelegate> sharedUIDelegate;
90 COMPtr<EditingDelegate> sharedEditingDelegate;
91 COMPtr<ResourceLoadDelegate> sharedResourceLoadDelegate;
92
93 IWebFrame* frame;
94 HWND webViewWindow;
95
96 LayoutTestController* layoutTestController = 0;
97 CFRunLoopTimerRef waitToDumpWatchdog = 0; 
98
99 const unsigned maxViewWidth = 800;
100 const unsigned maxViewHeight = 600;
101
102 void setPersistentUserStyleSheetLocation(CFStringRef url)
103 {
104     persistentUserStyleSheetLocation = url;
105 }
106
107 wstring urlSuitableForTestResult(const wstring& url)
108 {
109     if (!url.c_str() || url.find(L"file://") == wstring::npos)
110         return url;
111
112     return PathFindFileNameW(url.c_str());
113 }
114
115 static LRESULT CALLBACK DumpRenderTreeWndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
116 {
117     switch (msg) {
118         case WM_DESTROY:
119             for (unsigned i = openWindows().size() - 1; i >= 0; --i) {
120                 if (openWindows()[i] == hWnd) {
121                     openWindows().remove(i);
122                     windowToWebViewMap().remove(hWnd);
123                     break;
124                 }
125             }
126             return 0;
127             break;
128         default:
129             return DefWindowProc(hWnd, msg, wParam, lParam);
130     }
131 }
132
133 static const wstring& exePath()
134 {
135     static wstring path;
136     static bool initialized;
137
138     if (initialized)
139         return path;
140     initialized = true;
141
142     TCHAR buffer[MAX_PATH];
143     GetModuleFileName(GetModuleHandle(0), buffer, ARRAYSIZE(buffer));
144     path = buffer;
145     int lastSlash = path.rfind('\\');
146     if (lastSlash != -1 && lastSlash + 1 < path.length())
147         path = path.substr(0, lastSlash + 1);
148
149     return path;
150 }
151
152 static const wstring& fontsPath()
153 {
154     static wstring path;
155     static bool initialized;
156
157     if (initialized)
158         return path;
159     initialized = true;
160
161     DWORD size = GetEnvironmentVariable(fontsEnvironmentVariable, 0, 0);
162     Vector<TCHAR> buffer(size);
163     if (GetEnvironmentVariable(fontsEnvironmentVariable, buffer.data(), buffer.size())) {
164         path = buffer.data();
165         if (path[path.length() - 1] != '\\')
166             path.append(L"\\");
167         return path;
168     }
169
170     path = exePath() + TEXT("DumpRenderTree.resources\\");
171     return path;
172 }
173
174 #ifdef DEBUG_WEBKIT_HAS_SUFFIX
175 #define WEBKITDLL TEXT("WebKit_debug.dll")
176 #else
177 #define WEBKITDLL TEXT("WebKit.dll")
178 #endif
179
180 static void initialize()
181 {
182     if (HMODULE webKitModule = LoadLibrary(WEBKITDLL))
183         if (FARPROC dllRegisterServer = GetProcAddress(webKitModule, "DllRegisterServer"))
184             dllRegisterServer();
185
186     // Init COM
187     OleInitialize(0);
188
189     static LPCTSTR fontsToInstall[] = {
190         TEXT("AHEM____.ttf"),
191         TEXT("Apple Chancery.ttf"),
192         TEXT("Courier Bold.ttf"),
193         TEXT("Courier.ttf"),
194         TEXT("Helvetica Bold Oblique.ttf"),
195         TEXT("Helvetica Bold.ttf"),
196         TEXT("Helvetica Oblique.ttf"),
197         TEXT("Helvetica.ttf"),
198         TEXT("Helvetica Neue Bold Italic.ttf"),
199         TEXT("Helvetica Neue Bold.ttf"),
200         TEXT("Helvetica Neue Condensed Black.ttf"),
201         TEXT("Helvetica Neue Condensed Bold.ttf"),
202         TEXT("Helvetica Neue Italic.ttf"),
203         TEXT("Helvetica Neue Light Italic.ttf"),
204         TEXT("Helvetica Neue Light.ttf"),
205         TEXT("Helvetica Neue UltraLight Italic.ttf"),
206         TEXT("Helvetica Neue UltraLight.ttf"),
207         TEXT("Helvetica Neue.ttf"),
208         TEXT("Lucida Grande.ttf"),
209         TEXT("Lucida Grande Bold.ttf"),
210         TEXT("Monaco.ttf"),
211         TEXT("Papyrus.ttf"),
212         TEXT("Times Bold Italic.ttf"),
213         TEXT("Times Bold.ttf"),
214         TEXT("Times Italic.ttf"),
215         TEXT("Times Roman.ttf"),
216         TEXT("WebKit Layout Tests.ttf")
217     };
218
219     wstring resourcesPath = fontsPath();
220
221     COMPtr<IWebTextRenderer> textRenderer;
222     if (SUCCEEDED(CoCreateInstance(CLSID_WebTextRenderer, 0, CLSCTX_ALL, IID_IWebTextRenderer, (void**)&textRenderer)))
223         for (int i = 0; i < ARRAYSIZE(fontsToInstall); ++i)
224             textRenderer->registerPrivateFont(wstring(resourcesPath + fontsToInstall[i]).c_str());
225
226     // Register a host window
227     WNDCLASSEX wcex;
228
229     wcex.cbSize = sizeof(WNDCLASSEX);
230
231     wcex.style         = CS_HREDRAW | CS_VREDRAW;
232     wcex.lpfnWndProc   = DumpRenderTreeWndProc;
233     wcex.cbClsExtra    = 0;
234     wcex.cbWndExtra    = 0;
235     wcex.hInstance     = GetModuleHandle(0);
236     wcex.hIcon         = 0;
237     wcex.hCursor       = LoadCursor(0, IDC_ARROW);
238     wcex.hbrBackground = 0;
239     wcex.lpszMenuName  = 0;
240     wcex.lpszClassName = kDumpRenderTreeClassName;
241     wcex.hIconSm       = 0;
242
243     RegisterClassEx(&wcex);
244 }
245
246 void displayWebView()
247 {
248     ::InvalidateRect(webViewWindow, 0, TRUE);
249     ::UpdateWindow(webViewWindow);
250 }
251
252 void dumpFrameScrollPosition(IWebFrame* frame)
253 {
254     if (!frame)
255         return;
256
257     COMPtr<IWebFramePrivate> framePrivate;
258     if (FAILED(frame->QueryInterface(framePrivate.adoptionPointer())))
259         return;
260
261     SIZE scrollPosition;
262     if (FAILED(framePrivate->scrollOffset(&scrollPosition)))
263         return;
264
265     if (abs(scrollPosition.cx) > 0.00000001 || abs(scrollPosition.cy) > 0.00000001) {
266         COMPtr<IWebFrame> parent;
267         if (FAILED(frame->parentFrame(parent.adoptionPointer())))
268             return;
269         if (parent) {
270             BSTR name;
271             if (FAILED(frame->name(&name)))
272                 return;
273             printf("frame '%S' ", name ? name : L"");
274             SysFreeString(name);
275         }
276         printf("scrolled to %.f,%.f\n", (double)scrollPosition.cx, (double)scrollPosition.cy);
277     }
278
279     if (::layoutTestController->dumpChildFrameScrollPositions()) {
280         COMPtr<IEnumVARIANT> enumKids;
281         if (FAILED(frame->childFrames(enumKids.adoptionPointer())))
282             return;
283         VARIANT var;
284         VariantInit(&var);
285         while (enumKids->Next(1, &var, 0) == S_OK) {
286             ASSERT(V_VT(&var) == VT_UNKNOWN);
287             COMPtr<IWebFrame> framePtr;
288             V_UNKNOWN(&var)->QueryInterface(IID_IWebFrame, (void**)&framePtr);
289             dumpFrameScrollPosition(framePtr.get());
290             VariantClear(&var);
291         }
292     }
293 }
294
295 static wstring dumpFramesAsText(IWebFrame* frame)
296 {
297     if (!frame)
298         return L"";
299
300     COMPtr<IDOMDocument> document;
301     if (FAILED(frame->DOMDocument(document.adoptionPointer())))
302         return L"";
303
304     COMPtr<IDOMElement> documentElement;
305     if (FAILED(document->documentElement(documentElement.adoptionPointer())))
306         return L"";
307
308     wstring result;
309
310     // Add header for all but the main frame.
311     COMPtr<IWebFrame> parent;
312     if (FAILED(frame->parentFrame(parent.adoptionPointer())))
313         return L"";
314     if (parent) {
315         BSTR name = L"";
316         if (FAILED(frame->name(&name)))
317             return L"";
318
319         result.append(L"\n--------\nFrame: '");
320         result.append(name ? name : L"", SysStringLen(name));
321         result.append(L"'\n--------\n");
322
323         SysFreeString(name);
324     }
325
326     BSTR innerText = 0;
327     COMPtr<IDOMElementPrivate> docPrivate;
328     if (SUCCEEDED(documentElement->QueryInterface(docPrivate.adoptionPointer())))
329         docPrivate->innerText(&innerText);
330
331     result.append(innerText ? innerText : L"", SysStringLen(innerText));
332     result.append(L"\n");
333
334     SysFreeString(innerText);
335
336     if (::layoutTestController->dumpChildFramesAsText()) {
337         COMPtr<IEnumVARIANT> enumKids;
338         if (FAILED(frame->childFrames(enumKids.adoptionPointer())))
339             return L"";
340         VARIANT var;
341         VariantInit(&var);
342         while (enumKids->Next(1, &var, 0) == S_OK) {
343             ASSERT(V_VT(&var) == VT_UNKNOWN);
344             COMPtr<IWebFrame> framePtr;
345             V_UNKNOWN(&var)->QueryInterface(IID_IWebFrame, (void**)&framePtr);
346             result.append(dumpFramesAsText(framePtr.get()));
347             VariantClear(&var);
348         }
349     }
350
351     return result;
352 }
353
354 static int compareHistoryItems(const void* item1, const void* item2)
355 {
356     COMPtr<IWebHistoryItemPrivate> itemA;
357     if (FAILED((*(COMPtr<IUnknown>*)item1)->QueryInterface(itemA.adoptionPointer())))
358         return 0;
359
360     COMPtr<IWebHistoryItemPrivate> itemB;
361     if (FAILED((*(COMPtr<IUnknown>*)item2)->QueryInterface(itemB.adoptionPointer())))
362         return 0;
363
364     BSTR targetA;
365     if (FAILED(itemA->target(&targetA)))
366         return 0;
367
368     BSTR targetB;
369     if (FAILED(itemB->target(&targetB))) {
370         SysFreeString(targetA);
371         return 0;
372     }
373
374     int result = wcsicmp(wstring(targetA, SysStringLen(targetA)).c_str(), wstring(targetB, SysStringLen(targetB)).c_str());
375     SysFreeString(targetA);
376     SysFreeString(targetB);
377     return result;
378 }
379
380 static void dumpHistoryItem(IWebHistoryItem* item, int indent, bool current)
381 {
382     assert(item);
383
384     int start = 0;
385     if (current) {
386         printf("curr->");
387         start = 6;
388     }
389     for (int i = start; i < indent; i++)
390         putchar(' ');
391
392     BSTR url;
393     if (FAILED(item->URLString(&url)))
394         return;
395     printf("%S", url ? url : L"");
396     SysFreeString(url);
397
398     COMPtr<IWebHistoryItemPrivate> itemPrivate;
399     if (FAILED(item->QueryInterface(itemPrivate.adoptionPointer())))
400         return;
401
402     BSTR target;
403     if (FAILED(itemPrivate->target(&target)))
404         return;
405     if (SysStringLen(target))
406         printf(" (in frame \"%S\")", target);
407     SysFreeString(target);
408     BOOL isTargetItem = FALSE;
409     if (FAILED(itemPrivate->isTargetItem(&isTargetItem)))
410         return;
411     if (isTargetItem)
412         printf("  **nav target**");
413     putchar('\n');
414
415     unsigned kidsCount;
416     SAFEARRAY* arrPtr;
417     if (FAILED(itemPrivate->children(&kidsCount, &arrPtr)) || !kidsCount)
418         return;
419
420     Vector<COMPtr<IUnknown> > kidsVector;
421
422     LONG lowerBound;
423     if (FAILED(::SafeArrayGetLBound(arrPtr, 1, &lowerBound)))
424         goto exit;
425
426     LONG upperBound;
427     if (FAILED(::SafeArrayGetUBound(arrPtr, 1, &upperBound)))
428         goto exit;
429
430     LONG length = upperBound - lowerBound + 1;
431     if (!length)
432         goto exit;
433     ASSERT(length == kidsCount);
434
435     IUnknown** safeArrayData;
436     if (FAILED(::SafeArrayAccessData(arrPtr, (void**)&safeArrayData)))
437         goto exit;
438
439     for (int i = 0; i < length; ++i)
440         kidsVector.append(safeArrayData[i]);
441     ::SafeArrayUnaccessData(arrPtr);
442
443     // must sort to eliminate arbitrary result ordering which defeats reproducible testing
444     qsort(kidsVector.data(), kidsCount, sizeof(kidsVector[0]), compareHistoryItems);
445
446     for (unsigned i = 0; i < kidsCount; ++i) {
447         COMPtr<IWebHistoryItem> item;
448         kidsVector[i]->QueryInterface(item.adoptionPointer());
449         dumpHistoryItem(item.get(), indent + 4, false);
450     }
451
452 exit:
453     if (arrPtr && SUCCEEDED(::SafeArrayUnlock(arrPtr)))
454         ::SafeArrayDestroy(arrPtr);
455 }
456
457 static void dumpBackForwardList(IWebView* webView)
458 {
459     ASSERT(webView);
460
461     printf("\n============== Back Forward List ==============\n");
462
463     COMPtr<IWebBackForwardList> bfList;
464     if (FAILED(webView->backForwardList(bfList.adoptionPointer())))
465         return;
466
467     // Print out all items in the list after prevTestBFItem, which was from the previous test
468     // Gather items from the end of the list, the print them out from oldest to newest
469
470     Vector<COMPtr<IUnknown> > itemsToPrint;
471
472     int forwardListCount;
473     if (FAILED(bfList->forwardListCount(&forwardListCount)))
474         return;
475
476     for (int i = forwardListCount; i > 0; --i) {
477         COMPtr<IWebHistoryItem> item;
478         if (FAILED(bfList->itemAtIndex(i, item.adoptionPointer())))
479             return;
480         // something is wrong if the item from the last test is in the forward part of the b/f list
481         assert(item != prevTestBFItem);
482         COMPtr<IUnknown> itemUnknown;
483         item->QueryInterface(itemUnknown.adoptionPointer());
484         itemsToPrint.append(itemUnknown);
485     }
486     
487     COMPtr<IWebHistoryItem> currentItem;
488     if (FAILED(bfList->currentItem(currentItem.adoptionPointer())))
489         return;
490
491     assert(currentItem != prevTestBFItem);
492     COMPtr<IUnknown> currentItemUnknown;
493     currentItem->QueryInterface(currentItemUnknown.adoptionPointer());
494     itemsToPrint.append(currentItemUnknown);
495     int currentItemIndex = itemsToPrint.size() - 1;
496
497     int backListCount;
498     if (FAILED(bfList->backListCount(&backListCount)))
499         return;
500
501     for (int i = -1; i >= -backListCount; --i) {
502         COMPtr<IWebHistoryItem> item;
503         if (FAILED(bfList->itemAtIndex(i, item.adoptionPointer())))
504             return;
505         if (item == prevTestBFItem)
506             break;
507         COMPtr<IUnknown> itemUnknown;
508         item->QueryInterface(itemUnknown.adoptionPointer());
509         itemsToPrint.append(itemUnknown);
510     }
511
512     for (int i = itemsToPrint.size() - 1; i >= 0; --i) {
513         COMPtr<IWebHistoryItem> historyItemToPrint;
514         itemsToPrint[i]->QueryInterface(historyItemToPrint.adoptionPointer());
515         dumpHistoryItem(historyItemToPrint.get(), 8, i == currentItemIndex);
516     }
517
518     printf("===============================================\n");
519 }
520
521 static void dumpBackForwardListForAllWindows()
522 {
523     unsigned count = openWindows().size();
524     for (unsigned i = 0; i < count; i++) {
525         HWND window = openWindows()[i];
526         IWebView* webView = windowToWebViewMap().get(window);
527         dumpBackForwardList(webView);
528     }
529 }
530
531 void dump()
532 {
533     COMPtr<IWebDataSource> dataSource;
534     if (SUCCEEDED(frame->dataSource(dataSource.adoptionPointer()))) {
535         COMPtr<IWebURLResponse> response;
536         if (SUCCEEDED(dataSource->response(response.adoptionPointer())) && response) {
537             BSTR mimeType;
538             if (SUCCEEDED(response->MIMEType(&mimeType)))
539                 ::layoutTestController->setDumpAsText(::layoutTestController->dumpAsText() | !_tcscmp(mimeType, TEXT("text/plain")));
540             SysFreeString(mimeType);
541         }
542     }
543
544     BSTR resultString = 0;
545
546     if (dumpTree) {
547         if (::layoutTestController->dumpAsText()) {
548             ::InvalidateRect(webViewWindow, 0, TRUE);
549             ::SendMessage(webViewWindow, WM_PAINT, 0, 0);
550             wstring result = dumpFramesAsText(frame);
551             resultString = SysAllocStringLen(result.data(), result.size());
552         } else {
553             bool isSVGW3CTest = strstr(currentTest, "svg\\W3C-SVG-1.1");
554             unsigned width;
555             unsigned height;
556             if (isSVGW3CTest) {
557                 width = 480;
558                 height = 360;
559             } else {
560                 width = maxViewWidth;
561                 height = maxViewHeight;
562             }
563
564             ::SetWindowPos(webViewWindow, 0, 0, 0, width, height, SWP_NOMOVE);
565             ::InvalidateRect(webViewWindow, 0, TRUE);
566             ::SendMessage(webViewWindow, WM_PAINT, 0, 0);
567
568             COMPtr<IWebFramePrivate> framePrivate;
569             if (FAILED(frame->QueryInterface(framePrivate.adoptionPointer())))
570                 goto fail;
571             framePrivate->renderTreeAsExternalRepresentation(&resultString);
572         }
573         
574         if (!resultString)
575             printf("ERROR: nil result from %s", ::layoutTestController->dumpAsText() ? "IDOMElement::innerText" : "IFrameViewPrivate::renderTreeAsExternalRepresentation");
576         else {
577             unsigned stringLength = SysStringLen(resultString);
578             int bufferSize = ::WideCharToMultiByte(CP_UTF8, 0, resultString, stringLength, 0, 0, 0, 0);
579             char* buffer = (char*)malloc(bufferSize + 1);
580             ::WideCharToMultiByte(CP_UTF8, 0, resultString, stringLength, buffer, bufferSize + 1, 0, 0);
581             fwrite(buffer, 1, bufferSize, stdout);
582             free(buffer);
583             if (!::layoutTestController->dumpAsText())
584                 dumpFrameScrollPosition(frame);
585         }
586         if (::layoutTestController->dumpBackForwardList())
587             dumpBackForwardListForAllWindows();
588     }
589
590     if (printSeparators) {
591         puts("#EOF");
592         fputs("#EOF\n", stderr);
593         fflush(stdout);
594         fflush(stderr);
595     }
596
597     if (dumpPixels) {
598         if (layoutTestController->dumpAsText() || layoutTestController->dumpDOMAsWebArchive() || layoutTestController->dumpSourceAsWebArchive())
599             printf("#EOF\n");
600         else
601             dumpWebViewAsPixelsAndCompareWithExpected(currentTest, dumpAllPixels);
602         fflush(stdout);
603     }
604
605 fail:
606     SysFreeString(resultString);
607     // This will exit from our message loop
608     PostQuitMessage(0);
609     done = true;
610 }
611
612 static bool shouldLogFrameLoadDelegates(const char* pathOrURL)
613 {
614     return strstr(pathOrURL, "loading/");
615 }
616
617 static void resetWebViewToConsistentStateBeforeTesting()
618 {
619     COMPtr<IWebView> webView;
620     if (FAILED(frame->webView(webView.adoptionPointer()))) 
621         return;
622
623     webView->setPolicyDelegate(0);
624
625     COMPtr<IWebIBActions> webIBActions(Query, webView);
626     if (webIBActions) {
627         webIBActions->makeTextStandardSize(0);
628         webIBActions->resetPageZoom(0);
629     }
630
631     COMPtr<IWebPreferences> preferences;
632     if (SUCCEEDED(webView->preferences(preferences.adoptionPointer()))) {
633         preferences->setPrivateBrowsingEnabled(FALSE);
634         preferences->setJavaScriptCanOpenWindowsAutomatically(TRUE);
635
636         if (persistentUserStyleSheetLocation) {
637             Vector<wchar_t> urlCharacters(CFStringGetLength(persistentUserStyleSheetLocation.get()));
638             CFStringGetCharacters(persistentUserStyleSheetLocation.get(), CFRangeMake(0, CFStringGetLength(persistentUserStyleSheetLocation.get())), (UniChar *)urlCharacters.data());
639             BSTR url = SysAllocStringLen(urlCharacters.data(), urlCharacters.size());
640             preferences->setUserStyleSheetLocation(url);
641             SysFreeString(url);
642             preferences->setUserStyleSheetEnabled(TRUE);
643         } else
644             preferences->setUserStyleSheetEnabled(FALSE);
645
646         COMPtr<IWebPreferencesPrivate> prefsPrivate(Query, preferences);
647         if (prefsPrivate)
648             prefsPrivate->setAuthorAndUserStylesEnabled(TRUE);
649     }
650
651     COMPtr<IWebViewPrivate> webViewPrivate(Query, webView);
652     if (!webViewPrivate)
653         return;
654
655     HWND viewWindow;
656     if (SUCCEEDED(webViewPrivate->viewWindow(reinterpret_cast<OLE_HANDLE*>(&viewWindow))) && viewWindow)
657         SetFocus(viewWindow);
658 }
659
660 static void runTest(const char* pathOrURL)
661 {
662     static BSTR methodBStr = SysAllocString(TEXT("GET"));
663
664     BSTR urlBStr;
665  
666     CFStringRef str = CFStringCreateWithCString(0, pathOrURL, kCFStringEncodingWindowsLatin1);
667     CFURLRef url = CFURLCreateWithString(0, str, 0);
668
669     if (!url)
670         url = CFURLCreateWithFileSystemPath(0, str, kCFURLWindowsPathStyle, false);
671
672     CFRelease(str);
673
674     str = CFURLGetString(url);
675
676     CFIndex length = CFStringGetLength(str);
677     UniChar* buffer = new UniChar[length];
678
679     CFStringGetCharacters(str, CFRangeMake(0, length), buffer);
680     urlBStr = SysAllocStringLen((OLECHAR*)buffer, length);
681     delete[] buffer;
682
683     CFRelease(url);
684
685     currentTest = pathOrURL;
686
687     ::layoutTestController = new LayoutTestController(false, false);
688     done = false;
689     topLoadingFrame = 0;
690
691     if (shouldLogFrameLoadDelegates(pathOrURL))
692         layoutTestController->setDumpFrameLoadCallbacks(true);
693
694     COMPtr<IWebHistory> history(Create, CLSID_WebHistory);
695     if (history)
696         history->setOptionalSharedHistory(0);
697
698     resetWebViewToConsistentStateBeforeTesting();
699     sharedUIDelegate->resetUndoManager();
700
701     prevTestBFItem = 0;
702     COMPtr<IWebView> webView;
703     if (SUCCEEDED(frame->webView(webView.adoptionPointer()))) {
704         COMPtr<IWebBackForwardList> bfList;
705         if (SUCCEEDED(webView->backForwardList(bfList.adoptionPointer())))
706             bfList->currentItem(prevTestBFItem.adoptionPointer());
707     }
708
709     WorkQueue::shared()->clear();
710     WorkQueue::shared()->setFrozen(false);
711
712     HWND hostWindow;
713     webView->hostWindow(reinterpret_cast<OLE_HANDLE*>(&hostWindow));
714
715     COMPtr<IWebMutableURLRequest> request;
716     HRESULT hr = CoCreateInstance(CLSID_WebMutableURLRequest, 0, CLSCTX_ALL, IID_IWebMutableURLRequest, (void**)&request);
717     if (FAILED(hr))
718         goto exit;
719
720     request->initWithURL(urlBStr, WebURLRequestUseProtocolCachePolicy, 60);
721
722     request->setHTTPMethod(methodBStr);
723     frame->loadRequest(request.get());
724
725     MSG msg;
726     while (GetMessage(&msg, 0, 0, 0)) {
727         // We get spurious WM_MOUSELEAVE events which make event handling machinery think that mouse button
728         // is released during dragging (see e.g. fast\dynamic\layer-hit-test-crash.html).
729         // Mouse can never leave WebView during normal DumpRenderTree operation, so we just ignore all such events.
730         if (msg.message == WM_MOUSELEAVE)
731             continue;
732         TranslateMessage(&msg);
733         DispatchMessage(&msg);
734     }
735
736     frame->stopLoading();
737
738     if (::layoutTestController->closeRemainingWindowsWhenComplete()) {
739         Vector<HWND> windows = openWindows();
740         unsigned size = windows.size();
741         for (unsigned i = 0; i < size; i++) {
742             HWND window = windows[i];
743
744             // Don't try to close the main window
745             if (window == hostWindow)
746                 continue;
747
748             DestroyWindow(window);
749         }
750     }
751
752 exit:
753     SysFreeString(urlBStr);
754     delete ::layoutTestController;
755
756     return;
757 }
758
759 static void initializePreferences(IWebPreferences* preferences)
760 {
761 #ifdef USE_MAC_FONTS
762     BSTR standardFamily = SysAllocString(TEXT("Times"));
763     BSTR fixedFamily = SysAllocString(TEXT("Courier"));
764     BSTR sansSerifFamily = SysAllocString(TEXT("Helvetica"));
765     BSTR cursiveFamily = SysAllocString(TEXT("Apple Chancery"));
766     BSTR fantasyFamily = SysAllocString(TEXT("Papyrus"));
767 #else
768     BSTR standardFamily = SysAllocString(TEXT("Times New Roman"));
769     BSTR fixedFamily = SysAllocString(TEXT("Courier New"));
770     BSTR sansSerifFamily = SysAllocString(TEXT("Arial"));
771     BSTR cursiveFamily = SysAllocString(TEXT("Comic Sans MS")); // Not actually cursive, but it's what IE and Firefox use.
772     BSTR fantasyFamily = SysAllocString(TEXT("Times New Roman"));
773 #endif
774
775     preferences->setStandardFontFamily(standardFamily);
776     preferences->setFixedFontFamily(fixedFamily);
777     preferences->setSerifFontFamily(standardFamily);
778     preferences->setSansSerifFontFamily(sansSerifFamily);
779     preferences->setCursiveFontFamily(cursiveFamily);
780     preferences->setFantasyFontFamily(fantasyFamily);
781
782     preferences->setAutosaves(FALSE);
783     preferences->setJavaEnabled(FALSE);
784     preferences->setPlugInsEnabled(TRUE);
785     preferences->setDOMPasteAllowed(TRUE);
786     preferences->setEditableLinkBehavior(WebKitEditableLinkOnlyLiveWithShiftKey);
787     preferences->setFontSmoothing(FontSmoothingTypeStandard);
788     preferences->setUsesPageCache(FALSE);
789
790     SysFreeString(standardFamily);
791     SysFreeString(fixedFamily);
792     SysFreeString(sansSerifFamily);
793     SysFreeString(cursiveFamily);
794     SysFreeString(fantasyFamily);
795 }
796
797 static Boolean pthreadEqualCallback(const void* value1, const void* value2)
798 {
799     return (Boolean)pthread_equal(*(pthread_t*)value1, *(pthread_t*)value2);
800 }
801
802 static CFDictionaryKeyCallBacks pthreadKeyCallbacks = { 0, 0, 0, 0, pthreadEqualCallback, 0 };
803
804 static pthread_mutex_t javaScriptThreadsMutex = PTHREAD_MUTEX_INITIALIZER;
805 static bool javaScriptThreadsShouldTerminate;
806
807 static const int javaScriptThreadsCount = 4;
808 static CFMutableDictionaryRef javaScriptThreads()
809 {
810     assert(pthread_mutex_trylock(&javaScriptThreadsMutex) == EBUSY);
811     static CFMutableDictionaryRef staticJavaScriptThreads;
812     if (!staticJavaScriptThreads)
813         staticJavaScriptThreads = CFDictionaryCreateMutable(0, 0, &pthreadKeyCallbacks, 0);
814     return staticJavaScriptThreads;
815 }
816
817 // Loops forever, running a script and randomly respawning, until 
818 // javaScriptThreadsShouldTerminate becomes true.
819 void* runJavaScriptThread(void* arg)
820 {
821     const char* const script =
822     " \
823     var array = []; \
824     for (var i = 0; i < 10; i++) { \
825         array.push(String(i)); \
826     } \
827     ";
828
829     while (true) {
830         JSGlobalContextRef ctx = JSGlobalContextCreate(0);
831         JSStringRef scriptRef = JSStringCreateWithUTF8CString(script);
832
833         JSValueRef exception = 0;
834         JSEvaluateScript(ctx, scriptRef, 0, 0, 0, &exception);
835         assert(!exception);
836         
837         JSGlobalContextRelease(ctx);
838         JSStringRelease(scriptRef);
839         
840         JSGarbageCollect(ctx);
841
842         pthread_mutex_lock(&javaScriptThreadsMutex);
843
844         // Check for cancellation.
845         if (javaScriptThreadsShouldTerminate) {
846             pthread_mutex_unlock(&javaScriptThreadsMutex);
847             return 0;
848         }
849
850         // Respawn probabilistically.
851         if (rand() % 5 == 0) {
852             pthread_t pthread;
853             pthread_create(&pthread, 0, &runJavaScriptThread, 0);
854             pthread_detach(pthread);
855
856             pthread_t self = pthread_self();
857             CFDictionaryRemoveValue(javaScriptThreads(), self.p);
858             CFDictionaryAddValue(javaScriptThreads(), pthread.p, 0);
859
860             pthread_mutex_unlock(&javaScriptThreadsMutex);
861             return 0;
862         }
863
864         pthread_mutex_unlock(&javaScriptThreadsMutex);
865     }
866 }
867
868 static void startJavaScriptThreads(void)
869 {
870     pthread_mutex_lock(&javaScriptThreadsMutex);
871
872     for (int i = 0; i < javaScriptThreadsCount; i++) {
873         pthread_t pthread;
874         pthread_create(&pthread, 0, &runJavaScriptThread, 0);
875         pthread_detach(pthread);
876         CFDictionaryAddValue(javaScriptThreads(), pthread.p, 0);
877     }
878
879     pthread_mutex_unlock(&javaScriptThreadsMutex);
880 }
881
882 static void stopJavaScriptThreads(void)
883 {
884     pthread_mutex_lock(&javaScriptThreadsMutex);
885
886     javaScriptThreadsShouldTerminate = true;
887
888     pthread_t* pthreads[javaScriptThreadsCount] = {0};
889     int threadDictCount = CFDictionaryGetCount(javaScriptThreads());
890     assert(threadDictCount == javaScriptThreadsCount);
891     CFDictionaryGetKeysAndValues(javaScriptThreads(), (const void**)pthreads, 0);
892
893     pthread_mutex_unlock(&javaScriptThreadsMutex);
894
895     for (int i = 0; i < javaScriptThreadsCount; i++) {
896         pthread_t* pthread = pthreads[i];
897         pthread_join(*pthread, 0);
898         free(pthread);
899     }
900 }
901
902 Vector<HWND>& openWindows()
903 {
904     static Vector<HWND> vector;
905     return vector;
906 }
907
908 HashMap<HWND, IWebView*>& windowToWebViewMap()
909 {
910     static HashMap<HWND, IWebView*> map;
911     return map;
912 }
913
914 IWebView* createWebViewAndOffscreenWindow(HWND* webViewWindow)
915 {
916     HWND hostWindow = CreateWindowEx(WS_EX_TOOLWINDOW, kDumpRenderTreeClassName, TEXT("DumpRenderTree"), WS_POPUP,
917       -maxViewWidth, -maxViewHeight, maxViewWidth, maxViewHeight, 0, 0, GetModuleHandle(0), 0);
918
919     IWebView* webView;
920
921     HRESULT hr = CoCreateInstance(CLSID_WebView, 0, CLSCTX_ALL, IID_IWebView, (void**)&webView);
922     if (FAILED(hr)) {
923         fprintf(stderr, "Failed to create CLSID_WebView instance, error 0x%x\n", hr);
924         return 0;
925     }
926
927     if (FAILED(webView->setHostWindow((OLE_HANDLE)(ULONG64)hostWindow)))
928         return 0;
929
930     RECT clientRect;
931     clientRect.bottom = clientRect.left = clientRect.top = clientRect.right = 0;
932     BSTR groupName = SysAllocString(L"org.webkit.DumpRenderTree");
933     bool failed = FAILED(webView->initWithFrame(clientRect, 0, groupName));
934     SysFreeString(groupName);
935     if (failed)
936         return 0;
937
938     COMPtr<IWebViewPrivate> viewPrivate;
939     if (FAILED(webView->QueryInterface(viewPrivate.adoptionPointer())))
940         return 0;
941
942     viewPrivate->setShouldApplyMacFontAscentHack(TRUE);
943
944     BSTR pluginPath = SysAllocStringLen(0, exePath().length() + _tcslen(TestPluginDir));
945     _tcscpy(pluginPath, exePath().c_str());
946     _tcscat(pluginPath, TestPluginDir);
947     failed = FAILED(viewPrivate->addAdditionalPluginDirectory(pluginPath));
948     SysFreeString(pluginPath);
949     if (failed)
950         return 0;
951
952     HWND viewWindow;
953     if (FAILED(viewPrivate->viewWindow(reinterpret_cast<OLE_HANDLE*>(&viewWindow))))
954         return 0;
955     if (webViewWindow)
956         *webViewWindow = viewWindow;
957
958     SetWindowPos(viewWindow, 0, 0, 0, maxViewWidth, maxViewHeight, 0);
959     ShowWindow(hostWindow, SW_SHOW);
960
961     if (FAILED(webView->setFrameLoadDelegate(sharedFrameLoadDelegate.get())))
962         return 0;
963
964     if (FAILED(viewPrivate->setFrameLoadDelegatePrivate(sharedFrameLoadDelegate.get())))
965         return 0;
966
967     if (FAILED(webView->setUIDelegate(sharedUIDelegate.get())))
968         return 0;
969
970     COMPtr<IWebViewEditing> viewEditing;
971     if (FAILED(webView->QueryInterface(viewEditing.adoptionPointer())))
972         return 0;
973
974     if (FAILED(viewEditing->setEditingDelegate(sharedEditingDelegate.get())))
975         return 0;
976
977     if (FAILED(webView->setResourceLoadDelegate(sharedResourceLoadDelegate.get())))
978         return 0;
979
980     COMPtr<IWebPreferences> preferences;
981     if (FAILED(webView->preferences(preferences.adoptionPointer())))
982         return 0;
983
984     initializePreferences(preferences.get());
985
986     openWindows().append(hostWindow);
987     windowToWebViewMap().set(hostWindow, webView);
988     return webView;
989 }
990
991 int main(int argc, char* argv[])
992 {
993     leakChecking = false;
994
995     _setmode(1, _O_BINARY);
996     _setmode(2, _O_BINARY);
997
998     initialize();
999
1000     Vector<const char*> tests;
1001
1002     for (int i = 1; i < argc; ++i) {
1003         if (!stricmp(argv[i], "--threaded")) {
1004             threaded = true;
1005             continue;
1006         }
1007
1008         if (!stricmp(argv[i], "--dump-all-pixels")) {
1009             dumpAllPixels = true;
1010             continue;
1011         }
1012
1013         if (!stricmp(argv[i], "--pixel-tests")) {
1014             dumpPixels = true;
1015             continue;
1016         }
1017
1018         tests.append(argv[i]);
1019     }
1020
1021     policyDelegate = new PolicyDelegate();
1022     sharedFrameLoadDelegate.adoptRef(new FrameLoadDelegate);
1023     sharedUIDelegate.adoptRef(new UIDelegate);
1024     sharedEditingDelegate.adoptRef(new EditingDelegate);
1025     sharedResourceLoadDelegate.adoptRef(new ResourceLoadDelegate);
1026
1027     COMPtr<IWebView> webView(AdoptCOM, createWebViewAndOffscreenWindow(&webViewWindow));
1028     if (!webView)
1029         return -1;
1030
1031     COMPtr<IWebIconDatabase> iconDatabase;
1032     COMPtr<IWebIconDatabase> tmpIconDatabase;
1033     if (FAILED(CoCreateInstance(CLSID_WebIconDatabase, 0, CLSCTX_ALL, IID_IWebIconDatabase, (void**)&tmpIconDatabase)))
1034         return -1;
1035     if (FAILED(tmpIconDatabase->sharedIconDatabase(iconDatabase.adoptionPointer())))
1036         return -1;
1037         
1038     if (FAILED(webView->mainFrame(&frame)))
1039         return -1;
1040
1041     CFURLCacheRemoveAllCachedResponses(CFURLCacheSharedURLCache());
1042
1043 #ifdef _DEBUG
1044     _CrtMemState entryToMainMemCheckpoint;
1045     if (leakChecking)
1046         _CrtMemCheckpoint(&entryToMainMemCheckpoint);
1047 #endif
1048
1049     if (threaded)
1050         startJavaScriptThreads();
1051
1052     if (tests.size() == 1 && !strcmp(tests[0], "-")) {
1053         char filenameBuffer[2048];
1054         printSeparators = true;
1055         while (fgets(filenameBuffer, sizeof(filenameBuffer), stdin)) {
1056             char* newLineCharacter = strchr(filenameBuffer, '\n');
1057             if (newLineCharacter)
1058                 *newLineCharacter = '\0';
1059             
1060             if (strlen(filenameBuffer) == 0)
1061                 continue;
1062
1063             runTest(filenameBuffer);
1064         }
1065     } else {
1066         printSeparators = tests.size() > 1;
1067         for (int i = 0; i < tests.size(); i++)
1068             runTest(tests[i]);
1069     }
1070
1071     if (threaded)
1072         stopJavaScriptThreads();
1073     
1074     delete policyDelegate;
1075     frame->Release();
1076
1077 #ifdef _DEBUG
1078     if (leakChecking) {
1079         // dump leaks to stderr
1080         _CrtSetReportMode(_CRT_WARN, _CRTDBG_MODE_FILE);
1081         _CrtSetReportFile(_CRT_WARN, _CRTDBG_FILE_STDERR);
1082         _CrtMemDumpAllObjectsSince(&entryToMainMemCheckpoint);
1083     }
1084 #endif
1085
1086     return 0;
1087 }