mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-11-24 21:31:04 +00:00
bug 522855 - Part3: Make expiration lookups async, r=mak. Further modified by me, r=sdwilsh.
This commit is contained in:
parent
464d65029f
commit
30af0fd406
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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.
|
||||
*/
|
||||
|
Loading…
Reference in New Issue
Block a user