tor-browser

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

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