Bug 579846 - nsIHttpChannel::SetResponseHeader should work after the stream has ended [Fennec part], r=jduell, a=betaN+

This commit is contained in:
Honza Bambas 2011-01-23 23:49:30 +01:00
parent 63032f8fb3
commit 1cc1cc4167
10 changed files with 201 additions and 71 deletions

View File

@ -67,7 +67,8 @@ interface nsICacheInfoChannel_GECKO_2_0 : nsISupports
* Return an object that while not released prevents the channel from
* releasing the cache entry after all work on it has been done. Used by
* asynchronous consumers that needs to work with the cache entry long after
* onStopRequest has been called.
* onStopRequest has been called. Must be acquired no later than during
* onStopRequest.
*/
readonly attribute nsISupports cacheEntryClosePreventer;
};

View File

@ -64,6 +64,7 @@ HttpBaseChannel::HttpBaseChannel()
, mPriority(PRIORITY_NORMAL)
, mCaps(0)
, mRedirectionLimit(gHttpHandler->RedirectionLimit())
, mCacheEntryClosePreventionCount(0)
, mApplyConversion(PR_TRUE)
, mCanceled(PR_FALSE)
, mIsPending(PR_FALSE)
@ -1405,6 +1406,19 @@ HttpBaseChannel::SetupReplacementChannel(nsIURI *newURI,
//------------------------------------------------------------------------------
NS_IMPL_ISUPPORTS0(HttpBaseChannel::CacheEntryClosePreventer)
HttpBaseChannel::CacheEntryClosePreventer::CacheEntryClosePreventer(
HttpBaseChannel* channel)
: mChannel(channel)
{
mChannel->OnIncreaseCacheEntryClosePreventCount();
}
HttpBaseChannel::CacheEntryClosePreventer::~CacheEntryClosePreventer()
{
mChannel->OnDecreaseCacheEntryClosePreventCount();
}
} // namespace net
} // namespace mozilla

View File

@ -190,6 +190,26 @@ public:
nsHttpRequestHead * GetRequestHead() { return &mRequestHead; }
protected:
// Increment/decrement counter that, when positive, keeps channel's cache
// entry open after OnStopRequest if needed.
virtual void OnIncreaseCacheEntryClosePreventCount() = 0;
virtual void OnDecreaseCacheEntryClosePreventCount() = 0;
// Object created and returned on every call to cacheEntryClosePreventer
// attribute. Calls the two methods right above in its constructor and
// destructor respectively.
class CacheEntryClosePreventer : public nsISupports
{
public:
NS_DECL_ISUPPORTS
CacheEntryClosePreventer(HttpBaseChannel* channel);
private:
~CacheEntryClosePreventer();
nsRefPtr<HttpBaseChannel> mChannel;
};
nsresult ApplyContentConversions();
void AddCookiesToRequest();
@ -238,6 +258,10 @@ protected:
PRUint8 mCaps;
PRUint8 mRedirectionLimit;
// Keeps the number of CacheEntryClosePreventer objects being held,
// positive value prevents the cache entry from release in OnStopRequest.
PRUint32 mCacheEntryClosePreventionCount;
PRUint32 mApplyConversion : 1;
PRUint32 mCanceled : 1;
PRUint32 mIsPending : 1;

View File

