forked from mirrors/gecko-dev
		
	
		
			
				
	
	
		
			1771 lines
		
	
	
	
		
			44 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			1771 lines
		
	
	
	
		
			44 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
| /* -*- 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"
 | |
| 
 | |
| #include "mozilla/Unused.h"
 | |
| #include "mozilla/dom/PBackgroundSDBConnectionParent.h"
 | |
| #include "mozilla/dom/PBackgroundSDBRequestParent.h"
 | |
| #include "mozilla/dom/quota/Client.h"
 | |
| #include "mozilla/dom/quota/FileStreams.h"
 | |
| #include "mozilla/dom/quota/MemoryOutputStream.h"
 | |
| #include "mozilla/dom/quota/QuotaManager.h"
 | |
| #include "mozilla/dom/quota/UsageInfo.h"
 | |
| #include "mozilla/ipc/BackgroundParent.h"
 | |
| #include "mozilla/ipc/PBackgroundParent.h"
 | |
| #include "mozilla/ipc/PBackgroundSharedTypes.h"
 | |
| #include "nsIFileStreams.h"
 | |
| #include "nsIDirectoryEnumerator.h"
 | |
| #include "nsIPrincipal.h"
 | |
| #include "nsStringStream.h"
 | |
| #include "prio.h"
 | |
| #include "SimpleDBCommon.h"
 | |
| 
 | |
| #define DISABLE_ASSERTS_FOR_FUZZING 0
 | |
| 
 | |
| #if DISABLE_ASSERTS_FOR_FUZZING
 | |
| #  define ASSERT_UNLESS_FUZZING(...) \
 | |
|     do {                             \
 | |
|     } while (0)
 | |
| #else
 | |
| #  define ASSERT_UNLESS_FUZZING(...) MOZ_ASSERT(false, __VA_ARGS__)
 | |
| #endif
 | |
| 
 | |
| namespace mozilla {
 | |
| namespace 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<nsIFileStream> mFileStream;
 | |
|   nsCOMPtr<nsIRunnable> mCallback;
 | |
| 
 | |
|  public:
 | |
|   StreamHelper(nsIFileStream* aFileStream, nsIRunnable* aCallback);
 | |
| 
 | |
|   void AsyncClose();
 | |
| 
 | |
|  private:
 | |
|   ~StreamHelper() override;
 | |
| 
 | |
|   void RunOnBackgroundThread();
 | |
| 
 | |
|   void RunOnIOThread();
 | |
| 
 | |
|   NS_DECL_NSIRUNNABLE
 | |
| };
 | |
| 
 | |
| class Connection final : public PBackgroundSDBConnectionParent {
 | |
|   RefPtr<DirectoryLock> mDirectoryLock;
 | |
|   nsCOMPtr<nsIFileStream> mFileStream;
 | |
|   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)
 | |
| 
 | |
|   nsIFileStream* GetFileStream() const {
 | |
|     AssertIsOnIOThread();
 | |
| 
 | |
|     return mFileStream;
 | |
|   }
 | |
| 
 | |
|   PersistenceType GetPersistenceType() const { return mPersistenceType; }
 | |
| 
 | |
|   const PrincipalInfo& GetPrincipalInfo() const {
 | |
|     MOZ_ASSERT(NS_IsMainThread());
 | |
| 
 | |
|     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<nsIFileStream> aFileStream);
 | |
| 
 | |
|   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(GetCurrentEventTarget()),
 | |
|         mConnection(aConnection),
 | |
|         mResultCode(NS_OK),
 | |
|         mOperationMayProceed(true),
 | |
|         mActorDestroyed(false) {
 | |
|     AssertIsOnOwningThread();
 | |
|   }
 | |
| 
 | |
|   ~ConnectionOperationBase() override;
 | |
| 
 | |
|   void SendResults();
 | |
| 
 | |
|   void DatabaseWork();
 | |
| 
 | |
|   // Methods that subclasses must implement.
 | |
|   virtual nsresult DoDatabaseWork(nsIFileStream* aFileStream) = 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,
 | |
| 
 | |
|     // Opening directory or initializing quota manager on the PBackground
 | |
|     // thread. Next step is either DirectoryOpenPending if quota manager is
 | |
|     // already initialized or QuotaManagerPending if quota manager needs to be
 | |
|     // initialized.
 | |
|     FinishOpen,
 | |
| 
 | |
|     // Waiting for quota manager initialization to complete on the PBackground
 | |
|     // thread. Next step is either SendingResults if initialization failed or
 | |
|     // DirectoryOpenPending if initialization succeeded.
 | |
|     QuotaManagerPending,
 | |
| 
 | |
|     // 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<nsIFileStream> mFileStream;
 | |
|   nsCString mSuffix;
 | |
|   nsCString mGroup;
 | |
|   nsCString mOrigin;
 | |
|   State mState;
 | |
|   bool mFileStreamOpen;
 | |
| 
 | |
|  public:
 | |
|   OpenOp(Connection* aConnection, const SDBRequestParams& aParams);
 | |
| 
 | |
|   nsresult Dispatch() override;
 | |
| 
 | |
|  private:
 | |
|   ~OpenOp() override;
 | |
| 
 | |
|   nsresult Open();
 | |
| 
 | |
|   nsresult FinishOpen();
 | |
| 
 | |
|   nsresult QuotaManagerOpen();
 | |
| 
 | |
|   nsresult OpenDirectory();
 | |
| 
 | |
|   nsresult SendToIOThread();
 | |
| 
 | |
|   nsresult DatabaseWork();
 | |
| 
 | |
|   void StreamClosedCallback();
 | |
| 
 | |
|   // ConnectionOperationBase overrides
 | |
|   nsresult DoDatabaseWork(nsIFileStream* aFileStream) 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(nsIFileStream* aFileStream) override;
 | |
| 
 | |
|   void GetResponse(SDBRequestResponse& aResponse) override;
 | |
| };
 | |
| 
 | |
| class ReadOp final : public ConnectionOperationBase {
 | |
|   const SDBRequestReadParams mParams;
 | |
| 
 | |
|   RefPtr<MemoryOutputStream> mOutputStream;
 | |
| 
 | |
|  public:
 | |
|   ReadOp(Connection* aConnection, const SDBRequestParams& aParams);
 | |
| 
 | |
|   bool Init() override;
 | |
| 
 | |
|  private:
 | |
|   ~ReadOp() override = default;
 | |
| 
 | |
|   nsresult DoDatabaseWork(nsIFileStream* aFileStream) 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(nsIFileStream* aFileStream) override;
 | |
| 
 | |
|   void GetResponse(SDBRequestResponse& aResponse) override;
 | |
| };
 | |
| 
 | |
| class CloseOp final : public ConnectionOperationBase {
 | |
|  public:
 | |
|   explicit CloseOp(Connection* aConnection);
 | |
| 
 | |
|  private:
 | |
|   ~CloseOp() override = default;
 | |
| 
 | |
|   nsresult DoDatabaseWork(nsIFileStream* aFileStream) override;
 | |
| 
 | |
|   void GetResponse(SDBRequestResponse& aResponse) override;
 | |
| 
 | |
|   void OnSuccess() override;
 | |
| };
 | |
| 
 | |
| /*******************************************************************************
 | |
|  * Other class declarations
 | |
|  ******************************************************************************/
 | |
