2007-11-21 Alp Toker <alp@atoker.com>
[WebKit-https.git] / WebKitTools / DumpRenderTree / mac / DumpRenderTree.mm
1 /*
2  * Copyright (C) 2005, 2006, 2007 Apple, Inc.  All rights reserved.
3  *           (C) 2007 Graham Dennis (graham.dennis@gmail.com)
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  *
9  * 1.  Redistributions of source code must retain the above copyright
10  *     notice, this list of conditions and the following disclaimer. 
11  * 2.  Redistributions in binary form must reproduce the above copyright
12  *     notice, this list of conditions and the following disclaimer in the
13  *     documentation and/or other materials provided with the distribution. 
14  * 3.  Neither the name of Apple Computer, Inc. ("Apple") nor the names of
15  *     its contributors may be used to endorse or promote products derived
16  *     from this software without specific prior written permission. 
17  *
18  * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
19  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
20  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
21  * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
22  * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
23  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
24  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
25  * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
27  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28  */
29  
30 #import "DumpRenderTree.h"
31
32 #import "DumpRenderTreePasteboard.h"
33 #import "DumpRenderTreeWindow.h"
34 #import "EditingDelegate.h"
35 #import "EventSendingController.h"
36 #import "FrameLoadDelegate.h"
37 #import "LayoutTestController.h"
38 #import "NavigationController.h"
39 #import "ObjCPlugin.h"
40 #import "ObjCPluginFunction.h"
41 #import "PolicyDelegate.h"
42 #import "ResourceLoadDelegate.h"
43 #import "UIDelegate.h"
44 #import "WorkQueue.h"
45 #import "WorkQueueItem.h"
46
47 #import <ApplicationServices/ApplicationServices.h> // for CMSetDefaultProfileBySpace
48 #import <CoreFoundation/CoreFoundation.h>
49 #import <JavaScriptCore/Assertions.h>
50 #import <JavaScriptCore/JavaScriptCore.h>
51 #import <WebKit/DOMElementPrivate.h>
52 #import <WebKit/DOMExtensions.h>
53 #import <WebKit/DOMRange.h>
54 #import <WebKit/WebBackForwardList.h>
55 #import <WebKit/WebCoreStatistics.h>
56 #import <WebKit/WebDataSourcePrivate.h>
57 #import <WebKit/WebDocumentPrivate.h>
58 #import <WebKit/WebEditingDelegate.h>
59 #import <WebKit/WebFrameView.h>
60 #import <WebKit/WebHistory.h>
61 #import <WebKit/WebHistoryItemPrivate.h>
62 #import <WebKit/WebPluginDatabase.h>
63 #import <WebKit/WebPreferences.h>
64 #import <WebKit/WebPreferencesPrivate.h>
65 #import <WebKit/WebResourceLoadDelegate.h>
66 #import <WebKit/WebViewPrivate.h>
67 #import <getopt.h>
68 #import <mach-o/getsect.h>
69 #import <malloc/malloc.h>
70 #import <objc/objc-runtime.h>                       // for class_poseAs
71 #import <pthread.h>
72
73 #define COMMON_DIGEST_FOR_OPENSSL
74 #import <CommonCrypto/CommonDigest.h>               // for MD5 functions
75
76 @interface DumpRenderTreeEvent : NSEvent
77 @end
78
79 static void runTest(const char *pathOrURL);
80 static NSString *md5HashStringForBitmap(CGImageRef bitmap);
81
82 // Deciding when it's OK to dump out the state is a bit tricky.  All these must be true:
83 // - There is no load in progress
84 // - There is no work queued up (see workQueue var, below)
85 // - waitToDump==NO.  This means either waitUntilDone was never called, or it was called
86 //       and notifyDone was called subsequently.
87 // Note that the call to notifyDone and the end of the load can happen in either order.
88
89 volatile bool done;
90
91 NavigationController* navigationController = 0;
92 LayoutTestController* layoutTestController = 0;
93
94 WebFrame *mainFrame = 0;
95 // This is the topmost frame that is loading, during a given load, or nil when no load is 
96 // in progress.  Usually this is the same as the main frame, but not always.  In the case
97 // where a frameset is loaded, and then new content is loaded into one of the child frames,
98 // that child frame is the "topmost frame that is loading".
99 WebFrame *topLoadingFrame = nil;     // !nil iff a load is in progress
100
101
102 CFMutableSetRef disallowedURLs = 0;
103 CFRunLoopTimerRef waitToDumpWatchdog = 0;
104
105 // Delegates
106 static FrameLoadDelegate *frameLoadDelegate;
107 static UIDelegate *uiDelegate;
108 static EditingDelegate *editingDelegate;
109 static ResourceLoadDelegate *resourceLoadDelegate;
110 PolicyDelegate *policyDelegate;
111
112 static int dumpPixels;
113 static int dumpAllPixels;
114 static int threaded;
115 static int testRepaintDefault;
116 static int repaintSweepHorizontallyDefault;
117 static int dumpTree = YES;
118 static BOOL printSeparators;
119 static NSString *currentTest = nil;
120
121 static WebHistoryItem *prevTestBFItem = nil;  // current b/f item at the end of the previous test
122 static unsigned char* screenCaptureBuffer;
123 static CGColorSpaceRef sharedColorSpace;
124
125 const unsigned maxViewHeight = 600;
126 const unsigned maxViewWidth = 800;
127
128 static pthread_mutex_t javaScriptThreadsMutex = PTHREAD_MUTEX_INITIALIZER;
129 static BOOL javaScriptThreadsShouldTerminate;
130
131 static const int javaScriptThreadsCount = 4;
132 static CFMutableDictionaryRef javaScriptThreads()
133 {
134     assert(pthread_mutex_trylock(&javaScriptThreadsMutex) == EBUSY);
135     static CFMutableDictionaryRef staticJavaScriptThreads;
136     if (!staticJavaScriptThreads)
137         staticJavaScriptThreads = CFDictionaryCreateMutable(kCFAllocatorDefault, 0, NULL, NULL);
138     return staticJavaScriptThreads;
139 }
140
141 // Loops forever, running a script and randomly respawning, until 
142 // javaScriptThreadsShouldTerminate becomes true.
143 void* runJavaScriptThread(void* arg)
144 {
145     const char* const script =
146         "var array = [];"
147         "for (var i = 0; i < 10; i++) {"
148         "    array.push(String(i));"
149         "}";
150
151     while(1) {
152         JSGlobalContextRef ctx = JSGlobalContextCreate(NULL);
153         JSStringRef scriptRef = JSStringCreateWithUTF8CString(script);
154
155         JSValueRef exception = NULL;
156         JSEvaluateScript(ctx, scriptRef, NULL, NULL, 0, &exception);
157         assert(!exception);
158         
159         JSGlobalContextRelease(ctx);
160         JSStringRelease(scriptRef);
161         
162         JSGarbageCollect(ctx);
163
164         pthread_mutex_lock(&javaScriptThreadsMutex);
165
166         // Check for cancellation.
167         if (javaScriptThreadsShouldTerminate) {
168             pthread_mutex_unlock(&javaScriptThreadsMutex);
169             return 0;
170         }
171
172         // Respawn probabilistically.
173         if (random() % 5 == 0) {
174             pthread_t pthread;
175             pthread_create(&pthread, NULL, &runJavaScriptThread, NULL);
176             pthread_detach(pthread);
177
178             CFDictionaryRemoveValue(javaScriptThreads(), pthread_self());
179             CFDictionaryAddValue(javaScriptThreads(), pthread, NULL);
180
181             pthread_mutex_unlock(&javaScriptThreadsMutex);
182             return 0;
183         }
184
185         pthread_mutex_unlock(&javaScriptThreadsMutex);
186     }
187 }
188
189 static void startJavaScriptThreads()
190 {
191     pthread_mutex_lock(&javaScriptThreadsMutex);
192
193     for (int i = 0; i < javaScriptThreadsCount; i++) {
194         pthread_t pthread;
195         pthread_create(&pthread, NULL, &runJavaScriptThread, NULL);
196         pthread_detach(pthread);
197         CFDictionaryAddValue(javaScriptThreads(), pthread, NULL);
198     }
199
200     pthread_mutex_unlock(&javaScriptThreadsMutex);
201 }
202
203 static void stopJavaScriptThreads()
204 {
205     pthread_mutex_lock(&javaScriptThreadsMutex);
206
207     javaScriptThreadsShouldTerminate = true;
208
209     pthread_t* pthreads[javaScriptThreadsCount] = { 0 };
210     ASSERT(CFDictionaryGetCount(javaScriptThreads()) == javaScriptThreadsCount);
211     CFDictionaryGetKeysAndValues(javaScriptThreads(), (const void**)pthreads, 0);
212
213     pthread_mutex_unlock(&javaScriptThreadsMutex);
214
215     for (int i = 0; i < javaScriptThreadsCount; i++) {
216         pthread_t* pthread = pthreads[i];
217         pthread_join(*pthread, 0);
218         free(pthread);
219     }
220 }
221
222 static BOOL shouldIgnoreWebCoreNodeLeaks(CFStringRef URLString)
223 {
224     static CFStringRef const ignoreSet[] = {
225         // Keeping this infrastructure around in case we ever need it again.
226     };
227     static const int ignoreSetCount = sizeof(ignoreSet) / sizeof(CFStringRef);
228     
229     for (int i = 0; i < ignoreSetCount; i++) {
230         CFStringRef ignoreString = ignoreSet[i];
231         CFRange range = CFRangeMake(0, CFStringGetLength(URLString));
232         CFOptionFlags flags = kCFCompareAnchored | kCFCompareBackwards | kCFCompareCaseInsensitive;
233         if (CFStringFindWithOptions(URLString, ignoreString, range, flags, NULL))
234             return YES;
235     }
236     return NO;
237 }
238
239 static CMProfileRef currentColorProfile = 0;
240 static void restoreColorSpace(int ignored)
241 {
242     if (currentColorProfile) {
243         int error = CMSetDefaultProfileByUse(cmDisplayUse, currentColorProfile);
244         if (error)
245             fprintf(stderr, "Failed to retore previous color profile!  You may need to open System Preferences : Displays : Color and manually restore your color settings.  (Error: %i)", error);
246         currentColorProfile = 0;
247     }
248 }
249
250 static void crashHandler(int sig)
251 {
252     fprintf(stderr, "%s\n", strsignal(sig));
253     restoreColorSpace(0);
254     exit(128 + sig);
255 }
256
257 static void activateAhemFont()
258 {    
259     unsigned long fontDataLength;
260     char* fontData = getsectdata("__DATA", "Ahem", &fontDataLength);
261     if (!fontData) {
262         fprintf(stderr, "Failed to locate the Ahem font.\n");
263         exit(1);
264     }
265
266     ATSFontContainerRef fontContainer;
267     OSStatus status = ATSFontActivateFromMemory(fontData, fontDataLength, kATSFontContextLocal, kATSFontFormatUnspecified, NULL, kATSOptionFlagsDefault, &fontContainer);
268
269     if (status != noErr) {
270         fprintf(stderr, "Failed to activate the Ahem font.\n");
271         exit(1);
272     }
273 }
274
275 static void setDefaultColorProfileToRGB()
276 {
277     CMProfileRef genericProfile = (CMProfileRef)[[NSColorSpace genericRGBColorSpace] colorSyncProfile];
278     CMProfileRef previousProfile;
279     int error = CMGetDefaultProfileByUse(cmDisplayUse, &previousProfile);
280     if (error) {
281         fprintf(stderr, "Failed to get current color profile.  I will not be able to restore your current profile, thus I'm not changing it.  Many pixel tests may fail as a result.  (Error: %i)\n", error);
282         return;
283     }
284     if (previousProfile == genericProfile)
285         return;
286     CFStringRef previousProfileName;
287     CFStringRef genericProfileName;
288     char previousProfileNameString[1024];
289     char genericProfileNameString[1024];
290     CMCopyProfileDescriptionString(previousProfile, &previousProfileName);
291     CMCopyProfileDescriptionString(genericProfile, &genericProfileName);
292     CFStringGetCString(previousProfileName, previousProfileNameString, sizeof(previousProfileNameString), kCFStringEncodingUTF8);
293     CFStringGetCString(genericProfileName, genericProfileNameString, sizeof(previousProfileNameString), kCFStringEncodingUTF8);
294     CFRelease(genericProfileName);
295     CFRelease(previousProfileName);
296     
297     fprintf(stderr, "\n\nWARNING: Temporarily changing your system color profile from \"%s\" to \"%s\".\n", previousProfileNameString, genericProfileNameString);
298     fprintf(stderr, "This allows the WebKit pixel-based regression tests to have consistent color values across all machines.\n");
299     fprintf(stderr, "The colors on your screen will change for the duration of the testing.\n\n");
300     
301     if ((error = CMSetDefaultProfileByUse(cmDisplayUse, genericProfile)))
302         fprintf(stderr, "Failed to set color profile to \"%s\"! Many pixel tests will fail as a result.  (Error: %i)",
303             genericProfileNameString, error);
304     else {
305         currentColorProfile = previousProfile;
306         signal(SIGINT, restoreColorSpace);
307         signal(SIGHUP, restoreColorSpace);
308         signal(SIGTERM, restoreColorSpace);
309     }
310 }
311
312 static void* (*savedMalloc)(malloc_zone_t*, size_t);
313 static void* (*savedRealloc)(malloc_zone_t*, void*, size_t);
314
315 static void* checkedMalloc(malloc_zone_t* zone, size_t size)
316 {
317     if (size >= 0x10000000)
318         return 0;
319     return savedMalloc(zone, size);
320 }
321
322 static void* checkedRealloc(malloc_zone_t* zone, void* ptr, size_t size)
323 {
324     if (size >= 0x10000000)
325         return 0;
326     return savedRealloc(zone, ptr, size);
327 }
328
329 static void makeLargeMallocFailSilently()
330 {
331     malloc_zone_t* zone = malloc_default_zone();
332     savedMalloc = zone->malloc;
333     savedRealloc = zone->realloc;
334     zone->malloc = checkedMalloc;
335     zone->realloc = checkedRealloc;
336 }
337
338 WebView *createWebViewAndOffscreenWindow()
339 {
340     NSRect rect = NSMakeRect(0, 0, maxViewWidth, maxViewHeight);
341     WebView *webView = [[WebView alloc] initWithFrame:rect frameName:nil groupName:@"org.webkit.DumpRenderTree"];
342         
343     [webView setUIDelegate:uiDelegate];
344     [webView setFrameLoadDelegate:frameLoadDelegate];
345     [webView setEditingDelegate:editingDelegate];
346     [webView setResourceLoadDelegate:resourceLoadDelegate];
347
348     // Register the same schemes that Safari does
349     [WebView registerURLSchemeAsLocal:@"feed"];
350     [WebView registerURLSchemeAsLocal:@"feeds"];
351     [WebView registerURLSchemeAsLocal:@"feedsearch"];
352
353     // The back/forward cache is causing problems due to layouts during transition from one page to another.
354     // So, turn it off for now, but we might want to turn it back on some day.
355     [[webView backForwardList] setPageCacheSize:0];
356     
357     [webView setContinuousSpellCheckingEnabled:YES];
358     
359     // To make things like certain NSViews, dragging, and plug-ins work, put the WebView a window, but put it off-screen so you don't see it.
360     // Put it at -10000, -10000 in "flipped coordinates", since WebCore and the DOM use flipped coordinates.
361     NSRect windowRect = NSOffsetRect(rect, -10000, [[[NSScreen screens] objectAtIndex:0] frame].size.height - rect.size.height + 10000);
362     DumpRenderTreeWindow *window = [[DumpRenderTreeWindow alloc] initWithContentRect:windowRect styleMask:NSBorderlessWindowMask backing:NSBackingStoreBuffered defer:YES];
363     [[window contentView] addSubview:webView];
364     [window orderBack:nil];
365     [window setAutodisplay:NO];
366     
367     // For reasons that are not entirely clear, the following pair of calls makes WebView handle its
368     // dynamic scrollbars properly. Without it, every frame will always have scrollbars.
369     NSBitmapImageRep *imageRep = [webView bitmapImageRepForCachingDisplayInRect:[webView bounds]];
370     [webView cacheDisplayInRect:[webView bounds] toBitmapImageRep:imageRep];
371         
372     return webView;
373 }
374
375 void testStringByEvaluatingJavaScriptFromString()
376 {
377     // maps expected result <= JavaScript expression
378     NSDictionary *expressions = [NSDictionary dictionaryWithObjectsAndKeys:
379         @"0", @"0", 
380         @"0", @"'0'", 
381         @"", @"",
382         @"", @"''", 
383         @"", @"new String()", 
384         @"", @"new String('0')", 
385         @"", @"throw 1", 
386         @"", @"{ }", 
387         @"", @"[ ]", 
388         @"", @"//", 
389         @"", @"a.b.c", 
390         @"", @"(function() { throw 'error'; })()", 
391         @"", @"null",
392         @"", @"undefined",
393         @"true", @"true",
394         @"false", @"false",
395         nil
396     ];
397
398     NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
399     WebView *webView = [[WebView alloc] initWithFrame:NSZeroRect frameName:@"" groupName:@""];
400
401     NSEnumerator *enumerator = [expressions keyEnumerator];
402     id expression;
403     while ((expression = [enumerator nextObject])) {
404         NSString *expectedResult = [expressions objectForKey:expression];
405         NSString *result = [webView stringByEvaluatingJavaScriptFromString:expression];
406         assert([result isEqualToString:expectedResult]);
407     }
408
409     [webView close];
410     [webView release];
411     [pool release];
412 }
413
414 static void setDefaultsToConsistentValuesForTesting()
415 {
416     // Give some clear to undocumented defaults values
417     static const int MediumFontSmoothing = 2;
418     static const int BlueTintedAppearance = 1;
419
420     NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
421     [defaults setObject:@"DoubleMax" forKey:@"AppleScrollBarVariant"];
422     [defaults setInteger:4 forKey:@"AppleAntiAliasingThreshold"]; // smallest font size to CG should perform antialiasing on
423     [defaults setInteger:MediumFontSmoothing forKey:@"AppleFontSmoothing"];
424     [defaults setInteger:BlueTintedAppearance forKey:@"AppleAquaColorVariant"];
425     [defaults setObject:@"0.709800 0.835300 1.000000" forKey:@"AppleHighlightColor"];
426     [defaults setObject:@"0.500000 0.500000 0.500000" forKey:@"AppleOtherHighlightColor"];
427     [defaults setObject:[NSArray arrayWithObject:@"en"] forKey:@"AppleLanguages"];
428
429     WebPreferences *preferences = [WebPreferences standardPreferences];
430
431     [preferences setStandardFontFamily:@"Times"];
432     [preferences setFixedFontFamily:@"Courier"];
433     [preferences setSerifFontFamily:@"Times"];
434     [preferences setSansSerifFontFamily:@"Helvetica"];
435     [preferences setCursiveFontFamily:@"Apple Chancery"];
436     [preferences setFantasyFontFamily:@"Papyrus"];
437     [preferences setDefaultFontSize:16];
438     [preferences setDefaultFixedFontSize:13];
439     [preferences setMinimumFontSize:1];
440     [preferences setJavaEnabled:NO];
441     [preferences setJavaScriptCanOpenWindowsAutomatically:YES];
442     [preferences setEditableLinkBehavior:WebKitEditableLinkOnlyLiveWithShiftKey];
443     [preferences setTabsToLinks:NO];
444     [preferences setDOMPasteAllowed:YES];
445 }
446
447 static void installSignalHandlers()
448 {
449     signal(SIGILL, crashHandler);    /* 4:   illegal instruction (not reset when caught) */
450     signal(SIGTRAP, crashHandler);   /* 5:   trace trap (not reset when caught) */
451     signal(SIGEMT, crashHandler);    /* 7:   EMT instruction */
452     signal(SIGFPE, crashHandler);    /* 8:   floating point exception */
453     signal(SIGBUS, crashHandler);    /* 10:  bus error */
454     signal(SIGSEGV, crashHandler);   /* 11:  segmentation violation */
455     signal(SIGSYS, crashHandler);    /* 12:  bad argument to system call */
456     signal(SIGPIPE, crashHandler);   /* 13:  write on a pipe with no reader */
457     signal(SIGXCPU, crashHandler);   /* 24:  exceeded CPU time limit */
458     signal(SIGXFSZ, crashHandler);   /* 25:  exceeded file size limit */
459 }
460
461 static void allocateGlobalControllers()
462 {
463     // FIXME: We should remove these and move to the ObjC standard [Foo sharedInstance] model
464     navigationController = [[NavigationController alloc] init];
465     frameLoadDelegate = [[FrameLoadDelegate alloc] init];
466     uiDelegate = [[UIDelegate alloc] init];
467     editingDelegate = [[EditingDelegate alloc] init];
468     resourceLoadDelegate = [[ResourceLoadDelegate alloc] init];
469     policyDelegate = [[PolicyDelegate alloc] init];
470 }
471
472 // ObjC++ doens't seem to let me pass NSObject*& sadly.
473 static inline void releaseAndZero(NSObject** object)
474 {
475     [*object release];
476     *object = nil;
477 }
478
479 static void releaseGlobalControllers()
480 {
481     releaseAndZero(&navigationController);
482     releaseAndZero(&frameLoadDelegate);
483     releaseAndZero(&editingDelegate);
484     releaseAndZero(&resourceLoadDelegate);
485     releaseAndZero(&uiDelegate);
486     releaseAndZero(&policyDelegate);
487 }
488
489 static void initializeGlobalsFromCommandLineOptions(int argc, const char *argv[])
490 {
491     struct option options[] = {
492         {"dump-all-pixels", no_argument, &dumpAllPixels, YES},
493         {"horizontal-sweep", no_argument, &repaintSweepHorizontallyDefault, YES},
494         {"notree", no_argument, &dumpTree, NO},
495         {"pixel-tests", no_argument, &dumpPixels, YES},
496         {"repaint", no_argument, &testRepaintDefault, YES},
497         {"tree", no_argument, &dumpTree, YES},
498         {"threaded", no_argument, &threaded, YES},
499         {NULL, 0, NULL, 0}
500     };
501     
502     int option;
503     while ((option = getopt_long(argc, (char * const *)argv, "", options, NULL)) != -1) {
504         switch (option) {
505             case '?':   // unknown or ambiguous option
506             case ':':   // missing argument
507                 exit(1);
508                 break;
509         }
510     }
511 }
512
513 static void initializeColorSpaceAndScreeBufferForPixelTests()
514 {
515     setDefaultColorProfileToRGB();
516     screenCaptureBuffer = (unsigned char *)malloc(maxViewHeight * maxViewWidth * 4);
517     sharedColorSpace = CGColorSpaceCreateDeviceRGB();
518 }
519
520 static void addTestPluginsToPluginSearchPath(const char* executablePath)
521 {
522     NSString *pwd = [[NSString stringWithUTF8String:executablePath] stringByDeletingLastPathComponent];
523     [WebPluginDatabase setAdditionalWebPlugInPaths:[NSArray arrayWithObject:pwd]];
524     [[WebPluginDatabase sharedDatabase] refresh];
525 }
526
527 static bool useLongRunningServerMode(int argc, const char *argv[])
528 {
529     // This assumes you've already called getopt_long
530     return (argc == optind+1 && strcmp(argv[optind], "-") == 0);
531 }
532
533 static void runTestingServerLoop()
534 {
535     // When DumpRenderTree run in server mode, we just wait around for file names
536     // to be passed to us and read each in turn, passing the results back to the client
537     char filenameBuffer[2048];
538     while (fgets(filenameBuffer, sizeof(filenameBuffer), stdin)) {
539         char *newLineCharacter = strchr(filenameBuffer, '\n');
540         if (newLineCharacter)
541             *newLineCharacter = '\0';
542
543         if (strlen(filenameBuffer) == 0)
544             continue;
545
546         runTest(filenameBuffer);
547     }
548 }
549
550 static void prepareConsistentTestingEnvironment()
551 {
552     class_poseAs(objc_getClass("DumpRenderTreePasteboard"), objc_getClass("NSPasteboard"));
553     class_poseAs(objc_getClass("DumpRenderTreeEvent"), objc_getClass("NSEvent"));
554
555     setDefaultsToConsistentValuesForTesting();
556     activateAhemFont();
557     
558     if (dumpPixels)
559         initializeColorSpaceAndScreeBufferForPixelTests();
560     allocateGlobalControllers();
561     
562     makeLargeMallocFailSilently();
563 }
564
565 void dumpRenderTree(int argc, const char *argv[])
566 {
567     prepareConsistentTestingEnvironment();
568     initializeGlobalsFromCommandLineOptions(argc, argv);
569     addTestPluginsToPluginSearchPath(argv[0]);
570     installSignalHandlers();
571     
572     WebView *webView = createWebViewAndOffscreenWindow();
573     mainFrame = [webView mainFrame];
574
575     [[NSURLCache sharedURLCache] removeAllCachedResponses];
576
577     // <rdar://problem/5222911>
578     testStringByEvaluatingJavaScriptFromString();
579
580     if (threaded)
581         startJavaScriptThreads();
582
583     if (useLongRunningServerMode(argc, argv)) {
584         printSeparators = YES;
585         runTestingServerLoop();
586     } else {
587         printSeparators = (optind < argc-1 || (dumpPixels && dumpTree));
588         for (int i = optind; i != argc; ++i)
589             runTest(argv[i]);
590     }
591
592     if (threaded)
593         stopJavaScriptThreads();
594
595     [WebCoreStatistics emptyCache]; // Otherwise SVGImages trigger false positives for Frame/Node counts    
596     [webView close];
597     mainFrame = nil;
598
599     // Work around problem where registering drag types leaves an outstanding
600     // "perform selector" on the window, which retains the window. It's a bit
601     // inelegant and perhaps dangerous to just blow them all away, but in practice
602     // it probably won't cause any trouble (and this is just a test tool, after all).
603     NSWindow *window = [webView window];
604     [NSObject cancelPreviousPerformRequestsWithTarget:window];
605     
606     [window close]; // releases when closed
607     [webView release];
608     
609     releaseGlobalControllers();
610     
611     [DumpRenderTreePasteboard releaseLocalPasteboards];
612
613     // FIXME: This should be moved onto LayoutTestController and made into a HashSet
614     if (disallowedURLs) {
615         CFRelease(disallowedURLs);
616         disallowedURLs = 0;
617     }
618
619     if (dumpPixels)
620         restoreColorSpace(0);
621 }
622
623 int main(int argc, const char *argv[])
624 {
625     NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
626     [NSApplication sharedApplication]; // Force AppKit to init itself
627     dumpRenderTree(argc, argv);
628     [WebCoreStatistics garbageCollectJavaScriptObjects];
629     [pool release];
630     return 0;
631 }
632
633 static int compareHistoryItems(id item1, id item2, void *context)
634 {
635     return [[item1 target] caseInsensitiveCompare:[item2 target]];
636 }
637
638 static void dumpHistoryItem(WebHistoryItem *item, int indent, BOOL current)
639 {
640     int start = 0;
641     if (current) {
642         printf("curr->");
643         start = 6;
644     }
645     for (int i = start; i < indent; i++)
646         putchar(' ');
647     printf("%s", [[item URLString] UTF8String]);
648     NSString *target = [item target];
649     if (target && [target length] > 0)
650         printf(" (in frame \"%s\")", [target UTF8String]);
651     if ([item isTargetItem])
652         printf("  **nav target**");
653     putchar('\n');
654     NSArray *kids = [item children];
655     if (kids) {
656         // must sort to eliminate arbitrary result ordering which defeats reproducible testing
657         kids = [kids sortedArrayUsingFunction:&compareHistoryItems context:nil];
658         for (unsigned i = 0; i < [kids count]; i++)
659             dumpHistoryItem([kids objectAtIndex:i], indent+4, NO);
660     }
661 }
662
663 static void dumpFrameScrollPosition(WebFrame *f)
664 {
665     NSPoint scrollPosition = [[[[f frameView] documentView] superview] bounds].origin;
666     if (ABS(scrollPosition.x) > 0.00000001 || ABS(scrollPosition.y) > 0.00000001) {
667         if ([f parentFrame] != nil)
668             printf("frame '%s' ", [[f name] UTF8String]);
669         printf("scrolled to %.f,%.f\n", scrollPosition.x, scrollPosition.y);
670     }
671
672     if (layoutTestController->dumpChildFrameScrollPositions()) {
673         NSArray *kids = [f childFrames];
674         if (kids)
675             for (unsigned i = 0; i < [kids count]; i++)
676                 dumpFrameScrollPosition([kids objectAtIndex:i]);
677     }
678 }
679
680 static NSString *dumpFramesAsText(WebFrame *frame)
681 {
682     DOMDocument *document = [frame DOMDocument];
683     DOMElement *documentElement = [document documentElement];
684
685     if (!documentElement)
686         return @"";
687
688     NSMutableString *result = [[[NSMutableString alloc] init] autorelease];
689
690     // Add header for all but the main frame.
691     if ([frame parentFrame])
692         result = [NSMutableString stringWithFormat:@"\n--------\nFrame: '%@'\n--------\n", [frame name]];
693
694     [result appendFormat:@"%@\n", [documentElement innerText]];
695
696     if (layoutTestController->dumpChildFramesAsText()) {
697         NSArray *kids = [frame childFrames];
698         if (kids) {
699             for (unsigned i = 0; i < [kids count]; i++)
700                 [result appendString:dumpFramesAsText([kids objectAtIndex:i])];
701         }
702     }
703
704     return result;
705 }
706
707 static void convertMIMEType(NSMutableString *mimeType)
708 {
709     if ([mimeType isEqualToString:@"application/x-javascript"])
710         [mimeType setString:@"text/javascript"];
711 }
712
713 static void convertWebResourceDataToString(NSMutableDictionary *resource)
714 {
715     NSMutableString *mimeType = [resource objectForKey:@"WebResourceMIMEType"];
716     convertMIMEType(mimeType);
717     
718     if ([mimeType hasPrefix:@"text/"]) {
719         NSData *data = [resource objectForKey:@"WebResourceData"];
720         NSString *dataAsString = [[[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding] autorelease];
721         [resource setObject:dataAsString forKey:@"WebResourceData"];
722     }
723 }
724
725 static void normalizeWebResourceURL(NSMutableString *webResourceURL, NSString *oldURLBase)
726 {
727     [webResourceURL replaceOccurrencesOfString:oldURLBase
728                                     withString:@"file://"
729                                        options:NSLiteralSearch
730                                          range:NSMakeRange(0, [webResourceURL length])];
731 }
732
733 static void convertWebResourceResponseToDictionary(NSMutableDictionary *propertyList, NSString *oldURLBase)
734 {
735     NSURLResponse *response = nil;
736     NSData *responseData = [propertyList objectForKey:@"WebResourceResponse"]; // WebResourceResponseKey in WebResource.m
737     if ([responseData isKindOfClass:[NSData class]]) {
738         // Decode NSURLResponse
739         NSKeyedUnarchiver *unarchiver = [[NSKeyedUnarchiver alloc] initForReadingWithData:responseData];
740         response = [unarchiver decodeObjectForKey:@"WebResourceResponse"]; // WebResourceResponseKey in WebResource.m
741         [unarchiver finishDecoding];
742         [unarchiver release];
743     }        
744     
745     NSMutableDictionary *responseDictionary = [[NSMutableDictionary alloc] init];
746     
747     NSMutableString *urlString = [[[response URL] description] mutableCopy];
748     normalizeWebResourceURL(urlString, oldURLBase);
749     [responseDictionary setObject:urlString forKey:@"URL"];
750     [urlString release];
751     
752     NSMutableString *mimeTypeString = [[response MIMEType] mutableCopy];
753     convertMIMEType(mimeTypeString);
754     [responseDictionary setObject:mimeTypeString forKey:@"MIMEType"];
755     [mimeTypeString release];
756
757     NSString *textEncodingName = [response textEncodingName];
758     if (textEncodingName)
759         [responseDictionary setObject:textEncodingName forKey:@"textEncodingName"];
760     [responseDictionary setObject:[NSNumber numberWithLongLong:[response expectedContentLength]] forKey:@"expectedContentLength"];
761     
762     if ([response isKindOfClass:[NSHTTPURLResponse class]]) {
763         NSHTTPURLResponse *httpResponse = (NSHTTPURLResponse *)response;
764         
765         [responseDictionary setObject:[httpResponse allHeaderFields] forKey:@"allHeaderFields"];
766         [responseDictionary setObject:[NSNumber numberWithInt:[httpResponse statusCode]] forKey:@"statusCode"];
767     }
768     
769     [propertyList setObject:responseDictionary forKey:@"WebResourceResponse"];
770     [responseDictionary release];
771 }
772
773 static NSString *serializeWebArchiveToXML(WebArchive *webArchive)
774 {
775     NSString *errorString;
776     NSMutableDictionary *propertyList = [NSPropertyListSerialization propertyListFromData:[webArchive data]
777                                                                          mutabilityOption:NSPropertyListMutableContainersAndLeaves
778                                                                                    format:NULL
779                                                                          errorDescription:&errorString];
780     if (!propertyList)
781         return errorString;
782
783     // Normalize WebResourceResponse and WebResourceURL values in plist for testing
784     NSString *cwdURL = [@"file://" stringByAppendingString:[[[NSFileManager defaultManager] currentDirectoryPath] stringByExpandingTildeInPath]];
785     
786     NSMutableArray *resources = [NSMutableArray arrayWithCapacity:1];
787     [resources addObject:propertyList];
788
789     while ([resources count]) {
790         NSMutableDictionary *resourcePropertyList = [resources objectAtIndex:0];
791         [resources removeObjectAtIndex:0];
792
793         NSMutableDictionary *mainResource = [resourcePropertyList objectForKey:@"WebMainResource"];
794         normalizeWebResourceURL([mainResource objectForKey:@"WebResourceURL"], cwdURL);
795         convertWebResourceDataToString(mainResource);
796
797         // Add subframeArchives to list for processing
798         NSMutableArray *subframeArchives = [resourcePropertyList objectForKey:@"WebSubframeArchives"]; // WebSubframeArchivesKey in WebArchive.m
799         if (subframeArchives)
800             [resources addObjectsFromArray:subframeArchives];
801
802         NSMutableArray *subresources = [resourcePropertyList objectForKey:@"WebSubresources"]; // WebSubresourcesKey in WebArchive.m
803         NSEnumerator *enumerator = [subresources objectEnumerator];
804         NSMutableDictionary *subresourcePropertyList;
805         while ((subresourcePropertyList = [enumerator nextObject])) {
806             normalizeWebResourceURL([subresourcePropertyList objectForKey:@"WebResourceURL"], cwdURL);
807             convertWebResourceResponseToDictionary(subresourcePropertyList, cwdURL);
808             convertWebResourceDataToString(subresourcePropertyList);
809         }
810     }
811
812     NSData *xmlData = [NSPropertyListSerialization dataFromPropertyList:propertyList
813                                                                  format:NSPropertyListXMLFormat_v1_0
814                                                        errorDescription:&errorString];
815     if (!xmlData)
816         return errorString;
817
818     NSMutableString *string = [[[NSMutableString alloc] initWithData:xmlData encoding:NSUTF8StringEncoding] autorelease];
819
820     // Replace "Apple Computer" with "Apple" in the DTD declaration.
821     NSRange range = [string rangeOfString:@"-//Apple Computer//"];
822     if (range.location != NSNotFound)
823         [string replaceCharactersInRange:range withString:@"-//Apple//"];
824     
825     return string;
826 }
827
828 static void dumpBackForwardListForWebView(WebView *view)
829 {
830     printf("\n============== Back Forward List ==============\n");
831     WebBackForwardList *bfList = [view backForwardList];
832
833     // Print out all items in the list after prevTestBFItem, which was from the previous test
834     // Gather items from the end of the list, the print them out from oldest to newest
835     NSMutableArray *itemsToPrint = [[NSMutableArray alloc] init];
836     for (int i = [bfList forwardListCount]; i > 0; i--) {
837         WebHistoryItem *item = [bfList itemAtIndex:i];
838         // something is wrong if the item from the last test is in the forward part of the b/f list
839         assert(item != prevTestBFItem);
840         [itemsToPrint addObject:item];
841     }
842             
843     assert([bfList currentItem] != prevTestBFItem);
844     [itemsToPrint addObject:[bfList currentItem]];
845     int currentItemIndex = [itemsToPrint count] - 1;
846
847     for (int i = -1; i >= -[bfList backListCount]; i--) {
848         WebHistoryItem *item = [bfList itemAtIndex:i];
849         if (item == prevTestBFItem)
850             break;
851         [itemsToPrint addObject:item];
852     }
853
854     for (int i = [itemsToPrint count]-1; i >= 0; i--)
855         dumpHistoryItem([itemsToPrint objectAtIndex:i], 8, i == currentItemIndex);
856
857     [itemsToPrint release];
858     printf("===============================================\n");
859 }
860
861 static void sizeWebViewForCurrentTest()
862 {
863     // W3C SVG tests expect to be 480x360
864     bool isSVGW3CTest = ([currentTest rangeOfString:@"svg/W3C-SVG-1.1"].length);
865     if (isSVGW3CTest)
866         [[mainFrame webView] setFrameSize:NSMakeSize(480, 360)];
867     else
868         [[mainFrame webView] setFrameSize:NSMakeSize(maxViewWidth, maxViewHeight)];
869 }
870
871 static const char *methodNameStringForFailedTest()
872 {
873     const char *errorMessage;
874     if (layoutTestController->dumpAsText())
875         errorMessage = "[documentElement innerText]";
876     else if (layoutTestController->dumpDOMAsWebArchive())
877         errorMessage = "[[mainFrame DOMDocument] webArchive]";
878     else if (layoutTestController->dumpSourceAsWebArchive())
879         errorMessage = "[[mainFrame dataSource] webArchive]";
880     else
881         errorMessage = "[mainFrame renderTreeAsExternalRepresentation]";
882
883     return errorMessage;
884 }
885
886 static void dumpBackForwardListForAllWindows()
887 {
888     CFArrayRef allWindows = (CFArrayRef)[DumpRenderTreeWindow allWindows];
889     unsigned count = CFArrayGetCount(allWindows);
890     for (unsigned i = 0; i < count; i++) {
891         NSWindow *window = (NSWindow *)CFArrayGetValueAtIndex(allWindows, i);
892         WebView *webView = [[[window contentView] subviews] objectAtIndex:0];
893         dumpBackForwardListForWebView(webView);
894     }
895 }
896
897 static void dumpWebViewAsPixelsAndCompareWithExpected()
898 {
899     if (!layoutTestController->dumpAsText() && !layoutTestController->dumpDOMAsWebArchive() && !layoutTestController->dumpSourceAsWebArchive()) {
900         // grab a bitmap from the view
901         WebView* view = [mainFrame webView];
902         NSSize webViewSize = [view frame].size;
903         
904         CGContextRef cgContext = CGBitmapContextCreate(screenCaptureBuffer, static_cast<size_t>(webViewSize.width), static_cast<size_t>(webViewSize.height), 8, static_cast<size_t>(webViewSize.width) * 4, sharedColorSpace, kCGBitmapByteOrder32Host | kCGImageAlphaPremultipliedLast);
905         
906         NSGraphicsContext* savedContext = [[[NSGraphicsContext currentContext] retain] autorelease];
907         NSGraphicsContext* nsContext = [NSGraphicsContext graphicsContextWithGraphicsPort:cgContext flipped:NO];
908         [NSGraphicsContext setCurrentContext:nsContext];
909         
910         if (!layoutTestController->testRepaint()) {
911             NSBitmapImageRep *imageRep;
912             [view displayIfNeeded];
913             [view lockFocus];
914             imageRep = [[NSBitmapImageRep alloc] initWithFocusedViewRect:[view frame]];
915             [view unlockFocus];
916             [imageRep draw];
917             [imageRep release];
918         } else if (!layoutTestController->testRepaintSweepHorizontally()) {
919             NSRect line = NSMakeRect(0, 0, webViewSize.width, 1);
920             while (line.origin.y < webViewSize.height) {
921                 [view displayRectIgnoringOpacity:line inContext:nsContext];
922                 line.origin.y++;
923             }
924         } else {
925             NSRect column = NSMakeRect(0, 0, 1, webViewSize.height);
926             while (column.origin.x < webViewSize.width) {
927                 [view displayRectIgnoringOpacity:column inContext:nsContext];
928                 column.origin.x++;
929             }
930         }
931         if (layoutTestController->dumpSelectionRect()) {
932             NSView *documentView = [[mainFrame frameView] documentView];
933             if ([documentView conformsToProtocol:@protocol(WebDocumentSelection)]) {
934                 [[NSColor redColor] set];
935                 [NSBezierPath strokeRect:[documentView convertRect:[(id <WebDocumentSelection>)documentView selectionRect] fromView:nil]];
936             }
937         }
938         
939         [NSGraphicsContext setCurrentContext:savedContext];
940         
941         CGImageRef bitmapImage = CGBitmapContextCreateImage(cgContext);
942         CGContextRelease(cgContext);
943         
944         // compute the actual hash to compare to the expected image's hash
945         NSString *actualHash = md5HashStringForBitmap(bitmapImage);
946         printf("\nActualHash: %s\n", [actualHash UTF8String]);
947         
948         BOOL dumpImage;
949         if (dumpAllPixels)
950             dumpImage = YES;
951         else {
952             // FIXME: It's unfortunate that we hardcode the file naming scheme here.
953             // At one time, the perl script had all the knowledge about file layout.
954             // Some day we should restore that setup by passing in more parameters to this tool
955             // or returning more information from the tool to the perl script
956             NSString *baseTestPath = [currentTest stringByDeletingPathExtension];
957             NSString *baselineHashPath = [baseTestPath stringByAppendingString:@"-expected.checksum"];
958             NSString *baselineHash = [NSString stringWithContentsOfFile:baselineHashPath encoding:NSUTF8StringEncoding error:nil];
959             NSString *baselineImagePath = [baseTestPath stringByAppendingString:@"-expected.png"];
960             
961             printf("BaselineHash: %s\n", [baselineHash UTF8String]);
962             
963             /// send the image to stdout if the hash mismatches or if there's no file in the file system
964             dumpImage = ![baselineHash isEqualToString:actualHash] || access([baselineImagePath fileSystemRepresentation], F_OK) != 0;
965         }
966         
967         if (dumpImage) {
968             CFMutableDataRef imageData = CFDataCreateMutable(0, 0);
969             CGImageDestinationRef imageDest = CGImageDestinationCreateWithData(imageData, CFSTR("public.png"), 1, 0);
970             CGImageDestinationAddImage(imageDest, bitmapImage, 0);
971             CGImageDestinationFinalize(imageDest);
972             CFRelease(imageDest);
973             printf("Content-length: %lu\n", CFDataGetLength(imageData));
974             fwrite(CFDataGetBytePtr(imageData), 1, CFDataGetLength(imageData), stdout);
975             CFRelease(imageData);
976         }
977         
978         CGImageRelease(bitmapImage);
979     }
980     
981     printf("#EOF\n");
982 }
983
984 static void invalidateAnyPreviousWaitToDumpWatchdog()
985 {
986     if (waitToDumpWatchdog) {
987         CFRunLoopTimerInvalidate(waitToDumpWatchdog);
988         CFRelease(waitToDumpWatchdog);
989         waitToDumpWatchdog = 0;
990     }
991 }
992
993 void dump()
994 {
995     invalidateAnyPreviousWaitToDumpWatchdog();
996
997     if (dumpTree) {
998         NSString *resultString = nil;
999         NSData *resultData = nil;
1000
1001         bool dumpAsText = layoutTestController->dumpAsText();
1002         dumpAsText |= [[[mainFrame dataSource] _responseMIMEType] isEqualToString:@"text/plain"];
1003         layoutTestController->setDumpAsText(dumpAsText);
1004         if (layoutTestController->dumpAsText()) {
1005             resultString = dumpFramesAsText(mainFrame);
1006         } else if (layoutTestController->dumpDOMAsWebArchive()) {
1007             WebArchive *webArchive = [[mainFrame DOMDocument] webArchive];
1008             resultString = serializeWebArchiveToXML(webArchive);
1009         } else if (layoutTestController->dumpSourceAsWebArchive()) {
1010             WebArchive *webArchive = [[mainFrame dataSource] webArchive];
1011             resultString = serializeWebArchiveToXML(webArchive);
1012         } else {
1013             sizeWebViewForCurrentTest();
1014             resultString = [mainFrame renderTreeAsExternalRepresentation];
1015         }
1016
1017         if (resultString && !resultData)
1018             resultData = [resultString dataUsingEncoding:NSUTF8StringEncoding];
1019
1020         if (resultData) {
1021             fwrite([resultData bytes], 1, [resultData length], stdout);
1022
1023             if (!layoutTestController->dumpAsText() && !layoutTestController->dumpDOMAsWebArchive() && !layoutTestController->dumpSourceAsWebArchive())
1024                 dumpFrameScrollPosition(mainFrame);
1025
1026             if (layoutTestController->dumpBackForwardList())
1027                 dumpBackForwardListForAllWindows();
1028         } else
1029             printf("ERROR: nil result from %s", methodNameStringForFailedTest());
1030
1031         if (printSeparators)
1032             puts("#EOF");
1033     }
1034     
1035     if (dumpPixels)
1036         dumpWebViewAsPixelsAndCompareWithExpected();
1037
1038     fflush(stdout);
1039
1040     done = YES;
1041 }
1042
1043 static bool shouldLogFrameLoadDelegates(const char *pathOrURL)
1044 {
1045     return strstr(pathOrURL, "loading/");
1046 }
1047
1048 static CFURLRef createCFURLFromPathOrURL(CFStringRef pathOrURLString)
1049 {
1050     CFURLRef URL;
1051     if (CFStringHasPrefix(pathOrURLString, CFSTR("http://")) || CFStringHasPrefix(pathOrURLString, CFSTR("https://")))
1052         URL = CFURLCreateWithString(NULL, pathOrURLString, NULL);
1053     else
1054         URL = CFURLCreateWithFileSystemPath(NULL, pathOrURLString, kCFURLPOSIXPathStyle, FALSE);
1055     return URL;
1056 }
1057
1058 static void resetWebViewToConsistentStateBeforeTesting()
1059 {
1060     [(EditingDelegate *)[[mainFrame webView] editingDelegate] setAcceptsEditing:YES];
1061     [[mainFrame webView] makeTextStandardSize:nil];
1062     [[mainFrame webView] setTabKeyCyclesThroughElements: YES];
1063     [[mainFrame webView] setPolicyDelegate:nil];
1064     [[mainFrame webView] _setDashboardBehavior:WebDashboardBehaviorUseBackwardCompatibilityMode to:NO];
1065     [[[mainFrame webView] preferences] setPrivateBrowsingEnabled:NO];
1066     [WebView _setUsesTestModeFocusRingColor:YES];
1067 }
1068
1069 static void runTest(const char *pathOrURL)
1070 {
1071     CFStringRef pathOrURLString = CFStringCreateWithCString(NULL, pathOrURL, kCFStringEncodingUTF8);
1072     if (!pathOrURLString) {
1073         fprintf(stderr, "Failed to parse filename as UTF-8: %s\n", pathOrURL);
1074         return;
1075     }
1076
1077     CFURLRef URL = createCFURLFromPathOrURL(pathOrURLString);
1078     if (!URL) {
1079         CFRelease(pathOrURLString);
1080         fprintf(stderr, "Can't turn %s into a CFURL\n", pathOrURL);
1081         return;
1082     }
1083
1084     resetWebViewToConsistentStateBeforeTesting();
1085
1086     layoutTestController = new LayoutTestController(testRepaintDefault, repaintSweepHorizontallyDefault);
1087     topLoadingFrame = nil;
1088     done = NO;
1089
1090     if (disallowedURLs)
1091         CFSetRemoveAllValues(disallowedURLs);
1092     if (shouldLogFrameLoadDelegates(pathOrURL))
1093         layoutTestController->setDumpFrameLoadCallbacks(true);
1094
1095     if ([WebHistory optionalSharedHistory])
1096         [WebHistory setOptionalSharedHistory:nil];
1097     lastMousePosition = NSMakePoint(0, 0);
1098
1099     if (currentTest != nil)
1100         CFRelease(currentTest);
1101     currentTest = (NSString *)pathOrURLString;
1102     [prevTestBFItem release];
1103     prevTestBFItem = [[[[mainFrame webView] backForwardList] currentItem] retain];
1104
1105     WorkQueue::shared()->clear();
1106     WorkQueue::shared()->setFrozen(false);
1107
1108     BOOL _shouldIgnoreWebCoreNodeLeaks = shouldIgnoreWebCoreNodeLeaks(CFURLGetString(URL));
1109     if (_shouldIgnoreWebCoreNodeLeaks)
1110         [WebCoreStatistics startIgnoringWebCoreNodeLeaks];
1111
1112     NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
1113     [mainFrame loadRequest:[NSURLRequest requestWithURL:(NSURL *)URL]];
1114     CFRelease(URL);
1115     [pool release];
1116     while (!done) {
1117         pool = [[NSAutoreleasePool alloc] init];
1118         [[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantPast]];
1119         [pool release];
1120     }
1121     pool = [[NSAutoreleasePool alloc] init];
1122     [EventSendingController clearSavedEvents];
1123     [[mainFrame webView] setSelectedDOMRange:nil affinity:NSSelectionAffinityDownstream];
1124
1125     WorkQueue::shared()->clear();
1126
1127     if (layoutTestController->closeRemainingWindowsWhenComplete()) {
1128         NSArray* array = [DumpRenderTreeWindow allWindows];
1129         
1130         unsigned count = [array count];
1131         for (unsigned i = 0; i < count; i++) {
1132             NSWindow *window = [array objectAtIndex:i];
1133
1134             // Don't try to close the main window
1135             if (window == [[mainFrame webView] window])
1136                 continue;
1137             
1138             WebView *webView = [[[window contentView] subviews] objectAtIndex:0];
1139
1140             [webView close];
1141             [window close];
1142         }
1143     }
1144     
1145     [pool release];
1146
1147     // We should only have our main window left when we're done
1148     ASSERT(CFArrayGetCount(allWindowsRef) == 1);
1149     ASSERT(CFArrayGetValueAtIndex(allWindowsRef, 0) == [[mainFrame webView] window]);
1150
1151     delete layoutTestController;
1152     layoutTestController = 0;
1153
1154     if (_shouldIgnoreWebCoreNodeLeaks)
1155         [WebCoreStatistics stopIgnoringWebCoreNodeLeaks];
1156 }
1157
1158 /* Hashes a bitmap and returns a text string for comparison and saving to a file */
1159 static NSString *md5HashStringForBitmap(CGImageRef bitmap)
1160 {
1161     MD5_CTX md5Context;
1162     unsigned char hash[16];
1163     
1164     unsigned bitsPerPixel = CGImageGetBitsPerPixel(bitmap);
1165     assert(bitsPerPixel == 32); // ImageDiff assumes 32 bit RGBA, we must as well.
1166     unsigned bytesPerPixel = bitsPerPixel / 8;
1167     unsigned pixelsHigh = CGImageGetHeight(bitmap);
1168     unsigned pixelsWide = CGImageGetWidth(bitmap);
1169     unsigned bytesPerRow = CGImageGetBytesPerRow(bitmap);
1170     assert(bytesPerRow >= (pixelsWide * bytesPerPixel));
1171     
1172     MD5_Init(&md5Context);
1173     unsigned char *bitmapData = screenCaptureBuffer;
1174     for (unsigned row = 0; row < pixelsHigh; row++) {
1175         MD5_Update(&md5Context, bitmapData, pixelsWide * bytesPerPixel);
1176         bitmapData += bytesPerRow;
1177     }
1178     MD5_Final(hash, &md5Context);
1179     
1180     char hex[33] = "";
1181     for (int i = 0; i < 16; i++) {
1182        snprintf(hex, 33, "%s%02x", hex, hash[i]);
1183     }
1184
1185     return [NSString stringWithUTF8String:hex];
1186 }
1187
1188 void displayWebView()
1189 {
1190     NSView *webView = [mainFrame webView];
1191     [webView display];
1192     [webView lockFocus];
1193     [[[NSColor blackColor] colorWithAlphaComponent:0.66] set];
1194     NSRectFillUsingOperation([webView frame], NSCompositeSourceOver);
1195     [webView unlockFocus];
1196 }
1197
1198 @implementation DumpRenderTreeEvent
1199
1200 + (NSPoint)mouseLocation
1201 {
1202     return [[[mainFrame webView] window] convertBaseToScreen:lastMousePosition];
1203 }
1204
1205 @end