You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@cordova.apache.org by ag...@apache.org on 2012/11/23 03:38:31 UTC

[3/4] ios commit: Use the VC's address in the User-Agent instead of a GUID.

Use the VC's address in the User-Agent instead of a GUID.

This allows us to simplify CDVURLProtocol a bit, and to also
remove explicitly setting cordova.iOSVCAddr.

This also changes from caching the with-GUID UA to storing the original
UA. The original UA is more useful.


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

Branch: refs/heads/master
Commit: e8a740176a465092b0d55dc9aee9a3e0ac314691
Parents: 66fed79
Author: Andrew Grieve <ag...@chromium.org>
Authored: Thu Nov 22 21:34:03 2012 -0500
Committer: Andrew Grieve <ag...@chromium.org>
Committed: Thu Nov 22 21:34:03 2012 -0500

----------------------------------------------------------------------
 CordovaLib/Classes/CDVInAppBrowser.m   |    2 +-
 CordovaLib/Classes/CDVURLProtocol.m    |  108 +++++++++++++++------------
 CordovaLib/Classes/CDVViewController.h |    4 +-
 CordovaLib/Classes/CDVViewController.m |   47 +++++++-----
 4 files changed, 90 insertions(+), 71 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/cordova-ios/blob/e8a74017/CordovaLib/Classes/CDVInAppBrowser.m
