From b3267037da2b5ba4369740297165c2d5c95d5835 Mon Sep 17 00:00:00 2001 From: Dan Witte Date: Sun, 15 Mar 2009 21:38:56 -0700 Subject: [PATCH] Bug 444600 - (cookiemonster2) Cookies go missing after a few days. r=sdwilsh, sr=mconnor --- extensions/cookie/test/Makefile.in | 1 + netwerk/cookie/public/nsICookieService.idl | 23 +- netwerk/cookie/src/nsCookieService.cpp | 247 +++++++++++++++------ netwerk/cookie/src/nsCookieService.h | 11 +- netwerk/test/TestCookie.cpp | 36 +-- 5 files changed, 201 insertions(+), 117 deletions(-) diff --git a/extensions/cookie/test/Makefile.in b/extensions/cookie/test/Makefile.in index 2da3754adb62..dbeccb9841d8 100644 --- a/extensions/cookie/test/Makefile.in +++ b/extensions/cookie/test/Makefile.in @@ -71,6 +71,7 @@ _TEST_FILES = \ test_same_base_domain_5.html \ test_same_base_domain_6.html \ file_loopback_inner.html \ + test_eviction.html \ $(NULL) # XXX see bug 454857 diff --git a/netwerk/cookie/public/nsICookieService.idl b/netwerk/cookie/public/nsICookieService.idl index 3b109b0fdf4a..0f8b752266ba 100644 --- a/netwerk/cookie/public/nsICookieService.idl +++ b/netwerk/cookie/public/nsICookieService.idl @@ -52,18 +52,23 @@ interface nsIChannel; * list is changed, or a cookie is rejected: * * topic : "cookie-changed" - * broadcast whenever the cookie list changes in some way. there - * are four possible data strings for this notification; one - * notification will be broadcast for each change, and will involve - * a single cookie. - * subject: an nsICookie2 interface pointer representing the cookie object - * that changed. + * broadcast whenever the cookie list changes in some way. see + * explanation of data strings below. + * subject: see below. * data : "deleted" - * a cookie was deleted. the subject is the deleted cookie. + * a cookie was deleted. the subject is an nsICookie2 representing + * the deleted cookie. * "added" - * a cookie was added. the subject is the added cookie. + * a cookie was added. the subject is an nsICookie2 representing + * the added cookie. * "changed" - * a cookie was changed. the subject is the new cookie. + * a cookie was changed. the subject is an nsICookie2 representing + * the new cookie. (note that host, path, and name are invariant + * for a given cookie; other parameters may change.) + * "batch-deleted" + * a batch of cookies was deleted (for instance, as part of a purging + * operation). the subject is an nsIArray of nsICookie2's representing + * the deleted cookies. * "cleared" * the entire cookie list was cleared. the subject is null. * "reload" diff --git a/netwerk/cookie/src/nsCookieService.cpp b/netwerk/cookie/src/nsCookieService.cpp index 8ac245744eae..9d7b3eaee051 100644 --- a/netwerk/cookie/src/nsCookieService.cpp +++ b/netwerk/cookie/src/nsCookieService.cpp @@ -55,7 +55,9 @@ #include "nsILineInputStream.h" #include "nsIEffectiveTLDService.h" +#include "nsTArray.h" #include "nsCOMArray.h" +#include "nsIMutableArray.h" #include "nsArrayEnumerator.h" #include "nsAutoPtr.h" #include "nsReadableUtils.h" @@ -86,6 +88,7 @@ static const char kCookieFileName[] = "cookies.sqlite"; #define COOKIES_SCHEMA_VERSION 2 static const PRInt64 kCookieStaleThreshold = 60 * PR_USEC_PER_SEC; // 1 minute in microseconds +static const PRInt64 kCookiePurgeAge = 30 * 24 * 60 * 60 * PR_USEC_PER_SEC; // 30 days in microseconds static const char kOldCookieFileName[] = "cookies.txt"; @@ -117,6 +120,7 @@ static const PRUint32 BEHAVIOR_REJECT = 2; static const char kPrefCookiesPermissions[] = "network.cookie.cookieBehavior"; static const char kPrefMaxNumberOfCookies[] = "network.cookie.maxNumber"; static const char kPrefMaxCookiesPerHost[] = "network.cookie.maxPerHost"; +static const char kPrefCookiePurgeAge[] = "network.cookie.purgeAge"; // struct for temporarily storing cookie attributes during header parsing struct nsCookieAttributes @@ -206,7 +210,13 @@ static PRLogModuleInfo *sCookieLog = PR_NewLogModule("cookie"); #define COOKIE_LOGFAILURE(a, b, c, d) LogFailure(a, b, c, d) #define COOKIE_LOGSUCCESS(a, b, c, d, e) LogSuccess(a, b, c, d, e) -#define COOKIE_LOGEVICTED(a) LogEvicted(a) + +#define COOKIE_LOGEVICTED(a) \ + PR_BEGIN_MACRO \ + if (PR_LOG_TEST(sCookieLog, PR_LOG_DEBUG)) \ + LogEvicted(a); \ + PR_END_MACRO + #define COOKIE_LOGSTRING(lvl, fmt) \ PR_BEGIN_MACRO \ PR_LOG(sCookieLog, lvl, fmt); \ @@ -299,11 +309,6 @@ LogSuccess(PRBool aSetCookie, nsIURI *aHostURI, const char *aCookieString, nsCoo static void LogEvicted(nsCookie *aCookie) { - // if logging isn't enabled, return now to save cycles - if (!PR_LOG_TEST(sCookieLog, PR_LOG_DEBUG)) { - return; - } - PR_LOG(sCookieLog, PR_LOG_DEBUG,("===== COOKIE EVICTED =====\n")); LogCookie(aCookie); @@ -377,10 +382,12 @@ NS_IMPL_ISUPPORTS5(nsCookieService, nsCookieService::nsCookieService() : mHostTable(&mDefaultHostTable) + , mCookieOldestTime(LL_MAXINT) , mCookieCount(0) , mCookiesPermissions(BEHAVIOR_ACCEPT) , mMaxNumberOfCookies(kMaxNumberOfCookies) , mMaxCookiesPerHost(kMaxCookiesPerHost) + , mCookiePurgeAge(kCookiePurgeAge) { } @@ -401,6 +408,7 @@ nsCookieService::Init() prefBranch->AddObserver(kPrefCookiesPermissions, this, PR_TRUE); prefBranch->AddObserver(kPrefMaxNumberOfCookies, this, PR_TRUE); prefBranch->AddObserver(kPrefMaxCookiesPerHost, this, PR_TRUE); + prefBranch->AddObserver(kPrefCookiePurgeAge, this, PR_TRUE); PrefChanged(prefBranch); } @@ -774,18 +782,19 @@ nsCookieService::NotifyRejected(nsIURI *aHostURI) mObserverService->NotifyObservers(aHostURI, "cookie-rejected", nsnull); } -// notify observers that the cookie list changed. there are four possible +// notify observers that the cookie list changed. there are five possible // values for aData: -// "deleted" means a cookie was deleted. aCookie is the deleted cookie. -// "added" means a cookie was added. aCookie is the added cookie. -// "changed" means a cookie was altered. aCookie is the new cookie. -// "cleared" means the entire cookie list was cleared. aCookie is null. +// "deleted" means a cookie was deleted. aSubject is the deleted cookie. +// "added" means a cookie was added. aSubject is the added cookie. +// "changed" means a cookie was altered. aSubject is the new cookie. +// "cleared" means the entire cookie list was cleared. aSubject is null. +// "batch-deleted" means multiple cookies were deleted. aSubject is the list of cookies. void -nsCookieService::NotifyChanged(nsICookie2 *aCookie, +nsCookieService::NotifyChanged(nsISupports *aSubject, const PRUnichar *aData) { if (mObserverService) - mObserverService->NotifyObservers(aCookie, "cookie-changed", aData); + mObserverService->NotifyObservers(aSubject, "cookie-changed", aData); } /****************************************************************************** @@ -805,6 +814,9 @@ nsCookieService::PrefChanged(nsIPrefBranch *aPrefBranch) if (NS_SUCCEEDED(aPrefBranch->GetIntPref(kPrefMaxCookiesPerHost, &val))) mMaxCookiesPerHost = (PRUint16) LIMIT(val, 0, 0xFFFF, 0xFFFF); + + if (NS_SUCCEEDED(aPrefBranch->GetIntPref(kPrefCookiePurgeAge, &val))) + mCookiePurgeAge = val * PR_USEC_PER_SEC; // convert seconds to microseconds } /****************************************************************************** @@ -895,7 +907,7 @@ nsCookieService::Add(const nsACString &aDomain, return NS_ERROR_OUT_OF_MEMORY; } - AddInternal(cookie, currentTimeInUsec / PR_USEC_PER_SEC, nsnull, nsnull, PR_TRUE); + AddInternal(cookie, currentTimeInUsec, nsnull, nsnull, PR_TRUE); return NS_OK; } @@ -1135,7 +1147,7 @@ nsCookieService::ImportCookies(nsIFile *aCookieFile) if (originalCookieCount == 0) AddCookieToList(newCookie); else - AddInternal(newCookie, currentTime, nsnull, nsnull, PR_TRUE); + AddInternal(newCookie, currentTimeInUsec, nsnull, nsnull, PR_TRUE); } COOKIE_LOGSTRING(PR_LOG_DEBUG, ("ImportCookies(): %ld cookies imported", mCookieCount)); @@ -1430,7 +1442,7 @@ nsCookieService::SetCookieInternal(nsIURI *aHostURI, // add the cookie to the list. AddInternal() takes care of logging. // we get the current time again here, since it may have changed during prompting - AddInternal(cookie, PR_Now() / PR_USEC_PER_SEC, aHostURI, savedCookieHeader.get(), aFromHttp); + AddInternal(cookie, PR_Now(), aHostURI, savedCookieHeader.get(), aFromHttp); return newCookie; } @@ -1441,11 +1453,13 @@ nsCookieService::SetCookieInternal(nsIURI *aHostURI, // reached). also performs list maintenance by removing expired cookies. void nsCookieService::AddInternal(nsCookie *aCookie, - PRInt64 aCurrentTime, + PRInt64 aCurrentTimeInUsec, nsIURI *aHostURI, const char *aCookieHeader, PRBool aFromHttp) { + PRInt64 currentTime = aCurrentTimeInUsec / PR_USEC_PER_SEC; + // if the new cookie is httponly, make sure we're not coming from script if (!aFromHttp && aCookie->IsHttpOnly()) { COOKIE_LOGFAILURE(SET_COOKIE, aHostURI, aCookieHeader, "cookie is httponly; coming from script"); @@ -1459,7 +1473,7 @@ nsCookieService::AddInternal(nsCookie *aCookie, nsListIter matchIter; PRBool foundCookie = FindCookie(aCookie->Host(), aCookie->Name(), aCookie->Path(), - matchIter, aCurrentTime); + matchIter, currentTime); nsRefPtr oldCookie; if (foundCookie) { @@ -1474,7 +1488,7 @@ nsCookieService::AddInternal(nsCookie *aCookie, RemoveCookieFromList(matchIter); // check if the cookie has expired - if (aCookie->Expiry() <= aCurrentTime) { + if (aCookie->Expiry() <= currentTime) { COOKIE_LOGFAILURE(SET_COOKIE, aHostURI, aCookieHeader, "previously stored cookie was deleted"); NotifyChanged(oldCookie, NS_LITERAL_STRING("deleted").get()); return; @@ -1486,36 +1500,30 @@ nsCookieService::AddInternal(nsCookie *aCookie, } else { // check if cookie has already expired - if (aCookie->Expiry() <= aCurrentTime) { + if (aCookie->Expiry() <= currentTime) { COOKIE_LOGFAILURE(SET_COOKIE, aHostURI, aCookieHeader, "cookie has already expired"); return; } // check if we have to delete an old cookie. - nsEnumerationData data(aCurrentTime, LL_MAXINT); + nsEnumerationData data(currentTime, LL_MAXINT); if (CountCookiesFromHostInternal(aCookie->RawHost(), data) >= mMaxCookiesPerHost) { // remove the oldest cookie from host oldCookie = data.iter.current; + COOKIE_LOGEVICTED(oldCookie); RemoveCookieFromList(data.iter); - } else if (mCookieCount >= mMaxNumberOfCookies) { - // try to make room, by removing expired cookies - RemoveExpiredCookies(aCurrentTime); - - // check if we still have to get rid of something - if (mCookieCount >= mMaxNumberOfCookies) { - // find the position of the oldest cookie, and remove it - data.oldestTime = LL_MAXINT; - FindOldestCookie(data); - oldCookie = data.iter.current; - RemoveCookieFromList(data.iter); - } - } - - // if we deleted an old cookie, notify consumers - if (oldCookie) { - COOKIE_LOGEVICTED(oldCookie); NotifyChanged(oldCookie, NS_LITERAL_STRING("deleted").get()); + + } else if (mCookieCount >= 1.1 * mMaxNumberOfCookies && + aCurrentTimeInUsec - mCookieOldestTime >= 1.1 * mCookiePurgeAge) { + // we're over both size and age limits by 10%; time to purge the table! + // do this by: + // 1) removing expired cookies; + // 2) evicting the balance of old cookies, until we reach the size limit. + // note that the mCookieOldestTime indicator can be pessimistic - if it's + // older than the actual oldest cookie, we'll just purge more eagerly. + PurgeCookies(aCurrentTimeInUsec); } } @@ -2069,32 +2077,151 @@ nsCookieService::RemoveAllFromMemory() // which releases all their respective children. mHostTable->Clear(); mCookieCount = 0; + mCookieOldestTime = LL_MAXINT; } -PLDHashOperator -removeExpiredCallback(nsCookieEntry *aEntry, - void *aArg) +// stores temporary data for enumerating over the hash entries, +// since enumeration is done using callback functions +struct nsPurgeData { - const PRInt64 ¤tTime = *static_cast(aArg); + nsPurgeData(PRInt64 aCurrentTime, + PRInt64 aPurgeTime, + nsTArray &aPurgeList, + nsIMutableArray *aRemovedList) + : currentTime(aCurrentTime) + , purgeTime(aPurgeTime) + , oldestTime(LL_MAXINT) + , purgeList(aPurgeList) + , removedList(aRemovedList) {} + + // the current time, in seconds + PRInt64 currentTime; + + // lastAccessed time older than which cookies are eligible for purge + PRInt64 purgeTime; + + // lastAccessed time of the oldest cookie found during purge, to update our indicator + PRInt64 oldestTime; + + // list of cookies over the age limit, for purging + nsTArray &purgeList; + + // list of all cookies we've removed, for notification + nsIMutableArray *removedList; +}; + +// comparator class for lastaccessed times of cookies. +class CompareCookiesByAge { + public: + // returns true if (a == b); false otherwise. + PRBool Equals(const nsListIter &a, const nsListIter &b) const + { + return a.current->LastAccessed() == b.current->LastAccessed(); + } + + // returns true if (a < b); false otherwise. + PRBool LessThan(const nsListIter &a, const nsListIter &b) const + { + return a.current->LastAccessed() < b.current->LastAccessed(); + } +}; + +PLDHashOperator +purgeCookiesCallback(nsCookieEntry *aEntry, + void *aArg) +{ + nsPurgeData &data = *static_cast(aArg); for (nsListIter iter(aEntry, nsnull, aEntry->Head()); iter.current; ) { - if (iter.current->Expiry() <= currentTime) + // check if the cookie has expired + if (iter.current->Expiry() <= data.currentTime) { + nsCookie *cookie = iter.current; + data.removedList->AppendElement(cookie, PR_FALSE); + COOKIE_LOGEVICTED(cookie); + // remove from list. this takes care of updating the iterator for us nsCookieService::gCookieService->RemoveCookieFromList(iter); - else + + } else { + // check if the cookie is over the age limit + if (iter.current->LastAccessed() <= data.purgeTime) { + data.purgeList.AppendElement(iter); + + } else if (iter.current->LastAccessed() < data.oldestTime) { + // reset our indicator + data.oldestTime = iter.current->LastAccessed(); + } + ++iter; + } } return PL_DHASH_NEXT; } -// removes any expired cookies from memory +// purges expired and old cookies in a batch operation. void -nsCookieService::RemoveExpiredCookies(PRInt64 aCurrentTime) +nsCookieService::PurgeCookies(PRInt64 aCurrentTimeInUsec) { #ifdef PR_LOGGING PRUint32 initialCookieCount = mCookieCount; + COOKIE_LOGSTRING(PR_LOG_DEBUG, ("PurgeCookies(): beginning purge with %ld cookies and %lld age", + mCookieCount, aCurrentTimeInUsec - mCookieOldestTime)); #endif - mHostTable->EnumerateEntries(removeExpiredCallback, &aCurrentTime); - COOKIE_LOGSTRING(PR_LOG_DEBUG, ("RemoveExpiredCookies(): %ld purged; %ld remain", initialCookieCount - mCookieCount, mCookieCount)); + + nsAutoTArray purgeList; + + nsCOMPtr removedList = do_CreateInstance(NS_ARRAY_CONTRACTID); + if (!removedList) + return; + + nsPurgeData data(aCurrentTimeInUsec / PR_USEC_PER_SEC, aCurrentTimeInUsec - mCookiePurgeAge, purgeList, removedList); + mHostTable->EnumerateEntries(purgeCookiesCallback, &data); + +#ifdef PR_LOGGING + PRUint32 postExpiryCookieCount = mCookieCount; +#endif + + // now we have a list of iterators for cookies over the age limit. + // sort them by age, and then we'll see how many to remove... + purgeList.Sort(CompareCookiesByAge()); + + // only remove old cookies until we reach the max cookie limit, no more. + PRUint32 excess = mCookieCount - mMaxNumberOfCookies; + //printf("count %u, maxnum %u, listlen %u, excess %d\n", mCookieCount, mMaxNumberOfCookies, list.Length(), excess); + if (purgeList.Length() > excess) { + // we're not purging everything in the list, so update our indicator + data.oldestTime = purgeList[excess].current->LastAccessed(); + + purgeList.SetLength(excess); + } + + // traverse the list and remove cookies. the iterators we've stored + // in the list aren't stable under list mutation, so we need to do a + // fresh linked list traversal from the hash entryclass for each cookie. + for (PRUint32 i = 0; i < purgeList.Length(); ++i) { + for (nsListIter iter(purgeList[i].entry, nsnull, purgeList[i].entry->Head()); iter.current; ++iter) { + if (iter.current == purgeList[i].current) { + // remove from list. this takes care of updating the iterator for us + nsCookie *cookie = iter.current; + removedList->AppendElement(cookie, PR_FALSE); + COOKIE_LOGEVICTED(cookie); + + RemoveCookieFromList(iter); + break; + } + } + } + + // 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 + mCookieOldestTime = data.oldestTime; + + COOKIE_LOGSTRING(PR_LOG_DEBUG, ("PurgeCookies(): %ld expired; %ld purged; %ld remain; %lld oldest age", + initialCookieCount - postExpiryCookieCount, + mCookieCount - postExpiryCookieCount, + mCookieCount, + aCurrentTimeInUsec - mCookieOldestTime)); } // find whether a given cookie has been previously set. this is provided by the @@ -2282,6 +2409,10 @@ nsCookieService::AddCookieToList(nsCookie *aCookie, PRBool aWriteToDB) entry->Head() = aCookie; ++mCookieCount; + // keep track of the oldest cookie, for when it comes time to purge + if (aCookie->LastAccessed() < mCookieOldestTime) + mCookieOldestTime = aCookie->LastAccessed(); + // if it's a non-session cookie and hasn't just been read from the db, write it out. if (aWriteToDB && !aCookie->IsSession() && mStmtInsert) { // use our cached sqlite "insert" statement @@ -2329,23 +2460,3 @@ nsCookieService::UpdateCookieInList(nsCookie *aCookie, PRInt64 aLastAccessed) } } -static PLDHashOperator -findOldestCallback(nsCookieEntry *aEntry, - void *aArg) -{ - nsEnumerationData *data = static_cast(aArg); - for (nsListIter iter(aEntry, nsnull, aEntry->Head()); iter.current; ++iter) { - // check if we've found the oldest cookie so far - if (data->oldestTime > iter.current->LastAccessed()) { - data->oldestTime = iter.current->LastAccessed(); - data->iter = iter; - } - } - return PL_DHASH_NEXT; -} - -void -nsCookieService::FindOldestCookie(nsEnumerationData &aData) -{ - mHostTable->EnumerateEntries(findOldestCallback, &aData); -} diff --git a/netwerk/cookie/src/nsCookieService.h b/netwerk/cookie/src/nsCookieService.h index cb810087fe00..567d7664a5d3 100644 --- a/netwerk/cookie/src/nsCookieService.h +++ b/netwerk/cookie/src/nsCookieService.h @@ -171,7 +171,7 @@ class nsCookieService : public nsICookieService void GetCookieInternal(nsIURI *aHostURI, nsIChannel *aChannel, PRBool aHttpBound, char **aCookie); nsresult SetCookieStringInternal(nsIURI *aHostURI, nsIPrompt *aPrompt, const char *aCookieHeader, const char *aServerTime, nsIChannel *aChannel, PRBool aFromHttp); PRBool SetCookieInternal(nsIURI *aHostURI, nsIChannel *aChannel, nsDependentCString &aCookieHeader, PRInt64 aServerTime, PRBool aFromHttp); - void AddInternal(nsCookie *aCookie, PRInt64 aCurrentTime, nsIURI *aHostURI, const char *aCookieHeader, PRBool aFromHttp); + void AddInternal(nsCookie *aCookie, PRInt64 aCurrentTimeInUsec, nsIURI *aHostURI, const char *aCookieHeader, PRBool aFromHttp); void RemoveCookieFromList(nsListIter &aIter); PRBool AddCookieToList(nsCookie *aCookie, PRBool aWriteToDB = PR_TRUE); void UpdateCookieInList(nsCookie *aCookie, PRInt64 aLastAccessed); @@ -183,12 +183,11 @@ class nsCookieService : public nsICookieService static PRBool CheckPath(nsCookieAttributes &aCookie, nsIURI *aHostURI); static PRBool GetExpiry(nsCookieAttributes &aCookie, PRInt64 aServerTime, PRInt64 aCurrentTime); void RemoveAllFromMemory(); - void RemoveExpiredCookies(PRInt64 aCurrentTime); + void PurgeCookies(PRInt64 aCurrentTimeInUsec); PRBool FindCookie(const nsAFlatCString &aHost, const nsAFlatCString &aName, const nsAFlatCString &aPath, nsListIter &aIter, PRInt64 aCurrentTime); - void FindOldestCookie(nsEnumerationData &aData); PRUint32 CountCookiesFromHostInternal(const nsACString &aHost, nsEnumerationData &aData); void NotifyRejected(nsIURI *aHostURI); - void NotifyChanged(nsICookie2 *aCookie, const PRUnichar *aData); + void NotifyChanged(nsISupports *aSubject, const PRUnichar *aData); protected: // cached members @@ -204,19 +203,21 @@ class nsCookieService : public nsICookieService nsTHashtable *mHostTable; nsTHashtable mDefaultHostTable; nsTHashtable mPrivateHostTable; + PRInt64 mCookieOldestTime; PRUint32 mCookieCount; // cached prefs PRUint8 mCookiesPermissions; // BEHAVIOR_{ACCEPT, REJECTFOREIGN, REJECT} PRUint16 mMaxNumberOfCookies; PRUint16 mMaxCookiesPerHost; + PRUint64 mCookiePurgeAge; // private static member, used to cache a ptr to nsCookieService, // so we can make nsCookieService a singleton xpcom object. static nsCookieService *gCookieService; // this callback needs access to member functions - friend PLDHashOperator removeExpiredCallback(nsCookieEntry *aEntry, void *aArg); + friend PLDHashOperator purgeCookiesCallback(nsCookieEntry *aEntry, void *aArg); }; #endif // nsCookieService_h__ diff --git a/netwerk/test/TestCookie.cpp b/netwerk/test/TestCookie.cpp index 6db1b71b4f5c..c61a26ff9146 100644 --- a/netwerk/test/TestCookie.cpp +++ b/netwerk/test/TestCookie.cpp @@ -761,41 +761,7 @@ main(PRInt32 argc, char *argv[]) GetACookie(cookieService, "http://creation.ordering.tests/", nsnull, getter_Copies(cookie)); rv[0] = CheckResult(cookie.get(), MUST_EQUAL, expected.get()); - // test that cookies are evicted by order of lastAccessed time, if the limit on total cookies - // (3000) is reached - nsCAutoString host; - for (PRInt32 i = 0; i < 3010; ++i) { - host = NS_LITERAL_CSTRING("http://eviction."); - host.AppendInt(i); - host += NS_LITERAL_CSTRING(".tests/"); - SetACookie(cookieService, host.get(), nsnull, "test=eviction", nsnull); - - if (i == 9) { - // sleep a couple of seconds, to make sure the first 10 cookies are older than - // subsequent ones (timer resolution varies on different platforms). - PR_Sleep(2 * PR_TicksPerSecond()); - } - } - rv[1] = NS_SUCCEEDED(cookieMgr->GetEnumerator(getter_AddRefs(enumerator))); - i = 0; - rv[2] = PR_FALSE; // init to failure in case we break from the while loop - while (NS_SUCCEEDED(enumerator->HasMoreElements(&more)) && more) { - nsCOMPtr cookie; - if (NS_FAILED(enumerator->GetNext(getter_AddRefs(cookie)))) break; - ++i; - - // keep tabs on the third cookie, so we can check it later - nsCOMPtr cookie2(do_QueryInterface(cookie)); - if (!cookie2) break; - nsCAutoString domain; - cookie2->GetRawHost(domain); - PRInt32 hostNumber; - PRInt32 numInts = PR_sscanf(domain.get(), "eviction.%ld.tests", &hostNumber); - if (numInts != 1 || hostNumber < 10) break; - } - rv[2] = i == 3000; - - allTestsPassed = PrintResult(rv, 3) && allTestsPassed; + allTestsPassed = PrintResult(rv, 1) && allTestsPassed; // XXX the following are placeholders: add these tests please!