Return-Path: X-Original-To: apmail-cordova-commits-archive@www.apache.org Delivered-To: apmail-cordova-commits-archive@www.apache.org Received: from mail.apache.org (hermes.apache.org [140.211.11.3]) by minotaur.apache.org (Postfix) with SMTP id 2B7E310034 for ; Tue, 22 Oct 2013 19:20:41 +0000 (UTC) Received: (qmail 99984 invoked by uid 500); 22 Oct 2013 19:20:11 -0000 Delivered-To: apmail-cordova-commits-archive@cordova.apache.org Received: (qmail 99720 invoked by uid 500); 22 Oct 2013 19:20:03 -0000 Mailing-List: contact commits-help@cordova.apache.org; run by ezmlm Precedence: bulk List-Help: List-Unsubscribe: List-Post: List-Id: Reply-To: dev@cordova.apache.org Delivered-To: mailing list commits@cordova.apache.org Received: (qmail 99650 invoked by uid 99); 22 Oct 2013 19:20:00 -0000 Received: from tyr.zones.apache.org (HELO tyr.zones.apache.org) (140.211.11.114) by apache.org (qpsmtpd/0.29) with ESMTP; Tue, 22 Oct 2013 19:20:00 +0000 Received: by tyr.zones.apache.org (Postfix, from userid 65534) id 5A65F50FC1; Tue, 22 Oct 2013 19:19:59 +0000 (UTC) Content-Type: text/plain; charset="us-ascii" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit From: agrieve@apache.org To: commits@cordova.apache.org Date: Tue, 22 Oct 2013 19:19:59 -0000 Message-Id: <4c27ce9e226541a38a216eadc4e24084@git.apache.org> X-Mailer: ASF-Git Admin Mailer Subject: [01/16] android commit: [CB-3384] Make UriResolver assert that IO is not on the UI nor WebCore threads. (cherry picked from commit 99341bce295d7ab373de9f91c43a4d2a59be22c2) Updated Branches: refs/heads/2.9.x 6a57a3c45 -> 93b9b53ac [CB-3384] Make UriResolver assert that IO is not on the UI nor WebCore threads. (cherry picked from commit 99341bce295d7ab373de9f91c43a4d2a59be22c2) Project: http://git-wip-us.apache.org/repos/asf/cordova-android/repo Commit: http://git-wip-us.apache.org/repos/asf/cordova-android/commit/e9b46e5c Tree: http://git-wip-us.apache.org/repos/asf/cordova-android/tree/e9b46e5c Diff: http://git-wip-us.apache.org/repos/asf/cordova-android/diff/e9b46e5c Branch: refs/heads/2.9.x Commit: e9b46e5cf6d8a91918001795f2bd395e7573c186 Parents: 6a57a3c Author: Andrew Grieve Authored: Fri Jul 5 11:44:38 2013 -0400 Committer: Andrew Grieve Committed: Tue Oct 22 15:04:08 2013 -0400 ---------------------------------------------------------------------- .../src/org/apache/cordova/CordovaWebView.java | 22 ++-- .../cordova/IceCreamCordovaWebViewClient.java | 43 ++++--- .../src/org/apache/cordova/UriResolver.java | 3 - .../src/org/apache/cordova/UriResolvers.java | 124 +++++++++++-------- 4 files changed, 106 insertions(+), 86 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/cordova-android/blob/e9b46e5c/framework/src/org/apache/cordova/CordovaWebView.java ---------------------------------------------------------------------- diff --git a/framework/src/org/apache/cordova/CordovaWebView.java b/framework/src/org/apache/cordova/CordovaWebView.java index 472f5af..278bfa4 100755 --- a/framework/src/org/apache/cordova/CordovaWebView.java +++ b/framework/src/org/apache/cordova/CordovaWebView.java @@ -961,22 +961,22 @@ public class CordovaWebView extends WebView { if (!uri.isAbsolute()) { throw new IllegalArgumentException("Relative URIs are not yet supported by resolveUri."); } + UriResolver ret = null; // Check the against the white-list before delegating to plugins. if (("http".equals(uri.getScheme()) || "https".equals(uri.getScheme())) && !Config.isUrlWhiteListed(uri.toString())) { LOG.w(TAG, "resolveUri - URL is not in whitelist: " + uri); - return new UriResolvers.ErrorUriResolver(uri, "Whitelist rejection"); - } - - // Give plugins a chance to handle the request. - UriResolver resolver = ((org.apache.cordova.PluginManager)pluginManager).resolveUri(uri); - if (resolver == null && !fromWebView) { - resolver = UriResolvers.forUri(uri, cordova.getActivity()); - if (resolver == null) { - resolver = new UriResolvers.ErrorUriResolver(uri, "Unresolvable URI"); + ret = UriResolvers.createError("Whitelist rejection for: " + uri); + } else { + // Give plugins a chance to handle the request. + ret = ((org.apache.cordova.PluginManager)pluginManager).resolveUri(uri); + } + if (ret == null && !fromWebView) { + ret = UriResolvers.forUri(uri, cordova.getActivity()); + if (ret == null) { + ret = UriResolvers.createError("Unresolvable URI: " + uri); } } - - return resolver; + return ret == null ? null : UriResolvers.makeThreadChecking(ret); } } http://git-wip-us.apache.org/repos/asf/cordova-android/blob/e9b46e5c/framework/src/org/apache/cordova/IceCreamCordovaWebViewClient.java ---------------------------------------------------------------------- diff --git a/framework/src/org/apache/cordova/IceCreamCordovaWebViewClient.java b/framework/src/org/apache/cordova/IceCreamCordovaWebViewClient.java index 8527d35..c23d580 100644 --- a/framework/src/org/apache/cordova/IceCreamCordovaWebViewClient.java +++ b/framework/src/org/apache/cordova/IceCreamCordovaWebViewClient.java @@ -44,27 +44,34 @@ public class IceCreamCordovaWebViewClient extends CordovaWebViewClient { @Override public WebResourceResponse shouldInterceptRequest(WebView view, String url) { - UriResolver uriResolver = appView.resolveUri(Uri.parse(url), true); - - if (uriResolver == null && url.startsWith("file:///android_asset/")) { - if (url.contains("?") || url.contains("#") || needsIceCreamSpecialsInAssetUrlFix(url)) { - uriResolver = appView.resolveUri(Uri.parse(url), false); + // Disable checks during shouldInterceptRequest since there is no way to avoid IO here :(. + UriResolvers.webCoreThread = null; + try { + UriResolver uriResolver = appView.resolveUri(Uri.parse(url), true); + + if (uriResolver == null && url.startsWith("file:///android_asset/")) { + if (url.contains("?") || url.contains("#") || needsIceCreamSpecialsInAssetUrlFix(url)) { + uriResolver = appView.resolveUri(Uri.parse(url), false); + } } - } - - if (uriResolver != null) { - try { - InputStream stream = uriResolver.getInputStream(); - String mimeType = uriResolver.getMimeType(); - // If we don't know how to open this file, let the browser continue loading - return new WebResourceResponse(mimeType, "UTF-8", stream); - } catch (IOException e) { - LOG.e("IceCreamCordovaWebViewClient", "Error occurred while loading a file.", e); - // Results in a 404. - return new WebResourceResponse("text/plain", "UTF-8", null); + + if (uriResolver != null) { + try { + InputStream stream = uriResolver.getInputStream(); + String mimeType = uriResolver.getMimeType(); + // If we don't know how to open this file, let the browser continue loading + return new WebResourceResponse(mimeType, "UTF-8", stream); + } catch (IOException e) { + LOG.e("IceCreamCordovaWebViewClient", "Error occurred while loading a file.", e); + // Results in a 404. + return new WebResourceResponse("text/plain", "UTF-8", null); + } } + return null; + } finally { + // Tell the Thread-Checking resolve what thread the WebCore thread is. + UriResolvers.webCoreThread = Thread.currentThread(); } - return null; } private static boolean needsIceCreamSpecialsInAssetUrlFix(String url) { http://git-wip-us.apache.org/repos/asf/cordova-android/blob/e9b46e5c/framework/src/org/apache/cordova/UriResolver.java ---------------------------------------------------------------------- diff --git a/framework/src/org/apache/cordova/UriResolver.java b/framework/src/org/apache/cordova/UriResolver.java index 42e9a3a..8341b18 100644 --- a/framework/src/org/apache/cordova/UriResolver.java +++ b/framework/src/org/apache/cordova/UriResolver.java @@ -31,9 +31,6 @@ import android.net.Uri; */ public interface UriResolver { - /** Returns the URI that this instance will resolve. */ - Uri getUri(); - /** * Returns the InputStream for the resource. * Throws an exception if it cannot be read. http://git-wip-us.apache.org/repos/asf/cordova-android/blob/e9b46e5c/framework/src/org/apache/cordova/UriResolvers.java ---------------------------------------------------------------------- diff --git a/framework/src/org/apache/cordova/UriResolvers.java b/framework/src/org/apache/cordova/UriResolvers.java index e8be407..dcb5001 100644 --- a/framework/src/org/apache/cordova/UriResolvers.java +++ b/framework/src/org/apache/cordova/UriResolvers.java @@ -34,76 +34,64 @@ import android.content.ContentResolver; import android.content.Context; import android.content.res.AssetManager; import android.net.Uri; +import android.os.Looper; /* * UriResolver implementations. */ public final class UriResolvers { + static Thread webCoreThread; + private UriResolvers() {} private static final class FileUriResolver implements UriResolver { - private final Uri uri; + private final File localFile; private String mimeType; - private File localFile; FileUriResolver(Uri uri) { - this.uri = uri; - } - - public Uri getUri() { - return uri; + localFile = new File(uri.getPath()); } public InputStream getInputStream() throws IOException { - return new FileInputStream(getLocalFile()); + return new FileInputStream(localFile); } public OutputStream getOutputStream() throws FileNotFoundException { - return new FileOutputStream(getLocalFile()); + return new FileOutputStream(localFile); } public String getMimeType() { if (mimeType == null) { - mimeType = FileHelper.getMimeTypeForExtension(getLocalFile().getName()); + mimeType = FileHelper.getMimeTypeForExtension(localFile.getName()); } return mimeType; } public boolean isWritable() { - File f = getLocalFile(); - if (f.isDirectory()) { + if (localFile.isDirectory()) { return false; } - if (f.exists()) { - return f.canWrite(); + if (localFile.exists()) { + return localFile.canWrite(); } - return f.getParentFile().canWrite(); + return localFile.getParentFile().canWrite(); } public File getLocalFile() { - if (localFile == null) { - localFile = new File(uri.getPath()); - } return localFile; } } private static final class AssetUriResolver implements UriResolver { - private final Uri uri; private final AssetManager assetManager; private final String assetPath; private String mimeType; AssetUriResolver(Uri uri, AssetManager assetManager) { - this.uri = uri; this.assetManager = assetManager; this.assetPath = uri.getPath().substring(15); } - public Uri getUri() { - return uri; - } - public InputStream getInputStream() throws IOException { return assetManager.open(assetPath); } @@ -138,10 +126,6 @@ public final class UriResolvers { this.contentResolver = contentResolver; } - public Uri getUri() { - return uri; - } - public InputStream getInputStream() throws IOException { return contentResolver.openInputStream(uri); } @@ -166,88 +150,108 @@ public final class UriResolvers { } } - static final class ErrorUriResolver implements UriResolver { - final Uri uri; + private static final class ErrorUriResolver implements UriResolver { final String errorMsg; - ErrorUriResolver(Uri uri, String errorMsg) { - this.uri = uri; + ErrorUriResolver(String errorMsg) { this.errorMsg = errorMsg; } - @Override public boolean isWritable() { return false; } - @Override - public Uri getUri() { - return uri; - } - - @Override public File getLocalFile() { return null; } - @Override public OutputStream getOutputStream() throws IOException { throw new FileNotFoundException(errorMsg); } - @Override public String getMimeType() { return null; } - @Override public InputStream getInputStream() throws IOException { throw new FileNotFoundException(errorMsg); } } private static final class ReadOnlyResolver implements UriResolver { - private Uri uri; private InputStream inputStream; private String mimeType; public ReadOnlyResolver(Uri uri, InputStream inputStream, String mimeType) { - this.uri = uri; this.inputStream = inputStream; this.mimeType = mimeType; } - @Override public boolean isWritable() { return false; } - @Override - public Uri getUri() { - return uri; - } - - @Override public File getLocalFile() { return null; } - @Override public OutputStream getOutputStream() throws IOException { throw new FileNotFoundException("URI is not writable"); } - @Override public String getMimeType() { return mimeType; } - @Override public InputStream getInputStream() throws IOException { return inputStream; } } + private static final class ThreadCheckingResolver implements UriResolver { + final UriResolver delegate; + + ThreadCheckingResolver(UriResolver delegate) { + this.delegate = delegate; + } + + private static void checkThread() { + Thread curThread = Thread.currentThread(); + if (curThread == Looper.getMainLooper().getThread()) { + throw new IllegalStateException("Do not perform IO operations on the UI thread. Use CordovaInterface.getThreadPool() instead."); + } + if (curThread == webCoreThread) { + throw new IllegalStateException("Tried to perform an IO operation on the WebCore thread. Use CordovaInterface.getThreadPool() instead."); + } + } + + public boolean isWritable() { + checkThread(); + return delegate.isWritable(); + } + + + public File getLocalFile() { + checkThread(); + return delegate.getLocalFile(); + } + + public OutputStream getOutputStream() throws IOException { + checkThread(); + return delegate.getOutputStream(); + } + + public String getMimeType() { + checkThread(); + return delegate.getMimeType(); + } + + public InputStream getInputStream() throws IOException { + checkThread(); + return delegate.getInputStream(); + } + } + public static UriResolver createInline(Uri uri, String response, String mimeType) { return createInline(uri, EncodingUtils.getBytes(response, "UTF-8"), mimeType); } @@ -260,6 +264,10 @@ public final class UriResolvers { return new ReadOnlyResolver(uri, inputStream, mimeType); } + public static UriResolver createError(String errorMsg) { + return new ErrorUriResolver(errorMsg); + } + /* Package-private to force clients to go through CordovaWebView.resolveUri(). */ static UriResolver forUri(Uri uri, Context context) { String scheme = uri.getScheme(); @@ -274,4 +282,12 @@ public final class UriResolvers { } return null; } + + /* Used only by CordovaWebView.resolveUri(). */ + static UriResolver makeThreadChecking(UriResolver resolver) { + if (resolver instanceof ThreadCheckingResolver) { + return resolver; + } + return new ThreadCheckingResolver(resolver); + } } \ No newline at end of file