author | Mark Hammond <mhammond@skippinet.com.au> |
Mon, 15 Sep 2014 11:33:12 +1000 | |
changeset 205324 | 449fb0bd623fd7cc7174216f935c7108a516e404 |
parent 205323 | 54ca5ea4de1dacd96868be7cb520798193461bee |
child 205325 | 02648d3811fe6989a304a42534b7babfd532e5d3 |
push id | 49139 |
push user | cbook@mozilla.com |
push date | Mon, 15 Sep 2014 12:34:39 +0000 |
treeherder | mozilla-inbound@ff1d0d059574 [default view] [failures only] |
perfherder | [talos] [build metrics] [platform microbench] (compared to previous push) |
reviewers | ehsan |
bugs | 1058433 |
milestone | 35.0a1 |
first release with | nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
|
last release without | nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
|
--- a/build/automation.py.in +++ b/build/automation.py.in @@ -298,33 +298,34 @@ class Automation(object): def setupPermissionsDatabase(self, profileDir, permissions): # Included for reftest compatibility; # see https://bugzilla.mozilla.org/show_bug.cgi?id=688667 # Open database and create table permDB = sqlite3.connect(os.path.join(profileDir, "permissions.sqlite")) cursor = permDB.cursor(); - cursor.execute("PRAGMA user_version=3"); + cursor.execute("PRAGMA user_version=4"); # SQL copied from nsPermissionManager.cpp cursor.execute("""CREATE TABLE IF NOT EXISTS moz_hosts ( id INTEGER PRIMARY KEY, host TEXT, type TEXT, permission INTEGER, expireType INTEGER, expireTime INTEGER, + modificationTime INTEGER, appId INTEGER, isInBrowserElement INTEGER)""") # Insert desired permissions for perm in permissions.keys(): for host,allow in permissions[perm]: - cursor.execute("INSERT INTO moz_hosts values(NULL, ?, ?, ?, 0, 0, 0, 0)", + cursor.execute("INSERT INTO moz_hosts values(NULL, ?, ?, ?, 0, 0, 0, 0, 0)", (host, perm, 1 if allow else 2)) # Commit and close permDB.commit() cursor.close() def initializeProfile(self, profileDir, extraPrefs=None,
--- a/dom/ipc/ContentChild.cpp +++ b/dom/ipc/ContentChild.cpp @@ -1709,22 +1709,26 @@ ContentChild::RecvAddPermission(const IP MOZ_ASSERT(secMan); nsCOMPtr<nsIPrincipal> principal; nsresult rv = secMan->GetAppCodebasePrincipal(uri, permission.appId, permission.isInBrowserElement, getter_AddRefs(principal)); NS_ENSURE_SUCCESS(rv, true); + // child processes don't care about modification time. + int64_t modificationTime = 0; + permissionManager->AddInternal(principal, nsCString(permission.type), permission.capability, 0, permission.expireType, permission.expireTime, + modificationTime, nsPermissionManager::eNotify, nsPermissionManager::eNoDBOperation); #endif return true; } bool
--- a/extensions/cookie/nsPermissionManager.cpp +++ b/extensions/cookie/nsPermissionManager.cpp @@ -352,17 +352,17 @@ nsPermissionManager::AppClearDataObserve nsCOMPtr<nsIObserverService> observerService = do_GetService("@mozilla.org/observer-service;1"); observerService->AddObserver(new AppClearDataObserver(), "webapps-clear-data", /* holdsWeak= */ false); } //////////////////////////////////////////////////////////////////////////////// // nsPermissionManager Implementation static const char kPermissionsFileName[] = "permissions.sqlite"; -#define HOSTS_SCHEMA_VERSION 3 +#define HOSTS_SCHEMA_VERSION 4 static const char kHostpermFileName[] = "hostperm.1"; // Default permissions are read from a URL - this is the preference we read // to find that URL. static const char kDefaultsUrlPrefName[] = "permissions.manager.defaultsUrl"; // If the pref above doesn't exist, the URL we use by default. static const char kDefaultsUrl[] = "resource://app/chrome/browser/default_permissions"; @@ -427,18 +427,22 @@ nsPermissionManager::Init() for (uint32_t i = 0; i < perms.Length(); i++) { const IPC::Permission &perm = perms[i]; nsCOMPtr<nsIPrincipal> principal; rv = GetPrincipal(perm.host, perm.appId, perm.isInBrowserElement, getter_AddRefs(principal)); NS_ENSURE_SUCCESS(rv, rv); + // The child process doesn't care about modification times - it neither + // reads nor writes, nor removes them based on the date - so 0 (which + // will end up as now()) is fine. + uint64_t modificationTime = 0; AddInternal(principal, perm.type, perm.capability, 0, perm.expireType, - perm.expireTime, eNotify, eNoDBOperation); + perm.expireTime, modificationTime, eNotify, eNoDBOperation); } // Stop here; we don't need the DB in the child process return NS_OK; } // ignore failure here, since it's non-fatal (we can run fine without // persistent storage - e.g. if there's no profile). @@ -541,32 +545,49 @@ nsPermissionManager::InitDB(bool aRemove NS_ENSURE_SUCCESS(rv, rv); rv = mDBConn->SetSchemaVersion(HOSTS_SCHEMA_VERSION); NS_ENSURE_SUCCESS(rv, rv); } // fall through to the next upgrade + // Version 3->4 is the creation of the modificationTime field. + case 3: + { + rv = mDBConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING( + "ALTER TABLE moz_hosts ADD modificationTime INTEGER")); + NS_ENSURE_SUCCESS(rv, rv); + + // We leave the modificationTime at zero for all existing records; using + // now() would mean, eg, that doing "remove all from the last hour" + // within the first hour after migration would remove all permissions. + + rv = mDBConn->SetSchemaVersion(HOSTS_SCHEMA_VERSION); + NS_ENSURE_SUCCESS(rv, rv); + } + + // fall through to the next upgrade + // current version. case HOSTS_SCHEMA_VERSION: break; // downgrading. // if columns have been added to the table, we can still use the ones we // understand safely. if columns have been deleted or altered, just // 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, expireType, expireTime, appId, isInBrowserElement FROM moz_hosts"), + "SELECT host, type, permission, expireType, expireTime, modificationTime, appId, isInBrowserElement 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); @@ -578,28 +599,28 @@ nsPermissionManager::InitDB(bool aRemove } // 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->CreateAsyncStatement(NS_LITERAL_CSTRING( "INSERT INTO moz_hosts " - "(id, host, type, permission, expireType, expireTime, appId, isInBrowserElement) " - "VALUES (?1, ?2, ?3, ?4, ?5, ?6, ?7, ?8)"), getter_AddRefs(mStmtInsert)); + "(id, host, type, permission, expireType, expireTime, modificationTime, appId, isInBrowserElement) " + "VALUES (?1, ?2, ?3, ?4, ?5, ?6, ?7, ?8, ?9)"), getter_AddRefs(mStmtInsert)); NS_ENSURE_SUCCESS(rv, rv); rv = mDBConn->CreateAsyncStatement(NS_LITERAL_CSTRING( "DELETE FROM moz_hosts " "WHERE id = ?1"), getter_AddRefs(mStmtDelete)); NS_ENSURE_SUCCESS(rv, rv); rv = mDBConn->CreateAsyncStatement(NS_LITERAL_CSTRING( "UPDATE moz_hosts " - "SET permission = ?2, expireType= ?3, expireTime = ?4 WHERE id = ?1"), + "SET permission = ?2, expireType= ?3, expireTime = ?4, modificationTime = ?5 WHERE id = ?1"), getter_AddRefs(mStmtUpdate)); NS_ENSURE_SUCCESS(rv, rv); // Always import default permissions. ImportDefaults(); // check whether to import or just read in the db if (tableExists) return Read(); @@ -621,16 +642,17 @@ nsPermissionManager::CreateTable() return mDBConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING( "CREATE TABLE moz_hosts (" " id INTEGER PRIMARY KEY" ",host TEXT" ",type TEXT" ",permission INTEGER" ",expireType INTEGER" ",expireTime INTEGER" + ",modificationTime INTEGER" ",appId INTEGER" ",isInBrowserElement INTEGER" ")")); } NS_IMETHODIMP nsPermissionManager::Add(nsIURI *aURI, const char *aType, @@ -674,27 +696,31 @@ nsPermissionManager::AddFromPrincipal(ns return NS_OK; } // Permissions may not be added to expanded principals. if (IsExpandedPrincipal(aPrincipal)) { return NS_ERROR_INVALID_ARG; } + // A modificationTime of zero will cause AddInternal to use now(). + int64_t modificationTime = 0; + return AddInternal(aPrincipal, nsDependentCString(aType), aPermission, 0, - aExpireType, aExpireTime, eNotify, eWriteToDB); + aExpireType, aExpireTime, modificationTime, eNotify, eWriteToDB); } nsresult nsPermissionManager::AddInternal(nsIPrincipal* aPrincipal, const nsAFlatCString &aType, uint32_t aPermission, int64_t aID, uint32_t aExpireType, int64_t aExpireTime, + int64_t aModificationTime, NotifyOperationType aNotifyOperation, DBOperationType aDBOperation) { nsAutoCString host; nsresult rv = GetHostForPrincipal(aPrincipal, host); NS_ENSURE_SUCCESS(rv, rv); if (!IsChildProcess()) { @@ -755,25 +781,37 @@ nsPermissionManager::AddInternal(nsIPrin aExpireTime == oldPermissionEntry.mExpireTime)) op = eOperationNone; else if (oldPermissionEntry.mID == cIDPermissionIsDefault) // The existing permission is one added as a default and the new permission // doesn't exactly match so we are replacing the default. This is true // even if the new permission is UNKNOWN_ACTION (which means a "logical // remove" of the default) op = eOperationReplacingDefault; + else if (aID == cIDPermissionIsDefault) + // We are adding a default permission but a "real" permission already + // exists. This almost-certainly means we just did a removeAllSince and + // are re-importing defaults - so we can ignore this. + op = eOperationNone; else if (aPermission == nsIPermissionManager::UNKNOWN_ACTION) op = eOperationRemoving; else op = eOperationChanging; } + // child processes should *always* be passed a modificationTime of zero. + MOZ_ASSERT(!IsChildProcess() || aModificationTime == 0); + // do the work for adding, deleting, or changing a permission: // update the in-memory list, write to the db, and notify consumers. int64_t id; + if (aModificationTime == 0) { + aModificationTime = PR_Now() / 1000; + } + switch (op) { case eOperationNone: { // nothing to do return NS_OK; } case eOperationAdding: @@ -781,28 +819,30 @@ nsPermissionManager::AddInternal(nsIPrin 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(PermissionEntry(id, typeIndex, aPermission, aExpireType, aExpireTime)); + entry->GetPermissions().AppendElement(PermissionEntry(id, typeIndex, aPermission, + aExpireType, aExpireTime, + aModificationTime)); if (aDBOperation == eWriteToDB && aExpireType != nsIPermissionManager::EXPIRE_SESSION) { uint32_t appId; rv = aPrincipal->GetAppId(&appId); NS_ENSURE_SUCCESS(rv, rv); bool isInBrowserElement; rv = aPrincipal->GetIsInBrowserElement(&isInBrowserElement); NS_ENSURE_SUCCESS(rv, rv); - UpdateDB(op, mStmtInsert, id, host, aType, aPermission, aExpireType, aExpireTime, appId, isInBrowserElement); + UpdateDB(op, mStmtInsert, id, host, aType, aPermission, aExpireType, aExpireTime, aModificationTime, appId, isInBrowserElement); } if (aNotifyOperation == eNotify) { NotifyObserversWithPermission(host, entry->GetKey()->mAppId, entry->GetKey()->mIsInBrowserElement, mTypeArray[typeIndex], aPermission, @@ -819,17 +859,17 @@ nsPermissionManager::AddInternal(nsIPrin PermissionEntry oldPermissionEntry = entry->GetPermissions()[index]; id = oldPermissionEntry.mID; entry->GetPermissions().RemoveElementAt(index); if (aDBOperation == eWriteToDB) // We care only about the id here so we pass dummy values for all other // parameters. UpdateDB(op, mStmtDelete, id, EmptyCString(), EmptyCString(), 0, - nsIPermissionManager::EXPIRE_NEVER, 0, 0, false); + nsIPermissionManager::EXPIRE_NEVER, 0, 0, 0, false); if (aNotifyOperation == eNotify) { NotifyObserversWithPermission(host, entry->GetKey()->mAppId, entry->GetKey()->mIsInBrowserElement, mTypeArray[typeIndex], oldPermissionEntry.mPermission, oldPermissionEntry.mExpireType, @@ -861,22 +901,23 @@ nsPermissionManager::AddInternal(nsIPrin entry->GetPermissions()[index].mNonSessionPermission = aPermission; entry->GetPermissions()[index].mNonSessionExpireType = aExpireType; entry->GetPermissions()[index].mNonSessionExpireTime = aExpireTime; } entry->GetPermissions()[index].mPermission = aPermission; entry->GetPermissions()[index].mExpireType = aExpireType; entry->GetPermissions()[index].mExpireTime = aExpireTime; + entry->GetPermissions()[index].mModificationTime = aModificationTime; if (aDBOperation == eWriteToDB && aExpireType != nsIPermissionManager::EXPIRE_SESSION) - // We care only about the id, the permission and expireType/expireTime here. + // We care only about the id, the permission and expireType/expireTime/modificationTime here. // We pass dummy values for all other parameters. UpdateDB(op, mStmtUpdate, id, EmptyCString(), EmptyCString(), - aPermission, aExpireType, aExpireTime, 0, false); + aPermission, aExpireType, aExpireTime, aModificationTime, 0, false); if (aNotifyOperation == eNotify) { NotifyObserversWithPermission(host, entry->GetKey()->mAppId, entry->GetKey()->mIsInBrowserElement, mTypeArray[typeIndex], aPermission, aExpireType, @@ -910,28 +951,30 @@ nsPermissionManager::AddInternal(nsIPrin // default support that. NS_ENSURE_TRUE(aExpireType == EXPIRE_NEVER, NS_ERROR_UNEXPECTED); // update the existing entry in memory. entry->GetPermissions()[index].mID = id; entry->GetPermissions()[index].mPermission = aPermission; entry->GetPermissions()[index].mExpireType = aExpireType; entry->GetPermissions()[index].mExpireTime = aExpireTime; + entry->GetPermissions()[index].mModificationTime = aModificationTime; // If requested, create the entry in the DB. if (aDBOperation == eWriteToDB) { uint32_t appId; rv = aPrincipal->GetAppId(&appId); NS_ENSURE_SUCCESS(rv, rv); bool isInBrowserElement; rv = aPrincipal->GetIsInBrowserElement(&isInBrowserElement); NS_ENSURE_SUCCESS(rv, rv); - UpdateDB(eOperationAdding, mStmtInsert, id, host, aType, aPermission, aExpireType, aExpireTime, appId, isInBrowserElement); + UpdateDB(eOperationAdding, mStmtInsert, id, host, aType, aPermission, + aExpireType, aExpireTime, aModificationTime, appId, isInBrowserElement); } if (aNotifyOperation == eNotify) { NotifyObserversWithPermission(host, entry->GetKey()->mAppId, entry->GetKey()->mIsInBrowserElement, mTypeArray[typeIndex], aPermission, @@ -978,27 +1021,35 @@ nsPermissionManager::RemoveFromPrincipal // AddInternal() handles removal, just let it do the work return AddInternal(aPrincipal, nsDependentCString(aType), nsIPermissionManager::UNKNOWN_ACTION, 0, nsIPermissionManager::EXPIRE_NEVER, 0, + 0, eNotify, eWriteToDB); } NS_IMETHODIMP nsPermissionManager::RemoveAll() { ENSURE_NOT_CHILD_PROCESS; return RemoveAllInternal(true); } +NS_IMETHODIMP +nsPermissionManager::RemoveAllSince(int64_t aSince) +{ + ENSURE_NOT_CHILD_PROCESS; + return RemoveAllModifiedSince(aSince); +} + void nsPermissionManager::CloseDB(bool aRebuildOnSuccess) { // Null the statements, this will finalize them. mStmtInsert = nullptr; mStmtDelete = nullptr; mStmtUpdate = nullptr; if (mDBConn) { @@ -1318,22 +1369,26 @@ nsPermissionManager::GetPermissionHashKe // No entry, really... return nullptr; } // helper struct for passing arguments into hash enumeration callback. struct nsGetEnumeratorData { - nsGetEnumeratorData(nsCOMArray<nsIPermission> *aArray, const nsTArray<nsCString> *aTypes) + nsGetEnumeratorData(nsCOMArray<nsIPermission> *aArray, + const nsTArray<nsCString> *aTypes, + int64_t aSince = 0) : array(aArray) - , types(aTypes) {} + , types(aTypes) + , since(aSince) {} nsCOMArray<nsIPermission> *array; const nsTArray<nsCString> *types; + int64_t since; }; static PLDHashOperator AddPermissionsToList(nsPermissionManager::PermissionHashKey* entry, void *arg) { nsGetEnumeratorData *data = static_cast<nsGetEnumeratorData *>(arg); for (uint32_t i = 0; i < entry->GetPermissions().Length(); ++i) { @@ -1390,16 +1445,86 @@ NS_IMETHODIMP nsPermissionManager::Obser else if (!nsCRT::strcmp(aTopic, "profile-do-change")) { // the profile has already changed; init the db from the new location InitDB(false); } return NS_OK; } +static PLDHashOperator +AddPermissionsModifiedSinceToList( + nsPermissionManager::PermissionHashKey* entry, void* arg) +{ + nsGetEnumeratorData* data = static_cast<nsGetEnumeratorData *>(arg); + + for (size_t i = 0; i < entry->GetPermissions().Length(); ++i) { + const nsPermissionManager::PermissionEntry& permEntry = entry->GetPermissions()[i]; + + if (data->since > permEntry.mModificationTime) { + continue; + } + + nsPermission* perm = new nsPermission(entry->GetKey()->mHost, + entry->GetKey()->mAppId, + entry->GetKey()->mIsInBrowserElement, + data->types->ElementAt(permEntry.mType), + permEntry.mPermission, + permEntry.mExpireType, + permEntry.mExpireTime); + + data->array->AppendObject(perm); + } + return PL_DHASH_NEXT; +} + +nsresult +nsPermissionManager::RemoveAllModifiedSince(int64_t aModificationTime) +{ + ENSURE_NOT_CHILD_PROCESS; + + // roll an nsCOMArray of all our permissions, then hand out an enumerator + nsCOMArray<nsIPermission> array; + nsGetEnumeratorData data(&array, &mTypeArray, aModificationTime); + + mPermissionTable.EnumerateEntries(AddPermissionsModifiedSinceToList, &data); + + for (int32_t i = 0; i<array.Count(); ++i) { + nsAutoCString host; + bool isInBrowserElement = false; + nsAutoCString type; + uint32_t appId = 0; + + array[i]->GetHost(host); + array[i]->GetIsInBrowserElement(&isInBrowserElement); + array[i]->GetType(type); + array[i]->GetAppId(&appId); + + nsCOMPtr<nsIPrincipal> principal; + if (NS_FAILED(GetPrincipal(host, appId, isInBrowserElement, + getter_AddRefs(principal)))) { + NS_ERROR("GetPrincipal() failed!"); + continue; + } + // AddInternal handles removal, so let it do the work... + AddInternal( + principal, + type, + nsIPermissionManager::UNKNOWN_ACTION, + 0, + nsIPermissionManager::EXPIRE_NEVER, 0, 0, + nsPermissionManager::eNotify, + nsPermissionManager::eWriteToDB); + } + // now re-import any defaults as they may now be required if we just deleted + // an override. + ImportDefaults(); + return NS_OK; +} + PLDHashOperator nsPermissionManager::GetPermissionsForApp(nsPermissionManager::PermissionHashKey* entry, void* arg) { GetPermissionsForAppStruct* data = static_cast<GetPermissionsForAppStruct*>(arg); for (uint32_t i = 0; i < entry->GetPermissions().Length(); ++i) { nsPermissionManager::PermissionEntry& permEntry = entry->GetPermissions()[i]; @@ -1470,16 +1595,17 @@ nsPermissionManager::RemovePermissionsFo } AddInternal(principal, type, nsIPermissionManager::UNKNOWN_ACTION, 0, nsIPermissionManager::EXPIRE_NEVER, 0, + 0, nsPermissionManager::eNotify, nsPermissionManager::eNoDBOperation); } return NS_OK; } PLDHashOperator @@ -1641,25 +1767,26 @@ nsPermissionManager::Read() bool hasResult; rv = stmtDeleteExpired->ExecuteStep(&hasResult); NS_ENSURE_SUCCESS(rv, rv); } nsCOMPtr<mozIStorageStatement> stmt; rv = mDBConn->CreateStatement(NS_LITERAL_CSTRING( - "SELECT id, host, type, permission, expireType, expireTime, appId, isInBrowserElement " + "SELECT id, host, type, permission, expireType, expireTime, modificationTime, appId, isInBrowserElement " "FROM moz_hosts"), getter_AddRefs(stmt)); NS_ENSURE_SUCCESS(rv, rv); int64_t id; nsAutoCString host, type; uint32_t permission; uint32_t expireType; int64_t expireTime; + int64_t modificationTime; uint32_t appId; bool isInBrowserElement; bool hasResult; bool readError = false; 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. @@ -1677,35 +1804,37 @@ nsPermissionManager::Read() if (NS_FAILED(rv)) { readError = true; continue; } permission = stmt->AsInt32(3); expireType = stmt->AsInt32(4); - // convert into int64_t value (milliseconds) + // convert into int64_t values (milliseconds) expireTime = stmt->AsInt64(5); + modificationTime = stmt->AsInt64(6); - if (stmt->AsInt64(6) < 0) { + if (stmt->AsInt64(7) < 0) { readError = true; continue; } - appId = static_cast<uint32_t>(stmt->AsInt64(6)); - isInBrowserElement = static_cast<bool>(stmt->AsInt32(7)); + + appId = static_cast<uint32_t>(stmt->AsInt64(7)); + isInBrowserElement = static_cast<bool>(stmt->AsInt32(8)); nsCOMPtr<nsIPrincipal> principal; nsresult rv = GetPrincipal(host, appId, isInBrowserElement, getter_AddRefs(principal)); if (NS_FAILED(rv)) { readError = true; continue; } rv = AddInternal(principal, type, permission, id, expireType, expireTime, - eDontNotify, eNoDBOperation); + modificationTime, eDontNotify, eNoDBOperation); if (NS_FAILED(rv)) { readError = true; continue; } } if (readError) { NS_ERROR("Error occured while reading the permissions database!"); @@ -1843,18 +1972,24 @@ nsPermissionManager::_DoImport(nsIInputS if (NS_FAILED(rv)) continue; } nsCOMPtr<nsIPrincipal> principal; nsresult rv = GetPrincipal(lineArray[3], getter_AddRefs(principal)); NS_ENSURE_SUCCESS(rv, rv); + // the import file format doesn't handle modification times, so we use + // 0, which AddInternal will convert to now() + int64_t modificationTime = 0; + rv = AddInternal(principal, lineArray[1], permission, id, - nsIPermissionManager::EXPIRE_NEVER, 0, eDontNotify, operation); + nsIPermissionManager::EXPIRE_NEVER, 0, + modificationTime, + eDontNotify, operation); NS_ENSURE_SUCCESS(rv, rv); } } while (isMore); return NS_OK; } @@ -1875,16 +2010,17 @@ void nsPermissionManager::UpdateDB(OperationType aOp, mozIStorageAsyncStatement* aStmt, int64_t aID, const nsACString &aHost, const nsACString &aType, uint32_t aPermission, uint32_t aExpireType, int64_t aExpireTime, + int64_t aModificationTime, uint32_t aAppId, bool aIsInBrowserElement) { ENSURE_NOT_CHILD_PROCESS_NORET; nsresult rv; // no statement is ok - just means we don't have a profile @@ -1907,20 +2043,23 @@ nsPermissionManager::UpdateDB(OperationT if (NS_FAILED(rv)) break; rv = aStmt->BindInt32ByIndex(4, aExpireType); if (NS_FAILED(rv)) break; rv = aStmt->BindInt64ByIndex(5, aExpireTime); if (NS_FAILED(rv)) break; - rv = aStmt->BindInt64ByIndex(6, aAppId); + rv = aStmt->BindInt64ByIndex(6, aModificationTime); if (NS_FAILED(rv)) break; - rv = aStmt->BindInt64ByIndex(7, aIsInBrowserElement); + rv = aStmt->BindInt64ByIndex(7, aAppId); + if (NS_FAILED(rv)) break; + + rv = aStmt->BindInt64ByIndex(8, aIsInBrowserElement); break; } case eOperationRemoving: { rv = aStmt->BindInt64ByIndex(0, aID); break; } @@ -1932,16 +2071,19 @@ nsPermissionManager::UpdateDB(OperationT rv = aStmt->BindInt32ByIndex(1, aPermission); if (NS_FAILED(rv)) break; rv = aStmt->BindInt32ByIndex(2, aExpireType); if (NS_FAILED(rv)) break; rv = aStmt->BindInt64ByIndex(3, aExpireTime); + if (NS_FAILED(rv)) break; + + rv = aStmt->BindInt64ByIndex(4, aModificationTime); 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 @@ -32,32 +32,35 @@ class nsPermissionManager MOZ_FINAL : pu public nsIObserver, public nsSupportsWeakReference { public: class PermissionEntry { public: PermissionEntry(int64_t aID, uint32_t aType, uint32_t aPermission, - uint32_t aExpireType, int64_t aExpireTime) + uint32_t aExpireType, int64_t aExpireTime, + int64_t aModificationTime) : mID(aID) , mType(aType) , mPermission(aPermission) , mExpireType(aExpireType) , mExpireTime(aExpireTime) + , mModificationTime(aModificationTime) , mNonSessionPermission(aPermission) , mNonSessionExpireType(aExpireType) , mNonSessionExpireTime(aExpireTime) {} int64_t mID; uint32_t mType; uint32_t mPermission; uint32_t mExpireType; int64_t mExpireTime; + int64_t mModificationTime; uint32_t mNonSessionPermission; uint32_t mNonSessionExpireType; uint32_t mNonSessionExpireTime; }; /** * PermissionKey is the key used by PermissionHashKey hash table. * @@ -149,17 +152,17 @@ public: inline PermissionEntry GetPermission(uint32_t aType) const { for (uint32_t i = 0; i < mPermissions.Length(); ++i) if (mPermissions[i].mType == aType) return mPermissions[i]; // unknown permission... return relevant data return PermissionEntry(-1, aType, nsIPermissionManager::UNKNOWN_ACTION, - nsIPermissionManager::EXPIRE_NEVER, 0); + nsIPermissionManager::EXPIRE_NEVER, 0, 0); } private: nsAutoTArray<PermissionEntry, 1> mPermissions; }; // nsISupports NS_DECL_ISUPPORTS @@ -195,16 +198,17 @@ public: static const int64_t cIDPermissionIsDefault = -1; nsresult AddInternal(nsIPrincipal* aPrincipal, const nsAFlatCString &aType, uint32_t aPermission, int64_t aID, uint32_t aExpireType, int64_t aExpireTime, + int64_t aModificationTime, NotifyOperationType aNotifyOperation, DBOperationType aDBOperation); /** * Initialize the "webapp-uninstall" observing. * Will create a nsPermissionManager instance if needed. * That way, we can prevent have nsPermissionManager created at startup just * to be able to clear data when an application is uninstalled. @@ -255,16 +259,17 @@ private: static void UpdateDB(OperationType aOp, mozIStorageAsyncStatement* aStmt, int64_t aID, const nsACString& aHost, const nsACString& aType, uint32_t aPermission, uint32_t aExpireType, int64_t aExpireTime, + int64_t aModificationTime, uint32_t aAppId, bool aIsInBrowserElement); nsresult RemoveExpiredPermissionsForApp(uint32_t aAppId); /** * This struct has to be passed as an argument to GetPermissionsForApp. * |appId| and |browserOnly| have to be defined. @@ -294,16 +299,23 @@ private: /** * This method restores an app's permissions when its session ends. */ static PLDHashOperator RemoveExpiredPermissionsForAppEnumerator(PermissionHashKey* entry, void* nonused); + + /** + * This method removes all permissions modified after the specified time. + */ + nsresult + RemoveAllModifiedSince(int64_t aModificationTime); + nsCOMPtr<nsIObserverService> mObserverService; nsCOMPtr<nsIIDNService> mIDNService; nsCOMPtr<mozIStorageConnection> mDBConn; nsCOMPtr<mozIStorageAsyncStatement> mStmtInsert; nsCOMPtr<mozIStorageAsyncStatement> mStmtDelete; nsCOMPtr<mozIStorageAsyncStatement> mStmtUpdate;
--- a/extensions/cookie/test/unit/test_permmanager_defaults.js +++ b/extensions/cookie/test/unit/test_permmanager_defaults.js @@ -1,15 +1,22 @@ /* Any copyright is dedicated to the Public Domain. http://creativecommons.org/publicdomain/zero/1.0/ */ // The origin we use in most of the tests. const TEST_ORIGIN = "example.org"; +const TEST_ORIGIN_2 = "example.com"; const TEST_PERMISSION = "test-permission"; +function promiseTimeout(delay) { + let deferred = Promise.defer(); + do_timeout(delay, deferred.resolve); + return deferred.promise; +} + function run_test() { run_next_test(); } add_task(function* do_test() { // setup a profile. do_get_profile(); @@ -23,16 +30,17 @@ add_task(function* do_test() { ostream.init(file, -1, 0666, 0); let conv = Cc["@mozilla.org/intl/converter-output-stream;1"]. createInstance(Ci.nsIConverterOutputStream); conv.init(ostream, "UTF-8", 0, 0); conv.writeString("# this is a comment\n"); conv.writeString("\n"); // a blank line! conv.writeString("host\t" + TEST_PERMISSION + "\t1\t" + TEST_ORIGIN + "\n"); + conv.writeString("host\t" + TEST_PERMISSION + "\t1\t" + TEST_ORIGIN_2 + "\n"); ostream.close(); // Set the preference used by the permission manager so the file is read. Services.prefs.setCharPref("permissions.manager.defaultsUrl", "file://" + file.path); // initialize the permission manager service - it will read that default. let pm = Cc["@mozilla.org/permissionmanager;1"]. getService(Ci.nsIPermissionManager); @@ -87,16 +95,57 @@ add_task(function* do_test() { pm.addFromPrincipal(principal, TEST_PERMISSION, Ci.nsIPermissionManager.PROMPT_ACTION); // it should be reflected in a permission check, in the enumerator and the DB do_check_eq(Ci.nsIPermissionManager.PROMPT_ACTION, pm.testPermissionFromPrincipal(principal, TEST_PERMISSION)); do_check_eq(Ci.nsIPermissionManager.PROMPT_ACTION, findCapabilityViaEnum()); yield checkCapabilityViaDB(Ci.nsIPermissionManager.PROMPT_ACTION); + // -------------------------------------------------------------- + // check default permissions and removeAllSince work as expected. + pm.removeAll(); // ensure only defaults are there. + + let permURI2 = NetUtil.newURI("http://" + TEST_ORIGIN_2); + let principal2 = Services.scriptSecurityManager.getNoAppCodebasePrincipal(permURI2); + + // default for both principals is allow. + do_check_eq(Ci.nsIPermissionManager.ALLOW_ACTION, + pm.testPermissionFromPrincipal(principal, TEST_PERMISSION)); + do_check_eq(Ci.nsIPermissionManager.ALLOW_ACTION, + pm.testPermissionFromPrincipal(principal2, TEST_PERMISSION)); + + // Add a default override for TEST_ORIGIN_2 - this one should *not* be + // restored in removeAllSince() + pm.addFromPrincipal(principal2, TEST_PERMISSION, Ci.nsIPermissionManager.DENY_ACTION); + do_check_eq(Ci.nsIPermissionManager.DENY_ACTION, + pm.testPermissionFromPrincipal(principal2, TEST_PERMISSION)); + yield promiseTimeout(20); + + let since = Number(Date.now()); + yield promiseTimeout(20); + + // explicitly add a permission which overrides the default for the first + // principal - this one *should* be removed by removeAllSince. + pm.addFromPrincipal(principal, TEST_PERMISSION, Ci.nsIPermissionManager.DENY_ACTION); + do_check_eq(Ci.nsIPermissionManager.DENY_ACTION, + pm.testPermissionFromPrincipal(principal, TEST_PERMISSION)); + + // do a removeAllSince. + pm.removeAllSince(since); + + // the default for the first principal should re-appear as we modified it + // later then |since| + do_check_eq(Ci.nsIPermissionManager.ALLOW_ACTION, + pm.testPermissionFromPrincipal(principal, TEST_PERMISSION)); + + // but the permission for principal2 should remain as we added that before |since|. + do_check_eq(Ci.nsIPermissionManager.DENY_ACTION, + pm.testPermissionFromPrincipal(principal2, TEST_PERMISSION)); + // remove the temp file we created. file.remove(false); }); // use an enumerator to find the requested permission. Returns the permission // value (ie, the "capability" in nsIPermission parlance) or null if it can't // be found. function findCapabilityViaEnum(host = TEST_ORIGIN, type = TEST_PERMISSION) {
--- a/extensions/cookie/test/unit/test_permmanager_load_invalid_entries.js +++ b/extensions/cookie/test/unit/test_permmanager_load_invalid_entries.js @@ -108,22 +108,34 @@ function run_test() { " (id, host, type, permission, expireType, expireTime, appId, isInBrowserElement) " + "VALUES (" + i + ", '" + data.host + "', '" + data.type + "', " + data.permission + ", " + data.expireType + ", " + data.expireTime + ", " + data.appId + ", " + data.isInBrowserElement + ")" ); } + let earliestNow = Number(Date.now()); // Initialize the permission manager service var pm = Cc["@mozilla.org/permissionmanager;1"] .getService(Ci.nsIPermissionManager); + let latestNow = Number(Date.now()); - // The schema should still be 3. We want this test to be updated for each - // schema update. - do_check_eq(connection.schemaVersion, 3); + // The schema should be upgraded to 4, and a 'modificationTime' column should + // exist with all records having a value of 0. + do_check_eq(connection.schemaVersion, 4); + + let select = connection.createStatement("SELECT modificationTime FROM moz_hosts") + let numMigrated = 0; + while (select.executeStep()) { + let thisModTime = select.getInt64(0); + do_check_true(thisModTime == 0, "new modifiedTime field is correct"); + numMigrated += 1; + } + // check we found at least 1 record that was migrated. + do_check_true(numMigrated > 0, "we found at least 1 record that was migrated"); // This permission should always be there. let principal = Cc["@mozilla.org/scriptsecuritymanager;1"] .getService(Ci.nsIScriptSecurityManager) .getNoAppCodebasePrincipal(NetUtil.newURI("http://example.org")); do_check_eq(pm.testPermissionFromPrincipal(principal, 'test-load-invalid-entries'), Ci.nsIPermissionManager.ALLOW_ACTION); }
new file mode 100644 --- /dev/null +++ b/extensions/cookie/test/unit/test_permmanager_removesince.js @@ -0,0 +1,69 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +// Test that removing permissions since a specified time behaves as expected. + +let test_generator = do_run_test(); + +function run_test() { + do_test_pending(); + test_generator.next(); +} + +function continue_test() +{ + do_run_generator(test_generator); +} + +function do_run_test() { + // Set up a profile. + let profile = do_get_profile(); + + let pm = Services.perms; + + // to help with testing edge-cases, we will arrange for .removeAllSince to + // remove *all* permissions from one principal and one permission from another. + let permURI1 = NetUtil.newURI("http://example.com"); + let principal1 = Services.scriptSecurityManager.getNoAppCodebasePrincipal(permURI1); + + let permURI2 = NetUtil.newURI("http://example.org"); + let principal2 = Services.scriptSecurityManager.getNoAppCodebasePrincipal(permURI2); + + // add a permission now - this isn't going to be removed. + pm.addFromPrincipal(principal1, "test/remove-since", 1); + + // sleep briefly, then record the time - we'll remove all since then. + do_timeout(20, continue_test); + yield; + + let since = Number(Date.now()); + + // *sob* - on Windows at least, the now recorded by nsPermissionManager.cpp + // might be a couple of ms *earlier* than what JS sees. So another sleep + // to ensure our |since| is greater than the time of the permissions we + // are now adding. Sadly this means we'll never be able to test when since + // exactly equals the modTime, but there you go... + do_timeout(20, continue_test); + yield; + + // add another item - this second one should get nuked. + pm.addFromPrincipal(principal1, "test/remove-since-2", 1); + + // add 2 items for the second principal - both will be removed. + pm.addFromPrincipal(principal2, "test/remove-since", 1); + pm.addFromPrincipal(principal2, "test/remove-since-2", 1); + + // do the removal. + pm.removeAllSince(since); + + // principal1 - the first one should remain. + do_check_eq(1, pm.testPermissionFromPrincipal(principal1, "test/remove-since")); + // but the second should have been removed. + do_check_eq(0, pm.testPermissionFromPrincipal(principal1, "test/remove-since-2")); + + // principal2 - both should have been removed. + do_check_eq(0, pm.testPermissionFromPrincipal(principal2, "test/remove-since")); + do_check_eq(0, pm.testPermissionFromPrincipal(principal2, "test/remove-since-2")); + + do_finish_generator_test(test_generator); +}
--- a/extensions/cookie/test/unit/xpcshell.ini +++ b/extensions/cookie/test/unit/xpcshell.ini @@ -18,16 +18,17 @@ support-files = [test_cookies_thirdparty_session.js] [test_domain_eviction.js] [test_eviction.js] [test_permmanager_defaults.js] [test_permmanager_expiration.js] [test_permmanager_getPermissionObject.js] [test_permmanager_notifications.js] [test_permmanager_removeall.js] +[test_permmanager_removesince.js] [test_permmanager_load_invalid_entries.js] skip-if = debug == true [test_permmanager_idn.js] [test_permmanager_subdomains.js] [test_permmanager_local_files.js] [test_permmanager_mailto.js] [test_permmanager_cleardata.js] [test_schema_2_migration.js]
--- a/netwerk/base/public/nsIPermissionManager.idl +++ b/netwerk/base/public/nsIPermissionManager.idl @@ -32,17 +32,17 @@ interface nsIURI; interface nsIObserver; interface nsIPrincipal; interface nsIDOMWindow; interface nsIPermission; interface nsISimpleEnumerator; -[scriptable, uuid(c9fec678-f194-43c9-96b0-7bd9dbdd6bb0)] +[scriptable, uuid(620d9b61-8997-4d13-aa64-ec03341dd75b)] 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. @@ -128,16 +128,21 @@ interface nsIPermissionManager : nsISupp void removeFromPrincipal(in nsIPrincipal principal, in string type); /** * Clear permission information for all websites. */ void removeAll(); /** + * Clear all permission information added since the specified time. + */ + void removeAllSince(in int64_t since); + + /** * Test whether a website has permission to perform the given action. * @param uri the uri to be tested * @param type a case-sensitive ASCII string, identifying the consumer * @param return see add(), param permission. returns UNKNOWN_ACTION when * there is no stored permission for this uri and / or type. */ uint32_t testPermission(in nsIURI uri, in string type);
--- a/testing/mozbase/mozprofile/mozprofile/permissions.py +++ b/testing/mozbase/mozprofile/mozprofile/permissions.py @@ -236,18 +236,22 @@ class Permissions(object): type TEXT, permission INTEGER, expireType INTEGER, expireTime INTEGER)""") rows = cursor.execute("PRAGMA table_info(moz_hosts)") count = len(rows.fetchall()) + # if the db contains 9 columns, we're using user_version 4 + if count == 9: + statement = "INSERT INTO moz_hosts values(NULL, ?, ?, ?, 0, 0, 0, 0, 0)" + cursor.execute("PRAGMA user_version=4;") # if the db contains 8 columns, we're using user_version 3 - if count == 8: + elif count == 8: statement = "INSERT INTO moz_hosts values(NULL, ?, ?, ?, 0, 0, 0, 0)" cursor.execute("PRAGMA user_version=3;") else: statement = "INSERT INTO moz_hosts values(NULL, ?, ?, ?, 0, 0)" cursor.execute("PRAGMA user_version=2;") for location in locations: # set the permissions
--- a/testing/mozbase/mozprofile/tests/permissions.py +++ b/testing/mozbase/mozprofile/tests/permissions.py @@ -35,17 +35,28 @@ http://127.0.0.1:8888 privileg self.locations_file.close() def write_perm_db(self, version=3): permDB = sqlite3.connect(os.path.join(self.profile_dir, "permissions.sqlite")) cursor = permDB.cursor() cursor.execute("PRAGMA user_version=%d;" % version) - if version == 3: + if version == 4: + cursor.execute("""CREATE TABLE IF NOT EXISTS moz_hosts ( + id INTEGER PRIMARY KEY, + host TEXT, + type TEXT, + permission INTEGER, + expireType INTEGER, + expireTime INTEGER, + modificationTime INTEGER, + appId INTEGER, + isInBrowserElement INTEGER)""") + elif version == 3: cursor.execute("""CREATE TABLE IF NOT EXISTS moz_hosts ( id INTEGER PRIMARY KEY, host TEXT, type TEXT, permission INTEGER, expireType INTEGER, expireTime INTEGER, appId INTEGER, @@ -54,17 +65,17 @@ http://127.0.0.1:8888 privileg cursor.execute("""CREATE TABLE IF NOT EXISTS moz_hosts ( id INTEGER PRIMARY KEY, host TEXT, type TEXT, permission INTEGER, expireType INTEGER, expireTime INTEGER)""") else: - raise Exception("version must be 2 or 3") + raise Exception("version must be 2, 3 or 4") permDB.commit() cursor.close() def test_create_permissions_db(self): perms = Permissions(self.profile_dir, self.locations_file.name) perms_db_filename = os.path.join(self.profile_dir, 'permissions.sqlite') @@ -144,22 +155,24 @@ http://127.0.0.1:8888 privileg con = sqlite3.connect(perms_db_filename) cur = con.cursor() cur.execute(select_stmt) entries = cur.fetchall() self.assertEqual(len(entries), 3) - columns = 8 if version == 3 else 6 + columns = 9 if version == 4 else (8 if version == 3 else 6) self.assertEqual(len(entries[0]), columns) for x in range(4, columns): self.assertEqual(entries[0][x], 0) def test_existing_permissions_db_v2(self): self.verify_user_version(2) def test_existing_permissions_db_v3(self): self.verify_user_version(3) + def test_existing_permissions_db_v4(self): + self.verify_user_version(4) if __name__ == '__main__': unittest.main()