EMMA Coverage Report (generated Fri Aug 23 16:39:17 PDT 2013)
[all classes][org.chromium.android_webview]

COVERAGE SUMMARY FOR SOURCE FILE [AndroidProtocolHandler.java]

nameclass, %method, %block, %line, %
AndroidProtocolHandler.java100% (1/1)83%  (10/12)66%  (329/496)68%  (68.1/100)

COVERAGE BREAKDOWN BY CLASS AND METHOD

nameclass, %method, %block, %line, %
     
class AndroidProtocolHandler100% (1/1)83%  (10/12)66%  (329/496)68%  (68.1/100)
AndroidProtocolHandler (): void 0%   (0/1)0%   (0/3)0%   (0/1)
setResourceContextForTesting (Context): void 0%   (0/1)0%   (0/3)0%   (0/2)
verifyUrl (String): Uri 100% (1/1)38%  (17/45)55%  (6/11)
openResource (Context, Uri): InputStream 100% (1/1)54%  (98/183)57%  (17.8/31)
openContent (Context, Uri): InputStream 100% (1/1)64%  (21/33)60%  (3.6/6)
<static initializer> 100% (1/1)75%  (6/8)75%  (0.8/1)
getMimeType (Context, InputStream, String): String 100% (1/1)76%  (32/42)69%  (9/13)
openAsset (Context, Uri): InputStream 100% (1/1)80%  (47/59)87%  (7.8/9)
stripQueryParameters (Uri): Uri 100% (1/1)80%  (32/40)87%  (6.1/7)
open (Context, String): InputStream 100% (1/1)90%  (38/42)83%  (10/12)
getFieldId (Context, String, String): int 100% (1/1)100% (25/25)100% (4/4)
getValueType (Context, int): int 100% (1/1)100% (13/13)100% (3/3)

