Landing Dave Camp's fix for bug 362446. Add quotas to DOM storage. r=enndeakin@sympatico.ca, sr=jst@mozilla.org

This commit is contained in:
jst%mozilla.org 2007-01-18 01:52:15 +00:00
parent ece1c4512f
commit bcda25eda9
6 changed files with 226 additions and 47 deletions

View File

@ -103,5 +103,7 @@
#define NS_ERROR_DOM_XPCONNECT_ACCESS_DENIED NS_ERROR_GENERATE_FAILURE(NS_ERROR_MODULE_DOM,1011)
#define NS_ERROR_DOM_BAD_URI NS_ERROR_GENERATE_FAILURE(NS_ERROR_MODULE_DOM,1012)
#define NS_ERROR_DOM_RETVAL_UNDEFINED NS_ERROR_GENERATE_FAILURE(NS_ERROR_MODULE_DOM,1013)
#define NS_ERROR_DOM_QUOTA_REACHED NS_ERROR_GENERATE_FAILURE(NS_ERROR_MODULE_DOM,1014)
#endif // nsDOMError_h__

View File

@ -99,6 +99,7 @@ DOM_MSG_DEF(NS_ERROR_DOM_PROP_ACCESS_DENIED, "Access to property denied")
DOM_MSG_DEF(NS_ERROR_DOM_XPCONNECT_ACCESS_DENIED, "Access to XPConnect service denied")
DOM_MSG_DEF(NS_ERROR_DOM_BAD_URI, "Access to restricted URI denied")
DOM_MSG_DEF(NS_ERROR_DOM_RETVAL_UNDEFINED, "Return value is undefined")
DOM_MSG_DEF(NS_ERROR_DOM_QUOTA_REACHED, "Persistent storage maximum size reached")
/* common global codes (from nsError.h) */

View File

