574d25430bbe122ef6822bd1c096934084c4a294
[WebKit-https.git] / Tools / DumpRenderTree / mac / DumpRenderTree.mm
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 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 "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"
57 #import "WorkQueue.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>
86 #import <getopt.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>
93 #import <wtf/text/WTFString.h>
94
95 #if !PLATFORM(IOS)
96 #import <Carbon/Carbon.h>
97 #import <WebKit/WebDynamicScrollBarsView.h>
98 #endif
99
100 #if PLATFORM(IOS)
101 #import <CoreGraphics/CGFontDB.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/WAKWindow.h>
108 #import <WebKit/WebCoreThread.h>
109 #import <WebKit/WebCoreThreadRun.h>
110 #import <WebKit/WebDOMOperations.h>
111 #import <fcntl.h>
112 #import "DumpRenderTreeBrowserView.h"
113 #endif
114
115 extern "C" {
116 #import <mach-o/getsect.h>
117 }
118
119 using namespace std;
120
121 #if !PLATFORM(IOS)
122 @interface DumpRenderTreeApplication : NSApplication
123 @end
124
125 @interface DumpRenderTreeEvent : NSEvent
126 @end
127 #else
128 @interface ScrollViewResizerDelegate : NSObject
129 @end
130
131 @implementation ScrollViewResizerDelegate
132 - (void)view:(UIWebDocumentView *)view didSetFrame:(CGRect)newFrame oldFrame:(CGRect)oldFrame asResultOfZoom:(BOOL)wasResultOfZoom
133 {
134     UIView *scrollView = [view superview];
135     while (![scrollView isKindOfClass:[UIWebScrollView class]])
136         scrollView = [scrollView superview];
137
138     ASSERT(scrollView && [scrollView isKindOfClass:[UIWebScrollView class]]);
139     const CGSize scrollViewSize = [scrollView bounds].size;
140     CGSize contentSize = newFrame.size;
141     contentSize.height = _ROUNDF_(MAX(CGRectGetMaxY(newFrame), scrollViewSize.height));
142     [(UIWebScrollView *)scrollView setContentSize:contentSize];
143 }
144 @end
145 #endif
146
147 @interface NSURLRequest (PrivateThingsWeShouldntReallyUse)
148 +(void)setAllowsAnyHTTPSCertificate:(BOOL)allow forHost:(NSString *)host;
149 @end
150
151 #if USE(APPKIT)
152 @interface NSSound (Details)
153 + (void)_setAlertType:(NSUInteger)alertType;
154 @end
155 #endif
156
157 static void runTest(const string& testPathOrURL);
158
159 // Deciding when it's OK to dump out the state is a bit tricky.  All these must be true:
160 // - There is no load in progress
161 // - There is no work queued up (see workQueue var, below)
162 // - waitToDump==NO.  This means either waitUntilDone was never called, or it was called
163 //       and notifyDone was called subsequently.
164 // Note that the call to notifyDone and the end of the load can happen in either order.
165
166 volatile bool done;
167
168 NavigationController* gNavigationController = 0;
169 RefPtr<TestRunner> gTestRunner;
170
171 WebFrame *mainFrame = 0;
172 // This is the topmost frame that is loading, during a given load, or nil when no load is 
173 // in progress.  Usually this is the same as the main frame, but not always.  In the case
174 // where a frameset is loaded, and then new content is loaded into one of the child frames,
175 // that child frame is the "topmost frame that is loading".
176 WebFrame *topLoadingFrame = nil;     // !nil iff a load is in progress
177
178
179 CFMutableSetRef disallowedURLs = 0;
180 static CFRunLoopTimerRef waitToDumpWatchdog = 0;
181
182 // Delegates
183 static FrameLoadDelegate *frameLoadDelegate;
184 static UIDelegate *uiDelegate;
185 static EditingDelegate *editingDelegate;
186 static ResourceLoadDelegate *resourceLoadDelegate;
187 static HistoryDelegate *historyDelegate;
188 PolicyDelegate *policyDelegate;
189 DefaultPolicyDelegate *defaultPolicyDelegate;
190 StorageTrackerDelegate *storageDelegate;
191 #if PLATFORM(IOS)
192 static ScrollViewResizerDelegate *scrollViewResizerDelegate;
193 #endif
194
195 static int dumpPixelsForAllTests = NO;
196 static bool dumpPixelsForCurrentTest = false;
197 static int threaded;
198 static int dumpTree = YES;
199 static int useTimeoutWatchdog = YES;
200 static int forceComplexText;
201 static int useAcceleratedDrawing;
202 static int gcBetweenTests;
203 static BOOL printSeparators;
204 static RetainPtr<CFStringRef> persistentUserStyleSheetLocation;
205
206 static WebHistoryItem *prevTestBFItem = nil;  // current b/f item at the end of the previous test
207
208 #if PLATFORM(IOS)
209 const unsigned phoneViewHeight = 480;
210 const unsigned phoneViewWidth = 320;
211 const unsigned phoneBrowserScrollViewHeight = 416;
212 const unsigned phoneBrowserAddressBarOffset = 60;
213 const CGRect layoutTestViewportRect = { {0, 0}, {static_cast<CGFloat>(TestRunner::viewWidth), static_cast<CGFloat>(TestRunner::viewHeight)} };
214 UIWebBrowserView *gWebBrowserView = nil;
215 UIWebScrollView *gWebScrollView = nil;
216 DumpRenderTreeWindow *gDrtWindow = nil;
217 #endif
218
219 #ifdef __OBJC2__
220 static void swizzleAllMethods(Class imposter, Class original)
221 {
222     unsigned int imposterMethodCount;
223     Method* imposterMethods = class_copyMethodList(imposter, &imposterMethodCount);
224
225     unsigned int originalMethodCount;
226     Method* originalMethods = class_copyMethodList(original, &originalMethodCount);
227
228     for (unsigned int i = 0; i < imposterMethodCount; i++) {
229         SEL imposterMethodName = method_getName(imposterMethods[i]);
230
231         // Attempt to add the method to the original class.  If it fails, the method already exists and we should
232         // instead exchange the implementations.
233         if (class_addMethod(original, imposterMethodName, method_getImplementation(imposterMethods[i]), method_getTypeEncoding(imposterMethods[i])))
234             continue;
235
236         unsigned int j = 0;
237         for (; j < originalMethodCount; j++) {
238             SEL originalMethodName = method_getName(originalMethods[j]);
239             if (sel_isEqual(imposterMethodName, originalMethodName))
240                 break;
241         }
242
243         // If class_addMethod failed above then the method must exist on the original class.
244         ASSERT(j < originalMethodCount);
245         method_exchangeImplementations(imposterMethods[i], originalMethods[j]);
246     }
247
248     free(imposterMethods);
249     free(originalMethods);
250 }
251 #endif
252
253 static void poseAsClass(const char* imposter, const char* original)
254 {
255     Class imposterClass = objc_getClass(imposter);
256     Class originalClass = objc_getClass(original);
257
258 #ifndef __OBJC2__
259     class_poseAs(imposterClass, originalClass);
260 #else
261
262     // Swizzle instance methods
263     swizzleAllMethods(imposterClass, originalClass);
264     // and then class methods
265     swizzleAllMethods(object_getClass(imposterClass), object_getClass(originalClass));
266 #endif
267 }
268
269 void setPersistentUserStyleSheetLocation(CFStringRef url)
270 {
271     persistentUserStyleSheetLocation = url;
272 }
273
274 static bool shouldIgnoreWebCoreNodeLeaks(const string& URLString)
275 {
276     static char* const ignoreSet[] = {
277         // Keeping this infrastructure around in case we ever need it again.
278     };
279     static const int ignoreSetCount = sizeof(ignoreSet) / sizeof(char*);
280     
281     for (int i = 0; i < ignoreSetCount; i++) {
282         // FIXME: ignore case
283         string curIgnore(ignoreSet[i]);
284         // Match at the end of the URLString
285         if (!URLString.compare(URLString.length() - curIgnore.length(), curIgnore.length(), curIgnore))
286             return true;
287     }
288     return false;
289 }
290
291 #if !PLATFORM(IOS)
292 static NSSet *allowedFontFamilySet()
293 {
294     static NSSet *fontFamilySet = [[NSSet setWithObjects:
295         @"Ahem",
296         @"Al Bayan",
297         @"American Typewriter",
298         @"Andale Mono",
299         @"Apple Braille",
300         @"Apple Color Emoji",
301         @"Apple Chancery",
302         @"Apple Garamond BT",
303         @"Apple LiGothic",
304         @"Apple LiSung",
305         @"Apple Symbols",
306         @"AppleGothic",
307         @"AppleMyungjo",
308         @"Arial Black",
309         @"Arial Hebrew",
310         @"Arial Narrow",
311         @"Arial Rounded MT Bold",
312         @"Arial Unicode MS",
313         @"Arial",
314         @"Ayuthaya",
315         @"Baghdad",
316         @"Baskerville",
317         @"BiauKai",
318         @"Big Caslon",
319         @"Brush Script MT",
320         @"Chalkboard",
321         @"Chalkduster",
322         @"Charcoal CY",
323         @"Cochin",
324         @"Comic Sans MS",
325         @"Copperplate",
326         @"Corsiva Hebrew",
327         @"Courier New",
328         @"Courier",
329         @"DecoType Naskh",
330         @"Devanagari MT",
331         @"Didot",
332         @"Euphemia UCAS",
333         @"Futura",
334         @"GB18030 Bitmap",
335         @"Geeza Pro",
336         @"Geneva CY",
337         @"Geneva",
338         @"Georgia",
339         @"Gill Sans",
340         @"Gujarati MT",
341         @"GungSeo",
342         @"Gurmukhi MT",
343         @"HeadLineA",
344         @"Hei",
345         @"Heiti SC",
346         @"Heiti TC",
347         @"Helvetica CY",
348         @"Helvetica Neue",
349         @"Helvetica",
350         @"Herculanum",
351         @"Hiragino Kaku Gothic Pro",
352         @"Hiragino Kaku Gothic ProN",
353         @"Hiragino Kaku Gothic Std",
354         @"Hiragino Kaku Gothic StdN",
355         @"Hiragino Maru Gothic Monospaced",
356         @"Hiragino Maru Gothic Pro",
357         @"Hiragino Maru Gothic ProN",
358         @"Hiragino Mincho Pro",
359         @"Hiragino Mincho ProN",
360         @"Hiragino Sans GB",
361         @"Hoefler Text",
362         @"Impact",
363         @"InaiMathi",
364         @"Kai",
365         @"Kailasa",
366         @"Kokonor",
367         @"Krungthep",
368         @"KufiStandardGK",
369         @"LiHei Pro",
370         @"LiSong Pro",
371         @"Lucida Grande",
372         @"Marker Felt",
373         @"Menlo",
374         @"Microsoft Sans Serif",
375         @"Monaco",
376         @"Mshtakan",
377         @"Nadeem",
378         @"New Peninim MT",
379         @"Optima",
380         @"Osaka",
381         @"Papyrus",
382         @"PCMyungjo",
383         @"PilGi",
384         @"Plantagenet Cherokee",
385         @"Raanana",
386         @"Sathu",
387         @"Silom",
388         @"Skia",
389         @"Songti SC",
390         @"Songti TC",
391         @"STFangsong",
392         @"STHeiti",
393         @"STIXGeneral",
394         @"STIXSizeOneSym",
395         @"STKaiti",
396         @"STSong",
397         @"Symbol",
398         @"System Font",
399         @"Tahoma",
400         @"Thonburi",
401         @"Times New Roman",
402         @"Times",
403         @"Trebuchet MS",
404         @"Verdana",
405         @"Webdings",
406         @"WebKit WeightWatcher",
407         @"Wingdings 2",
408         @"Wingdings 3",
409         @"Wingdings",
410         @"Zapf Dingbats",
411         @"Zapfino",
412         nil] retain];
413     
414     return fontFamilySet;
415 }
416
417 static IMP appKitAvailableFontFamiliesIMP;
418 static IMP appKitAvailableFontsIMP;
419
420 static NSArray *drt_NSFontManager_availableFontFamilies(id self, SEL _cmd)
421 {
422     static NSArray *availableFontFamilies;
423     if (availableFontFamilies)
424         return availableFontFamilies;
425     
426     NSArray *availableFamilies = wtfCallIMP<id>(appKitAvailableFontFamiliesIMP, self, _cmd);
427
428     NSMutableSet *prunedFamiliesSet = [NSMutableSet setWithArray:availableFamilies];
429     [prunedFamiliesSet intersectSet:allowedFontFamilySet()];
430
431     availableFontFamilies = [[prunedFamiliesSet allObjects] retain];
432     return availableFontFamilies;
433 }
434
435 static NSArray *drt_NSFontManager_availableFonts(id self, SEL _cmd)
436 {
437     static NSArray *availableFonts;
438     if (availableFonts)
439         return availableFonts;
440     
441     NSSet *allowedFamilies = allowedFontFamilySet();
442     NSMutableArray *availableFontList = [[NSMutableArray alloc] initWithCapacity:[allowedFamilies count] * 2];
443     for (NSString *fontFamily in allowedFontFamilySet()) {
444         NSArray* fontsForFamily = [[NSFontManager sharedFontManager] availableMembersOfFontFamily:fontFamily];
445         for (NSArray* fontInfo in fontsForFamily) {
446             // Font name is the first entry in the array.
447             [availableFontList addObject:[fontInfo objectAtIndex:0]];
448         }
449     }
450
451     availableFonts = availableFontList;
452     return availableFonts;
453 }
454
455 static void swizzleNSFontManagerMethods()
456 {
457     Method availableFontFamiliesMethod = class_getInstanceMethod(objc_getClass("NSFontManager"), @selector(availableFontFamilies));
458     ASSERT(availableFontFamiliesMethod);
459     if (!availableFontFamiliesMethod) {
460         NSLog(@"Failed to swizzle the \"availableFontFamilies\" method on NSFontManager");
461         return;
462     }
463     
464     appKitAvailableFontFamiliesIMP = method_setImplementation(availableFontFamiliesMethod, (IMP)drt_NSFontManager_availableFontFamilies);
465
466     Method availableFontsMethod = class_getInstanceMethod(objc_getClass("NSFontManager"), @selector(availableFonts));
467     ASSERT(availableFontsMethod);
468     if (!availableFontsMethod) {
469         NSLog(@"Failed to swizzle the \"availableFonts\" method on NSFontManager");
470         return;
471     }
472     
473     appKitAvailableFontsIMP = method_setImplementation(availableFontsMethod, (IMP)drt_NSFontManager_availableFonts);
474 }
475
476 static void activateTestingFonts()
477 {
478     static const char* fontFileNames[] = {
479         "AHEM____.TTF",
480         "WebKitWeightWatcher100.ttf",
481         "WebKitWeightWatcher200.ttf",
482         "WebKitWeightWatcher300.ttf",
483         "WebKitWeightWatcher400.ttf",
484         "WebKitWeightWatcher500.ttf",
485         "WebKitWeightWatcher600.ttf",
486         "WebKitWeightWatcher700.ttf",
487         "WebKitWeightWatcher800.ttf",
488         "WebKitWeightWatcher900.ttf",
489 #if __MAC_OS_X_VERSION_MIN_REQUIRED >= 1080
490         "SampleFont.sfont",
491 #endif
492         0
493     };
494
495     NSMutableArray *fontURLs = [NSMutableArray array];
496     NSURL *resourcesDirectory = [NSURL URLWithString:@"DumpRenderTree.resources" relativeToURL:[[NSBundle mainBundle] executableURL]];
497     for (unsigned i = 0; fontFileNames[i]; ++i) {
498         NSURL *fontURL = [resourcesDirectory URLByAppendingPathComponent:[NSString stringWithUTF8String:fontFileNames[i]]];
499         [fontURLs addObject:[fontURL absoluteURL]];
500     }
501
502     CFArrayRef errors = 0;
503     if (!CTFontManagerRegisterFontsForURLs((CFArrayRef)fontURLs, kCTFontManagerScopeProcess, &errors)) {
504         NSLog(@"Failed to activate fonts: %@", errors);
505         CFRelease(errors);
506         exit(1);
507     }
508 }
509
510 static void adjustFonts()
511 {
512     swizzleNSFontManagerMethods();
513     activateTestingFonts();
514 }
515 #else
516 static void activateFontIOS(const uint8_t* fontData, unsigned long length, std::string sectionName)
517 {
518     CGDataProviderRef data = CGDataProviderCreateWithData(nullptr, fontData, length, nullptr);
519     if (!data) {
520         fprintf(stderr, "Failed to create CGDataProviderRef for the %s font.\n", sectionName.c_str());
521         exit(1);
522     }
523
524     CGFontRef cgFont = CGFontCreateWithDataProvider(data);
525     CGDataProviderRelease(data);
526     if (!cgFont) {
527         fprintf(stderr, "Failed to create CGFontRef for the %s font.\n", sectionName.c_str());
528         exit(1);
529     }
530
531     CFErrorRef error = nullptr;
532     CTFontManagerRegisterGraphicsFont(cgFont, &error);
533     if (error) {
534         fprintf(stderr, "Failed to add CGFont to CoreText for the %s font: %s.\n", sectionName.c_str(), CFStringGetCStringPtr(CFErrorCopyDescription(error), kCFStringEncodingUTF8));
535         exit(1);
536     }
537     CGFontRelease(cgFont);
538 }
539
540 static void activateFontsIOS()
541 {
542     // __asm() requires a string literal, so we can't do this as either local variables or template parameters.
543 #define fontData(sectionName) \
544 { \
545     extern const uint8_t start##sectionName __asm("section$start$__DATA$" # sectionName); \
546     extern const uint8_t end##sectionName __asm("section$end$__DATA$" # sectionName); \
547     activateFontIOS(&start##sectionName, &end##sectionName - &start##sectionName, #sectionName); \
548 }
549     fontData(Ahem);
550     fontData(WeightWatcher100);
551     fontData(WeightWatcher200);
552     fontData(WeightWatcher300);
553     fontData(WeightWatcher400);
554     fontData(WeightWatcher500);
555     fontData(WeightWatcher600);
556     fontData(WeightWatcher700);
557     fontData(WeightWatcher800);
558     fontData(WeightWatcher900);
559 }
560 #endif // !PLATFORM(IOS)
561
562
563 #if PLATFORM(IOS)
564 void adjustWebDocumentForFlexibleViewport(UIWebBrowserView *webBrowserView, UIWebScrollView *scrollView)
565 {
566     // These values match MobileSafari's, see -[TabDocument _createDocumentView].
567     [webBrowserView setMinimumScale:0.25f forDocumentTypes:UIEveryDocumentMask];
568     [webBrowserView setMaximumScale:5.0f forDocumentTypes:UIEveryDocumentMask];
569     [webBrowserView setInitialScale:UIWebViewScalesToFitScale forDocumentTypes:UIEveryDocumentMask];
570     [webBrowserView setViewportSize:CGSizeMake(UIWebViewStandardViewportWidth, UIWebViewGrowsAndShrinksToFitHeight) forDocumentTypes:UIEveryDocumentMask];
571
572     // Adjust the viewport view and viewport to have similar behavior
573     // as the browser.
574     [(DumpRenderTreeBrowserView *)webBrowserView setScrollingUsesUIWebScrollView:YES];
575     [webBrowserView setDelegate:scrollViewResizerDelegate];
576
577     CGRect viewportRect = CGRectMake(0, 0, phoneViewWidth, phoneBrowserScrollViewHeight);
578     [scrollView setBounds:viewportRect];
579     [scrollView setFrame:viewportRect];
580
581     [webBrowserView setMinimumSize:viewportRect.size];
582     [webBrowserView setAutoresizes:YES];
583     CGRect browserViewFrame = [webBrowserView frame];
584     browserViewFrame.origin = CGPointMake(0, phoneBrowserAddressBarOffset);
585     [webBrowserView setFrame:browserViewFrame];
586 }
587
588 void adjustWebDocumentForStandardViewport(UIWebBrowserView *webBrowserView, UIWebScrollView *scrollView)
589 {
590     [webBrowserView setMinimumScale:1.0f forDocumentTypes:UIEveryDocumentMask];
591     [webBrowserView setMaximumScale:5.0f forDocumentTypes:UIEveryDocumentMask];
592     [webBrowserView setInitialScale:1.0f forDocumentTypes:UIEveryDocumentMask];
593
594     [(DumpRenderTreeBrowserView *)webBrowserView setScrollingUsesUIWebScrollView:NO];
595     [webBrowserView setDelegate: nil];
596
597     [scrollView setBounds:layoutTestViewportRect];
598     [scrollView setFrame:layoutTestViewportRect];
599
600     [webBrowserView setMinimumSize:layoutTestViewportRect.size];
601     [webBrowserView setAutoresizes:NO];
602     CGRect browserViewFrame = [webBrowserView frame];
603     browserViewFrame.origin = CGPointZero;
604     [webBrowserView setFrame:browserViewFrame];
605 }
606 #endif
607
608 #if !PLATFORM(IOS)
609 @interface DRTMockScroller : NSScroller
610 @end
611
612 @implementation DRTMockScroller
613
614 - (NSRect)rectForPart:(NSScrollerPart)partCode
615 {
616     if (partCode != NSScrollerKnob)
617         return [super rectForPart:partCode];
618
619     NSRect frameRect = [self frame];
620     NSRect bounds = [self bounds];
621     BOOL isHorizontal = frameRect.size.width > frameRect.size.height;
622     CGFloat trackLength = isHorizontal ? bounds.size.width : bounds.size.height;
623     CGFloat minKnobSize = isHorizontal ? bounds.size.height : bounds.size.width;
624     CGFloat knobLength = max(minKnobSize, static_cast<CGFloat>(round(trackLength * [self knobProportion])));
625     CGFloat knobPosition = static_cast<CGFloat>((round([self doubleValue] * (trackLength - knobLength))));
626     
627     if (isHorizontal)
628         return NSMakeRect(bounds.origin.x + knobPosition, bounds.origin.y, knobLength, bounds.size.height);
629
630     return NSMakeRect(bounds.origin.x, bounds.origin.y +  + knobPosition, bounds.size.width, knobLength);
631 }
632
633 - (void)drawKnob
634 {
635     if (![self isEnabled])
636         return;
637
638     NSRect knobRect = [self rectForPart:NSScrollerKnob];
639     
640     static NSColor *knobColor = [[NSColor colorWithDeviceRed:0x80 / 255.0 green:0x80 / 255.0 blue:0x80 / 255.0 alpha:1] retain];
641     [knobColor set];
642
643     NSRectFill(knobRect);
644 }
645
646 - (void)drawRect:(NSRect)dirtyRect
647 {
648     static NSColor *trackColor = [[NSColor colorWithDeviceRed:0xC0 / 255.0 green:0xC0 / 255.0 blue:0xC0 / 255.0 alpha:1] retain];
649     static NSColor *disabledTrackColor = [[NSColor colorWithDeviceRed:0xE0 / 255.0 green:0xE0 / 255.0 blue:0xE0 / 255.0 alpha:1] retain];
650
651     if ([self isEnabled])
652         [trackColor set];
653     else
654         [disabledTrackColor set];
655
656     NSRectFill(dirtyRect);
657     
658     [self drawKnob];
659 }
660
661 @end
662
663 static void registerMockScrollbars()
664 {
665     [WebDynamicScrollBarsView setCustomScrollerClass:[DRTMockScroller class]];
666 }
667 #endif
668
669 WebView *createWebViewAndOffscreenWindow()
670 {
671 #if !PLATFORM(IOS)
672     NSRect rect = NSMakeRect(0, 0, TestRunner::viewWidth, TestRunner::viewHeight);
673     WebView *webView = [[WebView alloc] initWithFrame:rect frameName:nil groupName:@"org.webkit.DumpRenderTree"];
674 #else
675     UIWebBrowserView *webBrowserView = [[[DumpRenderTreeBrowserView alloc] initWithFrame:layoutTestViewportRect] autorelease];
676     [webBrowserView setInputViewObeysDOMFocus:YES];
677     WebView *webView = [[webBrowserView webView] retain];
678     [webView setGroupName:@"org.webkit.DumpRenderTree"];
679 #endif
680
681     [webView setUIDelegate:uiDelegate];
682     [webView setFrameLoadDelegate:frameLoadDelegate];
683     [webView setEditingDelegate:editingDelegate];
684     [webView setResourceLoadDelegate:resourceLoadDelegate];
685     [webView _setGeolocationProvider:[MockGeolocationProvider shared]];
686     [webView _setDeviceOrientationProvider:[WebDeviceOrientationProviderMock shared]];
687     [webView _setNotificationProvider:[MockWebNotificationProvider shared]];
688
689     // Register the same schemes that Safari does
690     [WebView registerURLSchemeAsLocal:@"feed"];
691     [WebView registerURLSchemeAsLocal:@"feeds"];
692     [WebView registerURLSchemeAsLocal:@"feedsearch"];
693     
694 #if !PLATFORM(IOS)
695     [webView setContinuousSpellCheckingEnabled:YES];
696     [webView setAutomaticQuoteSubstitutionEnabled:NO];
697     [webView setAutomaticLinkDetectionEnabled:NO];
698     [webView setAutomaticDashSubstitutionEnabled:NO];
699     [webView setAutomaticTextReplacementEnabled:NO];
700     [webView setAutomaticSpellingCorrectionEnabled:YES];
701     [webView setGrammarCheckingEnabled:YES];
702
703     [webView setDefersCallbacks:NO];
704     [webView setInteractiveFormValidationEnabled:YES];
705     [webView setValidationMessageTimerMagnification:-1];
706     
707     // 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.
708     // Put it at -10000, -10000 in "flipped coordinates", since WebCore and the DOM use flipped coordinates.
709     NSRect windowRect = NSOffsetRect(rect, -10000, [(NSScreen *)[[NSScreen screens] objectAtIndex:0] frame].size.height - rect.size.height + 10000);
710     DumpRenderTreeWindow *window = [[DumpRenderTreeWindow alloc] initWithContentRect:windowRect styleMask:NSBorderlessWindowMask backing:NSBackingStoreBuffered defer:YES];
711
712     [window setColorSpace:[[[NSScreen screens] objectAtIndex:0] colorSpace]];
713     [window setCollectionBehavior:NSWindowCollectionBehaviorStationary];
714     [[window contentView] addSubview:webView];
715     [window orderBack:nil];
716     [window setAutodisplay:NO];
717
718     [window startListeningForAcceleratedCompositingChanges];
719 #else
720     DumpRenderTreeWindow *drtWindow = [[DumpRenderTreeWindow alloc] initWithLayer:[webBrowserView layer]];
721     [drtWindow setContentView:webView];
722     [webBrowserView setWAKWindow:drtWindow];
723
724     [[webView window] makeFirstResponder:[[[webView mainFrame] frameView] documentView]];
725
726     CGRect uiWindowRect = layoutTestViewportRect;
727     uiWindowRect.origin.y += [UIApp statusBarHeight];
728     UIWindow *uiWindow = [[[UIWindow alloc] initWithFrame:uiWindowRect] autorelease];
729
730     // The UIWindow and UIWebBrowserView are released when the DumpRenderTreeWindow is closed.
731     drtWindow.uiWindow = uiWindow;
732     drtWindow.browserView = webBrowserView;
733
734     UIWebScrollView *scrollView = [[UIWebScrollView alloc] initWithFrame:layoutTestViewportRect];
735     [scrollView addSubview:webBrowserView];
736
737     [uiWindow addSubview:scrollView];
738     [scrollView release];
739
740     adjustWebDocumentForStandardViewport(webBrowserView, scrollView);
741 #endif
742     
743 #if !PLATFORM(IOS)
744     // For reasons that are not entirely clear, the following pair of calls makes WebView handle its
745     // dynamic scrollbars properly. Without it, every frame will always have scrollbars.
746     NSBitmapImageRep *imageRep = [webView bitmapImageRepForCachingDisplayInRect:[webView bounds]];
747     [webView cacheDisplayInRect:[webView bounds] toBitmapImageRep:imageRep];
748 #else
749     [[webView mainFrame] _setVisibleSize:CGSizeMake(phoneViewWidth, phoneViewHeight)];
750     [[webView preferences] _setTelephoneNumberParsingEnabled:NO];
751
752     // Initialize the global UIViews, and set the key UIWindow to be painted.
753     if (!gWebBrowserView) {
754         gWebBrowserView = [webBrowserView retain];
755         gWebScrollView = [scrollView retain];
756         gDrtWindow = [drtWindow retain];
757         [uiWindow makeKeyAndVisible];
758         [uiWindow retain];
759     }
760 #endif
761
762     return webView;
763 }
764
765 static void destroyWebViewAndOffscreenWindow()
766 {
767     WebView *webView = [mainFrame webView];
768 #if !PLATFORM(IOS)
769     NSWindow *window = [webView window];
770 #endif
771     [webView close];
772     mainFrame = nil;
773
774 #if !PLATFORM(IOS)
775     // Work around problem where registering drag types leaves an outstanding
776     // "perform selector" on the window, which retains the window. It's a bit
777     // inelegant and perhaps dangerous to just blow them all away, but in practice
778     // it probably won't cause any trouble (and this is just a test tool, after all).
779     [NSObject cancelPreviousPerformRequestsWithTarget:window];
780
781     [window close]; // releases when closed
782 #else
783     UIWindow *uiWindow = [gWebBrowserView window];
784     [uiWindow removeFromSuperview];
785     [uiWindow release];
786 #endif
787
788     [webView release];
789 }
790
791 static NSString *libraryPathForDumpRenderTree()
792 {
793     //FIXME: This may not be sufficient to prevent interactions/crashes
794     //when running more than one copy of DumpRenderTree.
795     //See https://bugs.webkit.org/show_bug.cgi?id=10906
796     char* dumpRenderTreeTemp = getenv("DUMPRENDERTREE_TEMP");
797     if (dumpRenderTreeTemp)
798         return [[NSFileManager defaultManager] stringWithFileSystemRepresentation:dumpRenderTreeTemp length:strlen(dumpRenderTreeTemp)];
799     else
800         return [@"~/Library/Application Support/DumpRenderTree" stringByExpandingTildeInPath];
801 }
802
803 // Called before each test.
804 static void resetWebPreferencesToConsistentValues()
805 {
806     WebPreferences *preferences = [WebPreferences standardPreferences];
807
808     [preferences setAllowUniversalAccessFromFileURLs:YES];
809     [preferences setAllowFileAccessFromFileURLs:YES];
810     [preferences setStandardFontFamily:@"Times"];
811     [preferences setFixedFontFamily:@"Courier"];
812     [preferences setSerifFontFamily:@"Times"];
813     [preferences setSansSerifFontFamily:@"Helvetica"];
814     [preferences setCursiveFontFamily:@"Apple Chancery"];
815     [preferences setFantasyFontFamily:@"Papyrus"];
816     [preferences setPictographFontFamily:@"Apple Color Emoji"];
817     [preferences setDefaultFontSize:16];
818     [preferences setDefaultFixedFontSize:13];
819     [preferences setMinimumFontSize:0];
820     [preferences setDefaultTextEncodingName:@"ISO-8859-1"];
821     [preferences setJavaEnabled:NO];
822     [preferences setJavaScriptEnabled:YES];
823     [preferences setEditableLinkBehavior:WebKitEditableLinkOnlyLiveWithShiftKey];
824 #if !PLATFORM(IOS)
825     [preferences setTabsToLinks:NO];
826 #endif
827     [preferences setDOMPasteAllowed:YES];
828 #if !PLATFORM(IOS)
829     [preferences setShouldPrintBackgrounds:YES];
830 #endif
831     [preferences setCacheModel:WebCacheModelDocumentBrowser];
832     [preferences setXSSAuditorEnabled:NO];
833     [preferences setExperimentalNotificationsEnabled:NO];
834     [preferences setPlugInsEnabled:YES];
835 #if !PLATFORM(IOS)
836     [preferences setTextAreasAreResizable:YES];
837 #endif
838
839     [preferences setPrivateBrowsingEnabled:NO];
840     [preferences setAuthorAndUserStylesEnabled:YES];
841     [preferences setShrinksStandaloneImagesToFit:YES];
842     [preferences setJavaScriptCanOpenWindowsAutomatically:YES];
843     [preferences setJavaScriptCanAccessClipboard:YES];
844     [preferences setOfflineWebApplicationCacheEnabled:YES];
845     [preferences setDeveloperExtrasEnabled:NO];
846     [preferences setJavaScriptExperimentsEnabled:YES];
847     [preferences setLoadsImagesAutomatically:YES];
848     [preferences setLoadsSiteIconsIgnoringImageLoadingPreference:NO];
849     [preferences setFrameFlatteningEnabled:NO];
850     [preferences setSpatialNavigationEnabled:NO];
851     if (persistentUserStyleSheetLocation) {
852         [preferences setUserStyleSheetLocation:[NSURL URLWithString:(NSString *)(persistentUserStyleSheetLocation.get())]];
853         [preferences setUserStyleSheetEnabled:YES];
854     } else
855         [preferences setUserStyleSheetEnabled:NO];
856 #if PLATFORM(IOS)
857     [preferences setMediaPlaybackAllowsInline:YES];
858     [preferences setMediaPlaybackRequiresUserGesture:NO];
859
860     // Enable the tracker before creating the first WebView will
861     // cause initialization to use the correct database paths.
862     [preferences setStorageTrackerEnabled:YES];
863 #endif
864
865 #if ENABLE(IOS_TEXT_AUTOSIZING)
866     // Disable text autosizing by default.
867     [preferences _setMinimumZoomFontSize:0];
868 #endif
869
870     // The back/forward cache is causing problems due to layouts during transition from one page to another.
871     // So, turn it off for now, but we might want to turn it back on some day.
872     [preferences setUsesPageCache:NO];
873     [preferences setAcceleratedCompositingEnabled:YES];
874 #if USE(CA)
875     [preferences setCanvasUsesAcceleratedDrawing:YES];
876     [preferences setAcceleratedDrawingEnabled:useAcceleratedDrawing];
877 #endif
878     [preferences setWebGLEnabled:NO];
879     [preferences setCSSRegionsEnabled:YES];
880     [preferences setUsePreHTML5ParserQuirks:NO];
881     [preferences setAsynchronousSpellCheckingEnabled:NO];
882 #if !PLATFORM(IOS)
883     ASSERT([preferences mockScrollbarsEnabled]);
884 #endif
885
886 #if ENABLE(WEB_AUDIO)
887     [preferences setWebAudioEnabled:YES];
888 #endif
889
890 #if ENABLE(IOS_TEXT_AUTOSIZING)
891     // Disable text autosizing by default.
892     [preferences _setMinimumZoomFontSize:0];
893 #endif
894
895     [preferences setScreenFontSubstitutionEnabled:YES];
896
897 #if ENABLE(MEDIA_SOURCE)
898     [preferences setMediaSourceEnabled:YES];
899 #endif
900
901     [WebPreferences _setCurrentNetworkLoaderSessionCookieAcceptPolicy:NSHTTPCookieAcceptPolicyOnlyFromMainDocumentDomain];
902 }
903
904 // Called once on DumpRenderTree startup.
905 static void setDefaultsToConsistentValuesForTesting()
906 {
907 #if PLATFORM(IOS)
908     WebThreadLock();
909 #endif
910
911     static const int NoFontSmoothing = 0;
912     static const int BlueTintedAppearance = 1;
913
914     NSString *libraryPath = libraryPathForDumpRenderTree();
915
916     NSDictionary *dict = @{
917         @"AppleKeyboardUIMode": @1,
918         @"AppleAntiAliasingThreshold": @4,
919         @"AppleFontSmoothing": @(NoFontSmoothing),
920         @"AppleAquaColorVariant": @(BlueTintedAppearance),
921         @"AppleHighlightColor": @"0.709800 0.835300 1.000000",
922         @"AppleOtherHighlightColor":@"0.500000 0.500000 0.500000",
923         @"AppleLanguages": @[ @"en" ],
924         WebKitEnableFullDocumentTeardownPreferenceKey: @YES,
925         WebKitFullScreenEnabledPreferenceKey: @YES,
926         @"UseWebKitWebInspector": @YES,
927 #if !PLATFORM(IOS)
928         @"NSTestCorrectionDictionary": @{
929             @"notationl": @"notational",
930             @"mesage": @"message",
931             @"wouldn": @"would",
932             @"wellcome": @"welcome",
933             @"hellolfworld": @"hello\nworld"
934         },
935 #endif
936         @"WebKitKerningAndLigaturesEnabledByDefault": @NO,
937         @"AppleScrollBarVariant": @"DoubleMax",
938 #if !PLATFORM(IOS)
939         @"NSScrollAnimationEnabled": @NO,
940 #endif
941         @"NSOverlayScrollersEnabled": @NO,
942         @"AppleShowScrollBars": @"Always",
943         @"NSButtonAnimationsEnabled": @NO, // Ideally, we should find a way to test animations, but for now, make sure that the dumped snapshot matches actual state.
944         WebDatabaseDirectoryDefaultsKey: [libraryPath stringByAppendingPathComponent:@"Databases"],
945         WebStorageDirectoryDefaultsKey: [libraryPath stringByAppendingPathComponent:@"LocalStorage"],
946         WebKitLocalCacheDefaultsKey: [libraryPath stringByAppendingPathComponent:@"LocalCache"]
947     };
948
949     [[NSUserDefaults standardUserDefaults] setValuesForKeysWithDictionary:dict];
950 }
951
952 static void runThread(void* arg)
953 {
954     static ThreadIdentifier previousId = 0;
955     ThreadIdentifier currentId = currentThread();
956     // Verify 2 successive threads do not get the same Id.
957     ASSERT(previousId != currentId);
958     previousId = currentId;
959 }
960
961 static void* runPthread(void* arg)
962 {
963     runThread(arg);
964     return 0;
965 }
966
967 static void testThreadIdentifierMap()
968 {
969     // Imitate 'foreign' threads that are not created by WTF.
970     pthread_t pthread;
971     pthread_create(&pthread, 0, &runPthread, 0);
972     pthread_join(pthread, 0);
973
974     pthread_create(&pthread, 0, &runPthread, 0);
975     pthread_join(pthread, 0);
976
977     // Now create another thread using WTF. On OSX, it will have the same pthread handle
978     // but should get a different ThreadIdentifier.
979     createThread(runThread, 0, "DumpRenderTree: test");
980 }
981
982 static void allocateGlobalControllers()
983 {
984     // FIXME: We should remove these and move to the ObjC standard [Foo sharedInstance] model
985     gNavigationController = [[NavigationController alloc] init];
986     frameLoadDelegate = [[FrameLoadDelegate alloc] init];
987     uiDelegate = [[UIDelegate alloc] init];
988     editingDelegate = [[EditingDelegate alloc] init];
989     resourceLoadDelegate = [[ResourceLoadDelegate alloc] init];
990     policyDelegate = [[PolicyDelegate alloc] init];
991     historyDelegate = [[HistoryDelegate alloc] init];
992     storageDelegate = [[StorageTrackerDelegate alloc] init];
993     defaultPolicyDelegate = [[DefaultPolicyDelegate alloc] init];
994 #if PLATFORM(IOS)
995     scrollViewResizerDelegate = [[ScrollViewResizerDelegate alloc] init];
996 #endif
997 }
998
999 // ObjC++ doens't seem to let me pass NSObject*& sadly.
1000 static inline void releaseAndZero(NSObject** object)
1001 {
1002     [*object release];
1003     *object = nil;
1004 }
1005
1006 static void releaseGlobalControllers()
1007 {
1008     releaseAndZero(&gNavigationController);
1009     releaseAndZero(&frameLoadDelegate);
1010     releaseAndZero(&editingDelegate);
1011     releaseAndZero(&resourceLoadDelegate);
1012     releaseAndZero(&uiDelegate);
1013     releaseAndZero(&policyDelegate);
1014     releaseAndZero(&storageDelegate);
1015 #if PLATFORM(IOS)
1016     releaseAndZero(&scrollViewResizerDelegate);
1017 #endif
1018 }
1019
1020 static void initializeGlobalsFromCommandLineOptions(int argc, const char *argv[])
1021 {
1022     struct option options[] = {
1023         {"notree", no_argument, &dumpTree, NO},
1024         {"pixel-tests", no_argument, &dumpPixelsForAllTests, YES},
1025         {"tree", no_argument, &dumpTree, YES},
1026         {"threaded", no_argument, &threaded, YES},
1027         {"complex-text", no_argument, &forceComplexText, YES},
1028         {"accelerated-drawing", no_argument, &useAcceleratedDrawing, YES},
1029         {"gc-between-tests", no_argument, &gcBetweenTests, YES},
1030         {"no-timeout", no_argument, &useTimeoutWatchdog, NO},
1031         {nullptr, 0, nullptr, 0}
1032     };
1033     
1034     int option;
1035     while ((option = getopt_long(argc, (char * const *)argv, "", options, nullptr)) != -1) {
1036         switch (option) {
1037             case '?':   // unknown or ambiguous option
1038             case ':':   // missing argument
1039                 exit(1);
1040                 break;
1041         }
1042     }
1043 }
1044
1045 static void addTestPluginsToPluginSearchPath(const char* executablePath)
1046 {
1047 #if !PLATFORM(IOS)
1048     NSString *pwd = [[NSString stringWithUTF8String:executablePath] stringByDeletingLastPathComponent];
1049     [WebPluginDatabase setAdditionalWebPlugInPaths:[NSArray arrayWithObject:pwd]];
1050     [[WebPluginDatabase sharedDatabase] refresh];
1051 #endif
1052 }
1053
1054 static bool useLongRunningServerMode(int argc, const char *argv[])
1055 {
1056     // This assumes you've already called getopt_long
1057     return (argc == optind+1 && strcmp(argv[optind], "-") == 0);
1058 }
1059
1060 static void runTestingServerLoop()
1061 {
1062     // When DumpRenderTree run in server mode, we just wait around for file names
1063     // to be passed to us and read each in turn, passing the results back to the client
1064     char filenameBuffer[2048];
1065     while (fgets(filenameBuffer, sizeof(filenameBuffer), stdin)) {
1066         char *newLineCharacter = strchr(filenameBuffer, '\n');
1067         if (newLineCharacter)
1068             *newLineCharacter = '\0';
1069
1070         if (strlen(filenameBuffer) == 0)
1071             continue;
1072
1073         runTest(filenameBuffer);
1074     }
1075 }
1076
1077 static void prepareConsistentTestingEnvironment()
1078 {
1079 #if !PLATFORM(IOS)
1080     poseAsClass("DumpRenderTreePasteboard", "NSPasteboard");
1081     poseAsClass("DumpRenderTreeEvent", "NSEvent");
1082 #else
1083     poseAsClass("DumpRenderTreeEvent", "GSEvent");
1084 #endif
1085
1086     [[WebPreferences standardPreferences] setAutosaves:NO];
1087
1088 #if !PLATFORM(IOS)
1089     // FIXME: We'd like to start with a clean state for every test, but this function can't be used more than once yet.
1090     [WebPreferences _switchNetworkLoaderToNewTestingSession];
1091
1092     NSURLCache *sharedCache =
1093         [[NSURLCache alloc] initWithMemoryCapacity:1024 * 1024
1094                                       diskCapacity:0
1095                                           diskPath:[libraryPathForDumpRenderTree() stringByAppendingPathComponent:@"URLCache"]];
1096     [NSURLCache setSharedURLCache:sharedCache];
1097     [sharedCache release];
1098
1099     adjustFonts();
1100     registerMockScrollbars();
1101
1102     // The mock scrollbars setting cannot be modified after creating a view, so we have to do it now.
1103     [[WebPreferences standardPreferences] setMockScrollbarsEnabled:YES];
1104 #else
1105     activateFontsIOS();
1106 #endif
1107     
1108     allocateGlobalControllers();
1109     
1110     makeLargeMallocFailSilently();
1111
1112 #if !PLATFORM(IOS) && __MAC_OS_X_VERSION_MIN_REQUIRED >= 1090
1113     NSActivityOptions options = (NSActivityUserInitiatedAllowingIdleSystemSleep | NSActivityLatencyCritical) & ~(NSActivitySuddenTerminationDisabled | NSActivityAutomaticTerminationDisabled);
1114     static id assertion = [[[NSProcessInfo processInfo] beginActivityWithOptions:options reason:@"DumpRenderTree should not be subject to process suppression"] retain];
1115     ASSERT_UNUSED(assertion, assertion);
1116 #endif
1117 }
1118
1119 void dumpRenderTree(int argc, const char *argv[])
1120 {
1121 #if PLATFORM(IOS)
1122     NSString *identifier = [[NSBundle mainBundle] bundleIdentifier];
1123     const char *stdinPath = [[NSString stringWithFormat:@"/tmp/%@_IN", identifier] UTF8String];
1124     const char *stdoutPath = [[NSString stringWithFormat:@"/tmp/%@_OUT", identifier] UTF8String];
1125     const char *stderrPath = [[NSString stringWithFormat:@"/tmp/%@_ERROR", identifier] UTF8String];
1126
1127     int infd = open(stdinPath, O_RDWR);
1128     dup2(infd, STDIN_FILENO);
1129     int outfd = open(stdoutPath, O_RDWR);
1130     dup2(outfd, STDOUT_FILENO);
1131     int errfd = open(stderrPath, O_RDWR | O_NONBLOCK);
1132     dup2(errfd, STDERR_FILENO);
1133 #endif
1134
1135     initializeGlobalsFromCommandLineOptions(argc, argv);
1136     prepareConsistentTestingEnvironment();
1137     addTestPluginsToPluginSearchPath(argv[0]);
1138
1139     if (forceComplexText)
1140         [WebView _setAlwaysUsesComplexTextCodePath:YES];
1141
1142 #if USE(APPKIT)
1143     [NSSound _setAlertType:0];
1144 #endif
1145
1146     WebView *webView = createWebViewAndOffscreenWindow();
1147     mainFrame = [webView mainFrame];
1148
1149     [[NSURLCache sharedURLCache] removeAllCachedResponses];
1150     [WebCache empty];
1151
1152     [NSURLRequest setAllowsAnyHTTPSCertificate:YES forHost:@"localhost"];
1153     [NSURLRequest setAllowsAnyHTTPSCertificate:YES forHost:@"127.0.0.1"];
1154
1155     // http://webkit.org/b/32689
1156     testThreadIdentifierMap();
1157
1158     if (threaded)
1159         startJavaScriptThreads();
1160
1161     if (useLongRunningServerMode(argc, argv)) {
1162         printSeparators = YES;
1163         runTestingServerLoop();
1164     } else {
1165         printSeparators = optind < argc - 1;
1166         for (int i = optind; i != argc; ++i)
1167             runTest(argv[i]);
1168     }
1169
1170     if (threaded)
1171         stopJavaScriptThreads();
1172
1173     destroyWebViewAndOffscreenWindow();
1174     
1175     releaseGlobalControllers();
1176     
1177 #if !PLATFORM(IOS)
1178     [DumpRenderTreePasteboard releaseLocalPasteboards];
1179 #endif
1180
1181     // FIXME: This should be moved onto TestRunner and made into a HashSet
1182     if (disallowedURLs) {
1183         CFRelease(disallowedURLs);
1184         disallowedURLs = 0;
1185     }
1186
1187 #if PLATFORM(IOS)
1188     close(infd);
1189     close(outfd);
1190     close(errfd);
1191 #endif
1192 }
1193
1194 #if PLATFORM(IOS)
1195 static int _argc;
1196 static const char **_argv;
1197
1198 @implementation DumpRenderTree
1199
1200 - (void)_runDumpRenderTree
1201 {
1202     dumpRenderTree(_argc, _argv);
1203 }
1204
1205 - (void)applicationDidFinishLaunching:(NSNotification *)notification
1206 {
1207     [self performSelectorOnMainThread:@selector(_runDumpRenderTree) withObject:nil waitUntilDone:NO];
1208 }
1209
1210 - (void)_deferDumpToMainThread
1211 {
1212     ASSERT(WebThreadIsCurrent());
1213     
1214     dispatch_async(dispatch_get_main_queue(), ^{
1215         dump();
1216     });
1217 }
1218
1219 - (void)_webThreadEventLoopHasRun
1220 {
1221     ASSERT(!WebThreadIsCurrent());
1222     _hasFlushedWebThreadRunQueue = YES;
1223 }
1224
1225 - (void)_webThreadInvoked
1226 {
1227     ASSERT(WebThreadIsCurrent());
1228     dispatch_async(dispatch_get_main_queue(), ^{
1229         [self _webThreadEventLoopHasRun];
1230     });
1231 }
1232
1233 - (void)applicationDidEnterBackground:(UIApplication *)application
1234 {
1235     /* Apps will get suspended or killed some time after entering the background state but we want to be able to run multiple copies of DumpRenderTree. Periodically check to see if our remaining background time dips below a threshold and create a new background task.
1236     */
1237     void (^expirationHandler)() = ^ {
1238         [application endBackgroundTask:backgroundTaskIdentifier];
1239         backgroundTaskIdentifier = UIBackgroundTaskInvalid;
1240     };
1241
1242     dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
1243
1244         NSTimeInterval timeRemaining;
1245         while (true) {
1246             timeRemaining = [application backgroundTimeRemaining];
1247             if (timeRemaining <= 10.0 || backgroundTaskIdentifier == UIBackgroundTaskInvalid) {
1248                 [application endBackgroundTask:backgroundTaskIdentifier];
1249                 backgroundTaskIdentifier = [application beginBackgroundTaskWithExpirationHandler:expirationHandler];
1250             }
1251             sleep(5);
1252         }
1253     });
1254 }
1255
1256 // The test can end in response to a delegate callback while there are still methods queued on the Web Thread.
1257 // If we do not ensure the Web Thread has been run, the callback can be done on a WebView that no longer exists.
1258 // To avoid this, _waitForWebThread dispatches a call to the WebThread event loop, actively processing the delegate
1259 // callbacks in the main thread while waiting for the call to be invoked on the Web Thread.
1260 - (void)_waitForWebThread
1261 {
1262     ASSERT(!WebThreadIsCurrent());
1263     _hasFlushedWebThreadRunQueue = NO;
1264     WebThreadRun(^{
1265         [self _webThreadInvoked];
1266     });
1267     while (!_hasFlushedWebThreadRunQueue) {
1268         NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
1269         [[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantPast]];
1270         [pool release];
1271     }
1272 }
1273
1274 @end
1275 #endif
1276
1277 int DumpRenderTreeMain(int argc, const char *argv[])
1278 {
1279 #if PLATFORM(IOS)
1280     _UIApplicationLoadWebKit();
1281 #endif
1282     NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
1283
1284     setDefaultsToConsistentValuesForTesting(); // Must be called before NSApplication initialization.
1285
1286 #if !PLATFORM(IOS)
1287     [DumpRenderTreeApplication sharedApplication]; // Force AppKit to init itself
1288
1289     dumpRenderTree(argc, argv);
1290 #else
1291     _argc = argc;
1292     _argv = argv;
1293     UIApplicationMain(argc, (char**)argv, @"DumpRenderTree", @"DumpRenderTree");
1294 #endif
1295     [WebCoreStatistics garbageCollectJavaScriptObjects];
1296     [WebCoreStatistics emptyCache]; // Otherwise SVGImages trigger false positives for Frame/Node counts
1297     if (JSC::Options::logHeapStatisticsAtExit())
1298         JSC::HeapStatistics::reportSuccess();
1299     [pool release];
1300     return 0;
1301 }
1302
1303 static NSInteger compareHistoryItems(id item1, id item2, void *context)
1304 {
1305     return [[item1 target] caseInsensitiveCompare:[item2 target]];
1306 }
1307
1308 static NSData *dumpAudio()
1309 {
1310     const vector<char>& dataVector = gTestRunner->audioResult();
1311     
1312     NSData *data = [NSData dataWithBytes:dataVector.data() length:dataVector.size()];
1313     return data;
1314 }
1315
1316 static void dumpHistoryItem(WebHistoryItem *item, int indent, BOOL current)
1317 {
1318     int start = 0;
1319     if (current) {
1320         printf("curr->");
1321         start = 6;
1322     }
1323     for (int i = start; i < indent; i++)
1324         putchar(' ');
1325     
1326     NSString *urlString = [item URLString];
1327     if ([[NSURL URLWithString:urlString] isFileURL]) {
1328         NSRange range = [urlString rangeOfString:@"/LayoutTests/"];
1329         urlString = [@"(file test):" stringByAppendingString:[urlString substringFromIndex:(range.length + range.location)]];
1330     }
1331     
1332     printf("%s", [urlString UTF8String]);
1333     NSString *target = [item target];
1334     if (target && [target length] > 0)
1335         printf(" (in frame \"%s\")", [target UTF8String]);
1336     if ([item isTargetItem])
1337         printf("  **nav target**");
1338     putchar('\n');
1339     NSArray *kids = [item children];
1340     if (kids) {
1341         // must sort to eliminate arbitrary result ordering which defeats reproducible testing
1342         kids = [kids sortedArrayUsingFunction:&compareHistoryItems context:nil];
1343         for (unsigned i = 0; i < [kids count]; i++)
1344             dumpHistoryItem([kids objectAtIndex:i], indent+4, NO);
1345     }
1346 }
1347
1348 static void dumpFrameScrollPosition(WebFrame *f)
1349 {
1350     WebScriptObject* scriptObject = [f windowObject];
1351     NSPoint scrollPosition = NSMakePoint(
1352         [[scriptObject valueForKey:@"pageXOffset"] floatValue],
1353         [[scriptObject valueForKey:@"pageYOffset"] floatValue]);
1354     if (ABS(scrollPosition.x) > 0.00000001 || ABS(scrollPosition.y) > 0.00000001) {
1355         if ([f parentFrame] != nil)
1356             printf("frame '%s' ", [[f name] UTF8String]);
1357         printf("scrolled to %.f,%.f\n", scrollPosition.x, scrollPosition.y);
1358     }
1359
1360     if (gTestRunner->dumpChildFrameScrollPositions()) {
1361         NSArray *kids = [f childFrames];
1362         if (kids)
1363             for (unsigned i = 0; i < [kids count]; i++)
1364                 dumpFrameScrollPosition([kids objectAtIndex:i]);
1365     }
1366 }
1367
1368 static NSString *dumpFramesAsText(WebFrame *frame)
1369 {
1370     DOMDocument *document = [frame DOMDocument];
1371     DOMElement *documentElement = [document documentElement];
1372
1373     if (!documentElement)
1374         return @"";
1375
1376     NSMutableString *result = [[[NSMutableString alloc] init] autorelease];
1377
1378     // Add header for all but the main frame.
1379     if ([frame parentFrame])
1380         result = [NSMutableString stringWithFormat:@"\n--------\nFrame: '%@'\n--------\n", [frame name]];
1381
1382     [result appendFormat:@"%@\n", [documentElement innerText]];
1383
1384     if (gTestRunner->dumpChildFramesAsText()) {
1385         NSArray *kids = [frame childFrames];
1386         if (kids) {
1387             for (unsigned i = 0; i < [kids count]; i++)
1388                 [result appendString:dumpFramesAsText([kids objectAtIndex:i])];
1389         }
1390     }
1391
1392     return result;
1393 }
1394
1395 static NSData *dumpFrameAsPDF(WebFrame *frame)
1396 {
1397 #if !PLATFORM(IOS)
1398     if (!frame)
1399         return nil;
1400
1401     // Sadly we have to dump to a file and then read from that file again
1402     // +[NSPrintOperation PDFOperationWithView:insideRect:] requires a rect and prints to a single page
1403     // likewise +[NSView dataWithPDFInsideRect:] also prints to a single continuous page
1404     // The goal of this function is to test "real" printing across multiple pages.
1405     // FIXME: It's possible there might be printing SPI to let us print a multi-page PDF to an NSData object
1406     NSString *path = [libraryPathForDumpRenderTree() stringByAppendingPathComponent:@"test.pdf"];
1407
1408     NSMutableDictionary *printInfoDict = [NSMutableDictionary dictionaryWithDictionary:[[NSPrintInfo sharedPrintInfo] dictionary]];
1409     [printInfoDict setObject:NSPrintSaveJob forKey:NSPrintJobDisposition];
1410     [printInfoDict setObject:path forKey:NSPrintSavePath];
1411
1412     NSPrintInfo *printInfo = [[NSPrintInfo alloc] initWithDictionary:printInfoDict];
1413     [printInfo setHorizontalPagination:NSAutoPagination];
1414     [printInfo setVerticalPagination:NSAutoPagination];
1415     [printInfo setVerticallyCentered:NO];
1416
1417     NSPrintOperation *printOperation = [NSPrintOperation printOperationWithView:[frame frameView] printInfo:printInfo];
1418     [printOperation setShowPanels:NO];
1419     [printOperation runOperation];
1420
1421     [printInfo release];
1422
1423     NSData *pdfData = [NSData dataWithContentsOfFile:path];
1424     [[NSFileManager defaultManager] removeFileAtPath:path handler:nil];
1425
1426     return pdfData;
1427 #else
1428     return nil;
1429 #endif
1430 }
1431
1432 static void dumpBackForwardListForWebView(WebView *view)
1433 {
1434     printf("\n============== Back Forward List ==============\n");
1435     WebBackForwardList *bfList = [view backForwardList];
1436
1437     // Print out all items in the list after prevTestBFItem, which was from the previous test
1438     // Gather items from the end of the list, the print them out from oldest to newest
1439     NSMutableArray *itemsToPrint = [[NSMutableArray alloc] init];
1440     for (int i = [bfList forwardListCount]; i > 0; i--) {
1441         WebHistoryItem *item = [bfList itemAtIndex:i];
1442         // something is wrong if the item from the last test is in the forward part of the b/f list
1443         assert(item != prevTestBFItem);
1444         [itemsToPrint addObject:item];
1445     }
1446             
1447     assert([bfList currentItem] != prevTestBFItem);
1448     [itemsToPrint addObject:[bfList currentItem]];
1449     int currentItemIndex = [itemsToPrint count] - 1;
1450
1451     for (int i = -1; i >= -[bfList backListCount]; i--) {
1452         WebHistoryItem *item = [bfList itemAtIndex:i];
1453         if (item == prevTestBFItem)
1454             break;
1455         [itemsToPrint addObject:item];
1456     }
1457
1458     for (int i = [itemsToPrint count]-1; i >= 0; i--)
1459         dumpHistoryItem([itemsToPrint objectAtIndex:i], 8, i == currentItemIndex);
1460
1461     [itemsToPrint release];
1462     printf("===============================================\n");
1463 }
1464
1465 #if !PLATFORM(IOS)
1466 static void changeWindowScaleIfNeeded(const char* testPathOrUR)
1467 {
1468     bool hasHighDPIWindow = [[[mainFrame webView] window] backingScaleFactor] != 1;
1469     WTF::String localPathOrUrl = String(testPathOrUR);
1470     bool needsHighDPIWindow = localPathOrUrl.findIgnoringCase("hidpi-") != notFound;
1471     if (hasHighDPIWindow == needsHighDPIWindow)
1472         return;
1473
1474     CGFloat newScaleFactor = needsHighDPIWindow ? 2 : 1;
1475     // When the new scale factor is set on the window first, WebView doesn't see it as a new scale and stops propagating the behavior change to WebCore::Page.
1476     gTestRunner->setBackingScaleFactor(newScaleFactor);
1477     [[[mainFrame webView] window] _setWindowResolution:newScaleFactor displayIfChanged:YES];
1478 }
1479 #endif
1480
1481 static void sizeWebViewForCurrentTest()
1482 {
1483     [uiDelegate resetWindowOrigin];
1484
1485     // W3C SVG tests expect to be 480x360
1486     bool isSVGW3CTest = (gTestRunner->testPathOrURL().find("svg/W3C-SVG-1.1") != string::npos);
1487     if (isSVGW3CTest)
1488         [[mainFrame webView] setFrameSize:NSMakeSize(TestRunner::w3cSVGViewWidth, TestRunner::w3cSVGViewHeight)];
1489     else
1490         [[mainFrame webView] setFrameSize:NSMakeSize(TestRunner::viewWidth, TestRunner::viewHeight)];
1491 }
1492
1493 static const char *methodNameStringForFailedTest()
1494 {
1495     const char *errorMessage;
1496     if (gTestRunner->dumpAsText())
1497         errorMessage = "[documentElement innerText]";
1498     else if (gTestRunner->dumpDOMAsWebArchive())
1499         errorMessage = "[[mainFrame DOMDocument] webArchive]";
1500     else if (gTestRunner->dumpSourceAsWebArchive())
1501         errorMessage = "[[mainFrame dataSource] webArchive]";
1502     else
1503         errorMessage = "[mainFrame renderTreeAsExternalRepresentation]";
1504
1505     return errorMessage;
1506 }
1507
1508 static void dumpBackForwardListForAllWindows()
1509 {
1510     CFArrayRef openWindows = (CFArrayRef)[DumpRenderTreeWindow openWindows];
1511     unsigned count = CFArrayGetCount(openWindows);
1512     for (unsigned i = 0; i < count; i++) {
1513         NSWindow *window = (NSWindow *)CFArrayGetValueAtIndex(openWindows, i);
1514 #if !PLATFORM(IOS)
1515         WebView *webView = [[[window contentView] subviews] objectAtIndex:0];
1516 #else
1517         ASSERT([[window contentView] isKindOfClass:[WebView class]]);
1518         WebView *webView = (WebView *)[window contentView];
1519 #endif
1520         dumpBackForwardListForWebView(webView);
1521     }
1522 }
1523
1524 static void invalidateAnyPreviousWaitToDumpWatchdog()
1525 {
1526     if (waitToDumpWatchdog) {
1527         CFRunLoopTimerInvalidate(waitToDumpWatchdog);
1528         CFRelease(waitToDumpWatchdog);
1529         waitToDumpWatchdog = 0;
1530     }
1531 }
1532
1533 void setWaitToDumpWatchdog(CFRunLoopTimerRef timer)
1534 {
1535     ASSERT(timer);
1536     ASSERT(shouldSetWaitToDumpWatchdog());
1537     waitToDumpWatchdog = timer;
1538     CFRunLoopAddTimer(CFRunLoopGetCurrent(), waitToDumpWatchdog, kCFRunLoopCommonModes);
1539 }
1540
1541 bool shouldSetWaitToDumpWatchdog()
1542 {
1543     return !waitToDumpWatchdog && useTimeoutWatchdog;
1544 }
1545
1546 static void updateDisplay()
1547 {
1548     WebView *webView = [mainFrame webView];
1549
1550     if ([webView _isUsingAcceleratedCompositing])
1551         [webView display];
1552     else
1553         [webView displayIfNeeded];
1554 }
1555
1556 void dump()
1557 {
1558 #if PLATFORM(IOS)
1559     // This can get called on the web thread if from a JavaScript notifyDone().
1560     if (WebThreadIsCurrent()) {
1561         [(DumpRenderTree *)UIApp _deferDumpToMainThread];
1562         return;
1563     }
1564     WebThreadLock();
1565 #endif
1566
1567     updateDisplay();
1568
1569     invalidateAnyPreviousWaitToDumpWatchdog();
1570     ASSERT(!gTestRunner->hasPendingWebNotificationClick());
1571
1572     if (dumpTree) {
1573         NSString *resultString = nil;
1574         NSData *resultData = nil;
1575         NSString *resultMimeType = @"text/plain";
1576
1577         if ([[[mainFrame dataSource] _responseMIMEType] isEqualToString:@"text/plain"]) {
1578             gTestRunner->setDumpAsText(true);
1579             gTestRunner->setGeneratePixelResults(false);
1580         }
1581         if (gTestRunner->dumpAsAudio()) {
1582             resultData = dumpAudio();
1583             resultMimeType = @"audio/wav";
1584         } else if (gTestRunner->dumpAsText()) {
1585             resultString = dumpFramesAsText(mainFrame);
1586         } else if (gTestRunner->dumpAsPDF()) {
1587             resultData = dumpFrameAsPDF(mainFrame);
1588             resultMimeType = @"application/pdf";
1589         } else if (gTestRunner->dumpDOMAsWebArchive()) {
1590             WebArchive *webArchive = [[mainFrame DOMDocument] webArchive];
1591             resultString = CFBridgingRelease(createXMLStringFromWebArchiveData((CFDataRef)[webArchive data]));
1592             resultMimeType = @"application/x-webarchive";
1593         } else if (gTestRunner->dumpSourceAsWebArchive()) {
1594             WebArchive *webArchive = [[mainFrame dataSource] webArchive];
1595             resultString = CFBridgingRelease(createXMLStringFromWebArchiveData((CFDataRef)[webArchive data]));
1596             resultMimeType = @"application/x-webarchive";
1597         } else
1598             resultString = [mainFrame renderTreeAsExternalRepresentationForPrinting:gTestRunner->isPrinting()];
1599
1600         if (resultString && !resultData)
1601             resultData = [resultString dataUsingEncoding:NSUTF8StringEncoding];
1602
1603         printf("Content-Type: %s\n", [resultMimeType UTF8String]);
1604
1605         WTF::FastMallocStatistics mallocStats = WTF::fastMallocStatistics();
1606         printf("DumpMalloc: %li\n", mallocStats.committedVMBytes);
1607
1608         if (gTestRunner->dumpAsAudio())
1609             printf("Content-Length: %lu\n", static_cast<unsigned long>([resultData length]));
1610
1611         if (resultData) {
1612             fwrite([resultData bytes], 1, [resultData length], stdout);
1613
1614             if (!gTestRunner->dumpAsText() && !gTestRunner->dumpDOMAsWebArchive() && !gTestRunner->dumpSourceAsWebArchive() && !gTestRunner->dumpAsAudio())
1615                 dumpFrameScrollPosition(mainFrame);
1616
1617             if (gTestRunner->dumpBackForwardList())
1618                 dumpBackForwardListForAllWindows();
1619         } else
1620             printf("ERROR: nil result from %s", methodNameStringForFailedTest());
1621
1622         // Stop the watchdog thread before we leave this test to make sure it doesn't
1623         // fire in between tests causing the next test to fail.
1624         // This is a speculative fix for: https://bugs.webkit.org/show_bug.cgi?id=32339
1625         invalidateAnyPreviousWaitToDumpWatchdog();
1626
1627         if (printSeparators) {
1628             puts("#EOF");       // terminate the content block
1629             fputs("#EOF\n", stderr);
1630         }            
1631     }
1632
1633     if (dumpPixelsForCurrentTest && gTestRunner->generatePixelResults())
1634         // FIXME: when isPrinting is set, dump the image with page separators.
1635         dumpWebViewAsPixelsAndCompareWithExpected(gTestRunner->expectedPixelHash());
1636
1637     puts("#EOF");   // terminate the (possibly empty) pixels block
1638
1639     fflush(stdout);
1640     fflush(stderr);
1641
1642     done = YES;
1643     CFRunLoopStop(CFRunLoopGetMain());
1644 }
1645
1646 static bool shouldLogFrameLoadDelegates(const char* pathOrURL)
1647 {
1648     return strstr(pathOrURL, "loading/");
1649 }
1650
1651 static bool shouldLogHistoryDelegates(const char* pathOrURL)
1652 {
1653     return strstr(pathOrURL, "globalhistory/");
1654 }
1655
1656 static bool shouldDumpAsText(const char* pathOrURL)
1657 {
1658     return strstr(pathOrURL, "dumpAsText/");
1659 }
1660
1661 static bool shouldEnableDeveloperExtras(const char* pathOrURL)
1662 {
1663     return true;
1664 }
1665
1666 #if PLATFORM(IOS)
1667 static bool shouldMakeViewportFlexible(const char* pathOrURL)
1668 {
1669     return strstr(pathOrURL, "viewport/");
1670 }
1671 #endif
1672
1673 static void resetWebViewToConsistentStateBeforeTesting()
1674 {
1675     WebView *webView = [mainFrame webView];
1676 #if PLATFORM(IOS)
1677     adjustWebDocumentForStandardViewport(gWebBrowserView, gWebScrollView);
1678     [webView _setAllowsMessaging:YES];
1679     [mainFrame setMediaDataLoadsAutomatically:YES];
1680 #endif
1681     [webView setEditable:NO];
1682     [(EditingDelegate *)[webView editingDelegate] setAcceptsEditing:YES];
1683     [webView makeTextStandardSize:nil];
1684     [webView resetPageZoom:nil];
1685     [webView _scaleWebView:1.0 atOrigin:NSZeroPoint];
1686 #if !PLATFORM(IOS)
1687     [webView _setCustomBackingScaleFactor:0];
1688 #endif
1689     [webView setTabKeyCyclesThroughElements:YES];
1690     [webView setPolicyDelegate:defaultPolicyDelegate];
1691     [policyDelegate setPermissive:NO];
1692     [policyDelegate setControllerToNotifyDone:0];
1693     [frameLoadDelegate resetToConsistentState];
1694 #if !PLATFORM(IOS)
1695     [webView _setDashboardBehavior:WebDashboardBehaviorUseBackwardCompatibilityMode to:NO];
1696 #endif
1697     [webView _clearMainFrameName];
1698     [[webView undoManager] removeAllActions];
1699     [WebView _removeAllUserContentFromGroup:[webView groupName]];
1700 #if !PLATFORM(IOS)
1701     [[webView window] setAutodisplay:NO];
1702 #endif
1703     [webView setTracksRepaints:NO];
1704     
1705     resetWebPreferencesToConsistentValues();
1706
1707     TestRunner::setSerializeHTTPLoads(false);
1708
1709     setlocale(LC_ALL, "");
1710
1711     if (gTestRunner) {
1712         WebCoreTestSupport::resetInternalsObject([mainFrame globalContext]);
1713         // in the case that a test using the chrome input field failed, be sure to clean up for the next test
1714         gTestRunner->removeChromeInputField();
1715     }
1716
1717 #if !PLATFORM(IOS)
1718     [webView setContinuousSpellCheckingEnabled:YES];
1719     [webView setAutomaticQuoteSubstitutionEnabled:NO];
1720     [webView setAutomaticLinkDetectionEnabled:NO];
1721     [webView setAutomaticDashSubstitutionEnabled:NO];
1722     [webView setAutomaticTextReplacementEnabled:NO];
1723     [webView setAutomaticSpellingCorrectionEnabled:YES];
1724     [webView setGrammarCheckingEnabled:YES];
1725
1726     [WebView _setUsesTestModeFocusRingColor:YES];
1727 #endif
1728     [WebView _resetOriginAccessWhitelists];
1729     [WebView _setAllowsRoundingHacks:NO];
1730
1731     [[MockGeolocationProvider shared] stopTimer];
1732     [[MockWebNotificationProvider shared] reset];
1733     
1734 #if !PLATFORM(IOS)
1735     // Clear the contents of the general pasteboard
1736     [[NSPasteboard generalPasteboard] declareTypes:[NSArray arrayWithObject:NSStringPboardType] owner:nil];
1737 #endif
1738
1739     [mainFrame _clearOpener];
1740 }
1741
1742 #if PLATFORM(IOS)
1743 // Work around <rdar://problem/9909073> WebKit's method of calling delegates on
1744 // the main thread is not thread safe. If the web thread is attempting to call
1745 // out to a delegate method on the main thread, we want to spin the main thread
1746 // run loop until the delegate method completes before taking the web thread
1747 // lock to prevent potentially re-entering WebCore.
1748 static void WebThreadLockAfterDelegateCallbacksHaveCompleted()
1749 {
1750     dispatch_semaphore_t delegateSemaphore = dispatch_semaphore_create(0);
1751     WebThreadRun(^{
1752         dispatch_semaphore_signal(delegateSemaphore);
1753     });
1754
1755     while (dispatch_semaphore_wait(delegateSemaphore, DISPATCH_TIME_NOW)) {
1756         NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
1757         [[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantPast]];
1758         [pool release];
1759     }
1760
1761     WebThreadLock();
1762     
1763     dispatch_release(delegateSemaphore);
1764 }
1765 #endif
1766
1767 static void runTest(const string& inputLine)
1768 {
1769     ASSERT(!inputLine.empty());
1770
1771     TestCommand command = parseInputLine(inputLine);
1772     const string& pathOrURL = command.pathOrURL;
1773     dumpPixelsForCurrentTest = command.shouldDumpPixels || dumpPixelsForAllTests;
1774
1775     NSString *pathOrURLString = [NSString stringWithUTF8String:pathOrURL.c_str()];
1776     if (!pathOrURLString) {
1777         fprintf(stderr, "Failed to parse \"%s\" as UTF-8\n", pathOrURL.c_str());
1778         return;
1779     }
1780
1781     NSURL *url;
1782     if ([pathOrURLString hasPrefix:@"http://"] || [pathOrURLString hasPrefix:@"https://"] || [pathOrURLString hasPrefix:@"file://"])
1783         url = [NSURL URLWithString:pathOrURLString];
1784     else
1785         url = [NSURL fileURLWithPath:pathOrURLString];
1786     if (!url) {
1787         fprintf(stderr, "Failed to parse \"%s\" as a URL\n", pathOrURL.c_str());
1788         return;
1789     }
1790
1791     const char* testURL([[url absoluteString] UTF8String]);
1792     
1793     resetWebViewToConsistentStateBeforeTesting();
1794 #if !PLATFORM(IOS)
1795     changeWindowScaleIfNeeded(testURL);
1796 #endif
1797
1798     gTestRunner = TestRunner::create(testURL, command.expectedPixelHash);
1799     topLoadingFrame = nil;
1800 #if !PLATFORM(IOS)
1801     ASSERT(!draggingInfo); // the previous test should have called eventSender.mouseUp to drop!
1802     releaseAndZero(&draggingInfo);
1803 #endif
1804     done = NO;
1805
1806     sizeWebViewForCurrentTest();
1807     gTestRunner->setIconDatabaseEnabled(false);
1808
1809     if (disallowedURLs)
1810         CFSetRemoveAllValues(disallowedURLs);
1811     if (shouldLogFrameLoadDelegates(pathOrURL.c_str()))
1812         gTestRunner->setDumpFrameLoadCallbacks(true);
1813
1814     if (shouldLogHistoryDelegates(pathOrURL.c_str()))
1815         [[mainFrame webView] setHistoryDelegate:historyDelegate];
1816     else
1817         [[mainFrame webView] setHistoryDelegate:nil];
1818
1819     if (shouldEnableDeveloperExtras(pathOrURL.c_str())) {
1820         gTestRunner->setDeveloperExtrasEnabled(true);
1821         if (shouldDumpAsText(pathOrURL.c_str())) {
1822             gTestRunner->setDumpAsText(true);
1823             gTestRunner->setGeneratePixelResults(false);
1824         }
1825     }
1826
1827 #if PLATFORM(IOS)
1828     if (shouldMakeViewportFlexible(pathOrURL.c_str()))
1829         adjustWebDocumentForFlexibleViewport(gWebBrowserView, gWebScrollView);
1830 #endif
1831
1832     if ([WebHistory optionalSharedHistory])
1833         [WebHistory setOptionalSharedHistory:nil];
1834     lastMousePosition = NSZeroPoint;
1835     lastClickPosition = NSZeroPoint;
1836
1837     [prevTestBFItem release];
1838     prevTestBFItem = [[[[mainFrame webView] backForwardList] currentItem] retain];
1839
1840     WorkQueue::shared()->clear();
1841     WorkQueue::shared()->setFrozen(false);
1842
1843     bool ignoreWebCoreNodeLeaks = shouldIgnoreWebCoreNodeLeaks(testURL);
1844     if (ignoreWebCoreNodeLeaks)
1845         [WebCoreStatistics startIgnoringWebCoreNodeLeaks];
1846
1847     NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
1848     [mainFrame loadRequest:[NSURLRequest requestWithURL:url]];
1849     [pool release];
1850
1851     while (!done) {
1852         pool = [[NSAutoreleasePool alloc] init];
1853         CFRunLoopRunInMode(kCFRunLoopDefaultMode, 10, false);
1854         [pool release];
1855     }
1856
1857 #if PLATFORM(IOS)
1858     [(DumpRenderTree *)UIApp _waitForWebThread];
1859     WebThreadLockAfterDelegateCallbacksHaveCompleted();
1860 #endif
1861     pool = [[NSAutoreleasePool alloc] init];
1862     [EventSendingController clearSavedEvents];
1863     [[mainFrame webView] setSelectedDOMRange:nil affinity:NSSelectionAffinityDownstream];
1864
1865     WorkQueue::shared()->clear();
1866
1867     if (gTestRunner->closeRemainingWindowsWhenComplete()) {
1868         NSArray* array = [DumpRenderTreeWindow openWindows];
1869
1870         unsigned count = [array count];
1871         for (unsigned i = 0; i < count; i++) {
1872             NSWindow *window = [array objectAtIndex:i];
1873
1874             // Don't try to close the main window
1875             if (window == [[mainFrame webView] window])
1876                 continue;
1877             
1878 #if !PLATFORM(IOS)
1879             WebView *webView = [[[window contentView] subviews] objectAtIndex:0];
1880 #else
1881             ASSERT([[window contentView] isKindOfClass:[WebView class]]);
1882             WebView *webView = (WebView *)[window contentView];
1883 #endif
1884
1885             [webView close];
1886             [window close];
1887         }
1888     }
1889
1890     // If developer extras enabled Web Inspector may have been open by the test.
1891     if (shouldEnableDeveloperExtras(pathOrURL.c_str())) {
1892         gTestRunner->closeWebInspector();
1893         gTestRunner->setDeveloperExtrasEnabled(false);
1894     }
1895
1896     resetWebViewToConsistentStateBeforeTesting();
1897
1898     // Loading an empty request synchronously replaces the document with a blank one, which is necessary
1899     // to stop timers, WebSockets and other activity that could otherwise spill output into next test's results.
1900     [mainFrame loadRequest:[NSURLRequest requestWithURL:[NSURL URLWithString:@""]]];
1901
1902     [pool release];
1903
1904     // We should only have our main window left open when we're done
1905     ASSERT(CFArrayGetCount(openWindowsRef) == 1);
1906     ASSERT(CFArrayGetValueAtIndex(openWindowsRef, 0) == [[mainFrame webView] window]);
1907
1908     gTestRunner.clear();
1909
1910     if (ignoreWebCoreNodeLeaks)
1911         [WebCoreStatistics stopIgnoringWebCoreNodeLeaks];
1912
1913     if (gcBetweenTests)
1914         [WebCoreStatistics garbageCollectJavaScriptObjects];
1915 }
1916
1917 void displayWebView()
1918 {
1919 #if !PLATFORM(IOS)
1920     WebView *webView = [mainFrame webView];
1921     [webView display];
1922     
1923     [webView setTracksRepaints:YES];
1924     [webView resetTrackedRepaints];
1925 #else
1926     // FIXME: <rdar://problem/5106253> DumpRenderTree: fix DRT and ImageDiff to re-enable pixel tests
1927     [gDrtWindow layoutTilesNow];
1928     [gDrtWindow setNeedsDisplayInRect:[gDrtWindow frame]];
1929     [CATransaction flush];
1930 #endif
1931 }
1932
1933 #if !PLATFORM(IOS)
1934 @implementation DumpRenderTreeEvent
1935
1936 + (NSPoint)mouseLocation
1937 {
1938     return [[[mainFrame webView] window] convertBaseToScreen:lastMousePosition];
1939 }
1940
1941 @end
1942
1943 @implementation DumpRenderTreeApplication
1944
1945 - (BOOL)isRunning
1946 {
1947     // <rdar://problem/7686123> Java plug-in freezes unless NSApplication is running
1948     return YES;
1949 }
1950
1951 @end
1952 #endif