Bug 1325088 - Part 1: Add net-response-time-onstart/onstop to cache index. r=michal
authorJunior Hsu <juhsu@mozilla.com>
Tue, 28 Mar 2017 11:40:21 +0800
changeset 401124 913b6b05d4c1ddbdea5e593bd72d41bbdf39896b
parent 401123 58dfedd4fb41a53ccb1fb7114d374e8c24a1a183
child 401125 e7af85a4fd7b918bb4806418d72187c496262d48
push id1490
push usermtabara@mozilla.com
push dateMon, 31 Jul 2017 14:08:16 +0000
treeherdermozilla-release@70e32e6bf15e [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmichal
bugs1325088
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 1325088 - Part 1: Add net-response-time-onstart/onstop to cache index. r=michal
netwerk/cache2/CacheFile.cpp
netwerk/cache2/CacheFileIOManager.cpp
netwerk/cache2/CacheFileIOManager.h
netwerk/cache2/CacheIndex.cpp
netwerk/cache2/CacheIndex.h
--- a/netwerk/cache2/CacheFile.cpp
+++ b/netwerk/cache2/CacheFile.cpp
@@ -1149,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, nullptr);
+    CacheFileIOManager::UpdateIndexEntry(mHandle, nullptr, &aExpirationTime, nullptr, nullptr, nullptr);
 
   return mMetadata->SetExpirationTime(aExpirationTime);
 }
 
 nsresult
 CacheFile::GetExpirationTime(uint32_t *_retval)
 {
   CacheFileAutoLock lock(this);
@@ -1178,17 +1178,17 @@ 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, nullptr);
+    CacheFileIOManager::UpdateIndexEntry(mHandle, &aFrecency, nullptr, nullptr, nullptr, nullptr);
 
   return mMetadata->SetFrecency(aFrecency);
 }
 
 nsresult
 CacheFile::GetFrecency(uint32_t *_retval)
 {
   CacheFileAutoLock lock(this);
@@ -1216,17 +1216,17 @@ CacheFile::SetAltMetadata(const char* aA
     // 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);
+    CacheFileIOManager::UpdateIndexEntry(mHandle, nullptr, nullptr, &hasAltData, nullptr, nullptr);
   }
   return rv;
 }
 
 nsresult
 CacheFile::GetLastModified(uint32_t *_retval)
 {
   CacheFileAutoLock lock(this);
@@ -2388,17 +2388,33 @@ CacheFile::InitIndexEntry()
   uint32_t expTime;
   mMetadata->GetExpirationTime(&expTime);
 
   uint32_t frecency;
   mMetadata->GetFrecency(&frecency);
 
   bool hasAltData = mMetadata->GetElement(CacheFileUtils::kAltDataKey) ? true : false;
 
-  rv = CacheFileIOManager::UpdateIndexEntry(mHandle, &frecency, &expTime, &hasAltData);
+  static auto toUint16 = [](const char* s) -> uint16_t {
+    if (s) {
+      nsresult rv;
+      uint64_t n64 = nsCString(s).ToInteger64(&rv);
+      MOZ_ASSERT(NS_SUCCEEDED(rv));
+      return n64 <= kIndexTimeOutOfBound ? n64 : kIndexTimeOutOfBound ;
+    }
+    return kIndexTimeNotAvailable;
+  };
+
+  const char *onStartTimeStr = mMetadata->GetElement("net-response-time-onstart");
+  uint16_t onStartTime = toUint16(onStartTimeStr);
+
+  const char *onStopTimeStr = mMetadata->GetElement("net-response-time-onstop");
+  uint16_t onStopTime = toUint16(onStopTimeStr);
+
+  rv = CacheFileIOManager::UpdateIndexEntry(mHandle, &frecency, &expTime, &hasAltData, &onStartTime, &onStopTime);
   NS_ENSURE_SUCCESS(rv, rv);
 
   return NS_OK;
 }
 
 size_t
 CacheFile::SizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf) const
 {
--- a/netwerk/cache2/CacheFileIOManager.cpp
+++ b/netwerk/cache2/CacheFileIOManager.cpp
@@ -973,51 +973,64 @@ 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, nullptr, &sizeInK);
+    CacheIndex::UpdateEntry(mHandle->Hash(), nullptr, nullptr, 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,
-                        const bool      *aHasAltData)
+                        const bool      *aHasAltData,
+                        const uint16_t  *aOnStartTime,
+                        const uint16_t  *aOnStopTime)
     : mHandle(aHandle)
     , mHasFrecency(false)
     , mHasExpirationTime(false)
     , mHasHasAltData(false)
