summaryrefslogtreecommitdiffstats
path: root/dom/simpledb
diff options
context:
space:
mode:
Diffstat (limited to 'dom/simpledb')
-rw-r--r--dom/simpledb/ActorsChild.cpp222
-rw-r--r--dom/simpledb/ActorsChild.h106
-rw-r--r--dom/simpledb/ActorsParent.cpp1792
-rw-r--r--dom/simpledb/ActorsParent.h54
-rw-r--r--dom/simpledb/PBackgroundSDBConnection.ipdl65
-rw-r--r--dom/simpledb/PBackgroundSDBRequest.ipdl51
-rw-r--r--dom/simpledb/SDBConnection.cpp433
-rw-r--r--dom/simpledb/SDBConnection.h90
-rw-r--r--dom/simpledb/SDBRequest.cpp125
-rw-r--r--dom/simpledb/SDBRequest.h62
-rw-r--r--dom/simpledb/SDBResults.cpp56
-rw-r--r--dom/simpledb/SDBResults.h31
-rw-r--r--dom/simpledb/SimpleDBCommon.cpp13
-rw-r--r--dom/simpledb/SimpleDBCommon.h18
-rw-r--r--dom/simpledb/moz.build40
-rw-r--r--dom/simpledb/nsISDBCallbacks.idl22
-rw-r--r--dom/simpledb/nsISDBConnection.idl35
-rw-r--r--dom/simpledb/nsISDBRequest.idl20
-rw-r--r--dom/simpledb/nsISDBResults.idl17
19 files changed, 3252 insertions, 0 deletions
diff --git a/dom/simpledb/ActorsChild.cpp b/dom/simpledb/ActorsChild.cpp
new file mode 100644
index 0000000000..f6c234fa1d
--- /dev/null
+++ b/dom/simpledb/ActorsChild.cpp
@@ -0,0 +1,222 @@
+/* -*- 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 "ActorsChild.h"
+
+// Local includes
+#include "SDBConnection.h"
+#include "SDBRequest.h"
+#include "SDBResults.h"
+
+// Global includes
+#include "mozilla/Assertions.h"
+#include "mozilla/dom/PBackgroundSDBRequest.h"
+#include "nsError.h"
+#include "nsID.h"
+#include "nsISDBResults.h"
+#include "nsVariant.h"
+
+namespace mozilla::dom {
+
+/*******************************************************************************
+ * SDBConnectionChild
+ ******************************************************************************/
+
+SDBConnectionChild::SDBConnectionChild(SDBConnection* aConnection)
+ : mConnection(aConnection) {
+ AssertIsOnOwningThread();
+ MOZ_ASSERT(aConnection);
+
+ MOZ_COUNT_CTOR(SDBConnectionChild);
+}
+
+SDBConnectionChild::~SDBConnectionChild() {
+ AssertIsOnOwningThread();
+
+ MOZ_COUNT_DTOR(SDBConnectionChild);
+}
+
+void SDBConnectionChild::SendDeleteMeInternal() {
+ AssertIsOnOwningThread();
+
+ if (mConnection) {
+ mConnection->ClearBackgroundActor();
+ mConnection = nullptr;
+
+ MOZ_ALWAYS_TRUE(PBackgroundSDBConnectionChild::SendDeleteMe());
+ }
+}
+
+void SDBConnectionChild::ActorDestroy(ActorDestroyReason aWhy) {
+ AssertIsOnOwningThread();
+
+ if (mConnection) {
+ mConnection->ClearBackgroundActor();
+#ifdef DEBUG
+ mConnection = nullptr;
+#endif
+ }
+}
+
+PBackgroundSDBRequestChild* SDBConnectionChild::AllocPBackgroundSDBRequestChild(
+ const SDBRequestParams& aParams) {
+ AssertIsOnOwningThread();
+
+ MOZ_CRASH(
+ "PBackgroundSDBRequestChild actors should be manually "
+ "constructed!");
+}
+
+bool SDBConnectionChild::DeallocPBackgroundSDBRequestChild(
+ PBackgroundSDBRequestChild* aActor) {
+ AssertIsOnOwningThread();
+ MOZ_ASSERT(aActor);
+
+ delete static_cast<SDBRequestChild*>(aActor);
+ return true;
+}
+
+mozilla::ipc::IPCResult SDBConnectionChild::RecvAllowToClose() {
+ AssertIsOnOwningThread();
+
+ if (mConnection) {
+ mConnection->AllowToClose();
+ }
+
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult SDBConnectionChild::RecvClosed() {
+ AssertIsOnOwningThread();
+
+ if (mConnection) {
+ mConnection->OnClose(/* aAbnormal */ true);
+ }
+
+ return IPC_OK();
+}
+
+/*******************************************************************************
+ * SDBRequestChild
+ ******************************************************************************/
+
+SDBRequestChild::SDBRequestChild(SDBRequest* aRequest)
+ : mConnection(aRequest->GetConnection()), mRequest(aRequest) {
+ AssertIsOnOwningThread();
+ MOZ_ASSERT(aRequest);
+
+ MOZ_COUNT_CTOR(SDBRequestChild);
+}
+
+SDBRequestChild::~SDBRequestChild() {
+ AssertIsOnOwningThread();
+
+ MOZ_COUNT_DTOR(SDBRequestChild);
+}
+
+#ifdef DEBUG
+
+void SDBRequestChild::AssertIsOnOwningThread() const {
+ MOZ_ASSERT(mRequest);
+ mRequest->AssertIsOnOwningThread();
+}
+
+#endif // DEBUG
+
+void SDBRequestChild::HandleResponse(nsresult aResponse) {
+ AssertIsOnOwningThread();
+ MOZ_ASSERT(NS_FAILED(aResponse));
+ MOZ_ASSERT(mRequest);
+
+ mRequest->SetError(aResponse);
+}
+
+void SDBRequestChild::HandleResponse() {
+ AssertIsOnOwningThread();
+ MOZ_ASSERT(mRequest);
+
+ RefPtr<nsVariant> variant = new nsVariant();
+ variant->SetAsVoid();
+
+ mRequest->SetResult(variant);
+}
+
+void SDBRequestChild::HandleResponse(const nsCString& aResponse) {
+ AssertIsOnOwningThread();
+ MOZ_ASSERT(mRequest);
+
+ RefPtr<SDBResult> result = new SDBResult(aResponse);
+
+ RefPtr<nsVariant> variant = new nsVariant();
+ variant->SetAsInterface(NS_GET_IID(nsISDBResult), result);
+
+ mRequest->SetResult(variant);
+}
+
+void SDBRequestChild::ActorDestroy(ActorDestroyReason aWhy) {
+ AssertIsOnOwningThread();
+
+ if (mConnection) {
+ mConnection->AssertIsOnOwningThread();
+
+ mConnection->OnRequestFinished();
+#ifdef DEBUG
+ mConnection = nullptr;
+#endif
+ }
+}
+
+mozilla::ipc::IPCResult SDBRequestChild::Recv__delete__(
+ const SDBRequestResponse& aResponse) {
+ AssertIsOnOwningThread();
+ MOZ_ASSERT(mRequest);
+ MOZ_ASSERT(mConnection);
+
+ switch (aResponse.type()) {
+ case SDBRequestResponse::Tnsresult:
+ HandleResponse(aResponse.get_nsresult());
+ break;
+
+ case SDBRequestResponse::TSDBRequestOpenResponse:
+ HandleResponse();
+
+ mConnection->OnOpen();
+
+ break;
+
+ case SDBRequestResponse::TSDBRequestSeekResponse:
+ HandleResponse();
+ break;
+
+ case SDBRequestResponse::TSDBRequestReadResponse:
+ HandleResponse(aResponse.get_SDBRequestReadResponse().data());
+ break;
+
+ case SDBRequestResponse::TSDBRequestWriteResponse:
+ HandleResponse();
+ break;
+
+ case SDBRequestResponse::TSDBRequestCloseResponse:
+ HandleResponse();
+
+ mConnection->OnClose(/* aAbnormal */ false);
+
+ break;
+
+ default:
+ MOZ_CRASH("Unknown response type!");
+ }
+
+ mConnection->OnRequestFinished();
+
+ // Null this out so that we don't try to call OnRequestFinished() again in
+ // ActorDestroy.
+ mConnection = nullptr;
+
+ return IPC_OK();
+}
+
+} // namespace mozilla::dom
diff --git a/dom/simpledb/ActorsChild.h b/dom/simpledb/ActorsChild.h
new file mode 100644
index 0000000000..ba8a77b7ac
--- /dev/null
+++ b/dom/simpledb/ActorsChild.h
@@ -0,0 +1,106 @@
+/* -*- 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/. */
+
+#ifndef mozilla_dom_simpledb_ActorsChild_h
+#define mozilla_dom_simpledb_ActorsChild_h
+
+#include <cstdint>
+#include "ErrorList.h"
+#include "mozilla/RefPtr.h"
+#include "mozilla/dom/PBackgroundSDBConnectionChild.h"
+#include "mozilla/dom/PBackgroundSDBRequestChild.h"
+#include "mozilla/ipc/ProtocolUtils.h"
+#include "nsISupports.h"
+#include "nsStringFwd.h"
+
+namespace mozilla {
+namespace ipc {
+
+class BackgroundChildImpl;
+
+} // namespace ipc
+
+namespace dom {
+
+class SDBConnection;
+class SDBRequest;
+
+class SDBConnectionChild final : public PBackgroundSDBConnectionChild {
+ friend class mozilla::ipc::BackgroundChildImpl;
+ friend class SDBConnection;
+
+ SDBConnection* mConnection;
+
+ NS_DECL_OWNINGTHREAD
+
+ public:
+ void AssertIsOnOwningThread() const {
+ NS_ASSERT_OWNINGTHREAD(SDBConnectionChild);
+ }
+
+ private:
+ // Only created by SDBConnection.
+ explicit SDBConnectionChild(SDBConnection* aConnection);
+
+ // Only destroyed by mozilla::ipc::BackgroundChildImpl.
+ ~SDBConnectionChild();
+
+ void SendDeleteMeInternal();
+
+ // IPDL methods are only called by IPDL.
+ virtual void ActorDestroy(ActorDestroyReason aWhy) override;
+
+ virtual PBackgroundSDBRequestChild* AllocPBackgroundSDBRequestChild(
+ const SDBRequestParams& aParams) override;
+
+ virtual bool DeallocPBackgroundSDBRequestChild(
+ PBackgroundSDBRequestChild* aActor) override;
+
+ virtual mozilla::ipc::IPCResult RecvAllowToClose() override;
+
+ virtual mozilla::ipc::IPCResult RecvClosed() override;
+};
+
+class SDBRequestChild final : public PBackgroundSDBRequestChild {
+ friend class SDBConnectionChild;
+ friend class SDBConnection;
+
+ RefPtr<SDBConnection> mConnection;
+ RefPtr<SDBRequest> mRequest;
+
+ public:
+ void AssertIsOnOwningThread() const
+#ifdef DEBUG
+ ;
+#else
+ {
+ }
+#endif
+
+ private:
+ // Only created by SDBConnection.
+ explicit SDBRequestChild(SDBRequest* aRequest);
+
+ // Only destroyed by SDBConnectionChild.
+ ~SDBRequestChild();
+
+ void HandleResponse(nsresult aResponse);
+
+ void HandleResponse();
+
+ void HandleResponse(const nsCString& aResponse);
+
+ // IPDL methods are only called by IPDL.
+ virtual void ActorDestroy(ActorDestroyReason aWhy) override;
+
+ virtual mozilla::ipc::IPCResult Recv__delete__(
+ const SDBRequestResponse& aResponse) override;
+};
+
+} // namespace dom
+} // namespace mozilla
+
+#endif // mozilla_dom_simpledb_ActorsChild_h
diff --git a/dom/simpledb/ActorsParent.cpp b/dom/simpledb/ActorsParent.cpp
new file mode 100644
index 0000000000..5b09a16908
--- /dev/null
+++ b/dom/simpledb/ActorsParent.cpp
@@ -0,0 +1,1792 @@
+/* -*- 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)
+
+ 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,
+ public OpenDirectoryListener {
+ 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_DECL_ISUPPORTS_INHERITED
+
+ NS_IMETHOD
+ Run() override;
+
+ // OpenDirectoryListener overrides.
+ void DirectoryLockAcquired(DirectoryLock* aLock) override;
+
+ void DirectoryLockFailed() override;
+};
+
+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
+ ******************************************************************************/
+
+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().take();
+}
+
+bool RecvPBackgroundSDBConnectionConstructor(
+ PBackgroundSDBConnectionParent* aActor,
+ const PersistenceType& aPersistenceType,
+ const PrincipalInfo& aPrincipalInfo) {
+ AssertIsOnBackgroundThread();
+ MOZ_ASSERT(aActor);
+ MOZ_ASSERT(!QuotaClient::IsShuttingDownOnBackgroundThread());
+
+ return true;
+}
+
+bool DeallocPBackgroundSDBConnectionParent(
+ PBackgroundSDBConnectionParent* aActor) {
+ AssertIsOnBackgroundThread();
+ MOZ_ASSERT(aActor);
+
+ RefPtr<Connection> actor = dont_AddRef(static_cast<Connection*>(aActor));
+ 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
+
+ RefPtr<DirectoryLock> directoryLock = quotaManager->CreateDirectoryLock(
+ GetConnection()->GetPersistenceType(), mOriginMetadata,
+ mozilla::dom::quota::Client::SDB,
+ /* aExclusive */ false);
+
+ mState = State::DirectoryOpenPending;
+ directoryLock->Acquire(this);
+
+ 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(MOZ_TO_RESULT(quotaManager->EnsureStorageIsInitialized()));
+
+ 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->EnsureTemporaryStorageIsInitialized()));
+ 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_IMPL_ISUPPORTS_INHERITED0(OpenOp, ConnectionOperationBase)
+
+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
diff --git a/dom/simpledb/ActorsParent.h b/dom/simpledb/ActorsParent.h
new file mode 100644
index 0000000000..9502fe4097
--- /dev/null
+++ b/dom/simpledb/ActorsParent.h
@@ -0,0 +1,54 @@
+/* -*- 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/. */
+
+#ifndef mozilla_dom_simpledb_ActorsParent_h
+#define mozilla_dom_simpledb_ActorsParent_h
+
+#include "mozilla/dom/quota/PersistenceType.h"
+
+template <class>
+struct already_AddRefed;
+
+namespace mozilla {
+
+namespace ipc {
+
+class PrincipalInfo;
+
+} // namespace ipc
+
+namespace dom {
+
+class PBackgroundSDBConnectionParent;
+
+namespace quota {
+
+class Client;
+
+} // namespace quota
+
+PBackgroundSDBConnectionParent* AllocPBackgroundSDBConnectionParent(
+ const mozilla::dom::quota::PersistenceType& aPersistenceType,
+ const mozilla::ipc::PrincipalInfo& aPrincipalInfo);
+
+bool RecvPBackgroundSDBConnectionConstructor(
+ PBackgroundSDBConnectionParent* aActor,
+ const mozilla::dom::quota::PersistenceType& aPersistenceType,
+ const mozilla::ipc::PrincipalInfo& aPrincipalInfo);
+
+bool DeallocPBackgroundSDBConnectionParent(
+ PBackgroundSDBConnectionParent* aActor);
+
+namespace simpledb {
+
+already_AddRefed<mozilla::dom::quota::Client> CreateQuotaClient();
+
+} // namespace simpledb
+
+} // namespace dom
+} // namespace mozilla
+
+#endif // mozilla_dom_simpledb_ActorsParent_h
diff --git a/dom/simpledb/PBackgroundSDBConnection.ipdl b/dom/simpledb/PBackgroundSDBConnection.ipdl
new file mode 100644
index 0000000000..e8508f17b8
--- /dev/null
+++ b/dom/simpledb/PBackgroundSDBConnection.ipdl
@@ -0,0 +1,65 @@
+/* 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 protocol PBackground;
+include protocol PBackgroundSDBRequest;
+
+namespace mozilla {
+namespace dom {
+
+struct SDBRequestOpenParams
+{
+ nsString name;
+};
+
+struct SDBRequestSeekParams
+{
+ uint64_t offset;
+};
+
+struct SDBRequestReadParams
+{
+ uint64_t size;
+};
+
+struct SDBRequestWriteParams
+{
+ nsCString data;
+};
+
+struct SDBRequestCloseParams
+{
+};
+
+union SDBRequestParams
+{
+ SDBRequestOpenParams;
+ SDBRequestSeekParams;
+ SDBRequestReadParams;
+ SDBRequestWriteParams;
+ SDBRequestCloseParams;
+};
+
+[ManualDealloc, ChildImpl=virtual, ParentImpl=virtual]
+protocol PBackgroundSDBConnection
+{
+ manager PBackground;
+
+ manages PBackgroundSDBRequest;
+
+parent:
+ async DeleteMe();
+
+ async PBackgroundSDBRequest(SDBRequestParams params);
+
+child:
+ async __delete__();
+
+ async AllowToClose();
+
+ async Closed();
+};
+
+} // namespace dom
+} // namespace mozilla
diff --git a/dom/simpledb/PBackgroundSDBRequest.ipdl b/dom/simpledb/PBackgroundSDBRequest.ipdl
new file mode 100644
index 0000000000..938edc0122
--- /dev/null
+++ b/dom/simpledb/PBackgroundSDBRequest.ipdl
@@ -0,0 +1,51 @@
+/* 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 protocol PBackgroundSDBConnection;
+
+namespace mozilla {
+namespace dom {
+
+struct SDBRequestOpenResponse
+{
+};
+
+struct SDBRequestSeekResponse
+{
+};
+
+struct SDBRequestReadResponse
+{
+ nsCString data;
+};
+
+struct SDBRequestWriteResponse
+{
+};
+
+struct SDBRequestCloseResponse
+{
+};
+
+union SDBRequestResponse
+{
+ nsresult;
+ SDBRequestOpenResponse;
+ SDBRequestSeekResponse;
+ SDBRequestReadResponse;
+ SDBRequestWriteResponse;
+ SDBRequestCloseResponse;
+};
+
+[ManualDealloc, ChildImpl=virtual, ParentImpl=virtual]
+protocol PBackgroundSDBRequest
+{
+ manager PBackgroundSDBConnection;
+
+child:
+ async __delete__(SDBRequestResponse response);
+};
+
+} // namespace dom
+} // namespace mozilla
diff --git a/dom/simpledb/SDBConnection.cpp b/dom/simpledb/SDBConnection.cpp
new file mode 100644
index 0000000000..25d3273c55
--- /dev/null
+++ b/dom/simpledb/SDBConnection.cpp
@@ -0,0 +1,433 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=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 "SDBConnection.h"
+
+// Local includes
+#include "ActorsChild.h"
+#include "SDBRequest.h"
+#include "SimpleDBCommon.h"
+
+// Global includes
+#include <stdint.h>
+#include <utility>
+#include "MainThreadUtils.h"
+#include "js/ArrayBuffer.h"
+#include "js/RootingAPI.h"
+#include "js/TypeDecls.h"
+#include "js/experimental/TypedData.h"
+#include "mozilla/Assertions.h"
+#include "mozilla/MacroForEach.h"
+#include "mozilla/Maybe.h"
+#include "mozilla/Preferences.h"
+#include "mozilla/RefPtr.h"
+#include "mozilla/Variant.h"
+#include "mozilla/dom/PBackgroundSDBConnection.h"
+#include "mozilla/dom/quota/QuotaManager.h"
+#include "mozilla/fallible.h"
+#include "mozilla/ipc/BackgroundChild.h"
+#include "mozilla/ipc/BackgroundUtils.h"
+#include "mozilla/ipc/PBackgroundChild.h"
+#include "mozilla/ipc/PBackgroundSharedTypes.h"
+#include "nsDebug.h"
+#include "nsError.h"
+#include "nsISDBCallbacks.h"
+#include "nsISupportsUtils.h"
+#include "nsStringFwd.h"
+#include "nscore.h"
+
+namespace mozilla::dom {
+
+using namespace mozilla::ipc;
+
+namespace {
+
+nsresult GetWriteData(JSContext* aCx, JS::Handle<JS::Value> aValue,
+ nsCString& aData) {
+ if (aValue.isObject()) {
+ JS::Rooted<JSObject*> obj(aCx, &aValue.toObject());
+
+ bool isView = false;
+ if (JS::IsArrayBufferObject(obj) ||
+ (isView = JS_IsArrayBufferViewObject(obj))) {
+ uint8_t* data;
+ size_t length;
+ bool unused;
+ if (isView) {
+ JS_GetObjectAsArrayBufferView(obj, &length, &unused, &data);
+ } else {
+ JS::GetObjectAsArrayBuffer(obj, &length, &data);
+ }
+
+ // Throw for large buffers to prevent truncation.
+ if (length > INT32_MAX) {
+ return NS_ERROR_ILLEGAL_VALUE;
+ }
+
+ if (NS_WARN_IF(!aData.Assign(reinterpret_cast<char*>(data), length,
+ fallible_t()))) {
+ return NS_ERROR_OUT_OF_MEMORY;
+ }
+
+ return NS_OK;
+ }
+ }
+
+ return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+} // namespace
+
+SDBConnection::SDBConnection()
+ : mBackgroundActor(nullptr),
+ mPersistenceType(quota::PERSISTENCE_TYPE_INVALID),
+ mRunningRequest(false),
+ mOpen(false),
+ mAllowedToClose(false) {
+ AssertIsOnOwningThread();
+}
+
+SDBConnection::~SDBConnection() {
+ AssertIsOnOwningThread();
+
+ if (mBackgroundActor) {
+ mBackgroundActor->SendDeleteMeInternal();
+ MOZ_ASSERT(!mBackgroundActor, "SendDeleteMeInternal should have cleared!");
+ }
+}
+
+// static
+nsresult SDBConnection::Create(REFNSIID aIID, void** aResult) {
+ MOZ_ASSERT(aResult);
+
+ if (NS_WARN_IF(!Preferences::GetBool(kPrefSimpleDBEnabled, false))) {
+ return NS_ERROR_NOT_AVAILABLE;
+ }
+
+ RefPtr<SDBConnection> connection = new SDBConnection();
+
+ nsresult rv = connection->QueryInterface(aIID, aResult);
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+
+ return NS_OK;
+}
+
+void SDBConnection::ClearBackgroundActor() {
+ AssertIsOnOwningThread();
+
+ mBackgroundActor = nullptr;
+}
+
+void SDBConnection::OnNewRequest() {
+ AssertIsOnOwningThread();
+ MOZ_ASSERT(!mRunningRequest);
+
+ mRunningRequest = true;
+}
+
+void SDBConnection::OnRequestFinished() {
+ AssertIsOnOwningThread();
+ MOZ_ASSERT(mRunningRequest);
+
+ mRunningRequest = false;
+}
+
+void SDBConnection::OnOpen() {
+ AssertIsOnOwningThread();
+ MOZ_ASSERT(!mOpen);
+
+ mOpen = true;
+}
+
+void SDBConnection::OnClose(bool aAbnormal) {
+ AssertIsOnOwningThread();
+ MOZ_ASSERT(mOpen);
+
+ mOpen = false;
+
+ if (aAbnormal) {
+ MOZ_ASSERT(mAllowedToClose);
+
+ if (mCloseCallback) {
+ mCloseCallback->OnClose(this);
+ }
+ }
+}
+
+void SDBConnection::AllowToClose() {
+ AssertIsOnOwningThread();
+
+ mAllowedToClose = true;
+}
+
+nsresult SDBConnection::CheckState() {
+ AssertIsOnOwningThread();
+
+ if (mAllowedToClose) {
+ return NS_ERROR_ABORT;
+ }
+
+ if (mRunningRequest) {
+ return NS_ERROR_NOT_AVAILABLE;
+ }
+
+ return NS_OK;
+}
+
+nsresult SDBConnection::EnsureBackgroundActor() {
+ AssertIsOnOwningThread();
+
+ if (mBackgroundActor) {
+ return NS_OK;
+ }
+
+ PBackgroundChild* backgroundActor =
+ BackgroundChild::GetOrCreateForCurrentThread();
+ if (NS_WARN_IF(!backgroundActor)) {
+ return NS_ERROR_FAILURE;
+ }
+
+ SDBConnectionChild* actor = new SDBConnectionChild(this);
+
+ mBackgroundActor = static_cast<SDBConnectionChild*>(
+ backgroundActor->SendPBackgroundSDBConnectionConstructor(
+ actor, mPersistenceType, *mPrincipalInfo));
+ if (NS_WARN_IF(!mBackgroundActor)) {
+ return NS_ERROR_FAILURE;
+ }
+
+ return NS_OK;
+}
+
+nsresult SDBConnection::InitiateRequest(SDBRequest* aRequest,
+ const SDBRequestParams& aParams) {
+ AssertIsOnOwningThread();
+ MOZ_ASSERT(aRequest);
+ MOZ_ASSERT(mBackgroundActor);
+
+ auto actor = new SDBRequestChild(aRequest);
+
+ if (!mBackgroundActor->SendPBackgroundSDBRequestConstructor(actor, aParams)) {
+ return NS_ERROR_FAILURE;
+ }
+
+ // Balanced in SDBRequestChild::Recv__delete__().
+ OnNewRequest();
+
+ return NS_OK;
+}
+
+NS_IMPL_ISUPPORTS(SDBConnection, nsISDBConnection)
+
+NS_IMETHODIMP
+SDBConnection::Init(nsIPrincipal* aPrincipal,
+ const nsACString& aPersistenceType) {
+ MOZ_ASSERT(NS_IsMainThread());
+ MOZ_ASSERT(aPrincipal);
+
+ UniquePtr<PrincipalInfo> principalInfo(new PrincipalInfo());
+ nsresult rv = PrincipalToPrincipalInfo(aPrincipal, principalInfo.get());
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+
+ if (principalInfo->type() != PrincipalInfo::TContentPrincipalInfo &&
+ principalInfo->type() != PrincipalInfo::TSystemPrincipalInfo) {
+ NS_WARNING("Simpledb not allowed for this principal!");
+ return NS_ERROR_INVALID_ARG;
+ }
+
+ if (NS_WARN_IF(!quota::QuotaManager::IsPrincipalInfoValid(*principalInfo))) {
+ return NS_ERROR_INVALID_ARG;
+ }
+
+ PersistenceType persistenceType;
+ if (aPersistenceType.IsVoid()) {
+ persistenceType = quota::PERSISTENCE_TYPE_DEFAULT;
+ } else {
+ const auto maybePersistenceType =
+ quota::PersistenceTypeFromString(aPersistenceType, fallible);
+ if (NS_WARN_IF(maybePersistenceType.isNothing())) {
+ return NS_ERROR_INVALID_ARG;
+ }
+
+ persistenceType = maybePersistenceType.value();
+ }
+
+ mPrincipalInfo = std::move(principalInfo);
+ mPersistenceType = persistenceType;
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+SDBConnection::Open(const nsAString& aName, nsISDBRequest** _retval) {
+ AssertIsOnOwningThread();
+
+ nsresult rv = CheckState();
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+
+ if (mOpen) {
+ return NS_ERROR_ALREADY_INITIALIZED;
+ }
+
+ SDBRequestOpenParams params;
+ params.name() = aName;
+
+ RefPtr<SDBRequest> request = new SDBRequest(this);
+
+ rv = EnsureBackgroundActor();
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+
+ rv = InitiateRequest(request, params);
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+
+ request.forget(_retval);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+SDBConnection::Seek(uint64_t aOffset, nsISDBRequest** _retval) {
+ AssertIsOnOwningThread();
+
+ nsresult rv = CheckState();
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+
+ if (!mOpen) {
+ return NS_BASE_STREAM_CLOSED;
+ }
+
+ SDBRequestSeekParams params;
+ params.offset() = aOffset;
+
+ RefPtr<SDBRequest> request = new SDBRequest(this);
+
+ rv = InitiateRequest(request, params);
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+
+ request.forget(_retval);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+SDBConnection::Read(uint64_t aSize, nsISDBRequest** _retval) {
+ AssertIsOnOwningThread();
+
+ nsresult rv = CheckState();
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+
+ if (!mOpen) {
+ return NS_BASE_STREAM_CLOSED;
+ }
+
+ SDBRequestReadParams params;
+ params.size() = aSize;
+
+ RefPtr<SDBRequest> request = new SDBRequest(this);
+
+ rv = InitiateRequest(request, params);
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+
+ request.forget(_retval);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+SDBConnection::Write(JS::Handle<JS::Value> aValue, JSContext* aCx,
+ nsISDBRequest** _retval) {
+ AssertIsOnOwningThread();
+
+ nsresult rv = CheckState();
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+
+ if (!mOpen) {
+ return NS_BASE_STREAM_CLOSED;
+ }
+
+ JS::Rooted<JS::Value> value(aCx, aValue);
+
+ nsCString data;
+ rv = GetWriteData(aCx, value, data);
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+
+ SDBRequestWriteParams params;
+ params.data() = data;
+
+ RefPtr<SDBRequest> request = new SDBRequest(this);
+
+ rv = InitiateRequest(request, params);
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+
+ request.forget(_retval);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+SDBConnection::Close(nsISDBRequest** _retval) {
+ AssertIsOnOwningThread();
+
+ nsresult rv = CheckState();
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+
+ if (!mOpen) {
+ return NS_BASE_STREAM_CLOSED;
+ }
+
+ SDBRequestCloseParams params;
+
+ RefPtr<SDBRequest> request = new SDBRequest(this);
+
+ rv = InitiateRequest(request, params);
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+
+ request.forget(_retval);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+SDBConnection::GetCloseCallback(nsISDBCloseCallback** aCloseCallback) {
+ AssertIsOnOwningThread();
+ MOZ_ASSERT(aCloseCallback);
+
+ NS_IF_ADDREF(*aCloseCallback = mCloseCallback);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+SDBConnection::SetCloseCallback(nsISDBCloseCallback* aCloseCallback) {
+ AssertIsOnOwningThread();
+
+ mCloseCallback = aCloseCallback;
+ return NS_OK;
+}
+
+} // namespace mozilla::dom
diff --git a/dom/simpledb/SDBConnection.h b/dom/simpledb/SDBConnection.h
new file mode 100644
index 0000000000..0f0a714f89
--- /dev/null
+++ b/dom/simpledb/SDBConnection.h
@@ -0,0 +1,90 @@
+/* -*- 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/. */
+
+#ifndef mozilla_dom_simpledb_SDBConnection_h
+#define mozilla_dom_simpledb_SDBConnection_h
+
+#include <cstdint>
+#include "ErrorList.h"
+#include "mozilla/UniquePtr.h"
+#include "mozilla/dom/quota/PersistenceType.h"
+#include "nsCOMPtr.h"
+#include "nsID.h"
+#include "nsISDBConnection.h"
+#include "nsISupports.h"
+
+#define NS_SDBCONNECTION_CONTRACTID "@mozilla.org/dom/sdb-connection;1"
+
+class nsISDBCloseCallback;
+
+namespace mozilla {
+
+namespace ipc {
+
+class PBackgroundChild;
+class PrincipalInfo;
+
+} // namespace ipc
+
+namespace dom {
+
+class SDBConnectionChild;
+class SDBRequest;
+class SDBRequestParams;
+
+class SDBConnection final : public nsISDBConnection {
+ using PersistenceType = mozilla::dom::quota::PersistenceType;
+ using PBackgroundChild = mozilla::ipc::PBackgroundChild;
+ using PrincipalInfo = mozilla::ipc::PrincipalInfo;
+
+ nsCOMPtr<nsISDBCloseCallback> mCloseCallback;
+
+ UniquePtr<PrincipalInfo> mPrincipalInfo;
+
+ SDBConnectionChild* mBackgroundActor;
+
+ PersistenceType mPersistenceType;
+ bool mRunningRequest;
+ bool mOpen;
+ bool mAllowedToClose;
+
+ public:
+ static nsresult Create(REFNSIID aIID, void** aResult);
+
+ void AssertIsOnOwningThread() const { NS_ASSERT_OWNINGTHREAD(SDBConnection); }
+
+ void ClearBackgroundActor();
+
+ void OnNewRequest();
+
+ void OnRequestFinished();
+
+ void OnOpen();
+
+ void OnClose(bool aAbnormal);
+
+ void AllowToClose();
+
+ private:
+ SDBConnection();
+
+ ~SDBConnection();
+
+ nsresult CheckState();
+
+ nsresult EnsureBackgroundActor();
+
+ nsresult InitiateRequest(SDBRequest* aRequest,
+ const SDBRequestParams& aParams);
+
+ NS_DECL_ISUPPORTS
+ NS_DECL_NSISDBCONNECTION
+};
+
+} // namespace dom
+} // namespace mozilla
+
+#endif /* mozilla_dom_simpledb_SDBConnection_h */
diff --git a/dom/simpledb/SDBRequest.cpp b/dom/simpledb/SDBRequest.cpp
new file mode 100644
index 0000000000..1f5bccc3c7
--- /dev/null
+++ b/dom/simpledb/SDBRequest.cpp
@@ -0,0 +1,125 @@
+/* -*- 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 "SDBRequest.h"
+
+// Local includes
+#include "SDBConnection.h"
+
+// Global includes
+#include <utility>
+#include "mozilla/MacroForEach.h"
+#include "nsError.h"
+#include "nsISDBCallbacks.h"
+#include "nsISupportsUtils.h"
+#include "nsIVariant.h"
+#include "nsThreadUtils.h"
+#include "nscore.h"
+
+namespace mozilla::dom {
+
+SDBRequest::SDBRequest(SDBConnection* aConnection)
+ : mConnection(aConnection),
+ mResultCode(NS_OK),
+ mHaveResultOrErrorCode(false) {
+ AssertIsOnOwningThread();
+ MOZ_ASSERT(aConnection);
+}
+
+SDBRequest::~SDBRequest() { AssertIsOnOwningThread(); }
+
+void SDBRequest::SetResult(nsIVariant* aResult) {
+ AssertIsOnOwningThread();
+ MOZ_ASSERT(aResult);
+ MOZ_ASSERT(mConnection);
+ MOZ_ASSERT(!mHaveResultOrErrorCode);
+
+ mResult = aResult;
+ mHaveResultOrErrorCode = true;
+
+ FireCallback();
+}
+
+void SDBRequest::SetError(nsresult aRv) {
+ AssertIsOnOwningThread();
+ MOZ_ASSERT(mConnection);
+ MOZ_ASSERT(mResultCode == NS_OK);
+ MOZ_ASSERT(!mHaveResultOrErrorCode);
+
+ mResultCode = aRv;
+ mHaveResultOrErrorCode = true;
+
+ FireCallback();
+}
+
+void SDBRequest::FireCallback() {
+ AssertIsOnOwningThread();
+
+ if (mCallback) {
+ nsCOMPtr<nsISDBCallback> callback;
+ callback.swap(mCallback);
+
+ MOZ_ALWAYS_SUCCEEDS(
+ NS_DispatchToCurrentThread(NewRunnableMethod<RefPtr<SDBRequest>>(
+ "nsISDBCallback::OnComplete", callback, &nsISDBCallback::OnComplete,
+ this)));
+ }
+}
+
+NS_IMPL_CYCLE_COLLECTING_ADDREF(SDBRequest)
+NS_IMPL_CYCLE_COLLECTING_RELEASE(SDBRequest)
+
+NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(SDBRequest)
+ NS_INTERFACE_MAP_ENTRY(nsISupports)
+ NS_INTERFACE_MAP_ENTRY(nsISDBRequest)
+NS_INTERFACE_MAP_END
+
+NS_IMPL_CYCLE_COLLECTION(SDBRequest, mCallback, mResult)
+
+NS_IMETHODIMP
+SDBRequest::GetResult(nsIVariant** aResult) {
+ AssertIsOnOwningThread();
+ MOZ_ASSERT(aResult);
+
+ if (!mHaveResultOrErrorCode) {
+ return NS_ERROR_NOT_AVAILABLE;
+ }
+
+ NS_IF_ADDREF(*aResult = mResult);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+SDBRequest::GetResultCode(nsresult* aResultCode) {
+ AssertIsOnOwningThread();
+ MOZ_ASSERT(aResultCode);
+
+ if (!mHaveResultOrErrorCode) {
+ return NS_ERROR_NOT_AVAILABLE;
+ }
+
+ *aResultCode = mResultCode;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+SDBRequest::GetCallback(nsISDBCallback** aCallback) {
+ AssertIsOnOwningThread();
+ MOZ_ASSERT(aCallback);
+
+ NS_IF_ADDREF(*aCallback = mCallback);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+SDBRequest::SetCallback(nsISDBCallback* aCallback) {
+ AssertIsOnOwningThread();
+
+ mCallback = aCallback;
+ return NS_OK;
+}
+
+} // namespace mozilla::dom
diff --git a/dom/simpledb/SDBRequest.h b/dom/simpledb/SDBRequest.h
new file mode 100644
index 0000000000..450cf8a788
--- /dev/null
+++ b/dom/simpledb/SDBRequest.h
@@ -0,0 +1,62 @@
+/* -*- 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/. */
+
+#ifndef mozilla_dom_simpledb_SDBRequest_h
+#define mozilla_dom_simpledb_SDBRequest_h
+
+#include <cstdint>
+#include "ErrorList.h"
+#include "mozilla/Assertions.h"
+#include "mozilla/RefPtr.h"
+#include "nsCOMPtr.h"
+#include "nsCycleCollectionParticipant.h"
+#include "nsISDBRequest.h"
+#include "nsISupports.h"
+
+class nsISDBCallback;
+class nsIVariant;
+
+namespace mozilla::dom {
+
+class SDBConnection;
+
+class SDBRequest final : public nsISDBRequest {
+ RefPtr<SDBConnection> mConnection;
+
+ nsCOMPtr<nsIVariant> mResult;
+ nsCOMPtr<nsISDBCallback> mCallback;
+
+ nsresult mResultCode;
+ bool mHaveResultOrErrorCode;
+
+ public:
+ explicit SDBRequest(SDBConnection* aConnection);
+
+ void AssertIsOnOwningThread() const { NS_ASSERT_OWNINGTHREAD(SDBRequest); }
+
+ SDBConnection* GetConnection() const {
+ AssertIsOnOwningThread();
+
+ return mConnection;
+ }
+
+ void SetResult(nsIVariant* aResult);
+
+ void SetError(nsresult aRv);
+
+ private:
+ ~SDBRequest();
+
+ void FireCallback();
+
+ NS_DECL_CYCLE_COLLECTING_ISUPPORTS
+ NS_DECL_NSISDBREQUEST
+ NS_DECL_CYCLE_COLLECTION_CLASS(SDBRequest)
+};
+
+} // namespace mozilla::dom
+
+#endif // mozilla_dom_simpledb_SDBRequest_h
diff --git a/dom/simpledb/SDBResults.cpp b/dom/simpledb/SDBResults.cpp
new file mode 100644
index 0000000000..e17f437988
--- /dev/null
+++ b/dom/simpledb/SDBResults.cpp
@@ -0,0 +1,56 @@
+/* -*- 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 "SDBResults.h"
+
+#include <cstdint>
+#include <cstring>
+#include <new>
+#include <utility>
+#include "ErrorList.h"
+#include "js/RootingAPI.h"
+#include "js/TypeDecls.h"
+#include "mozilla/Assertions.h"
+#include "mozilla/MacroForEach.h"
+#include "nsContentUtils.h"
+#include "nsDebug.h"
+#include "nsError.h"
+#include "nsTArray.h"
+#include "nscore.h"
+
+namespace mozilla::dom {
+
+SDBResult::SDBResult(const nsACString& aData) : mData(aData) {}
+
+NS_IMPL_ISUPPORTS(SDBResult, nsISDBResult)
+
+NS_IMETHODIMP
+SDBResult::GetAsArray(nsTArray<uint8_t>& aData) {
+ uint32_t length = mData.Length();
+ aData.SetLength(length);
+
+ if (length != 0) {
+ memcpy(aData.Elements(), mData.BeginReading(), length * sizeof(uint8_t));
+ }
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+SDBResult::GetAsArrayBuffer(JSContext* aCx,
+ JS::MutableHandle<JS::Value> _retval) {
+ JS::Rooted<JSObject*> arrayBuffer(aCx);
+ nsresult rv =
+ nsContentUtils::CreateArrayBuffer(aCx, mData, arrayBuffer.address());
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+
+ _retval.setObject(*arrayBuffer);
+ return NS_OK;
+}
+
+} // namespace mozilla::dom
diff --git a/dom/simpledb/SDBResults.h b/dom/simpledb/SDBResults.h
new file mode 100644
index 0000000000..1fbebbda47
--- /dev/null
+++ b/dom/simpledb/SDBResults.h
@@ -0,0 +1,31 @@
+/* -*- 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/. */
+
+#ifndef mozilla_dom_simpledb_SDBResults_h
+#define mozilla_dom_simpledb_SDBResults_h
+
+#include "nsISDBResults.h"
+#include "nsISupports.h"
+#include "nsString.h"
+
+namespace mozilla::dom {
+
+class SDBResult : public nsISDBResult {
+ nsCString mData;
+
+ public:
+ explicit SDBResult(const nsACString& aData);
+
+ private:
+ virtual ~SDBResult() = default;
+
+ NS_DECL_ISUPPORTS
+ NS_DECL_NSISDBRESULT
+};
+
+} // namespace mozilla::dom
+
+#endif // mozilla_dom_simpledb_SDBResults_h
diff --git a/dom/simpledb/SimpleDBCommon.cpp b/dom/simpledb/SimpleDBCommon.cpp
new file mode 100644
index 0000000000..8be1a7d603
--- /dev/null
+++ b/dom/simpledb/SimpleDBCommon.cpp
@@ -0,0 +1,13 @@
+/* -*- 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 "SimpleDBCommon.h"
+
+namespace mozilla::dom {
+
+const char* kPrefSimpleDBEnabled = "dom.simpleDB.enabled";
+
+} // namespace mozilla::dom
diff --git a/dom/simpledb/SimpleDBCommon.h b/dom/simpledb/SimpleDBCommon.h
new file mode 100644
index 0000000000..5eecb66932
--- /dev/null
+++ b/dom/simpledb/SimpleDBCommon.h
@@ -0,0 +1,18 @@
+/* -*- 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/. */
+
+#ifndef mozilla_dom_simpledb_SimpledbCommon_h
+#define mozilla_dom_simpledb_SimpledbCommon_h
+
+#include "mozilla/dom/quota/QuotaCommon.h"
+
+namespace mozilla::dom {
+
+extern const char* kPrefSimpleDBEnabled;
+
+} // namespace mozilla::dom
+
+#endif // mozilla_dom_simpledb_SimpledbCommon_h
diff --git a/dom/simpledb/moz.build b/dom/simpledb/moz.build
new file mode 100644
index 0000000000..54b4568f69
--- /dev/null
+++ b/dom/simpledb/moz.build
@@ -0,0 +1,40 @@
+# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
+# vim: set filetype=python:
+# 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/.
+
+XPIDL_SOURCES += [
+ "nsISDBCallbacks.idl",
+ "nsISDBConnection.idl",
+ "nsISDBRequest.idl",
+ "nsISDBResults.idl",
+]
+
+XPIDL_MODULE = "dom_simpledb"
+
+EXPORTS.mozilla.dom.simpledb += [
+ "ActorsParent.h",
+]
+
+EXPORTS.mozilla.dom += [
+ "SDBConnection.h",
+]
+
+UNIFIED_SOURCES += [
+ "ActorsChild.cpp",
+ "ActorsParent.cpp",
+ "SDBConnection.cpp",
+ "SDBRequest.cpp",
+ "SDBResults.cpp",
+ "SimpleDBCommon.cpp",
+]
+
+IPDL_SOURCES += [
+ "PBackgroundSDBConnection.ipdl",
+ "PBackgroundSDBRequest.ipdl",
+]
+
+include("/ipc/chromium/chromium-config.mozbuild")
+
+FINAL_LIBRARY = "xul"
diff --git a/dom/simpledb/nsISDBCallbacks.idl b/dom/simpledb/nsISDBCallbacks.idl
new file mode 100644
index 0000000000..eee5297d2e
--- /dev/null
+++ b/dom/simpledb/nsISDBCallbacks.idl
@@ -0,0 +1,22 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=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 "nsISupports.idl"
+
+interface nsISDBConnection;
+interface nsISDBRequest;
+
+[scriptable, function, uuid(8cbd576c-c6bf-42fd-96ee-3b824dafe1d4)]
+interface nsISDBCallback : nsISupports
+{
+ void onComplete(in nsISDBRequest aRequest);
+};
+
+[scriptable, function, uuid(e0821d43-62b9-40fe-99f8-ff9ab3184cbf)]
+interface nsISDBCloseCallback : nsISupports
+{
+ void onClose(in nsISDBConnection aConnection);
+};
diff --git a/dom/simpledb/nsISDBConnection.idl b/dom/simpledb/nsISDBConnection.idl
new file mode 100644
index 0000000000..ec275fd6c2
--- /dev/null
+++ b/dom/simpledb/nsISDBConnection.idl
@@ -0,0 +1,35 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=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 "nsISupports.idl"
+
+interface nsIPrincipal;
+interface nsISDBCloseCallback;
+interface nsISDBRequest;
+
+[scriptable, builtinclass, uuid(ea420fdd-548f-44f9-9286-59aad6a40f01)]
+interface nsISDBConnection : nsISupports
+{
+ [must_use] void
+ init(in nsIPrincipal aPrincipal, [optional] in ACString aPersistenceType);
+
+ [must_use] nsISDBRequest
+ open(in AString aName);
+
+ [must_use] nsISDBRequest
+ seek(in unsigned long long offset);
+
+ [must_use] nsISDBRequest
+ read(in unsigned long long size);
+
+ [must_use, implicit_jscontext] nsISDBRequest
+ write(in jsval value);
+
+ [must_use] nsISDBRequest
+ close();
+
+ attribute nsISDBCloseCallback closeCallback;
+};
diff --git a/dom/simpledb/nsISDBRequest.idl b/dom/simpledb/nsISDBRequest.idl
new file mode 100644
index 0000000000..f51c0b5ff0
--- /dev/null
+++ b/dom/simpledb/nsISDBRequest.idl
@@ -0,0 +1,20 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=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 "nsISupports.idl"
+
+interface nsISDBCallback;
+interface nsIVariant;
+
+[scriptable, uuid(13f05bcf-715c-427e-aac8-df9b2c1ec1e3)]
+interface nsISDBRequest : nsISupports
+{
+ [must_use] readonly attribute nsIVariant result;
+
+ [must_use] readonly attribute nsresult resultCode;
+
+ attribute nsISDBCallback callback;
+};
diff --git a/dom/simpledb/nsISDBResults.idl b/dom/simpledb/nsISDBResults.idl
new file mode 100644
index 0000000000..0e4b25e759
--- /dev/null
+++ b/dom/simpledb/nsISDBResults.idl
@@ -0,0 +1,17 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=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 "nsISupports.idl"
+
+[scriptable, uuid(bca19e01-b34e-4a48-8875-2f4cb871febf)]
+interface nsISDBResult : nsISupports
+{
+ [must_use] Array<uint8_t>
+ getAsArray();
+
+ [must_use, implicit_jscontext] jsval
+ getAsArrayBuffer();
+};