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