From 1c36ea73feb89f609e85069af45d01167b045ce7 Mon Sep 17 00:00:00 2001 From: alain lhomme Date: Fri, 30 Nov 2018 16:22:51 +0100 Subject: [PATCH] (issue-#361) (iOS & Android) add request headers support --- README.md | 5 +++- src/android/InAppBrowser.java | 45 +++++++++++++++++++++++++------- src/ios/CDVInAppBrowserOptions.h | 1 + src/ios/CDVInAppBrowserOptions.m | 18 +++++++++++++ src/ios/CDVUIInAppBrowser.h | 2 +- src/ios/CDVUIInAppBrowser.m | 19 +++++++------- src/ios/CDVWKInAppBrowser.h | 2 +- src/ios/CDVWKInAppBrowser.m | 21 ++++++++------- www/inappbrowser.js | 25 +++++++++++++++--- 9 files changed, 104 insertions(+), 34 deletions(-) diff --git a/README.md b/README.md index 503dd83..d99c858 100644 --- a/README.md +++ b/README.md @@ -83,7 +83,7 @@ simply hook `window.open` during initialization. For example: Opens a URL in a new `InAppBrowser` instance, the current browser instance, or the system browser. - var ref = cordova.InAppBrowser.open(url, target, options); + var ref = cordova.InAppBrowser.open(url, target, options, headers); - __ref__: Reference to the `InAppBrowser` window when the target is set to `'_blank'`. _(InAppBrowser)_ @@ -158,6 +158,9 @@ instance, or the system browser. - __hardwareback__: works the same way as on Android platform. - __fullscreen__: set to `yes` to create the browser control without a border around it. Please note that if __location=no__ is also specified, there will be no control presented to user to close IAB window. +- __headers__: Headers for the http request. Optional. _(String)_ or _(javascript object)_ + - _(String)_: headers must be in `header=value` form, separated by commas : `header1=value1,header2=value2`. don't use _(String)_ if commas or equals can be contained in headers or values. + - _(javascript object)_: headers are stored in object's properties like this `{ 'header1': 'value1', 'header2': 'value2'}`. this storage always works even if headers contain commas or equals. ### Supported Platforms diff --git a/src/android/InAppBrowser.java b/src/android/InAppBrowser.java index 93d9460..cefc01b 100644 --- a/src/android/InAppBrowser.java +++ b/src/android/InAppBrowser.java @@ -160,6 +160,7 @@ public class InAppBrowser extends CordovaPlugin { } final String target = t; final HashMap features = parseFeature(args.optString(2)); + final HashMap headers = parseHeaders(args.optString(3)); LOG.d(LOG_TAG, "target = " + target); @@ -225,7 +226,7 @@ public class InAppBrowser extends CordovaPlugin { // load in InAppBrowser else { LOG.d(LOG_TAG, "loading in InAppBrowser"); - result = showWebPage(url, features); + result = showWebPage(url, features, headers); } } // SYSTEM @@ -236,7 +237,7 @@ public class InAppBrowser extends CordovaPlugin { // BLANK - or anything else else { LOG.d(LOG_TAG, "in blank"); - result = showWebPage(url, features); + result = showWebPage(url, features, headers); } PluginResult pluginResult = new PluginResult(PluginResult.Status.OK, result); @@ -258,7 +259,7 @@ public class InAppBrowser extends CordovaPlugin { @Override public void run() { ((InAppBrowserClient)inAppWebView.getWebViewClient()).waitForBeforeload = false; - inAppWebView.loadUrl(url); + inAppWebView.loadUrl(url, null); } }); } @@ -395,7 +396,7 @@ public class InAppBrowser extends CordovaPlugin { public void run() { if (Build.VERSION.SDK_INT < Build.VERSION_CODES.KITKAT) { // This action will have the side-effect of blurring the currently focused element - inAppWebView.loadUrl("javascript:" + finalScriptToInject); + inAppWebView.loadUrl("javascript:" + finalScriptToInject, null); } else { inAppWebView.evaluateJavascript(finalScriptToInject, null); } @@ -434,6 +435,31 @@ public class InAppBrowser extends CordovaPlugin { } } + /** + * Put the headers string into a hash map + * + * @param headersString string of headers comma separated (key=value) + * @return map of headers + */ + private HashMap parseHeaders(String headersString) { + if (headersString.equals(NULL)) { + return null; + } else { + HashMap map = new HashMap(); + StringTokenizer headers = new StringTokenizer(headersString, ","); + StringTokenizer header; + while(headers.hasMoreElements()) { + header = new StringTokenizer(headers.nextToken(), "="); + if (header.hasMoreElements()) { + String key = header.nextToken().replace("@e","=").replace("@c", ",").replace("@a","@"); + String value = header.nextToken().replace("@e","=").replace("@c", ",").replace("@a","@"); + map.put(key, value); + } + } + return map; + } + } + /** * Display a new browser with the specified URL. * @@ -529,7 +555,7 @@ public class InAppBrowser extends CordovaPlugin { // NB: From SDK 19: "If you call methods on WebView from any thread // other than your app's UI thread, it can cause unexpected results." // http://developer.android.com/guide/webapps/migrating.html#Threads - childView.loadUrl("about:blank"); + childView.loadUrl("about:blank", null); try { JSONObject obj = new JSONObject(); @@ -586,9 +612,9 @@ public class InAppBrowser extends CordovaPlugin { imm.hideSoftInputFromWindow(edittext.getWindowToken(), 0); if (!url.startsWith("http") && !url.startsWith("file:")) { - this.inAppWebView.loadUrl("http://" + url); + this.inAppWebView.loadUrl("http://" + url, null); } else { - this.inAppWebView.loadUrl(url); + this.inAppWebView.loadUrl(url, null); } this.inAppWebView.requestFocus(); } @@ -612,8 +638,9 @@ public class InAppBrowser extends CordovaPlugin { * * @param url the url to load. * @param features jsonObject + * @param headers headers for navigation */ - public String showWebPage(final String url, HashMap features) { + public String showWebPage(final String url, HashMap features, final HashMap headers) { // Determine if we should hide the location bar. showLocationBar = true; showZoomControls = true; @@ -987,7 +1014,7 @@ public class InAppBrowser extends CordovaPlugin { CookieManager.getInstance().setAcceptThirdPartyCookies(inAppWebView,true); } - inAppWebView.loadUrl(url); + inAppWebView.loadUrl(url, headers); inAppWebView.setId(Integer.valueOf(6)); inAppWebView.getSettings().setLoadWithOverviewMode(true); inAppWebView.getSettings().setUseWideViewPort(useWideViewPort); diff --git a/src/ios/CDVInAppBrowserOptions.h b/src/ios/CDVInAppBrowserOptions.h index 14b7a7a..3592c7f 100644 --- a/src/ios/CDVInAppBrowserOptions.h +++ b/src/ios/CDVInAppBrowserOptions.h @@ -48,5 +48,6 @@ @property (nonatomic, assign) BOOL beforeload; + (CDVInAppBrowserOptions*)parseOptions:(NSString*)options; ++ (NSMutableURLRequest*)createRequest:(NSURL*)url headers:(NSString*)headers; @end diff --git a/src/ios/CDVInAppBrowserOptions.m b/src/ios/CDVInAppBrowserOptions.m index e1cc7d3..855c55a 100644 --- a/src/ios/CDVInAppBrowserOptions.m +++ b/src/ios/CDVInAppBrowserOptions.m @@ -88,4 +88,22 @@ return obj; } ++ (NSMutableURLRequest*)createRequest:(NSURL*)url headers:(NSString*)headers +{ + NSMutableURLRequest* request = [NSMutableURLRequest requestWithURL:url]; + if (headers != nil) { + NSArray* pairs = [headers componentsSeparatedByString:@","]; + for (NSString* pair in pairs) { + NSArray* keyvalue = [pair componentsSeparatedByString:@"="]; + + if ([keyvalue count] == 2) { + NSString* key = [[[[keyvalue objectAtIndex:0] stringByReplacingOccurrencesOfString:@"@e" withString:@"="] stringByReplacingOccurrencesOfString:@"@c" withString:@","] stringByReplacingOccurrencesOfString:@"@a" withString:@"@"]; + NSString* value = [[[[keyvalue objectAtIndex:1] stringByReplacingOccurrencesOfString:@"@e" withString:@"="] stringByReplacingOccurrencesOfString:@"@c" withString:@","] stringByReplacingOccurrencesOfString:@"@a" withString:@"@"]; + [request setValue:value forHTTPHeaderField:key]; + } + } + } + return request; +} + @end diff --git a/src/ios/CDVUIInAppBrowser.h b/src/ios/CDVUIInAppBrowser.h index 55c3662..0dc2a2d 100644 --- a/src/ios/CDVUIInAppBrowser.h +++ b/src/ios/CDVUIInAppBrowser.h @@ -81,7 +81,7 @@ @property (nonatomic) NSURL* currentURL; - (void)close; -- (void)navigateTo:(NSURL*)url; +- (void)navigateTo:(NSURL*)url headers:(NSString *)headers; - (void)showLocationBar:(BOOL)show; - (void)showToolBar:(BOOL)show : (NSString *) toolbarPosition; - (void)setCloseButtonTitle:(NSString*)title : (NSString*) colorString; diff --git a/src/ios/CDVUIInAppBrowser.m b/src/ios/CDVUIInAppBrowser.m index 501987a..6fcfe1e 100644 --- a/src/ios/CDVUIInAppBrowser.m +++ b/src/ios/CDVUIInAppBrowser.m @@ -93,6 +93,7 @@ static CDVUIInAppBrowser* instance = nil; NSString* url = [command argumentAtIndex:0]; NSString* target = [command argumentAtIndex:1 withDefault:kInAppBrowserTargetSelf]; NSString* options = [command argumentAtIndex:2 withDefault:@"" andClass:[NSString class]]; + NSString* headers = [command argumentAtIndex:3 withDefault:@"" andClass:[NSString class]]; self.callbackId = command.callbackId; @@ -109,11 +110,11 @@ static CDVUIInAppBrowser* instance = nil; } if ([target isEqualToString:kInAppBrowserTargetSelf]) { - [self openInCordovaWebView:absoluteUrl withOptions:options]; + [self openInCordovaWebView:absoluteUrl withOptions:options withHeaders:headers]; } else if ([target isEqualToString:kInAppBrowserTargetSystem]) { [self openInSystem:absoluteUrl]; } else { // _blank or anything else - [self openInInAppBrowser:absoluteUrl withOptions:options]; + [self openInInAppBrowser:absoluteUrl withOptions:options withHeaders:headers]; } pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK]; @@ -125,7 +126,7 @@ static CDVUIInAppBrowser* instance = nil; [self.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId]; } -- (void)openInInAppBrowser:(NSURL*)url withOptions:(NSString*)options +- (void)openInInAppBrowser:(NSURL*)url withOptions:(NSString*)options withHeaders:(NSString*)headers { CDVInAppBrowserOptions* browserOptions = [CDVInAppBrowserOptions parseOptions:options]; @@ -222,7 +223,7 @@ static CDVUIInAppBrowser* instance = nil; _useBeforeload = browserOptions.beforeload; _waitForBeforeload = browserOptions.beforeload; - [self.inAppBrowserViewController navigateTo:url]; + [self.inAppBrowserViewController navigateTo:url headers:headers]; if (!browserOptions.hidden) { [self show:nil]; } @@ -291,9 +292,9 @@ static CDVUIInAppBrowser* instance = nil; }); } -- (void)openInCordovaWebView:(NSURL*)url withOptions:(NSString*)options +- (void)openInCordovaWebView:(NSURL*)url withOptions:(NSString*)options withHeaders:(NSString*)headers { - NSURLRequest* request = [NSURLRequest requestWithURL:url]; + NSMutableURLRequest* request = [CDVInAppBrowserOptions createRequest:url headers:headers]; #ifdef __CORDOVA_4_0_0 // the webview engine itself will filter for this according to policy @@ -334,7 +335,7 @@ static CDVUIInAppBrowser* instance = nil; NSURL* url = [NSURL URLWithString:urlStr]; _waitForBeforeload = NO; - [self.inAppBrowserViewController navigateTo:url]; + [self.inAppBrowserViewController navigateTo:url headers:nil]; } // This is a helper method for the inject{Script|Style}{Code|File} API calls, which @@ -901,9 +902,9 @@ static CDVUIInAppBrowser* instance = nil; }); } -- (void)navigateTo:(NSURL*)url +- (void)navigateTo:(NSURL*)url headers:(NSString*)headers { - NSURLRequest* request = [NSURLRequest requestWithURL:url]; + NSMutableURLRequest* request = [CDVInAppBrowserOptions createRequest:url headers:headers]; if (_userAgentLockToken != 0) { [self.webView loadRequest:request]; diff --git a/src/ios/CDVWKInAppBrowser.h b/src/ios/CDVWKInAppBrowser.h index 8b5925e..0e7ed58 100644 --- a/src/ios/CDVWKInAppBrowser.h +++ b/src/ios/CDVWKInAppBrowser.h @@ -70,7 +70,7 @@ @property (nonatomic) NSURL* currentURL; - (void)close; -- (void)navigateTo:(NSURL*)url; +- (void)navigateTo:(NSURL*)url headers:(NSString *)headers; - (void)showLocationBar:(BOOL)show; - (void)showToolBar:(BOOL)show : (NSString *) toolbarPosition; - (void)setCloseButtonTitle:(NSString*)title : (NSString*) colorString; diff --git a/src/ios/CDVWKInAppBrowser.m b/src/ios/CDVWKInAppBrowser.m index 6c1b51a..9ff7f52 100644 --- a/src/ios/CDVWKInAppBrowser.m +++ b/src/ios/CDVWKInAppBrowser.m @@ -100,6 +100,7 @@ static CDVWKInAppBrowser* instance = nil; NSString* url = [command argumentAtIndex:0]; NSString* target = [command argumentAtIndex:1 withDefault:kInAppBrowserTargetSelf]; NSString* options = [command argumentAtIndex:2 withDefault:@"" andClass:[NSString class]]; + NSString* headers = [command argumentAtIndex:3 withDefault:@"" andClass:[NSString class]]; self.callbackId = command.callbackId; @@ -116,11 +117,11 @@ static CDVWKInAppBrowser* instance = nil; } if ([target isEqualToString:kInAppBrowserTargetSelf]) { - [self openInCordovaWebView:absoluteUrl withOptions:options]; + [self openInCordovaWebView:absoluteUrl withOptions:options withHeaders:headers]; } else if ([target isEqualToString:kInAppBrowserTargetSystem]) { [self openInSystem:absoluteUrl]; } else { // _blank or anything else - [self openInInAppBrowser:absoluteUrl withOptions:options]; + [self openInInAppBrowser:absoluteUrl withOptions:options withHeaders:headers]; } pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK]; @@ -132,7 +133,7 @@ static CDVWKInAppBrowser* instance = nil; [self.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId]; } -- (void)openInInAppBrowser:(NSURL*)url withOptions:(NSString*)options +- (void)openInInAppBrowser:(NSURL*)url withOptions:(NSString*)options withHeaders:(NSString*)headers { CDVInAppBrowserOptions* browserOptions = [CDVInAppBrowserOptions parseOptions:options]; @@ -269,7 +270,7 @@ static CDVWKInAppBrowser* instance = nil; _useBeforeload = browserOptions.beforeload; _waitForBeforeload = browserOptions.beforeload; - [self.inAppBrowserViewController navigateTo:url]; + [self.inAppBrowserViewController navigateTo:url headers:headers]; [self show:nil withNoAnimate:browserOptions.hidden]; } @@ -352,9 +353,9 @@ static CDVWKInAppBrowser* instance = nil; }); } -- (void)openInCordovaWebView:(NSURL*)url withOptions:(NSString*)options +- (void)openInCordovaWebView:(NSURL*)url withOptions:(NSString*)options withHeaders:(NSString*)headers { - NSURLRequest* request = [NSURLRequest requestWithURL:url]; + NSMutableURLRequest* request = [CDVInAppBrowserOptions createRequest:url headers:headers]; #ifdef __CORDOVA_4_0_0 // the webview engine itself will filter for this according to policy @@ -364,7 +365,7 @@ static CDVWKInAppBrowser* instance = nil; if ([self.commandDelegate URLIsWhitelisted:url]) { [self.webView loadRequest:request]; } else { // this assumes the InAppBrowser can be excepted from the white-list - [self openInInAppBrowser:url withOptions:options]; + [self openInInAppBrowser:url withOptions:options withHeaders:headers]; } #endif } @@ -393,7 +394,7 @@ static CDVWKInAppBrowser* instance = nil; NSURL* url = [NSURL URLWithString:urlStr]; _waitForBeforeload = NO; - [self.inAppBrowserViewController navigateTo:url]; + [self.inAppBrowserViewController navigateTo:url headers:nil]; } // This is a helper method for the inject{Script|Style}{Code|File} API calls, which @@ -1020,9 +1021,9 @@ BOOL isExiting = FALSE; }); } -- (void)navigateTo:(NSURL*)url +- (void)navigateTo:(NSURL*)url headers:(NSString*)headers { - NSURLRequest* request = [NSURLRequest requestWithURL:url]; + NSMutableURLRequest* request = [CDVInAppBrowserOptions createRequest:url headers:headers]; if (_userAgentLockToken != 0) { [self.webView loadRequest:request]; diff --git a/www/inappbrowser.js b/www/inappbrowser.js index 7764765..caa1419 100644 --- a/www/inappbrowser.js +++ b/www/inappbrowser.js @@ -97,7 +97,7 @@ } }; - module.exports = function (strUrl, strWindowName, strWindowFeatures, callbacks) { + module.exports = function (strUrl, strWindowName, strWindowFeatures, windowHeaders, callbacks) { // Don't catch calls that write to existing frames (e.g. named iframes). if (window.frames && window.frames[strWindowName]) { var origOpenFunc = modulemapper.getOriginalSymbol(window, 'open'); @@ -115,10 +115,29 @@ var cb = function (eventname) { iab._eventHandler(eventname); }; - + var strWindowHeaders = ''; + if (windowHeaders) { + if (typeof windowHeaders === 'string' || windowHeaders instanceof String) { + strWindowHeaders = windowHeaders.replace(/@/gi, '@a'); + } else { + var first = true; + for (var k in windowHeaders) { + if (windowHeaders.hasOwnProperty(k)) { + var key = k.replace(/@/gi, '@a').replace(/,/gi, '@c').replace(/=/gi, '@e'); + var value = windowHeaders[k].toString().replace(/@/gi, '@a').replace(/,/gi, '@c').replace(/=/gi, '@e'); + if (first) { + first = false; + } else { + strWindowHeaders += ','; + } + strWindowHeaders += key + '=' + value; + } + } + } + } strWindowFeatures = strWindowFeatures || ''; - exec(cb, cb, 'InAppBrowser', 'open', [strUrl, strWindowName, strWindowFeatures]); + exec(cb, cb, 'InAppBrowser', 'open', [strUrl, strWindowName, strWindowFeatures, strWindowHeaders]); return iab; }; })();