diff --git a/framework/res/xml/config.xml b/framework/res/xml/config.xml index 0f00c3b3..ee68c3b7 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/AndroidWebView.java b/framework/src/org/apache/cordova/AndroidWebView.java index bd733845..8986d583 100755 --- a/framework/src/org/apache/cordova/AndroidWebView.java +++ b/framework/src/org/apache/cordova/AndroidWebView.java @@ -86,7 +86,8 @@ public class AndroidWebView extends WebView implements CordovaWebView { private WebChromeClient.CustomViewCallback mCustomViewCallback; private CordovaResourceApi resourceApi; - private Whitelist whitelist; + private Whitelist internalWhitelist; + private Whitelist externalWhitelist; private CordovaPreferences preferences; // The URL passed to loadUrl(), not necessarily the URL of the current page. String loadedUrl; @@ -110,12 +111,14 @@ public class AndroidWebView extends WebView implements CordovaWebView { // Use two-phase init so that the control will work with XML layouts. @Override public void init(CordovaInterface cordova, List pluginEntries, - Whitelist whitelist, CordovaPreferences preferences) { + Whitelist internalWhitelist, Whitelist externalWhitelist, + CordovaPreferences preferences) { if (this.cordova != null) { throw new IllegalStateException(); } this.cordova = cordova; - this.whitelist = whitelist; + this.internalWhitelist = internalWhitelist; + this.externalWhitelist = externalWhitelist; this.preferences = preferences; pluginManager = new PluginManager(this, this.cordova, pluginEntries); @@ -351,7 +354,7 @@ public class AndroidWebView extends WebView implements CordovaWebView { 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); } } @@ -426,7 +429,7 @@ public class AndroidWebView extends WebView implements CordovaWebView { 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 loadUrlIntoView(url, true); @@ -744,9 +747,14 @@ public class AndroidWebView extends WebView implements CordovaWebView { @Override public Whitelist getWhitelist() { - return this.whitelist; + return this.internalWhitelist; } - + + @Override + public Whitelist getExternalWhitelist() { + return this.externalWhitelist; + } + @Override public CordovaPreferences getPreferences() { return preferences; 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 e5cb1db6..0b1f5290 100644 --- a/framework/src/org/apache/cordova/ConfigXmlParser.java +++ b/framework/src/org/apache/cordova/ConfigXmlParser.java @@ -36,11 +36,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() { @@ -75,6 +80,11 @@ public class ConfigXmlParser { 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) { if (eventType == XmlResourceParser.START_TAG) { String strNode = xml.getName(); @@ -96,8 +106,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 fa332e5b..c2971ae0 100755 --- a/framework/src/org/apache/cordova/CordovaActivity.java +++ b/framework/src/org/apache/cordova/CordovaActivity.java @@ -121,7 +121,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; @@ -181,7 +182,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; @@ -256,7 +258,7 @@ public class CordovaActivity extends Activity implements CordovaInterface { // If all else fails, return a default WebView ret = new AndroidWebView(this); } - ret.init(this, pluginEntries, whitelist, preferences); + ret.init(this, pluginEntries, internalWhitelist, externalWhitelist, preferences); return ret; } @@ -544,7 +546,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() { public void run() { diff --git a/framework/src/org/apache/cordova/CordovaUriHelper.java b/framework/src/org/apache/cordova/CordovaUriHelper.java index f7d22da8..6c1c4fa9 100644 --- a/framework/src/org/apache/cordova/CordovaUriHelper.java +++ b/framework/src/org/apache/cordova/CordovaUriHelper.java @@ -48,18 +48,11 @@ public class CordovaUriHelper { */ @TargetApi(Build.VERSION_CODES.ICE_CREAM_SANDWICH_MR1) public boolean shouldOverrideUrlLoading(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.getPluginManager().onOverrideUrlLoading(url)) { - + if (this.appView.getPluginManager().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 @@ public 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 @@ public 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 213e062c..47abfc0d 100644 --- a/framework/src/org/apache/cordova/CordovaWebView.java +++ b/framework/src/org/apache/cordova/CordovaWebView.java @@ -13,7 +13,8 @@ public interface CordovaWebView { public static final String CORDOVA_VERSION = "4.0.0-dev"; void init(CordovaInterface cordova, List pluginEntries, - Whitelist whitelist, CordovaPreferences preferences); + Whitelist internalWhitelist, Whitelist externalWhitelist, + CordovaPreferences preferences); View getView(); @@ -81,6 +82,7 @@ public interface CordovaWebView { PluginManager getPluginManager(); Whitelist getWhitelist(); + Whitelist getExternalWhitelist(); CordovaPreferences getPreferences(); void onFilePickerResult(Uri uri); 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) diff --git a/test/res/xml/config.xml b/test/res/xml/config.xml index 7572732b..148bad5a 100644 --- a/test/res/xml/config.xml +++ b/test/res/xml/config.xml @@ -26,6 +26,10 @@ Apache Cordova Team + + + + diff --git a/test/src/org/apache/cordova/test/CordovaWebViewTestActivity.java b/test/src/org/apache/cordova/test/CordovaWebViewTestActivity.java index b143521e..43e0b2bc 100644 --- a/test/src/org/apache/cordova/test/CordovaWebViewTestActivity.java +++ b/test/src/org/apache/cordova/test/CordovaWebViewTestActivity.java @@ -51,7 +51,8 @@ public class CordovaWebViewTestActivity extends Activity implements CordovaInter Config.init(this); cordovaWebView = (CordovaWebView) findViewById(R.id.cordovaWebView); - cordovaWebView.init(this, Config.getPluginEntries(), Config.getWhitelist(), Config.getPreferences()); + cordovaWebView.init(this, Config.getPluginEntries(), Config.getWhitelist(), + Config.getExternalWhitelist(), Config.getPreferences()); cordovaWebView.loadUrl("file:///android_asset/www/index.html");