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