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