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/. */
7 #include "MainThreadUtils.h"
8 #include "ServiceWorkerQuotaUtils.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"
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"
24 using mozilla::dom::quota::QuotaManagerService
;
26 namespace mozilla::dom
{
29 * QuotaUsageChecker implements the quota usage checking algorithm.
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::DeleteDataFromBaseDomain().
40 * QuotaUsageChecker::CheckQuotaHeadroom() implements this step.
42 * If the algorithm is done or error out, the QuotaUsageCheck::mCallback will
43 * be called with a bool result for external handling.
45 class QuotaUsageChecker final
: public nsIQuotaCallback
,
46 public nsIQuotaUsageCallback
,
47 public nsIClearDataCallback
{
50 // For QuotaManagerService::Estimate()
51 NS_DECL_NSIQUOTACALLBACK
53 // For QuotaManagerService::GetUsageForPrincipal()
54 NS_DECL_NSIQUOTAUSAGECALLBACK
56 // For nsIClearDataService::DeleteDataFromPrincipal() and
57 // nsIClearDataService::DeleteDataFromBaseDomain()
58 NS_DECL_NSICLEARDATACALLBACK
60 QuotaUsageChecker(nsIPrincipal
* aPrincipal
,
61 ServiceWorkerQuotaMitigationCallback
&& aCallback
);
65 void RunCallback(bool aResult
);
68 ~QuotaUsageChecker() = default;
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
&);
75 void CheckQuotaHeadroom();
77 nsCOMPtr
<nsIPrincipal
> mPrincipal
;
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
;
85 uint64_t mOriginUsage
;
90 NS_IMPL_ISUPPORTS(QuotaUsageChecker
, nsIQuotaCallback
, nsIQuotaUsageCallback
,
93 QuotaUsageChecker::QuotaUsageChecker(
94 nsIPrincipal
* aPrincipal
, ServiceWorkerQuotaMitigationCallback
&& aCallback
)
95 : mPrincipal(aPrincipal
),
96 mCallback(std::move(aCallback
)),
97 mGettingOriginUsageDone(false),
98 mGettingGroupUsageDone(false),
104 void QuotaUsageChecker::Start() {
105 MOZ_ASSERT(NS_IsMainThread());
112 RefPtr
<QuotaUsageChecker
> self
= this;
113 auto scopeExit
= MakeScopeExit([self
]() { self
->RunCallback(false); });
115 RefPtr
<QuotaManagerService
> qms
= QuotaManagerService::GetOrCreate();
118 // Asynchronious getting quota usage for principal
119 nsCOMPtr
<nsIQuotaUsageRequest
> usageRequest
;
120 if (NS_WARN_IF(NS_FAILED(qms
->GetUsageForPrincipal(
121 mPrincipal
, this, false, getter_AddRefs(usageRequest
))))) {
125 // Asynchronious getting group usage and limit
126 nsCOMPtr
<nsIQuotaRequest
> request
;
128 NS_FAILED(qms
->Estimate(mPrincipal
, getter_AddRefs(request
))))) {
131 request
->SetCallback(this);
136 void QuotaUsageChecker::RunCallback(bool aResult
) {
137 MOZ_ASSERT(mIsChecking
&& mCallback
);
142 mGettingOriginUsageDone
= false;
143 mGettingGroupUsageDone
= false;
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
))) {
158 nsCOMPtr
<nsISupports
> supports
;
159 rv
= result
->GetAsInterface(&iid
, getter_AddRefs(supports
));
160 if (NS_WARN_IF(NS_FAILED(rv
))) {
166 aResult
= do_QueryInterface(supports
);
170 void QuotaUsageChecker::CheckQuotaHeadroom() {
171 MOZ_ASSERT(NS_IsMainThread());
173 const uint64_t groupHeadroom
=
174 static_cast<uint64_t>(
176 dom_serviceWorkers_mitigations_group_usage_headroom_kb()) *
178 const uint64_t groupAvailable
= mGroupLimit
- mGroupUsage
;
180 // Group usage head room is satisfied, does not need the usage mitigation.
181 if (groupAvailable
> groupHeadroom
) {
186 RefPtr
<QuotaUsageChecker
> self
= this;
187 auto scopeExit
= MakeScopeExit([self
]() { self
->RunCallback(false); });
189 nsCOMPtr
<nsIClearDataService
> csd
=
190 do_GetService("@mozilla.org/clear-data-service;1");
193 // Group usage headroom is not satisfied even removing the origin usage,
194 // clear all group usage.
195 if ((groupAvailable
+ mOriginUsage
) < groupHeadroom
) {
197 nsresult rv
= mPrincipal
->GetHost(host
);
198 if (NS_WARN_IF(NS_FAILED(rv
))) {
202 rv
= csd
->DeleteDataFromBaseDomain(
203 host
, false, nsIClearDataService::CLEAR_DOM_QUOTA
, this);
204 if (NS_WARN_IF(NS_FAILED(rv
))) {
208 // clear the origin usage since it makes group usage headroom be satisifed.
210 nsresult rv
= csd
->DeleteDataFromPrincipal(
211 mPrincipal
, false, nsIClearDataService::CLEAR_DOM_QUOTA
, this);
212 if (NS_WARN_IF(NS_FAILED(rv
))) {
220 // nsIQuotaUsageCallback implementation
222 NS_IMETHODIMP
QuotaUsageChecker::OnUsageResult(
223 nsIQuotaUsageRequest
* aUsageRequest
) {
224 MOZ_ASSERT(NS_IsMainThread());
225 MOZ_ASSERT(aUsageRequest
);
227 RefPtr
<QuotaUsageChecker
> self
= this;
228 auto scopeExit
= MakeScopeExit([self
]() { self
->RunCallback(false); });
231 nsresult rv
= aUsageRequest
->GetResultCode(&resultCode
);
232 if (NS_WARN_IF(NS_FAILED(rv
) || NS_FAILED(resultCode
))) {
236 nsCOMPtr
<nsIQuotaOriginUsageResult
> usageResult
;
237 rv
= GetResult(aUsageRequest
, usageResult
);
238 if (NS_WARN_IF(NS_FAILED(rv
))) {
241 MOZ_ASSERT(usageResult
);
243 rv
= usageResult
->GetUsage(&mOriginUsage
);
244 if (NS_WARN_IF(NS_FAILED(rv
))) {
248 MOZ_ASSERT(!mGettingOriginUsageDone
);
249 mGettingOriginUsageDone
= true;
253 // Call CheckQuotaHeadroom() when both
254 // QuotaManagerService::GetUsageForPrincipal() and
255 // QuotaManagerService::Estimate() are done.
256 if (mGettingOriginUsageDone
&& mGettingGroupUsageDone
) {
257 CheckQuotaHeadroom();
262 // nsIQuotaCallback implementation
264 NS_IMETHODIMP
QuotaUsageChecker::OnComplete(nsIQuotaRequest
* aRequest
) {
265 MOZ_ASSERT(NS_IsMainThread());
266 MOZ_ASSERT(aRequest
);
268 RefPtr
<QuotaUsageChecker
> self
= this;
269 auto scopeExit
= MakeScopeExit([self
]() { self
->RunCallback(false); });
272 nsresult rv
= aRequest
->GetResultCode(&resultCode
);
273 if (NS_WARN_IF(NS_FAILED(rv
) || NS_FAILED(resultCode
))) {
277 nsCOMPtr
<nsIQuotaEstimateResult
> estimateResult
;
278 rv
= GetResult(aRequest
, estimateResult
);
279 if (NS_WARN_IF(NS_FAILED(rv
))) {
282 MOZ_ASSERT(estimateResult
);
284 rv
= estimateResult
->GetUsage(&mGroupUsage
);
285 if (NS_WARN_IF(NS_FAILED(rv
))) {
289 rv
= estimateResult
->GetLimit(&mGroupLimit
);
290 if (NS_WARN_IF(NS_FAILED(rv
))) {
294 MOZ_ASSERT(!mGettingGroupUsageDone
);
295 mGettingGroupUsageDone
= true;
299 // Call CheckQuotaHeadroom() when both
300 // QuotaManagerService::GetUsageForPrincipal() and
301 // QuotaManagerService::Estimate() are done.
302 if (mGettingOriginUsageDone
&& mGettingGroupUsageDone
) {
303 CheckQuotaHeadroom();
308 // nsIClearDataCallback implementation
310 NS_IMETHODIMP
QuotaUsageChecker::OnDataDeleted(uint32_t aFailedFlags
) {
315 // Helper methods in ServiceWorkerQuotaUtils.h
317 void ClearQuotaUsageIfNeeded(nsIPrincipal
* aPrincipal
,
318 ServiceWorkerQuotaMitigationCallback
&& aCallback
) {
319 MOZ_ASSERT(NS_IsMainThread());
320 MOZ_ASSERT(aPrincipal
);
322 RefPtr
<QuotaUsageChecker
> checker
=
323 MakeRefPtr
<QuotaUsageChecker
>(aPrincipal
, std::move(aCallback
));
327 } // namespace mozilla::dom