blob: f366bd850a9b6568548310345efcd86968ecc363 [file] [log] [blame]
/*
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 "CDVInAppBrowser.h"
#import "CDVPluginResult.h"
#import "CDVUserAgentUtil.h"
#import "CDVJSON.h"
#define kInAppBrowserTargetSelf @"_self"
#define kInAppBrowserTargetSystem @"_system"
#define kInAppBrowserTargetBlank @"_blank"
#define TOOLBAR_HEIGHT 44.0
#define LOCATIONBAR_HEIGHT 21.0
#define FOOTER_HEIGHT ((TOOLBAR_HEIGHT) + (LOCATIONBAR_HEIGHT))
#pragma mark CDVInAppBrowser
@implementation CDVInAppBrowser
- (CDVInAppBrowser*)initWithWebView:(UIWebView*)theWebView
{
self = [super initWithWebView:theWebView];
if (self != nil) {
// your initialization here
}
return self;
}
- (void)onReset
{
[self close:nil];
}
- (void)close:(CDVInvokedUrlCommand*)command
{
if (self.inAppBrowserViewController != nil) {
[self.inAppBrowserViewController close];
self.inAppBrowserViewController = nil;
}
self.callbackId = nil;
}
- (void)open:(CDVInvokedUrlCommand*)command
{
CDVPluginResult* pluginResult;
NSString* url = [command argumentAtIndex:0];
NSString* target = [command argumentAtIndex:1 withDefault:kInAppBrowserTargetSelf];
NSString* options = [command argumentAtIndex:2 withDefault:@"" andClass:[NSString class]];
self.callbackId = command.callbackId;
if (url != nil) {
NSURL* baseUrl = [self.webView.request URL];
NSURL* absoluteUrl = [[NSURL URLWithString:url relativeToURL:baseUrl] absoluteURL];
if ([target isEqualToString:kInAppBrowserTargetSelf]) {
[self openInCordovaWebView:absoluteUrl withOptions:options];
} else if ([target isEqualToString:kInAppBrowserTargetSystem]) {
[self openInSystem:absoluteUrl];
} else { // _blank or anything else
[self openInInAppBrowser:absoluteUrl withOptions:options];
}
pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK];
} else {
pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsString:@"incorrect number of arguments"];
}
[pluginResult setKeepCallback:[NSNumber numberWithBool:YES]];
[self.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId];
}
- (void)openInInAppBrowser:(NSURL*)url withOptions:(NSString*)options
{
if (self.inAppBrowserViewController == nil) {
NSString* originalUA = [CDVUserAgentUtil originalUserAgent];
self.inAppBrowserViewController = [[CDVInAppBrowserViewController alloc] initWithUserAgent:originalUA prevUserAgent:[self.commandDelegate userAgent]];
self.inAppBrowserViewController.navigationDelegate = self;
if ([self.viewController conformsToProtocol:@protocol(CDVScreenOrientationDelegate)]) {
self.inAppBrowserViewController.orientationDelegate = (UIViewController <CDVScreenOrientationDelegate>*)self.viewController;
}
}
CDVInAppBrowserOptions* browserOptions = [CDVInAppBrowserOptions parseOptions:options];
[self.inAppBrowserViewController showLocationBar:browserOptions.location];
// Set Presentation Style
UIModalPresentationStyle presentationStyle = UIModalPresentationFullScreen; // default
if (browserOptions.presentationstyle != nil) {
if ([browserOptions.presentationstyle isEqualToString:@"pagesheet"]) {
presentationStyle = UIModalPresentationPageSheet;
} else if ([browserOptions.presentationstyle isEqualToString:@"formsheet"]) {
presentationStyle = UIModalPresentationFormSheet;
}
}
self.inAppBrowserViewController.modalPresentationStyle = presentationStyle;
// Set Transition Style
UIModalTransitionStyle transitionStyle = UIModalTransitionStyleCoverVertical; // default
if (browserOptions.transitionstyle != nil) {
if ([browserOptions.transitionstyle isEqualToString:@"fliphorizontal"]) {
transitionStyle = UIModalTransitionStyleFlipHorizontal;
} else if ([browserOptions.transitionstyle isEqualToString:@"crossdissolve"]) {
transitionStyle = UIModalTransitionStyleCrossDissolve;
}
}
self.inAppBrowserViewController.modalTransitionStyle = transitionStyle;
// UIWebView options
self.inAppBrowserViewController.webView.scalesPageToFit = browserOptions.enableviewportscale;
self.inAppBrowserViewController.webView.mediaPlaybackRequiresUserAction = browserOptions.mediaplaybackrequiresuseraction;
self.inAppBrowserViewController.webView.allowsInlineMediaPlayback = browserOptions.allowinlinemediaplayback;
if (IsAtLeastiOSVersion(@"6.0")) {
self.inAppBrowserViewController.webView.keyboardDisplayRequiresUserAction = browserOptions.keyboarddisplayrequiresuseraction;
self.inAppBrowserViewController.webView.suppressesIncrementalRendering = browserOptions.suppressesincrementalrendering;
}
if (self.viewController.modalViewController != self.inAppBrowserViewController) {
[self.viewController presentModalViewController:self.inAppBrowserViewController animated:YES];
}
[self.inAppBrowserViewController navigateTo:url];
}
- (void)openInCordovaWebView:(NSURL*)url withOptions:(NSString*)options
{
if ([self.commandDelegate URLIsWhitelisted:url]) {
NSURLRequest* request = [NSURLRequest requestWithURL:url];
[self.webView loadRequest:request];
} else { // this assumes the InAppBrowser can be excepted from the white-list
[self openInInAppBrowser:url withOptions:options];
}
}
- (void)openInSystem:(NSURL*)url
{
if ([[UIApplication sharedApplication] canOpenURL:url]) {
[[UIApplication sharedApplication] openURL:url];
} else { // handle any custom schemes to plugins
[[NSNotificationCenter defaultCenter] postNotification:[NSNotification notificationWithName:CDVPluginHandleOpenURLNotification object:url]];
}
}
// This is a helper method for the inject{Script|Style}{Code|File} API calls, which
// provides a consistent method for injecting JavaScript code into the document.
//
// If a wrapper string is supplied, then the source string will be JSON-encoded (adding
// quotes) and wrapped using string formatting. (The wrapper string should have a single
// '%@' marker).
//
// If no wrapper is supplied, then the source string is executed directly.
- (void)injectDeferredObject:(NSString*)source withWrapper:(NSString*)jsWrapper
{
if (!_injectedIframeBridge) {
_injectedIframeBridge = YES;
// Create an iframe bridge in the new document to communicate with the CDVInAppBrowserViewController
[self.inAppBrowserViewController.webView stringByEvaluatingJavaScriptFromString:@"(function(d){var e = _cdvIframeBridge = d.createElement('iframe');e.style.display='none';d.body.appendChild(e);})(document)"];
}
if (jsWrapper != nil) {
NSString* sourceArrayString = [@[source] JSONString];
if (sourceArrayString) {
NSString* sourceString = [sourceArrayString substringWithRange:NSMakeRange(1, [sourceArrayString length] - 2)];
NSString* jsToInject = [NSString stringWithFormat:jsWrapper, sourceString];
[self.inAppBrowserViewController.webView stringByEvaluatingJavaScriptFromString:jsToInject];
}
} else {
[self.inAppBrowserViewController.webView stringByEvaluatingJavaScriptFromString:source];
}
}
- (void)injectScriptCode:(CDVInvokedUrlCommand*)command
{
NSString* jsWrapper = nil;
if ((command.callbackId != nil) && ![command.callbackId isEqualToString:@"INVALID"]) {
jsWrapper = [NSString stringWithFormat:@"_cdvIframeBridge.src='gap-iab://%@/'+window.escape(JSON.stringify([eval(%%@)]));", command.callbackId];
}
[self injectDeferredObject:[command argumentAtIndex:0] withWrapper:jsWrapper];
}
- (void)injectScriptFile:(CDVInvokedUrlCommand*)command
{
NSString* jsWrapper;
if ((command.callbackId != nil) && ![command.callbackId isEqualToString:@"INVALID"]) {
jsWrapper = [NSString stringWithFormat:@"(function(d) { var c = d.createElement('script'); c.src = %%@; c.onload = function() { _cdvIframeBridge.src='gap-iab://%@'; }; d.body.appendChild(c); })(document)", command.callbackId];
} else {
jsWrapper = @"(function(d) { var c = d.createElement('script'); c.src = %@; d.body.appendChild(c); })(document)";
}
[self injectDeferredObject:[command argumentAtIndex:0] withWrapper:jsWrapper];
}
- (void)injectStyleCode:(CDVInvokedUrlCommand*)command
{
NSString* jsWrapper;
if ((command.callbackId != nil) && ![command.callbackId isEqualToString:@"INVALID"]) {
jsWrapper = [NSString stringWithFormat:@"(function(d) { var c = d.createElement('style'); c.innerHTML = %%@; c.onload = function() { _cdvIframeBridge.src='gap-iab://%@'; }; d.body.appendChild(c); })(document)", command.callbackId];
} else {
jsWrapper = @"(function(d) { var c = d.createElement('style'); c.innerHTML = %@; d.body.appendChild(c); })(document)";
}
[self injectDeferredObject:[command argumentAtIndex:0] withWrapper:jsWrapper];
}
- (void)injectStyleFile:(CDVInvokedUrlCommand*)command
{
NSString* jsWrapper;
if ((command.callbackId != nil) && ![command.callbackId isEqualToString:@"INVALID"]) {
jsWrapper = [NSString stringWithFormat:@"(function(d) { var c = d.createElement('link'); c.rel='stylesheet'; c.type='text/css'; c.href = %%@; c.onload = function() { _cdvIframeBridge.src='gap-iab://%@'; }; d.body.appendChild(c); })(document)", command.callbackId];
} else {
jsWrapper = @"(function(d) { var c = d.createElement('link'); c.rel='stylesheet', c.type='text/css'; c.href = %@; d.body.appendChild(c); })(document)";
}
[self injectDeferredObject:[command argumentAtIndex:0] withWrapper:jsWrapper];
}
/**
* The iframe bridge provided for the InAppBrowser is capable of executing any oustanding callback belonging
* to the InAppBrowser plugin. Care has been taken that other callbacks cannot be triggered, and that no
* other code execution is possible.
*
* To trigger the bridge, the iframe (or any other resource) should attempt to load a url of the form:
*
* gap-iab://<callbackId>/<arguments>
*
* where <callbackId> is the string id of the callback to trigger (something like "InAppBrowser0123456789")
*
* If present, the path component of the special gap-iab:// url is expected to be a URL-escaped JSON-encoded
* value to pass to the callback. [NSURL path] should take care of the URL-unescaping, and a JSON_EXCEPTION
* is returned if the JSON is invalid.
*/
- (BOOL)webView:(UIWebView*)theWebView shouldStartLoadWithRequest:(NSURLRequest*)request navigationType:(UIWebViewNavigationType)navigationType
{
NSURL* url = request.URL;
// See if the url uses the 'gap-iab' protocol. If so, the host should be the id of a callback to execute,
// and the path, if present, should be a JSON-encoded value to pass to the callback.
if ([[url scheme] isEqualToString:@"gap-iab"]) {
NSString* scriptCallbackId = [url host];
CDVPluginResult* pluginResult = nil;
if ([scriptCallbackId hasPrefix:@"InAppBrowser"]) {
NSString* scriptResult = [url path];
NSError* __autoreleasing error = nil;
// The message should be a JSON-encoded array of the result of the script which executed.
if ((scriptResult != nil) && ([scriptResult length] > 1)) {
scriptResult = [scriptResult substringFromIndex:1];
NSData* decodedResult = [NSJSONSerialization JSONObjectWithData:[scriptResult dataUsingEncoding:NSUTF8StringEncoding] options:kNilOptions error:&error];
if ((error == nil) && [decodedResult isKindOfClass:[NSArray class]]) {
pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsArray:(NSArray*)decodedResult];
} else {
pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_JSON_EXCEPTION];
}
} else {
pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsArray:@[]];
}
[self.commandDelegate sendPluginResult:pluginResult callbackId:scriptCallbackId];
return NO;
}
}
return YES;
}
- (void)webViewDidStartLoad:(UIWebView*)theWebView
{
_injectedIframeBridge = NO;
if (self.callbackId != nil) {
NSString* url = [[self.inAppBrowserViewController requestedURL] absoluteString];
CDVPluginResult* pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK
messageAsDictionary:@{@"type":@"loadstart", @"url":url}];
[pluginResult setKeepCallback:[NSNumber numberWithBool:YES]];
[self.commandDelegate sendPluginResult:pluginResult callbackId:self.callbackId];
}
}
- (void)webViewDidFinishLoad:(UIWebView*)theWebView
{
if (self.callbackId != nil) {
// TODO: It would be more useful to return the URL the page is actually on (e.g. if it's been redirected).
NSString* url = [[self.inAppBrowserViewController requestedURL] absoluteString];
CDVPluginResult* pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK
messageAsDictionary:@{@"type":@"loadstop", @"url":url}];
[pluginResult setKeepCallback:[NSNumber numberWithBool:YES]];
[self.commandDelegate sendPluginResult:pluginResult callbackId:self.callbackId];
}
}
- (void)webView:(UIWebView*)theWebView didFailLoadWithError:(NSError*)error
{
if (self.callbackId != nil) {
NSString* url = [[self.inAppBrowserViewController requestedURL] absoluteString];
CDVPluginResult* pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR
messageAsDictionary:@{@"type":@"loaderror", @"url":url, @"code": [NSNumber numberWithInt:error.code], @"message": error.localizedDescription}];
[pluginResult setKeepCallback:[NSNumber numberWithBool:YES]];
[self.commandDelegate sendPluginResult:pluginResult callbackId:self.callbackId];
}
}
- (void)browserExit
{
if (self.callbackId != nil) {
CDVPluginResult* pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK
messageAsDictionary:@{@"type":@"exit"}];
[pluginResult setKeepCallback:[NSNumber numberWithBool:YES]];
[self.commandDelegate sendPluginResult:pluginResult callbackId:self.callbackId];
}
// Don't recycle the ViewController since it may be consuming a lot of memory.
// Also - this is required for the PDF/User-Agent bug work-around.
self.inAppBrowserViewController = nil;
}
@end
#pragma mark CDVInAppBrowserViewController
@implementation CDVInAppBrowserViewController
@synthesize requestedURL = _requestedURL;
- (id)initWithUserAgent:(NSString*)userAgent prevUserAgent:(NSString*)prevUserAgent
{
self = [super init];
if (self != nil) {
_userAgent = userAgent;
_prevUserAgent = prevUserAgent;
_webViewDelegate = [[CDVWebViewDelegate alloc] initWithDelegate:self];
[self createViews];
}
return self;
}
- (void)createViews
{
// We create the views in code for primarily for ease of upgrades and not requiring an external .xib to be included
CGRect webViewBounds = self.view.bounds;
webViewBounds.size.height -= FOOTER_HEIGHT;
self.webView = [[UIWebView alloc] initWithFrame:webViewBounds];
self.webView.autoresizingMask = (UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight);
[self.view addSubview:self.webView];
[self.view sendSubviewToBack:self.webView];
self.webView.delegate = _webViewDelegate;
self.webView.backgroundColor = [UIColor whiteColor];
self.webView.clearsContextBeforeDrawing = YES;
self.webView.clipsToBounds = YES;
self.webView.contentMode = UIViewContentModeScaleToFill;
self.webView.contentStretch = CGRectFromString(@"{{0, 0}, {1, 1}}");
self.webView.multipleTouchEnabled = YES;
self.webView.opaque = YES;
self.webView.scalesPageToFit = NO;
self.webView.userInteractionEnabled = YES;
self.spinner = [[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleWhite];
self.spinner.alpha = 1.000;
self.spinner.autoresizesSubviews = YES;
self.spinner.autoresizingMask = UIViewAutoresizingFlexibleLeftMargin | UIViewAutoresizingFlexibleTopMargin;
self.spinner.clearsContextBeforeDrawing = NO;
self.spinner.clipsToBounds = NO;
self.spinner.contentMode = UIViewContentModeScaleToFill;
self.spinner.contentStretch = CGRectFromString(@"{{0, 0}, {1, 1}}");
self.spinner.frame = CGRectMake(454.0, 231.0, 20.0, 20.0);
self.spinner.hidden = YES;
self.spinner.hidesWhenStopped = YES;
self.spinner.multipleTouchEnabled = NO;
self.spinner.opaque = NO;
self.spinner.userInteractionEnabled = NO;
[self.spinner stopAnimating];
self.closeButton = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemDone target:self action:@selector(close)];
self.closeButton.enabled = YES;
self.closeButton.imageInsets = UIEdgeInsetsZero;
self.closeButton.style = UIBarButtonItemStylePlain;
self.closeButton.width = 32.000;
UIBarButtonItem* flexibleSpaceButton = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemFlexibleSpace target:nil action:nil];
UIBarButtonItem* fixedSpaceButton = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemFixedSpace target:nil action:nil];
fixedSpaceButton.width = 20;
self.toolbar = [[UIToolbar alloc] initWithFrame:CGRectMake(0.0, (self.view.bounds.size.height - TOOLBAR_HEIGHT), self.view.bounds.size.width, TOOLBAR_HEIGHT)];
self.toolbar.alpha = 1.000;
self.toolbar.autoresizesSubviews = YES;
self.toolbar.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleTopMargin;
self.toolbar.barStyle = UIBarStyleBlackOpaque;
self.toolbar.clearsContextBeforeDrawing = NO;
self.toolbar.clipsToBounds = NO;
self.toolbar.contentMode = UIViewContentModeScaleToFill;
self.toolbar.contentStretch = CGRectFromString(@"{{0, 0}, {1, 1}}");
self.toolbar.hidden = NO;
self.toolbar.multipleTouchEnabled = NO;
self.toolbar.opaque = NO;
self.toolbar.userInteractionEnabled = YES;
CGFloat labelInset = 5.0;
self.addressLabel = [[UILabel alloc] initWithFrame:CGRectMake(labelInset, (self.view.bounds.size.height - FOOTER_HEIGHT), self.view.bounds.size.width - labelInset, LOCATIONBAR_HEIGHT)];
self.addressLabel.adjustsFontSizeToFitWidth = NO;
self.addressLabel.alpha = 1.000;
self.addressLabel.autoresizesSubviews = YES;
self.addressLabel.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleRightMargin | UIViewAutoresizingFlexibleTopMargin;
self.addressLabel.backgroundColor = [UIColor clearColor];
self.addressLabel.baselineAdjustment = UIBaselineAdjustmentAlignCenters;
self.addressLabel.clearsContextBeforeDrawing = YES;
self.addressLabel.clipsToBounds = YES;
self.addressLabel.contentMode = UIViewContentModeScaleToFill;
self.addressLabel.contentStretch = CGRectFromString(@"{{0, 0}, {1, 1}}");
self.addressLabel.enabled = YES;
self.addressLabel.hidden = NO;
self.addressLabel.lineBreakMode = UILineBreakModeTailTruncation;
self.addressLabel.minimumFontSize = 10.000;
self.addressLabel.multipleTouchEnabled = NO;
self.addressLabel.numberOfLines = 1;
self.addressLabel.opaque = NO;
self.addressLabel.shadowOffset = CGSizeMake(0.0, -1.0);
self.addressLabel.text = @"Loading...";
self.addressLabel.textAlignment = UITextAlignmentLeft;
self.addressLabel.textColor = [UIColor colorWithWhite:1.000 alpha:1.000];
self.addressLabel.userInteractionEnabled = NO;
NSString* frontArrowString = @"â–º"; // create arrow from Unicode char
self.forwardButton = [[UIBarButtonItem alloc] initWithTitle:frontArrowString style:UIBarButtonItemStylePlain target:self action:@selector(goForward:)];
self.forwardButton.enabled = YES;
self.forwardButton.imageInsets = UIEdgeInsetsZero;
NSString* backArrowString = @"â—„"; // create arrow from Unicode char
self.backButton = [[UIBarButtonItem alloc] initWithTitle:backArrowString style:UIBarButtonItemStylePlain target:self action:@selector(goBack:)];
self.backButton.enabled = YES;
self.backButton.imageInsets = UIEdgeInsetsZero;
[self.toolbar setItems:@[self.closeButton, flexibleSpaceButton, self.backButton, fixedSpaceButton, self.forwardButton]];
self.view.backgroundColor = [UIColor grayColor];
[self.view addSubview:self.toolbar];
[self.view addSubview:self.addressLabel];
[self.view addSubview:self.spinner];
}
- (void)showLocationBar:(BOOL)show
{
CGRect addressLabelFrame = self.addressLabel.frame;
BOOL locationBarVisible = (addressLabelFrame.size.height > 0);
// prevent double show/hide
if (locationBarVisible == show) {
return;
}
if (show) {
CGRect webViewBounds = self.view.bounds;
webViewBounds.size.height -= FOOTER_HEIGHT;
self.webView.frame = webViewBounds;
CGRect addressLabelFrame = self.addressLabel.frame;
addressLabelFrame.size.height = LOCATIONBAR_HEIGHT;
self.addressLabel.frame = addressLabelFrame;
} else {
CGRect webViewBounds = self.view.bounds;
webViewBounds.size.height -= TOOLBAR_HEIGHT;
self.webView.frame = webViewBounds;
CGRect addressLabelFrame = self.addressLabel.frame;
addressLabelFrame.size.height = 0;
self.addressLabel.frame = addressLabelFrame;
}
}
- (void)viewDidLoad
{
[super viewDidLoad];
}
- (void)viewDidUnload
{
[self.webView loadHTMLString:nil baseURL:nil];
[CDVUserAgentUtil releaseLock:&_userAgentLockToken];
[super viewDidUnload];
}
- (void)close
{
[CDVUserAgentUtil releaseLock:&_userAgentLockToken];
if ([self respondsToSelector:@selector(presentingViewController)]) {
[[self presentingViewController] dismissViewControllerAnimated:YES completion:nil];
} else {
[[self parentViewController] dismissModalViewControllerAnimated:YES];
}
if ((self.navigationDelegate != nil) && [self.navigationDelegate respondsToSelector:@selector(browserExit)]) {
[self.navigationDelegate browserExit];
}
}
- (void)navigateTo:(NSURL*)url
{
NSURLRequest* request = [NSURLRequest requestWithURL:url];
_requestedURL = url;
if (_userAgentLockToken != 0) {
[self.webView loadRequest:request];
} else {
[CDVUserAgentUtil acquireLock:^(NSInteger lockToken) {
_userAgentLockToken = lockToken;
[CDVUserAgentUtil setUserAgent:_userAgent lockToken:lockToken];
[self.webView loadRequest:request];
}];
}
}
- (void)goBack:(id)sender
{
[self.webView goBack];
}
- (void)goForward:(id)sender
{
[self.webView goForward];
}
#pragma mark UIWebViewDelegate
- (void)webViewDidStartLoad:(UIWebView*)theWebView
{
// loading url, start spinner, update back/forward
self.addressLabel.text = @"Loading...";
self.backButton.enabled = theWebView.canGoBack;
self.forwardButton.enabled = theWebView.canGoForward;
[self.spinner startAnimating];
return [self.navigationDelegate webViewDidStartLoad:theWebView];
}
- (BOOL)webView:(UIWebView*)theWebView shouldStartLoadWithRequest:(NSURLRequest*)request navigationType:(UIWebViewNavigationType)navigationType
{
return [self.navigationDelegate webView:theWebView shouldStartLoadWithRequest:request navigationType:navigationType];
}
- (void)webViewDidFinishLoad:(UIWebView*)theWebView
{
// update url, stop spinner, update back/forward
self.addressLabel.text = theWebView.request.URL.absoluteString;
self.backButton.enabled = theWebView.canGoBack;
self.forwardButton.enabled = theWebView.canGoForward;
[self.spinner stopAnimating];
// Work around a bug where the first time a PDF is opened, all UIWebViews
// reload their User-Agent from NSUserDefaults.
// This work-around makes the following assumptions:
// 1. The app has only a single Cordova Webview. If not, then the app should
// take it upon themselves to load a PDF in the background as a part of
// their start-up flow.
// 2. That the PDF does not require any additional network requests. We change
// the user-agent here back to that of the CDVViewController, so requests
// from it must pass through its white-list. This *does* break PDFs that
// contain links to other remote PDF/websites.
// More info at https://issues.apache.org/jira/browse/CB-2225
BOOL isPDF = [@"true" isEqualToString :[theWebView stringByEvaluatingJavaScriptFromString:@"document.body==null"]];
if (isPDF) {
[CDVUserAgentUtil setUserAgent:_prevUserAgent lockToken:_userAgentLockToken];
}
[self.navigationDelegate webViewDidFinishLoad:theWebView];
}
- (void)webView:(UIWebView*)theWebView didFailLoadWithError:(NSError*)error
{
// log fail message, stop spinner, update back/forward
NSLog(@"webView:didFailLoadWithError - %@", [error localizedDescription]);
self.backButton.enabled = theWebView.canGoBack;
self.forwardButton.enabled = theWebView.canGoForward;
[self.spinner stopAnimating];
self.addressLabel.text = @"Load Error";
[self.navigationDelegate webView:theWebView didFailLoadWithError:error];
}
#pragma mark CDVScreenOrientationDelegate
- (BOOL)shouldAutorotate
{
if ((self.orientationDelegate != nil) && [self.orientationDelegate respondsToSelector:@selector(shouldAutorotate)]) {
return [self.orientationDelegate shouldAutorotate];
}
return YES;
}
- (NSUInteger)supportedInterfaceOrientations
{
if ((self.orientationDelegate != nil) && [self.orientationDelegate respondsToSelector:@selector(supportedInterfaceOrientations)]) {
return [self.orientationDelegate supportedInterfaceOrientations];
}
return 1 << UIInterfaceOrientationPortrait;
}
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
{
if ((self.orientationDelegate != nil) && [self.orientationDelegate respondsToSelector:@selector(shouldAutorotateToInterfaceOrientation:)]) {
return [self.orientationDelegate shouldAutorotateToInterfaceOrientation:interfaceOrientation];
}
return YES;
}
@end
@implementation CDVInAppBrowserOptions
- (id)init
{
if (self = [super init]) {
// default values
self.location = YES;
self.enableviewportscale = NO;
self.mediaplaybackrequiresuseraction = NO;
self.allowinlinemediaplayback = NO;
self.keyboarddisplayrequiresuseraction = YES;
self.suppressesincrementalrendering = NO;
}
return self;
}
+ (CDVInAppBrowserOptions*)parseOptions:(NSString*)options
{
CDVInAppBrowserOptions* obj = [[CDVInAppBrowserOptions alloc] init];
// NOTE: this parsing does not handle quotes within values
NSArray* pairs = [options componentsSeparatedByString:@","];
// parse keys and values, set the properties
for (NSString* pair in pairs) {
NSArray* keyvalue = [pair componentsSeparatedByString:@"="];
if ([keyvalue count] == 2) {
NSString* key = [[keyvalue objectAtIndex:0] lowercaseString];
NSString* value = [[keyvalue objectAtIndex:1] lowercaseString];
BOOL isBoolean = [value isEqualToString:@"yes"] || [value isEqualToString:@"no"];
NSNumberFormatter* numberFormatter = [[NSNumberFormatter alloc] init];
[numberFormatter setAllowsFloats:YES];
BOOL isNumber = [numberFormatter numberFromString:value] != nil;
// set the property according to the key name
if ([obj respondsToSelector:NSSelectorFromString(key)]) {
if (isNumber) {
[obj setValue:[numberFormatter numberFromString:value] forKey:key];
} else if (isBoolean) {
[obj setValue:[NSNumber numberWithBool:[value isEqualToString:@"yes"]] forKey:key];
} else {
[obj setValue:value forKey:key];
}
}
}
}
return obj;
}
@end