Bug 1290481 - P3: Record padding size into cache.sqlite. r=bkelly
authorTom Tung <shes050117@gmail.com>
Mon, 10 Jul 2017 16:54:20 +0800
changeset 429023 3b635d9ede1b2bb834abd31a0a81ead928695bdf
parent 429022 d21a58f0e34192f3f65369846c3e674c5b524655
child 429024 8c60914e10e9595831071bcb6e02de39a90b1e96
push id7761
push userjlund@mozilla.com
push dateFri, 15 Sep 2017 00:19:52 +0000
treeherdermozilla-beta@c38455951db4 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersbkelly
bugs1290481
milestone57.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 1290481 - P3: Record padding size into cache.sqlite. r=bkelly MozReview-Commit-ID: 6poDeyErBjc
dom/cache/DBSchema.cpp
dom/cache/DBSchema.h
dom/cache/Manager.cpp
--- a/dom/cache/DBSchema.cpp
+++ b/dom/cache/DBSchema.cpp
@@ -5,16 +5,17 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "mozilla/dom/cache/DBSchema.h"
 
 #include "ipc/IPCMessageUtils.h"
 #include "mozilla/BasePrincipal.h"
 #include "mozilla/dom/HeadersBinding.h"
 #include "mozilla/dom/InternalHeaders.h"
+#include "mozilla/dom/InternalResponse.h"
 #include "mozilla/dom/RequestBinding.h"
 #include "mozilla/dom/ResponseBinding.h"
 #include "mozilla/dom/cache/CacheTypes.h"
 #include "mozilla/dom/cache/SavedTypes.h"
 #include "mozilla/dom/cache/Types.h"
 #include "mozilla/dom/cache/TypeUtils.h"
 #include "mozIStorageConnection.h"
 #include "mozIStorageStatement.h"
@@ -315,16 +316,17 @@ static nsresult QueryCache(mozIStorageCo
                            uint32_t aMaxResults = UINT32_MAX);
 static nsresult MatchByVaryHeader(mozIStorageConnection* aConn,
                                   const CacheRequest& aRequest,
                                   EntryId entryId, bool* aSuccessOut);
 static nsresult DeleteEntries(mozIStorageConnection* aConn,
                               const nsTArray<EntryId>& aEntryIdList,
                               nsTArray<nsID>& aDeletedBodyIdListOut,
                               nsTArray<IdCount>& aDeletedSecurityIdListOut,
+                              int64_t* aDeletedPaddingSizeOut,
                               uint32_t aPos=0, int32_t aLen=-1);
 static nsresult InsertSecurityInfo(mozIStorageConnection* aConn,
                                    nsICryptoHash* aCrypto,
                                    const nsACString& aData, int32_t *aIdOut);
 static nsresult DeleteSecurityInfo(mozIStorageConnection* aConn, int32_t aId,
                                    int32_t aCount);
 static nsresult DeleteSecurityInfoList(mozIStorageConnection* aConn,
                                        const nsTArray<IdCount>& aDeletedStorageIdList);
@@ -594,33 +596,38 @@ CreateCacheId(mozIStorageConnection* aCo
   rv = state->GetInt64(0, aCacheIdOut);
   if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
 
   return rv;
 }
 
 nsresult
 DeleteCacheId(mozIStorageConnection* aConn, CacheId aCacheId,
-              nsTArray<nsID>& aDeletedBodyIdListOut)
+              nsTArray<nsID>& aDeletedBodyIdListOut,
+              int64_t* aDeletedPaddingSizeOut)
 {
   MOZ_ASSERT(!NS_IsMainThread());
   MOZ_DIAGNOSTIC_ASSERT(aConn);
+  MOZ_DIAGNOSTIC_ASSERT(aDeletedPaddingSizeOut);
 
   // Delete the bodies explicitly as we need to read out the body IDs
   // anyway.  These body IDs must be deleted one-by-one as content may
   // still be referencing them invidivually.
   AutoTArray<EntryId, 256> matches;
   nsresult rv = QueryAll(aConn, aCacheId, matches);
   if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
 
   AutoTArray<IdCount, 16> deletedSecurityIdList;
+  int64_t deletedPaddingSize = 0;
   rv = DeleteEntries(aConn, matches, aDeletedBodyIdListOut,
-                     deletedSecurityIdList);
+                     deletedSecurityIdList, &deletedPaddingSize);
   if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
 
