Add a method to CDVCommandDelegate for executing JS. The method polls for pending exec() calls at the same time, so should be used when the evall'ed JS calls callbacks of any kind.
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/4e52b1e6 Tree: http://git-wip-us.apache.org/repos/asf/incubator-cordova-ios/tree/4e52b1e6 Diff: http://git-wip-us.apache.org/repos/asf/incubator-cordova-ios/diff/4e52b1e6 Branch: refs/heads/master Commit: 4e52b1e6fc8f304bde1b21ccff5811a36a31bd5a Parents: ba7d771 Author: Andrew Grieve <agri...@chromium.org> Authored: Wed Oct 3 16:22:20 2012 -0400 Committer: Andrew Grieve <agri...@chromium.org> Committed: Thu Oct 4 15:00:52 2012 -0400 ---------------------------------------------------------------------- CordovaLib/Classes/CDVCommandDelegate.h | 5 ++- CordovaLib/Classes/CDVCommandQueue.h | 3 +- CordovaLib/Classes/CDVCommandQueue.m | 27 ++++++----------- CordovaLib/Classes/CDVPlugin.m | 6 ++- CordovaLib/Classes/CDVURLProtocol.m | 2 +- CordovaLib/Classes/CDVViewController.m | 41 ++++++++++++++++++++------ 6 files changed, 52 insertions(+), 32 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/incubator-cordova-ios/blob/4e52b1e6/CordovaLib/Classes/CDVCommandDelegate.h ---------------------------------------------------------------------- diff --git a/CordovaLib/Classes/CDVCommandDelegate.h b/CordovaLib/Classes/CDVCommandDelegate.h index 7acf3e4..7f42785 100644 --- a/CordovaLib/Classes/CDVCommandDelegate.h +++ b/CordovaLib/Classes/CDVCommandDelegate.h @@ -32,8 +32,9 @@ // being made. Instead, they should use getCommandInstance and call methods // directly. - (BOOL)execute:(CDVInvokedUrlCommand*)command; -// Sends a plugin result to the JS. The success/failure is inferred from the -// plugin's status. +// Sends a plugin result to the JS. - (void)sendPluginResult:(CDVPluginResult*)result callbackId:(NSString*)callbackId; +// Evaluates the given JS. +- (void)evalJs:(NSString*)js; @end http://git-wip-us.apache.org/repos/asf/incubator-cordova-ios/blob/4e52b1e6/CordovaLib/Classes/CDVCommandQueue.h ---------------------------------------------------------------------- diff --git a/CordovaLib/Classes/CDVCommandQueue.h b/CordovaLib/Classes/CDVCommandQueue.h index cc522fe..9195259 100644 --- a/CordovaLib/Classes/CDVCommandQueue.h +++ b/CordovaLib/Classes/CDVCommandQueue.h @@ -30,12 +30,13 @@ BOOL _currentlyExecuting; } +@property (nonatomic, readonly) BOOL currentlyExecuting; + - (id)initWithViewController:(CDVViewController*)viewController; - (void)resetRequestId; - (void)enqueCommandBatch:(NSString*)batchJSON; - (void)maybeFetchCommandsFromJs:(NSNumber*)requestId; -- (void)executeCommandsFromJson:(NSString*)queuedCommandsJSON; - (void)fetchCommandsFromJs; - (void)executePending; - (BOOL)execute:(CDVInvokedUrlCommand*)command; http://git-wip-us.apache.org/repos/asf/incubator-cordova-ios/blob/4e52b1e6/CordovaLib/Classes/CDVCommandQueue.m ---------------------------------------------------------------------- diff --git a/CordovaLib/Classes/CDVCommandQueue.m b/CordovaLib/Classes/CDVCommandQueue.m index f547663..a7d2126 100644 --- a/CordovaLib/Classes/CDVCommandQueue.m +++ b/CordovaLib/Classes/CDVCommandQueue.m @@ -17,12 +17,15 @@ under the License. */ +#include <objc/message.h> #import "CDV.h" #import "CDVCommandQueue.h" #import "CDVViewController.h" @implementation CDVCommandQueue +@synthesize currentlyExecuting = _currentlyExecuting; + - (id)initWithViewController:(CDVViewController*)viewController { self = [super init]; @@ -40,20 +43,10 @@ - (void)enqueCommandBatch:(NSString*)batchJSON { - [_queue addObject:batchJSON]; -} - -/** - * Fetches the command queue and executes each command. It is possible that the - * queue will not be empty after this function has completed since the executed - * commands may have run callbacks which queued more commands. - * - * Returns the number of executed commands. - */ -- (void)executeCommandsFromJson:(NSString*)queuedCommandsJSON -{ - [self enqueCommandBatch:queuedCommandsJSON]; - [self executePending]; + if ([batchJSON length] > 0) { + [_queue addObject:batchJSON]; + [self executePending]; + } } - (void)maybeFetchCommandsFromJs:(NSNumber*)requestId @@ -81,12 +74,12 @@ for (;; ) { // Grab all the queued commands from the JS side. NSString* queuedCommandsJSON = [_viewController.webView stringByEvaluatingJavaScriptFromString: - @"cordova.require('cordova/plugin/ios/nativecomm')()"]; + @"cordova.require('cordova/exec').nativeFetchMessages()"]; - if ([@"[]" isEqualToString:queuedCommandsJSON]) { + if ([queuedCommandsJSON length] == 0) { break; } - [self executeCommandsFromJson:queuedCommandsJSON]; + [self enqueCommandBatch:queuedCommandsJSON]; } [_viewController.webView stringByEvaluatingJavaScriptFromString: http://git-wip-us.apache.org/repos/asf/incubator-cordova-ios/blob/4e52b1e6/CordovaLib/Classes/CDVPlugin.m ---------------------------------------------------------------------- diff --git a/CordovaLib/Classes/CDVPlugin.m b/CordovaLib/Classes/CDVPlugin.m index a747d75..5039396 100644 --- a/CordovaLib/Classes/CDVPlugin.m +++ b/CordovaLib/Classes/CDVPlugin.m @@ -127,12 +127,14 @@ - (NSString*)success:(CDVPluginResult*)pluginResult callbackId:(NSString*)callbackId { - return [self writeJavascript:[pluginResult toSuccessCallbackString:callbackId]]; + [self.commandDelegate evalJs:[pluginResult toSuccessCallbackString:callbackId]]; + return @""; } - (NSString*)error:(CDVPluginResult*)pluginResult callbackId:(NSString*)callbackId { - return [self writeJavascript:[pluginResult toErrorCallbackString:callbackId]]; + [self.commandDelegate evalJs:[pluginResult toErrorCallbackString:callbackId]]; + return @""; } @end http://git-wip-us.apache.org/repos/asf/incubator-cordova-ios/blob/4e52b1e6/CordovaLib/Classes/CDVURLProtocol.m ---------------------------------------------------------------------- diff --git a/CordovaLib/Classes/CDVURLProtocol.m b/CordovaLib/Classes/CDVURLProtocol.m index 3d59f1f..012775d 100644 --- a/CordovaLib/Classes/CDVURLProtocol.m +++ b/CordovaLib/Classes/CDVURLProtocol.m @@ -96,7 +96,7 @@ static NSMutableSet* gRegisteredControllers = nil; } BOOL hasCmds = [queuedCommandsJSON length] > 0; if (hasCmds) { - SEL sel = @selector(executeCommandsFromJson:); + SEL sel = @selector(enqueCommandBatch:); [viewController.commandQueue performSelectorOnMainThread:sel withObject:queuedCommandsJSON waitUntilDone:NO]; } else { SEL sel = @selector(maybeFetchCommandsFromJs:); http://git-wip-us.apache.org/repos/asf/incubator-cordova-ios/blob/4e52b1e6/CordovaLib/Classes/CDVViewController.m ---------------------------------------------------------------------- diff --git a/CordovaLib/Classes/CDVViewController.m b/CordovaLib/Classes/CDVViewController.m index c6251c2..a3632f5 100644 --- a/CordovaLib/Classes/CDVViewController.m +++ b/CordovaLib/Classes/CDVViewController.m @@ -663,7 +663,7 @@ + (NSString*)applicationDocumentsDirectory { NSArray* paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES); - NSString* basePath = ([paths count] > 0) ?[paths objectAtIndex:0] : nil; + NSString* basePath = ([paths count] > 0) ? [paths objectAtIndex:0] : nil; return basePath; } @@ -794,23 +794,46 @@ BOOL gSplashScreenShown = NO; } #pragma mark CordovaCommands +- (void)evalJsHelper:(NSString*)js +{ + void (^doIt)() = ^{ + NSString* commandsJSON = [self.webView stringByEvaluatingJavaScriptFromString:js]; + [_commandQueue enqueCommandBatch:commandsJSON]; + }; + + // Cycle the run-loop before executing the JS. + // This works around a bug where sometimes alerts() within callbacks can cause + // dead-lock. + // If the commandQueue is currently executing, then we know that it is safe to + // execute the callback immediately. + if (![NSThread isMainThread] || !_commandQueue.currentlyExecuting) { + dispatch_async (dispatch_get_main_queue (), doIt); + } else { + doIt (); + } +} - (void)sendPluginResult:(CDVPluginResult*)result callbackId:(NSString*)callbackId { int status = [result.status intValue]; BOOL keepCallback = [result.keepCallback boolValue]; - id message = result.message == nil ?[NSNull null] : result.message; + id message = result.message == nil ? [NSNull null] : result.message; // Use an array to encode the message as JSON. message = [NSArray arrayWithObject:message]; NSString* encodedMessage = [message cdvjk_JSONString]; // And then strip off the outer []s. - encodedMessage = [encodedMessage substringWithRange:NSMakeRange(1, [encodedMessage length] - 2)]; + encodedMessage = [encodedMessage substringWithRange:NSMakeRange (1, [encodedMessage length] - 2)]; NSString* js = [NSString stringWithFormat:@"cordova.require('cordova/exec').nativeCallback('%@',%d,%@,%d)", callbackId, status, encodedMessage, keepCallback]; - NSString* commandsJSON = [self.webView stringByEvaluatingJavaScriptFromString:js]; - [_commandQueue enqueCommandBatch:commandsJSON]; + [self evalJsHelper:js]; +} + +- (void)evalJs:(NSString*)js +{ + js = [js stringByAppendingString:@";cordova.require('cordova/exec').nativeFetchMessages()"]; + [self evalJsHelper:js]; } - (BOOL)execute:(CDVInvokedUrlCommand*)command @@ -861,7 +884,7 @@ BOOL gSplashScreenShown = NO; if ((obj != nil) && [obj isKindOfClass:[CDVPlugin class]]) { [self registerPlugin:obj withClassName:className]; } else { - NSLog(@"CDVPlugin class %@ (pluginName: %@) does not exist.", className, pluginName); + NSLog (@"CDVPlugin class %@ (pluginName: %@) does not exist.", className, pluginName); } } return obj; @@ -898,9 +921,9 @@ BOOL gSplashScreenShown = NO; NSString* plistPath = [[NSBundle mainBundle] pathForResource:plistName ofType:@"plist"]; NSData* plistXML = [[NSFileManager defaultManager] contentsAtPath:plistPath]; NSDictionary* temp = (NSDictionary*)[NSPropertyListSerialization - propertyListFromData:plistXML - mutabilityOption:NSPropertyListMutableContainersAndLeaves - format:&format errorDescription:&errorDesc]; +propertyListFromData: plistXML +mutabilityOption: NSPropertyListMutableContainersAndLeaves +format: &format errorDescription : &errorDesc]; return temp; }