forked from github/cordova-android
Compare commits
8 Commits
master
...
unplug-whi
Author | SHA1 | Date | |
---|---|---|---|
![]() |
198364afb9 | ||
![]() |
ed78b557cd | ||
![]() |
83377d366a | ||
![]() |
8df2d4fcfd | ||
![]() |
a0acb4ce9a | ||
![]() |
fe15d34a80 | ||
![]() |
23584274d2 | ||
![]() |
44aa98887f |
@ -86,9 +86,8 @@ public class AndroidWebView extends WebView implements CordovaWebView {
|
|||||||
private WebChromeClient.CustomViewCallback mCustomViewCallback;
|
private WebChromeClient.CustomViewCallback mCustomViewCallback;
|
||||||
|
|
||||||
private CordovaResourceApi resourceApi;
|
private CordovaResourceApi resourceApi;
|
||||||
private Whitelist internalWhitelist;
|
|
||||||
private Whitelist externalWhitelist;
|
|
||||||
private CordovaPreferences preferences;
|
private CordovaPreferences preferences;
|
||||||
|
private CordovaUriHelper helper;
|
||||||
// The URL passed to loadUrl(), not necessarily the URL of the current page.
|
// The URL passed to loadUrl(), not necessarily the URL of the current page.
|
||||||
String loadedUrl;
|
String loadedUrl;
|
||||||
|
|
||||||
@ -111,19 +110,17 @@ public class AndroidWebView extends WebView implements CordovaWebView {
|
|||||||
// Use two-phase init so that the control will work with XML layouts.
|
// Use two-phase init so that the control will work with XML layouts.
|
||||||
@Override
|
@Override
|
||||||
public void init(CordovaInterface cordova, List<PluginEntry> pluginEntries,
|
public void init(CordovaInterface cordova, List<PluginEntry> pluginEntries,
|
||||||
Whitelist internalWhitelist, Whitelist externalWhitelist,
|
|
||||||
CordovaPreferences preferences) {
|
CordovaPreferences preferences) {
|
||||||
if (this.cordova != null) {
|
if (this.cordova != null) {
|
||||||
throw new IllegalStateException();
|
throw new IllegalStateException();
|
||||||
}
|
}
|
||||||
this.cordova = cordova;
|
this.cordova = cordova;
|
||||||
this.internalWhitelist = internalWhitelist;
|
|
||||||
this.externalWhitelist = externalWhitelist;
|
|
||||||
this.preferences = preferences;
|
this.preferences = preferences;
|
||||||
|
this.helper = new CordovaUriHelper(cordova, this);
|
||||||
|
|
||||||
pluginManager = new PluginManager(this, this.cordova, pluginEntries);
|
pluginManager = new PluginManager(this, this.cordova, pluginEntries);
|
||||||
resourceApi = new CordovaResourceApi(this.getContext(), pluginManager);
|
resourceApi = new CordovaResourceApi(this.getContext(), pluginManager);
|
||||||
bridge = new CordovaBridge(pluginManager, new NativeToJsMessageQueue(this, cordova));
|
bridge = new CordovaBridge(pluginManager, new NativeToJsMessageQueue(this, cordova), helper);
|
||||||
pluginManager.addService("App", "org.apache.cordova.CoreAndroid");
|
pluginManager.addService("App", "org.apache.cordova.CoreAndroid");
|
||||||
initWebViewSettings();
|
initWebViewSettings();
|
||||||
|
|
||||||
@ -354,7 +351,7 @@ public class AndroidWebView extends WebView implements CordovaWebView {
|
|||||||
if (LOG.isLoggable(LOG.DEBUG) && !url.startsWith("javascript:")) {
|
if (LOG.isLoggable(LOG.DEBUG) && !url.startsWith("javascript:")) {
|
||||||
LOG.d(TAG, ">>> loadUrlNow()");
|
LOG.d(TAG, ">>> loadUrlNow()");
|
||||||
}
|
}
|
||||||
if (url.startsWith("file://") || url.startsWith("javascript:") || internalWhitelist.isUrlWhiteListed(url)) {
|
if (url.startsWith("javascript:") || helper.shouldAllowNavigation(url)) {
|
||||||
super.loadUrl(url);
|
super.loadUrl(url);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -429,7 +426,7 @@ public class AndroidWebView extends WebView implements CordovaWebView {
|
|||||||
if (!openExternal) {
|
if (!openExternal) {
|
||||||
|
|
||||||
// Make sure url is in whitelist
|
// Make sure url is in whitelist
|
||||||
if (url.startsWith("file://") || internalWhitelist.isUrlWhiteListed(url)) {
|
if (helper.shouldAllowNavigation(url)) {
|
||||||
// TODO: What about params?
|
// TODO: What about params?
|
||||||
// Load new URL
|
// Load new URL
|
||||||
loadUrlIntoView(url, true);
|
loadUrlIntoView(url, true);
|
||||||
@ -748,16 +745,6 @@ public class AndroidWebView extends WebView implements CordovaWebView {
|
|||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public Whitelist getWhitelist() {
|
|
||||||
return this.internalWhitelist;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Whitelist getExternalWhitelist() {
|
|
||||||
return this.externalWhitelist;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public CordovaPreferences getPreferences() {
|
public CordovaPreferences getPreferences() {
|
||||||
return preferences;
|
return preferences;
|
||||||
|
@ -46,48 +46,6 @@ public class Config {
|
|||||||
parser = new ConfigXmlParser();
|
parser = new ConfigXmlParser();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Add entry to approved list of URLs (whitelist)
|
|
||||||
*
|
|
||||||
* @param origin URL regular expression to allow
|
|
||||||
* @param subdomains T=include all subdomains under origin
|
|
||||||
*/
|
|
||||||
public static void addWhiteListEntry(String origin, boolean subdomains) {
|
|
||||||
if (parser == null) {
|
|
||||||
Log.e(TAG, "Config was not initialised. Did you forget to Config.init(this)?");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
parser.getInternalWhitelist().addWhiteListEntry(origin, subdomains);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Determine if URL is in approved list of URLs to load.
|
|
||||||
*
|
|
||||||
* @param url
|
|
||||||
* @return true if whitelisted
|
|
||||||
*/
|
|
||||||
public static boolean isUrlWhiteListed(String url) {
|
|
||||||
if (parser == null) {
|
|
||||||
Log.e(TAG, "Config was not initialised. Did you forget to Config.init(this)?");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return parser.getInternalWhitelist().isUrlWhiteListed(url);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Determine if URL is in approved list of URLs to launch external applications.
|
|
||||||
*
|
|
||||||
* @param url
|
|
||||||
* @return true if whitelisted
|
|
||||||
*/
|
|
||||||
public static boolean isUrlExternallyWhiteListed(String url) {
|
|
||||||
if (parser == null) {
|
|
||||||
Log.e(TAG, "Config was not initialised. Did you forget to Config.init(this)?");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return parser.getExternalWhitelist().isUrlWhiteListed(url);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static String getStartUrl() {
|
public static String getStartUrl() {
|
||||||
if (parser == null) {
|
if (parser == null) {
|
||||||
@ -100,14 +58,6 @@ public class Config {
|
|||||||
return parser.getPreferences().getString("errorurl", null);
|
return parser.getPreferences().getString("errorurl", null);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Whitelist getWhitelist() {
|
|
||||||
return parser.getInternalWhitelist();
|
|
||||||
}
|
|
||||||
|
|
||||||
public static Whitelist getExternalWhitelist() {
|
|
||||||
return parser.getExternalWhitelist();
|
|
||||||
}
|
|
||||||
|
|
||||||
public static List<PluginEntry> getPluginEntries() {
|
public static List<PluginEntry> getPluginEntries() {
|
||||||
return parser.getPluginEntries();
|
return parser.getPluginEntries();
|
||||||
}
|
}
|
||||||
|
@ -36,18 +36,8 @@ public class ConfigXmlParser {
|
|||||||
|
|
||||||
private String launchUrl = "file:///android_asset/www/index.html";
|
private String launchUrl = "file:///android_asset/www/index.html";
|
||||||
private CordovaPreferences prefs = new CordovaPreferences();
|
private CordovaPreferences prefs = new CordovaPreferences();
|
||||||
private Whitelist internalWhitelist = new Whitelist();
|
|
||||||
private Whitelist externalWhitelist = new Whitelist();
|
|
||||||
private ArrayList<PluginEntry> pluginEntries = new ArrayList<PluginEntry>(20);
|
private ArrayList<PluginEntry> pluginEntries = new ArrayList<PluginEntry>(20);
|
||||||
|
|
||||||
public Whitelist getInternalWhitelist() {
|
|
||||||
return internalWhitelist;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Whitelist getExternalWhitelist() {
|
|
||||||
return externalWhitelist;
|
|
||||||
}
|
|
||||||
|
|
||||||
public CordovaPreferences getPreferences() {
|
public CordovaPreferences getPreferences() {
|
||||||
return prefs;
|
return prefs;
|
||||||
}
|
}
|
||||||
@ -59,7 +49,7 @@ public class ConfigXmlParser {
|
|||||||
public String getLaunchUrl() {
|
public String getLaunchUrl() {
|
||||||
return launchUrl;
|
return launchUrl;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void parse(Activity action) {
|
public void parse(Activity action) {
|
||||||
// First checking the class namespace for config.xml
|
// First checking the class namespace for config.xml
|
||||||
int id = action.getResources().getIdentifier("config", "xml", action.getClass().getPackage().getName());
|
int id = action.getResources().getIdentifier("config", "xml", action.getClass().getPackage().getName());
|
||||||
@ -74,78 +64,20 @@ public class ConfigXmlParser {
|
|||||||
parse(action.getResources().getXml(id));
|
parse(action.getResources().getXml(id));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
boolean insideFeature = false;
|
||||||
|
String service = "", pluginClass = "", paramType = "";
|
||||||
|
boolean onload = false;
|
||||||
|
|
||||||
public void parse(XmlResourceParser xml) {
|
public void parse(XmlResourceParser xml) {
|
||||||
int eventType = -1;
|
int eventType = -1;
|
||||||
String service = "", pluginClass = "", paramType = "";
|
|
||||||
boolean onload = false;
|
|
||||||
boolean insideFeature = false;
|
|
||||||
|
|
||||||
// Add implicitly allowed URLs
|
|
||||||
internalWhitelist.addWhiteListEntry("file:///*", false);
|
|
||||||
internalWhitelist.addWhiteListEntry("content:///*", false);
|
|
||||||
internalWhitelist.addWhiteListEntry("data:*", false);
|
|
||||||
|
|
||||||
while (eventType != XmlResourceParser.END_DOCUMENT) {
|
while (eventType != XmlResourceParser.END_DOCUMENT) {
|
||||||
if (eventType == XmlResourceParser.START_TAG) {
|
if (eventType == XmlResourceParser.START_TAG) {
|
||||||
String strNode = xml.getName();
|
handleStartTag(xml);
|
||||||
if (strNode.equals("feature")) {
|
|
||||||
//Check for supported feature sets aka. plugins (Accelerometer, Geolocation, etc)
|
|
||||||
//Set the bit for reading params
|
|
||||||
insideFeature = true;
|
|
||||||
service = xml.getAttributeValue(null, "name");
|
|
||||||
}
|
|
||||||
else if (insideFeature && strNode.equals("param")) {
|
|
||||||
paramType = xml.getAttributeValue(null, "name");
|
|
||||||
if (paramType.equals("service")) // check if it is using the older service param
|
|
||||||
service = xml.getAttributeValue(null, "value");
|
|
||||||
else if (paramType.equals("package") || paramType.equals("android-package"))
|
|
||||||
pluginClass = xml.getAttributeValue(null,"value");
|
|
||||||
else if (paramType.equals("onload"))
|
|
||||||
onload = "true".equals(xml.getAttributeValue(null, "value"));
|
|
||||||
}
|
|
||||||
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) {
|
|
||||||
externalWhitelist.addWhiteListEntry(origin, (subdomains != null) && (subdomains.compareToIgnoreCase("true") == 0));
|
|
||||||
} else {
|
|
||||||
if ("*".equals(origin)) {
|
|
||||||
// Special-case * origin to mean http and https when used for internal
|
|
||||||
// whitelist. This prevents external urls like sms: and geo: from being
|
|
||||||
// handled internally.
|
|
||||||
internalWhitelist.addWhiteListEntry("http://*/*", false);
|
|
||||||
internalWhitelist.addWhiteListEntry("https://*/*", false);
|
|
||||||
} else {
|
|
||||||
internalWhitelist.addWhiteListEntry(origin, (subdomains != null) && (subdomains.compareToIgnoreCase("true") == 0));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if (strNode.equals("preference")) {
|
|
||||||
String name = xml.getAttributeValue(null, "name").toLowerCase(Locale.ENGLISH);
|
|
||||||
String value = xml.getAttributeValue(null, "value");
|
|
||||||
prefs.set(name, value);
|
|
||||||
}
|
|
||||||
else if (strNode.equals("content")) {
|
|
||||||
String src = xml.getAttributeValue(null, "src");
|
|
||||||
if (src != null) {
|
|
||||||
setStartUrl(src);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
else if (eventType == XmlResourceParser.END_TAG)
|
else if (eventType == XmlResourceParser.END_TAG)
|
||||||
{
|
{
|
||||||
String strNode = xml.getName();
|
handleEndTag(xml);
|
||||||
if (strNode.equals("feature")) {
|
|
||||||
pluginEntries.add(new PluginEntry(service, pluginClass, onload));
|
|
||||||
|
|
||||||
service = "";
|
|
||||||
pluginClass = "";
|
|
||||||
insideFeature = false;
|
|
||||||
onload = false;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
eventType = xml.next();
|
eventType = xml.next();
|
||||||
@ -157,6 +89,48 @@ public class ConfigXmlParser {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void handleStartTag(XmlResourceParser xml) {
|
||||||
|
String strNode = xml.getName();
|
||||||
|
if (strNode.equals("feature")) {
|
||||||
|
//Check for supported feature sets aka. plugins (Accelerometer, Geolocation, etc)
|
||||||
|
//Set the bit for reading params
|
||||||
|
insideFeature = true;
|
||||||
|
service = xml.getAttributeValue(null, "name");
|
||||||
|
}
|
||||||
|
else if (insideFeature && strNode.equals("param")) {
|
||||||
|
paramType = xml.getAttributeValue(null, "name");
|
||||||
|
if (paramType.equals("service")) // check if it is using the older service param
|
||||||
|
service = xml.getAttributeValue(null, "value");
|
||||||
|
else if (paramType.equals("package") || paramType.equals("android-package"))
|
||||||
|
pluginClass = xml.getAttributeValue(null,"value");
|
||||||
|
else if (paramType.equals("onload"))
|
||||||
|
onload = "true".equals(xml.getAttributeValue(null, "value"));
|
||||||
|
}
|
||||||
|
else if (strNode.equals("preference")) {
|
||||||
|
String name = xml.getAttributeValue(null, "name").toLowerCase(Locale.ENGLISH);
|
||||||
|
String value = xml.getAttributeValue(null, "value");
|
||||||
|
prefs.set(name, value);
|
||||||
|
}
|
||||||
|
else if (strNode.equals("content")) {
|
||||||
|
String src = xml.getAttributeValue(null, "src");
|
||||||
|
if (src != null) {
|
||||||
|
setStartUrl(src);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void handleEndTag(XmlResourceParser xml) {
|
||||||
|
String strNode = xml.getName();
|
||||||
|
if (strNode.equals("feature")) {
|
||||||
|
pluginEntries.add(new PluginEntry(service, pluginClass, onload));
|
||||||
|
|
||||||
|
service = "";
|
||||||
|
pluginClass = "";
|
||||||
|
insideFeature = false;
|
||||||
|
onload = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private void setStartUrl(String src) {
|
private void setStartUrl(String src) {
|
||||||
Pattern schemeRegex = Pattern.compile("^[a-z-]+://");
|
Pattern schemeRegex = Pattern.compile("^[a-z-]+://");
|
||||||
Matcher matcher = schemeRegex.matcher(src);
|
Matcher matcher = schemeRegex.matcher(src);
|
||||||
|
@ -123,8 +123,6 @@ public class CordovaActivity extends Activity implements CordovaInterface {
|
|||||||
|
|
||||||
// Read from config.xml:
|
// Read from config.xml:
|
||||||
protected CordovaPreferences preferences;
|
protected CordovaPreferences preferences;
|
||||||
protected Whitelist internalWhitelist;
|
|
||||||
protected Whitelist externalWhitelist;
|
|
||||||
protected String launchUrl;
|
protected String launchUrl;
|
||||||
protected ArrayList<PluginEntry> pluginEntries;
|
protected ArrayList<PluginEntry> pluginEntries;
|
||||||
|
|
||||||
@ -185,8 +183,6 @@ public class CordovaActivity extends Activity implements CordovaInterface {
|
|||||||
preferences = parser.getPreferences();
|
preferences = parser.getPreferences();
|
||||||
preferences.setPreferencesBundle(getIntent().getExtras());
|
preferences.setPreferencesBundle(getIntent().getExtras());
|
||||||
preferences.copyIntoIntentExtras(this);
|
preferences.copyIntoIntentExtras(this);
|
||||||
internalWhitelist = parser.getInternalWhitelist();
|
|
||||||
externalWhitelist = parser.getExternalWhitelist();
|
|
||||||
launchUrl = parser.getLaunchUrl();
|
launchUrl = parser.getLaunchUrl();
|
||||||
pluginEntries = parser.getPluginEntries();
|
pluginEntries = parser.getPluginEntries();
|
||||||
Config.parser = parser;
|
Config.parser = parser;
|
||||||
@ -266,7 +262,7 @@ public class CordovaActivity extends Activity implements CordovaInterface {
|
|||||||
// If all else fails, return a default WebView
|
// If all else fails, return a default WebView
|
||||||
ret = new AndroidWebView(this);
|
ret = new AndroidWebView(this);
|
||||||
}
|
}
|
||||||
ret.init(this, pluginEntries, internalWhitelist, externalWhitelist, preferences);
|
ret.init(this, pluginEntries, preferences);
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -554,7 +550,11 @@ public class CordovaActivity extends Activity implements CordovaInterface {
|
|||||||
|
|
||||||
// If errorUrl specified, then load it
|
// If errorUrl specified, then load it
|
||||||
final String errorUrl = preferences.getString("errorUrl", null);
|
final String errorUrl = preferences.getString("errorUrl", null);
|
||||||
if ((errorUrl != null) && (errorUrl.startsWith("file://") || internalWhitelist.isUrlWhiteListed(errorUrl)) && (!failingUrl.equals(errorUrl))) {
|
CordovaUriHelper helper = new CordovaUriHelper(this, appView);
|
||||||
|
if ((errorUrl != null) &&
|
||||||
|
(!failingUrl.equals(errorUrl)) &&
|
||||||
|
(appView != null && helper.shouldAllowNavigation(errorUrl))
|
||||||
|
) {
|
||||||
// Load URL on UI thread
|
// Load URL on UI thread
|
||||||
me.runOnUiThread(new Runnable() {
|
me.runOnUiThread(new Runnable() {
|
||||||
public void run() {
|
public void run() {
|
||||||
|
@ -37,12 +37,14 @@ public class CordovaBridge {
|
|||||||
private NativeToJsMessageQueue jsMessageQueue;
|
private NativeToJsMessageQueue jsMessageQueue;
|
||||||
private volatile int expectedBridgeSecret = -1; // written by UI thread, read by JS thread.
|
private volatile int expectedBridgeSecret = -1; // written by UI thread, read by JS thread.
|
||||||
private String loadedUrl;
|
private String loadedUrl;
|
||||||
|
protected CordovaUriHelper helper;
|
||||||
|
|
||||||
public CordovaBridge(PluginManager pluginManager, NativeToJsMessageQueue jsMessageQueue) {
|
public CordovaBridge(PluginManager pluginManager, NativeToJsMessageQueue jsMessageQueue, CordovaUriHelper helper) {
|
||||||
this.pluginManager = pluginManager;
|
this.pluginManager = pluginManager;
|
||||||
this.jsMessageQueue = jsMessageQueue;
|
this.jsMessageQueue = jsMessageQueue;
|
||||||
|
this.helper = helper;
|
||||||
}
|
}
|
||||||
|
|
||||||
public String jsExec(int bridgeSecret, String service, String action, String callbackId, String arguments) throws JSONException, IllegalAccessException {
|
public String jsExec(int bridgeSecret, String service, String action, String callbackId, String arguments) throws JSONException, IllegalAccessException {
|
||||||
if (!verifySecret("exec()", bridgeSecret)) {
|
if (!verifySecret("exec()", bridgeSecret)) {
|
||||||
return null;
|
return null;
|
||||||
@ -163,9 +165,9 @@ public class CordovaBridge {
|
|||||||
}
|
}
|
||||||
else if (defaultValue != null && defaultValue.startsWith("gap_init:")) {
|
else if (defaultValue != null && defaultValue.startsWith("gap_init:")) {
|
||||||
// Protect against random iframes being able to talk through the bridge.
|
// Protect against random iframes being able to talk through the bridge.
|
||||||
// Trust only file URLs and the start URL's domain.
|
// Trust only file URLs and pages which the app would have been allowed
|
||||||
// The extra origin.startsWith("http") is to protect against iframes with data: having "" as origin.
|
// to navigate to anyway.
|
||||||
if (origin.startsWith("file:") || (origin.startsWith("http") && loadedUrl.startsWith(origin))) {
|
if (origin.startsWith("file:") || helper.shouldAllowNavigation(origin)) {
|
||||||
// Enable the bridge
|
// Enable the bridge
|
||||||
int bridgeMode = Integer.parseInt(defaultValue.substring(9));
|
int bridgeMode = Integer.parseInt(defaultValue.substring(9));
|
||||||
jsMessageQueue.setBridgeMode(bridgeMode);
|
jsMessageQueue.setBridgeMode(bridgeMode);
|
||||||
|
@ -162,19 +162,67 @@ public class CordovaPlugin {
|
|||||||
* Called when an activity you launched exits, giving you the requestCode you started it with,
|
* Called when an activity you launched exits, giving you the requestCode you started it with,
|
||||||
* the resultCode it returned, and any additional data from it.
|
* the resultCode it returned, and any additional data from it.
|
||||||
*
|
*
|
||||||
* @param requestCode The request code originally supplied to startActivityForResult(),
|
* @param requestCode The request code originally supplied to startActivityForResult(),
|
||||||
* allowing you to identify who this result came from.
|
* allowing you to identify who this result came from.
|
||||||
* @param resultCode The integer result code returned by the child activity through its setResult().
|
* @param resultCode The integer result code returned by the child activity through its setResult().
|
||||||
* @param intent An Intent, which can return result data to the caller (various data can be attached to Intent "extras").
|
* @param intent An Intent, which can return result data to the caller (various data can be
|
||||||
|
* attached to Intent "extras").
|
||||||
*/
|
*/
|
||||||
public void onActivityResult(int requestCode, int resultCode, Intent intent) {
|
public void onActivityResult(int requestCode, int resultCode, Intent intent) {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Hook for blocking the loading of external resources.
|
||||||
|
*
|
||||||
|
* This will be called when the WebView's shouldInterceptRequest wants to
|
||||||
|
* know whether to open a connection to an external resource. Return false
|
||||||
|
* to block the request: if any plugin returns false, Cordova will block
|
||||||
|
* the request. If all plugins return null, the default policy will be
|
||||||
|
* enforced. If at least one plugin returns true, and no plugins return
|
||||||
|
* false, then the request will proceed.
|
||||||
|
*
|
||||||
|
* Note that this only affects resource requests which are routed through
|
||||||
|
* WebViewClient.shouldInterceptRequest, such as XMLHttpRequest requests and
|
||||||
|
* img tag loads. WebSockets and media requests (such as <video> and <audio>
|
||||||
|
* tags) are not affected by this method. Use CSP headers to control access
|
||||||
|
* to such resources.
|
||||||
|
*/
|
||||||
|
public Boolean shouldAllowRequest(String url) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Hook for blocking navigation by the Cordova WebView.
|
||||||
|
*
|
||||||
|
* This will be called when the WebView's needs to know whether to navigate
|
||||||
|
* to a new page. Return false to block the navigation: if any plugin
|
||||||
|
* returns false, Cordova will block the navigation. If all plugins return
|
||||||
|
* null, the default policy will be enforced. It at least one plugin returns
|
||||||
|
* true, and no plugins return false, then the navigation will proceed.
|
||||||
|
*/
|
||||||
|
public Boolean shouldAllowNavigation(String url) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Hook for blocking the launching of Intents by the Cordova application.
|
||||||
|
*
|
||||||
|
* This will be called when the WebView will not navigate to a page, but
|
||||||
|
* could launch an intent to handle the URL. Return false to block this: if
|
||||||
|
* any plugin returns false, Cordova will block the navigation. If all
|
||||||
|
* plugins return null, the default policy will be enforced. If at least one
|
||||||
|
* plugin returns true, and no plugins return false, then the URL will be
|
||||||
|
* opened.
|
||||||
|
*/
|
||||||
|
public Boolean shouldOpenExternalUrl(String url) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* By specifying a <url-filter> in config.xml you can map a URL (using startsWith atm) to this method.
|
* By specifying a <url-filter> in config.xml you can map a URL (using startsWith atm) to this method.
|
||||||
*
|
*
|
||||||
* @param url The URL that is trying to be loaded in the Cordova webview.
|
* @param url The URL that is trying to be loaded in the Cordova webview.
|
||||||
* @return Return true to prevent the URL from loading. Default is false.
|
* @return Return true to prevent the URL from loading. Default is false.
|
||||||
*/
|
*/
|
||||||
public boolean onOverrideUrlLoading(String url) {
|
public boolean onOverrideUrlLoading(String url) {
|
||||||
return false;
|
return false;
|
||||||
|
@ -23,6 +23,7 @@ import android.annotation.TargetApi;
|
|||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
import android.net.Uri;
|
import android.net.Uri;
|
||||||
import android.os.Build;
|
import android.os.Build;
|
||||||
|
import android.util.Log;
|
||||||
import android.webkit.WebView;
|
import android.webkit.WebView;
|
||||||
|
|
||||||
public class CordovaUriHelper {
|
public class CordovaUriHelper {
|
||||||
@ -37,11 +38,77 @@ public class CordovaUriHelper {
|
|||||||
appView = webView;
|
appView = webView;
|
||||||
cordova = cdv;
|
cordova = cdv;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Determine whether the webview should be allowed to navigate to a given URL.
|
||||||
|
*
|
||||||
|
* This method implements the default whitelist policy when no plugins override
|
||||||
|
* shouldAllowNavigation
|
||||||
|
*/
|
||||||
|
public boolean shouldAllowNavigation(String url) {
|
||||||
|
Boolean pluginManagerAllowsNavigation = this.appView.getPluginManager().shouldAllowNavigation(url);
|
||||||
|
if (pluginManagerAllowsNavigation == null) {
|
||||||
|
// Default policy:
|
||||||
|
// Internal urls on file:// or data:// that do not contain "/app_webview/" are allowed for navigation
|
||||||
|
if(url.startsWith("file://") || url.startsWith("data:"))
|
||||||
|
{
|
||||||
|
//This directory on WebKit/Blink based webviews contains SQLite databases!
|
||||||
|
//DON'T CHANGE THIS UNLESS YOU KNOW WHAT YOU'RE DOING!
|
||||||
|
return !url.contains("/app_webview/");
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return pluginManagerAllowsNavigation;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Determine whether the webview should be allowed to launch an intent for a given URL.
|
||||||
|
*
|
||||||
|
* This method implements the default whitelist policy when no plugins override
|
||||||
|
* shouldOpenExternalUrl
|
||||||
|
*/
|
||||||
|
public boolean shouldOpenExternalUrl(String url) {
|
||||||
|
Boolean pluginManagerAllowsExternalUrl = this.appView.getPluginManager().shouldOpenExternalUrl(url);
|
||||||
|
if (pluginManagerAllowsExternalUrl == null) {
|
||||||
|
// Default policy:
|
||||||
|
// External URLs are not allowed
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return pluginManagerAllowsExternalUrl;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Determine whether the webview should be allowed to request a resource from a given URL.
|
||||||
|
*
|
||||||
|
* This method implements the default whitelist policy when no plugins override
|
||||||
|
* shouldAllowRequest
|
||||||
|
*/
|
||||||
|
public boolean shouldAllowRequest(String url) {
|
||||||
|
|
||||||
|
Boolean pluginManagerAllowsRequest = this.appView.getPluginManager().shouldAllowRequest(url);
|
||||||
|
if (pluginManagerAllowsRequest == null) {
|
||||||
|
// Default policy:
|
||||||
|
// Internal urls on file:// or data:// that do not contain "/app_webview/" are allowed for navigation
|
||||||
|
if(url.startsWith("file://") || url.startsWith("data:"))
|
||||||
|
{
|
||||||
|
//This directory on WebKit/Blink based webviews contains SQLite databases!
|
||||||
|
//DON'T CHANGE THIS UNLESS YOU KNOW WHAT YOU'RE DOING!
|
||||||
|
return !url.contains("/app_webview/");
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return pluginManagerAllowsRequest;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Give the host application a chance to take over the control when a new url
|
* Give the host application a chance to take over the control when a new url
|
||||||
* is about to be loaded in the current WebView.
|
* is about to be loaded in the current WebView.
|
||||||
*
|
*
|
||||||
|
* This method implements the default whitelist policy when no plugins override
|
||||||
|
* the whitelist methods:
|
||||||
|
* Internal urls on file:// or data:// that do not contain "app_webview" are allowed for navigation
|
||||||
|
* External urls are not allowed.
|
||||||
|
*
|
||||||
* @param view The WebView that is initiating the callback.
|
* @param view The WebView that is initiating the callback.
|
||||||
* @param url The url to be loaded.
|
* @param url The url to be loaded.
|
||||||
* @return true to override, false for default behavior
|
* @return true to override, false for default behavior
|
||||||
@ -49,23 +116,15 @@ public class CordovaUriHelper {
|
|||||||
@TargetApi(Build.VERSION_CODES.ICE_CREAM_SANDWICH_MR1)
|
@TargetApi(Build.VERSION_CODES.ICE_CREAM_SANDWICH_MR1)
|
||||||
public boolean shouldOverrideUrlLoading(String url) {
|
public boolean shouldOverrideUrlLoading(String url) {
|
||||||
// Give plugins the chance to handle the url
|
// Give plugins the chance to handle the url
|
||||||
if (this.appView.getPluginManager().onOverrideUrlLoading(url)) {
|
if (shouldAllowNavigation(url)) {
|
||||||
// Do nothing other than what the plugins wanted.
|
|
||||||
// If any returned true, then the request was handled.
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
else if(url.startsWith("file://") | url.startsWith("data:"))
|
|
||||||
{
|
|
||||||
//This directory on WebKit/Blink based webviews contains SQLite databases!
|
|
||||||
//DON'T CHANGE THIS UNLESS YOU KNOW WHAT YOU'RE DOING!
|
|
||||||
return url.contains("app_webview");
|
|
||||||
}
|
|
||||||
else if (appView.getWhitelist().isUrlWhiteListed(url)) {
|
|
||||||
// Allow internal navigation
|
// Allow internal navigation
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
else if (appView.getExternalWhitelist().isUrlWhiteListed(url))
|
if (shouldOpenExternalUrl(url)) {
|
||||||
{
|
// Do nothing other than what the plugins wanted.
|
||||||
|
// If any returned false, then the request was either blocked
|
||||||
|
// completely, or handled out-of-band by the plugin. If they all
|
||||||
|
// returned true, then we should open the URL here.
|
||||||
try {
|
try {
|
||||||
Intent intent = new Intent(Intent.ACTION_VIEW);
|
Intent intent = new Intent(Intent.ACTION_VIEW);
|
||||||
intent.setData(Uri.parse(url));
|
intent.setData(Uri.parse(url));
|
||||||
@ -77,10 +136,11 @@ public class CordovaUriHelper {
|
|||||||
this.cordova.getActivity().startActivity(intent);
|
this.cordova.getActivity().startActivity(intent);
|
||||||
return true;
|
return true;
|
||||||
} catch (android.content.ActivityNotFoundException e) {
|
} catch (android.content.ActivityNotFoundException e) {
|
||||||
LOG.e(TAG, "Error loading url " + url, e);
|
Log.e(TAG, "Error loading url " + url, e);
|
||||||
}
|
}
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
// Intercept the request and do nothing with it -- block it
|
// Block by default
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -13,7 +13,6 @@ public interface CordovaWebView {
|
|||||||
public static final String CORDOVA_VERSION = "4.0.0-dev";
|
public static final String CORDOVA_VERSION = "4.0.0-dev";
|
||||||
|
|
||||||
void init(CordovaInterface cordova, List<PluginEntry> pluginEntries,
|
void init(CordovaInterface cordova, List<PluginEntry> pluginEntries,
|
||||||
Whitelist internalWhitelist, Whitelist externalWhitelist,
|
|
||||||
CordovaPreferences preferences);
|
CordovaPreferences preferences);
|
||||||
|
|
||||||
View getView();
|
View getView();
|
||||||
@ -81,8 +80,6 @@ public interface CordovaWebView {
|
|||||||
|
|
||||||
PluginManager getPluginManager();
|
PluginManager getPluginManager();
|
||||||
|
|
||||||
Whitelist getWhitelist();
|
|
||||||
Whitelist getExternalWhitelist();
|
|
||||||
CordovaPreferences getPreferences();
|
CordovaPreferences getPreferences();
|
||||||
|
|
||||||
void onFilePickerResult(Uri uri);
|
void onFilePickerResult(Uri uri);
|
||||||
|
@ -45,7 +45,7 @@ public class IceCreamCordovaWebViewClient extends AndroidWebViewClient {
|
|||||||
try {
|
try {
|
||||||
// Check the against the whitelist and lock out access to the WebView directory
|
// Check the against the whitelist and lock out access to the WebView directory
|
||||||
// Changing this will cause problems for your application
|
// Changing this will cause problems for your application
|
||||||
if (isUrlHarmful(url)) {
|
if (!helper.shouldAllowRequest(url)) {
|
||||||
LOG.w(TAG, "URL blocked by whitelist: " + url);
|
LOG.w(TAG, "URL blocked by whitelist: " + url);
|
||||||
// Results in a 404.
|
// Results in a 404.
|
||||||
return new WebResourceResponse("text/plain", "UTF-8", null);
|
return new WebResourceResponse("text/plain", "UTF-8", null);
|
||||||
@ -71,11 +71,6 @@ public class IceCreamCordovaWebViewClient extends AndroidWebViewClient {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean isUrlHarmful(String url) {
|
|
||||||
return ((url.startsWith("http:") || url.startsWith("https:")) && !appView.getWhitelist().isUrlWhiteListed(url))
|
|
||||||
|| url.contains("app_webview");
|
|
||||||
}
|
|
||||||
|
|
||||||
private static boolean needsKitKatContentUrlFix(Uri uri) {
|
private static boolean needsKitKatContentUrlFix(Uri uri) {
|
||||||
return android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.KITKAT && "content".equals(uri.getScheme());
|
return android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.KITKAT && "content".equals(uri.getScheme());
|
||||||
}
|
}
|
||||||
|
@ -253,6 +253,110 @@ public class PluginManager {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called when the webview is going to request an external resource.
|
||||||
|
*
|
||||||
|
* This delegates to the installed plugins, which must all return true for
|
||||||
|
* this method to return true.
|
||||||
|
*
|
||||||
|
* @param url The URL that is being requested.
|
||||||
|
* @return Tri-State:
|
||||||
|
* null: All plugins returned null (the default). This
|
||||||
|
* indicates that the default policy should be
|
||||||
|
* followed.
|
||||||
|
* true: All plugins returned true (allow the resource
|
||||||
|
* to load)
|
||||||
|
* false: At least one plugin returned false (block the
|
||||||
|
* resource)
|
||||||
|
*/
|
||||||
|
public Boolean shouldAllowRequest(String url) {
|
||||||
|
Boolean anyResponded = null;
|
||||||
|
for (PluginEntry entry : this.entryMap.values()) {
|
||||||
|
CordovaPlugin plugin = pluginMap.get(entry.service);
|
||||||
|
if (plugin != null) {
|
||||||
|
Boolean result = plugin.shouldAllowRequest(url);
|
||||||
|
if (result != null) {
|
||||||
|
anyResponded = true;
|
||||||
|
if (!result) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// This will be true if all plugins allow the request, or null if no plugins override the method
|
||||||
|
return anyResponded;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called when the webview is going to change the URL of the loaded content.
|
||||||
|
*
|
||||||
|
* This delegates to the installed plugins, which must all return true for
|
||||||
|
* this method to return true. A true result will allow the new page to load;
|
||||||
|
* a false result will prevent the page from loading.
|
||||||
|
*
|
||||||
|
* @param url The URL that is being requested.
|
||||||
|
* @return Tri-State:
|
||||||
|
* null: All plugins returned null (the default). This
|
||||||
|
* indicates that the default policy should be
|
||||||
|
* followed.
|
||||||
|
* true: All plugins returned true (allow the navigation)
|
||||||
|
* false: At least one plugin returned false (block the
|
||||||
|
* navigation)
|
||||||
|
*/
|
||||||
|
public Boolean shouldAllowNavigation(String url) {
|
||||||
|
Boolean anyResponded = null;
|
||||||
|
for (PluginEntry entry : this.entryMap.values()) {
|
||||||
|
CordovaPlugin plugin = pluginMap.get(entry.service);
|
||||||
|
if (plugin != null) {
|
||||||
|
Boolean result = plugin.shouldAllowNavigation(url);
|
||||||
|
if (result != null) {
|
||||||
|
anyResponded = true;
|
||||||
|
if (!result) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// This will be true if all plugins allow the request, or null if no plugins override the method
|
||||||
|
return anyResponded;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called when the webview is going not going to navigate, but may launch
|
||||||
|
* an Intent for an URL.
|
||||||
|
*
|
||||||
|
* This delegates to the installed plugins, which must all return true for
|
||||||
|
* this method to return true. A true result will allow the URL to launch;
|
||||||
|
* a false result will prevent the URL from loading.
|
||||||
|
*
|
||||||
|
* @param url The URL that is being requested.
|
||||||
|
* @return Tri-State:
|
||||||
|
* null: All plugins returned null (the default). This
|
||||||
|
* indicates that the default policy should be
|
||||||
|
* followed.
|
||||||
|
* true: All plugins returned true (allow the URL to
|
||||||
|
* launch an intent)
|
||||||
|
* false: At least one plugin returned false (block the
|
||||||
|
* intent)
|
||||||
|
*/
|
||||||
|
public Boolean shouldOpenExternalUrl(String url) {
|
||||||
|
Boolean anyResponded = null;
|
||||||
|
for (PluginEntry entry : this.entryMap.values()) {
|
||||||
|
CordovaPlugin plugin = pluginMap.get(entry.service);
|
||||||
|
if (plugin != null) {
|
||||||
|
Boolean result = plugin.shouldOpenExternalUrl(url);
|
||||||
|
if (result != null) {
|
||||||
|
anyResponded = true;
|
||||||
|
if (!result) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// This will be true if all plugins allow the request, or null if no plugins override the method
|
||||||
|
return anyResponded;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Called when the URL of the webview changes.
|
* Called when the URL of the webview changes.
|
||||||
*
|
*
|
||||||
|
@ -51,8 +51,7 @@ public class CordovaWebViewTestActivity extends Activity implements CordovaInter
|
|||||||
Config.init(this);
|
Config.init(this);
|
||||||
|
|
||||||
cordovaWebView = (CordovaWebView) findViewById(R.id.cordovaWebView);
|
cordovaWebView = (CordovaWebView) findViewById(R.id.cordovaWebView);
|
||||||
cordovaWebView.init(this, Config.getPluginEntries(), Config.getWhitelist(),
|
cordovaWebView.init(this, Config.getPluginEntries(), Config.getPreferences());
|
||||||
Config.getExternalWhitelist(), Config.getPreferences());
|
|
||||||
|
|
||||||
cordovaWebView.loadUrl("file:///android_asset/www/index.html");
|
cordovaWebView.loadUrl("file:///android_asset/www/index.html");
|
||||||
|
|
||||||
|
@ -34,7 +34,7 @@ public class menus extends CordovaActivity {
|
|||||||
super.onCreate(savedInstanceState);
|
super.onCreate(savedInstanceState);
|
||||||
// need the title to be shown (config.xml) for the options menu to be visible
|
// need the title to be shown (config.xml) for the options menu to be visible
|
||||||
super.init();
|
super.init();
|
||||||
super.registerForContextMenu(super.appView);
|
super.registerForContextMenu(super.appView.getView());
|
||||||
super.loadUrl("file:///android_asset/www/menus/index.html");
|
super.loadUrl("file:///android_asset/www/menus/index.html");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user