Have launch working on iOS without Menu
Project: http://git-wip-us.apache.org/repos/asf/cordova-app-harness/repo Commit: http://git-wip-us.apache.org/repos/asf/cordova-app-harness/commit/d172011d Tree: http://git-wip-us.apache.org/repos/asf/cordova-app-harness/tree/d172011d Diff: http://git-wip-us.apache.org/repos/asf/cordova-app-harness/diff/d172011d Branch: refs/heads/master Commit: d172011de59aae1f7ff8605e476c7489997e0b60 Parents: 8ed807c Author: Andrew Grieve <[email protected]> Authored: Tue Oct 15 22:58:50 2013 -0400 Committer: Andrew Grieve <[email protected]> Committed: Wed Oct 23 21:55:17 2013 -0400 ---------------------------------------------------------------------- AppBundle/appBundle.js | 8 ++ AppBundle/src/ios/AppBundle.m | 214 +++++++++++++++-------------- createproject.sh | 2 +- www/cdvah_js/AppBundleAlias.js | 89 +++++------- www/cdvah_js/AppsService.js | 60 ++------ www/cdvah_js/ListCtrl.js | 23 +--- www/cdvah_js/ServeCordovaJSHandler.js | 50 ++++--- 7 files changed, 198 insertions(+), 248 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/cordova-app-harness/blob/d172011d/AppBundle/appBundle.js ---------------------------------------------------------------------- diff --git a/AppBundle/appBundle.js b/AppBundle/appBundle.js index cde9ce3..80a57b8 100644 --- a/AppBundle/appBundle.js +++ b/AppBundle/appBundle.js @@ -30,6 +30,14 @@ exports.addAlias = function(sourceUriMatchRegex, sourceUriReplaceRegex, replaceS exec(win, fail, 'AppBundle', 'addAlias', [sourceUriMatchRegex, sourceUriReplaceRegex, replaceString, redirectToReplacedUrl]); }; +exports.setResetUrl = function(urlRegex, callback) { + exec(callback, null, 'AppBundle', 'setResetUrl', [urlRegex]); +}; + +exports.injectJsForUrl = function(urlRegex, jsSnippet, callback) { + exec(callback, null, 'AppBundle', 'injectJs', [urlRegex, jsSnippet]); +}; + exports.clearAllAliases = function(callback){ exec(callback, null, 'AppBundle', 'clearAllAliases', []); }; http://git-wip-us.apache.org/repos/asf/cordova-app-harness/blob/d172011d/AppBundle/src/ios/AppBundle.m ---------------------------------------------------------------------- diff --git a/AppBundle/src/ios/AppBundle.m b/AppBundle/src/ios/AppBundle.m index 3106531..2f7b2e6 100644 --- a/AppBundle/src/ios/AppBundle.m +++ b/AppBundle/src/ios/AppBundle.m @@ -22,37 +22,40 @@ @class RouteParams; static NSString* const kAppBundlePrefix = @"app-bundle:///"; -static NSString* gPathPrefix; -static UIWebView* gWebView; -static NSMutableArray* gRerouteParams; -static RouteParams* gAppBundleParams; +static NSString* gPathPrefix = nil; +static UIWebView* gWebView = nil; +static NSMutableArray* gRerouteParams = nil; +static RouteParams* gResetUrlParams = nil; @interface AppBundle : CDVPlugin {} - (void)addAlias:(CDVInvokedUrlCommand*)command; - (void)clearAllAliases:(CDVInvokedUrlCommand*)command; @end -@interface AppBundle() -{} -- (void) resetMap; +@interface AppBundle() {} +- (void)resetMap; @end -@interface RouteParams : NSObject -{ -@public +@interface RouteParams : NSObject { + @public NSRegularExpression* _matchRegex; NSRegularExpression* _replaceRegex; NSString* _replacer; BOOL _redirectToReplacedUrl; + NSString* _jsToInject; +} +@end + +@implementation RouteParams +- (BOOL)matches:(NSString*)uriString { + NSRange wholeStringRange = NSMakeRange(0, [uriString length]); + NSInteger numMatches = [_matchRegex numberOfMatchesInString:uriString options:0 range:wholeStringRange]; + return numMatches > 0; } -- (RouteParams*)initWithMatchRegex:(NSRegularExpression*)matchRegex replaceRegex:(NSRegularExpression*)replaceRegex replacer:(NSString*)replacer shouldRedirect:(BOOL)redirectToReplacedUrl; @end @interface AppBundleURLProtocol : NSURLProtocol -+ (RouteParams*) getChosenParams:(NSString*)uriString; -- (void)issueNSURLResponseForFile:(NSString*)file; -- (void)issueRedirectResponseForFile:(NSString*)file; -- (void)issueNotFoundResponse; ++ (RouteParams*)getChosenParams:(NSString*)uriString forInjection:(BOOL)forInjection; @end @@ -60,48 +63,60 @@ static RouteParams* gAppBundleParams; @implementation AppBundle -- (void)resetMap -{ +- (void)resetMap { NSError *error = NULL; NSRegularExpression* bundleMatchRegex = [NSRegularExpression regularExpressionWithPattern:@"^app-bundle:///.*" options:0 error:&error]; NSRegularExpression* bundleReplaceRegex = [NSRegularExpression regularExpressionWithPattern:@"^app-bundle:///" options:0 error:&error]; - gAppBundleParams = [[RouteParams alloc] initWithMatchRegex:bundleMatchRegex replaceRegex:bundleReplaceRegex replacer:gPathPrefix shouldRedirect:YES]; + RouteParams* params = [[RouteParams alloc] init]; + params->_matchRegex = bundleMatchRegex; + params->_replaceRegex = bundleReplaceRegex; + params->_replacer = gPathPrefix; + params->_redirectToReplacedUrl = YES; gRerouteParams = [[NSMutableArray alloc] init]; - [gRerouteParams addObject:gAppBundleParams]; -} - -- (CDVPlugin*)initWithWebView:(UIWebView*)theWebView -{ - self = [super initWithWebView:theWebView]; - gWebView = theWebView; - if (self) { - [NSURLProtocol registerClass:[AppBundleURLProtocol class]]; - gPathPrefix = [[NSBundle mainBundle] pathForResource:@"cordova.js" ofType:@"" inDirectory:@"www"]; - gPathPrefix = [gPathPrefix stringByDeletingLastPathComponent]; - gPathPrefix = [[NSURL fileURLWithPath:gPathPrefix] absoluteString]; - [self resetMap]; + [gRerouteParams addObject:params]; +} + +- (void)pluginInitialize { + + [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(pageDidLoad) name:CDVPageDidLoadNotification object:self.webView]; + gWebView = self.webView; + [NSURLProtocol registerClass:[AppBundleURLProtocol class]]; + gPathPrefix = [[NSBundle mainBundle] pathForResource:@"cordova.js" ofType:@"" inDirectory:@"www"]; + gPathPrefix = [gPathPrefix stringByDeletingLastPathComponent]; + gPathPrefix = [[NSURL fileURLWithPath:gPathPrefix] absoluteString]; + [self resetMap]; +} + +- (void)pageDidLoad { + if (gResetUrlParams != nil) { + NSString* url = [self.webView stringByEvaluatingJavaScriptFromString:@"location.href.replace(/#.*/, '')"]; + if ([gResetUrlParams matches:url]) { + [self resetMap]; + } } - return self; } -- (void)addAlias:(CDVInvokedUrlCommand*)command -{ + +- (void)addAlias:(CDVInvokedUrlCommand*)command { CDVPluginResult* pluginResult = nil; NSError* error = nil; - NSString* sourceUrlMatchRegexString = [[command.arguments objectAtIndex:0] stringByReplacingOccurrencesOfString:@"{BUNDLE_WWW}" withString:[NSRegularExpression escapedPatternForString:gPathPrefix]]; + NSString* sourceUrlMatchRegexString = [[command argumentAtIndex:0] stringByReplacingOccurrencesOfString:@"{BUNDLE_WWW}" withString:[NSRegularExpression escapedPatternForString:gPathPrefix]]; NSRegularExpression* sourceUrlMatchRegex = [NSRegularExpression regularExpressionWithPattern:sourceUrlMatchRegexString options:0 error:&error]; if(error) { pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsString:@"Match regex is invalid"]; } else { - NSString* sourceUrlReplaceRegexString = [[command.arguments objectAtIndex:1] stringByReplacingOccurrencesOfString:@"{BUNDLE_WWW}" withString:[NSRegularExpression escapedPatternForString:gPathPrefix]]; + NSString* sourceUrlReplaceRegexString = [[command argumentAtIndex:1] stringByReplacingOccurrencesOfString:@"{BUNDLE_WWW}" withString:[NSRegularExpression escapedPatternForString:gPathPrefix]]; NSRegularExpression* sourceUrlReplaceRegex = [NSRegularExpression regularExpressionWithPattern:sourceUrlReplaceRegexString options:0 error:&error]; if(error) { pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsString:@"Replace regex is invalid"]; } else { - NSString* replaceString = [[command.arguments objectAtIndex:2] stringByReplacingOccurrencesOfString:@"{BUNDLE_WWW}" withString:gPathPrefix]; - BOOL redirectToReplacedUrl = [[command.arguments objectAtIndex:3] boolValue]; + NSString* replaceString = [[command argumentAtIndex:2] stringByReplacingOccurrencesOfString:@"{BUNDLE_WWW}" withString:gPathPrefix]; - RouteParams* params = [[RouteParams alloc] initWithMatchRegex:sourceUrlMatchRegex replaceRegex:sourceUrlReplaceRegex replacer:replaceString shouldRedirect:redirectToReplacedUrl]; + RouteParams* params = [[RouteParams alloc] init]; + params->_matchRegex = sourceUrlMatchRegex; + params->_replaceRegex = sourceUrlReplaceRegex; + params->_replacer = replaceString; + params->_redirectToReplacedUrl = [[command argumentAtIndex:3] boolValue]; [gRerouteParams addObject:params]; pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK]; } @@ -109,83 +124,68 @@ static RouteParams* gAppBundleParams; [self.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId]; } -- (void)clearAllAliases:(CDVInvokedUrlCommand*)command -{ +- (void)clearAllAliases:(CDVInvokedUrlCommand*)command { [self resetMap]; CDVPluginResult* pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK]; [self.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId]; } -@end -#pragma mark RouteParams - -@implementation RouteParams - -- (RouteParams*)initWithMatchRegex:(NSRegularExpression*)matchRegex replaceRegex:(NSRegularExpression*)replaceRegex replacer:(NSString*)replacer shouldRedirect:(BOOL)redirectToReplacedUrl -{ - self = [super init]; - if(self) - { - _matchRegex = matchRegex; - _replaceRegex = replaceRegex; - _replacer = replacer; - _redirectToReplacedUrl = redirectToReplacedUrl; - } - return self; +- (void)injectJs:(CDVInvokedUrlCommand*)command { + RouteParams* params = [[RouteParams alloc] init]; + params->_matchRegex = [NSRegularExpression regularExpressionWithPattern:[command argumentAtIndex:0] options:0 error:nil]; + params->_jsToInject = [command argumentAtIndex:1]; + [gRerouteParams addObject:params]; + [self.commandDelegate sendPluginResult:[CDVPluginResult resultWithStatus:CDVCommandStatus_OK] callbackId:command.callbackId]; } +- (void)setResetUrl:(CDVInvokedUrlCommand*)command { + RouteParams* params = [[RouteParams alloc] init]; + params->_matchRegex = [NSRegularExpression regularExpressionWithPattern:[command argumentAtIndex:0] options:0 error:nil]; + gResetUrlParams = params; + [self.commandDelegate sendPluginResult:[CDVPluginResult resultWithStatus:CDVCommandStatus_OK] callbackId:command.callbackId]; +} @end #pragma mark AppBundleURLProtocol @implementation AppBundleURLProtocol -+ (RouteParams*)getChosenParams:(NSString*)uriString -{ - NSRange wholeStringRange = NSMakeRange(0, [uriString length]); - for(RouteParams* param in gRerouteParams) { - NSInteger numMatches = [param->_matchRegex numberOfMatchesInString:uriString options:0 range:wholeStringRange]; - if (numMatches > 0) { ++ (RouteParams*)getChosenParams:(NSString*)uriString forInjection:(BOOL)forInjection { + for (RouteParams* param in gRerouteParams) { + if (forInjection != !!param->_jsToInject) { + continue; + } + if ([param matches:uriString]) { return param; } } return nil; } -+ (BOOL)canInitWithRequest:(NSURLRequest*)request -{ ++ (BOOL)canInitWithRequest:(NSURLRequest*)request { NSURL* url = [request URL]; NSString* urlString = [url absoluteString]; - RouteParams* params = [AppBundleURLProtocol getChosenParams:urlString]; + RouteParams* params = [AppBundleURLProtocol getChosenParams:urlString forInjection:NO]; return params != nil; } -+ (NSURLRequest*)canonicalRequestForRequest:(NSURLRequest*)request -{ ++ (NSURLRequest*)canonicalRequestForRequest:(NSURLRequest*)request { return request; } -- (void)issueNotFoundResponse -{ +- (void)issueNotFoundResponse { NSURL* url = [[self request] URL]; NSURLResponse *response = [[NSHTTPURLResponse alloc] initWithURL:url statusCode:404 HTTPVersion:@"HTTP/1.1" headerFields:@{}]; [[self client] URLProtocol:self didReceiveResponse:response cacheStoragePolicy:NSURLCacheStorageNotAllowed]; [[self client] URLProtocolDidFinishLoading:self]; } -- (void)issueNSURLResponseForFile:(NSString*)file -{ - NSURL* uri = [[self request] URL]; - NSString* uriString = [uri absoluteString]; - RouteParams* params = [AppBundleURLProtocol getChosenParams:uriString]; - NSRange wholeStringRange = NSMakeRange(0, [uriString length]); - NSString* newUrlString = [params->_replaceRegex stringByReplacingMatchesInString:uriString options:0 range:wholeStringRange withTemplate:params->_replacer]; - if ([newUrlString hasPrefix:@"file://"]) { - NSURL *newUrl = [NSURL URLWithString:newUrlString]; - NSString* path = [newUrl path]; +- (void)issueNSURLResponseForUrl:(NSURL*)url { + if ([[url scheme] isEqualToString:@"file"]) { + NSString* path = [url path]; FILE* fp = fopen([path UTF8String], "r"); if (fp) { - NSURLResponse *response = [[NSHTTPURLResponse alloc] initWithURL:uri statusCode:200 HTTPVersion:@"HTTP/1.1" headerFields:@{}]; + NSURLResponse *response = [[NSURLResponse alloc] initWithURL:url MIMEType:@"text/html" expectedContentLength:-1 textEncodingName:@"utf8"];//[[NSHTTPURLResponse alloc] initWithURL:url statusCode:200 HTTPVersion:@"HTTP/1.1" headerFields:@{}]; [[self client] URLProtocol:self didReceiveResponse:response cacheStoragePolicy:NSURLCacheStorageNotAllowed]; char* buf = malloc(32768); @@ -201,38 +201,44 @@ static RouteParams* gAppBundleParams; [self issueNotFoundResponse]; } } else { - NSLog(@"Cannot redirect to %@. You can only redirect to file: uri's", newUrlString); + NSLog(@"Cannot redirect to %@. You can only redirect to file: uri's", url); [self issueNotFoundResponse]; } } -- (void)issueRedirectResponseForFile:(NSString*)uriString -{ - RouteParams* params = [AppBundleURLProtocol getChosenParams:uriString]; - if(params != nil && params->_redirectToReplacedUrl) - { - if([gWebView isLoading]) { - [gWebView stopLoading]; - } - NSRange wholeStringRange = NSMakeRange(0, [uriString length]); - NSString* newUrlString = [params->_replaceRegex stringByReplacingMatchesInString:uriString options:0 range:wholeStringRange withTemplate:params->_replacer]; - NSURL *newUrl = [NSURL URLWithString:newUrlString]; - NSURLRequest *request = [NSURLRequest requestWithURL:newUrl]; - [gWebView loadRequest:request]; +- (void)issueRedirectResponseForUrl:(NSURL*)url { + if([gWebView isLoading]) { + [gWebView stopLoading]; } + NSURLRequest *request = [NSURLRequest requestWithURL:url]; + [gWebView loadRequest:request]; } -- (void)startLoading -{ - NSURL *url = [[self request] URL]; - NSString* urlString = [url absoluteString]; - NSURL* mainUrl = [[self request] mainDocumentURL]; - NSString* mainUrlString = [mainUrl absoluteString]; +- (void)issueTopLevelRedirect:(NSURL*)url origURL:(NSURL*)origURL { + if([gWebView isLoading]) { + [gWebView stopLoading]; + } + [gWebView loadData:[NSData dataWithContentsOfURL:url] MIMEType:@"text/html" textEncodingName:@"utf8" baseURL:origURL]; +} + +- (void)startLoading { + NSURLRequest* request = [self request]; + NSString* urlString = [[request URL] absoluteString]; + + RouteParams* params = [AppBundleURLProtocol getChosenParams:urlString forInjection:NO]; + NSRange wholeStringRange = NSMakeRange(0, [urlString length]); + NSString* newUrlString = [params->_replaceRegex stringByReplacingMatchesInString:urlString options:0 range:wholeStringRange withTemplate:params->_replacer]; + NSURL* newUrl = [NSURL URLWithString:newUrlString]; + + BOOL isTopLevelNavigation = [request.URL isEqual:request.mainDocumentURL]; - if([mainUrlString isEqualToString:urlString]){ - [self issueRedirectResponseForFile:urlString]; + // iOS 6+ just gives "Frame load interrupted" when you try and feed it data via a URLProtocol. + if (isTopLevelNavigation) { + [self issueTopLevelRedirect:newUrl origURL:[request URL]]; + } else if(params->_redirectToReplacedUrl) { + [self issueRedirectResponseForUrl:newUrl]; } else { - [self issueNSURLResponseForFile:urlString]; + [self issueNSURLResponseForUrl:newUrl]; } } http://git-wip-us.apache.org/repos/asf/cordova-app-harness/blob/d172011d/createproject.sh ---------------------------------------------------------------------- diff --git a/createproject.sh b/createproject.sh index 435ef9d..a5512d5 100755 --- a/createproject.sh +++ b/createproject.sh @@ -39,7 +39,7 @@ ln -s ../www www $CORDOVA platform add ios ../../cordova-ios/bin/update_cordova_subproject platforms/ios/CordovaAppHarness.xcodeproj -$CORDOVA plugin add ../../../mobile_chrome_apps/AppBundle +$CORDOVA plugin add ../AppBundle $CORDOVA plugin add ../../../mobile_chrome_apps/zip $CORDOVA plugin add ../../../BarcodeScanner # https://github.com/wildabeast/BarcodeScanner.git $CORDOVA plugin add ../../cordova-plugin-file http://git-wip-us.apache.org/repos/asf/cordova-app-harness/blob/d172011d/www/cdvah_js/AppBundleAlias.js ---------------------------------------------------------------------- diff --git a/www/cdvah_js/AppBundleAlias.js b/www/cdvah_js/AppBundleAlias.js index 5e1bbb8..bad75f2 100644 --- a/www/cdvah_js/AppBundleAlias.js +++ b/www/cdvah_js/AppBundleAlias.js @@ -1,70 +1,51 @@ -(function(){ +(function() { "use strict"; /* global myApp */ - myApp.run(["AppsService", function(AppsService){ + myApp.factory("AppBundle", ['$window', function($window) { // URI aliasing : the ability to launch an app in the harness, query the document.location and get the same location as would have been got if you run the app separately // Without URI aliasing, document.location in the harness would give something like file:///APP_HARNESS_INSTALLED_APPS_LOCATION/www/index.html function aliasUri(sourceUriMatchRegex, sourceUriReplaceRegex, replaceString, redirectToReplacedUrl){ var deferred = Q.defer(); - var appBundle = cordova.require("AppBundle.AppBundle"); - try { - appBundle.addAlias(sourceUriMatchRegex, sourceUriReplaceRegex, replaceString, redirectToReplacedUrl, function(succeded){ - if(succeded){ - deferred.resolve(); - } else { - deferred.reject(new Error("Unable to set up uri aliasing")); - } - }); - } catch(e) { - deferred.reject(new Error(e)); - } finally { - return deferred.promise; - } - } - - function replaceCharWithString(string, searchChar, replaceString){ - var ret = ""; - for(var i = 0; i < string.length; i++){ - if(string[i] === searchChar){ - ret += replaceString; + cordova.plugins.appBundle.addAlias(sourceUriMatchRegex, sourceUriReplaceRegex, replaceString, redirectToReplacedUrl, function(succeded) { + if (succeded){ + deferred.resolve(); } else { - ret += string[i]; + deferred.reject(new Error("Unable to set up uri aliasing")); } - } - return ret; + }); + return deferred.promise; } - function getRegex(string){ - string = replaceCharWithString(string, "\\", "\\\\"); - string = replaceCharWithString(string, "[", "\\["); - string = replaceCharWithString(string, "^", "\\^"); - string = replaceCharWithString(string, "$", "\\$"); - string = replaceCharWithString(string, ".", "\\."); - string = replaceCharWithString(string, "|", "\\|"); - string = replaceCharWithString(string, "?", "\\?"); - string = replaceCharWithString(string, "*", "\\*"); - string = replaceCharWithString(string, "+", "\\+"); - string = replaceCharWithString(string, "(", "\\("); - string = replaceCharWithString(string, ")", "\\)"); - return string; + function setResetUrl(url) { + var deferred = Q.defer(); + cordova.plugins.appBundle.setResetUrl(url, deferred.resolve); + return deferred.promise; } - AppsService.addPreLaunchHook(function(appEntry, appInstallLocation, wwwLocation) { - wwwLocation += (wwwLocation.charAt(wwwLocation.length - 1) === "/")? "" : "/"; - appInstallLocation += (appInstallLocation.charAt(appInstallLocation.length - 1) === "/")? "" : "/"; - //Make any direct references to the bundle paths such as file:///android_asset point to the installed location without redirecting - //{BUNDLE_WWW} in the regex is automatically replaced by the appBundle component - return aliasUri("^{BUNDLE_WWW}.*", "^{BUNDLE_WWW}", wwwLocation, false /* redirect */) - .then(function(){ - //For cordova serve apps, we additionally have to redirect requests to the original cordova.js to a modified cordova.js we have locally - if(appEntry.Source === "serve"){ - var regexWWWLoc = getRegex(wwwLocation); - var regex = "^" + regexWWWLoc + "cordova.js(\\?|#.*)?"; - return aliasUri(regex, regex, appInstallLocation + "cordova.js", false); - } - }); - }, 500 /* Give it a priority */); + function injectJsForUrl(url, js) { + var deferred = Q.defer(); + cordova.plugins.appBundle.injectJsForUrl(url, js, deferred.resolve); + return deferred.promise; + } + + function reset() { + var deferred = Q.defer(); + cordova.plugins.appBundle.clearAllAliases(deferred.resolve); + return deferred.promise; + } + + function escapeRegExp(str) { + return str.replace(/[-\[\]\/{}()*+?.\\^$|]/g, '\\$&'); + } + + return { + aliasUri: aliasUri, + reset: reset, + setResetUrl: setResetUrl, + injectJsForUrl: injectJsForUrl + }; + }]); })(); http://git-wip-us.apache.org/repos/asf/cordova-app-harness/blob/d172011d/www/cdvah_js/AppsService.js ---------------------------------------------------------------------- diff --git a/www/cdvah_js/AppsService.js b/www/cdvah_js/AppsService.js index cd565d0..d5b53ec 100644 --- a/www/cdvah_js/AppsService.js +++ b/www/cdvah_js/AppsService.js @@ -6,8 +6,6 @@ var platformId = cordova.platformId; // Map of type -> handler. var _installHandlerFactories = {}; - // functions to run before launching an app - var preLaunchHooks = []; var _installHandlers = null; var _lastLaunchedAppId = null; @@ -71,14 +69,13 @@ return (path.match(/^[a-z0-9+.-]+:/) != null); } - function getAppStartPageFromConfig(configFile, appBaseLocation) { - appBaseLocation += (appBaseLocation.charAt(appBaseLocation.length - 1) === "/")? "" : "/"; + function getAppStartPageFromConfig(configFile) { return ResourcesLoader.readFileContents(configFile) .then(function(contents){ if(!contents) { throw new Error("Config file is empty. Unable to find a start page for your app."); } else { - var startLocation = appBaseLocation + "index.html"; + var startLocation = 'index.html'; var parser = new DOMParser(); var xmlDoc = parser.parseFromString(contents, "text/xml"); var els = xmlDoc.getElementsByTagName("content"); @@ -88,13 +85,8 @@ for(var i = els.length - 1; i >= 0; i--) { var el = els[i]; var srcValue = el.getAttribute("src"); - if(srcValue) { - if(isUrlAbsolute(srcValue)) { - startLocation = srcValue; - } else { - srcValue = srcValue.charAt(0) === "/" ? srcValue.substring(1) : srcValue; - startLocation = appBaseLocation + srcValue; - } + if (srcValue) { + startLocation = srcValue; break; } } @@ -118,25 +110,14 @@ appPaths.platformWWWLocation = installPath + "/www/"; return Q.fcall(function(){ - return getAppStartPageFromConfig(appPaths.configLocation, appPaths.platformWWWLocation); + return getAppStartPageFromConfig(appPaths.configLocation); }) - .then(function(startLocation){ - appPaths.startLocation = startLocation; + .then(function(startLocation) { + appPaths.startLocation = cordova.require('cordova/urlutil').makeAbsolute(startLocation); return appPaths; }); } - function insertObjectAtPriority(objArr, handler, priority){ - var i = 0; - var objToInsert = { "priority" : priority, "handler" : handler }; - for(i = 0; i < objArr.length; i++){ - if(objArr[i].priority > objToInsert.priority) { - break; - } - } - objArr.splice(i, 0, objToInsert); - } - return { // return promise with the array of apps getAppList : function() { @@ -147,25 +128,17 @@ }, launchApp : function(handler) { - var appEntry; - var startLocation; _lastLaunchedAppId = handler.appId; return writeAppsJson() .then(function(){ return getAppPathsForHandler(handler); }) .then(function(appPaths){ - startLocation = appPaths.startLocation; - var result = Q.resolve(0 /* dummy value to set up chain */); - preLaunchHooks.forEach(function (currHook) { - result = result.then(function(){ - return currHook.handler(appEntry, appPaths.appInstallLocation, appPaths.platformWWWLocation); - }); + var installPath = INSTALL_DIRECTORY + '/' + handler.appId; + return handler.prepareForLaunch(installPath, appPaths.startLocation) + .then(function() { + window.location = appPaths.startLocation; }); - return result; - }) - .then(function() { - window.location = startLocation; }); }, @@ -199,20 +172,13 @@ updateApp : function(handler){ return Q.fcall(function() { var installPath = INSTALL_DIRECTORY + '/' + handler.appId; - return handler.updateApp(installPath); + return handler.updateApp(installPath) + .then(writeAppsJson); }); }, registerInstallHandlerFactory : function(handlerFactory) { _installHandlerFactories[handlerFactory.type] = handlerFactory; - }, - - addPreLaunchHook : function(handler, priority){ - if(!priority) { - // Assign a default priority - priority = 500; - } - insertObjectAtPriority(preLaunchHooks, handler, priority); } }; }]); http://git-wip-us.apache.org/repos/asf/cordova-app-harness/blob/d172011d/www/cdvah_js/ListCtrl.js ---------------------------------------------------------------------- diff --git a/www/cdvah_js/ListCtrl.js b/www/cdvah_js/ListCtrl.js index 928fb48..566d134 100644 --- a/www/cdvah_js/ListCtrl.js +++ b/www/cdvah_js/ListCtrl.js @@ -1,34 +1,15 @@ (function(){ "use strict"; /* global myApp */ - myApp.controller("ListCtrl", [ "notifier", "$rootScope", "$scope", "$location", "$routeParams", "AppsService", function (notifier, $rootScope, $scope, $location, $routeParams, AppsService) { + myApp.controller("ListCtrl", [ "AppBundle", "notifier", "$rootScope", "$scope", "$routeParams", "AppsService", function (AppBundle, notifier, $rootScope, $scope, $routeParams, AppsService) { $scope.appList = []; $rootScope.appTitle = 'Cordova App Harness'; initialise(); - function clearAppBundleAliases(){ - var deferred = Q.defer(); - var appBundle = cordova.require("AppBundle.AppBundle"); - - try { - appBundle.clearAllAliases(function(succeded){ - if(succeded){ - deferred.resolve(); - } else { - deferred.reject(new Error("Unable to clear old url aliases. Please restart App Harness.")); - } - }); - } catch(e) { - deferred.reject(new Error(e)); - } finally { - return deferred.promise; - } - } - function initialise() { //if we are navigating here after running an app, reset any aliases set for the app by app harness or any aliases setup by the previous app - return clearAppBundleAliases() + return AppBundle.reset() .then(function(){ if($routeParams.lastLaunched) { return AppsService.getLastRunApp() http://git-wip-us.apache.org/repos/asf/cordova-app-harness/blob/d172011d/www/cdvah_js/ServeCordovaJSHandler.js ---------------------------------------------------------------------- diff --git a/www/cdvah_js/ServeCordovaJSHandler.js b/www/cdvah_js/ServeCordovaJSHandler.js index eb8beed..5026de6 100644 --- a/www/cdvah_js/ServeCordovaJSHandler.js +++ b/www/cdvah_js/ServeCordovaJSHandler.js @@ -1,28 +1,9 @@ (function(){ "use strict"; /* global myApp */ - myApp.run(["AppsService", "ResourcesLoader", "ContextMenuInjectScript", function(AppsService, ResourcesLoader, ContextMenuInjectScript){ + myApp.run(["$location", "AppBundle", "AppsService", "ResourcesLoader", "ContextMenuInjectScript", function($location, AppBundle, AppsService, ResourcesLoader, ContextMenuInjectScript){ var platformId = cordova.platformId; - AppsService.addPreLaunchHook(function(appEntry, appInstallLocation , wwwLocation) { - if(appEntry.Source === "serve"){ - return Q.fcall(function(){ - // We can't inject the context menu script into cordova.js remotely - // So we create a local copy of the cordova.js used by the server, - // append the context menu script (requests for cordova.js are routed to it) - wwwLocation += (wwwLocation.charAt(wwwLocation.length - 1) === "/")? "" : "/"; - var cordovaJSPath = wwwLocation + "cordova.js"; - return ResourcesLoader.xhrGet(cordovaJSPath); - }) - .then(function(xhr){ - var dataToAppend = ContextMenuInjectScript.getInjectString(appEntry.Name); - var completeText = xhr.responseText + dataToAppend; - appInstallLocation += (appInstallLocation.charAt(appInstallLocation.length - 1) === "/")? "" : "/"; - return ResourcesLoader.writeFileContents(appInstallLocation + "cordova.js", completeText); - }); - } - }, 250 /* Give it a priority */); - function ServeHandler(url, appId) { this.url = url; this.appId = appId || ''; @@ -56,6 +37,10 @@ var files = self._cachedProjectJson['wwwFileList']; var i = 0; function downloadNext() { + // Don't download cordova.js. We want to use the version bundled with the harness. + if (/\/cordova(?:_plugins)?.js$/.exec(files[i])) { + ++i; + } if (!files[i]) { self.lastUpdated = new Date(); return; @@ -69,12 +54,35 @@ } return ResourcesLoader.ensureDirectoryExists(installPath + '/config.xml') .then(function() { - return ResourcesLoader.writeFileContents(installPath + '/config.xml', self._cachedConfigXml); + return ResourcesLoader.writeFileContents(installPath + '/config.xml', self._cachedConfigXml) }) .then(downloadNext); }); }; + ServeHandler.prototype.prepareForLaunch = function(installPath, launchUrl) { + var harnessUrl = cordova.require('cordova/urlutil').makeAbsolute(location.pathname); + var harnessDir = harnessUrl.replace(/\/[^\/]*$/, ''); + var installUrl = cordova.require('cordova/urlutil').makeAbsolute(installPath); + var injectString = ContextMenuInjectScript.getInjectString() + // Inject the context menu script for all pages except the harness menu. + return AppBundle.injectJsForUrl('^(?!' + harnessUrl + ')', injectString) + .then(function() { + // Allow navigations back to the menu. + return AppBundle.setResetUrl('^' + harnessUrl); + }) + .then(function() { + // Make any references to www/ point to the app's install location. + return AppBundle.aliasUri('^' + harnessDir, '^' + harnessDir, installUrl + '/www', false /* redirect */); + }) + .then(function() { + return AppBundle.aliasUri('/cordova\\.js.*', '.+', harnessDir + '/cordova.js', false /* redirect */); + }) + .then(function() { + return AppBundle.aliasUri('/cordova_plugins\\.js.*', '.+', harnessDir + '/cordova_plugins.js', false /* redirect */); + }); + }; + function createFromUrl(url) { // Strip platform and trailing slash if they exist. url = url.replace(/\/$/, '').replace(new RegExp(platformId + '$'), '').replace(/\/$/, '');