+    , mHasOnStartTime(false)
+    , mHasOnStopTime(false)
   {
     if (aFrecency) {
       mHasFrecency = true;
       mFrecency = *aFrecency;
     }
     if (aExpirationTime) {
       mHasExpirationTime = true;
       mExpirationTime = *aExpirationTime;
     }
     if (aHasAltData) {
       mHasHasAltData = true;
       mHasAltData = *aHasAltData;
     }
+    if (aOnStartTime) {
+      mHasOnStartTime = true;
+      mOnStartTime = *aOnStartTime;
+    }
+    if (aOnStopTime) {
+      mHasOnStopTime = true;
+      mOnStopTime = *aOnStopTime;
+    }
   }
 
 protected:
   ~UpdateIndexEntryEvent()
   {
   }
 
 public:
@@ -1026,30 +1039,36 @@ public:
     if (mHandle->IsClosed() || mHandle->IsDoomed()) {
       return NS_OK;
     }
 
     CacheIndex::UpdateEntry(mHandle->Hash(),
                             mHasFrecency ? &mFrecency : nullptr,
                             mHasExpirationTime ? &mExpirationTime : nullptr,
                             mHasHasAltData ? &mHasAltData : nullptr,
+                            mHasOnStartTime ? &mOnStartTime : nullptr,
+                            mHasOnStopTime ? &mOnStopTime : nullptr,
                             nullptr);
     return NS_OK;
   }
 
 protected:
   RefPtr<CacheFileHandle>   mHandle;
 
   bool                      mHasFrecency;
   bool                      mHasExpirationTime;
   bool                      mHasHasAltData;
+  bool                      mHasOnStartTime;
+  bool                      mHasOnStopTime;
 
   uint32_t                  mFrecency;
   uint32_t                  mExpirationTime;
   bool                      mHasAltData;
+  uint16_t                  mOnStartTime;
+  uint16_t                  mOnStopTime;
 };
 
 class MetadataWriteScheduleEvent : public Runnable
 {
 public:
   enum EMode {
     SCHEDULE,
     UNSCHEDULE,
@@ -2052,17 +2071,18 @@ CacheFileIOManager::WriteInternal(CacheF
         aHandle->mFileSize = writeEnd;
       }
     }
 
     uint32_t newSizeInK = aHandle->FileSizeInK();
 
     if (oldSizeInK != newSizeInK && !aHandle->IsDoomed() &&
         !aHandle->IsSpecialFile()) {
-      CacheIndex::UpdateEntry(aHandle->Hash(), nullptr, nullptr, nullptr, &newSizeInK);
+      CacheIndex::UpdateEntry(aHandle->Hash(), nullptr, nullptr, nullptr,
+                              nullptr, nullptr, &newSizeInK);
 
       if (oldSizeInK < newSizeInK) {
         EvictIfOverLimitInternal();
       }
     }
   }
 
   if (bytesWritten != aCount) {
@@ -2581,17 +2601,18 @@ 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, nullptr, &newSizeInK);
+    CacheIndex::UpdateEntry(aHandle->Hash(), nullptr, nullptr, nullptr, nullptr,
+                            nullptr, &newSizeInK);
 
     if (oldSizeInK < newSizeInK) {
       EvictIfOverLimitInternal();
     }
   }
 
   return NS_OK;
 }
