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