Back out bug 722034 and bug 746018 due to performance regression, a=josh, a=taras
authorBrian Smith <bsmith@mozilla.com>
Tue, 03 Jul 2012 15:31:21 -0700
changeset 100634 c576c2b8d9badfccda180d8aa8e020714b037fca
parent 99882 73783bf75c4cb7a186f4b59e0b0f918a3d7839ff
child 100635 deec51854e4ef152d162aaaf3382228eaf9f52a7
push id173
push userlsblakk@mozilla.com
push dateFri, 24 Aug 2012 15:39:16 +0000
treeherdermozilla-release@bcc45eb1fb41 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjosh, taras
bugs722034, 746018
milestone15.0a1
Back out bug 722034 and bug 746018 due to performance regression, a=josh, a=taras
netwerk/base/src/AutoClose.h
netwerk/cache/nsCacheEntryDescriptor.cpp
netwerk/cache/nsCacheService.cpp
netwerk/cache/nsDiskCacheBlockFile.cpp
netwerk/cache/nsDiskCacheMap.cpp
netwerk/cache/nsDiskCacheStreams.cpp
netwerk/protocol/http/HttpBaseChannel.cpp
netwerk/protocol/http/HttpBaseChannel.h
netwerk/protocol/http/nsHttpChannel.cpp
netwerk/protocol/http/nsHttpChannel.h
netwerk/protocol/http/nsHttpHandler.cpp
netwerk/protocol/http/nsHttpHandler.h
netwerk/protocol/http/nsHttpResponseHead.cpp
deleted file mode 100644
--- a/netwerk/base/src/AutoClose.h
+++ /dev/null
@@ -1,76 +0,0 @@
-/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
-/* vim: set ts=2 et sw=2 tw=80: */
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this file,
- * You can obtain one at http://mozilla.org/MPL/2.0/. */
-
-#ifndef mozilla_net_AutoClose_h
-#define mozilla_net_AutoClose_h
-
-#include "nsCOMPtr.h"
-
-namespace mozilla { namespace net {
-
-// Like an nsAutoPtr for XPCOM streams (e.g. nsIAsyncInputStream) and other
-// refcounted classes that need to have the Close() method called explicitly
-// before they are destroyed.
-template <typename T>
-class AutoClose
-{
-public:
-  AutoClose() { } 
-  ~AutoClose(){
-    Close();
-  }
-
-  operator bool() const
-  {
-    return mPtr;
-  }
-
-  already_AddRefed<T> forget()
-  {
-    return mPtr.forget();
-  }
-
-  void takeOver(AutoClose<T> & rhs)
-  {
-    Close();
-    mPtr = rhs.mPtr.forget();
-  }
-
-  // assign from |do_QueryInterface(expr, &rv)|
-  void operator=(const nsQueryInterfaceWithError rhs)
-  {
-    Close();
-    mPtr = rhs;
-  }
-
-  void CloseAndRelease()
-  {
-    Close();
-    mPtr = nsnull;
-  }
-
-  T* operator->() const
-  {
-    return mPtr.operator->();
-  }
-
-private:
-  void Close()
-  {
-    if (mPtr) {
-      mPtr->Close();
-    }
-  }
-
-  void operator=(const AutoClose<T> &) MOZ_DELETE;
-  AutoClose(const AutoClose<T> &) MOZ_DELETE;
-
-  nsCOMPtr<T> mPtr;
-};
-
-} } // namespace mozilla::net
-
-#endif // mozilla_net_AutoClose_h
--- a/netwerk/cache/nsCacheEntryDescriptor.cpp
+++ b/netwerk/cache/nsCacheEntryDescriptor.cpp
@@ -535,21 +535,16 @@ nsInputStreamWrapper::LazyInit()
     NS_ENSURE_TRUE(mode & nsICache::ACCESS_READ, NS_ERROR_UNEXPECTED);
 
     nsCacheEntry* cacheEntry = mDescriptor->CacheEntry();
     if (!cacheEntry) return NS_ERROR_NOT_AVAILABLE;
 
     rv = nsCacheService::OpenInputStreamForEntry(cacheEntry, mode,
                                                  mStartOffset,
                                                  getter_AddRefs(mInput));
-
-    CACHE_LOG_DEBUG(("nsInputStreamWrapper::LazyInit "
-                      "[entry=%p, wrapper=%p, mInput=%p, rv=%d]",
-                      mDescriptor, this, mInput.get(), PRIntn(rv)));
-
     if (NS_FAILED(rv)) return rv;
 
     mInitialized = true;
     return NS_OK;
 }
 
 nsresult nsCacheEntryDescriptor::
 nsInputStreamWrapper::Close()
