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

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

mercurial