Don't use (Details) when exposing SPI
[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 ()
149 + (void)_setAlertType:(NSUInteger)alertType;
150 @end
151 #endif
152
153 @interface WebView ()
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     [preferences setMetaRefreshEnabled:YES];
932
933     if (persistentUserStyleSheetLocation) {
934         [preferences setUserStyleSheetLocation:[NSURL URLWithString:(NSString *)(persistentUserStyleSheetLocation.get())]];
935         [preferences setUserStyleSheetEnabled:YES];
936     } else
937         [preferences setUserStyleSheetEnabled:NO];
938 #if PLATFORM(IOS)
939     [preferences setMediaPlaybackAllowsInline:YES];
940     [preferences setMediaPlaybackRequiresUserGesture:NO];
941
942     // Enable the tracker before creating the first WebView will
943     // cause initialization to use the correct database paths.
944     [preferences setStorageTrackerEnabled:YES];
945 #endif
946
947 #if ENABLE(IOS_TEXT_AUTOSIZING)
948     // Disable text autosizing by default.
949     [preferences _setMinimumZoomFontSize:0];
950 #endif
951
952     // The back/forward cache is causing problems due to layouts during transition from one page to another.
953     // So, turn it off for now, but we might want to turn it back on some day.
954     [preferences setUsesPageCache:NO];
955     [preferences setAcceleratedCompositingEnabled:YES];
956 #if USE(CA)
957     [preferences setCanvasUsesAcceleratedDrawing:YES];
958     [preferences setAcceleratedDrawingEnabled:useAcceleratedDrawing];
959 #endif
960     [preferences setWebGLEnabled:NO];
961     [preferences setCSSRegionsEnabled:YES];
962     [preferences setUsePreHTML5ParserQuirks:NO];
963     [preferences setAsynchronousSpellCheckingEnabled:NO];
964 #if !PLATFORM(IOS)
965     ASSERT([preferences mockScrollbarsEnabled]);
966 #endif
967
968 #if ENABLE(WEB_AUDIO)
969     [preferences setWebAudioEnabled:YES];
970 #endif
971
972 #if ENABLE(IOS_TEXT_AUTOSIZING)
973     // Disable text autosizing by default.
974     [preferences _setMinimumZoomFontSize:0];
975 #endif
976
977 #if ENABLE(MEDIA_SOURCE)
978     [preferences setMediaSourceEnabled:YES];
979 #endif
980
981     [WebPreferences _clearNetworkLoaderSession];
982     [WebPreferences _setCurrentNetworkLoaderSessionCookieAcceptPolicy:NSHTTPCookieAcceptPolicyOnlyFromMainDocumentDomain];
983 }
984
985 // Called once on DumpRenderTree startup.
986 static void setDefaultsToConsistentValuesForTesting()
987 {
988 #if PLATFORM(IOS)
989     WebThreadLock();
990 #endif
991
992     static const int NoFontSmoothing = 0;
993     static const int BlueTintedAppearance = 1;
994
995     NSString *libraryPath = libraryPathForDumpRenderTree();
996
997     NSDictionary *dict = @{
998         @"AppleKeyboardUIMode": @1,
999         @"AppleAntiAliasingThreshold": @4,
1000         @"AppleFontSmoothing": @(NoFontSmoothing),
1001         @"AppleAquaColorVariant": @(BlueTintedAppearance),
1002         @"AppleHighlightColor": @"0.709800 0.835300 1.000000",
1003         @"AppleOtherHighlightColor":@"0.500000 0.500000 0.500000",
1004         @"AppleLanguages": @[ @"en" ],
1005         WebKitEnableFullDocumentTeardownPreferenceKey: @YES,
1006         WebKitFullScreenEnabledPreferenceKey: @YES,
1007         @"UseWebKitWebInspector": @YES,
1008 #if !PLATFORM(IOS)
1009         @"NSPreferredSpellServerLanguage": @"en_US",
1010         @"NSUserDictionaryReplacementItems": @[],
1011         @"NSTestCorrectionDictionary": @{
1012             @"notationl": @"notational",
1013             @"mesage": @"message",
1014             @"wouldn": @"would",
1015             @"wellcome": @"welcome",
1016             @"hellolfworld": @"hello\nworld"
1017         },
1018 #endif
1019         @"AppleScrollBarVariant": @"DoubleMax",
1020 #if !PLATFORM(IOS)
1021         @"NSScrollAnimationEnabled": @NO,
1022 #endif
1023         @"NSOverlayScrollersEnabled": @NO,
1024         @"AppleShowScrollBars": @"Always",
1025         @"NSButtonAnimationsEnabled": @NO, // Ideally, we should find a way to test animations, but for now, make sure that the dumped snapshot matches actual state.
1026 #if PLATFORM(MAC) && __MAC_OS_X_VERSION_MIN_REQUIRED > 101000
1027         @"AppleSystemFontOSSubversion": @(10),
1028 #endif
1029         @"NSWindowDisplayWithRunLoopObserver": @YES, // Temporary workaround, see <rdar://problem/20351297>.
1030     };
1031
1032     [[NSUserDefaults standardUserDefaults] setValuesForKeysWithDictionary:dict];
1033
1034 #if PLATFORM(MAC)
1035     // Make NSFont use the new defaults.
1036     [NSFont initialize];
1037 #endif
1038
1039     NSDictionary *processInstanceDefaults = @{
1040         WebDatabaseDirectoryDefaultsKey: [libraryPath stringByAppendingPathComponent:@"Databases"],
1041         WebStorageDirectoryDefaultsKey: [libraryPath stringByAppendingPathComponent:@"LocalStorage"],
1042         WebKitLocalCacheDefaultsKey: [libraryPath stringByAppendingPathComponent:@"LocalCache"],
1043 #if PLATFORM(MAC) && __MAC_OS_X_VERSION_MIN_REQUIRED > 101000
1044         // This needs to also be added to argument domain because of <rdar://problem/20210002>.
1045         @"AppleSystemFontOSSubversion": @(10),
1046 #endif
1047     };
1048
1049     [[NSUserDefaults standardUserDefaults] setVolatileDomain:processInstanceDefaults forName:NSArgumentDomain];
1050 }
1051
1052 static void runThread(void* arg)
1053 {
1054     static ThreadIdentifier previousId = 0;
1055     ThreadIdentifier currentId = currentThread();
1056     // Verify 2 successive threads do not get the same Id.
1057     ASSERT(previousId != currentId);
1058     previousId = currentId;
1059 }
1060
1061 static void* runPthread(void* arg)
1062 {
1063     runThread(arg);
1064     return 0;
1065 }
1066
1067 static void testThreadIdentifierMap()
1068 {
1069     // Imitate 'foreign' threads that are not created by WTF.
1070     pthread_t pthread;
1071     pthread_create(&pthread, 0, &runPthread, 0);
1072     pthread_join(pthread, 0);
1073
1074     pthread_create(&pthread, 0, &runPthread, 0);
1075     pthread_join(pthread, 0);
1076
1077     // Now create another thread using WTF. On OSX, it will have the same pthread handle
1078     // but should get a different ThreadIdentifier.
1079     createThread(runThread, 0, "DumpRenderTree: test");
1080 }
1081
1082 static void allocateGlobalControllers()
1083 {
1084     // FIXME: We should remove these and move to the ObjC standard [Foo sharedInstance] model
1085     gNavigationController = [[NavigationController alloc] init];
1086     frameLoadDelegate = [[FrameLoadDelegate alloc] init];
1087     uiDelegate = [[UIDelegate alloc] init];
1088     editingDelegate = [[EditingDelegate alloc] init];
1089     resourceLoadDelegate = [[ResourceLoadDelegate alloc] init];
1090     policyDelegate = [[PolicyDelegate alloc] init];
1091     historyDelegate = [[HistoryDelegate alloc] init];
1092     defaultPolicyDelegate = [[DefaultPolicyDelegate alloc] init];
1093 #if PLATFORM(IOS)
1094     scrollViewResizerDelegate = [[ScrollViewResizerDelegate alloc] init];
1095 #endif
1096 }
1097
1098 // ObjC++ doens't seem to let me pass NSObject*& sadly.
1099 static inline void releaseAndZero(NSObject** object)
1100 {
1101     [*object release];
1102     *object = nil;
1103 }
1104
1105 static void releaseGlobalControllers()
1106 {
1107     releaseAndZero(&gNavigationController);
1108     releaseAndZero(&frameLoadDelegate);
1109     releaseAndZero(&editingDelegate);
1110     releaseAndZero(&resourceLoadDelegate);
1111     releaseAndZero(&uiDelegate);
1112     releaseAndZero(&policyDelegate);
1113 #if PLATFORM(IOS)
1114     releaseAndZero(&scrollViewResizerDelegate);
1115 #endif
1116 }
1117
1118 static void initializeGlobalsFromCommandLineOptions(int argc, const char *argv[])
1119 {
1120     struct option options[] = {
1121         {"notree", no_argument, &dumpTree, NO},
1122         {"pixel-tests", no_argument, &dumpPixelsForAllTests, YES},
1123         {"tree", no_argument, &dumpTree, YES},
1124         {"threaded", no_argument, &threaded, YES},
1125         {"complex-text", no_argument, &forceComplexText, YES},
1126         {"accelerated-drawing", no_argument, &useAcceleratedDrawing, YES},
1127         {"gc-between-tests", no_argument, &gcBetweenTests, YES},
1128         {"no-timeout", no_argument, &useTimeoutWatchdog, NO},
1129         {"allowed-host", required_argument, nullptr, 'a'},
1130         {"show-webview", no_argument, &showWebView, YES},
1131         {nullptr, 0, nullptr, 0}
1132     };
1133     
1134     int option;
1135     while ((option = getopt_long(argc, (char * const *)argv, "", options, nullptr)) != -1) {
1136         switch (option) {
1137             case '?':   // unknown or ambiguous option
1138             case ':':   // missing argument
1139                 exit(1);
1140                 break;
1141             case 'a': // "allowed-host"
1142                 allowedHosts.insert(optarg);
1143                 break;
1144         }
1145     }
1146 }
1147
1148 static void addTestPluginsToPluginSearchPath(const char* executablePath)
1149 {
1150 #if !PLATFORM(IOS)
1151     NSString *pwd = [[NSString stringWithUTF8String:executablePath] stringByDeletingLastPathComponent];
1152     [WebPluginDatabase setAdditionalWebPlugInPaths:[NSArray arrayWithObject:pwd]];
1153     [[WebPluginDatabase sharedDatabase] refresh];
1154 #endif
1155 }
1156
1157 static bool useLongRunningServerMode(int argc, const char *argv[])
1158 {
1159     // This assumes you've already called getopt_long
1160     return (argc == optind+1 && strcmp(argv[optind], "-") == 0);
1161 }
1162
1163 static void runTestingServerLoop()
1164 {
1165     // When DumpRenderTree run in server mode, we just wait around for file names
1166     // to be passed to us and read each in turn, passing the results back to the client
1167     char filenameBuffer[2048];
1168     while (fgets(filenameBuffer, sizeof(filenameBuffer), stdin)) {
1169         char *newLineCharacter = strchr(filenameBuffer, '\n');
1170         if (newLineCharacter)
1171             *newLineCharacter = '\0';
1172
1173         if (strlen(filenameBuffer) == 0)
1174             continue;
1175
1176         runTest(filenameBuffer);
1177     }
1178 }
1179
1180 static void prepareConsistentTestingEnvironment()
1181 {
1182 #if !PLATFORM(IOS)
1183     poseAsClass("DumpRenderTreePasteboard", "NSPasteboard");
1184     poseAsClass("DumpRenderTreeEvent", "NSEvent");
1185 #else
1186     poseAsClass("DumpRenderTreeEvent", "GSEvent");
1187 #endif
1188
1189     [[WebPreferences standardPreferences] setAutosaves:NO];
1190
1191 #if !PLATFORM(IOS)
1192     // +[WebPreferences _switchNetworkLoaderToNewTestingSession] calls +[NSURLCache sharedURLCache], which initializes a default cache on disk.
1193     // Making the shared cache memory-only avoids touching the file system.
1194     RetainPtr<NSURLCache> sharedCache =
1195         adoptNS([[NSURLCache alloc] initWithMemoryCapacity:1024 * 1024
1196                                       diskCapacity:0
1197                                           diskPath:nil]);
1198     [NSURLCache setSharedURLCache:sharedCache.get()];
1199
1200     [WebPreferences _switchNetworkLoaderToNewTestingSession];
1201
1202     adjustFonts();
1203     registerMockScrollbars();
1204
1205     // The mock scrollbars setting cannot be modified after creating a view, so we have to do it now.
1206     [[WebPreferences standardPreferences] setMockScrollbarsEnabled:YES];
1207 #else
1208     activateFontsIOS();
1209 #endif
1210     
1211     allocateGlobalControllers();
1212     
1213     makeLargeMallocFailSilently();
1214
1215 #if PLATFORM(MAC)
1216     NSActivityOptions options = (NSActivityUserInitiatedAllowingIdleSystemSleep | NSActivityLatencyCritical) & ~(NSActivitySuddenTerminationDisabled | NSActivityAutomaticTerminationDisabled);
1217     static id assertion = [[[NSProcessInfo processInfo] beginActivityWithOptions:options reason:@"DumpRenderTree should not be subject to process suppression"] retain];
1218     ASSERT_UNUSED(assertion, assertion);
1219 #endif
1220 }
1221
1222 const char crashedMessage[] = "#CRASHED\n";
1223
1224 void writeCrashedMessageOnFatalError(int signalCode)
1225 {
1226     // Reset the default action for the signal so that we run ReportCrash(8) on pending and
1227     // subsequent instances of the signal.
1228     signal(signalCode, SIG_DFL);
1229
1230     // WRITE(2) and FSYNC(2) are considered safe to call from a signal handler by SIGACTION(2).
1231     write(STDERR_FILENO, &crashedMessage[0], sizeof(crashedMessage) - 1);
1232     fsync(STDERR_FILENO);
1233 }
1234
1235 void dumpRenderTree(int argc, const char *argv[])
1236 {
1237 #if PLATFORM(IOS)
1238     NSString *identifier = [[NSBundle mainBundle] bundleIdentifier];
1239     const char *stdinPath = [[NSString stringWithFormat:@"/tmp/%@_IN", identifier] UTF8String];
1240     const char *stdoutPath = [[NSString stringWithFormat:@"/tmp/%@_OUT", identifier] UTF8String];
1241     const char *stderrPath = [[NSString stringWithFormat:@"/tmp/%@_ERROR", identifier] UTF8String];
1242
1243     int infd = open(stdinPath, O_RDWR);
1244     dup2(infd, STDIN_FILENO);
1245     int outfd = open(stdoutPath, O_RDWR);
1246     dup2(outfd, STDOUT_FILENO);
1247     int errfd = open(stderrPath, O_RDWR | O_NONBLOCK);
1248     dup2(errfd, STDERR_FILENO);
1249 #endif
1250
1251     signal(SIGILL, &writeCrashedMessageOnFatalError);
1252     signal(SIGFPE, &writeCrashedMessageOnFatalError);
1253     signal(SIGBUS, &writeCrashedMessageOnFatalError);
1254     signal(SIGSEGV, &writeCrashedMessageOnFatalError);
1255
1256     initializeGlobalsFromCommandLineOptions(argc, argv);
1257     prepareConsistentTestingEnvironment();
1258     addTestPluginsToPluginSearchPath(argv[0]);
1259
1260     if (forceComplexText)
1261         [WebView _setAlwaysUsesComplexTextCodePath:YES];
1262
1263 #if USE(APPKIT)
1264     [NSSound _setAlertType:0];
1265 #endif
1266
1267     WebView *webView = createWebViewAndOffscreenWindow();
1268     mainFrame = [webView mainFrame];
1269
1270     [[NSURLCache sharedURLCache] removeAllCachedResponses];
1271     [WebCache empty];
1272
1273     [NSURLRequest setAllowsAnyHTTPSCertificate:YES forHost:@"localhost"];
1274     [NSURLRequest setAllowsAnyHTTPSCertificate:YES forHost:@"127.0.0.1"];
1275
1276     // http://webkit.org/b/32689
1277     testThreadIdentifierMap();
1278
1279     if (threaded)
1280         startJavaScriptThreads();
1281
1282     if (useLongRunningServerMode(argc, argv)) {
1283         printSeparators = YES;
1284         runTestingServerLoop();
1285     } else {
1286         printSeparators = optind < argc - 1;
1287         for (int i = optind; i != argc; ++i)
1288             runTest(argv[i]);
1289     }
1290
1291     if (threaded)
1292         stopJavaScriptThreads();
1293
1294     destroyWebViewAndOffscreenWindow();
1295     
1296     releaseGlobalControllers();
1297     
1298 #if !PLATFORM(IOS)
1299     [DumpRenderTreePasteboard releaseLocalPasteboards];
1300 #endif
1301
1302     // FIXME: This should be moved onto TestRunner and made into a HashSet
1303     if (disallowedURLs) {
1304         CFRelease(disallowedURLs);
1305         disallowedURLs = 0;
1306     }
1307
1308 #if PLATFORM(IOS)
1309     close(infd);
1310     close(outfd);
1311     close(errfd);
1312 #endif
1313 }
1314
1315 #if PLATFORM(IOS)
1316 static int _argc;
1317 static const char **_argv;
1318
1319 @implementation DumpRenderTree
1320
1321 - (void)_runDumpRenderTree
1322 {
1323     dumpRenderTree(_argc, _argv);
1324 }
1325
1326 - (void)applicationDidFinishLaunching:(NSNotification *)notification
1327 {
1328     [self performSelectorOnMainThread:@selector(_runDumpRenderTree) withObject:nil waitUntilDone:NO];
1329 }
1330
1331 - (void)applicationDidEnterBackground:(UIApplication *)application
1332 {
1333     /* 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.
1334     */
1335     void (^expirationHandler)() = ^ {
1336         [application endBackgroundTask:backgroundTaskIdentifier];
1337         backgroundTaskIdentifier = UIBackgroundTaskInvalid;
1338     };
1339
1340     dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
1341
1342         NSTimeInterval timeRemaining;
1343         while (true) {
1344             timeRemaining = [application backgroundTimeRemaining];
1345             if (timeRemaining <= 10.0 || backgroundTaskIdentifier == UIBackgroundTaskInvalid) {
1346                 [application endBackgroundTask:backgroundTaskIdentifier];
1347                 backgroundTaskIdentifier = [application beginBackgroundTaskWithExpirationHandler:expirationHandler];
1348             }
1349             sleep(5);
1350         }
1351     });
1352 }
1353
1354 - (void)_webThreadEventLoopHasRun
1355 {
1356     ASSERT(!WebThreadIsCurrent());
1357     _hasFlushedWebThreadRunQueue = YES;
1358 }
1359
1360 - (void)_webThreadInvoked
1361 {
1362     ASSERT(WebThreadIsCurrent());
1363     dispatch_async(dispatch_get_main_queue(), ^{
1364         [self _webThreadEventLoopHasRun];
1365     });
1366 }
1367
1368 // The test can end in response to a delegate callback while there are still methods queued on the Web Thread.
1369 // If we do not ensure the Web Thread has been run, the callback can be done on a WebView that no longer exists.
1370 // To avoid this, _waitForWebThread dispatches a call to the WebThread event loop, actively processing the delegate
1371 // callbacks in the main thread while waiting for the call to be invoked on the Web Thread.
1372 - (void)_waitForWebThread
1373 {
1374     ASSERT(!WebThreadIsCurrent());
1375     _hasFlushedWebThreadRunQueue = NO;
1376     WebThreadRun(^{
1377         [self _webThreadInvoked];
1378     });
1379     while (!_hasFlushedWebThreadRunQueue) {
1380         NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
1381         [[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantPast]];
1382         [pool release];
1383     }
1384 }
1385
1386 @end
1387 #endif
1388
1389 static bool returningFromMain = false;
1390
1391 void atexitFunction()
1392 {
1393     if (returningFromMain)
1394         return;
1395
1396     NSLog(@"DumpRenderTree is exiting unexpectedly. Generating a crash log.");
1397     __builtin_trap();
1398 }
1399
1400 int DumpRenderTreeMain(int argc, const char *argv[])
1401 {
1402     atexit(atexitFunction);
1403
1404 #if PLATFORM(IOS)
1405     _UIApplicationLoadWebKit();
1406 #endif
1407     NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
1408
1409     setDefaultsToConsistentValuesForTesting(); // Must be called before NSApplication initialization.
1410
1411 #if !PLATFORM(IOS)
1412     [DumpRenderTreeApplication sharedApplication]; // Force AppKit to init itself
1413
1414     dumpRenderTree(argc, argv);
1415 #else
1416     _argc = argc;
1417     _argv = argv;
1418     UIApplicationMain(argc, (char**)argv, @"DumpRenderTree", @"DumpRenderTree");
1419 #endif
1420     [WebCoreStatistics garbageCollectJavaScriptObjects];
1421     [WebCoreStatistics emptyCache]; // Otherwise SVGImages trigger false positives for Frame/Node counts
1422     if (JSC::Options::logHeapStatisticsAtExit())
1423         JSC::HeapStatistics::reportSuccess();
1424     [pool release];
1425     returningFromMain = true;
1426     return 0;
1427 }
1428
1429 static NSInteger compareHistoryItems(id item1, id item2, void *context)
1430 {
1431     return [[item1 target] caseInsensitiveCompare:[item2 target]];
1432 }
1433
1434 static NSData *dumpAudio()
1435 {
1436     const vector<char>& dataVector = gTestRunner->audioResult();
1437     
1438     NSData *data = [NSData dataWithBytes:dataVector.data() length:dataVector.size()];
1439     return data;
1440 }
1441
1442 static void dumpHistoryItem(WebHistoryItem *item, int indent, BOOL current)
1443 {
1444     int start = 0;
1445     if (current) {
1446         printf("curr->");
1447         start = 6;
1448     }
1449     for (int i = start; i < indent; i++)
1450         putchar(' ');
1451     
1452     NSString *urlString = [item URLString];
1453     if ([[NSURL URLWithString:urlString] isFileURL]) {
1454         NSRange range = [urlString rangeOfString:@"/LayoutTests/"];
1455         urlString = [@"(file test):" stringByAppendingString:[urlString substringFromIndex:(range.length + range.location)]];
1456     }
1457     
1458     printf("%s", [urlString UTF8String]);
1459     NSString *target = [item target];
1460     if (target && [target length] > 0)
1461         printf(" (in frame \"%s\")", [target UTF8String]);
1462     if ([item isTargetItem])
1463         printf("  **nav target**");
1464     putchar('\n');
1465     NSArray *kids = [item children];
1466     if (kids) {
1467         // must sort to eliminate arbitrary result ordering which defeats reproducible testing
1468         kids = [kids sortedArrayUsingFunction:&compareHistoryItems context:nil];
1469         for (unsigned i = 0; i < [kids count]; i++)
1470             dumpHistoryItem([kids objectAtIndex:i], indent+4, NO);
1471     }
1472 }
1473
1474 static void dumpFrameScrollPosition(WebFrame *f)
1475 {
1476     WebScriptObject* scriptObject = [f windowObject];
1477     NSPoint scrollPosition = NSMakePoint(
1478         [[scriptObject valueForKey:@"pageXOffset"] floatValue],
1479         [[scriptObject valueForKey:@"pageYOffset"] floatValue]);
1480     if (ABS(scrollPosition.x) > 0.00000001 || ABS(scrollPosition.y) > 0.00000001) {
1481         if ([f parentFrame] != nil)
1482             printf("frame '%s' ", [[f name] UTF8String]);
1483         printf("scrolled to %.f,%.f\n", scrollPosition.x, scrollPosition.y);
1484     }
1485
1486     if (gTestRunner->dumpChildFrameScrollPositions()) {
1487         NSArray *kids = [f childFrames];
1488         if (kids)
1489             for (unsigned i = 0; i < [kids count]; i++)
1490                 dumpFrameScrollPosition([kids objectAtIndex:i]);
1491     }
1492 }
1493
1494 static NSString *dumpFramesAsText(WebFrame *frame)
1495 {
1496     DOMDocument *document = [frame DOMDocument];
1497     DOMElement *documentElement = [document documentElement];
1498
1499     if (!documentElement)
1500         return @"";
1501
1502     NSMutableString *result = [[[NSMutableString alloc] init] autorelease];
1503
1504     // Add header for all but the main frame.
1505     if ([frame parentFrame])
1506         result = [NSMutableString stringWithFormat:@"\n--------\nFrame: '%@'\n--------\n", [frame name]];
1507
1508     [result appendFormat:@"%@\n", [documentElement innerText]];
1509
1510     if (gTestRunner->dumpChildFramesAsText()) {
1511         NSArray *kids = [frame childFrames];
1512         if (kids) {
1513             for (unsigned i = 0; i < [kids count]; i++)
1514                 [result appendString:dumpFramesAsText([kids objectAtIndex:i])];
1515         }
1516     }
1517
1518     return result;
1519 }
1520
1521 static NSData *dumpFrameAsPDF(WebFrame *frame)
1522 {
1523 #if !PLATFORM(IOS)
1524     if (!frame)
1525         return nil;
1526
1527     // Sadly we have to dump to a file and then read from that file again
1528     // +[NSPrintOperation PDFOperationWithView:insideRect:] requires a rect and prints to a single page
1529     // likewise +[NSView dataWithPDFInsideRect:] also prints to a single continuous page
1530     // The goal of this function is to test "real" printing across multiple pages.
1531     // FIXME: It's possible there might be printing SPI to let us print a multi-page PDF to an NSData object
1532     NSString *path = [libraryPathForDumpRenderTree() stringByAppendingPathComponent:@"test.pdf"];
1533
1534     NSMutableDictionary *printInfoDict = [NSMutableDictionary dictionaryWithDictionary:[[NSPrintInfo sharedPrintInfo] dictionary]];
1535     [printInfoDict setObject:NSPrintSaveJob forKey:NSPrintJobDisposition];
1536     [printInfoDict setObject:path forKey:NSPrintSavePath];
1537
1538     NSPrintInfo *printInfo = [[NSPrintInfo alloc] initWithDictionary:printInfoDict];
1539     [printInfo setHorizontalPagination:NSAutoPagination];
1540     [printInfo setVerticalPagination:NSAutoPagination];
1541     [printInfo setVerticallyCentered:NO];
1542
1543     NSPrintOperation *printOperation = [NSPrintOperation printOperationWithView:[frame frameView] printInfo:printInfo];
1544     [printOperation setShowPanels:NO];
1545     [printOperation runOperation];
1546
1547     [printInfo release];
1548
1549     NSData *pdfData = [NSData dataWithContentsOfFile:path];
1550     [[NSFileManager defaultManager] removeFileAtPath:path handler:nil];
1551
1552     return pdfData;
1553 #else
1554     return nil;
1555 #endif
1556 }
1557
1558 static void dumpBackForwardListForWebView(WebView *view)
1559 {
1560     printf("\n============== Back Forward List ==============\n");
1561     WebBackForwardList *bfList = [view backForwardList];
1562
1563     // Print out all items in the list after prevTestBFItem, which was from the previous test
1564     // Gather items from the end of the list, the print them out from oldest to newest
1565     NSMutableArray *itemsToPrint = [[NSMutableArray alloc] init];
1566     for (int i = [bfList forwardListCount]; i > 0; i--) {
1567         WebHistoryItem *item = [bfList itemAtIndex:i];
1568         // something is wrong if the item from the last test is in the forward part of the b/f list
1569         assert(item != prevTestBFItem);
1570         [itemsToPrint addObject:item];
1571     }
1572             
1573     assert([bfList currentItem] != prevTestBFItem);
1574     [itemsToPrint addObject:[bfList currentItem]];
1575     int currentItemIndex = [itemsToPrint count] - 1;
1576
1577     for (int i = -1; i >= -[bfList backListCount]; i--) {
1578         WebHistoryItem *item = [bfList itemAtIndex:i];
1579         if (item == prevTestBFItem)
1580             break;
1581         [itemsToPrint addObject:item];
1582     }
1583
1584     for (int i = [itemsToPrint count]-1; i >= 0; i--)
1585         dumpHistoryItem([itemsToPrint objectAtIndex:i], 8, i == currentItemIndex);
1586
1587     [itemsToPrint release];
1588     printf("===============================================\n");
1589 }
1590
1591 #if !PLATFORM(IOS)
1592 static void changeWindowScaleIfNeeded(const char* testPathOrUR)
1593 {
1594     bool hasHighDPIWindow = [[[mainFrame webView] window] backingScaleFactor] != 1;
1595     WTF::String localPathOrUrl = String(testPathOrUR);
1596     bool needsHighDPIWindow = localPathOrUrl.findIgnoringCase("/hidpi-") != notFound;
1597     if (hasHighDPIWindow == needsHighDPIWindow)
1598         return;
1599
1600     CGFloat newScaleFactor = needsHighDPIWindow ? 2 : 1;
1601     // 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.
1602     gTestRunner->setBackingScaleFactor(newScaleFactor);
1603     [[[mainFrame webView] window] _setWindowResolution:newScaleFactor displayIfChanged:YES];
1604 }
1605 #endif
1606
1607 static void sizeWebViewForCurrentTest()
1608 {
1609     [uiDelegate resetWindowOrigin];
1610
1611     // W3C SVG tests expect to be 480x360
1612     bool isSVGW3CTest = (gTestRunner->testURL().find("svg/W3C-SVG-1.1") != string::npos);
1613     if (isSVGW3CTest)
1614         [[mainFrame webView] setFrameSize:NSMakeSize(TestRunner::w3cSVGViewWidth, TestRunner::w3cSVGViewHeight)];
1615     else
1616         [[mainFrame webView] setFrameSize:NSMakeSize(TestRunner::viewWidth, TestRunner::viewHeight)];
1617 }
1618
1619 static const char *methodNameStringForFailedTest()
1620 {
1621     const char *errorMessage;
1622     if (gTestRunner->dumpAsText())
1623         errorMessage = "[documentElement innerText]";
1624     else if (gTestRunner->dumpDOMAsWebArchive())
1625         errorMessage = "[[mainFrame DOMDocument] webArchive]";
1626     else if (gTestRunner->dumpSourceAsWebArchive())
1627         errorMessage = "[[mainFrame dataSource] webArchive]";
1628     else
1629         errorMessage = "[mainFrame renderTreeAsExternalRepresentation]";
1630
1631     return errorMessage;
1632 }
1633
1634 static void dumpBackForwardListForAllWindows()
1635 {
1636     CFArrayRef openWindows = (CFArrayRef)[DumpRenderTreeWindow openWindows];
1637     unsigned count = CFArrayGetCount(openWindows);
1638     for (unsigned i = 0; i < count; i++) {
1639         NSWindow *window = (NSWindow *)CFArrayGetValueAtIndex(openWindows, i);
1640 #if !PLATFORM(IOS)
1641         WebView *webView = [[[window contentView] subviews] objectAtIndex:0];
1642 #else
1643         ASSERT([[window contentView] isKindOfClass:[WebView class]]);
1644         WebView *webView = (WebView *)[window contentView];
1645 #endif
1646         dumpBackForwardListForWebView(webView);
1647     }
1648 }
1649
1650 static void invalidateAnyPreviousWaitToDumpWatchdog()
1651 {
1652     if (waitToDumpWatchdog) {
1653         CFRunLoopTimerInvalidate(waitToDumpWatchdog);
1654         CFRelease(waitToDumpWatchdog);
1655         waitToDumpWatchdog = 0;
1656     }
1657 }
1658
1659 void setWaitToDumpWatchdog(CFRunLoopTimerRef timer)
1660 {
1661     ASSERT(timer);
1662     ASSERT(shouldSetWaitToDumpWatchdog());
1663     waitToDumpWatchdog = timer;
1664     CFRunLoopAddTimer(CFRunLoopGetCurrent(), waitToDumpWatchdog, kCFRunLoopCommonModes);
1665 }
1666
1667 bool shouldSetWaitToDumpWatchdog()
1668 {
1669     return !waitToDumpWatchdog && useTimeoutWatchdog;
1670 }
1671
1672 static void updateDisplay()
1673 {
1674     WebView *webView = [mainFrame webView];
1675 #if PLATFORM(IOS)
1676     [gWebBrowserView layoutIfNeeded]; // Re-enables tile painting, which was disabled when committing the frame load.
1677     [gDrtWindow layoutTilesNow];
1678     [webView _flushCompositingChanges];
1679 #else
1680     if ([webView _isUsingAcceleratedCompositing])
1681         [webView display];
1682     else
1683         [webView displayIfNeeded];
1684 #endif
1685 }
1686
1687 void dump()
1688 {
1689 #if PLATFORM(IOS)
1690     WebThreadLock();
1691 #endif
1692
1693     updateDisplay();
1694
1695     invalidateAnyPreviousWaitToDumpWatchdog();
1696     ASSERT(!gTestRunner->hasPendingWebNotificationClick());
1697
1698     if (dumpTree) {
1699         NSString *resultString = nil;
1700         NSData *resultData = nil;
1701         NSString *resultMimeType = @"text/plain";
1702
1703         if ([[[mainFrame dataSource] _responseMIMEType] isEqualToString:@"text/plain"]) {
1704             gTestRunner->setDumpAsText(true);
1705             gTestRunner->setGeneratePixelResults(false);
1706         }
1707         if (gTestRunner->dumpAsAudio()) {
1708             resultData = dumpAudio();
1709             resultMimeType = @"audio/wav";
1710         } else if (gTestRunner->dumpAsText()) {
1711             resultString = dumpFramesAsText(mainFrame);
1712         } else if (gTestRunner->dumpAsPDF()) {
1713             resultData = dumpFrameAsPDF(mainFrame);
1714             resultMimeType = @"application/pdf";
1715         } else if (gTestRunner->dumpDOMAsWebArchive()) {
1716             WebArchive *webArchive = [[mainFrame DOMDocument] webArchive];
1717             resultString = CFBridgingRelease(createXMLStringFromWebArchiveData((CFDataRef)[webArchive data]));
1718             resultMimeType = @"application/x-webarchive";
1719         } else if (gTestRunner->dumpSourceAsWebArchive()) {
1720             WebArchive *webArchive = [[mainFrame dataSource] webArchive];
1721             resultString = CFBridgingRelease(createXMLStringFromWebArchiveData((CFDataRef)[webArchive data]));
1722             resultMimeType = @"application/x-webarchive";
1723         } else
1724             resultString = [mainFrame renderTreeAsExternalRepresentationForPrinting:gTestRunner->isPrinting()];
1725
1726         if (resultString && !resultData)
1727             resultData = [resultString dataUsingEncoding:NSUTF8StringEncoding];
1728
1729         printf("Content-Type: %s\n", [resultMimeType UTF8String]);
1730
1731         WTF::FastMallocStatistics mallocStats = WTF::fastMallocStatistics();
1732         printf("DumpMalloc: %li\n", mallocStats.committedVMBytes);
1733
1734         if (gTestRunner->dumpAsAudio())
1735             printf("Content-Length: %lu\n", static_cast<unsigned long>([resultData length]));
1736
1737         if (resultData) {
1738             fwrite([resultData bytes], 1, [resultData length], stdout);
1739
1740             if (!gTestRunner->dumpAsText() && !gTestRunner->dumpDOMAsWebArchive() && !gTestRunner->dumpSourceAsWebArchive() && !gTestRunner->dumpAsAudio())
1741                 dumpFrameScrollPosition(mainFrame);
1742
1743             if (gTestRunner->dumpBackForwardList())
1744                 dumpBackForwardListForAllWindows();
1745         } else
1746             printf("ERROR: nil result from %s", methodNameStringForFailedTest());
1747
1748         // Stop the watchdog thread before we leave this test to make sure it doesn't
1749         // fire in between tests causing the next test to fail.
1750         // This is a speculative fix for: https://bugs.webkit.org/show_bug.cgi?id=32339
1751         invalidateAnyPreviousWaitToDumpWatchdog();
1752
1753         if (printSeparators)
1754             puts("#EOF");       // terminate the content block
1755     }
1756
1757     if (dumpPixelsForCurrentTest && gTestRunner->generatePixelResults())
1758         // FIXME: when isPrinting is set, dump the image with page separators.
1759         dumpWebViewAsPixelsAndCompareWithExpected(gTestRunner->expectedPixelHash());
1760
1761     puts("#EOF");   // terminate the (possibly empty) pixels block
1762     fflush(stdout);
1763
1764     done = YES;
1765     CFRunLoopStop(CFRunLoopGetMain());
1766 }
1767
1768 static bool shouldLogFrameLoadDelegates(const char* pathOrURL)
1769 {
1770     return strstr(pathOrURL, "loading/");
1771 }
1772
1773 static bool shouldLogHistoryDelegates(const char* pathOrURL)
1774 {
1775     return strstr(pathOrURL, "globalhistory/");
1776 }
1777
1778 static bool shouldDumpAsText(const char* pathOrURL)
1779 {
1780     return strstr(pathOrURL, "dumpAsText/");
1781 }
1782
1783 static bool shouldEnableDeveloperExtras(const char* pathOrURL)
1784 {
1785     return true;
1786 }
1787
1788 #if PLATFORM(IOS)
1789 static bool shouldMakeViewportFlexible(const char* pathOrURL)
1790 {
1791     return strstr(pathOrURL, "viewport/");
1792 }
1793 #endif
1794
1795 static void resetWebViewToConsistentStateBeforeTesting()
1796 {
1797     WebView *webView = [mainFrame webView];
1798 #if PLATFORM(IOS)
1799     adjustWebDocumentForStandardViewport(gWebBrowserView, gWebScrollView);
1800     [webView _setAllowsMessaging:YES];
1801     [mainFrame setMediaDataLoadsAutomatically:YES];
1802 #endif
1803     [webView setEditable:NO];
1804     [(EditingDelegate *)[webView editingDelegate] setAcceptsEditing:YES];
1805     [webView makeTextStandardSize:nil];
1806     [webView resetPageZoom:nil];
1807     [webView _scaleWebView:1.0 atOrigin:NSZeroPoint];
1808 #if !PLATFORM(IOS)
1809     [webView _setCustomBackingScaleFactor:0];
1810 #endif
1811     [webView setTabKeyCyclesThroughElements:YES];
1812     [webView setPolicyDelegate:defaultPolicyDelegate];
1813     [policyDelegate setPermissive:NO];
1814     [policyDelegate setControllerToNotifyDone:0];
1815     [frameLoadDelegate resetToConsistentState];
1816 #if !PLATFORM(IOS)
1817     [webView _setDashboardBehavior:WebDashboardBehaviorUseBackwardCompatibilityMode to:NO];
1818 #endif
1819     [webView _clearMainFrameName];
1820     [[webView undoManager] removeAllActions];
1821     [WebView _removeAllUserContentFromGroup:[webView groupName]];
1822 #if !PLATFORM(IOS)
1823     [[webView window] setAutodisplay:NO];
1824 #endif
1825     [webView setTracksRepaints:NO];
1826
1827     [WebCache clearCachedCredentials];
1828     
1829     resetWebPreferencesToConsistentValues();
1830
1831     TestRunner::setSerializeHTTPLoads(false);
1832
1833     setlocale(LC_ALL, "");
1834
1835     if (gTestRunner) {
1836         WebCoreTestSupport::resetInternalsObject([mainFrame globalContext]);
1837         // in the case that a test using the chrome input field failed, be sure to clean up for the next test
1838         gTestRunner->removeChromeInputField();
1839     }
1840
1841 #if !PLATFORM(IOS)
1842     if (WebCore::Frame* frame = [webView _mainCoreFrame])
1843         WebCoreTestSupport::clearWheelEventTestTrigger(*frame);
1844 #endif
1845
1846 #if !PLATFORM(IOS)
1847     [webView setContinuousSpellCheckingEnabled:YES];
1848     [webView setAutomaticQuoteSubstitutionEnabled:NO];
1849     [webView setAutomaticLinkDetectionEnabled:NO];
1850     [webView setAutomaticDashSubstitutionEnabled:NO];
1851     [webView setAutomaticTextReplacementEnabled:NO];
1852     [webView setAutomaticSpellingCorrectionEnabled:YES];
1853     [webView setGrammarCheckingEnabled:YES];
1854
1855     [WebView _setUsesTestModeFocusRingColor:YES];
1856 #endif
1857     [WebView _resetOriginAccessWhitelists];
1858     [WebView _setAllowsRoundingHacks:NO];
1859
1860     [[MockGeolocationProvider shared] stopTimer];
1861     [[MockWebNotificationProvider shared] reset];
1862     
1863 #if !PLATFORM(IOS)
1864     // Clear the contents of the general pasteboard
1865     [[NSPasteboard generalPasteboard] declareTypes:[NSArray arrayWithObject:NSStringPboardType] owner:nil];
1866 #endif
1867
1868     [mainFrame _clearOpener];
1869 }
1870
1871 #if PLATFORM(IOS)
1872 // Work around <rdar://problem/9909073> WebKit's method of calling delegates on
1873 // the main thread is not thread safe. If the web thread is attempting to call
1874 // out to a delegate method on the main thread, we want to spin the main thread
1875 // run loop until the delegate method completes before taking the web thread
1876 // lock to prevent potentially re-entering WebCore.
1877 static void WebThreadLockAfterDelegateCallbacksHaveCompleted()
1878 {
1879     dispatch_semaphore_t delegateSemaphore = dispatch_semaphore_create(0);
1880     WebThreadRun(^{
1881         dispatch_semaphore_signal(delegateSemaphore);
1882     });
1883
1884     while (dispatch_semaphore_wait(delegateSemaphore, DISPATCH_TIME_NOW)) {
1885         NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
1886         [[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantPast]];
1887         [pool release];
1888     }
1889
1890     WebThreadLock();
1891     
1892     dispatch_release(delegateSemaphore);
1893 }
1894 #endif
1895
1896 static NSString *testPathFromURL(NSURL* url)
1897 {
1898     if ([url isFileURL]) {
1899         NSString *filePath = [url path];
1900         NSRange layoutTestsRange = [filePath rangeOfString:@"/LayoutTests/"];
1901         if (layoutTestsRange.location == NSNotFound)
1902             return nil;
1903
1904         return [filePath substringFromIndex:NSMaxRange(layoutTestsRange)];
1905     }
1906     
1907     // HTTP test URLs look like: http://127.0.0.1:8000/inspector/resource-tree/resource-request-content-after-loading-and-clearing-cache.html
1908     if (![[url scheme] isEqualToString:@"http"] && ![[url scheme] isEqualToString:@"https"])
1909         return nil;
1910
1911     if ([[url host] isEqualToString:@"127.0.0.1"] && ([[url port] intValue] == 8000 || [[url port] intValue] == 8443))
1912         return [url path];
1913
1914     return nil;
1915 }
1916
1917 static void runTest(const string& inputLine)
1918 {
1919     ASSERT(!inputLine.empty());
1920
1921     TestCommand command = parseInputLine(inputLine);
1922     const string& pathOrURL = command.pathOrURL;
1923     dumpPixelsForCurrentTest = command.shouldDumpPixels || dumpPixelsForAllTests;
1924
1925     NSString *pathOrURLString = [NSString stringWithUTF8String:pathOrURL.c_str()];
1926     if (!pathOrURLString) {
1927         fprintf(stderr, "Failed to parse \"%s\" as UTF-8\n", pathOrURL.c_str());
1928         return;
1929     }
1930
1931     NSURL *url;
1932     if ([pathOrURLString hasPrefix:@"http://"] || [pathOrURLString hasPrefix:@"https://"] || [pathOrURLString hasPrefix:@"file://"])
1933         url = [NSURL URLWithString:pathOrURLString];
1934     else
1935         url = [NSURL fileURLWithPath:pathOrURLString];
1936     if (!url) {
1937         fprintf(stderr, "Failed to parse \"%s\" as a URL\n", pathOrURL.c_str());
1938         return;
1939     }
1940
1941     NSString *testPath = testPathFromURL(url);
1942     if (!testPath)
1943         testPath = [url absoluteString];
1944     NSString *informationString = [@"CRASHING TEST: " stringByAppendingString:testPath];
1945     WKSetCrashReportApplicationSpecificInformation((CFStringRef)informationString);
1946
1947     const char* testURL([[url absoluteString] UTF8String]);
1948     
1949     resetWebViewToConsistentStateBeforeTesting();
1950 #if !PLATFORM(IOS)
1951     changeWindowScaleIfNeeded(testURL);
1952 #endif
1953
1954     gTestRunner = TestRunner::create(testURL, command.expectedPixelHash);
1955     gTestRunner->setAllowedHosts(allowedHosts);
1956     gTestRunner->setCustomTimeout(command.timeout);
1957     topLoadingFrame = nil;
1958 #if !PLATFORM(IOS)
1959     ASSERT(!draggingInfo); // the previous test should have called eventSender.mouseUp to drop!
1960     releaseAndZero(&draggingInfo);
1961 #endif
1962     done = NO;
1963
1964     sizeWebViewForCurrentTest();
1965     gTestRunner->setIconDatabaseEnabled(false);
1966     gTestRunner->clearAllApplicationCaches();
1967
1968     if (disallowedURLs)
1969         CFSetRemoveAllValues(disallowedURLs);
1970     if (shouldLogFrameLoadDelegates(pathOrURL.c_str()))
1971         gTestRunner->setDumpFrameLoadCallbacks(true);
1972
1973     if (shouldLogHistoryDelegates(pathOrURL.c_str()))
1974         [[mainFrame webView] setHistoryDelegate:historyDelegate];
1975     else
1976         [[mainFrame webView] setHistoryDelegate:nil];
1977
1978     if (shouldEnableDeveloperExtras(pathOrURL.c_str())) {
1979         gTestRunner->setDeveloperExtrasEnabled(true);
1980         if (shouldDumpAsText(pathOrURL.c_str())) {
1981             gTestRunner->setDumpAsText(true);
1982             gTestRunner->setGeneratePixelResults(false);
1983         }
1984     }
1985
1986 #if PLATFORM(IOS)
1987     if (shouldMakeViewportFlexible(pathOrURL.c_str()))
1988         adjustWebDocumentForFlexibleViewport(gWebBrowserView, gWebScrollView);
1989 #endif
1990
1991     if ([WebHistory optionalSharedHistory])
1992         [WebHistory setOptionalSharedHistory:nil];
1993
1994     lastMousePosition = NSZeroPoint;
1995     lastClickPosition = NSZeroPoint;
1996
1997     [prevTestBFItem release];
1998     prevTestBFItem = [[[[mainFrame webView] backForwardList] currentItem] retain];
1999
2000     auto& workQueue = WorkQueue::singleton();
2001     workQueue.clear();
2002     workQueue.setFrozen(false);
2003
2004     bool ignoreWebCoreNodeLeaks = shouldIgnoreWebCoreNodeLeaks(testURL);
2005     if (ignoreWebCoreNodeLeaks)
2006         [WebCoreStatistics startIgnoringWebCoreNodeLeaks];
2007
2008     NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
2009     [mainFrame loadRequest:[NSURLRequest requestWithURL:url]];
2010     [pool release];
2011
2012     while (!done) {
2013         pool = [[NSAutoreleasePool alloc] init];
2014         CFRunLoopRunInMode(kCFRunLoopDefaultMode, 10, false);
2015         [pool release];
2016     }
2017
2018 #if PLATFORM(IOS)
2019     [(DumpRenderTree *)UIApp _waitForWebThread];
2020     WebThreadLockAfterDelegateCallbacksHaveCompleted();
2021 #endif
2022     pool = [[NSAutoreleasePool alloc] init];
2023     [EventSendingController clearSavedEvents];
2024     [[mainFrame webView] setSelectedDOMRange:nil affinity:NSSelectionAffinityDownstream];
2025
2026     workQueue.clear();
2027
2028     if (gTestRunner->closeRemainingWindowsWhenComplete()) {
2029         NSArray* array = [DumpRenderTreeWindow openWindows];
2030
2031         unsigned count = [array count];
2032         for (unsigned i = 0; i < count; i++) {
2033             NSWindow *window = [array objectAtIndex:i];
2034
2035             // Don't try to close the main window
2036             if (window == [[mainFrame webView] window])
2037                 continue;
2038             
2039 #if !PLATFORM(IOS)
2040             WebView *webView = [[[window contentView] subviews] objectAtIndex:0];
2041 #else
2042             ASSERT([[window contentView] isKindOfClass:[WebView class]]);
2043             WebView *webView = (WebView *)[window contentView];
2044 #endif
2045
2046             [webView close];
2047             [window close];
2048         }
2049     }
2050
2051     // If developer extras enabled Web Inspector may have been open by the test.
2052     if (shouldEnableDeveloperExtras(pathOrURL.c_str())) {
2053         gTestRunner->closeWebInspector();
2054         gTestRunner->setDeveloperExtrasEnabled(false);
2055     }
2056
2057     resetWebViewToConsistentStateBeforeTesting();
2058
2059     // Loading an empty request synchronously replaces the document with a blank one, which is necessary
2060     // to stop timers, WebSockets and other activity that could otherwise spill output into next test's results.
2061     [mainFrame loadRequest:[NSURLRequest requestWithURL:[NSURL URLWithString:@""]]];
2062
2063     [pool release];
2064
2065     // We should only have our main window left open when we're done
2066     ASSERT(CFArrayGetCount(openWindowsRef) == 1);
2067     ASSERT(CFArrayGetValueAtIndex(openWindowsRef, 0) == [[mainFrame webView] window]);
2068
2069     gTestRunner = nullptr;
2070
2071     if (ignoreWebCoreNodeLeaks)
2072         [WebCoreStatistics stopIgnoringWebCoreNodeLeaks];
2073
2074     if (gcBetweenTests)
2075         [WebCoreStatistics garbageCollectJavaScriptObjects];
2076
2077     fputs("#EOF\n", stderr);
2078     fflush(stderr);
2079 }
2080
2081 void displayWebView()
2082 {
2083 #if !PLATFORM(IOS)
2084     WebView *webView = [mainFrame webView];
2085     [webView display];
2086
2087     // FIXME: Tracking repaints is not specific to Mac. We should enable such support on iOS.
2088     [webView setTracksRepaints:YES];
2089     [webView resetTrackedRepaints];
2090 #else
2091     [gDrtWindow layoutTilesNow];
2092     [gDrtWindow setNeedsDisplayInRect:[gDrtWindow frame]];
2093     [CATransaction flush];
2094 #endif
2095 }
2096
2097 #if !PLATFORM(IOS)
2098 @implementation DumpRenderTreeEvent
2099
2100 + (NSPoint)mouseLocation
2101 {
2102     return [[[mainFrame webView] window] convertBaseToScreen:lastMousePosition];
2103 }
2104
2105 @end
2106
2107 @implementation DumpRenderTreeApplication
2108
2109 - (BOOL)isRunning
2110 {
2111     // <rdar://problem/7686123> Java plug-in freezes unless NSApplication is running
2112     return YES;
2113 }
2114
2115 @end
2116 #endif