Bug 1325091 - Add flag indicating presence of alternative data in the cache entry to cache index. r=michal
authorJunior Hsu <juhsu@mozilla.com>
Mon, 06 Mar 2017 18:21:59 +0800
changeset 348747 10514ae0b128eb7d1257821224ac6366cc3a0aae
parent 348746 229705d51c0077f0dec34acc38161d4460bed51e
child 348748 706d07027f02713274e0246df2eb4e472fc67efd
push id88304
push userryanvm@gmail.com
push dateWed, 22 Mar 2017 01:53:57 +0000
treeherdermozilla-inbound@706d07027f02 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmichal
bugs1325091
milestone55.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1325091 - Add flag indicating presence of alternative data in the cache entry to cache index. r=michal
netwerk/cache2/CacheFile.cpp
netwerk/cache2/CacheFile.h
netwerk/cache2/CacheFileIOManager.cpp
netwerk/cache2/CacheFileIOManager.h
netwerk/cache2/CacheFileUtils.cpp
netwerk/cache2/CacheIndex.cpp
netwerk/cache2/CacheIndex.h
--- a/netwerk/cache2/CacheFile.cpp
+++ b/netwerk/cache2/CacheFile.cpp
@@ -884,17 +884,17 @@ CacheFile::OpenOutputStream(CacheOutputC
   if (mAltDataOffset != -1) {
     // Remove alt-data
     rv = Truncate(mAltDataOffset);
     if (NS_FAILED(rv)) {
       LOG(("CacheFile::OpenOutputStream() - Truncating alt-data failed "
            "[rv=0x%08" PRIx32 "]", static_cast<uint32_t>(rv)));
       return rv;
     }
-    mMetadata->SetElement(CacheFileUtils::kAltDataKey, nullptr);
+    SetAltMetadata(nullptr);
     mAltDataOffset = -1;
   }
 
   // Once we open output stream we no longer allow preloading of chunks without
   // input stream. There is no reason to believe that some input stream will be
   // opened soon. Otherwise we would cache unused chunks of all newly created
   // entries until the CacheFile is destroyed.
   mPreloadWithoutInputStreams = false;
@@ -913,18 +913,16 @@ nsresult
 CacheFile::OpenAlternativeOutputStream(CacheOutputCloseListener *aCloseListener,
                                        const char *aAltDataType,
                                        nsIOutputStream **_retval)
 {
   CacheFileAutoLock lock(this);
 
   MOZ_ASSERT(mHandle || mMemoryOnly || mOpeningFile);
 
-  nsresult rv;
-
   if (!mReady) {
     LOG(("CacheFile::OpenAlternativeOutputStream() - CacheFile is not ready "
          "[this=%p]", this));
 
     return NS_ERROR_NOT_AVAILABLE;
   }
 
   if (mOutput) {
@@ -936,37 +934,37 @@ CacheFile::OpenAlternativeOutputStream(C
 
   // Fail if there is any input stream opened for alternative data
   for (uint32_t i = 0; i < mInputs.Length(); ++i) {
     if (mInputs[i]->IsAlternativeData()) {
       return NS_ERROR_NOT_AVAILABLE;
     }
   }
 
+  nsresult rv;
+
   if (mAltDataOffset != -1) {
     // Truncate old alt-data
     rv = Truncate(mAltDataOffset);
     if (NS_FAILED(rv)) {
       LOG(("CacheFile::OpenAlternativeOutputStream() - Truncating old alt-data "
            "failed [rv=0x%08" PRIx32 "]", static_cast<uint32_t>(rv)));
       return rv;
     }
   } else {
     mAltDataOffset = mDataSize;
   }
 
   nsAutoCString altMetadata;
   CacheFileUtils::BuildAlternativeDataInfo(aAltDataType, mAltDataOffset,
                                            altMetadata);
-  rv = mMetadata->SetElement(CacheFileUtils::kAltDataKey, altMetadata.get());
+  rv = SetAltMetadata(altMetadata.get());
   if (NS_FAILED(rv)) {
-    // Removing element shouldn't fail because it doesn't allocate memory.
-    mMetadata->SetElement(CacheFileUtils::kAltDataKey, nullptr);
-
-    mAltDataOffset = -1;
+    LOG(("CacheFile::OpenAlternativeOutputStream() - Set Metadata for alt-data"
+         "failed [rv=0x%08" PRIx32 "]", static_cast<uint32_t>(rv)));
     return rv;
   }
 
   // Once we open output stream we no longer allow preloading of chunks without
   // input stream. There is no reason to believe that some input stream will be
   // opened soon. Otherwise we would cache unused chunks of all newly created
   // entries until the CacheFile is destroyed.
   mPreloadWithoutInputStreams = false;
@@ -1151,17 +1149,17 @@ CacheFile::SetExpirationTime(uint32_t aE
        this, aExpirationTime));
 
   MOZ_ASSERT(mMetadata);
   NS_ENSURE_TRUE(mMetadata, NS_ERROR_UNEXPECTED);
 
   PostWriteTimer();
 
   if (mHandle && !mHandle->IsDoomed())
-    CacheFileIOManager::UpdateIndexEntry(mHandle, nullptr, &aExpirationTime);
+    CacheFileIOManager::UpdateIndexEntry(mHandle, nullptr, &aExpirationTime, nullptr);
 
   return mMetadata->SetExpirationTime(aExpirationTime);
 }
 
 nsresult
 CacheFile::GetExpirationTime(uint32_t *_retval)
 {
   CacheFileAutoLock lock(this);
@@ -1180,32 +1178,60 @@ CacheFile::SetFrecency(uint32_t aFrecenc
        this, aFrecency));
 
   MOZ_ASSERT(mMetadata);
   NS_ENSURE_TRUE(mMetadata, NS_ERROR_UNEXPECTED);
 
   PostWriteTimer();
 
   if (mHandle && !mHandle->IsDoomed())
-    CacheFileIOManager::UpdateIndexEntry(mHandle, &aFrecency, nullptr);
+    CacheFileIOManager::UpdateIndexEntry(mHandle, &aFrecency, nullptr, nullptr);
 
   return mMetadata->SetFrecency(aFrecency);
 }
 
 nsresult
 CacheFile::GetFrecency(uint32_t *_retval)
 {
   CacheFileAutoLock lock(this);
   MOZ_ASSERT(mMetadata);
   NS_ENSURE_TRUE(mMetadata, NS_ERROR_UNEXPECTED);
 
   return mMetadata->GetFrecency(_retval);
 }
 
 nsresult
+CacheFile::SetAltMetadata(const char* aAltMetadata)
+{
+  LOG(("CacheFile::SetAltMetadata() this=%p, aAltMetadata=%s",
+       this, aAltMetadata ? aAltMetadata : ""));
+
+  MOZ_ASSERT(mMetadata);
+  NS_ENSURE_TRUE(mMetadata, NS_ERROR_UNEXPECTED);
+
+  PostWriteTimer();
+
+  nsresult rv = mMetadata->SetElement(CacheFileUtils::kAltDataKey, aAltMetadata);
+  bool hasAltData = aAltMetadata ? true : false;
+
+  if (NS_FAILED(rv)) {
+    // Removing element shouldn't fail because it doesn't allocate memory.
+    mMetadata->SetElement(CacheFileUtils::kAltDataKey, nullptr);
+
+    mAltDataOffset = -1;
+    hasAltData = false;
+  }
+
+  if (mHandle && !mHandle->IsDoomed()) {
+    CacheFileIOManager::UpdateIndexEntry(mHandle, nullptr, nullptr, &hasAltData);
+  }
+  return rv;
+}
+
+nsresult
 CacheFile::GetLastModified(uint32_t *_retval)
 {
   CacheFileAutoLock lock(this);
   MOZ_ASSERT(mMetadata);
   NS_ENSURE_TRUE(mMetadata, NS_ERROR_UNEXPECTED);
 
   return mMetadata->GetLastModified(_retval);
 }
@@ -2360,17 +2386,19 @@ CacheFile::InitIndexEntry()
   NS_ENSURE_SUCCESS(rv, rv);
 
   uint32_t expTime;
   mMetadata->GetExpirationTime(&expTime);
 
   uint32_t frecency;
   mMetadata->GetFrecency(&frecency);
 
-  rv = CacheFileIOManager::UpdateIndexEntry(mHandle, &frecency, &expTime);
+  bool hasAltData = mMetadata->GetElement(CacheFileUtils::kAltDataKey) ? true : false;
+
+  rv = CacheFileIOManager::UpdateIndexEntry(mHandle, &frecency, &expTime, &hasAltData);
   NS_ENSURE_SUCCESS(rv, rv);
 
   return NS_OK;
 }
 
 size_t
 CacheFile::SizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf) const
 {
--- a/netwerk/cache2/CacheFile.h
+++ b/netwerk/cache2/CacheFile.h
@@ -181,16 +181,17 @@ private:
   void WriteMetadataIfNeededLocked(bool aFireAndForget = false);
   void PostWriteTimer();
 
   void CleanUpCachedChunks();
 
   nsresult PadChunkWithZeroes(uint32_t aChunkIdx);
 
   void SetError(nsresult aStatus);
+  nsresult SetAltMetadata(const char* aAltMetadata);
 
   nsresult InitIndexEntry();
 
   mozilla::Mutex mLock;
   bool           mOpeningFile;
   bool           mReady;
   bool           mMemoryOnly;
   bool           mSkipSizeCheck;
--- a/netwerk/cache2/CacheFileIOManager.cpp
+++ b/netwerk/cache2/CacheFileIOManager.cpp
@@ -973,44 +973,51 @@ public:
     CacheIndex::InitEntry(mHandle->Hash(), mOriginAttrsHash, mAnonymous,
                           mPinning);
 
     // We cannot set the filesize before we init the entry. If we're opening
     // an existing entry file, frecency and expiration time will be set after
     // parsing the entry file, but we must set the filesize here since nobody is
     // going to set it if there is no write to the file.
     uint32_t sizeInK = mHandle->FileSizeInK();
-    CacheIndex::UpdateEntry(mHandle->Hash(), nullptr, nullptr, &sizeInK);
+    CacheIndex::UpdateEntry(mHandle->Hash(), nullptr, nullptr, nullptr, &sizeInK);
 
     return NS_OK;
   }
 
 protected:
   RefPtr<CacheFileHandle> mHandle;
   OriginAttrsHash         mOriginAttrsHash;
   bool                    mAnonymous;
   bool                    mPinning;
 };
 
 class UpdateIndexEntryEvent : public Runnable {
 public:
-  UpdateIndexEntryEvent(CacheFileHandle *aHandle, const uint32_t *aFrecency,
-                        const uint32_t *aExpirationTime)
+  UpdateIndexEntryEvent(CacheFileHandle *aHandle,
+                        const uint32_t  *aFrecency,
+                        const uint32_t  *aExpirationTime,
+                        const bool      *aHasAltData)
     : mHandle(aHandle)
     , mHasFrecency(false)
     , mHasExpirationTime(false)
+    , mHasHasAltData(false)
   {
     if (aFrecency) {
       mHasFrecency = true;
       mFrecency = *aFrecency;
     }
     if (aExpirationTime) {
       mHasExpirationTime = true;
       mExpirationTime = *aExpirationTime;
     }
+    if (aHasAltData) {
+      mHasHasAltData = true;
+      mHasAltData = *aHasAltData;
+    }
   }
 
 protected:
   ~UpdateIndexEntryEvent()
   {
   }
 
 public:
@@ -1018,26 +1025,31 @@ public:
   {
     if (mHandle->IsClosed() || mHandle->IsDoomed()) {
       return NS_OK;
     }
 
     CacheIndex::UpdateEntry(mHandle->Hash(),
                             mHasFrecency ? &mFrecency : nullptr,
                             mHasExpirationTime ? &mExpirationTime : nullptr,
+                            mHasHasAltData ? &mHasAltData : nullptr,
                             nullptr);
     return NS_OK;
   }
 
 protected:
-  RefPtr<CacheFileHandle> mHandle;
+  RefPtr<CacheFileHandle>   mHandle;
+
   bool                      mHasFrecency;
   bool                      mHasExpirationTime;
+  bool                      mHasHasAltData;
+
   uint32_t                  mFrecency;
   uint32_t                  mExpirationTime;
+  bool                      mHasAltData;
 };
 
 class MetadataWriteScheduleEvent : public Runnable
 {
 public:
   enum EMode {
     SCHEDULE,
     UNSCHEDULE,
@@ -2040,17 +2052,17 @@ CacheFileIOManager::WriteInternal(CacheF
         aHandle->mFileSize = writeEnd;
       }
     }
 
     uint32_t newSizeInK = aHandle->FileSizeInK();
 
     if (oldSizeInK != newSizeInK && !aHandle->IsDoomed() &&
         !aHandle->IsSpecialFile()) {
-      CacheIndex::UpdateEntry(aHandle->Hash(), nullptr, nullptr, &newSizeInK);
+      CacheIndex::UpdateEntry(aHandle->Hash(), nullptr, nullptr, nullptr, &newSizeInK);
 
       if (oldSizeInK < newSizeInK) {
         EvictIfOverLimitInternal();
       }
     }
   }
 
   if (bytesWritten != aCount) {
@@ -2569,17 +2581,17 @@ CacheFileIOManager::TruncateSeekSetEOFIn
   }
 
   uint32_t oldSizeInK = aHandle->FileSizeInK();
   aHandle->mFileSize = aEOFPos;
   uint32_t newSizeInK = aHandle->FileSizeInK();
 
   if (oldSizeInK != newSizeInK && !aHandle->IsDoomed() &&
       !aHandle->IsSpecialFile()) {
-    CacheIndex::UpdateEntry(aHandle->Hash(), nullptr, nullptr, &newSizeInK);
+    CacheIndex::UpdateEntry(aHandle->Hash(), nullptr, nullptr, nullptr, &newSizeInK);
 
     if (oldSizeInK < newSizeInK) {
       EvictIfOverLimitInternal();
     }
   }
 
   return NS_OK;
 }
