Bug 1733673 [wpt PR 31066] - Annotate CSS Transforms WPT reftests as fuzzy where...
[gecko.git] / dom / simpledb / ActorsParent.cpp
blob56e45949557713f28d5269cbf889a6ff2fabdd77
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/Maybe.h"
24 #include "mozilla/Preferences.h"
25 #include "mozilla/RefPtr.h"
26 #include "mozilla/Result.h"
27 #include "mozilla/ResultExtensions.h"
28 #include "mozilla/SpinEventLoopUntil.h"
29 #include "mozilla/StaticPtr.h"
30 #include "mozilla/Unused.h"
31 #include "mozilla/Variant.h"
32 #include "mozilla/dom/PBackgroundSDBConnection.h"
33 #include "mozilla/dom/PBackgroundSDBConnectionParent.h"
34 #include "mozilla/dom/PBackgroundSDBRequestParent.h"
35 #include "mozilla/dom/ipc/IdType.h"
36 #include "mozilla/dom/quota/Client.h"
37 #include "mozilla/dom/quota/ClientImpl.h"
38 #include "mozilla/dom/quota/DirectoryLock.h"
39 #include "mozilla/dom/quota/FileStreams.h"
40 #include "mozilla/dom/quota/MemoryOutputStream.h"
41 #include "mozilla/dom/quota/QuotaCommon.h"
42 #include "mozilla/dom/quota/QuotaManager.h"
43 #include "mozilla/dom/quota/UsageInfo.h"
44 #include "mozilla/ipc/BackgroundParent.h"
45 #include "mozilla/ipc/BackgroundUtils.h"
46 #include "mozilla/ipc/PBackgroundParent.h"
47 #include "mozilla/ipc/PBackgroundSharedTypes.h"
48 #include "mozilla/ipc/ProtocolUtils.h"
49 #include "nsCOMPtr.h"
50 #include "nsDebug.h"
51 #include "nsError.h"
52 #include "nsIDirectoryEnumerator.h"
53 #include "nsIEventTarget.h"
54 #include "nsIFile.h"
55 #include "nsIFileStreams.h"
56 #include "nsIInputStream.h"
57 #include "nsIOutputStream.h"
58 #include "nsIRunnable.h"
59 #include "nsISeekableStream.h"
60 #include "nsISupports.h"
61 #include "nsIThread.h"
62 #include "nsLiteralString.h"
63 #include "nsString.h"
64 #include "nsStringFwd.h"
65 #include "nsStringStream.h"
66 #include "nsTArray.h"
67 #include "nsTLiteralString.h"
68 #include "nsTStringRepr.h"
69 #include "nsThreadUtils.h"
70 #include "nscore.h"
71 #include "prio.h"
73 #define DISABLE_ASSERTS_FOR_FUZZING 0
75 #if DISABLE_ASSERTS_FOR_FUZZING
76 # define ASSERT_UNLESS_FUZZING(...) \
77 do { \
78 } while (0)
79 #else
80 # define ASSERT_UNLESS_FUZZING(...) MOZ_ASSERT(false, __VA_ARGS__)
81 #endif
83 namespace mozilla::dom {
85 using namespace mozilla::dom::quota;
86 using namespace mozilla::ipc;
88 namespace {
90 /*******************************************************************************
91 * Constants
92 ******************************************************************************/
94 const uint32_t kCopyBufferSize = 32768;
96 constexpr auto kSDBSuffix = u".sdb"_ns;
98 /*******************************************************************************
99 * Actor class declarations
100 ******************************************************************************/
102 class StreamHelper final : public Runnable {
103 nsCOMPtr<nsIEventTarget> mOwningEventTarget;
104 nsCOMPtr<nsIFileStream> mFileStream;
105 nsCOMPtr<nsIRunnable> mCallback;
107 public:
108 StreamHelper(nsIFileStream* aFileStream, nsIRunnable* aCallback);
110 void AsyncClose();
112 private:
113 ~StreamHelper() override;
115 void RunOnBackgroundThread();
117 void RunOnIOThread();
119 NS_DECL_NSIRUNNABLE
122 class Connection final : public PBackgroundSDBConnectionParent {
123 RefPtr<DirectoryLock> mDirectoryLock;
124 nsCOMPtr<nsIFileStream> mFileStream;
125 const PrincipalInfo mPrincipalInfo;
126 nsCString mOrigin;
127 nsString mName;
129 PersistenceType mPersistenceType;
130 bool mRunningRequest;
131 bool mOpen;
132 bool mAllowedToClose;
133 bool mActorDestroyed;
135 public:
136 Connection(PersistenceType aPersistenceType,
137 const PrincipalInfo& aPrincipalInfo);
139 NS_INLINE_DECL_THREADSAFE_REFCOUNTING(mozilla::dom::Connection)
141 Maybe<DirectoryLock&> MaybeDirectoryLockRef() const {
142 AssertIsOnBackgroundThread();
144 return ToMaybeRef(mDirectoryLock.get());
147 nsIFileStream* GetFileStream() const {
148 AssertIsOnIOThread();
150 return mFileStream;
153 PersistenceType GetPersistenceType() const { return mPersistenceType; }
155 const PrincipalInfo& GetPrincipalInfo() const {
156 MOZ_ASSERT(NS_IsMainThread());
158 return mPrincipalInfo;
161 const nsCString& Origin() const {
162 AssertIsOnBackgroundThread();
163 MOZ_ASSERT(!mOrigin.IsEmpty());
165 return mOrigin;
168 const nsString& Name() const {
169 AssertIsOnBackgroundThread();
170 MOZ_ASSERT(!mName.IsEmpty());
172 return mName;
175 void OnNewRequest();
177 void OnRequestFinished();
179 void OnOpen(const nsACString& aOrigin, const nsAString& aName,
180 already_AddRefed<DirectoryLock> aDirectoryLock,
181 already_AddRefed<nsIFileStream> aFileStream);
183 void OnClose();
185 void AllowToClose();
187 private:
188 ~Connection();
190 void MaybeCloseStream();
192 bool VerifyRequestParams(const SDBRequestParams& aParams) const;
194 // IPDL methods.
195 virtual void ActorDestroy(ActorDestroyReason aWhy) override;
197 mozilla::ipc::IPCResult RecvDeleteMe() override;
199 virtual PBackgroundSDBRequestParent* AllocPBackgroundSDBRequestParent(
200 const SDBRequestParams& aParams) override;
202 virtual mozilla::ipc::IPCResult RecvPBackgroundSDBRequestConstructor(
203 PBackgroundSDBRequestParent* aActor,
204 const SDBRequestParams& aParams) override;
206 virtual bool DeallocPBackgroundSDBRequestParent(
207 PBackgroundSDBRequestParent* aActor) override;
210 class ConnectionOperationBase : public Runnable,
211 public PBackgroundSDBRequestParent {
212 nsCOMPtr<nsIEventTarget> mOwningEventTarget;
213 RefPtr<Connection> mConnection;
214 nsresult mResultCode;
215 Atomic<bool> mOperationMayProceed;
216 bool mActorDestroyed;
218 public:
219 nsIEventTarget* OwningEventTarget() const {
220 MOZ_ASSERT(mOwningEventTarget);
222 return mOwningEventTarget;
225 bool IsOnOwningThread() const {
226 MOZ_ASSERT(mOwningEventTarget);
228 bool current;
229 return NS_SUCCEEDED(mOwningEventTarget->IsOnCurrentThread(&current)) &&
230 current;
233 void AssertIsOnOwningThread() const {
234 MOZ_ASSERT(IsOnBackgroundThread());
235 MOZ_ASSERT(IsOnOwningThread());
238 Connection* GetConnection() const {
239 MOZ_ASSERT(mConnection);
241 return mConnection;
244 nsresult ResultCode() const { return mResultCode; }
246 void MaybeSetFailureCode(nsresult aErrorCode) {
247 MOZ_ASSERT(NS_FAILED(aErrorCode));
249 if (NS_SUCCEEDED(mResultCode)) {
250 mResultCode = aErrorCode;
254 // May be called on any thread, but you should call IsActorDestroyed() if
255 // you know you're on the background thread because it is slightly faster.
256 bool OperationMayProceed() const { return mOperationMayProceed; }
258 bool IsActorDestroyed() const {
259 AssertIsOnOwningThread();
261 return mActorDestroyed;
264 // May be overridden by subclasses if they need to perform work on the
265 // background thread before being dispatched but must always call the base
266 // class implementation. Returning false will kill the child actors and
267 // prevent dispatch.
268 virtual bool Init();
270 virtual nsresult Dispatch();
272 // This callback will be called on the background thread before releasing the
273 // final reference to this request object. Subclasses may perform any
274 // additional cleanup here but must always call the base class implementation.
275 virtual void Cleanup();
277 protected:
278 ConnectionOperationBase(Connection* aConnection)
279 : Runnable("dom::ConnectionOperationBase"),
280 mOwningEventTarget(GetCurrentEventTarget()),
281 mConnection(aConnection),
282 mResultCode(NS_OK),
283 mOperationMayProceed(true),
284 mActorDestroyed(false) {
285 AssertIsOnOwningThread();
288 ~ConnectionOperationBase() override;
290 void SendResults();
292 void DatabaseWork();
294 // Methods that subclasses must implement.
295 virtual nsresult DoDatabaseWork(nsIFileStream* aFileStream) = 0;
297 // Subclasses use this override to set the IPDL response value.
298 virtual void GetResponse(SDBRequestResponse& aResponse) = 0;
300 // A method that subclasses may implement.
301 virtual void OnSuccess();
303 private:
304 NS_IMETHOD
305 Run() override;
307 // IPDL methods.
308 void ActorDestroy(ActorDestroyReason aWhy) override;
311 class OpenOp final : public ConnectionOperationBase,
312 public OpenDirectoryListener {
313 enum class State {
314 // Just created on the PBackground thread, dispatched to the main thread.
315 // Next step is FinishOpen.
316 Initial,
318 // Opening directory or initializing quota manager on the PBackground
319 // thread. Next step is either DirectoryOpenPending if quota manager is
320 // already initialized or QuotaManagerPending if quota manager needs to be
321 // initialized.
322 FinishOpen,
324 // Waiting for quota manager initialization to complete on the PBackground
325 // thread. Next step is either SendingResults if initialization failed or
326 // DirectoryOpenPending if initialization succeeded.
327 QuotaManagerPending,
329 // Waiting for directory open allowed on the PBackground thread. The next
330 // step is either SendingResults if directory lock failed to acquire, or
331 // DatabaseWorkOpen if directory lock is acquired.
332 DirectoryOpenPending,
334 // Waiting to do/doing work on the QuotaManager IO thread. Its next step is
335 // SendingResults.
336 DatabaseWorkOpen,
338 // Waiting to send/sending results on the PBackground thread. Next step is
339 // Completed.
340 SendingResults,
342 // All done.
343 Completed
346 const SDBRequestOpenParams mParams;
347 RefPtr<DirectoryLock> mDirectoryLock;
348 nsCOMPtr<nsIFileStream> mFileStream;
349 // XXX Consider changing this to ClientMetadata.
350 quota::OriginMetadata mOriginMetadata;
351 State mState;
352 bool mFileStreamOpen;
354 public:
355 OpenOp(Connection* aConnection, const SDBRequestParams& aParams);
357 nsresult Dispatch() override;
359 private:
360 ~OpenOp() override;
362 nsresult Open();
364 nsresult FinishOpen();
366 nsresult QuotaManagerOpen();
368 nsresult OpenDirectory();
370 nsresult SendToIOThread();
372 nsresult DatabaseWork();
374 void StreamClosedCallback();
376 // ConnectionOperationBase overrides
377 nsresult DoDatabaseWork(nsIFileStream* aFileStream) override;
379 void GetResponse(SDBRequestResponse& aResponse) override;
381 void OnSuccess() override;
383 void Cleanup() override;
385 NS_DECL_ISUPPORTS_INHERITED
387 NS_IMETHOD
388 Run() override;
390 // OpenDirectoryListener overrides.
391 void DirectoryLockAcquired(DirectoryLock* aLock) override;
393 void DirectoryLockFailed() override;
396 class SeekOp final : public ConnectionOperationBase {
397 const SDBRequestSeekParams mParams;
399 public:
400 SeekOp(Connection* aConnection, const SDBRequestParams& aParams);
402 private:
403 ~SeekOp() override = default;
405 nsresult DoDatabaseWork(nsIFileStream* aFileStream) override;
407 void GetResponse(SDBRequestResponse& aResponse) override;
410 class ReadOp final : public ConnectionOperationBase {
411 const SDBRequestReadParams mParams;
413 RefPtr<MemoryOutputStream> mOutputStream;
415 public:
416 ReadOp(Connection* aConnection, const SDBRequestParams& aParams);
418 bool Init() override;
420 private:
421 ~ReadOp() override = default;
423 nsresult DoDatabaseWork(nsIFileStream* aFileStream) override;
425 void GetResponse(SDBRequestResponse& aResponse) override;
428 class WriteOp final : public ConnectionOperationBase {
429 const SDBRequestWriteParams mParams;
431 nsCOMPtr<nsIInputStream> mInputStream;
433 uint64_t mSize;
435 public:
436 WriteOp(Connection* aConnection, const SDBRequestParams& aParams);
438 bool Init() override;
440 private:
441 ~WriteOp() override = default;
443 nsresult DoDatabaseWork(nsIFileStream* aFileStream) override;
445 void GetResponse(SDBRequestResponse& aResponse) override;
448 class CloseOp final : public ConnectionOperationBase {
449 public:
450 explicit CloseOp(Connection* aConnection);
452 private:
453 ~CloseOp() override = default;
455 nsresult DoDatabaseWork(nsIFileStream* aFileStream) override;
457 void GetResponse(SDBRequestResponse& aResponse) override;
459 void OnSuccess() override;
462 /*******************************************************************************
463 * Other class declarations
464 ******************************************************************************/
466 class QuotaClient final : public mozilla::dom::quota::Client {
467 static QuotaClient* sInstance;
469 bool mShutdownRequested;
471 public:
472 QuotaClient();
474 static bool IsShuttingDownOnBackgroundThread() {
475 AssertIsOnBackgroundThread();
477 if (sInstance) {
478 return sInstance->IsShuttingDown();
481 return QuotaManager::IsShuttingDown();
484 static bool IsShuttingDownOnNonBackgroundThread() {
485 MOZ_ASSERT(!IsOnBackgroundThread());
487 return QuotaManager::IsShuttingDown();
490 bool IsShuttingDown() const {
491 AssertIsOnBackgroundThread();
493 return mShutdownRequested;
496 NS_INLINE_DECL_THREADSAFE_REFCOUNTING(QuotaClient, override)
498 Type GetType() override;
500 Result<UsageInfo, nsresult> InitOrigin(PersistenceType aPersistenceType,
501 const OriginMetadata& aOriginMetadata,
502 const AtomicBool& aCanceled) override;
504 nsresult InitOriginWithoutTracking(PersistenceType aPersistenceType,
505 const OriginMetadata& aOriginMetadata,
506 const AtomicBool& aCanceled) override;
508 Result<UsageInfo, nsresult> GetUsageForOrigin(
509 PersistenceType aPersistenceType, const OriginMetadata& aOriginMetadata,
510 const AtomicBool& aCanceled) override;
512 void OnOriginClearCompleted(PersistenceType aPersistenceType,
513 const nsACString& aOrigin) override;
515 void ReleaseIOThreadObjects() override;
517 void AbortOperationsForLocks(
518 const DirectoryLockIdTable& aDirectoryLockIds) override;
520 void AbortOperationsForProcess(ContentParentId aContentParentId) override;
522 void AbortAllOperations() override;
524 void StartIdleMaintenance() override;
526 void StopIdleMaintenance() override;
528 private:
529 ~QuotaClient() override;
531 void InitiateShutdown() override;
532 bool IsShutdownCompleted() const override;
533 nsCString GetShutdownStatus() const override;
534 void ForceKillActors() override;
535 void FinalizeShutdown() override;
538 /*******************************************************************************
539 * Globals
540 ******************************************************************************/
542 using ConnectionArray = nsTArray<NotNull<RefPtr<Connection>>>;
544 StaticAutoPtr<ConnectionArray> gOpenConnections;
546 template <typename Condition>
547 void AllowToCloseConnectionsMatching(const Condition& aCondition) {
548 AssertIsOnBackgroundThread();
550 if (gOpenConnections) {
551 for (const auto& connection : *gOpenConnections) {
552 if (aCondition(*connection)) {
553 connection->AllowToClose();
559 } // namespace
561 /*******************************************************************************
562 * Exported functions
563 ******************************************************************************/
565 PBackgroundSDBConnectionParent* AllocPBackgroundSDBConnectionParent(
566 const PersistenceType& aPersistenceType,
567 const PrincipalInfo& aPrincipalInfo) {
568 AssertIsOnBackgroundThread();
570 if (NS_WARN_IF(QuotaClient::IsShuttingDownOnBackgroundThread())) {
571 return nullptr;
574 if (NS_WARN_IF(!IsValidPersistenceType(aPersistenceType))) {
575 ASSERT_UNLESS_FUZZING();
576 return nullptr;
579 if (NS_WARN_IF(aPrincipalInfo.type() == PrincipalInfo::TNullPrincipalInfo)) {
580 ASSERT_UNLESS_FUZZING();
581 return nullptr;
584 if (NS_WARN_IF(!QuotaManager::IsPrincipalInfoValid(aPrincipalInfo))) {
585 ASSERT_UNLESS_FUZZING();
586 return nullptr;
589 RefPtr<Connection> actor = new Connection(aPersistenceType, aPrincipalInfo);
591 return actor.forget().take();
594 bool RecvPBackgroundSDBConnectionConstructor(
595 PBackgroundSDBConnectionParent* aActor,
596 const PersistenceType& aPersistenceType,
597 const PrincipalInfo& aPrincipalInfo) {
598 AssertIsOnBackgroundThread();
599 MOZ_ASSERT(aActor);
600 MOZ_ASSERT(!QuotaClient::IsShuttingDownOnBackgroundThread());
602 return true;
605 bool DeallocPBackgroundSDBConnectionParent(
606 PBackgroundSDBConnectionParent* aActor) {
607 AssertIsOnBackgroundThread();
608 MOZ_ASSERT(aActor);
610 RefPtr<Connection> actor = dont_AddRef(static_cast<Connection*>(aActor));
611 return true;
614 namespace simpledb {
616 already_AddRefed<mozilla::dom::quota::Client> CreateQuotaClient() {
617 AssertIsOnBackgroundThread();
619 RefPtr<QuotaClient> client = new QuotaClient();
620 return client.forget();
623 } // namespace simpledb
625 /*******************************************************************************
626 * StreamHelper
627 ******************************************************************************/
629 StreamHelper::StreamHelper(nsIFileStream* aFileStream, nsIRunnable* aCallback)
630 : Runnable("dom::StreamHelper"),
631 mOwningEventTarget(GetCurrentEventTarget()),
632 mFileStream(aFileStream),
633 mCallback(aCallback) {
634 AssertIsOnBackgroundThread();
635 MOZ_ASSERT(aFileStream);
636 MOZ_ASSERT(aCallback);
639 StreamHelper::~StreamHelper() {
640 MOZ_ASSERT(!mFileStream);
641 MOZ_ASSERT(!mCallback);
644 void StreamHelper::AsyncClose() {
645 AssertIsOnBackgroundThread();
647 QuotaManager* quotaManager = QuotaManager::Get();
648 MOZ_ASSERT(quotaManager);
650 MOZ_ALWAYS_SUCCEEDS(
651 quotaManager->IOThread()->Dispatch(this, NS_DISPATCH_NORMAL));
654 void StreamHelper::RunOnBackgroundThread() {
655 AssertIsOnBackgroundThread();
657 nsCOMPtr<nsIFileStream> fileStream;
658 mFileStream.swap(fileStream);
660 nsCOMPtr<nsIRunnable> callback;
661 mCallback.swap(callback);
663 callback->Run();
666 void StreamHelper::RunOnIOThread() {
667 AssertIsOnIOThread();
668 MOZ_ASSERT(mFileStream);
670 nsCOMPtr<nsIInputStream> inputStream = do_QueryInterface(mFileStream);
671 MOZ_ASSERT(inputStream);
673 nsresult rv = inputStream->Close();
674 Unused << NS_WARN_IF(NS_FAILED(rv));
676 MOZ_ALWAYS_SUCCEEDS(mOwningEventTarget->Dispatch(this, NS_DISPATCH_NORMAL));
679 NS_IMETHODIMP
680 StreamHelper::Run() {
681 MOZ_ASSERT(mCallback);
683 if (IsOnBackgroundThread()) {
684 RunOnBackgroundThread();
685 } else {
686 RunOnIOThread();
689 return NS_OK;
692 /*******************************************************************************
693 * Connection
694 ******************************************************************************/
696 Connection::Connection(PersistenceType aPersistenceType,
697 const PrincipalInfo& aPrincipalInfo)
698 : mPrincipalInfo(aPrincipalInfo),
699 mPersistenceType(aPersistenceType),
700 mRunningRequest(false),
701 mOpen(false),
702 mAllowedToClose(false),
703 mActorDestroyed(false) {
704 AssertIsOnBackgroundThread();
705 MOZ_ASSERT(!QuotaClient::IsShuttingDownOnBackgroundThread());
708 Connection::~Connection() {
709 MOZ_ASSERT(!mRunningRequest);
710 MOZ_ASSERT(!mOpen);
711 MOZ_ASSERT(mActorDestroyed);
714 void Connection::OnNewRequest() {
715 AssertIsOnBackgroundThread();
716 MOZ_ASSERT(!mRunningRequest);
718 mRunningRequest = true;
721 void Connection::OnRequestFinished() {
722 AssertIsOnBackgroundThread();
723 MOZ_ASSERT(mRunningRequest);
725 mRunningRequest = false;
727 MaybeCloseStream();
730 void Connection::OnOpen(const nsACString& aOrigin, const nsAString& aName,
731 already_AddRefed<DirectoryLock> aDirectoryLock,
732 already_AddRefed<nsIFileStream> aFileStream) {
733 AssertIsOnBackgroundThread();
734 MOZ_ASSERT(!aOrigin.IsEmpty());
735 MOZ_ASSERT(!aName.IsEmpty());
736 MOZ_ASSERT(mOrigin.IsEmpty());
737 MOZ_ASSERT(mName.IsEmpty());
738 MOZ_ASSERT(!mDirectoryLock);
739 MOZ_ASSERT(!mFileStream);
740 MOZ_ASSERT(!mOpen);
742 mOrigin = aOrigin;
743 mName = aName;
744 mDirectoryLock = aDirectoryLock;
745 mFileStream = aFileStream;
746 mOpen = true;
748 if (!gOpenConnections) {
749 gOpenConnections = new ConnectionArray();
752 gOpenConnections->AppendElement(WrapNotNullUnchecked(this));
755 void Connection::OnClose() {
756 AssertIsOnBackgroundThread();
757 MOZ_ASSERT(!mOrigin.IsEmpty());
758 MOZ_ASSERT(mDirectoryLock);
759 MOZ_ASSERT(mFileStream);
760 MOZ_ASSERT(mOpen);
762 mOrigin.Truncate();
763 mName.Truncate();
764 mDirectoryLock = nullptr;
765 mFileStream = nullptr;
766 mOpen = false;
768 MOZ_ASSERT(gOpenConnections);
769 gOpenConnections->RemoveElement(this);
771 if (gOpenConnections->IsEmpty()) {
772 gOpenConnections = nullptr;
775 if (mAllowedToClose && !mActorDestroyed) {
776 Unused << SendClosed();
780 void Connection::AllowToClose() {
781 AssertIsOnBackgroundThread();
783 if (mAllowedToClose) {
784 return;
787 mAllowedToClose = true;
789 if (!mActorDestroyed) {
790 Unused << SendAllowToClose();
793 MaybeCloseStream();
796 void Connection::MaybeCloseStream() {
797 AssertIsOnBackgroundThread();
799 if (!mRunningRequest && mOpen && mAllowedToClose) {
800 nsCOMPtr<nsIRunnable> callback = NewRunnableMethod(
801 "dom::Connection::OnClose", this, &Connection::OnClose);
803 RefPtr<StreamHelper> helper = new StreamHelper(mFileStream, callback);
804 helper->AsyncClose();
808 bool Connection::VerifyRequestParams(const SDBRequestParams& aParams) const {
809 AssertIsOnBackgroundThread();
810 MOZ_ASSERT(aParams.type() != SDBRequestParams::T__None);
812 switch (aParams.type()) {
813 case SDBRequestParams::TSDBRequestOpenParams: {
814 if (NS_WARN_IF(mOpen)) {
815 ASSERT_UNLESS_FUZZING();
816 return false;
819 break;
822 case SDBRequestParams::TSDBRequestSeekParams:
823 case SDBRequestParams::TSDBRequestReadParams:
824 case SDBRequestParams::TSDBRequestWriteParams:
825 case SDBRequestParams::TSDBRequestCloseParams: {
826 if (NS_WARN_IF(!mOpen)) {
827 ASSERT_UNLESS_FUZZING();
828 return false;
831 break;
834 default:
835 MOZ_CRASH("Should never get here!");
838 return true;
841 void Connection::ActorDestroy(ActorDestroyReason aWhy) {
842 AssertIsOnBackgroundThread();
843 MOZ_ASSERT(!mActorDestroyed);
845 mActorDestroyed = true;
847 AllowToClose();
850 mozilla::ipc::IPCResult Connection::RecvDeleteMe() {
851 AssertIsOnBackgroundThread();
852 MOZ_ASSERT(!mActorDestroyed);
854 IProtocol* mgr = Manager();
855 if (!PBackgroundSDBConnectionParent::Send__delete__(this)) {
856 return IPC_FAIL_NO_REASON(mgr);
859 return IPC_OK();
862 PBackgroundSDBRequestParent* Connection::AllocPBackgroundSDBRequestParent(
863 const SDBRequestParams& aParams) {
864 AssertIsOnBackgroundThread();
865 MOZ_ASSERT(aParams.type() != SDBRequestParams::T__None);
867 if (aParams.type() == SDBRequestParams::TSDBRequestOpenParams &&
868 NS_WARN_IF(QuotaClient::IsShuttingDownOnBackgroundThread())) {
869 return nullptr;
872 if (mAllowedToClose) {
873 return nullptr;
876 #ifdef DEBUG
877 // Always verify parameters in DEBUG builds!
878 bool trustParams = false;
879 #else
880 PBackgroundParent* backgroundActor = Manager();
881 MOZ_ASSERT(backgroundActor);
883 bool trustParams = !BackgroundParent::IsOtherProcessActor(backgroundActor);
884 #endif
886 if (NS_WARN_IF(!trustParams && !VerifyRequestParams(aParams))) {
887 ASSERT_UNLESS_FUZZING();
888 return nullptr;
891 if (NS_WARN_IF(mRunningRequest)) {
892 ASSERT_UNLESS_FUZZING();
893 return nullptr;
896 RefPtr<ConnectionOperationBase> actor;
898 switch (aParams.type()) {
899 case SDBRequestParams::TSDBRequestOpenParams:
900 actor = new OpenOp(this, aParams);
901 break;
903 case SDBRequestParams::TSDBRequestSeekParams:
904 actor = new SeekOp(this, aParams);
905 break;
907 case SDBRequestParams::TSDBRequestReadParams:
908 actor = new ReadOp(this, aParams);
909 break;
911 case SDBRequestParams::TSDBRequestWriteParams:
912 actor = new WriteOp(this, aParams);
913 break;
915 case SDBRequestParams::TSDBRequestCloseParams:
916 actor = new CloseOp(this);
917 break;
919 default:
920 MOZ_CRASH("Should never get here!");
923 // Transfer ownership to IPDL.
924 return actor.forget().take();
927 mozilla::ipc::IPCResult Connection::RecvPBackgroundSDBRequestConstructor(
928 PBackgroundSDBRequestParent* aActor, const SDBRequestParams& aParams) {
929 AssertIsOnBackgroundThread();
930 MOZ_ASSERT(aActor);
931 MOZ_ASSERT(aParams.type() != SDBRequestParams::T__None);
932 MOZ_ASSERT_IF(aParams.type() == SDBRequestParams::TSDBRequestOpenParams,
933 !QuotaClient::IsShuttingDownOnBackgroundThread());
934 MOZ_ASSERT(!mAllowedToClose);
935 MOZ_ASSERT(!mRunningRequest);
937 auto* op = static_cast<ConnectionOperationBase*>(aActor);
939 if (NS_WARN_IF(!op->Init())) {
940 op->Cleanup();
941 return IPC_FAIL_NO_REASON(this);
944 if (NS_WARN_IF(NS_FAILED(op->Dispatch()))) {
945 op->Cleanup();
946 return IPC_FAIL_NO_REASON(this);
949 return IPC_OK();
952 bool Connection::DeallocPBackgroundSDBRequestParent(
953 PBackgroundSDBRequestParent* aActor) {
954 AssertIsOnBackgroundThread();
955 MOZ_ASSERT(aActor);
957 // Transfer ownership back from IPDL.
958 RefPtr<ConnectionOperationBase> actor =
959 dont_AddRef(static_cast<ConnectionOperationBase*>(aActor));
960 return true;
963 /*******************************************************************************
964 * ConnectionOperationBase
965 ******************************************************************************/
967 ConnectionOperationBase::~ConnectionOperationBase() {
968 MOZ_ASSERT(
969 !mConnection,
970 "ConnectionOperationBase::Cleanup() was not called by a subclass!");
971 MOZ_ASSERT(mActorDestroyed);
974 bool ConnectionOperationBase::Init() {
975 AssertIsOnBackgroundThread();
976 MOZ_ASSERT(mConnection);
978 mConnection->OnNewRequest();
980 return true;
983 nsresult ConnectionOperationBase::Dispatch() {
984 if (NS_WARN_IF(QuotaClient::IsShuttingDownOnBackgroundThread()) ||
985 IsActorDestroyed()) {
986 return NS_ERROR_ABORT;
989 QuotaManager* quotaManager = QuotaManager::Get();
990 MOZ_ASSERT(quotaManager);
992 nsresult rv = quotaManager->IOThread()->Dispatch(this, NS_DISPATCH_NORMAL);
993 if (NS_WARN_IF(NS_FAILED(rv))) {
994 return rv;
997 return NS_OK;
1000 void ConnectionOperationBase::Cleanup() {
1001 AssertIsOnOwningThread();
1002 MOZ_ASSERT(mConnection);
1004 mConnection->OnRequestFinished();
1006 mConnection = nullptr;
1009 void ConnectionOperationBase::SendResults() {
1010 AssertIsOnOwningThread();
1012 if (IsActorDestroyed()) {
1013 MaybeSetFailureCode(NS_ERROR_FAILURE);
1014 } else {
1015 SDBRequestResponse response;
1017 if (NS_SUCCEEDED(mResultCode)) {
1018 GetResponse(response);
1020 MOZ_ASSERT(response.type() != SDBRequestResponse::T__None);
1021 MOZ_ASSERT(response.type() != SDBRequestResponse::Tnsresult);
1022 } else {
1023 response = mResultCode;
1026 Unused << PBackgroundSDBRequestParent::Send__delete__(this, response);
1028 if (NS_SUCCEEDED(mResultCode)) {
1029 OnSuccess();
1033 Cleanup();
1036 void ConnectionOperationBase::DatabaseWork() {
1037 AssertIsOnIOThread();
1038 MOZ_ASSERT(NS_SUCCEEDED(mResultCode));
1040 if (!OperationMayProceed()) {
1041 // The operation was canceled in some way, likely because the child process
1042 // has crashed.
1043 mResultCode = NS_ERROR_ABORT;
1044 } else {
1045 nsIFileStream* fileStream = mConnection->GetFileStream();
1046 MOZ_ASSERT(fileStream);
1048 nsresult rv = DoDatabaseWork(fileStream);
1049 if (NS_FAILED(rv)) {
1050 mResultCode = rv;
1054 MOZ_ALWAYS_SUCCEEDS(mOwningEventTarget->Dispatch(this, NS_DISPATCH_NORMAL));
1057 void ConnectionOperationBase::OnSuccess() { AssertIsOnOwningThread(); }
1059 NS_IMETHODIMP
1060 ConnectionOperationBase::Run() {
1061 if (IsOnBackgroundThread()) {
1062 SendResults();
1063 } else {
1064 DatabaseWork();
1067 return NS_OK;
1070 void ConnectionOperationBase::ActorDestroy(ActorDestroyReason aWhy) {
1071 AssertIsOnBackgroundThread();
1073 mOperationMayProceed = false;
1074 mActorDestroyed = true;
1077 OpenOp::OpenOp(Connection* aConnection, const SDBRequestParams& aParams)
1078 : ConnectionOperationBase(aConnection),
1079 mParams(aParams.get_SDBRequestOpenParams()),
1080 mState(State::Initial),
1081 mFileStreamOpen(false) {
1082 MOZ_ASSERT(aParams.type() == SDBRequestParams::TSDBRequestOpenParams);
1085 OpenOp::~OpenOp() {
1086 MOZ_ASSERT(!mDirectoryLock);
1087 MOZ_ASSERT(!mFileStream);
1088 MOZ_ASSERT(!mFileStreamOpen);
1089 MOZ_ASSERT_IF(OperationMayProceed(),
1090 mState == State::Initial || mState == State::Completed);
1093 nsresult OpenOp::Dispatch() {
1094 MOZ_ALWAYS_SUCCEEDS(NS_DispatchToMainThread(this));
1096 return NS_OK;
1099 nsresult OpenOp::Open() {
1100 MOZ_ASSERT(NS_IsMainThread());
1101 MOZ_ASSERT(mState == State::Initial);
1103 if (NS_WARN_IF(QuotaClient::IsShuttingDownOnNonBackgroundThread()) ||
1104 !OperationMayProceed()) {
1105 return NS_ERROR_ABORT;
1108 if (NS_WARN_IF(!Preferences::GetBool(kPrefSimpleDBEnabled, false))) {
1109 return NS_ERROR_UNEXPECTED;
1112 PersistenceType persistenceType = GetConnection()->GetPersistenceType();
1114 const PrincipalInfo& principalInfo = GetConnection()->GetPrincipalInfo();
1116 if (principalInfo.type() == PrincipalInfo::TSystemPrincipalInfo) {
1117 mOriginMetadata = {QuotaManager::GetInfoForChrome(), persistenceType};
1118 } else {
1119 MOZ_ASSERT(principalInfo.type() == PrincipalInfo::TContentPrincipalInfo);
1121 QM_TRY_INSPECT(const auto& principal,
1122 PrincipalInfoToPrincipal(principalInfo));
1124 QM_TRY_UNWRAP(auto principalMetadata,
1125 QuotaManager::GetInfoFromPrincipal(principal));
1127 mOriginMetadata = {std::move(principalMetadata), persistenceType};
1130 mState = State::FinishOpen;
1131 MOZ_ALWAYS_SUCCEEDS(OwningEventTarget()->Dispatch(this, NS_DISPATCH_NORMAL));
1133 return NS_OK;
1136 nsresult OpenOp::FinishOpen() {
1137 AssertIsOnOwningThread();
1138 MOZ_ASSERT(mState == State::FinishOpen);
1140 if (gOpenConnections) {
1141 for (const auto& connection : *gOpenConnections) {
1142 if (connection->Origin() == mOriginMetadata.mOrigin &&
1143 connection->Name() == mParams.name()) {
1144 return NS_ERROR_STORAGE_BUSY;
1149 if (QuotaManager::Get()) {
1150 nsresult rv = OpenDirectory();
1151 if (NS_WARN_IF(NS_FAILED(rv))) {
1152 return rv;
1155 return NS_OK;
1158 mState = State::QuotaManagerPending;
1159 QuotaManager::GetOrCreate(this);
1161 return NS_OK;
1164 nsresult OpenOp::QuotaManagerOpen() {
1165 AssertIsOnOwningThread();
1166 MOZ_ASSERT(mState == State::QuotaManagerPending);
1168 if (NS_WARN_IF(!QuotaManager::Get())) {
1169 return NS_ERROR_FAILURE;
1172 nsresult rv = OpenDirectory();
1173 if (NS_WARN_IF(NS_FAILED(rv))) {
1174 return rv;
1177 return NS_OK;
1180 nsresult OpenOp::OpenDirectory() {
1181 AssertIsOnOwningThread();
1182 MOZ_ASSERT(mState == State::FinishOpen ||
1183 mState == State::QuotaManagerPending);
1184 MOZ_ASSERT(!mOriginMetadata.mOrigin.IsEmpty());
1185 MOZ_ASSERT(!mDirectoryLock);
1186 MOZ_ASSERT(!QuotaClient::IsShuttingDownOnBackgroundThread());
1187 MOZ_ASSERT(QuotaManager::Get());
1189 RefPtr<DirectoryLock> directoryLock =
1190 QuotaManager::Get()->CreateDirectoryLock(
1191 GetConnection()->GetPersistenceType(), mOriginMetadata,
1192 mozilla::dom::quota::Client::SDB,
1193 /* aExclusive */ false);
1195 mState = State::DirectoryOpenPending;
1196 directoryLock->Acquire(this);
1198 return NS_OK;
1201 nsresult OpenOp::SendToIOThread() {
1202 AssertIsOnOwningThread();
1203 MOZ_ASSERT(mState == State::DirectoryOpenPending);
1205 if (NS_WARN_IF(QuotaClient::IsShuttingDownOnBackgroundThread()) ||
1206 IsActorDestroyed()) {
1207 return NS_ERROR_ABORT;
1210 mFileStream =
1211 new FileStream(GetConnection()->GetPersistenceType(), mOriginMetadata,
1212 mozilla::dom::quota::Client::SDB);
1214 QuotaManager* quotaManager = QuotaManager::Get();
1215 MOZ_ASSERT(quotaManager);
1217 // Must set this before dispatching otherwise we will race with the IO thread.
1218 mState = State::DatabaseWorkOpen;
1220 nsresult rv = quotaManager->IOThread()->Dispatch(this, NS_DISPATCH_NORMAL);
1221 if (NS_WARN_IF(NS_FAILED(rv))) {
1222 return rv;
1225 return NS_OK;
1228 nsresult OpenOp::DatabaseWork() {
1229 AssertIsOnIOThread();
1230 MOZ_ASSERT(mState == State::DatabaseWorkOpen);
1231 MOZ_ASSERT(mFileStream);
1232 MOZ_ASSERT(!mFileStreamOpen);
1234 if (NS_WARN_IF(QuotaClient::IsShuttingDownOnNonBackgroundThread()) ||
1235 !OperationMayProceed()) {
1236 return NS_ERROR_ABORT;
1239 QuotaManager* quotaManager = QuotaManager::Get();
1240 MOZ_ASSERT(quotaManager);
1242 QM_TRY(MOZ_TO_RESULT(quotaManager->EnsureStorageIsInitialized()));
1244 QM_TRY_INSPECT(
1245 const auto& dbDirectory,
1246 ([persistenceType = GetConnection()->GetPersistenceType(), &quotaManager,
1247 this]()
1248 -> mozilla::Result<std::pair<nsCOMPtr<nsIFile>, bool>, nsresult> {
1249 if (persistenceType == PERSISTENCE_TYPE_PERSISTENT) {
1250 QM_TRY_RETURN(quotaManager->EnsurePersistentOriginIsInitialized(
1251 mOriginMetadata));
1254 QM_TRY(
1255 MOZ_TO_RESULT(quotaManager->EnsureTemporaryStorageIsInitialized()));
1256 QM_TRY_RETURN(quotaManager->EnsureTemporaryOriginIsInitialized(
1257 persistenceType, mOriginMetadata));
1259 .map([](const auto& res) { return res.first; })));
1261 nsresult rv =
1262 dbDirectory->Append(NS_LITERAL_STRING_FROM_CSTRING(SDB_DIRECTORY_NAME));
1263 if (NS_WARN_IF(NS_FAILED(rv))) {
1264 return rv;
1267 bool exists;
1268 rv = dbDirectory->Exists(&exists);
1269 if (NS_WARN_IF(NS_FAILED(rv))) {
1270 return rv;
1273 if (!exists) {
1274 rv = dbDirectory->Create(nsIFile::DIRECTORY_TYPE, 0755);
1275 if (NS_WARN_IF(NS_FAILED(rv))) {
1276 return rv;
1279 #ifdef DEBUG
1280 else {
1281 bool isDirectory;
1282 MOZ_ASSERT(NS_SUCCEEDED(dbDirectory->IsDirectory(&isDirectory)));
1283 MOZ_ASSERT(isDirectory);
1285 #endif
1287 nsCOMPtr<nsIFile> dbFile;
1288 rv = dbDirectory->Clone(getter_AddRefs(dbFile));
1289 if (NS_WARN_IF(NS_FAILED(rv))) {
1290 return rv;
1293 rv = dbFile->Append(mParams.name() + kSDBSuffix);
1294 if (NS_WARN_IF(NS_FAILED(rv))) {
1295 return rv;
1298 nsString databaseFilePath;
1299 rv = dbFile->GetPath(databaseFilePath);
1300 if (NS_WARN_IF(NS_FAILED(rv))) {
1301 return rv;
1304 rv = mFileStream->Init(dbFile, PR_RDWR | PR_CREATE_FILE, 0644, 0);
1305 if (NS_WARN_IF(NS_FAILED(rv))) {
1306 return rv;
1309 mFileStreamOpen = true;
1311 rv = DoDatabaseWork(mFileStream);
1312 if (NS_WARN_IF(NS_FAILED(rv))) {
1313 return rv;
1316 // Must set mState before dispatching otherwise we will race with the owning
1317 // thread.
1318 mState = State::SendingResults;
1320 rv = OwningEventTarget()->Dispatch(this, NS_DISPATCH_NORMAL);
1321 if (NS_WARN_IF(NS_FAILED(rv))) {
1322 return rv;
1325 return NS_OK;
1328 void OpenOp::StreamClosedCallback() {
1329 AssertIsOnOwningThread();
1330 MOZ_ASSERT(NS_FAILED(ResultCode()));
1331 MOZ_ASSERT(mDirectoryLock);
1332 MOZ_ASSERT(mFileStream);
1333 MOZ_ASSERT(mFileStreamOpen);
1335 mDirectoryLock = nullptr;
1336 mFileStream = nullptr;
1337 mFileStreamOpen = false;
1340 nsresult OpenOp::DoDatabaseWork(nsIFileStream* aFileStream) {
1341 AssertIsOnIOThread();
1343 return NS_OK;
1346 void OpenOp::GetResponse(SDBRequestResponse& aResponse) {
1347 AssertIsOnOwningThread();
1349 aResponse = SDBRequestOpenResponse();
1352 void OpenOp::OnSuccess() {
1353 AssertIsOnOwningThread();
1354 MOZ_ASSERT(NS_SUCCEEDED(ResultCode()));
1355 MOZ_ASSERT(!mOriginMetadata.mOrigin.IsEmpty());
1356 MOZ_ASSERT(mDirectoryLock);
1357 MOZ_ASSERT(mFileStream);
1358 MOZ_ASSERT(mFileStreamOpen);
1360 RefPtr<DirectoryLock> directoryLock;
1361 nsCOMPtr<nsIFileStream> fileStream;
1363 mDirectoryLock.swap(directoryLock);
1364 mFileStream.swap(fileStream);
1365 mFileStreamOpen = false;
1367 GetConnection()->OnOpen(mOriginMetadata.mOrigin, mParams.name(),
1368 directoryLock.forget(), fileStream.forget());
1371 void OpenOp::Cleanup() {
1372 AssertIsOnOwningThread();
1373 MOZ_ASSERT_IF(mFileStreamOpen, mFileStream);
1375 if (mFileStream && mFileStreamOpen) {
1376 // If we have an initialized file stream then the operation must have failed
1377 // and there must be a directory lock too.
1378 MOZ_ASSERT(NS_FAILED(ResultCode()));
1379 MOZ_ASSERT(mDirectoryLock);
1381 // We must close the stream on the I/O thread before releasing it on this
1382 // thread. The directory lock can't be released either.
1383 nsCOMPtr<nsIRunnable> callback =
1384 NewRunnableMethod("dom::OpenOp::StreamClosedCallback", this,
1385 &OpenOp::StreamClosedCallback);
1387 RefPtr<StreamHelper> helper = new StreamHelper(mFileStream, callback);
1388 helper->AsyncClose();
1389 } else {
1390 MOZ_ASSERT(!mFileStreamOpen);
1392 mDirectoryLock = nullptr;
1393 mFileStream = nullptr;
1396 ConnectionOperationBase::Cleanup();
1399 NS_IMPL_ISUPPORTS_INHERITED0(OpenOp, ConnectionOperationBase)
1401 NS_IMETHODIMP
1402 OpenOp::Run() {
1403 nsresult rv;
1405 switch (mState) {
1406 case State::Initial:
1407 rv = Open();
1408 break;
1410 case State::FinishOpen:
1411 rv = FinishOpen();
1412 break;
1414 case State::QuotaManagerPending:
1415 rv = QuotaManagerOpen();
1416 break;
1418 case State::DatabaseWorkOpen:
1419 rv = DatabaseWork();
1420 break;
1422 case State::SendingResults:
1423 SendResults();
1424 return NS_OK;
1426 default:
1427 MOZ_CRASH("Bad state!");
1430 if (NS_WARN_IF(NS_FAILED(rv)) && mState != State::SendingResults) {
1431 MaybeSetFailureCode(rv);
1433 // Must set mState before dispatching otherwise we will race with the owning
1434 // thread.
1435 mState = State::SendingResults;
1437 if (IsOnOwningThread()) {
1438 SendResults();
1439 } else {
1440 MOZ_ALWAYS_SUCCEEDS(
1441 OwningEventTarget()->Dispatch(this, NS_DISPATCH_NORMAL));
1445 return NS_OK;
1448 void OpenOp::DirectoryLockAcquired(DirectoryLock* aLock) {
1449 AssertIsOnOwningThread();
1450 MOZ_ASSERT(mState == State::DirectoryOpenPending);
1451 MOZ_ASSERT(!mDirectoryLock);
1453 mDirectoryLock = aLock;
1455 nsresult rv = SendToIOThread();
1456 if (NS_WARN_IF(NS_FAILED(rv))) {
1457 MaybeSetFailureCode(rv);
1459 // The caller holds a strong reference to us, no need for a self reference
1460 // before calling Run().
1462 mState = State::SendingResults;
1463 MOZ_ALWAYS_SUCCEEDS(Run());
1465 return;
1469 void OpenOp::DirectoryLockFailed() {
1470 AssertIsOnOwningThread();
1471 MOZ_ASSERT(mState == State::DirectoryOpenPending);
1472 MOZ_ASSERT(!mDirectoryLock);
1474 MaybeSetFailureCode(NS_ERROR_FAILURE);
1476 // The caller holds a strong reference to us, no need for a self reference
1477 // before calling Run().
1479 mState = State::SendingResults;
1480 MOZ_ALWAYS_SUCCEEDS(Run());
1483 SeekOp::SeekOp(Connection* aConnection, const SDBRequestParams& aParams)
1484 : ConnectionOperationBase(aConnection),
1485 mParams(aParams.get_SDBRequestSeekParams()) {
1486 MOZ_ASSERT(aParams.type() == SDBRequestParams::TSDBRequestSeekParams);
1489 nsresult SeekOp::DoDatabaseWork(nsIFileStream* aFileStream) {
1490 AssertIsOnIOThread();
1491 MOZ_ASSERT(aFileStream);
1493 nsCOMPtr<nsISeekableStream> seekableStream = do_QueryInterface(aFileStream);
1494 MOZ_ASSERT(seekableStream);
1496 nsresult rv =
1497 seekableStream->Seek(nsISeekableStream::NS_SEEK_SET, mParams.offset());
1499 if (NS_WARN_IF(NS_FAILED(rv))) {
1500 return rv;
1503 return NS_OK;
1506 void SeekOp::GetResponse(SDBRequestResponse& aResponse) {
1507 aResponse = SDBRequestSeekResponse();
1510 ReadOp::ReadOp(Connection* aConnection, const SDBRequestParams& aParams)
1511 : ConnectionOperationBase(aConnection),
1512 mParams(aParams.get_SDBRequestReadParams()) {
1513 MOZ_ASSERT(aParams.type() == SDBRequestParams::TSDBRequestReadParams);
1516 bool ReadOp::Init() {
1517 AssertIsOnOwningThread();
1519 if (NS_WARN_IF(!ConnectionOperationBase::Init())) {
1520 return false;
1523 mOutputStream = MemoryOutputStream::Create(mParams.size());
1524 if (NS_WARN_IF(!mOutputStream)) {
1525 return false;
1528 return true;
1531 nsresult ReadOp::DoDatabaseWork(nsIFileStream* aFileStream) {
1532 AssertIsOnIOThread();
1533 MOZ_ASSERT(aFileStream);
1535 nsCOMPtr<nsIInputStream> inputStream = do_QueryInterface(aFileStream);
1536 MOZ_ASSERT(inputStream);
1538 nsresult rv;
1540 uint64_t offset = 0;
1542 do {
1543 char copyBuffer[kCopyBufferSize];
1545 uint64_t max = mParams.size() - offset;
1546 if (max == 0) {
1547 break;
1550 uint32_t count = sizeof(copyBuffer);
1551 if (count > max) {
1552 count = max;
1555 uint32_t numRead;
1556 rv = inputStream->Read(copyBuffer, count, &numRead);
1557 if (NS_WARN_IF(NS_FAILED(rv))) {
1558 return rv;
1561 if (!numRead) {
1562 break;
1565 uint32_t numWrite;
1566 rv = mOutputStream->Write(copyBuffer, numRead, &numWrite);
1567 if (NS_WARN_IF(NS_FAILED(rv))) {
1568 return rv;
1571 if (NS_WARN_IF(numWrite != numRead)) {
1572 return NS_ERROR_FAILURE;
1575 offset += numWrite;
1576 } while (true);
1578 MOZ_ASSERT(offset == mParams.size());
1580 MOZ_ALWAYS_SUCCEEDS(mOutputStream->Close());
1582 return NS_OK;
1585 void ReadOp::GetResponse(SDBRequestResponse& aResponse) {
1586 aResponse = SDBRequestReadResponse(mOutputStream->Data());
1589 WriteOp::WriteOp(Connection* aConnection, const SDBRequestParams& aParams)
1590 : ConnectionOperationBase(aConnection),
1591 mParams(aParams.get_SDBRequestWriteParams()),
1592 mSize(0) {
1593 MOZ_ASSERT(aParams.type() == SDBRequestParams::TSDBRequestWriteParams);
1596 bool WriteOp::Init() {
1597 AssertIsOnOwningThread();
1599 if (NS_WARN_IF(!ConnectionOperationBase::Init())) {
1600 return false;
1603 const nsCString& string = mParams.data();
1605 nsCOMPtr<nsIInputStream> inputStream;
1606 nsresult rv = NS_NewCStringInputStream(getter_AddRefs(inputStream), string);
1607 if (NS_WARN_IF(NS_FAILED(rv))) {
1608 return false;
1611 mInputStream = std::move(inputStream);
1612 mSize = string.Length();
1614 return true;
1617 nsresult WriteOp::DoDatabaseWork(nsIFileStream* aFileStream) {
1618 AssertIsOnIOThread();
1619 MOZ_ASSERT(aFileStream);
1621 nsCOMPtr<nsIOutputStream> outputStream = do_QueryInterface(aFileStream);
1622 MOZ_ASSERT(outputStream);
1624 nsresult rv;
1626 do {
1627 char copyBuffer[kCopyBufferSize];
1629 uint32_t numRead;
1630 rv = mInputStream->Read(copyBuffer, sizeof(copyBuffer), &numRead);
1631 if (NS_WARN_IF(NS_FAILED(rv))) {
1632 break;
1635 if (!numRead) {
1636 break;
1639 uint32_t numWrite;
1640 rv = outputStream->Write(copyBuffer, numRead, &numWrite);
1641 if (NS_WARN_IF(NS_FAILED(rv))) {
1642 return rv;
1645 if (NS_WARN_IF(numWrite != numRead)) {
1646 return NS_ERROR_FAILURE;
1648 } while (true);
1650 MOZ_ALWAYS_SUCCEEDS(mInputStream->Close());
1652 return NS_OK;
1655 void WriteOp::GetResponse(SDBRequestResponse& aResponse) {
1656 aResponse = SDBRequestWriteResponse();
1659 CloseOp::CloseOp(Connection* aConnection)
1660 : ConnectionOperationBase(aConnection) {}
1662 nsresult CloseOp::DoDatabaseWork(nsIFileStream* aFileStream) {
1663 AssertIsOnIOThread();
1664 MOZ_ASSERT(aFileStream);
1666 nsCOMPtr<nsIInputStream> inputStream = do_QueryInterface(aFileStream);
1667 MOZ_ASSERT(inputStream);
1669 nsresult rv = inputStream->Close();
1670 if (NS_WARN_IF(NS_FAILED(rv))) {
1671 return rv;
1674 return NS_OK;
1677 void CloseOp::GetResponse(SDBRequestResponse& aResponse) {
1678 aResponse = SDBRequestCloseResponse();
1681 void CloseOp::OnSuccess() {
1682 AssertIsOnOwningThread();
1684 GetConnection()->OnClose();
1687 /*******************************************************************************
1688 * QuotaClient
1689 ******************************************************************************/
1691 QuotaClient* QuotaClient::sInstance = nullptr;
1693 QuotaClient::QuotaClient() : mShutdownRequested(false) {
1694 AssertIsOnBackgroundThread();
1695 MOZ_ASSERT(!sInstance, "We expect this to be a singleton!");
1697 sInstance = this;
1700 QuotaClient::~QuotaClient() {
1701 AssertIsOnBackgroundThread();
1702 MOZ_ASSERT(sInstance == this, "We expect this to be a singleton!");
1704 sInstance = nullptr;
1707 mozilla::dom::quota::Client::Type QuotaClient::GetType() {
1708 return QuotaClient::SDB;
1711 Result<UsageInfo, nsresult> QuotaClient::InitOrigin(
1712 PersistenceType aPersistenceType, const OriginMetadata& aOriginMetadata,
1713 const AtomicBool& aCanceled) {
1714 AssertIsOnIOThread();
1716 return GetUsageForOrigin(aPersistenceType, aOriginMetadata, aCanceled);
1719 nsresult QuotaClient::InitOriginWithoutTracking(
1720 PersistenceType aPersistenceType, const OriginMetadata& aOriginMetadata,
1721 const AtomicBool& aCanceled) {
1722 AssertIsOnIOThread();
1724 return NS_OK;
1727 Result<UsageInfo, nsresult> QuotaClient::GetUsageForOrigin(
1728 PersistenceType aPersistenceType, const OriginMetadata& aOriginMetadata,
1729 const AtomicBool& aCanceled) {
1730 AssertIsOnIOThread();
1732 QuotaManager* quotaManager = QuotaManager::Get();
1733 MOZ_ASSERT(quotaManager);
1735 QM_TRY_UNWRAP(auto directory, quotaManager->GetDirectoryForOrigin(
1736 aPersistenceType, aOriginMetadata.mOrigin));
1738 MOZ_ASSERT(directory);
1740 nsresult rv =
1741 directory->Append(NS_LITERAL_STRING_FROM_CSTRING(SDB_DIRECTORY_NAME));
1742 if (NS_WARN_IF(NS_FAILED(rv))) {
1743 return Err(rv);
1746 DebugOnly<bool> exists;
1747 MOZ_ASSERT(NS_SUCCEEDED(directory->Exists(&exists)) && exists);
1749 QM_TRY_RETURN(ReduceEachFileAtomicCancelable(
1750 *directory, aCanceled, UsageInfo{},
1751 [](UsageInfo usageInfo,
1752 const nsCOMPtr<nsIFile>& file) -> Result<UsageInfo, nsresult> {
1753 QM_TRY_INSPECT(const bool& isDirectory,
1754 MOZ_TO_RESULT_INVOKE(file, IsDirectory));
1756 if (isDirectory) {
1757 Unused << WARN_IF_FILE_IS_UNKNOWN(*file);
1758 return usageInfo;
1761 nsString leafName;
1762 QM_TRY(MOZ_TO_RESULT(file->GetLeafName(leafName)));
1764 if (StringEndsWith(leafName, kSDBSuffix)) {
1765 QM_TRY_INSPECT(const int64_t& fileSize,
1766 MOZ_TO_RESULT_INVOKE(file, GetFileSize));
1768 MOZ_ASSERT(fileSize >= 0);
1770 return usageInfo +
1771 UsageInfo{DatabaseUsageType(Some(uint64_t(fileSize)))};
1774 Unused << WARN_IF_FILE_IS_UNKNOWN(*file);
1776 return usageInfo;
1777 }));
1780 void QuotaClient::OnOriginClearCompleted(PersistenceType aPersistenceType,
1781 const nsACString& aOrigin) {
1782 AssertIsOnIOThread();
1785 void QuotaClient::ReleaseIOThreadObjects() { AssertIsOnIOThread(); }
1787 void QuotaClient::AbortOperationsForLocks(
1788 const DirectoryLockIdTable& aDirectoryLockIds) {
1789 AssertIsOnBackgroundThread();
1791 AllowToCloseConnectionsMatching([&aDirectoryLockIds](const auto& connection) {
1792 // If the connections is registered in gOpenConnections then it must have
1793 // a directory lock.
1794 return IsLockForObjectContainedInLockTable(connection, aDirectoryLockIds);
1798 void QuotaClient::AbortOperationsForProcess(ContentParentId aContentParentId) {
1799 AssertIsOnBackgroundThread();
1802 void QuotaClient::AbortAllOperations() {
1803 AssertIsOnBackgroundThread();
1805 AllowToCloseConnectionsMatching([](const auto&) { return true; });
1808 void QuotaClient::StartIdleMaintenance() { AssertIsOnBackgroundThread(); }
1810 void QuotaClient::StopIdleMaintenance() { AssertIsOnBackgroundThread(); }
1812 void QuotaClient::InitiateShutdown() {
1813 AssertIsOnBackgroundThread();
1814 MOZ_ASSERT(!mShutdownRequested);
1816 mShutdownRequested = true;
1818 if (gOpenConnections) {
1819 for (const auto& connection : *gOpenConnections) {
1820 connection->AllowToClose();
1825 bool QuotaClient::IsShutdownCompleted() const { return !gOpenConnections; }
1827 void QuotaClient::ForceKillActors() {
1828 // Currently we don't implement killing actors (are there any to kill here?).
1831 nsCString QuotaClient::GetShutdownStatus() const {
1832 // XXX Gather information here.
1833 return "To be implemented"_ns;
1836 void QuotaClient::FinalizeShutdown() {
1837 // Nothing to do here.
1840 } // namespace mozilla::dom