LocalStorageManager.cpp (14274B)
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 #include "LocalStorageManager.h" 8 9 #include "LocalStorage.h" 10 #include "StorageDBThread.h" 11 #include "StorageIPC.h" 12 #include "StorageUtils.h" 13 #include "mozilla/Services.h" 14 #include "mozilla/StaticPrefs_dom.h" 15 #include "mozilla/dom/LocalStorageCommon.h" 16 #include "mozilla/ipc/BackgroundChild.h" 17 #include "mozilla/ipc/PBackgroundChild.h" 18 #include "nsIEffectiveTLDService.h" 19 #include "nsIObserverService.h" 20 #include "nsNetCID.h" 21 #include "nsNetUtil.h" 22 #include "nsPIDOMWindow.h" 23 #include "nsPrintfCString.h" 24 #include "nsThreadUtils.h" 25 #include "nsXULAppAPI.h" 26 27 namespace mozilla::dom { 28 29 using namespace StorageUtils; 30 31 LocalStorageManager* LocalStorageManager::sSelf = nullptr; 32 33 // static 34 uint32_t LocalStorageManager::GetOriginQuota() { 35 return StaticPrefs::dom_storage_default_quota() * 1024; // pref is in kBs 36 } 37 38 // static 39 uint32_t LocalStorageManager::GetSiteQuota() { 40 return std::max(StaticPrefs::dom_storage_default_quota(), 41 StaticPrefs::dom_storage_default_site_quota()) * 42 1024; // pref is in kBs 43 } 44 45 NS_IMPL_ISUPPORTS(LocalStorageManager, nsIDOMStorageManager, 46 nsILocalStorageManager) 47 48 LocalStorageManager::LocalStorageManager() : mCaches(8) { 49 MOZ_ASSERT(!NextGenLocalStorageEnabled()); 50 51 StorageObserver* observer = StorageObserver::Self(); 52 NS_ASSERTION( 53 observer, 54 "No StorageObserver, cannot observe private data delete notifications!"); 55 56 if (observer) { 57 observer->AddSink(this); 58 } 59 60 NS_ASSERTION(!sSelf, 61 "Somebody is trying to " 62 "do_CreateInstance(\"@mozilla/dom/localStorage-manager;1\""); 63 sSelf = this; 64 65 if (!XRE_IsParentProcess()) { 66 // Do this only on the child process. The thread IPC bridge 67 // is also used to communicate chrome observer notifications. 68 // Note: must be called after we set sSelf 69 for (const uint32_t id : {0, 1}) { 70 StorageDBChild::GetOrCreate(id); 71 } 72 } 73 } 74 75 LocalStorageManager::~LocalStorageManager() { 76 StorageObserver* observer = StorageObserver::Self(); 77 if (observer) { 78 observer->RemoveSink(this); 79 } 80 81 sSelf = nullptr; 82 } 83 84 // static 85 nsAutoCString LocalStorageManager::CreateOrigin( 86 const nsACString& aOriginSuffix, const nsACString& aOriginNoSuffix) { 87 // Note: some hard-coded sqlite statements are dependent on the format this 88 // method returns. Changing this without updating those sqlite statements 89 // will cause malfunction. 90 91 nsAutoCString scope; 92 scope.Append(aOriginSuffix); 93 scope.Append(':'); 94 scope.Append(aOriginNoSuffix); 95 return scope; 96 } 97 98 LocalStorageCache* LocalStorageManager::GetCache( 99 const nsACString& aOriginSuffix, const nsACString& aOriginNoSuffix) { 100 CacheOriginHashtable* table = mCaches.GetOrInsertNew(aOriginSuffix); 101 LocalStorageCacheHashKey* entry = table->GetEntry(aOriginNoSuffix); 102 if (!entry) { 103 return nullptr; 104 } 105 106 return entry->cache(); 107 } 108 109 already_AddRefed<StorageUsage> LocalStorageManager::GetOriginUsage( 110 const nsACString& aOriginNoSuffix, const uint32_t aPrivateBrowsingId) { 111 return do_AddRef(mUsages.LookupOrInsertWith(aOriginNoSuffix, [&] { 112 auto usage = MakeRefPtr<StorageUsage>(aOriginNoSuffix); 113 114 StorageDBChild* storageChild = 115 StorageDBChild::GetOrCreate(aPrivateBrowsingId); 116 if (storageChild) { 117 storageChild->AsyncGetUsage(usage); 118 } 119 120 return usage; 121 })); 122 } 123 124 already_AddRefed<LocalStorageCache> LocalStorageManager::PutCache( 125 const nsACString& aOriginSuffix, const nsACString& aOriginNoSuffix, 126 const nsACString& aQuotaKey, nsIPrincipal* aPrincipal) { 127 CacheOriginHashtable* table = mCaches.GetOrInsertNew(aOriginSuffix); 128 LocalStorageCacheHashKey* entry = table->PutEntry(aOriginNoSuffix); 129 RefPtr<LocalStorageCache> cache = entry->cache(); 130 131 // Lifetime handled by the cache, do persist 132 cache->Init(this, true, aPrincipal, aQuotaKey); 133 return cache.forget(); 134 } 135 136 void LocalStorageManager::DropCache(LocalStorageCache* aCache) { 137 if (!NS_IsMainThread()) { 138 NS_WARNING( 139 "StorageManager::DropCache called on a non-main thread, shutting " 140 "down?"); 141 } 142 143 CacheOriginHashtable* table = mCaches.GetOrInsertNew(aCache->OriginSuffix()); 144 table->RemoveEntry(aCache->OriginNoSuffix()); 145 } 146 147 nsresult LocalStorageManager::GetStorageInternal( 148 CreateMode aCreateMode, mozIDOMWindow* aWindow, nsIPrincipal* aPrincipal, 149 nsIPrincipal* aStoragePrincipal, const nsAString& aDocumentURI, 150 bool aPrivate, Storage** aRetval) { 151 nsAutoCString originAttrSuffix; 152 nsAutoCString originKey; 153 nsAutoCString quotaKey; 154 155 aStoragePrincipal->OriginAttributesRef().CreateSuffix(originAttrSuffix); 156 157 nsresult rv = aStoragePrincipal->GetStorageOriginKey(originKey); 158 if (NS_WARN_IF(NS_FAILED(rv))) { 159 return NS_ERROR_NOT_AVAILABLE; 160 } 161 162 rv = aStoragePrincipal->GetLocalStorageQuotaKey(quotaKey); 163 if (NS_WARN_IF(NS_FAILED(rv))) { 164 return NS_ERROR_NOT_AVAILABLE; 165 } 166 167 RefPtr<LocalStorageCache> cache = GetCache(originAttrSuffix, originKey); 168 169 // Get or create a cache for the given scope 170 if (!cache) { 171 if (aCreateMode == CreateMode::UseIfExistsNeverCreate) { 172 *aRetval = nullptr; 173 return NS_OK; 174 } 175 176 if (aCreateMode == CreateMode::CreateIfShouldPreload) { 177 const uint32_t privateBrowsingId = 178 aStoragePrincipal->GetPrivateBrowsingId(); 179 180 // This is a demand to just preload the cache, if the scope has 181 // no data stored, bypass creation and preload of the cache. 182 StorageDBChild* db = StorageDBChild::Get(privateBrowsingId); 183 if (db) { 184 if (!db->ShouldPreloadOrigin(LocalStorageManager::CreateOrigin( 185 originAttrSuffix, originKey))) { 186 return NS_OK; 187 } 188 } else { 189 if (originKey.EqualsLiteral("knalb.:about")) { 190 return NS_OK; 191 } 192 } 193 } 194 195 #if !defined(MOZ_WIDGET_ANDROID) 196 ::mozilla::ipc::PBackgroundChild* backgroundActor = 197 ::mozilla::ipc::BackgroundChild::GetOrCreateForCurrentThread(); 198 if (NS_WARN_IF(!backgroundActor)) { 199 return NS_ERROR_FAILURE; 200 } 201 202 ::mozilla::ipc::PrincipalInfo principalInfo; 203 rv = mozilla::ipc::PrincipalToPrincipalInfo(aStoragePrincipal, 204 &principalInfo); 205 if (NS_WARN_IF(NS_FAILED(rv))) { 206 return rv; 207 } 208 209 uint32_t privateBrowsingId; 210 rv = aStoragePrincipal->GetPrivateBrowsingId(&privateBrowsingId); 211 if (NS_WARN_IF(NS_FAILED(rv))) { 212 return rv; 213 } 214 215 if (!backgroundActor->CanSend()) { 216 return NS_ERROR_FAILURE; 217 } 218 #endif 219 220 // There is always a single instance of a cache per scope 221 // in a single instance of a DOM storage manager. 222 cache = PutCache(originAttrSuffix, originKey, quotaKey, aStoragePrincipal); 223 224 #if !defined(MOZ_WIDGET_ANDROID) 225 LocalStorageCacheChild* actor = new LocalStorageCacheChild(cache); 226 227 MOZ_ALWAYS_TRUE( 228 backgroundActor->SendPBackgroundLocalStorageCacheConstructor( 229 actor, principalInfo, originKey, privateBrowsingId)); 230 231 cache->SetActor(actor); 232 #endif 233 } 234 235 if (aRetval) { 236 nsCOMPtr<nsPIDOMWindowInner> inner = nsPIDOMWindowInner::From(aWindow); 237 238 RefPtr<Storage> storage = 239 new LocalStorage(inner, this, cache, aDocumentURI, aPrincipal, 240 aStoragePrincipal, aPrivate); 241 storage.forget(aRetval); 242 } 243 244 return NS_OK; 245 } 246 247 NS_IMETHODIMP 248 LocalStorageManager::PrecacheStorage(nsIPrincipal* aPrincipal, 249 nsIPrincipal* aStoragePrincipal, 250 Storage** aRetval) { 251 return GetStorageInternal(CreateMode::CreateIfShouldPreload, nullptr, 252 aPrincipal, aStoragePrincipal, u""_ns, false, 253 aRetval); 254 } 255 256 NS_IMETHODIMP 257 LocalStorageManager::CreateStorage(mozIDOMWindow* aWindow, 258 nsIPrincipal* aPrincipal, 259 nsIPrincipal* aStoragePrincipal, 260 const nsAString& aDocumentURI, bool aPrivate, 261 Storage** aRetval) { 262 return GetStorageInternal(CreateMode::CreateAlways, aWindow, aPrincipal, 263 aStoragePrincipal, aDocumentURI, aPrivate, aRetval); 264 } 265 266 NS_IMETHODIMP 267 LocalStorageManager::GetStorage(mozIDOMWindow* aWindow, 268 nsIPrincipal* aPrincipal, 269 nsIPrincipal* aStoragePrincipal, bool aPrivate, 270 Storage** aRetval) { 271 return GetStorageInternal(CreateMode::UseIfExistsNeverCreate, aWindow, 272 aPrincipal, aStoragePrincipal, u""_ns, aPrivate, 273 aRetval); 274 } 275 276 NS_IMETHODIMP 277 LocalStorageManager::CloneStorage(Storage* aStorage) { 278 // Cloning is supported only for sessionStorage 279 return NS_ERROR_NOT_IMPLEMENTED; 280 } 281 282 NS_IMETHODIMP 283 LocalStorageManager::CheckStorage(nsIPrincipal* aPrincipal, Storage* aStorage, 284 bool* aRetval) { 285 MOZ_ASSERT(NS_IsMainThread()); 286 MOZ_ASSERT(aPrincipal); 287 MOZ_ASSERT(aStorage); 288 MOZ_ASSERT(aRetval); 289 290 // Only used by sessionStorage. 291 return NS_ERROR_NOT_IMPLEMENTED; 292 } 293 294 NS_IMETHODIMP 295 LocalStorageManager::GetNextGenLocalStorageEnabled(bool* aResult) { 296 MOZ_ASSERT(NS_IsMainThread()); 297 MOZ_ASSERT(aResult); 298 299 *aResult = NextGenLocalStorageEnabled(); 300 return NS_OK; 301 } 302 303 NS_IMETHODIMP 304 LocalStorageManager::Preload(nsIPrincipal* aPrincipal, JSContext* aContext, 305 Promise** _retval) { 306 MOZ_ASSERT(NS_IsMainThread()); 307 MOZ_ASSERT(aPrincipal); 308 MOZ_ASSERT(_retval); 309 310 return NS_ERROR_NOT_IMPLEMENTED; 311 } 312 313 NS_IMETHODIMP 314 LocalStorageManager::IsPreloaded(nsIPrincipal* aPrincipal, JSContext* aContext, 315 Promise** _retval) { 316 MOZ_ASSERT(NS_IsMainThread()); 317 MOZ_ASSERT(aPrincipal); 318 MOZ_ASSERT(_retval); 319 320 return NS_ERROR_NOT_IMPLEMENTED; 321 } 322 323 NS_IMETHODIMP 324 LocalStorageManager::GetState(nsIPrincipal* aPrincipal, JSContext* aContext, 325 Promise** _retval) { 326 MOZ_ASSERT(NS_IsMainThread()); 327 MOZ_ASSERT(aPrincipal); 328 MOZ_ASSERT(_retval); 329 330 return NS_ERROR_NOT_IMPLEMENTED; 331 } 332 333 void LocalStorageManager::ClearCaches(uint32_t aUnloadFlags, 334 const OriginAttributesPattern& aPattern, 335 const nsACString& aOriginScope) { 336 for (const auto& cacheEntry : mCaches) { 337 OriginAttributes oa; 338 DebugOnly<bool> rv = oa.PopulateFromSuffix(cacheEntry.GetKey()); 339 MOZ_ASSERT(rv); 340 if (!aPattern.Matches(oa)) { 341 // This table doesn't match the given origin attributes pattern 342 continue; 343 } 344 345 CacheOriginHashtable* table = cacheEntry.GetWeak(); 346 347 for (auto iter2 = table->Iter(); !iter2.Done(); iter2.Next()) { 348 LocalStorageCache* cache = iter2.Get()->cache(); 349 350 if (aOriginScope.IsEmpty() || 351 StringBeginsWith(cache->OriginNoSuffix(), aOriginScope)) { 352 cache->UnloadItems(aUnloadFlags); 353 } 354 } 355 } 356 } 357 358 nsresult LocalStorageManager::Observe(const char* aTopic, 359 const nsAString& aOriginAttributesPattern, 360 const nsACString& aOriginScope) { 361 OriginAttributesPattern pattern; 362 if (!pattern.Init(aOriginAttributesPattern)) { 363 NS_ERROR("Cannot parse origin attributes pattern"); 364 return NS_ERROR_FAILURE; 365 } 366 367 // Clear everything, caches + database 368 if (!strcmp(aTopic, "cookie-cleared")) { 369 ClearCaches(LocalStorageCache::kUnloadComplete, pattern, ""_ns); 370 return NS_OK; 371 } 372 373 // Clear everything, caches + database 374 if (!strcmp(aTopic, "extension:purge-localStorage-caches")) { 375 ClearCaches(LocalStorageCache::kUnloadComplete, pattern, aOriginScope); 376 return NS_OK; 377 } 378 379 if (!strcmp(aTopic, "browser:purge-sessionStorage") || 380 !strcmp(aTopic, "extension:purge-sessionStorage")) { 381 // This is only meant for SessionStorageManager. 382 return NS_OK; 383 } 384 385 // Clear from caches everything that has been stored 386 // while in session-only mode 387 if (!strcmp(aTopic, "session-only-cleared")) { 388 ClearCaches(LocalStorageCache::kUnloadSession, pattern, aOriginScope); 389 return NS_OK; 390 } 391 392 // Clear all private-browsing caches 393 if (!strcmp(aTopic, "private-browsing-data-cleared")) { 394 ClearCaches(LocalStorageCache::kUnloadComplete, pattern, ""_ns); 395 return NS_OK; 396 } 397 398 // Clear localStorage data belonging to an origin pattern 399 if (!strcmp(aTopic, "clear-origin-attributes-data") || 400 !strcmp(aTopic, "dom-storage:clear-origin-attributes-data")) { 401 ClearCaches(LocalStorageCache::kUnloadComplete, pattern, ""_ns); 402 return NS_OK; 403 } 404 405 if (!strcmp(aTopic, "profile-change")) { 406 // For case caches are still referenced - clear them completely 407 ClearCaches(LocalStorageCache::kUnloadComplete, pattern, ""_ns); 408 mCaches.Clear(); 409 return NS_OK; 410 } 411 412 #ifdef DOM_STORAGE_TESTS 413 if (!strcmp(aTopic, "test-reload")) { 414 // This immediately completely reloads all caches from the database. 415 ClearCaches(LocalStorageCache::kTestReload, pattern, ""_ns); 416 return NS_OK; 417 } 418 419 if (!strcmp(aTopic, "test-flushed")) { 420 if (!XRE_IsParentProcess()) { 421 nsCOMPtr<nsIObserverService> obs = 422 mozilla::services::GetObserverService(); 423 if (obs) { 424 obs->NotifyObservers(nullptr, "domstorage-test-flushed", nullptr); 425 } 426 } 427 428 return NS_OK; 429 } 430 #endif 431 432 NS_ERROR("Unexpected topic"); 433 return NS_ERROR_UNEXPECTED; 434 } 435 436 // static 437 LocalStorageManager* LocalStorageManager::Self() { 438 MOZ_ASSERT(!NextGenLocalStorageEnabled()); 439 440 return sSelf; 441 } 442 443 LocalStorageManager* LocalStorageManager::Ensure() { 444 MOZ_ASSERT(!NextGenLocalStorageEnabled()); 445 446 if (sSelf) { 447 return sSelf; 448 } 449 450 // Cause sSelf to be populated. 451 nsCOMPtr<nsIDOMStorageManager> initializer = 452 do_GetService("@mozilla.org/dom/localStorage-manager;1"); 453 MOZ_ASSERT(sSelf, "Didn't initialize?"); 454 455 return sSelf; 456 } 457 458 } // namespace mozilla::dom