webapprt/mac/webapprt.mm

Thu, 22 Jan 2015 13:21:57 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Thu, 22 Jan 2015 13:21:57 +0100
branch
TOR_BUG_9701
changeset 15
b8a032363ba2
permissions
-rw-r--r--

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 }

mercurial