Bug 1874684 - Part 4: Prefer const references instead of copying Instant values....
[gecko.git] / dom / simpledb / ActorsParent.cpp
blob7864cd31f4aecb1d94ca2e43c1ffadd3c507539f
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, override)
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 enum class State {
307 // Just created on the PBackground thread, dispatched to the main thread.
308 // Next step is FinishOpen.
309 Initial,
311 // Ensuring quota manager is created and opening directory on the
312 // PBackground thread. Next step is either SendingResults if quota manager
313 // is not available or DirectoryOpenPending if quota manager is available.
314 FinishOpen,
316 // Waiting for directory open allowed on the PBackground thread. The next
317 // step is either SendingResults if directory lock failed to acquire, or
318 // DatabaseWorkOpen if directory lock is acquired.
319 DirectoryOpenPending,
321 // Waiting to do/doing work on the QuotaManager IO thread. Its next step is
322 // SendingResults.
323 DatabaseWorkOpen,
325 // Waiting to send/sending results on the PBackground thread. Next step is
326 // Completed.
327 SendingResults,
329 // All done.
330 Completed
333 const SDBRequestOpenParams mParams;
334 RefPtr<DirectoryLock> mDirectoryLock;
335 nsCOMPtr<nsIFileRandomAccessStream> mFileRandomAccessStream;
336 // XXX Consider changing this to ClientMetadata.
337 quota::OriginMetadata mOriginMetadata;
338 State mState;
339 bool mFileRandomAccessStreamOpen;
341 public:
342 OpenOp(Connection* aConnection, const SDBRequestParams& aParams);
344 nsresult Dispatch() override;
346 private:
347 ~OpenOp() override;
349 nsresult Open();
351 nsresult FinishOpen();
353 nsresult SendToIOThread();
355 nsresult DatabaseWork();
357 void StreamClosedCallback();
359 // ConnectionOperationBase overrides
360 nsresult DoDatabaseWork(
361 nsIFileRandomAccessStream* aFileRandomAccessStream) override;
363 void GetResponse(SDBRequestResponse& aResponse) override;
365 void OnSuccess() override;
367 void Cleanup() override;
369 NS_IMETHOD
370 Run() override;
372 void DirectoryLockAcquired(DirectoryLock* aLock);
374 void DirectoryLockFailed();
377 class SeekOp final : public ConnectionOperationBase {
378 const SDBRequestSeekParams mParams;
380 public:
381 SeekOp(Connection* aConnection, const SDBRequestParams& aParams);
383 private:
384 ~SeekOp() override = default;
386 nsresult DoDatabaseWork(
387 nsIFileRandomAccessStream* aFileRandomAccessStream) override;
389 void GetResponse(SDBRequestResponse& aResponse) override;
392 class ReadOp final : public ConnectionOperationBase {
393 const SDBRequestReadParams mParams;
395 RefPtr<FixedBufferOutputStream> mOutputStream;
397 public:
398 ReadOp(Connection* aConnection, const SDBRequestParams& aParams);
400 bool Init() override;
402 private:
403 ~ReadOp() override = default;
405 nsresult DoDatabaseWork(
406 nsIFileRandomAccessStream* aFileRandomAccessStream) override;
408 void GetResponse(SDBRequestResponse& aResponse) override;
411 class WriteOp final : public ConnectionOperationBase {
412 const SDBRequestWriteParams mParams;
414 nsCOMPtr<nsIInputStream> mInputStream;
416 uint64_t mSize;
418 public:
419 WriteOp(Connection* aConnection, const SDBRequestParams& aParams);
421 bool Init() override;
423 private:
424 ~WriteOp() override = default;
426 nsresult DoDatabaseWork(
427 nsIFileRandomAccessStream* aFileRandomAccessStream) override;
429 void GetResponse(SDBRequestResponse& aResponse) override;
432 class CloseOp final : public ConnectionOperationBase {
433 public:
434 explicit CloseOp(Connection* aConnection);
436 private:
437 ~CloseOp() override = default;
439 nsresult DoDatabaseWork(
440 nsIFileRandomAccessStream* aFileRandomAccessStream) override;
442 void GetResponse(SDBRequestResponse& aResponse) override;
444 void OnSuccess() override;
447 /*******************************************************************************
448 * Other class declarations
449 ******************************************************************************/
451 class QuotaClient final : public mozilla::dom::quota::Client {
452 static QuotaClient* sInstance;
454 public:
455 QuotaClient();
457 NS_INLINE_DECL_THREADSAFE_REFCOUNTING(QuotaClient, override)
459 Type GetType() override;
461 Result<UsageInfo, nsresult> InitOrigin(PersistenceType aPersistenceType,
462 const OriginMetadata& aOriginMetadata,
463 const AtomicBool& aCanceled) override;
465 nsresult InitOriginWithoutTracking(PersistenceType aPersistenceType,
466 const OriginMetadata& aOriginMetadata,
467 const AtomicBool& aCanceled) override;
469 Result<UsageInfo, nsresult> GetUsageForOrigin(
470 PersistenceType aPersistenceType, const OriginMetadata& aOriginMetadata,
471 const AtomicBool& aCanceled) override;
473 void OnOriginClearCompleted(PersistenceType aPersistenceType,
474 const nsACString& aOrigin) override;
476 void OnRepositoryClearCompleted(PersistenceType aPersistenceType) override;
478 void ReleaseIOThreadObjects() override;
480 void AbortOperationsForLocks(
481 const DirectoryLockIdTable& aDirectoryLockIds) override;
483 void AbortOperationsForProcess(ContentParentId aContentParentId) override;
485 void AbortAllOperations() override;
487 void StartIdleMaintenance() override;
489 void StopIdleMaintenance() override;
491 private:
492 ~QuotaClient() override;
494 void InitiateShutdown() override;
495 bool IsShutdownCompleted() const override;
496 nsCString GetShutdownStatus() const override;
497 void ForceKillActors() override;
498 void FinalizeShutdown() override;
501 /*******************************************************************************
502 * Globals
503 ******************************************************************************/
505 using ConnectionArray = nsTArray<NotNull<RefPtr<Connection>>>;
507 StaticAutoPtr<ConnectionArray> gOpenConnections;
509 template <typename Condition>
510 void AllowToCloseConnectionsMatching(const Condition& aCondition) {
511 AssertIsOnBackgroundThread();
513 if (gOpenConnections) {
514 for (const auto& connection : *gOpenConnections) {
515 if (aCondition(*connection)) {
516 connection->AllowToClose();
522 } // namespace
524 /*******************************************************************************
525 * Exported functions
526 ******************************************************************************/
528 already_AddRefed<PBackgroundSDBConnectionParent>
529 AllocPBackgroundSDBConnectionParent(const PersistenceType& aPersistenceType,
530 const PrincipalInfo& aPrincipalInfo) {
531 AssertIsOnBackgroundThread();
533 if (NS_WARN_IF(QuotaClient::IsShuttingDownOnBackgroundThread())) {
534 return nullptr;
537 if (NS_WARN_IF(!IsValidPersistenceType(aPersistenceType))) {
538 MOZ_CRASH_UNLESS_FUZZING();
539 return nullptr;
542 if (NS_WARN_IF(aPrincipalInfo.type() == PrincipalInfo::TNullPrincipalInfo)) {
543 MOZ_CRASH_UNLESS_FUZZING();
544 return nullptr;
547 if (NS_WARN_IF(!QuotaManager::IsPrincipalInfoValid(aPrincipalInfo))) {
548 MOZ_CRASH_UNLESS_FUZZING();
549 return nullptr;
552 RefPtr<Connection> actor = new Connection(aPersistenceType, aPrincipalInfo);
554 return actor.forget();
557 bool RecvPBackgroundSDBConnectionConstructor(
558 PBackgroundSDBConnectionParent* aActor,
559 const PersistenceType& aPersistenceType,
560 const PrincipalInfo& aPrincipalInfo) {
561 AssertIsOnBackgroundThread();
562 MOZ_ASSERT(aActor);
563 MOZ_ASSERT(!QuotaClient::IsShuttingDownOnBackgroundThread());
565 return true;
568 namespace simpledb {
570 already_AddRefed<mozilla::dom::quota::Client> CreateQuotaClient() {
571 AssertIsOnBackgroundThread();
573 RefPtr<QuotaClient> client = new QuotaClient();
574 return client.forget();
577 } // namespace simpledb
579 /*******************************************************************************
580 * StreamHelper
581 ******************************************************************************/
583 StreamHelper::StreamHelper(nsIFileRandomAccessStream* aFileRandomAccessStream,
584 nsIRunnable* aCallback)
585 : Runnable("dom::StreamHelper"),
586 mOwningEventTarget(GetCurrentSerialEventTarget()),
587 mFileRandomAccessStream(aFileRandomAccessStream),
588 mCallback(aCallback) {
589 AssertIsOnBackgroundThread();
590 MOZ_ASSERT(aFileRandomAccessStream);
591 MOZ_ASSERT(aCallback);
594 StreamHelper::~StreamHelper() {
595 MOZ_ASSERT(!mFileRandomAccessStream);
596 MOZ_ASSERT(!mCallback);
599 void StreamHelper::AsyncClose() {
600 AssertIsOnBackgroundThread();
602 QuotaManager* quotaManager = QuotaManager::Get();
603 MOZ_ASSERT(quotaManager);
605 MOZ_ALWAYS_SUCCEEDS(
606 quotaManager->IOThread()->Dispatch(this, NS_DISPATCH_NORMAL));
609 void StreamHelper::RunOnBackgroundThread() {
610 AssertIsOnBackgroundThread();
612 nsCOMPtr<nsIFileRandomAccessStream> fileRandomAccessStream;
613 mFileRandomAccessStream.swap(fileRandomAccessStream);
615 nsCOMPtr<nsIRunnable> callback;
616 mCallback.swap(callback);
618 callback->Run();
621 void StreamHelper::RunOnIOThread() {
622 AssertIsOnIOThread();
623 MOZ_ASSERT(mFileRandomAccessStream);
625 nsCOMPtr<nsIInputStream> inputStream =
626 do_QueryInterface(mFileRandomAccessStream);
627 MOZ_ASSERT(inputStream);
629 nsresult rv = inputStream->Close();
630 Unused << NS_WARN_IF(NS_FAILED(rv));
632 MOZ_ALWAYS_SUCCEEDS(mOwningEventTarget->Dispatch(this, NS_DISPATCH_NORMAL));
635 NS_IMETHODIMP
636 StreamHelper::Run() {
637 MOZ_ASSERT(mCallback);
639 if (IsOnBackgroundThread()) {
640 RunOnBackgroundThread();
641 } else {
642 RunOnIOThread();
645 return NS_OK;
648 /*******************************************************************************
649 * Connection
650 ******************************************************************************/
652 Connection::Connection(PersistenceType aPersistenceType,
653 const PrincipalInfo& aPrincipalInfo)
654 : mPrincipalInfo(aPrincipalInfo),
655 mPersistenceType(aPersistenceType),
656 mRunningRequest(false),
657 mOpen(false),
658 mAllowedToClose(false),
659 mActorDestroyed(false) {
660 AssertIsOnBackgroundThread();
661 MOZ_ASSERT(!QuotaClient::IsShuttingDownOnBackgroundThread());
664 Connection::~Connection() {
665 MOZ_ASSERT(!mRunningRequest);
666 MOZ_ASSERT(!mOpen);
667 MOZ_ASSERT(mActorDestroyed);
670 void Connection::OnNewRequest() {
671 AssertIsOnBackgroundThread();
672 MOZ_ASSERT(!mRunningRequest);
674 mRunningRequest = true;
677 void Connection::OnRequestFinished() {
678 AssertIsOnBackgroundThread();
679 MOZ_ASSERT(mRunningRequest);
681 mRunningRequest = false;
683 MaybeCloseStream();
686 void Connection::OnOpen(
687 const nsACString& aOrigin, const nsAString& aName,
688 already_AddRefed<DirectoryLock> aDirectoryLock,
689 already_AddRefed<nsIFileRandomAccessStream> aFileRandomAccessStream) {
690 AssertIsOnBackgroundThread();
691 MOZ_ASSERT(!aOrigin.IsEmpty());
692 MOZ_ASSERT(!aName.IsEmpty());
693 MOZ_ASSERT(mOrigin.IsEmpty());
694 MOZ_ASSERT(mName.IsEmpty());
695 MOZ_ASSERT(!mDirectoryLock);
696 MOZ_ASSERT(!mFileRandomAccessStream);
697 MOZ_ASSERT(!mOpen);
699 mOrigin = aOrigin;
700 mName = aName;
701 mDirectoryLock = aDirectoryLock;
702 mFileRandomAccessStream = aFileRandomAccessStream;
703 mOpen = true;
705 if (!gOpenConnections) {
706 gOpenConnections = new ConnectionArray();
709 gOpenConnections->AppendElement(WrapNotNullUnchecked(this));
712 void Connection::OnClose() {
713 AssertIsOnBackgroundThread();
714 MOZ_ASSERT(!mOrigin.IsEmpty());
715 MOZ_ASSERT(mDirectoryLock);
716 MOZ_ASSERT(mFileRandomAccessStream);
717 MOZ_ASSERT(mOpen);
719 mOrigin.Truncate();
720 mName.Truncate();
721 mDirectoryLock = nullptr;
722 mFileRandomAccessStream = nullptr;
723 mOpen = false;
725 MOZ_ASSERT(gOpenConnections);
726 gOpenConnections->RemoveElement(this);
728 if (gOpenConnections->IsEmpty()) {
729 gOpenConnections = nullptr;
732 if (mAllowedToClose && !mActorDestroyed) {
733 Unused << SendClosed();
737 void Connection::AllowToClose() {
738 AssertIsOnBackgroundThread();
740 if (mAllowedToClose) {
741 return;
744 mAllowedToClose = true;
746 if (!mActorDestroyed) {
747 Unused << SendAllowToClose();
750 MaybeCloseStream();
753 void Connection::MaybeCloseStream() {
754 AssertIsOnBackgroundThread();
756 if (!mRunningRequest && mOpen && mAllowedToClose) {
757 nsCOMPtr<nsIRunnable> callback = NewRunnableMethod(
758 "dom::Connection::OnClose", this, &Connection::OnClose);
760 RefPtr<StreamHelper> helper =
761 new StreamHelper(mFileRandomAccessStream, callback);
762 helper->AsyncClose();
766 bool Connection::VerifyRequestParams(const SDBRequestParams& aParams) const {
767 AssertIsOnBackgroundThread();
768 MOZ_ASSERT(aParams.type() != SDBRequestParams::T__None);
770 switch (aParams.type()) {
771 case SDBRequestParams::TSDBRequestOpenParams: {
772 if (NS_WARN_IF(mOpen)) {
773 MOZ_CRASH_UNLESS_FUZZING();
774 return false;
777 break;
780 case SDBRequestParams::TSDBRequestSeekParams:
781 case SDBRequestParams::TSDBRequestReadParams:
782 case SDBRequestParams::TSDBRequestWriteParams:
783 case SDBRequestParams::TSDBRequestCloseParams: {
784 if (NS_WARN_IF(!mOpen)) {
785 MOZ_CRASH_UNLESS_FUZZING();
786 return false;
789 break;
792 default:
793 MOZ_CRASH("Should never get here!");
796 return true;
799 void Connection::ActorDestroy(ActorDestroyReason aWhy) {
800 AssertIsOnBackgroundThread();
801 MOZ_ASSERT(!mActorDestroyed);
803 mActorDestroyed = true;
805 AllowToClose();
808 mozilla::ipc::IPCResult Connection::RecvDeleteMe() {
809 AssertIsOnBackgroundThread();
810 MOZ_ASSERT(!mActorDestroyed);
812 IProtocol* mgr = Manager();
813 if (!PBackgroundSDBConnectionParent::Send__delete__(this)) {
814 return IPC_FAIL_NO_REASON(mgr);
817 return IPC_OK();
820 PBackgroundSDBRequestParent* Connection::AllocPBackgroundSDBRequestParent(
821 const SDBRequestParams& aParams) {
822 AssertIsOnBackgroundThread();
823 MOZ_ASSERT(aParams.type() != SDBRequestParams::T__None);
825 if (aParams.type() == SDBRequestParams::TSDBRequestOpenParams &&
826 NS_WARN_IF(QuotaClient::IsShuttingDownOnBackgroundThread())) {
827 return nullptr;
830 if (mAllowedToClose) {
831 return nullptr;
834 #ifdef DEBUG
835 // Always verify parameters in DEBUG builds!
836 bool trustParams = false;
837 #else
838 PBackgroundParent* backgroundActor = Manager();
839 MOZ_ASSERT(backgroundActor);
841 bool trustParams = !BackgroundParent::IsOtherProcessActor(backgroundActor);
842 #endif
844 if (NS_WARN_IF(!trustParams && !VerifyRequestParams(aParams))) {
845 MOZ_CRASH_UNLESS_FUZZING();
846 return nullptr;
849 if (NS_WARN_IF(mRunningRequest)) {
850 MOZ_CRASH_UNLESS_FUZZING();
851 return nullptr;
854 QM_TRY(QuotaManager::EnsureCreated(), nullptr);
856 RefPtr<ConnectionOperationBase> actor;
858 switch (aParams.type()) {
859 case SDBRequestParams::TSDBRequestOpenParams:
860 actor = new OpenOp(this, aParams);
861 break;
863 case SDBRequestParams::TSDBRequestSeekParams:
864 actor = new SeekOp(this, aParams);
865 break;
867 case SDBRequestParams::TSDBRequestReadParams:
868 actor = new ReadOp(this, aParams);
869 break;
871 case SDBRequestParams::TSDBRequestWriteParams:
872 actor = new WriteOp(this, aParams);
873 break;
875 case SDBRequestParams::TSDBRequestCloseParams:
876 actor = new CloseOp(this);
877 break;
879 default:
880 MOZ_CRASH("Should never get here!");
883 // Transfer ownership to IPDL.
884 return actor.forget().take();
887 mozilla::ipc::IPCResult Connection::RecvPBackgroundSDBRequestConstructor(
888 PBackgroundSDBRequestParent* aActor, const SDBRequestParams& aParams) {
889 AssertIsOnBackgroundThread();
890 MOZ_ASSERT(aActor);
891 MOZ_ASSERT(aParams.type() != SDBRequestParams::T__None);
892 MOZ_ASSERT_IF(aParams.type() == SDBRequestParams::TSDBRequestOpenParams,
893 !QuotaClient::IsShuttingDownOnBackgroundThread());
894 MOZ_ASSERT(!mAllowedToClose);
895 MOZ_ASSERT(!mRunningRequest);
897 auto* op = static_cast<ConnectionOperationBase*>(aActor);
899 if (NS_WARN_IF(!op->Init())) {
900 op->Cleanup();
901 return IPC_FAIL_NO_REASON(this);
904 if (NS_WARN_IF(NS_FAILED(op->Dispatch()))) {
905 op->Cleanup();
906 return IPC_FAIL_NO_REASON(this);
909 return IPC_OK();
912 bool Connection::DeallocPBackgroundSDBRequestParent(
913 PBackgroundSDBRequestParent* aActor) {
914 AssertIsOnBackgroundThread();
915 MOZ_ASSERT(aActor);
917 // Transfer ownership back from IPDL.
918 RefPtr<ConnectionOperationBase> actor =
919 dont_AddRef(static_cast<ConnectionOperationBase*>(aActor));
920 return true;
923 /*******************************************************************************
924 * ConnectionOperationBase
925 ******************************************************************************/
927 ConnectionOperationBase::~ConnectionOperationBase() {
928 MOZ_ASSERT(
929 !mConnection,
930 "ConnectionOperationBase::Cleanup() was not called by a subclass!");
931 MOZ_ASSERT(mActorDestroyed);
934 bool ConnectionOperationBase::Init() {
935 AssertIsOnBackgroundThread();
936 MOZ_ASSERT(mConnection);
938 mConnection->OnNewRequest();
940 return true;
943 nsresult ConnectionOperationBase::Dispatch() {
944 if (NS_WARN_IF(QuotaClient::IsShuttingDownOnBackgroundThread()) ||
945 IsActorDestroyed()) {
946 return NS_ERROR_ABORT;
949 QuotaManager* quotaManager = QuotaManager::Get();
950 MOZ_ASSERT(quotaManager);
952 nsresult rv = quotaManager->IOThread()->Dispatch(this, NS_DISPATCH_NORMAL);
953 if (NS_WARN_IF(NS_FAILED(rv))) {
954 return rv;
957 return NS_OK;
960 void ConnectionOperationBase::Cleanup() {
961 AssertIsOnOwningThread();
962 MOZ_ASSERT(mConnection);
964 mConnection->OnRequestFinished();
966 mConnection = nullptr;
969 void ConnectionOperationBase::SendResults() {
970 AssertIsOnOwningThread();
972 if (IsActorDestroyed()) {
973 MaybeSetFailureCode(NS_ERROR_FAILURE);
974 } else {
975 SDBRequestResponse response;
977 if (NS_SUCCEEDED(mResultCode)) {
978 GetResponse(response);
980 MOZ_ASSERT(response.type() != SDBRequestResponse::T__None);
981 MOZ_ASSERT(response.type() != SDBRequestResponse::Tnsresult);
982 } else {
983 response = mResultCode;
986 Unused << PBackgroundSDBRequestParent::Send__delete__(this, response);
988 if (NS_SUCCEEDED(mResultCode)) {
989 OnSuccess();
993 Cleanup();
996 void ConnectionOperationBase::DatabaseWork() {
997 AssertIsOnIOThread();
998 MOZ_ASSERT(NS_SUCCEEDED(mResultCode));
1000 if (!OperationMayProceed()) {
1001 // The operation was canceled in some way, likely because the child process
1002 // has crashed.
1003 mResultCode = NS_ERROR_ABORT;
1004 } else {
1005 nsIFileRandomAccessStream* fileRandomAccessStream =
1006 mConnection->GetFileRandomAccessStream();
1007 MOZ_ASSERT(fileRandomAccessStream);
1009 nsresult rv = DoDatabaseWork(fileRandomAccessStream);
1010 if (NS_FAILED(rv)) {
1011 mResultCode = rv;
1015 MOZ_ALWAYS_SUCCEEDS(mOwningEventTarget->Dispatch(this, NS_DISPATCH_NORMAL));
1018 void ConnectionOperationBase::OnSuccess() { AssertIsOnOwningThread(); }
1020 NS_IMETHODIMP
1021 ConnectionOperationBase::Run() {
1022 if (IsOnBackgroundThread()) {
1023 SendResults();
1024 } else {
1025 DatabaseWork();
1028 return NS_OK;
1031 void ConnectionOperationBase::ActorDestroy(ActorDestroyReason aWhy) {
1032 AssertIsOnBackgroundThread();
1034 mOperationMayProceed = false;
1035 mActorDestroyed = true;
1038 OpenOp::OpenOp(Connection* aConnection, const SDBRequestParams& aParams)
1039 : ConnectionOperationBase(aConnection),
1040 mParams(aParams.get_SDBRequestOpenParams()),
1041 mState(State::Initial),
1042 mFileRandomAccessStreamOpen(false) {
1043 MOZ_ASSERT(aParams.type() == SDBRequestParams::TSDBRequestOpenParams);
1046 OpenOp::~OpenOp() {
1047 MOZ_ASSERT(!mDirectoryLock);
1048 MOZ_ASSERT(!mFileRandomAccessStream);
1049 MOZ_ASSERT(!mFileRandomAccessStreamOpen);
1050 MOZ_ASSERT_IF(OperationMayProceed(),
1051 mState == State::Initial || mState == State::Completed);
1054 nsresult OpenOp::Dispatch() {
1055 MOZ_ALWAYS_SUCCEEDS(NS_DispatchToMainThread(this));
1057 return NS_OK;
1060 nsresult OpenOp::Open() {
1061 MOZ_ASSERT(NS_IsMainThread());
1062 MOZ_ASSERT(mState == State::Initial);
1064 if (NS_WARN_IF(QuotaClient::IsShuttingDownOnNonBackgroundThread()) ||
1065 !OperationMayProceed()) {
1066 return NS_ERROR_ABORT;
1069 if (NS_WARN_IF(!Preferences::GetBool(kPrefSimpleDBEnabled, false))) {
1070 return NS_ERROR_UNEXPECTED;
1073 mState = State::FinishOpen;
1074 MOZ_ALWAYS_SUCCEEDS(OwningEventTarget()->Dispatch(this, NS_DISPATCH_NORMAL));
1076 return NS_OK;
1079 nsresult OpenOp::FinishOpen() {
1080 AssertIsOnOwningThread();
1081 MOZ_ASSERT(mOriginMetadata.mOrigin.IsEmpty());
1082 MOZ_ASSERT(!mDirectoryLock);
1083 MOZ_ASSERT(mState == State::FinishOpen);
1085 if (NS_WARN_IF(QuotaClient::IsShuttingDownOnBackgroundThread()) ||
1086 IsActorDestroyed()) {
1087 return NS_ERROR_ABORT;
1090 QuotaManager* quotaManager = QuotaManager::Get();
1091 MOZ_ASSERT(quotaManager);
1093 const PrincipalInfo& principalInfo = GetConnection()->GetPrincipalInfo();
1095 PersistenceType persistenceType = GetConnection()->GetPersistenceType();
1097 if (principalInfo.type() == PrincipalInfo::TSystemPrincipalInfo) {
1098 mOriginMetadata = {QuotaManager::GetInfoForChrome(), persistenceType};
1099 } else {
1100 MOZ_ASSERT(principalInfo.type() == PrincipalInfo::TContentPrincipalInfo);
1102 QM_TRY_UNWRAP(
1103 auto principalMetadata,
1104 quotaManager->GetInfoFromValidatedPrincipalInfo(principalInfo));
1106 mOriginMetadata = {std::move(principalMetadata), persistenceType};
1109 if (gOpenConnections) {
1110 for (const auto& connection : *gOpenConnections) {
1111 if (connection->Origin() == mOriginMetadata.mOrigin &&
1112 connection->Name() == mParams.name()) {
1113 return NS_ERROR_STORAGE_BUSY;
1118 // Open the directory
1120 mState = State::DirectoryOpenPending;
1122 quotaManager
1123 ->OpenClientDirectory({mOriginMetadata, mozilla::dom::quota::Client::SDB})
1124 ->Then(
1125 GetCurrentSerialEventTarget(), __func__,
1126 [self = RefPtr(this)](
1127 const ClientDirectoryLockPromise::ResolveOrRejectValue& aValue) {
1128 if (aValue.IsResolve()) {
1129 self->DirectoryLockAcquired(aValue.ResolveValue());
1130 } else {
1131 self->DirectoryLockFailed();
1135 return NS_OK;
1138 nsresult OpenOp::SendToIOThread() {
1139 AssertIsOnOwningThread();
1140 MOZ_ASSERT(mState == State::DirectoryOpenPending);
1142 if (NS_WARN_IF(QuotaClient::IsShuttingDownOnBackgroundThread()) ||
1143 IsActorDestroyed()) {
1144 return NS_ERROR_ABORT;
1147 mFileRandomAccessStream = new FileRandomAccessStream(
1148 GetConnection()->GetPersistenceType(), mOriginMetadata,
1149 mozilla::dom::quota::Client::SDB);
1151 QuotaManager* quotaManager = QuotaManager::Get();
1152 MOZ_ASSERT(quotaManager);
1154 // Must set this before dispatching otherwise we will race with the IO thread.
1155 mState = State::DatabaseWorkOpen;
1157 nsresult rv = quotaManager->IOThread()->Dispatch(this, NS_DISPATCH_NORMAL);
1158 if (NS_WARN_IF(NS_FAILED(rv))) {
1159 return rv;
1162 return NS_OK;
1165 nsresult OpenOp::DatabaseWork() {
1166 AssertIsOnIOThread();
1167 MOZ_ASSERT(mState == State::DatabaseWorkOpen);
1168 MOZ_ASSERT(mFileRandomAccessStream);
1169 MOZ_ASSERT(!mFileRandomAccessStreamOpen);
1171 if (NS_WARN_IF(QuotaClient::IsShuttingDownOnNonBackgroundThread()) ||
1172 !OperationMayProceed()) {
1173 return NS_ERROR_ABORT;
1176 QuotaManager* quotaManager = QuotaManager::Get();
1177 MOZ_ASSERT(quotaManager);
1179 QM_TRY_INSPECT(
1180 const auto& dbDirectory,
1181 ([persistenceType = GetConnection()->GetPersistenceType(), &quotaManager,
1182 this]()
1183 -> mozilla::Result<std::pair<nsCOMPtr<nsIFile>, bool>, nsresult> {
1184 if (persistenceType == PERSISTENCE_TYPE_PERSISTENT) {
1185 QM_TRY_RETURN(quotaManager->EnsurePersistentOriginIsInitialized(
1186 mOriginMetadata));
1189 QM_TRY(MOZ_TO_RESULT(
1190 quotaManager->EnsureTemporaryStorageIsInitializedInternal()));
1191 QM_TRY_RETURN(quotaManager->EnsureTemporaryOriginIsInitialized(
1192 persistenceType, mOriginMetadata));
1194 .map([](const auto& res) { return res.first; })));
1196 nsresult rv =
1197 dbDirectory->Append(NS_LITERAL_STRING_FROM_CSTRING(SDB_DIRECTORY_NAME));
1198 if (NS_WARN_IF(NS_FAILED(rv))) {
1199 return rv;
1202 bool exists;
1203 rv = dbDirectory->Exists(&exists);
1204 if (NS_WARN_IF(NS_FAILED(rv))) {
1205 return rv;
1208 if (!exists) {
1209 rv = dbDirectory->Create(nsIFile::DIRECTORY_TYPE, 0755);
1210 if (NS_WARN_IF(NS_FAILED(rv))) {
1211 return rv;
1214 #ifdef DEBUG
1215 else {
1216 bool isDirectory;
1217 MOZ_ASSERT(NS_SUCCEEDED(dbDirectory->IsDirectory(&isDirectory)));
1218 MOZ_ASSERT(isDirectory);
1220 #endif
1222 nsCOMPtr<nsIFile> dbFile;
1223 rv = dbDirectory->Clone(getter_AddRefs(dbFile));
1224 if (NS_WARN_IF(NS_FAILED(rv))) {
1225 return rv;
1228 rv = dbFile->Append(mParams.name() + kSDBSuffix);
1229 if (NS_WARN_IF(NS_FAILED(rv))) {
1230 return rv;
1233 nsString databaseFilePath;
1234 rv = dbFile->GetPath(databaseFilePath);
1235 if (NS_WARN_IF(NS_FAILED(rv))) {
1236 return rv;
1239 rv = mFileRandomAccessStream->Init(dbFile, PR_RDWR | PR_CREATE_FILE, 0644, 0);
1240 if (NS_WARN_IF(NS_FAILED(rv))) {
1241 return rv;
1244 mFileRandomAccessStreamOpen = true;
1246 rv = DoDatabaseWork(mFileRandomAccessStream);
1247 if (NS_WARN_IF(NS_FAILED(rv))) {
1248 return rv;
1251 // Must set mState before dispatching otherwise we will race with the owning
1252 // thread.
1253 mState = State::SendingResults;
1255 rv = OwningEventTarget()->Dispatch(this, NS_DISPATCH_NORMAL);
1256 if (NS_WARN_IF(NS_FAILED(rv))) {
1257 return rv;
1260 return NS_OK;
1263 void OpenOp::StreamClosedCallback() {
1264 AssertIsOnOwningThread();
1265 MOZ_ASSERT(NS_FAILED(ResultCode()));
1266 MOZ_ASSERT(mDirectoryLock);
1267 MOZ_ASSERT(mFileRandomAccessStream);
1268 MOZ_ASSERT(mFileRandomAccessStreamOpen);
1270 mDirectoryLock = nullptr;
1271 mFileRandomAccessStream = nullptr;
1272 mFileRandomAccessStreamOpen = false;
1275 nsresult OpenOp::DoDatabaseWork(
1276 nsIFileRandomAccessStream* aFileRandomAccessStream) {
1277 AssertIsOnIOThread();
1279 return NS_OK;
1282 void OpenOp::GetResponse(SDBRequestResponse& aResponse) {
1283 AssertIsOnOwningThread();
1285 aResponse = SDBRequestOpenResponse();
1288 void OpenOp::OnSuccess() {
1289 AssertIsOnOwningThread();
1290 MOZ_ASSERT(NS_SUCCEEDED(ResultCode()));
1291 MOZ_ASSERT(!mOriginMetadata.mOrigin.IsEmpty());
1292 MOZ_ASSERT(mDirectoryLock);
1293 MOZ_ASSERT(mFileRandomAccessStream);
1294 MOZ_ASSERT(mFileRandomAccessStreamOpen);
1296 RefPtr<DirectoryLock> directoryLock;
1297 nsCOMPtr<nsIFileRandomAccessStream> fileRandomAccessStream;
1299 mDirectoryLock.swap(directoryLock);
1300 mFileRandomAccessStream.swap(fileRandomAccessStream);
1301 mFileRandomAccessStreamOpen = false;
1303 GetConnection()->OnOpen(mOriginMetadata.mOrigin, mParams.name(),
1304 directoryLock.forget(),
1305 fileRandomAccessStream.forget());
1308 void OpenOp::Cleanup() {
1309 AssertIsOnOwningThread();
1310 MOZ_ASSERT_IF(mFileRandomAccessStreamOpen, mFileRandomAccessStream);
1312 if (mFileRandomAccessStream && mFileRandomAccessStreamOpen) {
1313 // If we have an initialized file stream then the operation must have failed
1314 // and there must be a directory lock too.
1315 MOZ_ASSERT(NS_FAILED(ResultCode()));
1316 MOZ_ASSERT(mDirectoryLock);
1318 // We must close the stream on the I/O thread before releasing it on this
1319 // thread. The directory lock can't be released either.
1320 nsCOMPtr<nsIRunnable> callback =
1321 NewRunnableMethod("dom::OpenOp::StreamClosedCallback", this,
1322 &OpenOp::StreamClosedCallback);
1324 RefPtr<StreamHelper> helper =
1325 new StreamHelper(mFileRandomAccessStream, callback);
1326 helper->AsyncClose();
1327 } else {
1328 MOZ_ASSERT(!mFileRandomAccessStreamOpen);
1330 mDirectoryLock = nullptr;
1331 mFileRandomAccessStream = nullptr;
1334 ConnectionOperationBase::Cleanup();
1337 NS_IMETHODIMP
1338 OpenOp::Run() {
1339 nsresult rv;
1341 switch (mState) {
1342 case State::Initial:
1343 rv = Open();
1344 break;
1346 case State::FinishOpen:
1347 rv = FinishOpen();
1348 break;
1350 case State::DatabaseWorkOpen:
1351 rv = DatabaseWork();
1352 break;
1354 case State::SendingResults:
1355 SendResults();
1356 return NS_OK;
1358 default:
1359 MOZ_CRASH("Bad state!");
1362 if (NS_WARN_IF(NS_FAILED(rv)) && mState != State::SendingResults) {
1363 MaybeSetFailureCode(rv);
1365 // Must set mState before dispatching otherwise we will race with the owning
1366 // thread.
1367 mState = State::SendingResults;
1369 if (IsOnOwningThread()) {
1370 SendResults();
1371 } else {
1372 MOZ_ALWAYS_SUCCEEDS(
1373 OwningEventTarget()->Dispatch(this, NS_DISPATCH_NORMAL));
1377 return NS_OK;
1380 void OpenOp::DirectoryLockAcquired(DirectoryLock* aLock) {
1381 AssertIsOnOwningThread();
1382 MOZ_ASSERT(mState == State::DirectoryOpenPending);
1383 MOZ_ASSERT(!mDirectoryLock);
1385 mDirectoryLock = aLock;
1387 nsresult rv = SendToIOThread();
1388 if (NS_WARN_IF(NS_FAILED(rv))) {
1389 MaybeSetFailureCode(rv);
1391 // The caller holds a strong reference to us, no need for a self reference
1392 // before calling Run().
1394 mState = State::SendingResults;
1395 MOZ_ALWAYS_SUCCEEDS(Run());
1397 return;
1401 void OpenOp::DirectoryLockFailed() {
1402 AssertIsOnOwningThread();
1403 MOZ_ASSERT(mState == State::DirectoryOpenPending);
1404 MOZ_ASSERT(!mDirectoryLock);
1406 MaybeSetFailureCode(NS_ERROR_FAILURE);
1408 // The caller holds a strong reference to us, no need for a self reference
1409 // before calling Run().
1411 mState = State::SendingResults;
1412 MOZ_ALWAYS_SUCCEEDS(Run());
1415 SeekOp::SeekOp(Connection* aConnection, const SDBRequestParams& aParams)
1416 : ConnectionOperationBase(aConnection),
1417 mParams(aParams.get_SDBRequestSeekParams()) {
1418 MOZ_ASSERT(aParams.type() == SDBRequestParams::TSDBRequestSeekParams);
1421 nsresult SeekOp::DoDatabaseWork(
1422 nsIFileRandomAccessStream* aFileRandomAccessStream) {
1423 AssertIsOnIOThread();
1424 MOZ_ASSERT(aFileRandomAccessStream);
1426 nsresult rv = aFileRandomAccessStream->Seek(nsISeekableStream::NS_SEEK_SET,
1427 mParams.offset());
1429 if (NS_WARN_IF(NS_FAILED(rv))) {
1430 return rv;
1433 return NS_OK;
1436 void SeekOp::GetResponse(SDBRequestResponse& aResponse) {
1437 aResponse = SDBRequestSeekResponse();
1440 ReadOp::ReadOp(Connection* aConnection, const SDBRequestParams& aParams)
1441 : ConnectionOperationBase(aConnection),
1442 mParams(aParams.get_SDBRequestReadParams()) {
1443 MOZ_ASSERT(aParams.type() == SDBRequestParams::TSDBRequestReadParams);
1446 bool ReadOp::Init() {
1447 AssertIsOnOwningThread();
1449 if (NS_WARN_IF(!ConnectionOperationBase::Init())) {
1450 return false;
1453 if (NS_WARN_IF(mParams.size() > std::numeric_limits<std::size_t>::max())) {
1454 return false;
1457 mOutputStream = FixedBufferOutputStream::Create(mParams.size(), fallible);
1458 if (NS_WARN_IF(!mOutputStream)) {
1459 return false;
1462 return true;
1465 nsresult ReadOp::DoDatabaseWork(
1466 nsIFileRandomAccessStream* aFileRandomAccessStream) {
1467 AssertIsOnIOThread();
1468 MOZ_ASSERT(aFileRandomAccessStream);
1470 nsCOMPtr<nsIInputStream> inputStream =
1471 do_QueryInterface(aFileRandomAccessStream);
1472 MOZ_ASSERT(inputStream);
1474 nsresult rv;
1476 uint64_t offset = 0;
1478 do {
1479 char copyBuffer[kCopyBufferSize];
1481 uint64_t max = mParams.size() - offset;
1482 if (max == 0) {
1483 break;
1486 uint32_t count = sizeof(copyBuffer);
1487 if (count > max) {
1488 count = max;
1491 uint32_t numRead;
1492 rv = inputStream->Read(copyBuffer, count, &numRead);
1493 if (NS_WARN_IF(NS_FAILED(rv))) {
1494 return rv;
1497 if (!numRead) {
1498 break;
1501 uint32_t numWrite;
1502 rv = mOutputStream->Write(copyBuffer, numRead, &numWrite);
1503 if (NS_WARN_IF(NS_FAILED(rv))) {
1504 return rv;
1507 if (NS_WARN_IF(numWrite != numRead)) {
1508 return NS_ERROR_FAILURE;
1511 offset += numWrite;
1512 } while (true);
1514 MOZ_ASSERT(offset == mParams.size());
1516 MOZ_ALWAYS_SUCCEEDS(mOutputStream->Close());
1518 return NS_OK;
1521 void ReadOp::GetResponse(SDBRequestResponse& aResponse) {
1522 aResponse = SDBRequestReadResponse(nsCString(mOutputStream->WrittenData()));
1525 WriteOp::WriteOp(Connection* aConnection, const SDBRequestParams& aParams)
1526 : ConnectionOperationBase(aConnection),
1527 mParams(aParams.get_SDBRequestWriteParams()),
1528 mSize(0) {
1529 MOZ_ASSERT(aParams.type() == SDBRequestParams::TSDBRequestWriteParams);
1532 bool WriteOp::Init() {
1533 AssertIsOnOwningThread();
1535 if (NS_WARN_IF(!ConnectionOperationBase::Init())) {
1536 return false;
1539 const nsCString& string = mParams.data();
1541 nsCOMPtr<nsIInputStream> inputStream;
1542 nsresult rv = NS_NewCStringInputStream(getter_AddRefs(inputStream), string);
1543 if (NS_WARN_IF(NS_FAILED(rv))) {
1544 return false;
1547 mInputStream = std::move(inputStream);
1548 mSize = string.Length();
1550 return true;
1553 nsresult WriteOp::DoDatabaseWork(
1554 nsIFileRandomAccessStream* aFileRandomAccessStream) {
1555 AssertIsOnIOThread();
1556 MOZ_ASSERT(aFileRandomAccessStream);
1558 nsCOMPtr<nsIOutputStream> outputStream =
1559 do_QueryInterface(aFileRandomAccessStream);
1560 MOZ_ASSERT(outputStream);
1562 nsresult rv;
1564 do {
1565 char copyBuffer[kCopyBufferSize];
1567 uint32_t numRead;
1568 rv = mInputStream->Read(copyBuffer, sizeof(copyBuffer), &numRead);
1569 if (NS_WARN_IF(NS_FAILED(rv))) {
1570 break;
1573 if (!numRead) {
1574 break;
1577 uint32_t numWrite;
1578 rv = outputStream->Write(copyBuffer, numRead, &numWrite);
1579 if (NS_WARN_IF(NS_FAILED(rv))) {
1580 return rv;
1583 if (NS_WARN_IF(numWrite != numRead)) {
1584 return NS_ERROR_FAILURE;
1586 } while (true);
1588 MOZ_ALWAYS_SUCCEEDS(mInputStream->Close());
1590 return NS_OK;
1593 void WriteOp::GetResponse(SDBRequestResponse& aResponse) {
1594 aResponse = SDBRequestWriteResponse();
1597 CloseOp::CloseOp(Connection* aConnection)
1598 : ConnectionOperationBase(aConnection) {}
1600 nsresult CloseOp::DoDatabaseWork(
1601 nsIFileRandomAccessStream* aFileRandomAccessStream) {
1602 AssertIsOnIOThread();
1603 MOZ_ASSERT(aFileRandomAccessStream);
1605 nsCOMPtr<nsIInputStream> inputStream =
1606 do_QueryInterface(aFileRandomAccessStream);
1607 MOZ_ASSERT(inputStream);
1609 nsresult rv = inputStream->Close();
1610 if (NS_WARN_IF(NS_FAILED(rv))) {
1611 return rv;
1614 return NS_OK;
1617 void CloseOp::GetResponse(SDBRequestResponse& aResponse) {
1618 aResponse = SDBRequestCloseResponse();
1621 void CloseOp::OnSuccess() {
1622 AssertIsOnOwningThread();
1624 GetConnection()->OnClose();
1627 /*******************************************************************************
1628 * QuotaClient
1629 ******************************************************************************/
1631 QuotaClient* QuotaClient::sInstance = nullptr;
1633 QuotaClient::QuotaClient() {
1634 AssertIsOnBackgroundThread();
1635 MOZ_ASSERT(!sInstance, "We expect this to be a singleton!");
1637 sInstance = this;
1640 QuotaClient::~QuotaClient() {
1641 AssertIsOnBackgroundThread();
1642 MOZ_ASSERT(sInstance == this, "We expect this to be a singleton!");
1644 sInstance = nullptr;
1647 mozilla::dom::quota::Client::Type QuotaClient::GetType() {
1648 return QuotaClient::SDB;
1651 Result<UsageInfo, nsresult> QuotaClient::InitOrigin(
1652 PersistenceType aPersistenceType, const OriginMetadata& aOriginMetadata,
1653 const AtomicBool& aCanceled) {
1654 AssertIsOnIOThread();
1656 return GetUsageForOrigin(aPersistenceType, aOriginMetadata, aCanceled);
1659 nsresult QuotaClient::InitOriginWithoutTracking(
1660 PersistenceType aPersistenceType, const OriginMetadata& aOriginMetadata,
1661 const AtomicBool& aCanceled) {
1662 AssertIsOnIOThread();
1664 return NS_OK;
1667 Result<UsageInfo, nsresult> QuotaClient::GetUsageForOrigin(
1668 PersistenceType aPersistenceType, const OriginMetadata& aOriginMetadata,
1669 const AtomicBool& aCanceled) {
1670 AssertIsOnIOThread();
1671 MOZ_ASSERT(aOriginMetadata.mPersistenceType == aPersistenceType);
1673 QuotaManager* quotaManager = QuotaManager::Get();
1674 MOZ_ASSERT(quotaManager);
1676 QM_TRY_UNWRAP(auto directory,
1677 quotaManager->GetOriginDirectory(aOriginMetadata));
1679 MOZ_ASSERT(directory);
1681 nsresult rv =
1682 directory->Append(NS_LITERAL_STRING_FROM_CSTRING(SDB_DIRECTORY_NAME));
1683 if (NS_WARN_IF(NS_FAILED(rv))) {
1684 return Err(rv);
1687 DebugOnly<bool> exists;
1688 MOZ_ASSERT(NS_SUCCEEDED(directory->Exists(&exists)) && exists);
1690 QM_TRY_RETURN(ReduceEachFileAtomicCancelable(
1691 *directory, aCanceled, UsageInfo{},
1692 [](UsageInfo usageInfo,
1693 const nsCOMPtr<nsIFile>& file) -> Result<UsageInfo, nsresult> {
1694 QM_TRY_INSPECT(const bool& isDirectory,
1695 MOZ_TO_RESULT_INVOKE_MEMBER(file, IsDirectory));
1697 if (isDirectory) {
1698 Unused << WARN_IF_FILE_IS_UNKNOWN(*file);
1699 return usageInfo;
1702 nsString leafName;
1703 QM_TRY(MOZ_TO_RESULT(file->GetLeafName(leafName)));
1705 if (StringEndsWith(leafName, kSDBSuffix)) {
1706 QM_TRY_INSPECT(const int64_t& fileSize,
1707 MOZ_TO_RESULT_INVOKE_MEMBER(file, GetFileSize));
1709 MOZ_ASSERT(fileSize >= 0);
1711 return usageInfo +
1712 UsageInfo{DatabaseUsageType(Some(uint64_t(fileSize)))};
1715 Unused << WARN_IF_FILE_IS_UNKNOWN(*file);
1717 return usageInfo;
1718 }));
1721 void QuotaClient::OnOriginClearCompleted(PersistenceType aPersistenceType,
1722 const nsACString& aOrigin) {
1723 AssertIsOnIOThread();
1726 void QuotaClient::OnRepositoryClearCompleted(PersistenceType aPersistenceType) {
1727 AssertIsOnIOThread();
1730 void QuotaClient::ReleaseIOThreadObjects() { AssertIsOnIOThread(); }
1732 void QuotaClient::AbortOperationsForLocks(
1733 const DirectoryLockIdTable& aDirectoryLockIds) {
1734 AssertIsOnBackgroundThread();
1736 AllowToCloseConnectionsMatching([&aDirectoryLockIds](const auto& connection) {
1737 // If the connections is registered in gOpenConnections then it must have
1738 // a directory lock.
1739 return IsLockForObjectContainedInLockTable(connection, aDirectoryLockIds);
1743 void QuotaClient::AbortOperationsForProcess(ContentParentId aContentParentId) {
1744 AssertIsOnBackgroundThread();
1747 void QuotaClient::AbortAllOperations() {
1748 AssertIsOnBackgroundThread();
1750 AllowToCloseConnectionsMatching([](const auto&) { return true; });
1753 void QuotaClient::StartIdleMaintenance() { AssertIsOnBackgroundThread(); }
1755 void QuotaClient::StopIdleMaintenance() { AssertIsOnBackgroundThread(); }
1757 void QuotaClient::InitiateShutdown() {
1758 AssertIsOnBackgroundThread();
1760 if (gOpenConnections) {
1761 for (const auto& connection : *gOpenConnections) {
1762 connection->AllowToClose();
1767 bool QuotaClient::IsShutdownCompleted() const { return !gOpenConnections; }
1769 void QuotaClient::ForceKillActors() {
1770 // Currently we don't implement killing actors (are there any to kill here?).
1773 nsCString QuotaClient::GetShutdownStatus() const {
1774 // XXX Gather information here.
1775 return "To be implemented"_ns;
1778 void QuotaClient::FinalizeShutdown() {
1779 // Nothing to do here.
1782 } // namespace mozilla::dom