tor-browser

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

BoundStorageKey.cpp (9550B)


      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/. */
      6 
      7 #include "BoundStorageKey.h"
      8 
      9 #include "BoundStorageKeyCache.h"
     10 #include "mozilla/dom/WorkerPrivate.h"
     11 #include "mozilla/dom/cache/AutoUtils.h"
     12 #include "mozilla/dom/cache/Cache.h"
     13 #include "mozilla/dom/cache/CacheStorageChild.h"
     14 #include "mozilla/dom/cache/CacheWorkerRef.h"
     15 #include "mozilla/dom/quota/PrincipalUtils.h"
     16 #include "mozilla/ipc/BackgroundChild.h"
     17 #include "mozilla/ipc/PBackgroundChild.h"
     18 #include "mozilla/ipc/PBackgroundSharedTypes.h"
     19 
     20 namespace mozilla::dom::cache {
     21 using mozilla::ipc::BackgroundChild;
     22 using mozilla::ipc::PrincipalInfo;
     23 NS_IMPL_ISUPPORTS(BoundStorageKey, nsISupports)
     24 
     25 template <typename PromiseType>
     26 struct BoundStorageKeyCacheStorage::Entry final {
     27  RefPtr<PromiseType> mPromise;
     28  CacheOpArgs mArgs;
     29 };
     30 
     31 nsresult BoundStorageKey::Init(Namespace aNamespace,
     32                               const PrincipalInfo& aPrincipalInfo,
     33                               nsISerialEventTarget* aTarget) {
     34  MOZ_DIAGNOSTIC_ASSERT(aTarget);
     35 
     36  // setup child and parent actors and retarget to aTarget
     37  auto* actor = new BoundStorageKeyChild(this);
     38  MOZ_ASSERT(actor);
     39 
     40  {
     41    ::mozilla::ipc::Endpoint<PBoundStorageKeyChild> childEP;
     42    ::mozilla::ipc::Endpoint<PBoundStorageKeyParent> parentEP;
     43 
     44    auto rv = PBoundStorageKey::CreateEndpoints(&parentEP, &childEP);
     45    (void)NS_WARN_IF(NS_FAILED(rv));
     46 
     47    PBackgroundChild* bgActor = BackgroundChild::GetOrCreateForCurrentThread();
     48    if (NS_WARN_IF(!bgActor)) {
     49      NS_WARNING("BoundStorageKey failed to obtain bgActor");
     50      return NS_ERROR_UNEXPECTED;
     51    }
     52 
     53    bgActor->SendCreateBoundStorageKeyParent(std::move(parentEP), aNamespace,
     54                                             aPrincipalInfo);
     55 
     56    if (NS_WARN_IF(!(childEP.Bind(actor, aTarget)))) {
     57      NS_WARNING("BoundStorageKeyChildActor failed to bind to target.");
     58      return NS_ERROR_UNEXPECTED;
     59    }
     60  }
     61 
     62  mActor = actor;
     63  return NS_OK;
     64 }
     65 
     66 void BoundStorageKey::OnActorDestroy(BoundStorageKeyChild* aActor) {
     67  MOZ_DIAGNOSTIC_ASSERT(mActor);
     68  MOZ_DIAGNOSTIC_ASSERT(mActor == aActor);
     69  MOZ_DIAGNOSTIC_ASSERT(!NS_FAILED(mStatus));
     70 
     71  // Note that we will never get an actor again in case another request is
     72  // made before this object is destructed.
     73  mActor->ClearListener();
     74  mActor = nullptr;
     75  mStatus = NS_ERROR_UNEXPECTED;
     76 }
     77 
     78 BoundStorageKey::~BoundStorageKey() {
     79  if (mActor) {
     80    mActor->StartDestroyFromListener();
     81  }
     82  MOZ_DIAGNOSTIC_ASSERT(!mActor);
     83 }
     84 
     85 // static
     86 already_AddRefed<BoundStorageKeyCacheStorage>
     87 BoundStorageKeyCacheStorage::Create(Namespace aNamespace,
     88                                    nsIGlobalObject* aGlobal,
     89                                    WorkerPrivate* aWorkerPrivate,
     90                                    nsISerialEventTarget* aActorTarget,
     91                                    ErrorResult& aRv) {
     92  MOZ_DIAGNOSTIC_ASSERT(aWorkerPrivate);
     93 
     94  if (aWorkerPrivate->GetOriginAttributes().IsPrivateBrowsing() &&
     95      !StaticPrefs::dom_cache_privateBrowsing_enabled()) {
     96    NS_WARNING("BoundStorageKey not supported during private browsing.");
     97 
     98    aRv = NS_ERROR_DOM_SECURITY_ERR;
     99    return nullptr;
    100  }
    101 
    102  const PrincipalInfo& principalInfo =
    103      aWorkerPrivate->GetEffectiveStoragePrincipalInfo();
    104 
    105  QM_TRY(OkIf(quota::IsPrincipalInfoValid(principalInfo)), nullptr,
    106         [&aRv](const auto) { aRv = NS_ERROR_FAILURE; });
    107 
    108  // We have a number of cases where we want to skip the https scheme
    109  // validation:
    110  //
    111  // 1) Any worker when dom.caches.testing.enabled pref is true.
    112  // 2) Any worker when dom.serviceWorkers.testing.enabled pref is true.  This
    113  //    is mainly because most sites using SWs will expect Cache to work if
    114  //    SWs are enabled.
    115  // 3) If the window that created this worker has the devtools SW testing
    116  //    option enabled.  Same reasoning as (2).
    117  // 4) If the worker itself is a ServiceWorker, then we always skip the
    118  //    origin checks.  The ServiceWorker has its own trusted origin checks
    119  //    that are better than ours.  In addition, we don't have information
    120  //    about the window any more, so we can't do our own checks.
    121  bool testingEnabled = StaticPrefs::dom_caches_testing_enabled() ||
    122                        StaticPrefs::dom_serviceWorkers_testing_enabled() ||
    123                        aWorkerPrivate->ServiceWorkersTestingInWindow() ||
    124                        aWorkerPrivate->IsServiceWorker();
    125 
    126  if (!IsTrusted(principalInfo, testingEnabled)) {
    127    NS_WARNING("BoundStorageKey not supported on untrusted origins.");
    128 
    129    aRv = NS_ERROR_UNEXPECTED;
    130    return nullptr;
    131  }
    132 
    133  RefPtr<BoundStorageKeyCacheStorage> ref =
    134      new BoundStorageKeyCacheStorage(aNamespace, aGlobal, principalInfo);
    135 
    136  auto rv = ref->Init(aWorkerPrivate, aNamespace, principalInfo, aActorTarget);
    137  if (NS_WARN_IF(NS_FAILED(rv))) {
    138    aRv = rv;
    139    return nullptr;
    140  }
    141 
    142  MOZ_ASSERT(ref->mActor);
    143  return ref.forget();
    144 }
    145 
    146 nsresult BoundStorageKeyCacheStorage::Init(WorkerPrivate* aWorkerPrivate,
    147                                           Namespace aNamespace,
    148                                           const PrincipalInfo& aPrincipalInfo,
    149                                           nsISerialEventTarget* aTarget) {
    150  auto rc = BoundStorageKey::Init(aNamespace, aPrincipalInfo, aTarget);
    151  if (NS_FAILED(rc)) {
    152    return rc;
    153  }
    154 
    155  mCacheStorageChild = CreateCacheStorageChild(aWorkerPrivate);
    156  MOZ_ASSERT(mCacheStorageChild);
    157 
    158  return mCacheStorageChild ? NS_OK : NS_ERROR_FAILURE;
    159 }
    160 
    161 BoundStorageKeyCacheStorage::BoundStorageKeyCacheStorage(
    162    Namespace aNamespace, nsIGlobalObject* aGlobal,
    163    const mozilla::ipc::PrincipalInfo& aPrincipalInfo)
    164    : mGlobal(aGlobal),
    165      mPrincipalInfo(MakeUnique<PrincipalInfo>(aPrincipalInfo)),
    166      mNamespace(aNamespace) {}
    167 
    168 void BoundStorageKeyCacheStorage::OnActorDestroy(CacheStorageChild* aActor) {
    169  AssertOwningThread();
    170 
    171  MOZ_DIAGNOSTIC_ASSERT(mActor);
    172  MOZ_DIAGNOSTIC_ASSERT(mCacheStorageChild.get() == aActor);
    173 
    174  mCacheStorageChild->ClearListener();
    175  mCacheStorageChild = nullptr;
    176 }
    177 
    178 BoundStorageKeyCacheStorage::~BoundStorageKeyCacheStorage() {
    179  AssertOwningThread();
    180  if (mCacheStorageChild) {
    181    mCacheStorageChild->StartDestroyFromListener();
    182  }
    183 }
    184 
    185 already_AddRefed<CacheStorageChild>
    186 BoundStorageKeyCacheStorage::CreateCacheStorageChild(
    187    WorkerPrivate* aWorkerPrivate) {
    188  SafeRefPtr<CacheWorkerRef> workerRef;
    189  if (aWorkerPrivate) {
    190    aWorkerPrivate->AssertIsOnWorkerThread();
    191    workerRef =
    192        CacheWorkerRef::Create(aWorkerPrivate, CacheWorkerRef::eIPCWorkerRef);
    193    if (NS_WARN_IF(!workerRef)) {
    194      return nullptr;
    195    }
    196  }
    197 
    198  RefPtr<CacheStorageChild> newActor =
    199      new CacheStorageChild(this, std::move(workerRef), mActor.get());
    200  auto* constructedActor = mActor->SendPCacheStorageConstructor(
    201      newActor, mNamespace, *mPrincipalInfo);
    202 
    203  if (NS_WARN_IF(!constructedActor)) {
    204    mStatus = NS_ERROR_UNEXPECTED;
    205    return nullptr;
    206  }
    207 
    208  MOZ_DIAGNOSTIC_ASSERT(newActor == constructedActor);
    209  return newActor.forget();
    210 }
    211 
    212 template <typename EntryType>
    213 void BoundStorageKeyCacheStorage::RunRequest(EntryType&& aEntry) {
    214  MOZ_ASSERT(mActor);
    215  MOZ_ASSERT(mCacheStorageChild);
    216 
    217  AutoChildOpArgs args(this, aEntry.mArgs, 1);
    218  RefPtr<CacheStoragePromise> p{aEntry.mPromise.forget()};
    219  mCacheStorageChild->ExecuteOp(mGlobal, p, this, args.SendAsOpArgs());
    220 }
    221 
    222 auto BoundStorageKeyCacheStorage::Open(const nsAString& aKey, ErrorResult& aRv)
    223    -> already_AddRefed<CacheStoragePromise> {
    224  AssertOwningThread();
    225 
    226  if (NS_WARN_IF(NS_FAILED(mStatus))) {
    227    aRv = mStatus;
    228    return nullptr;
    229  }
    230 
    231  RefPtr<OpenResultPromise::Private> promise{
    232      new OpenResultPromise::Private(__func__)};
    233  RunRequest(Entry<OpenResultPromise::Private>{
    234      promise, StorageOpenArgs{nsString(aKey)}});
    235 
    236  return promise.forget();
    237 }
    238 
    239 auto BoundStorageKeyCacheStorage::Has(const nsAString& aKey, ErrorResult& aRv)
    240    -> already_AddRefed<CacheStoragePromise> {
    241  AssertOwningThread();
    242 
    243  if (NS_WARN_IF(NS_FAILED(mStatus))) {
    244    aRv = mStatus;
    245    return nullptr;
    246  }
    247 
    248  RefPtr<HasResultPromise::Private> promise{
    249      new HasResultPromise::Private(__func__)};
    250  RunRequest(Entry<HasResultPromise::Private>{promise,
    251                                              StorageHasArgs(nsString(aKey))});
    252 
    253  return promise.forget();
    254 }
    255 
    256 auto BoundStorageKeyCacheStorage::Delete(const nsAString& aKey,
    257                                         ErrorResult& aRv)
    258    -> already_AddRefed<CacheStoragePromise> {
    259  AssertOwningThread();
    260 
    261  if (NS_WARN_IF(NS_FAILED(mStatus))) {
    262    aRv = mStatus;
    263    return nullptr;
    264  }
    265 
    266  RefPtr<DeleteResultPromise::Private> promise{
    267      new DeleteResultPromise::Private(__func__)};
    268  RunRequest(Entry<DeleteResultPromise::Private>{
    269      promise, StorageDeleteArgs{nsString(aKey)}});
    270 
    271  return promise.forget();
    272 }
    273 
    274 auto BoundStorageKeyCacheStorage::Keys(ErrorResult& aRv)
    275    -> already_AddRefed<CacheStoragePromise> {
    276  AssertOwningThread();
    277 
    278  if (NS_WARN_IF(NS_FAILED(mStatus))) {
    279    aRv = mStatus;
    280    return nullptr;
    281  }
    282 
    283  RefPtr<KeysResultPromise::Private> promise{
    284      new KeysResultPromise::Private(__func__)};
    285  RunRequest(Entry<KeysResultPromise::Private>{promise, StorageKeysArgs()});
    286 
    287  return promise.forget();
    288 }
    289 
    290 }  // namespace mozilla::dom::cache