bug 723159 - Base64.decode not available on eclair r=mfinkle a=akeybl
authorBrad Lassey <blassey@mozilla.com>
Wed, 01 Feb 2012 14:37:03 -0500
changeset 87110 40793282254ebde719faecb2f7a657a5f70b6a71
parent 87109 cc433916cd1de3d15efcad4a23f201875a42d8d3
child 87111 7c4652b0f3b08c7faf52debb6399e09ccf0dc14d
push id818
push usermfinkle@mozilla.com
push dateMon, 06 Feb 2012 17:06:02 +0000
treeherdermozilla-aurora@1f3329e303fe [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmfinkle, akeybl
bugs723159
milestone12.0a2
bug 723159 - Base64.decode not available on eclair r=mfinkle a=akeybl
mobile/android/base/AwesomeBarTabs.java
mobile/android/base/GeckoApp.java
mobile/android/base/GeckoAppShell.java
mobile/android/base/Tabs.java
mobile/android/base/db/BrowserProvider.java.in
--- a/mobile/android/base/AwesomeBarTabs.java
+++ b/mobile/android/base/AwesomeBarTabs.java
@@ -44,17 +44,16 @@ import android.database.Cursor;
 import android.graphics.Bitmap;
 import android.graphics.BitmapFactory;
 import android.graphics.Color;
 import android.graphics.LightingColorFilter;
 import android.graphics.drawable.Drawable;
 import android.os.AsyncTask;
 import android.os.SystemClock;
 import android.util.AttributeSet;
-import android.util.Base64;
 import android.util.Log;
 import android.util.Pair;
 import android.view.LayoutInflater;
 import android.view.MotionEvent;
 import android.view.View;
 import android.view.ViewGroup;
 import android.view.inputmethod.InputMethodManager;
 import android.widget.AdapterView;
@@ -470,17 +469,17 @@ public class AwesomeBarTabs extends TabH
 
             return v;
         }
 
         private Drawable getDrawableFromDataURI(String dataURI) {
             String base64 = dataURI.substring(dataURI.indexOf(',') + 1);
             Drawable drawable = null;
             try {
-                byte[] bytes = Base64.decode(base64, Base64.DEFAULT);
+                byte[] bytes = GeckoAppShell.decodeBase64(base64);
                 ByteArrayInputStream stream = new ByteArrayInputStream(bytes);
                 drawable = Drawable.createFromStream(stream, "src");
                 stream.close();
             } catch (IllegalArgumentException e) {
                 Log.i(LOGTAG, "exception while decoding drawable: " + base64, e);
             } catch (IOException e) { }
             return drawable;
         }
