|
1 /* This Source Code Form is subject to the terms of the Mozilla Public |
|
2 * License, v. 2.0. If a copy of the MPL was not distributed with this file, |
|
3 * You can obtain one at http://mozilla.org/MPL/2.0/. */ |
|
4 |
|
5 //PLAN: |
|
6 |
|
7 // open my bundle, check for an override binary signature |
|
8 // find the newest firefox. open its bundle, get the version number. |
|
9 // if the firefox version is different than ours: |
|
10 // delete our own binary, |
|
11 // copy the newer webapprt binary from Firefox |
|
12 // exec it, and quit |
|
13 |
|
14 #include <stdio.h> |
|
15 #include <string.h> |
|
16 #include <unistd.h> |
|
17 #include <dirent.h> |
|
18 #include <sys/stat.h> |
|
19 |
|
20 #include <Cocoa/Cocoa.h> |
|
21 #include <Foundation/Foundation.h> |
|
22 |
|
23 #include "nsXPCOMGlue.h" |
|
24 #include "nsINIParser.h" |
|
25 #include "nsXPCOMPrivate.h" // for MAXPATHLEN and XPCOM_DLL |
|
26 #include "nsXULAppAPI.h" |
|
27 #include "nsComponentManagerUtils.h" |
|
28 #include "nsCOMPtr.h" |
|
29 #include "nsIFile.h" |
|
30 #include "nsStringGlue.h" |
|
31 #include "mozilla/AppData.h" |
|
32 |
|
33 using namespace mozilla; |
|
34 |
|
35 const char WEBAPPRT_EXECUTABLE[] = "webapprt-stub"; |
|
36 const char FXAPPINI_NAME[] = "application.ini"; |
|
37 const char WEBAPPINI_NAME[] = "webapp.ini"; |
|
38 const char WEBRTINI_NAME[] = "webapprt.ini"; |
|
39 |
|
40 //need the correct relative path here |
|
41 const char APP_CONTENTS_PATH[] = "/Contents/MacOS/"; |
|
42 |
|
43 //the path to the WebappRT subdir within the Firefox app contents dir |
|
44 const char WEBAPPRT_PATH[] = "webapprt/"; |
|
45 |
|
46 void ExecNewBinary(NSString* launchPath); |
|
47 |
|
48 NSString *PathToWebRT(NSString* alternateBinaryID); |
|
49 |
|
50 NSException* MakeException(NSString* name, NSString* message); |
|
51 |
|
52 void DisplayErrorAlert(NSString* title, NSString* message); |
|
53 |
|
54 XRE_GetFileFromPathType XRE_GetFileFromPath; |
|
55 XRE_CreateAppDataType XRE_CreateAppData; |
|
56 XRE_FreeAppDataType XRE_FreeAppData; |
|
57 XRE_mainType XRE_main; |
|
58 |
|
59 const nsDynamicFunctionLoad kXULFuncs[] = { |
|
60 { "XRE_GetFileFromPath", (NSFuncPtr*) &XRE_GetFileFromPath }, |
|
61 { "XRE_CreateAppData", (NSFuncPtr*) &XRE_CreateAppData }, |
|
62 { "XRE_FreeAppData", (NSFuncPtr*) &XRE_FreeAppData }, |
|
63 { "XRE_main", (NSFuncPtr*) &XRE_main }, |
|
64 { nullptr, nullptr } |
|
65 }; |
|
66 |
|
67 nsresult |
|
68 AttemptGRELoad(char *greDir) |
|
69 { |
|
70 nsresult rv; |
|
71 char xpcomDLLPath[MAXPATHLEN]; |
|
72 snprintf(xpcomDLLPath, MAXPATHLEN, "%s%s", greDir, XPCOM_DLL); |
|
73 |
|
74 rv = XPCOMGlueStartup(xpcomDLLPath); |
|
75 |
|
76 if (NS_FAILED(rv)) { |
|
77 return rv; |
|
78 } |
|
79 |
|
80 rv = XPCOMGlueLoadXULFunctions(kXULFuncs); |
|
81 if (NS_FAILED(rv)) { |
|
82 return rv; |
|
83 } |
|
84 |
|
85 return rv; |
|
86 } |
|
87 |
|
88 int |
|
89 main(int argc, char **argv) |
|
90 { |
|
91 NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; |
|
92 |
|
93 NSDictionary *args = [[NSUserDefaults standardUserDefaults] |
|
94 volatileDomainForName:NSArgumentDomain]; |
|
95 |
|
96 NSString *firefoxPath = nil; |
|
97 NSString *alternateBinaryID = nil; |
|
98 |
|
99 //this is our version, to be compared with the version of the binary we are asked to use |
|
100 NSString* myVersion = [NSString stringWithFormat:@"%s", NS_STRINGIFY(GRE_BUILDID)]; |
|
101 |
|
102 NSLog(@"MY WEBAPPRT BUILDID: %@", myVersion); |
|
103 |
|
104 |
|
105 //I need to look in our bundle first, before deciding what firefox binary to use |
|
106 NSBundle* myBundle = [NSBundle mainBundle]; |
|
107 NSString* myBundlePath = [myBundle bundlePath]; |
|
108 alternateBinaryID = [myBundle objectForInfoDictionaryKey:@"FirefoxBinary"]; |
|
109 NSLog(@"found override firefox binary: %@", alternateBinaryID); |
|
110 |
|
111 @try { |
|
112 //find a webapprt binary to launch with. throws an exception with error dialog if none found. |
|
113 firefoxPath = PathToWebRT(alternateBinaryID); |
|
114 NSLog(@"USING FIREFOX : %@", firefoxPath); |
|
115 |
|
116 NSString *myWebRTPath = [myBundle pathForAuxiliaryExecutable: @"webapprt"]; |
|
117 if (!myWebRTPath) { |
|
118 @throw MakeException(@"Missing Web Runtime Files", @"Cannot locate binary for this App"); |
|
119 } |
|
120 |
|
121 //GET FIREFOX BUILD ID |
|
122 NSString *firefoxINIFilePath = [NSString stringWithFormat:@"%@%s%s", firefoxPath, APP_CONTENTS_PATH, FXAPPINI_NAME]; |
|
123 nsINIParser ffparser; |
|
124 NSLog(@"Looking for firefox ini file here: %@", firefoxINIFilePath); |
|
125 |
|
126 if (NS_FAILED(ffparser.Init([firefoxINIFilePath UTF8String]))) { |
|
127 NSLog(@"Unable to locate Firefox application.ini"); |
|
128 @throw MakeException(@"Error", @"Unable to parse environment files for application startup"); |
|
129 } |
|
130 |
|
131 char ffVersChars[MAXPATHLEN]; |
|
132 if (NS_FAILED(ffparser.GetString("App", "BuildID", ffVersChars, MAXPATHLEN))) { |
|
133 NSLog(@"Unable to retrieve Firefox BuildID"); |
|
134 @throw MakeException(@"Error", @"Unable to determine Firefox version."); |
|
135 } |
|
136 NSString* firefoxVersion = [NSString stringWithFormat:@"%s", ffVersChars]; |
|
137 |
|
138 NSLog(@"FIREFOX WEBAPPRT BUILDID: %@", firefoxVersion); |
|
139 //GOT FIREFOX BUILD ID |
|
140 |
|
141 //COMPARE MY BUILD ID AND FIREFOX BUILD ID |
|
142 if ([myVersion compare: firefoxVersion] != NSOrderedSame) { |
|
143 //we are going to assume that if they are different, we need to re-copy the webapprt, regardless of whether |
|
144 // it is newer or older. If we don't find a webapprt, then the current Firefox must not be new enough to run webapps. |
|
145 NSLog(@"### This Application has an old webrt. Updating it."); |
|
146 NSLog(@"### My webapprt path: %@", myWebRTPath); |
|
147 |
|
148 NSFileManager* fileClerk = [[NSFileManager alloc] init]; |
|
149 NSError *errorDesc = nil; |
|
150 |
|
151 //we know the firefox path, so copy the new webapprt here |
|
152 NSString *newWebRTPath = [NSString stringWithFormat: @"%@%s%s", firefoxPath, APP_CONTENTS_PATH, WEBAPPRT_EXECUTABLE]; |
|
153 NSLog(@"### Firefox webapprt path: %@", newWebRTPath); |
|
154 if (![fileClerk fileExistsAtPath:newWebRTPath]) { |
|
155 NSString* msg = [NSString stringWithFormat: @"This version of Firefox (%@) cannot run web applications, because it is not recent enough or damaged", firefoxVersion]; |
|
156 @throw MakeException(@"Missing Web Runtime Files", msg); |
|
157 } |
|
158 |
|
159 [fileClerk removeItemAtPath: myWebRTPath error: &errorDesc]; |
|
160 if (errorDesc != nil) { |
|
161 NSLog(@"failed to unlink old binary file at path: %@ with error: %@", myWebRTPath, errorDesc); |
|
162 @throw MakeException(@"Unable To Update", @"Failed preparation for Web Runtime update"); |
|
163 } |
|
164 |
|
165 [fileClerk copyItemAtPath: newWebRTPath toPath: myWebRTPath error: &errorDesc]; |
|
166 [fileClerk release]; |
|
167 if (errorDesc != nil) { |
|
168 NSLog(@"failed to copy new webrt file: %@", errorDesc); |
|
169 @throw MakeException(@"Unable To Update Web Runtime", @"Failed to update Web Runtime"); |
|
170 } else { |
|
171 NSLog(@"### Successfully updated webapprt, relaunching"); |
|
172 } |
|
173 |
|
174 //execv the new binary, and ride off into the sunset |
|
175 ExecNewBinary(myWebRTPath); |
|
176 |
|
177 } else { |
|
178 //we are ready to load XUL and such, and go go go |
|
179 |
|
180 NSLog(@"This Application has the newest webrt. Launching!"); |
|
181 |
|
182 int result = 0; |
|
183 char rtINIPath[MAXPATHLEN]; |
|
184 |
|
185 // Set up our environment to know where webapp.ini was loaded from. |
|
186 char appEnv[MAXPATHLEN]; |
|
187 snprintf(appEnv, MAXPATHLEN, "%s%s%s", [myBundlePath UTF8String], APP_CONTENTS_PATH, WEBAPPINI_NAME); |
|
188 if (setenv("XUL_APP_FILE", appEnv, 1)) { |
|
189 NSLog(@"Couldn't set XUL_APP_FILE to: %s", appEnv); |
|
190 @throw MakeException(@"Error", @"Unable to set Web Runtime INI file."); |
|
191 } |
|
192 NSLog(@"Set XUL_APP_FILE to: %s", appEnv); |
|
193 |
|
194 //CONSTRUCT GREDIR AND CALL XPCOMGLUE WITH IT |
|
195 char greDir[MAXPATHLEN]; |
|
196 snprintf(greDir, MAXPATHLEN, "%s%s", [firefoxPath UTF8String], APP_CONTENTS_PATH); |
|
197 if (!NS_SUCCEEDED(AttemptGRELoad(greDir))) { |
|
198 @throw MakeException(@"Error", @"Unable to load XUL files for application startup"); |
|
199 } |
|
200 |
|
201 // NOTE: The GRE has successfully loaded, so we can use XPCOM now |
|
202 |
|
203 NS_LogInit(); |
|
204 { // Scope for any XPCOM stuff we create |
|
205 |
|
206 // Get the path to the runtime directory. |
|
207 char rtDir[MAXPATHLEN]; |
|
208 snprintf(rtDir, MAXPATHLEN, "%s%s%s", [firefoxPath UTF8String], APP_CONTENTS_PATH, WEBAPPRT_PATH); |
|
209 |
|
210 // Get the path to the runtime's INI file. This is in the runtime |
|
211 // directory. |
|
212 snprintf(rtINIPath, MAXPATHLEN, "%s%s%s%s", [firefoxPath UTF8String], APP_CONTENTS_PATH, WEBAPPRT_PATH, WEBRTINI_NAME); |
|
213 NSLog(@"WebappRT application.ini path: %s", rtINIPath); |
|
214 |
|
215 // Load the runtime's INI from its path. |
|
216 nsCOMPtr<nsIFile> rtINI; |
|
217 if (NS_FAILED(XRE_GetFileFromPath(rtINIPath, getter_AddRefs(rtINI)))) { |
|
218 NSLog(@"Runtime INI path not recognized: '%s'\n", rtINIPath); |
|
219 @throw MakeException(@"Error", @"Incorrect path to base INI file."); |
|
220 } |
|
221 |
|
222 bool exists; |
|
223 nsresult rv = rtINI->Exists(&exists); |
|
224 if (NS_FAILED(rv) || !exists) { |
|
225 NSString* msg = [NSString stringWithFormat: @"This copy of Firefox (%@) cannot run Apps, because it is missing the Web Runtime's application.ini file", firefoxVersion]; |
|
226 @throw MakeException(@"Missing Web Runtime application.ini", msg); |
|
227 } |
|
228 |
|
229 nsXREAppData *webShellAppData; |
|
230 if (NS_FAILED(XRE_CreateAppData(rtINI, &webShellAppData))) { |
|
231 NSLog(@"Couldn't read WebappRT application.ini: %s", rtINIPath); |
|
232 @throw MakeException(@"Error", @"Unable to parse base INI file."); |
|
233 } |
|
234 |
|
235 NSString *profile = [args objectForKey:@"profile"]; |
|
236 if (profile) { |
|
237 NSLog(@"Profile specified with -profile: %@", profile); |
|
238 } |
|
239 else { |
|
240 nsINIParser parser; |
|
241 if (NS_FAILED(parser.Init(appEnv))) { |
|
242 NSLog(@"%s was not found\n", appEnv); |
|
243 @throw MakeException(@"Error", @"Unable to parse environment files for application startup"); |
|
244 } |
|
245 char profile[MAXPATHLEN]; |
|
246 if (NS_FAILED(parser.GetString("Webapp", "Profile", profile, MAXPATHLEN))) { |
|
247 NSLog(@"Unable to retrieve profile from App INI file"); |
|
248 @throw MakeException(@"Error", @"Unable to retrieve installation profile."); |
|
249 } |
|
250 NSLog(@"setting app profile: %s", profile); |
|
251 SetAllocatedString(webShellAppData->profile, profile); |
|
252 } |
|
253 |
|
254 nsCOMPtr<nsIFile> directory; |
|
255 if (NS_FAILED(XRE_GetFileFromPath(rtDir, getter_AddRefs(directory)))) { |
|
256 NSLog(@"Unable to open app dir"); |
|
257 @throw MakeException(@"Error", @"Unable to open App directory."); |
|
258 } |
|
259 |
|
260 nsCOMPtr<nsIFile> xreDir; |
|
261 if (NS_FAILED(XRE_GetFileFromPath(greDir, getter_AddRefs(xreDir)))) { |
|
262 NSLog(@"Unable to open XRE dir"); |
|
263 @throw MakeException(@"Error", @"Unable to open App XRE directory."); |
|
264 } |
|
265 |
|
266 xreDir.forget(&webShellAppData->xreDirectory); |
|
267 NS_IF_RELEASE(webShellAppData->directory); |
|
268 directory.forget(&webShellAppData->directory); |
|
269 |
|
270 // There is only XUL. |
|
271 result = XRE_main(argc, argv, webShellAppData, 0); |
|
272 |
|
273 XRE_FreeAppData(webShellAppData); |
|
274 } |
|
275 NS_LogTerm(); |
|
276 |
|
277 return result; |
|
278 } |
|
279 |
|
280 } |
|
281 @catch (NSException *e) { |
|
282 NSLog(@"got exception: %@", e); |
|
283 DisplayErrorAlert([e name], [e reason]); |
|
284 } |
|
285 @finally { |
|
286 [pool drain]; |
|
287 } |
|
288 return 0; |
|
289 } //end main |
|
290 |
|
291 |
|
292 NSException* |
|
293 MakeException(NSString* name, NSString* message) |
|
294 { |
|
295 NSException* myException = [NSException |
|
296 exceptionWithName:name |
|
297 reason:message |
|
298 userInfo:nil]; |
|
299 return myException; |
|
300 } |
|
301 |
|
302 void |
|
303 DisplayErrorAlert(NSString* title, NSString* message) |
|
304 { |
|
305 CFUserNotificationDisplayNotice(0, kCFUserNotificationNoteAlertLevel, |
|
306 NULL, NULL, NULL, |
|
307 (CFStringRef)title, |
|
308 (CFStringRef)message, |
|
309 CFSTR("Quit") |
|
310 ); |
|
311 } |
|
312 |
|
313 /* Find the currently installed Firefox, if any, and return |
|
314 * an absolute path to it. may return nil */ |
|
315 NSString |
|
316 *PathToWebRT(NSString* alternateBinaryID) |
|
317 { |
|
318 //default is firefox |
|
319 NSString *binaryPath = nil; |
|
320 |
|
321 // We're run from the Firefox bundle during WebappRT chrome and content tests. |
|
322 NSString *myBundlePath = [[NSBundle mainBundle] bundlePath]; |
|
323 NSString *fxPath = [NSString stringWithFormat:@"%@%sfirefox-bin", |
|
324 myBundlePath, APP_CONTENTS_PATH]; |
|
325 if ([[NSFileManager defaultManager] fileExistsAtPath:fxPath]) { |
|
326 return myBundlePath; |
|
327 } |
|
328 |
|
329 //we look for these flavors of Firefox, in this order |
|
330 NSArray* launchBinarySearchList = [NSArray arrayWithObjects: @"org.mozilla.nightly", |
|
331 @"org.mozilla.aurora", |
|
332 @"org.mozilla.firefox", nil]; |
|
333 |
|
334 // If they provided a binary ID, use that. |
|
335 if (alternateBinaryID != nil && ([alternateBinaryID length] > 0)) { |
|
336 binaryPath = [[NSWorkspace sharedWorkspace] absolutePathForAppBundleWithIdentifier:alternateBinaryID]; |
|
337 if (binaryPath && [binaryPath length] > 0) { |
|
338 return binaryPath; |
|
339 } |
|
340 } |
|
341 |
|
342 //No override found, loop through the various flavors of firefox we have |
|
343 for (NSString* signature in launchBinarySearchList) { |
|
344 NSLog(@"SEARCHING for webapprt, trying: %@", signature); |
|
345 binaryPath = [[NSWorkspace sharedWorkspace] absolutePathForAppBundleWithIdentifier:signature]; |
|
346 if (binaryPath && [binaryPath length] > 0) { |
|
347 return binaryPath; |
|
348 } |
|
349 } |
|
350 |
|
351 NSLog(@"unable to find a valid webrt path"); |
|
352 @throw MakeException(@"This App requires that Firefox version 16 or above is installed.", @"Firefox 16+ has not been detected."); |
|
353 |
|
354 return nil; |
|
355 } |
|
356 |
|
357 void |
|
358 ExecNewBinary(NSString* launchPath) |
|
359 { |
|
360 NSLog(@" launching webrt at path: %@\n", launchPath); |
|
361 |
|
362 const char *const newargv[] = {[launchPath UTF8String], NULL}; |
|
363 |
|
364 NSLog(@"COMMAND LINE: '%@ %s'", launchPath, newargv[0]); |
|
365 execv([launchPath UTF8String], (char **)newargv); |
|
366 } |