diff options
Diffstat (limited to 'dom/simpledb')
-rw-r--r-- | dom/simpledb/ActorsChild.cpp | 222 | ||||
-rw-r--r-- | dom/simpledb/ActorsChild.h | 106 | ||||
-rw-r--r-- | dom/simpledb/ActorsParent.cpp | 1792 | ||||
-rw-r--r-- | dom/simpledb/ActorsParent.h | 54 | ||||
-rw-r--r-- | dom/simpledb/PBackgroundSDBConnection.ipdl | 65 | ||||
-rw-r--r-- | dom/simpledb/PBackgroundSDBRequest.ipdl | 51 | ||||
-rw-r--r-- | dom/simpledb/SDBConnection.cpp | 433 | ||||
-rw-r--r-- | dom/simpledb/SDBConnection.h | 90 | ||||
-rw-r--r-- | dom/simpledb/SDBRequest.cpp | 125 | ||||
-rw-r--r-- | dom/simpledb/SDBRequest.h | 62 | ||||
-rw-r--r-- | dom/simpledb/SDBResults.cpp | 56 | ||||
-rw-r--r-- | dom/simpledb/SDBResults.h | 31 | ||||
-rw-r--r-- | dom/simpledb/SimpleDBCommon.cpp | 13 | ||||
-rw-r--r-- | dom/simpledb/SimpleDBCommon.h | 18 | ||||
-rw-r--r-- | dom/simpledb/moz.build | 40 | ||||
-rw-r--r-- | dom/simpledb/nsISDBCallbacks.idl | 22 | ||||
-rw-r--r-- | dom/simpledb/nsISDBConnection.idl | 35 | ||||
-rw-r--r-- | dom/simpledb/nsISDBRequest.idl | 20 | ||||
-rw-r--r-- | dom/simpledb/nsISDBResults.idl | 17 |
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(¤t)) && + 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(), "aManager, + 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(); +}; |