1// Copyright (c) 2012 The Chromium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4 
5package org.chromium.android_webview;
6 
7import android.content.Context;
8import android.content.res.AssetManager;
9import android.net.Uri;
10import android.util.Log;
11import android.util.TypedValue;
12 
13import java.io.InputStream;
14import java.io.IOException;
15import java.net.URLConnection;
16import java.util.List;
17 
18import org.chromium.base.CalledByNativeUnchecked;
19import org.chromium.base.JNINamespace;
20 
21/**
22 * Implements the Java side of Android URL protocol jobs.
23 * See android_protocol_handler.cc.
24 */
25@JNINamespace("android_webview")
26public class AndroidProtocolHandler {
27    private static final String TAG = "AndroidProtocolHandler";
28 
29    // Supported URL schemes. This needs to be kept in sync with
30    // clank/native/framework/chrome/url_request_android_job.cc.
31    private static final String FILE_SCHEME = "file";
32    private static final String CONTENT_SCHEME = "content";
33 
34    /**
35     * Open an InputStream for an Android resource.
36     * @param context The context manager.
37     * @param url The url to load.
38     * @return An InputStream to the Android resource.
39     */
40    // TODO(bulach): this should have either a throw clause, or
41    // handle the exception in the java side rather than the native side.
42    @CalledByNativeUnchecked
43    public static InputStream open(Context context, String url) {
44        Uri uri = verifyUrl(url);
45        if (uri == null) {
46            return null;
47        }
48        String path = uri.getPath();
49        if (uri.getScheme().equals(FILE_SCHEME)) {
50            if (path.startsWith(nativeGetAndroidAssetPath())) {
51                return openAsset(context, uri);
52            } else if (path.startsWith(nativeGetAndroidResourcePath())) {
53                return openResource(context, uri);
54            }
55        } else if (uri.getScheme().equals(CONTENT_SCHEME)) {
56            return openContent(context, uri);
57        }
58        return null;
59    }
60 
61    private static int getFieldId(Context context, String assetType, String assetName)
62        throws ClassNotFoundException, NoSuchFieldException, IllegalAccessException {
63        Class<?> d = context.getClassLoader()
64            .loadClass(context.getPackageName() + ".R$" + assetType);
65        java.lang.reflect.Field field = d.getField(assetName);
66        int id = field.getInt(null);
67        return id;
68    }
69 
70    private static int getValueType(Context context, int field_id) {
71      TypedValue value = new TypedValue();
72      context.getResources().getValue(field_id, value, true);
73      return value.type;
74    }
75 
76    private static InputStream openResource(Context context, Uri uri) {
77        assert(uri.getScheme().equals(FILE_SCHEME));
78        assert(uri.getPath() != null);
79        assert(uri.getPath().startsWith(nativeGetAndroidResourcePath()));
80        // The path must be of the form "/android_res/asset_type/asset_name.ext".
81        List<String> pathSegments = uri.getPathSegments();
82        if (pathSegments.size() != 3) {
83            Log.e(TAG, "Incorrect resource path: " + uri);
84            return null;
85        }
86        String assetPath = pathSegments.get(0);
87        String assetType = pathSegments.get(1);
88        String assetName = pathSegments.get(2);
89        if (!("/" + assetPath + "/").equals(nativeGetAndroidResourcePath())) {
90            Log.e(TAG, "Resource path does not start with " + nativeGetAndroidResourcePath() +
91                  ": " + uri);
92            return null;
93        }
94        // Drop the file extension.
95        assetName = assetName.split("\\.")[0];
96        try {
97            // Use the application context for resolving the resource package name so that we do
98            // not use the browser's own resources. Note that if 'context' here belongs to the
99            // test suite, it does not have a separate application context. In that case we use
100            // the original context object directly.
101            if (context.getApplicationContext() != null) {
102                context = context.getApplicationContext();
103            }
104            int field_id = getFieldId(context, assetType, assetName);
105            int value_type = getValueType(context, field_id);
106            if (value_type == TypedValue.TYPE_STRING) {
107                return context.getResources().openRawResource(field_id);
108            } else {
109                Log.e(TAG, "Asset not of type string: " + uri);
110                return null;
111            }
112        } catch (ClassNotFoundException e) {
113            Log.e(TAG, "Unable to open resource URL: " + uri, e);
114            return null;
115        } catch (NoSuchFieldException e) {
116            Log.e(TAG, "Unable to open resource URL: " + uri, e);
117            return null;
118        } catch (IllegalAccessException e) {
119            Log.e(TAG, "Unable to open resource URL: " + uri, e);
120            return null;
121        }
122    }
123 
124    private static InputStream openAsset(Context context, Uri uri) {
125        assert(uri.getScheme().equals(FILE_SCHEME));
126        assert(uri.getPath() != null);
127        assert(uri.getPath().startsWith(nativeGetAndroidAssetPath()));
128        String path = uri.getPath().replaceFirst(nativeGetAndroidAssetPath(), "");
129        try {
130            AssetManager assets = context.getAssets();
131            return assets.open(path, AssetManager.ACCESS_STREAMING);
132        } catch (IOException e) {
133            Log.e(TAG, "Unable to open asset URL: " + uri);
134            return null;
135        }
136    }
137 
138    private static InputStream openContent(Context context, Uri uri) {
139        assert(uri.getScheme().equals(CONTENT_SCHEME));
140        try {
141            // We strip the query parameters before opening the stream to
142            // ensure that the URL we try to load exactly matches the URL
143            // we have permission to read.
144            Uri baseUri = stripQueryParameters(uri);
145            return context.getContentResolver().openInputStream(baseUri);
146        } catch (Exception e) {
147            Log.e(TAG, "Unable to open content URL: " + uri);
148            return null;
149        }
150    }
151 
152    /**
153     * Determine the mime type for an Android resource.
154     * @param context The context manager.
155     * @param stream The opened input stream which to examine.
156     * @param url The url from which the stream was opened.
157     * @return The mime type or null if the type is unknown.
158     */
159    // TODO(bulach): this should have either a throw clause, or
160    // handle the exception in the java side rather than the native side.
161    @CalledByNativeUnchecked
162    public static String getMimeType(Context context, InputStream stream, String url) {
163        Uri uri = verifyUrl(url);
164        if (uri == null) {
165            return null;
166        }
167        String path = uri.getPath();
168        // The content URL type can be queried directly.
169        if (uri.getScheme().equals(CONTENT_SCHEME)) {
170            return context.getContentResolver().getType(uri);
171        // Asset files may have a known extension.
172        } else if (uri.getScheme().equals(FILE_SCHEME) &&
173                   path.startsWith(nativeGetAndroidAssetPath())) {
174            String mimeType = URLConnection.guessContentTypeFromName(path);
175            if (mimeType != null) {
176                return mimeType;
177            }
178        }
179        // Fall back to sniffing the type from the stream.
180        try {
181            return URLConnection.guessContentTypeFromStream(stream);
182        } catch (IOException e) {
183            return null;
184        }
185    }
186 
187    /**
188     * Make sure the given string URL is correctly formed and parse it into a Uri.
189     * @return a Uri instance, or null if the URL was invalid.
190     */
191    private static Uri verifyUrl(String url) {
192        if (url == null) {
193            return null;
194        }
195        Uri uri = Uri.parse(url);
196        if (uri == null) {
197            Log.e(TAG, "Malformed URL: " + url);
198            return null;
199        }
200        String path = uri.getPath();
201        if (path == null || path.length() == 0) {
202            Log.e(TAG, "URL does not have a path: " + url);
203            return null;
204        }
205        return uri;
206    }
207 
208    /**
209     * Remove query parameters from a Uri.
210     * @param uri The input uri.
211     * @return The given uri without query parameters.
212     */
213    private static Uri stripQueryParameters(Uri uri) {
214        assert(uri.getAuthority() != null);
215        assert(uri.getPath() != null);
216        Uri.Builder builder = new Uri.Builder();
217        builder.scheme(uri.getScheme());
218        builder.encodedAuthority(uri.getAuthority());
219        builder.encodedPath(uri.getPath());
220        return builder.build();
221    }
222 
223    /**
224     * Set the context to be used for resolving resource queries.
225     * @param context Context to be used, or null for the default application
226     *                context.
227     */
228    public static void setResourceContextForTesting(Context context) {
229        nativeSetResourceContextForTesting(context);
230    }
231 
232    private static native void nativeSetResourceContextForTesting(Context context);
233    private static native String nativeGetAndroidAssetPath();
234    private static native String nativeGetAndroidResourcePath();
235}

[all classes][org.chromium.android_webview]
EMMA 2.0.5312 (C) Vladimir Roubtsov