Skip to content

feat: add request headers and cookies as new options #1024

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 4 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
182 changes: 153 additions & 29 deletions src/android/InAppBrowser.java
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,8 @@ Licensed to the Apache Software Foundation (ASF) under one
import android.content.Intent;
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
import android.os.Handler;
import android.os.HandlerThread;
import android.os.Parcelable;
import android.provider.Browser;
import android.content.res.Resources;
Expand All @@ -35,6 +37,7 @@ Licensed to the Apache Software Foundation (ASF) under one
import android.os.Build;
import android.os.Bundle;
import android.text.InputType;
import android.util.Base64;
import android.util.TypedValue;
import android.view.Gravity;
import android.view.KeyEvent;
Expand Down Expand Up @@ -63,6 +66,9 @@ Licensed to the Apache Software Foundation (ASF) under one
import android.widget.RelativeLayout;
import android.widget.TextView;

import androidx.annotation.NonNull;
import androidx.annotation.Nullable;

import org.apache.cordova.CallbackContext;
import org.apache.cordova.Config;
import org.apache.cordova.CordovaArgs;
Expand All @@ -80,9 +86,12 @@ Licensed to the Apache Software Foundation (ASF) under one
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Iterator;
import java.util.List;
import java.util.HashMap;
import java.util.Map;
import java.util.StringTokenizer;
import java.util.concurrent.CountDownLatch;

