mirror of
https://github.com/apache/cordova-android.git
synced 2025-02-20 23:56:20 +08:00
[CB-3384] Make UriResolver assert that IO is not on the UI nor WebCore threads.
This commit is contained in:
parent
bf6291020a
commit
99341bce29
@ -959,22 +959,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");
|
||||
ret = UriResolvers.createError("Whitelist rejection for: " + uri);
|
||||
} else {
|
||||
// Give plugins a chance to handle the request.
|
||||
ret = ((org.apache.cordova.PluginManager)pluginManager).resolveUri(uri);
|
||||
}
|
||||
|
||||
// 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");
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
@ -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) {
|
||||
|
@ -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.
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user