mirror of
https://github.com/apache/cordova-android.git
synced 2025-02-20 23:56:20 +08:00
[CB-3384] Reworked UriResolver into CordovaResourceApi.
Changes were made after trying to use the API for Camera, FileTransfer, Media. The main difference is separating the concept of URI remapping from the read/write helpers.
This commit is contained in:
parent
210d7c76e6
commit
77e9092108
@ -22,7 +22,6 @@ import org.apache.cordova.CordovaArgs;
|
||||
import org.apache.cordova.CordovaWebView;
|
||||
import org.apache.cordova.CordovaInterface;
|
||||
import org.apache.cordova.CallbackContext;
|
||||
import org.apache.cordova.UriResolver;
|
||||
import org.json.JSONArray;
|
||||
import org.json.JSONException;
|
||||
|
||||
@ -165,13 +164,12 @@ public class CordovaPlugin {
|
||||
}
|
||||
|
||||
/**
|
||||
* Hook for overriding the default URI handling mechanism.
|
||||
* Applies to WebView requests as well as requests made by plugins.
|
||||
* Hook for redirecting requests. Applies to WebView requests as well as requests made by plugins.
|
||||
*/
|
||||
public UriResolver resolveUri(Uri uri) {
|
||||
public Uri remapUri(Uri uri) {
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Called when the WebView does a top-level navigation or refreshes.
|
||||
*
|
||||
|
347
framework/src/org/apache/cordova/CordovaResourceApi.java
Normal file
347
framework/src/org/apache/cordova/CordovaResourceApi.java
Normal file
@ -0,0 +1,347 @@
|
||||
/*
|
||||
Licensed to the Apache Software Foundation (ASF) under one
|
||||
or more contributor license agreements. See the NOTICE file
|
||||
distributed with this work for additional information
|
||||
regarding copyright ownership. The ASF licenses this file
|
||||
to you under the Apache License, Version 2.0 (the
|
||||
"License"); you may not use this file except in compliance
|
||||
with the License. You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing,
|
||||
software distributed under the License is distributed on an
|
||||
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
KIND, either express or implied. See the License for the
|
||||
specific language governing permissions and limitations
|
||||
under the License.
|
||||
*/
|
||||
package org.apache.cordova;
|
||||
|
||||
import android.content.ContentResolver;
|
||||
import android.content.Context;
|
||||
import android.content.res.AssetFileDescriptor;
|
||||
import android.content.res.AssetManager;
|
||||
import android.database.Cursor;
|
||||
import android.net.Uri;
|
||||
import android.os.Looper;
|
||||
import android.util.Base64;
|
||||
import android.util.Base64InputStream;
|
||||
|
||||
import com.squareup.okhttp.OkHttpClient;
|
||||
|
||||
import org.apache.http.util.EncodingUtils;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.net.HttpURLConnection;
|
||||
import java.net.URL;
|
||||
import java.nio.channels.FileChannel;
|
||||
|
||||
public class CordovaResourceApi {
|
||||
@SuppressWarnings("unused")
|
||||
private static final String LOG_TAG = "CordovaResourceApi";
|
||||
|
||||
public static final int URI_TYPE_FILE = 0;
|
||||
public static final int URI_TYPE_ASSET = 1;
|
||||
public static final int URI_TYPE_CONTENT = 2;
|
||||
public static final int URI_TYPE_RESOURCE = 3;
|
||||
public static final int URI_TYPE_DATA = 4;
|
||||
public static final int URI_TYPE_HTTP = 5;
|
||||
public static final int URI_TYPE_HTTPS = 6;
|
||||
public static final int URI_TYPE_UNKNOWN = -1;
|
||||
|
||||
private static final String[] LOCAL_FILE_PROJECTION = { "_data" };
|
||||
|
||||
// Creating this is light-weight.
|
||||
private static OkHttpClient httpClient = new OkHttpClient();
|
||||
|
||||
static Thread webCoreThread;
|
||||
|
||||
private final AssetManager assetManager;
|
||||
private final ContentResolver contentResolver;
|
||||
private final PluginManager pluginManager;
|
||||
private boolean threadCheckingEnabled = true;
|
||||
|
||||
|
||||
public CordovaResourceApi(Context context, PluginManager pluginManager) {
|
||||
this.contentResolver = context.getContentResolver();
|
||||
this.assetManager = context.getAssets();
|
||||
this.pluginManager = pluginManager;
|
||||
}
|
||||
|
||||
public void setThreadCheckingEnabled(boolean value) {
|
||||
threadCheckingEnabled = value;
|
||||
}
|
||||
|
||||
public boolean isThreadCheckingEnabled() {
|
||||
return threadCheckingEnabled;
|
||||
}
|
||||
|
||||
public static int getUriType(Uri uri) {
|
||||
assertNonRelative(uri);
|
||||
String scheme = uri.getScheme();
|
||||
if (ContentResolver.SCHEME_CONTENT.equals(scheme)) {
|
||||
return URI_TYPE_CONTENT;
|
||||
}
|
||||
if (ContentResolver.SCHEME_ANDROID_RESOURCE.equals(scheme)) {
|
||||
return URI_TYPE_RESOURCE;
|
||||
}
|
||||
if (ContentResolver.SCHEME_FILE.equals(scheme)) {
|
||||
if (uri.getPath().startsWith("/android_asset/")) {
|
||||
return URI_TYPE_ASSET;
|
||||
}
|
||||
return URI_TYPE_FILE;
|
||||
}
|
||||
if ("data".equals(scheme)) {
|
||||
return URI_TYPE_DATA;
|
||||
}
|
||||
if ("http".equals(scheme)) {
|
||||
return URI_TYPE_HTTP;
|
||||
}
|
||||
if ("https".equals(scheme)) {
|
||||
return URI_TYPE_HTTPS;
|
||||
}
|
||||
return URI_TYPE_UNKNOWN;
|
||||
}
|
||||
|
||||
public Uri remapUri(Uri uri) {
|
||||
assertNonRelative(uri);
|
||||
Uri pluginUri = pluginManager.remapUri(uri);
|
||||
return pluginUri != null ? pluginUri : uri;
|
||||
}
|
||||
|
||||
public String remapPath(String path) {
|
||||
return remapUri(Uri.fromFile(new File(path))).getPath();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a File that points to the resource, or null if the resource
|
||||
* is not on the local filesystem.
|
||||
*/
|
||||
public File mapUriToFile(Uri uri) {
|
||||
assertBackgroundThread();
|
||||
switch (getUriType(uri)) {
|
||||
case URI_TYPE_FILE:
|
||||
return new File(uri.getPath());
|
||||
case URI_TYPE_CONTENT: {
|
||||
Cursor cursor = contentResolver.query(uri, LOCAL_FILE_PROJECTION, null, null, null);
|
||||
if (cursor != null) {
|
||||
try {
|
||||
int columnIndex = cursor.getColumnIndex(LOCAL_FILE_PROJECTION[0]);
|
||||
if (columnIndex != -1 && cursor.getCount() > 0) {
|
||||
cursor.moveToFirst();
|
||||
String realPath = cursor.getString(columnIndex);
|
||||
if (realPath != null) {
|
||||
return new File(realPath);
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
cursor.close();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Opens a stream to the givne URI, also providing the MIME type & length.
|
||||
* @return Never returns null.
|
||||
* @throws Throws an InvalidArgumentException for relative URIs. Relative URIs should be
|
||||
* resolved before being passed into this function.
|
||||
* @throws Throws an IOException if the URI cannot be opened.
|
||||
*/
|
||||
public OpenForReadResult openForRead(Uri uri) throws IOException {
|
||||
assertBackgroundThread();
|
||||
switch (getUriType(uri)) {
|
||||
case URI_TYPE_FILE: {
|
||||
FileInputStream inputStream = new FileInputStream(uri.getPath());
|
||||
String mimeType = FileHelper.getMimeTypeForExtension(uri.getPath());
|
||||
long length = inputStream.getChannel().size();
|
||||
return new OpenForReadResult(uri, inputStream, mimeType, length, null);
|
||||
}
|
||||
case URI_TYPE_ASSET: {
|
||||
String assetPath = uri.getPath().substring(15);
|
||||
AssetFileDescriptor assetFd = null;
|
||||
InputStream inputStream;
|
||||
long length = -1;
|
||||
try {
|
||||
assetFd = assetManager.openFd(assetPath);
|
||||
inputStream = assetFd.createInputStream();
|
||||
length = assetFd.getLength();
|
||||
} catch (FileNotFoundException e) {
|
||||
// Will occur if the file is compressed.
|
||||
inputStream = assetManager.open(assetPath);
|
||||
}
|
||||
String mimeType = FileHelper.getMimeTypeForExtension(assetPath);
|
||||
return new OpenForReadResult(uri, inputStream, mimeType, length, assetFd);
|
||||
}
|
||||
case URI_TYPE_CONTENT:
|
||||
case URI_TYPE_RESOURCE: {
|
||||
String mimeType = contentResolver.getType(uri);
|
||||
AssetFileDescriptor assetFd = contentResolver.openAssetFileDescriptor(uri, "r");
|
||||
InputStream inputStream = assetFd.createInputStream();
|
||||
long length = assetFd.getLength();
|
||||
return new OpenForReadResult(uri, inputStream, mimeType, length, assetFd);
|
||||
}
|
||||
case URI_TYPE_DATA: {
|
||||
OpenForReadResult ret = readDataUri(uri);
|
||||
if (ret == null) {
|
||||
break;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
case URI_TYPE_HTTP:
|
||||
case URI_TYPE_HTTPS: {
|
||||
HttpURLConnection conn = httpClient.open(new URL(uri.toString()));
|
||||
conn.setDoInput(true);
|
||||
String mimeType = conn.getHeaderField("Content-Type");
|
||||
int length = conn.getContentLength();
|
||||
InputStream inputStream = conn.getInputStream();
|
||||
return new OpenForReadResult(uri, inputStream, mimeType, length, null);
|
||||
}
|
||||
}
|
||||
throw new FileNotFoundException("URI not supported by CordovaResourceApi: " + uri);
|
||||
}
|
||||
|
||||
public OutputStream openOutputStream(Uri uri) throws IOException {
|
||||
return openOutputStream(uri, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Opens a stream to the given URI.
|
||||
* @return Never returns null.
|
||||
* @throws Throws an InvalidArgumentException for relative URIs. Relative URIs should be
|
||||
* resolved before being passed into this function.
|
||||
* @throws Throws an IOException if the URI cannot be opened.
|
||||
*/
|
||||
public OutputStream openOutputStream(Uri uri, boolean append) throws IOException {
|
||||
assertBackgroundThread();
|
||||
switch (getUriType(uri)) {
|
||||
case URI_TYPE_FILE: {
|
||||
File localFile = new File(uri.getPath());
|
||||
File parent = localFile.getParentFile();
|
||||
if (parent != null) {
|
||||
parent.mkdirs();
|
||||
}
|
||||
return new FileOutputStream(localFile, append);
|
||||
}
|
||||
case URI_TYPE_CONTENT:
|
||||
case URI_TYPE_RESOURCE: {
|
||||
AssetFileDescriptor assetFd = contentResolver.openAssetFileDescriptor(uri, append ? "wa" : "w");
|
||||
return assetFd.createOutputStream();
|
||||
}
|
||||
}
|
||||
throw new FileNotFoundException("URI not supported by CordovaResourceApi: " + uri);
|
||||
}
|
||||
|
||||
public HttpURLConnection createHttpConnection(Uri uri) throws IOException {
|
||||
assertBackgroundThread();
|
||||
return httpClient.open(new URL(uri.toString()));
|
||||
}
|
||||
|
||||
// Copies the input to the output in the most efficient manner possible.
|
||||
// Closes both streams.
|
||||
public void copyResource(OpenForReadResult input, OutputStream outputStream) throws IOException {
|
||||
assertBackgroundThread();
|
||||
try {
|
||||
InputStream inputStream = input.inputStream;
|
||||
if (inputStream instanceof FileInputStream && outputStream instanceof FileOutputStream) {
|
||||
FileChannel inChannel = ((FileInputStream)input.inputStream).getChannel();
|
||||
FileChannel outChannel = ((FileOutputStream)outputStream).getChannel();
|
||||
long offset = 0;
|
||||
long length = input.length;
|
||||
if (input.assetFd != null) {
|
||||
offset = input.assetFd.getStartOffset();
|
||||
}
|
||||
outChannel.transferFrom(inChannel, offset, length);
|
||||
} else {
|
||||
final int BUFFER_SIZE = 8192;
|
||||
byte[] buffer = new byte[BUFFER_SIZE];
|
||||
|
||||
for (;;) {
|
||||
int bytesRead = inputStream.read(buffer, 0, BUFFER_SIZE);
|
||||
|
||||
if (bytesRead <= 0) {
|
||||
break;
|
||||
}
|
||||
outputStream.write(buffer, 0, bytesRead);
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
input.inputStream.close();
|
||||
if (outputStream != null) {
|
||||
outputStream.close();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void copyResource(Uri sourceUri, OutputStream outputStream) throws IOException {
|
||||
copyResource(openForRead(sourceUri), outputStream);
|
||||
}
|
||||
|
||||
|
||||
private void assertBackgroundThread() {
|
||||
if (threadCheckingEnabled) {
|
||||
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.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private OpenForReadResult readDataUri(Uri uri) {
|
||||
String uriAsString = uri.toString().substring(5);
|
||||
int commaPos = uriAsString.indexOf(',');
|
||||
if (commaPos == -1) {
|
||||
return null;
|
||||
}
|
||||
String[] mimeParts = uriAsString.substring(0, commaPos).split(";");
|
||||
String contentType = null;
|
||||
boolean base64 = false;
|
||||
if (mimeParts.length > 0) {
|
||||
contentType = mimeParts[0];
|
||||
}
|
||||
for (int i = 1; i < mimeParts.length; ++i) {
|
||||
if ("base64".equalsIgnoreCase(mimeParts[i])) {
|
||||
base64 = true;
|
||||
}
|
||||
}
|
||||
String dataPartAsString = uriAsString.substring(commaPos + 1);
|
||||
byte[] data = base64 ? Base64.decode(dataPartAsString, Base64.DEFAULT) : EncodingUtils.getBytes(dataPartAsString, "UTF-8");
|
||||
InputStream inputStream = new ByteArrayInputStream(data);
|
||||
return new OpenForReadResult(uri, inputStream, contentType, data.length, null);
|
||||
}
|
||||
|
||||
private static void assertNonRelative(Uri uri) {
|
||||
if (!uri.isAbsolute()) {
|
||||
throw new IllegalArgumentException("Relative URIs are not supported.");
|
||||
}
|
||||
}
|
||||
|
||||
public static final class OpenForReadResult {
|
||||
public final Uri uri;
|
||||
public final InputStream inputStream;
|
||||
public final String mimeType;
|
||||
public final long length;
|
||||
public final AssetFileDescriptor assetFd;
|
||||
|
||||
OpenForReadResult(Uri uri, InputStream inputStream, String mimeType, long length, AssetFileDescriptor assetFd) {
|
||||
this.uri = uri;
|
||||
this.inputStream = inputStream;
|
||||
this.mimeType = mimeType;
|
||||
this.length = length;
|
||||
this.assetFd = assetFd;
|
||||
}
|
||||
}
|
||||
}
|
@ -23,8 +23,6 @@ import java.lang.reflect.InvocationTargetException;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.Stack;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import org.apache.cordova.Config;
|
||||
import org.apache.cordova.CordovaInterface;
|
||||
@ -96,6 +94,8 @@ public class CordovaWebView extends WebView {
|
||||
|
||||
private ActivityResult mResult = null;
|
||||
|
||||
private CordovaResourceApi resourceApi;
|
||||
|
||||
class ActivityResult {
|
||||
|
||||
int request;
|
||||
@ -306,6 +306,7 @@ public class CordovaWebView extends WebView {
|
||||
pluginManager = new PluginManager(this, this.cordova);
|
||||
jsMessageQueue = new NativeToJsMessageQueue(this, cordova);
|
||||
exposedJsApi = new ExposedJsApi(pluginManager, jsMessageQueue);
|
||||
resourceApi = new CordovaResourceApi(this.getContext(), pluginManager);
|
||||
exposeJsInterface();
|
||||
}
|
||||
|
||||
@ -955,37 +956,7 @@ public class CordovaWebView extends WebView {
|
||||
mResult = new ActivityResult(requestCode, resultCode, intent);
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolves the given URI, giving plugins a chance to re-route or customly handle the URI.
|
||||
* A white-list rejection will be returned if the URI does not pass the white-list.
|
||||
* @return Never returns null.
|
||||
* @throws Throws an InvalidArgumentException for relative URIs. Relative URIs should be
|
||||
* resolved before being passed into this function.
|
||||
*/
|
||||
public UriResolver resolveUri(Uri uri) {
|
||||
return resolveUri(uri, false);
|
||||
}
|
||||
|
||||
UriResolver resolveUri(Uri uri, boolean fromWebView) {
|
||||
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);
|
||||
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 ret == null ? null : UriResolvers.makeThreadChecking(ret);
|
||||
public CordovaResourceApi getResourceApi() {
|
||||
return resourceApi;
|
||||
}
|
||||
}
|
||||
|
@ -48,7 +48,7 @@ import android.webkit.WebViewClient;
|
||||
*/
|
||||
public class CordovaWebViewClient extends WebViewClient {
|
||||
|
||||
private static final String TAG = "Cordova";
|
||||
private static final String TAG = "CordovaWebViewClient";
|
||||
private static final String CORDOVA_EXEC_URL_PREFIX = "http://cdv_exec/";
|
||||
CordovaInterface cordova;
|
||||
CordovaWebView appView;
|
||||
|
@ -19,20 +19,22 @@
|
||||
package org.apache.cordova;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
|
||||
import org.apache.cordova.CordovaInterface;
|
||||
import org.apache.cordova.CordovaResourceApi.OpenForReadResult;
|
||||
import org.apache.cordova.LOG;
|
||||
|
||||
import android.annotation.TargetApi;
|
||||
import android.net.Uri;
|
||||
import android.os.Build;
|
||||
import android.util.Log;
|
||||
import android.webkit.WebResourceResponse;
|
||||
import android.webkit.WebView;
|
||||
|
||||
@TargetApi(Build.VERSION_CODES.HONEYCOMB)
|
||||
public class IceCreamCordovaWebViewClient extends CordovaWebViewClient {
|
||||
|
||||
private static final String TAG = "IceCreamCordovaWebViewClient";
|
||||
|
||||
public IceCreamCordovaWebViewClient(CordovaInterface cordova) {
|
||||
super(cordova);
|
||||
@ -44,38 +46,44 @@ public class IceCreamCordovaWebViewClient extends CordovaWebViewClient {
|
||||
|
||||
@Override
|
||||
public WebResourceResponse shouldInterceptRequest(WebView view, String url) {
|
||||
// Disable checks during shouldInterceptRequest since there is no way to avoid IO here :(.
|
||||
UriResolvers.webCoreThread = null;
|
||||
// Tell the Thread-Checking resolve what thread the WebCore thread is.
|
||||
CordovaResourceApi.webCoreThread = Thread.currentThread();
|
||||
Log.e("WHAAAA", "FOOD " + CordovaResourceApi.webCoreThread);
|
||||
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);
|
||||
}
|
||||
// Check the against the white-list.
|
||||
if ((url.startsWith("http:") || url.startsWith("https:")) && !Config.isUrlWhiteListed(url)) {
|
||||
LOG.w(TAG, "URL blocked by whitelist: " + url);
|
||||
// Results in a 404.
|
||||
return new WebResourceResponse("text/plain", "UTF-8", null);
|
||||
}
|
||||
|
||||
CordovaResourceApi resourceApi = appView.getResourceApi();
|
||||
Uri origUri = Uri.parse(url);
|
||||
// Allow plugins to intercept WebView requests.
|
||||
Uri remappedUri = resourceApi.remapUri(origUri);
|
||||
|
||||
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 (!origUri.equals(remappedUri) || needsSpecialsInAssetUrlFix(origUri)) {
|
||||
OpenForReadResult result = resourceApi.openForRead(remappedUri);
|
||||
return new WebResourceResponse(result.mimeType, "UTF-8", result.inputStream);
|
||||
}
|
||||
// If we don't need to special-case the request, let the browser load it.
|
||||
return null;
|
||||
} finally {
|
||||
// Tell the Thread-Checking resolve what thread the WebCore thread is.
|
||||
UriResolvers.webCoreThread = Thread.currentThread();
|
||||
} 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);
|
||||
}
|
||||
}
|
||||
|
||||
private static boolean needsSpecialsInAssetUrlFix(Uri uri) {
|
||||
if (CordovaResourceApi.getUriType(uri) != CordovaResourceApi.URI_TYPE_ASSET) {
|
||||
return false;
|
||||
}
|
||||
if (uri.getQuery() != null || uri.getFragment() != null) {
|
||||
return true;
|
||||
}
|
||||
|
||||
private static boolean needsIceCreamSpecialsInAssetUrlFix(String url) {
|
||||
if (!url.contains("%20")){
|
||||
if (!uri.toString().contains("%")) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -83,8 +91,7 @@ public class IceCreamCordovaWebViewClient extends CordovaWebViewClient {
|
||||
case android.os.Build.VERSION_CODES.ICE_CREAM_SANDWICH:
|
||||
case android.os.Build.VERSION_CODES.ICE_CREAM_SANDWICH_MR1:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
@ -26,7 +26,6 @@ import java.util.concurrent.atomic.AtomicInteger;
|
||||
|
||||
import org.apache.cordova.CordovaArgs;
|
||||
import org.apache.cordova.CordovaWebView;
|
||||
import org.apache.cordova.UriResolver;
|
||||
import org.apache.cordova.CallbackContext;
|
||||
import org.apache.cordova.CordovaInterface;
|
||||
import org.apache.cordova.CordovaPlugin;
|
||||
@ -40,7 +39,6 @@ import android.content.res.XmlResourceParser;
|
||||
|
||||
import android.net.Uri;
|
||||
import android.util.Log;
|
||||
import android.webkit.WebResourceResponse;
|
||||
|
||||
/**
|
||||
* PluginManager is exposed to JavaScript in the Cordova WebView.
|
||||
@ -394,10 +392,10 @@ public class PluginManager {
|
||||
LOG.e(TAG, "=====================================================================================");
|
||||
}
|
||||
|
||||
UriResolver resolveUri(Uri uri) {
|
||||
Uri remapUri(Uri uri) {
|
||||
for (PluginEntry entry : this.entries.values()) {
|
||||
if (entry.plugin != null) {
|
||||
UriResolver ret = entry.plugin.resolveUri(uri);
|
||||
Uri ret = entry.plugin.remapUri(uri);
|
||||
if (ret != null) {
|
||||
return ret;
|
||||
}
|
||||
|
@ -1,69 +0,0 @@
|
||||
/*
|
||||
Licensed to the Apache Software Foundation (ASF) under one
|
||||
or more contributor license agreements. See the NOTICE file
|
||||
distributed with this work for additional information
|
||||
regarding copyright ownership. The ASF licenses this file
|
||||
to you under the Apache License, Version 2.0 (the
|
||||
"License"); you may not use this file except in compliance
|
||||
with the License. You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing,
|
||||
software distributed under the License is distributed on an
|
||||
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
KIND, either express or implied. See the License for the
|
||||
specific language governing permissions and limitations
|
||||
under the License.
|
||||
*/
|
||||
package org.apache.cordova;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
|
||||
/*
|
||||
* Interface for a class that can resolve URIs.
|
||||
* See CordovaUriResolver for an example.
|
||||
*/
|
||||
public abstract class UriResolver {
|
||||
|
||||
/**
|
||||
* Returns the InputStream for the resource.
|
||||
* Throws an exception if it cannot be read.
|
||||
* Never returns null.
|
||||
*/
|
||||
public abstract InputStream getInputStream() throws IOException;
|
||||
|
||||
/**
|
||||
* Returns the MIME type of the resource.
|
||||
* Returns null if the MIME type cannot be determined (e.g. content: that doesn't exist).
|
||||
*/
|
||||
public abstract String getMimeType();
|
||||
|
||||
/** Returns whether the resource is writable. */
|
||||
public abstract boolean isWritable();
|
||||
|
||||
/**
|
||||
* Returns a File that points to the resource, or null if the resource
|
||||
* is not on the local file system.
|
||||
*/
|
||||
public abstract File getLocalFile();
|
||||
|
||||
/**
|
||||
* Returns the OutputStream for the resource.
|
||||
* Throws an exception if it cannot be written to.
|
||||
* Never returns null.
|
||||
*/
|
||||
public OutputStream getOutputStream() throws IOException {
|
||||
throw new IOException("Writing is not suppported");
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the length of the input stream, or -1 if it is not computable.
|
||||
*/
|
||||
public long computeLength() throws IOException {
|
||||
return -1;
|
||||
}
|
||||
}
|
@ -1,341 +0,0 @@
|
||||
/*
|
||||
Licensed to the Apache Software Foundation (ASF) under one
|
||||
or more contributor license agreements. See the NOTICE file
|
||||
distributed with this work for additional information
|
||||
regarding copyright ownership. The ASF licenses this file
|
||||
to you under the Apache License, Version 2.0 (the
|
||||
"License"); you may not use this file except in compliance
|
||||
with the License. You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing,
|
||||
software distributed under the License is distributed on an
|
||||
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
KIND, either express or implied. See the License for the
|
||||
specific language governing permissions and limitations
|
||||
under the License.
|
||||
*/
|
||||
package org.apache.cordova;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
|
||||
import org.apache.cordova.FileHelper;
|
||||
import org.apache.http.util.EncodingUtils;
|
||||
|
||||
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 long computeSizeFromResolver(UriResolver resolver) throws IOException {
|
||||
InputStream inputStream = resolver.getInputStream();
|
||||
if (inputStream instanceof FileInputStream) {
|
||||
return ((FileInputStream)inputStream).getChannel().size();
|
||||
}
|
||||
if (inputStream instanceof ByteArrayInputStream) {
|
||||
return ((ByteArrayInputStream)inputStream).available();
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
private static final class FileUriResolver extends UriResolver {
|
||||
private final File localFile;
|
||||
private String mimeType;
|
||||
private FileInputStream cachedInputStream;
|
||||
|
||||
FileUriResolver(Uri uri) {
|
||||
localFile = new File(uri.getPath());
|
||||
}
|
||||
|
||||
public InputStream getInputStream() throws IOException {
|
||||
if (cachedInputStream == null) {
|
||||
cachedInputStream = new FileInputStream(localFile);
|
||||
}
|
||||
return cachedInputStream;
|
||||
}
|
||||
|
||||
public OutputStream getOutputStream() throws FileNotFoundException {
|
||||
File parent = localFile.getParentFile();
|
||||
if (parent != null) {
|
||||
localFile.getParentFile().mkdirs();
|
||||
}
|
||||
return new FileOutputStream(localFile);
|
||||
}
|
||||
|
||||
public String getMimeType() {
|
||||
if (mimeType == null) {
|
||||
mimeType = FileHelper.getMimeTypeForExtension(localFile.getName());
|
||||
}
|
||||
return mimeType;
|
||||
}
|
||||
|
||||
public boolean isWritable() {
|
||||
if (localFile.isDirectory()) {
|
||||
return false;
|
||||
}
|
||||
if (localFile.exists()) {
|
||||
return localFile.canWrite();
|
||||
}
|
||||
return localFile.getParentFile().canWrite();
|
||||
}
|
||||
|
||||
public File getLocalFile() {
|
||||
return localFile;
|
||||
}
|
||||
|
||||
public long computeLength() throws IOException {
|
||||
return localFile.length();
|
||||
}
|
||||
}
|
||||
|
||||
private static final class AssetUriResolver extends UriResolver {
|
||||
private final AssetManager assetManager;
|
||||
private final String assetPath;
|
||||
private String mimeType;
|
||||
private InputStream cachedInputStream;
|
||||
|
||||
AssetUriResolver(Uri uri, AssetManager assetManager) {
|
||||
this.assetManager = assetManager;
|
||||
this.assetPath = uri.getPath().substring(15);
|
||||
}
|
||||
|
||||
public InputStream getInputStream() throws IOException {
|
||||
if (cachedInputStream == null) {
|
||||
cachedInputStream = assetManager.open(assetPath);
|
||||
}
|
||||
return cachedInputStream;
|
||||
}
|
||||
|
||||
public OutputStream getOutputStream() throws FileNotFoundException {
|
||||
throw new FileNotFoundException("URI not writable.");
|
||||
}
|
||||
|
||||
public String getMimeType() {
|
||||
if (mimeType == null) {
|
||||
mimeType = FileHelper.getMimeTypeForExtension(assetPath);
|
||||
}
|
||||
return mimeType;
|
||||
}
|
||||
|
||||
public boolean isWritable() {
|
||||
return false;
|
||||
}
|
||||
|
||||
public File getLocalFile() {
|
||||
return null;
|
||||
}
|
||||
|
||||
public long computeLength() throws IOException {
|
||||
return computeSizeFromResolver(this);
|
||||
}
|
||||
}
|
||||
|
||||
private static final class ContentUriResolver extends UriResolver {
|
||||
private final Uri uri;
|
||||
private final ContentResolver contentResolver;
|
||||
private String mimeType;
|
||||
private InputStream cachedInputStream;
|
||||
|
||||
ContentUriResolver(Uri uri, ContentResolver contentResolver) {
|
||||
this.uri = uri;
|
||||
this.contentResolver = contentResolver;
|
||||
}
|
||||
|
||||
public InputStream getInputStream() throws IOException {
|
||||
if (cachedInputStream == null) {
|
||||
cachedInputStream = contentResolver.openInputStream(uri);
|
||||
}
|
||||
return cachedInputStream;
|
||||
}
|
||||
|
||||
public OutputStream getOutputStream() throws FileNotFoundException {
|
||||
return contentResolver.openOutputStream(uri);
|
||||
}
|
||||
|
||||
public String getMimeType() {
|
||||
if (mimeType == null) {
|
||||
mimeType = contentResolver.getType(uri);
|
||||
}
|
||||
return mimeType;
|
||||
}
|
||||
|
||||
public boolean isWritable() {
|
||||
return uri.getScheme().equals(ContentResolver.SCHEME_CONTENT);
|
||||
}
|
||||
|
||||
public File getLocalFile() {
|
||||
return null;
|
||||
}
|
||||
|
||||
public long computeLength() throws IOException {
|
||||
return computeSizeFromResolver(this);
|
||||
}
|
||||
}
|
||||
|
||||
private static final class ErrorUriResolver extends UriResolver {
|
||||
final String errorMsg;
|
||||
|
||||
ErrorUriResolver(String errorMsg) {
|
||||
this.errorMsg = errorMsg;
|
||||
}
|
||||
|
||||
public boolean isWritable() {
|
||||
return false;
|
||||
}
|
||||
|
||||
public File getLocalFile() {
|
||||
return null;
|
||||
}
|
||||
|
||||
public OutputStream getOutputStream() throws IOException {
|
||||
throw new FileNotFoundException(errorMsg);
|
||||
}
|
||||
|
||||
public String getMimeType() {
|
||||
return null;
|
||||
}
|
||||
|
||||
public InputStream getInputStream() throws IOException {
|
||||
throw new FileNotFoundException(errorMsg);
|
||||
}
|
||||
}
|
||||
|
||||
private static final class ReadOnlyResolver extends UriResolver {
|
||||
private InputStream inputStream;
|
||||
private String mimeType;
|
||||
|
||||
public ReadOnlyResolver(Uri uri, InputStream inputStream, String mimeType) {
|
||||
this.inputStream = inputStream;
|
||||
this.mimeType = mimeType;
|
||||
}
|
||||
|
||||
public boolean isWritable() {
|
||||
return false;
|
||||
}
|
||||
|
||||
public File getLocalFile() {
|
||||
return null;
|
||||
}
|
||||
|
||||
public OutputStream getOutputStream() throws IOException {
|
||||
throw new FileNotFoundException("URI is not writable");
|
||||
}
|
||||
|
||||
public String getMimeType() {
|
||||
return mimeType;
|
||||
}
|
||||
|
||||
public InputStream getInputStream() throws IOException {
|
||||
return inputStream;
|
||||
}
|
||||
|
||||
public long computeLength() throws IOException {
|
||||
return computeSizeFromResolver(this);
|
||||
}
|
||||
}
|
||||
|
||||
private static final class ThreadCheckingResolver extends 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 long computeLength() throws IOException {
|
||||
checkThread();
|
||||
return delegate.computeLength();
|
||||
}
|
||||
}
|
||||
|
||||
public static UriResolver createInline(Uri uri, String response, String mimeType) {
|
||||
return createInline(uri, EncodingUtils.getBytes(response, "UTF-8"), mimeType);
|
||||
}
|
||||
|
||||
public static UriResolver createInline(Uri uri, byte[] response, String mimeType) {
|
||||
return new ReadOnlyResolver(uri, new ByteArrayInputStream(response), mimeType);
|
||||
}
|
||||
|
||||
public static UriResolver createReadOnly(Uri uri, InputStream inputStream, String mimeType) {
|
||||
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();
|
||||
if (ContentResolver.SCHEME_CONTENT.equals(scheme) || ContentResolver.SCHEME_ANDROID_RESOURCE.equals(scheme)) {
|
||||
return new ContentUriResolver(uri, context.getContentResolver());
|
||||
}
|
||||
if (ContentResolver.SCHEME_FILE.equals(scheme)) {
|
||||
if (uri.getPath().startsWith("/android_asset/")) {
|
||||
return new AssetUriResolver(uri, context.getAssets());
|
||||
}
|
||||
return new FileUriResolver(uri);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/* Used only by CordovaWebView.resolveUri(). */
|
||||
static UriResolver makeThreadChecking(UriResolver resolver) {
|
||||
if (resolver instanceof ThreadCheckingResolver) {
|
||||
return resolver;
|
||||
}
|
||||
return new ThreadCheckingResolver(resolver);
|
||||
}
|
||||
}
|
@ -45,7 +45,7 @@
|
||||
<uses-permission android:name="android.permission.GET_ACCOUNTS" />
|
||||
<uses-permission android:name="android.permission.BROADCAST_STICKY" />
|
||||
|
||||
<uses-sdk android:minSdkVersion="7" />
|
||||
<uses-sdk android:minSdkVersion="8" />
|
||||
|
||||
<instrumentation
|
||||
android:name="android.test.InstrumentationTestRunner"
|
||||
|
@ -22,9 +22,9 @@ package org.apache.cordova.test;
|
||||
*
|
||||
*/
|
||||
|
||||
import org.apache.cordova.CordovaResourceApi;
|
||||
import org.apache.cordova.CordovaResourceApi.OpenForReadResult;
|
||||
import org.apache.cordova.CordovaWebView;
|
||||
import org.apache.cordova.UriResolver;
|
||||
import org.apache.cordova.UriResolvers;
|
||||
import org.apache.cordova.CallbackContext;
|
||||
import org.apache.cordova.CordovaPlugin;
|
||||
import org.apache.cordova.PluginEntry;
|
||||
@ -34,6 +34,9 @@ import org.json.JSONException;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStreamReader;
|
||||
import java.io.OutputStream;
|
||||
import java.util.Scanner;
|
||||
|
||||
import android.graphics.Bitmap;
|
||||
import android.graphics.BitmapFactory;
|
||||
@ -41,16 +44,17 @@ import android.net.Uri;
|
||||
import android.os.Environment;
|
||||
import android.provider.MediaStore;
|
||||
import android.test.ActivityInstrumentationTestCase2;
|
||||
import android.util.Log;
|
||||
|
||||
public class UriResolversTest extends ActivityInstrumentationTestCase2<CordovaWebViewTestActivity> {
|
||||
public class CordovaResourceApiTest extends ActivityInstrumentationTestCase2<CordovaWebViewTestActivity> {
|
||||
|
||||
public UriResolversTest()
|
||||
public CordovaResourceApiTest()
|
||||
{
|
||||
super(CordovaWebViewTestActivity.class);
|
||||
}
|
||||
|
||||
CordovaWebView cordovaWebView;
|
||||
CordovaResourceApi resourceApi;
|
||||
|
||||
private CordovaWebViewTestActivity activity;
|
||||
String execPayload;
|
||||
Integer execStatus;
|
||||
@ -59,32 +63,26 @@ public class UriResolversTest extends ActivityInstrumentationTestCase2<CordovaWe
|
||||
super.setUp();
|
||||
activity = this.getActivity();
|
||||
cordovaWebView = activity.cordovaWebView;
|
||||
cordovaWebView.pluginManager.addService(new PluginEntry("UriResolverTestPlugin1", new CordovaPlugin() {
|
||||
resourceApi = cordovaWebView.getResourceApi();
|
||||
resourceApi.setThreadCheckingEnabled(false);
|
||||
cordovaWebView.pluginManager.addService(new PluginEntry("CordovaResourceApiTestPlugin1", new CordovaPlugin() {
|
||||
@Override
|
||||
public UriResolver resolveUri(Uri uri) {
|
||||
if ("plugin-uri".equals(uri.getScheme())) {
|
||||
return cordovaWebView.resolveUri(uri.buildUpon().scheme("file").build());
|
||||
public Uri remapUri(Uri uri) {
|
||||
if (uri.getQuery() != null && uri.getQuery().contains("pluginRewrite")) {
|
||||
return cordovaWebView.getResourceApi().remapUri(
|
||||
Uri.parse("data:text/plain;charset=utf-8,pass"));
|
||||
}
|
||||
return null;
|
||||
}
|
||||
public boolean execute(String action, JSONArray args, CallbackContext callbackContext) throws JSONException {
|
||||
synchronized (UriResolversTest.this) {
|
||||
synchronized (CordovaResourceApiTest.this) {
|
||||
execPayload = args.getString(0);
|
||||
execStatus = args.getInt(1);
|
||||
UriResolversTest.this.notify();
|
||||
CordovaResourceApiTest.this.notify();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}));
|
||||
cordovaWebView.pluginManager.addService(new PluginEntry("UriResolverTestPlugin2", new CordovaPlugin() {
|
||||
@Override
|
||||
public UriResolver resolveUri(Uri uri) {
|
||||
if (uri.getQueryParameter("pluginRewrite") != null) {
|
||||
return UriResolvers.createInline(uri, "pass", "my/mime");
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}));
|
||||
}
|
||||
|
||||
private Uri createTestImageContentUri() {
|
||||
@ -94,19 +92,15 @@ public class UriResolversTest extends ActivityInstrumentationTestCase2<CordovaWe
|
||||
return Uri.parse(stored);
|
||||
}
|
||||
|
||||
private void performResolverTest(Uri uri, String expectedMimeType, File expectedLocalFile,
|
||||
boolean expectedIsWritable,
|
||||
private void performApiTest(Uri uri, String expectedMimeType, File expectedLocalFile,
|
||||
boolean expectRead, boolean expectWrite) throws IOException {
|
||||
UriResolver resolver = cordovaWebView.resolveUri(uri);
|
||||
assertEquals(expectedLocalFile, resolver.getLocalFile());
|
||||
assertEquals(expectedMimeType, resolver.getMimeType());
|
||||
if (expectedIsWritable) {
|
||||
assertTrue(resolver.isWritable());
|
||||
} else {
|
||||
assertFalse(resolver.isWritable());
|
||||
}
|
||||
uri = resourceApi.remapUri(uri);
|
||||
assertEquals(expectedLocalFile, resourceApi.mapUriToFile(uri));
|
||||
|
||||
try {
|
||||
resolver.getInputStream().read();
|
||||
OpenForReadResult readResult = resourceApi.openForRead(uri);
|
||||
assertEquals(expectedMimeType, readResult.mimeType);
|
||||
readResult.inputStream.read();
|
||||
if (!expectRead) {
|
||||
fail("Expected getInputStream to throw.");
|
||||
}
|
||||
@ -116,10 +110,12 @@ public class UriResolversTest extends ActivityInstrumentationTestCase2<CordovaWe
|
||||
}
|
||||
}
|
||||
try {
|
||||
resolver.getOutputStream().write(123);
|
||||
OutputStream outStream = resourceApi.openOutputStream(uri);
|
||||
outStream.write(123);
|
||||
if (!expectWrite) {
|
||||
fail("Expected getOutputStream to throw.");
|
||||
}
|
||||
outStream.close();
|
||||
} catch (IOException e) {
|
||||
if (expectWrite) {
|
||||
throw e;
|
||||
@ -130,25 +126,27 @@ public class UriResolversTest extends ActivityInstrumentationTestCase2<CordovaWe
|
||||
public void testValidContentUri() throws IOException
|
||||
{
|
||||
Uri contentUri = createTestImageContentUri();
|
||||
performResolverTest(contentUri, "image/jpeg", null, true, true, true);
|
||||
File localFile = resourceApi.mapUriToFile(contentUri);
|
||||
assertNotNull(localFile);
|
||||
performApiTest(contentUri, "image/jpeg", localFile, true, true);
|
||||
}
|
||||
|
||||
public void testInvalidContentUri() throws IOException
|
||||
{
|
||||
Uri contentUri = Uri.parse("content://media/external/images/media/999999999");
|
||||
performResolverTest(contentUri, null, null, true, false, false);
|
||||
performApiTest(contentUri, null, null, false, false);
|
||||
}
|
||||
|
||||
public void testValidAssetUri() throws IOException
|
||||
{
|
||||
Uri assetUri = Uri.parse("file:///android_asset/www/index.html?foo#bar"); // Also check for stripping off ? and # correctly.
|
||||
performResolverTest(assetUri, "text/html", null, false, true, false);
|
||||
performApiTest(assetUri, "text/html", null, true, false);
|
||||
}
|
||||
|
||||
public void testInvalidAssetUri() throws IOException
|
||||
{
|
||||
Uri assetUri = Uri.parse("file:///android_asset/www/missing.html");
|
||||
performResolverTest(assetUri, "text/html", null, false, false, false);
|
||||
performApiTest(assetUri, "text/html", null, false, false);
|
||||
}
|
||||
|
||||
public void testFileUriToExistingFile() throws IOException
|
||||
@ -156,7 +154,7 @@ public class UriResolversTest extends ActivityInstrumentationTestCase2<CordovaWe
|
||||
File f = File.createTempFile("te s t", ".txt"); // Also check for dealing with spaces.
|
||||
try {
|
||||
Uri fileUri = Uri.parse(f.toURI().toString() + "?foo#bar"); // Also check for stripping off ? and # correctly.
|
||||
performResolverTest(fileUri, "text/plain", f, true, true, true);
|
||||
performApiTest(fileUri, "text/plain", f, true, true);
|
||||
} finally {
|
||||
f.delete();
|
||||
}
|
||||
@ -167,7 +165,7 @@ public class UriResolversTest extends ActivityInstrumentationTestCase2<CordovaWe
|
||||
File f = new File(Environment.getExternalStorageDirectory() + "/somefilethatdoesntexist");
|
||||
Uri fileUri = Uri.parse(f.toURI().toString());
|
||||
try {
|
||||
performResolverTest(fileUri, null, f, true, false, true);
|
||||
performApiTest(fileUri, null, f, false, true);
|
||||
} finally {
|
||||
f.delete();
|
||||
}
|
||||
@ -175,52 +173,70 @@ public class UriResolversTest extends ActivityInstrumentationTestCase2<CordovaWe
|
||||
|
||||
public void testFileUriToMissingFileWithMissingParent() throws IOException
|
||||
{
|
||||
File f = new File(Environment.getExternalStorageDirectory() + "/somedirthatismissing/somefilethatdoesntexist");
|
||||
File f = new File(Environment.getExternalStorageDirectory() + "/somedirthatismissing" + System.currentTimeMillis() + "/somefilethatdoesntexist");
|
||||
Uri fileUri = Uri.parse(f.toURI().toString());
|
||||
performResolverTest(fileUri, null, f, false, false, false);
|
||||
performApiTest(fileUri, null, f, false, true);
|
||||
}
|
||||
|
||||
public void testUnrecognizedUri() throws IOException
|
||||
{
|
||||
Uri uri = Uri.parse("somescheme://foo");
|
||||
performResolverTest(uri, null, null, false, false, false);
|
||||
performApiTest(uri, null, null, false, false);
|
||||
}
|
||||
|
||||
public void testRelativeUri()
|
||||
{
|
||||
try {
|
||||
cordovaWebView.resolveUri(Uri.parse("/foo"));
|
||||
resourceApi.openForRead(Uri.parse("/foo"));
|
||||
fail("Should have thrown for relative URI 1.");
|
||||
} catch (Throwable t) {
|
||||
}
|
||||
try {
|
||||
cordovaWebView.resolveUri(Uri.parse("//foo/bar"));
|
||||
resourceApi.openForRead(Uri.parse("//foo/bar"));
|
||||
fail("Should have thrown for relative URI 2.");
|
||||
} catch (Throwable t) {
|
||||
}
|
||||
try {
|
||||
cordovaWebView.resolveUri(Uri.parse("foo.png"));
|
||||
resourceApi.openForRead(Uri.parse("foo.png"));
|
||||
fail("Should have thrown for relative URI 3.");
|
||||
} catch (Throwable t) {
|
||||
}
|
||||
}
|
||||
|
||||
public void testPluginOverrides1() throws IOException
|
||||
{
|
||||
Uri uri = Uri.parse("plugin-uri://foohost/android_asset/www/index.html");
|
||||
performResolverTest(uri, "text/html", null, false, true, false);
|
||||
}
|
||||
|
||||
public void testPluginOverrides2() throws IOException
|
||||
public void testPluginOverride() throws IOException
|
||||
{
|
||||
Uri uri = Uri.parse("plugin-uri://foohost/android_asset/www/index.html?pluginRewrite=yes");
|
||||
performResolverTest(uri, "my/mime", null, false, true, false);
|
||||
performApiTest(uri, "text/plain", null, true, false);
|
||||
}
|
||||
|
||||
public void testWhitelistRejection() throws IOException
|
||||
public void testMainThreadUsage() throws IOException
|
||||
{
|
||||
Uri uri = Uri.parse("http://foohost.com/");
|
||||
performResolverTest(uri, null, null, false, false, false);
|
||||
Uri assetUri = Uri.parse("file:///android_asset/www/index.html");
|
||||
resourceApi.setThreadCheckingEnabled(true);
|
||||
try {
|
||||
resourceApi.openForRead(assetUri);
|
||||
fail("Should have thrown for main thread check.");
|
||||
} catch (Throwable t) {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public void testDataUriPlain() throws IOException
|
||||
{
|
||||
Uri uri = Uri.parse("data:text/plain;charset=utf-8,pass");
|
||||
OpenForReadResult readResult = resourceApi.openForRead(uri);
|
||||
assertEquals("text/plain", readResult.mimeType);
|
||||
String data = new Scanner(readResult.inputStream, "UTF-8").useDelimiter("\\A").next();
|
||||
assertEquals("pass", data);
|
||||
}
|
||||
|
||||
public void testDataUriBase64() throws IOException
|
||||
{
|
||||
Uri uri = Uri.parse("data:text/js;charset=utf-8;base64,cGFzcw==");
|
||||
OpenForReadResult readResult = resourceApi.openForRead(uri);
|
||||
assertEquals("text/js", readResult.mimeType);
|
||||
String data = new Scanner(readResult.inputStream, "UTF-8").useDelimiter("\\A").next();
|
||||
assertEquals("pass", data);
|
||||
}
|
||||
|
||||
public void testWebViewRequestIntercept() throws IOException
|
||||
@ -229,7 +245,7 @@ public class UriResolversTest extends ActivityInstrumentationTestCase2<CordovaWe
|
||||
"var x = new XMLHttpRequest;\n" +
|
||||
"x.open('GET', 'file://foo?pluginRewrite=1', false);\n" +
|
||||
"x.send();\n" +
|
||||
"cordova.require('cordova/exec')(null,null,'UriResolverTestPlugin1', 'foo', [x.responseText, x.status])");
|
||||
"cordova.require('cordova/exec')(null,null,'CordovaResourceApiTestPlugin1', 'foo', [x.responseText, x.status])");
|
||||
execPayload = null;
|
||||
execStatus = null;
|
||||
try {
|
||||
@ -248,7 +264,7 @@ public class UriResolversTest extends ActivityInstrumentationTestCase2<CordovaWe
|
||||
"var x = new XMLHttpRequest;\n" +
|
||||
"x.open('GET', 'http://foo/bar', false);\n" +
|
||||
"x.send();\n" +
|
||||
"cordova.require('cordova/exec')(null,null,'UriResolverTestPlugin1', 'foo', [x.responseText, x.status])");
|
||||
"cordova.require('cordova/exec')(null,null,'CordovaResourceApiTestPlugin1', 'foo', [x.responseText, x.status])");
|
||||
execPayload = null;
|
||||
execStatus = null;
|
||||
try {
|
Loading…
Reference in New Issue
Block a user