1 /* 2 * libjingle 3 * Copyright 2013, Google Inc. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions are met: 7 * 8 * 1. Redistributions of source code must retain the above copyright notice, 9 * this list of conditions and the following disclaimer. 10 * 2. Redistributions in binary form must reproduce the above copyright notice, 11 * this list of conditions and the following disclaimer in the documentation 12 * and/or other materials provided with the distribution. 13 * 3. The name of the author may not be used to endorse or promote products 14 * derived from this software without specific prior written permission. 15 * 16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED 17 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 18 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO 19 * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 20 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 21 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; 22 * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 23 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR 24 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF 25 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 26 */ 27 28 #import "GAEChannelClient.h" 29 30 #import "RTCPeerConnectionFactory.h" 31 32 #if TARGET_OS_IPHONE 33 34 #import <UIKit/UIKit.h> 35 36 @interface GAEChannelClient () <UIWebViewDelegate> 37 38 @property(nonatomic, strong) UIWebView* webView; 39 40 #else 41 42 #import <WebKit/WebKit.h> 43 44 @interface GAEChannelClient () 45 46 @property(nonatomic, strong) WebView* webView; 47 48 #endif 49 50 @end 51 52 @implementation GAEChannelClient 53 54 - (instancetype)initWithToken:(NSString*)token 55 delegate:(id<GAEMessageHandler>)delegate { 56 NSParameterAssert([token length] > 0); 57 NSParameterAssert(delegate); 58 self = [super init]; 59 if (self) { 60 #if TARGET_OS_IPHONE 61 _webView = [[UIWebView alloc] init]; 62 _webView.delegate = self; 63 #else 64 _webView = [[WebView alloc] init]; 65 _webView.policyDelegate = self; 66 #endif 67 _delegate = delegate; 68 NSString* htmlPath = 69 [[NSBundle mainBundle] pathForResource:@"channel" ofType:@"html"]; 70 NSURL* htmlUrl = [NSURL fileURLWithPath:htmlPath]; 71 NSString* path = [NSString 72 stringWithFormat:@"%@?token=%@", [htmlUrl absoluteString], token]; 73 #if TARGET_OS_IPHONE 74 [_webView 75 #else 76 [[_webView mainFrame] 77 #endif 78 loadRequest:[NSURLRequest requestWithURL:[NSURL URLWithString:path]]]; 79 } 80 return self; 81 } 82 83 - (void)dealloc { 84 #if TARGET_OS_IPHONE 85 _webView.delegate = nil; 86 [_webView stopLoading]; 87 #else 88 _webView.policyDelegate = nil; 89 [[_webView mainFrame] stopLoading]; 90 #endif 91 } 92 93 #if TARGET_OS_IPHONE 94 #pragma mark - UIWebViewDelegate 95 96 - (BOOL)webView:(UIWebView*)webView 97 shouldStartLoadWithRequest:(NSURLRequest*)request 98 navigationType:(UIWebViewNavigationType)navigationType { 99 #else 100 // WebPolicyDelegate is an informal delegate. 101 #pragma mark - WebPolicyDelegate 102 103 - (void)webView:(WebView*)webView 104 decidePolicyForNavigationAction:(NSDictionary*)actionInformation 105 request:(NSURLRequest*)request 106 frame:(WebFrame*)frame 107 decisionListener:(id<WebPolicyDecisionListener>)listener { 108 #endif 109 NSString* scheme = [request.URL scheme]; 110 NSAssert(scheme, @"scheme is nil: %@", request); 111 if (![scheme isEqualToString:@"js-frame"]) { 112 #if TARGET_OS_IPHONE 113 return YES; 114 #else 115 [listener use]; 116 return; 117 #endif 118 } 119 dispatch_async(dispatch_get_main_queue(), ^{ 120 NSString* queuedMessage = [webView 121 stringByEvaluatingJavaScriptFromString:@"popQueuedMessage();"]; 122 NSAssert([queuedMessage length], @"Empty queued message from JS"); 123 124 NSDictionary* queuedMessageDict = 125 [GAEChannelClient jsonStringToDictionary:queuedMessage]; 126 NSString* method = queuedMessageDict[@"type"]; 127 NSAssert(method, @"Missing method: %@", queuedMessageDict); 128 NSDictionary* payload = queuedMessageDict[@"payload"]; // May be nil. 129 130 if ([method isEqualToString:@"onopen"]) { 131 [self.delegate onOpen]; 132 } else if ([method isEqualToString:@"onmessage"]) { 133 NSDictionary* payloadData = 134 [GAEChannelClient jsonStringToDictionary:payload[@"data"]]; 135 [self.delegate onMessage:payloadData]; 136 } else if ([method isEqualToString:@"onclose"]) { 137 [self.delegate onClose]; 138 } else if ([method isEqualToString:@"onerror"]) { 139 NSNumber* codeNumber = payload[@"code"]; 140 int code = [codeNumber intValue]; 141 NSAssert([codeNumber isEqualToNumber:[NSNumber numberWithInt:code]], 142 @"Unexpected non-integral code: %@", payload); 143 [self.delegate onError:code withDescription:payload[@"description"]]; 144 } else { 145 NSAssert(NO, @"Invalid message sent from UIWebView: %@", queuedMessage); 146 } 147 }); 148 #if TARGET_OS_IPHONE 149 return NO; 150 #else 151 [listener ignore]; 152 return; 153 #endif 154 } 155 156 #pragma mark - Private 157 158 + (NSDictionary*)jsonStringToDictionary:(NSString*)str { 159 NSData* data = [str dataUsingEncoding:NSUTF8StringEncoding]; 160 NSError* error; 161 NSDictionary* dict = 162 [NSJSONSerialization JSONObjectWithData:data options:0 error:&error]; 163 NSAssert(!error, @"Invalid JSON? %@", str); 164 return dict; 165 } 166 167 @end 168