LocalStorageManager2.cpp (18396B)
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 2 /* vim: set ts=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 "LocalStorageManager2.h" 8 9 // Local includes 10 #include "ActorsChild.h" 11 #include "LSObject.h" 12 13 // Global includes 14 #include <utility> 15 16 #include "MainThreadUtils.h" 17 #include "jsapi.h" 18 #include "mozilla/Assertions.h" 19 #include "mozilla/ErrorResult.h" 20 #include "mozilla/OriginAttributes.h" 21 #include "mozilla/RefPtr.h" 22 #include "mozilla/RemoteLazyInputStreamThread.h" 23 #include "mozilla/dom/LocalStorageCommon.h" 24 #include "mozilla/dom/PBackgroundLSRequest.h" 25 #include "mozilla/dom/PBackgroundLSSharedTypes.h" 26 #include "mozilla/dom/PBackgroundLSSimpleRequest.h" 27 #include "mozilla/dom/Promise.h" 28 #include "mozilla/dom/quota/PrincipalUtils.h" 29 #include "mozilla/dom/quota/PromiseUtils.h" 30 #include "mozilla/ipc/BackgroundChild.h" 31 #include "mozilla/ipc/BackgroundUtils.h" 32 #include "mozilla/ipc/PBackgroundChild.h" 33 #include "mozilla/ipc/PBackgroundSharedTypes.h" 34 #include "nsCOMPtr.h" 35 #include "nsDebug.h" 36 #include "nsError.h" 37 #include "nsIEventTarget.h" 38 #include "nsILocalStorageManager.h" 39 #include "nsIPrincipal.h" 40 #include "nsIRunnable.h" 41 #include "nsPIDOMWindow.h" 42 #include "nsStringFwd.h" 43 #include "nsThreadUtils.h" 44 #include "nscore.h" 45 #include "xpcpublic.h" 46 47 namespace mozilla::dom { 48 49 namespace { 50 51 class AsyncRequestHelper final : public Runnable, 52 public LSRequestChildCallback { 53 enum class State { 54 /** 55 * The AsyncRequestHelper has been created and dispatched to the 56 * RemoteLazyInputStream Thread. 57 */ 58 Initial, 59 /** 60 * Start() has been invoked on the RemoteLazyInputStream Thread and 61 * LocalStorageManager2::StartRequest has been invoked from there, sending 62 * an IPC message to PBackground to service the request. We stay in this 63 * state until a response is received. 64 */ 65 ResponsePending, 66 /** 67 * A response has been received and AsyncRequestHelper has been dispatched 68 * back to the owning event target to call Finish(). 69 */ 70 Finishing, 71 /** 72 * Finish() has been called on the main thread. The promise will be resolved 73 * according to the received response. 74 */ 75 Complete 76 }; 77 78 // The object we are issuing a request on behalf of. Present because of the 79 // need to invoke LocalStorageManager2::StartRequest off the main thread. 80 // Dropped on return to the main-thread in Finish(). 81 RefPtr<LocalStorageManager2> mManager; 82 // The thread the AsyncRequestHelper was created on. This should be the main 83 // thread. 84 nsCOMPtr<nsIEventTarget> mOwningEventTarget; 85 // The IPC actor handling the request with standard IPC allocation rules. 86 // Our reference is nulled in OnResponse which corresponds to the actor's 87 // __destroy__ method. 88 LSRequestChild* mActor; 89 RefPtr<Promise> mPromise; 90 const LSRequestParams mParams; 91 LSRequestResponse mResponse; 92 nsresult mResultCode; 93 State mState; 94 95 public: 96 AsyncRequestHelper(LocalStorageManager2* aManager, Promise* aPromise, 97 const LSRequestParams& aParams) 98 : Runnable("dom::LocalStorageManager2::AsyncRequestHelper"), 99 mManager(aManager), 100 mOwningEventTarget(GetCurrentSerialEventTarget()), 101 mActor(nullptr), 102 mPromise(aPromise), 103 mParams(aParams), 104 mResultCode(NS_OK), 105 mState(State::Initial) {} 106 107 bool IsOnOwningThread() const { 108 MOZ_ASSERT(mOwningEventTarget); 109 110 bool current; 111 return NS_SUCCEEDED(mOwningEventTarget->IsOnCurrentThread(¤t)) && 112 current; 113 } 114 115 void AssertIsOnOwningThread() const { 116 MOZ_ASSERT(NS_IsMainThread()); 117 MOZ_ASSERT(IsOnOwningThread()); 118 } 119 120 nsresult Dispatch(); 121 122 private: 123 ~AsyncRequestHelper() = default; 124 125 nsresult Start(); 126 127 void Finish(); 128 129 NS_DECL_ISUPPORTS_INHERITED 130 131 NS_DECL_NSIRUNNABLE 132 133 // LSRequestChildCallback 134 void OnResponse(LSRequestResponse&& aResponse) override; 135 }; 136 137 class SimpleRequestResolver final : public LSSimpleRequestChildCallback { 138 RefPtr<Promise> mPromise; 139 140 public: 141 explicit SimpleRequestResolver(Promise* aPromise) : mPromise(aPromise) {} 142 143 NS_INLINE_DECL_REFCOUNTING(SimpleRequestResolver, override); 144 145 private: 146 ~SimpleRequestResolver() = default; 147 148 void HandleResponse(nsresult aResponse); 149 150 void HandleResponse(bool aResponse); 151 152 void HandleResponse(const nsTArray<LSItemInfo>& aResponse); 153 154 // LSRequestChildCallback 155 void OnResponse(const LSSimpleRequestResponse& aResponse) override; 156 }; 157 158 nsresult CheckedPrincipalToPrincipalInfo( 159 nsIPrincipal* aPrincipal, mozilla::ipc::PrincipalInfo& aPrincipalInfo) { 160 MOZ_ASSERT(NS_IsMainThread()); 161 MOZ_ASSERT(aPrincipal); 162 163 nsresult rv = PrincipalToPrincipalInfo(aPrincipal, &aPrincipalInfo); 164 if (NS_WARN_IF(NS_FAILED(rv))) { 165 return rv; 166 } 167 168 if (NS_WARN_IF(!quota::IsPrincipalInfoValid(aPrincipalInfo))) { 169 return NS_ERROR_FAILURE; 170 } 171 172 if (aPrincipalInfo.type() != 173 mozilla::ipc::PrincipalInfo::TContentPrincipalInfo && 174 aPrincipalInfo.type() != 175 mozilla::ipc::PrincipalInfo::TSystemPrincipalInfo) { 176 return NS_ERROR_UNEXPECTED; 177 } 178 179 return NS_OK; 180 } 181 182 } // namespace 183 184 LocalStorageManager2::LocalStorageManager2() { 185 MOZ_ASSERT(NS_IsMainThread()); 186 MOZ_ASSERT(NextGenLocalStorageEnabled()); 187 } 188 189 LocalStorageManager2::~LocalStorageManager2() { MOZ_ASSERT(NS_IsMainThread()); } 190 191 NS_IMPL_ISUPPORTS(LocalStorageManager2, nsIDOMStorageManager, 192 nsILocalStorageManager) 193 194 NS_IMETHODIMP 195 LocalStorageManager2::PrecacheStorage(nsIPrincipal* aPrincipal, 196 nsIPrincipal* aStoragePrincipal, 197 Storage** _retval) { 198 MOZ_ASSERT(NS_IsMainThread()); 199 MOZ_ASSERT(aPrincipal); 200 MOZ_ASSERT(aStoragePrincipal); 201 MOZ_ASSERT(_retval); 202 203 // This method was created as part of the e10s-ification of the old LS 204 // implementation to perform a preload in the content/current process. That's 205 // not how things work in LSNG. Instead everything happens in the parent 206 // process, triggered by the official preloading spot, 207 // ContentParent::AboutToLoadHttpDocumentForChild. 208 return NS_ERROR_NOT_IMPLEMENTED; 209 } 210 211 NS_IMETHODIMP 212 LocalStorageManager2::CreateStorage(mozIDOMWindow* aWindow, 213 nsIPrincipal* aPrincipal, 214 nsIPrincipal* aStoragePrincipal, 215 const nsAString& aDocumentURI, 216 bool aPrivate, Storage** _retval) { 217 MOZ_ASSERT(NS_IsMainThread()); 218 MOZ_ASSERT(aPrincipal); 219 MOZ_ASSERT(aStoragePrincipal); 220 MOZ_ASSERT(_retval); 221 222 nsCOMPtr<nsPIDOMWindowInner> inner = nsPIDOMWindowInner::From(aWindow); 223 224 RefPtr<LSObject> object; 225 nsresult rv = LSObject::CreateForPrincipal(inner, aPrincipal, 226 aStoragePrincipal, aDocumentURI, 227 aPrivate, getter_AddRefs(object)); 228 if (NS_WARN_IF(NS_FAILED(rv))) { 229 return rv; 230 } 231 232 object.forget(_retval); 233 return NS_OK; 234 } 235 236 NS_IMETHODIMP 237 LocalStorageManager2::GetStorage(mozIDOMWindow* aWindow, 238 nsIPrincipal* aPrincipal, 239 nsIPrincipal* aStoragePrincipal, bool aPrivate, 240 Storage** _retval) { 241 MOZ_ASSERT(NS_IsMainThread()); 242 MOZ_ASSERT(aPrincipal); 243 MOZ_ASSERT(aStoragePrincipal); 244 MOZ_ASSERT(_retval); 245 246 return NS_ERROR_NOT_IMPLEMENTED; 247 } 248 249 NS_IMETHODIMP 250 LocalStorageManager2::CloneStorage(Storage* aStorageToCloneFrom) { 251 MOZ_ASSERT(NS_IsMainThread()); 252 MOZ_ASSERT(aStorageToCloneFrom); 253 254 // Cloning is specific to sessionStorage; state is forked when a new tab is 255 // opened from an existing tab. 256 return NS_ERROR_NOT_IMPLEMENTED; 257 } 258 259 NS_IMETHODIMP 260 LocalStorageManager2::CheckStorage(nsIPrincipal* aPrincipal, Storage* aStorage, 261 bool* _retval) { 262 MOZ_ASSERT(NS_IsMainThread()); 263 MOZ_ASSERT(aPrincipal); 264 MOZ_ASSERT(aStorage); 265 MOZ_ASSERT(_retval); 266 267 // Only used by sessionStorage. 268 return NS_ERROR_NOT_IMPLEMENTED; 269 } 270 271 NS_IMETHODIMP 272 LocalStorageManager2::GetNextGenLocalStorageEnabled(bool* aResult) { 273 MOZ_ASSERT(NS_IsMainThread()); 274 MOZ_ASSERT(aResult); 275 276 *aResult = NextGenLocalStorageEnabled(); 277 return NS_OK; 278 } 279 280 NS_IMETHODIMP 281 LocalStorageManager2::Preload(nsIPrincipal* aPrincipal, JSContext* aContext, 282 Promise** _retval) { 283 MOZ_ASSERT(NS_IsMainThread()); 284 MOZ_ASSERT(aPrincipal); 285 MOZ_ASSERT(_retval); 286 287 nsCString originAttrSuffix; 288 nsCString originKey; 289 nsresult rv = aPrincipal->GetStorageOriginKey(originKey); 290 aPrincipal->OriginAttributesRef().CreateSuffix(originAttrSuffix); 291 if (NS_FAILED(rv)) { 292 return NS_ERROR_NOT_AVAILABLE; 293 } 294 295 mozilla::ipc::PrincipalInfo principalInfo; 296 rv = CheckedPrincipalToPrincipalInfo(aPrincipal, principalInfo); 297 if (NS_WARN_IF(NS_FAILED(rv))) { 298 return rv; 299 } 300 301 RefPtr<Promise> promise; 302 303 if (aContext) { 304 rv = quota::CreatePromise(aContext, getter_AddRefs(promise)); 305 if (NS_WARN_IF(NS_FAILED(rv))) { 306 return rv; 307 } 308 } 309 310 LSRequestCommonParams commonParams; 311 commonParams.principalInfo() = principalInfo; 312 commonParams.storagePrincipalInfo() = principalInfo; 313 commonParams.originKey() = originKey; 314 315 LSRequestPreloadDatastoreParams params(commonParams); 316 317 RefPtr<AsyncRequestHelper> helper = 318 new AsyncRequestHelper(this, promise, params); 319 320 // This will start and finish the async request on the RemoteLazyInputStream 321 // thread. 322 // This must be done on RemoteLazyInputStream Thread because it's very likely 323 // that a content process will issue a prepare datastore request for the same 324 // principal while blocking the content process on the main thread. 325 // There would be a potential for deadlock if the preloading was initialized 326 // from the main thread of the parent process and a11y issued a synchronous 327 // message from the parent process to the content process (approximately at 328 // the same time) because the preload request wouldn't be able to respond 329 // to the Ready message by sending the Finish message which is needed to 330 // finish the preload request and unblock the prepare datastore request. 331 rv = helper->Dispatch(); 332 if (NS_WARN_IF(NS_FAILED(rv))) { 333 return rv; 334 } 335 336 promise.forget(_retval); 337 return NS_OK; 338 } 339 340 NS_IMETHODIMP 341 LocalStorageManager2::IsPreloaded(nsIPrincipal* aPrincipal, JSContext* aContext, 342 Promise** _retval) { 343 MOZ_ASSERT(NS_IsMainThread()); 344 MOZ_ASSERT(aPrincipal); 345 MOZ_ASSERT(_retval); 346 347 RefPtr<Promise> promise; 348 nsresult rv = quota::CreatePromise(aContext, getter_AddRefs(promise)); 349 if (NS_WARN_IF(NS_FAILED(rv))) { 350 return rv; 351 } 352 353 LSSimpleRequestPreloadedParams params; 354 355 rv = CheckedPrincipalToPrincipalInfo(aPrincipal, params.principalInfo()); 356 if (NS_WARN_IF(NS_FAILED(rv))) { 357 return rv; 358 } 359 360 params.storagePrincipalInfo() = params.principalInfo(); 361 362 rv = StartSimpleRequest(promise, params); 363 if (NS_WARN_IF(NS_FAILED(rv))) { 364 return rv; 365 } 366 367 promise.forget(_retval); 368 return NS_OK; 369 } 370 371 NS_IMETHODIMP 372 LocalStorageManager2::GetState(nsIPrincipal* aPrincipal, JSContext* aContext, 373 Promise** _retval) { 374 MOZ_ASSERT(NS_IsMainThread()); 375 MOZ_ASSERT(aPrincipal); 376 MOZ_ASSERT(_retval); 377 378 RefPtr<Promise> promise; 379 nsresult rv = quota::CreatePromise(aContext, getter_AddRefs(promise)); 380 if (NS_WARN_IF(NS_FAILED(rv))) { 381 return rv; 382 } 383 384 LSSimpleRequestGetStateParams params; 385 386 rv = CheckedPrincipalToPrincipalInfo(aPrincipal, params.principalInfo()); 387 if (NS_WARN_IF(NS_FAILED(rv))) { 388 return rv; 389 } 390 391 params.storagePrincipalInfo() = params.principalInfo(); 392 393 rv = StartSimpleRequest(promise, params); 394 if (NS_WARN_IF(NS_FAILED(rv))) { 395 return rv; 396 } 397 398 promise.forget(_retval); 399 return NS_OK; 400 } 401 402 LSRequestChild* LocalStorageManager2::StartRequest( 403 const LSRequestParams& aParams, LSRequestChildCallback* aCallback) { 404 AssertIsOnDOMFileThread(); 405 406 mozilla::ipc::PBackgroundChild* backgroundActor = 407 mozilla::ipc::BackgroundChild::GetOrCreateForCurrentThread(); 408 if (NS_WARN_IF(!backgroundActor)) { 409 return nullptr; 410 } 411 412 auto actor = new LSRequestChild(); 413 414 if (!backgroundActor->SendPBackgroundLSRequestConstructor(actor, aParams)) { 415 return nullptr; 416 } 417 418 // Must set callback after calling SendPBackgroundLSRequestConstructor since 419 // it can be called synchronously when SendPBackgroundLSRequestConstructor 420 // fails. 421 actor->SetCallback(aCallback); 422 423 return actor; 424 } 425 426 nsresult LocalStorageManager2::StartSimpleRequest( 427 Promise* aPromise, const LSSimpleRequestParams& aParams) { 428 MOZ_ASSERT(NS_IsMainThread()); 429 MOZ_ASSERT(aPromise); 430 431 mozilla::ipc::PBackgroundChild* backgroundActor = 432 mozilla::ipc::BackgroundChild::GetOrCreateForCurrentThread(); 433 if (NS_WARN_IF(!backgroundActor)) { 434 return NS_ERROR_FAILURE; 435 } 436 437 auto actor = new LSSimpleRequestChild(); 438 439 if (!backgroundActor->SendPBackgroundLSSimpleRequestConstructor(actor, 440 aParams)) { 441 return NS_ERROR_FAILURE; 442 } 443 444 RefPtr<SimpleRequestResolver> resolver = new SimpleRequestResolver(aPromise); 445 446 // Must set callback after calling SendPBackgroundLSRequestConstructor since 447 // it can be called synchronously when SendPBackgroundLSRequestConstructor 448 // fails. 449 actor->SetCallback(resolver); 450 451 return NS_OK; 452 } 453 454 nsresult AsyncRequestHelper::Dispatch() { 455 AssertIsOnOwningThread(); 456 457 nsCOMPtr<nsIEventTarget> domFileThread = 458 RemoteLazyInputStreamThread::GetOrCreate(); 459 if (NS_WARN_IF(!domFileThread)) { 460 return NS_ERROR_ILLEGAL_DURING_SHUTDOWN; 461 } 462 463 nsresult rv = domFileThread->Dispatch(this, NS_DISPATCH_NORMAL); 464 if (NS_WARN_IF(NS_FAILED(rv))) { 465 return rv; 466 } 467 468 return NS_OK; 469 } 470 471 nsresult AsyncRequestHelper::Start() { 472 AssertIsOnDOMFileThread(); 473 MOZ_ASSERT(mState == State::Initial); 474 475 mState = State::ResponsePending; 476 477 LSRequestChild* actor = mManager->StartRequest(mParams, this); 478 if (NS_WARN_IF(!actor)) { 479 return NS_ERROR_FAILURE; 480 } 481 482 mActor = actor; 483 484 return NS_OK; 485 } 486 487 void AsyncRequestHelper::Finish() { 488 AssertIsOnOwningThread(); 489 MOZ_ASSERT(mState == State::Finishing); 490 491 if (NS_WARN_IF(NS_FAILED(mResultCode))) { 492 if (mPromise) { 493 mPromise->MaybeReject(mResultCode); 494 } 495 } else { 496 switch (mResponse.type()) { 497 case LSRequestResponse::Tnsresult: 498 if (mPromise) { 499 mPromise->MaybeReject(mResponse.get_nsresult()); 500 } 501 break; 502 503 case LSRequestResponse::TLSRequestPreloadDatastoreResponse: 504 if (mPromise) { 505 const LSRequestPreloadDatastoreResponse& preloadDatastoreResponse = 506 mResponse.get_LSRequestPreloadDatastoreResponse(); 507 508 const bool invalidated = preloadDatastoreResponse.invalidated(); 509 510 if (invalidated) { 511 mPromise->MaybeReject(NS_ERROR_ABORT); 512 } else { 513 mPromise->MaybeResolveWithUndefined(); 514 } 515 } 516 break; 517 default: 518 MOZ_CRASH("Unknown response type!"); 519 } 520 } 521 522 mManager = nullptr; 523 mPromise = nullptr; 524 525 mState = State::Complete; 526 } 527 528 NS_IMPL_ISUPPORTS_INHERITED0(AsyncRequestHelper, Runnable) 529 530 NS_IMETHODIMP 531 AsyncRequestHelper::Run() { 532 nsresult rv; 533 534 switch (mState) { 535 case State::Initial: 536 rv = Start(); 537 break; 538 539 case State::Finishing: 540 Finish(); 541 return NS_OK; 542 543 default: 544 MOZ_CRASH("Bad state!"); 545 } 546 547 if (NS_WARN_IF(NS_FAILED(rv)) && mState != State::Finishing) { 548 if (NS_SUCCEEDED(mResultCode)) { 549 mResultCode = rv; 550 } 551 552 mState = State::Finishing; 553 554 if (IsOnOwningThread()) { 555 Finish(); 556 } else { 557 MOZ_ALWAYS_SUCCEEDS( 558 mOwningEventTarget->Dispatch(this, NS_DISPATCH_NORMAL)); 559 } 560 } 561 562 return NS_OK; 563 } 564 565 void AsyncRequestHelper::OnResponse(LSRequestResponse&& aResponse) { 566 AssertIsOnDOMFileThread(); 567 MOZ_ASSERT(mState == State::ResponsePending); 568 569 mActor = nullptr; 570 571 mResponse = std::move(aResponse); 572 573 mState = State::Finishing; 574 575 MOZ_ALWAYS_SUCCEEDS(mOwningEventTarget->Dispatch(this, NS_DISPATCH_NORMAL)); 576 } 577 578 void SimpleRequestResolver::HandleResponse(nsresult aResponse) { 579 MOZ_ASSERT(NS_IsMainThread()); 580 MOZ_ASSERT(mPromise); 581 582 mPromise->MaybeReject(aResponse); 583 } 584 585 void SimpleRequestResolver::HandleResponse(bool aResponse) { 586 MOZ_ASSERT(NS_IsMainThread()); 587 MOZ_ASSERT(mPromise); 588 589 mPromise->MaybeResolve(aResponse); 590 } 591 592 [[nodiscard]] static bool ToJSValue(JSContext* aCx, 593 const nsTArray<LSItemInfo>& aArgument, 594 JS::MutableHandle<JS::Value> aValue) { 595 JS::Rooted<JSObject*> obj(aCx, JS_NewPlainObject(aCx)); 596 if (!obj) { 597 return false; 598 } 599 600 for (size_t i = 0; i < aArgument.Length(); ++i) { 601 const LSItemInfo& itemInfo = aArgument[i]; 602 603 const nsString& key = itemInfo.key(); 604 605 JS::Rooted<JS::Value> value(aCx); 606 if (!ToJSValue(aCx, itemInfo.value().AsString(), &value)) { 607 return false; 608 } 609 610 if (!JS_DefineUCProperty(aCx, obj, key.BeginReading(), key.Length(), value, 611 JSPROP_ENUMERATE)) { 612 return false; 613 } 614 } 615 616 aValue.setObject(*obj); 617 return true; 618 } 619 620 void SimpleRequestResolver::HandleResponse( 621 const nsTArray<LSItemInfo>& aResponse) { 622 MOZ_ASSERT(NS_IsMainThread()); 623 MOZ_ASSERT(mPromise); 624 625 mPromise->MaybeResolve(aResponse); 626 } 627 628 void SimpleRequestResolver::OnResponse( 629 const LSSimpleRequestResponse& aResponse) { 630 MOZ_ASSERT(NS_IsMainThread()); 631 632 switch (aResponse.type()) { 633 case LSSimpleRequestResponse::Tnsresult: 634 HandleResponse(aResponse.get_nsresult()); 635 break; 636 637 case LSSimpleRequestResponse::TLSSimpleRequestPreloadedResponse: 638 HandleResponse( 639 aResponse.get_LSSimpleRequestPreloadedResponse().preloaded()); 640 break; 641 642 case LSSimpleRequestResponse::TLSSimpleRequestGetStateResponse: 643 HandleResponse( 644 aResponse.get_LSSimpleRequestGetStateResponse().itemInfos()); 645 break; 646 647 default: 648 MOZ_CRASH("Unknown response type!"); 649 } 650 } 651 652 } // namespace mozilla::dom