cordova-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From shaz...@apache.org
Subject [8/9] ios commit: Merge branch 'master' into 4.0.x
Date Tue, 19 May 2015 23:21:19 GMT
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/4.0.x
Commit: d0877d08aa8a720132dc36144d9d187e7d481667
Parents: c23efe1 94a4cb2
Author: Shazron Abdullah <shazron@apache.org>
Authored: Tue May 19 16:15:20 2015 -0700
Committer: Shazron Abdullah <shazron@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


Mime
View raw message