@@ -2874,17 +2886,17 @@ CacheFileIOManager::OverLimitEvictionInt
       // EnsureEntryExists().
       rv = CacheIndex::EnsureEntryExists(&hash);
       NS_ENSURE_SUCCESS(rv, rv);
 
       // Move the entry at the end of both lists to make sure we won't end up
       // failing on one entry forever.
       uint32_t frecency = 0;
       uint32_t expTime = nsICacheEntry::NO_EXPIRATION_TIME;
-      rv = CacheIndex::UpdateEntry(&hash, &frecency, &expTime, nullptr);
+      rv = CacheIndex::UpdateEntry(&hash, &frecency, &expTime, nullptr, nullptr);
       NS_ENSURE_SUCCESS(rv, rv);
 
       consecutiveFailures++;
       if (consecutiveFailures >= cnt) {
         // This doesn't necessarily mean that we've tried to doom every entry
         // but we've reached a sane number of tries. It is likely that another
         // eviction will start soon. And as said earlier, this normally doesn't
         // happen at all.
@@ -3538,36 +3550,38 @@ CacheFileIOManager::InitIndexEntry(Cache
 
   return NS_OK;
 }
 
 // static
 nsresult
 CacheFileIOManager::UpdateIndexEntry(CacheFileHandle *aHandle,
                                      const uint32_t  *aFrecency,
-                                     const uint32_t  *aExpirationTime)
+                                     const uint32_t  *aExpirationTime,
+                                     const bool      *aHasAltData)
 {
   LOG(("CacheFileIOManager::UpdateIndexEntry() [handle=%p, frecency=%s, "
-       "expirationTime=%s]", aHandle,
+       "expirationTime=%s hasAltData=%s]", aHandle,
        aFrecency ? nsPrintfCString("%u", *aFrecency).get() : "",
-       aExpirationTime ? nsPrintfCString("%u", *aExpirationTime).get() : ""));
+       aExpirationTime ? nsPrintfCString("%u", *aExpirationTime).get() : "",
+       aHasAltData ? (*aHasAltData ? "true" : "false") : ""));
 
   nsresult rv;
   RefPtr<CacheFileIOManager> ioMan = gInstance;
 
   if (aHandle->IsClosed() || !ioMan) {
     return NS_ERROR_NOT_INITIALIZED;
   }
 
   if (aHandle->IsSpecialFile()) {
     return NS_ERROR_UNEXPECTED;
   }
 
   RefPtr<UpdateIndexEntryEvent> ev =
