Bug 1850713: remove duplicated setting of early hint preloader id in `ScriptLoader...
[gecko.git] / dom / simpledb / ActorsParent.cpp
blob5b09a169080de84b9d466b142a7186d6c2440f4e
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/. */
7 #include "ActorsParent.h"
9 // Local includes
10 #include "SimpleDBCommon.h"
12 // Global includes
13 #include <cstdint>
14 #include <cstdlib>
15 #include <new>
16 #include <utility>
17 #include "ErrorList.h"
18 #include "MainThreadUtils.h"
19 #include "mozilla/AlreadyAddRefed.h"
20 #include "mozilla/Assertions.h"
21 #include "mozilla/Atomics.h"
22 #include "mozilla/DebugOnly.h"
23 #include "mozilla/FixedBufferOutputStream.h"
24 #include "mozilla/Maybe.h"
25 #include "mozilla/Preferences.h"
26 #include "mozilla/RefPtr.h"
27 #include "mozilla/Result.h"
28 #include "mozilla/ResultExtensions.h"
29 #include "mozilla/SpinEventLoopUntil.h"
30 #include "mozilla/StaticPtr.h"
31 #include "mozilla/Unused.h"
32 #include "mozilla/Variant.h"
33 #include "mozilla/dom/PBackgroundSDBConnection.h"
34 #include "mozilla/dom/PBackgroundSDBConnectionParent.h"
35 #include "mozilla/dom/PBackgroundSDBRequestParent.h"
36 #include "mozilla/dom/ipc/IdType.h"
37 #include "mozilla/dom/quota/Client.h"
38 #include "mozilla/dom/quota/ClientImpl.h"
39 #include "mozilla/dom/quota/DirectoryLock.h"
40 #include "mozilla/dom/quota/FileStreams.h"
41 #include "mozilla/dom/quota/QuotaCommon.h"
42 #include "mozilla/dom/quota/QuotaManager.h"
43 #include "mozilla/dom/quota/ResultExtensions.h"
44 #include "mozilla/dom/quota/UsageInfo.h"
45 #include "mozilla/ipc/BackgroundParent.h"
46 #include "mozilla/ipc/BackgroundUtils.h"
47 #include "mozilla/ipc/PBackgroundParent.h"
48 #include "mozilla/ipc/PBackgroundSharedTypes.h"
49 #include "mozilla/ipc/ProtocolUtils.h"
50 #include "nsCOMPtr.h"
51 #include "nsDebug.h"
52 #include "nsError.h"
53 #include "nsIDirectoryEnumerator.h"
54 #include "nsIEventTarget.h"
55 #include "nsIFile.h"
56 #include "nsIFileStreams.h"
57 #include "nsIInputStream.h"
58 #include "nsIOutputStream.h"
59 #include "nsIRunnable.h"
60 #include "nsISeekableStream.h"
61 #include "nsISupports.h"
62 #include "nsIThread.h"
63 #include "nsLiteralString.h"
64 #include "nsString.h"
65 #include "nsStringFwd.h"
66 #include "nsStringStream.h"
67 #include "nsTArray.h"
68 #include "nsTLiteralString.h"
69 #include "nsTStringRepr.h"
70 #include "nsThreadUtils.h"
71 #include "nscore.h"
72 #include "prio.h"
74 namespace mozilla::dom {
76 using namespace mozilla::dom::quota;
77 using namespace mozilla::ipc;
79 namespace {
81 /*******************************************************************************
82 * Constants
83 ******************************************************************************/
85 const uint32_t kCopyBufferSize = 32768;
87 constexpr auto kSDBSuffix = u".sdb"_ns;
89 /*******************************************************************************
90 * Actor class declarations
91 ******************************************************************************/
93 class StreamHelper final : public Runnable {
94 nsCOMPtr<nsIEventTarget> mOwningEventTarget;
95 nsCOMPtr<nsIFileRandomAccessStream> mFileRandomAccessStream;
96 nsCOMPtr<nsIRunnable> mCallback;
98 public:
99 StreamHelper(nsIFileRandomAccessStream* aFileRandomAccessStream,
100 nsIRunnable* aCallback);
102 void AsyncClose();
104 private:
105 ~StreamHelper() override;
107 void RunOnBackgroundThread();
109 void RunOnIOThread();
111 NS_DECL_NSIRUNNABLE
114 class Connection final : public PBackgroundSDBConnectionParent {
115 RefPtr<DirectoryLock> mDirectoryLock;
116 nsCOMPtr<nsIFileRandomAccessStream> mFileRandomAccessStream;
117 const PrincipalInfo mPrincipalInfo;
118 nsCString mOrigin;
119 nsString mName;
121 PersistenceType mPersistenceType;
122 bool mRunningRequest;
123 bool mOpen;
124 bool mAllowedToClose;
125 bool mActorDestroyed;
127 public:
128 Connection(PersistenceType aPersistenceType,
129 const PrincipalInfo& aPrincipalInfo);
131 NS_INLINE_DECL_THREADSAFE_REFCOUNTING(mozilla::dom::Connection)
133 Maybe<DirectoryLock&> MaybeDirectoryLockRef() const {
134 AssertIsOnBackgroundThread();
136 return ToMaybeRef(mDirectoryLock.get());
139 nsIFileRandomAccessStream* GetFileRandomAccessStream() const {
140 AssertIsOnIOThread();
142 return mFileRandomAccessStream;
145 PersistenceType GetPersistenceType() const { return mPersistenceType; }
147 const PrincipalInfo& GetPrincipalInfo() const {
148 AssertIsOnBackgroundThread();
150 return mPrincipalInfo;
153 const nsCString& Origin() const {
154 AssertIsOnBackgroundThread();
155 MOZ_ASSERT(!mOrigin.IsEmpty());
157 return mOrigin;
160 const nsString& Name() const {
161 AssertIsOnBackgroundThread();
162 MOZ_ASSERT(!mName.IsEmpty());
164 return mName;
167 void OnNewRequest();
169 void OnRequestFinished();
171 void OnOpen(
172 const nsACString& aOrigin, const nsAString& aName,
173 already_AddRefed<DirectoryLock> aDirectoryLock,
174 already_AddRefed<nsIFileRandomAccessStream> aFileRandomAccessStream);
176 void OnClose();
178 void AllowToClose();
180 private:
181 ~Connection();
183 void MaybeCloseStream();
185 bool VerifyRequestParams(const SDBRequestParams& aParams) const;
187 // IPDL methods.
188 virtual void ActorDestroy(ActorDestroyReason aWhy) override;
190 mozilla::ipc::IPCResult RecvDeleteMe() override;
192 virtual PBackgroundSDBRequestParent* AllocPBackgroundSDBRequestParent(
193 const SDBRequestParams& aParams) override;
195 virtual mozilla::ipc::IPCResult RecvPBackgroundSDBRequestConstructor(
196 PBackgroundSDBRequestParent* aActor,
197 const SDBRequestParams& aParams) override;
199 virtual bool DeallocPBackgroundSDBRequestParent(
200 PBackgroundSDBRequestParent* aActor) override;
203 class ConnectionOperationBase : public Runnable,
204 public PBackgroundSDBRequestParent {
205 nsCOMPtr<nsIEventTarget> mOwningEventTarget;
206 RefPtr<Connection> mConnection;
207 nsresult mResultCode;
208 Atomic<bool> mOperationMayProceed;
209 bool mActorDestroyed;
211 public:
212 nsIEventTarget* OwningEventTarget() const {
213 MOZ_ASSERT(mOwningEventTarget);
215 return mOwningEventTarget;
218 bool IsOnOwningThread() const {
219 MOZ_ASSERT(mOwningEventTarget);
221 bool current;
222 return NS_SUCCEEDED(mOwningEventTarget->IsOnCurrentThread(&current)) &&
223 current;
226 void AssertIsOnOwningThread() const {
227 MOZ_ASSERT(IsOnBackgroundThread());
228 MOZ_ASSERT(IsOnOwningThread());
231 Connection* GetConnection() const {
232 MOZ_ASSERT(mConnection);
234 return mConnection;
237 nsresult ResultCode() const { return mResultCode; }
239 void MaybeSetFailureCode(nsresult aErrorCode) {
240 MOZ_ASSERT(NS_FAILED(aErrorCode));
242 if (NS_SUCCEEDED(mResultCode)) {
243 mResultCode = aErrorCode;
247 // May be called on any thread, but you should call IsActorDestroyed() if
248 // you know you're on the background thread because it is slightly faster.
249 bool OperationMayProceed() const { return mOperationMayProceed; }
251 bool IsActorDestroyed() const {
252 AssertIsOnOwningThread();
254 return mActorDestroyed;
257 // May be overridden by subclasses if they need to perform work on the
258 // background thread before being dispatched but must always call the base
259 // class implementation. Returning false will kill the child actors and
260 // prevent dispatch.
261 virtual bool Init();
263 virtual nsresult Dispatch();
265 // This callback will be called on the background thread before releasing the
266 // final reference to this request object. Subclasses may perform any
267 // additional cleanup here but must always call the base class implementation.
268 virtual void Cleanup();
270 protected:
271 ConnectionOperationBase(Connection* aConnection)
272 : Runnable("dom::ConnectionOperationBase"),
273 mOwningEventTarget(GetCurrentSerialEventTarget()),
274 mConnection(aConnection),
275 mResultCode(NS_OK),
276 mOperationMayProceed(true),
277 mActorDestroyed(false) {
278 AssertIsOnOwningThread();
281 ~ConnectionOperationBase() override;
283 void SendResults();
285 void DatabaseWork();
287 // Methods that subclasses must implement.
288 virtual nsresult DoDatabaseWork(
289 nsIFileRandomAccessStream* aFileRandomAccessStream) = 0;
291 // Subclasses use this override to set the IPDL response value.
292 virtual void GetResponse(SDBRequestResponse& aResponse) = 0;
294 // A method that subclasses may implement.
295 virtual void OnSuccess();
297 private:
298 NS_IMETHOD
299 Run() override;
301 // IPDL methods.
302 void ActorDestroy(ActorDestroyReason aWhy) override;
305 class OpenOp final : public ConnectionOperationBase,
306 public OpenDirectoryListener {
307 enum class State {
308 // Just created on the PBackground thread, dispatched to the main thread.
309 // Next step is FinishOpen.
310 Initial,
312 // Ensuring quota manager is created and opening directory on the
313 // PBackground thread. Next step is either SendingResults if quota manager
314 // is not available or DirectoryOpenPending if quota manager is available.
315 FinishOpen,
317 // Waiting for directory open allowed on the PBackground thread. The next
318 // step is either SendingResults if directory lock failed to acquire, or
319 // DatabaseWorkOpen if directory lock is acquired.
320 DirectoryOpenPending,
322 // Waiting to do/doing work on the QuotaManager IO thread. Its next step is
323 // SendingResults.
324 DatabaseWorkOpen,
326 // Waiting to send/sending results on the PBackground thread. Next step is
327 // Completed.
328 SendingResults,
330 // All done.
331 Completed
334 const SDBRequestOpenParams mParams;
335 RefPtr<DirectoryLock> mDirectoryLock;
336 nsCOMPtr<nsIFileRandomAccessStream> mFileRandomAccessStream;
337 // XXX Consider changing this to ClientMetadata.
338 quota::OriginMetadata mOriginMetadata;
339 State mState;
340 bool mFileRandomAccessStreamOpen;
342 public:
343 OpenOp(Connection* aConnection, const SDBRequestParams& aParams);
345 nsresult Dispatch() override;
347 private:
348 ~OpenOp() override;
350 nsresult Open();
352 nsresult FinishOpen();
354 nsresult SendToIOThread();
356 nsresult DatabaseWork();
358 void StreamClosedCallback();
360 // ConnectionOperationBase overrides
361 nsresult DoDatabaseWork(
362 nsIFileRandomAccessStream* aFileRandomAccessStream) override;
364 void GetResponse(SDBRequestResponse& aResponse) override;
366 void OnSuccess() override;
368 void Cleanup() override;
370 NS_DECL_ISUPPORTS_INHERITED
372 NS_IMETHOD
373 Run() override;
375 // OpenDirectoryListener overrides.
376 void DirectoryLockAcquired(DirectoryLock* aLock) override;
378 void DirectoryLockFailed() override;
381 class SeekOp final : public ConnectionOperationBase {
382 const SDBRequestSeekParams mParams;
384 public:
385 SeekOp(Connection* aConnection, const SDBRequestParams& aParams);
387 private:
388 ~SeekOp() override = default;
390 nsresult DoDatabaseWork(
391 nsIFileRandomAccessStream* aFileRandomAccessStream) override;
393 void GetResponse(SDBRequestResponse& aResponse) override;
396 class ReadOp final : public ConnectionOperationBase {
397 const SDBRequestReadParams mParams;
399 RefPtr<FixedBufferOutputStream> mOutputStream;
401 public:
402 ReadOp(Connection* aConnection, const SDBRequestParams& aParams);
404 bool Init() override;
406 private:
407 ~ReadOp() override = default;
409 nsresult DoDatabaseWork(
410 nsIFileRandomAccessStream* aFileRandomAccessStream) override;
412 void GetResponse(SDBRequestResponse& aResponse) override;
415 class WriteOp final : public ConnectionOperationBase {
416 const SDBRequestWriteParams mParams;
418 nsCOMPtr<nsIInputStream> mInputStream;
420 uint64_t mSize;
422 public:
423 WriteOp(Connection* aConnection, const SDBRequestParams& aParams);
425 bool Init() override;
427 private:
428 ~WriteOp() override = default;
430 nsresult DoDatabaseWork(
431 nsIFileRandomAccessStream* aFileRandomAccessStream) override;
433 void GetResponse(SDBRequestResponse& aResponse) override;
436 class CloseOp final : public ConnectionOperationBase {
437 public:
438 explicit CloseOp(Connection* aConnection);
440 private:
441 ~CloseOp() override = default;
443 nsresult DoDatabaseWork(
444 nsIFileRandomAccessStream* aFileRandomAccessStream) override;
446 void GetResponse(SDBRequestResponse& aResponse) override;
448 void OnSuccess() override;
451 /*******************************************************************************
452 * Other class declarations
453 ******************************************************************************/
455 class QuotaClient final : public mozilla::dom::quota::Client {
456 static QuotaClient* sInstance;
458 public:
459 QuotaClient();
461 NS_INLINE_DECL_THREADSAFE_REFCOUNTING(QuotaClient, override)
463 Type GetType() override;
465 Result<UsageInfo, nsresult> InitOrigin(PersistenceType aPersistenceType,
466 const OriginMetadata& aOriginMetadata,
467 const AtomicBool& aCanceled) override;
469 nsresult InitOriginWithoutTracking(PersistenceType aPersistenceType,
470 const OriginMetadata& aOriginMetadata,
471 const AtomicBool& aCanceled) override;
473 Result<UsageInfo, nsresult> GetUsageForOrigin(
474 PersistenceType aPersistenceType, const OriginMetadata& aOriginMetadata,
475 const AtomicBool& aCanceled) override;
477 void OnOriginClearCompleted(PersistenceType aPersistenceType,
478 const nsACString& aOrigin) override;
480 void OnRepositoryClearCompleted(PersistenceType aPersistenceType) override;
482 void ReleaseIOThreadObjects() override;
484 void AbortOperationsForLocks(
485 const DirectoryLockIdTable& aDirectoryLockIds) override;
487 void AbortOperationsForProcess(ContentParentId aContentParentId) override;
489 void AbortAllOperations() override;
491 void StartIdleMaintenance() override;
493 void StopIdleMaintenance() override;
495 private:
496 ~QuotaClient() override;
498 void InitiateShutdown() override;
499 bool IsShutdownCompleted() const override;
500 nsCString GetShutdownStatus() const override;
501 void ForceKillActors() override;
502 void FinalizeShutdown() override;
505 /*******************************************************************************
506 * Globals
507 ******************************************************************************/
509 using ConnectionArray = nsTArray<NotNull<RefPtr<Connection>>>;
511 StaticAutoPtr<ConnectionArray> gOpenConnections;
513 template <typename Condition>
514 void AllowToCloseConnectionsMatching(const Condition& aCondition) {
515 AssertIsOnBackgroundThread();
517 if (gOpenConnections) {
518 for (const auto& connection : *gOpenConnections) {
519 if (aCondition(*connection)) {
520 connection->AllowToClose();
526 } // namespace
528 /*******************************************************************************
529 * Exported functions
530 ******************************************************************************/
532 PBackgroundSDBConnectionParent* AllocPBackgroundSDBConnectionParent(
533 const PersistenceType& aPersistenceType,
534 const PrincipalInfo& aPrincipalInfo) {
535 AssertIsOnBackgroundThread();
537 if (NS_WARN_IF(QuotaClient::IsShuttingDownOnBackgroundThread())) {
538 return nullptr;
541 if (NS_WARN_IF(!IsValidPersistenceType(aPersistenceType))) {
542 MOZ_CRASH_UNLESS_FUZZING();
543 return nullptr;
546 if (NS_WARN_IF(aPrincipalInfo.type() == PrincipalInfo::TNullPrincipalInfo)) {
547 MOZ_CRASH_UNLESS_FUZZING();
548 return nullptr;
551 if (NS_WARN_IF(!QuotaManager::IsPrincipalInfoValid(aPrincipalInfo))) {
552 MOZ_CRASH_UNLESS_FUZZING();
553 return nullptr;
556 RefPtr<Connection> actor = new Connection(aPersistenceType, aPrincipalInfo);
558 return actor.forget().take();
561 bool RecvPBackgroundSDBConnectionConstructor(
562 PBackgroundSDBConnectionParent* aActor,
563 const PersistenceType& aPersistenceType,
564 const PrincipalInfo& aPrincipalInfo) {
565 AssertIsOnBackgroundThread();
566 MOZ_ASSERT(aActor);
567 MOZ_ASSERT(!QuotaClient::IsShuttingDownOnBackgroundThread());
569 return true;
572 bool DeallocPBackgroundSDBConnectionParent(
573 PBackgroundSDBConnectionParent* aActor) {
574 AssertIsOnBackgroundThread();
575 MOZ_ASSERT(aActor);
577 RefPtr<Connection> actor = dont_AddRef(static_cast<Connection*>(aActor));
578 return true;
581 namespace simpledb {
583 already_AddRefed<mozilla::dom::quota::Client> CreateQuotaClient() {
584 AssertIsOnBackgroundThread();
586 RefPtr<QuotaClient> client = new QuotaClient();
587 return client.forget();
590 } // namespace simpledb
592 /*******************************************************************************
593 * StreamHelper
594 ******************************************************************************/
596 StreamHelper::StreamHelper(nsIFileRandomAccessStream* aFileRandomAccessStream,
597 nsIRunnable* aCallback)
598 : Runnable("dom::StreamHelper"),
599 mOwningEventTarget(GetCurrentSerialEventTarget()),
600 mFileRandomAccessStream(aFileRandomAccessStream),
601 mCallback(aCallback) {
602 AssertIsOnBackgroundThread();
603 MOZ_ASSERT(aFileRandomAccessStream);
604 MOZ_ASSERT(aCallback);
607 StreamHelper::~StreamHelper() {
608 MOZ_ASSERT(!mFileRandomAccessStream);
609 MOZ_ASSERT(!mCallback);
612 void StreamHelper::AsyncClose() {
613 AssertIsOnBackgroundThread();
615 QuotaManager* quotaManager = QuotaManager::Get();
616 MOZ_ASSERT(quotaManager);
618 MOZ_ALWAYS_SUCCEEDS(
619 quotaManager->IOThread()->Dispatch(this, NS_DISPATCH_NORMAL));
622 void StreamHelper::RunOnBackgroundThread() {
623 AssertIsOnBackgroundThread();
625 nsCOMPtr<nsIFileRandomAccessStream> fileRandomAccessStream;
626 mFileRandomAccessStream.swap(fileRandomAccessStream);
628 nsCOMPtr<nsIRunnable> callback;
629 mCallback.swap(callback);
631 callback->Run();
634 void StreamHelper::RunOnIOThread() {
635 AssertIsOnIOThread();
636 MOZ_ASSERT(mFileRandomAccessStream);
638 nsCOMPtr<nsIInputStream> inputStream =
639 do_QueryInterface(mFileRandomAccessStream);
640 MOZ_ASSERT(inputStream);
642 nsresult rv = inputStream->Close();
643 Unused << NS_WARN_IF(NS_FAILED(rv));
645 MOZ_ALWAYS_SUCCEEDS(mOwningEventTarget->Dispatch(this, NS_DISPATCH_NORMAL));
648 NS_IMETHODIMP
649 StreamHelper::Run() {
650 MOZ_ASSERT(mCallback);
652 if (IsOnBackgroundThread()) {
653 RunOnBackgroundThread();
654 } else {
655 RunOnIOThread();
658 return NS_OK;
661 /*******************************************************************************
662 * Connection
663 ******************************************************************************/
665 Connection::Connection(PersistenceType aPersistenceType,
666 const PrincipalInfo& aPrincipalInfo)
667 : mPrincipalInfo(aPrincipalInfo),
668 mPersistenceType(aPersistenceType),
669 mRunningRequest(false),
670 mOpen(false),
671 mAllowedToClose(false),
672 mActorDestroyed(false) {
673 AssertIsOnBackgroundThread();
674 MOZ_ASSERT(!QuotaClient::IsShuttingDownOnBackgroundThread());
677 Connection::~Connection() {
678 MOZ_ASSERT(!mRunningRequest);
679 MOZ_ASSERT(!mOpen);
680 MOZ_ASSERT(mActorDestroyed);
683 void Connection::OnNewRequest() {
684 AssertIsOnBackgroundThread();
685 MOZ_ASSERT(!mRunningRequest);
687 mRunningRequest = true;
690 void Connection::OnRequestFinished() {
691 AssertIsOnBackgroundThread();
692 MOZ_ASSERT(mRunningRequest);
694 mRunningRequest = false;
696 MaybeCloseStream();
699 void Connection::OnOpen(
700 const nsACString& aOrigin, const nsAString& aName,
701 already_AddRefed<DirectoryLock> aDirectoryLock,
702 already_AddRefed<nsIFileRandomAccessStream> aFileRandomAccessStream) {
703 AssertIsOnBackgroundThread();
704 MOZ_ASSERT(!aOrigin.IsEmpty());
705 MOZ_ASSERT(!aName.IsEmpty());
706 MOZ_ASSERT(mOrigin.IsEmpty());
707 MOZ_ASSERT(mName.IsEmpty());
708 MOZ_ASSERT(!mDirectoryLock);
709 MOZ_ASSERT(!mFileRandomAccessStream);
710 MOZ_ASSERT(!mOpen);
712 mOrigin = aOrigin;
713 mName = aName;
714 mDirectoryLock = aDirectoryLock;
715 mFileRandomAccessStream = aFileRandomAccessStream;
716 mOpen = true;
718 if (!gOpenConnections) {
719 gOpenConnections = new ConnectionArray();
722 gOpenConnections->AppendElement(WrapNotNullUnchecked(this));
725 void Connection::OnClose() {
726 AssertIsOnBackgroundThread();
727 MOZ_ASSERT(!mOrigin.IsEmpty());
728 MOZ_ASSERT(mDirectoryLock);
729 MOZ_ASSERT(mFileRandomAccessStream);
730 MOZ_ASSERT(mOpen);
732 mOrigin.Truncate();
733 mName.Truncate();
734 mDirectoryLock = nullptr;
735 mFileRandomAccessStream = nullptr;
736 mOpen = false;
738 MOZ_ASSERT(gOpenConnections);
739 gOpenConnections->RemoveElement(this);
741 if (gOpenConnections->IsEmpty()) {
742 gOpenConnections = nullptr;
745 if (mAllowedToClose && !mActorDestroyed) {
746 Unused << SendClosed();
750 void Connection::AllowToClose() {
751 AssertIsOnBackgroundThread();
753 if (mAllowedToClose) {
754 return;
757 mAllowedToClose = true;
759 if (!mActorDestroyed) {
760 Unused << SendAllowToClose();
763 MaybeCloseStream();
766 void Connection::MaybeCloseStream() {
767 AssertIsOnBackgroundThread();
769 if (!mRunningRequest && mOpen && mAllowedToClose) {
770 nsCOMPtr<nsIRunnable> callback = NewRunnableMethod(
771 "dom::Connection::OnClose", this, &Connection::OnClose);
773 RefPtr<StreamHelper> helper =
774 new StreamHelper(mFileRandomAccessStream, callback);
775 helper->AsyncClose();
779 bool Connection::VerifyRequestParams(const SDBRequestParams& aParams) const {
780 AssertIsOnBackgroundThread();
781 MOZ_ASSERT(aParams.type() != SDBRequestParams::T__None);
783 switch (aParams.type()) {
784 case SDBRequestParams::TSDBRequestOpenParams: {
785 if (NS_WARN_IF(mOpen)) {
786 MOZ_CRASH_UNLESS_FUZZING();
787 return false;
790 break;
793 case SDBRequestParams::TSDBRequestSeekParams:
794 case SDBRequestParams::TSDBRequestReadParams:
795 case SDBRequestParams::TSDBRequestWriteParams:
796 case SDBRequestParams::TSDBRequestCloseParams: {
797 if (NS_WARN_IF(!mOpen)) {
798 MOZ_CRASH_UNLESS_FUZZING();
799 return false;
802 break;
805 default:
806 MOZ_CRASH("Should never get here!");
809 return true;
812 void Connection::ActorDestroy(ActorDestroyReason aWhy) {
813 AssertIsOnBackgroundThread();
814 MOZ_ASSERT(!mActorDestroyed);
816 mActorDestroyed = true;
818 AllowToClose();
821 mozilla::ipc::IPCResult Connection::RecvDeleteMe() {
822 AssertIsOnBackgroundThread();
823 MOZ_ASSERT(!mActorDestroyed);
825 IProtocol* mgr = Manager();
826 if (!PBackgroundSDBConnectionParent::Send__delete__(this)) {
827 return IPC_FAIL_NO_REASON(mgr);
830 return IPC_OK();
833 PBackgroundSDBRequestParent* Connection::AllocPBackgroundSDBRequestParent(
834 const SDBRequestParams& aParams) {
835 AssertIsOnBackgroundThread();
836 MOZ_ASSERT(aParams.type() != SDBRequestParams::T__None);
838 if (aParams.type() == SDBRequestParams::TSDBRequestOpenParams &&
839 NS_WARN_IF(QuotaClient::IsShuttingDownOnBackgroundThread())) {
840 return nullptr;
843 if (mAllowedToClose) {
844 return nullptr;
847 #ifdef DEBUG
848 // Always verify parameters in DEBUG builds!
849 bool trustParams = false;
850 #else
851 PBackgroundParent* backgroundActor = Manager();
852 MOZ_ASSERT(backgroundActor);
854 bool trustParams = !BackgroundParent::IsOtherProcessActor(backgroundActor);
855 #endif
857 if (NS_WARN_IF(!trustParams && !VerifyRequestParams(aParams))) {
858 MOZ_CRASH_UNLESS_FUZZING();
859 return nullptr;
862 if (NS_WARN_IF(mRunningRequest)) {
863 MOZ_CRASH_UNLESS_FUZZING();
864 return nullptr;
867 QM_TRY(QuotaManager::EnsureCreated(), nullptr);
869 RefPtr<ConnectionOperationBase> actor;
871 switch (aParams.type()) {
872 case SDBRequestParams::TSDBRequestOpenParams:
873 actor = new OpenOp(this, aParams);
874 break;
876 case SDBRequestParams::TSDBRequestSeekParams:
877 actor = new SeekOp(this, aParams);
878 break;
880 case SDBRequestParams::TSDBRequestReadParams:
881 actor = new ReadOp(this, aParams);
882 break;
884 case SDBRequestParams::TSDBRequestWriteParams:
885 actor = new WriteOp(this, aParams);
886 break;
888 case SDBRequestParams::TSDBRequestCloseParams:
889 actor = new CloseOp(this);
890 break;
892 default:
893 MOZ_CRASH("Should never get here!");
896 // Transfer ownership to IPDL.
897 return actor.forget().take();
900 mozilla::ipc::IPCResult Connection::RecvPBackgroundSDBRequestConstructor(
901 PBackgroundSDBRequestParent* aActor, const SDBRequestParams& aParams) {
902 AssertIsOnBackgroundThread();
903 MOZ_ASSERT(aActor);
904 MOZ_ASSERT(aParams.type() != SDBRequestParams::T__None);
905 MOZ_ASSERT_IF(aParams.type() == SDBRequestParams::TSDBRequestOpenParams,
906 !QuotaClient::IsShuttingDownOnBackgroundThread());
907 MOZ_ASSERT(!mAllowedToClose);
908 MOZ_ASSERT(!mRunningRequest);
910 auto* op = static_cast<ConnectionOperationBase*>(aActor);
912 if (NS_WARN_IF(!op->Init())) {
913 op->Cleanup();
914 return IPC_FAIL_NO_REASON(this);
917 if (NS_WARN_IF(NS_FAILED(op->Dispatch()))) {
918 op->Cleanup();
919 return IPC_FAIL_NO_REASON(this);
922 return IPC_OK();
925 bool Connection::DeallocPBackgroundSDBRequestParent(
926 PBackgroundSDBRequestParent* aActor) {
927 AssertIsOnBackgroundThread();
928 MOZ_ASSERT(aActor);
930 // Transfer ownership back from IPDL.
931 RefPtr<ConnectionOperationBase> actor =
932 dont_AddRef(static_cast<ConnectionOperationBase*>(aActor));
933 return true;
936 /*******************************************************************************
937 * ConnectionOperationBase
938 ******************************************************************************/
940 ConnectionOperationBase::~ConnectionOperationBase() {
941 MOZ_ASSERT(
942 !mConnection,
943 "ConnectionOperationBase::Cleanup() was not called by a subclass!");
944 MOZ_ASSERT(mActorDestroyed);
947 bool ConnectionOperationBase::Init() {
948 AssertIsOnBackgroundThread();
949 MOZ_ASSERT(mConnection);
951 mConnection->OnNewRequest();
953 return true;
956 nsresult ConnectionOperationBase::Dispatch() {
957 if (NS_WARN_IF(QuotaClient::IsShuttingDownOnBackgroundThread()) ||
958 IsActorDestroyed()) {
959 return NS_ERROR_ABORT;
962 QuotaManager* quotaManager = QuotaManager::Get();
963 MOZ_ASSERT(quotaManager);
965 nsresult rv = quotaManager->IOThread()->Dispatch(this, NS_DISPATCH_NORMAL);
966 if (NS_WARN_IF(NS_FAILED(rv))) {
967 return rv;
970 return NS_OK;
973 void ConnectionOperationBase::Cleanup() {
974 AssertIsOnOwningThread();
975 MOZ_ASSERT(mConnection);
977 mConnection->OnRequestFinished();
979 mConnection = nullptr;
982 void ConnectionOperationBase::SendResults() {
983 AssertIsOnOwningThread();
985 if (IsActorDestroyed()) {
986 MaybeSetFailureCode(NS_ERROR_FAILURE);
987 } else {
988 SDBRequestResponse response;
990 if (NS_SUCCEEDED(mResultCode)) {
991 GetResponse(response);
993 MOZ_ASSERT(response.type() != SDBRequestResponse::T__None);
994 MOZ_ASSERT(response.type() != SDBRequestResponse::Tnsresult);
995 } else {
996 response = mResultCode;
999 Unused << PBackgroundSDBRequestParent::Send__delete__(this, response);
1001 if (NS_SUCCEEDED(mResultCode)) {
1002 OnSuccess();
1006 Cleanup();
1009 void ConnectionOperationBase::DatabaseWork() {
1010 AssertIsOnIOThread();
1011 MOZ_ASSERT(NS_SUCCEEDED(mResultCode));
1013 if (!OperationMayProceed()) {
1014 // The operation was canceled in some way, likely because the child process
1015 // has crashed.
1016 mResultCode = NS_ERROR_ABORT;
1017 } else {
1018 nsIFileRandomAccessStream* fileRandomAccessStream =
1019 mConnection->GetFileRandomAccessStream();
1020 MOZ_ASSERT(fileRandomAccessStream);
1022 nsresult rv = DoDatabaseWork(fileRandomAccessStream);
1023 if (NS_FAILED(rv)) {
1024 mResultCode = rv;
1028 MOZ_ALWAYS_SUCCEEDS(mOwningEventTarget->Dispatch(this, NS_DISPATCH_NORMAL));
1031 void ConnectionOperationBase::OnSuccess() { AssertIsOnOwningThread(); }
1033 NS_IMETHODIMP
1034 ConnectionOperationBase::Run() {
1035 if (IsOnBackgroundThread()) {
1036 SendResults();
1037 } else {
1038 DatabaseWork();
1041 return NS_OK;
1044 void ConnectionOperationBase::ActorDestroy(ActorDestroyReason aWhy) {
1045 AssertIsOnBackgroundThread();
1047 mOperationMayProceed = false;
1048 mActorDestroyed = true;
1051 OpenOp::OpenOp(Connection* aConnection, const SDBRequestParams& aParams)
1052 : ConnectionOperationBase(aConnection),
1053 mParams(aParams.get_SDBRequestOpenParams()),
1054 mState(State::Initial),
1055 mFileRandomAccessStreamOpen(false) {
1056 MOZ_ASSERT(aParams.type() == SDBRequestParams::TSDBRequestOpenParams);
1059 OpenOp::~OpenOp() {
1060 MOZ_ASSERT(!mDirectoryLock);
1061 MOZ_ASSERT(!mFileRandomAccessStream);
1062 MOZ_ASSERT(!mFileRandomAccessStreamOpen);
1063 MOZ_ASSERT_IF(OperationMayProceed(),
1064 mState == State::Initial || mState == State::Completed);
1067 nsresult OpenOp::Dispatch() {
1068 MOZ_ALWAYS_SUCCEEDS(NS_DispatchToMainThread(this));
1070 return NS_OK;
1073 nsresult OpenOp::Open() {
1074 MOZ_ASSERT(NS_IsMainThread());
1075 MOZ_ASSERT(mState == State::Initial);
1077 if (NS_WARN_IF(QuotaClient::IsShuttingDownOnNonBackgroundThread()) ||
1078 !OperationMayProceed()) {
1079 return NS_ERROR_ABORT;
1082 if (NS_WARN_IF(!Preferences::GetBool(kPrefSimpleDBEnabled, false))) {
1083 return NS_ERROR_UNEXPECTED;
1086 mState = State::FinishOpen;
1087 MOZ_ALWAYS_SUCCEEDS(OwningEventTarget()->Dispatch(this, NS_DISPATCH_NORMAL));
1089 return NS_OK;
1092 nsresult OpenOp::FinishOpen() {
1093 AssertIsOnOwningThread();
1094 MOZ_ASSERT(mOriginMetadata.mOrigin.IsEmpty());
1095 MOZ_ASSERT(!mDirectoryLock);
1096 MOZ_ASSERT(mState == State::FinishOpen);
1098 if (NS_WARN_IF(QuotaClient::IsShuttingDownOnBackgroundThread()) ||
1099 IsActorDestroyed()) {
1100 return NS_ERROR_ABORT;
1103 QuotaManager* quotaManager = QuotaManager::Get();
1104 MOZ_ASSERT(quotaManager);
1106 const PrincipalInfo& principalInfo = GetConnection()->GetPrincipalInfo();
1108 PersistenceType persistenceType = GetConnection()->GetPersistenceType();
1110 if (principalInfo.type() == PrincipalInfo::TSystemPrincipalInfo) {
1111 mOriginMetadata = {QuotaManager::GetInfoForChrome(), persistenceType};
1112 } else {
1113 MOZ_ASSERT(principalInfo.type() == PrincipalInfo::TContentPrincipalInfo);
1115 QM_TRY_UNWRAP(
1116 auto principalMetadata,
1117 quotaManager->GetInfoFromValidatedPrincipalInfo(principalInfo));
1119 mOriginMetadata = {std::move(principalMetadata), persistenceType};
1122 if (gOpenConnections) {
1123 for (const auto& connection : *gOpenConnections) {
1124 if (connection->Origin() == mOriginMetadata.mOrigin &&
1125 connection->Name() == mParams.name()) {
1126 return NS_ERROR_STORAGE_BUSY;
1131 // Open the directory
1133 RefPtr<DirectoryLock> directoryLock = quotaManager->CreateDirectoryLock(
1134 GetConnection()->GetPersistenceType(), mOriginMetadata,
1135 mozilla::dom::quota::Client::SDB,
1136 /* aExclusive */ false);
1138 mState = State::DirectoryOpenPending;
1139 directoryLock->Acquire(this);
1141 return NS_OK;
1144 nsresult OpenOp::SendToIOThread() {
1145 AssertIsOnOwningThread();
1146 MOZ_ASSERT(mState == State::DirectoryOpenPending);
1148 if (NS_WARN_IF(QuotaClient::IsShuttingDownOnBackgroundThread()) ||
1149 IsActorDestroyed()) {
1150 return NS_ERROR_ABORT;
1153 mFileRandomAccessStream = new FileRandomAccessStream(
1154 GetConnection()->GetPersistenceType(), mOriginMetadata,
1155 mozilla::dom::quota::Client::SDB);
1157 QuotaManager* quotaManager = QuotaManager::Get();
1158 MOZ_ASSERT(quotaManager);
1160 // Must set this before dispatching otherwise we will race with the IO thread.
1161 mState = State::DatabaseWorkOpen;
1163 nsresult rv = quotaManager->IOThread()->Dispatch(this, NS_DISPATCH_NORMAL);
1164 if (NS_WARN_IF(NS_FAILED(rv))) {
1165 return rv;
1168 return NS_OK;
1171 nsresult OpenOp::DatabaseWork() {
1172 AssertIsOnIOThread();
1173 MOZ_ASSERT(mState == State::DatabaseWorkOpen);
1174 MOZ_ASSERT(mFileRandomAccessStream);
1175 MOZ_ASSERT(!mFileRandomAccessStreamOpen);
1177 if (NS_WARN_IF(QuotaClient::IsShuttingDownOnNonBackgroundThread()) ||
1178 !OperationMayProceed()) {
1179 return NS_ERROR_ABORT;
1182 QuotaManager* quotaManager = QuotaManager::Get();
1183 MOZ_ASSERT(quotaManager);
1185 QM_TRY(MOZ_TO_RESULT(quotaManager->EnsureStorageIsInitialized()));
1187 QM_TRY_INSPECT(
1188 const auto& dbDirectory,
1189 ([persistenceType = GetConnection()->GetPersistenceType(), &quotaManager,
1190 this]()
1191 -> mozilla::Result<std::pair<nsCOMPtr<nsIFile>, bool>, nsresult> {
1192 if (persistenceType == PERSISTENCE_TYPE_PERSISTENT) {
1193 QM_TRY_RETURN(quotaManager->EnsurePersistentOriginIsInitialized(
1194 mOriginMetadata));
1197 QM_TRY(
1198 MOZ_TO_RESULT(quotaManager->EnsureTemporaryStorageIsInitialized()));
1199 QM_TRY_RETURN(quotaManager->EnsureTemporaryOriginIsInitialized(
1200 persistenceType, mOriginMetadata));
1202 .map([](const auto& res) { return res.first; })));
1204 nsresult rv =
1205 dbDirectory->Append(NS_LITERAL_STRING_FROM_CSTRING(SDB_DIRECTORY_NAME));
1206 if (NS_WARN_IF(NS_FAILED(rv))) {
1207 return rv;
1210 bool exists;
1211 rv = dbDirectory->Exists(&exists);
1212 if (NS_WARN_IF(NS_FAILED(rv))) {
1213 return rv;
1216 if (!exists) {
1217 rv = dbDirectory->Create(nsIFile::DIRECTORY_TYPE, 0755);
1218 if (NS_WARN_IF(NS_FAILED(rv))) {
1219 return rv;
1222 #ifdef DEBUG
1223 else {
1224 bool isDirectory;
1225 MOZ_ASSERT(NS_SUCCEEDED(dbDirectory->IsDirectory(&isDirectory)));
1226 MOZ_ASSERT(isDirectory);
1228 #endif
1230 nsCOMPtr<nsIFile> dbFile;
1231 rv = dbDirectory->Clone(getter_AddRefs(dbFile));
1232 if (NS_WARN_IF(NS_FAILED(rv))) {
1233 return rv;
1236 rv = dbFile->Append(mParams.name() + kSDBSuffix);
1237 if (NS_WARN_IF(NS_FAILED(rv))) {
1238 return rv;
1241 nsString databaseFilePath;
1242 rv = dbFile->GetPath(databaseFilePath);
1243 if (NS_WARN_IF(NS_FAILED(rv))) {
1244 return rv;
1247 rv = mFileRandomAccessStream->Init(dbFile, PR_RDWR | PR_CREATE_FILE, 0644, 0);
1248 if (NS_WARN_IF(NS_FAILED(rv))) {
1249 return rv;
1252 mFileRandomAccessStreamOpen = true;
1254 rv = DoDatabaseWork(mFileRandomAccessStream);
1255 if (NS_WARN_IF(NS_FAILED(rv))) {
1256 return rv;
1259 // Must set mState before dispatching otherwise we will race with the owning
1260 // thread.
1261 mState = State::SendingResults;
1263 rv = OwningEventTarget()->Dispatch(this, NS_DISPATCH_NORMAL);
1264 if (NS_WARN_IF(NS_FAILED(rv))) {
1265 return rv;
1268 return NS_OK;
1271 void OpenOp::StreamClosedCallback() {
1272 AssertIsOnOwningThread();
1273 MOZ_ASSERT(NS_FAILED(ResultCode()));
1274 MOZ_ASSERT(mDirectoryLock);
1275 MOZ_ASSERT(mFileRandomAccessStream);
1276 MOZ_ASSERT(mFileRandomAccessStreamOpen);
1278 mDirectoryLock = nullptr;
1279 mFileRandomAccessStream = nullptr;
1280 mFileRandomAccessStreamOpen = false;
1283 nsresult OpenOp::DoDatabaseWork(
1284 nsIFileRandomAccessStream* aFileRandomAccessStream) {
1285 AssertIsOnIOThread();
1287 return NS_OK;
1290 void OpenOp::GetResponse(SDBRequestResponse& aResponse) {
1291 AssertIsOnOwningThread();
1293 aResponse = SDBRequestOpenResponse();
1296 void OpenOp::OnSuccess() {
1297 AssertIsOnOwningThread();
1298 MOZ_ASSERT(NS_SUCCEEDED(ResultCode()));
1299 MOZ_ASSERT(!mOriginMetadata.mOrigin.IsEmpty());
1300 MOZ_ASSERT(mDirectoryLock);
1301 MOZ_ASSERT(mFileRandomAccessStream);
1302 MOZ_ASSERT(mFileRandomAccessStreamOpen);
1304 RefPtr<DirectoryLock> directoryLock;
1305 nsCOMPtr<nsIFileRandomAccessStream> fileRandomAccessStream;
1307 mDirectoryLock.swap(directoryLock);
1308 mFileRandomAccessStream.swap(fileRandomAccessStream);
1309 mFileRandomAccessStreamOpen = false;
1311 GetConnection()->OnOpen(mOriginMetadata.mOrigin, mParams.name(),
1312 directoryLock.forget(),
1313 fileRandomAccessStream.forget());
1316 void OpenOp::Cleanup() {
1317 AssertIsOnOwningThread();
1318 MOZ_ASSERT_IF(mFileRandomAccessStreamOpen, mFileRandomAccessStream);
1320 if (mFileRandomAccessStream && mFileRandomAccessStreamOpen) {
1321 // If we have an initialized file stream then the operation must have failed
1322 // and there must be a directory lock too.
1323 MOZ_ASSERT(NS_FAILED(ResultCode()));
1324 MOZ_ASSERT(mDirectoryLock);
1326 // We must close the stream on the I/O thread before releasing it on this
1327 // thread. The directory lock can't be released either.
1328 nsCOMPtr<nsIRunnable> callback =
1329 NewRunnableMethod("dom::OpenOp::StreamClosedCallback", this,
1330 &OpenOp::StreamClosedCallback);
1332 RefPtr<StreamHelper> helper =
1333 new StreamHelper(mFileRandomAccessStream, callback);
1334 helper->AsyncClose();
1335 } else {
1336 MOZ_ASSERT(!mFileRandomAccessStreamOpen);
1338 mDirectoryLock = nullptr;
1339 mFileRandomAccessStream = nullptr;
1342 ConnectionOperationBase::Cleanup();
1345 NS_IMPL_ISUPPORTS_INHERITED0(OpenOp, ConnectionOperationBase)
1347 NS_IMETHODIMP
1348 OpenOp::Run() {
1349 nsresult rv;
1351 switch (mState) {
1352 case State::Initial:
1353 rv = Open();
1354 break;
1356 case State::FinishOpen:
1357 rv = FinishOpen();
1358 break;
1360 case State::DatabaseWorkOpen:
1361 rv = DatabaseWork();
1362 break;
1364 case State::SendingResults:
1365 SendResults();
1366 return NS_OK;
1368 default:
1369 MOZ_CRASH("Bad state!");
1372 if (NS_WARN_IF(NS_FAILED(rv)) && mState != State::SendingResults) {
1373 MaybeSetFailureCode(rv);
1375 // Must set mState before dispatching otherwise we will race with the owning
1376 // thread.
1377 mState = State::SendingResults;
1379 if (IsOnOwningThread()) {
1380 SendResults();
1381 } else {
1382 MOZ_ALWAYS_SUCCEEDS(
1383 OwningEventTarget()->Dispatch(this, NS_DISPATCH_NORMAL));
1387 return NS_OK;
1390 void OpenOp::DirectoryLockAcquired(DirectoryLock* aLock) {
1391 AssertIsOnOwningThread();
1392 MOZ_ASSERT(mState == State::DirectoryOpenPending);
1393 MOZ_ASSERT(!mDirectoryLock);
1395 mDirectoryLock = aLock;
1397 nsresult rv = SendToIOThread();
1398 if (NS_WARN_IF(NS_FAILED(rv))) {
1399 MaybeSetFailureCode(rv);
1401 // The caller holds a strong reference to us, no need for a self reference
1402 // before calling Run().
1404 mState = State::SendingResults;
1405 MOZ_ALWAYS_SUCCEEDS(Run());
1407 return;
1411 void OpenOp::DirectoryLockFailed() {
1412 AssertIsOnOwningThread();
1413 MOZ_ASSERT(mState == State::DirectoryOpenPending);
1414 MOZ_ASSERT(!mDirectoryLock);
1416 MaybeSetFailureCode(NS_ERROR_FAILURE);
1418 // The caller holds a strong reference to us, no need for a self reference
1419 // before calling Run().
1421 mState = State::SendingResults;
1422 MOZ_ALWAYS_SUCCEEDS(Run());
1425 SeekOp::SeekOp(Connection* aConnection, const SDBRequestParams& aParams)
1426 : ConnectionOperationBase(aConnection),
1427 mParams(aParams.get_SDBRequestSeekParams()) {
1428 MOZ_ASSERT(aParams.type() == SDBRequestParams::TSDBRequestSeekParams);
1431 nsresult SeekOp::DoDatabaseWork(
1432 nsIFileRandomAccessStream* aFileRandomAccessStream) {
1433 AssertIsOnIOThread();
1434 MOZ_ASSERT(aFileRandomAccessStream);
1436 nsresult rv = aFileRandomAccessStream->Seek(nsISeekableStream::NS_SEEK_SET,
1437 mParams.offset());
1439 if (NS_WARN_IF(NS_FAILED(rv))) {
1440 return rv;
1443 return NS_OK;
1446 void SeekOp::GetResponse(SDBRequestResponse& aResponse) {
1447 aResponse = SDBRequestSeekResponse();
1450 ReadOp::ReadOp(Connection* aConnection, const SDBRequestParams& aParams)
1451 : ConnectionOperationBase(aConnection),
1452 mParams(aParams.get_SDBRequestReadParams()) {
1453 MOZ_ASSERT(aParams.type() == SDBRequestParams::TSDBRequestReadParams);
1456 bool ReadOp::Init() {
1457 AssertIsOnOwningThread();
1459 if (NS_WARN_IF(!ConnectionOperationBase::Init())) {
1460 return false;
1463 if (NS_WARN_IF(mParams.size() > std::numeric_limits<std::size_t>::max())) {
1464 return false;
1467 mOutputStream = FixedBufferOutputStream::Create(mParams.size(), fallible);
1468 if (NS_WARN_IF(!mOutputStream)) {
1469 return false;
1472 return true;
1475 nsresult ReadOp::DoDatabaseWork(
1476 nsIFileRandomAccessStream* aFileRandomAccessStream) {
1477 AssertIsOnIOThread();
1478 MOZ_ASSERT(aFileRandomAccessStream);
1480 nsCOMPtr<nsIInputStream> inputStream =
1481 do_QueryInterface(aFileRandomAccessStream);
1482 MOZ_ASSERT(inputStream);
1484 nsresult rv;
1486 uint64_t offset = 0;
1488 do {
1489 char copyBuffer[kCopyBufferSize];
1491 uint64_t max = mParams.size() - offset;
1492 if (max == 0) {
1493 break;
1496 uint32_t count = sizeof(copyBuffer);
1497 if (count > max) {
1498 count = max;
1501 uint32_t numRead;
1502 rv = inputStream->Read(copyBuffer, count, &numRead);
1503 if (NS_WARN_IF(NS_FAILED(rv))) {
1504 return rv;
1507 if (!numRead) {
1508 break;
1511 uint32_t numWrite;
1512 rv = mOutputStream->Write(copyBuffer, numRead, &numWrite);
1513 if (NS_WARN_IF(NS_FAILED(rv))) {
1514 return rv;
1517 if (NS_WARN_IF(numWrite != numRead)) {
1518 return NS_ERROR_FAILURE;
1521 offset += numWrite;
1522 } while (true);
1524 MOZ_ASSERT(offset == mParams.size());
1526 MOZ_ALWAYS_SUCCEEDS(mOutputStream->Close());
1528 return NS_OK;
1531 void ReadOp::GetResponse(SDBRequestResponse& aResponse) {
1532 aResponse = SDBRequestReadResponse(nsCString(mOutputStream->WrittenData()));
1535 WriteOp::WriteOp(Connection* aConnection, const SDBRequestParams& aParams)
1536 : ConnectionOperationBase(aConnection),
1537 mParams(aParams.get_SDBRequestWriteParams()),
1538 mSize(0) {
1539 MOZ_ASSERT(aParams.type() == SDBRequestParams::TSDBRequestWriteParams);
1542 bool WriteOp::Init() {
1543 AssertIsOnOwningThread();
1545 if (NS_WARN_IF(!ConnectionOperationBase::Init())) {
1546 return false;
1549 const nsCString& string = mParams.data();
1551 nsCOMPtr<nsIInputStream> inputStream;
1552 nsresult rv = NS_NewCStringInputStream(getter_AddRefs(inputStream), string);
1553 if (NS_WARN_IF(NS_FAILED(rv))) {
1554 return false;
1557 mInputStream = std::move(inputStream);
1558 mSize = string.Length();
1560 return true;
1563 nsresult WriteOp::DoDatabaseWork(
1564 nsIFileRandomAccessStream* aFileRandomAccessStream) {
1565 AssertIsOnIOThread();
1566 MOZ_ASSERT(aFileRandomAccessStream);
1568 nsCOMPtr<nsIOutputStream> outputStream =
1569 do_QueryInterface(aFileRandomAccessStream);
1570 MOZ_ASSERT(outputStream);
1572 nsresult rv;
1574 do {
1575 char copyBuffer[kCopyBufferSize];
1577 uint32_t numRead;
1578 rv = mInputStream->Read(copyBuffer, sizeof(copyBuffer), &numRead);
1579 if (NS_WARN_IF(NS_FAILED(rv))) {
1580 break;
1583 if (!numRead) {
1584 break;
1587 uint32_t numWrite;
1588 rv = outputStream->Write(copyBuffer, numRead, &numWrite);
1589 if (NS_WARN_IF(NS_FAILED(rv))) {
1590 return rv;
1593 if (NS_WARN_IF(numWrite != numRead)) {
1594 return NS_ERROR_FAILURE;
1596 } while (true);
1598 MOZ_ALWAYS_SUCCEEDS(mInputStream->Close());
1600 return NS_OK;
1603 void WriteOp::GetResponse(SDBRequestResponse& aResponse) {
1604 aResponse = SDBRequestWriteResponse();
1607 CloseOp::CloseOp(Connection* aConnection)
1608 : ConnectionOperationBase(aConnection) {}
1610 nsresult CloseOp::DoDatabaseWork(
1611 nsIFileRandomAccessStream* aFileRandomAccessStream) {
1612 AssertIsOnIOThread();
1613 MOZ_ASSERT(aFileRandomAccessStream);
1615 nsCOMPtr<nsIInputStream> inputStream =
1616 do_QueryInterface(aFileRandomAccessStream);
1617 MOZ_ASSERT(inputStream);
1619 nsresult rv = inputStream->Close();
1620 if (NS_WARN_IF(NS_FAILED(rv))) {
1621 return rv;
1624 return NS_OK;
1627 void CloseOp::GetResponse(SDBRequestResponse& aResponse) {
1628 aResponse = SDBRequestCloseResponse();
1631 void CloseOp::OnSuccess() {
1632 AssertIsOnOwningThread();
1634 GetConnection()->OnClose();
1637 /*******************************************************************************
1638 * QuotaClient
1639 ******************************************************************************/
1641 QuotaClient* QuotaClient::sInstance = nullptr;
1643 QuotaClient::QuotaClient() {
1644 AssertIsOnBackgroundThread();
1645 MOZ_ASSERT(!sInstance, "We expect this to be a singleton!");
1647 sInstance = this;
1650 QuotaClient::~QuotaClient() {
1651 AssertIsOnBackgroundThread();
1652 MOZ_ASSERT(sInstance == this, "We expect this to be a singleton!");
1654 sInstance = nullptr;
1657 mozilla::dom::quota::Client::Type QuotaClient::GetType() {
1658 return QuotaClient::SDB;
1661 Result<UsageInfo, nsresult> QuotaClient::InitOrigin(
1662 PersistenceType aPersistenceType, const OriginMetadata& aOriginMetadata,
1663 const AtomicBool& aCanceled) {
1664 AssertIsOnIOThread();
1666 return GetUsageForOrigin(aPersistenceType, aOriginMetadata, aCanceled);
1669 nsresult QuotaClient::InitOriginWithoutTracking(
1670 PersistenceType aPersistenceType, const OriginMetadata& aOriginMetadata,
1671 const AtomicBool& aCanceled) {
1672 AssertIsOnIOThread();
1674 return NS_OK;
1677 Result<UsageInfo, nsresult> QuotaClient::GetUsageForOrigin(
1678 PersistenceType aPersistenceType, const OriginMetadata& aOriginMetadata,
1679 const AtomicBool& aCanceled) {
1680 AssertIsOnIOThread();
1681 MOZ_ASSERT(aOriginMetadata.mPersistenceType == aPersistenceType);
1683 QuotaManager* quotaManager = QuotaManager::Get();
1684 MOZ_ASSERT(quotaManager);
1686 QM_TRY_UNWRAP(auto directory,
1687 quotaManager->GetOriginDirectory(aOriginMetadata));
1689 MOZ_ASSERT(directory);
1691 nsresult rv =
1692 directory->Append(NS_LITERAL_STRING_FROM_CSTRING(SDB_DIRECTORY_NAME));
1693 if (NS_WARN_IF(NS_FAILED(rv))) {
1694 return Err(rv);
1697 DebugOnly<bool> exists;
1698 MOZ_ASSERT(NS_SUCCEEDED(directory->Exists(&exists)) && exists);
1700 QM_TRY_RETURN(ReduceEachFileAtomicCancelable(
1701 *directory, aCanceled, UsageInfo{},
1702 [](UsageInfo usageInfo,
1703 const nsCOMPtr<nsIFile>& file) -> Result<UsageInfo, nsresult> {
1704 QM_TRY_INSPECT(const bool& isDirectory,
1705 MOZ_TO_RESULT_INVOKE_MEMBER(file, IsDirectory));
1707 if (isDirectory) {
1708 Unused << WARN_IF_FILE_IS_UNKNOWN(*file);
1709 return usageInfo;
1712 nsString leafName;
1713 QM_TRY(MOZ_TO_RESULT(file->GetLeafName(leafName)));
1715 if (StringEndsWith(leafName, kSDBSuffix)) {
1716 QM_TRY_INSPECT(const int64_t& fileSize,
1717 MOZ_TO_RESULT_INVOKE_MEMBER(file, GetFileSize));
1719 MOZ_ASSERT(fileSize >= 0);
1721 return usageInfo +
1722 UsageInfo{DatabaseUsageType(Some(uint64_t(fileSize)))};
1725 Unused << WARN_IF_FILE_IS_UNKNOWN(*file);
1727 return usageInfo;
1728 }));
1731 void QuotaClient::OnOriginClearCompleted(PersistenceType aPersistenceType,
1732 const nsACString& aOrigin) {
1733 AssertIsOnIOThread();
1736 void QuotaClient::OnRepositoryClearCompleted(PersistenceType aPersistenceType) {
1737 AssertIsOnIOThread();
1740 void QuotaClient::ReleaseIOThreadObjects() { AssertIsOnIOThread(); }
1742 void QuotaClient::AbortOperationsForLocks(
1743 const DirectoryLockIdTable& aDirectoryLockIds) {
1744 AssertIsOnBackgroundThread();
1746 AllowToCloseConnectionsMatching([&aDirectoryLockIds](const auto& connection) {
1747 // If the connections is registered in gOpenConnections then it must have
1748 // a directory lock.
1749 return IsLockForObjectContainedInLockTable(connection, aDirectoryLockIds);
1753 void QuotaClient::AbortOperationsForProcess(ContentParentId aContentParentId) {
1754 AssertIsOnBackgroundThread();
1757 void QuotaClient::AbortAllOperations() {
1758 AssertIsOnBackgroundThread();
1760 AllowToCloseConnectionsMatching([](const auto&) { return true; });
1763 void QuotaClient::StartIdleMaintenance() { AssertIsOnBackgroundThread(); }
1765 void QuotaClient::StopIdleMaintenance() { AssertIsOnBackgroundThread(); }
1767 void QuotaClient::InitiateShutdown() {
1768 AssertIsOnBackgroundThread();
1770 if (gOpenConnections) {
1771 for (const auto& connection : *gOpenConnections) {
1772 connection->AllowToClose();
1777 bool QuotaClient::IsShutdownCompleted() const { return !gOpenConnections; }
1779 void QuotaClient::ForceKillActors() {
1780 // Currently we don't implement killing actors (are there any to kill here?).
1783 nsCString QuotaClient::GetShutdownStatus() const {
1784 // XXX Gather information here.
1785 return "To be implemented"_ns;
1788 void QuotaClient::FinalizeShutdown() {
1789 // Nothing to do here.
1792 } // namespace mozilla::dom