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;
};