Bug 722033 - Use asyncOpenCacheEntry() in nsHttpChannel::OpenOfflineCacheEntryForWriting()
authorMichal Novotny <michal.novotny@gmail.com>
Thu, 22 Mar 2012 23:53:55 +0100
changeset 91900 5b61e3d75735e331ac401f5f7b057c9997d03aa0
parent 91899 6c8d1da1e1db869a7a9588be955142085810c445
child 91901 835ac64631ec9c647c7ab93861bb9b9cce2313c9
push idunknown
push userunknown
push dateunknown
bugs722033
milestone14.0a1
Bug 722033 - Use asyncOpenCacheEntry() in nsHttpChannel::OpenOfflineCacheEntryForWriting()
netwerk/protocol/http/nsHttpChannel.cpp
netwerk/protocol/http/nsHttpChannel.h
--- a/netwerk/protocol/http/nsHttpChannel.cpp
+++ b/netwerk/protocol/http/nsHttpChannel.cpp
@@ -120,17 +120,16 @@ AutoRedirectVetoNotifier::ReportRedirect
 
 nsHttpChannel::nsHttpChannel()
     : HttpAsyncAborter<nsHttpChannel>(this)
     , mLogicalOffset(0)
     , mCacheAccess(0)
     , mPostID(0)
     , mRequestTime(0)
     , mOnCacheEntryAvailableCallback(nsnull)
-    , mAsyncCacheOpen(false)
     , mCachedContentIsValid(false)
     , mCachedContentIsPartial(false)
     , mTransactionReplaced(false)
     , mAuthRetryPending(false)
     , mResuming(false)
     , mInitedCacheEntry(false)
     , mCacheForOfflineUse(false)
     , mCachingOpportunistically(false)
@@ -241,16 +240,22 @@ nsHttpChannel::Connect(bool firstTime)
         if (mResuming && (mLoadFlags & LOAD_ONLY_FROM_CACHE)) {
             LOG(("Resuming from cache is not supported yet"));
             return NS_ERROR_DOCUMENT_NOT_CACHED;
         }
 
         // open a cache entry for this channel...
         rv = OpenCacheEntry();
 
