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 file,
5 * You can obtain one at http://mozilla.org/MPL/2.0/. */
7 #include "StorageManager.h"
8 #include "fs/FileSystemRequestHandler.h"
13 #include "ErrorList.h"
14 #include "fs/FileSystemRequestHandler.h"
15 #include "MainThreadUtils.h"
16 #include "js/CallArgs.h"
17 #include "js/TypeDecls.h"
18 #include "mozilla/Attributes.h"
19 #include "mozilla/ErrorResult.h"
20 #include "mozilla/MacroForEach.h"
21 #include "mozilla/Maybe.h"
22 #include "mozilla/Mutex.h"
23 #include "mozilla/RefPtr.h"
24 #include "mozilla/dom/BindingDeclarations.h"
25 #include "mozilla/dom/Document.h"
26 #include "mozilla/dom/FileSystemManager.h"
27 #include "mozilla/dom/Promise.h"
28 #include "mozilla/dom/PromiseWorkerProxy.h"
29 #include "mozilla/dom/StorageManagerBinding.h"
30 #include "mozilla/dom/WorkerCommon.h"
31 #include "mozilla/dom/WorkerPrivate.h"
32 #include "mozilla/dom/WorkerRunnable.h"
33 #include "mozilla/dom/WorkerStatus.h"
34 #include "mozilla/dom/quota/QuotaManagerService.h"
35 #include "nsContentPermissionHelper.h"
36 #include "nsContentUtils.h"
39 #include "nsIGlobalObject.h"
40 #include "nsIPrincipal.h"
41 #include "nsIQuotaCallbacks.h"
42 #include "nsIQuotaManagerService.h"
43 #include "nsIQuotaRequests.h"
44 #include "nsIQuotaResults.h"
45 #include "nsIVariant.h"
46 #include "nsLiteralString.h"
47 #include "nsPIDOMWindow.h"
49 #include "nsStringFlags.h"
50 #include "nsTLiteralString.h"
61 using namespace mozilla::dom::quota
;
63 namespace mozilla::dom
{
67 // This class is used to get quota usage, request persist and check persisted
69 class RequestResolver final
: public nsIQuotaCallback
{
71 enum Type
{ Estimate
, Persist
, Persisted
};
74 class FinishWorkerRunnable
;
76 // If this resolver was created for a window then mPromise must be non-null.
77 // Otherwise mProxy must be non-null.
78 RefPtr
<Promise
> mPromise
;
79 RefPtr
<PromiseWorkerProxy
> mProxy
;
82 StorageEstimate mStorageEstimate
;
87 RequestResolver(Type aType
, Promise
* aPromise
)
92 MOZ_ASSERT(NS_IsMainThread());
96 RequestResolver(Type aType
, PromiseWorkerProxy
* aProxy
)
97 : mProxy(aProxy
), mResultCode(NS_OK
), mType(aType
), mPersisted(false) {
98 MOZ_ASSERT(NS_IsMainThread());
102 void ResolveOrReject();
104 NS_DECL_THREADSAFE_ISUPPORTS
105 NS_DECL_NSIQUOTACALLBACK
108 ~RequestResolver() = default;
110 nsresult
GetStorageEstimate(nsIVariant
* aResult
);
112 nsresult
GetPersisted(nsIVariant
* aResult
);
114 nsresult
OnCompleteInternal(nsIQuotaRequest
* aRequest
);
119 // This class is used to return promise on worker thread.
120 class RequestResolver::FinishWorkerRunnable final
121 : public WorkerThreadRunnable
{
122 RefPtr
<RequestResolver
> mResolver
;
125 explicit FinishWorkerRunnable(RequestResolver
* aResolver
)
126 : WorkerThreadRunnable("RequestResolver::FinishWorkerRunnable"),
127 mResolver(aResolver
) {
128 MOZ_ASSERT(NS_IsMainThread());
129 MOZ_ASSERT(aResolver
);
132 bool WorkerRun(JSContext
* aCx
, WorkerPrivate
* aWorkerPrivate
) override
;
135 class EstimateWorkerMainThreadRunnable final
: public WorkerMainThreadRunnable
{
136 RefPtr
<PromiseWorkerProxy
> mProxy
;
139 EstimateWorkerMainThreadRunnable(WorkerPrivate
* aWorkerPrivate
,
140 PromiseWorkerProxy
* aProxy
)
141 : WorkerMainThreadRunnable(aWorkerPrivate
,
142 "StorageManager :: Estimate"_ns
),
144 MOZ_ASSERT(aWorkerPrivate
);
145 aWorkerPrivate
->AssertIsOnWorkerThread();
149 bool MainThreadRun() override
;
152 class PersistedWorkerMainThreadRunnable final
153 : public WorkerMainThreadRunnable
{
154 RefPtr
<PromiseWorkerProxy
> mProxy
;
157 PersistedWorkerMainThreadRunnable(WorkerPrivate
* aWorkerPrivate
,
158 PromiseWorkerProxy
* aProxy
)
159 : WorkerMainThreadRunnable(aWorkerPrivate
,
160 "StorageManager :: Persisted"_ns
),
162 MOZ_ASSERT(aWorkerPrivate
);
163 aWorkerPrivate
->AssertIsOnWorkerThread();
167 bool MainThreadRun() override
;
170 /*******************************************************************************
171 * PersistentStoragePermissionRequest
172 ******************************************************************************/
174 class PersistentStoragePermissionRequest final
175 : public ContentPermissionRequestBase
{
176 RefPtr
<Promise
> mPromise
;
179 PersistentStoragePermissionRequest(nsIPrincipal
* aPrincipal
,
180 nsPIDOMWindowInner
* aWindow
,
182 : ContentPermissionRequestBase(aPrincipal
, aWindow
,
183 "dom.storageManager"_ns
,
184 "persistent-storage"_ns
),
187 MOZ_ASSERT(aPromise
);
192 NS_DECL_ISUPPORTS_INHERITED
193 NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(PersistentStoragePermissionRequest
,
194 ContentPermissionRequestBase
)
196 // nsIContentPermissionRequest
197 NS_IMETHOD
Cancel(void) override
;
198 NS_IMETHOD
Allow(JS::Handle
<JS::Value
> choices
) override
;
201 ~PersistentStoragePermissionRequest() = default;
204 nsresult
Estimate(nsIPrincipal
* aPrincipal
, nsIQuotaCallback
* aCallback
,
205 nsIQuotaRequest
** aRequest
) {
206 MOZ_ASSERT(aPrincipal
);
207 MOZ_ASSERT(aCallback
);
208 MOZ_ASSERT(aRequest
);
210 // Firefox and Quota Manager have always used the schemeless origin group
211 // (https://storage.spec.whatwg.org/#schemeless-origin-group) for quota limit
212 // purposes. This has been to prevent a site/eTLD+1 from claiming more than
213 // its fair share of storage through the use of sub-domains. Because the limit
214 // is at the group level and the usage needs to make sense in the context of
215 // that limit, we also expose the group usage. Bug 1374970 reflects this
216 // reality and bug 1305665 tracks our plan to eliminate our use of groups for
219 nsCOMPtr
<nsIQuotaManagerService
> qms
= QuotaManagerService::GetOrCreate();
220 if (NS_WARN_IF(!qms
)) {
221 return NS_ERROR_FAILURE
;
224 nsCOMPtr
<nsIQuotaRequest
> request
;
225 nsresult rv
= qms
->Estimate(aPrincipal
, getter_AddRefs(request
));
226 if (NS_WARN_IF(NS_FAILED(rv
))) {
230 MOZ_ALWAYS_SUCCEEDS(request
->SetCallback(aCallback
));
232 request
.forget(aRequest
);
236 nsresult
Persisted(nsIPrincipal
* aPrincipal
, nsIQuotaCallback
* aCallback
,
237 nsIQuotaRequest
** aRequest
) {
238 MOZ_ASSERT(aPrincipal
);
239 MOZ_ASSERT(aCallback
);
240 MOZ_ASSERT(aRequest
);
242 nsCOMPtr
<nsIQuotaManagerService
> qms
= QuotaManagerService::GetOrCreate();
243 if (NS_WARN_IF(!qms
)) {
244 return NS_ERROR_FAILURE
;
247 nsCOMPtr
<nsIQuotaRequest
> request
;
248 nsresult rv
= qms
->Persisted(aPrincipal
, getter_AddRefs(request
));
249 if (NS_WARN_IF(NS_FAILED(rv
))) {
253 // All the methods in nsIQuotaManagerService shouldn't synchronously fire
254 // any callbacks when they are being executed. Even when a result is ready,
255 // a new runnable should be dispatched to current thread to fire the callback
256 // asynchronously. It's safe to set the callback after we call Persisted().
257 MOZ_ALWAYS_SUCCEEDS(request
->SetCallback(aCallback
));
259 request
.forget(aRequest
);
264 already_AddRefed
<Promise
> ExecuteOpOnMainOrWorkerThread(
265 nsIGlobalObject
* aGlobal
, RequestResolver::Type aType
, ErrorResult
& aRv
) {
267 MOZ_ASSERT_IF(aType
== RequestResolver::Type::Persist
, NS_IsMainThread());
269 RefPtr
<Promise
> promise
= Promise::Create(aGlobal
, aRv
);
270 if (NS_WARN_IF(!promise
)) {
274 if (NS_IsMainThread()) {
275 nsCOMPtr
<nsPIDOMWindowInner
> window
= do_QueryInterface(aGlobal
);
276 if (NS_WARN_IF(!window
)) {
277 aRv
.Throw(NS_ERROR_FAILURE
);
281 nsCOMPtr
<Document
> doc
= window
->GetExtantDoc();
282 if (NS_WARN_IF(!doc
)) {
283 aRv
.Throw(NS_ERROR_FAILURE
);
287 nsCOMPtr
<nsIPrincipal
> principal
= doc
->NodePrincipal();
288 MOZ_ASSERT(principal
);
290 // Storage Standard 7. API
291 // If origin is an opaque origin, then reject promise with a TypeError.
292 if (principal
->GetIsNullPrincipal()) {
294 case RequestResolver::Type::Persisted
:
295 promise
->MaybeRejectWithTypeError(
296 "persisted() called for opaque origin");
298 case RequestResolver::Type::Persist
:
299 promise
->MaybeRejectWithTypeError(
300 "persist() called for opaque origin");
302 case RequestResolver::Type::Estimate
:
303 promise
->MaybeRejectWithTypeError(
304 "estimate() called for opaque origin");
308 return promise
.forget();
312 case RequestResolver::Type::Persisted
: {
313 RefPtr
<RequestResolver
> resolver
=
314 new RequestResolver(RequestResolver::Type::Persisted
, promise
);
316 RefPtr
<nsIQuotaRequest
> request
;
317 aRv
= Persisted(principal
, resolver
, getter_AddRefs(request
));
322 case RequestResolver::Type::Persist
: {
323 RefPtr
<PersistentStoragePermissionRequest
> request
=
324 new PersistentStoragePermissionRequest(principal
, window
, promise
);
326 // In private browsing mode, no permission prompt.
327 if (doc
->IsInPrivateBrowsing()) {
328 aRv
= request
->Cancel();
329 } else if (!request
->CheckPermissionDelegate()) {
330 aRv
= request
->Cancel();
332 aRv
= request
->Start();
338 case RequestResolver::Type::Estimate
: {
339 RefPtr
<RequestResolver
> resolver
=
340 new RequestResolver(RequestResolver::Type::Estimate
, promise
);
342 RefPtr
<nsIQuotaRequest
> request
;
343 aRv
= Estimate(principal
, resolver
, getter_AddRefs(request
));
349 MOZ_CRASH("Invalid aRequest type!");
352 if (NS_WARN_IF(aRv
.Failed())) {
356 return promise
.forget();
359 WorkerPrivate
* workerPrivate
= GetCurrentThreadWorkerPrivate();
360 MOZ_ASSERT(workerPrivate
);
362 RefPtr
<PromiseWorkerProxy
> promiseProxy
=
363 PromiseWorkerProxy::Create(workerPrivate
, promise
);
364 if (NS_WARN_IF(!promiseProxy
)) {
365 aRv
.Throw(NS_ERROR_FAILURE
);
370 case RequestResolver::Type::Estimate
: {
371 RefPtr
<EstimateWorkerMainThreadRunnable
> runnnable
=
372 new EstimateWorkerMainThreadRunnable(promiseProxy
->GetWorkerPrivate(),
374 runnnable
->Dispatch(promiseProxy
->GetWorkerPrivate(), Canceling
, aRv
);
379 case RequestResolver::Type::Persisted
: {
380 RefPtr
<PersistedWorkerMainThreadRunnable
> runnnable
=
381 new PersistedWorkerMainThreadRunnable(
382 promiseProxy
->GetWorkerPrivate(), promiseProxy
);
383 runnnable
->Dispatch(promiseProxy
->GetWorkerPrivate(), Canceling
, aRv
);
389 MOZ_CRASH("Invalid aRequest type");
392 if (NS_WARN_IF(aRv
.Failed())) {
396 return promise
.forget();
401 /*******************************************************************************
402 * Local class implementations
403 ******************************************************************************/
405 void RequestResolver::ResolveOrReject() {
406 class MOZ_STACK_CLASS AutoCleanup final
{
407 RefPtr
<PromiseWorkerProxy
> mProxy
;
410 explicit AutoCleanup(PromiseWorkerProxy
* aProxy
) : mProxy(aProxy
) {
421 RefPtr
<Promise
> promise
;
422 Maybe
<AutoCleanup
> autoCleanup
;
428 promise
= mProxy
->GetWorkerPromise();
432 // Only clean up for worker case.
433 autoCleanup
.emplace(mProxy
);
438 if (mType
== Type::Estimate
) {
439 if (NS_SUCCEEDED(mResultCode
)) {
440 promise
->MaybeResolve(mStorageEstimate
);
442 promise
->MaybeRejectWithTypeError(
443 "Internal error while estimating storage usage");
449 MOZ_ASSERT(mType
== Type::Persist
|| mType
== Type::Persisted
);
451 if (NS_SUCCEEDED(mResultCode
)) {
452 promise
->MaybeResolve(mPersisted
);
454 promise
->MaybeResolve(false);
458 NS_IMPL_ISUPPORTS(RequestResolver
, nsIQuotaCallback
)
460 nsresult
RequestResolver::GetStorageEstimate(nsIVariant
* aResult
) {
462 MOZ_ASSERT(mType
== Type::Estimate
);
464 MOZ_ASSERT(aResult
->GetDataType() == nsIDataType::VTYPE_INTERFACE_IS
);
467 nsCOMPtr
<nsISupports
> supports
;
468 nsresult rv
= aResult
->GetAsInterface(&iid
, getter_AddRefs(supports
));
469 if (NS_WARN_IF(NS_FAILED(rv
))) {
475 nsCOMPtr
<nsIQuotaEstimateResult
> estimateResult
= do_QueryInterface(supports
);
476 MOZ_ASSERT(estimateResult
);
479 estimateResult
->GetUsage(&mStorageEstimate
.mUsage
.Construct()));
482 estimateResult
->GetLimit(&mStorageEstimate
.mQuota
.Construct()));
487 nsresult
RequestResolver::GetPersisted(nsIVariant
* aResult
) {
489 MOZ_ASSERT(mType
== Type::Persist
|| mType
== Type::Persisted
);
492 uint16_t dataType
= aResult
->GetDataType();
495 if (mType
== Type::Persist
) {
496 MOZ_ASSERT(dataType
== nsIDataType::VTYPE_VOID
);
502 MOZ_ASSERT(dataType
== nsIDataType::VTYPE_BOOL
);
505 nsresult rv
= aResult
->GetAsBool(&persisted
);
506 if (NS_WARN_IF(NS_FAILED(rv
))) {
510 mPersisted
= persisted
;
514 nsresult
RequestResolver::OnCompleteInternal(nsIQuotaRequest
* aRequest
) {
515 MOZ_ASSERT(NS_IsMainThread());
516 MOZ_ASSERT(aRequest
);
519 nsresult rv
= aRequest
->GetResultCode(&resultCode
);
520 if (NS_WARN_IF(NS_FAILED(rv
))) {
524 if (NS_FAILED(resultCode
)) {
528 nsCOMPtr
<nsIVariant
> result
;
529 rv
= aRequest
->GetResult(getter_AddRefs(result
));
530 if (NS_WARN_IF(NS_FAILED(rv
))) {
534 if (mType
== Type::Estimate
) {
535 rv
= GetStorageEstimate(result
);
537 MOZ_ASSERT(mType
== Type::Persist
|| mType
== Type::Persisted
);
539 rv
= GetPersisted(result
);
541 if (NS_WARN_IF(NS_FAILED(rv
))) {
548 nsresult
RequestResolver::Finish() {
549 // In a main thread request.
551 MOZ_ASSERT(mPromise
);
558 // In a worker thread request.
559 MutexAutoLock
lock(mProxy
->Lock());
561 if (NS_WARN_IF(mProxy
->CleanedUp())) {
562 return NS_ERROR_FAILURE
;
565 RefPtr
<FinishWorkerRunnable
> runnable
= new FinishWorkerRunnable(this);
566 if (NS_WARN_IF(!runnable
->Dispatch(mProxy
->GetWorkerPrivate()))) {
567 return NS_ERROR_FAILURE
;
575 RequestResolver::OnComplete(nsIQuotaRequest
* aRequest
) {
576 MOZ_ASSERT(NS_IsMainThread());
577 MOZ_ASSERT(aRequest
);
579 mResultCode
= OnCompleteInternal(aRequest
);
581 nsresult rv
= Finish();
582 if (NS_WARN_IF(NS_FAILED(rv
))) {
589 bool RequestResolver::FinishWorkerRunnable::WorkerRun(
590 JSContext
* aCx
, WorkerPrivate
* aWorkerPrivate
) {
592 MOZ_ASSERT(aWorkerPrivate
);
593 aWorkerPrivate
->AssertIsOnWorkerThread();
595 MOZ_ASSERT(mResolver
);
596 mResolver
->ResolveOrReject();
601 bool EstimateWorkerMainThreadRunnable::MainThreadRun() {
602 MOZ_ASSERT(NS_IsMainThread());
604 nsCOMPtr
<nsIPrincipal
> principal
;
607 MutexAutoLock
lock(mProxy
->Lock());
608 if (mProxy
->CleanedUp()) {
611 principal
= mProxy
->GetWorkerPrivate()->GetPrincipal();
614 MOZ_ASSERT(principal
);
616 RefPtr
<RequestResolver
> resolver
=
617 new RequestResolver(RequestResolver::Type::Estimate
, mProxy
);
619 RefPtr
<nsIQuotaRequest
> request
;
620 nsresult rv
= Estimate(principal
, resolver
, getter_AddRefs(request
));
621 if (NS_WARN_IF(NS_FAILED(rv
))) {
628 bool PersistedWorkerMainThreadRunnable::MainThreadRun() {
629 MOZ_ASSERT(NS_IsMainThread());
631 nsCOMPtr
<nsIPrincipal
> principal
;
634 MutexAutoLock
lock(mProxy
->Lock());
635 if (mProxy
->CleanedUp()) {
638 principal
= mProxy
->GetWorkerPrivate()->GetPrincipal();
641 MOZ_ASSERT(principal
);
643 RefPtr
<RequestResolver
> resolver
=
644 new RequestResolver(RequestResolver::Type::Persisted
, mProxy
);
646 RefPtr
<nsIQuotaRequest
> request
;
647 nsresult rv
= Persisted(principal
, resolver
, getter_AddRefs(request
));
648 if (NS_WARN_IF(NS_FAILED(rv
))) {
655 nsresult
PersistentStoragePermissionRequest::Start() {
656 MOZ_ASSERT(NS_IsMainThread());
659 #ifdef MOZ_WIDGET_ANDROID
660 // on Android calling `ShowPrompt` here calls
661 // `nsContentPermissionUtils::AskPermission` once, and a response of
662 // `PromptResult::Pending` calls it again. This results in multiple requests
663 // for storage access, so we check the prompt prefs only to ensure we only
665 pr
= CheckPromptPrefs();
667 nsresult rv
= ShowPrompt(pr
);
668 if (NS_WARN_IF(NS_FAILED(rv
))) {
672 if (pr
== PromptResult::Granted
) {
673 return Allow(JS::UndefinedHandleValue
);
675 if (pr
== PromptResult::Denied
) {
679 return nsContentPermissionUtils::AskPermission(this, mWindow
);
682 NS_IMPL_ISUPPORTS_CYCLE_COLLECTION_INHERITED_0(
683 PersistentStoragePermissionRequest
, ContentPermissionRequestBase
)
685 NS_IMPL_CYCLE_COLLECTION_INHERITED(PersistentStoragePermissionRequest
,
686 ContentPermissionRequestBase
, mPromise
)
689 PersistentStoragePermissionRequest::Cancel() {
690 MOZ_ASSERT(NS_IsMainThread());
691 MOZ_ASSERT(mPromise
);
693 RefPtr
<RequestResolver
> resolver
=
694 new RequestResolver(RequestResolver::Type::Persisted
, mPromise
);
696 RefPtr
<nsIQuotaRequest
> request
;
698 return Persisted(mPrincipal
, resolver
, getter_AddRefs(request
));
702 PersistentStoragePermissionRequest::Allow(JS::Handle
<JS::Value
> aChoices
) {
703 MOZ_ASSERT(NS_IsMainThread());
705 RefPtr
<RequestResolver
> resolver
=
706 new RequestResolver(RequestResolver::Type::Persist
, mPromise
);
708 nsCOMPtr
<nsIQuotaManagerService
> qms
= QuotaManagerService::GetOrCreate();
709 if (NS_WARN_IF(!qms
)) {
710 return NS_ERROR_FAILURE
;
713 RefPtr
<nsIQuotaRequest
> request
;
715 nsresult rv
= qms
->Persist(mPrincipal
, getter_AddRefs(request
));
716 if (NS_WARN_IF(NS_FAILED(rv
))) {
720 MOZ_ALWAYS_SUCCEEDS(request
->SetCallback(resolver
));
725 /*******************************************************************************
727 ******************************************************************************/
729 StorageManager::StorageManager(nsIGlobalObject
* aGlobal
) : mOwner(aGlobal
) {
733 StorageManager::~StorageManager() { Shutdown(); }
735 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(StorageManager
)
736 NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
737 NS_INTERFACE_MAP_ENTRY(nsISupports
)
740 NS_IMPL_CYCLE_COLLECTING_ADDREF(StorageManager
)
741 NS_IMPL_CYCLE_COLLECTING_RELEASE(StorageManager
)
743 NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE_CLASS(StorageManager
)
744 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(StorageManager
)
746 NS_IMPL_CYCLE_COLLECTION_UNLINK(mOwner
)
747 NS_IMPL_CYCLE_COLLECTION_UNLINK_PRESERVED_WRAPPER
748 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
749 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(StorageManager
)
750 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mOwner
)
751 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mFileSystemManager
)
752 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
754 void StorageManager::Shutdown() {
755 if (mFileSystemManager
) {
756 mFileSystemManager
->Shutdown();
757 mFileSystemManager
= nullptr;
761 already_AddRefed
<FileSystemManager
> StorageManager::GetFileSystemManager() {
762 if (!mFileSystemManager
) {
765 mFileSystemManager
= MakeRefPtr
<FileSystemManager
>(mOwner
, this);
768 return do_AddRef(mFileSystemManager
);
771 // WebIDL Boilerplate
773 JSObject
* StorageManager::WrapObject(JSContext
* aCx
,
774 JS::Handle
<JSObject
*> aGivenProto
) {
775 return StorageManager_Binding::Wrap(aCx
, this, aGivenProto
);
780 already_AddRefed
<Promise
> StorageManager::Persisted(ErrorResult
& aRv
) {
783 return ExecuteOpOnMainOrWorkerThread(mOwner
, RequestResolver::Type::Persisted
,
787 already_AddRefed
<Promise
> StorageManager::Persist(ErrorResult
& aRv
) {
790 return ExecuteOpOnMainOrWorkerThread(mOwner
, RequestResolver::Type::Persist
,
794 already_AddRefed
<Promise
> StorageManager::Estimate(ErrorResult
& aRv
) {
797 return ExecuteOpOnMainOrWorkerThread(mOwner
, RequestResolver::Type::Estimate
,
801 already_AddRefed
<Promise
> StorageManager::GetDirectory(ErrorResult
& aRv
) {
802 return RefPtr(GetFileSystemManager())->GetDirectory(aRv
);
805 } // namespace mozilla::dom