@@ -2886,17 +2907,18 @@ 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, nullptr);
+      rv = CacheIndex::UpdateEntry(&hash, &frecency, &expTime, nullptr, nullptr,
+                                   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.
@@ -3551,37 +3573,42 @@ CacheFileIOManager::InitIndexEntry(Cache
   return NS_OK;
 }
 
 // static
 nsresult
 CacheFileIOManager::UpdateIndexEntry(CacheFileHandle *aHandle,
                                      const uint32_t  *aFrecency,
                                      const uint32_t  *aExpirationTime,
-                                     const bool      *aHasAltData)
+                                     const bool      *aHasAltData,
+                                     const uint16_t  *aOnStartTime,
+                                     const uint16_t  *aOnStopTime)
 {
   LOG(("CacheFileIOManager::UpdateIndexEntry() [handle=%p, frecency=%s, "
-       "expirationTime=%s hasAltData=%s]", aHandle,
+       "expirationTime=%s, hasAltData=%s, onStartTime=%s, onStopTime=%s]", aHandle,
        aFrecency ? nsPrintfCString("%u", *aFrecency).get() : "",
        aExpirationTime ? nsPrintfCString("%u", *aExpirationTime).get() : "",
-       aHasAltData ? (*aHasAltData ? "true" : "false") : ""));
+       aHasAltData ? (*aHasAltData ? "true" : "false") : "",
+       aOnStartTime ? nsPrintfCString("%u", *aOnStartTime).get() : "",
+       aOnStopTime ? nsPrintfCString("%u", *aOnStopTime).get() : ""));
 
   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, aHasAltData);
+    new UpdateIndexEntryEvent(aHandle, aFrecency, aExpirationTime, aHasAltData,
+                              aOnStartTime, aOnStopTime);
   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
@@ -332,17 +332,19 @@ public:
 
   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 bool      *aHasAltData);
+                                   const bool      *aHasAltData,
+                                   const uint16_t  *aOnStartTime,
+                                   const uint16_t  *aOnStopTime);
 
   static nsresult UpdateIndexEntry();
 
   enum EEnumerateMode {
     ENTRIES,
     DOOMED
   };
 
