mirror of
https://gitlab.winehq.org/wine/wine-gecko.git
synced 2024-09-13 09:24:08 -07:00
Bug 969417 - Cleanup naming conventions in favicon code. r=rnewman
This commit is contained in:
parent
6269828bc6
commit
73c812f40f
@ -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);
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -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;
|
||||
}
|
||||
|
146
mobile/android/base/favicons/cache/FaviconCache.java
vendored
146
mobile/android/base/favicons/cache/FaviconCache.java
vendored
@ -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();
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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 +
|
||||
'}';
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user