| 
 | |
| class QuotaClient final : public mozilla::dom::quota::Client {
 | |
|   static QuotaClient* sInstance;
 | |
| 
 | |
|   bool mShutdownRequested;
 | |
| 
 | |
|  public:
 | |
|   QuotaClient();
 | |
| 
 | |
|   static bool IsShuttingDownOnBackgroundThread() {
 | |
|     AssertIsOnBackgroundThread();
 | |
| 
 | |
|     if (sInstance) {
 | |
|       return sInstance->IsShuttingDown();
 | |
|     }
 | |
| 
 | |
|     return QuotaManager::IsShuttingDown();
 | |
|   }
 | |
| 
 | |
|   static bool IsShuttingDownOnNonBackgroundThread() {
 | |
|     MOZ_ASSERT(!IsOnBackgroundThread());
 | |
| 
 | |
|     return QuotaManager::IsShuttingDown();
 | |
|   }
 | |
| 
 | |
|   bool IsShuttingDown() const {
 | |
|     AssertIsOnBackgroundThread();
 | |
| 
 | |
|     return mShutdownRequested;
 | |
|   }
 | |
| 
 | |
|   NS_INLINE_DECL_THREADSAFE_REFCOUNTING(QuotaClient, override)
 | |
| 
 | |
|   Type GetType() override;
 | |
| 
 | |
|   Result<UsageInfo, nsresult> InitOrigin(PersistenceType aPersistenceType,
 | |
|                                          const nsACString& aGroup,
 | |
|                                          const nsACString& aOrigin,
 | |
|                                          const AtomicBool& aCanceled) override;
 | |
| 
 | |
|   nsresult InitOriginWithoutTracking(PersistenceType aPersistenceType,
 | |
|                                      const nsACString& aGroup,
 | |
|                                      const nsACString& aOrigin,
 | |
|                                      const AtomicBool& aCanceled) override;
 | |
| 
 | |
|   Result<UsageInfo, nsresult> GetUsageForOrigin(
 | |
|       PersistenceType aPersistenceType, const nsACString& aGroup,
 | |
|       const nsACString& aOrigin, const AtomicBool& aCanceled) override;
 | |
| 
 | |
|   void OnOriginClearCompleted(PersistenceType aPersistenceType,
 | |
|                               const nsACString& aOrigin) override;
 | |
| 
 | |
|   void ReleaseIOThreadObjects() override;
 | |
| 
 | |
|   void AbortOperations(const nsACString& aOrigin) override;
 | |
| 
 | |
|   void AbortOperationsForProcess(ContentParentId aContentParentId) override;
 | |
| 
 | |
|   void StartIdleMaintenance() override;
 | |
| 
 | |
|   void StopIdleMaintenance() override;
 | |
| 
 | |
|   void ShutdownWorkThreads() override;
 | |
| 
 | |
|  private:
 | |
|   ~QuotaClient() override;
 | |
| };
 | |
