Bug 1563023 - Part 2: Add support for getting origin usage from memory; r=asuth
authorJan Varga <jan.varga@gmail.com>
Thu, 22 Aug 2019 20:51:09 +0000
changeset 553246 e7c68f2d86c5713e233e3bfdeb7bdda9bdebb5de
parent 553245 0b546821bdb29e94882fa7580507b9606f723416
child 553247 f329121924593211aec7be307db680df661f103a
push id2165
push userffxbld-merge
push dateMon, 14 Oct 2019 16:30:58 +0000
treeherdermozilla-release@0eae18af659f [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 2: Add support for getting origin usage from memory; r=asuth This patch modifies getUsageForPrincipal to support getting origin usage from memory. Support for getting group usage is factored out to a standalone method called Estimate. Operations based on NormalOriginOperationBase can now avoid directory locking if they don't touch disk. Differential Revision: https://phabricator.services.mozilla.com/D38087
dom/quota/ActorsChild.cpp
dom/quota/ActorsChild.h
dom/quota/ActorsParent.cpp
dom/quota/OriginScope.h
dom/quota/PQuota.ipdl
dom/quota/PQuotaRequest.ipdl
dom/quota/PQuotaUsageRequest.ipdl
dom/quota/PersistenceType.h
dom/quota/QuotaManager.h
dom/quota/QuotaManagerService.cpp
dom/quota/QuotaResults.cpp
dom/quota/QuotaResults.h
dom/quota/StorageManager.cpp
dom/quota/UsageInfo.h
dom/quota/nsIQuotaManagerService.idl
dom/quota/nsIQuotaResults.idl
--- a/dom/quota/ActorsChild.cpp
+++ b/dom/quota/ActorsChild.cpp
@@ -160,18 +160,18 @@ void QuotaUsageRequestChild::HandleRespo
   mRequest->SetResult(variant);
 }
 
 void QuotaUsageRequestChild::HandleResponse(
     const OriginUsageResponse& aResponse) {
   AssertIsOnOwningThread();
   MOZ_ASSERT(mRequest);
 
-  RefPtr<OriginUsageResult> result = new OriginUsageResult(
-      aResponse.usage(), aResponse.fileUsage(), aResponse.limit());
+  RefPtr<OriginUsageResult> result =
+      new OriginUsageResult(aResponse.usage(), aResponse.fileUsage());
 
   RefPtr<nsVariant> variant = new nsVariant();
   variant->SetAsInterface(NS_GET_IID(nsIQuotaOriginUsageResult), result);
 
   mRequest->SetResult(variant);
 }
 
 void QuotaUsageRequestChild::ActorDestroy(ActorDestroyReason aWhy) {
@@ -258,16 +258,29 @@ void QuotaRequestChild::HandleResponse(b
   MOZ_ASSERT(mRequest);
 
   RefPtr<nsVariant> variant = new nsVariant();
   variant->SetAsBool(aResponse);
 
   mRequest->SetResult(variant);
 }
 
+void QuotaRequestChild::HandleResponse(const EstimateResponse& aResponse) {
+  AssertIsOnOwningThread();
+  MOZ_ASSERT(mRequest);
+
+  RefPtr<EstimateResult> result =
+      new EstimateResult(aResponse.usage(), aResponse.limit());
+
+  RefPtr<nsVariant> variant = new nsVariant();
+  variant->SetAsInterface(NS_GET_IID(nsIQuotaEstimateResult), result);
+
+  mRequest->SetResult(variant);
+}
+
 void QuotaRequestChild::HandleResponse(const nsTArray<nsCString>& aResponse) {
   AssertIsOnOwningThread();
   MOZ_ASSERT(mRequest);
 
   RefPtr<nsVariant> variant = new nsVariant();
 
   if (aResponse.IsEmpty()) {
     variant->SetAsEmptyArray();
@@ -320,16 +333,20 @@ mozilla::ipc::IPCResult QuotaRequestChil
     case RequestResponse::TInitOriginResponse:
       HandleResponse(aResponse.get_InitOriginResponse().created());
       break;
 
     case RequestResponse::TPersistedResponse:
       HandleResponse(aResponse.get_PersistedResponse().persisted());
       break;
 
+    case RequestResponse::TEstimateResponse:
+      HandleResponse(aResponse.get_EstimateResponse());
+      break;
+
     case RequestResponse::TListInitializedOriginsResponse:
       HandleResponse(aResponse.get_ListInitializedOriginsResponse().origins());
       break;
 
     default:
       MOZ_CRASH("Unknown response type!");
   }
 
--- a/dom/quota/ActorsChild.h
+++ b/dom/quota/ActorsChild.h
@@ -124,16 +124,18 @@ class QuotaRequestChild final : public P
   ~QuotaRequestChild();
 
   void HandleResponse(nsresult aResponse);
 
   void HandleResponse();
 
   void HandleResponse(bool aResponse);
 
+  void HandleResponse(const EstimateResponse& aResponse);
+
   void HandleResponse(const nsTArray<nsCString>& aResponse);
 
   // IPDL methods are only called by IPDL.
   virtual void ActorDestroy(ActorDestroyReason aWhy) override;
 
   virtual mozilla::ipc::IPCResult Recv__delete__(
       const RequestResponse& aResponse) override;
 };
--- a/dom/quota/ActorsParent.cpp
+++ b/dom/quota/ActorsParent.cpp
@@ -863,17 +863,16 @@ class OriginOperationBase : public Backg
     State_Complete
   };
 
  private:
   State mState;
   bool mActorDestroyed;
 
  protected:
-  bool mNeedsMainThreadInit;
   bool mNeedsQuotaManagerInit;
 
  public:
   void NoteActorDestroyed() {
     AssertIsOnOwningThread();
 
     mActorDestroyed = true;
   }
@@ -887,17 +886,16 @@ class OriginOperationBase : public Backg
  protected:
   explicit OriginOperationBase(
       nsIEventTarget* aOwningThread = GetCurrentThreadEventTarget())
       : BackgroundThreadObject(aOwningThread),
         Runnable("dom::quota::OriginOperationBase"),
         mResultCode(NS_OK),
         mState(State_Initial),
         mActorDestroyed(false),
-        mNeedsMainThreadInit(false),
         mNeedsQuotaManagerInit(false) {}
 
   // Reference counted.
   virtual ~OriginOperationBase() {
     MOZ_ASSERT(mState == State_Complete);
     MOZ_ASSERT(mActorDestroyed);
   }
 
