netwerk/cache/nsCacheService.cpp

changeset 0
6474c204b198
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/netwerk/cache/nsCacheService.cpp	Wed Dec 31 06:09:35 2014 +0100
     1.3 @@ -0,0 +1,3286 @@
     1.4 +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
     1.5 +/* vim: set ts=8 sts=4 et sw=4 tw=80: */
     1.6 +/* This Source Code Form is subject to the terms of the Mozilla Public
     1.7 + * License, v. 2.0. If a copy of the MPL was not distributed with this
     1.8 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
     1.9 +
    1.10 +#include "mozilla/ArrayUtils.h"
    1.11 +#include "mozilla/Attributes.h"
    1.12 +#include "mozilla/Assertions.h"
    1.13 +#include "mozilla/DebugOnly.h"
    1.14 +
    1.15 +#include "necko-config.h"
    1.16 +
    1.17 +#include "nsCache.h"
    1.18 +#include "nsCacheService.h"
    1.19 +#include "nsCacheRequest.h"
    1.20 +#include "nsCacheEntry.h"
    1.21 +#include "nsCacheEntryDescriptor.h"
    1.22 +#include "nsCacheDevice.h"
    1.23 +#include "nsMemoryCacheDevice.h"
    1.24 +#include "nsICacheVisitor.h"
    1.25 +#include "nsDiskCacheDevice.h"
    1.26 +#include "nsDiskCacheDeviceSQL.h"
    1.27 +#include "nsCacheUtils.h"
    1.28 +#include "../cache2/CacheObserver.h"
    1.29 +
    1.30 +#include "nsIObserverService.h"
    1.31 +#include "nsIPrefService.h"
    1.32 +#include "nsIPrefBranch.h"
    1.33 +#include "nsIFile.h"
    1.34 +#include "nsIOService.h"
    1.35 +#include "nsDirectoryServiceDefs.h"
    1.36 +#include "nsAppDirectoryServiceDefs.h"
    1.37 +#include "nsThreadUtils.h"
    1.38 +#include "nsProxyRelease.h"
    1.39 +#include "nsVoidArray.h"
    1.40 +#include "nsDeleteDir.h"
    1.41 +#include "nsNetCID.h"
    1.42 +#include <math.h>  // for log()
    1.43 +#include "mozilla/Services.h"
    1.44 +#include "nsITimer.h"
    1.45 +#include "mozIStorageService.h"
    1.46 +
    1.47 +#include "mozilla/net/NeckoCommon.h"
    1.48 +#include "mozilla/VisualEventTracer.h"
    1.49 +#include <algorithm>
    1.50 +
    1.51 +using namespace mozilla;
    1.52 +
    1.53 +/******************************************************************************
    1.54 + * nsCacheProfilePrefObserver
    1.55 + *****************************************************************************/
    1.56 +#define DISK_CACHE_ENABLE_PREF      "browser.cache.disk.enable"
    1.57 +#define DISK_CACHE_DIR_PREF         "browser.cache.disk.parent_directory"
    1.58 +#define DISK_CACHE_SMART_SIZE_FIRST_RUN_PREF\
    1.59 +    "browser.cache.disk.smart_size.first_run"
    1.60 +#define DISK_CACHE_SMART_SIZE_ENABLED_PREF \
    1.61 +    "browser.cache.disk.smart_size.enabled"
    1.62 +#define DISK_CACHE_SMART_SIZE_PREF "browser.cache.disk.smart_size_cached_value"
    1.63 +#define DISK_CACHE_CAPACITY_PREF    "browser.cache.disk.capacity"
    1.64 +#define DISK_CACHE_MAX_ENTRY_SIZE_PREF "browser.cache.disk.max_entry_size"
    1.65 +#define DISK_CACHE_CAPACITY         256000
    1.66 +
    1.67 +#define DISK_CACHE_USE_OLD_MAX_SMART_SIZE_PREF \
    1.68 +    "browser.cache.disk.smart_size.use_old_max"
    1.69 +
    1.70 +#define OFFLINE_CACHE_ENABLE_PREF   "browser.cache.offline.enable"
    1.71 +#define OFFLINE_CACHE_DIR_PREF      "browser.cache.offline.parent_directory"
    1.72 +#define OFFLINE_CACHE_CAPACITY_PREF "browser.cache.offline.capacity"
    1.73 +#define OFFLINE_CACHE_CAPACITY      512000
    1.74 +
    1.75 +#define MEMORY_CACHE_ENABLE_PREF    "browser.cache.memory.enable"
    1.76 +#define MEMORY_CACHE_CAPACITY_PREF  "browser.cache.memory.capacity"
    1.77 +#define MEMORY_CACHE_MAX_ENTRY_SIZE_PREF "browser.cache.memory.max_entry_size"
    1.78 +
    1.79 +#define CACHE_COMPRESSION_LEVEL_PREF "browser.cache.compression_level"
    1.80 +#define CACHE_COMPRESSION_LEVEL     1
    1.81 +
    1.82 +#define SANITIZE_ON_SHUTDOWN_PREF   "privacy.sanitize.sanitizeOnShutdown"
    1.83 +#define CLEAR_ON_SHUTDOWN_PREF      "privacy.clearOnShutdown.cache"
    1.84 +
    1.85 +static const char * observerList[] = { 
    1.86 +    "profile-before-change",
    1.87 +    "profile-do-change",
    1.88 +    NS_XPCOM_SHUTDOWN_OBSERVER_ID,
    1.89 +    "last-pb-context-exited",
    1.90 +    "suspend_process_notification",
    1.91 +    "resume_process_notification"
    1.92 +};
    1.93 +
    1.94 +static const char * prefList[] = { 
    1.95 +    DISK_CACHE_ENABLE_PREF,
    1.96 +    DISK_CACHE_SMART_SIZE_ENABLED_PREF,
    1.97 +    DISK_CACHE_CAPACITY_PREF,
    1.98 +    DISK_CACHE_DIR_PREF,
    1.99 +    DISK_CACHE_MAX_ENTRY_SIZE_PREF,
   1.100 +    DISK_CACHE_USE_OLD_MAX_SMART_SIZE_PREF,
   1.101 +    OFFLINE_CACHE_ENABLE_PREF,
   1.102 +    OFFLINE_CACHE_CAPACITY_PREF,
   1.103 +    OFFLINE_CACHE_DIR_PREF,
   1.104 +    MEMORY_CACHE_ENABLE_PREF,
   1.105 +    MEMORY_CACHE_CAPACITY_PREF,
   1.106 +    MEMORY_CACHE_MAX_ENTRY_SIZE_PREF,
   1.107 +    CACHE_COMPRESSION_LEVEL_PREF,
   1.108 +    SANITIZE_ON_SHUTDOWN_PREF,
   1.109 +    CLEAR_ON_SHUTDOWN_PREF
   1.110 +};
   1.111 +
   1.112 +// Cache sizes, in KB
   1.113 +const int32_t DEFAULT_CACHE_SIZE = 250 * 1024;  // 250 MB
   1.114 +#ifdef ANDROID
   1.115 +const int32_t MAX_CACHE_SIZE = 200 * 1024;      // 200 MB
   1.116 +const int32_t OLD_MAX_CACHE_SIZE = 200 * 1024;  // 200 MB
   1.117 +#else
   1.118 +const int32_t MAX_CACHE_SIZE = 350 * 1024;      // 350 MB
   1.119 +const int32_t OLD_MAX_CACHE_SIZE = 1024 * 1024; //   1 GB
   1.120 +#endif
   1.121 +// Default cache size was 50 MB for many years until FF 4:
   1.122 +const int32_t PRE_GECKO_2_0_DEFAULT_CACHE_SIZE = 50 * 1024;
   1.123 +
   1.124 +class nsCacheProfilePrefObserver : public nsIObserver
   1.125 +{
   1.126 +public:
   1.127 +    NS_DECL_THREADSAFE_ISUPPORTS
   1.128 +    NS_DECL_NSIOBSERVER
   1.129 +
   1.130 +    nsCacheProfilePrefObserver()
   1.131 +        : mHaveProfile(false)
   1.132 +        , mDiskCacheEnabled(false)
   1.133 +        , mDiskCacheCapacity(0)
   1.134 +        , mDiskCacheMaxEntrySize(-1) // -1 means "no limit"
   1.135 +        , mSmartSizeEnabled(false)
   1.136 +        , mShouldUseOldMaxSmartSize(false)
   1.137 +        , mOfflineCacheEnabled(false)
   1.138 +        , mOfflineCacheCapacity(0)
   1.139 +        , mMemoryCacheEnabled(true)
   1.140 +        , mMemoryCacheCapacity(-1)
   1.141 +        , mMemoryCacheMaxEntrySize(-1) // -1 means "no limit"
   1.142 +        , mCacheCompressionLevel(CACHE_COMPRESSION_LEVEL)
   1.143 +        , mSanitizeOnShutdown(false)
   1.144 +        , mClearCacheOnShutdown(false)
   1.145 +    {
   1.146 +    }
   1.147 +
   1.148 +    virtual ~nsCacheProfilePrefObserver() {}
   1.149 +    
   1.150 +    nsresult        Install();
   1.151 +    void            Remove();
   1.152 +    nsresult        ReadPrefs(nsIPrefBranch* branch);
   1.153 +    
   1.154 +    bool            DiskCacheEnabled();
   1.155 +    int32_t         DiskCacheCapacity()         { return mDiskCacheCapacity; }
   1.156 +    void            SetDiskCacheCapacity(int32_t);
   1.157 +    int32_t         DiskCacheMaxEntrySize()     { return mDiskCacheMaxEntrySize; }
   1.158 +    nsIFile *       DiskCacheParentDirectory()  { return mDiskCacheParentDirectory; }
   1.159 +    bool            SmartSizeEnabled()          { return mSmartSizeEnabled; }
   1.160 +
   1.161 +    bool            ShouldUseOldMaxSmartSize()        { return mShouldUseOldMaxSmartSize; }
   1.162 +    void            SetUseNewMaxSmartSize(bool useNew)     { mShouldUseOldMaxSmartSize = !useNew; }
   1.163 +
   1.164 +    bool            OfflineCacheEnabled();
   1.165 +    int32_t         OfflineCacheCapacity()         { return mOfflineCacheCapacity; }
   1.166 +    nsIFile *       OfflineCacheParentDirectory()  { return mOfflineCacheParentDirectory; }
   1.167 +    
   1.168 +    bool            MemoryCacheEnabled();
   1.169 +    int32_t         MemoryCacheCapacity();
   1.170 +    int32_t         MemoryCacheMaxEntrySize()     { return mMemoryCacheMaxEntrySize; }
   1.171 +
   1.172 +    int32_t         CacheCompressionLevel();
   1.173 +
   1.174 +    bool            SanitizeAtShutdown() { return mSanitizeOnShutdown && mClearCacheOnShutdown; }
   1.175 +
   1.176 +    static uint32_t GetSmartCacheSize(const nsAString& cachePath,
   1.177 +                                      uint32_t currentSize,
   1.178 +                                      bool shouldUseOldMaxSmartSize);
   1.179 +
   1.180 +    bool                    PermittedToSmartSize(nsIPrefBranch*, bool firstRun);
   1.181 +
   1.182 +private:
   1.183 +    bool                    mHaveProfile;
   1.184 +    
   1.185 +    bool                    mDiskCacheEnabled;
   1.186 +    int32_t                 mDiskCacheCapacity; // in kilobytes
   1.187 +    int32_t                 mDiskCacheMaxEntrySize; // in kilobytes
   1.188 +    nsCOMPtr<nsIFile>       mDiskCacheParentDirectory;
   1.189 +    bool                    mSmartSizeEnabled;
   1.190 +
   1.191 +    bool                    mShouldUseOldMaxSmartSize;
   1.192 +
   1.193 +    bool                    mOfflineCacheEnabled;
   1.194 +    int32_t                 mOfflineCacheCapacity; // in kilobytes
   1.195 +    nsCOMPtr<nsIFile>       mOfflineCacheParentDirectory;
   1.196 +    
   1.197 +    bool                    mMemoryCacheEnabled;
   1.198 +    int32_t                 mMemoryCacheCapacity; // in kilobytes
   1.199 +    int32_t                 mMemoryCacheMaxEntrySize; // in kilobytes
   1.200 +
   1.201 +    int32_t                 mCacheCompressionLevel;
   1.202 +
   1.203 +    bool                    mSanitizeOnShutdown;
   1.204 +    bool                    mClearCacheOnShutdown;
   1.205 +};
   1.206 +
   1.207 +NS_IMPL_ISUPPORTS(nsCacheProfilePrefObserver, nsIObserver)
   1.208 +
   1.209 +class nsSetDiskSmartSizeCallback MOZ_FINAL : public nsITimerCallback
   1.210 +{
   1.211 +public:
   1.212 +    NS_DECL_THREADSAFE_ISUPPORTS
   1.213 +
   1.214 +    NS_IMETHOD Notify(nsITimer* aTimer) {
   1.215 +        if (nsCacheService::gService) {
   1.216 +            nsCacheServiceAutoLock autoLock(LOCK_TELEM(NSSETDISKSMARTSIZECALLBACK_NOTIFY));
   1.217 +            nsCacheService::gService->SetDiskSmartSize_Locked();
   1.218 +            nsCacheService::gService->mSmartSizeTimer = nullptr;
   1.219 +        }
   1.220 +        return NS_OK;
   1.221 +    }
   1.222 +};
   1.223 +
   1.224 +NS_IMPL_ISUPPORTS(nsSetDiskSmartSizeCallback, nsITimerCallback)
   1.225 +
   1.226 +// Runnable sent to main thread after the cache IO thread calculates available
   1.227 +// disk space, so that there is no race in setting mDiskCacheCapacity.
   1.228 +class nsSetSmartSizeEvent: public nsRunnable 
   1.229 +{
   1.230 +public:
   1.231 +    nsSetSmartSizeEvent(int32_t smartSize)
   1.232 +        : mSmartSize(smartSize) {}
   1.233 +
   1.234 +    NS_IMETHOD Run() 
   1.235 +    {
   1.236 +        NS_ASSERTION(NS_IsMainThread(), 
   1.237 +                     "Setting smart size data off the main thread");
   1.238 +
   1.239 +        // Main thread may have already called nsCacheService::Shutdown
   1.240 +        if (!nsCacheService::IsInitialized())
   1.241 +            return NS_ERROR_NOT_AVAILABLE;
   1.242 +
   1.243 +        // Ensure smart sizing wasn't switched off while event was pending.
   1.244 +        // It is safe to access the observer without the lock since we are
   1.245 +        // on the main thread and the value changes only on the main thread.
   1.246 +        if (!nsCacheService::gService->mObserver->SmartSizeEnabled())
   1.247 +            return NS_OK;
   1.248 +
   1.249 +        nsCacheService::SetDiskCacheCapacity(mSmartSize);
   1.250 +
   1.251 +        nsCOMPtr<nsIPrefBranch> ps = do_GetService(NS_PREFSERVICE_CONTRACTID);
   1.252 +        if (!ps ||
   1.253 +            NS_FAILED(ps->SetIntPref(DISK_CACHE_SMART_SIZE_PREF, mSmartSize)))
   1.254 +            NS_WARNING("Failed to set smart size pref");
   1.255 +
   1.256 +        return NS_OK;
   1.257 +    }
   1.258 +
   1.259 +private:
   1.260 +    int32_t mSmartSize;
   1.261 +};
   1.262 +
   1.263 +
   1.264 +// Runnable sent from main thread to cacheIO thread
   1.265 +class nsGetSmartSizeEvent: public nsRunnable
   1.266 +{
   1.267 +public:
   1.268 +    nsGetSmartSizeEvent(const nsAString& cachePath, uint32_t currentSize,
   1.269 +                        bool shouldUseOldMaxSmartSize)
   1.270 +      : mCachePath(cachePath)
   1.271 +      , mCurrentSize(currentSize)
   1.272 +      , mShouldUseOldMaxSmartSize(shouldUseOldMaxSmartSize)
   1.273 +    {}
   1.274 +   
   1.275 +    // Calculates user's disk space available on a background thread and
   1.276 +    // dispatches this value back to the main thread.
   1.277 +    NS_IMETHOD Run()
   1.278 +    {
   1.279 +        uint32_t size;
   1.280 +        size = nsCacheProfilePrefObserver::GetSmartCacheSize(mCachePath,
   1.281 +                                                             mCurrentSize,
   1.282 +                                                             mShouldUseOldMaxSmartSize);
   1.283 +        NS_DispatchToMainThread(new nsSetSmartSizeEvent(size));
   1.284 +        return NS_OK;
   1.285 +    }
   1.286 +
   1.287 +private:
   1.288 +    nsString mCachePath;
   1.289 +    uint32_t mCurrentSize;
   1.290 +    bool     mShouldUseOldMaxSmartSize;
   1.291 +};
   1.292 +
   1.293 +class nsBlockOnCacheThreadEvent : public nsRunnable {
   1.294 +public:
   1.295 +    nsBlockOnCacheThreadEvent()
   1.296 +    {
   1.297 +    }
   1.298 +    NS_IMETHOD Run()
   1.299 +    {
   1.300 +        nsCacheServiceAutoLock autoLock(LOCK_TELEM(NSBLOCKONCACHETHREADEVENT_RUN));
   1.301 +#ifdef PR_LOGGING
   1.302 +        CACHE_LOG_DEBUG(("nsBlockOnCacheThreadEvent [%p]\n", this));
   1.303 +#endif
   1.304 +        nsCacheService::gService->mCondVar.Notify();
   1.305 +        return NS_OK;
   1.306 +    }
   1.307 +};
   1.308 +
   1.309 +
   1.310 +nsresult
   1.311 +nsCacheProfilePrefObserver::Install()
   1.312 +{
   1.313 +    // install profile-change observer
   1.314 +    nsCOMPtr<nsIObserverService> observerService =
   1.315 +        mozilla::services::GetObserverService();
   1.316 +    if (!observerService)
   1.317 +        return NS_ERROR_FAILURE;
   1.318 +    
   1.319 +    nsresult rv, rv2 = NS_OK;
   1.320 +    for (unsigned int i=0; i<ArrayLength(observerList); i++) {
   1.321 +        rv = observerService->AddObserver(this, observerList[i], false);
   1.322 +        if (NS_FAILED(rv)) 
   1.323 +            rv2 = rv;
   1.324 +    }
   1.325 +    
   1.326 +    // install preferences observer
   1.327 +    nsCOMPtr<nsIPrefBranch> branch = do_GetService(NS_PREFSERVICE_CONTRACTID);
   1.328 +    if (!branch) return NS_ERROR_FAILURE;
   1.329 +
   1.330 +    for (unsigned int i=0; i<ArrayLength(prefList); i++) {
   1.331 +        rv = branch->AddObserver(prefList[i], this, false);
   1.332 +        if (NS_FAILED(rv))
   1.333 +            rv2 = rv;
   1.334 +    }
   1.335 +
   1.336 +    // Determine if we have a profile already
   1.337 +    //     Install() is called *after* the profile-after-change notification
   1.338 +    //     when there is only a single profile, or it is specified on the
   1.339 +    //     commandline at startup.
   1.340 +    //     In that case, we detect the presence of a profile by the existence
   1.341 +    //     of the NS_APP_USER_PROFILE_50_DIR directory.
   1.342 +
   1.343 +    nsCOMPtr<nsIFile> directory;
   1.344 +    rv = NS_GetSpecialDirectory(NS_APP_USER_PROFILE_50_DIR,
   1.345 +                                getter_AddRefs(directory));
   1.346 +    if (NS_SUCCEEDED(rv))
   1.347 +        mHaveProfile = true;
   1.348 +
   1.349 +    rv = ReadPrefs(branch);
   1.350 +    NS_ENSURE_SUCCESS(rv, rv);
   1.351 +
   1.352 +    return rv2;
   1.353 +}
   1.354 +
   1.355 +
   1.356 +void
   1.357 +nsCacheProfilePrefObserver::Remove()
   1.358 +{
   1.359 +    // remove Observer Service observers
   1.360 +    nsCOMPtr<nsIObserverService> obs =
   1.361 +        mozilla::services::GetObserverService();
   1.362 +    if (obs) {
   1.363 +        for (unsigned int i=0; i<ArrayLength(observerList); i++) {
   1.364 +            obs->RemoveObserver(this, observerList[i]);
   1.365 +        }
   1.366 +    }
   1.367 +
   1.368 +    // remove Pref Service observers
   1.369 +    nsCOMPtr<nsIPrefBranch> prefs =
   1.370 +        do_GetService(NS_PREFSERVICE_CONTRACTID);
   1.371 +    if (!prefs)
   1.372 +        return;
   1.373 +    for (unsigned int i=0; i<ArrayLength(prefList); i++)
   1.374 +        prefs->RemoveObserver(prefList[i], this); // remove cache pref observers
   1.375 +}
   1.376 +
   1.377 +void
   1.378 +nsCacheProfilePrefObserver::SetDiskCacheCapacity(int32_t capacity)
   1.379 +{
   1.380 +    mDiskCacheCapacity = std::max(0, capacity);
   1.381 +}
   1.382 +
   1.383 +
   1.384 +NS_IMETHODIMP
   1.385 +nsCacheProfilePrefObserver::Observe(nsISupports *     subject,
   1.386 +                                    const char *      topic,
   1.387 +                                    const char16_t * data_unicode)
   1.388 +{
   1.389 +    nsresult rv;
   1.390 +    NS_ConvertUTF16toUTF8 data(data_unicode);
   1.391 +    CACHE_LOG_ALWAYS(("Observe [topic=%s data=%s]\n", topic, data.get()));
   1.392 +
   1.393 +    if (!nsCacheService::IsInitialized()) {
   1.394 +        if (!strcmp("resume_process_notification", topic)) {
   1.395 +            // A suspended process has a closed cache, so re-open it here.
   1.396 +            nsCacheService::GlobalInstance()->Init();
   1.397 +        }
   1.398 +        return NS_OK;
   1.399 +    }
   1.400 +
   1.401 +    if (!strcmp(NS_XPCOM_SHUTDOWN_OBSERVER_ID, topic)) {
   1.402 +        // xpcom going away, shutdown cache service
   1.403 +        nsCacheService::GlobalInstance()->Shutdown();
   1.404 +    } else if (!strcmp("profile-before-change", topic)) {
   1.405 +        // profile before change
   1.406 +        mHaveProfile = false;
   1.407 +
   1.408 +        // XXX shutdown devices
   1.409 +        nsCacheService::OnProfileShutdown(!strcmp("shutdown-cleanse",
   1.410 +                                                  data.get()));
   1.411 +        
   1.412 +    } else if (!strcmp("suspend_process_notification", topic)) {
   1.413 +        // A suspended process may never return, so shutdown the cache to reduce
   1.414 +        // cache corruption.
   1.415 +        nsCacheService::GlobalInstance()->Shutdown();
   1.416 +    } else if (!strcmp("profile-do-change", topic)) {
   1.417 +        // profile after change
   1.418 +        mHaveProfile = true;
   1.419 +        nsCOMPtr<nsIPrefBranch> branch = do_GetService(NS_PREFSERVICE_CONTRACTID);
   1.420 +        ReadPrefs(branch);
   1.421 +        nsCacheService::OnProfileChanged();
   1.422 +    
   1.423 +    } else if (!strcmp(NS_PREFBRANCH_PREFCHANGE_TOPIC_ID, topic)) {
   1.424 +
   1.425 +        // ignore pref changes until we're done switch profiles
   1.426 +        if (!mHaveProfile)  
   1.427 +            return NS_OK;
   1.428 +
   1.429 +        nsCOMPtr<nsIPrefBranch> branch = do_QueryInterface(subject, &rv);
   1.430 +        if (NS_FAILED(rv))  
   1.431 +            return rv;
   1.432 +
   1.433 +        // which preference changed?
   1.434 +        if (!strcmp(DISK_CACHE_ENABLE_PREF, data.get())) {
   1.435 +
   1.436 +            rv = branch->GetBoolPref(DISK_CACHE_ENABLE_PREF,
   1.437 +                                     &mDiskCacheEnabled);
   1.438 +            if (NS_FAILED(rv))  
   1.439 +                return rv;
   1.440 +            nsCacheService::SetDiskCacheEnabled(DiskCacheEnabled());
   1.441 +
   1.442 +        } else if (!strcmp(DISK_CACHE_CAPACITY_PREF, data.get())) {
   1.443 +
   1.444 +            int32_t capacity = 0;
   1.445 +            rv = branch->GetIntPref(DISK_CACHE_CAPACITY_PREF, &capacity);
   1.446 +            if (NS_FAILED(rv))  
   1.447 +                return rv;
   1.448 +            mDiskCacheCapacity = std::max(0, capacity);
   1.449 +            nsCacheService::SetDiskCacheCapacity(mDiskCacheCapacity);
   1.450 +       
   1.451 +        // Update the cache capacity when smart sizing is turned on/off 
   1.452 +        } else if (!strcmp(DISK_CACHE_SMART_SIZE_ENABLED_PREF, data.get())) {
   1.453 +            // Is the update because smartsizing was turned on, or off?
   1.454 +            rv = branch->GetBoolPref(DISK_CACHE_SMART_SIZE_ENABLED_PREF,
   1.455 +                                     &mSmartSizeEnabled);
   1.456 +            if (NS_FAILED(rv)) 
   1.457 +                return rv;
   1.458 +            int32_t newCapacity = 0;
   1.459 +            if (mSmartSizeEnabled) {
   1.460 +                nsCacheService::SetDiskSmartSize();
   1.461 +            } else {
   1.462 +                // Smart sizing switched off: use user specified size
   1.463 +                rv = branch->GetIntPref(DISK_CACHE_CAPACITY_PREF, &newCapacity);
   1.464 +                if (NS_FAILED(rv)) 
   1.465 +                    return rv;
   1.466 +                mDiskCacheCapacity = std::max(0, newCapacity);
   1.467 +                nsCacheService::SetDiskCacheCapacity(mDiskCacheCapacity);
   1.468 +            }
   1.469 +        } else if (!strcmp(DISK_CACHE_USE_OLD_MAX_SMART_SIZE_PREF, data.get())) {
   1.470 +            rv = branch->GetBoolPref(DISK_CACHE_USE_OLD_MAX_SMART_SIZE_PREF,
   1.471 +                                     &mShouldUseOldMaxSmartSize);
   1.472 +            if (NS_FAILED(rv))
   1.473 +                return rv;
   1.474 +        } else if (!strcmp(DISK_CACHE_MAX_ENTRY_SIZE_PREF, data.get())) {
   1.475 +            int32_t newMaxSize;
   1.476 +            rv = branch->GetIntPref(DISK_CACHE_MAX_ENTRY_SIZE_PREF,
   1.477 +                                    &newMaxSize);
   1.478 +            if (NS_FAILED(rv)) 
   1.479 +                return rv;
   1.480 +
   1.481 +            mDiskCacheMaxEntrySize = std::max(-1, newMaxSize);
   1.482 +            nsCacheService::SetDiskCacheMaxEntrySize(mDiskCacheMaxEntrySize);
   1.483 +          
   1.484 +#if 0            
   1.485 +        } else if (!strcmp(DISK_CACHE_DIR_PREF, data.get())) {
   1.486 +            // XXX We probaby don't want to respond to this pref except after
   1.487 +            // XXX profile changes.  Ideally, there should be somekind of user
   1.488 +            // XXX notification that the pref change won't take effect until
   1.489 +            // XXX the next time the profile changes (browser launch)
   1.490 +#endif            
   1.491 +        } else
   1.492 +
   1.493 +        // which preference changed?
   1.494 +        if (!strcmp(OFFLINE_CACHE_ENABLE_PREF, data.get())) {
   1.495 +
   1.496 +            rv = branch->GetBoolPref(OFFLINE_CACHE_ENABLE_PREF,
   1.497 +                                     &mOfflineCacheEnabled);
   1.498 +            if (NS_FAILED(rv))  return rv;
   1.499 +            nsCacheService::SetOfflineCacheEnabled(OfflineCacheEnabled());
   1.500 +
   1.501 +        } else if (!strcmp(OFFLINE_CACHE_CAPACITY_PREF, data.get())) {
   1.502 +
   1.503 +            int32_t capacity = 0;
   1.504 +            rv = branch->GetIntPref(OFFLINE_CACHE_CAPACITY_PREF, &capacity);
   1.505 +            if (NS_FAILED(rv))  return rv;
   1.506 +            mOfflineCacheCapacity = std::max(0, capacity);
   1.507 +            nsCacheService::SetOfflineCacheCapacity(mOfflineCacheCapacity);
   1.508 +#if 0
   1.509 +        } else if (!strcmp(OFFLINE_CACHE_DIR_PREF, data.get())) {
   1.510 +            // XXX We probaby don't want to respond to this pref except after
   1.511 +            // XXX profile changes.  Ideally, there should be some kind of user
   1.512 +            // XXX notification that the pref change won't take effect until
   1.513 +            // XXX the next time the profile changes (browser launch)
   1.514 +#endif
   1.515 +        } else
   1.516 +
   1.517 +        if (!strcmp(MEMORY_CACHE_ENABLE_PREF, data.get())) {
   1.518 +
   1.519 +            rv = branch->GetBoolPref(MEMORY_CACHE_ENABLE_PREF,
   1.520 +                                     &mMemoryCacheEnabled);
   1.521 +            if (NS_FAILED(rv))  
   1.522 +                return rv;
   1.523 +            nsCacheService::SetMemoryCache();
   1.524 +            
   1.525 +        } else if (!strcmp(MEMORY_CACHE_CAPACITY_PREF, data.get())) {
   1.526 +
   1.527 +            mMemoryCacheCapacity = -1;
   1.528 +            (void) branch->GetIntPref(MEMORY_CACHE_CAPACITY_PREF,
   1.529 +                                      &mMemoryCacheCapacity);
   1.530 +            nsCacheService::SetMemoryCache();
   1.531 +        } else if (!strcmp(MEMORY_CACHE_MAX_ENTRY_SIZE_PREF, data.get())) {
   1.532 +            int32_t newMaxSize;
   1.533 +            rv = branch->GetIntPref(MEMORY_CACHE_MAX_ENTRY_SIZE_PREF,
   1.534 +                                     &newMaxSize);
   1.535 +            if (NS_FAILED(rv)) 
   1.536 +                return rv;
   1.537 +            
   1.538 +            mMemoryCacheMaxEntrySize = std::max(-1, newMaxSize);
   1.539 +            nsCacheService::SetMemoryCacheMaxEntrySize(mMemoryCacheMaxEntrySize);
   1.540 +        } else if (!strcmp(CACHE_COMPRESSION_LEVEL_PREF, data.get())) {
   1.541 +            mCacheCompressionLevel = CACHE_COMPRESSION_LEVEL;
   1.542 +            (void)branch->GetIntPref(CACHE_COMPRESSION_LEVEL_PREF,
   1.543 +                                     &mCacheCompressionLevel);
   1.544 +            mCacheCompressionLevel = std::max(0, mCacheCompressionLevel);
   1.545 +            mCacheCompressionLevel = std::min(9, mCacheCompressionLevel);
   1.546 +        } else if (!strcmp(SANITIZE_ON_SHUTDOWN_PREF, data.get())) {
   1.547 +            rv = branch->GetBoolPref(SANITIZE_ON_SHUTDOWN_PREF,
   1.548 +                                     &mSanitizeOnShutdown);
   1.549 +            if (NS_FAILED(rv))
   1.550 +                return rv;
   1.551 +            nsCacheService::SetDiskCacheEnabled(DiskCacheEnabled());
   1.552 +        } else if (!strcmp(CLEAR_ON_SHUTDOWN_PREF, data.get())) {
   1.553 +            rv = branch->GetBoolPref(CLEAR_ON_SHUTDOWN_PREF,
   1.554 +                                     &mClearCacheOnShutdown);
   1.555 +            if (NS_FAILED(rv))
   1.556 +                return rv;
   1.557 +            nsCacheService::SetDiskCacheEnabled(DiskCacheEnabled());
   1.558 +        }
   1.559 +    } else if (!strcmp("last-pb-context-exited", topic)) {
   1.560 +        nsCacheService::LeavePrivateBrowsing();
   1.561 +    }
   1.562 +
   1.563 +    return NS_OK;
   1.564 +}
   1.565 +
   1.566 +// Returns default ("smart") size (in KB) of cache, given available disk space
   1.567 +// (also in KB)
   1.568 +static uint32_t
   1.569 +SmartCacheSize(const uint32_t availKB, bool shouldUseOldMaxSmartSize)
   1.570 +{
   1.571 +    uint32_t maxSize = shouldUseOldMaxSmartSize ? OLD_MAX_CACHE_SIZE : MAX_CACHE_SIZE;
   1.572 +
   1.573 +    if (availKB > 100 * 1024 * 1024)
   1.574 +        return maxSize;  // skip computing if we're over 100 GB
   1.575 +
   1.576 +    // Grow/shrink in 10 MB units, deliberately, so that in the common case we
   1.577 +    // don't shrink cache and evict items every time we startup (it's important
   1.578 +    // that we don't slow down startup benchmarks).
   1.579 +    uint32_t sz10MBs = 0;
   1.580 +    uint32_t avail10MBs = availKB / (1024*10);
   1.581 +
   1.582 +    // .5% of space above 25 GB
   1.583 +    if (avail10MBs > 2500) {
   1.584 +        sz10MBs += static_cast<uint32_t>((avail10MBs - 2500)*.005);
   1.585 +        avail10MBs = 2500;
   1.586 +    }
   1.587 +    // 1% of space between 7GB -> 25 GB
   1.588 +    if (avail10MBs > 700) {
   1.589 +        sz10MBs += static_cast<uint32_t>((avail10MBs - 700)*.01);
   1.590 +        avail10MBs = 700;
   1.591 +    }
   1.592 +    // 5% of space between 500 MB -> 7 GB
   1.593 +    if (avail10MBs > 50) {
   1.594 +        sz10MBs += static_cast<uint32_t>((avail10MBs - 50)*.05);
   1.595 +        avail10MBs = 50;
   1.596 +    }
   1.597 +
   1.598 +#ifdef ANDROID
   1.599 +    // On Android, smaller/older devices may have very little storage and
   1.600 +    // device owners may be sensitive to storage footprint: Use a smaller
   1.601 +    // percentage of available space and a smaller minimum.
   1.602 +
   1.603 +    // 20% of space up to 500 MB (10 MB min)
   1.604 +    sz10MBs += std::max<uint32_t>(1, static_cast<uint32_t>(avail10MBs * .2));
   1.605 +#else
   1.606 +    // 40% of space up to 500 MB (50 MB min)
   1.607 +    sz10MBs += std::max<uint32_t>(5, static_cast<uint32_t>(avail10MBs * .4));
   1.608 +#endif
   1.609 +
   1.610 +    return std::min<uint32_t>(maxSize, sz10MBs * 10 * 1024);
   1.611 +}
   1.612 +
   1.613 + /* Computes our best guess for the default size of the user's disk cache, 
   1.614 +  * based on the amount of space they have free on their hard drive. 
   1.615 +  * We use a tiered scheme: the more space available, 
   1.616 +  * the larger the disk cache will be. However, we do not want
   1.617 +  * to enable the disk cache to grow to an unbounded size, so the larger the
   1.618 +  * user's available space is, the smaller of a percentage we take. We set a
   1.619 +  * lower bound of 50MB and an upper bound of 1GB.  
   1.620 +  *
   1.621 +  *@param:  None.
   1.622 +  *@return: The size that the user's disk cache should default to, in kBytes.
   1.623 +  */
   1.624 +uint32_t
   1.625 +nsCacheProfilePrefObserver::GetSmartCacheSize(const nsAString& cachePath,
   1.626 +                                              uint32_t currentSize,
   1.627 +                                              bool shouldUseOldMaxSmartSize)
   1.628 +{
   1.629 +    // Check for free space on device where cache directory lives
   1.630 +    nsresult rv;
   1.631 +    nsCOMPtr<nsIFile> 
   1.632 +        cacheDirectory (do_CreateInstance(NS_LOCAL_FILE_CONTRACTID, &rv));
   1.633 +    if (NS_FAILED(rv) || !cacheDirectory)
   1.634 +        return DEFAULT_CACHE_SIZE;
   1.635 +    rv = cacheDirectory->InitWithPath(cachePath);
   1.636 +    if (NS_FAILED(rv))
   1.637 +        return DEFAULT_CACHE_SIZE;
   1.638 +    int64_t bytesAvailable;
   1.639 +    rv = cacheDirectory->GetDiskSpaceAvailable(&bytesAvailable);
   1.640 +    if (NS_FAILED(rv))
   1.641 +        return DEFAULT_CACHE_SIZE;
   1.642 +
   1.643 +    return SmartCacheSize(static_cast<uint32_t>((bytesAvailable / 1024) +
   1.644 +                                                currentSize),
   1.645 +                          shouldUseOldMaxSmartSize);
   1.646 +}
   1.647 +
   1.648 +/* Determine if we are permitted to dynamically size the user's disk cache based
   1.649 + * on their disk space available. We may do this so long as the pref 
   1.650 + * smart_size.enabled is true.
   1.651 + */
   1.652 +bool
   1.653 +nsCacheProfilePrefObserver::PermittedToSmartSize(nsIPrefBranch* branch, bool
   1.654 +                                                 firstRun)
   1.655 +{
   1.656 +    nsresult rv;
   1.657 +    if (firstRun) {
   1.658 +        // check if user has set cache size in the past
   1.659 +        bool userSet;
   1.660 +        rv = branch->PrefHasUserValue(DISK_CACHE_CAPACITY_PREF, &userSet);
   1.661 +        if (NS_FAILED(rv)) userSet = true;
   1.662 +        if (userSet) {
   1.663 +            int32_t oldCapacity;
   1.664 +            // If user explicitly set cache size to be smaller than old default
   1.665 +            // of 50 MB, then keep user's value. Otherwise use smart sizing.
   1.666 +            rv = branch->GetIntPref(DISK_CACHE_CAPACITY_PREF, &oldCapacity);
   1.667 +            if (oldCapacity < PRE_GECKO_2_0_DEFAULT_CACHE_SIZE) {
   1.668 +                mSmartSizeEnabled = false;
   1.669 +                branch->SetBoolPref(DISK_CACHE_SMART_SIZE_ENABLED_PREF,
   1.670 +                                    mSmartSizeEnabled);
   1.671 +                return mSmartSizeEnabled;
   1.672 +            }
   1.673 +        }
   1.674 +        // Set manual setting to MAX cache size as starting val for any
   1.675 +        // adjustment by user: (bug 559942 comment 65)
   1.676 +        int32_t maxSize = mShouldUseOldMaxSmartSize ? OLD_MAX_CACHE_SIZE : MAX_CACHE_SIZE;
   1.677 +        branch->SetIntPref(DISK_CACHE_CAPACITY_PREF, maxSize);
   1.678 +    }
   1.679 +
   1.680 +    rv = branch->GetBoolPref(DISK_CACHE_SMART_SIZE_ENABLED_PREF,
   1.681 +                             &mSmartSizeEnabled);
   1.682 +    if (NS_FAILED(rv))
   1.683 +        mSmartSizeEnabled = false;
   1.684 +    return mSmartSizeEnabled;
   1.685 +}
   1.686 +
   1.687 +
   1.688 +nsresult
   1.689 +nsCacheProfilePrefObserver::ReadPrefs(nsIPrefBranch* branch)
   1.690 +{
   1.691 +    nsresult rv = NS_OK;
   1.692 +
   1.693 +    // read disk cache device prefs
   1.694 +    mDiskCacheEnabled = true;  // presume disk cache is enabled
   1.695 +    (void) branch->GetBoolPref(DISK_CACHE_ENABLE_PREF, &mDiskCacheEnabled);
   1.696 +
   1.697 +    mDiskCacheCapacity = DISK_CACHE_CAPACITY;
   1.698 +    (void)branch->GetIntPref(DISK_CACHE_CAPACITY_PREF, &mDiskCacheCapacity);
   1.699 +    mDiskCacheCapacity = std::max(0, mDiskCacheCapacity);
   1.700 +
   1.701 +    (void) branch->GetIntPref(DISK_CACHE_MAX_ENTRY_SIZE_PREF,
   1.702 +                              &mDiskCacheMaxEntrySize);
   1.703 +    mDiskCacheMaxEntrySize = std::max(-1, mDiskCacheMaxEntrySize);
   1.704 +    
   1.705 +    (void) branch->GetComplexValue(DISK_CACHE_DIR_PREF,     // ignore error
   1.706 +                                   NS_GET_IID(nsIFile),
   1.707 +                                   getter_AddRefs(mDiskCacheParentDirectory));
   1.708 +
   1.709 +    (void) branch->GetBoolPref(DISK_CACHE_USE_OLD_MAX_SMART_SIZE_PREF,
   1.710 +                               &mShouldUseOldMaxSmartSize);
   1.711 +    
   1.712 +    if (!mDiskCacheParentDirectory) {
   1.713 +        nsCOMPtr<nsIFile>  directory;
   1.714 +
   1.715 +        // try to get the disk cache parent directory
   1.716 +        rv = NS_GetSpecialDirectory(NS_APP_CACHE_PARENT_DIR,
   1.717 +                                    getter_AddRefs(directory));
   1.718 +        if (NS_FAILED(rv)) {
   1.719 +            // try to get the profile directory (there may not be a profile yet)
   1.720 +            nsCOMPtr<nsIFile> profDir;
   1.721 +            NS_GetSpecialDirectory(NS_APP_USER_PROFILE_50_DIR,
   1.722 +                                   getter_AddRefs(profDir));
   1.723 +            NS_GetSpecialDirectory(NS_APP_USER_PROFILE_LOCAL_50_DIR,
   1.724 +                                   getter_AddRefs(directory));
   1.725 +            if (!directory)
   1.726 +                directory = profDir;
   1.727 +            else if (profDir) {
   1.728 +                nsCacheService::MoveOrRemoveDiskCache(profDir, directory, 
   1.729 +                                                      "Cache");
   1.730 +            }
   1.731 +        }
   1.732 +        // use file cache in build tree only if asked, to avoid cache dir litter
   1.733 +        if (!directory && PR_GetEnv("NECKO_DEV_ENABLE_DISK_CACHE")) {
   1.734 +            rv = NS_GetSpecialDirectory(NS_XPCOM_CURRENT_PROCESS_DIR,
   1.735 +                                        getter_AddRefs(directory));
   1.736 +        }
   1.737 +        if (directory)
   1.738 +            mDiskCacheParentDirectory = do_QueryInterface(directory, &rv);
   1.739 +    }
   1.740 +    if (mDiskCacheParentDirectory) {
   1.741 +        bool firstSmartSizeRun;
   1.742 +        rv = branch->GetBoolPref(DISK_CACHE_SMART_SIZE_FIRST_RUN_PREF, 
   1.743 +                                 &firstSmartSizeRun); 
   1.744 +        if (NS_FAILED(rv)) 
   1.745 +            firstSmartSizeRun = false;
   1.746 +        if (PermittedToSmartSize(branch, firstSmartSizeRun)) {
   1.747 +            // Avoid evictions: use previous cache size until smart size event
   1.748 +            // updates mDiskCacheCapacity
   1.749 +            rv = branch->GetIntPref(firstSmartSizeRun ?
   1.750 +                                    DISK_CACHE_CAPACITY_PREF :
   1.751 +                                    DISK_CACHE_SMART_SIZE_PREF,
   1.752 +                                    &mDiskCacheCapacity);
   1.753 +            if (NS_FAILED(rv))
   1.754 +                mDiskCacheCapacity = DEFAULT_CACHE_SIZE;
   1.755 +        }
   1.756 +
   1.757 +        if (firstSmartSizeRun) {
   1.758 +            // It is no longer our first run
   1.759 +            rv = branch->SetBoolPref(DISK_CACHE_SMART_SIZE_FIRST_RUN_PREF, 
   1.760 +                                     false);
   1.761 +            if (NS_FAILED(rv)) 
   1.762 +                NS_WARNING("Failed setting first_run pref in ReadPrefs.");
   1.763 +        }
   1.764 +    }
   1.765 +
   1.766 +    // read offline cache device prefs
   1.767 +    mOfflineCacheEnabled = true;  // presume offline cache is enabled
   1.768 +    (void) branch->GetBoolPref(OFFLINE_CACHE_ENABLE_PREF,
   1.769 +                              &mOfflineCacheEnabled);
   1.770 +
   1.771 +    mOfflineCacheCapacity = OFFLINE_CACHE_CAPACITY;
   1.772 +    (void)branch->GetIntPref(OFFLINE_CACHE_CAPACITY_PREF,
   1.773 +                             &mOfflineCacheCapacity);
   1.774 +    mOfflineCacheCapacity = std::max(0, mOfflineCacheCapacity);
   1.775 +
   1.776 +    (void) branch->GetComplexValue(OFFLINE_CACHE_DIR_PREF,     // ignore error
   1.777 +                                   NS_GET_IID(nsIFile),
   1.778 +                                   getter_AddRefs(mOfflineCacheParentDirectory));
   1.779 +
   1.780 +    if (!mOfflineCacheParentDirectory) {
   1.781 +        nsCOMPtr<nsIFile>  directory;
   1.782 +
   1.783 +        // try to get the offline cache parent directory
   1.784 +        rv = NS_GetSpecialDirectory(NS_APP_CACHE_PARENT_DIR,
   1.785 +                                    getter_AddRefs(directory));
   1.786 +        if (NS_FAILED(rv)) {
   1.787 +            // try to get the profile directory (there may not be a profile yet)
   1.788 +            nsCOMPtr<nsIFile> profDir;
   1.789 +            NS_GetSpecialDirectory(NS_APP_USER_PROFILE_50_DIR,
   1.790 +                                   getter_AddRefs(profDir));
   1.791 +            NS_GetSpecialDirectory(NS_APP_USER_PROFILE_LOCAL_50_DIR,
   1.792 +                                   getter_AddRefs(directory));
   1.793 +            if (!directory)
   1.794 +                directory = profDir;
   1.795 +            else if (profDir) {
   1.796 +                nsCacheService::MoveOrRemoveDiskCache(profDir, directory, 
   1.797 +                                                      "OfflineCache");
   1.798 +            }
   1.799 +        }
   1.800 +#if DEBUG
   1.801 +        if (!directory) {
   1.802 +            // use current process directory during development
   1.803 +            rv = NS_GetSpecialDirectory(NS_XPCOM_CURRENT_PROCESS_DIR,
   1.804 +                                        getter_AddRefs(directory));
   1.805 +        }
   1.806 +#endif
   1.807 +        if (directory)
   1.808 +            mOfflineCacheParentDirectory = do_QueryInterface(directory, &rv);
   1.809 +    }
   1.810 +
   1.811 +    // read memory cache device prefs
   1.812 +    (void) branch->GetBoolPref(MEMORY_CACHE_ENABLE_PREF, &mMemoryCacheEnabled);
   1.813 +
   1.814 +    mMemoryCacheCapacity = -1;
   1.815 +    (void) branch->GetIntPref(MEMORY_CACHE_CAPACITY_PREF,
   1.816 +                              &mMemoryCacheCapacity);
   1.817 +
   1.818 +    (void) branch->GetIntPref(MEMORY_CACHE_MAX_ENTRY_SIZE_PREF,
   1.819 +                              &mMemoryCacheMaxEntrySize);
   1.820 +    mMemoryCacheMaxEntrySize = std::max(-1, mMemoryCacheMaxEntrySize);
   1.821 +
   1.822 +    // read cache compression level pref
   1.823 +    mCacheCompressionLevel = CACHE_COMPRESSION_LEVEL;
   1.824 +    (void)branch->GetIntPref(CACHE_COMPRESSION_LEVEL_PREF,
   1.825 +                             &mCacheCompressionLevel);
   1.826 +    mCacheCompressionLevel = std::max(0, mCacheCompressionLevel);
   1.827 +    mCacheCompressionLevel = std::min(9, mCacheCompressionLevel);
   1.828 +
   1.829 +    // read cache shutdown sanitization prefs
   1.830 +    (void) branch->GetBoolPref(SANITIZE_ON_SHUTDOWN_PREF,
   1.831 +                               &mSanitizeOnShutdown);
   1.832 +    (void) branch->GetBoolPref(CLEAR_ON_SHUTDOWN_PREF,
   1.833 +                               &mClearCacheOnShutdown);
   1.834 +
   1.835 +    return rv;
   1.836 +}
   1.837 +
   1.838 +nsresult
   1.839 +nsCacheService::DispatchToCacheIOThread(nsIRunnable* event)
   1.840 +{
   1.841 +    if (!gService->mCacheIOThread) return NS_ERROR_NOT_AVAILABLE;
   1.842 +    return gService->mCacheIOThread->Dispatch(event, NS_DISPATCH_NORMAL);
   1.843 +}
   1.844 +
   1.845 +nsresult
   1.846 +nsCacheService::SyncWithCacheIOThread()
   1.847 +{
   1.848 +    gService->mLock.AssertCurrentThreadOwns();
   1.849 +    if (!gService->mCacheIOThread) return NS_ERROR_NOT_AVAILABLE;
   1.850 +
   1.851 +    nsCOMPtr<nsIRunnable> event = new nsBlockOnCacheThreadEvent();
   1.852 +
   1.853 +    // dispatch event - it will notify the monitor when it's done
   1.854 +    nsresult rv =
   1.855 +        gService->mCacheIOThread->Dispatch(event, NS_DISPATCH_NORMAL);
   1.856 +    if (NS_FAILED(rv)) {
   1.857 +        NS_WARNING("Failed dispatching block-event");
   1.858 +        return NS_ERROR_UNEXPECTED;
   1.859 +    }
   1.860 +
   1.861 +    // wait until notified, then return
   1.862 +    rv = gService->mCondVar.Wait();
   1.863 +
   1.864 +    return rv;
   1.865 +}
   1.866 +
   1.867 +
   1.868 +bool
   1.869 +nsCacheProfilePrefObserver::DiskCacheEnabled()
   1.870 +{
   1.871 +    if ((mDiskCacheCapacity == 0) || (!mDiskCacheParentDirectory))  return false;
   1.872 +    return mDiskCacheEnabled && (!mSanitizeOnShutdown || !mClearCacheOnShutdown);
   1.873 +}
   1.874 +
   1.875 +
   1.876 +bool
   1.877 +nsCacheProfilePrefObserver::OfflineCacheEnabled()
   1.878 +{
   1.879 +    if ((mOfflineCacheCapacity == 0) || (!mOfflineCacheParentDirectory))
   1.880 +        return false;
   1.881 +
   1.882 +    return mOfflineCacheEnabled;
   1.883 +}
   1.884 +
   1.885 +
   1.886 +bool
   1.887 +nsCacheProfilePrefObserver::MemoryCacheEnabled()
   1.888 +{
   1.889 +    if (mMemoryCacheCapacity == 0)  return false;
   1.890 +    return mMemoryCacheEnabled;
   1.891 +}
   1.892 +
   1.893 +
   1.894 +/**
   1.895 + * MemoryCacheCapacity
   1.896 + *
   1.897 + * If the browser.cache.memory.capacity preference is positive, we use that
   1.898 + * value for the amount of memory available for the cache.
   1.899 + *
   1.900 + * If browser.cache.memory.capacity is zero, the memory cache is disabled.
   1.901 + * 
   1.902 + * If browser.cache.memory.capacity is negative or not present, we use a
   1.903 + * formula that grows less than linearly with the amount of system memory, 
   1.904 + * with an upper limit on the cache size. No matter how much physical RAM is
   1.905 + * present, the default cache size would not exceed 32 MB. This maximum would
   1.906 + * apply only to systems with more than 4 GB of RAM (e.g. terminal servers)
   1.907 + *
   1.908 + *   RAM   Cache
   1.909 + *   ---   -----
   1.910 + *   32 Mb   2 Mb
   1.911 + *   64 Mb   4 Mb
   1.912 + *  128 Mb   6 Mb
   1.913 + *  256 Mb  10 Mb
   1.914 + *  512 Mb  14 Mb
   1.915 + * 1024 Mb  18 Mb
   1.916 + * 2048 Mb  24 Mb
   1.917 + * 4096 Mb  30 Mb
   1.918 + *
   1.919 + * The equation for this is (for cache size C and memory size K (kbytes)):
   1.920 + *  x = log2(K) - 14
   1.921 + *  C = x^2/3 + x + 2/3 + 0.1 (0.1 for rounding)
   1.922 + *  if (C > 32) C = 32
   1.923 + */
   1.924 +
   1.925 +int32_t
   1.926 +nsCacheProfilePrefObserver::MemoryCacheCapacity()
   1.927 +{
   1.928 +    int32_t capacity = mMemoryCacheCapacity;
   1.929 +    if (capacity >= 0) {
   1.930 +        CACHE_LOG_DEBUG(("Memory cache capacity forced to %d\n", capacity));
   1.931 +        return capacity;
   1.932 +    }
   1.933 +
   1.934 +    static uint64_t bytes = PR_GetPhysicalMemorySize();
   1.935 +    CACHE_LOG_DEBUG(("Physical Memory size is %llu\n", bytes));
   1.936 +
   1.937 +    // If getting the physical memory failed, arbitrarily assume
   1.938 +    // 32 MB of RAM. We use a low default to have a reasonable
   1.939 +    // size on all the devices we support.
   1.940 +    if (bytes == 0)
   1.941 +        bytes = 32 * 1024 * 1024;
   1.942 +
   1.943 +    // Conversion from unsigned int64_t to double doesn't work on all platforms.
   1.944 +    // We need to truncate the value at INT64_MAX to make sure we don't
   1.945 +    // overflow.
   1.946 +    if (bytes > INT64_MAX)
   1.947 +        bytes = INT64_MAX;
   1.948 +
   1.949 +    uint64_t kbytes = bytes >> 10;
   1.950 +
   1.951 +    double kBytesD = double(kbytes);
   1.952 +
   1.953 +    double x = log(kBytesD)/log(2.0) - 14;
   1.954 +    if (x > 0) {
   1.955 +        capacity = (int32_t)(x * x / 3.0 + x + 2.0 / 3 + 0.1); // 0.1 for rounding
   1.956 +        if (capacity > 32)
   1.957 +            capacity = 32;
   1.958 +        capacity   *= 1024;
   1.959 +    } else {
   1.960 +        capacity    = 0;
   1.961 +    }
   1.962 +
   1.963 +    return capacity;
   1.964 +}
   1.965 +
   1.966 +int32_t
   1.967 +nsCacheProfilePrefObserver::CacheCompressionLevel()
   1.968 +{
   1.969 +    return mCacheCompressionLevel;
   1.970 +}
   1.971 +
   1.972 +/******************************************************************************
   1.973 + * nsProcessRequestEvent
   1.974 + *****************************************************************************/
   1.975 +
   1.976 +class nsProcessRequestEvent : public nsRunnable {
   1.977 +public:
   1.978 +    nsProcessRequestEvent(nsCacheRequest *aRequest)
   1.979 +    {
   1.980 +        MOZ_EVENT_TRACER_NAME_OBJECT(aRequest, aRequest->mKey.get());
   1.981 +        MOZ_EVENT_TRACER_WAIT(aRequest, "net::cache::ProcessRequest");
   1.982 +        mRequest = aRequest;
   1.983 +    }
   1.984 +
   1.985 +    NS_IMETHOD Run()
   1.986 +    {
   1.987 +        nsresult rv;
   1.988 +
   1.989 +        NS_ASSERTION(mRequest->mListener,
   1.990 +                     "Sync OpenCacheEntry() posted to background thread!");
   1.991 +
   1.992 +        nsCacheServiceAutoLock lock(LOCK_TELEM(NSPROCESSREQUESTEVENT_RUN));
   1.993 +        rv = nsCacheService::gService->ProcessRequest(mRequest,
   1.994 +                                                      false,
   1.995 +                                                      nullptr);
   1.996 +
   1.997 +        // Don't delete the request if it was queued
   1.998 +        if (!(mRequest->IsBlocking() &&
   1.999 +            rv == NS_ERROR_CACHE_WAIT_FOR_VALIDATION))
  1.1000 +            delete mRequest;
  1.1001 +
  1.1002 +        return NS_OK;
  1.1003 +    }
  1.1004 +
  1.1005 +protected:
  1.1006 +    virtual ~nsProcessRequestEvent() {}
  1.1007 +
  1.1008 +private:
  1.1009 +    nsCacheRequest *mRequest;
  1.1010 +};
  1.1011 +
  1.1012 +/******************************************************************************
  1.1013 + * nsDoomEvent
  1.1014 + *****************************************************************************/
  1.1015 +
  1.1016 +class nsDoomEvent : public nsRunnable {
  1.1017 +public:
  1.1018 +    nsDoomEvent(nsCacheSession *session,
  1.1019 +                const nsACString &key,
  1.1020 +                nsICacheListener *listener)
  1.1021 +    {
  1.1022 +        mKey = *session->ClientID();
  1.1023 +        mKey.Append(':');
  1.1024 +        mKey.Append(key);
  1.1025 +        mStoragePolicy = session->StoragePolicy();
  1.1026 +        mListener = listener;
  1.1027 +        mThread = do_GetCurrentThread();
  1.1028 +        // We addref the listener here and release it in nsNotifyDoomListener
  1.1029 +        // on the callers thread. If posting of nsNotifyDoomListener event fails
  1.1030 +        // we leak the listener which is better than releasing it on a wrong
  1.1031 +        // thread.
  1.1032 +        NS_IF_ADDREF(mListener);
  1.1033 +    }
  1.1034 +
  1.1035 +    NS_IMETHOD Run()
  1.1036 +    {
  1.1037 +        nsCacheServiceAutoLock lock(LOCK_TELEM(NSDOOMEVENT_RUN));
  1.1038 +
  1.1039 +        bool foundActive = true;
  1.1040 +        nsresult status = NS_ERROR_NOT_AVAILABLE;
  1.1041 +        nsCacheEntry *entry;
  1.1042 +        entry = nsCacheService::gService->mActiveEntries.GetEntry(&mKey);
  1.1043 +        if (!entry) {
  1.1044 +            bool collision = false;
  1.1045 +            foundActive = false;
  1.1046 +            entry = nsCacheService::gService->SearchCacheDevices(&mKey,
  1.1047 +                                                                 mStoragePolicy,
  1.1048 +                                                                 &collision);
  1.1049 +        }
  1.1050 +
  1.1051 +        if (entry) {
  1.1052 +            status = NS_OK;
  1.1053 +            nsCacheService::gService->DoomEntry_Internal(entry, foundActive);
  1.1054 +        }
  1.1055 +
  1.1056 +        if (mListener) {
  1.1057 +            mThread->Dispatch(new nsNotifyDoomListener(mListener, status),
  1.1058 +                              NS_DISPATCH_NORMAL);
  1.1059 +            // posted event will release the reference on the correct thread
  1.1060 +            mListener = nullptr;
  1.1061 +        }
  1.1062 +
  1.1063 +        return NS_OK;
  1.1064 +    }
  1.1065 +
  1.1066 +private:
  1.1067 +    nsCString             mKey;
  1.1068 +    nsCacheStoragePolicy  mStoragePolicy;
  1.1069 +    nsICacheListener     *mListener;
  1.1070 +    nsCOMPtr<nsIThread>   mThread;
  1.1071 +};
  1.1072 +
  1.1073 +/******************************************************************************
  1.1074 + * nsCacheService
  1.1075 + *****************************************************************************/
  1.1076 +nsCacheService *   nsCacheService::gService = nullptr;
  1.1077 +
  1.1078 +NS_IMPL_ISUPPORTS(nsCacheService, nsICacheService, nsICacheServiceInternal,
  1.1079 +                  nsIMemoryReporter)
  1.1080 +
  1.1081 +nsCacheService::nsCacheService()
  1.1082 +    : mObserver(nullptr),
  1.1083 +      mLock("nsCacheService.mLock"),
  1.1084 +      mCondVar(mLock, "nsCacheService.mCondVar"),
  1.1085 +      mTimeStampLock("nsCacheService.mTimeStampLock"),
  1.1086 +      mInitialized(false),
  1.1087 +      mClearingEntries(false),
  1.1088 +      mEnableMemoryDevice(true),
  1.1089 +      mEnableDiskDevice(true),
  1.1090 +      mMemoryDevice(nullptr),
  1.1091 +      mDiskDevice(nullptr),
  1.1092 +      mOfflineDevice(nullptr),
  1.1093 +      mTotalEntries(0),
  1.1094 +      mCacheHits(0),
  1.1095 +      mCacheMisses(0),
  1.1096 +      mMaxKeyLength(0),
  1.1097 +      mMaxDataSize(0),
  1.1098 +      mMaxMetaSize(0),
  1.1099 +      mDeactivateFailures(0),
  1.1100 +      mDeactivatedUnboundEntries(0)
  1.1101 +{
  1.1102 +    NS_ASSERTION(gService==nullptr, "multiple nsCacheService instances!");
  1.1103 +    gService = this;
  1.1104 +
  1.1105 +    // create list of cache devices
  1.1106 +    PR_INIT_CLIST(&mDoomedEntries);
  1.1107 +}
  1.1108 +
  1.1109 +nsCacheService::~nsCacheService()
  1.1110 +{
  1.1111 +    if (mInitialized) // Shutdown hasn't been called yet.
  1.1112 +        (void) Shutdown();
  1.1113 +
  1.1114 +    if (mObserver) {
  1.1115 +        mObserver->Remove();
  1.1116 +        NS_RELEASE(mObserver);
  1.1117 +    }
  1.1118 +
  1.1119 +    gService = nullptr;
  1.1120 +}
  1.1121 +
  1.1122 +
  1.1123 +nsresult
  1.1124 +nsCacheService::Init()
  1.1125 +{
  1.1126 +    // Thie method must be called on the main thread because mCacheIOThread must
  1.1127 +    // only be modified on the main thread.
  1.1128 +    if (!NS_IsMainThread()) {
  1.1129 +        NS_ERROR("nsCacheService::Init called off the main thread");
  1.1130 +        return NS_ERROR_NOT_SAME_THREAD;
  1.1131 +    }
  1.1132 +
  1.1133 +    NS_ASSERTION(!mInitialized, "nsCacheService already initialized.");
  1.1134 +    if (mInitialized)
  1.1135 +        return NS_ERROR_ALREADY_INITIALIZED;
  1.1136 +
  1.1137 +    if (mozilla::net::IsNeckoChild()) {
  1.1138 +        return NS_ERROR_UNEXPECTED;
  1.1139 +    }
  1.1140 +
  1.1141 +    CACHE_LOG_INIT();
  1.1142 +
  1.1143 +    nsresult rv;
  1.1144 +
  1.1145 +    mStorageService = do_GetService("@mozilla.org/storage/service;1", &rv);
  1.1146 +    NS_ENSURE_SUCCESS(rv, rv);
  1.1147 +
  1.1148 +    MOZ_EVENT_TRACER_NAME_OBJECT(nsCacheService::gService, "nsCacheService");
  1.1149 +
  1.1150 +    rv = NS_NewNamedThread("Cache I/O",
  1.1151 +                           getter_AddRefs(mCacheIOThread));
  1.1152 +    if (NS_FAILED(rv)) {
  1.1153 +        NS_RUNTIMEABORT("Can't create cache IO thread");
  1.1154 +    }
  1.1155 +
  1.1156 +    rv = nsDeleteDir::Init();
  1.1157 +    if (NS_FAILED(rv)) {
  1.1158 +        NS_WARNING("Can't initialize nsDeleteDir");
  1.1159 +    }
  1.1160 +
  1.1161 +    // initialize hashtable for active cache entries
  1.1162 +    rv = mActiveEntries.Init();
  1.1163 +    if (NS_FAILED(rv)) return rv;
  1.1164 +
  1.1165 +    // create profile/preference observer
  1.1166 +    if (!mObserver) {
  1.1167 +      mObserver = new nsCacheProfilePrefObserver();
  1.1168 +      NS_ADDREF(mObserver);
  1.1169 +      mObserver->Install();
  1.1170 +    }
  1.1171 +
  1.1172 +    mEnableDiskDevice    = mObserver->DiskCacheEnabled();
  1.1173 +    mEnableOfflineDevice = mObserver->OfflineCacheEnabled();
  1.1174 +    mEnableMemoryDevice  = mObserver->MemoryCacheEnabled();
  1.1175 +
  1.1176 +    RegisterWeakMemoryReporter(this);
  1.1177 +
  1.1178 +    mInitialized = true;
  1.1179 +    return NS_OK;
  1.1180 +}
  1.1181 +
  1.1182 +// static
  1.1183 +PLDHashOperator
  1.1184 +nsCacheService::ShutdownCustomCacheDeviceEnum(const nsAString& aProfileDir,
  1.1185 +                                              nsRefPtr<nsOfflineCacheDevice>& aDevice,
  1.1186 +                                              void* aUserArg)
  1.1187 +{
  1.1188 +    aDevice->Shutdown();
  1.1189 +    return PL_DHASH_REMOVE;
  1.1190 +}
  1.1191 +
  1.1192 +void
  1.1193 +nsCacheService::Shutdown()
  1.1194 +{
  1.1195 +    // This method must be called on the main thread because mCacheIOThread must
  1.1196 +    // only be modified on the main thread.
  1.1197 +    if (!NS_IsMainThread()) {
  1.1198 +        NS_RUNTIMEABORT("nsCacheService::Shutdown called off the main thread");
  1.1199 +    }
  1.1200 +
  1.1201 +    nsCOMPtr<nsIThread> cacheIOThread;
  1.1202 +    Telemetry::AutoTimer<Telemetry::NETWORK_DISK_CACHE_SHUTDOWN> totalTimer;
  1.1203 +
  1.1204 +    bool shouldSanitize = false;
  1.1205 +    nsCOMPtr<nsIFile> parentDir;
  1.1206 +
  1.1207 +    {
  1.1208 +        nsCacheServiceAutoLock lock(LOCK_TELEM(NSCACHESERVICE_SHUTDOWN));
  1.1209 +        NS_ASSERTION(mInitialized,
  1.1210 +            "can't shutdown nsCacheService unless it has been initialized.");
  1.1211 +        if (!mInitialized)
  1.1212 +            return;
  1.1213 +
  1.1214 +        mClearingEntries = true;
  1.1215 +        DoomActiveEntries(nullptr);
  1.1216 +    }
  1.1217 +
  1.1218 +    CloseAllStreams();
  1.1219 +
  1.1220 +    UnregisterWeakMemoryReporter(this);
  1.1221 +
  1.1222 +    {
  1.1223 +        nsCacheServiceAutoLock lock(LOCK_TELEM(NSCACHESERVICE_SHUTDOWN));
  1.1224 +        NS_ASSERTION(mInitialized, "Bad state");
  1.1225 +
  1.1226 +        mInitialized = false;
  1.1227 +
  1.1228 +        // Clear entries
  1.1229 +        ClearDoomList();
  1.1230 +
  1.1231 +        if (mSmartSizeTimer) {
  1.1232 +            mSmartSizeTimer->Cancel();
  1.1233 +            mSmartSizeTimer = nullptr;
  1.1234 +        }
  1.1235 +
  1.1236 +        // Make sure to wait for any pending cache-operations before
  1.1237 +        // proceeding with destructive actions (bug #620660)
  1.1238 +        (void) SyncWithCacheIOThread();
  1.1239 +
  1.1240 +        // obtain the disk cache directory in case we need to sanitize it
  1.1241 +        parentDir = mObserver->DiskCacheParentDirectory();
  1.1242 +        shouldSanitize = mObserver->SanitizeAtShutdown();
  1.1243 +
  1.1244 +        // deallocate memory and disk caches
  1.1245 +        delete mMemoryDevice;
  1.1246 +        mMemoryDevice = nullptr;
  1.1247 +
  1.1248 +        delete mDiskDevice;
  1.1249 +        mDiskDevice = nullptr;
  1.1250 +
  1.1251 +        if (mOfflineDevice)
  1.1252 +            mOfflineDevice->Shutdown();
  1.1253 +
  1.1254 +        NS_IF_RELEASE(mOfflineDevice);
  1.1255 +
  1.1256 +        mCustomOfflineDevices.Enumerate(&nsCacheService::ShutdownCustomCacheDeviceEnum, nullptr);
  1.1257 +
  1.1258 +#ifdef PR_LOGGING
  1.1259 +        LogCacheStatistics();
  1.1260 +#endif
  1.1261 +
  1.1262 +        mClearingEntries = false;
  1.1263 +        mCacheIOThread.swap(cacheIOThread);
  1.1264 +    }
  1.1265 +
  1.1266 +    if (cacheIOThread)
  1.1267 +        nsShutdownThread::BlockingShutdown(cacheIOThread);
  1.1268 +
  1.1269 +    if (shouldSanitize) {
  1.1270 +        nsresult rv = parentDir->AppendNative(NS_LITERAL_CSTRING("Cache"));
  1.1271 +        if (NS_SUCCEEDED(rv)) {
  1.1272 +            bool exists;
  1.1273 +            if (NS_SUCCEEDED(parentDir->Exists(&exists)) && exists)
  1.1274 +                nsDeleteDir::DeleteDir(parentDir, false);
  1.1275 +        }
  1.1276 +        Telemetry::AutoTimer<Telemetry::NETWORK_DISK_CACHE_SHUTDOWN_CLEAR_PRIVATE> timer;
  1.1277 +        nsDeleteDir::Shutdown(shouldSanitize);
  1.1278 +    } else {
  1.1279 +        Telemetry::AutoTimer<Telemetry::NETWORK_DISK_CACHE_DELETEDIR_SHUTDOWN> timer;
  1.1280 +        nsDeleteDir::Shutdown(shouldSanitize);
  1.1281 +    }
  1.1282 +}
  1.1283 +
  1.1284 +
  1.1285 +nsresult
  1.1286 +nsCacheService::Create(nsISupports* aOuter, const nsIID& aIID, void* *aResult)
  1.1287 +{
  1.1288 +    nsresult  rv;
  1.1289 +
  1.1290 +    if (aOuter != nullptr)
  1.1291 +        return NS_ERROR_NO_AGGREGATION;
  1.1292 +
  1.1293 +    nsCacheService * cacheService = new nsCacheService();
  1.1294 +    if (cacheService == nullptr)
  1.1295 +        return NS_ERROR_OUT_OF_MEMORY;
  1.1296 +
  1.1297 +    NS_ADDREF(cacheService);
  1.1298 +    rv = cacheService->Init();
  1.1299 +    if (NS_SUCCEEDED(rv)) {
  1.1300 +        rv = cacheService->QueryInterface(aIID, aResult);
  1.1301 +    }
  1.1302 +    NS_RELEASE(cacheService);
  1.1303 +    return rv;
  1.1304 +}
  1.1305 +
  1.1306 +
  1.1307 +NS_IMETHODIMP
  1.1308 +nsCacheService::CreateSession(const char *          clientID,
  1.1309 +                              nsCacheStoragePolicy  storagePolicy, 
  1.1310 +                              bool                  streamBased,
  1.1311 +                              nsICacheSession     **result)
  1.1312 +{
  1.1313 +    *result = nullptr;
  1.1314 +
  1.1315 +    if (this == nullptr)  return NS_ERROR_NOT_AVAILABLE;
  1.1316 +
  1.1317 +    nsCacheSession * session = new nsCacheSession(clientID, storagePolicy, streamBased);
  1.1318 +    if (!session)  return NS_ERROR_OUT_OF_MEMORY;
  1.1319 +
  1.1320 +    NS_ADDREF(*result = session);
  1.1321 +
  1.1322 +    return NS_OK;
  1.1323 +}
  1.1324 +
  1.1325 +
  1.1326 +nsresult
  1.1327 +nsCacheService::EvictEntriesForSession(nsCacheSession * session)
  1.1328 +{
  1.1329 +    NS_ASSERTION(gService, "nsCacheService::gService is null.");
  1.1330 +    return gService->EvictEntriesForClient(session->ClientID()->get(),
  1.1331 +                                 session->StoragePolicy());
  1.1332 +}
  1.1333 +
  1.1334 +namespace {
  1.1335 +
  1.1336 +class EvictionNotifierRunnable : public nsRunnable
  1.1337 +{
  1.1338 +public:
  1.1339 +    EvictionNotifierRunnable(nsISupports* aSubject)
  1.1340 +        : mSubject(aSubject)
  1.1341 +    { }
  1.1342 +
  1.1343 +    NS_DECL_NSIRUNNABLE
  1.1344 +
  1.1345 +private:
  1.1346 +    nsCOMPtr<nsISupports> mSubject;
  1.1347 +};
  1.1348 +
  1.1349 +NS_IMETHODIMP
  1.1350 +EvictionNotifierRunnable::Run()
  1.1351 +{
  1.1352 +    nsCOMPtr<nsIObserverService> obsSvc =
  1.1353 +        mozilla::services::GetObserverService();
  1.1354 +    if (obsSvc) {
  1.1355 +        obsSvc->NotifyObservers(mSubject,
  1.1356 +                                NS_CACHESERVICE_EMPTYCACHE_TOPIC_ID,
  1.1357 +                                nullptr);
  1.1358 +    }
  1.1359 +    return NS_OK;
  1.1360 +}
  1.1361 +
  1.1362 +} // anonymous namespace
  1.1363 +
  1.1364 +nsresult
  1.1365 +nsCacheService::EvictEntriesForClient(const char *          clientID,
  1.1366 +                                      nsCacheStoragePolicy  storagePolicy)
  1.1367 +{
  1.1368 +    nsRefPtr<EvictionNotifierRunnable> r =
  1.1369 +        new EvictionNotifierRunnable(NS_ISUPPORTS_CAST(nsICacheService*, this));
  1.1370 +    NS_DispatchToMainThread(r);
  1.1371 +
  1.1372 +    nsCacheServiceAutoLock lock(LOCK_TELEM(NSCACHESERVICE_EVICTENTRIESFORCLIENT));
  1.1373 +    nsresult res = NS_OK;
  1.1374 +
  1.1375 +    if (storagePolicy == nsICache::STORE_ANYWHERE ||
  1.1376 +        storagePolicy == nsICache::STORE_ON_DISK) {
  1.1377 +
  1.1378 +        if (mEnableDiskDevice) {
  1.1379 +            nsresult rv = NS_OK;
  1.1380 +            if (!mDiskDevice)
  1.1381 +                rv = CreateDiskDevice();
  1.1382 +            if (mDiskDevice)
  1.1383 +                rv = mDiskDevice->EvictEntries(clientID);
  1.1384 +            if (NS_FAILED(rv))
  1.1385 +                res = rv;
  1.1386 +        }
  1.1387 +    }
  1.1388 +
  1.1389 +    // Only clear the offline cache if it has been specifically asked for.
  1.1390 +    if (storagePolicy == nsICache::STORE_OFFLINE) {
  1.1391 +        if (mEnableOfflineDevice) {
  1.1392 +            nsresult rv = NS_OK;
  1.1393 +            if (!mOfflineDevice)
  1.1394 +                rv = CreateOfflineDevice();
  1.1395 +            if (mOfflineDevice)
  1.1396 +                rv = mOfflineDevice->EvictEntries(clientID);
  1.1397 +            if (NS_FAILED(rv))
  1.1398 +                res = rv;
  1.1399 +        }
  1.1400 +    }
  1.1401 +
  1.1402 +    if (storagePolicy == nsICache::STORE_ANYWHERE ||
  1.1403 +        storagePolicy == nsICache::STORE_IN_MEMORY) {
  1.1404 +        // If there is no memory device, there is no need to evict it...
  1.1405 +        if (mMemoryDevice) {
  1.1406 +            nsresult rv = mMemoryDevice->EvictEntries(clientID);
  1.1407 +            if (NS_FAILED(rv))
  1.1408 +                res = rv;
  1.1409 +        }
  1.1410 +    }
  1.1411 +
  1.1412 +    return res;
  1.1413 +}
  1.1414 +
  1.1415 +
  1.1416 +nsresult        
  1.1417 +nsCacheService::IsStorageEnabledForPolicy(nsCacheStoragePolicy  storagePolicy,
  1.1418 +                                          bool *              result)
  1.1419 +{
  1.1420 +    if (gService == nullptr) return NS_ERROR_NOT_AVAILABLE;
  1.1421 +    nsCacheServiceAutoLock lock(LOCK_TELEM(NSCACHESERVICE_ISSTORAGEENABLEDFORPOLICY));
  1.1422 +
  1.1423 +    *result = gService->IsStorageEnabledForPolicy_Locked(storagePolicy);
  1.1424 +    return NS_OK;
  1.1425 +}
  1.1426 +
  1.1427 +
  1.1428 +nsresult
  1.1429 +nsCacheService::DoomEntry(nsCacheSession   *session,
  1.1430 +                          const nsACString &key,
  1.1431 +                          nsICacheListener *listener)
  1.1432 +{
  1.1433 +    CACHE_LOG_DEBUG(("Dooming entry for session %p, key %s\n",
  1.1434 +                     session, PromiseFlatCString(key).get()));
  1.1435 +    NS_ASSERTION(gService, "nsCacheService::gService is null.");
  1.1436 +
  1.1437 +    if (!gService->mInitialized)
  1.1438 +        return NS_ERROR_NOT_INITIALIZED;
  1.1439 +
  1.1440 +    return DispatchToCacheIOThread(new nsDoomEvent(session, key, listener));
  1.1441 +}
  1.1442 +
  1.1443 +
  1.1444 +bool          
  1.1445 +nsCacheService::IsStorageEnabledForPolicy_Locked(nsCacheStoragePolicy  storagePolicy)
  1.1446 +{
  1.1447 +    if (gService->mEnableMemoryDevice &&
  1.1448 +        (storagePolicy == nsICache::STORE_ANYWHERE ||
  1.1449 +         storagePolicy == nsICache::STORE_IN_MEMORY)) {
  1.1450 +        return true;
  1.1451 +    }
  1.1452 +    if (gService->mEnableDiskDevice &&
  1.1453 +        (storagePolicy == nsICache::STORE_ANYWHERE ||
  1.1454 +         storagePolicy == nsICache::STORE_ON_DISK)) {
  1.1455 +        return true;
  1.1456 +    }
  1.1457 +    if (gService->mEnableOfflineDevice &&
  1.1458 +        storagePolicy == nsICache::STORE_OFFLINE) {
  1.1459 +        return true;
  1.1460 +    }
  1.1461 +    
  1.1462 +    return false;
  1.1463 +}
  1.1464 +
  1.1465 +NS_IMETHODIMP nsCacheService::VisitEntries(nsICacheVisitor *visitor)
  1.1466 +{
  1.1467 +    NS_ENSURE_ARG_POINTER(visitor);
  1.1468 +
  1.1469 +    nsCacheServiceAutoLock lock(LOCK_TELEM(NSCACHESERVICE_VISITENTRIES));
  1.1470 +
  1.1471 +    if (!(mEnableDiskDevice || mEnableMemoryDevice))
  1.1472 +        return NS_ERROR_NOT_AVAILABLE;
  1.1473 +
  1.1474 +    // XXX record the fact that a visitation is in progress, 
  1.1475 +    // XXX i.e. keep list of visitors in progress.
  1.1476 +    
  1.1477 +    nsresult rv = NS_OK;
  1.1478 +    // If there is no memory device, there are then also no entries to visit...
  1.1479 +    if (mMemoryDevice) {
  1.1480 +        rv = mMemoryDevice->Visit(visitor);
  1.1481 +        if (NS_FAILED(rv)) return rv;
  1.1482 +    }
  1.1483 +
  1.1484 +    if (mEnableDiskDevice) {
  1.1485 +        if (!mDiskDevice) {
  1.1486 +            rv = CreateDiskDevice();
  1.1487 +            if (NS_FAILED(rv)) return rv;
  1.1488 +        }
  1.1489 +        rv = mDiskDevice->Visit(visitor);
  1.1490 +        if (NS_FAILED(rv)) return rv;
  1.1491 +    }
  1.1492 +
  1.1493 +    if (mEnableOfflineDevice) {
  1.1494 +        if (!mOfflineDevice) {
  1.1495 +            rv = CreateOfflineDevice();
  1.1496 +            if (NS_FAILED(rv)) return rv;
  1.1497 +        }
  1.1498 +        rv = mOfflineDevice->Visit(visitor);
  1.1499 +        if (NS_FAILED(rv)) return rv;
  1.1500 +    }
  1.1501 +
  1.1502 +    // XXX notify any shutdown process that visitation is complete for THIS visitor.
  1.1503 +    // XXX keep queue of visitors
  1.1504 +
  1.1505 +    return NS_OK;
  1.1506 +}
  1.1507 +
  1.1508 +void nsCacheService::FireClearNetworkCacheStoredAnywhereNotification()
  1.1509 +{
  1.1510 +    MOZ_ASSERT(NS_IsMainThread());
  1.1511 +    nsCOMPtr<nsIObserverService> obsvc = mozilla::services::GetObserverService();
  1.1512 +    if (obsvc) {
  1.1513 +        obsvc->NotifyObservers(nullptr,
  1.1514 +                               "network-clear-cache-stored-anywhere",
  1.1515 +                               nullptr);
  1.1516 +    }
  1.1517 +}
  1.1518 +
  1.1519 +NS_IMETHODIMP nsCacheService::EvictEntries(nsCacheStoragePolicy storagePolicy)
  1.1520 +{
  1.1521 +    if (storagePolicy == nsICache::STORE_ANYWHERE) {
  1.1522 +        // if not called on main thread, dispatch the notification to the main thread to notify observers
  1.1523 +        if (!NS_IsMainThread()) { 
  1.1524 +            nsCOMPtr<nsIRunnable> event = NS_NewRunnableMethod(this,
  1.1525 +                                                               &nsCacheService::FireClearNetworkCacheStoredAnywhereNotification);
  1.1526 +            NS_DispatchToMainThread(event, NS_DISPATCH_NORMAL);
  1.1527 +        } else {
  1.1528 +            // else you're already on main thread - notify observers
  1.1529 +            FireClearNetworkCacheStoredAnywhereNotification(); 
  1.1530 +        }
  1.1531 +    }
  1.1532 +
  1.1533 +    NS_IMETHODIMP r;
  1.1534 +    r = EvictEntriesForClient(nullptr, storagePolicy);
  1.1535 +
  1.1536 +    // XXX: Bloody hack until we get this notifier in FF14.0:
  1.1537 +    // https://developer.mozilla.org/en/XPCOM_Interface_Reference/nsICacheListener#onCacheEntryDoomed%28%29
  1.1538 +    if (storagePolicy == nsICache::STORE_ANYWHERE &&
  1.1539 +            NS_IsMainThread() && gService && gService->mInitialized) {
  1.1540 +        nsCacheServiceAutoLock lock(LOCK_TELEM(NSCACHESERVICE_EVICTENTRIESFORCLIENT));
  1.1541 +        gService->mClearingEntries = true;
  1.1542 +        gService->DoomActiveEntries(nullptr);
  1.1543 +        gService->ClearDoomList();
  1.1544 +        (void) SyncWithCacheIOThread();
  1.1545 +        gService->mClearingEntries = false;
  1.1546 +    }
  1.1547 +    return r; 
  1.1548 +}
  1.1549 +
  1.1550 +NS_IMETHODIMP nsCacheService::GetCacheIOTarget(nsIEventTarget * *aCacheIOTarget)
  1.1551 +{
  1.1552 +    NS_ENSURE_ARG_POINTER(aCacheIOTarget);
  1.1553 +
  1.1554 +    // Because mCacheIOThread can only be changed on the main thread, it can be
  1.1555 +    // read from the main thread without the lock. This is useful to prevent
  1.1556 +    // blocking the main thread on other cache operations.
  1.1557 +    if (!NS_IsMainThread()) {
  1.1558 +        Lock(LOCK_TELEM(NSCACHESERVICE_GETCACHEIOTARGET));
  1.1559 +    }
  1.1560 +
  1.1561 +    nsresult rv;
  1.1562 +    if (mCacheIOThread) {
  1.1563 +        NS_ADDREF(*aCacheIOTarget = mCacheIOThread);
  1.1564 +        rv = NS_OK;
  1.1565 +    } else {
  1.1566 +        *aCacheIOTarget = nullptr;
  1.1567 +        rv = NS_ERROR_NOT_AVAILABLE;
  1.1568 +    }
  1.1569 +
  1.1570 +    if (!NS_IsMainThread()) {
  1.1571 +        Unlock();
  1.1572 +    }
  1.1573 +
  1.1574 +    return rv;
  1.1575 +}
  1.1576 +
  1.1577 +/* nsICacheServiceInternal
  1.1578 + * readonly attribute double lockHeldTime;
  1.1579 +*/
  1.1580 +NS_IMETHODIMP nsCacheService::GetLockHeldTime(double *aLockHeldTime)
  1.1581 +{
  1.1582 +    MutexAutoLock lock(mTimeStampLock);
  1.1583 +
  1.1584 +    if (mLockAcquiredTimeStamp.IsNull()) {
  1.1585 +        *aLockHeldTime = 0.0;
  1.1586 +    }
  1.1587 +    else {
  1.1588 +        *aLockHeldTime = 
  1.1589 +            (TimeStamp::Now() - mLockAcquiredTimeStamp).ToMilliseconds();
  1.1590 +    }
  1.1591 +
  1.1592 +    return NS_OK;
  1.1593 +}
  1.1594 +
  1.1595 +/**
  1.1596 + * Internal Methods
  1.1597 + */
  1.1598 +nsresult
  1.1599 +nsCacheService::CreateDiskDevice()
  1.1600 +{
  1.1601 +    if (!mInitialized)      return NS_ERROR_NOT_AVAILABLE;
  1.1602 +    if (!mEnableDiskDevice) return NS_ERROR_NOT_AVAILABLE;
  1.1603 +    if (mDiskDevice)        return NS_OK;
  1.1604 +
  1.1605 +    mDiskDevice = new nsDiskCacheDevice;
  1.1606 +    if (!mDiskDevice)       return NS_ERROR_OUT_OF_MEMORY;
  1.1607 +
  1.1608 +    // set the preferences
  1.1609 +    mDiskDevice->SetCacheParentDirectory(mObserver->DiskCacheParentDirectory());
  1.1610 +    mDiskDevice->SetCapacity(mObserver->DiskCacheCapacity());
  1.1611 +    mDiskDevice->SetMaxEntrySize(mObserver->DiskCacheMaxEntrySize());
  1.1612 +
  1.1613 +    nsresult rv = mDiskDevice->Init();
  1.1614 +    if (NS_FAILED(rv)) {
  1.1615 +#if DEBUG
  1.1616 +        printf("###\n");
  1.1617 +        printf("### mDiskDevice->Init() failed (0x%.8x)\n",
  1.1618 +               static_cast<uint32_t>(rv));
  1.1619 +        printf("###    - disabling disk cache for this session.\n");
  1.1620 +        printf("###\n");
  1.1621 +#endif
  1.1622 +        mEnableDiskDevice = false;
  1.1623 +        delete mDiskDevice;
  1.1624 +        mDiskDevice = nullptr;
  1.1625 +        return rv;
  1.1626 +    }
  1.1627 +
  1.1628 +    Telemetry::Accumulate(Telemetry::DISK_CACHE_SMART_SIZE_USING_OLD_MAX,
  1.1629 +                          mObserver->ShouldUseOldMaxSmartSize());
  1.1630 +
  1.1631 +    NS_ASSERTION(!mSmartSizeTimer, "Smartsize timer was already fired!");
  1.1632 +
  1.1633 +    // Disk device is usually created during the startup. Delay smart size
  1.1634 +    // calculation to avoid possible massive IO caused by eviction of entries
  1.1635 +    // in case the new smart size is smaller than current cache usage.
  1.1636 +    mSmartSizeTimer = do_CreateInstance("@mozilla.org/timer;1", &rv);
  1.1637 +    if (NS_SUCCEEDED(rv)) {
  1.1638 +        rv = mSmartSizeTimer->InitWithCallback(new nsSetDiskSmartSizeCallback(),
  1.1639 +                                               1000*60*3,
  1.1640 +                                               nsITimer::TYPE_ONE_SHOT);
  1.1641 +        if (NS_FAILED(rv)) {
  1.1642 +            NS_WARNING("Failed to post smart size timer");
  1.1643 +            mSmartSizeTimer = nullptr;
  1.1644 +        }
  1.1645 +    } else {
  1.1646 +        NS_WARNING("Can't create smart size timer");
  1.1647 +    }
  1.1648 +    // Ignore state of the timer and return success since the purpose of the
  1.1649 +    // method (create the disk-device) has been fulfilled
  1.1650 +
  1.1651 +    return NS_OK;
  1.1652 +}
  1.1653 +
  1.1654 +// Runnable sent from cache thread to main thread
  1.1655 +class nsDisableOldMaxSmartSizePrefEvent: public nsRunnable
  1.1656 +{
  1.1657 +public:
  1.1658 +    nsDisableOldMaxSmartSizePrefEvent() {}
  1.1659 +
  1.1660 +    NS_IMETHOD Run()
  1.1661 +    {
  1.1662 +        // Main thread may have already called nsCacheService::Shutdown
  1.1663 +        if (!nsCacheService::IsInitialized())
  1.1664 +            return NS_ERROR_NOT_AVAILABLE;
  1.1665 +
  1.1666 +        nsCOMPtr<nsIPrefBranch> branch = do_GetService(NS_PREFSERVICE_CONTRACTID);
  1.1667 +        if (!branch) {
  1.1668 +            return NS_ERROR_NOT_AVAILABLE;
  1.1669 +        }
  1.1670 +
  1.1671 +        nsresult rv = branch->SetBoolPref(DISK_CACHE_USE_OLD_MAX_SMART_SIZE_PREF, false);
  1.1672 +        if (NS_FAILED(rv)) {
  1.1673 +            NS_WARNING("Failed to disable old max smart size");
  1.1674 +            return rv;
  1.1675 +        }
  1.1676 +
  1.1677 +        // It is safe to call SetDiskSmartSize_Locked() without holding the lock
  1.1678 +        // when we are on main thread and nsCacheService is initialized.
  1.1679 +        nsCacheService::gService->SetDiskSmartSize_Locked();
  1.1680 +
  1.1681 +        if (nsCacheService::gService->mObserver->PermittedToSmartSize(branch, false)) {
  1.1682 +            rv = branch->SetIntPref(DISK_CACHE_CAPACITY_PREF, MAX_CACHE_SIZE);
  1.1683 +            if (NS_FAILED(rv)) {
  1.1684 +                NS_WARNING("Failed to set cache capacity pref");
  1.1685 +            }
  1.1686 +        }
  1.1687 +
  1.1688 +        return NS_OK;
  1.1689 +    }
  1.1690 +};
  1.1691 +
  1.1692 +void
  1.1693 +nsCacheService::MarkStartingFresh()
  1.1694 +{
  1.1695 +    if (!gService->mObserver->ShouldUseOldMaxSmartSize()) {
  1.1696 +        // Already using new max, nothing to do here
  1.1697 +        return;
  1.1698 +    }
  1.1699 +
  1.1700 +    gService->mObserver->SetUseNewMaxSmartSize(true);
  1.1701 +
  1.1702 +    // We always dispatch an event here because we don't want to deal with lock
  1.1703 +    // reentrance issues.
  1.1704 +    NS_DispatchToMainThread(new nsDisableOldMaxSmartSizePrefEvent());
  1.1705 +}
  1.1706 +
  1.1707 +nsresult
  1.1708 +nsCacheService::GetOfflineDevice(nsOfflineCacheDevice **aDevice)
  1.1709 +{
  1.1710 +    if (!mOfflineDevice) {
  1.1711 +        nsresult rv = CreateOfflineDevice();
  1.1712 +        NS_ENSURE_SUCCESS(rv, rv);
  1.1713 +    }
  1.1714 +
  1.1715 +    NS_ADDREF(*aDevice = mOfflineDevice);
  1.1716 +    return NS_OK;
  1.1717 +}
  1.1718 +
  1.1719 +nsresult
  1.1720 +nsCacheService::GetCustomOfflineDevice(nsIFile *aProfileDir,
  1.1721 +                                       int32_t aQuota,
  1.1722 +                                       nsOfflineCacheDevice **aDevice)
  1.1723 +{
  1.1724 +    nsresult rv;
  1.1725 +
  1.1726 +    nsAutoString profilePath;
  1.1727 +    rv = aProfileDir->GetPath(profilePath);
  1.1728 +    NS_ENSURE_SUCCESS(rv, rv);
  1.1729 +
  1.1730 +    if (!mCustomOfflineDevices.Get(profilePath, aDevice)) {
  1.1731 +        rv = CreateCustomOfflineDevice(aProfileDir, aQuota, aDevice);
  1.1732 +        NS_ENSURE_SUCCESS(rv, rv);
  1.1733 +
  1.1734 +        (*aDevice)->SetAutoShutdown();
  1.1735 +        mCustomOfflineDevices.Put(profilePath, *aDevice);
  1.1736 +    }
  1.1737 +
  1.1738 +    return NS_OK;
  1.1739 +}
  1.1740 +
  1.1741 +nsresult
  1.1742 +nsCacheService::CreateOfflineDevice()
  1.1743 +{
  1.1744 +    CACHE_LOG_ALWAYS(("Creating default offline device"));
  1.1745 +
  1.1746 +    if (mOfflineDevice)        return NS_OK;
  1.1747 +    if (!nsCacheService::IsInitialized()) {
  1.1748 +        return NS_ERROR_NOT_AVAILABLE;
  1.1749 +    }
  1.1750 +
  1.1751 +    nsresult rv = CreateCustomOfflineDevice(
  1.1752 +        mObserver->OfflineCacheParentDirectory(),
  1.1753 +        mObserver->OfflineCacheCapacity(),
  1.1754 +        &mOfflineDevice);
  1.1755 +    NS_ENSURE_SUCCESS(rv, rv);
  1.1756 +
  1.1757 +    return NS_OK;
  1.1758 +}
  1.1759 +
  1.1760 +nsresult
  1.1761 +nsCacheService::CreateCustomOfflineDevice(nsIFile *aProfileDir,
  1.1762 +                                          int32_t aQuota,
  1.1763 +                                          nsOfflineCacheDevice **aDevice)
  1.1764 +{
  1.1765 +    NS_ENSURE_ARG(aProfileDir);
  1.1766 +
  1.1767 +#if defined(PR_LOGGING)
  1.1768 +    nsAutoCString profilePath;
  1.1769 +    aProfileDir->GetNativePath(profilePath);
  1.1770 +    CACHE_LOG_ALWAYS(("Creating custom offline device, %s, %d",
  1.1771 +                      profilePath.BeginReading(), aQuota));
  1.1772 +#endif
  1.1773 +
  1.1774 +    if (!mInitialized)         return NS_ERROR_NOT_AVAILABLE;
  1.1775 +    if (!mEnableOfflineDevice) return NS_ERROR_NOT_AVAILABLE;
  1.1776 +
  1.1777 +    *aDevice = new nsOfflineCacheDevice;
  1.1778 +
  1.1779 +    NS_ADDREF(*aDevice);
  1.1780 +
  1.1781 +    // set the preferences
  1.1782 +    (*aDevice)->SetCacheParentDirectory(aProfileDir);
  1.1783 +    (*aDevice)->SetCapacity(aQuota);
  1.1784 +
  1.1785 +    nsresult rv = (*aDevice)->InitWithSqlite(mStorageService);
  1.1786 +    if (NS_FAILED(rv)) {
  1.1787 +        CACHE_LOG_DEBUG(("OfflineDevice->InitWithSqlite() failed (0x%.8x)\n", rv));
  1.1788 +        CACHE_LOG_DEBUG(("    - disabling offline cache for this session.\n"));
  1.1789 +
  1.1790 +        NS_RELEASE(*aDevice);
  1.1791 +    }
  1.1792 +    return rv;
  1.1793 +}
  1.1794 +
  1.1795 +nsresult
  1.1796 +nsCacheService::CreateMemoryDevice()
  1.1797 +{
  1.1798 +    if (!mInitialized)        return NS_ERROR_NOT_AVAILABLE;
  1.1799 +    if (!mEnableMemoryDevice) return NS_ERROR_NOT_AVAILABLE;
  1.1800 +    if (mMemoryDevice)        return NS_OK;
  1.1801 +
  1.1802 +    mMemoryDevice = new nsMemoryCacheDevice;
  1.1803 +    if (!mMemoryDevice)       return NS_ERROR_OUT_OF_MEMORY;
  1.1804 +    
  1.1805 +    // set preference
  1.1806 +    int32_t capacity = mObserver->MemoryCacheCapacity();
  1.1807 +    CACHE_LOG_DEBUG(("Creating memory device with capacity %d\n", capacity));
  1.1808 +    mMemoryDevice->SetCapacity(capacity);
  1.1809 +    mMemoryDevice->SetMaxEntrySize(mObserver->MemoryCacheMaxEntrySize());
  1.1810 +
  1.1811 +    nsresult rv = mMemoryDevice->Init();
  1.1812 +    if (NS_FAILED(rv)) {
  1.1813 +        NS_WARNING("Initialization of Memory Cache failed.");
  1.1814 +        delete mMemoryDevice;
  1.1815 +        mMemoryDevice = nullptr;
  1.1816 +    }
  1.1817 +
  1.1818 +    return rv;
  1.1819 +}
  1.1820 +
  1.1821 +nsresult
  1.1822 +nsCacheService::RemoveCustomOfflineDevice(nsOfflineCacheDevice *aDevice)
  1.1823 +{
  1.1824 +    nsCOMPtr<nsIFile> profileDir = aDevice->BaseDirectory();
  1.1825 +    if (!profileDir)
  1.1826 +        return NS_ERROR_UNEXPECTED;
  1.1827 +
  1.1828 +    nsAutoString profilePath;
  1.1829 +    nsresult rv = profileDir->GetPath(profilePath);
  1.1830 +    NS_ENSURE_SUCCESS(rv, rv);
  1.1831 +
  1.1832 +    mCustomOfflineDevices.Remove(profilePath);
  1.1833 +    return NS_OK;
  1.1834 +}
  1.1835 +
  1.1836 +nsresult
  1.1837 +nsCacheService::CreateRequest(nsCacheSession *   session,
  1.1838 +                              const nsACString & clientKey,
  1.1839 +                              nsCacheAccessMode  accessRequested,
  1.1840 +                              bool               blockingMode,
  1.1841 +                              nsICacheListener * listener,
  1.1842 +                              nsCacheRequest **  request)
  1.1843 +{
  1.1844 +    NS_ASSERTION(request, "CreateRequest: request is null");
  1.1845 +     
  1.1846 +    nsAutoCString key(*session->ClientID());
  1.1847 +    key.Append(':');
  1.1848 +    key.Append(clientKey);
  1.1849 +
  1.1850 +    if (mMaxKeyLength < key.Length()) mMaxKeyLength = key.Length();
  1.1851 +
  1.1852 +    // create request
  1.1853 +    *request = new nsCacheRequest(key, listener, accessRequested,
  1.1854 +                                  blockingMode, session);
  1.1855 +
  1.1856 +    if (!listener)  return NS_OK;  // we're sync, we're done.
  1.1857 +
  1.1858 +    // get the request's thread
  1.1859 +    (*request)->mThread = do_GetCurrentThread();
  1.1860 +    
  1.1861 +    return NS_OK;
  1.1862 +}
  1.1863 +
  1.1864 +
  1.1865 +class nsCacheListenerEvent : public nsRunnable
  1.1866 +{
  1.1867 +public:
  1.1868 +    nsCacheListenerEvent(nsICacheListener *listener,
  1.1869 +                         nsICacheEntryDescriptor *descriptor,
  1.1870 +                         nsCacheAccessMode accessGranted,
  1.1871 +                         nsresult status)
  1.1872 +        : mListener(listener)      // transfers reference
  1.1873 +        , mDescriptor(descriptor)  // transfers reference (may be null)
  1.1874 +        , mAccessGranted(accessGranted)
  1.1875 +        , mStatus(status)
  1.1876 +    {}
  1.1877 +
  1.1878 +    NS_IMETHOD Run()
  1.1879 +    {
  1.1880 +        mozilla::eventtracer::AutoEventTracer tracer(
  1.1881 +            static_cast<nsIRunnable*>(this),
  1.1882 +            eventtracer::eExec,
  1.1883 +            eventtracer::eDone,
  1.1884 +            "net::cache::OnCacheEntryAvailable");
  1.1885 +
  1.1886 +        mListener->OnCacheEntryAvailable(mDescriptor, mAccessGranted, mStatus);
  1.1887 +
  1.1888 +        NS_RELEASE(mListener);
  1.1889 +        NS_IF_RELEASE(mDescriptor);
  1.1890 +        return NS_OK;
  1.1891 +    }
  1.1892 +
  1.1893 +private:
  1.1894 +    // We explicitly leak mListener or mDescriptor if Run is not called
  1.1895 +    // because otherwise we cannot guarantee that they are destroyed on
  1.1896 +    // the right thread.
  1.1897 +
  1.1898 +    nsICacheListener        *mListener;
  1.1899 +    nsICacheEntryDescriptor *mDescriptor;
  1.1900 +    nsCacheAccessMode        mAccessGranted;
  1.1901 +    nsresult                 mStatus;
  1.1902 +};
  1.1903 +
  1.1904 +
  1.1905 +nsresult
  1.1906 +nsCacheService::NotifyListener(nsCacheRequest *          request,
  1.1907 +                               nsICacheEntryDescriptor * descriptor,
  1.1908 +                               nsCacheAccessMode         accessGranted,
  1.1909 +                               nsresult                  status)
  1.1910 +{
  1.1911 +    NS_ASSERTION(request->mThread, "no thread set in async request!");
  1.1912 +
  1.1913 +    // Swap ownership, and release listener on target thread...
  1.1914 +    nsICacheListener *listener = request->mListener;
  1.1915 +    request->mListener = nullptr;
  1.1916 +
  1.1917 +    nsCOMPtr<nsIRunnable> ev =
  1.1918 +            new nsCacheListenerEvent(listener, descriptor,
  1.1919 +                                     accessGranted, status);
  1.1920 +    if (!ev) {
  1.1921 +        // Better to leak listener and descriptor if we fail because we don't
  1.1922 +        // want to destroy them inside the cache service lock or on potentially
  1.1923 +        // the wrong thread.
  1.1924 +        return NS_ERROR_OUT_OF_MEMORY;
  1.1925 +    }
  1.1926 +
  1.1927 +    MOZ_EVENT_TRACER_NAME_OBJECT(ev.get(), request->mKey.get());
  1.1928 +    MOZ_EVENT_TRACER_WAIT(ev.get(), "net::cache::OnCacheEntryAvailable");
  1.1929 +    return request->mThread->Dispatch(ev, NS_DISPATCH_NORMAL);
  1.1930 +}
  1.1931 +
  1.1932 +
  1.1933 +nsresult
  1.1934 +nsCacheService::ProcessRequest(nsCacheRequest *           request,
  1.1935 +                               bool                       calledFromOpenCacheEntry,
  1.1936 +                               nsICacheEntryDescriptor ** result)
  1.1937 +{
  1.1938 +    mozilla::eventtracer::AutoEventTracer tracer(
  1.1939 +        request,
  1.1940 +        eventtracer::eExec,
  1.1941 +        eventtracer::eDone,
  1.1942 +        "net::cache::ProcessRequest");
  1.1943 +
  1.1944 +    // !!! must be called with mLock held !!!
  1.1945 +    nsresult           rv;
  1.1946 +    nsCacheEntry *     entry = nullptr;
  1.1947 +    nsCacheEntry *     doomedEntry = nullptr;
  1.1948 +    nsCacheAccessMode  accessGranted = nsICache::ACCESS_NONE;
  1.1949 +    if (result) *result = nullptr;
  1.1950 +
  1.1951 +    while(1) {  // Activate entry loop
  1.1952 +        rv = ActivateEntry(request, &entry, &doomedEntry);  // get the entry for this request
  1.1953 +        if (NS_FAILED(rv))  break;
  1.1954 +
  1.1955 +        while(1) { // Request Access loop
  1.1956 +            NS_ASSERTION(entry, "no entry in Request Access loop!");
  1.1957 +            // entry->RequestAccess queues request on entry
  1.1958 +            rv = entry->RequestAccess(request, &accessGranted);
  1.1959 +            if (rv != NS_ERROR_CACHE_WAIT_FOR_VALIDATION) break;
  1.1960 +
  1.1961 +            if (request->IsBlocking()) {
  1.1962 +                if (request->mListener) {
  1.1963 +                    // async exits - validate, doom, or close will resume
  1.1964 +                    return rv;
  1.1965 +                }
  1.1966 +
  1.1967 +                // XXX this is probably wrong...
  1.1968 +                Unlock();
  1.1969 +                rv = request->WaitForValidation();
  1.1970 +                Lock(LOCK_TELEM(NSCACHESERVICE_PROCESSREQUEST));
  1.1971 +            }
  1.1972 +
  1.1973 +            PR_REMOVE_AND_INIT_LINK(request);
  1.1974 +            if (NS_FAILED(rv)) break;   // non-blocking mode returns WAIT_FOR_VALIDATION error
  1.1975 +            // okay, we're ready to process this request, request access again
  1.1976 +        }
  1.1977 +        if (rv != NS_ERROR_CACHE_ENTRY_DOOMED)  break;
  1.1978 +
  1.1979 +        if (entry->IsNotInUse()) {
  1.1980 +            // this request was the last one keeping it around, so get rid of it
  1.1981 +            DeactivateEntry(entry);
  1.1982 +        }
  1.1983 +        // loop back around to look for another entry
  1.1984 +    }
  1.1985 +
  1.1986 +    if (NS_SUCCEEDED(rv) && request->mProfileDir) {
  1.1987 +        // Custom cache directory has been demanded.  Preset the cache device.
  1.1988 +        if (entry->StoragePolicy() != nsICache::STORE_OFFLINE) {
  1.1989 +            // Failsafe check: this is implemented only for offline cache atm.
  1.1990 +            rv = NS_ERROR_FAILURE;
  1.1991 +        } else {
  1.1992 +            nsRefPtr<nsOfflineCacheDevice> customCacheDevice;
  1.1993 +            rv = GetCustomOfflineDevice(request->mProfileDir, -1,
  1.1994 +                                        getter_AddRefs(customCacheDevice));
  1.1995 +            if (NS_SUCCEEDED(rv))
  1.1996 +                entry->SetCustomCacheDevice(customCacheDevice);
  1.1997 +        }
  1.1998 +    }
  1.1999 +
  1.2000 +    nsICacheEntryDescriptor *descriptor = nullptr;
  1.2001 +    
  1.2002 +    if (NS_SUCCEEDED(rv))
  1.2003 +        rv = entry->CreateDescriptor(request, accessGranted, &descriptor);
  1.2004 +
  1.2005 +    // If doomedEntry is set, ActivatEntry() doomed an existing entry and
  1.2006 +    // created a new one for that cache-key. However, any pending requests
  1.2007 +    // on the doomed entry were not processed and we need to do that here.
  1.2008 +    // This must be done after adding the created entry to list of active
  1.2009 +    // entries (which is done in ActivateEntry()) otherwise the hashkeys crash
  1.2010 +    // (see bug ##561313). It is also important to do this after creating a
  1.2011 +    // descriptor for this request, or some other request may end up being
  1.2012 +    // executed first for the newly created entry.
  1.2013 +    // Finally, it is worth to emphasize that if doomedEntry is set,
  1.2014 +    // ActivateEntry() created a new entry for the request, which will be
  1.2015 +    // initialized by RequestAccess() and they both should have returned NS_OK.
  1.2016 +    if (doomedEntry) {
  1.2017 +        (void) ProcessPendingRequests(doomedEntry);
  1.2018 +        if (doomedEntry->IsNotInUse())
  1.2019 +            DeactivateEntry(doomedEntry);
  1.2020 +        doomedEntry = nullptr;
  1.2021 +    }
  1.2022 +
  1.2023 +    if (request->mListener) {  // Asynchronous
  1.2024 +    
  1.2025 +        if (NS_FAILED(rv) && calledFromOpenCacheEntry && request->IsBlocking())
  1.2026 +            return rv;  // skip notifying listener, just return rv to caller
  1.2027 +            
  1.2028 +        // call listener to report error or descriptor
  1.2029 +        nsresult rv2 = NotifyListener(request, descriptor, accessGranted, rv);
  1.2030 +        if (NS_FAILED(rv2) && NS_SUCCEEDED(rv)) {
  1.2031 +            rv = rv2;  // trigger delete request
  1.2032 +        }
  1.2033 +    } else {        // Synchronous
  1.2034 +        *result = descriptor;
  1.2035 +    }
  1.2036 +    return rv;
  1.2037 +}
  1.2038 +
  1.2039 +
  1.2040 +nsresult
  1.2041 +nsCacheService::OpenCacheEntry(nsCacheSession *           session,
  1.2042 +                               const nsACString &         key,
  1.2043 +                               nsCacheAccessMode          accessRequested,
  1.2044 +                               bool                       blockingMode,
  1.2045 +                               nsICacheListener *         listener,
  1.2046 +                               nsICacheEntryDescriptor ** result)
  1.2047 +{
  1.2048 +    CACHE_LOG_DEBUG(("Opening entry for session %p, key %s, mode %d, blocking %d\n",
  1.2049 +                     session, PromiseFlatCString(key).get(), accessRequested,
  1.2050 +                     blockingMode));
  1.2051 +    NS_ASSERTION(gService, "nsCacheService::gService is null.");
  1.2052 +    if (result)
  1.2053 +        *result = nullptr;
  1.2054 +
  1.2055 +    if (!gService->mInitialized)
  1.2056 +        return NS_ERROR_NOT_INITIALIZED;
  1.2057 +
  1.2058 +    nsCacheRequest * request = nullptr;
  1.2059 +
  1.2060 +    nsresult rv = gService->CreateRequest(session,
  1.2061 +                                          key,
  1.2062 +                                          accessRequested,
  1.2063 +                                          blockingMode,
  1.2064 +                                          listener,
  1.2065 +                                          &request);
  1.2066 +    if (NS_FAILED(rv))  return rv;
  1.2067 +
  1.2068 +    CACHE_LOG_DEBUG(("Created request %p\n", request));
  1.2069 +
  1.2070 +    // Process the request on the background thread if we are on the main thread
  1.2071 +    // and the the request is asynchronous
  1.2072 +    if (NS_IsMainThread() && listener && gService->mCacheIOThread) {
  1.2073 +        nsCOMPtr<nsIRunnable> ev =
  1.2074 +            new nsProcessRequestEvent(request);
  1.2075 +        rv = DispatchToCacheIOThread(ev);
  1.2076 +
  1.2077 +        // delete request if we didn't post the event
  1.2078 +        if (NS_FAILED(rv))
  1.2079 +            delete request;
  1.2080 +    }
  1.2081 +    else {
  1.2082 +
  1.2083 +        nsCacheServiceAutoLock lock(LOCK_TELEM(NSCACHESERVICE_OPENCACHEENTRY));
  1.2084 +        rv = gService->ProcessRequest(request, true, result);
  1.2085 +
  1.2086 +        // delete requests that have completed
  1.2087 +        if (!(listener && blockingMode &&
  1.2088 +            (rv == NS_ERROR_CACHE_WAIT_FOR_VALIDATION)))
  1.2089 +            delete request;
  1.2090 +    }
  1.2091 +
  1.2092 +    return rv;
  1.2093 +}
  1.2094 +
  1.2095 +
  1.2096 +nsresult
  1.2097 +nsCacheService::ActivateEntry(nsCacheRequest * request, 
  1.2098 +                              nsCacheEntry ** result,
  1.2099 +                              nsCacheEntry ** doomedEntry)
  1.2100 +{
  1.2101 +    CACHE_LOG_DEBUG(("Activate entry for request %p\n", request));
  1.2102 +    if (!mInitialized || mClearingEntries)
  1.2103 +        return NS_ERROR_NOT_AVAILABLE;
  1.2104 +
  1.2105 +    mozilla::eventtracer::AutoEventTracer tracer(
  1.2106 +        request,
  1.2107 +        eventtracer::eExec,
  1.2108 +        eventtracer::eDone,
  1.2109 +        "net::cache::ActivateEntry");
  1.2110 +
  1.2111 +    nsresult        rv = NS_OK;
  1.2112 +
  1.2113 +    NS_ASSERTION(request != nullptr, "ActivateEntry called with no request");
  1.2114 +    if (result) *result = nullptr;
  1.2115 +    if (doomedEntry) *doomedEntry = nullptr;
  1.2116 +    if ((!request) || (!result) || (!doomedEntry))
  1.2117 +        return NS_ERROR_NULL_POINTER;
  1.2118 +
  1.2119 +    // check if the request can be satisfied
  1.2120 +    if (!mEnableMemoryDevice && !request->IsStreamBased())
  1.2121 +        return NS_ERROR_FAILURE;
  1.2122 +    if (!IsStorageEnabledForPolicy_Locked(request->StoragePolicy()))
  1.2123 +        return NS_ERROR_FAILURE;
  1.2124 +
  1.2125 +    // search active entries (including those not bound to device)
  1.2126 +    nsCacheEntry *entry = mActiveEntries.GetEntry(&(request->mKey));
  1.2127 +    CACHE_LOG_DEBUG(("Active entry for request %p is %p\n", request, entry));
  1.2128 +
  1.2129 +    if (!entry) {
  1.2130 +        // search cache devices for entry
  1.2131 +        bool collision = false;
  1.2132 +        entry = SearchCacheDevices(&(request->mKey), request->StoragePolicy(), &collision);
  1.2133 +        CACHE_LOG_DEBUG(("Device search for request %p returned %p\n",
  1.2134 +                         request, entry));
  1.2135 +        // When there is a hashkey collision just refuse to cache it...
  1.2136 +        if (collision) return NS_ERROR_CACHE_IN_USE;
  1.2137 +
  1.2138 +        if (entry)  entry->MarkInitialized();
  1.2139 +    } else {
  1.2140 +        NS_ASSERTION(entry->IsActive(), "Inactive entry found in mActiveEntries!");
  1.2141 +    }
  1.2142 +
  1.2143 +    if (entry) {
  1.2144 +        ++mCacheHits;
  1.2145 +        entry->Fetched();
  1.2146 +    } else {
  1.2147 +        ++mCacheMisses;
  1.2148 +    }
  1.2149 +
  1.2150 +    if (entry &&
  1.2151 +        ((request->AccessRequested() == nsICache::ACCESS_WRITE) ||
  1.2152 +         ((request->StoragePolicy() != nsICache::STORE_OFFLINE) &&
  1.2153 +          (entry->mExpirationTime <= SecondsFromPRTime(PR_Now()) &&
  1.2154 +           request->WillDoomEntriesIfExpired()))))
  1.2155 +
  1.2156 +    {
  1.2157 +        // this is FORCE-WRITE request or the entry has expired
  1.2158 +        // we doom entry without processing pending requests, but store it in
  1.2159 +        // doomedEntry which causes pending requests to be processed below
  1.2160 +        rv = DoomEntry_Internal(entry, false);
  1.2161 +        *doomedEntry = entry;
  1.2162 +        if (NS_FAILED(rv)) {
  1.2163 +            // XXX what to do?  Increment FailedDooms counter?
  1.2164 +        }
  1.2165 +        entry = nullptr;
  1.2166 +    }
  1.2167 +
  1.2168 +    if (!entry) {
  1.2169 +        if (! (request->AccessRequested() & nsICache::ACCESS_WRITE)) {
  1.2170 +            // this is a READ-ONLY request
  1.2171 +            rv = NS_ERROR_CACHE_KEY_NOT_FOUND;
  1.2172 +            goto error;
  1.2173 +        }
  1.2174 +
  1.2175 +        entry = new nsCacheEntry(request->mKey,
  1.2176 +                                 request->IsStreamBased(),
  1.2177 +                                 request->StoragePolicy());
  1.2178 +        if (!entry)
  1.2179 +            return NS_ERROR_OUT_OF_MEMORY;
  1.2180 +
  1.2181 +        if (request->IsPrivate())
  1.2182 +            entry->MarkPrivate();
  1.2183 +        
  1.2184 +        entry->Fetched();
  1.2185 +        ++mTotalEntries;
  1.2186 +
  1.2187 +        // XXX  we could perform an early bind in some cases based on storage policy
  1.2188 +    }
  1.2189 +
  1.2190 +    if (!entry->IsActive()) {
  1.2191 +        rv = mActiveEntries.AddEntry(entry);
  1.2192 +        if (NS_FAILED(rv)) goto error;
  1.2193 +        CACHE_LOG_DEBUG(("Added entry %p to mActiveEntries\n", entry));
  1.2194 +        entry->MarkActive();  // mark entry active, because it's now in mActiveEntries
  1.2195 +    }
  1.2196 +    *result = entry;
  1.2197 +    return NS_OK;
  1.2198 +    
  1.2199 + error:
  1.2200 +    *result = nullptr;
  1.2201 +    delete entry;
  1.2202 +    return rv;
  1.2203 +}
  1.2204 +
  1.2205 +
  1.2206 +nsCacheEntry *
  1.2207 +nsCacheService::SearchCacheDevices(nsCString * key, nsCacheStoragePolicy policy, bool *collision)
  1.2208 +{
  1.2209 +    Telemetry::AutoTimer<Telemetry::CACHE_DEVICE_SEARCH_2> timer;
  1.2210 +    nsCacheEntry * entry = nullptr;
  1.2211 +
  1.2212 +    MOZ_EVENT_TRACER_NAME_OBJECT(key, key->BeginReading());
  1.2213 +    eventtracer::AutoEventTracer searchCacheDevices(
  1.2214 +        key,
  1.2215 +        eventtracer::eExec,
  1.2216 +        eventtracer::eDone,
  1.2217 +        "net::cache::SearchCacheDevices");
  1.2218 +
  1.2219 +    CACHE_LOG_DEBUG(("mMemoryDevice: 0x%p\n", mMemoryDevice));
  1.2220 +
  1.2221 +    *collision = false;
  1.2222 +    if ((policy == nsICache::STORE_ANYWHERE) || (policy == nsICache::STORE_IN_MEMORY)) {
  1.2223 +        // If there is no memory device, then there is nothing to search...
  1.2224 +        if (mMemoryDevice) {
  1.2225 +            entry = mMemoryDevice->FindEntry(key, collision);
  1.2226 +            CACHE_LOG_DEBUG(("Searching mMemoryDevice for key %s found: 0x%p, "
  1.2227 +                             "collision: %d\n", key->get(), entry, collision));
  1.2228 +        }
  1.2229 +    }
  1.2230 +
  1.2231 +    if (!entry && 
  1.2232 +        ((policy == nsICache::STORE_ANYWHERE) || (policy == nsICache::STORE_ON_DISK))) {
  1.2233 +
  1.2234 +        if (mEnableDiskDevice) {
  1.2235 +            if (!mDiskDevice) {
  1.2236 +                nsresult rv = CreateDiskDevice();
  1.2237 +                if (NS_FAILED(rv))
  1.2238 +                    return nullptr;
  1.2239 +            }
  1.2240 +            
  1.2241 +            entry = mDiskDevice->FindEntry(key, collision);
  1.2242 +        }
  1.2243 +    }
  1.2244 +
  1.2245 +    if (!entry && (policy == nsICache::STORE_OFFLINE ||
  1.2246 +                   (policy == nsICache::STORE_ANYWHERE &&
  1.2247 +                    gIOService->IsOffline()))) {
  1.2248 +
  1.2249 +        if (mEnableOfflineDevice) {
  1.2250 +            if (!mOfflineDevice) {
  1.2251 +                nsresult rv = CreateOfflineDevice();
  1.2252 +                if (NS_FAILED(rv))
  1.2253 +                    return nullptr;
  1.2254 +            }
  1.2255 +
  1.2256 +            entry = mOfflineDevice->FindEntry(key, collision);
  1.2257 +        }
  1.2258 +    }
  1.2259 +
  1.2260 +    return entry;
  1.2261 +}
  1.2262 +
  1.2263 +
  1.2264 +nsCacheDevice *
  1.2265 +nsCacheService::EnsureEntryHasDevice(nsCacheEntry * entry)
  1.2266 +{
  1.2267 +    nsCacheDevice * device = entry->CacheDevice();
  1.2268 +    // return device if found, possibly null if the entry is doomed i.e prevent
  1.2269 +    // doomed entries to bind to a device (see e.g. bugs #548406 and #596443)
  1.2270 +    if (device || entry->IsDoomed())  return device;
  1.2271 +
  1.2272 +    int64_t predictedDataSize = entry->PredictedDataSize();
  1.2273 +    if (entry->IsStreamData() && entry->IsAllowedOnDisk() && mEnableDiskDevice) {
  1.2274 +        // this is the default
  1.2275 +        if (!mDiskDevice) {
  1.2276 +            (void)CreateDiskDevice();  // ignore the error (check for mDiskDevice instead)
  1.2277 +        }
  1.2278 +
  1.2279 +        if (mDiskDevice) {
  1.2280 +            // Bypass the cache if Content-Length says the entry will be too big
  1.2281 +            if (predictedDataSize != -1 &&
  1.2282 +                mDiskDevice->EntryIsTooBig(predictedDataSize)) {
  1.2283 +                DebugOnly<nsresult> rv = nsCacheService::DoomEntry(entry);
  1.2284 +                NS_ASSERTION(NS_SUCCEEDED(rv),"DoomEntry() failed.");
  1.2285 +                return nullptr;
  1.2286 +            }
  1.2287 +
  1.2288 +            entry->MarkBinding();  // enter state of binding
  1.2289 +            nsresult rv = mDiskDevice->BindEntry(entry);
  1.2290 +            entry->ClearBinding(); // exit state of binding
  1.2291 +            if (NS_SUCCEEDED(rv))
  1.2292 +                device = mDiskDevice;
  1.2293 +        }
  1.2294 +    }
  1.2295 +
  1.2296 +    // if we can't use mDiskDevice, try mMemoryDevice
  1.2297 +    if (!device && mEnableMemoryDevice && entry->IsAllowedInMemory()) {        
  1.2298 +        if (!mMemoryDevice) {
  1.2299 +            (void)CreateMemoryDevice();  // ignore the error (check for mMemoryDevice instead)
  1.2300 +        }
  1.2301 +        if (mMemoryDevice) {
  1.2302 +            // Bypass the cache if Content-Length says entry will be too big
  1.2303 +            if (predictedDataSize != -1 &&
  1.2304 +                mMemoryDevice->EntryIsTooBig(predictedDataSize)) {
  1.2305 +                DebugOnly<nsresult> rv = nsCacheService::DoomEntry(entry);
  1.2306 +                NS_ASSERTION(NS_SUCCEEDED(rv),"DoomEntry() failed.");
  1.2307 +                return nullptr;
  1.2308 +            }
  1.2309 +
  1.2310 +            entry->MarkBinding();  // enter state of binding
  1.2311 +            nsresult rv = mMemoryDevice->BindEntry(entry);
  1.2312 +            entry->ClearBinding(); // exit state of binding
  1.2313 +            if (NS_SUCCEEDED(rv))
  1.2314 +                device = mMemoryDevice;
  1.2315 +        }
  1.2316 +    }
  1.2317 +
  1.2318 +    if (!device && entry->IsStreamData() &&
  1.2319 +        entry->IsAllowedOffline() && mEnableOfflineDevice) {
  1.2320 +        if (!mOfflineDevice) {
  1.2321 +            (void)CreateOfflineDevice(); // ignore the error (check for mOfflineDevice instead)
  1.2322 +        }
  1.2323 +
  1.2324 +        device = entry->CustomCacheDevice()
  1.2325 +               ? entry->CustomCacheDevice()
  1.2326 +               : mOfflineDevice;
  1.2327 +
  1.2328 +        if (device) {
  1.2329 +            entry->MarkBinding();
  1.2330 +            nsresult rv = device->BindEntry(entry);
  1.2331 +            entry->ClearBinding();
  1.2332 +            if (NS_FAILED(rv))
  1.2333 +                device = nullptr;
  1.2334 +        }
  1.2335 +    }
  1.2336 +
  1.2337 +    if (device) 
  1.2338 +        entry->SetCacheDevice(device);
  1.2339 +    return device;
  1.2340 +}
  1.2341 +
  1.2342 +nsresult
  1.2343 +nsCacheService::DoomEntry(nsCacheEntry * entry)
  1.2344 +{
  1.2345 +    return gService->DoomEntry_Internal(entry, true);
  1.2346 +}
  1.2347 +
  1.2348 +
  1.2349 +nsresult
  1.2350 +nsCacheService::DoomEntry_Internal(nsCacheEntry * entry,
  1.2351 +                                   bool doProcessPendingRequests)
  1.2352 +{
  1.2353 +    if (entry->IsDoomed())  return NS_OK;
  1.2354 +    
  1.2355 +    CACHE_LOG_DEBUG(("Dooming entry %p\n", entry));
  1.2356 +    nsresult  rv = NS_OK;
  1.2357 +    entry->MarkDoomed();
  1.2358 +    
  1.2359 +    NS_ASSERTION(!entry->IsBinding(), "Dooming entry while binding device.");
  1.2360 +    nsCacheDevice * device = entry->CacheDevice();
  1.2361 +    if (device)  device->DoomEntry(entry);
  1.2362 +
  1.2363 +    if (entry->IsActive()) {
  1.2364 +        // remove from active entries
  1.2365 +        mActiveEntries.RemoveEntry(entry);
  1.2366 +        CACHE_LOG_DEBUG(("Removed entry %p from mActiveEntries\n", entry));
  1.2367 +        entry->MarkInactive();
  1.2368 +     }
  1.2369 +
  1.2370 +    // put on doom list to wait for descriptors to close
  1.2371 +    NS_ASSERTION(PR_CLIST_IS_EMPTY(entry), "doomed entry still on device list");
  1.2372 +    PR_APPEND_LINK(entry, &mDoomedEntries);
  1.2373 +
  1.2374 +    // handle pending requests only if we're supposed to
  1.2375 +    if (doProcessPendingRequests) {
  1.2376 +        // tell pending requests to get on with their lives...
  1.2377 +        rv = ProcessPendingRequests(entry);
  1.2378 +
  1.2379 +        // All requests have been removed, but there may still be open descriptors
  1.2380 +        if (entry->IsNotInUse()) {
  1.2381 +            DeactivateEntry(entry); // tell device to get rid of it
  1.2382 +        }
  1.2383 +    }
  1.2384 +    return rv;
  1.2385 +}
  1.2386 +
  1.2387 +
  1.2388 +void
  1.2389 +nsCacheService::OnProfileShutdown(bool cleanse)
  1.2390 +{
  1.2391 +    if (!gService)  return;
  1.2392 +    if (!gService->mInitialized) {
  1.2393 +        // The cache service has been shut down, but someone is still holding
  1.2394 +        // a reference to it. Ignore this call.
  1.2395 +        return;
  1.2396 +    }
  1.2397 +    {
  1.2398 +        nsCacheServiceAutoLock lock(LOCK_TELEM(NSCACHESERVICE_ONPROFILESHUTDOWN));
  1.2399 +        gService->mClearingEntries = true;
  1.2400 +        gService->DoomActiveEntries(nullptr);
  1.2401 +    }
  1.2402 +
  1.2403 +    gService->CloseAllStreams();
  1.2404 +
  1.2405 +    nsCacheServiceAutoLock lock(LOCK_TELEM(NSCACHESERVICE_ONPROFILESHUTDOWN));
  1.2406 +    gService->ClearDoomList();
  1.2407 +
  1.2408 +    // Make sure to wait for any pending cache-operations before
  1.2409 +    // proceeding with destructive actions (bug #620660)
  1.2410 +    (void) SyncWithCacheIOThread();
  1.2411 +
  1.2412 +    if (gService->mDiskDevice && gService->mEnableDiskDevice) {
  1.2413 +        if (cleanse)
  1.2414 +            gService->mDiskDevice->EvictEntries(nullptr);
  1.2415 +
  1.2416 +        gService->mDiskDevice->Shutdown();
  1.2417 +    }
  1.2418 +    gService->mEnableDiskDevice = false;
  1.2419 +
  1.2420 +    if (gService->mOfflineDevice && gService->mEnableOfflineDevice) {
  1.2421 +        if (cleanse)
  1.2422 +            gService->mOfflineDevice->EvictEntries(nullptr);
  1.2423 +
  1.2424 +        gService->mOfflineDevice->Shutdown();
  1.2425 +    }
  1.2426 +    gService->mCustomOfflineDevices.Enumerate(
  1.2427 +        &nsCacheService::ShutdownCustomCacheDeviceEnum, nullptr);
  1.2428 +
  1.2429 +    gService->mEnableOfflineDevice = false;
  1.2430 +
  1.2431 +    if (gService->mMemoryDevice) {
  1.2432 +        // clear memory cache
  1.2433 +        gService->mMemoryDevice->EvictEntries(nullptr);
  1.2434 +    }
  1.2435 +
  1.2436 +    gService->mClearingEntries = false;
  1.2437 +}
  1.2438 +
  1.2439 +
  1.2440 +void
  1.2441 +nsCacheService::OnProfileChanged()
  1.2442 +{
  1.2443 +    if (!gService)  return;
  1.2444 +
  1.2445 +    CACHE_LOG_DEBUG(("nsCacheService::OnProfileChanged"));
  1.2446 + 
  1.2447 +    nsCacheServiceAutoLock lock(LOCK_TELEM(NSCACHESERVICE_ONPROFILECHANGED));
  1.2448 +    
  1.2449 +    gService->mEnableDiskDevice    = gService->mObserver->DiskCacheEnabled();
  1.2450 +    gService->mEnableOfflineDevice = gService->mObserver->OfflineCacheEnabled();
  1.2451 +    gService->mEnableMemoryDevice  = gService->mObserver->MemoryCacheEnabled();
  1.2452 +
  1.2453 +    if (gService->mDiskDevice) {
  1.2454 +        gService->mDiskDevice->SetCacheParentDirectory(gService->mObserver->DiskCacheParentDirectory());
  1.2455 +        gService->mDiskDevice->SetCapacity(gService->mObserver->DiskCacheCapacity());
  1.2456 +
  1.2457 +        // XXX initialization of mDiskDevice could be made lazily, if mEnableDiskDevice is false
  1.2458 +        nsresult rv = gService->mDiskDevice->Init();
  1.2459 +        if (NS_FAILED(rv)) {
  1.2460 +            NS_ERROR("nsCacheService::OnProfileChanged: Re-initializing disk device failed");
  1.2461 +            gService->mEnableDiskDevice = false;
  1.2462 +            // XXX delete mDiskDevice?
  1.2463 +        }
  1.2464 +    }
  1.2465 +
  1.2466 +    if (gService->mOfflineDevice) {
  1.2467 +        gService->mOfflineDevice->SetCacheParentDirectory(gService->mObserver->OfflineCacheParentDirectory());
  1.2468 +        gService->mOfflineDevice->SetCapacity(gService->mObserver->OfflineCacheCapacity());
  1.2469 +
  1.2470 +        // XXX initialization of mOfflineDevice could be made lazily, if mEnableOfflineDevice is false
  1.2471 +        nsresult rv = gService->mOfflineDevice->InitWithSqlite(gService->mStorageService);
  1.2472 +        if (NS_FAILED(rv)) {
  1.2473 +            NS_ERROR("nsCacheService::OnProfileChanged: Re-initializing offline device failed");
  1.2474 +            gService->mEnableOfflineDevice = false;
  1.2475 +            // XXX delete mOfflineDevice?
  1.2476 +        }
  1.2477 +    }
  1.2478 +
  1.2479 +    // If memoryDevice exists, reset its size to the new profile
  1.2480 +    if (gService->mMemoryDevice) {
  1.2481 +        if (gService->mEnableMemoryDevice) {
  1.2482 +            // make sure that capacity is reset to the right value
  1.2483 +            int32_t capacity = gService->mObserver->MemoryCacheCapacity();
  1.2484 +            CACHE_LOG_DEBUG(("Resetting memory device capacity to %d\n",
  1.2485 +                             capacity));
  1.2486 +            gService->mMemoryDevice->SetCapacity(capacity);
  1.2487 +        } else {
  1.2488 +            // tell memory device to evict everything
  1.2489 +            CACHE_LOG_DEBUG(("memory device disabled\n"));
  1.2490 +            gService->mMemoryDevice->SetCapacity(0);
  1.2491 +            // Don't delete memory device, because some entries may be active still...
  1.2492 +        }
  1.2493 +    }
  1.2494 +}
  1.2495 +
  1.2496 +
  1.2497 +void
  1.2498 +nsCacheService::SetDiskCacheEnabled(bool    enabled)
  1.2499 +{
  1.2500 +    if (!gService)  return;
  1.2501 +    nsCacheServiceAutoLock lock(LOCK_TELEM(NSCACHESERVICE_SETDISKCACHEENABLED));
  1.2502 +    gService->mEnableDiskDevice = enabled;
  1.2503 +}
  1.2504 +
  1.2505 +
  1.2506 +void
  1.2507 +nsCacheService::SetDiskCacheCapacity(int32_t  capacity)
  1.2508 +{
  1.2509 +    if (!gService)  return;
  1.2510 +    nsCacheServiceAutoLock lock(LOCK_TELEM(NSCACHESERVICE_SETDISKCACHECAPACITY));
  1.2511 +
  1.2512 +    if (gService->mDiskDevice) {
  1.2513 +        gService->mDiskDevice->SetCapacity(capacity);
  1.2514 +    }
  1.2515 +
  1.2516 +    gService->mEnableDiskDevice = gService->mObserver->DiskCacheEnabled();
  1.2517 +}
  1.2518 +
  1.2519 +void
  1.2520 +nsCacheService::SetDiskCacheMaxEntrySize(int32_t  maxSize)
  1.2521 +{
  1.2522 +    if (!gService)  return;
  1.2523 +    nsCacheServiceAutoLock lock(LOCK_TELEM(NSCACHESERVICE_SETDISKCACHEMAXENTRYSIZE));
  1.2524 +
  1.2525 +    if (gService->mDiskDevice) {
  1.2526 +        gService->mDiskDevice->SetMaxEntrySize(maxSize);
  1.2527 +    }
  1.2528 +}
  1.2529 +
  1.2530 +void
  1.2531 +nsCacheService::SetMemoryCacheMaxEntrySize(int32_t  maxSize)
  1.2532 +{
  1.2533 +    if (!gService)  return;
  1.2534 +    nsCacheServiceAutoLock lock(LOCK_TELEM(NSCACHESERVICE_SETMEMORYCACHEMAXENTRYSIZE));
  1.2535 +
  1.2536 +    if (gService->mMemoryDevice) {
  1.2537 +        gService->mMemoryDevice->SetMaxEntrySize(maxSize);
  1.2538 +    }
  1.2539 +}
  1.2540 +
  1.2541 +void
  1.2542 +nsCacheService::SetOfflineCacheEnabled(bool    enabled)
  1.2543 +{
  1.2544 +    if (!gService)  return;
  1.2545 +    nsCacheServiceAutoLock lock(LOCK_TELEM(NSCACHESERVICE_SETOFFLINECACHEENABLED));
  1.2546 +    gService->mEnableOfflineDevice = enabled;
  1.2547 +}
  1.2548 +
  1.2549 +void
  1.2550 +nsCacheService::SetOfflineCacheCapacity(int32_t  capacity)
  1.2551 +{
  1.2552 +    if (!gService)  return;
  1.2553 +    nsCacheServiceAutoLock lock(LOCK_TELEM(NSCACHESERVICE_SETOFFLINECACHECAPACITY));
  1.2554 +
  1.2555 +    if (gService->mOfflineDevice) {
  1.2556 +        gService->mOfflineDevice->SetCapacity(capacity);
  1.2557 +    }
  1.2558 +
  1.2559 +    gService->mEnableOfflineDevice = gService->mObserver->OfflineCacheEnabled();
  1.2560 +}
  1.2561 +
  1.2562 +
  1.2563 +void
  1.2564 +nsCacheService::SetMemoryCache()
  1.2565 +{
  1.2566 +    if (!gService)  return;
  1.2567 +
  1.2568 +    CACHE_LOG_DEBUG(("nsCacheService::SetMemoryCache"));
  1.2569 +
  1.2570 +    nsCacheServiceAutoLock lock(LOCK_TELEM(NSCACHESERVICE_SETMEMORYCACHE));
  1.2571 +
  1.2572 +    gService->mEnableMemoryDevice = gService->mObserver->MemoryCacheEnabled();
  1.2573 +
  1.2574 +    if (gService->mEnableMemoryDevice) {
  1.2575 +        if (gService->mMemoryDevice) {
  1.2576 +            int32_t capacity = gService->mObserver->MemoryCacheCapacity();
  1.2577 +            // make sure that capacity is reset to the right value
  1.2578 +            CACHE_LOG_DEBUG(("Resetting memory device capacity to %d\n",
  1.2579 +                             capacity));
  1.2580 +            gService->mMemoryDevice->SetCapacity(capacity);
  1.2581 +        }
  1.2582 +    } else {
  1.2583 +        if (gService->mMemoryDevice) {
  1.2584 +            // tell memory device to evict everything
  1.2585 +            CACHE_LOG_DEBUG(("memory device disabled\n"));
  1.2586 +            gService->mMemoryDevice->SetCapacity(0);
  1.2587 +            // Don't delete memory device, because some entries may be active still...
  1.2588 +        }
  1.2589 +    }
  1.2590 +}
  1.2591 +
  1.2592 +
  1.2593 +/******************************************************************************
  1.2594 + * static methods for nsCacheEntryDescriptor
  1.2595 + *****************************************************************************/
  1.2596 +void
  1.2597 +nsCacheService::CloseDescriptor(nsCacheEntryDescriptor * descriptor)
  1.2598 +{
  1.2599 +    // ask entry to remove descriptor
  1.2600 +    nsCacheEntry * entry = descriptor->CacheEntry();
  1.2601 +    bool doomEntry;
  1.2602 +    bool stillActive = entry->RemoveDescriptor(descriptor, &doomEntry);
  1.2603 +
  1.2604 +    if (!entry->IsValid()) {
  1.2605 +        gService->ProcessPendingRequests(entry);
  1.2606 +    }
  1.2607 +
  1.2608 +    if (doomEntry) {
  1.2609 +        gService->DoomEntry_Internal(entry, true);
  1.2610 +        return;
  1.2611 +    }
  1.2612 +
  1.2613 +    if (!stillActive) {
  1.2614 +        gService->DeactivateEntry(entry);
  1.2615 +    }
  1.2616 +}
  1.2617 +
  1.2618 +
  1.2619 +nsresult        
  1.2620 +nsCacheService::GetFileForEntry(nsCacheEntry *         entry,
  1.2621 +                                nsIFile **             result)
  1.2622 +{
  1.2623 +    nsCacheDevice * device = gService->EnsureEntryHasDevice(entry);
  1.2624 +    if (!device)  return  NS_ERROR_UNEXPECTED;
  1.2625 +    
  1.2626 +    return device->GetFileForEntry(entry, result);
  1.2627 +}
  1.2628 +
  1.2629 +
  1.2630 +nsresult
  1.2631 +nsCacheService::OpenInputStreamForEntry(nsCacheEntry *     entry,
  1.2632 +                                        nsCacheAccessMode  mode,
  1.2633 +                                        uint32_t           offset,
  1.2634 +                                        nsIInputStream  ** result)
  1.2635 +{
  1.2636 +    nsCacheDevice * device = gService->EnsureEntryHasDevice(entry);
  1.2637 +    if (!device)  return  NS_ERROR_UNEXPECTED;
  1.2638 +
  1.2639 +    return device->OpenInputStreamForEntry(entry, mode, offset, result);
  1.2640 +}
  1.2641 +
  1.2642 +nsresult
  1.2643 +nsCacheService::OpenOutputStreamForEntry(nsCacheEntry *     entry,
  1.2644 +                                         nsCacheAccessMode  mode,
  1.2645 +                                         uint32_t           offset,
  1.2646 +                                         nsIOutputStream ** result)
  1.2647 +{
  1.2648 +    nsCacheDevice * device = gService->EnsureEntryHasDevice(entry);
  1.2649 +    if (!device)  return  NS_ERROR_UNEXPECTED;
  1.2650 +
  1.2651 +    return device->OpenOutputStreamForEntry(entry, mode, offset, result);
  1.2652 +}
  1.2653 +
  1.2654 +
  1.2655 +nsresult
  1.2656 +nsCacheService::OnDataSizeChange(nsCacheEntry * entry, int32_t deltaSize)
  1.2657 +{
  1.2658 +    nsCacheDevice * device = gService->EnsureEntryHasDevice(entry);
  1.2659 +    if (!device)  return  NS_ERROR_UNEXPECTED;
  1.2660 +
  1.2661 +    return device->OnDataSizeChange(entry, deltaSize);
  1.2662 +}
  1.2663 +
  1.2664 +void
  1.2665 +nsCacheService::LockAcquired()
  1.2666 +{
  1.2667 +    MutexAutoLock lock(mTimeStampLock);
  1.2668 +    mLockAcquiredTimeStamp = TimeStamp::Now();
  1.2669 +}
  1.2670 +
  1.2671 +void
  1.2672 +nsCacheService::LockReleased()
  1.2673 +{
  1.2674 +    MutexAutoLock lock(mTimeStampLock);
  1.2675 +    mLockAcquiredTimeStamp = TimeStamp();
  1.2676 +}
  1.2677 +
  1.2678 +void
  1.2679 +nsCacheService::Lock(mozilla::Telemetry::ID mainThreadLockerID)
  1.2680 +{
  1.2681 +    mozilla::Telemetry::ID lockerID;
  1.2682 +    mozilla::Telemetry::ID generalID;
  1.2683 +
  1.2684 +    if (NS_IsMainThread()) {
  1.2685 +        lockerID = mainThreadLockerID;
  1.2686 +        generalID = mozilla::Telemetry::CACHE_SERVICE_LOCK_WAIT_MAINTHREAD_2;
  1.2687 +    } else {
  1.2688 +        lockerID = mozilla::Telemetry::HistogramCount;
  1.2689 +        generalID = mozilla::Telemetry::CACHE_SERVICE_LOCK_WAIT_2;
  1.2690 +    }
  1.2691 +
  1.2692 +    TimeStamp start(TimeStamp::Now());
  1.2693 +    MOZ_EVENT_TRACER_WAIT(nsCacheService::gService, "net::cache::lock");
  1.2694 +
  1.2695 +    gService->mLock.Lock();
  1.2696 +    gService->LockAcquired();
  1.2697 +
  1.2698 +    TimeStamp stop(TimeStamp::Now());
  1.2699 +    MOZ_EVENT_TRACER_EXEC(nsCacheService::gService, "net::cache::lock");
  1.2700 +
  1.2701 +    // Telemetry isn't thread safe on its own, but this is OK because we're
  1.2702 +    // protecting it with the cache lock. 
  1.2703 +    if (lockerID != mozilla::Telemetry::HistogramCount) {
  1.2704 +        mozilla::Telemetry::AccumulateTimeDelta(lockerID, start, stop);
  1.2705 +    }
  1.2706 +    mozilla::Telemetry::AccumulateTimeDelta(generalID, start, stop);
  1.2707 +}
  1.2708 +
  1.2709 +void
  1.2710 +nsCacheService::Unlock()
  1.2711 +{
  1.2712 +    gService->mLock.AssertCurrentThreadOwns();
  1.2713 +
  1.2714 +    nsTArray<nsISupports*> doomed;
  1.2715 +    doomed.SwapElements(gService->mDoomedObjects);
  1.2716 +
  1.2717 +    gService->LockReleased();
  1.2718 +    gService->mLock.Unlock();
  1.2719 +
  1.2720 +    MOZ_EVENT_TRACER_DONE(nsCacheService::gService, "net::cache::lock");
  1.2721 +
  1.2722 +    for (uint32_t i = 0; i < doomed.Length(); ++i)
  1.2723 +        doomed[i]->Release();
  1.2724 +}
  1.2725 +
  1.2726 +void
  1.2727 +nsCacheService::ReleaseObject_Locked(nsISupports * obj,
  1.2728 +                                     nsIEventTarget * target)
  1.2729 +{
  1.2730 +    gService->mLock.AssertCurrentThreadOwns();
  1.2731 +
  1.2732 +    bool isCur;
  1.2733 +    if (!target || (NS_SUCCEEDED(target->IsOnCurrentThread(&isCur)) && isCur)) {
  1.2734 +        gService->mDoomedObjects.AppendElement(obj);
  1.2735 +    } else {
  1.2736 +        NS_ProxyRelease(target, obj);
  1.2737 +    }
  1.2738 +}
  1.2739 +
  1.2740 +
  1.2741 +nsresult
  1.2742 +nsCacheService::SetCacheElement(nsCacheEntry * entry, nsISupports * element)
  1.2743 +{
  1.2744 +    entry->SetData(element);
  1.2745 +    entry->TouchData();
  1.2746 +    return NS_OK;
  1.2747 +}
  1.2748 +
  1.2749 +
  1.2750 +nsresult
  1.2751 +nsCacheService::ValidateEntry(nsCacheEntry * entry)
  1.2752 +{
  1.2753 +    nsCacheDevice * device = gService->EnsureEntryHasDevice(entry);
  1.2754 +    if (!device)  return  NS_ERROR_UNEXPECTED;
  1.2755 +
  1.2756 +    entry->MarkValid();
  1.2757 +    nsresult rv = gService->ProcessPendingRequests(entry);
  1.2758 +    NS_ASSERTION(rv == NS_OK, "ProcessPendingRequests failed.");
  1.2759 +    // XXX what else should be done?
  1.2760 +
  1.2761 +    return rv;
  1.2762 +}
  1.2763 +
  1.2764 +
  1.2765 +int32_t
  1.2766 +nsCacheService::CacheCompressionLevel()
  1.2767 +{
  1.2768 +    int32_t level = gService->mObserver->CacheCompressionLevel();
  1.2769 +    return level;
  1.2770 +}
  1.2771 +
  1.2772 +
  1.2773 +void
  1.2774 +nsCacheService::DeactivateEntry(nsCacheEntry * entry)
  1.2775 +{
  1.2776 +    CACHE_LOG_DEBUG(("Deactivating entry %p\n", entry));
  1.2777 +    nsresult  rv = NS_OK;
  1.2778 +    NS_ASSERTION(entry->IsNotInUse(), "### deactivating an entry while in use!");
  1.2779 +    nsCacheDevice * device = nullptr;
  1.2780 +
  1.2781 +    if (mMaxDataSize < entry->DataSize() )     mMaxDataSize = entry->DataSize();
  1.2782 +    if (mMaxMetaSize < entry->MetaDataSize() ) mMaxMetaSize = entry->MetaDataSize();
  1.2783 +
  1.2784 +    if (entry->IsDoomed()) {
  1.2785 +        // remove from Doomed list
  1.2786 +        PR_REMOVE_AND_INIT_LINK(entry);
  1.2787 +    } else if (entry->IsActive()) {
  1.2788 +        // remove from active entries
  1.2789 +        mActiveEntries.RemoveEntry(entry);
  1.2790 +        CACHE_LOG_DEBUG(("Removed deactivated entry %p from mActiveEntries\n",
  1.2791 +                         entry));
  1.2792 +        entry->MarkInactive();
  1.2793 +
  1.2794 +        // bind entry if necessary to store meta-data
  1.2795 +        device = EnsureEntryHasDevice(entry); 
  1.2796 +        if (!device) {
  1.2797 +            CACHE_LOG_DEBUG(("DeactivateEntry: unable to bind active "
  1.2798 +                             "entry %p\n",
  1.2799 +                             entry));
  1.2800 +            NS_WARNING("DeactivateEntry: unable to bind active entry\n");
  1.2801 +            return;
  1.2802 +        }
  1.2803 +    } else {
  1.2804 +        // if mInitialized == false,
  1.2805 +        // then we're shutting down and this state is okay.
  1.2806 +        NS_ASSERTION(!mInitialized, "DeactivateEntry: bad cache entry state.");
  1.2807 +    }
  1.2808 +
  1.2809 +    device = entry->CacheDevice();
  1.2810 +    if (device) {
  1.2811 +        rv = device->DeactivateEntry(entry);
  1.2812 +        if (NS_FAILED(rv)) {
  1.2813 +            // increment deactivate failure count
  1.2814 +            ++mDeactivateFailures;
  1.2815 +        }
  1.2816 +    } else {
  1.2817 +        // increment deactivating unbound entry statistic
  1.2818 +        ++mDeactivatedUnboundEntries;
  1.2819 +        delete entry; // because no one else will
  1.2820 +    }
  1.2821 +}
  1.2822 +
  1.2823 +
  1.2824 +nsresult
  1.2825 +nsCacheService::ProcessPendingRequests(nsCacheEntry * entry)
  1.2826 +{
  1.2827 +    mozilla::eventtracer::AutoEventTracer tracer(
  1.2828 +        entry,
  1.2829 +        eventtracer::eExec,
  1.2830 +        eventtracer::eDone,
  1.2831 +        "net::cache::ProcessPendingRequests");
  1.2832 +
  1.2833 +    nsresult            rv = NS_OK;
  1.2834 +    nsCacheRequest *    request = (nsCacheRequest *)PR_LIST_HEAD(&entry->mRequestQ);
  1.2835 +    nsCacheRequest *    nextRequest;
  1.2836 +    bool                newWriter = false;
  1.2837 +    
  1.2838 +    CACHE_LOG_DEBUG(("ProcessPendingRequests for %sinitialized %s %salid entry %p\n",
  1.2839 +                    (entry->IsInitialized()?"" : "Un"),
  1.2840 +                    (entry->IsDoomed()?"DOOMED" : ""),
  1.2841 +                    (entry->IsValid()? "V":"Inv"), entry));
  1.2842 +
  1.2843 +    if (request == &entry->mRequestQ)  return NS_OK;    // no queued requests
  1.2844 +
  1.2845 +    if (!entry->IsDoomed() && entry->IsInvalid()) {
  1.2846 +        // 1st descriptor closed w/o MarkValid()
  1.2847 +        NS_ASSERTION(PR_CLIST_IS_EMPTY(&entry->mDescriptorQ), "shouldn't be here with open descriptors");
  1.2848 +
  1.2849 +#if DEBUG
  1.2850 +        // verify no ACCESS_WRITE requests(shouldn't have any of these)
  1.2851 +        while (request != &entry->mRequestQ) {
  1.2852 +            NS_ASSERTION(request->AccessRequested() != nsICache::ACCESS_WRITE,
  1.2853 +                         "ACCESS_WRITE request should have been given a new entry");
  1.2854 +            request = (nsCacheRequest *)PR_NEXT_LINK(request);
  1.2855 +        }
  1.2856 +        request = (nsCacheRequest *)PR_LIST_HEAD(&entry->mRequestQ);        
  1.2857 +#endif
  1.2858 +        // find first request with ACCESS_READ_WRITE (if any) and promote it to 1st writer
  1.2859 +        while (request != &entry->mRequestQ) {
  1.2860 +            if (request->AccessRequested() == nsICache::ACCESS_READ_WRITE) {
  1.2861 +                newWriter = true;
  1.2862 +                CACHE_LOG_DEBUG(("  promoting request %p to 1st writer\n", request));
  1.2863 +                break;
  1.2864 +            }
  1.2865 +
  1.2866 +            request = (nsCacheRequest *)PR_NEXT_LINK(request);
  1.2867 +        }
  1.2868 +        
  1.2869 +        if (request == &entry->mRequestQ)   // no requests asked for ACCESS_READ_WRITE, back to top
  1.2870 +            request = (nsCacheRequest *)PR_LIST_HEAD(&entry->mRequestQ);
  1.2871 +        
  1.2872 +        // XXX what should we do if there are only READ requests in queue?
  1.2873 +        // XXX serialize their accesses, give them only read access, but force them to check validate flag?
  1.2874 +        // XXX or do readers simply presume the entry is valid
  1.2875 +        // See fix for bug #467392 below
  1.2876 +    }
  1.2877 +
  1.2878 +    nsCacheAccessMode  accessGranted = nsICache::ACCESS_NONE;
  1.2879 +
  1.2880 +    while (request != &entry->mRequestQ) {
  1.2881 +        nextRequest = (nsCacheRequest *)PR_NEXT_LINK(request);
  1.2882 +        CACHE_LOG_DEBUG(("  %sync request %p for %p\n",
  1.2883 +                        (request->mListener?"As":"S"), request, entry));
  1.2884 +
  1.2885 +        if (request->mListener) {
  1.2886 +
  1.2887 +            // Async request
  1.2888 +            PR_REMOVE_AND_INIT_LINK(request);
  1.2889 +
  1.2890 +            if (entry->IsDoomed()) {
  1.2891 +                rv = ProcessRequest(request, false, nullptr);
  1.2892 +                if (rv == NS_ERROR_CACHE_WAIT_FOR_VALIDATION)
  1.2893 +                    rv = NS_OK;
  1.2894 +                else
  1.2895 +                    delete request;
  1.2896 +
  1.2897 +                if (NS_FAILED(rv)) {
  1.2898 +                    // XXX what to do?
  1.2899 +                }
  1.2900 +            } else if (entry->IsValid() || newWriter) {
  1.2901 +                rv = entry->RequestAccess(request, &accessGranted);
  1.2902 +                NS_ASSERTION(NS_SUCCEEDED(rv),
  1.2903 +                             "if entry is valid, RequestAccess must succeed.");
  1.2904 +                // XXX if (newWriter)  NS_ASSERTION( accessGranted == request->AccessRequested(), "why not?");
  1.2905 +
  1.2906 +                // entry->CreateDescriptor dequeues request, and queues descriptor
  1.2907 +                nsICacheEntryDescriptor *descriptor = nullptr;
  1.2908 +                rv = entry->CreateDescriptor(request,
  1.2909 +                                             accessGranted,
  1.2910 +                                             &descriptor);
  1.2911 +
  1.2912 +                // post call to listener to report error or descriptor
  1.2913 +                rv = NotifyListener(request, descriptor, accessGranted, rv);
  1.2914 +                delete request;
  1.2915 +                if (NS_FAILED(rv)) {
  1.2916 +                    // XXX what to do?
  1.2917 +                }
  1.2918 +                
  1.2919 +            } else {
  1.2920 +                // read-only request to an invalid entry - need to wait for
  1.2921 +                // the entry to become valid so we post an event to process
  1.2922 +                // the request again later (bug #467392)
  1.2923 +                nsCOMPtr<nsIRunnable> ev =
  1.2924 +                    new nsProcessRequestEvent(request);
  1.2925 +                rv = DispatchToCacheIOThread(ev);
  1.2926 +                if (NS_FAILED(rv)) {
  1.2927 +                    delete request; // avoid leak
  1.2928 +                }
  1.2929 +            }
  1.2930 +        } else {
  1.2931 +
  1.2932 +            // Synchronous request
  1.2933 +            request->WakeUp();
  1.2934 +        }
  1.2935 +        if (newWriter)  break;  // process remaining requests after validation
  1.2936 +        request = nextRequest;
  1.2937 +    }
  1.2938 +
  1.2939 +    return NS_OK;
  1.2940 +}
  1.2941 +
  1.2942 +bool
  1.2943 +nsCacheService::IsDoomListEmpty()
  1.2944 +{
  1.2945 +    nsCacheEntry * entry = (nsCacheEntry *)PR_LIST_HEAD(&mDoomedEntries);
  1.2946 +    return &mDoomedEntries == entry;
  1.2947 +}
  1.2948 +
  1.2949 +void
  1.2950 +nsCacheService::ClearDoomList()
  1.2951 +{
  1.2952 +    nsCacheEntry * entry = (nsCacheEntry *)PR_LIST_HEAD(&mDoomedEntries);
  1.2953 +
  1.2954 +    while (entry != &mDoomedEntries) {
  1.2955 +        nsCacheEntry * next = (nsCacheEntry *)PR_NEXT_LINK(entry);
  1.2956 +
  1.2957 +        entry->DetachDescriptors();
  1.2958 +        DeactivateEntry(entry);
  1.2959 +        entry = next;
  1.2960 +    }
  1.2961 +}
  1.2962 +
  1.2963 +PLDHashOperator
  1.2964 +nsCacheService::GetActiveEntries(PLDHashTable *    table,
  1.2965 +                                 PLDHashEntryHdr * hdr,
  1.2966 +                                 uint32_t          number,
  1.2967 +                                 void *            arg)
  1.2968 +{
  1.2969 +    static_cast<nsVoidArray *>(arg)->AppendElement(
  1.2970 +        ((nsCacheEntryHashTableEntry *)hdr)->cacheEntry);
  1.2971 +    return PL_DHASH_NEXT;
  1.2972 +}
  1.2973 +
  1.2974 +struct ActiveEntryArgs
  1.2975 +{
  1.2976 +    nsTArray<nsCacheEntry*>* mActiveArray;
  1.2977 +    nsCacheService::DoomCheckFn mCheckFn;
  1.2978 +};
  1.2979 +
  1.2980 +void
  1.2981 +nsCacheService::DoomActiveEntries(DoomCheckFn check)
  1.2982 +{
  1.2983 +    nsAutoTArray<nsCacheEntry*, 8> array;
  1.2984 +    ActiveEntryArgs args = { &array, check };
  1.2985 +
  1.2986 +    mActiveEntries.VisitEntries(RemoveActiveEntry, &args);
  1.2987 +
  1.2988 +    uint32_t count = array.Length();
  1.2989 +    for (uint32_t i=0; i < count; ++i)
  1.2990 +        DoomEntry_Internal(array[i], true);
  1.2991 +}
  1.2992 +
  1.2993 +PLDHashOperator
  1.2994 +nsCacheService::RemoveActiveEntry(PLDHashTable *    table,
  1.2995 +                                  PLDHashEntryHdr * hdr,
  1.2996 +                                  uint32_t          number,
  1.2997 +                                  void *            arg)
  1.2998 +{
  1.2999 +    nsCacheEntry * entry = ((nsCacheEntryHashTableEntry *)hdr)->cacheEntry;
  1.3000 +    NS_ASSERTION(entry, "### active entry = nullptr!");
  1.3001 +
  1.3002 +    ActiveEntryArgs* args = static_cast<ActiveEntryArgs*>(arg);
  1.3003 +    if (args->mCheckFn && !args->mCheckFn(entry))
  1.3004 +        return PL_DHASH_NEXT;
  1.3005 +
  1.3006 +    NS_ASSERTION(args->mActiveArray, "### array = nullptr!");
  1.3007 +    args->mActiveArray->AppendElement(entry);
  1.3008 +
  1.3009 +    // entry is being removed from the active entry list
  1.3010 +    entry->MarkInactive();
  1.3011 +    return PL_DHASH_REMOVE; // and continue enumerating
  1.3012 +}
  1.3013 +
  1.3014 +
  1.3015 +void
  1.3016 +nsCacheService::CloseAllStreams()
  1.3017 +{
  1.3018 +    nsTArray<nsRefPtr<nsCacheEntryDescriptor::nsInputStreamWrapper> > inputs;
  1.3019 +    nsTArray<nsRefPtr<nsCacheEntryDescriptor::nsOutputStreamWrapper> > outputs;
  1.3020 +
  1.3021 +    {
  1.3022 +        nsCacheServiceAutoLock lock(LOCK_TELEM(NSCACHESERVICE_CLOSEALLSTREAMS));
  1.3023 +
  1.3024 +        nsVoidArray entries;
  1.3025 +
  1.3026 +#if DEBUG
  1.3027 +        // make sure there is no active entry
  1.3028 +        mActiveEntries.VisitEntries(GetActiveEntries, &entries);
  1.3029 +        NS_ASSERTION(entries.Count() == 0, "Bad state");
  1.3030 +#endif
  1.3031 +
  1.3032 +        // Get doomed entries
  1.3033 +        nsCacheEntry * entry = (nsCacheEntry *)PR_LIST_HEAD(&mDoomedEntries);
  1.3034 +        while (entry != &mDoomedEntries) {
  1.3035 +            nsCacheEntry * next = (nsCacheEntry *)PR_NEXT_LINK(entry);
  1.3036 +            entries.AppendElement(entry);
  1.3037 +            entry = next;
  1.3038 +        }
  1.3039 +
  1.3040 +        // Iterate through all entries and collect input and output streams
  1.3041 +        for (int32_t i = 0 ; i < entries.Count() ; i++) {
  1.3042 +            entry = static_cast<nsCacheEntry *>(entries.ElementAt(i));
  1.3043 +
  1.3044 +            nsTArray<nsRefPtr<nsCacheEntryDescriptor> > descs;
  1.3045 +            entry->GetDescriptors(descs);
  1.3046 +
  1.3047 +            for (uint32_t j = 0 ; j < descs.Length() ; j++) {
  1.3048 +                if (descs[j]->mOutputWrapper)
  1.3049 +                    outputs.AppendElement(descs[j]->mOutputWrapper);
  1.3050 +
  1.3051 +                for (int32_t k = 0 ; k < descs[j]->mInputWrappers.Count() ; k++)
  1.3052 +                    inputs.AppendElement(static_cast<
  1.3053 +                        nsCacheEntryDescriptor::nsInputStreamWrapper *>(
  1.3054 +                        descs[j]->mInputWrappers[k]));
  1.3055 +            }
  1.3056 +        }
  1.3057 +    }
  1.3058 +
  1.3059 +    uint32_t i;
  1.3060 +    for (i = 0 ; i < inputs.Length() ; i++)
  1.3061 +        inputs[i]->Close();
  1.3062 +
  1.3063 +    for (i = 0 ; i < outputs.Length() ; i++)
  1.3064 +        outputs[i]->Close();
  1.3065 +}
  1.3066 +
  1.3067 +
  1.3068 +bool
  1.3069 +nsCacheService::GetClearingEntries()
  1.3070 +{
  1.3071 +    AssertOwnsLock();
  1.3072 +    return gService->mClearingEntries;
  1.3073 +}
  1.3074 +
  1.3075 +// static
  1.3076 +void nsCacheService::GetCacheBaseDirectoty(nsIFile ** result)
  1.3077 +{
  1.3078 +    *result = nullptr;
  1.3079 +    if (!gService || !gService->mObserver)
  1.3080 +        return;
  1.3081 +
  1.3082 +    nsCOMPtr<nsIFile> directory =
  1.3083 +        gService->mObserver->DiskCacheParentDirectory();
  1.3084 +    if (!directory)
  1.3085 +        return;
  1.3086 +
  1.3087 +    directory->Clone(result);
  1.3088 +}
  1.3089 +
  1.3090 +// static
  1.3091 +void nsCacheService::GetDiskCacheDirectory(nsIFile ** result)
  1.3092 +{
  1.3093 +    nsCOMPtr<nsIFile> directory;
  1.3094 +    GetCacheBaseDirectoty(getter_AddRefs(directory));
  1.3095 +    if (!directory)
  1.3096 +        return;
  1.3097 +
  1.3098 +    nsresult rv = directory->AppendNative(NS_LITERAL_CSTRING("Cache"));
  1.3099 +    if (NS_FAILED(rv))
  1.3100 +        return;
  1.3101 +
  1.3102 +    directory.forget(result);
  1.3103 +}
  1.3104 +
  1.3105 +// static
  1.3106 +void nsCacheService::GetAppCacheDirectory(nsIFile ** result)
  1.3107 +{
  1.3108 +    nsCOMPtr<nsIFile> directory;
  1.3109 +    GetCacheBaseDirectoty(getter_AddRefs(directory));
  1.3110 +    if (!directory)
  1.3111 +        return;
  1.3112 +
  1.3113 +    nsresult rv = directory->AppendNative(NS_LITERAL_CSTRING("OfflineCache"));
  1.3114 +    if (NS_FAILED(rv))
  1.3115 +        return;
  1.3116 +
  1.3117 +    directory.forget(result);
  1.3118 +}
  1.3119 +
  1.3120 +
  1.3121 +#if defined(PR_LOGGING)
  1.3122 +void
  1.3123 +nsCacheService::LogCacheStatistics()
  1.3124 +{
  1.3125 +    uint32_t hitPercentage = (uint32_t)((((double)mCacheHits) /
  1.3126 +        ((double)(mCacheHits + mCacheMisses))) * 100);
  1.3127 +    CACHE_LOG_ALWAYS(("\nCache Service Statistics:\n\n"));
  1.3128 +    CACHE_LOG_ALWAYS(("    TotalEntries   = %d\n", mTotalEntries));
  1.3129 +    CACHE_LOG_ALWAYS(("    Cache Hits     = %d\n", mCacheHits));
  1.3130 +    CACHE_LOG_ALWAYS(("    Cache Misses   = %d\n", mCacheMisses));
  1.3131 +    CACHE_LOG_ALWAYS(("    Cache Hit %%    = %d%%\n", hitPercentage));
  1.3132 +    CACHE_LOG_ALWAYS(("    Max Key Length = %d\n", mMaxKeyLength));
  1.3133 +    CACHE_LOG_ALWAYS(("    Max Meta Size  = %d\n", mMaxMetaSize));
  1.3134 +    CACHE_LOG_ALWAYS(("    Max Data Size  = %d\n", mMaxDataSize));
  1.3135 +    CACHE_LOG_ALWAYS(("\n"));
  1.3136 +    CACHE_LOG_ALWAYS(("    Deactivate Failures         = %d\n",
  1.3137 +                      mDeactivateFailures));
  1.3138 +    CACHE_LOG_ALWAYS(("    Deactivated Unbound Entries = %d\n",
  1.3139 +                      mDeactivatedUnboundEntries));
  1.3140 +}
  1.3141 +#endif
  1.3142 +
  1.3143 +nsresult
  1.3144 +nsCacheService::SetDiskSmartSize()
  1.3145 +{
  1.3146 +    nsCacheServiceAutoLock lock(LOCK_TELEM(NSCACHESERVICE_SETDISKSMARTSIZE));
  1.3147 +
  1.3148 +    if (!gService) return NS_ERROR_NOT_AVAILABLE;
  1.3149 +
  1.3150 +    return gService->SetDiskSmartSize_Locked();
  1.3151 +}
  1.3152 +
  1.3153 +nsresult
  1.3154 +nsCacheService::SetDiskSmartSize_Locked()
  1.3155 +{
  1.3156 +    nsresult rv;
  1.3157 +
  1.3158 +    if (mozilla::net::CacheObserver::UseNewCache()) {
  1.3159 +        return NS_ERROR_NOT_AVAILABLE;
  1.3160 +    }
  1.3161 +
  1.3162 +    if (!mObserver->DiskCacheParentDirectory())
  1.3163 +        return NS_ERROR_NOT_AVAILABLE;
  1.3164 +
  1.3165 +    if (!mDiskDevice)
  1.3166 +        return NS_ERROR_NOT_AVAILABLE;
  1.3167 +
  1.3168 +    if (!mObserver->SmartSizeEnabled())
  1.3169 +        return NS_ERROR_NOT_AVAILABLE;
  1.3170 +
  1.3171 +    nsAutoString cachePath;
  1.3172 +    rv = mObserver->DiskCacheParentDirectory()->GetPath(cachePath);
  1.3173 +    if (NS_SUCCEEDED(rv)) {
  1.3174 +        nsCOMPtr<nsIRunnable> event =
  1.3175 +            new nsGetSmartSizeEvent(cachePath, mDiskDevice->getCacheSize(),
  1.3176 +                                    mObserver->ShouldUseOldMaxSmartSize());
  1.3177 +        DispatchToCacheIOThread(event);
  1.3178 +    } else {
  1.3179 +        return NS_ERROR_FAILURE;
  1.3180 +    }
  1.3181 +
  1.3182 +    return NS_OK;
  1.3183 +}
  1.3184 +
  1.3185 +void
  1.3186 +nsCacheService::MoveOrRemoveDiskCache(nsIFile *aOldCacheDir, 
  1.3187 +                                      nsIFile *aNewCacheDir,
  1.3188 +                                      const char *aCacheSubdir)
  1.3189 +{
  1.3190 +    bool same;
  1.3191 +    if (NS_FAILED(aOldCacheDir->Equals(aNewCacheDir, &same)) || same)
  1.3192 +        return;
  1.3193 +
  1.3194 +    nsCOMPtr<nsIFile> aOldCacheSubdir;
  1.3195 +    aOldCacheDir->Clone(getter_AddRefs(aOldCacheSubdir));
  1.3196 +
  1.3197 +    nsresult rv = aOldCacheSubdir->AppendNative(
  1.3198 +        nsDependentCString(aCacheSubdir));
  1.3199 +    if (NS_FAILED(rv))
  1.3200 +        return;
  1.3201 +
  1.3202 +    bool exists;
  1.3203 +    if (NS_FAILED(aOldCacheSubdir->Exists(&exists)) || !exists)
  1.3204 +        return;
  1.3205 +
  1.3206 +    nsCOMPtr<nsIFile> aNewCacheSubdir;
  1.3207 +    aNewCacheDir->Clone(getter_AddRefs(aNewCacheSubdir));
  1.3208 +
  1.3209 +    rv = aNewCacheSubdir->AppendNative(nsDependentCString(aCacheSubdir));
  1.3210 +    if (NS_FAILED(rv))
  1.3211 +        return;
  1.3212 +    
  1.3213 +    nsAutoCString newPath;
  1.3214 +    rv = aNewCacheSubdir->GetNativePath(newPath);
  1.3215 +    if (NS_FAILED(rv))
  1.3216 +        return;
  1.3217 +        
  1.3218 +    if (NS_SUCCEEDED(aNewCacheSubdir->Exists(&exists)) && !exists) {
  1.3219 +        // New cache directory does not exist, try to move the old one here
  1.3220 +        // rename needs an empty target directory
  1.3221 +
  1.3222 +        // Make sure the parent of the target sub-dir exists
  1.3223 +        rv = aNewCacheDir->Create(nsIFile::DIRECTORY_TYPE, 0777);
  1.3224 +        if (NS_SUCCEEDED(rv) || NS_ERROR_FILE_ALREADY_EXISTS == rv) {
  1.3225 +            nsAutoCString oldPath;
  1.3226 +            rv = aOldCacheSubdir->GetNativePath(oldPath);
  1.3227 +            if (NS_FAILED(rv))
  1.3228 +                return;
  1.3229 +            if (rename(oldPath.get(), newPath.get()) == 0)
  1.3230 +                return;
  1.3231 +        }
  1.3232 +    }
  1.3233 +    
  1.3234 +    // Delay delete by 1 minute to avoid IO thrash on startup.
  1.3235 +    nsDeleteDir::DeleteDir(aOldCacheSubdir, false, 60000);
  1.3236 +}
  1.3237 +
  1.3238 +static bool
  1.3239 +IsEntryPrivate(nsCacheEntry* entry)
  1.3240 +{
  1.3241 +    return entry->IsPrivate();
  1.3242 +}
  1.3243 +
  1.3244 +void
  1.3245 +nsCacheService::LeavePrivateBrowsing()
  1.3246 +{
  1.3247 +    nsCacheServiceAutoLock lock(LOCK_TELEM(NSCACHESERVICE_LEAVEPRIVATEBROWSING));
  1.3248 +
  1.3249 +    gService->DoomActiveEntries(IsEntryPrivate);
  1.3250 +
  1.3251 +    if (gService->mMemoryDevice) {
  1.3252 +        // clear memory cache
  1.3253 +        gService->mMemoryDevice->EvictPrivateEntries();
  1.3254 +    }
  1.3255 +}
  1.3256 +
  1.3257 +MOZ_DEFINE_MALLOC_SIZE_OF(DiskCacheDeviceMallocSizeOf)
  1.3258 +
  1.3259 +NS_IMETHODIMP
  1.3260 +nsCacheService::CollectReports(nsIHandleReportCallback* aHandleReport,
  1.3261 +                               nsISupports* aData)
  1.3262 +{
  1.3263 +    size_t disk = 0;
  1.3264 +    if (mDiskDevice) {
  1.3265 +        nsCacheServiceAutoLock
  1.3266 +            lock(LOCK_TELEM(NSCACHESERVICE_DISKDEVICEHEAPSIZE));
  1.3267 +        disk = mDiskDevice->SizeOfIncludingThis(DiskCacheDeviceMallocSizeOf);
  1.3268 +    }
  1.3269 +
  1.3270 +    size_t memory = mMemoryDevice ? mMemoryDevice->TotalSize() : 0;
  1.3271 +
  1.3272 +#define REPORT(_path, _amount, _desc)                                         \
  1.3273 +    do {                                                                      \
  1.3274 +        nsresult rv;                                                          \
  1.3275 +        rv = aHandleReport->Callback(EmptyCString(),                          \
  1.3276 +                                     NS_LITERAL_CSTRING(_path),               \
  1.3277 +                                     KIND_HEAP, UNITS_BYTES, _amount,         \
  1.3278 +                                     NS_LITERAL_CSTRING(_desc), aData);       \
  1.3279 +        NS_ENSURE_SUCCESS(rv, rv);                                            \
  1.3280 +    } while (0)
  1.3281 +
  1.3282 +    REPORT("explicit/network/disk-cache", disk,
  1.3283 +           "Memory used by the network disk cache.");
  1.3284 +
  1.3285 +    REPORT("explicit/network/memory-cache", memory,
  1.3286 +           "Memory used by the network memory cache.");
  1.3287 +
  1.3288 +    return NS_OK;
  1.3289 +}

mercurial