You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@cordova.apache.org by mm...@apache.org on 2013/02/25 14:41:01 UTC

[26/50] ios commit: [CB-2389] Distinguish top-level from sub-frame navigations.

[CB-2389] Distinguish top-level from sub-frame navigations.


Project: http://git-wip-us.apache.org/repos/asf/cordova-ios/repo
Commit: http://git-wip-us.apache.org/repos/asf/cordova-ios/commit/1bfff2d1
Tree: http://git-wip-us.apache.org/repos/asf/cordova-ios/tree/1bfff2d1
Diff: http://git-wip-us.apache.org/repos/asf/cordova-ios/diff/1bfff2d1

Branch: refs/heads/multipart_plugin_result
Commit: 1bfff2d16b18703930afcbb686d698a1f0861f4f
Parents: 7d64f65
Author: Andrew Grieve <ag...@chromium.org>
Authored: Wed Feb 20 13:16:39 2013 -0500
Committer: Andrew Grieve <ag...@chromium.org>
Committed: Wed Feb 20 13:16:39 2013 -0500

----------------------------------------------------------------------
 CordovaLib/Classes/CDVViewController.m          |   47 ++----
 CordovaLib/Classes/CDVWebViewDelegate.h         |   37 ++++
 CordovaLib/Classes/CDVWebViewDelegate.m         |  157 ++++++++++++++++++
 CordovaLib/CordovaLib.xcodeproj/project.pbxproj |    8 +
 4 files changed, 215 insertions(+), 34 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/cordova-ios/blob/1bfff2d1/CordovaLib/Classes/CDVViewController.m
----------------------------------------------------------------------
diff --git a/CordovaLib/Classes/CDVViewController.m b/CordovaLib/Classes/CDVViewController.m
index f6da41b..d625424 100644
--- a/CordovaLib/Classes/CDVViewController.m
+++ b/CordovaLib/Classes/CDVViewController.m
@@ -23,14 +23,13 @@
 #import "CDVCommandDelegateImpl.h"
 #import "CDVConfigParser.h"
 #import "CDVUserAgentUtil.h"
+#import "CDVWebViewDelegate.h"
 
 #define degreesToRadian(x) (M_PI * (x) / 180.0)
 
 @interface CDVViewController () {
     NSInteger _userAgentLockToken;
-    // Used to distinguish an iframe navigation from a top-level one.
-    NSURL* _topLevelNavigationURL;
-    BOOL _topLevelNavigationHasStartedLoad;
+    CDVWebViewDelegate* _webViewDelegate;
 }
 
 @property (nonatomic, readwrite, strong) NSXMLParser* configParser;
@@ -463,7 +462,8 @@
         [self.view addSubview:self.webView];
         [self.view sendSubviewToBack:self.webView];
 
-        self.webView.delegate = self;
+        _webViewDelegate = [[CDVWebViewDelegate alloc] initWithDelegate:self];
+        self.webView.delegate = _webViewDelegate;
 
         // register this viewcontroller with the NSURLProtocol, only after the User-Agent is set
         [CDVURLProtocol registerViewController:self];
