commit 3f4905592370efbcc5667a9fced5149870545ecf parent 5b5e2828847907898a837f41b1b6e26a6c2ca20f Author: Andrea Marchesini <amarchesini@mozilla.com> Date: Mon, 20 Oct 2025 11:44:22 +0000 Bug 1994761 - Add the updateTime value to cookies, r=cookie-reviewers,valentin Differential Revision: https://phabricator.services.mozilla.com/D269114 Diffstat:
16 files changed, 392 insertions(+), 87 deletions(-)
diff --git a/netwerk/cookie/Cookie.cpp b/netwerk/cookie/Cookie.cpp @@ -131,6 +131,17 @@ already_AddRefed<Cookie> Cookie::CreateValidated( cookie->mData.lastAccessedInUSec() = currentTimeInUsec; } + if (cookie->mData.updateTimeInUSec() > currentTimeInUsec) { + uint64_t diffInSeconds = + (cookie->mData.updateTimeInUSec() - currentTimeInUsec) / + PR_USEC_PER_SEC; + mozilla::glean::networking::cookie_access_fixup_diff.AccumulateSingleSample( + diffInSeconds); + glean::networking::cookie_timestamp_fixed_count.Get("updateTime"_ns).Add(1); + + cookie->mData.updateTimeInUSec() = currentTimeInUsec; + } + return cookie.forget(); } @@ -207,6 +218,10 @@ NS_IMETHODIMP Cookie::GetLastAccessed(int64_t* aTime) { *aTime = LastAccessedInUSec(); return NS_OK; } +NS_IMETHODIMP Cookie::GetUpdateTime(int64_t* aTime) { + *aTime = UpdateTimeInUSec(); + return NS_OK; +} NS_IMETHODIMP Cookie::GetSameSite(int32_t* aSameSite) { *aSameSite = SameSite(); return NS_OK; diff --git a/netwerk/cookie/Cookie.h b/netwerk/cookie/Cookie.h @@ -90,6 +90,7 @@ class Cookie final : public nsICookie { inline int64_t CreationTimeInUSec() const { return mData.creationTimeInUSec(); } + inline int64_t UpdateTimeInUSec() const { return mData.updateTimeInUSec(); } inline bool IsSession() const { return mData.isSession(); } inline bool IsDomain() const { return *mData.host().get() == '.'; } inline bool IsSecure() const { return mData.isSecure(); } @@ -120,6 +121,9 @@ class Cookie final : public nsICookie { inline void SetCreationTimeInUSec(int64_t aTimeInUSec) { mData.creationTimeInUSec() = aTimeInUSec; } + inline void SetUpdateTimeInUSec(int64_t aTimeInUSec) { + mData.updateTimeInUSec() = aTimeInUSec; + } inline void SetSchemeMap(uint8_t aSchemeMap) { mData.schemeMap() = aSchemeMap; } diff --git a/netwerk/cookie/CookieCommons.cpp b/netwerk/cookie/CookieCommons.cpp @@ -428,6 +428,7 @@ already_AddRefed<Cookie> CookieCommons::CreateCookieFromDocument( cookie->SetLastAccessedInUSec(currentTimeInUsec); cookie->SetCreationTimeInUSec( Cookie::GenerateUniqueCreationTimeInUSec(currentTimeInUsec)); + cookie->SetUpdateTimeInUSec(cookie->CreationTimeInUSec()); aBaseDomain = baseDomain; aAttrs = cookiePrincipal->OriginAttributesRef(); diff --git a/netwerk/cookie/CookieLogging.cpp b/netwerk/cookie/CookieLogging.cpp @@ -127,6 +127,12 @@ void CookieLogging::LogCookie(Cookie* aCookie) { &explodedTime); MOZ_LOG(gCookieLog, LogLevel::Debug, ("created: %s", timeString)); + PR_ExplodeTime(aCookie->UpdateTimeInUSec(), PR_GMTParameters, + &explodedTime); + PR_FormatTimeUSEnglish(timeString, TIME_STRING_LENGTH, "%c GMT", + &explodedTime); + MOZ_LOG(gCookieLog, LogLevel::Debug, ("created: %s", timeString)); + MOZ_LOG(gCookieLog, LogLevel::Debug, ("is secure: %s\n", aCookie->IsSecure() ? "true" : "false")); MOZ_LOG(gCookieLog, LogLevel::Debug, diff --git a/netwerk/cookie/CookieParser.cpp b/netwerk/cookie/CookieParser.cpp @@ -664,6 +664,7 @@ void CookieParser::Parse(const nsACString& aBaseDomain, bool aRequireHostMatch, mCookieData.expiryInMSec() = INT64_MAX; mCookieData.creationTimeInUSec() = Cookie::GenerateUniqueCreationTimeInUSec(aCurrentTimeInUSec); + mCookieData.updateTimeInUSec() = mCookieData.creationTimeInUSec(); mCookieData.schemeMap() = CookieCommons::URIToSchemeType(mHostURI); diff --git a/netwerk/cookie/CookiePersistentStorage.cpp b/netwerk/cookie/CookiePersistentStorage.cpp @@ -28,7 +28,7 @@ #include "nsVariant.h" #include "prprf.h" -constexpr auto COOKIES_SCHEMA_VERSION = 16; +constexpr auto COOKIES_SCHEMA_VERSION = 17; // parameter indexes; see |Read| constexpr auto IDX_NAME = 0; @@ -44,6 +44,7 @@ constexpr auto IDX_ORIGIN_ATTRIBUTES = 9; constexpr auto IDX_SAME_SITE = 10; constexpr auto IDX_SCHEME_MAP = 11; constexpr auto IDX_PARTITIONED_ATTRIBUTE_SET = 12; +constexpr auto IDX_UPDATE_TIME_INUSEC = 13; #define COOKIES_FILE "cookies.sqlite" @@ -109,6 +110,9 @@ void BindCookieParameters(mozIStorageBindingParamsArray* aParamsArray, aCookie->RawIsPartitioned()); MOZ_ASSERT(NS_SUCCEEDED(rv)); + rv = params->BindInt64ByName("updateTime"_ns, aCookie->UpdateTimeInUSec()); + MOZ_ASSERT(NS_SUCCEEDED(rv)); + // Bind the params to the array. rv = aParamsArray->AddParams(params); MOZ_ASSERT(NS_SUCCEEDED(rv)); @@ -1580,6 +1584,21 @@ CookiePersistentStorage::OpenDBResult CookiePersistentStorage::TryInitDB( nsLiteralCString("UPDATE moz_cookies SET expiry = expiry * 1000;")); NS_ENSURE_SUCCESS(rv, RESULT_RETRY); + [[fallthrough]]; + } + + case 16: { + // Add the updateTime column to the table. + rv = mSyncConn->ExecuteSimpleSQL( + nsLiteralCString("ALTER TABLE moz_cookies ADD updateTime INTEGER")); + NS_ENSURE_SUCCESS(rv, RESULT_RETRY); + + // OK so... this is tricky because we have to guess the creationTime + rv = mSyncConn->ExecuteSimpleSQL(nsLiteralCString( + "UPDATE moz_cookies SET updateTime = CAST(strftime('%s','now') AS " + "INTEGER) * 1000000")); + NS_ENSURE_SUCCESS(rv, RESULT_RETRY); + // No more upgrades. Update the schema version. rv = mSyncConn->SetSchemaVersion(COOKIES_SCHEMA_VERSION); NS_ENSURE_SUCCESS(rv, RESULT_RETRY); @@ -1849,7 +1868,8 @@ CookiePersistentStorage::OpenDBResult CookiePersistentStorage::Read() { "originAttributes, " "sameSite, " "schemeMap, " - "isPartitionedAttributeSet " + "isPartitionedAttributeSet, " + "updateTime " "FROM moz_cookies"), getter_AddRefs(stmt)); @@ -1925,6 +1945,7 @@ UniquePtr<CookieStruct> CookiePersistentStorage::GetCookieFromRow( int64_t expiryInMSec = aRow->AsInt64(IDX_EXPIRY_INMSEC); int64_t lastAccessedInUSec = aRow->AsInt64(IDX_LAST_ACCESSED_INUSEC); int64_t creationTimeInUSec = aRow->AsInt64(IDX_CREATION_TIME_INUSEC); + int64_t updateTimeInUSec = aRow->AsInt64(IDX_UPDATE_TIME_INUSEC); bool isSecure = 0 != aRow->AsInt32(IDX_SECURE); bool isHttpOnly = 0 != aRow->AsInt32(IDX_HTTPONLY); int32_t sameSite = aRow->AsInt32(IDX_SAME_SITE); @@ -1935,7 +1956,7 @@ UniquePtr<CookieStruct> CookiePersistentStorage::GetCookieFromRow( // Create a new constCookie and assign the data. return MakeUnique<CookieStruct>( name, value, host, path, expiryInMSec, lastAccessedInUSec, - creationTimeInUSec, isHttpOnly, false, isSecure, + creationTimeInUSec, updateTimeInUSec, isHttpOnly, false, isSecure, isPartitionedAttributeSet, sameSite, static_cast<nsICookie::schemeType>(schemeMap)); } @@ -2148,7 +2169,8 @@ nsresult CookiePersistentStorage::InitDBConnInternal() { "isHttpOnly, " "sameSite, " "schemeMap, " - "isPartitionedAttributeSet " + "isPartitionedAttributeSet, " + "updateTime " ") VALUES (" ":originAttributes, " ":name, " @@ -2162,7 +2184,8 @@ nsresult CookiePersistentStorage::InitDBConnInternal() { ":isHttpOnly, " ":sameSite, " ":schemeMap, " - ":isPartitionedAttributeSet " + ":isPartitionedAttributeSet, " + ":updateTime " ")"), getter_AddRefs(mStmtInsert)); NS_ENSURE_SUCCESS(rv, rv); @@ -2207,6 +2230,7 @@ nsresult CookiePersistentStorage::CreateTableWorker(const char* aName) { "sameSite INTEGER DEFAULT 0, " "schemeMap INTEGER DEFAULT 0, " "isPartitionedAttributeSet INTEGER DEFAULT 0, " + "updateTime INTEGER, " "CONSTRAINT moz_uniqueid UNIQUE (name, host, path, originAttributes)" ")"); return mSyncConn->ExecuteSimpleSQL(command); diff --git a/netwerk/cookie/CookieService.cpp b/netwerk/cookie/CookieService.cpp @@ -172,7 +172,7 @@ bool ProcessSameSiteCookieForForeignRequest(nsIChannel* aChannel, // without a SameSite value when used for unsafe http methods. if (aLaxByDefault && aCookie->SameSite() == nsICookie::SAMESITE_UNSET && StaticPrefs::network_cookie_sameSite_laxPlusPOST_timeout() > 0 && - currentTimeInUsec - aCookie->CreationTimeInUSec() <= + currentTimeInUsec - aCookie->UpdateTimeInUSec() <= (StaticPrefs::network_cookie_sameSite_laxPlusPOST_timeout() * PR_USEC_PER_SEC) && !NS_IsSafeMethodNav(aChannel)) { @@ -629,6 +629,7 @@ CookieService::SetCookieStringFromHttp(nsIURI* aHostURI, cookie->SetLastAccessedInUSec(currentTimeInUsec); cookie->SetCreationTimeInUSec( Cookie::GenerateUniqueCreationTimeInUSec(currentTimeInUsec)); + cookie->SetUpdateTimeInUSec(cookie->CreationTimeInUSec()); // Use TargetBrowsingContext to also take frame loads into account. RefPtr<BrowsingContext> bc = loadInfo->GetTargetBrowsingContext(); @@ -822,11 +823,14 @@ nsresult CookieService::AddInternal( NS_ENSURE_SUCCESS(rv, rv); int64_t currentTimeInUsec = PR_Now(); - CookieStruct cookieData( - nsCString(aName), nsCString(aValue), host, nsCString(aPath), aExpiry, - currentTimeInUsec, - Cookie::GenerateUniqueCreationTimeInUSec(currentTimeInUsec), aIsHttpOnly, - aIsSession, aIsSecure, aIsPartitioned, aSameSite, aSchemeMap); + int64_t uniqueCreationTimeInUSec = + Cookie::GenerateUniqueCreationTimeInUSec(currentTimeInUsec); + + CookieStruct cookieData(nsCString(aName), nsCString(aValue), host, + nsCString(aPath), aExpiry, currentTimeInUsec, + uniqueCreationTimeInUSec, uniqueCreationTimeInUSec, + aIsHttpOnly, aIsSession, aIsSecure, aIsPartitioned, + aSameSite, aSchemeMap); RefPtr<CookieValidation> cv = CookieValidation::Validate(cookieData); @@ -1731,6 +1735,7 @@ nsICookieValidation::ValidationError CookieService::SetCookiesFromIPC( cookie->SetLastAccessedInUSec(currentTimeInUsec); cookie->SetCreationTimeInUSec( Cookie::GenerateUniqueCreationTimeInUSec(currentTimeInUsec)); + cookie->SetUpdateTimeInUSec(cookie->CreationTimeInUSec()); storage->AddCookie(nullptr, aBaseDomain, aAttrs, cookie, currentTimeInUsec, aHostURI, ""_ns, aFromHttp, aIsThirdParty, diff --git a/netwerk/cookie/CookieValidation.cpp b/netwerk/cookie/CookieValidation.cpp @@ -115,10 +115,10 @@ void CookieValidation::ValidateInternal() { // This part checks if the caleers have set the expiry value to max 400 days. if (!mCookieData.isSession()) { int64_t maxageCap = StaticPrefs::network_cookie_maxageCap(); - int64_t currentTimeInMSec = - mCookieData.creationTimeInUSec() / PR_USEC_PER_MSEC; + int64_t creationTimeInMSec = + mCookieData.updateTimeInUSec() / PR_USEC_PER_MSEC; int64_t expiryInMSec = mCookieData.expiryInMSec(); - if (maxageCap && expiryInMSec > currentTimeInMSec + maxageCap * 1000) { + if (maxageCap && expiryInMSec > creationTimeInMSec + maxageCap * 1000) { mResult = eRejectedAttributeExpiryOversize; return; } diff --git a/netwerk/cookie/nsICookie.idl b/netwerk/cookie/nsICookie.idl @@ -122,6 +122,14 @@ interface nsICookie : nsISupports { readonly attribute int64_t creationTime; /** + * the update time of the cookie, in microseconds + * since midnight (00:00:00), January 1, 1970 UTC. + * It is the same as creationTime for a new cookie + * or different for a cookie that was replaced. + */ + readonly attribute int64_t updateTime; + + /** * the last time the cookie was accessed (i.e. created, * modified, or read by the server), in microseconds * since midnight (00:00:00), January 1, 1970 UTC. diff --git a/netwerk/ipc/NeckoChannelParams.ipdlh b/netwerk/ipc/NeckoChannelParams.ipdlh @@ -499,6 +499,7 @@ struct CookieStruct int64_t expiryInMSec; int64_t lastAccessedInUSec; int64_t creationTimeInUSec; + int64_t updateTimeInUSec; bool isHttpOnly; bool isSession; bool isSecure; diff --git a/netwerk/test/unit/head_cookies.js b/netwerk/test/unit/head_cookies.js @@ -206,7 +206,8 @@ function Cookie( originAttributes = {}, sameSite = Ci.nsICookie.SAMESITE_UNSET, schemeMap = Ci.nsICookie.SCHEME_UNSET, - isPartitioned = false + isPartitioned = false, + updateTime = null ) { this.name = name; this.value = value; @@ -223,6 +224,7 @@ function Cookie( this.sameSite = sameSite; this.schemeMap = schemeMap; this.isPartitioned = isPartitioned; + this.updateTime = updateTime || creationTime; // For pre version 15 compatibility: this.rawSameSite = sameSite; @@ -843,6 +845,83 @@ function CookieDatabaseConnection(file, schema) { break; } + case 17: { + if (!exists) { + this.db.executeSimpleSQL( + "CREATE TABLE moz_cookies ( \ + id INTEGER PRIMARY KEY, \ + originAttributes TEXT NOT NULL DEFAULT '', \ + name TEXT, \ + value TEXT, \ + host TEXT, \ + path TEXT, \ + expiry INTEGER, \ + lastAccessed INTEGER, \ + creationTime INTEGER, \ + isSecure INTEGER, \ + isHttpOnly INTEGER, \ + inBrowserElement INTEGER DEFAULT 0, \ + sameSite INTEGER DEFAULT 0, \ + schemeMap INTEGER DEFAULT 0, \ + isPartitionedAttributeSet INTEGER DEFAULT 0, \ + updateTime INTEGER, \ + CONSTRAINT moz_uniqueid UNIQUE (name, host, path, originAttributes))" + ); + + this.db.executeSimpleSQL("PRAGMA journal_mode = WAL"); + this.db.executeSimpleSQL("PRAGMA wal_autocheckpoint = 16"); + } + + this.stmtInsert = this.db.createStatement( + "INSERT INTO moz_cookies ( \ + name, \ + value, \ + host, \ + path, \ + expiry, \ + lastAccessed, \ + creationTime, \ + isSecure, \ + isHttpOnly, \ + inBrowserElement, \ + originAttributes, \ + sameSite, \ + schemeMap, \ + isPartitionedAttributeSet, \ + updateTime \ + ) VALUES ( \ + :name, \ + :value, \ + :host, \ + :path, \ + :expiry, \ + :lastAccessed, \ + :creationTime, \ + :isSecure, \ + :isHttpOnly, \ + :inBrowserElement, \ + :originAttributes, \ + :sameSite, \ + :schemeMap, \ + :isPartitionedAttributeSet, \ + :updateTime)" + ); + + this.stmtDelete = this.db.createStatement( + "DELETE FROM moz_cookies \ + WHERE name = :name AND host = :host AND path = :path AND \ + originAttributes = :originAttributes" + ); + + this.stmtUpdate = this.db.createStatement( + "UPDATE moz_cookies SET lastAccessed = :lastAccessed \ + WHERE name = :name AND host = :host AND path = :path AND \ + originAttributes = :originAttributes" + ); + + break; + } + default: do_throw("unrecognized schemaVersion!"); } @@ -1012,6 +1091,30 @@ CookieDatabaseConnection.prototype = { ); break; + case 17: + this.stmtInsert.bindByName("name", cookie.name); + this.stmtInsert.bindByName("value", cookie.value); + this.stmtInsert.bindByName("host", cookie.host); + this.stmtInsert.bindByName("path", cookie.path); + this.stmtInsert.bindByName("expiry", cookie.expiry); + this.stmtInsert.bindByName("lastAccessed", cookie.lastAccessed); + this.stmtInsert.bindByName("creationTime", cookie.creationTime); + this.stmtInsert.bindByName("isSecure", cookie.isSecure); + this.stmtInsert.bindByName("isHttpOnly", cookie.isHttpOnly); + this.stmtInsert.bindByName("inBrowserElement", cookie.inBrowserElement); + this.stmtInsert.bindByName( + "originAttributes", + ChromeUtils.originAttributesToSuffix(cookie.originAttributes) + ); + this.stmtInsert.bindByName("sameSite", cookie.sameSite); + this.stmtInsert.bindByName("schemeMap", cookie.schemeMap); + this.stmtInsert.bindByName( + "isPartitionedAttributeSet", + cookie.isPartitioned + ); + this.stmtInsert.bindByName("updateTime", cookie.updateTime); + break; + default: do_throw("unrecognized schemaVersion!"); } @@ -1042,6 +1145,9 @@ CookieDatabaseConnection.prototype = { case 12: case 13: case 14: + case 15: + case 16: + case 17: this.stmtDelete.bindByName("name", cookie.name); this.stmtDelete.bindByName("host", cookie.host); this.stmtDelete.bindByName("path", cookie.path); @@ -1088,6 +1194,9 @@ CookieDatabaseConnection.prototype = { case 12: case 13: case 14: + case 15: + case 16: + case 17: this.stmtDelete.bindByName("name", cookie.name); this.stmtDelete.bindByName("host", cookie.host); this.stmtDelete.bindByName("path", cookie.path); diff --git a/netwerk/test/unit/test_cookies_sync_failure.js b/netwerk/test/unit/test_cookies_sync_failure.js @@ -28,7 +28,7 @@ let now; let futureExpiry; let cookie; -var COOKIE_DATABASE_SCHEMA_CURRENT = 16; +var COOKIE_DATABASE_SCHEMA_CURRENT = 17; var test_generator = do_run_test(); diff --git a/netwerk/test/unit/test_invalid_cookie_fix.js b/netwerk/test/unit/test_invalid_cookie_fix.js @@ -51,11 +51,15 @@ add_task(async function test_invalid_cookie_fix() { // Create a schema 16 database. let schema16db = new CookieDatabaseConnection( do_get_cookie_file(profile), - 16 + 17 ); - let now = Date.now(); - let future = now + 60 * 60 * 24 * 1000 * 1000; + const nowInMSec = Date.now(); + const farFarInThePastInMSec = nowInMSec - 60 * 60 * 24 * 1000 * 1000; + const farFarInTheFutureInMSec = nowInMSec + 60 * 60 * 24 * 1000 * 1000; + const nearFutureInMSec = nowInMSec + 60 * 60 * 24 * 1000; + const nowInUSec = nowInMSec * 1000; + const farFarInThePastInUSec = farFarInThePastInMSec * 1000; // CookieValidation.result => eRejectedNoneRequiresSecure schema16db.insertCookie( @@ -64,16 +68,17 @@ add_task(async function test_invalid_cookie_fix() { "Some data", "foo.com", "/", - now, - now, - now, + nearFutureInMSec, + nowInUSec, + nowInUSec, false, false, false, false, {}, Ci.nsICookie.SAMESITE_NONE, - Ci.nsICookie.SCHEME_UNSET + Ci.nsICookie.SCHEME_UNSET, + nowInUSec ) ); @@ -84,16 +89,17 @@ add_task(async function test_invalid_cookie_fix() { "Some data", "foo.com", "/", - now, - now, - now, + nearFutureInMSec, + nowInUSec, + nowInUSec, false, false, false, false, {}, Ci.nsICookie.SAMESITE_LAX, - Ci.nsICookie.SCHEME_UNSET + Ci.nsICookie.SCHEME_UNSET, + nowInUSec ) ); @@ -104,16 +110,17 @@ add_task(async function test_invalid_cookie_fix() { "Some data", "foo.com", "/", - now, - now, - now, + nearFutureInMSec, + nowInUSec, + nowInUSec, false, false, false, false, {}, Ci.nsICookie.SAMESITE_STRICT, - Ci.nsICookie.SCHEME_UNSET + Ci.nsICookie.SCHEME_UNSET, + nowInUSec ) ); @@ -124,16 +131,17 @@ add_task(async function test_invalid_cookie_fix() { "Some data", "foo.com", "/", - now, - now, - now, + nearFutureInMSec, + nowInUSec, + nowInUSec, false, false, false, false, {}, Ci.nsICookie.SAMESITE_UNSET, - Ci.nsICookie.SCHEME_UNSET + Ci.nsICookie.SCHEME_UNSET, + nowInUSec ) ); @@ -144,16 +152,17 @@ add_task(async function test_invalid_cookie_fix() { "Some data", "foo.com", "/", - now, - now, - now, + nearFutureInMSec, + nowInUSec, + nowInUSec, false, true, false, false, {}, Ci.nsICookie.SAMESITE_NONE, - Ci.nsICookie.SCHEME_UNSET + Ci.nsICookie.SCHEME_UNSET, + nowInUSec ) ); @@ -164,16 +173,17 @@ add_task(async function test_invalid_cookie_fix() { "Some data", "foo.com", "/", - future, - now, - now, + farFarInTheFutureInMSec, + nowInUSec, + nowInUSec, false, false, false, false, {}, Ci.nsICookie.SAMESITE_UNSET, - Ci.nsICookie.SCHEME_UNSET + Ci.nsICookie.SCHEME_UNSET, + nowInUSec ) ); @@ -184,16 +194,17 @@ add_task(async function test_invalid_cookie_fix() { "", "foo.com", "/", - now, - now, - now, + nearFutureInMSec, + nowInUSec, + nowInUSec, false, false, false, false, {}, Ci.nsICookie.SAMESITE_UNSET, - Ci.nsICookie.SCHEME_UNSET + Ci.nsICookie.SCHEME_UNSET, + nowInUSec ) ); @@ -204,16 +215,17 @@ add_task(async function test_invalid_cookie_fix() { "", "foo.com", "/", - now, - now, - now, + nearFutureInMSec, + nowInUSec, + nowInUSec, false, false, false, false, {}, Ci.nsICookie.SAMESITE_UNSET, - Ci.nsICookie.SCHEME_UNSET + Ci.nsICookie.SCHEME_UNSET, + nowInUSec ) ); @@ -224,16 +236,38 @@ add_task(async function test_invalid_cookie_fix() { " test9", "foo.com", "/", - now, - now, - now, + nearFutureInMSec, + nowInUSec, + nowInUSec, false, false, false, false, {}, Ci.nsICookie.SAMESITE_UNSET, - Ci.nsICookie.SCHEME_UNSET + Ci.nsICookie.SCHEME_UNSET, + nowInUSec + ) + ); + + // CookieValidation.result => eOK + schema16db.insertCookie( + new Cookie( + "testA", + "Some data", + "foo.com", + "/", + nearFutureInMSec, + nowInUSec, + farFarInThePastInUSec, + false, + false, + false, + false, + {}, + Ci.nsICookie.SAMESITE_UNSET, + Ci.nsICookie.SCHEME_UNSET, + nowInUSec ) ); @@ -265,64 +299,71 @@ add_task(async function test_invalid_cookie_fix() { name: "", sameSite: Ci.nsICookie.SAMESITE_UNSET, isSecure: 0, - creationTime: now, - expiry: now, + creationTime: nowInUSec, + expiry: nearFutureInMSec, }, { name: " test8", sameSite: Ci.nsICookie.SAMESITE_UNSET, isSecure: 0, - creationTime: now, - expiry: now, + creationTime: nowInUSec, + expiry: nearFutureInMSec, }, { name: "test1", sameSite: Ci.nsICookie.SAMESITE_NONE, isSecure: 0, - creationTime: now, - expiry: now, + creationTime: nowInUSec, + expiry: nearFutureInMSec, }, { name: "test2", sameSite: Ci.nsICookie.SAMESITE_LAX, isSecure: 0, - creationTime: now, - expiry: now, + creationTime: nowInUSec, + expiry: nearFutureInMSec, }, { name: "test3", sameSite: Ci.nsICookie.SAMESITE_STRICT, isSecure: 0, - creationTime: now, - expiry: now, + creationTime: nowInUSec, + expiry: nearFutureInMSec, }, { name: "test4", sameSite: Ci.nsICookie.SAMESITE_UNSET, isSecure: 0, - creationTime: now, - expiry: now, + creationTime: nowInUSec, + expiry: nearFutureInMSec, }, { name: "test5", sameSite: Ci.nsICookie.SAMESITE_NONE, isSecure: 1, - creationTime: now, - expiry: now, + creationTime: nowInUSec, + expiry: nearFutureInMSec, }, { name: "test6", sameSite: Ci.nsICookie.SAMESITE_UNSET, isSecure: 0, - creationTime: now, - expiry: future, + creationTime: nowInUSec, + expiry: farFarInTheFutureInMSec, }, { name: "test9", sameSite: Ci.nsICookie.SAMESITE_UNSET, isSecure: 0, - creationTime: now, - expiry: now, + creationTime: nowInUSec, + expiry: nearFutureInMSec, + }, + { + name: "testA", + sameSite: Ci.nsICookie.SAMESITE_UNSET, + isSecure: 0, + creationTime: farFarInThePastInUSec, + expiry: nearFutureInMSec, }, ]); @@ -338,7 +379,7 @@ add_task(async function test_invalid_cookie_fix() { await promise; // Assert inserted cookies are in the db and correctly handled by services. - Assert.equal(Services.cookies.countCookiesFromHost("foo.com"), 6); + Assert.equal(Services.cookies.countCookiesFromHost("foo.com"), 7); // Close the profile. await promise_close_profile(); @@ -368,48 +409,55 @@ add_task(async function test_invalid_cookie_fix() { name: "test1", sameSite: Ci.nsICookie.SAMESITE_UNSET, isSecure: 0, - creationTime: now, - expiry: now, + creationTime: nowInUSec, + expiry: nearFutureInMSec, }, { name: "test2", sameSite: Ci.nsICookie.SAMESITE_LAX, isSecure: 0, - creationTime: now, - expiry: now, + creationTime: nowInUSec, + expiry: nearFutureInMSec, }, { name: "test3", sameSite: Ci.nsICookie.SAMESITE_STRICT, isSecure: 0, - creationTime: now, - expiry: now, + creationTime: nowInUSec, + expiry: nearFutureInMSec, }, { name: "test4", sameSite: Ci.nsICookie.SAMESITE_UNSET, isSecure: 0, - creationTime: now, - expiry: now, + creationTime: nowInUSec, + expiry: nearFutureInMSec, }, { name: "test5", sameSite: Ci.nsICookie.SAMESITE_NONE, isSecure: 1, - creationTime: now, - expiry: now, + creationTime: nowInUSec, + expiry: nearFutureInMSec, }, { name: "test6", sameSite: Ci.nsICookie.SAMESITE_UNSET, isSecure: 0, - creationTime: now, + creationTime: nowInUSec, expiry: results.find(a => a.name === "test6").expiry, }, + { + name: "testA", + sameSite: Ci.nsICookie.SAMESITE_UNSET, + isSecure: 0, + creationTime: farFarInThePastInUSec, + expiry: nearFutureInMSec, + }, ]); for (const r of results) { - Assert.less(r.expiry, future); + Assert.less(r.expiry, farFarInTheFutureInMSec); } stmt.finalize(); diff --git a/netwerk/test/unit/test_schema_17_migration.js b/netwerk/test/unit/test_schema_17_migration.js @@ -0,0 +1,81 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +// Test cookie database schema 17 +"use strict"; + +add_task(async function test_schema_17_migration() { + // Set up a profile. + let profile = do_get_profile(); + + // Start the cookieservice, to force creation of a database. + Services.cookies.sessionCookies; + + // Close the profile. + await promise_close_profile(); + + // Remove the cookie file in order to create another database file. + do_get_cookie_file(profile).remove(false); + + // Create a schema 16 database. + let schema16db = new CookieDatabaseConnection( + do_get_cookie_file(profile), + 16 + ); + + let nowInUSec = Date.now() * 1000; + let futureInMSec = Date.now() + 60 * 60 * 24 * 1000; + + schema16db.insertCookie( + new Cookie( + "test1", + "Some data", + "foo.com", + "/", + futureInMSec, + nowInUSec, + nowInUSec, + false, + false, + false, + false, + {}, + Ci.nsICookie.SAMESITE_NONE, + Ci.nsICookie.SCHEME_UNSET + ) + ); + + schema16db.close(); + schema16db = null; + + // Reload profile. + await promise_load_profile(); + + // Assert inserted cookies are in the db and correctly handled by services. + Assert.equal(Services.cookies.countCookiesFromHost("foo.com"), 1); + + // Check if the time was reset + { + const dbConnection = Services.storage.openDatabase( + do_get_cookie_file(profile) + ); + const stmt = dbConnection.createStatement( + "SELECT updateTime FROM moz_cookies" + ); + + const results = []; + while (stmt.executeStep()) { + results.push(stmt.getInt64(0)); + } + + Assert.equal(results.length, 1); + Assert.greater(results[0], 0); + + stmt.finalize(); + dbConnection.close(); + } + + // Cleanup + Services.cookies.removeAll(); + do_close_profile(); +}); diff --git a/netwerk/test/unit/xpcshell.toml b/netwerk/test/unit/xpcshell.toml @@ -1255,6 +1255,8 @@ skip-if = ["os == 'win' && os_version == '11.26100' && processor == 'x86_64' && ["test_schema_15_migration.js"] +["test_schema_17_migration.js"] + ["test_schema_2_migration.js"] ["test_schema_3_migration.js"] diff --git a/toolkit/components/cookiebanners/nsCookieRule.cpp b/toolkit/components/cookiebanners/nsCookieRule.cpp @@ -26,7 +26,7 @@ nsCookieRule::nsCookieRule(bool aIsOptOut, const nsACString& aName, mUnsetValue = aUnsetValue; net::CookieStruct cookieData(nsCString(aName), nsCString(aValue), - nsCString(aHost), nsCString(aPath), 0, 0, 0, + nsCString(aHost), nsCString(aPath), 0, 0, 0, 0, aIsHttpOnly, aIsSession, aIsSecure, false, aSameSite, aSchemeMap);