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