Thu, 22 Jan 2015 13:21:57 +0100
Incorporate requested changes from Mozilla in review:
https://bugzilla.mozilla.org/show_bug.cgi?id=1123480#c6
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/. */
5 //PLAN:
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
14 #include <stdio.h>
15 #include <string.h>
16 #include <unistd.h>
17 #include <dirent.h>
18 #include <sys/stat.h>
20 #include <Cocoa/Cocoa.h>
21 #include <Foundation/Foundation.h>
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"
33 using namespace mozilla;
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";
40 //need the correct relative path here
41 const char APP_CONTENTS_PATH[] = "/Contents/MacOS/";
43 //the path to the WebappRT subdir within the Firefox app contents dir
44 const char WEBAPPRT_PATH[] = "webapprt/";
46 void ExecNewBinary(NSString* launchPath);
48 NSString *PathToWebRT(NSString* alternateBinaryID);
50 NSException* MakeException(NSString* name, NSString* message);
52 void DisplayErrorAlert(NSString* title, NSString* message);
54 XRE_GetFileFromPathType XRE_GetFileFromPath;
55 XRE_CreateAppDataType XRE_CreateAppData;
56 XRE_FreeAppDataType XRE_FreeAppData;
57 XRE_mainType XRE_main;
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 };
67 nsresult
68 AttemptGRELoad(char *greDir)
69 {
70 nsresult rv;
71 char xpcomDLLPath[MAXPATHLEN];
72 snprintf(xpcomDLLPath, MAXPATHLEN, "%s%s", greDir, XPCOM_DLL);
74 rv = XPCOMGlueStartup(xpcomDLLPath);
76 if (NS_FAILED(rv)) {
77 return rv;
78 }
80 rv = XPCOMGlueLoadXULFunctions(kXULFuncs);
81 if (NS_FAILED(rv)) {
82 return rv;
83 }
85 return rv;
86 }
88 int
89 main(int argc, char **argv)
90 {
91 NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
93 NSDictionary *args = [[NSUserDefaults standardUserDefaults]
94 volatileDomainForName:NSArgumentDomain];
96 NSString *firefoxPath = nil;
97 NSString *alternateBinaryID = nil;
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)];
102 NSLog(@"MY WEBAPPRT BUILDID: %@", myVersion);
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);
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);
116 NSString *myWebRTPath = [myBundle pathForAuxiliaryExecutable: @"webapprt"];
117 if (!myWebRTPath) {
118 @throw MakeException(@"Missing Web Runtime Files", @"Cannot locate binary for this App");
119 }
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);
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 }
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];
138 NSLog(@"FIREFOX WEBAPPRT BUILDID: %@", firefoxVersion);
139 //GOT FIREFOX BUILD ID
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);
148 NSFileManager* fileClerk = [[NSFileManager alloc] init];
149 NSError *errorDesc = nil;
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 }
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 }
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 }
174 //execv the new binary, and ride off into the sunset
175 ExecNewBinary(myWebRTPath);
177 } else {
178 //we are ready to load XUL and such, and go go go
180 NSLog(@"This Application has the newest webrt. Launching!");
182 int result = 0;
183 char rtINIPath[MAXPATHLEN];
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);
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 }
201 // NOTE: The GRE has successfully loaded, so we can use XPCOM now
203 NS_LogInit();
204 { // Scope for any XPCOM stuff we create
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);
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);
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 }
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 }
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 }
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 }
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 }
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 }
266 xreDir.forget(&webShellAppData->xreDirectory);
267 NS_IF_RELEASE(webShellAppData->directory);
268 directory.forget(&webShellAppData->directory);
270 // There is only XUL.
271 result = XRE_main(argc, argv, webShellAppData, 0);
273 XRE_FreeAppData(webShellAppData);
274 }
275 NS_LogTerm();
277 return result;
278 }
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
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 }
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 }
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;
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 }
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];
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 }
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 }
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.");
354 return nil;
355 }
357 void
358 ExecNewBinary(NSString* launchPath)
359 {
360 NSLog(@" launching webrt at path: %@\n", launchPath);
362 const char *const newargv[] = {[launchPath UTF8String], NULL};
364 NSLog(@"COMMAND LINE: '%@ %s'", launchPath, newargv[0]);
365 execv([launchPath UTF8String], (char **)newargv);
366 }