Bug 1563023 - Part 3: Change client usage type to support the null value; r=asuth
authorJan Varga <jan.varga@gmail.com>
Thu, 22 Aug 2019 20:52:09 +0000
changeset 489506 94258b96e70dc1622ef697138b115088b13c3554
parent 489505 76ed02ac9371a169ebd0e30e08127c59be2d96e6
child 489507 538e930470815c3c6ada96cb75ce398fb28db8c0
push id36475
push userncsoregi@mozilla.com
push dateFri, 23 Aug 2019 09:45:38 +0000
treeherdermozilla-central@5df00af5913e [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersasuth
bugs1563023
milestone70.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 1563023 - Part 3: Change client usage type to support the null value; r=asuth This patch wraps the uint64_t type in a Maybe container, so the client usage can represent a state when there are no files on disk for the given client. Zero usage then represents a state when there are some files but they are empty or the client tracks logical size (not physical size of files on disk) and the logical size is zero. This can be useful especially for LocalStorage. Differential Revision: https://phabricator.services.mozilla.com/D38181
dom/cache/QuotaClient.cpp
dom/indexedDB/ActorsParent.cpp
dom/indexedDB/FileManager.h
dom/localstorage/ActorsParent.cpp
dom/quota/ActorsParent.cpp
dom/quota/UsageInfo.h
dom/simpledb/ActorsParent.cpp
--- a/dom/cache/QuotaClient.cpp
+++ b/dom/cache/QuotaClient.cpp
@@ -18,16 +18,17 @@
 #include "nsIFile.h"
 #include "nsISimpleEnumerator.h"
 #include "nsThreadUtils.h"
 
 namespace {
 
 using mozilla::Atomic;
 using mozilla::MutexAutoLock;
+using mozilla::Some;
 using mozilla::Unused;
 using mozilla::dom::ContentParentId;
 using mozilla::dom::cache::DirPaddingFile;
 using mozilla::dom::cache::Manager;
 using mozilla::dom::cache::QuotaInfo;
 using mozilla::dom::quota::AssertIsOnIOThread;
 using mozilla::dom::quota::Client;
 using mozilla::dom::quota::PersistenceType;
@@ -64,17 +65,17 @@ static nsresult GetBodyUsage(nsIFile* aD
 
     int64_t fileSize;
     rv = file->GetFileSize(&fileSize);
     if (NS_WARN_IF(NS_FAILED(rv))) {
       return rv;
     }
     MOZ_DIAGNOSTIC_ASSERT(fileSize >= 0);
 
-    aUsageInfo->AppendToFileUsage(fileSize);
+    aUsageInfo->AppendToFileUsage(Some(fileSize));
   }
 
   return NS_OK;
 }
 
 static nsresult LockedGetPaddingSizeFromDB(nsIFile* aDir,
                                            const nsACString& aGroup,
                                            const nsACString& aOrigin,
@@ -409,17 +410,17 @@ class CacheQuotaClient final : public Cl
         if (NS_WARN_IF(NS_FAILED(rv))) {
           REPORT_TELEMETRY_ERR_IN_INIT(aInitializing, kQuotaInternalError,
                                        Cache_GetPaddingSize);
           return rv;
         }
       }
     }
 
-    aUsageInfo->AppendToFileUsage(paddingSize);
+    aUsageInfo->AppendToFileUsage(Some(paddingSize));
 
     nsCOMPtr<nsIDirectoryEnumerator> entries;
     rv = dir->GetDirectoryEntries(getter_AddRefs(entries));
     if (NS_WARN_IF(NS_FAILED(rv))) {
       REPORT_TELEMETRY_ERR_IN_INIT(aInitializing, kQuotaExternalError,
                                    Cache_GetDirEntries);
       return rv;
     }
@@ -473,17 +474,17 @@ class CacheQuotaClient final : public Cl
         rv = file->GetFileSize(&fileSize);
         if (NS_WARN_IF(NS_FAILED(rv))) {
           REPORT_TELEMETRY_ERR_IN_INIT(aInitializing, kQuotaExternalError,
                                        Cache_GetFileSize);
           return rv;
         }
         MOZ_DIAGNOSTIC_ASSERT(fileSize >= 0);
 
-        aUsageInfo->AppendToDatabaseUsage(fileSize);
+        aUsageInfo->AppendToDatabaseUsage(Some(fileSize));
         continue;
       }
 
       // Ignore directory padding file
       if (leafName.EqualsLiteral(PADDING_FILE_NAME) ||
           leafName.EqualsLiteral(PADDING_TMP_FILE_NAME)) {
         continue;
       }