--- 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          0x00000004
+#define kIndexVersion          0x00000005
 #define kUpdateIndexStartDelay 50000 // in milliseconds
 
 #define INDEX_NAME      "index"
 #define TEMP_INDEX_NAME "index.tmp"
 #define JOURNAL_NAME    "index.log"
 
 namespace mozilla {
 namespace net {
@@ -913,23 +913,28 @@ CacheIndex::RemoveEntry(const SHA1Sum::H
 }
 
 // static
 nsresult
 CacheIndex::UpdateEntry(const SHA1Sum::Hash *aHash,
                         const uint32_t      *aFrecency,
                         const uint32_t      *aExpirationTime,
                         const bool          *aHasAltData,
+                        const uint16_t      *aOnStartTime,
+                        const uint16_t      *aOnStopTime,
                         const uint32_t      *aSize)
 {
   LOG(("CacheIndex::UpdateEntry() [hash=%08x%08x%08x%08x%08x, "
-       "frecency=%s, expirationTime=%s, hasAltData=%s, size=%s]", LOGSHA1(aHash),
+       "frecency=%s, expirationTime=%s, hasAltData=%s, onStartTime=%s, "
+       "onStopTime=%s, size=%s]", LOGSHA1(aHash),
        aFrecency ? nsPrintfCString("%u", *aFrecency).get() : "",
        aExpirationTime ? nsPrintfCString("%u", *aExpirationTime).get() : "",
        aHasAltData ? (*aHasAltData ? "true" : "false") : "",
+       aOnStartTime ? nsPrintfCString("%u", *aOnStartTime).get() : "",
+       aOnStopTime ? nsPrintfCString("%u", *aOnStopTime).get() : "",
        aSize ? nsPrintfCString("%u", *aSize).get() : ""));
 
   MOZ_ASSERT(CacheFileIOManager::IsOnIOThread());
 
   StaticMutexAutoLock lock(sLock);
 
   RefPtr<CacheIndex> index = gInstance;
 
@@ -950,32 +955,45 @@ 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, aHasAltData, aSize)) {
+      if (!HasEntryChanged(entry, aFrecency, aExpirationTime, aHasAltData,
+                           aOnStartTime, aOnStopTime, aSize)) {
         return NS_OK;
       }
 
       MOZ_ASSERT(entry->IsFresh());
       MOZ_ASSERT(entry->IsInitialized());
       entry->MarkDirty();
 
       if (aFrecency) {
         entry->SetFrecency(*aFrecency);
       }
 
       if (aExpirationTime) {
         entry->SetExpirationTime(*aExpirationTime);
       }
 
+      if (aHasAltData) {
+        entry->SetHasAltData(*aHasAltData);
+      }
+
+      if (aOnStartTime) {
+        entry->SetOnStartTime(*aOnStartTime);
+      }
+
+      if (aOnStopTime) {
+        entry->SetOnStopTime(*aOnStopTime);
+      }
+
       if (aSize) {
         entry->SetFileSize(*aSize);
       }
     } else {
       CacheIndexEntryUpdate *updated = index->mPendingUpdates.GetEntry(*aHash);
       DebugOnly<bool> removed = updated && updated->IsRemoved();
 
       MOZ_ASSERT(updated || !removed);
@@ -1002,16 +1020,28 @@ CacheIndex::UpdateEntry(const SHA1Sum::H
       if (aFrecency) {
         updated->SetFrecency(*aFrecency);
       }
 
       if (aExpirationTime) {
         updated->SetExpirationTime(*aExpirationTime);
       }
 
+      if (aHasAltData) {
+        updated->SetHasAltData(*aHasAltData);
+      }
+
+      if (aOnStartTime) {
+        updated->SetOnStartTime(*aOnStartTime);
+      }
+
+      if (aOnStopTime) {
+        updated->SetOnStopTime(*aOnStopTime);
+      }
+
       if (aSize) {
         updated->SetFileSize(*aSize);
       }
     }
   }
 
   index->WriteIndexToDiskIfNeeded();
 
@@ -1509,30 +1539,40 @@ CacheIndex::IsCollision(CacheIndexEntry 
 }
 
 // static
 bool
 CacheIndex::HasEntryChanged(CacheIndexEntry *aEntry,
                             const uint32_t  *aFrecency,
                             const uint32_t  *aExpirationTime,
                             const bool      *aHasAltData,
+                            const uint16_t  *aOnStartTime,
+                            const uint16_t  *aOnStopTime,
                             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 (aOnStartTime && *aOnStartTime != aEntry->GetOnStartTime()) {
+    return true;
+  }
+
+  if (aOnStopTime && *aOnStopTime != aEntry->GetOnStopTime()) {
+    return true;
+  }
+
   if (aSize &&
       (*aSize & CacheIndexEntry::kFileSizeMask) != aEntry->GetFileSize()) {
     return true;
   }
 
   return false;
 }
 
@@ -2667,16 +2707,30 @@ CacheIndex::InitEntryFromDiskData(CacheI
   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);
 
+  static auto getUint16MetaData = [&aMetaData](const char *key) -> uint16_t {
+    const char* s64 = aMetaData->GetElement(key);
+    if (s64) {
+      nsresult rv;
+      uint64_t n64 = nsCString(s64).ToInteger64(&rv);
+      MOZ_ASSERT(NS_SUCCEEDED(rv));
+      return n64 <= kIndexTimeOutOfBound ? n64 : kIndexTimeOutOfBound;
+    }
+    return kIndexTimeNotAvailable;
+  };
+
+  aEntry->SetOnStartTime(getUint16MetaData("net-response-time-onstart"));
+  aEntry->SetOnStopTime(getUint16MetaData("net-response-time-onstop"));
+
   aEntry->SetFileSize(static_cast<uint32_t>(
                         std::min(static_cast<int64_t>(PR_UINT32_MAX),
                                  (aFileSize + 0x3FF) >> 10)));
   return NS_OK;
 }
 
 bool
 CacheIndex::IsUpdatePending()