+  *aDeletedPaddingSizeOut = deletedPaddingSize;
+
   rv = DeleteSecurityInfoList(aConn, deletedSecurityIdList);
   if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
 
   // Delete the remainder of the cache using cascade semantics.
   nsCOMPtr<mozIStorageStatement> state;
   rv = aConn->CreateStatement(NS_LITERAL_CSTRING(
     "DELETE FROM caches WHERE id=:id;"
   ), getter_AddRefs(state));
@@ -788,69 +795,79 @@ CacheMatchAll(mozIStorageConnection* aCo
 }
 
 nsresult
 CachePut(mozIStorageConnection* aConn, CacheId aCacheId,
          const CacheRequest& aRequest,
          const nsID* aRequestBodyId,
          const CacheResponse& aResponse,
          const nsID* aResponseBodyId,
-         nsTArray<nsID>& aDeletedBodyIdListOut)
+         nsTArray<nsID>& aDeletedBodyIdListOut,
+         int64_t* aDeletedPaddingSizeOut)
 {
   MOZ_ASSERT(!NS_IsMainThread());
   MOZ_DIAGNOSTIC_ASSERT(aConn);
+  MOZ_DIAGNOSTIC_ASSERT(aDeletedPaddingSizeOut);
 
   CacheQueryParams params(false, false, false, false,
                            NS_LITERAL_STRING(""));
   AutoTArray<EntryId, 256> matches;
   nsresult rv = QueryCache(aConn, aCacheId, aRequest, params, matches);
   if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
 
   AutoTArray<IdCount, 16> deletedSecurityIdList;
+  int64_t deletedPaddingSize = 0;
   rv = DeleteEntries(aConn, matches, aDeletedBodyIdListOut,
-                     deletedSecurityIdList);
+                     deletedSecurityIdList, &deletedPaddingSize);
   if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
 
   rv = InsertEntry(aConn, aCacheId, aRequest, aRequestBodyId, aResponse,
                    aResponseBodyId);
   if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
 
   // Delete the security values after doing the insert to avoid churning
   // the security table when its not necessary.
   rv = DeleteSecurityInfoList(aConn, deletedSecurityIdList);
   if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
 
+  *aDeletedPaddingSizeOut = deletedPaddingSize;
+
   return rv;
 }
 
 nsresult
 CacheDelete(mozIStorageConnection* aConn, CacheId aCacheId,
             const CacheRequest& aRequest,
             const CacheQueryParams& aParams,
-            nsTArray<nsID>& aDeletedBodyIdListOut, bool* aSuccessOut)
+            nsTArray<nsID>& aDeletedBodyIdListOut,
+            int64_t* aDeletedPaddingSizeOut, bool* aSuccessOut)
 {
   MOZ_ASSERT(!NS_IsMainThread());
   MOZ_DIAGNOSTIC_ASSERT(aConn);
+  MOZ_DIAGNOSTIC_ASSERT(aDeletedPaddingSizeOut);
   MOZ_DIAGNOSTIC_ASSERT(aSuccessOut);
 
   *aSuccessOut = false;
 
   AutoTArray<EntryId, 256> matches;
   nsresult rv = QueryCache(aConn, aCacheId, aRequest, aParams, matches);
   if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
 
   if (matches.IsEmpty()) {
     return rv;
   }
 
   AutoTArray<IdCount, 16> deletedSecurityIdList;
+  int64_t deletedPaddingSize = 0;
   rv = DeleteEntries(aConn, matches, aDeletedBodyIdListOut,
-                     deletedSecurityIdList);
+                     deletedSecurityIdList, &deletedPaddingSize);
   if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
 
+  *aDeletedPaddingSizeOut = deletedPaddingSize;
+
   rv = DeleteSecurityInfoList(aConn, deletedSecurityIdList);
   if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
 
   *aSuccessOut = true;
 
   return rv;
 }
 
