diff --git a/toolkit/components/places/src/nsFaviconService.cpp b/toolkit/components/places/src/nsFaviconService.cpp index 2e3e85129024..6ca2a76983f3 100644 --- a/toolkit/components/places/src/nsFaviconService.cpp +++ b/toolkit/components/places/src/nsFaviconService.cpp @@ -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 mFaviconService; + nsRefPtr mFaviconService; nsCOMPtr mChannel; nsCOMPtr mPageURI; nsCOMPtr 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 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 removeFavicons; + rv = mDBConn->CreateStatement(NS_LITERAL_CSTRING( + "DELETE FROM moz_favicons" + ), getter_AddRefs(removeFavicons)); + NS_ENSURE_SUCCESS(rv, rv); + + mozIStorageStatement *stmts[] = { + removeReferences, + removeFavicons + }; + nsCOMPtr 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 cachingChannel(do_QueryInterface(mChannel)); + if (cachingChannel) { + nsCOMPtr cacheToken; + rv = cachingChannel->GetCacheToken(getter_AddRefs(cacheToken)); + if (NS_SUCCEEDED(rv)) { + nsCOMPtr 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); diff --git a/toolkit/components/places/src/nsFaviconService.h b/toolkit/components/places/src/nsFaviconService.h index baae9ff7fcc1..03594aec4a73 100644 --- a/toolkit/components/places/src/nsFaviconService.h +++ b/toolkit/components/places/src/nsFaviconService.h @@ -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 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; };