--- a/mobile/android/base/GeckoApp.java
+++ b/mobile/android/base/GeckoApp.java
@@ -391,17 +391,17 @@ abstract public class GeckoApp
     {
         Iterator<ExtraMenuItem> i = sExtraMenuItems.iterator();
         while (i.hasNext()) {
             final ExtraMenuItem item = i.next();
             if (aMenu.findItem(item.id) == null) {
                 final MenuItem mi = aMenu.add(Menu.NONE, item.id, Menu.NONE, item.label);
                 if (item.icon != null) {
                     if (item.icon.startsWith("data")) {
-                        byte[] raw = Base64.decode(item.icon.substring(22), Base64.DEFAULT);
+                        byte[] raw = GeckoAppShell.decodeBase64(item.icon.substring(22));
                         Bitmap bitmap = BitmapFactory.decodeByteArray(raw, 0, raw.length);
                         BitmapDrawable drawable = new BitmapDrawable(bitmap);
                         mi.setIcon(drawable);
                     }
                     else if (item.icon.startsWith("jar:") || item.icon.startsWith("file://")) {
                         GeckoAppShell.getHandler().post(new Runnable() {
                             public void run() {
                                 try {
--- a/mobile/android/base/GeckoAppShell.java
+++ b/mobile/android/base/GeckoAppShell.java
@@ -1797,9 +1797,111 @@ public class GeckoAppShell
 
     public static void enableNetworkNotifications() {
         GeckoNetworkManager.getInstance().enableNotifications();
     }
 
     public static void disableNetworkNotifications() {
         GeckoNetworkManager.getInstance().disableNotifications();
     }
+
+    private static final int GUID_ENCODE_FLAGS = Base64.URL_SAFE | Base64.NO_WRAP;
+
+    /**
+     * taken from http://www.source-code.biz/base64coder/java/Base64Coder.java.txt and modified (MIT License)
+     */
+    // Mapping table from 6-bit nibbles to Base64 characters.
+    private static final byte[] map1 = new byte[64];
+    static {
+      int i=0;
+      for (byte c='A'; c<='Z'; c++) map1[i++] = c;
+      for (byte c='a'; c<='z'; c++) map1[i++] = c;
+      for (byte c='0'; c<='9'; c++) map1[i++] = c;
+      map1[i++] = '-'; map1[i++] = '_'; 
+    }
+
+    // Mapping table from Base64 characters to 6-bit nibbles.
+    private static final byte[] map2 = new byte[128];
+    static {
+        for (int i=0; i<map2.length; i++) map2[i] = -1;
+        for (int i=0; i<64; i++) map2[map1[i]] = (byte)i;
+    }
+
+    final static byte EQUALS_ASCII = (byte) '=';
+
+    /**
+     * Encodes a byte array into Base64 format.
+     * No blanks or line breaks are inserted in the output.
+     * @param in    An array containing the data bytes to be encoded.
+     * @return      A character array containing the Base64 encoded data.
+     */
+    public static byte[] encodeBase64(byte[] in) {
+        if (Build.VERSION.SDK_INT >=Build.VERSION_CODES.FROYO)
+            return Base64.encode(in, GUID_ENCODE_FLAGS);
+        int oDataLen = (in.length*4+2)/3;       // output length without padding
+        int oLen = ((in.length+2)/3)*4;         // output length including padding
+        byte[] out = new byte[oLen];
+        int ip = 0;
+        int iEnd = in.length;
+        int op = 0;
+        while (ip < iEnd) {
+            int i0 = in[ip++] & 0xff;
+            int i1 = ip < iEnd ? in[ip++] & 0xff : 0;
+            int i2 = ip < iEnd ? in[ip++] & 0xff : 0;
+            int o0 = i0 >>> 2;
+            int o1 = ((i0 &   3) << 4) | (i1 >>> 4);
+            int o2 = ((i1 & 0xf) << 2) | (i2 >>> 6);
+            int o3 = i2 & 0x3F;
+            out[op++] = map1[o0];
+            out[op++] = map1[o1];
+            out[op] = op < oDataLen ? map1[o2] : EQUALS_ASCII; op++;
+            out[op] = op < oDataLen ? map1[o3] : EQUALS_ASCII; op++;
+        }
+        return out; 
+    }
+
+    /**
+     * Decodes a byte array from Base64 format.
+     * No blanks or line breaks are allowed within the Base64 encoded input data.
+     * @param in    A character array containing the Base64 encoded data.
+     * @param iOff  Offset of the first character in <code>in</code> to be processed.
+     * @param iLen  Number of characters to process in <code>in</code>, starting at <code>iOff</code>.
+     * @return      An array containing the decoded data bytes.
+     * @throws      IllegalArgumentException If the input is not valid Base64 encoded data.
+     */
+    public static byte[] decodeBase64(byte[] in) {
+        if (Build.VERSION.SDK_INT >=Build.VERSION_CODES.FROYO)
+            return Base64.decode(in, GUID_ENCODE_FLAGS);
+        int iOff = 0;
+        int iLen = in.length;
+        if (iLen%4 != 0) throw new IllegalArgumentException ("Length of Base64 encoded input string is not a multiple of 4.");
+        while (iLen > 0 && in[iOff+iLen-1] == '=') iLen--;
+        int oLen = (iLen*3) / 4;
+        byte[] out = new byte[oLen];
+        int ip = iOff;
+        int iEnd = iOff + iLen;
+        int op = 0;
+        while (ip < iEnd) {
+            int i0 = in[ip++];
+            int i1 = in[ip++];
+            int i2 = ip < iEnd ? in[ip++] : 'A';
+            int i3 = ip < iEnd ? in[ip++] : 'A';
+            if (i0 > 127 || i1 > 127 || i2 > 127 || i3 > 127)
+                throw new IllegalArgumentException ("Illegal character in Base64 encoded data.");
+            int b0 = map2[i0];
+            int b1 = map2[i1];
+            int b2 = map2[i2];
+            int b3 = map2[i3];
+            if (b0 < 0 || b1 < 0 || b2 < 0 || b3 < 0)
+                throw new IllegalArgumentException ("Illegal character in Base64 encoded data.");
+            int o0 = ( b0       <<2) | (b1>>>4);
+            int o1 = ((b1 & 0xf)<<4) | (b2>>>2);
+            int o2 = ((b2 &   3)<<6) |  b3;
+            out[op++] = (byte)o0;
+            if (op<oLen) out[op++] = (byte)o1;
+            if (op<oLen) out[op++] = (byte)o2; }
+        return out; 
+    }
+
+    public static byte[] decodeBase64(String s) {
+        return decodeBase64(s.getBytes());
+    }
 }
--- a/mobile/android/base/Tabs.java
+++ b/mobile/android/base/Tabs.java
@@ -41,17 +41,16 @@ import java.util.ArrayList;
 import java.util.HashMap;
 import java.util.Iterator;
 
 import org.json.JSONException;
 import org.json.JSONObject;
 
 import android.content.ContentResolver;
 import android.os.SystemClock;
-import android.util.Base64;
 import android.util.Log;
 
 public class Tabs implements GeckoEventListener {
     private static final String LOGTAG = "GeckoTabs";
 
     private Tab selectedTab;
     private HashMap<Integer, Tab> tabs;
     private ArrayList<Tab> order;
@@ -284,17 +283,17 @@ public class Tabs implements GeckoEventL
                 closeTab(tab);
             } else if (event.equals("Tab:Select")) {
                 selectTab(message.getInt("tabID"));
             } else if (event.equals("Tab:ScreenshotData")) {
                 Tab tab = getTab(message.getInt("tabID"));
                 String data = message.getString("data");
                 if (data.length() < 22)
                     return;
-                byte[] compressed = Base64.decode(data.substring(22), Base64.DEFAULT);
+                byte[] compressed = GeckoAppShell.decodeBase64(data.substring(22));
                 GeckoApp.mAppContext.processThumbnail(tab, null, compressed);
             }
         } catch (Exception e) { 
             Log.i(LOGTAG, "handleMessage throws " + e + " for message: " + event);
         }
     }
 
     public void refreshThumbnails() {
--- a/mobile/android/base/db/BrowserProvider.java.in
+++ b/mobile/android/base/db/BrowserProvider.java.in
@@ -65,17 +65,16 @@ import android.content.UriMatcher;
 import android.database.Cursor;
 import android.database.MatrixCursor;
 import android.database.sqlite.SQLiteDatabase;
 import android.database.sqlite.SQLiteOpenHelper;
 import android.database.sqlite.SQLiteQueryBuilder;
 import android.net.Uri;
 import android.os.Build;
 import android.text.TextUtils;
-import android.util.Base64;
 import android.util.Log;
 
 public class BrowserProvider extends ContentProvider {
     private static final String LOGTAG = "GeckoBrowserProvider";
     private Context mContext;
 
     static final String DATABASE_NAME = "browser.db";
 
@@ -204,64 +203,18 @@ public class BrowserProvider extends Con
     }
 
     private HashMap<String, DatabaseHelper> mDatabasePerProfile;
 
     static final String qualifyColumn(String table, String column) {
         return table + "." + column;
     }
 
-    private static final int GUID_ENCODE_FLAGS = Base64.URL_SAFE | Base64.NO_WRAP;
-
-    /**
-     * taken from http://www.source-code.biz/base64coder/java/Base64Coder.java.txt and modified (MIT License)
-     */
-    // Mapping table from 6-bit nibbles to Base64 characters.
-    private static final byte[] map1 = new byte[64];
-    static {
-      int i=0;
-      for (byte c='A'; c<='Z'; c++) map1[i++] = c;
-      for (byte c='a'; c<='z'; c++) map1[i++] = c;
-      for (byte c='0'; c<='9'; c++) map1[i++] = c;
-      map1[i++] = '-'; map1[i++] = '_'; 
-    }
-    final static byte EQUALS_ASCII = (byte) '=';
-    /**
-     * Encodes a byte array into Base64 format.
-     * No blanks or line breaks are inserted in the output.
-     * @param in    An array containing the data bytes to be encoded.
-     * @return      A character array containing the Base64 encoded data.
-     */
-    public static byte[] encodeBase64(byte[] in) {
-        if (Build.VERSION.SDK_INT >=Build.VERSION_CODES.FROYO)
-            return Base64.encode(in, GUID_ENCODE_FLAGS);
-        int oDataLen = (in.length*4+2)/3;       // output length without padding
-        int oLen = ((in.length+2)/3)*4;         // output length including padding
-        byte[] out = new byte[oLen];
-        int ip = 0;
-        int iEnd = in.length;
-        int op = 0;
-        while (ip < iEnd) {
-            int i0 = in[ip++] & 0xff;
-            int i1 = ip < iEnd ? in[ip++] & 0xff : 0;
-            int i2 = ip < iEnd ? in[ip++] & 0xff : 0;
-            int o0 = i0 >>> 2;
-            int o1 = ((i0 &   3) << 4) | (i1 >>> 4);
-            int o2 = ((i1 & 0xf) << 2) | (i2 >>> 6);
-            int o3 = i2 & 0x3F;
-            out[op++] = map1[o0];
-            out[op++] = map1[o1];
-            out[op] = op < oDataLen ? map1[o2] : EQUALS_ASCII; op++;
-            out[op] = op < oDataLen ? map1[o3] : EQUALS_ASCII; op++;
-        }
-        return out; 
-    }
-
     public static String generateGuid() {
-        byte[] encodedBytes = encodeBase64(generateRandomBytes(9));
+        byte[] encodedBytes = GeckoAppShell.encodeBase64(generateRandomBytes(9));
         return new String(encodedBytes);
     }
 
     private static byte[] generateRandomBytes(int length) {
         byte[] bytes = new byte[length];
 
         Random random = new Random(System.nanoTime());
         random.nextBytes(bytes);