1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/webapprt/mac/webapprt.mm Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,366 @@ 1.4 +/* This Source Code Form is subject to the terms of the Mozilla Public 1.5 + * License, v. 2.0. If a copy of the MPL was not distributed with this file, 1.6 + * You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.7 + 1.8 +//PLAN: 1.9 + 1.10 + // open my bundle, check for an override binary signature 1.11 + // find the newest firefox. open its bundle, get the version number. 1.12 + // if the firefox version is different than ours: 1.13 + // delete our own binary, 1.14 + // copy the newer webapprt binary from Firefox 1.15 + // exec it, and quit 1.16 + 1.17 +#include <stdio.h> 1.18 +#include <string.h> 1.19 +#include <unistd.h> 1.20 +#include <dirent.h> 1.21 +#include <sys/stat.h> 1.22 + 1.23 +#include <Cocoa/Cocoa.h> 1.24 +#include <Foundation/Foundation.h> 1.25 + 1.26 +#include "nsXPCOMGlue.h" 1.27 +#include "nsINIParser.h" 1.28 +#include "nsXPCOMPrivate.h" // for MAXPATHLEN and XPCOM_DLL 1.29 +#include "nsXULAppAPI.h" 1.30 +#include "nsComponentManagerUtils.h" 1.31 +#include "nsCOMPtr.h" 1.32 +#include "nsIFile.h" 1.33 +#include "nsStringGlue.h" 1.34 +#include "mozilla/AppData.h" 1.35 + 1.36 +using namespace mozilla; 1.37 + 1.38 +const char WEBAPPRT_EXECUTABLE[] = "webapprt-stub"; 1.39 +const char FXAPPINI_NAME[] = "application.ini"; 1.40 +const char WEBAPPINI_NAME[] = "webapp.ini"; 1.41 +const char WEBRTINI_NAME[] = "webapprt.ini"; 1.42 + 1.43 +//need the correct relative path here 1.44 +const char APP_CONTENTS_PATH[] = "/Contents/MacOS/"; 1.45 + 1.46 +//the path to the WebappRT subdir within the Firefox app contents dir 1.47 +const char WEBAPPRT_PATH[] = "webapprt/"; 1.48 + 1.49 +void ExecNewBinary(NSString* launchPath); 1.50 + 1.51 +NSString *PathToWebRT(NSString* alternateBinaryID); 1.52 + 1.53 +NSException* MakeException(NSString* name, NSString* message); 1.54 + 1.55 +void DisplayErrorAlert(NSString* title, NSString* message); 1.56 + 1.57 +XRE_GetFileFromPathType XRE_GetFileFromPath; 1.58 +XRE_CreateAppDataType XRE_CreateAppData; 1.59 +XRE_FreeAppDataType XRE_FreeAppData; 1.60 +XRE_mainType XRE_main; 1.61 + 1.62 +const nsDynamicFunctionLoad kXULFuncs[] = { 1.63 + { "XRE_GetFileFromPath", (NSFuncPtr*) &XRE_GetFileFromPath }, 1.64 + { "XRE_CreateAppData", (NSFuncPtr*) &XRE_CreateAppData }, 1.65 + { "XRE_FreeAppData", (NSFuncPtr*) &XRE_FreeAppData }, 1.66 + { "XRE_main", (NSFuncPtr*) &XRE_main }, 1.67 + { nullptr, nullptr } 1.68 +}; 1.69 + 1.70 +nsresult 1.71 +AttemptGRELoad(char *greDir) 1.72 +{ 1.73 + nsresult rv; 1.74 + char xpcomDLLPath[MAXPATHLEN]; 1.75 + snprintf(xpcomDLLPath, MAXPATHLEN, "%s%s", greDir, XPCOM_DLL); 1.76 + 1.77 + rv = XPCOMGlueStartup(xpcomDLLPath); 1.78 + 1.79 + if (NS_FAILED(rv)) { 1.80 + return rv; 1.81 + } 1.82 + 1.83 + rv = XPCOMGlueLoadXULFunctions(kXULFuncs); 1.84 + if (NS_FAILED(rv)) { 1.85 + return rv; 1.86 + } 1.87 + 1.88 + return rv; 1.89 +} 1.90 + 1.91 +int 1.92 +main(int argc, char **argv) 1.93 +{ 1.94 + NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; 1.95 + 1.96 + NSDictionary *args = [[NSUserDefaults standardUserDefaults] 1.97 + volatileDomainForName:NSArgumentDomain]; 1.98 + 1.99 + NSString *firefoxPath = nil; 1.100 + NSString *alternateBinaryID = nil; 1.101 + 1.102 + //this is our version, to be compared with the version of the binary we are asked to use 1.103 + NSString* myVersion = [NSString stringWithFormat:@"%s", NS_STRINGIFY(GRE_BUILDID)]; 1.104 + 1.105 + NSLog(@"MY WEBAPPRT BUILDID: %@", myVersion); 1.106 + 1.107 + 1.108 + //I need to look in our bundle first, before deciding what firefox binary to use 1.109 + NSBundle* myBundle = [NSBundle mainBundle]; 1.110 + NSString* myBundlePath = [myBundle bundlePath]; 1.111 + alternateBinaryID = [myBundle objectForInfoDictionaryKey:@"FirefoxBinary"]; 1.112 + NSLog(@"found override firefox binary: %@", alternateBinaryID); 1.113 + 1.114 + @try { 1.115 + //find a webapprt binary to launch with. throws an exception with error dialog if none found. 1.116 + firefoxPath = PathToWebRT(alternateBinaryID); 1.117 + NSLog(@"USING FIREFOX : %@", firefoxPath); 1.118 + 1.119 + NSString *myWebRTPath = [myBundle pathForAuxiliaryExecutable: @"webapprt"]; 1.120 + if (!myWebRTPath) { 1.121 + @throw MakeException(@"Missing Web Runtime Files", @"Cannot locate binary for this App"); 1.122 + } 1.123 + 1.124 + //GET FIREFOX BUILD ID 1.125 + NSString *firefoxINIFilePath = [NSString stringWithFormat:@"%@%s%s", firefoxPath, APP_CONTENTS_PATH, FXAPPINI_NAME]; 1.126 + nsINIParser ffparser; 1.127 + NSLog(@"Looking for firefox ini file here: %@", firefoxINIFilePath); 1.128 + 1.129 + if (NS_FAILED(ffparser.Init([firefoxINIFilePath UTF8String]))) { 1.130 + NSLog(@"Unable to locate Firefox application.ini"); 1.131 + @throw MakeException(@"Error", @"Unable to parse environment files for application startup"); 1.132 + } 1.133 + 1.134 + char ffVersChars[MAXPATHLEN]; 1.135 + if (NS_FAILED(ffparser.GetString("App", "BuildID", ffVersChars, MAXPATHLEN))) { 1.136 + NSLog(@"Unable to retrieve Firefox BuildID"); 1.137 + @throw MakeException(@"Error", @"Unable to determine Firefox version."); 1.138 + } 1.139 + NSString* firefoxVersion = [NSString stringWithFormat:@"%s", ffVersChars]; 1.140 + 1.141 + NSLog(@"FIREFOX WEBAPPRT BUILDID: %@", firefoxVersion); 1.142 + //GOT FIREFOX BUILD ID 1.143 + 1.144 + //COMPARE MY BUILD ID AND FIREFOX BUILD ID 1.145 + if ([myVersion compare: firefoxVersion] != NSOrderedSame) { 1.146 + //we are going to assume that if they are different, we need to re-copy the webapprt, regardless of whether 1.147 + // it is newer or older. If we don't find a webapprt, then the current Firefox must not be new enough to run webapps. 1.148 + NSLog(@"### This Application has an old webrt. Updating it."); 1.149 + NSLog(@"### My webapprt path: %@", myWebRTPath); 1.150 + 1.151 + NSFileManager* fileClerk = [[NSFileManager alloc] init]; 1.152 + NSError *errorDesc = nil; 1.153 + 1.154 + //we know the firefox path, so copy the new webapprt here 1.155 + NSString *newWebRTPath = [NSString stringWithFormat: @"%@%s%s", firefoxPath, APP_CONTENTS_PATH, WEBAPPRT_EXECUTABLE]; 1.156 + NSLog(@"### Firefox webapprt path: %@", newWebRTPath); 1.157 + if (![fileClerk fileExistsAtPath:newWebRTPath]) { 1.158 + NSString* msg = [NSString stringWithFormat: @"This version of Firefox (%@) cannot run web applications, because it is not recent enough or damaged", firefoxVersion]; 1.159 + @throw MakeException(@"Missing Web Runtime Files", msg); 1.160 + } 1.161 + 1.162 + [fileClerk removeItemAtPath: myWebRTPath error: &errorDesc]; 1.163 + if (errorDesc != nil) { 1.164 + NSLog(@"failed to unlink old binary file at path: %@ with error: %@", myWebRTPath, errorDesc); 1.165 + @throw MakeException(@"Unable To Update", @"Failed preparation for Web Runtime update"); 1.166 + } 1.167 + 1.168 + [fileClerk copyItemAtPath: newWebRTPath toPath: myWebRTPath error: &errorDesc]; 1.169 + [fileClerk release]; 1.170 + if (errorDesc != nil) { 1.171 + NSLog(@"failed to copy new webrt file: %@", errorDesc); 1.172 + @throw MakeException(@"Unable To Update Web Runtime", @"Failed to update Web Runtime"); 1.173 + } else { 1.174 + NSLog(@"### Successfully updated webapprt, relaunching"); 1.175 + } 1.176 + 1.177 + //execv the new binary, and ride off into the sunset 1.178 + ExecNewBinary(myWebRTPath); 1.179 + 1.180 + } else { 1.181 + //we are ready to load XUL and such, and go go go 1.182 + 1.183 + NSLog(@"This Application has the newest webrt. Launching!"); 1.184 + 1.185 + int result = 0; 1.186 + char rtINIPath[MAXPATHLEN]; 1.187 + 1.188 + // Set up our environment to know where webapp.ini was loaded from. 1.189 + char appEnv[MAXPATHLEN]; 1.190 + snprintf(appEnv, MAXPATHLEN, "%s%s%s", [myBundlePath UTF8String], APP_CONTENTS_PATH, WEBAPPINI_NAME); 1.191 + if (setenv("XUL_APP_FILE", appEnv, 1)) { 1.192 + NSLog(@"Couldn't set XUL_APP_FILE to: %s", appEnv); 1.193 + @throw MakeException(@"Error", @"Unable to set Web Runtime INI file."); 1.194 + } 1.195 + NSLog(@"Set XUL_APP_FILE to: %s", appEnv); 1.196 + 1.197 + //CONSTRUCT GREDIR AND CALL XPCOMGLUE WITH IT 1.198 + char greDir[MAXPATHLEN]; 1.199 + snprintf(greDir, MAXPATHLEN, "%s%s", [firefoxPath UTF8String], APP_CONTENTS_PATH); 1.200 + if (!NS_SUCCEEDED(AttemptGRELoad(greDir))) { 1.201 + @throw MakeException(@"Error", @"Unable to load XUL files for application startup"); 1.202 + } 1.203 + 1.204 + // NOTE: The GRE has successfully loaded, so we can use XPCOM now 1.205 + 1.206 + NS_LogInit(); 1.207 + { // Scope for any XPCOM stuff we create 1.208 + 1.209 + // Get the path to the runtime directory. 1.210 + char rtDir[MAXPATHLEN]; 1.211 + snprintf(rtDir, MAXPATHLEN, "%s%s%s", [firefoxPath UTF8String], APP_CONTENTS_PATH, WEBAPPRT_PATH); 1.212 + 1.213 + // Get the path to the runtime's INI file. This is in the runtime 1.214 + // directory. 1.215 + snprintf(rtINIPath, MAXPATHLEN, "%s%s%s%s", [firefoxPath UTF8String], APP_CONTENTS_PATH, WEBAPPRT_PATH, WEBRTINI_NAME); 1.216 + NSLog(@"WebappRT application.ini path: %s", rtINIPath); 1.217 + 1.218 + // Load the runtime's INI from its path. 1.219 + nsCOMPtr<nsIFile> rtINI; 1.220 + if (NS_FAILED(XRE_GetFileFromPath(rtINIPath, getter_AddRefs(rtINI)))) { 1.221 + NSLog(@"Runtime INI path not recognized: '%s'\n", rtINIPath); 1.222 + @throw MakeException(@"Error", @"Incorrect path to base INI file."); 1.223 + } 1.224 + 1.225 + bool exists; 1.226 + nsresult rv = rtINI->Exists(&exists); 1.227 + if (NS_FAILED(rv) || !exists) { 1.228 + NSString* msg = [NSString stringWithFormat: @"This copy of Firefox (%@) cannot run Apps, because it is missing the Web Runtime's application.ini file", firefoxVersion]; 1.229 + @throw MakeException(@"Missing Web Runtime application.ini", msg); 1.230 + } 1.231 + 1.232 + nsXREAppData *webShellAppData; 1.233 + if (NS_FAILED(XRE_CreateAppData(rtINI, &webShellAppData))) { 1.234 + NSLog(@"Couldn't read WebappRT application.ini: %s", rtINIPath); 1.235 + @throw MakeException(@"Error", @"Unable to parse base INI file."); 1.236 + } 1.237 + 1.238 + NSString *profile = [args objectForKey:@"profile"]; 1.239 + if (profile) { 1.240 + NSLog(@"Profile specified with -profile: %@", profile); 1.241 + } 1.242 + else { 1.243 + nsINIParser parser; 1.244 + if (NS_FAILED(parser.Init(appEnv))) { 1.245 + NSLog(@"%s was not found\n", appEnv); 1.246 + @throw MakeException(@"Error", @"Unable to parse environment files for application startup"); 1.247 + } 1.248 + char profile[MAXPATHLEN]; 1.249 + if (NS_FAILED(parser.GetString("Webapp", "Profile", profile, MAXPATHLEN))) { 1.250 + NSLog(@"Unable to retrieve profile from App INI file"); 1.251 + @throw MakeException(@"Error", @"Unable to retrieve installation profile."); 1.252 + } 1.253 + NSLog(@"setting app profile: %s", profile); 1.254 + SetAllocatedString(webShellAppData->profile, profile); 1.255 + } 1.256 + 1.257 + nsCOMPtr<nsIFile> directory; 1.258 + if (NS_FAILED(XRE_GetFileFromPath(rtDir, getter_AddRefs(directory)))) { 1.259 + NSLog(@"Unable to open app dir"); 1.260 + @throw MakeException(@"Error", @"Unable to open App directory."); 1.261 + } 1.262 + 1.263 + nsCOMPtr<nsIFile> xreDir; 1.264 + if (NS_FAILED(XRE_GetFileFromPath(greDir, getter_AddRefs(xreDir)))) { 1.265 + NSLog(@"Unable to open XRE dir"); 1.266 + @throw MakeException(@"Error", @"Unable to open App XRE directory."); 1.267 + } 1.268 + 1.269 + xreDir.forget(&webShellAppData->xreDirectory); 1.270 + NS_IF_RELEASE(webShellAppData->directory); 1.271 + directory.forget(&webShellAppData->directory); 1.272 + 1.273 + // There is only XUL. 1.274 + result = XRE_main(argc, argv, webShellAppData, 0); 1.275 + 1.276 + XRE_FreeAppData(webShellAppData); 1.277 + } 1.278 + NS_LogTerm(); 1.279 + 1.280 + return result; 1.281 + } 1.282 + 1.283 + } 1.284 + @catch (NSException *e) { 1.285 + NSLog(@"got exception: %@", e); 1.286 + DisplayErrorAlert([e name], [e reason]); 1.287 + } 1.288 + @finally { 1.289 + [pool drain]; 1.290 + } 1.291 + return 0; 1.292 +} //end main 1.293 + 1.294 + 1.295 +NSException* 1.296 +MakeException(NSString* name, NSString* message) 1.297 +{ 1.298 + NSException* myException = [NSException 1.299 + exceptionWithName:name 1.300 + reason:message 1.301 + userInfo:nil]; 1.302 + return myException; 1.303 +} 1.304 + 1.305 +void 1.306 +DisplayErrorAlert(NSString* title, NSString* message) 1.307 +{ 1.308 + CFUserNotificationDisplayNotice(0, kCFUserNotificationNoteAlertLevel, 1.309 + NULL, NULL, NULL, 1.310 + (CFStringRef)title, 1.311 + (CFStringRef)message, 1.312 + CFSTR("Quit") 1.313 + ); 1.314 +} 1.315 + 1.316 +/* Find the currently installed Firefox, if any, and return 1.317 + * an absolute path to it. may return nil */ 1.318 +NSString 1.319 +*PathToWebRT(NSString* alternateBinaryID) 1.320 +{ 1.321 + //default is firefox 1.322 + NSString *binaryPath = nil; 1.323 + 1.324 + // We're run from the Firefox bundle during WebappRT chrome and content tests. 1.325 + NSString *myBundlePath = [[NSBundle mainBundle] bundlePath]; 1.326 + NSString *fxPath = [NSString stringWithFormat:@"%@%sfirefox-bin", 1.327 + myBundlePath, APP_CONTENTS_PATH]; 1.328 + if ([[NSFileManager defaultManager] fileExistsAtPath:fxPath]) { 1.329 + return myBundlePath; 1.330 + } 1.331 + 1.332 + //we look for these flavors of Firefox, in this order 1.333 + NSArray* launchBinarySearchList = [NSArray arrayWithObjects: @"org.mozilla.nightly", 1.334 + @"org.mozilla.aurora", 1.335 + @"org.mozilla.firefox", nil]; 1.336 + 1.337 + // If they provided a binary ID, use that. 1.338 + if (alternateBinaryID != nil && ([alternateBinaryID length] > 0)) { 1.339 + binaryPath = [[NSWorkspace sharedWorkspace] absolutePathForAppBundleWithIdentifier:alternateBinaryID]; 1.340 + if (binaryPath && [binaryPath length] > 0) { 1.341 + return binaryPath; 1.342 + } 1.343 + } 1.344 + 1.345 + //No override found, loop through the various flavors of firefox we have 1.346 + for (NSString* signature in launchBinarySearchList) { 1.347 + NSLog(@"SEARCHING for webapprt, trying: %@", signature); 1.348 + binaryPath = [[NSWorkspace sharedWorkspace] absolutePathForAppBundleWithIdentifier:signature]; 1.349 + if (binaryPath && [binaryPath length] > 0) { 1.350 + return binaryPath; 1.351 + } 1.352 + } 1.353 + 1.354 + NSLog(@"unable to find a valid webrt path"); 1.355 + @throw MakeException(@"This App requires that Firefox version 16 or above is installed.", @"Firefox 16+ has not been detected."); 1.356 + 1.357 + return nil; 1.358 +} 1.359 + 1.360 +void 1.361 +ExecNewBinary(NSString* launchPath) 1.362 +{ 1.363 + NSLog(@" launching webrt at path: %@\n", launchPath); 1.364 + 1.365 + const char *const newargv[] = {[launchPath UTF8String], NULL}; 1.366 + 1.367 + NSLog(@"COMMAND LINE: '%@ %s'", launchPath, newargv[0]); 1.368 + execv([launchPath UTF8String], (char **)newargv); 1.369 +}