Bug 1853814 [wpt PR 42017] - LoAF: Expose script URL for promise resolvers, a=testonly
[gecko.git] / dom / serviceworkers / ServiceWorkerQuotaUtils.cpp
blob65e051eb40dfb828f9a2d7fedeab53c45e0723e7
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"
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"
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 {
48 public:
49 NS_DECL_ISUPPORTS
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);
63 void Start();
65 void RunCallback(bool aResult);
67 private:
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;
84 bool mIsChecking;
85 uint64_t mOriginUsage;
86 uint64_t mGroupUsage;
87 uint64_t mGroupLimit;
90 NS_IMPL_ISUPPORTS(QuotaUsageChecker, nsIQuotaCallback, nsIQuotaUsageCallback,
91 nsIClearDataCallback)
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) {}
104 void QuotaUsageChecker::Start() {
105 MOZ_ASSERT(NS_IsMainThread());
107 if (mIsChecking) {
108 return;
110 mIsChecking = true;
112 RefPtr<QuotaUsageChecker> self = this;
113 auto scopeExit = MakeScopeExit([self]() { self->RunCallback(false); });
115 RefPtr<QuotaManagerService> qms = QuotaManagerService::GetOrCreate();
116 MOZ_ASSERT(qms);
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))))) {
122 return;
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;
131 request->SetCallback(this);
133 scopeExit.release();
136 void QuotaUsageChecker::RunCallback(bool aResult) {
137 MOZ_ASSERT(mIsChecking && mCallback);
138 if (!mIsChecking) {
139 return;
141 mIsChecking = false;
142 mGettingOriginUsageDone = false;
143 mGettingGroupUsageDone = false;
145 mCallback(aResult);
146 mCallback = nullptr;
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;
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;
164 free(iid);
166 aResult = do_QueryInterface(supports);
167 return NS_OK;
170 void QuotaUsageChecker::CheckQuotaHeadroom() {
171 MOZ_ASSERT(NS_IsMainThread());
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;
180 // Group usage head room is satisfied, does not need the usage mitigation.
181 if (groupAvailable > groupHeadroom) {
182 RunCallback(true);
183 return;
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");
191 MOZ_ASSERT(csd);
193 // Group usage headroom is not satisfied even removing the origin usage,
194 // clear all group usage.
195 if ((groupAvailable + mOriginUsage) < groupHeadroom) {
196 nsAutoCString host;
197 nsresult rv = mPrincipal->GetHost(host);
198 if (NS_WARN_IF(NS_FAILED(rv))) {
199 return;
202 rv = csd->DeleteDataFromBaseDomain(
203 host, false, nsIClearDataService::CLEAR_DOM_QUOTA, this);
204 if (NS_WARN_IF(NS_FAILED(rv))) {
205 return;
208 // clear the origin usage since it makes group usage headroom be satisifed.
209 } else {
210 nsresult rv = csd->DeleteDataFromPrincipal(
211 mPrincipal, false, nsIClearDataService::CLEAR_DOM_QUOTA, this);
212 if (NS_WARN_IF(NS_FAILED(rv))) {
213 return;
217 scopeExit.release();
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); });
230 nsresult resultCode;
231 nsresult rv = aUsageRequest->GetResultCode(&resultCode);
232 if (NS_WARN_IF(NS_FAILED(rv) || NS_FAILED(resultCode))) {
233 return rv;
236 nsCOMPtr<nsIQuotaOriginUsageResult> usageResult;
237 rv = GetResult(aUsageRequest, usageResult);
238 if (NS_WARN_IF(NS_FAILED(rv))) {
239 return rv;
241 MOZ_ASSERT(usageResult);
243 rv = usageResult->GetUsage(&mOriginUsage);
244 if (NS_WARN_IF(NS_FAILED(rv))) {
245 return rv;
248 MOZ_ASSERT(!mGettingOriginUsageDone);
249 mGettingOriginUsageDone = true;
251 scopeExit.release();
253 // Call CheckQuotaHeadroom() when both
254 // QuotaManagerService::GetUsageForPrincipal() and
255 // QuotaManagerService::Estimate() are done.
256 if (mGettingOriginUsageDone && mGettingGroupUsageDone) {
257 CheckQuotaHeadroom();
259 return NS_OK;
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); });
271 nsresult resultCode;
272 nsresult rv = aRequest->GetResultCode(&resultCode);
273 if (NS_WARN_IF(NS_FAILED(rv) || NS_FAILED(resultCode))) {
274 return rv;
277 nsCOMPtr<nsIQuotaEstimateResult> estimateResult;
278 rv = GetResult(aRequest, estimateResult);
279 if (NS_WARN_IF(NS_FAILED(rv))) {
280 return rv;
282 MOZ_ASSERT(estimateResult);
284 rv = estimateResult->GetUsage(&mGroupUsage);
285 if (NS_WARN_IF(NS_FAILED(rv))) {
286 return rv;
289 rv = estimateResult->GetLimit(&mGroupLimit);
290 if (NS_WARN_IF(NS_FAILED(rv))) {
291 return rv;
294 MOZ_ASSERT(!mGettingGroupUsageDone);
295 mGettingGroupUsageDone = true;
297 scopeExit.release();
299 // Call CheckQuotaHeadroom() when both
300 // QuotaManagerService::GetUsageForPrincipal() and
301 // QuotaManagerService::Estimate() are done.
302 if (mGettingOriginUsageDone && mGettingGroupUsageDone) {
303 CheckQuotaHeadroom();
305 return NS_OK;
308 // nsIClearDataCallback implementation
310 NS_IMETHODIMP QuotaUsageChecker::OnDataDeleted(uint32_t aFailedFlags) {
311 RunCallback(true);
312 return NS_OK;
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));
324 checker->Start();
327 } // namespace mozilla::dom