diff options
| author | Jules Laplace <jules@okfoc.us> | 2015-09-10 14:58:03 -0400 |
|---|---|---|
| committer | Jules Laplace <jules@okfoc.us> | 2015-09-10 14:58:03 -0400 |
| commit | d73a4b1c5a2540077607dcc4001acbae85980ae4 (patch) | |
| tree | c30089f1742f9430bb18679dc6664157a5dc66f4 /StoneIsland/platforms/ios/CordovaLib/Classes/CDVCommandQueue.m | |
| parent | 124e6c0a8d9577b4a30e0b265f5c23d637c41966 (diff) | |
app skeleton
Diffstat (limited to 'StoneIsland/platforms/ios/CordovaLib/Classes/CDVCommandQueue.m')
| -rw-r--r-- | StoneIsland/platforms/ios/CordovaLib/Classes/CDVCommandQueue.m | 211 |
1 files changed, 211 insertions, 0 deletions
diff --git a/StoneIsland/platforms/ios/CordovaLib/Classes/CDVCommandQueue.m b/StoneIsland/platforms/ios/CordovaLib/Classes/CDVCommandQueue.m new file mode 100644 index 00000000..48264b2a --- /dev/null +++ b/StoneIsland/platforms/ios/CordovaLib/Classes/CDVCommandQueue.m @@ -0,0 +1,211 @@ +/* + Licensed to the Apache Software Foundation (ASF) under one + or more contributor license agreements. See the NOTICE file + distributed with this work for additional information + regarding copyright ownership. The ASF licenses this file + to you under the Apache License, Version 2.0 (the + "License"); you may not use this file except in compliance + with the License. You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, + software distributed under the License is distributed on an + "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + KIND, either express or implied. See the License for the + specific language governing permissions and limitations + under the License. + */ + +#include <objc/message.h> +#import "CDV.h" +#import "CDVCommandQueue.h" +#import "CDVViewController.h" +#import "CDVCommandDelegateImpl.h" +#import "CDVJSON_private.h" + +// Parse JS on the main thread if it's shorter than this. +static const NSInteger JSON_SIZE_FOR_MAIN_THREAD = 4 * 1024; // Chosen arbitrarily. +// Execute multiple commands in one go until this many seconds have passed. +static const double MAX_EXECUTION_TIME = .008; // Half of a 60fps frame. + +@interface CDVCommandQueue () { + NSInteger _lastCommandQueueFlushRequestId; + __weak CDVViewController* _viewController; + NSMutableArray* _queue; + NSTimeInterval _startExecutionTime; +} +@end + +@implementation CDVCommandQueue + +- (BOOL)currentlyExecuting +{ + return _startExecutionTime > 0; +} + +- (id)initWithViewController:(CDVViewController*)viewController +{ + self = [super init]; + if (self != nil) { + _viewController = viewController; + _queue = [[NSMutableArray alloc] init]; + } + return self; +} + +- (void)dispose +{ + // TODO(agrieve): Make this a zeroing weak ref once we drop support for 4.3. + _viewController = nil; +} + +- (void)resetRequestId +{ + _lastCommandQueueFlushRequestId = 0; +} + +- (void)enqueueCommandBatch:(NSString*)batchJSON +{ + if ([batchJSON length] > 0) { + NSMutableArray* commandBatchHolder = [[NSMutableArray alloc] init]; + [_queue addObject:commandBatchHolder]; + if ([batchJSON length] < JSON_SIZE_FOR_MAIN_THREAD) { + [commandBatchHolder addObject:[batchJSON cdv_JSONObject]]; + } else { + dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^() { + NSMutableArray* result = [batchJSON cdv_JSONObject]; + @synchronized(commandBatchHolder) { + [commandBatchHolder addObject:result]; + } + [self performSelectorOnMainThread:@selector(executePending) withObject:nil waitUntilDone:NO]; + }); + } + } +} + +- (void)processXhrExecBridgePoke:(NSNumber*)requestId +{ + NSInteger rid = [requestId integerValue]; + + // An ID of 1 is a special case because that signifies the first request of + // the page. Since resetRequestId is called from webViewDidStartLoad, and the + // JS context at the time of webViewDidStartLoad is still that of the previous + // page, it's possible for requests from the previous page to come in after this + // point. We ignore these by enforcing that ID=1 be the first ID. + if ((_lastCommandQueueFlushRequestId == 0) && (rid != 1)) { + CDV_EXEC_LOG(@"Exec: Ignoring exec request from previous page."); + return; + } + + // Use the request ID to determine if we've already flushed for this request. + // This is required only because the NSURLProtocol enqueues the same request + // multiple times. + if (rid > _lastCommandQueueFlushRequestId) { + _lastCommandQueueFlushRequestId = [requestId integerValue]; + [self fetchCommandsFromJs]; + [self executePending]; + } +} + +- (void)fetchCommandsFromJs +{ + // Grab all the queued commands from the JS side. + NSString* queuedCommandsJSON = [_viewController.webView stringByEvaluatingJavaScriptFromString: + @"cordova.require('cordova/exec').nativeFetchMessages()"]; + + CDV_EXEC_LOG(@"Exec: Flushed JS->native queue (hadCommands=%d).", [queuedCommandsJSON length] > 0); + [self enqueueCommandBatch:queuedCommandsJSON]; +} + +- (void)executePending +{ + // Make us re-entrant-safe. + if (_startExecutionTime > 0) { + return; + } + @try { + _startExecutionTime = [NSDate timeIntervalSinceReferenceDate]; + + while ([_queue count] > 0) { + NSMutableArray* commandBatchHolder = _queue[0]; + NSMutableArray* commandBatch = nil; + @synchronized(commandBatchHolder) { + // If the next-up command is still being decoded, wait for it. + if ([commandBatchHolder count] == 0) { + break; + } + commandBatch = commandBatchHolder[0]; + } + + while ([commandBatch count] > 0) { + @autoreleasepool { + // Execute the commands one-at-a-time. + NSArray* jsonEntry = [commandBatch dequeue]; + if ([commandBatch count] == 0) { + [_queue removeObjectAtIndex:0]; + } + CDVInvokedUrlCommand* command = [CDVInvokedUrlCommand commandFromJson:jsonEntry]; + CDV_EXEC_LOG(@"Exec(%@): Calling %@.%@", command.callbackId, command.className, command.methodName); + + if (![self execute:command]) { +#ifdef DEBUG + NSString* commandJson = [jsonEntry cdv_JSONString]; + static NSUInteger maxLogLength = 1024; + NSString* commandString = ([commandJson length] > maxLogLength) ? + [NSString stringWithFormat:@"%@[...]", [commandJson substringToIndex:maxLogLength]] : + commandJson; + + DLog(@"FAILED pluginJSON = %@", commandString); +#endif + } + } + + // Yield if we're taking too long. + if (([_queue count] > 0) && ([NSDate timeIntervalSinceReferenceDate] - _startExecutionTime > MAX_EXECUTION_TIME)) { + [self performSelector:@selector(executePending) withObject:nil afterDelay:0]; + return; + } + } + } + } @finally + { + _startExecutionTime = 0; + } +} + +- (BOOL)execute:(CDVInvokedUrlCommand*)command +{ + if ((command.className == nil) || (command.methodName == nil)) { + NSLog(@"ERROR: Classname and/or methodName not found for command."); + return NO; + } + + // Fetch an instance of this class + CDVPlugin* obj = [_viewController.commandDelegate getCommandInstance:command.className]; + + if (!([obj isKindOfClass:[CDVPlugin class]])) { + NSLog(@"ERROR: Plugin '%@' not found, or is not a CDVPlugin. Check your plugin mapping in config.xml.", command.className); + return NO; + } + BOOL retVal = YES; + double started = [[NSDate date] timeIntervalSince1970] * 1000.0; + // Find the proper selector to call. + NSString* methodName = [NSString stringWithFormat:@"%@:", command.methodName]; + SEL normalSelector = NSSelectorFromString(methodName); + if ([obj respondsToSelector:normalSelector]) { + // [obj performSelector:normalSelector withObject:command]; + ((void (*)(id, SEL, id))objc_msgSend)(obj, normalSelector, command); + } else { + // There's no method to call, so throw an error. + NSLog(@"ERROR: Method '%@' not defined in Plugin '%@'", methodName, command.className); + retVal = NO; + } + double elapsed = [[NSDate date] timeIntervalSince1970] * 1000.0 - started; + if (elapsed > 10) { + NSLog(@"THREAD WARNING: ['%@'] took '%f' ms. Plugin should use a background thread.", command.className, elapsed); + } + return retVal; +} + +@end |