--- a/dom/indexedDB/ActorsParent.cpp
+++ b/dom/indexedDB/ActorsParent.cpp
@@ -8905,32 +8905,32 @@ nsresult RemoveDatabaseFilesAndDirectory
       return rv;
     }
 
     if (NS_WARN_IF(!isDirectory)) {
       IDB_REPORT_INTERNAL_ERR();
       return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
     }
 
-    uint64_t usage = 0;
+    uint64_t usage;
 
     if (aQuotaManager) {
-      rv = FileManager::GetUsage(fmDirectory, &usage);
+      rv = FileManager::GetUsage(fmDirectory, usage);
       if (NS_WARN_IF(NS_FAILED(rv))) {
         return rv;
       }
     }
 
     rv = fmDirectory->Remove(true);
     if (NS_WARN_IF(NS_FAILED(rv))) {
       // We may have deleted some files, check if we can and update quota
       // information before returning the error.
       if (aQuotaManager) {
         uint64_t newUsage;
-        if (NS_SUCCEEDED(FileManager::GetUsage(fmDirectory, &newUsage))) {
+        if (NS_SUCCEEDED(FileManager::GetUsage(fmDirectory, newUsage))) {
           MOZ_ASSERT(newUsage <= usage);
           usage = usage - newUsage;
         }
       }
     }
 
     if (aQuotaManager && usage) {
       aQuotaManager->DecreaseUsageForOrigin(aPersistenceType, aGroup, aOrigin,
@@ -15639,39 +15639,38 @@ nsresult FileManager::InitDirectory(nsIF
       }
     }
   }
 
   return NS_OK;
 }
 
 // static
-nsresult FileManager::GetUsage(nsIFile* aDirectory, uint64_t* aUsage) {
+nsresult FileManager::GetUsage(nsIFile* aDirectory, Maybe<uint64_t>& aUsage) {
   AssertIsOnIOThread();
   MOZ_ASSERT(aDirectory);
-  MOZ_ASSERT(aUsage);
 
   bool exists;
   nsresult rv = aDirectory->Exists(&exists);
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return rv;
   }
 
   if (!exists) {
-    *aUsage = 0;
+    aUsage.reset();
     return NS_OK;
   }
 
   nsCOMPtr<nsIDirectoryEnumerator> entries;
   rv = aDirectory->GetDirectoryEntries(getter_AddRefs(entries));
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return rv;
   }
 
-  uint64_t usage = 0;
+  Maybe<uint64_t> usage;
 
   nsCOMPtr<nsIFile> file;
   while (NS_SUCCEEDED((rv = entries->GetNextFile(getter_AddRefs(file)))) &&
          file) {
     nsString leafName;
     rv = file->GetLeafName(leafName);
     if (NS_WARN_IF(NS_FAILED(rv))) {
       return rv;
@@ -15682,24 +15681,39 @@ nsresult FileManager::GetUsage(nsIFile* 
     }
 
     int64_t fileSize;
     rv = file->GetFileSize(&fileSize);
     if (NS_WARN_IF(NS_FAILED(rv))) {
       return rv;
     }
 
-    UsageInfo::IncrementUsage(&usage, uint64_t(fileSize));
-  }
-
-  if (NS_WARN_IF(NS_FAILED(rv))) {
-    return rv;
-  }
-
-  *aUsage = usage;
+    UsageInfo::IncrementUsage(usage, Some(uint64_t(fileSize)));
+  }
+
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+
+  aUsage = usage;
+  return NS_OK;
+}
+
+// static
+nsresult FileManager::GetUsage(nsIFile* aDirectory, uint64_t& aUsage) {
+  AssertIsOnIOThread();
+  MOZ_ASSERT(aDirectory);
+
+  Maybe<uint64_t> usage;
+  nsresult rv = GetUsage(aDirectory, usage);
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+
+  aUsage = usage.valueOr(0);
   return NS_OK;
 }
 
 /*******************************************************************************
  * QuotaClient
  ******************************************************************************/
 
 QuotaClient* QuotaClient::sInstance = nullptr;