@@ -986,30 +984,32 @@ class NormalOriginOperationBase : public
   RefPtr<DirectoryLock> mDirectoryLock;
 
  protected:
   Nullable<PersistenceType> mPersistenceType;
   OriginScope mOriginScope;
   Nullable<Client::Type> mClientType;
   mozilla::Atomic<bool> mCanceled;
   const bool mExclusive;
+  bool mNeedsDirectoryLocking;
 
  public:
   void RunImmediately() {
     MOZ_ASSERT(GetState() == State_Initial);
 
     MOZ_ALWAYS_SUCCEEDS(this->Run());
   }
 
  protected:
   NormalOriginOperationBase(const Nullable<PersistenceType>& aPersistenceType,
                             const OriginScope& aOriginScope, bool aExclusive)
       : mPersistenceType(aPersistenceType),
         mOriginScope(aOriginScope),
-        mExclusive(aExclusive) {
+        mExclusive(aExclusive),
+        mNeedsDirectoryLocking(true) {
     AssertIsOnOwningThread();
   }
 
   ~NormalOriginOperationBase() {}
 
  private:
   // Need to declare refcounting unconditionally, because
   // OpenDirectoryListener has pure-virtual refcounting.
@@ -1181,33 +1181,26 @@ 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 {
-  // If mGetGroupUsage is false, we use mUsageInfo to record the origin usage
-  // and the file usage. Otherwise, we use it to record the group usage and the
-  // limit.
   UsageInfo mUsageInfo;
-
-  const OriginUsageParams mParams;
   nsCString mSuffix;
   nsCString mGroup;
-  bool mGetGroupUsage;
+  bool mFromMemory;
 
  public:
   explicit GetOriginUsageOp(const UsageRequestParams& aParams);
 
-  MOZ_IS_CLASS_INIT bool Init(Quota* aQuota) override;
-
  private:
-  ~GetOriginUsageOp() {}
+  ~GetOriginUsageOp() = default;
 
   virtual nsresult DoDirectoryWork(QuotaManager* aQuotaManager) override;
 
   void GetResponse(UsageRequestResponse& aResponse) override;
 };
 
 class QuotaRequestBase : public NormalOriginOperationBase,
                          public PQuotaRequestParent {
@@ -1376,16 +1369,32 @@ class PersistOp final : public PersistRe
  private:
   ~PersistOp() {}
 
   nsresult DoDirectoryWork(QuotaManager* aQuotaManager) override;
 
   void GetResponse(RequestResponse& aResponse) override;
 };
 
+class EstimateOp final : public QuotaRequestBase {
+  nsCString mGroup;
+  uint64_t mUsage;
+  uint64_t mLimit;
+
+ public:
+  explicit EstimateOp(const RequestParams& aParams);
+
+ private:
+  ~EstimateOp() = default;
+
+  virtual nsresult DoDirectoryWork(QuotaManager* aQuotaManager) override;
+
+  void GetResponse(RequestResponse& aResponse) override;
+};
+
 class ListInitializedOriginsOp final : public QuotaRequestBase,
                                        public TraverseRepositoryHelper {
   // XXX Bug 1521541 will make each origin has it's own state.
   nsTArray<nsCString> mOrigins;
 
  public:
   ListInitializedOriginsOp();
 
@@ -6066,46 +6075,65 @@ uint64_t QuotaManager::GetGroupLimit() c
   uint64_t x = std::min<uint64_t>(mTemporaryStorageLimit * .20, 2 GB);
 
   // In low-storage situations, make an exception (while not exceeding the total
   // storage limit).
   return std::min<uint64_t>(mTemporaryStorageLimit,
                             std::max<uint64_t>(x, 10 MB));
 }
 
-void QuotaManager::GetGroupUsageAndLimit(const nsACString& aGroup,
-                                         UsageInfo* aUsageInfo) {
-  AssertIsOnIOThread();
-  MOZ_ASSERT(aUsageInfo);
+uint64_t QuotaManager::GetGroupUsage(const nsACString& aGroup) {
+  AssertIsOnIOThread();
+
+  uint64_t usage = 0;
 
   {
     MutexAutoLock lock(mQuotaMutex);
 
-    aUsageInfo->SetLimit(GetGroupLimit());
-    aUsageInfo->ResetUsage();
+    GroupInfoPair* pair;
+    if (mGroupInfoPairs.Get(aGroup, &pair)) {
+      for (const PersistenceType type : kBestEffortPersistenceTypes) {
+        RefPtr<GroupInfo> groupInfo = pair->LockedGetGroupInfo(type);
+        if (groupInfo) {
+          AssertNoOverflow(usage, groupInfo->mUsage);
+          usage += groupInfo->mUsage;
+        }
+      }
+    }
+  }
+
+  return usage;
+}
+
+uint64_t QuotaManager::GetOriginUsage(const nsACString& aGroup,
+                                      const nsACString& aOrigin) {
+  AssertIsOnIOThread();
+
+  uint64_t usage = 0;
+
+  {
+    MutexAutoLock lock(mQuotaMutex);
 
     GroupInfoPair* pair;
-    if (!mGroupInfoPairs.Get(aGroup, &pair)) {
-      return;
-    }
-
-    // Calculate temporary group usage
-    RefPtr<GroupInfo> temporaryGroupInfo =
-        pair->LockedGetGroupInfo(PERSISTENCE_TYPE_TEMPORARY);
-    if (temporaryGroupInfo) {
-      aUsageInfo->AppendToDatabaseUsage(temporaryGroupInfo->mUsage);
-    }
-
-    // Calculate default group usage
-    RefPtr<GroupInfo> defaultGroupInfo =
-        pair->LockedGetGroupInfo(PERSISTENCE_TYPE_DEFAULT);
-    if (defaultGroupInfo) {
-      aUsageInfo->AppendToDatabaseUsage(defaultGroupInfo->mUsage);
-    }
-  }
+    if (mGroupInfoPairs.Get(aGroup, &pair)) {
+      for (const PersistenceType type : kBestEffortPersistenceTypes) {
+        RefPtr<GroupInfo> groupInfo = pair->LockedGetGroupInfo(type);
+        if (groupInfo) {
+          RefPtr<OriginInfo> originInfo =
+              groupInfo->LockedGetOriginInfo(aOrigin);
+          if (originInfo) {
+            AssertNoOverflow(usage, originInfo->LockedUsage());
+            usage += originInfo->LockedUsage();
+          }
+        }
+      }
+    }
+  }
+
+  return usage;
 }
 
 void QuotaManager::NotifyStoragePressure(uint64_t aUsage) {
   mQuotaMutex.AssertNotCurrentThreadOwns();
 
   RefPtr<StoragePressureRunnable> storagePressureRunnable =
       new StoragePressureRunnable(aUsage);
 
@@ -7126,27 +7154,37 @@ NS_IMPL_ISUPPORTS_INHERITED0(NormalOrigi
 
 void NormalOriginOperationBase::Open() {
   AssertIsOnOwningThread();
   MOZ_ASSERT(GetState() == State_CreatingQuotaManager);
   MOZ_ASSERT(QuotaManager::Get());
 
   AdvanceState();
 
-  QuotaManager::Get()->OpenDirectoryInternal(mPersistenceType, mOriginScope,
-                                             mClientType, mExclusive, this);
+  if (mNeedsDirectoryLocking) {
+    QuotaManager::Get()->OpenDirectoryInternal(mPersistenceType, mOriginScope,
+                                               mClientType, mExclusive, this);
+  } else {
+    nsresult rv = DirectoryOpen();
+    if (NS_WARN_IF(NS_FAILED(rv))) {
+      Finish(rv);
+      return;
+    }
+  }
 }
 
 void NormalOriginOperationBase::UnblockOpen() {
   AssertIsOnOwningThread();
   MOZ_ASSERT(GetState() == State_UnblockingOpen);
 
   SendResults();
 
-  mDirectoryLock = nullptr;
+  if (mNeedsDirectoryLocking) {
+    mDirectoryLock = nullptr;
+  }
 
   AdvanceState();
 }
 
 void NormalOriginOperationBase::DirectoryLockAcquired(DirectoryLock* aLock) {
   AssertIsOnOwningThread();
   MOZ_ASSERT(aLock);
   MOZ_ASSERT(GetState() == State_DirectoryOpenPending);
@@ -7365,16 +7403,28 @@ bool Quota::VerifyRequestParams(const Re
               !QuotaManager::IsPrincipalInfoValid(params.principalInfo()))) {
         ASSERT_UNLESS_FUZZING();
         return false;
       }
 
       break;
     }
 
+    case RequestParams::TEstimateParams: {
+      const EstimateParams& params = aParams.get_EstimateParams();
+
+      if (NS_WARN_IF(
+              !QuotaManager::IsPrincipalInfoValid(params.principalInfo()))) {
+        ASSERT_UNLESS_FUZZING();
+        return false;
+      }
+
+      break;
+    }
+
     default:
       MOZ_CRASH("Should never get here!");
   }
 
   return true;
 }
 
 void Quota::ActorDestroy(ActorDestroyReason aWhy) {
@@ -7501,16 +7551,20 @@ PQuotaRequestParent* Quota::AllocPQuotaR
     case RequestParams::TPersistedParams:
       actor = new PersistedOp(aParams);
       break;
 
     case RequestParams::TPersistParams:
       actor = new PersistOp(aParams);
       break;
 
+    case RequestParams::TEstimateParams:
+      actor = new EstimateOp(aParams);
+      break;
+
     case RequestParams::TListInitializedOriginsParams:
       actor = new ListInitializedOriginsOp();
       break;
 
     default:
       MOZ_CRASH("Should never get here!");
   }
 
