webengine/osswebengine/WebKitTools/WebKitLauncher/WebKitNightlyEnabler.m
changeset 0 dd21522fd290
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/webengine/osswebengine/WebKitTools/WebKitLauncher/WebKitNightlyEnabler.m	Mon Mar 30 12:54:55 2009 +0300
@@ -0,0 +1,165 @@
+/*
+ * Copyright (C) 2006, 2007 Apple Inc.  All rights reserved.
+ * Copyright (C) 2006 Graham Dennis.  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 Computer, 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>
+
+static void enableWebKitNightlyBehaviour() __attribute__ ((constructor));
+
+static NSString *WKNERunState = @"WKNERunState";
+static NSString *WKNEShouldMonitorShutdowns = @"WKNEShouldMonitorShutdowns";
+
+typedef enum {
+    RunStateShutDown,
+    RunStateInitializing,
+    RunStateRunning
+} WKNERunStates;
+
+static bool extensionBundlesWereLoaded = NO;
+static NSSet *extensionPaths = nil;
+
+static void myBundleDidLoad(CFNotificationCenterRef center, void *observer, CFStringRef name, const void *object, CFDictionaryRef userInfo)
+{
+    // Break out early if we have already detected an extension
+    if (extensionBundlesWereLoaded)
+        return;
+
+    NSBundle *bundle = (NSBundle *)object;
+    NSString *bundlePath = [[bundle bundlePath] stringByAbbreviatingWithTildeInPath];
+    NSString *bundleFileName = [bundlePath lastPathComponent];
+
+    // Explicitly ignore SIMBL.bundle, as its only purpose is to load extensions
+    // on a per-application basis.  It's presence indicates a user has application
+    // extensions, but not that any will be loaded into Safari
+    if ([bundleFileName isEqualToString:@"SIMBL.bundle"])
+        return;
+
+    // If the bundle lives inside a known extension path, flag it as an extension
+    NSEnumerator *e = [extensionPaths objectEnumerator];
+    NSString *path = nil;
+    while (path = [e nextObject]) {
+        if ([bundlePath length] < [path length])
+            continue;
+
+        if ([[bundlePath substringToIndex:[path length]] isEqualToString:path]) {
+            extensionBundlesWereLoaded = YES;
+            break;
+        }
+    }
+}
+
+static void myApplicationWillFinishLaunching(CFNotificationCenterRef center, void *observer, CFStringRef name, const void *object, CFDictionaryRef userInfo)
+{
+    CFNotificationCenterRemoveObserver(CFNotificationCenterGetLocalCenter(), &myApplicationWillFinishLaunching, NULL, NULL);
+    CFNotificationCenterRemoveObserver(CFNotificationCenterGetLocalCenter(), &myBundleDidLoad, NULL, NULL);
+    [extensionPaths release];
+    extensionPaths = nil;
+
+    NSUserDefaults *userDefaults = [NSUserDefaults standardUserDefaults];
+    [userDefaults setInteger:RunStateRunning forKey:WKNERunState];
+    [userDefaults synchronize];
+
+    if (extensionBundlesWereLoaded)
+        NSRunInformationalAlertPanel(@"Safari extensions detected",
+                                     @"Safari extensions were detected on your system.  Extensions are incompatible with nightly builds of WebKit, and may cause crashes or incorrect behavior.  Please disable them if you experience such behavior.", @"Continue",
+                                     nil, nil);
+}
+
+static void myApplicationWillTerminate(CFNotificationCenterRef center, void *observer, CFStringRef name, const void *object, CFDictionaryRef userInfo)
+{
+    NSUserDefaults *userDefaults = [NSUserDefaults standardUserDefaults];
+    [userDefaults setInteger:RunStateShutDown forKey:WKNERunState];
+    [userDefaults synchronize];
+}
+
+extern char **_CFGetProcessPath() __attribute__((weak));
+
+static void poseAsWebKitApp()
+{
+    char *webKitAppPath = getenv("WebKitAppPath");
+    if (!webKitAppPath || !_CFGetProcessPath)
+        return;
+
+    // Set up the main bundle early so it points at Safari.app
+    CFBundleGetMainBundle();
+
+    // Fiddle with CoreFoundation to have it pick up the executable path as being within WebKit.app
+    char **processPath = _CFGetProcessPath();
+    *processPath = NULL;
+    setenv("CFProcessPath", webKitAppPath, 1);
+    _CFGetProcessPath();
+
+    // Clean up
+    unsetenv("CFProcessPath");
+    unsetenv("WebKitAppPath");
+}
+
+static void enableWebKitNightlyBehaviour()
+{
+    unsetenv("DYLD_INSERT_LIBRARIES");
+    poseAsWebKitApp();
+
+    extensionPaths = [[NSSet alloc] initWithObjects:@"~/Library/InputManagers/", @"/Library/InputManagers/",
+                                                    @"~/Library/Application Support/SIMBL/Plugins/", @"/Library/Application Support/SIMBL/Plugins/",
+                                                    @"~/Library/Application Enhancers/", @"/Library/Application Enhancers/",
+                                                    nil];
+
+    NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
+    NSUserDefaults *userDefaults = [NSUserDefaults standardUserDefaults];
+    NSDictionary *defaultPrefs = [NSDictionary dictionaryWithObjectsAndKeys:[NSNumber numberWithBool:RunStateShutDown], WKNERunState,
+                                                                            [NSNumber numberWithBool:YES], WKNEShouldMonitorShutdowns, nil];
+    [userDefaults registerDefaults:defaultPrefs];
+    if ([userDefaults boolForKey:WKNEShouldMonitorShutdowns]) {
+        WKNERunStates savedState = (WKNERunStates)[userDefaults integerForKey:WKNERunState];
+        if (savedState == RunStateInitializing) {
+            // Use CoreFoundation here as AppKit hasn't been initialized at this stage of Safari's lifetime
+            CFOptionFlags responseFlags;
+            CFUserNotificationDisplayAlert(0, kCFUserNotificationCautionAlertLevel,
+                                           NULL, NULL, NULL,
+                                           CFSTR("WebKit failed to open correctly"),
+                                           CFSTR("WebKit failed to open correctly on your previous attempt. Please disable any Safari extensions that you may have installed.  If the problem continues to occur, please file a bug report at http://webkit.org/quality/reporting.html"), 
+                                           CFSTR("Continue"), NULL, NULL, &responseFlags);
+        }
+        else if (savedState == RunStateRunning) {
+            NSLog(@"WebKit failed to shut down cleanly.  Checking for Safari extensions.");
+            CFNotificationCenterAddObserver(CFNotificationCenterGetLocalCenter(), &myBundleDidLoad,
+                                            myBundleDidLoad, (CFStringRef) NSBundleDidLoadNotification,
+                                            NULL, CFNotificationSuspensionBehaviorDeliverImmediately);
+        }
+    }
+    [userDefaults setInteger:RunStateInitializing forKey:WKNERunState];
+    [userDefaults synchronize];
+
+    CFNotificationCenterAddObserver(CFNotificationCenterGetLocalCenter(), &myApplicationWillFinishLaunching,
+                                    myApplicationWillFinishLaunching, (CFStringRef) NSApplicationWillFinishLaunchingNotification,
+                                    NULL, CFNotificationSuspensionBehaviorDeliverImmediately);
+    CFNotificationCenterAddObserver(CFNotificationCenterGetLocalCenter(), &myApplicationWillTerminate,
+                                    myApplicationWillTerminate, (CFStringRef) NSApplicationWillTerminateNotification,
+                                    NULL, CFNotificationSuspensionBehaviorDeliverImmediately);
+    [pool release];
+}