/* 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 #import "CDV.h" #import "CDVCommandQueue.h" #import "CDVCommandDelegateImpl.h" #import "CDVConfigParser.h" #import "CDVUserAgentUtil.h" #import "CDVWebViewDelegate.h" #define degreesToRadian(x) (M_PI * (x) / 180.0) @interface CDVViewController () { NSInteger _userAgentLockToken; CDVWebViewDelegate* _webViewDelegate; } @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) NSArray* startupPluginNames; @property (nonatomic, readwrite, strong) NSDictionary* pluginsMap; @property (nonatomic, readwrite, strong) NSArray* supportedOrientations; @property (nonatomic, readwrite, assign) BOOL loadFromString; @property (readwrite, assign) BOOL initialized; @property (atomic, strong) NSURL* openURL; @end @implementation CDVViewController @synthesize webView, supportedOrientations; @synthesize pluginObjects, pluginsMap, whitelist, startupPluginNames; @synthesize configParser, settings, loadFromString; @synthesize useSplashScreen; @synthesize wwwFolderName, startPage, initialized, openURL; @synthesize commandDelegate = _commandDelegate; @synthesize commandQueue = _commandQueue; - (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]; [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(handleOpenURL:) name:CDVPluginHandleOpenURLNotification object:nil]; // read from UISupportedInterfaceOrientations (or UISupportedInterfaceOrientations~iPad, if its iPad) from -Info.plist self.supportedOrientations = [self parseInterfaceOrientations: [[[NSBundle mainBundle] infoDictionary] objectForKey:@"UISupportedInterfaceOrientations"]]; [self printMultitaskingInfo]; [self printDeprecationNotice]; self.initialized = YES; // load config.xml settings [self loadSettings]; useSplashScreen = YES; } } - (id)initWithNibName:(NSString*)nibNameOrNil bundle:(NSBundle*)nibBundleOrNil { self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil]; [self __init]; return self; } - (id)init { self = [super init]; [self __init]; return self; } - (void)printDeprecationNotice { if (!IsAtLeastiOSVersion(@"5.0")) { NSLog(@"CRITICAL: For Cordova 2.0, you will need to upgrade to at least iOS 5.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]; configParser = [[NSXMLParser alloc] initWithContentsOfURL:url]; if (configParser == nil) { NSLog(@"Failed to initialize XML parser."); return; } [configParser setDelegate:((id < NSXMLParserDelegate >)delegate)]; [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]; } // Implement viewDidLoad to do additional setup after loading the view, typically from a nib. - (void)viewDidLoad { [super viewDidLoad]; NSURL* appURL = nil; NSString* loadErr = 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 { NSString* startFilePath = [self.commandDelegate pathForResource:self.startPage]; if (startFilePath == nil) { loadErr = [NSString stringWithFormat:@"ERROR: Start Page at '%@/%@' was not found.", self.wwwFolderName, self.startPage]; NSLog(@"%@", loadErr); self.loadFromString = YES; appURL = nil; } else { appURL = [NSURL fileURLWithPath:startFilePath]; } } // // 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[@"BackupWebStorage"]; if ([backupWebStorage isKindOfClass:[NSString class]]) { backupWebStorageType = backupWebStorage; } else if ([backupWebStorage isKindOfClass:[NSNumber class]]) { NSLog(@"Deprecated: BackupWebStorage boolean property is a string property now (none, local, cloud). A boolean value of 'true' will be mapped to 'cloud'. Consult the docs: http://docs.cordova.io/en/edge/guide_project-settings_ios_index.md.html#Project%%20Settings%%20for%%20iOS"); backupWebStorageType = [(NSNumber*) backupWebStorage boolValue] ? @"cloud" : @"none"; } self.settings[@"BackupWebStorage"] = backupWebStorageType; if (IsAtLeastiOSVersion(@"5.1")) { [CDVLocalStorage __fixupDatabaseLocationsWithBackupType:backupWebStorageType]; } // // Instantiate the WebView /////////////// [self createGapView]; // ///////////////// NSNumber* enableLocation = [self.settings objectForKey:@"EnableLocation"]; NSString* enableViewportScale = [self.settings objectForKey:@"EnableViewportScale"]; NSNumber* allowInlineMediaPlayback = [self.settings objectForKey:@"AllowInlineMediaPlayback"]; BOOL mediaPlaybackRequiresUserAction = YES; // default value if ([self.settings objectForKey:@"MediaPlaybackRequiresUserAction"]) { mediaPlaybackRequiresUserAction = [(NSNumber*)[settings objectForKey:@"MediaPlaybackRequiresUserAction"] boolValue]; } self.webView.scalesPageToFit = [enableViewportScale boolValue]; /* * Fire up the GPS Service right away as it takes a moment for data to come back. */ if ([enableLocation boolValue]) { [[self.commandDelegate getCommandInstance:@"Geolocation"] getLocation:[CDVInvokedUrlCommand new]]; } /* * 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. */ if (IsAtLeastiOSVersion(@"5.1") && (([backupWebStorageType isEqualToString:@"local"]) || ([backupWebStorageType isEqualToString:@"cloud"] && !IsAtLeastiOSVersion(@"6.0")))) { [self registerPlugin:[[CDVLocalStorage alloc] initWithWebView:self.webView] withClassName:NSStringFromClass([CDVLocalStorage class])]; } /* * This is for iOS 4.x, where you can allow inline