@@ -1329,63 +1346,78 @@ MatchByVaryHeader(mozIStorageConnection*
   return rv;
 }
 
 nsresult
 DeleteEntries(mozIStorageConnection* aConn,
               const nsTArray<EntryId>& aEntryIdList,
               nsTArray<nsID>& aDeletedBodyIdListOut,
               nsTArray<IdCount>& aDeletedSecurityIdListOut,
+              int64_t* aDeletedPaddingSizeOut,
               uint32_t aPos, int32_t aLen)
 {
   MOZ_ASSERT(!NS_IsMainThread());
   MOZ_DIAGNOSTIC_ASSERT(aConn);
+  MOZ_DIAGNOSTIC_ASSERT(aDeletedPaddingSizeOut);
 
   if (aEntryIdList.IsEmpty()) {
     return NS_OK;
   }
 
   MOZ_DIAGNOSTIC_ASSERT(aPos < aEntryIdList.Length());
 
   if (aLen < 0) {
     aLen = aEntryIdList.Length() - aPos;
   }
 
   // Sqlite limits the number of entries allowed for an IN clause,
   // so split up larger operations.
   if (aLen > kMaxEntriesPerStatement) {
+    int64_t overallDeletedPaddingSize = 0;
     uint32_t curPos = aPos;
     int32_t remaining = aLen;
     while (remaining > 0) {
+      int64_t deletedPaddingSize = 0;
       int32_t max = kMaxEntriesPerStatement;
       int32_t curLen = std::min(max, remaining);
       nsresult rv = DeleteEntries(aConn, aEntryIdList, aDeletedBodyIdListOut,
-                                  aDeletedSecurityIdListOut, curPos, curLen);
+                                  aDeletedSecurityIdListOut,
+                                  &deletedPaddingSize, curPos, curLen);
       if (NS_FAILED(rv)) { return rv; }
 
+      MOZ_DIAGNOSTIC_ASSERT(INT64_MAX - deletedPaddingSize >=
+                            overallDeletedPaddingSize);
+      overallDeletedPaddingSize += deletedPaddingSize;
       curPos += curLen;
       remaining -= curLen;
     }
+
+    *aDeletedPaddingSizeOut += overallDeletedPaddingSize;
     return NS_OK;
   }
 
   nsCOMPtr<mozIStorageStatement> state;
   nsAutoCString query(
-    "SELECT request_body_id, response_body_id, response_security_info_id "
+    "SELECT "
+      "request_body_id, "
+      "response_body_id, "
+      "response_security_info_id, "
+      "response_padding_size "
     "FROM entries WHERE id IN ("
   );
   AppendListParamsToQuery(query, aEntryIdList, aPos, aLen);
   query.AppendLiteral(")");
 
   nsresult rv = aConn->CreateStatement(query, getter_AddRefs(state));
   if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
 
   rv = BindListParamsToQuery(state, aEntryIdList, aPos, aLen);
   if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
 
+  int64_t overallPaddingSize = 0;
   bool hasMoreData = false;
   while (NS_SUCCEEDED(state->ExecuteStep(&hasMoreData)) && hasMoreData) {
     // extract 0 to 2 nsID structs per row
     for (uint32_t i = 0; i < 2; ++i) {
       bool isNull = false;
 
       rv = state->GetIsNull(i, &isNull);
       if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
@@ -1419,18 +1451,34 @@ DeleteEntries(mozIStorageConnection* aCo
         }
       }
 
       // Otherwise add a new entry for this ID with a count of 1
       if (!found) {
         aDeletedSecurityIdListOut.AppendElement(IdCount(securityId));
       }
     }
+
+    // It's possible to have null padding size for non-opaque response
+    rv = state->GetIsNull(3, &isNull);
+    if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
+
+    if (!isNull) {
+      int64_t paddingSize = 0;
+      rv = state->GetInt64(3, &paddingSize);
+      if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
+
+      MOZ_DIAGNOSTIC_ASSERT(paddingSize >= 0);
+      MOZ_DIAGNOSTIC_ASSERT(INT64_MAX - overallPaddingSize >= paddingSize);
+      overallPaddingSize += paddingSize;
+    }
   }
 
