tor-browser

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

SessionStorageManager.cpp (30504B)


      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 "SessionStorageManager.h"
      8 
      9 #include "SessionStorage.h"
     10 #include "SessionStorageCache.h"
     11 #include "SessionStorageObserver.h"
     12 #include "StorageIPC.h"
     13 #include "StorageUtils.h"
     14 #include "mozilla/ClearOnShutdown.h"
     15 #include "mozilla/OriginAttributes.h"
     16 #include "mozilla/PrincipalHashKey.h"
     17 #include "mozilla/ScopeExit.h"
     18 #include "mozilla/StoragePrincipalHelper.h"
     19 #include "mozilla/dom/CanonicalBrowsingContext.h"
     20 #include "mozilla/dom/ContentChild.h"
     21 #include "mozilla/dom/LocalStorageCommon.h"
     22 #include "mozilla/dom/PBackgroundSessionStorageCache.h"
     23 #include "mozilla/dom/PBackgroundSessionStorageManager.h"
     24 #include "mozilla/dom/PermissionMessageUtils.h"
     25 #include "mozilla/dom/WindowGlobalParent.h"
     26 #include "mozilla/ipc/BackgroundChild.h"
     27 #include "mozilla/ipc/BackgroundParent.h"
     28 #include "mozilla/ipc/PBackgroundChild.h"
     29 #include "nsIXULRuntime.h"
     30 #include "nsTHashMap.h"
     31 #include "nsThreadUtils.h"
     32 
     33 namespace mozilla::dom {
     34 
     35 using namespace StorageUtils;
     36 
     37 static bool ExactDomainMatch(const nsACString& aOriginKey,
     38                             const nsACString& aOriginScope) {
     39  // aOriginScope is reversed domain with trailing dot.
     40  // e.g: example.com => moc.elpmaxe. (see StorageUtils.cpp)
     41  // aOriginKey is reversed domain with trailing dot, plus ":",
     42  // scheme, and optional port. e.g: moc.elpmaxe.:http:80 (see
     43  // https://searchfox.org/firefox-main/rev/987f566373ea82403c5c1235b219bd9e7d56a4aa/caps/BasePrincipal.cpp#1539)
     44  // To check if it is an exact match, we need to ensure that aOriginKey starts
     45  // with aOriginScope and the first character immediately following the
     46  // matching part is ":". i.e: Domain part of aOriginKey is identical to
     47  // aOriginScope.
     48  return StringBeginsWith(aOriginKey, aOriginScope) &&
     49         aOriginKey.CharAt(aOriginScope.Length()) == ':';
     50 }
     51 
     52 // Parent process, background thread hashmap that stores top context id and
     53 // manager pair.
     54 static StaticAutoPtr<
     55    nsRefPtrHashtable<nsUint64HashKey, BackgroundSessionStorageManager>>
     56    sManagers;
     57 
     58 bool RecvShutdownBackgroundSessionStorageManagers() {
     59  ::mozilla::ipc::AssertIsOnBackgroundThread();
     60 
     61  sManagers = nullptr;
     62  return true;
     63 }
     64 
     65 void RecvPropagateBackgroundSessionStorageManager(
     66    uint64_t aCurrentTopContextId, uint64_t aTargetTopContextId) {
     67  ::mozilla::ipc::AssertIsOnBackgroundThread();
     68 
     69  if (sManagers) {
     70    if (RefPtr<BackgroundSessionStorageManager> mgr =
     71            sManagers->Get(aCurrentTopContextId)) {
     72      mgr->MaybeDispatchSessionStoreUpdate();
     73      mgr->SetCurrentBrowsingContextId(aTargetTopContextId);
     74      // Because of bfcache, we may re-register aTargetTopContextId in
     75      // CanonicalBrowsingContext::ReplacedBy.
     76      // XXXBFCache do we want to tweak this behavior and ensure this is
     77      // called only once?
     78      sManagers->InsertOrUpdate(aTargetTopContextId, std::move(mgr));
     79    }
     80  }
     81 }
     82 
     83 bool RecvRemoveBackgroundSessionStorageManager(uint64_t aTopContextId) {
     84  ::mozilla::ipc::AssertIsOnBackgroundThread();
     85 
     86  if (sManagers) {
     87    RefPtr<BackgroundSessionStorageManager> mgr;
     88    sManagers->Remove(aTopContextId, getter_AddRefs(mgr));
     89 
     90    if (mgr) {
     91      mgr->CancelSessionStoreUpdate();
     92    }
     93  }
     94 
     95  return true;
     96 }
     97 
     98 bool RecvLoadSessionStorageData(
     99    uint64_t aTopContextId,
    100    nsTArray<mozilla::dom::SSCacheCopy>&& aCacheCopyList) {
    101  if (aCacheCopyList.IsEmpty()) {
    102    return true;
    103  }
    104 
    105  RefPtr<BackgroundSessionStorageManager> manager =
    106      BackgroundSessionStorageManager::GetOrCreate(aTopContextId);
    107 
    108  if (!manager) {
    109    return true;
    110  }
    111 
    112  for (const auto& cacheInit : aCacheCopyList) {
    113    OriginAttributes attrs;
    114    StoragePrincipalHelper::GetOriginAttributes(cacheInit.principalInfo(),
    115                                                attrs);
    116 
    117    nsAutoCString originAttrs;
    118    attrs.CreateSuffix(originAttrs);
    119 
    120    manager->UpdateData(originAttrs, cacheInit.originKey(), cacheInit.data());
    121  }
    122 
    123  return true;
    124 }
    125 
    126 bool RecvGetSessionStorageData(
    127    uint64_t aTopContextId, uint32_t aSizeLimit, bool aCancelSessionStoreTimer,
    128    ::mozilla::ipc::PBackgroundParent::GetSessionStorageManagerDataResolver&&
    129        aResolver) {
    130  nsTArray<mozilla::dom::SSCacheCopy> data;
    131  auto resolve = MakeScopeExit([&]() { aResolver(std::move(data)); });
    132 
    133  if (!sManagers) {
    134    return true;
    135  }
    136 
    137  RefPtr<BackgroundSessionStorageManager> manager =
    138      sManagers->Get(aTopContextId);
    139  if (!manager) {
    140    return true;
    141  }
    142 
    143  if (aCancelSessionStoreTimer) {
    144    manager->CancelSessionStoreUpdate();
    145  }
    146 
    147  manager->GetData(aSizeLimit, data);
    148 
    149  return true;
    150 }
    151 
    152 bool RecvClearStoragesForOrigin(const nsACString& aOriginAttrs,
    153                                const nsACString& aOriginKey) {
    154  mozilla::ipc::AssertIsInMainProcess();
    155  mozilla::ipc::AssertIsOnBackgroundThread();
    156 
    157  if (!sManagers) {
    158    return true;
    159  }
    160 
    161  for (auto& entry : *sManagers) {
    162    entry.GetData()->ClearStoragesForOrigin(aOriginAttrs, aOriginKey);
    163  }
    164 
    165  return true;
    166 }
    167 
    168 void SessionStorageManagerBase::ClearStoragesInternal(
    169    const OriginAttributesPattern& aPattern, const nsACString& aOriginScope,
    170    DomainMatchingMode aMode) {
    171  const bool isExactMatch = aMode == DomainMatchingMode::EXACT_MATCH;
    172 
    173  for (const auto& oaEntry : mOATable) {
    174    OriginAttributes oa;
    175    DebugOnly<bool> ok = oa.PopulateFromSuffix(oaEntry.GetKey());
    176    MOZ_ASSERT(ok);
    177    if (!aPattern.Matches(oa)) {
    178      // This table doesn't match the given origin attributes pattern
    179      continue;
    180    }
    181 
    182    OriginKeyHashTable* table = oaEntry.GetWeak();
    183    for (const auto& originKeyEntry : *table) {
    184      if (aOriginScope.IsEmpty() ||
    185          (!isExactMatch &&
    186           StringBeginsWith(originKeyEntry.GetKey(), aOriginScope)) ||
    187          (isExactMatch &&
    188           ExactDomainMatch(originKeyEntry.GetKey(), aOriginScope))) {
    189        const auto cache = originKeyEntry.GetData()->mCache;
    190        cache->Clear(false);
    191        cache->ResetWriteInfos();
    192      }
    193    }
    194  }
    195 }
    196 
    197 void SessionStorageManagerBase::ClearStoragesForOriginInternal(
    198    const nsACString& aOriginAttrs, const nsACString& aOriginKey) {
    199  for (const auto& oaEntry : mOATable) {
    200    // Filter tables which match the given origin attrs.
    201    if (oaEntry.GetKey() != aOriginAttrs) {
    202      continue;
    203    }
    204 
    205    OriginKeyHashTable* table = oaEntry.GetWeak();
    206    for (const auto& originKeyEntry : *table) {
    207      // Match exact origin (without origin attrs).
    208      if (originKeyEntry.GetKey() != aOriginKey) {
    209        continue;
    210      }
    211 
    212      const auto cache = originKeyEntry.GetData()->mCache;
    213      cache->Clear(false);
    214      cache->ResetWriteInfos();
    215    }
    216  }
    217 }
    218 
    219 SessionStorageManagerBase::OriginRecord*
    220 SessionStorageManagerBase::GetOriginRecord(
    221    const nsACString& aOriginAttrs, const nsACString& aOriginKey,
    222    const bool aMakeIfNeeded, SessionStorageCache* const aCloneFrom) {
    223  // XXX It seems aMakeIfNeeded is always known at compile-time, so this could
    224  // be split into two functions.
    225 
    226  if (aMakeIfNeeded) {
    227    return mOATable.GetOrInsertNew(aOriginAttrs)
    228        ->LookupOrInsertWith(
    229            aOriginKey,
    230            [&] {
    231              auto newOriginRecord = MakeUnique<OriginRecord>();
    232              if (aCloneFrom) {
    233                newOriginRecord->mCache = aCloneFrom->Clone();
    234              } else {
    235                newOriginRecord->mCache = new SessionStorageCache();
    236              }
    237              return newOriginRecord;
    238            })
    239        .get();
    240  }
    241 
    242  auto* const table = mOATable.Get(aOriginAttrs);
    243  if (!table) return nullptr;
    244 
    245  return table->Get(aOriginKey);
    246 }
    247 
    248 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(SessionStorageManager)
    249  NS_INTERFACE_MAP_ENTRY(nsISupports)
    250  NS_INTERFACE_MAP_ENTRY(nsIDOMStorageManager)
    251  NS_INTERFACE_MAP_ENTRY(nsIDOMSessionStorageManager)
    252 NS_INTERFACE_MAP_END
    253 
    254 NS_IMPL_CYCLE_COLLECTION(SessionStorageManager, mBrowsingContext)
    255 NS_IMPL_CYCLE_COLLECTING_ADDREF(SessionStorageManager)
    256 NS_IMPL_CYCLE_COLLECTING_RELEASE(SessionStorageManager)
    257 
    258 SessionStorageManager::SessionStorageManager(
    259    RefPtr<BrowsingContext> aBrowsingContext)
    260    : mBrowsingContext(std::move(aBrowsingContext)), mActor(nullptr) {
    261  AssertIsOnMainThread();
    262 
    263  StorageObserver* observer = StorageObserver::Self();
    264  NS_ASSERTION(
    265      observer,
    266      "No StorageObserver, cannot observe private data delete notifications!");
    267 
    268  if (observer) {
    269    observer->AddSink(this);
    270  }
    271 
    272  if (!XRE_IsParentProcess() && NextGenLocalStorageEnabled()) {
    273    // When LSNG is enabled the thread IPC bridge doesn't exist, so we have to
    274    // create own protocol to distribute chrome observer notifications to
    275    // content processes.
    276    mObserver = SessionStorageObserver::Get();
    277 
    278    if (!mObserver) {
    279      ContentChild* contentActor = ContentChild::GetSingleton();
    280      MOZ_ASSERT(contentActor);
    281 
    282      RefPtr<SessionStorageObserver> observer = new SessionStorageObserver();
    283 
    284      SessionStorageObserverChild* actor =
    285          new SessionStorageObserverChild(observer);
    286 
    287      MOZ_ALWAYS_TRUE(
    288          contentActor->SendPSessionStorageObserverConstructor(actor));
    289 
    290      observer->SetActor(actor);
    291 
    292      mObserver = std::move(observer);
    293    }
    294  }
    295 }
    296 
    297 SessionStorageManager::~SessionStorageManager() {
    298  StorageObserver* observer = StorageObserver::Self();
    299  if (observer) {
    300    observer->RemoveSink(this);
    301  }
    302 
    303  if (mActor) {
    304    mActor->SendDeleteMeInternal();
    305    MOZ_ASSERT(!mActor, "SendDeleteMeInternal should have cleared!");
    306  }
    307 }
    308 
    309 bool SessionStorageManager::CanLoadData() {
    310  AssertIsOnMainThread();
    311 
    312  return mBrowsingContext && !mBrowsingContext->IsDiscarded();
    313 }
    314 
    315 void SessionStorageManager::SetActor(SessionStorageManagerChild* aActor) {
    316  AssertIsOnMainThread();
    317  MOZ_ASSERT(aActor);
    318  MOZ_ASSERT(!mActor);
    319 
    320  mActor = aActor;
    321 }
    322 
    323 bool SessionStorageManager::ActorExists() const {
    324  AssertIsOnMainThread();
    325 
    326  return mActor;
    327 }
    328 
    329 void SessionStorageManager::ClearActor() {
    330  AssertIsOnMainThread();
    331  MOZ_ASSERT(mActor);
    332 
    333  mActor = nullptr;
    334 }
    335 
    336 nsresult SessionStorageManager::EnsureManager() {
    337  AssertIsOnMainThread();
    338  MOZ_ASSERT(CanLoadData());
    339 
    340  if (ActorExists()) {
    341    return NS_OK;
    342  }
    343 
    344  ::mozilla::ipc::PBackgroundChild* backgroundActor =
    345      ::mozilla::ipc::BackgroundChild::GetOrCreateForCurrentThread();
    346  if (NS_WARN_IF(!backgroundActor)) {
    347    return NS_ERROR_FAILURE;
    348  }
    349 
    350  RefPtr<SessionStorageManagerChild> actor =
    351      new SessionStorageManagerChild(this);
    352 
    353  if (!backgroundActor->SendPBackgroundSessionStorageManagerConstructor(
    354          actor, mBrowsingContext->Top()->Id())) {
    355    return NS_ERROR_FAILURE;
    356  }
    357 
    358  SetActor(actor);
    359 
    360  return NS_OK;
    361 }
    362 
    363 SessionStorageCacheChild* SessionStorageManager::EnsureCache(
    364    nsIPrincipal& aPrincipal, const nsACString& aOriginKey,
    365    SessionStorageCache& aCache) {
    366  AssertIsOnMainThread();
    367  MOZ_ASSERT(CanLoadData());
    368  MOZ_ASSERT(ActorExists());
    369 
    370  if (aCache.Actor()) {
    371    return aCache.Actor();
    372  }
    373 
    374  mozilla::ipc::PrincipalInfo info;
    375  nsresult rv = PrincipalToPrincipalInfo(&aPrincipal, &info);
    376 
    377  if (NS_FAILED(rv)) {
    378    return nullptr;
    379  }
    380 
    381  RefPtr<SessionStorageCacheChild> actor =
    382      new SessionStorageCacheChild(&aCache);
    383  if (!mActor->SendPBackgroundSessionStorageCacheConstructor(actor, info,
    384                                                             aOriginKey)) {
    385    return nullptr;
    386  }
    387 
    388  aCache.SetActor(actor);
    389 
    390  return actor;
    391 }
    392 
    393 nsresult SessionStorageManager::LoadData(nsIPrincipal& aPrincipal,
    394                                         SessionStorageCache& aCache) {
    395  AssertIsOnMainThread();
    396  MOZ_ASSERT(mActor);
    397 
    398  nsAutoCString originKey;
    399  nsresult rv = aPrincipal.GetStorageOriginKey(originKey);
    400  if (NS_WARN_IF(NS_FAILED(rv))) {
    401    return rv;
    402  }
    403 
    404  nsAutoCString originAttributes;
    405  aPrincipal.OriginAttributesRef().CreateSuffix(originAttributes);
    406 
    407  auto* const originRecord =
    408      GetOriginRecord(originAttributes, originKey, true, nullptr);
    409  MOZ_ASSERT(originRecord);
    410 
    411  if (originRecord->mLoaded) {
    412    return NS_OK;
    413  }
    414 
    415  RefPtr<SessionStorageCacheChild> cacheActor =
    416      EnsureCache(aPrincipal, originKey, aCache);
    417 
    418  if (!cacheActor) {
    419    return NS_ERROR_FAILURE;
    420  }
    421 
    422  nsTArray<SSSetItemInfo> data;
    423  if (!cacheActor->SendLoad(&data)) {
    424    return NS_ERROR_FAILURE;
    425  }
    426 
    427  originRecord->mCache->DeserializeData(data);
    428 
    429  originRecord->mLoaded.Flip();
    430  aCache.SetLoadedOrCloned();
    431 
    432  return NS_OK;
    433 }
    434 
    435 void SessionStorageManager::CheckpointData(nsIPrincipal& aPrincipal,
    436                                           SessionStorageCache& aCache) {
    437  AssertIsOnMainThread();
    438  MOZ_ASSERT(mActor);
    439 
    440  nsAutoCString originKey;
    441  nsresult rv = aPrincipal.GetStorageOriginKey(originKey);
    442  if (NS_WARN_IF(NS_FAILED(rv))) {
    443    return;
    444  }
    445 
    446  return CheckpointDataInternal(aPrincipal, originKey, aCache);
    447 }
    448 
    449 void SessionStorageManager::CheckpointDataInternal(
    450    nsIPrincipal& aPrincipal, const nsACString& aOriginKey,
    451    SessionStorageCache& aCache) {
    452  AssertIsOnMainThread();
    453  MOZ_ASSERT(mActor);
    454 
    455  nsTArray<SSWriteInfo> writeInfos = aCache.SerializeWriteInfos();
    456 
    457  if (writeInfos.IsEmpty()) {
    458    return;
    459  }
    460 
    461  RefPtr<SessionStorageCacheChild> cacheActor =
    462      EnsureCache(aPrincipal, aOriginKey, aCache);
    463 
    464  if (!cacheActor) {
    465    return;
    466  }
    467 
    468  (void)cacheActor->SendCheckpoint(writeInfos);
    469 
    470  aCache.ResetWriteInfos();
    471 }
    472 
    473 nsresult SessionStorageManager::ClearStoragesForOrigin(
    474    const nsACString& aOriginAttrs, const nsACString& aOriginKey) {
    475  AssertIsOnMainThread();
    476 
    477  ClearStoragesForOriginInternal(aOriginAttrs, aOriginKey);
    478 
    479  return NS_OK;
    480 }
    481 
    482 NS_IMETHODIMP
    483 SessionStorageManager::PrecacheStorage(nsIPrincipal* aPrincipal,
    484                                       nsIPrincipal* aStoragePrincipal,
    485                                       Storage** aRetval) {
    486  // Nothing to preload.
    487  return NS_OK;
    488 }
    489 
    490 NS_IMETHODIMP
    491 SessionStorageManager::GetSessionStorageCache(
    492    nsIPrincipal* aPrincipal, nsIPrincipal* aStoragePrincipal,
    493    RefPtr<SessionStorageCache>* aRetVal) {
    494  return GetSessionStorageCacheHelper(aStoragePrincipal, true, nullptr,
    495                                      aRetVal);
    496 }
    497 
    498 nsresult SessionStorageManager::GetSessionStorageCacheHelper(
    499    nsIPrincipal* aPrincipal, bool aMakeIfNeeded,
    500    SessionStorageCache* aCloneFrom, RefPtr<SessionStorageCache>* aRetVal) {
    501  nsAutoCString originKey;
    502  nsAutoCString originAttributes;
    503  nsresult rv = aPrincipal->GetStorageOriginKey(originKey);
    504  aPrincipal->OriginAttributesRef().CreateSuffix(originAttributes);
    505  if (NS_FAILED(rv)) {
    506    return NS_ERROR_NOT_AVAILABLE;
    507  }
    508 
    509  return GetSessionStorageCacheHelper(originAttributes, originKey,
    510                                      aMakeIfNeeded, aCloneFrom, aRetVal);
    511 }
    512 
    513 nsresult SessionStorageManager::GetSessionStorageCacheHelper(
    514    const nsACString& aOriginAttrs, const nsACString& aOriginKey,
    515    bool aMakeIfNeeded, SessionStorageCache* aCloneFrom,
    516    RefPtr<SessionStorageCache>* aRetVal) {
    517  if (OriginRecord* const originRecord = GetOriginRecord(
    518          aOriginAttrs, aOriginKey, aMakeIfNeeded, aCloneFrom)) {
    519    *aRetVal = originRecord->mCache;
    520  } else {
    521    *aRetVal = nullptr;
    522  }
    523  return NS_OK;
    524 }
    525 
    526 NS_IMETHODIMP
    527 SessionStorageManager::CreateStorage(mozIDOMWindow* aWindow,
    528                                     nsIPrincipal* aPrincipal,
    529                                     nsIPrincipal* aStoragePrincipal,
    530                                     const nsAString& aDocumentURI,
    531                                     bool aPrivate, Storage** aRetval) {
    532  RefPtr<SessionStorageCache> cache;
    533  nsresult rv = GetSessionStorageCache(aPrincipal, aStoragePrincipal, &cache);
    534  if (NS_FAILED(rv)) {
    535    return rv;
    536  }
    537 
    538  nsCOMPtr<nsPIDOMWindowInner> inner = nsPIDOMWindowInner::From(aWindow);
    539 
    540  RefPtr<SessionStorage> storage =
    541      new SessionStorage(inner, aPrincipal, aStoragePrincipal, cache, this,
    542                         aDocumentURI, aPrivate);
    543 
    544  storage.forget(aRetval);
    545  return NS_OK;
    546 }
    547 
    548 NS_IMETHODIMP
    549 SessionStorageManager::GetStorage(mozIDOMWindow* aWindow,
    550                                  nsIPrincipal* aPrincipal,
    551                                  nsIPrincipal* aStoragePrincipal,
    552                                  bool aPrivate, Storage** aRetval) {
    553  *aRetval = nullptr;
    554 
    555  RefPtr<SessionStorageCache> cache;
    556  nsresult rv =
    557      GetSessionStorageCacheHelper(aStoragePrincipal, false, nullptr, &cache);
    558  if (NS_FAILED(rv) || !cache) {
    559    return rv;
    560  }
    561 
    562  nsCOMPtr<nsPIDOMWindowInner> inner = nsPIDOMWindowInner::From(aWindow);
    563 
    564  RefPtr<SessionStorage> storage = new SessionStorage(
    565      inner, aPrincipal, aStoragePrincipal, cache, this, u""_ns, aPrivate);
    566 
    567  storage.forget(aRetval);
    568  return NS_OK;
    569 }
    570 
    571 NS_IMETHODIMP
    572 SessionStorageManager::CloneStorage(Storage* aStorage) {
    573  if (NS_WARN_IF(!aStorage)) {
    574    return NS_ERROR_UNEXPECTED;
    575  }
    576 
    577  if (aStorage->Type() != Storage::eSessionStorage) {
    578    return NS_ERROR_UNEXPECTED;
    579  }
    580 
    581  // ToDo: At the moment, we clone the cache on the child process and then
    582  // send the checkpoint.  It would be nicer if we either serailizing all the
    583  // data and sync to the parent process directly or clonig storage on the
    584  // parnet process and sync it to the child process on demand.
    585 
    586  RefPtr<SessionStorageCache> cache;
    587  nsresult rv = GetSessionStorageCacheHelper(
    588      aStorage->StoragePrincipal(), true,
    589      static_cast<SessionStorage*>(aStorage)->Cache(), &cache);
    590  if (NS_WARN_IF(NS_FAILED(rv))) {
    591    return rv;
    592  }
    593 
    594  // If cache was cloned from other storage, then we shouldn't load the cache
    595  // at the first access.
    596  cache->SetLoadedOrCloned();
    597 
    598  if (CanLoadData()) {
    599    rv = EnsureManager();
    600    if (NS_WARN_IF(NS_FAILED(rv))) {
    601      return rv;
    602    }
    603 
    604    CheckpointData(*aStorage->StoragePrincipal(), *cache);
    605  }
    606 
    607  return rv;
    608 }
    609 
    610 NS_IMETHODIMP
    611 SessionStorageManager::CheckStorage(nsIPrincipal* aPrincipal, Storage* aStorage,
    612                                    bool* aRetval) {
    613  if (NS_WARN_IF(!aStorage)) {
    614    return NS_ERROR_UNEXPECTED;
    615  }
    616 
    617  if (!aPrincipal) {
    618    return NS_ERROR_NOT_AVAILABLE;
    619  }
    620 
    621  *aRetval = false;
    622 
    623  RefPtr<SessionStorageCache> cache;
    624  nsresult rv =
    625      GetSessionStorageCacheHelper(aPrincipal, false, nullptr, &cache);
    626  if (NS_FAILED(rv) || !cache) {
    627    return rv;
    628  }
    629 
    630  if (aStorage->Type() != Storage::eSessionStorage) {
    631    return NS_OK;
    632  }
    633 
    634  RefPtr<SessionStorage> sessionStorage =
    635      static_cast<SessionStorage*>(aStorage);
    636  if (sessionStorage->Cache() != cache) {
    637    return NS_OK;
    638  }
    639 
    640  if (!StorageUtils::PrincipalsEqual(aStorage->StoragePrincipal(),
    641                                     aPrincipal)) {
    642    return NS_OK;
    643  }
    644 
    645  *aRetval = true;
    646  return NS_OK;
    647 }
    648 
    649 void SessionStorageManager::ClearStorages(
    650    const OriginAttributesPattern& aPattern, const nsACString& aOriginScope,
    651    DomainMatchingMode aMode) {
    652  if (CanLoadData()) {
    653    nsresult rv = EnsureManager();
    654    if (NS_WARN_IF(NS_FAILED(rv))) {
    655      return;
    656    }
    657 
    658    mActor->SendClearStorages(aPattern, nsCString(aOriginScope),
    659                              static_cast<uint32_t>(aMode));
    660  }
    661 
    662  ClearStoragesInternal(aPattern, aOriginScope, aMode);
    663 }
    664 
    665 nsresult SessionStorageManager::Observe(
    666    const char* aTopic, const nsAString& aOriginAttributesPattern,
    667    const nsACString& aOriginScope) {
    668  OriginAttributesPattern pattern;
    669  if (!pattern.Init(aOriginAttributesPattern)) {
    670    NS_ERROR("Cannot parse origin attributes pattern");
    671    return NS_ERROR_FAILURE;
    672  }
    673 
    674  // Clear everything, caches + database
    675  if (!strcmp(aTopic, "cookie-cleared")) {
    676    ClearStorages(pattern, ""_ns);
    677    return NS_OK;
    678  }
    679 
    680  // Clear from caches everything that has been stored
    681  // while in session-only mode
    682  if (!strcmp(aTopic, "session-only-cleared")) {
    683    ClearStorages(pattern, aOriginScope);
    684    return NS_OK;
    685  }
    686 
    687  // Clear everything (including so and pb data) from caches and database
    688  // for the given domain and subdomains.
    689  if (!strcmp(aTopic, "browser:purge-sessionStorage")) {
    690    ClearStorages(pattern, aOriginScope);
    691    return NS_OK;
    692  }
    693 
    694  // Clear everything (including so and pb data) from caches and database
    695  // for the given domain.
    696  if (!strcmp(aTopic, "extension:purge-sessionStorage")) {
    697    ClearStorages(pattern, aOriginScope, DomainMatchingMode::EXACT_MATCH);
    698    return NS_OK;
    699  }
    700 
    701  // Clear entries which match an OriginAttributesPattern.
    702  if (!strcmp(aTopic, "dom-storage:clear-origin-attributes-data") ||
    703      !strcmp(aTopic, "session-storage:clear-origin-attributes-data")) {
    704    ClearStorages(pattern, aOriginScope);
    705    return NS_OK;
    706  }
    707 
    708  if (!strcmp(aTopic, "profile-change")) {
    709    // For case caches are still referenced - clear them completely
    710    ClearStorages(pattern, ""_ns);
    711    mOATable.Clear();
    712    return NS_OK;
    713  }
    714 
    715  return NS_OK;
    716 }
    717 
    718 SessionStorageManager::OriginRecord::~OriginRecord() = default;
    719 
    720 // static
    721 void BackgroundSessionStorageManager::RemoveManager(uint64_t aTopContextId) {
    722  MOZ_ASSERT(XRE_IsParentProcess());
    723  AssertIsOnMainThread();
    724 
    725  ::mozilla::ipc::PBackgroundChild* backgroundActor =
    726      ::mozilla::ipc::BackgroundChild::GetOrCreateForCurrentThread();
    727  if (NS_WARN_IF(!backgroundActor)) {
    728    return;
    729  }
    730 
    731  if (NS_WARN_IF(!backgroundActor->SendRemoveBackgroundSessionStorageManager(
    732          aTopContextId))) {
    733    return;
    734  }
    735 }
    736 
    737 // static
    738 void BackgroundSessionStorageManager::PropagateManager(
    739    uint64_t aCurrentTopContextId, uint64_t aTargetTopContextId) {
    740  MOZ_ASSERT(XRE_IsParentProcess());
    741  AssertIsOnMainThread();
    742  MOZ_ASSERT(aCurrentTopContextId != aTargetTopContextId);
    743 
    744  ::mozilla::ipc::PBackgroundChild* backgroundActor =
    745      ::mozilla::ipc::BackgroundChild::GetOrCreateForCurrentThread();
    746  if (NS_WARN_IF(!backgroundActor)) {
    747    return;
    748  }
    749 
    750  if (NS_WARN_IF(!backgroundActor->SendPropagateBackgroundSessionStorageManager(
    751          aCurrentTopContextId, aTargetTopContextId))) {
    752    return;
    753  }
    754 }
    755 
    756 // static
    757 void BackgroundSessionStorageManager::LoadData(
    758    uint64_t aTopContextId,
    759    const nsTArray<mozilla::dom::SSCacheCopy>& aCacheCopyList) {
    760  MOZ_ASSERT(XRE_IsParentProcess());
    761  AssertIsOnMainThread();
    762 
    763  ::mozilla::ipc::PBackgroundChild* backgroundActor =
    764      ::mozilla::ipc::BackgroundChild::GetOrCreateForCurrentThread();
    765  if (NS_WARN_IF(!backgroundActor)) {
    766    return;
    767  }
    768 
    769  if (NS_WARN_IF(!backgroundActor->SendLoadSessionStorageManagerData(
    770          aTopContextId, aCacheCopyList))) {
    771    return;
    772  }
    773 }
    774 
    775 // static
    776 BackgroundSessionStorageManager* BackgroundSessionStorageManager::GetOrCreate(
    777    uint64_t aTopContextId) {
    778  MOZ_ASSERT(XRE_IsParentProcess());
    779  ::mozilla::ipc::AssertIsOnBackgroundThread();
    780 
    781  if (!sManagers) {
    782    sManagers = new nsRefPtrHashtable<nsUint64HashKey,
    783                                      BackgroundSessionStorageManager>();
    784    NS_DispatchToMainThread(NS_NewRunnableFunction(
    785        "dom::BackgroundSessionStorageManager::GetOrCreate", [] {
    786          RunOnShutdown(
    787              [] {
    788                ::mozilla::ipc::PBackgroundChild* backgroundActor = ::mozilla::
    789                    ipc::BackgroundChild::GetOrCreateForCurrentThread();
    790                if (NS_WARN_IF(!backgroundActor)) {
    791                  return;
    792                }
    793 
    794                if (NS_WARN_IF(
    795                        !backgroundActor
    796                             ->SendShutdownBackgroundSessionStorageManagers())) {
    797                  return;
    798                }
    799              },
    800              ShutdownPhase::XPCOMShutdown);
    801        }));
    802  }
    803 
    804  return sManagers
    805      ->LookupOrInsertWith(
    806          aTopContextId,
    807          [aTopContextId] {
    808            return new BackgroundSessionStorageManager(aTopContextId);
    809          })
    810      .get();
    811 }
    812 
    813 BackgroundSessionStorageManager::BackgroundSessionStorageManager(
    814    uint64_t aBrowsingContextId)
    815    : mCurrentBrowsingContextId(aBrowsingContextId) {
    816  MOZ_ASSERT(XRE_IsParentProcess());
    817  ::mozilla::ipc::AssertIsOnBackgroundThread();
    818 }
    819 
    820 BackgroundSessionStorageManager::~BackgroundSessionStorageManager() = default;
    821 
    822 void BackgroundSessionStorageManager::CopyDataToContentProcess(
    823    const nsACString& aOriginAttrs, const nsACString& aOriginKey,
    824    nsTArray<SSSetItemInfo>& aData) {
    825  MOZ_ASSERT(XRE_IsParentProcess());
    826  ::mozilla::ipc::AssertIsOnBackgroundThread();
    827 
    828  auto* const originRecord =
    829      GetOriginRecord(aOriginAttrs, aOriginKey, false, nullptr);
    830  if (!originRecord) {
    831    return;
    832  }
    833 
    834  aData = originRecord->mCache->SerializeData();
    835 }
    836 
    837 /* static */
    838 RefPtr<BackgroundSessionStorageManager::DataPromise>
    839 BackgroundSessionStorageManager::GetData(BrowsingContext* aContext,
    840                                         uint32_t aSizeLimit,
    841                                         bool aClearSessionStoreTimer) {
    842  MOZ_ASSERT(XRE_IsParentProcess());
    843  MOZ_ASSERT(aContext->IsTop());
    844 
    845  AssertIsOnMainThread();
    846 
    847  ::mozilla::ipc::PBackgroundChild* backgroundActor =
    848      ::mozilla::ipc::BackgroundChild::GetOrCreateForCurrentThread();
    849  if (NS_WARN_IF(!backgroundActor)) {
    850    return DataPromise::CreateAndReject(
    851        ::mozilla::ipc::ResponseRejectReason::SendError, __func__);
    852  }
    853 
    854  return backgroundActor->SendGetSessionStorageManagerData(
    855      aContext->Id(), aSizeLimit, aClearSessionStoreTimer);
    856 }
    857 
    858 void BackgroundSessionStorageManager::GetData(
    859    uint32_t aSizeLimit, nsTArray<SSCacheCopy>& aCacheCopyList) {
    860  for (auto& managerActor : mParticipatingActors) {
    861    for (auto* cacheActor :
    862         managerActor->ManagedPBackgroundSessionStorageCacheParent()) {
    863      auto* cache = static_cast<SessionStorageCacheParent*>(cacheActor);
    864      ::mozilla::ipc::PrincipalInfo info = cache->PrincipalInfo();
    865 
    866      OriginAttributes attributes;
    867      StoragePrincipalHelper::GetOriginAttributes(cache->PrincipalInfo(),
    868                                                  attributes);
    869 
    870      nsAutoCString originAttrs;
    871      attributes.CreateSuffix(originAttrs);
    872 
    873      auto* record =
    874          GetOriginRecord(originAttrs, cache->OriginKey(), false, nullptr);
    875 
    876      if (!record) {
    877        continue;
    878      }
    879 
    880      if (record->mCache->GetOriginQuotaUsage() > aSizeLimit) {
    881        continue;
    882      }
    883 
    884      nsTArray<SSSetItemInfo> data = record->mCache->SerializeData();
    885      if (data.IsEmpty()) {
    886        continue;
    887      }
    888 
    889      SSCacheCopy& cacheCopy = *aCacheCopyList.AppendElement();
    890      cacheCopy.originKey() = cache->OriginKey();
    891      cacheCopy.principalInfo() = info;
    892      cacheCopy.data().SwapElements(data);
    893    }
    894  }
    895 }
    896 
    897 void BackgroundSessionStorageManager::UpdateData(
    898    const nsACString& aOriginAttrs, const nsACString& aOriginKey,
    899    const nsTArray<SSWriteInfo>& aWriteInfos) {
    900  MOZ_ASSERT(XRE_IsParentProcess());
    901  ::mozilla::ipc::AssertIsOnBackgroundThread();
    902 
    903  auto* const originRecord =
    904      GetOriginRecord(aOriginAttrs, aOriginKey, true, nullptr);
    905  MOZ_ASSERT(originRecord);
    906 
    907  MaybeScheduleSessionStoreUpdate();
    908 
    909  originRecord->mCache->DeserializeWriteInfos(aWriteInfos);
    910 }
    911 
    912 void BackgroundSessionStorageManager::UpdateData(
    913    const nsACString& aOriginAttrs, const nsACString& aOriginKey,
    914    const nsTArray<SSSetItemInfo>& aData) {
    915  MOZ_ASSERT(XRE_IsParentProcess());
    916  ::mozilla::ipc::AssertIsOnBackgroundThread();
    917 
    918  auto* const originRecord =
    919      GetOriginRecord(aOriginAttrs, aOriginKey, true, nullptr);
    920  MOZ_ASSERT(originRecord);
    921 
    922  originRecord->mCache->DeserializeData(aData);
    923 }
    924 
    925 void BackgroundSessionStorageManager::ClearStorages(
    926    const OriginAttributesPattern& aPattern, const nsACString& aOriginScope,
    927    DomainMatchingMode aMode) {
    928  MOZ_ASSERT(XRE_IsParentProcess());
    929  ::mozilla::ipc::AssertIsOnBackgroundThread();
    930  ClearStoragesInternal(aPattern, aOriginScope, aMode);
    931 }
    932 
    933 void BackgroundSessionStorageManager::ClearStoragesForOrigin(
    934    const nsACString& aOriginAttrs, const nsACString& aOriginKey) {
    935  ::mozilla::ipc::AssertIsInMainProcess();
    936  ::mozilla::ipc::AssertIsOnBackgroundThread();
    937 
    938  for (auto& managerActor : mParticipatingActors) {
    939    QM_WARNONLY_TRY(OkIf(managerActor->SendClearStoragesForOrigin(
    940        nsCString(aOriginAttrs), nsCString(aOriginKey))));
    941  }
    942 
    943  ClearStoragesForOriginInternal(aOriginAttrs, aOriginKey);
    944 }
    945 
    946 void BackgroundSessionStorageManager::SetCurrentBrowsingContextId(
    947    uint64_t aBrowsingContextId) {
    948  MOZ_DIAGNOSTIC_ASSERT(aBrowsingContextId != mCurrentBrowsingContextId);
    949  mCurrentBrowsingContextId = aBrowsingContextId;
    950 }
    951 
    952 void BackgroundSessionStorageManager::MaybeScheduleSessionStoreUpdate() {
    953  if (!SessionStorePlatformCollection()) {
    954    return;
    955  }
    956 
    957  if (mSessionStoreCallbackTimer) {
    958    return;
    959  }
    960 
    961  if (StaticPrefs::browser_sessionstore_debug_no_auto_updates()) {
    962    DispatchSessionStoreUpdate();
    963    return;
    964  }
    965 
    966  auto result = NS_NewTimerWithFuncCallback(
    967      [](nsITimer*, void* aClosure) {
    968        auto* mgr = static_cast<BackgroundSessionStorageManager*>(aClosure);
    969        mgr->DispatchSessionStoreUpdate();
    970      },
    971      this, StaticPrefs::browser_sessionstore_interval(),
    972      nsITimer::TYPE_ONE_SHOT,
    973      "BackgroundSessionStorageManager::DispatchSessionStoreUpdate"_ns);
    974 
    975  if (result.isErr()) {
    976    return;
    977  }
    978 
    979  mSessionStoreCallbackTimer = result.unwrap();
    980 }
    981 
    982 void BackgroundSessionStorageManager::MaybeDispatchSessionStoreUpdate() {
    983  if (mSessionStoreCallbackTimer) {
    984    BackgroundSessionStorageManager::DispatchSessionStoreUpdate();
    985  }
    986 }
    987 
    988 void BackgroundSessionStorageManager::DispatchSessionStoreUpdate() {
    989  ::mozilla::ipc::AssertIsOnBackgroundThread();
    990  NS_DispatchToMainThread(NS_NewRunnableFunction(
    991      "CanonicalBrowsingContext::UpdateSessionStore",
    992      [targetBrowsingContextId = mCurrentBrowsingContextId]() {
    993        CanonicalBrowsingContext::UpdateSessionStoreForStorage(
    994            targetBrowsingContextId);
    995      }));
    996 
    997  CancelSessionStoreUpdate();
    998 }
    999 
   1000 void BackgroundSessionStorageManager::CancelSessionStoreUpdate() {
   1001  if (mSessionStoreCallbackTimer) {
   1002    mSessionStoreCallbackTimer->Cancel();
   1003    mSessionStoreCallbackTimer = nullptr;
   1004  }
   1005 }
   1006 
   1007 void BackgroundSessionStorageManager::AddParticipatingActor(
   1008    SessionStorageManagerParent* aActor) {
   1009  ::mozilla::ipc::AssertIsOnBackgroundThread();
   1010  mParticipatingActors.AppendElement(aActor);
   1011 }
   1012 
   1013 void BackgroundSessionStorageManager::RemoveParticipatingActor(
   1014    SessionStorageManagerParent* aActor) {
   1015  ::mozilla::ipc::AssertIsOnBackgroundThread();
   1016  mParticipatingActors.RemoveElement(aActor);
   1017 }
   1018 
   1019 }  // namespace mozilla::dom