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