@@ -16095,30 +16109,30 @@ nsresult QuotaClient::InitOrigin(Persist
       rv = databaseFile->GetFileSize(&fileSize);
       if (NS_WARN_IF(NS_FAILED(rv))) {
         REPORT_TELEMETRY_INIT_ERR(kQuotaExternalError, IDB_GetFileSize);
         return rv;
       }
 
       MOZ_ASSERT(fileSize >= 0);
 
-      aUsageInfo->AppendToDatabaseUsage(uint64_t(fileSize));
+      aUsageInfo->AppendToDatabaseUsage(Some(uint64_t(fileSize)));
 
       rv = walFile->GetFileSize(&fileSize);
       if (NS_SUCCEEDED(rv)) {
         MOZ_ASSERT(fileSize >= 0);
-        aUsageInfo->AppendToDatabaseUsage(uint64_t(fileSize));
+        aUsageInfo->AppendToDatabaseUsage(Some(uint64_t(fileSize)));
       } else if (NS_WARN_IF(rv != NS_ERROR_FILE_NOT_FOUND &&
                             rv != NS_ERROR_FILE_TARGET_DOES_NOT_EXIST)) {
         REPORT_TELEMETRY_INIT_ERR(kQuotaExternalError, IDB_GetWalFileSize);
         return rv;
       }
 
-      uint64_t usage;
-      rv = FileManager::GetUsage(fmDirectory, &usage);
+      Maybe<uint64_t> usage;
+      rv = FileManager::GetUsage(fmDirectory, usage);
       if (NS_WARN_IF(NS_FAILED(rv))) {
         REPORT_TELEMETRY_INIT_ERR(kQuotaExternalError, IDB_GetUsage);
         return rv;
       }
 
       aUsageInfo->AppendToFileUsage(usage);
     }
   }
@@ -16604,19 +16618,19 @@ nsresult QuotaClient::GetUsageForDirecto
     rv = file->GetFileSize(&fileSize);
     if (NS_WARN_IF(NS_FAILED(rv))) {
       return rv;
     }
 
     MOZ_ASSERT(fileSize >= 0);
 
     if (aDatabaseFiles) {
-      aUsageInfo->AppendToDatabaseUsage(uint64_t(fileSize));
-    } else {
-      aUsageInfo->AppendToFileUsage(uint64_t(fileSize));
+      aUsageInfo->AppendToDatabaseUsage(Some(uint64_t(fileSize)));
+    } else {
+      aUsageInfo->AppendToFileUsage(Some(uint64_t(fileSize)));
     }
   }
 
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return rv;
   }
 
   return NS_OK;
--- a/dom/indexedDB/FileManager.h
+++ b/dom/indexedDB/FileManager.h
@@ -52,17 +52,19 @@ class FileManager final {
                                                        int64_t aId);
 
   static nsresult InitDirectory(nsIFile* aDirectory, nsIFile* aDatabaseFile,
                                 PersistenceType aPersistenceType,
                                 const nsACString& aGroup,
                                 const nsACString& aOrigin,
                                 uint32_t aTelemetryId);
 
-  static nsresult GetUsage(nsIFile* aDirectory, uint64_t* aUsage);
+  static nsresult GetUsage(nsIFile* aDirectory, Maybe<uint64_t>& aUsage);
+
+  static nsresult GetUsage(nsIFile* aDirectory, uint64_t& aUsage);
 
   FileManager(PersistenceType aPersistenceType, const nsACString& aGroup,
               const nsACString& aOrigin, const nsAString& aDatabaseName,
               bool aEnforcingQuota);
 
   PersistenceType Type() const { return mPersistenceType; }
 
   const nsACString& Group() const { return mGroup; }
--- a/dom/localstorage/ActorsParent.cpp
+++ b/dom/localstorage/ActorsParent.cpp
@@ -8682,17 +8682,17 @@ nsresult QuotaClient::InitOrigin(Persist
     }
 
     MOZ_ASSERT(usage >= 0);
 
     if (!aForGetUsage) {
       InitUsageForOrigin(aOrigin, usage);
     }
 
-    aUsageInfo->AppendToDatabaseUsage(uint64_t(usage));
+    aUsageInfo->AppendToDatabaseUsage(Some(uint64_t(usage)));
   } else if (usageFileExists) {
     rv = usageFile->Remove(false);
     if (NS_WARN_IF(NS_FAILED(rv))) {
       REPORT_TELEMETRY_INIT_ERR(kQuotaExternalError, LS_Remove4);
       return rv;
     }
   }
 
