Bug 1357676 - Implement batch eviction r=jdm

1. Add network.cookie.QuotaPerHost, which has the default value 150.
2. When the cookies exceed more than 180, evict cookies to 150.
3. The concept of eviction is to sort all cookies by whether the cookie is expired and the cookie's last access time. Then, evict cookies by the given count.
4. Details of evict algorithm:
   4.1 Create a priority queue and push all cookies in it.
   4.2 Use custom comparator to compare the cookie by expiry and last access.
   4.3 Pop 30(180 - 150) cookies from the queue and append them to an output array.

Differential Revision: https://phabricator.services.mozilla.com/D3342

--HG--
extra : moz-landing-system : lando
This commit is contained in:
Kershaw Chang 2018-09-03 14:49:58 +00:00
parent 9e951cf226
commit 766fce11a1
4 changed files with 170 additions and 294 deletions

View File

@ -2297,6 +2297,9 @@ pref("network.cookie.move.interval_sec", 10);
pref("network.cookie.maxNumber", 3000);
pref("network.cookie.maxPerHost", 180);
// Cookies quota for each host. If cookies exceed the limit maxPerHost,
// (maxPerHost - quotaPerHost) cookies will be evicted.
pref("network.cookie.quotaPerHost", 150);
// The PAC file to load. Ignored unless network.proxy.type is 2.
pref("network.proxy.autoconfig_url", "");

View File

