Home | History | Annotate | Download | only in mac
      1 /*
      2  * Copyright (C) 2005, 2006, 2007, 2008, 2009 Apple Inc. All rights reserved.
      3  *           (C) 2007 Graham Dennis (graham.dennis (at) 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 "config.h"
     31 #import "DumpRenderTree.h"
     32 
     33 #import "AccessibilityController.h"
     34 #import "CheckedMalloc.h"
     35 #import "DumpRenderTreeDraggingInfo.h"
     36 #import "DumpRenderTreePasteboard.h"
     37 #import "DumpRenderTreeWindow.h"
     38 #import "EditingDelegate.h"
     39 #import "EventSendingController.h"
     40 #import "FrameLoadDelegate.h"
     41 #import "HistoryDelegate.h"
     42 #import "JavaScriptThreading.h"
     43 #import "LayoutTestController.h"
     44 #import "NavigationController.h"
     45 #import "ObjCPlugin.h"
     46 #import "ObjCPluginFunction.h"
     47 #import "PixelDumpSupport.h"
     48 #import "PolicyDelegate.h"
     49 #import "ResourceLoadDelegate.h"
     50 #import "UIDelegate.h"
     51 #import "WorkQueue.h"
     52 #import "WorkQueueItem.h"
     53 #import <Carbon/Carbon.h>
     54 #import <CoreFoundation/CoreFoundation.h>
     55 #import <WebKit/DOMElement.h>
     56 #import <WebKit/DOMExtensions.h>
     57 #import <WebKit/DOMRange.h>
     58 #import <WebKit/WebBackForwardList.h>
     59 #import <WebKit/WebCache.h>
     60 #import <WebKit/WebCoreStatistics.h>
     61 #import <WebKit/WebDataSourcePrivate.h>
     62 #import <WebKit/WebDatabaseManagerPrivate.h>
     63 #import <WebKit/WebDocumentPrivate.h>
     64 #import <WebKit/WebEditingDelegate.h>
     65 #import <WebKit/WebFrameView.h>
     66 #import <WebKit/WebHTMLRepresentationInternal.h>
     67 #import <WebKit/WebHistory.h>
     68 #import <WebKit/WebHistoryItemPrivate.h>
     69 #import <WebKit/WebInspector.h>
     70 #import <WebKit/WebKitNSStringExtras.h>
     71 #import <WebKit/WebPluginDatabase.h>
     72 #import <WebKit/WebPreferences.h>
     73 #import <WebKit/WebPreferencesPrivate.h>
     74 #import <WebKit/WebPreferenceKeysPrivate.h>
     75 #import <WebKit/WebResourceLoadDelegate.h>
     76 #import <WebKit/WebTypesInternal.h>
     77 #import <WebKit/WebViewPrivate.h>
     78 #import <getopt.h>
     79 #import <mach-o/getsect.h>
     80 #import <objc/objc-runtime.h>
     81 #import <wtf/Assertions.h>
     82 #import <wtf/RetainPtr.h>
     83 #import <wtf/Threading.h>
     84 #import <wtf/OwnPtr.h>
     85 
     86 using namespace std;
     87 
     88 @interface DumpRenderTreeEvent : NSEvent
     89 @end
     90 
     91 @interface NSURLRequest (PrivateThingsWeShouldntReallyUse)
     92 +(void)setAllowsAnyHTTPSCertificate:(BOOL)allow forHost:(NSString *)host;
     93 @end
     94 
     95 static void runTest(const string& testPathOrURL);
     96 
     97 // Deciding when it's OK to dump out the state is a bit tricky.  All these must be true:
     98 // - There is no load in progress
     99 // - There is no work queued up (see workQueue var, below)
    100 // - waitToDump==NO.  This means either waitUntilDone was never called, or it was called
    101 //       and notifyDone was called subsequently.
    102 // Note that the call to notifyDone and the end of the load can happen in either order.
    103 
    104 volatile bool done;
    105 
    106 NavigationController* gNavigationController = 0;
    107 LayoutTestController* gLayoutTestController = 0;
    108 
    109 WebFrame *mainFrame = 0;
    110 // This is the topmost frame that is loading, during a given load, or nil when no load is
    111 // in progress.  Usually this is the same as the main frame, but not always.  In the case
    112 // where a frameset is loaded, and then new content is loaded into one of the child frames,
    113 // that child frame is the "topmost frame that is loading".
    114 WebFrame *topLoadingFrame = nil;     // !nil iff a load is in progress
    115 
    116 
    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 static HistoryDelegate *historyDelegate;
    126 PolicyDelegate *policyDelegate;
    127 
    128 static int dumpPixels;
    129 static int threaded;
    130 static int dumpTree = YES;
    131 static int forceComplexText;
    132 static BOOL printSeparators;
    133 static RetainPtr<CFStringRef> persistentUserStyleSheetLocation;
    134 
    135 static WebHistoryItem *prevTestBFItem = nil;  // current b/f item at the end of the previous test
    136 
    137 const unsigned maxViewHeight = 600;
    138 const unsigned maxViewWidth = 800;
    139 
    140 #if __OBJC2__
    141 static void swizzleAllMethods(Class imposter, Class original)
    142 {
    143     unsigned int imposterMethodCount;
    144     Method* imposterMethods = class_copyMethodList(imposter, &imposterMethodCount);
    145 
    146     unsigned int originalMethodCount;
    147     Method* originalMethods = class_copyMethodList(original, &originalMethodCount);
    148 
    149     for (unsigned int i = 0; i < imposterMethodCount; i++) {
    150         SEL imposterMethodName = method_getName(imposterMethods[i]);
    151 
    152         // Attempt to add the method to the original class.  If it fails, the method already exists and we should
    153         // instead exchange the implementations.
    154         if (class_addMethod(original, imposterMethodName, method_getImplementation(imposterMethods[i]), method_getTypeEncoding(imposterMethods[i])))
    155             continue;
    156 
    157         unsigned int j = 0;
    158         for (; j < originalMethodCount; j++) {
    159             SEL originalMethodName = method_getName(originalMethods[j]);
    160             if (sel_isEqual(imposterMethodName, originalMethodName))
    161                 break;
    162         }
    163 
    164         // If class_addMethod failed above then the method must exist on the original class.
    165         ASSERT(j < originalMethodCount);
    166         method_exchangeImplementations(imposterMethods[i], originalMethods[j]);
    167     }
    168 
    169     free(imposterMethods);
    170     free(originalMethods);
    171 }
    172 #endif
    173 
    174 static void poseAsClass(const char* imposter, const char* original)
    175 {
    176     Class imposterClass = objc_getClass(imposter);
    177     Class originalClass = objc_getClass(original);
    178 
    179 #if !__OBJC2__
    180     class_poseAs(imposterClass, originalClass);
    181 #else
    182 
    183     // Swizzle instance methods
    184     swizzleAllMethods(imposterClass, originalClass);
    185     // and then class methods
    186     swizzleAllMethods(object_getClass(imposterClass), object_getClass(originalClass));
    187 #endif
    188 }
    189 
    190 void setPersistentUserStyleSheetLocation(CFStringRef url)
    191 {
    192     persistentUserStyleSheetLocation = url;
    193 }
    194 
    195 static bool shouldIgnoreWebCoreNodeLeaks(const string& URLString)
    196 {
    197     static char* const ignoreSet[] = {
    198         // Keeping this infrastructure around in case we ever need it again.
    199     };
    200     static const int ignoreSetCount = sizeof(ignoreSet) / sizeof(char*);
    201 
    202     for (int i = 0; i < ignoreSetCount; i++) {
    203         // FIXME: ignore case
    204         string curIgnore(ignoreSet[i]);
    205         // Match at the end of the URLString
    206         if (!URLString.compare(URLString.length() - curIgnore.length(), curIgnore.length(), curIgnore))
    207             return true;
    208     }
    209     return false;
    210 }
    211 
    212 static void activateFonts()
    213 {
    214 #if defined(BUILDING_ON_LEOPARD) || defined(BUILDING_ON_TIGER)
    215     static const char* fontSectionNames[] = {
    216         "Ahem",
    217         "WeightWatcher100",
    218         "WeightWatcher200",
    219         "WeightWatcher300",
    220         "WeightWatcher400",
    221         "WeightWatcher500",
    222         "WeightWatcher600",
    223         "WeightWatcher700",
    224         "WeightWatcher800",
    225         "WeightWatcher900",
    226         0
    227     };
    228 
    229     for (unsigned i = 0; fontSectionNames[i]; ++i) {
    230         unsigned long fontDataLength;
    231         char* fontData = getsectdata("__DATA", fontSectionNames[i], &fontDataLength);
    232         if (!fontData) {
    233             fprintf(stderr, "Failed to locate the %s font.\n", fontSectionNames[i]);
    234             exit(1);
    235         }
    236 
    237         ATSFontContainerRef fontContainer;
    238         OSStatus status = ATSFontActivateFromMemory(fontData, fontDataLength, kATSFontContextLocal, kATSFontFormatUnspecified, NULL, kATSOptionFlagsDefault, &fontContainer);
    239 
    240         if (status != noErr) {
    241             fprintf(stderr, "Failed to activate the %s font.\n", fontSectionNames[i]);
    242             exit(1);
    243         }
    244     }
    245 #else
    246 
    247     // Work around <rdar://problem/6698023> by activating fonts from disk
    248     // FIXME: This code can be removed once <rdar://problem/6698023> is addressed.
    249 
    250     static const char* fontFileNames[] = {
    251         "AHEM____.TTF",
    252         "WebKitWeightWatcher100.ttf",
    253         "WebKitWeightWatcher200.ttf",
    254         "WebKitWeightWatcher300.ttf",
    255         "WebKitWeightWatcher400.ttf",
    256         "WebKitWeightWatcher500.ttf",
    257         "WebKitWeightWatcher600.ttf",
    258         "WebKitWeightWatcher700.ttf",
    259         "WebKitWeightWatcher800.ttf",
    260         "WebKitWeightWatcher900.ttf",
    261         0
    262     };
    263 
    264     NSMutableArray *fontURLs = [NSMutableArray array];
    265     NSURL *resourcesDirectory = [NSURL URLWithString:@"DumpRenderTree.resources" relativeToURL:[[NSBundle mainBundle] executableURL]];
    266     for (unsigned i = 0; fontFileNames[i]; ++i) {
    267         NSURL *fontURL = [resourcesDirectory URLByAppendingPathComponent:[NSString stringWithUTF8String:fontFileNames[i]]];
    268         [fontURLs addObject:[fontURL absoluteURL]];
    269     }
    270 
    271     CFArrayRef errors = 0;
    272     if (!CTFontManagerRegisterFontsForURLs((CFArrayRef)fontURLs, kCTFontManagerScopeProcess, &errors)) {
    273         NSLog(@"Failed to activate fonts: %@", errors);
    274         CFRelease(errors);
    275         exit(1);
    276     }
    277 #endif
    278 }
    279 
    280 WebView *createWebViewAndOffscreenWindow()
    281 {
    282     NSRect rect = NSMakeRect(0, 0, maxViewWidth, maxViewHeight);
    283     WebView *webView = [[WebView alloc] initWithFrame:rect frameName:nil groupName:@"org.webkit.DumpRenderTree"];
    284 
    285     [webView setUIDelegate:uiDelegate];
    286     [webView setFrameLoadDelegate:frameLoadDelegate];
    287     [webView setEditingDelegate:editingDelegate];
    288     [webView setResourceLoadDelegate:resourceLoadDelegate];
    289 
    290     // Register the same schemes that Safari does
    291     [WebView registerURLSchemeAsLocal:@"feed"];
    292     [WebView registerURLSchemeAsLocal:@"feeds"];
    293     [WebView registerURLSchemeAsLocal:@"feedsearch"];
    294 
    295     [webView setContinuousSpellCheckingEnabled:YES];
    296 
    297     // 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.
    298     // Put it at -10000, -10000 in "flipped coordinates", since WebCore and the DOM use flipped coordinates.
    299     NSRect windowRect = NSOffsetRect(rect, -10000, [[[NSScreen screens] objectAtIndex:0] frame].size.height - rect.size.height + 10000);
    300     DumpRenderTreeWindow *window = [[DumpRenderTreeWindow alloc] initWithContentRect:windowRect styleMask:NSBorderlessWindowMask backing:NSBackingStoreBuffered defer:YES];
    301 
    302 #if !defined(BUILDING_ON_TIGER) && !defined(BUILDING_ON_LEOPARD)
    303     [window setColorSpace:[[NSScreen mainScreen] colorSpace]];
    304 #endif
    305 
    306     [[window contentView] addSubview:webView];
    307     [window orderBack:nil];
    308     [window setAutodisplay:NO];
    309 
    310     [window startListeningForAcceleratedCompositingChanges];
    311 
    312     // For reasons that are not entirely clear, the following pair of calls makes WebView handle its
    313     // dynamic scrollbars properly. Without it, every frame will always have scrollbars.
    314     NSBitmapImageRep *imageRep = [webView bitmapImageRepForCachingDisplayInRect:[webView bounds]];
    315     [webView cacheDisplayInRect:[webView bounds] toBitmapImageRep:imageRep];
    316 
    317     return webView;
    318 }
    319 
    320 void testStringByEvaluatingJavaScriptFromString()
    321 {
    322     // maps expected result <= JavaScript expression
    323     NSDictionary *expressions = [NSDictionary dictionaryWithObjectsAndKeys:
    324         @"0", @"0",
    325         @"0", @"'0'",
    326         @"", @"",
    327         @"", @"''",
    328         @"", @"new String()",
    329         @"", @"new String('0')",
    330         @"", @"throw 1",
    331         @"", @"{ }",
    332         @"", @"[ ]",
    333         @"", @"//",
    334         @"", @"a.b.c",
    335         @"", @"(function() { throw 'error'; })()",
    336         @"", @"null",
    337         @"", @"undefined",
    338         @"true", @"true",
    339         @"false", @"false",
    340         nil
    341     ];
    342 
    343     NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
    344     WebView *webView = [[WebView alloc] initWithFrame:NSZeroRect frameName:@"" groupName:@""];
    345 
    346     NSEnumerator *enumerator = [expressions keyEnumerator];
    347     id expression;
    348     while ((expression = [enumerator nextObject])) {
    349         NSString *expectedResult = [expressions objectForKey:expression];
    350         NSString *result = [webView stringByEvaluatingJavaScriptFromString:expression];
    351         assert([result isEqualToString:expectedResult]);
    352     }
    353 
    354     [webView close];
    355     [webView release];
    356     [pool release];
    357 }
    358 
    359 static NSString *libraryPathForDumpRenderTree()
    360 {
    361     return [@"~/Library/Application Support/DumpRenderTree" stringByExpandingTildeInPath];
    362 }
    363 
    364 // Called before each test.
    365 static void resetDefaultsToConsistentValues()
    366 {
    367     // Give some clear to undocumented defaults values
    368     static const int NoFontSmoothing = 0;
    369     static const int BlueTintedAppearance = 1;
    370 
    371     NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
    372     [defaults setInteger:4 forKey:@"AppleAntiAliasingThreshold"]; // smallest font size to CG should perform antialiasing on
    373     [defaults setInteger:NoFontSmoothing forKey:@"AppleFontSmoothing"];
    374     [defaults setInteger:BlueTintedAppearance forKey:@"AppleAquaColorVariant"];
    375     [defaults setObject:@"0.709800 0.835300 1.000000" forKey:@"AppleHighlightColor"];
    376     [defaults setObject:@"0.500000 0.500000 0.500000" forKey:@"AppleOtherHighlightColor"];
    377     [defaults setObject:[NSArray arrayWithObject:@"en"] forKey:@"AppleLanguages"];
    378     [defaults setBool:YES forKey:WebKitEnableFullDocumentTeardownPreferenceKey];
    379 
    380     // Scrollbars are drawn either using AppKit (which uses NSUserDefaults) or using HIToolbox (which uses CFPreferences / kCFPreferencesAnyApplication / kCFPreferencesCurrentUser / kCFPreferencesAnyHost)
    381     [defaults setObject:@"DoubleMax" forKey:@"AppleScrollBarVariant"];
    382     RetainPtr<CFTypeRef> initialValue = CFPreferencesCopyValue(CFSTR("AppleScrollBarVariant"), kCFPreferencesAnyApplication, kCFPreferencesCurrentUser, kCFPreferencesAnyHost);
    383     CFPreferencesSetValue(CFSTR("AppleScrollBarVariant"), CFSTR("DoubleMax"), kCFPreferencesAnyApplication, kCFPreferencesCurrentUser, kCFPreferencesAnyHost);
    384 #ifndef __LP64__
    385     // See <rdar://problem/6347388>.
    386     ThemeScrollBarArrowStyle style;
    387     GetThemeScrollBarArrowStyle(&style); // Force HIToolbox to read from CFPreferences
    388 #endif
    389     if (initialValue)
    390         CFPreferencesSetValue(CFSTR("AppleScrollBarVariant"), initialValue.get(), kCFPreferencesAnyApplication, kCFPreferencesCurrentUser, kCFPreferencesAnyHost);
    391 
    392     NSString *path = libraryPathForDumpRenderTree();
    393     [defaults setObject:[path stringByAppendingPathComponent:@"Databases"] forKey:WebDatabaseDirectoryDefaultsKey];
    394     [defaults setObject:[path stringByAppendingPathComponent:@"LocalCache"] forKey:WebKitLocalCacheDefaultsKey];
    395 
    396     WebPreferences *preferences = [WebPreferences standardPreferences];
    397 
    398     [preferences setAllowUniversalAccessFromFileURLs:YES];
    399     [preferences setStandardFontFamily:@"Times"];
    400     [preferences setFixedFontFamily:@"Courier"];
    401     [preferences setSerifFontFamily:@"Times"];
    402     [preferences setSansSerifFontFamily:@"Helvetica"];
    403     [preferences setCursiveFontFamily:@"Apple Chancery"];
    404     [preferences setFantasyFontFamily:@"Papyrus"];
    405     [preferences setDefaultFontSize:16];
    406     [preferences setDefaultFixedFontSize:13];
    407     [preferences setMinimumFontSize:1];
    408     [preferences setJavaEnabled:NO];
    409     [preferences setJavaScriptEnabled:YES];
    410     [preferences setEditableLinkBehavior:WebKitEditableLinkOnlyLiveWithShiftKey];
    411     [preferences setTabsToLinks:NO];
    412     [preferences setDOMPasteAllowed:YES];
    413     [preferences setShouldPrintBackgrounds:YES];
    414     [preferences setCacheModel:WebCacheModelDocumentBrowser];
    415     [preferences setXSSAuditorEnabled:NO];
    416     [preferences setExperimentalNotificationsEnabled:NO];
    417     [preferences setPluginAllowedRunTime:1];
    418 
    419     [preferences setPrivateBrowsingEnabled:NO];
    420     [preferences setAuthorAndUserStylesEnabled:YES];
    421     [preferences setJavaScriptCanOpenWindowsAutomatically:YES];
    422     [preferences setOfflineWebApplicationCacheEnabled:YES];
    423     [preferences setDeveloperExtrasEnabled:NO];
    424     [preferences setLoadsImagesAutomatically:YES];
    425     [preferences setFrameSetFlatteningEnabled:NO];
    426     if (persistentUserStyleSheetLocation) {
    427         [preferences setUserStyleSheetLocation:[NSURL URLWithString:(NSString *)(persistentUserStyleSheetLocation.get())]];
    428         [preferences setUserStyleSheetEnabled:YES];
    429     } else
    430         [preferences setUserStyleSheetEnabled:NO];
    431 
    432     // The back/forward cache is causing problems due to layouts during transition from one page to another.
    433     // So, turn it off for now, but we might want to turn it back on some day.
    434     [preferences setUsesPageCache:NO];
    435 
    436 #if defined(BUILDING_ON_LEOPARD)
    437     // Disable hardware composititing to avoid timeouts and crashes from buggy CoreVideo teardown code.
    438     // https://bugs.webkit.org/show_bug.cgi?id=28845 and rdar://problem/7228836
    439     SInt32 qtVersion;
    440     OSErr err = Gestalt(gestaltQuickTimeVersion, &qtVersion);
    441     assert(err == noErr);
    442     // Bug 7228836 exists in at least 7.6.3 through 7.6.4, hopefully it will be fixed in 7.6.5.
    443     // FIXME: Once we know the exact versions of QuickTime affected, we can update this check.
    444     if (qtVersion <= 0x07648000) // 7.6.4, final release (0x8).  See http://developer.apple.com/mac/library/techn
    445         [preferences setAcceleratedCompositingEnabled:NO];
    446     else
    447 #endif
    448         [preferences setAcceleratedCompositingEnabled:YES];
    449 
    450     [[NSHTTPCookieStorage sharedHTTPCookieStorage] setCookieAcceptPolicy:NSHTTPCookieAcceptPolicyOnlyFromMainDocumentDomain];
    451 
    452     setlocale(LC_ALL, "");
    453 }
    454 
    455 // Called once on DumpRenderTree startup.
    456 static void setDefaultsToConsistentValuesForTesting()
    457 {
    458     resetDefaultsToConsistentValues();
    459 
    460     NSString *path = libraryPathForDumpRenderTree();
    461     NSURLCache *sharedCache =
    462         [[NSURLCache alloc] initWithMemoryCapacity:1024 * 1024
    463                                       diskCapacity:0
    464                                           diskPath:[path stringByAppendingPathComponent:@"URLCache"]];
    465     [NSURLCache setSharedURLCache:sharedCache];
    466     [sharedCache release];
    467 
    468 }
    469 
    470 static void* runThread(void* arg)
    471 {
    472     static ThreadIdentifier previousId = 0;
    473     ThreadIdentifier currentId = currentThread();
    474     // Verify 2 successive threads do not get the same Id.
    475     ASSERT(previousId != currentId);
    476     previousId = currentId;
    477     return 0;
    478 }
    479 
    480 static void testThreadIdentifierMap()
    481 {
    482     // Imitate 'foreign' threads that are not created by WTF.
    483     pthread_t pthread;
    484     pthread_create(&pthread, 0, &runThread, 0);
    485     pthread_join(pthread, 0);
    486 
    487     pthread_create(&pthread, 0, &runThread, 0);
    488     pthread_join(pthread, 0);
    489 
    490     // Now create another thread using WTF. On OSX, it will have the same pthread handle
    491     // but should get a different ThreadIdentifier.
    492     createThread(runThread, 0, "DumpRenderTree: test");
    493 }
    494 
    495 static void crashHandler(int sig)
    496 {
    497     char *signalName = strsignal(sig);
    498     write(STDERR_FILENO, signalName, strlen(signalName));
    499     write(STDERR_FILENO, "\n", 1);
    500     restoreMainDisplayColorProfile(0);
    501     exit(128 + sig);
    502 }
    503 
    504 static void installSignalHandlers()
    505 {
    506     signal(SIGILL, crashHandler);    /* 4:   illegal instruction (not reset when caught) */
    507     signal(SIGTRAP, crashHandler);   /* 5:   trace trap (not reset when caught) */
    508     signal(SIGEMT, crashHandler);    /* 7:   EMT instruction */
    509     signal(SIGFPE, crashHandler);    /* 8:   floating point exception */
    510     signal(SIGBUS, crashHandler);    /* 10:  bus error */
    511     signal(SIGSEGV, crashHandler);   /* 11:  segmentation violation */
    512     signal(SIGSYS, crashHandler);    /* 12:  bad argument to system call */
    513     signal(SIGPIPE, crashHandler);   /* 13:  write on a pipe with no reader */
    514     signal(SIGXCPU, crashHandler);   /* 24:  exceeded CPU time limit */
    515     signal(SIGXFSZ, crashHandler);   /* 25:  exceeded file size limit */
    516 }
    517 
    518 static void allocateGlobalControllers()
    519 {
    520     // FIXME: We should remove these and move to the ObjC standard [Foo sharedInstance] model
    521     gNavigationController = [[NavigationController alloc] init];
    522     frameLoadDelegate = [[FrameLoadDelegate alloc] init];
    523     uiDelegate = [[UIDelegate alloc] init];
    524     editingDelegate = [[EditingDelegate alloc] init];
    525     resourceLoadDelegate = [[ResourceLoadDelegate alloc] init];
    526     policyDelegate = [[PolicyDelegate alloc] init];
    527     historyDelegate = [[HistoryDelegate alloc] init];
    528 }
    529 
    530 // ObjC++ doens't seem to let me pass NSObject*& sadly.
    531 static inline void releaseAndZero(NSObject** object)
    532 {
    533     [*object release];
    534     *object = nil;
    535 }
    536 
    537 static void releaseGlobalControllers()
    538 {
    539     releaseAndZero(&gNavigationController);
    540     releaseAndZero(&frameLoadDelegate);
    541     releaseAndZero(&editingDelegate);
    542     releaseAndZero(&resourceLoadDelegate);
    543     releaseAndZero(&uiDelegate);
    544     releaseAndZero(&policyDelegate);
    545 }
    546 
    547 static void initializeGlobalsFromCommandLineOptions(int argc, const char *argv[])
    548 {
    549     struct option options[] = {
    550         {"notree", no_argument, &dumpTree, NO},
    551         {"pixel-tests", no_argument, &dumpPixels, YES},
    552         {"tree", no_argument, &dumpTree, YES},
    553         {"threaded", no_argument, &threaded, YES},
    554         {"complex-text", no_argument, &forceComplexText, YES},
    555         {NULL, 0, NULL, 0}
    556     };
    557 
    558     int option;
    559     while ((option = getopt_long(argc, (char * const *)argv, "", options, NULL)) != -1) {
    560         switch (option) {
    561             case '?':   // unknown or ambiguous option
    562             case ':':   // missing argument
    563                 exit(1);
    564                 break;
    565         }
    566     }
    567 }
    568 
    569 static void addTestPluginsToPluginSearchPath(const char* executablePath)
    570 {
    571     NSString *pwd = [[NSString stringWithUTF8String:executablePath] stringByDeletingLastPathComponent];
    572     [WebPluginDatabase setAdditionalWebPlugInPaths:[NSArray arrayWithObject:pwd]];
    573     [[WebPluginDatabase sharedDatabase] refresh];
    574 }
    575 
    576 static bool useLongRunningServerMode(int argc, const char *argv[])
    577 {
    578     // This assumes you've already called getopt_long
    579     return (argc == optind+1 && strcmp(argv[optind], "-") == 0);
    580 }
    581 
    582 static void runTestingServerLoop()
    583 {
    584     // When DumpRenderTree run in server mode, we just wait around for file names
    585     // to be passed to us and read each in turn, passing the results back to the client
    586     char filenameBuffer[2048];
    587     while (fgets(filenameBuffer, sizeof(filenameBuffer), stdin)) {
    588         char *newLineCharacter = strchr(filenameBuffer, '\n');
    589         if (newLineCharacter)
    590             *newLineCharacter = '\0';
    591 
    592         if (strlen(filenameBuffer) == 0)
    593             continue;
    594 
    595         runTest(filenameBuffer);
    596     }
    597 }
    598 
    599 static void prepareConsistentTestingEnvironment()
    600 {
    601     poseAsClass("DumpRenderTreePasteboard", "NSPasteboard");
    602     poseAsClass("DumpRenderTreeEvent", "NSEvent");
    603 
    604     setDefaultsToConsistentValuesForTesting();
    605     activateFonts();
    606 
    607     if (dumpPixels)
    608         setupMainDisplayColorProfile();
    609     allocateGlobalControllers();
    610 
    611     makeLargeMallocFailSilently();
    612 }
    613 
    614 void dumpRenderTree(int argc, const char *argv[])
    615 {
    616     initializeGlobalsFromCommandLineOptions(argc, argv);
    617     prepareConsistentTestingEnvironment();
    618     addTestPluginsToPluginSearchPath(argv[0]);
    619     if (dumpPixels)
    620         installSignalHandlers();
    621 
    622     if (forceComplexText)
    623         [WebView _setAlwaysUsesComplexTextCodePath:YES];
    624 
    625     WebView *webView = createWebViewAndOffscreenWindow();
    626     mainFrame = [webView mainFrame];
    627 
    628     [[NSURLCache sharedURLCache] removeAllCachedResponses];
    629     [WebCache empty];
    630 
    631     // <http://webkit.org/b/31200> In order to prevent extra frame load delegate logging being generated if the first test to use SSL
    632     // is set to log frame load delegate calls we ignore SSL certificate errors on localhost and 127.0.0.1.
    633 #if BUILDING_ON_TIGER
    634     // Initialize internal NSURLRequest data for setAllowsAnyHTTPSCertificate:forHost: to work properly.
    635     [[[[NSURLRequest alloc] init] autorelease] HTTPMethod];
    636 #endif
    637     [NSURLRequest setAllowsAnyHTTPSCertificate:YES forHost:@"localhost"];
    638     [NSURLRequest setAllowsAnyHTTPSCertificate:YES forHost:@"127.0.0.1"];
    639 
    640     // <rdar://problem/5222911>
    641     testStringByEvaluatingJavaScriptFromString();
    642 
    643     // http://webkit.org/b/32689
    644     testThreadIdentifierMap();
    645 
    646     if (threaded)
    647         startJavaScriptThreads();
    648 
    649     if (useLongRunningServerMode(argc, argv)) {
    650         printSeparators = YES;
    651         runTestingServerLoop();
    652     } else {
    653         printSeparators = (optind < argc-1 || (dumpPixels && dumpTree));
    654         for (int i = optind; i != argc; ++i)
    655             runTest(argv[i]);
    656     }
    657 
    658     if (threaded)
    659         stopJavaScriptThreads();
    660 
    661     NSWindow *window = [webView window];
    662     [webView close];
    663     mainFrame = nil;
    664 
    665     // Work around problem where registering drag types leaves an outstanding
    666     // "perform selector" on the window, which retains the window. It's a bit
    667     // inelegant and perhaps dangerous to just blow them all away, but in practice
    668     // it probably won't cause any trouble (and this is just a test tool, after all).
    669     [NSObject cancelPreviousPerformRequestsWithTarget:window];
    670 
    671     [window close]; // releases when closed
    672     [webView release];
    673 
    674     releaseGlobalControllers();
    675 
    676     [DumpRenderTreePasteboard releaseLocalPasteboards];
    677 
    678     // FIXME: This should be moved onto LayoutTestController and made into a HashSet
    679     if (disallowedURLs) {
    680         CFRelease(disallowedURLs);
    681         disallowedURLs = 0;
    682     }
    683 
    684     if (dumpPixels)
    685         restoreMainDisplayColorProfile(0);
    686 }
    687 
    688 int main(int argc, const char *argv[])
    689 {
    690     NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
    691     [NSApplication sharedApplication]; // Force AppKit to init itself
    692     dumpRenderTree(argc, argv);
    693     [WebCoreStatistics garbageCollectJavaScriptObjects];
    694     [WebCoreStatistics emptyCache]; // Otherwise SVGImages trigger false positives for Frame/Node counts
    695     [pool release];
    696     return 0;
    697 }
    698 
    699 static NSInteger compareHistoryItems(id item1, id item2, void *context)
    700 {
    701     return [[item1 target] caseInsensitiveCompare:[item2 target]];
    702 }
    703 
    704 static void dumpHistoryItem(WebHistoryItem *item, int indent, BOOL current)
    705 {
    706     int start = 0;
    707     if (current) {
    708         printf("curr->");
    709         start = 6;
    710     }
    711     for (int i = start; i < indent; i++)
    712         putchar(' ');
    713 
    714     NSString *urlString = [item URLString];
    715     if ([[NSURL URLWithString:urlString] isFileURL]) {
    716         NSRange range = [urlString rangeOfString:@"/LayoutTests/"];
    717         urlString = [@"(file test):" stringByAppendingString:[urlString substringFromIndex:(range.length + range.location)]];
    718     }
    719 
    720     printf("%s", [urlString UTF8String]);
    721     NSString *target = [item target];
    722     if (target && [target length] > 0)
    723         printf(" (in frame \"%s\")", [target UTF8String]);
    724     if ([item isTargetItem])
    725         printf("  **nav target**");
    726     putchar('\n');
    727     NSArray *kids = [item children];
    728     if (kids) {
    729         // must sort to eliminate arbitrary result ordering which defeats reproducible testing
    730         kids = [kids sortedArrayUsingFunction:&compareHistoryItems context:nil];
    731         for (unsigned i = 0; i < [kids count]; i++)
    732             dumpHistoryItem([kids objectAtIndex:i], indent+4, NO);
    733     }
    734 }
    735 
    736 static void dumpFrameScrollPosition(WebFrame *f)
    737 {
    738     NSPoint scrollPosition = [[[[f frameView] documentView] superview] bounds].origin;
    739     if (ABS(scrollPosition.x) > 0.00000001 || ABS(scrollPosition.y) > 0.00000001) {
    740         if ([f parentFrame] != nil)
    741             printf("frame '%s' ", [[f name] UTF8String]);
    742         printf("scrolled to %.f,%.f\n", scrollPosition.x, scrollPosition.y);
    743     }
    744 
    745     if (gLayoutTestController->dumpChildFrameScrollPositions()) {
    746         NSArray *kids = [f childFrames];
    747         if (kids)
    748             for (unsigned i = 0; i < [kids count]; i++)
    749                 dumpFrameScrollPosition([kids objectAtIndex:i]);
    750     }
    751 }
    752 
    753 static NSString *dumpFramesAsText(WebFrame *frame)
    754 {
    755     DOMDocument *document = [frame DOMDocument];
    756     DOMElement *documentElement = [document documentElement];
    757 
    758     if (!documentElement)
    759         return @"";
    760 
    761     NSMutableString *result = [[[NSMutableString alloc] init] autorelease];
    762 
    763     // Add header for all but the main frame.
    764     if ([frame parentFrame])
    765         result = [NSMutableString stringWithFormat:@"\n--------\nFrame: '%@'\n--------\n", [frame name]];
    766 
    767     [result appendFormat:@"%@\n", [documentElement innerText]];
    768 
    769     if (gLayoutTestController->dumpChildFramesAsText()) {
    770         NSArray *kids = [frame childFrames];
    771         if (kids) {
    772             for (unsigned i = 0; i < [kids count]; i++)
    773                 [result appendString:dumpFramesAsText([kids objectAtIndex:i])];
    774         }
    775     }
    776 
    777     return result;
    778 }
    779 
    780 static NSData *dumpFrameAsPDF(WebFrame *frame)
    781 {
    782     if (!frame)
    783         return nil;
    784 
    785     // Sadly we have to dump to a file and then read from that file again
    786     // +[NSPrintOperation PDFOperationWithView:insideRect:] requires a rect and prints to a single page
    787     // likewise +[NSView dataWithPDFInsideRect:] also prints to a single continuous page
    788     // The goal of this function is to test "real" printing across multiple pages.
    789     // FIXME: It's possible there might be printing SPI to let us print a multi-page PDF to an NSData object
    790     NSString *path = [libraryPathForDumpRenderTree() stringByAppendingPathComponent:@"test.pdf"];
    791 
    792     NSMutableDictionary *printInfoDict = [NSMutableDictionary dictionaryWithDictionary:[[NSPrintInfo sharedPrintInfo] dictionary]];
    793     [printInfoDict setObject:NSPrintSaveJob forKey:NSPrintJobDisposition];
    794     [printInfoDict setObject:path forKey:NSPrintSavePath];
    795 
    796     NSPrintInfo *printInfo = [[NSPrintInfo alloc] initWithDictionary:printInfoDict];
    797     [printInfo setHorizontalPagination:NSAutoPagination];
    798     [printInfo setVerticalPagination:NSAutoPagination];
    799     [printInfo setVerticallyCentered:NO];
    800 
    801     NSPrintOperation *printOperation = [NSPrintOperation printOperationWithView:[frame frameView] printInfo:printInfo];
    802     [printOperation setShowPanels:NO];
    803     [printOperation runOperation];
    804 
    805     [printInfo release];
    806 
    807     NSData *pdfData = [NSData dataWithContentsOfFile:path];
    808     [[NSFileManager defaultManager] removeFileAtPath:path handler:nil];
    809 
    810     return pdfData;
    811 }
    812 
    813 static void convertMIMEType(NSMutableString *mimeType)
    814 {
    815 #ifdef BUILDING_ON_LEOPARD
    816     // Workaround for <rdar://problem/5539824> on Leopard
    817     if ([mimeType isEqualToString:@"text/xml"])
    818         [mimeType setString:@"application/xml"];
    819 #endif
    820     // Workaround for <rdar://problem/6234318> with Dashcode 2.0
    821     if ([mimeType isEqualToString:@"application/x-javascript"])
    822         [mimeType setString:@"text/javascript"];
    823 }
    824 
    825 static void convertWebResourceDataToString(NSMutableDictionary *resource)
    826 {
    827     NSMutableString *mimeType = [resource objectForKey:@"WebResourceMIMEType"];
    828     convertMIMEType(mimeType);
    829 
    830     if ([mimeType hasPrefix:@"text/"] || [[WebHTMLRepresentation supportedNonImageMIMETypes] containsObject:mimeType]) {
    831         NSString *textEncodingName = [resource objectForKey:@"WebResourceTextEncodingName"];
    832         NSStringEncoding stringEncoding;
    833         if ([textEncodingName length] > 0)
    834             stringEncoding = CFStringConvertEncodingToNSStringEncoding(CFStringConvertIANACharSetNameToEncoding((CFStringRef)textEncodingName));
    835         else
    836             stringEncoding = NSUTF8StringEncoding;
    837 
    838         NSData *data = [resource objectForKey:@"WebResourceData"];
    839         NSString *dataAsString = [[NSString alloc] initWithData:data encoding:stringEncoding];
    840         if (dataAsString)
    841             [resource setObject:dataAsString forKey:@"WebResourceData"];
    842         [dataAsString release];
    843     }
    844 }
    845 
    846 static void normalizeHTTPResponseHeaderFields(NSMutableDictionary *fields)
    847 {
    848     // Normalize headers
    849     if ([fields objectForKey:@"Date"])
    850         [fields setObject:@"Sun, 16 Nov 2008 17:00:00 GMT" forKey:@"Date"];
    851     if ([fields objectForKey:@"Last-Modified"])
    852         [fields setObject:@"Sun, 16 Nov 2008 16:55:00 GMT" forKey:@"Last-Modified"];
    853     if ([fields objectForKey:@"Etag"])
    854         [fields setObject:@"\"301925-21-45c7d72d3e780\"" forKey:@"Etag"];
    855     if ([fields objectForKey:@"Server"])
    856         [fields setObject:@"Apache/2.2.9 (Unix) mod_ssl/2.2.9 OpenSSL/0.9.7l PHP/5.2.6" forKey:@"Server"];
    857 
    858     // Remove headers
    859     if ([fields objectForKey:@"Connection"])
    860         [fields removeObjectForKey:@"Connection"];
    861     if ([fields objectForKey:@"Keep-Alive"])
    862         [fields removeObjectForKey:@"Keep-Alive"];
    863 }
    864 
    865 static void normalizeWebResourceURL(NSMutableString *webResourceURL)
    866 {
    867     static int fileUrlLength = [(NSString *)@"file://" length];
    868     NSRange layoutTestsWebArchivePathRange = [webResourceURL rangeOfString:@"/LayoutTests/" options:NSBackwardsSearch];
    869     if (layoutTestsWebArchivePathRange.location == NSNotFound)
    870         return;
    871     NSRange currentWorkingDirectoryRange = NSMakeRange(fileUrlLength, layoutTestsWebArchivePathRange.location - fileUrlLength);
    872     [webResourceURL replaceCharactersInRange:currentWorkingDirectoryRange withString:@""];
    873 }
    874 
    875 static void convertWebResourceResponseToDictionary(NSMutableDictionary *propertyList)
    876 {
    877     NSURLResponse *response = nil;
    878     NSData *responseData = [propertyList objectForKey:@"WebResourceResponse"]; // WebResourceResponseKey in WebResource.m
    879     if ([responseData isKindOfClass:[NSData class]]) {
    880         // Decode NSURLResponse
    881         NSKeyedUnarchiver *unarchiver = [[NSKeyedUnarchiver alloc] initForReadingWithData:responseData];
    882         response = [unarchiver decodeObjectForKey:@"WebResourceResponse"]; // WebResourceResponseKey in WebResource.m
    883         [unarchiver finishDecoding];
    884         [unarchiver release];
    885     }
    886 
    887     NSMutableDictionary *responseDictionary = [[NSMutableDictionary alloc] init];
    888 
    889     NSMutableString *urlString = [[[response URL] description] mutableCopy];
    890     normalizeWebResourceURL(urlString);
    891     [responseDictionary setObject:urlString forKey:@"URL"];
    892     [urlString release];
    893 
    894     NSMutableString *mimeTypeString = [[response MIMEType] mutableCopy];
    895     convertMIMEType(mimeTypeString);
    896     [responseDictionary setObject:mimeTypeString forKey:@"MIMEType"];
    897     [mimeTypeString release];
    898 
    899     NSString *textEncodingName = [response textEncodingName];
    900     if (textEncodingName)
    901         [responseDictionary setObject:textEncodingName forKey:@"textEncodingName"];
    902     [responseDictionary setObject:[NSNumber numberWithLongLong:[response expectedContentLength]] forKey:@"expectedContentLength"];
    903 
    904     if ([response isKindOfClass:[NSHTTPURLResponse class]]) {
    905         NSHTTPURLResponse *httpResponse = (NSHTTPURLResponse *)response;
    906 
    907         NSMutableDictionary *allHeaderFields = [[httpResponse allHeaderFields] mutableCopy];
    908         normalizeHTTPResponseHeaderFields(allHeaderFields);
    909         [responseDictionary setObject:allHeaderFields forKey:@"allHeaderFields"];
    910         [allHeaderFields release];
    911 
    912         [responseDictionary setObject:[NSNumber numberWithInt:[httpResponse statusCode]] forKey:@"statusCode"];
    913     }
    914 
    915     [propertyList setObject:responseDictionary forKey:@"WebResourceResponse"];
    916     [responseDictionary release];
    917 }
    918 
    919 static NSInteger compareResourceURLs(id resource1, id resource2, void *context)
    920 {
    921     NSString *url1 = [resource1 objectForKey:@"WebResourceURL"];
    922     NSString *url2 = [resource2 objectForKey:@"WebResourceURL"];
    923 
    924     return [url1 compare:url2];
    925 }
    926 
    927 static NSString *serializeWebArchiveToXML(WebArchive *webArchive)
    928 {
    929     NSString *errorString;
    930     NSMutableDictionary *propertyList = [NSPropertyListSerialization propertyListFromData:[webArchive data]
    931                                                                          mutabilityOption:NSPropertyListMutableContainersAndLeaves
    932                                                                                    format:NULL
    933                                                                          errorDescription:&errorString];
    934     if (!propertyList)
    935         return errorString;
    936 
    937     NSMutableArray *resources = [NSMutableArray arrayWithCapacity:1];
    938     [resources addObject:propertyList];
    939 
    940     while ([resources count]) {
    941         NSMutableDictionary *resourcePropertyList = [resources objectAtIndex:0];
    942         [resources removeObjectAtIndex:0];
    943 
    944         NSMutableDictionary *mainResource = [resourcePropertyList objectForKey:@"WebMainResource"];
    945         normalizeWebResourceURL([mainResource objectForKey:@"WebResourceURL"]);
    946         convertWebResourceDataToString(mainResource);
    947 
    948         // Add subframeArchives to list for processing
    949         NSMutableArray *subframeArchives = [resourcePropertyList objectForKey:@"WebSubframeArchives"]; // WebSubframeArchivesKey in WebArchive.m
    950         if (subframeArchives)
    951             [resources addObjectsFromArray:subframeArchives];
    952 
    953         NSMutableArray *subresources = [resourcePropertyList objectForKey:@"WebSubresources"]; // WebSubresourcesKey in WebArchive.m
    954         NSEnumerator *enumerator = [subresources objectEnumerator];
    955         NSMutableDictionary *subresourcePropertyList;
    956         while ((subresourcePropertyList = [enumerator nextObject])) {
    957             normalizeWebResourceURL([subresourcePropertyList objectForKey:@"WebResourceURL"]);
    958             convertWebResourceResponseToDictionary(subresourcePropertyList);
    959             convertWebResourceDataToString(subresourcePropertyList);
    960         }
    961 
    962         // Sort the subresources so they're always in a predictable order for the dump
    963         if (NSArray *sortedSubresources = [subresources sortedArrayUsingFunction:compareResourceURLs context:nil])
    964             [resourcePropertyList setObject:sortedSubresources forKey:@"WebSubresources"];
    965     }
    966 
    967     NSData *xmlData = [NSPropertyListSerialization dataFromPropertyList:propertyList
    968                                                                  format:NSPropertyListXMLFormat_v1_0
    969                                                        errorDescription:&errorString];
    970     if (!xmlData)
    971         return errorString;
    972 
    973     NSMutableString *string = [[[NSMutableString alloc] initWithData:xmlData encoding:NSUTF8StringEncoding] autorelease];
    974 
    975     // Replace "Apple Computer" with "Apple" in the DTD declaration.
    976     NSRange range = [string rangeOfString:@"-//Apple Computer//"];
    977     if (range.location != NSNotFound)
    978         [string replaceCharactersInRange:range withString:@"-//Apple//"];
    979 
    980     return string;
    981 }
    982 
    983 static void dumpBackForwardListForWebView(WebView *view)
    984 {
    985     printf("\n============== Back Forward List ==============\n");
    986     WebBackForwardList *bfList = [view backForwardList];
    987 
    988     // Print out all items in the list after prevTestBFItem, which was from the previous test
    989     // Gather items from the end of the list, the print them out from oldest to newest
    990     NSMutableArray *itemsToPrint = [[NSMutableArray alloc] init];
    991     for (int i = [bfList forwardListCount]; i > 0; i--) {
    992         WebHistoryItem *item = [bfList itemAtIndex:i];
    993         // something is wrong if the item from the last test is in the forward part of the b/f list
    994         assert(item != prevTestBFItem);
    995         [itemsToPrint addObject:item];
    996     }
    997 
    998     assert([bfList currentItem] != prevTestBFItem);
    999     [itemsToPrint addObject:[bfList currentItem]];
   1000     int currentItemIndex = [itemsToPrint count] - 1;
   1001 
   1002     for (int i = -1; i >= -[bfList backListCount]; i--) {
   1003         WebHistoryItem *item = [bfList itemAtIndex:i];
   1004         if (item == prevTestBFItem)
   1005             break;
   1006         [itemsToPrint addObject:item];
   1007     }
   1008 
   1009     for (int i = [itemsToPrint count]-1; i >= 0; i--)
   1010         dumpHistoryItem([itemsToPrint objectAtIndex:i], 8, i == currentItemIndex);
   1011 
   1012     [itemsToPrint release];
   1013     printf("===============================================\n");
   1014 }
   1015 
   1016 static void sizeWebViewForCurrentTest()
   1017 {
   1018     // W3C SVG tests expect to be 480x360
   1019     bool isSVGW3CTest = (gLayoutTestController->testPathOrURL().find("svg/W3C-SVG-1.1") != string::npos);
   1020     if (isSVGW3CTest)
   1021         [[mainFrame webView] setFrameSize:NSMakeSize(480, 360)];
   1022     else
   1023         [[mainFrame webView] setFrameSize:NSMakeSize(maxViewWidth, maxViewHeight)];
   1024 }
   1025 
   1026 static const char *methodNameStringForFailedTest()
   1027 {
   1028     const char *errorMessage;
   1029     if (gLayoutTestController->dumpAsText())
   1030         errorMessage = "[documentElement innerText]";
   1031     else if (gLayoutTestController->dumpDOMAsWebArchive())
   1032         errorMessage = "[[mainFrame DOMDocument] webArchive]";
   1033     else if (gLayoutTestController->dumpSourceAsWebArchive())
   1034         errorMessage = "[[mainFrame dataSource] webArchive]";
   1035     else
   1036         errorMessage = "[mainFrame renderTreeAsExternalRepresentation]";
   1037 
   1038     return errorMessage;
   1039 }
   1040 
   1041 static void dumpBackForwardListForAllWindows()
   1042 {
   1043     CFArrayRef openWindows = (CFArrayRef)[DumpRenderTreeWindow openWindows];
   1044     unsigned count = CFArrayGetCount(openWindows);
   1045     for (unsigned i = 0; i < count; i++) {
   1046         NSWindow *window = (NSWindow *)CFArrayGetValueAtIndex(openWindows, i);
   1047         WebView *webView = [[[window contentView] subviews] objectAtIndex:0];
   1048         dumpBackForwardListForWebView(webView);
   1049     }
   1050 }
   1051 
   1052 static void invalidateAnyPreviousWaitToDumpWatchdog()
   1053 {
   1054     if (waitToDumpWatchdog) {
   1055         CFRunLoopTimerInvalidate(waitToDumpWatchdog);
   1056         CFRelease(waitToDumpWatchdog);
   1057         waitToDumpWatchdog = 0;
   1058     }
   1059 }
   1060 
   1061 void dump()
   1062 {
   1063     invalidateAnyPreviousWaitToDumpWatchdog();
   1064 
   1065     bool dumpAsText = gLayoutTestController->dumpAsText();
   1066     if (dumpTree) {
   1067         NSString *resultString = nil;
   1068         NSData *resultData = nil;
   1069         NSString *resultMimeType = @"text/plain";
   1070 
   1071         dumpAsText |= [[[mainFrame dataSource] _responseMIMEType] isEqualToString:@"text/plain"];
   1072         gLayoutTestController->setDumpAsText(dumpAsText);
   1073         if (gLayoutTestController->dumpAsText()) {
   1074             resultString = dumpFramesAsText(mainFrame);
   1075         } else if (gLayoutTestController->dumpAsPDF()) {
   1076             resultData = dumpFrameAsPDF(mainFrame);
   1077             resultMimeType = @"application/pdf";
   1078         } else if (gLayoutTestController->dumpDOMAsWebArchive()) {
   1079             WebArchive *webArchive = [[mainFrame DOMDocument] webArchive];
   1080             resultString = serializeWebArchiveToXML(webArchive);
   1081             resultMimeType = @"application/x-webarchive";
   1082         } else if (gLayoutTestController->dumpSourceAsWebArchive()) {
   1083             WebArchive *webArchive = [[mainFrame dataSource] webArchive];
   1084             resultString = serializeWebArchiveToXML(webArchive);
   1085             resultMimeType = @"application/x-webarchive";
   1086         } else {
   1087             sizeWebViewForCurrentTest();
   1088             resultString = [mainFrame renderTreeAsExternalRepresentation];
   1089         }
   1090 
   1091         if (resultString && !resultData)
   1092             resultData = [resultString dataUsingEncoding:NSUTF8StringEncoding];
   1093 
   1094         printf("Content-Type: %s\n", [resultMimeType UTF8String]);
   1095 
   1096         if (resultData) {
   1097             fwrite([resultData bytes], 1, [resultData length], stdout);
   1098 
   1099             if (!gLayoutTestController->dumpAsText() && !gLayoutTestController->dumpDOMAsWebArchive() && !gLayoutTestController->dumpSourceAsWebArchive())
   1100                 dumpFrameScrollPosition(mainFrame);
   1101 
   1102             if (gLayoutTestController->dumpBackForwardList())
   1103                 dumpBackForwardListForAllWindows();
   1104         } else
   1105             printf("ERROR: nil result from %s", methodNameStringForFailedTest());
   1106 
   1107         // Stop the watchdog thread before we leave this test to make sure it doesn't
   1108         // fire in between tests causing the next test to fail.
   1109         // This is a speculative fix for: https://bugs.webkit.org/show_bug.cgi?id=32339
   1110         invalidateAnyPreviousWaitToDumpWatchdog();
   1111 
   1112         if (printSeparators) {
   1113             puts("#EOF");       // terminate the content block
   1114             fputs("#EOF\n", stderr);
   1115         }
   1116     }
   1117 
   1118     if (dumpPixels && !dumpAsText)
   1119         dumpWebViewAsPixelsAndCompareWithExpected(gLayoutTestController->expectedPixelHash());
   1120 
   1121     puts("#EOF");   // terminate the (possibly empty) pixels block
   1122 
   1123     fflush(stdout);
   1124     fflush(stderr);
   1125 
   1126     done = YES;
   1127 }
   1128 
   1129 static bool shouldLogFrameLoadDelegates(const char* pathOrURL)
   1130 {
   1131     return strstr(pathOrURL, "loading/");
   1132 }
   1133 
   1134 static bool shouldLogHistoryDelegates(const char* pathOrURL)
   1135 {
   1136     return strstr(pathOrURL, "globalhistory/");
   1137 }
   1138 
   1139 static bool shouldOpenWebInspector(const char* pathOrURL)
   1140 {
   1141     return strstr(pathOrURL, "inspector/");
   1142 }
   1143 
   1144 static void resetWebViewToConsistentStateBeforeTesting()
   1145 {
   1146     WebView *webView = [mainFrame webView];
   1147     [(EditingDelegate *)[webView editingDelegate] setAcceptsEditing:YES];
   1148     [webView makeTextStandardSize:nil];
   1149     [webView resetPageZoom:nil];
   1150     [webView setTabKeyCyclesThroughElements:YES];
   1151     [webView setPolicyDelegate:nil];
   1152     [policyDelegate setPermissive:NO];
   1153     [policyDelegate setControllerToNotifyDone:0];
   1154     [frameLoadDelegate resetToConsistentState];
   1155     [webView _setDashboardBehavior:WebDashboardBehaviorUseBackwardCompatibilityMode to:NO];
   1156     [webView _clearMainFrameName];
   1157     [[webView undoManager] removeAllActions];
   1158     [WebView _removeAllUserContentFromGroup:[webView groupName]];
   1159     [[webView window] setAutodisplay:NO];
   1160 
   1161     resetDefaultsToConsistentValues();
   1162 
   1163     [[mainFrame webView] setSmartInsertDeleteEnabled:YES];
   1164     [[[mainFrame webView] inspector] setJavaScriptProfilingEnabled:NO];
   1165 
   1166     [WebView _setUsesTestModeFocusRingColor:YES];
   1167     [WebView _resetOriginAccessWhiteLists];
   1168 }
   1169 
   1170 static void runTest(const string& testPathOrURL)
   1171 {
   1172     ASSERT(!testPathOrURL.empty());
   1173 
   1174     // Look for "'" as a separator between the path or URL, and the pixel dump hash that follows.
   1175     string pathOrURL(testPathOrURL);
   1176     string expectedPixelHash;
   1177 
   1178     size_t separatorPos = pathOrURL.find("'");
   1179     if (separatorPos != string::npos) {
   1180         pathOrURL = string(testPathOrURL, 0, separatorPos);
   1181         expectedPixelHash = string(testPathOrURL, separatorPos + 1);
   1182     }
   1183 
   1184     NSString *pathOrURLString = [NSString stringWithUTF8String:pathOrURL.c_str()];
   1185     if (!pathOrURLString) {
   1186         fprintf(stderr, "Failed to parse \"%s\" as UTF-8\n", pathOrURL.c_str());
   1187         return;
   1188     }
   1189 
   1190     NSURL *url;
   1191     if ([pathOrURLString hasPrefix:@"http://"] || [pathOrURLString hasPrefix:@"https://"])
   1192         url = [NSURL URLWithString:pathOrURLString];
   1193     else
   1194         url = [NSURL fileURLWithPath:pathOrURLString];
   1195     if (!url) {
   1196         fprintf(stderr, "Failed to parse \"%s\" as a URL\n", pathOrURL.c_str());
   1197         return;
   1198     }
   1199 
   1200     const string testURL([[url absoluteString] UTF8String]);
   1201 
   1202     resetWebViewToConsistentStateBeforeTesting();
   1203 
   1204     gLayoutTestController = new LayoutTestController(testURL, expectedPixelHash);
   1205     topLoadingFrame = nil;
   1206     ASSERT(!draggingInfo); // the previous test should have called eventSender.mouseUp to drop!
   1207     releaseAndZero(&draggingInfo);
   1208     done = NO;
   1209 
   1210     gLayoutTestController->setIconDatabaseEnabled(false);
   1211 
   1212     if (disallowedURLs)
   1213         CFSetRemoveAllValues(disallowedURLs);
   1214     if (shouldLogFrameLoadDelegates(pathOrURL.c_str()))
   1215         gLayoutTestController->setDumpFrameLoadCallbacks(true);
   1216 
   1217     if (shouldLogHistoryDelegates(pathOrURL.c_str()))
   1218         [[mainFrame webView] setHistoryDelegate:historyDelegate];
   1219     else
   1220         [[mainFrame webView] setHistoryDelegate:nil];
   1221 
   1222     if (shouldOpenWebInspector(pathOrURL.c_str()))
   1223         gLayoutTestController->showWebInspector();
   1224 
   1225     if ([WebHistory optionalSharedHistory])
   1226         [WebHistory setOptionalSharedHistory:nil];
   1227     lastMousePosition = NSZeroPoint;
   1228     lastClickPosition = NSZeroPoint;
   1229 
   1230     [prevTestBFItem release];
   1231     prevTestBFItem = [[[[mainFrame webView] backForwardList] currentItem] retain];
   1232 
   1233     WorkQueue::shared()->clear();
   1234     WorkQueue::shared()->setFrozen(false);
   1235 
   1236     bool ignoreWebCoreNodeLeaks = shouldIgnoreWebCoreNodeLeaks(testURL);
   1237     if (ignoreWebCoreNodeLeaks)
   1238         [WebCoreStatistics startIgnoringWebCoreNodeLeaks];
   1239 
   1240     NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
   1241     [mainFrame loadRequest:[NSURLRequest requestWithURL:url]];
   1242     [pool release];
   1243     while (!done) {
   1244         pool = [[NSAutoreleasePool alloc] init];
   1245         [[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantPast]];
   1246         [pool release];
   1247     }
   1248 
   1249     pool = [[NSAutoreleasePool alloc] init];
   1250     [EventSendingController clearSavedEvents];
   1251     [[mainFrame webView] setSelectedDOMRange:nil affinity:NSSelectionAffinityDownstream];
   1252 
   1253     WorkQueue::shared()->clear();
   1254 
   1255     if (gLayoutTestController->closeRemainingWindowsWhenComplete()) {
   1256         NSArray* array = [DumpRenderTreeWindow openWindows];
   1257 
   1258         unsigned count = [array count];
   1259         for (unsigned i = 0; i < count; i++) {
   1260             NSWindow *window = [array objectAtIndex:i];
   1261 
   1262             // Don't try to close the main window
   1263             if (window == [[mainFrame webView] window])
   1264                 continue;
   1265 
   1266             WebView *webView = [[[window contentView] subviews] objectAtIndex:0];
   1267 
   1268             [webView close];
   1269             [window close];
   1270         }
   1271     }
   1272 
   1273     if (shouldOpenWebInspector(pathOrURL.c_str()))
   1274         gLayoutTestController->closeWebInspector();
   1275 
   1276     resetWebViewToConsistentStateBeforeTesting();
   1277 
   1278     [mainFrame loadHTMLString:@"<html></html>" baseURL:[NSURL URLWithString:@"about:blank"]];
   1279     [mainFrame stopLoading];
   1280 
   1281     [pool release];
   1282 
   1283     // We should only have our main window left open when we're done
   1284     ASSERT(CFArrayGetCount(openWindowsRef) == 1);
   1285     ASSERT(CFArrayGetValueAtIndex(openWindowsRef, 0) == [[mainFrame webView] window]);
   1286 
   1287     gLayoutTestController->deref();
   1288     gLayoutTestController = 0;
   1289 
   1290     if (ignoreWebCoreNodeLeaks)
   1291         [WebCoreStatistics stopIgnoringWebCoreNodeLeaks];
   1292 }
   1293 
   1294 void displayWebView()
   1295 {
   1296     NSView *webView = [mainFrame webView];
   1297     [webView display];
   1298     [webView lockFocus];
   1299     [[[NSColor blackColor] colorWithAlphaComponent:0.66] set];
   1300     NSRectFillUsingOperation([webView frame], NSCompositeSourceOver);
   1301     [webView unlockFocus];
   1302 }
   1303 
   1304 @implementation DumpRenderTreeEvent
   1305 
   1306 + (NSPoint)mouseLocation
   1307 {
   1308     return [[[mainFrame webView] window] convertBaseToScreen:lastMousePosition];
   1309 }
   1310 
   1311 @end
   1312