diff --git a/browser/base/content/pageinfo/permissions.js b/browser/base/content/pageinfo/permissions.js index c26c95ae427e..70e953484112 100644 --- a/browser/base/content/pageinfo/permissions.js +++ b/browser/base/content/pageinfo/permissions.js @@ -5,7 +5,7 @@ Components.utils.import("resource:///modules/SitePermissions.jsm"); Components.utils.import("resource://gre/modules/BrowserUtils.jsm"); -const nsIQuotaManager = Components.interfaces.nsIQuotaManager; +const nsIQuotaManagerService = Components.interfaces.nsIQuotaManagerService; var gPermURI; var gUsageRequest; @@ -186,13 +186,15 @@ function initIndexedDBRow() row.appendChild(extras); - var quotaManager = Components.classes["@mozilla.org/dom/quota/manager;1"] - .getService(nsIQuotaManager); + var quotaManagerService = + Components.classes["@mozilla.org/dom/quota-manager-service;1"] + .getService(nsIQuotaManagerService); let principal = Components.classes["@mozilla.org/scriptsecuritymanager;1"] .getService(Components.interfaces.nsIScriptSecurityManager) .createCodebasePrincipal(gPermURI, {}); gUsageRequest = - quotaManager.getUsageForPrincipal(principal, onIndexedDBUsageCallback); + quotaManagerService.getUsageForPrincipal(principal, + onIndexedDBUsageCallback); var status = document.getElementById("indexedDBStatus"); var button = document.getElementById("indexedDBClear"); @@ -208,8 +210,8 @@ function onIndexedDBClear() .getService(Components.interfaces.nsIScriptSecurityManager) .createCodebasePrincipal(gPermURI, {}); - Components.classes["@mozilla.org/dom/quota/manager;1"] - .getService(nsIQuotaManager) + Components.classes["@mozilla.org/dom/quota-manager-service;1"] + .getService(nsIQuotaManagerService) .clearStoragesForPrincipal(principal); Components.classes["@mozilla.org/serviceworkers/manager;1"] @@ -220,14 +222,14 @@ function onIndexedDBClear() initIndexedDBRow(); } -function onIndexedDBUsageCallback(principal, usage, fileUsage) +function onIndexedDBUsageCallback(request) { - let uri = principal.URI; + let uri = request.principal.URI; if (!uri.equals(gPermURI)) { throw new Error("Callback received for bad URI: " + uri); } - if (usage) { + if (request.usage) { if (!("DownloadUtils" in window)) { Components.utils.import("resource://gre/modules/DownloadUtils.jsm"); } @@ -237,7 +239,7 @@ function onIndexedDBUsageCallback(principal, usage, fileUsage) status.value = gBundle.getFormattedString("indexedDBUsage", - DownloadUtils.convertByteUnits(usage)); + DownloadUtils.convertByteUnits(request.usage)); status.removeAttribute("hidden"); button.removeAttribute("hidden"); } diff --git a/dom/ipc/ContentParent.cpp b/dom/ipc/ContentParent.cpp index 005c0a634d2d..e8ad85b60c9d 100755 --- a/dom/ipc/ContentParent.cpp +++ b/dom/ipc/ContentParent.cpp @@ -69,7 +69,7 @@ #include "mozilla/dom/power/PowerManagerService.h" #include "mozilla/dom/PresentationParent.h" #include "mozilla/dom/PPresentationParent.h" -#include "mozilla/dom/quota/QuotaManager.h" +#include "mozilla/dom/quota/QuotaManagerService.h" #include "mozilla/dom/telephony/TelephonyParent.h" #include "mozilla/dom/time/DateCacheCleaner.h" #include "mozilla/dom/voicemail/VoicemailParent.h" @@ -1795,10 +1795,10 @@ ContentParent::ShutDownProcess(ShutDownMethod aMethod) return; } - using mozilla::dom::quota::QuotaManager; + using mozilla::dom::quota::QuotaManagerService; - if (QuotaManager* quotaManager = QuotaManager::Get()) { - quotaManager->AbortOperationsForProcess(mChildID); + if (QuotaManagerService* quotaManagerService = QuotaManagerService::Get()) { + quotaManagerService->AbortOperationsForProcess(mChildID); } // If Close() fails with an error, we'll end up back in this function, but diff --git a/dom/quota/ActorsChild.cpp b/dom/quota/ActorsChild.cpp new file mode 100644 index 000000000000..720bb5eae1c5 --- /dev/null +++ b/dom/quota/ActorsChild.cpp @@ -0,0 +1,268 @@ +/* -*- 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" + +#include "QuotaManagerService.h" +#include "QuotaRequests.h" + +namespace mozilla { +namespace dom { +namespace quota { + +/******************************************************************************* + * QuotaChild + ******************************************************************************/ + +QuotaChild::QuotaChild(QuotaManagerService* aService) + : mService(aService) +#ifdef DEBUG + , mOwningThread(NS_GetCurrentThread()) +#endif +{ + AssertIsOnOwningThread(); + MOZ_ASSERT(aService); + + MOZ_COUNT_CTOR(quota::QuotaChild); +} + +QuotaChild::~QuotaChild() +{ + AssertIsOnOwningThread(); + + MOZ_COUNT_DTOR(quota::QuotaChild); +} + +#ifdef DEBUG + +void +QuotaChild::AssertIsOnOwningThread() const +{ + MOZ_ASSERT(mOwningThread); + + bool current; + MOZ_ASSERT(NS_SUCCEEDED(mOwningThread->IsOnCurrentThread(¤t))); + MOZ_ASSERT(current); +} + +#endif // DEBUG + +void +QuotaChild::ActorDestroy(ActorDestroyReason aWhy) +{ + AssertIsOnOwningThread(); + + if (mService) { + mService->ClearBackgroundActor(); +#ifdef DEBUG + mService = nullptr; +#endif + } +} + +PQuotaUsageRequestChild* +QuotaChild::AllocPQuotaUsageRequestChild(const UsageRequestParams& aParams) +{ + AssertIsOnOwningThread(); + + MOZ_CRASH("PQuotaUsageRequestChild actors should be manually constructed!"); +} + +bool +QuotaChild::DeallocPQuotaUsageRequestChild(PQuotaUsageRequestChild* aActor) +{ + AssertIsOnOwningThread(); + MOZ_ASSERT(aActor); + + delete static_cast(aActor); + return true; +} + +PQuotaRequestChild* +QuotaChild::AllocPQuotaRequestChild(const RequestParams& aParams) +{ + AssertIsOnOwningThread(); + + MOZ_CRASH("PQuotaRequestChild actors should be manually constructed!"); +} + +bool +QuotaChild::DeallocPQuotaRequestChild(PQuotaRequestChild* aActor) +{ + AssertIsOnOwningThread(); + MOZ_ASSERT(aActor); + + delete static_cast(aActor); + return true; +} + +/******************************************************************************* + * QuotaUsageRequestChild + ******************************************************************************/ + +QuotaUsageRequestChild::QuotaUsageRequestChild(UsageRequest* aRequest) + : mRequest(aRequest) +{ + AssertIsOnOwningThread(); + + MOZ_COUNT_CTOR(quota::QuotaUsageRequestChild); +} + +QuotaUsageRequestChild::~QuotaUsageRequestChild() +{ + // Can't assert owning thread here because the request is cleared. + + MOZ_COUNT_DTOR(quota::QuotaUsageRequestChild); +} + +#ifdef DEBUG + +void +QuotaUsageRequestChild::AssertIsOnOwningThread() const +{ + MOZ_ASSERT(mRequest); + mRequest->AssertIsOnOwningThread(); +} + +#endif // DEBUG + +void +QuotaUsageRequestChild::HandleResponse(nsresult aResponse) +{ + AssertIsOnOwningThread(); + MOZ_ASSERT(NS_FAILED(aResponse)); + MOZ_ASSERT(mRequest); + + mRequest->SetError(aResponse); +} + +void +QuotaUsageRequestChild::HandleResponse(const UsageResponse& aResponse) +{ + AssertIsOnOwningThread(); + MOZ_ASSERT(mRequest); + + mRequest->SetResult(aResponse.usage(), aResponse.fileUsage()); +} + +void +QuotaUsageRequestChild::ActorDestroy(ActorDestroyReason aWhy) +{ + AssertIsOnOwningThread(); + + if (mRequest) { + mRequest->ClearBackgroundActor(); +#ifdef DEBUG + mRequest = nullptr; +#endif + } +} + +bool +QuotaUsageRequestChild::Recv__delete__(const UsageRequestResponse& aResponse) +{ + AssertIsOnOwningThread(); + MOZ_ASSERT(mRequest); + + switch (aResponse.type()) { + case UsageRequestResponse::Tnsresult: + HandleResponse(aResponse.get_nsresult()); + break; + + case UsageRequestResponse::TUsageResponse: + HandleResponse(aResponse.get_UsageResponse()); + break; + + default: + MOZ_CRASH("Unknown response type!"); + } + + return true; +} + +/******************************************************************************* + * QuotaRequestChild + ******************************************************************************/ + +QuotaRequestChild::QuotaRequestChild(Request* aRequest) + : mRequest(aRequest) +{ + AssertIsOnOwningThread(); + + MOZ_COUNT_CTOR(quota::QuotaRequestChild); +} + +QuotaRequestChild::~QuotaRequestChild() +{ + AssertIsOnOwningThread(); + + MOZ_COUNT_DTOR(quota::QuotaRequestChild); +} + +#ifdef DEBUG + +void +QuotaRequestChild::AssertIsOnOwningThread() const +{ + MOZ_ASSERT(mRequest); + mRequest->AssertIsOnOwningThread(); +} + +#endif // DEBUG + +void +QuotaRequestChild::HandleResponse(nsresult aResponse) +{ + AssertIsOnOwningThread(); + MOZ_ASSERT(NS_FAILED(aResponse)); + MOZ_ASSERT(mRequest); + + mRequest->SetError(aResponse); +} + +void +QuotaRequestChild::HandleResponse() +{ + AssertIsOnOwningThread(); + MOZ_ASSERT(mRequest); + + mRequest->SetResult(); +} + +void +QuotaRequestChild::ActorDestroy(ActorDestroyReason aWhy) +{ + AssertIsOnOwningThread(); +} + +bool +QuotaRequestChild::Recv__delete__(const RequestResponse& aResponse) +{ + AssertIsOnOwningThread(); + MOZ_ASSERT(mRequest); + + switch (aResponse.type()) { + case RequestResponse::Tnsresult: + HandleResponse(aResponse.get_nsresult()); + break; + + case RequestResponse::TClearOriginResponse: + case RequestResponse::TClearAppResponse: + case RequestResponse::TClearAllResponse: + case RequestResponse::TResetAllResponse: + HandleResponse(); + break; + + default: + MOZ_CRASH("Unknown response type!"); + } + + return true; +} + +} // namespace quota +} // namespace dom +} // namespace mozilla diff --git a/dom/quota/ActorsChild.h b/dom/quota/ActorsChild.h new file mode 100644 index 000000000000..ee85a3840eb1 --- /dev/null +++ b/dom/quota/ActorsChild.h @@ -0,0 +1,153 @@ +/* -*- 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_quota_ActorsChild_h +#define mozilla_dom_quota_ActorsChild_h + +#include "mozilla/dom/quota/PQuotaChild.h" +#include "mozilla/dom/quota/PQuotaRequestChild.h" +#include "mozilla/dom/quota/PQuotaUsageRequestChild.h" + +namespace mozilla { +namespace ipc { + +class BackgroundChildImpl; + +} // namespace ipc + +namespace dom { +namespace quota { + +class QuotaManagerService; +class Request; +class UsageRequest; + +class QuotaChild final + : public PQuotaChild +{ + friend class mozilla::ipc::BackgroundChildImpl; + friend class QuotaManagerService; + + QuotaManagerService* mService; + +#ifdef DEBUG + nsCOMPtr mOwningThread; +#endif + +public: + void + AssertIsOnOwningThread() const +#ifdef DEBUG + ; +#else + { } +#endif + +private: + // Only created by QuotaManagerService. + explicit QuotaChild(QuotaManagerService* aService); + + // Only destroyed by mozilla::ipc::BackgroundChildImpl. + ~QuotaChild(); + + // IPDL methods are only called by IPDL. + virtual void + ActorDestroy(ActorDestroyReason aWhy) override; + + virtual PQuotaUsageRequestChild* + AllocPQuotaUsageRequestChild(const UsageRequestParams& aParams) override; + + virtual bool + DeallocPQuotaUsageRequestChild(PQuotaUsageRequestChild* aActor) override; + + virtual PQuotaRequestChild* + AllocPQuotaRequestChild(const RequestParams& aParams) override; + + virtual bool + DeallocPQuotaRequestChild(PQuotaRequestChild* aActor) override; +}; + +class QuotaUsageRequestChild final + : public PQuotaUsageRequestChild +{ + friend class QuotaChild; + friend class QuotaManagerService; + + RefPtr mRequest; + +public: + void + AssertIsOnOwningThread() const +#ifdef DEBUG + ; +#else + { } +#endif + +private: + // Only created by QuotaManagerService. + explicit QuotaUsageRequestChild(UsageRequest* aRequest); + + // Only destroyed by QuotaChild. + ~QuotaUsageRequestChild(); + + void + HandleResponse(nsresult aResponse); + + void + HandleResponse(const UsageResponse& aResponse); + + // IPDL methods are only called by IPDL. + virtual void + ActorDestroy(ActorDestroyReason aWhy) override; + + virtual bool + Recv__delete__(const UsageRequestResponse& aResponse) override; +}; + +class QuotaRequestChild final + : public PQuotaRequestChild +{ + friend class QuotaChild; + friend class QuotaManagerService; + + RefPtr mRequest; + +public: + void + AssertIsOnOwningThread() const +#ifdef DEBUG + ; +#else + { } +#endif + +private: + // Only created by QuotaManagerService. + explicit QuotaRequestChild(Request* aRequest); + + // Only destroyed by QuotaChild. + ~QuotaRequestChild(); + + void + HandleResponse(nsresult aResponse); + + void + HandleResponse(); + + // IPDL methods are only called by IPDL. + virtual void + ActorDestroy(ActorDestroyReason aWhy) override; + + virtual bool + Recv__delete__(const RequestResponse& aResponse) override; +}; + +} // namespace quota +} // namespace dom +} // namespace mozilla + +#endif // mozilla_dom_quota_ActorsChild_h diff --git a/dom/quota/QuotaManager.cpp b/dom/quota/ActorsParent.cpp similarity index 81% rename from dom/quota/QuotaManager.cpp rename to dom/quota/ActorsParent.cpp index e2f5b742528c..a2235dd1231a 100644 --- a/dom/quota/QuotaManager.cpp +++ b/dom/quota/ActorsParent.cpp @@ -4,24 +4,21 @@ * 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 "QuotaManager.h" +#include "ActorsParent.h" -#include "mozIApplicationClearPrivateDataParams.h" #include "nsIBinaryInputStream.h" #include "nsIBinaryOutputStream.h" #include "nsIFile.h" -#include "nsIIdleService.h" +#include "nsIFileStreams.h" #include "nsIObserverService.h" #include "nsIPermissionManager.h" #include "nsIPrincipal.h" -#include "nsIQuotaRequest.h" #include "nsIRunnable.h" #include "nsISimpleEnumerator.h" #include "nsIScriptObjectPrincipal.h" #include "nsIScriptSecurityManager.h" #include "nsITimer.h" #include "nsIURI.h" -#include "nsIUsageCallback.h" #include "nsPIDOMWindow.h" #include @@ -33,12 +30,19 @@ #include "mozilla/dom/asmjscache/AsmJSCache.h" #include "mozilla/dom/cache/QuotaClient.h" #include "mozilla/dom/indexedDB/ActorsParent.h" +#include "mozilla/dom/quota/PQuotaParent.h" +#include "mozilla/dom/quota/PQuotaRequestParent.h" +#include "mozilla/dom/quota/PQuotaUsageRequestParent.h" +#include "mozilla/ipc/BackgroundParent.h" +#include "mozilla/ipc/BackgroundUtils.h" #include "mozilla/IntegerRange.h" #include "mozilla/Mutex.h" #include "mozilla/LazyIdleThread.h" #include "mozilla/Preferences.h" #include "mozilla/Services.h" +#include "mozilla/StaticPtr.h" #include "mozilla/TypeTraits.h" +#include "mozilla/unused.h" #include "nsAppDirectoryServiceDefs.h" #include "nsComponentManagerUtils.h" #include "nsAboutProtocolUtils.h" @@ -52,12 +56,23 @@ #include "nsScriptSecurityManager.h" #include "nsThreadUtils.h" #include "nsXULAppAPI.h" +#include "prio.h" #include "xpcpublic.h" #include "OriginScope.h" +#include "QuotaManager.h" +#include "QuotaManagerService.h" #include "QuotaObject.h" #include "UsageInfo.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 + // The amount of time, in milliseconds, that our IO thread will stay alive // after the last event it processes. #define DEFAULT_THREAD_TIMEOUT_MS 30000 @@ -91,6 +106,16 @@ namespace mozilla { namespace dom { namespace quota { +using namespace mozilla::ipc; + +const bool QuotaManager::kRunningXPCShellTests = +#ifdef ENABLE_TESTS + !!PR_GetEnv("XPCSHELL_TEST_PROFILE_DIR") +#else + false +#endif + ; + // We want profiles to be platform-independent so we always need to replace // the same characters on every platform. Windows has the most extensive set // of illegal characters so we use its FILE_ILLEGAL_CHARACTERS and @@ -178,6 +203,14 @@ public: MatchOriginScopes(const OriginScope& aOriginScope1, const OriginScope& aOriginScope2); + void + AssertIsOnOwningThread() const +#ifdef DEBUG + ; +#else + { } +#endif + const Nullable& GetPersistenceType() const { @@ -228,7 +261,7 @@ public: void AddBlockingLock(DirectoryLockImpl* aLock) { - MOZ_ASSERT(NS_IsMainThread()); + AssertIsOnOwningThread(); mBlocking.AppendElement(aLock); } @@ -242,7 +275,7 @@ public: void AddBlockedOnLock(DirectoryLockImpl* aLock) { - MOZ_ASSERT(NS_IsMainThread()); + AssertIsOnOwningThread(); mBlockedOn.AppendElement(aLock); } @@ -250,7 +283,7 @@ public: void MaybeUnblock(DirectoryLockImpl* aLock) { - MOZ_ASSERT(NS_IsMainThread()); + AssertIsOnOwningThread(); mBlockedOn.RemoveElement(aLock); if (mBlockedOn.IsEmpty()) { @@ -264,15 +297,116 @@ public: void Invalidate() { - MOZ_ASSERT(NS_IsMainThread()); + AssertIsOnOwningThread(); mInvalidated = true; } + NS_INLINE_DECL_REFCOUNTING(DirectoryLockImpl) + private: ~DirectoryLockImpl(); +}; + +class QuotaManager::CreateRunnable final + : public BackgroundThreadObject + , public nsRunnable +{ + nsTArray> mCallbacks; + nsString mBaseDirPath; + RefPtr mManager; + nsresult mResultCode; + + enum class State + { + Initial, + CreatingManager, + RegisteringObserver, + CallingCallbacks, + Completed + }; + + State mState; + +public: + CreateRunnable() + : mResultCode(NS_OK) + , mState(State::Initial) + { + AssertIsOnBackgroundThread(); + } + + void + AddCallback(nsIRunnable* aCallback) + { + AssertIsOnOwningThread(); + MOZ_ASSERT(aCallback); + + mCallbacks.AppendElement(aCallback); + } + +private: + ~CreateRunnable() + { } + + nsresult + Init(); + + nsresult + CreateManager(); + + nsresult + RegisterObserver(); + + void + CallCallbacks(); + + State + GetNextState(nsCOMPtr& aThread); + + NS_DECL_NSIRUNNABLE +}; + +class QuotaManager::ShutdownRunnable final + : public nsRunnable +{ + // Only touched on the main thread. + bool& mDone; + +public: + explicit ShutdownRunnable(bool& aDone) + : mDone(aDone) + { + MOZ_ASSERT(NS_IsMainThread()); + } + +private: + ~ShutdownRunnable() + { } + + NS_DECL_NSIRUNNABLE +}; + +class QuotaManager::ShutdownObserver final + : public nsIObserver +{ + nsCOMPtr mBackgroundThread; + +public: + explicit ShutdownObserver(nsIEventTarget* aBackgroundThread) + : mBackgroundThread(aBackgroundThread) + { + MOZ_ASSERT(NS_IsMainThread()); + } NS_DECL_ISUPPORTS + NS_DECL_NSIOBSERVER + +private: + ~ShutdownObserver() + { + MOZ_ASSERT(NS_IsMainThread()); + } }; namespace { @@ -506,38 +640,92 @@ private: }; class OriginOperationBase - : public nsRunnable + : public BackgroundThreadObject + , public nsRunnable { protected: + nsresult mResultCode; + enum State { // Not yet run. State_Initial, - // Running on the main thread in the listener for OpenDirectory. + // Running initialization on the main thread. + State_Initializing, + + // Running initialization on the owning thread. + State_FinishingInit, + + // Running quota manager initialization on the owning thread. + State_CreatingQuotaManager, + + // Running on the owning thread in the listener for OpenDirectory. State_DirectoryOpenPending, // Running on the IO thread. State_DirectoryWorkOpen, - // Running on the main thread after all work is done. + // Running on the owning thread after all work is done. State_UnblockingOpen, + // All done. State_Complete }; +private: State mState; - nsresult mResultCode; + bool mActorDestroyed; protected: - OriginOperationBase() - : mState(State_Initial) + bool mNeedsMainThreadInit; + bool mNeedsQuotaManagerInit; + +public: + void + NoteActorDestroyed() + { + AssertIsOnOwningThread(); + + mActorDestroyed = true; + } + + bool + IsActorDestroyed() const + { + AssertIsOnOwningThread(); + + return mActorDestroyed; + } + +protected: + explicit OriginOperationBase( + nsIEventTarget* aOwningThread = NS_GetCurrentThread()) + : BackgroundThreadObject(aOwningThread) , mResultCode(NS_OK) + , mState(State_Initial) + , mActorDestroyed(false) + , mNeedsMainThreadInit(false) + , mNeedsQuotaManagerInit(false) { } // Reference counted. virtual ~OriginOperationBase() { MOZ_ASSERT(mState == State_Complete); + MOZ_ASSERT(mActorDestroyed); + } + + State + GetState() const + { + return mState; + } + + void + SetState(State aState) + { + MOZ_ASSERT(mState == State_Initial); + mState = aState; } void @@ -545,6 +733,15 @@ protected: { switch (mState) { case State_Initial: + mState = State_Initializing; + return; + case State_Initializing: + mState = State_FinishingInit; + return; + case State_FinishingInit: + mState = State_CreatingQuotaManager; + return; + case State_CreatingQuotaManager: mState = State_DirectoryOpenPending; return; case State_DirectoryOpenPending: @@ -565,6 +762,12 @@ protected: Run(); virtual nsresult + DoInitOnMainThread() + { + return NS_OK; + } + + virtual void Open() = 0; nsresult @@ -580,10 +783,57 @@ protected: UnblockOpen() = 0; private: + nsresult + Init(); + + nsresult + InitOnMainThread(); + + nsresult + FinishInit(); + + nsresult + QuotaManagerOpen(); + nsresult DirectoryWork(); }; +class FinalizeOriginEvictionOp + : public OriginOperationBase +{ + nsTArray> mLocks; + +public: + FinalizeOriginEvictionOp(nsIEventTarget* aBackgroundThread, + nsTArray>& aLocks) + : OriginOperationBase(aBackgroundThread) + { + MOZ_ASSERT(!NS_IsMainThread()); + + mLocks.SwapElements(aLocks); + } + + void + Dispatch(); + + void + RunOnIOThreadImmediately(); + +private: + ~FinalizeOriginEvictionOp() + { } + + virtual void + Open() override; + + virtual nsresult + DoDirectoryWork(QuotaManager* aQuotaManager) override; + + virtual void + UnblockOpen() override; +}; + class NormalOriginOperationBase : public OriginOperationBase , public OpenDirectoryListener @@ -599,7 +849,7 @@ public: void RunImmediately() { - MOZ_ASSERT(mState == State_Initial); + MOZ_ASSERT(GetState() == State_Initial); MOZ_ALWAYS_TRUE(NS_SUCCEEDED(this->Run())); } @@ -612,7 +862,7 @@ protected: , mOriginScope(aOriginScope) , mExclusive(aExclusive) { - MOZ_ASSERT(NS_IsMainThread()); + AssertIsOnOwningThread(); } ~NormalOriginOperationBase() @@ -621,7 +871,7 @@ protected: private: NS_DECL_ISUPPORTS_INHERITED - virtual nsresult + virtual void Open() override; virtual void @@ -653,7 +903,7 @@ public: /* aExclusive */ false) , mTimestamp(aTimestamp) { - MOZ_ASSERT(NS_IsMainThread()); + AssertIsOnOwningThread(); } private: @@ -664,32 +914,83 @@ private: DoDirectoryWork(QuotaManager* aQuotaManager) override; virtual void - SendResults() override - { } + SendResults() override; }; -class GetUsageOp +/******************************************************************************* + * Actor class declarations + ******************************************************************************/ + +class Quota final + : public PQuotaParent +{ + DebugOnly mActorDestroyed; + +public: + Quota(); + + NS_INLINE_DECL_THREADSAFE_REFCOUNTING(mozilla::dom::quota::Quota) + +private: + ~Quota(); + + void + StartIdleMaintenance(); + + // IPDL methods. + virtual void + ActorDestroy(ActorDestroyReason aWhy) override; + + virtual PQuotaUsageRequestParent* + AllocPQuotaUsageRequestParent(const UsageRequestParams& aParams) override; + + virtual bool + RecvPQuotaUsageRequestConstructor(PQuotaUsageRequestParent* aActor, + const UsageRequestParams& aParams) override; + + virtual bool + DeallocPQuotaUsageRequestParent(PQuotaUsageRequestParent* aActor) override; + + virtual PQuotaRequestParent* + AllocPQuotaRequestParent(const RequestParams& aParams) override; + + virtual bool + RecvPQuotaRequestConstructor(PQuotaRequestParent* aActor, + const RequestParams& aParams) override; + + virtual bool + DeallocPQuotaRequestParent(PQuotaRequestParent* aActor) override; + + virtual bool + RecvStartIdleMaintenance() override; + + virtual bool + RecvStopIdleMaintenance() override; +}; + +class GetUsageOp final : public NormalOriginOperationBase - , public nsIQuotaRequest + , public PQuotaUsageRequestParent { UsageInfo mUsageInfo; - const nsCString mGroup; - nsCOMPtr mPrincipal; - nsCOMPtr mCallback; - const bool mIsApp; + const UsageParams mParams; + nsCString mGroup; + bool mIsApp; public: - GetUsageOp(const nsACString& aGroup, - const nsACString& aOrigin, - bool aIsApp, - nsIPrincipal* aPrincipal, - nsIUsageCallback* aCallback); + explicit GetUsageOp(const UsageRequestParams& aParams); + + bool + Init(Quota* aQuota); private: ~GetUsageOp() { } + virtual nsresult + DoInitOnMainThread() override; + nsresult AddToUsage(QuotaManager* aQuotaManager, PersistenceType aPersistenceType); @@ -700,23 +1001,55 @@ private: virtual void SendResults() override; - NS_DECL_ISUPPORTS_INHERITED - NS_DECL_NSIQUOTAREQUEST + // IPDL methods. + virtual void + ActorDestroy(ActorDestroyReason aWhy) override; + + virtual bool + RecvCancel() override; }; -class ResetOrClearOp +class QuotaRequestBase : public NormalOriginOperationBase + , public PQuotaRequestParent { - bool mClear; +public: + // May be overridden by subclasses if they need to perform work on the + // background thread before being run. + virtual bool + Init(Quota* aQuota); + +protected: + explicit QuotaRequestBase(bool aExclusive) + : NormalOriginOperationBase(Nullable(), + OriginScope::FromNull(), + aExclusive) + { } + + // Subclasses use this override to set the IPDL response value. + virtual void + GetResponse(RequestResponse& aResponse) = 0; + +private: + virtual void + SendResults() override; + + // IPDL methods. + virtual void + ActorDestroy(ActorDestroyReason aWhy) override; +}; + +class ResetOrClearOp final + : public QuotaRequestBase +{ + const bool mClear; public: explicit ResetOrClearOp(bool aClear) - : NormalOriginOperationBase(Nullable(), - OriginScope::FromNull(), - /* aExclusive */ true) + : QuotaRequestBase(/* aExclusive */ true) , mClear(aClear) { - MOZ_ASSERT(NS_IsMainThread()); + AssertIsOnOwningThread(); } private: @@ -730,27 +1063,28 @@ private: DoDirectoryWork(QuotaManager* aQuotaManager) override; virtual void - SendResults() override - { } + GetResponse(RequestResponse& aResponse) override; }; -class OriginClearOp - : public NormalOriginOperationBase +class OriginClearOp final + : public QuotaRequestBase { + const RequestParams mParams; + const bool mApp; + public: - OriginClearOp(Nullable aPersistenceType, - const OriginScope& aOriginScope) - : NormalOriginOperationBase(aPersistenceType, - aOriginScope, - /* aExclusive */ true) - { - MOZ_ASSERT(NS_IsMainThread()); - } + explicit OriginClearOp(const RequestParams& aParams); + + virtual bool + Init(Quota* aQuota) override; private: ~OriginClearOp() { } + virtual nsresult + DoInitOnMainThread() override; + void DeleteFiles(QuotaManager* aQuotaManager, PersistenceType aPersistenceType); @@ -759,42 +1093,7 @@ private: DoDirectoryWork(QuotaManager* aQuotaManager) override; virtual void - SendResults() override - { } -}; - -class FinalizeOriginEvictionOp - : public OriginOperationBase -{ - nsTArray> mLocks; - -public: - explicit FinalizeOriginEvictionOp( - nsTArray>& aLocks) - { - MOZ_ASSERT(!NS_IsMainThread()); - - mLocks.SwapElements(aLocks); - } - - void - Dispatch(); - - void - RunOnIOThreadImmediately(); - -private: - ~FinalizeOriginEvictionOp() - { } - - virtual nsresult - Open() override; - - virtual nsresult - DoDirectoryWork(QuotaManager* aQuotaManager) override; - - virtual void - UnblockOpen() override; + GetResponse(RequestResponse& aResponse) override; }; /******************************************************************************* @@ -850,6 +1149,38 @@ PatternMatchesOrigin(const nsACString& aPatternString, } // namespace +BackgroundThreadObject::BackgroundThreadObject() + : mOwningThread(NS_GetCurrentThread()) +{ + AssertIsOnOwningThread(); +} + +BackgroundThreadObject::BackgroundThreadObject(nsIEventTarget* aOwningThread) + : mOwningThread(aOwningThread) +{ +} + +#ifdef DEBUG + +void +BackgroundThreadObject::AssertIsOnOwningThread() const +{ + AssertIsOnBackgroundThread(); + MOZ_ASSERT(mOwningThread); + bool current; + MOZ_ASSERT(NS_SUCCEEDED(mOwningThread->IsOnCurrentThread(¤t))); + MOZ_ASSERT(current); +} + +#endif // DEBUG + +nsIEventTarget* +BackgroundThreadObject::OwningThread() const +{ + MOZ_ASSERT(mOwningThread); + return mOwningThread; +} + bool IsOnIOThread() { @@ -896,7 +1227,9 @@ ReportInternalError(const char* aFile, uint32_t aLine, const char* aStr) namespace { -QuotaManager* gInstance = nullptr; +StaticRefPtr gInstance; +bool gCreateFailed = false; +StaticRefPtr gCreateRunnable; mozilla::Atomic gShutdown(false); // Constants for temporary storage limit computing. @@ -1609,6 +1942,34 @@ GetTemporaryStorageLimit(nsIFile* aDirectory, uint64_t aCurrentUsage, } // namespace +/******************************************************************************* + * Exported functions + ******************************************************************************/ + +PQuotaParent* +AllocPQuotaParent() +{ + AssertIsOnBackgroundThread(); + + if (NS_WARN_IF(QuotaManager::IsShuttingDown())) { + return nullptr; + } + + RefPtr actor = new Quota(); + + return actor.forget().take(); +} + +bool +DeallocPQuotaParent(PQuotaParent* aActor) +{ + AssertIsOnBackgroundThread(); + MOZ_ASSERT(aActor); + + RefPtr actor = dont_AddRef(static_cast(aActor)); + return true; +} + /******************************************************************************* * Directory lock ******************************************************************************/ @@ -1633,7 +1994,7 @@ DirectoryLockImpl::DirectoryLockImpl(QuotaManager* aQuotaManager, , mInternal(aInternal) , mInvalidated(false) { - MOZ_ASSERT(NS_IsMainThread()); + AssertIsOnOwningThread(); MOZ_ASSERT(aQuotaManager); MOZ_ASSERT_IF(!aInternal, !aPersistenceType.IsNull()); MOZ_ASSERT_IF(!aInternal, @@ -1650,7 +2011,7 @@ DirectoryLockImpl::DirectoryLockImpl(QuotaManager* aQuotaManager, DirectoryLockImpl::~DirectoryLockImpl() { - MOZ_ASSERT(NS_IsMainThread()); + AssertIsOnOwningThread(); MOZ_ASSERT(mQuotaManager); for (DirectoryLockImpl* blockingLock : mBlocking) { @@ -1667,7 +2028,7 @@ bool DirectoryLockImpl::MatchOriginScopes(const OriginScope& aOriginScope1, const OriginScope& aOriginScope2) { - MOZ_ASSERT(NS_IsMainThread()); + AssertIsOnBackgroundThread(); bool match; @@ -1689,10 +2050,21 @@ DirectoryLockImpl::MatchOriginScopes(const OriginScope& aOriginScope1, return match; } +#ifdef DEBUG + +void +DirectoryLockImpl::AssertIsOnOwningThread() const +{ + MOZ_ASSERT(mQuotaManager); + mQuotaManager->AssertIsOnOwningThread(); +} + +#endif // DEBUG + bool DirectoryLockImpl::MustWaitFor(const DirectoryLockImpl& aExistingLock) { - MOZ_ASSERT(NS_IsMainThread()); + AssertIsOnOwningThread(); // Waiting is never required if the ops in comparison represent shared locks. if (!aExistingLock.mExclusive && !mExclusive) { @@ -1725,7 +2097,7 @@ DirectoryLockImpl::MustWaitFor(const DirectoryLockImpl& aExistingLock) void DirectoryLockImpl::NotifyOpenListener() { - MOZ_ASSERT(NS_IsMainThread()); + AssertIsOnOwningThread(); MOZ_ASSERT(mQuotaManager); MOZ_ASSERT(mOpenListener); @@ -1740,7 +2112,246 @@ DirectoryLockImpl::NotifyOpenListener() mQuotaManager->RemovePendingDirectoryLock(this); } -NS_IMPL_ISUPPORTS0(DirectoryLockImpl); +nsresult +QuotaManager:: +CreateRunnable::Init() +{ + MOZ_ASSERT(NS_IsMainThread()); + MOZ_ASSERT(mState == State::Initial); + + nsresult rv; + + nsCOMPtr baseDir; + rv = NS_GetSpecialDirectory(NS_APP_INDEXEDDB_PARENT_DIR, + getter_AddRefs(baseDir)); + if (NS_FAILED(rv)) { + rv = NS_GetSpecialDirectory(NS_APP_USER_PROFILE_50_DIR, + getter_AddRefs(baseDir)); + } + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + rv = baseDir->GetPath(mBaseDirPath); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + return NS_OK; +} + +nsresult +QuotaManager:: +CreateRunnable::CreateManager() +{ + AssertIsOnOwningThread(); + MOZ_ASSERT(mState == State::CreatingManager); + + mManager = new QuotaManager(); + + nsresult rv = mManager->Init(mBaseDirPath); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + return NS_OK; +} + +nsresult +QuotaManager:: +CreateRunnable::RegisterObserver() +{ + MOZ_ASSERT(NS_IsMainThread()); + MOZ_ASSERT(mState == State::RegisteringObserver); + + if (NS_FAILED(Preferences::AddIntVarCache(&gFixedLimitKB, PREF_FIXED_LIMIT, + kDefaultFixedLimitKB)) || + NS_FAILED(Preferences::AddUintVarCache(&gChunkSizeKB, + PREF_CHUNK_SIZE, + kDefaultChunkSizeKB))) { + NS_WARNING("Unable to respond to temp storage pref changes!"); + } + + if (NS_FAILED(Preferences::AddBoolVarCache(&gTestingEnabled, + PREF_TESTING_FEATURES, false))) { + NS_WARNING("Unable to respond to testing pref changes!"); + } + + nsCOMPtr observerService = + mozilla::services::GetObserverService(); + if (NS_WARN_IF(!observerService)) { + return NS_ERROR_FAILURE; + } + + nsCOMPtr observer = new ShutdownObserver(mOwningThread); + + nsresult rv = observerService->AddObserver(observer, + PROFILE_BEFORE_CHANGE_OBSERVER_ID, + false); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + QuotaManagerService* qms = QuotaManagerService::GetOrCreate(); + if (NS_WARN_IF(!qms)) { + return rv; + } + + qms->NoteLiveManager(mManager); + + return NS_OK; +} + +void +QuotaManager:: +CreateRunnable::CallCallbacks() +{ + AssertIsOnOwningThread(); + MOZ_ASSERT(mState == State::CallingCallbacks); + + gCreateRunnable = nullptr; + + if (NS_FAILED(mResultCode)) { + gCreateFailed = true; + } else { + gInstance = mManager; + } + + mManager = nullptr; + + nsTArray> callbacks; + mCallbacks.SwapElements(callbacks); + + for (nsCOMPtr& callback : callbacks) { + Unused << callback->Run(); + } +} + +auto +QuotaManager:: +CreateRunnable::GetNextState(nsCOMPtr& aThread) -> State +{ + switch (mState) { + case State::Initial: + aThread = mOwningThread; + return State::CreatingManager; + case State::CreatingManager: + aThread = do_GetMainThread(); + return State::RegisteringObserver; + case State::RegisteringObserver: + aThread = mOwningThread; + return State::CallingCallbacks; + case State::CallingCallbacks: + aThread = nullptr; + return State::Completed; + default: + MOZ_CRASH("Bad state!"); + } +} + +NS_IMETHODIMP +QuotaManager:: +CreateRunnable::Run() +{ + nsresult rv; + + switch (mState) { + case State::Initial: + rv = Init(); + break; + + case State::CreatingManager: + rv = CreateManager(); + break; + + case State::RegisteringObserver: + rv = RegisterObserver(); + break; + + case State::CallingCallbacks: + CallCallbacks(); + rv = NS_OK; + break; + + case State::Completed: + default: + MOZ_CRASH("Bad state!"); + } + + nsCOMPtr thread; + if (NS_WARN_IF(NS_FAILED(rv))) { + if (NS_SUCCEEDED(mResultCode)) { + mResultCode = rv; + } + + mState = State::CallingCallbacks; + thread = mOwningThread; + } else { + mState = GetNextState(thread); + } + + if (thread) { + MOZ_ALWAYS_TRUE(NS_SUCCEEDED(thread->Dispatch(this, NS_DISPATCH_NORMAL))); + } + + return NS_OK; +} + +NS_IMETHODIMP +QuotaManager:: +ShutdownRunnable::Run() +{ + if (NS_IsMainThread()) { + QuotaManagerService* qms = QuotaManagerService::Get(); + MOZ_ASSERT(qms); + + qms->NoteFinishedManager(); + + mDone = true; + + return NS_OK; + } + + AssertIsOnBackgroundThread(); + + RefPtr quotaManager = gInstance.get(); + if (quotaManager) { + quotaManager->Shutdown(); + + gInstance = nullptr; + } + + MOZ_ALWAYS_TRUE(NS_SUCCEEDED(NS_DispatchToMainThread(this))); + + return NS_OK; +} + +NS_IMPL_ISUPPORTS(QuotaManager::ShutdownObserver, nsIObserver) + +NS_IMETHODIMP +QuotaManager:: +ShutdownObserver::Observe(nsISupports* aSubject, + const char* aTopic, + const char16_t* aData) +{ + MOZ_ASSERT(NS_IsMainThread()); + MOZ_ASSERT(!strcmp(aTopic, PROFILE_BEFORE_CHANGE_OBSERVER_ID)); + + bool done = false; + + RefPtr shutdownRunnable = new ShutdownRunnable(done); + MOZ_ALWAYS_TRUE(NS_SUCCEEDED( + mBackgroundThread->Dispatch(shutdownRunnable, NS_DISPATCH_NORMAL))); + + nsIThread* currentThread = NS_GetCurrentThread(); + MOZ_ASSERT(currentThread); + + while (!done) { + MOZ_ALWAYS_TRUE(NS_ProcessNextEvent(currentThread)); + } + + return NS_OK; +} /******************************************************************************* * Quota object @@ -1990,67 +2601,51 @@ QuotaManager::QuotaManager() mTemporaryStorageInitialized(false), mStorageAreaInitialized(false) { - NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); - NS_ASSERTION(!gInstance, "More than one instance!"); + AssertIsOnOwningThread(); + MOZ_ASSERT(!gInstance); } QuotaManager::~QuotaManager() { - NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); - NS_ASSERTION(!gInstance || gInstance == this, "Different instances!"); - gInstance = nullptr; + AssertIsOnOwningThread(); + MOZ_ASSERT(!gInstance || gInstance == this); } -// static -QuotaManager* -QuotaManager::GetOrCreate() +void +QuotaManager::GetOrCreate(nsIRunnable* aCallback) { - NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); + AssertIsOnBackgroundThread(); if (IsShuttingDown()) { - NS_ERROR("Calling GetOrCreate() after shutdown!"); - return nullptr; + MOZ_ASSERT(false, "Calling GetOrCreate() after shutdown!"); + return; } - if (!gInstance) { - RefPtr instance(new QuotaManager()); + if (gInstance || gCreateFailed) { + MOZ_ASSERT(!gCreateRunnable); + MOZ_ASSERT_IF(gCreateFailed, !gInstance); - nsresult rv = instance->Init(); - NS_ENSURE_SUCCESS(rv, nullptr); + MOZ_ALWAYS_TRUE(NS_SUCCEEDED(NS_DispatchToCurrentThread(aCallback))); + } else { + if (!gCreateRunnable) { + gCreateRunnable = new CreateRunnable(); + MOZ_ALWAYS_TRUE(NS_SUCCEEDED(NS_DispatchToMainThread(gCreateRunnable))); + } - nsCOMPtr obs = mozilla::services::GetObserverService(); - NS_ENSURE_TRUE(obs, nullptr); - - // We need this callback to know when to shut down all our threads. - rv = obs->AddObserver(instance, PROFILE_BEFORE_CHANGE_OBSERVER_ID, false); - NS_ENSURE_SUCCESS(rv, nullptr); - - // The observer service will hold our last reference, don't AddRef here. - gInstance = instance; + gCreateRunnable->AddCallback(aCallback); } - - return gInstance; } // static QuotaManager* QuotaManager::Get() { + MOZ_ASSERT(!NS_IsMainThread()); + // Does not return an owning reference. return gInstance; } -// static -QuotaManager* -QuotaManager::FactoryCreate() -{ - // Returns a raw pointer that carries an owning reference! Lame, but the - // singleton factory macros force this. - QuotaManager* quotaManager = GetOrCreate(); - NS_IF_ADDREF(quotaManager); - return quotaManager; -} - // static bool QuotaManager::IsShuttingDown() @@ -2069,7 +2664,7 @@ QuotaManager::CreateDirectoryLock(Nullable aPersistenceType, OpenDirectoryListener* aOpenListener) -> already_AddRefed { - MOZ_ASSERT(NS_IsMainThread()); + AssertIsOnOwningThread(); MOZ_ASSERT_IF(!aInternal, !aPersistenceType.IsNull()); MOZ_ASSERT_IF(!aInternal, aPersistenceType.Value() != PERSISTENCE_TYPE_INVALID); @@ -2122,7 +2717,7 @@ QuotaManager::CreateDirectoryLockForEviction(PersistenceType aPersistenceType, bool aIsApp) -> already_AddRefed { - MOZ_ASSERT(NS_IsMainThread()); + AssertIsOnOwningThread(); MOZ_ASSERT(aPersistenceType != PERSISTENCE_TYPE_INVALID); MOZ_ASSERT(!aOrigin.IsEmpty()); @@ -2152,7 +2747,7 @@ QuotaManager::CreateDirectoryLockForEviction(PersistenceType aPersistenceType, void QuotaManager::RegisterDirectoryLock(DirectoryLockImpl* aLock) { - MOZ_ASSERT(NS_IsMainThread()); + AssertIsOnOwningThread(); MOZ_ASSERT(aLock); mDirectoryLocks.AppendElement(aLock); @@ -2188,7 +2783,7 @@ QuotaManager::RegisterDirectoryLock(DirectoryLockImpl* aLock) void QuotaManager::UnregisterDirectoryLock(DirectoryLockImpl* aLock) { - MOZ_ASSERT(NS_IsMainThread()); + AssertIsOnOwningThread(); MOZ_ALWAYS_TRUE(mDirectoryLocks.RemoveElement(aLock)); @@ -2224,7 +2819,7 @@ QuotaManager::UnregisterDirectoryLock(DirectoryLockImpl* aLock) void QuotaManager::RemovePendingDirectoryLock(DirectoryLockImpl* aLock) { - MOZ_ASSERT(NS_IsMainThread()); + AssertIsOnOwningThread(); MOZ_ASSERT(aLock); MOZ_ALWAYS_TRUE(mPendingDirectoryLocks.RemoveElement(aLock)); @@ -2235,7 +2830,7 @@ QuotaManager::CollectOriginsForEviction( uint64_t aMinSizeToBeFreed, nsTArray>& aLocks) { - MOZ_ASSERT(NS_IsMainThread()); + AssertIsOnOwningThread(); MOZ_ASSERT(aLocks.IsEmpty()); struct MOZ_STACK_CLASS Helper final @@ -2363,75 +2958,76 @@ QuotaManager::CollectOriginsForEviction( } nsresult -QuotaManager::Init() +QuotaManager::Init(const nsAString& aBaseDirPath) { nsresult rv; - if (XRE_IsParentProcess()) { - nsCOMPtr baseDir; - rv = NS_GetSpecialDirectory(NS_APP_INDEXEDDB_PARENT_DIR, - getter_AddRefs(baseDir)); - if (NS_FAILED(rv)) { - rv = NS_GetSpecialDirectory(NS_APP_USER_PROFILE_50_DIR, - getter_AddRefs(baseDir)); - } - NS_ENSURE_SUCCESS(rv, rv); - rv = CloneStoragePath(baseDir, - NS_LITERAL_STRING(INDEXEDDB_DIRECTORY_NAME), - mIndexedDBPath); - NS_ENSURE_SUCCESS(rv, rv); - - rv = baseDir->Append(NS_LITERAL_STRING(STORAGE_DIRECTORY_NAME)); - NS_ENSURE_SUCCESS(rv, rv); - - rv = baseDir->GetPath(mStoragePath); - NS_ENSURE_SUCCESS(rv, rv); - - rv = CloneStoragePath(baseDir, - NS_LITERAL_STRING(PERMANENT_DIRECTORY_NAME), - mPermanentStoragePath); - NS_ENSURE_SUCCESS(rv, rv); - - rv = CloneStoragePath(baseDir, - NS_LITERAL_STRING(TEMPORARY_DIRECTORY_NAME), - mTemporaryStoragePath); - NS_ENSURE_SUCCESS(rv, rv); - - rv = CloneStoragePath(baseDir, - NS_LITERAL_STRING(DEFAULT_DIRECTORY_NAME), - mDefaultStoragePath); - NS_ENSURE_SUCCESS(rv, rv); - - // Make a lazy thread for any IO we need (like clearing or enumerating the - // contents of storage directories). - mIOThread = new LazyIdleThread(DEFAULT_THREAD_TIMEOUT_MS, - NS_LITERAL_CSTRING("Storage I/O"), - LazyIdleThread::ManualShutdown); - - // Make a timer here to avoid potential failures later. We don't actually - // initialize the timer until shutdown. - mShutdownTimer = do_CreateInstance(NS_TIMER_CONTRACTID); - NS_ENSURE_TRUE(mShutdownTimer, NS_ERROR_FAILURE); + nsCOMPtr baseDir = do_CreateInstance(NS_LOCAL_FILE_CONTRACTID, &rv); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; } - if (NS_FAILED(Preferences::AddIntVarCache(&gFixedLimitKB, PREF_FIXED_LIMIT, - kDefaultFixedLimitKB)) || - NS_FAILED(Preferences::AddUintVarCache(&gChunkSizeKB, - PREF_CHUNK_SIZE, - kDefaultChunkSizeKB))) { - NS_WARNING("Unable to respond to temp storage pref changes!"); + rv = baseDir->InitWithPath(aBaseDirPath); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; } - if (NS_FAILED(Preferences::AddBoolVarCache(&gTestingEnabled, - PREF_TESTING_FEATURES, false))) { - NS_WARNING("Unable to respond to testing pref changes!"); + rv = CloneStoragePath(baseDir, + NS_LITERAL_STRING(INDEXEDDB_DIRECTORY_NAME), + mIndexedDBPath); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + rv = baseDir->Append(NS_LITERAL_STRING(STORAGE_DIRECTORY_NAME)); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + rv = baseDir->GetPath(mStoragePath); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + rv = CloneStoragePath(baseDir, + NS_LITERAL_STRING(PERMANENT_DIRECTORY_NAME), + mPermanentStoragePath); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + rv = CloneStoragePath(baseDir, + NS_LITERAL_STRING(TEMPORARY_DIRECTORY_NAME), + mTemporaryStoragePath); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + rv = CloneStoragePath(baseDir, + NS_LITERAL_STRING(DEFAULT_DIRECTORY_NAME), + mDefaultStoragePath); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + // Make a lazy thread for any IO we need (like clearing or enumerating the + // contents of storage directories). + mIOThread = new LazyIdleThread(DEFAULT_THREAD_TIMEOUT_MS, + NS_LITERAL_CSTRING("Storage I/O"), + LazyIdleThread::ManualShutdown); + + // Make a timer here to avoid potential failures later. We don't actually + // initialize the timer until shutdown. + mShutdownTimer = do_CreateInstance(NS_TIMER_CONTRACTID); + if (NS_WARN_IF(!mShutdownTimer)) { + return NS_ERROR_FAILURE; } static_assert(Client::IDB == 0 && Client::ASMJS == 1 && Client::DOMCACHE == 2 && Client::TYPE_MAX == 3, "Fix the registration!"); - NS_ASSERTION(mClients.Capacity() == Client::TYPE_MAX, - "Should be using an auto array with correct capacity!"); + MOZ_ASSERT(mClients.Capacity() == Client::TYPE_MAX, + "Should be using an auto array with correct capacity!"); // Register clients. mClients.AppendElement(indexedDB::CreateQuotaClient()); @@ -2441,6 +3037,58 @@ QuotaManager::Init() return NS_OK; } +void +QuotaManager::Shutdown() +{ + AssertIsOnOwningThread(); + + // Setting this flag prevents the service from being recreated and prevents + // further storagess from being created. + if (gShutdown.exchange(true)) { + NS_ERROR("Shutdown more than once?!"); + } + + StopIdleMaintenance(); + + // Kick off the shutdown timer. + MOZ_ALWAYS_TRUE(NS_SUCCEEDED( + mShutdownTimer->InitWithFuncCallback(&ShutdownTimerCallback, + this, + DEFAULT_SHUTDOWN_TIMER_MS, + nsITimer::TYPE_ONE_SHOT))); + + // Each client will spin the event loop while we wait on all the threads + // to close. Our timer may fire during that loop. + for (uint32_t index = 0; index < Client::TYPE_MAX; index++) { + mClients[index]->ShutdownWorkThreads(); + } + + // Cancel the timer regardless of whether it actually fired. + if (NS_FAILED(mShutdownTimer->Cancel())) { + NS_WARNING("Failed to cancel shutdown timer!"); + } + + // Give clients a chance to cleanup IO thread only objects. + nsCOMPtr runnable = + NS_NewRunnableMethod(this, &QuotaManager::ReleaseIOThreadObjects); + if (!runnable) { + NS_WARNING("Failed to create runnable!"); + } + + if (NS_FAILED(mIOThread->Dispatch(runnable, NS_DISPATCH_NORMAL))) { + NS_WARNING("Failed to dispatch runnable!"); + } + + // Make sure to join with our IO thread. + if (NS_FAILED(mIOThread->Shutdown())) { + NS_WARNING("Failed to shutdown IO thread!"); + } + + for (RefPtr& lock : mPendingDirectoryLocks) { + lock->Invalidate(); + } +} + void QuotaManager::InitQuotaForOrigin(PersistenceType aPersistenceType, const nsACString& aGroup, @@ -2504,7 +3152,7 @@ QuotaManager::UpdateOriginAccessTime(PersistenceType aPersistenceType, const nsACString& aGroup, const nsACString& aOrigin) { - NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); + AssertIsOnOwningThread(); MOZ_ASSERT(aPersistenceType != PERSISTENCE_TYPE_PERSISTENT); MutexAutoLock lock(mQuotaMutex); @@ -2662,7 +3310,7 @@ QuotaManager::GetQuotaObject(PersistenceType aPersistenceType, void QuotaManager::AbortOperationsForProcess(ContentParentId aContentParentId) { - MOZ_ASSERT(NS_IsMainThread()); + AssertIsOnOwningThread(); for (RefPtr& client : mClients) { client->AbortOperationsForProcess(aContentParentId); @@ -3120,7 +3768,7 @@ QuotaManager::OpenDirectory(PersistenceType aPersistenceType, bool aExclusive, OpenDirectoryListener* aOpenListener) { - MOZ_ASSERT(NS_IsMainThread()); + AssertIsOnOwningThread(); RefPtr lock = CreateDirectoryLock(Nullable(aPersistenceType), @@ -3140,7 +3788,7 @@ QuotaManager::OpenDirectoryInternal(Nullable aPersistenceType, bool aExclusive, OpenDirectoryListener* aOpenListener) { - MOZ_ASSERT(NS_IsMainThread()); + AssertIsOnOwningThread(); RefPtr lock = CreateDirectoryLock(aPersistenceType, @@ -3654,208 +4302,6 @@ QuotaManager::GetDirectoryMetadata(nsIFile* aDirectory, return NS_OK; } -NS_IMPL_ISUPPORTS(QuotaManager, nsIQuotaManager, nsIObserver) - -NS_IMETHODIMP -QuotaManager::GetUsageForPrincipal(nsIPrincipal* aPrincipal, - nsIUsageCallback* aCallback, - nsIQuotaRequest** _retval) -{ - NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); - - NS_ENSURE_ARG_POINTER(aPrincipal); - NS_ENSURE_ARG_POINTER(aCallback); - - // This only works from the main process. - NS_ENSURE_TRUE(XRE_IsParentProcess(), NS_ERROR_NOT_AVAILABLE); - - // Figure out which origin we're dealing with. - nsCString group; - nsCString origin; - bool isApp; - nsresult rv = GetInfoFromPrincipal(aPrincipal, &group, &origin, &isApp); - NS_ENSURE_SUCCESS(rv, rv); - - RefPtr op = - new GetUsageOp(group, origin, isApp, aPrincipal, aCallback); - - op->RunImmediately(); - - op.forget(_retval); - return NS_OK; -} - -NS_IMETHODIMP -QuotaManager::Clear() -{ - NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); - - if (!gTestingEnabled) { - NS_WARNING("Testing features are not enabled!"); - return NS_OK; - } - - RefPtr op = new ResetOrClearOp(/* aClear */ true); - - op->RunImmediately(); - - return NS_OK; -} - -NS_IMETHODIMP -QuotaManager::ClearStoragesForPrincipal(nsIPrincipal* aPrincipal, - const nsACString& aPersistenceType) -{ - NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); - - NS_ENSURE_ARG_POINTER(aPrincipal); - - Nullable persistenceType; - nsresult rv = - NullablePersistenceTypeFromText(aPersistenceType, &persistenceType); - if (NS_WARN_IF(NS_FAILED(rv))) { - return NS_ERROR_INVALID_ARG; - } - - // This only works from the main process. - NS_ENSURE_TRUE(XRE_IsParentProcess(), NS_ERROR_NOT_AVAILABLE); - - // Figure out which origin we're dealing with. - nsCString origin; - rv = GetInfoFromPrincipal(aPrincipal, nullptr, &origin, nullptr); - NS_ENSURE_SUCCESS(rv, rv); - - const mozilla::OriginAttributes& attrs = - mozilla::BasePrincipal::Cast(aPrincipal)->OriginAttributesRef(); - - nsAutoCString pattern; - GetOriginPatternString(attrs.mAppId, attrs.mInBrowser, origin, pattern); - - RefPtr op = - new OriginClearOp(persistenceType, OriginScope::FromPattern(pattern)); - - op->RunImmediately(); - - return NS_OK; -} - -NS_IMETHODIMP -QuotaManager::Reset() -{ - NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); - - if (!gTestingEnabled) { - NS_WARNING("Testing features are not enabled!"); - return NS_OK; - } - - RefPtr op = new ResetOrClearOp(/* aClear */ false); - - op->RunImmediately(); - - return NS_OK; -} - -NS_IMETHODIMP -QuotaManager::Observe(nsISupports* aSubject, - const char* aTopic, - const char16_t* aData) -{ - NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); - - if (!strcmp(aTopic, PROFILE_BEFORE_CHANGE_OBSERVER_ID)) { - // Setting this flag prevents the service from being recreated and prevents - // further storagess from being created. - if (gShutdown.exchange(true)) { - NS_ERROR("Shutdown more than once?!"); - } - - if (XRE_IsParentProcess()) { - // Kick off the shutdown timer. - if (NS_FAILED(mShutdownTimer->Init(this, DEFAULT_SHUTDOWN_TIMER_MS, - nsITimer::TYPE_ONE_SHOT))) { - NS_WARNING("Failed to initialize shutdown timer!"); - } - - // Each client will spin the event loop while we wait on all the threads - // to close. Our timer may fire during that loop. - for (uint32_t index = 0; index < Client::TYPE_MAX; index++) { - mClients[index]->ShutdownWorkThreads(); - } - - // Cancel the timer regardless of whether it actually fired. - if (NS_FAILED(mShutdownTimer->Cancel())) { - NS_WARNING("Failed to cancel shutdown timer!"); - } - - // Give clients a chance to cleanup IO thread only objects. - nsCOMPtr runnable = - NS_NewRunnableMethod(this, &QuotaManager::ReleaseIOThreadObjects); - if (!runnable) { - NS_WARNING("Failed to create runnable!"); - } - - if (NS_FAILED(mIOThread->Dispatch(runnable, NS_DISPATCH_NORMAL))) { - NS_WARNING("Failed to dispatch runnable!"); - } - - // Make sure to join with our IO thread. - if (NS_FAILED(mIOThread->Shutdown())) { - NS_WARNING("Failed to shutdown IO thread!"); - } - - for (RefPtr& lock : mPendingDirectoryLocks) { - lock->Invalidate(); - } - } - - return NS_OK; - } - - if (!strcmp(aTopic, NS_TIMER_CALLBACK_TOPIC)) { - NS_ASSERTION(XRE_IsParentProcess(), "Should only happen in the main process!"); - - NS_WARNING("Some storage operations are taking longer than expected " - "during shutdown and will be aborted!"); - - // Abort all operations. - for (RefPtr& client : mClients) { - client->AbortOperations(NullCString()); - } - - return NS_OK; - } - - if (!strcmp(aTopic, TOPIC_WEB_APP_CLEAR_DATA)) { - nsCOMPtr params = - do_QueryInterface(aSubject); - NS_ENSURE_TRUE(params, NS_ERROR_UNEXPECTED); - - uint32_t appId; - nsresult rv = params->GetAppId(&appId); - NS_ENSURE_SUCCESS(rv, rv); - - bool browserOnly; - rv = params->GetBrowserOnly(&browserOnly); - NS_ENSURE_SUCCESS(rv, rv); - - rv = ClearStoragesForApp(appId, browserOnly); - NS_ENSURE_SUCCESS(rv, rv); - - return NS_OK; - } - - if (!strcmp(aTopic, OBSERVER_TOPIC_IDLE_DAILY)) { - for (auto& client : mClients) { - client->PerformIdleMaintenance(); - } - return NS_OK; - } - - NS_NOTREACHED("Unknown topic!"); - return NS_ERROR_UNEXPECTED; -} - uint64_t QuotaManager::LockedCollectOriginsForEviction( uint64_t aMinSizeToBeFreed, @@ -3873,7 +4319,8 @@ QuotaManager::LockedCollectOriginsForEviction( { MutexAutoUnlock autoUnlock(mQuotaMutex); - MOZ_ALWAYS_TRUE(NS_SUCCEEDED(NS_DispatchToMainThread(helper))); + MOZ_ALWAYS_TRUE(NS_SUCCEEDED(mOwningThread->Dispatch(helper, + NS_DISPATCH_NORMAL))); } return helper->BlockAndReturnOriginsForEviction(aLocks); @@ -3908,27 +4355,6 @@ QuotaManager::LockedRemoveQuotaForOrigin(PersistenceType aPersistenceType, } } -nsresult -QuotaManager::ClearStoragesForApp(uint32_t aAppId, bool aBrowserOnly) -{ - NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); - NS_ASSERTION(aAppId != kUnknownAppId, "Bad appId!"); - - // This only works from the main process. - NS_ENSURE_TRUE(XRE_IsParentProcess(), NS_ERROR_NOT_AVAILABLE); - - nsAutoCString pattern; - GetOriginPatternStringMaybeIgnoreBrowser(aAppId, aBrowserOnly, pattern); - - RefPtr op = - new OriginClearOp(Nullable(), - OriginScope::FromPattern(pattern)); - - op->RunImmediately(); - - return NS_OK; -} - void QuotaManager::CheckTemporaryStorageLimits() { @@ -4094,7 +4520,7 @@ QuotaManager::FinalizeOriginEviction( NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!"); RefPtr op = - new FinalizeOriginEvictionOp(aLocks); + new FinalizeOriginEvictionOp(mOwningThread, aLocks); if (IsOnIOThread()) { op->RunOnIOThreadImmediately(); @@ -4145,6 +4571,23 @@ QuotaManager::GetOriginPatternString(uint32_t aAppId, _retval = aOrigin; } +void +QuotaManager::ShutdownTimerCallback(nsITimer* aTimer, void* aClosure) +{ + AssertIsOnBackgroundThread(); + + auto quotaManager = static_cast(aClosure); + MOZ_ASSERT(quotaManager); + + NS_WARNING("Some storage operations are taking longer than expected " + "during shutdown and will be aborted!"); + + // Abort all operations. + for (RefPtr& client : quotaManager->mClients) { + client->AbortOperations(NullCString()); + } +} + auto QuotaManager::GetDirectoryLockTable(PersistenceType aPersistenceType) -> DirectoryLockTable& @@ -4309,7 +4752,7 @@ CollectOriginsHelper::BlockAndReturnOriginsForEviction( NS_IMETHODIMP CollectOriginsHelper::Run() { - MOZ_ASSERT(NS_IsMainThread(), "Wrong thread!"); + AssertIsOnBackgroundThread(); QuotaManager* quotaManager = QuotaManager::Get(); NS_ASSERTION(quotaManager, "Shouldn't be null!"); @@ -4332,6 +4775,10 @@ CollectOriginsHelper::Run() return NS_OK; } +/******************************************************************************* + * OriginOperationBase + ******************************************************************************/ + NS_IMETHODIMP OriginOperationBase::Run() { @@ -4339,7 +4786,22 @@ OriginOperationBase::Run() switch (mState) { case State_Initial: { - rv = Open(); + rv = Init(); + break; + } + + case State_Initializing: { + rv = InitOnMainThread(); + break; + } + + case State_FinishingInit: { + rv = FinishInit(); + break; + } + + case State_CreatingQuotaManager: { + rv = QuotaManagerOpen(); break; } @@ -4372,7 +4834,7 @@ OriginOperationBase::Run() nsresult OriginOperationBase::DirectoryOpen() { - MOZ_ASSERT(NS_IsMainThread()); + AssertIsOnOwningThread(); MOZ_ASSERT(mState == State_DirectoryOpenPending); QuotaManager* quotaManager = QuotaManager::Get(); @@ -4402,7 +4864,81 @@ OriginOperationBase::Finish(nsresult aResult) // thread. mState = State_UnblockingOpen; - MOZ_ALWAYS_TRUE(NS_SUCCEEDED(NS_DispatchToMainThread(this))); + MOZ_ALWAYS_TRUE(NS_SUCCEEDED(mOwningThread->Dispatch(this, + NS_DISPATCH_NORMAL))); +} + +nsresult +OriginOperationBase::Init() +{ + AssertIsOnOwningThread(); + MOZ_ASSERT(mState == State_Initial); + + AdvanceState(); + + if (mNeedsMainThreadInit) { + MOZ_ALWAYS_TRUE(NS_SUCCEEDED(NS_DispatchToMainThread(this))); + } else { + AdvanceState(); + MOZ_ALWAYS_TRUE(NS_SUCCEEDED(Run())); + } + + return NS_OK; +} + +nsresult +OriginOperationBase::InitOnMainThread() +{ + MOZ_ASSERT(NS_IsMainThread()); + MOZ_ASSERT(mState == State_Initializing); + + nsresult rv = DoInitOnMainThread(); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + AdvanceState(); + + MOZ_ALWAYS_TRUE(NS_SUCCEEDED(mOwningThread->Dispatch(this, + NS_DISPATCH_NORMAL))); + + return NS_OK; +} + +nsresult +OriginOperationBase::FinishInit() +{ + AssertIsOnOwningThread(); + MOZ_ASSERT(mState == State_FinishingInit); + + if (QuotaManager::IsShuttingDown()) { + return NS_ERROR_FAILURE; + } + + AdvanceState(); + + if (mNeedsQuotaManagerInit && !QuotaManager::Get()) { + QuotaManager::GetOrCreate(this); + } else { + Open(); + } + + return NS_OK; +} + +nsresult +OriginOperationBase::QuotaManagerOpen() +{ + AssertIsOnOwningThread(); + MOZ_ASSERT(mState == State_CreatingQuotaManager); + + if (NS_WARN_IF(!QuotaManager::Get())) { + return NS_ERROR_FAILURE; + } + + Open(); + + return NS_OK; } nsresult @@ -4421,47 +4957,99 @@ OriginOperationBase::DirectoryWork() return rv; } - // Must set mState before dispatching otherwise we will race with the main + // Must set mState before dispatching otherwise we will race with the owning // thread. AdvanceState(); - MOZ_ALWAYS_TRUE(NS_SUCCEEDED(NS_DispatchToMainThread(this))); - - return NS_OK; -} - -NS_IMPL_ISUPPORTS_INHERITED0(NormalOriginOperationBase, nsRunnable) - -nsresult -NormalOriginOperationBase::Open() -{ - MOZ_ASSERT(NS_IsMainThread()); - MOZ_ASSERT(mState == State_Initial); - - if (QuotaManager::IsShuttingDown()) { - return NS_ERROR_FAILURE; - } - - QuotaManager* quotaManager = QuotaManager::Get(); - if (NS_WARN_IF(!quotaManager)) { - return NS_ERROR_FAILURE; - } - - AdvanceState(); - - quotaManager->OpenDirectoryInternal(mPersistenceType, - mOriginScope, - mExclusive, - this); + MOZ_ALWAYS_TRUE(NS_SUCCEEDED(mOwningThread->Dispatch(this, + NS_DISPATCH_NORMAL))); return NS_OK; } +void +FinalizeOriginEvictionOp::Dispatch() +{ + MOZ_ASSERT(!NS_IsMainThread()); + MOZ_ASSERT(GetState() == State_Initial); + + SetState(State_DirectoryOpenPending); + + MOZ_ALWAYS_TRUE(NS_SUCCEEDED(mOwningThread->Dispatch(this, + NS_DISPATCH_NORMAL))); +} + +void +FinalizeOriginEvictionOp::RunOnIOThreadImmediately() +{ + AssertIsOnIOThread(); + MOZ_ASSERT(GetState() == State_Initial); + + SetState(State_DirectoryWorkOpen); + + MOZ_ALWAYS_TRUE(NS_SUCCEEDED(this->Run())); +} + +void +FinalizeOriginEvictionOp::Open() +{ + MOZ_CRASH("Shouldn't get here!"); +} + +nsresult +FinalizeOriginEvictionOp::DoDirectoryWork(QuotaManager* aQuotaManager) +{ + AssertIsOnIOThread(); + + PROFILER_LABEL("Quota", "FinalizeOriginEvictionOp::DoDirectoryWork", + js::ProfileEntry::Category::OTHER); + + for (RefPtr& lock : mLocks) { + aQuotaManager->OriginClearCompleted(lock->GetPersistenceType().Value(), + lock->GetOriginScope(), + lock->GetIsApp().Value()); + } + + return NS_OK; +} + +void +FinalizeOriginEvictionOp::UnblockOpen() +{ + AssertIsOnOwningThread(); + MOZ_ASSERT(GetState() == State_UnblockingOpen); + +#ifdef DEBUG + NoteActorDestroyed(); +#endif + + mLocks.Clear(); + + AdvanceState(); +} + +NS_IMPL_ISUPPORTS_INHERITED0(NormalOriginOperationBase, nsRunnable) + +void +NormalOriginOperationBase::Open() +{ + AssertIsOnOwningThread(); + MOZ_ASSERT(GetState() == State_CreatingQuotaManager); + MOZ_ASSERT(QuotaManager::Get()); + + AdvanceState(); + + QuotaManager::Get()->OpenDirectoryInternal(mPersistenceType, + mOriginScope, + mExclusive, + this); +} + void NormalOriginOperationBase::UnblockOpen() { - MOZ_ASSERT(NS_IsMainThread()); - MOZ_ASSERT(mState == State_UnblockingOpen); + AssertIsOnOwningThread(); + MOZ_ASSERT(GetState() == State_UnblockingOpen); SendResults(); @@ -4473,9 +5061,9 @@ NormalOriginOperationBase::UnblockOpen() void NormalOriginOperationBase::DirectoryLockAcquired(DirectoryLock* aLock) { - MOZ_ASSERT(NS_IsMainThread()); + AssertIsOnOwningThread(); MOZ_ASSERT(aLock); - MOZ_ASSERT(mState == State_DirectoryOpenPending); + MOZ_ASSERT(GetState() == State_DirectoryOpenPending); MOZ_ASSERT(!mDirectoryLock); mDirectoryLock = aLock; @@ -4490,8 +5078,8 @@ NormalOriginOperationBase::DirectoryLockAcquired(DirectoryLock* aLock) void NormalOriginOperationBase::DirectoryLockFailed() { - MOZ_ASSERT(NS_IsMainThread()); - MOZ_ASSERT(mState == State_DirectoryOpenPending); + AssertIsOnOwningThread(); + MOZ_ASSERT(GetState() == State_DirectoryOpenPending); MOZ_ASSERT(!mDirectoryLock); Finish(NS_ERROR_FAILURE); @@ -4534,24 +5122,268 @@ SaveOriginAccessTimeOp::DoDirectoryWork(QuotaManager* aQuotaManager) return NS_OK; } -GetUsageOp::GetUsageOp(const nsACString& aGroup, - const nsACString& aOrigin, - bool aIsApp, - nsIPrincipal* aPrincipal, - nsIUsageCallback* aCallback) +void +SaveOriginAccessTimeOp::SendResults() +{ +#ifdef DEBUG + NoteActorDestroyed(); +#endif +} + +/******************************************************************************* + * Quota + ******************************************************************************/ + +Quota::Quota() + : mActorDestroyed(false) +{ +} + +Quota::~Quota() +{ + MOZ_ASSERT(mActorDestroyed); +} + +void +Quota::StartIdleMaintenance() +{ + AssertIsOnBackgroundThread(); + MOZ_ASSERT(!QuotaManager::IsShuttingDown()); + + QuotaManager* quotaManager = QuotaManager::Get(); + if (NS_WARN_IF(!quotaManager)) { + return; + } + + quotaManager->StartIdleMaintenance(); +} + +void +Quota::ActorDestroy(ActorDestroyReason aWhy) +{ + AssertIsOnBackgroundThread(); + MOZ_ASSERT(!mActorDestroyed); + + mActorDestroyed = true; +} + +PQuotaUsageRequestParent* +Quota::AllocPQuotaUsageRequestParent(const UsageRequestParams& aParams) +{ + RefPtr actor = new GetUsageOp(aParams); + + // Transfer ownership to IPDL. + return actor.forget().take(); +} + +bool +Quota::RecvPQuotaUsageRequestConstructor(PQuotaUsageRequestParent* aActor, + const UsageRequestParams& aParams) +{ + AssertIsOnBackgroundThread(); + MOZ_ASSERT(aActor); + MOZ_ASSERT(aParams.type() != UsageRequestParams::T__None); + + auto* op = static_cast(aActor); + + if (NS_WARN_IF(!op->Init(this))) { + return false; + } + + op->RunImmediately(); + return true; +} + +bool +Quota::DeallocPQuotaUsageRequestParent(PQuotaUsageRequestParent* aActor) +{ + AssertIsOnBackgroundThread(); + MOZ_ASSERT(aActor); + + // Transfer ownership back from IPDL. + RefPtr actor = + dont_AddRef(static_cast(aActor)); + return true; +} + +PQuotaRequestParent* +Quota::AllocPQuotaRequestParent(const RequestParams& aParams) +{ + AssertIsOnBackgroundThread(); + MOZ_ASSERT(aParams.type() != RequestParams::T__None); + + if (aParams.type() == RequestParams::TClearAppParams) { + PBackgroundParent* actor = Manager(); + MOZ_ASSERT(actor); + + if (BackgroundParent::IsOtherProcessActor(actor)) { + ASSERT_UNLESS_FUZZING(); + return nullptr; + } + } + + RefPtr actor; + + switch (aParams.type()) { + case RequestParams::TClearOriginParams: + case RequestParams::TClearAppParams: + actor = new OriginClearOp(aParams); + break; + + case RequestParams::TClearAllParams: + actor = new ResetOrClearOp(/* aClear */ true); + break; + + case RequestParams::TResetAllParams: + actor = new ResetOrClearOp(/* aClear */ false); + break; + + default: + MOZ_CRASH("Should never get here!"); + } + + MOZ_ASSERT(actor); + + // Transfer ownership to IPDL. + return actor.forget().take(); +} + +bool +Quota::RecvPQuotaRequestConstructor(PQuotaRequestParent* aActor, + const RequestParams& aParams) +{ + AssertIsOnBackgroundThread(); + MOZ_ASSERT(aActor); + MOZ_ASSERT(aParams.type() != RequestParams::T__None); + + auto* op = static_cast(aActor); + if (NS_WARN_IF(!op->Init(this))) { + return false; + } + + op->RunImmediately(); + return true; +} + +bool +Quota::DeallocPQuotaRequestParent(PQuotaRequestParent* aActor) +{ + AssertIsOnBackgroundThread(); + MOZ_ASSERT(aActor); + + // Transfer ownership back from IPDL. + RefPtr actor = + dont_AddRef(static_cast(aActor)); + return true; +} + +bool +Quota::RecvStartIdleMaintenance() +{ + AssertIsOnBackgroundThread(); + + PBackgroundParent* actor = Manager(); + MOZ_ASSERT(actor); + + if (BackgroundParent::IsOtherProcessActor(actor)) { + ASSERT_UNLESS_FUZZING(); + return false; + } + + if (QuotaManager::IsShuttingDown()) { + return true; + } + + QuotaManager* quotaManager = QuotaManager::Get(); + if (!quotaManager) { + nsCOMPtr callback = + NS_NewRunnableMethod(this, &Quota::StartIdleMaintenance); + + QuotaManager::GetOrCreate(callback); + return true; + } + + quotaManager->StartIdleMaintenance(); + + return true; +} + +bool +Quota::RecvStopIdleMaintenance() +{ + AssertIsOnBackgroundThread(); + + PBackgroundParent* actor = Manager(); + MOZ_ASSERT(actor); + + if (BackgroundParent::IsOtherProcessActor(actor)) { + ASSERT_UNLESS_FUZZING(); + return false; + } + + if (QuotaManager::IsShuttingDown()) { + return true; + } + + QuotaManager* quotaManager = QuotaManager::Get(); + if (!quotaManager) { + return true; + } + + quotaManager->StopIdleMaintenance(); + + return true; +} + +GetUsageOp::GetUsageOp(const UsageRequestParams& aParams) : NormalOriginOperationBase(Nullable(), - OriginScope::FromOrigin(aOrigin), + OriginScope::FromNull(), /* aExclusive */ false) - , mGroup(aGroup) - , mPrincipal(aPrincipal) - , mCallback(aCallback) - , mIsApp(aIsApp) + , mParams(aParams.get_UsageParams()) +{ + AssertIsOnOwningThread(); + MOZ_ASSERT(aParams.type() == UsageRequestParams::TUsageParams); +} + +bool +GetUsageOp::Init(Quota* aQuota) +{ + AssertIsOnOwningThread(); + MOZ_ASSERT(aQuota); + + mNeedsMainThreadInit = true; + mNeedsQuotaManagerInit = true; + + return true; +} + +nsresult +GetUsageOp::DoInitOnMainThread() { MOZ_ASSERT(NS_IsMainThread()); - MOZ_ASSERT(!aGroup.IsEmpty()); - MOZ_ASSERT(!aOrigin.IsEmpty()); - MOZ_ASSERT(aPrincipal); - MOZ_ASSERT(aCallback); + MOZ_ASSERT(GetState() == State_Initializing); + MOZ_ASSERT(mNeedsMainThreadInit); + + const PrincipalInfo& principalInfo = mParams.principalInfo(); + + nsresult rv; + nsCOMPtr principal = + PrincipalInfoToPrincipal(principalInfo, &rv); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + // Figure out which origin we're dealing with. + nsCString origin; + rv = QuotaManager::GetInfoFromPrincipal(principal, &mGroup, &origin, + &mIsApp); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + mOriginScope.SetFromOrigin(origin); + + return NS_OK; } nsresult @@ -4676,31 +5508,92 @@ GetUsageOp::DoDirectoryWork(QuotaManager* aQuotaManager) void GetUsageOp::SendResults() { - MOZ_ASSERT(NS_IsMainThread()); + AssertIsOnOwningThread(); - // Call the callback unless we were canceled. - if (!mUsageInfo.Canceled()) { - // XXX Implement better error reporting here, bug 1170019. - if (NS_FAILED(mResultCode)) { - mUsageInfo.ResetUsage(); + if (IsActorDestroyed()) { + if (NS_SUCCEEDED(mResultCode)) { + mResultCode = NS_ERROR_FAILURE; + } + } else { + if (mUsageInfo.Canceled()) { + mResultCode = NS_ERROR_FAILURE; } - mCallback->OnUsageResult(mPrincipal, mUsageInfo.TotalUsage(), mUsageInfo.FileUsage()); - } + UsageRequestResponse response; - // Clean up. - mPrincipal = nullptr; - mCallback = nullptr; + if (NS_SUCCEEDED(mResultCode)) { + UsageResponse usageResponse; + usageResponse.usage() = mUsageInfo.TotalUsage(); + usageResponse.fileUsage() = mUsageInfo.FileUsage(); + response = usageResponse; + } else { + response = mResultCode; + } + + Unused << PQuotaUsageRequestParent::Send__delete__(this, response); + } } -NS_IMPL_ISUPPORTS_INHERITED(GetUsageOp, - NormalOriginOperationBase, - nsIQuotaRequest) - -NS_IMETHODIMP -GetUsageOp::Cancel() +void +GetUsageOp::ActorDestroy(ActorDestroyReason aWhy) { - return mUsageInfo.Cancel(); + AssertIsOnOwningThread(); + + NoteActorDestroyed(); +} + +bool +GetUsageOp::RecvCancel() +{ + AssertIsOnOwningThread(); + + nsresult rv = mUsageInfo.Cancel(); + if (NS_WARN_IF(NS_FAILED(rv))) { + return false; + } + + return true; +} + +bool +QuotaRequestBase::Init(Quota* aQuota) +{ + AssertIsOnOwningThread(); + MOZ_ASSERT(aQuota); + + mNeedsQuotaManagerInit = true; + + return true; +} + +void +QuotaRequestBase::SendResults() +{ + AssertIsOnOwningThread(); + + if (IsActorDestroyed()) { + if (NS_SUCCEEDED(mResultCode)) { + mResultCode = NS_ERROR_FAILURE; + } + } else { + RequestResponse response; + + if (NS_SUCCEEDED(mResultCode)) { + GetResponse(response); + } else { + response = mResultCode; + } + + Unused << PQuotaRequestParent::Send__delete__(this, response); + } +} + +void +QuotaRequestBase::ActorDestroy(ActorDestroyReason aWhy) +{ + AssertIsOnOwningThread(); + + NoteActorDestroyed(); } void @@ -4746,6 +5639,98 @@ ResetOrClearOp::DoDirectoryWork(QuotaManager* aQuotaManager) return NS_OK; } +void +ResetOrClearOp::GetResponse(RequestResponse& aResponse) +{ + AssertIsOnOwningThread(); + if (mClear) { + aResponse = ClearAllResponse(); + } else { + aResponse = ResetAllResponse(); + } +} + +OriginClearOp::OriginClearOp(const RequestParams& aParams) + : QuotaRequestBase(/* aExclusive */ true) + , mParams(aParams) + , mApp(aParams.type() == RequestParams::TClearAppParams) +{ + MOZ_ASSERT(aParams.type() == RequestParams::TClearOriginParams || + aParams.type() == RequestParams::TClearAppParams); +} + +bool +OriginClearOp::Init(Quota* aQuota) +{ + AssertIsOnOwningThread(); + MOZ_ASSERT(aQuota); + + if (NS_WARN_IF(!QuotaRequestBase::Init(aQuota))) { + return false; + } + + if (mApp) { + const ClearAppParams& params = mParams.get_ClearAppParams(); + + nsAutoCString pattern; + QuotaManager::GetOriginPatternStringMaybeIgnoreBrowser(params.appId(), + params.browserOnly(), + pattern); + + mOriginScope.SetFromPattern(pattern); + } else { + const ClearOriginParams& params = mParams.get_ClearOriginParams(); + + if (params.persistenceTypeIsExplicit()) { + MOZ_ASSERT(params.persistenceType() != PERSISTENCE_TYPE_INVALID); + + mPersistenceType.SetValue(params.persistenceType()); + } + + mNeedsMainThreadInit = true; + } + + return true; +} + +nsresult +OriginClearOp::DoInitOnMainThread() +{ + MOZ_ASSERT(NS_IsMainThread()); + MOZ_ASSERT(GetState() == State_Initializing); + MOZ_ASSERT(!mApp); + MOZ_ASSERT(mNeedsMainThreadInit); + + const ClearOriginParams& params = mParams.get_ClearOriginParams(); + + const PrincipalInfo& principalInfo = params.principalInfo(); + + nsresult rv; + nsCOMPtr principal = + PrincipalInfoToPrincipal(principalInfo, &rv); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + // Figure out which origin we're dealing with. + nsCString origin; + rv = QuotaManager::GetInfoFromPrincipal(principal, nullptr, &origin, nullptr); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + const mozilla::OriginAttributes& attrs = + mozilla::BasePrincipal::Cast(principal)->OriginAttributesRef(); + + nsAutoCString pattern; + QuotaManager::GetOriginPatternString(attrs.mAppId, attrs.mInBrowser, origin, + pattern); + + mOriginScope.SetFromPattern(pattern); + + return NS_OK; +} + void OriginClearOp::DeleteFiles(QuotaManager* aQuotaManager, PersistenceType aPersistenceType) @@ -4872,59 +5857,15 @@ OriginClearOp::DoDirectoryWork(QuotaManager* aQuotaManager) } void -FinalizeOriginEvictionOp::Dispatch() +OriginClearOp::GetResponse(RequestResponse& aResponse) { - MOZ_ASSERT(!NS_IsMainThread()); - MOZ_ASSERT(mState == State_Initial); + AssertIsOnOwningThread(); - mState = State_DirectoryOpenPending; - - MOZ_ALWAYS_TRUE(NS_SUCCEEDED(NS_DispatchToMainThread(this))); -} - -void -FinalizeOriginEvictionOp::RunOnIOThreadImmediately() -{ - AssertIsOnIOThread(); - MOZ_ASSERT(mState == State_Initial); - - mState = State_DirectoryWorkOpen; - - MOZ_ALWAYS_TRUE(NS_SUCCEEDED(this->Run())); -} - -nsresult -FinalizeOriginEvictionOp::Open() -{ - MOZ_CRASH("Shouldn't get here!"); -} - -nsresult -FinalizeOriginEvictionOp::DoDirectoryWork(QuotaManager* aQuotaManager) -{ - AssertIsOnIOThread(); - - PROFILER_LABEL("Quota", "FinalizeOriginEvictionOp::DoDirectoryWork", - js::ProfileEntry::Category::OTHER); - - for (RefPtr& lock : mLocks) { - aQuotaManager->OriginClearCompleted(lock->GetPersistenceType().Value(), - lock->GetOriginScope(), - lock->GetIsApp().Value()); + if (mApp) { + aResponse = ClearAppResponse(); + } else { + aResponse = ClearOriginResponse(); } - - return NS_OK; -} - -void -FinalizeOriginEvictionOp::UnblockOpen() -{ - MOZ_ASSERT(NS_IsMainThread()); - MOZ_ASSERT(mState == State_UnblockingOpen); - - mLocks.Clear(); - - AdvanceState(); } nsresult diff --git a/dom/quota/ActorsParent.h b/dom/quota/ActorsParent.h new file mode 100644 index 000000000000..06ed9d342709 --- /dev/null +++ b/dom/quota/ActorsParent.h @@ -0,0 +1,26 @@ +/* -*- 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_quota_ActorsParent_h +#define mozilla_dom_quota_ActorsParent_h + +namespace mozilla { +namespace dom { +namespace quota { + +class PQuotaParent; + +PQuotaParent* +AllocPQuotaParent(); + +bool +DeallocPQuotaParent(PQuotaParent* aActor); + +} // namespace quota +} // namespace dom +} // namespace mozilla + +#endif // mozilla_dom_quota_ActorsParent_h diff --git a/dom/quota/Client.h b/dom/quota/Client.h index 9c929dd1f57f..af7641b83bbd 100644 --- a/dom/quota/Client.h +++ b/dom/quota/Client.h @@ -111,7 +111,7 @@ public: virtual void ReleaseIOThreadObjects() = 0; - // Methods which are called on the main thred. + // Methods which are called on the background thred. virtual void AbortOperations(const nsACString& aOrigin) = 0; @@ -119,7 +119,10 @@ public: AbortOperationsForProcess(ContentParentId aContentParentId) = 0; virtual void - PerformIdleMaintenance() = 0; + StartIdleMaintenance() = 0; + + virtual void + StopIdleMaintenance() = 0; virtual void ShutdownWorkThreads() = 0; diff --git a/dom/quota/OriginScope.h b/dom/quota/OriginScope.h index 77e77599f80d..0a7d4780ff5c 100644 --- a/dom/quota/OriginScope.h +++ b/dom/quota/OriginScope.h @@ -63,6 +63,27 @@ public: return mType; } + void + SetFromOrigin(const nsACString& aOrigin) + { + Assign(aOrigin); + mType = eOrigin; + } + + void + SetFromPattern(const nsACString& aPattern) + { + Assign(aPattern); + mType = ePattern; + } + + void + SetFromNull() + { + SetIsVoid(true); + mType = eNull; + } + private: OriginScope(const nsACString& aString, Type aType) : nsCString(aString), mType(aType) @@ -71,7 +92,7 @@ private: bool operator==(const OriginScope& aOther) = delete; - const Type mType; + Type mType; }; END_QUOTA_NAMESPACE diff --git a/dom/quota/PQuota.ipdl b/dom/quota/PQuota.ipdl new file mode 100644 index 000000000000..de24adc900ee --- /dev/null +++ b/dom/quota/PQuota.ipdl @@ -0,0 +1,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 protocol PBackground; +include protocol PQuotaRequest; +include protocol PQuotaUsageRequest; + +include PBackgroundSharedTypes; + +include "mozilla/dom/quota/SerializationHelpers.h"; + +using mozilla::dom::quota::PersistenceType + from "mozilla/dom/quota/PersistenceType.h"; + +namespace mozilla { +namespace dom { +namespace quota { + +struct UsageParams +{ + PrincipalInfo principalInfo; +}; + +union UsageRequestParams +{ + UsageParams; +}; + +struct ClearOriginParams +{ + PrincipalInfo principalInfo; + PersistenceType persistenceType; + bool persistenceTypeIsExplicit; +}; + +struct ClearAppParams +{ + uint32_t appId; + bool browserOnly; +}; + +struct ClearAllParams +{ +}; + +struct ResetAllParams +{ +}; + +union RequestParams +{ + ClearOriginParams; + ClearAppParams; + ClearAllParams; + ResetAllParams; +}; + +protocol PQuota +{ + manager PBackground; + + manages PQuotaRequest; + manages PQuotaUsageRequest; + +parent: + __delete__(); + + PQuotaUsageRequest(UsageRequestParams params); + + PQuotaRequest(RequestParams params); + + StartIdleMaintenance(); + + StopIdleMaintenance(); +}; + +} // namespace quota +} // namespace dom +} // namespace mozilla diff --git a/dom/quota/PQuotaRequest.ipdl b/dom/quota/PQuotaRequest.ipdl new file mode 100644 index 000000000000..82776eb14445 --- /dev/null +++ b/dom/quota/PQuotaRequest.ipdl @@ -0,0 +1,46 @@ +/* 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 PQuota; + +namespace mozilla { +namespace dom { +namespace quota { + +struct ClearOriginResponse +{ +}; + +struct ClearAppResponse +{ +}; + +struct ClearAllResponse +{ +}; + +struct ResetAllResponse +{ +}; + +union RequestResponse +{ + nsresult; + ClearOriginResponse; + ClearAppResponse; + ClearAllResponse; + ResetAllResponse; +}; + +protocol PQuotaRequest +{ + manager PQuota; + +child: + __delete__(RequestResponse response); +}; + +} // namespace quota +} // namespace dom +} // namespace mozilla diff --git a/dom/quota/PQuotaUsageRequest.ipdl b/dom/quota/PQuotaUsageRequest.ipdl new file mode 100644 index 000000000000..530f65e328cb --- /dev/null +++ b/dom/quota/PQuotaUsageRequest.ipdl @@ -0,0 +1,36 @@ +/* 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 PQuota; + +namespace mozilla { +namespace dom { +namespace quota { + +struct UsageResponse +{ + uint64_t usage; + uint64_t fileUsage; +}; + +union UsageRequestResponse +{ + nsresult; + UsageResponse; +}; + +protocol PQuotaUsageRequest +{ + manager PQuota; + +parent: + Cancel(); + +child: + __delete__(UsageRequestResponse response); +}; + +} // namespace quota +} // namespace dom +} // namespace mozilla diff --git a/dom/quota/QuotaCommon.h b/dom/quota/QuotaCommon.h index 25184ab6af25..b82b62affee6 100644 --- a/dom/quota/QuotaCommon.h +++ b/dom/quota/QuotaCommon.h @@ -30,8 +30,33 @@ NS_WARNING(str.get()); \ } while (0) +class nsIEventTarget; + BEGIN_QUOTA_NAMESPACE +class BackgroundThreadObject +{ +protected: + nsCOMPtr mOwningThread; + +public: + void + AssertIsOnOwningThread() const +#ifdef DEBUG + ; +#else + { } +#endif + + nsIEventTarget* + OwningThread() const; + +protected: + BackgroundThreadObject(); + + explicit BackgroundThreadObject(nsIEventTarget* aOwningThread); +}; + void AssertIsOnIOThread(); diff --git a/dom/quota/QuotaManager.h b/dom/quota/QuotaManager.h index 094315019c80..4d19be1990a0 100644 --- a/dom/quota/QuotaManager.h +++ b/dom/quota/QuotaManager.h @@ -9,9 +9,6 @@ #include "QuotaCommon.h" -#include "nsIObserver.h" -#include "nsIQuotaManager.h" - #include "mozilla/dom/Nullable.h" #include "mozilla/dom/ipc/IdType.h" #include "mozilla/Mutex.h" @@ -24,6 +21,7 @@ #define QUOTA_MANAGER_CONTRACTID "@mozilla.org/dom/quota/manager;1" +class nsIEventTarget; class nsIPrincipal; class nsIThread; class nsITimer; @@ -31,12 +29,6 @@ class nsIURI; class nsPIDOMWindow; class nsIRunnable; -namespace mozilla { -namespace dom { -class OptionalContentId; -} // namespace dom -} // namespace mozilla - BEGIN_QUOTA_NAMESPACE class DirectoryLockImpl; @@ -56,10 +48,8 @@ public: Release() = 0; }; -// nsISupports is needed for nsMainThreadPtrHandle -// XXX RemoveMe once bug 1164581 gets fixed. class DirectoryLock - : public nsISupports + : public RefCountedObject { friend class DirectoryLockImpl; @@ -101,8 +91,8 @@ struct OriginParams bool mIsApp; }; -class QuotaManager final : public nsIQuotaManager, - public nsIObserver +class QuotaManager final + : public BackgroundThreadObject { friend class DirectoryLockImpl; friend class GroupInfo; @@ -120,24 +110,26 @@ class QuotaManager final : public nsIQuotaManager, nsTArray> DirectoryLockTable; public: - NS_DECL_ISUPPORTS - NS_DECL_NSIQUOTAMANAGER - NS_DECL_NSIOBSERVER + class CreateRunnable; + +private: + class ShutdownRunnable; + class ShutdownObserver; + +public: + NS_INLINE_DECL_REFCOUNTING(QuotaManager) + + static const bool kRunningXPCShellTests; static const char kReplaceChars[]; - // Returns a non-owning reference. - static QuotaManager* - GetOrCreate(); + static void + GetOrCreate(nsIRunnable* aCallback); // Returns a non-owning reference. static QuotaManager* Get(); - // Returns an owning reference! No one should call this but the factory. - static QuotaManager* - FactoryCreate(); - // Returns true if we've begun the shutdown process. static bool IsShuttingDown(); @@ -261,6 +253,26 @@ public: void ResetOrClearCompleted(); + void + StartIdleMaintenance() + { + AssertIsOnOwningThread(); + + for (auto& client : mClients) { + client->StartIdleMaintenance(); + } + } + + void + StopIdleMaintenance() + { + AssertIsOnOwningThread(); + + for (auto& client : mClients) { + client->StopIdleMaintenance(); + } + } + void AssertCurrentThreadOwnsQuotaMutex() { @@ -372,7 +384,10 @@ private: virtual ~QuotaManager(); nsresult - Init(); + Init(const nsAString& aBaseDirPath); + + void + Shutdown(); already_AddRefed CreateDirectoryLock(Nullable aPersistenceType, @@ -429,9 +444,6 @@ private: int64_t aAccessTime, nsIFile* aDirectory); - nsresult - ClearStoragesForApp(uint32_t aAppId, bool aBrowserOnly); - void CheckTemporaryStorageLimits(); @@ -461,6 +473,9 @@ private: const nsACString& aOrigin, nsAutoCString& _retval); + static void + ShutdownTimerCallback(nsITimer* aTimer, void* aClosure); + mozilla::Mutex mQuotaMutex; nsClassHashtable mGroupInfoPairs; diff --git a/dom/quota/QuotaManagerService.cpp b/dom/quota/QuotaManagerService.cpp new file mode 100644 index 000000000000..680d59c0f9b8 --- /dev/null +++ b/dom/quota/QuotaManagerService.cpp @@ -0,0 +1,823 @@ +/* -*- 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 "QuotaManagerService.h" + +#include "ActorsChild.h" +#include "mozIApplicationClearPrivateDataParams.h" +#include "mozilla/BasePrincipal.h" +#include "mozilla/ClearOnShutdown.h" +#include "mozilla/Hal.h" +#include "mozilla/Preferences.h" +#include "mozilla/unused.h" +#include "mozilla/ipc/BackgroundChild.h" +#include "mozilla/ipc/BackgroundParent.h" +#include "mozilla/ipc/BackgroundUtils.h" +#include "mozilla/ipc/PBackgroundChild.h" +#include "nsIIdleService.h" +#include "nsIIPCBackgroundChildCreateCallback.h" +#include "nsIObserverService.h" +#include "nsIScriptSecurityManager.h" +#include "nsXULAppAPI.h" +#include "QuotaManager.h" +#include "QuotaRequests.h" + +#define PROFILE_BEFORE_CHANGE_OBSERVER_ID "profile-before-change" + +namespace mozilla { +namespace dom { +namespace quota { + +using namespace mozilla::ipc; + +namespace { + +// Preference that is used to enable testing features. +const char kTestingPref[] = "dom.quotaManager.testing"; + +const char kIdleServiceContractId[] = "@mozilla.org/widget/idleservice;1"; + +// The number of seconds we will wait after receiving the idle-daily +// notification before beginning maintenance. +const uint32_t kIdleObserverTimeSec = 1; + +mozilla::StaticRefPtr gQuotaManagerService; + +mozilla::Atomic gInitialized(false); +mozilla::Atomic gClosed(false); +mozilla::Atomic gTestingMode(false); + +void +TestingPrefChangedCallback(const char* aPrefName, + void* aClosure) +{ + MOZ_ASSERT(NS_IsMainThread()); + MOZ_ASSERT(!strcmp(aPrefName, kTestingPref)); + MOZ_ASSERT(!aClosure); + + gTestingMode = Preferences::GetBool(aPrefName); +} + +class AbortOperationsRunnable final + : public nsRunnable +{ + ContentParentId mContentParentId; + +public: + explicit AbortOperationsRunnable(ContentParentId aContentParentId) + : mContentParentId(aContentParentId) + { } + +private: + NS_DECL_NSIRUNNABLE +}; + +} // namespace + +class QuotaManagerService::BackgroundCreateCallback final + : public nsIIPCBackgroundChildCreateCallback +{ + RefPtr mService; + +public: + explicit + BackgroundCreateCallback(QuotaManagerService* aService) + : mService(aService) + { + MOZ_ASSERT(aService); + } + + NS_DECL_ISUPPORTS + +private: + ~BackgroundCreateCallback() + { } + + NS_DECL_NSIIPCBACKGROUNDCHILDCREATECALLBACK +}; + +class QuotaManagerService::PendingRequestInfo +{ +protected: + RefPtr mRequest; + +public: + explicit PendingRequestInfo(RequestBase* aRequest) + : mRequest(aRequest) + { } + + virtual ~PendingRequestInfo() + { } + + RequestBase* + GetRequest() const + { + return mRequest; + } + + virtual nsresult + InitiateRequest(QuotaChild* aActor) = 0; +}; + +class QuotaManagerService::UsageRequestInfo + : public PendingRequestInfo +{ + UsageRequestParams mParams; + +public: + UsageRequestInfo(UsageRequest* aRequest, + const UsageRequestParams& aParams) + : PendingRequestInfo(aRequest) + , mParams(aParams) + { + MOZ_ASSERT(aRequest); + MOZ_ASSERT(aParams.type() != UsageRequestParams::T__None); + } + + virtual nsresult + InitiateRequest(QuotaChild* aActor) override; +}; + +class QuotaManagerService::RequestInfo + : public PendingRequestInfo +{ + RequestParams mParams; + +public: + RequestInfo(Request* aRequest, + const RequestParams& aParams) + : PendingRequestInfo(aRequest) + , mParams(aParams) + { + MOZ_ASSERT(aRequest); + MOZ_ASSERT(aParams.type() != RequestParams::T__None); + } + + virtual nsresult + InitiateRequest(QuotaChild* aActor) override; +}; + +class QuotaManagerService::IdleMaintenanceInfo + : public PendingRequestInfo +{ + const bool mStart; + +public: + explicit IdleMaintenanceInfo(bool aStart) + : PendingRequestInfo(nullptr) + , mStart(aStart) + { } + + virtual nsresult + InitiateRequest(QuotaChild* aActor) override; +}; + +QuotaManagerService::QuotaManagerService() + : mBackgroundActor(nullptr) + , mBackgroundActorFailed(false) + , mIdleObserverRegistered(false) +{ + MOZ_ASSERT(NS_IsMainThread()); +} + +QuotaManagerService::~QuotaManagerService() +{ + MOZ_ASSERT(NS_IsMainThread()); + MOZ_ASSERT(!mIdleObserverRegistered); +} + +// static +QuotaManagerService* +QuotaManagerService::GetOrCreate() +{ + MOZ_ASSERT(NS_IsMainThread()); + + if (gClosed) { + MOZ_ASSERT(false, "Calling GetOrCreate() after shutdown!"); + return nullptr; + } + + if (!gQuotaManagerService) { + RefPtr instance(new QuotaManagerService()); + + nsresult rv = instance->Init(); + if (NS_WARN_IF(NS_FAILED(rv))) { + return nullptr; + } + + if (gInitialized.exchange(true)) { + MOZ_ASSERT(false, "Initialized more than once?!"); + } + + gQuotaManagerService = instance; + + ClearOnShutdown(&gQuotaManagerService); + } + + return gQuotaManagerService; +} + +// static +QuotaManagerService* +QuotaManagerService::Get() +{ + // Does not return an owning reference. + return gQuotaManagerService; +} + +// static +QuotaManagerService* +QuotaManagerService::FactoryCreate() +{ + // Returns a raw pointer that carries an owning reference! Lame, but the + // singleton factory macros force this. + QuotaManagerService* quotaManagerService = GetOrCreate(); + NS_IF_ADDREF(quotaManagerService); + return quotaManagerService; +} + +void +QuotaManagerService::ClearBackgroundActor() +{ + MOZ_ASSERT(NS_IsMainThread()); + + mBackgroundActor = nullptr; +} + +void +QuotaManagerService::NoteLiveManager(QuotaManager* aManager) +{ + MOZ_ASSERT(XRE_IsParentProcess()); + MOZ_ASSERT(NS_IsMainThread()); + MOZ_ASSERT(aManager); + + mBackgroundThread = aManager->OwningThread(); +} + +void +QuotaManagerService::NoteFinishedManager() +{ + MOZ_ASSERT(XRE_IsParentProcess()); + MOZ_ASSERT(NS_IsMainThread()); + + mBackgroundThread = nullptr; +} + +void +QuotaManagerService::AbortOperationsForProcess(ContentParentId aContentParentId) +{ + MOZ_ASSERT(XRE_IsParentProcess()); + MOZ_ASSERT(NS_IsMainThread()); + + if (!mBackgroundThread) { + return; + } + + RefPtr runnable = + new AbortOperationsRunnable(aContentParentId); + + MOZ_ALWAYS_TRUE(NS_SUCCEEDED( + mBackgroundThread->Dispatch(runnable, NS_DISPATCH_NORMAL))); +} + +nsresult +QuotaManagerService::Init() +{ + MOZ_ASSERT(NS_IsMainThread()); + + if (XRE_IsParentProcess()) { + nsCOMPtr observerService = + mozilla::services::GetObserverService(); + if (NS_WARN_IF(!observerService)) { + return NS_ERROR_FAILURE; + } + + nsresult rv = observerService->AddObserver(this, + PROFILE_BEFORE_CHANGE_OBSERVER_ID, + false); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + } + + Preferences::RegisterCallbackAndCall(TestingPrefChangedCallback, + kTestingPref); + + return NS_OK; +} + +void +QuotaManagerService::Destroy() +{ + // Setting the closed flag prevents the service from being recreated. + // Don't set it though if there's no real instance created. + if (gInitialized && gClosed.exchange(true)) { + MOZ_ASSERT(false, "Shutdown more than once?!"); + } + + Preferences::UnregisterCallback(TestingPrefChangedCallback, kTestingPref); + + delete this; +} + +nsresult +QuotaManagerService::InitiateRequest(nsAutoPtr& aInfo) +{ + // Nothing can be done here if we have previously failed to create a + // background actor. + if (mBackgroundActorFailed) { + return NS_ERROR_FAILURE; + } + + if (!mBackgroundActor && mPendingRequests.IsEmpty()) { + // We need to start the sequence to create a background actor for this + // thread. + + RefPtr cb = new BackgroundCreateCallback(this); + if (NS_WARN_IF(!BackgroundChild::GetOrCreateForCurrentThread(cb))) { + return NS_ERROR_FAILURE; + } + } + + // If we already have a background actor then we can start this request now. + if (mBackgroundActor) { + nsresult rv = aInfo->InitiateRequest(mBackgroundActor); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + } else { + mPendingRequests.AppendElement(aInfo.forget()); + } + + return NS_OK; +} + +nsresult +QuotaManagerService::BackgroundActorCreated(PBackgroundChild* aBackgroundActor) +{ + MOZ_ASSERT(NS_IsMainThread()); + MOZ_ASSERT(aBackgroundActor); + MOZ_ASSERT(!mBackgroundActor); + MOZ_ASSERT(!mBackgroundActorFailed); + + { + QuotaChild* actor = new QuotaChild(this); + + mBackgroundActor = + static_cast(aBackgroundActor->SendPQuotaConstructor(actor)); + } + + if (NS_WARN_IF(!mBackgroundActor)) { + BackgroundActorFailed(); + return NS_ERROR_FAILURE; + } + + nsresult rv = NS_OK; + + for (uint32_t index = 0, count = mPendingRequests.Length(); + index < count; + index++) { + nsAutoPtr info(mPendingRequests[index].forget()); + + nsresult rv2 = info->InitiateRequest(mBackgroundActor); + + // Warn for every failure, but just return the first failure if there are + // multiple failures. + if (NS_WARN_IF(NS_FAILED(rv2)) && NS_SUCCEEDED(rv)) { + rv = rv2; + } + } + + mPendingRequests.Clear(); + + return rv; +} + +void +QuotaManagerService::BackgroundActorFailed() +{ + MOZ_ASSERT(NS_IsMainThread()); + MOZ_ASSERT(!mPendingRequests.IsEmpty()); + MOZ_ASSERT(!mBackgroundActor); + MOZ_ASSERT(!mBackgroundActorFailed); + + mBackgroundActorFailed = true; + + for (uint32_t index = 0, count = mPendingRequests.Length(); + index < count; + index++) { + nsAutoPtr info(mPendingRequests[index].forget()); + + RequestBase* request = info->GetRequest(); + if (request) { + request->SetError(NS_ERROR_FAILURE); + } + } + + mPendingRequests.Clear(); +} + +void +QuotaManagerService::PerformIdleMaintenance() +{ + using namespace mozilla::hal; + + MOZ_ASSERT(XRE_IsParentProcess()); + MOZ_ASSERT(NS_IsMainThread()); + + // If we're running on battery power then skip all idle maintenance since we + // would otherwise be doing lots of disk I/O. + BatteryInformation batteryInfo; + +#ifdef MOZ_WIDGET_ANDROID + // Android XPCShell doesn't load the AndroidBridge that is needed to make + // GetCurrentBatteryInformation work... + if (!QuotaManager::kRunningXPCShellTests) +#endif + { + GetCurrentBatteryInformation(&batteryInfo); + } + + // If we're running XPCShell because we always want to be able to test this + // code so pretend that we're always charging. + if (QuotaManager::kRunningXPCShellTests) { + batteryInfo.level() = 100; + batteryInfo.charging() = true; + } + + if (NS_WARN_IF(!batteryInfo.charging())) { + return; + } + + if (QuotaManager::kRunningXPCShellTests) { + // We don't want user activity to impact this code if we're running tests. + Unused << Observe(nullptr, OBSERVER_TOPIC_IDLE, nullptr); + } else if (!mIdleObserverRegistered) { + nsCOMPtr idleService = + do_GetService(kIdleServiceContractId); + MOZ_ASSERT(idleService); + + MOZ_ALWAYS_TRUE(NS_SUCCEEDED( + idleService->AddIdleObserver(this, kIdleObserverTimeSec))); + + mIdleObserverRegistered = true; + } +} + +void +QuotaManagerService::RemoveIdleObserver() +{ + MOZ_ASSERT(XRE_IsParentProcess()); + MOZ_ASSERT(NS_IsMainThread()); + + if (mIdleObserverRegistered) { + nsCOMPtr idleService = + do_GetService(kIdleServiceContractId); + MOZ_ASSERT(idleService); + + MOZ_ALWAYS_TRUE(NS_SUCCEEDED( + idleService->RemoveIdleObserver(this, kIdleObserverTimeSec))); + + mIdleObserverRegistered = false; + } +} + +NS_IMPL_ADDREF(QuotaManagerService) +NS_IMPL_RELEASE_WITH_DESTROY(QuotaManagerService, Destroy()) +NS_IMPL_QUERY_INTERFACE(QuotaManagerService, + nsIQuotaManagerService, + nsIObserver) + +NS_IMETHODIMP +QuotaManagerService::GetUsageForPrincipal(nsIPrincipal* aPrincipal, + nsIQuotaUsageCallback* aCallback, + nsIQuotaUsageRequest** _retval) +{ + MOZ_ASSERT(NS_IsMainThread()); + MOZ_ASSERT(aPrincipal); + MOZ_ASSERT(aCallback); + MOZ_ASSERT(nsContentUtils::IsCallerChrome()); + + RefPtr request = new UsageRequest(aPrincipal, aCallback); + + UsageParams params; + + PrincipalInfo& principalInfo = params.principalInfo(); + + nsresult rv = PrincipalToPrincipalInfo(aPrincipal, &principalInfo); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + if (principalInfo.type() != PrincipalInfo::TContentPrincipalInfo && + principalInfo.type() != PrincipalInfo::TSystemPrincipalInfo) { + return NS_ERROR_UNEXPECTED; + } + + nsAutoPtr info(new UsageRequestInfo(request, params)); + + rv = InitiateRequest(info); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + request.forget(_retval); + return NS_OK; +} + +NS_IMETHODIMP +QuotaManagerService::Clear(nsIQuotaRequest** _retval) +{ + MOZ_ASSERT(NS_IsMainThread()); + MOZ_ASSERT(nsContentUtils::IsCallerChrome()); + + if (NS_WARN_IF(!gTestingMode)) { + return NS_ERROR_UNEXPECTED; + } + + RefPtr request = new Request(); + + ClearAllParams params; + + nsAutoPtr info(new RequestInfo(request, params)); + + nsresult rv = InitiateRequest(info); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + request.forget(_retval); + return NS_OK; +} + +NS_IMETHODIMP +QuotaManagerService::ClearStoragesForPrincipal(nsIPrincipal* aPrincipal, + const nsACString& aPersistenceType, + nsIQuotaRequest** _retval) +{ + MOZ_ASSERT(NS_IsMainThread()); + MOZ_ASSERT(aPrincipal); + MOZ_ASSERT(nsContentUtils::IsCallerChrome()); + + RefPtr request = new Request(aPrincipal); + + ClearOriginParams params; + + PrincipalInfo& principalInfo = params.principalInfo(); + + nsresult rv = PrincipalToPrincipalInfo(aPrincipal, &principalInfo); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + if (principalInfo.type() != PrincipalInfo::TContentPrincipalInfo && + principalInfo.type() != PrincipalInfo::TSystemPrincipalInfo) { + return NS_ERROR_UNEXPECTED; + } + + Nullable persistenceType; + rv = NullablePersistenceTypeFromText(aPersistenceType, &persistenceType); + if (NS_WARN_IF(NS_FAILED(rv))) { + return NS_ERROR_INVALID_ARG; + } + + if (persistenceType.IsNull()) { + params.persistenceTypeIsExplicit() = false; + } else { + params.persistenceType() = persistenceType.Value(); + params.persistenceTypeIsExplicit() = true; + } + + nsAutoPtr info(new RequestInfo(request, params)); + + rv = InitiateRequest(info); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + request.forget(_retval); + return NS_OK; +} + +NS_IMETHODIMP +QuotaManagerService::Reset(nsIQuotaRequest** _retval) +{ + MOZ_ASSERT(NS_IsMainThread()); + MOZ_ASSERT(nsContentUtils::IsCallerChrome()); + + if (NS_WARN_IF(!gTestingMode)) { + return NS_ERROR_UNEXPECTED; + } + + RefPtr request = new Request(); + + ResetAllParams params; + + nsAutoPtr info(new RequestInfo(request, params)); + + nsresult rv = InitiateRequest(info); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + request.forget(_retval); + return NS_OK; +} + +NS_IMETHODIMP +QuotaManagerService::Observe(nsISupports* aSubject, + const char* aTopic, + const char16_t* aData) +{ + MOZ_ASSERT(XRE_IsParentProcess()); + MOZ_ASSERT(NS_IsMainThread()); + + if (!strcmp(aTopic, PROFILE_BEFORE_CHANGE_OBSERVER_ID)) { + RemoveIdleObserver(); + return NS_OK; + } + + if (!strcmp(aTopic, TOPIC_WEB_APP_CLEAR_DATA)) { + nsCOMPtr params = + do_QueryInterface(aSubject); + if (NS_WARN_IF(!params)) { + return NS_ERROR_UNEXPECTED; + } + + uint32_t appId; + nsresult rv = params->GetAppId(&appId); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + bool browserOnly; + rv = params->GetBrowserOnly(&browserOnly); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + RefPtr request = new Request(); + + ClearAppParams requestParams; + requestParams.appId() = appId; + requestParams.browserOnly() = browserOnly; + + nsAutoPtr info(new RequestInfo(request, requestParams)); + + rv = InitiateRequest(info); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + return NS_OK; + } + + if (!strcmp(aTopic, OBSERVER_TOPIC_IDLE_DAILY)) { + PerformIdleMaintenance(); + return NS_OK; + } + + if (!strcmp(aTopic, OBSERVER_TOPIC_IDLE)) { + nsAutoPtr info( + new IdleMaintenanceInfo(/* aStart */ true)); + + nsresult rv = InitiateRequest(info); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + return NS_OK; + } + + if (!strcmp(aTopic, OBSERVER_TOPIC_ACTIVE)) { + RemoveIdleObserver(); + + nsAutoPtr info( + new IdleMaintenanceInfo(/* aStart */ false)); + + nsresult rv = InitiateRequest(info); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + return NS_OK; + } + + MOZ_ASSERT_UNREACHABLE("Should never get here!"); + return NS_OK; +} + +NS_IMETHODIMP +AbortOperationsRunnable::Run() +{ + AssertIsOnBackgroundThread(); + + if (QuotaManager::IsShuttingDown()) { + return NS_OK; + } + + QuotaManager* quotaManager = QuotaManager::Get(); + if (!quotaManager) { + return NS_OK; + } + + quotaManager->AbortOperationsForProcess(mContentParentId); + + return NS_OK; +} + +NS_IMPL_ISUPPORTS(QuotaManagerService::BackgroundCreateCallback, + nsIIPCBackgroundChildCreateCallback) + +void +QuotaManagerService:: +BackgroundCreateCallback::ActorCreated(PBackgroundChild* aActor) +{ + MOZ_ASSERT(NS_IsMainThread()); + MOZ_ASSERT(aActor); + MOZ_ASSERT(mService); + + RefPtr service; + mService.swap(service); + + service->BackgroundActorCreated(aActor); +} + +void +QuotaManagerService:: +BackgroundCreateCallback::ActorFailed() +{ + MOZ_ASSERT(NS_IsMainThread()); + MOZ_ASSERT(mService); + + RefPtr service; + mService.swap(service); + + service->BackgroundActorFailed(); +} + +nsresult +QuotaManagerService:: +UsageRequestInfo::InitiateRequest(QuotaChild* aActor) +{ + MOZ_ASSERT(aActor); + + auto request = static_cast(mRequest.get()); + + auto actor = new QuotaUsageRequestChild(request); + + if (!aActor->SendPQuotaUsageRequestConstructor(actor, mParams)) { + request->SetError(NS_ERROR_FAILURE); + return NS_ERROR_FAILURE; + } + + request->SetBackgroundActor(actor); + + return NS_OK; +} + +nsresult +QuotaManagerService:: +RequestInfo::InitiateRequest(QuotaChild* aActor) +{ + MOZ_ASSERT(aActor); + + auto request = static_cast(mRequest.get()); + + auto actor = new QuotaRequestChild(request); + + if (!aActor->SendPQuotaRequestConstructor(actor, mParams)) { + request->SetError(NS_ERROR_FAILURE); + return NS_ERROR_FAILURE; + } + + return NS_OK; +} + +nsresult +QuotaManagerService:: +IdleMaintenanceInfo::InitiateRequest(QuotaChild* aActor) +{ + MOZ_ASSERT(aActor); + + bool result; + + if (mStart) { + result = aActor->SendStartIdleMaintenance(); + } else { + result = aActor->SendStopIdleMaintenance(); + } + + if (!result) { + return NS_ERROR_FAILURE; + } + + return NS_OK; +} + +} // namespace quota +} // namespace dom +} // namespace mozilla diff --git a/dom/quota/QuotaManagerService.h b/dom/quota/QuotaManagerService.h new file mode 100644 index 000000000000..2b02183df103 --- /dev/null +++ b/dom/quota/QuotaManagerService.h @@ -0,0 +1,112 @@ +/* -*- 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_quota_QuotaManagerService_h +#define mozilla_dom_quota_QuotaManagerService_h + +#include "mozilla/dom/ipc/IdType.h" +#include "nsIObserver.h" +#include "nsIQuotaManagerService.h" + +#define QUOTAMANAGER_SERVICE_CONTRACTID \ + "@mozilla.org/dom/quota-manager-service;1" + +namespace mozilla { +namespace ipc { + +class PBackgroundChild; + +} // namespace ipc + +namespace dom { +namespace quota { + +class QuotaChild; +class QuotaManager; + +class QuotaManagerService final + : public nsIQuotaManagerService + , public nsIObserver +{ + typedef mozilla::ipc::PBackgroundChild PBackgroundChild; + + class BackgroundCreateCallback; + class PendingRequestInfo; + class UsageRequestInfo; + class RequestInfo; + class IdleMaintenanceInfo; + + nsCOMPtr mBackgroundThread; + + nsTArray> mPendingRequests; + + QuotaChild* mBackgroundActor; + + bool mBackgroundActorFailed; + bool mIdleObserverRegistered; + +public: + // Returns a non-owning reference. + static QuotaManagerService* + GetOrCreate(); + + // Returns a non-owning reference. + static QuotaManagerService* + Get(); + + // Returns an owning reference! No one should call this but the factory. + static QuotaManagerService* + FactoryCreate(); + + void + ClearBackgroundActor(); + + void + NoteLiveManager(QuotaManager* aManager); + + void + NoteFinishedManager(); + + // Called when a process is being shot down. Aborts any running operations + // for the given process. + void + AbortOperationsForProcess(ContentParentId aContentParentId); + +private: + QuotaManagerService(); + ~QuotaManagerService(); + + nsresult + Init(); + + void + Destroy(); + + nsresult + InitiateRequest(nsAutoPtr& aInfo); + + nsresult + BackgroundActorCreated(PBackgroundChild* aBackgroundActor); + + void + BackgroundActorFailed(); + + void + PerformIdleMaintenance(); + + void + RemoveIdleObserver(); + + NS_DECL_ISUPPORTS + NS_DECL_NSIQUOTAMANAGERSERVICE + NS_DECL_NSIOBSERVER +}; + +} // namespace quota +} // namespace dom +} // namespace mozilla + +#endif /* mozilla_dom_quota_QuotaManagerService_h */ diff --git a/dom/quota/QuotaRequests.cpp b/dom/quota/QuotaRequests.cpp new file mode 100644 index 000000000000..776d541b058f --- /dev/null +++ b/dom/quota/QuotaRequests.cpp @@ -0,0 +1,296 @@ +/* -*- 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 "QuotaRequests.h" + +#include "ActorsChild.h" +#include "nsIQuotaCallbacks.h" + +namespace mozilla { +namespace dom { +namespace quota { + +RequestBase::RequestBase() + : mResultCode(NS_OK) + , mHaveResultOrErrorCode(false) +{ +#ifdef DEBUG + mOwningThread = PR_GetCurrentThread(); +#endif + AssertIsOnOwningThread(); +} + +RequestBase::RequestBase(nsIPrincipal* aPrincipal) + : mPrincipal(aPrincipal) + , mResultCode(NS_OK) + , mHaveResultOrErrorCode(false) +{ +#ifdef DEBUG + mOwningThread = PR_GetCurrentThread(); +#endif + AssertIsOnOwningThread(); +} + +#ifdef DEBUG + +void +RequestBase::AssertIsOnOwningThread() const +{ + MOZ_ASSERT(mOwningThread); + MOZ_ASSERT(PR_GetCurrentThread() == mOwningThread); +} + +#endif // DEBUG + +void +RequestBase::SetError(nsresult aRv) +{ + AssertIsOnOwningThread(); + MOZ_ASSERT(mResultCode == NS_OK); + MOZ_ASSERT(!mHaveResultOrErrorCode); + + mResultCode = aRv; + mHaveResultOrErrorCode = true; + + FireCallback(); +} + +NS_IMPL_CYCLE_COLLECTION_0(RequestBase) + +NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(RequestBase) + NS_INTERFACE_MAP_ENTRY(nsISupports) +NS_INTERFACE_MAP_END + +NS_IMPL_CYCLE_COLLECTING_ADDREF(RequestBase) +NS_IMPL_CYCLE_COLLECTING_RELEASE(RequestBase) + +NS_IMETHODIMP +RequestBase::GetPrincipal(nsIPrincipal** aPrincipal) +{ + AssertIsOnOwningThread(); + MOZ_ASSERT(aPrincipal); + + NS_IF_ADDREF(*aPrincipal = mPrincipal); + return NS_OK; +} + +NS_IMETHODIMP +RequestBase::GetResultCode(nsresult* aResultCode) +{ + AssertIsOnOwningThread(); + MOZ_ASSERT(aResultCode); + + if (!mHaveResultOrErrorCode) { + return NS_ERROR_FAILURE; + } + + *aResultCode = mResultCode; + return NS_OK; +} + +UsageRequest::UsageRequest(nsIPrincipal* aPrincipal, + nsIQuotaUsageCallback* aCallback) + : RequestBase(aPrincipal) + , mCallback(aCallback) + , mUsage(0) + , mFileUsage(0) + , mBackgroundActor(nullptr) + , mCanceled(false) +{ + AssertIsOnOwningThread(); + MOZ_ASSERT(aPrincipal); + MOZ_ASSERT(aCallback); +} + +UsageRequest::~UsageRequest() +{ + AssertIsOnOwningThread(); +} + +void +UsageRequest::SetBackgroundActor(QuotaUsageRequestChild* aBackgroundActor) +{ + AssertIsOnOwningThread(); + MOZ_ASSERT(aBackgroundActor); + MOZ_ASSERT(!mBackgroundActor); + + mBackgroundActor = aBackgroundActor; + + if (mCanceled) { + mBackgroundActor->SendCancel(); + } +} + +void +UsageRequest::SetResult(uint64_t aUsage, uint64_t aFileUsage) +{ + AssertIsOnOwningThread(); + MOZ_ASSERT(!mHaveResultOrErrorCode); + + mUsage = aUsage; + mFileUsage = aFileUsage; + mHaveResultOrErrorCode = true; + + FireCallback(); +} + +NS_IMPL_CYCLE_COLLECTION_INHERITED(UsageRequest, RequestBase, mCallback) + +NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(UsageRequest) + NS_INTERFACE_MAP_ENTRY(nsIQuotaUsageRequest) +NS_INTERFACE_MAP_END_INHERITING(RequestBase) + +NS_IMPL_ADDREF_INHERITED(UsageRequest, RequestBase) +NS_IMPL_RELEASE_INHERITED(UsageRequest, RequestBase) + +NS_IMETHODIMP +UsageRequest::GetUsage(uint64_t* aUsage) +{ + AssertIsOnOwningThread(); + + if (!mHaveResultOrErrorCode) { + return NS_ERROR_FAILURE; + } + + *aUsage = mUsage; + return NS_OK; +} + +NS_IMETHODIMP +UsageRequest::GetFileUsage(uint64_t* aFileUsage) +{ + AssertIsOnOwningThread(); + MOZ_ASSERT(aFileUsage); + + if (!mHaveResultOrErrorCode) { + return NS_ERROR_FAILURE; + } + + *aFileUsage = mFileUsage; + return NS_OK; +} + +NS_IMETHODIMP +UsageRequest::GetCallback(nsIQuotaUsageCallback** aCallback) +{ + AssertIsOnOwningThread(); + MOZ_ASSERT(aCallback); + + NS_IF_ADDREF(*aCallback = mCallback); + return NS_OK; +} + +NS_IMETHODIMP +UsageRequest::SetCallback(nsIQuotaUsageCallback* aCallback) +{ + AssertIsOnOwningThread(); + + mCallback = aCallback; + return NS_OK; +} + +NS_IMETHODIMP +UsageRequest::Cancel() +{ + AssertIsOnOwningThread(); + + if (mCanceled) { + NS_WARNING("Canceled more than once?!"); + return NS_ERROR_UNEXPECTED; + } + + if (mBackgroundActor) { + mBackgroundActor->SendCancel(); + } + + mCanceled = true; + + return NS_OK; +} + +void +UsageRequest::FireCallback() +{ + AssertIsOnOwningThread(); + MOZ_ASSERT(mCallback); + + mCallback->OnUsageResult(this); + + // Clean up. + mCallback = nullptr; +} + +Request::Request() +{ + AssertIsOnOwningThread(); +} + +Request::Request(nsIPrincipal* aPrincipal) + : RequestBase(aPrincipal) +{ + AssertIsOnOwningThread(); +} + +Request::~Request() +{ + AssertIsOnOwningThread(); +} + +void +Request::SetResult() +{ + AssertIsOnOwningThread(); + MOZ_ASSERT(!mHaveResultOrErrorCode); + + mHaveResultOrErrorCode = true; + + FireCallback(); +} + +NS_IMPL_CYCLE_COLLECTION_INHERITED(Request, RequestBase, mCallback) + +NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(Request) + NS_INTERFACE_MAP_ENTRY(nsIQuotaRequest) +NS_INTERFACE_MAP_END_INHERITING(RequestBase) + +NS_IMPL_ADDREF_INHERITED(mozilla::dom::quota::Request, RequestBase) +NS_IMPL_RELEASE_INHERITED(mozilla::dom::quota::Request, RequestBase) + +NS_IMETHODIMP +Request::GetCallback(nsIQuotaCallback** aCallback) +{ + AssertIsOnOwningThread(); + MOZ_ASSERT(aCallback); + + NS_IF_ADDREF(*aCallback = mCallback); + return NS_OK; +} + +NS_IMETHODIMP +Request::SetCallback(nsIQuotaCallback* aCallback) +{ + AssertIsOnOwningThread(); + + mCallback = aCallback; + return NS_OK; +} + +void +Request::FireCallback() +{ + AssertIsOnOwningThread(); + + if (mCallback) { + mCallback->OnComplete(this); + + // Clean up. + mCallback = nullptr; + } +} + +} // namespace quota +} // namespace dom +} // namespace mozilla diff --git a/dom/quota/QuotaRequests.h b/dom/quota/QuotaRequests.h new file mode 100644 index 000000000000..aeeca73f4d35 --- /dev/null +++ b/dom/quota/QuotaRequests.h @@ -0,0 +1,141 @@ +/* -*- 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_quota_UsageRequest_h +#define mozilla_dom_quota_UsageRequest_h + +#include "nsCOMPtr.h" +#include "nsCycleCollectionParticipant.h" +#include "nsIQuotaRequests.h" + +class nsIPrincipal; +class nsIQuotaCallback; +class nsIQuotaUsageCallback; +struct PRThread; + +namespace mozilla { +namespace dom { +namespace quota { + +class QuotaUsageRequestChild; + +class RequestBase + : public nsIQuotaRequestBase +{ +protected: +#ifdef DEBUG + PRThread* mOwningThread; +#endif + + nsCOMPtr mPrincipal; + + nsresult mResultCode; + bool mHaveResultOrErrorCode; + +public: + void + AssertIsOnOwningThread() const +#ifdef DEBUG + ; +#else + { } +#endif + + void + SetError(nsresult aRv); + + NS_DECL_CYCLE_COLLECTING_ISUPPORTS + NS_DECL_NSIQUOTAREQUESTBASE + NS_DECL_CYCLE_COLLECTION_CLASS(RequestBase) + +protected: + RequestBase(); + + RequestBase(nsIPrincipal* aPrincipal); + + virtual ~RequestBase() + { + AssertIsOnOwningThread(); + } + + virtual void + FireCallback() = 0; +}; + +class UsageRequest final + : public RequestBase + , public nsIQuotaUsageRequest +{ + nsCOMPtr mCallback; + + uint64_t mUsage; + uint64_t mFileUsage; + + QuotaUsageRequestChild* mBackgroundActor; + + bool mCanceled; + +public: + UsageRequest(nsIPrincipal* aPrincipal, + nsIQuotaUsageCallback* aCallback); + + void + SetBackgroundActor(QuotaUsageRequestChild* aBackgroundActor); + + void + ClearBackgroundActor() + { + AssertIsOnOwningThread(); + + mBackgroundActor = nullptr; + } + + void + SetResult(uint64_t aUsage, uint64_t aFileUsage); + + NS_DECL_ISUPPORTS_INHERITED + NS_FORWARD_NSIQUOTAREQUESTBASE(RequestBase::) + NS_DECL_NSIQUOTAUSAGEREQUEST + NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(UsageRequest, RequestBase) + +private: + ~UsageRequest(); + + virtual void + FireCallback() override; +}; + +class Request final + : public RequestBase + , public nsIQuotaRequest +{ + nsCOMPtr mCallback; + +public: + Request(); + + explicit Request(nsIPrincipal* aPrincipal); + + void + SetResult(); + + NS_DECL_ISUPPORTS_INHERITED + NS_FORWARD_NSIQUOTAREQUESTBASE(RequestBase::) + NS_DECL_NSIQUOTAREQUEST + NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(Request, RequestBase) + +private: + ~Request(); + + virtual void + FireCallback() override; +}; + +} // namespace quota +} // namespace dom +} // namespace mozilla + +#endif // mozilla_dom_quota_UsageRequest_h diff --git a/dom/quota/moz.build b/dom/quota/moz.build index 359dfe445811..ff932a9ca431 100644 --- a/dom/quota/moz.build +++ b/dom/quota/moz.build @@ -5,27 +5,38 @@ # file, You can obtain one at http://mozilla.org/MPL/2.0/. XPIDL_SOURCES += [ - 'nsIQuotaManager.idl', - 'nsIQuotaRequest.idl', - 'nsIUsageCallback.idl', + 'nsIQuotaCallbacks.idl', + 'nsIQuotaManagerService.idl', + 'nsIQuotaRequests.idl', ] XPIDL_MODULE = 'dom_quota' EXPORTS.mozilla.dom.quota += [ + 'ActorsParent.h', 'Client.h', 'FileStreams.h', 'PersistenceType.h', 'QuotaCommon.h', 'QuotaManager.h', + 'QuotaManagerService.h', 'QuotaObject.h', 'SerializationHelpers.h', 'UsageInfo.h', ] UNIFIED_SOURCES += [ + 'ActorsChild.cpp', + 'ActorsParent.cpp', 'FileStreams.cpp', - 'QuotaManager.cpp', + 'QuotaManagerService.cpp', + 'QuotaRequests.cpp', +] + +IPDL_SOURCES += [ + 'PQuota.ipdl', + 'PQuotaRequest.ipdl', + 'PQuotaUsageRequest.ipdl', ] include('/ipc/chromium/chromium-config.mozbuild') diff --git a/dom/quota/nsIQuotaCallbacks.idl b/dom/quota/nsIQuotaCallbacks.idl new file mode 100644 index 000000000000..7c53db20c090 --- /dev/null +++ b/dom/quota/nsIQuotaCallbacks.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 nsIQuotaRequest; +interface nsIQuotaUsageRequest; + +[scriptable, function, uuid(c8a21a2a-17b9-4b63-ad95-e0fbcff5de18)] +interface nsIQuotaUsageCallback : nsISupports +{ + void onUsageResult(in nsIQuotaUsageRequest aRequest); +}; + +[scriptable, function, uuid(a08a28e2-5a74-4c84-8070-ed45a07eb013)] +interface nsIQuotaCallback : nsISupports +{ + void onComplete(in nsIQuotaRequest aRequest); +}; diff --git a/dom/quota/nsIQuotaManager.idl b/dom/quota/nsIQuotaManagerService.idl similarity index 86% rename from dom/quota/nsIQuotaManager.idl rename to dom/quota/nsIQuotaManagerService.idl index 135ec86cf5e0..763e25a89e8e 100644 --- a/dom/quota/nsIQuotaManager.idl +++ b/dom/quota/nsIQuotaManagerService.idl @@ -6,12 +6,13 @@ #include "nsISupports.idl" -interface nsIQuotaRequest; interface nsIPrincipal; -interface nsIUsageCallback; +interface nsIQuotaRequest; +interface nsIQuotaUsageCallback; +interface nsIQuotaUsageRequest; -[scriptable, builtinclass, uuid(101cf53c-e7f3-4723-9f43-a23a85c8eda0)] -interface nsIQuotaManager : nsISupports +[scriptable, builtinclass, uuid(1b3d0a38-8151-4cf9-89fa-4f92c2ef0e7e)] +interface nsIQuotaManagerService : nsISupports { /** * Schedules an asynchronous callback that will return the total amount of @@ -22,9 +23,9 @@ interface nsIQuotaManager : nsISupports * @param aCallback * The callback that will be called when the usage is available. */ - nsIQuotaRequest + nsIQuotaUsageRequest getUsageForPrincipal(in nsIPrincipal aPrincipal, - in nsIUsageCallback aCallback); + in nsIQuotaUsageCallback aCallback); /** * Removes all storages. The files may not be deleted immediately depending @@ -34,7 +35,7 @@ interface nsIQuotaManager : nsISupports * If the dom.quotaManager.testing preference is not true the call will be * a no-op. */ - void + nsIQuotaRequest clear(); /** @@ -44,7 +45,7 @@ interface nsIQuotaManager : nsISupports * @param aPrincipal * A principal for the origin whose storages are to be cleared. */ - void + nsIQuotaRequest clearStoragesForPrincipal(in nsIPrincipal aPrincipal, [optional] in ACString aPersistenceType); @@ -57,6 +58,6 @@ interface nsIQuotaManager : nsISupports * If the dom.quotaManager.testing preference is not true the call will be * a no-op. */ - void + nsIQuotaRequest reset(); }; diff --git a/dom/quota/nsIQuotaRequest.idl b/dom/quota/nsIQuotaRequest.idl deleted file mode 100644 index 8cf2fdc78adb..000000000000 --- a/dom/quota/nsIQuotaRequest.idl +++ /dev/null @@ -1,14 +0,0 @@ -/* -*- 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, function, uuid(d96769ed-63ac-4070-ac5a-4b0e1728618a)] -interface nsIQuotaRequest : nsISupports -{ - void - cancel(); -}; diff --git a/dom/quota/nsIQuotaRequests.idl b/dom/quota/nsIQuotaRequests.idl new file mode 100644 index 000000000000..7252045672ff --- /dev/null +++ b/dom/quota/nsIQuotaRequests.idl @@ -0,0 +1,38 @@ +/* -*- 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 nsIQuotaCallback; +interface nsIQuotaUsageCallback; + +[scriptable, uuid(9af54222-0407-48fd-a4ab-9457c986fc49)] +interface nsIQuotaRequestBase : nsISupports +{ + readonly attribute nsIPrincipal principal; + + readonly attribute nsresult resultCode; +}; + +[scriptable, uuid(166e28e6-cf6d-4927-a6d7-b51bca9d3469)] +interface nsIQuotaUsageRequest : nsIQuotaRequestBase +{ + readonly attribute unsigned long long usage; + + readonly attribute unsigned long long fileUsage; + + attribute nsIQuotaUsageCallback callback; + + void + cancel(); +}; + +[scriptable, uuid(22890e3e-ff25-4372-9684-d901060e2f6c)] +interface nsIQuotaRequest : nsIQuotaRequestBase +{ + attribute nsIQuotaCallback callback; +}; diff --git a/dom/quota/nsIUsageCallback.idl b/dom/quota/nsIUsageCallback.idl deleted file mode 100644 index 31449301126e..000000000000 --- a/dom/quota/nsIUsageCallback.idl +++ /dev/null @@ -1,17 +0,0 @@ -/* -*- 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; - -[scriptable, function, uuid(54b9f44f-533f-41ee-8fa8-86cc978125f0)] -interface nsIUsageCallback : nsISupports -{ - void onUsageResult(in nsIPrincipal aPrincipal, - in unsigned long long aUsage, - in unsigned long long aFileUsage); -}; diff --git a/ipc/glue/BackgroundChildImpl.cpp b/ipc/glue/BackgroundChildImpl.cpp index 81f96c3844b3..8aef9a57bd1c 100644 --- a/ipc/glue/BackgroundChildImpl.cpp +++ b/ipc/glue/BackgroundChildImpl.cpp @@ -18,6 +18,7 @@ #include "mozilla/dom/cache/ActorUtils.h" #include "mozilla/dom/indexedDB/PBackgroundIDBFactoryChild.h" #include "mozilla/dom/ipc/BlobChild.h" +#include "mozilla/dom/quota/PQuotaChild.h" #include "mozilla/dom/MessagePortChild.h" #include "mozilla/dom/NuwaChild.h" #include "mozilla/ipc/PBackgroundTestChild.h" @@ -415,6 +416,21 @@ BackgroundChildImpl::DeallocPAsmJSCacheEntryChild(PAsmJSCacheEntryChild* aActor) return true; } +BackgroundChildImpl::PQuotaChild* +BackgroundChildImpl::AllocPQuotaChild() +{ + MOZ_CRASH("PQuotaChild actor should be manually constructed!"); +} + +bool +BackgroundChildImpl::DeallocPQuotaChild(PQuotaChild* aActor) +{ + MOZ_ASSERT(aActor); + + delete aActor; + return true; +} + } // namespace ipc } // namespace mozilla diff --git a/ipc/glue/BackgroundChildImpl.h b/ipc/glue/BackgroundChildImpl.h index 2f4fe4fb983f..91beeeeb4c64 100644 --- a/ipc/glue/BackgroundChildImpl.h +++ b/ipc/glue/BackgroundChildImpl.h @@ -145,6 +145,12 @@ protected: virtual bool DeallocPAsmJSCacheEntryChild(PAsmJSCacheEntryChild* aActor) override; + + virtual PQuotaChild* + AllocPQuotaChild() override; + + virtual bool + DeallocPQuotaChild(PQuotaChild* aActor) override; }; class BackgroundChildImpl::ThreadLocal final diff --git a/ipc/glue/BackgroundParentImpl.cpp b/ipc/glue/BackgroundParentImpl.cpp index 0f047784d32c..a589fe130cc8 100644 --- a/ipc/glue/BackgroundParentImpl.cpp +++ b/ipc/glue/BackgroundParentImpl.cpp @@ -21,6 +21,7 @@ #include "mozilla/dom/cache/ActorUtils.h" #include "mozilla/dom/indexedDB/ActorsParent.h" #include "mozilla/dom/ipc/BlobParent.h" +#include "mozilla/dom/quota/ActorsParent.h" #include "mozilla/ipc/BackgroundParent.h" #include "mozilla/ipc/BackgroundUtils.h" #include "mozilla/ipc/PBackgroundSharedTypes.h" @@ -678,6 +679,25 @@ BackgroundParentImpl::DeallocPAsmJSCacheEntryParent( return true; } +BackgroundParentImpl::PQuotaParent* +BackgroundParentImpl::AllocPQuotaParent() +{ + AssertIsInMainProcess(); + AssertIsOnBackgroundThread(); + + return mozilla::dom::quota::AllocPQuotaParent(); +} + +bool +BackgroundParentImpl::DeallocPQuotaParent(PQuotaParent* aActor) +{ + AssertIsInMainProcess(); + AssertIsOnBackgroundThread(); + MOZ_ASSERT(aActor); + + return mozilla::dom::quota::DeallocPQuotaParent(aActor); +} + } // namespace ipc } // namespace mozilla diff --git a/ipc/glue/BackgroundParentImpl.h b/ipc/glue/BackgroundParentImpl.h index 618cab98e3c9..8b165afc1467 100644 --- a/ipc/glue/BackgroundParentImpl.h +++ b/ipc/glue/BackgroundParentImpl.h @@ -165,6 +165,12 @@ protected: virtual bool DeallocPAsmJSCacheEntryParent(PAsmJSCacheEntryParent* aActor) override; + + virtual PQuotaParent* + AllocPQuotaParent() override; + + virtual bool + DeallocPQuotaParent(PQuotaParent* aActor) override; }; } // namespace ipc diff --git a/ipc/glue/PBackground.ipdl b/ipc/glue/PBackground.ipdl index 74af077a261e..3d4670c819d7 100644 --- a/ipc/glue/PBackground.ipdl +++ b/ipc/glue/PBackground.ipdl @@ -14,6 +14,7 @@ include protocol PFileDescriptorSet; include protocol PMessagePort; include protocol PCameras; include protocol PNuwa; +include protocol PQuota; include protocol PServiceWorkerManager; include protocol PUDPSocket; include protocol PVsync; @@ -50,6 +51,7 @@ sync protocol PBackground manages PMessagePort; manages PCameras; manages PNuwa; + manages PQuota; manages PServiceWorkerManager; manages PUDPSocket; manages PVsync; @@ -84,6 +86,8 @@ parent: WriteParams write, PrincipalInfo principalInfo); + PQuota(); + child: PCache(); PCacheStreamControl(); diff --git a/layout/build/nsLayoutCID.h b/layout/build/nsLayoutCID.h index 5e7d639b86cc..1b43c7592350 100644 --- a/layout/build/nsLayoutCID.h +++ b/layout/build/nsLayoutCID.h @@ -75,7 +75,7 @@ { 0x3160e271, 0x138d, 0x4cc7, { 0x9d, 0x63, 0x64, 0x29, 0xf1, 0x69, 0x57, 0xc7 } } // {5a75c25a-5e7e-4d90-8f7c-07eb15cc0aa8} -#define QUOTA_MANAGER_CID \ +#define QUOTAMANAGER_SERVICE_CID \ { 0x5a75c25a, 0x5e7e, 0x4d90, { 0x8f, 0x7c, 0x07, 0xeb, 0x15, 0xcc, 0x0a, 0xa8 } } // {c74bde32-bcc7-4840-8430-c733351b212a} diff --git a/layout/build/nsLayoutModule.cpp b/layout/build/nsLayoutModule.cpp index ce7e5de03e97..8310b1c54871 100644 --- a/layout/build/nsLayoutModule.cpp +++ b/layout/build/nsLayoutModule.cpp @@ -87,7 +87,7 @@ #include "mozilla/dom/DOMException.h" #include "mozilla/dom/DOMRequest.h" #include "mozilla/dom/network/UDPSocketChild.h" -#include "mozilla/dom/quota/QuotaManager.h" +#include "mozilla/dom/quota/QuotaManagerService.h" #include "mozilla/dom/workers/ServiceWorkerManager.h" #include "mozilla/dom/workers/WorkerDebuggerManager.h" #include "mozilla/OSFileConstants.h" @@ -269,7 +269,7 @@ using namespace mozilla; using namespace mozilla::dom; using mozilla::dom::alarm::AlarmHalService; using mozilla::dom::power::PowerManagerService; -using mozilla::dom::quota::QuotaManager; +using mozilla::dom::quota::QuotaManagerService; using mozilla::dom::workers::ServiceWorkerManager; using mozilla::dom::workers::WorkerDebuggerManager; using mozilla::dom::UDPSocketChild; @@ -314,8 +314,8 @@ NS_GENERIC_FACTORY_CONSTRUCTOR(DOMSessionStorageManager) NS_GENERIC_FACTORY_CONSTRUCTOR(DOMLocalStorageManager) NS_GENERIC_FACTORY_SINGLETON_CONSTRUCTOR(DOMRequestService, DOMRequestService::FactoryCreate) -NS_GENERIC_FACTORY_SINGLETON_CONSTRUCTOR(QuotaManager, - QuotaManager::FactoryCreate) +NS_GENERIC_FACTORY_SINGLETON_CONSTRUCTOR(QuotaManagerService, + QuotaManagerService::FactoryCreate) NS_GENERIC_FACTORY_SINGLETON_CONSTRUCTOR(ServiceWorkerManager, ServiceWorkerManager::GetInstance) NS_GENERIC_FACTORY_CONSTRUCTOR(WorkerDebuggerManager) @@ -765,7 +765,7 @@ NS_DEFINE_NAMED_CID(NS_DOMLOCALSTORAGEMANAGER_CID); NS_DEFINE_NAMED_CID(NS_DOMJSON_CID); NS_DEFINE_NAMED_CID(NS_TEXTEDITOR_CID); NS_DEFINE_NAMED_CID(DOMREQUEST_SERVICE_CID); -NS_DEFINE_NAMED_CID(QUOTA_MANAGER_CID); +NS_DEFINE_NAMED_CID(QUOTAMANAGER_SERVICE_CID); NS_DEFINE_NAMED_CID(SERVICEWORKERMANAGER_CID); NS_DEFINE_NAMED_CID(WORKERDEBUGGERMANAGER_CID); #ifdef MOZ_WIDGET_GONK @@ -1074,7 +1074,7 @@ static const mozilla::Module::CIDEntry kLayoutCIDs[] = { { &kNS_DOMJSON_CID, false, nullptr, NS_NewJSON }, { &kNS_TEXTEDITOR_CID, false, nullptr, nsPlaintextEditorConstructor }, { &kDOMREQUEST_SERVICE_CID, false, nullptr, DOMRequestServiceConstructor }, - { &kQUOTA_MANAGER_CID, false, nullptr, QuotaManagerConstructor }, + { &kQUOTAMANAGER_SERVICE_CID, false, nullptr, QuotaManagerServiceConstructor }, { &kSERVICEWORKERMANAGER_CID, false, nullptr, ServiceWorkerManagerConstructor }, { &kWORKERDEBUGGERMANAGER_CID, true, nullptr, WorkerDebuggerManagerConstructor }, #ifdef MOZ_WIDGET_GONK @@ -1243,7 +1243,7 @@ static const mozilla::Module::ContractIDEntry kLayoutContracts[] = { { "@mozilla.org/dom/json;1", &kNS_DOMJSON_CID }, { "@mozilla.org/editor/texteditor;1", &kNS_TEXTEDITOR_CID }, { DOMREQUEST_SERVICE_CONTRACTID, &kDOMREQUEST_SERVICE_CID }, - { QUOTA_MANAGER_CONTRACTID, &kQUOTA_MANAGER_CID }, + { QUOTAMANAGER_SERVICE_CONTRACTID, &kQUOTAMANAGER_SERVICE_CID }, { SERVICEWORKERMANAGER_CONTRACTID, &kSERVICEWORKERMANAGER_CID }, { WORKERDEBUGGERMANAGER_CONTRACTID, &kWORKERDEBUGGERMANAGER_CID }, #ifdef MOZ_WIDGET_GONK @@ -1346,8 +1346,8 @@ static const mozilla::Module::CategoryEntry kLayoutCategories[] = { { "net-channel-event-sinks", "CSPService", CSPSERVICE_CONTRACTID }, { "net-channel-event-sinks", NS_MIXEDCONTENTBLOCKER_CONTRACTID, NS_MIXEDCONTENTBLOCKER_CONTRACTID }, { "app-startup", "Script Security Manager", "service," NS_SCRIPTSECURITYMANAGER_CONTRACTID }, - { TOPIC_WEB_APP_CLEAR_DATA, "QuotaManager", "service," QUOTA_MANAGER_CONTRACTID }, - { OBSERVER_TOPIC_IDLE_DAILY, "QuotaManager", QUOTA_MANAGER_CONTRACTID }, + { TOPIC_WEB_APP_CLEAR_DATA, "QuotaManagerService", "service," QUOTAMANAGER_SERVICE_CONTRACTID }, + { OBSERVER_TOPIC_IDLE_DAILY, "QuotaManagerService", QUOTAMANAGER_SERVICE_CONTRACTID }, #ifdef MOZ_WIDGET_GONK { "app-startup", "Volume Service", "service," NS_VOLUMESERVICE_CONTRACTID }, #endif diff --git a/testing/specialpowers/content/SpecialPowersObserverAPI.js b/testing/specialpowers/content/SpecialPowersObserverAPI.js index 8b2403521287..73ac9410ef93 100644 --- a/testing/specialpowers/content/SpecialPowersObserverAPI.js +++ b/testing/specialpowers/content/SpecialPowersObserverAPI.js @@ -491,44 +491,6 @@ SpecialPowersObserverAPI.prototype = { return undefined; // See comment at the beginning of this function. } - case 'SPQuotaManager': { - let qm = Cc['@mozilla.org/dom/quota/manager;1'] - .getService(Ci.nsIQuotaManager); - let mm = aMessage.target - .QueryInterface(Ci.nsIFrameLoaderOwner) - .frameLoader - .messageManager; - let msg = aMessage.data; - let principal = msg.principal; - let op = msg.op; - - if (op != 'clear' && op != 'getUsage' && op != 'reset') { - throw new SpecialPowersError('Invalid operation for SPQuotaManager'); - } - - if (op == 'clear') { - qm.clearStoragesForPrincipal(principal); - } else if (op == 'reset') { - qm.reset(); - } - - // We always use the getUsageForPrincipal callback even if we're clearing - // since we know that clear and getUsageForPrincipal are synchronized by the - // QuotaManager. - let callback = function(principal, usage, fileUsage) { - let reply = { id: msg.id }; - if (op == 'getUsage') { - reply.usage = usage; - reply.fileUsage = fileUsage; - } - mm.sendAsyncMessage(aMessage.name, reply); - }; - - qm.getUsageForPrincipal(principal, callback); - - return undefined; // See comment at the beginning of this function. - } - case "SPCleanUpSTSData": { let origin = aMessage.data.origin; let flags = aMessage.data.flags; diff --git a/testing/specialpowers/content/specialpowers.js b/testing/specialpowers/content/specialpowers.js index be12beeebbc8..098f1671b37b 100644 --- a/testing/specialpowers/content/specialpowers.js +++ b/testing/specialpowers/content/specialpowers.js @@ -41,7 +41,6 @@ function SpecialPowers(window) { "SpecialPowers.CreateFiles", "SpecialPowers.RemoveFiles", "SPPingService", - "SPQuotaManager", "SPLoadExtension", "SPStartupExtension", "SPUnloadExtension", diff --git a/testing/specialpowers/content/specialpowersAPI.js b/testing/specialpowers/content/specialpowersAPI.js index f744adf6c375..85baaf806b2c 100644 --- a/testing/specialpowers/content/specialpowersAPI.js +++ b/testing/specialpowers/content/specialpowersAPI.js @@ -47,7 +47,6 @@ function SpecialPowersAPI() { this._observingPermissions = false; this._fm = null; this._cb = null; - this._quotaManagerCallbackInfos = null; } function bindDOMWindowUtils(aWindow) { @@ -1932,61 +1931,6 @@ SpecialPowersAPI.prototype = { this._sendSyncMessage('SPObserverService', msg); }, - clearStorageForDoc: function(wrappedDocument, callback) { - this._quotaManagerRequest('clear', wrappedDocument, callback); - }, - - getStorageUsageForDoc: function(wrappedDocument, callback) { - this._quotaManagerRequest('getUsage', wrappedDocument, callback); - }, - - resetStorageForDoc: function(wrappedDocument, callback) { - this._quotaManagerRequest('reset', wrappedDocument, callback); - }, - - _quotaManagerRequest: function(op, wrappedDocument, callback) { - const messageTopic = "SPQuotaManager"; - const id = Cc["@mozilla.org/uuid-generator;1"] - .getService(Ci.nsIUUIDGenerator) - .generateUUID() - .toString(); - - let callbackInfo = { id: id, callback: callback }; - - if (this._quotaManagerCallbackInfos) { - callbackInfo.listener = this._quotaManagerCallbackInfos[0].listener; - this._quotaManagerCallbackInfos.push(callbackInfo) - } else { - callbackInfo.listener = function(msg) { - msg = msg.data; - for (let index in this._quotaManagerCallbackInfos) { - let callbackInfo = this._quotaManagerCallbackInfos[index]; - if (callbackInfo.id == msg.id) { - if (this._quotaManagerCallbackInfos.length > 1) { - this._quotaManagerCallbackInfos.splice(index, 1); - } else { - this._quotaManagerCallbackInfos = null; - this._removeMessageListener(messageTopic, callbackInfo.listener); - } - - if ('usage' in msg) { - callbackInfo.callback(msg.usage, msg.fileUsage); - } else { - callbackInfo.callback(); - } - } - } - }.bind(this); - - this._addMessageListener(messageTopic, callbackInfo.listener); - this._quotaManagerCallbackInfos = [ callbackInfo ]; - } - - let principal = unwrapIfWrapped(wrappedDocument).nodePrincipal; - let msg = { op: op, principal: principal, id: id }; - this._sendAsyncMessage(messageTopic, msg); - }, - createDOMFile: function(path, options) { return new File(path, options); }, diff --git a/toolkit/forgetaboutsite/ForgetAboutSite.jsm b/toolkit/forgetaboutsite/ForgetAboutSite.jsm index 7738fb942058..2e20c08d461d 100644 --- a/toolkit/forgetaboutsite/ForgetAboutSite.jsm +++ b/toolkit/forgetaboutsite/ForgetAboutSite.jsm @@ -150,8 +150,8 @@ this.ForgetAboutSite = { } // Offline Storages - let qm = Cc["@mozilla.org/dom/quota/manager;1"]. - getService(Ci.nsIQuotaManager); + let qms = Cc["@mozilla.org/dom/quota-manager-service;1"]. + getService(Ci.nsIQuotaManagerService); // delete data from both HTTP and HTTPS sites let caUtils = {}; let scriptLoader = Cc["@mozilla.org/moz/jssubscript-loader;1"]. @@ -162,8 +162,8 @@ this.ForgetAboutSite = { let httpsURI = caUtils.makeURI("https://" + aDomain); let httpPrincipal = Services.scriptSecurityManager.createCodebasePrincipal(httpURI, {}); let httpsPrincipal = Services.scriptSecurityManager.createCodebasePrincipal(httpsURI, {}); - qm.clearStoragesForPrincipal(httpPrincipal); - qm.clearStoragesForPrincipal(httpsPrincipal); + qms.clearStoragesForPrincipal(httpPrincipal); + qms.clearStoragesForPrincipal(httpsPrincipal); function onContentPrefsRemovalFinished() { // Everybody else (including extensions) diff --git a/toolkit/modules/Services.jsm b/toolkit/modules/Services.jsm index 3965c7922b2c..5cb88ff9e552 100644 --- a/toolkit/modules/Services.jsm +++ b/toolkit/modules/Services.jsm @@ -102,6 +102,7 @@ var initTable = [ ["blocklist", "@mozilla.org/extensions/blocklist;1", "nsIBlocklistService"], ["netUtils", "@mozilla.org/network/util;1", "nsINetUtil"], ["loadContextInfo", "@mozilla.org/load-context-info-factory;1", "nsILoadContextInfoFactory"], + ["qms", "@mozilla.org/dom/quota-manager-service;1", "nsIQuotaManagerService"], ]; initTable.forEach(([name, contract, intf, enabled = true]) => {