summaryrefslogtreecommitdiff
path: root/StoneIsland/platforms/ios/CordovaLib/Classes/CDVURLProtocol.m
diff options
context:
space:
mode:
authorJules Laplace <jules@okfoc.us>2015-09-10 14:58:03 -0400
committerJules Laplace <jules@okfoc.us>2015-09-10 14:58:03 -0400
commitd73a4b1c5a2540077607dcc4001acbae85980ae4 (patch)
treec30089f1742f9430bb18679dc6664157a5dc66f4 /StoneIsland/platforms/ios/CordovaLib/Classes/CDVURLProtocol.m
parent124e6c0a8d9577b4a30e0b265f5c23d637c41966 (diff)
app skeleton
Diffstat (limited to 'StoneIsland/platforms/ios/CordovaLib/Classes/CDVURLProtocol.m')
-rw-r--r--StoneIsland/platforms/ios/CordovaLib/Classes/CDVURLProtocol.m213
1 files changed, 213 insertions, 0 deletions
diff --git a/StoneIsland/platforms/ios/CordovaLib/Classes/CDVURLProtocol.m b/StoneIsland/platforms/ios/CordovaLib/Classes/CDVURLProtocol.m
new file mode 100644
index 00000000..fce5783a
--- /dev/null
+++ b/StoneIsland/platforms/ios/CordovaLib/Classes/CDVURLProtocol.m
@@ -0,0 +1,213 @@
+/*
+ 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 <AssetsLibrary/ALAsset.h>
+#import <AssetsLibrary/ALAssetRepresentation.h>
+#import <AssetsLibrary/ALAssetsLibrary.h>
+#import <MobileCoreServices/MobileCoreServices.h>
+#import "CDVURLProtocol.h"
+#import "CDVCommandQueue.h"
+#import "CDVWhitelist.h"
+#import "CDVViewController.h"
+
+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;
+
+NSString* const kCDVAssetsLibraryPrefixes = @"assets-library://";
+
+// 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
+
++ (void)registerPGHttpURLProtocol {}
+
++ (void)registerURLProtocol {}
+
+// Called to register the URLProtocol, and to make it away of an instance of
+// a ViewController.
++ (void)registerViewController:(CDVViewController*)viewController
+{
+ if (gRegisteredControllers == nil) {
+ [NSURLProtocol registerClass:[CDVURLProtocol class]];
+ gRegisteredControllers = [[NSMutableSet alloc] initWithCapacity:8];
+ // The whitelist doesn't change, so grab the first one and store it.
+ gWhitelist = viewController.whitelist;
+
+ // Note that we grab the whitelist from the first viewcontroller for now - but this will change
+ // when we allow a registered viewcontroller to have its own whitelist (e.g InAppBrowser)
+ // Differentiating the requests will be through the 'vc' http header below as used for the js->objc bridge.
+ // The 'vc' value is generated by casting the viewcontroller object to a (long long) value (see CDVViewController::webViewDidFinishLoad)
+ if (gWhitelist == nil) {
+ NSLog(@"WARNING: NO whitelist has been set in CDVURLProtocol.");
+ }
+ }
+
+ @synchronized(gRegisteredControllers) {
+ [gRegisteredControllers addObject:[NSNumber numberWithLongLong:(long long)viewController]];
+ }
+}
+
++ (void)unregisterViewController:(CDVViewController*)viewController
+{
+ @synchronized(gRegisteredControllers) {
+ [gRegisteredControllers removeObject:[NSNumber numberWithLongLong:(long long)viewController]];
+ }
+}
+
++ (BOOL)canInitWithRequest:(NSURLRequest*)theRequest
+{
+ NSURL* theUrl = [theRequest URL];
+ CDVViewController* viewController = viewControllerForRequest(theRequest);
+
+ if ([[theUrl absoluteString] hasPrefix:kCDVAssetsLibraryPrefixes]) {
+ return YES;
+ } else 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;
+ }
+ BOOL hasCmds = [queuedCommandsJSON length] > 0;
+ if (hasCmds) {
+ SEL sel = @selector(enqueueCommandBatch:);
+ [viewController.commandQueue performSelectorOnMainThread:sel withObject:queuedCommandsJSON waitUntilDone:NO];
+ [viewController.commandQueue performSelectorOnMainThread:@selector(executePending) withObject:nil waitUntilDone:NO];
+ } else {
+ SEL sel = @selector(processXhrExecBridgePoke:);
+ [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;
+ }
+ // 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];
+ }
+ }
+
+ return NO;
+}
+
++ (NSURLRequest*)canonicalRequestForRequest:(NSURLRequest*)request
+{
+ // NSLog(@"%@ received %@", self, NSStringFromSelector(_cmd));
+ return request;
+}
+
+- (void)startLoading
+{
+ // NSLog(@"%@ received %@ - start", self, NSStringFromSelector(_cmd));
+ NSURL* url = [[self request] URL];
+
+ if ([[url path] isEqualToString:@"/!gap_exec"]) {
+ [self sendResponseWithResponseCode:200 data:nil mimeType:nil];
+ return;
+ } else if ([[url absoluteString] hasPrefix:kCDVAssetsLibraryPrefixes]) {
+ ALAssetsLibraryAssetForURLResultBlock resultBlock = ^(ALAsset* asset) {
+ if (asset) {
+ // We have the asset! Get the data and send it along.
+ ALAssetRepresentation* assetRepresentation = [asset defaultRepresentation];
+ NSString* MIMEType = (__bridge_transfer NSString*)UTTypeCopyPreferredTagWithClass((__bridge CFStringRef)[assetRepresentation UTI], kUTTagClassMIMEType);
+ Byte* buffer = (Byte*)malloc((unsigned long)[assetRepresentation size]);
+ NSUInteger bufferSize = [assetRepresentation getBytes:buffer fromOffset:0.0 length:(NSUInteger)[assetRepresentation size] error:nil];
+ NSData* data = [NSData dataWithBytesNoCopy:buffer length:bufferSize freeWhenDone:YES];
+ [self sendResponseWithResponseCode:200 data:data mimeType:MIMEType];
+ } else {
+ // Retrieving the asset failed for some reason. Send an error.
+ [self sendResponseWithResponseCode:404 data:nil mimeType:nil];
+ }
+ };
+ ALAssetsLibraryAccessFailureBlock failureBlock = ^(NSError* error) {
+ // Retrieving the asset failed for some reason. Send an error.
+ [self sendResponseWithResponseCode:401 data:nil mimeType:nil];
+ };
+
+ ALAssetsLibrary* assetsLibrary = [[ALAssetsLibrary alloc] init];
+ [assetsLibrary assetForURL:url resultBlock:resultBlock failureBlock:failureBlock];
+ return;
+ }
+
+ NSString* body = [gWhitelist errorStringForURL:url];
+ [self sendResponseWithResponseCode:401 data:[body dataUsingEncoding:NSASCIIStringEncoding] mimeType:nil];
+}
+
+- (void)stopLoading
+{
+ // do any cleanup here
+}
+
++ (BOOL)requestIsCacheEquivalent:(NSURLRequest*)requestA toRequest:(NSURLRequest*)requestB
+{
+ return NO;
+}
+
+- (void)sendResponseWithResponseCode:(NSInteger)statusCode data:(NSData*)data mimeType:(NSString*)mimeType
+{
+ if (mimeType == nil) {
+ mimeType = @"text/plain";
+ }
+
+ NSHTTPURLResponse* response = [[NSHTTPURLResponse alloc] initWithURL:[[self request] URL] statusCode:statusCode HTTPVersion:@"HTTP/1.1" headerFields:@{@"Content-Type" : mimeType}];
+
+ [[self client] URLProtocol:self didReceiveResponse:response cacheStoragePolicy:NSURLCacheStorageNotAllowed];
+ if (data != nil) {
+ [[self client] URLProtocol:self didLoadData:data];
+ }
+ [[self client] URLProtocolDidFinishLoading:self];
+}
+
+@end