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