+        // do not continue if asyncOpenCacheEntry is in progress
+        if (mOnCacheEntryAvailableCallback) {
+            NS_ASSERTION(NS_SUCCEEDED(rv), "Unexpected state");
+            return NS_OK;
+        }
+
         if (NS_FAILED(rv)) {
             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 (mLoadFlags & LOAD_ONLY_FROM_CACHE) {
                 // If we have a fallback URI (and we're not already
                 // falling back), process the fallback asynchronously.
                 if (!mFallbackChannel && !mFallbackKey.IsEmpty()) {
@@ -261,20 +266,20 @@ nsHttpChannel::Connect(bool firstTime)
             // otherwise, let's just proceed without using the cache.
         }
 
         // if cacheForOfflineUse has been set, open up an offline cache
         // entry to update
         if (mCacheForOfflineUse) {
             rv = OpenOfflineCacheEntryForWriting();
             if (NS_FAILED(rv)) return rv;
+
+            if (mOnCacheEntryAvailableCallback)
+                return NS_OK;
         }
-
-        if (NS_SUCCEEDED(rv) && mAsyncCacheOpen)
-            return NS_OK;
     }
 
     // we may or may not have a cache entry at this point
     if (mCacheEntry) {
         // inspect the cache entry to determine whether or not we need to go
         // out to net to validate it.  this call sets mCachedContentIsValid
         // and may set request headers as required for cache validation.
         rv = CheckCache();
@@ -2029,17 +2034,17 @@ IsSubRangeRequest(nsHttpRequestHead &aRe
     return !byteRange.EqualsLiteral("bytes=0-");
 }
 
 nsresult
 nsHttpChannel::OpenCacheEntry()
 {
     nsresult rv;
 
-    mAsyncCacheOpen = false;
+    NS_ASSERTION(!mOnCacheEntryAvailableCallback, "Unexpected state");
     mLoadedFromApplicationCache = false;
 
     LOG(("nsHttpChannel::OpenCacheEntry [this=%p]", this));
 
     // make sure we're not abusing this function
     NS_PRECONDITION(!mCacheEntry, "cache entry already open");
 
     nsCAutoString cacheKey;
@@ -2123,70 +2128,65 @@ nsHttpChannel::OpenCacheEntry()
         // the offline cache entry non-atomically. ACCESS_READ will prevent us
         // from writing to the offline cache as a normal cache entry.
         rv = session->AsyncOpenCacheEntry(
             cacheKey,
             nsICache::ACCESS_READ,
             this,
             mLoadFlags & LOAD_BYPASS_LOCAL_CACHE_IF_BUSY);
 
-        if (NS_SUCCEEDED(rv)) {
-            mAsyncCacheOpen = true;
+        if (NS_SUCCEEDED(rv))
             return NS_OK;
-        }
+
+        mOnCacheEntryAvailableCallback = nsnull;
 
         // opening cache entry failed
-        return OnOfflineCacheEntryAvailable(nsnull, nsICache::ACCESS_NONE,
-                                            rv, true);
+        return OnOfflineCacheEntryAvailable(nsnull, nsICache::ACCESS_NONE, rv);
     }
 
-    return OpenNormalCacheEntry(true);
+    return OpenNormalCacheEntry();
 }
 
 nsresult
 nsHttpChannel::OnOfflineCacheEntryAvailable(nsICacheEntryDescriptor *aEntry,
                                             nsCacheAccessMode aAccess,
-                                            nsresult aEntryStatus,
-                                            bool aIsSync)
+                                            nsresult aEntryStatus)
 {
     nsresult rv;
 
     if (NS_SUCCEEDED(aEntryStatus)) {
         // We successfully opened an offline cache session and the entry,
         // so indicate we will load from the offline cache.
         mLoadedFromApplicationCache = true;
         mCacheEntry = aEntry;
         mCacheAccess = aAccess;
     }
 
     if (aEntryStatus == NS_ERROR_CACHE_WAIT_FOR_VALIDATION) {
         LOG(("bypassing local cache since it is busy\n"));
         // Don't try to load normal cache entry
-        return aIsSync ? NS_ERROR_NOT_AVAILABLE : Connect(false);
+        return NS_ERROR_NOT_AVAILABLE;
     }
 
     if (mCanceled && NS_FAILED(mStatus)) {
         LOG(("channel was canceled [this=%p status=%x]\n", this, mStatus));
         return mStatus;
     }
 
     if (NS_SUCCEEDED(aEntryStatus))
-        // Called from OnCacheEntryAvailable, advance to the next state
-        return Connect(false);
+        return NS_OK;
 
     if (!mCacheForOfflineUse && !mFallbackChannel) {
         nsCAutoString cacheKey;
         GenerateCacheKey(mPostID, cacheKey);
 
         // Check for namespace match.
         nsCOMPtr<nsIApplicationCacheNamespace> namespaceEntry;
         rv = mApplicationCache->GetMatchingNamespace
             (cacheKey, getter_AddRefs(namespaceEntry));
-        if (NS_FAILED(rv) && !aIsSync)
-            return Connect(false);
         NS_ENSURE_SUCCESS(rv, rv);
 
         PRUint32 namespaceType = 0;
         if (!namespaceEntry ||
             NS_FAILED(namespaceEntry->GetItemType(&namespaceType)) ||
             (namespaceType &
              (nsIApplicationCacheNamespace::NAMESPACE_FALLBACK |
               nsIApplicationCacheNamespace::NAMESPACE_OPPORTUNISTIC |
@@ -2194,24 +2194,22 @@ nsHttpChannel::OnOfflineCacheEntryAvaila
             // When loading from an application cache, only items
             // on the whitelist or matching a
             // fallback/opportunistic namespace should hit the
             // network...
             mLoadFlags |= LOAD_ONLY_FROM_CACHE;
 
             // ... and if there were an application cache entry,
             // we would have found it earlier.
-            return aIsSync ? NS_ERROR_CACHE_KEY_NOT_FOUND : Connect(false);
+            return NS_ERROR_CACHE_KEY_NOT_FOUND;
         }
 
         if (namespaceType &
             nsIApplicationCacheNamespace::NAMESPACE_FALLBACK) {
             rv = namespaceEntry->GetData(mFallbackKey);
-            if (NS_FAILED(rv) && !aIsSync)
-                return Connect(false);
             NS_ENSURE_SUCCESS(rv, rv);
         }
 
         if ((namespaceType &
              nsIApplicationCacheNamespace::NAMESPACE_OPPORTUNISTIC) &&
             mLoadFlags & LOAD_DOCUMENT_URI) {
             // Document loads for items in an opportunistic namespace
             // should be placed in the offline cache.
@@ -2219,22 +2217,22 @@ nsHttpChannel::OnOfflineCacheEntryAvaila
             mApplicationCache->GetClientID(clientID);
 
             mCacheForOfflineUse = !clientID.IsEmpty();
             SetOfflineCacheClientID(clientID);
             mCachingOpportunistically = true;
         }
     }
 
-    return OpenNormalCacheEntry(aIsSync);
+    return OpenNormalCacheEntry();
 }
 
 
 nsresult
-nsHttpChannel::OpenNormalCacheEntry(bool aIsSync)
+nsHttpChannel::OpenNormalCacheEntry()
 {
     NS_ASSERTION(!mCacheEntry, "We have already mCacheEntry");
 
     nsresult rv;
 
     nsCAutoString cacheKey;
     GenerateCacheKey(mPostID, cacheKey);
 
@@ -2252,36 +2250,29 @@ nsHttpChannel::OpenNormalCacheEntry(bool
     mOnCacheEntryAvailableCallback =
         &nsHttpChannel::OnNormalCacheEntryAvailable;
     rv = session->AsyncOpenCacheEntry(
         cacheKey,
         accessRequested,
         this,
         mLoadFlags & LOAD_BYPASS_LOCAL_CACHE_IF_BUSY);
 
-    if (NS_SUCCEEDED(rv)) {
-        mAsyncCacheOpen = true;
+    if (NS_SUCCEEDED(rv))
         return NS_OK;
-    }
-
-    if (!aIsSync)
-        // Called from OnCacheEntryAvailable, advance to the next state
-        rv = Connect(false);
+
+    mOnCacheEntryAvailableCallback = nsnull;
 
     return rv;
 }
 
 nsresult
 nsHttpChannel::OnNormalCacheEntryAvailable(nsICacheEntryDescriptor *aEntry,
                                            nsCacheAccessMode aAccess,
-                                           nsresult aEntryStatus,
-                                           bool aIsSync)
+                                           nsresult aEntryStatus)
 {
-    NS_ASSERTION(!aIsSync, "aIsSync should be false");
-
     if (NS_SUCCEEDED(aEntryStatus)) {
         mCacheEntry = aEntry;
         mCacheAccess = aAccess;
     }
 
     if (aEntryStatus == NS_ERROR_CACHE_WAIT_FOR_VALIDATION) {
         LOG(("bypassing local cache since it is busy\n"));
     }
@@ -2292,17 +2283,17 @@ nsHttpChannel::OnNormalCacheEntryAvailab
     }
 
     if ((mLoadFlags & LOAD_ONLY_FROM_CACHE) && NS_FAILED(aEntryStatus))
         // if this channel is only allowed to pull from the cache, then
         // we must fail if we were unable to open a cache entry.
         return NS_ERROR_DOCUMENT_NOT_CACHED;
 
     // advance to the next state...
-    return Connect(false);
+    return NS_OK;
 }
 
 
 nsresult
 nsHttpChannel::OpenOfflineCacheEntryForWriting()
 {
     nsresult rv;
 
@@ -2339,32 +2330,53 @@ nsHttpChannel::OpenOfflineCacheEntryForW
     if (NS_FAILED(rv)) return rv;
 
     rv = serv->CreateSession(mOfflineCacheClientID.get(),
                              nsICache::STORE_OFFLINE,
                              nsICache::STREAM_BASED,
                              getter_AddRefs(session));
     if (NS_FAILED(rv)) return rv;
 
-    rv = session->OpenCacheEntry(cacheKey, nsICache::ACCESS_READ_WRITE,
-                                 false, getter_AddRefs(mOfflineCacheEntry));
-
-    if (rv == NS_ERROR_CACHE_WAIT_FOR_VALIDATION) {
+    mOnCacheEntryAvailableCallback =
+        &nsHttpChannel::OnOfflineCacheEntryForWritingAvailable;
+    rv = session->AsyncOpenCacheEntry(cacheKey, nsICache::ACCESS_READ_WRITE,
+                                      this, true);
+    if (NS_SUCCEEDED(rv))
+        return NS_OK;
+
+    mOnCacheEntryAvailableCallback = nsnull;
+
+    return rv;
+}
+
+nsresult
+nsHttpChannel::OnOfflineCacheEntryForWritingAvailable(
+    nsICacheEntryDescriptor *aEntry,
+    nsCacheAccessMode aAccess,
+    nsresult aEntryStatus)
+{
+    if (NS_SUCCEEDED(aEntryStatus)) {
+        mOfflineCacheEntry = aEntry;
+        mOfflineCacheAccess = aAccess;
+    }
+
+    if (aEntryStatus == NS_ERROR_CACHE_WAIT_FOR_VALIDATION) {
         // access to the cache entry has been denied (because the cache entry
         // is probably in use by another channel).  Either the cache is being
         // read from (we're offline) or it's being updated elsewhere.
-        return NS_OK;
+        aEntryStatus = NS_OK;
     }
 
-    if (NS_SUCCEEDED(rv)) {
-        mOfflineCacheEntry->GetAccessGranted(&mOfflineCacheAccess);
-        LOG(("got offline cache entry [access=%x]\n", mOfflineCacheAccess));
+    if (mCanceled && NS_FAILED(mStatus)) {
+        LOG(("channel was canceled [this=%p status=%x]\n", this, mStatus));
+        return mStatus;
     }
 
-    return rv;
+    // advance to the next state...
+    return aEntryStatus;
 }
 
 // Generates the proper cache-key for this instance of nsHttpChannel
 nsresult
 nsHttpChannel::GenerateCacheKey(PRUint32 postID, nsACString &cacheKey)
 {
     AssembleCacheKey(mFallbackChannel ? mFallbackKey.get() : mSpec.get(),
                      postID, cacheKey);
@@ -4810,39 +4822,78 @@ nsHttpChannel::OnCacheEntryAvailable(nsI
     LOG(("nsHttpChannel::OnCacheEntryAvailable [this=%p entry=%p "
          "access=%x status=%x]\n", this, entry, access, status));
 
     // if the channel's already fired onStopRequest, then we should ignore
     // this event.
     if (!mIsPending)
         return NS_OK;
 
+    rv = OnCacheEntryAvailableInternal(entry, access, status);
+    if (NS_FAILED(rv)) {
+        CloseCacheEntry(true);
+        AsyncAbort(rv);
+    }
+
+    return NS_OK;
+}
+
+nsresult
+nsHttpChannel::OnCacheEntryAvailableInternal(nsICacheEntryDescriptor *entry,
+                                             nsCacheAccessMode access,
+                                             nsresult status)
+{
+    nsresult rv;
+
     nsOnCacheEntryAvailableCallback callback = mOnCacheEntryAvailableCallback;
     mOnCacheEntryAvailableCallback = nsnull;
 
     NS_ASSERTION(callback,
         "nsHttpChannel::OnCacheEntryAvailable called without callback");
-    rv = ((*this).*callback)(entry, access, status, false);
-
-    if (NS_FAILED(rv)) {
-        LOG(("AsyncOpenCacheEntry failed [rv=%x]\n", rv));
-        if (mLoadFlags & LOAD_ONLY_FROM_CACHE) {
-            // If we have a fallback URI (and we're not already
-            // falling back), process the fallback asynchronously.
-            if (!mFallbackChannel && !mFallbackKey.IsEmpty()) {
-                rv = AsyncCall(&nsHttpChannel::HandleAsyncFallback);
-                if (NS_SUCCEEDED(rv))
-                    return rv;
+    rv = ((*this).*callback)(entry, access, status);
+
+    if (mOnCacheEntryAvailableCallback) {
+        // callback fired another async open
+        NS_ASSERTION(NS_SUCCEEDED(rv), "Unexpected state");
+        return NS_OK;
+    }
+
+    if (callback != &nsHttpChannel::OnOfflineCacheEntryForWritingAvailable) {
+        if (NS_FAILED(rv)) {
+            LOG(("AsyncOpenCacheEntry failed [rv=%x]\n", rv));
+            if (mLoadFlags & LOAD_ONLY_FROM_CACHE) {
+                // If we have a fallback URI (and we're not already
+                // falling back), process the fallback asynchronously.
+                if (!mFallbackChannel && !mFallbackKey.IsEmpty()) {
+                    return AsyncCall(&nsHttpChannel::HandleAsyncFallback);
+                }
+                return NS_ERROR_DOCUMENT_NOT_CACHED;
             }
+            // proceed without using the cache
         }
-        CloseCacheEntry(true);
-        AsyncAbort(rv);
+
+        // if cacheForOfflineUse has been set, open up an offline cache entry
+        // to update
+        if (mCacheForOfflineUse) {
+            rv = OpenOfflineCacheEntryForWriting();
+            if (mOnCacheEntryAvailableCallback) {
+                NS_ASSERTION(NS_SUCCEEDED(rv), "Unexpected state");
+                return NS_OK;
+            }
+
+            if (NS_FAILED(rv))
+                return rv;
+        }
+    } else {
+        // check result of OnOfflineCacheEntryForWritingAvailable()
+        if (NS_FAILED(rv))
+            return rv;
     }
 
-    return NS_OK;
+    return Connect(false);
 }
 
 nsresult
 nsHttpChannel::DoAuthRetry(nsAHttpConnection *conn)
 {
     LOG(("nsHttpChannel::DoAuthRetry [this=%p]\n", this));
 
     NS_ASSERTION(!mTransaction, "should not have a transaction");
--- a/netwerk/protocol/http/nsHttpChannel.h
+++ b/netwerk/protocol/http/nsHttpChannel.h
@@ -205,24 +205,29 @@ private:
     void HandleAsyncReplaceWithProxy();
     nsresult ContinueHandleAsyncReplaceWithProxy(nsresult);
     nsresult ResolveProxy();
 
     // cache specific methods
     nsresult OpenCacheEntry();
     nsresult OnOfflineCacheEntryAvailable(nsICacheEntryDescriptor *aEntry,
                                           nsCacheAccessMode aAccess,
-                                          nsresult aResult,
-                                          bool aSync);
-    nsresult OpenNormalCacheEntry(bool aSync);
+                                          nsresult aResult);
+    nsresult OpenNormalCacheEntry();
     nsresult OnNormalCacheEntryAvailable(nsICacheEntryDescriptor *aEntry,
                                          nsCacheAccessMode aAccess,
-                                         nsresult aResult,
-                                         bool aSync);
+                                         nsresult aResult);
     nsresult OpenOfflineCacheEntryForWriting();
+    nsresult OnOfflineCacheEntryForWritingAvailable(
+        nsICacheEntryDescriptor *aEntry,
+        nsCacheAccessMode aAccess,
+        nsresult aResult);
+    nsresult OnCacheEntryAvailableInternal(nsICacheEntryDescriptor *entry,
+                                           nsCacheAccessMode access,
+                                           nsresult status);
     nsresult GenerateCacheKey(PRUint32 postID, nsACString &key);
     nsresult UpdateExpirationTime();
     nsresult CheckCache();
     nsresult ShouldUpdateOfflineCacheEntry(bool *shouldCacheForOfflineUse);
     nsresult ReadFromCache();
     void     CloseCacheEntry(bool doomOnFailure);
     void     CloseOfflineCacheEntry();
     nsresult InitCacheEntry();
@@ -294,19 +299,18 @@ private:
     nsCOMPtr<nsICacheEntryDescriptor> mCacheEntry;
     nsRefPtr<nsInputStreamPump>       mCachePump;
     nsAutoPtr<nsHttpResponseHead>     mCachedResponseHead;
     nsCacheAccessMode                 mCacheAccess;
     PRUint32                          mPostID;
     PRUint32                          mRequestTime;
 
     typedef nsresult (nsHttpChannel:: *nsOnCacheEntryAvailableCallback)(
-        nsICacheEntryDescriptor *, nsCacheAccessMode, nsresult, bool);
+        nsICacheEntryDescriptor *, nsCacheAccessMode, nsresult);
     nsOnCacheEntryAvailableCallback   mOnCacheEntryAvailableCallback;
-    bool                              mAsyncCacheOpen;
 
     nsCOMPtr<nsICacheEntryDescriptor> mOfflineCacheEntry;
     nsCacheAccessMode                 mOfflineCacheAccess;
     nsCString                         mOfflineCacheClientID;
 
     // auth specific data
     nsCOMPtr<nsIHttpChannelAuthProvider> mAuthProvider;