1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set sw=2 ts=8 et tw=80 : */
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
7 #include "CookieCommons.h"
8 #include "CookieLogging.h"
9 #include "CookieParser.h"
10 #include "mozilla/AppShutdown.h"
11 #include "mozilla/ClearOnShutdown.h"
12 #include "mozilla/Components.h"
13 #include "mozilla/ConsoleReportCollector.h"
14 #include "mozilla/ContentBlockingNotifier.h"
15 #include "mozilla/RefPtr.h"
16 #include "mozilla/dom/Document.h"
17 #include "mozilla/dom/nsMixedContentBlocker.h"
18 #include "mozilla/dom/Promise.h"
19 #include "mozilla/net/CookieJarSettings.h"
20 #include "mozilla/net/CookiePersistentStorage.h"
21 #include "mozilla/net/CookiePrivateStorage.h"
22 #include "mozilla/net/CookieService.h"
23 #include "mozilla/net/CookieServiceChild.h"
24 #include "mozilla/net/HttpBaseChannel.h"
25 #include "mozilla/net/NeckoCommon.h"
26 #include "mozilla/StaticPrefs_network.h"
27 #include "mozilla/StoragePrincipalHelper.h"
28 #include "mozilla/Telemetry.h"
29 #include "mozIThirdPartyUtil.h"
30 #include "nsICookiePermission.h"
31 #include "nsIConsoleReportCollector.h"
32 #include "nsIEffectiveTLDService.h"
33 #include "nsIScriptError.h"
34 #include "nsIScriptSecurityManager.h"
36 #include "nsIWebProgressListener.h"
37 #include "nsNetUtil.h"
38 #include "ThirdPartyUtil.h"
40 using namespace mozilla::dom
;
44 uint32_t MakeCookieBehavior(uint32_t aCookieBehavior
) {
45 bool isFirstPartyIsolated
= OriginAttributes::IsFirstPartyEnabled();
47 if (isFirstPartyIsolated
&&
49 nsICookieService::BEHAVIOR_REJECT_TRACKER_AND_PARTITION_FOREIGN
) {
50 return nsICookieService::BEHAVIOR_REJECT_TRACKER
;
52 return aCookieBehavior
;
56 Enables sanitizeOnShutdown cleaning prefs and disables the
57 network.cookie.lifetimePolicy
59 void MigrateCookieLifetimePrefs() {
60 // Former network.cookie.lifetimePolicy values ACCEPT_SESSION/ACCEPT_NORMALLY
61 // are not available anymore 2 = ACCEPT_SESSION
62 if (mozilla::Preferences::GetInt("network.cookie.lifetimePolicy") != 2) {
65 if (!mozilla::Preferences::GetBool("privacy.sanitize.sanitizeOnShutdown")) {
66 mozilla::Preferences::SetBool("privacy.sanitize.sanitizeOnShutdown", true);
67 // To avoid clearing categories that the user did not intend to clear
68 mozilla::Preferences::SetBool("privacy.clearOnShutdown.history", false);
69 mozilla::Preferences::SetBool("privacy.clearOnShutdown.formdata", false);
70 mozilla::Preferences::SetBool("privacy.clearOnShutdown.downloads", false);
71 mozilla::Preferences::SetBool("privacy.clearOnShutdown.sessions", false);
72 mozilla::Preferences::SetBool("privacy.clearOnShutdown.siteSettings",
75 // We will migrate the new clear on shutdown prefs to align both sets of
76 // prefs incase the user has not migrated yet. We don't have a new sessions
77 // prefs, as it was merged into cookiesAndStorage as part of the effort for
78 // the clear data revamp Bug 1853996
79 mozilla::Preferences::SetBool(
80 "privacy.clearOnShutdown_v2.historyFormDataAndDownloads", false);
81 mozilla::Preferences::SetBool("privacy.clearOnShutdown_v2.siteSettings",
84 mozilla::Preferences::SetBool("privacy.clearOnShutdown.cookies", true);
85 mozilla::Preferences::SetBool("privacy.clearOnShutdown.cache", true);
86 mozilla::Preferences::SetBool("privacy.clearOnShutdown.offlineApps", true);
88 // Migrate the new clear on shutdown prefs
89 mozilla::Preferences::SetBool("privacy.clearOnShutdown_v2.cookiesAndStorage",
91 mozilla::Preferences::SetBool("privacy.clearOnShutdown_v2.cache", true);
92 mozilla::Preferences::ClearUser("network.cookie.lifetimePolicy");
95 } // anonymous namespace
98 uint32_t nsICookieManager::GetCookieBehavior(bool aIsPrivate
) {
100 // To sync the cookieBehavior pref between regular and private mode in ETP
101 // custom mode, we will return the regular cookieBehavior pref for private
103 // 1. The regular cookieBehavior pref has a non-default value.
104 // 2. And the private cookieBehavior pref has a default value.
105 // Also, this can cover the migration case where the user has a non-default
106 // cookieBehavior before the private cookieBehavior was introduced. The
107 // getter here will directly return the regular cookieBehavior, so that the
108 // cookieBehavior for private mode is consistent.
109 if (mozilla::Preferences::HasUserValue(
110 "network.cookie.cookieBehavior.pbmode")) {
111 return MakeCookieBehavior(
112 mozilla::StaticPrefs::network_cookie_cookieBehavior_pbmode());
115 if (mozilla::Preferences::HasUserValue("network.cookie.cookieBehavior")) {
116 return MakeCookieBehavior(
117 mozilla::StaticPrefs::network_cookie_cookieBehavior());
120 return MakeCookieBehavior(
121 mozilla::StaticPrefs::network_cookie_cookieBehavior_pbmode());
123 return MakeCookieBehavior(
124 mozilla::StaticPrefs::network_cookie_cookieBehavior());
130 /******************************************************************************
131 * CookieService impl:
132 * useful types & constants
133 ******************************************************************************/
135 static StaticRefPtr
<CookieService
> gCookieService
;
137 constexpr auto CONSOLE_REJECTION_CATEGORY
= "cookiesRejection"_ns
;
141 void ComposeCookieString(nsTArray
<RefPtr
<Cookie
>>& aCookieList
,
142 nsACString
& aCookieString
) {
143 for (Cookie
* cookie
: aCookieList
) {
144 // check if we have anything to write
145 if (!cookie
->Name().IsEmpty() || !cookie
->Value().IsEmpty()) {
146 // if we've already added a cookie to the return list, append a "; " so
147 // that subsequent cookies are delimited in the final list.
148 if (!aCookieString
.IsEmpty()) {
149 aCookieString
.AppendLiteral("; ");
152 if (!cookie
->Name().IsEmpty()) {
153 // we have a name and value - write both
154 aCookieString
+= cookie
->Name() + "="_ns
+ cookie
->Value();
157 aCookieString
+= cookie
->Value();
163 // Return false if the cookie should be ignored for the current channel.
164 bool ProcessSameSiteCookieForForeignRequest(nsIChannel
* aChannel
,
166 bool aIsSafeTopLevelNav
,
167 bool aHadCrossSiteRedirects
,
168 bool aLaxByDefault
) {
169 // If it's a cross-site request and the cookie is same site only (strict)
171 if (aCookie
->SameSite() == nsICookie::SAMESITE_STRICT
) {
175 // Explicit SameSite=None cookies are always processed. When laxByDefault
176 // is OFF then so are default cookies.
177 if (aCookie
->SameSite() == nsICookie::SAMESITE_NONE
||
178 (!aLaxByDefault
&& aCookie
->IsDefaultSameSite())) {
182 // Lax-by-default cookies are processed even with an intermediate
183 // cross-site redirect (they are treated like aIsSameSiteForeign = false).
184 if (aLaxByDefault
&& aCookie
->IsDefaultSameSite() && aHadCrossSiteRedirects
&&
186 network_cookie_sameSite_laxByDefault_allowBoomerangRedirect()) {
190 int64_t currentTimeInUsec
= PR_Now();
192 // 2 minutes of tolerance for 'SameSite=Lax by default' for cookies set
193 // without a SameSite value when used for unsafe http methods.
194 if (aLaxByDefault
&& aCookie
->IsDefaultSameSite() &&
195 StaticPrefs::network_cookie_sameSite_laxPlusPOST_timeout() > 0 &&
196 currentTimeInUsec
- aCookie
->CreationTime() <=
197 (StaticPrefs::network_cookie_sameSite_laxPlusPOST_timeout() *
199 !NS_IsSafeMethodNav(aChannel
)) {
203 MOZ_ASSERT((aLaxByDefault
&& aCookie
->IsDefaultSameSite()) ||
204 aCookie
->SameSite() == nsICookie::SAMESITE_LAX
);
205 // We only have SameSite=Lax or lax-by-default cookies at this point. These
206 // are processed only if it's a top-level navigation
207 return aIsSafeTopLevelNav
;
212 /******************************************************************************
213 * CookieService impl:
214 * singleton instance ctor/dtor methods
215 ******************************************************************************/
217 already_AddRefed
<nsICookieService
> CookieService::GetXPCOMSingleton() {
218 if (IsNeckoChild()) {
219 return CookieServiceChild::GetSingleton();
222 return GetSingleton();
225 already_AddRefed
<CookieService
> CookieService::GetSingleton() {
226 NS_ASSERTION(!IsNeckoChild(), "not a parent process");
228 if (gCookieService
) {
229 return do_AddRef(gCookieService
);
232 // Create a new singleton CookieService.
233 // We AddRef only once since XPCOM has rules about the ordering of module
234 // teardowns - by the time our module destructor is called, it's too late to
235 // Release our members (e.g. nsIObserverService and nsIPrefBranch), since GC
236 // cycles have already been completed and would result in serious leaks.
238 // TODO: Verify what is the earliest point in time during shutdown where
239 // we can deny the creation of the CookieService as a whole.
240 gCookieService
= new CookieService();
241 if (gCookieService
) {
242 if (NS_SUCCEEDED(gCookieService
->Init())) {
243 ClearOnShutdown(&gCookieService
);
245 gCookieService
= nullptr;
249 return do_AddRef(gCookieService
);
252 /******************************************************************************
253 * CookieService impl:
255 ******************************************************************************/
257 NS_IMPL_ISUPPORTS(CookieService
, nsICookieService
, nsICookieManager
,
258 nsIObserver
, nsISupportsWeakReference
, nsIMemoryReporter
)
260 CookieService::CookieService() = default;
262 nsresult
CookieService::Init() {
264 mTLDService
= mozilla::components::EffectiveTLD::Service(&rv
);
265 NS_ENSURE_SUCCESS(rv
, rv
);
267 mThirdPartyUtil
= mozilla::components::ThirdPartyUtil::Service();
268 NS_ENSURE_SUCCESS(rv
, rv
);
270 // Init our default, and possibly private CookieStorages.
271 InitCookieStorages();
273 // Migrate network.cookie.lifetimePolicy pref to sanitizeOnShutdown prefs
274 MigrateCookieLifetimePrefs();
276 RegisterWeakMemoryReporter(this);
278 nsCOMPtr
<nsIObserverService
> os
= services::GetObserverService();
280 os
->AddObserver(this, "profile-before-change", true);
281 os
->AddObserver(this, "profile-do-change", true);
282 os
->AddObserver(this, "last-pb-context-exited", true);
287 void CookieService::InitCookieStorages() {
288 NS_ASSERTION(!mPersistentStorage
, "already have a default CookieStorage");
289 NS_ASSERTION(!mPrivateStorage
, "already have a private CookieStorage");
291 // Create two new CookieStorages. If we are in or beyond our observed
292 // shutdown phase, just be non-persistent.
293 if (MOZ_UNLIKELY(StaticPrefs::network_cookie_noPersistentStorage() ||
294 AppShutdown::IsInOrBeyond(ShutdownPhase::AppShutdown
))) {
295 mPersistentStorage
= CookiePrivateStorage::Create();
297 mPersistentStorage
= CookiePersistentStorage::Create();
300 mPrivateStorage
= CookiePrivateStorage::Create();
303 void CookieService::CloseCookieStorages() {
304 // return if we already closed
305 if (!mPersistentStorage
) {
309 // Let's nullify both storages before calling Close().
310 RefPtr
<CookieStorage
> privateStorage
;
311 privateStorage
.swap(mPrivateStorage
);
313 RefPtr
<CookieStorage
> persistentStorage
;
314 persistentStorage
.swap(mPersistentStorage
);
316 privateStorage
->Close();
317 persistentStorage
->Close();
320 CookieService::~CookieService() {
321 CloseCookieStorages();
323 UnregisterWeakMemoryReporter(this);
325 gCookieService
= nullptr;
329 CookieService::Observe(nsISupports
* /*aSubject*/, const char* aTopic
,
330 const char16_t
* /*aData*/) {
332 if (!strcmp(aTopic
, "profile-before-change")) {
333 // The profile is about to change,
334 // or is going away because the application is shutting down.
336 // Close the default DB connection and null out our CookieStorages before
338 CloseCookieStorages();
340 } else if (!strcmp(aTopic
, "profile-do-change")) {
341 NS_ASSERTION(!mPersistentStorage
, "shouldn't have a default CookieStorage");
342 NS_ASSERTION(!mPrivateStorage
, "shouldn't have a private CookieStorage");
344 // the profile has already changed; init the db from the new location.
345 // if we are in the private browsing state, however, we do not want to read
346 // data into it - we should instead put it into the default state, so it's
347 // ready for us if and when we switch back to it.
348 InitCookieStorages();
350 } else if (!strcmp(aTopic
, "last-pb-context-exited")) {
351 // Flush all the cookies stored by private browsing contexts
352 OriginAttributesPattern pattern
;
353 pattern
.mPrivateBrowsingId
.Construct(1);
354 RemoveCookiesWithOriginAttributes(pattern
, ""_ns
);
355 mPrivateStorage
= CookiePrivateStorage::Create();
362 CookieService::GetCookieBehavior(bool aIsPrivate
, uint32_t* aCookieBehavior
) {
363 NS_ENSURE_ARG_POINTER(aCookieBehavior
);
364 *aCookieBehavior
= nsICookieManager::GetCookieBehavior(aIsPrivate
);
369 CookieService::GetCookieStringFromDocument(Document
* aDocument
,
370 nsACString
& aCookie
) {
371 NS_ENSURE_ARG(aDocument
);
377 if (!IsInitialized()) {
381 bool thirdParty
= true;
382 nsPIDOMWindowInner
* innerWindow
= aDocument
->GetInnerWindow();
383 // in gtests we don't have a window, let's consider those requests as 3rd
386 ThirdPartyUtil
* thirdPartyUtil
= ThirdPartyUtil::GetInstance();
388 if (thirdPartyUtil
) {
389 Unused
<< thirdPartyUtil
->IsThirdPartyWindow(
390 innerWindow
->GetOuterWindow(), nullptr, &thirdParty
);
394 nsCOMPtr
<nsIPrincipal
> cookiePrincipal
=
395 aDocument
->EffectiveCookiePrincipal();
397 nsTArray
<nsCOMPtr
<nsIPrincipal
>> principals
;
398 principals
.AppendElement(cookiePrincipal
);
400 // CHIPS - If CHIPS is enabled the partitioned cookie jar is always available
401 // (and therefore the partitioned principal), the unpartitioned cookie jar is
402 // only available in first-party or third-party with storageAccess contexts.
403 // In both cases, the document will have storage access.
404 bool isCHIPS
= StaticPrefs::network_cookie_CHIPS_enabled() &&
405 aDocument
->CookieJarSettings()->GetPartitionForeign();
406 bool documentHasStorageAccess
= false;
407 rv
= aDocument
->HasStorageAccessSync(documentHasStorageAccess
);
408 NS_ENSURE_SUCCESS(rv
, rv
);
409 if (isCHIPS
&& documentHasStorageAccess
) {
410 // Assert that the cookie principal is unpartitioned.
411 MOZ_ASSERT(cookiePrincipal
->OriginAttributesRef().mPartitionKey
.IsEmpty());
412 // Only append the partitioned originAttributes if the partitionKey is set.
413 // The partitionKey could be empty for partitionKey in partitioned
414 // originAttributes if the document is for privilege context, such as the
415 // extension's background page.
416 if (!aDocument
->PartitionedPrincipal()
417 ->OriginAttributesRef()
418 .mPartitionKey
.IsEmpty()) {
419 principals
.AppendElement(aDocument
->PartitionedPrincipal());
423 nsTArray
<RefPtr
<Cookie
>> cookieList
;
425 for (auto& principal
: principals
) {
426 if (!CookieCommons::IsSchemeSupported(principal
)) {
430 CookieStorage
* storage
= PickStorage(principal
->OriginAttributesRef());
432 nsAutoCString baseDomain
;
433 rv
= CookieCommons::GetBaseDomain(principal
, baseDomain
);
434 if (NS_WARN_IF(NS_FAILED(rv
))) {
438 nsAutoCString hostFromURI
;
439 rv
= nsContentUtils::GetHostOrIPv6WithBrackets(principal
, hostFromURI
);
440 if (NS_WARN_IF(NS_FAILED(rv
))) {
444 nsAutoCString pathFromURI
;
445 rv
= principal
->GetFilePath(pathFromURI
);
446 if (NS_WARN_IF(NS_FAILED(rv
))) {
450 int64_t currentTimeInUsec
= PR_Now();
451 int64_t currentTime
= currentTimeInUsec
/ PR_USEC_PER_SEC
;
453 nsTArray
<RefPtr
<Cookie
>> cookies
;
454 storage
->GetCookiesFromHost(baseDomain
, principal
->OriginAttributesRef(),
456 if (cookies
.IsEmpty()) {
460 // check if the nsIPrincipal is using an https secure protocol.
461 // if it isn't, then we can't send a secure cookie over the connection.
462 bool potentiallyTrustworthy
=
463 principal
->GetIsOriginPotentiallyTrustworthy();
467 // iterate the cookies!
468 for (Cookie
* cookie
: cookies
) {
469 // check the host, since the base domain lookup is conservative.
470 if (!CookieCommons::DomainMatches(cookie
, hostFromURI
)) {
474 // if the cookie is httpOnly and it's not going directly to the HTTP
475 // connection, don't send it
476 if (cookie
->IsHttpOnly()) {
480 if (thirdParty
&& !CookieCommons::ShouldIncludeCrossSiteCookieForDocument(
481 cookie
, aDocument
)) {
485 // if the cookie is secure and the host scheme isn't, we can't send it
486 if (cookie
->IsSecure() && !potentiallyTrustworthy
) {
490 // if the nsIURI path doesn't match the cookie path, don't send it back
491 if (!CookieCommons::PathMatches(cookie
, pathFromURI
)) {
495 // check if the cookie has expired
496 if (cookie
->Expiry() <= currentTime
) {
500 // all checks passed - add to list and check if lastAccessed stamp needs
502 cookieList
.AppendElement(cookie
);
503 if (cookie
->IsStale()) {
508 if (cookieList
.IsEmpty()) {
512 // update lastAccessed timestamps. we only do this if the timestamp is stale
513 // by a certain amount, to avoid thrashing the db during pageload.
515 storage
->StaleCookies(cookieList
, currentTimeInUsec
);
519 if (cookieList
.IsEmpty()) {
523 // return cookies in order of path length; longest to shortest.
524 // this is required per RFC2109. if cookies match in length,
525 // then sort by creation time (see bug 236772).
526 cookieList
.Sort(CompareCookiesForSending());
527 ComposeCookieString(cookieList
, aCookie
);
533 CookieService::GetCookieStringFromHttp(nsIURI
* aHostURI
, nsIChannel
* aChannel
,
534 nsACString
& aCookieString
) {
535 NS_ENSURE_ARG(aHostURI
);
536 NS_ENSURE_ARG(aChannel
);
538 aCookieString
.Truncate();
540 if (!CookieCommons::IsSchemeSupported(aHostURI
)) {
544 uint32_t rejectedReason
= 0;
545 ThirdPartyAnalysisResult result
= mThirdPartyUtil
->AnalyzeChannel(
546 aChannel
, false, aHostURI
, nullptr, &rejectedReason
);
548 bool isSafeTopLevelNav
= CookieCommons::IsSafeTopLevelNav(aChannel
);
549 bool hadCrossSiteRedirects
= false;
550 bool isSameSiteForeign
= CookieCommons::IsSameSiteForeign(
551 aChannel
, aHostURI
, &hadCrossSiteRedirects
);
553 OriginAttributes storageOriginAttributes
;
554 StoragePrincipalHelper::GetOriginAttributes(
555 aChannel
, storageOriginAttributes
,
556 StoragePrincipalHelper::eStorageAccessPrincipal
);
558 nsTArray
<OriginAttributes
> originAttributesList
;
559 originAttributesList
.AppendElement(storageOriginAttributes
);
561 // CHIPS - If CHIPS is enabled the partitioned cookie jar is always available
562 // (and therefore the partitioned OriginAttributes), the unpartitioned cookie
563 // jar is only available in first-party or third-party with storageAccess
565 nsCOMPtr
<nsICookieJarSettings
> cookieJarSettings
=
566 CookieCommons::GetCookieJarSettings(aChannel
);
567 bool isCHIPS
= StaticPrefs::network_cookie_CHIPS_enabled() &&
568 cookieJarSettings
->GetPartitionForeign();
569 bool isUnpartitioned
=
570 !result
.contains(ThirdPartyAnalysis::IsForeign
) ||
571 result
.contains(ThirdPartyAnalysis::IsStorageAccessPermissionGranted
);
572 if (isCHIPS
&& isUnpartitioned
) {
573 // Assert that the storage originAttributes is empty. In other words,
574 // it's unpartitioned.
575 MOZ_ASSERT(storageOriginAttributes
.mPartitionKey
.IsEmpty());
576 // Add the partitioned principal to principals
577 OriginAttributes partitionedOriginAttributes
;
578 StoragePrincipalHelper::GetOriginAttributes(
579 aChannel
, partitionedOriginAttributes
,
580 StoragePrincipalHelper::ePartitionedPrincipal
);
581 // Only append the partitioned originAttributes if the partitionKey is set.
582 // The partitionKey could be empty for partitionKey in partitioned
583 // originAttributes if the channel is for privilege request, such as
584 // extension's requests.
585 if (!partitionedOriginAttributes
.mPartitionKey
.IsEmpty()) {
586 originAttributesList
.AppendElement(partitionedOriginAttributes
);
590 AutoTArray
<RefPtr
<Cookie
>, 8> foundCookieList
;
592 aHostURI
, aChannel
, result
.contains(ThirdPartyAnalysis::IsForeign
),
593 result
.contains(ThirdPartyAnalysis::IsThirdPartyTrackingResource
),
594 result
.contains(ThirdPartyAnalysis::IsThirdPartySocialTrackingResource
),
595 result
.contains(ThirdPartyAnalysis::IsStorageAccessPermissionGranted
),
596 rejectedReason
, isSafeTopLevelNav
, isSameSiteForeign
,
597 hadCrossSiteRedirects
, true, false, originAttributesList
,
600 ComposeCookieString(foundCookieList
, aCookieString
);
602 if (!aCookieString
.IsEmpty()) {
603 COOKIE_LOGSUCCESS(GET_COOKIE
, aHostURI
, aCookieString
, nullptr, false);
609 CookieService::SetCookieStringFromDocument(Document
* aDocument
,
610 const nsACString
& aCookieString
) {
611 NS_ENSURE_ARG(aDocument
);
613 if (!IsInitialized()) {
617 nsCOMPtr
<nsIURI
> documentURI
;
618 nsAutoCString baseDomain
;
619 OriginAttributes attrs
;
621 int64_t currentTimeInUsec
= PR_Now();
623 // This function is executed in this context, I don't need to keep objects
625 auto hasExistingCookiesLambda
= [&](const nsACString
& aBaseDomain
,
626 const OriginAttributes
& aAttrs
) {
627 CookieStorage
* storage
= PickStorage(aAttrs
);
628 return !!storage
->CountCookiesFromHost(aBaseDomain
,
629 aAttrs
.mPrivateBrowsingId
);
632 auto* basePrincipal
= BasePrincipal::Cast(aDocument
->NodePrincipal());
633 basePrincipal
->GetURI(getter_AddRefs(documentURI
));
634 if (NS_WARN_IF(!documentURI
)) {
635 // Document's principal is not a content or null (may be system), so
640 // Console report takes care of the correct reporting at the exit of this
642 RefPtr
<ConsoleReportCollector
> crc
= new ConsoleReportCollector();
643 auto scopeExit
= MakeScopeExit([&] { crc
->FlushConsoleReports(aDocument
); });
645 CookieParser
cookieParser(crc
, documentURI
);
647 RefPtr
<Cookie
> cookie
= CookieCommons::CreateCookieFromDocument(
648 cookieParser
, aDocument
, aCookieString
, currentTimeInUsec
, mTLDService
,
649 mThirdPartyUtil
, hasExistingCookiesLambda
, baseDomain
, attrs
);
654 bool thirdParty
= true;
655 nsPIDOMWindowInner
* innerWindow
= aDocument
->GetInnerWindow();
656 // in gtests we don't have a window, let's consider those requests as 3rd
659 ThirdPartyUtil
* thirdPartyUtil
= ThirdPartyUtil::GetInstance();
661 if (thirdPartyUtil
) {
662 Unused
<< thirdPartyUtil
->IsThirdPartyWindow(
663 innerWindow
->GetOuterWindow(), nullptr, &thirdParty
);
667 if (thirdParty
&& !CookieCommons::ShouldIncludeCrossSiteCookieForDocument(
668 cookie
, aDocument
)) {
672 // add the cookie to the list. AddCookie() takes care of logging.
673 PickStorage(attrs
)->AddCookie(
674 &cookieParser
, baseDomain
, attrs
, cookie
, currentTimeInUsec
, documentURI
,
675 aCookieString
, false, thirdParty
, aDocument
->GetBrowsingContext());
680 CookieService::SetCookieStringFromHttp(nsIURI
* aHostURI
,
681 const nsACString
& aCookieHeader
,
682 nsIChannel
* aChannel
) {
683 NS_ENSURE_ARG(aHostURI
);
684 NS_ENSURE_ARG(aChannel
);
686 if (!IsInitialized()) {
690 if (!CookieCommons::IsSchemeSupported(aHostURI
)) {
694 uint32_t rejectedReason
= 0;
695 ThirdPartyAnalysisResult result
= mThirdPartyUtil
->AnalyzeChannel(
696 aChannel
, false, aHostURI
, nullptr, &rejectedReason
);
698 OriginAttributes storagePrincipalOriginAttributes
;
699 StoragePrincipalHelper::GetOriginAttributes(
700 aChannel
, storagePrincipalOriginAttributes
,
701 StoragePrincipalHelper::eStorageAccessPrincipal
);
703 // get the base domain for the host URI.
704 // e.g. for "www.bbc.co.uk", this would be "bbc.co.uk".
705 // file:// URI's (i.e. with an empty host) are allowed, but any other
706 // scheme must have a non-empty host. A trailing dot in the host
708 bool requireHostMatch
;
709 nsAutoCString baseDomain
;
710 nsresult rv
= CookieCommons::GetBaseDomain(mTLDService
, aHostURI
, baseDomain
,
713 COOKIE_LOGFAILURE(SET_COOKIE
, aHostURI
, aCookieHeader
,
714 "couldn't get base domain from URI");
718 nsCOMPtr
<nsICookieJarSettings
> cookieJarSettings
=
719 CookieCommons::GetCookieJarSettings(aChannel
);
721 nsAutoCString hostFromURI
;
722 nsContentUtils::GetHostOrIPv6WithBrackets(aHostURI
, hostFromURI
);
724 nsAutoCString baseDomainFromURI
;
725 rv
= CookieCommons::GetBaseDomainFromHost(mTLDService
, hostFromURI
,
727 NS_ENSURE_SUCCESS(rv
, NS_OK
);
729 CookieStorage
* storage
= PickStorage(storagePrincipalOriginAttributes
);
731 // check default prefs
732 uint32_t priorCookieCount
= storage
->CountCookiesFromHost(
733 baseDomainFromURI
, storagePrincipalOriginAttributes
.mPrivateBrowsingId
);
735 nsCOMPtr
<nsIConsoleReportCollector
> crc
= do_QueryInterface(aChannel
);
737 CookieStatus cookieStatus
= CheckPrefs(
738 crc
, cookieJarSettings
, aHostURI
,
739 result
.contains(ThirdPartyAnalysis::IsForeign
),
740 result
.contains(ThirdPartyAnalysis::IsThirdPartyTrackingResource
),
741 result
.contains(ThirdPartyAnalysis::IsThirdPartySocialTrackingResource
),
742 result
.contains(ThirdPartyAnalysis::IsStorageAccessPermissionGranted
),
743 aCookieHeader
, priorCookieCount
, storagePrincipalOriginAttributes
,
746 MOZ_ASSERT_IF(rejectedReason
, cookieStatus
== STATUS_REJECTED
);
748 // fire a notification if third party or if cookie was rejected
749 // (but not if there was an error)
750 switch (cookieStatus
) {
751 case STATUS_REJECTED
:
752 CookieCommons::NotifyRejected(aHostURI
, aChannel
, rejectedReason
,
754 return NS_OK
; // Stop here
755 case STATUS_REJECTED_WITH_ERROR
:
756 CookieCommons::NotifyRejected(aHostURI
, aChannel
, rejectedReason
,
759 case STATUS_ACCEPTED
: // Fallthrough
760 case STATUS_ACCEPT_SESSION
:
761 NotifyAccepted(aChannel
);
767 bool addonAllowsLoad
= false;
768 nsCOMPtr
<nsIURI
> channelURI
;
769 NS_GetFinalChannelURI(aChannel
, getter_AddRefs(channelURI
));
770 nsCOMPtr
<nsILoadInfo
> loadInfo
= aChannel
->LoadInfo();
771 addonAllowsLoad
= BasePrincipal::Cast(loadInfo
->TriggeringPrincipal())
772 ->AddonAllowsLoad(channelURI
);
774 bool isForeignAndNotAddon
= false;
775 if (!addonAllowsLoad
) {
776 mThirdPartyUtil
->IsThirdPartyChannel(aChannel
, aHostURI
,
777 &isForeignAndNotAddon
);
779 // include sub-document navigations from cross-site to same-site
780 // wrt top-level in our check for thirdparty-ness
781 if (StaticPrefs::network_cookie_sameSite_crossSiteIframeSetCheck() &&
782 !isForeignAndNotAddon
&&
783 loadInfo
->GetExternalContentPolicyType() ==
784 ExtContentPolicy::TYPE_SUBDOCUMENT
) {
785 bool triggeringPrincipalIsThirdParty
= false;
786 BasePrincipal::Cast(loadInfo
->TriggeringPrincipal())
787 ->IsThirdPartyURI(channelURI
, &triggeringPrincipalIsThirdParty
);
788 isForeignAndNotAddon
|= triggeringPrincipalIsThirdParty
;
792 bool mustBePartitioned
=
793 isForeignAndNotAddon
&&
794 cookieJarSettings
->GetCookieBehavior() ==
795 nsICookieService::BEHAVIOR_REJECT_TRACKER_AND_PARTITION_FOREIGN
&&
796 !result
.contains(ThirdPartyAnalysis::IsStorageAccessPermissionGranted
);
798 nsCString
cookieHeader(aCookieHeader
);
800 // CHIPS - The partitioned cookie jar is always available and it is always
801 // possible to store cookies in it using the "Partitioned" attribute.
802 // Prepare the partitioned principals OAs to enable possible partitioned
803 // cookie storing from first-party or with StorageAccess.
804 // Similar behavior to CookieServiceChild::SetCookieStringFromHttp().
805 OriginAttributes partitionedPrincipalOriginAttributes
;
806 bool isPartitionedPrincipal
=
807 !storagePrincipalOriginAttributes
.mPartitionKey
.IsEmpty();
808 bool isCHIPS
= StaticPrefs::network_cookie_CHIPS_enabled() &&
809 cookieJarSettings
->GetPartitionForeign();
810 // Only need to get OAs if we don't already use the partitioned principal.
811 if (isCHIPS
&& !isPartitionedPrincipal
) {
812 StoragePrincipalHelper::GetOriginAttributes(
813 aChannel
, partitionedPrincipalOriginAttributes
,
814 StoragePrincipalHelper::ePartitionedPrincipal
);
817 // process each cookie in the header
818 bool moreCookieToRead
= true;
819 while (moreCookieToRead
) {
820 CookieParser
cookieParser(crc
, aHostURI
);
822 moreCookieToRead
= cookieParser
.Parse(
823 baseDomain
, requireHostMatch
, cookieStatus
, cookieHeader
, true,
824 isForeignAndNotAddon
, mustBePartitioned
,
825 storagePrincipalOriginAttributes
.IsPrivateBrowsing());
827 if (!cookieParser
.ContainsCookie()) {
831 // check permissions from site permission list.
832 if (!CookieCommons::CheckCookiePermission(aChannel
,
833 cookieParser
.CookieData())) {
834 COOKIE_LOGFAILURE(SET_COOKIE
, aHostURI
, aCookieHeader
,
835 "cookie rejected by permission manager");
836 CookieCommons::NotifyRejected(
838 nsIWebProgressListener::STATE_COOKIES_BLOCKED_BY_PERMISSION
,
840 cookieParser
.RejectCookie(CookieParser::RejectedByPermissionManager
);
844 // CHIPS - If the partitioned attribute is set, store cookie in partitioned
845 // cookie jar independent of context. If the cookies are stored in the
846 // partitioned cookie jar anyway no special treatment of CHIPS cookies
848 bool needPartitioned
= isCHIPS
&&
849 cookieParser
.CookieData().isPartitioned() &&
850 !isPartitionedPrincipal
;
851 OriginAttributes
& cookieOriginAttributes
=
852 needPartitioned
? partitionedPrincipalOriginAttributes
853 : storagePrincipalOriginAttributes
;
854 // Assert that partitionedPrincipalOriginAttributes are initialized if used.
857 !partitionedPrincipalOriginAttributes
.mPartitionKey
.IsEmpty());
859 // create a new Cookie
860 RefPtr
<Cookie
> cookie
=
861 Cookie::Create(cookieParser
.CookieData(), cookieOriginAttributes
);
864 int64_t currentTimeInUsec
= PR_Now();
865 cookie
->SetLastAccessed(currentTimeInUsec
);
866 cookie
->SetCreationTime(
867 Cookie::GenerateUniqueCreationTime(currentTimeInUsec
));
869 // Use TargetBrowsingContext to also take frame loads into account.
870 RefPtr
<BrowsingContext
> bc
= loadInfo
->GetTargetBrowsingContext();
872 // add the cookie to the list. AddCookie() takes care of logging.
873 storage
->AddCookie(&cookieParser
, baseDomain
, cookieOriginAttributes
,
874 cookie
, currentTimeInUsec
, aHostURI
, aCookieHeader
, true,
875 isForeignAndNotAddon
, bc
);
881 void CookieService::NotifyAccepted(nsIChannel
* aChannel
) {
882 ContentBlockingNotifier::OnDecision(
883 aChannel
, ContentBlockingNotifier::BlockingDecision::eAllow
, 0);
886 /******************************************************************************
888 * public transaction helper impl
889 ******************************************************************************/
892 CookieService::RunInTransaction(nsICookieTransactionCallback
* aCallback
) {
893 NS_ENSURE_ARG(aCallback
);
895 if (!IsInitialized()) {
896 return NS_ERROR_NOT_AVAILABLE
;
899 mPersistentStorage
->EnsureInitialized();
900 return mPersistentStorage
->RunInTransaction(aCallback
);
903 /******************************************************************************
904 * nsICookieManager impl:
906 ******************************************************************************/
909 CookieService::RemoveAll() {
910 if (!IsInitialized()) {
911 return NS_ERROR_NOT_AVAILABLE
;
914 mPersistentStorage
->EnsureInitialized();
915 mPersistentStorage
->RemoveAll();
920 CookieService::GetCookies(nsTArray
<RefPtr
<nsICookie
>>& aCookies
) {
921 if (!IsInitialized()) {
922 return NS_ERROR_NOT_AVAILABLE
;
925 mPersistentStorage
->EnsureInitialized();
927 // We expose only non-private cookies.
928 mPersistentStorage
->GetCookies(aCookies
);
934 CookieService::GetSessionCookies(nsTArray
<RefPtr
<nsICookie
>>& aCookies
) {
935 if (!IsInitialized()) {
936 return NS_ERROR_NOT_AVAILABLE
;
939 mPersistentStorage
->EnsureInitialized();
941 // We expose only non-private cookies.
942 mPersistentStorage
->GetSessionCookies(aCookies
);
948 CookieService::Add(const nsACString
& aHost
, const nsACString
& aPath
,
949 const nsACString
& aName
, const nsACString
& aValue
,
950 bool aIsSecure
, bool aIsHttpOnly
, bool aIsSession
,
951 int64_t aExpiry
, JS::Handle
<JS::Value
> aOriginAttributes
,
952 int32_t aSameSite
, nsICookie::schemeType aSchemeMap
,
954 OriginAttributes attrs
;
956 if (!aOriginAttributes
.isObject() || !attrs
.Init(aCx
, aOriginAttributes
)) {
957 return NS_ERROR_INVALID_ARG
;
960 return AddNative(aHost
, aPath
, aName
, aValue
, aIsSecure
, aIsHttpOnly
,
961 aIsSession
, aExpiry
, &attrs
, aSameSite
, aSchemeMap
);
964 NS_IMETHODIMP_(nsresult
)
965 CookieService::AddNative(const nsACString
& aHost
, const nsACString
& aPath
,
966 const nsACString
& aName
, const nsACString
& aValue
,
967 bool aIsSecure
, bool aIsHttpOnly
, bool aIsSession
,
968 int64_t aExpiry
, OriginAttributes
* aOriginAttributes
,
969 int32_t aSameSite
, nsICookie::schemeType aSchemeMap
) {
970 if (NS_WARN_IF(!aOriginAttributes
)) {
971 return NS_ERROR_FAILURE
;
974 if (!IsInitialized()) {
975 return NS_ERROR_NOT_AVAILABLE
;
978 // first, normalize the hostname, and fail if it contains illegal characters.
979 nsAutoCString
host(aHost
);
980 nsresult rv
= NormalizeHost(host
);
981 NS_ENSURE_SUCCESS(rv
, rv
);
983 // get the base domain for the host URI.
984 // e.g. for "www.bbc.co.uk", this would be "bbc.co.uk".
985 nsAutoCString baseDomain
;
986 rv
= CookieCommons::GetBaseDomainFromHost(mTLDService
, host
, baseDomain
);
987 NS_ENSURE_SUCCESS(rv
, rv
);
989 int64_t currentTimeInUsec
= PR_Now();
990 CookieKey key
= CookieKey(baseDomain
, *aOriginAttributes
);
992 CookieStruct
cookieData(nsCString(aName
), nsCString(aValue
), nsCString(aHost
),
993 nsCString(aPath
), aExpiry
, currentTimeInUsec
,
994 Cookie::GenerateUniqueCreationTime(currentTimeInUsec
),
995 aIsHttpOnly
, aIsSession
, aIsSecure
, false, aSameSite
,
996 aSameSite
, aSchemeMap
);
998 RefPtr
<Cookie
> cookie
= Cookie::Create(cookieData
, key
.mOriginAttributes
);
1001 CookieStorage
* storage
= PickStorage(*aOriginAttributes
);
1002 storage
->AddCookie(nullptr, baseDomain
, *aOriginAttributes
, cookie
,
1003 currentTimeInUsec
, nullptr, VoidCString(), true,
1004 !aOriginAttributes
->mPartitionKey
.IsEmpty(), nullptr);
1008 nsresult
CookieService::Remove(const nsACString
& aHost
,
1009 const OriginAttributes
& aAttrs
,
1010 const nsACString
& aName
,
1011 const nsACString
& aPath
) {
1012 // first, normalize the hostname, and fail if it contains illegal characters.
1013 nsAutoCString
host(aHost
);
1014 nsresult rv
= NormalizeHost(host
);
1015 NS_ENSURE_SUCCESS(rv
, rv
);
1017 nsAutoCString baseDomain
;
1018 if (!host
.IsEmpty()) {
1019 rv
= CookieCommons::GetBaseDomainFromHost(mTLDService
, host
, baseDomain
);
1020 NS_ENSURE_SUCCESS(rv
, rv
);
1023 if (!IsInitialized()) {
1024 return NS_ERROR_NOT_AVAILABLE
;
1027 CookieStorage
* storage
= PickStorage(aAttrs
);
1028 storage
->RemoveCookie(baseDomain
, aAttrs
, host
, PromiseFlatCString(aName
),
1029 PromiseFlatCString(aPath
));
1035 CookieService::Remove(const nsACString
& aHost
, const nsACString
& aName
,
1036 const nsACString
& aPath
,
1037 JS::Handle
<JS::Value
> aOriginAttributes
, JSContext
* aCx
) {
1038 OriginAttributes attrs
;
1040 if (!aOriginAttributes
.isObject() || !attrs
.Init(aCx
, aOriginAttributes
)) {
1041 return NS_ERROR_INVALID_ARG
;
1044 return RemoveNative(aHost
, aName
, aPath
, &attrs
);
1047 NS_IMETHODIMP_(nsresult
)
1048 CookieService::RemoveNative(const nsACString
& aHost
, const nsACString
& aName
,
1049 const nsACString
& aPath
,
1050 OriginAttributes
* aOriginAttributes
) {
1051 if (NS_WARN_IF(!aOriginAttributes
)) {
1052 return NS_ERROR_FAILURE
;
1055 nsresult rv
= Remove(aHost
, *aOriginAttributes
, aName
, aPath
);
1056 if (NS_WARN_IF(NS_FAILED(rv
))) {
1063 void CookieService::GetCookiesForURI(
1064 nsIURI
* aHostURI
, nsIChannel
* aChannel
, bool aIsForeign
,
1065 bool aIsThirdPartyTrackingResource
,
1066 bool aIsThirdPartySocialTrackingResource
,
1067 bool aStorageAccessPermissionGranted
, uint32_t aRejectedReason
,
1068 bool aIsSafeTopLevelNav
, bool aIsSameSiteForeign
,
1069 bool aHadCrossSiteRedirects
, bool aHttpBound
,
1070 bool aAllowSecureCookiesToInsecureOrigin
,
1071 const nsTArray
<OriginAttributes
>& aOriginAttrsList
,
1072 nsTArray
<RefPtr
<Cookie
>>& aCookieList
) {
1073 NS_ASSERTION(aHostURI
, "null host!");
1075 if (!CookieCommons::IsSchemeSupported(aHostURI
)) {
1079 if (!IsInitialized()) {
1083 nsCOMPtr
<nsICookieJarSettings
> cookieJarSettings
=
1084 CookieCommons::GetCookieJarSettings(aChannel
);
1086 nsCOMPtr
<nsIConsoleReportCollector
> crc
= do_QueryInterface(aChannel
);
1088 for (const auto& attrs
: aOriginAttrsList
) {
1089 CookieStorage
* storage
= PickStorage(attrs
);
1091 // get the base domain, host, and path from the URI.
1092 // e.g. for "www.bbc.co.uk", the base domain would be "bbc.co.uk".
1093 // file:// URI's (i.e. with an empty host) are allowed, but any other
1094 // scheme must have a non-empty host. A trailing dot in the host
1096 bool requireHostMatch
;
1097 nsAutoCString baseDomain
;
1098 nsAutoCString hostFromURI
;
1099 nsAutoCString pathFromURI
;
1100 nsresult rv
= CookieCommons::GetBaseDomain(mTLDService
, aHostURI
,
1101 baseDomain
, requireHostMatch
);
1102 if (NS_SUCCEEDED(rv
)) {
1103 rv
= nsContentUtils::GetHostOrIPv6WithBrackets(aHostURI
, hostFromURI
);
1105 if (NS_SUCCEEDED(rv
)) {
1106 rv
= aHostURI
->GetFilePath(pathFromURI
);
1108 if (NS_FAILED(rv
)) {
1109 COOKIE_LOGFAILURE(GET_COOKIE
, aHostURI
, VoidCString(),
1110 "invalid host/path from URI");
1114 nsAutoCString
normalizedHostFromURI(hostFromURI
);
1115 rv
= NormalizeHost(normalizedHostFromURI
);
1116 NS_ENSURE_SUCCESS_VOID(rv
);
1118 nsAutoCString baseDomainFromURI
;
1119 rv
= CookieCommons::GetBaseDomainFromHost(
1120 mTLDService
, normalizedHostFromURI
, baseDomainFromURI
);
1121 NS_ENSURE_SUCCESS_VOID(rv
);
1123 // check default prefs
1124 uint32_t rejectedReason
= aRejectedReason
;
1125 uint32_t priorCookieCount
= storage
->CountCookiesFromHost(
1126 baseDomainFromURI
, attrs
.mPrivateBrowsingId
);
1128 CookieStatus cookieStatus
= CheckPrefs(
1129 crc
, cookieJarSettings
, aHostURI
, aIsForeign
,
1130 aIsThirdPartyTrackingResource
, aIsThirdPartySocialTrackingResource
,
1131 aStorageAccessPermissionGranted
, VoidCString(), priorCookieCount
, attrs
,
1134 MOZ_ASSERT_IF(rejectedReason
, cookieStatus
== STATUS_REJECTED
);
1136 // for GetCookie(), we only fire acceptance/rejection notifications
1137 // (but not if there was an error)
1138 switch (cookieStatus
) {
1139 case STATUS_REJECTED
:
1140 // If we don't have any cookies from this host, fail silently.
1141 if (priorCookieCount
) {
1142 CookieCommons::NotifyRejected(aHostURI
, aChannel
, rejectedReason
,
1150 // Note: The following permissions logic is mirrored in
1151 // extensions::MatchPattern::MatchesCookie.
1152 // If it changes, please update that function, or file a bug for someone
1155 // check if aHostURI is using an https secure protocol.
1156 // if it isn't, then we can't send a secure cookie over the connection.
1157 // if SchemeIs fails, assume an insecure connection, to be on the safe side
1158 bool potentiallyTrustworthy
=
1159 nsMixedContentBlocker::IsPotentiallyTrustworthyOrigin(aHostURI
);
1161 int64_t currentTimeInUsec
= PR_Now();
1162 int64_t currentTime
= currentTimeInUsec
/ PR_USEC_PER_SEC
;
1165 nsTArray
<RefPtr
<Cookie
>> cookies
;
1166 storage
->GetCookiesFromHost(baseDomain
, attrs
, cookies
);
1167 if (cookies
.IsEmpty()) {
1172 StaticPrefs::network_cookie_sameSite_laxByDefault() &&
1173 !nsContentUtils::IsURIInPrefList(
1174 aHostURI
, "network.cookie.sameSite.laxByDefault.disabledHosts");
1176 // iterate the cookies!
1177 for (Cookie
* cookie
: cookies
) {
1178 // check the host, since the base domain lookup is conservative.
1179 if (!CookieCommons::DomainMatches(cookie
, hostFromURI
)) {
1183 // if the cookie is secure and the host scheme isn't, we avoid sending
1184 // cookie if possible. But for process synchronization purposes, we may
1185 // want the content process to know about the cookie (without it's value).
1186 // In which case we will wipe the value before sending
1187 if (cookie
->IsSecure() && !potentiallyTrustworthy
&&
1188 !aAllowSecureCookiesToInsecureOrigin
) {
1192 // if the cookie is httpOnly and it's not going directly to the HTTP
1193 // connection, don't send it
1194 if (cookie
->IsHttpOnly() && !aHttpBound
) {
1198 // if the nsIURI path doesn't match the cookie path, don't send it back
1199 if (!CookieCommons::PathMatches(cookie
, pathFromURI
)) {
1203 // check if the cookie has expired
1204 if (cookie
->Expiry() <= currentTime
) {
1208 // Check if we need to block the cookie because of opt-in partitioning.
1209 // We will only allow partitioned cookies with "partitioned" attribution
1210 // if opt-in partitioning is enabled.
1211 if (aIsForeign
&& cookieJarSettings
->GetPartitionForeign() &&
1212 (StaticPrefs::network_cookie_cookieBehavior_optInPartitioning() ||
1213 (attrs
.IsPrivateBrowsing() &&
1215 network_cookie_cookieBehavior_optInPartitioning_pbmode())) &&
1216 !(cookie
->IsPartitioned() && cookie
->RawIsPartitioned()) &&
1217 !aStorageAccessPermissionGranted
) {
1221 if (aHttpBound
&& aIsSameSiteForeign
) {
1222 bool blockCookie
= !ProcessSameSiteCookieForForeignRequest(
1223 aChannel
, cookie
, aIsSafeTopLevelNav
, aHadCrossSiteRedirects
,
1227 if (aHadCrossSiteRedirects
) {
1228 CookieLogging::LogMessageToConsole(
1229 crc
, aHostURI
, nsIScriptError::warningFlag
,
1230 CONSOLE_REJECTION_CATEGORY
, "CookieBlockedCrossSiteRedirect"_ns
,
1231 AutoTArray
<nsString
, 1>{
1232 NS_ConvertUTF8toUTF16(cookie
->Name()),
1239 // all checks passed - add to list and check if lastAccessed stamp needs
1241 aCookieList
.AppendElement(cookie
);
1242 if (cookie
->IsStale()) {
1247 if (aCookieList
.IsEmpty()) {
1251 // update lastAccessed timestamps. we only do this if the timestamp is stale
1252 // by a certain amount, to avoid thrashing the db during pageload.
1254 storage
->StaleCookies(aCookieList
, currentTimeInUsec
);
1258 if (aCookieList
.IsEmpty()) {
1262 // Send a notification about the acceptance of the cookies now that we found
1264 NotifyAccepted(aChannel
);
1266 // return cookies in order of path length; longest to shortest.
1267 // this is required per RFC2109. if cookies match in length,
1268 // then sort by creation time (see bug 236772).
1269 aCookieList
.Sort(CompareCookiesForSending());
1272 /******************************************************************************
1273 * CookieService impl:
1274 * private domain & permission compliance enforcement functions
1275 ******************************************************************************/
1277 nsresult
CookieService::NormalizeHost(nsCString
& aHost
) {
1279 nsresult rv
= NS_DomainToASCIIAllowAnyGlyphfulASCII(aHost
, host
);
1280 if (NS_FAILED(rv
)) {
1288 CookieStatus
CookieService::CheckPrefs(
1289 nsIConsoleReportCollector
* aCRC
, nsICookieJarSettings
* aCookieJarSettings
,
1290 nsIURI
* aHostURI
, bool aIsForeign
, bool aIsThirdPartyTrackingResource
,
1291 bool aIsThirdPartySocialTrackingResource
,
1292 bool aStorageAccessPermissionGranted
, const nsACString
& aCookieHeader
,
1293 const int aNumOfCookies
, const OriginAttributes
& aOriginAttrs
,
1294 uint32_t* aRejectedReason
) {
1297 MOZ_ASSERT(aRejectedReason
);
1299 *aRejectedReason
= 0;
1301 // don't let unsupported scheme sites get/set cookies (could be a security
1303 if (!CookieCommons::IsSchemeSupported(aHostURI
)) {
1304 COOKIE_LOGFAILURE(!aCookieHeader
.IsVoid(), aHostURI
, aCookieHeader
,
1305 "non http/https sites cannot read cookies");
1306 return STATUS_REJECTED_WITH_ERROR
;
1309 nsCOMPtr
<nsIPrincipal
> principal
=
1310 BasePrincipal::CreateContentPrincipal(aHostURI
, aOriginAttrs
);
1313 COOKIE_LOGFAILURE(!aCookieHeader
.IsVoid(), aHostURI
, aCookieHeader
,
1314 "non-content principals cannot get/set cookies");
1315 return STATUS_REJECTED_WITH_ERROR
;
1318 // check the permission list first; if we find an entry, it overrides
1319 // default prefs. see bug 184059.
1320 uint32_t cookiePermission
= nsICookiePermission::ACCESS_DEFAULT
;
1321 rv
= aCookieJarSettings
->CookiePermission(principal
, &cookiePermission
);
1322 if (NS_SUCCEEDED(rv
)) {
1323 switch (cookiePermission
) {
1324 case nsICookiePermission::ACCESS_DENY
:
1325 COOKIE_LOGFAILURE(!aCookieHeader
.IsVoid(), aHostURI
, aCookieHeader
,
1326 "cookies are blocked for this site");
1327 CookieLogging::LogMessageToConsole(
1328 aCRC
, aHostURI
, nsIScriptError::warningFlag
,
1329 CONSOLE_REJECTION_CATEGORY
, "CookieRejectedByPermissionManager"_ns
,
1330 AutoTArray
<nsString
, 1>{
1331 NS_ConvertUTF8toUTF16(aCookieHeader
),
1335 nsIWebProgressListener::STATE_COOKIES_BLOCKED_BY_PERMISSION
;
1336 return STATUS_REJECTED
;
1338 case nsICookiePermission::ACCESS_ALLOW
:
1339 return STATUS_ACCEPTED
;
1345 // No cookies allowed if this request comes from a resource in a 3rd party
1346 // context, when anti-tracking protection is enabled and when we don't have
1347 // access to the first-party cookie jar.
1348 if (aIsForeign
&& aIsThirdPartyTrackingResource
&&
1349 !aStorageAccessPermissionGranted
&&
1350 aCookieJarSettings
->GetRejectThirdPartyContexts()) {
1351 uint32_t rejectReason
=
1352 nsIWebProgressListener::STATE_COOKIES_BLOCKED_TRACKER
;
1353 if (StoragePartitioningEnabled(rejectReason
, aCookieJarSettings
)) {
1354 MOZ_ASSERT(!aOriginAttrs
.mPartitionKey
.IsEmpty(),
1355 "We must have a StoragePrincipal here!");
1356 return STATUS_ACCEPTED
;
1359 COOKIE_LOGFAILURE(!aCookieHeader
.IsVoid(), aHostURI
, aCookieHeader
,
1360 "cookies are disabled in trackers");
1361 if (aIsThirdPartySocialTrackingResource
) {
1363 nsIWebProgressListener::STATE_COOKIES_BLOCKED_SOCIALTRACKER
;
1365 *aRejectedReason
= nsIWebProgressListener::STATE_COOKIES_BLOCKED_TRACKER
;
1367 return STATUS_REJECTED
;
1370 // check default prefs.
1371 // Check aStorageAccessPermissionGranted when checking aCookieBehavior
1372 // so that we take things such as the content blocking allow list into
1374 if (aCookieJarSettings
->GetCookieBehavior() ==
1375 nsICookieService::BEHAVIOR_REJECT
&&
1376 !aStorageAccessPermissionGranted
) {
1377 COOKIE_LOGFAILURE(!aCookieHeader
.IsVoid(), aHostURI
, aCookieHeader
,
1378 "cookies are disabled");
1379 *aRejectedReason
= nsIWebProgressListener::STATE_COOKIES_BLOCKED_ALL
;
1380 return STATUS_REJECTED
;
1383 // check if cookie is foreign
1385 if (aCookieJarSettings
->GetCookieBehavior() ==
1386 nsICookieService::BEHAVIOR_REJECT_FOREIGN
&&
1387 !aStorageAccessPermissionGranted
) {
1388 COOKIE_LOGFAILURE(!aCookieHeader
.IsVoid(), aHostURI
, aCookieHeader
,
1389 "context is third party");
1390 CookieLogging::LogMessageToConsole(
1391 aCRC
, aHostURI
, nsIScriptError::warningFlag
,
1392 CONSOLE_REJECTION_CATEGORY
, "CookieRejectedThirdParty"_ns
,
1393 AutoTArray
<nsString
, 1>{
1394 NS_ConvertUTF8toUTF16(aCookieHeader
),
1396 *aRejectedReason
= nsIWebProgressListener::STATE_COOKIES_BLOCKED_FOREIGN
;
1397 return STATUS_REJECTED
;
1400 if (aCookieJarSettings
->GetLimitForeignContexts() &&
1401 !aStorageAccessPermissionGranted
&& aNumOfCookies
== 0) {
1402 COOKIE_LOGFAILURE(!aCookieHeader
.IsVoid(), aHostURI
, aCookieHeader
,
1403 "context is third party");
1404 CookieLogging::LogMessageToConsole(
1405 aCRC
, aHostURI
, nsIScriptError::warningFlag
,
1406 CONSOLE_REJECTION_CATEGORY
, "CookieRejectedThirdParty"_ns
,
1407 AutoTArray
<nsString
, 1>{
1408 NS_ConvertUTF8toUTF16(aCookieHeader
),
1410 *aRejectedReason
= nsIWebProgressListener::STATE_COOKIES_BLOCKED_FOREIGN
;
1411 return STATUS_REJECTED
;
1415 // if nothing has complained, accept cookie
1416 return STATUS_ACCEPTED
;
1419 /******************************************************************************
1420 * CookieService impl:
1421 * private cookielist management functions
1422 ******************************************************************************/
1424 // find whether a given cookie has been previously set. this is provided by the
1425 // nsICookieManager interface.
1427 CookieService::CookieExists(const nsACString
& aHost
, const nsACString
& aPath
,
1428 const nsACString
& aName
,
1429 JS::Handle
<JS::Value
> aOriginAttributes
,
1430 JSContext
* aCx
, bool* aFoundCookie
) {
1431 NS_ENSURE_ARG_POINTER(aCx
);
1432 NS_ENSURE_ARG_POINTER(aFoundCookie
);
1434 OriginAttributes attrs
;
1435 if (!aOriginAttributes
.isObject() || !attrs
.Init(aCx
, aOriginAttributes
)) {
1436 return NS_ERROR_INVALID_ARG
;
1438 return CookieExistsNative(aHost
, aPath
, aName
, &attrs
, aFoundCookie
);
1441 NS_IMETHODIMP_(nsresult
)
1442 CookieService::CookieExistsNative(const nsACString
& aHost
,
1443 const nsACString
& aPath
,
1444 const nsACString
& aName
,
1445 OriginAttributes
* aOriginAttributes
,
1446 bool* aFoundCookie
) {
1447 nsCOMPtr
<nsICookie
> cookie
;
1448 nsresult rv
= GetCookieNative(aHost
, aPath
, aName
, aOriginAttributes
,
1449 getter_AddRefs(cookie
));
1450 NS_ENSURE_SUCCESS(rv
, rv
);
1452 *aFoundCookie
= cookie
!= nullptr;
1457 NS_IMETHODIMP_(nsresult
)
1458 CookieService::GetCookieNative(const nsACString
& aHost
, const nsACString
& aPath
,
1459 const nsACString
& aName
,
1460 OriginAttributes
* aOriginAttributes
,
1461 nsICookie
** aCookie
) {
1462 NS_ENSURE_ARG_POINTER(aOriginAttributes
);
1463 NS_ENSURE_ARG_POINTER(aCookie
);
1465 if (!IsInitialized()) {
1466 return NS_ERROR_NOT_AVAILABLE
;
1469 nsAutoCString baseDomain
;
1471 CookieCommons::GetBaseDomainFromHost(mTLDService
, aHost
, baseDomain
);
1472 NS_ENSURE_SUCCESS(rv
, rv
);
1474 CookieListIter iter
{};
1475 CookieStorage
* storage
= PickStorage(*aOriginAttributes
);
1476 bool foundCookie
= storage
->FindCookie(baseDomain
, *aOriginAttributes
, aHost
,
1477 aName
, aPath
, iter
);
1480 RefPtr
<Cookie
> cookie
= iter
.Cookie();
1481 NS_ENSURE_TRUE(cookie
, NS_ERROR_NULL_POINTER
);
1483 cookie
.forget(aCookie
);
1489 // count the number of cookies stored by a particular host. this is provided by
1490 // the nsICookieManager interface.
1492 CookieService::CountCookiesFromHost(const nsACString
& aHost
,
1493 uint32_t* aCountFromHost
) {
1494 // first, normalize the hostname, and fail if it contains illegal characters.
1495 nsAutoCString
host(aHost
);
1496 nsresult rv
= NormalizeHost(host
);
1497 NS_ENSURE_SUCCESS(rv
, rv
);
1499 nsAutoCString baseDomain
;
1500 rv
= CookieCommons::GetBaseDomainFromHost(mTLDService
, host
, baseDomain
);
1501 NS_ENSURE_SUCCESS(rv
, rv
);
1503 if (!IsInitialized()) {
1504 return NS_ERROR_NOT_AVAILABLE
;
1507 mPersistentStorage
->EnsureInitialized();
1509 *aCountFromHost
= mPersistentStorage
->CountCookiesFromHost(baseDomain
, 0);
1514 // get an enumerator of cookies stored by a particular host. this is provided by
1515 // the nsICookieManager interface.
1517 CookieService::GetCookiesFromHost(const nsACString
& aHost
,
1518 JS::Handle
<JS::Value
> aOriginAttributes
,
1520 nsTArray
<RefPtr
<nsICookie
>>& aResult
) {
1521 // first, normalize the hostname, and fail if it contains illegal characters.
1522 nsAutoCString
host(aHost
);
1523 nsresult rv
= NormalizeHost(host
);
1524 NS_ENSURE_SUCCESS(rv
, rv
);
1526 nsAutoCString baseDomain
;
1527 rv
= CookieCommons::GetBaseDomainFromHost(mTLDService
, host
, baseDomain
);
1528 NS_ENSURE_SUCCESS(rv
, rv
);
1530 OriginAttributes attrs
;
1531 if (!aOriginAttributes
.isObject() || !attrs
.Init(aCx
, aOriginAttributes
)) {
1532 return NS_ERROR_INVALID_ARG
;
1535 if (!IsInitialized()) {
1536 return NS_ERROR_NOT_AVAILABLE
;
1539 CookieStorage
* storage
= PickStorage(attrs
);
1541 nsTArray
<RefPtr
<Cookie
>> cookies
;
1542 storage
->GetCookiesFromHost(baseDomain
, attrs
, cookies
);
1544 if (cookies
.IsEmpty()) {
1548 aResult
.SetCapacity(cookies
.Length());
1549 for (Cookie
* cookie
: cookies
) {
1550 aResult
.AppendElement(cookie
);
1557 CookieService::GetCookiesWithOriginAttributes(
1558 const nsAString
& aPattern
, const nsACString
& aHost
,
1559 nsTArray
<RefPtr
<nsICookie
>>& aResult
) {
1560 OriginAttributesPattern pattern
;
1561 if (!pattern
.Init(aPattern
)) {
1562 return NS_ERROR_INVALID_ARG
;
1565 nsAutoCString
host(aHost
);
1566 nsresult rv
= NormalizeHost(host
);
1567 NS_ENSURE_SUCCESS(rv
, rv
);
1569 nsAutoCString baseDomain
;
1570 rv
= CookieCommons::GetBaseDomainFromHost(mTLDService
, host
, baseDomain
);
1571 NS_ENSURE_SUCCESS(rv
, rv
);
1573 return GetCookiesWithOriginAttributes(pattern
, baseDomain
, aResult
);
1576 nsresult
CookieService::GetCookiesWithOriginAttributes(
1577 const OriginAttributesPattern
& aPattern
, const nsCString
& aBaseDomain
,
1578 nsTArray
<RefPtr
<nsICookie
>>& aResult
) {
1579 CookieStorage
* storage
= PickStorage(aPattern
);
1580 storage
->GetCookiesWithOriginAttributes(aPattern
, aBaseDomain
, aResult
);
1586 CookieService::RemoveCookiesWithOriginAttributes(const nsAString
& aPattern
,
1587 const nsACString
& aHost
) {
1588 MOZ_ASSERT(XRE_IsParentProcess());
1590 OriginAttributesPattern pattern
;
1591 if (!pattern
.Init(aPattern
)) {
1592 return NS_ERROR_INVALID_ARG
;
1595 nsAutoCString
host(aHost
);
1596 nsresult rv
= NormalizeHost(host
);
1597 NS_ENSURE_SUCCESS(rv
, rv
);
1599 nsAutoCString baseDomain
;
1600 rv
= CookieCommons::GetBaseDomainFromHost(mTLDService
, host
, baseDomain
);
1601 NS_ENSURE_SUCCESS(rv
, rv
);
1603 return RemoveCookiesWithOriginAttributes(pattern
, baseDomain
);
1606 nsresult
CookieService::RemoveCookiesWithOriginAttributes(
1607 const OriginAttributesPattern
& aPattern
, const nsCString
& aBaseDomain
) {
1608 if (!IsInitialized()) {
1609 return NS_ERROR_NOT_AVAILABLE
;
1612 CookieStorage
* storage
= PickStorage(aPattern
);
1613 storage
->RemoveCookiesWithOriginAttributes(aPattern
, aBaseDomain
);
1619 CookieService::RemoveCookiesFromExactHost(const nsACString
& aHost
,
1620 const nsAString
& aPattern
) {
1621 MOZ_ASSERT(XRE_IsParentProcess());
1623 OriginAttributesPattern pattern
;
1624 if (!pattern
.Init(aPattern
)) {
1625 return NS_ERROR_INVALID_ARG
;
1628 return RemoveCookiesFromExactHost(aHost
, pattern
);
1631 nsresult
CookieService::RemoveCookiesFromExactHost(
1632 const nsACString
& aHost
, const OriginAttributesPattern
& aPattern
) {
1633 nsAutoCString
host(aHost
);
1634 nsresult rv
= NormalizeHost(host
);
1635 NS_ENSURE_SUCCESS(rv
, rv
);
1637 nsAutoCString baseDomain
;
1638 rv
= CookieCommons::GetBaseDomainFromHost(mTLDService
, host
, baseDomain
);
1639 NS_ENSURE_SUCCESS(rv
, rv
);
1641 if (!IsInitialized()) {
1642 return NS_ERROR_NOT_AVAILABLE
;
1645 CookieStorage
* storage
= PickStorage(aPattern
);
1646 storage
->RemoveCookiesFromExactHost(aHost
, baseDomain
, aPattern
);
1653 class RemoveAllSinceRunnable
: public Runnable
{
1655 using CookieArray
= nsTArray
<RefPtr
<nsICookie
>>;
1656 RemoveAllSinceRunnable(Promise
* aPromise
, CookieService
* aSelf
,
1657 CookieArray
&& aCookieArray
, int64_t aSinceWhen
)
1658 : Runnable("RemoveAllSinceRunnable"),
1661 mList(std::move(aCookieArray
)),
1663 mSinceWhen(aSinceWhen
) {}
1665 NS_IMETHODIMP
Run() override
{
1668 if (mIndex
< mList
.Length()) {
1669 return NS_DispatchToCurrentThread(this);
1671 mPromise
->MaybeResolveWithUndefined();
1678 for (CookieArray::size_type iter
= 0;
1679 iter
< kYieldPeriod
&& mIndex
< mList
.Length(); ++mIndex
, ++iter
) {
1680 auto* cookie
= static_cast<Cookie
*>(mList
[mIndex
].get());
1681 if (cookie
->CreationTime() > mSinceWhen
&&
1682 NS_FAILED(mSelf
->Remove(cookie
->Host(), cookie
->OriginAttributesRef(),
1683 cookie
->Name(), cookie
->Path()))) {
1690 RefPtr
<Promise
> mPromise
;
1691 RefPtr
<CookieService
> mSelf
;
1693 CookieArray::size_type mIndex
;
1695 static const CookieArray::size_type kYieldPeriod
= 10;
1701 CookieService::RemoveAllSince(int64_t aSinceWhen
, JSContext
* aCx
,
1702 Promise
** aRetVal
) {
1703 nsIGlobalObject
* globalObject
= xpc::CurrentNativeGlobal(aCx
);
1704 if (NS_WARN_IF(!globalObject
)) {
1705 return NS_ERROR_UNEXPECTED
;
1709 RefPtr
<Promise
> promise
= Promise::Create(globalObject
, result
);
1710 if (NS_WARN_IF(result
.Failed())) {
1711 return result
.StealNSResult();
1714 mPersistentStorage
->EnsureInitialized();
1716 nsTArray
<RefPtr
<nsICookie
>> cookieList
;
1718 // We delete only non-private cookies.
1719 mPersistentStorage
->GetAll(cookieList
);
1721 RefPtr
<RemoveAllSinceRunnable
> runMe
= new RemoveAllSinceRunnable(
1722 promise
, this, std::move(cookieList
), aSinceWhen
);
1724 promise
.forget(aRetVal
);
1726 return runMe
->Run();
1731 class CompareCookiesCreationTime
{
1733 static bool Equals(const nsICookie
* aCookie1
, const nsICookie
* aCookie2
) {
1734 return static_cast<const Cookie
*>(aCookie1
)->CreationTime() ==
1735 static_cast<const Cookie
*>(aCookie2
)->CreationTime();
1738 static bool LessThan(const nsICookie
* aCookie1
, const nsICookie
* aCookie2
) {
1739 return static_cast<const Cookie
*>(aCookie1
)->CreationTime() <
1740 static_cast<const Cookie
*>(aCookie2
)->CreationTime();
1747 CookieService::GetCookiesSince(int64_t aSinceWhen
,
1748 nsTArray
<RefPtr
<nsICookie
>>& aResult
) {
1749 if (!IsInitialized()) {
1753 mPersistentStorage
->EnsureInitialized();
1755 // We expose only non-private cookies.
1756 nsTArray
<RefPtr
<nsICookie
>> cookieList
;
1757 mPersistentStorage
->GetAll(cookieList
);
1759 for (RefPtr
<nsICookie
>& cookie
: cookieList
) {
1760 if (static_cast<Cookie
*>(cookie
.get())->CreationTime() >= aSinceWhen
) {
1761 aResult
.AppendElement(cookie
);
1765 aResult
.Sort(CompareCookiesCreationTime());
1769 size_t CookieService::SizeOfIncludingThis(MallocSizeOf aMallocSizeOf
) const {
1770 size_t n
= aMallocSizeOf(this);
1772 if (mPersistentStorage
) {
1773 n
+= mPersistentStorage
->SizeOfIncludingThis(aMallocSizeOf
);
1775 if (mPrivateStorage
) {
1776 n
+= mPrivateStorage
->SizeOfIncludingThis(aMallocSizeOf
);
1782 MOZ_DEFINE_MALLOC_SIZE_OF(CookieServiceMallocSizeOf
)
1785 CookieService::CollectReports(nsIHandleReportCallback
* aHandleReport
,
1786 nsISupports
* aData
, bool /*aAnonymize*/) {
1787 MOZ_COLLECT_REPORT("explicit/cookie-service", KIND_HEAP
, UNITS_BYTES
,
1788 SizeOfIncludingThis(CookieServiceMallocSizeOf
),
1789 "Memory used by the cookie service.");
1794 bool CookieService::IsInitialized() const {
1795 if (!mPersistentStorage
) {
1796 NS_WARNING("No CookieStorage! Profile already close?");
1800 MOZ_ASSERT(mPrivateStorage
);
1804 CookieStorage
* CookieService::PickStorage(const OriginAttributes
& aAttrs
) {
1805 MOZ_ASSERT(IsInitialized());
1807 if (aAttrs
.IsPrivateBrowsing()) {
1808 return mPrivateStorage
;
1811 mPersistentStorage
->EnsureInitialized();
1812 return mPersistentStorage
;
1815 CookieStorage
* CookieService::PickStorage(
1816 const OriginAttributesPattern
& aAttrs
) {
1817 MOZ_ASSERT(IsInitialized());
1819 if (aAttrs
.mPrivateBrowsingId
.WasPassed() &&
1820 aAttrs
.mPrivateBrowsingId
.Value() > 0) {
1821 return mPrivateStorage
;
1824 mPersistentStorage
->EnsureInitialized();
1825 return mPersistentStorage
;
1828 bool CookieService::SetCookiesFromIPC(const nsACString
& aBaseDomain
,
1829 const OriginAttributes
& aAttrs
,
1830 nsIURI
* aHostURI
, bool aFromHttp
,
1832 const nsTArray
<CookieStruct
>& aCookies
,
1833 BrowsingContext
* aBrowsingContext
) {
1834 if (!IsInitialized()) {
1835 // If we are probably shutting down, we can ignore this cookie.
1839 CookieStorage
* storage
= PickStorage(aAttrs
);
1840 int64_t currentTimeInUsec
= PR_Now();
1842 for (const CookieStruct
& cookieData
: aCookies
) {
1843 if (!CookieCommons::CheckPathSize(cookieData
)) {
1847 // reject cookie if it's over the size limit, per RFC2109
1848 if (!CookieCommons::CheckNameAndValueSize(cookieData
)) {
1852 CookieCommons::RecordUnicodeTelemetry(cookieData
);
1854 if (!CookieCommons::CheckName(cookieData
)) {
1858 if (!CookieCommons::CheckValue(cookieData
)) {
1862 // create a new Cookie and copy attributes
1863 RefPtr
<Cookie
> cookie
= Cookie::Create(cookieData
, aAttrs
);
1868 cookie
->SetLastAccessed(currentTimeInUsec
);
1869 cookie
->SetCreationTime(
1870 Cookie::GenerateUniqueCreationTime(currentTimeInUsec
));
1872 storage
->AddCookie(nullptr, aBaseDomain
, aAttrs
, cookie
, currentTimeInUsec
,
1873 aHostURI
, ""_ns
, aFromHttp
, aIsThirdParty
,
1881 } // namespace mozilla