Bug 397416: Raise globalStorage quota for domains with the offline-app permission. r=enndeakin, sr=dveditz, blocking1.9=sicking
--- a/dom/src/storage/Makefile.in
+++ b/dom/src/storage/Makefile.in
@@ -55,16 +55,17 @@ REQUIRES = xpcom \
js \
layout \
locale \
necko \
pref \
unicharutil \
widget \
xpconnect \
+ prefetch \
$(NULL)
ifdef MOZ_STORAGE
REQUIRES += storage
endif
CPPSRCS = \
nsDOMStorage.cpp \
--- a/dom/src/storage/nsDOMStorage.cpp
+++ b/dom/src/storage/nsDOMStorage.cpp
@@ -49,28 +49,36 @@
#include "nsIURI.h"
#include "nsReadableUtils.h"
#include "nsIObserverService.h"
#include "nsNetUtil.h"
#include "nsIPrefBranch.h"
#include "nsICookiePermission.h"
#include "nsIPermissionManager.h"
#include "nsCycleCollectionParticipant.h"
+#include "nsIOfflineCacheUpdate.h"
+#include "nsIJSContextStack.h"
static const PRUint32 ASK_BEFORE_ACCEPT = 1;
static const PRUint32 ACCEPT_SESSION = 2;
static const PRUint32 BEHAVIOR_REJECT = 2;
static const PRUint32 DEFAULT_QUOTA = 5 * 1024;
+// Be generous with offline apps by default...
+static const PRUint32 DEFAULT_OFFLINE_APP_QUOTA = 200 * 1024;
+// ... but warn if it goes over this amount
+static const PRUint32 DEFAULT_OFFLINE_WARN_QUOTA = 50 * 1024;
static const char kPermissionType[] = "cookie";
static const char kStorageEnabled[] = "dom.storage.enabled";
static const char kDefaultQuota[] = "dom.storage.default_quota";
static const char kCookiesBehavior[] = "network.cookie.cookieBehavior";
static const char kCookiesLifetimePolicy[] = "network.cookie.lifetimePolicy";
+static const char kOfflineAppWarnQuota[] = "offline-apps.quota.warn";
+static const char kOfflineAppQuota[] = "offline-apps.quota.max";
//
// Helper that tells us whether the caller is secure or not.
//
static PRBool
IsCallerSecure()
{
@@ -99,21 +107,54 @@ IsCallerSecure()
}
PRBool isHttps = PR_FALSE;
nsresult rv = innerUri->SchemeIs("https", &isHttps);
return NS_SUCCEEDED(rv) && isHttps;
}
-static PRInt32
-GetQuota(const nsAString &domain)
+
+// Returns two quotas - A hard limit for which adding data will be an error,
+// and a limit after which a warning event will be sent to the observer
+// service. The warn limit may be -1, in which case there will be no warning.
+static void
+GetQuota(const nsAString &aDomain, PRInt32 *aQuota, PRInt32 *aWarnQuota)
{
+ // Fake a URI for the permission manager
+ nsCOMPtr<nsIURI> uri;
+ NS_NewURI(getter_AddRefs(uri), NS_LITERAL_STRING("http://") + aDomain);
+
+ if (uri) {
+ nsCOMPtr<nsIPermissionManager> permissionManager =
+ do_GetService(NS_PERMISSIONMANAGER_CONTRACTID);
+
+ PRUint32 perm;
+ if (permissionManager &&
+ NS_SUCCEEDED(permissionManager->TestExactPermission(uri, "offline-app", &perm)) &&
+ perm != nsIPermissionManager::UNKNOWN_ACTION &&
+ perm != nsIPermissionManager::DENY_ACTION) {
+ // This is an offline app, give more space by default.
+ *aQuota = ((PRInt32)nsContentUtils::GetIntPref(kOfflineAppQuota,
+ DEFAULT_OFFLINE_WARN_QUOTA * 1024));
+
+ if (perm == nsIOfflineCacheUpdateService::ALLOW_NO_WARN) {
+ *aWarnQuota = -1;
+ } else {
+ *aWarnQuota = ((PRInt32)nsContentUtils::GetIntPref(kOfflineAppWarnQuota,
+ DEFAULT_OFFLINE_WARN_QUOTA) * 1024);
+ }
+ return;
+ }
+ }
+
// FIXME: per-domain quotas?
- return ((PRInt32)nsContentUtils::GetIntPref(kDefaultQuota, DEFAULT_QUOTA) * 1024);
+ *aQuota = ((PRInt32)nsContentUtils::GetIntPref(kDefaultQuota,
+ DEFAULT_QUOTA) * 1024);
+ *aWarnQuota = -1;
}
nsSessionStorageEntry::nsSessionStorageEntry(KeyTypePointer aStr)
: nsStringHashKey(aStr), mItem(nsnull)
{
}
nsSessionStorageEntry::nsSessionStorageEntry(const nsSessionStorageEntry& aToCopy)
@@ -766,22 +807,47 @@ nsDOMStorage::SetDBValue(const nsAString
currentDomain = mDomain;
else
return NS_ERROR_DOM_SECURITY_ERR;
}
} else {
currentDomain = mDomain;
}
+ PRInt32 quota;
+ PRInt32 warnQuota;
+ GetQuota(currentDomain, "a, &warnQuota);
+
+ PRInt32 usage;
rv = gStorageDB->SetKey(mDomain, aKey, aValue, aSecure,
- currentDomain, GetQuota(currentDomain));
+ currentDomain, quota, &usage);
NS_ENSURE_SUCCESS(rv, rv);
mItemsCached = PR_FALSE;
+ if (warnQuota >= 0 && usage > warnQuota) {
+ // try to include the window that exceeded the warn quota
+ nsCOMPtr<nsIDOMWindow> window;
+ JSContext *cx;
+ nsCOMPtr<nsIJSContextStack> stack =
+ do_GetService("@mozilla.org/js/xpc/ContextStack;1");
+ if (stack && NS_SUCCEEDED(stack->Peek(&cx)) && cx) {
+ nsCOMPtr<nsIScriptContext> scriptContext;
+ scriptContext = GetScriptContextFromJSContext(cx);
+ if (scriptContext) {
+ window = do_QueryInterface(scriptContext->GetGlobalObject());
+ }
+ }
+
+ nsCOMPtr<nsIObserverService> os =
+ do_GetService("@mozilla.org/observer-service;1");
+ os->NotifyObservers(window, "dom-storage-warn-quota-exceeded",
+ currentDomain.get());
+ }
+
BroadcastChangeNotification();
#endif
return NS_OK;
}
nsresult
nsDOMStorage::SetSecure(const nsAString& aKey, PRBool aSecure)
--- a/dom/src/storage/nsDOMStorageDB.cpp
+++ b/dom/src/storage/nsDOMStorageDB.cpp
@@ -266,17 +266,18 @@ nsDOMStorageDB::GetKeyValue(const nsAStr
}
nsresult
nsDOMStorageDB::SetKey(const nsAString& aDomain,
const nsAString& aKey,
const nsAString& aValue,
PRBool aSecure,
const nsAString& aOwner,
- PRInt32 aQuota)
+ PRInt32 aQuota,
+ PRInt32 *aNewUsage)
{
mozStorageStatementScoper scope(mGetKeyValueStatement);
PRInt32 usage = 0;
nsresult rv;
if (!aOwner.IsEmpty()) {
rv = GetUsage(aOwner, &usage);
NS_ENSURE_SUCCESS(rv, rv);
@@ -357,16 +358,18 @@ nsDOMStorageDB::SetKey(const nsAString&
NS_ENSURE_SUCCESS(rv, rv);
}
if (!aOwner.IsEmpty()) {
mCachedOwner = aOwner;
mCachedUsage = usage;
}
+ *aNewUsage = usage;
+
return NS_OK;
}
nsresult
nsDOMStorageDB::SetSecure(const nsAString& aDomain,
const nsAString& aKey,
const PRBool aSecure)
{
--- a/dom/src/storage/nsDOMStorageDB.h
+++ b/dom/src/storage/nsDOMStorageDB.h
@@ -80,17 +80,18 @@ public:
* Set the value and secure flag for a key in storage.
*/
nsresult
SetKey(const nsAString& aDomain,
const nsAString& aKey,
const nsAString& aValue,
PRBool aSecure,
const nsAString& aOwner,
- PRInt32 aQuota);
+ PRInt32 aQuota,
+ PRInt32* aNewUsage);
/**
* Set the secure flag for a key in storage. Does nothing if the key was
* not found.
*/
nsresult
SetSecure(const nsAString& aDomain,
const nsAString& aKey,
--- a/modules/libpref/src/init/all.js
+++ b/modules/libpref/src/init/all.js
@@ -66,16 +66,24 @@ pref("browser.cache.memory.enable",
pref("browser.cache.disk_cache_ssl", false);
// 0 = once-per-session, 1 = each-time, 2 = never, 3 = when-appropriate/automatically
pref("browser.cache.check_doc_frequency", 3);
pref("browser.cache.offline.enable", true);
// offline cache capacity in kilobytes
pref("browser.cache.offline.capacity", 10240);
+// offline apps should be limited to this much data in global storage
+// (in kilobytes)
+pref("offline-apps.quota.max", 204800);
+
+// the user should be warned if offline app disk usage exceeds this amount
+// (in kilobytes)
+pref("offline-apps.quota.warn", 51200);
+
// Fastback caching - if this pref is negative, then we calculate the number
// of content viewers to cache based on the amount of available memory.
pref("browser.sessionhistory.max_total_viewers", -1);
pref("browser.display.use_document_fonts", 1); // 0 = never, 1 = quick, 2 = always
pref("browser.display.use_document_colors", true);
pref("browser.display.use_system_colors", false);
pref("browser.display.foreground_color", "#000000");
--- a/uriloader/prefetch/nsIOfflineCacheUpdate.idl
+++ b/uriloader/prefetch/nsIOfflineCacheUpdate.idl
@@ -200,16 +200,29 @@ interface nsIOfflineCacheUpdate : nsISup
* the observer to remove.
*/
void removeObserver(in nsIOfflineCacheUpdateObserver aObserver);
};
[scriptable, uuid(3abee04b-5bbb-4405-b659-35f780e38da0)]
interface nsIOfflineCacheUpdateService : nsISupports {
/**
+ * Constants for the offline-app permission.
+ *
+ * XXX: This isn't a great place for this, but it's really the only
+ * private offline-app-related interface
+ */
+
+ /**
+ * Allow the domain to use offline APIs, and don't warn about excessive
+ * usage.
+ */
+ const unsigned long ALLOW_NO_WARN = 3;
+
+ /**
* Access to the list of cache updates that have been scheduled.
*/
readonly attribute unsigned long numUpdates;
nsIOfflineCacheUpdate getUpdate(in unsigned long index);
/**
* Schedule a cache update for a given offline manifest. If an
* existing update is scheduled or running, that update will be returned.