michael@0: /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 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 michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: #ifndef nsDOMStorageCache_h___ michael@0: #define nsDOMStorageCache_h___ michael@0: michael@0: #include "nsIURI.h" michael@0: #include "nsIPrincipal.h" michael@0: #include "nsITimer.h" michael@0: michael@0: #include "nsString.h" michael@0: #include "nsDataHashtable.h" michael@0: #include "nsHashKeys.h" michael@0: #include "mozilla/Monitor.h" michael@0: #include "mozilla/Telemetry.h" michael@0: #include "nsAutoPtr.h" michael@0: michael@0: namespace mozilla { michael@0: namespace dom { michael@0: michael@0: class DOMStorage; michael@0: class DOMStorageUsage; michael@0: class DOMStorageManager; michael@0: class DOMStorageDBBridge; michael@0: michael@0: // Interface class on which only the database or IPC may call. michael@0: // Used to populate the cache with DB data. michael@0: class DOMStorageCacheBridge michael@0: { michael@0: public: michael@0: NS_IMETHOD_(MozExternalRefCountType) AddRef(void); michael@0: NS_IMETHOD_(void) Release(void); michael@0: michael@0: virtual ~DOMStorageCacheBridge() {} michael@0: michael@0: // The scope (origin) in the database usage format (reversed) michael@0: virtual const nsCString& Scope() const = 0; michael@0: michael@0: // Whether the cache is already fully loaded michael@0: virtual bool Loaded() = 0; michael@0: michael@0: // How many items has so far been loaded into the cache, used michael@0: // for optimization purposes michael@0: virtual uint32_t LoadedCount() = 0; michael@0: michael@0: // Called by the database to load a key and its value to the cache michael@0: virtual bool LoadItem(const nsAString& aKey, const nsString& aValue) = 0; michael@0: michael@0: // Called by the database after all keys and values has been loaded michael@0: // to this cache michael@0: virtual void LoadDone(nsresult aRv) = 0; michael@0: michael@0: // Use to synchronously wait until the cache gets fully loaded with data, michael@0: // this method exits after LoadDone has been called michael@0: virtual void LoadWait() = 0; michael@0: michael@0: protected: michael@0: ThreadSafeAutoRefCnt mRefCnt; michael@0: NS_DECL_OWNINGTHREAD michael@0: }; michael@0: michael@0: // Implementation of scope cache that is responsible for preloading data michael@0: // for persistent storage (localStorage) and hold data for non-private, michael@0: // private and session-only cookie modes. It is also responsible for michael@0: // persisting data changes using the database, works as a write-back cache. michael@0: class DOMStorageCache : public DOMStorageCacheBridge michael@0: { michael@0: public: michael@0: NS_IMETHOD_(void) Release(void); michael@0: michael@0: DOMStorageCache(const nsACString* aScope); michael@0: virtual ~DOMStorageCache(); michael@0: michael@0: void Init(DOMStorageManager* aManager, bool aPersistent, michael@0: nsIURI* aFirstPartyIsolationURI, nsIPrincipal* aPrincipal, michael@0: const nsACString& aQuotaScope); michael@0: michael@0: // Copies all data from the other storage. michael@0: void CloneFrom(const DOMStorageCache* aThat); michael@0: michael@0: // Starts async preload of this cache if it persistent and not loaded. michael@0: void Preload(); michael@0: michael@0: // Keeps the cache alive (i.e. present in the manager's hash table) for a time. michael@0: void KeepAlive(); michael@0: michael@0: // The set of methods that are invoked by DOM storage web API. michael@0: // We are passing the DOMStorage object just to let the cache michael@0: // read properties like mPrivate and mSessionOnly. michael@0: // Get* methods return error when load from the database has failed. michael@0: nsresult GetLength(const DOMStorage* aStorage, uint32_t* aRetval); michael@0: nsresult GetKey(const DOMStorage* aStorage, uint32_t index, nsAString& aRetval); michael@0: nsresult GetItem(const DOMStorage* aStorage, const nsAString& aKey, nsAString& aRetval); michael@0: nsresult SetItem(const DOMStorage* aStorage, const nsAString& aKey, const nsString& aValue, nsString& aOld); michael@0: nsresult RemoveItem(const DOMStorage* aStorage, const nsAString& aKey, nsString& aOld); michael@0: nsresult Clear(const DOMStorage* aStorage); michael@0: michael@0: nsTArray* GetKeys(const DOMStorage* aStorage); michael@0: michael@0: nsIURI* FirstPartyIsolationURI() const { return mFirstPartyIsolationURI; } michael@0: michael@0: // Whether the principal equals principal the cache was created for michael@0: bool CheckPrincipal(nsIPrincipal* aPrincipal) const; michael@0: nsIPrincipal* Principal() const { return mPrincipal; } michael@0: michael@0: // Starts the database engine thread or the IPC bridge michael@0: static DOMStorageDBBridge* StartDatabase(); michael@0: static DOMStorageDBBridge* GetDatabase(); michael@0: michael@0: // Stops the thread and flushes all uncommited data michael@0: static nsresult StopDatabase(); michael@0: michael@0: // DOMStorageCacheBridge michael@0: michael@0: virtual const nsCString& Scope() const { return mScope; } michael@0: virtual bool Loaded() { return mLoaded; } michael@0: virtual uint32_t LoadedCount(); michael@0: virtual bool LoadItem(const nsAString& aKey, const nsString& aValue); michael@0: virtual void LoadDone(nsresult aRv); michael@0: virtual void LoadWait(); michael@0: michael@0: // Cache keeps 3 sets of data: regular, private and session-only. michael@0: // This class keeps keys and values for a set and also caches michael@0: // size of the data for quick per-origin quota checking. michael@0: class Data michael@0: { michael@0: public: michael@0: Data() : mOriginQuotaUsage(0) {} michael@0: int64_t mOriginQuotaUsage; michael@0: nsDataHashtable mKeys; michael@0: }; michael@0: michael@0: public: michael@0: // Number of data sets we keep: default, private, session michael@0: static const uint32_t kDataSetCount = 3; michael@0: michael@0: private: michael@0: // API to clear the cache data, this is invoked by chrome operations michael@0: // like cookie deletion. michael@0: friend class DOMStorageManager; michael@0: michael@0: static const uint32_t kUnloadDefault = 1 << 0; michael@0: static const uint32_t kUnloadPrivate = 1 << 1; michael@0: static const uint32_t kUnloadSession = 1 << 2; michael@0: static const uint32_t kUnloadComplete = michael@0: kUnloadDefault | kUnloadPrivate | kUnloadSession; michael@0: michael@0: #ifdef DOM_STORAGE_TESTS michael@0: static const uint32_t kTestReload = 1 << 15; michael@0: #endif michael@0: michael@0: void UnloadItems(uint32_t aUnloadFlags); michael@0: michael@0: private: michael@0: // Synchronously blocks until the cache is fully loaded from the database michael@0: void WaitForPreload(mozilla::Telemetry::ID aTelemetryID); michael@0: michael@0: // Helper to get one of the 3 data sets (regular, private, session) michael@0: Data& DataSet(const DOMStorage* aStorage); michael@0: michael@0: // Whether the storage change is about to persist michael@0: bool Persist(const DOMStorage* aStorage) const; michael@0: michael@0: // Changes the quota usage on the given data set if it fits the quota. michael@0: // If not, then false is returned and no change to the set must be done. michael@0: bool ProcessUsageDelta(uint32_t aGetDataSetIndex, const int64_t aDelta); michael@0: bool ProcessUsageDelta(const DOMStorage* aStorage, const int64_t aDelta); michael@0: michael@0: private: michael@0: // When a cache is reponsible for its life time (in case of localStorage data michael@0: // cache) we need to refer our manager since removal of the cache from the hash michael@0: // table is handled in the destructor by call to the manager. michael@0: // Cache could potentially overlive the manager, hence the hard ref. michael@0: nsRefPtr mManager; michael@0: michael@0: // Reference to the usage counter object we check on for eTLD+1 quota limit. michael@0: // Obtained from the manager during initialization (Init method). michael@0: nsRefPtr mUsage; michael@0: michael@0: // Timer that holds this cache alive for a while after it has been preloaded. michael@0: nsCOMPtr mKeepAliveTimer; michael@0: michael@0: // The first party URI associated with this cache. michael@0: nsCOMPtr mFirstPartyIsolationURI; michael@0: michael@0: // Principal the cache has been initially created for, this is used only michael@0: // for sessionStorage access checks since sessionStorage objects are strictly michael@0: // scoped by a principal. localStorage objects on the other hand are scoped by michael@0: // origin only. michael@0: nsCOMPtr mPrincipal; michael@0: michael@0: // The scope this cache belongs to in the "DB format", i.e. reversed michael@0: nsCString mScope; michael@0: michael@0: // The eTLD+1 scope used to count quota usage. michael@0: nsCString mQuotaScope; michael@0: michael@0: // Non-private Browsing, Private Browsing and Session Only sets. michael@0: Data mData[kDataSetCount]; michael@0: michael@0: // This monitor is used to wait for full load of data. michael@0: mozilla::Monitor mMonitor; michael@0: michael@0: // Flag that is initially false. When the cache is about to work with michael@0: // the database (i.e. it is persistent) this flags is set to true after michael@0: // all keys and coresponding values are loaded from the database. michael@0: // This flag never goes from true back to false. michael@0: bool mLoaded; michael@0: michael@0: // Result of load from the database. Valid after mLoaded flag has been set. michael@0: nsresult mLoadResult; michael@0: michael@0: // Init() method has been called michael@0: bool mInitialized : 1; michael@0: michael@0: // This cache is about to be bound with the database (i.e. it has michael@0: // to load from the DB first and has to persist when modifying the michael@0: // default data set.) michael@0: bool mPersistent : 1; michael@0: michael@0: // - False when the session-only data set was never used. michael@0: // - True after access to session-only data has been made for the first time. michael@0: // We also fill session-only data set with the default one at that moment. michael@0: // Drops back to false when session-only data are cleared from chrome. michael@0: bool mSessionOnlyDataSetActive : 1; michael@0: michael@0: // Whether we have already captured state of the cache preload on our first access. michael@0: bool mPreloadTelemetryRecorded : 1; michael@0: michael@0: // DOMStorageDBThread on the parent or single process, michael@0: // DOMStorageDBChild on the child process. michael@0: static DOMStorageDBBridge* sDatabase; michael@0: michael@0: // False until we shut the database down. michael@0: static bool sDatabaseDown; michael@0: }; michael@0: michael@0: // DOMStorageUsage michael@0: // Infrastructure to manage and check eTLD+1 quota michael@0: class DOMStorageUsageBridge michael@0: { michael@0: public: michael@0: NS_INLINE_DECL_THREADSAFE_REFCOUNTING(DOMStorageUsageBridge) michael@0: michael@0: virtual const nsCString& Scope() = 0; michael@0: virtual void LoadUsage(const int64_t aUsage) = 0; michael@0: michael@0: protected: michael@0: // Protected destructor, to discourage deletion outside of Release(): michael@0: virtual ~DOMStorageUsageBridge() {} michael@0: }; michael@0: michael@0: class DOMStorageUsage : public DOMStorageUsageBridge michael@0: { michael@0: public: michael@0: DOMStorageUsage(const nsACString& aScope); michael@0: michael@0: bool CheckAndSetETLD1UsageDelta(uint32_t aDataSetIndex, int64_t aUsageDelta); michael@0: michael@0: private: michael@0: virtual const nsCString& Scope() { return mScope; } michael@0: virtual void LoadUsage(const int64_t aUsage); michael@0: michael@0: nsCString mScope; michael@0: int64_t mUsage[DOMStorageCache::kDataSetCount]; michael@0: }; michael@0: michael@0: } // ::dom michael@0: } // ::mozilla michael@0: michael@0: #endif