LCOV - app.info - netwerk/cache/nsCacheService.cpp

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