|  | /* | 
|  | * Copyright (C) 2006, 2007, 2008, 2009 Apple Inc.  All rights reserved. | 
|  | * | 
|  | * Redistribution and use in source and binary forms, with or without | 
|  | * modification, are permitted provided that the following conditions | 
|  | * are met: | 
|  | * | 
|  | * 1.  Redistributions of source code must retain the above copyright | 
|  | *     notice, this list of conditions and the following disclaimer. | 
|  | * 2.  Redistributions in binary form must reproduce the above copyright | 
|  | *     notice, this list of conditions and the following disclaimer in the | 
|  | *     documentation and/or other materials provided with the distribution. | 
|  | * 3.  Neither the name of Apple Inc. ("Apple") nor the names of | 
|  | *     its contributors may be used to endorse or promote products derived | 
|  | *     from this software without specific prior written permission. | 
|  | * | 
|  | * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY | 
|  | * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED | 
|  | * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE | 
|  | * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY | 
|  | * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES | 
|  | * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; | 
|  | * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND | 
|  | * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | 
|  | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF | 
|  | * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | 
|  | */ | 
|  |  | 
|  | #import <Cocoa/Cocoa.h> | 
|  | #import <CoreFoundation/CoreFoundation.h> | 
|  |  | 
|  | // We need to weak-import posix_spawn and friends as they're not available on Tiger. | 
|  | // The BSD-level system headers do not have availability macros, so we redeclare the | 
|  | // functions ourselves with the "weak" attribute. | 
|  |  | 
|  | #define WEAK_IMPORT __attribute__((weak)) | 
|  |  | 
|  | #define POSIX_SPAWN_SETEXEC 0x0040 | 
|  | typedef void *posix_spawnattr_t; | 
|  | typedef void *posix_spawn_file_actions_t; | 
|  | int posix_spawnattr_init(posix_spawnattr_t *) WEAK_IMPORT; | 
|  | int posix_spawn(pid_t * __restrict, const char * __restrict, const posix_spawn_file_actions_t *, const posix_spawnattr_t * __restrict, char *const __argv[ __restrict], char *const __envp[ __restrict]) WEAK_IMPORT; | 
|  | int posix_spawnattr_setbinpref_np(posix_spawnattr_t * __restrict, size_t, cpu_type_t *__restrict, size_t *__restrict) WEAK_IMPORT; | 
|  | int posix_spawnattr_setflags(posix_spawnattr_t *, short) WEAK_IMPORT; | 
|  |  | 
|  |  | 
|  | static void displayErrorAndQuit(NSString *title, NSString *message) | 
|  | { | 
|  | NSApplicationLoad(); | 
|  | NSRunCriticalAlertPanel(title, @"%@", @"Quit", nil, nil, message); | 
|  | exit(0); | 
|  | } | 
|  |  | 
|  | static int getLastVersionShown() | 
|  | { | 
|  | [[NSUserDefaults standardUserDefaults] registerDefaults:[NSDictionary dictionaryWithObject:@"-1" forKey:@"StartPageShownInVersion"]]; | 
|  | return [[NSUserDefaults standardUserDefaults] integerForKey:@"StartPageShownInVersion"]; | 
|  | } | 
|  |  | 
|  | static void saveLastVersionShown(int lastVersion) | 
|  | { | 
|  | [[NSUserDefaults standardUserDefaults] setInteger:lastVersion forKey:@"StartPageShownInVersion"]; | 
|  | [[NSUserDefaults standardUserDefaults] synchronize]; | 
|  | } | 
|  |  | 
|  | static NSString *getPathForStartPage() | 
|  | { | 
|  | return [[[NSBundle mainBundle] resourcePath] stringByAppendingPathComponent:@"start.html"]; | 
|  | } | 
|  |  | 
|  | static int getCurrentVersion() | 
|  | { | 
|  | return [[[[NSBundle mainBundle] infoDictionary] valueForKey:(NSString *)kCFBundleVersionKey] intValue]; | 
|  | } | 
|  |  | 
|  | static int getShowStartPageVersion() | 
|  | { | 
|  | return getCurrentVersion() + 1; | 
|  | } | 
|  |  | 
|  | static BOOL startPageDisabled() | 
|  | { | 
|  | return [[NSUserDefaults standardUserDefaults] boolForKey:@"StartPageDisabled"]; | 
|  | } | 
|  |  | 
|  | static void addStartPageToArgumentsIfNeeded(NSMutableArray *arguments) | 
|  | { | 
|  | if (startPageDisabled()) | 
|  | return; | 
|  |  | 
|  | if (getLastVersionShown() < getShowStartPageVersion()) { | 
|  | saveLastVersionShown(getCurrentVersion()); | 
|  | NSString *startPagePath = getPathForStartPage(); | 
|  | if (startPagePath) | 
|  | [arguments addObject:startPagePath]; | 
|  | } | 
|  | } | 
|  |  | 
|  | static cpu_type_t preferredArchitecture() | 
|  | { | 
|  | #if defined(__ppc__) | 
|  | return CPU_TYPE_POWERPC; | 
|  | #elif defined(__LP64__) | 
|  | return CPU_TYPE_X86_64; | 
|  | #else | 
|  | return CPU_TYPE_X86; | 
|  | #endif | 
|  | } | 
|  |  | 
|  | static void myExecve(NSString *executable, NSArray *args, NSDictionary *environment) | 
|  | { | 
|  | char **argv = (char **)calloc(sizeof(char *), [args count] + 1); | 
|  | char **env = (char **)calloc(sizeof(char *), [environment count] + 1); | 
|  |  | 
|  | NSEnumerator *e = [args objectEnumerator]; | 
|  | NSString *s; | 
|  | int i = 0; | 
|  | while ((s = [e nextObject])) | 
|  | argv[i++] = (char *) [s UTF8String]; | 
|  |  | 
|  | e = [environment keyEnumerator]; | 
|  | i = 0; | 
|  | while ((s = [e nextObject])) | 
|  | env[i++] = (char *) [[NSString stringWithFormat:@"%@=%@", s, [environment objectForKey:s]] UTF8String]; | 
|  |  | 
|  | if (posix_spawnattr_init && posix_spawn && posix_spawnattr_setbinpref_np && posix_spawnattr_setflags) { | 
|  | posix_spawnattr_t attr; | 
|  | posix_spawnattr_init(&attr); | 
|  | cpu_type_t architecturePreference[] = { preferredArchitecture(), CPU_TYPE_X86 }; | 
|  | posix_spawnattr_setbinpref_np(&attr, 2, architecturePreference, 0); | 
|  | short flags = POSIX_SPAWN_SETEXEC; | 
|  | posix_spawnattr_setflags(&attr, flags); | 
|  | posix_spawn(NULL, [executable fileSystemRepresentation], NULL, &attr, argv, env); | 
|  | } else | 
|  | execve([executable fileSystemRepresentation], argv, env); | 
|  | } | 
|  |  | 
|  | static NSBundle *locateSafariBundle() | 
|  | { | 
|  | NSArray *applicationDirectories = NSSearchPathForDirectoriesInDomains(NSApplicationDirectory, NSAllDomainsMask, YES); | 
|  | NSEnumerator *e = [applicationDirectories objectEnumerator]; | 
|  | NSString *applicationDirectory; | 
|  | while ((applicationDirectory = [e nextObject])) { | 
|  | NSString *possibleSafariPath = [applicationDirectory stringByAppendingPathComponent:@"Safari.app"]; | 
|  | NSBundle *possibleSafariBundle = [NSBundle bundleWithPath:possibleSafariPath]; | 
|  | if ([[possibleSafariBundle bundleIdentifier] isEqualToString:@"com.apple.Safari"]) | 
|  | return possibleSafariBundle; | 
|  | } | 
|  |  | 
|  | CFURLRef safariURL = nil; | 
|  | OSStatus err = LSFindApplicationForInfo(kLSUnknownCreator, CFSTR("com.apple.Safari"), nil, nil, &safariURL); | 
|  | if (err != noErr) | 
|  | displayErrorAndQuit(@"Unable to locate Safari", @"Nightly builds of WebKit require Safari to run.  Please check that it is available and then try again."); | 
|  |  | 
|  | NSBundle *safariBundle = [NSBundle bundleWithPath:[(NSURL *)safariURL path]]; | 
|  | CFRelease(safariURL); | 
|  | return safariBundle; | 
|  | } | 
|  |  | 
|  | static NSString *determineExecutablePath(NSBundle *bundle) | 
|  | { | 
|  | NSString *safariExecutablePath = [bundle executablePath]; | 
|  |  | 
|  | NSString *safariForWebKitDevelopmentExecutablePath = [bundle pathForAuxiliaryExecutable:@"SafariForWebKitDevelopment"]; | 
|  | if (![[NSFileManager defaultManager] fileExistsAtPath:safariForWebKitDevelopmentExecutablePath]) | 
|  | return safariExecutablePath; | 
|  |  | 
|  | SecStaticCodeRef staticCode; | 
|  | if (SecStaticCodeCreateWithPath((CFURLRef)[bundle executableURL], kSecCSDefaultFlags, &staticCode) != noErr) | 
|  | return [bundle executablePath]; | 
|  |  | 
|  | NSDictionary *codeInformation; | 
|  | if (SecCodeCopySigningInformation(staticCode, kSecCSRequirementInformation, (CFDictionaryRef*)&codeInformation) != noErr) { | 
|  | CFRelease(staticCode); | 
|  | return safariExecutablePath; | 
|  | } | 
|  | CFRelease(staticCode); | 
|  | [codeInformation autorelease]; | 
|  |  | 
|  | if ([codeInformation objectForKey:(id)kSecCodeInfoEntitlements]) | 
|  | return safariForWebKitDevelopmentExecutablePath; | 
|  |  | 
|  | return safariExecutablePath; | 
|  | } | 
|  |  | 
|  | static NSString *currentMacOSXVersion() | 
|  | { | 
|  | // Can't use -[NSProcessInfo operatingSystemVersionString] because it has too much stuff we don't want. | 
|  | NSString *systemLibraryPath = [NSSearchPathForDirectoriesInDomains(NSLibraryDirectory, NSSystemDomainMask, YES) objectAtIndex:0]; | 
|  | NSString *systemVersionPlistPath = [systemLibraryPath stringByAppendingPathComponent:@"CoreServices/SystemVersion.plist"]; | 
|  | NSDictionary *systemVersionInfo = [NSDictionary dictionaryWithContentsOfFile:systemVersionPlistPath]; | 
|  | return [systemVersionInfo objectForKey:@"ProductVersion"]; | 
|  | } | 
|  |  | 
|  | static NSString *currentMacOSXMajorVersion() | 
|  | { | 
|  | NSArray *allComponents = [currentMacOSXVersion() componentsSeparatedByString:@"."]; | 
|  | NSArray *majorAndMinorComponents = [allComponents objectsAtIndexes:[NSIndexSet indexSetWithIndexesInRange:NSMakeRange(0, 2)]]; | 
|  | return [majorAndMinorComponents componentsJoinedByString:@"."]; | 
|  | } | 
|  |  | 
|  | static NSString *fallbackMacOSXVersion(NSString *systemVersion) | 
|  | { | 
|  | NSDictionary *fallbackVersionMap = [[NSUserDefaults standardUserDefaults] dictionaryForKey:@"FallbackSystemVersions"]; | 
|  | if (!fallbackVersionMap) | 
|  | return nil; | 
|  | NSString *fallbackSystemVersion = [fallbackVersionMap objectForKey:systemVersion]; | 
|  | if (!fallbackSystemVersion || ![fallbackSystemVersion isKindOfClass:[NSString class]]) | 
|  | return nil; | 
|  | return fallbackSystemVersion; | 
|  | } | 
|  |  | 
|  | static BOOL checkFrameworkPath(NSString *frameworkPath) | 
|  | { | 
|  | BOOL isDirectory = NO; | 
|  | return [[NSFileManager defaultManager] fileExistsAtPath:frameworkPath isDirectory:&isDirectory] && isDirectory; | 
|  | } | 
|  |  | 
|  | static BOOL checkSafariVersion(NSBundle *safariBundle) | 
|  | { | 
|  | NSString *safariBundleVersion = [[safariBundle infoDictionary] objectForKey:(NSString *)kCFBundleVersionKey]; | 
|  | NSString *majorComponent = [[safariBundleVersion componentsSeparatedByString:@"."] objectAtIndex:0]; | 
|  | NSString *majorVersion = [majorComponent substringFromIndex:[majorComponent length] - 3]; | 
|  | return [majorVersion intValue] >= 530; | 
|  | } | 
|  |  | 
|  | int main(int argc, char *argv[]) | 
|  | { | 
|  | NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; | 
|  |  | 
|  | NSString *systemVersion = currentMacOSXMajorVersion(); | 
|  | NSString *frameworkPath = [[[NSBundle mainBundle] privateFrameworksPath] stringByAppendingPathComponent:systemVersion]; | 
|  |  | 
|  | BOOL frameworkPathIsUsable = checkFrameworkPath(frameworkPath); | 
|  |  | 
|  | if (!frameworkPathIsUsable) { | 
|  | NSString *fallbackSystemVersion = fallbackMacOSXVersion(systemVersion); | 
|  | if (fallbackSystemVersion) { | 
|  | frameworkPath = [[[NSBundle mainBundle] privateFrameworksPath] stringByAppendingPathComponent:fallbackSystemVersion]; | 
|  | frameworkPathIsUsable = checkFrameworkPath(frameworkPath); | 
|  | } | 
|  | } | 
|  |  | 
|  | if (!frameworkPathIsUsable) | 
|  | displayErrorAndQuit([NSString stringWithFormat:@"OS X %@ is not supported", systemVersion], | 
|  | [NSString stringWithFormat:@"Nightly builds of WebKit are not supported on OS X %@ at this time.", systemVersion]); | 
|  |  | 
|  | NSString *pathToEnablerLib = [[NSBundle mainBundle] pathForResource:@"WebKitNightlyEnabler" ofType:@"dylib"]; | 
|  | NSString *dyldInsertLibraries = pathToEnablerLib; | 
|  | NSString *pathToASanCrashReporterLib = [[NSBundle mainBundle] pathForResource:@"libasancrashreporter" ofType:@"dylib"]; | 
|  | if (pathToASanCrashReporterLib) | 
|  | dyldInsertLibraries = [@[ pathToASanCrashReporterLib, pathToEnablerLib ] componentsJoinedByString:@":"]; | 
|  |  | 
|  | NSBundle *safariBundle = locateSafariBundle(); | 
|  | NSString *executablePath = determineExecutablePath(safariBundle); | 
|  |  | 
|  | if (!checkSafariVersion(safariBundle)) { | 
|  | NSString *safariVersion = [[safariBundle localizedInfoDictionary] objectForKey:@"CFBundleShortVersionString"]; | 
|  | displayErrorAndQuit([NSString stringWithFormat:@"Safari %@ is not supported", safariVersion], | 
|  | [NSString stringWithFormat:@"Nightly builds of WebKit are not supported with Safari %@ at this time. Please update to a newer version of Safari.", safariVersion]); | 
|  | } | 
|  |  | 
|  | if ([frameworkPath rangeOfString:@":"].location != NSNotFound || | 
|  | [pathToEnablerLib rangeOfString:@":"].location != NSNotFound) | 
|  | displayErrorAndQuit(@"Unable to launch Safari", | 
|  | @"WebKit is located at a path containing an unsupported character.  Please move WebKit to a different location and try again."); | 
|  |  | 
|  | NSMutableArray *arguments = [NSMutableArray arrayWithObject:executablePath]; | 
|  | NSMutableDictionary *environment = [[[NSDictionary dictionaryWithObjectsAndKeys:frameworkPath, @"DYLD_FRAMEWORK_PATH", @"YES", @"WEBKIT_UNSET_DYLD_FRAMEWORK_PATH", | 
|  | dyldInsertLibraries, @"DYLD_INSERT_LIBRARIES", [[NSBundle mainBundle] executablePath], @"WebKitAppPath", nil] mutableCopy] autorelease]; | 
|  | [environment addEntriesFromDictionary:[[NSProcessInfo processInfo] environment]]; | 
|  | addStartPageToArgumentsIfNeeded(arguments); | 
|  |  | 
|  | while (*++argv) | 
|  | [arguments addObject:[NSString stringWithUTF8String:*argv]]; | 
|  |  | 
|  | myExecve(executablePath, arguments, environment); | 
|  |  | 
|  | char *error = strerror(errno); | 
|  | NSString *errorMessage = [NSString stringWithFormat:@"Launching Safari at %@ failed with the error '%s' (%d)", [safariBundle bundlePath], error, errno]; | 
|  | displayErrorAndQuit(@"Unable to launch Safari", errorMessage); | 
|  |  | 
|  | [pool release]; | 
|  | return 0; | 
|  | } |