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(¤tItem)))
654 return;
655
656 assert(currentItem != prevTestBFItem);
657 COMPtr<IUnknown> currentItemUnknown;
658 currentItem->QueryInterface(¤tItemUnknown);
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