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