ea46a2aa1efaa8d197b05d747e2317268d33e60a
[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 bool shouldLogFrameLoadDelegates(const char* pathOrURL)
587 {
588     return strstr(pathOrURL, "loading/");
589 }
590
591 static void runTest(const char* pathOrURL)
592 {
593     static BSTR methodBStr = SysAllocString(TEXT("GET"));
594
595     BSTR urlBStr;
596  
597     CFStringRef str = CFStringCreateWithCString(0, pathOrURL, kCFStringEncodingWindowsLatin1);
598     CFURLRef url = CFURLCreateWithString(0, str, 0);
599
600     if (!url)
601         url = CFURLCreateWithFileSystemPath(0, str, kCFURLWindowsPathStyle, false);
602
603     CFRelease(str);
604
605     str = CFURLGetString(url);
606
607     CFIndex length = CFStringGetLength(str);
608     UniChar* buffer = new UniChar[length];
609
610     CFStringGetCharacters(str, CFRangeMake(0, length), buffer);
611     urlBStr = SysAllocStringLen((OLECHAR*)buffer, length);
612     delete[] buffer;
613
614     CFRelease(url);
615
616     currentTest = pathOrURL;
617
618     ::layoutTestController = new LayoutTestController(false, false);
619     done = false;
620     topLoadingFrame = 0;
621     timedOut = false;
622
623     if (shouldLogFrameLoadDelegates(pathOrURL))
624         layoutTestController->setDumpFrameLoadCallbacks(true);
625
626     COMPtr<IWebHistory> history(Create, CLSID_WebHistory);
627     if (history)
628         history->setOptionalSharedHistory(0);
629
630     prevTestBFItem = 0;
631     COMPtr<IWebView> webView;
632     if (SUCCEEDED(frame->webView(&webView))) {
633         COMPtr<IWebBackForwardList> bfList;
634         if (SUCCEEDED(webView->backForwardList(&bfList)))
635             bfList->currentItem(&prevTestBFItem);
636
637         webView->setPolicyDelegate(NULL);
638
639         COMPtr<IWebIBActions> webIBActions;
640         if (SUCCEEDED(webView->QueryInterface(IID_IWebIBActions, (void**)&webIBActions)))
641             webIBActions->makeTextStandardSize(0);
642
643         COMPtr<IWebPreferences> preferences;
644         if (SUCCEEDED(webView->preferences(&preferences)))
645             preferences->setPrivateBrowsingEnabled(FALSE);
646     }
647
648     WorkQueue::shared()->clear();
649     WorkQueue::shared()->setFrozen(false);
650
651     // Set the test timeout timer
652     SetTimer(hostWindow, timeoutId, timeoutValue, 0);
653
654     COMPtr<IWebMutableURLRequest> request;
655     HRESULT hr = CoCreateInstance(CLSID_WebMutableURLRequest, 0, CLSCTX_ALL, IID_IWebMutableURLRequest, (void**)&request);
656     if (FAILED(hr))
657         goto exit;
658
659     request->initWithURL(urlBStr, WebURLRequestUseProtocolCachePolicy, 0);
660
661     request->setHTTPMethod(methodBStr);
662     frame->loadRequest(request.get());
663
664     MSG msg;
665     while (GetMessage(&msg, 0, 0, 0)) {
666         TranslateMessage(&msg);
667         DispatchMessage(&msg);
668     }
669     KillTimer(hostWindow, timeoutId);
670
671     if (timedOut) {
672         fprintf(stderr, "ERROR: Timed out running %s\n", pathOrURL);
673         printf("ERROR: Timed out loading page\n");
674
675         if (printSeparators)
676             puts("#EOF");
677     }
678
679     frame->stopLoading();
680
681 exit:
682     SysFreeString(urlBStr);
683     delete ::layoutTestController;
684
685     return;
686 }
687
688 static void initializePreferences(IWebPreferences* preferences)
689 {
690 #ifdef USE_MAC_FONTS
691     BSTR standardFamily = SysAllocString(TEXT("Times"));
692     BSTR fixedFamily = SysAllocString(TEXT("Courier"));
693     BSTR sansSerifFamily = SysAllocString(TEXT("Helvetica"));
694     BSTR cursiveFamily = SysAllocString(TEXT("Apple Chancery"));
695     BSTR fantasyFamily = SysAllocString(TEXT("Papyrus"));
696 #else
697     BSTR standardFamily = SysAllocString(TEXT("Times New Roman"));
698     BSTR fixedFamily = SysAllocString(TEXT("Courier New"));
699     BSTR sansSerifFamily = SysAllocString(TEXT("Arial"));
700     BSTR cursiveFamily = SysAllocString(TEXT("Comic Sans MS")); // Not actually cursive, but it's what IE and Firefox use.
701     BSTR fantasyFamily = SysAllocString(TEXT("Times New Roman"));
702 #endif
703
704     preferences->setStandardFontFamily(standardFamily);
705     preferences->setFixedFontFamily(fixedFamily);
706     preferences->setSerifFontFamily(standardFamily);
707     preferences->setSansSerifFontFamily(sansSerifFamily);
708     preferences->setCursiveFontFamily(cursiveFamily);
709     preferences->setFantasyFontFamily(fantasyFamily);
710
711     preferences->setAutosaves(FALSE);
712     preferences->setJavaEnabled(FALSE);
713     preferences->setPlugInsEnabled(TRUE);
714     preferences->setDOMPasteAllowed(TRUE);
715     preferences->setEditableLinkBehavior(WebKitEditableLinkOnlyLiveWithShiftKey);
716     preferences->setFontSmoothing(FontSmoothingTypeStandard);
717
718     SysFreeString(standardFamily);
719     SysFreeString(fixedFamily);
720     SysFreeString(sansSerifFamily);
721     SysFreeString(cursiveFamily);
722     SysFreeString(fantasyFamily);
723 }
724
725 static Boolean pthreadEqualCallback(const void* value1, const void* value2)
726 {
727     return (Boolean)pthread_equal(*(pthread_t*)value1, *(pthread_t*)value2);
728 }
729
730 static CFDictionaryKeyCallBacks pthreadKeyCallbacks = { 0, 0, 0, 0, pthreadEqualCallback, 0 };
731
732 static pthread_mutex_t javaScriptThreadsMutex = PTHREAD_MUTEX_INITIALIZER;
733 static bool javaScriptThreadsShouldTerminate;
734
735 static const int javaScriptThreadsCount = 4;
736 static CFMutableDictionaryRef javaScriptThreads()
737 {
738     assert(pthread_mutex_trylock(&javaScriptThreadsMutex) == EBUSY);
739     static CFMutableDictionaryRef staticJavaScriptThreads;
740     if (!staticJavaScriptThreads)
741         staticJavaScriptThreads = CFDictionaryCreateMutable(0, 0, &pthreadKeyCallbacks, 0);
742     return staticJavaScriptThreads;
743 }
744
745 // Loops forever, running a script and randomly respawning, until 
746 // javaScriptThreadsShouldTerminate becomes true.
747 void* runJavaScriptThread(void* arg)
748 {
749     const char* const script =
750     " \
751     var array = []; \
752     for (var i = 0; i < 10; i++) { \
753         array.push(String(i)); \
754     } \
755     ";
756
757     while (true) {
758         JSGlobalContextRef ctx = JSGlobalContextCreate(0);
759         JSStringRef scriptRef = JSStringCreateWithUTF8CString(script);
760
761         JSValueRef exception = 0;
762         JSEvaluateScript(ctx, scriptRef, 0, 0, 0, &exception);
763         assert(!exception);
764         
765         JSGlobalContextRelease(ctx);
766         JSStringRelease(scriptRef);
767         
768         JSGarbageCollect(ctx);
769
770         pthread_mutex_lock(&javaScriptThreadsMutex);
771
772         // Check for cancellation.
773         if (javaScriptThreadsShouldTerminate) {
774             pthread_mutex_unlock(&javaScriptThreadsMutex);
775             return 0;
776         }
777
778         // Respawn probabilistically.
779         if (rand() % 5 == 0) {
780             pthread_t pthread;
781             pthread_create(&pthread, 0, &runJavaScriptThread, 0);
782             pthread_detach(pthread);
783
784             pthread_t self = pthread_self();
785             CFDictionaryRemoveValue(javaScriptThreads(), self.p);
786             CFDictionaryAddValue(javaScriptThreads(), pthread.p, 0);
787
788             pthread_mutex_unlock(&javaScriptThreadsMutex);
789             return 0;
790         }
791
792         pthread_mutex_unlock(&javaScriptThreadsMutex);
793     }
794 }
795
796 static void startJavaScriptThreads(void)
797 {
798     pthread_mutex_lock(&javaScriptThreadsMutex);
799
800     for (int i = 0; i < javaScriptThreadsCount; i++) {
801         pthread_t pthread;
802         pthread_create(&pthread, 0, &runJavaScriptThread, 0);
803         pthread_detach(pthread);
804         CFDictionaryAddValue(javaScriptThreads(), pthread.p, 0);
805     }
806
807     pthread_mutex_unlock(&javaScriptThreadsMutex);
808 }
809
810 static void stopJavaScriptThreads(void)
811 {
812     pthread_mutex_lock(&javaScriptThreadsMutex);
813
814     javaScriptThreadsShouldTerminate = true;
815
816     pthread_t* pthreads[javaScriptThreadsCount] = {0};
817     int threadDictCount = CFDictionaryGetCount(javaScriptThreads());
818     assert(threadDictCount == javaScriptThreadsCount);
819     CFDictionaryGetKeysAndValues(javaScriptThreads(), (const void**)pthreads, 0);
820
821     pthread_mutex_unlock(&javaScriptThreadsMutex);
822
823     for (int i = 0; i < javaScriptThreadsCount; i++) {
824         pthread_t* pthread = pthreads[i];
825         pthread_join(*pthread, 0);
826         free(pthread);
827     }
828 }
829
830 int main(int argc, char* argv[])
831 {
832     leakChecking = false;
833
834     _setmode(1, _O_BINARY);
835
836     initialize(GetModuleHandle(0));
837
838     Vector<const char*> tests;
839
840     for (int i = 1; i < argc; ++i) {
841         if (!stricmp(argv[i], "--threaded")) {
842             threaded = true;
843             continue;
844         }
845
846         if (!stricmp(argv[i], "--dump-all-pixels")) {
847             dumpAllPixels = true;
848             continue;
849         }
850
851         if (!stricmp(argv[i], "--pixel-tests")) {
852             dumpPixels = true;
853             continue;
854         }
855
856         tests.append(argv[i]);
857     }
858
859     COMPtr<IWebView> webView;
860     HRESULT hr = CoCreateInstance(CLSID_WebView, 0, CLSCTX_ALL, IID_IWebView, (void**)&webView);
861     if (FAILED(hr)) {
862         fprintf(stderr, "Failed to create CLSID_WebView instance, error 0x%x\n", hr);
863         return -1;
864     }
865
866     if (FAILED(webView->setHostWindow((OLE_HANDLE)(ULONG64)hostWindow)))
867         return -1;
868
869     RECT clientRect;
870     clientRect.bottom = clientRect.left = clientRect.top = clientRect.right = 0;
871     BSTR groupName = SysAllocString(L"org.webkit.DumpRenderTree");
872     bool failed = FAILED(webView->initWithFrame(clientRect, 0, groupName));
873     SysFreeString(groupName);
874     if (failed)
875         return -1;
876
877     COMPtr<IWebViewPrivate> viewPrivate;
878     if (FAILED(webView->QueryInterface(&viewPrivate)))
879         return -1;
880     webView->Release();
881
882
883     BSTR pluginPath = SysAllocStringLen(0, exePath().length() + _tcslen(TestPluginDir));
884     _tcscpy(pluginPath, exePath().c_str());
885     _tcscat(pluginPath, TestPluginDir);
886     failed = FAILED(viewPrivate->addAdditionalPluginPath(pluginPath));
887     SysFreeString(pluginPath);
888     if (failed)
889         return -1;
890
891     if (FAILED(viewPrivate->viewWindow((OLE_HANDLE*)&webViewWindow)))
892         return -1;
893
894     SetWindowPos(webViewWindow, 0, 0, 0, maxViewWidth, maxViewHeight, 0);
895     ShowWindow(hostWindow, SW_SHOW);
896
897     COMPtr<FrameLoadDelegate> frameLoadDelegate;
898     frameLoadDelegate.adoptRef(new FrameLoadDelegate);
899     if (FAILED(webView->setFrameLoadDelegate(frameLoadDelegate.get())))
900         return -1;
901     if (FAILED(viewPrivate->setFrameLoadDelegatePrivate(frameLoadDelegate.get())))
902         return -1;
903
904     policyDelegate = new PolicyDelegate();
905
906     COMPtr<UIDelegate> uiDelegate;
907     uiDelegate.adoptRef(new UIDelegate);
908     if (FAILED(webView->setUIDelegate(uiDelegate.get())))
909         return -1;
910
911     COMPtr<IWebViewEditing> viewEditing;
912     if (FAILED(webView->QueryInterface(&viewEditing)))
913         return -1;
914     webView->Release();
915
916     COMPtr<EditingDelegate> editingDelegate;
917     editingDelegate.adoptRef(new EditingDelegate);
918     if (FAILED(viewEditing->setEditingDelegate(editingDelegate.get())))
919         return -1;
920
921     COMPtr<IWebPreferences> preferences;
922     if (FAILED(webView->preferences(&preferences)))
923         return -1;
924
925     initializePreferences(preferences.get());
926
927     COMPtr<IWebIconDatabase> iconDatabase;
928     COMPtr<IWebIconDatabase> tmpIconDatabase;
929     if (FAILED(CoCreateInstance(CLSID_WebIconDatabase, 0, CLSCTX_ALL, IID_IWebIconDatabase, (void**)&tmpIconDatabase)))
930         return -1;
931     if (FAILED(tmpIconDatabase->sharedIconDatabase(&iconDatabase)))
932         return -1;
933         
934     if (FAILED(webView->mainFrame(&frame)))
935         return -1;
936
937 #ifdef _DEBUG
938     _CrtMemState entryToMainMemCheckpoint;
939     if (leakChecking)
940         _CrtMemCheckpoint(&entryToMainMemCheckpoint);
941 #endif
942
943     if (threaded)
944         startJavaScriptThreads();
945
946     if (tests.size() == 1 && !strcmp(tests[0], "-")) {
947         char filenameBuffer[2048];
948         printSeparators = true;
949         while (fgets(filenameBuffer, sizeof(filenameBuffer), stdin)) {
950             char* newLineCharacter = strchr(filenameBuffer, '\n');
951             if (newLineCharacter)
952                 *newLineCharacter = '\0';
953             
954             if (strlen(filenameBuffer) == 0)
955                 continue;
956
957             runTest(filenameBuffer);
958             fflush(stdout);
959         }
960     } else {
961         printSeparators = tests.size() > 1;
962         for (int i = 0; i < tests.size(); i++)
963             runTest(tests[i]);
964     }
965
966     if (threaded)
967         stopJavaScriptThreads();
968     
969     delete policyDelegate;
970     frame->Release();
971
972 #ifdef _DEBUG
973     if (leakChecking) {
974         // dump leaks to stderr
975         _CrtSetReportMode(_CRT_WARN, _CRTDBG_MODE_FILE);
976         _CrtSetReportFile(_CRT_WARN, _CRTDBG_FILE_STDERR);
977         _CrtMemDumpAllObjectsSince(&entryToMainMemCheckpoint);
978     }
979 #endif
980
981     return 0;
982 }