Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
3052ddd
Customisations
dpa99c Jan 23, 2018
7aa94d3
Bug fix: Webview below 20px under the toolbar on iOS 11.
dpa99c Feb 27, 2018
25756f5
Flag version as custom fork.
dpa99c Feb 27, 2018
a543f59
Add Android support for postMessage API
dpa99c Mar 9, 2018
5c05191
Add iOS support for postMessage API.
dpa99c Nov 6, 2018
e8dc67a
Add support for Cancel option in the list of menu items on Android (e…
dpa99c Jun 11, 2019
09ce277
Support custom accessibility descriptions for buttons on Android
dpa99c Jun 11, 2019
5dd23b8
Support custom accessibility descriptions for buttons on iOS
Jun 12, 2019
6778883
Fixes for iOS 13
Sep 26, 2019
67bd29f
Fix hide animation on iOS 13
Sep 26, 2019
fb47d80
Fix issue where, on building using Xcode 11 (iOS 13 SDK), after closi…
Oct 1, 2019
c20893d
[iOS] Fix full screen display on iPhone X-family. Add extra options: …
Oct 25, 2019
9fed66d
[Android] Add extra options: title.fontSize, toolbar.paddingX
dpa99c Oct 29, 2019
8f9daa3
[iOS] Fix webview height when fullscreen=false
Nov 8, 2019
24254ee
Add try/catch to prevent crashes when dismissing dialog
dpa99c Jan 6, 2020
106853b
Merge branch 'master' of github.com:dpa99c/cordova-plugin-themeablebr…
dpa99c Jan 6, 2020
fb5d21e
WIP: Rework ThemeableBrowser to use WKWebView (fullscreen not working)
Jun 8, 2020
c33da4a
Hack to make notched vs unnotched heights correct in portrait only (d…
Jun 16, 2020
b00803d
Add missing CDVThemeableBrowserUIDelegate
Jun 16, 2020
d3dfdd7
Fix height in landscape and when orientation changes
Jun 16, 2020
4f10b6d
Update/remove deprecations to enable compatibility with cordova-ios@6
Jun 18, 2020
318c645
Make lastReducedStatusBarHeight private so it doesn't conflict with C…
Jun 18, 2020
7bc2b18
Display document title as toolbar title if so configured
Jun 23, 2020
9438e7e
Add missing call to start spinner
dpa99c Jun 23, 2020
a1845f7
Remove remaining references to UIWebView
Jun 30, 2020
3dff818
(iOS) Fix vertical alignment of title when full screen
Sep 7, 2020
b74e0b5
Add Whitelist and WhitelistPlugin classes since the former has been r…
dpa99c Jul 6, 2021
523bce8
bugfix: Prevent nil url from being set on button event dictionary to …
Feb 14, 2022
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
6 changes: 1 addition & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,10 +20,6 @@
cordova-plugin-themeablebrowser
===============================

**This repo is out of maintenance** due to its original mainteners are no longer able to maintain it in an acceptable fashion. Please consider forking this repo if it interests you. Apologies to everyone who still depends on this repo and thanks to everyone who has contributed.

---

This plugin is a fork of [org.apache.cordova.inappbrowser](https://github.com/apache/cordova-plugin-inappbrowser). It attempts to retain most of the features of the InAppBrowser. In fact, for the full list of features inherited from InAppBrowser, please refer to [InAppBrowser's documentation](https://github.com/apache/cordova-plugin-inappbrowser/blob/master/README.md).

The purpose of this plugin is to provide an in-app-browser that can also be configured to match the theme of your app, in order to give it a more immersive look and feel for your app, as well as provide a more consistent look and feel across platforms.
Expand Down Expand Up @@ -378,4 +374,4 @@ One is redefined.
License
-------

This project is licensed under Aapache License 2.0. See [LICENSE](LICENSE) file.
This project is licensed under Aapache License 2.0. See [LICENSE](LICENSE) file.
21 changes: 17 additions & 4 deletions package.json

Large diffs are not rendered by default.

6 changes: 5 additions & 1 deletion plugin.xml
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@
<plugin
xmlns="http://apache.org/cordova/ns/plugins/1.0"
id="cordova-plugin-themeablebrowser"
version="0.2.17">
version="0.2.17-custom">

<name>ThemeableBrowser</name>
<description>Cordova ThemeableBrowser Plugin</description>
Expand Down Expand Up @@ -49,6 +49,8 @@
<source-file src="src/android/ThemeableBrowserDialog.java" target-dir="src/com/initialxy/cordova/themeablebrowser" />
<source-file src="src/android/InAppChromeClient.java" target-dir="src/com/initialxy/cordova/themeablebrowser" />
<source-file src="src/android/ThemeableBrowserUnmarshaller.java" target-dir="src/com/initialxy/cordova/themeablebrowser" />
<source-file src="src/android/Whitelist.java" target-dir="src/com/initialxy/cordova/themeablebrowser" />
<source-file src="src/android/WhitelistPlugin.java" target-dir="src/com/initialxy/cordova/themeablebrowser" />
</platform>

<!-- ios -->
Expand All @@ -64,6 +66,8 @@

<header-file src="src/ios/CDVThemeableBrowser.h" />
<source-file src="src/ios/CDVThemeableBrowser.m" />
<header-file src="src/ios/CDVThemeableBrowserUIDelegate.h" />
<source-file src="src/ios/CDVThemeableBrowserUIDelegate.m" />

<framework src="CoreGraphics.framework" />
</platform>
Expand Down
213 changes: 193 additions & 20 deletions src/android/ThemeableBrowser.java

Large diffs are not rendered by default.

170 changes: 170 additions & 0 deletions src/android/Whitelist.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,170 @@
/*
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.
*/
package com.initialxy.cordova.themeablebrowser;

import java.net.MalformedURLException;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import org.apache.cordova.LOG;

import android.net.Uri;

public class Whitelist {
private static class URLPattern {
public Pattern scheme;
public Pattern host;
public Integer port;
public Pattern path;

private String regexFromPattern(String pattern, boolean allowWildcards) {
final String toReplace = "\\.[]{}()^$?+|";
StringBuilder regex = new StringBuilder();
for (int i=0; i < pattern.length(); i++) {
char c = pattern.charAt(i);
if (c == '*' && allowWildcards) {
regex.append(".");
} else if (toReplace.indexOf(c) > -1) {
regex.append('\\');
}
regex.append(c);
}
return regex.toString();
}

public URLPattern(String scheme, String host, String port, String path) throws MalformedURLException {
try {
if (scheme == null || "*".equals(scheme)) {
this.scheme = null;
} else {
this.scheme = Pattern.compile(regexFromPattern(scheme, false), Pattern.CASE_INSENSITIVE);
}
if ("*".equals(host)) {
this.host = null;
} else if (host.startsWith("*.")) {
this.host = Pattern.compile("([a-z0-9.-]*\\.)?" + regexFromPattern(host.substring(2), false), Pattern.CASE_INSENSITIVE);
} else {
this.host = Pattern.compile(regexFromPattern(host, false), Pattern.CASE_INSENSITIVE);
}
if (port == null || "*".equals(port)) {
this.port = null;
} else {
this.port = Integer.parseInt(port,10);
}
if (path == null || "/*".equals(path)) {
this.path = null;
} else {
this.path = Pattern.compile(regexFromPattern(path, true));
}
} catch (NumberFormatException e) {
throw new MalformedURLException("Port must be a number");
}
}

public boolean matches(Uri uri) {
try {
return ((scheme == null || scheme.matcher(uri.getScheme()).matches()) &&
(host == null || host.matcher(uri.getHost()).matches()) &&
(port == null || port.equals(uri.getPort())) &&
(path == null || path.matcher(uri.getPath()).matches()));
} catch (Exception e) {
LOG.d(TAG, e.toString());
return false;
}
}
}

private ArrayList<URLPattern> whiteList;

public static final String TAG = "Whitelist";

public Whitelist() {
this.whiteList = new ArrayList<URLPattern>();
}

/* Match patterns (from http://developer.chrome.com/extensions/match_patterns.html)
*
* <url-pattern> := <scheme>://<host><path>
* <scheme> := '*' | 'http' | 'https' | 'file' | 'ftp' | 'chrome-extension'
* <host> := '*' | '*.' <any char except '/' and '*'>+
* <path> := '/' <any chars>
*
* We extend this to explicitly allow a port attached to the host, and we allow
* the scheme to be omitted for backwards compatibility. (Also host is not required
* to begin with a "*" or "*.".)
*/
public void addWhiteListEntry(String origin, boolean subdomains) {
if (whiteList != null) {
try {
// Unlimited access to network resources
if (origin.compareTo("*") == 0) {
LOG.d(TAG, "Unlimited access to network resources");
whiteList = null;
}
else { // specific access
Pattern parts = Pattern.compile("^((\\*|[A-Za-z-]+):(//)?)?(\\*|((\\*\\.)?[^*/:]+))?(:(\\d+))?(/.*)?");
Matcher m = parts.matcher(origin);
if (m.matches()) {
String scheme = m.group(2);
String host = m.group(4);
// Special case for two urls which are allowed to have empty hosts
if (("file".equals(scheme) || "content".equals(scheme)) && host == null) host = "*";
String port = m.group(8);
String path = m.group(9);
if (scheme == null) {
// XXX making it stupid friendly for people who forget to include protocol/SSL
whiteList.add(new URLPattern("http", host, port, path));
whiteList.add(new URLPattern("https", host, port, path));
} else {
whiteList.add(new URLPattern(scheme, host, port, path));
}
}
}
} catch (Exception e) {
LOG.d(TAG, "Failed to add origin %s", origin);
}
}
}


/**
* Determine if URL is in approved list of URLs to load.
*
* @param uri
* @return true if wide open or whitelisted
*/
public boolean isUrlWhiteListed(String uri) {
// If there is no whitelist, then it's wide open
if (whiteList == null) return true;

Uri parsedUri = Uri.parse(uri);
// Look for match in white list
Iterator<URLPattern> pit = whiteList.iterator();
while (pit.hasNext()) {
URLPattern p = pit.next();
if (p.matches(parsedUri)) {
return true;
}
}
return false;
}

}
160 changes: 160 additions & 0 deletions src/android/WhitelistPlugin.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,160 @@
/*
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.
*/

package com.initialxy.cordova.themeablebrowser;

import org.apache.cordova.CordovaPlugin;
import org.apache.cordova.ConfigXmlParser;
import org.apache.cordova.LOG;
import org.xmlpull.v1.XmlPullParser;

import android.content.Context;

public class WhitelistPlugin extends CordovaPlugin {
private static final String LOG_TAG = "WhitelistPlugin";
private Whitelist allowedNavigations;
private Whitelist allowedIntents;
private Whitelist allowedRequests;

// Used when instantiated via reflection by PluginManager
public WhitelistPlugin() {
}
// These can be used by embedders to allow Java-configuration of whitelists.
public WhitelistPlugin(Context context) {
this(new Whitelist(), new Whitelist(), null);
new CustomConfigXmlParser().parse(context);
}
public WhitelistPlugin(XmlPullParser xmlParser) {
this(new Whitelist(), new Whitelist(), null);
new CustomConfigXmlParser().parse(xmlParser);
}
public WhitelistPlugin(Whitelist allowedNavigations, Whitelist allowedIntents, Whitelist allowedRequests) {
if (allowedRequests == null) {
allowedRequests = new Whitelist();
allowedRequests.addWhiteListEntry("file:///*", false);
allowedRequests.addWhiteListEntry("data:*", false);
}
this.allowedNavigations = allowedNavigations;
this.allowedIntents = allowedIntents;
this.allowedRequests = allowedRequests;
}
@Override
public void pluginInitialize() {
if (allowedNavigations == null) {
allowedNavigations = new Whitelist();
allowedIntents = new Whitelist();
allowedRequests = new Whitelist();
new CustomConfigXmlParser().parse(webView.getContext());
}
}

private class CustomConfigXmlParser extends ConfigXmlParser {
@Override
public void handleStartTag(XmlPullParser xml) {
String strNode = xml.getName();
if (strNode.equals("content")) {
String startPage = xml.getAttributeValue(null, "src");
allowedNavigations.addWhiteListEntry(startPage, false);
} else if (strNode.equals("allow-navigation")) {
String origin = xml.getAttributeValue(null, "href");
if ("*".equals(origin)) {
allowedNavigations.addWhiteListEntry("http://*/*", false);
allowedNavigations.addWhiteListEntry("https://*/*", false);
allowedNavigations.addWhiteListEntry("data:*", false);
} else {
allowedNavigations.addWhiteListEntry(origin, false);
}
} else if (strNode.equals("allow-intent")) {
String origin = xml.getAttributeValue(null, "href");
allowedIntents.addWhiteListEntry(origin, false);
} else if (strNode.equals("access")) {
String origin = xml.getAttributeValue(null, "origin");
String subdomains = xml.getAttributeValue(null, "subdomains");
boolean external = (xml.getAttributeValue(null, "launch-external") != null);
if (origin != null) {
if (external) {
LOG.w(LOG_TAG, "Found <access launch-external> within config.xml. Please use <allow-intent> instead.");
allowedIntents.addWhiteListEntry(origin, (subdomains != null) && (subdomains.compareToIgnoreCase("true") == 0));
} else {
if ("*".equals(origin)) {
allowedRequests.addWhiteListEntry("http://*/*", false);
allowedRequests.addWhiteListEntry("https://*/*", false);
} else {
allowedRequests.addWhiteListEntry(origin, (subdomains != null) && (subdomains.compareToIgnoreCase("true") == 0));
}
}
}
}
}
@Override
public void handleEndTag(XmlPullParser xml) {
}
}

@Override
public Boolean shouldAllowNavigation(String url) {
if (allowedNavigations.isUrlWhiteListed(url)) {
return true;
}
return null; // Default policy
}

@Override
public Boolean shouldAllowRequest(String url) {
if (Boolean.TRUE == shouldAllowNavigation(url)) {
return true;
}
if (allowedRequests.isUrlWhiteListed(url)) {
return true;
}
return null; // Default policy
}

@Override
public Boolean shouldOpenExternalUrl(String url) {
if (allowedIntents.isUrlWhiteListed(url)) {
return true;
}
return null; // Default policy
}

public Whitelist getAllowedNavigations() {
return allowedNavigations;
}

public void setAllowedNavigations(Whitelist allowedNavigations) {
this.allowedNavigations = allowedNavigations;
}

public Whitelist getAllowedIntents() {
return allowedIntents;
}

public void setAllowedIntents(Whitelist allowedIntents) {
this.allowedIntents = allowedIntents;
}

public Whitelist getAllowedRequests() {
return allowedRequests;
}

public void setAllowedRequests(Whitelist allowedRequests) {
this.allowedRequests = allowedRequests;
}
}
Loading