StorageManager.cpp (21936B)
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/. */ 6 7 #include "StorageManager.h" 8 9 #include <cstdint> 10 #include <cstdlib> 11 12 #include "ErrorList.h" 13 #include "MainThreadUtils.h" 14 #include "fs/FileSystemRequestHandler.h" 15 #include "js/CallArgs.h" 16 #include "js/TypeDecls.h" 17 #include "mozilla/Attributes.h" 18 #include "mozilla/ErrorResult.h" 19 #include "mozilla/Maybe.h" 20 #include "mozilla/Mutex.h" 21 #include "mozilla/RefPtr.h" 22 #include "mozilla/dom/BindingDeclarations.h" 23 #include "mozilla/dom/Document.h" 24 #include "mozilla/dom/FileSystemManager.h" 25 #include "mozilla/dom/Promise.h" 26 #include "mozilla/dom/PromiseWorkerProxy.h" 27 #include "mozilla/dom/StorageManagerBinding.h" 28 #include "mozilla/dom/WorkerCommon.h" 29 #include "mozilla/dom/WorkerPrivate.h" 30 #include "mozilla/dom/WorkerRunnable.h" 31 #include "mozilla/dom/WorkerStatus.h" 32 #include "mozilla/dom/quota/QuotaManagerService.h" 33 #include "nsContentPermissionHelper.h" 34 #include "nsContentUtils.h" 35 #include "nsDebug.h" 36 #include "nsError.h" 37 #include "nsIGlobalObject.h" 38 #include "nsIPrincipal.h" 39 #include "nsIQuotaCallbacks.h" 40 #include "nsIQuotaManagerService.h" 41 #include "nsIQuotaRequests.h" 42 #include "nsIQuotaResults.h" 43 #include "nsIVariant.h" 44 #include "nsLiteralString.h" 45 #include "nsPIDOMWindow.h" 46 #include "nsString.h" 47 #include "nsStringFlags.h" 48 #include "nsTLiteralString.h" 49 #include "nscore.h" 50 51 class JSObject; 52 struct JSContext; 53 struct nsID; 54 55 namespace mozilla { 56 class Runnable; 57 } 58 59 using namespace mozilla::dom::quota; 60 61 namespace mozilla::dom { 62 63 namespace { 64 65 // This class is used to get quota usage, request persist and check persisted 66 // status callbacks. 67 class RequestResolver final : public nsIQuotaCallback { 68 public: 69 enum Type { Estimate, Persist, Persisted }; 70 71 private: 72 class FinishWorkerRunnable; 73 74 // If this resolver was created for a window then mPromise must be non-null. 75 // Otherwise mProxy must be non-null. 76 RefPtr<Promise> mPromise; 77 RefPtr<PromiseWorkerProxy> mProxy; 78 79 nsresult mResultCode; 80 StorageEstimate mStorageEstimate; 81 const Type mType; 82 bool mPersisted; 83 84 public: 85 RequestResolver(Type aType, Promise* aPromise) 86 : mPromise(aPromise), 87 mResultCode(NS_OK), 88 mType(aType), 89 mPersisted(false) { 90 MOZ_ASSERT(NS_IsMainThread()); 91 MOZ_ASSERT(aPromise); 92 } 93 94 RequestResolver(Type aType, PromiseWorkerProxy* aProxy) 95 : mProxy(aProxy), mResultCode(NS_OK), mType(aType), mPersisted(false) { 96 MOZ_ASSERT(NS_IsMainThread()); 97 MOZ_ASSERT(aProxy); 98 } 99 100 void ResolveOrReject(); 101 102 NS_DECL_THREADSAFE_ISUPPORTS 103 NS_DECL_NSIQUOTACALLBACK 104 105 private: 106 ~RequestResolver() = default; 107 108 nsresult GetStorageEstimate(nsIVariant* aResult); 109 110 nsresult GetPersisted(nsIVariant* aResult); 111 112 nsresult OnCompleteInternal(nsIQuotaRequest* aRequest); 113 114 nsresult Finish(); 115 }; 116 117 // This class is used to return promise on worker thread. 118 class RequestResolver::FinishWorkerRunnable final 119 : public WorkerThreadRunnable { 120 RefPtr<RequestResolver> mResolver; 121 122 public: 123 explicit FinishWorkerRunnable(RequestResolver* aResolver) 124 : WorkerThreadRunnable("RequestResolver::FinishWorkerRunnable"), 125 mResolver(aResolver) { 126 MOZ_ASSERT(NS_IsMainThread()); 127 MOZ_ASSERT(aResolver); 128 } 129 130 bool WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) override; 131 }; 132 133 class EstimateWorkerMainThreadRunnable final : public WorkerMainThreadRunnable { 134 RefPtr<PromiseWorkerProxy> mProxy; 135 136 public: 137 EstimateWorkerMainThreadRunnable(WorkerPrivate* aWorkerPrivate, 138 PromiseWorkerProxy* aProxy) 139 : WorkerMainThreadRunnable(aWorkerPrivate, 140 "StorageManager :: Estimate"_ns), 141 mProxy(aProxy) { 142 MOZ_ASSERT(aWorkerPrivate); 143 aWorkerPrivate->AssertIsOnWorkerThread(); 144 MOZ_ASSERT(aProxy); 145 } 146 147 bool MainThreadRun() override; 148 }; 149 150 class PersistedWorkerMainThreadRunnable final 151 : public WorkerMainThreadRunnable { 152 RefPtr<PromiseWorkerProxy> mProxy; 153 154 public: 155 PersistedWorkerMainThreadRunnable(WorkerPrivate* aWorkerPrivate, 156 PromiseWorkerProxy* aProxy) 157 : WorkerMainThreadRunnable(aWorkerPrivate, 158 "StorageManager :: Persisted"_ns), 159 mProxy(aProxy) { 160 MOZ_ASSERT(aWorkerPrivate); 161 aWorkerPrivate->AssertIsOnWorkerThread(); 162 MOZ_ASSERT(aProxy); 163 } 164 165 bool MainThreadRun() override; 166 }; 167 168 /******************************************************************************* 169 * PersistentStoragePermissionRequest 170 ******************************************************************************/ 171 172 class PersistentStoragePermissionRequest final 173 : public ContentPermissionRequestBase { 174 RefPtr<Promise> mPromise; 175 176 public: 177 PersistentStoragePermissionRequest(nsIPrincipal* aPrincipal, 178 nsPIDOMWindowInner* aWindow, 179 Promise* aPromise) 180 : ContentPermissionRequestBase(aPrincipal, aWindow, 181 "dom.storageManager"_ns, 182 "persistent-storage"_ns), 183 mPromise(aPromise) { 184 MOZ_ASSERT(aWindow); 185 MOZ_ASSERT(aPromise); 186 } 187 188 nsresult Start(); 189 190 NS_DECL_ISUPPORTS_INHERITED 191 NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(PersistentStoragePermissionRequest, 192 ContentPermissionRequestBase) 193 194 // nsIContentPermissionRequest 195 NS_IMETHOD Cancel(void) override; 196 NS_IMETHOD Allow(JS::Handle<JS::Value> choices) override; 197 198 private: 199 ~PersistentStoragePermissionRequest() = default; 200 }; 201 202 nsresult Estimate(nsIPrincipal* aPrincipal, nsIQuotaCallback* aCallback, 203 nsIQuotaRequest** aRequest) { 204 MOZ_ASSERT(aPrincipal); 205 MOZ_ASSERT(aCallback); 206 MOZ_ASSERT(aRequest); 207 208 // Firefox and Quota Manager have always used the schemeless origin group 209 // (https://storage.spec.whatwg.org/#schemeless-origin-group) for quota limit 210 // purposes. This has been to prevent a site/eTLD+1 from claiming more than 211 // its fair share of storage through the use of sub-domains. Because the limit 212 // is at the group level and the usage needs to make sense in the context of 213 // that limit, we also expose the group usage. Bug 1374970 reflects this 214 // reality and bug 1305665 tracks our plan to eliminate our use of groups for 215 // this. 216 217 nsCOMPtr<nsIQuotaManagerService> qms = QuotaManagerService::GetOrCreate(); 218 if (NS_WARN_IF(!qms)) { 219 return NS_ERROR_FAILURE; 220 } 221 222 nsCOMPtr<nsIQuotaRequest> request; 223 nsresult rv = qms->Estimate(aPrincipal, getter_AddRefs(request)); 224 if (NS_WARN_IF(NS_FAILED(rv))) { 225 return rv; 226 } 227 228 MOZ_ALWAYS_SUCCEEDS(request->SetCallback(aCallback)); 229 230 request.forget(aRequest); 231 return NS_OK; 232 }; 233 234 nsresult Persisted(nsIPrincipal* aPrincipal, nsIQuotaCallback* aCallback, 235 nsIQuotaRequest** aRequest) { 236 MOZ_ASSERT(aPrincipal); 237 MOZ_ASSERT(aCallback); 238 MOZ_ASSERT(aRequest); 239 240 nsCOMPtr<nsIQuotaManagerService> qms = QuotaManagerService::GetOrCreate(); 241 if (NS_WARN_IF(!qms)) { 242 return NS_ERROR_FAILURE; 243 } 244 245 nsCOMPtr<nsIQuotaRequest> request; 246 nsresult rv = qms->Persisted(aPrincipal, getter_AddRefs(request)); 247 if (NS_WARN_IF(NS_FAILED(rv))) { 248 return rv; 249 } 250 251 // All the methods in nsIQuotaManagerService shouldn't synchronously fire 252 // any callbacks when they are being executed. Even when a result is ready, 253 // a new runnable should be dispatched to current thread to fire the callback 254 // asynchronously. It's safe to set the callback after we call Persisted(). 255 MOZ_ALWAYS_SUCCEEDS(request->SetCallback(aCallback)); 256 257 request.forget(aRequest); 258 259 return NS_OK; 260 }; 261 262 already_AddRefed<Promise> ExecuteOpOnMainOrWorkerThread( 263 nsIGlobalObject* aGlobal, RequestResolver::Type aType, ErrorResult& aRv) { 264 MOZ_ASSERT(aGlobal); 265 MOZ_ASSERT_IF(aType == RequestResolver::Type::Persist, NS_IsMainThread()); 266 267 RefPtr<Promise> promise = Promise::Create(aGlobal, aRv); 268 if (NS_WARN_IF(!promise)) { 269 return nullptr; 270 } 271 272 if (NS_IsMainThread()) { 273 nsCOMPtr<nsPIDOMWindowInner> window = do_QueryInterface(aGlobal); 274 if (NS_WARN_IF(!window)) { 275 aRv.Throw(NS_ERROR_FAILURE); 276 return nullptr; 277 } 278 279 nsCOMPtr<Document> doc = window->GetExtantDoc(); 280 if (NS_WARN_IF(!doc)) { 281 aRv.Throw(NS_ERROR_FAILURE); 282 return nullptr; 283 } 284 285 nsCOMPtr<nsIPrincipal> principal = doc->NodePrincipal(); 286 MOZ_ASSERT(principal); 287 288 // Storage Standard 7. API 289 // If origin is an opaque origin, then reject promise with a TypeError. 290 if (principal->GetIsNullPrincipal()) { 291 switch (aType) { 292 case RequestResolver::Type::Persisted: 293 promise->MaybeRejectWithTypeError( 294 "persisted() called for opaque origin"); 295 break; 296 case RequestResolver::Type::Persist: 297 promise->MaybeRejectWithTypeError( 298 "persist() called for opaque origin"); 299 break; 300 case RequestResolver::Type::Estimate: 301 promise->MaybeRejectWithTypeError( 302 "estimate() called for opaque origin"); 303 break; 304 } 305 306 return promise.forget(); 307 } 308 309 switch (aType) { 310 case RequestResolver::Type::Persisted: { 311 RefPtr<RequestResolver> resolver = 312 new RequestResolver(RequestResolver::Type::Persisted, promise); 313 314 RefPtr<nsIQuotaRequest> request; 315 aRv = Persisted(principal, resolver, getter_AddRefs(request)); 316 317 break; 318 } 319 320 case RequestResolver::Type::Persist: { 321 RefPtr<PersistentStoragePermissionRequest> request = 322 new PersistentStoragePermissionRequest(principal, window, promise); 323 324 // In private browsing mode, no permission prompt. 325 if (doc->IsInPrivateBrowsing()) { 326 aRv = request->Cancel(); 327 } else if (!request->CheckPermissionDelegate()) { 328 aRv = request->Cancel(); 329 } else { 330 aRv = request->Start(); 331 } 332 333 break; 334 } 335 336 case RequestResolver::Type::Estimate: { 337 RefPtr<RequestResolver> resolver = 338 new RequestResolver(RequestResolver::Type::Estimate, promise); 339 340 RefPtr<nsIQuotaRequest> request; 341 aRv = Estimate(principal, resolver, getter_AddRefs(request)); 342 343 break; 344 } 345 346 default: 347 MOZ_CRASH("Invalid aRequest type!"); 348 } 349 350 if (NS_WARN_IF(aRv.Failed())) { 351 return nullptr; 352 } 353 354 return promise.forget(); 355 } 356 357 WorkerPrivate* workerPrivate = GetCurrentThreadWorkerPrivate(); 358 MOZ_ASSERT(workerPrivate); 359 360 RefPtr<PromiseWorkerProxy> promiseProxy = 361 PromiseWorkerProxy::Create(workerPrivate, promise); 362 if (NS_WARN_IF(!promiseProxy)) { 363 aRv.Throw(NS_ERROR_FAILURE); 364 return nullptr; 365 } 366 367 switch (aType) { 368 case RequestResolver::Type::Estimate: { 369 RefPtr<EstimateWorkerMainThreadRunnable> runnnable = 370 new EstimateWorkerMainThreadRunnable(promiseProxy->GetWorkerPrivate(), 371 promiseProxy); 372 runnnable->Dispatch(promiseProxy->GetWorkerPrivate(), Canceling, aRv); 373 374 break; 375 } 376 377 case RequestResolver::Type::Persisted: { 378 RefPtr<PersistedWorkerMainThreadRunnable> runnnable = 379 new PersistedWorkerMainThreadRunnable( 380 promiseProxy->GetWorkerPrivate(), promiseProxy); 381 runnnable->Dispatch(promiseProxy->GetWorkerPrivate(), Canceling, aRv); 382 383 break; 384 } 385 386 default: 387 MOZ_CRASH("Invalid aRequest type"); 388 } 389 390 if (NS_WARN_IF(aRv.Failed())) { 391 return nullptr; 392 } 393 394 return promise.forget(); 395 }; 396 397 } // namespace 398 399 /******************************************************************************* 400 * Local class implementations 401 ******************************************************************************/ 402 403 void RequestResolver::ResolveOrReject() { 404 class MOZ_STACK_CLASS AutoCleanup final { 405 RefPtr<PromiseWorkerProxy> mProxy; 406 407 public: 408 explicit AutoCleanup(PromiseWorkerProxy* aProxy) : mProxy(aProxy) { 409 MOZ_ASSERT(aProxy); 410 } 411 412 ~AutoCleanup() { 413 MOZ_ASSERT(mProxy); 414 415 mProxy->CleanUp(); 416 } 417 }; 418 419 RefPtr<Promise> promise; 420 Maybe<AutoCleanup> autoCleanup; 421 422 if (mPromise) { 423 promise = mPromise; 424 } else { 425 MOZ_ASSERT(mProxy); 426 promise = mProxy->GetWorkerPromise(); 427 if (!promise) { 428 return; 429 } 430 // Only clean up for worker case. 431 autoCleanup.emplace(mProxy); 432 } 433 434 MOZ_ASSERT(promise); 435 436 if (mType == Type::Estimate) { 437 if (NS_SUCCEEDED(mResultCode)) { 438 promise->MaybeResolve(mStorageEstimate); 439 } else { 440 promise->MaybeRejectWithTypeError( 441 "Internal error while estimating storage usage"); 442 } 443 444 return; 445 } 446 447 MOZ_ASSERT(mType == Type::Persist || mType == Type::Persisted); 448 449 if (NS_SUCCEEDED(mResultCode)) { 450 promise->MaybeResolve(mPersisted); 451 } else { 452 promise->MaybeResolve(false); 453 } 454 } 455 456 NS_IMPL_ISUPPORTS(RequestResolver, nsIQuotaCallback) 457 458 nsresult RequestResolver::GetStorageEstimate(nsIVariant* aResult) { 459 MOZ_ASSERT(aResult); 460 MOZ_ASSERT(mType == Type::Estimate); 461 462 MOZ_ASSERT(aResult->GetDataType() == nsIDataType::VTYPE_INTERFACE_IS); 463 464 nsID* iid; 465 nsCOMPtr<nsISupports> supports; 466 nsresult rv = aResult->GetAsInterface(&iid, getter_AddRefs(supports)); 467 if (NS_WARN_IF(NS_FAILED(rv))) { 468 return rv; 469 } 470 471 free(iid); 472 473 nsCOMPtr<nsIQuotaEstimateResult> estimateResult = do_QueryInterface(supports); 474 MOZ_ASSERT(estimateResult); 475 476 MOZ_ALWAYS_SUCCEEDS( 477 estimateResult->GetUsage(&mStorageEstimate.mUsage.Construct())); 478 479 MOZ_ALWAYS_SUCCEEDS( 480 estimateResult->GetLimit(&mStorageEstimate.mQuota.Construct())); 481 482 return NS_OK; 483 } 484 485 nsresult RequestResolver::GetPersisted(nsIVariant* aResult) { 486 MOZ_ASSERT(aResult); 487 MOZ_ASSERT(mType == Type::Persist || mType == Type::Persisted); 488 489 #ifdef DEBUG 490 uint16_t dataType = aResult->GetDataType(); 491 #endif 492 493 if (mType == Type::Persist) { 494 MOZ_ASSERT(dataType == nsIDataType::VTYPE_VOID); 495 496 mPersisted = true; 497 return NS_OK; 498 } 499 500 MOZ_ASSERT(dataType == nsIDataType::VTYPE_BOOL); 501 502 bool persisted; 503 nsresult rv = aResult->GetAsBool(&persisted); 504 if (NS_WARN_IF(NS_FAILED(rv))) { 505 return rv; 506 } 507 508 mPersisted = persisted; 509 return NS_OK; 510 } 511 512 nsresult RequestResolver::OnCompleteInternal(nsIQuotaRequest* aRequest) { 513 MOZ_ASSERT(NS_IsMainThread()); 514 MOZ_ASSERT(aRequest); 515 516 nsresult resultCode; 517 nsresult rv = aRequest->GetResultCode(&resultCode); 518 if (NS_WARN_IF(NS_FAILED(rv))) { 519 return rv; 520 } 521 522 if (NS_FAILED(resultCode)) { 523 return resultCode; 524 } 525 526 nsCOMPtr<nsIVariant> result; 527 rv = aRequest->GetResult(getter_AddRefs(result)); 528 if (NS_WARN_IF(NS_FAILED(rv))) { 529 return rv; 530 } 531 532 if (mType == Type::Estimate) { 533 rv = GetStorageEstimate(result); 534 } else { 535 MOZ_ASSERT(mType == Type::Persist || mType == Type::Persisted); 536 537 rv = GetPersisted(result); 538 } 539 if (NS_WARN_IF(NS_FAILED(rv))) { 540 return rv; 541 } 542 543 return NS_OK; 544 } 545 546 nsresult RequestResolver::Finish() { 547 // In a main thread request. 548 if (!mProxy) { 549 MOZ_ASSERT(mPromise); 550 551 ResolveOrReject(); 552 return NS_OK; 553 } 554 555 { 556 // In a worker thread request. 557 MutexAutoLock lock(mProxy->Lock()); 558 559 if (NS_WARN_IF(mProxy->CleanedUp())) { 560 return NS_ERROR_FAILURE; 561 } 562 563 RefPtr<FinishWorkerRunnable> runnable = new FinishWorkerRunnable(this); 564 if (NS_WARN_IF(!runnable->Dispatch(mProxy->GetWorkerPrivate()))) { 565 return NS_ERROR_FAILURE; 566 } 567 } 568 569 return NS_OK; 570 } 571 572 NS_IMETHODIMP 573 RequestResolver::OnComplete(nsIQuotaRequest* aRequest) { 574 MOZ_ASSERT(NS_IsMainThread()); 575 MOZ_ASSERT(aRequest); 576 577 mResultCode = OnCompleteInternal(aRequest); 578 579 nsresult rv = Finish(); 580 if (NS_WARN_IF(NS_FAILED(rv))) { 581 return rv; 582 } 583 584 return NS_OK; 585 } 586 587 bool RequestResolver::FinishWorkerRunnable::WorkerRun( 588 JSContext* aCx, WorkerPrivate* aWorkerPrivate) { 589 MOZ_ASSERT(aCx); 590 MOZ_ASSERT(aWorkerPrivate); 591 aWorkerPrivate->AssertIsOnWorkerThread(); 592 593 MOZ_ASSERT(mResolver); 594 mResolver->ResolveOrReject(); 595 596 return true; 597 } 598 599 bool EstimateWorkerMainThreadRunnable::MainThreadRun() { 600 MOZ_ASSERT(NS_IsMainThread()); 601 602 nsCOMPtr<nsIPrincipal> principal; 603 604 { 605 MutexAutoLock lock(mProxy->Lock()); 606 if (mProxy->CleanedUp()) { 607 return true; 608 } 609 principal = mProxy->GetWorkerPrivate()->GetPrincipal(); 610 } 611 612 MOZ_ASSERT(principal); 613 614 RefPtr<RequestResolver> resolver = 615 new RequestResolver(RequestResolver::Type::Estimate, mProxy); 616 617 RefPtr<nsIQuotaRequest> request; 618 nsresult rv = Estimate(principal, resolver, getter_AddRefs(request)); 619 if (NS_WARN_IF(NS_FAILED(rv))) { 620 return false; 621 } 622 623 return true; 624 } 625 626 bool PersistedWorkerMainThreadRunnable::MainThreadRun() { 627 MOZ_ASSERT(NS_IsMainThread()); 628 629 nsCOMPtr<nsIPrincipal> principal; 630 631 { 632 MutexAutoLock lock(mProxy->Lock()); 633 if (mProxy->CleanedUp()) { 634 return true; 635 } 636 principal = mProxy->GetWorkerPrivate()->GetPrincipal(); 637 } 638 639 MOZ_ASSERT(principal); 640 641 RefPtr<RequestResolver> resolver = 642 new RequestResolver(RequestResolver::Type::Persisted, mProxy); 643 644 RefPtr<nsIQuotaRequest> request; 645 nsresult rv = Persisted(principal, resolver, getter_AddRefs(request)); 646 if (NS_WARN_IF(NS_FAILED(rv))) { 647 return false; 648 } 649 650 return true; 651 } 652 653 nsresult PersistentStoragePermissionRequest::Start() { 654 MOZ_ASSERT(NS_IsMainThread()); 655 656 PromptResult pr; 657 #ifdef MOZ_WIDGET_ANDROID 658 // on Android calling `ShowPrompt` here calls 659 // `nsContentPermissionUtils::AskPermission` once, and a response of 660 // `PromptResult::Pending` calls it again. This results in multiple requests 661 // for storage access, so we check the prompt prefs only to ensure we only 662 // request it once. 663 pr = CheckPromptPrefs(); 664 #else 665 nsresult rv = ShowPrompt(pr); 666 if (NS_WARN_IF(NS_FAILED(rv))) { 667 return rv; 668 } 669 #endif 670 if (pr == PromptResult::Granted) { 671 return Allow(JS::UndefinedHandleValue); 672 } 673 if (pr == PromptResult::Denied) { 674 return Cancel(); 675 } 676 677 return nsContentPermissionUtils::AskPermission(this, mWindow); 678 } 679 680 NS_IMPL_ISUPPORTS_CYCLE_COLLECTION_INHERITED_0( 681 PersistentStoragePermissionRequest, ContentPermissionRequestBase) 682 683 NS_IMPL_CYCLE_COLLECTION_INHERITED(PersistentStoragePermissionRequest, 684 ContentPermissionRequestBase, mPromise) 685 686 NS_IMETHODIMP 687 PersistentStoragePermissionRequest::Cancel() { 688 MOZ_ASSERT(NS_IsMainThread()); 689 MOZ_ASSERT(mPromise); 690 691 RefPtr<RequestResolver> resolver = 692 new RequestResolver(RequestResolver::Type::Persisted, mPromise); 693 694 RefPtr<nsIQuotaRequest> request; 695 696 return Persisted(mPrincipal, resolver, getter_AddRefs(request)); 697 } 698 699 NS_IMETHODIMP 700 PersistentStoragePermissionRequest::Allow(JS::Handle<JS::Value> aChoices) { 701 MOZ_ASSERT(NS_IsMainThread()); 702 703 RefPtr<RequestResolver> resolver = 704 new RequestResolver(RequestResolver::Type::Persist, mPromise); 705 706 nsCOMPtr<nsIQuotaManagerService> qms = QuotaManagerService::GetOrCreate(); 707 if (NS_WARN_IF(!qms)) { 708 return NS_ERROR_FAILURE; 709 } 710 711 RefPtr<nsIQuotaRequest> request; 712 713 nsresult rv = qms->Persist(mPrincipal, getter_AddRefs(request)); 714 if (NS_WARN_IF(NS_FAILED(rv))) { 715 return rv; 716 } 717 718 MOZ_ALWAYS_SUCCEEDS(request->SetCallback(resolver)); 719 720 return NS_OK; 721 } 722 723 /******************************************************************************* 724 * StorageManager 725 ******************************************************************************/ 726 727 StorageManager::StorageManager(nsIGlobalObject* aGlobal) : mOwner(aGlobal) { 728 MOZ_ASSERT(aGlobal); 729 } 730 731 StorageManager::~StorageManager() { Shutdown(); } 732 733 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(StorageManager) 734 NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY 735 NS_INTERFACE_MAP_ENTRY(nsISupports) 736 NS_INTERFACE_MAP_END 737 738 NS_IMPL_CYCLE_COLLECTING_ADDREF(StorageManager) 739 NS_IMPL_CYCLE_COLLECTING_RELEASE(StorageManager) 740 741 NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE_CLASS(StorageManager) 742 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(StorageManager) 743 tmp->Shutdown(); 744 NS_IMPL_CYCLE_COLLECTION_UNLINK(mOwner) 745 NS_IMPL_CYCLE_COLLECTION_UNLINK_PRESERVED_WRAPPER 746 NS_IMPL_CYCLE_COLLECTION_UNLINK_END 747 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(StorageManager) 748 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mOwner) 749 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mFileSystemManager) 750 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END 751 752 void StorageManager::Shutdown() { 753 if (mFileSystemManager) { 754 mFileSystemManager->Shutdown(); 755 mFileSystemManager = nullptr; 756 } 757 } 758 759 already_AddRefed<FileSystemManager> StorageManager::GetFileSystemManager() { 760 if (!mFileSystemManager) { 761 MOZ_ASSERT(mOwner); 762 763 mFileSystemManager = MakeRefPtr<FileSystemManager>(mOwner, this); 764 } 765 766 return do_AddRef(mFileSystemManager); 767 } 768 769 // WebIDL Boilerplate 770 771 JSObject* StorageManager::WrapObject(JSContext* aCx, 772 JS::Handle<JSObject*> aGivenProto) { 773 return StorageManager_Binding::Wrap(aCx, this, aGivenProto); 774 } 775 776 // WebIDL Interface 777 778 already_AddRefed<Promise> StorageManager::Persisted(ErrorResult& aRv) { 779 MOZ_ASSERT(mOwner); 780 781 return ExecuteOpOnMainOrWorkerThread(mOwner, RequestResolver::Type::Persisted, 782 aRv); 783 } 784 785 already_AddRefed<Promise> StorageManager::Persist(ErrorResult& aRv) { 786 MOZ_ASSERT(mOwner); 787 788 return ExecuteOpOnMainOrWorkerThread(mOwner, RequestResolver::Type::Persist, 789 aRv); 790 } 791 792 already_AddRefed<Promise> StorageManager::Estimate(ErrorResult& aRv) { 793 MOZ_ASSERT(mOwner); 794 795 return ExecuteOpOnMainOrWorkerThread(mOwner, RequestResolver::Type::Estimate, 796 aRv); 797 } 798 799 already_AddRefed<Promise> StorageManager::GetDirectory(ErrorResult& aRv) { 800 return RefPtr(GetFileSystemManager())->GetDirectory(aRv); 801 } 802 803 } // namespace mozilla::dom