Bug 460300 - Expire favicons when they expire from cache, and when the cache is cleared

This makes favicons expire from the favicon service much less often.  They are
now expired when the cache headers says they expire, or one week - whichever is
shorter.  Additionally, all favicons expire when the cache is cleared.
r=MaK77
This commit is contained in:
Shawn Wilsher 2008-12-11 15:01:10 -05:00
Родитель bec05aaa7b
Коммит 2b99203875
2 изменённых файлов: 106 добавлений и 36 удалений

Просмотреть файл

@ -47,6 +47,7 @@
*/
#include "nsFaviconService.h"
#include "nsICacheService.h"
#include "nsICacheVisitor.h"
#include "nsICachingChannel.h"
#include "nsICategoryManager.h"
@ -64,6 +65,7 @@
#include "mozStorageHelper.h"
#include "plbase64.h"
#include "nsPlacesTables.h"
#include "mozIStoragePendingStatement.h"
// For favicon optimization
#include "imgITools.h"
@ -86,6 +88,14 @@
// This still allows us to accept a favicon even if we cannot optimize it.
#define MAX_FAVICON_SIZE 10240
/**
* The maximum time we will keep a favicon around. We always ask the cache, if
* we can, but default to this value if we do not get a time back, or the time
* is more in the future than this.
* Currently set to one week from now.
*/
#define MAX_FAVICON_EXPIRATION PRTime(7 * 24 * 60 * 60 * PR_USEC_PER_SEC)
class FaviconLoadListener : public nsIStreamListener,
public nsIInterfaceRequestor,
public nsIChannelEventSink
@ -104,7 +114,7 @@ public:
private:
~FaviconLoadListener();
nsCOMPtr<nsFaviconService> mFaviconService;
nsRefPtr<nsFaviconService> mFaviconService;
nsCOMPtr<nsIChannel> mChannel;
nsCOMPtr<nsIURI> mPageURI;
nsCOMPtr<nsIURI> mFaviconURI;
@ -115,7 +125,11 @@ private:
nsFaviconService* nsFaviconService::gFaviconService;
NS_IMPL_ISUPPORTS1(nsFaviconService, nsIFaviconService)
NS_IMPL_ISUPPORTS2(
nsFaviconService
, nsIFaviconService
, nsIObserver
)
// nsFaviconService::nsFaviconService
@ -219,6 +233,55 @@ nsFaviconService::InitTables(mozIStorageConnection* aDBConn)
return NS_OK;
}
nsresult
nsFaviconService::ExpireAllFavicons()
{
// Remove all references to favicons. We would love to not have to do this
// because it could result in copying almost all of the moz_places into
// memory for some sync in the future. We mitigate this some by checking for
// non-null favicon ids, but views do not use indexes when querying.
nsCOMPtr<mozIStorageStatement> removeReferences;
nsresult rv = mDBConn->CreateStatement(NS_LITERAL_CSTRING(
"UPDATE moz_places_view "
"SET favicon_id = NULL "
"WHERE favicon_id <> NULL"
), getter_AddRefs(removeReferences));
NS_ENSURE_SUCCESS(rv, rv);
// Remove all favicons
nsCOMPtr<mozIStorageStatement> removeFavicons;
rv = mDBConn->CreateStatement(NS_LITERAL_CSTRING(
"DELETE FROM moz_favicons"
), getter_AddRefs(removeFavicons));
NS_ENSURE_SUCCESS(rv, rv);
mozIStorageStatement *stmts[] = {
removeReferences,
removeFavicons
};
nsCOMPtr<mozIStoragePendingStatement> ps;
rv = mDBConn->ExecuteAsync(stmts, NS_ARRAY_LENGTH(stmts), nsnull,
getter_AddRefs(ps));
NS_ENSURE_SUCCESS(rv, rv);
return NS_OK;
}
////////////////////////////////////////////////////////////////////////////////
//// nsIObserver
NS_IMETHODIMP
nsFaviconService::Observe(nsISupports *aSubject, const char *aTopic,
const PRUnichar *aData)
{
if (strcmp(aTopic, NS_CACHESERVICE_EMPTYCACHE_TOPIC_ID) == 0)
(void)ExpireAllFavicons();
return NS_OK;
}
////////////////////////////////////////////////////////////////////////////////
//// nsIFaviconService
// nsFaviconService::SetFaviconUrlForPage
@ -228,11 +291,9 @@ nsFaviconService::SetFaviconUrlForPage(nsIURI* aPageURI, nsIURI* aFaviconURI)
NS_ENSURE_ARG_POINTER(aPageURI);
NS_ENSURE_ARG_POINTER(aFaviconURI);
// we don't care whether there was data or what the expiration was
// we don't care if there was data
PRBool hasData;
PRTime expiration;
nsresult rv = SetFaviconUrlForPageInternal(aPageURI, aFaviconURI,
&hasData, &expiration);
nsresult rv = SetFaviconUrlForPageInternal(aPageURI, aFaviconURI, &hasData);
NS_ENSURE_SUCCESS(rv, rv);
// send favicon change notifications if the URL has any data
@ -266,16 +327,13 @@ nsFaviconService::GetDefaultFavicon(nsIURI** _retval)
// This sets aHasData if there was already icon data for this favicon. Used
// to know if we should try reloading.
//
// Expiration will be 0 if the icon has not been set yet.
//
// Does NOT send out notifications. Caller should send out notifications
// if the favicon has data.
nsresult
nsFaviconService::SetFaviconUrlForPageInternal(nsIURI* aPageURI,
nsIURI* aFaviconURI,
PRBool* aHasData,
PRTime* aExpiration)
PRBool* aHasData)
{
nsresult rv;
PRInt64 iconId = -1;
@ -297,10 +355,6 @@ nsFaviconService::SetFaviconUrlForPageInternal(nsIURI* aPageURI,
NS_ENSURE_SUCCESS(rv, rv);
if (dataSize > 0)
*aHasData = PR_TRUE;
// expiration
rv = mDBGetIconInfo->GetInt64(2, aExpiration);
NS_ENSURE_SUCCESS(rv, rv);
}
}
@ -313,7 +367,6 @@ nsFaviconService::SetFaviconUrlForPageInternal(nsIURI* aPageURI,
if (iconId == -1) {
// We did not find any entry, so create a new one
*aHasData = PR_FALSE;
*aExpiration = 0;
// not-binded params are automatically nullified by mozStorage
mozStorageStatementScoper scoper(mDBInsertIcon);
@ -391,9 +444,7 @@ nsFaviconService::UpdateBookmarkRedirectFavicon(nsIURI* aPageURI,
return NS_OK; // bookmarked directly, not through a redirect
PRBool hasData = PR_FALSE;
PRTime expiration = 0;
rv = SetFaviconUrlForPageInternal(bookmarkURI, aFaviconURI,
&hasData, &expiration);
rv = SetFaviconUrlForPageInternal(bookmarkURI, aFaviconURI, &hasData);
NS_ENSURE_SUCCESS(rv, rv);
if (hasData) {
@ -520,12 +571,11 @@ nsFaviconService::DoSetAndLoadFaviconForPage(nsIURI* aPageURI,
if (isErrorPage)
return NS_OK;
// See if we have data and get the expiration time for this favicon. It might
// be nice to say SetFaviconUrlForPageInternal here but we DON'T want to set
// the favicon for the page unless we know we have it. For example, if I go
// to random site x.com, the browser will still tell us that the favicon is
// x.com/favicon.ico even if there is no such file. We don't want to pollute
// our tables with this useless data.
// See if we have data and get the expiration time for this favicon. We DO
// NOT want to set the favicon for the page unless we know we have it. For
// example, if I go to random site x.com, the browser will still tell us that
// the favicon is x.com/favicon.ico even if there is no such file. We don't
// want to pollute our tables with this useless data.
PRBool hasData = PR_FALSE;
PRTime expiration = 0;
{ // scope for statement
@ -562,7 +612,7 @@ nsFaviconService::DoSetAndLoadFaviconForPage(nsIURI* aPageURI,
return NS_OK; // already set
// This will associate the favicon URL with the page.
rv = SetFaviconUrlForPageInternal(page, aFaviconURI, &hasData, &expiration);
rv = SetFaviconUrlForPageInternal(page, aFaviconURI, &hasData);
NS_ENSURE_SUCCESS(rv, rv);
SendFaviconNotifications(page, aFaviconURI);
@ -1119,15 +1169,27 @@ FaviconLoadListener::OnStopRequest(nsIRequest *aRequest, nsISupports *aContext,
return NS_OK;
}
// Expire this favicon in one day. An old version of this actually extracted
// the expiration time from the cache. But what if people (especially web
// developers) get a bad favicon or change it? The problem is that we're not
// aware when the icon has been reloaded in the cache or cleared. This way
// we'll always pick up any changes in the cache after a day. In most cases
// re-set favicons will come from the cache anyway and reloading them is not
// very expensive.
PRTime expiration = PR_Now() +
(PRInt64)(24 * 60 * 60) * (PRInt64)PR_USEC_PER_SEC;
// Attempt to get an expiration time from the cache. If this fails, we'll
// make one up.
PRTime expiration = -1;
nsCOMPtr<nsICachingChannel> cachingChannel(do_QueryInterface(mChannel));
if (cachingChannel) {
nsCOMPtr<nsISupports> cacheToken;
rv = cachingChannel->GetCacheToken(getter_AddRefs(cacheToken));
if (NS_SUCCEEDED(rv)) {
nsCOMPtr<nsICacheEntryInfo> cacheEntry(do_QueryInterface(cacheToken));
PRUint32 seconds;
rv = cacheEntry->GetExpirationTime(&seconds);
if (NS_SUCCEEDED(rv)) {
// Set the expiration, but make sure we honor our cap.
expiration = PR_Now() + PR_MIN(seconds * PR_USEC_PER_SEC,
MAX_FAVICON_EXPIRATION);
}
}
}
// If we did not obtain a time from the cache, or it was negative, use our cap
if (expiration < 0)
expiration = PR_Now() + MAX_FAVICON_EXPIRATION;
mozStorageTransaction transaction(mFaviconService->mDBConn, PR_FALSE);
// save the favicon data
@ -1140,7 +1202,7 @@ FaviconLoadListener::OnStopRequest(nsIRequest *aRequest, nsISupports *aContext,
// set the favicon for the page
PRBool hasData;
rv = mFaviconService->SetFaviconUrlForPageInternal(mPageURI, mFaviconURI,
&hasData, &expiration);
&hasData);
NS_ENSURE_SUCCESS(rv, rv);
mFaviconService->UpdateBookmarkRedirectFavicon(mPageURI, mFaviconURI);

Просмотреть файл

@ -44,11 +44,13 @@
#include "mozIStorageConnection.h"
#include "mozIStorageValueArray.h"
#include "mozIStorageStatement.h"
#include "nsIObserver.h"
// forward definition for friend class
class FaviconLoadListener;
class nsFaviconService : public nsIFaviconService
, public nsIObserver
{
public:
nsFaviconService();
@ -98,6 +100,7 @@ public:
NS_DECL_ISUPPORTS
NS_DECL_NSIFAVICONSERVICE
NS_DECL_NSIOBSERVER
private:
~nsFaviconService();
@ -125,11 +128,16 @@ private:
nsDataHashtable<nsCStringHashKey, PRUint32> mFailedFavicons;
nsresult SetFaviconUrlForPageInternal(nsIURI* aURI, nsIURI* aFavicon,
PRBool* aHasData, PRTime* aExpiration);
PRBool* aHasData);
nsresult UpdateBookmarkRedirectFavicon(nsIURI* aPage, nsIURI* aFavicon);
void SendFaviconNotifications(nsIURI* aPage, nsIURI* aFaviconURI);
/**
* Expires all favicons
*/
nsresult ExpireAllFavicons();
friend class FaviconLoadListener;
};