1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set ts=8 sts=2 et sw=2 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 file,
5 * You can obtain one at http://mozilla.org/MPL/2.0/. */
7 #include "AntiTrackingUtils.h"
9 #include "AntiTrackingLog.h"
10 #include "HttpBaseChannel.h"
11 #include "mozilla/BasePrincipal.h"
12 #include "mozilla/Components.h"
13 #include "mozilla/dom/BrowsingContext.h"
14 #include "mozilla/dom/CanonicalBrowsingContext.h"
15 #include "mozilla/net/CookieJarSettings.h"
16 #include "mozilla/LoadInfo.h"
17 #include "mozilla/dom/Document.h"
18 #include "mozilla/dom/WindowGlobalParent.h"
19 #include "mozilla/dom/WindowContext.h"
20 #include "mozilla/net/NeckoChannelParams.h"
21 #include "mozilla/PermissionManager.h"
22 #include "mozIThirdPartyUtil.h"
23 #include "nsEffectiveTLDService.h"
24 #include "nsGlobalWindowInner.h"
25 #include "nsIChannel.h"
26 #include "nsICookieService.h"
27 #include "nsIHttpChannel.h"
28 #include "nsIPermission.h"
30 #include "nsNetUtil.h"
31 #include "nsPIDOMWindow.h"
32 #include "nsQueryObject.h"
33 #include "nsRFPService.h"
34 #include "nsSandboxFlags.h"
35 #include "nsScriptSecurityManager.h"
36 #include "PartitioningExceptionList.h"
38 #define ANTITRACKING_PERM_KEY "3rdPartyStorage"
39 #define ANTITRACKING_FRAME_PERM_KEY "3rdPartyFrameStorage"
41 using namespace mozilla
;
42 using namespace mozilla::dom
;
44 /* static */ already_AddRefed
<nsPIDOMWindowInner
>
45 AntiTrackingUtils::GetInnerWindow(BrowsingContext
* aBrowsingContext
) {
46 MOZ_ASSERT(aBrowsingContext
);
48 nsCOMPtr
<nsPIDOMWindowOuter
> outer
= aBrowsingContext
->GetDOMWindow();
53 nsCOMPtr
<nsPIDOMWindowInner
> inner
= outer
->GetCurrentInnerWindow();
54 return inner
.forget();
57 /* static */ already_AddRefed
<nsPIDOMWindowOuter
>
58 AntiTrackingUtils::GetTopWindow(nsPIDOMWindowInner
* aWindow
) {
59 Document
* document
= aWindow
->GetExtantDoc();
64 nsIChannel
* channel
= document
->GetChannel();
69 nsCOMPtr
<nsPIDOMWindowOuter
> pwin
=
70 aWindow
->GetBrowsingContext()->Top()->GetDOMWindow();
80 already_AddRefed
<nsIURI
> AntiTrackingUtils::MaybeGetDocumentURIBeingLoaded(
81 nsIChannel
* aChannel
) {
82 nsCOMPtr
<nsIURI
> uriBeingLoaded
;
83 nsLoadFlags loadFlags
= 0;
84 nsresult rv
= aChannel
->GetLoadFlags(&loadFlags
);
85 if (NS_WARN_IF(NS_FAILED(rv
))) {
88 if (loadFlags
& nsIChannel::LOAD_DOCUMENT_URI
) {
89 // If the channel being loaded is a document channel, this call may be
90 // coming from an OnStopRequest notification, which might mean that our
91 // document may still be in the loading process, so we may need to pass in
92 // the uriBeingLoaded argument explicitly.
93 rv
= NS_GetFinalChannelURI(aChannel
, getter_AddRefs(uriBeingLoaded
));
94 if (NS_WARN_IF(NS_FAILED(rv
))) {
98 return uriBeingLoaded
.forget();
102 void AntiTrackingUtils::CreateStoragePermissionKey(
103 const nsACString
& aTrackingOrigin
, nsACString
& aPermissionKey
) {
104 MOZ_ASSERT(aPermissionKey
.IsEmpty());
106 static const nsLiteralCString prefix
=
107 nsLiteralCString(ANTITRACKING_PERM_KEY
"^");
109 aPermissionKey
.SetCapacity(prefix
.Length() + aTrackingOrigin
.Length());
110 aPermissionKey
.Append(prefix
);
111 aPermissionKey
.Append(aTrackingOrigin
);
115 bool AntiTrackingUtils::CreateStoragePermissionKey(nsIPrincipal
* aPrincipal
,
121 nsAutoCString origin
;
122 nsresult rv
= aPrincipal
->GetOriginNoSuffix(origin
);
123 if (NS_WARN_IF(NS_FAILED(rv
))) {
127 CreateStoragePermissionKey(origin
, aKey
);
132 void AntiTrackingUtils::CreateStorageFramePermissionKey(
133 const nsACString
& aTrackingSite
, nsACString
& aPermissionKey
) {
134 MOZ_ASSERT(aPermissionKey
.IsEmpty());
136 static const nsLiteralCString prefix
=
137 nsLiteralCString(ANTITRACKING_FRAME_PERM_KEY
"^");
139 aPermissionKey
.SetCapacity(prefix
.Length() + aTrackingSite
.Length());
140 aPermissionKey
.Append(prefix
);
141 aPermissionKey
.Append(aTrackingSite
);
145 bool AntiTrackingUtils::CreateStorageFramePermissionKey(
146 nsIPrincipal
* aPrincipal
, nsACString
& aKey
) {
147 MOZ_ASSERT(aPrincipal
);
150 nsresult rv
= aPrincipal
->GetSiteOriginNoSuffix(site
);
151 if (NS_WARN_IF(NS_FAILED(rv
))) {
155 CreateStorageFramePermissionKey(site
, aKey
);
160 bool AntiTrackingUtils::CreateStorageRequestPermissionKey(
161 nsIURI
* aURI
, nsACString
& aPermissionKey
) {
162 MOZ_ASSERT(aPermissionKey
.IsEmpty());
163 RefPtr
<nsEffectiveTLDService
> eTLDService
=
164 nsEffectiveTLDService::GetInstance();
169 nsresult rv
= eTLDService
->GetSite(aURI
, site
);
173 static const nsLiteralCString prefix
=
174 nsLiteralCString("AllowStorageAccessRequest^");
175 aPermissionKey
.SetCapacity(prefix
.Length() + site
.Length());
176 aPermissionKey
.Append(prefix
);
177 aPermissionKey
.Append(site
);
182 bool AntiTrackingUtils::IsStorageAccessPermission(nsIPermission
* aPermission
,
183 nsIPrincipal
* aPrincipal
) {
184 MOZ_ASSERT(aPermission
);
185 MOZ_ASSERT(aPrincipal
);
187 // The permission key may belong either to a tracking origin on the same
188 // origin as the granted origin, or on another origin as the granted origin
189 // (for example when a tracker in a third-party context uses window.open to
190 // open another origin where that second origin would be the granted origin.)
191 // But even in the second case, the type of the permission would still be
192 // formed by concatenating the granted origin to the end of the type name
193 // (see CreatePermissionKey). Therefore, we pass in the same argument to
194 // both tracking origin and granted origin here in order to compute the
195 // shorter permission key and will then do a prefix match on the type of the
196 // input permission to see if it is a storage access permission or not.
197 nsAutoCString permissionKey
;
198 bool result
= CreateStoragePermissionKey(aPrincipal
, permissionKey
);
199 if (NS_WARN_IF(!result
)) {
204 nsresult rv
= aPermission
->GetType(type
);
205 if (NS_WARN_IF(NS_FAILED(rv
))) {
209 return StringBeginsWith(type
, permissionKey
);
213 Maybe
<size_t> AntiTrackingUtils::CountSitesAllowStorageAccess(
214 nsIPrincipal
* aPrincipal
) {
215 PermissionManager
* permManager
= PermissionManager::GetInstance();
216 if (NS_WARN_IF(!permManager
)) {
220 nsAutoCString prefix
;
221 AntiTrackingUtils::CreateStoragePermissionKey(aPrincipal
, prefix
);
222 nsAutoCString framePrefix
;
223 AntiTrackingUtils::CreateStorageFramePermissionKey(aPrincipal
, framePrefix
);
225 using Permissions
= nsTArray
<RefPtr
<nsIPermission
>>;
227 nsresult rv
= permManager
->GetAllWithTypePrefix(prefix
, perms
);
228 if (NS_WARN_IF(NS_FAILED(rv
))) {
231 Permissions framePermissions
;
232 rv
= permManager
->GetAllWithTypePrefix(framePrefix
, framePermissions
);
233 if (NS_WARN_IF(NS_FAILED(rv
))) {
236 if (!perms
.AppendElements(framePermissions
, fallible
)) {
240 // Create a set of unique origins
241 using Sites
= nsTArray
<nsCString
>;
244 // Iterate over all permissions that have a prefix equal to the expected
245 // permission we are looking for. This includes two things we need to remove:
246 // duplicates and origin strings that have a prefix of aPrincipal's origin
247 // string, e.g. https://example.company when aPrincipal is
248 // https://example.com.
249 for (const auto& perm
: perms
) {
251 rv
= perm
->GetType(type
);
252 if (NS_WARN_IF(NS_FAILED(rv
))) {
256 // Let's make sure that we're not looking at a permission for
257 // https://exampletracker.company when we mean to look for the
258 // permission for https://exampletracker.com!
259 if (type
!= prefix
&& type
!= framePrefix
) {
263 nsCOMPtr
<nsIPrincipal
> principal
;
264 rv
= perm
->GetPrincipal(getter_AddRefs(principal
));
265 if (NS_WARN_IF(NS_FAILED(rv
))) {
270 rv
= principal
->GetSiteOrigin(site
);
271 if (NS_WARN_IF(NS_FAILED(rv
))) {
277 // Append if it would not be a duplicate
278 if (sites
.IndexOf(site
) == Sites::NoIndex
) {
279 sites
.AppendElement(site
);
283 return Some(sites
.Length());
287 bool AntiTrackingUtils::CheckStoragePermission(nsIPrincipal
* aPrincipal
,
288 const nsAutoCString
& aType
,
289 bool aIsInPrivateBrowsing
,
290 uint32_t* aRejectedReason
,
291 uint32_t aBlockedReason
) {
292 PermissionManager
* permManager
= PermissionManager::GetInstance();
293 if (NS_WARN_IF(!permManager
)) {
294 LOG(("Failed to obtain the permission manager"));
299 if (aIsInPrivateBrowsing
) {
300 LOG_PRIN(("Querying the permissions for private modei looking for a "
301 "permission of type %s for %s",
304 if (!permManager
->PermissionAvailable(aPrincipal
, aType
)) {
306 ("Permission isn't available for this principal in the current "
310 nsTArray
<RefPtr
<nsIPermission
>> permissions
;
311 nsresult rv
= permManager
->GetAllForPrincipal(aPrincipal
, permissions
);
312 if (NS_WARN_IF(NS_FAILED(rv
))) {
313 LOG(("Failed to get the list of permissions"));
318 for (const auto& permission
: permissions
) {
320 LOG(("Couldn't get the permission for unknown reasons"));
324 nsAutoCString permissionType
;
325 if (NS_SUCCEEDED(permission
->GetType(permissionType
)) &&
326 permissionType
!= aType
) {
327 LOG(("Non-matching permission type: %s", aType
.get()));
331 uint32_t capability
= 0;
332 if (NS_SUCCEEDED(permission
->GetCapability(&capability
)) &&
333 capability
!= nsIPermissionManager::ALLOW_ACTION
) {
334 LOG(("Non-matching permission capability: %d", capability
));
338 uint32_t expirationType
= 0;
339 if (NS_SUCCEEDED(permission
->GetExpireType(&expirationType
)) &&
340 expirationType
!= nsIPermissionManager ::EXPIRE_SESSION
) {
341 LOG(("Non-matching permission expiration type: %d", expirationType
));
345 int64_t expirationTime
= 0;
346 if (NS_SUCCEEDED(permission
->GetExpireTime(&expirationTime
)) &&
347 expirationTime
!= 0) {
348 LOG(("Non-matching permission expiration time: %" PRId64
,
353 LOG(("Found a matching permission"));
359 if (aRejectedReason
) {
360 *aRejectedReason
= aBlockedReason
;
365 nsresult rv
= permManager
->TestPermissionWithoutDefaultsFromPrincipal(
366 aPrincipal
, aType
, &result
);
367 if (NS_WARN_IF(NS_FAILED(rv
))) {
368 LOG(("Failed to test the permission"));
373 ("Testing permission type %s for %s resulted in %d (%s)", aType
.get(),
375 result
== nsIPermissionManager::ALLOW_ACTION
? "success" : "failure"),
378 if (result
!= nsIPermissionManager::ALLOW_ACTION
) {
379 if (aRejectedReason
) {
380 *aRejectedReason
= aBlockedReason
;
390 nsresult
AntiTrackingUtils::TestStoragePermissionInParent(
391 nsIPrincipal
* aTopPrincipal
, nsIPrincipal
* aPrincipal
, uint32_t* aResult
) {
392 NS_ENSURE_ARG(aResult
);
393 *aResult
= nsIPermissionManager::UNKNOWN_ACTION
;
394 NS_ENSURE_ARG(aTopPrincipal
);
395 NS_ENSURE_ARG(aPrincipal
);
397 nsCOMPtr
<nsIPermissionManager
> permMgr
=
398 components::PermissionManager::Service();
399 NS_ENSURE_TRUE(permMgr
, NS_ERROR_FAILURE
);
401 // Build the permission keys
402 nsAutoCString requestPermissionKey
;
403 bool success
= AntiTrackingUtils::CreateStoragePermissionKey(
404 aPrincipal
, requestPermissionKey
);
405 NS_ENSURE_TRUE(success
, NS_ERROR_FAILURE
);
407 nsAutoCString requestFramePermissionKey
;
408 success
= AntiTrackingUtils::CreateStorageFramePermissionKey(
409 aPrincipal
, requestFramePermissionKey
);
410 NS_ENSURE_TRUE(success
, NS_ERROR_FAILURE
);
412 // Test the permission
413 uint32_t access
= nsIPermissionManager::UNKNOWN_ACTION
;
414 nsresult rv
= permMgr
->TestPermissionFromPrincipal(
415 aTopPrincipal
, requestPermissionKey
, &access
);
416 NS_ENSURE_SUCCESS(rv
, rv
);
418 if (access
!= nsIPermissionManager::UNKNOWN_ACTION
) {
423 uint32_t frameAccess
= nsIPermissionManager::UNKNOWN_ACTION
;
424 rv
= permMgr
->TestPermissionFromPrincipal(
425 aTopPrincipal
, requestFramePermissionKey
, &frameAccess
);
426 NS_ENSURE_SUCCESS(rv
, rv
);
428 *aResult
= frameAccess
;
433 nsILoadInfo::StoragePermissionState
434 AntiTrackingUtils::GetStoragePermissionStateInParent(nsIChannel
* aChannel
) {
435 MOZ_ASSERT(aChannel
);
436 MOZ_DIAGNOSTIC_ASSERT(XRE_IsParentProcess());
438 nsCOMPtr
<nsILoadInfo
> loadInfo
= aChannel
->LoadInfo();
439 nsCOMPtr
<nsICookieJarSettings
> cookieJarSettings
;
441 auto policyType
= loadInfo
->GetExternalContentPolicyType();
443 // The channel is for the document load of the top-level window. The top-level
444 // window should always has 'hasStoragePermission' flag as false. So, we can
445 // return here directly.
446 if (policyType
== ExtContentPolicy::TYPE_DOCUMENT
) {
447 return nsILoadInfo::NoStoragePermission
;
451 loadInfo
->GetCookieJarSettings(getter_AddRefs(cookieJarSettings
));
452 if (NS_WARN_IF(NS_FAILED(rv
))) {
453 return nsILoadInfo::NoStoragePermission
;
456 int32_t cookieBehavior
= cookieJarSettings
->GetCookieBehavior();
458 // We only need to check the storage permission if the cookie behavior is
459 // BEHAVIOR_REJECT_TRACKER, BEHAVIOR_REJECT_TRACKER_AND_PARTITION_FOREIGN or
460 // BEHAVIOR_REJECT_FOREIGN with exceptions. Because ContentBlocking wouldn't
461 // update or check the storage permission if the cookie behavior is not
462 // belongs to these three.
463 if (!net::CookieJarSettings::IsRejectThirdPartyContexts(cookieBehavior
)) {
464 return nsILoadInfo::NoStoragePermission
;
467 RefPtr
<BrowsingContext
> bc
;
468 rv
= loadInfo
->GetTargetBrowsingContext(getter_AddRefs(bc
));
469 if (NS_WARN_IF(NS_FAILED(rv
)) || !bc
) {
470 return nsILoadInfo::NoStoragePermission
;
473 uint64_t targetWindowId
= GetTopLevelAntiTrackingWindowId(bc
);
474 nsCOMPtr
<nsIPrincipal
> targetPrincipal
;
476 if (targetWindowId
) {
477 RefPtr
<WindowGlobalParent
> wgp
=
478 WindowGlobalParent::GetByInnerWindowId(targetWindowId
);
480 if (NS_WARN_IF(!wgp
)) {
481 return nsILoadInfo::NoStoragePermission
;
484 targetPrincipal
= wgp
->DocumentPrincipal();
486 // We try to use the loading principal if there is no AntiTrackingWindowId.
487 targetPrincipal
= loadInfo
->GetLoadingPrincipal();
490 if (!targetPrincipal
) {
491 nsCOMPtr
<nsIHttpChannel
> httpChannel
= do_QueryInterface(aChannel
);
494 // We don't have a loading principal, let's see if this is a document
495 // channel which belongs to a top-level window.
496 bool isDocument
= false;
497 rv
= httpChannel
->GetIsMainDocumentChannel(&isDocument
);
498 if (NS_SUCCEEDED(rv
) && isDocument
) {
499 nsIScriptSecurityManager
* ssm
=
500 nsScriptSecurityManager::GetScriptSecurityManager();
501 Unused
<< ssm
->GetChannelResultPrincipal(
502 aChannel
, getter_AddRefs(targetPrincipal
));
507 // Let's use the triggering principal if we still have nothing on the hand.
508 if (!targetPrincipal
) {
509 targetPrincipal
= loadInfo
->TriggeringPrincipal();
512 // Cannot get the target principal, bail out.
513 if (NS_WARN_IF(!targetPrincipal
)) {
514 return nsILoadInfo::NoStoragePermission
;
517 if (targetPrincipal
->IsSystemPrincipal()) {
518 return nsILoadInfo::HasStoragePermission
;
521 nsCOMPtr
<nsIURI
> trackingURI
;
522 rv
= aChannel
->GetURI(getter_AddRefs(trackingURI
));
523 if (NS_WARN_IF(NS_FAILED(rv
))) {
524 return nsILoadInfo::NoStoragePermission
;
527 nsCOMPtr
<nsIPrincipal
> trackingPrincipal
=
528 BasePrincipal::CreateContentPrincipal(trackingURI
,
529 loadInfo
->GetOriginAttributes());
531 if (IsThirdPartyChannel(aChannel
)) {
532 nsAutoCString targetOrigin
;
533 nsAutoCString trackingOrigin
;
534 if (NS_FAILED(targetPrincipal
->GetOriginNoSuffix(targetOrigin
)) ||
535 NS_FAILED(trackingPrincipal
->GetOriginNoSuffix(trackingOrigin
))) {
536 return nsILoadInfo::NoStoragePermission
;
539 if (PartitioningExceptionList::Check(targetOrigin
, trackingOrigin
)) {
540 return nsILoadInfo::StoragePermissionAllowListed
;
545 AntiTrackingUtils::CreateStoragePermissionKey(trackingPrincipal
, type
);
547 uint32_t unusedReason
= 0;
549 if (AntiTrackingUtils::CheckStoragePermission(targetPrincipal
, type
,
550 NS_UsePrivateBrowsing(aChannel
),
551 &unusedReason
, unusedReason
)) {
552 return nsILoadInfo::HasStoragePermission
;
555 WindowContext
* wc
= bc
->GetCurrentWindowContext();
557 return nsILoadInfo::NoStoragePermission
;
559 WindowGlobalParent
* wgp
= wc
->Canonical();
561 return nsILoadInfo::NoStoragePermission
;
563 nsIPrincipal
* framePrincipal
= wgp
->DocumentPrincipal();
564 if (!framePrincipal
) {
565 return nsILoadInfo::NoStoragePermission
;
568 if (policyType
== ExtContentPolicy::TYPE_SUBDOCUMENT
) {
569 // For loads of framed documents, we only use storage access
570 // if the load is the result of a same-origin, same-site-initiated
571 // navigation of the frame.
572 uint64_t triggeringWindowId
;
573 rv
= loadInfo
->GetTriggeringWindowId(&triggeringWindowId
);
574 if (NS_WARN_IF(NS_FAILED(rv
))) {
575 return nsILoadInfo::NoStoragePermission
;
577 bool triggeringWindowHasStorageAccess
;
579 loadInfo
->GetTriggeringStorageAccess(&triggeringWindowHasStorageAccess
);
580 if (NS_WARN_IF(NS_FAILED(rv
))) {
581 return nsILoadInfo::NoStoragePermission
;
584 nsIScriptSecurityManager
* ssm
= nsContentUtils::GetSecurityManager();
585 RefPtr
<nsIPrincipal
> channelResultPrincipal
;
586 rv
= ssm
->GetChannelResultPrincipal(aChannel
,
587 getter_AddRefs(channelResultPrincipal
));
588 if (NS_WARN_IF(NS_FAILED(rv
))) {
589 return nsILoadInfo::NoStoragePermission
;
591 RefPtr
<net::HttpBaseChannel
> httpChannel
= do_QueryObject(aChannel
);
592 bool crossSiteInitiated
= false;
593 if (bc
&& bc
->GetParent()->GetCurrentWindowContext()) {
594 RefPtr
<WindowGlobalParent
> triggeringWGP
=
595 WindowGlobalParent::GetByInnerWindowId(triggeringWindowId
);
596 if (triggeringWGP
&& triggeringWGP
->DocumentPrincipal()) {
597 rv
= triggeringWGP
->DocumentPrincipal()->IsThirdPartyPrincipal(
598 channelResultPrincipal
, &crossSiteInitiated
);
600 crossSiteInitiated
= false;
605 if (!crossSiteInitiated
&& triggeringWindowHasStorageAccess
&&
606 trackingPrincipal
->Equals(framePrincipal
) && httpChannel
&&
607 !httpChannel
->HasRedirectTaintedOrigin()) {
608 return nsILoadInfo::HasStoragePermission
;
610 } else if (!bc
->IsTop()) {
611 // For subframe resources, check if the document has storage access
612 // and that the resource being loaded is same-site to the page.
613 bool isThirdParty
= true;
614 nsresult rv
= framePrincipal
->IsThirdPartyURI(trackingURI
, &isThirdParty
);
615 if (NS_SUCCEEDED(rv
) && wc
->GetUsingStorageAccess() && !isThirdParty
) {
616 return nsILoadInfo::HasStoragePermission
;
620 return nsILoadInfo::NoStoragePermission
;
623 uint64_t AntiTrackingUtils::GetTopLevelAntiTrackingWindowId(
624 BrowsingContext
* aBrowsingContext
) {
625 MOZ_ASSERT(aBrowsingContext
);
627 RefPtr
<WindowContext
> winContext
=
628 aBrowsingContext
->GetCurrentWindowContext();
629 if (!winContext
|| winContext
->GetCookieBehavior().isNothing()) {
633 // Do not check BEHAVIOR_REJECT_TRACKER_AND_PARTITION_FOREIGN her because when
634 // a third-party subresource is inside the main frame, we need to return the
635 // top-level window id to partition its cookies correctly.
636 uint32_t behavior
= *winContext
->GetCookieBehavior();
637 if (behavior
== nsICookieService::BEHAVIOR_REJECT_TRACKER
&&
638 aBrowsingContext
->IsTop()) {
642 return aBrowsingContext
->Top()->GetCurrentInnerWindowId();
645 uint64_t AntiTrackingUtils::GetTopLevelStorageAreaWindowId(
646 BrowsingContext
* aBrowsingContext
) {
647 MOZ_ASSERT(aBrowsingContext
);
649 if (Document::StorageAccessSandboxed(aBrowsingContext
->GetSandboxFlags())) {
653 BrowsingContext
* parentBC
= aBrowsingContext
->GetParent();
655 // No parent browsing context available!
659 if (!parentBC
->IsTop()) {
663 return parentBC
->GetCurrentInnerWindowId();
667 already_AddRefed
<nsIPrincipal
> AntiTrackingUtils::GetPrincipal(
668 BrowsingContext
* aBrowsingContext
) {
669 MOZ_ASSERT(aBrowsingContext
);
671 nsCOMPtr
<nsIPrincipal
> principal
;
673 if (XRE_IsContentProcess()) {
674 // Passing an out-of-process browsing context in child processes to
675 // this API won't get any result, so just assert.
676 MOZ_ASSERT(aBrowsingContext
->IsInProcess());
678 nsPIDOMWindowOuter
* outer
= aBrowsingContext
->GetDOMWindow();
679 if (NS_WARN_IF(!outer
)) {
683 nsPIDOMWindowInner
* inner
= outer
->GetCurrentInnerWindow();
684 if (NS_WARN_IF(!inner
)) {
688 principal
= nsGlobalWindowInner::Cast(inner
)->GetPrincipal();
690 WindowGlobalParent
* wgp
=
691 aBrowsingContext
->Canonical()->GetCurrentWindowGlobal();
692 if (NS_WARN_IF(!wgp
)) {
696 principal
= wgp
->DocumentPrincipal();
698 return principal
.forget();
702 bool AntiTrackingUtils::GetPrincipalAndTrackingOrigin(
703 BrowsingContext
* aBrowsingContext
, nsIPrincipal
** aPrincipal
,
704 nsACString
& aTrackingOrigin
) {
705 MOZ_ASSERT(aBrowsingContext
);
707 // Passing an out-of-process browsing context in child processes to
708 // this API won't get any result, so just assert.
709 MOZ_ASSERT_IF(XRE_IsContentProcess(), aBrowsingContext
->IsInProcess());
711 // Let's take the principal and the origin of the tracker.
712 nsCOMPtr
<nsIPrincipal
> principal
=
713 AntiTrackingUtils::GetPrincipal(aBrowsingContext
);
714 if (NS_WARN_IF(!principal
)) {
718 nsresult rv
= principal
->GetOriginNoSuffix(aTrackingOrigin
);
719 if (NS_WARN_IF(NS_FAILED(rv
))) {
724 principal
.forget(aPrincipal
);
731 uint32_t AntiTrackingUtils::GetCookieBehavior(
732 BrowsingContext
* aBrowsingContext
) {
733 MOZ_ASSERT(aBrowsingContext
);
735 RefPtr
<dom::WindowContext
> win
= aBrowsingContext
->GetCurrentWindowContext();
736 if (!win
|| win
->GetCookieBehavior().isNothing()) {
737 return nsICookieService::BEHAVIOR_REJECT
;
740 return *win
->GetCookieBehavior();
744 already_AddRefed
<WindowGlobalParent
>
745 AntiTrackingUtils::GetTopWindowExcludingExtensionAccessibleContentFrames(
746 CanonicalBrowsingContext
* aBrowsingContext
, nsIURI
* aURIBeingLoaded
) {
747 MOZ_ASSERT(XRE_IsParentProcess());
748 MOZ_ASSERT(aBrowsingContext
);
750 CanonicalBrowsingContext
* bc
= aBrowsingContext
;
751 RefPtr
<WindowGlobalParent
> prev
;
752 while (RefPtr
<WindowGlobalParent
> parent
= bc
->GetParentWindowContext()) {
753 CanonicalBrowsingContext
* parentBC
= parent
->BrowsingContext();
755 nsIPrincipal
* parentPrincipal
= parent
->DocumentPrincipal();
756 nsIURI
* uri
= prev
? prev
->GetDocumentURI() : aURIBeingLoaded
;
758 // If the new parent has permission to load the current page, we're
759 // at a moz-extension:// frame which has a host permission that allows
760 // it to load the document that we've loaded. In that case, stop at
761 // this frame and consider it the top-level frame.
763 BasePrincipal::Cast(parentPrincipal
)->AddonAllowsLoad(uri
, true)) {
771 prev
= bc
->GetCurrentWindowGlobal();
773 return prev
.forget();
777 nsresult
AntiTrackingUtils::IsThirdPartyToPartitionKeySite(
778 nsIChannel
* aChannel
, const nsCOMPtr
<nsIURI
>& aURI
, bool* aIsThirdParty
) {
779 MOZ_ASSERT(XRE_IsParentProcess());
780 NS_ENSURE_ARG(aChannel
);
782 NS_ENSURE_ARG(aIsThirdParty
);
783 nsCOMPtr
<nsILoadInfo
> loadInfo
= aChannel
->LoadInfo();
784 nsCOMPtr
<nsICookieJarSettings
> cjs
;
785 nsresult rv
= loadInfo
->GetCookieJarSettings(getter_AddRefs(cjs
));
786 NS_ENSURE_SUCCESS(rv
, rv
);
788 nsAutoString partitionKey
;
789 rv
= cjs
->GetPartitionKey(partitionKey
);
790 NS_ENSURE_SUCCESS(rv
, rv
);
792 nsAutoString scheme
, host
;
795 if (!OriginAttributes::ParsePartitionKey(partitionKey
, scheme
, host
, _unused1
,
797 return NS_ERROR_FAILURE
;
799 if (host
.IsEmpty()) {
800 return NS_ERROR_FAILURE
;
802 nsAutoString partitionKeySite
= scheme
+ u
"://"_ns
+ host
;
803 nsCOMPtr
<nsIURI
> partitionKeySiteURI
;
804 rv
= NS_NewURI(getter_AddRefs(partitionKeySiteURI
), partitionKeySite
);
805 NS_ENSURE_SUCCESS(rv
, rv
);
807 nsCOMPtr
<mozIThirdPartyUtil
> thirdPartyUtil
=
808 components::ThirdPartyUtil::Service();
809 return thirdPartyUtil
->IsThirdPartyURI(aURI
, partitionKeySiteURI
,
814 void AntiTrackingUtils::ComputeIsThirdPartyToTopWindow(nsIChannel
* aChannel
) {
815 MOZ_ASSERT(aChannel
);
816 MOZ_ASSERT(XRE_IsParentProcess());
818 nsCOMPtr
<nsILoadInfo
> loadInfo
= aChannel
->LoadInfo();
820 // When a top-level load is opened by window.open, BrowsingContext from
821 // LoadInfo is its opener, which may make the third-party caculation code
822 // below returns an incorrect result. So we use TYPE_DOCUMENT to
823 // ensure a top-level load is not considered 3rd-party.
824 auto policyType
= loadInfo
->GetExternalContentPolicyType();
825 if (policyType
== ExtContentPolicy::TYPE_DOCUMENT
) {
826 loadInfo
->SetIsThirdPartyContextToTopWindow(false);
830 RefPtr
<BrowsingContext
> bc
;
831 loadInfo
->GetBrowsingContext(getter_AddRefs(bc
));
833 bc
= loadInfo
->GetWorkerAssociatedBrowsingContext();
836 nsCOMPtr
<nsIURI
> uri
;
837 Unused
<< aChannel
->GetURI(getter_AddRefs(uri
));
839 // In some cases we don't have a browsingContext. For example, in xpcshell
840 // tests, channels that are used to download images and channels for loading
843 // If the flag was set before, we don't need to compute again. This could
844 // happen for the channels used to load worker scripts.
846 // Note that we cannot stop computing the flag in general even it has set
847 // before because sometimes we need to get the up-to-date flag, e.g.
849 if (static_cast<net::LoadInfo
*>(loadInfo
.get())
850 ->HasIsThirdPartyContextToTopWindowSet()) {
854 // We turn to check the top-level principal if there is no browsing context.
855 auto* principal
= BasePrincipal::Cast(loadInfo
->GetTopLevelPrincipal());
857 // If we don't have the top level principal, we try to compare directly to
858 // the scheme in the partition key. Failing that, we fall back to
859 // comparing to the loading principal. But we can only do this if this
860 // channel has a URI. If we have neither a channel URI or the browsing
861 // context, we can do nothing.
864 bool isThirdParty
= true;
866 IsThirdPartyToPartitionKeySite(aChannel
, uri
, &isThirdParty
);
867 if (NS_SUCCEEDED(rv
)) {
868 loadInfo
->SetIsThirdPartyContextToTopWindow(isThirdParty
);
872 principal
= BasePrincipal::Cast(loadInfo
->GetLoadingPrincipal());
875 bool isThirdParty
= true;
876 nsresult rv
= principal
->IsThirdPartyURI(uri
, &isThirdParty
);
878 if (NS_SUCCEEDED(rv
)) {
879 loadInfo
->SetIsThirdPartyContextToTopWindow(isThirdParty
);
885 RefPtr
<WindowGlobalParent
> topWindow
=
886 GetTopWindowExcludingExtensionAccessibleContentFrames(bc
->Canonical(),
889 if (NS_WARN_IF(!topWindow
)) {
893 nsCOMPtr
<nsIPrincipal
> topWindowPrincipal
= topWindow
->DocumentPrincipal();
894 if (topWindowPrincipal
&& !topWindowPrincipal
->GetIsNullPrincipal()) {
895 auto* basePrin
= BasePrincipal::Cast(topWindowPrincipal
);
896 bool isThirdParty
= true;
898 // For about:blank and about:srcdoc, we can't just compare uri to determine
899 // whether the page is third-party, so we use channel result principal
900 // instead. By doing this, an the resource inherits the principal from
901 // its parent is considered not a third-party.
902 if (NS_IsAboutBlank(uri
) || NS_IsAboutSrcdoc(uri
) ||
903 uri
->SchemeIs("blob")) {
904 nsIScriptSecurityManager
* ssm
= nsContentUtils::GetSecurityManager();
905 if (NS_WARN_IF(!ssm
)) {
909 nsCOMPtr
<nsIPrincipal
> principal
;
911 ssm
->GetChannelResultPrincipal(aChannel
, getter_AddRefs(principal
));
912 if (NS_WARN_IF(NS_FAILED(rv
))) {
916 basePrin
->IsThirdPartyPrincipal(principal
, &isThirdParty
);
918 basePrin
->IsThirdPartyURI(uri
, &isThirdParty
);
921 loadInfo
->SetIsThirdPartyContextToTopWindow(isThirdParty
);
926 bool AntiTrackingUtils::IsThirdPartyChannel(nsIChannel
* aChannel
) {
927 MOZ_ASSERT(aChannel
);
929 nsCOMPtr
<mozIThirdPartyUtil
> tpuService
=
930 mozilla::components::ThirdPartyUtil::Service();
934 bool thirdParty
= true;
935 nsresult rv
= tpuService
->IsThirdPartyChannel(aChannel
, nullptr, &thirdParty
);
943 bool AntiTrackingUtils::IsThirdPartyWindow(nsPIDOMWindowInner
* aWindow
,
947 // We assume that the window is foreign to the URI by default.
948 bool thirdParty
= true;
950 // We will skip checking URIs for about:blank and about:srcdoc because they
951 // have no domain. So, comparing them will always fail.
952 if (aURI
&& !NS_IsAboutBlank(aURI
) && !NS_IsAboutSrcdoc(aURI
)) {
953 nsCOMPtr
<nsIScriptObjectPrincipal
> scriptObjPrin
=
954 do_QueryInterface(aWindow
);
955 if (!scriptObjPrin
) {
959 nsCOMPtr
<nsIPrincipal
> prin
= scriptObjPrin
->GetPrincipal();
964 // Determine whether aURI is foreign with respect to the current principal.
965 nsresult rv
= prin
->IsThirdPartyURI(aURI
, &thirdParty
);
975 RefPtr
<Document
> doc
= aWindow
->GetDoc();
977 // If we can't get the document from the window, ex, about:blank, fallback
978 // to use IsThirdPartyWindow check that examine the whole hierarchy.
979 nsCOMPtr
<mozIThirdPartyUtil
> thirdPartyUtil
=
980 components::ThirdPartyUtil::Service();
981 Unused
<< thirdPartyUtil
->IsThirdPartyWindow(aWindow
->GetOuterWindow(),
982 nullptr, &thirdParty
);
986 return IsThirdPartyDocument(doc
);
990 bool AntiTrackingUtils::IsThirdPartyDocument(Document
* aDocument
) {
991 MOZ_ASSERT(aDocument
);
992 nsCOMPtr
<mozIThirdPartyUtil
> tpuService
=
993 mozilla::components::ThirdPartyUtil::Service();
997 bool thirdParty
= true;
998 if (!aDocument
->GetChannel()) {
999 // If we can't get the channel from the document, i.e. initial about:blank
1000 // page, we use the browsingContext of the document to check if it's in the
1001 // third-party context. If the browsing context is still not available, we
1002 // will treat the window as third-party.
1003 RefPtr
<BrowsingContext
> bc
= aDocument
->GetBrowsingContext();
1004 return bc
? IsThirdPartyContext(bc
) : true;
1007 nsresult rv
= tpuService
->IsThirdPartyChannel(aDocument
->GetChannel(),
1008 nullptr, &thirdParty
);
1009 if (NS_FAILED(rv
)) {
1016 bool AntiTrackingUtils::IsThirdPartyContext(BrowsingContext
* aBrowsingContext
) {
1017 MOZ_ASSERT(aBrowsingContext
);
1018 MOZ_ASSERT(aBrowsingContext
->IsInProcess());
1020 // iframes with SANDBOX_ORIGIN are always third-party contexts
1021 // because they are a unique origin
1022 nsIDocShell
* docShell
= aBrowsingContext
->GetDocShell();
1026 Document
* doc
= docShell
->GetExtantDocument();
1027 if (!doc
|| doc
->GetSandboxFlags() & SANDBOXED_ORIGIN
) {
1030 nsIPrincipal
* principal
= doc
->NodePrincipal();
1032 BrowsingContext
* traversingParent
= aBrowsingContext
->GetParent();
1033 while (traversingParent
) {
1034 // If the parent browsing context is not in the same process, it's
1036 if (!traversingParent
->IsInProcess()) {
1040 nsIDocShell
* parentDocShell
= traversingParent
->GetDocShell();
1041 if (!parentDocShell
) {
1044 Document
* parentDoc
= parentDocShell
->GetDocument();
1045 if (!parentDoc
|| parentDoc
->GetSandboxFlags() & SANDBOXED_ORIGIN
) {
1048 nsIPrincipal
* parentPrincipal
= parentDoc
->NodePrincipal();
1050 auto* parentBasePrin
= BasePrincipal::Cast(parentPrincipal
);
1051 bool isThirdParty
= true;
1053 parentBasePrin
->IsThirdPartyPrincipal(principal
, &isThirdParty
);
1058 traversingParent
= traversingParent
->GetParent();
1064 nsCString
AntiTrackingUtils::GrantedReasonToString(
1065 ContentBlockingNotifier::StorageAccessPermissionGrantedReason aReason
) {
1067 case ContentBlockingNotifier::eOpener
:
1069 case ContentBlockingNotifier::eOpenerAfterUserInteraction
:
1070 return "user interaction"_ns
;
1072 return "stroage access API"_ns
;
1077 void AntiTrackingUtils::UpdateAntiTrackingInfoForChannel(nsIChannel
* aChannel
) {
1078 MOZ_ASSERT(aChannel
);
1080 if (!XRE_IsParentProcess()) {
1084 MOZ_DIAGNOSTIC_ASSERT(XRE_IsParentProcess());
1086 AntiTrackingUtils::ComputeIsThirdPartyToTopWindow(aChannel
);
1088 nsCOMPtr
<nsILoadInfo
> loadInfo
= aChannel
->LoadInfo();
1090 Unused
<< loadInfo
->SetStoragePermission(
1091 AntiTrackingUtils::GetStoragePermissionStateInParent(aChannel
));
1093 // Note that we need to put this after computing the IsThirdPartyToTopWindow
1094 // flag because it will be used when getting the granular fingerprinting
1096 Maybe
<RFPTarget
> overriddenFingerprintingSettings
=
1097 nsRFPService::GetOverriddenFingerprintingSettingsForChannel(aChannel
);
1099 if (overriddenFingerprintingSettings
) {
1100 loadInfo
->SetOverriddenFingerprintingSettings(
1101 overriddenFingerprintingSettings
.ref());
1104 static_cast<mozilla::net::LoadInfo
*>(loadInfo
.get())
1105 ->MarkOverriddenFingerprintingSettingsAsSet();
1108 nsCOMPtr
<nsICookieJarSettings
> cookieJarSettings
;
1109 Unused
<< loadInfo
->GetCookieJarSettings(getter_AddRefs(cookieJarSettings
));
1110 // Subresources (including subdocuments) may have a different partition key,
1111 // particularly one without or with the same-site bit. We have to update that
1113 net::CookieJarSettings::Cast(cookieJarSettings
)
1114 ->UpdatePartitionKeyForDocumentLoadedByChannel(aChannel
);
1116 // We only update the IsOnContentBlockingAllowList flag and the partition key
1117 // for the top-level http channel.
1119 // The IsOnContentBlockingAllowList is only for http. For other types of
1120 // channels, such as 'file:', there will be no interface to modify this. So,
1121 // we only update it in http channels.
1123 // The partition key is computed based on the site, so it's no point to set it
1124 // for channels other than http channels.
1125 ExtContentPolicyType contentType
= loadInfo
->GetExternalContentPolicyType();
1126 nsCOMPtr
<nsIHttpChannel
> httpChannel
= do_QueryInterface(aChannel
);
1127 if (!httpChannel
|| contentType
!= ExtContentPolicy::TYPE_DOCUMENT
) {
1131 // Update the IsOnContentBlockingAllowList flag in the CookieJarSettings
1132 // if this is a top level loading. For sub-document loading, this flag
1133 // would inherit from the parent.
1134 net::CookieJarSettings::Cast(cookieJarSettings
)
1135 ->UpdateIsOnContentBlockingAllowList(aChannel
);
1137 // We only need to set FPD for top-level loads. FPD will automatically be
1138 // propagated to non-top level loads via CookieJarSetting.
1139 nsCOMPtr
<nsIURI
> uri
;
1140 Unused
<< aChannel
->GetURI(getter_AddRefs(uri
));
1141 net::CookieJarSettings::Cast(cookieJarSettings
)->SetPartitionKey(uri
, false);
1143 // Generate the fingerprinting randomization key for top-level loads. The key
1144 // will automatically be propagated to sub loads.
1145 auto RFPRandomKey
= nsRFPService::GenerateKey(aChannel
);
1147 net::CookieJarSettings::Cast(cookieJarSettings
)
1148 ->SetFingerprintingRandomizationKey(RFPRandomKey
.ref());