Skip to content

Commit

Permalink
[CB-3384] Rewrite of DataResource into UriResolver + UriResolvers
Browse files Browse the repository at this point in the history
Includes unit tests woot!

Note that this remove CordovaPlugin.shouldInterceptRequest(). Should be
fine since this method was introduced only a couple of releases ago, was
never documented, and afaict was only used by the Chrome Cordova plugins.
  • Loading branch information
agrieve committed Jun 28, 2013
1 parent fbf7f1c commit 892ffc8
Show file tree
Hide file tree
Showing 9 changed files with 694 additions and 77 deletions.
34 changes: 34 additions & 0 deletions framework/src/org/apache/cordova/CordovaWebView.java
Original file line number Diff line number Diff line change
Expand Up @@ -943,4 +943,38 @@ public WebBackForwardList restoreState(Bundle savedInstanceState)
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;
}
}
33 changes: 19 additions & 14 deletions framework/src/org/apache/cordova/FileHelper.java
Original file line number Diff line number Diff line change
Expand Up @@ -26,9 +26,12 @@ Licensed to the Apache Software Foundation (ASF) under one
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 {
Expand Down Expand Up @@ -124,6 +127,20 @@ public static String stripFileProtocol(String uriString) {
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.
*
Expand All @@ -137,19 +154,7 @@ public static String getMimeType(String uriString, CordovaInterface cordova) {
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;
Expand Down
49 changes: 16 additions & 33 deletions framework/src/org/apache/cordova/IceCreamCordovaWebViewClient.java
Original file line number Diff line number Diff line change
Expand Up @@ -18,14 +18,14 @@ Licensed to the Apache Software Foundation (ASF) under one
*/
package org.apache.cordova;

import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;

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;
Expand All @@ -44,45 +44,29 @@ public IceCreamCordovaWebViewClient(CordovaInterface cordova, CordovaWebView vie

@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();
}
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);
if (uriResolver == null && url.startsWith("file:///android_asset/")) {
if (url.contains("?") || url.contains("#") || needsIceCreamSpecialsInAssetUrlFix(url)) {
uriResolver = appView.resolveUri(Uri.parse(url), false);
}
}
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;
Expand All @@ -96,5 +80,4 @@ private static boolean needsIceCreamSpecialsInAssetUrlFix(String url) {
return false;
}
}

}
65 changes: 65 additions & 0 deletions framework/src/org/apache/cordova/UriResolver.java
Original file line number Diff line number Diff line change
@@ -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();
}
Loading

0 comments on commit 892ffc8

Please sign in to comment.