@ -58,8 +58,11 @@ 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;
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";
@ -100,6 +103,13 @@ IsCallerSecure()
return NS_SUCCEEDED(rv) && isHttps;
}
static PRInt32
GetQuota(const nsAString &domain)
{
// FIXME: per-domain quotas?
return ((PRInt32)nsContentUtils::GetIntPref(kDefaultQuota, DEFAULT_QUOTA) * 1024);
}
nsSessionStorageEntry::nsSessionStorageEntry(KeyTypePointer aStr)
: nsStringHashKey(aStr), mItem(nsnull)
{
@ -457,7 +467,8 @@ nsDOMStorage::GetItem(const nsAString& aKey, nsIDOMStorageItem **aItem)
else if (UseDB()) {
PRBool secure;
nsAutoString value;
nsresult rv = GetDBValue(aKey, value, &secure);
nsAutoString unused;
nsresult rv = GetDBValue(aKey, value, &secure, unused);
// return null if access isn't allowed or the key wasn't found
if (rv == NS_ERROR_DOM_SECURITY_ERR || rv == NS_ERROR_DOM_NOT_FOUND_ERR)
return NS_OK;
@ -544,14 +555,16 @@ NS_IMETHODIMP nsDOMStorage::RemoveItem(const nsAString& aKey)
nsresult rv = InitDB();
NS_ENSURE_SUCCESS(rv, rv);
nsAutoString unused;
nsAutoString value;
PRBool secureItem;
rv = GetDBValue(aKey, unused, &secureItem);
nsAutoString owner;
rv = GetDBValue(aKey, value, &secureItem, owner);
if (rv == NS_ERROR_DOM_NOT_FOUND_ERR)
return NS_OK;
NS_ENSURE_SUCCESS(rv, rv);
rv = gStorageDB->RemoveKey(mDomain, aKey);
rv = gStorageDB->RemoveKey(mDomain, aKey, owner,
aKey.Length() + value.Length());
NS_ENSURE_SUCCESS(rv, rv);
mItemsCached = PR_FALSE;
@ -622,7 +635,7 @@ nsDOMStorage::CacheKeysFromDB()
nsresult
nsDOMStorage::GetDBValue(const nsAString& aKey, nsAString& aValue,
PRBool* aSecure)
PRBool* aSecure, nsAString& aOwner)
{
aValue.Truncate();
@ -634,7 +647,7 @@ nsDOMStorage::GetDBValue(const nsAString& aKey, nsAString& aValue,
NS_ENSURE_SUCCESS(rv, rv);
nsAutoString value;
rv = gStorageDB->GetKeyValue(mDomain, aKey, value, aSecure);
rv = gStorageDB->GetKeyValue(mDomain, aKey, value, aSecure, aOwner);
if (NS_FAILED(rv))
return rv;
@ -660,8 +673,32 @@ nsDOMStorage::SetDBValue(const nsAString& aKey,
nsresult rv = InitDB();
NS_ENSURE_SUCCESS(rv, rv);
nsAutoString value;
rv = gStorageDB->SetKey(mDomain, aKey, aValue, aSecure);
// Get the current domain for quota enforcement
nsCOMPtr<nsIPrincipal> subjectPrincipal;
nsContentUtils::GetSecurityManager()->
GetSubjectPrincipal(getter_AddRefs(subjectPrincipal));
nsAutoString currentDomain;
if (subjectPrincipal) {
nsCOMPtr<nsIURI> uri;
rv = subjectPrincipal->GetURI(getter_AddRefs(uri));
if (NS_SUCCEEDED(rv) && uri) {
nsCAutoString currentDomainAscii;
uri->GetAsciiHost(currentDomainAscii);
currentDomain = NS_ConvertUTF8toUTF16(currentDomainAscii);
}
if (currentDomain.IsEmpty()) {
return NS_ERROR_DOM_SECURITY_ERR;
}
} else {
currentDomain = mDomain;
}
rv = gStorageDB->SetKey(mDomain, aKey, aValue, aSecure,
currentDomain, GetQuota(currentDomain));
NS_ENSURE_SUCCESS(rv, rv);
mItemsCached = PR_FALSE;
@ -828,7 +865,7 @@ nsDOMStorageList::NamedItem(const nsAString& aDomain,
PRPackedBool sessionOnly;
if (!nsDOMStorage::CanUseStorage(uri, &sessionOnly))
return NS_ERROR_DOM_SECURITY_ERR;
rv = uri->GetAsciiHost(currentDomain);
NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_SECURITY_ERR);
}
@ -1012,7 +1049,8 @@ nsDOMStorageItem::GetSecure(PRBool* aSecure)
if (mStorage->UseDB()) {
nsAutoString value;
return mStorage->GetDBValue(mKey, value, aSecure);
nsAutoString owner;
return mStorage->GetDBValue(mKey, value, aSecure, owner);
}
*aSecure = IsSecure();
@ -1044,7 +1082,8 @@ nsDOMStorageItem::GetValue(nsAString& aValue)
if (mStorage->UseDB()) {
// GetDBValue checks the secure state so no need to do it here
PRBool secure;
nsresult rv = mStorage->GetDBValue(mKey, aValue, &secure);
nsAutoString unused;
nsresult rv = mStorage->GetDBValue(mKey, aValue, &secure, unused);
return (rv == NS_ERROR_DOM_NOT_FOUND_ERR) ? NS_OK : rv;
}

View File

@ -139,7 +139,10 @@ public:
// retrieve the value and secure state corresponding to a key out of storage.
nsresult
GetDBValue(const nsAString& aKey, nsAString& aValue, PRBool* aSecure);
GetDBValue(const nsAString& aKey,
nsAString& aValue,
PRBool* aSecure,
nsAString& aOwner);
// set the value corresponding to a key in the storage. If
// aSecure is false, then attempts to modify a secure value

View File

@ -73,28 +73,63 @@ nsDOMStorageDB::Init()
NS_ENSURE_SUCCESS(rv, rv);
PRBool exists;
rv = mConnection->TableExists(NS_LITERAL_CSTRING("moz_webappsstore"), &exists);
rv = mConnection->TableExists(NS_LITERAL_CSTRING("webappsstore"), &exists);
NS_ENSURE_SUCCESS(rv, rv);
if (! exists) {
rv = mConnection->ExecuteSimpleSQL(
NS_LITERAL_CSTRING("CREATE TABLE moz_webappsstore ("
NS_LITERAL_CSTRING("CREATE TABLE webappsstore ("
"domain TEXT, "
"key TEXT, "
"value TEXT, "
"secure INTEGER) "));
"secure INTEGER, "
"owner TEXT)"));
NS_ENSURE_SUCCESS(rv, rv);
}
rv = mConnection->TableExists(NS_LITERAL_CSTRING("moz_webappsstore"),
&exists);
NS_ENSURE_SUCCESS(rv, rv);
if (exists) {
// upgrade an old store
// create a temporary index to handle dup checking
rv = mConnection->ExecuteSimpleSQL(
NS_LITERAL_CSTRING("CREATE UNIQUE INDEX webappsstore_tmp "
" ON webappsstore(domain, key)"));
// if the index can't be created, there are dup domain/key combos
// in moz_webappstore2, which indicates a bug elsewhere. Fail to upgrade
// in this case
if (NS_SUCCEEDED(rv)) {
rv = mConnection->ExecuteSimpleSQL(
NS_LITERAL_CSTRING("INSERT OR IGNORE INTO "
"webappsstore(domain, key, value, secure, owner) "
"SELECT domain, key, value, secure, domain "
"FROM moz_webappsstore"));
// try to drop the index even in case of an error
mConnection->ExecuteSimpleSQL(
NS_LITERAL_CSTRING("DROP INDEX webappsstore_tmp"));
NS_ENSURE_SUCCESS(rv, rv);
rv = mConnection->ExecuteSimpleSQL(
NS_LITERAL_CSTRING("DROP TABLE moz_webappsstore"));
NS_ENSURE_SUCCESS(rv, rv);
}
}
// retrieve all keys associated with a domain
rv = mConnection->CreateStatement(
NS_LITERAL_CSTRING("SELECT key, secure FROM moz_webappsstore "
NS_LITERAL_CSTRING("SELECT key, secure FROM webappsstore "
"WHERE domain = ?1"),
getter_AddRefs(mGetAllKeysStatement));
NS_ENSURE_SUCCESS(rv, rv);
// retrieve a value given a domain and a key
rv = mConnection->CreateStatement(
NS_LITERAL_CSTRING("SELECT value, secure FROM moz_webappsstore "
NS_LITERAL_CSTRING("SELECT value, secure, owner FROM webappsstore "
"WHERE domain = ?1 "
"AND key = ?2"),
getter_AddRefs(mGetKeyValueStatement));
@ -102,22 +137,24 @@ nsDOMStorageDB::Init()
// insert a new key
rv = mConnection->CreateStatement(
NS_LITERAL_CSTRING("INSERT INTO moz_webappsstore values (?1, ?2, ?3, ?4)"),
NS_LITERAL_CSTRING("INSERT INTO "
"webappsstore(domain, key, value, secure, owner) "
"VALUES (?1, ?2, ?3, ?4, ?5)"),
getter_AddRefs(mInsertKeyStatement));
NS_ENSURE_SUCCESS(rv, rv);
// update an existing key
rv = mConnection->CreateStatement(
NS_LITERAL_CSTRING("UPDATE moz_webappsstore "
"SET value = ?1, secure = ?2 "
"WHERE domain = ?3 "
"AND key = ?4 "),
NS_LITERAL_CSTRING("UPDATE webappsstore "
"SET value = ?1, secure = ?2, owner = ?3"
"WHERE domain = ?4 "
"AND key = ?5 "),
getter_AddRefs(mUpdateKeyStatement));
NS_ENSURE_SUCCESS(rv, rv);
// update the secure status of an existing key
rv = mConnection->CreateStatement(
NS_LITERAL_CSTRING("UPDATE moz_webappsstore "
NS_LITERAL_CSTRING("UPDATE webappsstore "
"SET secure = ?1 "
"WHERE domain = ?2 "
"AND key = ?3 "),
@ -126,7 +163,7 @@ nsDOMStorageDB::Init()
// remove a key
rv = mConnection->CreateStatement(
NS_LITERAL_CSTRING("DELETE FROM moz_webappsstore "
NS_LITERAL_CSTRING("DELETE FROM webappsstore "
"WHERE domain = ?1 "
"AND key = ?2"),
getter_AddRefs(mRemoveKeyStatement));
@ -134,8 +171,16 @@ nsDOMStorageDB::Init()
// remove all keys
rv = mConnection->CreateStatement(
NS_LITERAL_CSTRING("DELETE FROM moz_webappsstore"),
NS_LITERAL_CSTRING("DELETE FROM webappsstore"),
getter_AddRefs(mRemoveAllStatement));
NS_ENSURE_SUCCESS(rv, rv);
// check the usage for a given owner
rv = mConnection->CreateStatement(
NS_LITERAL_CSTRING("SELECT SUM(LENGTH(key) + LENGTH(value)) "
"FROM webappsstore "
"WHERE owner = ?1"),
getter_AddRefs(mGetUsageStatement));
return rv;
}
@ -179,7 +224,8 @@ nsresult
nsDOMStorageDB::GetKeyValue(const nsAString& aDomain,
const nsAString& aKey,
nsAString& aValue,
PRBool* aSecure)
PRBool* aSecure,
nsAString& aOwner)
{
mozStorageStatementScoper scope(mGetKeyValueStatement);
@ -199,6 +245,9 @@ nsDOMStorageDB::GetKeyValue(const nsAString& aDomain,
rv = mGetKeyValueStatement->GetInt32(1, &secureInt);
NS_ENSURE_SUCCESS(rv, rv);
rv = mGetKeyValueStatement->GetString(2, aOwner);
NS_ENSURE_SUCCESS(rv, rv);
}
else {
rv = NS_ERROR_DOM_NOT_FOUND_ERR;
@ -213,11 +262,26 @@ nsresult
nsDOMStorageDB::SetKey(const nsAString& aDomain,
const nsAString& aKey,
const nsAString& aValue,
PRBool aSecure)
PRBool aSecure,
const nsAString& aOwner,
PRInt32 aQuota)
{
mozStorageStatementScoper scope(mGetKeyValueStatement);
PRInt32 usage = 0;
nsresult rv;
if (!aOwner.IsEmpty()) {
if (aOwner == mCachedOwner) {
usage = mCachedUsage;
} else {
rv = GetUsage(aOwner, &usage);
NS_ENSURE_SUCCESS(rv, rv);
}
}
nsresult rv = mGetKeyValueStatement->BindStringParameter(0, aDomain);
usage += aKey.Length() + aValue.Length();
rv = mGetKeyValueStatement->BindStringParameter(0, aDomain);
NS_ENSURE_SUCCESS(rv, rv);
rv = mGetKeyValueStatement->BindStringParameter(1, aKey);
NS_ENSURE_SUCCESS(rv, rv);
@ -235,34 +299,67 @@ nsDOMStorageDB::SetKey(const nsAString& aDomain,
return NS_ERROR_DOM_SECURITY_ERR;
}
mGetKeyValueStatement->Reset();
nsAutoString previousOwner;
rv = mGetKeyValueStatement->GetString(2, previousOwner);
NS_ENSURE_SUCCESS(rv, rv);
if (previousOwner == aOwner) {
nsAutoString previousValue;
rv = mGetKeyValueStatement->GetString(0, previousValue);
NS_ENSURE_SUCCESS(rv, rv);
usage -= aKey.Length() + previousValue.Length();
}
mGetKeyValueStatement->Reset();
if (usage > aQuota) {
return NS_ERROR_DOM_QUOTA_REACHED;
}
mozStorageStatementScoper scopeupdate(mUpdateKeyStatement);
rv = mUpdateKeyStatement->BindStringParameter(0, aValue);
NS_ENSURE_SUCCESS(rv, rv);
rv = mUpdateKeyStatement->BindInt32Parameter(1, aSecure);
NS_ENSURE_SUCCESS(rv, rv);
rv = mUpdateKeyStatement->BindStringParameter(2, aDomain);
rv = mUpdateKeyStatement->BindStringParameter(2, aOwner);
NS_ENSURE_SUCCESS(rv, rv);
rv = mUpdateKeyStatement->BindStringParameter(3, aKey);
rv = mUpdateKeyStatement->BindStringParameter(3, aDomain);
NS_ENSURE_SUCCESS(rv, rv);
rv = mUpdateKeyStatement->BindStringParameter(4, aKey);
NS_ENSURE_SUCCESS(rv, rv);
return mUpdateKeyStatement->Execute();
rv = mUpdateKeyStatement->Execute();
NS_ENSURE_SUCCESS(rv, rv);
}
else {
if (usage > aQuota) {
return NS_ERROR_DOM_QUOTA_REACHED;
}
mozStorageStatementScoper scopeinsert(mInsertKeyStatement);
rv = mInsertKeyStatement->BindStringParameter(0, aDomain);
NS_ENSURE_SUCCESS(rv, rv);
rv = mInsertKeyStatement->BindStringParameter(1, aKey);
NS_ENSURE_SUCCESS(rv, rv);
rv = mInsertKeyStatement->BindStringParameter(2, aValue);
NS_ENSURE_SUCCESS(rv, rv);
rv = mInsertKeyStatement->BindInt32Parameter(3, aSecure);
NS_ENSURE_SUCCESS(rv, rv);
rv = mInsertKeyStatement->BindStringParameter(4, aOwner);
NS_ENSURE_SUCCESS(rv, rv);
rv = mInsertKeyStatement->Execute();
NS_ENSURE_SUCCESS(rv, rv);
}
mozStorageStatementScoper scopeinsert(mInsertKeyStatement);
if (!aOwner.IsEmpty()) {
mCachedOwner = aOwner;
mCachedUsage = usage;
}
rv = mInsertKeyStatement->BindStringParameter(0, aDomain);
NS_ENSURE_SUCCESS(rv, rv);
rv = mInsertKeyStatement->BindStringParameter(1, aKey);
NS_ENSURE_SUCCESS(rv, rv);
rv = mInsertKeyStatement->BindStringParameter(2, aValue);
NS_ENSURE_SUCCESS(rv, rv);
rv = mInsertKeyStatement->BindInt32Parameter(3, aSecure);
NS_ENSURE_SUCCESS(rv, rv);
return mInsertKeyStatement->Execute();
return NS_OK;
}
nsresult
@ -301,10 +398,16 @@ nsDOMStorageDB::SetSecure(const nsAString& aDomain,
nsresult
nsDOMStorageDB::RemoveKey(const nsAString& aDomain,
const nsAString& aKey)
const nsAString& aKey,
const nsAString& aOwner,
PRInt32 aKeyUsage)
{
mozStorageStatementScoper scope(mRemoveKeyStatement);
if (aOwner == mCachedOwner) {
mCachedUsage -= aKeyUsage;
}
nsresult rv = mRemoveKeyStatement->BindStringParameter(0, aDomain);
NS_ENSURE_SUCCESS(rv, rv);
rv = mRemoveKeyStatement->BindStringParameter(1, aKey);
@ -319,3 +422,23 @@ nsDOMStorageDB::RemoveAll()
mozStorageStatementScoper scope(mRemoveAllStatement);
return mRemoveAllStatement->Execute();
}
nsresult
nsDOMStorageDB::GetUsage(const nsAString &aOwner, PRInt32 *aUsage)
{
mozStorageStatementScoper scope(mGetUsageStatement);
nsresult rv = mGetUsageStatement->BindStringParameter(0, aOwner);
NS_ENSURE_SUCCESS(rv, rv);
PRBool exists;
rv = mGetUsageStatement->ExecuteStep(&exists);
NS_ENSURE_SUCCESS(rv, rv);
if (!exists) {
*aUsage = 0;
return NS_OK;
}
return mGetUsageStatement->GetInt32(0, aUsage);
}

View File

@ -73,7 +73,8 @@ public:
GetKeyValue(const nsAString& aDomain,
const nsAString& aKey,
nsAString& aValue,
PRBool* aSecure);
PRBool* aSecure,
nsAString& aOwner);
/**
* Set the value and secure flag for a key in storage.
@ -82,7 +83,9 @@ public:
SetKey(const nsAString& aDomain,
const nsAString& aKey,
const nsAString& aValue,
PRBool aSecure);
PRBool aSecure,
const nsAString& aOwner,
PRInt32 aQuota);
/**
* Set the secure flag for a key in storage. Does nothing if the key was
@ -98,7 +101,9 @@ public:
*/
nsresult
RemoveKey(const nsAString& aDomain,
const nsAString& aKey);
const nsAString& aKey,
const nsAString& aOwner,
PRInt32 aKeyUsage);
/**
* Removes all keys from storage. Used when clearing storage.
@ -108,6 +113,8 @@ public:
protected:
nsresult GetUsage(const nsAString &aOwner, PRInt32 *aUsage);
nsCOMPtr<mozIStorageConnection> mConnection;
nsCOMPtr<mozIStorageStatement> mGetAllKeysStatement;
@ -117,6 +124,10 @@ protected:
nsCOMPtr<mozIStorageStatement> mSetSecureStatement;
nsCOMPtr<mozIStorageStatement> mRemoveKeyStatement;
nsCOMPtr<mozIStorageStatement> mRemoveAllStatement;
nsCOMPtr<mozIStorageStatement> mGetUsageStatement;
nsAutoString mCachedOwner;
PRInt32 mCachedUsage;
};
#endif /* nsDOMStorageDB_h___ */