@ -66,7 +66,7 @@ HttpChannelChild::HttpChannelChild()
, mSendResumeAt(false)
, mSuspendCount(0)
, mIPCOpen(false)
, mKeptAlive(false)
, mDeferredIPDLClose(false)
{
LOG(("Creating HttpChannelChild @%x\n", this));
}
@ -90,8 +90,8 @@ NS_IMETHODIMP_(nsrefcnt) HttpChannelChild::Release()
--mRefCnt;
NS_LOG_RELEASE(this, mRefCnt, "HttpChannelChild");
if (mRefCnt == 1 && mKeptAlive && mIPCOpen) {
mKeptAlive = false;
if (mRefCnt == 1 && mDeferredIPDLClose && mIPCOpen) {
mDeferredIPDLClose = false;
// Send_delete calls NeckoChild::DeallocPHttpChannel, which will release
// again to refcount==0
PHttpChannelChild::Send__delete__(this);
@ -112,6 +112,7 @@ NS_INTERFACE_MAP_BEGIN(HttpChannelChild)
NS_INTERFACE_MAP_ENTRY(nsIHttpChannel)
NS_INTERFACE_MAP_ENTRY(nsIHttpChannelInternal)
NS_INTERFACE_MAP_ENTRY(nsICacheInfoChannel)
NS_INTERFACE_MAP_ENTRY(nsICacheInfoChannel_GECKO_2_0)
NS_INTERFACE_MAP_ENTRY(nsIResumableChannel)
NS_INTERFACE_MAP_ENTRY(nsISupportsPriority)
NS_INTERFACE_MAP_ENTRY(nsIProxiedChannel)
@ -391,16 +392,7 @@ HttpChannelChild::OnStopRequest(const nsresult& statusCode)
mLoadGroup->RemoveRequest(this, nsnull, mStatus);
}
if (!(mLoadFlags & LOAD_DOCUMENT_URI)) {
// This calls NeckoChild::DeallocPHttpChannel(), which deletes |this| if IPDL
// holds the last reference. Don't rely on |this| existing after here.
PHttpChannelChild::Send__delete__(this);
} else {
// We need to keep the document loading channel alive for further
// communication, mainly for collecting a security state values.
mKeptAlive = true;
SendDocumentChannelCleanup();
}
MaybeCloseIPDL();
}
class ProgressEvent : public ChannelEvent
@ -557,8 +549,7 @@ HttpChannelChild::OnCancel(const nsresult& status)
mListener = NULL;
mListenerContext = NULL;
if (mIPCOpen)
PHttpChannelChild::Send__delete__(this);
MaybeCloseIPDL(true /* Force document channel deletion */);
}
class DeleteSelfEvent : public ChannelEvent
@ -584,7 +575,34 @@ HttpChannelChild::RecvDeleteSelf()
void
HttpChannelChild::DeleteSelf()
{
Send__delete__(this);
MaybeCloseIPDL(true /* Force document channel deletion */);
}
void
HttpChannelChild::MaybeCloseIPDL(bool forceDocumentLoadDeletion)
{
if (mCacheEntryClosePreventionCount) {
// Someone is still holding the cache close prevention lock, keep this
// channel alive to be able to communicate lock release to the parent.
mDeferredIPDLClose = true;
return;
}
if ((mLoadFlags & LOAD_DOCUMENT_URI) && !forceDocumentLoadDeletion) {
// We need to keep the document loading channel alive for further
// communication, mainly for collecting a security state values.
mDeferredIPDLClose = true;
if (mIPCOpen)
SendDocumentChannelCleanup();
return;
}
// No need to keep the child channel, delete it.
// This calls NeckoChild::DeallocPHttpChannel(), which deletes |this| if IPDL
// holds the last reference. Don't rely on |this| existing after here.
mDeferredIPDLClose = false;
if (mIPCOpen)
PHttpChannelChild::Send__delete__(this);
}
class Redirect1Event : public ChannelEvent
@ -996,6 +1014,21 @@ HttpChannelChild::SetRequestHeader(const nsACString& aHeader,
return NS_OK;
}
NS_IMETHODIMP
HttpChannelChild::SetResponseHeader(const nsACString& aHeader,
const nsACString& aValue,
PRBool aMerge)
{
nsresult rv = HttpBaseChannel::SetResponseHeader(aHeader, aValue, aMerge);
if (NS_FAILED(rv))
return rv;
nsCString header(aHeader);
nsCString value(aValue);
SendSetResponseHeader(header, value, aMerge);
return NS_OK;
}
//-----------------------------------------------------------------------------
// HttpChannelChild::nsIHttpChannelInternal
//-----------------------------------------------------------------------------
@ -1053,6 +1086,36 @@ HttpChannelChild::IsFromCache(PRBool *value)
return NS_OK;
}
//-----------------------------------------------------------------------------
// nsHttpChannel::nsICacheInfoChannel_GECKO_2_0
//-----------------------------------------------------------------------------
NS_IMETHODIMP
HttpChannelChild::GetCacheEntryClosePreventer(nsISupports** _retval)
{
NS_ADDREF(*_retval = new CacheEntryClosePreventer(this));
return NS_OK;
}
void
HttpChannelChild::OnIncreaseCacheEntryClosePreventCount()
{
LOG(("HttpChannelChild::mCacheEntryClosePreventionCount increased to %d, [this=%x]",
mCacheEntryClosePreventionCount, this));
++mCacheEntryClosePreventionCount;
}
void
HttpChannelChild::OnDecreaseCacheEntryClosePreventCount()
{
LOG(("HttpChannelChild::mCacheEntryClosePreventionCount decreased to %d, [this=%x]",
mCacheEntryClosePreventionCount, this));
--mCacheEntryClosePreventionCount;
if (!mCacheEntryClosePreventionCount && mDeferredIPDLClose)
MaybeCloseIPDL();
}
//-----------------------------------------------------------------------------
// HttpChannelChild::nsIResumableChannel
//-----------------------------------------------------------------------------

