LCOV - code coverage report
Current view: directory - netwerk/cache - nsCacheService.cpp (source / functions) Found Hit Coverage
Test: app.info Lines: 1180 918 77.8 %
Date: 2012-06-02 Functions: 133 103 77.4 %

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

Generated by: LCOV version 1.7