@SuppressLint("SetJavaScriptEnabled")
public class InAppBrowser extends CordovaPlugin {
Expand Down Expand Up @@ -120,8 +129,10 @@ public class InAppBrowser extends CordovaPlugin {
private static final String FULLSCREEN = "fullscreen";

private static final int TOOLBAR_HEIGHT = 48;
private static final String COOKIES = "cookies";
private static final String HEADERS = "headers";

private static final List customizableOptions = Arrays.asList(CLOSE_BUTTON_CAPTION, TOOLBAR_COLOR, NAVIGATION_COLOR, CLOSE_BUTTON_COLOR, FOOTER_COLOR);
private static final List customizableOptions = Arrays.asList(CLOSE_BUTTON_CAPTION, TOOLBAR_COLOR, NAVIGATION_COLOR, CLOSE_BUTTON_COLOR, FOOTER_COLOR, COOKIES, HEADERS);

private InAppBrowserDialog dialog;
private WebView inAppWebView;
Expand Down Expand Up @@ -152,6 +163,12 @@ public class InAppBrowser extends CordovaPlugin {
private String[] allowedSchemes;
private InAppBrowserClient currentClient;

@Nullable
private Map<String, String> headers;
@Nullable
private Map<String, String> cookies;


/**
* Executes the request and returns PluginResult.
*
Expand All @@ -170,12 +187,16 @@ public boolean execute(String action, CordovaArgs args, final CallbackContext ca
}
final String target = t;
final HashMap<String, String> features = parseFeature(args.optString(2));
parseHeadersAndCookies(features);

LOG.d(LOG_TAG, "target = " + target);

this.cordova.getActivity().runOnUiThread(new Runnable() {
@Override
public void run() {

setCookies(clearAllCache, clearSessionCache, cookies);

String result = "";
// SELF
if (SELF.equals(target)) {
Expand Down Expand Up @@ -273,7 +294,7 @@ public void run() {
} else {
((InAppBrowserClient)inAppWebView.getWebViewClient()).waitForBeforeload = false;
}
inAppWebView.loadUrl(url);
inAppWebView.loadUrl(url,headers);

}
});
Expand Down Expand Up @@ -380,6 +401,18 @@ public void onDestroy() {
closeDialog();
}

private void parseHeadersAndCookies(HashMap<String, String> features){
String headersSet = features.get(HEADERS);
if (headersSet != null) {
headers = deserializeMapOption(headersSet);
}

String cookiesSet = features.get(COOKIES);
if (cookiesSet != null) {
cookies = deserializeMapOption(cookiesSet);
}
}

/**
* Inject an object (script or style) into the InAppBrowser WebView.
*
Expand Down Expand Up @@ -948,30 +981,30 @@ public boolean onShowFileChooser (WebView webView, ValueCallback<Uri[]> filePath
settings.setJavaScriptCanOpenWindowsAutomatically(true);
settings.setBuiltInZoomControls(showZoomControls);
settings.setPluginState(android.webkit.WebSettings.PluginState.ON);

// download event

inAppWebView.setDownloadListener(
new DownloadListener(){
public void onDownloadStart(
String url, String userAgent, String contentDisposition, String mimetype, long contentLength
){
try{
JSONObject succObj = new JSONObject();
succObj.put("type", DOWNLOAD_EVENT);
succObj.put("url",url);
succObj.put("userAgent",userAgent);
succObj.put("contentDisposition",contentDisposition);
succObj.put("mimetype",mimetype);
succObj.put("contentLength",contentLength);
sendUpdate(succObj, true);
}
catch(Exception e){
LOG.e(LOG_TAG,e.getMessage());
new DownloadListener(){
public void onDownloadStart(
String url, String userAgent, String contentDisposition, String mimetype, long contentLength
){
try{
JSONObject succObj = new JSONObject();
succObj.put("type", DOWNLOAD_EVENT);
succObj.put("url",url);
succObj.put("userAgent",userAgent);
succObj.put("contentDisposition",contentDisposition);
succObj.put("mimetype",mimetype);
succObj.put("contentLength",contentLength);
sendUpdate(succObj, true);
}
catch(Exception e){
LOG.e(LOG_TAG,e.getMessage());
}
}
}
}
);
);

// Add postMessage interface
class JsObject {
Expand Down Expand Up @@ -1011,16 +1044,10 @@ public void postMessage(String data) {
}
settings.setDomStorageEnabled(true);

if (clearAllCache) {
CookieManager.getInstance().removeAllCookie();
} else if (clearSessionCache) {
CookieManager.getInstance().removeSessionCookie();
}

// Enable Thirdparty Cookies
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);
Expand Down Expand Up @@ -1071,10 +1098,107 @@ public void postMessage(String data) {
}
}
};

this.cordova.getActivity().runOnUiThread(runnable);
return "";
}

private void setCookies(boolean clearAllCache,
boolean clearSessionCache,
@Nullable Map<String, String> cookies) {

int operations = 0;
if (clearAllCache) {
operations++;
}
if (clearSessionCache) {
operations++;
}
if (cookies != null) {
operations += cookies.size();
}

CountDownLatch cookiesCountdown = new CountDownLatch(operations);

Runnable cookiesRunnable = cookiesRunnable(
cookiesCountdown,
clearAllCache,
clearSessionCache,
cookies);

//CookieManager setCookie needs a background Looper to await completion
HandlerThread handlerThread = new HandlerThread("backgroundThread");
if (!handlerThread.isAlive())
handlerThread.start();
Handler threadHandler = new Handler(handlerThread.getLooper());
threadHandler.post(cookiesRunnable); //runs on main thread
try {
cookiesCountdown.await();
} catch (InterruptedException e) {
LOG.e(LOG_TAG, "cookies set was interrupted by timeout.", e);
}
handlerThread.quitSafely();
}

@NonNull
private Runnable cookiesRunnable(
CountDownLatch cookiesCountdown,
boolean clearAllCache,
boolean clearSessionCache,
@Nullable Map<String, String> cookies) {

return () -> {

if (clearAllCache) {
CookieManager.getInstance().removeAllCookies(value -> {
if (!value) {
LOG.e(LOG_TAG, "unable to removeAllCookies in CookieManager!");
}
cookiesCountdown.countDown();
});
} else if (clearSessionCache) {
CookieManager.getInstance().removeSessionCookies(value -> {
if (!value) {
LOG.e(LOG_TAG, "unable to removeSessionCookies in CookieManager!");
}
cookiesCountdown.countDown();
});
}

//Set all cookies from options
if (cookies != null && cookies.size() > 0) {

for (String cookieKey : cookies.keySet()) {
CookieManager.getInstance().setCookie(cookieKey, cookies.get(cookieKey), value -> {
if (!value) {
LOG.e(LOG_TAG, "unable to set the cookie in CookieManager!");
}
cookiesCountdown.countDown();
});
}
}

};
}

@Nullable
private Map<String, String> deserializeMapOption(@NonNull String serializedMapOption) {
Map<String, String> result = new HashMap<>();
String base64 = serializedMapOption.replace("@", "=");
String json = new String(Base64.decode(base64, Base64.DEFAULT));
try {
JSONObject jsonObject = new JSONObject(json);
for (Iterator<String> keys = jsonObject.keys(); keys.hasNext(); ) {
String key = keys.next();
result.put(key, jsonObject.getString(key));
}
} catch (JSONException e) {
LOG.e(LOG_TAG, "headers options are not serialized as a valid json.", e);
return null;
}
return result;
}

/**
* Create a new plugin success result and send it back to JavaScript
*
Expand Down
11 changes: 11 additions & 0 deletions src/android/android.iml
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
<?xml version="1.0" encoding="UTF-8"?>
<module type="JAVA_MODULE" version="4">
<component name="NewModuleRootManager" inherit-compiler-output="true">
<exclude-output />
<content url="file://$MODULE_DIR$">
<sourceFolder url="file://$MODULE_DIR$" isTestSource="false" packagePrefix="org.apache.cordova.inappbrowser" />
</content>
<orderEntry type="inheritedJdk" />
<orderEntry type="sourceFolder" forTests="false" />
</component>
</module>
8 changes: 7 additions & 1 deletion src/ios/CDVInAppBrowserOptions.h
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@
under the License.
*/


@interface CDVInAppBrowserOptions : NSObject {}

@property (nonatomic, assign) BOOL location;
Expand Down Expand Up @@ -45,6 +44,13 @@
@property (nonatomic, assign) BOOL disallowoverscroll;
@property (nonatomic, copy) NSString* beforeload;

@property (nonatomic, copy) NSDictionary* headers;

/**
Key is the cookie name, value is a Set-Cookie HTTP header string representation.
*/
@property (nonatomic, copy) NSDictionary* cookies;

+ (CDVInAppBrowserOptions*)parseOptions:(NSString*)options;

@end
25 changes: 24 additions & 1 deletion src/ios/CDVInAppBrowserOptions.m
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,9 @@ - (id)init
self.toolbarcolor = nil;
self.toolbartranslucent = YES;
self.beforeload = @"";

self.headers = @{};
self.cookies = @{};
}

return self;
Expand All @@ -70,13 +73,33 @@ + (CDVInAppBrowserOptions*)parseOptions:(NSString*)options
NSNumberFormatter* numberFormatter = [[NSNumberFormatter alloc] init];
[numberFormatter setAllowsFloats:YES];
BOOL isNumber = [numberFormatter numberFromString:value_lc] != nil;


//JSON values are base64 encoded
//We need to replace `=` to `@` cause `=` is used as key/value separator for options
//Cookies and headers are serialized in JSON and base64 encoding is need to include JSON into JS->Native options serialization.
BOOL isJson = false;
NSData *base64Value = [[NSData alloc] initWithBase64EncodedString:[value stringByReplacingOccurrencesOfString:@"@" withString:@"="]
options:NSDataBase64DecodingIgnoreUnknownCharacters];

NSError *error = nil;
id jsonValue = nil;
if(base64Value) {
jsonValue = [NSJSONSerialization JSONObjectWithData:base64Value
options:0
error:&error];
if(!error && jsonValue) {
isJson = true;
}
}

// set the property according to the key name
if ([obj respondsToSelector:NSSelectorFromString(key)]) {
if (isNumber) {
[obj setValue:[numberFormatter numberFromString:value_lc] forKey:key];
} else if (isBoolean) {
[obj setValue:[NSNumber numberWithBool:[value_lc isEqualToString:@"yes"]] forKey:key];
} else if (isJson) {
[obj setValue:jsonValue forKey:key];
} else {
[obj setValue:value forKey:key];
}
Expand Down
2 changes: 1 addition & 1 deletion src/ios/CDVWKInAppBrowser.h
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@
@property (nonatomic) NSURL* currentURL;

- (void)close;
- (void)navigateTo:(NSURL*)url;
- (void)navigateTo:(NSURL*)url options:(CDVInAppBrowserOptions*)options;
- (void)showLocationBar:(BOOL)show;
- (void)showToolBar:(BOOL)show : (NSString *) toolbarPosition;
- (void)setCloseButtonTitle:(NSString*)title : (NSString*) colorString : (int) buttonIndex;
Expand Down
Loading