/* 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 "CDVCommandDelegateImpl.h" #import "CDVConfigParser.h" #import "CDVUserAgentUtil.h" #import "CDVWebViewDelegate.h" #import #import "CDVHandleOpenURL.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 wwwFolderName, startPage, initialized, openURL, baseUserAgent; @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(onPageDidLoad:) name:CDVPageDidLoadNotification 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(@"6.0")) { NSLog(@"CRITICAL: For Cordova 3.5.0 and above, you will need to upgrade to at least iOS 6.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]; } - (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 settingForKey:@"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; } // 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 settingForKey:@"BackupWebStorage"]; if ([backupWebStorage isKindOfClass:[NSString class]]) { backupWebStorageType = backupWebStorage; } [self setSetting:backupWebStorageType forKey:@"BackupWebStorage"]; if (IsAtLeastiOSVersion(@"5.1")) { [CDVLocalStorage __fixupDatabaseLocationsWithBackupType:backupWebStorageType]; } // // Instantiate the WebView /////////////// if (!self.webView) { [self createGapView]; } // Configure WebView _webViewDelegate = [[CDVWebViewDelegate alloc] initWithDelegate:self]; self.webView.delegate = _webViewDelegate; // register this viewcontroller with the NSURLProtocol, only after the User-Agent is set [CDVURLProtocol registerViewController:self]; // ///////////////// NSString* enableViewportScale = [self settingForKey:@"EnableViewportScale"]; NSNumber* allowInlineMediaPlayback = [self settingForKey:@"AllowInlineMediaPlayback"]; BOOL mediaPlaybackRequiresUserAction = YES; // default value if ([self settingForKey:@"MediaPlaybackRequiresUserAction"]) { mediaPlaybackRequiresUserAction = [(NSNumber*)[self settingForKey:@"MediaPlaybackRequiresUserAction"] boolValue]; } self.webView.scalesPageToFit = [enableViewportScale boolValue]; /* * 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