| @@ -0,0 +1,180 @@ | ||
| /* | ||
| 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. | ||
| */ | ||
|
|
||
| #import "CDVCommandDelegateImpl.h" | ||
| #import "CDVJSON_private.h" | ||
| #import "CDVCommandQueue.h" | ||
| #import "CDVPluginResult.h" | ||
| #import "CDVViewController.h" | ||
|
|
||
| @implementation CDVCommandDelegateImpl | ||
|
|
||
| - (id)initWithViewController:(CDVViewController*)viewController | ||
| { | ||
| self = [super init]; | ||
| if (self != nil) { | ||
| _viewController = viewController; | ||
| _commandQueue = _viewController.commandQueue; | ||
|
|
||
| NSError* err = nil; | ||
| _callbackIdPattern = [NSRegularExpression regularExpressionWithPattern:@"[^A-Za-z0-9._-]" options:0 error:&err]; | ||
| if (err != nil) { | ||
| // Couldn't initialize Regex | ||
| NSLog(@"Error: Couldn't initialize regex"); | ||
| _callbackIdPattern = nil; | ||
| } | ||
| } | ||
| return self; | ||
| } | ||
|
|
||
| - (NSString*)pathForResource:(NSString*)resourcepath | ||
| { | ||
| NSBundle* mainBundle = [NSBundle mainBundle]; | ||
| NSMutableArray* directoryParts = [NSMutableArray arrayWithArray:[resourcepath componentsSeparatedByString:@"/"]]; | ||
| NSString* filename = [directoryParts lastObject]; | ||
|
|
||
| [directoryParts removeLastObject]; | ||
|
|
||
| NSString* directoryPartsJoined = [directoryParts componentsJoinedByString:@"/"]; | ||
| NSString* directoryStr = _viewController.wwwFolderName; | ||
|
|
||
| if ([directoryPartsJoined length] > 0) { | ||
| directoryStr = [NSString stringWithFormat:@"%@/%@", _viewController.wwwFolderName, [directoryParts componentsJoinedByString:@"/"]]; | ||
| } | ||
|
|
||
| return [mainBundle pathForResource:filename ofType:@"" inDirectory:directoryStr]; | ||
| } | ||
|
|
||
| - (void)flushCommandQueueWithDelayedJs | ||
| { | ||
| _delayResponses = YES; | ||
| [_commandQueue executePending]; | ||
| _delayResponses = NO; | ||
| } | ||
|
|
||
| - (void)evalJsHelper2:(NSString*)js | ||
| { | ||
| CDV_EXEC_LOG(@"Exec: evalling: %@", [js substringToIndex:MIN([js length], 160)]); | ||
| NSString* commandsJSON = [_viewController.webView stringByEvaluatingJavaScriptFromString:js]; | ||
| if ([commandsJSON length] > 0) { | ||
| CDV_EXEC_LOG(@"Exec: Retrieved new exec messages by chaining."); | ||
| } | ||
|
|
||
| [_commandQueue enqueueCommandBatch:commandsJSON]; | ||
| [_commandQueue executePending]; | ||
| } | ||
|
|
||
| - (void)evalJsHelper:(NSString*)js | ||
| { | ||
| // Cycle the run-loop before executing the JS. | ||
| // For _delayResponses - | ||
| // This ensures that we don't eval JS during the middle of an existing JS | ||
| // function (possible since UIWebViewDelegate callbacks can be synchronous). | ||
| // For !isMainThread - | ||
| // It's a hard error to eval on the non-UI thread. | ||
| // For !_commandQueue.currentlyExecuting - | ||
| // 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. | ||
| // Using (dispatch_get_main_queue()) does *not* fix deadlocks for some reason, | ||
| // but performSelectorOnMainThread: does. | ||
| if (_delayResponses || ![NSThread isMainThread] || !_commandQueue.currentlyExecuting) { | ||
| [self performSelectorOnMainThread:@selector(evalJsHelper2:) withObject:js waitUntilDone:NO]; | ||
| } else { | ||
| [self evalJsHelper2:js]; | ||
| } | ||
| } | ||
|
|
||
| - (BOOL)isValidCallbackId:(NSString*)callbackId | ||
| { | ||
| if ((callbackId == nil) || (_callbackIdPattern == nil)) { | ||
| return NO; | ||
| } | ||
|
|
||
| // Disallow if too long or if any invalid characters were found. | ||
| if (([callbackId length] > 100) || [_callbackIdPattern firstMatchInString:callbackId options:0 range:NSMakeRange(0, [callbackId length])]) { | ||
| return NO; | ||
| } | ||
| return YES; | ||
| } | ||
|
|
||
| - (void)sendPluginResult:(CDVPluginResult*)result callbackId:(NSString*)callbackId | ||
| { | ||
| CDV_EXEC_LOG(@"Exec(%@): Sending result. Status=%@", callbackId, result.status); | ||
| // This occurs when there is are no win/fail callbacks for the call. | ||
| if ([@"INVALID" isEqualToString : callbackId]) { | ||
| return; | ||
| } | ||
| // This occurs when the callback id is malformed. | ||
| if (![self isValidCallbackId:callbackId]) { | ||
| NSLog(@"Invalid callback id received by sendPluginResult"); | ||
| return; | ||
| } | ||
| int status = [result.status intValue]; | ||
| BOOL keepCallback = [result.keepCallback boolValue]; | ||
| NSString* argumentsAsJSON = [result argumentsAsJSON]; | ||
|
|
||
| NSString* js = [NSString stringWithFormat:@"cordova.require('cordova/exec').nativeCallback('%@',%d,%@,%d)", callbackId, status, argumentsAsJSON, keepCallback]; | ||
|
|
||
| [self evalJsHelper:js]; | ||
| } | ||
|
|
||
| - (void)evalJs:(NSString*)js | ||
| { | ||
| [self evalJs:js scheduledOnRunLoop:YES]; | ||
| } | ||
|
|
||
| - (void)evalJs:(NSString*)js scheduledOnRunLoop:(BOOL)scheduledOnRunLoop | ||
| { | ||
| js = [NSString stringWithFormat:@"cordova.require('cordova/exec').nativeEvalAndFetch(function(){%@})", js]; | ||
| if (scheduledOnRunLoop) { | ||
| [self evalJsHelper:js]; | ||
| } else { | ||
| [self evalJsHelper2:js]; | ||
| } | ||
| } | ||
|
|
||
| - (id)getCommandInstance:(NSString*)pluginName | ||
| { | ||
| return [_viewController getCommandInstance:pluginName]; | ||
| } | ||
|
|
||
| - (void)runInBackground:(void (^)())block | ||
| { | ||
| dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), block); | ||
| } | ||
|
|
||
| - (NSString*)userAgent | ||
| { | ||
| return [_viewController userAgent]; | ||
| } | ||
|
|
||
| - (BOOL)URLIsWhitelisted:(NSURL*)url | ||
| { | ||
| return ![_viewController.whitelist schemeIsAllowed:[url scheme]] || | ||
| [_viewController.whitelist URLIsAllowed:url logFailure:NO]; | ||
| } | ||
|
|
||
| - (NSDictionary*)settings | ||
| { | ||
| return _viewController.settings; | ||
| } | ||
|
|
||
| @end |
| @@ -0,0 +1,40 @@ | ||
| /* | ||
| 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. | ||
| */ | ||
|
|
||
| #import <Foundation/Foundation.h> | ||
|
|
||
| @class CDVInvokedUrlCommand; | ||
| @class CDVViewController; | ||
|
|
||
| @interface CDVCommandQueue : NSObject | ||
|
|
||
| @property (nonatomic, readonly) BOOL currentlyExecuting; | ||
|
|
||
| - (id)initWithViewController:(CDVViewController*)viewController; | ||
| - (void)dispose; | ||
|
|
||
| - (void)resetRequestId; | ||
| - (void)enqueueCommandBatch:(NSString*)batchJSON; | ||
|
|
||
| - (void)processXhrExecBridgePoke:(NSNumber*)requestId; | ||
| - (void)fetchCommandsFromJs; | ||
| - (void)executePending; | ||
| - (BOOL)execute:(CDVInvokedUrlCommand*)command; | ||
|
|
||
| @end |
| @@ -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 |
| @@ -0,0 +1,31 @@ | ||
| /* | ||
| 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. | ||
| */ | ||
|
|
||
| @interface CDVConfigParser : NSObject <NSXMLParserDelegate> | ||
| { | ||
| NSString* featureName; | ||
| } | ||
|
|
||
| @property (nonatomic, readonly, strong) NSMutableDictionary* pluginsDict; | ||
| @property (nonatomic, readonly, strong) NSMutableDictionary* settings; | ||
| @property (nonatomic, readonly, strong) NSMutableArray* whitelistHosts; | ||
| @property (nonatomic, readonly, strong) NSMutableArray* startupPluginNames; | ||
| @property (nonatomic, readonly, strong) NSString* startPage; | ||
|
|
||
| @end |
| @@ -0,0 +1,88 @@ | ||
| /* | ||
| 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. | ||
| */ | ||
|
|
||
| #import "CDVConfigParser.h" | ||
|
|
||
| @interface CDVConfigParser () | ||
|
|
||
| @property (nonatomic, readwrite, strong) NSMutableDictionary* pluginsDict; | ||
| @property (nonatomic, readwrite, strong) NSMutableDictionary* settings; | ||
| @property (nonatomic, readwrite, strong) NSMutableArray* whitelistHosts; | ||
| @property (nonatomic, readwrite, strong) NSMutableArray* startupPluginNames; | ||
| @property (nonatomic, readwrite, strong) NSString* startPage; | ||
|
|
||
| @end | ||
|
|
||
| @implementation CDVConfigParser | ||
|
|
||
| @synthesize pluginsDict, settings, whitelistHosts, startPage, startupPluginNames; | ||
|
|
||
| - (id)init | ||
| { | ||
| self = [super init]; | ||
| if (self != nil) { | ||
| self.pluginsDict = [[NSMutableDictionary alloc] initWithCapacity:30]; | ||
| self.settings = [[NSMutableDictionary alloc] initWithCapacity:30]; | ||
| self.whitelistHosts = [[NSMutableArray alloc] initWithCapacity:30]; | ||
| [self.whitelistHosts addObject:@"file:///*"]; | ||
| [self.whitelistHosts addObject:@"content:///*"]; | ||
| [self.whitelistHosts addObject:@"data:///*"]; | ||
| self.startupPluginNames = [[NSMutableArray alloc] initWithCapacity:8]; | ||
| featureName = nil; | ||
| } | ||
| return self; | ||
| } | ||
|
|
||
| - (void)parser:(NSXMLParser*)parser didStartElement:(NSString*)elementName namespaceURI:(NSString*)namespaceURI qualifiedName:(NSString*)qualifiedName attributes:(NSDictionary*)attributeDict | ||
| { | ||
| if ([elementName isEqualToString:@"preference"]) { | ||
| settings[[attributeDict[@"name"] lowercaseString]] = attributeDict[@"value"]; | ||
| } else if ([elementName isEqualToString:@"feature"]) { // store feature name to use with correct parameter set | ||
| featureName = [attributeDict[@"name"] lowercaseString]; | ||
| } else if ((featureName != nil) && [elementName isEqualToString:@"param"]) { | ||
| NSString* paramName = [attributeDict[@"name"] lowercaseString]; | ||
| id value = attributeDict[@"value"]; | ||
| if ([paramName isEqualToString:@"ios-package"]) { | ||
| pluginsDict[featureName] = value; | ||
| } | ||
| BOOL paramIsOnload = ([paramName isEqualToString:@"onload"] && [@"true" isEqualToString : value]); | ||
| BOOL attribIsOnload = [@"true" isEqualToString :[attributeDict[@"onload"] lowercaseString]]; | ||
| if (paramIsOnload || attribIsOnload) { | ||
| [self.startupPluginNames addObject:featureName]; | ||
| } | ||
| } else if ([elementName isEqualToString:@"access"]) { | ||
| [whitelistHosts addObject:attributeDict[@"origin"]]; | ||
| } else if ([elementName isEqualToString:@"content"]) { | ||
| self.startPage = attributeDict[@"src"]; | ||
| } | ||
| } | ||
|
|
||
| - (void)parser:(NSXMLParser*)parser didEndElement:(NSString*)elementName namespaceURI:(NSString*)namespaceURI qualifiedName:(NSString*)qualifiedName | ||
| { | ||
| if ([elementName isEqualToString:@"feature"]) { // no longer handling a feature so release | ||
| featureName = nil; | ||
| } | ||
| } | ||
|
|
||
| - (void)parser:(NSXMLParser*)parser parseErrorOccurred:(NSError*)parseError | ||
| { | ||
| NSAssert(NO, @"config.xml parse error line %ld col %ld", (long)[parser lineNumber], (long)[parser columnNumber]); | ||
| } | ||
|
|
||
| @end |
| @@ -0,0 +1,25 @@ | ||
| /* | ||
| 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. | ||
| */ | ||
|
|
||
| #ifdef DEBUG | ||
| #define DLog(fmt, ...) NSLog((@"%s [Line %d] " fmt), __PRETTY_FUNCTION__, __LINE__, ##__VA_ARGS__) | ||
| #else | ||
| #define DLog(...) | ||
| #endif | ||
| #define ALog(fmt, ...) NSLog((@"%s [Line %d] " fmt), __PRETTY_FUNCTION__, __LINE__, ##__VA_ARGS__) |
| @@ -0,0 +1,28 @@ | ||
| /* | ||
| 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. | ||
| */ | ||
|
|
||
| #import "CDVPlugin.h" | ||
|
|
||
| @interface CDVHandleOpenURL : CDVPlugin | ||
|
|
||
| @property (nonatomic, strong) NSURL* url; | ||
| @property (nonatomic, assign) BOOL pageLoaded; | ||
|
|
||
| @end | ||
|
|
| @@ -0,0 +1,74 @@ | ||
| /* | ||
| 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. | ||
| */ | ||
|
|
||
| #import "CDVHandleOpenURL.h" | ||
| #import "CDV.h" | ||
|
|
||
| @implementation CDVHandleOpenURL | ||
|
|
||
| - (void)pluginInitialize | ||
| { | ||
| [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(applicationLaunchedWithUrl:) name:CDVPluginHandleOpenURLNotification object:nil]; | ||
| [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(applicationPageDidLoad:) name:CDVPageDidLoadNotification object:nil]; | ||
| } | ||
|
|
||
| - (void)applicationLaunchedWithUrl:(NSNotification*)notification | ||
| { | ||
| NSURL *url = [notification object]; | ||
| self.url = url; | ||
|
|
||
| // warm-start handler | ||
| if (self.pageLoaded) { | ||
| [self processOpenUrl:self.url pageLoaded:YES]; | ||
| self.url = nil; | ||
| } | ||
| } | ||
|
|
||
| - (void)applicationPageDidLoad:(NSNotification*)notification | ||
| { | ||
| // cold-start handler | ||
|
|
||
| self.pageLoaded = YES; | ||
|
|
||
| if (self.url) { | ||
| [self processOpenUrl:self.url pageLoaded:YES]; | ||
| self.url = nil; | ||
| } | ||
| } | ||
|
|
||
| - (void)processOpenUrl:(NSURL*)url pageLoaded:(BOOL)pageLoaded | ||
| { | ||
| if (!pageLoaded) { | ||
| // query the webview for readystate | ||
| NSString* readyState = [self.webView stringByEvaluatingJavaScriptFromString:@"document.readyState"]; | ||
| pageLoaded = [readyState isEqualToString:@"loaded"] || [readyState isEqualToString:@"complete"]; | ||
| } | ||
|
|
||
| if (pageLoaded) { | ||
| // calls into javascript global function 'handleOpenURL' | ||
| NSString* jsString = [NSString stringWithFormat:@"document.addEventListener('deviceready',function(){if (typeof handleOpenURL === 'function') { handleOpenURL(\"%@\");}});", url]; | ||
| [self.webView stringByEvaluatingJavaScriptFromString:jsString]; | ||
| } else { | ||
| // save for when page has loaded | ||
| self.url = url; | ||
| } | ||
| } | ||
|
|
||
|
|
||
| @end |
| @@ -0,0 +1,52 @@ | ||
| /* | ||
| 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. | ||
| */ | ||
|
|
||
| #import <Foundation/Foundation.h> | ||
|
|
||
| @interface CDVInvokedUrlCommand : NSObject { | ||
| NSString* _callbackId; | ||
| NSString* _className; | ||
| NSString* _methodName; | ||
| NSArray* _arguments; | ||
| } | ||
|
|
||
| @property (nonatomic, readonly) NSArray* arguments; | ||
| @property (nonatomic, readonly) NSString* callbackId; | ||
| @property (nonatomic, readonly) NSString* className; | ||
| @property (nonatomic, readonly) NSString* methodName; | ||
|
|
||
| + (CDVInvokedUrlCommand*)commandFromJson:(NSArray*)jsonEntry; | ||
|
|
||
| - (id)initWithArguments:(NSArray*)arguments | ||
| callbackId:(NSString*)callbackId | ||
| className:(NSString*)className | ||
| methodName:(NSString*)methodName; | ||
|
|
||
| - (id)initFromJson:(NSArray*)jsonEntry; | ||
|
|
||
| // Returns the argument at the given index. | ||
| // If index >= the number of arguments, returns nil. | ||
| // If the argument at the given index is NSNull, returns nil. | ||
| - (id)argumentAtIndex:(NSUInteger)index; | ||
| // Same as above, but returns defaultValue instead of nil. | ||
| - (id)argumentAtIndex:(NSUInteger)index withDefault:(id)defaultValue; | ||
| // Same as above, but returns defaultValue instead of nil, and if the argument is not of the expected class, returns defaultValue | ||
| - (id)argumentAtIndex:(NSUInteger)index withDefault:(id)defaultValue andClass:(Class)aClass; | ||
|
|
||
| @end |
| @@ -0,0 +1,117 @@ | ||
| /* | ||
| 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. | ||
| */ | ||
|
|
||
| #import "CDVInvokedUrlCommand.h" | ||
| #import "CDVJSON_private.h" | ||
| #import "NSData+Base64.h" | ||
|
|
||
| @implementation CDVInvokedUrlCommand | ||
|
|
||
| @synthesize arguments = _arguments; | ||
| @synthesize callbackId = _callbackId; | ||
| @synthesize className = _className; | ||
| @synthesize methodName = _methodName; | ||
|
|
||
| + (CDVInvokedUrlCommand*)commandFromJson:(NSArray*)jsonEntry | ||
| { | ||
| return [[CDVInvokedUrlCommand alloc] initFromJson:jsonEntry]; | ||
| } | ||
|
|
||
| - (id)initFromJson:(NSArray*)jsonEntry | ||
| { | ||
| id tmp = [jsonEntry objectAtIndex:0]; | ||
| NSString* callbackId = tmp == [NSNull null] ? nil : tmp; | ||
| 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]; | ||
| } | ||
|
|
||
| - (id)initWithArguments:(NSArray*)arguments | ||
| callbackId:(NSString*)callbackId | ||
| className:(NSString*)className | ||
| methodName:(NSString*)methodName | ||
| { | ||
| self = [super init]; | ||
| if (self != nil) { | ||
| _arguments = arguments; | ||
| _callbackId = callbackId; | ||
| _className = className; | ||
| _methodName = methodName; | ||
| } | ||
| [self massageArguments]; | ||
| return self; | ||
| } | ||
|
|
||
| - (void)massageArguments | ||
| { | ||
| NSMutableArray* newArgs = nil; | ||
|
|
||
| for (NSUInteger i = 0, count = [_arguments count]; i < count; ++i) { | ||
| id arg = [_arguments objectAtIndex:i]; | ||
| if (![arg isKindOfClass:[NSDictionary class]]) { | ||
| continue; | ||
| } | ||
| NSDictionary* dict = arg; | ||
| NSString* type = [dict objectForKey:@"CDVType"]; | ||
| if (!type || ![type isEqualToString:@"ArrayBuffer"]) { | ||
| continue; | ||
| } | ||
| NSString* data = [dict objectForKey:@"data"]; | ||
| if (!data) { | ||
| continue; | ||
| } | ||
| if (newArgs == nil) { | ||
| newArgs = [NSMutableArray arrayWithArray:_arguments]; | ||
| _arguments = newArgs; | ||
| } | ||
| [newArgs replaceObjectAtIndex:i withObject:[NSData cdv_dataFromBase64String:data]]; | ||
| } | ||
| } | ||
|
|
||
| - (id)argumentAtIndex:(NSUInteger)index | ||
| { | ||
| return [self argumentAtIndex:index withDefault:nil]; | ||
| } | ||
|
|
||
| - (id)argumentAtIndex:(NSUInteger)index withDefault:(id)defaultValue | ||
| { | ||
| return [self argumentAtIndex:index withDefault:defaultValue andClass:nil]; | ||
| } | ||
|
|
||
| - (id)argumentAtIndex:(NSUInteger)index withDefault:(id)defaultValue andClass:(Class)aClass | ||
| { | ||
| if (index >= [_arguments count]) { | ||
| return defaultValue; | ||
| } | ||
| id ret = [_arguments objectAtIndex:index]; | ||
| if (ret == [NSNull null]) { | ||
| ret = defaultValue; | ||
| } | ||
| if ((aClass != nil) && ![ret isKindOfClass:aClass]) { | ||
| ret = defaultValue; | ||
| } | ||
| return ret; | ||
| } | ||
|
|
||
| @end |
| @@ -0,0 +1,37 @@ | ||
| /* | ||
| 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. | ||
| */ | ||
|
|
||
| #import "CDVAvailabilityDeprecated.h" | ||
|
|
||
| @interface NSArray (CDVJSONSerializing) | ||
| - (NSString*)JSONString CDV_DEPRECATED(3.8 .0, "Use NSJSONSerialization instead."); | ||
|
|
||
| @end | ||
|
|
||
| @interface NSDictionary (CDVJSONSerializing) | ||
| - (NSString*)JSONString CDV_DEPRECATED(3.8 .0, "Use NSJSONSerialization instead."); | ||
|
|
||
| @end | ||
|
|
||
| @interface NSString (CDVJSONSerializing) | ||
| - (id)JSONObject CDV_DEPRECATED(3.8 .0, "Use NSJSONSerialization instead."); | ||
|
|
||
| - (id)JSONFragment CDV_DEPRECATED(3.8 .0, "Use NSJSONSerialization instead."); | ||
|
|
||
| @end |
| @@ -0,0 +1,52 @@ | ||
| /* | ||
| 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. | ||
| */ | ||
|
|
||
| #import "CDVJSON_private.h" | ||
|
|
||
| @implementation NSArray (CDVJSONSerializing) | ||
|
|
||
| - (NSString*)JSONString | ||
| { | ||
| return [self cdv_JSONString]; | ||
| } | ||
|
|
||
| @end | ||
|
|
||
| @implementation NSDictionary (CDVJSONSerializing) | ||
|
|
||
| - (NSString*)JSONString | ||
| { | ||
| return [self cdv_JSONString]; | ||
| } | ||
|
|
||
| @end | ||
|
|
||
| @implementation NSString (CDVJSONSerializing) | ||
|
|
||
| - (id)JSONObject | ||
| { | ||
| return [self cdv_JSONObject]; | ||
| } | ||
|
|
||
| - (id)JSONFragment | ||
| { | ||
| return [self cdv_JSONFragment]; | ||
| } | ||
|
|
||
| @end |
| @@ -0,0 +1,31 @@ | ||
| /* | ||
| 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. | ||
| */ | ||
|
|
||
| @interface NSArray (CDVJSONSerializingPrivate) | ||
| - (NSString*)cdv_JSONString; | ||
| @end | ||
|
|
||
| @interface NSDictionary (CDVJSONSerializingPrivate) | ||
| - (NSString*)cdv_JSONString; | ||
| @end | ||
|
|
||
| @interface NSString (CDVJSONSerializingPrivate) | ||
| - (id)cdv_JSONObject; | ||
| - (id)cdv_JSONFragment; | ||
| @end |
| @@ -0,0 +1,91 @@ | ||
| /* | ||
| 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. | ||
| */ | ||
|
|
||
| #import "CDVJSON_private.h" | ||
| #import <Foundation/NSJSONSerialization.h> | ||
|
|
||
| @implementation NSArray (CDVJSONSerializingPrivate) | ||
|
|
||
| - (NSString*)cdv_JSONString | ||
| { | ||
| NSError* error = nil; | ||
| NSData* jsonData = [NSJSONSerialization dataWithJSONObject:self | ||
| options:0 | ||
| error:&error]; | ||
|
|
||
| if (error != nil) { | ||
| NSLog(@"NSArray JSONString error: %@", [error localizedDescription]); | ||
| return nil; | ||
| } else { | ||
| return [[NSString alloc] initWithData:jsonData encoding:NSUTF8StringEncoding]; | ||
| } | ||
| } | ||
|
|
||
| @end | ||
|
|
||
| @implementation NSDictionary (CDVJSONSerializingPrivate) | ||
|
|
||
| - (NSString*)cdv_JSONString | ||
| { | ||
| NSError* error = nil; | ||
| NSData* jsonData = [NSJSONSerialization dataWithJSONObject:self | ||
| options:NSJSONWritingPrettyPrinted | ||
| error:&error]; | ||
|
|
||
| if (error != nil) { | ||
| NSLog(@"NSDictionary JSONString error: %@", [error localizedDescription]); | ||
| return nil; | ||
| } else { | ||
| return [[NSString alloc] initWithData:jsonData encoding:NSUTF8StringEncoding]; | ||
| } | ||
| } | ||
|
|
||
| @end | ||
|
|
||
| @implementation NSString (CDVJSONSerializingPrivate) | ||
|
|
||
| - (id)cdv_JSONObject | ||
| { | ||
| NSError* error = nil; | ||
| id object = [NSJSONSerialization JSONObjectWithData:[self dataUsingEncoding:NSUTF8StringEncoding] | ||
| options:NSJSONReadingMutableContainers | ||
| error:&error]; | ||
|
|
||
| if (error != nil) { | ||
| NSLog(@"NSString JSONObject error: %@", [error localizedDescription]); | ||
| } | ||
|
|
||
| return object; | ||
| } | ||
|
|
||
| - (id)cdv_JSONFragment | ||
| { | ||
| NSError* error = nil; | ||
| id object = [NSJSONSerialization JSONObjectWithData:[self dataUsingEncoding:NSUTF8StringEncoding] | ||
| options:NSJSONReadingAllowFragments | ||
| error:&error]; | ||
|
|
||
| if (error != nil) { | ||
| NSLog(@"NSString JSONObject error: %@", [error localizedDescription]); | ||
| } | ||
|
|
||
| return object; | ||
| } | ||
|
|
||
| @end |
| @@ -0,0 +1,50 @@ | ||
| /* | ||
| 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. | ||
| */ | ||
|
|
||
| #import "CDVPlugin.h" | ||
|
|
||
| #define kCDVLocalStorageErrorDomain @"kCDVLocalStorageErrorDomain" | ||
| #define kCDVLocalStorageFileOperationError 1 | ||
|
|
||
| @interface CDVLocalStorage : CDVPlugin | ||
|
|
||
| @property (nonatomic, readonly, strong) NSMutableArray* backupInfo; | ||
|
|
||
| - (BOOL)shouldBackup; | ||
| - (BOOL)shouldRestore; | ||
| - (void)backup:(CDVInvokedUrlCommand*)command; | ||
| - (void)restore:(CDVInvokedUrlCommand*)command; | ||
|
|
||
| + (void)__fixupDatabaseLocationsWithBackupType:(NSString*)backupType; | ||
| // Visible for testing. | ||
| + (BOOL)__verifyAndFixDatabaseLocationsWithAppPlistDict:(NSMutableDictionary*)appPlistDict | ||
| bundlePath:(NSString*)bundlePath | ||
| fileManager:(NSFileManager*)fileManager; | ||
| @end | ||
|
|
||
| @interface CDVBackupInfo : NSObject | ||
|
|
||
| @property (nonatomic, copy) NSString* original; | ||
| @property (nonatomic, copy) NSString* backup; | ||
| @property (nonatomic, copy) NSString* label; | ||
|
|
||
| - (BOOL)shouldBackup; | ||
| - (BOOL)shouldRestore; | ||
|
|
||
| @end |
| @@ -0,0 +1,67 @@ | ||
| /* | ||
| 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. | ||
| */ | ||
|
|
||
| #import <Foundation/Foundation.h> | ||
| #import <UIKit/UIKit.h> | ||
| #import "CDVPluginResult.h" | ||
| #import "NSMutableArray+QueueAdditions.h" | ||
| #import "CDVCommandDelegate.h" | ||
|
|
||
| extern NSString* const CDVPageDidLoadNotification; | ||
| extern NSString* const CDVPluginHandleOpenURLNotification; | ||
| extern NSString* const CDVPluginResetNotification; | ||
| extern NSString* const CDVLocalNotification; | ||
| extern NSString* const CDVRemoteNotification; | ||
| extern NSString* const CDVRemoteNotificationError; | ||
|
|
||
| @interface CDVPlugin : NSObject {} | ||
|
|
||
| @property (nonatomic, weak) UIWebView* webView; | ||
| @property (nonatomic, weak) UIViewController* viewController; | ||
| @property (nonatomic, weak) id <CDVCommandDelegate> commandDelegate; | ||
|
|
||
| @property (readonly, assign) BOOL hasPendingOperation; | ||
|
|
||
| - (CDVPlugin*)initWithWebView:(UIWebView*)theWebView; | ||
| - (void)pluginInitialize; | ||
|
|
||
| - (void)handleOpenURL:(NSNotification*)notification; | ||
| - (void)onAppTerminate; | ||
| - (void)onMemoryWarning; | ||
| - (void)onReset; | ||
| - (void)dispose; | ||
|
|
||
| /* | ||
| // see initWithWebView implementation | ||
| - (void) onPause {} | ||
| - (void) onResume {} | ||
| - (void) onOrientationWillChange {} | ||
| - (void) onOrientationDidChange {} | ||
| - (void)didReceiveLocalNotification:(NSNotification *)notification; | ||
| */ | ||
|
|
||
| - (id)appDelegate; | ||
|
|
||
| - (NSString*)writeJavascript:(NSString*)javascript CDV_DEPRECATED(3.6, "Use the CDVCommandDelegate equivalent of evalJs:. This will be removed in 4.0.0"); | ||
|
|
||
| - (NSString*)success:(CDVPluginResult*)pluginResult callbackId:(NSString*)callbackId CDV_DEPRECATED(3.6, "Use the CDVCommandDelegate equivalent of sendPluginResult:callbackId. This will be removed in 4.0.0"); | ||
|
|
||
| - (NSString*)error:(CDVPluginResult*)pluginResult callbackId:(NSString*)callbackId CDV_DEPRECATED(3.6, "Use the CDVCommandDelegate equivalent of sendPluginResult:callbackId. This will be removed in 4.0.0"); | ||
|
|
||
| @end |
| @@ -0,0 +1,154 @@ | ||
| /* | ||
| 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. | ||
| */ | ||
|
|
||
| #import "CDVPlugin.h" | ||
|
|
||
| NSString* const CDVPageDidLoadNotification = @"CDVPageDidLoadNotification"; | ||
| NSString* const CDVPluginHandleOpenURLNotification = @"CDVPluginHandleOpenURLNotification"; | ||
| NSString* const CDVPluginResetNotification = @"CDVPluginResetNotification"; | ||
| NSString* const CDVLocalNotification = @"CDVLocalNotification"; | ||
| NSString* const CDVRemoteNotification = @"CDVRemoteNotification"; | ||
| NSString* const CDVRemoteNotificationError = @"CDVRemoteNotificationError"; | ||
|
|
||
| @interface CDVPlugin () | ||
|
|
||
| @property (readwrite, assign) BOOL hasPendingOperation; | ||
|
|
||
| @end | ||
|
|
||
| @implementation CDVPlugin | ||
| @synthesize webView, viewController, commandDelegate, hasPendingOperation; | ||
|
|
||
| // Do not override these methods. Use pluginInitialize instead. | ||
| - (CDVPlugin*)initWithWebView:(UIWebView*)theWebView settings:(NSDictionary*)classSettings | ||
| { | ||
| return [self initWithWebView:theWebView]; | ||
| } | ||
|
|
||
| - (CDVPlugin*)initWithWebView:(UIWebView*)theWebView | ||
| { | ||
| self = [super init]; | ||
| if (self) { | ||
| [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(onAppTerminate) name:UIApplicationWillTerminateNotification object:nil]; | ||
| [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(onMemoryWarning) name:UIApplicationDidReceiveMemoryWarningNotification object:nil]; | ||
| [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(handleOpenURL:) name:CDVPluginHandleOpenURLNotification object:nil]; | ||
| [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(onReset) name:CDVPluginResetNotification object:theWebView]; | ||
|
|
||
| self.webView = theWebView; | ||
| } | ||
| return self; | ||
| } | ||
|
|
||
| - (void)pluginInitialize | ||
| { | ||
| // You can listen to more app notifications, see: | ||
| // http://developer.apple.com/library/ios/#DOCUMENTATION/UIKit/Reference/UIApplication_Class/Reference/Reference.html#//apple_ref/doc/uid/TP40006728-CH3-DontLinkElementID_4 | ||
|
|
||
| // NOTE: if you want to use these, make sure you uncomment the corresponding notification handler | ||
|
|
||
| // [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(onPause) name:UIApplicationDidEnterBackgroundNotification object:nil]; | ||
| // [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(onResume) name:UIApplicationWillEnterForegroundNotification object:nil]; | ||
| // [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(onOrientationWillChange) name:UIApplicationWillChangeStatusBarOrientationNotification object:nil]; | ||
| // [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(onOrientationDidChange) name:UIApplicationDidChangeStatusBarOrientationNotification object:nil]; | ||
|
|
||
| // Added in 2.3.0 | ||
| // [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(didReceiveLocalNotification:) name:CDVLocalNotification object:nil]; | ||
|
|
||
| // Added in 2.5.0 | ||
| // [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(pageDidLoad:) name:CDVPageDidLoadNotification object:self.webView]; | ||
| } | ||
|
|
||
| - (void)dispose | ||
| { | ||
| viewController = nil; | ||
| commandDelegate = nil; | ||
| webView = nil; | ||
| } | ||
|
|
||
| /* | ||
| // NOTE: for onPause and onResume, calls into JavaScript must not call or trigger any blocking UI, like alerts | ||
| - (void) onPause {} | ||
| - (void) onResume {} | ||
| - (void) onOrientationWillChange {} | ||
| - (void) onOrientationDidChange {} | ||
| */ | ||
|
|
||
| /* NOTE: calls into JavaScript must not call or trigger any blocking UI, like alerts */ | ||
| - (void)handleOpenURL:(NSNotification*)notification | ||
| { | ||
| // override to handle urls sent to your app | ||
| // register your url schemes in your App-Info.plist | ||
|
|
||
| NSURL* url = [notification object]; | ||
|
|
||
| if ([url isKindOfClass:[NSURL class]]) { | ||
| /* Do your thing! */ | ||
| } | ||
| } | ||
|
|
||
| /* NOTE: calls into JavaScript must not call or trigger any blocking UI, like alerts */ | ||
| - (void)onAppTerminate | ||
| { | ||
| // override this if you need to do any cleanup on app exit | ||
| } | ||
|
|
||
| - (void)onMemoryWarning | ||
| { | ||
| // override to remove caches, etc | ||
| } | ||
|
|
||
| - (void)onReset | ||
| { | ||
| // Override to cancel any long-running requests when the WebView navigates or refreshes. | ||
| } | ||
|
|
||
| - (void)dealloc | ||
| { | ||
| [[NSNotificationCenter defaultCenter] removeObserver:self]; // this will remove all notification unless added using addObserverForName:object:queue:usingBlock: | ||
| } | ||
|
|
||
| - (id)appDelegate | ||
| { | ||
| return [[UIApplication sharedApplication] delegate]; | ||
| } | ||
|
|
||
| - (NSString*)writeJavascript:(NSString*)javascript | ||
| { | ||
| return [self.webView stringByEvaluatingJavaScriptFromString:javascript]; | ||
| } | ||
|
|
||
| - (NSString*)success:(CDVPluginResult*)pluginResult callbackId:(NSString*)callbackId | ||
| { | ||
| [self.commandDelegate evalJs:[pluginResult toSuccessCallbackString:callbackId]]; | ||
| return @""; | ||
| } | ||
|
|
||
| - (NSString*)error:(CDVPluginResult*)pluginResult callbackId:(NSString*)callbackId | ||
| { | ||
| [self.commandDelegate evalJs:[pluginResult toErrorCallbackString:callbackId]]; | ||
| return @""; | ||
| } | ||
|
|
||
| // default implementation does nothing, ideally, we are not registered for notification if we aren't going to do anything. | ||
| // - (void)didReceiveLocalNotification:(NSNotification *)notification | ||
| // { | ||
| // // UILocalNotification* localNotification = [notification object]; // get the payload as a LocalNotification | ||
| // } | ||
|
|
||
| @end |
| @@ -0,0 +1,71 @@ | ||
| /* | ||
| 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. | ||
| */ | ||
|
|
||
| #import <Foundation/Foundation.h> | ||
| #import "CDVAvailability.h" | ||
|
|
||
| typedef enum { | ||
| CDVCommandStatus_NO_RESULT = 0, | ||
| CDVCommandStatus_OK, | ||
| CDVCommandStatus_CLASS_NOT_FOUND_EXCEPTION, | ||
| CDVCommandStatus_ILLEGAL_ACCESS_EXCEPTION, | ||
| CDVCommandStatus_INSTANTIATION_EXCEPTION, | ||
| CDVCommandStatus_MALFORMED_URL_EXCEPTION, | ||
| CDVCommandStatus_IO_EXCEPTION, | ||
| CDVCommandStatus_INVALID_ACTION, | ||
| CDVCommandStatus_JSON_EXCEPTION, | ||
| CDVCommandStatus_ERROR | ||
| } CDVCommandStatus; | ||
|
|
||
| @interface CDVPluginResult : NSObject {} | ||
|
|
||
| @property (nonatomic, strong, readonly) NSNumber* status; | ||
| @property (nonatomic, strong, readonly) id message; | ||
| @property (nonatomic, strong) NSNumber* keepCallback; | ||
| // This property can be used to scope the lifetime of another object. For example, | ||
| // Use it to store the associated NSData when `message` is created using initWithBytesNoCopy. | ||
| @property (nonatomic, strong) id associatedObject; | ||
|
|
||
| - (CDVPluginResult*)init; | ||
| + (CDVPluginResult*)resultWithStatus:(CDVCommandStatus)statusOrdinal; | ||
| + (CDVPluginResult*)resultWithStatus:(CDVCommandStatus)statusOrdinal messageAsString:(NSString*)theMessage; | ||
| + (CDVPluginResult*)resultWithStatus:(CDVCommandStatus)statusOrdinal messageAsArray:(NSArray*)theMessage; | ||
| + (CDVPluginResult*)resultWithStatus:(CDVCommandStatus)statusOrdinal messageAsInt:(int)theMessage; | ||
| + (CDVPluginResult*)resultWithStatus:(CDVCommandStatus)statusOrdinal messageAsDouble:(double)theMessage; | ||
| + (CDVPluginResult*)resultWithStatus:(CDVCommandStatus)statusOrdinal messageAsBool:(BOOL)theMessage; | ||
| + (CDVPluginResult*)resultWithStatus:(CDVCommandStatus)statusOrdinal messageAsDictionary:(NSDictionary*)theMessage; | ||
| + (CDVPluginResult*)resultWithStatus:(CDVCommandStatus)statusOrdinal messageAsArrayBuffer:(NSData*)theMessage; | ||
| + (CDVPluginResult*)resultWithStatus:(CDVCommandStatus)statusOrdinal messageAsMultipart:(NSArray*)theMessages; | ||
| + (CDVPluginResult*)resultWithStatus:(CDVCommandStatus)statusOrdinal messageToErrorObject:(int)errorCode; | ||
|
|
||
| + (void)setVerbose:(BOOL)verbose; | ||
| + (BOOL)isVerbose; | ||
|
|
||
| - (void)setKeepCallbackAsBool:(BOOL)bKeepCallback; | ||
|
|
||
| - (NSString*)argumentsAsJSON; | ||
|
|
||
| // These methods are used by the legacy plugin return result method | ||
| - (NSString*)toJSONString CDV_DEPRECATED(3.6, "Only used by toSuccessCallbackString and toErrorCallbackString which are deprecated. This will be removed in 4.0.0"); | ||
|
|
||
| - (NSString*)toSuccessCallbackString:(NSString*)callbackId CDV_DEPRECATED(3.6, "Use the CDVCommandDelegate method sendPluginResult:callbackId instead. This will be removed in 4.0.0"); | ||
|
|
||
| - (NSString*)toErrorCallbackString:(NSString*)callbackId CDV_DEPRECATED(3.6, "Use the CDVCommandDelegate method sendPluginResult:callbackId instead. This will be removed in 4.0.0"); | ||
|
|
||
| @end |
| @@ -0,0 +1,224 @@ | ||
| /* | ||
| 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. | ||
| */ | ||
|
|
||
| #import "CDVPluginResult.h" | ||
| #import "CDVJSON_private.h" | ||
| #import "CDVDebug.h" | ||
| #import "NSData+Base64.h" | ||
|
|
||
| @interface CDVPluginResult () | ||
|
|
||
| - (CDVPluginResult*)initWithStatus:(CDVCommandStatus)statusOrdinal message:(id)theMessage; | ||
|
|
||
| @end | ||
|
|
||
| @implementation CDVPluginResult | ||
| @synthesize status, message, keepCallback, associatedObject; | ||
|
|
||
| static NSArray* org_apache_cordova_CommandStatusMsgs; | ||
|
|
||
| id messageFromArrayBuffer(NSData* data) | ||
| { | ||
| return @{ | ||
| @"CDVType" : @"ArrayBuffer", | ||
| @"data" :[data cdv_base64EncodedString] | ||
| }; | ||
| } | ||
|
|
||
| id massageMessage(id message) | ||
| { | ||
| if ([message isKindOfClass:[NSData class]]) { | ||
| return messageFromArrayBuffer(message); | ||
| } | ||
| return message; | ||
| } | ||
|
|
||
| id messageFromMultipart(NSArray* theMessages) | ||
| { | ||
| NSMutableArray* messages = [NSMutableArray arrayWithArray:theMessages]; | ||
|
|
||
| for (NSUInteger i = 0; i < messages.count; ++i) { | ||
| [messages replaceObjectAtIndex:i withObject:massageMessage([messages objectAtIndex:i])]; | ||
| } | ||
|
|
||
| return @{ | ||
| @"CDVType" : @"MultiPart", | ||
| @"messages" : messages | ||
| }; | ||
| } | ||
|
|
||
| + (void)initialize | ||
| { | ||
| org_apache_cordova_CommandStatusMsgs = [[NSArray alloc] initWithObjects:@"No result", | ||
| @"OK", | ||
| @"Class not found", | ||
| @"Illegal access", | ||
| @"Instantiation error", | ||
| @"Malformed url", | ||
| @"IO error", | ||
| @"Invalid action", | ||
| @"JSON error", | ||
| @"Error", | ||
| nil]; | ||
| } | ||
|
|
||
| - (CDVPluginResult*)init | ||
| { | ||
| return [self initWithStatus:CDVCommandStatus_NO_RESULT message:nil]; | ||
| } | ||
|
|
||
| - (CDVPluginResult*)initWithStatus:(CDVCommandStatus)statusOrdinal message:(id)theMessage | ||
| { | ||
| self = [super init]; | ||
| if (self) { | ||
| status = [NSNumber numberWithInt:statusOrdinal]; | ||
| message = theMessage; | ||
| keepCallback = [NSNumber numberWithBool:NO]; | ||
| } | ||
| return self; | ||
| } | ||
|
|
||
| + (CDVPluginResult*)resultWithStatus:(CDVCommandStatus)statusOrdinal | ||
| { | ||
| return [[self alloc] initWithStatus:statusOrdinal message:nil]; | ||
| } | ||
|
|
||
| + (CDVPluginResult*)resultWithStatus:(CDVCommandStatus)statusOrdinal messageAsString:(NSString*)theMessage | ||
| { | ||
| return [[self alloc] initWithStatus:statusOrdinal message:theMessage]; | ||
| } | ||
|
|
||
| + (CDVPluginResult*)resultWithStatus:(CDVCommandStatus)statusOrdinal messageAsArray:(NSArray*)theMessage | ||
| { | ||
| return [[self alloc] initWithStatus:statusOrdinal message:theMessage]; | ||
| } | ||
|
|
||
| + (CDVPluginResult*)resultWithStatus:(CDVCommandStatus)statusOrdinal messageAsInt:(int)theMessage | ||
| { | ||
| return [[self alloc] initWithStatus:statusOrdinal message:[NSNumber numberWithInt:theMessage]]; | ||
| } | ||
|
|
||
| + (CDVPluginResult*)resultWithStatus:(CDVCommandStatus)statusOrdinal messageAsDouble:(double)theMessage | ||
| { | ||
| return [[self alloc] initWithStatus:statusOrdinal message:[NSNumber numberWithDouble:theMessage]]; | ||
| } | ||
|
|
||
| + (CDVPluginResult*)resultWithStatus:(CDVCommandStatus)statusOrdinal messageAsBool:(BOOL)theMessage | ||
| { | ||
| return [[self alloc] initWithStatus:statusOrdinal message:[NSNumber numberWithBool:theMessage]]; | ||
| } | ||
|
|
||
| + (CDVPluginResult*)resultWithStatus:(CDVCommandStatus)statusOrdinal messageAsDictionary:(NSDictionary*)theMessage | ||
| { | ||
| return [[self alloc] initWithStatus:statusOrdinal message:theMessage]; | ||
| } | ||
|
|
||
| + (CDVPluginResult*)resultWithStatus:(CDVCommandStatus)statusOrdinal messageAsArrayBuffer:(NSData*)theMessage | ||
| { | ||
| return [[self alloc] initWithStatus:statusOrdinal message:messageFromArrayBuffer(theMessage)]; | ||
| } | ||
|
|
||
| + (CDVPluginResult*)resultWithStatus:(CDVCommandStatus)statusOrdinal messageAsMultipart:(NSArray*)theMessages | ||
| { | ||
| return [[self alloc] initWithStatus:statusOrdinal message:messageFromMultipart(theMessages)]; | ||
| } | ||
|
|
||
| + (CDVPluginResult*)resultWithStatus:(CDVCommandStatus)statusOrdinal messageToErrorObject:(int)errorCode | ||
| { | ||
| NSDictionary* errDict = @{@"code" :[NSNumber numberWithInt:errorCode]}; | ||
|
|
||
| return [[self alloc] initWithStatus:statusOrdinal message:errDict]; | ||
| } | ||
|
|
||
| - (void)setKeepCallbackAsBool:(BOOL)bKeepCallback | ||
| { | ||
| [self setKeepCallback:[NSNumber numberWithBool:bKeepCallback]]; | ||
| } | ||
|
|
||
| - (NSString*)argumentsAsJSON | ||
| { | ||
| id arguments = (self.message == nil ? [NSNull null] : self.message); | ||
| NSArray* argumentsWrappedInArray = [NSArray arrayWithObject:arguments]; | ||
|
|
||
| NSString* argumentsJSON = [argumentsWrappedInArray cdv_JSONString]; | ||
|
|
||
| argumentsJSON = [argumentsJSON substringWithRange:NSMakeRange(1, [argumentsJSON length] - 2)]; | ||
|
|
||
| return argumentsJSON; | ||
| } | ||
|
|
||
| // These methods are used by the legacy plugin return result method | ||
| - (NSString*)toJSONString | ||
| { | ||
| NSDictionary* dict = [NSDictionary dictionaryWithObjectsAndKeys: | ||
| self.status, @"status", | ||
| self.message ? self. message:[NSNull null], @"message", | ||
| self.keepCallback, @"keepCallback", | ||
| nil]; | ||
|
|
||
| NSError* error = nil; | ||
| NSData* jsonData = [NSJSONSerialization dataWithJSONObject:dict | ||
| options:NSJSONWritingPrettyPrinted | ||
| error:&error]; | ||
| NSString* resultString = nil; | ||
|
|
||
| if (error != nil) { | ||
| NSLog(@"toJSONString error: %@", [error localizedDescription]); | ||
| } else { | ||
| resultString = [[NSString alloc] initWithData:jsonData encoding:NSUTF8StringEncoding]; | ||
| } | ||
|
|
||
| if ([[self class] isVerbose]) { | ||
| NSLog(@"PluginResult:toJSONString - %@", resultString); | ||
| } | ||
| return resultString; | ||
| } | ||
|
|
||
| - (NSString*)toSuccessCallbackString:(NSString*)callbackId | ||
| { | ||
| NSString* successCB = [NSString stringWithFormat:@"cordova.callbackSuccess('%@',%@);", callbackId, [self toJSONString]]; | ||
|
|
||
| if ([[self class] isVerbose]) { | ||
| NSLog(@"PluginResult toSuccessCallbackString: %@", successCB); | ||
| } | ||
| return successCB; | ||
| } | ||
|
|
||
| - (NSString*)toErrorCallbackString:(NSString*)callbackId | ||
| { | ||
| NSString* errorCB = [NSString stringWithFormat:@"cordova.callbackError('%@',%@);", callbackId, [self toJSONString]]; | ||
|
|
||
| if ([[self class] isVerbose]) { | ||
| NSLog(@"PluginResult toErrorCallbackString: %@", errorCB); | ||
| } | ||
| return errorCB; | ||
| } | ||
|
|
||
| static BOOL gIsVerbose = NO; | ||
| + (void)setVerbose:(BOOL)verbose | ||
| { | ||
| gIsVerbose = verbose; | ||
| } | ||
|
|
||
| + (BOOL)isVerbose | ||
| { | ||
| return gIsVerbose; | ||
| } | ||
|
|
||
| @end |
| @@ -0,0 +1,28 @@ | ||
| /* | ||
| 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. | ||
| */ | ||
|
|
||
| #import <Foundation/Foundation.h> | ||
|
|
||
| @protocol CDVScreenOrientationDelegate <NSObject> | ||
|
|
||
| - (NSUInteger)supportedInterfaceOrientations; | ||
| - (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation; | ||
| - (BOOL)shouldAutorotate; | ||
|
|
||
| @end |
| @@ -0,0 +1,22 @@ | ||
| /* | ||
| 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. | ||
| */ | ||
|
|
||
| // This file was emptied out in 3.6.0 release (July 2014). | ||
| // It will be deleted in a future release. | ||
| #import <CoreLocation/CoreLocation.h> |
| @@ -0,0 +1,27 @@ | ||
| /* | ||
| 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. | ||
| */ | ||
|
|
||
| #import <Foundation/Foundation.h> | ||
|
|
||
| @interface CDVTimer : NSObject | ||
|
|
||
| + (void)start:(NSString*)name; | ||
| + (void)stop:(NSString*)name; | ||
|
|
||
| @end |
| @@ -0,0 +1,123 @@ | ||
| /* | ||
| 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. | ||
| */ | ||
|
|
||
| #import "CDVTimer.h" | ||
|
|
||
| #pragma mark CDVTimerItem | ||
|
|
||
| @interface CDVTimerItem : NSObject | ||
|
|
||
| @property (nonatomic, strong) NSString* name; | ||
| @property (nonatomic, strong) NSDate* started; | ||
| @property (nonatomic, strong) NSDate* ended; | ||
|
|
||
| - (void)log; | ||
|
|
||
| @end | ||
|
|
||
| @implementation CDVTimerItem | ||
|
|
||
| - (void)log | ||
| { | ||
| NSLog(@"[CDVTimer][%@] %fms", self.name, [self.ended timeIntervalSinceDate:self.started] * 1000.0); | ||
| } | ||
|
|
||
| @end | ||
|
|
||
| #pragma mark CDVTimer | ||
|
|
||
| @interface CDVTimer () | ||
|
|
||
| @property (nonatomic, strong) NSMutableDictionary* items; | ||
|
|
||
| @end | ||
|
|
||
| @implementation CDVTimer | ||
|
|
||
| #pragma mark object methods | ||
|
|
||
| - (id)init | ||
| { | ||
| if (self = [super init]) { | ||
| self.items = [NSMutableDictionary dictionaryWithCapacity:6]; | ||
| } | ||
|
|
||
| return self; | ||
| } | ||
|
|
||
| - (void)add:(NSString*)name | ||
| { | ||
| if ([self.items objectForKey:[name lowercaseString]] == nil) { | ||
| CDVTimerItem* item = [CDVTimerItem new]; | ||
| item.name = name; | ||
| item.started = [NSDate new]; | ||
| [self.items setObject:item forKey:[name lowercaseString]]; | ||
| } else { | ||
| NSLog(@"Timer called '%@' already exists.", name); | ||
| } | ||
| } | ||
|
|
||
| - (void)remove:(NSString*)name | ||
| { | ||
| CDVTimerItem* item = [self.items objectForKey:[name lowercaseString]]; | ||
|
|
||
| if (item != nil) { | ||
| item.ended = [NSDate new]; | ||
| [item log]; | ||
| [self.items removeObjectForKey:[name lowercaseString]]; | ||
| } else { | ||
| NSLog(@"Timer called '%@' does not exist.", name); | ||
| } | ||
| } | ||
|
|
||
| - (void)removeAll | ||
| { | ||
| [self.items removeAllObjects]; | ||
| } | ||
|
|
||
| #pragma mark class methods | ||
|
|
||
| + (void)start:(NSString*)name | ||
| { | ||
| [[CDVTimer sharedInstance] add:name]; | ||
| } | ||
|
|
||
| + (void)stop:(NSString*)name | ||
| { | ||
| [[CDVTimer sharedInstance] remove:name]; | ||
| } | ||
|
|
||
| + (void)clearAll | ||
| { | ||
| [[CDVTimer sharedInstance] removeAll]; | ||
| } | ||
|
|
||
| + (CDVTimer*)sharedInstance | ||
| { | ||
| static dispatch_once_t pred = 0; | ||
| __strong static CDVTimer* _sharedObject = nil; | ||
|
|
||
| dispatch_once(&pred, ^{ | ||
| _sharedObject = [[self alloc] init]; | ||
| }); | ||
|
|
||
| return _sharedObject; | ||
| } | ||
|
|
||
| @end |
| @@ -0,0 +1,29 @@ | ||
| /* | ||
| 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. | ||
| */ | ||
|
|
||
| #import <Foundation/Foundation.h> | ||
| #import "CDVAvailability.h" | ||
|
|
||
| @class CDVViewController; | ||
|
|
||
| @interface CDVURLProtocol : NSURLProtocol {} | ||
|
|
||
| + (void)registerViewController:(CDVViewController*)viewController; | ||
| + (void)unregisterViewController:(CDVViewController*)viewController; | ||
| @end |
| @@ -0,0 +1,213 @@ | ||
| /* | ||
| 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. | ||
| */ | ||
|
|
||
| #import <AssetsLibrary/ALAsset.h> | ||
| #import <AssetsLibrary/ALAssetRepresentation.h> | ||
| #import <AssetsLibrary/ALAssetsLibrary.h> | ||
| #import <MobileCoreServices/MobileCoreServices.h> | ||
| #import "CDVURLProtocol.h" | ||
| #import "CDVCommandQueue.h" | ||
| #import "CDVWhitelist.h" | ||
| #import "CDVViewController.h" | ||
|
|
||
| static CDVWhitelist* gWhitelist = nil; | ||
| // Contains a set of NSNumbers of addresses of controllers. It doesn't store | ||
| // the actual pointer to avoid retaining. | ||
| static NSMutableSet* gRegisteredControllers = nil; | ||
|
|
||
| NSString* const kCDVAssetsLibraryPrefixes = @"assets-library://"; | ||
|
|
||
| // Returns the registered view controller that sent the given request. | ||
| // If the user-agent is not from a UIWebView, or if it's from an unregistered one, | ||
| // then nil is returned. | ||
| static CDVViewController *viewControllerForRequest(NSURLRequest* request) | ||
| { | ||
| // The exec bridge explicitly sets the VC address in a header. | ||
| // This works around the User-Agent not being set for file: URLs. | ||
| NSString* addrString = [request valueForHTTPHeaderField:@"vc"]; | ||
|
|
||
| if (addrString == nil) { | ||
| NSString* userAgent = [request valueForHTTPHeaderField:@"User-Agent"]; | ||
| if (userAgent == nil) { | ||
| return nil; | ||
| } | ||
| NSUInteger bracketLocation = [userAgent rangeOfString:@"(" options:NSBackwardsSearch].location; | ||
| if (bracketLocation == NSNotFound) { | ||
| return nil; | ||
| } | ||
| addrString = [userAgent substringFromIndex:bracketLocation + 1]; | ||
| } | ||
|
|
||
| long long viewControllerAddress = [addrString longLongValue]; | ||
| @synchronized(gRegisteredControllers) { | ||
| if (![gRegisteredControllers containsObject:[NSNumber numberWithLongLong:viewControllerAddress]]) { | ||
| return nil; | ||
| } | ||
| } | ||
|
|
||
| return (__bridge CDVViewController*)(void*)viewControllerAddress; | ||
| } | ||
|
|
||
| @implementation CDVURLProtocol | ||
|
|
||
| + (void)registerPGHttpURLProtocol {} | ||
|
|
||
| + (void)registerURLProtocol {} | ||
|
|
||
| // Called to register the URLProtocol, and to make it away of an instance of | ||
| // a ViewController. | ||
| + (void)registerViewController:(CDVViewController*)viewController | ||
| { | ||
| if (gRegisteredControllers == nil) { | ||
| [NSURLProtocol registerClass:[CDVURLProtocol class]]; | ||
| gRegisteredControllers = [[NSMutableSet alloc] initWithCapacity:8]; | ||
| // The whitelist doesn't change, so grab the first one and store it. | ||
| gWhitelist = viewController.whitelist; | ||
|
|
||
| // Note that we grab the whitelist from the first viewcontroller for now - but this will change | ||
| // when we allow a registered viewcontroller to have its own whitelist (e.g InAppBrowser) | ||
| // Differentiating the requests will be through the 'vc' http header below as used for the js->objc bridge. | ||
| // The 'vc' value is generated by casting the viewcontroller object to a (long long) value (see CDVViewController::webViewDidFinishLoad) | ||
| if (gWhitelist == nil) { | ||
| NSLog(@"WARNING: NO whitelist has been set in CDVURLProtocol."); | ||
| } | ||
| } | ||
|
|
||
| @synchronized(gRegisteredControllers) { | ||
| [gRegisteredControllers addObject:[NSNumber numberWithLongLong:(long long)viewController]]; | ||
| } | ||
| } | ||
|
|
||
| + (void)unregisterViewController:(CDVViewController*)viewController | ||
| { | ||
| @synchronized(gRegisteredControllers) { | ||
| [gRegisteredControllers removeObject:[NSNumber numberWithLongLong:(long long)viewController]]; | ||
| } | ||
| } | ||
|
|
||
| + (BOOL)canInitWithRequest:(NSURLRequest*)theRequest | ||
| { | ||
| NSURL* theUrl = [theRequest URL]; | ||
| CDVViewController* viewController = viewControllerForRequest(theRequest); | ||
|
|
||
| if ([[theUrl absoluteString] hasPrefix:kCDVAssetsLibraryPrefixes]) { | ||
| return YES; | ||
| } else if (viewController != nil) { | ||
| if ([[theUrl path] isEqualToString:@"/!gap_exec"]) { | ||
| NSString* queuedCommandsJSON = [theRequest valueForHTTPHeaderField:@"cmds"]; | ||
| NSString* requestId = [theRequest valueForHTTPHeaderField:@"rc"]; | ||
| if (requestId == nil) { | ||
| NSLog(@"!cordova request missing rc header"); | ||
| return NO; | ||
| } | ||
| BOOL hasCmds = [queuedCommandsJSON length] > 0; | ||
| if (hasCmds) { | ||
| SEL sel = @selector(enqueueCommandBatch:); | ||
| [viewController.commandQueue performSelectorOnMainThread:sel withObject:queuedCommandsJSON waitUntilDone:NO]; | ||
| [viewController.commandQueue performSelectorOnMainThread:@selector(executePending) withObject:nil waitUntilDone:NO]; | ||
| } else { | ||
| SEL sel = @selector(processXhrExecBridgePoke:); | ||
| [viewController.commandQueue performSelectorOnMainThread:sel withObject:[NSNumber numberWithInteger:[requestId integerValue]] waitUntilDone:NO]; | ||
| } | ||
| // Returning NO here would be 20% faster, but it spams WebInspector's console with failure messages. | ||
| // If JS->Native bridge speed is really important for an app, they should use the iframe bridge. | ||
| // Returning YES here causes the request to come through canInitWithRequest two more times. | ||
| // For this reason, we return NO when cmds exist. | ||
| return !hasCmds; | ||
| } | ||
| // we only care about http and https connections. | ||
| // CORS takes care of http: trying to access file: URLs. | ||
| if ([gWhitelist schemeIsAllowed:[theUrl scheme]]) { | ||
| // if it FAILS the whitelist, we return TRUE, so we can fail the connection later | ||
| return ![gWhitelist URLIsAllowed:theUrl]; | ||
| } | ||
| } | ||
|
|
||
| return NO; | ||
| } | ||
|
|
||
| + (NSURLRequest*)canonicalRequestForRequest:(NSURLRequest*)request | ||
| { | ||
| // NSLog(@"%@ received %@", self, NSStringFromSelector(_cmd)); | ||
| return request; | ||
| } | ||
|
|
||
| - (void)startLoading | ||
| { | ||
| // NSLog(@"%@ received %@ - start", self, NSStringFromSelector(_cmd)); | ||
| NSURL* url = [[self request] URL]; | ||
|
|
||
| if ([[url path] isEqualToString:@"/!gap_exec"]) { | ||
| [self sendResponseWithResponseCode:200 data:nil mimeType:nil]; | ||
| return; | ||
| } else if ([[url absoluteString] hasPrefix:kCDVAssetsLibraryPrefixes]) { | ||
| ALAssetsLibraryAssetForURLResultBlock resultBlock = ^(ALAsset* asset) { | ||
| if (asset) { | ||
| // We have the asset! Get the data and send it along. | ||
| ALAssetRepresentation* assetRepresentation = [asset defaultRepresentation]; | ||
| NSString* MIMEType = (__bridge_transfer NSString*)UTTypeCopyPreferredTagWithClass((__bridge CFStringRef)[assetRepresentation UTI], kUTTagClassMIMEType); | ||
| Byte* buffer = (Byte*)malloc((unsigned long)[assetRepresentation size]); | ||
| NSUInteger bufferSize = [assetRepresentation getBytes:buffer fromOffset:0.0 length:(NSUInteger)[assetRepresentation size] error:nil]; | ||
| NSData* data = [NSData dataWithBytesNoCopy:buffer length:bufferSize freeWhenDone:YES]; | ||
| [self sendResponseWithResponseCode:200 data:data mimeType:MIMEType]; | ||
| } else { | ||
| // Retrieving the asset failed for some reason. Send an error. | ||
| [self sendResponseWithResponseCode:404 data:nil mimeType:nil]; | ||
| } | ||
| }; | ||
| ALAssetsLibraryAccessFailureBlock failureBlock = ^(NSError* error) { | ||
| // Retrieving the asset failed for some reason. Send an error. | ||
| [self sendResponseWithResponseCode:401 data:nil mimeType:nil]; | ||
| }; | ||
|
|
||
| ALAssetsLibrary* assetsLibrary = [[ALAssetsLibrary alloc] init]; | ||
| [assetsLibrary assetForURL:url resultBlock:resultBlock failureBlock:failureBlock]; | ||
| return; | ||
| } | ||
|
|
||
| NSString* body = [gWhitelist errorStringForURL:url]; | ||
| [self sendResponseWithResponseCode:401 data:[body dataUsingEncoding:NSASCIIStringEncoding] mimeType:nil]; | ||
| } | ||
|
|
||
| - (void)stopLoading | ||
| { | ||
| // do any cleanup here | ||
| } | ||
|
|
||
| + (BOOL)requestIsCacheEquivalent:(NSURLRequest*)requestA toRequest:(NSURLRequest*)requestB | ||
| { | ||
| return NO; | ||
| } | ||
|
|
||
| - (void)sendResponseWithResponseCode:(NSInteger)statusCode data:(NSData*)data mimeType:(NSString*)mimeType | ||
| { | ||
| if (mimeType == nil) { | ||
| mimeType = @"text/plain"; | ||
| } | ||
|
|
||
| NSHTTPURLResponse* response = [[NSHTTPURLResponse alloc] initWithURL:[[self request] URL] statusCode:statusCode HTTPVersion:@"HTTP/1.1" headerFields:@{@"Content-Type" : mimeType}]; | ||
|
|
||
| [[self client] URLProtocol:self didReceiveResponse:response cacheStoragePolicy:NSURLCacheStorageNotAllowed]; | ||
| if (data != nil) { | ||
| [[self client] URLProtocol:self didLoadData:data]; | ||
| } | ||
| [[self client] URLProtocolDidFinishLoading:self]; | ||
| } | ||
|
|
||
| @end |
| @@ -0,0 +1,27 @@ | ||
| /* | ||
| 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. | ||
| */ | ||
|
|
||
| #import <Foundation/Foundation.h> | ||
|
|
||
| @interface CDVUserAgentUtil : NSObject | ||
| + (NSString*)originalUserAgent; | ||
| + (void)acquireLock:(void (^)(NSInteger lockToken))block; | ||
| + (void)releaseLock:(NSInteger*)lockToken; | ||
| + (void)setUserAgent:(NSString*)value lockToken:(NSInteger)lockToken; | ||
| @end |
| @@ -0,0 +1,122 @@ | ||
| /* | ||
| 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. | ||
| */ | ||
|
|
||
| #import "CDVUserAgentUtil.h" | ||
|
|
||
| #import <UIKit/UIKit.h> | ||
|
|
||
| // #define VerboseLog NSLog | ||
| #define VerboseLog(...) do {} while (0) | ||
|
|
||
| static NSString* const kCdvUserAgentKey = @"Cordova-User-Agent"; | ||
| static NSString* const kCdvUserAgentVersionKey = @"Cordova-User-Agent-Version"; | ||
|
|
||
| static NSString* gOriginalUserAgent = nil; | ||
| static NSInteger gNextLockToken = 0; | ||
| static NSInteger gCurrentLockToken = 0; | ||
| static NSMutableArray* gPendingSetUserAgentBlocks = nil; | ||
|
|
||
| @implementation CDVUserAgentUtil | ||
|
|
||
| + (NSString*)originalUserAgent | ||
| { | ||
| if (gOriginalUserAgent == nil) { | ||
| [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(onAppLocaleDidChange:) | ||
| name:NSCurrentLocaleDidChangeNotification object:nil]; | ||
|
|
||
| NSUserDefaults* userDefaults = [NSUserDefaults standardUserDefaults]; | ||
| NSString* systemVersion = [[UIDevice currentDevice] systemVersion]; | ||
| NSString* localeStr = [[NSLocale currentLocale] localeIdentifier]; | ||
| // Record the model since simulator can change it without re-install (CB-5420). | ||
| NSString* model = [UIDevice currentDevice].model; | ||
| NSString* systemAndLocale = [NSString stringWithFormat:@"%@ %@ %@", model, systemVersion, localeStr]; | ||
|
|
||
| NSString* cordovaUserAgentVersion = [userDefaults stringForKey:kCdvUserAgentVersionKey]; | ||
| gOriginalUserAgent = [userDefaults stringForKey:kCdvUserAgentKey]; | ||
| BOOL cachedValueIsOld = ![systemAndLocale isEqualToString:cordovaUserAgentVersion]; | ||
|
|
||
| if ((gOriginalUserAgent == nil) || cachedValueIsOld) { | ||
| UIWebView* sampleWebView = [[UIWebView alloc] initWithFrame:CGRectZero]; | ||
| gOriginalUserAgent = [sampleWebView stringByEvaluatingJavaScriptFromString:@"navigator.userAgent"]; | ||
|
|
||
| [userDefaults setObject:gOriginalUserAgent forKey:kCdvUserAgentKey]; | ||
| [userDefaults setObject:systemAndLocale forKey:kCdvUserAgentVersionKey]; | ||
|
|
||
| [userDefaults synchronize]; | ||
| } | ||
| } | ||
| return gOriginalUserAgent; | ||
| } | ||
|
|
||
| + (void)onAppLocaleDidChange:(NSNotification*)notification | ||
| { | ||
| // TODO: We should figure out how to update the user-agent of existing UIWebViews when this happens. | ||
| // Maybe use the PDF bug (noted in setUserAgent:). | ||
| gOriginalUserAgent = nil; | ||
| } | ||
|
|
||
| + (void)acquireLock:(void (^)(NSInteger lockToken))block | ||
| { | ||
| if (gCurrentLockToken == 0) { | ||
| gCurrentLockToken = ++gNextLockToken; | ||
| VerboseLog(@"Gave lock %d", gCurrentLockToken); | ||
| block(gCurrentLockToken); | ||
| } else { | ||
| if (gPendingSetUserAgentBlocks == nil) { | ||
| gPendingSetUserAgentBlocks = [[NSMutableArray alloc] initWithCapacity:4]; | ||
| } | ||
| VerboseLog(@"Waiting for lock"); | ||
| [gPendingSetUserAgentBlocks addObject:block]; | ||
| } | ||
| } | ||
|
|
||
| + (void)releaseLock:(NSInteger*)lockToken | ||
| { | ||
| if (*lockToken == 0) { | ||
| return; | ||
| } | ||
| NSAssert(gCurrentLockToken == *lockToken, @"Got token %ld, expected %ld", (long)*lockToken, (long)gCurrentLockToken); | ||
|
|
||
| VerboseLog(@"Released lock %d", *lockToken); | ||
| if ([gPendingSetUserAgentBlocks count] > 0) { | ||
| void (^block)() = [gPendingSetUserAgentBlocks objectAtIndex:0]; | ||
| [gPendingSetUserAgentBlocks removeObjectAtIndex:0]; | ||
| gCurrentLockToken = ++gNextLockToken; | ||
| NSLog(@"Gave lock %ld", (long)gCurrentLockToken); | ||
| block(gCurrentLockToken); | ||
| } else { | ||
| gCurrentLockToken = 0; | ||
| } | ||
| *lockToken = 0; | ||
| } | ||
|
|
||
| + (void)setUserAgent:(NSString*)value lockToken:(NSInteger)lockToken | ||
| { | ||
| NSAssert(gCurrentLockToken == lockToken, @"Got token %ld, expected %ld", (long)lockToken, (long)gCurrentLockToken); | ||
| VerboseLog(@"User-Agent set to: %@", value); | ||
|
|
||
| // Setting the UserAgent must occur before a UIWebView is instantiated. | ||
| // It is read per instantiation, so it does not affect previously created views. | ||
| // Except! When a PDF is loaded, all currently active UIWebViews reload their | ||
| // User-Agent from the NSUserDefaults some time after the DidFinishLoad of the PDF bah! | ||
| NSDictionary* dict = [[NSDictionary alloc] initWithObjectsAndKeys:value, @"UserAgent", nil]; | ||
| [[NSUserDefaults standardUserDefaults] registerDefaults:dict]; | ||
| } | ||
|
|
||
| @end |
| @@ -0,0 +1,84 @@ | ||
| /* | ||
| 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. | ||
| */ | ||
|
|
||
| #import <UIKit/UIKit.h> | ||
| #import <Foundation/NSJSONSerialization.h> | ||
| #import "CDVAvailability.h" | ||
| #import "CDVInvokedUrlCommand.h" | ||
| #import "CDVCommandDelegate.h" | ||
| #import "CDVCommandQueue.h" | ||
| #import "CDVWhitelist.h" | ||
| #import "CDVScreenOrientationDelegate.h" | ||
| #import "CDVPlugin.h" | ||
|
|
||
| @interface CDVViewController : UIViewController <UIWebViewDelegate, CDVScreenOrientationDelegate>{ | ||
| @protected | ||
| id <CDVCommandDelegate> _commandDelegate; | ||
| @protected | ||
| CDVCommandQueue* _commandQueue; | ||
| NSString* _userAgent; | ||
| } | ||
|
|
||
| @property (nonatomic, strong) IBOutlet UIWebView* webView; | ||
|
|
||
| @property (nonatomic, readonly, strong) NSMutableDictionary* pluginObjects; | ||
| @property (nonatomic, readonly, strong) NSDictionary* pluginsMap; | ||
| @property (nonatomic, readonly, strong) NSMutableDictionary* settings; | ||
| @property (nonatomic, readonly, strong) NSXMLParser* configParser; | ||
| @property (nonatomic, readonly, strong) CDVWhitelist* whitelist; // readonly for public | ||
| @property (nonatomic, readonly, assign) BOOL loadFromString; | ||
|
|
||
| @property (nonatomic, readwrite, copy) NSString* wwwFolderName; | ||
| @property (nonatomic, readwrite, copy) NSString* startPage; | ||
| @property (nonatomic, readonly, strong) CDVCommandQueue* commandQueue; | ||
| @property (nonatomic, readonly, strong) id <CDVCommandDelegate> commandDelegate; | ||
|
|
||
| /** | ||
| The complete user agent that Cordova will use when sending web requests. | ||
| */ | ||
| @property (nonatomic, readonly) NSString* userAgent; | ||
|
|
||
| /** | ||
| The base user agent data that Cordova will use to build its user agent. If this | ||
| property isn't set, Cordova will use the standard web view user agent as its | ||
| base. | ||
| */ | ||
| @property (nonatomic, readwrite, copy) NSString* baseUserAgent; | ||
|
|
||
| + (NSDictionary*)getBundlePlist:(NSString*)plistName; | ||
| + (NSString*)applicationDocumentsDirectory; | ||
|
|
||
| - (void)printMultitaskingInfo; | ||
| - (void)createGapView; | ||
| - (UIWebView*)newCordovaViewWithFrame:(CGRect)bounds; | ||
|
|
||
| - (void)javascriptAlert:(NSString*)text; | ||
| - (NSString*)appURLScheme; | ||
|
|
||
| - (NSArray*)parseInterfaceOrientations:(NSArray*)orientations; | ||
| - (BOOL)supportsOrientation:(UIInterfaceOrientation)orientation; | ||
|
|
||
| - (id)getCommandInstance:(NSString*)pluginName; | ||
| - (void)registerPlugin:(CDVPlugin*)plugin withClassName:(NSString*)className; | ||
| - (void)registerPlugin:(CDVPlugin*)plugin withPluginName:(NSString*)pluginName; | ||
|
|
||
| - (BOOL)URLisAllowed:(NSURL*)url; | ||
| - (void)processOpenUrl:(NSURL*)url; | ||
|
|
||
| @end |
| @@ -0,0 +1,41 @@ | ||
| /* | ||
| 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. | ||
| */ | ||
|
|
||
| #import <UIKit/UIKit.h> | ||
| #import "CDVAvailability.h" | ||
|
|
||
| /** | ||
| * Distinguishes top-level navigations from sub-frame navigations. | ||
| * shouldStartLoadWithRequest is called for every request, but didStartLoad | ||
| * and didFinishLoad is called only for top-level navigations. | ||
| * Relevant bug: CB-2389 | ||
| */ | ||
| @interface CDVWebViewDelegate : NSObject <UIWebViewDelegate>{ | ||
| __weak NSObject <UIWebViewDelegate>* _delegate; | ||
| NSInteger _loadCount; | ||
| NSInteger _state; | ||
| NSInteger _curLoadToken; | ||
| NSInteger _loadStartPollCount; | ||
| } | ||
|
|
||
| - (id)initWithDelegate:(NSObject <UIWebViewDelegate>*)delegate; | ||
|
|
||
| - (BOOL)request:(NSURLRequest*)newRequest isEqualToRequestAfterStrippingFragments:(NSURLRequest*)originalRequest; | ||
|
|
||
| @end |
| @@ -0,0 +1,34 @@ | ||
| /* | ||
| 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. | ||
| */ | ||
|
|
||
| #import <Foundation/Foundation.h> | ||
|
|
||
| extern NSString* const kCDVDefaultWhitelistRejectionString; | ||
|
|
||
| @interface CDVWhitelist : NSObject | ||
|
|
||
| @property (nonatomic, copy) NSString* whitelistRejectionFormatString; | ||
|
|
||
| - (id)initWithArray:(NSArray*)array; | ||
| - (BOOL)schemeIsAllowed:(NSString*)scheme; | ||
| - (BOOL)URLIsAllowed:(NSURL*)url; | ||
| - (BOOL)URLIsAllowed:(NSURL*)url logFailure:(BOOL)logFailure; | ||
| - (NSString*)errorStringForURL:(NSURL*)url; | ||
|
|
||
| @end |
| @@ -0,0 +1,285 @@ | ||
| /* | ||
| 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. | ||
| */ | ||
|
|
||
| #import "CDVWhitelist.h" | ||
|
|
||
| NSString* const kCDVDefaultWhitelistRejectionString = @"ERROR whitelist rejection: url='%@'"; | ||
| NSString* const kCDVDefaultSchemeName = @"cdv-default-scheme"; | ||
|
|
||
| @interface CDVWhitelistPattern : NSObject { | ||
| @private | ||
| NSRegularExpression* _scheme; | ||
| NSRegularExpression* _host; | ||
| NSNumber* _port; | ||
| NSRegularExpression* _path; | ||
| } | ||
|
|
||
| + (NSString*)regexFromPattern:(NSString*)pattern allowWildcards:(bool)allowWildcards; | ||
| - (id)initWithScheme:(NSString*)scheme host:(NSString*)host port:(NSString*)port path:(NSString*)path; | ||
| - (bool)matches:(NSURL*)url; | ||
|
|
||
| @end | ||
|
|
||
| @implementation CDVWhitelistPattern | ||
|
|
||
| + (NSString*)regexFromPattern:(NSString*)pattern allowWildcards:(bool)allowWildcards | ||
| { | ||
| NSString* regex = [NSRegularExpression escapedPatternForString:pattern]; | ||
|
|
||
| if (allowWildcards) { | ||
| regex = [regex stringByReplacingOccurrencesOfString:@"\\*" withString:@".*"]; | ||
|
|
||
| /* [NSURL path] has the peculiarity that a trailing slash at the end of a path | ||
| * will be omitted. This regex tweak compensates for that. | ||
| */ | ||
| if ([regex hasSuffix:@"\\/.*"]) { | ||
| regex = [NSString stringWithFormat:@"%@(\\/.*)?", [regex substringToIndex:([regex length] - 4)]]; | ||
| } | ||
| } | ||
| return [NSString stringWithFormat:@"%@$", regex]; | ||
| } | ||
|
|
||
| - (id)initWithScheme:(NSString*)scheme host:(NSString*)host port:(NSString*)port path:(NSString*)path | ||
| { | ||
| self = [super init]; // Potentially change "self" | ||
| if (self) { | ||
| if ((scheme == nil) || [scheme isEqualToString:@"*"]) { | ||
| _scheme = nil; | ||
| } else { | ||
| _scheme = [NSRegularExpression regularExpressionWithPattern:[CDVWhitelistPattern regexFromPattern:scheme allowWildcards:NO] options:NSRegularExpressionCaseInsensitive error:nil]; | ||
| } | ||
| if ([host isEqualToString:@"*"]) { | ||
| _host = nil; | ||
| } else if ([host hasPrefix:@"*."]) { | ||
| _host = [NSRegularExpression regularExpressionWithPattern:[NSString stringWithFormat:@"([a-z0-9.-]*\\.)?%@", [CDVWhitelistPattern regexFromPattern:[host substringFromIndex:2] allowWildcards:false]] options:NSRegularExpressionCaseInsensitive error:nil]; | ||
| } else { | ||
| _host = [NSRegularExpression regularExpressionWithPattern:[CDVWhitelistPattern regexFromPattern:host allowWildcards:NO] options:NSRegularExpressionCaseInsensitive error:nil]; | ||
| } | ||
| if ((port == nil) || [port isEqualToString:@"*"]) { | ||
| _port = nil; | ||
| } else { | ||
| _port = [[NSNumber alloc] initWithInteger:[port integerValue]]; | ||
| } | ||
| if ((path == nil) || [path isEqualToString:@"/*"]) { | ||
| _path = nil; | ||
| } else { | ||
| _path = [NSRegularExpression regularExpressionWithPattern:[CDVWhitelistPattern regexFromPattern:path allowWildcards:YES] options:0 error:nil]; | ||
| } | ||
| } | ||
| return self; | ||
| } | ||
|
|
||
| - (bool)matches:(NSURL*)url | ||
| { | ||
| return (_scheme == nil || [_scheme numberOfMatchesInString:[url scheme] options:NSMatchingAnchored range:NSMakeRange(0, [[url scheme] length])]) && | ||
| (_host == nil || [_host numberOfMatchesInString:[url host] options:NSMatchingAnchored range:NSMakeRange(0, [[url host] length])]) && | ||
| (_port == nil || [[url port] isEqualToNumber:_port]) && | ||
| (_path == nil || [_path numberOfMatchesInString:[url path] options:NSMatchingAnchored range:NSMakeRange(0, [[url path] length])]) | ||
| ; | ||
| } | ||
|
|
||
| @end | ||
|
|
||
| @interface CDVWhitelist () | ||
|
|
||
| @property (nonatomic, readwrite, strong) NSMutableArray* whitelist; | ||
| @property (nonatomic, readwrite, strong) NSMutableSet* permittedSchemes; | ||
|
|
||
| - (void)addWhiteListEntry:(NSString*)pattern; | ||
|
|
||
| @end | ||
|
|
||
| @implementation CDVWhitelist | ||
|
|
||
| @synthesize whitelist, permittedSchemes, whitelistRejectionFormatString; | ||
|
|
||
| - (id)initWithArray:(NSArray*)array | ||
| { | ||
| self = [super init]; | ||
| if (self) { | ||
| self.whitelist = [[NSMutableArray alloc] init]; | ||
| self.permittedSchemes = [[NSMutableSet alloc] init]; | ||
| self.whitelistRejectionFormatString = kCDVDefaultWhitelistRejectionString; | ||
|
|
||
| for (NSString* pattern in array) { | ||
| [self addWhiteListEntry:pattern]; | ||
| } | ||
| } | ||
| return self; | ||
| } | ||
|
|
||
| - (BOOL)isIPv4Address:(NSString*)externalHost | ||
| { | ||
| // an IPv4 address has 4 octets b.b.b.b where b is a number between 0 and 255. | ||
| // for our purposes, b can also be the wildcard character '*' | ||
|
|
||
| // we could use a regex to solve this problem but then I would have two problems | ||
| // anyways, this is much clearer and maintainable | ||
| NSArray* octets = [externalHost componentsSeparatedByString:@"."]; | ||
| NSUInteger num_octets = [octets count]; | ||
|
|
||
| // quick check | ||
| if (num_octets != 4) { | ||
| return NO; | ||
| } | ||
|
|
||
| // restrict number parsing to 0-255 | ||
| NSNumberFormatter* numberFormatter = [[NSNumberFormatter alloc] init]; | ||
| [numberFormatter setMinimum:[NSNumber numberWithUnsignedInteger:0]]; | ||
| [numberFormatter setMaximum:[NSNumber numberWithUnsignedInteger:255]]; | ||
|
|
||
| // iterate through each octet, and test for a number between 0-255 or if it equals '*' | ||
| for (NSUInteger i = 0; i < num_octets; ++i) { | ||
| NSString* octet = [octets objectAtIndex:i]; | ||
|
|
||
| if ([octet isEqualToString:@"*"]) { // passes - check next octet | ||
| continue; | ||
| } else if ([numberFormatter numberFromString:octet] == nil) { // fails - not a number and not within our range, return | ||
| return NO; | ||
| } | ||
| } | ||
|
|
||
| return YES; | ||
| } | ||
|
|
||
| - (void)addWhiteListEntry:(NSString*)origin | ||
| { | ||
| if (self.whitelist == nil) { | ||
| return; | ||
| } | ||
|
|
||
| if ([origin isEqualToString:@"*"]) { | ||
| NSLog(@"Unlimited access to network resources"); | ||
| self.whitelist = nil; | ||
| self.permittedSchemes = nil; | ||
| } else { // specific access | ||
| NSRegularExpression* parts = [NSRegularExpression regularExpressionWithPattern:@"^((\\*|[A-Za-z-]+)://)?(((\\*\\.)?[^*/:]+)|\\*)?(:(\\d+))?(/.*)?" options:0 error:nil]; | ||
| NSTextCheckingResult* m = [parts firstMatchInString:origin options:NSMatchingAnchored range:NSMakeRange(0, [origin length])]; | ||
| if (m != nil) { | ||
| NSRange r; | ||
| NSString* scheme = nil; | ||
| r = [m rangeAtIndex:2]; | ||
| if (r.location != NSNotFound) { | ||
| scheme = [origin substringWithRange:r]; | ||
| } | ||
|
|
||
| NSString* host = nil; | ||
| r = [m rangeAtIndex:3]; | ||
| if (r.location != NSNotFound) { | ||
| host = [origin substringWithRange:r]; | ||
| } | ||
|
|
||
| // Special case for two urls which are allowed to have empty hosts | ||
| if (([scheme isEqualToString:@"file"] || [scheme isEqualToString:@"content"]) && (host == nil)) { | ||
| host = @"*"; | ||
| } | ||
|
|
||
| NSString* port = nil; | ||
| r = [m rangeAtIndex:7]; | ||
| if (r.location != NSNotFound) { | ||
| port = [origin substringWithRange:r]; | ||
| } | ||
|
|
||
| NSString* path = nil; | ||
| r = [m rangeAtIndex:8]; | ||
| if (r.location != NSNotFound) { | ||
| path = [origin substringWithRange:r]; | ||
| } | ||
|
|
||
| if (scheme == nil) { | ||
| // XXX making it stupid friendly for people who forget to include protocol/SSL | ||
| [self.whitelist addObject:[[CDVWhitelistPattern alloc] initWithScheme:@"http" host:host port:port path:path]]; | ||
| [self.whitelist addObject:[[CDVWhitelistPattern alloc] initWithScheme:@"https" host:host port:port path:path]]; | ||
| } else { | ||
| [self.whitelist addObject:[[CDVWhitelistPattern alloc] initWithScheme:scheme host:host port:port path:path]]; | ||
| } | ||
|
|
||
| if (self.permittedSchemes != nil) { | ||
| if ([scheme isEqualToString:@"*"]) { | ||
| self.permittedSchemes = nil; | ||
| } else if (scheme != nil) { | ||
| [self.permittedSchemes addObject:scheme]; | ||
| } | ||
| } | ||
| } | ||
| } | ||
| } | ||
|
|
||
| - (BOOL)schemeIsAllowed:(NSString*)scheme | ||
| { | ||
| if ([scheme isEqualToString:@"http"] || | ||
| [scheme isEqualToString:@"https"] || | ||
| [scheme isEqualToString:@"ftp"] || | ||
| [scheme isEqualToString:@"ftps"]) { | ||
| return YES; | ||
| } | ||
|
|
||
| return (self.permittedSchemes == nil) || [self.permittedSchemes containsObject:scheme]; | ||
| } | ||
|
|
||
| - (BOOL)URLIsAllowed:(NSURL*)url | ||
| { | ||
| return [self URLIsAllowed:url logFailure:YES]; | ||
| } | ||
|
|
||
| - (BOOL)URLIsAllowed:(NSURL*)url logFailure:(BOOL)logFailure | ||
| { | ||
| // Shortcut acceptance: Are all urls whitelisted ("*" in whitelist)? | ||
| if (whitelist == nil) { | ||
| return YES; | ||
| } | ||
|
|
||
| // Shortcut rejection: Check that the scheme is supported | ||
| NSString* scheme = [[url scheme] lowercaseString]; | ||
| if (![self schemeIsAllowed:scheme]) { | ||
| if (logFailure) { | ||
| NSLog(@"%@", [self errorStringForURL:url]); | ||
| } | ||
| return NO; | ||
| } | ||
|
|
||
| // http[s] and ftp[s] should also validate against the common set in the kCDVDefaultSchemeName list | ||
| if ([scheme isEqualToString:@"http"] || [scheme isEqualToString:@"https"] || [scheme isEqualToString:@"ftp"] || [scheme isEqualToString:@"ftps"]) { | ||
| NSURL* newUrl = [NSURL URLWithString:[NSString stringWithFormat:@"%@://%@%@", kCDVDefaultSchemeName, [url host], [[url path] stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding]]]; | ||
| // If it is allowed, we are done. If not, continue to check for the actual scheme-specific list | ||
| if ([self URLIsAllowed:newUrl logFailure:NO]) { | ||
| return YES; | ||
| } | ||
| } | ||
|
|
||
| // Check the url against patterns in the whitelist | ||
| for (CDVWhitelistPattern* p in self.whitelist) { | ||
| if ([p matches:url]) { | ||
| return YES; | ||
| } | ||
| } | ||
|
|
||
| if (logFailure) { | ||
| NSLog(@"%@", [self errorStringForURL:url]); | ||
| } | ||
| // if we got here, the url host is not in the white-list, do nothing | ||
| return NO; | ||
| } | ||
|
|
||
| - (NSString*)errorStringForURL:(NSURL*)url | ||
| { | ||
| return [NSString stringWithFormat:self.whitelistRejectionFormatString, [url absoluteString]]; | ||
| } | ||
|
|
||
| @end |
| @@ -0,0 +1,27 @@ | ||
| /* | ||
| 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. | ||
| */ | ||
|
|
||
| #import <Foundation/Foundation.h> | ||
| #import "CDVAvailabilityDeprecated.h" | ||
|
|
||
| @interface NSArray (Comparisons) | ||
|
|
||
| - (id)objectAtIndex:(NSUInteger)index withDefault:(id)aDefault CDV_DEPRECATED(3.8 .0, "Use [command argumentAtIndex] instead."); | ||
|
|
||
| @end |
| @@ -0,0 +1,43 @@ | ||
| /* | ||
| 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. | ||
| */ | ||
|
|
||
| #import "NSArray+Comparisons.h" | ||
|
|
||
| @implementation NSArray (Comparisons) | ||
|
|
||
| - (id)objectAtIndex:(NSUInteger)index withDefault:(id)aDefault | ||
| { | ||
| id obj = nil; | ||
|
|
||
| @try { | ||
| if (index < [self count]) { | ||
| obj = [self objectAtIndex:index]; | ||
| } | ||
| if ((obj == [NSNull null]) || (obj == nil)) { | ||
| return aDefault; | ||
| } | ||
| } | ||
| @catch(NSException* exception) { | ||
| NSLog(@"Exception - Name: %@ Reason: %@", [exception name], [exception reason]); | ||
| } | ||
|
|
||
| return obj; | ||
| } | ||
|
|
||
| @end |