Bug 547031 - Improve async error handling in cookies. Part 5: Fix observers to deal with reentrancy. r=sdwilsh, a=betaN+

This commit is contained in:
Dan Witte 2010-11-12 09:32:35 -08:00
parent 4e2da22b12
commit b5497e6704
3 changed files with 48 additions and 29 deletions

View File

@ -1391,13 +1391,13 @@ nsCookieService::NotifyChanged(nsISupports *aSubject,
mObserverService->NotifyObservers(aSubject, "cookie-changed", aData);
}
void
nsCookieService::NotifyPurged(nsICookie2* aCookie)
already_AddRefed<nsIArray>
nsCookieService::CreatePurgeList(nsICookie2* aCookie)
{
nsCOMPtr<nsIMutableArray> removedList =
do_CreateInstance(NS_ARRAY_CONTRACTID);
removedList->AppendElement(aCookie, PR_FALSE);
NotifyChanged(removedList, NS_LITERAL_STRING("batch-deleted").get());
return removedList.forget();
}
/******************************************************************************
@ -1550,14 +1550,14 @@ nsCookieService::Remove(const nsACString &aHost,
NS_ENSURE_SUCCESS(rv, rv);
nsListIter matchIter;
nsRefPtr<nsCookie> cookie;
if (FindCookie(baseDomain,
host,
PromiseFlatCString(aName),
PromiseFlatCString(aPath),
matchIter)) {
nsRefPtr<nsCookie> cookie = matchIter.Cookie();
cookie = matchIter.Cookie();
RemoveCookieFromList(matchIter);
NotifyChanged(cookie, NS_LITERAL_STRING("deleted").get());
}
// check if we need to add the host to the permissions blacklist.
@ -1575,6 +1575,11 @@ nsCookieService::Remove(const nsACString &aHost,
mPermissionService->SetAccess(uri, nsICookiePermission::ACCESS_DENY);
}
if (cookie) {
// Everything's done. Notify observers.
NotifyChanged(cookie, NS_LITERAL_STRING("deleted").get());
}
return NS_OK;
}
@ -1706,10 +1711,10 @@ nsCookieService::AsyncReadComplete()
mDefaultDBState->hostArray.Clear();
mDefaultDBState->readSet.Clear();
mObserverService->NotifyObservers(nsnull, "cookie-db-read", nsnull);
COOKIE_LOGSTRING(PR_LOG_DEBUG, ("Read(): %ld cookies read",
mDefaultDBState->cookieCount));
mObserverService->NotifyObservers(nsnull, "cookie-db-read", nsnull);
}
void
@ -1865,8 +1870,6 @@ nsCookieService::EnsureReadComplete()
mDefaultDBState->syncConn = nsnull;
mDefaultDBState->readSet.Clear();
mObserverService->NotifyObservers(nsnull, "cookie-db-read", nsnull);
COOKIE_LOGSTRING(PR_LOG_DEBUG,
("EnsureReadComplete(): %ld cookies read", readCount));
}
@ -1889,6 +1892,9 @@ nsCookieService::ImportCookies(nsIFile *aCookieFile)
nsCOMPtr<nsILineInputStream> lineInputStream = do_QueryInterface(fileInputStream, &rv);
if (NS_FAILED(rv)) return rv;
// First, ensure we've read in everything from the database, if we have one.
EnsureReadComplete();
static const char kTrue[] = "TRUE";
nsCAutoString buffer, baseDomain;
@ -1929,9 +1935,6 @@ nsCookieService::ImportCookies(nsIFile *aCookieFile)
*
*/
// First, ensure we've read in everything from the database, if we have one.
EnsureReadComplete();
// We will likely be adding a bunch of cookies to the DB, so we use async
// batching with storage to make this super fast.
nsCOMPtr<mozIStorageBindingParamsArray> paramsArray;
@ -2384,6 +2387,7 @@ nsCookieService::AddInternal(const nsCString &aBaseDomain,
aCookie->Name(), aCookie->Path(), matchIter);
nsRefPtr<nsCookie> oldCookie;
nsCOMPtr<nsIArray> purgedList;
if (foundCookie) {
oldCookie = matchIter.Cookie();
@ -2399,11 +2403,12 @@ nsCookieService::AddInternal(const nsCString &aBaseDomain,
return;
}
// Remove the stale cookie and notify.
// Remove the stale cookie. We save notification for later, once all list
// modifications are complete.
RemoveCookieFromList(matchIter);
COOKIE_LOGFAILURE(SET_COOKIE, aHostURI, aCookieHeader,
"stale cookie was purged");
NotifyPurged(oldCookie);
purgedList = CreatePurgeList(oldCookie);
// We've done all we need to wrt removing and notifying the stale cookie.
// From here on out, we pretend pretend it didn't exist, so that we
@ -2452,7 +2457,7 @@ nsCookieService::AddInternal(const nsCString &aBaseDomain,
// remove the oldest cookie from the domain
RemoveCookieFromList(iter);
COOKIE_LOGEVICTED(oldCookie, "Too many cookies for this domain");
NotifyPurged(oldCookie);
purgedList = CreatePurgeList(oldCookie);
} else if (mDBState->cookieCount >= ADD_TEN_PERCENT(mMaxNumberOfCookies)) {
PRInt64 maxAge = aCurrentTimeInUsec - mDBState->cookieOldestTime;
@ -2464,7 +2469,7 @@ nsCookieService::AddInternal(const nsCString &aBaseDomain,
// 2) evicting the balance of old cookies until we reach the size limit.
// note that the cookieOldestTime indicator can be pessimistic - if it's
// older than the actual oldest cookie, we'll just purge more eagerly.
PurgeCookies(aCurrentTimeInUsec);
purgedList = PurgeCookies(aCurrentTimeInUsec);
}
}
}
@ -2472,10 +2477,16 @@ nsCookieService::AddInternal(const nsCString &aBaseDomain,
// Add the cookie to the db. We do not supply a params array for batching
// because this might result in removals and additions being out of order.
AddCookieToList(aBaseDomain, aCookie, mDBState, NULL);
COOKIE_LOGSUCCESS(SET_COOKIE, aHostURI, aCookieHeader, aCookie, foundCookie);
// Now that list mutations are complete, notify observers. We do it here
// because observers may themselves attempt to mutate the list.
if (purgedList) {
NotifyChanged(purgedList, NS_LITERAL_STRING("batch-deleted").get());
}
NotifyChanged(aCookie, foundCookie ? NS_LITERAL_STRING("changed").get()
: NS_LITERAL_STRING("added").get());
COOKIE_LOGSUCCESS(SET_COOKIE, aHostURI, aCookieHeader, aCookie, foundCookie);
}
/******************************************************************************
@ -3158,10 +3169,12 @@ purgeCookiesCallback(nsCookieEntry *aEntry,
}
// purges expired and old cookies in a batch operation.
void
already_AddRefed<nsIArray>
nsCookieService::PurgeCookies(PRInt64 aCurrentTimeInUsec)
{
NS_ASSERTION(mDBState->hostTable.Count() > 0, "table is empty");
EnsureReadComplete();
#ifdef PR_LOGGING
PRUint32 initialCookieCount = mDBState->cookieCount;
COOKIE_LOGSTRING(PR_LOG_DEBUG,
@ -3172,8 +3185,6 @@ nsCookieService::PurgeCookies(PRInt64 aCurrentTimeInUsec)
nsAutoTArray<nsListIter, kMaxNumberOfCookies> purgeList;
nsCOMPtr<nsIMutableArray> removedList = do_CreateInstance(NS_ARRAY_CONTRACTID);
if (!removedList)
return;
// Create a params array to batch the removals. This is OK here because
// all the removals are in order, and there are no interleaved additions.
@ -3183,8 +3194,6 @@ nsCookieService::PurgeCookies(PRInt64 aCurrentTimeInUsec)
stmt->NewBindingParamsArray(getter_AddRefs(paramsArray));
}
EnsureReadComplete();
nsPurgeData data(aCurrentTimeInUsec / PR_USEC_PER_SEC,
aCurrentTimeInUsec - mCookiePurgeAge, purgeList, removedList, paramsArray);
mDBState->hostTable.EnumerateEntries(purgeCookiesCallback, &data);
@ -3232,9 +3241,6 @@ nsCookieService::PurgeCookies(PRInt64 aCurrentTimeInUsec)
}
}
// take all the cookies in the removed list, and notify about them in one batch
NotifyChanged(removedList, NS_LITERAL_STRING("batch-deleted").get());
// reset the oldest time indicator
mDBState->cookieOldestTime = data.oldestTime;
@ -3244,6 +3250,8 @@ nsCookieService::PurgeCookies(PRInt64 aCurrentTimeInUsec)
postExpiryCookieCount - mDBState->cookieCount,
mDBState->cookieCount,
aCurrentTimeInUsec - mDBState->cookieOldestTime));
return removedList.forget();
}
// find whether a given cookie has been previously set. this is provided by the

View File

@ -68,6 +68,7 @@ class nsIPrefBranch;
class nsIObserverService;
class nsIURI;
class nsIChannel;
class nsIArray;
class mozIStorageService;
class mozIThirdPartyUtil;
class ReadCookieDBListener;
@ -267,12 +268,13 @@ class nsCookieService : public nsICookieService
static PRBool CheckPath(nsCookieAttributes &aCookie, nsIURI *aHostURI);
static PRBool GetExpiry(nsCookieAttributes &aCookie, PRInt64 aServerTime, PRInt64 aCurrentTime);
void RemoveAllFromMemory();
void PurgeCookies(PRInt64 aCurrentTimeInUsec);
already_AddRefed<nsIArray> PurgeCookies(PRInt64 aCurrentTimeInUsec);
PRBool FindCookie(const nsCString& aBaseDomain, const nsAFlatCString &aHost, const nsAFlatCString &aName, const nsAFlatCString &aPath, nsListIter &aIter);
static void FindStaleCookie(nsCookieEntry *aEntry, PRInt64 aCurrentTime, nsListIter &aIter);
void NotifyRejected(nsIURI *aHostURI);
void NotifyChanged(nsISupports *aSubject, const PRUnichar *aData);
void NotifyPurged(nsICookie2* aCookie);
already_AddRefed<nsIArray> CreatePurgeList(nsICookie2* aCookie);
protected:
// cached members.

View File

@ -48,8 +48,17 @@ interface nsIChannel;
* page load. See nsICookieManager for methods to manipulate the cookie
* database directly. This separation of interface is mainly historical.
*
* This service broadcasts the following notifications when the cookie
* list is changed, or a cookie is rejected:
* This service broadcasts the notifications detailed below when the cookie
* list is changed, or a cookie is rejected.
*
* NOTE: observers of these notifications *must* not attempt to change profile
* or switch into or out of private browsing mode from within the
* observer. Doing so will cause undefined behavior. Mutating the cookie
* list (e.g. by calling methods on nsICookieService and friends) is
* allowed, but beware that there may be pending notifications you haven't
* seen yet -- for instance, a "batch-deleted" notification will likely be
* immediately followed by "added". You may check the state of the cookie
* list to determine if this is the case.
*
* topic : "cookie-changed"
* broadcast whenever the cookie list changes in some way. see