michael@0: /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ michael@0: /* vim: set ts=2 et sw=2 tw=80: */ michael@0: /* This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this file, michael@0: * You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: #ifndef mozilla_dom_quota_quotamanager_h__ michael@0: #define mozilla_dom_quota_quotamanager_h__ michael@0: michael@0: #include "QuotaCommon.h" michael@0: michael@0: #include "nsIObserver.h" michael@0: #include "nsIQuotaManager.h" michael@0: michael@0: #include "mozilla/dom/Nullable.h" michael@0: #include "mozilla/Mutex.h" michael@0: michael@0: #include "nsClassHashtable.h" michael@0: #include "nsRefPtrHashtable.h" michael@0: michael@0: #include "ArrayCluster.h" michael@0: #include "Client.h" michael@0: #include "PersistenceType.h" michael@0: #include "StoragePrivilege.h" michael@0: michael@0: #define QUOTA_MANAGER_CONTRACTID "@mozilla.org/dom/quota/manager;1" michael@0: michael@0: class nsIOfflineStorage; michael@0: class nsIPrincipal; michael@0: class nsIThread; michael@0: class nsITimer; michael@0: class nsIURI; michael@0: class nsPIDOMWindow; michael@0: class nsIRunnable; michael@0: michael@0: BEGIN_QUOTA_NAMESPACE michael@0: michael@0: class AcquireListener; michael@0: class AsyncUsageRunnable; michael@0: class CheckQuotaHelper; michael@0: class CollectOriginsHelper; michael@0: class FinalizeOriginEvictionRunnable; michael@0: class GroupInfo; michael@0: class GroupInfoPair; michael@0: class OriginClearRunnable; michael@0: class OriginInfo; michael@0: class OriginOrPatternString; michael@0: class QuotaObject; michael@0: class ResetOrClearRunnable; michael@0: struct SynchronizedOp; michael@0: michael@0: class QuotaManager MOZ_FINAL : public nsIQuotaManager, michael@0: public nsIObserver michael@0: { michael@0: friend class AsyncUsageRunnable; michael@0: friend class CollectOriginsHelper; michael@0: friend class FinalizeOriginEvictionRunnable; michael@0: friend class GroupInfo; michael@0: friend class OriginClearRunnable; michael@0: friend class OriginInfo; michael@0: friend class QuotaObject; michael@0: friend class ResetOrClearRunnable; michael@0: michael@0: enum MozBrowserPatternFlag michael@0: { michael@0: MozBrowser = 0, michael@0: NotMozBrowser, michael@0: IgnoreMozBrowser michael@0: }; michael@0: michael@0: typedef void michael@0: (*WaitingOnStoragesCallback)(nsTArray >&, void*); michael@0: michael@0: public: michael@0: NS_DECL_ISUPPORTS michael@0: NS_DECL_NSIQUOTAMANAGER michael@0: NS_DECL_NSIOBSERVER michael@0: michael@0: // Returns a non-owning reference. michael@0: static QuotaManager* michael@0: GetOrCreate(); michael@0: michael@0: // Returns a non-owning reference. michael@0: static QuotaManager* michael@0: Get(); michael@0: michael@0: // Returns an owning reference! No one should call this but the factory. michael@0: static QuotaManager* michael@0: FactoryCreate(); michael@0: michael@0: // Returns true if we've begun the shutdown process. michael@0: static bool IsShuttingDown(); michael@0: michael@0: void michael@0: InitQuotaForOrigin(PersistenceType aPersistenceType, michael@0: const nsACString& aGroup, michael@0: const nsACString& aOrigin, michael@0: uint64_t aLimitBytes, michael@0: uint64_t aUsageBytes, michael@0: int64_t aAccessTime); michael@0: michael@0: void michael@0: DecreaseUsageForOrigin(PersistenceType aPersistenceType, michael@0: const nsACString& aGroup, michael@0: const nsACString& aOrigin, michael@0: int64_t aSize); michael@0: michael@0: void michael@0: UpdateOriginAccessTime(PersistenceType aPersistenceType, michael@0: const nsACString& aGroup, michael@0: const nsACString& aOrigin); michael@0: michael@0: void michael@0: RemoveQuota(); michael@0: michael@0: void michael@0: RemoveQuotaForPersistenceType(PersistenceType); michael@0: michael@0: void michael@0: RemoveQuotaForOrigin(PersistenceType aPersistenceType, michael@0: const nsACString& aGroup, michael@0: const nsACString& aOrigin) michael@0: { michael@0: MutexAutoLock lock(mQuotaMutex); michael@0: LockedRemoveQuotaForOrigin(aPersistenceType, aGroup, aOrigin); michael@0: } michael@0: michael@0: void michael@0: RemoveQuotaForPattern(PersistenceType aPersistenceType, michael@0: const nsACString& aPattern); michael@0: michael@0: already_AddRefed michael@0: GetQuotaObject(PersistenceType aPersistenceType, michael@0: const nsACString& aGroup, michael@0: const nsACString& aOrigin, michael@0: nsIFile* aFile); michael@0: michael@0: already_AddRefed michael@0: GetQuotaObject(PersistenceType aPersistenceType, michael@0: const nsACString& aGroup, michael@0: const nsACString& aOrigin, michael@0: const nsAString& aPath); michael@0: michael@0: // Set the Window that the current thread is doing operations for. michael@0: // The caller is responsible for ensuring that aWindow is held alive. michael@0: static void michael@0: SetCurrentWindow(nsPIDOMWindow* aWindow) michael@0: { michael@0: QuotaManager* quotaManager = Get(); michael@0: NS_ASSERTION(quotaManager, "Must have a manager here!"); michael@0: michael@0: quotaManager->SetCurrentWindowInternal(aWindow); michael@0: } michael@0: michael@0: static void michael@0: CancelPromptsForWindow(nsPIDOMWindow* aWindow) michael@0: { michael@0: NS_ASSERTION(aWindow, "Passed null window!"); michael@0: michael@0: QuotaManager* quotaManager = Get(); michael@0: NS_ASSERTION(quotaManager, "Must have a manager here!"); michael@0: michael@0: quotaManager->CancelPromptsForWindowInternal(aWindow); michael@0: } michael@0: michael@0: // Called when a storage is created. michael@0: bool michael@0: RegisterStorage(nsIOfflineStorage* aStorage); michael@0: michael@0: // Called when a storage is being unlinked or destroyed. michael@0: void michael@0: UnregisterStorage(nsIOfflineStorage* aStorage); michael@0: michael@0: // Called when a storage has been closed. michael@0: void michael@0: OnStorageClosed(nsIOfflineStorage* aStorage); michael@0: michael@0: // Called when a window is being purged from the bfcache or the user leaves michael@0: // a page which isn't going into the bfcache. Forces any live storage michael@0: // objects to close themselves and aborts any running transactions. michael@0: void michael@0: AbortCloseStoragesForWindow(nsPIDOMWindow* aWindow); michael@0: michael@0: // Used to check if there are running transactions in a given window. michael@0: bool michael@0: HasOpenTransactions(nsPIDOMWindow* aWindow); michael@0: michael@0: // Waits for storages to be cleared and for version change transactions to michael@0: // complete before dispatching the given runnable. michael@0: nsresult michael@0: WaitForOpenAllowed(const OriginOrPatternString& aOriginOrPattern, michael@0: Nullable aPersistenceType, michael@0: const nsACString& aId, nsIRunnable* aRunnable); michael@0: michael@0: // Acquire exclusive access to the storage given (waits for all others to michael@0: // close). If storages need to close first, the callback will be invoked michael@0: // with an array of said storages. michael@0: nsresult michael@0: AcquireExclusiveAccess(nsIOfflineStorage* aStorage, michael@0: const nsACString& aOrigin, michael@0: Nullable aPersistenceType, michael@0: AcquireListener* aListener, michael@0: WaitingOnStoragesCallback aCallback, michael@0: void* aClosure) michael@0: { michael@0: NS_ASSERTION(aStorage, "Need a storage here!"); michael@0: return AcquireExclusiveAccess(aOrigin, aPersistenceType, aStorage, michael@0: aListener, aCallback, aClosure); michael@0: } michael@0: michael@0: nsresult michael@0: AcquireExclusiveAccess(const nsACString& aOrigin, michael@0: Nullable aPersistenceType, michael@0: AcquireListener* aListener, michael@0: WaitingOnStoragesCallback aCallback, michael@0: void* aClosure) michael@0: { michael@0: return AcquireExclusiveAccess(aOrigin, aPersistenceType, nullptr, michael@0: aListener, aCallback, aClosure); michael@0: } michael@0: michael@0: void michael@0: AllowNextSynchronizedOp(const OriginOrPatternString& aOriginOrPattern, michael@0: Nullable aPersistenceType, michael@0: const nsACString& aId); michael@0: michael@0: bool michael@0: IsClearOriginPending(const nsACString& aPattern, michael@0: Nullable aPersistenceType) michael@0: { michael@0: return !!FindSynchronizedOp(aPattern, aPersistenceType, EmptyCString()); michael@0: } michael@0: michael@0: nsresult michael@0: GetDirectoryForOrigin(PersistenceType aPersistenceType, michael@0: const nsACString& aASCIIOrigin, michael@0: nsIFile** aDirectory) const; michael@0: michael@0: nsresult michael@0: EnsureOriginIsInitialized(PersistenceType aPersistenceType, michael@0: const nsACString& aGroup, michael@0: const nsACString& aOrigin, michael@0: bool aTrackQuota, michael@0: nsIFile** aDirectory); michael@0: michael@0: void michael@0: OriginClearCompleted(PersistenceType aPersistenceType, michael@0: const OriginOrPatternString& aOriginOrPattern); michael@0: michael@0: void michael@0: ResetOrClearCompleted(); michael@0: michael@0: void michael@0: AssertCurrentThreadOwnsQuotaMutex() michael@0: { michael@0: mQuotaMutex.AssertCurrentThreadOwns(); michael@0: } michael@0: michael@0: nsIThread* michael@0: IOThread() michael@0: { michael@0: NS_ASSERTION(mIOThread, "This should never be null!"); michael@0: return mIOThread; michael@0: } michael@0: michael@0: already_AddRefed michael@0: GetClient(Client::Type aClientType); michael@0: michael@0: const nsString& michael@0: GetStoragePath(PersistenceType aPersistenceType) const michael@0: { michael@0: if (aPersistenceType == PERSISTENCE_TYPE_PERSISTENT) { michael@0: return mPersistentStoragePath; michael@0: } michael@0: michael@0: NS_ASSERTION(aPersistenceType == PERSISTENCE_TYPE_TEMPORARY, "Huh?"); michael@0: michael@0: return mTemporaryStoragePath; michael@0: } michael@0: michael@0: uint64_t michael@0: GetGroupLimit() const; michael@0: michael@0: static uint32_t michael@0: GetStorageQuotaMB(); michael@0: michael@0: static void michael@0: GetStorageId(PersistenceType aPersistenceType, michael@0: const nsACString& aOrigin, michael@0: Client::Type aClientType, michael@0: const nsAString& aName, michael@0: nsACString& aDatabaseId); michael@0: michael@0: static nsresult michael@0: GetInfoFromURI(nsIURI* aURI, michael@0: uint32_t aAppId, michael@0: bool aInMozBrowser, michael@0: nsACString* aGroup, michael@0: nsACString* aASCIIOrigin, michael@0: StoragePrivilege* aPrivilege, michael@0: PersistenceType* aDefaultPersistenceType); michael@0: michael@0: static nsresult michael@0: GetInfoFromPrincipal(nsIPrincipal* aPrincipal, michael@0: nsACString* aGroup, michael@0: nsACString* aASCIIOrigin, michael@0: StoragePrivilege* aPrivilege, michael@0: PersistenceType* aDefaultPersistenceType); michael@0: michael@0: static nsresult michael@0: GetInfoFromWindow(nsPIDOMWindow* aWindow, michael@0: nsACString* aGroup, michael@0: nsACString* aASCIIOrigin, michael@0: StoragePrivilege* aPrivilege, michael@0: PersistenceType* aDefaultPersistenceType); michael@0: michael@0: static void michael@0: GetInfoForChrome(nsACString* aGroup, michael@0: nsACString* aASCIIOrigin, michael@0: StoragePrivilege* aPrivilege, michael@0: PersistenceType* aDefaultPersistenceType); michael@0: michael@0: static void michael@0: GetOriginPatternString(uint32_t aAppId, bool aBrowserOnly, michael@0: const nsACString& aOrigin, nsAutoCString& _retval) michael@0: { michael@0: return GetOriginPatternString(aAppId, michael@0: aBrowserOnly ? MozBrowser : NotMozBrowser, michael@0: aOrigin, _retval); michael@0: } michael@0: michael@0: static void michael@0: GetOriginPatternStringMaybeIgnoreBrowser(uint32_t aAppId, bool aBrowserOnly, michael@0: nsAutoCString& _retval) michael@0: { michael@0: return GetOriginPatternString(aAppId, michael@0: aBrowserOnly ? MozBrowser : IgnoreMozBrowser, michael@0: EmptyCString(), _retval); michael@0: } michael@0: michael@0: private: michael@0: QuotaManager(); michael@0: michael@0: virtual ~QuotaManager(); michael@0: michael@0: nsresult michael@0: Init(); michael@0: michael@0: void michael@0: SetCurrentWindowInternal(nsPIDOMWindow* aWindow); michael@0: michael@0: void michael@0: CancelPromptsForWindowInternal(nsPIDOMWindow* aWindow); michael@0: michael@0: // Determine if the quota is lifted for the Window the current thread is michael@0: // using. michael@0: bool michael@0: LockedQuotaIsLifted(); michael@0: michael@0: uint64_t michael@0: LockedCollectOriginsForEviction(uint64_t aMinSizeToBeFreed, michael@0: nsTArray& aOriginInfos); michael@0: michael@0: void michael@0: LockedRemoveQuotaForOrigin(PersistenceType aPersistenceType, michael@0: const nsACString& aGroup, michael@0: const nsACString& aOrigin); michael@0: michael@0: nsresult michael@0: AcquireExclusiveAccess(const nsACString& aOrigin, michael@0: Nullable aPersistenceType, michael@0: nsIOfflineStorage* aStorage, michael@0: AcquireListener* aListener, michael@0: WaitingOnStoragesCallback aCallback, michael@0: void* aClosure); michael@0: michael@0: void michael@0: AddSynchronizedOp(const OriginOrPatternString& aOriginOrPattern, michael@0: Nullable aPersistenceType); michael@0: michael@0: nsresult michael@0: RunSynchronizedOp(nsIOfflineStorage* aStorage, michael@0: SynchronizedOp* aOp); michael@0: michael@0: SynchronizedOp* michael@0: FindSynchronizedOp(const nsACString& aPattern, michael@0: Nullable aPersistenceType, michael@0: const nsACString& aId); michael@0: michael@0: nsresult michael@0: MaybeUpgradeIndexedDBDirectory(); michael@0: michael@0: nsresult michael@0: InitializeOrigin(PersistenceType aPersistenceType, michael@0: const nsACString& aGroup, michael@0: const nsACString& aOrigin, michael@0: bool aTrackQuota, michael@0: int64_t aAccessTime, michael@0: nsIFile* aDirectory); michael@0: michael@0: nsresult michael@0: ClearStoragesForApp(uint32_t aAppId, bool aBrowserOnly); michael@0: michael@0: void michael@0: CheckTemporaryStorageLimits(); michael@0: michael@0: // Collect inactive and the least recently used origins. michael@0: uint64_t michael@0: CollectOriginsForEviction(uint64_t aMinSizeToBeFreed, michael@0: nsTArray& aOriginInfos); michael@0: michael@0: void michael@0: DeleteTemporaryFilesForOrigin(const nsACString& aOrigin); michael@0: michael@0: void michael@0: FinalizeOriginEviction(nsTArray& aOrigins); michael@0: michael@0: void michael@0: SaveOriginAccessTime(const nsACString& aOrigin, int64_t aTimestamp); michael@0: michael@0: void michael@0: ReleaseIOThreadObjects() michael@0: { michael@0: AssertIsOnIOThread(); michael@0: michael@0: for (uint32_t index = 0; index < Client::TYPE_MAX; index++) { michael@0: mClients[index]->ReleaseIOThreadObjects(); michael@0: } michael@0: } michael@0: michael@0: static void michael@0: GetOriginPatternString(uint32_t aAppId, michael@0: MozBrowserPatternFlag aBrowserFlag, michael@0: const nsACString& aOrigin, michael@0: nsAutoCString& _retval); michael@0: michael@0: static PLDHashOperator michael@0: RemoveQuotaForPersistenceTypeCallback(const nsACString& aKey, michael@0: nsAutoPtr& aValue, michael@0: void* aUserArg); michael@0: michael@0: static PLDHashOperator michael@0: RemoveQuotaCallback(const nsACString& aKey, michael@0: nsAutoPtr& aValue, michael@0: void* aUserArg); michael@0: michael@0: static PLDHashOperator michael@0: RemoveQuotaForPatternCallback(const nsACString& aKey, michael@0: nsAutoPtr& aValue, michael@0: void* aUserArg); michael@0: michael@0: static PLDHashOperator michael@0: GetOriginsExceedingGroupLimit(const nsACString& aKey, michael@0: GroupInfoPair* aValue, michael@0: void* aUserArg); michael@0: michael@0: static PLDHashOperator michael@0: GetAllTemporaryStorageOrigins(const nsACString& aKey, michael@0: GroupInfoPair* aValue, michael@0: void* aUserArg); michael@0: michael@0: static PLDHashOperator michael@0: AddTemporaryStorageOrigins(const nsACString& aKey, michael@0: ArrayCluster* aValue, michael@0: void* aUserArg); michael@0: michael@0: static PLDHashOperator michael@0: GetInactiveTemporaryStorageOrigins(const nsACString& aKey, michael@0: GroupInfoPair* aValue, michael@0: void* aUserArg); michael@0: michael@0: // TLS storage index for the current thread's window. michael@0: unsigned int mCurrentWindowIndex; michael@0: michael@0: mozilla::Mutex mQuotaMutex; michael@0: michael@0: nsClassHashtable mGroupInfoPairs; michael@0: michael@0: // A map of Windows to the corresponding quota helper. michael@0: nsRefPtrHashtable, michael@0: CheckQuotaHelper> mCheckQuotaHelpers; michael@0: michael@0: // Maintains a list of live storages per origin. michael@0: nsClassHashtable > mLiveStorages; michael@0: michael@0: // Maintains a list of synchronized operatons that are in progress or queued. michael@0: nsAutoTArray, 5> mSynchronizedOps; michael@0: michael@0: // Thread on which IO is performed. michael@0: nsCOMPtr mIOThread; michael@0: michael@0: // A timer that gets activated at shutdown to ensure we close all storages. michael@0: nsCOMPtr mShutdownTimer; michael@0: michael@0: // A list of all successfully initialized origins. This list isn't protected michael@0: // by any mutex but it is only ever touched on the IO thread. michael@0: nsTArray mInitializedOrigins; michael@0: michael@0: nsAutoTArray, Client::TYPE_MAX> mClients; michael@0: michael@0: nsString mIndexedDBPath; michael@0: nsString mPersistentStoragePath; michael@0: nsString mTemporaryStoragePath; michael@0: michael@0: uint64_t mTemporaryStorageLimit; michael@0: uint64_t mTemporaryStorageUsage; michael@0: bool mTemporaryStorageInitialized; michael@0: michael@0: bool mStorageAreaInitialized; michael@0: }; michael@0: michael@0: class AutoEnterWindow michael@0: { michael@0: public: michael@0: AutoEnterWindow(nsPIDOMWindow* aWindow) michael@0: { michael@0: QuotaManager::SetCurrentWindow(aWindow); michael@0: } michael@0: michael@0: ~AutoEnterWindow() michael@0: { michael@0: QuotaManager::SetCurrentWindow(nullptr); michael@0: } michael@0: }; michael@0: michael@0: END_QUOTA_NAMESPACE michael@0: michael@0: #endif /* mozilla_dom_quota_quotamanager_h__ */