+  *aDeletedPaddingSizeOut = overallPaddingSize;
+
   // Dependent records removed via ON DELETE CASCADE
 
   query = NS_LITERAL_CSTRING(
     "DELETE FROM entries WHERE id IN ("
   );
   AppendListParamsToQuery(query, aEntryIdList, aPos, aLen);
   query.AppendLiteral(")");
 
@@ -1666,16 +1714,17 @@ InsertEntry(mozIStorageConnection* aConn
       "request_body_id, "
       "response_type, "
       "response_status, "
       "response_status_text, "
       "response_headers_guard, "
       "response_body_id, "
       "response_security_info_id, "
       "response_principal_info, "
+      "response_padding_size, "
       "cache_id "
     ") VALUES ("
       ":request_method, "
       ":request_url_no_query, "
       ":request_url_no_query_hash, "
       ":request_url_query, "
       ":request_url_query_hash, "
       ":request_url_fragment, "
@@ -1691,16 +1740,17 @@ InsertEntry(mozIStorageConnection* aConn
       ":request_body_id, "
       ":response_type, "
       ":response_status, "
       ":response_status_text, "
       ":response_headers_guard, "
       ":response_body_id, "
       ":response_security_info_id, "
       ":response_principal_info, "
+      ":response_padding_size, "
       ":cache_id "
     ");"
   ), getter_AddRefs(state));
   if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
 
   rv = state->BindUTF8StringByName(NS_LITERAL_CSTRING("request_method"),
                                    aRequest.method());
   if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
@@ -1808,16 +1858,28 @@ InsertEntry(mozIStorageConnection* aConn
     cInfo.attrs().CreateSuffix(suffix);
     serializedInfo.Append(suffix);
   }
 
   rv = state->BindUTF8StringByName(NS_LITERAL_CSTRING("response_principal_info"),
                                    serializedInfo);
   if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
 
