Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(692)

Unified Diff: chrome/android/java/src/org/chromium/chrome/browser/AndroidProtocolAdapter.java

Issue 10832034: Upstreaming AndroidProtocolAdapter. (Closed) Base URL: http://git.chromium.org/chromium/src.git@master
Patch Set: Created 8 years, 5 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
« no previous file with comments | « build/jar_file_jni_generator.gypi ('k') | chrome/browser/android/android_protocol_adapter.h » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: chrome/android/java/src/org/chromium/chrome/browser/AndroidProtocolAdapter.java
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/AndroidProtocolAdapter.java b/chrome/android/java/src/org/chromium/chrome/browser/AndroidProtocolAdapter.java
new file mode 100644
index 0000000000000000000000000000000000000000..d9134a7fe5038e8a3060a39391c848c46bb3e80a
--- /dev/null
+++ b/chrome/android/java/src/org/chromium/chrome/browser/AndroidProtocolAdapter.java
@@ -0,0 +1,233 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.chrome.browser;
+
+import android.content.Context;
+import android.content.res.AssetManager;
+import android.net.Uri;
+import android.util.Log;
+import android.util.TypedValue;
+
+import java.io.InputStream;
+import java.io.IOException;
+import java.net.URLConnection;
+import java.util.List;
+
+import org.chromium.base.CalledByNativeUnchecked;
+
+/**
+ * Implements the Java side of Android URL protocol jobs.
+ * See android_protocol_adapter.cc.
+ */
+public class AndroidProtocolAdapter {
+ private static final String TAG = "AndroidProtocolAdapter";
+
+ // Supported URL schemes. This needs to be kept in sync with
+ // clank/native/framework/chrome/url_request_android_job.cc.
+ private static final String FILE_SCHEME = "file";
+ private static final String CONTENT_SCHEME = "content";
+
+ /**
+ * Open an InputStream for an Android resource.
+ * @param context The context manager.
+ * @param url The url to load.
+ * @return An InputStream to the Android resource.
+ */
+ // TODO(bulach): this should have either a throw clause, or
+ // handle the exception in the java side rather than the native side.
+ @CalledByNativeUnchecked
+ public static InputStream open(Context context, String url) {
+ Uri uri = verifyUrl(url);
+ if (uri == null) {
+ return null;
+ }
+ String path = uri.getPath();
+ if (uri.getScheme().equals(FILE_SCHEME)) {
+ if (path.startsWith(nativeGetAndroidAssetPath())) {
+ return openAsset(context, uri);
+ } else if (path.startsWith(nativeGetAndroidResourcePath())) {
+ return openResource(context, uri);
+ }
+ } else if (uri.getScheme().equals(CONTENT_SCHEME)) {
+ return openContent(context, uri);
+ }
+ return null;
+ }
+
+ private static int getFieldId(Context context, String assetType, String assetName)
+ throws ClassNotFoundException, NoSuchFieldException, IllegalAccessException {
+ Class<?> d = context.getClassLoader()
+ .loadClass(context.getPackageName() + ".R$" + assetType);
+ java.lang.reflect.Field field = d.getField(assetName);
+ int id = field.getInt(null);
+ return id;
+ }
+
+ private static int getValueType(Context context, int field_id) {
+ TypedValue value = new TypedValue();
+ context.getResources().getValue(field_id, value, true);
+ return value.type;
+ }
+
+ private static InputStream openResource(Context context, Uri uri) {
+ assert(uri.getScheme().equals(FILE_SCHEME));
+ assert(uri.getPath() != null);
+ assert(uri.getPath().startsWith(nativeGetAndroidResourcePath()));
+ // The path must be of the form "/android_res/asset_type/asset_name.ext".
+ List<String> pathSegments = uri.getPathSegments();
+ if (pathSegments.size() != 3) {
+ Log.e(TAG, "Incorrect resource path: " + uri);
+ return null;
+ }
+ String assetPath = pathSegments.get(0);
+ String assetType = pathSegments.get(1);
+ String assetName = pathSegments.get(2);
+ if (!("/" + assetPath + "/").equals(nativeGetAndroidResourcePath())) {
+ Log.e(TAG, "Resource path does not start with " + nativeGetAndroidResourcePath() +
+ ": " + uri);
+ return null;
+ }
+ // Drop the file extension.
+ assetName = assetName.split("\\.")[0];
+ try {
+ // Use the application context for resolving the resource package name so that we do
+ // not use the browser's own resources. Note that if 'context' here belongs to the
+ // test suite, it does not have a separate application context. In that case we use
+ // the original context object directly.
+ if (context.getApplicationContext() != null) {
+ context = context.getApplicationContext();
+ }
+ int field_id = getFieldId(context, assetType, assetName);
+ int value_type = getValueType(context, field_id);
+ if (value_type == TypedValue.TYPE_STRING) {
+ return context.getResources().openRawResource(field_id);
+ } else {
+ Log.e(TAG, "Asset not of type string: " + uri);
+ return null;
+ }
+ } catch (ClassNotFoundException e) {
+ Log.e(TAG, "Unable to open resource URL: " + uri, e);
+ return null;
+ } catch (NoSuchFieldException e) {
+ Log.e(TAG, "Unable to open resource URL: " + uri, e);
+ return null;
+ } catch (IllegalAccessException e) {
+ Log.e(TAG, "Unable to open resource URL: " + uri, e);
+ return null;
+ }
+ }
+
+ private static InputStream openAsset(Context context, Uri uri) {
+ assert(uri.getScheme().equals(FILE_SCHEME));
+ assert(uri.getPath() != null);
+ assert(uri.getPath().startsWith(nativeGetAndroidAssetPath()));
+ String path = uri.getPath().replaceFirst(nativeGetAndroidAssetPath(), "");
+ try {
+ AssetManager assets = context.getAssets();
+ return assets.open(path, AssetManager.ACCESS_STREAMING);
+ } catch (IOException e) {
+ Log.e(TAG, "Unable to open asset URL: " + uri);
+ return null;
+ }
+ }
+
+ private static InputStream openContent(Context context, Uri uri) {
+ assert(uri.getScheme().equals(CONTENT_SCHEME));
+ try {
+ // We strip the query parameters before opening the stream to
+ // ensure that the URL we try to load exactly matches the URL
+ // we have permission to read.
+ Uri baseUri = stripQueryParameters(uri);
+ return context.getContentResolver().openInputStream(baseUri);
+ } catch (Exception e) {
+ Log.e(TAG, "Unable to open content URL: " + uri);
+ return null;
+ }
+ }
+
+ /**
+ * Determine the mime type for an Android resource.
+ * @param context The context manager.
+ * @param stream The opened input stream which to examine.
+ * @param url The url from which the stream was opened.
+ * @return The mime type or null if the type is unknown.
+ */
+ // TODO(bulach): this should have either a throw clause, or
+ // handle the exception in the java side rather than the native side.
+ @CalledByNativeUnchecked
+ public static String getMimeType(Context context, InputStream stream, String url) {
+ Uri uri = verifyUrl(url);
+ if (uri == null) {
+ return null;
+ }
+ String path = uri.getPath();
+ // The content URL type can be queried directly.
+ if (uri.getScheme().equals(CONTENT_SCHEME)) {
+ return context.getContentResolver().getType(uri);
+ // Asset files may have a known extension.
+ } else if (uri.getScheme().equals(FILE_SCHEME) &&
+ path.startsWith(nativeGetAndroidAssetPath())) {
+ String mimeType = URLConnection.guessContentTypeFromName(path);
+ if (mimeType != null) {
+ return mimeType;
+ }
+ }
+ // Fall back to sniffing the type from the stream.
+ try {
+ return URLConnection.guessContentTypeFromStream(stream);
+ } catch (IOException e) {
+ return null;
+ }
+ }
+
+ /**
+ * Make sure the given string URL is correctly formed and parse it into a Uri.
+ * @return a Uri instance, or null if the URL was invalid.
+ */
+ private static Uri verifyUrl(String url) {
+ if (url == null) {
+ return null;
+ }
+ Uri uri = Uri.parse(url);
+ if (uri == null) {
+ Log.e(TAG, "Malformed URL: " + url);
+ return null;
+ }
+ String path = uri.getPath();
+ if (path == null || path.length() == 0) {
+ Log.e(TAG, "URL does not have a path: " + url);
+ return null;
+ }
+ return uri;
+ }
+
+ /**
+ * Remove query parameters from a Uri.
+ * @param uri The input uri.
+ * @return The given uri without query parameters.
+ */
+ private static Uri stripQueryParameters(Uri uri) {
+ assert(uri.getAuthority() != null);
+ assert(uri.getPath() != null);
+ Uri.Builder builder = new Uri.Builder();
+ builder.scheme(uri.getScheme());
+ builder.encodedAuthority(uri.getAuthority());
+ builder.encodedPath(uri.getPath());
+ return builder.build();
+ }
+
+ /**
+ * Set the context to be used for resolving resource queries.
+ * @param context Context to be used, or null for the default application
+ * context.
+ */
+ public static void setResourceContextForTesting(Context context) {
+ nativeSetResourceContextForTesting(context);
+ }
+
+ private static native void nativeSetResourceContextForTesting(Context context);
+ private static native String nativeGetAndroidAssetPath();
+ private static native String nativeGetAndroidResourcePath();
+}
« no previous file with comments | « build/jar_file_jni_generator.gypi ('k') | chrome/browser/android/android_protocol_adapter.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698