-    new UpdateIndexEntryEvent(aHandle, aFrecency, aExpirationTime);
+    new UpdateIndexEntryEvent(aHandle, aFrecency, aExpirationTime, aHasAltData);
   rv = ioMan->mIOThread->Dispatch(ev, aHandle->mPriority
                                   ? CacheIOThread::WRITE_PRIORITY
                                   : CacheIOThread::WRITE);
   NS_ENSURE_SUCCESS(rv, rv);
 
   return NS_OK;
 }
 
--- a/netwerk/cache2/CacheFileIOManager.h
+++ b/netwerk/cache2/CacheFileIOManager.h
@@ -331,17 +331,18 @@ public:
                                  bool aPinning);
 
   static nsresult InitIndexEntry(CacheFileHandle *aHandle,
                                  OriginAttrsHash  aOriginAttrsHash,
                                  bool             aAnonymous,
                                  bool             aPinning);
   static nsresult UpdateIndexEntry(CacheFileHandle *aHandle,
                                    const uint32_t  *aFrecency,
-                                   const uint32_t  *aExpirationTime);
+                                   const uint32_t  *aExpirationTime,
+                                   const bool      *aHasAltData);
 
   static nsresult UpdateIndexEntry();
 
   enum EEnumerateMode {
     ENTRIES,
     DOOMED
   };
 