@@ -7970,66 +8024,66 @@ void GetUsageOp::GetResponse(UsageReques
   if (!mOriginUsages.IsEmpty()) {
     nsTArray<OriginUsage>& originUsages =
         aResponse.get_AllUsageResponse().originUsages();
 
     mOriginUsages.SwapElements(originUsages);
   }
 }
 
-GetOriginUsageOp::GetOriginUsageOp(const UsageRequestParams& aParams)
-    : mParams(aParams.get_OriginUsageParams()),
-      mGetGroupUsage(aParams.get_OriginUsageParams().getGroupUsage()) {
+GetOriginUsageOp::GetOriginUsageOp(const UsageRequestParams& aParams) {
   AssertIsOnOwningThread();
   MOZ_ASSERT(aParams.type() == UsageRequestParams::TOriginUsageParams);
-}
-
-bool GetOriginUsageOp::Init(Quota* aQuota) {
-  AssertIsOnOwningThread();
-  MOZ_ASSERT(aQuota);
-
-  if (NS_WARN_IF(!QuotaUsageRequestBase::Init(aQuota))) {
-    return false;
-  }
-
-  // Figure out which origin we're dealing with.
-  nsCString origin;
-  QuotaManager::GetInfoFromValidatedPrincipalInfo(mParams.principalInfo(),
-                                                  &mSuffix, &mGroup, &origin);
-
-  mOriginScope.SetFromOrigin(origin);
-
-  return true;
+
+  const OriginUsageParams& params = aParams.get_OriginUsageParams();
+
+  QuotaManager::GetInfoFromValidatedPrincipalInfo(
+      params.principalInfo(), &mSuffix, &mGroup, mOriginScope.AsOriginSetter());
+
+  mFromMemory = params.fromMemory();
+
+  // Overwrite NormalOriginOperationBase default values.
+  if (mFromMemory) {
+    mNeedsDirectoryLocking = false;
+  }
+
+  // Overwrite OriginOperationBase default values.
+  mNeedsQuotaManagerInit = true;
 }
 
 nsresult GetOriginUsageOp::DoDirectoryWork(QuotaManager* aQuotaManager) {
   AssertIsOnIOThread();
   MOZ_ASSERT(mUsageInfo.TotalUsage() == 0);
 
   AUTO_PROFILER_LABEL("GetOriginUsageOp::DoDirectoryWork", OTHER);
 
   nsresult rv;
 
-  if (mGetGroupUsage) {
-    // Ensure temporary storage is initialized first. It will initialize all
-    // origins for temporary storage including origins belonging to our group by
-    // traversing the repositories. EnsureStorageIsInitialized is needed before
-    // EnsureTemporaryStorageIsInitialized.
+  if (mFromMemory) {
+    // This must be called before EnsureTemporaryStorageIsInitialized.
     rv = aQuotaManager->EnsureStorageIsInitialized();
     if (NS_WARN_IF(NS_FAILED(rv))) {
       return rv;
     }
 
+    // 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 and limit (the method doesn't have to stat any files).
-    aQuotaManager->GetGroupUsageAndLimit(mGroup, &mUsageInfo);
+    // 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);
 
     return NS_OK;
   }
 
   // Add all the persistent/temporary/default storage files we care about.
   for (const PersistenceType type : kAllPersistenceTypes) {
     UsageInfo usageInfo;
     rv = GetUsageForOrigin(aQuotaManager, type, mGroup,
@@ -8044,25 +8098,18 @@ nsresult GetOriginUsageOp::DoDirectoryWo
   return NS_OK;
 }
 
 void GetOriginUsageOp::GetResponse(UsageRequestResponse& aResponse) {
   AssertIsOnOwningThread();
 
   OriginUsageResponse usageResponse;
 
-  // We'll get the group usage when mGetGroupUsage is true and get the
-  // origin usage when mGetGroupUsage is false.
   usageResponse.usage() = mUsageInfo.TotalUsage();
-
-  if (mGetGroupUsage) {
-    usageResponse.limit() = mUsageInfo.Limit();
-  } else {
-    usageResponse.fileUsage() = mUsageInfo.FileUsage();
-  }
+  usageResponse.fileUsage() = mUsageInfo.FileUsage();
 
   aResponse = usageResponse;
 }
 
 bool QuotaRequestBase::Init(Quota* aQuota) {
   AssertIsOnOwningThread();
   MOZ_ASSERT(aQuota);
 
@@ -8751,16 +8798,70 @@ nsresult PersistOp::DoDirectoryWork(Quot
 }
 
 void PersistOp::GetResponse(RequestResponse& aResponse) {
   AssertIsOnOwningThread();
 
   aResponse = PersistResponse();
 }
 
+EstimateOp::EstimateOp(const RequestParams& aParams)
+    : QuotaRequestBase(/* aExclusive */ false), mUsage(0), mLimit(0) {
+  AssertIsOnOwningThread();
+  MOZ_ASSERT(aParams.type() == RequestParams::TEstimateParams);
+
+  QuotaManager::GetInfoFromValidatedPrincipalInfo(
+      aParams.get_EstimateParams().principalInfo(), nullptr, &mGroup, nullptr);
+
+  // Overwrite NormalOriginOperationBase default values.
+  mNeedsDirectoryLocking = false;
+
+  // Overwrite OriginOperationBase default values.
+  mNeedsQuotaManagerInit = true;
+}
+
+nsresult EstimateOp::DoDirectoryWork(QuotaManager* aQuotaManager) {
+  AssertIsOnIOThread();
+
+  AUTO_PROFILER_LABEL("EstimateOp::DoDirectoryWork", OTHER);
+
+  // This must be called before EnsureTemporaryStorageIsInitialized.
+  nsresult rv = aQuotaManager->EnsureStorageIsInitialized();
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+
+  // 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 origins belonging
+  // to our group).
+  rv = aQuotaManager->EnsureTemporaryStorageIsInitialized();
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+
+  // Get cached usage (the method doesn't have to stat any files).
+  mUsage = aQuotaManager->GetGroupUsage(mGroup);
+
+  mLimit = aQuotaManager->GetGroupLimit();
+
+  return NS_OK;
+}
+
+void EstimateOp::GetResponse(RequestResponse& aResponse) {
+  AssertIsOnOwningThread();
+
+  EstimateResponse estimateResponse;
+
+  estimateResponse.usage() = mUsage;
+  estimateResponse.limit() = mLimit;
+
+  aResponse = estimateResponse;
+}
+
 ListInitializedOriginsOp::ListInitializedOriginsOp()
     : QuotaRequestBase(/* aExclusive */ false), TraverseRepositoryHelper() {
   AssertIsOnOwningThread();
 }
 
 bool ListInitializedOriginsOp::Init(Quota* aQuota) {
   AssertIsOnOwningThread();
   MOZ_ASSERT(aQuota);
--- a/dom/quota/OriginScope.h
+++ b/dom/quota/OriginScope.h
@@ -227,16 +227,58 @@ class OriginScope {
       bool operator()(const Null& aOther) { return true; }
     };
 
     return aOther.mData.match(Matcher(*this));
   }
 
   OriginScope Clone() { return OriginScope(mData); }
 
+  template <typename T, typename U>
+  class Setter {
+    typedef void (OriginScope::*Method)(const U& aOrigin);
+
+    OriginScope* mOriginScope;
+    Method mMethod;
+    T mData;
+
+   public:
+    Setter(OriginScope* aOriginScope, Method aMethod)
+        : mOriginScope(aOriginScope), mMethod(aMethod) {}
+
+    Setter(Setter&& aOther)
+        : mOriginScope(aOther.mOriginScope),
+          mMethod(aOther.mMethod),
+          mData(std::move(aOther.mData)) {}
+
+    ~Setter() { ((*mOriginScope).*mMethod)(mData); }
+
+    operator T&() { return mData; }
+
+    operator T*() { return &mData; }
+
+   private:
+    Setter() = delete;
+    Setter(const Setter&) = delete;
+    Setter& operator=(const Setter&) = delete;
+    Setter& operator=(const Setter&&) = delete;
+  };
+
+  /**
+   * Magic helper for cases where you are calling a method that takes a
+   * ns(A)CString outparam to write an origin into. This method returns a helper
+   * temporary that returns the underlying string storage, then on the
+   * destruction of the temporary at the conclusion of the call, automatically
+   * invokes SetFromOrigin using the origin value that was written into the
+   * string.
+   */
+  Setter<nsCString, nsACString> AsOriginSetter() {
+    return Setter<nsCString, nsACString>(this, &OriginScope::SetFromOrigin);
+  }
+
  private:
   // Move constructors
   explicit OriginScope(const Origin&& aOrigin) : mData(aOrigin) {}
 
   explicit OriginScope(const Prefix&& aPrefix) : mData(aPrefix) {}
 
   explicit OriginScope(const Pattern&& aPattern) : mData(aPattern) {}
 
--- a/dom/quota/PQuota.ipdl
+++ b/dom/quota/PQuota.ipdl
@@ -43,17 +43,17 @@ struct InitOriginParams
 struct AllUsageParams
 {
   bool getAll;
 };
 
 struct OriginUsageParams
 {
   PrincipalInfo principalInfo;
-  bool getGroupUsage;
+  bool fromMemory;
 };
 
 union UsageRequestParams
 {
   AllUsageParams;
   OriginUsageParams;
 };
 
@@ -95,32 +95,38 @@ struct PersistedParams
   PrincipalInfo principalInfo;
 };
 
 struct PersistParams
 {
   PrincipalInfo principalInfo;
 };
 
+struct EstimateParams
+{
+  PrincipalInfo principalInfo;
+};
+
 struct ListInitializedOriginsParams
 {
 };
 
 union RequestParams
 {
   InitParams;
   InitTemporaryStorageParams;
   InitOriginParams;
   ClearOriginParams;
   ResetOriginParams;
   ClearDataParams;
   ClearAllParams;
   ResetAllParams;
   PersistedParams;
   PersistParams;
+  EstimateParams;
   ListInitializedOriginsParams;
 };
 
 protocol PQuota
 {
   manager PBackground;
 
   manages PQuotaRequest;
--- a/dom/quota/PQuotaRequest.ipdl
+++ b/dom/quota/PQuotaRequest.ipdl
@@ -45,16 +45,22 @@ struct PersistedResponse
 {
   bool persisted;
 };
 
 struct PersistResponse
 {
 };
 
+struct EstimateResponse
+{
+  uint64_t usage;
+  uint64_t limit;
+};
+
 struct ListInitializedOriginsResponse
 {
   nsCString[] origins;
 };
 
 union RequestResponse
 {
   nsresult;
@@ -63,16 +69,17 @@ union RequestResponse
   InitOriginResponse;
   ClearOriginResponse;
   ResetOriginResponse;
   ClearDataResponse;
   ClearAllResponse;
   ResetAllResponse;
   PersistedResponse;
   PersistResponse;
+  EstimateResponse;
   ListInitializedOriginsResponse;
 };
 
 protocol PQuotaRequest
 {
   manager PQuota;
 
 child:
--- a/dom/quota/PQuotaUsageRequest.ipdl
+++ b/dom/quota/PQuotaUsageRequest.ipdl
@@ -20,17 +20,16 @@ struct AllUsageResponse
 {
   OriginUsage[] originUsages;
 };
 
 struct OriginUsageResponse
 {
   uint64_t usage;
   uint64_t fileUsage;
-  uint64_t limit;
 };
 
 union UsageRequestResponse
 {
   nsresult;
   AllUsageResponse;
   OriginUsageResponse;
 };
--- a/dom/quota/PersistenceType.h
+++ b/dom/quota/PersistenceType.h
@@ -17,16 +17,19 @@ enum PersistenceType {
   PERSISTENCE_TYPE_PERSISTENT = 0,
   PERSISTENCE_TYPE_TEMPORARY,
   PERSISTENCE_TYPE_DEFAULT,
 
   // Only needed for IPC serialization helper, should never be used in code.
   PERSISTENCE_TYPE_INVALID
 };
 
+static const PersistenceType kBestEffortPersistenceTypes[] = {
+    PERSISTENCE_TYPE_TEMPORARY, PERSISTENCE_TYPE_DEFAULT};
+
 static const PersistenceType kAllPersistenceTypes[] = {
     PERSISTENCE_TYPE_PERSISTENT, PERSISTENCE_TYPE_TEMPORARY,
     PERSISTENCE_TYPE_DEFAULT};
 
 inline void PersistenceTypeToText(PersistenceType aPersistenceType,
                                   nsACString& aText) {
   switch (aPersistenceType) {
     case PERSISTENCE_TYPE_PERSISTENT:
--- a/dom/quota/QuotaManager.h
+++ b/dom/quota/QuotaManager.h
@@ -379,17 +379,19 @@ class QuotaManager final : public Backgr
 
     MOZ_ASSERT(aPersistenceType == PERSISTENCE_TYPE_DEFAULT);
 
     return mDefaultStoragePath;
   }
 
   uint64_t GetGroupLimit() const;
 
-  void GetGroupUsageAndLimit(const nsACString& aGroup, UsageInfo* aUsageInfo);
+  uint64_t GetGroupUsage(const nsACString& aGroup);
+
+  uint64_t GetOriginUsage(const nsACString& aGroup, const nsACString& aOrigin);
 
   void NotifyStoragePressure(uint64_t aUsage);
 
   static void GetStorageId(PersistenceType aPersistenceType,
                            const nsACString& aOrigin, Client::Type aClientType,
                            nsACString& aDatabaseId);
 
   static bool IsPrincipalInfoValid(const PrincipalInfo& aPrincipalInfo);
--- a/dom/quota/QuotaManagerService.cpp
+++ b/dom/quota/QuotaManagerService.cpp
@@ -495,33 +495,33 @@ QuotaManagerService::GetUsage(nsIQuotaUs
 
   request.forget(_retval);
   return NS_OK;
 }
 
 NS_IMETHODIMP
 QuotaManagerService::GetUsageForPrincipal(nsIPrincipal* aPrincipal,
                                           nsIQuotaUsageCallback* aCallback,
-                                          bool aGetGroupUsage,
+                                          bool aFromMemory,
                                           nsIQuotaUsageRequest** _retval) {
   MOZ_ASSERT(NS_IsMainThread());
   MOZ_ASSERT(aPrincipal);
   MOZ_ASSERT(aCallback);
 
   RefPtr<UsageRequest> request = new UsageRequest(aPrincipal, aCallback);
 
   OriginUsageParams params;
 
   nsresult rv =
       CheckedPrincipalToPrincipalInfo(aPrincipal, params.principalInfo());
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return rv;
   }
 
-  params.getGroupUsage() = aGetGroupUsage;
+  params.fromMemory() = aFromMemory;
 
   nsAutoPtr<PendingRequestInfo> info(new UsageRequestInfo(request, params));
 
   rv = InitiateRequest(info);
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return rv;
   }
 
@@ -736,16 +736,43 @@ QuotaManagerService::Persist(nsIPrincipa
     return rv;
   }
 
   request.forget(_retval);
   return NS_OK;
 }
 
 NS_IMETHODIMP
+QuotaManagerService::Estimate(nsIPrincipal* aPrincipal,
+                              nsIQuotaRequest** _retval) {
+  MOZ_ASSERT(NS_IsMainThread());
+  MOZ_ASSERT(aPrincipal);
+
+  RefPtr<Request> request = new Request(aPrincipal);
+
+  EstimateParams params;
+
+  nsresult rv =
+      CheckedPrincipalToPrincipalInfo(aPrincipal, params.principalInfo());
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+
+  nsAutoPtr<PendingRequestInfo> info(new RequestInfo(request, params));
+
+  rv = InitiateRequest(info);
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+
+  request.forget(_retval);
+  return NS_OK;
+}
+
+NS_IMETHODIMP
 QuotaManagerService::ListInitializedOrigins(nsIQuotaCallback* aCallback,
                                             nsIQuotaRequest** _retval) {
   MOZ_ASSERT(NS_IsMainThread());
   MOZ_ASSERT(aCallback);
 
   RefPtr<Request> request = new Request(aCallback);
 
   ListInitializedOriginsParams params;
--- a/dom/quota/QuotaResults.cpp
+++ b/dom/quota/QuotaResults.cpp
@@ -44,19 +44,18 @@ UsageResult::GetUsage(uint64_t* aUsage) 
 NS_IMETHODIMP
 UsageResult::GetLastAccessed(uint64_t* aLastAccessed) {
   MOZ_ASSERT(aLastAccessed);
 
   *aLastAccessed = mLastAccessed;
   return NS_OK;
 }
 
-OriginUsageResult::OriginUsageResult(uint64_t aUsage, uint64_t aFileUsage,
-                                     uint64_t aLimit)
-    : mUsage(aUsage), mFileUsage(aFileUsage), mLimit(aLimit) {}
+OriginUsageResult::OriginUsageResult(uint64_t aUsage, uint64_t aFileUsage)
+    : mUsage(aUsage), mFileUsage(aFileUsage) {}
 
 NS_IMPL_ISUPPORTS(OriginUsageResult, nsIQuotaOriginUsageResult)
 
 NS_IMETHODIMP
 OriginUsageResult::GetUsage(uint64_t* aUsage) {
   MOZ_ASSERT(aUsage);
 
   *aUsage = mUsage;
@@ -66,18 +65,31 @@ OriginUsageResult::GetUsage(uint64_t* aU
 NS_IMETHODIMP
 OriginUsageResult::GetFileUsage(uint64_t* aFileUsage) {
   MOZ_ASSERT(aFileUsage);
 
   *aFileUsage = mFileUsage;
   return NS_OK;
 }
 
+EstimateResult::EstimateResult(uint64_t aUsage, uint64_t aLimit)
+    : mUsage(aUsage), mLimit(aLimit) {}
+
+NS_IMPL_ISUPPORTS(EstimateResult, nsIQuotaEstimateResult)
+
 NS_IMETHODIMP
-OriginUsageResult::GetLimit(uint64_t* aLimit) {
+EstimateResult::GetUsage(uint64_t* aUsage) {
+  MOZ_ASSERT(aUsage);
+
+  *aUsage = mUsage;
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+EstimateResult::GetLimit(uint64_t* aLimit) {
   MOZ_ASSERT(aLimit);
 
   *aLimit = mLimit;
   return NS_OK;
 }
 
 InitializedOriginsResult::InitializedOriginsResult(const nsACString& aOrigin)
     : mOrigin(aOrigin) {}
--- a/dom/quota/QuotaResults.h
+++ b/dom/quota/QuotaResults.h
@@ -28,28 +28,41 @@ class UsageResult : public nsIQuotaUsage
 
   NS_DECL_ISUPPORTS
   NS_DECL_NSIQUOTAUSAGERESULT
 };
 
 class OriginUsageResult : public nsIQuotaOriginUsageResult {
   uint64_t mUsage;
   uint64_t mFileUsage;
-  uint64_t mLimit;
 
  public:
-  OriginUsageResult(uint64_t aUsage, uint64_t aFileUsage, uint64_t aLimit);
+  OriginUsageResult(uint64_t aUsage, uint64_t aFileUsage);
 
  private:
   virtual ~OriginUsageResult() {}
 
   NS_DECL_ISUPPORTS
   NS_DECL_NSIQUOTAORIGINUSAGERESULT
 };
 
+class EstimateResult : public nsIQuotaEstimateResult {
+  uint64_t mUsage;
+  uint64_t mLimit;
+
+ public:
+  EstimateResult(uint64_t aUsage, uint64_t aLimit);
+
+ private:
+  virtual ~EstimateResult() {}
+
+  NS_DECL_ISUPPORTS
+  NS_DECL_NSIQUOTAESTIMATERESULT
+};
+
 class InitializedOriginsResult : public nsIQuotaInitializedOriginsResult {
   nsCString mOrigin;
 
  public:
   explicit InitializedOriginsResult(const nsACString& aOrigin);
 
  private:
   virtual ~InitializedOriginsResult() = default;
--- a/dom/quota/StorageManager.cpp
+++ b/dom/quota/StorageManager.cpp
@@ -22,18 +22,17 @@ using namespace mozilla::dom::quota;
 
 namespace mozilla {
 namespace dom {
 
 namespace {
 
 // This class is used to get quota usage, request persist and check persisted
 // status callbacks.
-class RequestResolver final : public nsIQuotaCallback,
-                              public nsIQuotaUsageCallback {
+class RequestResolver final : public nsIQuotaCallback {
  public:
   enum Type { Estimate, Persist, Persisted };
 
  private:
   class FinishWorkerRunnable;
 
   // If this resolver was created for a window then mPromise must be non-null.
   // Otherwise mProxy must be non-null.
@@ -60,27 +59,25 @@ class RequestResolver final : public nsI
     MOZ_ASSERT(NS_IsMainThread());
     MOZ_ASSERT(aProxy);
   }
 
   void ResolveOrReject();
 
   NS_DECL_THREADSAFE_ISUPPORTS
   NS_DECL_NSIQUOTACALLBACK
-  NS_DECL_NSIQUOTAUSAGECALLBACK
 
  private:
   ~RequestResolver() {}
 
   nsresult GetStorageEstimate(nsIVariant* aResult);
 
   nsresult GetPersisted(nsIVariant* aResult);
 
-  template <typename T>
-  nsresult OnCompleteOrUsageResult(T* aRequest);
+  nsresult OnCompleteInternal(nsIQuotaRequest* aRequest);
 
   nsresult Finish();
 };
 
 // This class is used to return promise on worker thread.
 class RequestResolver::FinishWorkerRunnable final : public WorkerRunnable {
   RefPtr<RequestResolver> mResolver;
 
@@ -159,34 +156,45 @@ class PersistentStoragePermissionRequest
   // nsIContentPermissionRequest
   NS_IMETHOD Cancel(void) override;
   NS_IMETHOD Allow(JS::HandleValue choices) override;
 
  private:
   ~PersistentStoragePermissionRequest() = default;
 };
 
-nsresult GetUsageForPrincipal(nsIPrincipal* aPrincipal,
-                              nsIQuotaUsageCallback* aCallback,
-                              nsIQuotaUsageRequest** aRequest) {
+nsresult Estimate(nsIPrincipal* aPrincipal, nsIQuotaCallback* aCallback,
+                  nsIQuotaRequest** aRequest) {
   MOZ_ASSERT(aPrincipal);
   MOZ_ASSERT(aCallback);
   MOZ_ASSERT(aRequest);
 
+  // Firefox and Quota Manager have always used the schemeless origin group
+  // (https://storage.spec.whatwg.org/#schemeless-origin-group) for quota limit
+  // purposes. This has been to prevent a site/eTLD+1 from claiming more than
+  // its fair share of storage through the use of sub-domains. Because the limit
+  // is at the group level and the usage needs to make sense in the context of
+  // that limit, we also expose the group usage. Bug 1374970 reflects this
+  // reality and bug 1305665 tracks our plan to eliminate our use of groups for
+  // this.
+
   nsCOMPtr<nsIQuotaManagerService> qms = QuotaManagerService::GetOrCreate();
   if (NS_WARN_IF(!qms)) {
     return NS_ERROR_FAILURE;
   }
 
-  nsresult rv =
-      qms->GetUsageForPrincipal(aPrincipal, aCallback, true, aRequest);
+  nsCOMPtr<nsIQuotaRequest> request;
+  nsresult rv = qms->Estimate(aPrincipal, getter_AddRefs(request));
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return rv;
   }
 
+  MOZ_ALWAYS_SUCCEEDS(request->SetCallback(aCallback));
+
+  request.forget(aRequest);
   return NS_OK;
 };
 
 nsresult Persisted(nsIPrincipal* aPrincipal, nsIQuotaCallback* aCallback,
                    nsIQuotaRequest** aRequest) {
   MOZ_ASSERT(aPrincipal);
   MOZ_ASSERT(aCallback);
   MOZ_ASSERT(aRequest);
@@ -270,19 +278,18 @@ already_AddRefed<Promise> ExecuteOpOnMai
 
         break;
       }
 
       case RequestResolver::Type::Estimate: {
         RefPtr<RequestResolver> resolver =
             new RequestResolver(RequestResolver::Type::Estimate, promise);
 
-        RefPtr<nsIQuotaUsageRequest> request;
-        aRv =
-            GetUsageForPrincipal(principal, resolver, getter_AddRefs(request));
+        RefPtr<nsIQuotaRequest> request;
+        aRv = Estimate(principal, resolver, getter_AddRefs(request));
 
         break;
       }
 
       default:
         MOZ_CRASH("Invalid aRequest type!");
     }
 
@@ -384,42 +391,41 @@ void RequestResolver::ResolveOrReject() 
 
   if (NS_SUCCEEDED(mResultCode)) {
     promise->MaybeResolve(mPersisted);
   } else {
     promise->MaybeResolve(false);
   }
 }
 
-NS_IMPL_ISUPPORTS(RequestResolver, nsIQuotaUsageCallback, nsIQuotaCallback)
+NS_IMPL_ISUPPORTS(RequestResolver, nsIQuotaCallback)
 
 nsresult RequestResolver::GetStorageEstimate(nsIVariant* aResult) {
   MOZ_ASSERT(aResult);
   MOZ_ASSERT(mType == Type::Estimate);
 
   MOZ_ASSERT(aResult->GetDataType() == nsIDataType::VTYPE_INTERFACE_IS);
 
   nsID* iid;
   nsCOMPtr<nsISupports> supports;
   nsresult rv = aResult->GetAsInterface(&iid, getter_AddRefs(supports));
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return rv;
   }
 
   free(iid);
 
-  nsCOMPtr<nsIQuotaOriginUsageResult> originUsageResult =
-      do_QueryInterface(supports);
-  MOZ_ASSERT(originUsageResult);
+  nsCOMPtr<nsIQuotaEstimateResult> estimateResult = do_QueryInterface(supports);
+  MOZ_ASSERT(estimateResult);
 
   MOZ_ALWAYS_SUCCEEDS(
-      originUsageResult->GetUsage(&mStorageEstimate.mUsage.Construct()));
+      estimateResult->GetUsage(&mStorageEstimate.mUsage.Construct()));
 
   MOZ_ALWAYS_SUCCEEDS(
-      originUsageResult->GetLimit(&mStorageEstimate.mQuota.Construct()));
+      estimateResult->GetLimit(&mStorageEstimate.mQuota.Construct()));
 
   return NS_OK;
 }
 
 nsresult RequestResolver::GetPersisted(nsIVariant* aResult) {
   MOZ_ASSERT(aResult);
   MOZ_ASSERT(mType == Type::Persist || mType == Type::Persisted);
 
@@ -441,18 +447,17 @@ nsresult RequestResolver::GetPersisted(n
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return rv;
   }
 
   mPersisted = persisted;
   return NS_OK;
 }
 
-template <typename T>
-nsresult RequestResolver::OnCompleteOrUsageResult(T* aRequest) {
+nsresult RequestResolver::OnCompleteInternal(nsIQuotaRequest* aRequest) {
   MOZ_ASSERT(NS_IsMainThread());
   MOZ_ASSERT(aRequest);
 
   nsresult resultCode;
   nsresult rv = aRequest->GetResultCode(&resultCode);
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return rv;
   }
@@ -507,32 +512,17 @@ nsresult RequestResolver::Finish() {
   return NS_OK;
 }
 
 NS_IMETHODIMP
 RequestResolver::OnComplete(nsIQuotaRequest* aRequest) {
   MOZ_ASSERT(NS_IsMainThread());
   MOZ_ASSERT(aRequest);
 
-  mResultCode = OnCompleteOrUsageResult(aRequest);
-
-  nsresult rv = Finish();
-  if (NS_WARN_IF(NS_FAILED(rv))) {
-    return rv;
-  }
-
-  return NS_OK;
-}
-
-NS_IMETHODIMP
-RequestResolver::OnUsageResult(nsIQuotaUsageRequest* aRequest) {
-  MOZ_ASSERT(NS_IsMainThread());
-  MOZ_ASSERT(aRequest);
-
-  mResultCode = OnCompleteOrUsageResult(aRequest);
+  mResultCode = OnCompleteInternal(aRequest);
 
   nsresult rv = Finish();
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return rv;
   }
 
   return NS_OK;
 }
@@ -562,19 +552,18 @@ bool EstimateWorkerMainThreadRunnable::M
     principal = mProxy->GetWorkerPrivate()->GetPrincipal();
   }
 
   MOZ_ASSERT(principal);
 
   RefPtr<RequestResolver> resolver =
       new RequestResolver(RequestResolver::Type::Estimate, mProxy);
 
-  RefPtr<nsIQuotaUsageRequest> request;
-  nsresult rv =
-      GetUsageForPrincipal(principal, resolver, getter_AddRefs(request));
+  RefPtr<nsIQuotaRequest> request;
+  nsresult rv = Estimate(principal, resolver, getter_AddRefs(request));
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return false;
   }
 
   return true;
 }
 
 bool PersistedWorkerMainThreadRunnable::MainThreadRun() {
--- a/dom/quota/UsageInfo.h
+++ b/dom/quota/UsageInfo.h
@@ -11,64 +11,54 @@
 
 #include "mozilla/Atomics.h"
 #include "mozilla/CheckedInt.h"
 
 BEGIN_QUOTA_NAMESPACE
 
 class UsageInfo {
  public:
-  UsageInfo() : mDatabaseUsage(0), mFileUsage(0), mLimit(0) {}
+  UsageInfo() : mDatabaseUsage(0), mFileUsage(0) {}
 
   virtual ~UsageInfo() {}
 
   void Append(const UsageInfo& aUsageInfo) {
     IncrementUsage(&mDatabaseUsage, aUsageInfo.mDatabaseUsage);
     IncrementUsage(&mFileUsage, aUsageInfo.mFileUsage);
   }
 
   void AppendToDatabaseUsage(uint64_t aUsage) {
     IncrementUsage(&mDatabaseUsage, aUsage);
   }
 
   void AppendToFileUsage(uint64_t aUsage) {
     IncrementUsage(&mFileUsage, aUsage);
   }
 
-  void SetLimit(uint64_t aLimit) { mLimit = aLimit; }
-
   uint64_t DatabaseUsage() { return mDatabaseUsage; }
 
   uint64_t FileUsage() { return mFileUsage; }
 
-  uint64_t Limit() { return mLimit; }
-
   uint64_t TotalUsage() {
     uint64_t totalUsage = mDatabaseUsage;
     IncrementUsage(&totalUsage, mFileUsage);
     return totalUsage;
   }
 
-  void ResetUsage() {
-    mDatabaseUsage = 0;
-    mFileUsage = 0;
-  }
-
   static void IncrementUsage(uint64_t* aUsage, uint64_t aDelta) {
     MOZ_ASSERT(aUsage);
     CheckedUint64 value = *aUsage;
     value += aDelta;
     if (value.isValid()) {
       *aUsage = value.value();
     } else {
       *aUsage = UINT64_MAX;
     }
   }
 
  private:
   uint64_t mDatabaseUsage;
   uint64_t mFileUsage;
-  uint64_t mLimit;
 };
 
 END_QUOTA_NAMESPACE
 
 #endif  // mozilla_dom_quota_usageinfo_h__
--- a/dom/quota/nsIQuotaManagerService.idl
+++ b/dom/quota/nsIQuotaManagerService.idl
@@ -70,26 +70,28 @@ interface nsIQuotaManagerService : nsISu
   /**
    * Schedules an asynchronous callback that will return the total amount of
    * disk space being used by storages for the given origin.
    *
    * @param aPrincipal
    *        A principal for the origin whose usage is being queried.
    * @param aCallback
    *        The callback that will be called when the usage is available.
-   * @param aGetGroupUsage
-   *        An optional flag to indicate whether getting group usage and limit
-   *        or origin usage and file usage. The default value is false.
+   * @param aFromMemory
+   *        An optional flag to indicate whether the cached usage should be
+   *        obtained. The default value is false.  Note that this operation may
+   *        still be delayed by other operations on the QM I/O thread that are
+   *        peforming I/O.
    * Note:  Origin usage here represents total usage of an origin. However,
-   *        group usage here represents only non-persistent usage of a group.
+   *        cached usage here represents only non-persistent usage of an origin.
    */
   [must_use] nsIQuotaUsageRequest
   getUsageForPrincipal(in nsIPrincipal aPrincipal,
                        in nsIQuotaUsageCallback aCallback,
-                       [optional] in boolean aGetGroupUsage);
+                       [optional] in boolean aFromMemory);
 
   /**
    * Schedules an asynchronous callback that will inspect all origins and
    * just returns the origin strings of initialized origins.
    *
    * @param aCallback
    *        The callback that will be called when the origin is collected.
    */
@@ -216,9 +218,28 @@ interface nsIQuotaManagerService : nsISu
   /**
    * Persist given origin.
    *
    * @param aPrincipal
    *        A principal for the origin which we want to persist.
    */
   [must_use] nsIQuotaRequest
   persist(in nsIPrincipal aPrincipal);
+
+  /**
+   * Given an origin, asynchronously calculate its group quota usage and quota
+   * limit. An origin's group is the set of all origins that share the same
+   * eTLD+1. This method is intended to be used for our implementation of the
+   * StorageManager.estimate() method. When we fix bug 1305665 and stop tracking
+   * quota limits on a group basis, this method will switch to operating on
+   * origins. Callers should strongly consider whether they want to be using
+   * getUsageForPrincipal() instead.
+   *
+   * This mechanism uses cached quota values and does not perform any I/O on its
+   * own, but it may be delayed by QuotaManager operations that do need to
+   * perform I/O on the QuotaManager I/O thread.
+   *
+   * @param aPrincipal
+   *        A principal for the origin (group) which we want to estimate.
+   */
+  [must_use] nsIQuotaRequest
+  estimate(in nsIPrincipal aPrincipal);
 };
--- a/dom/quota/nsIQuotaResults.idl
+++ b/dom/quota/nsIQuotaResults.idl
@@ -19,16 +19,22 @@ interface nsIQuotaUsageResult : nsISuppo
 };
 
 [scriptable, function, uuid(96df03d2-116a-493f-bb0b-118c212a6b32)]
 interface nsIQuotaOriginUsageResult : nsISupports
 {
   readonly attribute unsigned long long usage;
 
   readonly attribute unsigned long long fileUsage;
+};
+
+[scriptable, function, uuid(9827fc69-7ea9-48ef-b30d-2e2ae0451ec0)]
+interface nsIQuotaEstimateResult : nsISupports
+{
+  readonly attribute unsigned long long usage;
 
   readonly attribute unsigned long long limit;
 };
 
 [scriptable, function, uuid(5d8c2fbe-9ccc-4bab-8f03-8591dfc8e351)]
 interface nsIQuotaInitializedOriginsResult : nsISupports
 {
   readonly attribute ACString origin;