2 * Copyright (C) 2005, 2006, 2007, 2008, 2009 Apple Inc. All rights reserved.
3 * (C) 2007 Graham Dennis (graham.dennis@gmail.com)
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
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.
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.
31 #import "DumpRenderTree.h"
33 #import "AccessibilityController.h"
34 #import "CheckedMalloc.h"
35 #import "DefaultPolicyDelegate.h"
36 #import "DumpRenderTreeDraggingInfo.h"
37 #import "DumpRenderTreePasteboard.h"
38 #import "DumpRenderTreeWindow.h"
39 #import "EditingDelegate.h"
40 #import "EventSendingController.h"
41 #import "FrameLoadDelegate.h"
42 #import "HistoryDelegate.h"
43 #import "JavaScriptThreading.h"
44 #import "TestRunner.h"
45 #import "MockGeolocationProvider.h"
46 #import "MockWebNotificationProvider.h"
47 #import "NavigationController.h"
48 #import "ObjCPlugin.h"
49 #import "ObjCPluginFunction.h"
50 #import "PixelDumpSupport.h"
51 #import "PolicyDelegate.h"
52 #import "ResourceLoadDelegate.h"
53 #import "StorageTrackerDelegate.h"
54 #import "UIDelegate.h"
55 #import "WebArchiveDumpSupport.h"
56 #import "WebCoreTestSupport.h"
58 #import "WorkQueueItem.h"
59 #import <CoreFoundation/CoreFoundation.h>
60 #import <JavaScriptCore/HeapStatistics.h>
61 #import <JavaScriptCore/Options.h>
62 #import <WebKit/DOMElement.h>
63 #import <WebKit/DOMExtensions.h>
64 #import <WebKit/DOMRange.h>
65 #import <WebKit/WebArchive.h>
66 #import <WebKit/WebBackForwardList.h>
67 #import <WebKit/WebCache.h>
68 #import <WebKit/WebCoreStatistics.h>
69 #import <WebKit/WebDataSourcePrivate.h>
70 #import <WebKit/WebDatabaseManagerPrivate.h>
71 #import <WebKit/WebDocumentPrivate.h>
72 #import <WebKit/WebDeviceOrientationProviderMock.h>
73 #import <WebKit/WebEditingDelegate.h>
74 #import <WebKit/WebFrameView.h>
75 #import <WebKit/WebHistory.h>
76 #import <WebKit/WebHistoryItemPrivate.h>
77 #import <WebKit/WebInspector.h>
78 #import <WebKit/WebKitNSStringExtras.h>
79 #import <WebKit/WebPluginDatabase.h>
80 #import <WebKit/WebPreferences.h>
81 #import <WebKit/WebPreferencesPrivate.h>
82 #import <WebKit/WebPreferenceKeysPrivate.h>
83 #import <WebKit/WebResourceLoadDelegate.h>
84 #import <WebKit/WebStorageManagerPrivate.h>
85 #import <WebKit/WebViewPrivate.h>
87 #import <wtf/Assertions.h>
88 #import <wtf/FastMalloc.h>
89 #import <wtf/RetainPtr.h>
90 #import <wtf/Threading.h>
91 #import <wtf/ObjcRuntimeExtras.h>
92 #import <wtf/OwnPtr.h>
95 #import <Carbon/Carbon.h>
96 #import <WebKit/WebDynamicScrollBarsView.h>
100 #import <CoreGraphics/CGFontDB.h>
101 #import <GraphicsServices/GSFont.h>
102 #import <QuartzCore/QuartzCore.h>
103 #import <UIKit/UIApplication_Private.h>
104 #import <UIKit/UIMath.h>
105 #import <UIKit/UIWebBrowserView.h>
106 #import <UIKit/UIWebScrollView.h>
107 #import <WebKit/WAKViewPrivate.h>
108 #import <WebKit/WAKWindow.h>
109 #import <WebKit/WebCoreThread.h>
110 #import <WebKit/WebCoreThreadRun.h>
111 #import <WebKit/WebDOMOperations.h>
113 #import "DumpRenderTreeBrowserView.h"
117 #import <mach-o/getsect.h>
123 @interface DumpRenderTreeApplication : NSApplication
126 @interface DumpRenderTreeEvent : NSEvent
129 @interface ScrollViewResizerDelegate : NSObject
132 @implementation ScrollViewResizerDelegate
133 - (void)view:(UIWebDocumentView *)view didSetFrame:(CGRect)newFrame oldFrame:(CGRect)oldFrame asResultOfZoom:(BOOL)wasResultOfZoom
135 UIView *scrollView = [view superview];
136 while (![scrollView isKindOfClass:[UIWebScrollView class]])
137 scrollView = [scrollView superview];
139 ASSERT(scrollView && [scrollView isKindOfClass:[UIWebScrollView class]]);
140 const CGSize scrollViewSize = [scrollView bounds].size;
141 CGSize contentSize = newFrame.size;
142 contentSize.height = _ROUNDF_(MAX(CGRectGetMaxY(newFrame), scrollViewSize.height));
143 [(UIWebScrollView *)scrollView setContentSize:contentSize];
148 @interface NSURLRequest (PrivateThingsWeShouldntReallyUse)
149 +(void)setAllowsAnyHTTPSCertificate:(BOOL)allow forHost:(NSString *)host;
153 @interface NSSound (Details)
154 + (void)_setAlertType:(NSUInteger)alertType;
158 static void runTest(const string& testPathOrURL);
160 // Deciding when it's OK to dump out the state is a bit tricky. All these must be true:
161 // - There is no load in progress
162 // - There is no work queued up (see workQueue var, below)
163 // - waitToDump==NO. This means either waitUntilDone was never called, or it was called
164 // and notifyDone was called subsequently.
165 // Note that the call to notifyDone and the end of the load can happen in either order.
169 NavigationController* gNavigationController = 0;
170 RefPtr<TestRunner> gTestRunner;
172 WebFrame *mainFrame = 0;
173 // This is the topmost frame that is loading, during a given load, or nil when no load is
174 // in progress. Usually this is the same as the main frame, but not always. In the case
175 // where a frameset is loaded, and then new content is loaded into one of the child frames,
176 // that child frame is the "topmost frame that is loading".
177 WebFrame *topLoadingFrame = nil; // !nil iff a load is in progress
180 CFMutableSetRef disallowedURLs = 0;
181 static CFRunLoopTimerRef waitToDumpWatchdog = 0;
184 static FrameLoadDelegate *frameLoadDelegate;
185 static UIDelegate *uiDelegate;
186 static EditingDelegate *editingDelegate;
187 static ResourceLoadDelegate *resourceLoadDelegate;
188 static HistoryDelegate *historyDelegate;
189 PolicyDelegate *policyDelegate;
190 DefaultPolicyDelegate *defaultPolicyDelegate;
191 StorageTrackerDelegate *storageDelegate;
193 static ScrollViewResizerDelegate *scrollViewResizerDelegate;
196 static int dumpPixelsForAllTests = NO;
197 static bool dumpPixelsForCurrentTest = false;
199 static int dumpTree = YES;
200 static int useTimeoutWatchdog = YES;
201 static int forceComplexText;
202 static int useAcceleratedDrawing;
203 static int gcBetweenTests;
204 static BOOL printSeparators;
205 static RetainPtr<CFStringRef> persistentUserStyleSheetLocation;
207 static WebHistoryItem *prevTestBFItem = nil; // current b/f item at the end of the previous test
210 const unsigned phoneViewHeight = 480;
211 const unsigned phoneViewWidth = 320;
212 const unsigned phoneBrowserScrollViewHeight = 416;
213 const unsigned phoneBrowserAddressBarOffset = 60;
214 const CGRect layoutTestViewportRect = { {0, 0}, {static_cast<CGFloat>(TestRunner::viewWidth), static_cast<CGFloat>(TestRunner::viewHeight)} };
215 UIWebBrowserView *gWebBrowserView = nil;
216 UIWebScrollView *gWebScrollView = nil;
217 DumpRenderTreeWindow *gDrtWindow = nil;
221 static void swizzleAllMethods(Class imposter, Class original)
223 unsigned int imposterMethodCount;
224 Method* imposterMethods = class_copyMethodList(imposter, &imposterMethodCount);
226 unsigned int originalMethodCount;
227 Method* originalMethods = class_copyMethodList(original, &originalMethodCount);
229 for (unsigned int i = 0; i < imposterMethodCount; i++) {
230 SEL imposterMethodName = method_getName(imposterMethods[i]);
232 // Attempt to add the method to the original class. If it fails, the method already exists and we should
233 // instead exchange the implementations.
234 if (class_addMethod(original, imposterMethodName, method_getImplementation(imposterMethods[i]), method_getTypeEncoding(imposterMethods[i])))
238 for (; j < originalMethodCount; j++) {
239 SEL originalMethodName = method_getName(originalMethods[j]);
240 if (sel_isEqual(imposterMethodName, originalMethodName))
244 // If class_addMethod failed above then the method must exist on the original class.
245 ASSERT(j < originalMethodCount);
246 method_exchangeImplementations(imposterMethods[i], originalMethods[j]);
249 free(imposterMethods);
250 free(originalMethods);
254 static void poseAsClass(const char* imposter, const char* original)
256 Class imposterClass = objc_getClass(imposter);
257 Class originalClass = objc_getClass(original);
260 class_poseAs(imposterClass, originalClass);
263 // Swizzle instance methods
264 swizzleAllMethods(imposterClass, originalClass);
265 // and then class methods
266 swizzleAllMethods(object_getClass(imposterClass), object_getClass(originalClass));
270 void setPersistentUserStyleSheetLocation(CFStringRef url)
272 persistentUserStyleSheetLocation = url;
275 static bool shouldIgnoreWebCoreNodeLeaks(const string& URLString)
277 static char* const ignoreSet[] = {
278 // Keeping this infrastructure around in case we ever need it again.
280 static const int ignoreSetCount = sizeof(ignoreSet) / sizeof(char*);
282 for (int i = 0; i < ignoreSetCount; i++) {
283 // FIXME: ignore case
284 string curIgnore(ignoreSet[i]);
285 // Match at the end of the URLString
286 if (!URLString.compare(URLString.length() - curIgnore.length(), curIgnore.length(), curIgnore))
293 static NSSet *allowedFontFamilySet()
295 static NSSet *fontFamilySet = [[NSSet setWithObjects:
298 @"American Typewriter",
301 @"Apple Color Emoji",
303 @"Apple Garamond BT",
312 @"Arial Rounded MT Bold",
352 @"Hiragino Kaku Gothic Pro",
353 @"Hiragino Kaku Gothic ProN",
354 @"Hiragino Kaku Gothic Std",
355 @"Hiragino Kaku Gothic StdN",
356 @"Hiragino Maru Gothic Monospaced",
357 @"Hiragino Maru Gothic Pro",
358 @"Hiragino Maru Gothic ProN",
359 @"Hiragino Mincho Pro",
360 @"Hiragino Mincho ProN",
375 @"Microsoft Sans Serif",
385 @"Plantagenet Cherokee",
406 @"WebKit WeightWatcher",
414 return fontFamilySet;
417 static NSSet *systemHiddenFontFamilySet()
419 static NSSet *fontFamilySet = [[NSSet setWithObjects:
423 return fontFamilySet;
426 static IMP appKitAvailableFontFamiliesIMP;
427 static IMP appKitAvailableFontsIMP;
429 static NSArray *drt_NSFontManager_availableFontFamilies(id self, SEL _cmd)
431 static NSArray *availableFontFamilies;
432 if (availableFontFamilies)
433 return availableFontFamilies;
435 NSArray *availableFamilies = wtfCallIMP<id>(appKitAvailableFontFamiliesIMP, self, _cmd);
437 NSMutableSet *prunedFamiliesSet = [NSMutableSet setWithArray:availableFamilies];
438 [prunedFamiliesSet intersectSet:allowedFontFamilySet()];
440 availableFontFamilies = [[prunedFamiliesSet allObjects] retain];
441 return availableFontFamilies;
444 static NSArray *drt_NSFontManager_availableFonts(id self, SEL _cmd)
446 static NSArray *availableFonts;
448 return availableFonts;
450 NSSet *allowedFamilies = allowedFontFamilySet();
451 NSMutableArray *availableFontList = [[NSMutableArray alloc] initWithCapacity:[allowedFamilies count] * 2];
452 for (NSString *fontFamily in allowedFontFamilySet()) {
453 NSArray* fontsForFamily = [[NSFontManager sharedFontManager] availableMembersOfFontFamily:fontFamily];
454 for (NSArray* fontInfo in fontsForFamily) {
455 // Font name is the first entry in the array.
456 [availableFontList addObject:[fontInfo objectAtIndex:0]];
460 for (NSString *hiddenFontFamily in systemHiddenFontFamilySet()) {
461 [availableFontList addObject:hiddenFontFamily];
464 availableFonts = availableFontList;
465 return availableFonts;
468 static void swizzleNSFontManagerMethods()
470 Method availableFontFamiliesMethod = class_getInstanceMethod(objc_getClass("NSFontManager"), @selector(availableFontFamilies));
471 ASSERT(availableFontFamiliesMethod);
472 if (!availableFontFamiliesMethod) {
473 NSLog(@"Failed to swizzle the \"availableFontFamilies\" method on NSFontManager");
477 appKitAvailableFontFamiliesIMP = method_setImplementation(availableFontFamiliesMethod, (IMP)drt_NSFontManager_availableFontFamilies);
479 Method availableFontsMethod = class_getInstanceMethod(objc_getClass("NSFontManager"), @selector(availableFonts));
480 ASSERT(availableFontsMethod);
481 if (!availableFontsMethod) {
482 NSLog(@"Failed to swizzle the \"availableFonts\" method on NSFontManager");
486 appKitAvailableFontsIMP = method_setImplementation(availableFontsMethod, (IMP)drt_NSFontManager_availableFonts);
489 static void activateTestingFonts()
491 static const char* fontFileNames[] = {
493 "WebKitWeightWatcher100.ttf",
494 "WebKitWeightWatcher200.ttf",
495 "WebKitWeightWatcher300.ttf",
496 "WebKitWeightWatcher400.ttf",
497 "WebKitWeightWatcher500.ttf",
498 "WebKitWeightWatcher600.ttf",
499 "WebKitWeightWatcher700.ttf",
500 "WebKitWeightWatcher800.ttf",
501 "WebKitWeightWatcher900.ttf",
502 #if __MAC_OS_X_VERSION_MIN_REQUIRED >= 1080
508 NSMutableArray *fontURLs = [NSMutableArray array];
509 NSURL *resourcesDirectory = [NSURL URLWithString:@"DumpRenderTree.resources" relativeToURL:[[NSBundle mainBundle] executableURL]];
510 for (unsigned i = 0; fontFileNames[i]; ++i) {
511 NSURL *fontURL = [resourcesDirectory URLByAppendingPathComponent:[NSString stringWithUTF8String:fontFileNames[i]]];
512 [fontURLs addObject:[fontURL absoluteURL]];
515 CFArrayRef errors = 0;
516 if (!CTFontManagerRegisterFontsForURLs((CFArrayRef)fontURLs, kCTFontManagerScopeProcess, &errors)) {
517 NSLog(@"Failed to activate fonts: %@", errors);
523 static void adjustFonts()
525 swizzleNSFontManagerMethods();
526 activateTestingFonts();
529 static void activateFontsIOS()
531 static const char* fontSectionNames[] = {
545 for (unsigned i = 0; fontSectionNames[i]; ++i) {
546 unsigned long fontDataLength;
547 char* fontData = getsectdata("__DATA", fontSectionNames[i], &fontDataLength);
549 fprintf(stderr, "Failed to locate the %s font.\n", fontSectionNames[i]);
553 CGDataProviderRef data = CGDataProviderCreateWithData(NULL, fontData, fontDataLength, NULL);
555 fprintf(stderr, "Failed to create CGDataProviderRef for the %s font.\n", fontSectionNames[i]);
559 CGFontRef cgFont = CGFontCreateWithDataProvider(data);
560 CGDataProviderRelease(data);
562 fprintf(stderr, "Failed to create CGFontRef for the %s font.\n", fontSectionNames[i]);
566 if (!GSFontAddCGFont(cgFont)) {
567 fprintf(stderr, "Failed to add CGFont to GraphicsServices for the %s font.\n", fontSectionNames[i]);
570 CGFontRelease(cgFont);
573 #endif // !PLATFORM(IOS)
577 void adjustWebDocumentForFlexibleViewport(UIWebBrowserView *webBrowserView, UIWebScrollView *scrollView)
579 // These values match MobileSafari's, see -[TabDocument _createDocumentView].
580 [webBrowserView setMinimumScale:0.25f forDocumentTypes:UIEveryDocumentMask];
581 [webBrowserView setMaximumScale:5.0f forDocumentTypes:UIEveryDocumentMask];
582 [webBrowserView setInitialScale:UIWebViewScalesToFitScale forDocumentTypes:UIEveryDocumentMask];
583 [webBrowserView setViewportSize:CGSizeMake(UIWebViewStandardViewportWidth, UIWebViewGrowsAndShrinksToFitHeight) forDocumentTypes:UIEveryDocumentMask];
585 // Adjust the viewport view and viewport to have similar behavior
587 [(DumpRenderTreeBrowserView *)webBrowserView setScrollingUsesUIWebScrollView:YES];
588 [webBrowserView setDelegate:scrollViewResizerDelegate];
590 CGRect viewportRect = CGRectMake(0, 0, phoneViewWidth, phoneBrowserScrollViewHeight);
591 [scrollView setBounds:viewportRect];
592 [scrollView setFrame:viewportRect];
594 [webBrowserView setMinimumSize:viewportRect.size];
595 [webBrowserView setAutoresizes:YES];
596 CGRect browserViewFrame = [webBrowserView frame];
597 browserViewFrame.origin = CGPointMake(0, phoneBrowserAddressBarOffset);
598 [webBrowserView setFrame:browserViewFrame];
601 void adjustWebDocumentForStandardViewport(UIWebBrowserView *webBrowserView, UIWebScrollView *scrollView)
603 [webBrowserView setMinimumScale:1.0f forDocumentTypes:UIEveryDocumentMask];
604 [webBrowserView setMaximumScale:5.0f forDocumentTypes:UIEveryDocumentMask];
605 [webBrowserView setInitialScale:1.0f forDocumentTypes:UIEveryDocumentMask];
607 [(DumpRenderTreeBrowserView *)webBrowserView setScrollingUsesUIWebScrollView:NO];
608 [webBrowserView setDelegate: nil];
610 [scrollView setBounds:layoutTestViewportRect];
611 [scrollView setFrame:layoutTestViewportRect];
613 [webBrowserView setMinimumSize:layoutTestViewportRect.size];
614 [webBrowserView setAutoresizes:NO];
615 CGRect browserViewFrame = [webBrowserView frame];
616 browserViewFrame.origin = CGPointZero;
617 [webBrowserView setFrame:browserViewFrame];
622 @interface DRTMockScroller : NSScroller
625 @implementation DRTMockScroller
627 - (NSRect)rectForPart:(NSScrollerPart)partCode
629 if (partCode != NSScrollerKnob)
630 return [super rectForPart:partCode];
632 NSRect frameRect = [self frame];
633 NSRect bounds = [self bounds];
634 BOOL isHorizontal = frameRect.size.width > frameRect.size.height;
635 CGFloat trackLength = isHorizontal ? bounds.size.width : bounds.size.height;
636 CGFloat minKnobSize = isHorizontal ? bounds.size.height : bounds.size.width;
637 CGFloat knobLength = max(minKnobSize, static_cast<CGFloat>(round(trackLength * [self knobProportion])));
638 CGFloat knobPosition = static_cast<CGFloat>((round([self doubleValue] * (trackLength - knobLength))));
641 return NSMakeRect(bounds.origin.x + knobPosition, bounds.origin.y, knobLength, bounds.size.height);
643 return NSMakeRect(bounds.origin.x, bounds.origin.y + + knobPosition, bounds.size.width, knobLength);
648 if (![self isEnabled])
651 NSRect knobRect = [self rectForPart:NSScrollerKnob];
653 static NSColor *knobColor = [[NSColor colorWithDeviceRed:0x80 / 255.0 green:0x80 / 255.0 blue:0x80 / 255.0 alpha:1] retain];
656 NSRectFill(knobRect);
659 - (void)drawRect:(NSRect)dirtyRect
661 static NSColor *trackColor = [[NSColor colorWithDeviceRed:0xC0 / 255.0 green:0xC0 / 255.0 blue:0xC0 / 255.0 alpha:1] retain];
662 static NSColor *disabledTrackColor = [[NSColor colorWithDeviceRed:0xE0 / 255.0 green:0xE0 / 255.0 blue:0xE0 / 255.0 alpha:1] retain];
664 if ([self isEnabled])
667 [disabledTrackColor set];
669 NSRectFill(dirtyRect);
676 static void registerMockScrollbars()
678 [WebDynamicScrollBarsView setCustomScrollerClass:[DRTMockScroller class]];
682 WebView *createWebViewAndOffscreenWindow()
685 NSRect rect = NSMakeRect(0, 0, TestRunner::viewWidth, TestRunner::viewHeight);
686 WebView *webView = [[WebView alloc] initWithFrame:rect frameName:nil groupName:@"org.webkit.DumpRenderTree"];
688 UIWebBrowserView *webBrowserView = [[[DumpRenderTreeBrowserView alloc] initWithFrame:layoutTestViewportRect] autorelease];
690 WebView *webView = [[webBrowserView webView] retain];
691 [webView setGroupName:@"org.webkit.DumpRenderTree"];
694 [webView setUIDelegate:uiDelegate];
695 [webView setFrameLoadDelegate:frameLoadDelegate];
696 [webView setEditingDelegate:editingDelegate];
697 [webView setResourceLoadDelegate:resourceLoadDelegate];
698 [webView _setGeolocationProvider:[MockGeolocationProvider shared]];
699 [webView _setDeviceOrientationProvider:[WebDeviceOrientationProviderMock shared]];
700 [webView _setNotificationProvider:[MockWebNotificationProvider shared]];
702 // Register the same schemes that Safari does
703 [WebView registerURLSchemeAsLocal:@"feed"];
704 [WebView registerURLSchemeAsLocal:@"feeds"];
705 [WebView registerURLSchemeAsLocal:@"feedsearch"];
708 [webView setContinuousSpellCheckingEnabled:YES];
709 [webView setAutomaticQuoteSubstitutionEnabled:NO];
710 [webView setAutomaticLinkDetectionEnabled:NO];
711 [webView setAutomaticDashSubstitutionEnabled:NO];
712 [webView setAutomaticTextReplacementEnabled:NO];
713 [webView setAutomaticSpellingCorrectionEnabled:YES];
714 [webView setGrammarCheckingEnabled:YES];
716 [webView setDefersCallbacks:NO];
717 [webView setInteractiveFormValidationEnabled:YES];
718 [webView setValidationMessageTimerMagnification:-1];
720 // 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.
721 // Put it at -10000, -10000 in "flipped coordinates", since WebCore and the DOM use flipped coordinates.
722 NSRect windowRect = NSOffsetRect(rect, -10000, [(NSScreen *)[[NSScreen screens] objectAtIndex:0] frame].size.height - rect.size.height + 10000);
723 DumpRenderTreeWindow *window = [[DumpRenderTreeWindow alloc] initWithContentRect:windowRect styleMask:NSBorderlessWindowMask backing:NSBackingStoreBuffered defer:YES];
725 [window setColorSpace:[[NSScreen mainScreen] colorSpace]];
726 [window setCollectionBehavior:NSWindowCollectionBehaviorStationary];
727 [[window contentView] addSubview:webView];
728 [window orderBack:nil];
729 [window setAutodisplay:NO];
731 [window startListeningForAcceleratedCompositingChanges];
733 DumpRenderTreeWindow *drtWindow = [[DumpRenderTreeWindow alloc] initWithLayer:[webBrowserView layer]];
734 [drtWindow setContentView:webView];
735 [webBrowserView setWAKWindow:drtWindow];
737 [[webView window] makeFirstResponder:[[[webView mainFrame] frameView] documentView]];
739 CGRect uiWindowRect = layoutTestViewportRect;
740 uiWindowRect.origin.y += [UIApp statusBarHeight];
741 UIWindow *uiWindow = [[[UIWindow alloc] initWithFrame:uiWindowRect] autorelease];
743 // The UIWindow and UIWebBrowserView are released when the DumpRenderTreeWindow is closed.
744 drtWindow.uiWindow = uiWindow;
745 drtWindow.browserView = webBrowserView;
747 UIWebScrollView *scrollView = [[UIWebScrollView alloc] initWithFrame:layoutTestViewportRect];
748 [scrollView addSubview:webBrowserView];
750 [uiWindow addSubview:scrollView];
751 [scrollView release];
753 adjustWebDocumentForStandardViewport(webBrowserView, scrollView);
757 // For reasons that are not entirely clear, the following pair of calls makes WebView handle its
758 // dynamic scrollbars properly. Without it, every frame will always have scrollbars.
759 NSBitmapImageRep *imageRep = [webView bitmapImageRepForCachingDisplayInRect:[webView bounds]];
760 [webView cacheDisplayInRect:[webView bounds] toBitmapImageRep:imageRep];
762 [[webView mainFrame] _setVisibleSize:CGSizeMake(phoneViewWidth, phoneViewHeight)];
763 [[webView preferences] _setTelephoneNumberParsingEnabled:NO];
765 // Initialize the global UIViews, and set the key UIWindow to be painted.
766 if (!gWebBrowserView) {
767 gWebBrowserView = [webBrowserView retain];
768 gWebScrollView = [scrollView retain];
769 gDrtWindow = [drtWindow retain];
770 [uiWindow makeKeyAndVisible];
778 static NSString *libraryPathForDumpRenderTree()
780 //FIXME: This may not be sufficient to prevent interactions/crashes
781 //when running more than one copy of DumpRenderTree.
782 //See https://bugs.webkit.org/show_bug.cgi?id=10906
783 char* dumpRenderTreeTemp = getenv("DUMPRENDERTREE_TEMP");
784 if (dumpRenderTreeTemp)
785 return [[NSFileManager defaultManager] stringWithFileSystemRepresentation:dumpRenderTreeTemp length:strlen(dumpRenderTreeTemp)];
787 return [@"~/Library/Application Support/DumpRenderTree" stringByExpandingTildeInPath];
790 // Called before each test.
791 static void resetWebPreferencesToConsistentValues()
793 WebPreferences *preferences = [WebPreferences standardPreferences];
795 [preferences setAllowUniversalAccessFromFileURLs:YES];
796 [preferences setAllowFileAccessFromFileURLs:YES];
797 [preferences setStandardFontFamily:@"Times"];
798 [preferences setFixedFontFamily:@"Courier"];
799 [preferences setSerifFontFamily:@"Times"];
800 [preferences setSansSerifFontFamily:@"Helvetica"];
801 [preferences setCursiveFontFamily:@"Apple Chancery"];
802 [preferences setFantasyFontFamily:@"Papyrus"];
803 [preferences setPictographFontFamily:@"Apple Color Emoji"];
804 [preferences setDefaultFontSize:16];
805 [preferences setDefaultFixedFontSize:13];
806 [preferences setMinimumFontSize:0];
807 [preferences setDefaultTextEncodingName:@"ISO-8859-1"];
808 [preferences setJavaEnabled:NO];
809 [preferences setJavaScriptEnabled:YES];
810 [preferences setEditableLinkBehavior:WebKitEditableLinkOnlyLiveWithShiftKey];
812 [preferences setTabsToLinks:NO];
814 [preferences setDOMPasteAllowed:YES];
816 [preferences setShouldPrintBackgrounds:YES];
818 [preferences setCacheModel:WebCacheModelDocumentBrowser];
819 [preferences setXSSAuditorEnabled:NO];
820 [preferences setExperimentalNotificationsEnabled:NO];
821 [preferences setPlugInsEnabled:YES];
823 [preferences setTextAreasAreResizable:YES];
826 [preferences setPrivateBrowsingEnabled:NO];
827 [preferences setAuthorAndUserStylesEnabled:YES];
828 [preferences setJavaScriptCanOpenWindowsAutomatically:YES];
829 [preferences setJavaScriptCanAccessClipboard:YES];
830 [preferences setOfflineWebApplicationCacheEnabled:YES];
831 [preferences setDeveloperExtrasEnabled:NO];
832 [preferences setJavaScriptExperimentsEnabled:YES];
833 [preferences setLoadsImagesAutomatically:YES];
834 [preferences setLoadsSiteIconsIgnoringImageLoadingPreference:NO];
835 [preferences setFrameFlatteningEnabled:NO];
836 [preferences setSpatialNavigationEnabled:NO];
837 if (persistentUserStyleSheetLocation) {
838 [preferences setUserStyleSheetLocation:[NSURL URLWithString:(NSString *)(persistentUserStyleSheetLocation.get())]];
839 [preferences setUserStyleSheetEnabled:YES];
841 [preferences setUserStyleSheetEnabled:NO];
843 [preferences setMediaPlaybackAllowsInline:YES];
844 [preferences setMediaPlaybackRequiresUserGesture:NO];
846 // Enable the tracker before creating the first WebView will
847 // cause initialization to use the correct database paths.
848 [preferences setStorageTrackerEnabled:YES];
851 #if ENABLE(IOS_TEXT_AUTOSIZING)
852 // Disable text autosizing by default.
853 [preferences _setMinimumZoomFontSize:0];
856 // The back/forward cache is causing problems due to layouts during transition from one page to another.
857 // So, turn it off for now, but we might want to turn it back on some day.
858 [preferences setUsesPageCache:NO];
859 [preferences setAcceleratedCompositingEnabled:YES];
861 [preferences setCanvasUsesAcceleratedDrawing:YES];
862 [preferences setAcceleratedDrawingEnabled:useAcceleratedDrawing];
864 [preferences setWebGLEnabled:NO];
865 [preferences setCSSRegionsEnabled:YES];
866 [preferences setCSSGridLayoutEnabled:NO];
867 [preferences setUsePreHTML5ParserQuirks:NO];
868 [preferences setAsynchronousSpellCheckingEnabled:NO];
869 [preferences setMockScrollbarsEnabled:YES];
870 [preferences setSeamlessIFramesEnabled:YES];
872 #if ENABLE(WEB_AUDIO)
873 [preferences setWebAudioEnabled:YES];
876 #if ENABLE(IOS_TEXT_AUTOSIZING)
877 // Disable text autosizing by default.
878 [preferences _setMinimumZoomFontSize:0];
881 [preferences setScreenFontSubstitutionEnabled:YES];
883 #if ENABLE(MEDIA_SOURCE)
884 [preferences setMediaSourceEnabled:YES];
887 [WebPreferences _setCurrentNetworkLoaderSessionCookieAcceptPolicy:NSHTTPCookieAcceptPolicyOnlyFromMainDocumentDomain];
890 // Called once on DumpRenderTree startup.
891 static void setDefaultsToConsistentValuesForTesting()
897 static const int NoFontSmoothing = 0;
898 static const int BlueTintedAppearance = 1;
900 NSString *libraryPath = libraryPathForDumpRenderTree();
902 NSDictionary *dict = @{
903 @"AppleKeyboardUIMode": @1,
904 @"AppleMagnifiedMode": @YES,
905 @"AppleAntiAliasingThreshold": @4,
906 @"AppleFontSmoothing": @(NoFontSmoothing),
907 @"AppleAquaColorVariant": @(BlueTintedAppearance),
908 @"AppleHighlightColor": @"0.709800 0.835300 1.000000",
909 @"AppleOtherHighlightColor":@"0.500000 0.500000 0.500000",
910 @"AppleLanguages": @[ @"en" ],
911 WebKitEnableFullDocumentTeardownPreferenceKey: @YES,
912 WebKitFullScreenEnabledPreferenceKey: @YES,
913 @"UseWebKitWebInspector": @YES,
915 @"NSTestCorrectionDictionary": @{
916 @"notationl": @"notational",
917 @"mesage": @"message",
919 @"wellcome": @"welcome",
920 @"hellolfworld": @"hello\nworld"
923 @"WebKitKerningAndLigaturesEnabledByDefault": @NO,
924 @"AppleScrollBarVariant": @"DoubleMax",
926 @"NSScrollAnimationEnabled": @NO,
928 @"NSOverlayScrollersEnabled": @NO,
929 @"AppleShowScrollBars": @"Always",
930 WebDatabaseDirectoryDefaultsKey: [libraryPath stringByAppendingPathComponent:@"Databases"],
931 WebStorageDirectoryDefaultsKey: [libraryPath stringByAppendingPathComponent:@"LocalStorage"],
932 WebKitLocalCacheDefaultsKey: [libraryPath stringByAppendingPathComponent:@"LocalCache"]
935 [[NSUserDefaults standardUserDefaults] setValuesForKeysWithDictionary:dict];
938 static void runThread(void* arg)
940 static ThreadIdentifier previousId = 0;
941 ThreadIdentifier currentId = currentThread();
942 // Verify 2 successive threads do not get the same Id.
943 ASSERT(previousId != currentId);
944 previousId = currentId;
947 static void* runPthread(void* arg)
953 static void testThreadIdentifierMap()
955 // Imitate 'foreign' threads that are not created by WTF.
957 pthread_create(&pthread, 0, &runPthread, 0);
958 pthread_join(pthread, 0);
960 pthread_create(&pthread, 0, &runPthread, 0);
961 pthread_join(pthread, 0);
963 // Now create another thread using WTF. On OSX, it will have the same pthread handle
964 // but should get a different ThreadIdentifier.
965 createThread(runThread, 0, "DumpRenderTree: test");
968 static void allocateGlobalControllers()
970 // FIXME: We should remove these and move to the ObjC standard [Foo sharedInstance] model
971 gNavigationController = [[NavigationController alloc] init];
972 frameLoadDelegate = [[FrameLoadDelegate alloc] init];
973 uiDelegate = [[UIDelegate alloc] init];
974 editingDelegate = [[EditingDelegate alloc] init];
975 resourceLoadDelegate = [[ResourceLoadDelegate alloc] init];
976 policyDelegate = [[PolicyDelegate alloc] init];
977 historyDelegate = [[HistoryDelegate alloc] init];
978 storageDelegate = [[StorageTrackerDelegate alloc] init];
979 defaultPolicyDelegate = [[DefaultPolicyDelegate alloc] init];
981 scrollViewResizerDelegate = [[ScrollViewResizerDelegate alloc] init];
985 // ObjC++ doens't seem to let me pass NSObject*& sadly.
986 static inline void releaseAndZero(NSObject** object)
992 static void releaseGlobalControllers()
994 releaseAndZero(&gNavigationController);
995 releaseAndZero(&frameLoadDelegate);
996 releaseAndZero(&editingDelegate);
997 releaseAndZero(&resourceLoadDelegate);
998 releaseAndZero(&uiDelegate);
999 releaseAndZero(&policyDelegate);
1000 releaseAndZero(&storageDelegate);
1002 releaseAndZero(&scrollViewResizerDelegate);
1006 static void initializeGlobalsFromCommandLineOptions(int argc, const char *argv[])
1008 struct option options[] = {
1009 {"notree", no_argument, &dumpTree, NO},
1010 {"pixel-tests", no_argument, &dumpPixelsForAllTests, YES},
1011 {"tree", no_argument, &dumpTree, YES},
1012 {"threaded", no_argument, &threaded, YES},
1013 {"complex-text", no_argument, &forceComplexText, YES},
1014 {"accelerated-drawing", no_argument, &useAcceleratedDrawing, YES},
1015 {"gc-between-tests", no_argument, &gcBetweenTests, YES},
1016 {"no-timeout", no_argument, &useTimeoutWatchdog, NO},
1021 while ((option = getopt_long(argc, (char * const *)argv, "", options, NULL)) != -1) {
1023 case '?': // unknown or ambiguous option
1024 case ':': // missing argument
1031 static void addTestPluginsToPluginSearchPath(const char* executablePath)
1034 NSString *pwd = [[NSString stringWithUTF8String:executablePath] stringByDeletingLastPathComponent];
1035 [WebPluginDatabase setAdditionalWebPlugInPaths:[NSArray arrayWithObject:pwd]];
1036 [[WebPluginDatabase sharedDatabase] refresh];
1040 static bool useLongRunningServerMode(int argc, const char *argv[])
1042 // This assumes you've already called getopt_long
1043 return (argc == optind+1 && strcmp(argv[optind], "-") == 0);
1046 static void runTestingServerLoop()
1048 // When DumpRenderTree run in server mode, we just wait around for file names
1049 // to be passed to us and read each in turn, passing the results back to the client
1050 char filenameBuffer[2048];
1051 while (fgets(filenameBuffer, sizeof(filenameBuffer), stdin)) {
1052 char *newLineCharacter = strchr(filenameBuffer, '\n');
1053 if (newLineCharacter)
1054 *newLineCharacter = '\0';
1056 if (strlen(filenameBuffer) == 0)
1059 runTest(filenameBuffer);
1063 static void prepareConsistentTestingEnvironment()
1066 poseAsClass("DumpRenderTreePasteboard", "NSPasteboard");
1067 poseAsClass("DumpRenderTreeEvent", "NSEvent");
1069 poseAsClass("DumpRenderTreeEvent", "GSEvent");
1072 [[WebPreferences standardPreferences] setAutosaves:NO];
1076 // FIXME: We'd like to start with a clean state for every test, but this function can't be used more than once yet.
1077 [WebPreferences _switchNetworkLoaderToNewTestingSession];
1079 NSURLCache *sharedCache =
1080 [[NSURLCache alloc] initWithMemoryCapacity:1024 * 1024
1082 diskPath:[libraryPathForDumpRenderTree() stringByAppendingPathComponent:@"URLCache"]];
1083 [NSURLCache setSharedURLCache:sharedCache];
1084 [sharedCache release];
1087 registerMockScrollbars();
1092 allocateGlobalControllers();
1094 makeLargeMallocFailSilently();
1096 #if !PLATFORM(IOS) && __MAC_OS_X_VERSION_MIN_REQUIRED >= 1090
1097 NSActivityOptions options = (NSActivityUserInitiatedAllowingIdleSystemSleep | NSActivityLatencyCritical) & ~(NSActivitySuddenTerminationDisabled | NSActivityAutomaticTerminationDisabled);
1098 static id assertion = [[[NSProcessInfo processInfo] beginActivityWithOptions:options reason:@"DumpRenderTree should not be subject to process suppression"] retain];
1099 ASSERT_UNUSED(assertion, assertion);
1103 void dumpRenderTree(int argc, const char *argv[])
1106 int infd = open("/tmp/DumpRenderTree_IN", O_RDWR);
1107 dup2(infd, STDIN_FILENO);
1108 int outfd = open("/tmp/DumpRenderTree_OUT", O_RDWR);
1109 dup2(outfd, STDOUT_FILENO);
1110 int errfd = open("/tmp/DumpRenderTree_ERROR", O_RDWR | O_NONBLOCK);
1111 dup2(errfd, STDERR_FILENO);
1114 initializeGlobalsFromCommandLineOptions(argc, argv);
1115 prepareConsistentTestingEnvironment();
1116 addTestPluginsToPluginSearchPath(argv[0]);
1118 if (forceComplexText)
1119 [WebView _setAlwaysUsesComplexTextCodePath:YES];
1122 [NSSound _setAlertType:0];
1125 WebView *webView = createWebViewAndOffscreenWindow();
1126 mainFrame = [webView mainFrame];
1128 [[NSURLCache sharedURLCache] removeAllCachedResponses];
1131 [NSURLRequest setAllowsAnyHTTPSCertificate:YES forHost:@"localhost"];
1132 [NSURLRequest setAllowsAnyHTTPSCertificate:YES forHost:@"127.0.0.1"];
1134 // http://webkit.org/b/32689
1135 testThreadIdentifierMap();
1138 startJavaScriptThreads();
1140 if (useLongRunningServerMode(argc, argv)) {
1141 printSeparators = YES;
1142 runTestingServerLoop();
1144 printSeparators = optind < argc - 1;
1145 for (int i = optind; i != argc; ++i)
1150 stopJavaScriptThreads();
1153 NSWindow *window = [webView window];
1159 // Work around problem where registering drag types leaves an outstanding
1160 // "perform selector" on the window, which retains the window. It's a bit
1161 // inelegant and perhaps dangerous to just blow them all away, but in practice
1162 // it probably won't cause any trouble (and this is just a test tool, after all).
1163 [NSObject cancelPreviousPerformRequestsWithTarget:window];
1165 [window close]; // releases when closed
1167 UIWindow *uiWindow = [gWebBrowserView window];
1168 [uiWindow removeFromSuperview];
1174 releaseGlobalControllers();
1177 [DumpRenderTreePasteboard releaseLocalPasteboards];
1180 // FIXME: This should be moved onto TestRunner and made into a HashSet
1181 if (disallowedURLs) {
1182 CFRelease(disallowedURLs);
1195 static const char **_argv;
1197 @implementation DumpRenderTree
1199 - (void)_runDumpRenderTree
1201 dumpRenderTree(_argc, _argv);
1204 - (void)applicationDidFinishLaunching:(NSNotification *)notification
1206 [self performSelectorOnMainThread:@selector(_runDumpRenderTree) withObject:nil waitUntilDone:NO];
1209 - (void)_deferDumpToMainThread
1211 ASSERT(WebThreadIsCurrent());
1213 dispatch_async(dispatch_get_main_queue(), ^{
1218 - (void)_webThreadEventLoopHasRun
1220 ASSERT(!WebThreadIsCurrent());
1221 _hasFlushedWebThreadRunQueue = YES;
1224 - (void)_webThreadInvoked
1226 ASSERT(WebThreadIsCurrent());
1227 dispatch_async(dispatch_get_main_queue(), ^{
1228 [self _webThreadEventLoopHasRun];
1232 // The test can end in response to a delegate callback while there are still methods queued on the Web Thread.
1233 // If we do not ensure the Web Thread has been run, the callback can be done on a WebView that no longer exists.
1234 // To avoid this, _waitForWebThread dispatches a call to the WebThread event loop, actively processing the delegate
1235 // callbacks in the main thread while waiting for the call to be invoked on the Web Thread.
1236 - (void)_waitForWebThread
1238 ASSERT(!WebThreadIsCurrent());
1239 _hasFlushedWebThreadRunQueue = NO;
1241 [self _webThreadInvoked];
1243 while (!_hasFlushedWebThreadRunQueue) {
1244 NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
1245 [[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantPast]];
1253 int DumpRenderTreeMain(int argc, const char *argv[])
1256 _UIApplicationLoadWebKit();
1258 NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
1260 setDefaultsToConsistentValuesForTesting(); // Must be called before NSApplication initialization.
1263 [DumpRenderTreeApplication sharedApplication]; // Force AppKit to init itself
1265 dumpRenderTree(argc, argv);
1269 UIApplicationMain(argc, (char**)argv, @"DumpRenderTree", nil);
1271 [WebCoreStatistics garbageCollectJavaScriptObjects];
1272 [WebCoreStatistics emptyCache]; // Otherwise SVGImages trigger false positives for Frame/Node counts
1273 if (JSC::Options::logHeapStatisticsAtExit())
1274 JSC::HeapStatistics::reportSuccess();
1279 static NSInteger compareHistoryItems(id item1, id item2, void *context)
1281 return [[item1 target] caseInsensitiveCompare:[item2 target]];
1284 static NSData *dumpAudio()
1286 const vector<char>& dataVector = gTestRunner->audioResult();
1288 NSData *data = [NSData dataWithBytes:dataVector.data() length:dataVector.size()];
1292 static void dumpHistoryItem(WebHistoryItem *item, int indent, BOOL current)
1299 for (int i = start; i < indent; i++)
1302 NSString *urlString = [item URLString];
1303 if ([[NSURL URLWithString:urlString] isFileURL]) {
1304 NSRange range = [urlString rangeOfString:@"/LayoutTests/"];
1305 urlString = [@"(file test):" stringByAppendingString:[urlString substringFromIndex:(range.length + range.location)]];
1308 printf("%s", [urlString UTF8String]);
1309 NSString *target = [item target];
1310 if (target && [target length] > 0)
1311 printf(" (in frame \"%s\")", [target UTF8String]);
1312 if ([item isTargetItem])
1313 printf(" **nav target**");
1315 NSArray *kids = [item children];
1317 // must sort to eliminate arbitrary result ordering which defeats reproducible testing
1318 kids = [kids sortedArrayUsingFunction:&compareHistoryItems context:nil];
1319 for (unsigned i = 0; i < [kids count]; i++)
1320 dumpHistoryItem([kids objectAtIndex:i], indent+4, NO);
1324 static void dumpFrameScrollPosition(WebFrame *f)
1326 WebScriptObject* scriptObject = [f windowObject];
1327 NSPoint scrollPosition = NSMakePoint(
1328 [[scriptObject valueForKey:@"pageXOffset"] floatValue],
1329 [[scriptObject valueForKey:@"pageYOffset"] floatValue]);
1330 if (ABS(scrollPosition.x) > 0.00000001 || ABS(scrollPosition.y) > 0.00000001) {
1331 if ([f parentFrame] != nil)
1332 printf("frame '%s' ", [[f name] UTF8String]);
1333 printf("scrolled to %.f,%.f\n", scrollPosition.x, scrollPosition.y);
1336 if (gTestRunner->dumpChildFrameScrollPositions()) {
1337 NSArray *kids = [f childFrames];
1339 for (unsigned i = 0; i < [kids count]; i++)
1340 dumpFrameScrollPosition([kids objectAtIndex:i]);
1344 static NSString *dumpFramesAsText(WebFrame *frame)
1346 DOMDocument *document = [frame DOMDocument];
1347 DOMElement *documentElement = [document documentElement];
1349 if (!documentElement)
1352 NSMutableString *result = [[[NSMutableString alloc] init] autorelease];
1354 // Add header for all but the main frame.
1355 if ([frame parentFrame])
1356 result = [NSMutableString stringWithFormat:@"\n--------\nFrame: '%@'\n--------\n", [frame name]];
1358 [result appendFormat:@"%@\n", [documentElement innerText]];
1360 if (gTestRunner->dumpChildFramesAsText()) {
1361 NSArray *kids = [frame childFrames];
1363 for (unsigned i = 0; i < [kids count]; i++)
1364 [result appendString:dumpFramesAsText([kids objectAtIndex:i])];
1371 static NSData *dumpFrameAsPDF(WebFrame *frame)
1377 // Sadly we have to dump to a file and then read from that file again
1378 // +[NSPrintOperation PDFOperationWithView:insideRect:] requires a rect and prints to a single page
1379 // likewise +[NSView dataWithPDFInsideRect:] also prints to a single continuous page
1380 // The goal of this function is to test "real" printing across multiple pages.
1381 // FIXME: It's possible there might be printing SPI to let us print a multi-page PDF to an NSData object
1382 NSString *path = [libraryPathForDumpRenderTree() stringByAppendingPathComponent:@"test.pdf"];
1384 NSMutableDictionary *printInfoDict = [NSMutableDictionary dictionaryWithDictionary:[[NSPrintInfo sharedPrintInfo] dictionary]];
1385 [printInfoDict setObject:NSPrintSaveJob forKey:NSPrintJobDisposition];
1386 [printInfoDict setObject:path forKey:NSPrintSavePath];
1388 NSPrintInfo *printInfo = [[NSPrintInfo alloc] initWithDictionary:printInfoDict];
1389 [printInfo setHorizontalPagination:NSAutoPagination];
1390 [printInfo setVerticalPagination:NSAutoPagination];
1391 [printInfo setVerticallyCentered:NO];
1393 NSPrintOperation *printOperation = [NSPrintOperation printOperationWithView:[frame frameView] printInfo:printInfo];
1394 [printOperation setShowPanels:NO];
1395 [printOperation runOperation];
1397 [printInfo release];
1399 NSData *pdfData = [NSData dataWithContentsOfFile:path];
1400 [[NSFileManager defaultManager] removeFileAtPath:path handler:nil];
1408 static void dumpBackForwardListForWebView(WebView *view)
1410 printf("\n============== Back Forward List ==============\n");
1411 WebBackForwardList *bfList = [view backForwardList];
1413 // Print out all items in the list after prevTestBFItem, which was from the previous test
1414 // Gather items from the end of the list, the print them out from oldest to newest
1415 NSMutableArray *itemsToPrint = [[NSMutableArray alloc] init];
1416 for (int i = [bfList forwardListCount]; i > 0; i--) {
1417 WebHistoryItem *item = [bfList itemAtIndex:i];
1418 // something is wrong if the item from the last test is in the forward part of the b/f list
1419 assert(item != prevTestBFItem);
1420 [itemsToPrint addObject:item];
1423 assert([bfList currentItem] != prevTestBFItem);
1424 [itemsToPrint addObject:[bfList currentItem]];
1425 int currentItemIndex = [itemsToPrint count] - 1;
1427 for (int i = -1; i >= -[bfList backListCount]; i--) {
1428 WebHistoryItem *item = [bfList itemAtIndex:i];
1429 if (item == prevTestBFItem)
1431 [itemsToPrint addObject:item];
1434 for (int i = [itemsToPrint count]-1; i >= 0; i--)
1435 dumpHistoryItem([itemsToPrint objectAtIndex:i], 8, i == currentItemIndex);
1437 [itemsToPrint release];
1438 printf("===============================================\n");
1441 static void sizeWebViewForCurrentTest()
1443 // W3C SVG tests expect to be 480x360
1444 bool isSVGW3CTest = (gTestRunner->testPathOrURL().find("svg/W3C-SVG-1.1") != string::npos);
1446 [[mainFrame webView] setFrameSize:NSMakeSize(TestRunner::w3cSVGViewWidth, TestRunner::w3cSVGViewHeight)];
1448 [[mainFrame webView] setFrameSize:NSMakeSize(TestRunner::viewWidth, TestRunner::viewHeight)];
1451 static const char *methodNameStringForFailedTest()
1453 const char *errorMessage;
1454 if (gTestRunner->dumpAsText())
1455 errorMessage = "[documentElement innerText]";
1456 else if (gTestRunner->dumpDOMAsWebArchive())
1457 errorMessage = "[[mainFrame DOMDocument] webArchive]";
1458 else if (gTestRunner->dumpSourceAsWebArchive())
1459 errorMessage = "[[mainFrame dataSource] webArchive]";
1461 errorMessage = "[mainFrame renderTreeAsExternalRepresentation]";
1463 return errorMessage;
1466 static void dumpBackForwardListForAllWindows()
1468 CFArrayRef openWindows = (CFArrayRef)[DumpRenderTreeWindow openWindows];
1469 unsigned count = CFArrayGetCount(openWindows);
1470 for (unsigned i = 0; i < count; i++) {
1471 NSWindow *window = (NSWindow *)CFArrayGetValueAtIndex(openWindows, i);
1473 WebView *webView = [[[window contentView] subviews] objectAtIndex:0];
1475 ASSERT([[window contentView] isKindOfClass:[WebView class]]);
1476 WebView *webView = (WebView *)[window contentView];
1478 dumpBackForwardListForWebView(webView);
1482 static void invalidateAnyPreviousWaitToDumpWatchdog()
1484 if (waitToDumpWatchdog) {
1485 CFRunLoopTimerInvalidate(waitToDumpWatchdog);
1486 CFRelease(waitToDumpWatchdog);
1487 waitToDumpWatchdog = 0;
1491 void setWaitToDumpWatchdog(CFRunLoopTimerRef timer)
1494 ASSERT(shouldSetWaitToDumpWatchdog());
1495 waitToDumpWatchdog = timer;
1496 CFRunLoopAddTimer(CFRunLoopGetCurrent(), waitToDumpWatchdog, kCFRunLoopCommonModes);
1499 bool shouldSetWaitToDumpWatchdog()
1501 return !waitToDumpWatchdog && useTimeoutWatchdog;
1507 // This can get called on the web thread if from a JavaScript notifyDone().
1508 if (WebThreadIsCurrent()) {
1509 [(DumpRenderTree *)UIApp _deferDumpToMainThread];
1515 invalidateAnyPreviousWaitToDumpWatchdog();
1516 ASSERT(!gTestRunner->hasPendingWebNotificationClick());
1519 NSString *resultString = nil;
1520 NSData *resultData = nil;
1521 NSString *resultMimeType = @"text/plain";
1523 if ([[[mainFrame dataSource] _responseMIMEType] isEqualToString:@"text/plain"]) {
1524 gTestRunner->setDumpAsText(true);
1525 gTestRunner->setGeneratePixelResults(false);
1527 if (gTestRunner->dumpAsAudio()) {
1528 resultData = dumpAudio();
1529 resultMimeType = @"audio/wav";
1530 } else if (gTestRunner->dumpAsText()) {
1531 resultString = dumpFramesAsText(mainFrame);
1532 } else if (gTestRunner->dumpAsPDF()) {
1533 resultData = dumpFrameAsPDF(mainFrame);
1534 resultMimeType = @"application/pdf";
1535 } else if (gTestRunner->dumpDOMAsWebArchive()) {
1536 WebArchive *webArchive = [[mainFrame DOMDocument] webArchive];
1537 resultString = CFBridgingRelease(createXMLStringFromWebArchiveData((CFDataRef)[webArchive data]));
1538 resultMimeType = @"application/x-webarchive";
1539 } else if (gTestRunner->dumpSourceAsWebArchive()) {
1540 WebArchive *webArchive = [[mainFrame dataSource] webArchive];
1541 resultString = CFBridgingRelease(createXMLStringFromWebArchiveData((CFDataRef)[webArchive data]));
1542 resultMimeType = @"application/x-webarchive";
1544 resultString = [mainFrame renderTreeAsExternalRepresentationForPrinting:gTestRunner->isPrinting()];
1546 if (resultString && !resultData)
1547 resultData = [resultString dataUsingEncoding:NSUTF8StringEncoding];
1549 printf("Content-Type: %s\n", [resultMimeType UTF8String]);
1551 WTF::FastMallocStatistics mallocStats = WTF::fastMallocStatistics();
1552 printf("DumpMalloc: %li\n", mallocStats.committedVMBytes);
1554 if (gTestRunner->dumpAsAudio())
1555 printf("Content-Length: %lu\n", static_cast<unsigned long>([resultData length]));
1558 fwrite([resultData bytes], 1, [resultData length], stdout);
1560 if (!gTestRunner->dumpAsText() && !gTestRunner->dumpDOMAsWebArchive() && !gTestRunner->dumpSourceAsWebArchive() && !gTestRunner->dumpAsAudio())
1561 dumpFrameScrollPosition(mainFrame);
1563 if (gTestRunner->dumpBackForwardList())
1564 dumpBackForwardListForAllWindows();
1566 printf("ERROR: nil result from %s", methodNameStringForFailedTest());
1568 // Stop the watchdog thread before we leave this test to make sure it doesn't
1569 // fire in between tests causing the next test to fail.
1570 // This is a speculative fix for: https://bugs.webkit.org/show_bug.cgi?id=32339
1571 invalidateAnyPreviousWaitToDumpWatchdog();
1573 if (printSeparators) {
1574 puts("#EOF"); // terminate the content block
1575 fputs("#EOF\n", stderr);
1579 if (dumpPixelsForCurrentTest && gTestRunner->generatePixelResults())
1580 // FIXME: when isPrinting is set, dump the image with page separators.
1581 dumpWebViewAsPixelsAndCompareWithExpected(gTestRunner->expectedPixelHash());
1583 puts("#EOF"); // terminate the (possibly empty) pixels block
1589 CFRunLoopStop(CFRunLoopGetMain());
1592 static bool shouldLogFrameLoadDelegates(const char* pathOrURL)
1594 return strstr(pathOrURL, "loading/");
1597 static bool shouldLogHistoryDelegates(const char* pathOrURL)
1599 return strstr(pathOrURL, "globalhistory/");
1602 static bool shouldOpenWebInspector(const char* pathOrURL)
1604 return strstr(pathOrURL, "inspector/");
1607 static bool shouldDumpAsText(const char* pathOrURL)
1609 return strstr(pathOrURL, "dumpAsText/");
1612 static bool shouldEnableDeveloperExtras(const char* pathOrURL)
1618 static bool shouldMakeViewportFlexible(const char* pathOrURL)
1620 return strstr(pathOrURL, "viewport/");
1624 static void resetWebViewToConsistentStateBeforeTesting()
1626 WebView *webView = [mainFrame webView];
1628 adjustWebDocumentForStandardViewport(gWebBrowserView, gWebScrollView);
1629 [webView _setAllowsMessaging:YES];
1630 [mainFrame setMediaDataLoadsAutomatically:YES];
1632 [webView setEditable:NO];
1633 [(EditingDelegate *)[webView editingDelegate] setAcceptsEditing:YES];
1634 [webView makeTextStandardSize:nil];
1635 [webView resetPageZoom:nil];
1636 [webView _scaleWebView:1.0 atOrigin:NSZeroPoint];
1638 [webView _setCustomBackingScaleFactor:0];
1640 [webView setTabKeyCyclesThroughElements:YES];
1641 [webView setPolicyDelegate:defaultPolicyDelegate];
1642 [policyDelegate setPermissive:NO];
1643 [policyDelegate setControllerToNotifyDone:0];
1644 [frameLoadDelegate resetToConsistentState];
1646 [webView _setDashboardBehavior:WebDashboardBehaviorUseBackwardCompatibilityMode to:NO];
1648 [webView _clearMainFrameName];
1649 [[webView undoManager] removeAllActions];
1650 [WebView _removeAllUserContentFromGroup:[webView groupName]];
1652 [[webView window] setAutodisplay:NO];
1654 [webView setTracksRepaints:NO];
1656 resetWebPreferencesToConsistentValues();
1658 TestRunner::setSerializeHTTPLoads(false);
1660 setlocale(LC_ALL, "");
1663 WebCoreTestSupport::resetInternalsObject([mainFrame globalContext]);
1664 // in the case that a test using the chrome input field failed, be sure to clean up for the next test
1665 gTestRunner->removeChromeInputField();
1669 [webView setContinuousSpellCheckingEnabled:YES];
1670 [webView setAutomaticQuoteSubstitutionEnabled:NO];
1671 [webView setAutomaticLinkDetectionEnabled:NO];
1672 [webView setAutomaticDashSubstitutionEnabled:NO];
1673 [webView setAutomaticTextReplacementEnabled:NO];
1674 [webView setAutomaticSpellingCorrectionEnabled:YES];
1675 [webView setGrammarCheckingEnabled:YES];
1677 [WebView _setUsesTestModeFocusRingColor:YES];
1679 [WebView _resetOriginAccessWhitelists];
1680 [WebView _setAllowsRoundingHacks:NO];
1682 [[MockGeolocationProvider shared] stopTimer];
1683 [[MockWebNotificationProvider shared] reset];
1686 // Clear the contents of the general pasteboard
1687 [[NSPasteboard generalPasteboard] declareTypes:[NSArray arrayWithObject:NSStringPboardType] owner:nil];
1690 [mainFrame _clearOpener];
1694 // Work around <rdar://problem/9909073> WebKit's method of calling delegates on
1695 // the main thread is not thread safe. If the web thread is attempting to call
1696 // out to a delegate method on the main thread, we want to spin the main thread
1697 // run loop until the delegate method completes before taking the web thread
1698 // lock to prevent potentially re-entering WebCore.
1699 static void WebThreadLockAfterDelegateCallbacksHaveCompleted()
1701 dispatch_semaphore_t delegateSemaphore = dispatch_semaphore_create(0);
1703 dispatch_semaphore_signal(delegateSemaphore);
1706 while (dispatch_semaphore_wait(delegateSemaphore, DISPATCH_TIME_NOW)) {
1707 NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
1708 [[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantPast]];
1714 dispatch_release(delegateSemaphore);
1718 static void runTest(const string& inputLine)
1720 ASSERT(!inputLine.empty());
1722 TestCommand command = parseInputLine(inputLine);
1723 const string& pathOrURL = command.pathOrURL;
1724 dumpPixelsForCurrentTest = command.shouldDumpPixels || dumpPixelsForAllTests;
1726 NSString *pathOrURLString = [NSString stringWithUTF8String:pathOrURL.c_str()];
1727 if (!pathOrURLString) {
1728 fprintf(stderr, "Failed to parse \"%s\" as UTF-8\n", pathOrURL.c_str());
1733 if ([pathOrURLString hasPrefix:@"http://"] || [pathOrURLString hasPrefix:@"https://"] || [pathOrURLString hasPrefix:@"file://"])
1734 url = [NSURL URLWithString:pathOrURLString];
1736 url = [NSURL fileURLWithPath:pathOrURLString];
1738 fprintf(stderr, "Failed to parse \"%s\" as a URL\n", pathOrURL.c_str());
1742 const string testURL([[url absoluteString] UTF8String]);
1744 resetWebViewToConsistentStateBeforeTesting();
1746 gTestRunner = TestRunner::create(testURL, command.expectedPixelHash);
1747 topLoadingFrame = nil;
1749 ASSERT(!draggingInfo); // the previous test should have called eventSender.mouseUp to drop!
1750 releaseAndZero(&draggingInfo);
1754 sizeWebViewForCurrentTest();
1755 gTestRunner->setIconDatabaseEnabled(false);
1758 CFSetRemoveAllValues(disallowedURLs);
1759 if (shouldLogFrameLoadDelegates(pathOrURL.c_str()))
1760 gTestRunner->setDumpFrameLoadCallbacks(true);
1762 if (shouldLogHistoryDelegates(pathOrURL.c_str()))
1763 [[mainFrame webView] setHistoryDelegate:historyDelegate];
1765 [[mainFrame webView] setHistoryDelegate:nil];
1767 if (shouldEnableDeveloperExtras(pathOrURL.c_str())) {
1768 gTestRunner->setDeveloperExtrasEnabled(true);
1769 if (shouldOpenWebInspector(pathOrURL.c_str()))
1770 gTestRunner->showWebInspector();
1771 if (shouldDumpAsText(pathOrURL.c_str())) {
1772 gTestRunner->setDumpAsText(true);
1773 gTestRunner->setGeneratePixelResults(false);
1778 if (shouldMakeViewportFlexible(pathOrURL.c_str()))
1779 adjustWebDocumentForFlexibleViewport(gWebBrowserView, gWebScrollView);
1782 if ([WebHistory optionalSharedHistory])
1783 [WebHistory setOptionalSharedHistory:nil];
1784 lastMousePosition = NSZeroPoint;
1785 lastClickPosition = NSZeroPoint;
1787 [prevTestBFItem release];
1788 prevTestBFItem = [[[[mainFrame webView] backForwardList] currentItem] retain];
1790 WorkQueue::shared()->clear();
1791 WorkQueue::shared()->setFrozen(false);
1793 bool ignoreWebCoreNodeLeaks = shouldIgnoreWebCoreNodeLeaks(testURL);
1794 if (ignoreWebCoreNodeLeaks)
1795 [WebCoreStatistics startIgnoringWebCoreNodeLeaks];
1797 NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
1798 [mainFrame loadRequest:[NSURLRequest requestWithURL:url]];
1802 pool = [[NSAutoreleasePool alloc] init];
1803 CFRunLoopRunInMode(kCFRunLoopDefaultMode, 10, false);
1808 [(DumpRenderTree *)UIApp _waitForWebThread];
1809 WebThreadLockAfterDelegateCallbacksHaveCompleted();
1811 pool = [[NSAutoreleasePool alloc] init];
1812 [EventSendingController clearSavedEvents];
1813 [[mainFrame webView] setSelectedDOMRange:nil affinity:NSSelectionAffinityDownstream];
1815 WorkQueue::shared()->clear();
1817 if (gTestRunner->closeRemainingWindowsWhenComplete()) {
1818 NSArray* array = [DumpRenderTreeWindow openWindows];
1820 unsigned count = [array count];
1821 for (unsigned i = 0; i < count; i++) {
1822 NSWindow *window = [array objectAtIndex:i];
1824 // Don't try to close the main window
1825 if (window == [[mainFrame webView] window])
1829 WebView *webView = [[[window contentView] subviews] objectAtIndex:0];
1831 ASSERT([[window contentView] isKindOfClass:[WebView class]]);
1832 WebView *webView = (WebView *)[window contentView];
1840 // If developer extras enabled Web Inspector may have been open by the test.
1841 if (shouldEnableDeveloperExtras(pathOrURL.c_str())) {
1842 gTestRunner->closeWebInspector();
1843 gTestRunner->setDeveloperExtrasEnabled(false);
1846 resetWebViewToConsistentStateBeforeTesting();
1848 // Loading an empty request synchronously replaces the document with a blank one, which is necessary
1849 // to stop timers, WebSockets and other activity that could otherwise spill output into next test's results.
1850 [mainFrame loadRequest:[NSURLRequest requestWithURL:[NSURL URLWithString:@""]]];
1854 // We should only have our main window left open when we're done
1855 ASSERT(CFArrayGetCount(openWindowsRef) == 1);
1856 ASSERT(CFArrayGetValueAtIndex(openWindowsRef, 0) == [[mainFrame webView] window]);
1858 gTestRunner.clear();
1860 if (ignoreWebCoreNodeLeaks)
1861 [WebCoreStatistics stopIgnoringWebCoreNodeLeaks];
1864 [WebCoreStatistics garbageCollectJavaScriptObjects];
1867 void displayWebView()
1870 WebView *webView = [mainFrame webView];
1873 [webView setTracksRepaints:YES];
1874 [webView resetTrackedRepaints];
1876 // FIXME: <rdar://problem/5106253> DumpRenderTree: fix DRT and ImageDiff to re-enable pixel tests
1877 [gDrtWindow layoutTilesNow];
1878 [gDrtWindow setNeedsDisplayInRect:[gDrtWindow frame]];
1879 [CATransaction flush];
1884 @implementation DumpRenderTreeEvent
1886 + (NSPoint)mouseLocation
1888 return [[[mainFrame webView] window] convertBaseToScreen:lastMousePosition];
1893 @implementation DumpRenderTreeApplication
1897 // <rdar://problem/7686123> Java plug-in freezes unless NSApplication is running