--- a/netwerk/cache2/CacheIndex.h
+++ b/netwerk/cache2/CacheIndex.h
@@ -33,16 +33,19 @@ class nsITimer;
 
 namespace mozilla {
 namespace net {
 
 class CacheFileMetadata;
 class FileOpenHelper;
 class CacheIndexIterator;
 
+const uint16_t kIndexTimeNotAvailable = 0xFFFFU;
+const uint16_t kIndexTimeOutOfBound = 0xFFFEU;
+
 typedef struct {
   // Version of the index. The index must be ignored and deleted when the file
   // on disk was written with a newer version.
   uint32_t mVersion;
 
   // Timestamp of time when the last successful write of the index started.
   // During update process we use this timestamp for a quick validation of entry
   // files. If last modified time of the file is lower than this timestamp, we
@@ -57,21 +60,24 @@ typedef struct {
   uint32_t mIsDirty;
 } CacheIndexHeader;
 
 static_assert(
   sizeof(CacheIndexHeader::mVersion) + sizeof(CacheIndexHeader::mTimeStamp) +
   sizeof(CacheIndexHeader::mIsDirty) == sizeof(CacheIndexHeader),
   "Unexpected sizeof(CacheIndexHeader)!");
 
+#pragma pack(push, 4)
 struct CacheIndexRecord {
   SHA1Sum::Hash   mHash;
   uint32_t        mFrecency;
   OriginAttrsHash mOriginAttrsHash;
   uint32_t        mExpirationTime;
+  uint16_t        mOnStartTime;
+  uint16_t        mOnStopTime;
 
   /*
    *    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
@@ -80,23 +86,27 @@ struct CacheIndexRecord {
    *    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)
+    , mOnStartTime(kIndexTimeNotAvailable)
+    , mOnStopTime(kIndexTimeNotAvailable)
     , mFlags(0)
   {}
 };
+#pragma pack(pop)
 
 static_assert(
   sizeof(CacheIndexRecord::mHash) + sizeof(CacheIndexRecord::mFrecency) +
   sizeof(CacheIndexRecord::mOriginAttrsHash) + sizeof(CacheIndexRecord::mExpirationTime) +
+  sizeof(CacheIndexRecord::mOnStartTime) + sizeof(CacheIndexRecord::mOnStopTime) +
   sizeof(CacheIndexRecord::mFlags) == sizeof(CacheIndexRecord),
   "Unexpected sizeof(CacheIndexRecord)!");
 
 class CacheIndexEntry : public PLDHashEntryHdr
 {
 public:
   typedef const SHA1Sum::Hash& KeyType;
   typedef const SHA1Sum::Hash* KeyTypePointer;
@@ -145,33 +155,39 @@ public:
 
   CacheIndexEntry& operator=(const CacheIndexEntry& aOther)
   {
     MOZ_ASSERT(memcmp(&mRec->mHash, &aOther.mRec->mHash,
                sizeof(SHA1Sum::Hash)) == 0);
     mRec->mFrecency = aOther.mRec->mFrecency;
     mRec->mExpirationTime = aOther.mRec->mExpirationTime;
     mRec->mOriginAttrsHash = aOther.mRec->mOriginAttrsHash;
+    mRec->mOnStartTime = aOther.mRec->mOnStartTime;
+    mRec->mOnStopTime = aOther.mRec->mOnStopTime;
     mRec->mFlags = aOther.mRec->mFlags;
     return *this;
   }
 
   void InitNew()
   {
     mRec->mFrecency = 0;
     mRec->mExpirationTime = nsICacheEntry::NO_EXPIRATION_TIME;
     mRec->mOriginAttrsHash = 0;
+    mRec->mOnStartTime = kIndexTimeNotAvailable;
+    mRec->mOnStopTime = kIndexTimeNotAvailable;
     mRec->mFlags = 0;
   }
 
   void Init(OriginAttrsHash aOriginAttrsHash, bool aAnonymous, bool aPinned)
   {
     MOZ_ASSERT(mRec->mFrecency == 0);
     MOZ_ASSERT(mRec->mExpirationTime == nsICacheEntry::NO_EXPIRATION_TIME);
     MOZ_ASSERT(mRec->mOriginAttrsHash == 0);
+    MOZ_ASSERT(mRec->mOnStartTime == kIndexTimeNotAvailable);
+    MOZ_ASSERT(mRec->mOnStopTime == kIndexTimeNotAvailable);
     // When we init the entry it must be fresh and may be dirty
     MOZ_ASSERT((mRec->mFlags & ~kDirtyMask) == kFreshMask);
 
     mRec->mOriginAttrsHash = aOriginAttrsHash;
     mRec->mFlags |= kInitializedMask;
     if (aAnonymous) {
       mRec->mFlags |= kAnonymousMask;
     }
@@ -211,16 +227,28 @@ public:
 
   void     SetHasAltData(bool aHasAltData)
   {
     aHasAltData ? mRec->mFlags |= kHasAltDataMask
                 : mRec->mFlags &= ~kHasAltDataMask;
   }
   bool     GetHasAltData() const { return !!(mRec->mFlags & kHasAltDataMask); }
 
+  void     SetOnStartTime(uint16_t aTime)
+  {
+    mRec->mOnStartTime = aTime;
+  }
+  uint16_t  GetOnStartTime() const { return mRec->mOnStartTime; }
+
+  void     SetOnStopTime(uint16_t aTime)
+  {
+    mRec->mOnStopTime = aTime;
+  }
+  uint16_t  GetOnStopTime() const { return mRec->mOnStopTime; }
+
   // 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;
     }
@@ -241,38 +269,44 @@ public:
 
   void WriteToBuf(void *aBuf)
   {
     uint8_t* ptr = static_cast<uint8_t*>(aBuf);
     memcpy(ptr, mRec->mHash, sizeof(SHA1Sum::Hash)); ptr += sizeof(SHA1Sum::Hash);
     NetworkEndian::writeUint32(ptr, mRec->mFrecency); ptr += sizeof(uint32_t);
     NetworkEndian::writeUint64(ptr, mRec->mOriginAttrsHash); ptr += sizeof(uint64_t);
     NetworkEndian::writeUint32(ptr, mRec->mExpirationTime); ptr += sizeof(uint32_t);
+    NetworkEndian::writeUint16(ptr, mRec->mOnStartTime); ptr += sizeof(uint16_t);
+    NetworkEndian::writeUint16(ptr, mRec->mOnStopTime); ptr += sizeof(uint16_t);
     // Dirty and fresh flags should never go to disk, since they make sense only
     // during current session.
     NetworkEndian::writeUint32(ptr, mRec->mFlags & ~(kDirtyMask | kFreshMask));
   }
 
   void ReadFromBuf(void *aBuf)
   {
     const uint8_t* ptr = static_cast<const uint8_t*>(aBuf);
     MOZ_ASSERT(memcmp(&mRec->mHash, ptr, sizeof(SHA1Sum::Hash)) == 0); ptr += sizeof(SHA1Sum::Hash);
     mRec->mFrecency = NetworkEndian::readUint32(ptr); ptr += sizeof(uint32_t);
     mRec->mOriginAttrsHash = NetworkEndian::readUint64(ptr); ptr += sizeof(uint64_t);
     mRec->mExpirationTime = NetworkEndian::readUint32(ptr); ptr += sizeof(uint32_t);
+    mRec->mOnStartTime = NetworkEndian::readUint16(ptr); ptr += sizeof(uint16_t);
+    mRec->mOnStopTime = NetworkEndian::readUint16(ptr); ptr += sizeof(uint16_t);
     mRec->mFlags = NetworkEndian::readUint32(ptr);
   }
 
   void Log() const {
     LOG(("CacheIndexEntry::Log() [this=%p, hash=%08x%08x%08x%08x%08x, fresh=%u,"
          " initialized=%u, removed=%u, dirty=%u, anonymous=%u, "
-         "originAttrsHash=%" PRIx64 ", frecency=%u, expirationTime=%u, size=%u]",
+         "originAttrsHash=%" PRIx64 ", frecency=%u, expirationTime=%u, "
+         "hasAltData=%u, onStartTime=%u, onStopTime=%u, size=%u]",
          this, LOGSHA1(mRec->mHash), IsFresh(), IsInitialized(), IsRemoved(),
          IsDirty(), Anonymous(), OriginAttrsHash(), GetFrecency(),
-         GetExpirationTime(), GetFileSize()));
+         GetExpirationTime(), GetHasAltData(), GetOnStartTime(),
+         GetOnStopTime(), GetFileSize()));
   }
 
   static bool RecordMatchesLoadContextInfo(CacheIndexRecord *aRec,
                                            nsILoadContextInfo *aInfo)
   {
     MOZ_ASSERT(aInfo);
 
     if (!aInfo->IsPrivate() &&
@@ -352,17 +386,18 @@ public:
     mUpdateFlags = 0;
     *(static_cast<CacheIndexEntry *>(this)) = aOther;
     return *this;
   }
 
   void InitNew()
   {
     mUpdateFlags = kFrecencyUpdatedMask | kExpirationUpdatedMask |
-                   kFileSizeUpdatedMask;
+                   kHasAltDataUpdatedMask | kOnStartTimeUpdatedMask |
+                   kOnStopTimeUpdatedMask | kFileSizeUpdatedMask;
     CacheIndexEntry::InitNew();
   }
 
   void SetFrecency(uint32_t aFrecency)
   {
     mUpdateFlags |= kFrecencyUpdatedMask;
     CacheIndexEntry::SetFrecency(aFrecency);
   }
@@ -374,33 +409,50 @@ public:
   }
 
   void SetHasAltData(bool aHasAltData)
   {
     mUpdateFlags |= kHasAltDataUpdatedMask;
     CacheIndexEntry::SetHasAltData(aHasAltData);
   }
 
+  void SetOnStartTime(uint16_t aTime)
+  {
+    mUpdateFlags |= kOnStartTimeUpdatedMask;
+    CacheIndexEntry::SetOnStartTime(aTime);
+  }
+
+  void SetOnStopTime(uint16_t aTime)
+  {
+    mUpdateFlags |= kOnStopTimeUpdatedMask;
+    CacheIndexEntry::SetOnStopTime(aTime);
+  }
+
   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 & kOnStartTimeUpdatedMask) {
+      aDst->mRec->mOnStartTime = mRec->mOnStartTime;
+    }
+    if (mUpdateFlags & kOnStopTimeUpdatedMask) {
+      aDst->mRec->mOnStopTime = mRec->mOnStopTime;
+    }
     if (mUpdateFlags & kHasAltDataUpdatedMask &&
         ((aDst->mRec->mFlags ^ mRec->mFlags) & kHasAltDataMask)) {
       // Toggle the bit if we need to.
       aDst->mRec->mFlags ^= kHasAltDataMask;
     }
 
     if (mUpdateFlags & kFileSizeUpdatedMask) {
       // Copy all flags except |HasAltData|.
@@ -412,16 +464,18 @@ public:
     }
   }
 
 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;
+  static const uint32_t kOnStartTimeUpdatedMask = 0x00000010;
+  static const uint32_t kOnStopTimeUpdatedMask = 0x00000020;
 
   uint32_t mUpdateFlags;
 };
 
 class CacheIndexStats
 {
 public:
   CacheIndexStats()
@@ -653,16 +707,18 @@ 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 uint16_t      *aOnStartTime,
+                              const uint16_t      *aOnStopTime,
                               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,
@@ -753,16 +809,18 @@ private:
                           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 uint16_t  *aOnStartTime,
+                              const uint16_t  *aOnStopTime,
                               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