You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@cordova.apache.org by sh...@apache.org on 2015/06/25 03:09:19 UTC
[31/50] ios commit: Merge branch 'master' into 4.0.x
Merge branch 'master' into 4.0.x
Project: http://git-wip-us.apache.org/repos/asf/cordova-ios/repo
Commit: http://git-wip-us.apache.org/repos/asf/cordova-ios/commit/d0877d08
Tree: http://git-wip-us.apache.org/repos/asf/cordova-ios/tree/d0877d08
Diff: http://git-wip-us.apache.org/repos/asf/cordova-ios/diff/d0877d08
Branch: refs/heads/master
Commit: d0877d08aa8a720132dc36144d9d187e7d481667
Parents: c23efe1 94a4cb2
Author: Shazron Abdullah <sh...@apache.org>
Authored: Tue May 19 16:15:20 2015 -0700
Committer: Shazron Abdullah <sh...@apache.org>
Committed: Tue May 19 16:15:20 2015 -0700
----------------------------------------------------------------------
.../CDVUIWebViewEngine/CDVUIWebViewDelegate.m | 4 +--
.../Classes/Public/CDVCommandDelegateImpl.m | 2 +-
CordovaLib/Classes/Public/CDVViewController.m | 30 +++++++++++++++-----
bin/templates/scripts/cordova/lib/list-devices | 12 +++++---
.../scripts/cordova/lib/list-emulator-images | 3 +-
5 files changed, 35 insertions(+), 16 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/cordova-ios/blob/d0877d08/CordovaLib/Classes/Private/Plugins/CDVUIWebViewEngine/CDVUIWebViewDelegate.m
----------------------------------------------------------------------
diff --cc CordovaLib/Classes/Private/Plugins/CDVUIWebViewEngine/CDVUIWebViewDelegate.m
index 01872f6,0000000..19f6ef5
mode 100644,000000..100644
--- a/CordovaLib/Classes/Private/Plugins/CDVUIWebViewEngine/CDVUIWebViewDelegate.m
+++ b/CordovaLib/Classes/Private/Plugins/CDVUIWebViewEngine/CDVUIWebViewDelegate.m
@@@ -1,399 -1,0 +1,399 @@@
+/*
+ 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.
+ */
+
+//
+// Testing shows:
+//
+// In all cases, webView.request.URL is the previous page's URL (or empty) during the didStartLoad callback.
+// When loading a page with a redirect:
+// 1. shouldStartLoading (requestURL is target page)
+// 2. didStartLoading
+// 3. shouldStartLoading (requestURL is redirect target)
+// 4. didFinishLoad (request.URL is redirect target)
+//
+// Note the lack of a second didStartLoading **
+//
+// When loading a page with iframes:
+// 1. shouldStartLoading (requestURL is main page)
+// 2. didStartLoading
+// 3. shouldStartLoading (requestURL is one of the iframes)
+// 4. didStartLoading
+// 5. didFinishLoad
+// 6. didFinishLoad
+//
+// Note there is no way to distinguish which didFinishLoad maps to which didStartLoad **
+//
+// Loading a page by calling window.history.go(-1):
+// 1. didStartLoading
+// 2. didFinishLoad
+//
+// Note the lack of a shouldStartLoading call **
+// Actually - this is fixed on iOS6. iOS6 has a shouldStart. **
+//
+// Loading a page by calling location.reload()
+// 1. shouldStartLoading
+// 2. didStartLoading
+// 3. didFinishLoad
+//
+// Loading a page with an iframe that fails to load:
+// 1. shouldStart (main page)
+// 2. didStart
+// 3. shouldStart (iframe)
+// 4. didStart
+// 5. didFailWithError
+// 6. didFinish
+//
+// Loading a page with an iframe that fails to load due to an invalid URL:
+// 1. shouldStart (main page)
+// 2. didStart
+// 3. shouldStart (iframe)
+// 5. didFailWithError
+// 6. didFinish
+//
+// This case breaks our logic since there is a missing didStart. To prevent this,
+// we check URLs in shouldStart and return NO if they are invalid.
+//
+// Loading a page with an invalid URL
+// 1. shouldStart (main page)
+// 2. didFailWithError
+//
+// TODO: Record order when page is re-navigated before the first navigation finishes.
+//
+
+#import "CDVUIWebViewDelegate.h"
+
+// #define VerboseLog NSLog
+#define VerboseLog(...) do { \
+} while (0)
+
+typedef enum {
+ STATE_IDLE = 0,
+ STATE_WAITING_FOR_LOAD_START = 1,
+ STATE_WAITING_FOR_LOAD_FINISH = 2,
+ STATE_IOS5_POLLING_FOR_LOAD_START = 3,
+ STATE_IOS5_POLLING_FOR_LOAD_FINISH = 4,
+ STATE_CANCELLED = 5
+} State;
+
+static NSString *stripFragment(NSString* url)
+{
+ NSRange r = [url rangeOfString:@"#"];
+
+ if (r.location == NSNotFound) {
+ return url;
+ }
+ return [url substringToIndex:r.location];
+}
+
+@implementation CDVUIWebViewDelegate
+
+- (id)initWithDelegate:(NSObject <UIWebViewDelegate>*)delegate
+{
+ self = [super init];
+ if (self != nil) {
+ _delegate = delegate;
+ _loadCount = -1;
+ _state = STATE_IDLE;
+ }
+ return self;
+}
+
+- (BOOL)request:(NSURLRequest*)newRequest isEqualToRequestAfterStrippingFragments:(NSURLRequest*)originalRequest
+{
+ if (originalRequest.URL && newRequest.URL) {
+ NSString* originalRequestUrl = [originalRequest.URL absoluteString];
+ NSString* newRequestUrl = [newRequest.URL absoluteString];
+
+ NSString* baseOriginalRequestUrl = stripFragment(originalRequestUrl);
+ NSString* baseNewRequestUrl = stripFragment(newRequestUrl);
+ return [baseOriginalRequestUrl isEqualToString:baseNewRequestUrl];
+ }
+
+ return NO;
+}
+
+- (BOOL)isPageLoaded:(UIWebView*)webView
+{
+ NSString* readyState = [webView stringByEvaluatingJavaScriptFromString:@"document.readyState"];
+
+ return [readyState isEqualToString:@"loaded"] || [readyState isEqualToString:@"complete"];
+}
+
+- (BOOL)isJsLoadTokenSet:(UIWebView*)webView
+{
+ NSString* loadToken = [webView stringByEvaluatingJavaScriptFromString:@"window.__cordovaLoadToken"];
+
+ return [[NSString stringWithFormat:@"%ld", (long)_curLoadToken] isEqualToString:loadToken];
+}
+
+- (void)setLoadToken:(UIWebView*)webView
+{
+ _curLoadToken += 1;
+ [webView stringByEvaluatingJavaScriptFromString:[NSString stringWithFormat:@"window.__cordovaLoadToken=%ld", (long)_curLoadToken]];
+}
+
+- (NSString*)evalForCurrentURL:(UIWebView*)webView
+{
+ return [webView stringByEvaluatingJavaScriptFromString:@"location.href"];
+}
+
+- (void)pollForPageLoadStart:(UIWebView*)webView
+{
+ if (_state != STATE_IOS5_POLLING_FOR_LOAD_START) {
+ return;
+ }
+ if (![self isJsLoadTokenSet:webView]) {
+ VerboseLog(@"Polled for page load start. result = YES!");
+ _state = STATE_IOS5_POLLING_FOR_LOAD_FINISH;
+ [self setLoadToken:webView];
+ if ([_delegate respondsToSelector:@selector(webViewDidStartLoad:)]) {
+ [_delegate webViewDidStartLoad:webView];
+ }
+ [self pollForPageLoadFinish:webView];
+ } else {
+ VerboseLog(@"Polled for page load start. result = NO");
+ // Poll only for 1 second, and then fall back on checking only when delegate methods are called.
+ ++_loadStartPollCount;
+ if (_loadStartPollCount < (1000 * .05)) {
+ [self performSelector:@selector(pollForPageLoadStart:) withObject:webView afterDelay:.05];
+ }
+ }
+}
+
+- (void)pollForPageLoadFinish:(UIWebView*)webView
+{
+ if (_state != STATE_IOS5_POLLING_FOR_LOAD_FINISH) {
+ return;
+ }
+ if ([self isPageLoaded:webView]) {
+ VerboseLog(@"Polled for page load finish. result = YES!");
+ _state = STATE_IDLE;
+ if ([_delegate respondsToSelector:@selector(webViewDidFinishLoad:)]) {
+ [_delegate webViewDidFinishLoad:webView];
+ }
+ } else {
+ VerboseLog(@"Polled for page load finish. result = NO");
+ [self performSelector:@selector(pollForPageLoadFinish:) withObject:webView afterDelay:.05];
+ }
+}
+
+- (BOOL)shouldLoadRequest:(NSURLRequest*)request
+{
+ NSString* scheme = [[request URL] scheme];
+
+ if ([scheme isEqualToString:@"mailto"] || [scheme isEqualToString:@"tel"]) {
+ return YES;
+ }
+
+ return [NSURLConnection canHandleRequest:request];
+}
+
+- (BOOL)webView:(UIWebView*)webView shouldStartLoadWithRequest:(NSURLRequest*)request navigationType:(UIWebViewNavigationType)navigationType
+{
+ BOOL shouldLoad = YES;
+
+ if ([_delegate respondsToSelector:@selector(webView:shouldStartLoadWithRequest:navigationType:)]) {
+ shouldLoad = [_delegate webView:webView shouldStartLoadWithRequest:request navigationType:navigationType];
+ }
+
+ VerboseLog(@"webView shouldLoad=%d (before) state=%d loadCount=%d URL=%@", shouldLoad, _state, _loadCount, request.URL);
+
+ if (shouldLoad) {
+ // When devtools refresh occurs, it blindly uses the same request object. If a history.replaceState() has occured, then
+ // mainDocumentURL != URL even though it's a top-level navigation.
+ BOOL isDevToolsRefresh = (request == webView.request);
+ BOOL isTopLevelNavigation = isDevToolsRefresh || [request.URL isEqual:[request mainDocumentURL]];
+ if (isTopLevelNavigation) {
+ // Ignore hash changes that don't navigate to a different page.
+ // webView.request does actually update when history.replaceState() gets called.
+ if ([self request:request isEqualToRequestAfterStrippingFragments:webView.request]) {
+ NSString* prevURL = [self evalForCurrentURL:webView];
+ if ([prevURL isEqualToString:[request.URL absoluteString]]) {
+ VerboseLog(@"Page reload detected.");
+ } else {
+ VerboseLog(@"Detected hash change shouldLoad");
+ return shouldLoad;
+ }
+ }
+
+ switch (_state) {
+ case STATE_WAITING_FOR_LOAD_FINISH:
+ // Redirect case.
+ // We expect loadCount == 1.
+ if (_loadCount != 1) {
+ NSLog(@"CDVWebViewDelegate: Detected redirect when loadCount=%ld", (long)_loadCount);
+ }
+ break;
+
+ case STATE_IDLE:
+ case STATE_IOS5_POLLING_FOR_LOAD_START:
+ case STATE_CANCELLED:
+ // Page navigation start.
+ _loadCount = 0;
+ _state = STATE_WAITING_FOR_LOAD_START;
+ break;
+
+ default:
+ {
- _loadCount = 0;
- _state = STATE_WAITING_FOR_LOAD_START;
+ NSString* description = [NSString stringWithFormat:@"CDVWebViewDelegate: Navigation started when state=%ld", (long)_state];
+ NSLog(@"%@", description);
++ _loadCount = 0;
++ _state = STATE_WAITING_FOR_LOAD_START;
+ if ([_delegate respondsToSelector:@selector(webView:didFailLoadWithError:)]) {
+ NSDictionary* errorDictionary = @{NSLocalizedDescriptionKey : description};
+ NSError* error = [[NSError alloc] initWithDomain:@"CDVUIWebViewDelegate" code:1 userInfo:errorDictionary];
+ [_delegate webView:webView didFailLoadWithError:error];
+ }
+ }
+ }
+ } else {
+ // Deny invalid URLs so that we don't get the case where we go straight from
+ // webViewShouldLoad -> webViewDidFailLoad (messes up _loadCount).
+ shouldLoad = shouldLoad && [self shouldLoadRequest:request];
+ }
+ VerboseLog(@"webView shouldLoad=%d (after) isTopLevelNavigation=%d state=%d loadCount=%d", shouldLoad, isTopLevelNavigation, _state, _loadCount);
+ }
+ return shouldLoad;
+}
+
+- (void)webViewDidStartLoad:(UIWebView*)webView
+{
+ VerboseLog(@"webView didStartLoad (before). state=%d loadCount=%d", _state, _loadCount);
+ BOOL fireCallback = NO;
+ switch (_state) {
+ case STATE_IDLE:
+ break;
+
+ case STATE_CANCELLED:
+ fireCallback = YES;
+ _state = STATE_WAITING_FOR_LOAD_FINISH;
+ _loadCount += 1;
+ break;
+
+ case STATE_WAITING_FOR_LOAD_START:
+ if (_loadCount != 0) {
+ NSLog(@"CDVWebViewDelegate: Unexpected loadCount in didStart. count=%ld", (long)_loadCount);
+ }
+ fireCallback = YES;
+ _state = STATE_WAITING_FOR_LOAD_FINISH;
+ _loadCount = 1;
+ break;
+
+ case STATE_WAITING_FOR_LOAD_FINISH:
+ _loadCount += 1;
+ break;
+
+ case STATE_IOS5_POLLING_FOR_LOAD_START:
+ [self pollForPageLoadStart:webView];
+ break;
+
+ case STATE_IOS5_POLLING_FOR_LOAD_FINISH:
+ [self pollForPageLoadFinish:webView];
+ break;
+
+ default:
+ NSLog(@"CDVWebViewDelegate: Unexpected didStart with state=%ld loadCount=%ld", (long)_state, (long)_loadCount);
+ }
+ VerboseLog(@"webView didStartLoad (after). state=%d loadCount=%d fireCallback=%d", _state, _loadCount, fireCallback);
+ if (fireCallback && [_delegate respondsToSelector:@selector(webViewDidStartLoad:)]) {
+ [_delegate webViewDidStartLoad:webView];
+ }
+}
+
+- (void)webViewDidFinishLoad:(UIWebView*)webView
+{
+ VerboseLog(@"webView didFinishLoad (before). state=%d loadCount=%d", _state, _loadCount);
+ BOOL fireCallback = NO;
+ switch (_state) {
+ case STATE_IDLE:
+ break;
+
+ case STATE_WAITING_FOR_LOAD_START:
+ NSLog(@"CDVWebViewDelegate: Unexpected didFinish while waiting for load start.");
+ break;
+
+ case STATE_WAITING_FOR_LOAD_FINISH:
+ if (_loadCount == 1) {
+ fireCallback = YES;
+ _state = STATE_IDLE;
+ }
+ _loadCount -= 1;
+ break;
+
+ case STATE_IOS5_POLLING_FOR_LOAD_START:
+ [self pollForPageLoadStart:webView];
+ break;
+
+ case STATE_IOS5_POLLING_FOR_LOAD_FINISH:
+ [self pollForPageLoadFinish:webView];
+ break;
+ }
+ VerboseLog(@"webView didFinishLoad (after). state=%d loadCount=%d fireCallback=%d", _state, _loadCount, fireCallback);
+ if (fireCallback && [_delegate respondsToSelector:@selector(webViewDidFinishLoad:)]) {
+ [_delegate webViewDidFinishLoad:webView];
+ }
+}
+
+- (void)webView:(UIWebView*)webView didFailLoadWithError:(NSError*)error
+{
+ VerboseLog(@"webView didFailLoad (before). state=%d loadCount=%d", _state, _loadCount);
+ BOOL fireCallback = NO;
+
+ switch (_state) {
+ case STATE_IDLE:
+ break;
+
+ case STATE_WAITING_FOR_LOAD_START:
+ if ([error code] == NSURLErrorCancelled) {
+ _state = STATE_CANCELLED;
+ } else {
+ _state = STATE_IDLE;
+ }
+ fireCallback = YES;
+ break;
+
+ case STATE_WAITING_FOR_LOAD_FINISH:
+ if ([error code] != NSURLErrorCancelled) {
+ if (_loadCount == 1) {
+ _state = STATE_IDLE;
+ fireCallback = YES;
+ }
+ _loadCount = -1;
+ } else {
+ fireCallback = YES;
+ _state = STATE_CANCELLED;
+ _loadCount -= 1;
+ }
+ break;
+
+ case STATE_IOS5_POLLING_FOR_LOAD_START:
+ [self pollForPageLoadStart:webView];
+ break;
+
+ case STATE_IOS5_POLLING_FOR_LOAD_FINISH:
+ [self pollForPageLoadFinish:webView];
+ break;
+ }
+ VerboseLog(@"webView didFailLoad (after). state=%d loadCount=%d, fireCallback=%d", _state, _loadCount, fireCallback);
+ if (fireCallback && [_delegate respondsToSelector:@selector(webView:didFailLoadWithError:)]) {
+ [_delegate webView:webView didFailLoadWithError:error];
+ }
+}
+
+@end
http://git-wip-us.apache.org/repos/asf/cordova-ios/blob/d0877d08/CordovaLib/Classes/Public/CDVCommandDelegateImpl.m
----------------------------------------------------------------------
diff --cc CordovaLib/Classes/Public/CDVCommandDelegateImpl.m
index 629d8e8,0000000..6d168ab
mode 100644,000000..100644
--- a/CordovaLib/Classes/Public/CDVCommandDelegateImpl.m
+++ b/CordovaLib/Classes/Public/CDVCommandDelegateImpl.m
@@@ -1,186 -1,0 +1,186 @@@
+/*
+ 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
+
+@synthesize urlTransformer;
+
+- (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)]);
+ [_viewController.webViewEngine evaluateJavaScript:js completionHandler:^(id obj, NSError* error) {
+ // TODO: obj can be something other than string
+ if ([obj isKindOfClass:[NSString class]]) {
+ NSString* commandsJSON = (NSString*)obj;
+ 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];
++ js = [NSString stringWithFormat:@"try{cordova.require('cordova/exec').nativeEvalAndFetch(function(){%@})}catch(e){console.log('exeption nativeEvalAndFetch : '+e);};", 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 shouldAllowNavigationToURL:url];
+}
+
+- (NSDictionary*)settings
+{
+ return _viewController.settings;
+}
+
+@end
http://git-wip-us.apache.org/repos/asf/cordova-ios/blob/d0877d08/CordovaLib/Classes/Public/CDVViewController.m
----------------------------------------------------------------------
diff --cc CordovaLib/Classes/Public/CDVViewController.m
index b09581c,0000000..39c10c7
mode 100644,000000..100644
--- a/CordovaLib/Classes/Public/CDVViewController.m
+++ b/CordovaLib/Classes/Public/CDVViewController.m
@@@ -1,744 -1,0 +1,760 @@@
+/*
+ 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"
+
+@interface CDVViewController () {
+ NSInteger _userAgentLockToken;
+}
+
+@property (nonatomic, readwrite, strong) NSXMLParser* configParser;
+@property (nonatomic, readwrite, strong) NSMutableDictionary* settings;
+@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, strong) id <CDVWebViewEngineProtocol> webViewEngine;
+
+@property (readwrite, assign) BOOL initialized;
+
+@property (atomic, strong) NSURL* openURL;
+
+@end
+
+@implementation CDVViewController
+
+@synthesize supportedOrientations;
+@synthesize pluginObjects, pluginsMap, startupPluginNames;
+@synthesize configParser, settings;
+@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)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
+{
+ return [self shouldAllowNavigationToURL:url];
+}
+
+- (void)parseSettingsWithParser:(NSObject <NSXMLParserDelegate>*)delegate
+{
+ // 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];
+}
+
+- (void)loadSettings
+{
+ CDVConfigParser* delegate = [[CDVConfigParser alloc] init];
+
+ [self parseSettingsWithParser:delegate];
+
+ // Get the plugin dictionary, whitelist and settings from the delegate.
+ self.pluginsMap = delegate.pluginsDict;
+ self.startupPluginNames = delegate.startupPluginNames;
+ 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) {
+ 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];
+
+ NSString* backupWebStorageType = @"cloud"; // default value
+
+ id backupWebStorage = [self.settings cordovaSettingForKey:@"BackupWebStorage"];
+ if ([backupWebStorage isKindOfClass:[NSString class]]) {
+ backupWebStorageType = backupWebStorage;
+ }
+ [self.settings setCordovaSetting:backupWebStorageType forKey:@"BackupWebStorage"];
+
+ // // 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 7/8 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;
+}
+
+- (BOOL)shouldAutorotate
+{
+ return YES;
+}
+
+- (NSUInteger)supportedInterfaceOrientations
+{
+ NSUInteger ret = 0;
+
+ if ([self supportsOrientation:UIInterfaceOrientationPortrait]) {
+ ret = ret | (1 << UIInterfaceOrientationPortrait);
+ }
+ if ([self supportsOrientation:UIInterfaceOrientationPortraitUpsideDown]) {
+ ret = ret | (1 << UIInterfaceOrientationPortraitUpsideDown);
+ }
+ if ([self supportsOrientation:UIInterfaceOrientationLandscapeRight]) {
+ ret = ret | (1 << UIInterfaceOrientationLandscapeRight);
+ }
+ if ([self supportsOrientation: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];
- }
++ if (_userAgent != nil) {
++ return _userAgent;
++ }
++
++ NSString* localBaseUserAgent;
++ if (self.baseUserAgent != nil) {
++ localBaseUserAgent = self.baseUserAgent;
++ } else if ([self settingForKey:@"OverrideUserAgent"] != nil) {
++ localBaseUserAgent = [self settingForKey:@"OverrideUserAgent"];
++ } else {
++ localBaseUserAgent = [CDVUserAgentUtil originalUserAgent];
++ }
++ NSString* appendUserAgent = [self settingForKey:@"AppendUserAgent"];
++ if (appendUserAgent) {
++ _userAgent = [NSString stringWithFormat:@"%@ %@", localBaseUserAgent, appendUserAgent];
++ } else {
+ // 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 Network Policy Plugin (Whitelist) hooks
+
+/* This implements the default policy for resource loading and navigation, if there
+ * are no plugins installed which override the whitelist methods.
+ */
+- (BOOL)defaultResourcePolicyForURL:(NSURL*)url
+{
+ /*
+ * If a URL is being loaded that's a file/http/https URL, just load it internally
+ */
+ if ([url isFileURL]) {
+ 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;
+ }
+
+ return NO;
+}
+
+- (BOOL)shouldAllowRequestForURL:(NSURL*)url
+{
+ BOOL anyPluginsResponded = NO;
+ BOOL shouldAllowRequest = NO;
+
+ for (NSString* pluginName in pluginObjects) {
+ CDVPlugin* plugin = [pluginObjects objectForKey:pluginName];
+ SEL selector = NSSelectorFromString(@"shouldAllowRequestForURL:");
+ if ([plugin respondsToSelector:selector]) {
+ anyPluginsResponded = YES;
+ shouldAllowRequest = ((BOOL (*)(id, SEL, id))objc_msgSend)(plugin, selector, url);
+ if (!shouldAllowRequest) {
+ break;
+ }
+ }
+ }
+
+ if (anyPluginsResponded) {
+ return shouldAllowRequest;
+ }
+
+ /* Default Policy */
+ return [self defaultResourcePolicyForURL:url];
+}
+
+- (BOOL)shouldAllowNavigationToURL:(NSURL*)url
+{
+ BOOL anyPluginsResponded = NO;
+ BOOL shouldAllowNavigation = NO;
+
+ for (NSString* pluginName in pluginObjects) {
+ CDVPlugin* plugin = [pluginObjects objectForKey:pluginName];
+ SEL selector = NSSelectorFromString(@"shouldAllowNavigationToURL:");
+ if ([plugin respondsToSelector:selector]) {
+ anyPluginsResponded = YES;
+ shouldAllowNavigation = ((BOOL (*)(id, SEL, id))objc_msgSend)(plugin, selector, url);
+ if (!shouldAllowNavigation) {
+ break;
+ }
+ }
+ }
+
+ if (anyPluginsResponded) {
+ return shouldAllowNavigation;
+ }
+
+ /* Default Policy */
+ return [self defaultResourcePolicyForURL:url];
+}
+
+- (BOOL)shouldOpenExternalURL:(NSURL*)url
+{
+ BOOL anyPluginsResponded = NO;
+ BOOL shouldOpenExternalURL = NO;
+
+ for (NSString* pluginName in pluginObjects) {
+ CDVPlugin* plugin = [pluginObjects objectForKey:pluginName];
+ SEL selector = NSSelectorFromString(@"shouldOpenExternalURL:");
+ if ([plugin respondsToSelector:selector]) {
+ anyPluginsResponded = YES;
+ shouldOpenExternalURL = ((BOOL (*)(id, SEL, id))objc_msgSend)(plugin, selector, url);
+ if (!shouldOpenExternalURL) {
+ break;
+ }
+ }
+ }
+
+ if (anyPluginsResponded) {
+ return shouldOpenExternalURL;
+ }
+
+ /* Default policy */
+ return NO;
+}
+
+#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;
+}
+
+#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');"];
++
++ /** Clipboard fix **/
++ UIPasteboard *pasteboard = [UIPasteboard generalPasteboard];
++ NSString *string = pasteboard.string;
++ if (string) {
++ [pasteboard setValue:string forPasteboardType:@"public.text"];
++ }
+}
+
+// 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)];
+}
+
+- (NSInteger*)userAgentLockToken
+{
+ return &_userAgentLockToken;
+}
+
+@end
---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@cordova.apache.org
For additional commands, e-mail: commits-help@cordova.apache.org