@ -61,6 +61,7 @@
#include "mozilla/StaticPrefs.h"
#include "mozilla/Telemetry.h"
#include "nsIConsoleService.h"
#include "nsTPriorityQueue.h"
#include "nsVariant.h"
using namespace mozilla;
@ -119,6 +120,7 @@ static const int64_t kCookiePurgeAge =
// network.cookie.maxNumber and network.cookie.maxPerHost prefs respectively.
static const uint32_t kMaxNumberOfCookies = 3000;
static const uint32_t kMaxCookiesPerHost = 180;
static const uint32_t kCookieQuotaPerHost = 150;
static const uint32_t kMaxBytesPerCookie = 4096;
static const uint32_t kMaxBytesPerPath = 1024;
@ -126,6 +128,7 @@ static const uint32_t kMaxBytesPerPath = 1024;
static const char kPrefCookieBehavior[] = "network.cookie.cookieBehavior";
static const char kPrefMaxNumberOfCookies[] = "network.cookie.maxNumber";
static const char kPrefMaxCookiesPerHost[] = "network.cookie.maxPerHost";
static const char kPrefCookieQuotaPerHost[] = "network.cookie.quotaPerHost";
static const char kPrefCookiePurgeAge[] = "network.cookie.purgeAge";
static const char kPrefThirdPartySession[] = "network.cookie.thirdparty.sessionOnly";
static const char kPrefThirdPartyNonsecureSession[] = "network.cookie.thirdparty.nonsecureSessionOnly";
@ -516,6 +519,26 @@ public:
NS_IMPL_ISUPPORTS(AppClearDataObserver, nsIObserver)
// comparator class for sorting cookies by entry and index.
class CompareCookiesByIndex {
public:
bool Equals(const nsListIter &a, const nsListIter &b) const
{
NS_ASSERTION(a.entry != b.entry || a.index != b.index,
"cookie indexes should never be equal");
return false;
}
bool LessThan(const nsListIter &a, const nsListIter &b) const
{
// compare by entryclass pointer, then by index.
if (a.entry != b.entry)
return a.entry < b.entry;
return a.index < b.index;
}
};
} // namespace
size_t
@ -612,6 +635,7 @@ nsCookieService::nsCookieService()
, mLeaveSecureAlone(true)
, mMaxNumberOfCookies(kMaxNumberOfCookies)
, mMaxCookiesPerHost(kMaxCookiesPerHost)
, mCookieQuotaPerHost(kCookieQuotaPerHost)
, mCookiePurgeAge(kCookiePurgeAge)
, mThread(nullptr)
, mMonitor("CookieThread")
@ -2387,6 +2411,25 @@ nsCookieService::CreatePurgeList(nsICookie2* aCookie)
return removedList.forget();
}
void
nsCookieService::CreateOrUpdatePurgeList(nsIArray** aPurgedList, nsICookie2* aCookie)
{
if (!*aPurgedList) {
COOKIE_LOGSTRING(LogLevel::Debug, ("Creating new purge list"));
nsCOMPtr<nsIArray> purgedList = CreatePurgeList(aCookie);
purgedList.forget(aPurgedList);
return;
}
nsCOMPtr<nsIMutableArray> purgedList = do_QueryInterface(*aPurgedList);
if (purgedList) {
COOKIE_LOGSTRING(LogLevel::Debug, ("Updating existing purge list"));
purgedList->AppendElement(aCookie);
} else {
COOKIE_LOGSTRING(LogLevel::Debug, ("Could not QI aPurgedList!"));
}
}
/******************************************************************************
* nsCookieService:
* public transaction helper impl
@ -2430,8 +2473,15 @@ nsCookieService::PrefChanged(nsIPrefBranch *aPrefBranch)
if (NS_SUCCEEDED(aPrefBranch->GetIntPref(kPrefMaxNumberOfCookies, &val)))
mMaxNumberOfCookies = (uint16_t) LIMIT(val, 1, 0xFFFF, kMaxNumberOfCookies);
if (NS_SUCCEEDED(aPrefBranch->GetIntPref(kPrefMaxCookiesPerHost, &val)))
mMaxCookiesPerHost = (uint16_t) LIMIT(val, 1, 0xFFFF, kMaxCookiesPerHost);
if (NS_SUCCEEDED(aPrefBranch->GetIntPref(kPrefCookieQuotaPerHost, &val))) {
mCookieQuotaPerHost =
(uint16_t) LIMIT(val, 1, mMaxCookiesPerHost - 1, kCookieQuotaPerHost);
}
if (NS_SUCCEEDED(aPrefBranch->GetIntPref(kPrefMaxCookiesPerHost, &val))) {
mMaxCookiesPerHost =
(uint16_t) LIMIT(val, mCookieQuotaPerHost + 1, 0xFFFF, kMaxCookiesPerHost);
}
if (NS_SUCCEEDED(aPrefBranch->GetIntPref(kPrefCookiePurgeAge, &val))) {
mCookiePurgeAge =
@ -3783,15 +3833,16 @@ nsCookieService::AddInternal(const nsCookieKey &aKey,
// check if we have to delete an old cookie.
nsCookieEntry *entry = mDBState->hostTable.GetEntry(aKey);
if (entry && entry->GetCookies().Length() >= mMaxCookiesPerHost) {
nsListIter iter;
nsTArray<nsListIter> removedIterList;
// Prioritize evicting insecure cookies.
// (draft-ietf-httpbis-cookie-alone section 3.3)
mozilla::Maybe<bool> optionalSecurity = mLeaveSecureAlone ? Some(false) : Nothing();
int64_t oldestCookieTime = FindStaleCookie(entry, currentTime, aHostURI, optionalSecurity, iter);
if (iter.entry == nullptr) {
uint32_t limit = mMaxCookiesPerHost - mCookieQuotaPerHost;
FindStaleCookies(entry, currentTime, optionalSecurity, removedIterList, limit);
if (removedIterList.Length() == 0) {
if (aCookie->IsSecure()) {
// It's valid to evict a secure cookie for another secure cookie.
oldestCookieTime = FindStaleCookie(entry, currentTime, aHostURI, Some(true), iter);
FindStaleCookies(entry, currentTime, Some(true), removedIterList, limit);
} else {
Telemetry::Accumulate(Telemetry::COOKIE_LEAVE_SECURE_ALONE,
EVICTING_SECURE_BLOCKED);
@ -3801,17 +3852,22 @@ nsCookieService::AddInternal(const nsCookieKey &aKey,
}
}
MOZ_ASSERT(iter.entry);
oldCookie = iter.Cookie();
if (oldestCookieTime > 0 && mLeaveSecureAlone) {
TelemetryForEvictingStaleCookie(oldCookie, oldestCookieTime);
MOZ_ASSERT(!removedIterList.IsEmpty());
// Sort |removedIterList| by index again, since we have to remove the cookie
// in the reverse order.
removedIterList.Sort(CompareCookiesByIndex());
for (auto it = removedIterList.rbegin(); it != removedIterList.rend(); it++) {
RefPtr<nsCookie> evictedCookie = (*it).Cookie();
if (mLeaveSecureAlone && evictedCookie->Expiry() <= currentTime) {
TelemetryForEvictingStaleCookie(evictedCookie,
evictedCookie->LastAccessed());
}
COOKIE_LOGEVICTED(evictedCookie, "Too many cookies for this domain");
RemoveCookieFromList(*it);
CreateOrUpdatePurgeList(getter_AddRefs(purgedList), evictedCookie);
MOZ_ASSERT((*it).entry);
}
// remove the oldest cookie from the domain
RemoveCookieFromList(iter);
COOKIE_LOGEVICTED(oldCookie, "Too many cookies for this domain");
purgedList = CreatePurgeList(oldCookie);
} else if (mDBState->cookieCount >= ADD_TEN_PERCENT(mMaxNumberOfCookies)) {
int64_t maxAge = aCurrentTimeInUsec - mDBState->cookieOldestTime;
int64_t purgeAge = ADD_TEN_PERCENT(mCookiePurgeAge);
@ -4573,26 +4629,6 @@ public:
}
};
// comparator class for sorting cookies by entry and index.
class CompareCookiesByIndex {
public:
bool Equals(const nsListIter &a, const nsListIter &b) const
{
NS_ASSERTION(a.entry != b.entry || a.index != b.index,
"cookie indexes should never be equal");
return false;
}
bool LessThan(const nsListIter &a, const nsListIter &b) const
{
// compare by entryclass pointer, then by index.
if (a.entry != b.entry)
return a.entry < b.entry;
return a.index < b.index;
}
};
// purges expired and old cookies in a batch operation.
already_AddRefed<nsIArray>
nsCookieService::PurgeCookies(int64_t aCurrentTimeInUsec)
@ -4775,94 +4811,75 @@ nsCookieService::CookieExistsNative(nsICookie2* aCookie,
return NS_OK;
}
// For a given base domain, find either an expired cookie or the oldest cookie
// by lastAccessed time.
int64_t
nsCookieService::FindStaleCookie(nsCookieEntry *aEntry,
int64_t aCurrentTime,
nsIURI* aSource,
const mozilla::Maybe<bool> &aIsSecure,
nsListIter &aIter)
{
aIter.entry = nullptr;
bool requireHostMatch = true;
nsAutoCString baseDomain, sourceHost, sourcePath;
if (aSource) {
GetBaseDomain(mTLDService, aSource, baseDomain, requireHostMatch);
aSource->GetAsciiHost(sourceHost);
sourcePath = GetPathFromURI(aSource);
// Cookie comparator for the priority queue used in FindStaleCookies.
// Note that the expired cookie has the highest priority.
// Other non-expired cookies are sorted by their age.
class CookieIterComparator {
private:
CompareCookiesByAge mAgeComparator;
int64_t mCurrentTime;
public:
explicit CookieIterComparator(int64_t aTime)
: mCurrentTime(aTime) {}
bool LessThan(const nsListIter& lhs, const nsListIter& rhs)
{
bool lExpired = lhs.Cookie()->Expiry() <= mCurrentTime;
bool rExpired = rhs.Cookie()->Expiry() <= mCurrentTime;
if (lExpired && !rExpired) {
return true;
}
if (!lExpired && rExpired) {
return false;
}
return mAgeComparator.LessThan(lhs, rhs);
}
};
// Given the output iter array and the count limit, find cookies
// sort by expiry and lastAccessed time.
void
nsCookieService::FindStaleCookies(nsCookieEntry *aEntry,
int64_t aCurrentTime,
const mozilla::Maybe<bool> &aIsSecure,
nsTArray<nsListIter>& aOutput,
uint32_t aLimit)
{
MOZ_ASSERT(aLimit);
const nsCookieEntry::ArrayType &cookies = aEntry->GetCookies();
aOutput.Clear();
int64_t oldestNonMatchingCookieTime = 0;
nsListIter oldestNonMatchingCookie;
oldestNonMatchingCookie.entry = nullptr;
CookieIterComparator comp(aCurrentTime);
nsTPriorityQueue<nsListIter, CookieIterComparator> queue(comp);
int64_t oldestCookieTime = 0;
nsListIter oldestCookie;
oldestCookie.entry = nullptr;
int64_t actualOldestCookieTime = cookies.Length() ? cookies[0]->LastAccessed() : 0;
for (nsCookieEntry::IndexType i = 0; i < cookies.Length(); ++i) {
nsCookie *cookie = cookies[i];
// If we found an expired cookie, we're done.
if (cookie->Expiry() <= aCurrentTime) {
aIter.entry = aEntry;
aIter.index = i;
return -1;
queue.Push(nsListIter(aEntry, i));
continue;
}
int64_t lastAccessed = cookie->LastAccessed();
// Record the age of the oldest cookie that is stored for this host.
// oldestCookieTime is the age of the oldest cookie with a matching
// secure flag, which may be more recent than an older cookie with
// a non-matching secure flag.
if (actualOldestCookieTime > lastAccessed) {
actualOldestCookieTime = lastAccessed;
}
if (aIsSecure.isSome() && !aIsSecure.value()) {
// We want to look for the oldest non-secure cookie first time through,
// then find the oldest secure cookie the second time we are called.
// We want to look for the non-secure cookie first time through,
// then find the secure cookie the second time this function is called.
if (cookie->IsSecure()) {
continue;
}
}
// This cookie is a candidate for eviction if we have no information about
// the source request, or if it is not a path or domain match against the
// source request.
bool isPrimaryEvictionCandidate = true;
if (aSource) {
isPrimaryEvictionCandidate = !PathMatches(cookie, sourcePath) || !DomainMatches(cookie, sourceHost);
}
if (isPrimaryEvictionCandidate &&
(!oldestNonMatchingCookie.entry ||
oldestNonMatchingCookieTime > lastAccessed)) {
oldestNonMatchingCookieTime = lastAccessed;
oldestNonMatchingCookie.entry = aEntry;
oldestNonMatchingCookie.index = i;
}
// Check if we've found the oldest cookie so far.
if (!oldestCookie.entry || oldestCookieTime > lastAccessed) {
oldestCookieTime = lastAccessed;
oldestCookie.entry = aEntry;
oldestCookie.index = i;
}
queue.Push(nsListIter(aEntry, i));
}
// Prefer to evict the oldest cookie with a non-matching path/domain,
// followed by the oldest matching cookie.
if (oldestNonMatchingCookie.entry) {
aIter = oldestNonMatchingCookie;
} else {
aIter = oldestCookie;
uint32_t count = 0;
while (!queue.IsEmpty() && count < aLimit) {
aOutput.AppendElement(queue.Pop());
count++;
}
return actualOldestCookieTime;
}
void

View File

@ -317,13 +317,14 @@ class nsCookieService final : public nsICookieService
already_AddRefed<nsIArray> PurgeCookies(int64_t aCurrentTimeInUsec);
bool FindCookie(const nsCookieKey& aKey, const nsCString& aHost, const nsCString& aName, const nsCString& aPath, nsListIter &aIter);
bool FindSecureCookie(const nsCookieKey& aKey, nsCookie* aCookie);
int64_t FindStaleCookie(nsCookieEntry *aEntry, int64_t aCurrentTime, nsIURI* aSource, const mozilla::Maybe<bool> &aIsSecure, nsListIter &aIter);
void FindStaleCookies(nsCookieEntry *aEntry, int64_t aCurrentTime, const mozilla::Maybe<bool> &aIsSecure, nsTArray<nsListIter>& aOutput, uint32_t aLimit);
void TelemetryForEvictingStaleCookie(nsCookie* aEvicted, int64_t oldestCookieTime);
void NotifyRejected(nsIURI *aHostURI, nsIChannel* aChannel, uint32_t aRejectedReason);
void NotifyThirdParty(nsIURI *aHostURI, bool aAccepted, nsIChannel *aChannel);
void NotifyChanged(nsISupports *aSubject, const char16_t *aData, bool aOldCookieIsSession = false, bool aFromHttp = false);
void NotifyPurged(nsICookie2* aCookie);
already_AddRefed<nsIArray> CreatePurgeList(nsICookie2* aCookie);
void CreateOrUpdatePurgeList(nsIArray** aPurgeList, nsICookie2* aCookie);
void UpdateCookieOldestTime(DBState* aDBState, nsCookie* aCookie);
nsresult GetCookiesWithOriginAttributes(const mozilla::OriginAttributesPattern& aPattern, const nsCString& aBaseDomain, nsISimpleEnumerator **aEnumerator);
@ -362,6 +363,7 @@ class nsCookieService final : public nsICookieService
bool mLeaveSecureAlone;
uint16_t mMaxNumberOfCookies;
uint16_t mMaxCookiesPerHost;
uint16_t mCookieQuotaPerHost;
int64_t mCookiePurgeAge;
// thread

View File

@ -9,215 +9,69 @@ const cm = cs.QueryInterface(Ci.nsICookieManager);
function run_test() {
var tests = [];
Services.prefs.setIntPref("network.cookie.staleThreshold", 0);
for (var host of BASE_HOSTNAMES) {
var base = SUBDOMAINS[0] + host;
var sub = SUBDOMAINS[1] + host;
var other = SUBDOMAINS[2] + host;
var another = SUBDOMAINS[3] + host;
tests.push([host, test_basic_eviction.bind(this, base, sub, other, another)]);
add_task(async function a() {
var t = tests.splice(0, 1)[0];
info('testing with host ' + t[0]);
await t[1]();
cm.removeAll();
});
tests.push([host, test_domain_or_path_matches_not_both.bind(this, base, sub, other, another)]);
add_task(async function() {
var t = tests.splice(0, 1)[0];
info('testing with host ' + t[0]);
await t[1]();
cm.removeAll();
});
}
add_task(async function() {
await test_localdomain();
await test_basic_eviction("example.org");
cm.removeAll();
});
add_task(async function() {
await test_path_prefix();
});
run_next_test();
}
// Verify that cookies that share a path prefix with the URI path are still considered
// candidates for eviction, since the paths do not actually match.
async function test_path_prefix() {
Services.prefs.setIntPref("network.cookie.maxPerHost", 2);
const BASE_URI = Services.io.newURI("http://example.org/");
const BASE_BAR = Services.io.newURI("http://example.org/bar/");
const BASE_BARBAR = Services.io.newURI("http://example.org/barbar/");
await setCookie("session_first", null, null, null, BASE_URI);
await setCookie("session_second", null, "/bar", null, BASE_BAR);
verifyCookies(['session_first', 'session_second'], BASE_URI);
await setCookie("session_third", null, "/barbar", null, BASE_BARBAR);
verifyCookies(['session_first', 'session_third'], BASE_URI);
}
// Verify that subdomains of localhost are treated as separate hosts and aren't considered
// candidates for eviction.
async function test_localdomain() {
Services.prefs.setIntPref("network.cookie.maxPerHost", 2);
const BASE_URI = Services.io.newURI("http://localhost");
const BASE_BAR = Services.io.newURI("http://localhost/bar");
const OTHER_URI = Services.io.newURI("http://other.localhost");
const OTHER_BAR = Services.io.newURI("http://other.localhost/bar");
await setCookie("session_no_path", null, null, null, BASE_URI);
await setCookie("session_bar_path", null, "/bar", null, BASE_BAR);
await setCookie("session_no_path", null, null, null, OTHER_URI);
await setCookie("session_bar_path", null, "/bar", null, OTHER_BAR);
verifyCookies(['session_no_path',
'session_bar_path'], BASE_URI);
verifyCookies(['session_no_path',
'session_bar_path'], OTHER_URI);
await setCookie("session_another_no_path", null, null, null, BASE_URI);
verifyCookies(['session_no_path',
'session_another_no_path'], BASE_URI);
await setCookie("session_another_no_path", null, null, null, OTHER_URI);
verifyCookies(['session_no_path',
'session_another_no_path'], OTHER_URI);
}
// Ensure that cookies are still considered candidates for eviction if either the domain
// or path matches, but not both.
async function test_domain_or_path_matches_not_both(base_host,
subdomain_host,
other_subdomain_host,
another_subdomain_host) {
Services.prefs.setIntPref("network.cookie.maxPerHost", 2);
const BASE_URI = Services.io.newURI("http://" + base_host);
const PUB_FOO_PATH = Services.io.newURI("http://" + subdomain_host + "/foo/");
const WWW_BAR_PATH = Services.io.newURI("http://" + other_subdomain_host + "/bar/");
const OTHER_BAR_PATH = Services.io.newURI("http://" + another_subdomain_host + "/bar/");
const PUB_BAR_PATH = Services.io.newURI("http://" + subdomain_host + "/bar/");
const WWW_FOO_PATH = Services.io.newURI("http://" + other_subdomain_host + "/foo/");
await setCookie("session_pub_with_foo_path", subdomain_host, "/foo", null, PUB_FOO_PATH);
await setCookie("session_www_with_bar_path", other_subdomain_host, "/bar", null, WWW_BAR_PATH);
verifyCookies(['session_pub_with_foo_path',
'session_www_with_bar_path'], BASE_URI);
await setCookie("session_pub_with_bar_path", subdomain_host, "/bar", null, PUB_BAR_PATH);
verifyCookies(['session_www_with_bar_path',
'session_pub_with_bar_path'], BASE_URI);
await setCookie("session_other_with_bar_path", another_subdomain_host, "/bar", null, OTHER_BAR_PATH);
verifyCookies(['session_pub_with_bar_path',
'session_other_with_bar_path'], BASE_URI);
}
async function test_basic_eviction(base_host, subdomain_host, other_subdomain_host) {
async function test_basic_eviction(base_host) {
Services.prefs.setIntPref("network.cookie.quotaPerHost", 2);
Services.prefs.setIntPref("network.cookie.maxPerHost", 5);
const BASE_URI = Services.io.newURI("http://" + base_host);
const SUBDOMAIN_URI = Services.io.newURI("http://" + subdomain_host);
const OTHER_SUBDOMAIN_URI = Services.io.newURI("http://" + other_subdomain_host);
const FOO_PATH = Services.io.newURI("http://" + base_host + "/foo/");
const BAR_PATH = Services.io.newURI("http://" + base_host + "/bar/");
const ALL_SUBDOMAINS = '.' + base_host;
const OTHER_SUBDOMAIN = other_subdomain_host;
// Initialize the set of cookies with a mix of non-session cookies with no path,
// and session cookies with explicit paths. Any subsequent cookies added will cause
// existing cookies to be evicted.
await setCookie("non_session_non_path_non_domain", null, null, 100000, BASE_URI);
await setCookie("non_session_non_path_subdomain", ALL_SUBDOMAINS, null, 100000, SUBDOMAIN_URI);
await setCookie("session_non_path_pub_domain", OTHER_SUBDOMAIN, null, null, OTHER_SUBDOMAIN_URI);
await setCookie("session_foo_path", null, "/foo", null, FOO_PATH);
await setCookie("session_bar_path", null, "/bar", null, BAR_PATH);
verifyCookies(['non_session_non_path_non_domain',
'non_session_non_path_subdomain',
'session_non_path_pub_domain',
'session_foo_path',
'session_bar_path'], BASE_URI);
// Ensure that cookies set for the / path appear more recent.
cs.getCookieString(OTHER_SUBDOMAIN_URI, null)
verifyCookies(['non_session_non_path_non_domain',
'session_foo_path',
'session_bar_path',
'non_session_non_path_subdomain',
'session_non_path_pub_domain'], BASE_URI);
// Evict oldest cookie that does not match example.org/foo (session_bar_path)
await setCookie("session_foo_path_1", null, "/foo", null, FOO_PATH);
await setCookie("session_foo_path_2", null, "/foo", null, FOO_PATH);
verifyCookies(['non_session_non_path_non_domain',
'session_foo_path',
'non_session_non_path_subdomain',
'session_non_path_pub_domain',
'session_foo_path_2'], BASE_URI);
// Evict oldest cookie that does not match example.org/bar (session_foo_path)
await setCookie("session_bar_path_2", null, "/bar", null, BAR_PATH);
verifyCookies(['non_session_non_path_non_domain',
'non_session_non_path_subdomain',
'session_non_path_pub_domain',
await setCookie("session_foo_path_3", null, "/foo", null, FOO_PATH);
await setCookie("session_foo_path_4", null, "/foo", null, FOO_PATH);
await setCookie("session_foo_path_5", null, "/foo", null, FOO_PATH);
verifyCookies(['session_foo_path_1',
'session_foo_path_2',
'session_foo_path_3',
'session_foo_path_4',
'session_foo_path_5'], BASE_URI);
// Check if cookies are evicted by creation time.
await setCookie("session_foo_path_6", null, "/foo", null, FOO_PATH);
verifyCookies(['session_foo_path_4',
'session_foo_path_5',
'session_foo_path_6'], BASE_URI);
await setCookie("session_bar_path_1", null, "/bar", null, BAR_PATH);
await setCookie("session_bar_path_2", null, "/bar", null, BAR_PATH);
verifyCookies(['session_foo_path_4',
'session_foo_path_5',
'session_foo_path_6',
'session_bar_path_1',
'session_bar_path_2'], BASE_URI);
// Evict oldest cookie that does not match example.org/ (session_non_path_pub_domain)
await setCookie("non_session_non_path_non_domain_2", null, null, 100000, BASE_URI);
verifyCookies(['non_session_non_path_non_domain',
'non_session_non_path_subdomain',
'session_foo_path_2',
'session_bar_path_2',
'non_session_non_path_non_domain_2'], BASE_URI);
// Check if cookies are evicted by last accessed time.
cs.getCookieString(FOO_PATH, null);
await setCookie("session_foo_path_7", null, "/foo", null, FOO_PATH);
verifyCookies(['session_foo_path_5',
'session_foo_path_6',
'session_foo_path_7'], BASE_URI);
// Evict oldest cookie that does not match example.org/ (session_foo_path_2)
await setCookie("session_non_path_non_domain_3", null, null, null, BASE_URI);
verifyCookies(['non_session_non_path_non_domain',
'non_session_non_path_subdomain',
'session_bar_path_2',
'non_session_non_path_non_domain_2',
'session_non_path_non_domain_3'], BASE_URI);
await setCookie("non_session_expired_foo_path_1", null, "/foo", 1, FOO_PATH);
await setCookie("non_session_expired_foo_path_2", null, "/foo", 1, FOO_PATH);
verifyCookies(['session_foo_path_5',
'session_foo_path_6',
'session_foo_path_7',
'non_session_expired_foo_path_1',
'non_session_expired_foo_path_2'], BASE_URI);
// Evict oldest cookie; all such cookies match example.org/bar (non_session_non_path_non_domain)
await setCookie("non_session_bar_path_non_domain", null, null, 100000, BAR_PATH);
verifyCookies(['non_session_non_path_subdomain',
'session_bar_path_2',
'non_session_non_path_non_domain_2',
'session_non_path_non_domain_3',
'non_session_bar_path_non_domain'], BASE_URI);
// Evict oldest cookie that deose not match example.org/ (session_bar_path_2)
await setCookie("non_session_non_path_pub_domain", null, null, 100000, OTHER_SUBDOMAIN_URI);
verifyCookies(['non_session_non_path_subdomain',
'non_session_non_path_non_domain_2',
'session_non_path_non_domain_3',
'non_session_bar_path_non_domain',
'non_session_non_path_pub_domain'], BASE_URI);
// Evict oldest cookie that does not match example.org/bar (non_session_non_path_pub_domain)
await setCookie("non_session_bar_path_non_domain_2", null, '/bar', 100000, BAR_PATH);
verifyCookies(['non_session_non_path_subdomain',
'non_session_non_path_non_domain_2',
'session_non_path_non_domain_3',
'non_session_bar_path_non_domain',
'non_session_bar_path_non_domain_2'], BASE_URI);
// Evict oldest cookie that does not match example.org/ (non_session_bar_path_non_domain)
await setCookie("non_session_non_path_non_domain_4", null, null, 100000, BASE_URI);
verifyCookies(['non_session_non_path_subdomain',
'non_session_non_path_non_domain_2',
'session_non_path_non_domain_3',
'non_session_bar_path_non_domain_2',
'non_session_non_path_non_domain_4'], BASE_URI);
// At this point all remaining cookies have a path of / and either don't have a domain
// or have one that matches subdomains.
// They will therefore be evicted from oldest to newest if all new cookies added share
// similar characteristics.
// Check if expired cookies are evicted first.
await new Promise(resolve => do_timeout(1000, resolve));
await setCookie("session_foo_path_8", null, "/foo", null, FOO_PATH);
verifyCookies(['session_foo_path_6',
'session_foo_path_7',
'session_foo_path_8'], BASE_URI);
}
// Verify that the given cookie names exist, and are ordered from least to most recently accessed