diff --git a/framework/src/org/apache/cordova/CordovaWebView.java b/framework/src/org/apache/cordova/CordovaWebView.java index 67775a19..3a8e48d8 100755 --- a/framework/src/org/apache/cordova/CordovaWebView.java +++ b/framework/src/org/apache/cordova/CordovaWebView.java @@ -943,4 +943,38 @@ public class CordovaWebView extends WebView { public void storeResult(int requestCode, int resultCode, Intent intent) { 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."); + } + // 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 = pluginManager.resolveUri(uri); + if (resolver == null && !fromWebView) { + resolver = UriResolvers.forUri(uri, cordova.getActivity()); + if (resolver == null) { + resolver = new UriResolvers.ErrorUriResolver(uri, "Unresolvable URI"); + } + } + + return resolver; + } } diff --git a/framework/src/org/apache/cordova/FileHelper.java b/framework/src/org/apache/cordova/FileHelper.java index ebbdc8df..0b2ba1c7 100644 --- a/framework/src/org/apache/cordova/FileHelper.java +++ b/framework/src/org/apache/cordova/FileHelper.java @@ -26,9 +26,12 @@ import org.apache.cordova.api.CordovaInterface; import org.apache.cordova.api.LOG; import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; -import java.net.URLConnection; +import java.io.OutputStream; +import java.nio.charset.Charset; import java.util.Locale; public class FileHelper { @@ -124,6 +127,20 @@ public class FileHelper { return uriString; } + public static String getMimeTypeForExtension(String path) { + String extension = path; + int lastDot = extension.lastIndexOf('.'); + if (lastDot != -1) { + extension = extension.substring(lastDot + 1); + } + // Convert the URI string to lower case to ensure compatibility with MimeTypeMap (see CB-2185). + extension = extension.toLowerCase(Locale.getDefault()); + if (extension.equals("3ga")) { + return "audio/3gpp"; + } + return MimeTypeMap.getSingleton().getMimeTypeFromExtension(extension); + } + /** * Returns the mime type of the data specified by the given URI string. * @@ -137,19 +154,7 @@ public class FileHelper { if (uriString.startsWith("content://")) { mimeType = cordova.getActivity().getContentResolver().getType(uri); } else { - // MimeTypeMap.getFileExtensionFromUrl() fails when there are query parameters. - String extension = uri.getPath(); - int lastDot = extension.lastIndexOf('.'); - if (lastDot != -1) { - extension = extension.substring(lastDot + 1); - } - // Convert the URI string to lower case to ensure compatibility with MimeTypeMap (see CB-2185). - extension = extension.toLowerCase(); - if (extension.equals("3ga")) { - mimeType = "audio/3gpp"; - } else { - mimeType = MimeTypeMap.getSingleton().getMimeTypeFromExtension(extension); - } + mimeType = getMimeTypeForExtension(uri.getPath()); } return mimeType; diff --git a/framework/src/org/apache/cordova/IceCreamCordovaWebViewClient.java b/framework/src/org/apache/cordova/IceCreamCordovaWebViewClient.java index 1e190b66..8527d35e 100644 --- a/framework/src/org/apache/cordova/IceCreamCordovaWebViewClient.java +++ b/framework/src/org/apache/cordova/IceCreamCordovaWebViewClient.java @@ -18,7 +18,6 @@ */ package org.apache.cordova; -import java.io.ByteArrayInputStream; import java.io.IOException; import java.io.InputStream; @@ -26,6 +25,7 @@ import org.apache.cordova.api.CordovaInterface; import org.apache.cordova.api.LOG; import android.annotation.TargetApi; +import android.net.Uri; import android.os.Build; import android.webkit.WebResourceResponse; import android.webkit.WebView; @@ -44,45 +44,29 @@ public class IceCreamCordovaWebViewClient extends CordovaWebViewClient { @Override public WebResourceResponse shouldInterceptRequest(WebView view, String url) { - //Check if plugins intercept the request - WebResourceResponse ret = super.shouldInterceptRequest(view, url); + UriResolver uriResolver = appView.resolveUri(Uri.parse(url), true); - if(!Config.isUrlWhiteListed(url) && (url.startsWith("http://") || url.startsWith("https://"))) - { - ret = getWhitelistResponse(); + if (uriResolver == null && url.startsWith("file:///android_asset/")) { + if (url.contains("?") || url.contains("#") || needsIceCreamSpecialsInAssetUrlFix(url)) { + uriResolver = appView.resolveUri(Uri.parse(url), false); + } } - else if(ret == null && (url.contains("?") || url.contains("#") || needsIceCreamSpecialsInAssetUrlFix(url))){ - ret = generateWebResourceResponse(url); - } - else if (ret == null && this.appView.pluginManager != null) { - ret = this.appView.pluginManager.shouldInterceptRequest(url); - } - return ret; - } - - private WebResourceResponse getWhitelistResponse() - { - WebResourceResponse emptyResponse; - String empty = ""; - ByteArrayInputStream data = new ByteArrayInputStream(empty.getBytes()); - return new WebResourceResponse("text/plain", "UTF-8", data); - } - - private WebResourceResponse generateWebResourceResponse(String url) { - if (url.startsWith("file:///android_asset/")) { - String mimetype = FileHelper.getMimeType(url, cordova); - + + if (uriResolver != null) { try { - InputStream stream = FileHelper.getInputStreamFromUriString(url, cordova); - WebResourceResponse response = new WebResourceResponse(mimetype, "UTF-8", stream); - return response; + 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("generateWebResourceResponse", e.getMessage(), 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; } - + private static boolean needsIceCreamSpecialsInAssetUrlFix(String url) { if (!url.contains("%20")){ return false; @@ -96,5 +80,4 @@ public class IceCreamCordovaWebViewClient extends CordovaWebViewClient { return false; } } - } diff --git a/framework/src/org/apache/cordova/UriResolver.java b/framework/src/org/apache/cordova/UriResolver.java new file mode 100644 index 00000000..42e9a3ad --- /dev/null +++ b/framework/src/org/apache/cordova/UriResolver.java @@ -0,0 +1,65 @@ +/* + 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; + +import android.net.Uri; + +/* + * Interface for a class that can resolve URIs. + * See CordovaUriResolver for an example. + */ +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. + * Never returns null. + */ + InputStream getInputStream() throws IOException; + + /** + * Returns the OutputStream for the resource. + * Throws an exception if it cannot be written to. + * Never returns null. + */ + OutputStream getOutputStream() 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). + */ + String getMimeType(); + + /** Returns whether the resource is writable. */ + boolean isWritable(); + + /** + * Returns a File that points to the resource, or null if the resource + * is not on the local file system. + */ + File getLocalFile(); +} diff --git a/framework/src/org/apache/cordova/UriResolvers.java b/framework/src/org/apache/cordova/UriResolvers.java new file mode 100644 index 00000000..e8be4071 --- /dev/null +++ b/framework/src/org/apache/cordova/UriResolvers.java @@ -0,0 +1,277 @@ +/* + 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; + +/* + * UriResolver implementations. + */ +public final class UriResolvers { + private UriResolvers() {} + + private static final class FileUriResolver implements UriResolver { + private final Uri uri; + private String mimeType; + private File localFile; + + FileUriResolver(Uri uri) { + this.uri = uri; + } + + public Uri getUri() { + return uri; + } + + public InputStream getInputStream() throws IOException { + return new FileInputStream(getLocalFile()); + } + + public OutputStream getOutputStream() throws FileNotFoundException { + return new FileOutputStream(getLocalFile()); + } + + public String getMimeType() { + if (mimeType == null) { + mimeType = FileHelper.getMimeTypeForExtension(getLocalFile().getName()); + } + return mimeType; + } + + public boolean isWritable() { + File f = getLocalFile(); + if (f.isDirectory()) { + return false; + } + if (f.exists()) { + return f.canWrite(); + } + return f.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); + } + + 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; + } + } + + private static final class ContentUriResolver implements UriResolver { + private final Uri uri; + private final ContentResolver contentResolver; + private String mimeType; + + ContentUriResolver(Uri uri, ContentResolver contentResolver) { + this.uri = uri; + this.contentResolver = contentResolver; + } + + public Uri getUri() { + return uri; + } + + public InputStream getInputStream() throws IOException { + return contentResolver.openInputStream(uri); + } + + 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; + } + } + + static final class ErrorUriResolver implements UriResolver { + final Uri uri; + final String errorMsg; + + ErrorUriResolver(Uri uri, String errorMsg) { + this.uri = uri; + 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; + } + } + + 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); + } + + /* 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; + } +} \ No newline at end of file diff --git a/framework/src/org/apache/cordova/api/CordovaPlugin.java b/framework/src/org/apache/cordova/api/CordovaPlugin.java index 2b225e64..07035e5f 100644 --- a/framework/src/org/apache/cordova/api/CordovaPlugin.java +++ b/framework/src/org/apache/cordova/api/CordovaPlugin.java @@ -20,14 +20,12 @@ package org.apache.cordova.api; import org.apache.cordova.CordovaArgs; import org.apache.cordova.CordovaWebView; +import org.apache.cordova.UriResolver; import org.json.JSONArray; import org.json.JSONException; -import android.annotation.TargetApi; import android.content.Intent; -import android.os.Build; -import android.util.Log; -import android.webkit.WebResourceResponse; +import android.net.Uri; /** * Plugins must extend this class and override one of the execute methods. @@ -165,13 +163,10 @@ public class CordovaPlugin { } /** - * By specifying a in config.xml you can map a URL prefix to this method. It applies to all resources loaded in the WebView, not just top-level navigation. - * - * @param url The URL of the resource to be loaded. - * @return Return a WebResourceResponse for the resource, or null to let the WebView handle it normally. + * Hook for overriding the default URI handling mechanism. + * Applies to WebView requests as well as requests made by plugins. */ - @TargetApi(Build.VERSION_CODES.HONEYCOMB) - public WebResourceResponse shouldInterceptRequest(String url) { + public UriResolver resolveUri(Uri uri) { return null; } diff --git a/framework/src/org/apache/cordova/api/PluginManager.java b/framework/src/org/apache/cordova/api/PluginManager.java index e392dfa2..083e882f 100755 --- a/framework/src/org/apache/cordova/api/PluginManager.java +++ b/framework/src/org/apache/cordova/api/PluginManager.java @@ -26,12 +26,14 @@ import java.util.concurrent.atomic.AtomicInteger; import org.apache.cordova.CordovaArgs; import org.apache.cordova.CordovaWebView; +import org.apache.cordova.UriResolver; import org.json.JSONException; import org.xmlpull.v1.XmlPullParserException; import android.content.Intent; import android.content.res.XmlResourceParser; +import android.net.Uri; import android.util.Log; import android.webkit.WebResourceResponse; @@ -379,25 +381,6 @@ public class PluginManager { return false; } - /** - * Called when the WebView is loading any resource, top-level or not. - * - * Uses the same url-filter tag as onOverrideUrlLoading. - * - * @param url The URL of the resource to be loaded. - * @return Return a WebResourceResponse with the resource, or null if the WebView should handle it. - */ - public WebResourceResponse shouldInterceptRequest(String url) { - Iterator> it = this.urlMap.entrySet().iterator(); - while (it.hasNext()) { - HashMap.Entry pairs = it.next(); - if (url.startsWith(pairs.getKey())) { - return this.getPlugin(pairs.getValue()).shouldInterceptRequest(url); - } - } - return null; - } - /** * Called when the app navigates or refreshes. */ @@ -419,6 +402,18 @@ public class PluginManager { LOG.e(TAG, "====================================================================================="); } + /* Should be package private */ public UriResolver resolveUri(Uri uri) { + for (PluginEntry entry : this.entries.values()) { + if (entry.plugin != null) { + UriResolver ret = entry.plugin.resolveUri(uri); + if (ret != null) { + return ret; + } + } + } + return null; + } + private class PluginManagerService extends CordovaPlugin { @Override public boolean execute(String action, CordovaArgs args, final CallbackContext callbackContext) throws JSONException { diff --git a/test/src/org/apache/cordova/test/UriResolversTest.java b/test/src/org/apache/cordova/test/UriResolversTest.java new file mode 100644 index 00000000..21584b9e --- /dev/null +++ b/test/src/org/apache/cordova/test/UriResolversTest.java @@ -0,0 +1,263 @@ + +package org.apache.cordova.test; + +/* + * + * 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. + * + */ + +import org.apache.cordova.CordovaWebView; +import org.apache.cordova.UriResolver; +import org.apache.cordova.UriResolvers; +import org.apache.cordova.api.CallbackContext; +import org.apache.cordova.api.CordovaPlugin; +import org.apache.cordova.api.PluginEntry; +import org.apache.cordova.test.actions.CordovaWebViewTestActivity; +import org.json.JSONArray; +import org.json.JSONException; + +import java.io.File; +import java.io.IOException; + +import android.graphics.Bitmap; +import android.graphics.BitmapFactory; +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 { + + public UriResolversTest() + { + super(CordovaWebViewTestActivity.class); + } + + CordovaWebView cordovaWebView; + private CordovaWebViewTestActivity activity; + String execPayload; + Integer execStatus; + + protected void setUp() throws Exception { + super.setUp(); + activity = this.getActivity(); + cordovaWebView = activity.cordovaWebView; + cordovaWebView.pluginManager.addService(new PluginEntry("UriResolverTestPlugin1", new CordovaPlugin() { + @Override + public UriResolver resolveUri(Uri uri) { + if ("plugin-uri".equals(uri.getScheme())) { + return cordovaWebView.resolveUri(uri.buildUpon().scheme("file").build()); + } + return null; + } + public boolean execute(String action, JSONArray args, CallbackContext callbackContext) throws JSONException { + synchronized (UriResolversTest.this) { + execPayload = args.getString(0); + execStatus = args.getInt(1); + UriResolversTest.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() { + Bitmap imageBitmap = BitmapFactory.decodeResource(activity.getResources(), R.drawable.icon); + String stored = MediaStore.Images.Media.insertImage(activity.getContentResolver(), + imageBitmap, "app-icon", "desc"); + return Uri.parse(stored); + } + + private void performResolverTest(Uri uri, String expectedMimeType, File expectedLocalFile, + boolean expectedIsWritable, + 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()); + } + try { + resolver.getInputStream().read(); + if (!expectRead) { + fail("Expected getInputStream to throw."); + } + } catch (IOException e) { + if (expectRead) { + throw e; + } + } + try { + resolver.getOutputStream().write(123); + if (!expectWrite) { + fail("Expected getOutputStream to throw."); + } + } catch (IOException e) { + if (expectWrite) { + throw e; + } + } + } + + public void testValidContentUri() throws IOException + { + Uri contentUri = createTestImageContentUri(); + performResolverTest(contentUri, "image/jpeg", null, true, true, true); + } + + public void testInvalidContentUri() throws IOException + { + Uri contentUri = Uri.parse("content://media/external/images/media/999999999"); + performResolverTest(contentUri, null, null, true, 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); + } + + public void testInvalidAssetUri() throws IOException + { + Uri assetUri = Uri.parse("file:///android_asset/www/missing.html"); + performResolverTest(assetUri, "text/html", null, false, false, false); + } + + public void testFileUriToExistingFile() throws IOException + { + 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); + } finally { + f.delete(); + } + } + + public void testFileUriToMissingFile() throws IOException + { + File f = new File(Environment.getExternalStorageDirectory() + "/somefilethatdoesntexist"); + Uri fileUri = Uri.parse(f.toURI().toString()); + try { + performResolverTest(fileUri, null, f, true, false, true); + } finally { + f.delete(); + } + } + + public void testFileUriToMissingFileWithMissingParent() throws IOException + { + File f = new File(Environment.getExternalStorageDirectory() + "/somedirthatismissing/somefilethatdoesntexist"); + Uri fileUri = Uri.parse(f.toURI().toString()); + performResolverTest(fileUri, null, f, false, false, false); + } + + public void testUnrecognizedUri() throws IOException + { + Uri uri = Uri.parse("somescheme://foo"); + performResolverTest(uri, null, null, false, false, false); + } + + public void testRelativeUri() + { + try { + cordovaWebView.resolveUri(Uri.parse("/foo")); + fail("Should have thrown for relative URI 1."); + } catch (Throwable t) { + } + try { + cordovaWebView.resolveUri(Uri.parse("//foo/bar")); + fail("Should have thrown for relative URI 2."); + } catch (Throwable t) { + } + try { + cordovaWebView.resolveUri(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 + { + Uri uri = Uri.parse("plugin-uri://foohost/android_asset/www/index.html?pluginRewrite=yes"); + performResolverTest(uri, "my/mime", null, false, true, false); + } + + public void testWhitelistRejection() throws IOException + { + Uri uri = Uri.parse("http://foohost.com/"); + performResolverTest(uri, null, null, false, false, false); + } + + public void testWebViewRequestIntercept() throws IOException + { + cordovaWebView.sendJavascript( + "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])"); + execPayload = null; + execStatus = null; + try { + synchronized (this) { + this.wait(2000); + } + } catch (InterruptedException e) { + } + assertEquals("pass", execPayload); + assertEquals(execStatus.intValue(), 200); + } + + public void testWebViewWhiteListRejection() throws IOException + { + cordovaWebView.sendJavascript( + "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])"); + execPayload = null; + execStatus = null; + try { + synchronized (this) { + this.wait(2000); + } + } catch (InterruptedException e) { + } + assertEquals("", execPayload); + assertEquals(execStatus.intValue(), 404); + } +} diff --git a/test/src/org/apache/cordova/test/actions/CordovaWebViewTestActivity.java b/test/src/org/apache/cordova/test/actions/CordovaWebViewTestActivity.java index 1185bad1..3be5ab21 100644 --- a/test/src/org/apache/cordova/test/actions/CordovaWebViewTestActivity.java +++ b/test/src/org/apache/cordova/test/actions/CordovaWebViewTestActivity.java @@ -36,7 +36,7 @@ import android.content.Intent; import android.os.Bundle; public class CordovaWebViewTestActivity extends Activity implements CordovaInterface { - CordovaWebView cordovaWebView; + public CordovaWebView cordovaWebView; private final ExecutorService threadPool = Executors.newCachedThreadPool();