+  if (aResponse.paddingSize() == InternalResponse::UNKNOWN_PADDING_SIZE) {
+    MOZ_DIAGNOSTIC_ASSERT(aResponse.type() != ResponseType::Opaque);
+    rv = state->BindNullByName(NS_LITERAL_CSTRING("response_padding_size"));
+  } else {
+    MOZ_DIAGNOSTIC_ASSERT(aResponse.paddingSize() >= 0);
+    MOZ_DIAGNOSTIC_ASSERT(aResponse.type() == ResponseType::Opaque);
+
+    rv = state->BindInt64ByName(NS_LITERAL_CSTRING("response_padding_size"),
+                                aResponse.paddingSize());
+  }
+  if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
+
   rv = state->BindInt64ByName(NS_LITERAL_CSTRING("cache_id"), aCacheId);
   if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
 
   rv = state->Execute();
   if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
 
   rv = aConn->CreateStatement(NS_LITERAL_CSTRING(
     "SELECT last_insert_rowid()"
@@ -1893,17 +1955,17 @@ InsertEntry(mozIStorageConnection* aConn
   if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
 
   const nsTArray<nsCString>& responseUrlList = aResponse.urlList();
   for (uint32_t i = 0; i < responseUrlList.Length(); ++i) {
     rv = state->BindUTF8StringByName(NS_LITERAL_CSTRING("url"),
                                      responseUrlList[i]);
     if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
 
-    rv = state->BindInt64ByName(NS_LITERAL_CSTRING("entry_id"), entryId);
+    rv = state->BindInt32ByName(NS_LITERAL_CSTRING("entry_id"), entryId);
     if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
 
     rv = state->Execute();
     if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
   }
 
   return rv;
 }
@@ -1920,16 +1982,17 @@ ReadResponse(mozIStorageConnection* aCon
   nsresult rv = aConn->CreateStatement(NS_LITERAL_CSTRING(
     "SELECT "
       "entries.response_type, "
       "entries.response_status, "
       "entries.response_status_text, "
       "entries.response_headers_guard, "
       "entries.response_body_id, "
       "entries.response_principal_info, "
+      "entries.response_padding_size, "
       "security_info.data "
     "FROM entries "
     "LEFT OUTER JOIN security_info "
     "ON entries.response_security_info_id=security_info.id "
     "WHERE entries.id=:id;"
   ), getter_AddRefs(state));
   if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
 
@@ -1981,17 +2044,37 @@ ReadResponse(mozIStorageConnection* aCon
       NS_WARNING("Something went wrong parsing a serialized principal!");
       return NS_ERROR_FAILURE;
     }
 
     aSavedResponseOut->mValue.principalInfo() =
       mozilla::ipc::ContentPrincipalInfo(attrs, void_t(), specNoSuffix);
   }
 
-  rv = state->GetBlobAsUTF8String(6, aSavedResponseOut->mValue.channelInfo().securityInfo());
+  bool nullPadding = false;
+  rv = state->GetIsNull(6, &nullPadding);
+  if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
+
+  if (nullPadding) {
+    MOZ_DIAGNOSTIC_ASSERT(aSavedResponseOut->mValue.type() !=
+                          ResponseType::Opaque);
+    aSavedResponseOut->mValue.paddingSize() =
+      InternalResponse::UNKNOWN_PADDING_SIZE;
+  } else {
+    MOZ_DIAGNOSTIC_ASSERT(aSavedResponseOut->mValue.type() ==
+                          ResponseType::Opaque);
+    int64_t paddingSize = 0;
+    rv = state->GetInt64(6, &paddingSize);
+    if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
+
+    MOZ_DIAGNOSTIC_ASSERT(paddingSize >= 0);
+    aSavedResponseOut->mValue.paddingSize() = paddingSize;
+  }
+
+  rv = state->GetBlobAsUTF8String(7, aSavedResponseOut->mValue.channelInfo().securityInfo());
   if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
 
   rv = aConn->CreateStatement(NS_LITERAL_CSTRING(
     "SELECT "
       "name, "
       "value "
     "FROM response_headers "
     "WHERE entry_id=:entry_id;"
--- a/dom/cache/DBSchema.h
+++ b/dom/cache/DBSchema.h
@@ -37,17 +37,18 @@ CreateOrMigrateSchema(mozIStorageConnect
 nsresult
 InitializeConnection(mozIStorageConnection* aConn);
 
 nsresult
 CreateCacheId(mozIStorageConnection* aConn, CacheId* aCacheIdOut);
 
 nsresult
 DeleteCacheId(mozIStorageConnection* aConn, CacheId aCacheId,
-              nsTArray<nsID>& aDeletedBodyIdListOut);
+              nsTArray<nsID>& aDeletedBodyIdListOut,
+              int64_t* aDeletedPaddingSizeOut);
 
 // TODO: Consider removing unused IsCacheOrphaned after writing cleanup code. (bug 1110446)
 nsresult
 IsCacheOrphaned(mozIStorageConnection* aConn, CacheId aCacheId,
                 bool* aOrphanedOut);
 
 nsresult
 FindOrphanedCacheIds(mozIStorageConnection* aConn,
@@ -68,23 +69,25 @@ CacheMatchAll(mozIStorageConnection* aCo
               nsTArray<SavedResponse>& aSavedResponsesOut);
 
 nsresult
 CachePut(mozIStorageConnection* aConn, CacheId aCacheId,
          const CacheRequest& aRequest,
          const nsID* aRequestBodyId,
          const CacheResponse& aResponse,
          const nsID* aResponseBodyId,
-         nsTArray<nsID>& aDeletedBodyIdListOut);
+         nsTArray<nsID>& aDeletedBodyIdListOut,
+         int64_t* aDeletedPaddingSizeOut);
 
 nsresult
 CacheDelete(mozIStorageConnection* aConn, CacheId aCacheId,
             const CacheRequest& aRequest,
             const CacheQueryParams& aParams,
             nsTArray<nsID>& aDeletedBodyIdListOut,
+            int64_t* aDeletedPaddingSizeOut,
             bool* aSuccessOut);
 
 nsresult
 CacheKeys(mozIStorageConnection* aConn, CacheId aCacheId,
           const CacheRequestOrVoid& aRequestOrVoid,
           const CacheQueryParams& aParams,
           nsTArray<SavedRequest>& aSavedRequestsOut);
 
--- a/dom/cache/Manager.cpp
+++ b/dom/cache/Manager.cpp
@@ -74,17 +74,19 @@ public:
 
       // Clean up orphaned Cache objects
       AutoTArray<CacheId, 8> orphanedCacheIdList;
       nsresult rv = db::FindOrphanedCacheIds(aConn, orphanedCacheIdList);
       if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
 
       for (uint32_t i = 0; i < orphanedCacheIdList.Length(); ++i) {
         AutoTArray<nsID, 16> deletedBodyIdList;
-        rv = db::DeleteCacheId(aConn, orphanedCacheIdList[i], deletedBodyIdList);
+        int64_t deletedPaddingSize = 0;
+        rv = db::DeleteCacheId(aConn, orphanedCacheIdList[i], deletedBodyIdList,
+                               &deletedPaddingSize);
         if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
 
         rv = BodyDeleteFiles(aQuotaInfo, aDBDir, deletedBodyIdList);
         if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
       }
 
       // Clean up orphaned body objects
       AutoTArray<nsID, 64> knownBodyIdList;
@@ -435,26 +437,28 @@ protected:
 // a Cache object that has been orphaned.
 class Manager::DeleteOrphanedCacheAction final : public SyncDBAction
 {
 public:
   DeleteOrphanedCacheAction(Manager* aManager, CacheId aCacheId)
     : SyncDBAction(DBAction::Existing)
     , mManager(aManager)
     , mCacheId(aCacheId)
+    , mDeletedPaddingSize(0)
   { }
 
   virtual nsresult
   RunSyncWithDBOnTarget(const QuotaInfo& aQuotaInfo, nsIFile* aDBDir,
                         mozIStorageConnection* aConn) override
   {
     mozStorageTransaction trans(aConn, false,
                                 mozIStorageConnection::TRANSACTION_IMMEDIATE);
 
-    nsresult rv = db::DeleteCacheId(aConn, mCacheId, mDeletedBodyIdList);
+    nsresult rv = db::DeleteCacheId(aConn, mCacheId, mDeletedBodyIdList,
+                                    &mDeletedPaddingSize);
     if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
 
     rv = trans.Commit();
     if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
 
     return rv;
   }
 
@@ -466,16 +470,18 @@ public:
     // ensure we release the manager on the initiating thread
     mManager = nullptr;
   }
 
 private:
   RefPtr<Manager> mManager;
   const CacheId mCacheId;
   nsTArray<nsID> mDeletedBodyIdList;
+  // Track any pad amount associated with orphaned entries.
+  int64_t mDeletedPaddingSize;
 };
 
 // ----------------------------------------------------------------------------
 
 class Manager::CacheMatchAction final : public Manager::BaseAction
 {
 public:
   CacheMatchAction(Manager* aManager, ListenerId aListenerId,
@@ -616,16 +622,17 @@ public:
     : DBAction(DBAction::Existing)
     , mManager(aManager)
     , mListenerId(aListenerId)
     , mCacheId(aCacheId)
     , mList(aPutList.Length())
     , mExpectedAsyncCopyCompletions(1)
     , mAsyncResult(NS_OK)
     , mMutex("cache::Manager::CachePutAllAction")
+    , mDeletedPaddingSize(0)
   {
     MOZ_DIAGNOSTIC_ASSERT(!aPutList.IsEmpty());
     MOZ_DIAGNOSTIC_ASSERT(aPutList.Length() == aRequestStreamList.Length());
     MOZ_DIAGNOSTIC_ASSERT(aPutList.Length() == aResponseStreamList.Length());
 
     for (uint32_t i = 0; i < aPutList.Length(); ++i) {
       Entry* entry = mList.AppendElement();
       entry->mRequest = aPutList[i].request();
@@ -676,17 +683,16 @@ private:
 
       rv = StartStreamCopy(aQuotaInfo, mList[i], ResponseStream,
                            &mExpectedAsyncCopyCompletions);
       if (NS_WARN_IF(NS_FAILED(rv))) {
         break;
       }
     }
 
-
     // Always call OnAsyncCopyComplete() manually here.  This covers the
     // case where there is no async copying and also reports any startup
     // errors correctly.  If we hit an error, then OnAsyncCopyComplete()
     // will cancel any async copying.
     OnAsyncCopyComplete(rv);
   }
 
   // Called once for each asynchronous file copy whether it succeeds or
@@ -768,23 +774,26 @@ private:
           return;
         }
       }
 
       rv = db::CachePut(mConn, mCacheId, e.mRequest,
                         e.mRequestStream ? &e.mRequestBodyId : nullptr,
                         e.mResponse,
                         e.mResponseStream ? &e.mResponseBodyId : nullptr,
-                        mDeletedBodyIdList);
+                        mDeletedBodyIdList, &mDeletedPaddingSize);
       if (NS_WARN_IF(NS_FAILED(rv))) {
         DoResolve(rv);
         return;
       }
     }
 
+    // XXXtt: Write .padding file to the cache directory
+    // UpdateDirectoryFile(mDBDir);
+
     rv = trans.Commit();
     Unused << NS_WARN_IF(NS_FAILED(rv));
 
     DoResolve(rv);
   }
 
   virtual void
   CompleteOnInitiatingThread(nsresult aRv) override
