android support! thanks to TachiWeb devs.
This commit is contained in:
168
AndroidCompat/src/main/java/android/webkit/MimeTypeMap.java
Normal file
168
AndroidCompat/src/main/java/android/webkit/MimeTypeMap.java
Normal file
@@ -0,0 +1,168 @@
|
||||
/*
|
||||
* Copyright (C) 2007 The Android Open Source Project
|
||||
*
|
||||
* Licensed 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 android.webkit;
|
||||
|
||||
import android.text.TextUtils;
|
||||
import libcore.net.MimeUtils;
|
||||
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
/**
|
||||
* Two-way map that maps MIME-types to file extensions and vice versa.
|
||||
*
|
||||
* <p>See also {@link java.net.URLConnection#guessContentTypeFromName}
|
||||
* and {@link java.net.URLConnection#guessContentTypeFromStream}. This
|
||||
* class and {@code URLConnection} share the same MIME-type database.
|
||||
*/
|
||||
public class MimeTypeMap {
|
||||
private static final MimeTypeMap sMimeTypeMap = new MimeTypeMap();
|
||||
|
||||
private MimeTypeMap() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the file extension or an empty string iff there is no
|
||||
* extension. This method is a convenience method for obtaining the
|
||||
* extension of a url and has undefined results for other Strings.
|
||||
* @param url
|
||||
* @return The file extension of the given url.
|
||||
*/
|
||||
public static String getFileExtensionFromUrl(String url) {
|
||||
if (!TextUtils.isEmpty(url)) {
|
||||
int fragment = url.lastIndexOf('#');
|
||||
if (fragment > 0) {
|
||||
url = url.substring(0, fragment);
|
||||
}
|
||||
|
||||
int query = url.lastIndexOf('?');
|
||||
if (query > 0) {
|
||||
url = url.substring(0, query);
|
||||
}
|
||||
|
||||
int filenamePos = url.lastIndexOf('/');
|
||||
String filename =
|
||||
0 <= filenamePos ? url.substring(filenamePos + 1) : url;
|
||||
|
||||
// if the filename contains special characters, we don't
|
||||
// consider it valid for our matching purposes:
|
||||
if (!filename.isEmpty() &&
|
||||
Pattern.matches("[a-zA-Z_0-9\\.\\-\\(\\)\\%]+", filename)) {
|
||||
int dotPos = filename.lastIndexOf('.');
|
||||
if (0 <= dotPos) {
|
||||
return filename.substring(dotPos + 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return "";
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if the given MIME type has an entry in the map.
|
||||
* @param mimeType A MIME type (i.e. text/plain)
|
||||
* @return True iff there is a mimeType entry in the map.
|
||||
*/
|
||||
public boolean hasMimeType(String mimeType) {
|
||||
return MimeUtils.hasMimeType(mimeType);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the MIME type for the given extension.
|
||||
* @param extension A file extension without the leading '.'
|
||||
* @return The MIME type for the given extension or null iff there is none.
|
||||
*/
|
||||
public String getMimeTypeFromExtension(String extension) {
|
||||
return MimeUtils.guessMimeTypeFromExtension(extension);
|
||||
}
|
||||
|
||||
// Static method called by jni.
|
||||
private static String mimeTypeFromExtension(String extension) {
|
||||
return MimeUtils.guessMimeTypeFromExtension(extension);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if the given extension has a registered MIME type.
|
||||
* @param extension A file extension without the leading '.'
|
||||
* @return True iff there is an extension entry in the map.
|
||||
*/
|
||||
public boolean hasExtension(String extension) {
|
||||
return MimeUtils.hasExtension(extension);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the registered extension for the given MIME type. Note that some
|
||||
* MIME types map to multiple extensions. This call will return the most
|
||||
* common extension for the given MIME type.
|
||||
* @param mimeType A MIME type (i.e. text/plain)
|
||||
* @return The extension for the given MIME type or null iff there is none.
|
||||
*/
|
||||
public String getExtensionFromMimeType(String mimeType) {
|
||||
return MimeUtils.guessExtensionFromMimeType(mimeType);
|
||||
}
|
||||
|
||||
/**
|
||||
* If the given MIME type is null, or one of the "generic" types (text/plain
|
||||
* or application/octet-stream) map it to a type that Android can deal with.
|
||||
* If the given type is not generic, return it unchanged.
|
||||
*
|
||||
* @param mimeType MIME type provided by the server.
|
||||
* @param url URL of the data being loaded.
|
||||
* @param contentDisposition Content-disposition header given by the server.
|
||||
* @return The MIME type that should be used for this data.
|
||||
*/
|
||||
/* package */ String remapGenericMimeType(String mimeType, String url,
|
||||
String contentDisposition) {
|
||||
// If we have one of "generic" MIME types, try to deduce
|
||||
// the right MIME type from the file extension (if any):
|
||||
if ("text/plain".equals(mimeType) ||
|
||||
"application/octet-stream".equals(mimeType)) {
|
||||
|
||||
// for attachment, use the filename in the Content-Disposition
|
||||
// to guess the mimetype
|
||||
String filename = null;
|
||||
if (contentDisposition != null) {
|
||||
filename = URLUtil.parseContentDisposition(contentDisposition);
|
||||
}
|
||||
if (filename != null) {
|
||||
url = filename;
|
||||
}
|
||||
String extension = getFileExtensionFromUrl(url);
|
||||
String newMimeType = getMimeTypeFromExtension(extension);
|
||||
if (newMimeType != null) {
|
||||
mimeType = newMimeType;
|
||||
}
|
||||
} else if ("text/vnd.wap.wml".equals(mimeType)) {
|
||||
// As we don't support wml, render it as plain text
|
||||
mimeType = "text/plain";
|
||||
} else {
|
||||
// It seems that xhtml+xml and vnd.wap.xhtml+xml mime
|
||||
// subtypes are used interchangeably. So treat them the same.
|
||||
if ("application/vnd.wap.xhtml+xml".equals(mimeType)) {
|
||||
mimeType = "application/xhtml+xml";
|
||||
}
|
||||
}
|
||||
return mimeType;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the singleton instance of MimeTypeMap.
|
||||
* @return The singleton instance of the MIME-type map.
|
||||
*/
|
||||
public static MimeTypeMap getSingleton() {
|
||||
return sMimeTypeMap;
|
||||
}
|
||||
}
|
||||
364
AndroidCompat/src/main/java/android/webkit/URLUtil.java
Normal file
364
AndroidCompat/src/main/java/android/webkit/URLUtil.java
Normal file
@@ -0,0 +1,364 @@
|
||||
/*
|
||||
* Copyright (C) 2006 The Android Open Source Project
|
||||
*
|
||||
* Licensed 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 android.webkit;
|
||||
import android.net.ParseException;
|
||||
import android.net.Uri;
|
||||
import android.net.WebAddress;
|
||||
import android.util.Log;
|
||||
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import java.util.Locale;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
public final class URLUtil {
|
||||
private static final String LOGTAG = "webkit";
|
||||
private static final boolean TRACE = false;
|
||||
// to refer to bar.png under your package's asset/foo/ directory, use
|
||||
// "file:///android_asset/foo/bar.png".
|
||||
static final String ASSET_BASE = "file:///android_asset/";
|
||||
// to refer to bar.png under your package's res/drawable/ directory, use
|
||||
// "file:///android_res/drawable/bar.png". Use "drawable" to refer to
|
||||
// "drawable-hdpi" directory as well.
|
||||
static final String RESOURCE_BASE = "file:///android_res/";
|
||||
static final String FILE_BASE = "file://";
|
||||
static final String PROXY_BASE = "file:///cookieless_proxy/";
|
||||
static final String CONTENT_BASE = "content:";
|
||||
/**
|
||||
* Cleans up (if possible) user-entered web addresses
|
||||
*/
|
||||
public static String guessUrl(String inUrl) {
|
||||
String retVal = inUrl;
|
||||
WebAddress webAddress;
|
||||
if (TRACE) Log.v(LOGTAG, "guessURL before queueRequest: " + inUrl);
|
||||
if (inUrl.length() == 0) return inUrl;
|
||||
if (inUrl.startsWith("about:")) return inUrl;
|
||||
// Do not try to interpret data scheme URLs
|
||||
if (inUrl.startsWith("data:")) return inUrl;
|
||||
// Do not try to interpret file scheme URLs
|
||||
if (inUrl.startsWith("file:")) return inUrl;
|
||||
// Do not try to interpret javascript scheme URLs
|
||||
if (inUrl.startsWith("javascript:")) return inUrl;
|
||||
// bug 762454: strip period off end of url
|
||||
if (inUrl.endsWith(".") == true) {
|
||||
inUrl = inUrl.substring(0, inUrl.length() - 1);
|
||||
}
|
||||
try {
|
||||
webAddress = new WebAddress(inUrl);
|
||||
} catch (ParseException ex) {
|
||||
if (TRACE) {
|
||||
Log.v(LOGTAG, "smartUrlFilter: failed to parse url = " + inUrl);
|
||||
}
|
||||
return retVal;
|
||||
}
|
||||
// Check host
|
||||
if (webAddress.getHost().indexOf('.') == -1) {
|
||||
// no dot: user probably entered a bare domain. try .com
|
||||
webAddress.setHost("www." + webAddress.getHost() + ".com");
|
||||
}
|
||||
return webAddress.toString();
|
||||
}
|
||||
public static String composeSearchUrl(String inQuery, String template,
|
||||
String queryPlaceHolder) {
|
||||
int placeHolderIndex = template.indexOf(queryPlaceHolder);
|
||||
if (placeHolderIndex < 0) {
|
||||
return null;
|
||||
}
|
||||
String query;
|
||||
StringBuilder buffer = new StringBuilder();
|
||||
buffer.append(template.substring(0, placeHolderIndex));
|
||||
try {
|
||||
query = java.net.URLEncoder.encode(inQuery, "utf-8");
|
||||
buffer.append(query);
|
||||
} catch (UnsupportedEncodingException ex) {
|
||||
return null;
|
||||
}
|
||||
buffer.append(template.substring(
|
||||
placeHolderIndex + queryPlaceHolder.length()));
|
||||
return buffer.toString();
|
||||
}
|
||||
public static byte[] decode(byte[] url) throws IllegalArgumentException {
|
||||
if (url.length == 0) {
|
||||
return new byte[0];
|
||||
}
|
||||
// Create a new byte array with the same length to ensure capacity
|
||||
byte[] tempData = new byte[url.length];
|
||||
int tempCount = 0;
|
||||
for (int i = 0; i < url.length; i++) {
|
||||
byte b = url[i];
|
||||
if (b == '%') {
|
||||
if (url.length - i > 2) {
|
||||
b = (byte) (parseHex(url[i + 1]) * 16
|
||||
+ parseHex(url[i + 2]));
|
||||
i += 2;
|
||||
} else {
|
||||
throw new IllegalArgumentException("Invalid format");
|
||||
}
|
||||
}
|
||||
tempData[tempCount++] = b;
|
||||
}
|
||||
byte[] retData = new byte[tempCount];
|
||||
System.arraycopy(tempData, 0, retData, 0, tempCount);
|
||||
return retData;
|
||||
}
|
||||
/**
|
||||
* @return True iff the url is correctly URL encoded
|
||||
*/
|
||||
static boolean verifyURLEncoding(String url) {
|
||||
int count = url.length();
|
||||
if (count == 0) {
|
||||
return false;
|
||||
}
|
||||
int index = url.indexOf('%');
|
||||
while (index >= 0 && index < count) {
|
||||
if (index < count - 2) {
|
||||
try {
|
||||
parseHex((byte) url.charAt(++index));
|
||||
parseHex((byte) url.charAt(++index));
|
||||
} catch (IllegalArgumentException e) {
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
index = url.indexOf('%', index + 1);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
private static int parseHex(byte b) {
|
||||
if (b >= '0' && b <= '9') return (b - '0');
|
||||
if (b >= 'A' && b <= 'F') return (b - 'A' + 10);
|
||||
if (b >= 'a' && b <= 'f') return (b - 'a' + 10);
|
||||
throw new IllegalArgumentException("Invalid hex char '" + b + "'");
|
||||
}
|
||||
/**
|
||||
* @return True iff the url is an asset file.
|
||||
*/
|
||||
public static boolean isAssetUrl(String url) {
|
||||
return (null != url) && url.startsWith(ASSET_BASE);
|
||||
}
|
||||
/**
|
||||
* @return True iff the url is a resource file.
|
||||
* @hide
|
||||
*/
|
||||
public static boolean isResourceUrl(String url) {
|
||||
return (null != url) && url.startsWith(RESOURCE_BASE);
|
||||
}
|
||||
/**
|
||||
* @return True iff the url is a proxy url to allow cookieless network
|
||||
* requests from a file url.
|
||||
* @deprecated Cookieless proxy is no longer supported.
|
||||
*/
|
||||
@Deprecated
|
||||
public static boolean isCookielessProxyUrl(String url) {
|
||||
return (null != url) && url.startsWith(PROXY_BASE);
|
||||
}
|
||||
/**
|
||||
* @return True iff the url is a local file.
|
||||
*/
|
||||
public static boolean isFileUrl(String url) {
|
||||
return (null != url) && (url.startsWith(FILE_BASE) &&
|
||||
!url.startsWith(ASSET_BASE) &&
|
||||
!url.startsWith(PROXY_BASE));
|
||||
}
|
||||
/**
|
||||
* @return True iff the url is an about: url.
|
||||
*/
|
||||
public static boolean isAboutUrl(String url) {
|
||||
return (null != url) && url.startsWith("about:");
|
||||
}
|
||||
/**
|
||||
* @return True iff the url is a data: url.
|
||||
*/
|
||||
public static boolean isDataUrl(String url) {
|
||||
return (null != url) && url.startsWith("data:");
|
||||
}
|
||||
/**
|
||||
* @return True iff the url is a javascript: url.
|
||||
*/
|
||||
public static boolean isJavaScriptUrl(String url) {
|
||||
return (null != url) && url.startsWith("javascript:");
|
||||
}
|
||||
/**
|
||||
* @return True iff the url is an http: url.
|
||||
*/
|
||||
public static boolean isHttpUrl(String url) {
|
||||
return (null != url) &&
|
||||
(url.length() > 6) &&
|
||||
url.substring(0, 7).equalsIgnoreCase("http://");
|
||||
}
|
||||
/**
|
||||
* @return True iff the url is an https: url.
|
||||
*/
|
||||
public static boolean isHttpsUrl(String url) {
|
||||
return (null != url) &&
|
||||
(url.length() > 7) &&
|
||||
url.substring(0, 8).equalsIgnoreCase("https://");
|
||||
}
|
||||
/**
|
||||
* @return True iff the url is a network url.
|
||||
*/
|
||||
public static boolean isNetworkUrl(String url) {
|
||||
if (url == null || url.length() == 0) {
|
||||
return false;
|
||||
}
|
||||
return isHttpUrl(url) || isHttpsUrl(url);
|
||||
}
|
||||
/**
|
||||
* @return True iff the url is a content: url.
|
||||
*/
|
||||
public static boolean isContentUrl(String url) {
|
||||
return (null != url) && url.startsWith(CONTENT_BASE);
|
||||
}
|
||||
/**
|
||||
* @return True iff the url is valid.
|
||||
*/
|
||||
public static boolean isValidUrl(String url) {
|
||||
if (url == null || url.length() == 0) {
|
||||
return false;
|
||||
}
|
||||
return (isAssetUrl(url) ||
|
||||
isResourceUrl(url) ||
|
||||
isFileUrl(url) ||
|
||||
isAboutUrl(url) ||
|
||||
isHttpUrl(url) ||
|
||||
isHttpsUrl(url) ||
|
||||
isJavaScriptUrl(url) ||
|
||||
isContentUrl(url));
|
||||
}
|
||||
/**
|
||||
* Strips the url of the anchor.
|
||||
*/
|
||||
public static String stripAnchor(String url) {
|
||||
int anchorIndex = url.indexOf('#');
|
||||
if (anchorIndex != -1) {
|
||||
return url.substring(0, anchorIndex);
|
||||
}
|
||||
return url;
|
||||
}
|
||||
/**
|
||||
* Guesses canonical filename that a download would have, using
|
||||
* the URL and contentDisposition. File extension, if not defined,
|
||||
* is added based on the mimetype
|
||||
* @param url Url to the content
|
||||
* @param contentDisposition Content-Disposition HTTP header or null
|
||||
* @param mimeType Mime-type of the content or null
|
||||
*
|
||||
* @return suggested filename
|
||||
*/
|
||||
public static final String guessFileName(
|
||||
String url,
|
||||
String contentDisposition,
|
||||
String mimeType) {
|
||||
String filename = null;
|
||||
String extension = null;
|
||||
// If we couldn't do anything with the hint, move toward the content disposition
|
||||
if (filename == null && contentDisposition != null) {
|
||||
filename = parseContentDisposition(contentDisposition);
|
||||
if (filename != null) {
|
||||
int index = filename.lastIndexOf('/') + 1;
|
||||
if (index > 0) {
|
||||
filename = filename.substring(index);
|
||||
}
|
||||
}
|
||||
}
|
||||
// If all the other http-related approaches failed, use the plain uri
|
||||
if (filename == null) {
|
||||
String decodedUrl = Uri.decode(url);
|
||||
if (decodedUrl != null) {
|
||||
int queryIndex = decodedUrl.indexOf('?');
|
||||
// If there is a query string strip it, same as desktop browsers
|
||||
if (queryIndex > 0) {
|
||||
decodedUrl = decodedUrl.substring(0, queryIndex);
|
||||
}
|
||||
if (!decodedUrl.endsWith("/")) {
|
||||
int index = decodedUrl.lastIndexOf('/') + 1;
|
||||
if (index > 0) {
|
||||
filename = decodedUrl.substring(index);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// Finally, if couldn't get filename from URI, get a generic filename
|
||||
if (filename == null) {
|
||||
filename = "downloadfile";
|
||||
}
|
||||
// Split filename between base and extension
|
||||
// Add an extension if filename does not have one
|
||||
int dotIndex = filename.indexOf('.');
|
||||
if (dotIndex < 0) {
|
||||
if (mimeType != null) {
|
||||
extension = MimeTypeMap.getSingleton().getExtensionFromMimeType(mimeType);
|
||||
if (extension != null) {
|
||||
extension = "." + extension;
|
||||
}
|
||||
}
|
||||
if (extension == null) {
|
||||
if (mimeType != null && mimeType.toLowerCase(Locale.ROOT).startsWith("text/")) {
|
||||
if (mimeType.equalsIgnoreCase("text/html")) {
|
||||
extension = ".html";
|
||||
} else {
|
||||
extension = ".txt";
|
||||
}
|
||||
} else {
|
||||
extension = ".bin";
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (mimeType != null) {
|
||||
// Compare the last segment of the extension against the mime type.
|
||||
// If there's a mismatch, discard the entire extension.
|
||||
int lastDotIndex = filename.lastIndexOf('.');
|
||||
String typeFromExt = MimeTypeMap.getSingleton().getMimeTypeFromExtension(
|
||||
filename.substring(lastDotIndex + 1));
|
||||
if (typeFromExt != null && !typeFromExt.equalsIgnoreCase(mimeType)) {
|
||||
extension = MimeTypeMap.getSingleton().getExtensionFromMimeType(mimeType);
|
||||
if (extension != null) {
|
||||
extension = "." + extension;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (extension == null) {
|
||||
extension = filename.substring(dotIndex);
|
||||
}
|
||||
filename = filename.substring(0, dotIndex);
|
||||
}
|
||||
return filename + extension;
|
||||
}
|
||||
/** Regex used to parse content-disposition headers */
|
||||
private static final Pattern CONTENT_DISPOSITION_PATTERN =
|
||||
Pattern.compile("attachment;\\s*filename\\s*=\\s*(\"?)([^\"]*)\\1\\s*$",
|
||||
Pattern.CASE_INSENSITIVE);
|
||||
/*
|
||||
* Parse the Content-Disposition HTTP Header. The format of the header
|
||||
* is defined here: http://www.w3.org/Protocols/rfc2616/rfc2616-sec19.html
|
||||
* This header provides a filename for content that is going to be
|
||||
* downloaded to the file system. We only support the attachment type.
|
||||
* Note that RFC 2616 specifies the filename value must be double-quoted.
|
||||
* Unfortunately some servers do not quote the value so to maintain
|
||||
* consistent behaviour with other browsers, we allow unquoted values too.
|
||||
*/
|
||||
static String parseContentDisposition(String contentDisposition) {
|
||||
try {
|
||||
Matcher m = CONTENT_DISPOSITION_PATTERN.matcher(contentDisposition);
|
||||
if (m.find()) {
|
||||
return m.group(2);
|
||||
}
|
||||
} catch (IllegalStateException ex) {
|
||||
// This function is defined as returning null when it can't parse the header
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user