Bug 969417 - Cleanup naming conventions in favicon code. r=rnewman

This commit is contained in:
Chris Kitching 2014-03-12 16:20:38 +00:00
parent 6269828bc6
commit 73c812f40f
11 changed files with 384 additions and 389 deletions

View File

@ -42,7 +42,6 @@ import org.mozilla.gecko.home.SearchEngine;
import org.mozilla.gecko.menu.GeckoMenu;
import org.mozilla.gecko.preferences.GeckoPreferences;
import org.mozilla.gecko.prompts.Prompt;
import org.mozilla.gecko.prompts.PromptListItem;
import org.mozilla.gecko.sync.setup.SyncAccounts;
import org.mozilla.gecko.toolbar.AutocompleteHandler;
import org.mozilla.gecko.toolbar.BrowserToolbar;
@ -1447,7 +1446,7 @@ abstract public class BrowserApp extends GeckoApp
// If we failed to load a favicon, we use the default favicon instead.
Tabs.getInstance()
.updateFaviconForURL(pageUrl,
(favicon == null) ? Favicons.sDefaultFavicon : favicon);
(favicon == null) ? Favicons.defaultFavicon : favicon);
}
};

View File

@ -13,7 +13,6 @@ import org.mozilla.gecko.Tab;
import org.mozilla.gecko.Tabs;
import org.mozilla.gecko.db.BrowserDB;
import org.mozilla.gecko.favicons.cache.FaviconCache;
import org.mozilla.gecko.gfx.BitmapUtils;
import org.mozilla.gecko.util.GeckoJarReader;
import org.mozilla.gecko.util.NonEvictingLruCache;
import org.mozilla.gecko.util.ThreadUtils;
@ -29,12 +28,9 @@ import android.util.SparseArray;
import java.io.File;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import java.util.Set;
public class Favicons {
private static final String LOGTAG = "GeckoFavicons";
@ -53,25 +49,25 @@ public class Favicons {
public static final int FLAG_PERSIST = 2;
public static final int FLAG_SCALE = 4;
protected static Context sContext;
protected static Context context;
// The default Favicon to show if no other can be found.
public static Bitmap sDefaultFavicon;
public static Bitmap defaultFavicon;
// The density-adjusted default Favicon dimensions.
public static int sDefaultFaviconSize;
public static int defaultFaviconSize;
// The density-adjusted maximum Favicon dimensions.
public static int sLargestFaviconSize;
public static int largestFaviconSize;
private static final SparseArray<LoadFaviconTask> sLoadTasks = new SparseArray<LoadFaviconTask>();
private static final SparseArray<LoadFaviconTask> loadTasks = new SparseArray<LoadFaviconTask>();
// Cache to hold mappings between page URLs and Favicon URLs. Used to avoid going to the DB when
// doing so is not necessary.
private static final NonEvictingLruCache<String, String> sPageURLMappings = new NonEvictingLruCache<String, String>(NUM_PAGE_URL_MAPPINGS_TO_STORE);
private static final NonEvictingLruCache<String, String> pageURLMappings = new NonEvictingLruCache<String, String>(NUM_PAGE_URL_MAPPINGS_TO_STORE);
public static String getFaviconURLForPageURLFromCache(String pageURL) {
return sPageURLMappings.get(pageURL);
return pageURLMappings.get(pageURL);
}
/**
@ -79,10 +75,10 @@ public class Favicons {
* Useful for short-circuiting local database access.
*/
public static void putFaviconURLForPageURLInCache(String pageURL, String faviconURL) {
sPageURLMappings.put(pageURL, faviconURL);
pageURLMappings.put(pageURL, faviconURL);
}
private static FaviconCache sFaviconsCache;
private static FaviconCache faviconsCache;
/**
* Returns either NOT_LOADING, or LOADED if the onFaviconLoaded call could
@ -117,7 +113,7 @@ public class Favicons {
* Returns null otherwise.
*/
public static Bitmap getSizedFaviconForPageFromCache(final String pageURL, int targetSize) {
final String faviconURL = sPageURLMappings.get(pageURL);
final String faviconURL = pageURLMappings.get(pageURL);
if (faviconURL == null) {
return null;
}
@ -143,7 +139,7 @@ public class Favicons {
// Do we know the favicon URL for this page already?
String cacheURL = faviconURL;
if (cacheURL == null) {
cacheURL = sPageURLMappings.get(pageURL);
cacheURL = pageURLMappings.get(pageURL);
}
// If there's no favicon URL given, try and hit the cache with the default one.
@ -153,7 +149,7 @@ public class Favicons {
// If it's something we can't even figure out a default URL for, just give up.
if (cacheURL == null) {
return dispatchResult(pageURL, null, sDefaultFavicon, listener);
return dispatchResult(pageURL, null, defaultFavicon, listener);
}
Bitmap cachedIcon = getSizedFaviconFromCache(cacheURL, targetSize);
@ -162,8 +158,8 @@ public class Favicons {
}
// Check if favicon has failed.
if (sFaviconsCache.isFailedFavicon(cacheURL)) {
return dispatchResult(pageURL, cacheURL, sDefaultFavicon, listener);
if (faviconsCache.isFailedFavicon(cacheURL)) {
return dispatchResult(pageURL, cacheURL, defaultFavicon, listener);
}
// Failing that, try and get one from the database or internet.
@ -181,7 +177,7 @@ public class Favicons {
* null if no applicable Favicon exists in the cache.
*/
public static Bitmap getSizedFaviconFromCache(String faviconURL, int targetSize) {
return sFaviconsCache.getFaviconForDimensions(faviconURL, targetSize);
return faviconsCache.getFaviconForDimensions(faviconURL, targetSize);
}
/**
@ -201,10 +197,10 @@ public class Favicons {
public static int getSizedFaviconForPageFromLocal(final String pageURL, final int targetSize, final OnFaviconLoadedListener callback) {
// Firstly, try extremely hard to cheat.
// Have we cached this favicon URL? If we did, we can consult the memcache right away.
String targetURL = sPageURLMappings.get(pageURL);
String targetURL = pageURLMappings.get(pageURL);
if (targetURL != null) {
// Check if favicon has failed.
if (sFaviconsCache.isFailedFavicon(targetURL)) {
if (faviconsCache.isFailedFavicon(targetURL)) {
return dispatchResult(pageURL, targetURL, null, callback);
}
@ -219,15 +215,15 @@ public class Favicons {
// No joy using in-memory resources. Go to background thread and ask the database.
LoadFaviconTask task = new LoadFaviconTask(ThreadUtils.getBackgroundHandler(), pageURL, targetURL, 0, callback, targetSize, true);
int taskId = task.getId();
synchronized(sLoadTasks) {
sLoadTasks.put(taskId, task);
synchronized(loadTasks) {
loadTasks.put(taskId, task);
}
task.execute();
return taskId;
}
public static int getSizedFaviconForPageFromLocal(final String pageURL, final OnFaviconLoadedListener callback) {
return getSizedFaviconForPageFromLocal(pageURL, sDefaultFaviconSize, callback);
return getSizedFaviconForPageFromLocal(pageURL, defaultFaviconSize, callback);
}
/**
@ -250,7 +246,7 @@ public class Favicons {
}
}
targetURL = BrowserDB.getFaviconUrlForHistoryUrl(sContext.getContentResolver(), pageURL);
targetURL = BrowserDB.getFaviconUrlForHistoryUrl(context.getContentResolver(), pageURL);
if (targetURL == null) {
// Nothing in the history database. Fall back to the default URL and hope for the best.
targetURL = guessDefaultFaviconURL(pageURL);
@ -286,8 +282,8 @@ public class Favicons {
LoadFaviconTask task = new LoadFaviconTask(ThreadUtils.getBackgroundHandler(), pageUrl, faviconUrl, flags, listener, targetSize, false);
int taskId = task.getId();
synchronized(sLoadTasks) {
sLoadTasks.put(taskId, task);
synchronized(loadTasks) {
loadTasks.put(taskId, task);
}
task.execute();
@ -296,7 +292,7 @@ public class Favicons {
}
public static void putFaviconInMemCache(String pageUrl, Bitmap image) {
sFaviconsCache.putSingleFavicon(pageUrl, image);
faviconsCache.putSingleFavicon(pageUrl, image);
}
/**
@ -308,7 +304,7 @@ public class Favicons {
* @param images An iterator over the new favicons to put in the cache.
*/
public static void putFaviconsInMemCache(String pageUrl, Iterator<Bitmap> images, boolean permanently) {
sFaviconsCache.putFavicons(pageUrl, images, permanently);
faviconsCache.putFavicons(pageUrl, images, permanently);
}
public static void putFaviconsInMemCache(String pageUrl, Iterator<Bitmap> images) {
@ -316,12 +312,12 @@ public class Favicons {
}
public static void clearMemCache() {
sFaviconsCache.evictAll();
sPageURLMappings.evictAll();
faviconsCache.evictAll();
pageURLMappings.evictAll();
}
public static void putFaviconInFailedCache(String faviconURL) {
sFaviconsCache.putFailed(faviconURL);
faviconsCache.putFailed(faviconURL);
}
public static boolean cancelFaviconLoad(int taskId) {
@ -330,13 +326,14 @@ public class Favicons {
}
boolean cancelled;
synchronized (sLoadTasks) {
if (sLoadTasks.indexOfKey(taskId) < 0)
synchronized (loadTasks) {
if (loadTasks.indexOfKey(taskId) < 0) {
return false;
}
Log.d(LOGTAG, "Cancelling favicon load (" + taskId + ")");
LoadFaviconTask task = sLoadTasks.get(taskId);
LoadFaviconTask task = loadTasks.get(taskId);
cancelled = task.cancel(false);
}
return cancelled;
@ -346,12 +343,12 @@ public class Favicons {
Log.d(LOGTAG, "Closing Favicons database");
// Cancel any pending tasks
synchronized (sLoadTasks) {
final int count = sLoadTasks.size();
synchronized (loadTasks) {
final int count = loadTasks.size();
for (int i = 0; i < count; i++) {
cancelFaviconLoad(sLoadTasks.keyAt(i));
cancelFaviconLoad(loadTasks.keyAt(i));
}
sLoadTasks.clear();
loadTasks.clear();
}
LoadFaviconTask.closeHTTPClient();
@ -364,7 +361,7 @@ public class Favicons {
* @return The dominant colour of the provided Favicon.
*/
public static int getFaviconColor(String url) {
return sFaviconsCache.getDominantColor(url);
return faviconsCache.getDominantColor(url);
}
/**
@ -376,24 +373,24 @@ public class Favicons {
*/
public static void attachToContext(Context context) throws Exception {
final Resources res = context.getResources();
sContext = context;
Favicons.context = context;
// Decode the default Favicon ready for use.
sDefaultFavicon = BitmapFactory.decodeResource(res, R.drawable.favicon);
if (sDefaultFavicon == null) {
defaultFavicon = BitmapFactory.decodeResource(res, R.drawable.favicon);
if (defaultFavicon == null) {
throw new Exception("Null default favicon was returned from the resources system!");
}
sDefaultFaviconSize = res.getDimensionPixelSize(R.dimen.favicon_bg);
defaultFaviconSize = res.getDimensionPixelSize(R.dimen.favicon_bg);
// Screen-density-adjusted upper limit on favicon size. Favicons larger than this are
// downscaled to this size or discarded.
sLargestFaviconSize = context.getResources().getDimensionPixelSize(R.dimen.favicon_largest_interesting_size);
sFaviconsCache = new FaviconCache(FAVICON_CACHE_SIZE_BYTES, sLargestFaviconSize);
largestFaviconSize = context.getResources().getDimensionPixelSize(R.dimen.favicon_largest_interesting_size);
faviconsCache = new FaviconCache(FAVICON_CACHE_SIZE_BYTES, largestFaviconSize);
// Initialize page mappings for each of our special pages.
for (String url : AboutPages.getDefaultIconPages()) {
sPageURLMappings.putWithoutEviction(url, BUILT_IN_FAVICON_URL);
pageURLMappings.putWithoutEviction(url, BUILT_IN_FAVICON_URL);
}
// Load and cache the built-in favicon in each of its sizes.
@ -453,8 +450,8 @@ public class Favicons {
}
public static void removeLoadTask(int taskId) {
synchronized(sLoadTasks) {
sLoadTasks.delete(taskId);
synchronized(loadTasks) {
loadTasks.delete(taskId);
}
}
@ -464,7 +461,7 @@ public class Favicons {
* @param faviconURL Favicon URL to check for failure.
*/
static boolean isFailedFavicon(String faviconURL) {
return sFaviconsCache.isFailedFavicon(faviconURL);
return faviconsCache.isFailedFavicon(faviconURL);
}
/**

View File

@ -22,7 +22,7 @@ import org.mozilla.gecko.favicons.decoders.LoadFaviconResult;
import org.mozilla.gecko.util.GeckoJarReader;
import org.mozilla.gecko.util.ThreadUtils;
import org.mozilla.gecko.util.UiAsyncTask;
import static org.mozilla.gecko.favicons.Favicons.sContext;
import static org.mozilla.gecko.favicons.Favicons.context;
import java.io.IOException;
import java.io.InputStream;
@ -53,21 +53,21 @@ public class LoadFaviconTask extends UiAsyncTask<Void, Void, Bitmap> {
// by the server.
private static final int DEFAULT_FAVICON_BUFFER_SIZE = 25000;
private static AtomicInteger mNextFaviconLoadId = new AtomicInteger(0);
private int mId;
private String mPageUrl;
private String mFaviconUrl;
private OnFaviconLoadedListener mListener;
private int mFlags;
private static AtomicInteger nextFaviconLoadId = new AtomicInteger(0);
private int id;
private String pageUrl;
private String faviconURL;
private OnFaviconLoadedListener listener;
private int flags;
private final boolean mOnlyFromLocal;
private final boolean onlyFromLocal;
// Assuming square favicons, judging by width only is acceptable.
protected int mTargetWidth;
private LinkedList<LoadFaviconTask> mChainees;
private boolean mIsChaining;
protected int targetWidth;
private LinkedList<LoadFaviconTask> chainees;
private boolean isChaining;
static AndroidHttpClient sHttpClient = AndroidHttpClient.newInstance(GeckoAppShell.getGeckoInterface().getDefaultUAString());
static AndroidHttpClient httpClient = AndroidHttpClient.newInstance(GeckoAppShell.getGeckoInterface().getDefaultUAString());
public LoadFaviconTask(Handler backgroundThreadHandler,
String pageUrl, String faviconUrl, int flags,
@ -76,33 +76,33 @@ public class LoadFaviconTask extends UiAsyncTask<Void, Void, Bitmap> {
}
public LoadFaviconTask(Handler backgroundThreadHandler,
String pageUrl, String faviconUrl, int flags,
OnFaviconLoadedListener aListener, int targetSize, boolean fromLocal) {
OnFaviconLoadedListener listener, int targetWidth, boolean onlyFromLocal) {
super(backgroundThreadHandler);
mId = mNextFaviconLoadId.incrementAndGet();
id = nextFaviconLoadId.incrementAndGet();
mPageUrl = pageUrl;
mFaviconUrl = faviconUrl;
mListener = aListener;
mFlags = flags;
mTargetWidth = targetSize;
mOnlyFromLocal = fromLocal;
this.pageUrl = pageUrl;
this.faviconURL = faviconUrl;
this.listener = listener;
this.flags = flags;
this.targetWidth = targetWidth;
this.onlyFromLocal = onlyFromLocal;
}
// Runs in background thread
private LoadFaviconResult loadFaviconFromDb() {
ContentResolver resolver = sContext.getContentResolver();
return BrowserDB.getFaviconForFaviconUrl(resolver, mFaviconUrl);
ContentResolver resolver = context.getContentResolver();
return BrowserDB.getFaviconForFaviconUrl(resolver, faviconURL);
}
// Runs in background thread
private void saveFaviconToDb(final byte[] encodedFavicon) {
if ((mFlags & FLAG_PERSIST) == 0) {
if ((flags & FLAG_PERSIST) == 0) {
return;
}
ContentResolver resolver = sContext.getContentResolver();
BrowserDB.updateFaviconForUrl(resolver, mPageUrl, encodedFavicon, mFaviconUrl);
ContentResolver resolver = context.getContentResolver();
BrowserDB.updateFaviconForUrl(resolver, pageUrl, encodedFavicon, faviconURL);
}
/**
@ -121,7 +121,7 @@ public class LoadFaviconTask extends UiAsyncTask<Void, Void, Bitmap> {
}
HttpGet request = new HttpGet(faviconURI);
HttpResponse response = sHttpClient.execute(request);
HttpResponse response = httpClient.execute(request);
if (response == null) {
return null;
}
@ -172,7 +172,7 @@ public class LoadFaviconTask extends UiAsyncTask<Void, Void, Bitmap> {
if (uri.startsWith("jar:jar:")) {
Log.d(LOGTAG, "Fetching favicon from JAR.");
try {
return GeckoJarReader.getBitmap(sContext.getResources(), uri);
return GeckoJarReader.getBitmap(context.getResources(), uri);
} catch (Exception e) {
// Just about anything could happen here.
Log.w(LOGTAG, "Error fetching favicon from JAR.", e);
@ -287,27 +287,27 @@ public class LoadFaviconTask extends UiAsyncTask<Void, Void, Bitmap> {
// Handle the case of malformed favicon URL.
// If favicon is empty, fall back to the stored one.
if (TextUtils.isEmpty(mFaviconUrl)) {
if (TextUtils.isEmpty(faviconURL)) {
// Try to get the favicon URL from the memory cache.
storedFaviconUrl = Favicons.getFaviconURLForPageURLFromCache(mPageUrl);
storedFaviconUrl = Favicons.getFaviconURLForPageURLFromCache(pageUrl);
// If that failed, try to get the URL from the database.
if (storedFaviconUrl == null) {
storedFaviconUrl = Favicons.getFaviconURLForPageURL(mPageUrl);
storedFaviconUrl = Favicons.getFaviconURLForPageURL(pageUrl);
if (storedFaviconUrl != null) {
// If that succeeded, cache the URL loaded from the database in memory.
Favicons.putFaviconURLForPageURLInCache(mPageUrl, storedFaviconUrl);
Favicons.putFaviconURLForPageURLInCache(pageUrl, storedFaviconUrl);
}
}
// If we found a faviconURL - use it.
if (storedFaviconUrl != null) {
mFaviconUrl = storedFaviconUrl;
faviconURL = storedFaviconUrl;
} else {
// If we don't have a stored one, fall back to the default.
mFaviconUrl = Favicons.guessDefaultFaviconURL(mPageUrl);
faviconURL = Favicons.guessDefaultFaviconURL(pageUrl);
if (TextUtils.isEmpty(mFaviconUrl)) {
if (TextUtils.isEmpty(faviconURL)) {
return null;
}
isUsingDefaultURL = true;
@ -316,7 +316,7 @@ public class LoadFaviconTask extends UiAsyncTask<Void, Void, Bitmap> {
// Check if favicon has failed - if so, give up. We need this check because, sometimes, we
// didn't know the real Favicon URL until we asked the database.
if (Favicons.isFailedFavicon(mFaviconUrl)) {
if (Favicons.isFailedFavicon(faviconURL)) {
return null;
}
@ -329,10 +329,10 @@ public class LoadFaviconTask extends UiAsyncTask<Void, Void, Bitmap> {
// If there is, just join the queue and wait for it to finish. If not, we carry on.
synchronized(loadsInFlight) {
// Another load of the current Favicon is already underway
LoadFaviconTask existingTask = loadsInFlight.get(mFaviconUrl);
LoadFaviconTask existingTask = loadsInFlight.get(faviconURL);
if (existingTask != null && !existingTask.isCancelled()) {
existingTask.chainTasks(this);
mIsChaining = true;
isChaining = true;
// If we are chaining, we want to keep the first task started to do this job as the one
// in the hashmap so subsequent tasks will add themselves to its chaining list.
@ -341,7 +341,7 @@ public class LoadFaviconTask extends UiAsyncTask<Void, Void, Bitmap> {
// We do not want to update the hashmap if the task has chained - other tasks need to
// chain onto the same parent task.
loadsInFlight.put(mFaviconUrl, this);
loadsInFlight.put(faviconURL, this);
}
if (isCancelled()) {
@ -354,20 +354,20 @@ public class LoadFaviconTask extends UiAsyncTask<Void, Void, Bitmap> {
return pushToCacheAndGetResult(loadedBitmaps);
}
if (mOnlyFromLocal || isCancelled()) {
if (onlyFromLocal || isCancelled()) {
return null;
}
// Let's see if it's in a JAR.
image = fetchJARFavicon(mFaviconUrl);
image = fetchJARFavicon(faviconURL);
if (imageIsValid(image)) {
// We don't want to put this into the DB.
Favicons.putFaviconInMemCache(mFaviconUrl, image);
Favicons.putFaviconInMemCache(faviconURL, image);
return image;
}
try {
loadedBitmaps = downloadFavicon(new URI(mFaviconUrl));
loadedBitmaps = downloadFavicon(new URI(faviconURL));
} catch (URISyntaxException e) {
Log.e(LOGTAG, "The provided favicon URL is not valid");
return null;
@ -381,7 +381,7 @@ public class LoadFaviconTask extends UiAsyncTask<Void, Void, Bitmap> {
}
if (isUsingDefaultURL) {
Favicons.putFaviconInFailedCache(mFaviconUrl);
Favicons.putFaviconInFailedCache(faviconURL);
return null;
}
@ -390,16 +390,16 @@ public class LoadFaviconTask extends UiAsyncTask<Void, Void, Bitmap> {
}
// If we're not already trying the default URL, try it now.
final String guessed = Favicons.guessDefaultFaviconURL(mPageUrl);
final String guessed = Favicons.guessDefaultFaviconURL(pageUrl);
if (guessed == null) {
Favicons.putFaviconInFailedCache(mFaviconUrl);
Favicons.putFaviconInFailedCache(faviconURL);
return null;
}
image = fetchJARFavicon(guessed);
if (imageIsValid(image)) {
// We don't want to put this into the DB.
Favicons.putFaviconInMemCache(mFaviconUrl, image);
Favicons.putFaviconInMemCache(faviconURL, image);
return image;
}
@ -428,8 +428,8 @@ public class LoadFaviconTask extends UiAsyncTask<Void, Void, Bitmap> {
* we are under extreme memory pressure and find ourselves dropping the cache immediately.
*/
private Bitmap pushToCacheAndGetResult(LoadFaviconResult loadedBitmaps) {
Favicons.putFaviconsInMemCache(mFaviconUrl, loadedBitmaps.getBitmaps());
Bitmap result = Favicons.getSizedFaviconFromCache(mFaviconUrl, mTargetWidth);
Favicons.putFaviconsInMemCache(faviconURL, loadedBitmaps.getBitmaps());
Bitmap result = Favicons.getSizedFaviconFromCache(faviconURL, targetWidth);
return result;
}
@ -441,7 +441,7 @@ public class LoadFaviconTask extends UiAsyncTask<Void, Void, Bitmap> {
@Override
protected void onPostExecute(Bitmap image) {
if (mIsChaining) {
if (isChaining) {
return;
}
@ -450,10 +450,10 @@ public class LoadFaviconTask extends UiAsyncTask<Void, Void, Bitmap> {
synchronized (loadsInFlight) {
// Prevent any other tasks from chaining on this one.
loadsInFlight.remove(mFaviconUrl);
loadsInFlight.remove(faviconURL);
}
// Since any update to mChainees is done while holding the loadsInFlight lock, once we reach
// Since any update to chainees is done while holding the loadsInFlight lock, once we reach
// this point no further updates to that list can possibly take place (As far as other tasks
// are concerned, there is no longer a task to chain from. The above block will have waited
// for any tasks that were adding themselves to the list before reaching this point.)
@ -463,8 +463,8 @@ public class LoadFaviconTask extends UiAsyncTask<Void, Void, Bitmap> {
// actually happens outside of the strange situations unit tests create.
// Share the result with all chained tasks.
if (mChainees != null) {
for (LoadFaviconTask t : mChainees) {
if (chainees != null) {
for (LoadFaviconTask t : chainees) {
// In the case that we just decoded multiple favicons, either we're passing the right
// image now, or the call into the cache in processResult will fetch the right one.
t.processResult(image);
@ -473,35 +473,35 @@ public class LoadFaviconTask extends UiAsyncTask<Void, Void, Bitmap> {
}
private void processResult(Bitmap image) {
Favicons.removeLoadTask(mId);
Favicons.removeLoadTask(id);
Bitmap scaled = image;
// Notify listeners, scaling if required.
if (mTargetWidth != -1 && image != null && image.getWidth() != mTargetWidth) {
scaled = Favicons.getSizedFaviconFromCache(mFaviconUrl, mTargetWidth);
if (targetWidth != -1 && image != null && image.getWidth() != targetWidth) {
scaled = Favicons.getSizedFaviconFromCache(faviconURL, targetWidth);
}
Favicons.dispatchResult(mPageUrl, mFaviconUrl, scaled, mListener);
Favicons.dispatchResult(pageUrl, faviconURL, scaled, listener);
}
@Override
protected void onCancelled() {
Favicons.removeLoadTask(mId);
Favicons.removeLoadTask(id);
synchronized(loadsInFlight) {
// Only remove from the hashmap if the task there is the one that's being canceled.
// Cancellation of a task that would have chained is not interesting to the hashmap.
final LoadFaviconTask primary = loadsInFlight.get(mFaviconUrl);
final LoadFaviconTask primary = loadsInFlight.get(faviconURL);
if (primary == this) {
loadsInFlight.remove(mFaviconUrl);
loadsInFlight.remove(faviconURL);
return;
}
if (primary == null) {
// This shouldn't happen.
return;
}
if (primary.mChainees != null) {
primary.mChainees.remove(this);
if (primary.chainees != null) {
primary.chainees.remove(this);
}
}
@ -518,15 +518,15 @@ public class LoadFaviconTask extends UiAsyncTask<Void, Void, Bitmap> {
* @param aChainee LoadFaviconTask
*/
private void chainTasks(LoadFaviconTask aChainee) {
if (mChainees == null) {
mChainees = new LinkedList<LoadFaviconTask>();
if (chainees == null) {
chainees = new LinkedList<LoadFaviconTask>();
}
mChainees.add(aChainee);
chainees.add(aChainee);
}
int getId() {
return mId;
return id;
}
static void closeHTTPClient() {
@ -534,8 +534,8 @@ public class LoadFaviconTask extends UiAsyncTask<Void, Void, Bitmap> {
// the connection pool, which typically involves closing a connection --
// which counts as network activity.
if (ThreadUtils.isOnBackgroundThread()) {
if (sHttpClient != null) {
sHttpClient.close();
if (httpClient != null) {
httpClient.close();
}
return;
}

View File

@ -20,7 +20,7 @@ import java.util.concurrent.atomic.AtomicInteger;
* When a favicon at a particular URL is decoded, it will yield one or more bitmaps.
* While in memory, these bitmaps are stored in a list, sorted in ascending order of size, in a
* FaviconsForURL object.
* The collection of FaviconsForURL objects currently in the cache is stored in mBackingMap, keyed
* The collection of FaviconsForURL objects currently in the cache is stored in backingMap, keyed
* by favicon URL.
*
* A second map exists for permanent cache entries -- ones that are never expired. These entries
@ -59,7 +59,7 @@ import java.util.concurrent.atomic.AtomicInteger;
* as well as the bitmap, a pointer to the encapsulating FaviconsForURL object (Used by the LRU
* culler), the size of the encapsulated image, a flag indicating if this is a primary favicon, and
* a flag indicating if the entry is invalid.
* All FaviconCacheElement objects are tracked in the mOrdering LinkedList. This is used to record
* All FaviconCacheElement objects are tracked in the ordering LinkedList. This is used to record
* LRU information about FaviconCacheElements. In particular, the most recently used FaviconCacheElement
* will be at the start of the list, the least recently used at the end of the list.
*
@ -98,7 +98,7 @@ public class FaviconCache {
private static final int NUM_FAVICON_SIZES = 4;
// Dimensions of the largest favicon to store in the cache. Everything is downscaled to this.
public final int mMaxCachedWidth;
public final int maxCachedWidth;
// Retry failed favicons after 20 minutes.
public static final long FAILURE_RETRY_MILLISECONDS = 1000 * 60 * 20;
@ -107,16 +107,16 @@ public class FaviconCache {
// Since favicons may be container formats holding multiple icons, the underlying type holds a
// sorted list of bitmap payloads in ascending order of size. The underlying type may be queried
// for the least larger payload currently present.
private final ConcurrentHashMap<String, FaviconsForURL> mBackingMap = new ConcurrentHashMap<String, FaviconsForURL>();
private final ConcurrentHashMap<String, FaviconsForURL> backingMap = new ConcurrentHashMap<String, FaviconsForURL>();
// And the same, but never evicted.
private final ConcurrentHashMap<String, FaviconsForURL> mPermanentBackingMap = new ConcurrentHashMap<String, FaviconsForURL>();
private final ConcurrentHashMap<String, FaviconsForURL> permanentBackingMap = new ConcurrentHashMap<String, FaviconsForURL>();
// A linked list used to implement a queue, defining the LRU properties of the cache. Elements
// contained within the various FaviconsForURL objects are held here, the least recently used
// of which at the end of the list. When space needs to be reclaimed, the appropriate bitmap is
// culled.
private final LinkedList<FaviconCacheElement> mOrdering = new LinkedList<FaviconCacheElement>();
private final LinkedList<FaviconCacheElement> ordering = new LinkedList<FaviconCacheElement>();
// The above structures, if used correctly, enable this cache to exhibit LRU semantics across all
// favicon payloads in the system, as well as enabling the dynamic selection from the cache of
@ -124,38 +124,38 @@ public class FaviconCache {
// are provided by the underlying file format).
// Current size, in bytes, of the bitmap data present in the LRU cache.
private final AtomicInteger mCurrentSize = new AtomicInteger(0);
private final AtomicInteger currentSize = new AtomicInteger(0);
// The maximum quantity, in bytes, of bitmap data which may be stored in the cache.
private final int mMaxSizeBytes;
private final int maxSizeBytes;
// Tracks the number of ongoing read operations. Enables the first one in to lock writers out and
// the last one out to let them in.
private final AtomicInteger mOngoingReads = new AtomicInteger(0);
private final AtomicInteger ongoingReads = new AtomicInteger(0);
// Used to ensure transaction fairness - each txn acquires and releases this as the first operation.
// The effect is an orderly, inexpensive ordering enforced on txns to prevent writer starvation.
private final Semaphore mTurnSemaphore = new Semaphore(1);
private final Semaphore turnSemaphore = new Semaphore(1);
// A deviation from the usual MRSW solution - this semaphore is used to guard modification to the
// ordering map. This allows for read transactions to update the most-recently-used value without
// needing to take out the write lock.
private final Semaphore mReorderingSemaphore = new Semaphore(1);
private final Semaphore reorderingSemaphore = new Semaphore(1);
// The semaphore one must acquire in order to perform a write.
private final Semaphore mWriteLock = new Semaphore(1);
private final Semaphore writeLock = new Semaphore(1);
/**
* Called by txns performing only reads as they start. Prevents writer starvation with a turn
* semaphore and locks writers out if this is the first concurrent reader txn starting up.
*/
private void startRead() {
mTurnSemaphore.acquireUninterruptibly();
mTurnSemaphore.release();
turnSemaphore.acquireUninterruptibly();
turnSemaphore.release();
if (mOngoingReads.incrementAndGet() == 1) {
if (ongoingReads.incrementAndGet() == 1) {
// First one in. Wait for writers to finish and lock them out.
mWriteLock.acquireUninterruptibly();
writeLock.acquireUninterruptibly();
}
}
@ -164,8 +164,8 @@ public class FaviconCache {
* concluding read transaction then then writers are subsequently allowed in.
*/
private void finishRead() {
if (mOngoingReads.decrementAndGet() == 0) {
mWriteLock.release();
if (ongoingReads.decrementAndGet() == 0) {
writeLock.release();
}
}
@ -174,21 +174,21 @@ public class FaviconCache {
* Upon return, no other txns will be executing concurrently.
*/
private void startWrite() {
mTurnSemaphore.acquireUninterruptibly();
mWriteLock.acquireUninterruptibly();
turnSemaphore.acquireUninterruptibly();
writeLock.acquireUninterruptibly();
}
/**
* Called by a concluding write transaction - unlocks the structure.
*/
private void finishWrite() {
mTurnSemaphore.release();
mWriteLock.release();
turnSemaphore.release();
writeLock.release();
}
public FaviconCache(int maxSize, int maxWidthToCache) {
mMaxSizeBytes = maxSize;
mMaxCachedWidth = maxWidthToCache;
maxSizeBytes = maxSize;
maxCachedWidth = maxWidthToCache;
}
/**
@ -208,19 +208,19 @@ public class FaviconCache {
try {
// If we don't have it in the cache, it certainly isn't a known failure.
// Non-evictable favicons are never failed, so we don't need to
// check mPermanentBackingMap.
if (!mBackingMap.containsKey(faviconURL)) {
// check permanentBackingMap.
if (!backingMap.containsKey(faviconURL)) {
return false;
}
FaviconsForURL container = mBackingMap.get(faviconURL);
FaviconsForURL container = backingMap.get(faviconURL);
// If the has failed flag is not set, it's certainly not a known failure.
if (!container.mHasFailed) {
if (!container.hasFailed) {
return false;
}
final long failureTimestamp = container.mDownloadTimestamp;
final long failureTimestamp = container.downloadTimestamp;
// Calculate elapsed time since the failing download.
final long failureDiff = System.currentTimeMillis() - failureTimestamp;
@ -240,7 +240,7 @@ public class FaviconCache {
// If the entry is no longer failed, remove the record of it from the cache.
try {
recordRemoved(mBackingMap.remove(faviconURL));
recordRemoved(backingMap.remove(faviconURL));
return false;
} finally {
finishWrite();
@ -257,7 +257,7 @@ public class FaviconCache {
try {
FaviconsForURL container = new FaviconsForURL(0, true);
recordRemoved(mBackingMap.put(faviconURL, container));
recordRemoved(backingMap.put(faviconURL, container));
} finally {
finishWrite();
}
@ -288,9 +288,9 @@ public class FaviconCache {
startRead();
try {
container = mPermanentBackingMap.get(faviconURL);
container = permanentBackingMap.get(faviconURL);
if (container == null) {
container = mBackingMap.get(faviconURL);
container = backingMap.get(faviconURL);
if (container == null) {
// We don't have it!
return null;
@ -307,22 +307,22 @@ public class FaviconCache {
// cacheElementIndex now holds either the index of the next least largest bitmap from
// targetSize, or -1 if targetSize > all bitmaps.
if (cacheElementIndex != -1) {
// If cacheElementIndex is not the sentinel value, then it is a valid index into mFavicons.
cacheElement = container.mFavicons.get(cacheElementIndex);
// If cacheElementIndex is not the sentinel value, then it is a valid index into favicons.
cacheElement = container.favicons.get(cacheElementIndex);
if (cacheElement.mInvalidated) {
if (cacheElement.invalidated) {
return null;
}
// If we found exactly what we wanted - we're done.
if (cacheElement.mImageSize == targetSize) {
if (cacheElement.imageSize == targetSize) {
setMostRecentlyUsedWithinRead(cacheElement);
return cacheElement.mFaviconPayload;
return cacheElement.faviconPayload;
}
} else {
// We requested an image larger than all primaries. Set the element to start the search
// from to the element beyond the end of the array, so the search runs backwards.
cacheElementIndex = container.mFavicons.size();
cacheElementIndex = container.favicons.size();
}
// We did not find exactly what we wanted, but now have set cacheElementIndex to the index
@ -339,12 +339,12 @@ public class FaviconCache {
if (targetSize == -1) {
// We got the biggest primary, so that's what we'll return.
return cacheElement.mFaviconPayload;
return cacheElement.faviconPayload;
}
// Scaling logic...
Bitmap largestElementBitmap = cacheElement.mFaviconPayload;
int largestSize = cacheElement.mImageSize;
Bitmap largestElementBitmap = cacheElement.faviconPayload;
int largestSize = cacheElement.imageSize;
if (largestSize >= targetSize) {
// The largest we have is larger than the target - downsize to target.
@ -389,7 +389,7 @@ public class FaviconCache {
if (!wasPermanent) {
if (setMostRecentlyUsedWithinWrite(newElement)) {
mCurrentSize.addAndGet(newElement.sizeOf());
currentSize.addAndGet(newElement.sizeOf());
}
}
} finally {
@ -409,14 +409,14 @@ public class FaviconCache {
startRead();
try {
FaviconsForURL element = mPermanentBackingMap.get(key);
FaviconsForURL element = permanentBackingMap.get(key);
if (element == null) {
element = mBackingMap.get(key);
element = backingMap.get(key);
}
if (element == null) {
Log.w(LOGTAG, "Cannot compute dominant color of non-cached favicon. Cache fullness " +
mCurrentSize.get() + '/' + mMaxSizeBytes);
currentSize.get() + '/' + maxSizeBytes);
finishRead();
return 0xFFFFFF;
}
@ -441,25 +441,25 @@ public class FaviconCache {
int sizeRemoved = 0;
for (FaviconCacheElement e : wasRemoved.mFavicons) {
for (FaviconCacheElement e : wasRemoved.favicons) {
sizeRemoved += e.sizeOf();
mOrdering.remove(e);
ordering.remove(e);
}
mCurrentSize.addAndGet(-sizeRemoved);
currentSize.addAndGet(-sizeRemoved);
}
private Bitmap produceCacheableBitmap(Bitmap favicon) {
// Never cache the default Favicon, or the null Favicon.
if (favicon == Favicons.sDefaultFavicon || favicon == null) {
if (favicon == Favicons.defaultFavicon || favicon == null) {
return null;
}
// Some sites serve up insanely huge Favicons (Seen 512x512 ones...)
// While we want to cache nice big icons, we apply a limit based on screen density for the
// sake of space.
if (favicon.getWidth() > mMaxCachedWidth) {
return Bitmap.createScaledBitmap(favicon, mMaxCachedWidth, mMaxCachedWidth, true);
if (favicon.getWidth() > maxCachedWidth) {
return Bitmap.createScaledBitmap(favicon, maxCachedWidth, maxCachedWidth, true);
}
return favicon;
@ -473,13 +473,13 @@ public class FaviconCache {
* @return true if this element already existed in the list, false otherwise. (Useful for preventing multiple-insertion.)
*/
private boolean setMostRecentlyUsedWithinRead(FaviconCacheElement element) {
mReorderingSemaphore.acquireUninterruptibly();
reorderingSemaphore.acquireUninterruptibly();
try {
boolean contained = mOrdering.remove(element);
mOrdering.offer(element);
boolean contained = ordering.remove(element);
ordering.offer(element);
return contained;
} finally {
mReorderingSemaphore.release();
reorderingSemaphore.release();
}
}
@ -491,8 +491,8 @@ public class FaviconCache {
* @return true if this element already existed in the list, false otherwise. (Useful for preventing multiple-insertion.)
*/
private boolean setMostRecentlyUsedWithinWrite(FaviconCacheElement element) {
boolean contained = mOrdering.remove(element);
mOrdering.offer(element);
boolean contained = ordering.remove(element);
ordering.offer(element);
return contained;
}
@ -523,11 +523,11 @@ public class FaviconCache {
// Set the new element as the most recently used one.
setMostRecentlyUsedWithinWrite(newElement);
mCurrentSize.addAndGet(newElement.sizeOf());
currentSize.addAndGet(newElement.sizeOf());
// Update the value in the LruCache...
FaviconsForURL wasRemoved;
wasRemoved = mBackingMap.put(faviconURL, toInsert);
wasRemoved = backingMap.put(faviconURL, toInsert);
recordRemoved(wasRemoved);
} finally {
@ -562,20 +562,20 @@ public class FaviconCache {
startWrite();
try {
if (permanently) {
mPermanentBackingMap.put(faviconURL, toInsert);
permanentBackingMap.put(faviconURL, toInsert);
return;
}
for (FaviconCacheElement newElement : toInsert.mFavicons) {
for (FaviconCacheElement newElement : toInsert.favicons) {
setMostRecentlyUsedWithinWrite(newElement);
}
// In the event this insertion is being made to a key that already held a value, the subsequent recordRemoved
// call will subtract the size of the old value, preventing double-counting.
mCurrentSize.addAndGet(sizeGained);
currentSize.addAndGet(sizeGained);
// Update the value in the LruCache...
recordRemoved(mBackingMap.put(faviconURL, toInsert));
recordRemoved(backingMap.put(faviconURL, toInsert));
} finally {
finishWrite();
}
@ -588,24 +588,24 @@ public class FaviconCache {
* Otherwise, do nothing.
*/
private void cullIfRequired() {
Log.d(LOGTAG, "Favicon cache fullness: " + mCurrentSize.get() + '/' + mMaxSizeBytes);
Log.d(LOGTAG, "Favicon cache fullness: " + currentSize.get() + '/' + maxSizeBytes);
if (mCurrentSize.get() <= mMaxSizeBytes) {
if (currentSize.get() <= maxSizeBytes) {
return;
}
startWrite();
try {
while (mCurrentSize.get() > mMaxSizeBytes) {
while (currentSize.get() > maxSizeBytes) {
// Cull the least recently used element.
FaviconCacheElement victim;
victim = mOrdering.poll();
victim = ordering.poll();
mCurrentSize.addAndGet(-victim.sizeOf());
currentSize.addAndGet(-victim.sizeOf());
victim.onEvictedFromCache();
Log.d(LOGTAG, "After cull: " + mCurrentSize.get() + '/' + mMaxSizeBytes);
Log.d(LOGTAG, "After cull: " + currentSize.get() + '/' + maxSizeBytes);
}
} finally {
finishWrite();
@ -620,9 +620,9 @@ public class FaviconCache {
// Note that we neither clear, nor track the size of, the permanent map.
try {
mCurrentSize.set(0);
mBackingMap.clear();
mOrdering.clear();
currentSize.set(0);
backingMap.clear();
ordering.clear();
} finally {
finishWrite();

View File

@ -12,49 +12,49 @@ import android.graphics.Bitmap;
*/
public class FaviconCacheElement implements Comparable<FaviconCacheElement> {
// Was this Favicon computed via scaling another primary Favicon, or is this a primary Favicon?
final boolean mIsPrimary;
final boolean isPrimary;
// The Favicon bitmap.
Bitmap mFaviconPayload;
Bitmap faviconPayload;
// If set, mFaviconPayload is absent. Since the underlying ICO may contain multiple primary
// If set, faviconPayload is absent. Since the underlying ICO may contain multiple primary
// payloads, primary payloads are never truly deleted from the cache, but instead have their
// payload deleted and this flag set on their FaviconCacheElement. That way, the cache always
// has a record of the existence of a primary payload, even if it is no longer in the cache.
// This means that when a request comes in that will be best served using a primary that is in
// the database but no longer cached, we know that it exists and can go get it (Useful when ICO
// support is added).
volatile boolean mInvalidated;
volatile boolean invalidated;
final int mImageSize;
final int imageSize;
// Used for LRU pruning.
final FaviconsForURL mBackpointer;
final FaviconsForURL backpointer;
public FaviconCacheElement(Bitmap payload, boolean isPrimary, int imageSize, FaviconsForURL backpointer) {
mFaviconPayload = payload;
mIsPrimary = isPrimary;
mImageSize = imageSize;
mBackpointer = backpointer;
public FaviconCacheElement(Bitmap payload, boolean primary, int size, FaviconsForURL backpointer) {
this.faviconPayload = payload;
this.isPrimary = primary;
this.imageSize = size;
this.backpointer = backpointer;
}
public FaviconCacheElement(Bitmap payload, boolean isPrimary, FaviconsForURL backpointer) {
mFaviconPayload = payload;
mIsPrimary = isPrimary;
mBackpointer = backpointer;
public FaviconCacheElement(Bitmap faviconPayload, boolean isPrimary, FaviconsForURL backpointer) {
this.faviconPayload = faviconPayload;
this.isPrimary = isPrimary;
this.backpointer = backpointer;
if (payload != null) {
mImageSize = payload.getWidth();
if (faviconPayload != null) {
imageSize = faviconPayload.getWidth();
} else {
mImageSize = 0;
imageSize = 0;
}
}
public int sizeOf() {
if (mInvalidated) {
if (invalidated) {
return 0;
}
return mFaviconPayload.getRowBytes() * mFaviconPayload.getHeight();
return faviconPayload.getRowBytes() * faviconPayload.getHeight();
}
/**
@ -68,20 +68,20 @@ public class FaviconCacheElement implements Comparable<FaviconCacheElement> {
*/
@Override
public int compareTo(FaviconCacheElement another) {
if (mInvalidated && !another.mInvalidated) {
if (invalidated && !another.invalidated) {
return -1;
}
if (!mInvalidated && another.mInvalidated) {
if (!invalidated && another.invalidated) {
return 1;
}
if (mInvalidated) {
if (invalidated) {
return 0;
}
final int w1 = mImageSize;
final int w2 = another.mImageSize;
final int w1 = imageSize;
final int w2 = another.imageSize;
if (w1 > w2) {
return 1;
} else if (w2 > w1) {
@ -96,20 +96,20 @@ public class FaviconCacheElement implements Comparable<FaviconCacheElement> {
* If primary, drop the payload and set invalid. If secondary, just unlink from parent node.
*/
public void onEvictedFromCache() {
if (mIsPrimary) {
if (isPrimary) {
// So we keep a record of which primaries exist in the database for this URL, we
// don't actually delete the entry for primaries. Instead, we delete their payload
// and flag them as invalid. This way, we can later figure out that what a request
// really want is one of the primaries that have been dropped from the cache, and we
// can go get it.
mInvalidated = true;
mFaviconPayload = null;
invalidated = true;
faviconPayload = null;
} else {
// Secondaries don't matter - just delete them.
if (mBackpointer == null) {
if (backpointer == null) {
return;
}
mBackpointer.mFavicons.remove(this);
backpointer.favicons.remove(this);
}
}
}

View File

@ -14,21 +14,21 @@ import java.util.Collections;
public class FaviconsForURL {
private static final String LOGTAG = "FaviconForURL";
private volatile int mDominantColor = -1;
private volatile int dominantColor = -1;
final long mDownloadTimestamp;
final ArrayList<FaviconCacheElement> mFavicons;
final long downloadTimestamp;
final ArrayList<FaviconCacheElement> favicons;
public final boolean mHasFailed;
public final boolean hasFailed;
public FaviconsForURL(int size) {
this(size, false);
}
public FaviconsForURL(int size, boolean hasFailed) {
mHasFailed = hasFailed;
mDownloadTimestamp = System.currentTimeMillis();
mFavicons = new ArrayList<FaviconCacheElement>(size);
public FaviconsForURL(int size, boolean failed) {
hasFailed = failed;
downloadTimestamp = System.currentTimeMillis();
favicons = new ArrayList<FaviconCacheElement>(size);
}
public FaviconCacheElement addSecondary(Bitmap favicon, int imageSize) {
@ -42,19 +42,19 @@ public class FaviconsForURL {
private FaviconCacheElement addInternal(Bitmap favicon, boolean isPrimary, int imageSize) {
FaviconCacheElement c = new FaviconCacheElement(favicon, isPrimary, imageSize, this);
int index = Collections.binarySearch(mFavicons, c);
int index = Collections.binarySearch(favicons, c);
// We've already got an equivalent one. We don't care about this new one. This only occurs in certain obscure
// case conditions.
if (index >= 0) {
return mFavicons.get(index);
return favicons.get(index);
}
// binarySearch returns -x - 1 where x is the insertion point of the element. Convert
// this to the actual insertion point..
index++;
index = -index;
mFavicons.add(index, c);
favicons.add(index, c);
return c;
}
@ -70,7 +70,7 @@ public class FaviconsForURL {
// Create a dummy object to hold the target value for comparable.
FaviconCacheElement dummy = new FaviconCacheElement(null, false, targetSize, null);
int index = Collections.binarySearch(mFavicons, dummy);
int index = Collections.binarySearch(favicons, dummy);
// The search routine returns the index of an element equal to dummy, if present.
// Otherwise, it returns -x - 1, where x is the index in the ArrayList where dummy would be
@ -82,11 +82,11 @@ public class FaviconsForURL {
// index is now 'x', as described above.
// The routine will return mFavicons.size() as the index iff dummy is larger than all elements
// The routine will return favicons.size() as the index iff dummy is larger than all elements
// present (So the "index at which it should be inserted" is the index after the end.
// In this case, we set the sentinel value -1 to indicate that we just requested something
// larger than all primaries.
if (index == mFavicons.size()) {
if (index == favicons.size()) {
index = -1;
}
@ -101,19 +101,19 @@ public class FaviconsForURL {
* primary at all (The input index may be a secondary which is larger than the actual available
* primary.)
*
* @param fromIndex The index into mFavicons from which to start the search.
* @param fromIndex The index into favicons from which to start the search.
* @return The FaviconCacheElement of the next valid primary from the given index. If none exists,
* then returns the previous valid primary. If none exists, returns null (Insanity.).
*/
public FaviconCacheElement getNextPrimary(final int fromIndex) {
final int numIcons = mFavicons.size();
final int numIcons = favicons.size();
int searchIndex = fromIndex;
while (searchIndex < numIcons) {
FaviconCacheElement element = mFavicons.get(searchIndex);
FaviconCacheElement element = favicons.get(searchIndex);
if (element.mIsPrimary) {
if (element.mInvalidated) {
if (element.isPrimary) {
if (element.invalidated) {
// We return null here, despite the possible existence of other primaries,
// because we know the most suitable primary for this request exists, but is
// no longer in the cache. By returning null, we cause the caller to load the
@ -128,10 +128,10 @@ public class FaviconsForURL {
// No larger primary available. Let's look for smaller ones...
searchIndex = fromIndex - 1;
while (searchIndex >= 0) {
FaviconCacheElement element = mFavicons.get(searchIndex);
FaviconCacheElement element = favicons.get(searchIndex);
if (element.mIsPrimary) {
if (element.mInvalidated) {
if (element.isPrimary) {
if (element.invalidated) {
return null;
}
return element;
@ -148,17 +148,17 @@ public class FaviconsForURL {
* Ensure the dominant colour field is populated for this favicon.
*/
public int ensureDominantColor() {
if (mDominantColor == -1) {
if (dominantColor == -1) {
// Find a payload, any payload, that is not invalidated.
for (FaviconCacheElement element : mFavicons) {
if (!element.mInvalidated) {
mDominantColor = BitmapUtils.getDominantColor(element.mFaviconPayload);
return mDominantColor;
for (FaviconCacheElement element : favicons) {
if (!element.invalidated) {
dominantColor = BitmapUtils.getDominantColor(element.faviconPayload);
return dominantColor;
}
}
mDominantColor = 0xFFFFFF;
dominantColor = 0xFFFFFF;
}
return mDominantColor;
return dominantColor;
}
}

View File

@ -89,14 +89,14 @@ public class FaviconDecoder {
LoadFaviconResult result;
if (isDecodableByAndroid(buffer, offset)) {
result = new LoadFaviconResult();
result.mOffset = offset;
result.mLength = length;
result.mIsICO = false;
result.offset = offset;
result.length = length;
result.isICO = false;
// We assume here that decodeByteArray doesn't hold on to the entire supplied
// buffer -- worst case, each of our buffers will be twice the necessary size.
result.mBitmapsDecoded = new SingleBitmapIterator(BitmapUtils.decodeByteArray(buffer, offset, length));
result.mFaviconBytes = buffer;
result.bitmapsDecoded = new SingleBitmapIterator(BitmapUtils.decodeByteArray(buffer, offset, length));
result.faviconBytes = buffer;
return result;
}
@ -193,10 +193,10 @@ public class FaviconDecoder {
* Iterator to hold a single bitmap.
*/
static class SingleBitmapIterator implements Iterator<Bitmap> {
private Bitmap mBitmap;
private Bitmap bitmap;
public SingleBitmapIterator(Bitmap b) {
mBitmap = b;
bitmap = b;
}
/**
@ -207,22 +207,22 @@ public class FaviconDecoder {
* @return The bitmap carried by this SingleBitmapIterator.
*/
public Bitmap peek() {
return mBitmap;
return bitmap;
}
@Override
public boolean hasNext() {
return mBitmap != null;
return bitmap != null;
}
@Override
public Bitmap next() {
if (mBitmap == null) {
if (bitmap == null) {
throw new NoSuchElementException("Element already returned from SingleBitmapIterator.");
}
Bitmap ret = mBitmap;
mBitmap = null;
Bitmap ret = bitmap;
bitmap = null;
return ret;
}

View File

@ -10,7 +10,6 @@ import org.mozilla.gecko.gfx.BitmapUtils;
import android.util.SparseArray;
import java.util.Collection;
import java.util.Iterator;
import java.util.NoSuchElementException;
@ -79,55 +78,55 @@ public class ICODecoder implements Iterable<Bitmap> {
public static final int ICO_ICONDIRENTRY_LENGTH_BYTES = 16;
// The buffer containing bytes to attempt to decode.
private byte[] mDecodand;
private byte[] decodand;
// The region of the decodand to decode.
private int mOffset;
private int mLen;
private int offset;
private int len;
private IconDirectoryEntry[] mIconDirectory;
private boolean mIsValid;
private boolean mHasDecoded;
private IconDirectoryEntry[] iconDirectory;
private boolean isValid;
private boolean hasDecoded;
public ICODecoder(byte[] buffer, int offset, int len) {
mDecodand = buffer;
mOffset = offset;
mLen = len;
public ICODecoder(byte[] decodand, int offset, int len) {
this.decodand = decodand;
this.offset = offset;
this.len = len;
}
/**
* Decode the Icon Directory for this ICO and store the result in mIconDirectory.
* Decode the Icon Directory for this ICO and store the result in iconDirectory.
*
* @return true if ICO decoding was considered to probably be a success, false if it certainly
* was a failure.
*/
private boolean decodeIconDirectoryAndPossiblyPrune() {
mHasDecoded = true;
hasDecoded = true;
// Fail if the end of the described range is out of bounds.
if (mOffset + mLen > mDecodand.length) {
if (offset + len > decodand.length) {
return false;
}
// Fail if we don't have enough space for the header.
if (mLen < ICO_HEADER_LENGTH_BYTES) {
if (len < ICO_HEADER_LENGTH_BYTES) {
return false;
}
// Check that the reserved fields in the header are indeed zero, and that the type field
// specifies ICO. If not, we've probably been given something that isn't really an ICO.
if (mDecodand[mOffset] != 0 ||
mDecodand[mOffset + 1] != 0 ||
mDecodand[mOffset + 2] != 1 ||
mDecodand[mOffset + 3] != 0) {
if (decodand[offset] != 0 ||
decodand[offset + 1] != 0 ||
decodand[offset + 2] != 1 ||
decodand[offset + 3] != 0) {
return false;
}
// Here, and in many other places, byte values are ANDed with 0xFF. This is because Java
// bytes are signed - to obtain a numerical value of a longer type which holds the unsigned
// interpretation of the byte of interest, we do this.
int numEncodedImages = (mDecodand[mOffset + 4] & 0xFF) |
(mDecodand[mOffset + 5] & 0xFF) << 8;
int numEncodedImages = (decodand[offset + 4] & 0xFF) |
(decodand[offset + 5] & 0xFF) << 8;
// Fail if there are no images or the field is corrupt.
@ -139,12 +138,12 @@ public class ICODecoder implements Iterable<Bitmap> {
// Fail if there is not enough space in the buffer for the stated number of icondir entries,
// let alone the data.
if (mLen < headerAndDirectorySize) {
if (len < headerAndDirectorySize) {
return false;
}
// Put the pointer on the first byte of the first Icon Directory Entry.
int bufferIndex = mOffset + ICO_HEADER_LENGTH_BYTES;
int bufferIndex = offset + ICO_HEADER_LENGTH_BYTES;
// We now iterate over the Icon Directory, decoding each entry as we go. We also need to
// discard all entries except one >= the maximum interesting size.
@ -157,35 +156,35 @@ public class ICODecoder implements Iterable<Bitmap> {
for (int i = 0; i < numEncodedImages; i++, bufferIndex += ICO_ICONDIRENTRY_LENGTH_BYTES) {
// Decode the Icon Directory Entry at this offset.
IconDirectoryEntry newEntry = IconDirectoryEntry.createFromBuffer(mDecodand, mOffset, mLen, bufferIndex);
newEntry.mIndex = i;
IconDirectoryEntry newEntry = IconDirectoryEntry.createFromBuffer(decodand, offset, len, bufferIndex);
newEntry.index = i;
if (newEntry.mIsErroneous) {
if (newEntry.isErroneous) {
continue;
}
if (newEntry.mWidth > Favicons.sLargestFaviconSize) {
if (newEntry.width > Favicons.largestFaviconSize) {
// If we already have a smaller image larger than the maximum size of interest, we
// don't care about the new one which is larger than the smallest image larger than
// the maximum size.
if (newEntry.mWidth >= minimumMaximum) {
if (newEntry.width >= minimumMaximum) {
continue;
}
// Remove the previous minimum-maximum.
preferenceArray.delete(minimumMaximum);
minimumMaximum = newEntry.mWidth;
minimumMaximum = newEntry.width;
}
IconDirectoryEntry oldEntry = preferenceArray.get(newEntry.mWidth);
IconDirectoryEntry oldEntry = preferenceArray.get(newEntry.width);
if (oldEntry == null) {
preferenceArray.put(newEntry.mWidth, newEntry);
preferenceArray.put(newEntry.width, newEntry);
continue;
}
if (oldEntry.compareTo(newEntry) < 0) {
preferenceArray.put(newEntry.mWidth, newEntry);
preferenceArray.put(newEntry.width, newEntry);
}
}
@ -197,24 +196,24 @@ public class ICODecoder implements Iterable<Bitmap> {
}
// Allocate space for the icon directory entries in the decoded directory.
mIconDirectory = new IconDirectoryEntry[count];
iconDirectory = new IconDirectoryEntry[count];
// The size of the data in the buffer that we find useful.
int retainedSpace = ICO_HEADER_LENGTH_BYTES;
for (int i = 0; i < count; i++) {
IconDirectoryEntry e = preferenceArray.valueAt(i);
retainedSpace += ICO_ICONDIRENTRY_LENGTH_BYTES + e.mPayloadSize;
mIconDirectory[i] = e;
retainedSpace += ICO_ICONDIRENTRY_LENGTH_BYTES + e.payloadSize;
iconDirectory[i] = e;
}
mIsValid = true;
isValid = true;
// Set the number of images field in the buffer to reflect the number of retained entries.
mDecodand[mOffset + 4] = (byte) mIconDirectory.length;
mDecodand[mOffset + 5] = (byte) (mIconDirectory.length >>> 8);
decodand[offset + 4] = (byte) iconDirectory.length;
decodand[offset + 5] = (byte) (iconDirectory.length >>> 8);
if ((mLen - retainedSpace) > COMPACT_THRESHOLD) {
if ((len - retainedSpace) > COMPACT_THRESHOLD) {
compactingCopy(retainedSpace);
}
@ -228,19 +227,19 @@ public class ICODecoder implements Iterable<Bitmap> {
byte[] buf = new byte[spaceRetained];
// Copy the header.
System.arraycopy(mDecodand, mOffset, buf, 0, ICO_HEADER_LENGTH_BYTES);
System.arraycopy(decodand, offset, buf, 0, ICO_HEADER_LENGTH_BYTES);
int headerPtr = ICO_HEADER_LENGTH_BYTES;
int payloadPtr = ICO_HEADER_LENGTH_BYTES + (mIconDirectory.length * ICO_ICONDIRENTRY_LENGTH_BYTES);
int payloadPtr = ICO_HEADER_LENGTH_BYTES + (iconDirectory.length * ICO_ICONDIRENTRY_LENGTH_BYTES);
int ind = 0;
for (IconDirectoryEntry entry : mIconDirectory) {
for (IconDirectoryEntry entry : iconDirectory) {
// Copy this entry.
System.arraycopy(mDecodand, mOffset + entry.getOffset(), buf, headerPtr, ICO_ICONDIRENTRY_LENGTH_BYTES);
System.arraycopy(decodand, offset + entry.getOffset(), buf, headerPtr, ICO_ICONDIRENTRY_LENGTH_BYTES);
// Copy its payload.
System.arraycopy(mDecodand, mOffset + entry.mPayloadOffset, buf, payloadPtr, entry.mPayloadSize);
System.arraycopy(decodand, offset + entry.payloadOffset, buf, payloadPtr, entry.payloadSize);
// Update the offset field.
buf[headerPtr + 12] = (byte) payloadPtr;
@ -248,17 +247,17 @@ public class ICODecoder implements Iterable<Bitmap> {
buf[headerPtr + 14] = (byte) (payloadPtr >>> 16);
buf[headerPtr + 15] = (byte) (payloadPtr >>> 24);
entry.mPayloadOffset = payloadPtr;
entry.mIndex = ind;
entry.payloadOffset = payloadPtr;
entry.index = ind;
payloadPtr += entry.mPayloadSize;
payloadPtr += entry.payloadSize;
headerPtr += ICO_ICONDIRENTRY_LENGTH_BYTES;
ind++;
}
mDecodand = buf;
mOffset = 0;
mLen = spaceRetained;
decodand = buf;
offset = 0;
len = spaceRetained;
}
/**
@ -269,16 +268,16 @@ public class ICODecoder implements Iterable<Bitmap> {
* fails.
*/
public Bitmap decodeBitmapAtIndex(int index) {
final IconDirectoryEntry iconDirEntry = mIconDirectory[index];
final IconDirectoryEntry iconDirEntry = iconDirectory[index];
if (iconDirEntry.mPayloadIsPNG) {
if (iconDirEntry.payloadIsPNG) {
// PNG payload. Simply extract it and decode it.
return BitmapUtils.decodeByteArray(mDecodand, mOffset + iconDirEntry.mPayloadOffset, iconDirEntry.mPayloadSize);
return BitmapUtils.decodeByteArray(decodand, offset + iconDirEntry.payloadOffset, iconDirEntry.payloadSize);
}
// The payload is a BMP, so we need to do some magic to get the decoder to do what we want.
// We construct an ICO containing just the image we want, and let Android do the rest.
byte[] decodeTarget = new byte[ICO_HEADER_LENGTH_BYTES + ICO_ICONDIRENTRY_LENGTH_BYTES + iconDirEntry.mPayloadSize];
byte[] decodeTarget = new byte[ICO_HEADER_LENGTH_BYTES + ICO_ICONDIRENTRY_LENGTH_BYTES + iconDirEntry.payloadSize];
// Set the type field in the ICO header.
decodeTarget[2] = 1;
@ -287,11 +286,11 @@ public class ICODecoder implements Iterable<Bitmap> {
decodeTarget[4] = 1;
// Copy the ICONDIRENTRY we need into the new buffer.
System.arraycopy(mDecodand, mOffset + iconDirEntry.getOffset(), decodeTarget, ICO_HEADER_LENGTH_BYTES, ICO_ICONDIRENTRY_LENGTH_BYTES);
System.arraycopy(decodand, offset + iconDirEntry.getOffset(), decodeTarget, ICO_HEADER_LENGTH_BYTES, ICO_ICONDIRENTRY_LENGTH_BYTES);
// Copy the payload into the new buffer.
final int singlePayloadOffset = ICO_HEADER_LENGTH_BYTES + ICO_ICONDIRENTRY_LENGTH_BYTES;
System.arraycopy(mDecodand, mOffset + iconDirEntry.mPayloadOffset, decodeTarget, singlePayloadOffset, iconDirEntry.mPayloadSize);
System.arraycopy(decodand, offset + iconDirEntry.payloadOffset, decodeTarget, singlePayloadOffset, iconDirEntry.payloadSize);
// Update the offset field of the ICONDIRENTRY to make the new ICO valid.
decodeTarget[ICO_HEADER_LENGTH_BYTES + 12] = (byte) singlePayloadOffset;
@ -311,12 +310,12 @@ public class ICODecoder implements Iterable<Bitmap> {
@Override
public ICOIterator iterator() {
// If a previous call to decode concluded this ICO is invalid, abort.
if (mHasDecoded && !mIsValid) {
if (hasDecoded && !isValid) {
return null;
}
// If we've not been decoded before, but now fail to make any sense of the ICO, abort.
if (!mHasDecoded) {
if (!hasDecoded) {
if (!decodeIconDirectoryAndPossiblyPrune()) {
return null;
}
@ -339,11 +338,11 @@ public class ICODecoder implements Iterable<Bitmap> {
LoadFaviconResult result = new LoadFaviconResult();
result.mBitmapsDecoded = bitmaps;
result.mFaviconBytes = mDecodand;
result.mOffset = mOffset;
result.mLength = mLen;
result.mIsICO = true;
result.bitmapsDecoded = bitmaps;
result.faviconBytes = decodand;
result.offset = offset;
result.length = len;
result.isICO = true;
return result;
}
@ -356,12 +355,12 @@ public class ICODecoder implements Iterable<Bitmap> {
@Override
public boolean hasNext() {
return mIndex < mIconDirectory.length;
return mIndex < iconDirectory.length;
}
@Override
public Bitmap next() {
if (mIndex > mIconDirectory.length) {
if (mIndex > iconDirectory.length) {
throw new NoSuchElementException("No more elements in this ICO.");
}
return decodeBitmapAtIndex(mIndex++);
@ -369,10 +368,10 @@ public class ICODecoder implements Iterable<Bitmap> {
@Override
public void remove() {
if (mIconDirectory[mIndex] == null) {
if (iconDirectory[mIndex] == null) {
throw new IllegalStateException("Remove already called for element " + mIndex);
}
mIconDirectory[mIndex] = null;
iconDirectory[mIndex] = null;
}
}
}

View File

@ -9,28 +9,28 @@ package org.mozilla.gecko.favicons.decoders;
*/
public class IconDirectoryEntry implements Comparable<IconDirectoryEntry> {
public static int sMaxBPP;
public static int maxBPP;
int mWidth;
int mHeight;
int mPaletteSize;
int mBitsPerPixel;
int mPayloadSize;
int mPayloadOffset;
boolean mPayloadIsPNG;
int width;
int height;
int paletteSize;
int bitsPerPixel;
int payloadSize;
int payloadOffset;
boolean payloadIsPNG;
// Tracks the index in the Icon Directory of this entry. Useful only for pruning.
int mIndex;
boolean mIsErroneous;
int index;
boolean isErroneous;
public IconDirectoryEntry(int width, int height, int paletteSize, int bitsPerPixel, int payloadSize, int payloadOffset, boolean payloadIsPNG) {
mWidth = width;
mHeight = height;
mPaletteSize = paletteSize;
mBitsPerPixel = bitsPerPixel;
mPayloadSize = payloadSize;
mPayloadOffset = payloadOffset;
mPayloadIsPNG = payloadIsPNG;
this.width = width;
this.height = height;
this.paletteSize = paletteSize;
this.bitsPerPixel = bitsPerPixel;
this.payloadSize = payloadSize;
this.payloadOffset = payloadOffset;
this.payloadIsPNG = payloadIsPNG;
}
/**
@ -40,7 +40,7 @@ public class IconDirectoryEntry implements Comparable<IconDirectoryEntry> {
*/
public static IconDirectoryEntry getErroneousEntry() {
IconDirectoryEntry ret = new IconDirectoryEntry(-1, -1, -1, -1, -1, -1, false);
ret.mIsErroneous = true;
ret.isErroneous = true;
return ret;
}
@ -118,63 +118,63 @@ public class IconDirectoryEntry implements Comparable<IconDirectoryEntry> {
* Get the number of bytes from the start of the ICO file to the beginning of this entry.
*/
public int getOffset() {
return ICODecoder.ICO_HEADER_LENGTH_BYTES + (mIndex * ICODecoder.ICO_ICONDIRENTRY_LENGTH_BYTES);
return ICODecoder.ICO_HEADER_LENGTH_BYTES + (index * ICODecoder.ICO_ICONDIRENTRY_LENGTH_BYTES);
}
@Override
public int compareTo(IconDirectoryEntry another) {
if (mWidth > another.mWidth) {
if (width > another.width) {
return 1;
}
if (mWidth < another.mWidth) {
if (width < another.width) {
return -1;
}
// Where both images exceed the max BPP, take the smaller of the two BPP values.
if (mBitsPerPixel >= sMaxBPP && another.mBitsPerPixel >= sMaxBPP) {
if (mBitsPerPixel < another.mBitsPerPixel) {
if (bitsPerPixel >= maxBPP && another.bitsPerPixel >= maxBPP) {
if (bitsPerPixel < another.bitsPerPixel) {
return 1;
}
if (mBitsPerPixel > another.mBitsPerPixel) {
if (bitsPerPixel > another.bitsPerPixel) {
return -1;
}
}
// Otherwise, take the larger of the BPP values.
if (mBitsPerPixel > another.mBitsPerPixel) {
if (bitsPerPixel > another.bitsPerPixel) {
return 1;
}
if (mBitsPerPixel < another.mBitsPerPixel) {
if (bitsPerPixel < another.bitsPerPixel) {
return -1;
}
// Prefer large palettes.
if (mPaletteSize > another.mPaletteSize) {
if (paletteSize > another.paletteSize) {
return 1;
}
if (mPaletteSize < another.mPaletteSize) {
if (paletteSize < another.paletteSize) {
return -1;
}
// Prefer smaller payloads.
if (mPayloadSize < another.mPayloadSize) {
if (payloadSize < another.payloadSize) {
return 1;
}
if (mPayloadSize > another.mPayloadSize) {
if (payloadSize > another.payloadSize) {
return -1;
}
// If all else fails, prefer PNGs over BMPs. They tend to be smaller.
if (mPayloadIsPNG && !another.mPayloadIsPNG) {
if (payloadIsPNG && !another.payloadIsPNG) {
return 1;
}
if (!mPayloadIsPNG && another.mPayloadIsPNG) {
if (!payloadIsPNG && another.payloadIsPNG) {
return -1;
}
@ -182,20 +182,20 @@ public class IconDirectoryEntry implements Comparable<IconDirectoryEntry> {
}
public static void setMaxBPP(int maxBPP) {
sMaxBPP = maxBPP;
IconDirectoryEntry.maxBPP = maxBPP;
}
@Override
public String toString() {
return "IconDirectoryEntry{" +
"\nmWidth=" + mWidth +
", \nmHeight=" + mHeight +
", \nmPaletteSize=" + mPaletteSize +
", \nmBitsPerPixel=" + mBitsPerPixel +
", \nmPayloadSize=" + mPayloadSize +
", \nmPayloadOffset=" + mPayloadOffset +
", \nmPayloadIsPNG=" + mPayloadIsPNG +
", \nmIndex=" + mIndex +
"\nwidth=" + width +
", \nheight=" + height +
", \npaletteSize=" + paletteSize +
", \nbitsPerPixel=" + bitsPerPixel +
", \npayloadSize=" + payloadSize +
", \npayloadOffset=" + payloadOffset +
", \npayloadIsPNG=" + payloadIsPNG +
", \nindex=" + index +
'}';
}
}

View File

@ -21,15 +21,15 @@ import java.util.Iterator;
public class LoadFaviconResult {
private static final String LOGTAG = "LoadFaviconResult";
byte[] mFaviconBytes;
int mOffset;
int mLength;
byte[] faviconBytes;
int offset;
int length;
boolean mIsICO;
Iterator<Bitmap> mBitmapsDecoded;
boolean isICO;
Iterator<Bitmap> bitmapsDecoded;
public Iterator<Bitmap> getBitmaps() {
return mBitmapsDecoded;
return bitmapsDecoded;
}
/**
@ -40,17 +40,17 @@ public class LoadFaviconResult {
*/
public byte[] getBytesForDatabaseStorage() {
// Begin by normalising the buffer.
if (mOffset != 0 || mLength != mFaviconBytes.length) {
final byte[] normalised = new byte[mLength];
System.arraycopy(mFaviconBytes, mOffset, normalised, 0, mLength);
mOffset = 0;
mFaviconBytes = normalised;
if (offset != 0 || length != faviconBytes.length) {
final byte[] normalised = new byte[length];
System.arraycopy(faviconBytes, offset, normalised, 0, length);
offset = 0;
faviconBytes = normalised;
}
// For results containing a single image, we re-encode the result as a PNG in an effort to
// save space.
if (!mIsICO) {
Bitmap favicon = ((FaviconDecoder.SingleBitmapIterator) mBitmapsDecoded).peek();
if (!isICO) {
Bitmap favicon = ((FaviconDecoder.SingleBitmapIterator) bitmapsDecoded).peek();
byte[] data = null;
ByteArrayOutputStream stream = new ByteArrayOutputStream();
@ -68,7 +68,7 @@ public class LoadFaviconResult {
// We may instead want to consider re-encoding the entire ICO as a collection of efficiently
// encoded PNGs. This may not be worth the CPU time (Indeed, the encoding of single-image
// favicons may also not be worth the time/space tradeoff.).
return mFaviconBytes;
return faviconBytes;
}
}

View File

@ -133,13 +133,13 @@ public class SearchEnginePreference extends CustomListPreference {
if (mFaviconView != null) {
desiredWidth = mFaviconView.getWidth();
} else {
// sLargestFaviconSize is initialized when Favicons is attached to a
// largestFaviconSize is initialized when Favicons is attached to a
// context, which occurs during GeckoApp.onCreate. That might not
// ever happen (leaving it at 0), so we fall back.
if (Favicons.sLargestFaviconSize == 0) {
if (Favicons.largestFaviconSize == 0) {
desiredWidth = 128;
} else {
desiredWidth = Favicons.sLargestFaviconSize;
desiredWidth = Favicons.largestFaviconSize;
}
}