bug 522855 - Part3: Make expiration lookups async, r=mak. Further modified by me, r=sdwilsh.

This commit is contained in:
Shawn Wilsher 2009-11-11 12:04:16 +01:00
parent 464d65029f
commit 30af0fd406
2 changed files with 162 additions and 82 deletions

View File

@ -64,16 +64,14 @@
#include "nsReadableUtils.h"
#include "nsStreamUtils.h"
#include "nsStringStream.h"
#include "mozStorageHelper.h"
#include "plbase64.h"
#include "nsPlacesTables.h"
#include "mozIStoragePendingStatement.h"
#include "mozIStorageStatementCallback.h"
#include "mozIStorageError.h"
#include "nsPlacesTables.h"
#include "nsIPrefService.h"
#include "Helpers.h"
#include "mozilla/storage.h"
// For large favicons optimization.
#include "imgITools.h"
#include "imgIContainer.h"
@ -111,8 +109,8 @@ class FaviconLoadListener : public nsIStreamListener,
public nsIChannelEventSink
{
public:
FaviconLoadListener(nsFaviconService* aFaviconService,
nsIURI* aPageURI, nsIURI* aFaviconURI,
FaviconLoadListener(nsIURI* aPageURI,
nsIURI* aFaviconURI,
nsIChannel* aChannel);
NS_DECL_ISUPPORTS
@ -124,7 +122,6 @@ public:
private:
~FaviconLoadListener();
nsRefPtr<nsFaviconService> mFaviconService;
nsCOMPtr<nsIChannel> mChannel;
nsCOMPtr<nsIURI> mPageURI;
nsCOMPtr<nsIURI> mFaviconURI;
@ -219,6 +216,108 @@ GetEffectivePageForFavicon(nsIURI *aPageURI,
return pageURI.forget();
}
/**
* This class gets the expiration data for a favicon, and starts the lookup of
* the favicon's data if it should be loaded.
*/
class FaviconExpirationGetter : public AsyncStatementCallback
{
public:
NS_DECL_ISUPPORTS
FaviconExpirationGetter(nsIURI *aPageURI,
nsIURI *aFaviconURI,
bool aForceReload) :
mPageURI(aPageURI)
, mFaviconURI(aFaviconURI)
, mForceReload(aForceReload)
, mHasData(false)
, mExpiration(0)
{
}
/**
* Performs a lookup of the needed information asynchronously, and loads the
* icon if necessary.
*/
NS_IMETHOD checkAndLoad(mozIStorageStatement *aStatement)
{
mozStorageStatementScoper scoper(aStatement);
nsresult rv = BindStatementURI(aStatement, 0, mFaviconURI);
NS_ENSURE_SUCCESS(rv, rv);
nsCOMPtr<mozIStoragePendingStatement> ps;
rv = aStatement->ExecuteAsync(this, getter_AddRefs(ps));
NS_ENSURE_SUCCESS(rv, rv);
// ExecuteAsync will reset the statement for us.
scoper.Abandon();
return NS_OK;
}
NS_IMETHOD HandleResult(mozIStorageResultSet *aResultSet)
{
nsCOMPtr<mozIStorageRow> row;
nsresult rv = aResultSet->GetNextRow(getter_AddRefs(row));
NS_ENSURE_SUCCESS(rv, rv);
PRInt32 dataSize = 0;
(void)row->GetInt32(1, &dataSize);
mHasData = dataSize > 0;
(void)row->GetInt64(2, &mExpiration);
return NS_OK;
}
NS_IMETHOD HandleCompletion(PRUint16 aReason)
{
if (aReason != mozIStorageStatementCallback::REASON_FINISHED)
return NS_OK;
// 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. We also do not
// want to reload the icon if it hasn't expired yet.
if (mHasData && PR_Now() < mExpiration && !mForceReload) {
// Our data is likely still valid, but we should check to make sure the
// URI has changed, otherwise there is no need to notify.
nsFaviconService *fs = nsFaviconService::GetFaviconService();
NS_ENSURE_TRUE(fs, NS_ERROR_OUT_OF_MEMORY);
fs->checkAndNotify(mPageURI, mFaviconURI);
return NS_OK;
}
nsCOMPtr<nsIChannel> channel;
nsresult rv = NS_NewChannel(getter_AddRefs(channel), mFaviconURI);
NS_ENSURE_SUCCESS(rv, rv);
nsCOMPtr<nsIStreamListener> listener =
new FaviconLoadListener(mPageURI, mFaviconURI, channel);
NS_ENSURE_TRUE(listener, NS_ERROR_OUT_OF_MEMORY);
nsCOMPtr<nsIInterfaceRequestor> listenerRequestor =
do_QueryInterface(listener, &rv);
NS_ENSURE_SUCCESS(rv, rv);
rv = channel->SetNotificationCallbacks(listenerRequestor);
NS_ENSURE_SUCCESS(rv, rv);
rv = channel->AsyncOpen(listener, nsnull);
NS_ENSURE_SUCCESS(rv, rv);
return NS_OK;
}
private:
nsCOMPtr<nsIURI> mPageURI;
nsCOMPtr<nsIURI> mFaviconURI;
const bool mForceReload;
bool mHasData;
PRTime mExpiration;
};
NS_IMPL_ISUPPORTS1(
FaviconExpirationGetter,
mozIStorageStatementCallback
);
} // anonymous namespace
@ -614,70 +713,11 @@ nsFaviconService::DoSetAndLoadFaviconForPage(nsIURI* aPageURI,
nsCOMPtr<nsIURI> page = GetEffectivePageForFavicon(aPageURI, aFaviconURI);
NS_ENSURE_TRUE(page, NS_OK);
// 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;
{
mozStorageStatementScoper scoper(mDBGetIconInfo);
rv = BindStatementURI(mDBGetIconInfo, 0, aFaviconURI);
NS_ENSURE_SUCCESS(rv, rv);
nsRefPtr<FaviconExpirationGetter> dataGetter =
new FaviconExpirationGetter(page, aFaviconURI, !!aForceReload);
NS_ENSURE_TRUE(dataGetter, NS_ERROR_OUT_OF_MEMORY);
PRBool hasMatch;
rv = mDBGetIconInfo->ExecuteStep(&hasMatch);
NS_ENSURE_SUCCESS(rv, rv);
if (hasMatch) {
PRInt32 dataSize;
mDBGetIconInfo->GetInt32(1, &dataSize);
hasData = dataSize > 0;
mDBGetIconInfo->GetInt64(2, &expiration);
}
}
// See if this favicon has expired. We don't want to waste time reloading
// from the web or cache if we have a recent version.
PRTime now = PR_Now();
if (hasData && now < expiration && !aForceReload) {
// data still valid, no need to reload
// For page revisits (pretty common) we DON'T want to send out any
// notifications if we've already set the favicon. These notifications will
// cause parts of the UI to update and will be slow. This also saves us a
// database write in these cases.
nsCOMPtr<nsIURI> oldFavicon;
PRBool faviconsEqual;
if (NS_SUCCEEDED(GetFaviconForPage(page, getter_AddRefs(oldFavicon))) &&
NS_SUCCEEDED(aFaviconURI->Equals(oldFavicon, &faviconsEqual)) &&
faviconsEqual)
return NS_OK; // already set
// This will associate the favicon URL with the page.
rv = SetFaviconUrlForPageInternal(page, aFaviconURI, &hasData);
NS_ENSURE_SUCCESS(rv, rv);
SendFaviconNotifications(page, aFaviconURI);
UpdateBookmarkRedirectFavicon(page, aFaviconURI);
return NS_OK;
}
nsCOMPtr<nsIChannel> channel;
rv = NS_NewChannel(getter_AddRefs(channel), aFaviconURI);
NS_ENSURE_SUCCESS(rv, rv);
nsCOMPtr<nsIStreamListener> listener =
new FaviconLoadListener(this, page, aFaviconURI, channel);
NS_ENSURE_TRUE(listener, NS_ERROR_OUT_OF_MEMORY);
nsCOMPtr<nsIInterfaceRequestor> listenerRequestor =
do_QueryInterface(listener, &rv);
NS_ENSURE_SUCCESS(rv, rv);
rv = channel->SetNotificationCallbacks(listenerRequestor);
NS_ENSURE_SUCCESS(rv, rv);
rv = channel->AsyncOpen(listener, nsnull);
rv = dataGetter->checkAndLoad(mDBGetIconInfo);
NS_ENSURE_SUCCESS(rv, rv);
// DB will be updated and observers notified when data has finished loading.
@ -1126,6 +1166,33 @@ nsFaviconService::GetFaviconDataAsync(nsIURI* aFaviconURI,
return mDBGetData->ExecuteAsync(aCallback, getter_AddRefs(pendingStatement));
}
void
nsFaviconService::checkAndNotify(nsIURI *aPageURI,
nsIURI *aFaviconURI)
{
// For page revisits (pretty common) we DON'T want to send out any
// notifications if we've already set the favicon. These notifications will
// cause parts of the UI to update and will be slow. This also saves us a
// database write in these cases.
nsCOMPtr<nsIURI> oldFavicon;
PRBool faviconsEqual;
if (NS_SUCCEEDED(GetFaviconForPage(aPageURI, getter_AddRefs(oldFavicon))) &&
NS_SUCCEEDED(aFaviconURI->Equals(oldFavicon, &faviconsEqual)) &&
faviconsEqual)
return; // already set
// This will associate the favicon URL with the page.
PRBool hasData;
nsresult rv = SetFaviconUrlForPageInternal(aPageURI, aFaviconURI, &hasData);
if (NS_FAILED(rv))
return;
if (hasData) {
SendFaviconNotifications(aPageURI, aFaviconURI);
UpdateBookmarkRedirectFavicon(aPageURI, aFaviconURI);
}
}
////////////////////////////////////////////////////////////////////////////////
//// FaviconLoadListener
@ -1135,10 +1202,9 @@ NS_IMPL_ISUPPORTS4(FaviconLoadListener,
nsIInterfaceRequestor,
nsIChannelEventSink)
FaviconLoadListener::FaviconLoadListener(nsFaviconService* aFaviconService,
nsIURI* aPageURI, nsIURI* aFaviconURI,
FaviconLoadListener::FaviconLoadListener(nsIURI* aPageURI,
nsIURI* aFaviconURI,
nsIChannel* aChannel) :
mFaviconService(aFaviconService),
mChannel(aChannel),
mPageURI(aPageURI),
mFaviconURI(aFaviconURI)
@ -1159,9 +1225,12 @@ NS_IMETHODIMP
FaviconLoadListener::OnStopRequest(nsIRequest *aRequest, nsISupports *aContext,
nsresult aStatusCode)
{
nsFaviconService *fs = nsFaviconService::GetFaviconService();
NS_ENSURE_TRUE(fs, NS_ERROR_UNEXPECTED);
if (NS_FAILED(aStatusCode) || mData.Length() == 0) {
// load failed, add to failed cache
mFaviconService->AddFailedFavicon(mFaviconURI);
fs->AddFailedFavicon(mFaviconURI);
return NS_OK;
}
@ -1203,7 +1272,7 @@ FaviconLoadListener::OnStopRequest(nsIRequest *aRequest, nsISupports *aContext,
if (mimeType.IsEmpty()) {
// we can not handle favicons that do not have a recognisable MIME type
mFaviconService->AddFailedFavicon(mFaviconURI);
fs->AddFailedFavicon(mFaviconURI);
return NS_OK;
}
@ -1229,26 +1298,26 @@ FaviconLoadListener::OnStopRequest(nsIRequest *aRequest, nsISupports *aContext,
if (expiration < 0)
expiration = PR_Now() + MAX_FAVICON_EXPIRATION;
mozStorageTransaction transaction(mFaviconService->mDBConn, PR_FALSE);
mozStorageTransaction transaction(fs->mDBConn, PR_FALSE);
// save the favicon data
// This could fail if the favicon is bigger than defined limit, in such a
// case data will not be saved to the db but we will still continue.
(void) mFaviconService->SetFaviconData(mFaviconURI,
reinterpret_cast<PRUint8*>(const_cast<char*>(mData.get())),
mData.Length(), mimeType, expiration);
(void)fs->SetFaviconData(mFaviconURI,
reinterpret_cast<PRUint8*>(const_cast<char*>(mData.get())),
mData.Length(), mimeType, expiration);
// set the favicon for the page
PRBool hasData;
rv = mFaviconService->SetFaviconUrlForPageInternal(mPageURI, mFaviconURI,
rv = fs->SetFaviconUrlForPageInternal(mPageURI, mFaviconURI,
&hasData);
NS_ENSURE_SUCCESS(rv, rv);
mFaviconService->UpdateBookmarkRedirectFavicon(mPageURI, mFaviconURI);
fs->UpdateBookmarkRedirectFavicon(mPageURI, mFaviconURI);
rv = transaction.Commit();
NS_ENSURE_SUCCESS(rv, rv);
mFaviconService->SendFaviconNotifications(mPageURI, mFaviconURI);
fs->SendFaviconNotifications(mPageURI, mFaviconURI);
return NS_OK;
}

View File

@ -112,6 +112,17 @@ public:
nsresult GetFaviconDataAsync(nsIURI *aFaviconURI,
mozIStorageStatementCallback *aCallback);
/**
* Checks to see if a favicon's URI has changed, and notifies callers if it
* has.
*
* @param aPageURI
* The URI of the page aFaviconURI is for.
* @param aFaviconURI
* The URI for the favicon we want to test for on aPageURI.
*/
void checkAndNotify(nsIURI *aPageURI, nsIURI *aFaviconURI);
/**
* Finalize all internal statements.
*/