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