Skip to content

BEKBAPP-2954 re-integrate fork solution (https://github.com/apache/co… #1062

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

Closed
wants to merge 2 commits into from
Closed
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
12 changes: 12 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,18 @@ description: Open an in-app browser window.
-->


# Reason for fork
[PR](https://github.com/apache/cordova-plugin-inappbrowser/pull/1024) for passing cookies to the InAppBrowser was already available.
However it is not getting merged.

That's why we did following things:
- Created a [fork](https://github.com/edorex/cordova-plugin-inappbrowser):
- Created a feature branch `headers-cookies`
- Included all the changes from the inital [PR](https://github.com/apache/cordova-plugin-inappbrowser/pull/1024)
- Created [PR](https://github.com/edorex/cordova-plugin-inappbrowser/pull/1062) to merge it into master
- Referrencing our forked and updated master branch in the project.


# cordova-plugin-inappbrowser

[![Android Testsuite](https://github.com/apache/cordova-plugin-inappbrowser/actions/workflows/android.yml/badge.svg)](https://github.com/apache/cordova-plugin-inappbrowser/actions/workflows/android.yml) [![Chrome Testsuite](https://github.com/apache/cordova-plugin-inappbrowser/actions/workflows/chrome.yml/badge.svg)](https://github.com/apache/cordova-plugin-inappbrowser/actions/workflows/chrome.yml) [![iOS Testsuite](https://github.com/apache/cordova-plugin-inappbrowser/actions/workflows/ios.yml/badge.svg)](https://github.com/apache/cordova-plugin-inappbrowser/actions/workflows/ios.yml) [![Lint Test](https://github.com/apache/cordova-plugin-inappbrowser/actions/workflows/lint.yml/badge.svg)](https://github.com/apache/cordova-plugin-inappbrowser/actions/workflows/lint.yml)
Expand Down
142 changes: 133 additions & 9 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 @@ -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
7 changes: 7 additions & 0 deletions src/ios/CDVInAppBrowserOptions.h
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,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
23 changes: 23 additions & 0 deletions 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 @@ -71,12 +74,32 @@ + (CDVInAppBrowserOptions*)parseOptions:(NSString*)options
[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
36 changes: 32 additions & 4 deletions src/ios/CDVWKInAppBrowser.m
Original file line number Diff line number Diff line change
Expand Up @@ -151,6 +151,30 @@ - (void)openInInAppBrowser:(NSURL*)url withOptions:(NSString*)options
}];
}

// Set all cookies
WKHTTPCookieStore* cookieStore = dataStore.httpCookieStore;
for(NSString* key in browserOptions.cookies){

NSURL* url = [NSURL URLWithString:key];
if(!url){
NSLog(@"Cookie key %@ is not a proper NSURL!",key);
continue;
}

NSArray<NSHTTPCookie*> *cookies = [NSHTTPCookie cookiesWithResponseHeaderFields:@{ @"Set-Cookie" : browserOptions.cookies[key] } forURL:url];

if(cookies.count == 0) {
NSLog(@"No cookies to process for url %@!",url);
}

for(NSHTTPCookie* cookie in cookies){
//Is not possible to wait for completion because it seems the handler is called after the WKWebView is loaded
//See: https://stackoverflow.com/questions/49452968/wkhttpcookiestore-setcookie-completion-handler-not-called
[cookieStore setCookie:cookie completionHandler:nil];
}

}

if (self.inAppBrowserViewController == nil) {
self.inAppBrowserViewController = [[CDVWKInAppBrowserViewController alloc] initWithBrowserOptions: browserOptions andSettings:self.commandDelegate.settings];
self.inAppBrowserViewController.navigationDelegate = self;
Expand Down Expand Up @@ -209,7 +233,7 @@ - (void)openInInAppBrowser:(NSURL*)url withOptions:(NSString*)options
}
_waitForBeforeload = ![_beforeload isEqualToString:@""];

[self.inAppBrowserViewController navigateTo:url];
[self.inAppBrowserViewController navigateTo:url options:browserOptions];
if (!browserOptions.hidden) {
[self show:nil withNoAnimate:browserOptions.hidden];
}
Expand Down Expand Up @@ -322,7 +346,7 @@ - (void)loadAfterBeforeload:(CDVInvokedUrlCommand*)command
NSURL* url = [NSURL URLWithString:urlStr];
//_beforeload = @"";
_waitForBeforeload = NO;
[self.inAppBrowserViewController navigateTo:url];
[self.inAppBrowserViewController navigateTo:url options:nil];
}

// This is a helper method for the inject{Script|Style}{Code|File} API calls, which
Expand Down Expand Up @@ -1041,12 +1065,16 @@ - (void)close
});
}

- (void)navigateTo:(NSURL*)url
- (void)navigateTo:(NSURL*)url options:(CDVInAppBrowserOptions*)options
{
if ([url.scheme isEqualToString:@"file"]) {
[self.webView loadFileURL:url allowingReadAccessToURL:url];
} else {
NSURLRequest* request = [NSURLRequest requestWithURL:url];
NSMutableURLRequest* request = [NSMutableURLRequest requestWithURL:url];
for(NSString* key in options.headers) {
NSString* value = options.headers[key];
[request setValue:value forHTTPHeaderField:key];
}
[self.webView loadRequest:request];
}
}
Expand Down