WebKitTools:
[WebKit-https.git] / WebKitTools / WebKitLauncher / WebKitNightlyEnabler.m
1 /*
2  * Copyright (C) 2006 Apple Computer, Inc.  All rights reserved.
3  * Copyright (C) 2006 Graham Dennis.  All rights reserved.
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 Computer, 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 <Cocoa/Cocoa.h>
31 #include <mach-o/dyld.h>
32 #include <dlfcn.h>
33 #include <mach-o/loader.h>
34 #include <mach-o/nlist.h>
35 #include <string.h>
36
37 static void cleanUpAfterOurselves(void) __attribute__ ((constructor));
38 static void *symbol_lookup(char *symbol);
39
40 static NSString *WKNEDidShutDownCleanly = @"WKNEDidShutDownCleanly";
41 static NSString *WKNEShouldMonitorShutdowns = @"WKNEShouldMonitorShutdowns";
42
43 static bool extensionBundlesWereLoaded = NO;
44 static NSSet *extensionPaths = nil;
45
46 static void myBundleDidLoad(CFNotificationCenterRef center, void *observer, CFStringRef name, const void *object, CFDictionaryRef userInfo)
47 {
48     // Break out early if we have already detected an extension
49     if (extensionBundlesWereLoaded)
50         return;
51
52     NSBundle *bundle = (NSBundle *)object;
53     NSString *bundlePath = [[bundle bundlePath] stringByAbbreviatingWithTildeInPath];
54     NSString *bundleFileName = [bundlePath lastPathComponent];
55
56     // Explicitly ignore SIMBL.bundle, as its only purpose is to load extensions
57     // on a per-application basis.  It's presence indicates a user has application
58     // extensions, but not that any will be loaded into Safari
59     if ([bundleFileName isEqualToString:@"SIMBL.bundle"])
60         return;
61
62     // If the bundle lives inside a known extension path, flag it as an extension
63     NSEnumerator *e = [extensionPaths objectEnumerator];
64     NSString *path = nil;
65     while (path = [e nextObject]) {
66         if ([bundlePath length] < [path length])
67             continue;
68
69         if ([[bundlePath substringToIndex:[path length]] isEqualToString:path]) {
70             extensionBundlesWereLoaded = YES;
71             break;
72         }
73     }
74 }
75
76 static void myApplicationWillFinishLaunching(CFNotificationCenterRef center, void *observer, CFStringRef name, const void *object, CFDictionaryRef userInfo)
77 {
78     CFNotificationCenterRemoveObserver(CFNotificationCenterGetLocalCenter(), &myApplicationWillFinishLaunching, NULL, NULL);
79     CFNotificationCenterRemoveObserver(CFNotificationCenterGetLocalCenter(), &myBundleDidLoad, NULL, NULL);
80     [extensionPaths release];
81     extensionPaths = nil;
82
83     if (extensionBundlesWereLoaded)
84         NSRunInformationalAlertPanel(@"Safari extensions detected",
85                                      @"Safari extensions were detected on your system. They 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);
86 }
87
88 static void myApplicationWillTerminate(CFNotificationCenterRef center, void *observer, CFStringRef name, const void *object, CFDictionaryRef userInfo)
89 {
90     NSUserDefaults *userDefaults = [NSUserDefaults standardUserDefaults];
91     [userDefaults setBool:YES forKey:WKNEDidShutDownCleanly];
92     [userDefaults synchronize];
93 }
94
95 void cleanUpAfterOurselves(void)
96 {
97     char **args = *(char***)_NSGetArgv();
98     char **procPath = symbol_lookup("___CFProcessPath");
99     char *procPathBackup = *procPath;
100     *procPath = args[0];
101     CFBundleGetMainBundle();
102     *procPath = procPathBackup;
103     unsetenv("DYLD_INSERT_LIBRARIES");
104     unsetenv("CFProcessPath");
105
106     extensionPaths = [[NSSet alloc] initWithObjects:@"~/Library/InputManagers/", @"/Library/InputManagers/",
107                                                     @"~/Library/Application Support/SIMBL/Plugins/", @"/Library/Application Support/SIMBL/Plugins/",
108                                                     @"~/Library/Application Enhancers/", @"/Library/Application Enhancers/",
109                                                     nil];
110
111     NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
112     NSUserDefaults *userDefaults = [NSUserDefaults standardUserDefaults];
113     NSDictionary *defaultPrefs = [NSDictionary dictionaryWithObjectsAndKeys:[NSNumber numberWithBool:YES], WKNEDidShutDownCleanly,
114                                                                             [NSNumber numberWithBool:YES], WKNEShouldMonitorShutdowns, nil];
115     [userDefaults registerDefaults:defaultPrefs];
116     if ([userDefaults boolForKey:WKNEShouldMonitorShutdowns] && ![userDefaults boolForKey:WKNEDidShutDownCleanly])
117     {
118         NSLog(@"WebKit failed to shut down cleanly.  Checking for Safari extensions.");
119         CFNotificationCenterAddObserver(CFNotificationCenterGetLocalCenter(), &myBundleDidLoad,
120                                         myBundleDidLoad, (CFStringRef) NSBundleDidLoadNotification,
121                                         NULL, CFNotificationSuspensionBehaviorDeliverImmediately);
122         CFNotificationCenterAddObserver(CFNotificationCenterGetLocalCenter(), &myApplicationWillFinishLaunching,
123                                         myApplicationWillFinishLaunching, (CFStringRef) NSApplicationWillFinishLaunchingNotification,
124                                         NULL, CFNotificationSuspensionBehaviorDeliverImmediately);
125     }
126     [userDefaults setBool:NO forKey:WKNEDidShutDownCleanly];
127     [userDefaults synchronize];
128
129     CFNotificationCenterAddObserver(CFNotificationCenterGetLocalCenter(), &myApplicationWillTerminate,
130                                     myApplicationWillTerminate, (CFStringRef) NSApplicationWillTerminateNotification,
131                                     NULL, CFNotificationSuspensionBehaviorDeliverImmediately);
132     [pool release];
133 }
134
135 #if __LP64__
136 #define LC_SEGMENT_COMMAND LC_SEGMENT_64
137 #define macho_header mach_header_64
138 #define macho_segment_command segment_command_64
139 #define macho_section section_64
140 #define getsectdatafromheader getsectdatafromheader_64
141 #define macho_nlist nlist_64
142 #else
143 #define LC_SEGMENT_COMMAND LC_SEGMENT
144 #define macho_header mach_header
145 #define macho_segment_command segment_command
146 #define macho_section section
147 #define macho_nlist nlist
148 #endif
149
150 void *GDSymbolLookup(const struct macho_header *header, const char *symbol);
151
152 void *symbol_lookup(char *symbol)
153 {
154     int i;
155     for(i=0;i<_dyld_image_count();i++)
156     {    
157         void *symbolResult = GDSymbolLookup((const struct macho_header*)_dyld_get_image_header(i), symbol);
158         if (symbolResult)
159             return symbolResult;
160     }
161     return NULL;
162 }
163
164 void *GDSymbolLookup(const struct macho_header *header, const char *symbol)
165 {
166     if (!header || !symbol)
167         return NULL;
168     if ((header->magic != MH_MAGIC) && (header->magic != MH_MAGIC_64))
169         return NULL;
170     
171     uint32_t currCommand;
172     const struct load_command *loadCommand = (const struct load_command *)( ((void *)header) + sizeof(struct macho_header));
173     const struct macho_segment_command *segCommand;
174     
175     const struct symtab_command *symtabCommand = NULL;
176     const struct dysymtab_command *dysymtabCommand = NULL;
177     const struct macho_segment_command *textSegment = NULL;
178     const struct macho_segment_command *linkEditSegment = NULL;
179     
180     for (currCommand = 0; currCommand < header->ncmds; currCommand++)
181     {
182         switch (loadCommand->cmd)
183         {
184             case LC_SEGMENT_COMMAND:
185                 segCommand = (const struct macho_segment_command *)loadCommand;
186                 if (strcmp(segCommand->segname, "__TEXT")==0)
187                     textSegment = segCommand;
188                 else if (strcmp(segCommand->segname, "__LINKEDIT")==0)
189                     linkEditSegment = segCommand;
190                     break;
191                 
192             case LC_SYMTAB:
193                 symtabCommand = (const struct symtab_command *)loadCommand;
194                 break;
195                 
196             case LC_DYSYMTAB:
197                 dysymtabCommand = (const struct dysymtab_command *)loadCommand;
198                 break;
199         }
200         
201         loadCommand = (const struct load_command *)(((void*)loadCommand) + loadCommand->cmdsize);
202     }
203     if (textSegment==NULL || linkEditSegment==NULL || symtabCommand==NULL || dysymtabCommand==NULL) {
204         return NULL;
205     }
206     
207     typedef enum { Start = 0, LocalSymbols, ExternalSymbols, Done } SymbolSearchState;
208     uint32_t currentSymbolIndex;
209     uint32_t maximumSymbolIndex;
210     SymbolSearchState state;
211     
212     for (state = Start + 1; state < Done; state++)
213     {
214         switch(state) {
215             case LocalSymbols:
216                 currentSymbolIndex = dysymtabCommand->ilocalsym;
217                 maximumSymbolIndex = dysymtabCommand->ilocalsym + dysymtabCommand->nlocalsym;
218                 break;
219                 
220             case ExternalSymbols:
221                 currentSymbolIndex = dysymtabCommand->iextdefsym;
222                 maximumSymbolIndex = dysymtabCommand->nextdefsym;
223                 break;
224                 
225             default:
226                 return NULL;
227         }
228         for (; currentSymbolIndex < maximumSymbolIndex; currentSymbolIndex++)
229         {
230             const struct macho_nlist *symbolTableEntry;
231             symbolTableEntry = (const struct macho_nlist *)(currentSymbolIndex*sizeof(struct macho_nlist)
232                                                             + (ptrdiff_t)header + symtabCommand->symoff
233                                                             + linkEditSegment->vmaddr - linkEditSegment->fileoff
234                                                             - textSegment->vmaddr);
235             int32_t stringTableIndex = symbolTableEntry->n_un.n_strx;
236             if (stringTableIndex<0)
237                 continue;
238             const char *stringTableEntry = (const char*)(stringTableIndex + (ptrdiff_t)header + symtabCommand->stroff
239                                                          + linkEditSegment->vmaddr - linkEditSegment->fileoff
240                                                          - textSegment->vmaddr);
241             if (strcmp(symbol, stringTableEntry)==0) {
242                 return ((void*)header) - textSegment->vmaddr + symbolTableEntry->n_value;
243             }
244         }
245         state++;
246     }
247     return NULL;
248 }
249
250