Bug 481775 - Crash in [@ nsCookieService::RemoveCookieFromList(nsListIter&)]. r=sdwilsh

This commit is contained in:
Dan Witte 2009-10-16 14:01:47 -07:00
parent fb4694efc4
commit 93fa18da2f
2 changed files with 169 additions and 112 deletions

View File

@ -377,8 +377,7 @@ NS_IMPL_ISUPPORTS5(nsCookieService,
nsISupportsWeakReference)
nsCookieService::nsCookieService()
: mHostTable(&mDefaultHostTable)
, mCookieCount(0)
: mDBState(&mDefaultDBState)
, mCookiesPermissions(BEHAVIOR_ACCEPT)
, mMaxNumberOfCookies(kMaxNumberOfCookies)
, mMaxCookiesPerHost(kMaxCookiesPerHost)
@ -388,7 +387,7 @@ nsCookieService::nsCookieService()
nsresult
nsCookieService::Init()
{
if (!mHostTable->Init()) {
if (!mDBState->hostTable.Init()) {
return NS_ERROR_OUT_OF_MEMORY;
}
@ -408,11 +407,6 @@ nsCookieService::Init()
// failure here is non-fatal (we can run fine without
// persistent storage - e.g. if there's no profile)
rv = InitDB();
if (rv == NS_ERROR_FILE_CORRUPTED) {
// database is corrupt - delete and try again
COOKIE_LOGSTRING(PR_LOG_WARNING, ("Init(): db corrupt, trying again", rv));
rv = InitDB(PR_TRUE);
}
if (NS_FAILED(rv))
COOKIE_LOGSTRING(PR_LOG_WARNING, ("Init(): InitDB() gave error %x", rv));
@ -428,7 +422,8 @@ nsCookieService::Init()
PRBool inPrivateBrowsing = PR_FALSE;
pbs->GetPrivateBrowsingEnabled(&inPrivateBrowsing);
if (inPrivateBrowsing) {
Observe(nsnull, NS_PRIVATE_BROWSING_SWITCH_TOPIC, NS_LITERAL_STRING(NS_PRIVATE_BROWSING_ENTER).get());
Observe(nsnull, NS_PRIVATE_BROWSING_SWITCH_TOPIC,
NS_LITERAL_STRING(NS_PRIVATE_BROWSING_ENTER).get());
}
}
}
@ -443,10 +438,32 @@ nsCookieService::Init()
}
nsresult
nsCookieService::InitDB(PRBool aDeleteExistingDB)
nsCookieService::InitDB()
{
// null out any existing connection
NS_ASSERTION(mDBState == &mDefaultDBState, "not in default DB state");
// attempt to open and read the database
nsresult rv = TryInitDB(PR_FALSE);
if (rv == NS_ERROR_FILE_CORRUPTED) {
// database is corrupt - delete and try again
COOKIE_LOGSTRING(PR_LOG_WARNING, ("InitDB(): db corrupt, trying again", rv));
rv = TryInitDB(PR_TRUE);
}
if (NS_FAILED(rv)) {
// reset our DB connection and statements
CloseDB();
}
return rv;
}
nsresult
nsCookieService::TryInitDB(PRBool aDeleteExistingDB)
{
// null out any existing connection, and clear the cookie table
CloseDB();
RemoveAllFromMemory();
nsCOMPtr<nsIFile> cookieFile;
nsresult rv = NS_GetSpecialDirectory(NS_APP_USER_PROFILE_50_DIR, getter_AddRefs(cookieFile));
@ -464,12 +481,13 @@ nsCookieService::InitDB(PRBool aDeleteExistingDB)
if (!storage)
return NS_ERROR_UNEXPECTED;
// cache a connection to the cookie database
rv = storage->OpenUnsharedDatabase(cookieFile, getter_AddRefs(mDBConn));
// open a connection to the cookie database, and only cache our connection
// and statements upon success.
rv = storage->OpenUnsharedDatabase(cookieFile, getter_AddRefs(mDBState->dbConn));
NS_ENSURE_SUCCESS(rv, rv);
PRBool tableExists = PR_FALSE;
mDBConn->TableExists(NS_LITERAL_CSTRING("moz_cookies"), &tableExists);
mDBState->dbConn->TableExists(NS_LITERAL_CSTRING("moz_cookies"), &tableExists);
if (!tableExists) {
rv = CreateTable();
NS_ENSURE_SUCCESS(rv, rv);
@ -477,7 +495,7 @@ nsCookieService::InitDB(PRBool aDeleteExistingDB)
} else {
// table already exists; check the schema version before reading
PRInt32 dbSchemaVersion;
rv = mDBConn->GetSchemaVersion(&dbSchemaVersion);
rv = mDBState->dbConn->GetSchemaVersion(&dbSchemaVersion);
NS_ENSURE_SUCCESS(rv, rv);
switch (dbSchemaVersion) {
@ -487,12 +505,12 @@ nsCookieService::InitDB(PRBool aDeleteExistingDB)
case 1:
{
// add the lastAccessed column to the table
rv = mDBConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
rv = mDBState->dbConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
"ALTER TABLE moz_cookies ADD lastAccessed INTEGER"));
NS_ENSURE_SUCCESS(rv, rv);
// update the schema version
rv = mDBConn->SetSchemaVersion(COOKIES_SCHEMA_VERSION);
rv = mDBState->dbConn->SetSchemaVersion(COOKIES_SCHEMA_VERSION);
NS_ENSURE_SUCCESS(rv, rv);
}
// fall through to the next upgrade
@ -509,7 +527,7 @@ nsCookieService::InitDB(PRBool aDeleteExistingDB)
// below, by verifying the columns we care about are all there. for now,
// re-set the schema version in the db, in case the checks succeed (if
// they don't, we're dropping the table anyway).
rv = mDBConn->SetSchemaVersion(COOKIES_SCHEMA_VERSION);
rv = mDBState->dbConn->SetSchemaVersion(COOKIES_SCHEMA_VERSION);
NS_ENSURE_SUCCESS(rv, rv);
}
// fall through to downgrade check
@ -524,14 +542,14 @@ nsCookieService::InitDB(PRBool aDeleteExistingDB)
{
// check if all the expected columns exist
nsCOMPtr<mozIStorageStatement> stmt;
rv = mDBConn->CreateStatement(NS_LITERAL_CSTRING(
rv = mDBState->dbConn->CreateStatement(NS_LITERAL_CSTRING(
"SELECT id, name, value, host, path, expiry, isSecure, isHttpOnly "
"FROM moz_cookies"), 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_cookies"));
rv = mDBState->dbConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING("DROP TABLE moz_cookies"));
NS_ENSURE_SUCCESS(rv, rv);
rv = CreateTable();
@ -542,24 +560,27 @@ nsCookieService::InitDB(PRBool aDeleteExistingDB)
}
// make operations on the table asynchronous, for performance
mDBConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING("PRAGMA synchronous = OFF"));
mDBState->dbConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING("PRAGMA synchronous = OFF"));
// open in exclusive mode for performance
mDBConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING("PRAGMA locking_mode = EXCLUSIVE"));
mDBState->dbConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING("PRAGMA locking_mode = EXCLUSIVE"));
// cache frequently used statements (for insertion, deletion, and updating)
rv = mDBConn->CreateStatement(NS_LITERAL_CSTRING(
rv = mDBState->dbConn->CreateStatement(NS_LITERAL_CSTRING(
"INSERT INTO moz_cookies "
"(id, name, value, host, path, expiry, lastAccessed, isSecure, isHttpOnly) "
"VALUES (?1, ?2, ?3, ?4, ?5, ?6, ?7, ?8, ?9)"), getter_AddRefs(mStmtInsert));
"VALUES (?1, ?2, ?3, ?4, ?5, ?6, ?7, ?8, ?9)"),
getter_AddRefs(mDBState->stmtInsert));
NS_ENSURE_SUCCESS(rv, rv);
rv = mDBConn->CreateStatement(NS_LITERAL_CSTRING(
"DELETE FROM moz_cookies WHERE id = ?1"), getter_AddRefs(mStmtDelete));
rv = mDBState->dbConn->CreateStatement(NS_LITERAL_CSTRING(
"DELETE FROM moz_cookies WHERE id = ?1"),
getter_AddRefs(mDBState->stmtDelete));
NS_ENSURE_SUCCESS(rv, rv);
rv = mDBConn->CreateStatement(NS_LITERAL_CSTRING(
"UPDATE moz_cookies SET lastAccessed = ?1 WHERE id = ?2"), getter_AddRefs(mStmtUpdate));
rv = mDBState->dbConn->CreateStatement(NS_LITERAL_CSTRING(
"UPDATE moz_cookies SET lastAccessed = ?1 WHERE id = ?2"),
getter_AddRefs(mDBState->stmtUpdate));
NS_ENSURE_SUCCESS(rv, rv);
// if we deleted a corrupt db, don't attempt to import - return now
@ -593,11 +614,11 @@ nsresult
nsCookieService::CreateTable()
{
// set the schema version, before creating the table
nsresult rv = mDBConn->SetSchemaVersion(COOKIES_SCHEMA_VERSION);
nsresult rv = mDBState->dbConn->SetSchemaVersion(COOKIES_SCHEMA_VERSION);
if (NS_FAILED(rv)) return rv;
// create the table
return mDBConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
return mDBState->dbConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
"CREATE TABLE moz_cookies ("
"id INTEGER PRIMARY KEY, name TEXT, value TEXT, host TEXT, path TEXT,"
"expiry INTEGER, lastAccessed INTEGER, isSecure INTEGER, isHttpOnly INTEGER)"));
@ -606,12 +627,14 @@ nsCookieService::CreateTable()
void
nsCookieService::CloseDB()
{
// finalize our statements and close the db connection.
NS_ASSERTION(!mPrivateDBState.dbConn, "private DB connection should always be null");
// finalize our statements and close the db connection for the default state.
// since we own these objects, nulling the pointers is sufficient here.
mStmtInsert = nsnull;
mStmtDelete = nsnull;
mStmtUpdate = nsnull;
mDBConn = nsnull;
mDefaultDBState.stmtInsert = nsnull;
mDefaultDBState.stmtDelete = nsnull;
mDefaultDBState.stmtUpdate = nsnull;
mDefaultDBState.dbConn = nsnull;
}
nsCookieService::~nsCookieService()
@ -632,10 +655,10 @@ nsCookieService::Observe(nsISupports *aSubject,
// or is going away because the application is shutting down.
RemoveAllFromMemory();
if (mDBConn) {
if (mDBState->dbConn) {
if (!nsCRT::strcmp(aData, NS_LITERAL_STRING("shutdown-cleanse").get())) {
// clear the cookie file
nsresult rv = mDBConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING("DELETE FROM moz_cookies"));
nsresult rv = mDBState->dbConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING("DELETE FROM moz_cookies"));
if (NS_FAILED(rv))
NS_WARNING("db delete failed");
}
@ -645,8 +668,17 @@ nsCookieService::Observe(nsISupports *aSubject,
}
} else if (!strcmp(aTopic, "profile-do-change")) {
// the profile has already changed; init the db from the new location
InitDB();
// the profile has already changed; init the db from the new location.
// if we are in the private browsing state, however, we do not want to read
// data into it - we should instead put it into the default state, so it's
// ready for us if and when we switch back to it.
if (mDBState == &mPrivateDBState) {
mDBState = &mDefaultDBState;
InitDB();
mDBState = &mPrivateDBState;
} else {
InitDB();
}
} else if (!strcmp(aTopic, NS_PREFBRANCH_PREFCHANGE_TOPIC_ID)) {
nsCOMPtr<nsIPrefBranch> prefBranch = do_QueryInterface(aSubject);
@ -654,25 +686,31 @@ nsCookieService::Observe(nsISupports *aSubject,
PrefChanged(prefBranch);
} else if (!strcmp(aTopic, NS_PRIVATE_BROWSING_SWITCH_TOPIC)) {
if (NS_LITERAL_STRING(NS_PRIVATE_BROWSING_ENTER).Equals(aData)) {
// backup the existing in-memory DB
if (mPrivateHostTable.IsInitialized() || mPrivateHostTable.Init()) {
mHostTable = &mPrivateHostTable;
mCookieCount = mHostTable->Count();
NotifyChanged(nsnull, NS_LITERAL_STRING("reload").get());
}
// close the connection to the on-disk DB
CloseDB();
// continue to use the in-memory DB
} else if (NS_LITERAL_STRING(NS_PRIVATE_BROWSING_LEAVE).Equals(aData)) {
// open the connection to the on-disk DB
InitDB();
// restore the in-memory DB as it was prior to private browsing
if (mPrivateHostTable.IsInitialized())
mPrivateHostTable.Clear();
mHostTable = &mDefaultHostTable;
mCookieCount = mHostTable->Count();
if (!mPrivateDBState.hostTable.IsInitialized() &&
!mPrivateDBState.hostTable.Init())
return NS_ERROR_OUT_OF_MEMORY;
NS_ASSERTION(mDBState == &mPrivateDBState, "already in private state");
NS_ASSERTION(mPrivateDBState.cookieCount == 0, "private count not 0");
NS_ASSERTION(mPrivateDBState.hostTable.Count() == 0, "private table not empty");
NS_ASSERTION(!mPrivateDBState.dbConn, "private DB connection not null");
// swap the private and default states
mDBState = &mPrivateDBState;
NotifyChanged(nsnull, NS_LITERAL_STRING("reload").get());
} else if (NS_LITERAL_STRING(NS_PRIVATE_BROWSING_LEAVE).Equals(aData)) {
// restore the default state, and clear the private one
mDBState = &mDefaultDBState;
NS_ASSERTION(!mPrivateDBState.dbConn, "private DB connection not null");
mPrivateDBState.cookieCount = 0;
if (mPrivateDBState.hostTable.IsInitialized())
mPrivateDBState.hostTable.Clear();
NotifyChanged(nsnull, NS_LITERAL_STRING("reload").get());
// continue to use both on-disk and in-memory DB
}
}
@ -758,7 +796,7 @@ nsCookieService::SetCookieStringInternal(nsIURI *aHostURI,
// start a transaction on the storage db, to optimize insertions.
// transaction will automically commit on completion
mozStorageTransaction transaction(mDBConn, PR_TRUE);
mozStorageTransaction transaction(mDBState->dbConn, PR_TRUE);
// switch to a nice string type now, and process each cookie in the header
nsDependentCString cookieHeader(aCookieHeader);
@ -819,12 +857,14 @@ nsCookieService::RemoveAll()
RemoveAllFromMemory();
// clear the cookie file
if (mDBConn) {
nsresult rv = mDBConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING("DELETE FROM moz_cookies"));
if (mDBState->dbConn) {
NS_ASSERTION(mDBState == &mDefaultDBState, "not in default DB state");
nsresult rv = mDBState->dbConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING("DELETE FROM moz_cookies"));
if (NS_FAILED(rv)) {
// Database must be corrupted, so remove it completely.
nsCOMPtr<nsIFile> dbFile;
mDBConn->GetDatabaseFile(getter_AddRefs(dbFile));
mDBState->dbConn->GetDatabaseFile(getter_AddRefs(dbFile));
CloseDB();
dbFile->Remove(PR_FALSE);
@ -864,10 +904,10 @@ COMArrayCallback(nsCookieEntry *aEntry,
NS_IMETHODIMP
nsCookieService::GetEnumerator(nsISimpleEnumerator **aEnumerator)
{
nsCOMArray<nsICookie> cookieList(mCookieCount);
nsCOMArray<nsICookie> cookieList(mDBState->cookieCount);
nsGetEnumeratorData data(&cookieList, PR_Now() / PR_USEC_PER_SEC);
mHostTable->EnumerateEntries(COMArrayCallback, &data);
mDBState->hostTable.EnumerateEntries(COMArrayCallback, &data);
return NS_NewArrayEnumerator(aEnumerator, cookieList);
}
@ -951,8 +991,9 @@ nsCookieService::Read()
{
// scope the deletion, so the write lock is released when finished
nsCOMPtr<mozIStorageStatement> stmtDeleteExpired;
rv = mDBConn->CreateStatement(NS_LITERAL_CSTRING("DELETE FROM moz_cookies WHERE expiry <= ?1"),
getter_AddRefs(stmtDeleteExpired));
rv = mDBState->dbConn->CreateStatement(NS_LITERAL_CSTRING(
"DELETE FROM moz_cookies WHERE expiry <= ?1"),
getter_AddRefs(stmtDeleteExpired));
NS_ENSURE_SUCCESS(rv, rv);
rv = stmtDeleteExpired->BindInt64Parameter(0, PR_Now() / PR_USEC_PER_SEC);
@ -965,7 +1006,7 @@ nsCookieService::Read()
// let the reading begin!
nsCOMPtr<mozIStorageStatement> stmt;
rv = mDBConn->CreateStatement(NS_LITERAL_CSTRING(
rv = mDBState->dbConn->CreateStatement(NS_LITERAL_CSTRING(
"SELECT id, name, value, host, path, expiry, lastAccessed, isSecure, isHttpOnly "
"FROM moz_cookies"), getter_AddRefs(stmt));
NS_ENSURE_SUCCESS(rv, rv);
@ -1008,7 +1049,7 @@ nsCookieService::Read()
delete newCookie;
}
COOKIE_LOGSTRING(PR_LOG_DEBUG, ("Read(): %ld cookies read", mCookieCount));
COOKIE_LOGSTRING(PR_LOG_DEBUG, ("Read(): %ld cookies read", mDBState->cookieCount));
return rv;
}
@ -1026,7 +1067,7 @@ nsCookieService::ImportCookies(nsIFile *aCookieFile)
// start a transaction on the storage db, to optimize insertions.
// transaction will automically commit on completion
mozStorageTransaction transaction(mDBConn, PR_TRUE);
mozStorageTransaction transaction(mDBState->dbConn, PR_TRUE);
static const char kTrue[] = "TRUE";
@ -1037,7 +1078,7 @@ nsCookieService::ImportCookies(nsIFile *aCookieFile)
PRInt32 numInts;
PRInt64 expires;
PRBool isDomain, isHttpOnly = PR_FALSE;
PRUint32 originalCookieCount = mCookieCount;
PRUint32 originalCookieCount = mDBState->cookieCount;
PRInt64 currentTimeInUsec = PR_Now();
PRInt64 currentTime = currentTimeInUsec / PR_USEC_PER_SEC;
@ -1139,7 +1180,7 @@ nsCookieService::ImportCookies(nsIFile *aCookieFile)
AddInternal(newCookie, currentTime, nsnull, nsnull, PR_TRUE);
}
COOKIE_LOGSTRING(PR_LOG_DEBUG, ("ImportCookies(): %ld cookies imported", mCookieCount));
COOKIE_LOGSTRING(PR_LOG_DEBUG, ("ImportCookies(): %ld cookies imported", mDBState->cookieCount));
return NS_OK;
}
@ -1231,7 +1272,7 @@ nsCookieService::GetCookieInternal(nsIURI *aHostURI,
// begin hash lookup, walking up the subdomain levels.
// we use nextDot to force a lookup of the original host (without leading dot).
do {
nsCookieEntry *entry = mHostTable->GetEntry(currentDot);
nsCookieEntry *entry = mDBState->hostTable.GetEntry(currentDot);
cookie = entry ? entry->Head() : nsnull;
for (; cookie; cookie = cookie->Next()) {
// if the cookie is secure and the host scheme isn't, we can't send it
@ -1295,7 +1336,7 @@ nsCookieService::GetCookieInternal(nsIURI *aHostURI,
if (stale) {
// start a transaction on the storage db, to optimize updates.
// transaction will automically commit on completion.
mozStorageTransaction transaction(mDBConn, PR_TRUE);
mozStorageTransaction transaction(mDBState->dbConn, PR_TRUE);
for (PRInt32 i = 0; i < count; ++i) {
cookie = foundCookieList.ElementAt(i);
@ -1456,7 +1497,7 @@ nsCookieService::AddInternal(nsCookie *aCookie,
// start a transaction on the storage db, to optimize deletions/insertions.
// transaction will automically commit on completion. if we already have a
// transaction (e.g. from SetCookie*()), this will have no effect.
mozStorageTransaction transaction(mDBConn, PR_TRUE);
mozStorageTransaction transaction(mDBState->dbConn, PR_TRUE);
nsListIter matchIter;
PRBool foundCookie = FindCookie(aCookie->Host(), aCookie->Name(), aCookie->Path(),
@ -1499,12 +1540,12 @@ nsCookieService::AddInternal(nsCookie *aCookie,
oldCookie = data.iter.current;
RemoveCookieFromList(data.iter);
} else if (mCookieCount >= mMaxNumberOfCookies) {
} else if (mDBState->cookieCount >= mMaxNumberOfCookies) {
// try to make room, by removing expired cookies
RemoveExpiredCookies(aCurrentTime);
// check if we still have to get rid of something
if (mCookieCount >= mMaxNumberOfCookies) {
if (mDBState->cookieCount >= mMaxNumberOfCookies) {
// find the position of the oldest cookie, and remove it
data.oldestTime = LL_MAXINT;
FindOldestCookie(data);
@ -2068,8 +2109,8 @@ nsCookieService::RemoveAllFromMemory()
{
// clearing the hashtable will call each nsCookieEntry's dtor,
// which releases all their respective children.
mHostTable->Clear();
mCookieCount = 0;
mDBState->hostTable.Clear();
mDBState->cookieCount = 0;
}
PLDHashOperator
@ -2092,10 +2133,12 @@ void
nsCookieService::RemoveExpiredCookies(PRInt64 aCurrentTime)
{
#ifdef PR_LOGGING
PRUint32 initialCookieCount = mCookieCount;
PRUint32 initialCookieCount = mDBState->cookieCount;
#endif
mHostTable->EnumerateEntries(removeExpiredCallback, &aCurrentTime);
COOKIE_LOGSTRING(PR_LOG_DEBUG, ("RemoveExpiredCookies(): %ld purged; %ld remain", initialCookieCount - mCookieCount, mCookieCount));
mDBState->hostTable.EnumerateEntries(removeExpiredCallback, &aCurrentTime);
COOKIE_LOGSTRING(PR_LOG_DEBUG,
("RemoveExpiredCookies(): %ld purged; %ld remain",
initialCookieCount - mDBState->cookieCount, mDBState->cookieCount));
}
// find whether a given cookie has been previously set. this is provided by the
@ -2134,7 +2177,7 @@ nsCookieService::CountCookiesFromHostInternal(const nsACString &aHost,
const char *currentDot = hostWithDot.get();
const char *nextDot = currentDot + 1;
do {
nsCookieEntry *entry = mHostTable->GetEntry(currentDot);
nsCookieEntry *entry = mDBState->hostTable.GetEntry(currentDot);
for (nsListIter iter(entry); iter.current; ++iter) {
// only count non-expired cookies
if (iter.current->Expiry() > aData.currentTime) {
@ -2183,7 +2226,7 @@ nsCookieService::GetCookiesFromHost(const nsACString &aHost,
const char *currentDot = hostWithDot.get();
const char *nextDot = currentDot + 1;
do {
nsCookieEntry *entry = mHostTable->GetEntry(currentDot);
nsCookieEntry *entry = mDBState->hostTable.GetEntry(currentDot);
for (nsListIter iter(entry); iter.current; ++iter) {
// only append non-expired cookies
if (iter.current->Expiry() > currentTime)
@ -2207,7 +2250,7 @@ nsCookieService::FindCookie(const nsAFlatCString &aHost,
nsListIter &aIter,
PRInt64 aCurrentTime)
{
nsCookieEntry *entry = mHostTable->GetEntry(aHost.get());
nsCookieEntry *entry = mDBState->hostTable.GetEntry(aHost.get());
for (aIter = nsListIter(entry); aIter.current; ++aIter) {
if (aIter.current->Expiry() > aCurrentTime &&
aPath.Equals(aIter.current->Path()) &&
@ -2224,14 +2267,14 @@ void
nsCookieService::RemoveCookieFromList(nsListIter &aIter)
{
// if it's a non-session cookie, remove it from the db
if (!aIter.current->IsSession() && mStmtDelete) {
if (!aIter.current->IsSession() && mDBState->dbConn) {
// use our cached sqlite "delete" statement
mozStorageStatementScoper scoper(mStmtDelete);
mozStorageStatementScoper scoper(mDBState->stmtDelete);
nsresult rv = mStmtDelete->BindInt64Parameter(0, aIter.current->CreationID());
nsresult rv = mDBState->stmtDelete->BindInt64Parameter(0, aIter.current->CreationID());
if (NS_SUCCEEDED(rv)) {
PRBool hasResult;
rv = mStmtDelete->ExecuteStep(&hasResult);
rv = mDBState->stmtDelete->ExecuteStep(&hasResult);
}
if (NS_FAILED(rv)) {
@ -2244,7 +2287,7 @@ nsCookieService::RemoveCookieFromList(nsListIter &aIter)
// we're removing the last element in the list - so just remove the entry
// from the hash. note that the entryclass' dtor will take care of
// releasing this last element for us!
mHostTable->RawRemoveEntry(aIter.entry);
mDBState->hostTable.RawRemoveEntry(aIter.entry);
aIter.current = nsnull;
} else {
@ -2260,7 +2303,7 @@ nsCookieService::RemoveCookieFromList(nsListIter &aIter)
}
}
--mCookieCount;
--mDBState->cookieCount;
}
nsresult
@ -2299,7 +2342,7 @@ bindCookieParameters(mozIStorageStatement* aStmt, const nsCookie* aCookie)
PRBool
nsCookieService::AddCookieToList(nsCookie *aCookie, PRBool aWriteToDB)
{
nsCookieEntry *entry = mHostTable->PutEntry(aCookie->Host().get());
nsCookieEntry *entry = mDBState->hostTable.PutEntry(aCookie->Host().get());
if (!entry) {
NS_ERROR("can't insert element into a null entry!");
@ -2310,17 +2353,17 @@ nsCookieService::AddCookieToList(nsCookie *aCookie, PRBool aWriteToDB)
aCookie->Next() = entry->Head();
entry->Head() = aCookie;
++mCookieCount;
++mDBState->cookieCount;
// if it's a non-session cookie and hasn't just been read from the db, write it out.
if (aWriteToDB && !aCookie->IsSession() && mStmtInsert) {
if (aWriteToDB && !aCookie->IsSession() && mDBState->dbConn) {
// use our cached sqlite "insert" statement
mozStorageStatementScoper scoper(mStmtInsert);
mozStorageStatementScoper scoper(mDBState->stmtInsert);
nsresult rv = bindCookieParameters(mStmtInsert, aCookie);
nsresult rv = bindCookieParameters(mDBState->stmtInsert, aCookie);
if (NS_SUCCEEDED(rv)) {
PRBool hasResult;
rv = mStmtInsert->ExecuteStep(&hasResult);
rv = mDBState->stmtInsert->ExecuteStep(&hasResult);
}
if (NS_FAILED(rv)) {
@ -2339,16 +2382,16 @@ nsCookieService::UpdateCookieInList(nsCookie *aCookie, PRInt64 aLastAccessed)
aCookie->SetLastAccessed(aLastAccessed);
// if it's a non-session cookie, update it in the db too
if (!aCookie->IsSession() && mStmtUpdate) {
if (!aCookie->IsSession() && mDBState->dbConn) {
// use our cached sqlite "update" statement
mozStorageStatementScoper scoper(mStmtUpdate);
mozStorageStatementScoper scoper(mDBState->stmtUpdate);
nsresult rv = mStmtUpdate->BindInt64Parameter(0, aLastAccessed);
nsresult rv = mDBState->stmtUpdate->BindInt64Parameter(0, aLastAccessed);
if (NS_SUCCEEDED(rv)) {
rv = mStmtUpdate->BindInt64Parameter(1, aCookie->CreationID());
rv = mDBState->stmtUpdate->BindInt64Parameter(1, aCookie->CreationID());
if (NS_SUCCEEDED(rv)) {
PRBool hasResult;
rv = mStmtUpdate->ExecuteStep(&hasResult);
rv = mDBState->stmtUpdate->ExecuteStep(&hasResult);
}
}
@ -2377,5 +2420,5 @@ findOldestCallback(nsCookieEntry *aEntry,
void
nsCookieService::FindOldestCookie(nsEnumerationData &aData)
{
mHostTable->EnumerateEntries(findOldestCallback, &aData);
mDBState->hostTable.EnumerateEntries(findOldestCallback, &aData);
}

View File

@ -140,6 +140,20 @@ class nsCookieEntry : public PLDHashEntryHdr
nsCookie *mHead;
};
// encapsulates in-memory and on-disk DB states, so we can
// conveniently switch state when entering or exiting private browsing.
struct DBState
{
DBState() : cookieCount(0) { }
nsTHashtable<nsCookieEntry> hostTable;
PRUint32 cookieCount;
nsCOMPtr<mozIStorageConnection> dbConn;
nsCOMPtr<mozIStorageStatement> stmtInsert;
nsCOMPtr<mozIStorageStatement> stmtDelete;
nsCOMPtr<mozIStorageStatement> stmtUpdate;
};
/******************************************************************************
* nsCookieService:
* class declaration
@ -165,7 +179,8 @@ class nsCookieService : public nsICookieService
protected:
void PrefChanged(nsIPrefBranch *aPrefBranch);
nsresult InitDB(PRBool aDeleteExistingDB = PR_FALSE);
nsresult InitDB();
nsresult TryInitDB(PRBool aDeleteExistingDB);
nsresult CreateTable();
void CloseDB();
nsresult Read();
@ -192,20 +207,19 @@ class nsCookieService : public nsICookieService
void NotifyChanged(nsICookie2 *aCookie, const PRUnichar *aData);
protected:
// cached members
nsCOMPtr<mozIStorageConnection> mDBConn;
nsCOMPtr<mozIStorageStatement> mStmtInsert;
nsCOMPtr<mozIStorageStatement> mStmtDelete;
nsCOMPtr<mozIStorageStatement> mStmtUpdate;
// cached members.
nsCOMPtr<nsIObserverService> mObserverService;
nsCOMPtr<nsICookiePermission> mPermissionService;
nsCOMPtr<nsIEffectiveTLDService> mTLDService;
// impl members
nsTHashtable<nsCookieEntry> *mHostTable;
nsTHashtable<nsCookieEntry> mDefaultHostTable;
nsTHashtable<nsCookieEntry> mPrivateHostTable;
PRUint32 mCookieCount;
// we have two separate DB states: one for normal browsing and one for
// private browsing, switching between them as appropriate. this state
// encapsulates both the in-memory table and the on-disk DB.
// note that the private states' dbConn should always be null - we never
// want to be dealing with the on-disk DB when in private browsing.
DBState *mDBState;
DBState mDefaultDBState;
DBState mPrivateDBState;
// cached prefs
PRUint8 mCookiesPermissions; // BEHAVIOR_{ACCEPT, REJECTFOREIGN, REJECT}