diff --git a/browser/components/preferences/advanced.js b/browser/components/preferences/advanced.js
index 6b5cdd1e0d5..e41921988b7 100644
--- a/browser/components/preferences/advanced.js
+++ b/browser/components/preferences/advanced.js
@@ -70,6 +70,7 @@ var gAdvancedPane = {
#ifdef MOZ_CRASHREPORTER
this.initSubmitCrashes();
#endif
+ this.updateActualCacheSize();
},
/**
@@ -190,6 +191,8 @@ var gAdvancedPane = {
*
* browser.cache.disk.capacity
* - the size of the browser cache in KB
+ * browser.cache.disk.smart_size.enabled
+ * - If disabled, disk.capacity is used
*/
/**
@@ -200,7 +203,50 @@ var gAdvancedPane = {
document.documentElement.openSubDialog("chrome://browser/content/preferences/connection.xul",
"", null);
},
+
+ // Retrieves the amount of space currently used by disk cache
+ updateActualCacheSize: function ()
+ {
+ var visitor = {
+ visitDevice: function (deviceID, deviceInfo)
+ {
+ if (deviceID == "disk") {
+ var actualSizeLabel = document.getElementById("actualCacheSize");
+ var sizeStrings = DownloadUtils.convertByteUnits(deviceInfo.totalSize);
+ var prefStrBundle = document.getElementById("bundlePreferences");
+ var sizeStr = prefStrBundle.getFormattedString("actualCacheSize",
+ sizeStrings);
+ actualSizeLabel.value = sizeStr;
+ }
+ // Do not enumerate entries
+ return false;
+ },
+ visitEntry: function (deviceID, entryInfo)
+ {
+ // Do not enumerate entries.
+ return false;
+ }
+ };
+ var cacheService =
+ Components.classes["@mozilla.org/network/cache-service;1"]
+ .getService(Components.interfaces.nsICacheService);
+ cacheService.visitEntries(visitor);
+ },
+
+ updateCacheSizeUI: function (smartSizeEnabled)
+ {
+ document.getElementById("useCacheBefore").disabled = smartSizeEnabled;
+ document.getElementById("cacheSize").disabled = smartSizeEnabled;
+ document.getElementById("useCacheAfter").disabled = smartSizeEnabled;
+ },
+
+ readSmartSizeEnabled: function ()
+ {
+ var enabled = document.getElementById("browser.cache.disk.smart_size.enabled").value;
+ this.updateCacheSizeUI(enabled);
+ },
+
/**
* Converts the cache size from units of KB to units of MB and returns that
* value.
@@ -232,6 +278,7 @@ var gAdvancedPane = {
try {
cacheService.evictEntries(Components.interfaces.nsICache.STORE_ANYWHERE);
} catch(ex) {}
+ this.updateActualCacheSize();
},
readOfflineNotify: function()
diff --git a/browser/components/preferences/advanced.xul b/browser/components/preferences/advanced.xul
index fb25989f508..aad310f00b1 100644
--- a/browser/components/preferences/advanced.xul
+++ b/browser/components/preferences/advanced.xul
@@ -85,8 +85,12 @@
-
-
+
+
+
+
#ifdef MOZ_UPDATER
-
-
-
+
+
+
-
- &connectionDesc.label;
-
-
-
+
+ &connectionDesc.label;
+
+
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/browser/locales/en-US/chrome/browser/preferences/advanced.dtd b/browser/locales/en-US/chrome/browser/preferences/advanced.dtd
index aa3d49536e7..89b966500af 100644
--- a/browser/locales/en-US/chrome/browser/preferences/advanced.dtd
+++ b/browser/locales/en-US/chrome/browser/preferences/advanced.dtd
@@ -51,6 +51,7 @@
+
diff --git a/browser/locales/en-US/chrome/browser/preferences/preferences.properties b/browser/locales/en-US/chrome/browser/preferences/preferences.properties
index 9b5c82e2bfa..dbe53645957 100644
--- a/browser/locales/en-US/chrome/browser/preferences/preferences.properties
+++ b/browser/locales/en-US/chrome/browser/preferences/preferences.properties
@@ -104,6 +104,12 @@ offlineAppUsage=%1$S %2$S
offlinepermissionstext=The following websites are not allowed to store data for offline use:
offlinepermissionstitle=Offline Data
+####Preferences::Advanced::Network
+#LOCALIZATION NOTE: The next string is for the disk usage of the http cache.
+# e.g., "Your cache is currently using 200 MB"
+# %1$S = size
+# %2$S = unit (MB, KB, etc.)
+actualCacheSize=Your cache is currently using %1$S %2$S of disk space
#### Syncing
connect.label=Connect
diff --git a/modules/libpref/src/init/all.js b/modules/libpref/src/init/all.js
index 2be2fda1195..a76a85acc11 100644
--- a/modules/libpref/src/init/all.js
+++ b/modules/libpref/src/init/all.js
@@ -60,6 +60,11 @@ pref("general.warnOnAboutConfig", true);
pref("browser.bookmarks.max_backups", 5);
pref("browser.cache.disk.enable", true);
+// Is this the first-time smartsizing has been introduced?
+pref("browser.cache.disk.smart_size.first_run", true);
+// Does the user want smart-sizing?
+pref("browser.cache.disk.smart_size.enabled", true);
+// Size explicitly set by the user. Used when smart_size.enabled == false
#ifndef WINCE
pref("browser.cache.disk.capacity", 256000);
#else
diff --git a/netwerk/cache/nsCacheService.cpp b/netwerk/cache/nsCacheService.cpp
index d0c6f5ba4ea..5b844cf5cfd 100644
--- a/netwerk/cache/nsCacheService.cpp
+++ b/netwerk/cache/nsCacheService.cpp
@@ -84,6 +84,11 @@
#define DISK_CACHE_ENABLE_PREF "browser.cache.disk.enable"
#define DISK_CACHE_DIR_PREF "browser.cache.disk.parent_directory"
+#define DISK_CACHE_SMART_SIZE_FIRST_RUN_PREF\
+ "browser.cache.disk.smart_size.first_run"
+#define DISK_CACHE_SMART_SIZE_ENABLED_PREF \
+ "browser.cache.disk.smart_size.enabled"
+#define DISK_CACHE_SMART_SIZE_PREF "browser.cache.disk.smart_size_cached_value"
#define DISK_CACHE_CAPACITY_PREF "browser.cache.disk.capacity"
#define DISK_CACHE_MAX_ENTRY_SIZE_PREF "browser.cache.disk.max_entry_size"
#define DISK_CACHE_CAPACITY 256000
@@ -106,6 +111,7 @@ static const char * observerList[] = {
static const char * prefList[] = {
#ifdef NECKO_DISK_CACHE
DISK_CACHE_ENABLE_PREF,
+ DISK_CACHE_SMART_SIZE_ENABLED_PREF,
DISK_CACHE_CAPACITY_PREF,
DISK_CACHE_DIR_PREF,
#endif
@@ -118,6 +124,12 @@ static const char * prefList[] = {
MEMORY_CACHE_CAPACITY_PREF
};
+// Let our base line be 250MB.
+const PRInt32 BASE_LINE = 250 * 1024 * 1024;
+const PRInt32 MIN_SIZE = 50 * 1024 * 1024;
+const PRInt32 MAX_SIZE = 1024 * 1024 * 1024;
+
+
class nsCacheProfilePrefObserver : public nsIObserver
{
public:
@@ -144,6 +156,7 @@ public:
PRBool DiskCacheEnabled();
PRInt32 DiskCacheCapacity() { return mDiskCacheCapacity; }
+ void SetDiskCacheCapacity(PRInt32);
nsILocalFile * DiskCacheParentDirectory() { return mDiskCacheParentDirectory; }
PRBool OfflineCacheEnabled();
@@ -153,7 +166,10 @@ public:
PRBool MemoryCacheEnabled();
PRInt32 MemoryCacheCapacity();
+ static PRUint32 GetSmartCacheSize(void);
+
private:
+ bool PermittedToSmartSize(nsIPrefBranch*, PRBool firstRun);
PRBool mHaveProfile;
PRBool mDiskCacheEnabled;
@@ -170,7 +186,85 @@ private:
PRBool mInPrivateBrowsing;
};
-NS_IMPL_ISUPPORTS1(nsCacheProfilePrefObserver, nsIObserver)
+NS_IMPL_THREADSAFE_ISUPPORTS1(nsCacheProfilePrefObserver, nsIObserver)
+
+// Runnable sent to main thread after the cache IO thread calculates available
+// disk space, so that there is no race in setting mDiskCacheCapacity.
+class nsSetSmartSizeEvent: public nsRunnable
+{
+public:
+ nsSetSmartSizeEvent(bool firstRun,
+ PRInt32 smartSize,
+ nsIPrefBranch* branch,
+ nsCacheProfilePrefObserver* observer)
+ : mFirstRun(firstRun)
+ , mSmartSize(smartSize)
+ , mBranch(branch)
+ , mObserver(observer)
+ {
+ }
+
+ NS_IMETHOD Run()
+ {
+ nsresult rv;
+ NS_ASSERTION(NS_IsMainThread(),
+ "Setting smart size data off the main thread");
+ PRBool smartSizeEnabled;
+ rv = mBranch->GetBoolPref(DISK_CACHE_SMART_SIZE_ENABLED_PREF,
+ &smartSizeEnabled);
+ if (NS_FAILED(rv)) smartSizeEnabled = PR_FALSE;
+ // ensure smart sizing wasn't switched off while event was pending
+ if (smartSizeEnabled) {
+ // must set both fields to be safe
+ nsCacheService::SetDiskCacheCapacity(mSmartSize);
+ mObserver->SetDiskCacheCapacity(mSmartSize);
+ rv = mBranch->SetIntPref(DISK_CACHE_SMART_SIZE_PREF, mSmartSize);
+ if (NS_FAILED(rv)) NS_WARNING("Failed to set smart size pref");
+ }
+ return rv;
+ }
+
+private:
+ bool mFirstRun;
+ PRInt32 mSmartSize;
+ nsCOMPtr mBranch;
+ nsRefPtr mObserver;
+};
+
+
+// Runnable sent from main thread to cacheIO thread
+class nsGetSmartSizeEvent: public nsRunnable
+{
+public:
+ nsGetSmartSizeEvent(bool firstRun,
+ nsIPrefBranch* branch,
+ nsCacheProfilePrefObserver* observer)
+ : mFirstRun(firstRun)
+ , mSmartSize(0)
+ , mBranch(branch)
+ , mObserver(observer)
+ {
+ }
+
+ // Calculates user's disk space available on a background thread and
+ // dispatches this value back to the main thread.
+ NS_IMETHOD Run()
+ {
+ mSmartSize = nsCacheProfilePrefObserver::GetSmartCacheSize() / 1024;
+ nsCOMPtr event = new nsSetSmartSizeEvent(mFirstRun,
+ mSmartSize,
+ mBranch,
+ mObserver);
+ NS_DispatchToMainThread(event);
+ return NS_OK;
+ }
+
+private:
+ bool mFirstRun;
+ PRInt32 mSmartSize;
+ nsCOMPtr mBranch;
+ nsRefPtr mObserver;
+};
nsresult
@@ -246,6 +340,12 @@ nsCacheProfilePrefObserver::Remove()
prefs->RemoveObserver(prefList[i], this); // remove cache pref observers
}
+void
+nsCacheProfilePrefObserver::SetDiskCacheCapacity(PRInt32 capacity)
+{
+ mDiskCacheCapacity = PR_MAX(0, capacity);
+}
+
NS_IMETHODIMP
nsCacheProfilePrefObserver::Observe(nsISupports * subject,
@@ -302,6 +402,31 @@ nsCacheProfilePrefObserver::Observe(nsISupports * subject,
if (NS_FAILED(rv)) return rv;
mDiskCacheCapacity = PR_MAX(0, capacity);
nsCacheService::SetDiskCacheCapacity(mDiskCacheCapacity);
+
+ // Update the cache capacity when smart sizing is turned on/off
+ } else if (!strcmp(DISK_CACHE_SMART_SIZE_ENABLED_PREF, data.get())) {
+ // Is the update because smartsizing was turned on, or off?
+ PRBool smartSizeEnabled;
+ rv = branch->GetBoolPref(DISK_CACHE_SMART_SIZE_ENABLED_PREF,
+ &smartSizeEnabled);
+ if (NS_FAILED(rv)) return rv;
+ PRInt32 newCapacity = 0;
+ if (smartSizeEnabled) {
+ // Smart sizing switched on: recalculate the capacity.
+ nsCOMPtr event = new nsGetSmartSizeEvent(false,
+ branch, this);
+ rv = nsCacheService::DispatchToCacheIOThread(event);
+ // If the dispatch failed, just use our base line for the size
+ if (NS_FAILED(rv)) mDiskCacheCapacity = BASE_LINE;
+ } else {
+ // Smart sizing switched off: use user specified size
+ rv = branch->GetIntPref(DISK_CACHE_CAPACITY_PREF, &newCapacity);
+ if (NS_FAILED(rv)) return rv;
+ mDiskCacheCapacity = PR_MAX(0, newCapacity);
+ nsCacheService::SetDiskCacheCapacity(mDiskCacheCapacity);
+ }
+
+
#if 0
} else if (!strcmp(DISK_CACHE_DIR_PREF, data.get())) {
// XXX We probaby don't want to respond to this pref except after
@@ -397,6 +522,108 @@ nsCacheProfilePrefObserver::Observe(nsISupports * subject,
return NS_OK;
}
+
+
+
+ /* Computes our best guess for the default size of the user's disk cache,
+ * based on the amount of space they have free on their hard drive.
+ * We use a tiered scheme: the more space available,
+ * the larger the disk cache will be. However, we do not want
+ * to enable the disk cache to grow to an unbounded size, so the larger the
+ * user's available space is, the smaller of a percentage we take. We set a
+ * lower bound of 50MB and an upper bound of 1GB.
+ *
+ *@param: None.
+ *@return: The size that the user's disk cache should default to, in bytes.
+ */
+PRUint32
+nsCacheProfilePrefObserver::GetSmartCacheSize(void) {
+ // Get a handle to disk where cache lives, so we can check for free space
+ nsresult rv;
+ nsCOMPtr profileDirectory;
+ rv = NS_GetSpecialDirectory(NS_APP_USER_PROFILE_50_DIR,
+ getter_AddRefs(profileDirectory));
+ if (NS_FAILED(rv)) {
+ return BASE_LINE;
+ }
+ nsCOMPtr diskHandle = do_QueryInterface(profileDirectory);
+ PRInt64 bytesAvailable;
+ diskHandle->GetDiskSpaceAvailable(&bytesAvailable);
+
+ /* 0MB <= Available < 500MB
+ * Use between 50MB and 200MB
+ */
+ if (bytesAvailable < BASE_LINE * 2) {
+ return PR_MAX(MIN_SIZE, bytesAvailable * 4 / 10);
+ }
+
+ /* 500MB <= Available < 2500MB
+ * Use 250MB
+ */
+ if (bytesAvailable < static_cast(BASE_LINE) * 10) {
+ return BASE_LINE;
+ }
+
+ /* 2500MB <= Available < 5000MB
+ * Use between 250MB and 500MB
+ */
+ if (bytesAvailable < static_cast(BASE_LINE) * 20) {
+ return bytesAvailable / 10;
+ }
+
+ /* 5000MB <= Available < 50000MB
+ * Use 625MB
+ */
+ if (bytesAvailable < static_cast(BASE_LINE) * 200 ) {
+ return BASE_LINE * 5 / 2;
+ }
+
+ /* 50000MB <= Available < 75000MB
+ * Use 800MB
+ */
+ if (bytesAvailable < static_cast(BASE_LINE) * 300) {
+ return BASE_LINE / 5 * 16;
+ }
+
+ /* We have come within range of the ceiling
+ * Use 1GB
+ */
+ return MAX_SIZE;
+}
+
+/* Determine if we are permitted to dynamically size the user's disk cache based
+ * on their disk space available. We may do this so long as the pref
+ * smart_size.enabled is true.
+ */
+bool
+nsCacheProfilePrefObserver::PermittedToSmartSize(nsIPrefBranch* branch, PRBool
+ firstRun)
+{
+ nsresult rv;
+ // If user has explicitly set cache size to be smaller than previous default
+ // of 250MB, then smart sizing is off by default. Otherwise, smart sizing is
+ // on by default.
+ if (firstRun) {
+ // check if user has set cache size in the past
+ PRBool userSet;
+ rv = branch->PrefHasUserValue(DISK_CACHE_CAPACITY_PREF, &userSet);
+ if (NS_FAILED(rv)) userSet = PR_TRUE;
+ if (userSet) {
+ PRInt32 oldCapacity;
+ rv = branch->GetIntPref(DISK_CACHE_CAPACITY_PREF, &oldCapacity);
+ if (oldCapacity < BASE_LINE / 1024) {
+ branch->SetBoolPref(DISK_CACHE_SMART_SIZE_ENABLED_PREF,
+ PR_FALSE);
+ return false;
+ }
+ }
+ }
+ PRBool smartSizeEnabled;
+ rv = branch->GetBoolPref(DISK_CACHE_SMART_SIZE_ENABLED_PREF,
+ &smartSizeEnabled);
+ if (NS_FAILED(rv)) return false;
+ return !!smartSizeEnabled;
+}
nsresult
@@ -456,6 +683,37 @@ nsCacheProfilePrefObserver::ReadPrefs(nsIPrefBranch* branch)
if (directory)
mDiskCacheParentDirectory = do_QueryInterface(directory, &rv);
}
+ if (mDiskCacheParentDirectory) {
+ PRBool firstSmartSizeRun;
+ rv = branch->GetBoolPref(DISK_CACHE_SMART_SIZE_FIRST_RUN_PREF,
+ &firstSmartSizeRun);
+ if (NS_FAILED(rv)) firstSmartSizeRun = PR_FALSE;
+ if (PermittedToSmartSize(branch, firstSmartSizeRun)) {
+ // Prevent unnecessary eviction before smart size event returns
+ if (!firstSmartSizeRun) {
+ PRInt32 oldSmartSize;
+ rv = branch->GetIntPref(DISK_CACHE_SMART_SIZE_PREF,
+ &oldSmartSize);
+ mDiskCacheCapacity = oldSmartSize;
+ } else {
+ rv = branch->SetIntPref(DISK_CACHE_CAPACITY_PREF,
+ MAX_SIZE / 1024);
+ if (NS_FAILED(rv)) NS_WARNING("Failed setting capacity pref");
+ }
+ nsCOMPtr event =
+ new nsGetSmartSizeEvent(!!firstSmartSizeRun, branch, this);
+ rv = nsCacheService::DispatchToCacheIOThread(event);
+ if (NS_FAILED(rv)) mDiskCacheCapacity = BASE_LINE;
+ }
+
+ if (firstSmartSizeRun) {
+ // It is no longer our first run
+ rv = branch->SetBoolPref(DISK_CACHE_SMART_SIZE_FIRST_RUN_PREF,
+ PR_FALSE);
+ if (NS_FAILED(rv))
+ NS_WARNING("Failed setting first_run pref in ReadPrefs.");
+ }
+ }
#endif // !NECKO_DISK_CACHE
#ifdef NECKO_OFFLINE_CACHE
@@ -513,6 +771,13 @@ nsCacheProfilePrefObserver::ReadPrefs(nsIPrefBranch* branch)
return rv;
}
+nsresult
+nsCacheService::DispatchToCacheIOThread(nsIRunnable* event)
+{
+ if (!gService->mCacheIOThread) return NS_ERROR_NOT_AVAILABLE;
+ return gService->mCacheIOThread->Dispatch(event, NS_DISPATCH_NORMAL);
+}
+
PRBool
nsCacheProfilePrefObserver::DiskCacheEnabled()
diff --git a/netwerk/cache/nsCacheService.h b/netwerk/cache/nsCacheService.h
index 8ca2e5dfe3a..eb7df3289fa 100644
--- a/netwerk/cache/nsCacheService.h
+++ b/netwerk/cache/nsCacheService.h
@@ -143,6 +143,9 @@ public:
static void ReleaseObject_Locked(nsISupports * object,
nsIEventTarget * target = nsnull);
+ static nsresult DispatchToCacheIOThread(nsIRunnable* event);
+
+
/**
* Methods called by nsCacheProfilePrefObserver
*/