@@ -568,24 +563,19 @@ nsInputStreamWrapper::Available(PRUint32
 
     return mInput->Available(avail);
 }
 
 nsresult nsCacheEntryDescriptor::
 nsInputStreamWrapper::Read(char *buf, PRUint32 count, PRUint32 *countRead)
 {
     nsresult rv = EnsureInit();
-    if (NS_SUCCEEDED(rv))
-        rv = mInput->Read(buf, count, countRead);
+    if (NS_FAILED(rv)) return rv;
 
-    CACHE_LOG_DEBUG(("nsInputStreamWrapper::Read "
-                      "[entry=%p, wrapper=%p, mInput=%p, rv=%d]",
-                      mDescriptor, this, mInput.get(), rv));
-
-    return rv;
+    return mInput->Read(buf, count, countRead);
 }
 
 nsresult nsCacheEntryDescriptor::
 nsInputStreamWrapper::ReadSegments(nsWriteSegmentFun writer, void *closure,
                                    PRUint32 count, PRUint32 *countRead)
 {
     // cache stream not buffered
     return NS_ERROR_NOT_IMPLEMENTED;
--- a/netwerk/cache/nsCacheService.cpp
+++ b/netwerk/cache/nsCacheService.cpp
@@ -1754,17 +1754,17 @@ nsCacheService::ProcessRequest(nsCacheRe
         (void) ProcessPendingRequests(doomedEntry);
         if (doomedEntry->IsNotInUse())
             DeactivateEntry(doomedEntry);
         doomedEntry = nsnull;
     }
 
     if (request->mListener) {  // Asynchronous
     
-        if (NS_FAILED(rv) && calledFromOpenCacheEntry && request->IsBlocking())
+        if (NS_FAILED(rv) && calledFromOpenCacheEntry)
             return rv;  // skip notifying listener, just return rv to caller
             
         // call listener to report error or descriptor
         nsresult rv2 = NotifyListener(request, descriptor, accessGranted, rv);
         if (NS_FAILED(rv2) && NS_SUCCEEDED(rv)) {
             rv = rv2;  // trigger delete request
         }
     } else {        // Synchronous
--- a/netwerk/cache/nsDiskCacheBlockFile.cpp
+++ b/netwerk/cache/nsDiskCacheBlockFile.cpp
@@ -1,15 +1,14 @@
 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
  *
  * This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
-#include "nsCache.h"
 #include "nsDiskCache.h"
 #include "nsDiskCacheBlockFile.h"
 #include "mozilla/FileUtils.h"
 
 using namespace mozilla;
 
 /******************************************************************************
  * nsDiskCacheBlockFile - 
@@ -27,25 +26,24 @@ nsDiskCacheBlockFile::Open(nsILocalFile 
         return NS_ERROR_INVALID_ARG;
 
     mBlockSize = blockSize;
     mBitMapWords = bitMapSize / 32;
     PRUint32 bitMapBytes = mBitMapWords * 4;
     
     // open the file - restricted to user, the data could be confidential
     nsresult rv = blockFile->OpenNSPRFileDesc(PR_RDWR | PR_CREATE_FILE, 00600, &mFD);
-    if (NS_FAILED(rv)) {
-        CACHE_LOG_DEBUG(("CACHE: nsDiskCacheBlockFile::Open "
-                         "[this=%p] unable to open or create file: %d",
-                         this, rv));
-        return rv;  // unable to open or create file
-    }
+    if (NS_FAILED(rv))  return rv;  // unable to open or create file
     
     // allocate bit map buffer
     mBitMap = new PRUint32[mBitMapWords];
+    if (!mBitMap) {
+        rv = NS_ERROR_OUT_OF_MEMORY;
+        goto error_exit;
+    }
     
     // check if we just creating the file
     mFileSize = PR_Available(mFD);
     if (mFileSize < 0) {
         // XXX an error occurred. We could call PR_GetError(), but how would that help?
         rv = NS_ERROR_UNEXPECTED;
         goto error_exit;
     }
@@ -76,23 +74,19 @@ nsDiskCacheBlockFile::Open(nsILocalFile 
         // little bit smaller than used blocks times blocksize,
         // because the last block will generally not be 'whole'.
         const PRUint32  estimatedSize = CalcBlockFileSize();
         if ((PRUint32)mFileSize + blockSize < estimatedSize) {
             rv = NS_ERROR_UNEXPECTED;
             goto error_exit;
         }
     }
-    CACHE_LOG_DEBUG(("CACHE: nsDiskCacheBlockFile::Open [this=%p] succeeded",
-                      this));
     return NS_OK;
 
 error_exit:
-    CACHE_LOG_DEBUG(("CACHE: nsDiskCacheBlockFile::Open [this=%p] failed with "
-                     "error %d", this, rv));
     Close(false);
     return rv;
 }
 
 
 /******************************************************************************
  *  Close
  *****************************************************************************/
@@ -235,19 +229,16 @@ nsDiskCacheBlockFile::ReadBlocks( void *
 
     // read the blocks
     PRInt32 bytesToRead = *bytesRead;
     if ((bytesToRead <= 0) || ((PRUint32)bytesToRead > mBlockSize * numBlocks)) {
         bytesToRead = mBlockSize * numBlocks;
     }
     *bytesRead = PR_Read(mFD, buffer, bytesToRead);
     
-    CACHE_LOG_DEBUG(("CACHE: nsDiskCacheBlockFile::Read [this=%p] "
-                     "returned %d / %d bytes", this, *bytesRead, bytesToRead));
-
     return NS_OK;
 }
 
 
 /******************************************************************************
  *  FlushBitMap
  *****************************************************************************/
 nsresult
--- a/netwerk/cache/nsDiskCacheMap.cpp
+++ b/netwerk/cache/nsDiskCacheMap.cpp
@@ -72,18 +72,16 @@ nsDiskCacheMap::Open(nsILocalFile *  cac
             goto error_exit;
         }
     } else if (mapSize >= sizeof(nsDiskCacheHeader)) {  // read existing _CACHE_MAP_
         
         // if _CACHE_MAP_ exists, so should the block files
         if (!cacheFilesExist)
             goto error_exit;
 
-        CACHE_LOG_DEBUG(("CACHE: nsDiskCacheMap::Open [this=%p] reading map", this));
-
         // read the header
         PRUint32 bytesRead = PR_Read(mMapFD, &mHeader, sizeof(nsDiskCacheHeader));
         if (sizeof(nsDiskCacheHeader) != bytesRead)  goto error_exit;
         mHeader.Unswap();
 
         if (mHeader.mIsDirty || (mHeader.mVersion != nsDiskCache::kCurrentVersion))
             goto error_exit;
 
@@ -683,21 +681,17 @@ nsDiskCacheMap::ReadDiskCacheEntry(nsDis
         // open and read the file
         nsCOMPtr<nsILocalFile> file;
         rv = GetLocalFileForDiskCacheRecord(record,
                                             nsDiskCache::kMetaData,
                                             false,
                                             getter_AddRefs(file));
         NS_ENSURE_SUCCESS(rv, nsnull);
 
-        CACHE_LOG_DEBUG(("CACHE: nsDiskCacheMap::ReadDiskCacheEntry"
-                         "[this=%p] reading disk cache entry", this));
-
         PRFileDesc * fd = nsnull;
-
         // open the file - restricted to user, the data could be confidential
         rv = file->OpenNSPRFileDesc(PR_RDONLY, 00600, &fd);
         NS_ENSURE_SUCCESS(rv, nsnull);
         
         PRInt32 fileSize = PR_Available(fd);
         if (fileSize < 0) {
             // an error occurred. We could call PR_GetError(), but how would that help?
             rv = NS_ERROR_UNEXPECTED;
--- a/netwerk/cache/nsDiskCacheStreams.cpp
+++ b/netwerk/cache/nsDiskCacheStreams.cpp
@@ -103,66 +103,42 @@ nsDiskCacheInputStream::Available(PRUint
 }
 
 
 NS_IMETHODIMP
 nsDiskCacheInputStream::Read(char * buffer, PRUint32 count, PRUint32 * bytesRead)
 {
     *bytesRead = 0;
 
-    if (mClosed) {
-        CACHE_LOG_DEBUG(("CACHE: nsDiskCacheInputStream::Read "
-                         "[stream=%p] stream was closed",
-                         this, buffer, count));
+    if (mClosed)
         return NS_OK;
-    }
     
-    if (mPos == mStreamEnd) {
-        CACHE_LOG_DEBUG(("CACHE: nsDiskCacheInputStream::Read "
-                         "[stream=%p] stream at end of file",
-                         this, buffer, count));
-        return NS_OK;
-    }
-    if (mPos > mStreamEnd) {
-        CACHE_LOG_DEBUG(("CACHE: nsDiskCacheInputStream::Read "
-                         "[stream=%p] stream past end of file (!)",
-                         this, buffer, count));
-        return NS_ERROR_UNEXPECTED;
-    }
+    if (mPos == mStreamEnd)  return NS_OK;
+    if (mPos > mStreamEnd)   return NS_ERROR_UNEXPECTED;
     
     if (count > mStreamEnd - mPos)
         count = mStreamEnd - mPos;
 
     if (mFD) {
         // just read from file
         PRInt32  result = PR_Read(mFD, buffer, count);
-        if (result < 0) {
-            PRErrorCode error = PR_GetError();
-            nsresult rv = NS_ErrorAccordingToNSPR();
-            CACHE_LOG_DEBUG(("CACHE: nsDiskCacheInputStream::Read PR_Read failed"
-                             "[stream=%p, rv=%d, NSPR error %s",
-                             this, PRIntn(rv), PR_ErrorToName(error)));
-            return rv;
-        }
+        if (result < 0)  return  NS_ErrorAccordingToNSPR();
         
         mPos += (PRUint32)result;
         *bytesRead = (PRUint32)result;
         
     } else if (mBuffer) {
         // read data from mBuffer
         memcpy(buffer, mBuffer + mPos, count);
         mPos += count;
         *bytesRead = count;
     } else {
         // no data source for input stream
     }
 
-    CACHE_LOG_DEBUG(("CACHE: nsDiskCacheInputStream::Read "
-                     "[stream=%p, count=%ud, byteRead=%ud] ",
-                     this, PRUintn(count), PRUintn(*bytesRead)));
     return NS_OK;
 }
 
 
 NS_IMETHODIMP
 nsDiskCacheInputStream::ReadSegments(nsWriteSegmentFun writer,
                                      void *            closure,
                                      PRUint32          count,
@@ -698,18 +674,16 @@ nsDiskCacheStreamIO::UpdateFileSize()
 }
 
 
 nsresult
 nsDiskCacheStreamIO::OpenCacheFile(PRIntn flags, PRFileDesc ** fd)
 {
     NS_ENSURE_ARG_POINTER(fd);
     
-    CACHE_LOG_DEBUG(("nsDiskCacheStreamIO::OpenCacheFile"));
-
     nsresult         rv;
     nsDiskCacheMap * cacheMap = mDevice->CacheMap();
     
     rv = cacheMap->GetLocalFileForDiskCacheRecord(&mBinding->mRecord,
                                                   nsDiskCache::kData,
                                                   !!(flags & PR_CREATE_FILE),
                                                   getter_AddRefs(mLocalFile));
     if (NS_FAILED(rv))  return rv;
--- a/netwerk/protocol/http/HttpBaseChannel.cpp
+++ b/netwerk/protocol/http/HttpBaseChannel.cpp
@@ -45,16 +45,17 @@ HttpBaseChannel::HttpBaseChannel()
   , mInheritApplicationCache(true)
   , mChooseApplicationCache(false)
   , mLoadedFromApplicationCache(false)
   , mChannelIsForDownload(false)
   , mTracingEnabled(true)
   , mTimingEnabled(false)
   , mAllowSpdy(true)
   , mSuspendCount(0)
+  , mRedirectedCachekeys(nsnull)
 {
   LOG(("Creating HttpBaseChannel @%x\n", this));
 
   // grab a reference to the handler to ensure that it doesn't go away.
   NS_ADDREF(gHttpHandler);
 
   // Subfields of unions cannot be targeted in an initializer list
   mSelfAddr.raw.family = PR_AF_UNSPEC;
@@ -1620,17 +1621,18 @@ HttpBaseChannel::SetupReplacementChannel
     else
       httpInternal->SetDocumentURI(mDocumentURI);
 
     // if there is a chain of keys for redirect-responses we transfer it to
     // the new channel (see bug #561276)
     if (mRedirectedCachekeys) {
         LOG(("HttpBaseChannel::SetupReplacementChannel "
              "[this=%p] transferring chain of redirect cache-keys", this));
-        httpInternal->SetCacheKeysRedirectChain(mRedirectedCachekeys.forget());
+        httpInternal->SetCacheKeysRedirectChain(mRedirectedCachekeys);
+        mRedirectedCachekeys = nsnull;
     }
   }
   
   // transfer application cache information
   nsCOMPtr<nsIApplicationCacheChannel> appCacheChannel =
     do_QueryInterface(newChannel);
   if (appCacheChannel) {
     appCacheChannel->SetApplicationCache(mApplicationCache);
--- a/netwerk/protocol/http/HttpBaseChannel.h
+++ b/netwerk/protocol/http/HttpBaseChannel.h
@@ -135,17 +135,20 @@ public:
   NS_IMETHOD GetLocalPort(PRInt32* port);
   NS_IMETHOD GetRemoteAddress(nsACString& addr);
   NS_IMETHOD GetRemotePort(PRInt32* port);
   NS_IMETHOD GetAllowSpdy(bool *aAllowSpdy);
   NS_IMETHOD SetAllowSpdy(bool aAllowSpdy);
   
   inline void CleanRedirectCacheChainIfNecessary()
   {
-      mRedirectedCachekeys = nsnull;
+      if (mRedirectedCachekeys) {
+          delete mRedirectedCachekeys;
+          mRedirectedCachekeys = nsnull;
+      }
   }
   NS_IMETHOD HTTPUpgrade(const nsACString & aProtocolName,
                          nsIHttpUpgradeListener *aListener); 
 
   // nsISupportsPriority
   NS_IMETHOD GetPriority(PRInt32 *value);
   NS_IMETHOD AdjustPriority(PRInt32 delta);
 
@@ -264,17 +267,17 @@ protected:
   PRUint32                          mTracingEnabled             : 1;
   // True if timing collection is enabled
   PRUint32                          mTimingEnabled              : 1;
   PRUint32                          mAllowSpdy                  : 1;
 
   // Current suspension depth for this channel object
   PRUint32                          mSuspendCount;
 
-  nsAutoPtr<nsTArray<nsCString> >   mRedirectedCachekeys;
+  nsTArray<nsCString>              *mRedirectedCachekeys;
 };
 
 // Share some code while working around C++'s absurd inability to handle casting
 // of member functions between base/derived types.
 // - We want to store member function pointer to call at resume time, but one
 //   such function--HandleAsyncAbort--we want to share between the
 //   nsHttpChannel/HttpChannelChild.  Can't define it in base class, because
 //   then we'd have to cast member function ptr between base/derived class
--- a/netwerk/protocol/http/nsHttpChannel.cpp
+++ b/netwerk/protocol/http/nsHttpChannel.cpp
@@ -5,147 +5,53 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "nsHttpChannel.h"
 #include "nsHttpHandler.h"
 #include "nsStandardURL.h"
 #include "nsIApplicationCacheService.h"
 #include "nsIApplicationCacheContainer.h"
 #include "nsIAuthInformation.h"
-#include "nsICryptoHash.h"
 #include "nsIStringBundle.h"
 #include "nsIIDNService.h"
 #include "nsIStreamListenerTee.h"
 #include "nsISeekableStream.h"
 #include "nsMimeTypes.h"
 #include "nsNetUtil.h"
 #include "prprf.h"
 #include "prnetdb.h"
 #include "nsEscape.h"
 #include "nsStreamUtils.h"
 #include "nsIOService.h"
 #include "nsICacheService.h"
 #include "nsDNSPrefetch.h"
 #include "nsChannelClassifier.h"
 #include "nsIRedirectResultListener.h"
 #include "mozilla/TimeStamp.h"
+#include "mozilla/Telemetry.h"
 #include "nsDOMError.h"
 #include "nsAlgorithm.h"
 #include "sampler.h"
 #include "nsIConsoleService.h"
 #include "base/compiler_specific.h"
 #include "NullHttpTransaction.h"
 
-namespace mozilla { namespace net {
- 
-namespace {
+using namespace mozilla;
 
 // Device IDs for various cache types
 const char kDiskDeviceID[] = "disk";
 const char kMemoryDeviceID[] = "memory";
 const char kOfflineDeviceID[] = "offline";
 
 // True if the local cache should be bypassed when processing a request.
 #define BYPASS_LOCAL_CACHE(loadFlags) \
         (loadFlags & (nsIRequest::LOAD_BYPASS_CACHE | \
                       nsICachingChannel::LOAD_BYPASS_LOCAL_CACHE))
 
 static NS_DEFINE_CID(kStreamListenerTeeCID, NS_STREAMLISTENERTEE_CID);
-static NS_DEFINE_CID(kStreamTransportServiceCID,
-                     NS_STREAMTRANSPORTSERVICE_CID);
-
-const mozilla::Telemetry::ID UNKNOWN_DEVICE
-    = static_cast<mozilla::Telemetry::ID>(0);
-void
-AccumulateCacheHitTelemetry(mozilla::Telemetry::ID deviceHistogram,
-                            PRUint32 hitOrMiss)
-{
-    mozilla::Telemetry::Accumulate(
-            mozilla::Telemetry::HTTP_CACHE_DISPOSITION, hitOrMiss);
-    if (deviceHistogram != UNKNOWN_DEVICE) {
-        mozilla::Telemetry::Accumulate(deviceHistogram, hitOrMiss);
-    }
-}
-
-const char *
-GetCacheSessionNameForStoragePolicy(nsCacheStoragePolicy storagePolicy,
-                                    bool isPrivate)
-{
-    MOZ_ASSERT(!isPrivate || storagePolicy == nsICache::STORE_IN_MEMORY);
-
-    switch (storagePolicy) {
-    case nsICache::STORE_IN_MEMORY:
-        return isPrivate ? "HTTP-memory-only-PB" : "HTTP-memory-only";
-    case nsICache::STORE_OFFLINE:
-        return "HTTP-offline";
-    default:
-        return "HTTP";
-    }
-}
-
-// Computes and returns a SHA1 hash of the input buffer. The input buffer
-// must be a null-terminated string.
-nsresult
-Hash(const char *buf, nsACString &hash)
-{
-    nsresult rv;
-      
-    nsCOMPtr<nsICryptoHash> hasher
-      = do_CreateInstance(NS_CRYPTO_HASH_CONTRACTID, &rv);
-    NS_ENSURE_SUCCESS(rv, rv);
-
-    rv = hasher->Init(nsICryptoHash::SHA1);
-    NS_ENSURE_SUCCESS(rv, rv);
-
-    rv = hasher->Update(reinterpret_cast<unsigned const char*>(buf),
-                         strlen(buf));
-    NS_ENSURE_SUCCESS(rv, rv);
-
-    rv = hasher->Finish(true, hash);
-    NS_ENSURE_SUCCESS(rv, rv);
-
-    return NS_OK;
-}
-
-bool IsRedirectStatus(PRUint32 status)
-{
-    // 305 disabled as a security measure (see bug 187996).
-    return status == 300 || status == 301 || status == 302 || status == 303 ||
-           status == 307 || status == 308;
-}
-
-// We only treat 3xx responses as redirects if they have a Location header and
-// the status code is in a whitelist.
-bool
-WillRedirect(const nsHttpResponseHead * response)
-{
-    return IsRedirectStatus(response->Status()) &&
-           response->PeekHeader(nsHttp::Location);
-}
-
-void
-MaybeMarkCacheEntryValid(const void * channel,
-                         nsICacheEntryDescriptor * cacheEntry,
-                         nsCacheAccessMode cacheAccess)
-{
-    // Mark the cache entry as valid in order to allow others access to it.
-    // XXX: Is it really necessary to check for write acccess to the entry?
-    if (cacheAccess & nsICache::ACCESS_WRITE) {
-        nsresult rv = cacheEntry->MarkValid();
-        LOG(("Marking cache entry valid "
-             "[channel=%p, entry=%p, access=%d, result=%d]",
-             channel, cacheEntry, PRIntn(cacheAccess), PRIntn(rv)));
-    } else {
-        LOG(("Not marking read-only cache entry valid "
-             "[channel=%p, entry=%p, access=%d]", 
-             channel, cacheEntry, PRIntn(cacheAccess)));
-    }
-}
-
-} // unnamed namespace
 
 class AutoRedirectVetoNotifier
 {
 public:
     AutoRedirectVetoNotifier(nsHttpChannel* channel) : mChannel(channel) {}
     ~AutoRedirectVetoNotifier() {ReportRedirectResult(false);}
     void RedirectSucceeded() {ReportRedirectResult(true);}
 
@@ -166,131 +72,24 @@ AutoRedirectVetoNotifier::ReportRedirect
     NS_QueryNotificationCallbacks(mChannel, 
                                   NS_GET_IID(nsIRedirectResultListener), 
                                   getter_AddRefs(vetoHook));
     mChannel = nsnull;
     if (vetoHook)
         vetoHook->OnRedirectResult(succeeded);
 }
 
-class HttpCacheQuery : public nsRunnable, public nsICacheListener
-{
-public:
-    HttpCacheQuery(nsHttpChannel * channel,
-                   const nsACString & clientID,
-                   nsCacheStoragePolicy storagePolicy,
-                   bool usingPrivateBrowsing,
-                   const nsACString & cacheKey,
-                   nsCacheAccessMode accessToRequest,
-                   bool noWait,
-                   bool usingSSL,
-                   bool loadedFromApplicationCache)
-        // in
-        : mChannel(channel)
-        , mHasQueryString(HasQueryString(channel->mRequestHead.Method(),
-                                         channel->mURI))
-        , mLoadFlags(channel->mLoadFlags)
-        , mCacheForOfflineUse(channel->mCacheForOfflineUse)
-        , mFallbackChannel(channel->mFallbackChannel)
-        , mClientID(clientID)
-        , mStoragePolicy(storagePolicy)
-        , mUsingPrivateBrowsing(usingPrivateBrowsing)
-        , mCacheKey(cacheKey)
-        , mAccessToRequest(accessToRequest)
-        , mNoWait(noWait)
-        , mUsingSSL(usingSSL)
-        , mLoadedFromApplicationCache(loadedFromApplicationCache)
-        // internal
-        , mCacheAccess(0)
-        , mStatus(NS_ERROR_NOT_INITIALIZED)
-        , mRunCount(0)
-        // in/out
-        , mRequestHead(channel->mRequestHead)
-        , mRedirectedCachekeys(channel->mRedirectedCachekeys.forget())
-        // out
-        , mCachedContentIsValid(false)
-        , mCachedContentIsPartial(false)
-        , mCustomConditionalRequest(false)
-        , mDidReval(false)
-        , mCacheEntryDeviceTelemetryID(UNKNOWN_DEVICE)
-    {
-        MOZ_ASSERT(NS_IsMainThread());
-    }
-
-    nsresult Dispatch();
-
-private:
-    NS_DECL_ISUPPORTS_INHERITED
-    NS_DECL_NSIRUNNABLE
-    NS_DECL_NSICACHELISTENER
-
-    MOZ_ALWAYS_INLINE void AssertOnCacheThread() const
-    {
-        MOZ_ASSERT(mCacheThread);
-#ifdef DEBUG
-        bool onCacheThread;
-        nsresult rv = mCacheThread->IsOnCurrentThread(&onCacheThread);
-        MOZ_ASSERT(NS_SUCCEEDED(rv));
-        MOZ_ASSERT(onCacheThread);
-#endif
-    }
-
-    static bool HasQueryString(nsHttpAtom method, nsIURI * uri);
-    nsresult CheckCache();
-    bool ResponseWouldVary() const;
-    bool MustValidateBasedOnQueryUrl() const;
-    nsresult SetupByteRangeRequest(PRUint32 partialLen);
-    nsresult StartBufferingCachedEntity();
-
-    nsCOMPtr<nsICacheListener> mChannel;
-    const bool mHasQueryString;
-    const PRUint32 mLoadFlags;
-    const bool mCacheForOfflineUse;
-    const bool mFallbackChannel;
-    const InfallableCopyCString mClientID;
-    const nsCacheStoragePolicy mStoragePolicy;
-    const bool mUsingPrivateBrowsing;
-    const InfallableCopyCString mCacheKey;
-    const nsCacheAccessMode mAccessToRequest;
-    const bool mNoWait;
-    const bool mUsingSSL;
-    const bool mLoadedFromApplicationCache;
-
-    // Used only internally 
-    nsCOMPtr<nsIEventTarget> mCacheThread;
-    nsCOMPtr<nsICacheEntryDescriptor> mCacheEntry;
-    nsCacheAccessMode mCacheAccess;
-    nsresult mStatus;
-    PRUint32 mRunCount;
-
-    // Copied from HttpcacheQuery into nsHttpChannel by nsHttpChannel
-    friend class nsHttpChannel;
-    /*in/out*/ nsHttpRequestHead mRequestHead;
-    /*in/out*/ nsAutoPtr<nsTArray<nsCString> > mRedirectedCachekeys;
-    /*out*/ AutoClose<nsIAsyncInputStream> mCacheAsyncInputStream;
-    /*out*/ nsAutoPtr<nsHttpResponseHead> mCachedResponseHead;
-    /*out*/ nsCOMPtr<nsISupports> mCachedSecurityInfo;
-    /*out*/ bool mCachedContentIsValid;
-    /*out*/ bool mCachedContentIsPartial;
-    /*out*/ bool mCustomConditionalRequest;
-    /*out*/ bool mDidReval;
-    /*out*/ mozilla::Telemetry::ID mCacheEntryDeviceTelemetryID;
-};
-
-NS_IMPL_ISUPPORTS_INHERITED1(HttpCacheQuery, nsRunnable, nsICacheListener)
-
 //-----------------------------------------------------------------------------
 // nsHttpChannel <public>
 //-----------------------------------------------------------------------------
 
 nsHttpChannel::nsHttpChannel()
     : ALLOW_THIS_IN_INITIALIZER_LIST(HttpAsyncAborter<nsHttpChannel>(this))
     , mLogicalOffset(0)
     , mCacheAccess(0)
-    , mCacheEntryDeviceTelemetryID(UNKNOWN_DEVICE)
     , mPostID(0)
     , mRequestTime(0)
     , mOnCacheEntryAvailableCallback(nsnull)
     , mCachedContentIsValid(false)
     , mCachedContentIsPartial(false)
     , mTransactionReplaced(false)
     , mAuthRetryPending(false)
     , mResuming(false)
@@ -336,17 +135,17 @@ nsHttpChannel::Init(nsIURI *uri,
 
     return rv;
 }
 //-----------------------------------------------------------------------------
 // nsHttpChannel <private>
 //-----------------------------------------------------------------------------
 
 nsresult
-nsHttpChannel::Connect()
+nsHttpChannel::Connect(bool firstTime)
 {
     nsresult rv;
 
     LOG(("nsHttpChannel::Connect [this=%p]\n", this));
 
     // Even if we're in private browsing mode, we still enforce existing STS
     // data (it is read-only).
     // if the connection is not using SSL and either the exact host matches or
@@ -385,91 +184,108 @@ nsHttpChannel::Connect()
             }
         }
     }
 
     // ensure that we are using a valid hostname
     if (!net_IsValidHostName(nsDependentCString(mConnectionInfo->Host())))
         return NS_ERROR_UNKNOWN_HOST;
 
-    // Consider opening a TCP connection right away
-    SpeculativeConnect();
-
-    // are we offline?
-    bool offline = gIOService->IsOffline();
-    if (offline)
-        mLoadFlags |= LOAD_ONLY_FROM_CACHE;
-    else if (PL_strcmp(mConnectionInfo->ProxyType(), "unknown") == 0)
-        return ResolveProxy();  // Lazily resolve proxy info
-
-    // Don't allow resuming when cache must be used
-    if (mResuming && (mLoadFlags & LOAD_ONLY_FROM_CACHE)) {
-        LOG(("Resuming from cache is not supported yet"));
-        return NS_ERROR_DOCUMENT_NOT_CACHED;
-    }
-
-    if (!gHttpHandler->UseCache())
-        return ContinueConnect();
-
-    // open a cache entry for this channel...
-    rv = OpenCacheEntry(usingSSL);
-
-    // 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()) {
-                return AsyncCall(&nsHttpChannel::HandleAsyncFallback);
-            }
+    // true when called from AsyncOpen
+    if (firstTime) {
+
+        // Consider opening a TCP connection right away
+        SpeculativeConnect();
+
+        // are we offline?
+        bool offline = gIOService->IsOffline();
+        if (offline)
+            mLoadFlags |= LOAD_ONLY_FROM_CACHE;
+        else if (PL_strcmp(mConnectionInfo->ProxyType(), "unknown") == 0)
+            return ResolveProxy();  // Lazily resolve proxy info
+
+        // Don't allow resuming when cache must be used
+        if (mResuming && (mLoadFlags & LOAD_ONLY_FROM_CACHE)) {
+            LOG(("Resuming from cache is not supported yet"));
             return NS_ERROR_DOCUMENT_NOT_CACHED;
         }
-        // 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)
+
+        // 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;
-    }
-
-    return ContinueConnect();
-}
-
-nsresult
-nsHttpChannel::ContinueConnect()
-{
+        }
+
+        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()) {
+                    return AsyncCall(&nsHttpChannel::HandleAsyncFallback);
+                }
+                return NS_ERROR_DOCUMENT_NOT_CACHED;
+            }
+            // 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;
+        }
+    }
+
     // 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();
