• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2005, 2006, 2007, 2008, 2009 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 "config.h"
30 #include "DumpRenderTree.h"
31 
32 #include "EditingDelegate.h"
33 #include "FrameLoadDelegate.h"
34 #include "HistoryDelegate.h"
35 #include "LayoutTestController.h"
36 #include "PixelDumpSupport.h"
37 #include "PolicyDelegate.h"
38 #include "ResourceLoadDelegate.h"
39 #include "UIDelegate.h"
40 #include "WorkQueueItem.h"
41 #include "WorkQueue.h"
42 
43 #include <comutil.h>
44 #include <fcntl.h>
45 #include <io.h>
46 #include <math.h>
47 #include <pthread.h>
48 #include <shlwapi.h>
49 #include <stdio.h>
50 #include <string.h>
51 #include <tchar.h>
52 #include <wtf/RetainPtr.h>
53 #include <wtf/Vector.h>
54 #include <windows.h>
55 #include <CoreFoundation/CoreFoundation.h>
56 #include <JavaScriptCore/JavaScriptCore.h>
57 #include <WebKit/WebKit.h>
58 #include <WebKit/WebKitCOMAPI.h>
59 
60 #if USE(CFNETWORK)
61 #include <CFNetwork/CFURLCachePriv.h>
62 #endif
63 
64 #if USE(CFNETWORK)
65 #include <CFNetwork/CFHTTPCookiesPriv.h>
66 #endif
67 
68 using namespace std;
69 
70 #ifdef DEBUG_ALL
71 const LPWSTR TestPluginDir = L"TestNetscapePlugin_Debug";
72 #else
73 const LPWSTR TestPluginDir = L"TestNetscapePlugin";
74 #endif
75 
76 static LPCWSTR fontsEnvironmentVariable = L"WEBKIT_TESTFONTS";
77 #define USE_MAC_FONTS
78 
79 const LPCWSTR kDumpRenderTreeClassName = L"DumpRenderTreeWindow";
80 
81 static bool dumpTree = true;
82 static bool dumpPixels;
83 static bool dumpAllPixels;
84 static bool printSeparators;
85 static bool leakChecking = false;
86 static bool threaded = false;
87 static bool forceComplexText = false;
88 static bool printSupportedFeatures = false;
89 static RetainPtr<CFStringRef> persistentUserStyleSheetLocation;
90 
91 volatile bool done;
92 // This is the topmost frame that is loading, during a given load, or nil when no load is
93 // in progress.  Usually this is the same as the main frame, but not always.  In the case
94 // where a frameset is loaded, and then new content is loaded into one of the child frames,
95 // that child frame is the "topmost frame that is loading".
96 IWebFrame* topLoadingFrame;     // !nil iff a load is in progress
97 static COMPtr<IWebHistoryItem> prevTestBFItem;  // current b/f item at the end of the previous test
98 PolicyDelegate* policyDelegate;
99 COMPtr<FrameLoadDelegate> sharedFrameLoadDelegate;
100 COMPtr<UIDelegate> sharedUIDelegate;
101 COMPtr<EditingDelegate> sharedEditingDelegate;
102 COMPtr<HistoryDelegate> sharedHistoryDelegate;
103 
104 IWebFrame* frame;
105 HWND webViewWindow;
106 
107 RefPtr<LayoutTestController> gLayoutTestController;
108 
109 UINT_PTR waitToDumpWatchdog = 0;
110 
setPersistentUserStyleSheetLocation(CFStringRef url)111 void setPersistentUserStyleSheetLocation(CFStringRef url)
112 {
113     persistentUserStyleSheetLocation = url;
114 }
115 
setAlwaysAcceptCookies(bool alwaysAcceptCookies)116 bool setAlwaysAcceptCookies(bool alwaysAcceptCookies)
117 {
118 #if USE(CFNETWORK)
119     COMPtr<IWebCookieManager> cookieManager;
120     if (FAILED(WebKitCreateInstance(CLSID_WebCookieManager, 0, IID_IWebCookieManager, reinterpret_cast<void**>(&cookieManager))))
121         return false;
122     CFHTTPCookieStorageRef cookieStorage = 0;
123     if (FAILED(cookieManager->cookieStorage(&cookieStorage)) || !cookieStorage)
124         return false;
125 
126     WebKitCookieStorageAcceptPolicy cookieAcceptPolicy = alwaysAcceptCookies ? WebKitCookieStorageAcceptPolicyAlways : WebKitCookieStorageAcceptPolicyOnlyFromMainDocumentDomain;
127     CFHTTPCookieStorageSetCookieAcceptPolicy(cookieStorage, cookieAcceptPolicy);
128     return true;
129 #else
130     // FIXME: Implement!
131     return false;
132 #endif
133 }
134 
substringFromIndex(CFStringRef string,CFIndex index)135 static RetainPtr<CFStringRef> substringFromIndex(CFStringRef string, CFIndex index)
136 {
137     return RetainPtr<CFStringRef>(AdoptCF, CFStringCreateWithSubstring(kCFAllocatorDefault, string, CFRangeMake(index, CFStringGetLength(string) - index)));
138 }
139 
urlSuitableForTestResult(const wstring & urlString)140 wstring urlSuitableForTestResult(const wstring& urlString)
141 {
142     RetainPtr<CFURLRef> url(AdoptCF, CFURLCreateWithBytes(kCFAllocatorDefault, reinterpret_cast<const UInt8*>(urlString.c_str()), urlString.length() * sizeof(wstring::value_type), kCFStringEncodingUTF16, 0));
143 
144     RetainPtr<CFStringRef> scheme(AdoptCF, CFURLCopyScheme(url.get()));
145     if (scheme && CFStringCompare(scheme.get(), CFSTR("file"), kCFCompareCaseInsensitive) != kCFCompareEqualTo)
146         return urlString;
147 
148     COMPtr<IWebDataSource> dataSource;
149     if (FAILED(frame->dataSource(&dataSource))) {
150         if (FAILED(frame->provisionalDataSource(&dataSource)))
151             return urlString;
152     }
153 
154     COMPtr<IWebMutableURLRequest> request;
155     if (FAILED(dataSource->request(&request)))
156         return urlString;
157 
158     _bstr_t requestURLString;
159     if (FAILED(request->URL(requestURLString.GetAddress())))
160         return urlString;
161 
162     RetainPtr<CFURLRef> requestURL(AdoptCF, CFURLCreateWithBytes(kCFAllocatorDefault, reinterpret_cast<const UInt8*>(requestURLString.GetBSTR()), requestURLString.length() * sizeof(OLECHAR), kCFStringEncodingUTF16, 0));
163     RetainPtr<CFURLRef> baseURL(AdoptCF, CFURLCreateCopyDeletingLastPathComponent(kCFAllocatorDefault, requestURL.get()));
164 
165     RetainPtr<CFStringRef> basePath(AdoptCF, CFURLCopyPath(baseURL.get()));
166     RetainPtr<CFStringRef> path(AdoptCF, CFURLCopyPath(url.get()));
167 
168     return cfStringRefToWString(substringFromIndex(path.get(), CFStringGetLength(basePath.get())).get());
169 }
170 
lastPathComponent(const wstring & urlString)171 wstring lastPathComponent(const wstring& urlString)
172 {
173     if (urlString.empty())
174         return urlString;
175 
176     RetainPtr<CFURLRef> url(AdoptCF, CFURLCreateWithBytes(kCFAllocatorDefault, reinterpret_cast<const UInt8*>(urlString.c_str()), urlString.length() * sizeof(wstring::value_type), kCFStringEncodingUTF16, 0));
177     RetainPtr<CFStringRef> lastPathComponent(CFURLCopyLastPathComponent(url.get()));
178 
179     return cfStringRefToWString(lastPathComponent.get());
180 }
181 
toUTF8(const wchar_t * wideString,size_t length)182 static string toUTF8(const wchar_t* wideString, size_t length)
183 {
184     int result = WideCharToMultiByte(CP_UTF8, 0, wideString, length + 1, 0, 0, 0, 0);
185     Vector<char> utf8Vector(result);
186     result = WideCharToMultiByte(CP_UTF8, 0, wideString, length + 1, utf8Vector.data(), result, 0, 0);
187     if (!result)
188         return string();
189 
190     return string(utf8Vector.data(), utf8Vector.size() - 1);
191 }
192 
toUTF8(BSTR bstr)193 string toUTF8(BSTR bstr)
194 {
195     return toUTF8(bstr, SysStringLen(bstr));
196 }
197 
toUTF8(const wstring & wideString)198 string toUTF8(const wstring& wideString)
199 {
200     return toUTF8(wideString.c_str(), wideString.length());
201 }
202 
cfStringRefToWString(CFStringRef cfStr)203 wstring cfStringRefToWString(CFStringRef cfStr)
204 {
205     Vector<wchar_t> v(CFStringGetLength(cfStr));
206     CFStringGetCharacters(cfStr, CFRangeMake(0, CFStringGetLength(cfStr)), (UniChar *)v.data());
207 
208     return wstring(v.data(), v.size());
209 }
210 
DumpRenderTreeWndProc(HWND hWnd,UINT msg,WPARAM wParam,LPARAM lParam)211 static LRESULT CALLBACK DumpRenderTreeWndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
212 {
213     switch (msg) {
214         case WM_DESTROY:
215             for (unsigned i = openWindows().size() - 1; i >= 0; --i) {
216                 if (openWindows()[i] == hWnd) {
217                     openWindows().remove(i);
218                     windowToWebViewMap().remove(hWnd);
219                     break;
220                 }
221             }
222             return 0;
223             break;
224         default:
225             return DefWindowProc(hWnd, msg, wParam, lParam);
226     }
227 }
228 
exePath()229 static const wstring& exePath()
230 {
231     static wstring path;
232     static bool initialized;
233 
234     if (initialized)
235         return path;
236     initialized = true;
237 
238     TCHAR buffer[MAX_PATH];
239     GetModuleFileName(GetModuleHandle(0), buffer, ARRAYSIZE(buffer));
240     path = buffer;
241     int lastSlash = path.rfind('\\');
242     if (lastSlash != -1 && lastSlash + 1 < path.length())
243         path = path.substr(0, lastSlash + 1);
244 
245     return path;
246 }
247 
fontsPath()248 static const wstring& fontsPath()
249 {
250     static wstring path;
251     static bool initialized;
252 
253     if (initialized)
254         return path;
255     initialized = true;
256 
257     DWORD size = GetEnvironmentVariable(fontsEnvironmentVariable, 0, 0);
258     Vector<TCHAR> buffer(size);
259     if (GetEnvironmentVariable(fontsEnvironmentVariable, buffer.data(), buffer.size())) {
260         path = buffer.data();
261         if (path[path.length() - 1] != '\\')
262             path.append(L"\\");
263         return path;
264     }
265 
266     path = exePath() + TEXT("DumpRenderTree.resources\\");
267     return path;
268 }
269 
addQTDirToPATH()270 static void addQTDirToPATH()
271 {
272     static LPCWSTR pathEnvironmentVariable = L"PATH";
273     static LPCWSTR quickTimeKeyName = L"Software\\Apple Computer, Inc.\\QuickTime";
274     static LPCWSTR quickTimeSysDir = L"QTSysDir";
275     static bool initialized;
276 
277     if (initialized)
278         return;
279     initialized = true;
280 
281     // Get the QuickTime dll directory from the registry. The key can be in either HKLM or HKCU.
282     WCHAR qtPath[MAX_PATH];
283     DWORD qtPathBufferLen = sizeof(qtPath);
284     DWORD keyType;
285     HRESULT result = SHGetValue(HKEY_LOCAL_MACHINE, quickTimeKeyName, quickTimeSysDir, &keyType, (LPVOID)qtPath, &qtPathBufferLen);
286     if (result != ERROR_SUCCESS || !qtPathBufferLen || keyType != REG_SZ) {
287         qtPathBufferLen = sizeof(qtPath);
288         result = SHGetValue(HKEY_CURRENT_USER, quickTimeKeyName, quickTimeSysDir, &keyType, (LPVOID)qtPath, &qtPathBufferLen);
289         if (result != ERROR_SUCCESS || !qtPathBufferLen || keyType != REG_SZ)
290             return;
291     }
292 
293     // Read the current PATH.
294     DWORD pathSize = GetEnvironmentVariableW(pathEnvironmentVariable, 0, 0);
295     Vector<WCHAR> oldPath(pathSize);
296     if (!GetEnvironmentVariableW(pathEnvironmentVariable, oldPath.data(), oldPath.size()))
297         return;
298 
299     // And add the QuickTime dll.
300     wstring newPath;
301     newPath.append(qtPath);
302     newPath.append(L";");
303     newPath.append(oldPath.data(), oldPath.size());
304     SetEnvironmentVariableW(pathEnvironmentVariable, newPath.data());
305 }
306 
307 #ifdef DEBUG_ALL
308 #define WEBKITDLL TEXT("WebKit_debug.dll")
309 #else
310 #define WEBKITDLL TEXT("WebKit.dll")
311 #endif
312 
initialize()313 static void initialize()
314 {
315     if (HMODULE webKitModule = LoadLibrary(WEBKITDLL))
316         if (FARPROC dllRegisterServer = GetProcAddress(webKitModule, "DllRegisterServer"))
317             dllRegisterServer();
318 
319     // Init COM
320     OleInitialize(0);
321 
322     static LPCTSTR fontsToInstall[] = {
323         TEXT("AHEM____.ttf"),
324         TEXT("Apple Chancery.ttf"),
325         TEXT("Courier Bold.ttf"),
326         TEXT("Courier.ttf"),
327         TEXT("Helvetica Bold Oblique.ttf"),
328         TEXT("Helvetica Bold.ttf"),
329         TEXT("Helvetica Oblique.ttf"),
330         TEXT("Helvetica.ttf"),
331         TEXT("Helvetica Neue Bold Italic.ttf"),
332         TEXT("Helvetica Neue Bold.ttf"),
333         TEXT("Helvetica Neue Condensed Black.ttf"),
334         TEXT("Helvetica Neue Condensed Bold.ttf"),
335         TEXT("Helvetica Neue Italic.ttf"),
336         TEXT("Helvetica Neue Light Italic.ttf"),
337         TEXT("Helvetica Neue Light.ttf"),
338         TEXT("Helvetica Neue UltraLight Italic.ttf"),
339         TEXT("Helvetica Neue UltraLight.ttf"),
340         TEXT("Helvetica Neue.ttf"),
341         TEXT("Lucida Grande.ttf"),
342         TEXT("Lucida Grande Bold.ttf"),
343         TEXT("Monaco.ttf"),
344         TEXT("Papyrus.ttf"),
345         TEXT("Times Bold Italic.ttf"),
346         TEXT("Times Bold.ttf"),
347         TEXT("Times Italic.ttf"),
348         TEXT("Times Roman.ttf"),
349         TEXT("WebKit Layout Tests 2.ttf"),
350         TEXT("WebKit Layout Tests.ttf"),
351         TEXT("WebKitWeightWatcher100.ttf"),
352         TEXT("WebKitWeightWatcher200.ttf"),
353         TEXT("WebKitWeightWatcher300.ttf"),
354         TEXT("WebKitWeightWatcher400.ttf"),
355         TEXT("WebKitWeightWatcher500.ttf"),
356         TEXT("WebKitWeightWatcher600.ttf"),
357         TEXT("WebKitWeightWatcher700.ttf"),
358         TEXT("WebKitWeightWatcher800.ttf"),
359         TEXT("WebKitWeightWatcher900.ttf")
360     };
361 
362     wstring resourcesPath = fontsPath();
363 
364     COMPtr<IWebTextRenderer> textRenderer;
365     if (SUCCEEDED(WebKitCreateInstance(CLSID_WebTextRenderer, 0, IID_IWebTextRenderer, (void**)&textRenderer)))
366         for (int i = 0; i < ARRAYSIZE(fontsToInstall); ++i)
367             textRenderer->registerPrivateFont(wstring(resourcesPath + fontsToInstall[i]).c_str());
368 
369     // Add the QuickTime dll directory to PATH or QT 7.6 will fail to initialize on systems
370     // linked with older versions of qtmlclientlib.dll.
371     addQTDirToPATH();
372 
373     // Register a host window
374     WNDCLASSEX wcex;
375 
376     wcex.cbSize = sizeof(WNDCLASSEX);
377 
378     wcex.style         = CS_HREDRAW | CS_VREDRAW;
379     wcex.lpfnWndProc   = DumpRenderTreeWndProc;
380     wcex.cbClsExtra    = 0;
381     wcex.cbWndExtra    = 0;
382     wcex.hInstance     = GetModuleHandle(0);
383     wcex.hIcon         = 0;
384     wcex.hCursor       = LoadCursor(0, IDC_ARROW);
385     wcex.hbrBackground = 0;
386     wcex.lpszMenuName  = 0;
387     wcex.lpszClassName = kDumpRenderTreeClassName;
388     wcex.hIconSm       = 0;
389 
390     RegisterClassEx(&wcex);
391 }
392 
displayWebView()393 void displayWebView()
394 {
395     ::InvalidateRect(webViewWindow, 0, TRUE);
396     ::SendMessage(webViewWindow, WM_PAINT, 0, 0);
397 }
398 
dumpFrameScrollPosition(IWebFrame * frame)399 void dumpFrameScrollPosition(IWebFrame* frame)
400 {
401     if (!frame)
402         return;
403 
404     COMPtr<IWebFramePrivate> framePrivate;
405     if (FAILED(frame->QueryInterface(&framePrivate)))
406         return;
407 
408     SIZE scrollPosition;
409     if (FAILED(framePrivate->scrollOffset(&scrollPosition)))
410         return;
411 
412     if (abs(scrollPosition.cx) > 0.00000001 || abs(scrollPosition.cy) > 0.00000001) {
413         COMPtr<IWebFrame> parent;
414         if (FAILED(frame->parentFrame(&parent)))
415             return;
416         if (parent) {
417             BSTR name;
418             if (FAILED(frame->name(&name)))
419                 return;
420             printf("frame '%S' ", name ? name : L"");
421             SysFreeString(name);
422         }
423         printf("scrolled to %.f,%.f\n", (double)scrollPosition.cx, (double)scrollPosition.cy);
424     }
425 
426     if (::gLayoutTestController->dumpChildFrameScrollPositions()) {
427         COMPtr<IEnumVARIANT> enumKids;
428         if (FAILED(frame->childFrames(&enumKids)))
429             return;
430         VARIANT var;
431         VariantInit(&var);
432         while (enumKids->Next(1, &var, 0) == S_OK) {
433             ASSERT(V_VT(&var) == VT_UNKNOWN);
434             COMPtr<IWebFrame> framePtr;
435             V_UNKNOWN(&var)->QueryInterface(IID_IWebFrame, (void**)&framePtr);
436             dumpFrameScrollPosition(framePtr.get());
437             VariantClear(&var);
438         }
439     }
440 }
441 
dumpFramesAsText(IWebFrame * frame)442 static wstring dumpFramesAsText(IWebFrame* frame)
443 {
444     if (!frame)
445         return L"";
446 
447     COMPtr<IDOMDocument> document;
448     if (FAILED(frame->DOMDocument(&document)))
449         return L"";
450 
451     COMPtr<IDOMElement> documentElement;
452     if (FAILED(document->documentElement(&documentElement)))
453         return L"";
454 
455     wstring result;
456 
457     // Add header for all but the main frame.
458     COMPtr<IWebFrame> parent;
459     if (FAILED(frame->parentFrame(&parent)))
460         return L"";
461     if (parent) {
462         BSTR name = L"";
463         if (FAILED(frame->name(&name)))
464             return L"";
465 
466         result.append(L"\n--------\nFrame: '");
467         result.append(name ? name : L"", SysStringLen(name));
468         result.append(L"'\n--------\n");
469 
470         SysFreeString(name);
471     }
472 
473     BSTR innerText = 0;
474     COMPtr<IDOMElementPrivate> docPrivate;
475     if (SUCCEEDED(documentElement->QueryInterface(&docPrivate)))
476         docPrivate->innerText(&innerText);
477 
478     result.append(innerText ? innerText : L"", SysStringLen(innerText));
479     result.append(L"\n");
480 
481     SysFreeString(innerText);
482 
483     if (::gLayoutTestController->dumpChildFramesAsText()) {
484         COMPtr<IEnumVARIANT> enumKids;
485         if (FAILED(frame->childFrames(&enumKids)))
486             return L"";
487         VARIANT var;
488         VariantInit(&var);
489         while (enumKids->Next(1, &var, 0) == S_OK) {
490             ASSERT(V_VT(&var) == VT_UNKNOWN);
491             COMPtr<IWebFrame> framePtr;
492             V_UNKNOWN(&var)->QueryInterface(IID_IWebFrame, (void**)&framePtr);
493             result.append(dumpFramesAsText(framePtr.get()));
494             VariantClear(&var);
495         }
496     }
497 
498     return result;
499 }
500 
compareHistoryItems(const void * item1,const void * item2)501 static int compareHistoryItems(const void* item1, const void* item2)
502 {
503     COMPtr<IWebHistoryItemPrivate> itemA;
504     if (FAILED((*(COMPtr<IUnknown>*)item1)->QueryInterface(&itemA)))
505         return 0;
506 
507     COMPtr<IWebHistoryItemPrivate> itemB;
508     if (FAILED((*(COMPtr<IUnknown>*)item2)->QueryInterface(&itemB)))
509         return 0;
510 
511     BSTR targetA;
512     if (FAILED(itemA->target(&targetA)))
513         return 0;
514 
515     BSTR targetB;
516     if (FAILED(itemB->target(&targetB))) {
517         SysFreeString(targetA);
518         return 0;
519     }
520 
521     int result = wcsicmp(wstring(targetA, SysStringLen(targetA)).c_str(), wstring(targetB, SysStringLen(targetB)).c_str());
522     SysFreeString(targetA);
523     SysFreeString(targetB);
524     return result;
525 }
526 
dumpHistoryItem(IWebHistoryItem * item,int indent,bool current)527 static void dumpHistoryItem(IWebHistoryItem* item, int indent, bool current)
528 {
529     assert(item);
530 
531     int start = 0;
532     if (current) {
533         printf("curr->");
534         start = 6;
535     }
536     for (int i = start; i < indent; i++)
537         putchar(' ');
538 
539     BSTR url;
540     if (FAILED(item->URLString(&url)))
541         return;
542 
543     if (wcsstr(url, L"file:/") == url) {
544         static wchar_t* layoutTestsString = L"/LayoutTests/";
545         static wchar_t* fileTestString = L"(file test):";
546 
547         wchar_t* result = wcsstr(url, layoutTestsString);
548         if (result == NULL)
549             return;
550         wchar_t* start = result + wcslen(layoutTestsString);
551 
552         BSTR newURL = SysAllocStringLen(NULL, SysStringLen(url));
553         wcscpy(newURL, fileTestString);
554         wcscpy(newURL + wcslen(fileTestString), start);
555 
556         SysFreeString(url);
557         url = newURL;
558     }
559 
560     printf("%S", url ? url : L"");
561     SysFreeString(url);
562 
563     COMPtr<IWebHistoryItemPrivate> itemPrivate;
564     if (FAILED(item->QueryInterface(&itemPrivate)))
565         return;
566 
567     BSTR target;
568     if (FAILED(itemPrivate->target(&target)))
569         return;
570     if (SysStringLen(target))
571         printf(" (in frame \"%S\")", target);
572     SysFreeString(target);
573     BOOL isTargetItem = FALSE;
574     if (FAILED(itemPrivate->isTargetItem(&isTargetItem)))
575         return;
576     if (isTargetItem)
577         printf("  **nav target**");
578     putchar('\n');
579 
580     unsigned kidsCount;
581     SAFEARRAY* arrPtr;
582     if (FAILED(itemPrivate->children(&kidsCount, &arrPtr)) || !kidsCount)
583         return;
584 
585     Vector<COMPtr<IUnknown> > kidsVector;
586 
587     LONG lowerBound;
588     if (FAILED(::SafeArrayGetLBound(arrPtr, 1, &lowerBound)))
589         goto exit;
590 
591     LONG upperBound;
592     if (FAILED(::SafeArrayGetUBound(arrPtr, 1, &upperBound)))
593         goto exit;
594 
595     LONG length = upperBound - lowerBound + 1;
596     if (!length)
597         goto exit;
598     ASSERT(length == kidsCount);
599 
600     IUnknown** safeArrayData;
601     if (FAILED(::SafeArrayAccessData(arrPtr, (void**)&safeArrayData)))
602         goto exit;
603 
604     for (int i = 0; i < length; ++i)
605         kidsVector.append(safeArrayData[i]);
606     ::SafeArrayUnaccessData(arrPtr);
607 
608     // must sort to eliminate arbitrary result ordering which defeats reproducible testing
609     qsort(kidsVector.data(), kidsCount, sizeof(kidsVector[0]), compareHistoryItems);
610 
611     for (unsigned i = 0; i < kidsCount; ++i) {
612         COMPtr<IWebHistoryItem> item;
613         kidsVector[i]->QueryInterface(&item);
614         dumpHistoryItem(item.get(), indent + 4, false);
615     }
616 
617 exit:
618     if (arrPtr && SUCCEEDED(::SafeArrayUnlock(arrPtr)))
619         ::SafeArrayDestroy(arrPtr);
620 }
621 
dumpBackForwardList(IWebView * webView)622 static void dumpBackForwardList(IWebView* webView)
623 {
624     ASSERT(webView);
625 
626     printf("\n============== Back Forward List ==============\n");
627 
628     COMPtr<IWebBackForwardList> bfList;
629     if (FAILED(webView->backForwardList(&bfList)))
630         return;
631 
632     // Print out all items in the list after prevTestBFItem, which was from the previous test
633     // Gather items from the end of the list, the print them out from oldest to newest
634 
635     Vector<COMPtr<IUnknown> > itemsToPrint;
636 
637     int forwardListCount;
638     if (FAILED(bfList->forwardListCount(&forwardListCount)))
639         return;
640 
641     for (int i = forwardListCount; i > 0; --i) {
642         COMPtr<IWebHistoryItem> item;
643         if (FAILED(bfList->itemAtIndex(i, &item)))
644             return;
645         // something is wrong if the item from the last test is in the forward part of the b/f list
646         assert(item != prevTestBFItem);
647         COMPtr<IUnknown> itemUnknown;
648         item->QueryInterface(&itemUnknown);
649         itemsToPrint.append(itemUnknown);
650     }
651 
652     COMPtr<IWebHistoryItem> currentItem;
653     if (FAILED(bfList->currentItem(&currentItem)))
654         return;
655 
656     assert(currentItem != prevTestBFItem);
657     COMPtr<IUnknown> currentItemUnknown;
658     currentItem->QueryInterface(&currentItemUnknown);
659     itemsToPrint.append(currentItemUnknown);
660     int currentItemIndex = itemsToPrint.size() - 1;
661 
662     int backListCount;
663     if (FAILED(bfList->backListCount(&backListCount)))
664         return;
665 
666     for (int i = -1; i >= -backListCount; --i) {
667         COMPtr<IWebHistoryItem> item;
668         if (FAILED(bfList->itemAtIndex(i, &item)))
669             return;
670         if (item == prevTestBFItem)
671             break;
672         COMPtr<IUnknown> itemUnknown;
673         item->QueryInterface(&itemUnknown);
674         itemsToPrint.append(itemUnknown);
675     }
676 
677     for (int i = itemsToPrint.size() - 1; i >= 0; --i) {
678         COMPtr<IWebHistoryItem> historyItemToPrint;
679         itemsToPrint[i]->QueryInterface(&historyItemToPrint);
680         dumpHistoryItem(historyItemToPrint.get(), 8, i == currentItemIndex);
681     }
682 
683     printf("===============================================\n");
684 }
685 
dumpBackForwardListForAllWindows()686 static void dumpBackForwardListForAllWindows()
687 {
688     unsigned count = openWindows().size();
689     for (unsigned i = 0; i < count; i++) {
690         HWND window = openWindows()[i];
691         IWebView* webView = windowToWebViewMap().get(window).get();
692         dumpBackForwardList(webView);
693     }
694 }
695 
invalidateAnyPreviousWaitToDumpWatchdog()696 static void invalidateAnyPreviousWaitToDumpWatchdog()
697 {
698     if (!waitToDumpWatchdog)
699         return;
700 
701     KillTimer(0, waitToDumpWatchdog);
702     waitToDumpWatchdog = 0;
703 }
704 
dump()705 void dump()
706 {
707     invalidateAnyPreviousWaitToDumpWatchdog();
708 
709     COMPtr<IWebDataSource> dataSource;
710     if (SUCCEEDED(frame->dataSource(&dataSource))) {
711         COMPtr<IWebURLResponse> response;
712         if (SUCCEEDED(dataSource->response(&response)) && response) {
713             BSTR mimeType;
714             if (SUCCEEDED(response->MIMEType(&mimeType)) && !_tcscmp(mimeType, TEXT("text/plain"))) {
715                 ::gLayoutTestController->setDumpAsText(true);
716                 ::gLayoutTestController->setGeneratePixelResults(false);
717             }
718             SysFreeString(mimeType);
719         }
720     }
721 
722     BSTR resultString = 0;
723 
724     if (dumpTree) {
725         if (::gLayoutTestController->dumpAsText()) {
726             ::InvalidateRect(webViewWindow, 0, TRUE);
727             ::SendMessage(webViewWindow, WM_PAINT, 0, 0);
728             wstring result = dumpFramesAsText(frame);
729             resultString = SysAllocStringLen(result.data(), result.size());
730         } else {
731             bool isSVGW3CTest = (gLayoutTestController->testPathOrURL().find("svg\\W3C-SVG-1.1") != string::npos);
732             unsigned width;
733             unsigned height;
734             if (isSVGW3CTest) {
735                 width = 480;
736                 height = 360;
737             } else {
738                 width = LayoutTestController::maxViewWidth;
739                 height = LayoutTestController::maxViewHeight;
740             }
741 
742             ::SetWindowPos(webViewWindow, 0, 0, 0, width, height, SWP_NOMOVE);
743             ::InvalidateRect(webViewWindow, 0, TRUE);
744             ::SendMessage(webViewWindow, WM_PAINT, 0, 0);
745 
746             COMPtr<IWebFramePrivate> framePrivate;
747             if (FAILED(frame->QueryInterface(&framePrivate)))
748                 goto fail;
749             framePrivate->renderTreeAsExternalRepresentation(gLayoutTestController->isPrinting(), &resultString);
750         }
751 
752         if (!resultString)
753             printf("ERROR: nil result from %s", ::gLayoutTestController->dumpAsText() ? "IDOMElement::innerText" : "IFrameViewPrivate::renderTreeAsExternalRepresentation");
754         else {
755             unsigned stringLength = SysStringLen(resultString);
756             int bufferSize = ::WideCharToMultiByte(CP_UTF8, 0, resultString, stringLength, 0, 0, 0, 0);
757             char* buffer = (char*)malloc(bufferSize + 1);
758             ::WideCharToMultiByte(CP_UTF8, 0, resultString, stringLength, buffer, bufferSize + 1, 0, 0);
759             fwrite(buffer, 1, bufferSize, stdout);
760             free(buffer);
761             if (!::gLayoutTestController->dumpAsText())
762                 dumpFrameScrollPosition(frame);
763         }
764         if (::gLayoutTestController->dumpBackForwardList())
765             dumpBackForwardListForAllWindows();
766     }
767 
768     if (printSeparators) {
769         puts("#EOF");   // terminate the content block
770         fputs("#EOF\n", stderr);
771         fflush(stdout);
772         fflush(stderr);
773     }
774 
775     if (dumpPixels
776      && gLayoutTestController->generatePixelResults()
777      && !gLayoutTestController->dumpDOMAsWebArchive()
778      && !gLayoutTestController->dumpSourceAsWebArchive())
779         dumpWebViewAsPixelsAndCompareWithExpected(gLayoutTestController->expectedPixelHash());
780 
781     printf("#EOF\n");   // terminate the (possibly empty) pixels block
782     fflush(stdout);
783 
784 fail:
785     SysFreeString(resultString);
786     // This will exit from our message loop.
787     PostQuitMessage(0);
788     done = true;
789 }
790 
shouldLogFrameLoadDelegates(const char * pathOrURL)791 static bool shouldLogFrameLoadDelegates(const char* pathOrURL)
792 {
793     return strstr(pathOrURL, "/loading/") || strstr(pathOrURL, "\\loading\\");
794 }
795 
shouldLogHistoryDelegates(const char * pathOrURL)796 static bool shouldLogHistoryDelegates(const char* pathOrURL)
797 {
798     return strstr(pathOrURL, "/globalhistory/") || strstr(pathOrURL, "\\globalhistory\\");
799 }
800 
shouldOpenWebInspector(const char * pathOrURL)801 static bool shouldOpenWebInspector(const char* pathOrURL)
802 {
803     return strstr(pathOrURL, "/inspector/") || strstr(pathOrURL, "\\inspector\\");
804 }
805 
shouldDumpAsText(const char * pathOrURL)806 static bool shouldDumpAsText(const char* pathOrURL)
807 {
808     return strstr(pathOrURL, "/dumpAsText/") || strstr(pathOrURL, "\\dumpAsText\\");
809 }
810 
shouldEnableDeveloperExtras(const char * pathOrURL)811 static bool shouldEnableDeveloperExtras(const char* pathOrURL)
812 {
813     return true;
814 }
815 
resetDefaultsToConsistentValues(IWebPreferences * preferences)816 static void resetDefaultsToConsistentValues(IWebPreferences* preferences)
817 {
818 #ifdef USE_MAC_FONTS
819     static BSTR standardFamily = SysAllocString(TEXT("Times"));
820     static BSTR fixedFamily = SysAllocString(TEXT("Courier"));
821     static BSTR sansSerifFamily = SysAllocString(TEXT("Helvetica"));
822     static BSTR cursiveFamily = SysAllocString(TEXT("Apple Chancery"));
823     static BSTR fantasyFamily = SysAllocString(TEXT("Papyrus"));
824 #else
825     static BSTR standardFamily = SysAllocString(TEXT("Times New Roman"));
826     static BSTR fixedFamily = SysAllocString(TEXT("Courier New"));
827     static BSTR sansSerifFamily = SysAllocString(TEXT("Arial"));
828     static BSTR cursiveFamily = SysAllocString(TEXT("Comic Sans MS")); // Not actually cursive, but it's what IE and Firefox use.
829     static BSTR fantasyFamily = SysAllocString(TEXT("Times New Roman"));
830 #endif
831 
832     preferences->setStandardFontFamily(standardFamily);
833     preferences->setFixedFontFamily(fixedFamily);
834     preferences->setSerifFontFamily(standardFamily);
835     preferences->setSansSerifFontFamily(sansSerifFamily);
836     preferences->setCursiveFontFamily(cursiveFamily);
837     preferences->setFantasyFontFamily(fantasyFamily);
838 
839     preferences->setAutosaves(FALSE);
840     preferences->setDefaultFontSize(16);
841     preferences->setDefaultFixedFontSize(13);
842     preferences->setMinimumFontSize(0);
843     preferences->setJavaEnabled(FALSE);
844     preferences->setPlugInsEnabled(TRUE);
845     preferences->setDOMPasteAllowed(TRUE);
846     preferences->setEditableLinkBehavior(WebKitEditableLinkOnlyLiveWithShiftKey);
847     preferences->setFontSmoothing(FontSmoothingTypeStandard);
848     preferences->setUsesPageCache(FALSE);
849     preferences->setPrivateBrowsingEnabled(FALSE);
850     preferences->setJavaScriptCanOpenWindowsAutomatically(TRUE);
851     preferences->setJavaScriptEnabled(TRUE);
852     preferences->setTabsToLinks(FALSE);
853     preferences->setShouldPrintBackgrounds(TRUE);
854     preferences->setLoadsImagesAutomatically(TRUE);
855     preferences->setEditingBehavior(WebKitEditingWinBehavior);
856 
857     if (persistentUserStyleSheetLocation) {
858         Vector<wchar_t> urlCharacters(CFStringGetLength(persistentUserStyleSheetLocation.get()));
859         CFStringGetCharacters(persistentUserStyleSheetLocation.get(), CFRangeMake(0, CFStringGetLength(persistentUserStyleSheetLocation.get())), (UniChar *)urlCharacters.data());
860         BSTR url = SysAllocStringLen(urlCharacters.data(), urlCharacters.size());
861         preferences->setUserStyleSheetLocation(url);
862         SysFreeString(url);
863         preferences->setUserStyleSheetEnabled(TRUE);
864     } else
865         preferences->setUserStyleSheetEnabled(FALSE);
866 
867     COMPtr<IWebPreferencesPrivate> prefsPrivate(Query, preferences);
868     if (prefsPrivate) {
869         prefsPrivate->setAllowUniversalAccessFromFileURLs(TRUE);
870         prefsPrivate->setAllowFileAccessFromFileURLs(TRUE);
871         prefsPrivate->setAuthorAndUserStylesEnabled(TRUE);
872         prefsPrivate->setDeveloperExtrasEnabled(FALSE);
873         prefsPrivate->setExperimentalNotificationsEnabled(TRUE);
874         prefsPrivate->setShouldPaintNativeControls(FALSE); // FIXME - need to make DRT pass with Windows native controls <http://bugs.webkit.org/show_bug.cgi?id=25592>
875         prefsPrivate->setJavaScriptCanAccessClipboard(TRUE);
876         prefsPrivate->setXSSAuditorEnabled(FALSE);
877         prefsPrivate->setFrameFlatteningEnabled(FALSE);
878         prefsPrivate->setOfflineWebApplicationCacheEnabled(TRUE);
879         prefsPrivate->setLoadsSiteIconsIgnoringImageLoadingPreference(FALSE);
880     }
881     setAlwaysAcceptCookies(false);
882 
883     setlocale(LC_ALL, "");
884 }
885 
resetWebViewToConsistentStateBeforeTesting()886 static void resetWebViewToConsistentStateBeforeTesting()
887 {
888     COMPtr<IWebView> webView;
889     if (FAILED(frame->webView(&webView)))
890         return;
891 
892     webView->setPolicyDelegate(0);
893     policyDelegate->setPermissive(false);
894     policyDelegate->setControllerToNotifyDone(0);
895 
896     COMPtr<IWebIBActions> webIBActions(Query, webView);
897     if (webIBActions) {
898         webIBActions->makeTextStandardSize(0);
899         webIBActions->resetPageZoom(0);
900     }
901 
902 
903     COMPtr<IWebPreferences> preferences;
904     if (SUCCEEDED(webView->preferences(&preferences)))
905         resetDefaultsToConsistentValues(preferences.get());
906 
907     COMPtr<IWebViewEditing> viewEditing;
908     if (SUCCEEDED(webView->QueryInterface(&viewEditing)))
909         viewEditing->setSmartInsertDeleteEnabled(TRUE);
910 
911     COMPtr<IWebViewPrivate> webViewPrivate(Query, webView);
912     if (!webViewPrivate)
913         return;
914 
915     double minimumInterval = 0;
916     if (SUCCEEDED(webViewPrivate->defaultMinimumTimerInterval(&minimumInterval)))
917         webViewPrivate->setMinimumTimerInterval(minimumInterval);
918 
919     COMPtr<IWebInspector> inspector;
920     if (SUCCEEDED(webViewPrivate->inspector(&inspector)))
921         inspector->setJavaScriptProfilingEnabled(FALSE);
922 
923     HWND viewWindow;
924     if (SUCCEEDED(webViewPrivate->viewWindow(reinterpret_cast<OLE_HANDLE*>(&viewWindow))) && viewWindow)
925         SetFocus(viewWindow);
926 
927     webViewPrivate->clearMainFrameName();
928     webViewPrivate->resetOriginAccessWhitelists();
929 
930     BSTR groupName;
931     if (SUCCEEDED(webView->groupName(&groupName))) {
932         webViewPrivate->removeAllUserContentFromGroup(groupName);
933         SysFreeString(groupName);
934     }
935 
936     sharedUIDelegate->resetUndoManager();
937 
938     sharedFrameLoadDelegate->resetToConsistentState();
939 
940     COMPtr<IWebFramePrivate> framePrivate;
941     if (SUCCEEDED(frame->QueryInterface(&framePrivate)))
942         framePrivate->clearOpener();
943 }
944 
runTest(const string & testPathOrURL)945 static void runTest(const string& testPathOrURL)
946 {
947     static BSTR methodBStr = SysAllocString(TEXT("GET"));
948 
949     // Look for "'" as a separator between the path or URL, and the pixel dump hash that follows.
950     string pathOrURL(testPathOrURL);
951     string expectedPixelHash;
952 
953     size_t separatorPos = pathOrURL.find("'");
954     if (separatorPos != string::npos) {
955         pathOrURL = string(testPathOrURL, 0, separatorPos);
956         expectedPixelHash = string(testPathOrURL, separatorPos + 1);
957     }
958 
959     BSTR urlBStr;
960 
961     CFStringRef str = CFStringCreateWithCString(0, pathOrURL.c_str(), kCFStringEncodingWindowsLatin1);
962     CFURLRef url = CFURLCreateWithString(0, str, 0);
963 
964     if (!url)
965         url = CFURLCreateWithFileSystemPath(0, str, kCFURLWindowsPathStyle, false);
966 
967     CFRelease(str);
968 
969     str = CFURLGetString(url);
970 
971     CFIndex length = CFStringGetLength(str);
972     UniChar* buffer = new UniChar[length];
973 
974     CFStringGetCharacters(str, CFRangeMake(0, length), buffer);
975     urlBStr = SysAllocStringLen((OLECHAR*)buffer, length);
976     delete[] buffer;
977 
978     CFRelease(url);
979 
980     ::gLayoutTestController = LayoutTestController::create(pathOrURL, expectedPixelHash);
981     done = false;
982     topLoadingFrame = 0;
983 
984     gLayoutTestController->setIconDatabaseEnabled(false);
985 
986     if (shouldLogFrameLoadDelegates(pathOrURL.c_str()))
987         gLayoutTestController->setDumpFrameLoadCallbacks(true);
988 
989     COMPtr<IWebView> webView;
990     if (SUCCEEDED(frame->webView(&webView))) {
991         COMPtr<IWebViewPrivate> viewPrivate;
992         if (SUCCEEDED(webView->QueryInterface(&viewPrivate))) {
993             if (shouldLogHistoryDelegates(pathOrURL.c_str())) {
994                 gLayoutTestController->setDumpHistoryDelegateCallbacks(true);
995                 viewPrivate->setHistoryDelegate(sharedHistoryDelegate.get());
996             } else
997                 viewPrivate->setHistoryDelegate(0);
998         }
999     }
1000     COMPtr<IWebHistory> history;
1001     if (SUCCEEDED(WebKitCreateInstance(CLSID_WebHistory, 0, __uuidof(history), reinterpret_cast<void**>(&history))))
1002         history->setOptionalSharedHistory(0);
1003 
1004     resetWebViewToConsistentStateBeforeTesting();
1005 
1006     if (shouldEnableDeveloperExtras(pathOrURL.c_str())) {
1007         gLayoutTestController->setDeveloperExtrasEnabled(true);
1008         if (shouldOpenWebInspector(pathOrURL.c_str()))
1009             gLayoutTestController->showWebInspector();
1010     }
1011     if (shouldDumpAsText(pathOrURL.c_str())) {
1012         gLayoutTestController->setDumpAsText(true);
1013         gLayoutTestController->setGeneratePixelResults(false);
1014     }
1015 
1016     prevTestBFItem = 0;
1017     if (webView) {
1018         COMPtr<IWebBackForwardList> bfList;
1019         if (SUCCEEDED(webView->backForwardList(&bfList)))
1020             bfList->currentItem(&prevTestBFItem);
1021     }
1022 
1023     WorkQueue::shared()->clear();
1024     WorkQueue::shared()->setFrozen(false);
1025 
1026     HWND hostWindow;
1027     webView->hostWindow(reinterpret_cast<OLE_HANDLE*>(&hostWindow));
1028 
1029     COMPtr<IWebMutableURLRequest> request;
1030     HRESULT hr = WebKitCreateInstance(CLSID_WebMutableURLRequest, 0, IID_IWebMutableURLRequest, (void**)&request);
1031     if (FAILED(hr))
1032         goto exit;
1033 
1034     request->initWithURL(urlBStr, WebURLRequestUseProtocolCachePolicy, 60);
1035 
1036     request->setHTTPMethod(methodBStr);
1037     frame->loadRequest(request.get());
1038 
1039     MSG msg;
1040     while (GetMessage(&msg, 0, 0, 0)) {
1041         // We get spurious WM_MOUSELEAVE events which make event handling machinery think that mouse button
1042         // is released during dragging (see e.g. fast\dynamic\layer-hit-test-crash.html).
1043         // Mouse can never leave WebView during normal DumpRenderTree operation, so we just ignore all such events.
1044         if (msg.message == WM_MOUSELEAVE)
1045             continue;
1046         TranslateMessage(&msg);
1047         DispatchMessage(&msg);
1048     }
1049 
1050     if (shouldEnableDeveloperExtras(pathOrURL.c_str())) {
1051         gLayoutTestController->closeWebInspector();
1052         gLayoutTestController->setDeveloperExtrasEnabled(false);
1053     }
1054 
1055     resetWebViewToConsistentStateBeforeTesting();
1056 
1057     frame->stopLoading();
1058 
1059     if (::gLayoutTestController->closeRemainingWindowsWhenComplete()) {
1060         Vector<HWND> windows = openWindows();
1061         unsigned size = windows.size();
1062         for (unsigned i = 0; i < size; i++) {
1063             HWND window = windows[i];
1064 
1065             // Don't try to close the main window
1066             if (window == hostWindow)
1067                 continue;
1068 
1069             DestroyWindow(window);
1070         }
1071     }
1072 
1073 exit:
1074     SysFreeString(urlBStr);
1075     ::gLayoutTestController.clear();
1076 
1077     return;
1078 }
1079 
pthreadEqualCallback(const void * value1,const void * value2)1080 static Boolean pthreadEqualCallback(const void* value1, const void* value2)
1081 {
1082     return (Boolean)pthread_equal(*(pthread_t*)value1, *(pthread_t*)value2);
1083 }
1084 
1085 static CFDictionaryKeyCallBacks pthreadKeyCallbacks = { 0, 0, 0, 0, pthreadEqualCallback, 0 };
1086 
1087 static pthread_mutex_t javaScriptThreadsMutex = PTHREAD_MUTEX_INITIALIZER;
1088 static bool javaScriptThreadsShouldTerminate;
1089 
1090 static const int javaScriptThreadsCount = 4;
javaScriptThreads()1091 static CFMutableDictionaryRef javaScriptThreads()
1092 {
1093     assert(pthread_mutex_trylock(&javaScriptThreadsMutex) == EBUSY);
1094     static CFMutableDictionaryRef staticJavaScriptThreads;
1095     if (!staticJavaScriptThreads)
1096         staticJavaScriptThreads = CFDictionaryCreateMutable(0, 0, &pthreadKeyCallbacks, 0);
1097     return staticJavaScriptThreads;
1098 }
1099 
1100 // Loops forever, running a script and randomly respawning, until
1101 // javaScriptThreadsShouldTerminate becomes true.
runJavaScriptThread(void * arg)1102 void* runJavaScriptThread(void* arg)
1103 {
1104     const char* const script =
1105     " \
1106     var array = []; \
1107     for (var i = 0; i < 10; i++) { \
1108         array.push(String(i)); \
1109     } \
1110     ";
1111 
1112     while (true) {
1113         JSGlobalContextRef ctx = JSGlobalContextCreate(0);
1114         JSStringRef scriptRef = JSStringCreateWithUTF8CString(script);
1115 
1116         JSValueRef exception = 0;
1117         JSEvaluateScript(ctx, scriptRef, 0, 0, 1, &exception);
1118         assert(!exception);
1119 
1120         JSGlobalContextRelease(ctx);
1121         JSStringRelease(scriptRef);
1122 
1123         JSGarbageCollect(ctx);
1124 
1125         pthread_mutex_lock(&javaScriptThreadsMutex);
1126 
1127         // Check for cancellation.
1128         if (javaScriptThreadsShouldTerminate) {
1129             pthread_mutex_unlock(&javaScriptThreadsMutex);
1130             return 0;
1131         }
1132 
1133         // Respawn probabilistically.
1134         if (rand() % 5 == 0) {
1135             pthread_t pthread;
1136             pthread_create(&pthread, 0, &runJavaScriptThread, 0);
1137             pthread_detach(pthread);
1138 
1139             pthread_t self = pthread_self();
1140             CFDictionaryRemoveValue(javaScriptThreads(), self.p);
1141             CFDictionaryAddValue(javaScriptThreads(), pthread.p, 0);
1142 
1143             pthread_mutex_unlock(&javaScriptThreadsMutex);
1144             return 0;
1145         }
1146 
1147         pthread_mutex_unlock(&javaScriptThreadsMutex);
1148     }
1149 }
1150 
startJavaScriptThreads(void)1151 static void startJavaScriptThreads(void)
1152 {
1153     pthread_mutex_lock(&javaScriptThreadsMutex);
1154 
1155     for (int i = 0; i < javaScriptThreadsCount; i++) {
1156         pthread_t pthread;
1157         pthread_create(&pthread, 0, &runJavaScriptThread, 0);
1158         pthread_detach(pthread);
1159         CFDictionaryAddValue(javaScriptThreads(), pthread.p, 0);
1160     }
1161 
1162     pthread_mutex_unlock(&javaScriptThreadsMutex);
1163 }
1164 
stopJavaScriptThreads(void)1165 static void stopJavaScriptThreads(void)
1166 {
1167     pthread_mutex_lock(&javaScriptThreadsMutex);
1168 
1169     javaScriptThreadsShouldTerminate = true;
1170 
1171     pthread_t* pthreads[javaScriptThreadsCount] = {0};
1172     int threadDictCount = CFDictionaryGetCount(javaScriptThreads());
1173     assert(threadDictCount == javaScriptThreadsCount);
1174     CFDictionaryGetKeysAndValues(javaScriptThreads(), (const void**)pthreads, 0);
1175 
1176     pthread_mutex_unlock(&javaScriptThreadsMutex);
1177 
1178     for (int i = 0; i < javaScriptThreadsCount; i++) {
1179         pthread_t* pthread = pthreads[i];
1180         pthread_join(*pthread, 0);
1181         free(pthread);
1182     }
1183 }
1184 
openWindows()1185 Vector<HWND>& openWindows()
1186 {
1187     static Vector<HWND> vector;
1188     return vector;
1189 }
1190 
windowToWebViewMap()1191 WindowToWebViewMap& windowToWebViewMap()
1192 {
1193     static WindowToWebViewMap map;
1194     return map;
1195 }
1196 
createWebViewAndOffscreenWindow(HWND * webViewWindow)1197 IWebView* createWebViewAndOffscreenWindow(HWND* webViewWindow)
1198 {
1199     unsigned maxViewWidth = LayoutTestController::maxViewWidth;
1200     unsigned maxViewHeight = LayoutTestController::maxViewHeight;
1201     HWND hostWindow = CreateWindowEx(WS_EX_TOOLWINDOW, kDumpRenderTreeClassName, TEXT("DumpRenderTree"), WS_POPUP,
1202       -maxViewWidth, -maxViewHeight, maxViewWidth, maxViewHeight, 0, 0, GetModuleHandle(0), 0);
1203 
1204     IWebView* webView;
1205 
1206     HRESULT hr = WebKitCreateInstance(CLSID_WebView, 0, IID_IWebView, (void**)&webView);
1207     if (FAILED(hr)) {
1208         fprintf(stderr, "Failed to create CLSID_WebView instance, error 0x%x\n", hr);
1209         return 0;
1210     }
1211 
1212     if (FAILED(webView->setHostWindow((OLE_HANDLE)(ULONG64)hostWindow)))
1213         return 0;
1214 
1215     RECT clientRect;
1216     clientRect.bottom = clientRect.left = clientRect.top = clientRect.right = 0;
1217     BSTR groupName = SysAllocString(L"org.webkit.DumpRenderTree");
1218     bool failed = FAILED(webView->initWithFrame(clientRect, 0, groupName));
1219     SysFreeString(groupName);
1220     if (failed)
1221         return 0;
1222 
1223     COMPtr<IWebViewPrivate> viewPrivate;
1224     if (FAILED(webView->QueryInterface(&viewPrivate)))
1225         return 0;
1226 
1227     viewPrivate->setShouldApplyMacFontAscentHack(TRUE);
1228     viewPrivate->setAlwaysUsesComplexTextCodePath(forceComplexText);
1229 
1230     BSTR pluginPath = SysAllocStringLen(0, exePath().length() + _tcslen(TestPluginDir));
1231     _tcscpy(pluginPath, exePath().c_str());
1232     _tcscat(pluginPath, TestPluginDir);
1233     failed = FAILED(viewPrivate->addAdditionalPluginDirectory(pluginPath));
1234     SysFreeString(pluginPath);
1235     if (failed)
1236         return 0;
1237 
1238     HWND viewWindow;
1239     if (FAILED(viewPrivate->viewWindow(reinterpret_cast<OLE_HANDLE*>(&viewWindow))))
1240         return 0;
1241     if (webViewWindow)
1242         *webViewWindow = viewWindow;
1243 
1244     SetWindowPos(viewWindow, 0, 0, 0, maxViewWidth, maxViewHeight, 0);
1245     ShowWindow(hostWindow, SW_SHOW);
1246 
1247     if (FAILED(webView->setFrameLoadDelegate(sharedFrameLoadDelegate.get())))
1248         return 0;
1249 
1250     if (FAILED(viewPrivate->setFrameLoadDelegatePrivate(sharedFrameLoadDelegate.get())))
1251         return 0;
1252 
1253     if (FAILED(webView->setUIDelegate(sharedUIDelegate.get())))
1254         return 0;
1255 
1256     COMPtr<IWebViewEditing> viewEditing;
1257     if (FAILED(webView->QueryInterface(&viewEditing)))
1258         return 0;
1259 
1260     if (FAILED(viewEditing->setEditingDelegate(sharedEditingDelegate.get())))
1261         return 0;
1262 
1263     ResourceLoadDelegate* resourceLoadDelegate = new ResourceLoadDelegate();
1264     HRESULT result = webView->setResourceLoadDelegate(resourceLoadDelegate);
1265     resourceLoadDelegate->Release(); // The delegate is owned by the WebView, so release our reference to it.
1266     if (FAILED(result))
1267         return 0;
1268 
1269     openWindows().append(hostWindow);
1270     windowToWebViewMap().set(hostWindow, webView);
1271     return webView;
1272 }
1273 
1274 #if USE(CFNETWORK)
sharedCFURLCache()1275 RetainPtr<CFURLCacheRef> sharedCFURLCache()
1276 {
1277 #ifndef DEBUG_ALL
1278     HMODULE module = GetModuleHandle(TEXT("CFNetwork.dll"));
1279 #else
1280     HMODULE module = GetModuleHandle(TEXT("CFNetwork_debug.dll"));
1281 #endif
1282     if (!module)
1283         return 0;
1284 
1285     typedef CFURLCacheRef (*CFURLCacheCopySharedURLCacheProcPtr)(void);
1286     if (CFURLCacheCopySharedURLCacheProcPtr copyCache = reinterpret_cast<CFURLCacheCopySharedURLCacheProcPtr>(GetProcAddress(module, "CFURLCacheCopySharedURLCache")))
1287         return RetainPtr<CFURLCacheRef>(AdoptCF, copyCache());
1288 
1289     typedef CFURLCacheRef (*CFURLCacheSharedURLCacheProcPtr)(void);
1290     if (CFURLCacheSharedURLCacheProcPtr sharedCache = reinterpret_cast<CFURLCacheSharedURLCacheProcPtr>(GetProcAddress(module, "CFURLCacheSharedURLCache")))
1291         return sharedCache();
1292 
1293     return 0;
1294 }
1295 #endif
1296 
exceptionFilter(EXCEPTION_POINTERS *)1297 static LONG WINAPI exceptionFilter(EXCEPTION_POINTERS*)
1298 {
1299     fputs("#CRASHED\n", stderr);
1300     fflush(stderr);
1301     return EXCEPTION_CONTINUE_SEARCH;
1302 }
1303 
main(int argc,char * argv[])1304 int main(int argc, char* argv[])
1305 {
1306     // Cygwin calls ::SetErrorMode(SEM_FAILCRITICALERRORS), which we will inherit. This is bad for
1307     // testing/debugging, as it causes the post-mortem debugger not to be invoked. We reset the
1308     // error mode here to work around Cygwin's behavior. See <http://webkit.org/b/55222>.
1309     ::SetErrorMode(0);
1310 
1311     ::SetUnhandledExceptionFilter(exceptionFilter);
1312 
1313     leakChecking = false;
1314 
1315     _setmode(1, _O_BINARY);
1316     _setmode(2, _O_BINARY);
1317 
1318     initialize();
1319 
1320     Vector<const char*> tests;
1321 
1322     for (int i = 1; i < argc; ++i) {
1323         if (!stricmp(argv[i], "--threaded")) {
1324             threaded = true;
1325             continue;
1326         }
1327 
1328         if (!stricmp(argv[i], "--dump-all-pixels")) {
1329             dumpAllPixels = true;
1330             continue;
1331         }
1332 
1333         if (!stricmp(argv[i], "--pixel-tests")) {
1334             dumpPixels = true;
1335             continue;
1336         }
1337 
1338         if (!stricmp(argv[i], "--complex-text")) {
1339             forceComplexText = true;
1340             continue;
1341         }
1342 
1343         if (!stricmp(argv[i], "--print-supported-features")) {
1344             printSupportedFeatures = true;
1345             continue;
1346         }
1347 
1348         tests.append(argv[i]);
1349     }
1350 
1351     policyDelegate = new PolicyDelegate();
1352     sharedFrameLoadDelegate.adoptRef(new FrameLoadDelegate);
1353     sharedUIDelegate.adoptRef(new UIDelegate);
1354     sharedEditingDelegate.adoptRef(new EditingDelegate);
1355     sharedHistoryDelegate.adoptRef(new HistoryDelegate);
1356 
1357     // FIXME - need to make DRT pass with Windows native controls <http://bugs.webkit.org/show_bug.cgi?id=25592>
1358     COMPtr<IWebPreferences> tmpPreferences;
1359     if (FAILED(WebKitCreateInstance(CLSID_WebPreferences, 0, IID_IWebPreferences, reinterpret_cast<void**>(&tmpPreferences))))
1360         return -1;
1361     COMPtr<IWebPreferences> standardPreferences;
1362     if (FAILED(tmpPreferences->standardPreferences(&standardPreferences)))
1363         return -1;
1364     COMPtr<IWebPreferencesPrivate> standardPreferencesPrivate;
1365     if (FAILED(standardPreferences->QueryInterface(&standardPreferencesPrivate)))
1366         return -1;
1367     standardPreferencesPrivate->setShouldPaintNativeControls(FALSE);
1368     standardPreferences->setJavaScriptEnabled(TRUE);
1369     standardPreferences->setDefaultFontSize(16);
1370     standardPreferences->setAcceleratedCompositingEnabled(true);
1371     standardPreferences->setContinuousSpellCheckingEnabled(TRUE);
1372 
1373     if (printSupportedFeatures) {
1374         BOOL acceleratedCompositingAvailable;
1375         standardPreferences->acceleratedCompositingEnabled(&acceleratedCompositingAvailable);
1376 
1377 #if ENABLE(3D_RENDERING)
1378         // In theory, we could have a software-based 3D rendering implementation that we use when
1379         // hardware-acceleration is not available. But we don't have any such software
1380         // implementation, so 3D rendering is only available when hardware-acceleration is.
1381         BOOL threeDRenderingAvailable = acceleratedCompositingAvailable;
1382 #else
1383         BOOL threeDRenderingAvailable = FALSE;
1384 #endif
1385 
1386         printf("SupportedFeatures:%s %s\n", acceleratedCompositingAvailable ? "AcceleratedCompositing" : "", threeDRenderingAvailable ? "3DRendering" : "");
1387         return 0;
1388     }
1389 
1390     COMPtr<IWebView> webView(AdoptCOM, createWebViewAndOffscreenWindow(&webViewWindow));
1391     if (!webView)
1392         return -1;
1393 
1394     COMPtr<IWebIconDatabase> iconDatabase;
1395     COMPtr<IWebIconDatabase> tmpIconDatabase;
1396     if (FAILED(WebKitCreateInstance(CLSID_WebIconDatabase, 0, IID_IWebIconDatabase, (void**)&tmpIconDatabase)))
1397         return -1;
1398     if (FAILED(tmpIconDatabase->sharedIconDatabase(&iconDatabase)))
1399         return -1;
1400 
1401     if (FAILED(webView->mainFrame(&frame)))
1402         return -1;
1403 
1404 #if USE(CFNETWORK)
1405     RetainPtr<CFURLCacheRef> urlCache = sharedCFURLCache();
1406     CFURLCacheRemoveAllCachedResponses(urlCache.get());
1407 #endif
1408 
1409 #ifdef _DEBUG
1410     _CrtMemState entryToMainMemCheckpoint;
1411     if (leakChecking)
1412         _CrtMemCheckpoint(&entryToMainMemCheckpoint);
1413 #endif
1414 
1415     if (threaded)
1416         startJavaScriptThreads();
1417 
1418     if (tests.size() == 1 && !strcmp(tests[0], "-")) {
1419         char filenameBuffer[2048];
1420         printSeparators = true;
1421         while (fgets(filenameBuffer, sizeof(filenameBuffer), stdin)) {
1422             char* newLineCharacter = strchr(filenameBuffer, '\n');
1423             if (newLineCharacter)
1424                 *newLineCharacter = '\0';
1425 
1426             if (strlen(filenameBuffer) == 0)
1427                 continue;
1428 
1429             runTest(filenameBuffer);
1430         }
1431     } else {
1432         printSeparators = tests.size() > 1;
1433         for (int i = 0; i < tests.size(); i++)
1434             runTest(tests[i]);
1435     }
1436 
1437     if (threaded)
1438         stopJavaScriptThreads();
1439 
1440     delete policyDelegate;
1441     frame->Release();
1442 
1443 #ifdef _DEBUG
1444     if (leakChecking) {
1445         // dump leaks to stderr
1446         _CrtSetReportMode(_CRT_WARN, _CRTDBG_MODE_FILE);
1447         _CrtSetReportFile(_CRT_WARN, _CRTDBG_FILE_STDERR);
1448         _CrtMemDumpAllObjectsSince(&entryToMainMemCheckpoint);
1449     }
1450 #endif
1451 
1452     shutDownWebKit();
1453 
1454     return 0;
1455 }
1456