LocalStorageCache.h (11841B)
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */ 3 /* This Source Code Form is subject to the terms of the Mozilla Public 4 * License, v. 2.0. If a copy of the MPL was not distributed with this 5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 6 7 #ifndef mozilla_dom_LocalStorageCache_h 8 #define mozilla_dom_LocalStorageCache_h 9 10 #include "mozilla/Atomics.h" 11 #include "mozilla/Monitor.h" 12 #include "nsHashKeys.h" 13 #include "nsIPrincipal.h" 14 #include "nsString.h" 15 #include "nsTHashMap.h" 16 17 namespace mozilla::dom { 18 19 class LocalStorage; 20 class LocalStorageCacheChild; 21 class LocalStorageManager; 22 class StorageUsage; 23 class StorageDBBridge; 24 25 // Interface class on which only the database or IPC may call. 26 // Used to populate the cache with DB data. 27 class LocalStorageCacheBridge { 28 public: 29 NS_IMETHOD_(MozExternalRefCountType) AddRef(void); 30 NS_IMETHOD_(void) Release(void); 31 32 // The origin of the cache, result is concatenation of OriginNoSuffix() and 33 // OriginSuffix(), see below. 34 virtual const nsCString Origin() const = 0; 35 36 // The origin attributes suffix alone, this is usually passed as an 37 // |aOriginSuffix| argument to various methods 38 virtual const nsCString& OriginSuffix() const = 0; 39 40 // The origin in the database usage format (reversed) and without the suffix 41 virtual const nsCString& OriginNoSuffix() const = 0; 42 43 // Whether the cache is already fully loaded 44 virtual bool Loaded() = 0; 45 46 // How many items has so far been loaded into the cache, used 47 // for optimization purposes 48 virtual uint32_t LoadedCount() = 0; 49 50 // Called by the database to load a key and its value to the cache 51 virtual bool LoadItem(const nsAString& aKey, const nsAString& aValue) = 0; 52 53 // Called by the database after all keys and values has been loaded 54 // to this cache 55 virtual void LoadDone(nsresult aRv) = 0; 56 57 // Use to synchronously wait until the cache gets fully loaded with data, 58 // this method exits after LoadDone has been called 59 virtual void LoadWait() = 0; 60 61 protected: 62 virtual ~LocalStorageCacheBridge() = default; 63 64 ThreadSafeAutoRefCnt mRefCnt; 65 NS_DECL_OWNINGTHREAD 66 }; 67 68 // Implementation of scope cache that is responsible for preloading data 69 // for persistent storage (localStorage) and hold data for non-private, 70 // private and session-only cookie modes. It is also responsible for 71 // persisting data changes using the database, works as a write-back cache. 72 class LocalStorageCache : public LocalStorageCacheBridge { 73 public: 74 void AssertIsOnOwningThread() const { NS_ASSERT_OWNINGTHREAD(LocalStorage); } 75 76 void SetActor(LocalStorageCacheChild* aActor); 77 78 void ClearActor() { 79 AssertIsOnOwningThread(); 80 81 mActor = nullptr; 82 } 83 84 NS_IMETHOD_(void) Release(void) override; 85 86 enum MutationSource { 87 // The mutation is a result of an explicit JS mutation in this process. 88 // The mutation should be sent to the sDatabase. Quota will be checked and 89 // QuotaExceededError may be returned without the mutation being applied. 90 ContentMutation, 91 // The mutation initially was triggered in a different process and is being 92 // propagated to this cache via LocalStorage::ApplyEvent. The mutation 93 // should 94 // not be sent to the sDatabase because the originating process is already 95 // doing that. (In addition to the redundant writes being wasteful, there 96 // is the potential for other processes to see inconsistent state from the 97 // database while preloading.) Quota will be updated but not checked 98 // because it's assumed it was checked in another process and data-coherency 99 // is more important than slightly exceeding quota. 100 E10sPropagated 101 }; 102 103 // Note: We pass aOriginNoSuffix through the ctor here, because 104 // LocalStorageCacheHashKey's ctor is creating this class and 105 // accepts reversed-origin-no-suffix as an argument - the hashing key. 106 explicit LocalStorageCache(const nsACString* aOriginNoSuffix); 107 108 protected: 109 virtual ~LocalStorageCache(); 110 111 public: 112 void Init(LocalStorageManager* aManager, bool aPersistent, 113 nsIPrincipal* aPrincipal, const nsACString& aQuotaOriginScope); 114 115 // Get size of per-origin data. 116 int64_t GetOriginQuotaUsage(const LocalStorage* aStorage) const; 117 118 // Starts async preload of this cache if it persistent and not loaded. 119 void Preload(); 120 121 // The set of methods that are invoked by DOM storage web API. 122 // We are passing the LocalStorage object just to let the cache 123 // read properties like mPrivate and mSessionOnly. 124 // Get* methods return error when load from the database has failed. 125 nsresult GetLength(const LocalStorage* aStorage, uint32_t* aRetval); 126 nsresult GetKey(const LocalStorage* aStorage, uint32_t index, 127 nsAString& aRetval); 128 nsresult GetItem(const LocalStorage* aStorage, const nsAString& aKey, 129 nsAString& aRetval); 130 nsresult SetItem(const LocalStorage* aStorage, const nsAString& aKey, 131 const nsAString& aValue, nsString& aOld, 132 const MutationSource aSource = ContentMutation); 133 nsresult RemoveItem(const LocalStorage* aStorage, const nsAString& aKey, 134 nsString& aOld, 135 const MutationSource aSource = ContentMutation); 136 nsresult Clear(const LocalStorage* aStorage, 137 const MutationSource aSource = ContentMutation); 138 139 void GetKeys(const LocalStorage* aStorage, nsTArray<nsString>& aKeys); 140 141 // LocalStorageCacheBridge 142 143 const nsCString Origin() const override; 144 const nsCString& OriginNoSuffix() const override { return mOriginNoSuffix; } 145 const nsCString& OriginSuffix() const override { return mOriginSuffix; } 146 bool Loaded() override { return mLoaded; } 147 uint32_t LoadedCount() override; 148 bool LoadItem(const nsAString& aKey, const nsAString& aValue) override; 149 void LoadDone(nsresult aRv) override; 150 void LoadWait() override; 151 152 // Cache keeps 3 sets of data: regular, private and session-only. 153 // This class keeps keys and values for a set and also caches 154 // size of the data for quick per-origin quota checking. 155 class Data { 156 public: 157 Data() : mOriginQuotaUsage(0) {} 158 int64_t mOriginQuotaUsage; 159 nsTHashMap<nsStringHashKey, nsString> mKeys; 160 }; 161 162 public: 163 // Number of data sets we keep: default, session 164 static const uint32_t kDataSetCount = 2; 165 166 private: 167 // API to clear the cache data, this is invoked by chrome operations 168 // like cookie deletion. 169 friend class LocalStorageManager; 170 171 static const uint32_t kUnloadDefault = 1 << 0; 172 static const uint32_t kUnloadSession = 1 << 1; 173 static const uint32_t kUnloadComplete = kUnloadDefault | kUnloadSession; 174 175 #ifdef DOM_STORAGE_TESTS 176 static const uint32_t kTestReload = 1 << 15; 177 #endif 178 179 void UnloadItems(uint32_t aUnloadFlags); 180 181 private: 182 // Synchronously blocks until the cache is fully loaded from the database 183 void WaitForPreload(); 184 185 // Helper to get one of the 3 data sets (regular, private, session) 186 Data& DataSet(const LocalStorage* aStorage); 187 188 // Used for firing storage events and synchronization of caches in other 189 // content processes. 190 void NotifyObservers(const LocalStorage* aStorage, const nsAString& aKey, 191 const nsAString& aOldValue, const nsAString& aNewValue); 192 193 // Whether the storage change is about to persist 194 bool Persist(const LocalStorage* aStorage) const; 195 196 // Changes the quota usage on the given data set if it fits the quota. 197 // If not, then false is returned and no change to the set must be done. 198 // A special case is if aSource==E10sPropagated, then we will return true even 199 // if the change would put us over quota. This is done to ensure coherency of 200 // caches between processes in the face of races. It does allow an attacker 201 // to potentially use N multiples of the quota storage limit if they can 202 // arrange for their origin to execute code in N processes. However, this is 203 // not considered a particularly concerning threat model because it's already 204 // very possible for a rogue page to attempt to intentionally fill up the 205 // user's storage through the use of multiple domains. 206 bool ProcessUsageDelta(uint32_t aGetDataSetIndex, const int64_t aDelta, 207 const MutationSource aSource = ContentMutation); 208 bool ProcessUsageDelta(const LocalStorage* aStorage, const int64_t aDelta, 209 const MutationSource aSource = ContentMutation); 210 211 private: 212 // When a cache is reponsible for its life time (in case of localStorage data 213 // cache) we need to refer our manager since removal of the cache from the 214 // hash table is handled in the destructor by call to the manager. Cache 215 // could potentially overlive the manager, hence the hard ref. 216 RefPtr<LocalStorageManager> mManager; 217 218 // Reference to the usage counter object we check on for eTLD+1 quota limit. 219 // Obtained from the manager during initialization (Init method). 220 RefPtr<StorageUsage> mUsage; 221 222 // The LocalStorageCacheChild is created at the same time of this class. 223 // In normal operation, the actor will be synchronously cleared in our 224 // destructor when we tell it to delete itself. In a shutdown-related edge 225 // case in the parent process for JSM's, it is possible for the actor to be 226 // destroyed while this class remains alive, in which case it will be nulled 227 // out. 228 LocalStorageCacheChild* mActor; 229 230 // The origin this cache belongs to in the "DB format", i.e. reversed 231 nsCString mOriginNoSuffix; 232 233 // The origin attributes suffix 234 nsCString mOriginSuffix; 235 236 // The eTLD+1 scope used to count quota usage. It is in the reversed format 237 // and contains the origin attributes suffix. 238 nsCString mQuotaOriginScope; 239 240 // Non-private Browsing, Private Browsing and Session Only sets. 241 Data mData[kDataSetCount]; 242 243 // This monitor is used to wait for full load of data. 244 mozilla::Monitor mMonitor MOZ_UNANNOTATED; 245 246 // Flag that is initially false. When the cache is about to work with 247 // the database (i.e. it is persistent) this flags is set to true after 248 // all keys and coresponding values are loaded from the database. 249 // This flag never goes from true back to false. Since this flag is 250 // critical for mData hashtable synchronization, it's made atomic. 251 Atomic<bool, ReleaseAcquire> mLoaded; 252 253 // Result of load from the database. Valid after mLoaded flag has been set. 254 nsresult mLoadResult; 255 256 // Expected to be only 0 or 1. 257 uint32_t mPrivateBrowsingId; 258 259 // Init() method has been called 260 bool mInitialized : 1; 261 262 // This cache is about to be bound with the database (i.e. it has 263 // to load from the DB first and has to persist when modifying the 264 // default data set.) 265 bool mPersistent : 1; 266 267 // Whether we have already captured state of the cache preload on our first 268 // access. 269 bool mPreloadTelemetryRecorded : 1; 270 }; 271 272 // StorageUsage 273 // Infrastructure to manage and check eTLD+1 quota 274 class StorageUsageBridge { 275 public: 276 NS_INLINE_DECL_THREADSAFE_VIRTUAL_REFCOUNTING(StorageUsageBridge) 277 278 virtual const nsCString& OriginScope() = 0; 279 virtual void LoadUsage(const int64_t aUsage) = 0; 280 281 protected: 282 // Protected destructor, to discourage deletion outside of Release(): 283 virtual ~StorageUsageBridge() = default; 284 }; 285 286 class StorageUsage : public StorageUsageBridge { 287 public: 288 explicit StorageUsage(const nsACString& aOriginScope); 289 290 bool CheckAndSetETLD1UsageDelta( 291 uint32_t aDataSetIndex, int64_t aUsageDelta, 292 const LocalStorageCache::MutationSource aSource); 293 294 private: 295 const nsCString& OriginScope() override { return mOriginScope; } 296 void LoadUsage(const int64_t aUsage) override; 297 298 nsCString mOriginScope; 299 int64_t mUsage[LocalStorageCache::kDataSetCount]; 300 }; 301 302 } // namespace mozilla::dom 303 304 #endif // mozilla_dom_LocalStorageCache_h