diff --git a/framework/src/org/apache/cordova/AndroidWebViewClient.java b/framework/src/org/apache/cordova/AndroidWebViewClient.java index 44862a6b..c0171774 100755 --- a/framework/src/org/apache/cordova/AndroidWebViewClient.java +++ b/framework/src/org/apache/cordova/AndroidWebViewClient.java @@ -197,7 +197,19 @@ public class AndroidWebViewClient extends WebViewClient { // Clear timeout flag appView.loadUrlTimeout++; - // Handle error + // If this is a "Protocol Not Supported" error, then revert to the previous + // page. If there was no previous page, then punt. The application's config + // is likely incorrect (start page set to sms: or something like that) + if (errorCode == WebViewClient.ERROR_UNSUPPORTED_SCHEME) { + if (view.canGoBack()) { + view.goBack(); + return; + } else { + super.onReceivedError(view, errorCode, description, failingUrl); + } + } + + // Handle other errors by passing them to the webview in JS JSONObject data = new JSONObject(); try { data.put("errorCode", errorCode); diff --git a/framework/src/org/apache/cordova/CordovaActivity.java b/framework/src/org/apache/cordova/CordovaActivity.java index f84e4a33..fa332e5b 100755 --- a/framework/src/org/apache/cordova/CordovaActivity.java +++ b/framework/src/org/apache/cordova/CordovaActivity.java @@ -394,6 +394,9 @@ public class CordovaActivity extends Activity implements CordovaInterface { if (this.appView == null) { return; } + // Force window to have focus, so application always + // receive user input. Workaround for some devices (Samsung Galaxy Note 3 at least) + this.getWindow().getDecorView().requestFocus(); this.appView.handleResume(this.keepRunning, this.activityResultKeepRunning); diff --git a/framework/src/org/apache/cordova/CordovaUriHelper.java b/framework/src/org/apache/cordova/CordovaUriHelper.java index 077e425e..f7d22da8 100644 --- a/framework/src/org/apache/cordova/CordovaUriHelper.java +++ b/framework/src/org/apache/cordova/CordovaUriHelper.java @@ -19,8 +19,11 @@ package org.apache.cordova; +import android.annotation.TargetApi; import android.content.Intent; import android.net.Uri; +import android.os.Build; +import android.webkit.WebView; public class CordovaUriHelper { @@ -43,6 +46,7 @@ public class CordovaUriHelper { * @param url The url to be loaded. * @return true to override, false for default behavior */ + @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:")) @@ -68,6 +72,11 @@ public class CordovaUriHelper { try { Intent intent = new Intent(Intent.ACTION_VIEW); intent.setData(Uri.parse(url)); + intent.addCategory(Intent.CATEGORY_BROWSABLE); + intent.setComponent(null); + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH_MR1) { + intent.setSelector(null); + } this.cordova.getActivity().startActivity(intent); } catch (android.content.ActivityNotFoundException e) { LOG.e(TAG, "Error loading url " + url, e); diff --git a/framework/src/org/apache/cordova/NativeToJsMessageQueue.java b/framework/src/org/apache/cordova/NativeToJsMessageQueue.java index 75d5d434..b9460513 100755 --- a/framework/src/org/apache/cordova/NativeToJsMessageQueue.java +++ b/framework/src/org/apache/cordova/NativeToJsMessageQueue.java @@ -321,11 +321,16 @@ public class NativeToJsMessageQueue { } } }; + final Runnable resetNetworkRunnable = new Runnable() { + public void run() { + online = false; + // If the following call triggers a notifyOfFlush, then ignore it. + ignoreNextFlush = true; + webView.setNetworkAvailable(true); + } + }; @Override void reset() { - online = false; - // If the following call triggers a notifyOfFlush, then ignore it. - ignoreNextFlush = true; - webView.setNetworkAvailable(true); + cordova.getActivity().runOnUiThread(resetNetworkRunnable); } @Override void onNativeToJsMessageAvailable() { cordova.getActivity().runOnUiThread(toggleNetworkRunnable); diff --git a/framework/src/org/apache/cordova/Whitelist.java b/framework/src/org/apache/cordova/Whitelist.java index ecbb7f61..5101ec35 100644 --- a/framework/src/org/apache/cordova/Whitelist.java +++ b/framework/src/org/apache/cordova/Whitelist.java @@ -124,15 +124,15 @@ public class Whitelist { whiteList = null; } else { // specific access - Pattern parts = Pattern.compile("^((\\*|[A-Za-z-]+)://)?(\\*|((\\*\\.)?[^*/:]+))?(:(\\d+))?(/.*)?"); + 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(3); + 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(7); - String path = m.group(8); + 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)); diff --git a/test/assets/www/error.html b/test/assets/www/error.html new file mode 100644 index 00000000..aad167dc --- /dev/null +++ b/test/assets/www/error.html @@ -0,0 +1,8 @@ + + + OH NOES! + + +

Things went terribly wrong!

+ + \ No newline at end of file diff --git a/test/src/org/apache/cordova/test/CordovaWebViewTestActivity.java b/test/src/org/apache/cordova/test/CordovaWebViewTestActivity.java index 6f96e839..b143521e 100644 --- a/test/src/org/apache/cordova/test/CordovaWebViewTestActivity.java +++ b/test/src/org/apache/cordova/test/CordovaWebViewTestActivity.java @@ -22,6 +22,8 @@ package org.apache.cordova.test; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; +import org.apache.cordova.AndroidChromeClient; +import org.apache.cordova.AndroidWebViewClient; import org.apache.cordova.Config; import org.apache.cordova.CordovaWebView; import org.apache.cordova.CordovaInterface; @@ -45,10 +47,11 @@ public class CordovaWebViewTestActivity extends Activity implements CordovaInter setContentView(R.layout.main); - cordovaWebView = (CordovaWebView) findViewById(R.id.cordovaWebView); + //CB-7238: This has to be added now, because it got removed from somewhere else Config.init(this); - cordovaWebView.init(this, - Config.getPluginEntries(), Config.getWhitelist(), Config.getPreferences()); + + cordovaWebView = (CordovaWebView) findViewById(R.id.cordovaWebView); + cordovaWebView.init(this, Config.getPluginEntries(), Config.getWhitelist(), Config.getPreferences()); cordovaWebView.loadUrl("file:///android_asset/www/index.html"); diff --git a/test/src/org/apache/cordova/test/SabotagedActivity.java b/test/src/org/apache/cordova/test/SabotagedActivity.java new file mode 100644 index 00000000..e1170ba3 --- /dev/null +++ b/test/src/org/apache/cordova/test/SabotagedActivity.java @@ -0,0 +1,73 @@ +package org.apache.cordova.test; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.io.FileOutputStream; + +import org.apache.cordova.Config; +import org.apache.cordova.CordovaActivity; + +import android.content.res.AssetManager; +import android.os.Bundle; +import android.os.Environment; +import android.util.Log; + +public class SabotagedActivity extends CordovaActivity { + + private String BAD_ASSET = "www/error.html"; + private String LOG_TAG = "SabotagedActivity"; + + + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + +// copyErrorAsset(); + super.init(); + super.loadUrl(Config.getStartUrl()); + } + + /* + * Sometimes we need to move code around before we can do anything. This will + * copy the bad code out of the assets before we initalize Cordova so that when Cordova actually + * initializes, we have something for it to navigate to. + */ + + private void copyErrorAsset () { + AssetManager assetManager = getAssets(); + String[] files = null; + try { + files = assetManager.list(BAD_ASSET); + } catch (IOException e) { + Log.e(LOG_TAG, e.getMessage()); + } + + for(String filename : files) { + InputStream in = null; + OutputStream out = null; + try { + in = assetManager.open(BAD_ASSET); + out = new FileOutputStream(Environment.getExternalStorageDirectory().toString() +"/" + filename); + copy(in, out); + in.close(); + in = null; + out.flush(); + out.close(); + out = null; + } catch(Exception e) { + Log.e("tag", e.getMessage()); + } + } + } + + + //Quick and Dirty Copy! + private void copy(InputStream in, OutputStream out) throws IOException { + byte[] buffer = new byte[1024]; + int read; + while((read = in.read(buffer)) != -1){ + out.write(buffer, 0, read); + } + } +} diff --git a/test/src/org/apache/cordova/test/junit/IntentUriOverrideTest.java b/test/src/org/apache/cordova/test/junit/IntentUriOverrideTest.java new file mode 100644 index 00000000..aea06afe --- /dev/null +++ b/test/src/org/apache/cordova/test/junit/IntentUriOverrideTest.java @@ -0,0 +1,73 @@ +package org.apache.cordova.test.junit; + +import org.apache.cordova.CordovaWebView; +import org.apache.cordova.test.SabotagedActivity; +import org.apache.cordova.test.splashscreen; + +import android.app.Instrumentation; +import android.content.Context; +import android.content.Intent; +import android.content.res.AssetManager; +import android.test.ActivityInstrumentationTestCase2; +import android.widget.FrameLayout; +import android.widget.LinearLayout; + + +public class IntentUriOverrideTest extends ActivityInstrumentationTestCase2 { + + private int TIMEOUT = 1000; + + private SabotagedActivity testActivity; + private FrameLayout containerView; + private LinearLayout innerContainer; + private CordovaWebView testView; + private Instrumentation mInstr; + private String BAD_URL = "file:///sdcard/download/wl-exploit.htm"; + + + public IntentUriOverrideTest() + { + super("org.apache.cordova.test",SabotagedActivity.class); + } + + + protected void setUp() throws Exception { + super.setUp(); + mInstr = this.getInstrumentation(); + Intent badIntent = new Intent(); + badIntent.setClassName("org.apache.cordova.test", "org.apache.cordova.test.SabotagedActivity"); + badIntent.putExtra("url", BAD_URL); + setActivityIntent(badIntent); + testActivity = getActivity(); + containerView = (FrameLayout) testActivity.findViewById(android.R.id.content); + innerContainer = (LinearLayout) containerView.getChildAt(0); + testView = (CordovaWebView) innerContainer.getChildAt(0); + } + + + public void testPreconditions(){ + assertNotNull(innerContainer); + assertNotNull(testView); + } + + public void testChangeStartUrl() throws Throwable + { + runTestOnUiThread(new Runnable() { + public void run() + { + boolean isBadUrl = testView.getUrl().equals(BAD_URL); + assertFalse(isBadUrl); + } + }); + } + + private void sleep() { + try { + Thread.sleep(TIMEOUT); + } catch (InterruptedException e) { + fail("Unexpected Timeout"); + } + } + + +}