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