@@ -8772,17 +8772,17 @@ nsresult QuotaClient::GetUsageForOrigin(
   MOZ_ASSERT(aUsageInfo);
 
   // We can't open the database at this point, since it can be already used
   // by the connection thread. Use the cached value instead.
 
   int64_t usage;
   if (mozilla::dom::GetUsageForOrigin(aOrigin, usage)) {
     MOZ_ASSERT(usage >= 0);
-    aUsageInfo->AppendToDatabaseUsage(usage);
+    aUsageInfo->AppendToDatabaseUsage(Some(usage));
   }
 
   return NS_OK;
 }
 
 nsresult QuotaClient::AboutToClearOrigins(
     const Nullable<PersistenceType>& aPersistenceType,
     const OriginScope& aOriginScope) {
--- a/dom/quota/ActorsParent.cpp
+++ b/dom/quota/ActorsParent.cpp
@@ -599,23 +599,20 @@ class QuotaManager::Observer final : pub
 namespace {
 
 /*******************************************************************************
  * Local class declarations
  ******************************************************************************/
 
 }  // namespace
 
-class ClientUsageArray final : public AutoTArray<uint64_t, Client::TYPE_MAX> {
+class ClientUsageArray final
+    : public AutoTArray<Maybe<uint64_t>, Client::TYPE_MAX> {
  public:
-  ClientUsageArray() {
-    for (uint32_t index = 0; index < uint32_t(Client::TypeMax()); index++) {
-      AppendElement(0);
-    }
-  }
+  ClientUsageArray() { SetLength(Client::TypeMax()); }
 };
 
 class OriginInfo final {
   friend class GroupInfo;
   friend class QuotaManager;
   friend class QuotaObject;
 
  public:
@@ -630,18 +627,18 @@ class OriginInfo final {
   const nsCString& Origin() const { return mOrigin; }
 
   int64_t LockedUsage() const {
     AssertCurrentThreadOwnsQuotaMutex();
 
 #ifdef DEBUG
     uint64_t usage = 0;
     for (uint32_t index = 0; index < uint32_t(Client::TypeMax()); index++) {
-      AssertNoOverflow(usage, mClientUsages[index]);
-      usage += mClientUsages[index];
+      AssertNoOverflow(usage, mClientUsages[index].valueOr(0));
+      usage += mClientUsages[index].valueOr(0);
     }
     MOZ_ASSERT(mUsage == usage);
 #endif
 
     return mUsage;
   }
 
   int64_t LockedAccessTime() const {
@@ -1181,19 +1178,20 @@ class GetUsageOp final : public QuotaUsa
   nsresult ProcessOrigin(QuotaManager* aQuotaManager, nsIFile* aOriginDir,
                          const bool aPersistent,
                          const PersistenceType aPersistenceType) override;
 
   void GetResponse(UsageRequestResponse& aResponse) override;
 };
 
 class GetOriginUsageOp final : public QuotaUsageRequestBase {
-  UsageInfo mUsageInfo;
   nsCString mSuffix;
   nsCString mGroup;
+  uint64_t mUsage;
+  uint64_t mFileUsage;
   bool mFromMemory;
 
  public:
   explicit GetOriginUsageOp(const UsageRequestParams& aParams);
 
  private:
   ~GetOriginUsageOp() = default;
 
@@ -2853,34 +2851,37 @@ bool QuotaObject::LockedMaybeUpdateSize(
       if (!mOriginInfo->LockedPersisted()) {
         AssertNoUnderflow(groupInfo->mUsage, delta);
         groupInfo->mUsage -= delta;
       }
 
       AssertNoUnderflow(mOriginInfo->mUsage, delta);
       mOriginInfo->mUsage -= delta;
 
-      AssertNoUnderflow(mOriginInfo->mClientUsages[mClientType], delta);
-      mOriginInfo->mClientUsages[mClientType] -= delta;
+      MOZ_ASSERT(mOriginInfo->mClientUsages[mClientType].isSome());
+      AssertNoUnderflow(mOriginInfo->mClientUsages[mClientType].value(), delta);
+      mOriginInfo->mClientUsages[mClientType] =
+          Some(mOriginInfo->mClientUsages[mClientType].value() - delta);
 
       mSize = aSize;
     }
     return true;
   }
 
   MOZ_ASSERT(mSize < aSize);
 
   RefPtr<GroupInfo> complementaryGroupInfo =
       groupInfo->mGroupInfoPair->LockedGetGroupInfo(
           ComplementaryPersistenceType(groupInfo->mPersistenceType));
 
   uint64_t delta = aSize - mSize;
 
-  AssertNoOverflow(mOriginInfo->mClientUsages[mClientType], delta);
-  uint64_t newClientUsage = mOriginInfo->mClientUsages[mClientType] + delta;
+  AssertNoOverflow(mOriginInfo->mClientUsages[mClientType].valueOr(0), delta);
+  uint64_t newClientUsage =
+      mOriginInfo->mClientUsages[mClientType].valueOr(0) + delta;
 
   AssertNoOverflow(mOriginInfo->mUsage, delta);
   uint64_t newUsage = mOriginInfo->mUsage + delta;
 
   // Temporary storage has no limit for origin usage (there's a group and the
   // global limit though).
 
   uint64_t newGroupUsage = groupInfo->mUsage;
@@ -2966,18 +2967,18 @@ bool QuotaObject::LockedMaybeUpdateSize(
     }
 
     // We unlocked and relocked several times so we need to recompute all the
     // essential variables and recheck the group limit.
 
     AssertNoUnderflow(aSize, mSize);
     delta = aSize - mSize;
 
-    AssertNoOverflow(mOriginInfo->mClientUsages[mClientType], delta);
-    newClientUsage = mOriginInfo->mClientUsages[mClientType] + delta;
+    AssertNoOverflow(mOriginInfo->mClientUsages[mClientType].valueOr(0), delta);
+    newClientUsage = mOriginInfo->mClientUsages[mClientType].valueOr(0) + delta;
 
     AssertNoOverflow(mOriginInfo->mUsage, delta);
     newUsage = mOriginInfo->mUsage + delta;
 
     newGroupUsage = groupInfo->mUsage;
     if (!mOriginInfo->LockedPersisted()) {
       AssertNoOverflow(groupInfo->mUsage, delta);
       newGroupUsage += delta;
@@ -3006,17 +3007,17 @@ bool QuotaObject::LockedMaybeUpdateSize(
     newTemporaryStorageUsage = quotaManager->mTemporaryStorageUsage + delta;
 
     NS_ASSERTION(
         newTemporaryStorageUsage <= quotaManager->mTemporaryStorageLimit,
         "How come?!");
 
     // Ok, we successfully freed enough space and the operation can continue
     // without throwing the quota error.
-    mOriginInfo->mClientUsages[mClientType] = newClientUsage;
+    mOriginInfo->mClientUsages[mClientType] = Some(newClientUsage);
 
     mOriginInfo->mUsage = newUsage;
     if (!mOriginInfo->LockedPersisted()) {
       groupInfo->mUsage = newGroupUsage;
     }
     quotaManager->mTemporaryStorageUsage = newTemporaryStorageUsage;
     ;
 
@@ -3029,17 +3030,17 @@ bool QuotaObject::LockedMaybeUpdateSize(
     // ops for the evicted origins.
     MutexAutoUnlock autoUnlock(quotaManager->mQuotaMutex);
 
     quotaManager->FinalizeOriginEviction(locks);
 
     return true;
   }
 
-  mOriginInfo->mClientUsages[mClientType] = newClientUsage;
+  mOriginInfo->mClientUsages[mClientType] = Some(newClientUsage);
 
   mOriginInfo->mUsage = newUsage;
   if (!mOriginInfo->LockedPersisted()) {
     groupInfo->mUsage = newGroupUsage;
   }
   quotaManager->mTemporaryStorageUsage = newTemporaryStorageUsage;
 
   mSize = aSize;
@@ -4383,22 +4384,22 @@ nsresult QuotaManager::InitializeOrigin(
                                           /* aForGetUsage */ false);
     if (NS_WARN_IF(NS_FAILED(rv))) {
       // error should have reported in InitOrigin
       RECORD_IN_NIGHTLY(statusKeeper, rv);
       CONTINUE_IN_NIGHTLY_RETURN_IN_OTHERS(rv);
     }
 
     if (trackQuota) {
-      uint64_t clientUsage = usageInfo.TotalUsage();
+      Maybe<uint64_t> clientUsage = usageInfo.TotalUsage();
 
       clientUsages[clientType] = clientUsage;
 
-      AssertNoOverflow(usage, clientUsage);
-      usage += clientUsage;
+      AssertNoOverflow(usage, clientUsage.valueOr(0));
+      usage += clientUsage.valueOr(0);
     }
   }
   if (NS_WARN_IF(NS_FAILED(rv))) {
     REPORT_TELEMETRY_INIT_ERR(kQuotaInternalError, Ori_GetNextFile);
     RECORD_IN_NIGHTLY(statusKeeper, rv);
 #ifndef NIGHTLY_BUILD
     return rv;
 #endif
@@ -6744,30 +6745,31 @@ OriginInfo::OriginInfo(GroupInfo* aGroup
   MOZ_ASSERT(aGroupInfo);
   MOZ_ASSERT(aClientUsages.Length() == Client::TypeMax());
   MOZ_ASSERT_IF(aPersisted,
                 aGroupInfo->mPersistenceType == PERSISTENCE_TYPE_DEFAULT);
 
 #ifdef DEBUG
   uint64_t usage = 0;
   for (uint32_t index = 0; index < uint32_t(Client::TypeMax()); index++) {
-    AssertNoOverflow(usage, aClientUsages[index]);
-    usage += aClientUsages[index];
+    AssertNoOverflow(usage, aClientUsages[index].valueOr(0));
+    usage += aClientUsages[index].valueOr(0);
   }
   MOZ_ASSERT(aUsage == usage);
 #endif
 
   MOZ_COUNT_CTOR(OriginInfo);
 }
 
 void OriginInfo::LockedDecreaseUsage(Client::Type aClientType, int64_t aSize) {
   AssertCurrentThreadOwnsQuotaMutex();
 
-  AssertNoUnderflow(mClientUsages[aClientType], aSize);
-  mClientUsages[aClientType] -= aSize;
+  MOZ_ASSERT(mClientUsages[aClientType].isSome());
+  AssertNoUnderflow(mClientUsages[aClientType].value(), aSize);
+  mClientUsages[aClientType] = Some(mClientUsages[aClientType].value() - aSize);
 
   AssertNoUnderflow(mUsage, aSize);
   mUsage -= aSize;
 
   if (!LockedPersisted()) {
     AssertNoUnderflow(mGroupInfo->mUsage, aSize);
     mGroupInfo->mUsage -= aSize;
   }
@@ -6777,19 +6779,19 @@ void OriginInfo::LockedDecreaseUsage(Cli
 
   AssertNoUnderflow(quotaManager->mTemporaryStorageUsage, aSize);
   quotaManager->mTemporaryStorageUsage -= aSize;
 }
 
 void OriginInfo::LockedResetUsageForClient(Client::Type aClientType) {
   AssertCurrentThreadOwnsQuotaMutex();
 
-  uint64_t size = mClientUsages[aClientType];
-
-  mClientUsages[aClientType] = 0;
+  uint64_t size = mClientUsages[aClientType].valueOr(0);
+
+  mClientUsages[aClientType].reset();
 
   AssertNoUnderflow(mUsage, size);
   mUsage -= size;
 
   if (!LockedPersisted()) {
     AssertNoUnderflow(mGroupInfo->mUsage, size);
     mGroupInfo->mUsage -= size;
   }
@@ -7692,17 +7694,16 @@ bool QuotaUsageRequestBase::Init(Quota* 
 
 nsresult QuotaUsageRequestBase::GetUsageForOrigin(
     QuotaManager* aQuotaManager, PersistenceType aPersistenceType,
     const nsACString& aGroup, const nsACString& aOrigin,
     UsageInfo* aUsageInfo) {
   AssertIsOnIOThread();
   MOZ_ASSERT(aQuotaManager);
   MOZ_ASSERT(aUsageInfo);
-  MOZ_ASSERT(aUsageInfo->TotalUsage() == 0);
 
   nsCOMPtr<nsIFile> directory;
   nsresult rv = aQuotaManager->GetDirectoryForOrigin(aPersistenceType, aOrigin,
                                                      getter_AddRefs(directory));
   NS_ENSURE_SUCCESS(rv, rv);
 
   bool exists;
   rv = directory->Exists(&exists);
@@ -7977,17 +7978,17 @@ nsresult GetUsageOp::ProcessOrigin(Quota
   UsageInfo usageInfo;
   rv = GetUsageForOrigin(aQuotaManager, aPersistenceType, group, origin,
                          &usageInfo);
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return rv;
   }
 
   ProcessOriginInternal(aQuotaManager, aPersistenceType, origin, timestamp,
-                        persisted, usageInfo.TotalUsage());
+                        persisted, usageInfo.TotalUsage().valueOr(0));
 
   return NS_OK;
 }
 
 nsresult GetUsageOp::DoDirectoryWork(QuotaManager* aQuotaManager) {
   AssertIsOnIOThread();
 
   AUTO_PROFILER_LABEL("GetUsageOp::DoDirectoryWork", OTHER);
@@ -8024,17 +8025,18 @@ void GetUsageOp::GetResponse(UsageReques
   if (!mOriginUsages.IsEmpty()) {
     nsTArray<OriginUsage>& originUsages =
         aResponse.get_AllUsageResponse().originUsages();
 
     mOriginUsages.SwapElements(originUsages);
   }
 }
 
-GetOriginUsageOp::GetOriginUsageOp(const UsageRequestParams& aParams) {
+GetOriginUsageOp::GetOriginUsageOp(const UsageRequestParams& aParams)
+    : mUsage(0), mFileUsage(0) {
   AssertIsOnOwningThread();
   MOZ_ASSERT(aParams.type() == UsageRequestParams::TOriginUsageParams);
 
   const OriginUsageParams& params = aParams.get_OriginUsageParams();
 
   QuotaManager::GetInfoFromValidatedPrincipalInfo(
       params.principalInfo(), &mSuffix, &mGroup, mOriginScope.AsOriginSetter());
 
@@ -8046,17 +8048,18 @@ GetOriginUsageOp::GetOriginUsageOp(const
   }
 
   // Overwrite OriginOperationBase default values.
   mNeedsQuotaManagerInit = true;
 }
 
 nsresult GetOriginUsageOp::DoDirectoryWork(QuotaManager* aQuotaManager) {
   AssertIsOnIOThread();
-  MOZ_ASSERT(mUsageInfo.TotalUsage() == 0);
+  MOZ_ASSERT(mUsage == 0);
+  MOZ_ASSERT(mFileUsage == 0);
 
   AUTO_PROFILER_LABEL("GetOriginUsageOp::DoDirectoryWork", OTHER);
 
   nsresult rv;
 
   if (mFromMemory) {
     // This must be called before EnsureTemporaryStorageIsInitialized.
     rv = aQuotaManager->EnsureStorageIsInitialized();
@@ -8067,49 +8070,47 @@ nsresult GetOriginUsageOp::DoDirectoryWo
     // Ensure temporary storage is initialized. If temporary storage hasn't been
     // initialized yet, the method will initialize it by traversing the
     // repositories for temporary and default storage (including our origin).
     rv = aQuotaManager->EnsureTemporaryStorageIsInitialized();
     if (NS_WARN_IF(NS_FAILED(rv))) {
       return rv;
     }
 
-    // Get cached usage (the method doesn't have to stat any files).
-    uint64_t usage =
-        aQuotaManager->GetOriginUsage(mGroup, mOriginScope.GetOrigin());
-
-    // File usage is not tracked in memory separately, so just add to the
-    // database usage.
-    mUsageInfo.AppendToDatabaseUsage(usage);
+    // Get cached usage (the method doesn't have to stat any files). File usage
+    // is not tracked in memory separately, so just add to the total usage.
+    mUsage = aQuotaManager->GetOriginUsage(mGroup, mOriginScope.GetOrigin());
 
     return NS_OK;
   }
 
+  UsageInfo usageInfo;
+
   // Add all the persistent/temporary/default storage files we care about.
   for (const PersistenceType type : kAllPersistenceTypes) {
-    UsageInfo usageInfo;
     rv = GetUsageForOrigin(aQuotaManager, type, mGroup,
                            mOriginScope.GetOrigin(), &usageInfo);
     if (NS_WARN_IF(NS_FAILED(rv))) {
       return rv;
     }
-
-    mUsageInfo.Append(usageInfo);
-  }
+  }
+
+  mUsage = usageInfo.TotalUsage().valueOr(0);
+  mFileUsage = usageInfo.FileUsage().valueOr(0);
 
   return NS_OK;
 }
 
 void GetOriginUsageOp::GetResponse(UsageRequestResponse& aResponse) {
   AssertIsOnOwningThread();
 
   OriginUsageResponse usageResponse;
 
-  usageResponse.usage() = mUsageInfo.TotalUsage();
-  usageResponse.fileUsage() = mUsageInfo.FileUsage();
+  usageResponse.usage() = mUsage;
+  usageResponse.fileUsage() = mFileUsage;
 
   aResponse = usageResponse;
 }
 
 bool QuotaRequestBase::Init(Quota* aQuota) {
   AssertIsOnOwningThread();
   MOZ_ASSERT(aQuota);
 
--- a/dom/quota/UsageInfo.h
+++ b/dom/quota/UsageInfo.h
@@ -9,56 +9,61 @@
 
 #include "mozilla/dom/quota/QuotaCommon.h"
 
 #include "mozilla/Atomics.h"
 #include "mozilla/CheckedInt.h"
 
 BEGIN_QUOTA_NAMESPACE
 
-class UsageInfo {
+class UsageInfo final {
  public:
-  UsageInfo() : mDatabaseUsage(0), mFileUsage(0) {}
+  void Append(const UsageInfo& aUsageInfo) {
+    IncrementUsage(mDatabaseUsage, aUsageInfo.mDatabaseUsage);
+    IncrementUsage(mFileUsage, aUsageInfo.mFileUsage);
+  }
 
-  virtual ~UsageInfo() {}
-
-  void Append(const UsageInfo& aUsageInfo) {
-    IncrementUsage(&mDatabaseUsage, aUsageInfo.mDatabaseUsage);
-    IncrementUsage(&mFileUsage, aUsageInfo.mFileUsage);
+  void AppendToDatabaseUsage(const Maybe<uint64_t>& aUsage) {
+    IncrementUsage(mDatabaseUsage, aUsage);
   }
 
-  void AppendToDatabaseUsage(uint64_t aUsage) {
-    IncrementUsage(&mDatabaseUsage, aUsage);
-  }
-
-  void AppendToFileUsage(uint64_t aUsage) {
-    IncrementUsage(&mFileUsage, aUsage);
+  void AppendToFileUsage(const Maybe<uint64_t>& aUsage) {
+    IncrementUsage(mFileUsage, aUsage);
   }
 
-  uint64_t DatabaseUsage() { return mDatabaseUsage; }
+  const Maybe<uint64_t>& DatabaseUsage() const { return mDatabaseUsage; }
 
-  uint64_t FileUsage() { return mFileUsage; }
+  const Maybe<uint64_t>& FileUsage() const { return mFileUsage; }
 
-  uint64_t TotalUsage() {
-    uint64_t totalUsage = mDatabaseUsage;
-    IncrementUsage(&totalUsage, mFileUsage);
+  Maybe<uint64_t> TotalUsage() {
+    Maybe<uint64_t> totalUsage;
+
+    IncrementUsage(totalUsage, mDatabaseUsage);
+    IncrementUsage(totalUsage, mFileUsage);
+
     return totalUsage;
   }
 
-  static void IncrementUsage(uint64_t* aUsage, uint64_t aDelta) {
-    MOZ_ASSERT(aUsage);
-    CheckedUint64 value = *aUsage;
-    value += aDelta;
+  static void IncrementUsage(Maybe<uint64_t>& aUsage,
+                             const Maybe<uint64_t>& aDelta) {
+    if (aDelta.isNothing()) {
+      return;
+    }
+
+    CheckedUint64 value = aUsage.valueOr(0);
+
+    value += aDelta.value();
+
     if (value.isValid()) {
-      *aUsage = value.value();
+      aUsage = Some(value.value());
     } else {
-      *aUsage = UINT64_MAX;
+      aUsage = Some(UINT64_MAX);
     }
   }
 
  private:
-  uint64_t mDatabaseUsage;
-  uint64_t mFileUsage;
+  Maybe<uint64_t> mDatabaseUsage;
+  Maybe<uint64_t> mFileUsage;
 };
 
 END_QUOTA_NAMESPACE
 
 #endif  // mozilla_dom_quota_usageinfo_h__
--- a/dom/simpledb/ActorsParent.cpp
+++ b/dom/simpledb/ActorsParent.cpp
@@ -1671,17 +1671,17 @@ nsresult QuotaClient::GetUsageForOrigin(
     int64_t fileSize;
     rv = file->GetFileSize(&fileSize);
     if (NS_WARN_IF(NS_FAILED(rv))) {
       return rv;
     }
 
     MOZ_ASSERT(fileSize >= 0);
 
-    aUsageInfo->AppendToDatabaseUsage(uint64_t(fileSize));
+    aUsageInfo->AppendToDatabaseUsage(Some(uint64_t(fileSize)));
   }
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return rv;
   }
 
   return NS_OK;
 }