--- a/netwerk/cache2/CacheFileUtils.cpp
+++ b/netwerk/cache2/CacheFileUtils.cpp
@@ -545,17 +545,20 @@ ParseAlternativeDataInfo(const char *aIn
     return NS_ERROR_NOT_AVAILABLE;
   }
 
   // The requested alt-data representation is not available
   if (altDataOffset < 0) {
     return NS_ERROR_NOT_AVAILABLE;
   }
 
-  *_offset = altDataOffset;
+  if (_offset) {
+    *_offset = altDataOffset;
+  }
+
   if (_type) {
     mozilla::Unused << p.ReadUntil(Tokenizer::Token::EndOfFile(), *_type);
   }
 
   return NS_OK;
 }
 
 void
--- a/netwerk/cache2/CacheIndex.cpp
+++ b/netwerk/cache2/CacheIndex.cpp
@@ -22,17 +22,17 @@
 #include <algorithm>
 #include "mozilla/Telemetry.h"
 #include "mozilla/Unused.h"
 
 
 #define kMinUnwrittenChanges   300
 #define kMinDumpInterval       20000 // in milliseconds
 #define kMaxBufSize            16384
-#define kIndexVersion          0x00000003
+#define kIndexVersion          0x00000004
 #define kUpdateIndexStartDelay 50000 // in milliseconds
 
 #define INDEX_NAME      "index"
 #define TEMP_INDEX_NAME "index.tmp"
 #define JOURNAL_NAME    "index.log"
 
 namespace mozilla {
 namespace net {
@@ -912,22 +912,24 @@ CacheIndex::RemoveEntry(const SHA1Sum::H
   return NS_OK;
 }
 
 // static
 nsresult
 CacheIndex::UpdateEntry(const SHA1Sum::Hash *aHash,
                         const uint32_t      *aFrecency,
                         const uint32_t      *aExpirationTime,
+                        const bool          *aHasAltData,
                         const uint32_t      *aSize)
 {
   LOG(("CacheIndex::UpdateEntry() [hash=%08x%08x%08x%08x%08x, "
-       "frecency=%s, expirationTime=%s, size=%s]", LOGSHA1(aHash),
+       "frecency=%s, expirationTime=%s, hasAltData=%s, size=%s]", LOGSHA1(aHash),
        aFrecency ? nsPrintfCString("%u", *aFrecency).get() : "",
        aExpirationTime ? nsPrintfCString("%u", *aExpirationTime).get() : "",
+       aHasAltData ? (*aHasAltData ? "true" : "false") : "",
        aSize ? nsPrintfCString("%u", *aSize).get() : ""));
 
   MOZ_ASSERT(CacheFileIOManager::IsOnIOThread());
 
   StaticMutexAutoLock lock(sLock);
 
   RefPtr<CacheIndex> index = gInstance;
 
@@ -948,17 +950,17 @@ CacheIndex::UpdateEntry(const SHA1Sum::H
       entry = nullptr;
     }
 
     if (index->mState == READY || index->mState == UPDATING ||
         index->mState == BUILDING) {
       MOZ_ASSERT(index->mPendingUpdates.Count() == 0);
       MOZ_ASSERT(entry);
 
-      if (!HasEntryChanged(entry, aFrecency, aExpirationTime, aSize)) {
+      if (!HasEntryChanged(entry, aFrecency, aExpirationTime, aHasAltData, aSize)) {
         return NS_OK;
       }
 
       MOZ_ASSERT(entry->IsFresh());
       MOZ_ASSERT(entry->IsInitialized());
       entry->MarkDirty();
 
       if (aFrecency) {
@@ -1506,26 +1508,31 @@ CacheIndex::IsCollision(CacheIndexEntry 
   return false;
 }
 
 // static
 bool
 CacheIndex::HasEntryChanged(CacheIndexEntry *aEntry,
                             const uint32_t  *aFrecency,
                             const uint32_t  *aExpirationTime,
+                            const bool      *aHasAltData,
                             const uint32_t  *aSize)
 {
   if (aFrecency && *aFrecency != aEntry->GetFrecency()) {
     return true;
   }
 
   if (aExpirationTime && *aExpirationTime != aEntry->GetExpirationTime()) {
     return true;
   }
 
+  if (aHasAltData && *aHasAltData != aEntry->GetHasAltData()) {
+    return true;
+  }
+
   if (aSize &&
       (*aSize & CacheIndexEntry::kFileSizeMask) != aEntry->GetFileSize()) {
     return true;
   }
 
   return false;
 }
 
@@ -2631,17 +2638,17 @@ CacheIndex::SetupDirectoryEnumerator()
   NS_ENSURE_SUCCESS(rv, rv);
 
   mDirEnumerator = do_QueryInterface(enumerator, &rv);
   NS_ENSURE_SUCCESS(rv, rv);
 
   return NS_OK;
 }
 
-void
+nsresult
 CacheIndex::InitEntryFromDiskData(CacheIndexEntry *aEntry,
                                   CacheFileMetadata *aMetaData,
                                   int64_t aFileSize)
 {
   aEntry->InitNew();
   aEntry->MarkDirty();
   aEntry->MarkFresh();
 
@@ -2652,19 +2659,28 @@ CacheIndex::InitEntryFromDiskData(CacheI
   uint32_t expirationTime;
   aMetaData->GetExpirationTime(&expirationTime);
   aEntry->SetExpirationTime(expirationTime);
 
   uint32_t frecency;
   aMetaData->GetFrecency(&frecency);
   aEntry->SetFrecency(frecency);
 
+  const char *altData = aMetaData->GetElement(CacheFileUtils::kAltDataKey);
+  bool hasAltData = altData ? true : false;
+  if (hasAltData &&
+      NS_FAILED(CacheFileUtils::ParseAlternativeDataInfo(altData, nullptr, nullptr))) {
+    return NS_ERROR_FAILURE;
+  }
+  aEntry->SetHasAltData(hasAltData);
+
   aEntry->SetFileSize(static_cast<uint32_t>(
                         std::min(static_cast<int64_t>(PR_UINT32_MAX),
                                  (aFileSize + 0x3FF) >> 10)));
+  return NS_OK;
 }
 
 bool
 CacheIndex::IsUpdatePending()
 {
   sLock.AssertCurrentThreadOwns();
 
   if (mUpdateTimer || mUpdateEventPending) {
@@ -2797,20 +2813,25 @@ CacheIndex::BuildIndex()
 
     if (NS_FAILED(rv)) {
       LOG(("CacheIndex::BuildIndex() - CacheFileMetadata::SyncReadMetadata() "
            "failed, removing file. [name=%s]", leaf.get()));
       file->Remove(false);
     } else {
       CacheIndexEntryAutoManage entryMng(&hash, this);
       entry = mIndex.PutEntry(hash);
-      InitEntryFromDiskData(entry, meta, size);
-      LOG(("CacheIndex::BuildIndex() - Added entry to index. [hash=%s]",
-           leaf.get()));
-      entry->Log();
+      if (NS_FAILED(InitEntryFromDiskData(entry, meta, size))) {
+        LOG(("CacheIndex::BuildIndex() - CacheFile::InitEntryFromDiskData() "
+             "failed, removing file. [name=%s]", leaf.get()));
+        file->Remove(false);
+      } else {
+        LOG(("CacheIndex::BuildIndex() - Added entry to index. [name=%s]",
+             leaf.get()));
+        entry->Log();
+      }
     }
   }
 
   NS_NOTREACHED("We should never get here");
 }
 
 bool
 CacheIndex::StartUpdatingIndexIfNeeded(bool aSwitchingToReadyState)
@@ -3035,28 +3056,36 @@ CacheIndex::UpdateIndex()
     entry = mIndex.GetEntry(hash);
     MOZ_ASSERT(!entry || !entry->IsFresh());
 
     CacheIndexEntryAutoManage entryMng(&hash, this);
 
     if (NS_FAILED(rv)) {
       LOG(("CacheIndex::UpdateIndex() - CacheFileMetadata::SyncReadMetadata() "
            "failed, removing file. [name=%s]", leaf.get()));
+    } else {
+      entry = mIndex.PutEntry(hash);
+      rv = InitEntryFromDiskData(entry, meta, size);
+      if (NS_FAILED(rv)) {
+        LOG(("CacheIndex::UpdateIndex() - CacheIndex::InitEntryFromDiskData "
+             "failed, removing file. [name=%s]", leaf.get()));
+      }
+    }
+
+    if (NS_FAILED(rv)) {
       file->Remove(false);
       if (entry) {
         entry->MarkRemoved();
         entry->MarkFresh();
         entry->MarkDirty();
       }
     } else {
-      entry = mIndex.PutEntry(hash);
-      InitEntryFromDiskData(entry, meta, size);
-      LOG(("CacheIndex::UpdateIndex() - Added/updated entry to/in index. "
-           "[hash=%s]", leaf.get()));
-      entry->Log();
+        LOG(("CacheIndex::UpdateIndex() - Added/updated entry to/in index. "
+             "[name=%s]", leaf.get()));
+        entry->Log();
     }
   }
 
   NS_NOTREACHED("We should never get here");
 }
 
 void
 CacheIndex::FinishUpdate(bool aSucceeded)
--- a/netwerk/cache2/CacheIndex.h
+++ b/netwerk/cache2/CacheIndex.h
@@ -70,17 +70,18 @@ struct CacheIndexRecord {
 
   /*
    *    1000 0000 0000 0000 0000 0000 0000 0000 : initialized
    *    0100 0000 0000 0000 0000 0000 0000 0000 : anonymous
    *    0010 0000 0000 0000 0000 0000 0000 0000 : removed
    *    0001 0000 0000 0000 0000 0000 0000 0000 : dirty
    *    0000 1000 0000 0000 0000 0000 0000 0000 : fresh
    *    0000 0100 0000 0000 0000 0000 0000 0000 : pinned
-   *    0000 0011 0000 0000 0000 0000 0000 0000 : reserved
+   *    0000 0010 0000 0000 0000 0000 0000 0000 : has cached alt data
+   *    0000 0001 0000 0000 0000 0000 0000 0000 : reserved
    *    0000 0000 1111 1111 1111 1111 1111 1111 : file size (in kB)
    */
   uint32_t        mFlags;
 
   CacheIndexRecord()
     : mFrecency(0)
     , mOriginAttrsHash(0)
     , mExpirationTime(nsICacheEntry::NO_EXPIRATION_TIME)
@@ -203,16 +204,23 @@ public:
   uint32_t GetFrecency() const { return mRec->mFrecency; }
 
   void     SetExpirationTime(uint32_t aExpirationTime)
   {
     mRec->mExpirationTime = aExpirationTime;
   }
   uint32_t GetExpirationTime() const { return mRec->mExpirationTime; }
 
+  void     SetHasAltData(bool aHasAltData)
+  {
+    aHasAltData ? mRec->mFlags |= kHasAltDataMask
+                : mRec->mFlags &= ~kHasAltDataMask;
+  }
+  bool     GetHasAltData() const { return !!(mRec->mFlags & kHasAltDataMask); }
+
   // Sets filesize in kilobytes.
   void     SetFileSize(uint32_t aFileSize)
   {
     if (aFileSize > kFileSizeMask) {
       LOG(("CacheIndexEntry::SetFileSize() - FileSize is too large, "
            "truncating to %u", kFileSizeMask));
       aFileSize = kFileSizeMask;
     }
@@ -306,17 +314,19 @@ private:
   // This flag is set when the information about the entry is fresh, i.e.
   // we've created or opened this entry during this session, or we've seen
   // this entry during update or build process.
   static const uint32_t kFreshMask       = 0x08000000;
 
   // Indicates a pinned entry.
   static const uint32_t kPinnedMask      = 0x04000000;
 
-  static const uint32_t kReservedMask    = 0x03000000;
+  // Indicates there is cached alternative data in the entry.
+  static const uint32_t kHasAltDataMask = 0x02000000;
+  static const uint32_t kReservedMask    = 0x01000000;
 
   // FileSize in kilobytes
   static const uint32_t kFileSizeMask    = 0x00FFFFFF;
 
   nsAutoPtr<CacheIndexRecord> mRec;
 };
 
 class CacheIndexEntryUpdate : public CacheIndexEntry
@@ -358,45 +368,60 @@ public:
   }
 
   void SetExpirationTime(uint32_t aExpirationTime)
   {
     mUpdateFlags |= kExpirationUpdatedMask;
     CacheIndexEntry::SetExpirationTime(aExpirationTime);
   }
 
+  void SetHasAltData(bool aHasAltData)
+  {
+    mUpdateFlags |= kHasAltDataUpdatedMask;
+    CacheIndexEntry::SetHasAltData(aHasAltData);
+  }
+
   void SetFileSize(uint32_t aFileSize)
   {
     mUpdateFlags |= kFileSizeUpdatedMask;
     CacheIndexEntry::SetFileSize(aFileSize);
   }
 
   void ApplyUpdate(CacheIndexEntry *aDst) {
     MOZ_ASSERT(memcmp(&mRec->mHash, &aDst->mRec->mHash,
                sizeof(SHA1Sum::Hash)) == 0);
     if (mUpdateFlags & kFrecencyUpdatedMask) {
       aDst->mRec->mFrecency = mRec->mFrecency;
     }
     if (mUpdateFlags & kExpirationUpdatedMask) {
       aDst->mRec->mExpirationTime = mRec->mExpirationTime;
     }
     aDst->mRec->mOriginAttrsHash = mRec->mOriginAttrsHash;
+
+    if (mUpdateFlags & kHasAltDataUpdatedMask &&
+        ((aDst->mRec->mFlags ^ mRec->mFlags) & kHasAltDataMask)) {
+      // Toggle the bit if we need to.
+      aDst->mRec->mFlags ^= kHasAltDataMask;
+    }
+
     if (mUpdateFlags & kFileSizeUpdatedMask) {
-      aDst->mRec->mFlags = mRec->mFlags;
+      // Copy all flags except |HasAltData|.
+      aDst->mRec->mFlags |= (mRec->mFlags & ~kHasAltDataMask);
     } else {
-      // Copy all flags except file size.
+      // Copy all flags except |HasAltData| and file size.
       aDst->mRec->mFlags &= kFileSizeMask;
-      aDst->mRec->mFlags |= (mRec->mFlags & ~kFileSizeMask);
+      aDst->mRec->mFlags |= (mRec->mFlags & ~kHasAltDataMask & ~kFileSizeMask);
     }
   }
 
 private:
   static const uint32_t kFrecencyUpdatedMask = 0x00000001;
   static const uint32_t kExpirationUpdatedMask = 0x00000002;
   static const uint32_t kFileSizeUpdatedMask = 0x00000004;
+  static const uint32_t kHasAltDataUpdatedMask = 0x00000008;
 
   uint32_t mUpdateFlags;
 };
 
 class CacheIndexStats
 {
 public:
   CacheIndexStats()
@@ -627,16 +652,17 @@ public:
 
   // Update some information in entry. The entry MUST be present in index and
   // MUST be initialized. Call to AddEntry() or EnsureEntryExists() and to
   // InitEntry() must precede the call to this method.
   // Pass nullptr if the value didn't change.
   static nsresult UpdateEntry(const SHA1Sum::Hash *aHash,
                               const uint32_t      *aFrecency,
                               const uint32_t      *aExpirationTime,
+                              const bool          *aHasAltData,
                               const uint32_t      *aSize);
 
   // Remove all entries from the index. Called when clearing the whole cache.
   static nsresult RemoveAll();
 
   enum EntryStatus {
     EXISTS         = 0,
     DOES_NOT_EXIST = 1,
@@ -726,16 +752,17 @@ private:
   static bool IsCollision(CacheIndexEntry *aEntry,
                           OriginAttrsHash  aOriginAttrsHash,
                           bool             aAnonymous);
 
   // Checks whether any of the information about the entry has changed.
   static bool HasEntryChanged(CacheIndexEntry *aEntry,
                               const uint32_t  *aFrecency,
                               const uint32_t  *aExpirationTime,
+                              const bool      *aHasAltData,
                               const uint32_t  *aSize);
 
   // Merge all pending operations from mPendingUpdates into mIndex.
   void ProcessPendingOperations();
 
   // Following methods perform writing of the index file.
   //
   // The index is written periodically, but not earlier than once in
@@ -828,19 +855,19 @@ private:
 
   // Following methods perform updating and building of the index.
   // Timer callback that starts update or build process.
   static void DelayedUpdate(nsITimer *aTimer, void *aClosure);
   void DelayedUpdateLocked();
   // Posts timer event that start update or build process.
   nsresult ScheduleUpdateTimer(uint32_t aDelay);
   nsresult SetupDirectoryEnumerator();
-  void InitEntryFromDiskData(CacheIndexEntry *aEntry,
-                             CacheFileMetadata *aMetaData,
-                             int64_t aFileSize);
+  nsresult InitEntryFromDiskData(CacheIndexEntry *aEntry,
+                                 CacheFileMetadata *aMetaData,
+                                 int64_t aFileSize);
   // Returns true when either a timer is scheduled or event is posted.
   bool IsUpdatePending();
   // Iterates through all files in entries directory that we didn't create/open
   // during this session, parses them and adds the entries to the index.
   void BuildIndex();
 
   bool StartUpdatingIndexIfNeeded(bool aSwitchingToReadyState = false);
   // Starts update or build process or fires a timer when it is too early after