Updated Branches: refs/heads/master b8e0f17dd -> ad836cbb7
Change wire format of exec handler. The JS now sends commands as a JSON array instead of pre-processed JSON object. The code maintains compatibility with old-style plugins by performing the "options" dict extraction on the native side when the plugin responds to method:withDict: selector. The new way of implementing native callbacks is to us a method called method:, which is passed the CDVInvokedUrlCommand. Update CDVFileTransfer to use the new API. Project: http://git-wip-us.apache.org/repos/asf/incubator-cordova-ios/repo Commit: http://git-wip-us.apache.org/repos/asf/incubator-cordova-ios/commit/ad836cbb Tree: http://git-wip-us.apache.org/repos/asf/incubator-cordova-ios/tree/ad836cbb Diff: http://git-wip-us.apache.org/repos/asf/incubator-cordova-ios/diff/ad836cbb Branch: refs/heads/master Commit: ad836cbb7afffe49ecc9f28708a910bd325d2527 Parents: b8e0f17 Author: Andrew Grieve <agri...@chromium.org> Authored: Fri Jul 27 13:34:49 2012 -0400 Committer: Andrew Grieve <agri...@chromium.org> Committed: Fri Jul 27 20:05:40 2012 -0400 ---------------------------------------------------------------------- CordovaLib/Classes/CDVFileTransfer.h | 6 +- CordovaLib/Classes/CDVFileTransfer.m | 55 ++++++------- CordovaLib/Classes/CDVInvokedUrlCommand.h | 31 +++++-- CordovaLib/Classes/CDVInvokedUrlCommand.m | 70 ++++++++++++---- CordovaLib/Classes/CDVViewController.m | 36 +++++--- CordovaLib/CordovaLibTests/CDVFileTransferTests.m | 22 ++++-- 6 files changed, 143 insertions(+), 77 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/incubator-cordova-ios/blob/ad836cbb/CordovaLib/Classes/CDVFileTransfer.h ---------------------------------------------------------------------- diff --git a/CordovaLib/Classes/CDVFileTransfer.h b/CordovaLib/Classes/CDVFileTransfer.h index 54a37b3..3adbf37 100644 --- a/CordovaLib/Classes/CDVFileTransfer.h +++ b/CordovaLib/Classes/CDVFileTransfer.h @@ -41,12 +41,12 @@ extern NSString* const kOptionsKeyCookie; } -- (void) upload:(NSArray*)arguments withDict:(NSDictionary*)options; -- (void) download:(NSArray*)arguments withDict:(NSDictionary*)options; +- (void) upload:(CDVInvokedUrlCommand*)command; +- (void) download:(CDVInvokedUrlCommand*)command; - (NSString*) escapePathComponentForUrlString:(NSString*)urlString; // Visible for testing. -- (NSURLRequest*) requestForUpload:(NSArray*)arguments withDict:(NSDictionary*)options fileData:(NSData*)fileData; +- (NSURLRequest*) requestForUploadCommand:(CDVInvokedUrlCommand*)command fileData:(NSData*)fileData; -(NSMutableDictionary*) createFileTransferError:(int)code AndSource:(NSString*)source AndTarget:(NSString*)target; -(NSMutableDictionary*) createFileTransferError:(int)code http://git-wip-us.apache.org/repos/asf/incubator-cordova-ios/blob/ad836cbb/CordovaLib/Classes/CDVFileTransfer.m ---------------------------------------------------------------------- diff --git a/CordovaLib/Classes/CDVFileTransfer.m b/CordovaLib/Classes/CDVFileTransfer.m index 147be32..e831537 100644 --- a/CordovaLib/Classes/CDVFileTransfer.m +++ b/CordovaLib/Classes/CDVFileTransfer.m @@ -23,9 +23,9 @@ @interface CDVFileTransfer () // Creates a delegate to handle an upload. -- (CDVFileTransferDelegate*)delegateForUpload:(NSArray*)arguments; +- (CDVFileTransferDelegate*)delegateForUploadCommand:(CDVInvokedUrlCommand*)command; // Creates an NSData* for the file for the given upload arguments. -- (NSData*)fileDataForUploadArguments:(NSArray*)arguments; +- (NSData*)fileDataForUploadCommand:(CDVInvokedUrlCommand*)command; @end // Buffer size to use for streaming uploads. @@ -74,18 +74,17 @@ static CFIndex WriteDataToStream(NSData* data, CFWriteStreamRef stream) { return [schemeAndHost stringByAppendingString:pathComponent]; } -- (NSURLRequest*) requestForUpload:(NSArray*)arguments withDict:(NSDictionary*)options fileData:(NSData*)fileData { - NSString* callbackId = [arguments objectAtIndex:0]; - +- (NSURLRequest*) requestForUploadCommand:(CDVInvokedUrlCommand *)command fileData:(NSData *)fileData { // arguments order from js: [filePath, server, fileKey, fileName, mimeType, params, debug, chunkedMode] // however, params is a JavaScript object and during marshalling is put into the options dict, // thus debug and chunkedMode are the 6th and 7th arguments - - NSString* target = (NSString*)[arguments objectAtIndex:1]; - NSString* server = (NSString*)[arguments objectAtIndex:2]; - NSString* fileKey = (NSString*)[arguments objectAtIndex:3]; - NSString* fileName = [arguments objectAtIndex:4 withDefault:@"no-filename"]; - NSString* mimeType = [arguments objectAtIndex:5 withDefault:nil]; + NSArray* arguments = command.arguments; + NSString* target = (NSString*)[arguments objectAtIndex:0]; + NSString* server = (NSString*)[arguments objectAtIndex:1]; + NSString* fileKey = (NSString*)[arguments objectAtIndex:2]; + NSString* fileName = [arguments objectAtIndex:3 withDefault:@"no-filename"]; + NSString* mimeType = [arguments objectAtIndex:4 withDefault:nil]; + NSDictionary* options = [arguments objectAtIndex:5 withDefault:nil]; // NSString* trustAllHosts = (NSString*)[arguments objectAtIndex:6]; // allow self-signed certs BOOL chunkedMode = [[arguments objectAtIndex:7 withDefault:[NSNumber numberWithBool:YES]] boolValue]; @@ -113,7 +112,7 @@ static CFIndex WriteDataToStream(NSData* data, CFWriteStreamRef stream) { if(errorCode > 0) { result = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsDictionary: [self createFileTransferError:errorCode AndSource:target AndTarget:server]]; - [self writeJavascript:[result toErrorCallbackString:callbackId]]; + [self writeJavascript:[result toErrorCallbackString:command.callbackId]]; return nil; } @@ -228,22 +227,21 @@ static CFIndex WriteDataToStream(NSData* data, CFWriteStreamRef stream) { return req; } -- (CDVFileTransferDelegate*) delegateForUpload:(NSArray*)arguments { - NSString* callbackId = [arguments objectAtIndex:0]; - NSString* target = (NSString*)[arguments objectAtIndex:1]; - NSString* server = (NSString*)[arguments objectAtIndex:2]; +- (CDVFileTransferDelegate*) delegateForUploadCommand:(CDVInvokedUrlCommand *)command { + NSString* target = [command.arguments objectAtIndex:0]; + NSString* server = [command.arguments objectAtIndex:1]; CDVFileTransferDelegate* delegate = [[[CDVFileTransferDelegate alloc] init] autorelease]; delegate.command = self; delegate.direction = CDV_TRANSFER_UPLOAD; - delegate.callbackId = callbackId; + delegate.callbackId = command.callbackId; delegate.source = server; delegate.target = target; return delegate; } -- (NSData*) fileDataForUploadArguments:(NSArray*)arguments { - NSString* target = (NSString*)[arguments objectAtIndex:1]; +- (NSData*) fileDataForUploadCommand:(CDVInvokedUrlCommand*)command { + NSString* target = (NSString*)[command.arguments objectAtIndex:0]; NSError *err = nil; // Extract the path part out of a file: URL. NSString* filePath = [target hasPrefix:@"/"] ? [[target copy] autorelease] : [[NSURL URLWithString:target] path]; @@ -256,19 +254,18 @@ static CFIndex WriteDataToStream(NSData* data, CFWriteStreamRef stream) { return fileData; } -- (void) upload:(NSArray*)arguments withDict:(NSDictionary*)options { +- (void) upload:(CDVInvokedUrlCommand*)command { // fileData and req are split into helper functions to ease the unit testing of delegateForUpload. - NSData* fileData = [self fileDataForUploadArguments:arguments]; - NSURLRequest* req = [self requestForUpload:arguments withDict:options fileData:fileData]; - CDVFileTransferDelegate* delegate = [self delegateForUpload:arguments]; + NSData* fileData = [self fileDataForUploadCommand:command]; + NSURLRequest* req = [self requestForUploadCommand:command fileData:fileData]; + CDVFileTransferDelegate* delegate = [self delegateForUploadCommand:command]; [NSURLConnection connectionWithRequest:req delegate:delegate]; } -- (void) download:(NSArray*)arguments withDict:(NSDictionary*)options { +- (void) download:(CDVInvokedUrlCommand*)command { DLog(@"File Transfer downloading file..."); - NSString * callbackId = [arguments objectAtIndex:0]; - NSString * sourceUrl = [arguments objectAtIndex:1]; - NSString * filePath = [arguments objectAtIndex:2]; + NSString * sourceUrl = [command.arguments objectAtIndex:0]; + NSString * filePath = [command.arguments objectAtIndex:1]; CDVPluginResult *result = nil; CDVFileTransferError errorCode = 0; @@ -293,7 +290,7 @@ static CFIndex WriteDataToStream(NSData* data, CFWriteStreamRef stream) { if(errorCode > 0) { result = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsDictionary: [self createFileTransferError:errorCode AndSource:sourceUrl AndTarget:filePath]]; - [self writeJavascript:[result toErrorCallbackString:callbackId]]; + [self writeJavascript:[result toErrorCallbackString:command.callbackId]]; return; } @@ -302,7 +299,7 @@ static CFIndex WriteDataToStream(NSData* data, CFWriteStreamRef stream) { CDVFileTransferDelegate* delegate = [[[CDVFileTransferDelegate alloc] init] autorelease]; delegate.command = self; delegate.direction = CDV_TRANSFER_DOWNLOAD; - delegate.callbackId = callbackId; + delegate.callbackId = command.callbackId; delegate.source = sourceUrl; delegate.target = filePath; http://git-wip-us.apache.org/repos/asf/incubator-cordova-ios/blob/ad836cbb/CordovaLib/Classes/CDVInvokedUrlCommand.h ---------------------------------------------------------------------- diff --git a/CordovaLib/Classes/CDVInvokedUrlCommand.h b/CordovaLib/Classes/CDVInvokedUrlCommand.h index b7beb7d..ff7f1ff 100644 --- a/CordovaLib/Classes/CDVInvokedUrlCommand.h +++ b/CordovaLib/Classes/CDVInvokedUrlCommand.h @@ -21,19 +21,30 @@ @interface CDVInvokedUrlCommand : NSObject { - NSString* className; - NSString* methodName; - NSMutableArray* arguments; - NSMutableDictionary* options; + NSString* _callbackId; + NSString* _className; + NSString* _methodName; + NSArray* _arguments; } -@property(retain) NSMutableArray* arguments; -@property(retain) NSMutableDictionary* options; -@property(copy) NSString* className; -@property(copy) NSString* methodName; +@property(nonatomic, readonly) NSArray* arguments; +@property(nonatomic, readonly) NSString* callbackId; +@property(nonatomic, readonly) NSString* className; +@property(nonatomic, readonly) NSString* methodName; -+ (CDVInvokedUrlCommand*) commandFromObject:(NSDictionary*)object; ++ (CDVInvokedUrlCommand*) commandFromJson:(NSArray*)jsonEntry; + +- (id) initWithArguments:(NSArray*)arguments + callbackId:(NSString*)callbackId + className:(NSString*)className + methodName:(NSString*)methodName; + +- (id) initFromJson:(NSArray*)jsonEntry; + +// The first NSDictionary found in the arguments will be returned in legacyDict. +// The arguments array with be prepended with the callbackId and have the first +// dict removed from it. +- (void) legacyArguments:(NSMutableArray**)legacyArguments andDict:(NSMutableDictionary**)legacyDict; -- (void) dealloc; @end http://git-wip-us.apache.org/repos/asf/incubator-cordova-ios/blob/ad836cbb/CordovaLib/Classes/CDVInvokedUrlCommand.m ---------------------------------------------------------------------- diff --git a/CordovaLib/Classes/CDVInvokedUrlCommand.m b/CordovaLib/Classes/CDVInvokedUrlCommand.m index 45b0a1d..677e5af 100644 --- a/CordovaLib/Classes/CDVInvokedUrlCommand.m +++ b/CordovaLib/Classes/CDVInvokedUrlCommand.m @@ -22,28 +22,68 @@ @implementation CDVInvokedUrlCommand -@synthesize arguments; -@synthesize options; -@synthesize className; -@synthesize methodName; +@synthesize arguments = _arguments; +@synthesize callbackId = _callbackId; +@synthesize className = _className; +@synthesize methodName = _methodName; -+ (CDVInvokedUrlCommand*) commandFromObject:(NSDictionary*)object ++ (CDVInvokedUrlCommand*) commandFromJson:(NSArray*)jsonEntry { - CDVInvokedUrlCommand* iuc = [[[CDVInvokedUrlCommand alloc] init] autorelease]; - iuc.className = [object objectForKey:@"className"]; - iuc.methodName = [object objectForKey:@"methodName"]; - iuc.arguments = [object objectForKey:@"arguments"]; - iuc.options = [object objectForKey:@"options"]; + return [[[CDVInvokedUrlCommand alloc] initFromJson:jsonEntry] autorelease]; +} + +- (id) initFromJson:(NSArray*)jsonEntry +{ + NSString* callbackId = [jsonEntry objectAtIndex:0]; + NSString* className = [jsonEntry objectAtIndex:1]; + NSString* methodName = [jsonEntry objectAtIndex:2]; + NSMutableArray* arguments = [jsonEntry objectAtIndex:3]; + + return [self initWithArguments:arguments + callbackId:callbackId + className:className + methodName:methodName]; +} - return iuc; +- (id) initWithArguments:(NSArray*)arguments + callbackId:(NSString*)callbackId + className:(NSString*)className + methodName:(NSString*)methodName +{ + self = [super init]; + if (self != nil) { + _arguments = [arguments retain]; + _callbackId = [callbackId retain]; + _className = [className retain]; + _methodName = [methodName retain]; + } + return self; +} + +- (void) legacyArguments:(NSMutableArray**)legacyArguments andDict:(NSMutableDictionary**)legacyDict { + NSMutableArray* newArguments = [NSMutableArray arrayWithArray:_arguments]; + for (NSUInteger i = 0; i < [newArguments count]; ++i) { + if ([[newArguments objectAtIndex:i] isKindOfClass:[NSDictionary class]]) { + if (legacyDict != NULL) { + *legacyDict = [newArguments objectAtIndex:i]; + } + [newArguments removeObjectAtIndex:i]; + break; + } + } + [newArguments insertObject:_callbackId atIndex:0]; + if (legacyArguments != NULL) { + *legacyArguments = newArguments; + } } + - (void) dealloc { - [arguments release]; - [options release]; - [className release]; - [methodName release]; + [_arguments release]; + [_callbackId release]; + [_className release]; + [_methodName release]; [super dealloc]; } http://git-wip-us.apache.org/repos/asf/incubator-cordova-ios/blob/ad836cbb/CordovaLib/Classes/CDVViewController.m ---------------------------------------------------------------------- diff --git a/CordovaLib/Classes/CDVViewController.m b/CordovaLib/Classes/CDVViewController.m index bd99ecc..63b2de0 100644 --- a/CordovaLib/Classes/CDVViewController.m +++ b/CordovaLib/Classes/CDVViewController.m @@ -767,21 +767,21 @@ BOOL gSplashScreenShown = NO; // Parse the returned JSON array. - NSArray* queuedCommands = - [queuedCommandsJSON cdvjk_objectFromJSONString]; + NSArray* queuedCommands = [queuedCommandsJSON cdvjk_mutableObjectFromJSONString]; // Iterate over and execute all of the commands. - for (NSString* commandJson in queuedCommands) { - - if(![self.commandDelegate execute: - [CDVInvokedUrlCommand commandFromObject: - [commandJson cdvjk_mutableObjectFromJSONString]]]) - { + for (NSArray* jsonEntry in queuedCommands) { + CDVInvokedUrlCommand* command = [CDVInvokedUrlCommand commandFromJson:jsonEntry]; + if(![self.commandDelegate execute:command]) { +#ifdef DEBUG + NSString* commandJson = [jsonEntry cdvjk_JSONString]; static NSUInteger maxLogLength = 1024; NSString* commandString = ([commandJson length] > maxLogLength) ? [NSString stringWithFormat:@"%@[...]", [commandJson substringToIndex:maxLogLength]] : commandJson; + DLog(@"FAILED pluginJSON = %@", commandString); +#endif } } @@ -824,16 +824,24 @@ BOOL gSplashScreenShown = NO; } BOOL retVal = YES; - // construct the fill method name to ammend the second argument. - NSString* fullMethodName = [[NSString alloc] initWithFormat:@"%@:withDict:", command.methodName]; - if ([obj respondsToSelector:NSSelectorFromString(fullMethodName)]) { - [obj performSelector:NSSelectorFromString(fullMethodName) withObject:command.arguments withObject:command.options]; + // Find the proper selector to call. + NSString* methodName = [NSString stringWithFormat:@"%@:", command.methodName]; + NSString* methodNameWithDict = [NSString stringWithFormat:@"%@:withDict:", command.methodName]; + SEL normalSelector = NSSelectorFromString(methodName); + SEL legacySelector = NSSelectorFromString(methodNameWithDict); + // Test for the legacy selector first in case they both exist. + if ([obj respondsToSelector:legacySelector]) { + NSMutableArray* arguments = nil; + NSMutableDictionary* dict = nil; + [command legacyArguments:&arguments andDict:&dict]; + [obj performSelector:legacySelector withObject:arguments withObject:dict]; + } else if ([obj respondsToSelector:normalSelector]) { + [obj performSelector:normalSelector withObject:command]; } else { // There's no method to call, so throw an error. - NSLog(@"ERROR: Method '%@' not defined in Plugin '%@'", fullMethodName, command.className); + NSLog(@"ERROR: Method '%@' not defined in Plugin '%@'", methodName, command.className); retVal = NO; } - [fullMethodName release]; return retVal; } http://git-wip-us.apache.org/repos/asf/incubator-cordova-ios/blob/ad836cbb/CordovaLib/CordovaLibTests/CDVFileTransferTests.m ---------------------------------------------------------------------- diff --git a/CordovaLib/CordovaLibTests/CDVFileTransferTests.m b/CordovaLib/CordovaLibTests/CDVFileTransferTests.m index 6e2fcf7..6c2b929 100644 --- a/CordovaLib/CordovaLibTests/CDVFileTransferTests.m +++ b/CordovaLib/CordovaLibTests/CDVFileTransferTests.m @@ -60,8 +60,9 @@ static NSData* readStream(NSInputStream* stream) { - (void)setUp { [super setUp]; + _arguments = [[NSMutableArray alloc] initWithObjects: - kDummyArgCallbackId, kDummyArgTarget, kDummyArgServer, kDummyArgFileKey, + kDummyArgTarget, kDummyArgServer, kDummyArgFileKey, [NSNull null], [NSNull null], [NSNull null], [NSNull null], [NSNull null], nil]; _dummyFileData = [[kDummyFileContents dataUsingEncoding:NSUTF8StringEncoding] retain]; _fileTransfer = [[CDVFileTransfer alloc] init]; @@ -79,19 +80,27 @@ static NSData* readStream(NSInputStream* stream) { } - (void)setFilePathArg:(NSString*)filePath { - [_arguments replaceObjectAtIndex:1 withObject:filePath]; + [_arguments replaceObjectAtIndex:0 withObject:filePath]; } - (void)setServerUrlArg:(NSString*)serverUrl { - [_arguments replaceObjectAtIndex:2 withObject:serverUrl]; + [_arguments replaceObjectAtIndex:1 withObject:serverUrl]; } - (void)setChunkedModeArg:(BOOL)chunk { [_arguments replaceObjectAtIndex:7 withObject:[NSNumber numberWithBool:chunk]]; } +- (void)setParams:(NSDictionary*)params { + [_arguments replaceObjectAtIndex:5 withObject:params]; +} + - (NSURLRequest*)requestForUpload { - return [_fileTransfer requestForUpload:_arguments withDict:nil fileData:_dummyFileData]; + CDVInvokedUrlCommand* command = [[[CDVInvokedUrlCommand alloc] initWithArguments:_arguments + callbackId:kDummyArgCallbackId + className:@"FileTransfer" + methodName:@"upload"] autorelease]; + return [_fileTransfer requestForUploadCommand:command fileData:_dummyFileData]; } - (void)checkUploadRequest:(NSURLRequest*)request chunked:(BOOL)chunked { @@ -166,9 +175,10 @@ static NSData* readStream(NSInputStream* stream) { [self setChunkedModeArg:NO]; NSDictionary* headers = [NSDictionary dictionaryWithObjectsAndKeys:@"val1", @"key1", @"val2", @"key2", nil]; - NSDictionary* options = [NSDictionary dictionaryWithObjectsAndKeys:@"cookieval", kOptionsKeyCookie, + NSDictionary* params = [NSDictionary dictionaryWithObjectsAndKeys:@"cookieval", kOptionsKeyCookie, headers, @"headers", @"val3", @"key3", nil]; - NSURLRequest* request = [_fileTransfer requestForUpload:_arguments withDict:options fileData:_dummyFileData]; + [self setParams:params]; + NSURLRequest* request = [self requestForUpload]; NSString* payload = [[[NSString alloc] initWithData:[request HTTPBody] encoding:NSUTF8StringEncoding] autorelease]; // Check that headers are properly set. STAssertTrue([@"val1" isEqualToString:[request valueForHTTPHeaderField:@"key1"]], nil);