tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

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