1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/mobile/android/base/util/GeckoJarReader.java Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,177 @@ 1.4 +/* This Source Code Form is subject to the terms of the Mozilla Public 1.5 + * License, v. 2.0. If a copy of the MPL was not distributed with this file, 1.6 + * You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.7 + 1.8 +package org.mozilla.gecko.util; 1.9 + 1.10 +import org.mozilla.gecko.mozglue.NativeZip; 1.11 + 1.12 +import android.content.res.Resources; 1.13 +import android.graphics.Bitmap; 1.14 +import android.graphics.drawable.BitmapDrawable; 1.15 +import android.util.Log; 1.16 +import org.mozilla.gecko.mozglue.RobocopTarget; 1.17 + 1.18 +import java.io.BufferedReader; 1.19 +import java.io.IOException; 1.20 +import java.io.InputStream; 1.21 +import java.io.InputStreamReader; 1.22 +import java.net.URI; 1.23 +import java.net.URISyntaxException; 1.24 +import java.util.Stack; 1.25 + 1.26 +/* Reads out of a multiple level deep jar file such as 1.27 + * jar:jar:file:///data/app/org.mozilla.fennec.apk!/omni.ja!/chrome/chrome/content/branding/favicon32.png 1.28 + */ 1.29 +public final class GeckoJarReader { 1.30 + private static final String LOGTAG = "GeckoJarReader"; 1.31 + 1.32 + private GeckoJarReader() {} 1.33 + 1.34 + public static Bitmap getBitmap(Resources resources, String url) { 1.35 + BitmapDrawable drawable = getBitmapDrawable(resources, url); 1.36 + return (drawable != null) ? drawable.getBitmap() : null; 1.37 + } 1.38 + 1.39 + public static BitmapDrawable getBitmapDrawable(Resources resources, String url) { 1.40 + Stack<String> jarUrls = parseUrl(url); 1.41 + InputStream inputStream = null; 1.42 + BitmapDrawable bitmap = null; 1.43 + 1.44 + NativeZip zip = null; 1.45 + try { 1.46 + // Load the initial jar file as a zip 1.47 + zip = getZipFile(jarUrls.pop()); 1.48 + inputStream = getStream(zip, jarUrls, url); 1.49 + if (inputStream != null) { 1.50 + bitmap = new BitmapDrawable(resources, inputStream); 1.51 + } 1.52 + } catch (IOException ex) { 1.53 + Log.e(LOGTAG, "Exception ", ex); 1.54 + } catch (URISyntaxException ex) { 1.55 + Log.e(LOGTAG, "Exception ", ex); 1.56 + } finally { 1.57 + if (inputStream != null) { 1.58 + try { 1.59 + inputStream.close(); 1.60 + } catch(IOException ex) { 1.61 + Log.e(LOGTAG, "Error closing stream", ex); 1.62 + } 1.63 + } 1.64 + if (zip != null) { 1.65 + zip.close(); 1.66 + } 1.67 + } 1.68 + 1.69 + return bitmap; 1.70 + } 1.71 + 1.72 + public static String getText(String url) { 1.73 + Stack<String> jarUrls = parseUrl(url); 1.74 + 1.75 + NativeZip zip = null; 1.76 + BufferedReader reader = null; 1.77 + String text = null; 1.78 + try { 1.79 + zip = getZipFile(jarUrls.pop()); 1.80 + InputStream input = getStream(zip, jarUrls, url); 1.81 + if (input != null) { 1.82 + reader = new BufferedReader(new InputStreamReader(input)); 1.83 + text = reader.readLine(); 1.84 + } 1.85 + } catch (IOException ex) { 1.86 + Log.e(LOGTAG, "Exception ", ex); 1.87 + } catch (URISyntaxException ex) { 1.88 + Log.e(LOGTAG, "Exception ", ex); 1.89 + } finally { 1.90 + if (reader != null) { 1.91 + try { 1.92 + reader.close(); 1.93 + } catch(IOException ex) { 1.94 + Log.e(LOGTAG, "Error closing reader", ex); 1.95 + } 1.96 + } 1.97 + if (zip != null) { 1.98 + zip.close(); 1.99 + } 1.100 + } 1.101 + 1.102 + return text; 1.103 + } 1.104 + 1.105 + private static NativeZip getZipFile(String url) throws IOException, URISyntaxException { 1.106 + URI fileUrl = new URI(url); 1.107 + return new NativeZip(fileUrl.getPath()); 1.108 + } 1.109 + 1.110 + @RobocopTarget 1.111 + public static InputStream getStream(String url) { 1.112 + Stack<String> jarUrls = parseUrl(url); 1.113 + try { 1.114 + NativeZip zip = getZipFile(jarUrls.pop()); 1.115 + return getStream(zip, jarUrls, url); 1.116 + } catch (Exception ex) { 1.117 + // Some JNI code throws IllegalArgumentException on a bad file name; 1.118 + // swallow the error and return null. We could also see legitimate 1.119 + // IOExceptions here. 1.120 + return null; 1.121 + } 1.122 + } 1.123 + 1.124 + private static InputStream getStream(NativeZip zip, Stack<String> jarUrls, String origUrl) { 1.125 + InputStream inputStream = null; 1.126 + 1.127 + // loop through children jar files until we reach the innermost one 1.128 + while (!jarUrls.empty()) { 1.129 + String fileName = jarUrls.pop(); 1.130 + 1.131 + if (inputStream != null) { 1.132 + // intermediate NativeZips and InputStreams will be garbage collected. 1.133 + try { 1.134 + zip = new NativeZip(inputStream); 1.135 + } catch (IllegalArgumentException e) { 1.136 + String description = "!!! BUG 849589 !!! origUrl=" + origUrl; 1.137 + Log.e(LOGTAG, description, e); 1.138 + throw new IllegalArgumentException(description); 1.139 + } 1.140 + } 1.141 + 1.142 + inputStream = zip.getInputStream(fileName); 1.143 + if (inputStream == null) { 1.144 + Log.d(LOGTAG, "No Entry for " + fileName); 1.145 + return null; 1.146 + } 1.147 + } 1.148 + 1.149 + return inputStream; 1.150 + } 1.151 + 1.152 + /* Returns a stack of strings breaking the url up into pieces. Each piece 1.153 + * is assumed to point to a jar file except for the final one. Callers should 1.154 + * pass in the url to parse, and null for the parent parameter (used for recursion) 1.155 + * For example, jar:jar:file:///data/app/org.mozilla.fennec.apk!/omni.ja!/chrome/chrome/content/branding/favicon32.png 1.156 + * will return: 1.157 + * file:///data/app/org.mozilla.fennec.apk 1.158 + * omni.ja 1.159 + * chrome/chrome/content/branding/favicon32.png 1.160 + */ 1.161 + private static Stack<String> parseUrl(String url) { 1.162 + return parseUrl(url, null); 1.163 + } 1.164 + 1.165 + private static Stack<String> parseUrl(String url, Stack<String> results) { 1.166 + if (results == null) { 1.167 + results = new Stack<String>(); 1.168 + } 1.169 + 1.170 + if (url.startsWith("jar:")) { 1.171 + int jarEnd = url.lastIndexOf("!"); 1.172 + String subStr = url.substring(4, jarEnd); 1.173 + results.push(url.substring(jarEnd+2)); // remove the !/ characters 1.174 + return parseUrl(subStr, results); 1.175 + } else { 1.176 + results.push(url); 1.177 + return results; 1.178 + } 1.179 + } 1.180 +}