@@ -513,13 +513,9 @@
  */
 - (void)webViewDidStartLoad:(UIWebView*)theWebView
 {
-    // The request of theWebView is not yet set to the new one at this point.
-    if (!_topLevelNavigationHasStartedLoad) {
-        _topLevelNavigationHasStartedLoad = YES;
-        NSLog(@"Started load of %@", _topLevelNavigationURL);
-        [_commandQueue resetRequestId];
-        [[NSNotificationCenter defaultCenter] postNotification:[NSNotification notificationWithName:CDVPluginResetNotification object:nil]];
-    }
+    NSLog(@"Resetting plugins due to page load.");
+    [_commandQueue resetRequestId];
+    [[NSNotificationCenter defaultCenter] postNotification:[NSNotification notificationWithName:CDVPluginResetNotification object:self.webView]];
 }
 
 /**
@@ -527,26 +523,21 @@
  */
 - (void)webViewDidFinishLoad:(UIWebView*)theWebView
 {
+    NSLog(@"Finished load of: %@", theWebView.request.URL);
     // It's safe to release the lock even if this is just a sub-frame that's finished loading.
     [CDVUserAgentUtil releaseLock:&_userAgentLockToken];
 
-    if (![_topLevelNavigationURL isEqual:theWebView.request.URL]) {
-        return;
-    }
-    NSLog(@"Finished load of %@", _topLevelNavigationURL);
-    _topLevelNavigationURL = nil;
-
-    /*
-     * Hide the Top Activity THROBBER in the Battery Bar
-     */
-    [[UIApplication sharedApplication] setNetworkActivityIndicatorVisible:NO];
-
     // The .onNativeReady().fire() will work when cordova.js is already loaded.
     // The _nativeReady = true; is used when this is run before cordova.js is loaded.
     NSString* nativeReady = @"try{cordova.require('cordova/channel').onNativeReady.fire();}catch(e){window._nativeReady = true;}";
     // Don't use [commandDelegate evalJs] here since it relies on cordova.js being loaded already.
     [self.webView stringByEvaluatingJavaScriptFromString:nativeReady];
 
+    /*
+     * Hide the Top Activity THROBBER in the Battery Bar
+     */
+    [[UIApplication sharedApplication] setNetworkActivityIndicatorVisible:NO];
+
     [self processOpenUrl];
 
     [[NSNotificationCenter defaultCenter] postNotification:[NSNotification notificationWithName:CDVPageDidLoadNotification object:nil]];
@@ -563,17 +554,6 @@
 {
     NSURL* url = [request URL];
 
-    // Check if this is just a sub-frame navigation.
-    BOOL isTopLevelNavigation = [url isEqual:[request mainDocumentURL]];
-
-    if (isTopLevelNavigation) {
-        if (_topLevelNavigationURL != nil) {
-            NSLog(@"Warning: _topLevelNavigationURL was not set to nil in shouldStartLoadWithRequest");
-        }
-        _topLevelNavigationHasStartedLoad = NO;
-        _topLevelNavigationURL = url;
-    }
-
     /*
      * Execute any commands queued with cordova.exec() on the JS side.
      * The part of the URL after gap:// is irrelevant.
@@ -850,7 +830,6 @@
     [[NSNotificationCenter defaultCenter] removeObserver:self name:UIApplicationDidEnterBackgroundNotification object:nil];
     [[NSNotificationCenter defaultCenter] removeObserver:self name:NSCurrentLocaleDidChangeNotification object:nil];
     [[NSNotificationCenter defaultCenter] removeObserver:self name:CDVPluginHandleOpenURLNotification object:nil];
-
     self.webView.delegate = nil;
     self.webView = nil;
     [CDVUserAgentUtil releaseLock:&_userAgentLockToken];

http://git-wip-us.apache.org/repos/asf/cordova-ios/blob/1bfff2d1/CordovaLib/Classes/CDVWebViewDelegate.h
----------------------------------------------------------------------
diff --git a/CordovaLib/Classes/CDVWebViewDelegate.h b/CordovaLib/Classes/CDVWebViewDelegate.h
new file mode 100644
index 0000000..8a89a22
--- /dev/null
+++ b/CordovaLib/Classes/CDVWebViewDelegate.h
@@ -0,0 +1,37 @@
+/*
+ Licensed to the Apache Software Foundation (ASF) under one
+ or more contributor license agreements.  See the NOTICE file
+ distributed with this work for additional information
+ regarding copyright ownership.  The ASF licenses this file
+ to you under the Apache License, Version 2.0 (the
+ "License"); you may not use this file except in compliance
+ with the License.  You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing,
+ software distributed under the License is distributed on an
+ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ KIND, either express or implied.  See the License for the
+ specific language governing permissions and limitations
+ under the License.
+ */
+
+#import <UIKit/UIKit.h>
+
+/**
+ * Distinguishes top-level navigations from sub-frame navigations.
+ * shouldStartLoadWithRequest is called for every request, but didStartLoad
+ * and didFinishLoad is called only for top-level navigations.
+ * Relevant bug: CB-2389
+ */
+@interface CDVWebViewDelegate : NSObject <UIWebViewDelegate>{
+    __weak NSObject <UIWebViewDelegate>* _delegate;
+    NSInteger _loadCount;
+    NSInteger _state;
+    NSInteger _curLoadToken;
+}
+
+- (id)initWithDelegate:(NSObject <UIWebViewDelegate>*)delegate;
+
+@end

http://git-wip-us.apache.org/repos/asf/cordova-ios/blob/1bfff2d1/CordovaLib/Classes/CDVWebViewDelegate.m
----------------------------------------------------------------------
diff --git a/CordovaLib/Classes/CDVWebViewDelegate.m b/CordovaLib/Classes/CDVWebViewDelegate.m
new file mode 100644
index 0000000..9ee8186
--- /dev/null
+++ b/CordovaLib/Classes/CDVWebViewDelegate.m
@@ -0,0 +1,157 @@
+/*
+ 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 "CDVWebViewDelegate.h"
+#import "CDVAvailability.h"
+
+typedef enum {
+    STATE_NORMAL,
+    STATE_SHOULD_LOAD_MISSING,
+    STATE_WAITING_FOR_START,
+    STATE_WAITING_FOR_FINISH
+} State;
+
+@implementation CDVWebViewDelegate
+
+- (id)initWithDelegate:(NSObject <UIWebViewDelegate>*)delegate
+{
+    self = [super init];
+    if (self != nil) {
+        _delegate = delegate;
+        _loadCount = -1;
+        _state = STATE_NORMAL;
+    }
+    return self;
+}
+
+- (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:@"%d", _curLoadToken] isEqualToString:loadToken];
+}
+
+- (void)setLoadToken:(UIWebView*)webView
+{
+    _curLoadToken += 1;
+    [webView stringByEvaluatingJavaScriptFromString:[NSString stringWithFormat:@"window.__cordovaLoadToken=%d", _curLoadToken]];
+}
+
+- (void)pollForPageLoadStart:(UIWebView*)webView
+{
+    if ((_state != STATE_WAITING_FOR_START) && (_state != STATE_SHOULD_LOAD_MISSING)) {
+        return;
+    }
+    if (![self isJsLoadTokenSet:webView]) {
+        _state = STATE_WAITING_FOR_FINISH;
+        [self setLoadToken:webView];
+        [_delegate webViewDidStartLoad:webView];
+        [self pollForPageLoadFinish:webView];
+    }
+}
+
+- (void)pollForPageLoadFinish:(UIWebView*)webView
+{
+    if (_state != STATE_WAITING_FOR_FINISH) {
+        return;
+    }
+    if ([self isPageLoaded:webView]) {
+        _state = STATE_SHOULD_LOAD_MISSING;
+        [_delegate webViewDidFinishLoad:webView];
+    } else {
+        [self performSelector:@selector(pollForPageLoaded) withObject:webView afterDelay:50];
+    }
+}
+
+- (BOOL)webView:(UIWebView*)webView shouldStartLoadWithRequest:(NSURLRequest*)request navigationType:(UIWebViewNavigationType)navigationType
+{
+    BOOL shouldLoad = [_delegate webView:webView shouldStartLoadWithRequest:request navigationType:navigationType];
+
+    if (shouldLoad) {
+        BOOL isTopLevelNavigation = [request.URL isEqual:[request mainDocumentURL]];
+        if (isTopLevelNavigation) {
+            _loadCount = 0;
+            _state = STATE_NORMAL;
+        }
+    }
+    return shouldLoad;
+}
+
+- (void)webViewDidStartLoad:(UIWebView*)webView
+{
+    if (_state == STATE_NORMAL) {
+        if (_loadCount == 0) {
+            [_delegate webViewDidStartLoad:webView];
+            _loadCount += 1;
+        } else if (_loadCount > 0) {
+            _loadCount += 1;
+        } else if (!IsAtLeastiOSVersion(@"6.0")) {
+            // If history.go(-1) is used pre-iOS6, the shouldStartLoadWithRequest function is not called.
+            // Without shouldLoad, we can't distinguish an iframe from a top-level navigation.
+            // We could try to distinguish using [UIWebView canGoForward], but that's too much complexity,
+            // and would work only on the first time it was used.
+
+            // Our work-around is to set a JS variable and poll until it disappears (from a naviagtion).
+            _state = STATE_WAITING_FOR_START;
+            [self setLoadToken:webView];
+        }
+    } else {
+        [self pollForPageLoadStart:webView];
+        [self pollForPageLoadFinish:webView];
+    }
+}
+
+- (void)webViewDidFinishLoad:(UIWebView*)webView
+{
+    if (_state == STATE_NORMAL) {
+        if (_loadCount == 1) {
+            [_delegate webViewDidFinishLoad:webView];
+            _loadCount -= 1;
+        } else if (_loadCount > 1) {
+            _loadCount -= 1;
+        }
+    } else {
+        [self pollForPageLoadStart:webView];
+        [self pollForPageLoadFinish:webView];
+    }
+}
+
+- (void)webView:(UIWebView*)webView didFailLoadWithError:(NSError*)error
+{
+    if (_state == STATE_NORMAL) {
+        if (_loadCount == 1) {
+            [_delegate webView:webView didFailLoadWithError:error];
+            _loadCount -= 1;
+        } else if (_loadCount > 1) {
+            _loadCount -= 1;
+        }
+    } else {
+        [self pollForPageLoadStart:webView];
+        [self pollForPageLoadFinish:webView];
+    }
+}
+
+@end

http://git-wip-us.apache.org/repos/asf/cordova-ios/blob/1bfff2d1/CordovaLib/CordovaLib.xcodeproj/project.pbxproj
----------------------------------------------------------------------
diff --git a/CordovaLib/CordovaLib.xcodeproj/project.pbxproj b/CordovaLib/CordovaLib.xcodeproj/project.pbxproj
index bf0d8f7..7855a4f 100644
--- a/CordovaLib/CordovaLib.xcodeproj/project.pbxproj
+++ b/CordovaLib/CordovaLib.xcodeproj/project.pbxproj
@@ -84,6 +84,8 @@
 		EB96673C16A8970A00D86CDF /* CDVUserAgentUtil.m in Sources */ = {isa = PBXBuildFile; fileRef = EB96673A16A8970900D86CDF /* CDVUserAgentUtil.m */; };
 		EBA3557315ABD38C00F4DE24 /* NSArray+Comparisons.h in Headers */ = {isa = PBXBuildFile; fileRef = EBA3557115ABD38C00F4DE24 /* NSArray+Comparisons.h */; settings = {ATTRIBUTES = (Public, ); }; };
 		EBA3557515ABD38C00F4DE24 /* NSArray+Comparisons.m in Sources */ = {isa = PBXBuildFile; fileRef = EBA3557215ABD38C00F4DE24 /* NSArray+Comparisons.m */; };
+		EBFF4DBC16D3FE2E008F452B /* CDVWebViewDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = EBFF4DBA16D3FE2E008F452B /* CDVWebViewDelegate.m */; };
+		EBFF4DBD16D3FE2E008F452B /* CDVWebViewDelegate.h in Headers */ = {isa = PBXBuildFile; fileRef = EBFF4DBB16D3FE2E008F452B /* CDVWebViewDelegate.h */; };
 		F858FBC6166009A8007DA594 /* CDVConfigParser.h in Headers */ = {isa = PBXBuildFile; fileRef = F858FBC4166009A8007DA594 /* CDVConfigParser.h */; settings = {ATTRIBUTES = (Public, ); }; };
 		F858FBC7166009A8007DA594 /* CDVConfigParser.m in Sources */ = {isa = PBXBuildFile; fileRef = F858FBC5166009A8007DA594 /* CDVConfigParser.m */; };
 /* End PBXBuildFile section */
@@ -180,6 +182,8 @@
 		EB96673A16A8970900D86CDF /* CDVUserAgentUtil.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = CDVUserAgentUtil.m; path = Classes/CDVUserAgentUtil.m; sourceTree = "<group>"; };
 		EBA3557115ABD38C00F4DE24 /* NSArray+Comparisons.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "NSArray+Comparisons.h"; path = "Classes/NSArray+Comparisons.h"; sourceTree = "<group>"; };
 		EBA3557215ABD38C00F4DE24 /* NSArray+Comparisons.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = "NSArray+Comparisons.m"; path = "Classes/NSArray+Comparisons.m"; sourceTree = "<group>"; };
+		EBFF4DBA16D3FE2E008F452B /* CDVWebViewDelegate.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = CDVWebViewDelegate.m; path = Classes/CDVWebViewDelegate.m; sourceTree = "<group>"; };
+		EBFF4DBB16D3FE2E008F452B /* CDVWebViewDelegate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = CDVWebViewDelegate.h; path = Classes/CDVWebViewDelegate.h; sourceTree = "<group>"; };
 		F858FBC4166009A8007DA594 /* CDVConfigParser.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = CDVConfigParser.h; path = Classes/CDVConfigParser.h; sourceTree = "<group>"; };
 		F858FBC5166009A8007DA594 /* CDVConfigParser.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = CDVConfigParser.m; path = Classes/CDVConfigParser.m; sourceTree = "<group>"; };
 /* End PBXFileReference section */
@@ -257,6 +261,8 @@
 		888700D710922F56009987E8 /* Commands */ = {
 			isa = PBXGroup;
 			children = (
+				EBFF4DBA16D3FE2E008F452B /* CDVWebViewDelegate.m */,
+				EBFF4DBB16D3FE2E008F452B /* CDVWebViewDelegate.h */,
 				30C5F1DD15AF9E950052A00D /* CDVDevice.h */,
 				30C5F1DE15AF9E950052A00D /* CDVDevice.m */,
 				301F2F2914F3C9CA003FE9FC /* CDV.h */,
@@ -400,6 +406,7 @@
 				F858FBC6166009A8007DA594 /* CDVConfigParser.h in Headers */,
 				30F3930B169F839700B22307 /* CDVJSON.h in Headers */,
 				EB96673B16A8970A00D86CDF /* CDVUserAgentUtil.h in Headers */,
+				EBFF4DBD16D3FE2E008F452B /* CDVWebViewDelegate.h in Headers */,
 			);
 			runOnlyForDeploymentPostprocessing = 0;
 		};
@@ -494,6 +501,7 @@
 				F858FBC7166009A8007DA594 /* CDVConfigParser.m in Sources */,
 				30F3930C169F839700B22307 /* CDVJSON.m in Sources */,
 				EB96673C16A8970A00D86CDF /* CDVUserAgentUtil.m in Sources */,
+				EBFF4DBC16D3FE2E008F452B /* CDVWebViewDelegate.m in Sources */,
 			);
 			runOnlyForDeploymentPostprocessing = 0;
 		};