LCOV - code coverage report
Current view: top level - netwerk/cache - nsCacheService.cpp (source / functions) Hit Total Coverage
Test: output.info Lines: 185 1480 12.5 %
Date: 2017-07-14 16:53:18 Functions: 21 150 14.0 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
       2             : /* vim: set ts=8 sts=4 et sw=4 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 "mozilla/ArrayUtils.h"
       8             : #include "mozilla/Attributes.h"
       9             : #include "mozilla/Assertions.h"
      10             : #include "mozilla/DebugOnly.h"
      11             : 
      12             : #include "necko-config.h"
      13             : 
      14             : #include "nsCache.h"
      15             : #include "nsCacheService.h"
      16             : #include "nsCacheRequest.h"
      17             : #include "nsCacheEntry.h"
      18             : #include "nsCacheEntryDescriptor.h"
      19             : #include "nsCacheDevice.h"
      20             : #include "nsMemoryCacheDevice.h"
      21             : #include "nsICacheVisitor.h"
      22             : #include "nsDiskCacheDevice.h"
      23             : #include "nsDiskCacheDeviceSQL.h"
      24             : #include "nsCacheUtils.h"
      25             : #include "../cache2/CacheObserver.h"
      26             : 
      27             : #include "nsIObserverService.h"
      28             : #include "nsIPrefService.h"
      29             : #include "nsIPrefBranch.h"
      30             : #include "nsIFile.h"
      31             : #include "nsIOService.h"
      32             : #include "nsDirectoryServiceDefs.h"
      33             : #include "nsAppDirectoryServiceDefs.h"
      34             : #include "nsThreadUtils.h"
      35             : #include "nsProxyRelease.h"
      36             : #include "nsDeleteDir.h"
      37             : #include "nsNetCID.h"
      38             : #include <math.h>  // for log()
      39             : #include "mozilla/Services.h"
      40             : #include "nsITimer.h"
      41             : #include "mozIStorageService.h"
      42             : 
      43             : #include "mozilla/net/NeckoCommon.h"
      44             : #include <algorithm>
      45             : 
      46             : using namespace mozilla;
      47             : using namespace mozilla::net;
      48             : 
      49             : /******************************************************************************
      50             :  * nsCacheProfilePrefObserver
      51             :  *****************************************************************************/
      52             : #define DISK_CACHE_ENABLE_PREF      "browser.cache.disk.enable"
      53             : #define DISK_CACHE_DIR_PREF         "browser.cache.disk.parent_directory"
      54             : #define DISK_CACHE_SMART_SIZE_FIRST_RUN_PREF\
      55             :     "browser.cache.disk.smart_size.first_run"
      56             : #define DISK_CACHE_SMART_SIZE_ENABLED_PREF \
      57             :     "browser.cache.disk.smart_size.enabled"
      58             : #define DISK_CACHE_SMART_SIZE_PREF "browser.cache.disk.smart_size_cached_value"
      59             : #define DISK_CACHE_CAPACITY_PREF    "browser.cache.disk.capacity"
      60             : #define DISK_CACHE_MAX_ENTRY_SIZE_PREF "browser.cache.disk.max_entry_size"
      61             : #define DISK_CACHE_CAPACITY         256000
      62             : 
      63             : #define DISK_CACHE_USE_OLD_MAX_SMART_SIZE_PREF \
      64             :     "browser.cache.disk.smart_size.use_old_max"
      65             : 
      66             : #define OFFLINE_CACHE_ENABLE_PREF   "browser.cache.offline.enable"
      67             : #define OFFLINE_CACHE_DIR_PREF      "browser.cache.offline.parent_directory"
      68             : #define OFFLINE_CACHE_CAPACITY_PREF "browser.cache.offline.capacity"
      69             : #define OFFLINE_CACHE_CAPACITY      512000
      70             : 
      71             : #define MEMORY_CACHE_ENABLE_PREF    "browser.cache.memory.enable"
      72             : #define MEMORY_CACHE_CAPACITY_PREF  "browser.cache.memory.capacity"
      73             : #define MEMORY_CACHE_MAX_ENTRY_SIZE_PREF "browser.cache.memory.max_entry_size"
      74             : 
      75             : #define CACHE_COMPRESSION_LEVEL_PREF "browser.cache.compression_level"
      76             : #define CACHE_COMPRESSION_LEVEL     1
      77             : 
      78             : #define SANITIZE_ON_SHUTDOWN_PREF   "privacy.sanitize.sanitizeOnShutdown"
      79             : #define CLEAR_ON_SHUTDOWN_PREF      "privacy.clearOnShutdown.cache"
      80             : 
      81             : static const char * observerList[] = {
      82             :     "profile-before-change",
      83             :     "profile-do-change",
      84             :     NS_XPCOM_SHUTDOWN_OBSERVER_ID,
      85             :     "last-pb-context-exited",
      86             :     "suspend_process_notification",
      87             :     "resume_process_notification"
      88             : };
      89             : 
      90             : static const char * prefList[] = {
      91             :     DISK_CACHE_ENABLE_PREF,
      92             :     DISK_CACHE_SMART_SIZE_ENABLED_PREF,
      93             :     DISK_CACHE_CAPACITY_PREF,
      94             :     DISK_CACHE_DIR_PREF,
      95             :     DISK_CACHE_MAX_ENTRY_SIZE_PREF,
      96             :     DISK_CACHE_USE_OLD_MAX_SMART_SIZE_PREF,
      97             :     OFFLINE_CACHE_ENABLE_PREF,
      98             :     OFFLINE_CACHE_CAPACITY_PREF,
      99             :     OFFLINE_CACHE_DIR_PREF,
     100             :     MEMORY_CACHE_ENABLE_PREF,
     101             :     MEMORY_CACHE_CAPACITY_PREF,
     102             :     MEMORY_CACHE_MAX_ENTRY_SIZE_PREF,
     103             :     CACHE_COMPRESSION_LEVEL_PREF,
     104             :     SANITIZE_ON_SHUTDOWN_PREF,
     105             :     CLEAR_ON_SHUTDOWN_PREF
     106             : };
     107             : 
     108             : // Cache sizes, in KB
     109             : const int32_t DEFAULT_CACHE_SIZE = 250 * 1024;  // 250 MB
     110             : #ifdef ANDROID
     111             : const int32_t MAX_CACHE_SIZE = 200 * 1024;      // 200 MB
     112             : const int32_t OLD_MAX_CACHE_SIZE = 200 * 1024;  // 200 MB
     113             : #else
     114             : const int32_t MAX_CACHE_SIZE = 350 * 1024;      // 350 MB
     115             : const int32_t OLD_MAX_CACHE_SIZE = 1024 * 1024; //   1 GB
     116             : #endif
     117             : // Default cache size was 50 MB for many years until FF 4:
     118             : const int32_t PRE_GECKO_2_0_DEFAULT_CACHE_SIZE = 50 * 1024;
     119             : 
     120             : class nsCacheProfilePrefObserver : public nsIObserver
     121             : {
     122           0 :     virtual ~nsCacheProfilePrefObserver() {}
     123             : 
     124             : public:
     125             :     NS_DECL_THREADSAFE_ISUPPORTS
     126             :     NS_DECL_NSIOBSERVER
     127             : 
     128           1 :     nsCacheProfilePrefObserver()
     129           1 :         : mHaveProfile(false)
     130             :         , mDiskCacheEnabled(false)
     131             :         , mDiskCacheCapacity(0)
     132             :         , mDiskCacheMaxEntrySize(-1) // -1 means "no limit"
     133             :         , mSmartSizeEnabled(false)
     134             :         , mShouldUseOldMaxSmartSize(false)
     135             :         , mOfflineCacheEnabled(false)
     136             :         , mOfflineCacheCapacity(0)
     137             :         , mMemoryCacheEnabled(true)
     138             :         , mMemoryCacheCapacity(-1)
     139             :         , mMemoryCacheMaxEntrySize(-1) // -1 means "no limit"
     140             :         , mCacheCompressionLevel(CACHE_COMPRESSION_LEVEL)
     141             :         , mSanitizeOnShutdown(false)
     142           1 :         , mClearCacheOnShutdown(false)
     143             :     {
     144           1 :     }
     145             : 
     146             :     nsresult        Install();
     147             :     void            Remove();
     148             :     nsresult        ReadPrefs(nsIPrefBranch* branch);
     149             : 
     150             :     bool            DiskCacheEnabled();
     151           0 :     int32_t         DiskCacheCapacity()         { return mDiskCacheCapacity; }
     152             :     void            SetDiskCacheCapacity(int32_t);
     153           0 :     int32_t         DiskCacheMaxEntrySize()     { return mDiskCacheMaxEntrySize; }
     154           1 :     nsIFile *       DiskCacheParentDirectory()  { return mDiskCacheParentDirectory; }
     155           0 :     bool            SmartSizeEnabled()          { return mSmartSizeEnabled; }
     156             : 
     157           0 :     bool            ShouldUseOldMaxSmartSize()        { return mShouldUseOldMaxSmartSize; }
     158           0 :     void            SetUseNewMaxSmartSize(bool useNew)     { mShouldUseOldMaxSmartSize = !useNew; }
     159             : 
     160             :     bool            OfflineCacheEnabled();
     161           0 :     int32_t         OfflineCacheCapacity()         { return mOfflineCacheCapacity; }
     162           0 :     nsIFile *       OfflineCacheParentDirectory()  { return mOfflineCacheParentDirectory; }
     163             : 
     164             :     bool            MemoryCacheEnabled();
     165             :     int32_t         MemoryCacheCapacity();
     166           0 :     int32_t         MemoryCacheMaxEntrySize()     { return mMemoryCacheMaxEntrySize; }
     167             : 
     168             :     int32_t         CacheCompressionLevel();
     169             : 
     170           0 :     bool            SanitizeAtShutdown() { return mSanitizeOnShutdown && mClearCacheOnShutdown; }
     171             : 
     172             :     static uint32_t GetSmartCacheSize(const nsAString& cachePath,
     173             :                                       uint32_t currentSize,
     174             :                                       bool shouldUseOldMaxSmartSize);
     175             : 
     176             :     bool                    PermittedToSmartSize(nsIPrefBranch*, bool firstRun);
     177             : 
     178             : private:
     179             :     bool                    mHaveProfile;
     180             : 
     181             :     bool                    mDiskCacheEnabled;
     182             :     int32_t                 mDiskCacheCapacity; // in kilobytes
     183             :     int32_t                 mDiskCacheMaxEntrySize; // in kilobytes
     184             :     nsCOMPtr<nsIFile>       mDiskCacheParentDirectory;
     185             :     bool                    mSmartSizeEnabled;
     186             : 
     187             :     bool                    mShouldUseOldMaxSmartSize;
     188             : 
     189             :     bool                    mOfflineCacheEnabled;
     190             :     int32_t                 mOfflineCacheCapacity; // in kilobytes
     191             :     nsCOMPtr<nsIFile>       mOfflineCacheParentDirectory;
     192             : 
     193             :     bool                    mMemoryCacheEnabled;
     194             :     int32_t                 mMemoryCacheCapacity; // in kilobytes
     195             :     int32_t                 mMemoryCacheMaxEntrySize; // in kilobytes
     196             : 
     197             :     int32_t                 mCacheCompressionLevel;
     198             : 
     199             :     bool                    mSanitizeOnShutdown;
     200             :     bool                    mClearCacheOnShutdown;
     201             : };
     202             : 
     203         127 : NS_IMPL_ISUPPORTS(nsCacheProfilePrefObserver, nsIObserver)
     204             : 
     205           0 : class nsSetDiskSmartSizeCallback final : public nsITimerCallback
     206             : {
     207           0 :     ~nsSetDiskSmartSizeCallback() {}
     208             : 
     209             : public:
     210             :     NS_DECL_THREADSAFE_ISUPPORTS
     211             : 
     212           0 :     NS_IMETHOD Notify(nsITimer* aTimer) override {
     213           0 :         if (nsCacheService::gService) {
     214           0 :             nsCacheServiceAutoLock autoLock(LOCK_TELEM(NSSETDISKSMARTSIZECALLBACK_NOTIFY));
     215           0 :             nsCacheService::gService->SetDiskSmartSize_Locked();
     216           0 :             nsCacheService::gService->mSmartSizeTimer = nullptr;
     217             :         }
     218           0 :         return NS_OK;
     219             :     }
     220             : };
     221             : 
     222           0 : NS_IMPL_ISUPPORTS(nsSetDiskSmartSizeCallback, nsITimerCallback)
     223             : 
     224             : // Runnable sent to main thread after the cache IO thread calculates available
     225             : // disk space, so that there is no race in setting mDiskCacheCapacity.
     226           0 : class nsSetSmartSizeEvent: public Runnable
     227             : {
     228             : public:
     229           0 :   explicit nsSetSmartSizeEvent(int32_t smartSize)
     230           0 :     : mozilla::Runnable("nsSetSmartSizeEvent")
     231           0 :     , mSmartSize(smartSize)
     232             :   {
     233           0 :   }
     234             : 
     235           0 :   NS_IMETHOD Run()
     236             :   {
     237           0 :     NS_ASSERTION(NS_IsMainThread(),
     238             :                  "Setting smart size data off the main thread");
     239             : 
     240             :     // Main thread may have already called nsCacheService::Shutdown
     241           0 :     if (!nsCacheService::IsInitialized())
     242           0 :       return NS_ERROR_NOT_AVAILABLE;
     243             : 
     244             :     // Ensure smart sizing wasn't switched off while event was pending.
     245             :     // It is safe to access the observer without the lock since we are
     246             :     // on the main thread and the value changes only on the main thread.
     247           0 :     if (!nsCacheService::gService->mObserver->SmartSizeEnabled())
     248           0 :       return NS_OK;
     249             : 
     250           0 :     nsCacheService::SetDiskCacheCapacity(mSmartSize);
     251             : 
     252           0 :     nsCOMPtr<nsIPrefBranch> ps = do_GetService(NS_PREFSERVICE_CONTRACTID);
     253           0 :     if (!ps ||
     254           0 :         NS_FAILED(ps->SetIntPref(DISK_CACHE_SMART_SIZE_PREF, mSmartSize)))
     255           0 :       NS_WARNING("Failed to set smart size pref");
     256             : 
     257           0 :     return NS_OK;
     258             :     }
     259             : 
     260             : private:
     261             :     int32_t mSmartSize;
     262             : };
     263             : 
     264             : 
     265             : // Runnable sent from main thread to cacheIO thread
     266           0 : class nsGetSmartSizeEvent: public Runnable
     267             : {
     268             : public:
     269           0 :   nsGetSmartSizeEvent(const nsAString& cachePath,
     270             :                       uint32_t currentSize,
     271             :                       bool shouldUseOldMaxSmartSize)
     272           0 :     : mozilla::Runnable("nsGetSmartSizeEvent")
     273             :     , mCachePath(cachePath)
     274             :     , mCurrentSize(currentSize)
     275           0 :     , mShouldUseOldMaxSmartSize(shouldUseOldMaxSmartSize)
     276             :   {
     277           0 :   }
     278             : 
     279             :   // Calculates user's disk space available on a background thread and
     280             :   // dispatches this value back to the main thread.
     281           0 :   NS_IMETHOD Run() override
     282             :   {
     283             :     uint32_t size;
     284           0 :     size = nsCacheProfilePrefObserver::GetSmartCacheSize(
     285           0 :       mCachePath, mCurrentSize, mShouldUseOldMaxSmartSize);
     286           0 :     NS_DispatchToMainThread(new nsSetSmartSizeEvent(size));
     287           0 :     return NS_OK;
     288             :     }
     289             : 
     290             : private:
     291             :     nsString mCachePath;
     292             :     uint32_t mCurrentSize;
     293             :     bool     mShouldUseOldMaxSmartSize;
     294             : };
     295             : 
     296           0 : class nsBlockOnCacheThreadEvent : public Runnable {
     297             : public:
     298           0 :   nsBlockOnCacheThreadEvent()
     299           0 :     : mozilla::Runnable("nsBlockOnCacheThreadEvent")
     300             :   {
     301           0 :     }
     302           0 :     NS_IMETHOD Run() override
     303             :     {
     304           0 :         nsCacheServiceAutoLock autoLock(LOCK_TELEM(NSBLOCKONCACHETHREADEVENT_RUN));
     305           0 :         CACHE_LOG_DEBUG(("nsBlockOnCacheThreadEvent [%p]\n", this));
     306           0 :         nsCacheService::gService->mNotified = true;
     307           0 :         nsCacheService::gService->mCondVar.Notify();
     308           0 :         return NS_OK;
     309             :     }
     310             : };
     311             : 
     312             : 
     313             : nsresult
     314           1 : nsCacheProfilePrefObserver::Install()
     315             : {
     316             :     // install profile-change observer
     317             :     nsCOMPtr<nsIObserverService> observerService =
     318           2 :         mozilla::services::GetObserverService();
     319           1 :     if (!observerService)
     320           0 :         return NS_ERROR_FAILURE;
     321             : 
     322           1 :     nsresult rv, rv2 = NS_OK;
     323           7 :     for (unsigned int i=0; i<ArrayLength(observerList); i++) {
     324           6 :         rv = observerService->AddObserver(this, observerList[i], false);
     325           6 :         if (NS_FAILED(rv))
     326           0 :             rv2 = rv;
     327             :     }
     328             : 
     329             :     // install preferences observer
     330           2 :     nsCOMPtr<nsIPrefBranch> branch = do_GetService(NS_PREFSERVICE_CONTRACTID);
     331           1 :     if (!branch) return NS_ERROR_FAILURE;
     332             : 
     333          16 :     for (unsigned int i=0; i<ArrayLength(prefList); i++) {
     334          15 :         rv = branch->AddObserver(prefList[i], this, false);
     335          15 :         if (NS_FAILED(rv))
     336           0 :             rv2 = rv;
     337             :     }
     338             : 
     339             :     // Determine if we have a profile already
     340             :     //     Install() is called *after* the profile-after-change notification
     341             :     //     when there is only a single profile, or it is specified on the
     342             :     //     commandline at startup.
     343             :     //     In that case, we detect the presence of a profile by the existence
     344             :     //     of the NS_APP_USER_PROFILE_50_DIR directory.
     345             : 
     346           2 :     nsCOMPtr<nsIFile> directory;
     347           1 :     rv = NS_GetSpecialDirectory(NS_APP_USER_PROFILE_50_DIR,
     348           2 :                                 getter_AddRefs(directory));
     349           1 :     if (NS_SUCCEEDED(rv))
     350           1 :         mHaveProfile = true;
     351             : 
     352           1 :     rv = ReadPrefs(branch);
     353           1 :     NS_ENSURE_SUCCESS(rv, rv);
     354             : 
     355           1 :     return rv2;
     356             : }
     357             : 
     358             : 
     359             : void
     360           0 : nsCacheProfilePrefObserver::Remove()
     361             : {
     362             :     // remove Observer Service observers
     363             :     nsCOMPtr<nsIObserverService> obs =
     364           0 :         mozilla::services::GetObserverService();
     365           0 :     if (obs) {
     366           0 :         for (unsigned int i=0; i<ArrayLength(observerList); i++) {
     367           0 :             obs->RemoveObserver(this, observerList[i]);
     368             :         }
     369             :     }
     370             : 
     371             :     // remove Pref Service observers
     372             :     nsCOMPtr<nsIPrefBranch> prefs =
     373           0 :         do_GetService(NS_PREFSERVICE_CONTRACTID);
     374           0 :     if (!prefs)
     375           0 :         return;
     376           0 :     for (unsigned int i=0; i<ArrayLength(prefList); i++)
     377           0 :         prefs->RemoveObserver(prefList[i], this); // remove cache pref observers
     378             : }
     379             : 
     380             : void
     381           0 : nsCacheProfilePrefObserver::SetDiskCacheCapacity(int32_t capacity)
     382             : {
     383           0 :     mDiskCacheCapacity = std::max(0, capacity);
     384           0 : }
     385             : 
     386             : 
     387             : NS_IMETHODIMP
     388           0 : nsCacheProfilePrefObserver::Observe(nsISupports *     subject,
     389             :                                     const char *      topic,
     390             :                                     const char16_t * data_unicode)
     391             : {
     392             :     nsresult rv;
     393           0 :     NS_ConvertUTF16toUTF8 data(data_unicode);
     394           0 :     CACHE_LOG_INFO(("Observe [topic=%s data=%s]\n", topic, data.get()));
     395             : 
     396           0 :     if (!nsCacheService::IsInitialized()) {
     397           0 :         if (!strcmp("resume_process_notification", topic)) {
     398             :             // A suspended process has a closed cache, so re-open it here.
     399           0 :             nsCacheService::GlobalInstance()->Init();
     400             :         }
     401           0 :         return NS_OK;
     402             :     }
     403             : 
     404           0 :     if (!strcmp(NS_XPCOM_SHUTDOWN_OBSERVER_ID, topic)) {
     405             :         // xpcom going away, shutdown cache service
     406           0 :         nsCacheService::GlobalInstance()->Shutdown();
     407           0 :     } else if (!strcmp("profile-before-change", topic)) {
     408             :         // profile before change
     409           0 :         mHaveProfile = false;
     410             : 
     411             :         // XXX shutdown devices
     412           0 :         nsCacheService::OnProfileShutdown();
     413           0 :     } else if (!strcmp("suspend_process_notification", topic)) {
     414             :         // A suspended process may never return, so shutdown the cache to reduce
     415             :         // cache corruption.
     416           0 :         nsCacheService::GlobalInstance()->Shutdown();
     417           0 :     } else if (!strcmp("profile-do-change", topic)) {
     418             :         // profile after change
     419           0 :         mHaveProfile = true;
     420           0 :         nsCOMPtr<nsIPrefBranch> branch = do_GetService(NS_PREFSERVICE_CONTRACTID);
     421           0 :         if (!branch) {
     422           0 :             return NS_ERROR_FAILURE;
     423             :         }
     424           0 :         (void)ReadPrefs(branch);
     425           0 :         nsCacheService::OnProfileChanged();
     426             : 
     427           0 :     } else if (!strcmp(NS_PREFBRANCH_PREFCHANGE_TOPIC_ID, topic)) {
     428             : 
     429             :         // ignore pref changes until we're done switch profiles
     430           0 :         if (!mHaveProfile)
     431           0 :             return NS_OK;
     432             : 
     433           0 :         nsCOMPtr<nsIPrefBranch> branch = do_QueryInterface(subject, &rv);
     434           0 :         if (NS_FAILED(rv))
     435           0 :             return rv;
     436             : 
     437             :         // which preference changed?
     438           0 :         if (!strcmp(DISK_CACHE_ENABLE_PREF, data.get())) {
     439             : 
     440           0 :             rv = branch->GetBoolPref(DISK_CACHE_ENABLE_PREF,
     441           0 :                                      &mDiskCacheEnabled);
     442           0 :             if (NS_FAILED(rv))
     443           0 :                 return rv;
     444           0 :             nsCacheService::SetDiskCacheEnabled(DiskCacheEnabled());
     445             : 
     446           0 :         } else if (!strcmp(DISK_CACHE_CAPACITY_PREF, data.get())) {
     447             : 
     448           0 :             int32_t capacity = 0;
     449           0 :             rv = branch->GetIntPref(DISK_CACHE_CAPACITY_PREF, &capacity);
     450           0 :             if (NS_FAILED(rv))
     451           0 :                 return rv;
     452           0 :             mDiskCacheCapacity = std::max(0, capacity);
     453           0 :             nsCacheService::SetDiskCacheCapacity(mDiskCacheCapacity);
     454             : 
     455             :         // Update the cache capacity when smart sizing is turned on/off
     456           0 :         } else if (!strcmp(DISK_CACHE_SMART_SIZE_ENABLED_PREF, data.get())) {
     457             :             // Is the update because smartsizing was turned on, or off?
     458           0 :             rv = branch->GetBoolPref(DISK_CACHE_SMART_SIZE_ENABLED_PREF,
     459           0 :                                      &mSmartSizeEnabled);
     460           0 :             if (NS_FAILED(rv))
     461           0 :                 return rv;
     462           0 :             int32_t newCapacity = 0;
     463           0 :             if (mSmartSizeEnabled) {
     464           0 :                 nsCacheService::SetDiskSmartSize();
     465             :             } else {
     466             :                 // Smart sizing switched off: use user specified size
     467           0 :                 rv = branch->GetIntPref(DISK_CACHE_CAPACITY_PREF, &newCapacity);
     468           0 :                 if (NS_FAILED(rv))
     469           0 :                     return rv;
     470           0 :                 mDiskCacheCapacity = std::max(0, newCapacity);
     471           0 :                 nsCacheService::SetDiskCacheCapacity(mDiskCacheCapacity);
     472             :             }
     473           0 :         } else if (!strcmp(DISK_CACHE_USE_OLD_MAX_SMART_SIZE_PREF, data.get())) {
     474           0 :             rv = branch->GetBoolPref(DISK_CACHE_USE_OLD_MAX_SMART_SIZE_PREF,
     475           0 :                                      &mShouldUseOldMaxSmartSize);
     476           0 :             if (NS_FAILED(rv))
     477           0 :                 return rv;
     478           0 :         } else if (!strcmp(DISK_CACHE_MAX_ENTRY_SIZE_PREF, data.get())) {
     479             :             int32_t newMaxSize;
     480           0 :             rv = branch->GetIntPref(DISK_CACHE_MAX_ENTRY_SIZE_PREF,
     481           0 :                                     &newMaxSize);
     482           0 :             if (NS_FAILED(rv))
     483           0 :                 return rv;
     484             : 
     485           0 :             mDiskCacheMaxEntrySize = std::max(-1, newMaxSize);
     486           0 :             nsCacheService::SetDiskCacheMaxEntrySize(mDiskCacheMaxEntrySize);
     487             : 
     488             : #if 0
     489             :         } else if (!strcmp(DISK_CACHE_DIR_PREF, data.get())) {
     490             :             // XXX We probaby don't want to respond to this pref except after
     491             :             // XXX profile changes.  Ideally, there should be somekind of user
     492             :             // XXX notification that the pref change won't take effect until
     493             :             // XXX the next time the profile changes (browser launch)
     494             : #endif
     495             :         } else
     496             : 
     497             :         // which preference changed?
     498           0 :         if (!strcmp(OFFLINE_CACHE_ENABLE_PREF, data.get())) {
     499             : 
     500           0 :             rv = branch->GetBoolPref(OFFLINE_CACHE_ENABLE_PREF,
     501           0 :                                      &mOfflineCacheEnabled);
     502           0 :             if (NS_FAILED(rv))  return rv;
     503           0 :             nsCacheService::SetOfflineCacheEnabled(OfflineCacheEnabled());
     504             : 
     505           0 :         } else if (!strcmp(OFFLINE_CACHE_CAPACITY_PREF, data.get())) {
     506             : 
     507           0 :             int32_t capacity = 0;
     508           0 :             rv = branch->GetIntPref(OFFLINE_CACHE_CAPACITY_PREF, &capacity);
     509           0 :             if (NS_FAILED(rv))  return rv;
     510           0 :             mOfflineCacheCapacity = std::max(0, capacity);
     511           0 :             nsCacheService::SetOfflineCacheCapacity(mOfflineCacheCapacity);
     512             : #if 0
     513             :         } else if (!strcmp(OFFLINE_CACHE_DIR_PREF, data.get())) {
     514             :             // XXX We probaby don't want to respond to this pref except after
     515             :             // XXX profile changes.  Ideally, there should be some kind of user
     516             :             // XXX notification that the pref change won't take effect until
     517             :             // XXX the next time the profile changes (browser launch)
     518             : #endif
     519             :         } else
     520             : 
     521           0 :         if (!strcmp(MEMORY_CACHE_ENABLE_PREF, data.get())) {
     522             : 
     523           0 :             rv = branch->GetBoolPref(MEMORY_CACHE_ENABLE_PREF,
     524           0 :                                      &mMemoryCacheEnabled);
     525           0 :             if (NS_FAILED(rv))
     526           0 :                 return rv;
     527           0 :             nsCacheService::SetMemoryCache();
     528             : 
     529           0 :         } else if (!strcmp(MEMORY_CACHE_CAPACITY_PREF, data.get())) {
     530             : 
     531           0 :             mMemoryCacheCapacity = -1;
     532           0 :             (void) branch->GetIntPref(MEMORY_CACHE_CAPACITY_PREF,
     533           0 :                                       &mMemoryCacheCapacity);
     534           0 :             nsCacheService::SetMemoryCache();
     535           0 :         } else if (!strcmp(MEMORY_CACHE_MAX_ENTRY_SIZE_PREF, data.get())) {
     536             :             int32_t newMaxSize;
     537           0 :             rv = branch->GetIntPref(MEMORY_CACHE_MAX_ENTRY_SIZE_PREF,
     538           0 :                                      &newMaxSize);
     539           0 :             if (NS_FAILED(rv))
     540           0 :                 return rv;
     541             : 
     542           0 :             mMemoryCacheMaxEntrySize = std::max(-1, newMaxSize);
     543           0 :             nsCacheService::SetMemoryCacheMaxEntrySize(mMemoryCacheMaxEntrySize);
     544           0 :         } else if (!strcmp(CACHE_COMPRESSION_LEVEL_PREF, data.get())) {
     545           0 :             mCacheCompressionLevel = CACHE_COMPRESSION_LEVEL;
     546           0 :             (void)branch->GetIntPref(CACHE_COMPRESSION_LEVEL_PREF,
     547           0 :                                      &mCacheCompressionLevel);
     548           0 :             mCacheCompressionLevel = std::max(0, mCacheCompressionLevel);
     549           0 :             mCacheCompressionLevel = std::min(9, mCacheCompressionLevel);
     550           0 :         } else if (!strcmp(SANITIZE_ON_SHUTDOWN_PREF, data.get())) {
     551           0 :             rv = branch->GetBoolPref(SANITIZE_ON_SHUTDOWN_PREF,
     552           0 :                                      &mSanitizeOnShutdown);
     553           0 :             if (NS_FAILED(rv))
     554           0 :                 return rv;
     555           0 :             nsCacheService::SetDiskCacheEnabled(DiskCacheEnabled());
     556           0 :         } else if (!strcmp(CLEAR_ON_SHUTDOWN_PREF, data.get())) {
     557           0 :             rv = branch->GetBoolPref(CLEAR_ON_SHUTDOWN_PREF,
     558           0 :                                      &mClearCacheOnShutdown);
     559           0 :             if (NS_FAILED(rv))
     560           0 :                 return rv;
     561           0 :             nsCacheService::SetDiskCacheEnabled(DiskCacheEnabled());
     562             :         }
     563           0 :     } else if (!strcmp("last-pb-context-exited", topic)) {
     564           0 :         nsCacheService::LeavePrivateBrowsing();
     565             :     }
     566             : 
     567           0 :     return NS_OK;
     568             : }
     569             : 
     570             : // Returns default ("smart") size (in KB) of cache, given available disk space
     571             : // (also in KB)
     572             : static uint32_t
     573           0 : SmartCacheSize(const uint32_t availKB, bool shouldUseOldMaxSmartSize)
     574             : {
     575           0 :     uint32_t maxSize = shouldUseOldMaxSmartSize ? OLD_MAX_CACHE_SIZE : MAX_CACHE_SIZE;
     576             : 
     577           0 :     if (availKB > 100 * 1024 * 1024)
     578           0 :         return maxSize;  // skip computing if we're over 100 GB
     579             : 
     580             :     // Grow/shrink in 10 MB units, deliberately, so that in the common case we
     581             :     // don't shrink cache and evict items every time we startup (it's important
     582             :     // that we don't slow down startup benchmarks).
     583           0 :     uint32_t sz10MBs = 0;
     584           0 :     uint32_t avail10MBs = availKB / (1024*10);
     585             : 
     586             :     // .5% of space above 25 GB
     587           0 :     if (avail10MBs > 2500) {
     588           0 :         sz10MBs += static_cast<uint32_t>((avail10MBs - 2500)*.005);
     589           0 :         avail10MBs = 2500;
     590             :     }
     591             :     // 1% of space between 7GB -> 25 GB
     592           0 :     if (avail10MBs > 700) {
     593           0 :         sz10MBs += static_cast<uint32_t>((avail10MBs - 700)*.01);
     594           0 :         avail10MBs = 700;
     595             :     }
     596             :     // 5% of space between 500 MB -> 7 GB
     597           0 :     if (avail10MBs > 50) {
     598           0 :         sz10MBs += static_cast<uint32_t>((avail10MBs - 50)*.05);
     599           0 :         avail10MBs = 50;
     600             :     }
     601             : 
     602             : #ifdef ANDROID
     603             :     // On Android, smaller/older devices may have very little storage and
     604             :     // device owners may be sensitive to storage footprint: Use a smaller
     605             :     // percentage of available space and a smaller minimum.
     606             : 
     607             :     // 20% of space up to 500 MB (10 MB min)
     608             :     sz10MBs += std::max<uint32_t>(1, static_cast<uint32_t>(avail10MBs * .2));
     609             : #else
     610             :     // 40% of space up to 500 MB (50 MB min)
     611           0 :     sz10MBs += std::max<uint32_t>(5, static_cast<uint32_t>(avail10MBs * .4));
     612             : #endif
     613             : 
     614           0 :     return std::min<uint32_t>(maxSize, sz10MBs * 10 * 1024);
     615             : }
     616             : 
     617             :  /* Computes our best guess for the default size of the user's disk cache,
     618             :   * based on the amount of space they have free on their hard drive.
     619             :   * We use a tiered scheme: the more space available,
     620             :   * the larger the disk cache will be. However, we do not want
     621             :   * to enable the disk cache to grow to an unbounded size, so the larger the
     622             :   * user's available space is, the smaller of a percentage we take. We set a
     623             :   * lower bound of 50MB and an upper bound of 1GB.
     624             :   *
     625             :   *@param:  None.
     626             :   *@return: The size that the user's disk cache should default to, in kBytes.
     627             :   */
     628             : uint32_t
     629           0 : nsCacheProfilePrefObserver::GetSmartCacheSize(const nsAString& cachePath,
     630             :                                               uint32_t currentSize,
     631             :                                               bool shouldUseOldMaxSmartSize)
     632             : {
     633             :     // Check for free space on device where cache directory lives
     634             :     nsresult rv;
     635             :     nsCOMPtr<nsIFile>
     636           0 :         cacheDirectory (do_CreateInstance(NS_LOCAL_FILE_CONTRACTID, &rv));
     637           0 :     if (NS_FAILED(rv) || !cacheDirectory)
     638           0 :         return DEFAULT_CACHE_SIZE;
     639           0 :     rv = cacheDirectory->InitWithPath(cachePath);
     640           0 :     if (NS_FAILED(rv))
     641           0 :         return DEFAULT_CACHE_SIZE;
     642             :     int64_t bytesAvailable;
     643           0 :     rv = cacheDirectory->GetDiskSpaceAvailable(&bytesAvailable);
     644           0 :     if (NS_FAILED(rv))
     645           0 :         return DEFAULT_CACHE_SIZE;
     646             : 
     647           0 :     return SmartCacheSize(static_cast<uint32_t>((bytesAvailable / 1024) +
     648             :                                                 currentSize),
     649           0 :                           shouldUseOldMaxSmartSize);
     650             : }
     651             : 
     652             : /* Determine if we are permitted to dynamically size the user's disk cache based
     653             :  * on their disk space available. We may do this so long as the pref
     654             :  * smart_size.enabled is true.
     655             :  */
     656             : bool
     657           1 : nsCacheProfilePrefObserver::PermittedToSmartSize(nsIPrefBranch* branch, bool
     658             :                                                  firstRun)
     659             : {
     660             :     nsresult rv;
     661           1 :     if (firstRun) {
     662             :         // check if user has set cache size in the past
     663             :         bool userSet;
     664           0 :         rv = branch->PrefHasUserValue(DISK_CACHE_CAPACITY_PREF, &userSet);
     665           0 :         if (NS_FAILED(rv)) userSet = true;
     666           0 :         if (userSet) {
     667             :             int32_t oldCapacity;
     668             :             // If user explicitly set cache size to be smaller than old default
     669             :             // of 50 MB, then keep user's value. Otherwise use smart sizing.
     670           0 :             rv = branch->GetIntPref(DISK_CACHE_CAPACITY_PREF, &oldCapacity);
     671           0 :             if (oldCapacity < PRE_GECKO_2_0_DEFAULT_CACHE_SIZE) {
     672           0 :                 mSmartSizeEnabled = false;
     673           0 :                 branch->SetBoolPref(DISK_CACHE_SMART_SIZE_ENABLED_PREF,
     674           0 :                                     mSmartSizeEnabled);
     675           0 :                 return mSmartSizeEnabled;
     676             :             }
     677             :         }
     678             :         // Set manual setting to MAX cache size as starting val for any
     679             :         // adjustment by user: (bug 559942 comment 65)
     680           0 :         int32_t maxSize = mShouldUseOldMaxSmartSize ? OLD_MAX_CACHE_SIZE : MAX_CACHE_SIZE;
     681           0 :         branch->SetIntPref(DISK_CACHE_CAPACITY_PREF, maxSize);
     682             :     }
     683             : 
     684           1 :     rv = branch->GetBoolPref(DISK_CACHE_SMART_SIZE_ENABLED_PREF,
     685           2 :                              &mSmartSizeEnabled);
     686           1 :     if (NS_FAILED(rv))
     687           0 :         mSmartSizeEnabled = false;
     688           1 :     return mSmartSizeEnabled;
     689             : }
     690             : 
     691             : 
     692             : nsresult
     693           1 : nsCacheProfilePrefObserver::ReadPrefs(nsIPrefBranch* branch)
     694             : {
     695           1 :     nsresult rv = NS_OK;
     696             : 
     697             :     // read disk cache device prefs
     698           1 :     mDiskCacheEnabled = true;  // presume disk cache is enabled
     699           1 :     (void) branch->GetBoolPref(DISK_CACHE_ENABLE_PREF, &mDiskCacheEnabled);
     700             : 
     701           1 :     mDiskCacheCapacity = DISK_CACHE_CAPACITY;
     702           1 :     (void)branch->GetIntPref(DISK_CACHE_CAPACITY_PREF, &mDiskCacheCapacity);
     703           1 :     mDiskCacheCapacity = std::max(0, mDiskCacheCapacity);
     704             : 
     705           1 :     (void) branch->GetIntPref(DISK_CACHE_MAX_ENTRY_SIZE_PREF,
     706           2 :                               &mDiskCacheMaxEntrySize);
     707           1 :     mDiskCacheMaxEntrySize = std::max(-1, mDiskCacheMaxEntrySize);
     708             : 
     709           1 :     (void) branch->GetComplexValue(DISK_CACHE_DIR_PREF,     // ignore error
     710             :                                    NS_GET_IID(nsIFile),
     711           2 :                                    getter_AddRefs(mDiskCacheParentDirectory));
     712             : 
     713           1 :     (void) branch->GetBoolPref(DISK_CACHE_USE_OLD_MAX_SMART_SIZE_PREF,
     714           2 :                                &mShouldUseOldMaxSmartSize);
     715             : 
     716           1 :     if (!mDiskCacheParentDirectory) {
     717           2 :         nsCOMPtr<nsIFile>  directory;
     718             : 
     719             :         // try to get the disk cache parent directory
     720           1 :         rv = NS_GetSpecialDirectory(NS_APP_CACHE_PARENT_DIR,
     721           2 :                                     getter_AddRefs(directory));
     722           1 :         if (NS_FAILED(rv)) {
     723             :             // try to get the profile directory (there may not be a profile yet)
     724           2 :             nsCOMPtr<nsIFile> profDir;
     725           1 :             NS_GetSpecialDirectory(NS_APP_USER_PROFILE_50_DIR,
     726           2 :                                    getter_AddRefs(profDir));
     727           1 :             NS_GetSpecialDirectory(NS_APP_USER_PROFILE_LOCAL_50_DIR,
     728           2 :                                    getter_AddRefs(directory));
     729           1 :             if (!directory)
     730           0 :                 directory = profDir;
     731           1 :             else if (profDir) {
     732           1 :                 nsCacheService::MoveOrRemoveDiskCache(profDir, directory,
     733           1 :                                                       "Cache");
     734             :             }
     735             :         }
     736             :         // use file cache in build tree only if asked, to avoid cache dir litter
     737           1 :         if (!directory && PR_GetEnv("NECKO_DEV_ENABLE_DISK_CACHE")) {
     738           0 :             rv = NS_GetSpecialDirectory(NS_XPCOM_CURRENT_PROCESS_DIR,
     739           0 :                                         getter_AddRefs(directory));
     740             :         }
     741           1 :         if (directory)
     742           1 :             mDiskCacheParentDirectory = do_QueryInterface(directory, &rv);
     743             :     }
     744           1 :     if (mDiskCacheParentDirectory) {
     745             :         bool firstSmartSizeRun;
     746           1 :         rv = branch->GetBoolPref(DISK_CACHE_SMART_SIZE_FIRST_RUN_PREF,
     747           1 :                                  &firstSmartSizeRun);
     748           1 :         if (NS_FAILED(rv))
     749           0 :             firstSmartSizeRun = false;
     750           1 :         if (PermittedToSmartSize(branch, firstSmartSizeRun)) {
     751             :             // Avoid evictions: use previous cache size until smart size event
     752             :             // updates mDiskCacheCapacity
     753           0 :             rv = branch->GetIntPref(firstSmartSizeRun ?
     754             :                                     DISK_CACHE_CAPACITY_PREF :
     755             :                                     DISK_CACHE_SMART_SIZE_PREF,
     756           0 :                                     &mDiskCacheCapacity);
     757           0 :             if (NS_FAILED(rv))
     758           0 :                 mDiskCacheCapacity = DEFAULT_CACHE_SIZE;
     759             :         }
     760             : 
     761           1 :         if (firstSmartSizeRun) {
     762             :             // It is no longer our first run
     763           0 :             rv = branch->SetBoolPref(DISK_CACHE_SMART_SIZE_FIRST_RUN_PREF,
     764           0 :                                      false);
     765           0 :             if (NS_FAILED(rv))
     766           0 :                 NS_WARNING("Failed setting first_run pref in ReadPrefs.");
     767             :         }
     768             :     }
     769             : 
     770             :     // read offline cache device prefs
     771           1 :     mOfflineCacheEnabled = true;  // presume offline cache is enabled
     772           1 :     (void) branch->GetBoolPref(OFFLINE_CACHE_ENABLE_PREF,
     773           2 :                               &mOfflineCacheEnabled);
     774             : 
     775           1 :     mOfflineCacheCapacity = OFFLINE_CACHE_CAPACITY;
     776           1 :     (void)branch->GetIntPref(OFFLINE_CACHE_CAPACITY_PREF,
     777           2 :                              &mOfflineCacheCapacity);
     778           1 :     mOfflineCacheCapacity = std::max(0, mOfflineCacheCapacity);
     779             : 
     780           1 :     (void) branch->GetComplexValue(OFFLINE_CACHE_DIR_PREF,     // ignore error
     781             :                                    NS_GET_IID(nsIFile),
     782           2 :                                    getter_AddRefs(mOfflineCacheParentDirectory));
     783             : 
     784           1 :     if (!mOfflineCacheParentDirectory) {
     785           2 :         nsCOMPtr<nsIFile>  directory;
     786             : 
     787             :         // try to get the offline cache parent directory
     788           1 :         rv = NS_GetSpecialDirectory(NS_APP_CACHE_PARENT_DIR,
     789           2 :                                     getter_AddRefs(directory));
     790           1 :         if (NS_FAILED(rv)) {
     791             :             // try to get the profile directory (there may not be a profile yet)
     792           2 :             nsCOMPtr<nsIFile> profDir;
     793           1 :             NS_GetSpecialDirectory(NS_APP_USER_PROFILE_50_DIR,
     794           2 :                                    getter_AddRefs(profDir));
     795           1 :             NS_GetSpecialDirectory(NS_APP_USER_PROFILE_LOCAL_50_DIR,
     796           2 :                                    getter_AddRefs(directory));
     797           1 :             if (!directory)
     798           0 :                 directory = profDir;
     799           1 :             else if (profDir) {
     800           1 :                 nsCacheService::MoveOrRemoveDiskCache(profDir, directory,
     801           1 :                                                       "OfflineCache");
     802             :             }
     803             :         }
     804             : #if DEBUG
     805           1 :         if (!directory) {
     806             :             // use current process directory during development
     807           0 :             rv = NS_GetSpecialDirectory(NS_XPCOM_CURRENT_PROCESS_DIR,
     808           0 :                                         getter_AddRefs(directory));
     809             :         }
     810             : #endif
     811           1 :         if (directory)
     812           1 :             mOfflineCacheParentDirectory = do_QueryInterface(directory, &rv);
     813             :     }
     814             : 
     815             :     // read memory cache device prefs
     816           1 :     (void) branch->GetBoolPref(MEMORY_CACHE_ENABLE_PREF, &mMemoryCacheEnabled);
     817             : 
     818           1 :     mMemoryCacheCapacity = -1;
     819           1 :     (void) branch->GetIntPref(MEMORY_CACHE_CAPACITY_PREF,
     820           2 :                               &mMemoryCacheCapacity);
     821             : 
     822           1 :     (void) branch->GetIntPref(MEMORY_CACHE_MAX_ENTRY_SIZE_PREF,
     823           2 :                               &mMemoryCacheMaxEntrySize);
     824           1 :     mMemoryCacheMaxEntrySize = std::max(-1, mMemoryCacheMaxEntrySize);
     825             : 
     826             :     // read cache compression level pref
     827           1 :     mCacheCompressionLevel = CACHE_COMPRESSION_LEVEL;
     828           1 :     (void)branch->GetIntPref(CACHE_COMPRESSION_LEVEL_PREF,
     829           2 :                              &mCacheCompressionLevel);
     830           1 :     mCacheCompressionLevel = std::max(0, mCacheCompressionLevel);
     831           1 :     mCacheCompressionLevel = std::min(9, mCacheCompressionLevel);
     832             : 
     833             :     // read cache shutdown sanitization prefs
     834           1 :     (void) branch->GetBoolPref(SANITIZE_ON_SHUTDOWN_PREF,
     835           2 :                                &mSanitizeOnShutdown);
     836           1 :     (void) branch->GetBoolPref(CLEAR_ON_SHUTDOWN_PREF,
     837           2 :                                &mClearCacheOnShutdown);
     838             : 
     839           1 :     return rv;
     840             : }
     841             : 
     842             : nsresult
     843           0 : nsCacheService::DispatchToCacheIOThread(nsIRunnable* event)
     844             : {
     845           0 :     if (!gService || !gService->mCacheIOThread) return NS_ERROR_NOT_AVAILABLE;
     846           0 :     return gService->mCacheIOThread->Dispatch(event, NS_DISPATCH_NORMAL);
     847             : }
     848             : 
     849             : nsresult
     850           0 : nsCacheService::SyncWithCacheIOThread()
     851             : {
     852           0 :     if (!gService || !gService->mCacheIOThread) return NS_ERROR_NOT_AVAILABLE;
     853           0 :     gService->mLock.AssertCurrentThreadOwns();
     854             : 
     855           0 :     nsCOMPtr<nsIRunnable> event = new nsBlockOnCacheThreadEvent();
     856             : 
     857             :     // dispatch event - it will notify the monitor when it's done
     858             :     nsresult rv =
     859           0 :         gService->mCacheIOThread->Dispatch(event, NS_DISPATCH_NORMAL);
     860           0 :     if (NS_FAILED(rv)) {
     861           0 :         NS_WARNING("Failed dispatching block-event");
     862           0 :         return NS_ERROR_UNEXPECTED;
     863             :     }
     864             : 
     865             :     // wait until notified, then return
     866           0 :     gService->mNotified = false;
     867           0 :     while (!gService->mNotified) {
     868           0 :       gService->mCondVar.Wait();
     869             :     }
     870             : 
     871           0 :     return NS_OK;
     872             : }
     873             : 
     874             : 
     875             : bool
     876           1 : nsCacheProfilePrefObserver::DiskCacheEnabled()
     877             : {
     878           1 :     if ((mDiskCacheCapacity == 0) || (!mDiskCacheParentDirectory))  return false;
     879           1 :     return mDiskCacheEnabled && (!mSanitizeOnShutdown || !mClearCacheOnShutdown);
     880             : }
     881             : 
     882             : 
     883             : bool
     884           1 : nsCacheProfilePrefObserver::OfflineCacheEnabled()
     885             : {
     886           1 :     if ((mOfflineCacheCapacity == 0) || (!mOfflineCacheParentDirectory))
     887           0 :         return false;
     888             : 
     889           1 :     return mOfflineCacheEnabled;
     890             : }
     891             : 
     892             : 
     893             : bool
     894           1 : nsCacheProfilePrefObserver::MemoryCacheEnabled()
     895             : {
     896           1 :     if (mMemoryCacheCapacity == 0)  return false;
     897           1 :     return mMemoryCacheEnabled;
     898             : }
     899             : 
     900             : 
     901             : /**
     902             :  * MemoryCacheCapacity
     903             :  *
     904             :  * If the browser.cache.memory.capacity preference is positive, we use that
     905             :  * value for the amount of memory available for the cache.
     906             :  *
     907             :  * If browser.cache.memory.capacity is zero, the memory cache is disabled.
     908             :  *
     909             :  * If browser.cache.memory.capacity is negative or not present, we use a
     910             :  * formula that grows less than linearly with the amount of system memory,
     911             :  * with an upper limit on the cache size. No matter how much physical RAM is
     912             :  * present, the default cache size would not exceed 32 MB. This maximum would
     913             :  * apply only to systems with more than 4 GB of RAM (e.g. terminal servers)
     914             :  *
     915             :  *   RAM   Cache
     916             :  *   ---   -----
     917             :  *   32 Mb   2 Mb
     918             :  *   64 Mb   4 Mb
     919             :  *  128 Mb   6 Mb
     920             :  *  256 Mb  10 Mb
     921             :  *  512 Mb  14 Mb
     922             :  * 1024 Mb  18 Mb
     923             :  * 2048 Mb  24 Mb
     924             :  * 4096 Mb  30 Mb
     925             :  *
     926             :  * The equation for this is (for cache size C and memory size K (kbytes)):
     927             :  *  x = log2(K) - 14
     928             :  *  C = x^2/3 + x + 2/3 + 0.1 (0.1 for rounding)
     929             :  *  if (C > 32) C = 32
     930             :  */
     931             : 
     932             : int32_t
     933           0 : nsCacheProfilePrefObserver::MemoryCacheCapacity()
     934             : {
     935           0 :     int32_t capacity = mMemoryCacheCapacity;
     936           0 :     if (capacity >= 0) {
     937           0 :         CACHE_LOG_DEBUG(("Memory cache capacity forced to %d\n", capacity));
     938           0 :         return capacity;
     939             :     }
     940             : 
     941           0 :     static uint64_t bytes = PR_GetPhysicalMemorySize();
     942           0 :     CACHE_LOG_DEBUG(("Physical Memory size is %" PRIu64 "\n", bytes));
     943             : 
     944             :     // If getting the physical memory failed, arbitrarily assume
     945             :     // 32 MB of RAM. We use a low default to have a reasonable
     946             :     // size on all the devices we support.
     947           0 :     if (bytes == 0)
     948           0 :         bytes = 32 * 1024 * 1024;
     949             : 
     950             :     // Conversion from unsigned int64_t to double doesn't work on all platforms.
     951             :     // We need to truncate the value at INT64_MAX to make sure we don't
     952             :     // overflow.
     953           0 :     if (bytes > INT64_MAX)
     954           0 :         bytes = INT64_MAX;
     955             : 
     956           0 :     uint64_t kbytes = bytes >> 10;
     957             : 
     958           0 :     double kBytesD = double(kbytes);
     959             : 
     960           0 :     double x = log(kBytesD)/log(2.0) - 14;
     961           0 :     if (x > 0) {
     962           0 :         capacity = (int32_t)(x * x / 3.0 + x + 2.0 / 3 + 0.1); // 0.1 for rounding
     963           0 :         if (capacity > 32)
     964           0 :             capacity = 32;
     965           0 :         capacity   *= 1024;
     966             :     } else {
     967           0 :         capacity    = 0;
     968             :     }
     969             : 
     970           0 :     return capacity;
     971             : }
     972             : 
     973             : int32_t
     974           0 : nsCacheProfilePrefObserver::CacheCompressionLevel()
     975             : {
     976           0 :     return mCacheCompressionLevel;
     977             : }
     978             : 
     979             : /******************************************************************************
     980             :  * nsProcessRequestEvent
     981             :  *****************************************************************************/
     982             : 
     983             : class nsProcessRequestEvent : public Runnable {
     984             : public:
     985           0 :   explicit nsProcessRequestEvent(nsCacheRequest* aRequest)
     986           0 :     : mozilla::Runnable("nsProcessRequestEvent")
     987             :   {
     988           0 :     mRequest = aRequest;
     989           0 :     }
     990             : 
     991           0 :     NS_IMETHOD Run() override
     992             :     {
     993             :         nsresult rv;
     994             : 
     995           0 :         NS_ASSERTION(mRequest->mListener,
     996             :                      "Sync OpenCacheEntry() posted to background thread!");
     997             : 
     998           0 :         nsCacheServiceAutoLock lock(LOCK_TELEM(NSPROCESSREQUESTEVENT_RUN));
     999           0 :         rv = nsCacheService::gService->ProcessRequest(mRequest,
    1000             :                                                       false,
    1001           0 :                                                       nullptr);
    1002             : 
    1003             :         // Don't delete the request if it was queued
    1004           0 :         if (!(mRequest->IsBlocking() &&
    1005             :             rv == NS_ERROR_CACHE_WAIT_FOR_VALIDATION))
    1006           0 :             delete mRequest;
    1007             : 
    1008           0 :         return NS_OK;
    1009             :     }
    1010             : 
    1011             : protected:
    1012           0 :     virtual ~nsProcessRequestEvent() {}
    1013             : 
    1014             : private:
    1015             :     nsCacheRequest *mRequest;
    1016             : };
    1017             : 
    1018             : /******************************************************************************
    1019             :  * nsDoomEvent
    1020             :  *****************************************************************************/
    1021             : 
    1022           0 : class nsDoomEvent : public Runnable {
    1023             : public:
    1024           0 :     nsDoomEvent(nsCacheSession *session,
    1025             :                 const nsACString &key,
    1026             :                 nsICacheListener *listener)
    1027           0 :       : mozilla::Runnable("nsDoomEvent")
    1028             :     {
    1029           0 :         mKey = *session->ClientID();
    1030           0 :         mKey.Append(':');
    1031           0 :         mKey.Append(key);
    1032           0 :         mStoragePolicy = session->StoragePolicy();
    1033           0 :         mListener = listener;
    1034           0 :         mEventTarget = GetCurrentThreadEventTarget();
    1035             :         // We addref the listener here and release it in nsNotifyDoomListener
    1036             :         // on the callers thread. If posting of nsNotifyDoomListener event fails
    1037             :         // we leak the listener which is better than releasing it on a wrong
    1038             :         // thread.
    1039           0 :         NS_IF_ADDREF(mListener);
    1040           0 :     }
    1041             : 
    1042           0 :     NS_IMETHOD Run() override
    1043             :     {
    1044           0 :         nsCacheServiceAutoLock lock;
    1045             : 
    1046           0 :         bool foundActive = true;
    1047           0 :         nsresult status = NS_ERROR_NOT_AVAILABLE;
    1048             :         nsCacheEntry *entry;
    1049           0 :         entry = nsCacheService::gService->mActiveEntries.GetEntry(&mKey);
    1050           0 :         if (!entry) {
    1051           0 :             bool collision = false;
    1052           0 :             foundActive = false;
    1053           0 :             entry = nsCacheService::gService->SearchCacheDevices(&mKey,
    1054             :                                                                  mStoragePolicy,
    1055           0 :                                                                  &collision);
    1056             :         }
    1057             : 
    1058           0 :         if (entry) {
    1059           0 :             status = NS_OK;
    1060           0 :             nsCacheService::gService->DoomEntry_Internal(entry, foundActive);
    1061             :         }
    1062             : 
    1063           0 :         if (mListener) {
    1064           0 :             mEventTarget->Dispatch(new nsNotifyDoomListener(mListener, status),
    1065           0 :                                    NS_DISPATCH_NORMAL);
    1066             :             // posted event will release the reference on the correct thread
    1067           0 :             mListener = nullptr;
    1068             :         }
    1069             : 
    1070           0 :         return NS_OK;
    1071             :     }
    1072             : 
    1073             : private:
    1074             :     nsCString             mKey;
    1075             :     nsCacheStoragePolicy  mStoragePolicy;
    1076             :     nsICacheListener     *mListener;
    1077             :     nsCOMPtr<nsIEventTarget> mEventTarget;
    1078             : };
    1079             : 
    1080             : /******************************************************************************
    1081             :  * nsCacheService
    1082             :  *****************************************************************************/
    1083             : nsCacheService *   nsCacheService::gService = nullptr;
    1084             : 
    1085          13 : NS_IMPL_ISUPPORTS(nsCacheService, nsICacheService, nsICacheServiceInternal,
    1086             :                   nsIMemoryReporter)
    1087             : 
    1088           1 : nsCacheService::nsCacheService()
    1089             :     : mObserver(nullptr),
    1090             :       mLock("nsCacheService.mLock"),
    1091             :       mCondVar(mLock, "nsCacheService.mCondVar"),
    1092             :       mNotified(false),
    1093             :       mTimeStampLock("nsCacheService.mTimeStampLock"),
    1094             :       mInitialized(false),
    1095             :       mClearingEntries(false),
    1096             :       mEnableMemoryDevice(true),
    1097             :       mEnableDiskDevice(true),
    1098             :       mMemoryDevice(nullptr),
    1099             :       mDiskDevice(nullptr),
    1100             :       mOfflineDevice(nullptr),
    1101             :       mTotalEntries(0),
    1102             :       mCacheHits(0),
    1103             :       mCacheMisses(0),
    1104             :       mMaxKeyLength(0),
    1105             :       mMaxDataSize(0),
    1106             :       mMaxMetaSize(0),
    1107             :       mDeactivateFailures(0),
    1108           1 :       mDeactivatedUnboundEntries(0)
    1109             : {
    1110           1 :     NS_ASSERTION(gService==nullptr, "multiple nsCacheService instances!");
    1111           1 :     gService = this;
    1112             : 
    1113             :     // create list of cache devices
    1114           1 :     PR_INIT_CLIST(&mDoomedEntries);
    1115           1 : }
    1116             : 
    1117           0 : nsCacheService::~nsCacheService()
    1118             : {
    1119           0 :     if (mInitialized) // Shutdown hasn't been called yet.
    1120           0 :         (void) Shutdown();
    1121             : 
    1122           0 :     if (mObserver) {
    1123           0 :         mObserver->Remove();
    1124           0 :         NS_RELEASE(mObserver);
    1125             :     }
    1126             : 
    1127           0 :     gService = nullptr;
    1128           0 : }
    1129             : 
    1130             : 
    1131             : nsresult
    1132           1 : nsCacheService::Init()
    1133             : {
    1134             :     // Thie method must be called on the main thread because mCacheIOThread must
    1135             :     // only be modified on the main thread.
    1136           1 :     if (!NS_IsMainThread()) {
    1137           0 :         NS_ERROR("nsCacheService::Init called off the main thread");
    1138           0 :         return NS_ERROR_NOT_SAME_THREAD;
    1139             :     }
    1140             : 
    1141           1 :     NS_ASSERTION(!mInitialized, "nsCacheService already initialized.");
    1142           1 :     if (mInitialized)
    1143           0 :         return NS_ERROR_ALREADY_INITIALIZED;
    1144             : 
    1145           1 :     if (mozilla::net::IsNeckoChild()) {
    1146           0 :         return NS_ERROR_UNEXPECTED;
    1147             :     }
    1148             : 
    1149             :     nsresult rv;
    1150             : 
    1151           1 :     mStorageService = do_GetService("@mozilla.org/storage/service;1", &rv);
    1152           1 :     NS_ENSURE_SUCCESS(rv, rv);
    1153             : 
    1154           1 :     rv = NS_NewNamedThread("Cache I/O",
    1155           2 :                            getter_AddRefs(mCacheIOThread));
    1156           1 :     if (NS_FAILED(rv)) {
    1157           0 :         MOZ_CRASH("Can't create cache IO thread");
    1158             :     }
    1159             : 
    1160           1 :     rv = nsDeleteDir::Init();
    1161           1 :     if (NS_FAILED(rv)) {
    1162           0 :         NS_WARNING("Can't initialize nsDeleteDir");
    1163             :     }
    1164             : 
    1165             :     // initialize hashtable for active cache entries
    1166           1 :     mActiveEntries.Init();
    1167             : 
    1168             :     // create profile/preference observer
    1169           1 :     if (!mObserver) {
    1170           1 :       mObserver = new nsCacheProfilePrefObserver();
    1171           1 :       NS_ADDREF(mObserver);
    1172           1 :       mObserver->Install();
    1173             :     }
    1174             : 
    1175           1 :     mEnableDiskDevice    = mObserver->DiskCacheEnabled();
    1176           1 :     mEnableOfflineDevice = mObserver->OfflineCacheEnabled();
    1177           1 :     mEnableMemoryDevice  = mObserver->MemoryCacheEnabled();
    1178             : 
    1179           1 :     RegisterWeakMemoryReporter(this);
    1180             : 
    1181           1 :     mInitialized = true;
    1182           1 :     return NS_OK;
    1183             : }
    1184             : 
    1185             : void
    1186           0 : nsCacheService::Shutdown()
    1187             : {
    1188             :     // This method must be called on the main thread because mCacheIOThread must
    1189             :     // only be modified on the main thread.
    1190           0 :     if (!NS_IsMainThread()) {
    1191           0 :         MOZ_CRASH("nsCacheService::Shutdown called off the main thread");
    1192             :     }
    1193             : 
    1194           0 :     nsCOMPtr<nsIThread> cacheIOThread;
    1195           0 :     Telemetry::AutoTimer<Telemetry::NETWORK_DISK_CACHE_SHUTDOWN> totalTimer;
    1196             : 
    1197           0 :     bool shouldSanitize = false;
    1198           0 :     nsCOMPtr<nsIFile> parentDir;
    1199             : 
    1200             :     {
    1201           0 :         nsCacheServiceAutoLock lock(LOCK_TELEM(NSCACHESERVICE_SHUTDOWN));
    1202           0 :         NS_ASSERTION(mInitialized,
    1203             :             "can't shutdown nsCacheService unless it has been initialized.");
    1204           0 :         if (!mInitialized)
    1205           0 :             return;
    1206             : 
    1207           0 :         mClearingEntries = true;
    1208           0 :         DoomActiveEntries(nullptr);
    1209             :     }
    1210             : 
    1211           0 :     CloseAllStreams();
    1212             : 
    1213           0 :     UnregisterWeakMemoryReporter(this);
    1214             : 
    1215             :     {
    1216           0 :         nsCacheServiceAutoLock lock(LOCK_TELEM(NSCACHESERVICE_SHUTDOWN));
    1217           0 :         NS_ASSERTION(mInitialized, "Bad state");
    1218             : 
    1219           0 :         mInitialized = false;
    1220             : 
    1221             :         // Clear entries
    1222           0 :         ClearDoomList();
    1223             : 
    1224           0 :         if (mSmartSizeTimer) {
    1225           0 :             mSmartSizeTimer->Cancel();
    1226           0 :             mSmartSizeTimer = nullptr;
    1227             :         }
    1228             : 
    1229             :         // Make sure to wait for any pending cache-operations before
    1230             :         // proceeding with destructive actions (bug #620660)
    1231           0 :         (void) SyncWithCacheIOThread();
    1232           0 :         mActiveEntries.Shutdown();
    1233             : 
    1234             :         // obtain the disk cache directory in case we need to sanitize it
    1235           0 :         parentDir = mObserver->DiskCacheParentDirectory();
    1236           0 :         shouldSanitize = mObserver->SanitizeAtShutdown();
    1237             : 
    1238             :         // deallocate memory and disk caches
    1239           0 :         delete mMemoryDevice;
    1240           0 :         mMemoryDevice = nullptr;
    1241             : 
    1242           0 :         delete mDiskDevice;
    1243           0 :         mDiskDevice = nullptr;
    1244             : 
    1245           0 :         if (mOfflineDevice)
    1246           0 :             mOfflineDevice->Shutdown();
    1247             : 
    1248           0 :         NS_IF_RELEASE(mOfflineDevice);
    1249             : 
    1250           0 :         for (auto iter = mCustomOfflineDevices.Iter();
    1251           0 :              !iter.Done(); iter.Next()) {
    1252           0 :             iter.Data()->Shutdown();
    1253           0 :             iter.Remove();
    1254             :         }
    1255             : 
    1256           0 :         LogCacheStatistics();
    1257             : 
    1258           0 :         mClearingEntries = false;
    1259           0 :         mCacheIOThread.swap(cacheIOThread);
    1260             :     }
    1261             : 
    1262           0 :     if (cacheIOThread)
    1263           0 :         nsShutdownThread::BlockingShutdown(cacheIOThread);
    1264             : 
    1265           0 :     if (shouldSanitize) {
    1266           0 :         nsresult rv = parentDir->AppendNative(NS_LITERAL_CSTRING("Cache"));
    1267           0 :         if (NS_SUCCEEDED(rv)) {
    1268             :             bool exists;
    1269           0 :             if (NS_SUCCEEDED(parentDir->Exists(&exists)) && exists)
    1270           0 :                 nsDeleteDir::DeleteDir(parentDir, false);
    1271             :         }
    1272           0 :         Telemetry::AutoTimer<Telemetry::NETWORK_DISK_CACHE_SHUTDOWN_CLEAR_PRIVATE> timer;
    1273           0 :         nsDeleteDir::Shutdown(shouldSanitize);
    1274             :     } else {
    1275           0 :         Telemetry::AutoTimer<Telemetry::NETWORK_DISK_CACHE_DELETEDIR_SHUTDOWN> timer;
    1276           0 :         nsDeleteDir::Shutdown(shouldSanitize);
    1277             :     }
    1278             : }
    1279             : 
    1280             : 
    1281             : nsresult
    1282           1 : nsCacheService::Create(nsISupports* aOuter, const nsIID& aIID, void* *aResult)
    1283             : {
    1284             :     nsresult  rv;
    1285             : 
    1286           1 :     if (aOuter != nullptr)
    1287           0 :         return NS_ERROR_NO_AGGREGATION;
    1288             : 
    1289           1 :     nsCacheService * cacheService = new nsCacheService();
    1290           1 :     if (cacheService == nullptr)
    1291           0 :         return NS_ERROR_OUT_OF_MEMORY;
    1292             : 
    1293           1 :     NS_ADDREF(cacheService);
    1294           1 :     rv = cacheService->Init();
    1295           1 :     if (NS_SUCCEEDED(rv)) {
    1296           1 :         rv = cacheService->QueryInterface(aIID, aResult);
    1297             :     }
    1298           1 :     NS_RELEASE(cacheService);
    1299           1 :     return rv;
    1300             : }
    1301             : 
    1302             : 
    1303             : NS_IMETHODIMP
    1304           0 : nsCacheService::CreateSession(const char *          clientID,
    1305             :                               nsCacheStoragePolicy  storagePolicy,
    1306             :                               bool                  streamBased,
    1307             :                               nsICacheSession     **result)
    1308             : {
    1309           0 :     *result = nullptr;
    1310             : 
    1311           0 :     if (net::CacheObserver::UseNewCache())
    1312           0 :         return NS_ERROR_NOT_IMPLEMENTED;
    1313             : 
    1314           0 :     return CreateSessionInternal(clientID, storagePolicy, streamBased, result);
    1315             : }
    1316             : 
    1317             : nsresult
    1318           0 : nsCacheService::CreateSessionInternal(const char *          clientID,
    1319             :                                       nsCacheStoragePolicy  storagePolicy,
    1320             :                                       bool                  streamBased,
    1321             :                                       nsICacheSession     **result)
    1322             : {
    1323             :     RefPtr<nsCacheSession> session =
    1324           0 :         new nsCacheSession(clientID, storagePolicy, streamBased);
    1325           0 :     session.forget(result);
    1326             : 
    1327           0 :     return NS_OK;
    1328             : }
    1329             : 
    1330             : 
    1331             : nsresult
    1332           0 : nsCacheService::EvictEntriesForSession(nsCacheSession * session)
    1333             : {
    1334           0 :     NS_ASSERTION(gService, "nsCacheService::gService is null.");
    1335           0 :     return gService->EvictEntriesForClient(session->ClientID()->get(),
    1336           0 :                                  session->StoragePolicy());
    1337             : }
    1338             : 
    1339             : namespace {
    1340             : 
    1341           0 : class EvictionNotifierRunnable : public Runnable
    1342             : {
    1343             : public:
    1344           0 :   explicit EvictionNotifierRunnable(nsISupports* aSubject)
    1345           0 :     : mozilla::Runnable("EvictionNotifierRunnable")
    1346           0 :     , mSubject(aSubject)
    1347             :   {
    1348           0 :   }
    1349             : 
    1350             :   NS_DECL_NSIRUNNABLE
    1351             : 
    1352             : private:
    1353             :     nsCOMPtr<nsISupports> mSubject;
    1354             : };
    1355             : 
    1356             : NS_IMETHODIMP
    1357           0 : EvictionNotifierRunnable::Run()
    1358             : {
    1359             :     nsCOMPtr<nsIObserverService> obsSvc =
    1360           0 :         mozilla::services::GetObserverService();
    1361           0 :     if (obsSvc) {
    1362           0 :         obsSvc->NotifyObservers(mSubject,
    1363             :                                 NS_CACHESERVICE_EMPTYCACHE_TOPIC_ID,
    1364           0 :                                 nullptr);
    1365             :     }
    1366           0 :     return NS_OK;
    1367             : }
    1368             : 
    1369             : } // namespace
    1370             : 
    1371             : nsresult
    1372           0 : nsCacheService::EvictEntriesForClient(const char *          clientID,
    1373             :                                       nsCacheStoragePolicy  storagePolicy)
    1374             : {
    1375             :     RefPtr<EvictionNotifierRunnable> r =
    1376           0 :         new EvictionNotifierRunnable(NS_ISUPPORTS_CAST(nsICacheService*, this));
    1377           0 :     NS_DispatchToMainThread(r);
    1378             : 
    1379           0 :     nsCacheServiceAutoLock lock(LOCK_TELEM(NSCACHESERVICE_EVICTENTRIESFORCLIENT));
    1380           0 :     nsresult res = NS_OK;
    1381             : 
    1382           0 :     if (storagePolicy == nsICache::STORE_ANYWHERE ||
    1383             :         storagePolicy == nsICache::STORE_ON_DISK) {
    1384             : 
    1385           0 :         if (mEnableDiskDevice) {
    1386           0 :             nsresult rv = NS_OK;
    1387           0 :             if (!mDiskDevice)
    1388           0 :                 rv = CreateDiskDevice();
    1389           0 :             if (mDiskDevice)
    1390           0 :                 rv = mDiskDevice->EvictEntries(clientID);
    1391           0 :             if (NS_FAILED(rv))
    1392           0 :                 res = rv;
    1393             :         }
    1394             :     }
    1395             : 
    1396             :     // Only clear the offline cache if it has been specifically asked for.
    1397           0 :     if (storagePolicy == nsICache::STORE_OFFLINE) {
    1398           0 :         if (mEnableOfflineDevice) {
    1399           0 :             nsresult rv = NS_OK;
    1400           0 :             if (!mOfflineDevice)
    1401           0 :                 rv = CreateOfflineDevice();
    1402           0 :             if (mOfflineDevice)
    1403           0 :                 rv = mOfflineDevice->EvictEntries(clientID);
    1404           0 :             if (NS_FAILED(rv))
    1405           0 :                 res = rv;
    1406             :         }
    1407             :     }
    1408             : 
    1409           0 :     if (storagePolicy == nsICache::STORE_ANYWHERE ||
    1410             :         storagePolicy == nsICache::STORE_IN_MEMORY) {
    1411             :         // If there is no memory device, there is no need to evict it...
    1412           0 :         if (mMemoryDevice) {
    1413           0 :             nsresult rv = mMemoryDevice->EvictEntries(clientID);
    1414           0 :             if (NS_FAILED(rv))
    1415           0 :                 res = rv;
    1416             :         }
    1417             :     }
    1418             : 
    1419           0 :     return res;
    1420             : }
    1421             : 
    1422             : 
    1423             : nsresult
    1424           0 : nsCacheService::IsStorageEnabledForPolicy(nsCacheStoragePolicy  storagePolicy,
    1425             :                                           bool *              result)
    1426             : {
    1427           0 :     if (gService == nullptr) return NS_ERROR_NOT_AVAILABLE;
    1428           0 :     nsCacheServiceAutoLock lock(LOCK_TELEM(NSCACHESERVICE_ISSTORAGEENABLEDFORPOLICY));
    1429             : 
    1430           0 :     *result = gService->IsStorageEnabledForPolicy_Locked(storagePolicy);
    1431           0 :     return NS_OK;
    1432             : }
    1433             : 
    1434             : 
    1435             : nsresult
    1436           0 : nsCacheService::DoomEntry(nsCacheSession   *session,
    1437             :                           const nsACString &key,
    1438             :                           nsICacheListener *listener)
    1439             : {
    1440           0 :     CACHE_LOG_DEBUG(("Dooming entry for session %p, key %s\n",
    1441             :                      session, PromiseFlatCString(key).get()));
    1442           0 :     if (!gService || !gService->mInitialized)
    1443           0 :         return NS_ERROR_NOT_INITIALIZED;
    1444             : 
    1445           0 :     return DispatchToCacheIOThread(new nsDoomEvent(session, key, listener));
    1446             : }
    1447             : 
    1448             : 
    1449             : bool
    1450           0 : nsCacheService::IsStorageEnabledForPolicy_Locked(nsCacheStoragePolicy  storagePolicy)
    1451             : {
    1452           0 :     if (gService->mEnableMemoryDevice &&
    1453           0 :         (storagePolicy == nsICache::STORE_ANYWHERE ||
    1454             :          storagePolicy == nsICache::STORE_IN_MEMORY)) {
    1455           0 :         return true;
    1456             :     }
    1457           0 :     if (gService->mEnableDiskDevice &&
    1458           0 :         (storagePolicy == nsICache::STORE_ANYWHERE ||
    1459             :          storagePolicy == nsICache::STORE_ON_DISK)) {
    1460           0 :         return true;
    1461             :     }
    1462           0 :     if (gService->mEnableOfflineDevice &&
    1463             :         storagePolicy == nsICache::STORE_OFFLINE) {
    1464           0 :         return true;
    1465             :     }
    1466             : 
    1467           0 :     return false;
    1468             : }
    1469             : 
    1470           0 : NS_IMETHODIMP nsCacheService::VisitEntries(nsICacheVisitor *visitor)
    1471             : {
    1472           0 :     if (net::CacheObserver::UseNewCache())
    1473           0 :         return NS_ERROR_NOT_IMPLEMENTED;
    1474             : 
    1475           0 :     return VisitEntriesInternal(visitor);
    1476             : }
    1477             : 
    1478           0 : nsresult nsCacheService::VisitEntriesInternal(nsICacheVisitor *visitor)
    1479             : {
    1480           0 :     NS_ENSURE_ARG_POINTER(visitor);
    1481             : 
    1482           0 :     nsCacheServiceAutoLock lock(LOCK_TELEM(NSCACHESERVICE_VISITENTRIES));
    1483             : 
    1484           0 :     if (!(mEnableDiskDevice || mEnableMemoryDevice))
    1485           0 :         return NS_ERROR_NOT_AVAILABLE;
    1486             : 
    1487             :     // XXX record the fact that a visitation is in progress,
    1488             :     // XXX i.e. keep list of visitors in progress.
    1489             : 
    1490           0 :     nsresult rv = NS_OK;
    1491             :     // If there is no memory device, there are then also no entries to visit...
    1492           0 :     if (mMemoryDevice) {
    1493           0 :         rv = mMemoryDevice->Visit(visitor);
    1494           0 :         if (NS_FAILED(rv)) return rv;
    1495             :     }
    1496             : 
    1497           0 :     if (mEnableDiskDevice) {
    1498           0 :         if (!mDiskDevice) {
    1499           0 :             rv = CreateDiskDevice();
    1500           0 :             if (NS_FAILED(rv)) return rv;
    1501             :         }
    1502           0 :         rv = mDiskDevice->Visit(visitor);
    1503           0 :         if (NS_FAILED(rv)) return rv;
    1504             :     }
    1505             : 
    1506           0 :     if (mEnableOfflineDevice) {
    1507           0 :         if (!mOfflineDevice) {
    1508           0 :             rv = CreateOfflineDevice();
    1509           0 :             if (NS_FAILED(rv)) return rv;
    1510             :         }
    1511           0 :         rv = mOfflineDevice->Visit(visitor);
    1512           0 :         if (NS_FAILED(rv)) return rv;
    1513             :     }
    1514             : 
    1515             :     // XXX notify any shutdown process that visitation is complete for THIS visitor.
    1516             :     // XXX keep queue of visitors
    1517             : 
    1518           0 :     return NS_OK;
    1519             : }
    1520             : 
    1521           0 : void nsCacheService::FireClearNetworkCacheStoredAnywhereNotification()
    1522             : {
    1523           0 :     MOZ_ASSERT(NS_IsMainThread());
    1524           0 :     nsCOMPtr<nsIObserverService> obsvc = mozilla::services::GetObserverService();
    1525           0 :     if (obsvc) {
    1526           0 :         obsvc->NotifyObservers(nullptr,
    1527             :                                "network-clear-cache-stored-anywhere",
    1528           0 :                                nullptr);
    1529             :     }
    1530           0 : }
    1531             : 
    1532           0 : NS_IMETHODIMP nsCacheService::EvictEntries(nsCacheStoragePolicy storagePolicy)
    1533             : {
    1534           0 :     if (net::CacheObserver::UseNewCache())
    1535           0 :         return NS_ERROR_NOT_IMPLEMENTED;
    1536             : 
    1537           0 :     return EvictEntriesInternal(storagePolicy);
    1538             : }
    1539             : 
    1540           0 : nsresult nsCacheService::EvictEntriesInternal(nsCacheStoragePolicy storagePolicy)
    1541             : {
    1542           0 :     if (storagePolicy == nsICache::STORE_ANYWHERE) {
    1543             :         // if not called on main thread, dispatch the notification to the main thread to notify observers
    1544           0 :         if (!NS_IsMainThread()) {
    1545           0 :           nsCOMPtr<nsIRunnable> event = NewRunnableMethod(
    1546             :             "nsCacheService::FireClearNetworkCacheStoredAnywhereNotification",
    1547             :             this,
    1548           0 :             &nsCacheService::FireClearNetworkCacheStoredAnywhereNotification);
    1549           0 :           NS_DispatchToMainThread(event);
    1550             :         } else {
    1551             :             // else you're already on main thread - notify observers
    1552           0 :             FireClearNetworkCacheStoredAnywhereNotification();
    1553             :         }
    1554             :     }
    1555           0 :     return EvictEntriesForClient(nullptr, storagePolicy);
    1556             : }
    1557             : 
    1558           1 : NS_IMETHODIMP nsCacheService::GetCacheIOTarget(nsIEventTarget * *aCacheIOTarget)
    1559             : {
    1560           1 :     NS_ENSURE_ARG_POINTER(aCacheIOTarget);
    1561             : 
    1562             :     // Because mCacheIOThread can only be changed on the main thread, it can be
    1563             :     // read from the main thread without the lock. This is useful to prevent
    1564             :     // blocking the main thread on other cache operations.
    1565           1 :     if (!NS_IsMainThread()) {
    1566           0 :         Lock(LOCK_TELEM(NSCACHESERVICE_GETCACHEIOTARGET));
    1567             :     }
    1568             : 
    1569             :     nsresult rv;
    1570           1 :     if (mCacheIOThread) {
    1571           1 :         NS_ADDREF(*aCacheIOTarget = mCacheIOThread);
    1572           1 :         rv = NS_OK;
    1573             :     } else {
    1574           0 :         *aCacheIOTarget = nullptr;
    1575           0 :         rv = NS_ERROR_NOT_AVAILABLE;
    1576             :     }
    1577             : 
    1578           1 :     if (!NS_IsMainThread()) {
    1579           0 :         Unlock();
    1580             :     }
    1581             : 
    1582           1 :     return rv;
    1583             : }
    1584             : 
    1585           0 : NS_IMETHODIMP nsCacheService::GetLockHeldTime(double *aLockHeldTime)
    1586             : {
    1587           0 :     MutexAutoLock lock(mTimeStampLock);
    1588             : 
    1589           0 :     if (mLockAcquiredTimeStamp.IsNull()) {
    1590           0 :         *aLockHeldTime = 0.0;
    1591             :     }
    1592             :     else {
    1593           0 :         *aLockHeldTime =
    1594           0 :             (TimeStamp::Now() - mLockAcquiredTimeStamp).ToMilliseconds();
    1595             :     }
    1596             : 
    1597           0 :     return NS_OK;
    1598             : }
    1599             : 
    1600             : /**
    1601             :  * Internal Methods
    1602             :  */
    1603             : nsresult
    1604           0 : nsCacheService::CreateDiskDevice()
    1605             : {
    1606           0 :     if (!mInitialized)      return NS_ERROR_NOT_AVAILABLE;
    1607           0 :     if (!mEnableDiskDevice) return NS_ERROR_NOT_AVAILABLE;
    1608           0 :     if (mDiskDevice)        return NS_OK;
    1609             : 
    1610           0 :     mDiskDevice = new nsDiskCacheDevice;
    1611           0 :     if (!mDiskDevice)       return NS_ERROR_OUT_OF_MEMORY;
    1612             : 
    1613             :     // set the preferences
    1614           0 :     mDiskDevice->SetCacheParentDirectory(mObserver->DiskCacheParentDirectory());
    1615           0 :     mDiskDevice->SetCapacity(mObserver->DiskCacheCapacity());
    1616           0 :     mDiskDevice->SetMaxEntrySize(mObserver->DiskCacheMaxEntrySize());
    1617             : 
    1618           0 :     nsresult rv = mDiskDevice->Init();
    1619           0 :     if (NS_FAILED(rv)) {
    1620             : #if DEBUG
    1621           0 :         printf("###\n");
    1622           0 :         printf("### mDiskDevice->Init() failed (0x%.8x)\n",
    1623           0 :                static_cast<uint32_t>(rv));
    1624           0 :         printf("###    - disabling disk cache for this session.\n");
    1625           0 :         printf("###\n");
    1626             : #endif
    1627           0 :         mEnableDiskDevice = false;
    1628           0 :         delete mDiskDevice;
    1629           0 :         mDiskDevice = nullptr;
    1630           0 :         return rv;
    1631             :     }
    1632             : 
    1633           0 :     NS_ASSERTION(!mSmartSizeTimer, "Smartsize timer was already fired!");
    1634             : 
    1635             :     // Disk device is usually created during the startup. Delay smart size
    1636             :     // calculation to avoid possible massive IO caused by eviction of entries
    1637             :     // in case the new smart size is smaller than current cache usage.
    1638           0 :     mSmartSizeTimer = do_CreateInstance("@mozilla.org/timer;1", &rv);
    1639           0 :     if (NS_SUCCEEDED(rv)) {
    1640           0 :         rv = mSmartSizeTimer->InitWithCallback(new nsSetDiskSmartSizeCallback(),
    1641             :                                                1000*60*3,
    1642           0 :                                                nsITimer::TYPE_ONE_SHOT);
    1643           0 :         if (NS_FAILED(rv)) {
    1644           0 :             NS_WARNING("Failed to post smart size timer");
    1645           0 :             mSmartSizeTimer = nullptr;
    1646             :         }
    1647             :     } else {
    1648           0 :         NS_WARNING("Can't create smart size timer");
    1649             :     }
    1650             :     // Ignore state of the timer and return success since the purpose of the
    1651             :     // method (create the disk-device) has been fulfilled
    1652             : 
    1653           0 :     return NS_OK;
    1654             : }
    1655             : 
    1656             : // Runnable sent from cache thread to main thread
    1657           0 : class nsDisableOldMaxSmartSizePrefEvent: public Runnable
    1658             : {
    1659             : public:
    1660           0 :   nsDisableOldMaxSmartSizePrefEvent()
    1661           0 :     : mozilla::Runnable("nsDisableOldMaxSmartSizePrefEvent")
    1662             :   {
    1663           0 :   }
    1664             : 
    1665           0 :   NS_IMETHOD Run() override
    1666             :   {
    1667             :     // Main thread may have already called nsCacheService::Shutdown
    1668           0 :     if (!nsCacheService::IsInitialized())
    1669           0 :       return NS_ERROR_NOT_AVAILABLE;
    1670             : 
    1671           0 :     nsCOMPtr<nsIPrefBranch> branch = do_GetService(NS_PREFSERVICE_CONTRACTID);
    1672           0 :     if (!branch) {
    1673           0 :       return NS_ERROR_NOT_AVAILABLE;
    1674             :     }
    1675             : 
    1676             :     nsresult rv =
    1677           0 :       branch->SetBoolPref(DISK_CACHE_USE_OLD_MAX_SMART_SIZE_PREF, false);
    1678           0 :     if (NS_FAILED(rv)) {
    1679           0 :       NS_WARNING("Failed to disable old max smart size");
    1680           0 :       return rv;
    1681             :     }
    1682             : 
    1683             :     // It is safe to call SetDiskSmartSize_Locked() without holding the lock
    1684             :     // when we are on main thread and nsCacheService is initialized.
    1685           0 :     nsCacheService::gService->SetDiskSmartSize_Locked();
    1686             : 
    1687           0 :     if (nsCacheService::gService->mObserver->PermittedToSmartSize(branch,
    1688             :                                                                   false)) {
    1689           0 :       rv = branch->SetIntPref(DISK_CACHE_CAPACITY_PREF, MAX_CACHE_SIZE);
    1690           0 :       if (NS_FAILED(rv)) {
    1691           0 :         NS_WARNING("Failed to set cache capacity pref");
    1692             :       }
    1693             :     }
    1694             : 
    1695           0 :     return NS_OK;
    1696             :     }
    1697             : };
    1698             : 
    1699             : void
    1700           0 : nsCacheService::MarkStartingFresh()
    1701             : {
    1702           0 :     if (!gService || !gService->mObserver->ShouldUseOldMaxSmartSize()) {
    1703             :         // Already using new max, nothing to do here
    1704           0 :         return;
    1705             :     }
    1706             : 
    1707           0 :     gService->mObserver->SetUseNewMaxSmartSize(true);
    1708             : 
    1709             :     // We always dispatch an event here because we don't want to deal with lock
    1710             :     // reentrance issues.
    1711           0 :     NS_DispatchToMainThread(new nsDisableOldMaxSmartSizePrefEvent());
    1712             : }
    1713             : 
    1714             : nsresult
    1715           0 : nsCacheService::GetOfflineDevice(nsOfflineCacheDevice **aDevice)
    1716             : {
    1717           0 :     if (!mOfflineDevice) {
    1718           0 :         nsresult rv = CreateOfflineDevice();
    1719           0 :         NS_ENSURE_SUCCESS(rv, rv);
    1720             :     }
    1721             : 
    1722           0 :     NS_ADDREF(*aDevice = mOfflineDevice);
    1723           0 :     return NS_OK;
    1724             : }
    1725             : 
    1726             : nsresult
    1727           0 : nsCacheService::GetCustomOfflineDevice(nsIFile *aProfileDir,
    1728             :                                        int32_t aQuota,
    1729             :                                        nsOfflineCacheDevice **aDevice)
    1730             : {
    1731             :     nsresult rv;
    1732             : 
    1733           0 :     nsAutoString profilePath;
    1734           0 :     rv = aProfileDir->GetPath(profilePath);
    1735           0 :     NS_ENSURE_SUCCESS(rv, rv);
    1736             : 
    1737           0 :     if (!mCustomOfflineDevices.Get(profilePath, aDevice)) {
    1738           0 :         rv = CreateCustomOfflineDevice(aProfileDir, aQuota, aDevice);
    1739           0 :         NS_ENSURE_SUCCESS(rv, rv);
    1740             : 
    1741           0 :         (*aDevice)->SetAutoShutdown();
    1742           0 :         mCustomOfflineDevices.Put(profilePath, *aDevice);
    1743             :     }
    1744             : 
    1745           0 :     return NS_OK;
    1746             : }
    1747             : 
    1748             : nsresult
    1749           0 : nsCacheService::CreateOfflineDevice()
    1750             : {
    1751           0 :     CACHE_LOG_INFO(("Creating default offline device"));
    1752             : 
    1753           0 :     if (mOfflineDevice)        return NS_OK;
    1754           0 :     if (!nsCacheService::IsInitialized()) {
    1755           0 :         return NS_ERROR_NOT_AVAILABLE;
    1756             :     }
    1757             : 
    1758           0 :     nsresult rv = CreateCustomOfflineDevice(
    1759           0 :         mObserver->OfflineCacheParentDirectory(),
    1760           0 :         mObserver->OfflineCacheCapacity(),
    1761           0 :         &mOfflineDevice);
    1762           0 :     NS_ENSURE_SUCCESS(rv, rv);
    1763             : 
    1764           0 :     return NS_OK;
    1765             : }
    1766             : 
    1767             : nsresult
    1768           0 : nsCacheService::CreateCustomOfflineDevice(nsIFile *aProfileDir,
    1769             :                                           int32_t aQuota,
    1770             :                                           nsOfflineCacheDevice **aDevice)
    1771             : {
    1772           0 :     NS_ENSURE_ARG(aProfileDir);
    1773             : 
    1774           0 :     if (MOZ_LOG_TEST(gCacheLog, LogLevel::Info)) {
    1775           0 :       nsAutoCString profilePath;
    1776           0 :       aProfileDir->GetNativePath(profilePath);
    1777           0 :       CACHE_LOG_INFO(("Creating custom offline device, %s, %d",
    1778             :                         profilePath.BeginReading(), aQuota));
    1779             :     }
    1780             : 
    1781           0 :     if (!mInitialized)         return NS_ERROR_NOT_AVAILABLE;
    1782           0 :     if (!mEnableOfflineDevice) return NS_ERROR_NOT_AVAILABLE;
    1783             : 
    1784           0 :     *aDevice = new nsOfflineCacheDevice;
    1785             : 
    1786           0 :     NS_ADDREF(*aDevice);
    1787             : 
    1788             :     // set the preferences
    1789           0 :     (*aDevice)->SetCacheParentDirectory(aProfileDir);
    1790           0 :     (*aDevice)->SetCapacity(aQuota);
    1791             : 
    1792           0 :     nsresult rv = (*aDevice)->InitWithSqlite(mStorageService);
    1793           0 :     if (NS_FAILED(rv)) {
    1794           0 :         CACHE_LOG_DEBUG(("OfflineDevice->InitWithSqlite() failed (0x%.8" PRIx32 ")\n",
    1795             :                          static_cast<uint32_t>(rv)));
    1796           0 :         CACHE_LOG_DEBUG(("    - disabling offline cache for this session.\n"));
    1797             : 
    1798           0 :         NS_RELEASE(*aDevice);
    1799             :     }
    1800           0 :     return rv;
    1801             : }
    1802             : 
    1803             : nsresult
    1804           0 : nsCacheService::CreateMemoryDevice()
    1805             : {
    1806           0 :     if (!mInitialized)        return NS_ERROR_NOT_AVAILABLE;
    1807           0 :     if (!mEnableMemoryDevice) return NS_ERROR_NOT_AVAILABLE;
    1808           0 :     if (mMemoryDevice)        return NS_OK;
    1809             : 
    1810           0 :     mMemoryDevice = new nsMemoryCacheDevice;
    1811           0 :     if (!mMemoryDevice)       return NS_ERROR_OUT_OF_MEMORY;
    1812             : 
    1813             :     // set preference
    1814           0 :     int32_t capacity = mObserver->MemoryCacheCapacity();
    1815           0 :     CACHE_LOG_DEBUG(("Creating memory device with capacity %d\n", capacity));
    1816           0 :     mMemoryDevice->SetCapacity(capacity);
    1817           0 :     mMemoryDevice->SetMaxEntrySize(mObserver->MemoryCacheMaxEntrySize());
    1818             : 
    1819           0 :     nsresult rv = mMemoryDevice->Init();
    1820           0 :     if (NS_FAILED(rv)) {
    1821           0 :         NS_WARNING("Initialization of Memory Cache failed.");
    1822           0 :         delete mMemoryDevice;
    1823           0 :         mMemoryDevice = nullptr;
    1824             :     }
    1825             : 
    1826           0 :     return rv;
    1827             : }
    1828             : 
    1829             : nsresult
    1830           0 : nsCacheService::RemoveCustomOfflineDevice(nsOfflineCacheDevice *aDevice)
    1831             : {
    1832           0 :     nsCOMPtr<nsIFile> profileDir = aDevice->BaseDirectory();
    1833           0 :     if (!profileDir)
    1834           0 :         return NS_ERROR_UNEXPECTED;
    1835             : 
    1836           0 :     nsAutoString profilePath;
    1837           0 :     nsresult rv = profileDir->GetPath(profilePath);
    1838           0 :     NS_ENSURE_SUCCESS(rv, rv);
    1839             : 
    1840           0 :     mCustomOfflineDevices.Remove(profilePath);
    1841           0 :     return NS_OK;
    1842             : }
    1843             : 
    1844             : nsresult
    1845           0 : nsCacheService::CreateRequest(nsCacheSession *   session,
    1846             :                               const nsACString & clientKey,
    1847             :                               nsCacheAccessMode  accessRequested,
    1848             :                               bool               blockingMode,
    1849             :                               nsICacheListener * listener,
    1850             :                               nsCacheRequest **  request)
    1851             : {
    1852           0 :     NS_ASSERTION(request, "CreateRequest: request is null");
    1853             : 
    1854           0 :     nsAutoCString key(*session->ClientID());
    1855           0 :     key.Append(':');
    1856           0 :     key.Append(clientKey);
    1857             : 
    1858           0 :     if (mMaxKeyLength < key.Length()) mMaxKeyLength = key.Length();
    1859             : 
    1860             :     // create request
    1861           0 :     *request = new nsCacheRequest(key, listener, accessRequested,
    1862           0 :                                   blockingMode, session);
    1863             : 
    1864           0 :     if (!listener)  return NS_OK;  // we're sync, we're done.
    1865             : 
    1866             :     // get the request's thread
    1867           0 :     (*request)->mEventTarget = GetCurrentThreadEventTarget();
    1868             : 
    1869           0 :     return NS_OK;
    1870             : }
    1871             : 
    1872             : 
    1873           0 : class nsCacheListenerEvent : public Runnable
    1874             : {
    1875             : public:
    1876           0 :   nsCacheListenerEvent(nsICacheListener* listener,
    1877             :                        nsICacheEntryDescriptor* descriptor,
    1878             :                        nsCacheAccessMode accessGranted,
    1879             :                        nsresult status)
    1880           0 :     : mozilla::Runnable("nsCacheListenerEvent")
    1881             :     , mListener(listener)     // transfers reference
    1882             :     , mDescriptor(descriptor) // transfers reference (may be null)
    1883             :     , mAccessGranted(accessGranted)
    1884           0 :     , mStatus(status)
    1885             :   {
    1886           0 :   }
    1887             : 
    1888           0 :   NS_IMETHOD Run() override
    1889             :   {
    1890           0 :     mListener->OnCacheEntryAvailable(mDescriptor, mAccessGranted, mStatus);
    1891             : 
    1892           0 :     NS_RELEASE(mListener);
    1893           0 :     NS_IF_RELEASE(mDescriptor);
    1894           0 :     return NS_OK;
    1895             :     }
    1896             : 
    1897             : private:
    1898             :     // We explicitly leak mListener or mDescriptor if Run is not called
    1899             :     // because otherwise we cannot guarantee that they are destroyed on
    1900             :     // the right thread.
    1901             : 
    1902             :     nsICacheListener        *mListener;
    1903             :     nsICacheEntryDescriptor *mDescriptor;
    1904             :     nsCacheAccessMode        mAccessGranted;
    1905             :     nsresult                 mStatus;
    1906             : };
    1907             : 
    1908             : 
    1909             : nsresult
    1910           0 : nsCacheService::NotifyListener(nsCacheRequest *          request,
    1911             :                                nsICacheEntryDescriptor * descriptor,
    1912             :                                nsCacheAccessMode         accessGranted,
    1913             :                                nsresult                  status)
    1914             : {
    1915           0 :     NS_ASSERTION(request->mEventTarget, "no thread set in async request!");
    1916             : 
    1917             :     // Swap ownership, and release listener on target thread...
    1918           0 :     nsICacheListener *listener = request->mListener;
    1919           0 :     request->mListener = nullptr;
    1920             : 
    1921             :     nsCOMPtr<nsIRunnable> ev =
    1922             :             new nsCacheListenerEvent(listener, descriptor,
    1923           0 :                                      accessGranted, status);
    1924           0 :     if (!ev) {
    1925             :         // Better to leak listener and descriptor if we fail because we don't
    1926             :         // want to destroy them inside the cache service lock or on potentially
    1927             :         // the wrong thread.
    1928           0 :         return NS_ERROR_OUT_OF_MEMORY;
    1929             :     }
    1930             : 
    1931           0 :     return request->mEventTarget->Dispatch(ev, NS_DISPATCH_NORMAL);
    1932             : }
    1933             : 
    1934             : 
    1935             : nsresult
    1936           0 : nsCacheService::ProcessRequest(nsCacheRequest *           request,
    1937             :                                bool                       calledFromOpenCacheEntry,
    1938             :                                nsICacheEntryDescriptor ** result)
    1939             : {
    1940             :     // !!! must be called with mLock held !!!
    1941             :     nsresult           rv;
    1942           0 :     nsCacheEntry *     entry = nullptr;
    1943           0 :     nsCacheEntry *     doomedEntry = nullptr;
    1944           0 :     nsCacheAccessMode  accessGranted = nsICache::ACCESS_NONE;
    1945           0 :     if (result) *result = nullptr;
    1946             : 
    1947             :     while(1) {  // Activate entry loop
    1948           0 :         rv = ActivateEntry(request, &entry, &doomedEntry);  // get the entry for this request
    1949           0 :         if (NS_FAILED(rv))  break;
    1950             : 
    1951           0 :         while(1) { // Request Access loop
    1952           0 :             NS_ASSERTION(entry, "no entry in Request Access loop!");
    1953             :             // entry->RequestAccess queues request on entry
    1954           0 :             rv = entry->RequestAccess(request, &accessGranted);
    1955           0 :             if (rv != NS_ERROR_CACHE_WAIT_FOR_VALIDATION) break;
    1956             : 
    1957           0 :             if (request->IsBlocking()) {
    1958           0 :                 if (request->mListener) {
    1959             :                     // async exits - validate, doom, or close will resume
    1960           0 :                     return rv;
    1961             :                 }
    1962             : 
    1963             :                 // XXX this is probably wrong...
    1964           0 :                 Unlock();
    1965           0 :                 rv = request->WaitForValidation();
    1966           0 :                 Lock(LOCK_TELEM(NSCACHESERVICE_PROCESSREQUEST));
    1967             :             }
    1968             : 
    1969           0 :             PR_REMOVE_AND_INIT_LINK(request);
    1970           0 :             if (NS_FAILED(rv)) break;   // non-blocking mode returns WAIT_FOR_VALIDATION error
    1971             :             // okay, we're ready to process this request, request access again
    1972             :         }
    1973           0 :         if (rv != NS_ERROR_CACHE_ENTRY_DOOMED)  break;
    1974             : 
    1975           0 :         if (entry->IsNotInUse()) {
    1976             :             // this request was the last one keeping it around, so get rid of it
    1977           0 :             DeactivateEntry(entry);
    1978             :         }
    1979             :         // loop back around to look for another entry
    1980             :     }
    1981             : 
    1982           0 :     if (NS_SUCCEEDED(rv) && request->mProfileDir) {
    1983             :         // Custom cache directory has been demanded.  Preset the cache device.
    1984           0 :         if (entry->StoragePolicy() != nsICache::STORE_OFFLINE) {
    1985             :             // Failsafe check: this is implemented only for offline cache atm.
    1986           0 :             rv = NS_ERROR_FAILURE;
    1987             :         } else {
    1988           0 :             RefPtr<nsOfflineCacheDevice> customCacheDevice;
    1989           0 :             rv = GetCustomOfflineDevice(request->mProfileDir, -1,
    1990           0 :                                         getter_AddRefs(customCacheDevice));
    1991           0 :             if (NS_SUCCEEDED(rv))
    1992           0 :                 entry->SetCustomCacheDevice(customCacheDevice);
    1993             :         }
    1994             :     }
    1995             : 
    1996           0 :     nsICacheEntryDescriptor *descriptor = nullptr;
    1997             : 
    1998           0 :     if (NS_SUCCEEDED(rv))
    1999           0 :         rv = entry->CreateDescriptor(request, accessGranted, &descriptor);
    2000             : 
    2001             :     // If doomedEntry is set, ActivatEntry() doomed an existing entry and
    2002             :     // created a new one for that cache-key. However, any pending requests
    2003             :     // on the doomed entry were not processed and we need to do that here.
    2004             :     // This must be done after adding the created entry to list of active
    2005             :     // entries (which is done in ActivateEntry()) otherwise the hashkeys crash
    2006             :     // (see bug ##561313). It is also important to do this after creating a
    2007             :     // descriptor for this request, or some other request may end up being
    2008             :     // executed first for the newly created entry.
    2009             :     // Finally, it is worth to emphasize that if doomedEntry is set,
    2010             :     // ActivateEntry() created a new entry for the request, which will be
    2011             :     // initialized by RequestAccess() and they both should have returned NS_OK.
    2012           0 :     if (doomedEntry) {
    2013           0 :         (void) ProcessPendingRequests(doomedEntry);
    2014           0 :         if (doomedEntry->IsNotInUse())
    2015           0 :             DeactivateEntry(doomedEntry);
    2016           0 :         doomedEntry = nullptr;
    2017             :     }
    2018             : 
    2019           0 :     if (request->mListener) {  // Asynchronous
    2020             : 
    2021           0 :         if (NS_FAILED(rv) && calledFromOpenCacheEntry && request->IsBlocking())
    2022           0 :             return rv;  // skip notifying listener, just return rv to caller
    2023             : 
    2024             :         // call listener to report error or descriptor
    2025           0 :         nsresult rv2 = NotifyListener(request, descriptor, accessGranted, rv);
    2026           0 :         if (NS_FAILED(rv2) && NS_SUCCEEDED(rv)) {
    2027           0 :             rv = rv2;  // trigger delete request
    2028             :         }
    2029             :     } else {        // Synchronous
    2030           0 :         *result = descriptor;
    2031             :     }
    2032           0 :     return rv;
    2033             : }
    2034             : 
    2035             : 
    2036             : nsresult
    2037           0 : nsCacheService::OpenCacheEntry(nsCacheSession *           session,
    2038             :                                const nsACString &         key,
    2039             :                                nsCacheAccessMode          accessRequested,
    2040             :                                bool                       blockingMode,
    2041             :                                nsICacheListener *         listener,
    2042             :                                nsICacheEntryDescriptor ** result)
    2043             : {
    2044           0 :     CACHE_LOG_DEBUG(("Opening entry for session %p, key %s, mode %d, blocking %d\n",
    2045             :                      session, PromiseFlatCString(key).get(), accessRequested,
    2046             :                      blockingMode));
    2047           0 :     if (result)
    2048           0 :         *result = nullptr;
    2049             : 
    2050           0 :     if (!gService || !gService->mInitialized)
    2051           0 :         return NS_ERROR_NOT_INITIALIZED;
    2052             : 
    2053           0 :     nsCacheRequest * request = nullptr;
    2054             : 
    2055           0 :     nsresult rv = gService->CreateRequest(session,
    2056             :                                           key,
    2057             :                                           accessRequested,
    2058             :                                           blockingMode,
    2059             :                                           listener,
    2060           0 :                                           &request);
    2061           0 :     if (NS_FAILED(rv))  return rv;
    2062             : 
    2063           0 :     CACHE_LOG_DEBUG(("Created request %p\n", request));
    2064             : 
    2065             :     // Process the request on the background thread if we are on the main thread
    2066             :     // and the the request is asynchronous
    2067           0 :     if (NS_IsMainThread() && listener && gService->mCacheIOThread) {
    2068             :         nsCOMPtr<nsIRunnable> ev =
    2069           0 :             new nsProcessRequestEvent(request);
    2070           0 :         rv = DispatchToCacheIOThread(ev);
    2071             : 
    2072             :         // delete request if we didn't post the event
    2073           0 :         if (NS_FAILED(rv))
    2074           0 :             delete request;
    2075             :     }
    2076             :     else {
    2077             : 
    2078           0 :         nsCacheServiceAutoLock lock(LOCK_TELEM(NSCACHESERVICE_OPENCACHEENTRY));
    2079           0 :         rv = gService->ProcessRequest(request, true, result);
    2080             : 
    2081             :         // delete requests that have completed
    2082           0 :         if (!(listener && blockingMode &&
    2083             :             (rv == NS_ERROR_CACHE_WAIT_FOR_VALIDATION)))
    2084           0 :             delete request;
    2085             :     }
    2086             : 
    2087           0 :     return rv;
    2088             : }
    2089             : 
    2090             : 
    2091             : nsresult
    2092           0 : nsCacheService::ActivateEntry(nsCacheRequest * request,
    2093             :                               nsCacheEntry ** result,
    2094             :                               nsCacheEntry ** doomedEntry)
    2095             : {
    2096           0 :     CACHE_LOG_DEBUG(("Activate entry for request %p\n", request));
    2097           0 :     if (!mInitialized || mClearingEntries)
    2098           0 :         return NS_ERROR_NOT_AVAILABLE;
    2099             : 
    2100           0 :     nsresult        rv = NS_OK;
    2101             : 
    2102           0 :     NS_ASSERTION(request != nullptr, "ActivateEntry called with no request");
    2103           0 :     if (result) *result = nullptr;
    2104           0 :     if (doomedEntry) *doomedEntry = nullptr;
    2105           0 :     if ((!request) || (!result) || (!doomedEntry))
    2106           0 :         return NS_ERROR_NULL_POINTER;
    2107             : 
    2108             :     // check if the request can be satisfied
    2109           0 :     if (!mEnableMemoryDevice && !request->IsStreamBased())
    2110           0 :         return NS_ERROR_FAILURE;
    2111           0 :     if (!IsStorageEnabledForPolicy_Locked(request->StoragePolicy()))
    2112           0 :         return NS_ERROR_FAILURE;
    2113             : 
    2114             :     // search active entries (including those not bound to device)
    2115           0 :     nsCacheEntry *entry = mActiveEntries.GetEntry(&(request->mKey));
    2116           0 :     CACHE_LOG_DEBUG(("Active entry for request %p is %p\n", request, entry));
    2117             : 
    2118           0 :     if (!entry) {
    2119             :         // search cache devices for entry
    2120           0 :         bool collision = false;
    2121           0 :         entry = SearchCacheDevices(&(request->mKey), request->StoragePolicy(), &collision);
    2122           0 :         CACHE_LOG_DEBUG(("Device search for request %p returned %p\n",
    2123             :                          request, entry));
    2124             :         // When there is a hashkey collision just refuse to cache it...
    2125           0 :         if (collision) return NS_ERROR_CACHE_IN_USE;
    2126             : 
    2127           0 :         if (entry)  entry->MarkInitialized();
    2128             :     } else {
    2129           0 :         NS_ASSERTION(entry->IsActive(), "Inactive entry found in mActiveEntries!");
    2130             :     }
    2131             : 
    2132           0 :     if (entry) {
    2133           0 :         ++mCacheHits;
    2134           0 :         entry->Fetched();
    2135             :     } else {
    2136           0 :         ++mCacheMisses;
    2137             :     }
    2138             : 
    2139           0 :     if (entry &&
    2140           0 :         ((request->AccessRequested() == nsICache::ACCESS_WRITE) ||
    2141           0 :          ((request->StoragePolicy() != nsICache::STORE_OFFLINE) &&
    2142           0 :           (entry->mExpirationTime <= SecondsFromPRTime(PR_Now()) &&
    2143           0 :            request->WillDoomEntriesIfExpired()))))
    2144             : 
    2145             :     {
    2146             :         // this is FORCE-WRITE request or the entry has expired
    2147             :         // we doom entry without processing pending requests, but store it in
    2148             :         // doomedEntry which causes pending requests to be processed below
    2149           0 :         rv = DoomEntry_Internal(entry, false);
    2150           0 :         *doomedEntry = entry;
    2151           0 :         if (NS_FAILED(rv)) {
    2152             :             // XXX what to do?  Increment FailedDooms counter?
    2153             :         }
    2154           0 :         entry = nullptr;
    2155             :     }
    2156             : 
    2157           0 :     if (!entry) {
    2158           0 :         if (! (request->AccessRequested() & nsICache::ACCESS_WRITE)) {
    2159             :             // this is a READ-ONLY request
    2160           0 :             rv = NS_ERROR_CACHE_KEY_NOT_FOUND;
    2161           0 :             goto error;
    2162             :         }
    2163             : 
    2164           0 :         entry = new nsCacheEntry(request->mKey,
    2165           0 :                                  request->IsStreamBased(),
    2166           0 :                                  request->StoragePolicy());
    2167           0 :         if (!entry)
    2168           0 :             return NS_ERROR_OUT_OF_MEMORY;
    2169             : 
    2170           0 :         if (request->IsPrivate())
    2171           0 :             entry->MarkPrivate();
    2172             : 
    2173           0 :         entry->Fetched();
    2174           0 :         ++mTotalEntries;
    2175             : 
    2176             :         // XXX  we could perform an early bind in some cases based on storage policy
    2177             :     }
    2178             : 
    2179           0 :     if (!entry->IsActive()) {
    2180           0 :         rv = mActiveEntries.AddEntry(entry);
    2181           0 :         if (NS_FAILED(rv)) goto error;
    2182           0 :         CACHE_LOG_DEBUG(("Added entry %p to mActiveEntries\n", entry));
    2183           0 :         entry->MarkActive();  // mark entry active, because it's now in mActiveEntries
    2184             :     }
    2185           0 :     *result = entry;
    2186           0 :     return NS_OK;
    2187             : 
    2188             :  error:
    2189           0 :     *result = nullptr;
    2190           0 :     delete entry;
    2191           0 :     return rv;
    2192             : }
    2193             : 
    2194             : 
    2195             : nsCacheEntry *
    2196           0 : nsCacheService::SearchCacheDevices(nsCString * key, nsCacheStoragePolicy policy, bool *collision)
    2197             : {
    2198           0 :     Telemetry::AutoTimer<Telemetry::CACHE_DEVICE_SEARCH_2> timer;
    2199           0 :     nsCacheEntry * entry = nullptr;
    2200             : 
    2201           0 :     CACHE_LOG_DEBUG(("mMemoryDevice: 0x%p\n", mMemoryDevice));
    2202             : 
    2203           0 :     *collision = false;
    2204           0 :     if ((policy == nsICache::STORE_ANYWHERE) || (policy == nsICache::STORE_IN_MEMORY)) {
    2205             :         // If there is no memory device, then there is nothing to search...
    2206           0 :         if (mMemoryDevice) {
    2207           0 :             entry = mMemoryDevice->FindEntry(key, collision);
    2208           0 :             CACHE_LOG_DEBUG(("Searching mMemoryDevice for key %s found: 0x%p, "
    2209             :                              "collision: %d\n", key->get(), entry, *collision));
    2210             :         }
    2211             :     }
    2212             : 
    2213           0 :     if (!entry &&
    2214           0 :         ((policy == nsICache::STORE_ANYWHERE) || (policy == nsICache::STORE_ON_DISK))) {
    2215             : 
    2216           0 :         if (mEnableDiskDevice) {
    2217           0 :             if (!mDiskDevice) {
    2218           0 :                 nsresult rv = CreateDiskDevice();
    2219           0 :                 if (NS_FAILED(rv))
    2220           0 :                     return nullptr;
    2221             :             }
    2222             : 
    2223           0 :             entry = mDiskDevice->FindEntry(key, collision);
    2224             :         }
    2225             :     }
    2226             : 
    2227           0 :     if (!entry && (policy == nsICache::STORE_OFFLINE ||
    2228           0 :                    (policy == nsICache::STORE_ANYWHERE &&
    2229           0 :                     gIOService->IsOffline()))) {
    2230             : 
    2231           0 :         if (mEnableOfflineDevice) {
    2232           0 :             if (!mOfflineDevice) {
    2233           0 :                 nsresult rv = CreateOfflineDevice();
    2234           0 :                 if (NS_FAILED(rv))
    2235           0 :                     return nullptr;
    2236             :             }
    2237             : 
    2238           0 :             entry = mOfflineDevice->FindEntry(key, collision);
    2239             :         }
    2240             :     }
    2241             : 
    2242           0 :     return entry;
    2243             : }
    2244             : 
    2245             : 
    2246             : nsCacheDevice *
    2247           0 : nsCacheService::EnsureEntryHasDevice(nsCacheEntry * entry)
    2248             : {
    2249           0 :     nsCacheDevice * device = entry->CacheDevice();
    2250             :     // return device if found, possibly null if the entry is doomed i.e prevent
    2251             :     // doomed entries to bind to a device (see e.g. bugs #548406 and #596443)
    2252           0 :     if (device || entry->IsDoomed())  return device;
    2253             : 
    2254           0 :     int64_t predictedDataSize = entry->PredictedDataSize();
    2255           0 :     if (entry->IsStreamData() && entry->IsAllowedOnDisk() && mEnableDiskDevice) {
    2256             :         // this is the default
    2257           0 :         if (!mDiskDevice) {
    2258           0 :             (void)CreateDiskDevice();  // ignore the error (check for mDiskDevice instead)
    2259             :         }
    2260             : 
    2261           0 :         if (mDiskDevice) {
    2262             :             // Bypass the cache if Content-Length says the entry will be too big
    2263           0 :             if (predictedDataSize != -1 &&
    2264           0 :                 mDiskDevice->EntryIsTooBig(predictedDataSize)) {
    2265           0 :                 DebugOnly<nsresult> rv = nsCacheService::DoomEntry(entry);
    2266           0 :                 NS_ASSERTION(NS_SUCCEEDED(rv),"DoomEntry() failed.");
    2267           0 :                 return nullptr;
    2268             :             }
    2269             : 
    2270           0 :             entry->MarkBinding();  // enter state of binding
    2271           0 :             nsresult rv = mDiskDevice->BindEntry(entry);
    2272           0 :             entry->ClearBinding(); // exit state of binding
    2273           0 :             if (NS_SUCCEEDED(rv))
    2274           0 :                 device = mDiskDevice;
    2275             :         }
    2276             :     }
    2277             : 
    2278             :     // if we can't use mDiskDevice, try mMemoryDevice
    2279           0 :     if (!device && mEnableMemoryDevice && entry->IsAllowedInMemory()) {
    2280           0 :         if (!mMemoryDevice) {
    2281           0 :             (void)CreateMemoryDevice();  // ignore the error (check for mMemoryDevice instead)
    2282             :         }
    2283           0 :         if (mMemoryDevice) {
    2284             :             // Bypass the cache if Content-Length says entry will be too big
    2285           0 :             if (predictedDataSize != -1 &&
    2286           0 :                 mMemoryDevice->EntryIsTooBig(predictedDataSize)) {
    2287           0 :                 DebugOnly<nsresult> rv = nsCacheService::DoomEntry(entry);
    2288           0 :                 NS_ASSERTION(NS_SUCCEEDED(rv),"DoomEntry() failed.");
    2289           0 :                 return nullptr;
    2290             :             }
    2291             : 
    2292           0 :             entry->MarkBinding();  // enter state of binding
    2293           0 :             nsresult rv = mMemoryDevice->BindEntry(entry);
    2294           0 :             entry->ClearBinding(); // exit state of binding
    2295           0 :             if (NS_SUCCEEDED(rv))
    2296           0 :                 device = mMemoryDevice;
    2297             :         }
    2298             :     }
    2299             : 
    2300           0 :     if (!device && entry->IsStreamData() &&
    2301           0 :         entry->IsAllowedOffline() && mEnableOfflineDevice) {
    2302           0 :         if (!mOfflineDevice) {
    2303           0 :             (void)CreateOfflineDevice(); // ignore the error (check for mOfflineDevice instead)
    2304             :         }
    2305             : 
    2306           0 :         device = entry->CustomCacheDevice()
    2307           0 :                ? entry->CustomCacheDevice()
    2308             :                : mOfflineDevice;
    2309             : 
    2310           0 :         if (device) {
    2311           0 :             entry->MarkBinding();
    2312           0 :             nsresult rv = device->BindEntry(entry);
    2313           0 :             entry->ClearBinding();
    2314           0 :             if (NS_FAILED(rv))
    2315           0 :                 device = nullptr;
    2316             :         }
    2317             :     }
    2318             : 
    2319           0 :     if (device)
    2320           0 :         entry->SetCacheDevice(device);
    2321           0 :     return device;
    2322             : }
    2323             : 
    2324             : nsresult
    2325           0 : nsCacheService::DoomEntry(nsCacheEntry * entry)
    2326             : {
    2327           0 :     return gService->DoomEntry_Internal(entry, true);
    2328             : }
    2329             : 
    2330             : 
    2331             : nsresult
    2332           0 : nsCacheService::DoomEntry_Internal(nsCacheEntry * entry,
    2333             :                                    bool doProcessPendingRequests)
    2334             : {
    2335           0 :     if (entry->IsDoomed())  return NS_OK;
    2336             : 
    2337           0 :     CACHE_LOG_DEBUG(("Dooming entry %p\n", entry));
    2338           0 :     nsresult  rv = NS_OK;
    2339           0 :     entry->MarkDoomed();
    2340             : 
    2341           0 :     NS_ASSERTION(!entry->IsBinding(), "Dooming entry while binding device.");
    2342           0 :     nsCacheDevice * device = entry->CacheDevice();
    2343           0 :     if (device)  device->DoomEntry(entry);
    2344             : 
    2345           0 :     if (entry->IsActive()) {
    2346             :         // remove from active entries
    2347           0 :         mActiveEntries.RemoveEntry(entry);
    2348           0 :         CACHE_LOG_DEBUG(("Removed entry %p from mActiveEntries\n", entry));
    2349           0 :         entry->MarkInactive();
    2350             :      }
    2351             : 
    2352             :     // put on doom list to wait for descriptors to close
    2353           0 :     NS_ASSERTION(PR_CLIST_IS_EMPTY(entry), "doomed entry still on device list");
    2354           0 :     PR_APPEND_LINK(entry, &mDoomedEntries);
    2355             : 
    2356             :     // handle pending requests only if we're supposed to
    2357           0 :     if (doProcessPendingRequests) {
    2358             :         // tell pending requests to get on with their lives...
    2359           0 :         rv = ProcessPendingRequests(entry);
    2360             : 
    2361             :         // All requests have been removed, but there may still be open descriptors
    2362           0 :         if (entry->IsNotInUse()) {
    2363           0 :             DeactivateEntry(entry); // tell device to get rid of it
    2364             :         }
    2365             :     }
    2366           0 :     return rv;
    2367             : }
    2368             : 
    2369             : 
    2370             : void
    2371           0 : nsCacheService::OnProfileShutdown()
    2372             : {
    2373           0 :     if (!gService || !gService->mInitialized) {
    2374             :         // The cache service has been shut down, but someone is still holding
    2375             :         // a reference to it. Ignore this call.
    2376           0 :         return;
    2377             :     }
    2378             : 
    2379             :     {
    2380           0 :         nsCacheServiceAutoLock lock(LOCK_TELEM(NSCACHESERVICE_ONPROFILESHUTDOWN));
    2381           0 :         gService->mClearingEntries = true;
    2382           0 :         gService->DoomActiveEntries(nullptr);
    2383             :     }
    2384             : 
    2385           0 :     gService->CloseAllStreams();
    2386             : 
    2387           0 :     nsCacheServiceAutoLock lock(LOCK_TELEM(NSCACHESERVICE_ONPROFILESHUTDOWN));
    2388           0 :     gService->ClearDoomList();
    2389             : 
    2390             :     // Make sure to wait for any pending cache-operations before
    2391             :     // proceeding with destructive actions (bug #620660)
    2392           0 :     (void) SyncWithCacheIOThread();
    2393             : 
    2394           0 :     if (gService->mDiskDevice && gService->mEnableDiskDevice) {
    2395           0 :         gService->mDiskDevice->Shutdown();
    2396             :     }
    2397           0 :     gService->mEnableDiskDevice = false;
    2398             : 
    2399           0 :     if (gService->mOfflineDevice && gService->mEnableOfflineDevice) {
    2400           0 :         gService->mOfflineDevice->Shutdown();
    2401             :     }
    2402           0 :     for (auto iter = gService->mCustomOfflineDevices.Iter();
    2403           0 :          !iter.Done(); iter.Next()) {
    2404           0 :         iter.Data()->Shutdown();
    2405           0 :         iter.Remove();
    2406             :     }
    2407             : 
    2408           0 :     gService->mEnableOfflineDevice = false;
    2409             : 
    2410           0 :     if (gService->mMemoryDevice) {
    2411             :         // clear memory cache
    2412           0 :         gService->mMemoryDevice->EvictEntries(nullptr);
    2413             :     }
    2414             : 
    2415           0 :     gService->mClearingEntries = false;
    2416             : }
    2417             : 
    2418             : 
    2419             : void
    2420           0 : nsCacheService::OnProfileChanged()
    2421             : {
    2422           0 :     if (!gService)  return;
    2423             : 
    2424           0 :     CACHE_LOG_DEBUG(("nsCacheService::OnProfileChanged"));
    2425             : 
    2426           0 :     nsCacheServiceAutoLock lock(LOCK_TELEM(NSCACHESERVICE_ONPROFILECHANGED));
    2427             : 
    2428           0 :     gService->mEnableDiskDevice    = gService->mObserver->DiskCacheEnabled();
    2429           0 :     gService->mEnableOfflineDevice = gService->mObserver->OfflineCacheEnabled();
    2430           0 :     gService->mEnableMemoryDevice  = gService->mObserver->MemoryCacheEnabled();
    2431             : 
    2432           0 :     if (gService->mDiskDevice) {
    2433           0 :         gService->mDiskDevice->SetCacheParentDirectory(gService->mObserver->DiskCacheParentDirectory());
    2434           0 :         gService->mDiskDevice->SetCapacity(gService->mObserver->DiskCacheCapacity());
    2435             : 
    2436             :         // XXX initialization of mDiskDevice could be made lazily, if mEnableDiskDevice is false
    2437           0 :         nsresult rv = gService->mDiskDevice->Init();
    2438           0 :         if (NS_FAILED(rv)) {
    2439           0 :             NS_ERROR("nsCacheService::OnProfileChanged: Re-initializing disk device failed");
    2440           0 :             gService->mEnableDiskDevice = false;
    2441             :             // XXX delete mDiskDevice?
    2442             :         }
    2443             :     }
    2444             : 
    2445           0 :     if (gService->mOfflineDevice) {
    2446           0 :         gService->mOfflineDevice->SetCacheParentDirectory(gService->mObserver->OfflineCacheParentDirectory());
    2447           0 :         gService->mOfflineDevice->SetCapacity(gService->mObserver->OfflineCacheCapacity());
    2448             : 
    2449             :         // XXX initialization of mOfflineDevice could be made lazily, if mEnableOfflineDevice is false
    2450           0 :         nsresult rv = gService->mOfflineDevice->InitWithSqlite(gService->mStorageService);
    2451           0 :         if (NS_FAILED(rv)) {
    2452           0 :             NS_ERROR("nsCacheService::OnProfileChanged: Re-initializing offline device failed");
    2453           0 :             gService->mEnableOfflineDevice = false;
    2454             :             // XXX delete mOfflineDevice?
    2455             :         }
    2456             :     }
    2457             : 
    2458             :     // If memoryDevice exists, reset its size to the new profile
    2459           0 :     if (gService->mMemoryDevice) {
    2460           0 :         if (gService->mEnableMemoryDevice) {
    2461             :             // make sure that capacity is reset to the right value
    2462           0 :             int32_t capacity = gService->mObserver->MemoryCacheCapacity();
    2463           0 :             CACHE_LOG_DEBUG(("Resetting memory device capacity to %d\n",
    2464             :                              capacity));
    2465           0 :             gService->mMemoryDevice->SetCapacity(capacity);
    2466             :         } else {
    2467             :             // tell memory device to evict everything
    2468           0 :             CACHE_LOG_DEBUG(("memory device disabled\n"));
    2469           0 :             gService->mMemoryDevice->SetCapacity(0);
    2470             :             // Don't delete memory device, because some entries may be active still...
    2471             :         }
    2472             :     }
    2473             : }
    2474             : 
    2475             : 
    2476             : void
    2477           0 : nsCacheService::SetDiskCacheEnabled(bool    enabled)
    2478             : {
    2479           0 :     if (!gService)  return;
    2480           0 :     nsCacheServiceAutoLock lock(LOCK_TELEM(NSCACHESERVICE_SETDISKCACHEENABLED));
    2481           0 :     gService->mEnableDiskDevice = enabled;
    2482             : }
    2483             : 
    2484             : 
    2485             : void
    2486           0 : nsCacheService::SetDiskCacheCapacity(int32_t  capacity)
    2487             : {
    2488           0 :     if (!gService)  return;
    2489           0 :     nsCacheServiceAutoLock lock(LOCK_TELEM(NSCACHESERVICE_SETDISKCACHECAPACITY));
    2490             : 
    2491           0 :     if (gService->mDiskDevice) {
    2492           0 :         gService->mDiskDevice->SetCapacity(capacity);
    2493             :     }
    2494             : 
    2495           0 :     gService->mEnableDiskDevice = gService->mObserver->DiskCacheEnabled();
    2496             : }
    2497             : 
    2498             : void
    2499           0 : nsCacheService::SetDiskCacheMaxEntrySize(int32_t  maxSize)
    2500             : {
    2501           0 :     if (!gService)  return;
    2502           0 :     nsCacheServiceAutoLock lock(LOCK_TELEM(NSCACHESERVICE_SETDISKCACHEMAXENTRYSIZE));
    2503             : 
    2504           0 :     if (gService->mDiskDevice) {
    2505           0 :         gService->mDiskDevice->SetMaxEntrySize(maxSize);
    2506             :     }
    2507             : }
    2508             : 
    2509             : void
    2510           0 : nsCacheService::SetMemoryCacheMaxEntrySize(int32_t  maxSize)
    2511             : {
    2512           0 :     if (!gService)  return;
    2513           0 :     nsCacheServiceAutoLock lock(LOCK_TELEM(NSCACHESERVICE_SETMEMORYCACHEMAXENTRYSIZE));
    2514             : 
    2515           0 :     if (gService->mMemoryDevice) {
    2516           0 :         gService->mMemoryDevice->SetMaxEntrySize(maxSize);
    2517             :     }
    2518             : }
    2519             : 
    2520             : void
    2521           0 : nsCacheService::SetOfflineCacheEnabled(bool    enabled)
    2522             : {
    2523           0 :     if (!gService)  return;
    2524           0 :     nsCacheServiceAutoLock lock(LOCK_TELEM(NSCACHESERVICE_SETOFFLINECACHEENABLED));
    2525           0 :     gService->mEnableOfflineDevice = enabled;
    2526             : }
    2527             : 
    2528             : void
    2529           0 : nsCacheService::SetOfflineCacheCapacity(int32_t  capacity)
    2530             : {
    2531           0 :     if (!gService)  return;
    2532           0 :     nsCacheServiceAutoLock lock(LOCK_TELEM(NSCACHESERVICE_SETOFFLINECACHECAPACITY));
    2533             : 
    2534           0 :     if (gService->mOfflineDevice) {
    2535           0 :         gService->mOfflineDevice->SetCapacity(capacity);
    2536             :     }
    2537             : 
    2538           0 :     gService->mEnableOfflineDevice = gService->mObserver->OfflineCacheEnabled();
    2539             : }
    2540             : 
    2541             : 
    2542             : void
    2543           0 : nsCacheService::SetMemoryCache()
    2544             : {
    2545           0 :     if (!gService)  return;
    2546             : 
    2547           0 :     CACHE_LOG_DEBUG(("nsCacheService::SetMemoryCache"));
    2548             : 
    2549           0 :     nsCacheServiceAutoLock lock(LOCK_TELEM(NSCACHESERVICE_SETMEMORYCACHE));
    2550             : 
    2551           0 :     gService->mEnableMemoryDevice = gService->mObserver->MemoryCacheEnabled();
    2552             : 
    2553           0 :     if (gService->mEnableMemoryDevice) {
    2554           0 :         if (gService->mMemoryDevice) {
    2555           0 :             int32_t capacity = gService->mObserver->MemoryCacheCapacity();
    2556             :             // make sure that capacity is reset to the right value
    2557           0 :             CACHE_LOG_DEBUG(("Resetting memory device capacity to %d\n",
    2558             :                              capacity));
    2559           0 :             gService->mMemoryDevice->SetCapacity(capacity);
    2560             :         }
    2561             :     } else {
    2562           0 :         if (gService->mMemoryDevice) {
    2563             :             // tell memory device to evict everything
    2564           0 :             CACHE_LOG_DEBUG(("memory device disabled\n"));
    2565           0 :             gService->mMemoryDevice->SetCapacity(0);
    2566             :             // Don't delete memory device, because some entries may be active still...
    2567             :         }
    2568             :     }
    2569             : }
    2570             : 
    2571             : 
    2572             : /******************************************************************************
    2573             :  * static methods for nsCacheEntryDescriptor
    2574             :  *****************************************************************************/
    2575             : void
    2576           0 : nsCacheService::CloseDescriptor(nsCacheEntryDescriptor * descriptor)
    2577             : {
    2578             :     // ask entry to remove descriptor
    2579           0 :     nsCacheEntry * entry = descriptor->CacheEntry();
    2580             :     bool doomEntry;
    2581           0 :     bool stillActive = entry->RemoveDescriptor(descriptor, &doomEntry);
    2582             : 
    2583           0 :     if (!entry->IsValid()) {
    2584           0 :         gService->ProcessPendingRequests(entry);
    2585             :     }
    2586             : 
    2587           0 :     if (doomEntry) {
    2588           0 :         gService->DoomEntry_Internal(entry, true);
    2589           0 :         return;
    2590             :     }
    2591             : 
    2592           0 :     if (!stillActive) {
    2593           0 :         gService->DeactivateEntry(entry);
    2594             :     }
    2595             : }
    2596             : 
    2597             : 
    2598             : nsresult
    2599           0 : nsCacheService::GetFileForEntry(nsCacheEntry *         entry,
    2600             :                                 nsIFile **             result)
    2601             : {
    2602           0 :     nsCacheDevice * device = gService->EnsureEntryHasDevice(entry);
    2603           0 :     if (!device)  return  NS_ERROR_UNEXPECTED;
    2604             : 
    2605           0 :     return device->GetFileForEntry(entry, result);
    2606             : }
    2607             : 
    2608             : 
    2609             : nsresult
    2610           0 : nsCacheService::OpenInputStreamForEntry(nsCacheEntry *     entry,
    2611             :                                         nsCacheAccessMode  mode,
    2612             :                                         uint32_t           offset,
    2613             :                                         nsIInputStream  ** result)
    2614             : {
    2615           0 :     nsCacheDevice * device = gService->EnsureEntryHasDevice(entry);
    2616           0 :     if (!device)  return  NS_ERROR_UNEXPECTED;
    2617             : 
    2618           0 :     return device->OpenInputStreamForEntry(entry, mode, offset, result);
    2619             : }
    2620             : 
    2621             : nsresult
    2622           0 : nsCacheService::OpenOutputStreamForEntry(nsCacheEntry *     entry,
    2623             :                                          nsCacheAccessMode  mode,
    2624             :                                          uint32_t           offset,
    2625             :                                          nsIOutputStream ** result)
    2626             : {
    2627           0 :     nsCacheDevice * device = gService->EnsureEntryHasDevice(entry);
    2628           0 :     if (!device)  return  NS_ERROR_UNEXPECTED;
    2629             : 
    2630           0 :     return device->OpenOutputStreamForEntry(entry, mode, offset, result);
    2631             : }
    2632             : 
    2633             : 
    2634             : nsresult
    2635           0 : nsCacheService::OnDataSizeChange(nsCacheEntry * entry, int32_t deltaSize)
    2636             : {
    2637           0 :     nsCacheDevice * device = gService->EnsureEntryHasDevice(entry);
    2638           0 :     if (!device)  return  NS_ERROR_UNEXPECTED;
    2639             : 
    2640           0 :     return device->OnDataSizeChange(entry, deltaSize);
    2641             : }
    2642             : 
    2643             : void
    2644           0 : nsCacheService::LockAcquired()
    2645             : {
    2646           0 :     MutexAutoLock lock(mTimeStampLock);
    2647           0 :     mLockAcquiredTimeStamp = TimeStamp::Now();
    2648           0 : }
    2649             : 
    2650             : void
    2651           0 : nsCacheService::LockReleased()
    2652             : {
    2653           0 :     MutexAutoLock lock(mTimeStampLock);
    2654           0 :     mLockAcquiredTimeStamp = TimeStamp();
    2655           0 : }
    2656             : 
    2657             : void
    2658           0 : nsCacheService::Lock()
    2659             : {
    2660           0 :     gService->mLock.Lock();
    2661           0 :     gService->LockAcquired();
    2662           0 : }
    2663             : 
    2664             : void
    2665           0 : nsCacheService::Lock(mozilla::Telemetry::HistogramID mainThreadLockerID)
    2666             : {
    2667             :     mozilla::Telemetry::HistogramID lockerID;
    2668             :     mozilla::Telemetry::HistogramID generalID;
    2669             : 
    2670           0 :     if (NS_IsMainThread()) {
    2671           0 :         lockerID = mainThreadLockerID;
    2672           0 :         generalID = mozilla::Telemetry::CACHE_SERVICE_LOCK_WAIT_MAINTHREAD_2;
    2673             :     } else {
    2674           0 :         lockerID = mozilla::Telemetry::HistogramCount;
    2675           0 :         generalID = mozilla::Telemetry::CACHE_SERVICE_LOCK_WAIT_2;
    2676             :     }
    2677             : 
    2678           0 :     TimeStamp start(TimeStamp::Now());
    2679             : 
    2680           0 :     nsCacheService::Lock();
    2681             : 
    2682           0 :     TimeStamp stop(TimeStamp::Now());
    2683             : 
    2684             :     // Telemetry isn't thread safe on its own, but this is OK because we're
    2685             :     // protecting it with the cache lock.
    2686           0 :     if (lockerID != mozilla::Telemetry::HistogramCount) {
    2687           0 :         mozilla::Telemetry::AccumulateTimeDelta(lockerID, start, stop);
    2688             :     }
    2689           0 :     mozilla::Telemetry::AccumulateTimeDelta(generalID, start, stop);
    2690           0 : }
    2691             : 
    2692             : void
    2693           0 : nsCacheService::Unlock()
    2694             : {
    2695           0 :     gService->mLock.AssertCurrentThreadOwns();
    2696             : 
    2697           0 :     nsTArray<nsISupports*> doomed;
    2698           0 :     doomed.SwapElements(gService->mDoomedObjects);
    2699             : 
    2700           0 :     gService->LockReleased();
    2701           0 :     gService->mLock.Unlock();
    2702             : 
    2703           0 :     for (uint32_t i = 0; i < doomed.Length(); ++i)
    2704           0 :         doomed[i]->Release();
    2705           0 : }
    2706             : 
    2707             : void
    2708           0 : nsCacheService::ReleaseObject_Locked(nsISupports * obj,
    2709             :                                      nsIEventTarget * target)
    2710             : {
    2711           0 :     gService->mLock.AssertCurrentThreadOwns();
    2712             : 
    2713             :     bool isCur;
    2714           0 :     if (!target || (NS_SUCCEEDED(target->IsOnCurrentThread(&isCur)) && isCur)) {
    2715           0 :         gService->mDoomedObjects.AppendElement(obj);
    2716             :     } else {
    2717             :         NS_ProxyRelease(
    2718           0 :           "nsCacheService::ReleaseObject_Locked::obj", target, dont_AddRef(obj));
    2719             :     }
    2720           0 : }
    2721             : 
    2722             : 
    2723             : nsresult
    2724           0 : nsCacheService::SetCacheElement(nsCacheEntry * entry, nsISupports * element)
    2725             : {
    2726           0 :     entry->SetData(element);
    2727           0 :     entry->TouchData();
    2728           0 :     return NS_OK;
    2729             : }
    2730             : 
    2731             : 
    2732             : nsresult
    2733           0 : nsCacheService::ValidateEntry(nsCacheEntry * entry)
    2734             : {
    2735           0 :     nsCacheDevice * device = gService->EnsureEntryHasDevice(entry);
    2736           0 :     if (!device)  return  NS_ERROR_UNEXPECTED;
    2737             : 
    2738           0 :     entry->MarkValid();
    2739           0 :     nsresult rv = gService->ProcessPendingRequests(entry);
    2740           0 :     NS_ASSERTION(rv == NS_OK, "ProcessPendingRequests failed.");
    2741             :     // XXX what else should be done?
    2742             : 
    2743           0 :     return rv;
    2744             : }
    2745             : 
    2746             : 
    2747             : int32_t
    2748           0 : nsCacheService::CacheCompressionLevel()
    2749             : {
    2750           0 :     int32_t level = gService->mObserver->CacheCompressionLevel();
    2751           0 :     return level;
    2752             : }
    2753             : 
    2754             : 
    2755             : void
    2756           0 : nsCacheService::DeactivateEntry(nsCacheEntry * entry)
    2757             : {
    2758           0 :     CACHE_LOG_DEBUG(("Deactivating entry %p\n", entry));
    2759           0 :     nsresult  rv = NS_OK;
    2760           0 :     NS_ASSERTION(entry->IsNotInUse(), "### deactivating an entry while in use!");
    2761           0 :     nsCacheDevice * device = nullptr;
    2762             : 
    2763           0 :     if (mMaxDataSize < entry->DataSize() )     mMaxDataSize = entry->DataSize();
    2764           0 :     if (mMaxMetaSize < entry->MetaDataSize() ) mMaxMetaSize = entry->MetaDataSize();
    2765             : 
    2766           0 :     if (entry->IsDoomed()) {
    2767             :         // remove from Doomed list
    2768           0 :         PR_REMOVE_AND_INIT_LINK(entry);
    2769           0 :     } else if (entry->IsActive()) {
    2770             :         // remove from active entries
    2771           0 :         mActiveEntries.RemoveEntry(entry);
    2772           0 :         CACHE_LOG_DEBUG(("Removed deactivated entry %p from mActiveEntries\n",
    2773             :                          entry));
    2774           0 :         entry->MarkInactive();
    2775             : 
    2776             :         // bind entry if necessary to store meta-data
    2777           0 :         device = EnsureEntryHasDevice(entry);
    2778           0 :         if (!device) {
    2779           0 :             CACHE_LOG_DEBUG(("DeactivateEntry: unable to bind active "
    2780             :                              "entry %p\n",
    2781             :                              entry));
    2782           0 :             NS_WARNING("DeactivateEntry: unable to bind active entry\n");
    2783           0 :             return;
    2784             :         }
    2785             :     } else {
    2786             :         // if mInitialized == false,
    2787             :         // then we're shutting down and this state is okay.
    2788           0 :         NS_ASSERTION(!mInitialized, "DeactivateEntry: bad cache entry state.");
    2789             :     }
    2790             : 
    2791           0 :     device = entry->CacheDevice();
    2792           0 :     if (device) {
    2793           0 :         rv = device->DeactivateEntry(entry);
    2794           0 :         if (NS_FAILED(rv)) {
    2795             :             // increment deactivate failure count
    2796           0 :             ++mDeactivateFailures;
    2797             :         }
    2798             :     } else {
    2799             :         // increment deactivating unbound entry statistic
    2800           0 :         ++mDeactivatedUnboundEntries;
    2801           0 :         delete entry; // because no one else will
    2802             :     }
    2803             : }
    2804             : 
    2805             : 
    2806             : nsresult
    2807           0 : nsCacheService::ProcessPendingRequests(nsCacheEntry * entry)
    2808             : {
    2809           0 :     nsresult            rv = NS_OK;
    2810           0 :     nsCacheRequest *    request = (nsCacheRequest *)PR_LIST_HEAD(&entry->mRequestQ);
    2811             :     nsCacheRequest *    nextRequest;
    2812           0 :     bool                newWriter = false;
    2813             : 
    2814           0 :     CACHE_LOG_DEBUG(("ProcessPendingRequests for %sinitialized %s %salid entry %p\n",
    2815             :                     (entry->IsInitialized()?"" : "Un"),
    2816             :                     (entry->IsDoomed()?"DOOMED" : ""),
    2817             :                     (entry->IsValid()? "V":"Inv"), entry));
    2818             : 
    2819           0 :     if (request == &entry->mRequestQ)  return NS_OK;    // no queued requests
    2820             : 
    2821           0 :     if (!entry->IsDoomed() && entry->IsInvalid()) {
    2822             :         // 1st descriptor closed w/o MarkValid()
    2823           0 :         NS_ASSERTION(PR_CLIST_IS_EMPTY(&entry->mDescriptorQ), "shouldn't be here with open descriptors");
    2824             : 
    2825             : #if DEBUG
    2826             :         // verify no ACCESS_WRITE requests(shouldn't have any of these)
    2827           0 :         while (request != &entry->mRequestQ) {
    2828           0 :             NS_ASSERTION(request->AccessRequested() != nsICache::ACCESS_WRITE,
    2829             :                          "ACCESS_WRITE request should have been given a new entry");
    2830           0 :             request = (nsCacheRequest *)PR_NEXT_LINK(request);
    2831             :         }
    2832           0 :         request = (nsCacheRequest *)PR_LIST_HEAD(&entry->mRequestQ);
    2833             : #endif
    2834             :         // find first request with ACCESS_READ_WRITE (if any) and promote it to 1st writer
    2835           0 :         while (request != &entry->mRequestQ) {
    2836           0 :             if (request->AccessRequested() == nsICache::ACCESS_READ_WRITE) {
    2837           0 :                 newWriter = true;
    2838           0 :                 CACHE_LOG_DEBUG(("  promoting request %p to 1st writer\n", request));
    2839           0 :                 break;
    2840             :             }
    2841             : 
    2842           0 :             request = (nsCacheRequest *)PR_NEXT_LINK(request);
    2843             :         }
    2844             : 
    2845           0 :         if (request == &entry->mRequestQ)   // no requests asked for ACCESS_READ_WRITE, back to top
    2846           0 :             request = (nsCacheRequest *)PR_LIST_HEAD(&entry->mRequestQ);
    2847             : 
    2848             :         // XXX what should we do if there are only READ requests in queue?
    2849             :         // XXX serialize their accesses, give them only read access, but force them to check validate flag?
    2850             :         // XXX or do readers simply presume the entry is valid
    2851             :         // See fix for bug #467392 below
    2852             :     }
    2853             : 
    2854           0 :     nsCacheAccessMode  accessGranted = nsICache::ACCESS_NONE;
    2855             : 
    2856           0 :     while (request != &entry->mRequestQ) {
    2857           0 :         nextRequest = (nsCacheRequest *)PR_NEXT_LINK(request);
    2858           0 :         CACHE_LOG_DEBUG(("  %sync request %p for %p\n",
    2859             :                         (request->mListener?"As":"S"), request, entry));
    2860             : 
    2861           0 :         if (request->mListener) {
    2862             : 
    2863             :             // Async request
    2864           0 :             PR_REMOVE_AND_INIT_LINK(request);
    2865             : 
    2866           0 :             if (entry->IsDoomed()) {
    2867           0 :                 rv = ProcessRequest(request, false, nullptr);
    2868           0 :                 if (rv == NS_ERROR_CACHE_WAIT_FOR_VALIDATION)
    2869           0 :                     rv = NS_OK;
    2870             :                 else
    2871           0 :                     delete request;
    2872             : 
    2873           0 :                 if (NS_FAILED(rv)) {
    2874             :                     // XXX what to do?
    2875             :                 }
    2876           0 :             } else if (entry->IsValid() || newWriter) {
    2877           0 :                 rv = entry->RequestAccess(request, &accessGranted);
    2878           0 :                 NS_ASSERTION(NS_SUCCEEDED(rv),
    2879             :                              "if entry is valid, RequestAccess must succeed.");
    2880             :                 // XXX if (newWriter)  NS_ASSERTION( accessGranted == request->AccessRequested(), "why not?");
    2881             : 
    2882             :                 // entry->CreateDescriptor dequeues request, and queues descriptor
    2883           0 :                 nsICacheEntryDescriptor *descriptor = nullptr;
    2884           0 :                 rv = entry->CreateDescriptor(request,
    2885             :                                              accessGranted,
    2886           0 :                                              &descriptor);
    2887             : 
    2888             :                 // post call to listener to report error or descriptor
    2889           0 :                 rv = NotifyListener(request, descriptor, accessGranted, rv);
    2890           0 :                 delete request;
    2891           0 :                 if (NS_FAILED(rv)) {
    2892             :                     // XXX what to do?
    2893             :                 }
    2894             : 
    2895             :             } else {
    2896             :                 // read-only request to an invalid entry - need to wait for
    2897             :                 // the entry to become valid so we post an event to process
    2898             :                 // the request again later (bug #467392)
    2899             :                 nsCOMPtr<nsIRunnable> ev =
    2900           0 :                     new nsProcessRequestEvent(request);
    2901           0 :                 rv = DispatchToCacheIOThread(ev);
    2902           0 :                 if (NS_FAILED(rv)) {
    2903           0 :                     delete request; // avoid leak
    2904             :                 }
    2905             :             }
    2906             :         } else {
    2907             : 
    2908             :             // Synchronous request
    2909           0 :             request->WakeUp();
    2910             :         }
    2911           0 :         if (newWriter)  break;  // process remaining requests after validation
    2912           0 :         request = nextRequest;
    2913             :     }
    2914             : 
    2915           0 :     return NS_OK;
    2916             : }
    2917             : 
    2918             : bool
    2919           0 : nsCacheService::IsDoomListEmpty()
    2920             : {
    2921           0 :     nsCacheEntry * entry = (nsCacheEntry *)PR_LIST_HEAD(&mDoomedEntries);
    2922           0 :     return &mDoomedEntries == entry;
    2923             : }
    2924             : 
    2925             : void
    2926           0 : nsCacheService::ClearDoomList()
    2927             : {
    2928           0 :     nsCacheEntry * entry = (nsCacheEntry *)PR_LIST_HEAD(&mDoomedEntries);
    2929             : 
    2930           0 :     while (entry != &mDoomedEntries) {
    2931           0 :         nsCacheEntry * next = (nsCacheEntry *)PR_NEXT_LINK(entry);
    2932             : 
    2933           0 :         entry->DetachDescriptors();
    2934           0 :         DeactivateEntry(entry);
    2935           0 :         entry = next;
    2936             :     }
    2937           0 : }
    2938             : 
    2939             : void
    2940           0 : nsCacheService::DoomActiveEntries(DoomCheckFn check)
    2941             : {
    2942           0 :     AutoTArray<nsCacheEntry*, 8> array;
    2943             : 
    2944           0 :     for (auto iter = mActiveEntries.Iter(); !iter.Done(); iter.Next()) {
    2945             :         nsCacheEntry* entry =
    2946           0 :             static_cast<nsCacheEntryHashTableEntry*>(iter.Get())->cacheEntry;
    2947             : 
    2948           0 :         if (check && !check(entry)) {
    2949           0 :             continue;
    2950             :         }
    2951             : 
    2952           0 :         array.AppendElement(entry);
    2953             : 
    2954             :         // entry is being removed from the active entry list
    2955           0 :         entry->MarkInactive();
    2956           0 :         iter.Remove();
    2957             :     }
    2958             : 
    2959           0 :     uint32_t count = array.Length();
    2960           0 :     for (uint32_t i = 0; i < count; ++i) {
    2961           0 :         DoomEntry_Internal(array[i], true);
    2962             :     }
    2963           0 : }
    2964             : 
    2965             : void
    2966           0 : nsCacheService::CloseAllStreams()
    2967             : {
    2968           0 :     nsTArray<RefPtr<nsCacheEntryDescriptor::nsInputStreamWrapper> > inputs;
    2969           0 :     nsTArray<RefPtr<nsCacheEntryDescriptor::nsOutputStreamWrapper> > outputs;
    2970             : 
    2971             :     {
    2972           0 :         nsCacheServiceAutoLock lock(LOCK_TELEM(NSCACHESERVICE_CLOSEALLSTREAMS));
    2973             : 
    2974           0 :         nsTArray<nsCacheEntry*> entries;
    2975             : 
    2976             : #if DEBUG
    2977             :         // make sure there is no active entry
    2978           0 :         for (auto iter = mActiveEntries.Iter(); !iter.Done(); iter.Next()) {
    2979           0 :             auto entry = static_cast<nsCacheEntryHashTableEntry*>(iter.Get());
    2980           0 :             entries.AppendElement(entry->cacheEntry);
    2981             :         }
    2982           0 :         NS_ASSERTION(entries.IsEmpty(), "Bad state");
    2983             : #endif
    2984             : 
    2985             :         // Get doomed entries
    2986           0 :         nsCacheEntry * entry = (nsCacheEntry *)PR_LIST_HEAD(&mDoomedEntries);
    2987           0 :         while (entry != &mDoomedEntries) {
    2988           0 :             nsCacheEntry * next = (nsCacheEntry *)PR_NEXT_LINK(entry);
    2989           0 :             entries.AppendElement(entry);
    2990           0 :             entry = next;
    2991             :         }
    2992             : 
    2993             :         // Iterate through all entries and collect input and output streams
    2994           0 :         for (size_t i = 0; i < entries.Length(); i++) {
    2995           0 :             entry = entries.ElementAt(i);
    2996             : 
    2997           0 :             nsTArray<RefPtr<nsCacheEntryDescriptor> > descs;
    2998           0 :             entry->GetDescriptors(descs);
    2999             : 
    3000           0 :             for (uint32_t j = 0 ; j < descs.Length() ; j++) {
    3001           0 :                 if (descs[j]->mOutputWrapper)
    3002           0 :                     outputs.AppendElement(descs[j]->mOutputWrapper);
    3003             : 
    3004           0 :                 for (size_t k = 0; k < descs[j]->mInputWrappers.Length(); k++)
    3005           0 :                     inputs.AppendElement(descs[j]->mInputWrappers[k]);
    3006             :             }
    3007             :         }
    3008             :     }
    3009             : 
    3010             :     uint32_t i;
    3011           0 :     for (i = 0 ; i < inputs.Length() ; i++)
    3012           0 :         inputs[i]->Close();
    3013             : 
    3014           0 :     for (i = 0 ; i < outputs.Length() ; i++)
    3015           0 :         outputs[i]->Close();
    3016           0 : }
    3017             : 
    3018             : 
    3019             : bool
    3020           0 : nsCacheService::GetClearingEntries()
    3021             : {
    3022           0 :     AssertOwnsLock();
    3023           0 :     return gService->mClearingEntries;
    3024             : }
    3025             : 
    3026             : // static
    3027           1 : void nsCacheService::GetCacheBaseDirectoty(nsIFile ** result)
    3028             : {
    3029           1 :     *result = nullptr;
    3030           1 :     if (!gService || !gService->mObserver)
    3031           0 :         return;
    3032             : 
    3033             :     nsCOMPtr<nsIFile> directory =
    3034           2 :         gService->mObserver->DiskCacheParentDirectory();
    3035           1 :     if (!directory)
    3036           0 :         return;
    3037             : 
    3038           1 :     directory->Clone(result);
    3039             : }
    3040             : 
    3041             : // static
    3042           1 : void nsCacheService::GetDiskCacheDirectory(nsIFile ** result)
    3043             : {
    3044           2 :     nsCOMPtr<nsIFile> directory;
    3045           1 :     GetCacheBaseDirectoty(getter_AddRefs(directory));
    3046           1 :     if (!directory)
    3047           0 :         return;
    3048             : 
    3049           1 :     nsresult rv = directory->AppendNative(NS_LITERAL_CSTRING("Cache"));
    3050           1 :     if (NS_FAILED(rv))
    3051           0 :         return;
    3052             : 
    3053           1 :     directory.forget(result);
    3054             : }
    3055             : 
    3056             : // static
    3057           0 : void nsCacheService::GetAppCacheDirectory(nsIFile ** result)
    3058             : {
    3059           0 :     nsCOMPtr<nsIFile> directory;
    3060           0 :     GetCacheBaseDirectoty(getter_AddRefs(directory));
    3061           0 :     if (!directory)
    3062           0 :         return;
    3063             : 
    3064           0 :     nsresult rv = directory->AppendNative(NS_LITERAL_CSTRING("OfflineCache"));
    3065           0 :     if (NS_FAILED(rv))
    3066           0 :         return;
    3067             : 
    3068           0 :     directory.forget(result);
    3069             : }
    3070             : 
    3071             : 
    3072             : void
    3073           0 : nsCacheService::LogCacheStatistics()
    3074             : {
    3075           0 :     uint32_t hitPercentage = (uint32_t)((((double)mCacheHits) /
    3076           0 :         ((double)(mCacheHits + mCacheMisses))) * 100);
    3077           0 :     CACHE_LOG_INFO(("\nCache Service Statistics:\n\n"));
    3078           0 :     CACHE_LOG_INFO(("    TotalEntries   = %d\n", mTotalEntries));
    3079           0 :     CACHE_LOG_INFO(("    Cache Hits     = %d\n", mCacheHits));
    3080           0 :     CACHE_LOG_INFO(("    Cache Misses   = %d\n", mCacheMisses));
    3081           0 :     CACHE_LOG_INFO(("    Cache Hit %%    = %d%%\n", hitPercentage));
    3082           0 :     CACHE_LOG_INFO(("    Max Key Length = %d\n", mMaxKeyLength));
    3083           0 :     CACHE_LOG_INFO(("    Max Meta Size  = %d\n", mMaxMetaSize));
    3084           0 :     CACHE_LOG_INFO(("    Max Data Size  = %d\n", mMaxDataSize));
    3085           0 :     CACHE_LOG_INFO(("\n"));
    3086           0 :     CACHE_LOG_INFO(("    Deactivate Failures         = %d\n",
    3087             :                       mDeactivateFailures));
    3088           0 :     CACHE_LOG_INFO(("    Deactivated Unbound Entries = %d\n",
    3089             :                       mDeactivatedUnboundEntries));
    3090           0 : }
    3091             : 
    3092             : nsresult
    3093           0 : nsCacheService::SetDiskSmartSize()
    3094             : {
    3095           0 :     nsCacheServiceAutoLock lock(LOCK_TELEM(NSCACHESERVICE_SETDISKSMARTSIZE));
    3096             : 
    3097           0 :     if (!gService) return NS_ERROR_NOT_AVAILABLE;
    3098             : 
    3099           0 :     return gService->SetDiskSmartSize_Locked();
    3100             : }
    3101             : 
    3102             : nsresult
    3103           0 : nsCacheService::SetDiskSmartSize_Locked()
    3104             : {
    3105             :     nsresult rv;
    3106             : 
    3107           0 :     if (mozilla::net::CacheObserver::UseNewCache()) {
    3108           0 :         return NS_ERROR_NOT_AVAILABLE;
    3109             :     }
    3110             : 
    3111           0 :     if (!mObserver->DiskCacheParentDirectory())
    3112           0 :         return NS_ERROR_NOT_AVAILABLE;
    3113             : 
    3114           0 :     if (!mDiskDevice)
    3115           0 :         return NS_ERROR_NOT_AVAILABLE;
    3116             : 
    3117           0 :     if (!mObserver->SmartSizeEnabled())
    3118           0 :         return NS_ERROR_NOT_AVAILABLE;
    3119             : 
    3120           0 :     nsAutoString cachePath;
    3121           0 :     rv = mObserver->DiskCacheParentDirectory()->GetPath(cachePath);
    3122           0 :     if (NS_SUCCEEDED(rv)) {
    3123             :         nsCOMPtr<nsIRunnable> event =
    3124           0 :             new nsGetSmartSizeEvent(cachePath, mDiskDevice->getCacheSize(),
    3125           0 :                                     mObserver->ShouldUseOldMaxSmartSize());
    3126           0 :         DispatchToCacheIOThread(event);
    3127             :     } else {
    3128           0 :         return NS_ERROR_FAILURE;
    3129             :     }
    3130             : 
    3131           0 :     return NS_OK;
    3132             : }
    3133             : 
    3134             : void
    3135           2 : nsCacheService::MoveOrRemoveDiskCache(nsIFile *aOldCacheDir,
    3136             :                                       nsIFile *aNewCacheDir,
    3137             :                                       const char *aCacheSubdir)
    3138             : {
    3139             :     bool same;
    3140           2 :     if (NS_FAILED(aOldCacheDir->Equals(aNewCacheDir, &same)) || same)
    3141           4 :         return;
    3142             : 
    3143           0 :     nsCOMPtr<nsIFile> aOldCacheSubdir;
    3144           0 :     aOldCacheDir->Clone(getter_AddRefs(aOldCacheSubdir));
    3145             : 
    3146           0 :     nsresult rv = aOldCacheSubdir->AppendNative(
    3147           0 :         nsDependentCString(aCacheSubdir));
    3148           0 :     if (NS_FAILED(rv))
    3149           0 :         return;
    3150             : 
    3151             :     bool exists;
    3152           0 :     if (NS_FAILED(aOldCacheSubdir->Exists(&exists)) || !exists)
    3153           0 :         return;
    3154             : 
    3155           0 :     nsCOMPtr<nsIFile> aNewCacheSubdir;
    3156           0 :     aNewCacheDir->Clone(getter_AddRefs(aNewCacheSubdir));
    3157             : 
    3158           0 :     rv = aNewCacheSubdir->AppendNative(nsDependentCString(aCacheSubdir));
    3159           0 :     if (NS_FAILED(rv))
    3160           0 :         return;
    3161             : 
    3162           0 :     nsAutoCString newPath;
    3163           0 :     rv = aNewCacheSubdir->GetNativePath(newPath);
    3164           0 :     if (NS_FAILED(rv))
    3165           0 :         return;
    3166             : 
    3167           0 :     if (NS_SUCCEEDED(aNewCacheSubdir->Exists(&exists)) && !exists) {
    3168             :         // New cache directory does not exist, try to move the old one here
    3169             :         // rename needs an empty target directory
    3170             : 
    3171             :         // Make sure the parent of the target sub-dir exists
    3172           0 :         rv = aNewCacheDir->Create(nsIFile::DIRECTORY_TYPE, 0777);
    3173           0 :         if (NS_SUCCEEDED(rv) || NS_ERROR_FILE_ALREADY_EXISTS == rv) {
    3174           0 :             nsAutoCString oldPath;
    3175           0 :             rv = aOldCacheSubdir->GetNativePath(oldPath);
    3176           0 :             if (NS_FAILED(rv))
    3177           0 :                 return;
    3178           0 :             if (rename(oldPath.get(), newPath.get()) == 0)
    3179           0 :                 return;
    3180             :         }
    3181             :     }
    3182             : 
    3183             :     // Delay delete by 1 minute to avoid IO thrash on startup.
    3184           0 :     nsDeleteDir::DeleteDir(aOldCacheSubdir, false, 60000);
    3185             : }
    3186             : 
    3187             : static bool
    3188           0 : IsEntryPrivate(nsCacheEntry* entry)
    3189             : {
    3190           0 :     return entry->IsPrivate();
    3191             : }
    3192             : 
    3193             : void
    3194           0 : nsCacheService::LeavePrivateBrowsing()
    3195             : {
    3196           0 :     nsCacheServiceAutoLock lock;
    3197             : 
    3198           0 :     gService->DoomActiveEntries(IsEntryPrivate);
    3199             : 
    3200           0 :     if (gService->mMemoryDevice) {
    3201             :         // clear memory cache
    3202           0 :         gService->mMemoryDevice->EvictPrivateEntries();
    3203             :     }
    3204           0 : }
    3205             : 
    3206           0 : MOZ_DEFINE_MALLOC_SIZE_OF(DiskCacheDeviceMallocSizeOf)
    3207             : 
    3208             : NS_IMETHODIMP
    3209           0 : nsCacheService::CollectReports(nsIHandleReportCallback* aHandleReport,
    3210             :                                nsISupports* aData, bool aAnonymize)
    3211             : {
    3212           0 :     size_t disk = 0;
    3213           0 :     if (mDiskDevice) {
    3214             :         nsCacheServiceAutoLock
    3215           0 :             lock(LOCK_TELEM(NSCACHESERVICE_DISKDEVICEHEAPSIZE));
    3216           0 :         disk = mDiskDevice->SizeOfIncludingThis(DiskCacheDeviceMallocSizeOf);
    3217             :     }
    3218             : 
    3219           0 :     size_t memory = mMemoryDevice ? mMemoryDevice->TotalSize() : 0;
    3220             : 
    3221           0 :     MOZ_COLLECT_REPORT(
    3222             :         "explicit/network/disk-cache", KIND_HEAP, UNITS_BYTES, disk,
    3223           0 :         "Memory used by the network disk cache.");
    3224             : 
    3225           0 :     MOZ_COLLECT_REPORT(
    3226             :         "explicit/network/memory-cache", KIND_HEAP, UNITS_BYTES, memory,
    3227           0 :         "Memory used by the network memory cache.");
    3228             : 
    3229           0 :     return NS_OK;
    3230             : }

Generated by: LCOV version 1.13