@@ -975,41 +984,44 @@ private:
   // thread activity is guaranteed complete
   nsTArray<nsID> mDeletedBodyIdList;
 
   // accessed from any thread while mMutex locked
   Mutex mMutex;
   nsTArray<nsCOMPtr<nsISupports>> mCopyContextList;
 
   Maybe<QuotaInfo> mQuotaInfo;
+  // Track any pad amount associated with overwritten entries.
+  int64_t mDeletedPaddingSize;
 };
 
 // ----------------------------------------------------------------------------
 
 class Manager::CacheDeleteAction final : public Manager::BaseAction
 {
 public:
   CacheDeleteAction(Manager* aManager, ListenerId aListenerId,
                     CacheId aCacheId, const CacheDeleteArgs& aArgs)
     : BaseAction(aManager, aListenerId)
     , mCacheId(aCacheId)
     , mArgs(aArgs)
     , mSuccess(false)
+    , mDeletedPaddingSize(0)
   { }
 
   virtual nsresult
   RunSyncWithDBOnTarget(const QuotaInfo& aQuotaInfo, nsIFile* aDBDir,
                         mozIStorageConnection* aConn) override
   {
     mozStorageTransaction trans(aConn, false,
                                 mozIStorageConnection::TRANSACTION_IMMEDIATE);
 
     nsresult rv = db::CacheDelete(aConn, mCacheId, mArgs.request(),
                                   mArgs.params(), mDeletedBodyIdList,
-                                  &mSuccess);
+                                  &mDeletedPaddingSize, &mSuccess);
     if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
 
     rv = trans.Commit();
     if (NS_WARN_IF(NS_FAILED(rv))) {
       mSuccess = false;
       return rv;
     }
 
@@ -1028,16 +1040,18 @@ public:
     return aCacheId == mCacheId;
   }
 
 private:
   const CacheId mCacheId;
   const CacheDeleteArgs mArgs;
   bool mSuccess;
   nsTArray<nsID> mDeletedBodyIdList;
+  // Track any pad amount associated with deleted entries.
+  int64_t mDeletedPaddingSize;
 };
 
 // ----------------------------------------------------------------------------
 
 class Manager::CacheKeysAction final : public Manager::BaseAction
 {
 public:
   CacheKeysAction(Manager* aManager, ListenerId aListenerId,
@@ -1277,16 +1291,18 @@ public:
                                         &exists, &mCacheId);
     if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
 
     if (!exists) {
       mCacheDeleted = false;
       return NS_OK;
     }
 
+    // Don't delete the removing padding size here, we'll delete it on
+    // DeleteOrphanedCacheAction.
     rv = db::StorageForgetCache(aConn, mNamespace, mArgs.key());
     if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
 
     rv = trans.Commit();
     if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
 
     mCacheDeleted = true;
     return rv;