michael@0: /* This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this file, michael@0: * You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: //PLAN: michael@0: michael@0: // open my bundle, check for an override binary signature michael@0: // find the newest firefox. open its bundle, get the version number. michael@0: // if the firefox version is different than ours: michael@0: // delete our own binary, michael@0: // copy the newer webapprt binary from Firefox michael@0: // exec it, and quit michael@0: michael@0: #include michael@0: #include michael@0: #include michael@0: #include michael@0: #include michael@0: michael@0: #include michael@0: #include michael@0: michael@0: #include "nsXPCOMGlue.h" michael@0: #include "nsINIParser.h" michael@0: #include "nsXPCOMPrivate.h" // for MAXPATHLEN and XPCOM_DLL michael@0: #include "nsXULAppAPI.h" michael@0: #include "nsComponentManagerUtils.h" michael@0: #include "nsCOMPtr.h" michael@0: #include "nsIFile.h" michael@0: #include "nsStringGlue.h" michael@0: #include "mozilla/AppData.h" michael@0: michael@0: using namespace mozilla; michael@0: michael@0: const char WEBAPPRT_EXECUTABLE[] = "webapprt-stub"; michael@0: const char FXAPPINI_NAME[] = "application.ini"; michael@0: const char WEBAPPINI_NAME[] = "webapp.ini"; michael@0: const char WEBRTINI_NAME[] = "webapprt.ini"; michael@0: michael@0: //need the correct relative path here michael@0: const char APP_CONTENTS_PATH[] = "/Contents/MacOS/"; michael@0: michael@0: //the path to the WebappRT subdir within the Firefox app contents dir michael@0: const char WEBAPPRT_PATH[] = "webapprt/"; michael@0: michael@0: void ExecNewBinary(NSString* launchPath); michael@0: michael@0: NSString *PathToWebRT(NSString* alternateBinaryID); michael@0: michael@0: NSException* MakeException(NSString* name, NSString* message); michael@0: michael@0: void DisplayErrorAlert(NSString* title, NSString* message); michael@0: michael@0: XRE_GetFileFromPathType XRE_GetFileFromPath; michael@0: XRE_CreateAppDataType XRE_CreateAppData; michael@0: XRE_FreeAppDataType XRE_FreeAppData; michael@0: XRE_mainType XRE_main; michael@0: michael@0: const nsDynamicFunctionLoad kXULFuncs[] = { michael@0: { "XRE_GetFileFromPath", (NSFuncPtr*) &XRE_GetFileFromPath }, michael@0: { "XRE_CreateAppData", (NSFuncPtr*) &XRE_CreateAppData }, michael@0: { "XRE_FreeAppData", (NSFuncPtr*) &XRE_FreeAppData }, michael@0: { "XRE_main", (NSFuncPtr*) &XRE_main }, michael@0: { nullptr, nullptr } michael@0: }; michael@0: michael@0: nsresult michael@0: AttemptGRELoad(char *greDir) michael@0: { michael@0: nsresult rv; michael@0: char xpcomDLLPath[MAXPATHLEN]; michael@0: snprintf(xpcomDLLPath, MAXPATHLEN, "%s%s", greDir, XPCOM_DLL); michael@0: michael@0: rv = XPCOMGlueStartup(xpcomDLLPath); michael@0: michael@0: if (NS_FAILED(rv)) { michael@0: return rv; michael@0: } michael@0: michael@0: rv = XPCOMGlueLoadXULFunctions(kXULFuncs); michael@0: if (NS_FAILED(rv)) { michael@0: return rv; michael@0: } michael@0: michael@0: return rv; michael@0: } michael@0: michael@0: int michael@0: main(int argc, char **argv) michael@0: { michael@0: NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; michael@0: michael@0: NSDictionary *args = [[NSUserDefaults standardUserDefaults] michael@0: volatileDomainForName:NSArgumentDomain]; michael@0: michael@0: NSString *firefoxPath = nil; michael@0: NSString *alternateBinaryID = nil; michael@0: michael@0: //this is our version, to be compared with the version of the binary we are asked to use michael@0: NSString* myVersion = [NSString stringWithFormat:@"%s", NS_STRINGIFY(GRE_BUILDID)]; michael@0: michael@0: NSLog(@"MY WEBAPPRT BUILDID: %@", myVersion); michael@0: michael@0: michael@0: //I need to look in our bundle first, before deciding what firefox binary to use michael@0: NSBundle* myBundle = [NSBundle mainBundle]; michael@0: NSString* myBundlePath = [myBundle bundlePath]; michael@0: alternateBinaryID = [myBundle objectForInfoDictionaryKey:@"FirefoxBinary"]; michael@0: NSLog(@"found override firefox binary: %@", alternateBinaryID); michael@0: michael@0: @try { michael@0: //find a webapprt binary to launch with. throws an exception with error dialog if none found. michael@0: firefoxPath = PathToWebRT(alternateBinaryID); michael@0: NSLog(@"USING FIREFOX : %@", firefoxPath); michael@0: michael@0: NSString *myWebRTPath = [myBundle pathForAuxiliaryExecutable: @"webapprt"]; michael@0: if (!myWebRTPath) { michael@0: @throw MakeException(@"Missing Web Runtime Files", @"Cannot locate binary for this App"); michael@0: } michael@0: michael@0: //GET FIREFOX BUILD ID michael@0: NSString *firefoxINIFilePath = [NSString stringWithFormat:@"%@%s%s", firefoxPath, APP_CONTENTS_PATH, FXAPPINI_NAME]; michael@0: nsINIParser ffparser; michael@0: NSLog(@"Looking for firefox ini file here: %@", firefoxINIFilePath); michael@0: michael@0: if (NS_FAILED(ffparser.Init([firefoxINIFilePath UTF8String]))) { michael@0: NSLog(@"Unable to locate Firefox application.ini"); michael@0: @throw MakeException(@"Error", @"Unable to parse environment files for application startup"); michael@0: } michael@0: michael@0: char ffVersChars[MAXPATHLEN]; michael@0: if (NS_FAILED(ffparser.GetString("App", "BuildID", ffVersChars, MAXPATHLEN))) { michael@0: NSLog(@"Unable to retrieve Firefox BuildID"); michael@0: @throw MakeException(@"Error", @"Unable to determine Firefox version."); michael@0: } michael@0: NSString* firefoxVersion = [NSString stringWithFormat:@"%s", ffVersChars]; michael@0: michael@0: NSLog(@"FIREFOX WEBAPPRT BUILDID: %@", firefoxVersion); michael@0: //GOT FIREFOX BUILD ID michael@0: michael@0: //COMPARE MY BUILD ID AND FIREFOX BUILD ID michael@0: if ([myVersion compare: firefoxVersion] != NSOrderedSame) { michael@0: //we are going to assume that if they are different, we need to re-copy the webapprt, regardless of whether michael@0: // it is newer or older. If we don't find a webapprt, then the current Firefox must not be new enough to run webapps. michael@0: NSLog(@"### This Application has an old webrt. Updating it."); michael@0: NSLog(@"### My webapprt path: %@", myWebRTPath); michael@0: michael@0: NSFileManager* fileClerk = [[NSFileManager alloc] init]; michael@0: NSError *errorDesc = nil; michael@0: michael@0: //we know the firefox path, so copy the new webapprt here michael@0: NSString *newWebRTPath = [NSString stringWithFormat: @"%@%s%s", firefoxPath, APP_CONTENTS_PATH, WEBAPPRT_EXECUTABLE]; michael@0: NSLog(@"### Firefox webapprt path: %@", newWebRTPath); michael@0: if (![fileClerk fileExistsAtPath:newWebRTPath]) { michael@0: NSString* msg = [NSString stringWithFormat: @"This version of Firefox (%@) cannot run web applications, because it is not recent enough or damaged", firefoxVersion]; michael@0: @throw MakeException(@"Missing Web Runtime Files", msg); michael@0: } michael@0: michael@0: [fileClerk removeItemAtPath: myWebRTPath error: &errorDesc]; michael@0: if (errorDesc != nil) { michael@0: NSLog(@"failed to unlink old binary file at path: %@ with error: %@", myWebRTPath, errorDesc); michael@0: @throw MakeException(@"Unable To Update", @"Failed preparation for Web Runtime update"); michael@0: } michael@0: michael@0: [fileClerk copyItemAtPath: newWebRTPath toPath: myWebRTPath error: &errorDesc]; michael@0: [fileClerk release]; michael@0: if (errorDesc != nil) { michael@0: NSLog(@"failed to copy new webrt file: %@", errorDesc); michael@0: @throw MakeException(@"Unable To Update Web Runtime", @"Failed to update Web Runtime"); michael@0: } else { michael@0: NSLog(@"### Successfully updated webapprt, relaunching"); michael@0: } michael@0: michael@0: //execv the new binary, and ride off into the sunset michael@0: ExecNewBinary(myWebRTPath); michael@0: michael@0: } else { michael@0: //we are ready to load XUL and such, and go go go michael@0: michael@0: NSLog(@"This Application has the newest webrt. Launching!"); michael@0: michael@0: int result = 0; michael@0: char rtINIPath[MAXPATHLEN]; michael@0: michael@0: // Set up our environment to know where webapp.ini was loaded from. michael@0: char appEnv[MAXPATHLEN]; michael@0: snprintf(appEnv, MAXPATHLEN, "%s%s%s", [myBundlePath UTF8String], APP_CONTENTS_PATH, WEBAPPINI_NAME); michael@0: if (setenv("XUL_APP_FILE", appEnv, 1)) { michael@0: NSLog(@"Couldn't set XUL_APP_FILE to: %s", appEnv); michael@0: @throw MakeException(@"Error", @"Unable to set Web Runtime INI file."); michael@0: } michael@0: NSLog(@"Set XUL_APP_FILE to: %s", appEnv); michael@0: michael@0: //CONSTRUCT GREDIR AND CALL XPCOMGLUE WITH IT michael@0: char greDir[MAXPATHLEN]; michael@0: snprintf(greDir, MAXPATHLEN, "%s%s", [firefoxPath UTF8String], APP_CONTENTS_PATH); michael@0: if (!NS_SUCCEEDED(AttemptGRELoad(greDir))) { michael@0: @throw MakeException(@"Error", @"Unable to load XUL files for application startup"); michael@0: } michael@0: michael@0: // NOTE: The GRE has successfully loaded, so we can use XPCOM now michael@0: michael@0: NS_LogInit(); michael@0: { // Scope for any XPCOM stuff we create michael@0: michael@0: // Get the path to the runtime directory. michael@0: char rtDir[MAXPATHLEN]; michael@0: snprintf(rtDir, MAXPATHLEN, "%s%s%s", [firefoxPath UTF8String], APP_CONTENTS_PATH, WEBAPPRT_PATH); michael@0: michael@0: // Get the path to the runtime's INI file. This is in the runtime michael@0: // directory. michael@0: snprintf(rtINIPath, MAXPATHLEN, "%s%s%s%s", [firefoxPath UTF8String], APP_CONTENTS_PATH, WEBAPPRT_PATH, WEBRTINI_NAME); michael@0: NSLog(@"WebappRT application.ini path: %s", rtINIPath); michael@0: michael@0: // Load the runtime's INI from its path. michael@0: nsCOMPtr rtINI; michael@0: if (NS_FAILED(XRE_GetFileFromPath(rtINIPath, getter_AddRefs(rtINI)))) { michael@0: NSLog(@"Runtime INI path not recognized: '%s'\n", rtINIPath); michael@0: @throw MakeException(@"Error", @"Incorrect path to base INI file."); michael@0: } michael@0: michael@0: bool exists; michael@0: nsresult rv = rtINI->Exists(&exists); michael@0: if (NS_FAILED(rv) || !exists) { michael@0: NSString* msg = [NSString stringWithFormat: @"This copy of Firefox (%@) cannot run Apps, because it is missing the Web Runtime's application.ini file", firefoxVersion]; michael@0: @throw MakeException(@"Missing Web Runtime application.ini", msg); michael@0: } michael@0: michael@0: nsXREAppData *webShellAppData; michael@0: if (NS_FAILED(XRE_CreateAppData(rtINI, &webShellAppData))) { michael@0: NSLog(@"Couldn't read WebappRT application.ini: %s", rtINIPath); michael@0: @throw MakeException(@"Error", @"Unable to parse base INI file."); michael@0: } michael@0: michael@0: NSString *profile = [args objectForKey:@"profile"]; michael@0: if (profile) { michael@0: NSLog(@"Profile specified with -profile: %@", profile); michael@0: } michael@0: else { michael@0: nsINIParser parser; michael@0: if (NS_FAILED(parser.Init(appEnv))) { michael@0: NSLog(@"%s was not found\n", appEnv); michael@0: @throw MakeException(@"Error", @"Unable to parse environment files for application startup"); michael@0: } michael@0: char profile[MAXPATHLEN]; michael@0: if (NS_FAILED(parser.GetString("Webapp", "Profile", profile, MAXPATHLEN))) { michael@0: NSLog(@"Unable to retrieve profile from App INI file"); michael@0: @throw MakeException(@"Error", @"Unable to retrieve installation profile."); michael@0: } michael@0: NSLog(@"setting app profile: %s", profile); michael@0: SetAllocatedString(webShellAppData->profile, profile); michael@0: } michael@0: michael@0: nsCOMPtr directory; michael@0: if (NS_FAILED(XRE_GetFileFromPath(rtDir, getter_AddRefs(directory)))) { michael@0: NSLog(@"Unable to open app dir"); michael@0: @throw MakeException(@"Error", @"Unable to open App directory."); michael@0: } michael@0: michael@0: nsCOMPtr xreDir; michael@0: if (NS_FAILED(XRE_GetFileFromPath(greDir, getter_AddRefs(xreDir)))) { michael@0: NSLog(@"Unable to open XRE dir"); michael@0: @throw MakeException(@"Error", @"Unable to open App XRE directory."); michael@0: } michael@0: michael@0: xreDir.forget(&webShellAppData->xreDirectory); michael@0: NS_IF_RELEASE(webShellAppData->directory); michael@0: directory.forget(&webShellAppData->directory); michael@0: michael@0: // There is only XUL. michael@0: result = XRE_main(argc, argv, webShellAppData, 0); michael@0: michael@0: XRE_FreeAppData(webShellAppData); michael@0: } michael@0: NS_LogTerm(); michael@0: michael@0: return result; michael@0: } michael@0: michael@0: } michael@0: @catch (NSException *e) { michael@0: NSLog(@"got exception: %@", e); michael@0: DisplayErrorAlert([e name], [e reason]); michael@0: } michael@0: @finally { michael@0: [pool drain]; michael@0: } michael@0: return 0; michael@0: } //end main michael@0: michael@0: michael@0: NSException* michael@0: MakeException(NSString* name, NSString* message) michael@0: { michael@0: NSException* myException = [NSException michael@0: exceptionWithName:name michael@0: reason:message michael@0: userInfo:nil]; michael@0: return myException; michael@0: } michael@0: michael@0: void michael@0: DisplayErrorAlert(NSString* title, NSString* message) michael@0: { michael@0: CFUserNotificationDisplayNotice(0, kCFUserNotificationNoteAlertLevel, michael@0: NULL, NULL, NULL, michael@0: (CFStringRef)title, michael@0: (CFStringRef)message, michael@0: CFSTR("Quit") michael@0: ); michael@0: } michael@0: michael@0: /* Find the currently installed Firefox, if any, and return michael@0: * an absolute path to it. may return nil */ michael@0: NSString michael@0: *PathToWebRT(NSString* alternateBinaryID) michael@0: { michael@0: //default is firefox michael@0: NSString *binaryPath = nil; michael@0: michael@0: // We're run from the Firefox bundle during WebappRT chrome and content tests. michael@0: NSString *myBundlePath = [[NSBundle mainBundle] bundlePath]; michael@0: NSString *fxPath = [NSString stringWithFormat:@"%@%sfirefox-bin", michael@0: myBundlePath, APP_CONTENTS_PATH]; michael@0: if ([[NSFileManager defaultManager] fileExistsAtPath:fxPath]) { michael@0: return myBundlePath; michael@0: } michael@0: michael@0: //we look for these flavors of Firefox, in this order michael@0: NSArray* launchBinarySearchList = [NSArray arrayWithObjects: @"org.mozilla.nightly", michael@0: @"org.mozilla.aurora", michael@0: @"org.mozilla.firefox", nil]; michael@0: michael@0: // If they provided a binary ID, use that. michael@0: if (alternateBinaryID != nil && ([alternateBinaryID length] > 0)) { michael@0: binaryPath = [[NSWorkspace sharedWorkspace] absolutePathForAppBundleWithIdentifier:alternateBinaryID]; michael@0: if (binaryPath && [binaryPath length] > 0) { michael@0: return binaryPath; michael@0: } michael@0: } michael@0: michael@0: //No override found, loop through the various flavors of firefox we have michael@0: for (NSString* signature in launchBinarySearchList) { michael@0: NSLog(@"SEARCHING for webapprt, trying: %@", signature); michael@0: binaryPath = [[NSWorkspace sharedWorkspace] absolutePathForAppBundleWithIdentifier:signature]; michael@0: if (binaryPath && [binaryPath length] > 0) { michael@0: return binaryPath; michael@0: } michael@0: } michael@0: michael@0: NSLog(@"unable to find a valid webrt path"); michael@0: @throw MakeException(@"This App requires that Firefox version 16 or above is installed.", @"Firefox 16+ has not been detected."); michael@0: michael@0: return nil; michael@0: } michael@0: michael@0: void michael@0: ExecNewBinary(NSString* launchPath) michael@0: { michael@0: NSLog(@" launching webrt at path: %@\n", launchPath); michael@0: michael@0: const char *const newargv[] = {[launchPath UTF8String], NULL}; michael@0: michael@0: NSLog(@"COMMAND LINE: '%@ %s'", launchPath, newargv[0]); michael@0: execv([launchPath UTF8String], (char **)newargv); michael@0: }