BoundStorageKeyCache.cpp (12286B)
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 "BoundStorageKeyCache.h" 8 9 #include "mozilla/dom/CacheBinding.h" 10 #include "mozilla/dom/InternalResponse.h" 11 #include "mozilla/dom/Request.h" 12 #include "mozilla/dom/Response.h" 13 #include "mozilla/dom/ServiceWorkerUtils.h" 14 #include "mozilla/dom/cache/AutoUtils.h" 15 #include "mozilla/dom/cache/Cache.h" 16 #include "mozilla/dom/cache/CacheChild.h" 17 #include "mozilla/dom/cache/CacheWorkerRef.h" 18 // #include "mozilla/ErrorResult.h" 19 #include "nsIGlobalObject.h" 20 21 namespace mozilla::dom::cache { 22 23 NS_IMPL_ISUPPORTS(BoundStorageKeyCache, nsISupports) 24 25 using mozilla::ipc::PBackgroundChild; 26 27 BoundStorageKeyCache::BoundStorageKeyCache(nsIGlobalObject* aGlobal, 28 CacheChild* aActor, 29 Namespace aNamespace) 30 : mGlobal(aGlobal), mActor(aActor), mNamespace(aNamespace) { 31 MOZ_DIAGNOSTIC_ASSERT(mGlobal); 32 MOZ_DIAGNOSTIC_ASSERT(mActor); 33 MOZ_DIAGNOSTIC_ASSERT(mNamespace != INVALID_NAMESPACE); 34 mActor->SetListener(this); 35 } 36 37 // static 38 bool BoundStorageKeyCache::CachesEnabled(JSContext* aCx, JSObject* aObj) { 39 if (!IsSecureContextOrObjectIsFromSecureContext(aCx, aObj)) { 40 return StaticPrefs::dom_caches_testing_enabled() || 41 ServiceWorkersEnabled(aCx, aObj); 42 } 43 return true; 44 } 45 46 auto BoundStorageKeyCache::Match(JSContext* aCx, 47 const RequestOrUTF8String& aRequest, 48 const CacheQueryOptions& aOptions, 49 ErrorResult& aRv) 50 -> already_AddRefed<CachePromise> { 51 if (NS_WARN_IF(!mActor)) { 52 aRv.Throw(NS_ERROR_UNEXPECTED); 53 return nullptr; 54 } 55 56 CacheChild::AutoLock actorLock(*mActor); 57 58 SafeRefPtr<InternalRequest> ir = 59 ToInternalRequest(aCx, aRequest, IgnoreBody, aRv); 60 if (NS_WARN_IF(aRv.Failed())) { 61 return nullptr; 62 } 63 64 CacheQueryParams params; 65 ToCacheQueryParams(params, aOptions); 66 67 AutoChildOpArgs args( 68 this, CacheMatchArgs(CacheRequest(), params, GetOpenMode()), 1); 69 70 args.Add(*ir, IgnoreBody, IgnoreInvalidScheme, aRv); 71 if (NS_WARN_IF(aRv.Failed())) { 72 return nullptr; 73 } 74 75 RefPtr<CachePromise> promise = new MatchResultPromise::Private(__func__); 76 ExecuteOp(args, promise, aRv); 77 return promise.forget(); 78 } 79 80 auto BoundStorageKeyCache::MatchAll( 81 JSContext* aCx, const Optional<RequestOrUTF8String>& aRequest, 82 const CacheQueryOptions& aOptions, ErrorResult& aRv) 83 -> already_AddRefed<CachePromise> { 84 if (NS_WARN_IF(!mActor)) { 85 aRv.Throw(NS_ERROR_UNEXPECTED); 86 return nullptr; 87 } 88 89 CacheChild::AutoLock actorLock(*mActor); 90 91 CacheQueryParams params; 92 ToCacheQueryParams(params, aOptions); 93 94 AutoChildOpArgs args(this, 95 CacheMatchAllArgs(Nothing(), params, GetOpenMode()), 1); 96 97 if (aRequest.WasPassed()) { 98 SafeRefPtr<InternalRequest> ir = 99 ToInternalRequest(aCx, aRequest.Value(), IgnoreBody, aRv); 100 if (aRv.Failed()) { 101 return nullptr; 102 } 103 104 args.Add(*ir, IgnoreBody, IgnoreInvalidScheme, aRv); 105 if (aRv.Failed()) { 106 return nullptr; 107 } 108 } 109 110 RefPtr<CachePromise> promise{new MatchAllResultPromise::Private(__func__)}; 111 ExecuteOp(args, promise, aRv); 112 113 return promise.forget(); 114 } 115 116 auto BoundStorageKeyCache::Add(JSContext* aContext, 117 const RequestOrUTF8String& aRequest, 118 CallerType aCallerType, ErrorResult& aRv) 119 -> already_AddRefed<CachePromise> { 120 if (NS_WARN_IF(!mActor)) { 121 aRv.Throw(NS_ERROR_UNEXPECTED); 122 return nullptr; 123 } 124 125 CacheChild::AutoLock actorLock(*mActor); 126 127 if (!IsValidPutRequestMethod(aRequest, aRv)) { 128 return nullptr; 129 } 130 131 GlobalObject global(aContext, mGlobal->GetGlobalJSObject()); 132 MOZ_DIAGNOSTIC_ASSERT(!global.Failed()); 133 134 nsTArray<SafeRefPtr<Request>> requestList(1); 135 RootedDictionary<RequestInit> requestInit(aContext); 136 SafeRefPtr<Request> request = 137 Request::Constructor(global, aRequest, requestInit, aRv); 138 if (NS_WARN_IF(aRv.Failed())) { 139 return nullptr; 140 } 141 142 nsAutoCString url; 143 request->GetUrl(url); 144 if (NS_WARN_IF(!IsValidPutRequestURL(url, aRv))) { 145 return nullptr; 146 } 147 148 requestList.AppendElement(std::move(request)); 149 return AddAll(global, std::move(requestList), aCallerType, aRv); 150 } 151 152 auto BoundStorageKeyCache::AddAll( 153 JSContext* aContext, 154 const Sequence<OwningRequestOrUTF8String>& aRequestList, 155 CallerType aCallerType, ErrorResult& aRv) 156 -> already_AddRefed<CachePromise> { 157 if (NS_WARN_IF(!mActor)) { 158 aRv.Throw(NS_ERROR_UNEXPECTED); 159 return nullptr; 160 } 161 162 CacheChild::AutoLock actorLock(*mActor); 163 164 GlobalObject global(aContext, mGlobal->GetGlobalJSObject()); 165 MOZ_DIAGNOSTIC_ASSERT(!global.Failed()); 166 167 nsTArray<SafeRefPtr<Request>> requestList(aRequestList.Length()); 168 for (uint32_t i = 0; i < aRequestList.Length(); ++i) { 169 RequestOrUTF8String requestOrString; 170 171 if (aRequestList[i].IsRequest()) { 172 requestOrString.SetAsRequest() = aRequestList[i].GetAsRequest(); 173 if (NS_WARN_IF( 174 !IsValidPutRequestMethod(requestOrString.GetAsRequest(), aRv))) { 175 return nullptr; 176 } 177 } else { 178 requestOrString.SetAsUTF8String().ShareOrDependUpon( 179 aRequestList[i].GetAsUTF8String()); 180 } 181 182 RootedDictionary<RequestInit> requestInit(aContext); 183 SafeRefPtr<Request> request = 184 Request::Constructor(global, requestOrString, requestInit, aRv); 185 if (NS_WARN_IF(aRv.Failed())) { 186 return nullptr; 187 } 188 189 nsAutoCString url; 190 request->GetUrl(url); 191 if (NS_WARN_IF(!IsValidPutRequestURL(url, aRv))) { 192 return nullptr; 193 } 194 195 requestList.AppendElement(std::move(request)); 196 } 197 198 return AddAll(global, std::move(requestList), aCallerType, aRv); 199 } 200 201 auto BoundStorageKeyCache::Put(JSContext* aCx, 202 const RequestOrUTF8String& aRequest, 203 Response& aResponse, ErrorResult& aRv) 204 -> already_AddRefed<CachePromise> { 205 if (NS_WARN_IF(!mActor)) { 206 aRv.Throw(NS_ERROR_UNEXPECTED); 207 return nullptr; 208 } 209 210 CacheChild::AutoLock actorLock(*mActor); 211 212 if (NS_WARN_IF(!IsValidPutRequestMethod(aRequest, aRv))) { 213 return nullptr; 214 } 215 216 if (!IsValidPutResponseStatus(aResponse, PutStatusPolicy::Default, aRv)) { 217 return nullptr; 218 } 219 220 if (NS_WARN_IF(aResponse.GetPrincipalInfo() && 221 aResponse.GetPrincipalInfo()->type() == 222 mozilla::ipc::PrincipalInfo::TExpandedPrincipalInfo)) { 223 // WebExtensions Content Scripts can currently run fetch from their global 224 // which will end up to have an expanded principal, but we require that the 225 // contents of Cache storage for the content origin to be same-origin, and 226 // never an expanded principal (See Bug 1753810). 227 aRv.ThrowSecurityError("Disallowed on WebExtension ContentScript Request"); 228 return nullptr; 229 } 230 231 SafeRefPtr<InternalRequest> ir = 232 ToInternalRequest(aCx, aRequest, ReadBody, aRv); 233 if (NS_WARN_IF(aRv.Failed())) { 234 return nullptr; 235 } 236 237 AutoChildOpArgs args(this, CachePutAllArgs(), 1); 238 239 args.Add(aCx, *ir, ReadBody, TypeErrorOnInvalidScheme, aResponse, aRv); 240 if (NS_WARN_IF(aRv.Failed())) { 241 return nullptr; 242 } 243 244 RefPtr<CachePromise> promise{new PutResultPromise::Private(__func__)}; 245 ExecuteOp(args, promise, aRv); 246 247 return promise.forget(); 248 } 249 250 auto BoundStorageKeyCache::Delete(JSContext* aCx, 251 const RequestOrUTF8String& aRequest, 252 const CacheQueryOptions& aOptions, 253 ErrorResult& aRv) 254 -> already_AddRefed<CachePromise> { 255 if (NS_WARN_IF(!mActor)) { 256 aRv.Throw(NS_ERROR_UNEXPECTED); 257 return nullptr; 258 } 259 260 CacheChild::AutoLock actorLock(*mActor); 261 262 SafeRefPtr<InternalRequest> ir = 263 ToInternalRequest(aCx, aRequest, IgnoreBody, aRv); 264 if (NS_WARN_IF(aRv.Failed())) { 265 return nullptr; 266 } 267 268 CacheQueryParams params; 269 ToCacheQueryParams(params, aOptions); 270 271 AutoChildOpArgs args(this, CacheDeleteArgs(CacheRequest(), params), 1); 272 273 args.Add(*ir, IgnoreBody, IgnoreInvalidScheme, aRv); 274 if (NS_WARN_IF(aRv.Failed())) { 275 return nullptr; 276 } 277 278 RefPtr<CachePromise> promise{new DeleteResultPromise::Private(__func__)}; 279 ExecuteOp(args, promise, aRv); 280 281 return promise.forget(); 282 } 283 284 auto BoundStorageKeyCache::Keys(JSContext* aCx, 285 const Optional<RequestOrUTF8String>& aRequest, 286 const CacheQueryOptions& aOptions, 287 ErrorResult& aRv) 288 -> already_AddRefed<CachePromise> { 289 if (NS_WARN_IF(!mActor)) { 290 aRv.Throw(NS_ERROR_UNEXPECTED); 291 return nullptr; 292 } 293 294 CacheChild::AutoLock actorLock(*mActor); 295 296 CacheQueryParams params; 297 ToCacheQueryParams(params, aOptions); 298 299 AutoChildOpArgs args(this, CacheKeysArgs(Nothing(), params, GetOpenMode()), 300 1); 301 302 if (aRequest.WasPassed()) { 303 SafeRefPtr<InternalRequest> ir = 304 ToInternalRequest(aCx, aRequest.Value(), IgnoreBody, aRv); 305 if (NS_WARN_IF(aRv.Failed())) { 306 return nullptr; 307 } 308 309 args.Add(*ir, IgnoreBody, IgnoreInvalidScheme, aRv); 310 if (NS_WARN_IF(aRv.Failed())) { 311 return nullptr; 312 } 313 } 314 315 RefPtr<CachePromise> promise{new KeysResultPromise::Private(__func__)}; 316 ExecuteOp(args, promise, aRv); 317 318 return promise.forget(); 319 } 320 321 void BoundStorageKeyCache::OnActorDestroy(CacheChild* aActor) { 322 MOZ_DIAGNOSTIC_ASSERT(mActor); 323 MOZ_DIAGNOSTIC_ASSERT(mActor == aActor); 324 325 mActor->ClearListener(); 326 mActor = nullptr; 327 } 328 329 nsIGlobalObject* BoundStorageKeyCache::GetGlobalObject() const { 330 return mGlobal; 331 } 332 333 #ifdef DEBUG 334 void BoundStorageKeyCache::AssertOwningThread() const { 335 NS_ASSERT_OWNINGTHREAD(Cache); 336 } 337 #endif 338 339 BoundStorageKeyCache::~BoundStorageKeyCache() { 340 NS_ASSERT_OWNINGTHREAD(BoundStorageKeyCache); 341 if (mActor) { 342 mActor->StartDestroyFromListener(); 343 // OnActorDestroy() is called synchronously by StartDestroyFromListener(). 344 // So we should have already cleared the mActor. 345 MOZ_DIAGNOSTIC_ASSERT(!mActor); 346 } 347 } 348 349 void BoundStorageKeyCache::ExecuteOp(AutoChildOpArgs& aOpArgs, 350 RefPtr<CachePromise>& aPromise, 351 ErrorResult& aRv) { 352 MOZ_DIAGNOSTIC_ASSERT(mActor); 353 mActor->ExecuteOp(mGlobal, aPromise, this, aOpArgs.SendAsOpArgs()); 354 } 355 356 auto BoundStorageKeyCache::AddAll(const GlobalObject& aGlobal, 357 nsTArray<SafeRefPtr<Request>>&& aRequestList, 358 CallerType aCallerType, ErrorResult& aRv) 359 -> already_AddRefed<CachePromise> { 360 MOZ_DIAGNOSTIC_ASSERT(mActor); 361 362 // Fetch doesn't work on non-main threads yet. 363 RefPtr<CachePromise> promise = AddAllResultPromise::CreateAndReject( 364 ErrorResult(NS_ERROR_FAILURE), __func__); 365 return promise.forget(); 366 } 367 368 auto BoundStorageKeyCache::PutAll( 369 JSContext* aCx, const nsTArray<SafeRefPtr<Request>>& aRequestList, 370 const nsTArray<RefPtr<Response>>& aResponseList, ErrorResult& aRv) 371 -> already_AddRefed<CachePromise> { 372 MOZ_DIAGNOSTIC_ASSERT(aRequestList.Length() == aResponseList.Length()); 373 374 if (NS_WARN_IF(!mActor)) { 375 aRv.Throw(NS_ERROR_UNEXPECTED); 376 return nullptr; 377 } 378 379 CacheChild::AutoLock actorLock(*mActor); 380 381 AutoChildOpArgs args(this, CachePutAllArgs(), aRequestList.Length()); 382 383 for (uint32_t i = 0; i < aRequestList.Length(); ++i) { 384 SafeRefPtr<InternalRequest> ir = aRequestList[i]->GetInternalRequest(); 385 args.Add(aCx, *ir, ReadBody, TypeErrorOnInvalidScheme, *aResponseList[i], 386 aRv); 387 if (NS_WARN_IF(aRv.Failed())) { 388 return nullptr; 389 } 390 } 391 392 RefPtr<CachePromise> promise{new PutResultPromise::Private(__func__)}; 393 ExecuteOp(args, promise, aRv); 394 395 return promise.forget(); 396 } 397 398 OpenMode BoundStorageKeyCache::GetOpenMode() const { 399 return mNamespace == CHROME_ONLY_NAMESPACE ? OpenMode::Eager : OpenMode::Lazy; 400 } 401 402 } // namespace mozilla::dom::cache