| 
 | |
| /*******************************************************************************
 | |
|  * Globals
 | |
|  ******************************************************************************/
 | |
| 
 | |
| typedef nsTArray<RefPtr<Connection>> ConnectionArray;
 | |
| 
 | |
| StaticAutoPtr<ConnectionArray> gOpenConnections;
 | |
| 
 | |
| }  // 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))) {
 | |
|     ASSERT_UNLESS_FUZZING();
 | |
|     return nullptr;
 | |
|   }
 | |
| 
 | |
|   if (NS_WARN_IF(aPrincipalInfo.type() == PrincipalInfo::TNullPrincipalInfo)) {
 | |
|     ASSERT_UNLESS_FUZZING();
 | |
|     return nullptr;
 | |
|   }
 | |
| 
 | |
|   if (NS_WARN_IF(!QuotaManager::IsPrincipalInfoValid(aPrincipalInfo))) {
 | |
|     ASSERT_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(nsIFileStream* aFileStream, nsIRunnable* aCallback)
 | |
|     : Runnable("dom::StreamHelper"),
 | |
|       mOwningEventTarget(GetCurrentEventTarget()),
 | |
|       mFileStream(aFileStream),
 | |
|       mCallback(aCallback) {
 | |
|   AssertIsOnBackgroundThread();
 | |
|   MOZ_ASSERT(aFileStream);
 | |
|   MOZ_ASSERT(aCallback);
 | |
| }
 | |
| 
 | |
| StreamHelper::~StreamHelper() {
 | |
|   MOZ_ASSERT(!mFileStream);
 | |
|   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<nsIFileStream> fileStream;
 | |
|   mFileStream.swap(fileStream);
 | |
| 
 | |
|   nsCOMPtr<nsIRunnable> callback;
 | |
|   mCallback.swap(callback);
 | |
| 
 | |
|   callback->Run();
 | |
| }
 | |
| 
 | |
| void StreamHelper::RunOnIOThread() {
 | |
|   AssertIsOnIOThread();
 | |
|   MOZ_ASSERT(mFileStream);
 | |
| 
 | |
|   nsCOMPtr<nsIInputStream> inputStream = do_QueryInterface(mFileStream);
 | |
|   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<nsIFileStream> aFileStream) {
 | |
|   AssertIsOnBackgroundThread();
 | |
|   MOZ_ASSERT(!aOrigin.IsEmpty());
 | |
|   MOZ_ASSERT(!aName.IsEmpty());
 | |
|   MOZ_ASSERT(mOrigin.IsEmpty());
 | |
|   MOZ_ASSERT(mName.IsEmpty());
 | |
|   MOZ_ASSERT(!mDirectoryLock);
 | |
|   MOZ_ASSERT(!mFileStream);
 | |
|   MOZ_ASSERT(!mOpen);
 | |
| 
 | |
|   mOrigin = aOrigin;
 | |
|   mName = aName;
 | |
|   mDirectoryLock = aDirectoryLock;
 | |
|   mFileStream = aFileStream;
 | |
|   mOpen = true;
 | |
| 
 | |
|   if (!gOpenConnections) {
 | |
|     gOpenConnections = new ConnectionArray();
 | |
|   }
 | |
| 
 | |
|   gOpenConnections->AppendElement(this);
 | |
| }
 | |
| 
 | |
| void Connection::OnClose() {
 | |
|   AssertIsOnBackgroundThread();
 | |
|   MOZ_ASSERT(!mOrigin.IsEmpty());
 | |
|   MOZ_ASSERT(mDirectoryLock);
 | |
|   MOZ_ASSERT(mFileStream);
 | |
|   MOZ_ASSERT(mOpen);
 | |
| 
 | |
|   mOrigin.Truncate();
 | |
|   mName.Truncate();
 | |
|   mDirectoryLock = nullptr;
 | |
|   mFileStream = 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(mFileStream, 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)) {
 | |
|         ASSERT_UNLESS_FUZZING();
 | |
|         return false;
 | |
|       }
 | |
| 
 | |
|       break;
 | |
|     }
 | |
