Bug 552605 - Keep track of all redirects when validating cache entries, so we make sure cache headers are always kept track of. r=jrmuizel

This commit is contained in:
Joe Drew 2011-06-30 21:58:31 -04:00
Родитель 7622e1d969
Коммит c315bfbb99
2 изменённых файлов: 131 добавлений и 66 удалений

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

@ -276,36 +276,6 @@ public:
NS_IMPL_ISUPPORTS1(imgMemoryReporter, nsIMemoryReporter)
/**
* A class that implements nsIProgressEventSink and forwards all calls to it to
* the original notification callbacks of the channel. Also implements
* nsIInterfaceRequestor and gives out itself for nsIProgressEventSink calls,
* and forwards everything else to the channel's notification callbacks.
*/
class nsProgressNotificationProxy : public nsIProgressEventSink
, public nsIChannelEventSink
, public nsIInterfaceRequestor
{
public:
nsProgressNotificationProxy(nsIChannel* channel,
imgIRequest* proxy)
: mImageRequest(proxy)
{
channel->GetNotificationCallbacks(getter_AddRefs(mOriginalCallbacks));
}
NS_DECL_ISUPPORTS
NS_DECL_NSIPROGRESSEVENTSINK
NS_DECL_NSICHANNELEVENTSINK
NS_DECL_NSIINTERFACEREQUESTOR
private:
~nsProgressNotificationProxy() {}
nsCOMPtr<nsIInterfaceRequestor> mOriginalCallbacks;
nsCOMPtr<nsIRequest> mImageRequest;
};
NS_IMPL_ISUPPORTS3(nsProgressNotificationProxy,
nsIProgressEventSink,
nsIChannelEventSink,
@ -531,7 +501,9 @@ imgCacheEntry::imgCacheEntry(imgRequest *request)
mTouchedTime(SecondsFromPRTime(PR_Now())),
mExpiryTime(0),
mMustValidate(PR_FALSE),
mEvicted(PR_FALSE),
// We start off as evicted so we don't try to update the cache. PutIntoCache
// will set this to false.
mEvicted(PR_TRUE),
mHasNoProxies(PR_TRUE)
{}
@ -1233,18 +1205,18 @@ PRBool imgLoader::ValidateRequestWithNewChannel(imgRequest *request,
}
// Make sure that OnStatus/OnProgress calls have the right request set...
nsCOMPtr<nsIInterfaceRequestor> requestor(
new nsProgressNotificationProxy(newChannel, req));
if (!requestor)
nsRefPtr<nsProgressNotificationProxy> progressproxy =
new nsProgressNotificationProxy(newChannel, req);
if (!progressproxy)
return PR_FALSE;
newChannel->SetNotificationCallbacks(requestor);
imgCacheValidator *hvc = new imgCacheValidator(request, aCX);
nsRefPtr<imgCacheValidator> hvc = new imgCacheValidator(progressproxy, request, aCX);
if (!hvc) {
return PR_FALSE;
}
NS_ADDREF(hvc);
newChannel->SetNotificationCallbacks(hvc);
request->mValidator = hvc;
imgRequestProxy* proxy = static_cast<imgRequestProxy*>
@ -1263,8 +1235,6 @@ PRBool imgLoader::ValidateRequestWithNewChannel(imgRequest *request,
if (NS_SUCCEEDED(rv))
NS_ADDREF(*aProxyRequest = req.get());
NS_RELEASE(hvc);
return NS_SUCCEEDED(rv);
}
}
@ -2035,14 +2005,20 @@ NS_IMETHODIMP ProxyListener::OnDataAvailable(nsIRequest *aRequest, nsISupports *
* http validate class. check a channel for a 304
*/
NS_IMPL_ISUPPORTS2(imgCacheValidator, nsIStreamListener, nsIRequestObserver)
NS_IMPL_ISUPPORTS5(imgCacheValidator, nsIStreamListener, nsIRequestObserver,
nsIChannelEventSink, nsIInterfaceRequestor,
nsIAsyncVerifyRedirectCallback)
imgLoader imgCacheValidator::sImgLoader;
imgCacheValidator::imgCacheValidator(imgRequest *request, void *aContext) :
mRequest(request),
mContext(aContext)
{}
imgCacheValidator::imgCacheValidator(nsProgressNotificationProxy* progress,
imgRequest *request, void *aContext)
: mProgressProxy(progress),
mRequest(request),
mContext(aContext)
{
NewRequestAndEntry(getter_AddRefs(mNewRequest), getter_AddRefs(mNewEntry));
}
imgCacheValidator::~imgCacheValidator()
{
@ -2094,6 +2070,9 @@ NS_IMETHODIMP imgCacheValidator::OnStartRequest(nsIRequest *aRequest, nsISupport
mRequest = nsnull;
mNewRequest = nsnull;
mNewEntry = nsnull;
return NS_OK;
}
}
@ -2101,7 +2080,6 @@ NS_IMETHODIMP imgCacheValidator::OnStartRequest(nsIRequest *aRequest, nsISupport
// We can't load out of cache. We have to create a whole new request for the
// data that's coming in off the channel.
nsCOMPtr<nsIChannel> channel(do_QueryInterface(aRequest));
nsRefPtr<imgCacheEntry> entry;
nsCOMPtr<nsIURI> uri;
mRequest->GetURI(getter_AddRefs(uri));
@ -2118,34 +2096,24 @@ NS_IMETHODIMP imgCacheValidator::OnStartRequest(nsIRequest *aRequest, nsISupport
mRequest->mValidator = nsnull;
mRequest = nsnull;
imgRequest *request;
if (!NewRequestAndEntry(&request, getter_AddRefs(entry)))
return NS_ERROR_OUT_OF_MEMORY;
// We use originalURI here to fulfil the imgIRequest contract on GetURI.
nsCOMPtr<nsIURI> originalURI;
channel->GetOriginalURI(getter_AddRefs(originalURI));
request->Init(originalURI, uri, channel, channel, entry, NS_GetCurrentThread(), mContext);
mNewRequest->Init(originalURI, uri, channel, channel, mNewEntry, NS_GetCurrentThread(), mContext);
ProxyListener *pl = new ProxyListener(static_cast<nsIStreamListener *>(request));
if (!pl) {
request->CancelAndAbort(NS_ERROR_OUT_OF_MEMORY);
NS_RELEASE(request);
return NS_ERROR_OUT_OF_MEMORY;
}
ProxyListener *pl = new ProxyListener(static_cast<nsIStreamListener *>(mNewRequest));
mDestListener = static_cast<nsIStreamListener*>(pl);
// Try to add the new request into the cache. Note that the entry must be in
// the cache before the proxies' ownership changes, because adding a proxy
// changes the caching behaviour for imgRequests.
sImgLoader.PutIntoCache(originalURI, entry);
sImgLoader.PutIntoCache(originalURI, mNewEntry);
PRUint32 count = mProxies.Count();
for (PRInt32 i = count-1; i>=0; i--) {
imgRequestProxy *proxy = static_cast<imgRequestProxy *>(mProxies[i]);
proxy->ChangeOwner(request);
proxy->ChangeOwner(mNewRequest);
// Proxies waiting on cache validation should be deferring notifications.
// Undefer them.
@ -2159,10 +2127,8 @@ NS_IMETHODIMP imgCacheValidator::OnStartRequest(nsIRequest *aRequest, nsISupport
proxy->SyncNotifyListener();
}
NS_RELEASE(request);
if (!mDestListener)
return NS_OK;
mNewRequest = nsnull;
mNewEntry = nsnull;
return mDestListener->OnStartRequest(aRequest, ctxt);
}
@ -2200,3 +2166,62 @@ NS_IMETHODIMP imgCacheValidator::OnDataAvailable(nsIRequest *aRequest, nsISuppor
return mDestListener->OnDataAvailable(aRequest, ctxt, inStr, sourceOffset, count);
}
/** nsIInterfaceRequestor methods **/
NS_IMETHODIMP imgCacheValidator::GetInterface(const nsIID & aIID, void **aResult)
{
if (aIID.Equals(NS_GET_IID(nsIChannelEventSink)))
return QueryInterface(aIID, aResult);
return mProgressProxy->GetInterface(aIID, aResult);
}
// These functions are materially the same as the same functions in imgRequest.
// We duplicate them because we're verifying whether cache loads are necessary,
// not unconditionally loading.
/** nsIChannelEventSink methods **/
NS_IMETHODIMP imgCacheValidator::AsyncOnChannelRedirect(nsIChannel *oldChannel,
nsIChannel *newChannel, PRUint32 flags,
nsIAsyncVerifyRedirectCallback *callback)
{
// Note all cache information we get from the old channel.
mNewRequest->SetCacheValidation(mNewEntry, oldChannel);
// Prepare for callback
mRedirectCallback = callback;
mRedirectChannel = newChannel;
return mProgressProxy->AsyncOnChannelRedirect(oldChannel, newChannel, flags, this);
}
NS_IMETHODIMP imgCacheValidator::OnRedirectVerifyCallback(nsresult aResult)
{
// If we've already been told to abort, just do so.
if (NS_FAILED(aResult)) {
mRedirectCallback->OnRedirectVerifyCallback(aResult);
mRedirectCallback = nsnull;
mRedirectChannel = nsnull;
return NS_OK;
}
// make sure we have a protocol that returns data rather than opens
// an external application, e.g. mailto:
nsCOMPtr<nsIURI> uri;
mRedirectChannel->GetURI(getter_AddRefs(uri));
PRBool doesNotReturnData = PR_FALSE;
NS_URIChainHasFlags(uri, nsIProtocolHandler::URI_DOES_NOT_RETURN_DATA,
&doesNotReturnData);
nsresult result = NS_OK;
if (doesNotReturnData) {
result = NS_ERROR_ABORT;
}
mRedirectCallback->OnRedirectVerifyCallback(result);
mRedirectCallback = nsnull;
mRedirectChannel = nsnull;
return NS_OK;
}

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

@ -52,6 +52,8 @@
#include "imgRequest.h"
#include "nsIObserverService.h"
#include "nsIChannelPolicy.h"
#include "nsIProgressEventSink.h"
#include "nsIChannel.h"
#ifdef LOADER_THREADSAFE
#include "prlock.h"
@ -374,6 +376,33 @@ private:
nsCOMPtr<nsIStreamListener> mDestListener;
};
/**
* A class that implements nsIProgressEventSink and forwards all calls to it to
* the original notification callbacks of the channel. Also implements
* nsIInterfaceRequestor and gives out itself for nsIProgressEventSink calls,
* and forwards everything else to the channel's notification callbacks.
*/
class nsProgressNotificationProxy : public nsIProgressEventSink
, public nsIChannelEventSink
, public nsIInterfaceRequestor
{
public:
nsProgressNotificationProxy(nsIChannel* channel,
imgIRequest* proxy)
: mImageRequest(proxy) {
channel->GetNotificationCallbacks(getter_AddRefs(mOriginalCallbacks));
}
NS_DECL_ISUPPORTS
NS_DECL_NSIPROGRESSEVENTSINK
NS_DECL_NSICHANNELEVENTSINK
NS_DECL_NSIINTERFACEREQUESTOR
private:
~nsProgressNotificationProxy() {}
nsCOMPtr<nsIInterfaceRequestor> mOriginalCallbacks;
nsCOMPtr<nsIRequest> mImageRequest;
};
/**
* validate checker
@ -381,25 +410,36 @@ private:
#include "nsCOMArray.h"
class imgCacheValidator : public nsIStreamListener
class imgCacheValidator : public nsIStreamListener,
public nsIChannelEventSink,
public nsIInterfaceRequestor,
public nsIAsyncVerifyRedirectCallback
{
public:
imgCacheValidator(imgRequest *request, void *aContext);
imgCacheValidator(nsProgressNotificationProxy* progress, imgRequest *request, void *aContext);
virtual ~imgCacheValidator();
void AddProxy(imgRequestProxy *aProxy);
/* additional members */
NS_DECL_ISUPPORTS
NS_DECL_NSISTREAMLISTENER
NS_DECL_NSIREQUESTOBSERVER
NS_DECL_NSICHANNELEVENTSINK
NS_DECL_NSIINTERFACEREQUESTOR
NS_DECL_NSIASYNCVERIFYREDIRECTCALLBACK
private:
nsCOMPtr<nsIStreamListener> mDestListener;
nsRefPtr<nsProgressNotificationProxy> mProgressProxy;
nsCOMPtr<nsIAsyncVerifyRedirectCallback> mRedirectCallback;
nsCOMPtr<nsIChannel> mRedirectChannel;
nsRefPtr<imgRequest> mRequest;
nsCOMArray<imgIRequest> mProxies;
nsRefPtr<imgRequest> mNewRequest;
nsRefPtr<imgCacheEntry> mNewEntry;
void *mContext;
static imgLoader sImgLoader;