Bug 941868 - Part 2: load and cache certain preloaded favicons on launch, and remove favicon from about:home's HTML content. r=mcomella

This commit is contained in:
Richard Newman 2013-11-26 19:48:30 -08:00
parent fb5468e532
commit 3007c545eb
7 changed files with 158 additions and 21 deletions

View File

@ -39,5 +39,38 @@ public class AboutPages {
}
return url.startsWith(READER);
}
private static final String[] DEFAULT_ICON_PAGES = new String[] {
HOME,
ADDONS,
CONFIG,
DOWNLOADS,
FIREFOX,
HEALTHREPORT,
UPDATER
};
/**
* Callers must not modify the returned array.
*/
public static String[] getDefaultIconPages() {
return DEFAULT_ICON_PAGES;
}
public static boolean isDefaultIconPage(final String url) {
if (url == null ||
!url.startsWith("about:")) {
return false;
}
// TODO: it'd be quicker to not compare the "about:" part every time.
for (int i = 0; i < DEFAULT_ICON_PAGES.length; ++i) {
if (DEFAULT_ICON_PAGES[i].equals(url)) {
return true;
}
}
return false;
}
}

View File

@ -1229,7 +1229,7 @@ abstract public class BrowserApp extends GeckoApp
@Override
public void addTab() {
Tabs.getInstance().loadUrl(AboutPages.HOME, Tabs.LOADURL_NEW_TAB);
super.loadHomePage(Tabs.LOADURL_NEW_TAB);
}
@Override
@ -1401,7 +1401,7 @@ abstract public class BrowserApp extends GeckoApp
}
private void openReadingList() {
Tabs.getInstance().loadUrl(AboutPages.HOME, Tabs.LOADURL_READING_LIST);
super.loadHomePage(Tabs.LOADURL_READING_LIST);
}
/* Favicon stuff. */

View File