| 
 | |
|     case SDBRequestParams::TSDBRequestSeekParams:
 | |
|     case SDBRequestParams::TSDBRequestReadParams:
 | |
|     case SDBRequestParams::TSDBRequestWriteParams:
 | |
|     case SDBRequestParams::TSDBRequestCloseParams: {
 | |
|       if (NS_WARN_IF(!mOpen)) {
 | |
|         ASSERT_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))) {
 | |
|     ASSERT_UNLESS_FUZZING();
 | |
|     return nullptr;
 | |
|   }
 | |
| 
 | |
|   if (NS_WARN_IF(mRunningRequest)) {
 | |
|     ASSERT_UNLESS_FUZZING();
 | |
|     return 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_FAILURE;
 | |
|   }
 | |
| 
 | |
|   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_FAILURE;
 | |
|   } else {
 | |
|     nsIFileStream* fileStream = mConnection->GetFileStream();
 | |
|     MOZ_ASSERT(fileStream);
 | |
| 
 | |
|     nsresult rv = DoDatabaseWork(fileStream);
 | |
|     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),
 | |
|       mFileStreamOpen(false) {
 | |
|   MOZ_ASSERT(aParams.type() == SDBRequestParams::TSDBRequestOpenParams);
 | |
| }
 | |
| 
 | |
| OpenOp::~OpenOp() {
 | |
|   MOZ_ASSERT(!mDirectoryLock);
 | |
|   MOZ_ASSERT(!mFileStream);
 | |
|   MOZ_ASSERT(!mFileStreamOpen);
 | |
|   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_FAILURE;
 | |
|   }
 | |
| 
 | |
|   if (NS_WARN_IF(!Preferences::GetBool(kPrefSimpleDBEnabled, false))) {
 | |
|     return NS_ERROR_UNEXPECTED;
 | |
|   }
 | |
| 
 | |
|   const PrincipalInfo& principalInfo = GetConnection()->GetPrincipalInfo();
 | |
| 
 | |