----------------------------------------------------------------------
diff --git a/CordovaLib/Classes/CDVInAppBrowser.m b/CordovaLib/Classes/CDVInAppBrowser.m
index 006eb41..227ab9a 100644
--- a/CordovaLib/Classes/CDVInAppBrowser.m
+++ b/CordovaLib/Classes/CDVInAppBrowser.m
@@ -80,7 +80,7 @@
 - (void)openInInAppBrowser:(NSURL*)url withOptions:(NSString*)options
 {
     if (self.inAppBrowserViewController == nil) {
-        NSString* originalUA = [self.webView stringByEvaluatingJavaScriptFromString:@"navigator.userAgent"];
+        NSString* originalUA = [CDVViewController originalUserAgent];
 
         CFUUIDRef uuidRef = CFUUIDCreate(kCFAllocatorDefault);
         CFStringRef uuidString = CFUUIDCreateString(kCFAllocatorDefault, uuidRef);

http://git-wip-us.apache.org/repos/asf/cordova-ios/blob/e8a74017/CordovaLib/Classes/CDVURLProtocol.m
----------------------------------------------------------------------
diff --git a/CordovaLib/Classes/CDVURLProtocol.m b/CordovaLib/Classes/CDVURLProtocol.m
index 5878b38..a645288 100644
--- a/CordovaLib/Classes/CDVURLProtocol.m
+++ b/CordovaLib/Classes/CDVURLProtocol.m
@@ -32,8 +32,37 @@ static CDVWhitelist * gWhitelist = nil;
 // Contains a set of NSNumbers of addresses of controllers. It doesn't store
 // the actual pointer to avoid retaining.
 static NSMutableSet* gRegisteredControllers = nil;
-// Contains a set of NSStrings of User Agents
-static NSMutableSet* gRegisteredUserAgents = nil;
+
+// Returns the registered view controller that sent the given request.
+// If the user-agent is not from a UIWebView, or if it's from an unregistered one,
+// then nil is returned.
+static CDVViewController *viewControllerForRequest(NSURLRequest* request)
+{
+    // The exec bridge explicitly sets the VC address in a header.
+    // This works around the User-Agent not being set for file: URLs.
+    NSString* addrString = [request valueForHTTPHeaderField:@"vc"];
+
+    if (addrString == nil) {
+        NSString* userAgent = [request valueForHTTPHeaderField:@"User-Agent"];
+        if (userAgent == nil) {
+            return nil;
+        }
+        NSUInteger bracketLocation = [userAgent rangeOfString:@"(" options:NSBackwardsSearch].location;
+        if (bracketLocation == NSNotFound) {
+            return nil;
+        }
+        addrString = [userAgent substringFromIndex:bracketLocation + 1];
+    }
+
+    long long viewControllerAddress = [addrString longLongValue];
+    @synchronized(gRegisteredControllers) {
+        if (![gRegisteredControllers containsObject:[NSNumber numberWithLongLong:viewControllerAddress]]) {
+            return nil;
+        }
+    }
+
+    return (__bridge CDVViewController*)(void*)viewControllerAddress;
+}
 
 @implementation CDVURLProtocol
 
@@ -48,7 +77,6 @@ static NSMutableSet* gRegisteredUserAgents = nil;
     if (gRegisteredControllers == nil) {
         [NSURLProtocol registerClass:[CDVURLProtocol class]];
         gRegisteredControllers = [[NSMutableSet alloc] initWithCapacity:8];
-        gRegisteredUserAgents = [[NSMutableSet alloc] initWithCapacity:8];
         // The whitelist doesn't change, so grab the first one and store it.
         gWhitelist = viewController.whitelist;
 
@@ -63,65 +91,49 @@ static NSMutableSet* gRegisteredUserAgents = nil;
 
     @synchronized(gRegisteredControllers) {
         [gRegisteredControllers addObject:[NSNumber numberWithLongLong:(long long)viewController]];
-        NSString* userAgent = [viewController.webView stringByEvaluatingJavaScriptFromString:@"navigator.userAgent"];
-        [gRegisteredUserAgents addObject:userAgent];
     }
 }
 
 + (void)unregisterViewController:(CDVViewController*)viewController
 {
-    [gRegisteredControllers removeObject:[NSNumber numberWithLongLong:(long long)viewController]];
-    NSString* userAgent = [viewController.webView stringByEvaluatingJavaScriptFromString:@"navigator.userAgent"];
-    [gRegisteredUserAgents removeObject:userAgent];
+    @synchronized(gRegisteredControllers) {
+        [gRegisteredControllers removeObject:[NSNumber numberWithLongLong:(long long)viewController]];
+    }
 }
 
 + (BOOL)canInitWithRequest:(NSURLRequest*)theRequest
 {
     NSURL* theUrl = [theRequest URL];
-    NSString* theScheme = [theUrl scheme];
-    NSString* theUserAgent = [theRequest valueForHTTPHeaderField:@"User-Agent"];
-
-    if ([[theUrl path] isEqualToString:@"/!gap_exec"]) {
-        NSString* viewControllerAddressStr = [theRequest valueForHTTPHeaderField:@"vc"];
-        if (viewControllerAddressStr == nil) {
-            NSLog(@"!cordova request missing vc header");
-            return NO;
-        }
-        long long viewControllerAddress = [viewControllerAddressStr longLongValue];
-        // Ensure that the CDVViewController has not been dealloc'ed.
-        CDVViewController* viewController = nil;
-        @synchronized(gRegisteredControllers) {
-            if (![gRegisteredControllers containsObject:[NSNumber numberWithLongLong:viewControllerAddress]]) {
+    CDVViewController* viewController = viewControllerForRequest(theRequest);
+
+    if (viewController != nil) {
+        if ([[theUrl path] isEqualToString:@"/!gap_exec"]) {
+            NSString* queuedCommandsJSON = [theRequest valueForHTTPHeaderField:@"cmds"];
+            NSString* requestId = [theRequest valueForHTTPHeaderField:@"rc"];
+            if (requestId == nil) {
+                NSLog(@"!cordova request missing rc header");
                 return NO;
             }
-            viewController = (__bridge CDVViewController*)(void*)viewControllerAddress;
-        }
-
-        NSString* queuedCommandsJSON = [theRequest valueForHTTPHeaderField:@"cmds"];
-        NSString* requestId = [theRequest valueForHTTPHeaderField:@"rc"];
-        if (requestId == nil) {
-            NSLog(@"!cordova request missing rc header");
-            return NO;
+            BOOL hasCmds = [queuedCommandsJSON length] > 0;
+            if (hasCmds) {
+                SEL sel = @selector(enqueCommandBatch:);
+                [viewController.commandQueue performSelectorOnMainThread:sel withObject:queuedCommandsJSON waitUntilDone:NO];
+            } else {
+                SEL sel = @selector(maybeFetchCommandsFromJs:);
+                [viewController.commandQueue performSelectorOnMainThread:sel withObject:[NSNumber numberWithInteger:[requestId integerValue]] waitUntilDone:NO];
+            }
+            // Returning NO here would be 20% faster, but it spams WebInspector's console with failure messages.
+            // If JS->Native bridge speed is really important for an app, they should use the iframe bridge.
+            // Returning YES here causes the request to come through canInitWithRequest two more times.
+            // For this reason, we return NO when cmds exist.
+            return !hasCmds;
         }
-        BOOL hasCmds = [queuedCommandsJSON length] > 0;
-        if (hasCmds) {
-            SEL sel = @selector(enqueCommandBatch:);
-            [viewController.commandQueue performSelectorOnMainThread:sel withObject:queuedCommandsJSON waitUntilDone:NO];
-        } else {
-            SEL sel = @selector(maybeFetchCommandsFromJs:);
-            [viewController.commandQueue performSelectorOnMainThread:sel withObject:[NSNumber numberWithInteger:[requestId integerValue]] waitUntilDone:NO];
+        // we only care about http and https connections.
+        // CORS takes care of http: trying to access file: URLs.
+        if ([gWhitelist schemeIsAllowed:[theUrl scheme]]) {
+            // if it FAILS the whitelist, we return TRUE, so we can fail the connection later
+            return ![gWhitelist URLIsAllowed:theUrl];
         }
-        // Returning NO here would be 20% faster, but it spams WebInspector's console with failure messages.
-        // If JS->Native bridge speed is really important for an app, they should use the iframe bridge.
-        // Returning YES here causes the request to come through canInitWithRequest two more times.
-        // For this reason, we return NO when cmds exist.
-        return !hasCmds;
-    }
-
-    // we only care about http and https connections, and registered user-agents
-    if ([gWhitelist schemeIsAllowed:theScheme] && [gRegisteredUserAgents containsObject:theUserAgent]) {
-        // if it FAILS the whitelist, we return TRUE, so we can fail the connection later
-        return ![gWhitelist URLIsAllowed:theUrl];
     }
 
     return NO;

http://git-wip-us.apache.org/repos/asf/cordova-ios/blob/e8a74017/CordovaLib/Classes/CDVViewController.h
----------------------------------------------------------------------
diff --git a/CordovaLib/Classes/CDVViewController.h b/CordovaLib/Classes/CDVViewController.h
index ab661f8..017af27 100644
--- a/CordovaLib/Classes/CDVViewController.h
+++ b/CordovaLib/Classes/CDVViewController.h
@@ -32,6 +32,7 @@
 @interface CDVViewController : UIViewController <UIWebViewDelegate, CDVScreenOrientationDelegate>{
     @private
     CDVCommandDelegateImpl* _commandDelegate;
+    NSString* _userAgent;
 }
 
 @property (nonatomic, strong) IBOutlet CDVCordovaView* webView;
@@ -51,10 +52,11 @@
 @property (nonatomic, readwrite, copy) NSString* startPage;
 @property (nonatomic, readonly, strong) CDVCommandQueue* commandQueue;
 @property (nonatomic, readonly, strong) CDVCommandDelegateImpl* commandDelegate;
-@property (nonatomic, readonly, copy) NSString* userAgent;
+@property (nonatomic, readonly) NSString* userAgent;
 
 + (NSDictionary*)getBundlePlist:(NSString*)plistName;
 + (NSString*)applicationDocumentsDirectory;
++ (NSString*)originalUserAgent;
 
 - (void)printMultitaskingInfo;
 - (void)createGapView;

http://git-wip-us.apache.org/repos/asf/cordova-ios/blob/e8a74017/CordovaLib/Classes/CDVViewController.m
----------------------------------------------------------------------
diff --git a/CordovaLib/Classes/CDVViewController.m b/CordovaLib/Classes/CDVViewController.m
index 20b9b35..85f3722 100644
--- a/CordovaLib/Classes/CDVViewController.m
+++ b/CordovaLib/Classes/CDVViewController.m
@@ -406,33 +406,39 @@
     return [[CDVCordovaView alloc] initWithFrame:bounds];
 }
 
-- (NSString*)userAgent
++ (NSString*)originalUserAgent
 {
-    NSUserDefaults* userDefaults = [NSUserDefaults standardUserDefaults];
-    NSString* systemVersion = [[UIDevice currentDevice] systemVersion];
+    static NSString* originalUserAgent = nil;
 
-    NSString* cordovaUserAgentVersion = [userDefaults stringForKey:CDV_USER_AGENT_VERSION_KEY];
-    NSString* cordovaUserAgent = [userDefaults stringForKey:CDV_USER_AGENT_KEY];
-    BOOL systemVersionChanged = ![systemVersion isEqualToString:cordovaUserAgentVersion];
+    if (originalUserAgent == nil) {
+        NSUserDefaults* userDefaults = [NSUserDefaults standardUserDefaults];
+        NSString* systemVersion = [[UIDevice currentDevice] systemVersion];
 
-    if ((cordovaUserAgent == nil) || (cordovaUserAgentVersion == nil) || systemVersionChanged) {
-        UIWebView* sampleWebView = [[UIWebView alloc] initWithFrame:CGRectZero];
-        NSString* originalUserAgent = [sampleWebView stringByEvaluatingJavaScriptFromString:@"navigator.userAgent"];
+        NSString* cordovaUserAgentVersion = [userDefaults stringForKey:CDV_USER_AGENT_VERSION_KEY];
+        originalUserAgent = [userDefaults stringForKey:CDV_USER_AGENT_KEY];
+        BOOL systemVersionChanged = ![systemVersion isEqualToString:cordovaUserAgentVersion];
 
-        // generate a GUID to append to the User-Agent
-        CFUUIDRef uuidRef = CFUUIDCreate(kCFAllocatorDefault);
-        CFStringRef uuidString = CFUUIDCreateString(kCFAllocatorDefault, uuidRef);
-        cordovaUserAgent = [NSString stringWithFormat:@"%@ (%@)", originalUserAgent, uuidString];
-        CFRelease(uuidString);
-        CFRelease(uuidRef);
+        if ((originalUserAgent == nil) || systemVersionChanged) {
+            UIWebView* sampleWebView = [[UIWebView alloc] initWithFrame:CGRectZero];
+            originalUserAgent = [sampleWebView stringByEvaluatingJavaScriptFromString:@"navigator.userAgent"];
 
-        [userDefaults setObject:cordovaUserAgent forKey:CDV_USER_AGENT_KEY];
-        [userDefaults setObject:systemVersion forKey:CDV_USER_AGENT_VERSION_KEY];
+            [userDefaults setObject:originalUserAgent forKey:CDV_USER_AGENT_KEY];
+            [userDefaults setObject:systemVersion forKey:CDV_USER_AGENT_VERSION_KEY];
 
-        [userDefaults synchronize];
+            [userDefaults synchronize];
+        }
     }
+    return originalUserAgent;
+}
 
-    return cordovaUserAgent;
+- (NSString*)userAgent
+{
+    if (_userAgent == nil) {
+        NSString* originalUserAgent = [[self class] originalUserAgent];
+        // Use our address as a unique number to append to the User-Agent.
+        _userAgent = [NSString stringWithFormat:@"%@ (%lld)", originalUserAgent, (long long)self];
+    }
+    return _userAgent;
 }
 
 - (void)createGapView
@@ -525,10 +531,9 @@
     }
     [self didRotateFromInterfaceOrientation:(UIInterfaceOrientation)[[UIDevice currentDevice] orientation]];
 
-    // The iOSVCAddr is used to identify the WebView instance when using one of the XHR js->native bridge modes.
     // 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 = [NSString stringWithFormat:@"cordova.iOSVCAddr='%lld';try{cordova.require('cordova/channel').onNativeReady.fire();}catch(e){window._nativeReady = true;}", (long long)self];
+    NSString* nativeReady = @"try{cordova.require('cordova/channel').onNativeReady.fire();}catch(e){window._nativeReady = true;}";
     [self.commandDelegate evalJs:nativeReady];
 }