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