|   if (principalInfo.type() == PrincipalInfo::TSystemPrincipalInfo) {
 | |
|     QuotaManager::GetInfoForChrome(&mSuffix, &mGroup, &mOrigin);
 | |
|   } else {
 | |
|     MOZ_ASSERT(principalInfo.type() == PrincipalInfo::TContentPrincipalInfo);
 | |
| 
 | |
|     auto principalOrErr = PrincipalInfoToPrincipal(principalInfo);
 | |
|     if (NS_WARN_IF(principalOrErr.isErr())) {
 | |
|       return principalOrErr.unwrapErr();
 | |
|     }
 | |
| 
 | |
|     nsCOMPtr<nsIPrincipal> principal = principalOrErr.unwrap();
 | |
| 
 | |
|     nsresult rv = QuotaManager::GetInfoFromPrincipal(principal, &mSuffix,
 | |
|                                                      &mGroup, &mOrigin);
 | |
|     if (NS_WARN_IF(NS_FAILED(rv))) {
 | |
|       return rv;
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   mState = State::FinishOpen;
 | |
|   MOZ_ALWAYS_SUCCEEDS(OwningEventTarget()->Dispatch(this, NS_DISPATCH_NORMAL));
 | |
| 
 | |
|   return NS_OK;
 | |
| }
 | |
| 
 | |
| nsresult OpenOp::FinishOpen() {
 | |
|   AssertIsOnOwningThread();
 | |
|   MOZ_ASSERT(mState == State::FinishOpen);
 | |
| 
 | |
|   if (gOpenConnections) {
 | |
|     for (Connection* connection : *gOpenConnections) {
 | |
|       if (connection->Origin() == mOrigin &&
 | |
|           connection->Name() == mParams.name()) {
 | |
|         return NS_ERROR_STORAGE_BUSY;
 | |
|       }
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   if (QuotaManager::Get()) {
 | |
|     nsresult rv = OpenDirectory();
 | |
|     if (NS_WARN_IF(NS_FAILED(rv))) {
 | |
|       return rv;
 | |
|     }
 | |
| 
 | |
|     return NS_OK;
 | |
|   }
 | |
| 
 | |
|   mState = State::QuotaManagerPending;
 | |
|   QuotaManager::GetOrCreate(this);
 | |
| 
 | |
|   return NS_OK;
 | |
| }
 | |
| 
 | |
| nsresult OpenOp::QuotaManagerOpen() {
 | |
|   AssertIsOnOwningThread();
 | |
|   MOZ_ASSERT(mState == State::QuotaManagerPending);
 | |
| 
 | |
|   if (NS_WARN_IF(!QuotaManager::Get())) {
 | |
|     return NS_ERROR_FAILURE;
 | |
|   }
 | |
| 
 | |
|   nsresult rv = OpenDirectory();
 | |
|   if (NS_WARN_IF(NS_FAILED(rv))) {
 | |
|     return rv;
 | |
|   }
 | |
| 
 | |
|   return NS_OK;
 | |
| }
 | |
| 
 | |
| nsresult OpenOp::OpenDirectory() {
 | |
|   AssertIsOnOwningThread();
 | |
|   MOZ_ASSERT(mState == State::FinishOpen ||
 | |
|              mState == State::QuotaManagerPending);
 | |
|   MOZ_ASSERT(!mOrigin.IsEmpty());
 | |
|   MOZ_ASSERT(!mDirectoryLock);
 | |
|   MOZ_ASSERT(!QuotaClient::IsShuttingDownOnBackgroundThread());
 | |
|   MOZ_ASSERT(QuotaManager::Get());
 | |
| 
 | |
|   mState = State::DirectoryOpenPending;
 | |
|   RefPtr<DirectoryLock> pendingDirectoryLock =
 | |
|       QuotaManager::Get()->OpenDirectory(GetConnection()->GetPersistenceType(),
 | |
|                                          mGroup, mOrigin,
 | |
|                                          mozilla::dom::quota::Client::SDB,
 | |
|                                          /* aExclusive */ false, this);
 | |
| 
 | |
|   return NS_OK;
 | |
| }
 | |
| 
 | |
| nsresult OpenOp::SendToIOThread() {
 | |
|   AssertIsOnOwningThread();
 | |
|   MOZ_ASSERT(mState == State::DirectoryOpenPending);
 | |
| 
 | |
|   if (NS_WARN_IF(QuotaClient::IsShuttingDownOnBackgroundThread()) ||
 | |
|       IsActorDestroyed()) {
 | |
|     return NS_ERROR_FAILURE;
 | |
|   }
 | |
| 
 | |
|   mFileStream = new FileStream(GetConnection()->GetPersistenceType(), mGroup,
 | |
|                                mOrigin, 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(mFileStream);
 | |
|   MOZ_ASSERT(!mFileStreamOpen);
 | |
| 
 | |
|   if (NS_WARN_IF(QuotaClient::IsShuttingDownOnNonBackgroundThread()) ||
 | |
|       !OperationMayProceed()) {
 | |
|     return NS_ERROR_FAILURE;
 | |
|   }
 | |
| 
 | |
|   QuotaManager* quotaManager = QuotaManager::Get();
 | |
|   MOZ_ASSERT(quotaManager);
 | |
| 
 | |
|   nsCOMPtr<nsIFile> dbDirectory;
 | |
|   nsresult rv = quotaManager->EnsureStorageAndOriginIsInitialized(
 | |
|       GetConnection()->GetPersistenceType(), mSuffix, mGroup, mOrigin,
 | |
|       mozilla::dom::quota::Client::SDB, getter_AddRefs(dbDirectory));
 | |
|   if (NS_WARN_IF(NS_FAILED(rv))) {
 | |
|     return rv;
 | |
|   }
 | |
| 
 | |
|   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 = mFileStream->Init(dbFile, PR_RDWR | PR_CREATE_FILE, 0644, 0);
 | |
|   if (NS_WARN_IF(NS_FAILED(rv))) {
 | |
|     return rv;
 | |
|   }
 | |
| 
 | |
|   mFileStreamOpen = true;
 | |
| 
 | |
|   rv = DoDatabaseWork(mFileStream);
 | |
|   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(mFileStream);
 | |
|   MOZ_ASSERT(mFileStreamOpen);
 | |
| 
 | |
|   mDirectoryLock = nullptr;
 | |
|   mFileStream = nullptr;
 | |
|   mFileStreamOpen = false;
 | |
| }
 | |
| 
 | |
| nsresult OpenOp::DoDatabaseWork(nsIFileStream* aFileStream) {
 | |
|   AssertIsOnIOThread();
 | |
| 
 | |
|   return NS_OK;
 | |
| }
 | |
| 
 | |
| void OpenOp::GetResponse(SDBRequestResponse& aResponse) {
 | |
|   AssertIsOnOwningThread();
 | |
| 
 | |
|   aResponse = SDBRequestOpenResponse();
 | |
| }
 | |
| 
 | |
| void OpenOp::OnSuccess() {
 | |
|   AssertIsOnOwningThread();
 | |
|   MOZ_ASSERT(NS_SUCCEEDED(ResultCode()));
 | |
|   MOZ_ASSERT(!mOrigin.IsEmpty());
 | |
|   MOZ_ASSERT(mDirectoryLock);
 | |
|   MOZ_ASSERT(mFileStream);
 | |
|   MOZ_ASSERT(mFileStreamOpen);
 | |
| 
 | |
|   RefPtr<DirectoryLock> directoryLock;
 | |
|   nsCOMPtr<nsIFileStream> fileStream;
 | |
| 
 | |
|   mDirectoryLock.swap(directoryLock);
 | |
|   mFileStream.swap(fileStream);
 | |
|   mFileStreamOpen = false;
 | |
| 
 | |
|   GetConnection()->OnOpen(mOrigin, mParams.name(), directoryLock.forget(),
 | |
|                           fileStream.forget());
 | |
| }
 | |
| 
 | |
| void OpenOp::Cleanup() {
 | |
|   AssertIsOnOwningThread();
 | |
|   MOZ_ASSERT_IF(mFileStreamOpen, mFileStream);
 | |
| 
 | |
|   if (mFileStream && mFileStreamOpen) {
 | |
|     // 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(mFileStream, callback);
 | |
|     helper->AsyncClose();
 | |
|   } else {
 | |
|     MOZ_ASSERT(!mFileStreamOpen);
 | |
| 
 | |
|     mDirectoryLock = nullptr;
 | |
|     mFileStream = 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::QuotaManagerPending:
 | |
|       rv = QuotaManagerOpen();
 | |
|       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(nsIFileStream* aFileStream) {
 | |
|   AssertIsOnIOThread();
 | |
|   MOZ_ASSERT(aFileStream);
 | |
| 
 | |
|   nsCOMPtr<nsISeekableStream> seekableStream = do_QueryInterface(aFileStream);
 | |
|   MOZ_ASSERT(seekableStream);
 | |
| 
 | |
|   nsresult rv =
 | |
|       seekableStream->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;
 | |
|   }
 | |
| 
 | |
|   mOutputStream = MemoryOutputStream::Create(mParams.size());
 | |
|   if (NS_WARN_IF(!mOutputStream)) {
 | |
|     return false;
 | |
|   }
 | |
| 
 | |
|   return true;
 | |
| }
 | |
| 
 | |
| nsresult ReadOp::DoDatabaseWork(nsIFileStream* aFileStream) {
 | |
|   AssertIsOnIOThread();
 | |
|   MOZ_ASSERT(aFileStream);
 | |
| 
 | |
|   nsCOMPtr<nsIInputStream> inputStream = do_QueryInterface(aFileStream);
 | |
|   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(mOutputStream->Data());
 | |
| }
 | |
| 
 | |
| 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(nsIFileStream* aFileStream) {
 | |
|   AssertIsOnIOThread();
 | |
|   MOZ_ASSERT(aFileStream);
 | |
| 
 | |
|   nsCOMPtr<nsIOutputStream> outputStream = do_QueryInterface(aFileStream);
 | |
|   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(nsIFileStream* aFileStream) {
 | |
|   AssertIsOnIOThread();
 | |
|   MOZ_ASSERT(aFileStream);
 | |
| 
 | |
|   nsCOMPtr<nsIInputStream> inputStream = do_QueryInterface(aFileStream);
 | |
|   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() : mShutdownRequested(false) {
 | |
|   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 nsACString& aGroup,
 | |
|     const nsACString& aOrigin, const AtomicBool& aCanceled) {
 | |
|   AssertIsOnIOThread();
 | |
| 
 | |
|   return GetUsageForOrigin(aPersistenceType, aGroup, aOrigin, aCanceled);
 | |
| }
 | |
| 
 | |
| nsresult QuotaClient::InitOriginWithoutTracking(
 | |
|     PersistenceType aPersistenceType, const nsACString& aGroup,
 | |
|     const nsACString& aOrigin, const AtomicBool& aCanceled) {
 | |
|   AssertIsOnIOThread();
 | |
| 
 | |
|   return NS_OK;
 | |
| }
 | |
| 
 | |
| Result<UsageInfo, nsresult> QuotaClient::GetUsageForOrigin(
 | |
|     PersistenceType aPersistenceType, const nsACString& aGroup,
 | |
|     const nsACString& aOrigin, const AtomicBool& aCanceled) {
 | |
|   AssertIsOnIOThread();
 | |
| 
 | |
|   QuotaManager* quotaManager = QuotaManager::Get();
 | |
|   MOZ_ASSERT(quotaManager);
 | |
| 
 | |
|   nsCOMPtr<nsIFile> directory;
 | |
|   nsresult rv = quotaManager->GetDirectoryForOrigin(aPersistenceType, aOrigin,
 | |
|                                                     getter_AddRefs(directory));
 | |
|   if (NS_WARN_IF(NS_FAILED(rv))) {
 | |
|     return Err(rv);
 | |
|   }
 | |
| 
 | |
|   MOZ_ASSERT(directory);
 | |
| 
 | |
|   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);
 | |
| 
 | |
|   nsCOMPtr<nsIDirectoryEnumerator> directoryEntries;
 | |
|   rv = directory->GetDirectoryEntries(getter_AddRefs(directoryEntries));
 | |
|   if (NS_WARN_IF(NS_FAILED(rv))) {
 | |
|     return Err(rv);
 | |
|   }
 | |
| 
 | |
|   UsageInfo res;
 | |
| 
 | |
|   while (!aCanceled) {
 | |
|     nsCOMPtr<nsIFile> file;
 | |
|     rv = directoryEntries->GetNextFile(getter_AddRefs(file));
 | |
|     if (NS_WARN_IF(NS_FAILED(rv))) {
 | |
|       return Err(rv);
 | |
|     }
 | |
| 
 | |
|     if (!file) {
 | |
|       break;
 | |
|     }
 | |
| 
 | |
|     bool isDirectory;
 | |
|     rv = file->IsDirectory(&isDirectory);
 | |
|     if (NS_WARN_IF(NS_FAILED(rv))) {
 | |
|       return Err(rv);
 | |
|     }
 | |
| 
 | |
|     if (isDirectory) {
 | |
|       Unused << WARN_IF_FILE_IS_UNKNOWN(*file);
 | |
|       continue;
 | |
|     }
 | |
| 
 | |
|     nsString leafName;
 | |
|     rv = file->GetLeafName(leafName);
 | |
|     if (NS_WARN_IF(NS_FAILED(rv))) {
 | |
|       return Err(rv);
 | |
|     }
 | |
| 
 | |
|     if (StringEndsWith(leafName, kSDBSuffix)) {
 | |
|       int64_t fileSize;
 | |
|       rv = file->GetFileSize(&fileSize);
 | |
|       if (NS_WARN_IF(NS_FAILED(rv))) {
 | |
|         return Err(rv);
 | |
|       }
 | |
| 
 | |
|       MOZ_ASSERT(fileSize >= 0);
 | |
| 
 | |
|       res += DatabaseUsageType(Some(uint64_t(fileSize)));
 | |
| 
 | |
|       continue;
 | |
|     }
 | |
| 
 | |
|     Unused << WARN_IF_FILE_IS_UNKNOWN(*file);
 | |
|   }
 | |
| 
 | |
|   return res;
 | |
| }
 | |
| 
 | |
| void QuotaClient::OnOriginClearCompleted(PersistenceType aPersistenceType,
 | |
|                                          const nsACString& aOrigin) {
 | |
|   AssertIsOnIOThread();
 | |
| }
 | |
| 
 | |
| void QuotaClient::ReleaseIOThreadObjects() { AssertIsOnIOThread(); }
 | |
| 
 | |
| void QuotaClient::AbortOperations(const nsACString& aOrigin) {
 | |
|   AssertIsOnBackgroundThread();
 | |
| 
 | |
|   if (gOpenConnections) {
 | |
|     for (Connection* connection : *gOpenConnections) {
 | |
|       if (aOrigin.IsVoid() || connection->Origin() == aOrigin) {
 | |
|         connection->AllowToClose();
 | |
|       }
 | |
|     }
 | |
|   }
 | |
| }
 | |
| 
 | |
| void QuotaClient::AbortOperationsForProcess(ContentParentId aContentParentId) {
 | |
|   AssertIsOnBackgroundThread();
 | |
| }
 | |
| 
 | |
| void QuotaClient::StartIdleMaintenance() { AssertIsOnBackgroundThread(); }
 | |
| 
 | |
| void QuotaClient::StopIdleMaintenance() { AssertIsOnBackgroundThread(); }
 | |
| 
 | |
| void QuotaClient::ShutdownWorkThreads() {
 | |
|   AssertIsOnBackgroundThread();
 | |
|   MOZ_ASSERT(!mShutdownRequested);
 | |
| 
 | |
|   mShutdownRequested = true;
 | |
| 
 | |
|   if (gOpenConnections) {
 | |
|     for (Connection* connection : *gOpenConnections) {
 | |
|       connection->AllowToClose();
 | |
|     }
 | |
| 
 | |
|     MOZ_ALWAYS_TRUE(SpinEventLoopUntil([&]() { return !gOpenConnections; }));
 | |
|   }
 | |
| }
 | |
| 
 | |
| }  // namespace dom
 | |
| }  // namespace mozilla
 | 
