LSObject.cpp (34905B)
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 "LSObject.h" 8 9 // Local includes 10 #include "ActorsChild.h" 11 #include "LSDatabase.h" 12 #include "LSObserver.h" 13 14 // Global includes 15 #include <utility> 16 17 #include "MainThreadUtils.h" 18 #include "mozilla/AppShutdown.h" 19 #include "mozilla/BasePrincipal.h" 20 #include "mozilla/ErrorResult.h" 21 #include "mozilla/Monitor.h" 22 #include "mozilla/OriginAttributes.h" 23 #include "mozilla/Preferences.h" 24 #include "mozilla/RemoteLazyInputStreamThread.h" 25 #include "mozilla/ScopeExit.h" 26 #include "mozilla/SpinEventLoopUntil.h" 27 #include "mozilla/StaticMutex.h" 28 #include "mozilla/StorageAccess.h" 29 #include "mozilla/dom/ClientInfo.h" 30 #include "mozilla/dom/Document.h" 31 #include "mozilla/dom/LocalStorageCommon.h" 32 #include "mozilla/dom/PBackgroundLSRequest.h" 33 #include "mozilla/dom/PBackgroundLSSharedTypes.h" 34 #include "mozilla/dom/quota/PrincipalUtils.h" 35 #include "mozilla/glean/DomLocalstorageMetrics.h" 36 #include "mozilla/ipc/BackgroundChild.h" 37 #include "mozilla/ipc/BackgroundUtils.h" 38 #include "mozilla/ipc/PBackgroundChild.h" 39 #include "nsCOMPtr.h" 40 #include "nsContentUtils.h" 41 #include "nsDebug.h" 42 #include "nsError.h" 43 #include "nsIEventTarget.h" 44 #include "nsIPrincipal.h" 45 #include "nsIRunnable.h" 46 #include "nsIScriptObjectPrincipal.h" 47 #include "nsISerialEventTarget.h" 48 #include "nsITimer.h" 49 #include "nsPIDOMWindow.h" 50 #include "nsString.h" 51 #include "nsTArray.h" 52 #include "nsTStringRepr.h" 53 #include "nsThread.h" 54 #include "nsThreadUtils.h" 55 #include "nsXULAppAPI.h" 56 #include "nscore.h" 57 58 /** 59 * Automatically cancel and abort synchronous LocalStorage requests (for example 60 * datastore preparation) if they take this long. We've chosen a value that is 61 * long enough that it is unlikely for the problem to be falsely triggered by 62 * slow system I/O. We've also chosen a value long enough so that automated 63 * tests should time out and fail if LocalStorage hangs. Also, this value is 64 * long enough so that testers can notice the (content process) hang; we want to 65 * know about the hangs, not hide them. On the other hand this value is less 66 * than 60 seconds which is used by nsTerminator to crash a hung main process. 67 */ 68 #define FAILSAFE_CANCEL_SYNC_OP_MS 50000 69 70 /** 71 * Interval with which to wake up while waiting for the sync op to complete to 72 * check ExpectingShutdown(). 73 */ 74 #define SYNC_OP_WAKE_INTERVAL_MS 500 75 76 namespace mozilla::dom { 77 78 namespace { 79 80 class RequestHelper; 81 82 /** 83 * Main-thread helper that implements the blocking logic required by 84 * LocalStorage's synchronous semantics. StartAndReturnResponse blocks on a 85 * monitor until a result is received. See StartAndReturnResponse() for info on 86 * this choice. 87 * 88 * The normal life-cycle of this method looks like: 89 * - Main Thread: LSObject::DoRequestSynchronously creates a RequestHelper and 90 * invokes StartAndReturnResponse(). It Dispatches the RequestHelper to the 91 * RemoteLazyInputStream thread, and waits on mMonitor. 92 * - RemoteLazyInputStream Thread: RequestHelper::Run is called, invoking 93 * Start() which invokes LSObject::StartRequest, which gets-or-creates the 94 * PBackground actor if necessary, sends LSRequest constructor which is 95 * provided with a callback reference to the RequestHelper. State advances to 96 * ResponsePending. 97 * - RemoteLazyInputStreamThread: LSRequestChild::Recv__delete__ is received, 98 * which invokes RequestHelepr::OnResponse, advancing the state to Complete 99 * and notifying mMonitor. 100 * - Main Thread: The main thread wakes up after waiting on the monitor, 101 * returning the received response. 102 * 103 * See LocalStorageCommon.h for high-level context and method comments for 104 * low-level details. 105 */ 106 class RequestHelper final : public Runnable, public LSRequestChildCallback { 107 enum class State { 108 /** 109 * The RequestHelper has been created and dispatched to the 110 * RemoteLazyInputStream Thread. 111 */ 112 Initial, 113 /** 114 * Start() has been invoked on the RemoteLazyInputStream Thread and 115 * LSObject::StartRequest has been invoked from there, sending an IPC 116 * message to PBackground to service the request. We stay in this state 117 * until a response is received or a timeout occurs. 118 */ 119 ResponsePending, 120 /** 121 * The request timed out, or failed in some fashion, and needs to be 122 * cancelled. A runnable has been dispatched to the DOM File thread to 123 * notify the parent actor, and the main thread will continue to block until 124 * we receive a reponse. 125 */ 126 Canceling, 127 /** 128 * The request is complete, either successfully or due to being cancelled. 129 * The main thread can stop waiting and immediately return to the caller of 130 * StartAndReturnResponse. 131 */ 132 Complete 133 }; 134 135 // The object we are issuing a request on behalf of. Present because of the 136 // need to invoke LSObject::StartRequest off the main thread. Dropped on 137 // return to the main-thread in Finish(). 138 RefPtr<LSObject> mObject; 139 // The thread the RequestHelper was created on. This should be the main 140 // thread. 141 nsCOMPtr<nsIEventTarget> mOwningEventTarget; 142 // The IPC actor handling the request with standard IPC allocation rules. 143 // Our reference is nulled in OnResponse which corresponds to the actor's 144 // __destroy__ method. 145 LSRequestChild* mActor; 146 const LSRequestParams mParams; 147 Monitor mMonitor; 148 LSRequestResponse mResponse MOZ_GUARDED_BY(mMonitor); 149 nsresult mResultCode MOZ_GUARDED_BY(mMonitor); 150 State mState MOZ_GUARDED_BY(mMonitor); 151 152 public: 153 RequestHelper(LSObject* aObject, const LSRequestParams& aParams) 154 : Runnable("dom::RequestHelper"), 155 mObject(aObject), 156 mOwningEventTarget(GetCurrentSerialEventTarget()), 157 mActor(nullptr), 158 mParams(aParams), 159 mMonitor("dom::RequestHelper::mMonitor"), 160 mResultCode(NS_OK), 161 mState(State::Initial) {} 162 163 bool IsOnOwningThread() const { 164 MOZ_ASSERT(mOwningEventTarget); 165 166 bool current; 167 return NS_SUCCEEDED(mOwningEventTarget->IsOnCurrentThread(¤t)) && 168 current; 169 } 170 171 void AssertIsOnOwningThread() const { 172 MOZ_ASSERT(NS_IsMainThread()); 173 MOZ_ASSERT(IsOnOwningThread()); 174 } 175 176 nsresult StartAndReturnResponse(LSRequestResponse& aResponse); 177 178 private: 179 ~RequestHelper() = default; 180 181 NS_DECL_ISUPPORTS_INHERITED 182 183 NS_DECL_NSIRUNNABLE 184 185 // LSRequestChildCallback 186 void OnResponse(LSRequestResponse&& aResponse) override; 187 }; 188 189 void AssertExplicitSnapshotInvariants(const LSObject& aObject) { 190 // Can be only called if the mInExplicitSnapshot flag is true. 191 // An explicit snapshot must have been created. 192 MOZ_ASSERT(aObject.InExplicitSnapshot()); 193 194 // If an explicit snapshot has been created then mDatabase must be not null. 195 // DropDatabase could be called in the meatime, but that must be preceded by 196 // Disconnect which sets mInExplicitSnapshot to false. EnsureDatabase could 197 // be called in the meantime too, but that can't set mDatabase to null or to 198 // a new value. See the comment below. 199 MOZ_ASSERT(aObject.DatabaseStrongRef()); 200 201 // Existence of a snapshot prevents the database from allowing to close. See 202 // LSDatabase::RequestAllowToClose and LSDatabase::NoteFinishedSnapshot. 203 // If the database is not allowed to close then mDatabase could not have been 204 // nulled out or set to a new value. See EnsureDatabase. 205 MOZ_ASSERT(!aObject.DatabaseStrongRef()->IsAllowedToClose()); 206 } 207 208 } // namespace 209 210 LSObject::LSObject(nsPIDOMWindowInner* aWindow, nsIPrincipal* aPrincipal, 211 nsIPrincipal* aStoragePrincipal) 212 : Storage(aWindow, aPrincipal, aStoragePrincipal), 213 mPrivateBrowsingId(0), 214 mInExplicitSnapshot(false) { 215 AssertIsOnOwningThread(); 216 MOZ_ASSERT(NextGenLocalStorageEnabled()); 217 } 218 219 LSObject::~LSObject() { 220 AssertIsOnOwningThread(); 221 222 DropObserver(); 223 } 224 225 // static 226 nsresult LSObject::CreateForWindow(nsPIDOMWindowInner* aWindow, 227 Storage** aStorage) { 228 MOZ_ASSERT(NS_IsMainThread()); 229 MOZ_ASSERT(aWindow); 230 MOZ_ASSERT(aStorage); 231 MOZ_ASSERT(NextGenLocalStorageEnabled()); 232 MOZ_ASSERT(StorageAllowedForWindow(aWindow) != StorageAccess::eDeny); 233 234 nsCOMPtr<nsIScriptObjectPrincipal> sop = do_QueryInterface(aWindow); 235 MOZ_ASSERT(sop); 236 237 nsCOMPtr<nsIPrincipal> principal = sop->GetPrincipal(); 238 if (NS_WARN_IF(!principal)) { 239 return NS_ERROR_FAILURE; 240 } 241 242 nsCOMPtr<nsIPrincipal> storagePrincipal = sop->GetEffectiveStoragePrincipal(); 243 if (NS_WARN_IF(!storagePrincipal)) { 244 return NS_ERROR_FAILURE; 245 } 246 247 if (principal->IsSystemPrincipal()) { 248 return NS_ERROR_NOT_AVAILABLE; 249 } 250 251 // localStorage is not available on some pages on purpose, for example 252 // about:home. Match the old implementation by using GenerateOriginKey 253 // for the check. 254 nsCString originAttrSuffix; 255 nsCString originKey; 256 nsresult rv = storagePrincipal->GetStorageOriginKey(originKey); 257 storagePrincipal->OriginAttributesRef().CreateSuffix(originAttrSuffix); 258 259 if (NS_FAILED(rv)) { 260 return NS_ERROR_NOT_AVAILABLE; 261 } 262 263 auto principalInfo = MakeUnique<PrincipalInfo>(); 264 rv = PrincipalToPrincipalInfo(principal, principalInfo.get()); 265 if (NS_WARN_IF(NS_FAILED(rv))) { 266 return rv; 267 } 268 269 MOZ_ASSERT(principalInfo->type() == PrincipalInfo::TContentPrincipalInfo); 270 271 auto storagePrincipalInfo = MakeUnique<PrincipalInfo>(); 272 rv = PrincipalToPrincipalInfo(storagePrincipal, storagePrincipalInfo.get()); 273 if (NS_WARN_IF(NS_FAILED(rv))) { 274 return rv; 275 } 276 277 MOZ_ASSERT(storagePrincipalInfo->type() == 278 PrincipalInfo::TContentPrincipalInfo); 279 280 if (NS_WARN_IF(!quota::IsPrincipalInfoValid(*storagePrincipalInfo))) { 281 return NS_ERROR_FAILURE; 282 } 283 284 #ifdef DEBUG 285 QM_TRY_INSPECT(const auto& principalMetadata, 286 quota::GetInfoFromPrincipal(storagePrincipal.get())); 287 288 MOZ_ASSERT(originAttrSuffix == principalMetadata.mSuffix); 289 290 const auto& origin = principalMetadata.mOrigin; 291 #else 292 QM_TRY_INSPECT(const auto& origin, 293 quota::GetOriginFromPrincipal(storagePrincipal.get())); 294 #endif 295 296 uint32_t privateBrowsingId; 297 rv = storagePrincipal->GetPrivateBrowsingId(&privateBrowsingId); 298 if (NS_WARN_IF(NS_FAILED(rv))) { 299 return rv; 300 } 301 302 Maybe<ClientInfo> clientInfo = aWindow->GetClientInfo(); 303 if (clientInfo.isNothing()) { 304 return NS_ERROR_FAILURE; 305 } 306 307 Maybe<nsID> clientId = Some(clientInfo.ref().Id()); 308 309 Maybe<PrincipalInfo> clientPrincipalInfo = 310 Some(clientInfo.ref().PrincipalInfo()); 311 312 nsString documentURI; 313 if (nsCOMPtr<Document> doc = aWindow->GetExtantDoc()) { 314 rv = doc->GetDocumentURI(documentURI); 315 if (NS_WARN_IF(NS_FAILED(rv))) { 316 return rv; 317 } 318 } 319 320 RefPtr<LSObject> object = new LSObject(aWindow, principal, storagePrincipal); 321 object->mPrincipalInfo = std::move(principalInfo); 322 object->mStoragePrincipalInfo = std::move(storagePrincipalInfo); 323 object->mPrivateBrowsingId = privateBrowsingId; 324 object->mClientId = clientId; 325 object->mClientPrincipalInfo = clientPrincipalInfo; 326 object->mOrigin = origin; 327 object->mOriginKey = originKey; 328 object->mDocumentURI = documentURI; 329 330 object.forget(aStorage); 331 return NS_OK; 332 } 333 334 // static 335 nsresult LSObject::CreateForPrincipal(nsPIDOMWindowInner* aWindow, 336 nsIPrincipal* aPrincipal, 337 nsIPrincipal* aStoragePrincipal, 338 const nsAString& aDocumentURI, 339 bool aPrivate, LSObject** aObject) { 340 MOZ_ASSERT(NS_IsMainThread()); 341 MOZ_ASSERT(aPrincipal); 342 MOZ_ASSERT(aStoragePrincipal); 343 MOZ_ASSERT(aObject); 344 345 nsCString originAttrSuffix; 346 nsCString originKey; 347 nsresult rv = aStoragePrincipal->GetStorageOriginKey(originKey); 348 aStoragePrincipal->OriginAttributesRef().CreateSuffix(originAttrSuffix); 349 if (NS_FAILED(rv)) { 350 return NS_ERROR_NOT_AVAILABLE; 351 } 352 353 auto principalInfo = MakeUnique<PrincipalInfo>(); 354 rv = PrincipalToPrincipalInfo(aPrincipal, principalInfo.get()); 355 if (NS_WARN_IF(NS_FAILED(rv))) { 356 return rv; 357 } 358 359 MOZ_ASSERT(principalInfo->type() == PrincipalInfo::TContentPrincipalInfo || 360 principalInfo->type() == PrincipalInfo::TSystemPrincipalInfo); 361 362 auto storagePrincipalInfo = MakeUnique<PrincipalInfo>(); 363 rv = PrincipalToPrincipalInfo(aStoragePrincipal, storagePrincipalInfo.get()); 364 if (NS_WARN_IF(NS_FAILED(rv))) { 365 return rv; 366 } 367 368 MOZ_ASSERT( 369 storagePrincipalInfo->type() == PrincipalInfo::TContentPrincipalInfo || 370 storagePrincipalInfo->type() == PrincipalInfo::TSystemPrincipalInfo); 371 372 if (NS_WARN_IF(!quota::IsPrincipalInfoValid(*storagePrincipalInfo))) { 373 return NS_ERROR_FAILURE; 374 } 375 376 #ifdef DEBUG 377 QM_TRY_INSPECT( 378 const auto& principalMetadata, 379 ([&storagePrincipalInfo, 380 &aPrincipal]() -> Result<quota::PrincipalMetadata, nsresult> { 381 if (storagePrincipalInfo->type() == 382 PrincipalInfo::TSystemPrincipalInfo) { 383 return quota::GetInfoForChrome(); 384 } 385 386 QM_TRY_RETURN(quota::GetInfoFromPrincipal(aPrincipal)); 387 }())); 388 389 MOZ_ASSERT(originAttrSuffix == principalMetadata.mSuffix); 390 391 const auto& origin = principalMetadata.mOrigin; 392 #else 393 QM_TRY_INSPECT(const auto& origin, 394 ([&storagePrincipalInfo, 395 &aPrincipal]() -> Result<nsAutoCString, nsresult> { 396 if (storagePrincipalInfo->type() == 397 PrincipalInfo::TSystemPrincipalInfo) { 398 return nsAutoCString{quota::GetOriginForChrome()}; 399 } 400 401 QM_TRY_RETURN(quota::GetOriginFromPrincipal(aPrincipal)); 402 }())); 403 #endif 404 405 Maybe<nsID> clientId; 406 if (aWindow) { 407 Maybe<ClientInfo> clientInfo = aWindow->GetClientInfo(); 408 if (clientInfo.isNothing()) { 409 return NS_ERROR_FAILURE; 410 } 411 412 clientId = Some(clientInfo.ref().Id()); 413 } else if (Preferences::GetBool("dom.storage.client_validation")) { 414 return NS_ERROR_FAILURE; 415 } 416 417 RefPtr<LSObject> object = 418 new LSObject(aWindow, aPrincipal, aStoragePrincipal); 419 object->mPrincipalInfo = std::move(principalInfo); 420 object->mStoragePrincipalInfo = std::move(storagePrincipalInfo); 421 object->mPrivateBrowsingId = aPrivate ? 1 : 0; 422 object->mClientId = clientId; 423 object->mOrigin = origin; 424 object->mOriginKey = originKey; 425 object->mDocumentURI = aDocumentURI; 426 427 object.forget(aObject); 428 return NS_OK; 429 } // namespace dom 430 431 LSRequestChild* LSObject::StartRequest(const LSRequestParams& aParams, 432 LSRequestChildCallback* aCallback) { 433 AssertIsOnDOMFileThread(); 434 435 mozilla::ipc::PBackgroundChild* backgroundActor = 436 mozilla::ipc::BackgroundChild::GetOrCreateForCurrentThread(); 437 if (NS_WARN_IF(!backgroundActor)) { 438 return nullptr; 439 } 440 441 LSRequestChild* actor = new LSRequestChild(); 442 443 if (!backgroundActor->SendPBackgroundLSRequestConstructor(actor, aParams)) { 444 return nullptr; 445 } 446 447 // Must set callback after calling SendPBackgroundLSRequestConstructor since 448 // it can be called synchronously when SendPBackgroundLSRequestConstructor 449 // fails. 450 actor->SetCallback(aCallback); 451 452 return actor; 453 } 454 455 Storage::StorageType LSObject::Type() const { 456 AssertIsOnOwningThread(); 457 458 return eLocalStorage; 459 } 460 461 bool LSObject::IsForkOf(const Storage* aStorage) const { 462 AssertIsOnOwningThread(); 463 MOZ_ASSERT(aStorage); 464 465 if (aStorage->Type() != eLocalStorage) { 466 return false; 467 } 468 469 return static_cast<const LSObject*>(aStorage)->mOrigin == mOrigin; 470 } 471 472 int64_t LSObject::GetOriginQuotaUsage() const { 473 AssertIsOnOwningThread(); 474 475 // It's not necessary to return an actual value here. This method is 476 // implemented only because the SessionStore currently needs it to cap the 477 // amount of data it persists to disk (via nsIDOMWindowUtils.getStorageUsage). 478 // Any callers that want to know about storage usage should be asking 479 // QuotaManager directly. 480 // 481 // Note: This may change as LocalStorage is repurposed to be the new 482 // SessionStorage backend. 483 return 0; 484 } 485 486 void LSObject::Disconnect() { 487 // Explicit snapshots which were not ended in JS, must be ended here while 488 // IPC is still available. We can't do that in DropDatabase because actors 489 // may have been destroyed already at that point. 490 if (mInExplicitSnapshot) { 491 AssertExplicitSnapshotInvariants(*this); 492 493 nsresult rv = mDatabase->EndExplicitSnapshot(); 494 (void)NS_WARN_IF(NS_FAILED(rv)); 495 496 mInExplicitSnapshot = false; 497 } 498 } 499 500 uint32_t LSObject::GetLength(nsIPrincipal& aSubjectPrincipal, 501 ErrorResult& aError) { 502 AssertIsOnOwningThread(); 503 504 if (!CanUseStorage(aSubjectPrincipal)) { 505 aError.Throw(NS_ERROR_DOM_SECURITY_ERR); 506 return 0; 507 } 508 509 nsresult rv = EnsureDatabase(); 510 if (NS_WARN_IF(NS_FAILED(rv))) { 511 aError.Throw(rv); 512 return 0; 513 } 514 515 uint32_t result; 516 rv = mDatabase->GetLength(this, &result); 517 if (NS_WARN_IF(NS_FAILED(rv))) { 518 aError.Throw(rv); 519 return 0; 520 } 521 522 return result; 523 } 524 525 void LSObject::Key(uint32_t aIndex, nsAString& aResult, 526 nsIPrincipal& aSubjectPrincipal, ErrorResult& aError) { 527 AssertIsOnOwningThread(); 528 529 if (!CanUseStorage(aSubjectPrincipal)) { 530 aError.Throw(NS_ERROR_DOM_SECURITY_ERR); 531 return; 532 } 533 534 nsresult rv = EnsureDatabase(); 535 if (NS_WARN_IF(NS_FAILED(rv))) { 536 aError.Throw(rv); 537 return; 538 } 539 540 nsString result; 541 rv = mDatabase->GetKey(this, aIndex, result); 542 if (NS_WARN_IF(NS_FAILED(rv))) { 543 aError.Throw(rv); 544 return; 545 } 546 547 aResult = result; 548 } 549 550 void LSObject::GetItem(const nsAString& aKey, nsAString& aResult, 551 nsIPrincipal& aSubjectPrincipal, ErrorResult& aError) { 552 AssertIsOnOwningThread(); 553 554 if (!CanUseStorage(aSubjectPrincipal)) { 555 aError.Throw(NS_ERROR_DOM_SECURITY_ERR); 556 return; 557 } 558 559 nsresult rv = EnsureDatabase(); 560 if (NS_WARN_IF(NS_FAILED(rv))) { 561 aError.Throw(rv); 562 return; 563 } 564 565 nsString result; 566 rv = mDatabase->GetItem(this, aKey, result); 567 if (NS_WARN_IF(NS_FAILED(rv))) { 568 aError.Throw(rv); 569 return; 570 } 571 572 aResult = result; 573 } 574 575 void LSObject::GetSupportedNames(nsTArray<nsString>& aNames) { 576 AssertIsOnOwningThread(); 577 578 if (!CanUseStorage(*nsContentUtils::SubjectPrincipal())) { 579 // Return just an empty array. 580 aNames.Clear(); 581 return; 582 } 583 584 nsresult rv = EnsureDatabase(); 585 if (NS_WARN_IF(NS_FAILED(rv))) { 586 return; 587 } 588 589 rv = mDatabase->GetKeys(this, aNames); 590 if (NS_WARN_IF(NS_FAILED(rv))) { 591 return; 592 } 593 } 594 595 void LSObject::SetItem(const nsAString& aKey, const nsAString& aValue, 596 nsIPrincipal& aSubjectPrincipal, ErrorResult& aError) { 597 AssertIsOnOwningThread(); 598 599 if (!CanUseStorage(aSubjectPrincipal)) { 600 aError.Throw(NS_ERROR_DOM_SECURITY_ERR); 601 return; 602 } 603 604 nsresult rv = EnsureDatabase(); 605 if (NS_WARN_IF(NS_FAILED(rv))) { 606 aError.Throw(rv); 607 return; 608 } 609 610 LSNotifyInfo info; 611 rv = mDatabase->SetItem(this, aKey, aValue, info); 612 if (rv == NS_ERROR_FILE_NO_DEVICE_SPACE) { 613 rv = NS_ERROR_DOM_QUOTA_EXCEEDED_ERR; 614 } 615 if (NS_WARN_IF(NS_FAILED(rv))) { 616 aError.Throw(rv); 617 return; 618 } 619 620 if (info.changed()) { 621 OnChange(aKey, info.oldValue(), aValue); 622 } 623 } 624 625 void LSObject::RemoveItem(const nsAString& aKey, 626 nsIPrincipal& aSubjectPrincipal, 627 ErrorResult& aError) { 628 AssertIsOnOwningThread(); 629 630 if (!CanUseStorage(aSubjectPrincipal)) { 631 aError.Throw(NS_ERROR_DOM_SECURITY_ERR); 632 return; 633 } 634 635 nsresult rv = EnsureDatabase(); 636 if (NS_WARN_IF(NS_FAILED(rv))) { 637 aError.Throw(rv); 638 return; 639 } 640 641 LSNotifyInfo info; 642 rv = mDatabase->RemoveItem(this, aKey, info); 643 if (NS_WARN_IF(NS_FAILED(rv))) { 644 aError.Throw(rv); 645 return; 646 } 647 648 if (info.changed()) { 649 OnChange(aKey, info.oldValue(), VoidString()); 650 } 651 } 652 653 void LSObject::Clear(nsIPrincipal& aSubjectPrincipal, ErrorResult& aError) { 654 AssertIsOnOwningThread(); 655 656 if (!CanUseStorage(aSubjectPrincipal)) { 657 aError.Throw(NS_ERROR_DOM_SECURITY_ERR); 658 return; 659 } 660 661 nsresult rv = EnsureDatabase(); 662 if (NS_WARN_IF(NS_FAILED(rv))) { 663 aError.Throw(rv); 664 return; 665 } 666 667 LSNotifyInfo info; 668 rv = mDatabase->Clear(this, info); 669 if (NS_WARN_IF(NS_FAILED(rv))) { 670 aError.Throw(rv); 671 return; 672 } 673 674 if (info.changed()) { 675 OnChange(VoidString(), VoidString(), VoidString()); 676 } 677 } 678 679 void LSObject::Open(nsIPrincipal& aSubjectPrincipal, ErrorResult& aError) { 680 AssertIsOnOwningThread(); 681 682 if (!CanUseStorage(aSubjectPrincipal)) { 683 aError.Throw(NS_ERROR_DOM_SECURITY_ERR); 684 return; 685 } 686 687 nsresult rv = EnsureDatabase(); 688 if (NS_WARN_IF(NS_FAILED(rv))) { 689 aError.Throw(rv); 690 return; 691 } 692 } 693 694 void LSObject::Close(nsIPrincipal& aSubjectPrincipal, ErrorResult& aError) { 695 AssertIsOnOwningThread(); 696 697 if (!CanUseStorage(aSubjectPrincipal)) { 698 aError.Throw(NS_ERROR_DOM_SECURITY_ERR); 699 return; 700 } 701 702 DropDatabase(); 703 } 704 705 void LSObject::BeginExplicitSnapshot(nsIPrincipal& aSubjectPrincipal, 706 ErrorResult& aError) { 707 AssertIsOnOwningThread(); 708 709 if (!CanUseStorage(aSubjectPrincipal)) { 710 aError.Throw(NS_ERROR_DOM_SECURITY_ERR); 711 return; 712 } 713 714 if (mInExplicitSnapshot) { 715 aError.Throw(NS_ERROR_ALREADY_INITIALIZED); 716 return; 717 } 718 719 nsresult rv = EnsureDatabase(); 720 if (NS_WARN_IF(NS_FAILED(rv))) { 721 aError.Throw(rv); 722 return; 723 } 724 725 rv = mDatabase->BeginExplicitSnapshot(this); 726 if (NS_WARN_IF(NS_FAILED(rv))) { 727 aError.Throw(rv); 728 return; 729 } 730 731 mInExplicitSnapshot = true; 732 } 733 734 void LSObject::CheckpointExplicitSnapshot(nsIPrincipal& aSubjectPrincipal, 735 ErrorResult& aError) { 736 AssertIsOnOwningThread(); 737 738 if (!CanUseStorage(aSubjectPrincipal)) { 739 aError.Throw(NS_ERROR_DOM_SECURITY_ERR); 740 return; 741 } 742 743 if (!mInExplicitSnapshot) { 744 aError.Throw(NS_ERROR_NOT_INITIALIZED); 745 return; 746 } 747 748 AssertExplicitSnapshotInvariants(*this); 749 750 nsresult rv = mDatabase->CheckpointExplicitSnapshot(); 751 if (NS_WARN_IF(NS_FAILED(rv))) { 752 aError.Throw(rv); 753 return; 754 } 755 } 756 757 void LSObject::EndExplicitSnapshot(nsIPrincipal& aSubjectPrincipal, 758 ErrorResult& aError) { 759 AssertIsOnOwningThread(); 760 761 if (!CanUseStorage(aSubjectPrincipal)) { 762 aError.Throw(NS_ERROR_DOM_SECURITY_ERR); 763 return; 764 } 765 766 if (!mInExplicitSnapshot) { 767 aError.Throw(NS_ERROR_NOT_INITIALIZED); 768 return; 769 } 770 771 AssertExplicitSnapshotInvariants(*this); 772 773 nsresult rv = mDatabase->EndExplicitSnapshot(); 774 if (NS_WARN_IF(NS_FAILED(rv))) { 775 aError.Throw(rv); 776 return; 777 } 778 779 mInExplicitSnapshot = false; 780 } 781 782 bool LSObject::GetHasSnapshot(nsIPrincipal& aSubjectPrincipal, 783 ErrorResult& aError) { 784 AssertIsOnOwningThread(); 785 786 if (!CanUseStorage(aSubjectPrincipal)) { 787 aError.Throw(NS_ERROR_DOM_SECURITY_ERR); 788 return false; 789 } 790 791 // We can't call `HasSnapshot` on the database if it's being closed, but we 792 // know that a database which is being closed can't have a snapshot, so we 793 // return false in that case directly here. 794 if (!mDatabase || mDatabase->IsAllowedToClose()) { 795 return false; 796 } 797 798 return mDatabase->HasSnapshot(); 799 } 800 801 int64_t LSObject::GetSnapshotUsage(nsIPrincipal& aSubjectPrincipal, 802 ErrorResult& aError) { 803 AssertIsOnOwningThread(); 804 805 if (!CanUseStorage(aSubjectPrincipal)) { 806 aError.Throw(NS_ERROR_DOM_SECURITY_ERR); 807 return 0; 808 } 809 810 if (!mDatabase || mDatabase->IsAllowedToClose()) { 811 aError.Throw(NS_ERROR_NOT_AVAILABLE); 812 return 0; 813 } 814 815 if (!mDatabase->HasSnapshot()) { 816 aError.Throw(NS_ERROR_NOT_AVAILABLE); 817 return 0; 818 } 819 820 return mDatabase->GetSnapshotUsage(); 821 } 822 823 NS_IMPL_ADDREF_INHERITED(LSObject, Storage) 824 NS_IMPL_RELEASE_INHERITED(LSObject, Storage) 825 826 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(LSObject) 827 NS_INTERFACE_MAP_END_INHERITING(Storage) 828 829 NS_IMPL_CYCLE_COLLECTION_CLASS(LSObject) 830 831 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(LSObject, Storage) 832 tmp->AssertIsOnOwningThread(); 833 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END 834 835 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(LSObject, Storage) 836 tmp->AssertIsOnOwningThread(); 837 tmp->DropDatabase(); 838 NS_IMPL_CYCLE_COLLECTION_UNLINK_END 839 840 nsresult LSObject::DoRequestSynchronously(const LSRequestParams& aParams, 841 LSRequestResponse& aResponse) { 842 // We don't need this yet, but once the request successfully finishes, it's 843 // too late to initialize PBackground child on the owning thread, because 844 // it can fail and parent would keep an extra strong ref to the datastore or 845 // observer. 846 mozilla::ipc::PBackgroundChild* backgroundActor = 847 mozilla::ipc::BackgroundChild::GetOrCreateForCurrentThread(); 848 if (NS_WARN_IF(!backgroundActor)) { 849 return NS_ERROR_FAILURE; 850 } 851 852 RefPtr<RequestHelper> helper = new RequestHelper(this, aParams); 853 854 // This will start and finish the request on the RemoteLazyInputStream thread. 855 // The owning thread is synchronously blocked while the request is 856 // asynchronously processed on the RemoteLazyInputStream thread. 857 nsresult rv = helper->StartAndReturnResponse(aResponse); 858 if (NS_WARN_IF(NS_FAILED(rv))) { 859 return rv; 860 } 861 862 if (aResponse.type() == LSRequestResponse::Tnsresult) { 863 nsresult errorCode = aResponse.get_nsresult(); 864 865 if (errorCode == NS_ERROR_FILE_NO_DEVICE_SPACE) { 866 errorCode = NS_ERROR_DOM_QUOTA_EXCEEDED_ERR; 867 } 868 869 return errorCode; 870 } 871 872 return NS_OK; 873 } 874 875 nsresult LSObject::EnsureDatabase() { 876 AssertIsOnOwningThread(); 877 878 if (mDatabase && !mDatabase->IsAllowedToClose()) { 879 return NS_OK; 880 } 881 882 mDatabase = LSDatabase::Get(mOrigin); 883 884 if (mDatabase) { 885 MOZ_ASSERT(!mDatabase->IsAllowedToClose()); 886 return NS_OK; 887 } 888 889 auto timerId = glean::localstorage_database::new_object_setup_time.Start(); 890 891 auto autoCancelTimer = MakeScopeExit([&timerId] { 892 glean::localstorage_database::new_object_setup_time.Cancel( 893 std::move(timerId)); 894 }); 895 896 // We don't need this yet, but once the request successfully finishes, it's 897 // too late to initialize PBackground child on the owning thread, because 898 // it can fail and parent would keep an extra strong ref to the datastore. 899 mozilla::ipc::PBackgroundChild* backgroundActor = 900 mozilla::ipc::BackgroundChild::GetOrCreateForCurrentThread(); 901 if (NS_WARN_IF(!backgroundActor)) { 902 return NS_ERROR_FAILURE; 903 } 904 905 LSRequestCommonParams commonParams; 906 commonParams.principalInfo() = *mPrincipalInfo; 907 commonParams.storagePrincipalInfo() = *mStoragePrincipalInfo; 908 commonParams.originKey() = mOriginKey; 909 910 LSRequestPrepareDatastoreParams params; 911 params.commonParams() = commonParams; 912 params.clientId() = mClientId; 913 params.clientPrincipalInfo() = mClientPrincipalInfo; 914 915 LSRequestResponse response; 916 917 nsresult rv = DoRequestSynchronously(params, response); 918 if (NS_WARN_IF(NS_FAILED(rv))) { 919 return rv; 920 } 921 922 MOZ_ASSERT(response.type() == 923 LSRequestResponse::TLSRequestPrepareDatastoreResponse); 924 925 LSRequestPrepareDatastoreResponse prepareDatastoreResponse = 926 std::move(response.get_LSRequestPrepareDatastoreResponse()); 927 928 auto childEndpoint = 929 std::move(prepareDatastoreResponse.databaseChildEndpoint()); 930 931 // The datastore is now ready on the parent side (prepared by the asynchronous 932 // request on the RemoteLazyInputStream thread). 933 // Let's create a direct connection to the datastore (through a database 934 // actor) from the owning thread. 935 // Note that we now can't error out, otherwise parent will keep an extra 936 // strong reference to the datastore. 937 938 RefPtr<LSDatabase> database = new LSDatabase(mOrigin); 939 940 RefPtr<LSDatabaseChild> actor = new LSDatabaseChild(database); 941 942 MOZ_ALWAYS_TRUE(childEndpoint.Bind(actor)); 943 944 database->SetActor(actor); 945 946 if (prepareDatastoreResponse.invalidated()) { 947 database->RequestAllowToClose(); 948 return NS_ERROR_ABORT; 949 } 950 951 autoCancelTimer.release(); 952 953 glean::localstorage_database::new_object_setup_time.StopAndAccumulate( 954 std::move(timerId)); 955 956 mDatabase = std::move(database); 957 958 return NS_OK; 959 } 960 961 void LSObject::DropDatabase() { 962 AssertIsOnOwningThread(); 963 964 mDatabase = nullptr; 965 } 966 967 nsresult LSObject::EnsureObserver() { 968 AssertIsOnOwningThread(); 969 970 if (mObserver) { 971 return NS_OK; 972 } 973 974 mObserver = LSObserver::Get(mOrigin); 975 976 if (mObserver) { 977 return NS_OK; 978 } 979 980 LSRequestPrepareObserverParams params; 981 params.principalInfo() = *mPrincipalInfo; 982 params.storagePrincipalInfo() = *mStoragePrincipalInfo; 983 params.clientId() = mClientId; 984 params.clientPrincipalInfo() = mClientPrincipalInfo; 985 986 LSRequestResponse response; 987 988 nsresult rv = DoRequestSynchronously(params, response); 989 if (NS_WARN_IF(NS_FAILED(rv))) { 990 return rv; 991 } 992 993 MOZ_ASSERT(response.type() == 994 LSRequestResponse::TLSRequestPrepareObserverResponse); 995 996 const LSRequestPrepareObserverResponse& prepareObserverResponse = 997 response.get_LSRequestPrepareObserverResponse(); 998 999 uint64_t observerId = prepareObserverResponse.observerId(); 1000 1001 // The obsserver is now ready on the parent side (prepared by the asynchronous 1002 // request on the RemoteLazyInputStream thread). 1003 // Let's create a direct connection to the observer (through an observer 1004 // actor) from the owning thread. 1005 // Note that we now can't error out, otherwise parent will keep an extra 1006 // strong reference to the observer. 1007 1008 mozilla::ipc::PBackgroundChild* backgroundActor = 1009 mozilla::ipc::BackgroundChild::GetForCurrentThread(); 1010 MOZ_ASSERT(backgroundActor); 1011 1012 RefPtr<LSObserver> observer = new LSObserver(mOrigin); 1013 1014 LSObserverChild* actor = new LSObserverChild(observer); 1015 1016 MOZ_ALWAYS_TRUE( 1017 backgroundActor->SendPBackgroundLSObserverConstructor(actor, observerId)); 1018 1019 observer->SetActor(actor); 1020 1021 mObserver = std::move(observer); 1022 1023 return NS_OK; 1024 } 1025 1026 void LSObject::DropObserver() { 1027 AssertIsOnOwningThread(); 1028 1029 if (mObserver) { 1030 mObserver = nullptr; 1031 } 1032 } 1033 1034 void LSObject::OnChange(const nsAString& aKey, const nsAString& aOldValue, 1035 const nsAString& aNewValue) { 1036 AssertIsOnOwningThread(); 1037 1038 NotifyChange(/* aStorage */ this, StoragePrincipal(), aKey, aOldValue, 1039 aNewValue, /* aStorageType */ kLocalStorageType, mDocumentURI, 1040 /* aIsPrivate */ !!mPrivateBrowsingId, 1041 /* aImmediateDispatch */ false); 1042 } 1043 1044 void LSObject::LastRelease() { 1045 AssertIsOnOwningThread(); 1046 1047 DropDatabase(); 1048 } 1049 1050 nsresult RequestHelper::StartAndReturnResponse(LSRequestResponse& aResponse) { 1051 AssertIsOnOwningThread(); 1052 1053 nsCOMPtr<nsIEventTarget> domFileThread = 1054 RemoteLazyInputStreamThread::GetOrCreate(); 1055 if (NS_WARN_IF(!domFileThread)) { 1056 return NS_ERROR_ILLEGAL_DURING_SHUTDOWN; 1057 } 1058 1059 nsresult rv = domFileThread->Dispatch(this, NS_DISPATCH_NORMAL); 1060 if (NS_WARN_IF(NS_FAILED(rv))) { 1061 return rv; 1062 } 1063 1064 TimeStamp deadline = TimeStamp::Now() + TimeDuration::FromMilliseconds( 1065 FAILSAFE_CANCEL_SYNC_OP_MS); 1066 1067 MonitorAutoLock lock(mMonitor); 1068 while (mState != State::Complete) { 1069 TimeStamp now = TimeStamp::Now(); 1070 // If we are expecting shutdown or have passed our deadline, immediately 1071 // dispatch ourselves to the DOM File thread to cancel the operation. We 1072 // don't abort until the cancellation has gone through, as otherwise we 1073 // could race with the DOM File thread. 1074 if (AppShutdown::IsShutdownImpending() || now >= deadline) { 1075 switch (mState) { 1076 case State::Initial: 1077 // The DOM File thread never even woke before ExpectingShutdown() or a 1078 // timeout - skip even creating the actor and just report an error. 1079 mResultCode = NS_ERROR_FAILURE; 1080 mState = State::Complete; 1081 continue; 1082 case State::ResponsePending: 1083 // The DOM File thread is currently waiting for a reply, switch to a 1084 // canceling state, and notify it to cancel by dispatching a runnable. 1085 mState = State::Canceling; 1086 MOZ_ALWAYS_SUCCEEDS( 1087 domFileThread->Dispatch(this, NS_DISPATCH_NORMAL)); 1088 [[fallthrough]]; 1089 case State::Canceling: 1090 // We've cancelled the request, so just need to wait indefinitely for 1091 // it to complete. 1092 lock.Wait(); 1093 continue; 1094 default: 1095 MOZ_ASSERT_UNREACHABLE("unexpected state"); 1096 } 1097 } 1098 1099 // Wait until either we reach out deadline or for SYNC_OP_WAIT_INTERVAL_MS. 1100 lock.Wait(TimeDuration::Min( 1101 TimeDuration::FromMilliseconds(SYNC_OP_WAKE_INTERVAL_MS), 1102 deadline - now)); 1103 } 1104 1105 // The operation is complete, clear our reference to the LSObject. 1106 mObject = nullptr; 1107 1108 if (NS_WARN_IF(NS_FAILED(mResultCode))) { 1109 return mResultCode; 1110 } 1111 1112 aResponse = std::move(mResponse); 1113 return NS_OK; 1114 } 1115 1116 NS_IMPL_ISUPPORTS_INHERITED0(RequestHelper, Runnable) 1117 1118 NS_IMETHODIMP 1119 RequestHelper::Run() { 1120 AssertIsOnDOMFileThread(); 1121 1122 MonitorAutoLock lock(mMonitor); 1123 1124 switch (mState) { 1125 case State::Initial: { 1126 mState = State::ResponsePending; 1127 { 1128 MonitorAutoUnlock unlock(mMonitor); 1129 mActor = mObject->StartRequest(mParams, this); 1130 } 1131 if (NS_WARN_IF(!mActor) && mState != State::Complete) { 1132 // If we fail to even create the actor, instantly fail and notify our 1133 // caller of the error. Otherwise we'll notify from OnResponse as called 1134 // by the actor. 1135 mResultCode = NS_ERROR_FAILURE; 1136 mState = State::Complete; 1137 lock.Notify(); 1138 } 1139 return NS_OK; 1140 } 1141 1142 case State::Canceling: { 1143 // StartRequest() could fail or OnResponse was already called, so we need 1144 // to check if actor is not null. The actor can also be in the final 1145 // (finishing) state, in that case we are not allowed to send the cancel 1146 // message and it wouldn't make any sense because the request is about to 1147 // be destroyed anyway. 1148 if (mActor && !mActor->Finishing()) { 1149 if (mActor->SendCancel()) { 1150 glean::localstorage_request::send_cancel_counter.Add(); 1151 } 1152 } 1153 1154 return NS_OK; 1155 } 1156 1157 case State::Complete: { 1158 // The operation was cancelled before we ran, do nothing. 1159 return NS_OK; 1160 } 1161 1162 default: 1163 MOZ_CRASH("Bad state!"); 1164 } 1165 } 1166 1167 void RequestHelper::OnResponse(LSRequestResponse&& aResponse) { 1168 AssertIsOnDOMFileThread(); 1169 1170 MonitorAutoLock lock(mMonitor); 1171 1172 MOZ_ASSERT(mState == State::ResponsePending || mState == State::Canceling); 1173 1174 mActor = nullptr; 1175 1176 mResponse = std::move(aResponse); 1177 1178 mState = State::Complete; 1179 1180 lock.Notify(); 1181 } 1182 1183 } // namespace mozilla::dom