You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@cordova.apache.org by ia...@apache.org on 2015/03/19 17:56:18 UTC
[29/50] [abbrv] ios commit: CB-8690 - Group files into folders in
CordovaLib/Classes
http://git-wip-us.apache.org/repos/asf/cordova-ios/blob/d58fa4aa/CordovaLib/Classes/Public/CDVViewController.m
----------------------------------------------------------------------
diff --git a/CordovaLib/Classes/Public/CDVViewController.m b/CordovaLib/Classes/Public/CDVViewController.m
new file mode 100644
index 0000000..6b40a84
--- /dev/null
+++ b/CordovaLib/Classes/Public/CDVViewController.m
@@ -0,0 +1,878 @@
+/*
+ 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 <objc/message.h>
+#import "CDV.h"
+#import "CDVPlugin+Private.h"
+#import "CDVUIWebViewDelegate.h"
+#import "CDVConfigParser.h"
+#import "CDVUserAgentUtil.h"
+#import <AVFoundation/AVFoundation.h>
+#import "NSDictionary+CordovaPreferences.h"
+#import "CDVHandleOpenURL.h"
+#import "CDVCommandDelegateImpl.h"
+
+#define degreesToRadian(x) (M_PI * (x) / 180.0)
+
+@interface CDVViewController () {
+ NSInteger _userAgentLockToken;
+}
+
+@property (nonatomic, readwrite, strong) NSXMLParser* configParser;
+@property (nonatomic, readwrite, strong) NSMutableDictionary* settings;
+@property (nonatomic, readwrite, strong) CDVWhitelist* whitelist;
+@property (nonatomic, readwrite, strong) NSMutableDictionary* pluginObjects;
+@property (nonatomic, readwrite, strong) NSMutableArray* startupPluginNames;
+@property (nonatomic, readwrite, strong) NSDictionary* pluginsMap;
+@property (nonatomic, readwrite, strong) NSArray* supportedOrientations;
+@property (nonatomic, readwrite, assign) BOOL loadFromString;
+@property (nonatomic, readwrite, strong) id <CDVWebViewEngineProtocol> webViewEngine;
+
+@property (readwrite, assign) BOOL initialized;
+
+@property (atomic, strong) NSURL* openURL;
+
+@end
+
+@implementation CDVViewController
+
+@synthesize supportedOrientations;
+@synthesize pluginObjects, pluginsMap, whitelist, startupPluginNames;
+@synthesize configParser, settings, loadFromString;
+@synthesize wwwFolderName, startPage, initialized, openURL, baseUserAgent;
+@synthesize commandDelegate = _commandDelegate;
+@synthesize commandQueue = _commandQueue;
+@synthesize webViewEngine = _webViewEngine;
+@dynamic webView;
+
+- (void)__init
+{
+ if ((self != nil) && !self.initialized) {
+ _commandQueue = [[CDVCommandQueue alloc] initWithViewController:self];
+ _commandDelegate = [[CDVCommandDelegateImpl alloc] initWithViewController:self];
+ [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(onAppWillTerminate:)
+ name:UIApplicationWillTerminateNotification object:nil];
+ [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(onAppWillResignActive:)
+ name:UIApplicationWillResignActiveNotification object:nil];
+ [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(onAppDidBecomeActive:)
+ name:UIApplicationDidBecomeActiveNotification object:nil];
+
+ [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(onAppWillEnterForeground:)
+ name:UIApplicationWillEnterForegroundNotification object:nil];
+ [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(onAppDidEnterBackground:)
+ name:UIApplicationDidEnterBackgroundNotification object:nil];
+
+ // read from UISupportedInterfaceOrientations (or UISupportedInterfaceOrientations~iPad, if its iPad) from -Info.plist
+ self.supportedOrientations = [self parseInterfaceOrientations:
+ [[[NSBundle mainBundle] infoDictionary] objectForKey:@"UISupportedInterfaceOrientations"]];
+
+ [self printVersion];
+ [self printMultitaskingInfo];
+ [self printPlatformVersionWarning];
+ self.initialized = YES;
+
+ // load config.xml settings
+ [self loadSettings];
+ }
+}
+
+- (id)initWithNibName:(NSString*)nibNameOrNil bundle:(NSBundle*)nibBundleOrNil
+{
+ self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
+ [self __init];
+ return self;
+}
+
+- (id)initWithCoder:(NSCoder*)aDecoder
+{
+ self = [super initWithCoder:aDecoder];
+ [self __init];
+ return self;
+}
+
+- (id)init
+{
+ self = [super init];
+ [self __init];
+ return self;
+}
+
+- (void)viewWillAppear:(BOOL)animated
+{
+ [super viewWillAppear:animated];
+}
+
+- (void)viewWillDisappear:(BOOL)animated
+{
+ [super viewWillDisappear:animated];
+}
+
+- (void)printVersion
+{
+ NSLog(@"Apache Cordova native platform version %@ is starting.", CDV_VERSION);
+}
+
+- (void)printPlatformVersionWarning
+{
+ if (!IsAtLeastiOSVersion(@"7.0")) {
+ NSLog(@"CRITICAL: For Cordova 4.0.0 and above, you will need to upgrade to at least iOS 7.0 or greater. Your current version of iOS is %@.",
+ [[UIDevice currentDevice] systemVersion]
+ );
+ }
+}
+
+- (void)printMultitaskingInfo
+{
+ UIDevice* device = [UIDevice currentDevice];
+ BOOL backgroundSupported = NO;
+
+ if ([device respondsToSelector:@selector(isMultitaskingSupported)]) {
+ backgroundSupported = device.multitaskingSupported;
+ }
+
+ NSNumber* exitsOnSuspend = [[NSBundle mainBundle] objectForInfoDictionaryKey:@"UIApplicationExitsOnSuspend"];
+ if (exitsOnSuspend == nil) { // if it's missing, it should be NO (i.e. multi-tasking on by default)
+ exitsOnSuspend = [NSNumber numberWithBool:NO];
+ }
+
+ NSLog(@"Multi-tasking -> Device: %@, App: %@", (backgroundSupported ? @"YES" : @"NO"), (![exitsOnSuspend intValue]) ? @"YES" : @"NO");
+}
+
+- (BOOL)URLisAllowed:(NSURL*)url
+{
+ if (self.whitelist == nil) {
+ return YES;
+ }
+
+ return [self.whitelist URLIsAllowed:url];
+}
+
+- (void)loadSettings
+{
+ CDVConfigParser* delegate = [[CDVConfigParser alloc] init];
+
+ // read from config.xml in the app bundle
+ NSString* path = [[NSBundle mainBundle] pathForResource:@"config" ofType:@"xml"];
+
+ if (![[NSFileManager defaultManager] fileExistsAtPath:path]) {
+ NSAssert(NO, @"ERROR: config.xml does not exist. Please run cordova-ios/bin/cordova_plist_to_config_xml path/to/project.");
+ return;
+ }
+
+ NSURL* url = [NSURL fileURLWithPath:path];
+
+ self.configParser = [[NSXMLParser alloc] initWithContentsOfURL:url];
+ if (self.configParser == nil) {
+ NSLog(@"Failed to initialize XML parser.");
+ return;
+ }
+ [self.configParser setDelegate:((id < NSXMLParserDelegate >)delegate)];
+ [self.configParser parse];
+
+ // Get the plugin dictionary, whitelist and settings from the delegate.
+ self.pluginsMap = delegate.pluginsDict;
+ self.startupPluginNames = delegate.startupPluginNames;
+ self.whitelist = [[CDVWhitelist alloc] initWithArray:delegate.whitelistHosts];
+ self.settings = delegate.settings;
+
+ // And the start folder/page.
+ self.wwwFolderName = @"www";
+ self.startPage = delegate.startPage;
+ if (self.startPage == nil) {
+ self.startPage = @"index.html";
+ }
+
+ // Initialize the plugin objects dict.
+ self.pluginObjects = [[NSMutableDictionary alloc] initWithCapacity:20];
+}
+
+- (NSURL*)appUrl
+{
+ NSURL* appURL = nil;
+
+ if ([self.startPage rangeOfString:@"://"].location != NSNotFound) {
+ appURL = [NSURL URLWithString:self.startPage];
+ } else if ([self.wwwFolderName rangeOfString:@"://"].location != NSNotFound) {
+ appURL = [NSURL URLWithString:[NSString stringWithFormat:@"%@/%@", self.wwwFolderName, self.startPage]];
+ } else {
+ // CB-3005 strip parameters from start page to check if page exists in resources
+ NSURL* startURL = [NSURL URLWithString:self.startPage];
+ NSString* startFilePath = [self.commandDelegate pathForResource:[startURL path]];
+
+ if (startFilePath == nil) {
+ self.loadFromString = YES;
+ appURL = nil;
+ } else {
+ appURL = [NSURL fileURLWithPath:startFilePath];
+ // CB-3005 Add on the query params or fragment.
+ NSString* startPageNoParentDirs = self.startPage;
+ NSRange r = [startPageNoParentDirs rangeOfCharacterFromSet:[NSCharacterSet characterSetWithCharactersInString:@"?#"] options:0];
+ if (r.location != NSNotFound) {
+ NSString* queryAndOrFragment = [self.startPage substringFromIndex:r.location];
+ appURL = [NSURL URLWithString:queryAndOrFragment relativeToURL:appURL];
+ }
+ }
+ }
+
+ return appURL;
+}
+
+- (NSURL*)errorUrl
+{
+ NSURL* errorURL = nil;
+
+ id setting = [self.settings cordovaSettingForKey:@"ErrorUrl"];
+
+ if (setting) {
+ NSString* errorUrlString = (NSString*)setting;
+ if ([errorUrlString rangeOfString:@"://"].location != NSNotFound) {
+ errorURL = [NSURL URLWithString:errorUrlString];
+ } else {
+ NSURL* url = [NSURL URLWithString:(NSString*)setting];
+ NSString* errorFilePath = [self.commandDelegate pathForResource:[url path]];
+ if (errorFilePath) {
+ errorURL = [NSURL fileURLWithPath:errorFilePath];
+ }
+ }
+ }
+
+ return errorURL;
+}
+
+- (UIView*)webView
+{
+ if (self.webViewEngine != nil) {
+ return self.webViewEngine.engineWebView;
+ }
+
+ return nil;
+}
+
+// Implement viewDidLoad to do additional setup after loading the view, typically from a nib.
+- (void)viewDidLoad
+{
+ [super viewDidLoad];
+
+ // // Fix the iOS 5.1 SECURITY_ERR bug (CB-347), this must be before the webView is instantiated ////
+
+ NSString* backupWebStorageType = @"cloud"; // default value
+
+ id backupWebStorage = [self.settings cordovaSettingForKey:@"BackupWebStorage"];
+ if ([backupWebStorage isKindOfClass:[NSString class]]) {
+ backupWebStorageType = backupWebStorage;
+ }
+ [self.settings setCordovaSetting:backupWebStorageType forKey:@"BackupWebStorage"];
+
+ [CDVLocalStorage __fixupDatabaseLocationsWithBackupType:backupWebStorageType];
+
+ // // Instantiate the WebView ///////////////
+
+ if (!self.webView) {
+ [self createGapView];
+ }
+
+ // register this viewcontroller with the NSURLProtocol, only after the User-Agent is set
+ [CDVURLProtocol registerViewController:self];
+
+ // /////////////////
+
+ /*
+ * Fire up CDVLocalStorage to work-around WebKit storage limitations: on all iOS 5.1+ versions for local-only backups, but only needed on iOS 5.1 for cloud backup.
+ With minimum iOS 6/7 supported, only first clause applies.
+ */
+ if ([backupWebStorageType isEqualToString:@"local"]) {
+ NSString* localStorageFeatureName = @"localstorage";
+ if ([self.pluginsMap objectForKey:localStorageFeatureName]) { // plugin specified in config
+ [self.startupPluginNames addObject:localStorageFeatureName];
+ }
+ }
+
+ if ([self.startupPluginNames count] > 0) {
+ [CDVTimer start:@"TotalPluginStartup"];
+
+ for (NSString* pluginName in self.startupPluginNames) {
+ [CDVTimer start:pluginName];
+ [self getCommandInstance:pluginName];
+ [CDVTimer stop:pluginName];
+ }
+
+ [CDVTimer stop:@"TotalPluginStartup"];
+ }
+
+ // /////////////////
+ NSURL* appURL = [self appUrl];
+
+ [CDVUserAgentUtil acquireLock:^(NSInteger lockToken) {
+ _userAgentLockToken = lockToken;
+ [CDVUserAgentUtil setUserAgent:self.userAgent lockToken:lockToken];
+ if (appURL) {
+ NSURLRequest* appReq = [NSURLRequest requestWithURL:appURL cachePolicy:NSURLRequestUseProtocolCachePolicy timeoutInterval:20.0];
+ [self.webViewEngine loadRequest:appReq];
+ } else {
+ NSString* loadErr = [NSString stringWithFormat:@"ERROR: Start Page at '%@/%@' was not found.", self.wwwFolderName, self.startPage];
+ NSLog(@"%@", loadErr);
+
+ NSURL* errorUrl = [self errorUrl];
+ if (errorUrl) {
+ errorUrl = [NSURL URLWithString:[NSString stringWithFormat:@"?error=%@", [loadErr stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding]] relativeToURL:errorUrl];
+ NSLog(@"%@", [errorUrl absoluteString]);
+ [self.webViewEngine loadRequest:[NSURLRequest requestWithURL:errorUrl]];
+ } else {
+ NSString* html = [NSString stringWithFormat:@"<html><body> %@ </body></html>", loadErr];
+ [self.webViewEngine loadHTMLString:html baseURL:nil];
+ }
+ }
+ }];
+}
+
+- (NSArray*)parseInterfaceOrientations:(NSArray*)orientations
+{
+ NSMutableArray* result = [[NSMutableArray alloc] init];
+
+ if (orientations != nil) {
+ NSEnumerator* enumerator = [orientations objectEnumerator];
+ NSString* orientationString;
+
+ while (orientationString = [enumerator nextObject]) {
+ if ([orientationString isEqualToString:@"UIInterfaceOrientationPortrait"]) {
+ [result addObject:[NSNumber numberWithInt:UIInterfaceOrientationPortrait]];
+ } else if ([orientationString isEqualToString:@"UIInterfaceOrientationPortraitUpsideDown"]) {
+ [result addObject:[NSNumber numberWithInt:UIInterfaceOrientationPortraitUpsideDown]];
+ } else if ([orientationString isEqualToString:@"UIInterfaceOrientationLandscapeLeft"]) {
+ [result addObject:[NSNumber numberWithInt:UIInterfaceOrientationLandscapeLeft]];
+ } else if ([orientationString isEqualToString:@"UIInterfaceOrientationLandscapeRight"]) {
+ [result addObject:[NSNumber numberWithInt:UIInterfaceOrientationLandscapeRight]];
+ }
+ }
+ }
+
+ // default
+ if ([result count] == 0) {
+ [result addObject:[NSNumber numberWithInt:UIInterfaceOrientationPortrait]];
+ }
+
+ return result;
+}
+
+- (NSInteger)mapIosOrientationToJsOrientation:(UIInterfaceOrientation)orientation
+{
+ switch (orientation) {
+ case UIInterfaceOrientationPortraitUpsideDown:
+ return 180;
+
+ case UIInterfaceOrientationLandscapeLeft:
+ return -90;
+
+ case UIInterfaceOrientationLandscapeRight:
+ return 90;
+
+ case UIInterfaceOrientationPortrait:
+ return 0;
+
+ default:
+ return 0;
+ }
+}
+
+- (void)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation completionHandler:(void (^)(BOOL))completionHandler
+{
+ // First, ask the webview via JS if it supports the new orientation
+ NSString* jsCall = [NSString stringWithFormat:
+ @"window.shouldRotateToOrientation && window.shouldRotateToOrientation(%ld);"
+ , (long)[self mapIosOrientationToJsOrientation:interfaceOrientation]];
+ __weak CDVViewController* weakSelf = self;
+
+ [_webViewEngine evaluateJavaScript:jsCall completionHandler:^(NSString* obj, NSError* error) {
+ if ([obj length] > 0) {
+ completionHandler([obj boolValue]);
+ } else {
+ // if js did not handle the new orientation (no return value), use values from the plist (via supportedOrientations)
+ completionHandler([weakSelf supportsOrientation:interfaceOrientation]);
+ }
+ }];
+}
+
+- (BOOL)shouldAutorotate
+{
+ return YES;
+}
+
+- (NSUInteger)supportedInterfaceOrientations
+{
+ NSUInteger ret = 0;
+
+ if ([self shouldAutorotateToInterfaceOrientation:UIInterfaceOrientationPortrait]) {
+ ret = ret | (1 << UIInterfaceOrientationPortrait);
+ }
+ if ([self shouldAutorotateToInterfaceOrientation:UIInterfaceOrientationPortraitUpsideDown]) {
+ ret = ret | (1 << UIInterfaceOrientationPortraitUpsideDown);
+ }
+ if ([self shouldAutorotateToInterfaceOrientation:UIInterfaceOrientationLandscapeRight]) {
+ ret = ret | (1 << UIInterfaceOrientationLandscapeRight);
+ }
+ if ([self shouldAutorotateToInterfaceOrientation:UIInterfaceOrientationLandscapeLeft]) {
+ ret = ret | (1 << UIInterfaceOrientationLandscapeLeft);
+ }
+
+ return ret;
+}
+
+- (BOOL)supportsOrientation:(UIInterfaceOrientation)orientation
+{
+ return [self.supportedOrientations containsObject:[NSNumber numberWithInt:orientation]];
+}
+
+- (UIView*)newCordovaViewWithFrame:(CGRect)bounds
+{
+ NSString* defaultWebViewEngineClass = @"CDVUIWebViewEngine";
+ NSString* webViewEngineClass = [self.settings cordovaSettingForKey:@"CordovaWebViewEngine"];
+
+ if (!webViewEngineClass) {
+ webViewEngineClass = defaultWebViewEngineClass;
+ }
+
+ // Find webViewEngine
+ if (NSClassFromString(webViewEngineClass)) {
+ self.webViewEngine = [[NSClassFromString(webViewEngineClass) alloc] initWithFrame:bounds];
+ // if a webView engine returns nil (not supported by the current iOS version) or doesn't conform to the protocol, we use UIWebView
+ if (!self.webViewEngine || ![self.webViewEngine conformsToProtocol:@protocol(CDVWebViewEngineProtocol)]) {
+ self.webViewEngine = [[NSClassFromString(defaultWebViewEngineClass) alloc] initWithFrame:bounds];
+ }
+ } else {
+ self.webViewEngine = [[NSClassFromString(defaultWebViewEngineClass) alloc] initWithFrame:bounds];
+ }
+
+ if ([self.webViewEngine isKindOfClass:[CDVPlugin class]]) {
+ [self registerPlugin:(CDVPlugin*)self.webViewEngine withClassName:webViewEngineClass];
+ }
+
+ return self.webViewEngine.engineWebView;
+}
+
+- (NSString*)userAgent
+{
+ if (_userAgent == nil) {
+ NSString* localBaseUserAgent;
+ if (self.baseUserAgent != nil) {
+ localBaseUserAgent = self.baseUserAgent;
+ } else {
+ localBaseUserAgent = [CDVUserAgentUtil originalUserAgent];
+ }
+ // Use our address as a unique number to append to the User-Agent.
+ _userAgent = [NSString stringWithFormat:@"%@ (%lld)", localBaseUserAgent, (long long)self];
+ }
+ return _userAgent;
+}
+
+- (void)createGapView
+{
+ CGRect webViewBounds = self.view.bounds;
+
+ webViewBounds.origin = self.view.bounds.origin;
+
+ UIView* view = [self newCordovaViewWithFrame:webViewBounds];
+
+ view.autoresizingMask = (UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight);
+ [self.view addSubview:view];
+ [self.view sendSubviewToBack:view];
+}
+
+- (void)didReceiveMemoryWarning
+{
+ // iterate through all the plugin objects, and call hasPendingOperation
+ // if at least one has a pending operation, we don't call [super didReceiveMemoryWarning]
+
+ NSEnumerator* enumerator = [self.pluginObjects objectEnumerator];
+ CDVPlugin* plugin;
+
+ BOOL doPurge = YES;
+
+ while ((plugin = [enumerator nextObject])) {
+ if (plugin.hasPendingOperation) {
+ NSLog(@"Plugin '%@' has a pending operation, memory purge is delayed for didReceiveMemoryWarning.", NSStringFromClass([plugin class]));
+ doPurge = NO;
+ }
+ }
+
+ if (doPurge) {
+ // Releases the view if it doesn't have a superview.
+ [super didReceiveMemoryWarning];
+ }
+
+ // Release any cached data, images, etc. that aren't in use.
+}
+
+- (void)viewDidUnload
+{
+ // Release any retained subviews of the main view.
+ // e.g. self.myOutlet = nil;
+
+ [CDVUserAgentUtil releaseLock:&_userAgentLockToken];
+
+ [super viewDidUnload];
+}
+
+#pragma mark UIWebViewDelegate
+
+/**
+ When web application loads Add stuff to the DOM, mainly the user-defined settings from the Settings.plist file, and
+ the device's data such as device ID, platform version, etc.
+ */
+- (void)webViewDidStartLoad:(UIWebView*)theWebView
+{
+ NSLog(@"Resetting plugins due to page load.");
+ [_commandQueue resetRequestId];
+ [[NSNotificationCenter defaultCenter] postNotification:[NSNotification notificationWithName:CDVPluginResetNotification object:self.webView]];
+}
+
+/**
+ Called when the webview finishes loading. This stops the activity view.
+ */
+- (void)webViewDidFinishLoad:(UIWebView*)theWebView
+{
+ NSLog(@"Finished load of: %@", theWebView.request.URL);
+ // It's safe to release the lock even if this is just a sub-frame that's finished loading.
+ [CDVUserAgentUtil releaseLock:&_userAgentLockToken];
+
+ /*
+ * Hide the Top Activity THROBBER in the Battery Bar
+ */
+ [[UIApplication sharedApplication] setNetworkActivityIndicatorVisible:NO];
+
+ [[NSNotificationCenter defaultCenter] postNotification:[NSNotification notificationWithName:CDVPageDidLoadNotification object:self.webView]];
+}
+
+- (void)webView:(UIWebView*)theWebView didFailLoadWithError:(NSError*)error
+{
+ [CDVUserAgentUtil releaseLock:&_userAgentLockToken];
+
+ NSString* message = [NSString stringWithFormat:@"Failed to load webpage with error: %@", [error localizedDescription]];
+ NSLog(@"%@", message);
+
+ NSURL* errorUrl = [self errorUrl];
+ if (errorUrl) {
+ errorUrl = [NSURL URLWithString:[NSString stringWithFormat:@"?error=%@", [message stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding]] relativeToURL:errorUrl];
+ NSLog(@"%@", [errorUrl absoluteString]);
+ [theWebView loadRequest:[NSURLRequest requestWithURL:errorUrl]];
+ }
+}
+
+- (BOOL)webView:(UIWebView*)theWebView shouldStartLoadWithRequest:(NSURLRequest*)request navigationType:(UIWebViewNavigationType)navigationType
+{
+ NSURL* url = [request URL];
+
+ /*
+ * Execute any commands queued with cordova.exec() on the JS side.
+ * The part of the URL after gap:// is irrelevant.
+ */
+ if ([[url scheme] isEqualToString:@"gap"]) {
+ [_commandQueue fetchCommandsFromJs];
+ // The delegate is called asynchronously in this case, so we don't have to use
+ // flushCommandQueueWithDelayedJs (setTimeout(0)) as we do with hash changes.
+ [_commandQueue executePending];
+ return NO;
+ }
+
+ if ([[url fragment] hasPrefix:@"%01"] || [[url fragment] hasPrefix:@"%02"]) {
+ // Delegate is called *immediately* for hash changes. This means that any
+ // calls to stringByEvaluatingJavascriptFromString will occur in the middle
+ // of an existing (paused) call stack. This doesn't cause errors, but may
+ // be unexpected to callers (exec callbacks will be called before exec() even
+ // returns). To avoid this, we do not do any synchronous JS evals by using
+ // flushCommandQueueWithDelayedJs.
+ NSString* inlineCommands = [[url fragment] substringFromIndex:3];
+ if ([inlineCommands length] == 0) {
+ // Reach in right away since the WebCore / Main thread are already synchronized.
+ [_commandQueue fetchCommandsFromJs];
+ } else {
+ inlineCommands = [inlineCommands stringByReplacingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
+ [_commandQueue enqueueCommandBatch:inlineCommands];
+ }
+ // Switch these for minor performance improvements, and to really live on the wild side.
+ // Callbacks will occur in the middle of the location.hash = ... statement!
+ [(CDVCommandDelegateImpl*)_commandDelegate flushCommandQueueWithDelayedJs];
+ // [_commandQueue executePending];
+
+ // Although we return NO, the hash change does end up taking effect.
+ return NO;
+ }
+
+ /*
+ * Give plugins the chance to handle the url
+ */
+ for (NSString* pluginName in pluginObjects) {
+ CDVPlugin* plugin = [pluginObjects objectForKey:pluginName];
+ SEL selector = NSSelectorFromString(@"shouldOverrideLoadWithRequest:navigationType:");
+ if ([plugin respondsToSelector:selector]) {
+ if (((BOOL (*)(id, SEL, id, int))objc_msgSend)(plugin, selector, request, navigationType) == YES) {
+ return NO;
+ }
+ }
+ }
+
+ /*
+ * If a URL is being loaded that's a file/http/https URL, just load it internally
+ */
+ if ([url isFileURL]) {
+ return YES;
+ }
+
+ /*
+ * If we loaded the HTML from a string, we let the app handle it
+ */
+ else if (self.loadFromString == YES) {
+ self.loadFromString = NO;
+ return YES;
+ }
+
+ /*
+ * all tel: scheme urls we let the UIWebview handle it using the default behavior
+ */
+ else if ([[url scheme] isEqualToString:@"tel"]) {
+ return YES;
+ }
+
+ /*
+ * all about: scheme urls are not handled
+ */
+ else if ([[url scheme] isEqualToString:@"about"]) {
+ return NO;
+ }
+
+ /*
+ * all data: scheme urls are handled
+ */
+ else if ([[url scheme] isEqualToString:@"data"]) {
+ return YES;
+ }
+
+ /*
+ * Handle all other types of urls (tel:, sms:), and requests to load a url in the main webview.
+ */
+ else {
+ if ([self.whitelist schemeIsAllowed:[url scheme]]) {
+ return [self.whitelist URLIsAllowed:url];
+ } else {
+ if ([[UIApplication sharedApplication] canOpenURL:url]) {
+ [[UIApplication sharedApplication] openURL:url];
+ } else { // handle any custom schemes to plugins
+ [[NSNotificationCenter defaultCenter] postNotification:[NSNotification notificationWithName:CDVPluginHandleOpenURLNotification object:url]];
+ }
+ }
+
+ return NO;
+ }
+
+ return YES;
+}
+
+#pragma mark GapHelpers
+
+- (void)javascriptAlert:(NSString*)text
+{
+ NSString* jsString = [NSString stringWithFormat:@"alert('%@');", text];
+
+ [self.commandDelegate evalJs:jsString];
+}
+
++ (NSString*)applicationDocumentsDirectory
+{
+ NSArray* paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
+ NSString* basePath = (([paths count] > 0) ? ([paths objectAtIndex:0]) : nil);
+
+ return basePath;
+}
+
+#pragma mark CordovaCommands
+
+- (void)registerPlugin:(CDVPlugin*)plugin withClassName:(NSString*)className
+{
+ if ([plugin respondsToSelector:@selector(setViewController:)]) {
+ [plugin setViewController:self];
+ }
+
+ if ([plugin respondsToSelector:@selector(setCommandDelegate:)]) {
+ [plugin setCommandDelegate:_commandDelegate];
+ }
+
+ [self.pluginObjects setObject:plugin forKey:className];
+ [plugin pluginInitialize];
+}
+
+- (void)registerPlugin:(CDVPlugin*)plugin withPluginName:(NSString*)pluginName
+{
+ if ([plugin respondsToSelector:@selector(setViewController:)]) {
+ [plugin setViewController:self];
+ }
+
+ if ([plugin respondsToSelector:@selector(setCommandDelegate:)]) {
+ [plugin setCommandDelegate:_commandDelegate];
+ }
+
+ NSString* className = NSStringFromClass([plugin class]);
+ [self.pluginObjects setObject:plugin forKey:className];
+ [self.pluginsMap setValue:className forKey:[pluginName lowercaseString]];
+ [plugin pluginInitialize];
+}
+
+/**
+ Returns an instance of a CordovaCommand object, based on its name. If one exists already, it is returned.
+ */
+- (id)getCommandInstance:(NSString*)pluginName
+{
+ // first, we try to find the pluginName in the pluginsMap
+ // (acts as a whitelist as well) if it does not exist, we return nil
+ // NOTE: plugin names are matched as lowercase to avoid problems - however, a
+ // possible issue is there can be duplicates possible if you had:
+ // "org.apache.cordova.Foo" and "org.apache.cordova.foo" - only the lower-cased entry will match
+ NSString* className = [self.pluginsMap objectForKey:[pluginName lowercaseString]];
+
+ if (className == nil) {
+ return nil;
+ }
+
+ id obj = [self.pluginObjects objectForKey:className];
+ if (!obj) {
+ obj = [[NSClassFromString(className)alloc] initWithWebViewEngine:_webViewEngine];
+
+ if (obj != nil) {
+ [self registerPlugin:obj withClassName:className];
+ } else {
+ NSLog(@"CDVPlugin class %@ (pluginName: %@) does not exist.", className, pluginName);
+ }
+ }
+ return obj;
+}
+
+#pragma mark -
+
+- (NSString*)appURLScheme
+{
+ NSString* URLScheme = nil;
+
+ NSArray* URLTypes = [[[NSBundle mainBundle] infoDictionary] objectForKey:@"CFBundleURLTypes"];
+
+ if (URLTypes != nil) {
+ NSDictionary* dict = [URLTypes objectAtIndex:0];
+ if (dict != nil) {
+ NSArray* URLSchemes = [dict objectForKey:@"CFBundleURLSchemes"];
+ if (URLSchemes != nil) {
+ URLScheme = [URLSchemes objectAtIndex:0];
+ }
+ }
+ }
+
+ return URLScheme;
+}
+
+/**
+ Returns the contents of the named plist bundle, loaded as a dictionary object
+ */
++ (NSDictionary*)getBundlePlist:(NSString*)plistName
+{
+ NSString* errorDesc = nil;
+ NSPropertyListFormat format;
+ NSString* plistPath = [[NSBundle mainBundle] pathForResource:plistName ofType:@"plist"];
+ NSData* plistXML = [[NSFileManager defaultManager] contentsAtPath:plistPath];
+ NSDictionary* temp = (NSDictionary*)[NSPropertyListSerialization
+ propertyListFromData:plistXML
+ mutabilityOption:NSPropertyListMutableContainersAndLeaves
+ format:&format errorDescription:&errorDesc];
+
+ return temp;
+}
+
+#pragma mark -
+#pragma mark UIApplicationDelegate impl
+
+/*
+ This method lets your application know that it is about to be terminated and purged from memory entirely
+ */
+- (void)onAppWillTerminate:(NSNotification*)notification
+{
+ // empty the tmp directory
+ NSFileManager* fileMgr = [[NSFileManager alloc] init];
+ NSError* __autoreleasing err = nil;
+
+ // clear contents of NSTemporaryDirectory
+ NSString* tempDirectoryPath = NSTemporaryDirectory();
+ NSDirectoryEnumerator* directoryEnumerator = [fileMgr enumeratorAtPath:tempDirectoryPath];
+ NSString* fileName = nil;
+ BOOL result;
+
+ while ((fileName = [directoryEnumerator nextObject])) {
+ NSString* filePath = [tempDirectoryPath stringByAppendingPathComponent:fileName];
+ result = [fileMgr removeItemAtPath:filePath error:&err];
+ if (!result && err) {
+ NSLog(@"Failed to delete: %@ (error: %@)", filePath, err);
+ }
+ }
+}
+
+/*
+ This method is called to let your application know that it is about to move from the active to inactive state.
+ You should use this method to pause ongoing tasks, disable timer, ...
+ */
+- (void)onAppWillResignActive:(NSNotification*)notification
+{
+ // NSLog(@"%@",@"applicationWillResignActive");
+ [self.commandDelegate evalJs:@"cordova.fireDocumentEvent('resign');" scheduledOnRunLoop:NO];
+}
+
+/*
+ In iOS 4.0 and later, this method is called as part of the transition from the background to the inactive state.
+ You can use this method to undo many of the changes you made to your application upon entering the background.
+ invariably followed by applicationDidBecomeActive
+ */
+- (void)onAppWillEnterForeground:(NSNotification*)notification
+{
+ // NSLog(@"%@",@"applicationWillEnterForeground");
+ [self.commandDelegate evalJs:@"cordova.fireDocumentEvent('resume');"];
+}
+
+// This method is called to let your application know that it moved from the inactive to active state.
+- (void)onAppDidBecomeActive:(NSNotification*)notification
+{
+ // NSLog(@"%@",@"applicationDidBecomeActive");
+ [self.commandDelegate evalJs:@"cordova.fireDocumentEvent('active');"];
+}
+
+/*
+ In iOS 4.0 and later, this method is called instead of the applicationWillTerminate: method
+ when the user quits an application that supports background execution.
+ */
+- (void)onAppDidEnterBackground:(NSNotification*)notification
+{
+ // NSLog(@"%@",@"applicationDidEnterBackground");
+ [self.commandDelegate evalJs:@"cordova.fireDocumentEvent('pause', null, true);" scheduledOnRunLoop:NO];
+}
+
+// ///////////////////////
+
+- (void)dealloc
+{
+ [CDVURLProtocol unregisterViewController:self];
+ [[NSNotificationCenter defaultCenter] removeObserver:self];
+
+ [CDVUserAgentUtil releaseLock:&_userAgentLockToken];
+ [_commandQueue dispose];
+ [[self.pluginObjects allValues] makeObjectsPerformSelector:@selector(dispose)];
+}
+
+@end
http://git-wip-us.apache.org/repos/asf/cordova-ios/blob/d58fa4aa/CordovaLib/Classes/Public/CDVWebViewEngineProtocol.h
----------------------------------------------------------------------
diff --git a/CordovaLib/Classes/Public/CDVWebViewEngineProtocol.h b/CordovaLib/Classes/Public/CDVWebViewEngineProtocol.h
new file mode 100644
index 0000000..18c1831
--- /dev/null
+++ b/CordovaLib/Classes/Public/CDVWebViewEngineProtocol.h
@@ -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>
+
+#define kCDVWebViewEngineScriptMessageHandlers @"kCDVWebViewEngineScriptMessageHandlers"
+#define kCDVWebViewEngineUIWebViewDelegate @"kCDVWebViewEngineUIWebViewDelegate"
+#define kCDVWebViewEngineWKNavigationDelegate @"kCDVWebViewEngineWKNavigationDelegate"
+#define kCDVWebViewEngineWKUIDelegate @"kCDVWebViewEngineWKUIDelegate"
+#define kCDVWebViewEngineWebViewPreferences @"kCDVWebViewEngineWebViewPreferences"
+
+@protocol CDVWebViewEngineProtocol <NSObject>
+
+@property (nonatomic, strong, readonly) UIView* engineWebView;
+
+- (id)loadRequest:(NSURLRequest*)request;
+- (id)loadHTMLString:(NSString*)string baseURL:(NSURL*)baseURL;
+- (void)evaluateJavaScript:(NSString*)javaScriptString completionHandler:(void (^)(id, NSError*))completionHandler;
+
+- (NSURL*)URL;
+
+- (instancetype)initWithFrame:(CGRect)frame;
+- (void)updateWithInfo:(NSDictionary*)info;
+
+@end
http://git-wip-us.apache.org/repos/asf/cordova-ios/blob/d58fa4aa/CordovaLib/Classes/Public/CDVWhitelist.h
----------------------------------------------------------------------
diff --git a/CordovaLib/Classes/Public/CDVWhitelist.h b/CordovaLib/Classes/Public/CDVWhitelist.h
new file mode 100644
index 0000000..9165097
--- /dev/null
+++ b/CordovaLib/Classes/Public/CDVWhitelist.h
@@ -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
http://git-wip-us.apache.org/repos/asf/cordova-ios/blob/d58fa4aa/CordovaLib/Classes/Public/CDVWhitelist.m
----------------------------------------------------------------------
diff --git a/CordovaLib/Classes/Public/CDVWhitelist.m b/CordovaLib/Classes/Public/CDVWhitelist.m
new file mode 100644
index 0000000..8e3be75
--- /dev/null
+++ b/CordovaLib/Classes/Public/CDVWhitelist.m
@@ -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
http://git-wip-us.apache.org/repos/asf/cordova-ios/blob/d58fa4aa/CordovaLib/Classes/Public/NSData+Base64.h
----------------------------------------------------------------------
diff --git a/CordovaLib/Classes/Public/NSData+Base64.h b/CordovaLib/Classes/Public/NSData+Base64.h
new file mode 100644
index 0000000..02d960c
--- /dev/null
+++ b/CordovaLib/Classes/Public/NSData+Base64.h
@@ -0,0 +1,43 @@
+//
+// NSData+Base64.h
+// base64
+//
+// Created by Matt Gallagher on 2009/06/03.
+// Copyright 2009 Matt Gallagher. All rights reserved.
+//
+// This software is provided 'as-is', without any express or implied
+// warranty. In no event will the authors be held liable for any damages
+// arising from the use of this software. Permission is granted to anyone to
+// use this software for any purpose, including commercial applications, and to
+// alter it and redistribute it freely, subject to the following restrictions:
+//
+// 1. The origin of this software must not be misrepresented; you must not
+// claim that you wrote the original software. If you use this software
+// in a product, an acknowledgment in the product documentation would be
+// appreciated but is not required.
+// 2. Altered source versions must be plainly marked as such, and must not be
+// misrepresented as being the original software.
+// 3. This notice may not be removed or altered from any source
+// distribution.
+//
+
+#import <Foundation/Foundation.h>
+#import "CDVAvailabilityDeprecated.h"
+
+void *CDVNewBase64Decode(
+ const char* inputBuffer,
+ size_t length,
+ size_t * outputLength);
+
+char *CDVNewBase64Encode(
+ const void* inputBuffer,
+ size_t length,
+ bool separateLines,
+ size_t * outputLength);
+
+@interface NSData (CDVBase64)
+
++ (NSData*)cdv_dataFromBase64String:(NSString*)aString;
+- (NSString*)cdv_base64EncodedString;
+
+@end
http://git-wip-us.apache.org/repos/asf/cordova-ios/blob/d58fa4aa/CordovaLib/Classes/Public/NSData+Base64.m
----------------------------------------------------------------------
diff --git a/CordovaLib/Classes/Public/NSData+Base64.m b/CordovaLib/Classes/Public/NSData+Base64.m
new file mode 100644
index 0000000..43560f0
--- /dev/null
+++ b/CordovaLib/Classes/Public/NSData+Base64.m
@@ -0,0 +1,298 @@
+//
+// NSData+Base64.m
+// base64
+//
+// Created by Matt Gallagher on 2009/06/03.
+// Copyright 2009 Matt Gallagher. All rights reserved.
+//
+// This software is provided 'as-is', without any express or implied
+// warranty. In no event will the authors be held liable for any damages
+// arising from the use of this software. Permission is granted to anyone to
+// use this software for any purpose, including commercial applications, and to
+// alter it and redistribute it freely, subject to the following restrictions:
+//
+// 1. The origin of this software must not be misrepresented; you must not
+// claim that you wrote the original software. If you use this software
+// in a product, an acknowledgment in the product documentation would be
+// appreciated but is not required.
+// 2. Altered source versions must be plainly marked as such, and must not be
+// misrepresented as being the original software.
+// 3. This notice may not be removed or altered from any source
+// distribution.
+//
+
+#import "NSData+Base64.h"
+
+//
+// Mapping from 6 bit pattern to ASCII character.
+//
+static unsigned char base64EncodeLookup[65] =
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
+
+//
+// Definition for "masked-out" areas of the base64DecodeLookup mapping
+//
+#define xx 65
+
+//
+// Mapping from ASCII character to 6 bit pattern.
+//
+static unsigned char base64DecodeLookup[256] =
+{
+ xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx,
+ xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx,
+ xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, 62, xx, xx, xx, 63,
+ 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, xx, xx, xx, xx, xx, xx,
+ xx, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14,
+ 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, xx, xx, xx, xx, xx,
+ xx, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40,
+ 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, xx, xx, xx, xx, xx,
+ xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx,
+ xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx,
+ xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx,
+ xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx,
+ xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx,
+ xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx,
+ xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx,
+ xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx,
+};
+
+//
+// Fundamental sizes of the binary and base64 encode/decode units in bytes
+//
+#define BINARY_UNIT_SIZE 3
+#define BASE64_UNIT_SIZE 4
+
+//
+// NewBase64Decode
+//
+// Decodes the base64 ASCII string in the inputBuffer to a newly malloced
+// output buffer.
+//
+// inputBuffer - the source ASCII string for the decode
+// length - the length of the string or -1 (to specify strlen should be used)
+// outputLength - if not-NULL, on output will contain the decoded length
+//
+// returns the decoded buffer. Must be free'd by caller. Length is given by
+// outputLength.
+//
+void *CDVNewBase64Decode(
+ const char* inputBuffer,
+ size_t length,
+ size_t * outputLength)
+{
+ if (length == -1) {
+ length = strlen(inputBuffer);
+ }
+
+ size_t outputBufferSize =
+ ((length + BASE64_UNIT_SIZE - 1) / BASE64_UNIT_SIZE) * BINARY_UNIT_SIZE;
+ unsigned char* outputBuffer = (unsigned char*)malloc(outputBufferSize);
+
+ size_t i = 0;
+ size_t j = 0;
+
+ while (i < length) {
+ //
+ // Accumulate 4 valid characters (ignore everything else)
+ //
+ unsigned char accumulated[BASE64_UNIT_SIZE];
+ size_t accumulateIndex = 0;
+
+ while (i < length) {
+ unsigned char decode = base64DecodeLookup[inputBuffer[i++]];
+ if (decode != xx) {
+ accumulated[accumulateIndex] = decode;
+ accumulateIndex++;
+
+ if (accumulateIndex == BASE64_UNIT_SIZE) {
+ break;
+ }
+ }
+ }
+
+ //
+ // Store the 6 bits from each of the 4 characters as 3 bytes
+ //
+ // (Uses improved bounds checking suggested by Alexandre Colucci)
+ //
+ if (accumulateIndex >= 2) {
+ outputBuffer[j] = (accumulated[0] << 2) | (accumulated[1] >> 4);
+ }
+ if (accumulateIndex >= 3) {
+ outputBuffer[j + 1] = (accumulated[1] << 4) | (accumulated[2] >> 2);
+ }
+ if (accumulateIndex >= 4) {
+ outputBuffer[j + 2] = (accumulated[2] << 6) | accumulated[3];
+ }
+ j += accumulateIndex - 1;
+ }
+
+ if (outputLength) {
+ *outputLength = j;
+ }
+ return outputBuffer;
+}
+
+//
+// NewBase64Encode
+//
+// Encodes the arbitrary data in the inputBuffer as base64 into a newly malloced
+// output buffer.
+//
+// inputBuffer - the source data for the encode
+// length - the length of the input in bytes
+// separateLines - if zero, no CR/LF characters will be added. Otherwise
+// a CR/LF pair will be added every 64 encoded chars.
+// outputLength - if not-NULL, on output will contain the encoded length
+// (not including terminating 0 char)
+//
+// returns the encoded buffer. Must be free'd by caller. Length is given by
+// outputLength.
+//
+char *CDVNewBase64Encode(
+ const void* buffer,
+ size_t length,
+ bool separateLines,
+ size_t * outputLength)
+{
+ const unsigned char* inputBuffer = (const unsigned char*)buffer;
+
+#define MAX_NUM_PADDING_CHARS 2
+#define OUTPUT_LINE_LENGTH 64
+#define INPUT_LINE_LENGTH ((OUTPUT_LINE_LENGTH / BASE64_UNIT_SIZE) * BINARY_UNIT_SIZE)
+#define CR_LF_SIZE 2
+
+ //
+ // Byte accurate calculation of final buffer size
+ //
+ size_t outputBufferSize =
+ ((length / BINARY_UNIT_SIZE)
+ + ((length % BINARY_UNIT_SIZE) ? 1 : 0))
+ * BASE64_UNIT_SIZE;
+ if (separateLines) {
+ outputBufferSize +=
+ (outputBufferSize / OUTPUT_LINE_LENGTH) * CR_LF_SIZE;
+ }
+
+ //
+ // Include space for a terminating zero
+ //
+ outputBufferSize += 1;
+
+ //
+ // Allocate the output buffer
+ //
+ char* outputBuffer = (char*)malloc(outputBufferSize);
+ if (!outputBuffer) {
+ return NULL;
+ }
+
+ size_t i = 0;
+ size_t j = 0;
+ const size_t lineLength = separateLines ? INPUT_LINE_LENGTH : length;
+ size_t lineEnd = lineLength;
+
+ while (true) {
+ if (lineEnd > length) {
+ lineEnd = length;
+ }
+
+ for (; i + BINARY_UNIT_SIZE - 1 < lineEnd; i += BINARY_UNIT_SIZE) {
+ //
+ // Inner loop: turn 48 bytes into 64 base64 characters
+ //
+ outputBuffer[j++] = base64EncodeLookup[(inputBuffer[i] & 0xFC) >> 2];
+ outputBuffer[j++] = base64EncodeLookup[((inputBuffer[i] & 0x03) << 4)
+ | ((inputBuffer[i + 1] & 0xF0) >> 4)];
+ outputBuffer[j++] = base64EncodeLookup[((inputBuffer[i + 1] & 0x0F) << 2)
+ | ((inputBuffer[i + 2] & 0xC0) >> 6)];
+ outputBuffer[j++] = base64EncodeLookup[inputBuffer[i + 2] & 0x3F];
+ }
+
+ if (lineEnd == length) {
+ break;
+ }
+
+ //
+ // Add the newline
+ //
+ // outputBuffer[j++] = '\r';
+ // outputBuffer[j++] = '\n';
+ lineEnd += lineLength;
+ }
+
+ if (i + 1 < length) {
+ //
+ // Handle the single '=' case
+ //
+ outputBuffer[j++] = base64EncodeLookup[(inputBuffer[i] & 0xFC) >> 2];
+ outputBuffer[j++] = base64EncodeLookup[((inputBuffer[i] & 0x03) << 4)
+ | ((inputBuffer[i + 1] & 0xF0) >> 4)];
+ outputBuffer[j++] = base64EncodeLookup[(inputBuffer[i + 1] & 0x0F) << 2];
+ outputBuffer[j++] = '=';
+ } else if (i < length) {
+ //
+ // Handle the double '=' case
+ //
+ outputBuffer[j++] = base64EncodeLookup[(inputBuffer[i] & 0xFC) >> 2];
+ outputBuffer[j++] = base64EncodeLookup[(inputBuffer[i] & 0x03) << 4];
+ outputBuffer[j++] = '=';
+ outputBuffer[j++] = '=';
+ }
+ outputBuffer[j] = 0;
+
+ //
+ // Set the output length and return the buffer
+ //
+ if (outputLength) {
+ *outputLength = j;
+ }
+ return outputBuffer;
+}
+
+@implementation NSData (CDVBase64)
+
+//
+// dataFromBase64String:
+//
+// Creates an NSData object containing the base64 decoded representation of
+// the base64 string 'aString'
+//
+// Parameters:
+// aString - the base64 string to decode
+//
+// returns the autoreleased NSData representation of the base64 string
+//
++ (NSData*)cdv_dataFromBase64String:(NSString*)aString
+{
+ size_t outputLength = 0;
+ void* outputBuffer = CDVNewBase64Decode([aString UTF8String], [aString length], &outputLength);
+
+ return [NSData dataWithBytesNoCopy:outputBuffer length:outputLength freeWhenDone:YES];
+}
+
+//
+// base64EncodedString
+//
+// Creates an NSString object that contains the base 64 encoding of the
+// receiver's data. Lines are broken at 64 characters long.
+//
+// returns an autoreleased NSString being the base 64 representation of the
+// receiver.
+//
+- (NSString*)cdv_base64EncodedString
+{
+ size_t outputLength = 0;
+ char* outputBuffer =
+ CDVNewBase64Encode([self bytes], [self length], true, &outputLength);
+
+ NSString* result = [[NSString alloc] initWithBytesNoCopy:outputBuffer
+ length:outputLength
+ encoding:NSASCIIStringEncoding
+ freeWhenDone:YES];
+
+ return result;
+}
+
+@end
http://git-wip-us.apache.org/repos/asf/cordova-ios/blob/d58fa4aa/CordovaLib/Classes/Public/NSDictionary+CordovaPreferences.h
----------------------------------------------------------------------
diff --git a/CordovaLib/Classes/Public/NSDictionary+CordovaPreferences.h b/CordovaLib/Classes/Public/NSDictionary+CordovaPreferences.h
new file mode 100644
index 0000000..9be2be2
--- /dev/null
+++ b/CordovaLib/Classes/Public/NSDictionary+CordovaPreferences.h
@@ -0,0 +1,35 @@
+/*
+ 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>
+
+@interface NSDictionary (CordovaPreferences)
+
+- (id)cordovaSettingForKey:(NSString*)key;
+- (BOOL)cordovaBoolSettingForKey:(NSString*)key defaultValue:(BOOL)defaultValue;
+- (CGFloat)cordovaFloatSettingForKey:(NSString*)key defaultValue:(CGFloat)defaultValue;
+
+@end
+
+@interface NSMutableDictionary (CordovaPreferences)
+
+- (void)setCordovaSetting:(id)value forKey:(NSString*)key;
+
+@end
http://git-wip-us.apache.org/repos/asf/cordova-ios/blob/d58fa4aa/CordovaLib/Classes/Public/NSDictionary+CordovaPreferences.m
----------------------------------------------------------------------
diff --git a/CordovaLib/Classes/Public/NSDictionary+CordovaPreferences.m b/CordovaLib/Classes/Public/NSDictionary+CordovaPreferences.m
new file mode 100644
index 0000000..dcac40f
--- /dev/null
+++ b/CordovaLib/Classes/Public/NSDictionary+CordovaPreferences.m
@@ -0,0 +1,63 @@
+/*
+ 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 "NSDictionary+CordovaPreferences.h"
+#import <Foundation/Foundation.h>
+
+@implementation NSDictionary (CordovaPreferences)
+
+- (id)cordovaSettingForKey:(NSString*)key
+{
+ return [self objectForKey:[key lowercaseString]];
+}
+
+- (BOOL)cordovaBoolSettingForKey:(NSString*)key defaultValue:(BOOL)defaultValue
+{
+ BOOL value = defaultValue;
+ id prefObj = [self cordovaSettingForKey:key];
+
+ if (prefObj != nil) {
+ value = [(NSNumber*)prefObj boolValue];
+ }
+
+ return value;
+}
+
+- (CGFloat)cordovaFloatSettingForKey:(NSString*)key defaultValue:(CGFloat)defaultValue
+{
+ CGFloat value = defaultValue;
+ id prefObj = [self cordovaSettingForKey:key];
+
+ if (prefObj != nil) {
+ value = [prefObj floatValue];
+ }
+
+ return value;
+}
+
+@end
+
+@implementation NSMutableDictionary (CordovaPreferences)
+
+- (void)setCordovaSetting:(id)value forKey:(NSString*)key
+{
+ [self setObject:value forKey:[key lowercaseString]];
+}
+
+@end
http://git-wip-us.apache.org/repos/asf/cordova-ios/blob/d58fa4aa/CordovaLib/Classes/Public/NSMutableArray+QueueAdditions.h
----------------------------------------------------------------------
diff --git a/CordovaLib/Classes/Public/NSMutableArray+QueueAdditions.h b/CordovaLib/Classes/Public/NSMutableArray+QueueAdditions.h
new file mode 100644
index 0000000..79e6516
--- /dev/null
+++ b/CordovaLib/Classes/Public/NSMutableArray+QueueAdditions.h
@@ -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>
+
+@interface NSMutableArray (QueueAdditions)
+
+- (id)cdv_pop;
+- (id)cdv_queueHead;
+- (id)cdv_dequeue;
+- (void)cdv_enqueue:(id)obj;
+
+@end
http://git-wip-us.apache.org/repos/asf/cordova-ios/blob/d58fa4aa/CordovaLib/Classes/Public/NSMutableArray+QueueAdditions.m
----------------------------------------------------------------------
diff --git a/CordovaLib/Classes/Public/NSMutableArray+QueueAdditions.m b/CordovaLib/Classes/Public/NSMutableArray+QueueAdditions.m
new file mode 100644
index 0000000..2b3acdc
--- /dev/null
+++ b/CordovaLib/Classes/Public/NSMutableArray+QueueAdditions.m
@@ -0,0 +1,58 @@
+/*
+ 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 "NSMutableArray+QueueAdditions.h"
+
+@implementation NSMutableArray (QueueAdditions)
+
+- (id)cdv_queueHead
+{
+ if ([self count] == 0) {
+ return nil;
+ }
+
+ return [self objectAtIndex:0];
+}
+
+- (__autoreleasing id)cdv_dequeue
+{
+ if ([self count] == 0) {
+ return nil;
+ }
+
+ id head = [self objectAtIndex:0];
+ if (head != nil) {
+ // [[head retain] autorelease]; ARC - the __autoreleasing on the return value should so the same thing
+ [self removeObjectAtIndex:0];
+ }
+
+ return head;
+}
+
+- (id)cdv_pop
+{
+ return [self cdv_dequeue];
+}
+
+- (void)cdv_enqueue:(id)object
+{
+ [self addObject:object];
+}
+
+@end
---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@cordova.apache.org
For additional commands, e-mail: commits-help@cordova.apache.org