diff --git a/framework/res/xml/config.xml b/framework/res/xml/config.xml index 50459b92..6f4a264b 100644 --- a/framework/res/xml/config.xml +++ b/framework/res/xml/config.xml @@ -30,7 +30,20 @@ Apache Cordova Team - + + + + + + + + + + diff --git a/framework/src/org/apache/cordova/Config.java b/framework/src/org/apache/cordova/Config.java index 5cae9a27..f13292c3 100644 --- a/framework/src/org/apache/cordova/Config.java +++ b/framework/src/org/apache/cordova/Config.java @@ -58,7 +58,7 @@ public class Config { Log.e(TAG, "Config was not initialised. Did you forget to Config.init(this)?"); return; } - parser.getWhitelist().addWhiteListEntry(origin, subdomains); + parser.getInternalWhitelist().addWhiteListEntry(origin, subdomains); } /** @@ -72,7 +72,21 @@ public class Config { Log.e(TAG, "Config was not initialised. Did you forget to Config.init(this)?"); return false; } - return parser.getWhitelist().isUrlWhiteListed(url); + 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() { @@ -87,7 +101,11 @@ public class Config { } public static Whitelist getWhitelist() { - return parser.getWhitelist(); + return parser.getInternalWhitelist(); + } + + public static Whitelist getExternalWhitelist() { + return parser.getExternalWhitelist(); } public static List getPluginEntries() { diff --git a/framework/src/org/apache/cordova/ConfigXmlParser.java b/framework/src/org/apache/cordova/ConfigXmlParser.java index a5958efa..1ada1af2 100644 --- a/framework/src/org/apache/cordova/ConfigXmlParser.java +++ b/framework/src/org/apache/cordova/ConfigXmlParser.java @@ -37,11 +37,16 @@ public class ConfigXmlParser { private String launchUrl = "file:///android_asset/www/index.html"; private CordovaPreferences prefs = new CordovaPreferences(); - private Whitelist whitelist = new Whitelist(); + private Whitelist internalWhitelist = new Whitelist(); + private Whitelist externalWhitelist = new Whitelist(); private ArrayList pluginEntries = new ArrayList(20); - public Whitelist getWhitelist() { - return whitelist; + public Whitelist getInternalWhitelist() { + return internalWhitelist; + } + + public Whitelist getExternalWhitelist() { + return externalWhitelist; } public CordovaPreferences getPreferences() { @@ -77,6 +82,11 @@ public class ConfigXmlParser { boolean insideFeature = false; ArrayList urlMap = null; + // Add implicitly allowed URLs + internalWhitelist.addWhiteListEntry("file:///*", false); + internalWhitelist.addWhiteListEntry("content:///*", false); + internalWhitelist.addWhiteListEntry("data:*", false); + while (eventType != XmlResourceParser.END_DOCUMENT) { if (eventType == XmlResourceParser.START_TAG) { String strNode = xml.getName(); @@ -104,8 +114,21 @@ public class ConfigXmlParser { 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) { - whitelist.addWhiteListEntry(origin, (subdomains != null) && (subdomains.compareToIgnoreCase("true") == 0)); + 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")) { diff --git a/framework/src/org/apache/cordova/CordovaActivity.java b/framework/src/org/apache/cordova/CordovaActivity.java index 2fdb9a7b..190309ec 100755 --- a/framework/src/org/apache/cordova/CordovaActivity.java +++ b/framework/src/org/apache/cordova/CordovaActivity.java @@ -130,7 +130,8 @@ public class CordovaActivity extends Activity implements CordovaInterface { // Read from config.xml: protected CordovaPreferences preferences; - protected Whitelist whitelist; + protected Whitelist internalWhitelist; + protected Whitelist externalWhitelist; protected String launchUrl; protected ArrayList pluginEntries; @@ -216,7 +217,8 @@ public class CordovaActivity extends Activity implements CordovaInterface { preferences = parser.getPreferences(); preferences.setPreferencesBundle(getIntent().getExtras()); preferences.copyIntoIntentExtras(this); - whitelist = parser.getWhitelist(); + internalWhitelist = parser.getInternalWhitelist(); + externalWhitelist = parser.getExternalWhitelist(); launchUrl = parser.getLaunchUrl(); pluginEntries = parser.getPluginEntries(); Config.parser = parser; @@ -324,7 +326,7 @@ public class CordovaActivity extends Activity implements CordovaInterface { if (appView.pluginManager == null) { appView.init(this, webViewClient != null ? webViewClient : makeWebViewClient(appView), webChromeClient != null ? webChromeClient : makeChromeClient(appView), - pluginEntries, whitelist, preferences); + pluginEntries, internalWhitelist, externalWhitelist, preferences); } // TODO: Have the views set this themselves. @@ -789,7 +791,7 @@ public class CordovaActivity extends Activity implements CordovaInterface { // If errorUrl specified, then load it final String errorUrl = preferences.getString("errorUrl", null); - if ((errorUrl != null) && (errorUrl.startsWith("file://") || whitelist.isUrlWhiteListed(errorUrl)) && (!failingUrl.equals(errorUrl))) { + if ((errorUrl != null) && (errorUrl.startsWith("file://") || internalWhitelist.isUrlWhiteListed(errorUrl)) && (!failingUrl.equals(errorUrl))) { // Load URL on UI thread me.runOnUiThread(new Runnable() { @@ -849,7 +851,7 @@ public class CordovaActivity extends Activity implements CordovaInterface { */ @Deprecated // Use whitelist object directly. public boolean isUrlWhiteListed(String url) { - return whitelist.isUrlWhiteListed(url); + return internalWhitelist.isUrlWhiteListed(url); } /* diff --git a/framework/src/org/apache/cordova/CordovaUriHelper.java b/framework/src/org/apache/cordova/CordovaUriHelper.java index bb785920..ada28515 100644 --- a/framework/src/org/apache/cordova/CordovaUriHelper.java +++ b/framework/src/org/apache/cordova/CordovaUriHelper.java @@ -48,18 +48,11 @@ class CordovaUriHelper { */ @TargetApi(Build.VERSION_CODES.ICE_CREAM_SANDWICH_MR1) boolean shouldOverrideUrlLoading(WebView view, String url) { - // The WebView should support http and https when going on the Internet - if(url.startsWith("http:") || url.startsWith("https:")) - { - // We only need to whitelist sites on the Internet! - if(appView.getWhitelist().isUrlWhiteListed(url)) - { - return false; - } - } // Give plugins the chance to handle the url - else if (this.appView.pluginManager.onOverrideUrlLoading(url)) { - + if (this.appView.pluginManager.onOverrideUrlLoading(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:")) { @@ -67,7 +60,11 @@ class CordovaUriHelper { //DON'T CHANGE THIS UNLESS YOU KNOW WHAT YOU'RE DOING! return url.contains("app_webview"); } - else + else if (appView.getWhitelist().isUrlWhiteListed(url)) { + // Allow internal navigation + return false; + } + else if (appView.getExternalWhitelist().isUrlWhiteListed(url)) { try { Intent intent = new Intent(Intent.ACTION_VIEW); @@ -78,11 +75,12 @@ class CordovaUriHelper { intent.setSelector(null); } this.cordova.getActivity().startActivity(intent); + return true; } catch (android.content.ActivityNotFoundException e) { LOG.e(TAG, "Error loading url " + url, e); } } - //Default behaviour should be to load the default intent, let's see what happens! + // Intercept the request and do nothing with it -- block it return true; } } diff --git a/framework/src/org/apache/cordova/CordovaWebView.java b/framework/src/org/apache/cordova/CordovaWebView.java index 72aa1716..403a7aec 100755 --- a/framework/src/org/apache/cordova/CordovaWebView.java +++ b/framework/src/org/apache/cordova/CordovaWebView.java @@ -88,7 +88,9 @@ public class CordovaWebView extends WebView { private WebChromeClient.CustomViewCallback mCustomViewCallback; private CordovaResourceApi resourceApi; - private Whitelist whitelist; + private Whitelist internalWhitelist; + private Whitelist externalWhitelist; + // The URL passed to loadUrl(), not necessarily the URL of the current page. String loadedUrl; private CordovaPreferences preferences; @@ -135,14 +137,16 @@ public class CordovaWebView extends WebView { // Use two-phase init so that the control will work with XML layouts. public void init(CordovaInterface cordova, CordovaWebViewClient webViewClient, CordovaChromeClient webChromeClient, - List pluginEntries, Whitelist whitelist, CordovaPreferences preferences) { + List pluginEntries, Whitelist internalWhitelist, Whitelist externalWhitelist, + CordovaPreferences preferences) { if (this.cordova != null) { throw new IllegalStateException(); } this.cordova = cordova; this.viewClient = webViewClient; this.chromeClient = webChromeClient; - this.whitelist = whitelist; + this.internalWhitelist = internalWhitelist; + this.externalWhitelist = externalWhitelist; this.preferences = preferences; super.setWebChromeClient(webChromeClient); super.setWebViewClient(webViewClient); @@ -165,7 +169,7 @@ public class CordovaWebView extends WebView { if (!Config.isInitialized()) { Config.init(cdv.getActivity()); } - init(cdv, makeWebViewClient(cdv), makeWebChromeClient(cdv), Config.getPluginEntries(), Config.getWhitelist(), Config.getPreferences()); + init(cdv, makeWebViewClient(cdv), makeWebChromeClient(cdv), Config.getPluginEntries(), Config.getWhitelist(), Config.getExternalWhitelist(), Config.getPreferences()); } } @@ -319,9 +323,13 @@ public class CordovaWebView extends WebView { public Whitelist getWhitelist() { - return this.whitelist; + return this.internalWhitelist; } - + + public Whitelist getExternalWhitelist() { + return this.externalWhitelist; + } + /** * Load the url into the webview. * @@ -427,7 +435,7 @@ public class CordovaWebView extends WebView { if (LOG.isLoggable(LOG.DEBUG) && !url.startsWith("javascript:")) { LOG.d(TAG, ">>> loadUrlNow()"); } - if (url.startsWith("file://") || url.startsWith("javascript:") || whitelist.isUrlWhiteListed(url)) { + if (url.startsWith("file://") || url.startsWith("javascript:") || internalWhitelist.isUrlWhiteListed(url)) { super.loadUrl(url); } } @@ -560,7 +568,7 @@ public class CordovaWebView extends WebView { if (!openExternal) { // Make sure url is in whitelist - if (url.startsWith("file://") || whitelist.isUrlWhiteListed(url)) { + if (url.startsWith("file://") || internalWhitelist.isUrlWhiteListed(url)) { // TODO: What about params? // Load new URL this.loadUrl(url); diff --git a/framework/src/org/apache/cordova/Whitelist.java b/framework/src/org/apache/cordova/Whitelist.java index 5101ec35..d0f823c3 100644 --- a/framework/src/org/apache/cordova/Whitelist.java +++ b/framework/src/org/apache/cordova/Whitelist.java @@ -98,10 +98,6 @@ public class Whitelist { public Whitelist() { this.whiteList = new ArrayList(); - // Add implicitly allowed URLs - addWhiteListEntry("file:///*", false); - addWhiteListEntry("content:///*", false); - addWhiteListEntry("data:*", false); } /* Match patterns (from http://developer.chrome.com/extensions/match_patterns.html)