tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

ServiceWorkerQuotaUtils.cpp (9446B)


      1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
      2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
      3 /* This Source Code Form is subject to the terms of the Mozilla Public
      4 * License, v. 2.0. If a copy of the MPL was not distributed with this
      5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
      6 
      7 #include "ServiceWorkerQuotaUtils.h"
      8 
      9 #include "MainThreadUtils.h"
     10 #include "mozilla/ScopeExit.h"
     11 #include "mozilla/Services.h"
     12 #include "mozilla/StaticPrefs_dom.h"
     13 #include "mozilla/dom/quota/QuotaManagerService.h"
     14 #include "nsIClearDataService.h"
     15 #include "nsID.h"
     16 #include "nsIPrincipal.h"
     17 #include "nsIQuotaCallbacks.h"
     18 #include "nsIQuotaRequests.h"
     19 #include "nsIQuotaResults.h"
     20 #include "nsISupports.h"
     21 #include "nsIVariant.h"
     22 #include "nsServiceManagerUtils.h"
     23 
     24 using mozilla::dom::quota::QuotaManagerService;
     25 
     26 namespace mozilla::dom {
     27 
     28 /*
     29 * QuotaUsageChecker implements the quota usage checking algorithm.
     30 *
     31 * 1. Getting the given origin/group usage through QuotaManagerService.
     32 *    QuotaUsageCheck::Start() implements this step.
     33 * 2. Checking if the group usage headroom is satisfied.
     34 *    It could be following three situations.
     35 *    a. Group headroom is satisfied without any usage mitigation.
     36 *    b. Group headroom is satisfied after origin usage mitigation.
     37 *       This invokes nsIClearDataService::DeleteDataFromPrincipal().
     38 *    c. Group headroom is satisfied after group usage mitigation.
     39 *       This invokes nsIClearDataService::DeleteDataFromSite().
     40 *    QuotaUsageChecker::CheckQuotaHeadroom() implements this step.
     41 *
     42 * If the algorithm is done or error out, the QuotaUsageCheck::mCallback will
     43 * be called with a bool result for external handling.
     44 */
     45 class QuotaUsageChecker final : public nsIQuotaCallback,
     46                                public nsIQuotaUsageCallback,
     47                                public nsIClearDataCallback {
     48 public:
     49  NS_DECL_ISUPPORTS
     50  // For QuotaManagerService::Estimate()
     51  NS_DECL_NSIQUOTACALLBACK
     52 
     53  // For QuotaManagerService::GetUsageForPrincipal()
     54  NS_DECL_NSIQUOTAUSAGECALLBACK
     55 
     56  // For nsIClearDataService::DeleteDataFromPrincipal() and
     57  // nsIClearDataService::DeleteDataFromSite()
     58  NS_DECL_NSICLEARDATACALLBACK
     59 
     60  QuotaUsageChecker(nsIPrincipal* aPrincipal,
     61                    ServiceWorkerQuotaMitigationCallback&& aCallback);
     62 
     63  void Start();
     64 
     65  void RunCallback(bool aResult);
     66 
     67 private:
     68  ~QuotaUsageChecker() = default;
     69 
     70  // This is a general help method to get nsIQuotaResult/nsIQuotaUsageResult
     71  // from nsIQuotaRequest/nsIQuotaUsageRequest
     72  template <typename T, typename U>
     73  nsresult GetResult(T* aRequest, U&);
     74 
     75  void CheckQuotaHeadroom();
     76 
     77  nsCOMPtr<nsIPrincipal> mPrincipal;
     78 
     79  // The external callback. Calling RunCallback(bool) instead of calling it
     80  // directly, RunCallback(bool) handles the internal status.
     81  ServiceWorkerQuotaMitigationCallback mCallback;
     82  bool mGettingOriginUsageDone;
     83  bool mGettingGroupUsageDone;
     84  bool mIsChecking;
     85  uint64_t mOriginUsage;
     86  uint64_t mGroupUsage;
     87  uint64_t mGroupLimit;
     88 };
     89 
     90 NS_IMPL_ISUPPORTS(QuotaUsageChecker, nsIQuotaCallback, nsIQuotaUsageCallback,
     91                  nsIClearDataCallback)
     92 
     93 QuotaUsageChecker::QuotaUsageChecker(
     94    nsIPrincipal* aPrincipal, ServiceWorkerQuotaMitigationCallback&& aCallback)
     95    : mPrincipal(aPrincipal),
     96      mCallback(std::move(aCallback)),
     97      mGettingOriginUsageDone(false),
     98      mGettingGroupUsageDone(false),
     99      mIsChecking(false),
    100      mOriginUsage(0),
    101      mGroupUsage(0),
    102      mGroupLimit(0) {}
    103 
    104 void QuotaUsageChecker::Start() {
    105  MOZ_ASSERT(NS_IsMainThread());
    106 
    107  if (mIsChecking) {
    108    return;
    109  }
    110  mIsChecking = true;
    111 
    112  RefPtr<QuotaUsageChecker> self = this;
    113  auto scopeExit = MakeScopeExit([self]() { self->RunCallback(false); });
    114 
    115  RefPtr<QuotaManagerService> qms = QuotaManagerService::GetOrCreate();
    116  MOZ_ASSERT(qms);
    117 
    118  // Asynchronious getting quota usage for principal
    119  nsCOMPtr<nsIQuotaUsageRequest> usageRequest;
    120  if (NS_WARN_IF(NS_FAILED(qms->GetUsageForPrincipal(
    121          mPrincipal, this, getter_AddRefs(usageRequest))))) {
    122    return;
    123  }
    124 
    125  // Asynchronious getting group usage and limit
    126  nsCOMPtr<nsIQuotaRequest> request;
    127  if (NS_WARN_IF(
    128          NS_FAILED(qms->Estimate(mPrincipal, getter_AddRefs(request))))) {
    129    return;
    130  }
    131  request->SetCallback(this);
    132 
    133  scopeExit.release();
    134 }
    135 
    136 void QuotaUsageChecker::RunCallback(bool aResult) {
    137  MOZ_ASSERT(mIsChecking && mCallback);
    138  if (!mIsChecking) {
    139    return;
    140  }
    141  mIsChecking = false;
    142  mGettingOriginUsageDone = false;
    143  mGettingGroupUsageDone = false;
    144 
    145  mCallback(aResult);
    146  mCallback = nullptr;
    147 }
    148 
    149 template <typename T, typename U>
    150 nsresult QuotaUsageChecker::GetResult(T* aRequest, U& aResult) {
    151  nsCOMPtr<nsIVariant> result;
    152  nsresult rv = aRequest->GetResult(getter_AddRefs(result));
    153  if (NS_WARN_IF(NS_FAILED(rv))) {
    154    return rv;
    155  }
    156 
    157  nsID* iid;
    158  nsCOMPtr<nsISupports> supports;
    159  rv = result->GetAsInterface(&iid, getter_AddRefs(supports));
    160  if (NS_WARN_IF(NS_FAILED(rv))) {
    161    return rv;
    162  }
    163 
    164  free(iid);
    165 
    166  aResult = do_QueryInterface(supports);
    167  return NS_OK;
    168 }
    169 
    170 void QuotaUsageChecker::CheckQuotaHeadroom() {
    171  MOZ_ASSERT(NS_IsMainThread());
    172 
    173  const uint64_t groupHeadroom =
    174      static_cast<uint64_t>(
    175          StaticPrefs::
    176              dom_serviceWorkers_mitigations_group_usage_headroom_kb()) *
    177      1024;
    178  const uint64_t groupAvailable = mGroupLimit - mGroupUsage;
    179 
    180  // Group usage head room is satisfied, does not need the usage mitigation.
    181  if (groupAvailable > groupHeadroom) {
    182    RunCallback(true);
    183    return;
    184  }
    185 
    186  RefPtr<QuotaUsageChecker> self = this;
    187  auto scopeExit = MakeScopeExit([self]() { self->RunCallback(false); });
    188 
    189  nsCOMPtr<nsIClearDataService> csd =
    190      do_GetService("@mozilla.org/clear-data-service;1");
    191  MOZ_ASSERT(csd);
    192 
    193  // Group usage headroom is not satisfied even removing the origin usage,
    194  // clear all group usage.
    195  if ((groupAvailable + mOriginUsage) < groupHeadroom) {
    196    nsAutoCString baseDomain;
    197    nsresult rv = mPrincipal->GetBaseDomain(baseDomain);
    198    if (NS_WARN_IF(NS_FAILED(rv))) {
    199      return;
    200    }
    201    rv = csd->DeleteDataFromSiteAndOriginAttributesPatternString(
    202        baseDomain, u""_ns, false, nsIClearDataService::CLEAR_DOM_QUOTA, this);
    203    if (NS_WARN_IF(NS_FAILED(rv))) {
    204      return;
    205    }
    206 
    207    // clear the origin usage since it makes group usage headroom be satisifed.
    208  } else {
    209    nsresult rv = csd->DeleteDataFromPrincipal(
    210        mPrincipal, false, nsIClearDataService::CLEAR_DOM_QUOTA, this);
    211    if (NS_WARN_IF(NS_FAILED(rv))) {
    212      return;
    213    }
    214  }
    215 
    216  scopeExit.release();
    217 }
    218 
    219 // nsIQuotaUsageCallback implementation
    220 
    221 NS_IMETHODIMP QuotaUsageChecker::OnUsageResult(
    222    nsIQuotaUsageRequest* aUsageRequest) {
    223  MOZ_ASSERT(NS_IsMainThread());
    224  MOZ_ASSERT(aUsageRequest);
    225 
    226  RefPtr<QuotaUsageChecker> self = this;
    227  auto scopeExit = MakeScopeExit([self]() { self->RunCallback(false); });
    228 
    229  nsresult resultCode;
    230  nsresult rv = aUsageRequest->GetResultCode(&resultCode);
    231  if (NS_WARN_IF(NS_FAILED(rv) || NS_FAILED(resultCode))) {
    232    return rv;
    233  }
    234 
    235  nsCOMPtr<nsIQuotaOriginUsageResult> usageResult;
    236  rv = GetResult(aUsageRequest, usageResult);
    237  if (NS_WARN_IF(NS_FAILED(rv))) {
    238    return rv;
    239  }
    240  MOZ_ASSERT(usageResult);
    241 
    242  rv = usageResult->GetUsage(&mOriginUsage);
    243  if (NS_WARN_IF(NS_FAILED(rv))) {
    244    return rv;
    245  }
    246 
    247  MOZ_ASSERT(!mGettingOriginUsageDone);
    248  mGettingOriginUsageDone = true;
    249 
    250  scopeExit.release();
    251 
    252  // Call CheckQuotaHeadroom() when both
    253  // QuotaManagerService::GetUsageForPrincipal() and
    254  // QuotaManagerService::Estimate() are done.
    255  if (mGettingOriginUsageDone && mGettingGroupUsageDone) {
    256    CheckQuotaHeadroom();
    257  }
    258  return NS_OK;
    259 }
    260 
    261 // nsIQuotaCallback implementation
    262 
    263 NS_IMETHODIMP QuotaUsageChecker::OnComplete(nsIQuotaRequest* aRequest) {
    264  MOZ_ASSERT(NS_IsMainThread());
    265  MOZ_ASSERT(aRequest);
    266 
    267  RefPtr<QuotaUsageChecker> self = this;
    268  auto scopeExit = MakeScopeExit([self]() { self->RunCallback(false); });
    269 
    270  nsresult resultCode;
    271  nsresult rv = aRequest->GetResultCode(&resultCode);
    272  if (NS_WARN_IF(NS_FAILED(rv) || NS_FAILED(resultCode))) {
    273    return rv;
    274  }
    275 
    276  nsCOMPtr<nsIQuotaEstimateResult> estimateResult;
    277  rv = GetResult(aRequest, estimateResult);
    278  if (NS_WARN_IF(NS_FAILED(rv))) {
    279    return rv;
    280  }
    281  MOZ_ASSERT(estimateResult);
    282 
    283  rv = estimateResult->GetUsage(&mGroupUsage);
    284  if (NS_WARN_IF(NS_FAILED(rv))) {
    285    return rv;
    286  }
    287 
    288  rv = estimateResult->GetLimit(&mGroupLimit);
    289  if (NS_WARN_IF(NS_FAILED(rv))) {
    290    return rv;
    291  }
    292 
    293  MOZ_ASSERT(!mGettingGroupUsageDone);
    294  mGettingGroupUsageDone = true;
    295 
    296  scopeExit.release();
    297 
    298  // Call CheckQuotaHeadroom() when both
    299  // QuotaManagerService::GetUsageForPrincipal() and
    300  // QuotaManagerService::Estimate() are done.
    301  if (mGettingOriginUsageDone && mGettingGroupUsageDone) {
    302    CheckQuotaHeadroom();
    303  }
    304  return NS_OK;
    305 }
    306 
    307 // nsIClearDataCallback implementation
    308 
    309 NS_IMETHODIMP QuotaUsageChecker::OnDataDeleted(uint32_t aFailedFlags) {
    310  RunCallback(true);
    311  return NS_OK;
    312 }
    313 
    314 // Helper methods in ServiceWorkerQuotaUtils.h
    315 
    316 void ClearQuotaUsageIfNeeded(nsIPrincipal* aPrincipal,
    317                             ServiceWorkerQuotaMitigationCallback&& aCallback) {
    318  MOZ_ASSERT(NS_IsMainThread());
    319  MOZ_ASSERT(aPrincipal);
    320 
    321  RefPtr<QuotaUsageChecker> checker =
    322      MakeRefPtr<QuotaUsageChecker>(aPrincipal, std::move(aCallback));
    323  checker->Start();
    324 }
    325 
    326 }  // namespace mozilla::dom