+        if (NS_FAILED(rv))
+            NS_WARNING("cache check failed");
+
         // read straight from the cache if possible...
         if (mCachedContentIsValid) {
             nsRunnableMethod<nsHttpChannel> *event = nsnull;
             if (!mCachedContentIsPartial) {
                 AsyncCall(&nsHttpChannel::AsyncOnExamineCachedResponse, &event);
             }
-            nsresult rv = ReadFromCache(true);
+            rv = ReadFromCache();
             if (NS_FAILED(rv) && event) {
                 event->Revoke();
             }
-
-            AccumulateCacheHitTelemetry(mCacheEntryDeviceTelemetryID,
-                                        kCacheHit);
-
+            mozilla::Telemetry::Accumulate(
+                    mozilla::Telemetry::HTTP_CACHE_DISPOSITION, kCacheHit);
+
+            char* cacheDeviceID = nsnull;
+            mCacheEntry->GetDeviceID(&cacheDeviceID);
+            if (cacheDeviceID) {
+                if (!strcmp(cacheDeviceID, kDiskDeviceID))
+                    mozilla::Telemetry::Accumulate(
+                            mozilla::Telemetry::HTTP_DISK_CACHE_DISPOSITION,
+                            kCacheHit);
+                else if (!strcmp(cacheDeviceID, kMemoryDeviceID))
+                    mozilla::Telemetry::Accumulate(
+                            mozilla::Telemetry::HTTP_MEMORY_CACHE_DISPOSITION,
+                            kCacheHit);
+                else if (!strcmp(cacheDeviceID, kOfflineDeviceID))
+                    mozilla::Telemetry::Accumulate(
+                            mozilla::Telemetry::HTTP_OFFLINE_CACHE_DISPOSITION,
+                            kCacheHit);
+            }
             return rv;
         }
         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
             // is effectively not in the cache.
             return NS_ERROR_DOCUMENT_NOT_CACHED;
@@ -484,17 +300,17 @@ nsHttpChannel::ContinueConnect()
         return NS_ERROR_DOCUMENT_NOT_CACHED;
     }
 
     if (mLoadFlags & LOAD_NO_NETWORK_IO) {
         return NS_ERROR_DOCUMENT_NOT_CACHED;
     }
 
     // hit the net...
-    nsresult rv = SetupTransaction();
+    rv = SetupTransaction();
     if (NS_FAILED(rv)) return rv;
 
     rv = gHttpHandler->InitiateTransaction(mTransaction, mPriority);
     if (NS_FAILED(rv)) return rv;
 
     rv = mTransactionPump->AsyncRead(this, nsnull);
     if (NS_FAILED(rv)) return rv;
 
@@ -584,18 +400,18 @@ nsHttpChannel::ContinueHandleAsyncRedire
         DoNotifyListener();
     }
 
     // close the cache entry.  Blow it away if we couldn't process the redirect
     // for some reason (the cache entry might be corrupt).
     if (mCacheEntry) {
         if (NS_FAILED(rv))
             mCacheEntry->Doom();
-    }
-    CloseCacheEntry(false);
+        CloseCacheEntry(false);
+    }
 
     mIsPending = false;
 
     if (mLoadGroup)
         mLoadGroup->RemoveRequest(this, nsnull, mStatus);
 
     return NS_OK;
 }
@@ -962,26 +778,34 @@ nsHttpChannel::CallOnStartRequest()
     // if this channel is for a download, close off access to the cache.
     if (mCacheEntry && mChannelIsForDownload) {
         mCacheEntry->Doom();
         CloseCacheEntry(false);
     }
 
     if (!mCanceled) {
         // create offline cache entry if offline caching was requested
-        if (ShouldUpdateOfflineCacheEntry()) {
-            LOG(("writing to the offline cache"));
-            rv = InitOfflineCacheEntry();
+        if (mCacheForOfflineUse) {
+            bool shouldCacheForOfflineUse;
+            rv = ShouldUpdateOfflineCacheEntry(&shouldCacheForOfflineUse);
             if (NS_FAILED(rv)) return rv;
+            
+            if (shouldCacheForOfflineUse) {
+                LOG(("writing to the offline cache"));
+                rv = InitOfflineCacheEntry();
+                if (NS_FAILED(rv)) return rv;
                 
-            rv = InstallOfflineCacheListener();
-            if (NS_FAILED(rv)) return rv;
-        } else if (mCacheForOfflineUse) {
-            LOG(("offline cache is up to date, not updating"));
-            CloseOfflineCacheEntry();
+                if (mOfflineCacheEntry) {
+                  rv = InstallOfflineCacheListener();
+                  if (NS_FAILED(rv)) return rv;
+                }
+            } else {
+                LOG(("offline cache is up to date, not updating"));
+                CloseOfflineCacheEntry();
+            }
         }
     }
 
     return NS_OK;
 }
 
 nsresult
 nsHttpChannel::ProcessFailedSSLConnect(PRUint32 httpStatus)
@@ -1167,21 +991,16 @@ nsHttpChannel::ProcessResponse()
         // If SSL proxy response needs to complete, wait to process connection
         // for Strict-Transport-Security.
     } else {
         // Given a successful connection, process any STS data that's relevant.
         rv = ProcessSTSHeader();
         NS_ASSERTION(NS_SUCCEEDED(rv), "ProcessSTSHeader failed, continuing load.");
     }
 
-    MOZ_ASSERT(!mCachedContentIsValid);
-    if (httpStatus != 304 && httpStatus != 206) {
-        mCacheAsyncInputStream.CloseAndRelease();
-    }
-
     // notify "http-on-examine-response" observers
     gHttpHandler->OnExamineResponse(this);
 
     SetCookie(mResponseHead->PeekHeader(nsHttp::Set_Cookie));
 
     // handle unused username and password in url (see bug 232567)
     if (httpStatus != 401 && httpStatus != 407) {
         if (!mAuthRetryPending)
@@ -1217,20 +1036,18 @@ nsHttpChannel::ProcessResponse()
         }
         // these can normally be cached
         rv = ProcessNormal();
         MaybeInvalidateCacheEntryForSubsequentGet();
         break;
     case 206:
         if (mCachedContentIsPartial) // an internal byte range request...
             rv = ProcessPartialContent();
-        else {
-            mCacheAsyncInputStream.CloseAndRelease();
+        else
             rv = ProcessNormal();
-        }
         break;
     case 300:
     case 301:
     case 302:
     case 307:
     case 308:
     case 303:
 #if 0
@@ -1245,17 +1062,16 @@ nsHttpChannel::ProcessResponse()
             LOG(("AsyncProcessRedirection failed [rv=%x]\n", rv));
             rv = ContinueProcessResponse(rv);
         }
         break;
     case 304:
         rv = ProcessNotModified();
         if (NS_FAILED(rv)) {
             LOG(("ProcessNotModified failed [rv=%x]\n", rv));
-            mCacheAsyncInputStream.CloseAndRelease();
             rv = ProcessNormal();
         }
         else {
             successfulReval = true;
         }
         break;
     case 401:
     case 407:
@@ -1286,27 +1102,44 @@ nsHttpChannel::ProcessResponse()
             mAuthRetryPending = true; // see DoAuthRetry
         break;
     default:
         rv = ProcessNormal();
         MaybeInvalidateCacheEntryForSubsequentGet();
         break;
     }
 
-    PRUint32 cacheDisposition;
+    int cacheDisposition;
     if (!mDidReval)
         cacheDisposition = kCacheMissed;
     else if (successfulReval)
         cacheDisposition = kCacheHitViaReval;
     else
         cacheDisposition = kCacheMissedViaReval;
 
