ActorsParent.cpp (47355B)
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 file, 5 * You can obtain one at http://mozilla.org/MPL/2.0/. */ 6 7 #include "ActorsParent.h" 8 9 // Local includes 10 #include "SimpleDBCommon.h" 11 12 // Global includes 13 #include <cstdint> 14 #include <new> 15 #include <utility> 16 17 #include "ErrorList.h" 18 #include "MainThreadUtils.h" 19 #include "NotifyUtils.h" 20 #include "mozilla/AlreadyAddRefed.h" 21 #include "mozilla/Assertions.h" 22 #include "mozilla/Atomics.h" 23 #include "mozilla/DebugOnly.h" 24 #include "mozilla/FixedBufferOutputStream.h" 25 #include "mozilla/Maybe.h" 26 #include "mozilla/Preferences.h" 27 #include "mozilla/RefPtr.h" 28 #include "mozilla/Result.h" 29 #include "mozilla/ResultExtensions.h" 30 #include "mozilla/SpinEventLoopUntil.h" 31 #include "mozilla/StaticPtr.h" 32 #include "mozilla/dom/PBackgroundSDBConnection.h" 33 #include "mozilla/dom/PBackgroundSDBConnectionParent.h" 34 #include "mozilla/dom/PBackgroundSDBRequestParent.h" 35 #include "mozilla/dom/ipc/IdType.h" 36 #include "mozilla/dom/quota/Client.h" 37 #include "mozilla/dom/quota/ClientDirectoryLock.h" 38 #include "mozilla/dom/quota/ClientDirectoryLockHandle.h" 39 #include "mozilla/dom/quota/ClientImpl.h" 40 #include "mozilla/dom/quota/FileStreams.h" 41 #include "mozilla/dom/quota/PrincipalUtils.h" 42 #include "mozilla/dom/quota/QuotaCommon.h" 43 #include "mozilla/dom/quota/QuotaManager.h" 44 #include "mozilla/dom/quota/ResultExtensions.h" 45 #include "mozilla/dom/quota/ThreadUtils.h" 46 #include "mozilla/dom/quota/UsageInfo.h" 47 #include "mozilla/ipc/BackgroundParent.h" 48 #include "mozilla/ipc/BackgroundUtils.h" 49 #include "mozilla/ipc/PBackgroundParent.h" 50 #include "mozilla/ipc/PBackgroundSharedTypes.h" 51 #include "mozilla/ipc/ProtocolUtils.h" 52 #include "nsCOMPtr.h" 53 #include "nsDebug.h" 54 #include "nsError.h" 55 #include "nsIDirectoryEnumerator.h" 56 #include "nsIEventTarget.h" 57 #include "nsIFile.h" 58 #include "nsIFileStreams.h" 59 #include "nsIInputStream.h" 60 #include "nsIOutputStream.h" 61 #include "nsIRunnable.h" 62 #include "nsISeekableStream.h" 63 #include "nsISupports.h" 64 #include "nsIThread.h" 65 #include "nsLiteralString.h" 66 #include "nsString.h" 67 #include "nsStringFwd.h" 68 #include "nsStringStream.h" 69 #include "nsTArray.h" 70 #include "nsTLiteralString.h" 71 #include "nsTStringRepr.h" 72 #include "nsThreadUtils.h" 73 #include "nscore.h" 74 #include "prio.h" 75 76 namespace mozilla::dom { 77 78 using namespace mozilla::dom::quota; 79 using namespace mozilla::ipc; 80 81 namespace { 82 83 /******************************************************************************* 84 * Constants 85 ******************************************************************************/ 86 87 const uint32_t kCopyBufferSize = 32768; 88 89 constexpr auto kSDBSuffix = u".sdb"_ns; 90 91 /******************************************************************************* 92 * Actor class declarations 93 ******************************************************************************/ 94 95 class StreamHelper final : public Runnable { 96 nsCOMPtr<nsIEventTarget> mOwningEventTarget; 97 nsCOMPtr<nsIFileRandomAccessStream> mFileRandomAccessStream; 98 nsCOMPtr<nsIRunnable> mCallback; 99 100 public: 101 StreamHelper(nsIFileRandomAccessStream* aFileRandomAccessStream, 102 nsIRunnable* aCallback); 103 104 void AsyncClose(); 105 106 private: 107 ~StreamHelper() override; 108 109 void RunOnBackgroundThread(); 110 111 void RunOnIOThread(); 112 113 NS_DECL_NSIRUNNABLE 114 }; 115 116 class Connection final : public PBackgroundSDBConnectionParent { 117 ClientDirectoryLockHandle mDirectoryLockHandle; 118 nsCOMPtr<nsIFileRandomAccessStream> mFileRandomAccessStream; 119 const PrincipalInfo mPrincipalInfo; 120 nsCString mOrigin; 121 nsString mName; 122 123 PersistenceType mPersistenceType; 124 bool mRunningRequest; 125 bool mOpen; 126 bool mAllowedToClose; 127 bool mActorDestroyed; 128 129 public: 130 Connection(PersistenceType aPersistenceType, 131 const PrincipalInfo& aPrincipalInfo); 132 133 NS_INLINE_DECL_THREADSAFE_REFCOUNTING(mozilla::dom::Connection, override) 134 135 Maybe<ClientDirectoryLock&> MaybeDirectoryLockRef() const { 136 AssertIsOnBackgroundThread(); 137 138 return ToMaybeRef(mDirectoryLockHandle.get()); 139 } 140 141 nsIFileRandomAccessStream* GetFileRandomAccessStream() const { 142 AssertIsOnIOThread(); 143 144 return mFileRandomAccessStream; 145 } 146 147 PersistenceType GetPersistenceType() const { return mPersistenceType; } 148 149 const PrincipalInfo& GetPrincipalInfo() const { 150 AssertIsOnBackgroundThread(); 151 152 return mPrincipalInfo; 153 } 154 155 const nsCString& Origin() const { 156 AssertIsOnBackgroundThread(); 157 MOZ_ASSERT(!mOrigin.IsEmpty()); 158 159 return mOrigin; 160 } 161 162 const nsString& Name() const { 163 AssertIsOnBackgroundThread(); 164 MOZ_ASSERT(!mName.IsEmpty()); 165 166 return mName; 167 } 168 169 void OnNewRequest(); 170 171 void OnRequestFinished(); 172 173 void OnOpen( 174 const nsACString& aOrigin, const nsAString& aName, 175 ClientDirectoryLockHandle aDirectoryLockHandle, 176 already_AddRefed<nsIFileRandomAccessStream> aFileRandomAccessStream); 177 178 void OnClose(); 179 180 void AllowToClose(); 181 182 private: 183 ~Connection(); 184 185 void MaybeCloseStream(); 186 187 bool VerifyRequestParams(const SDBRequestParams& aParams) const; 188 189 // IPDL methods. 190 virtual void ActorDestroy(ActorDestroyReason aWhy) override; 191 192 mozilla::ipc::IPCResult RecvDeleteMe() override; 193 194 virtual PBackgroundSDBRequestParent* AllocPBackgroundSDBRequestParent( 195 const SDBRequestParams& aParams) override; 196 197 virtual mozilla::ipc::IPCResult RecvPBackgroundSDBRequestConstructor( 198 PBackgroundSDBRequestParent* aActor, 199 const SDBRequestParams& aParams) override; 200 201 virtual bool DeallocPBackgroundSDBRequestParent( 202 PBackgroundSDBRequestParent* aActor) override; 203 }; 204 205 class ConnectionOperationBase : public Runnable, 206 public PBackgroundSDBRequestParent { 207 nsCOMPtr<nsIEventTarget> mOwningEventTarget; 208 RefPtr<Connection> mConnection; 209 nsresult mResultCode; 210 Atomic<bool> mOperationMayProceed; 211 bool mActorDestroyed; 212 213 public: 214 nsIEventTarget* OwningEventTarget() const { 215 MOZ_ASSERT(mOwningEventTarget); 216 217 return mOwningEventTarget; 218 } 219 220 bool IsOnOwningThread() const { 221 MOZ_ASSERT(mOwningEventTarget); 222 223 bool current; 224 return NS_SUCCEEDED(mOwningEventTarget->IsOnCurrentThread(¤t)) && 225 current; 226 } 227 228 void AssertIsOnOwningThread() const { 229 MOZ_ASSERT(IsOnBackgroundThread()); 230 MOZ_ASSERT(IsOnOwningThread()); 231 } 232 233 Connection* GetConnection() const { 234 MOZ_ASSERT(mConnection); 235 236 return mConnection; 237 } 238 239 nsresult ResultCode() const { return mResultCode; } 240 241 void MaybeSetFailureCode(nsresult aErrorCode) { 242 MOZ_ASSERT(NS_FAILED(aErrorCode)); 243 244 if (NS_SUCCEEDED(mResultCode)) { 245 mResultCode = aErrorCode; 246 } 247 } 248 249 // May be called on any thread, but you should call IsActorDestroyed() if 250 // you know you're on the background thread because it is slightly faster. 251 bool OperationMayProceed() const { return mOperationMayProceed; } 252 253 bool IsActorDestroyed() const { 254 AssertIsOnOwningThread(); 255 256 return mActorDestroyed; 257 } 258 259 // May be overridden by subclasses if they need to perform work on the 260 // background thread before being dispatched but must always call the base 261 // class implementation. Returning false will kill the child actors and 262 // prevent dispatch. 263 virtual bool Init(); 264 265 virtual nsresult Dispatch(); 266 267 // This callback will be called on the background thread before releasing the 268 // final reference to this request object. Subclasses may perform any 269 // additional cleanup here but must always call the base class implementation. 270 virtual void Cleanup(); 271 272 protected: 273 ConnectionOperationBase(Connection* aConnection) 274 : Runnable("dom::ConnectionOperationBase"), 275 mOwningEventTarget(GetCurrentSerialEventTarget()), 276 mConnection(aConnection), 277 mResultCode(NS_OK), 278 mOperationMayProceed(true), 279 mActorDestroyed(false) { 280 AssertIsOnOwningThread(); 281 } 282 283 ~ConnectionOperationBase() override; 284 285 void SendResults(); 286 287 void DatabaseWork(); 288 289 // Methods that subclasses must implement. 290 virtual nsresult DoDatabaseWork( 291 nsIFileRandomAccessStream* aFileRandomAccessStream) = 0; 292 293 // Subclasses use this override to set the IPDL response value. 294 virtual void GetResponse(SDBRequestResponse& aResponse) = 0; 295 296 // A method that subclasses may implement. 297 virtual void OnSuccess(); 298 299 private: 300 NS_IMETHOD 301 Run() override; 302 303 // IPDL methods. 304 void ActorDestroy(ActorDestroyReason aWhy) override; 305 }; 306 307 class OpenOp final : public ConnectionOperationBase { 308 enum class State { 309 // Just created on the PBackground thread, dispatched to the main thread. 310 // Next step is FinishOpen. 311 Initial, 312 313 // Ensuring quota manager is created and opening directory on the 314 // PBackground thread. Next step is either SendingResults if quota manager 315 // is not available or DirectoryOpenPending if quota manager is available. 316 FinishOpen, 317 318 // Waiting for directory open allowed on the PBackground thread. The next 319 // step is either SendingResults if directory lock failed to acquire, or 320 // DatabaseWorkOpen if directory lock is acquired. 321 DirectoryOpenPending, 322 323 // Waiting to do/doing work on the QuotaManager IO thread. Its next step is 324 // SendingResults. 325 DatabaseWorkOpen, 326 327 // Waiting to send/sending results on the PBackground thread. Next step is 328 // Completed. 329 SendingResults, 330 331 // All done. 332 Completed 333 }; 334 335 const SDBRequestOpenParams mParams; 336 ClientDirectoryLockHandle mDirectoryLockHandle; 337 nsCOMPtr<nsIFileRandomAccessStream> mFileRandomAccessStream; 338 // XXX Consider changing this to ClientMetadata. 339 quota::OriginMetadata mOriginMetadata; 340 State mState; 341 bool mFileRandomAccessStreamOpen; 342 343 public: 344 OpenOp(Connection* aConnection, const SDBRequestParams& aParams); 345 346 nsresult Dispatch() override; 347 348 private: 349 ~OpenOp() override; 350 351 nsresult Open(); 352 353 nsresult FinishOpen(); 354 355 nsresult SendToIOThread(); 356 357 nsresult DatabaseWork(); 358 359 void StreamClosedCallback(); 360 361 // ConnectionOperationBase overrides 362 nsresult DoDatabaseWork( 363 nsIFileRandomAccessStream* aFileRandomAccessStream) override; 364 365 void GetResponse(SDBRequestResponse& aResponse) override; 366 367 void OnSuccess() override; 368 369 void Cleanup() override; 370 371 NS_IMETHOD 372 Run() override; 373 374 void DirectoryLockAcquired(ClientDirectoryLockHandle aLockHandle); 375 376 void DirectoryLockFailed(); 377 }; 378 379 class SeekOp final : public ConnectionOperationBase { 380 const SDBRequestSeekParams mParams; 381 382 public: 383 SeekOp(Connection* aConnection, const SDBRequestParams& aParams); 384 385 private: 386 ~SeekOp() override = default; 387 388 nsresult DoDatabaseWork( 389 nsIFileRandomAccessStream* aFileRandomAccessStream) override; 390 391 void GetResponse(SDBRequestResponse& aResponse) override; 392 }; 393 394 class ReadOp final : public ConnectionOperationBase { 395 const SDBRequestReadParams mParams; 396 397 RefPtr<FixedBufferOutputStream> mOutputStream; 398 399 public: 400 ReadOp(Connection* aConnection, const SDBRequestParams& aParams); 401 402 bool Init() override; 403 404 private: 405 ~ReadOp() override = default; 406 407 nsresult DoDatabaseWork( 408 nsIFileRandomAccessStream* aFileRandomAccessStream) override; 409 410 void GetResponse(SDBRequestResponse& aResponse) override; 411 }; 412 413 class WriteOp final : public ConnectionOperationBase { 414 const SDBRequestWriteParams mParams; 415 416 nsCOMPtr<nsIInputStream> mInputStream; 417 418 uint64_t mSize; 419 420 public: 421 WriteOp(Connection* aConnection, const SDBRequestParams& aParams); 422 423 bool Init() override; 424 425 private: 426 ~WriteOp() override = default; 427 428 nsresult DoDatabaseWork( 429 nsIFileRandomAccessStream* aFileRandomAccessStream) override; 430 431 void GetResponse(SDBRequestResponse& aResponse) override; 432 }; 433 434 class CloseOp final : public ConnectionOperationBase { 435 public: 436 explicit CloseOp(Connection* aConnection); 437 438 private: 439 ~CloseOp() override = default; 440 441 nsresult DoDatabaseWork( 442 nsIFileRandomAccessStream* aFileRandomAccessStream) override; 443 444 void GetResponse(SDBRequestResponse& aResponse) override; 445 446 void OnSuccess() override; 447 }; 448 449 /******************************************************************************* 450 * Other class declarations 451 ******************************************************************************/ 452 453 class QuotaClient final : public mozilla::dom::quota::Client { 454 static QuotaClient* sInstance; 455 456 public: 457 QuotaClient(); 458 459 NS_INLINE_DECL_THREADSAFE_REFCOUNTING(QuotaClient, override) 460 461 Type GetType() override; 462 463 Result<UsageInfo, nsresult> InitOrigin(PersistenceType aPersistenceType, 464 const OriginMetadata& aOriginMetadata, 465 const AtomicBool& aCanceled) override; 466 467 nsresult InitOriginWithoutTracking(PersistenceType aPersistenceType, 468 const OriginMetadata& aOriginMetadata, 469 const AtomicBool& aCanceled) override; 470 471 Result<UsageInfo, nsresult> GetUsageForOrigin( 472 PersistenceType aPersistenceType, const OriginMetadata& aOriginMetadata, 473 const AtomicBool& aCanceled) override; 474 475 void OnOriginClearCompleted(const OriginMetadata& aOriginMetadata) override; 476 477 void OnRepositoryClearCompleted(PersistenceType aPersistenceType) override; 478 479 void ReleaseIOThreadObjects() override; 480 481 void AbortOperationsForLocks( 482 const DirectoryLockIdTable& aDirectoryLockIds) override; 483 484 void AbortOperationsForProcess(ContentParentId aContentParentId) override; 485 486 void AbortAllOperations() override; 487 488 void StartIdleMaintenance() override; 489 490 void StopIdleMaintenance() override; 491 492 private: 493 ~QuotaClient() override; 494 495 void InitiateShutdown() override; 496 bool IsShutdownCompleted() const override; 497 nsCString GetShutdownStatus() const override; 498 void ForceKillActors() override; 499 void FinalizeShutdown() override; 500 }; 501 502 /******************************************************************************* 503 * Globals 504 ******************************************************************************/ 505 506 using ConnectionArray = nsTArray<NotNull<RefPtr<Connection>>>; 507 508 StaticAutoPtr<ConnectionArray> gOpenConnections; 509 510 template <typename Condition> 511 void AllowToCloseConnectionsMatching(const Condition& aCondition) { 512 AssertIsOnBackgroundThread(); 513 514 if (gOpenConnections) { 515 for (const auto& connection : *gOpenConnections) { 516 if (aCondition(*connection)) { 517 connection->AllowToClose(); 518 } 519 } 520 } 521 } 522 523 } // namespace 524 525 /******************************************************************************* 526 * Exported functions 527 ******************************************************************************/ 528 529 already_AddRefed<PBackgroundSDBConnectionParent> 530 AllocPBackgroundSDBConnectionParent(const PersistenceType& aPersistenceType, 531 const PrincipalInfo& aPrincipalInfo) { 532 AssertIsOnBackgroundThread(); 533 534 if (NS_WARN_IF(QuotaClient::IsShuttingDownOnBackgroundThread())) { 535 return nullptr; 536 } 537 538 if (NS_WARN_IF(!IsValidPersistenceType(aPersistenceType))) { 539 MOZ_CRASH_UNLESS_FUZZING(); 540 return nullptr; 541 } 542 543 if (NS_WARN_IF(aPrincipalInfo.type() == PrincipalInfo::TNullPrincipalInfo)) { 544 MOZ_CRASH_UNLESS_FUZZING(); 545 return nullptr; 546 } 547 548 if (NS_WARN_IF(!quota::IsPrincipalInfoValid(aPrincipalInfo))) { 549 MOZ_CRASH_UNLESS_FUZZING(); 550 return nullptr; 551 } 552 553 RefPtr<Connection> actor = new Connection(aPersistenceType, aPrincipalInfo); 554 555 return actor.forget(); 556 } 557 558 bool RecvPBackgroundSDBConnectionConstructor( 559 PBackgroundSDBConnectionParent* aActor, 560 const PersistenceType& aPersistenceType, 561 const PrincipalInfo& aPrincipalInfo) { 562 AssertIsOnBackgroundThread(); 563 MOZ_ASSERT(aActor); 564 MOZ_ASSERT(!QuotaClient::IsShuttingDownOnBackgroundThread()); 565 566 return true; 567 } 568 569 namespace simpledb { 570 571 already_AddRefed<mozilla::dom::quota::Client> CreateQuotaClient() { 572 AssertIsOnBackgroundThread(); 573 574 RefPtr<QuotaClient> client = new QuotaClient(); 575 return client.forget(); 576 } 577 578 } // namespace simpledb 579 580 /******************************************************************************* 581 * StreamHelper 582 ******************************************************************************/ 583 584 StreamHelper::StreamHelper(nsIFileRandomAccessStream* aFileRandomAccessStream, 585 nsIRunnable* aCallback) 586 : Runnable("dom::StreamHelper"), 587 mOwningEventTarget(GetCurrentSerialEventTarget()), 588 mFileRandomAccessStream(aFileRandomAccessStream), 589 mCallback(aCallback) { 590 AssertIsOnBackgroundThread(); 591 MOZ_ASSERT(aFileRandomAccessStream); 592 MOZ_ASSERT(aCallback); 593 } 594 595 StreamHelper::~StreamHelper() { 596 MOZ_ASSERT(!mFileRandomAccessStream); 597 MOZ_ASSERT(!mCallback); 598 } 599 600 void StreamHelper::AsyncClose() { 601 AssertIsOnBackgroundThread(); 602 603 QuotaManager* quotaManager = QuotaManager::Get(); 604 MOZ_ASSERT(quotaManager); 605 606 MOZ_ALWAYS_SUCCEEDS( 607 quotaManager->IOThread()->Dispatch(this, NS_DISPATCH_NORMAL)); 608 } 609 610 void StreamHelper::RunOnBackgroundThread() { 611 AssertIsOnBackgroundThread(); 612 613 nsCOMPtr<nsIFileRandomAccessStream> fileRandomAccessStream; 614 mFileRandomAccessStream.swap(fileRandomAccessStream); 615 616 nsCOMPtr<nsIRunnable> callback; 617 mCallback.swap(callback); 618 619 callback->Run(); 620 } 621 622 void StreamHelper::RunOnIOThread() { 623 AssertIsOnIOThread(); 624 MOZ_ASSERT(mFileRandomAccessStream); 625 626 nsCOMPtr<nsIInputStream> inputStream = 627 do_QueryInterface(mFileRandomAccessStream); 628 MOZ_ASSERT(inputStream); 629 630 nsresult rv = inputStream->Close(); 631 (void)NS_WARN_IF(NS_FAILED(rv)); 632 633 MOZ_ALWAYS_SUCCEEDS(mOwningEventTarget->Dispatch(this, NS_DISPATCH_NORMAL)); 634 } 635 636 NS_IMETHODIMP 637 StreamHelper::Run() { 638 MOZ_ASSERT(mCallback); 639 640 if (IsOnBackgroundThread()) { 641 RunOnBackgroundThread(); 642 } else { 643 RunOnIOThread(); 644 } 645 646 return NS_OK; 647 } 648 649 /******************************************************************************* 650 * Connection 651 ******************************************************************************/ 652 653 Connection::Connection(PersistenceType aPersistenceType, 654 const PrincipalInfo& aPrincipalInfo) 655 : mPrincipalInfo(aPrincipalInfo), 656 mPersistenceType(aPersistenceType), 657 mRunningRequest(false), 658 mOpen(false), 659 mAllowedToClose(false), 660 mActorDestroyed(false) { 661 AssertIsOnBackgroundThread(); 662 MOZ_ASSERT(!QuotaClient::IsShuttingDownOnBackgroundThread()); 663 } 664 665 Connection::~Connection() { 666 MOZ_ASSERT(!mRunningRequest); 667 MOZ_ASSERT(!mOpen); 668 MOZ_ASSERT(mActorDestroyed); 669 } 670 671 void Connection::OnNewRequest() { 672 AssertIsOnBackgroundThread(); 673 MOZ_ASSERT(!mRunningRequest); 674 675 mRunningRequest = true; 676 } 677 678 void Connection::OnRequestFinished() { 679 AssertIsOnBackgroundThread(); 680 MOZ_ASSERT(mRunningRequest); 681 682 mRunningRequest = false; 683 684 MaybeCloseStream(); 685 } 686 687 void Connection::OnOpen( 688 const nsACString& aOrigin, const nsAString& aName, 689 ClientDirectoryLockHandle aDirectoryLockHandle, 690 already_AddRefed<nsIFileRandomAccessStream> aFileRandomAccessStream) { 691 AssertIsOnBackgroundThread(); 692 MOZ_ASSERT(!aOrigin.IsEmpty()); 693 MOZ_ASSERT(!aName.IsEmpty()); 694 MOZ_ASSERT(mOrigin.IsEmpty()); 695 MOZ_ASSERT(mName.IsEmpty()); 696 MOZ_ASSERT(!mDirectoryLockHandle); 697 MOZ_ASSERT(!mFileRandomAccessStream); 698 MOZ_ASSERT(!mOpen); 699 700 mOrigin = aOrigin; 701 mName = aName; 702 mDirectoryLockHandle = std::move(aDirectoryLockHandle); 703 mFileRandomAccessStream = aFileRandomAccessStream; 704 mOpen = true; 705 706 if (!gOpenConnections) { 707 gOpenConnections = new ConnectionArray(); 708 } 709 710 gOpenConnections->AppendElement(WrapNotNullUnchecked(this)); 711 712 if (mDirectoryLockHandle->Invalidated()) { 713 AllowToClose(); 714 } 715 } 716 717 void Connection::OnClose() { 718 AssertIsOnBackgroundThread(); 719 MOZ_ASSERT(!mOrigin.IsEmpty()); 720 MOZ_ASSERT(mDirectoryLockHandle); 721 MOZ_ASSERT(mFileRandomAccessStream); 722 MOZ_ASSERT(mOpen); 723 724 mOrigin.Truncate(); 725 mName.Truncate(); 726 727 { 728 auto destroyingDirectoryLockHandle = std::move(mDirectoryLockHandle); 729 } 730 731 mFileRandomAccessStream = nullptr; 732 mOpen = false; 733 734 MOZ_ASSERT(gOpenConnections); 735 gOpenConnections->RemoveElement(this); 736 737 if (gOpenConnections->IsEmpty()) { 738 gOpenConnections = nullptr; 739 } 740 741 if (mAllowedToClose && !mActorDestroyed) { 742 (void)SendClosed(); 743 } 744 } 745 746 void Connection::AllowToClose() { 747 AssertIsOnBackgroundThread(); 748 749 if (mAllowedToClose) { 750 return; 751 } 752 753 mAllowedToClose = true; 754 755 if (!mActorDestroyed) { 756 (void)SendAllowToClose(); 757 } 758 759 MaybeCloseStream(); 760 } 761 762 void Connection::MaybeCloseStream() { 763 AssertIsOnBackgroundThread(); 764 765 if (!mRunningRequest && mOpen && mAllowedToClose) { 766 nsCOMPtr<nsIRunnable> callback = NewRunnableMethod( 767 "dom::Connection::OnClose", this, &Connection::OnClose); 768 769 RefPtr<StreamHelper> helper = 770 new StreamHelper(mFileRandomAccessStream, callback); 771 helper->AsyncClose(); 772 } 773 } 774 775 bool Connection::VerifyRequestParams(const SDBRequestParams& aParams) const { 776 AssertIsOnBackgroundThread(); 777 MOZ_ASSERT(aParams.type() != SDBRequestParams::T__None); 778 779 switch (aParams.type()) { 780 case SDBRequestParams::TSDBRequestOpenParams: { 781 if (NS_WARN_IF(mOpen)) { 782 MOZ_CRASH_UNLESS_FUZZING(); 783 return false; 784 } 785 786 break; 787 } 788 789 case SDBRequestParams::TSDBRequestSeekParams: 790 case SDBRequestParams::TSDBRequestReadParams: 791 case SDBRequestParams::TSDBRequestWriteParams: 792 case SDBRequestParams::TSDBRequestCloseParams: { 793 if (NS_WARN_IF(!mOpen)) { 794 MOZ_CRASH_UNLESS_FUZZING(); 795 return false; 796 } 797 798 break; 799 } 800 801 default: 802 MOZ_CRASH("Should never get here!"); 803 } 804 805 return true; 806 } 807 808 void Connection::ActorDestroy(ActorDestroyReason aWhy) { 809 AssertIsOnBackgroundThread(); 810 MOZ_ASSERT(!mActorDestroyed); 811 812 mActorDestroyed = true; 813 814 AllowToClose(); 815 } 816 817 mozilla::ipc::IPCResult Connection::RecvDeleteMe() { 818 AssertIsOnBackgroundThread(); 819 MOZ_ASSERT(!mActorDestroyed); 820 821 IProtocol* mgr = Manager(); 822 if (!PBackgroundSDBConnectionParent::Send__delete__(this)) { 823 return IPC_FAIL_NO_REASON(mgr); 824 } 825 826 return IPC_OK(); 827 } 828 829 PBackgroundSDBRequestParent* Connection::AllocPBackgroundSDBRequestParent( 830 const SDBRequestParams& aParams) { 831 AssertIsOnBackgroundThread(); 832 MOZ_ASSERT(aParams.type() != SDBRequestParams::T__None); 833 834 if (aParams.type() == SDBRequestParams::TSDBRequestOpenParams && 835 NS_WARN_IF(QuotaClient::IsShuttingDownOnBackgroundThread())) { 836 return nullptr; 837 } 838 839 if (mAllowedToClose) { 840 return nullptr; 841 } 842 843 #ifdef DEBUG 844 // Always verify parameters in DEBUG builds! 845 bool trustParams = false; 846 #else 847 PBackgroundParent* backgroundActor = Manager(); 848 MOZ_ASSERT(backgroundActor); 849 850 bool trustParams = !BackgroundParent::IsOtherProcessActor(backgroundActor); 851 #endif 852 853 if (NS_WARN_IF(!trustParams && !VerifyRequestParams(aParams))) { 854 MOZ_CRASH_UNLESS_FUZZING(); 855 return nullptr; 856 } 857 858 if (NS_WARN_IF(mRunningRequest)) { 859 MOZ_CRASH_UNLESS_FUZZING(); 860 return nullptr; 861 } 862 863 QM_TRY(QuotaManager::EnsureCreated(), nullptr); 864 865 RefPtr<ConnectionOperationBase> actor; 866 867 switch (aParams.type()) { 868 case SDBRequestParams::TSDBRequestOpenParams: 869 actor = new OpenOp(this, aParams); 870 break; 871 872 case SDBRequestParams::TSDBRequestSeekParams: 873 actor = new SeekOp(this, aParams); 874 break; 875 876 case SDBRequestParams::TSDBRequestReadParams: 877 actor = new ReadOp(this, aParams); 878 break; 879 880 case SDBRequestParams::TSDBRequestWriteParams: 881 actor = new WriteOp(this, aParams); 882 break; 883 884 case SDBRequestParams::TSDBRequestCloseParams: 885 actor = new CloseOp(this); 886 break; 887 888 default: 889 MOZ_CRASH("Should never get here!"); 890 } 891 892 // Transfer ownership to IPDL. 893 return actor.forget().take(); 894 } 895 896 mozilla::ipc::IPCResult Connection::RecvPBackgroundSDBRequestConstructor( 897 PBackgroundSDBRequestParent* aActor, const SDBRequestParams& aParams) { 898 AssertIsOnBackgroundThread(); 899 MOZ_ASSERT(aActor); 900 MOZ_ASSERT(aParams.type() != SDBRequestParams::T__None); 901 MOZ_ASSERT_IF(aParams.type() == SDBRequestParams::TSDBRequestOpenParams, 902 !QuotaClient::IsShuttingDownOnBackgroundThread()); 903 MOZ_ASSERT(!mAllowedToClose); 904 MOZ_ASSERT(!mRunningRequest); 905 906 auto* op = static_cast<ConnectionOperationBase*>(aActor); 907 908 if (NS_WARN_IF(!op->Init())) { 909 op->Cleanup(); 910 return IPC_FAIL_NO_REASON(this); 911 } 912 913 if (NS_WARN_IF(NS_FAILED(op->Dispatch()))) { 914 op->Cleanup(); 915 return IPC_FAIL_NO_REASON(this); 916 } 917 918 return IPC_OK(); 919 } 920 921 bool Connection::DeallocPBackgroundSDBRequestParent( 922 PBackgroundSDBRequestParent* aActor) { 923 AssertIsOnBackgroundThread(); 924 MOZ_ASSERT(aActor); 925 926 // Transfer ownership back from IPDL. 927 RefPtr<ConnectionOperationBase> actor = 928 dont_AddRef(static_cast<ConnectionOperationBase*>(aActor)); 929 return true; 930 } 931 932 /******************************************************************************* 933 * ConnectionOperationBase 934 ******************************************************************************/ 935 936 ConnectionOperationBase::~ConnectionOperationBase() { 937 MOZ_ASSERT( 938 !mConnection, 939 "ConnectionOperationBase::Cleanup() was not called by a subclass!"); 940 MOZ_ASSERT(mActorDestroyed); 941 } 942 943 bool ConnectionOperationBase::Init() { 944 AssertIsOnBackgroundThread(); 945 MOZ_ASSERT(mConnection); 946 947 mConnection->OnNewRequest(); 948 949 return true; 950 } 951 952 nsresult ConnectionOperationBase::Dispatch() { 953 if (NS_WARN_IF(QuotaClient::IsShuttingDownOnBackgroundThread()) || 954 IsActorDestroyed()) { 955 return NS_ERROR_ABORT; 956 } 957 958 QuotaManager* quotaManager = QuotaManager::Get(); 959 MOZ_ASSERT(quotaManager); 960 961 nsresult rv = quotaManager->IOThread()->Dispatch(this, NS_DISPATCH_NORMAL); 962 if (NS_WARN_IF(NS_FAILED(rv))) { 963 return rv; 964 } 965 966 return NS_OK; 967 } 968 969 void ConnectionOperationBase::Cleanup() { 970 AssertIsOnOwningThread(); 971 MOZ_ASSERT(mConnection); 972 973 mConnection->OnRequestFinished(); 974 975 mConnection = nullptr; 976 } 977 978 void ConnectionOperationBase::SendResults() { 979 AssertIsOnOwningThread(); 980 981 if (IsActorDestroyed()) { 982 MaybeSetFailureCode(NS_ERROR_FAILURE); 983 } else { 984 SDBRequestResponse response; 985 986 if (NS_SUCCEEDED(mResultCode)) { 987 GetResponse(response); 988 989 MOZ_ASSERT(response.type() != SDBRequestResponse::T__None); 990 MOZ_ASSERT(response.type() != SDBRequestResponse::Tnsresult); 991 992 OnSuccess(); 993 } else { 994 response = mResultCode; 995 } 996 997 (void)PBackgroundSDBRequestParent::Send__delete__(this, response); 998 } 999 1000 Cleanup(); 1001 } 1002 1003 void ConnectionOperationBase::DatabaseWork() { 1004 AssertIsOnIOThread(); 1005 MOZ_ASSERT(NS_SUCCEEDED(mResultCode)); 1006 1007 if (!OperationMayProceed()) { 1008 // The operation was canceled in some way, likely because the child process 1009 // has crashed. 1010 mResultCode = NS_ERROR_ABORT; 1011 } else { 1012 nsIFileRandomAccessStream* fileRandomAccessStream = 1013 mConnection->GetFileRandomAccessStream(); 1014 MOZ_ASSERT(fileRandomAccessStream); 1015 1016 nsresult rv = DoDatabaseWork(fileRandomAccessStream); 1017 if (NS_FAILED(rv)) { 1018 mResultCode = rv; 1019 } 1020 } 1021 1022 MOZ_ALWAYS_SUCCEEDS(mOwningEventTarget->Dispatch(this, NS_DISPATCH_NORMAL)); 1023 } 1024 1025 void ConnectionOperationBase::OnSuccess() { AssertIsOnOwningThread(); } 1026 1027 NS_IMETHODIMP 1028 ConnectionOperationBase::Run() { 1029 if (IsOnBackgroundThread()) { 1030 SendResults(); 1031 } else { 1032 DatabaseWork(); 1033 } 1034 1035 return NS_OK; 1036 } 1037 1038 void ConnectionOperationBase::ActorDestroy(ActorDestroyReason aWhy) { 1039 AssertIsOnBackgroundThread(); 1040 1041 mOperationMayProceed = false; 1042 mActorDestroyed = true; 1043 } 1044 1045 OpenOp::OpenOp(Connection* aConnection, const SDBRequestParams& aParams) 1046 : ConnectionOperationBase(aConnection), 1047 mParams(aParams.get_SDBRequestOpenParams()), 1048 mState(State::Initial), 1049 mFileRandomAccessStreamOpen(false) { 1050 MOZ_ASSERT(aParams.type() == SDBRequestParams::TSDBRequestOpenParams); 1051 } 1052 1053 OpenOp::~OpenOp() { 1054 MOZ_ASSERT(mDirectoryLockHandle.IsInert()); 1055 MOZ_ASSERT(!mFileRandomAccessStream); 1056 MOZ_ASSERT(!mFileRandomAccessStreamOpen); 1057 MOZ_ASSERT_IF(OperationMayProceed(), 1058 mState == State::Initial || mState == State::Completed); 1059 } 1060 1061 nsresult OpenOp::Dispatch() { 1062 MOZ_ALWAYS_SUCCEEDS(NS_DispatchToMainThread(this)); 1063 1064 return NS_OK; 1065 } 1066 1067 nsresult OpenOp::Open() { 1068 MOZ_ASSERT(NS_IsMainThread()); 1069 MOZ_ASSERT(mState == State::Initial); 1070 1071 if (NS_WARN_IF(QuotaClient::IsShuttingDownOnNonBackgroundThread()) || 1072 !OperationMayProceed()) { 1073 return NS_ERROR_ABORT; 1074 } 1075 1076 if (NS_WARN_IF(!Preferences::GetBool(kPrefSimpleDBEnabled, false))) { 1077 return NS_ERROR_UNEXPECTED; 1078 } 1079 1080 mState = State::FinishOpen; 1081 MOZ_ALWAYS_SUCCEEDS(OwningEventTarget()->Dispatch(this, NS_DISPATCH_NORMAL)); 1082 1083 return NS_OK; 1084 } 1085 1086 nsresult OpenOp::FinishOpen() { 1087 AssertIsOnOwningThread(); 1088 MOZ_ASSERT(mOriginMetadata.mOrigin.IsEmpty()); 1089 MOZ_ASSERT(!mDirectoryLockHandle); 1090 MOZ_ASSERT(mState == State::FinishOpen); 1091 1092 if (NS_WARN_IF(QuotaClient::IsShuttingDownOnBackgroundThread()) || 1093 IsActorDestroyed()) { 1094 return NS_ERROR_ABORT; 1095 } 1096 1097 QuotaManager* quotaManager = QuotaManager::Get(); 1098 MOZ_ASSERT(quotaManager); 1099 1100 const PrincipalInfo& principalInfo = GetConnection()->GetPrincipalInfo(); 1101 1102 PersistenceType persistenceType = GetConnection()->GetPersistenceType(); 1103 1104 if (principalInfo.type() == PrincipalInfo::TSystemPrincipalInfo) { 1105 mOriginMetadata = {quota::GetInfoForChrome(), persistenceType}; 1106 } else { 1107 MOZ_ASSERT(principalInfo.type() == PrincipalInfo::TContentPrincipalInfo); 1108 1109 QM_TRY_UNWRAP( 1110 auto principalMetadata, 1111 quota::GetInfoFromValidatedPrincipalInfo(*quotaManager, principalInfo)); 1112 1113 mOriginMetadata = {std::move(principalMetadata), persistenceType}; 1114 } 1115 1116 if (gOpenConnections) { 1117 for (const auto& connection : *gOpenConnections) { 1118 if (connection->Origin() == mOriginMetadata.mOrigin && 1119 connection->Name() == mParams.name()) { 1120 return NS_ERROR_STORAGE_BUSY; 1121 } 1122 } 1123 } 1124 1125 // Open the directory 1126 1127 mState = State::DirectoryOpenPending; 1128 1129 quotaManager 1130 ->OpenClientDirectory({mOriginMetadata, mozilla::dom::quota::Client::SDB}) 1131 ->Then( 1132 GetCurrentSerialEventTarget(), __func__, 1133 [self = RefPtr(this)](QuotaManager::ClientDirectoryLockHandlePromise:: 1134 ResolveOrRejectValue&& aValue) { 1135 if (aValue.IsResolve()) { 1136 self->DirectoryLockAcquired(std::move(aValue.ResolveValue())); 1137 } else { 1138 self->DirectoryLockFailed(); 1139 } 1140 }); 1141 1142 return NS_OK; 1143 } 1144 1145 nsresult OpenOp::SendToIOThread() { 1146 AssertIsOnOwningThread(); 1147 MOZ_ASSERT(mState == State::DirectoryOpenPending); 1148 1149 if (NS_WARN_IF(QuotaClient::IsShuttingDownOnBackgroundThread()) || 1150 IsActorDestroyed()) { 1151 return NS_ERROR_ABORT; 1152 } 1153 1154 mFileRandomAccessStream = new FileRandomAccessStream( 1155 GetConnection()->GetPersistenceType(), mOriginMetadata, 1156 mozilla::dom::quota::Client::SDB); 1157 1158 QuotaManager* quotaManager = QuotaManager::Get(); 1159 MOZ_ASSERT(quotaManager); 1160 1161 // Must set this before dispatching otherwise we will race with the IO thread. 1162 mState = State::DatabaseWorkOpen; 1163 1164 nsresult rv = quotaManager->IOThread()->Dispatch(this, NS_DISPATCH_NORMAL); 1165 if (NS_WARN_IF(NS_FAILED(rv))) { 1166 return rv; 1167 } 1168 1169 simpledb::NotifyDatabaseWorkStarted(); 1170 1171 return NS_OK; 1172 } 1173 1174 nsresult OpenOp::DatabaseWork() { 1175 AssertIsOnIOThread(); 1176 MOZ_ASSERT(mState == State::DatabaseWorkOpen); 1177 MOZ_ASSERT(mFileRandomAccessStream); 1178 MOZ_ASSERT(!mFileRandomAccessStreamOpen); 1179 1180 if (NS_WARN_IF(QuotaClient::IsShuttingDownOnNonBackgroundThread()) || 1181 !OperationMayProceed()) { 1182 return NS_ERROR_ABORT; 1183 } 1184 1185 QuotaManager* quotaManager = QuotaManager::Get(); 1186 MOZ_ASSERT(quotaManager); 1187 1188 QM_TRY_INSPECT( 1189 const auto& dbDirectory, 1190 ([persistenceType = GetConnection()->GetPersistenceType(), "aManager, 1191 this]() -> mozilla::Result<nsCOMPtr<nsIFile>, nsresult> { 1192 if (persistenceType == PERSISTENCE_TYPE_PERSISTENT) { 1193 QM_TRY_RETURN(quotaManager->GetOriginDirectory(mOriginMetadata)); 1194 } 1195 1196 QM_TRY_RETURN( 1197 quotaManager->GetOrCreateTemporaryOriginDirectory(mOriginMetadata)); 1198 }())); 1199 1200 nsresult rv = 1201 dbDirectory->Append(NS_LITERAL_STRING_FROM_CSTRING(SDB_DIRECTORY_NAME)); 1202 if (NS_WARN_IF(NS_FAILED(rv))) { 1203 return rv; 1204 } 1205 1206 bool exists; 1207 rv = dbDirectory->Exists(&exists); 1208 if (NS_WARN_IF(NS_FAILED(rv))) { 1209 return rv; 1210 } 1211 1212 if (!exists) { 1213 rv = dbDirectory->Create(nsIFile::DIRECTORY_TYPE, 0755); 1214 if (NS_WARN_IF(NS_FAILED(rv))) { 1215 return rv; 1216 } 1217 } 1218 #ifdef DEBUG 1219 else { 1220 bool isDirectory; 1221 MOZ_ASSERT(NS_SUCCEEDED(dbDirectory->IsDirectory(&isDirectory))); 1222 MOZ_ASSERT(isDirectory); 1223 } 1224 #endif 1225 1226 nsCOMPtr<nsIFile> dbFile; 1227 rv = dbDirectory->Clone(getter_AddRefs(dbFile)); 1228 if (NS_WARN_IF(NS_FAILED(rv))) { 1229 return rv; 1230 } 1231 1232 rv = dbFile->Append(mParams.name() + kSDBSuffix); 1233 if (NS_WARN_IF(NS_FAILED(rv))) { 1234 return rv; 1235 } 1236 1237 nsString databaseFilePath; 1238 rv = dbFile->GetPath(databaseFilePath); 1239 if (NS_WARN_IF(NS_FAILED(rv))) { 1240 return rv; 1241 } 1242 1243 rv = mFileRandomAccessStream->Init(dbFile, PR_RDWR | PR_CREATE_FILE, 0644, 0); 1244 if (NS_WARN_IF(NS_FAILED(rv))) { 1245 return rv; 1246 } 1247 1248 mFileRandomAccessStreamOpen = true; 1249 1250 rv = DoDatabaseWork(mFileRandomAccessStream); 1251 if (NS_WARN_IF(NS_FAILED(rv))) { 1252 return rv; 1253 } 1254 1255 // Must set mState before dispatching otherwise we will race with the owning 1256 // thread. 1257 mState = State::SendingResults; 1258 1259 rv = OwningEventTarget()->Dispatch(this, NS_DISPATCH_NORMAL); 1260 if (NS_WARN_IF(NS_FAILED(rv))) { 1261 return rv; 1262 } 1263 1264 return NS_OK; 1265 } 1266 1267 void OpenOp::StreamClosedCallback() { 1268 AssertIsOnOwningThread(); 1269 MOZ_ASSERT(NS_FAILED(ResultCode())); 1270 MOZ_ASSERT(mDirectoryLockHandle); 1271 MOZ_ASSERT(mFileRandomAccessStream); 1272 MOZ_ASSERT(mFileRandomAccessStreamOpen); 1273 1274 { 1275 auto destroyingDirectoryLockHandle = std::move(mDirectoryLockHandle); 1276 } 1277 1278 mFileRandomAccessStream = nullptr; 1279 mFileRandomAccessStreamOpen = false; 1280 } 1281 1282 nsresult OpenOp::DoDatabaseWork( 1283 nsIFileRandomAccessStream* aFileRandomAccessStream) { 1284 AssertIsOnIOThread(); 1285 1286 SleepIfEnabled( 1287 StaticPrefs::dom_simpledb_databaseInitialization_pauseOnIOThreadMs()); 1288 1289 return NS_OK; 1290 } 1291 1292 void OpenOp::GetResponse(SDBRequestResponse& aResponse) { 1293 AssertIsOnOwningThread(); 1294 1295 aResponse = SDBRequestOpenResponse(); 1296 } 1297 1298 void OpenOp::OnSuccess() { 1299 AssertIsOnOwningThread(); 1300 MOZ_ASSERT(NS_SUCCEEDED(ResultCode())); 1301 MOZ_ASSERT(!mOriginMetadata.mOrigin.IsEmpty()); 1302 MOZ_ASSERT(mDirectoryLockHandle); 1303 MOZ_ASSERT(mFileRandomAccessStream); 1304 MOZ_ASSERT(mFileRandomAccessStreamOpen); 1305 1306 ClientDirectoryLockHandle directoryLockHandle; 1307 nsCOMPtr<nsIFileRandomAccessStream> fileRandomAccessStream; 1308 1309 directoryLockHandle = std::move(mDirectoryLockHandle); 1310 mFileRandomAccessStream.swap(fileRandomAccessStream); 1311 mFileRandomAccessStreamOpen = false; 1312 1313 GetConnection()->OnOpen(mOriginMetadata.mOrigin, mParams.name(), 1314 std::move(directoryLockHandle), 1315 fileRandomAccessStream.forget()); 1316 } 1317 1318 void OpenOp::Cleanup() { 1319 AssertIsOnOwningThread(); 1320 MOZ_ASSERT_IF(mFileRandomAccessStreamOpen, mFileRandomAccessStream); 1321 1322 if (mFileRandomAccessStream && mFileRandomAccessStreamOpen) { 1323 // If we have an initialized file stream then the operation must have failed 1324 // and there must be a directory lock too. 1325 MOZ_ASSERT(NS_FAILED(ResultCode())); 1326 MOZ_ASSERT(mDirectoryLockHandle); 1327 1328 // We must close the stream on the I/O thread before releasing it on this 1329 // thread. The directory lock can't be released either. 1330 nsCOMPtr<nsIRunnable> callback = 1331 NewRunnableMethod("dom::OpenOp::StreamClosedCallback", this, 1332 &OpenOp::StreamClosedCallback); 1333 1334 RefPtr<StreamHelper> helper = 1335 new StreamHelper(mFileRandomAccessStream, callback); 1336 helper->AsyncClose(); 1337 } else { 1338 { 1339 auto destroyingDirectoryLockHandle = std::move(mDirectoryLockHandle); 1340 } 1341 1342 mFileRandomAccessStream = nullptr; 1343 MOZ_ASSERT(!mFileRandomAccessStreamOpen); 1344 } 1345 1346 ConnectionOperationBase::Cleanup(); 1347 } 1348 1349 NS_IMETHODIMP 1350 OpenOp::Run() { 1351 nsresult rv; 1352 1353 switch (mState) { 1354 case State::Initial: 1355 rv = Open(); 1356 break; 1357 1358 case State::FinishOpen: 1359 rv = FinishOpen(); 1360 break; 1361 1362 case State::DatabaseWorkOpen: 1363 rv = DatabaseWork(); 1364 break; 1365 1366 case State::SendingResults: 1367 SendResults(); 1368 return NS_OK; 1369 1370 default: 1371 MOZ_CRASH("Bad state!"); 1372 } 1373 1374 if (NS_WARN_IF(NS_FAILED(rv)) && mState != State::SendingResults) { 1375 MaybeSetFailureCode(rv); 1376 1377 // Must set mState before dispatching otherwise we will race with the owning 1378 // thread. 1379 mState = State::SendingResults; 1380 1381 if (IsOnOwningThread()) { 1382 SendResults(); 1383 } else { 1384 MOZ_ALWAYS_SUCCEEDS( 1385 OwningEventTarget()->Dispatch(this, NS_DISPATCH_NORMAL)); 1386 } 1387 } 1388 1389 return NS_OK; 1390 } 1391 1392 void OpenOp::DirectoryLockAcquired(ClientDirectoryLockHandle aLockHandle) { 1393 AssertIsOnOwningThread(); 1394 MOZ_ASSERT(mState == State::DirectoryOpenPending); 1395 MOZ_ASSERT(!mDirectoryLockHandle); 1396 1397 mDirectoryLockHandle = std::move(aLockHandle); 1398 1399 auto cleanupAndReturn = [self = RefPtr(this)](const nsresult rv) { 1400 self->MaybeSetFailureCode(rv); 1401 1402 // The caller holds a strong reference to us, no need for a self reference 1403 // before calling Run(). 1404 1405 self->mState = State::SendingResults; 1406 MOZ_ALWAYS_SUCCEEDS(self->Run()); 1407 }; 1408 1409 if (mDirectoryLockHandle->Invalidated()) { 1410 return cleanupAndReturn(NS_ERROR_ABORT); 1411 } 1412 1413 QM_TRY(MOZ_TO_RESULT(SendToIOThread()), cleanupAndReturn); 1414 } 1415 1416 void OpenOp::DirectoryLockFailed() { 1417 AssertIsOnOwningThread(); 1418 MOZ_ASSERT(mState == State::DirectoryOpenPending); 1419 MOZ_ASSERT(!mDirectoryLockHandle); 1420 1421 MaybeSetFailureCode(NS_ERROR_FAILURE); 1422 1423 // The caller holds a strong reference to us, no need for a self reference 1424 // before calling Run(). 1425 1426 mState = State::SendingResults; 1427 MOZ_ALWAYS_SUCCEEDS(Run()); 1428 } 1429 1430 SeekOp::SeekOp(Connection* aConnection, const SDBRequestParams& aParams) 1431 : ConnectionOperationBase(aConnection), 1432 mParams(aParams.get_SDBRequestSeekParams()) { 1433 MOZ_ASSERT(aParams.type() == SDBRequestParams::TSDBRequestSeekParams); 1434 } 1435 1436 nsresult SeekOp::DoDatabaseWork( 1437 nsIFileRandomAccessStream* aFileRandomAccessStream) { 1438 AssertIsOnIOThread(); 1439 MOZ_ASSERT(aFileRandomAccessStream); 1440 1441 nsresult rv = aFileRandomAccessStream->Seek(nsISeekableStream::NS_SEEK_SET, 1442 mParams.offset()); 1443 1444 if (NS_WARN_IF(NS_FAILED(rv))) { 1445 return rv; 1446 } 1447 1448 return NS_OK; 1449 } 1450 1451 void SeekOp::GetResponse(SDBRequestResponse& aResponse) { 1452 aResponse = SDBRequestSeekResponse(); 1453 } 1454 1455 ReadOp::ReadOp(Connection* aConnection, const SDBRequestParams& aParams) 1456 : ConnectionOperationBase(aConnection), 1457 mParams(aParams.get_SDBRequestReadParams()) { 1458 MOZ_ASSERT(aParams.type() == SDBRequestParams::TSDBRequestReadParams); 1459 } 1460 1461 bool ReadOp::Init() { 1462 AssertIsOnOwningThread(); 1463 1464 if (NS_WARN_IF(!ConnectionOperationBase::Init())) { 1465 return false; 1466 } 1467 1468 if (NS_WARN_IF(mParams.size() > std::numeric_limits<std::size_t>::max())) { 1469 return false; 1470 } 1471 1472 mOutputStream = FixedBufferOutputStream::Create(mParams.size(), fallible); 1473 if (NS_WARN_IF(!mOutputStream)) { 1474 return false; 1475 } 1476 1477 return true; 1478 } 1479 1480 nsresult ReadOp::DoDatabaseWork( 1481 nsIFileRandomAccessStream* aFileRandomAccessStream) { 1482 AssertIsOnIOThread(); 1483 MOZ_ASSERT(aFileRandomAccessStream); 1484 1485 nsCOMPtr<nsIInputStream> inputStream = 1486 do_QueryInterface(aFileRandomAccessStream); 1487 MOZ_ASSERT(inputStream); 1488 1489 nsresult rv; 1490 1491 uint64_t offset = 0; 1492 1493 do { 1494 char copyBuffer[kCopyBufferSize]; 1495 1496 uint64_t max = mParams.size() - offset; 1497 if (max == 0) { 1498 break; 1499 } 1500 1501 uint32_t count = sizeof(copyBuffer); 1502 if (count > max) { 1503 count = max; 1504 } 1505 1506 uint32_t numRead; 1507 rv = inputStream->Read(copyBuffer, count, &numRead); 1508 if (NS_WARN_IF(NS_FAILED(rv))) { 1509 return rv; 1510 } 1511 1512 if (!numRead) { 1513 break; 1514 } 1515 1516 uint32_t numWrite; 1517 rv = mOutputStream->Write(copyBuffer, numRead, &numWrite); 1518 if (NS_WARN_IF(NS_FAILED(rv))) { 1519 return rv; 1520 } 1521 1522 if (NS_WARN_IF(numWrite != numRead)) { 1523 return NS_ERROR_FAILURE; 1524 } 1525 1526 offset += numWrite; 1527 } while (true); 1528 1529 MOZ_ASSERT(offset == mParams.size()); 1530 1531 MOZ_ALWAYS_SUCCEEDS(mOutputStream->Close()); 1532 1533 return NS_OK; 1534 } 1535 1536 void ReadOp::GetResponse(SDBRequestResponse& aResponse) { 1537 aResponse = SDBRequestReadResponse(nsCString(mOutputStream->WrittenData())); 1538 } 1539 1540 WriteOp::WriteOp(Connection* aConnection, const SDBRequestParams& aParams) 1541 : ConnectionOperationBase(aConnection), 1542 mParams(aParams.get_SDBRequestWriteParams()), 1543 mSize(0) { 1544 MOZ_ASSERT(aParams.type() == SDBRequestParams::TSDBRequestWriteParams); 1545 } 1546 1547 bool WriteOp::Init() { 1548 AssertIsOnOwningThread(); 1549 1550 if (NS_WARN_IF(!ConnectionOperationBase::Init())) { 1551 return false; 1552 } 1553 1554 const nsCString& string = mParams.data(); 1555 1556 nsCOMPtr<nsIInputStream> inputStream; 1557 nsresult rv = NS_NewCStringInputStream(getter_AddRefs(inputStream), string); 1558 if (NS_WARN_IF(NS_FAILED(rv))) { 1559 return false; 1560 } 1561 1562 mInputStream = std::move(inputStream); 1563 mSize = string.Length(); 1564 1565 return true; 1566 } 1567 1568 nsresult WriteOp::DoDatabaseWork( 1569 nsIFileRandomAccessStream* aFileRandomAccessStream) { 1570 AssertIsOnIOThread(); 1571 MOZ_ASSERT(aFileRandomAccessStream); 1572 1573 nsCOMPtr<nsIOutputStream> outputStream = 1574 do_QueryInterface(aFileRandomAccessStream); 1575 MOZ_ASSERT(outputStream); 1576 1577 nsresult rv; 1578 1579 do { 1580 char copyBuffer[kCopyBufferSize]; 1581 1582 uint32_t numRead; 1583 rv = mInputStream->Read(copyBuffer, sizeof(copyBuffer), &numRead); 1584 if (NS_WARN_IF(NS_FAILED(rv))) { 1585 break; 1586 } 1587 1588 if (!numRead) { 1589 break; 1590 } 1591 1592 uint32_t numWrite; 1593 rv = outputStream->Write(copyBuffer, numRead, &numWrite); 1594 if (NS_WARN_IF(NS_FAILED(rv))) { 1595 return rv; 1596 } 1597 1598 if (NS_WARN_IF(numWrite != numRead)) { 1599 return NS_ERROR_FAILURE; 1600 } 1601 } while (true); 1602 1603 MOZ_ALWAYS_SUCCEEDS(mInputStream->Close()); 1604 1605 return NS_OK; 1606 } 1607 1608 void WriteOp::GetResponse(SDBRequestResponse& aResponse) { 1609 aResponse = SDBRequestWriteResponse(); 1610 } 1611 1612 CloseOp::CloseOp(Connection* aConnection) 1613 : ConnectionOperationBase(aConnection) {} 1614 1615 nsresult CloseOp::DoDatabaseWork( 1616 nsIFileRandomAccessStream* aFileRandomAccessStream) { 1617 AssertIsOnIOThread(); 1618 MOZ_ASSERT(aFileRandomAccessStream); 1619 1620 nsCOMPtr<nsIInputStream> inputStream = 1621 do_QueryInterface(aFileRandomAccessStream); 1622 MOZ_ASSERT(inputStream); 1623 1624 nsresult rv = inputStream->Close(); 1625 if (NS_WARN_IF(NS_FAILED(rv))) { 1626 return rv; 1627 } 1628 1629 return NS_OK; 1630 } 1631 1632 void CloseOp::GetResponse(SDBRequestResponse& aResponse) { 1633 aResponse = SDBRequestCloseResponse(); 1634 } 1635 1636 void CloseOp::OnSuccess() { 1637 AssertIsOnOwningThread(); 1638 1639 GetConnection()->OnClose(); 1640 } 1641 1642 /******************************************************************************* 1643 * QuotaClient 1644 ******************************************************************************/ 1645 1646 QuotaClient* QuotaClient::sInstance = nullptr; 1647 1648 QuotaClient::QuotaClient() { 1649 AssertIsOnBackgroundThread(); 1650 MOZ_ASSERT(!sInstance, "We expect this to be a singleton!"); 1651 1652 sInstance = this; 1653 } 1654 1655 QuotaClient::~QuotaClient() { 1656 AssertIsOnBackgroundThread(); 1657 MOZ_ASSERT(sInstance == this, "We expect this to be a singleton!"); 1658 1659 sInstance = nullptr; 1660 } 1661 1662 mozilla::dom::quota::Client::Type QuotaClient::GetType() { 1663 return QuotaClient::SDB; 1664 } 1665 1666 Result<UsageInfo, nsresult> QuotaClient::InitOrigin( 1667 PersistenceType aPersistenceType, const OriginMetadata& aOriginMetadata, 1668 const AtomicBool& aCanceled) { 1669 AssertIsOnIOThread(); 1670 1671 return GetUsageForOrigin(aPersistenceType, aOriginMetadata, aCanceled); 1672 } 1673 1674 nsresult QuotaClient::InitOriginWithoutTracking( 1675 PersistenceType aPersistenceType, const OriginMetadata& aOriginMetadata, 1676 const AtomicBool& aCanceled) { 1677 AssertIsOnIOThread(); 1678 1679 return NS_OK; 1680 } 1681 1682 Result<UsageInfo, nsresult> QuotaClient::GetUsageForOrigin( 1683 PersistenceType aPersistenceType, const OriginMetadata& aOriginMetadata, 1684 const AtomicBool& aCanceled) { 1685 AssertIsOnIOThread(); 1686 MOZ_ASSERT(aOriginMetadata.mPersistenceType == aPersistenceType); 1687 1688 QuotaManager* quotaManager = QuotaManager::Get(); 1689 MOZ_ASSERT(quotaManager); 1690 1691 QM_TRY_UNWRAP(auto directory, 1692 quotaManager->GetOriginDirectory(aOriginMetadata)); 1693 1694 MOZ_ASSERT(directory); 1695 1696 nsresult rv = 1697 directory->Append(NS_LITERAL_STRING_FROM_CSTRING(SDB_DIRECTORY_NAME)); 1698 if (NS_WARN_IF(NS_FAILED(rv))) { 1699 return Err(rv); 1700 } 1701 1702 DebugOnly<bool> exists; 1703 MOZ_ASSERT(NS_SUCCEEDED(directory->Exists(&exists)) && exists); 1704 1705 QM_TRY_RETURN(ReduceEachFileAtomicCancelable( 1706 *directory, aCanceled, UsageInfo{}, 1707 [](UsageInfo usageInfo, 1708 const nsCOMPtr<nsIFile>& file) -> Result<UsageInfo, nsresult> { 1709 QM_TRY_INSPECT(const bool& isDirectory, 1710 MOZ_TO_RESULT_INVOKE_MEMBER(file, IsDirectory)); 1711 1712 if (isDirectory) { 1713 (void)WARN_IF_FILE_IS_UNKNOWN(*file); 1714 return usageInfo; 1715 } 1716 1717 nsString leafName; 1718 QM_TRY(MOZ_TO_RESULT(file->GetLeafName(leafName))); 1719 1720 if (StringEndsWith(leafName, kSDBSuffix)) { 1721 QM_TRY_INSPECT(const int64_t& fileSize, 1722 MOZ_TO_RESULT_INVOKE_MEMBER(file, GetFileSize)); 1723 1724 MOZ_ASSERT(fileSize >= 0); 1725 1726 return usageInfo + 1727 UsageInfo{DatabaseUsageType(Some(uint64_t(fileSize)))}; 1728 } 1729 1730 (void)WARN_IF_FILE_IS_UNKNOWN(*file); 1731 1732 return usageInfo; 1733 })); 1734 } 1735 1736 void QuotaClient::OnOriginClearCompleted( 1737 const OriginMetadata& aOriginMetadata) { 1738 AssertIsOnIOThread(); 1739 } 1740 1741 void QuotaClient::OnRepositoryClearCompleted(PersistenceType aPersistenceType) { 1742 AssertIsOnIOThread(); 1743 } 1744 1745 void QuotaClient::ReleaseIOThreadObjects() { AssertIsOnIOThread(); } 1746 1747 void QuotaClient::AbortOperationsForLocks( 1748 const DirectoryLockIdTable& aDirectoryLockIds) { 1749 AssertIsOnBackgroundThread(); 1750 1751 AllowToCloseConnectionsMatching([&aDirectoryLockIds](const auto& connection) { 1752 // If the connections is registered in gOpenConnections then it must have 1753 // a directory lock. 1754 return IsLockForObjectContainedInLockTable(connection, aDirectoryLockIds); 1755 }); 1756 } 1757 1758 void QuotaClient::AbortOperationsForProcess(ContentParentId aContentParentId) { 1759 AssertIsOnBackgroundThread(); 1760 } 1761 1762 void QuotaClient::AbortAllOperations() { 1763 AssertIsOnBackgroundThread(); 1764 1765 AllowToCloseConnectionsMatching([](const auto&) { return true; }); 1766 } 1767 1768 void QuotaClient::StartIdleMaintenance() { AssertIsOnBackgroundThread(); } 1769 1770 void QuotaClient::StopIdleMaintenance() { AssertIsOnBackgroundThread(); } 1771 1772 void QuotaClient::InitiateShutdown() { 1773 AssertIsOnBackgroundThread(); 1774 1775 if (gOpenConnections) { 1776 for (const auto& connection : *gOpenConnections) { 1777 connection->AllowToClose(); 1778 } 1779 } 1780 } 1781 1782 bool QuotaClient::IsShutdownCompleted() const { return !gOpenConnections; } 1783 1784 void QuotaClient::ForceKillActors() { 1785 // Currently we don't implement killing actors (are there any to kill here?). 1786 } 1787 1788 nsCString QuotaClient::GetShutdownStatus() const { 1789 // XXX Gather information here. 1790 return "To be implemented"_ns; 1791 } 1792 1793 void QuotaClient::FinalizeShutdown() { 1794 // Nothing to do here. 1795 } 1796 1797 } // namespace mozilla::dom