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