fixes bug 167463 "fromCacheOnly should be an attribute on nsICachingChannel"

and fixes bug 166927 "add nsICachingChannel should provide an attribute to
'load only if modified'" r=dougt sr=bzbarsky


git-svn-id: svn://10.0.0.236/trunk@131261 18797224-902f-48f8-a5cc-f745e15eee43
This commit is contained in:
darin%netscape.com 2002-10-06 03:20:40 +00:00
parent d76cc96df5
commit 591bccda72
7 changed files with 194 additions and 103 deletions

View File

@ -5154,25 +5154,27 @@ nsDocShell::DoURILoad(nsIURI * aURI,
* cache is free to go to the server for updated postdata.
*/
if (cacheChannel && cacheKey) {
if (mLoadType == LOAD_HISTORY || mLoadType == LOAD_RELOAD_CHARSET_CHANGE)
cacheChannel->SetCacheKey(cacheKey, PR_TRUE);
if (mLoadType == LOAD_HISTORY || mLoadType == LOAD_RELOAD_CHARSET_CHANGE) {
cacheChannel->SetCacheKey(cacheKey);
PRUint32 loadFlags;
if (NS_SUCCEEDED(channel->GetLoadFlags(&loadFlags)))
channel->SetLoadFlags(loadFlags | nsICachingChannel::LOAD_ONLY_FROM_CACHE);
}
else if (mLoadType == LOAD_RELOAD_NORMAL)
cacheChannel->SetCacheKey(cacheKey, PR_FALSE);
cacheChannel->SetCacheKey(cacheKey);
}
}
else {
/* If there is no postdata, set the cache key on the channel
* with the readFromCacheOnly set to false, so that cache will
* be free to get it from net if it is not found in cache.
/* If there is no postdata, set the cache key on the channel, and
* do not set the LOAD_ONLY_FROM_CACHE flag, so that the channel
* will be free to get it from net if it is not found in cache.
* New cache may use it creatively on CGI pages with GET
* method and even on those that say "no-cache"
*/
if (mLoadType == LOAD_HISTORY || mLoadType == LOAD_RELOAD_NORMAL
|| mLoadType == LOAD_RELOAD_CHARSET_CHANGE) {
if (cacheChannel && cacheKey)
cacheChannel->SetCacheKey(cacheKey, PR_FALSE);
cacheChannel->SetCacheKey(cacheKey);
}
}
if (aHeadersData) {

View File

@ -402,6 +402,12 @@ NS_IMETHODIMP imgLoader::LoadImage(nsIURI *aURI,
nsCOMPtr<nsICachingChannel> cacheChan(do_QueryInterface(newChannel));
if (cacheChan) {
// since this channel supports nsICachingChannel, we can ask it
// to only stream us data if the data comes off the net.
PRUint32 loadFlags;
if (NS_SUCCEEDED(newChannel->GetLoadFlags(&loadFlags)))
newChannel->SetLoadFlags(loadFlags | nsICachingChannel::LOAD_ONLY_IF_MODIFIED);
rv = CreateNewProxyForRequest(request, aLoadGroup, aObserver, aCX,
requestFlags, aRequest, _retval);
@ -909,10 +915,8 @@ NS_IMETHODIMP httpValidateChecker::OnStartRequest(nsIRequest *aRequest, nsISuppo
{
nsCOMPtr<nsICachingChannel> cacheChan(do_QueryInterface(aRequest));
if (cacheChan) {
PRBool isFromCache = PR_FALSE;
cacheChan->IsFromCache(&isFromCache);
if (isFromCache) {
PRBool isFromCache;
if (NS_SUCCEEDED(cacheChan->IsFromCache(&isFromCache)) && isFromCache) {
PRUint32 count;
mProxies.Count(&count);
@ -924,11 +928,6 @@ NS_IMETHODIMP httpValidateChecker::OnStartRequest(nsIRequest *aRequest, nsISuppo
}
mRequest->SetLoadId(mContext);
// XXX see bug 113959
// Don't call cancel here because it makes the HTTP cache entry go away.
aRequest->Cancel(NS_BINDING_ABORTED);
mRequest->mValidator = nsnull;
NS_RELEASE(mRequest);
@ -1021,6 +1020,15 @@ static NS_METHOD dispose_of_data(nsIInputStream* in, void* closure,
/* void onDataAvailable (in nsIRequest request, in nsISupports ctxt, in nsIInputStream inStr, in unsigned long sourceOffset, in unsigned long count); */
NS_IMETHODIMP httpValidateChecker::OnDataAvailable(nsIRequest *aRequest, nsISupports *ctxt, nsIInputStream *inStr, PRUint32 sourceOffset, PRUint32 count)
{
#ifdef DEBUG
nsCOMPtr<nsICachingChannel> cacheChan(do_QueryInterface(aRequest));
if (cacheChan) {
PRBool isFromCache;
if (NS_SUCCEEDED(cacheChan->IsFromCache(&isFromCache)) && isFromCache)
NS_ERROR("OnDataAvailable not suppressed by LOAD_ONLY_IF_MODIFIED load flag");
}
#endif
if (!mDestListener) {
// XXX see bug 113959
PRUint32 _retval;

View File

@ -98,6 +98,11 @@
#define NS_ERROR_REDIRECT_LOOP \
NS_ERROR_GENERATE_FAILURE(NS_ERROR_MODULE_NETWORK, 31)
/**
* nsresult passed through onStopRequest if the document could not be fetched from the cache.
*/
#define NS_ERROR_DOCUMENT_NOT_CACHED NS_ERROR_GENERATE_FAILURE(NS_ERROR_MODULE_NETWORK, 70)
// Where most necko status messages come from:
#define NECKO_MSGS_URL "chrome://necko/locale/necko.properties"

View File

@ -47,6 +47,8 @@ interface nsIFile;
* This interface provides:
* 1) Support for "stream as file" semantics (for JAR and plugins).
* 2) Support for "pinning" cached data in the cache (for printing and save-as).
* 3) Support for uniquely identifying cached data in cases when the URL
* is insufficient (e.g., HTTP form submission).
*/
[scriptable, uuid(b1f95f5e-ee05-4434-9d34-89a935d7feef)]
interface nsICachingChannel : nsISupports
@ -54,29 +56,37 @@ interface nsICachingChannel : nsISupports
/**
* Set/get the cache token... uniquely identifies the data in the cache.
* Holding a reference to this token prevents the cached data from being
* removed. A cache token retrieved from a particular instance of
* nsICachingChannel could be set on another instance of nsICachingChannel
* provided the underlying implementations are compatible. The implemen-
* tation of nsICachingChannel would be expected to only read from the
* cache entry identified by the cache token and not try to validate it.
* removed.
*
* A cache token retrieved from a particular instance of nsICachingChannel
* could be set on another instance of nsICachingChannel provided the
* underlying implementations are compatible. The implementation of
* nsICachingChannel would be expected to only read from the cache entry
* identified by the cache token and not try to validate it.
*
* The cache token can be treated as an opaque object; however, it can be
* QI'd to a nsICacheEntryDescriptor if more detailed information about the
* cache entry is needed.
* The cache token can be QI'd to a nsICacheEntryInfo if more detail
* about the cache entry is needed (e.g., expiration time).
*/
attribute nsISupports cacheToken;
/**
* Set/get the cache key... uniquely identifies the data in the cache.
* Holding a reference to this key DOES NOT prevent the cached data from
* being removed. It is otherwise similar to the cacheToken.
* Set/get the cache key... uniquely identifies the data in the cache
* for this channel. Holding a reference to this key does NOT prevent
* the cached data from being removed.
*
* A cache key retrieved from a particular instance of nsICachingChannel
* could be set on another instance of nsICachingChannel provided the
* underlying implementations are compatible and provided the new
* channel instance was created with the same URI. The implementation of
* nsICachingChannel would be expected to use the cache entry identified
* by the cache token. Depending on the value of nsIRequest::loadFlags,
* the cache entry may be validated, overwritten, or simply read.
*
* The fromCacheOnly flag inhibits fetching from the net if the data in the
* cache has been evicted. An error of NS_ERROR_DOCUMENT_NOT_CACHED will
* be sent to the listener's onStopRequest in this case.
* The cache key may be NULL indicating that the URI of the channel is
* sufficient to locate the same cache entry. Setting a NULL cache key
* is likewise valid.
*/
nsISupports getCacheKey();
void setCacheKey(in nsISupports cacheKey, in boolean fromCacheOnly);
attribute nsISupports cacheKey;
/**
* Specifies whether or not the data should be cached to a file. This
@ -100,11 +110,28 @@ interface nsICachingChannel : nsISupports
* and after the channel fires its OnStopRequest notification.
*/
boolean isFromCache();
};
%{C++
/**
* nsresult passed through onStopRequest if the document could not be fetched from the cache.
*/
#define NS_ERROR_DOCUMENT_NOT_CACHED NS_ERROR_GENERATE_FAILURE(NS_ERROR_MODULE_NETWORK, 70)
%}
/**************************************************************************
* Caching channel specific load flags:
*/
/**
* This load flag inhibits fetching from the net if the data in the cache
* has been evicted. An error of NS_ERROR_DOCUMENT_NOT_CACHED will be sent
* to the listener's onStopRequest in this case.
*/
const unsigned long LOAD_ONLY_FROM_CACHE = 1 << 30;
/**
* This load flag controls what happens when a document would be loaded
* from the cache to satisfy a call to AsyncOpen. If this attribute is
* set to TRUE, then the document will not be loaded from the cache. A
* stream listener can check nsICachingChannel::isFromCache to determine
* if the AsyncOpen will actually result in data being streamed.
*
* If this flag has been set, and the request can be satisfied via the
* cache, then the OnDataAvailable events will be skipped. The listener
* will only see OnStartRequest followed by OnStopRequest.
*/
const unsigned long LOAD_ONLY_IF_MODIFIED = 1 << 31;
};

View File

@ -146,7 +146,8 @@ interface nsIChannel : nsIRequest
/**************************************************************************
* Channel specific load flags:
*
* Bits 20-31 are reserved for future use by this interface
* Bits 20-31 are reserved for future use by this interface or one of its
* derivatives (e.g., see nsICachingChannel).
*/
/**

View File

@ -72,7 +72,6 @@ nsHttpChannel::nsHttpChannel()
, mIsPending(PR_FALSE)
, mApplyConversion(PR_TRUE)
, mAllowPipelining(PR_TRUE)
, mFromCacheOnly(PR_FALSE)
, mCachedContentIsValid(PR_FALSE)
, mCachedContentIsPartial(PR_FALSE)
, mResponseHeadersModified(PR_FALSE)
@ -202,6 +201,61 @@ nsHttpChannel::Init(nsIURI *uri,
// nsHttpChannel <private>
//-----------------------------------------------------------------------------
nsresult
nsHttpChannel::AsyncCall(nsAsyncCallback funcPtr)
{
nsCOMPtr<nsIEventQueueService> eqs;
nsCOMPtr<nsIEventQueue> eventQ;
nsHttpHandler::get()->GetEventQueueService(getter_AddRefs(eqs));
if (eqs)
eqs->ResolveEventQueue(NS_CURRENT_EVENTQ, getter_AddRefs(eventQ));
if (!eventQ)
return NS_ERROR_FAILURE;
nsAsyncCallEvent *event = new nsAsyncCallEvent;
if (!event)
return NS_ERROR_OUT_OF_MEMORY;
event->mFuncPtr = funcPtr;
NS_ADDREF_THIS();
PL_InitEvent(event, this,
nsHttpChannel::AsyncCall_EventHandlerFunc,
nsHttpChannel::AsyncCall_EventCleanupFunc);
PRStatus status = eventQ->PostEvent(event);
if (status != PR_SUCCESS) {
delete event;
NS_RELEASE_THIS();
return NS_ERROR_FAILURE;
}
return NS_OK;
}
void *PR_CALLBACK
nsHttpChannel::AsyncCall_EventHandlerFunc(PLEvent *ev)
{
nsHttpChannel *chan =
NS_STATIC_CAST(nsHttpChannel *, PL_GetEventOwner(ev));
nsAsyncCallEvent *ace = (nsAsyncCallEvent *) ev;
nsAsyncCallback funcPtr = ace->mFuncPtr;
if (chan) {
(chan->*funcPtr)();
NS_RELEASE(chan);
}
return nsnull;
}
void PR_CALLBACK
nsHttpChannel::AsyncCall_EventCleanupFunc(PLEvent *ev)
{
delete (nsAsyncCallEvent *) ev;
}
nsresult
nsHttpChannel::Connect(PRBool firstTime)
{
@ -221,7 +275,7 @@ nsHttpChannel::Connect(PRBool firstTime)
ioService->GetOffline(&offline);
if (offline)
mFromCacheOnly = PR_TRUE;
mLoadFlags |= LOAD_ONLY_FROM_CACHE;
// open a cache entry for this channel...
rv = OpenCacheEntry(offline, &delayed);
@ -230,7 +284,7 @@ nsHttpChannel::Connect(PRBool firstTime)
LOG(("OpenCacheEntry failed [rv=%x]\n", rv));
// if this channel is only allowed to pull from the cache, then
// we must fail if we were unable to open a cache entry.
if (mFromCacheOnly)
if (mLoadFlags & LOAD_ONLY_FROM_CACHE)
return NS_ERROR_DOCUMENT_NOT_CACHED;
// otherwise, let's just proceed without using the cache.
}
@ -251,7 +305,7 @@ nsHttpChannel::Connect(PRBool firstTime)
if (mCachedContentIsValid) {
return ReadFromCache();
}
else if (mFromCacheOnly) {
else if (mLoadFlags & LOAD_ONLY_FROM_CACHE) {
// the cache contains the requested resource, but it must be
// validated before we can reuse it. since we are not allowed
// to hit the net, there's nothing more to do. the document
@ -302,51 +356,6 @@ nsHttpChannel::AsyncAbort(nsresult status)
return NS_OK;
}
nsresult
nsHttpChannel::AsyncRedirect()
{
nsCOMPtr<nsIEventQueueService> eqs;
nsCOMPtr<nsIEventQueue> eventQ;
nsHttpHandler::get()->GetEventQueueService(getter_AddRefs(eqs));
if (eqs)
eqs->ResolveEventQueue(NS_CURRENT_EVENTQ, getter_AddRefs(eventQ));
if (!eventQ)
return NS_ERROR_FAILURE;
PLEvent *event = new PLEvent;
if (!event)
return NS_ERROR_OUT_OF_MEMORY;
NS_ADDREF_THIS();
PL_InitEvent(event, this,
nsHttpChannel::AsyncRedirect_EventHandlerFunc,
nsHttpChannel::AsyncRedirect_EventCleanupFunc);
PRStatus status = eventQ->PostEvent(event);
return status == PR_SUCCESS ? NS_OK : NS_ERROR_FAILURE;
}
void *PR_CALLBACK
nsHttpChannel::AsyncRedirect_EventHandlerFunc(PLEvent *ev)
{
nsHttpChannel *chan =
NS_STATIC_CAST(nsHttpChannel *, PL_GetEventOwner(ev));
if (chan) {
chan->HandleAsyncRedirect();
NS_RELEASE(chan);
}
return nsnull;
}
void PR_CALLBACK
nsHttpChannel::AsyncRedirect_EventCleanupFunc(PLEvent *ev)
{
delete ev;
}
void
nsHttpChannel::HandleAsyncRedirect()
{
@ -383,6 +392,26 @@ nsHttpChannel::HandleAsyncRedirect()
mLoadGroup->RemoveRequest(this, nsnull, mStatus);
}
void
nsHttpChannel::HandleAsyncNotModified()
{
LOG(("nsHttpChannel::HandleAsyncNotModified [this=%p]\n", this));
if (mListener) {
mListener->OnStartRequest(this, mListenerContext);
mListener->OnStopRequest(this, mListenerContext, mStatus);
mListener = 0;
mListenerContext = 0;
}
CloseCacheEntry(NS_OK);
mIsPending = PR_FALSE;
if (mLoadGroup)
mLoadGroup->RemoveRequest(this, nsnull, mStatus);
}
nsresult
nsHttpChannel::SetupTransaction()
{
@ -1237,7 +1266,13 @@ nsHttpChannel::ReadFromCache()
// header, otherwise we'll have to treat this like a normal 200 response.
if (mResponseHead && (mResponseHead->Status() / 100 == 3)
&& (mResponseHead->PeekHeader(nsHttp::Location)))
return AsyncRedirect();
return AsyncCall(&nsHttpChannel::HandleAsyncRedirect);
// have we been configured to skip reading from the cache?
if ((mLoadFlags & LOAD_ONLY_IF_MODIFIED) && !mCachedContentIsPartial) {
LOG(("skipping read from cache based on LOAD_ONLY_IF_MODIFIED load flag\n"));
return AsyncCall(&nsHttpChannel::HandleAsyncNotModified);
}
// Get a transport to the cached data...
rv = mCacheEntry->GetTransport(getter_AddRefs(mCacheTransport));
@ -3149,12 +3184,11 @@ nsHttpChannel::GetCacheKey(nsISupports **key)
}
NS_IMETHODIMP
nsHttpChannel::SetCacheKey(nsISupports *key, PRBool fromCacheOnly)
nsHttpChannel::SetCacheKey(nsISupports *key)
{
nsresult rv;
LOG(("nsHttpChannel::SetCacheKey [this=%x key=%x fromCacheOnly=%d]\n",
this, key, fromCacheOnly));
LOG(("nsHttpChannel::SetCacheKey [this=%x key=%x]\n", this, key));
// can only set the cache key if a load is not in progress
NS_ENSURE_TRUE(!mIsPending, NS_ERROR_IN_PROGRESS);
@ -3169,8 +3203,6 @@ nsHttpChannel::SetCacheKey(nsISupports *key, PRBool fromCacheOnly)
rv = container->GetData(&mPostID);
if (NS_FAILED(rv)) return rv;
}
mFromCacheOnly = fromCacheOnly;
return NS_OK;
}
@ -3210,9 +3242,15 @@ nsHttpChannel::GetCacheFile(nsIFile **cacheFile)
NS_IMETHODIMP
nsHttpChannel::IsFromCache(PRBool *value)
{
if (!mIsPending)
return NS_ERROR_NOT_AVAILABLE;
// return false if reading a partial cache entry; the data isn't entirely
// from the cache!
*value = (mCacheReadRequest && !mCachedContentIsPartial);
*value = (mCacheReadRequest || (mLoadFlags & LOAD_ONLY_IF_MODIFIED)) &&
mCachedContentIsValid && !mCachedContentIsPartial;
return NS_OK;
}
@ -3245,7 +3283,7 @@ nsHttpChannel::OnCacheEntryAvailable(nsICacheEntryDescriptor *entry,
LOG(("channel was canceled [this=%x status=%x]\n", this, mStatus));
rv = mStatus;
}
else if (mFromCacheOnly && NS_FAILED(status))
else if ((mLoadFlags & LOAD_ONLY_FROM_CACHE) && NS_FAILED(status))
// if this channel is only allowed to pull from the cache, then
// we must fail if we were unable to open a cache entry.
rv = NS_ERROR_DOCUMENT_NOT_CACHED;

View File

@ -94,10 +94,21 @@ public:
nsIProxyInfo* proxyInfo);
private:
typedef void (nsHttpChannel:: *nsAsyncCallback)(void);
//
// AsyncCall may be used to call a member function asynchronously.
//
struct nsAsyncCallEvent : PLEvent
{
nsAsyncCallback mFuncPtr;
};
nsresult AsyncCall(nsAsyncCallback funcPtr);
nsresult Connect(PRBool firstTime = PR_TRUE);
nsresult AsyncAbort(nsresult status);
nsresult AsyncRedirect();
void HandleAsyncRedirect();
void HandleAsyncNotModified();
nsresult SetupTransaction();
void ApplyContentConversions();
nsresult ProcessResponse();
@ -136,8 +147,8 @@ private:
nsresult GetCurrentPath(nsACString &);
void ClearPasswordManagerEntry(const char *host, PRInt32 port, const char *realm, const PRUnichar *user);
static void *PR_CALLBACK AsyncRedirect_EventHandlerFunc(PLEvent *);
static void PR_CALLBACK AsyncRedirect_EventCleanupFunc(PLEvent *);
static void *PR_CALLBACK AsyncCall_EventHandlerFunc(PLEvent *);
static void PR_CALLBACK AsyncCall_EventCleanupFunc(PLEvent *);
private:
nsCOMPtr<nsIURI> mOriginalURI;
@ -193,7 +204,6 @@ private:
PRPackedBool mIsPending;
PRPackedBool mApplyConversion;
PRPackedBool mAllowPipelining;
PRPackedBool mFromCacheOnly;
PRPackedBool mCachedContentIsValid;
PRPackedBool mCachedContentIsPartial;
PRPackedBool mResponseHeadersModified;