--- a/browser/components/migration/src/nsOperaProfileMigrator.cpp
+++ b/browser/components/migration/src/nsOperaProfileMigrator.cpp
@@ -879,17 +879,19 @@ nsOperaCookieMigrator::AddCookieOverride
nsCOMPtr<nsIURI> uri(do_CreateInstance("@mozilla.org/network/standard-url;1"));
if (!uri)
return NS_ERROR_OUT_OF_MEMORY;
uri->SetHost(domain);
rv = aManager->Add(uri, "cookie",
(mCurrHandlingInfo == 1 || mCurrHandlingInfo == 3)
? (PRUint32) nsIPermissionManager::ALLOW_ACTION
- : (PRUint32) nsIPermissionManager::DENY_ACTION);
+ : (PRUint32) nsIPermissionManager::DENY_ACTION,
+ nsIPermissionManager::EXPIRE_NEVER,
+ 0);
mCurrHandlingInfo = 0;
return rv;
}
nsresult
--- a/extensions/cookie/nsCookiePermission.cpp
+++ b/extensions/cookie/nsCookiePermission.cpp
@@ -184,17 +184,18 @@ NS_IMETHODIMP
nsCookiePermission::SetAccess(nsIURI *aURI,
nsCookieAccess aAccess)
{
//
// NOTE: nsCookieAccess values conveniently match up with
// the permission codes used by nsIPermissionManager.
// this is nice because it avoids conversion code.
//
- return mPermMgr->Add(aURI, kPermissionType, aAccess);
+ return mPermMgr->Add(aURI, kPermissionType, aAccess,
+ nsIPermissionManager::EXPIRE_NEVER, 0);
}
NS_IMETHODIMP
nsCookiePermission::CanAccess(nsIURI *aURI,
nsIChannel *aChannel,
nsCookieAccess *aResult)
{
#ifdef MOZ_MAIL_NEWS
@@ -356,23 +357,26 @@ nsCookiePermission::CanSetCookie(nsIURI
if (NS_FAILED(rv)) return rv;
if (*aResult == nsICookiePromptService::ACCEPT_SESSION_COOKIE)
*aIsSession = PR_TRUE;
if (rememberDecision) {
switch (*aResult) {
case nsICookiePromptService::DENY_COOKIE:
- mPermMgr->Add(aURI, kPermissionType, (PRUint32) nsIPermissionManager::DENY_ACTION);
+ mPermMgr->Add(aURI, kPermissionType, (PRUint32) nsIPermissionManager::DENY_ACTION,
+ nsIPermissionManager::EXPIRE_NEVER, 0);
break;
case nsICookiePromptService::ACCEPT_COOKIE:
- mPermMgr->Add(aURI, kPermissionType, (PRUint32) nsIPermissionManager::ALLOW_ACTION);
+ mPermMgr->Add(aURI, kPermissionType, (PRUint32) nsIPermissionManager::ALLOW_ACTION,
+ nsIPermissionManager::EXPIRE_NEVER, 0);
break;
case nsICookiePromptService::ACCEPT_SESSION_COOKIE:
- mPermMgr->Add(aURI, kPermissionType, nsICookiePermission::ACCESS_SESSION);
+ mPermMgr->Add(aURI, kPermissionType, nsICookiePermission::ACCESS_SESSION,
+ nsIPermissionManager::EXPIRE_NEVER, 0);
break;
default:
break;
}
}
} else {
// we're not prompting, so we must be limiting the lifetime somehow
// if it's a session cookie, we do nothing
--- a/extensions/cookie/nsPermission.cpp
+++ b/extensions/cookie/nsPermission.cpp
@@ -38,20 +38,24 @@
#include "nsPermission.h"
// nsPermission Implementation
NS_IMPL_ISUPPORTS1(nsPermission, nsIPermission)
nsPermission::nsPermission(const nsACString &aHost,
const nsACString &aType,
- PRUint32 aCapability)
+ PRUint32 aCapability,
+ PRUint32 aExpireType,
+ PRInt64 aExpireTime)
: mHost(aHost)
, mType(aType)
, mCapability(aCapability)
+ , mExpireType(aExpireType)
+ , mExpireTime(aExpireTime)
{
}
nsPermission::~nsPermission()
{
}
NS_IMETHODIMP
@@ -69,8 +73,22 @@ nsPermission::GetType(nsACString &aType)
}
NS_IMETHODIMP
nsPermission::GetCapability(PRUint32 *aCapability)
{
*aCapability = mCapability;
return NS_OK;
}
+
+NS_IMETHODIMP
+nsPermission::GetExpireType(PRUint32 *aExpireType)
+{
+ *aExpireType = mExpireType;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsPermission::GetExpireTime(PRInt64 *aExpireTime)
+{
+ *aExpireTime = mExpireTime;
+ return NS_OK;
+}
--- a/extensions/cookie/nsPermission.h
+++ b/extensions/cookie/nsPermission.h
@@ -45,18 +45,25 @@
class nsPermission : public nsIPermission
{
public:
// nsISupports
NS_DECL_ISUPPORTS
NS_DECL_NSIPERMISSION
- nsPermission(const nsACString &aHost, const nsACString &aType, PRUint32 aCapability);
+ nsPermission(const nsACString &aHost,
+ const nsACString &aType,
+ PRUint32 aCapability,
+ PRUint32 aExpireType,
+ PRInt64 aExpireTime);
+
virtual ~nsPermission();
protected:
nsCString mHost;
nsCString mType;
PRUint32 mCapability;
+ PRUint32 mExpireType;
+ PRInt64 mExpireTime;
};
#endif // nsPermission_h__
--- a/extensions/cookie/nsPermissionManager.cpp
+++ b/extensions/cookie/nsPermissionManager.cpp
@@ -90,17 +90,17 @@ nsHostEntry::nsHostEntry(const nsHostEnt
, mPermissions(toCopy.mPermissions)
{
}
////////////////////////////////////////////////////////////////////////////////
// nsPermissionManager Implementation
static const char kPermissionsFileName[] = "permissions.sqlite";
-#define HOSTS_SCHEMA_VERSION 1
+#define HOSTS_SCHEMA_VERSION 2
static const char kHostpermFileName[] = "hostperm.1";
static const char kPermissionChangeNotification[] = PERM_CHANGE_NOTIFICATION;
NS_IMPL_ISUPPORTS3(nsPermissionManager, nsIPermissionManager, nsIObserver, nsISupportsWeakReference)
nsPermissionManager::nsPermissionManager()
@@ -193,16 +193,34 @@ nsPermissionManager::InitDB(PRBool aRemo
NS_ENSURE_SUCCESS(rv, rv);
switch (dbSchemaVersion) {
// upgrading.
// every time you increment the database schema, you need to implement
// the upgrading code from the previous version to the new one.
// fall through to current version
+ case 1:
+ {
+ // previous non-expiry version of database. Upgrade it by adding the
+ // expiration columns
+ rv = mDBConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
+ "ALTER TABLE moz_hosts ADD expireType INTEGER"));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ rv = mDBConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
+ "ALTER TABLE moz_hosts ADD expireTime INTEGER"));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ rv = mDBConn->SetSchemaVersion(HOSTS_SCHEMA_VERSION);
+ NS_ENSURE_SUCCESS(rv, rv);
+ }
+
+ // fall through to the next upgrade
+
// current version.
case HOSTS_SCHEMA_VERSION:
break;
case 0:
{
NS_WARNING("couldn't get schema version!");
@@ -222,17 +240,18 @@ nsPermissionManager::InitDB(PRBool aRemo
// blow away the table and start from scratch! if you change the way
// a column is interpreted, make sure you also change its name so this
// check will catch it.
default:
{
// check if all the expected columns exist
nsCOMPtr<mozIStorageStatement> stmt;
rv = mDBConn->CreateStatement(NS_LITERAL_CSTRING(
- "SELECT host, type, permission FROM moz_hosts"), getter_AddRefs(stmt));
+ "SELECT host, type, permission, expireType, expireTime FROM moz_hosts"),
+ getter_AddRefs(stmt));
if (NS_SUCCEEDED(rv))
break;
// our columns aren't there - drop the table!
rv = mDBConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING("DROP TABLE moz_hosts"));
NS_ENSURE_SUCCESS(rv, rv);
rv = CreateTable();
@@ -243,28 +262,29 @@ nsPermissionManager::InitDB(PRBool aRemo
}
// make operations on the table asynchronous, for performance
mDBConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING("PRAGMA synchronous = OFF"));
// cache frequently used statements (for insertion, deletion, and updating)
rv = mDBConn->CreateStatement(NS_LITERAL_CSTRING(
"INSERT INTO moz_hosts "
- "(id, host, type, permission) "
- "VALUES (?1, ?2, ?3, ?4)"), getter_AddRefs(mStmtInsert));
+ "(id, host, type, permission, expireType, expireTime) "
+ "VALUES (?1, ?2, ?3, ?4, ?5, ?6)"), getter_AddRefs(mStmtInsert));
NS_ENSURE_SUCCESS(rv, rv);
rv = mDBConn->CreateStatement(NS_LITERAL_CSTRING(
"DELETE FROM moz_hosts "
"WHERE id = ?1"), getter_AddRefs(mStmtDelete));
NS_ENSURE_SUCCESS(rv, rv);
rv = mDBConn->CreateStatement(NS_LITERAL_CSTRING(
"UPDATE moz_hosts "
- "SET permission = ?2 WHERE id = ?1"), getter_AddRefs(mStmtUpdate));
+ "SET permission = ?2, expireType= ?3, expireTime = ?4 WHERE id = ?1"),
+ getter_AddRefs(mStmtUpdate));
NS_ENSURE_SUCCESS(rv, rv);
// check whether to import or just read in the db
if (tableExists)
return Read();
return Import();
}
@@ -279,41 +299,57 @@ nsPermissionManager::CreateTable()
// create the table
return mDBConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
"CREATE TABLE moz_hosts ("
" id INTEGER PRIMARY KEY"
",host TEXT"
",type TEXT"
",permission INTEGER"
+ ",expireType INTEGER"
+ ",expireTime INTEGER"
")"));
}
NS_IMETHODIMP
nsPermissionManager::Add(nsIURI *aURI,
const char *aType,
- PRUint32 aPermission)
+ PRUint32 aPermission,
+ PRUint32 aExpireType,
+ PRInt64 aExpireTime)
{
NS_ENSURE_ARG_POINTER(aURI);
NS_ENSURE_ARG_POINTER(aType);
+ NS_ENSURE_TRUE(aExpireType == nsIPermissionManager::EXPIRE_NEVER ||
+ aExpireType == nsIPermissionManager::EXPIRE_TIME ||
+ aExpireType == nsIPermissionManager::EXPIRE_SESSION,
+ NS_ERROR_INVALID_ARG);
nsresult rv;
+ // Skip addition if the permission is already expired.
+ if (aExpireType == nsIPermissionManager::EXPIRE_TIME &&
+ aExpireTime < PR_Now() / 1000)
+ return NS_OK;
+
nsCAutoString host;
rv = GetHost(aURI, host);
NS_ENSURE_SUCCESS(rv, rv);
- return AddInternal(host, nsDependentCString(aType), aPermission, 0, eNotify, eWriteToDB);
+ return AddInternal(host, nsDependentCString(aType), aPermission, 0,
+ aExpireType, aExpireTime, eNotify, eWriteToDB);
}
nsresult
nsPermissionManager::AddInternal(const nsAFlatCString &aHost,
const nsAFlatCString &aType,
PRUint32 aPermission,
PRInt64 aID,
+ PRUint32 aExpireType,
+ PRInt64 aExpireTime,
NotifyOperationType aNotifyOperation,
DBOperationType aDBOperation)
{
if (!gHostArena) {
gHostArena = new PLArenaPool;
if (!gHostArena)
return NS_ERROR_OUT_OF_MEMORY;
PL_INIT_ARENA_POOL(gHostArena, "PermissionHostArena", HOST_ARENA_SIZE);
@@ -330,27 +366,34 @@ nsPermissionManager::AddInternal(const n
if (!entry->GetKey()) {
mHostTable.RawRemoveEntry(entry);
return NS_ERROR_OUT_OF_MEMORY;
}
// figure out the transaction type, and get any existing permission value
OperationType op;
PRInt32 index = entry->GetPermissionIndex(typeIndex);
- PRUint32 oldPermission;
if (index == -1) {
if (aPermission == nsIPermissionManager::UNKNOWN_ACTION)
op = eOperationNone;
else
op = eOperationAdding;
} else {
- oldPermission = entry->GetPermissions()[index].mPermission;
+ nsPermissionEntry oldPermissionEntry = entry->GetPermissions()[index];
- if (aPermission == oldPermission)
+ // remove the permission if the permission is UNKNOWN, update the
+ // permission if its value or expire type have changed OR if the time has
+ // changed and the expire type is time, otherwise, don't modify. There's
+ // no need to modify a permission that doesn't expire with time when the
+ // only thing changed is the expire time.
+ if (aPermission == oldPermissionEntry.mPermission &&
+ aExpireType == oldPermissionEntry.mExpireType &&
+ (aExpireType != nsIPermissionManager::EXPIRE_TIME ||
+ aExpireTime == oldPermissionEntry.mExpireTime))
op = eOperationNone;
else if (aPermission == nsIPermissionManager::UNKNOWN_ACTION)
op = eOperationRemoving;
else
op = eOperationChanging;
}
// do the work for adding, deleting, or changing a permission:
@@ -368,65 +411,73 @@ nsPermissionManager::AddInternal(const n
if (aDBOperation == eWriteToDB) {
// we'll be writing to the database - generate a known unique id
id = ++mLargestID;
} else {
// we're reading from the database - use the id already assigned
id = aID;
}
- entry->GetPermissions().AppendElement(nsPermissionEntry(typeIndex, aPermission, id));
+ entry->GetPermissions().AppendElement(nsPermissionEntry(typeIndex, aPermission, id, aExpireType, aExpireTime));
- if (aDBOperation == eWriteToDB)
- UpdateDB(op, mStmtInsert, id, aHost, aType, aPermission);
+ if (aDBOperation == eWriteToDB && aExpireType != nsIPermissionManager::EXPIRE_SESSION)
+ UpdateDB(op, mStmtInsert, id, aHost, aType, aPermission, aExpireType, aExpireTime);
if (aNotifyOperation == eNotify) {
NotifyObserversWithPermission(aHost,
mTypeArray[typeIndex],
aPermission,
+ aExpireType,
+ aExpireTime,
NS_LITERAL_STRING("added").get());
}
break;
}
case eOperationRemoving:
{
- id = entry->GetPermissions()[index].mID;
+ nsPermissionEntry oldPermissionEntry = entry->GetPermissions()[index];
+ id = oldPermissionEntry.mID;
entry->GetPermissions().RemoveElementAt(index);
// If no more types are present, remove the entry
if (entry->GetPermissions().IsEmpty())
mHostTable.RawRemoveEntry(entry);
if (aDBOperation == eWriteToDB)
- UpdateDB(op, mStmtDelete, id, EmptyCString(), EmptyCString(), 0);
+ UpdateDB(op, mStmtDelete, id, EmptyCString(), EmptyCString(), 0,
+ nsIPermissionManager::EXPIRE_NEVER, 0);
if (aNotifyOperation == eNotify) {
NotifyObserversWithPermission(aHost,
mTypeArray[typeIndex],
- oldPermission,
+ oldPermissionEntry.mPermission,
+ oldPermissionEntry.mExpireType,
+ oldPermissionEntry.mExpireTime,
NS_LITERAL_STRING("deleted").get());
}
break;
}
case eOperationChanging:
{
id = entry->GetPermissions()[index].mID;
entry->GetPermissions()[index].mPermission = aPermission;
- if (aDBOperation == eWriteToDB)
- UpdateDB(op, mStmtUpdate, id, EmptyCString(), EmptyCString(), aPermission);
+ if (aDBOperation == eWriteToDB && aExpireType != nsIPermissionManager::EXPIRE_SESSION)
+ UpdateDB(op, mStmtUpdate, id, EmptyCString(), EmptyCString(), aPermission, aExpireType, aExpireTime);
if (aNotifyOperation == eNotify) {
NotifyObserversWithPermission(aHost,
mTypeArray[typeIndex],
aPermission,
+ aExpireType,
+ aExpireTime,
NS_LITERAL_STRING("changed").get());
}
break;
}
}
return NS_OK;
@@ -438,16 +489,18 @@ nsPermissionManager::Remove(const nsACSt
{
NS_ENSURE_ARG_POINTER(aType);
// AddInternal() handles removal, just let it do the work
return AddInternal(PromiseFlatCString(aHost),
nsDependentCString(aType),
nsIPermissionManager::UNKNOWN_ACTION,
0,
+ nsIPermissionManager::EXPIRE_NEVER,
+ 0,
eNotify,
eWriteToDB);
}
NS_IMETHODIMP
nsPermissionManager::RemoveAll()
{
nsresult rv = RemoveAllInternal();
@@ -511,35 +564,43 @@ nsPermissionManager::CommonTestPermissio
PRInt32 typeIndex = GetTypeIndex(aType, PR_FALSE);
// If type == -1, the type isn't known,
// so just return NS_OK
if (typeIndex == -1) return NS_OK;
nsHostEntry *entry = GetHostEntry(host, typeIndex, aExactHostMatch);
if (entry)
- *aPermission = entry->GetPermission(typeIndex);
+ *aPermission = entry->GetPermission(typeIndex).mPermission;
return NS_OK;
}
// Get hostentry for given host string and permission type.
// walk up the domain if needed.
// return null if nothing found.
nsHostEntry *
nsPermissionManager::GetHostEntry(const nsAFlatCString &aHost,
PRUint32 aType,
PRBool aExactHostMatch)
{
PRUint32 offset = 0;
nsHostEntry *entry;
+ PRInt64 now = PR_Now() / 1000;
+
do {
entry = mHostTable.GetEntry(aHost.get() + offset);
if (entry) {
- if (entry->GetPermission(aType) != nsIPermissionManager::UNKNOWN_ACTION)
+ nsPermissionEntry permEntry = entry->GetPermission(aType);
+
+ // if the entry is expired, remove and keep looking for others.
+ if (permEntry.mExpireType == nsIPermissionManager::EXPIRE_TIME &&
+ permEntry.mExpireTime < now)
+ Remove(aHost, mTypeArray[aType].get());
+ else if (permEntry.mPermission != nsIPermissionManager::UNKNOWN_ACTION)
break;
// reset entry, to be able to return null on failure
entry = nsnull;
}
if (aExactHostMatch)
break; // do not try super domains
@@ -567,17 +628,19 @@ AddPermissionsToList(nsHostEntry *entry,
{
nsGetEnumeratorData *data = static_cast<nsGetEnumeratorData *>(arg);
for (PRUint32 i = 0; i < entry->GetPermissions().Length(); ++i) {
nsPermissionEntry &permEntry = entry->GetPermissions()[i];
nsPermission *perm = new nsPermission(entry->GetHost(),
data->types->ElementAt(permEntry.mType),
- permEntry.mPermission);
+ permEntry.mPermission,
+ permEntry.mExpireType,
+ permEntry.mExpireTime);
data->array->AppendObject(perm);
}
return PL_DHASH_NEXT;
}
NS_IMETHODIMP nsPermissionManager::GetEnumerator(nsISimpleEnumerator **aEnum)
@@ -648,25 +711,28 @@ nsPermissionManager::GetTypeIndex(const
nsCString *elem = mTypeArray.AppendElement();
if (!elem)
return -1;
elem->Assign(aType);
return mTypeArray.Length() - 1;
}
-// wrapper function for mangling (host,type,perm) triplet into an nsIPermission.
+// wrapper function for mangling (host,type,perm,expireType,expireTime)
+// set into an nsIPermission.
void
nsPermissionManager::NotifyObserversWithPermission(const nsACString &aHost,
const nsCString &aType,
PRUint32 aPermission,
+ PRUint32 aExpireType,
+ PRInt64 aExpireTime,
const PRUnichar *aData)
{
nsCOMPtr<nsIPermission> permission =
- new nsPermission(aHost, aType, aPermission);
+ new nsPermission(aHost, aType, aPermission, aExpireType, aExpireTime);
if (permission)
NotifyObservers(permission, aData);
}
// notify observers that the permission list changed. there are four possible
// values for aData:
// "deleted" means a permission was deleted. aPermission is the deleted permission.
// "added" means a permission was added. aPermission is the added permission.
@@ -682,42 +748,69 @@ nsPermissionManager::NotifyObservers(nsI
aData);
}
nsresult
nsPermissionManager::Read()
{
nsresult rv;
+ // delete expired permissions before we read in the db
+ {
+ // this deletion has its own scope so the write lock is released when done.
+ nsCOMPtr<mozIStorageStatement> stmtDeleteExpired;
+ rv = mDBConn->CreateStatement(NS_LITERAL_CSTRING(
+ "DELETE FROM moz_hosts WHERE expireType = ?1 AND expireTime < ?2"),
+ getter_AddRefs(stmtDeleteExpired));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ rv = stmtDeleteExpired->BindInt32Parameter(0, nsIPermissionManager::EXPIRE_TIME);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ rv = stmtDeleteExpired->BindInt64Parameter(1, PR_Now() / 1000);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ PRBool hasResult;
+ rv = stmtDeleteExpired->ExecuteStep(&hasResult);
+ NS_ENSURE_SUCCESS(rv, rv);
+ }
+
nsCOMPtr<mozIStorageStatement> stmt;
rv = mDBConn->CreateStatement(NS_LITERAL_CSTRING(
- "SELECT id, host, type, permission "
+ "SELECT id, host, type, permission, expireType, expireTime "
"FROM moz_hosts"), getter_AddRefs(stmt));
NS_ENSURE_SUCCESS(rv, rv);
PRInt64 id;
nsCAutoString host, type;
PRUint32 permission;
+ PRUint32 expireType;
+ PRInt64 expireTime;
PRBool hasResult;
while (NS_SUCCEEDED(stmt->ExecuteStep(&hasResult)) && hasResult) {
// explicitly set our entry id counter for use in AddInternal(),
// and keep track of the largest id so we know where to pick up.
id = stmt->AsInt64(0);
if (id > mLargestID)
mLargestID = id;
rv = stmt->GetUTF8String(1, host);
NS_ENSURE_SUCCESS(rv, rv);
rv = stmt->GetUTF8String(2, type);
NS_ENSURE_SUCCESS(rv, rv);
permission = stmt->AsInt32(3);
+ expireType = stmt->AsInt32(4);
- rv = AddInternal(host, type, permission, id, eDontNotify, eNoDBOperation);
+ // convert into PRInt64 value (milliseconds)
+ expireTime = stmt->AsInt64(5);
+
+ rv = AddInternal(host, type, permission, id, expireType, expireTime,
+ eDontNotify, eNoDBOperation);
NS_ENSURE_SUCCESS(rv, rv);
}
return NS_OK;
}
static const char kMatchTypeHost[] = "host";
@@ -774,17 +867,18 @@ nsPermissionManager::Import()
// hosts might be encoded in UTF8; switch them to ACE to be consistent
if (!IsASCII(lineArray[3])) {
rv = NormalizeToACE(lineArray[3]);
if (NS_FAILED(rv))
continue;
}
- rv = AddInternal(lineArray[3], lineArray[1], permission, 0, eDontNotify, eWriteToDB);
+ rv = AddInternal(lineArray[3], lineArray[1], permission, 0,
+ nsIPermissionManager::EXPIRE_NEVER, 0, eDontNotify, eWriteToDB);
NS_ENSURE_SUCCESS(rv, rv);
}
}
// we're done importing - delete the old file
permissionsFile->Remove(PR_FALSE);
return NS_OK;
@@ -818,17 +912,19 @@ nsPermissionManager::GetHost(nsIURI *aUR
}
void
nsPermissionManager::UpdateDB(OperationType aOp,
mozIStorageStatement* aStmt,
PRInt64 aID,
const nsACString &aHost,
const nsACString &aType,
- PRUint32 aPermission)
+ PRUint32 aPermission,
+ PRUint32 aExpireType,
+ PRInt64 aExpireTime)
{
nsresult rv;
// no statement is ok - just means we don't have a profile
if (!aStmt)
return;
switch (aOp) {
@@ -839,31 +935,43 @@ nsPermissionManager::UpdateDB(OperationT
rv = aStmt->BindUTF8StringParameter(1, aHost);
if (NS_FAILED(rv)) break;
rv = aStmt->BindUTF8StringParameter(2, aType);
if (NS_FAILED(rv)) break;
rv = aStmt->BindInt32Parameter(3, aPermission);
+ if (NS_FAILED(rv)) break;
+
+ rv = aStmt->BindInt32Parameter(4, aExpireType);
+ if (NS_FAILED(rv)) break;
+
+ rv = aStmt->BindInt64Parameter(5, aExpireTime);
break;
}
case eOperationRemoving:
{
rv = aStmt->BindInt64Parameter(0, aID);
break;
}
case eOperationChanging:
{
rv = aStmt->BindInt64Parameter(0, aID);
if (NS_FAILED(rv)) break;
rv = aStmt->BindInt32Parameter(1, aPermission);
+ if (NS_FAILED(rv)) break;
+
+ rv = aStmt->BindInt32Parameter(2, aExpireType);
+ if (NS_FAILED(rv)) break;
+
+ rv = aStmt->BindInt64Parameter(3, aExpireTime);
break;
}
default:
{
NS_NOTREACHED("need a valid operation in UpdateDB()!");
rv = NS_ERROR_UNEXPECTED;
break;
--- a/extensions/cookie/nsPermissionManager.h
+++ b/extensions/cookie/nsPermissionManager.h
@@ -55,24 +55,29 @@ class nsIIDNService;
class mozIStorageConnection;
class mozIStorageStatement;
////////////////////////////////////////////////////////////////////////////////
class nsPermissionEntry
{
public:
- nsPermissionEntry(PRUint32 aType, PRUint32 aPermission, PRInt64 aID)
+ nsPermissionEntry(PRUint32 aType, PRUint32 aPermission, PRInt64 aID,
+ PRUint32 aExpireType, PRInt64 aExpireTime)
: mType(aType)
, mPermission(aPermission)
- , mID(aID) {}
+ , mID(aID)
+ , mExpireType(aExpireType)
+ , mExpireTime(aExpireTime) {}
PRUint32 mType;
PRUint32 mPermission;
PRInt64 mID;
+ PRUint32 mExpireType;
+ PRInt64 mExpireTime;
};
class nsHostEntry : public PLDHashEntryHdr
{
public:
// Hash methods
typedef const char* KeyType;
typedef const char* KeyTypePointer;
@@ -125,23 +130,26 @@ public:
{
for (PRUint32 i = 0; i < mPermissions.Length(); ++i)
if (mPermissions[i].mType == aType)
return i;
return -1;
}
- inline PRUint32 GetPermission(PRUint32 aType) const
+ inline nsPermissionEntry GetPermission(PRUint32 aType) const
{
for (PRUint32 i = 0; i < mPermissions.Length(); ++i)
if (mPermissions[i].mType == aType)
- return mPermissions[i].mPermission;
+ return mPermissions[i];
- return nsIPermissionManager::UNKNOWN_ACTION;
+ // unknown permission... return relevant data
+ nsPermissionEntry unk = nsPermissionEntry(aType, nsIPermissionManager::UNKNOWN_ACTION,
+ -1, nsIPermissionManager::EXPIRE_NEVER, 0);
+ return unk;
}
private:
const char *mHost;
nsAutoTArray<nsPermissionEntry, 1> mPermissions;
};
@@ -179,16 +187,18 @@ private:
eDontNotify,
eNotify
};
nsresult AddInternal(const nsAFlatCString &aHost,
const nsAFlatCString &aType,
PRUint32 aPermission,
PRInt64 aID,
+ PRUint32 aExpireType,
+ PRInt64 aExpireTime,
NotifyOperationType aNotifyOperation,
DBOperationType aDBOperation);
PRInt32 GetTypeIndex(const char *aTypeString,
PRBool aAdd);
nsHostEntry *GetHostEntry(const nsAFlatCString &aHost,
PRUint32 aType,
@@ -201,28 +211,32 @@ private:
nsresult InitDB(PRBool aRemoveFile);
nsresult CreateTable();
nsresult Import();
nsresult Read();
void NotifyObserversWithPermission(const nsACString &aHost,
const nsCString &aType,
PRUint32 aPermission,
+ PRUint32 aExpireType,
+ PRInt64 aExpireTime,
const PRUnichar *aData);
void NotifyObservers(nsIPermission *aPermission, const PRUnichar *aData);
nsresult RemoveAllInternal();
nsresult RemoveAllFromMemory();
nsresult NormalizeToACE(nsCString &aHost);
nsresult GetHost(nsIURI *aURI, nsACString &aResult);
static void UpdateDB(OperationType aOp,
mozIStorageStatement* aStmt,
PRInt64 aID,
const nsACString &aHost,
const nsACString &aType,
- PRUint32 aPermission);
+ PRUint32 aPermission,
+ PRUint32 aExpireType,
+ PRInt64 aExpireTime);
nsCOMPtr<nsIObserverService> mObserverService;
nsCOMPtr<nsIIDNService> mIDNService;
nsCOMPtr<mozIStorageConnection> mDBConn;
nsCOMPtr<mozIStorageStatement> mStmtInsert;
nsCOMPtr<mozIStorageStatement> mStmtDelete;
nsCOMPtr<mozIStorageStatement> mStmtUpdate;
new file mode 100644
--- /dev/null
+++ b/extensions/cookie/test/unit/test_permmanager_expiration.js
@@ -0,0 +1,54 @@
+const Cc = Components.classes;
+const Ci = Components.interfaces;
+
+ // setup a profile directory
+ var dir = do_get_profile();
+
+ // initialize the permission manager service
+ var pm = Cc["@mozilla.org/permissionmanager;1"]
+ .getService(Ci.nsIPermissionManager);
+
+ var ios = Cc["@mozilla.org/network/io-service;1"]
+ .getService(Ci.nsIIOService);
+ var permURI = ios.newURI("http://example.com", null, null);
+
+function run_test() {
+
+ // add a permission with *now* expiration
+ pm.add(permURI, "test/expiration-perm-exp", 1, pm.EXPIRE_TIME, (new Date()).getTime());
+
+ // add a permission with future expiration (100 milliseconds)
+ pm.add(permURI, "test/expiration-perm-exp2", 1, pm.EXPIRE_TIME, (new Date()).getTime() + 100);
+
+ // add a permission with future expiration (10000 milliseconds)
+ pm.add(permURI, "test/expiration-perm-exp3", 1, pm.EXPIRE_TIME, (new Date()).getTime() + 10000);
+
+ // add a permission without expiration
+ pm.add(permURI, "test/expiration-perm-nexp", 1, pm.EXPIRE_NEVER, 0);
+
+ // check that the permission expired
+ do_check_eq(0, pm.testPermission(permURI, "test/expiration-perm-exp"));
+
+ // ... and that the others didn't
+ do_check_eq(1, pm.testPermission(permURI, "test/expiration-perm-exp3"));
+ do_check_eq(1, pm.testPermission(permURI, "test/expiration-perm-nexp"));
+
+ // ... and that the short-term one will
+ do_test_pending();
+ do_timeout(200, "verifyExpiration();");
+
+ // clean up
+ do_test_pending();
+ do_timeout(300, "end_test();");
+}
+
+function verifyExpiration() {
+ do_check_eq(0, pm.testPermission(permURI, "test/expiration-perm-exp2"));
+ do_test_finished();
+}
+
+function end_test() {
+ // clean up
+ pm.removeAll();
+ do_test_finished();
+}
new file mode 100644
--- /dev/null
+++ b/extensions/cookie/test/unit/test_permmanager_notifications.js
@@ -0,0 +1,122 @@
+const Cc = Components.classes;
+const Ci = Components.interfaces;
+
+// setup a profile directory
+var dir = do_get_profile();
+
+// initialize the permission manager service
+var pm = Cc["@mozilla.org/permissionmanager;1"]
+ .getService(Ci.nsIPermissionManager);
+
+var ios = Cc["@mozilla.org/network/io-service;1"]
+ .getService(Ci.nsIIOService);
+var permURI = ios.newURI("http://example.com", null, null);
+
+var theTime = (new Date()).getTime();
+
+var numadds = 0;
+var numchanges = 0;
+var numdeletes = 0;
+var needsToClear = true;
+
+// will listen for stuff.
+var observer = {
+ QueryInterface:
+ function(iid) {
+ if (iid.equals(Ci.nsISupports) ||
+ iid.equals(Ci.nsIObserver))
+ return this;
+ throw Components.results.NS_ERROR_NO_INTERFACE;
+ },
+
+ observe:
+ function(subject, topic, data) {
+ if (topic !== "perm-changed")
+ return;
+
+ // "deleted" means a permission was deleted. aPermission is the deleted permission.
+ // "added" means a permission was added. aPermission is the added permission.
+ // "changed" means a permission was altered. aPermission is the new permission.
+ // "cleared" means the entire permission list was cleared. aPermission is null.
+ if (data == "added") {
+ var perm = subject.QueryInterface(Ci.nsIPermission);
+ numadds++;
+ switch (numadds) {
+ case 1: /* first add */
+ do_check_eq(pm.EXPIRE_TIME, perm.expireType);
+ do_check_eq(theTime + 10000, perm.expireTime);
+ break;
+ case 2: /* second add (permission-notify) */
+ do_check_eq(pm.EXPIRE_NEVER, perm.expireType);
+ do_check_eq(pm.DENY_ACTION, perm.capability);
+ break;
+ default:
+ do_throw("too many add notifications posted.");
+ }
+ do_test_finished();
+
+ } else if (data == "changed") {
+ var perm = subject.QueryInterface(Ci.nsIPermission);
+ numchanges++;
+ switch (numchanges) {
+ case 1:
+ do_check_eq(pm.EXPIRE_TIME, perm.expireType);
+ do_check_eq(theTime + 20000, perm.expireTime);
+ break;
+ default:
+ do_throw("too many change notifications posted.");
+ }
+ do_test_finished();
+
+ } else if (data == "deleted") {
+ var perm = subject.QueryInterface(Ci.nsIPermission);
+ numdeletes++;
+ switch (numdeletes) {
+ case 1:
+ do_check_eq("test/permission-notify", perm.type);
+ break;
+ default:
+ do_throw("too many delete notifications posted.");
+ }
+ do_test_finished();
+
+ } else if (data == "cleared") {
+ // only clear once: at the end
+ do_check_true(needsToClear);
+ needsToClear = false;
+ do_test_finished();
+ } else {
+ dump("subject: " + subject + " data: " + data + "\n");
+ }
+ },
+};
+
+function run_test() {
+
+ var obs = Cc["@mozilla.org/observer-service;1"].getService()
+ .QueryInterface(Ci.nsIObserverService);
+
+ obs.addObserver(observer, "perm-changed", false);
+
+ // add a permission
+ do_test_pending(); // for 'add' notification
+ pm.add(permURI, "test/expiration-perm", pm.ALLOW_ACTION, pm.EXPIRE_TIME, theTime + 10000);
+
+ do_test_pending(); // for 'change' notification
+ pm.add(permURI, "test/expiration-perm", pm.ALLOW_ACTION, pm.EXPIRE_TIME, theTime + 20000);
+
+ do_test_pending(); // for 'add' notification
+ pm.add(permURI, "test/permission-notify", pm.DENY_ACTION);
+
+ do_test_pending(); // for 'deleted' notification
+ pm.remove(permURI.asciiHost, "test/permission-notify");
+
+ do_test_pending(); // for 'cleared' notification
+ pm.removeAll();
+
+ do_timeout(100, "cleanup();");
+}
+
+function cleanup() {
+ obs.removeObserver(observer, "perm-changed");
+}
--- a/netwerk/base/public/nsIPermission.idl
+++ b/netwerk/base/public/nsIPermission.idl
@@ -33,18 +33,17 @@
* and other provisions required by the GPL or the LGPL. If you do not delete
* the provisions above, a recipient may use your version of this file under
* the terms of any one of the MPL, the GPL or the LGPL.
*
* ***** END LICENSE BLOCK ***** */
#include "nsISupports.idl"
-[scriptable, uuid(28F16D80-157B-11d5-A542-0010A401EB10)]
-
+[scriptable, uuid(5036f0f6-f77b-4168-9d57-a1c0dd66cf02)]
/**
* This interface defines a "permission" object,
* used to specify allowed/blocked objects from
* user-specified sites (cookies, images etc).
*/
interface nsIPermission : nsISupports
{
@@ -61,9 +60,22 @@ interface nsIPermission : nsISupports
* @see nsIPermissionManager
*/
readonly attribute ACString type;
/**
* The permission (see nsIPermissionManager.idl for allowed values)
*/
readonly attribute PRUint32 capability;
+
+ /**
+ * The expiration type of the permission (session, time-based or none).
+ * Constants are EXPIRE_*, defined in nsIPermissionManager.
+ * @see nsIPermissionManager
+ */
+ readonly attribute PRUint32 expireType;
+
+ /**
+ * The expiration time of the permission (milliseconds since Jan 1 1970
+ * 0:00:00).
+ */
+ readonly attribute PRInt64 expireTime;
};
--- a/netwerk/base/public/nsIPermissionManager.idl
+++ b/netwerk/base/public/nsIPermissionManager.idl
@@ -61,31 +61,40 @@
*/
#include "nsISupports.idl"
#include "nsISimpleEnumerator.idl"
interface nsIURI;
interface nsIObserver;
-[scriptable, uuid(00708302-684c-42d6-a5a3-995d51b1d17c)]
+[scriptable, uuid(0b83f9d5-3f96-41b6-91aa-ff3a7e4880d7)]
interface nsIPermissionManager : nsISupports
{
/**
* Predefined return values for the testPermission method and for
* the permission param of the add method
* NOTE: UNKNOWN_ACTION (0) is reserved to represent the
* default permission when no entry is found for a host, and
* should not be used by consumers to indicate otherwise.
*/
const PRUint32 UNKNOWN_ACTION = 0;
const PRUint32 ALLOW_ACTION = 1;
const PRUint32 DENY_ACTION = 2;
/**
+ * Predefined expiration types for permissions. Permissions can be permanent
+ * (never expire), expire at the end of the session, or expire at a specified
+ * time.
+ */
+ const PRUint32 EXPIRE_NEVER = 0;
+ const PRUint32 EXPIRE_SESSION = 1;
+ const PRUint32 EXPIRE_TIME = 2;
+
+ /**
* Add permission information for a given URI and permission type. This
* operation will cause the type string to be registered if it does not
* currently exist. If a permission already exists for a given type, it
* will be modified.
*
* @param uri the uri to add the permission for
* @param type a case-sensitive ASCII string, identifying the consumer.
* Consumers should choose this string to be unique, with
@@ -93,20 +102,28 @@ interface nsIPermissionManager : nsISupp
* @param permission an integer representing the desired action (e.g. allow
* or deny). The interpretation of this number is up to the
* consumer, and may represent different actions for different
* types. Consumers may use one of the enumerated permission
* actions defined above, for convenience.
* NOTE: UNKNOWN_ACTION (0) is reserved to represent the
* default permission when no entry is found for a host, and
* should not be used by consumers to indicate otherwise.
+ * @param expiretype a constant defining whether this permission should
+ * never expire (EXPIRE_NEVER), expire at the end of the
+ * session (EXPIRE_SESSION), or expire at a specified time
+ * (EXPIRE_TIME).
+ * @param expiretime an integer representation of when this permission
+ * should be forgotten (milliseconds since Jan 1 1970 0:00:00).
*/
void add(in nsIURI uri,
in string type,
- in PRUint32 permission);
+ in PRUint32 permission,
+ [optional] in PRUint32 expireType,
+ [optional] in PRInt64 expireTime);
/**
* Remove permission information for a given host string and permission type.
* The host string represents the exact entry in the permission list (such as
* obtained from the enumerator), not a URI which that permission might apply
* to.
*
* @param host the host to remove the permission for
--- a/xpinstall/src/nsInstallTrigger.cpp
+++ b/xpinstall/src/nsInstallTrigger.cpp
@@ -295,17 +295,18 @@ static void updatePermissions( const cha
host = Substring(hostlist, start, match-start);
host.CompressWhitespace();
host.Insert("http://", 0);
rv = NS_NewURI(getter_AddRefs(uri), host);
if (NS_SUCCEEDED(rv))
{
- aPermissionManager->Add( uri, XPI_PERMISSION, aPermission );
+ aPermissionManager->Add( uri, XPI_PERMISSION, aPermission,
+ nsIPermissionManager::EXPIRE_NEVER, 0 );
}
start = match+1;
} while ( match > 0 );
// save empty list, we don't need to do this again
aPrefBranch->SetCharPref( aPref, "");
}
}