Bug 699786 - Add API and infra to support favicon load cancellation (r=blassey)
authorLucas Rocha <lucasr@mozilla.com>
Thu, 17 Nov 2011 21:35:17 +0000
changeset 83538 e20ccbf697a12e9dc9231835add5fab108482bc5
parent 83537 fa4ef7d24a262ff7cdfd2a67b713b9088687c5f6
child 83539 9999a423d8abf7eb27e8fe3b96993a6f67d18f2e
push id519
push userakeybl@mozilla.com
push dateWed, 01 Feb 2012 00:38:35 +0000
treeherdermozilla-beta@788ea1ef610b [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersblassey
bugs699786
milestone11.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 699786 - Add API and infra to support favicon load cancellation (r=blassey)
embedding/android/Favicons.java
--- a/embedding/android/Favicons.java
+++ b/embedding/android/Favicons.java
@@ -56,23 +56,31 @@ import java.io.BufferedInputStream;
 import java.io.ByteArrayInputStream;
 import java.io.ByteArrayOutputStream;
 import java.io.IOException;
 import java.io.InputStream;
 import java.net.HttpURLConnection;
 import java.net.MalformedURLException;
 import java.net.URL;
 import java.util.HashMap;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.Set;
 
 public class Favicons {
     private static final String LOGTAG = "GeckoFavicons";
 
+    public static final long NOT_LOADING = 0;
+
     private Context mContext;
     private DatabaseHelper mDbHelper;
 
+    private Map<Long,LoadFaviconTask> mLoadTasks;
+    private long mNextFaviconLoadId;
+
     public interface OnFaviconLoadedListener {
         public void onFaviconLoaded(String url, Drawable favicon);
     }
 
     private class DatabaseHelper extends SQLiteOpenHelper {
         private static final String DATABASE_NAME = "favicon_urls.db";
         private static final String TABLE_NAME = "favicon_urls";
         private static final int DATABASE_VERSION = 1;
@@ -146,50 +154,83 @@ public class Favicons {
         }
     }
 
     public Favicons(Context context) {
         Log.d(LOGTAG, "Creating Favicons instance");
 
         mContext = context;
         mDbHelper = new DatabaseHelper(context);
+
+        mLoadTasks = new HashMap<Long,LoadFaviconTask>();
+        mNextFaviconLoadId = 0;
     }
 
-    public void loadFavicon(String pageUrl, String faviconUrl,
+    public long loadFavicon(String pageUrl, String faviconUrl,
             OnFaviconLoadedListener listener) {
 
-        Log.d(LOGTAG, "Calling loadFavicon() with URL = " + pageUrl +
-                        " and favicon URL = " + faviconUrl);
-
         // Handle the case where page url is empty
         if (pageUrl == null || pageUrl.length() == 0) {
             if (listener != null)
                 listener.onFaviconLoaded(null, null);
         }
 
-        new LoadFaviconTask(pageUrl, faviconUrl, listener).execute();
+        LoadFaviconTask task = new LoadFaviconTask(pageUrl, faviconUrl, listener);
+
+        long taskId = task.getId();
+        mLoadTasks.put(taskId, task);
+
+        task.execute();
+
+        Log.d(LOGTAG, "Calling loadFavicon() with URL = " + pageUrl +
+                        " and favicon URL = " + faviconUrl +
+                        " (" + taskId + ")");
+
+        return taskId;
+    }
+
+    public boolean cancelFaviconLoad(long taskId) {
+        Log.d(LOGTAG, "Requesting cancelation of favicon load (" + taskId + ")");
+
+        if (!mLoadTasks.containsKey(taskId))
+            return false;
+
+        Log.d(LOGTAG, "Cancelling favicon load (" + taskId + ")");
+
+        LoadFaviconTask task = mLoadTasks.get(taskId);
+        return task.cancel(false);
     }
 
     public void close() {
         Log.d(LOGTAG, "Closing Favicons database");
         mDbHelper.close();
+
+        // Cancel any pending tasks
+        Set<Long> taskIds = mLoadTasks.keySet();
+        Iterator iter = taskIds.iterator();
+        while (iter.hasNext()) {
+            long taskId = (Long) iter.next();
+            cancelFaviconLoad(taskId);
+        }
     }
 
     private class LoadFaviconTask extends AsyncTask<Void, Void, BitmapDrawable> {
+        private long mId;
         private String mPageUrl;
         private String mFaviconUrl;
         private OnFaviconLoadedListener mListener;
 
         public LoadFaviconTask(String pageUrl, String faviconUrl, OnFaviconLoadedListener listener) {
+            mId = ++mNextFaviconLoadId;
             mPageUrl = pageUrl;
             mFaviconUrl = faviconUrl;
             mListener = listener;
 
             Log.d(LOGTAG, "Creating LoadFaviconTask with URL = " + pageUrl +
-                            " and favicon URL = " + faviconUrl);
+                          " and favicon URL = " + faviconUrl);
         }
 
         // Runs in background thread
         private BitmapDrawable loadFaviconFromDb() {
             Log.d(LOGTAG, "Loading favicon from DB for URL = " + mPageUrl);
 
             ContentResolver resolver = mContext.getContentResolver();
 
@@ -240,17 +281,17 @@ public class Favicons {
 
             Log.d(LOGTAG, "Saving favicon URL for URL = " + mPageUrl);
             mDbHelper.setFaviconUrlForPageUrl(mPageUrl, mFaviconUrl);
         }
 
         // Runs in background thread
         private BitmapDrawable downloadFavicon(URL faviconUrl) {
             Log.d(LOGTAG, "Downloading favicon for URL = " + mPageUrl +
-                            " with favicon URL = " + mFaviconUrl);
+                          " with favicon URL = " + mFaviconUrl);
 
             // due to android bug 6066, we must download the entire image before using it
             // http://code.google.com/p/android/issues/detail?id=6066
             HttpURLConnection urlConnection = null;
             BufferedInputStream contentStream = null;
             ByteArrayInputStream byteStream = null;
             BitmapDrawable image = null;
 
@@ -290,16 +331,19 @@ public class Favicons {
             return image;
         }
 
         @Override
         protected BitmapDrawable doInBackground(Void... unused) {
             BitmapDrawable image = null;
             URL pageUrl = null;
 
+            if (isCancelled())
+                return null;
+
             // Handle the case of malformed URL
             try {
                 pageUrl = new URL(mPageUrl);
             } catch (MalformedURLException e) {
                 Log.d(LOGTAG, "The provided URL is not valid: " + e);
                 return null;
             }
 
@@ -316,39 +360,63 @@ public class Favicons {
                 }
             } catch (MalformedURLException e) {
                 Log.d(LOGTAG, "The provided favicon URL is not valid: " + e);
                 return null;
             }
 
             Log.d(LOGTAG, "Favicon URL is now: " + mFaviconUrl);
 
+            if (isCancelled())
+                return null;
+
             String storedFaviconUrl = mDbHelper.getFaviconUrlForPageUrl(mPageUrl);
             if (storedFaviconUrl != null && storedFaviconUrl.equals(mFaviconUrl)) {
                 image = loadFaviconFromDb();
 
+                if (isCancelled())
+                    return null;
+
                 // If favicon URL is defined but the favicon image is not
                 // stored in the database for some reason, we force download.
                 if (image == null) {
                     image = downloadFavicon(faviconUrl);
                 }
             } else {
                 image = downloadFavicon(faviconUrl);
             }
 
             return image;
         }
 
         @Override
         protected void onPostExecute(final BitmapDrawable image) {
-            Log.d(LOGTAG, "LoadFaviconTask finished for URL = " + mPageUrl);
+            Log.d(LOGTAG, "LoadFaviconTask finished for URL = " + mPageUrl +
+                          " (" + mId + ")");
+
+            mLoadTasks.remove(mId);
 
             if (mListener != null) {
                 // We want to always run the listener on UI thread
                 GeckoApp.mAppContext.runOnUiThread(new Runnable() {
                     public void run() {
                         mListener.onFaviconLoaded(mPageUrl, image);
                     }
                 });
             }
         }
+
+        @Override
+        protected void onCancelled() {
+            Log.d(LOGTAG, "LoadFaviconTask cancelled for URL = " + mPageUrl +
+                          " (" + mId + ")");
+
+            mLoadTasks.remove(mId);
+
+            // Note that we don't call the listener callback if the
+            // favicon load is cancelled.
+        }
+
+        public long getId() {
+            return mId;
+        }
     }
 }