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