-    AccumulateCacheHitTelemetry(mCacheEntry ? mCacheEntryDeviceTelemetryID
-                                            : UNKNOWN_DEVICE,
-                                cacheDisposition);
+    mozilla::Telemetry::Accumulate(mozilla::Telemetry::HTTP_CACHE_DISPOSITION,
+            cacheDisposition);
+    if (mCacheEntry) {
+        char* cacheDeviceID = nsnull;
+        mCacheEntry->GetDeviceID(&cacheDeviceID);
+        if (cacheDeviceID) {
+            if (!strcmp(cacheDeviceID, kDiskDeviceID))
+                mozilla::Telemetry::Accumulate(
+                        mozilla::Telemetry::HTTP_DISK_CACHE_DISPOSITION,
+                        cacheDisposition);
+            else if (!strcmp(cacheDeviceID, kMemoryDeviceID))
+                mozilla::Telemetry::Accumulate(
+                        mozilla::Telemetry::HTTP_MEMORY_CACHE_DISPOSITION,
+                        cacheDisposition);
+            else if (!strcmp(cacheDeviceID, kOfflineDeviceID))
+                mozilla::Telemetry::Accumulate(
+                        mozilla::Telemetry::HTTP_OFFLINE_CACHE_DISPOSITION,
+                        cacheDisposition);
+        }
+    }
 
     return rv;
 }
 
 nsresult
 nsHttpChannel::ContinueProcessResponse(nsresult rv)
 {
     if (rv == NS_ERROR_CORRUPTED_CONTENT) {
@@ -1772,33 +1605,31 @@ nsHttpChannel::ResolveProxy()
     PRUint32 resolveFlags = 0;
     if (mConnectionInfo->ProxyInfo())
         mConnectionInfo->ProxyInfo()->GetResolveFlags(&resolveFlags);
 
     return pps->AsyncResolve(mURI, resolveFlags, this, getter_AddRefs(mProxyRequest));
 }
 
 bool
-HttpCacheQuery::ResponseWouldVary() const
+nsHttpChannel::ResponseWouldVary()
 {
-    AssertOnCacheThread();
-
     nsresult rv;
     nsCAutoString buf, metaKey;
     mCachedResponseHead->GetHeader(nsHttp::Vary, buf);
     if (!buf.IsEmpty()) {
         NS_NAMED_LITERAL_CSTRING(prefix, "request-");
 
         // enumerate the elements of the Vary header...
         char *val = buf.BeginWriting(); // going to munge buf
         char *token = nsCRT::strtok(val, NS_HTTP_HEADER_SEPS, &val);
         while (token) {
-            LOG(("HttpCacheQuery::ResponseWouldVary [channel=%p] " \
+            LOG(("nsHttpChannel::ResponseWouldVary [this=%x] " \
                  "processing %s\n",
-                 mChannel.get(), token));
+                 this, token));
             //
             // if "*", then assume response would vary.  technically speaking,
             // "Vary: header, *" is not permitted, but we allow it anyways.
             //
             // We hash values of cookie-headers for the following reasons:
             //
             //   1- cookies can be very large in size
             //
@@ -1812,19 +1643,18 @@ HttpCacheQuery::ResponseWouldVary() cons
 
             // build cache meta data key...
             metaKey = prefix + nsDependentCString(token);
 
             // check the last value of the given request header to see if it has
             // since changed.  if so, then indeed the cached response is invalid.
             nsXPIDLCString lastVal;
             mCacheEntry->GetMetaDataElement(metaKey.get(), getter_Copies(lastVal));
-            LOG(("HttpCacheQuery::ResponseWouldVary [channel=%p] "
-                     "stored value = \"%s\"\n",
-                 mChannel.get(), lastVal.get()));
+            LOG(("nsHttpChannel::ResponseWouldVary [this=%x] " \
+                    "stored value = %c%s%c\n", this, '"', lastVal.get(), '"'));
 
             // Look for value of "Cookie" in the request headers
             nsHttpAtom atom = nsHttp::ResolveAtom(token);
             const char *newVal = mRequestHead.PeekHeader(atom);
             if (!lastVal.IsEmpty()) {
                 // value for this header in cache, but no value in request
                 if (!newVal)
                     return true; // yes - response would vary
@@ -1836,19 +1666,19 @@ HttpCacheQuery::ResponseWouldVary() cons
                 if (atom == nsHttp::Cookie) {
                     rv = Hash(newVal, hash);
                     // If hash failed, be conservative (the cached hash
                     // exists at this point) and claim response would vary
                     if (NS_FAILED(rv))
                         return true;
                     newVal = hash.get();
 
-                    LOG(("HttpCacheQuery::ResponseWouldVary [this=%p] " \
+                    LOG(("nsHttpChannel::ResponseWouldVary [this=%x] " \
                             "set-cookie value hashed to %s\n",
-                         mChannel.get(), newVal));
+                         this, newVal));
                 }
 
                 if (strcmp(newVal, lastVal))
                     return true; // yes, response would vary
 
             } else if (newVal) { // old value is empty, but newVal is set
                 return true;
             }
@@ -1866,16 +1696,41 @@ HttpCacheQuery::ResponseWouldVary() cons
 void
 nsHttpChannel::HandleAsyncAbort()
 {
     HttpAsyncAborter<nsHttpChannel>::HandleAsyncAbort();
 }
 
 
 nsresult
+nsHttpChannel::Hash(const char *buf, nsACString &hash)
+{
+    nsresult rv;
+    if (!mHasher) {
+        mHasher = do_CreateInstance(NS_CRYPTO_HASH_CONTRACTID, &rv);
+        if (NS_FAILED(rv)) {
+            LOG(("nsHttpChannel: Failed to instantiate crypto-hasher"));
+            return rv;
+        }
+    }
+
+    rv = mHasher->Init(nsICryptoHash::SHA1);
+    NS_ENSURE_SUCCESS(rv, rv);
+
+   rv = mHasher->Update(reinterpret_cast<unsigned const char*>(buf),
+                         strlen(buf));
+    NS_ENSURE_SUCCESS(rv, rv);
+
+    rv = mHasher->Finish(true, hash);
+    NS_ENSURE_SUCCESS(rv, rv);
+
+    return NS_OK;
+}
+
+nsresult
 nsHttpChannel::EnsureAssocReq()
 {
     // Confirm Assoc-Req response header on pipelined transactions
     // per draft-nottingham-http-pipeline-01.txt
     // of the form: GET http://blah.com/foo/bar?qv
     // return NS_OK as long as we don't find a violation
     // (i.e. no header is ok, as are malformed headers, as are
     // transactions that have not been pipelined (unless those have been
@@ -1981,20 +1836,18 @@ nsHttpChannel::EnsureAssocReq()
     return NS_OK;
 }
 
 //-----------------------------------------------------------------------------
 // nsHttpChannel <byte-range>
 //-----------------------------------------------------------------------------
 
 nsresult
-HttpCacheQuery::SetupByteRangeRequest(PRUint32 partialLen)
+nsHttpChannel::SetupByteRangeRequest(PRUint32 partialLen)
 {
-    AssertOnCacheThread();
-
     // cached content has been found to be partial, add necessary request
     // headers to complete cache entry.
 
     // use strongest validator available...
     const char *val = mCachedResponseHead->PeekHeader(nsHttp::ETag);
     if (!val)
         val = mCachedResponseHead->PeekHeader(nsHttp::Last_Modified);
     if (!val) {
@@ -2062,17 +1915,17 @@ nsHttpChannel::ProcessPartialContent()
     if (NS_FAILED(rv)) return rv;
 
     // notify observers interested in looking at a response that has been
     // merged with any cached headers (http-on-examine-merged-response).
     gHttpHandler->OnExamineMergedResponse(this);
 
     // the cached content is valid, although incomplete.
     mCachedContentIsValid = true;
-    return ReadFromCache(false);
+    return ReadFromCache();
 }
 
 nsresult
 nsHttpChannel::OnDoneReadingPartialCacheEntry(bool *streamDone)
 {
     nsresult rv;
 
     LOG(("nsHttpChannel::OnDoneReadingPartialCacheEntry [this=%p]", this));
@@ -2178,17 +2031,17 @@ nsHttpChannel::ProcessNotModified()
     rv = AddCacheEntryHeaders(mCacheEntry);
     if (NS_FAILED(rv)) return rv;
 
     // notify observers interested in looking at a reponse that has been
     // merged with any cached headers
     gHttpHandler->OnExamineMergedResponse(this);
 
     mCachedContentIsValid = true;
-    rv = ReadFromCache(false);
+    rv = ReadFromCache();
     if (NS_FAILED(rv)) return rv;
 
     mTransactionReplaced = true;
     return NS_OK;
 }
 
 nsresult
 nsHttpChannel::ProcessFallback(bool *waitingForRedirectCallback)
@@ -2232,17 +2085,18 @@ nsHttpChannel::ProcessFallback(bool *wai
     }
 
     mCacheForOfflineUse = false;
     mOfflineCacheClientID.Truncate();
     mOfflineCacheEntry = 0;
     mOfflineCacheAccess = 0;
 
     // Close the current cache entry.
-    CloseCacheEntry(true);
+    if (mCacheEntry)
+        CloseCacheEntry(true);
 
     // Create a new channel to load the fallback entry.
     nsRefPtr<nsIChannel> newChannel;
     rv = gHttpHandler->NewChannel(mURI, getter_AddRefs(newChannel));
     NS_ENSURE_SUCCESS(rv, rv);
 
     rv = SetupReplacementChannel(mURI, newChannel, true, false);
     NS_ENSURE_SUCCESS(rv, rv);
@@ -2324,17 +2178,17 @@ IsSubRangeRequest(nsHttpRequestHead &aRe
     if (!aRequestHead.PeekHeader(nsHttp::Range))
         return false;
     nsCAutoString byteRange;
     aRequestHead.GetHeader(nsHttp::Range, byteRange);
     return !byteRange.EqualsLiteral("bytes=0-");
 }
 
 nsresult
-nsHttpChannel::OpenCacheEntry(bool usingSSL)
+nsHttpChannel::OpenCacheEntry()
 {
     nsresult rv;
 
     NS_ASSERTION(!mOnCacheEntryAvailableCallback, "Unexpected state");
     mLoadedFromApplicationCache = false;
 
     LOG(("nsHttpChannel::OpenCacheEntry [this=%p]", this));
 
@@ -2399,45 +2253,51 @@ nsHttpChannel::OpenCacheEntry(bool using
         }
     }
 
     nsCOMPtr<nsICacheSession> session;
 
     // If we have an application cache, we check it first.
     if (mApplicationCache) {
         nsCAutoString appCacheClientID;
-        rv = mApplicationCache->GetClientID(appCacheClientID);
-        if (NS_SUCCEEDED(rv)) {
-            // We open with ACCESS_READ only, because we don't want to overwrite
-            // the offline cache entry non-atomically. ACCESS_READ will prevent
-            // us from writing to the offline cache as a normal cache entry.
-            mCacheQuery = new HttpCacheQuery(
-                                this, appCacheClientID,
-                                nsICache::STORE_OFFLINE, UsingPrivateBrowsing(),
-                                cacheKey, nsICache::ACCESS_READ,
-                                mLoadFlags & LOAD_BYPASS_LOCAL_CACHE_IF_BUSY,
-                                usingSSL, true);
-
-            mOnCacheEntryAvailableCallback =
-                &nsHttpChannel::OnOfflineCacheEntryAvailable;
-
-            rv = mCacheQuery->Dispatch();
-
-            if (NS_SUCCEEDED(rv))
-                return NS_OK;
-
-            mCacheQuery = nsnull;
-            mOnCacheEntryAvailableCallback = nsnull;
-        }
+        mApplicationCache->GetClientID(appCacheClientID);
+
+        nsCOMPtr<nsICacheService> serv =
+            do_GetService(NS_CACHESERVICE_CONTRACTID, &rv);
+        NS_ENSURE_SUCCESS(rv, rv);
+
+        rv = serv->CreateSession(appCacheClientID.get(),
+                                 nsICache::STORE_OFFLINE,
+                                 nsICache::STREAM_BASED,
+                                 getter_AddRefs(session));
+        NS_ENSURE_SUCCESS(rv, rv);
+
+        session->SetIsPrivate(UsingPrivateBrowsing());
+
+        mOnCacheEntryAvailableCallback =
+            &nsHttpChannel::OnOfflineCacheEntryAvailable;
+        // We open with ACCESS_READ only, because we don't want to overwrite
+        // 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))
+            return NS_OK;
+
+        mOnCacheEntryAvailableCallback = nsnull;
 
         // opening cache entry failed
         return OnOfflineCacheEntryAvailable(nsnull, nsICache::ACCESS_NONE, rv);
     }
 
-    return OpenNormalCacheEntry(usingSSL);
+    return OpenNormalCacheEntry();
 }
 
 nsresult
 nsHttpChannel::OnOfflineCacheEntryAvailable(nsICacheEntryDescriptor *aEntry,
                                             nsCacheAccessMode aAccess,
                                             nsresult aEntryStatus)
 {
     nsresult rv;
@@ -2445,18 +2305,16 @@ nsHttpChannel::OnOfflineCacheEntryAvaila
     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;
     }
 
-    // XXX: shouldn't we fail here? I thought we're supposed to fail the
-    // connection if we can't open an offline cache entry for writing.
     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 NS_ERROR_NOT_AVAILABLE;
     }
 
     if (mCanceled && NS_FAILED(mStatus)) {
         LOG(("channel was canceled [this=%p status=%x]\n", this, mStatus));
@@ -2494,57 +2352,55 @@ nsHttpChannel::OnOfflineCacheEntryAvaila
 
         if (namespaceType &
             nsIApplicationCacheNamespace::NAMESPACE_FALLBACK) {
             rv = namespaceEntry->GetData(mFallbackKey);
             NS_ENSURE_SUCCESS(rv, rv);
         }
     }
 
-    bool usingSSL = false;
-    (void) mURI->SchemeIs("https", &usingSSL);
-    return OpenNormalCacheEntry(usingSSL);
+    return OpenNormalCacheEntry();
 }
 
 
 nsresult
-nsHttpChannel::OpenNormalCacheEntry(bool usingSSL)
+nsHttpChannel::OpenNormalCacheEntry()
 {
     NS_ASSERTION(!mCacheEntry, "We have already mCacheEntry");
 
     nsresult rv;
 
-    bool isPrivate = UsingPrivateBrowsing();
-    nsCacheStoragePolicy storagePolicy = DetermineStoragePolicy(isPrivate);
-    nsDependentCString clientID(
-        GetCacheSessionNameForStoragePolicy(storagePolicy, isPrivate));
-
     nsCAutoString cacheKey;
     GenerateCacheKey(mPostID, cacheKey);
 
-    nsCacheAccessMode accessRequested;
+    nsCacheStoragePolicy storagePolicy = DetermineStoragePolicy();
+
+    nsCOMPtr<nsICacheSession> session;
+    rv = gHttpHandler->GetCacheSession(storagePolicy,
+                                       UsingPrivateBrowsing(),
+                                       getter_AddRefs(session));
+    if (NS_FAILED(rv))
+        return rv;
+
+    nsCacheAccessMode accessRequested = 0;
     rv = DetermineCacheAccess(&accessRequested);
     if (NS_FAILED(rv))
         return rv;
- 
-    mCacheQuery = new HttpCacheQuery(
-                                this, clientID, storagePolicy,
-                                UsingPrivateBrowsing(), cacheKey,
-                                accessRequested,
-                                mLoadFlags & LOAD_BYPASS_LOCAL_CACHE_IF_BUSY,
-                                usingSSL, false);
 
     mOnCacheEntryAvailableCallback =
         &nsHttpChannel::OnNormalCacheEntryAvailable;
-
-    rv = mCacheQuery->Dispatch();
+    rv = session->AsyncOpenCacheEntry(
+        cacheKey,
+        accessRequested,
+        this,
+        mLoadFlags & LOAD_BYPASS_LOCAL_CACHE_IF_BUSY);
+
     if (NS_SUCCEEDED(rv))
         return NS_OK;
 
-    mCacheQuery = nsnull;
     mOnCacheEntryAvailableCallback = nsnull;
 
     return rv;
 }
 
 nsresult
 nsHttpChannel::OnNormalCacheEntryAvailable(nsICacheEntryDescriptor *aEntry,
                                            nsCacheAccessMode aAccess,
@@ -2745,156 +2601,27 @@ nsHttpChannel::UpdateExpirationTime()
     if (mOfflineCacheEntry) {
         rv = mOfflineCacheEntry->SetExpirationTime(expirationTime);
         NS_ENSURE_SUCCESS(rv, rv);
     }
 
     return NS_OK;
 }
 
-NS_IMETHODIMP
-HttpCacheQuery::OnCacheEntryDoomed(nsresult)
-{
-    return NS_ERROR_UNEXPECTED;
-}
-
+// CheckCache is called from Connect after a cache entry has been opened for
+// this URL but before going out to net.  It's purpose is to set or clear the 
+// mCachedContentIsValid flag, and to configure an If-Modified-Since request
+// if validation is required.
 nsresult
-HttpCacheQuery::Dispatch()
-{
-    MOZ_ASSERT(NS_IsMainThread());
-
-    nsresult rv;
-
-    // XXX: Start the cache service; otherwise DispatchToCacheIOThread will
-    // fail.
-    nsCOMPtr<nsICacheService> service = 
-        do_GetService(NS_CACHESERVICE_CONTRACTID, &rv);
-
-    // Ensure the stream transport service gets initialized on the main thread
-    if (NS_SUCCEEDED(rv)) {
-        nsCOMPtr<nsIStreamTransportService> sts =
-            do_GetService(kStreamTransportServiceCID, &rv);
-    }
-
-    if (NS_SUCCEEDED(rv)) {
-        rv = service->GetCacheIOTarget(getter_AddRefs(mCacheThread));
-    }
-
-    if (NS_SUCCEEDED(rv)) {
-        rv = mCacheThread->Dispatch(this, NS_DISPATCH_NORMAL);
-    }
-
-    return rv;
-}
-
-NS_IMETHODIMP
-HttpCacheQuery::Run()
+nsHttpChannel::CheckCache()
 {
-    nsresult rv;
-    if (!NS_IsMainThread()) {
-        AssertOnCacheThread();
-
-        nsCOMPtr<nsICacheService> serv =
-            do_GetService(NS_CACHESERVICE_CONTRACTID, &rv);
-        nsCOMPtr<nsICacheSession> session;
-        if (NS_SUCCEEDED(rv)) {
-            rv = serv->CreateSession(mClientID.get(), mStoragePolicy,
-                                     nsICache::STREAM_BASED,
-                                     getter_AddRefs(session));
-        }
-        if (NS_SUCCEEDED(rv)) {
-            rv = session->SetIsPrivate(mUsingPrivateBrowsing);
-        }
-        if (NS_SUCCEEDED(rv)) {
-            rv = session->SetDoomEntriesIfExpired(false);
-        }
-        if (NS_SUCCEEDED(rv)) {
-            // AsyncOpenCacheEntry isn't really async when its called on the
-            // cache service thread.
-            rv = session->AsyncOpenCacheEntry(mCacheKey, mAccessToRequest, this,
-                                              mNoWait);
-        }
-        if (NS_FAILED(rv)) {
-            LOG(("Failed to open cache entry -- calling OnCacheEntryAvailable "
-                 "directly."));
-            rv = OnCacheEntryAvailable(nsnull, 0, rv);
-        }
-    } else {
-        // break cycles
-        nsCOMPtr<nsICacheListener> channel = mChannel.forget();
-        mCacheThread = nsnull;
-        nsCOMPtr<nsICacheEntryDescriptor> entry = mCacheEntry.forget();
-
-        rv = channel->OnCacheEntryAvailable(entry, mCacheAccess, mStatus);
-    }
-    
-    return rv;
-}
-
-NS_IMETHODIMP
-HttpCacheQuery::OnCacheEntryAvailable(nsICacheEntryDescriptor *entry,
-                                      nsCacheAccessMode access,
-                                      nsresult status)
-
-{
-    LOG(("HttpCacheQuery::OnCacheEntryAvailable [channel=%p entry=%p "
-         "access=%x status=%x, mRunConut=%d]\n", mChannel.get(), entry, access,
-         status, PRIntn(mRunCount)));
-
-    // XXX Bug 759805: Sometimes we will call this method directly from
-    // HttpCacheQuery::Run when AsyncOpenCacheEntry fails, but
-    // AsyncOpenCacheEntry will also call this method. As a workaround, we just
-    // ensure we only execute this code once.
-    NS_ENSURE_TRUE(mRunCount == 0, NS_ERROR_UNEXPECTED);
-    ++mRunCount;
-
-    AssertOnCacheThread();
-
-    mCacheEntry = entry;
-    mCacheAccess = access;
-    mStatus = status;
-
-    nsresult rv = CheckCache();
-    if (NS_FAILED(rv))
-        NS_WARNING("cache check failed");
-
-    if (mCachedContentIsValid) {
-        char* cacheDeviceID = nsnull;
-        mCacheEntry->GetDeviceID(&cacheDeviceID);
-        if (cacheDeviceID) {
-            if (!strcmp(cacheDeviceID, kDiskDeviceID)) {
-                mCacheEntryDeviceTelemetryID
-                    = mozilla::Telemetry::HTTP_DISK_CACHE_DISPOSITION;
-            } else if (!strcmp(cacheDeviceID, kMemoryDeviceID)) {
-                mCacheEntryDeviceTelemetryID
-                    = mozilla::Telemetry::HTTP_MEMORY_CACHE_DISPOSITION;
-            } else if (!strcmp(cacheDeviceID, kOfflineDeviceID)) {
-                mCacheEntryDeviceTelemetryID
-                    = mozilla::Telemetry::HTTP_OFFLINE_CACHE_DISPOSITION;
-            } else {
-                MOZ_NOT_REACHED("unknown cache device ID");
-            }
-
-            delete cacheDeviceID;
-        }
-    }
-
-    rv = NS_DispatchToMainThread(this);
-    return rv;
-}
-
-nsresult
-HttpCacheQuery::CheckCache()
-{
-    AssertOnCacheThread();
-
     nsresult rv = NS_OK;
 
-    LOG(("HttpCacheQuery::CheckCache enter [channel=%p entry=%p access=%d]",
-        mChannel.get(), mCacheEntry.get(), mCacheAccess));
+    LOG(("nsHTTPChannel::CheckCache enter [this=%p entry=%p access=%d]",
+        this, mCacheEntry.get(), mCacheAccess));
     
     // Be pessimistic: assume the cache entry has no useful data.
     mCachedContentIsValid = false;
 
     // Don't proceed unless we have opened a cache entry for reading.
     if (!mCacheEntry || !(mCacheAccess & nsICache::ACCESS_READ))
         return NS_OK;
 
@@ -2924,61 +2651,49 @@ HttpCacheQuery::CheckCache()
             (gHttpHandler->SessionStartTime() > lastModifiedTime);
 
     // Get the cached HTTP response headers
     rv = mCacheEntry->GetMetaDataElement("response-head", getter_Copies(buf));
     NS_ENSURE_SUCCESS(rv, rv);
 
     // Parse the cached HTTP response headers
     mCachedResponseHead = new nsHttpResponseHead();
+    if (!mCachedResponseHead)
+        return NS_ERROR_OUT_OF_MEMORY;
     rv = mCachedResponseHead->Parse((char *) buf.get());
     NS_ENSURE_SUCCESS(rv, rv);
     buf.Adopt(0);
 
-    bool isCachedRedirect = WillRedirect(mCachedResponseHead);
-
-    // Do not return 304 responses from the cache, and also do not return
-    // any other non-redirect 3xx responses from the cache (see bug 759043).
-    NS_ENSURE_TRUE((mCachedResponseHead->Status() / 100 != 3) ||
-                   isCachedRedirect, NS_ERROR_ABORT);
-
     // Don't bother to validate items that are read-only,
     // unless they are read-only because of INHIBIT_CACHING or because
     // we're updating the offline cache.
     // Don't bother to validate if this is a fallback entry.
     if (!mCacheForOfflineUse &&
         (mLoadedFromApplicationCache ||
          (mCacheAccess == nsICache::ACCESS_READ &&
-          !(mLoadFlags & nsIRequest::INHIBIT_CACHING)) ||
+          !(mLoadFlags & INHIBIT_CACHING)) ||
          mFallbackChannel)) {
-        rv = StartBufferingCachedEntity();
-        if (NS_SUCCEEDED(rv)) {
-            mCachedContentIsValid = true;
-            // XXX: Isn't the cache entry already valid?
-            MaybeMarkCacheEntryValid(this, mCacheEntry, mCacheAccess);
-        }
-        return rv;
-    }
+        mCachedContentIsValid = true;
+        return NS_OK;
+    }
+
+    PRUint16 isCachedRedirect = mCachedResponseHead->Status()/100 == 3;
 
     mCustomConditionalRequest =
         mRequestHead.PeekHeader(nsHttp::If_Modified_Since) ||
         mRequestHead.PeekHeader(nsHttp::If_None_Match) ||
         mRequestHead.PeekHeader(nsHttp::If_Unmodified_Since) ||
         mRequestHead.PeekHeader(nsHttp::If_Match) ||
         mRequestHead.PeekHeader(nsHttp::If_Range);
 
     if (method != nsHttp::Head && !isCachedRedirect) {
         // If the cached content-length is set and it does not match the data
         // size of the cached content, then the cached response is partial...
         // either we need to issue a byte range request or we need to refetch
         // the entire document.
-        //
-        // We exclude redirects from this check because we (usually) strip the
-        // entity when we store the cache entry, and even if we didn't, we
-        // always ignore a cached redirect's entity anyway. See bug 759043.
         PRInt64 contentLength = mCachedResponseHead->ContentLength();
         if (contentLength != PRInt64(-1)) {
             PRUint32 size;
             rv = mCacheEntry->GetDataSize(&size);
             NS_ENSURE_SUCCESS(rv, rv);
 
             if (PRInt64(size) != contentLength) {
                 LOG(("Cached data size does not match the Content-Length header "
@@ -2988,61 +2703,54 @@ HttpCacheQuery::CheckCache()
                     mCachedResponseHead->PeekHeader(nsHttp::Content_Encoding)
                     != nsnull;
                 if ((PRInt64(size) < contentLength) &&
                      size > 0 &&
                      !hasContentEncoding &&
                      mCachedResponseHead->IsResumable() &&
                      !mCustomConditionalRequest &&
                      !mCachedResponseHead->NoStore()) {
-                    // looks like a partial entry we can reuse; add If-Range
-                    // and Range headers.
+                    // looks like a partial entry we can reuse
                     rv = SetupByteRangeRequest(size);
-                    mCachedContentIsPartial = NS_SUCCEEDED(rv);
-                    if (mCachedContentIsPartial) {
-                        rv = StartBufferingCachedEntity();
-                    } else {
-                        // Make the request unconditional again.
-                        mRequestHead.ClearHeader(nsHttp::Range);
-                        mRequestHead.ClearHeader(nsHttp::If_Range);
-                    }
+                    NS_ENSURE_SUCCESS(rv, rv);
+                    mCachedContentIsPartial = true;
                 }
-                return rv;
+                return NS_OK;
             }
         }
     }
 
     bool doValidation = false;
     bool canAddImsHeader = true;
 
     // Cached entry is not the entity we request (see bug #633743)
     if (ResponseWouldVary()) {
         LOG(("Validating based on Vary headers returning TRUE\n"));
         canAddImsHeader = false;
         doValidation = true;
     }
     // If the LOAD_FROM_CACHE flag is set, any cached data can simply be used
-    else if (mLoadFlags & nsIRequest::LOAD_FROM_CACHE) {
+    else if (mLoadFlags & LOAD_FROM_CACHE) {
         LOG(("NOT validating based on LOAD_FROM_CACHE load flag\n"));
         doValidation = false;
     }
     // If the VALIDATE_ALWAYS flag is set, any cached data won't be used until
     // it's revalidated with the server.
-    else if (mLoadFlags & nsIRequest::VALIDATE_ALWAYS) {
+    else if (mLoadFlags & VALIDATE_ALWAYS) {
         LOG(("Validating based on VALIDATE_ALWAYS load flag\n"));
         doValidation = true;
     }
     // Even if the VALIDATE_NEVER flag is set, there are still some cases in
     // which we must validate the cached response with the server.
-    else if (mLoadFlags & nsIRequest::VALIDATE_NEVER) {
+    else if (mLoadFlags & VALIDATE_NEVER) {
         LOG(("VALIDATE_NEVER set\n"));
         // if no-store or if no-cache and ssl, validate cached response (see
         // bug 112564 for an explanation of this logic)
         if (mCachedResponseHead->NoStore() ||
-           (mCachedResponseHead->NoCache() && mUsingSSL)) {
+           (mCachedResponseHead->NoCache() && mConnectionInfo->UsingSSL())) {
             LOG(("Validating based on (no-store || (no-cache && ssl)) logic\n"));
             doValidation = true;
         }
         else {
             LOG(("NOT validating based on VALIDATE_NEVER load flag\n"));
             doValidation = false;
         }
     }
@@ -3063,17 +2771,17 @@ HttpCacheQuery::CheckCache()
 
         rv = mCacheEntry->GetExpirationTime(&time);
         NS_ENSURE_SUCCESS(rv, rv);
 
         if (NowInSeconds() <= time)
             doValidation = false;
         else if (mCachedResponseHead->MustValidateIfExpired())
             doValidation = true;
-        else if (mLoadFlags & nsIRequest::VALIDATE_ONCE_PER_SESSION) {
+        else if (mLoadFlags & VALIDATE_ONCE_PER_SESSION) {
             // If the cached response does not include expiration infor-
             // mation, then we must validate the response, despite whether
             // or not this is the first access this session.  This behavior
             // is consistent with existing browsers and is generally expected
             // by web authors.
             rv = mCachedResponseHead->ComputeFreshnessLifetime(&time);
             NS_ENSURE_SUCCESS(rv, rv);
 
@@ -3122,27 +2830,30 @@ HttpCacheQuery::CheckCache()
 
     // Bug #561276: We maintain a chain of cache-keys which returns cached
     // 3xx-responses (redirects) in order to detect cycles. If a cycle is
     // found, ignore the cached response and hit the net. Otherwise, use
     // the cached response and add the cache-key to the chain. Note that
     // a limited number of redirects (cached or not) is allowed and is
     // enforced independently of this mechanism
     if (!doValidation && isCachedRedirect) {
+        nsCAutoString cacheKey;
+        GenerateCacheKey(mPostID, cacheKey);
+
         if (!mRedirectedCachekeys)
             mRedirectedCachekeys = new nsTArray<nsCString>();
-        else if (mRedirectedCachekeys->Contains(mCacheKey))
+        else if (mRedirectedCachekeys->Contains(cacheKey))
             doValidation = true;
 
         LOG(("Redirection-chain %s key %s\n",
-             doValidation ? "contains" : "does not contain", mCacheKey.get()));
+             doValidation ? "contains" : "does not contain", cacheKey.get()));
 
         // Append cacheKey if not in the chain already
         if (!doValidation)
-            mRedirectedCachekeys->AppendElement(mCacheKey);
+            mRedirectedCachekeys->AppendElement(cacheKey);
     }
 
     mCachedContentIsValid = !doValidation;
 
     if (doValidation) {
         //
         // now, we are definitely going to issue a HTTP request to the server.
         // make it conditional if possible.
@@ -3171,303 +2882,154 @@ HttpCacheQuery::CheckCache()
             val = mCachedResponseHead->PeekHeader(nsHttp::ETag);
             if (val)
                 mRequestHead.SetHeader(nsHttp::If_None_Match,
                                        nsDependentCString(val));
             mDidReval = true;
         }
     }
 
-    // If there's any possibility that we may use the cached response's entity
-    // then start reading it into memory now. If we have to revalidate the
-    // entry and the revalidation fails, this will be a wasted effort, but
-    // it is much more likely that either we don't need to revalidate the entry
-    // or the entry will successfully revalidate.
-    if (mCachedContentIsValid || mDidReval) {
-        rv = StartBufferingCachedEntity();
-        if (NS_FAILED(rv)) {
-            // If we can't get the entity then we have to act as though we
-            // don't have the cache entry.
-            if (mDidReval) {
-                // Make the request unconditional again.
-                mRequestHead.ClearHeader(nsHttp::If_Modified_Since);
-                mRequestHead.ClearHeader(nsHttp::ETag);
-                mDidReval = false;
-            }
-            mCachedContentIsValid = false;
-        }
-    }
-
-    if (mCachedContentIsValid) {
-        // XXX: Isn't the cache entry already valid?
-        MaybeMarkCacheEntryValid(this, mCacheEntry, mCacheAccess);
-    }
-
-    LOG(("nsHTTPChannel::CheckCache exit [this=%p doValidation=%d]\n",
-         this, doValidation));
-    return rv;
-}
-
-/*static*/ inline bool
-HttpCacheQuery::HasQueryString(nsHttpAtom method, nsIURI * uri)
-{
-    // Must be called on the main thread because nsIURI does not implement
-    // thread-safe QueryInterface.
-    MOZ_ASSERT(NS_IsMainThread());
-
-    if (method != nsHttp::Get && method != nsHttp::Head)
-        return false;
-
-    nsCAutoString query;
-    nsCOMPtr<nsIURL> url = do_QueryInterface(uri);
-    nsresult rv = url->GetQuery(query);
-    return NS_SUCCEEDED(rv) && !query.IsEmpty();
+    LOG(("nsHTTPChannel::CheckCache exit [this=%p doValidation=%d]\n", this, doValidation));
+    return NS_OK;
 }
 
 bool
-HttpCacheQuery::MustValidateBasedOnQueryUrl() const
+nsHttpChannel::MustValidateBasedOnQueryUrl()
 {
-    AssertOnCacheThread();
-
     // RFC 2616, section 13.9 states that GET-requests with a query-url
     // MUST NOT be treated as fresh unless the server explicitly provides
     // an expiration-time in the response. See bug #468594
     // Section 13.2.1 (6th paragraph) defines "explicit expiration time"
-    if (mHasQueryString)
+    if (mRequestHead.Method() == nsHttp::Get)
     {
-        PRUint32 tmp; // we don't need the value, just whether it's set
-        nsresult rv = mCachedResponseHead->GetExpiresValue(&tmp);
-        if (NS_FAILED(rv)) {
-            rv = mCachedResponseHead->GetMaxAgeValue(&tmp);
+        nsCAutoString query;
+        nsCOMPtr<nsIURL> url = do_QueryInterface(mURI);
+        nsresult rv = url->GetQuery(query);
+        if (NS_SUCCEEDED(rv) && !query.IsEmpty()) {
+            PRUint32 tmp; // we don't need the value, just whether it's set
+            rv = mCachedResponseHead->GetExpiresValue(&tmp);
             if (NS_FAILED(rv)) {
-                return true;
+                rv = mCachedResponseHead->GetMaxAgeValue(&tmp);
+                if (NS_FAILED(rv)) {
+                    return true;
+                }
             }
         }
     }
     return false;
 }
 
 
-bool
-nsHttpChannel::ShouldUpdateOfflineCacheEntry()
+nsresult
+nsHttpChannel::ShouldUpdateOfflineCacheEntry(bool *shouldCacheForOfflineUse)
 {
-    if (!mCacheForOfflineUse || !mOfflineCacheEntry) {
-        return false;
+    *shouldCacheForOfflineUse = false;
+
+    if (!mOfflineCacheEntry) {
+        return NS_OK;
     }
 
     // if we're updating the cache entry, update the offline cache entry too
     if (mCacheEntry && (mCacheAccess & nsICache::ACCESS_WRITE)) {
-        return true;
+        *shouldCacheForOfflineUse = true;
+        return NS_OK;
     }
 
     // if there's nothing in the offline cache, add it
     if (mOfflineCacheEntry && (mOfflineCacheAccess == nsICache::ACCESS_WRITE)) {
-        return true;
+        *shouldCacheForOfflineUse = true;
+        return NS_OK;
     }
 
     // if the document is newer than the offline entry, update it
     PRUint32 docLastModifiedTime;
     nsresult rv = mResponseHead->GetLastModifiedValue(&docLastModifiedTime);
     if (NS_FAILED(rv)) {
-        return true;
+        *shouldCacheForOfflineUse = true;
+        return NS_OK;
     }
 
     PRUint32 offlineLastModifiedTime;
     rv = mOfflineCacheEntry->GetLastModified(&offlineLastModifiedTime);
-    if (NS_FAILED(rv)) {
-        return false;
-    }
+    NS_ENSURE_SUCCESS(rv, rv);
 
     if (docLastModifiedTime > offlineLastModifiedTime) {
-        return true;
-    }
-
-    return false;
-}
-
-nsresult
-HttpCacheQuery::StartBufferingCachedEntity()
-{
-    AssertOnCacheThread();
-
-    if (mUsingSSL) {
-        nsresult rv = mCacheEntry->GetSecurityInfo(
-                                      getter_AddRefs(mCachedSecurityInfo));
-        if (NS_FAILED(rv)) {
-            LOG(("failed to parse security-info [channel=%p, entry=%p]",
-                 this, mCacheEntry.get()));
-            NS_WARNING("failed to parse security-info");
-            return rv;
-        }
-        MOZ_ASSERT(mCachedSecurityInfo);
-        if (!mCachedSecurityInfo) {
-            LOG(("mCacheEntry->GetSecurityInfo returned success but did not "
-                 "return the security info [channel=%p, entry=%p]",
-                 this, mCacheEntry.get()));
-            return NS_ERROR_UNEXPECTED; // XXX error code
-        }
-    }
-
-    nsresult rv = NS_OK;
-
-    // Keep the conditions below in sync with the conditions in ReadFromCache.
-
-    if (WillRedirect(mCachedResponseHead)) {
-        // Do not even try to read the entity for a redirect because we do not
-        // return an entity to the application when we process redirects.
-        LOG(("Will skip read of cached redirect entity\n"));
+        *shouldCacheForOfflineUse = true;
         return NS_OK;
     }
 
-    if ((mLoadFlags & nsICachingChannel::LOAD_ONLY_IF_MODIFIED) &&
-        !mCachedContentIsPartial) {
-        // For LOAD_ONLY_IF_MODIFIED, we usually don't have to deal with the
-        // cached entity. 
-        if (!mCacheForOfflineUse) {
-            LOG(("Will skip read from cache based on LOAD_ONLY_IF_MODIFIED "
-                 "load flag\n"));
-            return NS_OK;
-        }
-
-        // If offline caching has been requested and the offline cache needs
-        // updating, we must complete the call even if the main cache entry
-        // is up to date. We don't know yet for sure whether the offline
-        // cache needs updating because at this point we haven't opened it
-        // for writing yet, so we have to start reading the cached entity now
-        // just in case.
-        LOG(("May skip read from cache based on LOAD_ONLY_IF_MODIFIED "
-              "load flag\n"));
-    }
-
-    // Open an input stream for the entity, but DO NOT create/connect
-    // mCachePump; that is done only when we decide to actually read the
-    // cached entity. By opening the input stream here, we allow the stream
-    // transport service to start reading the entity on one of its
-    // background threads while we do validation (if any).
-
-    nsCOMPtr<nsIInputStream> wrapper;
-
-    nsCOMPtr<nsIInputStream> stream;
-    nsCOMPtr<nsITransport> transport;
-
-    nsCOMPtr<nsIStreamTransportService> sts =
-        do_GetService(kStreamTransportServiceCID, &rv);
-    NS_ENSURE_SUCCESS(rv, rv);
-
-    rv = mCacheEntry->OpenInputStream(0, getter_AddRefs(stream));
-    if (NS_SUCCEEDED(rv)) {
-        rv = sts->CreateInputTransport(stream, PRInt64(-1), PRInt64(-1),
-                                        true, getter_AddRefs(transport));
-    }
-    if (NS_SUCCEEDED(rv)) {
-        rv = transport->OpenInputStream(0, 0, 0, getter_AddRefs(wrapper));
-    }
-    if (NS_SUCCEEDED(rv)) {
-        mCacheAsyncInputStream = do_QueryInterface(wrapper, &rv);
-    }
-    if (NS_SUCCEEDED(rv)) {
-        LOG(("Opened cache input stream [channel=%p, wrapper=%p, "
-              "transport=%p, stream=%p]", this, wrapper.get(),
-              transport.get(), stream.get()));
-    } else {
-        LOG(("Failed to open cache input stream [channel=%p, "
-              "wrapper=%p, transport=%p, stream=%p]", this,
-              wrapper.get(), transport.get(), stream.get()));
-
-        if (wrapper)
-            wrapper->Close();
-        if (stream)
-            stream->Close();
-    }
-
-    return rv;
+    return NS_OK;
 }
 
-// Actually process the cached response that we started to handle in CheckCache
-// and/or StartBufferingCachedEntity.
+// If the data in the cache hasn't expired, then there's no need to
+// talk with the server, not even to do an if-modified-since.  This
+// method creates a stream from the cache, synthesizing all the various
+// channel-related events.
 nsresult
-nsHttpChannel::ReadFromCache(bool alreadyMarkedValid)
+nsHttpChannel::ReadFromCache()
 {
+    nsresult rv;
+
     NS_ENSURE_TRUE(mCacheEntry, NS_ERROR_FAILURE);
     NS_ENSURE_TRUE(mCachedContentIsValid, NS_ERROR_FAILURE);
 
     LOG(("nsHttpChannel::ReadFromCache [this=%p] "
          "Using cached copy of: %s\n", this, mSpec.get()));
 
     if (mCachedResponseHead)
         mResponseHead = mCachedResponseHead;
 
     UpdateInhibitPersistentCachingFlag();
 
     // if we don't already have security info, try to get it from the cache 
     // entry. there are two cases to consider here: 1) we are just reading
     // from the cache, or 2) this may be due to a 304 not modified response,
     // in which case we could have security info from a socket transport.
     if (!mSecurityInfo)
-        mSecurityInfo = mCachedSecurityInfo;
-
-    if (!alreadyMarkedValid && !mCachedContentIsPartial) {
-        // We validated the entry, and we have write access to the cache, so
-        // mark the cache entry as valid in order to allow others access to
-        // this cache entry.
-        //
-        // TODO: This should be done asynchronously so we don't take the cache
-        // service lock on the main thread.
-        MaybeMarkCacheEntryValid(this, mCacheEntry, mCacheAccess);
-    }
-
-    nsresult rv;
-
-    // Keep the conditions below in sync with the conditions in
-    // StartBufferingCachedEntity.
-
-    if (WillRedirect(mResponseHead)) {
-        // TODO: Bug 759040 - We should call HandleAsyncRedirect directly here,
-        // to avoid event dispatching latency.
-        MOZ_ASSERT(!mCacheAsyncInputStream);
-        LOG(("Skipping skip read of cached redirect entity\n"));
+        mCacheEntry->GetSecurityInfo(getter_AddRefs(mSecurityInfo));
+
+    if ((mCacheAccess & nsICache::ACCESS_WRITE) && !mCachedContentIsPartial) {
+        // We have write access to the cache, but we don't need to go to the
+        // server to validate at this time, so just mark the cache entry as
+        // valid in order to allow others access to this cache entry.
+        mCacheEntry->MarkValid();
+    }
+
+    // if this is a cached redirect, we must process the redirect asynchronously
+    // since AsyncOpen may not have returned yet.  Make sure there is a Location
+    // header, otherwise we'll have to treat this like a normal 200 response.
+    if (mResponseHead && (mResponseHead->Status() / 100 == 3) 
+                      && (mResponseHead->PeekHeader(nsHttp::Location)))
         return AsyncCall(&nsHttpChannel::HandleAsyncRedirect);
-    }
-    
+
+    // have we been configured to skip reading from the cache?
     if ((mLoadFlags & LOAD_ONLY_IF_MODIFIED) && !mCachedContentIsPartial) {
-        if (!mCacheForOfflineUse) {
-            LOG(("Skipping read from cache based on LOAD_ONLY_IF_MODIFIED "
+        // if offline caching has been requested and the offline cache needs
+        // updating, complete the call even if the main cache entry is
+        // up-to-date
+        bool shouldUpdateOffline;
+        if (!mCacheForOfflineUse ||
+            NS_FAILED(ShouldUpdateOfflineCacheEntry(&shouldUpdateOffline)) ||
+            !shouldUpdateOffline) {
+
+            LOG(("skipping read from cache based on LOAD_ONLY_IF_MODIFIED "
                  "load flag\n"));
-            MOZ_ASSERT(!mCacheAsyncInputStream);
-            // TODO: Bug 759040 - We should call HandleAsyncNotModified directly
-            // here, to avoid event dispatching latency.
             return AsyncCall(&nsHttpChannel::HandleAsyncNotModified);
         }
-        
-        if (!ShouldUpdateOfflineCacheEntry()) {
-            LOG(("Skipping read from cache based on LOAD_ONLY_IF_MODIFIED "
-                 "load flag (mCacheForOfflineUse case)\n"));
-            mCacheAsyncInputStream.CloseAndRelease();
-            // TODO: Bug 759040 - We should call HandleAsyncNotModified directly
-            // here, to avoid event dispatching latency.
-            return AsyncCall(&nsHttpChannel::HandleAsyncNotModified);
-        }
-    }
-
-    MOZ_ASSERT(mCacheAsyncInputStream);
-    if (!mCacheAsyncInputStream) {
-        NS_ERROR("mCacheAsyncInputStream is null but we're expecting to "
-                        "be able to read from it.");
-        return NS_ERROR_UNEXPECTED;
-    }
-
-
-    nsCOMPtr<nsIAsyncInputStream> inputStream = mCacheAsyncInputStream.forget();
- 
-    rv = nsInputStreamPump::Create(getter_AddRefs(mCachePump), inputStream,
-                                   PRInt64(-1), PRInt64(-1), 0, 0, true);
-    if (NS_FAILED(rv)) {
-        inputStream->Close();
-        return rv;
-    }
+    }
+
+    // open input stream for reading...
+    nsCOMPtr<nsIInputStream> stream;
+    rv = mCacheEntry->OpenInputStream(0, getter_AddRefs(stream));
+    if (NS_FAILED(rv)) return rv;
+
+    rv = nsInputStreamPump::Create(getter_AddRefs(mCachePump),
+                                   stream, PRInt64(-1), PRInt64(-1), 0, 0,
+                                   true);
+    if (NS_FAILED(rv)) return rv;
 
     rv = mCachePump->AsyncRead(this, mListenerContext);
     if (NS_FAILED(rv)) return rv;
 
     if (mTimingEnabled)
         mCacheReadStart = mozilla::TimeStamp::Now();
 
     PRUint32 suspendCount = mSuspendCount;
@@ -3475,19 +3037,16 @@ nsHttpChannel::ReadFromCache(bool alread
         mCachePump->Suspend();
 
     return NS_OK;
 }
 
 void
 nsHttpChannel::CloseCacheEntry(bool doomOnFailure)
 {
-    mCacheQuery = nsnull;
-    mCacheAsyncInputStream.CloseAndRelease();
-
     if (!mCacheEntry)
         return;
 
     LOG(("nsHttpChannel::CloseCacheEntry [this=%p] mStatus=%x mCacheAccess=%x",
          this, mStatus, mCacheAccess));
 
     // If we have begun to create or replace a cache entry, and that cache
     // entry is not complete and not resumable, then it needs to be doomed.
@@ -4226,18 +3785,16 @@ nsHttpChannel::Cancel(nsresult status)
     mCanceled = true;
     mStatus = status;
     if (mProxyRequest)
         mProxyRequest->Cancel(status);
     if (mTransaction)
         gHttpHandler->CancelTransaction(mTransaction, status);
     if (mTransactionPump)
         mTransactionPump->Cancel(status);
-    mCacheQuery = nsnull;
-    mCacheAsyncInputStream.CloseAndRelease();
     if (mCachePump)
         mCachePump->Cancel(status);
     if (mAuthProvider)
         mAuthProvider->Cancel(status);
     return NS_OK;
 }
 
 NS_IMETHODIMP
@@ -4399,18 +3956,16 @@ nsHttpChannel::AsyncOpen(nsIStreamListen
 
 //-----------------------------------------------------------------------------
 // nsHttpChannel::nsIHttpChannelInternal
 //-----------------------------------------------------------------------------
 
 NS_IMETHODIMP
 nsHttpChannel::SetupFallbackChannel(const char *aFallbackKey)
 {
-    ENSURE_CALLED_BEFORE_ASYNC_OPEN();
-
     LOG(("nsHttpChannel::SetupFallbackChannel [this=%x, key=%s]",
          this, aFallbackKey));
     mFallbackChannel = true;
     mFallbackKey = aFallbackKey;
 
     return NS_OK;
 }
 
@@ -4943,18 +4498,18 @@ nsHttpChannel::OnStopRequest(nsIRequest 
             // blob response) may hold the token to the cache
             // entry. So we mark the cache valid here.
             // We also need to check the entry is stored as file
             // because we write to the cache asynchronously when
             // it isn't stored in the file and it isn't completely
             // written to the disk yet.
             mCacheEntry->MarkValid();
         }
-    }
-    CloseCacheEntry(!contentComplete);
+        CloseCacheEntry(!contentComplete);
+    }
 
     if (mOfflineCacheEntry)
         CloseOfflineCacheEntry();
 
     if (mLoadGroup)
         mLoadGroup->RemoveRequest(this, nsnull, status);
 
     // We don't need this info anymore
@@ -5279,17 +4834,18 @@ nsHttpChannel::GetCacheKey(nsISupports *
 
 NS_IMETHODIMP
 nsHttpChannel::SetCacheKey(nsISupports *key)
 {
     nsresult rv;
 
     LOG(("nsHttpChannel::SetCacheKey [this=%p key=%p]\n", this, key));
 
-    ENSURE_CALLED_BEFORE_ASYNC_OPEN();
+    // can only set the cache key if a load is not in progress
+    NS_ENSURE_TRUE(!mIsPending, NS_ERROR_IN_PROGRESS);
 
     if (!key)
         mPostID = 0;
     else {
         // extract the post id
         nsCOMPtr<nsISupportsPRUint32> container = do_QueryInterface(key, &rv);
         if (NS_FAILED(rv)) return rv;
 
@@ -5331,36 +4887,32 @@ nsHttpChannel::GetCacheForOfflineUse(boo
     *value = mCacheForOfflineUse;
 
     return NS_OK;
 }
 
 NS_IMETHODIMP
 nsHttpChannel::SetCacheForOfflineUse(bool value)
 {
-    ENSURE_CALLED_BEFORE_ASYNC_OPEN();
-
     mCacheForOfflineUse = value;
 
     return NS_OK;
 }
 
 NS_IMETHODIMP
 nsHttpChannel::GetOfflineCacheClientID(nsACString &value)
 {
     value = mOfflineCacheClientID;
 
     return NS_OK;
 }
 
 NS_IMETHODIMP
 nsHttpChannel::SetOfflineCacheClientID(const nsACString &value)
 {
-    ENSURE_CALLED_BEFORE_ASYNC_OPEN();
-
     mOfflineCacheClientID = value;
 
     return NS_OK;
 }
 
 NS_IMETHODIMP
 nsHttpChannel::GetCacheFile(nsIFile **cacheFile)
 {
@@ -5389,46 +4941,27 @@ nsHttpChannel::ResumeAt(PRUint64 aStartP
 // nsHttpChannel::nsICacheListener
 //-----------------------------------------------------------------------------
 
 NS_IMETHODIMP
 nsHttpChannel::OnCacheEntryAvailable(nsICacheEntryDescriptor *entry,
                                      nsCacheAccessMode access,
                                      nsresult status)
 {
-    MOZ_ASSERT(NS_IsMainThread());
-
     nsresult rv;
 
     LOG(("nsHttpChannel::OnCacheEntryAvailable [this=%p entry=%p "
          "access=%x status=%x]\n", this, entry, access, status));
 
-    if (mCacheQuery) {
-        mRequestHead = mCacheQuery->mRequestHead;
-        mRedirectedCachekeys = mCacheQuery->mRedirectedCachekeys.forget();
-        mCacheAsyncInputStream.takeOver(mCacheQuery->mCacheAsyncInputStream);
-        mCachedResponseHead = mCacheQuery->mCachedResponseHead.forget();
-        mCachedSecurityInfo = mCacheQuery->mCachedSecurityInfo.forget();
-        mCachedContentIsValid = mCacheQuery->mCachedContentIsValid;
-        mCachedContentIsPartial = mCacheQuery->mCachedContentIsPartial;
-        mCustomConditionalRequest = mCacheQuery->mCustomConditionalRequest;
-        mDidReval = mCacheQuery->mDidReval;
-        mCacheEntryDeviceTelemetryID = mCacheQuery->mCacheEntryDeviceTelemetryID;
-        mCacheQuery = nsnull;
-    }
-
     // if the channel's already fired onStopRequest, then we should ignore
     // this event.
-    if (!mIsPending) {
-        mCacheAsyncInputStream.CloseAndRelease();
+    if (!mIsPending)
         return NS_OK;
-    }
 
     rv = OnCacheEntryAvailableInternal(entry, access, status);
-
     if (NS_FAILED(rv)) {
         CloseCacheEntry(true);
         AsyncAbort(rv);
     }
 
     return NS_OK;
 }
 
@@ -5479,17 +5012,17 @@ nsHttpChannel::OnCacheEntryAvailableInte
                 return rv;
         }
     } else {
         // check result of OnOfflineCacheEntryForWritingAvailable()
         if (NS_FAILED(rv))
             return rv;
     }
 
-    return ContinueConnect();
+    return Connect(false);
 }
 
 NS_IMETHODIMP
 nsHttpChannel::OnCacheEntryDoomed(nsresult status)
 {
     return NS_ERROR_NOT_IMPLEMENTED;
 }
 
@@ -5559,17 +5092,17 @@ nsHttpChannel::GetApplicationCache(nsIAp
 {
     NS_IF_ADDREF(*out = mApplicationCache);
     return NS_OK;
 }
 
 NS_IMETHODIMP
 nsHttpChannel::SetApplicationCache(nsIApplicationCache *appCache)
 {
-    ENSURE_CALLED_BEFORE_ASYNC_OPEN();
+    NS_ENSURE_TRUE(!mWasOpened, NS_ERROR_ALREADY_OPENED);
 
     mApplicationCache = appCache;
     return NS_OK;
 }
 
 NS_IMETHODIMP
 nsHttpChannel::GetLoadedFromApplicationCache(bool *aLoadedFromApplicationCache)
 {
@@ -5582,33 +5115,33 @@ nsHttpChannel::GetInheritApplicationCach
 {
     *aInherit = mInheritApplicationCache;
     return NS_OK;
 }
 
 NS_IMETHODIMP
 nsHttpChannel::SetInheritApplicationCache(bool aInherit)
 {
-    ENSURE_CALLED_BEFORE_ASYNC_OPEN();
+    NS_ENSURE_TRUE(!mWasOpened, NS_ERROR_ALREADY_OPENED);
 
     mInheritApplicationCache = aInherit;
     return NS_OK;
 }
 
 NS_IMETHODIMP
 nsHttpChannel::GetChooseApplicationCache(bool *aChoose)
 {
     *aChoose = mChooseApplicationCache;
     return NS_OK;
 }
 
 NS_IMETHODIMP
 nsHttpChannel::SetChooseApplicationCache(bool aChoose)
 {
-    ENSURE_CALLED_BEFORE_ASYNC_OPEN();
+    NS_ENSURE_TRUE(!mWasOpened, NS_ERROR_ALREADY_OPENED);
 
     mChooseApplicationCache = aChoose;
     return NS_OK;
 }
 
 nsHttpChannel::OfflineCacheEntryAsForeignMarker*
 nsHttpChannel::GetOfflineCacheEntryAsForeignMarker()
 {
@@ -5808,58 +5341,43 @@ nsHttpChannel::InvalidateCacheEntryForLo
         } else
             NS_WARNING(("  failed getting ascii-spec\n"));
     } else {
         LOG(("  hosts not matching\n"));
     }
 }
 
 void
-nsHttpChannel::DoInvalidateCacheEntry(const nsCString &key)
+nsHttpChannel::DoInvalidateCacheEntry(nsACString &key)
 {
     // NOTE:
     // Following comments 24,32 and 33 in bug #327765, we only care about
     // the cache in the protocol-handler, not the application cache.
     // The logic below deviates from the original logic in OpenCacheEntry on
     // one point by using only READ_ONLY access-policy. I think this is safe.
 
     // First, find session holding the cache-entry - use current storage-policy
-    bool isPrivate = UsingPrivateBrowsing();
-    nsCacheStoragePolicy storagePolicy = DetermineStoragePolicy(isPrivate);
-    const char * clientID = GetCacheSessionNameForStoragePolicy(storagePolicy,
-                                                                isPrivate);
-
-    LOG(("DoInvalidateCacheEntry [channel=%p session=%s policy=%d key=%s]",
-         this, clientID, PRIntn(storagePolicy), key.get()));
-
-    nsresult rv;
-    nsCOMPtr<nsICacheService> serv =
-        do_GetService(NS_CACHESERVICE_CONTRACTID, &rv);
     nsCOMPtr<nsICacheSession> session;
-    if (NS_SUCCEEDED(rv)) {
-        rv = serv->CreateSession(clientID, storagePolicy,  
-                                 nsICache::STREAM_BASED,
-                                 getter_AddRefs(session));
-    }
-    if (NS_SUCCEEDED(rv)) {
-        rv = session->SetIsPrivate(UsingPrivateBrowsing());
-    }
-    if (NS_SUCCEEDED(rv)) {
-        rv = session->DoomEntry(key, nsnull);
-    }
-
-    LOG(("DoInvalidateCacheEntry [channel=%p session=%s policy=%d key=%s rv=%d]",
-         this, clientID, PRIntn(storagePolicy), key.get(), PRIntn(rv)));
+    nsCacheStoragePolicy storagePolicy = DetermineStoragePolicy();
+
+    nsresult rv = gHttpHandler->GetCacheSession(storagePolicy,
+                                                UsingPrivateBrowsing(),
+                                                getter_AddRefs(session));
+
+    if (NS_FAILED(rv))
+        return;
+
+    session->DoomEntry(key, nsnull);
 }
 
 nsCacheStoragePolicy
-nsHttpChannel::DetermineStoragePolicy(bool isPrivate)
+nsHttpChannel::DetermineStoragePolicy()
 {
     nsCacheStoragePolicy policy = nsICache::STORE_ANYWHERE;
-    if (isPrivate)
+    if (UsingPrivateBrowsing())
         policy = nsICache::STORE_IN_MEMORY;
     else if (mLoadFlags & INHIBIT_PERSISTENT_CACHING)
         policy = nsICache::STORE_IN_MEMORY;
 
     return policy;
 }
 
 nsresult
@@ -5884,10 +5402,8 @@ nsHttpChannel::DetermineCacheAccess(nsCa
 }
 
 void
 nsHttpChannel::AsyncOnExamineCachedResponse()
 {
     gHttpHandler->OnExamineCachedResponse(this);
 
 }
-
-} } // namespace mozilla::net
--- a/netwerk/protocol/http/nsHttpChannel.h
+++ b/netwerk/protocol/http/nsHttpChannel.h
@@ -21,27 +21,25 @@
 #include "nsIApplicationCacheChannel.h"
 #include "nsIPrompt.h"
 #include "nsIResumableChannel.h"
 #include "nsIProtocolProxyCallback.h"
 #include "nsICancelable.h"
 #include "nsIHttpAuthenticableChannel.h"
 #include "nsIHttpChannelAuthProvider.h"
 #include "nsIAsyncVerifyRedirectCallback.h"
+#include "nsICryptoHash.h"
 #include "nsITimedChannel.h"
 #include "nsDNSPrefetch.h"
 #include "TimingStruct.h"
-#include "AutoClose.h"
-#include "mozilla/Telemetry.h"
 
 class nsAHttpConnection;
+class AutoRedirectVetoNotifier;
 
-namespace mozilla { namespace net {
-
-class HttpCacheQuery;
+using namespace mozilla::net;
 
 //-----------------------------------------------------------------------------
 // nsHttpChannel
 //-----------------------------------------------------------------------------
 
 class nsHttpChannel : public HttpBaseChannel
                     , public HttpAsyncAborter<nsHttpChannel>
                     , public nsIStreamListener
@@ -153,33 +151,33 @@ public: /* internal necko use only */
         GetUsingPrivateBrowsing(&usingPB);
         return usingPB;
     }
 
 private:
     typedef nsresult (nsHttpChannel::*nsContinueRedirectionFunc)(nsresult result);
 
     bool     RequestIsConditional();
-    nsresult Connect();
-    nsresult ContinueConnect();
+    nsresult Connect(bool firstTime = true);
     void     SpeculativeConnect();
     nsresult SetupTransaction();
     nsresult CallOnStartRequest();
     nsresult ProcessResponse();
     nsresult ContinueProcessResponse(nsresult);
     nsresult ProcessNormal();
     nsresult ContinueProcessNormal(nsresult);
     nsresult ProcessNotModified();
     nsresult AsyncProcessRedirection(PRUint32 httpStatus);
     nsresult ContinueProcessRedirection(nsresult);
     nsresult ContinueProcessRedirectionAfterFallback(nsresult);
     bool     ShouldSSLProxyResponseContinue(PRUint32 httpStatus);
     nsresult ProcessFailedSSLConnect(PRUint32 httpStatus);
     nsresult ProcessFallback(bool *waitingForRedirectCallback);
     nsresult ContinueProcessFallback(nsresult);
+    bool     ResponseWouldVary();
     void     HandleAsyncAbort();
     nsresult EnsureAssocReq();
 
     nsresult ContinueOnStartRequest1(nsresult);
     nsresult ContinueOnStartRequest2(nsresult);
     nsresult ContinueOnStartRequest3(nsresult);
 
     // redirection specific methods
@@ -197,78 +195,85 @@ private:
     nsresult ProxyFailover();
     nsresult AsyncDoReplaceWithProxy(nsIProxyInfo *);
     nsresult ContinueDoReplaceWithProxy(nsresult);
     void HandleAsyncReplaceWithProxy();
     nsresult ContinueHandleAsyncReplaceWithProxy(nsresult);
     nsresult ResolveProxy();
 
     // cache specific methods
-    nsresult OpenCacheEntry(bool usingSSL);
+    nsresult OpenCacheEntry();
     nsresult OnOfflineCacheEntryAvailable(nsICacheEntryDescriptor *aEntry,
                                           nsCacheAccessMode aAccess,
                                           nsresult aResult);
-    nsresult OpenNormalCacheEntry(bool usingSSL);
+    nsresult OpenNormalCacheEntry();
     nsresult OnNormalCacheEntryAvailable(nsICacheEntryDescriptor *aEntry,
                                          nsCacheAccessMode aAccess,
                                          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();
-    bool ShouldUpdateOfflineCacheEntry();
-    nsresult StartBufferingCachedEntity(bool usingSSL);
-    nsresult ReadFromCache(bool alreadyMarkedValid);
+    nsresult ShouldUpdateOfflineCacheEntry(bool *shouldCacheForOfflineUse);
+    nsresult ReadFromCache();
     void     CloseCacheEntry(bool doomOnFailure);
     void     CloseOfflineCacheEntry();
     nsresult InitCacheEntry();
     void     UpdateInhibitPersistentCachingFlag();
     nsresult InitOfflineCacheEntry();
     nsresult AddCacheEntryHeaders(nsICacheEntryDescriptor *entry);
     nsresult StoreAuthorizationMetaData(nsICacheEntryDescriptor *entry);
     nsresult FinalizeCacheEntry();
     nsresult InstallCacheListener(PRUint32 offset = 0);
     nsresult InstallOfflineCacheListener();
     void     MaybeInvalidateCacheEntryForSubsequentGet();
-    nsCacheStoragePolicy DetermineStoragePolicy(bool isPrivate);
+    nsCacheStoragePolicy DetermineStoragePolicy();
     nsresult DetermineCacheAccess(nsCacheAccessMode *_retval);
     void     AsyncOnExamineCachedResponse();
 
     // Handle the bogus Content-Encoding Apache sometimes sends
     void ClearBogusContentEncodingIfNeeded();
 
     // byte range request specific methods
+    nsresult SetupByteRangeRequest(PRUint32 partialLen);
     nsresult ProcessPartialContent();
     nsresult OnDoneReadingPartialCacheEntry(bool *streamDone);
 
     nsresult DoAuthRetry(nsAHttpConnection *);
+    bool     MustValidateBasedOnQueryUrl();
 
     void     HandleAsyncRedirectChannelToHttps();
     nsresult AsyncRedirectChannelToHttps();
     nsresult ContinueAsyncRedirectChannelToHttps(nsresult rv);
 
     /**
      * A function that takes care of reading STS headers and enforcing STS 
      * load rules.  After a secure channel is erected, STS requires the channel
      * to be trusted or any STS header data on the channel is ignored.
      * This is called from ProcessResponse.
      */
     nsresult ProcessSTSHeader();
 
+    /**
+     * Computes and returns a 64 bit encoded string holding a hash of the
+     * input buffer. Input buffer must be a null-terminated string.
+     */
+    nsresult Hash(const char *buf, nsACString &hash);
+
     void InvalidateCacheEntryForLocation(const char *location);
     void AssembleCacheKey(const char *spec, PRUint32 postID, nsACString &key);
     nsresult CreateNewURI(const char *loc, nsIURI **newURI);
-    void DoInvalidateCacheEntry(const nsCString &key);
+    void DoInvalidateCacheEntry(nsACString &key);
 
     // Ref RFC2616 13.10: "invalidation... MUST only be performed if
     // the host part is the same as in the Request-URI"
     inline bool HostPartIsTheSame(nsIURI *uri) {
         nsCAutoString tmpHost1, tmpHost2;
         return (NS_SUCCEEDED(mURI->GetAsciiHost(tmpHost1)) &&
                 NS_SUCCEEDED(uri->GetAsciiHost(tmpHost2)) &&
                 (tmpHost1 == tmpHost2));
@@ -279,25 +284,20 @@ private:
     nsCOMPtr<nsICancelable>           mProxyRequest;
 
     nsRefPtr<nsInputStreamPump>       mTransactionPump;
     nsRefPtr<nsHttpTransaction>       mTransaction;
 
     PRUint64                          mLogicalOffset;
 
     // cache specific data
-    nsRefPtr<HttpCacheQuery>          mCacheQuery;
     nsCOMPtr<nsICacheEntryDescriptor> mCacheEntry;
-    // We must close mCacheAsyncInputStream explicitly to avoid leaks.
-    AutoClose<nsIAsyncInputStream>    mCacheAsyncInputStream;
     nsRefPtr<nsInputStreamPump>       mCachePump;
     nsAutoPtr<nsHttpResponseHead>     mCachedResponseHead;
-    nsCOMPtr<nsISupports>             mCachedSecurityInfo;
     nsCacheAccessMode                 mCacheAccess;
-    mozilla::Telemetry::ID            mCacheEntryDeviceTelemetryID;
     PRUint32                          mPostID;
     PRUint32                          mRequestTime;
 
     typedef nsresult (nsHttpChannel:: *nsOnCacheEntryAvailableCallback)(
         nsICacheEntryDescriptor *, nsCacheAccessMode, nsresult);
     nsOnCacheEntryAvailableCallback   mOnCacheEntryAvailableCallback;
 
     nsCOMPtr<nsICacheEntryDescriptor> mOfflineCacheEntry;
@@ -312,18 +312,16 @@ private:
 
     // If the channel is associated with a cache, and the URI matched
     // a fallback namespace, this will hold the key for the fallback
     // cache entry.
     nsCString                         mFallbackKey;
 
     friend class AutoRedirectVetoNotifier;
     friend class HttpAsyncAborter<nsHttpChannel>;
-    friend class HttpCacheQuery;
-
     nsCOMPtr<nsIURI>                  mRedirectURI;
     nsCOMPtr<nsIChannel>              mRedirectChannel;
     PRUint32                          mRedirectType;
 
     // state flags
     PRUint32                          mCachedContentIsValid     : 1;
     PRUint32                          mCachedContentIsPartial   : 1;
     PRUint32                          mTransactionReplaced      : 1;
@@ -341,16 +339,18 @@ private:
     PRUint32                          mFallingBack              : 1;
     PRUint32                          mWaitingForRedirectCallback : 1;
     // True if mRequestTime has been set. In such a case it is safe to update
     // the cache entry's expiration time. Otherwise, it is not(see bug 567360).
     PRUint32                          mRequestTimeInitialized : 1;
 
     nsTArray<nsContinueRedirectionFunc> mRedirectFuncStack;
 
+    nsCOMPtr<nsICryptoHash>        mHasher;
+
     PRTime                            mChannelCreationTime;
     mozilla::TimeStamp                mChannelCreationTimestamp;
     mozilla::TimeStamp                mAsyncOpenTime;
     mozilla::TimeStamp                mCacheReadStart;
     mozilla::TimeStamp                mCacheReadEnd;
     // copied from the transaction before we null out mTransaction
     // so that the timing can still be queried from OnStopRequest
     TimingStruct                      mTransactionTimings;
@@ -369,11 +369,9 @@ private: // cache telemetry
         kCacheHit = 1,
         kCacheHitViaReval = 2,
         kCacheMissedViaReval = 3,
         kCacheMissed = 4
     };
     bool mDidReval;
 };
 
-} } // namespace mozilla::net
-
 #endif // nsHttpChannel_h__
--- a/netwerk/protocol/http/nsHttpHandler.cpp
+++ b/netwerk/protocol/http/nsHttpHandler.cpp
@@ -402,16 +402,62 @@ nsHttpHandler::IsAcceptableEncoding(cons
     // to accept.
     if (!PL_strncasecmp(enc, "x-", 2))
         enc += 2;
     
     return nsHttp::FindToken(mAcceptEncodings.get(), enc, HTTP_LWS ",") != nsnull;
 }
 
 nsresult
+nsHttpHandler::GetCacheSession(nsCacheStoragePolicy storagePolicy,
+                               bool isPrivate,
+                               nsICacheSession **result)
+{
+    nsresult rv;
+
+    // Skip cache if disabled in preferences
+    if (!mUseCache)
+        return NS_ERROR_NOT_AVAILABLE;
+
+    // We want to get the pointer to the cache service each time we're called,
+    // because it's possible for some add-ons (such as Google Gears) to swap
+    // in new cache services on the fly, and we want to pick them up as
+    // appropriate.
+    nsCOMPtr<nsICacheService> serv = do_GetService(NS_CACHESERVICE_CONTRACTID,
+                                                   &rv);
+    if (NS_FAILED(rv)) return rv;
+
+    const char *sessionName = "HTTP";
+    switch (storagePolicy) {
+    case nsICache::STORE_IN_MEMORY:
+        sessionName = isPrivate ? "HTTP-memory-only-PB" : "HTTP-memory-only";
+        break;
+    case nsICache::STORE_OFFLINE:
+        sessionName = "HTTP-offline";
+        break;
+    default:
+        break;
+    }
+
+    nsCOMPtr<nsICacheSession> cacheSession;
+    rv = serv->CreateSession(sessionName,
+                             storagePolicy,
+                             nsICache::STREAM_BASED,
+                             getter_AddRefs(cacheSession));
+    if (NS_FAILED(rv)) return rv;
+
+    rv = cacheSession->SetDoomEntriesIfExpired(false);
+    if (NS_FAILED(rv)) return rv;
+
+    NS_ADDREF(*result = cacheSession);
+
+    return NS_OK;
+}
+
+nsresult
 nsHttpHandler::GetStreamConverterService(nsIStreamConverterService **result)
 {
     if (!mStreamConvSvc) {
         nsresult rv;
         mStreamConvSvc = do_GetService(NS_STREAMCONVERTERSERVICE_CONTRACTID, &rv);
         if (NS_FAILED(rv)) return rv;
     }
     *result = mStreamConvSvc;
--- a/netwerk/protocol/http/nsHttpHandler.h
+++ b/netwerk/protocol/http/nsHttpHandler.h
@@ -96,17 +96,17 @@ public:
     PRIntervalTime SpdyPingTimeout() { return mSpdyPingTimeout; }
 
     bool           PromptTempRedirect()      { return mPromptTempRedirect; }
 
     nsHttpAuthCache     *AuthCache() { return &mAuthCache; }
     nsHttpConnectionMgr *ConnMgr()   { return mConnMgr; }
 
     // cache support
-    bool UseCache() const { return mUseCache; }
+    nsresult GetCacheSession(nsCacheStoragePolicy, bool isPrivate, nsICacheSession **);
     PRUint32 GenerateUniqueID() { return ++mLastUniqueID; }
     PRUint32 SessionStartTime() { return mSessionStartTime; }
 
     //
     // Connection management methods:
     //
     // - the handler only owns idle connections; it does not own active
     //   connections.
--- a/netwerk/protocol/http/nsHttpResponseHead.cpp
+++ b/netwerk/protocol/http/nsHttpResponseHead.cpp
@@ -375,21 +375,17 @@ nsHttpResponseHead::MustValidateIfExpire
     return HasHeaderValue(nsHttp::Cache_Control, "must-revalidate");
 }
 
 bool
 nsHttpResponseHead::IsResumable() const
 {
     // even though some HTTP/1.0 servers may support byte range requests, we're not
     // going to bother with them, since those servers wouldn't understand If-Range.
-    // Also, while in theory it may be possible to resume when the status code
-    // is not 200, it is unlikely to be worth the trouble, especially for
-    // non-2xx responses.
-    return mStatus == 200 &&
-           mVersion >= NS_HTTP_VERSION_1_1 &&
+    return mVersion >= NS_HTTP_VERSION_1_1 &&
            PeekHeader(nsHttp::Content_Length) && 
           (PeekHeader(nsHttp::ETag) || PeekHeader(nsHttp::Last_Modified)) &&
            HasHeaderValue(nsHttp::Accept_Ranges, "bytes");
 }
 
 bool
 nsHttpResponseHead::ExpiresInPast() const
 {