View File

@ -70,6 +70,7 @@ namespace net {
class HttpChannelChild : public PHttpChannelChild
, public HttpBaseChannel
, public nsICacheInfoChannel
, public nsICacheInfoChannel_GECKO_2_0
, public nsIProxiedChannel
, public nsITraceableChannel
, public nsIApplicationCacheChannel
@ -82,6 +83,7 @@ class HttpChannelChild : public PHttpChannelChild
public:
NS_DECL_ISUPPORTS_INHERITED
NS_DECL_NSICACHEINFOCHANNEL
NS_DECL_NSICACHEINFOCHANNEL_GECKO_2_0
NS_DECL_NSIPROXIEDCHANNEL
NS_DECL_NSITRACEABLECHANNEL
NS_DECL_NSIAPPLICATIONCACHECONTAINER
@ -107,6 +109,9 @@ public:
NS_IMETHOD SetRequestHeader(const nsACString& aHeader,
const nsACString& aValue,
PRBool aMerge);
NS_IMETHOD SetResponseHeader(const nsACString& aHeader,
const nsACString& aValue,
PRBool aMerge);
// nsIHttpChannelInternal
NS_IMETHOD SetupFallbackChannel(const char *aFallbackKey);
// nsISupportsPriority
@ -166,7 +171,11 @@ private:
PRUint32 mSuspendCount;
bool mIPCOpen;
bool mKeptAlive;
// Indicates IPDL channel was not deleted during OnStopRequest, because
// 1) this a document-level channel (IPDL channel will be deleted during
// destructor); or 2) mCacheEntryClosePreventionCount is non-zero (IPDL
// channel will be deleted when count hits 0).
bool mDeferredIPDLClose;
void OnStartRequest(const nsHttpResponseHead& responseHead,
const PRBool& useResponseHead,
@ -190,6 +199,11 @@ private:
void Redirect3Complete();
void DeleteSelf();
void MaybeCloseIPDL(bool forceDocumentLoadDeletion = false);
virtual void OnIncreaseCacheEntryClosePreventCount();
virtual void OnDecreaseCacheEntryClosePreventCount();
friend class StartRequestEvent;
friend class StopRequestEvent;
friend class DataAvailableEvent;

View File

@ -85,6 +85,9 @@ HttpChannelParent::ActorDestroy(ActorDestroyReason why)
// We may still have refcount>0 if nsHttpChannel hasn't called OnStopRequest
// yet, but we must not send any more msgs to child.
mIPCClosed = true;
// As we know the child channel has finished, let the cache entry close.
mCacheClosePreventer = 0;
}
//-----------------------------------------------------------------------------
@ -297,12 +300,28 @@ HttpChannelParent::RecvCancel(const nsresult& status)
bool
HttpChannelParent::RecvSetCacheTokenCachedCharset(const nsCString& charset)
{
if (mCacheDescriptor)
mCacheDescriptor->SetMetaDataElement("charset",
nsHttpChannel *chan = static_cast<nsHttpChannel *>(mChannel.get());
nsresult rv;
nsCOMPtr<nsICacheEntryDescriptor> cacheDescriptor;
rv = chan->GetCacheToken(getter_AddRefs(cacheDescriptor));
if (NS_SUCCEEDED(rv))
cacheDescriptor->SetMetaDataElement("charset",
PromiseFlatCString(charset).get());
return true;
}
bool
HttpChannelParent::RecvSetResponseHeader(const nsCString& header,
const nsCString& value,
const bool& merge)
{
nsHttpChannel *chan = static_cast<nsHttpChannel *>(mChannel.get());
chan->SetResponseHeader(header, value, merge);
return true;
}
bool
HttpChannelParent::RecvUpdateAssociatedContentSecurity(const PRInt32& high,
const PRInt32& low,
@ -353,7 +372,7 @@ HttpChannelParent::RecvDocumentChannelCleanup()
{
// We must clear the cache entry here, else we'll block other channels from
// reading it if we've got it open for writing.
mCacheDescriptor = 0;
mCacheClosePreventer = 0;
return true;
}
@ -405,9 +424,14 @@ HttpChannelParent::OnStartRequest(nsIRequest *aRequest, nsISupports *aContext)
if (encodedChannel)
encodedChannel->SetApplyConversion(PR_FALSE);
// Keep the cache entry for future use in RecvSetCacheTokenCachedCharset().
// It could be already released by nsHttpChannel at that time.
chan->GetCacheToken(getter_AddRefs(mCacheDescriptor));
// Prevent cache entry from being closed during HttpChannel::OnStopRequest:
// - We need the cache entry for RecvSetCacheTokenCachedCharset()
// - The child channel may call GetCacheEntryClosePreventer, so we have to
// call it now (otherwise we could hit OnStopRequest and close entry
// before child gets a chance to keep it open).
// We close entry either when RecvDocumentChannelCleanup is called, or the
// IPDL channel is deleted.
chan->GetCacheEntryClosePreventer(getter_AddRefs(mCacheClosePreventer));
nsCString secInfoSerialization;
nsCOMPtr<nsISupports> secInfoSupp;
@ -428,12 +452,15 @@ HttpChannelParent::OnStartRequest(nsIRequest *aRequest, nsISupports *aContext)
tuple->mMerge = false;
}
nsCOMPtr<nsICacheEntryDescriptor> cacheDescriptor;
chan->GetCacheToken(getter_AddRefs(cacheDescriptor));
if (mIPCClosed ||
!SendOnStartRequest(responseHead ? *responseHead : nsHttpResponseHead(),
!!responseHead,
headers,
isFromCache,
mCacheDescriptor ? PR_TRUE : PR_FALSE,
cacheDescriptor ? PR_TRUE : PR_FALSE,
expirationTime, cachedCharset, secInfoSerialization))
{
return NS_ERROR_UNEXPECTED;

View File

@ -99,6 +99,9 @@ protected:
virtual bool RecvConnectChannel(const PRUint32& channelId);
virtual bool RecvSetPriority(const PRUint16& priority);
virtual bool RecvSetCacheTokenCachedCharset(const nsCString& charset);
virtual bool RecvSetResponseHeader(const nsCString& header,
const nsCString& value,
const bool& merge);
virtual bool RecvSuspend();
virtual bool RecvResume();
virtual bool RecvCancel(const nsresult& status);
@ -119,7 +122,7 @@ protected:
private:
nsCOMPtr<nsIChannel> mChannel;
nsCOMPtr<nsICacheEntryDescriptor> mCacheDescriptor;
nsCOMPtr<nsISupports> mCacheClosePreventer;
bool mIPCClosed; // PHttpChannel actor has been Closed()
nsCOMPtr<nsIChannel> mRedirectChannel;

View File

@ -86,6 +86,7 @@ parent:
SetPriority(PRUint16 priority);
SetCacheTokenCachedCharset(nsCString charset);
SetResponseHeader(nsCString header, nsCString value, bool merge);
UpdateAssociatedContentSecurity(PRInt32 high,
PRInt32 low,

View File

@ -121,7 +121,6 @@ nsHttpChannel::nsHttpChannel()
, mAsyncCacheOpen(PR_FALSE)
, mPendingAsyncCallOnResume(nsnull)
, mSuspendCount(0)
, mCacheEntryClosePreventionCount(0)
, mCachedContentIsValid(PR_FALSE)
, mCachedContentIsPartial(PR_FALSE)
, mTransactionReplaced(PR_FALSE)
@ -4231,46 +4230,33 @@ nsHttpChannel::SetCacheTokenCachedCharset(const nsACString &aCharset)
// nsHttpChannel::nsIHttpChannelInternal_GECKO_2_0
//-----------------------------------------------------------------------------
class HttpChannelCacheEntryClosePreventer : public nsISupports
{
public:
NS_DECL_ISUPPORTS
HttpChannelCacheEntryClosePreventer(nsHttpChannel* channel)
: mChannel(channel)
{
++mChannel->mCacheEntryClosePreventionCount;
LOG(("mCacheEntryClosePreventionCount increased to %d, [this=%x]",
mChannel->mCacheEntryClosePreventionCount,
mChannel.get()));
}
private:
~HttpChannelCacheEntryClosePreventer()
{
--mChannel->mCacheEntryClosePreventionCount;
LOG(("mCacheEntryClosePreventionCount decreased to %d, [this=%x]",
mChannel->mCacheEntryClosePreventionCount,
mChannel.get()));
if (!mChannel->mCacheEntryClosePreventionCount &&
mChannel->mDeferredCacheEntryClose) {
mChannel->CloseCacheEntryInternal();
}
}
nsRefPtr<nsHttpChannel> mChannel;
};
NS_IMPL_ISUPPORTS0(HttpChannelCacheEntryClosePreventer)
NS_IMETHODIMP
nsHttpChannel::GetCacheEntryClosePreventer(nsISupports** _retval)
{
NS_ADDREF(*_retval = new HttpChannelCacheEntryClosePreventer(this));
NS_ADDREF(*_retval = new CacheEntryClosePreventer(this));
return NS_OK;
}
void
nsHttpChannel::OnIncreaseCacheEntryClosePreventCount()
{
++mCacheEntryClosePreventionCount;
LOG(("nsHttpChannel::mCacheEntryClosePreventionCount increased to %d, [this=%x]",
mCacheEntryClosePreventionCount, this));
}
void
nsHttpChannel::OnDecreaseCacheEntryClosePreventCount()
{
--mCacheEntryClosePreventionCount;
LOG(("nsHttpChannel::mCacheEntryClosePreventionCount decreased to %d, [this=%x]",
mCacheEntryClosePreventionCount, this));
if (!mCacheEntryClosePreventionCount && mDeferredCacheEntryClose) {
CloseCacheEntryInternal();
}
}
//-----------------------------------------------------------------------------
// nsHttpChannel::nsICachingChannel
//-----------------------------------------------------------------------------

View File

@ -276,6 +276,9 @@ private:
*/
nsresult Hash(const char *buf, nsACString &hash);
virtual void OnIncreaseCacheEntryClosePreventCount();
virtual void OnDecreaseCacheEntryClosePreventCount();
private:
nsCOMPtr<nsISupports> mSecurityInfo;
nsCOMPtr<nsICancelable> mProxyRequest;
@ -327,10 +330,6 @@ private:
nsCOMPtr<nsIChannel> mRedirectChannel;
PRUint32 mRedirectType;
// Hold counter, keeps the number of calls to holdCacheEntry(), positive
// value prevents the cache entry from release in OnStopRequest.
PRUint32 mCacheEntryClosePreventionCount;
// state flags
PRUint32 mCachedContentIsValid : 1;
PRUint32 mCachedContentIsPartial : 1;
@ -368,8 +367,6 @@ private:
nsresult WaitForRedirectCallback();
void PushRedirectAsyncFunc(nsContinueRedirectionFunc func);
void PopRedirectAsyncFunc(nsContinueRedirectionFunc func);
friend class HttpChannelCacheEntryClosePreventer;
};
#endif // nsHttpChannel_h__