summaryrefslogtreecommitdiffstats
path: root/dom/simpledb/ActorsParent.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'dom/simpledb/ActorsParent.cpp')
-rw-r--r--dom/simpledb/ActorsParent.cpp1782
1 files changed, 1782 insertions, 0 deletions
diff --git a/dom/simpledb/ActorsParent.cpp b/dom/simpledb/ActorsParent.cpp
new file mode 100644
index 0000000000..7864cd31f4
--- /dev/null
+++ b/dom/simpledb/ActorsParent.cpp
@@ -0,0 +1,1782 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "ActorsParent.h"
+
+// Local includes
+#include "SimpleDBCommon.h"
+
+// Global includes
+#include <cstdint>
+#include <cstdlib>
+#include <new>
+#include <utility>
+#include "ErrorList.h"
+#include "MainThreadUtils.h"
+#include "mozilla/AlreadyAddRefed.h"
+#include "mozilla/Assertions.h"
+#include "mozilla/Atomics.h"
+#include "mozilla/DebugOnly.h"
+#include "mozilla/FixedBufferOutputStream.h"
+#include "mozilla/Maybe.h"
+#include "mozilla/Preferences.h"
+#include "mozilla/RefPtr.h"
+#include "mozilla/Result.h"
+#include "mozilla/ResultExtensions.h"
+#include "mozilla/SpinEventLoopUntil.h"
+#include "mozilla/StaticPtr.h"
+#include "mozilla/Unused.h"
+#include "mozilla/Variant.h"
+#include "mozilla/dom/PBackgroundSDBConnection.h"
+#include "mozilla/dom/PBackgroundSDBConnectionParent.h"
+#include "mozilla/dom/PBackgroundSDBRequestParent.h"
+#include "mozilla/dom/ipc/IdType.h"
+#include "mozilla/dom/quota/Client.h"
+#include "mozilla/dom/quota/ClientImpl.h"
+#include "mozilla/dom/quota/DirectoryLock.h"
+#include "mozilla/dom/quota/FileStreams.h"
+#include "mozilla/dom/quota/QuotaCommon.h"
+#include "mozilla/dom/quota/QuotaManager.h"
+#include "mozilla/dom/quota/ResultExtensions.h"
+#include "mozilla/dom/quota/UsageInfo.h"
+#include "mozilla/ipc/BackgroundParent.h"
+#include "mozilla/ipc/BackgroundUtils.h"
+#include "mozilla/ipc/PBackgroundParent.h"
+#include "mozilla/ipc/PBackgroundSharedTypes.h"
+#include "mozilla/ipc/ProtocolUtils.h"
+#include "nsCOMPtr.h"
+#include "nsDebug.h"
+#include "nsError.h"
+#include "nsIDirectoryEnumerator.h"
+#include "nsIEventTarget.h"
+#include "nsIFile.h"
+#include "nsIFileStreams.h"
+#include "nsIInputStream.h"
+#include "nsIOutputStream.h"
+#include "nsIRunnable.h"
+#include "nsISeekableStream.h"
+#include "nsISupports.h"
+#include "nsIThread.h"
+#include "nsLiteralString.h"
+#include "nsString.h"
+#include "nsStringFwd.h"
+#include "nsStringStream.h"
+#include "nsTArray.h"
+#include "nsTLiteralString.h"
+#include "nsTStringRepr.h"
+#include "nsThreadUtils.h"
+#include "nscore.h"
+#include "prio.h"
+
+namespace mozilla::dom {
+
+using namespace mozilla::dom::quota;
+using namespace mozilla::ipc;
+
+namespace {
+
+/*******************************************************************************
+ * Constants
+ ******************************************************************************/
+
+const uint32_t kCopyBufferSize = 32768;
+
+constexpr auto kSDBSuffix = u".sdb"_ns;
+
+/*******************************************************************************
+ * Actor class declarations
+ ******************************************************************************/
+
+class StreamHelper final : public Runnable {
+ nsCOMPtr<nsIEventTarget> mOwningEventTarget;
+ nsCOMPtr<nsIFileRandomAccessStream> mFileRandomAccessStream;
+ nsCOMPtr<nsIRunnable> mCallback;
+
+ public:
+ StreamHelper(nsIFileRandomAccessStream* aFileRandomAccessStream,
+ nsIRunnable* aCallback);
+
+ void AsyncClose();
+
+ private:
+ ~StreamHelper() override;
+
+ void RunOnBackgroundThread();
+
+ void RunOnIOThread();
+
+ NS_DECL_NSIRUNNABLE
+};
+
+class Connection final : public PBackgroundSDBConnectionParent {
+ RefPtr<DirectoryLock> mDirectoryLock;
+ nsCOMPtr<nsIFileRandomAccessStream> mFileRandomAccessStream;
+ const PrincipalInfo mPrincipalInfo;
+ nsCString mOrigin;
+ nsString mName;
+
+ PersistenceType mPersistenceType;
+ bool mRunningRequest;
+ bool mOpen;
+ bool mAllowedToClose;
+ bool mActorDestroyed;
+
+ public:
+ Connection(PersistenceType aPersistenceType,
+ const PrincipalInfo& aPrincipalInfo);
+
+ NS_INLINE_DECL_THREADSAFE_REFCOUNTING(mozilla::dom::Connection, override)
+
+ Maybe<DirectoryLock&> MaybeDirectoryLockRef() const {
+ AssertIsOnBackgroundThread();
+
+ return ToMaybeRef(mDirectoryLock.get());
+ }
+
+ nsIFileRandomAccessStream* GetFileRandomAccessStream() const {
+ AssertIsOnIOThread();
+
+ return mFileRandomAccessStream;
+ }
+
+ PersistenceType GetPersistenceType() const { return mPersistenceType; }
+
+ const PrincipalInfo& GetPrincipalInfo() const {
+ AssertIsOnBackgroundThread();
+
+ return mPrincipalInfo;
+ }
+
+ const nsCString& Origin() const {
+ AssertIsOnBackgroundThread();
+ MOZ_ASSERT(!mOrigin.IsEmpty());
+
+ return mOrigin;
+ }
+
+ const nsString& Name() const {
+ AssertIsOnBackgroundThread();
+ MOZ_ASSERT(!mName.IsEmpty());
+
+ return mName;
+ }
+
+ void OnNewRequest();
+
+ void OnRequestFinished();
+
+ void OnOpen(
+ const nsACString& aOrigin, const nsAString& aName,
+ already_AddRefed<DirectoryLock> aDirectoryLock,
+ already_AddRefed<nsIFileRandomAccessStream> aFileRandomAccessStream);
+
+ void OnClose();
+
+ void AllowToClose();
+
+ private:
+ ~Connection();
+
+ void MaybeCloseStream();
+
+ bool VerifyRequestParams(const SDBRequestParams& aParams) const;
+
+ // IPDL methods.
+ virtual void ActorDestroy(ActorDestroyReason aWhy) override;
+
+ mozilla::ipc::IPCResult RecvDeleteMe() override;
+
+ virtual PBackgroundSDBRequestParent* AllocPBackgroundSDBRequestParent(
+ const SDBRequestParams& aParams) override;
+
+ virtual mozilla::ipc::IPCResult RecvPBackgroundSDBRequestConstructor(
+ PBackgroundSDBRequestParent* aActor,
+ const SDBRequestParams& aParams) override;
+
+ virtual bool DeallocPBackgroundSDBRequestParent(
+ PBackgroundSDBRequestParent* aActor) override;
+};
+
+class ConnectionOperationBase : public Runnable,
+ public PBackgroundSDBRequestParent {
+ nsCOMPtr<nsIEventTarget> mOwningEventTarget;
+ RefPtr<Connection> mConnection;
+ nsresult mResultCode;
+ Atomic<bool> mOperationMayProceed;
+ bool mActorDestroyed;
+
+ public:
+ nsIEventTarget* OwningEventTarget() const {
+ MOZ_ASSERT(mOwningEventTarget);
+
+ return mOwningEventTarget;
+ }
+
+ bool IsOnOwningThread() const {
+ MOZ_ASSERT(mOwningEventTarget);
+
+ bool current;
+ return NS_SUCCEEDED(mOwningEventTarget->IsOnCurrentThread(&current)) &&
+ current;
+ }
+
+ void AssertIsOnOwningThread() const {
+ MOZ_ASSERT(IsOnBackgroundThread());
+ MOZ_ASSERT(IsOnOwningThread());
+ }
+
+ Connection* GetConnection() const {
+ MOZ_ASSERT(mConnection);
+
+ return mConnection;
+ }
+
+ nsresult ResultCode() const { return mResultCode; }
+
+ void MaybeSetFailureCode(nsresult aErrorCode) {
+ MOZ_ASSERT(NS_FAILED(aErrorCode));
+
+ if (NS_SUCCEEDED(mResultCode)) {
+ mResultCode = aErrorCode;
+ }
+ }
+
+ // May be called on any thread, but you should call IsActorDestroyed() if
+ // you know you're on the background thread because it is slightly faster.
+ bool OperationMayProceed() const { return mOperationMayProceed; }
+
+ bool IsActorDestroyed() const {
+ AssertIsOnOwningThread();
+
+ return mActorDestroyed;
+ }
+
+ // May be overridden by subclasses if they need to perform work on the
+ // background thread before being dispatched but must always call the base
+ // class implementation. Returning false will kill the child actors and
+ // prevent dispatch.
+ virtual bool Init();
+
+ virtual nsresult Dispatch();
+
+ // This callback will be called on the background thread before releasing the
+ // final reference to this request object. Subclasses may perform any
+ // additional cleanup here but must always call the base class implementation.
+ virtual void Cleanup();
+
+ protected:
+ ConnectionOperationBase(Connection* aConnection)
+ : Runnable("dom::ConnectionOperationBase"),
+ mOwningEventTarget(GetCurrentSerialEventTarget()),
+ mConnection(aConnection),
+ mResultCode(NS_OK),
+ mOperationMayProceed(true),
+ mActorDestroyed(false) {
+ AssertIsOnOwningThread();
+ }
+
+ ~ConnectionOperationBase() override;
+
+ void SendResults();
+
+ void DatabaseWork();
+
+ // Methods that subclasses must implement.
+ virtual nsresult DoDatabaseWork(
+ nsIFileRandomAccessStream* aFileRandomAccessStream) = 0;
+
+ // Subclasses use this override to set the IPDL response value.
+ virtual void GetResponse(SDBRequestResponse& aResponse) = 0;
+
+ // A method that subclasses may implement.
+ virtual void OnSuccess();
+
+ private:
+ NS_IMETHOD
+ Run() override;
+
+ // IPDL methods.
+ void ActorDestroy(ActorDestroyReason aWhy) override;
+};
+
+class OpenOp final : public ConnectionOperationBase {
+ enum class State {
+ // Just created on the PBackground thread, dispatched to the main thread.
+ // Next step is FinishOpen.
+ Initial,
+
+ // Ensuring quota manager is created and opening directory on the
+ // PBackground thread. Next step is either SendingResults if quota manager
+ // is not available or DirectoryOpenPending if quota manager is available.
+ FinishOpen,
+
+ // Waiting for directory open allowed on the PBackground thread. The next
+ // step is either SendingResults if directory lock failed to acquire, or
+ // DatabaseWorkOpen if directory lock is acquired.
+ DirectoryOpenPending,
+
+ // Waiting to do/doing work on the QuotaManager IO thread. Its next step is
+ // SendingResults.
+ DatabaseWorkOpen,
+
+ // Waiting to send/sending results on the PBackground thread. Next step is
+ // Completed.
+ SendingResults,
+
+ // All done.
+ Completed
+ };
+
+ const SDBRequestOpenParams mParams;
+ RefPtr<DirectoryLock> mDirectoryLock;
+ nsCOMPtr<nsIFileRandomAccessStream> mFileRandomAccessStream;
+ // XXX Consider changing this to ClientMetadata.
+ quota::OriginMetadata mOriginMetadata;
+ State mState;
+ bool mFileRandomAccessStreamOpen;
+
+ public:
+ OpenOp(Connection* aConnection, const SDBRequestParams& aParams);
+
+ nsresult Dispatch() override;
+
+ private:
+ ~OpenOp() override;
+
+ nsresult Open();
+
+ nsresult FinishOpen();
+
+ nsresult SendToIOThread();
+
+ nsresult DatabaseWork();
+
+ void StreamClosedCallback();
+
+ // ConnectionOperationBase overrides
+ nsresult DoDatabaseWork(
+ nsIFileRandomAccessStream* aFileRandomAccessStream) override;
+
+ void GetResponse(SDBRequestResponse& aResponse) override;
+
+ void OnSuccess() override;
+
+ void Cleanup() override;
+
+ NS_IMETHOD
+ Run() override;
+
+ void DirectoryLockAcquired(DirectoryLock* aLock);
+
+ void DirectoryLockFailed();
+};
+
+class SeekOp final : public ConnectionOperationBase {
+ const SDBRequestSeekParams mParams;
+
+ public:
+ SeekOp(Connection* aConnection, const SDBRequestParams& aParams);
+
+ private:
+ ~SeekOp() override = default;
+
+ nsresult DoDatabaseWork(
+ nsIFileRandomAccessStream* aFileRandomAccessStream) override;
+
+ void GetResponse(SDBRequestResponse& aResponse) override;
+};
+
+class ReadOp final : public ConnectionOperationBase {
+ const SDBRequestReadParams mParams;
+
+ RefPtr<FixedBufferOutputStream> mOutputStream;
+
+ public:
+ ReadOp(Connection* aConnection, const SDBRequestParams& aParams);
+
+ bool Init() override;
+
+ private:
+ ~ReadOp() override = default;
+
+ nsresult DoDatabaseWork(
+ nsIFileRandomAccessStream* aFileRandomAccessStream) override;
+
+ void GetResponse(SDBRequestResponse& aResponse) override;
+};
+
+class WriteOp final : public ConnectionOperationBase {
+ const SDBRequestWriteParams mParams;
+
+ nsCOMPtr<nsIInputStream> mInputStream;
+
+ uint64_t mSize;
+
+ public:
+ WriteOp(Connection* aConnection, const SDBRequestParams& aParams);
+
+ bool Init() override;
+
+ private:
+ ~WriteOp() override = default;
+
+ nsresult DoDatabaseWork(
+ nsIFileRandomAccessStream* aFileRandomAccessStream) override;
+
+ void GetResponse(SDBRequestResponse& aResponse) override;
+};
+
+class CloseOp final : public ConnectionOperationBase {
+ public:
+ explicit CloseOp(Connection* aConnection);
+
+ private:
+ ~CloseOp() override = default;
+
+ nsresult DoDatabaseWork(
+ nsIFileRandomAccessStream* aFileRandomAccessStream) override;
+
+ void GetResponse(SDBRequestResponse& aResponse) override;
+
+ void OnSuccess() override;
+};
+
+/*******************************************************************************
+ * Other class declarations
+ ******************************************************************************/
+
+class QuotaClient final : public mozilla::dom::quota::Client {
+ static QuotaClient* sInstance;
+
+ public:
+ QuotaClient();
+
+ NS_INLINE_DECL_THREADSAFE_REFCOUNTING(QuotaClient, override)
+
+ Type GetType() override;
+
+ Result<UsageInfo, nsresult> InitOrigin(PersistenceType aPersistenceType,
+ const OriginMetadata& aOriginMetadata,
+ const AtomicBool& aCanceled) override;
+
+ nsresult InitOriginWithoutTracking(PersistenceType aPersistenceType,
+ const OriginMetadata& aOriginMetadata,
+ const AtomicBool& aCanceled) override;
+
+ Result<UsageInfo, nsresult> GetUsageForOrigin(
+ PersistenceType aPersistenceType, const OriginMetadata& aOriginMetadata,
+ const AtomicBool& aCanceled) override;
+
+ void OnOriginClearCompleted(PersistenceType aPersistenceType,
+ const nsACString& aOrigin) override;
+
+ void OnRepositoryClearCompleted(PersistenceType aPersistenceType) override;
+
+ void ReleaseIOThreadObjects() override;
+
+ void AbortOperationsForLocks(
+ const DirectoryLockIdTable& aDirectoryLockIds) override;
+
+ void AbortOperationsForProcess(ContentParentId aContentParentId) override;
+
+ void AbortAllOperations() override;
+
+ void StartIdleMaintenance() override;
+
+ void StopIdleMaintenance() override;
+
+ private:
+ ~QuotaClient() override;
+
+ void InitiateShutdown() override;
+ bool IsShutdownCompleted() const override;
+ nsCString GetShutdownStatus() const override;
+ void ForceKillActors() override;
+ void FinalizeShutdown() override;
+};
+
+/*******************************************************************************
+ * Globals
+ ******************************************************************************/
+
+using ConnectionArray = nsTArray<NotNull<RefPtr<Connection>>>;
+
+StaticAutoPtr<ConnectionArray> gOpenConnections;
+
+template <typename Condition>
+void AllowToCloseConnectionsMatching(const Condition& aCondition) {
+ AssertIsOnBackgroundThread();
+
+ if (gOpenConnections) {
+ for (const auto& connection : *gOpenConnections) {
+ if (aCondition(*connection)) {
+ connection->AllowToClose();
+ }
+ }
+ }
+}
+
+} // namespace
+
+/*******************************************************************************
+ * Exported functions
+ ******************************************************************************/
+
+already_AddRefed<PBackgroundSDBConnectionParent>
+AllocPBackgroundSDBConnectionParent(const PersistenceType& aPersistenceType,
+ const PrincipalInfo& aPrincipalInfo) {
+ AssertIsOnBackgroundThread();
+
+ if (NS_WARN_IF(QuotaClient::IsShuttingDownOnBackgroundThread())) {
+ return nullptr;
+ }
+
+ if (NS_WARN_IF(!IsValidPersistenceType(aPersistenceType))) {
+ MOZ_CRASH_UNLESS_FUZZING();
+ return nullptr;
+ }
+
+ if (NS_WARN_IF(aPrincipalInfo.type() == PrincipalInfo::TNullPrincipalInfo)) {
+ MOZ_CRASH_UNLESS_FUZZING();
+ return nullptr;
+ }
+
+ if (NS_WARN_IF(!QuotaManager::IsPrincipalInfoValid(aPrincipalInfo))) {
+ MOZ_CRASH_UNLESS_FUZZING();
+ return nullptr;
+ }
+
+ RefPtr<Connection> actor = new Connection(aPersistenceType, aPrincipalInfo);
+
+ return actor.forget();
+}
+
+bool RecvPBackgroundSDBConnectionConstructor(
+ PBackgroundSDBConnectionParent* aActor,
+ const PersistenceType& aPersistenceType,
+ const PrincipalInfo& aPrincipalInfo) {
+ AssertIsOnBackgroundThread();
+ MOZ_ASSERT(aActor);
+ MOZ_ASSERT(!QuotaClient::IsShuttingDownOnBackgroundThread());
+
+ return true;
+}
+
+namespace simpledb {
+
+already_AddRefed<mozilla::dom::quota::Client> CreateQuotaClient() {
+ AssertIsOnBackgroundThread();
+
+ RefPtr<QuotaClient> client = new QuotaClient();
+ return client.forget();
+}
+
+} // namespace simpledb
+
+/*******************************************************************************
+ * StreamHelper
+ ******************************************************************************/
+
+StreamHelper::StreamHelper(nsIFileRandomAccessStream* aFileRandomAccessStream,
+ nsIRunnable* aCallback)
+ : Runnable("dom::StreamHelper"),
+ mOwningEventTarget(GetCurrentSerialEventTarget()),
+ mFileRandomAccessStream(aFileRandomAccessStream),
+ mCallback(aCallback) {
+ AssertIsOnBackgroundThread();
+ MOZ_ASSERT(aFileRandomAccessStream);
+ MOZ_ASSERT(aCallback);
+}
+
+StreamHelper::~StreamHelper() {
+ MOZ_ASSERT(!mFileRandomAccessStream);
+ MOZ_ASSERT(!mCallback);
+}
+
+void StreamHelper::AsyncClose() {
+ AssertIsOnBackgroundThread();
+
+ QuotaManager* quotaManager = QuotaManager::Get();
+ MOZ_ASSERT(quotaManager);
+
+ MOZ_ALWAYS_SUCCEEDS(
+ quotaManager->IOThread()->Dispatch(this, NS_DISPATCH_NORMAL));
+}
+
+void StreamHelper::RunOnBackgroundThread() {
+ AssertIsOnBackgroundThread();
+
+ nsCOMPtr<nsIFileRandomAccessStream> fileRandomAccessStream;
+ mFileRandomAccessStream.swap(fileRandomAccessStream);
+
+ nsCOMPtr<nsIRunnable> callback;
+ mCallback.swap(callback);
+
+ callback->Run();
+}
+
+void StreamHelper::RunOnIOThread() {
+ AssertIsOnIOThread();
+ MOZ_ASSERT(mFileRandomAccessStream);
+
+ nsCOMPtr<nsIInputStream> inputStream =
+ do_QueryInterface(mFileRandomAccessStream);
+ MOZ_ASSERT(inputStream);
+
+ nsresult rv = inputStream->Close();
+ Unused << NS_WARN_IF(NS_FAILED(rv));
+
+ MOZ_ALWAYS_SUCCEEDS(mOwningEventTarget->Dispatch(this, NS_DISPATCH_NORMAL));
+}
+
+NS_IMETHODIMP
+StreamHelper::Run() {
+ MOZ_ASSERT(mCallback);
+
+ if (IsOnBackgroundThread()) {
+ RunOnBackgroundThread();
+ } else {
+ RunOnIOThread();
+ }
+
+ return NS_OK;
+}
+
+/*******************************************************************************
+ * Connection
+ ******************************************************************************/
+
+Connection::Connection(PersistenceType aPersistenceType,
+ const PrincipalInfo& aPrincipalInfo)
+ : mPrincipalInfo(aPrincipalInfo),
+ mPersistenceType(aPersistenceType),
+ mRunningRequest(false),
+ mOpen(false),
+ mAllowedToClose(false),
+ mActorDestroyed(false) {
+ AssertIsOnBackgroundThread();
+ MOZ_ASSERT(!QuotaClient::IsShuttingDownOnBackgroundThread());
+}
+
+Connection::~Connection() {
+ MOZ_ASSERT(!mRunningRequest);
+ MOZ_ASSERT(!mOpen);
+ MOZ_ASSERT(mActorDestroyed);
+}
+
+void Connection::OnNewRequest() {
+ AssertIsOnBackgroundThread();
+ MOZ_ASSERT(!mRunningRequest);
+
+ mRunningRequest = true;
+}
+
+void Connection::OnRequestFinished() {
+ AssertIsOnBackgroundThread();
+ MOZ_ASSERT(mRunningRequest);
+
+ mRunningRequest = false;
+
+ MaybeCloseStream();
+}
+
+void Connection::OnOpen(
+ const nsACString& aOrigin, const nsAString& aName,
+ already_AddRefed<DirectoryLock> aDirectoryLock,
+ already_AddRefed<nsIFileRandomAccessStream> aFileRandomAccessStream) {
+ AssertIsOnBackgroundThread();
+ MOZ_ASSERT(!aOrigin.IsEmpty());
+ MOZ_ASSERT(!aName.IsEmpty());
+ MOZ_ASSERT(mOrigin.IsEmpty());
+ MOZ_ASSERT(mName.IsEmpty());
+ MOZ_ASSERT(!mDirectoryLock);
+ MOZ_ASSERT(!mFileRandomAccessStream);
+ MOZ_ASSERT(!mOpen);
+
+ mOrigin = aOrigin;
+ mName = aName;
+ mDirectoryLock = aDirectoryLock;
+ mFileRandomAccessStream = aFileRandomAccessStream;
+ mOpen = true;
+
+ if (!gOpenConnections) {
+ gOpenConnections = new ConnectionArray();
+ }
+
+ gOpenConnections->AppendElement(WrapNotNullUnchecked(this));
+}
+
+void Connection::OnClose() {
+ AssertIsOnBackgroundThread();
+ MOZ_ASSERT(!mOrigin.IsEmpty());
+ MOZ_ASSERT(mDirectoryLock);
+ MOZ_ASSERT(mFileRandomAccessStream);
+ MOZ_ASSERT(mOpen);
+
+ mOrigin.Truncate();
+ mName.Truncate();
+ mDirectoryLock = nullptr;
+ mFileRandomAccessStream = nullptr;
+ mOpen = false;
+
+ MOZ_ASSERT(gOpenConnections);
+ gOpenConnections->RemoveElement(this);
+
+ if (gOpenConnections->IsEmpty()) {
+ gOpenConnections = nullptr;
+ }
+
+ if (mAllowedToClose && !mActorDestroyed) {
+ Unused << SendClosed();
+ }
+}
+
+void Connection::AllowToClose() {
+ AssertIsOnBackgroundThread();
+
+ if (mAllowedToClose) {
+ return;
+ }
+
+ mAllowedToClose = true;
+
+ if (!mActorDestroyed) {
+ Unused << SendAllowToClose();
+ }
+
+ MaybeCloseStream();
+}
+
+void Connection::MaybeCloseStream() {
+ AssertIsOnBackgroundThread();
+
+ if (!mRunningRequest && mOpen && mAllowedToClose) {
+ nsCOMPtr<nsIRunnable> callback = NewRunnableMethod(
+ "dom::Connection::OnClose", this, &Connection::OnClose);
+
+ RefPtr<StreamHelper> helper =
+ new StreamHelper(mFileRandomAccessStream, callback);
+ helper->AsyncClose();
+ }
+}
+
+bool Connection::VerifyRequestParams(const SDBRequestParams& aParams) const {
+ AssertIsOnBackgroundThread();
+ MOZ_ASSERT(aParams.type() != SDBRequestParams::T__None);
+
+ switch (aParams.type()) {
+ case SDBRequestParams::TSDBRequestOpenParams: {
+ if (NS_WARN_IF(mOpen)) {
+ MOZ_CRASH_UNLESS_FUZZING();
+ return false;
+ }
+
+ break;
+ }
+
+ case SDBRequestParams::TSDBRequestSeekParams:
+ case SDBRequestParams::TSDBRequestReadParams:
+ case SDBRequestParams::TSDBRequestWriteParams:
+ case SDBRequestParams::TSDBRequestCloseParams: {
+ if (NS_WARN_IF(!mOpen)) {
+ MOZ_CRASH_UNLESS_FUZZING();
+ return false;
+ }
+
+ break;
+ }
+
+ default:
+ MOZ_CRASH("Should never get here!");
+ }
+
+ return true;
+}
+
+void Connection::ActorDestroy(ActorDestroyReason aWhy) {
+ AssertIsOnBackgroundThread();
+ MOZ_ASSERT(!mActorDestroyed);
+
+ mActorDestroyed = true;
+
+ AllowToClose();
+}
+
+mozilla::ipc::IPCResult Connection::RecvDeleteMe() {
+ AssertIsOnBackgroundThread();
+ MOZ_ASSERT(!mActorDestroyed);
+
+ IProtocol* mgr = Manager();
+ if (!PBackgroundSDBConnectionParent::Send__delete__(this)) {
+ return IPC_FAIL_NO_REASON(mgr);
+ }
+
+ return IPC_OK();
+}
+
+PBackgroundSDBRequestParent* Connection::AllocPBackgroundSDBRequestParent(
+ const SDBRequestParams& aParams) {
+ AssertIsOnBackgroundThread();
+ MOZ_ASSERT(aParams.type() != SDBRequestParams::T__None);
+
+ if (aParams.type() == SDBRequestParams::TSDBRequestOpenParams &&
+ NS_WARN_IF(QuotaClient::IsShuttingDownOnBackgroundThread())) {
+ return nullptr;
+ }
+
+ if (mAllowedToClose) {
+ return nullptr;
+ }
+
+#ifdef DEBUG
+ // Always verify parameters in DEBUG builds!
+ bool trustParams = false;
+#else
+ PBackgroundParent* backgroundActor = Manager();
+ MOZ_ASSERT(backgroundActor);
+
+ bool trustParams = !BackgroundParent::IsOtherProcessActor(backgroundActor);
+#endif
+
+ if (NS_WARN_IF(!trustParams && !VerifyRequestParams(aParams))) {
+ MOZ_CRASH_UNLESS_FUZZING();
+ return nullptr;
+ }
+
+ if (NS_WARN_IF(mRunningRequest)) {
+ MOZ_CRASH_UNLESS_FUZZING();
+ return nullptr;
+ }
+
+ QM_TRY(QuotaManager::EnsureCreated(), nullptr);
+
+ RefPtr<ConnectionOperationBase> actor;
+
+ switch (aParams.type()) {
+ case SDBRequestParams::TSDBRequestOpenParams:
+ actor = new OpenOp(this, aParams);
+ break;
+
+ case SDBRequestParams::TSDBRequestSeekParams:
+ actor = new SeekOp(this, aParams);
+ break;
+
+ case SDBRequestParams::TSDBRequestReadParams:
+ actor = new ReadOp(this, aParams);
+ break;
+
+ case SDBRequestParams::TSDBRequestWriteParams:
+ actor = new WriteOp(this, aParams);
+ break;
+
+ case SDBRequestParams::TSDBRequestCloseParams:
+ actor = new CloseOp(this);
+ break;
+
+ default:
+ MOZ_CRASH("Should never get here!");
+ }
+
+ // Transfer ownership to IPDL.
+ return actor.forget().take();
+}
+
+mozilla::ipc::IPCResult Connection::RecvPBackgroundSDBRequestConstructor(
+ PBackgroundSDBRequestParent* aActor, const SDBRequestParams& aParams) {
+ AssertIsOnBackgroundThread();
+ MOZ_ASSERT(aActor);
+ MOZ_ASSERT(aParams.type() != SDBRequestParams::T__None);
+ MOZ_ASSERT_IF(aParams.type() == SDBRequestParams::TSDBRequestOpenParams,
+ !QuotaClient::IsShuttingDownOnBackgroundThread());
+ MOZ_ASSERT(!mAllowedToClose);
+ MOZ_ASSERT(!mRunningRequest);
+
+ auto* op = static_cast<ConnectionOperationBase*>(aActor);
+
+ if (NS_WARN_IF(!op->Init())) {
+ op->Cleanup();
+ return IPC_FAIL_NO_REASON(this);
+ }
+
+ if (NS_WARN_IF(NS_FAILED(op->Dispatch()))) {
+ op->Cleanup();
+ return IPC_FAIL_NO_REASON(this);
+ }
+
+ return IPC_OK();
+}
+
+bool Connection::DeallocPBackgroundSDBRequestParent(
+ PBackgroundSDBRequestParent* aActor) {
+ AssertIsOnBackgroundThread();
+ MOZ_ASSERT(aActor);
+
+ // Transfer ownership back from IPDL.
+ RefPtr<ConnectionOperationBase> actor =
+ dont_AddRef(static_cast<ConnectionOperationBase*>(aActor));
+ return true;
+}
+
+/*******************************************************************************
+ * ConnectionOperationBase
+ ******************************************************************************/
+
+ConnectionOperationBase::~ConnectionOperationBase() {
+ MOZ_ASSERT(
+ !mConnection,
+ "ConnectionOperationBase::Cleanup() was not called by a subclass!");
+ MOZ_ASSERT(mActorDestroyed);
+}
+
+bool ConnectionOperationBase::Init() {
+ AssertIsOnBackgroundThread();
+ MOZ_ASSERT(mConnection);
+
+ mConnection->OnNewRequest();
+
+ return true;
+}
+
+nsresult ConnectionOperationBase::Dispatch() {
+ if (NS_WARN_IF(QuotaClient::IsShuttingDownOnBackgroundThread()) ||
+ IsActorDestroyed()) {
+ return NS_ERROR_ABORT;
+ }
+
+ QuotaManager* quotaManager = QuotaManager::Get();
+ MOZ_ASSERT(quotaManager);
+
+ nsresult rv = quotaManager->IOThread()->Dispatch(this, NS_DISPATCH_NORMAL);
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+
+ return NS_OK;
+}
+
+void ConnectionOperationBase::Cleanup() {
+ AssertIsOnOwningThread();
+ MOZ_ASSERT(mConnection);
+
+ mConnection->OnRequestFinished();
+
+ mConnection = nullptr;
+}
+
+void ConnectionOperationBase::SendResults() {
+ AssertIsOnOwningThread();
+
+ if (IsActorDestroyed()) {
+ MaybeSetFailureCode(NS_ERROR_FAILURE);
+ } else {
+ SDBRequestResponse response;
+
+ if (NS_SUCCEEDED(mResultCode)) {
+ GetResponse(response);
+
+ MOZ_ASSERT(response.type() != SDBRequestResponse::T__None);
+ MOZ_ASSERT(response.type() != SDBRequestResponse::Tnsresult);
+ } else {
+ response = mResultCode;
+ }
+
+ Unused << PBackgroundSDBRequestParent::Send__delete__(this, response);
+
+ if (NS_SUCCEEDED(mResultCode)) {
+ OnSuccess();
+ }
+ }
+
+ Cleanup();
+}
+
+void ConnectionOperationBase::DatabaseWork() {
+ AssertIsOnIOThread();
+ MOZ_ASSERT(NS_SUCCEEDED(mResultCode));
+
+ if (!OperationMayProceed()) {
+ // The operation was canceled in some way, likely because the child process
+ // has crashed.
+ mResultCode = NS_ERROR_ABORT;
+ } else {
+ nsIFileRandomAccessStream* fileRandomAccessStream =
+ mConnection->GetFileRandomAccessStream();
+ MOZ_ASSERT(fileRandomAccessStream);
+
+ nsresult rv = DoDatabaseWork(fileRandomAccessStream);
+ if (NS_FAILED(rv)) {
+ mResultCode = rv;
+ }
+ }
+
+ MOZ_ALWAYS_SUCCEEDS(mOwningEventTarget->Dispatch(this, NS_DISPATCH_NORMAL));
+}
+
+void ConnectionOperationBase::OnSuccess() { AssertIsOnOwningThread(); }
+
+NS_IMETHODIMP
+ConnectionOperationBase::Run() {
+ if (IsOnBackgroundThread()) {
+ SendResults();
+ } else {
+ DatabaseWork();
+ }
+
+ return NS_OK;
+}
+
+void ConnectionOperationBase::ActorDestroy(ActorDestroyReason aWhy) {
+ AssertIsOnBackgroundThread();
+
+ mOperationMayProceed = false;
+ mActorDestroyed = true;
+}
+
+OpenOp::OpenOp(Connection* aConnection, const SDBRequestParams& aParams)
+ : ConnectionOperationBase(aConnection),
+ mParams(aParams.get_SDBRequestOpenParams()),
+ mState(State::Initial),
+ mFileRandomAccessStreamOpen(false) {
+ MOZ_ASSERT(aParams.type() == SDBRequestParams::TSDBRequestOpenParams);
+}
+
+OpenOp::~OpenOp() {
+ MOZ_ASSERT(!mDirectoryLock);
+ MOZ_ASSERT(!mFileRandomAccessStream);
+ MOZ_ASSERT(!mFileRandomAccessStreamOpen);
+ MOZ_ASSERT_IF(OperationMayProceed(),
+ mState == State::Initial || mState == State::Completed);
+}
+
+nsresult OpenOp::Dispatch() {
+ MOZ_ALWAYS_SUCCEEDS(NS_DispatchToMainThread(this));
+
+ return NS_OK;
+}
+
+nsresult OpenOp::Open() {
+ MOZ_ASSERT(NS_IsMainThread());
+ MOZ_ASSERT(mState == State::Initial);
+
+ if (NS_WARN_IF(QuotaClient::IsShuttingDownOnNonBackgroundThread()) ||
+ !OperationMayProceed()) {
+ return NS_ERROR_ABORT;
+ }
+
+ if (NS_WARN_IF(!Preferences::GetBool(kPrefSimpleDBEnabled, false))) {
+ return NS_ERROR_UNEXPECTED;
+ }
+
+ mState = State::FinishOpen;
+ MOZ_ALWAYS_SUCCEEDS(OwningEventTarget()->Dispatch(this, NS_DISPATCH_NORMAL));
+
+ return NS_OK;
+}
+
+nsresult OpenOp::FinishOpen() {
+ AssertIsOnOwningThread();
+ MOZ_ASSERT(mOriginMetadata.mOrigin.IsEmpty());
+ MOZ_ASSERT(!mDirectoryLock);
+ MOZ_ASSERT(mState == State::FinishOpen);
+
+ if (NS_WARN_IF(QuotaClient::IsShuttingDownOnBackgroundThread()) ||
+ IsActorDestroyed()) {
+ return NS_ERROR_ABORT;
+ }
+
+ QuotaManager* quotaManager = QuotaManager::Get();
+ MOZ_ASSERT(quotaManager);
+
+ const PrincipalInfo& principalInfo = GetConnection()->GetPrincipalInfo();
+
+ PersistenceType persistenceType = GetConnection()->GetPersistenceType();
+
+ if (principalInfo.type() == PrincipalInfo::TSystemPrincipalInfo) {
+ mOriginMetadata = {QuotaManager::GetInfoForChrome(), persistenceType};
+ } else {
+ MOZ_ASSERT(principalInfo.type() == PrincipalInfo::TContentPrincipalInfo);
+
+ QM_TRY_UNWRAP(
+ auto principalMetadata,
+ quotaManager->GetInfoFromValidatedPrincipalInfo(principalInfo));
+
+ mOriginMetadata = {std::move(principalMetadata), persistenceType};
+ }
+
+ if (gOpenConnections) {
+ for (const auto& connection : *gOpenConnections) {
+ if (connection->Origin() == mOriginMetadata.mOrigin &&
+ connection->Name() == mParams.name()) {
+ return NS_ERROR_STORAGE_BUSY;
+ }
+ }
+ }
+
+ // Open the directory
+
+ mState = State::DirectoryOpenPending;
+
+ quotaManager
+ ->OpenClientDirectory({mOriginMetadata, mozilla::dom::quota::Client::SDB})
+ ->Then(
+ GetCurrentSerialEventTarget(), __func__,
+ [self = RefPtr(this)](
+ const ClientDirectoryLockPromise::ResolveOrRejectValue& aValue) {
+ if (aValue.IsResolve()) {
+ self->DirectoryLockAcquired(aValue.ResolveValue());
+ } else {
+ self->DirectoryLockFailed();
+ }
+ });
+
+ return NS_OK;
+}
+
+nsresult OpenOp::SendToIOThread() {
+ AssertIsOnOwningThread();
+ MOZ_ASSERT(mState == State::DirectoryOpenPending);
+
+ if (NS_WARN_IF(QuotaClient::IsShuttingDownOnBackgroundThread()) ||
+ IsActorDestroyed()) {
+ return NS_ERROR_ABORT;
+ }
+
+ mFileRandomAccessStream = new FileRandomAccessStream(
+ GetConnection()->GetPersistenceType(), mOriginMetadata,
+ mozilla::dom::quota::Client::SDB);
+
+ QuotaManager* quotaManager = QuotaManager::Get();
+ MOZ_ASSERT(quotaManager);
+
+ // Must set this before dispatching otherwise we will race with the IO thread.
+ mState = State::DatabaseWorkOpen;
+
+ nsresult rv = quotaManager->IOThread()->Dispatch(this, NS_DISPATCH_NORMAL);
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+
+ return NS_OK;
+}
+
+nsresult OpenOp::DatabaseWork() {
+ AssertIsOnIOThread();
+ MOZ_ASSERT(mState == State::DatabaseWorkOpen);
+ MOZ_ASSERT(mFileRandomAccessStream);
+ MOZ_ASSERT(!mFileRandomAccessStreamOpen);
+
+ if (NS_WARN_IF(QuotaClient::IsShuttingDownOnNonBackgroundThread()) ||
+ !OperationMayProceed()) {
+ return NS_ERROR_ABORT;
+ }
+
+ QuotaManager* quotaManager = QuotaManager::Get();
+ MOZ_ASSERT(quotaManager);
+
+ QM_TRY_INSPECT(
+ const auto& dbDirectory,
+ ([persistenceType = GetConnection()->GetPersistenceType(), &quotaManager,
+ this]()
+ -> mozilla::Result<std::pair<nsCOMPtr<nsIFile>, bool>, nsresult> {
+ if (persistenceType == PERSISTENCE_TYPE_PERSISTENT) {
+ QM_TRY_RETURN(quotaManager->EnsurePersistentOriginIsInitialized(
+ mOriginMetadata));
+ }
+
+ QM_TRY(MOZ_TO_RESULT(
+ quotaManager->EnsureTemporaryStorageIsInitializedInternal()));
+ QM_TRY_RETURN(quotaManager->EnsureTemporaryOriginIsInitialized(
+ persistenceType, mOriginMetadata));
+ }()
+ .map([](const auto& res) { return res.first; })));
+
+ nsresult rv =
+ dbDirectory->Append(NS_LITERAL_STRING_FROM_CSTRING(SDB_DIRECTORY_NAME));
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+
+ bool exists;
+ rv = dbDirectory->Exists(&exists);
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+
+ if (!exists) {
+ rv = dbDirectory->Create(nsIFile::DIRECTORY_TYPE, 0755);
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+ }
+#ifdef DEBUG
+ else {
+ bool isDirectory;
+ MOZ_ASSERT(NS_SUCCEEDED(dbDirectory->IsDirectory(&isDirectory)));
+ MOZ_ASSERT(isDirectory);
+ }
+#endif
+
+ nsCOMPtr<nsIFile> dbFile;
+ rv = dbDirectory->Clone(getter_AddRefs(dbFile));
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+
+ rv = dbFile->Append(mParams.name() + kSDBSuffix);
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+
+ nsString databaseFilePath;
+ rv = dbFile->GetPath(databaseFilePath);
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+
+ rv = mFileRandomAccessStream->Init(dbFile, PR_RDWR | PR_CREATE_FILE, 0644, 0);
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+
+ mFileRandomAccessStreamOpen = true;
+
+ rv = DoDatabaseWork(mFileRandomAccessStream);
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+
+ // Must set mState before dispatching otherwise we will race with the owning
+ // thread.
+ mState = State::SendingResults;
+
+ rv = OwningEventTarget()->Dispatch(this, NS_DISPATCH_NORMAL);
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+
+ return NS_OK;
+}
+
+void OpenOp::StreamClosedCallback() {
+ AssertIsOnOwningThread();
+ MOZ_ASSERT(NS_FAILED(ResultCode()));
+ MOZ_ASSERT(mDirectoryLock);
+ MOZ_ASSERT(mFileRandomAccessStream);
+ MOZ_ASSERT(mFileRandomAccessStreamOpen);
+
+ mDirectoryLock = nullptr;
+ mFileRandomAccessStream = nullptr;
+ mFileRandomAccessStreamOpen = false;
+}
+
+nsresult OpenOp::DoDatabaseWork(
+ nsIFileRandomAccessStream* aFileRandomAccessStream) {
+ AssertIsOnIOThread();
+
+ return NS_OK;
+}
+
+void OpenOp::GetResponse(SDBRequestResponse& aResponse) {
+ AssertIsOnOwningThread();
+
+ aResponse = SDBRequestOpenResponse();
+}
+
+void OpenOp::OnSuccess() {
+ AssertIsOnOwningThread();
+ MOZ_ASSERT(NS_SUCCEEDED(ResultCode()));
+ MOZ_ASSERT(!mOriginMetadata.mOrigin.IsEmpty());
+ MOZ_ASSERT(mDirectoryLock);
+ MOZ_ASSERT(mFileRandomAccessStream);
+ MOZ_ASSERT(mFileRandomAccessStreamOpen);
+
+ RefPtr<DirectoryLock> directoryLock;
+ nsCOMPtr<nsIFileRandomAccessStream> fileRandomAccessStream;
+
+ mDirectoryLock.swap(directoryLock);
+ mFileRandomAccessStream.swap(fileRandomAccessStream);
+ mFileRandomAccessStreamOpen = false;
+
+ GetConnection()->OnOpen(mOriginMetadata.mOrigin, mParams.name(),
+ directoryLock.forget(),
+ fileRandomAccessStream.forget());
+}
+
+void OpenOp::Cleanup() {
+ AssertIsOnOwningThread();
+ MOZ_ASSERT_IF(mFileRandomAccessStreamOpen, mFileRandomAccessStream);
+
+ if (mFileRandomAccessStream && mFileRandomAccessStreamOpen) {
+ // If we have an initialized file stream then the operation must have failed
+ // and there must be a directory lock too.
+ MOZ_ASSERT(NS_FAILED(ResultCode()));
+ MOZ_ASSERT(mDirectoryLock);
+
+ // We must close the stream on the I/O thread before releasing it on this
+ // thread. The directory lock can't be released either.
+ nsCOMPtr<nsIRunnable> callback =
+ NewRunnableMethod("dom::OpenOp::StreamClosedCallback", this,
+ &OpenOp::StreamClosedCallback);
+
+ RefPtr<StreamHelper> helper =
+ new StreamHelper(mFileRandomAccessStream, callback);
+ helper->AsyncClose();
+ } else {
+ MOZ_ASSERT(!mFileRandomAccessStreamOpen);
+
+ mDirectoryLock = nullptr;
+ mFileRandomAccessStream = nullptr;
+ }
+
+ ConnectionOperationBase::Cleanup();
+}
+
+NS_IMETHODIMP
+OpenOp::Run() {
+ nsresult rv;
+
+ switch (mState) {
+ case State::Initial:
+ rv = Open();
+ break;
+
+ case State::FinishOpen:
+ rv = FinishOpen();
+ break;
+
+ case State::DatabaseWorkOpen:
+ rv = DatabaseWork();
+ break;
+
+ case State::SendingResults:
+ SendResults();
+ return NS_OK;
+
+ default:
+ MOZ_CRASH("Bad state!");
+ }
+
+ if (NS_WARN_IF(NS_FAILED(rv)) && mState != State::SendingResults) {
+ MaybeSetFailureCode(rv);
+
+ // Must set mState before dispatching otherwise we will race with the owning
+ // thread.
+ mState = State::SendingResults;
+
+ if (IsOnOwningThread()) {
+ SendResults();
+ } else {
+ MOZ_ALWAYS_SUCCEEDS(
+ OwningEventTarget()->Dispatch(this, NS_DISPATCH_NORMAL));
+ }
+ }
+
+ return NS_OK;
+}
+
+void OpenOp::DirectoryLockAcquired(DirectoryLock* aLock) {
+ AssertIsOnOwningThread();
+ MOZ_ASSERT(mState == State::DirectoryOpenPending);
+ MOZ_ASSERT(!mDirectoryLock);
+
+ mDirectoryLock = aLock;
+
+ nsresult rv = SendToIOThread();
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ MaybeSetFailureCode(rv);
+
+ // The caller holds a strong reference to us, no need for a self reference
+ // before calling Run().
+
+ mState = State::SendingResults;
+ MOZ_ALWAYS_SUCCEEDS(Run());
+
+ return;
+ }
+}
+
+void OpenOp::DirectoryLockFailed() {
+ AssertIsOnOwningThread();
+ MOZ_ASSERT(mState == State::DirectoryOpenPending);
+ MOZ_ASSERT(!mDirectoryLock);
+
+ MaybeSetFailureCode(NS_ERROR_FAILURE);
+
+ // The caller holds a strong reference to us, no need for a self reference
+ // before calling Run().
+
+ mState = State::SendingResults;
+ MOZ_ALWAYS_SUCCEEDS(Run());
+}
+
+SeekOp::SeekOp(Connection* aConnection, const SDBRequestParams& aParams)
+ : ConnectionOperationBase(aConnection),
+ mParams(aParams.get_SDBRequestSeekParams()) {
+ MOZ_ASSERT(aParams.type() == SDBRequestParams::TSDBRequestSeekParams);
+}
+
+nsresult SeekOp::DoDatabaseWork(
+ nsIFileRandomAccessStream* aFileRandomAccessStream) {
+ AssertIsOnIOThread();
+ MOZ_ASSERT(aFileRandomAccessStream);
+
+ nsresult rv = aFileRandomAccessStream->Seek(nsISeekableStream::NS_SEEK_SET,
+ mParams.offset());
+
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+
+ return NS_OK;
+}
+
+void SeekOp::GetResponse(SDBRequestResponse& aResponse) {
+ aResponse = SDBRequestSeekResponse();
+}
+
+ReadOp::ReadOp(Connection* aConnection, const SDBRequestParams& aParams)
+ : ConnectionOperationBase(aConnection),
+ mParams(aParams.get_SDBRequestReadParams()) {
+ MOZ_ASSERT(aParams.type() == SDBRequestParams::TSDBRequestReadParams);
+}
+
+bool ReadOp::Init() {
+ AssertIsOnOwningThread();
+
+ if (NS_WARN_IF(!ConnectionOperationBase::Init())) {
+ return false;
+ }
+
+ if (NS_WARN_IF(mParams.size() > std::numeric_limits<std::size_t>::max())) {
+ return false;
+ }
+
+ mOutputStream = FixedBufferOutputStream::Create(mParams.size(), fallible);
+ if (NS_WARN_IF(!mOutputStream)) {
+ return false;
+ }
+
+ return true;
+}
+
+nsresult ReadOp::DoDatabaseWork(
+ nsIFileRandomAccessStream* aFileRandomAccessStream) {
+ AssertIsOnIOThread();
+ MOZ_ASSERT(aFileRandomAccessStream);
+
+ nsCOMPtr<nsIInputStream> inputStream =
+ do_QueryInterface(aFileRandomAccessStream);
+ MOZ_ASSERT(inputStream);
+
+ nsresult rv;
+
+ uint64_t offset = 0;
+
+ do {
+ char copyBuffer[kCopyBufferSize];
+
+ uint64_t max = mParams.size() - offset;
+ if (max == 0) {
+ break;
+ }
+
+ uint32_t count = sizeof(copyBuffer);
+ if (count > max) {
+ count = max;
+ }
+
+ uint32_t numRead;
+ rv = inputStream->Read(copyBuffer, count, &numRead);
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+
+ if (!numRead) {
+ break;
+ }
+
+ uint32_t numWrite;
+ rv = mOutputStream->Write(copyBuffer, numRead, &numWrite);
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+
+ if (NS_WARN_IF(numWrite != numRead)) {
+ return NS_ERROR_FAILURE;
+ }
+
+ offset += numWrite;
+ } while (true);
+
+ MOZ_ASSERT(offset == mParams.size());
+
+ MOZ_ALWAYS_SUCCEEDS(mOutputStream->Close());
+
+ return NS_OK;
+}
+
+void ReadOp::GetResponse(SDBRequestResponse& aResponse) {
+ aResponse = SDBRequestReadResponse(nsCString(mOutputStream->WrittenData()));
+}
+
+WriteOp::WriteOp(Connection* aConnection, const SDBRequestParams& aParams)
+ : ConnectionOperationBase(aConnection),
+ mParams(aParams.get_SDBRequestWriteParams()),
+ mSize(0) {
+ MOZ_ASSERT(aParams.type() == SDBRequestParams::TSDBRequestWriteParams);
+}
+
+bool WriteOp::Init() {
+ AssertIsOnOwningThread();
+
+ if (NS_WARN_IF(!ConnectionOperationBase::Init())) {
+ return false;
+ }
+
+ const nsCString& string = mParams.data();
+
+ nsCOMPtr<nsIInputStream> inputStream;
+ nsresult rv = NS_NewCStringInputStream(getter_AddRefs(inputStream), string);
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return false;
+ }
+
+ mInputStream = std::move(inputStream);
+ mSize = string.Length();
+
+ return true;
+}
+
+nsresult WriteOp::DoDatabaseWork(
+ nsIFileRandomAccessStream* aFileRandomAccessStream) {
+ AssertIsOnIOThread();
+ MOZ_ASSERT(aFileRandomAccessStream);
+
+ nsCOMPtr<nsIOutputStream> outputStream =
+ do_QueryInterface(aFileRandomAccessStream);
+ MOZ_ASSERT(outputStream);
+
+ nsresult rv;
+
+ do {
+ char copyBuffer[kCopyBufferSize];
+
+ uint32_t numRead;
+ rv = mInputStream->Read(copyBuffer, sizeof(copyBuffer), &numRead);
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ break;
+ }
+
+ if (!numRead) {
+ break;
+ }
+
+ uint32_t numWrite;
+ rv = outputStream->Write(copyBuffer, numRead, &numWrite);
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+
+ if (NS_WARN_IF(numWrite != numRead)) {
+ return NS_ERROR_FAILURE;
+ }
+ } while (true);
+
+ MOZ_ALWAYS_SUCCEEDS(mInputStream->Close());
+
+ return NS_OK;
+}
+
+void WriteOp::GetResponse(SDBRequestResponse& aResponse) {
+ aResponse = SDBRequestWriteResponse();
+}
+
+CloseOp::CloseOp(Connection* aConnection)
+ : ConnectionOperationBase(aConnection) {}
+
+nsresult CloseOp::DoDatabaseWork(
+ nsIFileRandomAccessStream* aFileRandomAccessStream) {
+ AssertIsOnIOThread();
+ MOZ_ASSERT(aFileRandomAccessStream);
+
+ nsCOMPtr<nsIInputStream> inputStream =
+ do_QueryInterface(aFileRandomAccessStream);
+ MOZ_ASSERT(inputStream);
+
+ nsresult rv = inputStream->Close();
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+
+ return NS_OK;
+}
+
+void CloseOp::GetResponse(SDBRequestResponse& aResponse) {
+ aResponse = SDBRequestCloseResponse();
+}
+
+void CloseOp::OnSuccess() {
+ AssertIsOnOwningThread();
+
+ GetConnection()->OnClose();
+}
+
+/*******************************************************************************
+ * QuotaClient
+ ******************************************************************************/
+
+QuotaClient* QuotaClient::sInstance = nullptr;
+
+QuotaClient::QuotaClient() {
+ AssertIsOnBackgroundThread();
+ MOZ_ASSERT(!sInstance, "We expect this to be a singleton!");
+
+ sInstance = this;
+}
+
+QuotaClient::~QuotaClient() {
+ AssertIsOnBackgroundThread();
+ MOZ_ASSERT(sInstance == this, "We expect this to be a singleton!");
+
+ sInstance = nullptr;
+}
+
+mozilla::dom::quota::Client::Type QuotaClient::GetType() {
+ return QuotaClient::SDB;
+}
+
+Result<UsageInfo, nsresult> QuotaClient::InitOrigin(
+ PersistenceType aPersistenceType, const OriginMetadata& aOriginMetadata,
+ const AtomicBool& aCanceled) {
+ AssertIsOnIOThread();
+
+ return GetUsageForOrigin(aPersistenceType, aOriginMetadata, aCanceled);
+}
+
+nsresult QuotaClient::InitOriginWithoutTracking(
+ PersistenceType aPersistenceType, const OriginMetadata& aOriginMetadata,
+ const AtomicBool& aCanceled) {
+ AssertIsOnIOThread();
+
+ return NS_OK;
+}
+
+Result<UsageInfo, nsresult> QuotaClient::GetUsageForOrigin(
+ PersistenceType aPersistenceType, const OriginMetadata& aOriginMetadata,
+ const AtomicBool& aCanceled) {
+ AssertIsOnIOThread();
+ MOZ_ASSERT(aOriginMetadata.mPersistenceType == aPersistenceType);
+
+ QuotaManager* quotaManager = QuotaManager::Get();
+ MOZ_ASSERT(quotaManager);
+
+ QM_TRY_UNWRAP(auto directory,
+ quotaManager->GetOriginDirectory(aOriginMetadata));
+
+ MOZ_ASSERT(directory);
+
+ nsresult rv =
+ directory->Append(NS_LITERAL_STRING_FROM_CSTRING(SDB_DIRECTORY_NAME));
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return Err(rv);
+ }
+
+ DebugOnly<bool> exists;
+ MOZ_ASSERT(NS_SUCCEEDED(directory->Exists(&exists)) && exists);
+
+ QM_TRY_RETURN(ReduceEachFileAtomicCancelable(
+ *directory, aCanceled, UsageInfo{},
+ [](UsageInfo usageInfo,
+ const nsCOMPtr<nsIFile>& file) -> Result<UsageInfo, nsresult> {
+ QM_TRY_INSPECT(const bool& isDirectory,
+ MOZ_TO_RESULT_INVOKE_MEMBER(file, IsDirectory));
+
+ if (isDirectory) {
+ Unused << WARN_IF_FILE_IS_UNKNOWN(*file);
+ return usageInfo;
+ }
+
+ nsString leafName;
+ QM_TRY(MOZ_TO_RESULT(file->GetLeafName(leafName)));
+
+ if (StringEndsWith(leafName, kSDBSuffix)) {
+ QM_TRY_INSPECT(const int64_t& fileSize,
+ MOZ_TO_RESULT_INVOKE_MEMBER(file, GetFileSize));
+
+ MOZ_ASSERT(fileSize >= 0);
+
+ return usageInfo +
+ UsageInfo{DatabaseUsageType(Some(uint64_t(fileSize)))};
+ }
+
+ Unused << WARN_IF_FILE_IS_UNKNOWN(*file);
+
+ return usageInfo;
+ }));
+}
+
+void QuotaClient::OnOriginClearCompleted(PersistenceType aPersistenceType,
+ const nsACString& aOrigin) {
+ AssertIsOnIOThread();
+}
+
+void QuotaClient::OnRepositoryClearCompleted(PersistenceType aPersistenceType) {
+ AssertIsOnIOThread();
+}
+
+void QuotaClient::ReleaseIOThreadObjects() { AssertIsOnIOThread(); }
+
+void QuotaClient::AbortOperationsForLocks(
+ const DirectoryLockIdTable& aDirectoryLockIds) {
+ AssertIsOnBackgroundThread();
+
+ AllowToCloseConnectionsMatching([&aDirectoryLockIds](const auto& connection) {
+ // If the connections is registered in gOpenConnections then it must have
+ // a directory lock.
+ return IsLockForObjectContainedInLockTable(connection, aDirectoryLockIds);
+ });
+}
+
+void QuotaClient::AbortOperationsForProcess(ContentParentId aContentParentId) {
+ AssertIsOnBackgroundThread();
+}
+
+void QuotaClient::AbortAllOperations() {
+ AssertIsOnBackgroundThread();
+
+ AllowToCloseConnectionsMatching([](const auto&) { return true; });
+}
+
+void QuotaClient::StartIdleMaintenance() { AssertIsOnBackgroundThread(); }
+
+void QuotaClient::StopIdleMaintenance() { AssertIsOnBackgroundThread(); }
+
+void QuotaClient::InitiateShutdown() {
+ AssertIsOnBackgroundThread();
+
+ if (gOpenConnections) {
+ for (const auto& connection : *gOpenConnections) {
+ connection->AllowToClose();
+ }
+ }
+}
+
+bool QuotaClient::IsShutdownCompleted() const { return !gOpenConnections; }
+
+void QuotaClient::ForceKillActors() {
+ // Currently we don't implement killing actors (are there any to kill here?).
+}
+
+nsCString QuotaClient::GetShutdownStatus() const {
+ // XXX Gather information here.
+ return "To be implemented"_ns;
+}
+
+void QuotaClient::FinalizeShutdown() {
+ // Nothing to do here.
+}
+
+} // namespace mozilla::dom