Bug 1769952 - Fix running raptor on a Win10-64 VM r=sparky
[gecko.git] / dom / simpledb / ActorsParent.cpp
blob033575ffd0cdccc49f776845a91d2d819cb10f92
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/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<nsIFileStream> mFileStream;
96 nsCOMPtr<nsIRunnable> mCallback;
98 public:
99 StreamHelper(nsIFileStream* aFileStream, nsIRunnable* aCallback);
101 void AsyncClose();
103 private:
104 ~StreamHelper() override;
106 void RunOnBackgroundThread();
108 void RunOnIOThread();
110 NS_DECL_NSIRUNNABLE
113 class Connection final : public PBackgroundSDBConnectionParent {
114 RefPtr<DirectoryLock> mDirectoryLock;
115 nsCOMPtr<nsIFileStream> mFileStream;
116 const PrincipalInfo mPrincipalInfo;
117 nsCString mOrigin;
118 nsString mName;
120 PersistenceType mPersistenceType;
121 bool mRunningRequest;
122 bool mOpen;
123 bool mAllowedToClose;
124 bool mActorDestroyed;
126 public:
127 Connection(PersistenceType aPersistenceType,
128 const PrincipalInfo& aPrincipalInfo);
130 NS_INLINE_DECL_THREADSAFE_REFCOUNTING(mozilla::dom::Connection)
132 Maybe<DirectoryLock&> MaybeDirectoryLockRef() const {
133 AssertIsOnBackgroundThread();
135 return ToMaybeRef(mDirectoryLock.get());
138 nsIFileStream* GetFileStream() const {
139 AssertIsOnIOThread();
141 return mFileStream;
144 PersistenceType GetPersistenceType() const { return mPersistenceType; }
146 const PrincipalInfo& GetPrincipalInfo() const {
147 MOZ_ASSERT(NS_IsMainThread());
149 return mPrincipalInfo;
152 const nsCString& Origin() const {
153 AssertIsOnBackgroundThread();
154 MOZ_ASSERT(!mOrigin.IsEmpty());
156 return mOrigin;
159 const nsString& Name() const {
160 AssertIsOnBackgroundThread();
161 MOZ_ASSERT(!mName.IsEmpty());
163 return mName;
166 void OnNewRequest();
168 void OnRequestFinished();
170 void OnOpen(const nsACString& aOrigin, const nsAString& aName,
171 already_AddRefed<DirectoryLock> aDirectoryLock,
172 already_AddRefed<nsIFileStream> aFileStream);
174 void OnClose();
176 void AllowToClose();
178 private:
179 ~Connection();
181 void MaybeCloseStream();
183 bool VerifyRequestParams(const SDBRequestParams& aParams) const;
185 // IPDL methods.
186 virtual void ActorDestroy(ActorDestroyReason aWhy) override;
188 mozilla::ipc::IPCResult RecvDeleteMe() override;
190 virtual PBackgroundSDBRequestParent* AllocPBackgroundSDBRequestParent(
191 const SDBRequestParams& aParams) override;
193 virtual mozilla::ipc::IPCResult RecvPBackgroundSDBRequestConstructor(
194 PBackgroundSDBRequestParent* aActor,
195 const SDBRequestParams& aParams) override;
197 virtual bool DeallocPBackgroundSDBRequestParent(
198 PBackgroundSDBRequestParent* aActor) override;
201 class ConnectionOperationBase : public Runnable,
202 public PBackgroundSDBRequestParent {
203 nsCOMPtr<nsIEventTarget> mOwningEventTarget;
204 RefPtr<Connection> mConnection;
205 nsresult mResultCode;
206 Atomic<bool> mOperationMayProceed;
207 bool mActorDestroyed;
209 public:
210 nsIEventTarget* OwningEventTarget() const {
211 MOZ_ASSERT(mOwningEventTarget);
213 return mOwningEventTarget;
216 bool IsOnOwningThread() const {
217 MOZ_ASSERT(mOwningEventTarget);
219 bool current;
220 return NS_SUCCEEDED(mOwningEventTarget->IsOnCurrentThread(&current)) &&
221 current;
224 void AssertIsOnOwningThread() const {
225 MOZ_ASSERT(IsOnBackgroundThread());
226 MOZ_ASSERT(IsOnOwningThread());
229 Connection* GetConnection() const {
230 MOZ_ASSERT(mConnection);
232 return mConnection;
235 nsresult ResultCode() const { return mResultCode; }
237 void MaybeSetFailureCode(nsresult aErrorCode) {
238 MOZ_ASSERT(NS_FAILED(aErrorCode));
240 if (NS_SUCCEEDED(mResultCode)) {
241 mResultCode = aErrorCode;
245 // May be called on any thread, but you should call IsActorDestroyed() if
246 // you know you're on the background thread because it is slightly faster.
247 bool OperationMayProceed() const { return mOperationMayProceed; }
249 bool IsActorDestroyed() const {
250 AssertIsOnOwningThread();
252 return mActorDestroyed;
255 // May be overridden by subclasses if they need to perform work on the
256 // background thread before being dispatched but must always call the base
257 // class implementation. Returning false will kill the child actors and
258 // prevent dispatch.
259 virtual bool Init();
261 virtual nsresult Dispatch();
263 // This callback will be called on the background thread before releasing the
264 // final reference to this request object. Subclasses may perform any
265 // additional cleanup here but must always call the base class implementation.
266 virtual void Cleanup();
268 protected:
269 ConnectionOperationBase(Connection* aConnection)
270 : Runnable("dom::ConnectionOperationBase"),
271 mOwningEventTarget(GetCurrentEventTarget()),
272 mConnection(aConnection),
273 mResultCode(NS_OK),
274 mOperationMayProceed(true),
275 mActorDestroyed(false) {
276 AssertIsOnOwningThread();
279 ~ConnectionOperationBase() override;
281 void SendResults();
283 void DatabaseWork();
285 // Methods that subclasses must implement.
286 virtual nsresult DoDatabaseWork(nsIFileStream* aFileStream) = 0;
288 // Subclasses use this override to set the IPDL response value.
289 virtual void GetResponse(SDBRequestResponse& aResponse) = 0;
291 // A method that subclasses may implement.
292 virtual void OnSuccess();
294 private:
295 NS_IMETHOD
296 Run() override;
298 // IPDL methods.
299 void ActorDestroy(ActorDestroyReason aWhy) override;
302 class OpenOp final : public ConnectionOperationBase,
303 public OpenDirectoryListener {
304 enum class State {
305 // Just created on the PBackground thread, dispatched to the main thread.
306 // Next step is FinishOpen.
307 Initial,
309 // Ensuring quota manager is created and opening directory on the
310 // PBackground thread. Next step is either SendingResults if quota manager
311 // is not available or DirectoryOpenPending if quota manager is available.
312 FinishOpen,
314 // Waiting for directory open allowed on the PBackground thread. The next
315 // step is either SendingResults if directory lock failed to acquire, or
316 // DatabaseWorkOpen if directory lock is acquired.
317 DirectoryOpenPending,
319 // Waiting to do/doing work on the QuotaManager IO thread. Its next step is
320 // SendingResults.
321 DatabaseWorkOpen,
323 // Waiting to send/sending results on the PBackground thread. Next step is
324 // Completed.
325 SendingResults,
327 // All done.
328 Completed
331 const SDBRequestOpenParams mParams;
332 RefPtr<DirectoryLock> mDirectoryLock;
333 nsCOMPtr<nsIFileStream> mFileStream;
334 // XXX Consider changing this to ClientMetadata.
335 quota::OriginMetadata mOriginMetadata;
336 State mState;
337 bool mFileStreamOpen;
339 public:
340 OpenOp(Connection* aConnection, const SDBRequestParams& aParams);
342 nsresult Dispatch() override;
344 private:
345 ~OpenOp() override;
347 nsresult Open();
349 nsresult FinishOpen();
351 nsresult SendToIOThread();
353 nsresult DatabaseWork();
355 void StreamClosedCallback();
357 // ConnectionOperationBase overrides
358 nsresult DoDatabaseWork(nsIFileStream* aFileStream) override;
360 void GetResponse(SDBRequestResponse& aResponse) override;
362 void OnSuccess() override;
364 void Cleanup() override;
366 NS_DECL_ISUPPORTS_INHERITED
368 NS_IMETHOD
369 Run() override;
371 // OpenDirectoryListener overrides.
372 void DirectoryLockAcquired(DirectoryLock* aLock) override;
374 void DirectoryLockFailed() override;
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(nsIFileStream* aFileStream) override;
388 void GetResponse(SDBRequestResponse& aResponse) override;
391 class ReadOp final : public ConnectionOperationBase {
392 const SDBRequestReadParams mParams;
394 RefPtr<MemoryOutputStream> mOutputStream;
396 public:
397 ReadOp(Connection* aConnection, const SDBRequestParams& aParams);
399 bool Init() override;
401 private:
402 ~ReadOp() override = default;
404 nsresult DoDatabaseWork(nsIFileStream* aFileStream) override;
406 void GetResponse(SDBRequestResponse& aResponse) override;
409 class WriteOp final : public ConnectionOperationBase {
410 const SDBRequestWriteParams mParams;
412 nsCOMPtr<nsIInputStream> mInputStream;
414 uint64_t mSize;
416 public:
417 WriteOp(Connection* aConnection, const SDBRequestParams& aParams);
419 bool Init() override;
421 private:
422 ~WriteOp() override = default;
424 nsresult DoDatabaseWork(nsIFileStream* aFileStream) override;
426 void GetResponse(SDBRequestResponse& aResponse) override;
429 class CloseOp final : public ConnectionOperationBase {
430 public:
431 explicit CloseOp(Connection* aConnection);
433 private:
434 ~CloseOp() override = default;
436 nsresult DoDatabaseWork(nsIFileStream* aFileStream) override;
438 void GetResponse(SDBRequestResponse& aResponse) override;
440 void OnSuccess() override;
443 /*******************************************************************************
444 * Other class declarations
445 ******************************************************************************/
447 class QuotaClient final : public mozilla::dom::quota::Client {
448 static QuotaClient* sInstance;
450 public:
451 QuotaClient();
453 NS_INLINE_DECL_THREADSAFE_REFCOUNTING(QuotaClient, override)
455 Type GetType() override;
457 Result<UsageInfo, nsresult> InitOrigin(PersistenceType aPersistenceType,
458 const OriginMetadata& aOriginMetadata,
459 const AtomicBool& aCanceled) override;
461 nsresult InitOriginWithoutTracking(PersistenceType aPersistenceType,
462 const OriginMetadata& aOriginMetadata,
463 const AtomicBool& aCanceled) override;
465 Result<UsageInfo, nsresult> GetUsageForOrigin(
466 PersistenceType aPersistenceType, const OriginMetadata& aOriginMetadata,
467 const AtomicBool& aCanceled) override;
469 void OnOriginClearCompleted(PersistenceType aPersistenceType,
470 const nsACString& aOrigin) override;
472 void ReleaseIOThreadObjects() override;
474 void AbortOperationsForLocks(
475 const DirectoryLockIdTable& aDirectoryLockIds) override;
477 void AbortOperationsForProcess(ContentParentId aContentParentId) override;
479 void AbortAllOperations() override;
481 void StartIdleMaintenance() override;
483 void StopIdleMaintenance() override;
485 private:
486 ~QuotaClient() override;
488 void InitiateShutdown() override;
489 bool IsShutdownCompleted() const override;
490 nsCString GetShutdownStatus() const override;
491 void ForceKillActors() override;
492 void FinalizeShutdown() override;
495 /*******************************************************************************
496 * Globals
497 ******************************************************************************/
499 using ConnectionArray = nsTArray<NotNull<RefPtr<Connection>>>;
501 StaticAutoPtr<ConnectionArray> gOpenConnections;
503 template <typename Condition>
504 void AllowToCloseConnectionsMatching(const Condition& aCondition) {
505 AssertIsOnBackgroundThread();
507 if (gOpenConnections) {
508 for (const auto& connection : *gOpenConnections) {
509 if (aCondition(*connection)) {
510 connection->AllowToClose();
516 } // namespace
518 /*******************************************************************************
519 * Exported functions
520 ******************************************************************************/
522 PBackgroundSDBConnectionParent* AllocPBackgroundSDBConnectionParent(
523 const PersistenceType& aPersistenceType,
524 const PrincipalInfo& aPrincipalInfo) {
525 AssertIsOnBackgroundThread();
527 if (NS_WARN_IF(QuotaClient::IsShuttingDownOnBackgroundThread())) {
528 return nullptr;
531 if (NS_WARN_IF(!IsValidPersistenceType(aPersistenceType))) {
532 MOZ_CRASH_UNLESS_FUZZING();
533 return nullptr;
536 if (NS_WARN_IF(aPrincipalInfo.type() == PrincipalInfo::TNullPrincipalInfo)) {
537 MOZ_CRASH_UNLESS_FUZZING();
538 return nullptr;
541 if (NS_WARN_IF(!QuotaManager::IsPrincipalInfoValid(aPrincipalInfo))) {
542 MOZ_CRASH_UNLESS_FUZZING();
543 return nullptr;
546 RefPtr<Connection> actor = new Connection(aPersistenceType, aPrincipalInfo);
548 return actor.forget().take();
551 bool RecvPBackgroundSDBConnectionConstructor(
552 PBackgroundSDBConnectionParent* aActor,
553 const PersistenceType& aPersistenceType,
554 const PrincipalInfo& aPrincipalInfo) {
555 AssertIsOnBackgroundThread();
556 MOZ_ASSERT(aActor);
557 MOZ_ASSERT(!QuotaClient::IsShuttingDownOnBackgroundThread());
559 return true;
562 bool DeallocPBackgroundSDBConnectionParent(
563 PBackgroundSDBConnectionParent* aActor) {
564 AssertIsOnBackgroundThread();
565 MOZ_ASSERT(aActor);
567 RefPtr<Connection> actor = dont_AddRef(static_cast<Connection*>(aActor));
568 return true;
571 namespace simpledb {
573 already_AddRefed<mozilla::dom::quota::Client> CreateQuotaClient() {
574 AssertIsOnBackgroundThread();
576 RefPtr<QuotaClient> client = new QuotaClient();
577 return client.forget();
580 } // namespace simpledb
582 /*******************************************************************************
583 * StreamHelper
584 ******************************************************************************/
586 StreamHelper::StreamHelper(nsIFileStream* aFileStream, nsIRunnable* aCallback)
587 : Runnable("dom::StreamHelper"),
588 mOwningEventTarget(GetCurrentEventTarget()),
589 mFileStream(aFileStream),
590 mCallback(aCallback) {
591 AssertIsOnBackgroundThread();
592 MOZ_ASSERT(aFileStream);
593 MOZ_ASSERT(aCallback);
596 StreamHelper::~StreamHelper() {
597 MOZ_ASSERT(!mFileStream);
598 MOZ_ASSERT(!mCallback);
601 void StreamHelper::AsyncClose() {
602 AssertIsOnBackgroundThread();
604 QuotaManager* quotaManager = QuotaManager::Get();
605 MOZ_ASSERT(quotaManager);
607 MOZ_ALWAYS_SUCCEEDS(
608 quotaManager->IOThread()->Dispatch(this, NS_DISPATCH_NORMAL));
611 void StreamHelper::RunOnBackgroundThread() {
612 AssertIsOnBackgroundThread();
614 nsCOMPtr<nsIFileStream> fileStream;
615 mFileStream.swap(fileStream);
617 nsCOMPtr<nsIRunnable> callback;
618 mCallback.swap(callback);
620 callback->Run();
623 void StreamHelper::RunOnIOThread() {
624 AssertIsOnIOThread();
625 MOZ_ASSERT(mFileStream);
627 nsCOMPtr<nsIInputStream> inputStream = do_QueryInterface(mFileStream);
628 MOZ_ASSERT(inputStream);
630 nsresult rv = inputStream->Close();
631 Unused << NS_WARN_IF(NS_FAILED(rv));
633 MOZ_ALWAYS_SUCCEEDS(mOwningEventTarget->Dispatch(this, NS_DISPATCH_NORMAL));
636 NS_IMETHODIMP
637 StreamHelper::Run() {
638 MOZ_ASSERT(mCallback);
640 if (IsOnBackgroundThread()) {
641 RunOnBackgroundThread();
642 } else {
643 RunOnIOThread();
646 return NS_OK;
649 /*******************************************************************************
650 * Connection
651 ******************************************************************************/
653 Connection::Connection(PersistenceType aPersistenceType,
654 const PrincipalInfo& aPrincipalInfo)
655 : mPrincipalInfo(aPrincipalInfo),
656 mPersistenceType(aPersistenceType),
657 mRunningRequest(false),
658 mOpen(false),
659 mAllowedToClose(false),
660 mActorDestroyed(false) {
661 AssertIsOnBackgroundThread();
662 MOZ_ASSERT(!QuotaClient::IsShuttingDownOnBackgroundThread());
665 Connection::~Connection() {
666 MOZ_ASSERT(!mRunningRequest);
667 MOZ_ASSERT(!mOpen);
668 MOZ_ASSERT(mActorDestroyed);
671 void Connection::OnNewRequest() {
672 AssertIsOnBackgroundThread();
673 MOZ_ASSERT(!mRunningRequest);
675 mRunningRequest = true;
678 void Connection::OnRequestFinished() {
679 AssertIsOnBackgroundThread();
680 MOZ_ASSERT(mRunningRequest);
682 mRunningRequest = false;
684 MaybeCloseStream();
687 void Connection::OnOpen(const nsACString& aOrigin, const nsAString& aName,
688 already_AddRefed<DirectoryLock> aDirectoryLock,
689 already_AddRefed<nsIFileStream> aFileStream) {
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(!mFileStream);
697 MOZ_ASSERT(!mOpen);
699 mOrigin = aOrigin;
700 mName = aName;
701 mDirectoryLock = aDirectoryLock;
702 mFileStream = aFileStream;
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(mFileStream);
717 MOZ_ASSERT(mOpen);
719 mOrigin.Truncate();
720 mName.Truncate();
721 mDirectoryLock = nullptr;
722 mFileStream = 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 = new StreamHelper(mFileStream, callback);
761 helper->AsyncClose();
765 bool Connection::VerifyRequestParams(const SDBRequestParams& aParams) const {
766 AssertIsOnBackgroundThread();
767 MOZ_ASSERT(aParams.type() != SDBRequestParams::T__None);
769 switch (aParams.type()) {
770 case SDBRequestParams::TSDBRequestOpenParams: {
771 if (NS_WARN_IF(mOpen)) {
772 MOZ_CRASH_UNLESS_FUZZING();
773 return false;
776 break;
779 case SDBRequestParams::TSDBRequestSeekParams:
780 case SDBRequestParams::TSDBRequestReadParams:
781 case SDBRequestParams::TSDBRequestWriteParams:
782 case SDBRequestParams::TSDBRequestCloseParams: {
783 if (NS_WARN_IF(!mOpen)) {
784 MOZ_CRASH_UNLESS_FUZZING();
785 return false;
788 break;
791 default:
792 MOZ_CRASH("Should never get here!");
795 return true;
798 void Connection::ActorDestroy(ActorDestroyReason aWhy) {
799 AssertIsOnBackgroundThread();
800 MOZ_ASSERT(!mActorDestroyed);
802 mActorDestroyed = true;
804 AllowToClose();
807 mozilla::ipc::IPCResult Connection::RecvDeleteMe() {
808 AssertIsOnBackgroundThread();
809 MOZ_ASSERT(!mActorDestroyed);
811 IProtocol* mgr = Manager();
812 if (!PBackgroundSDBConnectionParent::Send__delete__(this)) {
813 return IPC_FAIL_NO_REASON(mgr);
816 return IPC_OK();
819 PBackgroundSDBRequestParent* Connection::AllocPBackgroundSDBRequestParent(
820 const SDBRequestParams& aParams) {
821 AssertIsOnBackgroundThread();
822 MOZ_ASSERT(aParams.type() != SDBRequestParams::T__None);
824 if (aParams.type() == SDBRequestParams::TSDBRequestOpenParams &&
825 NS_WARN_IF(QuotaClient::IsShuttingDownOnBackgroundThread())) {
826 return nullptr;
829 if (mAllowedToClose) {
830 return nullptr;
833 #ifdef DEBUG
834 // Always verify parameters in DEBUG builds!
835 bool trustParams = false;
836 #else
837 PBackgroundParent* backgroundActor = Manager();
838 MOZ_ASSERT(backgroundActor);
840 bool trustParams = !BackgroundParent::IsOtherProcessActor(backgroundActor);
841 #endif
843 if (NS_WARN_IF(!trustParams && !VerifyRequestParams(aParams))) {
844 MOZ_CRASH_UNLESS_FUZZING();
845 return nullptr;
848 if (NS_WARN_IF(mRunningRequest)) {
849 MOZ_CRASH_UNLESS_FUZZING();
850 return nullptr;
853 RefPtr<ConnectionOperationBase> actor;
855 switch (aParams.type()) {
856 case SDBRequestParams::TSDBRequestOpenParams:
857 actor = new OpenOp(this, aParams);
858 break;
860 case SDBRequestParams::TSDBRequestSeekParams:
861 actor = new SeekOp(this, aParams);
862 break;
864 case SDBRequestParams::TSDBRequestReadParams:
865 actor = new ReadOp(this, aParams);
866 break;
868 case SDBRequestParams::TSDBRequestWriteParams:
869 actor = new WriteOp(this, aParams);
870 break;
872 case SDBRequestParams::TSDBRequestCloseParams:
873 actor = new CloseOp(this);
874 break;
876 default:
877 MOZ_CRASH("Should never get here!");
880 // Transfer ownership to IPDL.
881 return actor.forget().take();
884 mozilla::ipc::IPCResult Connection::RecvPBackgroundSDBRequestConstructor(
885 PBackgroundSDBRequestParent* aActor, const SDBRequestParams& aParams) {
886 AssertIsOnBackgroundThread();
887 MOZ_ASSERT(aActor);
888 MOZ_ASSERT(aParams.type() != SDBRequestParams::T__None);
889 MOZ_ASSERT_IF(aParams.type() == SDBRequestParams::TSDBRequestOpenParams,
890 !QuotaClient::IsShuttingDownOnBackgroundThread());
891 MOZ_ASSERT(!mAllowedToClose);
892 MOZ_ASSERT(!mRunningRequest);
894 auto* op = static_cast<ConnectionOperationBase*>(aActor);
896 if (NS_WARN_IF(!op->Init())) {
897 op->Cleanup();
898 return IPC_FAIL_NO_REASON(this);
901 if (NS_WARN_IF(NS_FAILED(op->Dispatch()))) {
902 op->Cleanup();
903 return IPC_FAIL_NO_REASON(this);
906 return IPC_OK();
909 bool Connection::DeallocPBackgroundSDBRequestParent(
910 PBackgroundSDBRequestParent* aActor) {
911 AssertIsOnBackgroundThread();
912 MOZ_ASSERT(aActor);
914 // Transfer ownership back from IPDL.
915 RefPtr<ConnectionOperationBase> actor =
916 dont_AddRef(static_cast<ConnectionOperationBase*>(aActor));
917 return true;
920 /*******************************************************************************
921 * ConnectionOperationBase
922 ******************************************************************************/
924 ConnectionOperationBase::~ConnectionOperationBase() {
925 MOZ_ASSERT(
926 !mConnection,
927 "ConnectionOperationBase::Cleanup() was not called by a subclass!");
928 MOZ_ASSERT(mActorDestroyed);
931 bool ConnectionOperationBase::Init() {
932 AssertIsOnBackgroundThread();
933 MOZ_ASSERT(mConnection);
935 mConnection->OnNewRequest();
937 return true;
940 nsresult ConnectionOperationBase::Dispatch() {
941 if (NS_WARN_IF(QuotaClient::IsShuttingDownOnBackgroundThread()) ||
942 IsActorDestroyed()) {
943 return NS_ERROR_ABORT;
946 QuotaManager* quotaManager = QuotaManager::Get();
947 MOZ_ASSERT(quotaManager);
949 nsresult rv = quotaManager->IOThread()->Dispatch(this, NS_DISPATCH_NORMAL);
950 if (NS_WARN_IF(NS_FAILED(rv))) {
951 return rv;
954 return NS_OK;
957 void ConnectionOperationBase::Cleanup() {
958 AssertIsOnOwningThread();
959 MOZ_ASSERT(mConnection);
961 mConnection->OnRequestFinished();
963 mConnection = nullptr;
966 void ConnectionOperationBase::SendResults() {
967 AssertIsOnOwningThread();
969 if (IsActorDestroyed()) {
970 MaybeSetFailureCode(NS_ERROR_FAILURE);
971 } else {
972 SDBRequestResponse response;
974 if (NS_SUCCEEDED(mResultCode)) {
975 GetResponse(response);
977 MOZ_ASSERT(response.type() != SDBRequestResponse::T__None);
978 MOZ_ASSERT(response.type() != SDBRequestResponse::Tnsresult);
979 } else {
980 response = mResultCode;
983 Unused << PBackgroundSDBRequestParent::Send__delete__(this, response);
985 if (NS_SUCCEEDED(mResultCode)) {
986 OnSuccess();
990 Cleanup();
993 void ConnectionOperationBase::DatabaseWork() {
994 AssertIsOnIOThread();
995 MOZ_ASSERT(NS_SUCCEEDED(mResultCode));
997 if (!OperationMayProceed()) {
998 // The operation was canceled in some way, likely because the child process
999 // has crashed.
1000 mResultCode = NS_ERROR_ABORT;
1001 } else {
1002 nsIFileStream* fileStream = mConnection->GetFileStream();
1003 MOZ_ASSERT(fileStream);
1005 nsresult rv = DoDatabaseWork(fileStream);
1006 if (NS_FAILED(rv)) {
1007 mResultCode = rv;
1011 MOZ_ALWAYS_SUCCEEDS(mOwningEventTarget->Dispatch(this, NS_DISPATCH_NORMAL));
1014 void ConnectionOperationBase::OnSuccess() { AssertIsOnOwningThread(); }
1016 NS_IMETHODIMP
1017 ConnectionOperationBase::Run() {
1018 if (IsOnBackgroundThread()) {
1019 SendResults();
1020 } else {
1021 DatabaseWork();
1024 return NS_OK;
1027 void ConnectionOperationBase::ActorDestroy(ActorDestroyReason aWhy) {
1028 AssertIsOnBackgroundThread();
1030 mOperationMayProceed = false;
1031 mActorDestroyed = true;
1034 OpenOp::OpenOp(Connection* aConnection, const SDBRequestParams& aParams)
1035 : ConnectionOperationBase(aConnection),
1036 mParams(aParams.get_SDBRequestOpenParams()),
1037 mState(State::Initial),
1038 mFileStreamOpen(false) {
1039 MOZ_ASSERT(aParams.type() == SDBRequestParams::TSDBRequestOpenParams);
1042 OpenOp::~OpenOp() {
1043 MOZ_ASSERT(!mDirectoryLock);
1044 MOZ_ASSERT(!mFileStream);
1045 MOZ_ASSERT(!mFileStreamOpen);
1046 MOZ_ASSERT_IF(OperationMayProceed(),
1047 mState == State::Initial || mState == State::Completed);
1050 nsresult OpenOp::Dispatch() {
1051 MOZ_ALWAYS_SUCCEEDS(NS_DispatchToMainThread(this));
1053 return NS_OK;
1056 nsresult OpenOp::Open() {
1057 MOZ_ASSERT(NS_IsMainThread());
1058 MOZ_ASSERT(mState == State::Initial);
1060 if (NS_WARN_IF(QuotaClient::IsShuttingDownOnNonBackgroundThread()) ||
1061 !OperationMayProceed()) {
1062 return NS_ERROR_ABORT;
1065 if (NS_WARN_IF(!Preferences::GetBool(kPrefSimpleDBEnabled, false))) {
1066 return NS_ERROR_UNEXPECTED;
1069 PersistenceType persistenceType = GetConnection()->GetPersistenceType();
1071 const PrincipalInfo& principalInfo = GetConnection()->GetPrincipalInfo();
1073 if (principalInfo.type() == PrincipalInfo::TSystemPrincipalInfo) {
1074 mOriginMetadata = {QuotaManager::GetInfoForChrome(), persistenceType};
1075 } else {
1076 MOZ_ASSERT(principalInfo.type() == PrincipalInfo::TContentPrincipalInfo);
1078 QM_TRY_INSPECT(const auto& principal,
1079 PrincipalInfoToPrincipal(principalInfo));
1081 QM_TRY_UNWRAP(auto principalMetadata,
1082 QuotaManager::GetInfoFromPrincipal(principal));
1084 mOriginMetadata = {std::move(principalMetadata), persistenceType};
1087 mState = State::FinishOpen;
1088 MOZ_ALWAYS_SUCCEEDS(OwningEventTarget()->Dispatch(this, NS_DISPATCH_NORMAL));
1090 return NS_OK;
1093 nsresult OpenOp::FinishOpen() {
1094 AssertIsOnOwningThread();
1095 MOZ_ASSERT(!mOriginMetadata.mOrigin.IsEmpty());
1096 MOZ_ASSERT(!mDirectoryLock);
1097 MOZ_ASSERT(mState == State::FinishOpen);
1099 if (NS_WARN_IF(QuotaClient::IsShuttingDownOnBackgroundThread()) ||
1100 IsActorDestroyed()) {
1101 return NS_ERROR_ABORT;
1104 if (gOpenConnections) {
1105 for (const auto& connection : *gOpenConnections) {
1106 if (connection->Origin() == mOriginMetadata.mOrigin &&
1107 connection->Name() == mParams.name()) {
1108 return NS_ERROR_STORAGE_BUSY;
1113 QM_TRY(QuotaManager::EnsureCreated());
1115 // Open the directory
1116 MOZ_ASSERT(QuotaManager::Get());
1118 RefPtr<DirectoryLock> directoryLock =
1119 QuotaManager::Get()->CreateDirectoryLock(
1120 GetConnection()->GetPersistenceType(), mOriginMetadata,
1121 mozilla::dom::quota::Client::SDB,
1122 /* aExclusive */ false);
1124 mState = State::DirectoryOpenPending;
1125 directoryLock->Acquire(this);
1127 return NS_OK;
1130 nsresult OpenOp::SendToIOThread() {
1131 AssertIsOnOwningThread();
1132 MOZ_ASSERT(mState == State::DirectoryOpenPending);
1134 if (NS_WARN_IF(QuotaClient::IsShuttingDownOnBackgroundThread()) ||
1135 IsActorDestroyed()) {
1136 return NS_ERROR_ABORT;
1139 mFileStream =
1140 new FileStream(GetConnection()->GetPersistenceType(), mOriginMetadata,
1141 mozilla::dom::quota::Client::SDB);
1143 QuotaManager* quotaManager = QuotaManager::Get();
1144 MOZ_ASSERT(quotaManager);
1146 // Must set this before dispatching otherwise we will race with the IO thread.
1147 mState = State::DatabaseWorkOpen;
1149 nsresult rv = quotaManager->IOThread()->Dispatch(this, NS_DISPATCH_NORMAL);
1150 if (NS_WARN_IF(NS_FAILED(rv))) {
1151 return rv;
1154 return NS_OK;
1157 nsresult OpenOp::DatabaseWork() {
1158 AssertIsOnIOThread();
1159 MOZ_ASSERT(mState == State::DatabaseWorkOpen);
1160 MOZ_ASSERT(mFileStream);
1161 MOZ_ASSERT(!mFileStreamOpen);
1163 if (NS_WARN_IF(QuotaClient::IsShuttingDownOnNonBackgroundThread()) ||
1164 !OperationMayProceed()) {
1165 return NS_ERROR_ABORT;
1168 QuotaManager* quotaManager = QuotaManager::Get();
1169 MOZ_ASSERT(quotaManager);
1171 QM_TRY(MOZ_TO_RESULT(quotaManager->EnsureStorageIsInitialized()));
1173 QM_TRY_INSPECT(
1174 const auto& dbDirectory,
1175 ([persistenceType = GetConnection()->GetPersistenceType(), &quotaManager,
1176 this]()
1177 -> mozilla::Result<std::pair<nsCOMPtr<nsIFile>, bool>, nsresult> {
1178 if (persistenceType == PERSISTENCE_TYPE_PERSISTENT) {
1179 QM_TRY_RETURN(quotaManager->EnsurePersistentOriginIsInitialized(
1180 mOriginMetadata));
1183 QM_TRY(
1184 MOZ_TO_RESULT(quotaManager->EnsureTemporaryStorageIsInitialized()));
1185 QM_TRY_RETURN(quotaManager->EnsureTemporaryOriginIsInitialized(
1186 persistenceType, mOriginMetadata));
1188 .map([](const auto& res) { return res.first; })));
1190 nsresult rv =
1191 dbDirectory->Append(NS_LITERAL_STRING_FROM_CSTRING(SDB_DIRECTORY_NAME));
1192 if (NS_WARN_IF(NS_FAILED(rv))) {
1193 return rv;
1196 bool exists;
1197 rv = dbDirectory->Exists(&exists);
1198 if (NS_WARN_IF(NS_FAILED(rv))) {
1199 return rv;
1202 if (!exists) {
1203 rv = dbDirectory->Create(nsIFile::DIRECTORY_TYPE, 0755);
1204 if (NS_WARN_IF(NS_FAILED(rv))) {
1205 return rv;
1208 #ifdef DEBUG
1209 else {
1210 bool isDirectory;
1211 MOZ_ASSERT(NS_SUCCEEDED(dbDirectory->IsDirectory(&isDirectory)));
1212 MOZ_ASSERT(isDirectory);
1214 #endif
1216 nsCOMPtr<nsIFile> dbFile;
1217 rv = dbDirectory->Clone(getter_AddRefs(dbFile));
1218 if (NS_WARN_IF(NS_FAILED(rv))) {
1219 return rv;
1222 rv = dbFile->Append(mParams.name() + kSDBSuffix);
1223 if (NS_WARN_IF(NS_FAILED(rv))) {
1224 return rv;
1227 nsString databaseFilePath;
1228 rv = dbFile->GetPath(databaseFilePath);
1229 if (NS_WARN_IF(NS_FAILED(rv))) {
1230 return rv;
1233 rv = mFileStream->Init(dbFile, PR_RDWR | PR_CREATE_FILE, 0644, 0);
1234 if (NS_WARN_IF(NS_FAILED(rv))) {
1235 return rv;
1238 mFileStreamOpen = true;
1240 rv = DoDatabaseWork(mFileStream);
1241 if (NS_WARN_IF(NS_FAILED(rv))) {
1242 return rv;
1245 // Must set mState before dispatching otherwise we will race with the owning
1246 // thread.
1247 mState = State::SendingResults;
1249 rv = OwningEventTarget()->Dispatch(this, NS_DISPATCH_NORMAL);
1250 if (NS_WARN_IF(NS_FAILED(rv))) {
1251 return rv;
1254 return NS_OK;
1257 void OpenOp::StreamClosedCallback() {
1258 AssertIsOnOwningThread();
1259 MOZ_ASSERT(NS_FAILED(ResultCode()));
1260 MOZ_ASSERT(mDirectoryLock);
1261 MOZ_ASSERT(mFileStream);
1262 MOZ_ASSERT(mFileStreamOpen);
1264 mDirectoryLock = nullptr;
1265 mFileStream = nullptr;
1266 mFileStreamOpen = false;
1269 nsresult OpenOp::DoDatabaseWork(nsIFileStream* aFileStream) {
1270 AssertIsOnIOThread();
1272 return NS_OK;
1275 void OpenOp::GetResponse(SDBRequestResponse& aResponse) {
1276 AssertIsOnOwningThread();
1278 aResponse = SDBRequestOpenResponse();
1281 void OpenOp::OnSuccess() {
1282 AssertIsOnOwningThread();
1283 MOZ_ASSERT(NS_SUCCEEDED(ResultCode()));
1284 MOZ_ASSERT(!mOriginMetadata.mOrigin.IsEmpty());
1285 MOZ_ASSERT(mDirectoryLock);
1286 MOZ_ASSERT(mFileStream);
1287 MOZ_ASSERT(mFileStreamOpen);
1289 RefPtr<DirectoryLock> directoryLock;
1290 nsCOMPtr<nsIFileStream> fileStream;
1292 mDirectoryLock.swap(directoryLock);
1293 mFileStream.swap(fileStream);
1294 mFileStreamOpen = false;
1296 GetConnection()->OnOpen(mOriginMetadata.mOrigin, mParams.name(),
1297 directoryLock.forget(), fileStream.forget());
1300 void OpenOp::Cleanup() {
1301 AssertIsOnOwningThread();
1302 MOZ_ASSERT_IF(mFileStreamOpen, mFileStream);
1304 if (mFileStream && mFileStreamOpen) {
1305 // If we have an initialized file stream then the operation must have failed
1306 // and there must be a directory lock too.
1307 MOZ_ASSERT(NS_FAILED(ResultCode()));
1308 MOZ_ASSERT(mDirectoryLock);
1310 // We must close the stream on the I/O thread before releasing it on this
1311 // thread. The directory lock can't be released either.
1312 nsCOMPtr<nsIRunnable> callback =
1313 NewRunnableMethod("dom::OpenOp::StreamClosedCallback", this,
1314 &OpenOp::StreamClosedCallback);
1316 RefPtr<StreamHelper> helper = new StreamHelper(mFileStream, callback);
1317 helper->AsyncClose();
1318 } else {
1319 MOZ_ASSERT(!mFileStreamOpen);
1321 mDirectoryLock = nullptr;
1322 mFileStream = nullptr;
1325 ConnectionOperationBase::Cleanup();
1328 NS_IMPL_ISUPPORTS_INHERITED0(OpenOp, ConnectionOperationBase)
1330 NS_IMETHODIMP
1331 OpenOp::Run() {
1332 nsresult rv;
1334 switch (mState) {
1335 case State::Initial:
1336 rv = Open();
1337 break;
1339 case State::FinishOpen:
1340 rv = FinishOpen();
1341 break;
1343 case State::DatabaseWorkOpen:
1344 rv = DatabaseWork();
1345 break;
1347 case State::SendingResults:
1348 SendResults();
1349 return NS_OK;
1351 default:
1352 MOZ_CRASH("Bad state!");
1355 if (NS_WARN_IF(NS_FAILED(rv)) && mState != State::SendingResults) {
1356 MaybeSetFailureCode(rv);
1358 // Must set mState before dispatching otherwise we will race with the owning
1359 // thread.
1360 mState = State::SendingResults;
1362 if (IsOnOwningThread()) {
1363 SendResults();
1364 } else {
1365 MOZ_ALWAYS_SUCCEEDS(
1366 OwningEventTarget()->Dispatch(this, NS_DISPATCH_NORMAL));
1370 return NS_OK;
1373 void OpenOp::DirectoryLockAcquired(DirectoryLock* aLock) {
1374 AssertIsOnOwningThread();
1375 MOZ_ASSERT(mState == State::DirectoryOpenPending);
1376 MOZ_ASSERT(!mDirectoryLock);
1378 mDirectoryLock = aLock;
1380 nsresult rv = SendToIOThread();
1381 if (NS_WARN_IF(NS_FAILED(rv))) {
1382 MaybeSetFailureCode(rv);
1384 // The caller holds a strong reference to us, no need for a self reference
1385 // before calling Run().
1387 mState = State::SendingResults;
1388 MOZ_ALWAYS_SUCCEEDS(Run());
1390 return;
1394 void OpenOp::DirectoryLockFailed() {
1395 AssertIsOnOwningThread();
1396 MOZ_ASSERT(mState == State::DirectoryOpenPending);
1397 MOZ_ASSERT(!mDirectoryLock);
1399 MaybeSetFailureCode(NS_ERROR_FAILURE);
1401 // The caller holds a strong reference to us, no need for a self reference
1402 // before calling Run().
1404 mState = State::SendingResults;
1405 MOZ_ALWAYS_SUCCEEDS(Run());
1408 SeekOp::SeekOp(Connection* aConnection, const SDBRequestParams& aParams)
1409 : ConnectionOperationBase(aConnection),
1410 mParams(aParams.get_SDBRequestSeekParams()) {
1411 MOZ_ASSERT(aParams.type() == SDBRequestParams::TSDBRequestSeekParams);
1414 nsresult SeekOp::DoDatabaseWork(nsIFileStream* aFileStream) {
1415 AssertIsOnIOThread();
1416 MOZ_ASSERT(aFileStream);
1418 nsCOMPtr<nsISeekableStream> seekableStream = do_QueryInterface(aFileStream);
1419 MOZ_ASSERT(seekableStream);
1421 nsresult rv =
1422 seekableStream->Seek(nsISeekableStream::NS_SEEK_SET, mParams.offset());
1424 if (NS_WARN_IF(NS_FAILED(rv))) {
1425 return rv;
1428 return NS_OK;
1431 void SeekOp::GetResponse(SDBRequestResponse& aResponse) {
1432 aResponse = SDBRequestSeekResponse();
1435 ReadOp::ReadOp(Connection* aConnection, const SDBRequestParams& aParams)
1436 : ConnectionOperationBase(aConnection),
1437 mParams(aParams.get_SDBRequestReadParams()) {
1438 MOZ_ASSERT(aParams.type() == SDBRequestParams::TSDBRequestReadParams);
1441 bool ReadOp::Init() {
1442 AssertIsOnOwningThread();
1444 if (NS_WARN_IF(!ConnectionOperationBase::Init())) {
1445 return false;
1448 mOutputStream = MemoryOutputStream::Create(mParams.size());
1449 if (NS_WARN_IF(!mOutputStream)) {
1450 return false;
1453 return true;
1456 nsresult ReadOp::DoDatabaseWork(nsIFileStream* aFileStream) {
1457 AssertIsOnIOThread();
1458 MOZ_ASSERT(aFileStream);
1460 nsCOMPtr<nsIInputStream> inputStream = do_QueryInterface(aFileStream);
1461 MOZ_ASSERT(inputStream);
1463 nsresult rv;
1465 uint64_t offset = 0;
1467 do {
1468 char copyBuffer[kCopyBufferSize];
1470 uint64_t max = mParams.size() - offset;
1471 if (max == 0) {
1472 break;
1475 uint32_t count = sizeof(copyBuffer);
1476 if (count > max) {
1477 count = max;
1480 uint32_t numRead;
1481 rv = inputStream->Read(copyBuffer, count, &numRead);
1482 if (NS_WARN_IF(NS_FAILED(rv))) {
1483 return rv;
1486 if (!numRead) {
1487 break;
1490 uint32_t numWrite;
1491 rv = mOutputStream->Write(copyBuffer, numRead, &numWrite);
1492 if (NS_WARN_IF(NS_FAILED(rv))) {
1493 return rv;
1496 if (NS_WARN_IF(numWrite != numRead)) {
1497 return NS_ERROR_FAILURE;
1500 offset += numWrite;
1501 } while (true);
1503 MOZ_ASSERT(offset == mParams.size());
1505 MOZ_ALWAYS_SUCCEEDS(mOutputStream->Close());
1507 return NS_OK;
1510 void ReadOp::GetResponse(SDBRequestResponse& aResponse) {
1511 aResponse = SDBRequestReadResponse(mOutputStream->Data());
1514 WriteOp::WriteOp(Connection* aConnection, const SDBRequestParams& aParams)
1515 : ConnectionOperationBase(aConnection),
1516 mParams(aParams.get_SDBRequestWriteParams()),
1517 mSize(0) {
1518 MOZ_ASSERT(aParams.type() == SDBRequestParams::TSDBRequestWriteParams);
1521 bool WriteOp::Init() {
1522 AssertIsOnOwningThread();
1524 if (NS_WARN_IF(!ConnectionOperationBase::Init())) {
1525 return false;
1528 const nsCString& string = mParams.data();
1530 nsCOMPtr<nsIInputStream> inputStream;
1531 nsresult rv = NS_NewCStringInputStream(getter_AddRefs(inputStream), string);
1532 if (NS_WARN_IF(NS_FAILED(rv))) {
1533 return false;
1536 mInputStream = std::move(inputStream);
1537 mSize = string.Length();
1539 return true;
1542 nsresult WriteOp::DoDatabaseWork(nsIFileStream* aFileStream) {
1543 AssertIsOnIOThread();
1544 MOZ_ASSERT(aFileStream);
1546 nsCOMPtr<nsIOutputStream> outputStream = do_QueryInterface(aFileStream);
1547 MOZ_ASSERT(outputStream);
1549 nsresult rv;
1551 do {
1552 char copyBuffer[kCopyBufferSize];
1554 uint32_t numRead;
1555 rv = mInputStream->Read(copyBuffer, sizeof(copyBuffer), &numRead);
1556 if (NS_WARN_IF(NS_FAILED(rv))) {
1557 break;
1560 if (!numRead) {
1561 break;
1564 uint32_t numWrite;
1565 rv = outputStream->Write(copyBuffer, numRead, &numWrite);
1566 if (NS_WARN_IF(NS_FAILED(rv))) {
1567 return rv;
1570 if (NS_WARN_IF(numWrite != numRead)) {
1571 return NS_ERROR_FAILURE;
1573 } while (true);
1575 MOZ_ALWAYS_SUCCEEDS(mInputStream->Close());
1577 return NS_OK;
1580 void WriteOp::GetResponse(SDBRequestResponse& aResponse) {
1581 aResponse = SDBRequestWriteResponse();
1584 CloseOp::CloseOp(Connection* aConnection)
1585 : ConnectionOperationBase(aConnection) {}
1587 nsresult CloseOp::DoDatabaseWork(nsIFileStream* aFileStream) {
1588 AssertIsOnIOThread();
1589 MOZ_ASSERT(aFileStream);
1591 nsCOMPtr<nsIInputStream> inputStream = do_QueryInterface(aFileStream);
1592 MOZ_ASSERT(inputStream);
1594 nsresult rv = inputStream->Close();
1595 if (NS_WARN_IF(NS_FAILED(rv))) {
1596 return rv;
1599 return NS_OK;
1602 void CloseOp::GetResponse(SDBRequestResponse& aResponse) {
1603 aResponse = SDBRequestCloseResponse();
1606 void CloseOp::OnSuccess() {
1607 AssertIsOnOwningThread();
1609 GetConnection()->OnClose();
1612 /*******************************************************************************
1613 * QuotaClient
1614 ******************************************************************************/
1616 QuotaClient* QuotaClient::sInstance = nullptr;
1618 QuotaClient::QuotaClient() {
1619 AssertIsOnBackgroundThread();
1620 MOZ_ASSERT(!sInstance, "We expect this to be a singleton!");
1622 sInstance = this;
1625 QuotaClient::~QuotaClient() {
1626 AssertIsOnBackgroundThread();
1627 MOZ_ASSERT(sInstance == this, "We expect this to be a singleton!");
1629 sInstance = nullptr;
1632 mozilla::dom::quota::Client::Type QuotaClient::GetType() {
1633 return QuotaClient::SDB;
1636 Result<UsageInfo, nsresult> QuotaClient::InitOrigin(
1637 PersistenceType aPersistenceType, const OriginMetadata& aOriginMetadata,
1638 const AtomicBool& aCanceled) {
1639 AssertIsOnIOThread();
1641 return GetUsageForOrigin(aPersistenceType, aOriginMetadata, aCanceled);
1644 nsresult QuotaClient::InitOriginWithoutTracking(
1645 PersistenceType aPersistenceType, const OriginMetadata& aOriginMetadata,
1646 const AtomicBool& aCanceled) {
1647 AssertIsOnIOThread();
1649 return NS_OK;
1652 Result<UsageInfo, nsresult> QuotaClient::GetUsageForOrigin(
1653 PersistenceType aPersistenceType, const OriginMetadata& aOriginMetadata,
1654 const AtomicBool& aCanceled) {
1655 AssertIsOnIOThread();
1657 QuotaManager* quotaManager = QuotaManager::Get();
1658 MOZ_ASSERT(quotaManager);
1660 QM_TRY_UNWRAP(auto directory, quotaManager->GetDirectoryForOrigin(
1661 aPersistenceType, aOriginMetadata.mOrigin));
1663 MOZ_ASSERT(directory);
1665 nsresult rv =
1666 directory->Append(NS_LITERAL_STRING_FROM_CSTRING(SDB_DIRECTORY_NAME));
1667 if (NS_WARN_IF(NS_FAILED(rv))) {
1668 return Err(rv);
1671 DebugOnly<bool> exists;
1672 MOZ_ASSERT(NS_SUCCEEDED(directory->Exists(&exists)) && exists);
1674 QM_TRY_RETURN(ReduceEachFileAtomicCancelable(
1675 *directory, aCanceled, UsageInfo{},
1676 [](UsageInfo usageInfo,
1677 const nsCOMPtr<nsIFile>& file) -> Result<UsageInfo, nsresult> {
1678 QM_TRY_INSPECT(const bool& isDirectory,
1679 MOZ_TO_RESULT_INVOKE_MEMBER(file, IsDirectory));
1681 if (isDirectory) {
1682 Unused << WARN_IF_FILE_IS_UNKNOWN(*file);
1683 return usageInfo;
1686 nsString leafName;
1687 QM_TRY(MOZ_TO_RESULT(file->GetLeafName(leafName)));
1689 if (StringEndsWith(leafName, kSDBSuffix)) {
1690 QM_TRY_INSPECT(const int64_t& fileSize,
1691 MOZ_TO_RESULT_INVOKE_MEMBER(file, GetFileSize));
1693 MOZ_ASSERT(fileSize >= 0);
1695 return usageInfo +
1696 UsageInfo{DatabaseUsageType(Some(uint64_t(fileSize)))};
1699 Unused << WARN_IF_FILE_IS_UNKNOWN(*file);
1701 return usageInfo;
1702 }));
1705 void QuotaClient::OnOriginClearCompleted(PersistenceType aPersistenceType,
1706 const nsACString& aOrigin) {
1707 AssertIsOnIOThread();
1710 void QuotaClient::ReleaseIOThreadObjects() { AssertIsOnIOThread(); }
1712 void QuotaClient::AbortOperationsForLocks(
1713 const DirectoryLockIdTable& aDirectoryLockIds) {
1714 AssertIsOnBackgroundThread();
1716 AllowToCloseConnectionsMatching([&aDirectoryLockIds](const auto& connection) {
1717 // If the connections is registered in gOpenConnections then it must have
1718 // a directory lock.
1719 return IsLockForObjectContainedInLockTable(connection, aDirectoryLockIds);
1723 void QuotaClient::AbortOperationsForProcess(ContentParentId aContentParentId) {
1724 AssertIsOnBackgroundThread();
1727 void QuotaClient::AbortAllOperations() {
1728 AssertIsOnBackgroundThread();
1730 AllowToCloseConnectionsMatching([](const auto&) { return true; });
1733 void QuotaClient::StartIdleMaintenance() { AssertIsOnBackgroundThread(); }
1735 void QuotaClient::StopIdleMaintenance() { AssertIsOnBackgroundThread(); }
1737 void QuotaClient::InitiateShutdown() {
1738 AssertIsOnBackgroundThread();
1740 if (gOpenConnections) {
1741 for (const auto& connection : *gOpenConnections) {
1742 connection->AllowToClose();
1747 bool QuotaClient::IsShutdownCompleted() const { return !gOpenConnections; }
1749 void QuotaClient::ForceKillActors() {
1750 // Currently we don't implement killing actors (are there any to kill here?).
1753 nsCString QuotaClient::GetShutdownStatus() const {
1754 // XXX Gather information here.
1755 return "To be implemented"_ns;
1758 void QuotaClient::FinalizeShutdown() {
1759 // Nothing to do here.
1762 } // namespace mozilla::dom