tor-browser

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

CacheFileContextEvictor.cpp (24827B)


      1 /* This Source Code Form is subject to the terms of the Mozilla Public
      2 * License, v. 2.0. If a copy of the MPL was not distributed with this
      3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
      4 
      5 #include "CacheLog.h"
      6 #include "CacheFileContextEvictor.h"
      7 #include "CacheFileIOManager.h"
      8 #include "CacheFileMetadata.h"
      9 #include "CacheIndex.h"
     10 #include "CacheIndexIterator.h"
     11 #include "CacheFileUtils.h"
     12 #include "CacheObserver.h"
     13 #include "mozilla/Components.h"
     14 #include "nsIEffectiveTLDService.h"
     15 #include "nsIFile.h"
     16 #include "LoadContextInfo.h"
     17 #include "nsThreadUtils.h"
     18 #include "nsString.h"
     19 #include "nsIDirectoryEnumerator.h"
     20 #include "mozilla/Base64.h"
     21 #include "mozilla/IntegerPrintfMacros.h"
     22 #include "nsContentUtils.h"
     23 #include "nsNetUtil.h"
     24 
     25 namespace mozilla::net {
     26 
     27 #define CONTEXT_EVICTION_PREFIX "ce_"
     28 const uint32_t kContextEvictionPrefixLength =
     29    sizeof(CONTEXT_EVICTION_PREFIX) - 1;
     30 
     31 bool CacheFileContextEvictor::sDiskAlreadySearched = false;
     32 
     33 CacheFileContextEvictor::CacheFileContextEvictor() {
     34  LOG(("CacheFileContextEvictor::CacheFileContextEvictor() [this=%p]", this));
     35 }
     36 
     37 CacheFileContextEvictor::~CacheFileContextEvictor() {
     38  LOG(("CacheFileContextEvictor::~CacheFileContextEvictor() [this=%p]", this));
     39 }
     40 
     41 nsresult CacheFileContextEvictor::Init(nsIFile* aCacheDirectory) {
     42  LOG(("CacheFileContextEvictor::Init()"));
     43 
     44  nsresult rv;
     45 
     46  MOZ_ASSERT(CacheFileIOManager::IsOnIOThread());
     47 
     48  CacheIndex::IsUpToDate(&mIndexIsUpToDate);
     49 
     50  mCacheDirectory = aCacheDirectory;
     51 
     52  rv = aCacheDirectory->Clone(getter_AddRefs(mEntriesDir));
     53  if (NS_WARN_IF(NS_FAILED(rv))) {
     54    return rv;
     55  }
     56 
     57  rv = mEntriesDir->AppendNative(nsLiteralCString(ENTRIES_DIR));
     58  if (NS_WARN_IF(NS_FAILED(rv))) {
     59    return rv;
     60  }
     61 
     62  if (!sDiskAlreadySearched) {
     63    LoadEvictInfoFromDisk();
     64    if ((mEntries.Length() != 0) && mIndexIsUpToDate) {
     65      CreateIterators();
     66      StartEvicting();
     67    }
     68  }
     69 
     70  return NS_OK;
     71 }
     72 
     73 void CacheFileContextEvictor::Shutdown() {
     74  LOG(("CacheFileContextEvictor::Shutdown()"));
     75 
     76  MOZ_ASSERT(CacheFileIOManager::IsOnIOThread());
     77 
     78  CloseIterators();
     79 }
     80 
     81 uint32_t CacheFileContextEvictor::ContextsCount() {
     82  MOZ_ASSERT(CacheFileIOManager::IsOnIOThread());
     83 
     84  return mEntries.Length();
     85 }
     86 
     87 nsresult CacheFileContextEvictor::AddContext(
     88    nsILoadContextInfo* aLoadContextInfo, bool aPinned,
     89    const nsAString& aOrigin, const nsAString& aBaseDomain) {
     90  LOG(
     91      ("CacheFileContextEvictor::AddContext() [this=%p, loadContextInfo=%p, "
     92       "pinned=%d]",
     93       this, aLoadContextInfo, aPinned));
     94 
     95  MOZ_ASSERT(aOrigin.IsEmpty() || aBaseDomain.IsEmpty(),
     96             "Cannot specify both origin and base domain");
     97 
     98  nsresult rv;
     99 
    100  MOZ_ASSERT(CacheFileIOManager::IsOnIOThread());
    101 
    102  CacheFileContextEvictorEntry* entry = nullptr;
    103  if (aLoadContextInfo) {
    104    for (uint32_t i = 0; i < mEntries.Length(); ++i) {
    105      if (mEntries[i]->mInfo && mEntries[i]->mInfo->Equals(aLoadContextInfo) &&
    106          mEntries[i]->mPinned == aPinned &&
    107          (mEntries[i]->mOrigin.Equals(aOrigin) ||
    108           mEntries[i]->mBaseDomain.Equals(aBaseDomain))) {
    109        entry = mEntries[i].get();
    110        break;
    111      }
    112    }
    113  } else {
    114    // Not providing load context info means we want to delete everything,
    115    // so let's not bother with any currently running context cleanups
    116    // for the same pinning state.
    117    for (uint32_t i = mEntries.Length(); i > 0;) {
    118      --i;
    119      if (mEntries[i]->mInfo && mEntries[i]->mPinned == aPinned) {
    120        RemoveEvictInfoFromDisk(mEntries[i]->mInfo, mEntries[i]->mPinned,
    121                                mEntries[i]->mOrigin, mEntries[i]->mBaseDomain);
    122        mEntries.RemoveElementAt(i);
    123      }
    124    }
    125  }
    126 
    127  if (!entry) {
    128    entry = new CacheFileContextEvictorEntry();
    129    entry->mInfo = aLoadContextInfo;
    130    entry->mPinned = aPinned;
    131    entry->mOrigin = aOrigin;
    132    entry->mBaseDomain = aBaseDomain;
    133    mEntries.AppendElement(WrapUnique(entry));
    134  }
    135 
    136  entry->mTimeStamp = PR_Now() / PR_USEC_PER_MSEC;
    137 
    138  PersistEvictionInfoToDisk(aLoadContextInfo, aPinned, aOrigin, aBaseDomain);
    139 
    140  if (mIndexIsUpToDate) {
    141    // Already existing context could be added again, in this case the iterator
    142    // would be recreated. Close the old iterator explicitely.
    143    if (entry->mIterator) {
    144      entry->mIterator->Close();
    145      entry->mIterator = nullptr;
    146    }
    147 
    148    rv = CacheIndex::GetIterator(aLoadContextInfo, false,
    149                                 getter_AddRefs(entry->mIterator));
    150    if (NS_FAILED(rv)) {
    151      // This could probably happen during shutdown. Remove the entry from
    152      // the array, but leave the info on the disk. No entry can be opened
    153      // during shutdown and we'll load the eviction info on next start.
    154      LOG(
    155          ("CacheFileContextEvictor::AddContext() - Cannot get an iterator. "
    156           "[rv=0x%08" PRIx32 "]",
    157           static_cast<uint32_t>(rv)));
    158      mEntries.RemoveElement(entry);
    159      return rv;
    160    }
    161 
    162    StartEvicting();
    163  }
    164 
    165  return NS_OK;
    166 }
    167 
    168 void CacheFileContextEvictor::CacheIndexStateChanged() {
    169  LOG(("CacheFileContextEvictor::CacheIndexStateChanged() [this=%p]", this));
    170 
    171  MOZ_ASSERT(CacheFileIOManager::IsOnIOThread());
    172 
    173  bool isUpToDate = false;
    174  CacheIndex::IsUpToDate(&isUpToDate);
    175  if (mEntries.Length() == 0) {
    176    // Just save the state and exit, since there is nothing to do
    177    mIndexIsUpToDate = isUpToDate;
    178    return;
    179  }
    180 
    181  if (!isUpToDate && !mIndexIsUpToDate) {
    182    // Index is outdated and status has not changed, nothing to do.
    183    return;
    184  }
    185 
    186  if (isUpToDate && mIndexIsUpToDate) {
    187    // Status has not changed, but make sure the eviction is running.
    188    if (mEvicting) {
    189      return;
    190    }
    191 
    192    // We're not evicting, but we should be evicting?!
    193    LOG(
    194        ("CacheFileContextEvictor::CacheIndexStateChanged() - Index is up to "
    195         "date, we have some context to evict but eviction is not running! "
    196         "Starting now."));
    197  }
    198 
    199  mIndexIsUpToDate = isUpToDate;
    200 
    201  if (mIndexIsUpToDate) {
    202    CreateIterators();
    203    StartEvicting();
    204  } else {
    205    CloseIterators();
    206  }
    207 }
    208 
    209 void CacheFileContextEvictor::WasEvicted(const nsACString& aKey, nsIFile* aFile,
    210                                         bool* aEvictedAsPinned,
    211                                         bool* aEvictedAsNonPinned) {
    212  LOG(("CacheFileContextEvictor::WasEvicted() [key=%s]",
    213       PromiseFlatCString(aKey).get()));
    214 
    215  *aEvictedAsPinned = false;
    216  *aEvictedAsNonPinned = false;
    217 
    218  MOZ_ASSERT(CacheFileIOManager::IsOnIOThread());
    219 
    220  nsCOMPtr<nsILoadContextInfo> info = CacheFileUtils::ParseKey(aKey);
    221  MOZ_ASSERT(info);
    222  if (!info) {
    223    LOG(("CacheFileContextEvictor::WasEvicted() - Cannot parse key!"));
    224    return;
    225  }
    226 
    227  for (uint32_t i = 0; i < mEntries.Length(); ++i) {
    228    const auto& entry = mEntries[i];
    229 
    230    if (entry->mInfo && !info->Equals(entry->mInfo)) {
    231      continue;
    232    }
    233 
    234    PRTime lastModifiedTime;
    235    if (NS_FAILED(aFile->GetLastModifiedTime(&lastModifiedTime))) {
    236      LOG(
    237          ("CacheFileContextEvictor::WasEvicted() - Cannot get last modified "
    238           "time, returning."));
    239      return;
    240    }
    241 
    242    if (lastModifiedTime > entry->mTimeStamp) {
    243      // File has been modified since context eviction.
    244      continue;
    245    }
    246 
    247    LOG(
    248        ("CacheFileContextEvictor::WasEvicted() - evicted [pinning=%d, "
    249         "mTimeStamp=%" PRId64 ", lastModifiedTime=%" PRId64 "]",
    250         entry->mPinned, entry->mTimeStamp, lastModifiedTime));
    251 
    252    if (entry->mPinned) {
    253      *aEvictedAsPinned = true;
    254    } else {
    255      *aEvictedAsNonPinned = true;
    256    }
    257  }
    258 }
    259 
    260 nsresult CacheFileContextEvictor::PersistEvictionInfoToDisk(
    261    nsILoadContextInfo* aLoadContextInfo, bool aPinned,
    262    const nsAString& aOrigin, const nsAString& aBaseDomain) {
    263  LOG(
    264      ("CacheFileContextEvictor::PersistEvictionInfoToDisk() [this=%p, "
    265       "loadContextInfo=%p]",
    266       this, aLoadContextInfo));
    267 
    268  nsresult rv;
    269 
    270  MOZ_ASSERT(CacheFileIOManager::IsOnIOThread());
    271 
    272  nsCOMPtr<nsIFile> file;
    273  rv = GetContextFile(aLoadContextInfo, aPinned, aOrigin, aBaseDomain,
    274                      getter_AddRefs(file));
    275  if (NS_WARN_IF(NS_FAILED(rv))) {
    276    return rv;
    277  }
    278 
    279  nsCString path = file->HumanReadablePath();
    280 
    281  PRFileDesc* fd;
    282  rv =
    283      file->OpenNSPRFileDesc(PR_RDWR | PR_CREATE_FILE | PR_TRUNCATE, 0600, &fd);
    284  if (NS_WARN_IF(NS_FAILED(rv))) {
    285    LOG(
    286        ("CacheFileContextEvictor::PersistEvictionInfoToDisk() - Creating file "
    287         "failed! [path=%s, rv=0x%08" PRIx32 "]",
    288         path.get(), static_cast<uint32_t>(rv)));
    289    return rv;
    290  }
    291 
    292  PR_Close(fd);
    293 
    294  LOG(
    295      ("CacheFileContextEvictor::PersistEvictionInfoToDisk() - Successfully "
    296       "created file. [path=%s]",
    297       path.get()));
    298 
    299  return NS_OK;
    300 }
    301 
    302 nsresult CacheFileContextEvictor::RemoveEvictInfoFromDisk(
    303    nsILoadContextInfo* aLoadContextInfo, bool aPinned,
    304    const nsAString& aOrigin, const nsAString& aBaseDomain) {
    305  LOG(
    306      ("CacheFileContextEvictor::RemoveEvictInfoFromDisk() [this=%p, "
    307       "loadContextInfo=%p]",
    308       this, aLoadContextInfo));
    309 
    310  nsresult rv;
    311 
    312  MOZ_ASSERT(CacheFileIOManager::IsOnIOThread());
    313 
    314  nsCOMPtr<nsIFile> file;
    315  rv = GetContextFile(aLoadContextInfo, aPinned, aOrigin, aBaseDomain,
    316                      getter_AddRefs(file));
    317  if (NS_WARN_IF(NS_FAILED(rv))) {
    318    return rv;
    319  }
    320 
    321  nsCString path = file->HumanReadablePath();
    322 
    323  rv = file->Remove(false);
    324  if (NS_WARN_IF(NS_FAILED(rv))) {
    325    LOG(
    326        ("CacheFileContextEvictor::RemoveEvictionInfoFromDisk() - Removing file"
    327         " failed! [path=%s, rv=0x%08" PRIx32 "]",
    328         path.get(), static_cast<uint32_t>(rv)));
    329    return rv;
    330  }
    331 
    332  LOG(
    333      ("CacheFileContextEvictor::RemoveEvictionInfoFromDisk() - Successfully "
    334       "removed file. [path=%s]",
    335       path.get()));
    336 
    337  return NS_OK;
    338 }
    339 
    340 nsresult CacheFileContextEvictor::LoadEvictInfoFromDisk() {
    341  LOG(("CacheFileContextEvictor::LoadEvictInfoFromDisk() [this=%p]", this));
    342 
    343  nsresult rv;
    344 
    345  MOZ_ASSERT(CacheFileIOManager::IsOnIOThread());
    346 
    347  sDiskAlreadySearched = true;
    348 
    349  nsCOMPtr<nsIDirectoryEnumerator> dirEnum;
    350  rv = mCacheDirectory->GetDirectoryEntries(getter_AddRefs(dirEnum));
    351  if (NS_WARN_IF(NS_FAILED(rv))) {
    352    return rv;
    353  }
    354 
    355  while (true) {
    356    nsCOMPtr<nsIFile> file;
    357    rv = dirEnum->GetNextFile(getter_AddRefs(file));
    358    if (!file) {
    359      break;
    360    }
    361 
    362    bool isDir = false;
    363    file->IsDirectory(&isDir);
    364    if (isDir) {
    365      continue;
    366    }
    367 
    368    nsAutoCString leaf;
    369    rv = file->GetNativeLeafName(leaf);
    370    if (NS_FAILED(rv)) {
    371      LOG(
    372          ("CacheFileContextEvictor::LoadEvictInfoFromDisk() - "
    373           "GetNativeLeafName() failed! Skipping file."));
    374      continue;
    375    }
    376 
    377    if (leaf.Length() < kContextEvictionPrefixLength) {
    378      continue;
    379    }
    380 
    381    if (!StringBeginsWith(leaf, nsLiteralCString(CONTEXT_EVICTION_PREFIX))) {
    382      continue;
    383    }
    384 
    385    nsAutoCString encoded;
    386    encoded = Substring(leaf, kContextEvictionPrefixLength);
    387    encoded.ReplaceChar('-', '/');
    388 
    389    nsAutoCString decoded;
    390    rv = Base64Decode(encoded, decoded);
    391    if (NS_FAILED(rv)) {
    392      LOG(
    393          ("CacheFileContextEvictor::LoadEvictInfoFromDisk() - Base64 decoding "
    394           "failed. Removing the file. [file=%s]",
    395           leaf.get()));
    396      file->Remove(false);
    397      continue;
    398    }
    399 
    400    bool pinned = decoded[0] == '\t';
    401    if (pinned) {
    402      decoded = Substring(decoded, 1);
    403    }
    404 
    405    nsAutoCString origin;
    406    nsAutoCString baseDomain;
    407    if (decoded.Contains('\t')) {
    408      auto split = decoded.Split('\t');
    409      MOZ_ASSERT(decoded.CountChar('\t') == 1);
    410 
    411      auto splitIt = split.begin();
    412      nsAutoCString value(*splitIt);
    413      ++splitIt;
    414      decoded = *splitIt;
    415 
    416      // Check if this is a base domain entry (prefixed with 's:')
    417      if (StringBeginsWith(value, "s:"_ns)) {
    418        baseDomain = nsAutoCString(Substring(value, 2));
    419      } else {
    420        origin = value;
    421      }
    422    }
    423 
    424    nsCOMPtr<nsILoadContextInfo> info;
    425    if (!"*"_ns.Equals(decoded)) {
    426      // "*" is indication of 'delete all', info left null will pass
    427      // to CacheFileContextEvictor::AddContext and clear all the cache data.
    428      info = CacheFileUtils::ParseKey(decoded);
    429      if (!info) {
    430        LOG(
    431            ("CacheFileContextEvictor::LoadEvictInfoFromDisk() - Cannot parse "
    432             "context key, removing file. [contextKey=%s, file=%s]",
    433             decoded.get(), leaf.get()));
    434        file->Remove(false);
    435        continue;
    436      }
    437    }
    438 
    439    PRTime lastModifiedTime;
    440    rv = file->GetLastModifiedTime(&lastModifiedTime);
    441    if (NS_FAILED(rv)) {
    442      continue;
    443    }
    444 
    445    auto entry = MakeUnique<CacheFileContextEvictorEntry>();
    446    entry->mInfo = info;
    447    entry->mPinned = pinned;
    448    CopyUTF8toUTF16(origin, entry->mOrigin);
    449    CopyUTF8toUTF16(baseDomain, entry->mBaseDomain);
    450    entry->mTimeStamp = lastModifiedTime;
    451    mEntries.AppendElement(std::move(entry));
    452  }
    453 
    454  return NS_OK;
    455 }
    456 
    457 nsresult CacheFileContextEvictor::GetContextFile(
    458    nsILoadContextInfo* aLoadContextInfo, bool aPinned,
    459    const nsAString& aOrigin, const nsAString& aBaseDomain, nsIFile** _retval) {
    460  MOZ_ASSERT(aOrigin.IsEmpty() || aBaseDomain.IsEmpty(),
    461             "Cannot specify both origin and base domain");
    462  nsresult rv;
    463 
    464  nsAutoCString keyPrefix;
    465  if (aPinned) {
    466    // Mark pinned context files with a tab char at the start.
    467    // Tab is chosen because it can never be used as a context key tag.
    468    keyPrefix.Append('\t');
    469  }
    470  if (aLoadContextInfo) {
    471    CacheFileUtils::AppendKeyPrefix(aLoadContextInfo, keyPrefix);
    472  } else {
    473    keyPrefix.Append('*');
    474  }
    475 
    476  if (!aOrigin.IsEmpty()) {
    477    keyPrefix.Append('\t');
    478    keyPrefix.Append(NS_ConvertUTF16toUTF8(aOrigin));
    479  } else if (!aBaseDomain.IsEmpty()) {
    480    keyPrefix.Append('\t');
    481    keyPrefix.AppendLiteral("s:");  // Prefix to identify base domain entries
    482    keyPrefix.Append(NS_ConvertUTF16toUTF8(aBaseDomain));
    483  }
    484 
    485  nsAutoCString leafName;
    486  leafName.AssignLiteral(CONTEXT_EVICTION_PREFIX);
    487 
    488  rv = Base64EncodeAppend(keyPrefix, leafName);
    489  if (NS_WARN_IF(NS_FAILED(rv))) {
    490    return rv;
    491  }
    492 
    493  // Replace '/' with '-' since '/' cannot be part of the filename.
    494  leafName.ReplaceChar('/', '-');
    495 
    496  nsCOMPtr<nsIFile> file;
    497  rv = mCacheDirectory->Clone(getter_AddRefs(file));
    498  if (NS_WARN_IF(NS_FAILED(rv))) {
    499    return rv;
    500  }
    501 
    502  rv = file->AppendNative(leafName);
    503  if (NS_WARN_IF(NS_FAILED(rv))) {
    504    return rv;
    505  }
    506 
    507  file.swap(*_retval);
    508  return NS_OK;
    509 }
    510 
    511 void CacheFileContextEvictor::CreateIterators() {
    512  LOG(("CacheFileContextEvictor::CreateIterators() [this=%p]", this));
    513 
    514  CloseIterators();
    515 
    516  nsresult rv;
    517 
    518  for (uint32_t i = 0; i < mEntries.Length();) {
    519    rv = CacheIndex::GetIterator(mEntries[i]->mInfo, false,
    520                                 getter_AddRefs(mEntries[i]->mIterator));
    521    if (NS_FAILED(rv)) {
    522      LOG(
    523          ("CacheFileContextEvictor::CreateIterators() - Cannot get an iterator"
    524           ". [rv=0x%08" PRIx32 "]",
    525           static_cast<uint32_t>(rv)));
    526      mEntries.RemoveElementAt(i);
    527      continue;
    528    }
    529 
    530    ++i;
    531  }
    532 }
    533 
    534 void CacheFileContextEvictor::CloseIterators() {
    535  LOG(("CacheFileContextEvictor::CloseIterators() [this=%p]", this));
    536 
    537  for (uint32_t i = 0; i < mEntries.Length(); ++i) {
    538    if (mEntries[i]->mIterator) {
    539      mEntries[i]->mIterator->Close();
    540      mEntries[i]->mIterator = nullptr;
    541    }
    542  }
    543 }
    544 
    545 void CacheFileContextEvictor::StartEvicting() {
    546  LOG(("CacheFileContextEvictor::StartEvicting() [this=%p]", this));
    547 
    548  MOZ_ASSERT(CacheFileIOManager::IsOnIOThread());
    549 
    550  if (mEvicting) {
    551    LOG(("CacheFileContextEvictor::StartEvicting() - already evicting."));
    552    return;
    553  }
    554 
    555  if (mEntries.Length() == 0) {
    556    LOG(("CacheFileContextEvictor::StartEvicting() - no context to evict."));
    557    return;
    558  }
    559 
    560  nsCOMPtr<nsIRunnable> ev =
    561      NewRunnableMethod("net::CacheFileContextEvictor::EvictEntries", this,
    562                        &CacheFileContextEvictor::EvictEntries);
    563 
    564  RefPtr<CacheIOThread> ioThread = CacheFileIOManager::IOThread();
    565 
    566  nsresult rv = ioThread->Dispatch(ev, CacheIOThread::EVICT);
    567  if (NS_FAILED(rv)) {
    568    LOG(
    569        ("CacheFileContextEvictor::StartEvicting() - Cannot dispatch event to "
    570         "IO thread. [rv=0x%08" PRIx32 "]",
    571         static_cast<uint32_t>(rv)));
    572  }
    573 
    574  mEvicting = true;
    575 }
    576 
    577 void CacheFileContextEvictor::EvictEntries() {
    578  LOG(("CacheFileContextEvictor::EvictEntries()"));
    579 
    580  nsresult rv;
    581 
    582  MOZ_ASSERT(CacheFileIOManager::IsOnIOThread());
    583 
    584  mEvicting = false;
    585 
    586  if (!mIndexIsUpToDate) {
    587    LOG(
    588        ("CacheFileContextEvictor::EvictEntries() - Stopping evicting due to "
    589         "outdated index."));
    590    return;
    591  }
    592 
    593  nsCOMPtr<nsIEffectiveTLDService> tldService =
    594      mozilla::components::EffectiveTLD::Service();
    595  MOZ_ASSERT(tldService);
    596 
    597  while (true) {
    598    if (CacheObserver::ShuttingDown()) {
    599      LOG(
    600          ("CacheFileContextEvictor::EvictEntries() - Stopping evicting due to "
    601           "shutdown."));
    602      mEvicting =
    603          true;  // We don't want to start eviction again during shutdown
    604                 // process. Setting this flag to true ensures it.
    605      return;
    606    }
    607 
    608    if (CacheIOThread::YieldAndRerun()) {
    609      LOG(
    610          ("CacheFileContextEvictor::EvictEntries() - Breaking loop for higher "
    611           "level events."));
    612      mEvicting = true;
    613      return;
    614    }
    615 
    616    if (mEntries.Length() == 0) {
    617      LOG(
    618          ("CacheFileContextEvictor::EvictEntries() - Stopping evicting, there "
    619           "is no context to evict."));
    620 
    621      // Allow index to notify AsyncGetDiskConsumption callbacks.  The size is
    622      // actual again.
    623      CacheIndex::OnAsyncEviction(false);
    624      return;
    625    }
    626 
    627    SHA1Sum::Hash hash;
    628    rv = mEntries[0]->mIterator->GetNextHash(&hash);
    629    if (rv == NS_ERROR_NOT_AVAILABLE) {
    630      LOG(
    631          ("CacheFileContextEvictor::EvictEntries() - No more entries left in "
    632           "iterator. [iterator=%p, info=%p]",
    633           mEntries[0]->mIterator.get(), mEntries[0]->mInfo.get()));
    634      RemoveEvictInfoFromDisk(mEntries[0]->mInfo, mEntries[0]->mPinned,
    635                              mEntries[0]->mOrigin, mEntries[0]->mBaseDomain);
    636      mEntries.RemoveElementAt(0);
    637      continue;
    638    }
    639    if (NS_FAILED(rv)) {
    640      LOG(
    641          ("CacheFileContextEvictor::EvictEntries() - Iterator failed to "
    642           "provide next hash (shutdown?), keeping eviction info on disk."
    643           " [iterator=%p, info=%p]",
    644           mEntries[0]->mIterator.get(), mEntries[0]->mInfo.get()));
    645      mEntries.RemoveElementAt(0);
    646      continue;
    647    }
    648 
    649    LOG(
    650        ("CacheFileContextEvictor::EvictEntries() - Processing hash. "
    651         "[hash=%08x%08x%08x%08x%08x, iterator=%p, info=%p]",
    652         LOGSHA1(&hash), mEntries[0]->mIterator.get(),
    653         mEntries[0]->mInfo.get()));
    654 
    655    RefPtr<CacheFileHandle> handle;
    656    CacheFileIOManager::gInstance->mHandles.GetHandle(&hash,
    657                                                      getter_AddRefs(handle));
    658    if (handle) {
    659      // We doom any active handle in CacheFileIOManager::EvictByContext(), so
    660      // this must be a new one. Skip it.
    661      LOG(
    662          ("CacheFileContextEvictor::EvictEntries() - Skipping entry since we "
    663           "found an active handle. [handle=%p key=%s]",
    664           handle.get(), handle->Key().get()));
    665      continue;
    666    }
    667 
    668    CacheIndex::EntryStatus status;
    669    bool pinned = false;
    670    auto callback = [&pinned](const CacheIndexEntry* aEntry) {
    671      pinned = aEntry->IsPinned();
    672    };
    673    rv = CacheIndex::HasEntry(hash, &status, callback);
    674    // This must never fail, since eviction (this code) happens only when the
    675    // index is up-to-date and thus the information is known.
    676    MOZ_ASSERT(NS_SUCCEEDED(rv));
    677 
    678    if (pinned != mEntries[0]->mPinned) {
    679      LOG(
    680          ("CacheFileContextEvictor::EvictEntries() - Skipping entry since "
    681           "pinning "
    682           "doesn't match [evicting pinned=%d, entry pinned=%d]",
    683           mEntries[0]->mPinned, pinned));
    684      continue;
    685    }
    686 
    687    // Read metadata from the file synchronously
    688    RefPtr<CacheFileMetadata> metadata = new CacheFileMetadata();
    689    {
    690      // Get and read metadata for the entry
    691      nsCOMPtr<nsIFile> file;
    692      CacheFileIOManager::gInstance->GetFile(&hash, getter_AddRefs(file));
    693 
    694      rv = metadata->SyncReadMetadata(file);
    695      if (NS_WARN_IF(NS_FAILED(rv))) {
    696        continue;
    697      }
    698    }
    699 
    700    // Check whether we must filter by either base domain or by origin.
    701    if (!mEntries[0]->mBaseDomain.IsEmpty() ||
    702        !mEntries[0]->mOrigin.IsEmpty()) {
    703      // Now get the context + enhance id + URL from the key.
    704      nsAutoCString uriSpec;
    705      RefPtr<nsILoadContextInfo> info =
    706          CacheFileUtils::ParseKey(metadata->GetKey(), nullptr, &uriSpec);
    707      MOZ_ASSERT(info);
    708      if (!info) {
    709        continue;
    710      }
    711 
    712      // Create URI from spec
    713      nsCOMPtr<nsIURI> uri;
    714      rv = NS_NewURI(getter_AddRefs(uri), uriSpec);
    715      if (NS_FAILED(rv)) {
    716        LOG(
    717            ("CacheFileContextEvictor::EvictEntries() - Skipping entry since "
    718             "NS_NewURI failed"));
    719        continue;
    720      }
    721 
    722      if (!mEntries[0]->mBaseDomain.IsEmpty()) {
    723        if (!tldService) {
    724          LOG(
    725              ("CacheFileContextEvictor::EvictEntries() - Failed to get TLD "
    726               "service, skipping entry"));
    727          continue;
    728        }
    729 
    730        // 1. Check base domain match of the uri
    731        nsAutoCString host;
    732        rv = uri->GetHost(host);
    733        if (NS_FAILED(rv) || host.IsEmpty()) {
    734          LOG(
    735              ("CacheFileContextEvictor::EvictEntries() - Failed to get host, "
    736               "skipping entry"));
    737          continue;
    738        }
    739 
    740        nsAutoCString baseDomainUTF8 =
    741            NS_ConvertUTF16toUTF8(mEntries[0]->mBaseDomain);
    742        bool hasRootDomain = false;
    743        rv = tldService->HasRootDomain(host, baseDomainUTF8, &hasRootDomain);
    744        if (NS_FAILED(rv)) {
    745          LOG(
    746              ("CacheFileContextEvictor::EvictEntries() - Failed to check root "
    747               "domain, skipping entry"));
    748          continue;
    749        }
    750 
    751        if (!hasRootDomain) {
    752          MOZ_ASSERT(info && info->OriginAttributesPtr());
    753 
    754          // 2. If the entry's URI does not match, also check the partitionKey
    755          //    to also clear cache entries partitioned under base domain.
    756          dom::PartitionKeyPatternDictionary partitionKeyPattern;
    757          partitionKeyPattern.mBaseDomain.Construct(mEntries[0]->mBaseDomain);
    758          OriginAttributesPattern originAttributesPattern;
    759          originAttributesPattern.mPartitionKeyPattern.Construct(
    760              partitionKeyPattern);
    761 
    762          if (!originAttributesPattern.Matches(*info->OriginAttributesPtr())) {
    763            LOG(
    764                ("CacheFileContextEvictor::EvictEntries() - Skipping entry "
    765                 "since "
    766                 "base domain does not match"));
    767            continue;
    768          }
    769        }
    770 
    771        LOG(
    772            ("CacheFileContextEvictor::EvictEntries() - Entry matches base "
    773             "domain [spec=%s, baseDomain=%s]",
    774             uriSpec.get(), baseDomainUTF8.get()));
    775      } else {
    776        // Check origin match
    777        nsAutoString urlOrigin;
    778        rv = nsContentUtils::GetWebExposedOriginSerialization(uri, urlOrigin);
    779        if (NS_FAILED(rv)) {
    780          LOG(
    781              ("CacheFileContextEvictor::EvictEntries() - Skipping entry since "
    782               "We failed to extract an origin"));
    783          continue;
    784        }
    785 
    786        if (!urlOrigin.Equals(mEntries[0]->mOrigin)) {
    787          LOG(
    788              ("CacheFileContextEvictor::EvictEntries() - Skipping entry since "
    789               "origin "
    790               "doesn't match"));
    791          continue;
    792        }
    793      }
    794    }
    795 
    796    nsAutoCString leafName;
    797    CacheFileIOManager::HashToStr(&hash, leafName);
    798 
    799    PRTime lastModifiedTime;
    800    nsCOMPtr<nsIFile> file;
    801    rv = mEntriesDir->Clone(getter_AddRefs(file));
    802    if (NS_SUCCEEDED(rv)) {
    803      rv = file->AppendNative(leafName);
    804    }
    805    if (NS_SUCCEEDED(rv)) {
    806      rv = file->GetLastModifiedTime(&lastModifiedTime);
    807    }
    808    if (NS_FAILED(rv)) {
    809      LOG(
    810          ("CacheFileContextEvictor::EvictEntries() - Cannot get last modified "
    811           "time, skipping entry."));
    812      continue;
    813    }
    814 
    815    if (lastModifiedTime > mEntries[0]->mTimeStamp) {
    816      LOG(
    817          ("CacheFileContextEvictor::EvictEntries() - Skipping newer entry. "
    818           "[mTimeStamp=%" PRId64 ", lastModifiedTime=%" PRId64 "]",
    819           mEntries[0]->mTimeStamp, lastModifiedTime));
    820      continue;
    821    }
    822 
    823    LOG(("CacheFileContextEvictor::EvictEntries - Removing entry."));
    824    file->Remove(false);
    825    CacheIndex::RemoveEntry(&hash, metadata->GetKey(), false);
    826  }
    827 
    828  MOZ_ASSERT_UNREACHABLE("We should never get here");
    829 }
    830 
    831 }  // namespace mozilla::net