@ -1354,8 +1354,8 @@ abstract public class GeckoApp
if (url == null) {
if (!mShouldRestore) {
// Show about:home if we aren't restoring previous session and
// there's no external URL
Tab tab = Tabs.getInstance().loadUrl(AboutPages.HOME, Tabs.LOADURL_NEW_TAB);
// there's no external URL.
loadHomePage(Tabs.LOADURL_NEW_TAB);
}
} else {
// If given an external URL, load it
@ -1364,6 +1364,14 @@ abstract public class GeckoApp
}
}
protected Tab loadHomePage() {
return loadHomePage(Tabs.LOADURL_NONE);
}
protected Tab loadHomePage(int flags) {
return Tabs.getInstance().loadUrl(AboutPages.HOME, flags);
}
private void initialize() {
mInitialized = true;

View File

@ -660,8 +660,8 @@ public class Tabs implements GeckoEventListener {
*
* @param url URL of page to load, or search term used if searchEngine is given
*/
public void loadUrl(String url) {
loadUrl(url, LOADURL_NONE);
public Tab loadUrl(String url) {
return loadUrl(url, LOADURL_NONE);
}
/**
@ -731,13 +731,34 @@ public class Tabs implements GeckoEventListener {
GeckoAppShell.sendEventToGecko(GeckoEvent.createBroadcastEvent("Tab:Load", args.toString()));
if ((added != null) && !delayLoad && !background) {
if (added == null) {
return null;
}
if (!delayLoad && !background) {
selectTab(added.getId());
}
// TODO: surely we could just fetch *any* cached icon?
if (AboutPages.isDefaultIconPage(url)) {
Log.d(LOGTAG, "Setting about: tab favicon inline.");
added.updateFavicon(getAboutPageFavicon(url));
}
return added;
}
/**
* These favicons are only used for the URL bar, so
* we fetch with that size.
*
* This method completes on the calling thread.
*/
private Bitmap getAboutPageFavicon(final String url) {
int faviconSize = Math.round(mAppContext.getResources().getDimension(R.dimen.browser_toolbar_favicon_size));
return Favicons.getCachedFaviconForSize(url, faviconSize);
}
/**
* Open the url as a new tab, and mark the selected tab as its "parent".
*

View File

@ -6,23 +6,28 @@
package org.mozilla.gecko.favicons;
import org.mozilla.gecko.AboutPages;
import org.mozilla.gecko.AppConstants;
import org.mozilla.gecko.R;
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.ThreadUtils;
import org.mozilla.gecko.util.GeckoJarReader;
import org.mozilla.gecko.util.NonEvictingLruCache;
import org.mozilla.gecko.util.ThreadUtils;
import android.content.Context;
import android.content.res.Resources;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.text.TextUtils;
import android.util.Log;
import java.io.File;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
@ -32,6 +37,9 @@ import java.util.Set;
public class Favicons {
private static final String LOGTAG = "GeckoFavicons";
// A magic URL representing the app's own favicon, used for about: pages.
private static final String BUILT_IN_FAVICON_URL = "about:favicon";
// Size of the favicon bitmap cache, in bytes (Counting payload only).
public static final int FAVICON_CACHE_SIZE_BYTES = 512 * 1024;
@ -97,6 +105,20 @@ public class Favicons {
return NOT_LOADING;
}
/**
* Only returns a non-null Bitmap if the entire path is cached -- the
* page URL to favicon URL, and the favicon URL to in-memory bitmaps.
*
* Returns null otherwise.
*/
public static Bitmap getCachedFaviconForSize(final String pageURL, int targetSize) {
final String faviconURL = sPageURLMappings.get(pageURL);
if (faviconURL == null) {
return null;
}
return getSizedFaviconFromCache(faviconURL, targetSize);
}
/**
* Get a Favicon as close as possible to the target dimensions for the URL provided.
* If a result is instantly available from the cache, it is returned and the listener is invoked.
@ -113,8 +135,13 @@ public class Favicons {
* LOADED if the value could be dispatched on the current thread.
*/
public static int getFaviconForSize(String pageURL, String faviconURL, int targetSize, int flags, OnFaviconLoadedListener listener) {
// If there's no favicon URL given, try and hit the cache with the default one.
// Do we know the favicon URL for this page already?
String cacheURL = faviconURL;
if (cacheURL == null) {
cacheURL = sPageURLMappings.get(pageURL);
}
// If there's no favicon URL given, try and hit the cache with the default one.
if (cacheURL == null) {
cacheURL = guessDefaultFaviconURL(pageURL);
}
@ -328,16 +355,49 @@ public class Favicons {
* @param context A reference to the GeckoApp instance.
*/
public static void attachToContext(Context context) throws Exception {
final Resources res = context.getResources();
sContext = context;
// Decode the default Favicon ready for use.
sDefaultFavicon = BitmapFactory.decodeResource(context.getResources(), R.drawable.favicon);
sDefaultFavicon = BitmapFactory.decodeResource(res, R.drawable.favicon);
if (sDefaultFavicon == null) {
throw new Exception("Null default favicon was returned from the resources system!");
}
sDefaultFaviconSize = context.getResources().getDimensionPixelSize(R.dimen.favicon_bg);
sFaviconsCache = new FaviconCache(FAVICON_CACHE_SIZE_BYTES, context.getResources().getDimensionPixelSize(R.dimen.favicon_largest_interesting_size));
sDefaultFaviconSize = res.getDimensionPixelSize(R.dimen.favicon_bg);
sFaviconsCache = new FaviconCache(FAVICON_CACHE_SIZE_BYTES, res.getDimensionPixelSize(R.dimen.favicon_largest_interesting_size));
// Initialize page mappings for each of our special pages.
for (String url : AboutPages.getDefaultIconPages()) {
sPageURLMappings.putWithoutEviction(url, BUILT_IN_FAVICON_URL);
}
// Load and cache the built-in favicon in each of its sizes.
// TODO: don't open the zip twice!
ArrayList<Bitmap> toInsert = new ArrayList<Bitmap>(2);
toInsert.add(loadBrandingBitmap(context, "favicon64.png"));
toInsert.add(loadBrandingBitmap(context, "favicon32.png"));
putFaviconsInMemCache(BUILT_IN_FAVICON_URL, toInsert.iterator());
}
/**
* Compute a string like:
* "jar:jar:file:///data/app/org.mozilla.firefox-1.apk!/assets/omni.ja!/chrome/chrome/content/branding/favicon64.png"
*/
private static String getBrandingBitmapPath(Context context, String name) {
final String apkPath = context.getPackageResourcePath();
return "jar:jar:" + new File(apkPath).toURI() + "!/" +
AppConstants.OMNIJAR_NAME + "!/" +
"chrome/chrome/content/branding/" + name;
}
private static Bitmap loadBrandingBitmap(Context context, String name) {
Bitmap b = GeckoJarReader.getBitmap(context.getResources(),
getBrandingBitmapPath(context, name));
if (b == null) {
throw new IllegalStateException("Bitmap " + name + " missing from JAR!");
}
return b;
}
/**

View File

@ -115,8 +115,12 @@ public class BrowserToolbar extends GeckoRelativeLayout
private ShapedButton mTabs;
private ImageButton mBack;
private ImageButton mForward;
private ImageButton mFavicon;
private ImageButton mStop;
// To de-bounce sets.
private Bitmap mLastFavicon;
private ImageButton mFavicon;
private ImageButton mSiteSecurity;
private PageActionLayout mPageActionLayout;
private Animation mProgressSpinner;
@ -428,7 +432,7 @@ public class BrowserToolbar extends GeckoRelativeLayout
mFavicon.setOnClickListener(faviconListener);
mSiteSecurity.setOnClickListener(faviconListener);
mStop.setOnClickListener(new Button.OnClickListener() {
@Override
public void onClick(View v) {
@ -741,12 +745,15 @@ public class BrowserToolbar extends GeckoRelativeLayout
}
public void setProgressVisibility(boolean visible) {
Log.d(LOGTAG, "setProgressVisibility: " + visible);
// The "Throbber start" and "Throbber stop" log messages in this method
// are needed by S1/S2 tests (http://mrcote.info/phonedash/#).
// See discussion in Bug 804457. Bug 805124 tracks paring these down.
if (visible) {
mFavicon.setImageResource(R.drawable.progress_spinner);
//To stop the glitch caused by mutiple start() calls.
mLastFavicon = null;
// To stop the glitch caused by multiple start() calls.
if (!mSpinnerVisible) {
setPageActionVisibility(true);
mFavicon.setAnimation(mProgressSpinner);
@ -868,15 +875,15 @@ public class BrowserToolbar extends GeckoRelativeLayout
setContentDescription(title != null ? title : mTitle.getHint());
}
// Sets the toolbar title according to the selected tab, obeying the mShowUrl prference.
// Sets the toolbar title according to the selected tab, obeying the mShowUrl preference.
private void updateTitle() {
Tab tab = Tabs.getInstance().getSelectedTab();
final Tab tab = Tabs.getInstance().getSelectedTab();
// Keep the title unchanged if there's no selected tab, or if the tab is entering reader mode.
if (tab == null || tab.isEnteringReaderMode()) {
return;
}
String url = tab.getURL();
final String url = tab.getURL();
if (!isEditing()) {
mUrlEditLayout.setText(url);
@ -923,8 +930,17 @@ public class BrowserToolbar extends GeckoRelativeLayout
}
private void setFavicon(Bitmap image) {
if (Tabs.getInstance().getSelectedTab().getState() == Tab.STATE_LOADING)
Log.d(LOGTAG, "setFavicon(" + image + ")");
if (Tabs.getInstance().getSelectedTab().getState() == Tab.STATE_LOADING) {
return;
}
if (image == mLastFavicon) {
Log.d(LOGTAG, "Ignoring favicon set: new favicon is identical to previous favicon.");
return;
}
mLastFavicon = image; // Cache the original so we can debounce without scaling.
if (image != null) {
image = Bitmap.createScaledBitmap(image, mFaviconSize, mFaviconSize, false);
@ -933,7 +949,7 @@ public class BrowserToolbar extends GeckoRelativeLayout
mFavicon.setImageDrawable(null);
}
}
private void setSecurityMode(String mode) {
int imageLevel = SiteIdentityPopup.getSecurityImageLevel(mode);
mSiteSecurity.setImageLevel(imageLevel);

View File

@ -16,7 +16,6 @@
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title>&abouthome.title;</title>
<link rel="icon" type="image/png" sizes="64x64" href="chrome://branding/content/favicon64.png" />
</head>
<body>
</body>