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 "mozilla/BasePrincipal.h"
11 #include "mozilla/Components.h"
12 #include "mozilla/dom/BrowsingContext.h"
13 #include "mozilla/dom/CanonicalBrowsingContext.h"
14 #include "mozilla/net/CookieJarSettings.h"
15 #include "mozilla/LoadInfo.h"
16 #include "mozilla/dom/Document.h"
17 #include "mozilla/dom/WindowGlobalParent.h"
18 #include "mozilla/dom/WindowContext.h"
19 #include "mozilla/net/NeckoChannelParams.h"
20 #include "mozilla/PermissionManager.h"
21 #include "mozIThirdPartyUtil.h"
22 #include "nsEffectiveTLDService.h"
23 #include "nsGlobalWindowInner.h"
24 #include "nsIChannel.h"
25 #include "nsICookieService.h"
26 #include "nsIHttpChannel.h"
27 #include "nsIPermission.h"
29 #include "nsNetUtil.h"
30 #include "nsPIDOMWindow.h"
31 #include "nsRFPService.h"
32 #include "nsSandboxFlags.h"
33 #include "nsScriptSecurityManager.h"
34 #include "PartitioningExceptionList.h"
36 #define ANTITRACKING_PERM_KEY "3rdPartyStorage"
37 #define ANTITRACKING_FRAME_PERM_KEY "3rdPartyFrameStorage"
39 using namespace mozilla
;
40 using namespace mozilla::dom
;
42 /* static */ already_AddRefed
<nsPIDOMWindowInner
>
43 AntiTrackingUtils::GetInnerWindow(BrowsingContext
* aBrowsingContext
) {
44 MOZ_ASSERT(aBrowsingContext
);
46 nsCOMPtr
<nsPIDOMWindowOuter
> outer
= aBrowsingContext
->GetDOMWindow();
51 nsCOMPtr
<nsPIDOMWindowInner
> inner
= outer
->GetCurrentInnerWindow();
52 return inner
.forget();
55 /* static */ already_AddRefed
<nsPIDOMWindowOuter
>
56 AntiTrackingUtils::GetTopWindow(nsPIDOMWindowInner
* aWindow
) {
57 Document
* document
= aWindow
->GetExtantDoc();
62 nsIChannel
* channel
= document
->GetChannel();
67 nsCOMPtr
<nsPIDOMWindowOuter
> pwin
=
68 aWindow
->GetBrowsingContext()->Top()->GetDOMWindow();
78 already_AddRefed
<nsIURI
> AntiTrackingUtils::MaybeGetDocumentURIBeingLoaded(
79 nsIChannel
* aChannel
) {
80 nsCOMPtr
<nsIURI
> uriBeingLoaded
;
81 nsLoadFlags loadFlags
= 0;
82 nsresult rv
= aChannel
->GetLoadFlags(&loadFlags
);
83 if (NS_WARN_IF(NS_FAILED(rv
))) {
86 if (loadFlags
& nsIChannel::LOAD_DOCUMENT_URI
) {
87 // If the channel being loaded is a document channel, this call may be
88 // coming from an OnStopRequest notification, which might mean that our
89 // document may still be in the loading process, so we may need to pass in
90 // the uriBeingLoaded argument explicitly.
91 rv
= NS_GetFinalChannelURI(aChannel
, getter_AddRefs(uriBeingLoaded
));
92 if (NS_WARN_IF(NS_FAILED(rv
))) {
96 return uriBeingLoaded
.forget();
100 void AntiTrackingUtils::CreateStoragePermissionKey(
101 const nsACString
& aTrackingOrigin
, nsACString
& aPermissionKey
) {
102 MOZ_ASSERT(aPermissionKey
.IsEmpty());
104 static const nsLiteralCString prefix
=
105 nsLiteralCString(ANTITRACKING_PERM_KEY
"^");
107 aPermissionKey
.SetCapacity(prefix
.Length() + aTrackingOrigin
.Length());
108 aPermissionKey
.Append(prefix
);
109 aPermissionKey
.Append(aTrackingOrigin
);
113 bool AntiTrackingUtils::CreateStoragePermissionKey(nsIPrincipal
* aPrincipal
,
119 nsAutoCString origin
;
120 nsresult rv
= aPrincipal
->GetOriginNoSuffix(origin
);
121 if (NS_WARN_IF(NS_FAILED(rv
))) {
125 CreateStoragePermissionKey(origin
, aKey
);
130 void AntiTrackingUtils::CreateStorageFramePermissionKey(
131 const nsACString
& aTrackingSite
, nsACString
& aPermissionKey
) {
132 MOZ_ASSERT(aPermissionKey
.IsEmpty());
134 static const nsLiteralCString prefix
=
135 nsLiteralCString(ANTITRACKING_FRAME_PERM_KEY
"^");
137 aPermissionKey
.SetCapacity(prefix
.Length() + aTrackingSite
.Length());
138 aPermissionKey
.Append(prefix
);
139 aPermissionKey
.Append(aTrackingSite
);
143 bool AntiTrackingUtils::CreateStorageFramePermissionKey(
144 nsIPrincipal
* aPrincipal
, nsACString
& aKey
) {
145 MOZ_ASSERT(aPrincipal
);
148 nsresult rv
= aPrincipal
->GetSiteOriginNoSuffix(site
);
149 if (NS_WARN_IF(NS_FAILED(rv
))) {
153 CreateStorageFramePermissionKey(site
, aKey
);
158 bool AntiTrackingUtils::CreateStorageRequestPermissionKey(
159 nsIURI
* aURI
, nsACString
& aPermissionKey
) {
160 MOZ_ASSERT(aPermissionKey
.IsEmpty());
161 RefPtr
<nsEffectiveTLDService
> eTLDService
=
162 nsEffectiveTLDService::GetInstance();
167 nsresult rv
= eTLDService
->GetSite(aURI
, site
);
171 static const nsLiteralCString prefix
=
172 nsLiteralCString("AllowStorageAccessRequest^");
173 aPermissionKey
.SetCapacity(prefix
.Length() + site
.Length());
174 aPermissionKey
.Append(prefix
);
175 aPermissionKey
.Append(site
);
180 bool AntiTrackingUtils::IsStorageAccessPermission(nsIPermission
* aPermission
,
181 nsIPrincipal
* aPrincipal
) {
182 MOZ_ASSERT(aPermission
);
183 MOZ_ASSERT(aPrincipal
);
185 // The permission key may belong either to a tracking origin on the same
186 // origin as the granted origin, or on another origin as the granted origin
187 // (for example when a tracker in a third-party context uses window.open to
188 // open another origin where that second origin would be the granted origin.)
189 // But even in the second case, the type of the permission would still be
190 // formed by concatenating the granted origin to the end of the type name
191 // (see CreatePermissionKey). Therefore, we pass in the same argument to
192 // both tracking origin and granted origin here in order to compute the
193 // shorter permission key and will then do a prefix match on the type of the
194 // input permission to see if it is a storage access permission or not.
195 nsAutoCString permissionKey
;
196 bool result
= CreateStoragePermissionKey(aPrincipal
, permissionKey
);
197 if (NS_WARN_IF(!result
)) {
202 nsresult rv
= aPermission
->GetType(type
);
203 if (NS_WARN_IF(NS_FAILED(rv
))) {
207 return StringBeginsWith(type
, permissionKey
);
211 Maybe
<size_t> AntiTrackingUtils::CountSitesAllowStorageAccess(
212 nsIPrincipal
* aPrincipal
) {
213 PermissionManager
* permManager
= PermissionManager::GetInstance();
214 if (NS_WARN_IF(!permManager
)) {
218 nsAutoCString prefix
;
219 AntiTrackingUtils::CreateStoragePermissionKey(aPrincipal
, prefix
);
220 nsAutoCString framePrefix
;
221 AntiTrackingUtils::CreateStorageFramePermissionKey(aPrincipal
, framePrefix
);
223 using Permissions
= nsTArray
<RefPtr
<nsIPermission
>>;
225 nsresult rv
= permManager
->GetAllWithTypePrefix(prefix
, perms
);
226 if (NS_WARN_IF(NS_FAILED(rv
))) {
229 Permissions framePermissions
;
230 rv
= permManager
->GetAllWithTypePrefix(framePrefix
, framePermissions
);
231 if (NS_WARN_IF(NS_FAILED(rv
))) {
234 if (!perms
.AppendElements(framePermissions
, fallible
)) {
238 // Create a set of unique origins
239 using Sites
= nsTArray
<nsCString
>;
242 // Iterate over all permissions that have a prefix equal to the expected
243 // permission we are looking for. This includes two things we need to remove:
244 // duplicates and origin strings that have a prefix of aPrincipal's origin
245 // string, e.g. https://example.company when aPrincipal is
246 // https://example.com.
247 for (const auto& perm
: perms
) {
249 rv
= perm
->GetType(type
);
250 if (NS_WARN_IF(NS_FAILED(rv
))) {
254 // Let's make sure that we're not looking at a permission for
255 // https://exampletracker.company when we mean to look for the
256 // permission for https://exampletracker.com!
257 if (type
!= prefix
&& type
!= framePrefix
) {
261 nsCOMPtr
<nsIPrincipal
> principal
;
262 rv
= perm
->GetPrincipal(getter_AddRefs(principal
));
263 if (NS_WARN_IF(NS_FAILED(rv
))) {
268 rv
= principal
->GetSiteOrigin(site
);
269 if (NS_WARN_IF(NS_FAILED(rv
))) {
275 // Append if it would not be a duplicate
276 if (sites
.IndexOf(site
) == Sites::NoIndex
) {
277 sites
.AppendElement(site
);
281 return Some(sites
.Length());
285 bool AntiTrackingUtils::CheckStoragePermission(nsIPrincipal
* aPrincipal
,
286 const nsAutoCString
& aType
,
287 bool aIsInPrivateBrowsing
,
288 uint32_t* aRejectedReason
,
289 uint32_t aBlockedReason
) {
290 PermissionManager
* permManager
= PermissionManager::GetInstance();
291 if (NS_WARN_IF(!permManager
)) {
292 LOG(("Failed to obtain the permission manager"));
297 if (aIsInPrivateBrowsing
) {
298 LOG_PRIN(("Querying the permissions for private modei looking for a "
299 "permission of type %s for %s",
302 if (!permManager
->PermissionAvailable(aPrincipal
, aType
)) {
304 ("Permission isn't available for this principal in the current "
308 nsTArray
<RefPtr
<nsIPermission
>> permissions
;
309 nsresult rv
= permManager
->GetAllForPrincipal(aPrincipal
, permissions
);
310 if (NS_WARN_IF(NS_FAILED(rv
))) {
311 LOG(("Failed to get the list of permissions"));
316 for (const auto& permission
: permissions
) {
318 LOG(("Couldn't get the permission for unknown reasons"));
322 nsAutoCString permissionType
;
323 if (NS_SUCCEEDED(permission
->GetType(permissionType
)) &&
324 permissionType
!= aType
) {
325 LOG(("Non-matching permission type: %s", aType
.get()));
329 uint32_t capability
= 0;
330 if (NS_SUCCEEDED(permission
->GetCapability(&capability
)) &&
331 capability
!= nsIPermissionManager::ALLOW_ACTION
) {
332 LOG(("Non-matching permission capability: %d", capability
));
336 uint32_t expirationType
= 0;
337 if (NS_SUCCEEDED(permission
->GetExpireType(&expirationType
)) &&
338 expirationType
!= nsIPermissionManager ::EXPIRE_SESSION
) {
339 LOG(("Non-matching permission expiration type: %d", expirationType
));
343 int64_t expirationTime
= 0;
344 if (NS_SUCCEEDED(permission
->GetExpireTime(&expirationTime
)) &&
345 expirationTime
!= 0) {
346 LOG(("Non-matching permission expiration time: %" PRId64
,
351 LOG(("Found a matching permission"));
357 if (aRejectedReason
) {
358 *aRejectedReason
= aBlockedReason
;
363 nsresult rv
= permManager
->TestPermissionWithoutDefaultsFromPrincipal(
364 aPrincipal
, aType
, &result
);
365 if (NS_WARN_IF(NS_FAILED(rv
))) {
366 LOG(("Failed to test the permission"));
371 ("Testing permission type %s for %s resulted in %d (%s)", aType
.get(),
373 result
== nsIPermissionManager::ALLOW_ACTION
? "success" : "failure"),
376 if (result
!= nsIPermissionManager::ALLOW_ACTION
) {
377 if (aRejectedReason
) {
378 *aRejectedReason
= aBlockedReason
;
388 nsILoadInfo::StoragePermissionState
389 AntiTrackingUtils::GetStoragePermissionStateInParent(nsIChannel
* aChannel
) {
390 MOZ_ASSERT(aChannel
);
391 MOZ_DIAGNOSTIC_ASSERT(XRE_IsParentProcess());
393 nsCOMPtr
<nsILoadInfo
> loadInfo
= aChannel
->LoadInfo();
394 nsCOMPtr
<nsICookieJarSettings
> cookieJarSettings
;
396 auto policyType
= loadInfo
->GetExternalContentPolicyType();
398 // The channel is for the document load of the top-level window. The top-level
399 // window should always has 'hasStoragePermission' flag as false. So, we can
400 // return here directly.
401 if (policyType
== ExtContentPolicy::TYPE_DOCUMENT
) {
402 return nsILoadInfo::NoStoragePermission
;
406 loadInfo
->GetCookieJarSettings(getter_AddRefs(cookieJarSettings
));
407 if (NS_WARN_IF(NS_FAILED(rv
))) {
408 return nsILoadInfo::NoStoragePermission
;
411 int32_t cookieBehavior
= cookieJarSettings
->GetCookieBehavior();
413 // We only need to check the storage permission if the cookie behavior is
414 // BEHAVIOR_REJECT_TRACKER, BEHAVIOR_REJECT_TRACKER_AND_PARTITION_FOREIGN or
415 // BEHAVIOR_REJECT_FOREIGN with exceptions. Because ContentBlocking wouldn't
416 // update or check the storage permission if the cookie behavior is not
417 // belongs to these three.
418 if (!net::CookieJarSettings::IsRejectThirdPartyContexts(cookieBehavior
)) {
419 return nsILoadInfo::NoStoragePermission
;
422 RefPtr
<BrowsingContext
> bc
;
423 rv
= loadInfo
->GetTargetBrowsingContext(getter_AddRefs(bc
));
424 if (NS_WARN_IF(NS_FAILED(rv
)) || !bc
) {
425 return nsILoadInfo::NoStoragePermission
;
428 uint64_t targetWindowId
= GetTopLevelAntiTrackingWindowId(bc
);
429 nsCOMPtr
<nsIPrincipal
> targetPrincipal
;
431 if (targetWindowId
) {
432 RefPtr
<WindowGlobalParent
> wgp
=
433 WindowGlobalParent::GetByInnerWindowId(targetWindowId
);
435 if (NS_WARN_IF(!wgp
)) {
436 return nsILoadInfo::NoStoragePermission
;
439 targetPrincipal
= wgp
->DocumentPrincipal();
441 // We try to use the loading principal if there is no AntiTrackingWindowId.
442 targetPrincipal
= loadInfo
->GetLoadingPrincipal();
445 if (!targetPrincipal
) {
446 nsCOMPtr
<nsIHttpChannel
> httpChannel
= do_QueryInterface(aChannel
);
449 // We don't have a loading principal, let's see if this is a document
450 // channel which belongs to a top-level window.
451 bool isDocument
= false;
452 rv
= httpChannel
->GetIsMainDocumentChannel(&isDocument
);
453 if (NS_SUCCEEDED(rv
) && isDocument
) {
454 nsIScriptSecurityManager
* ssm
=
455 nsScriptSecurityManager::GetScriptSecurityManager();
456 Unused
<< ssm
->GetChannelResultPrincipal(
457 aChannel
, getter_AddRefs(targetPrincipal
));
462 // Let's use the triggering principal if we still have nothing on the hand.
463 if (!targetPrincipal
) {
464 targetPrincipal
= loadInfo
->TriggeringPrincipal();
467 // Cannot get the target principal, bail out.
468 if (NS_WARN_IF(!targetPrincipal
)) {
469 return nsILoadInfo::NoStoragePermission
;
472 nsCOMPtr
<nsIURI
> trackingURI
;
473 rv
= aChannel
->GetURI(getter_AddRefs(trackingURI
));
474 if (NS_WARN_IF(NS_FAILED(rv
))) {
475 return nsILoadInfo::NoStoragePermission
;
478 nsCOMPtr
<nsIPrincipal
> trackingPrincipal
=
479 BasePrincipal::CreateContentPrincipal(trackingURI
,
480 loadInfo
->GetOriginAttributes());
482 if (IsThirdPartyChannel(aChannel
)) {
483 nsAutoCString targetOrigin
;
484 nsAutoCString trackingOrigin
;
485 if (NS_FAILED(targetPrincipal
->GetOriginNoSuffix(targetOrigin
)) ||
486 NS_FAILED(trackingPrincipal
->GetOriginNoSuffix(trackingOrigin
))) {
487 return nsILoadInfo::NoStoragePermission
;
490 if (PartitioningExceptionList::Check(targetOrigin
, trackingOrigin
)) {
491 return nsILoadInfo::StoragePermissionAllowListed
;
496 AntiTrackingUtils::CreateStoragePermissionKey(trackingPrincipal
, type
);
498 uint32_t unusedReason
= 0;
500 if (AntiTrackingUtils::CheckStoragePermission(targetPrincipal
, type
,
501 NS_UsePrivateBrowsing(aChannel
),
502 &unusedReason
, unusedReason
)) {
503 return nsILoadInfo::HasStoragePermission
;
506 // Only check the frame only permission if the channel is not in the top
509 RefPtr
<nsEffectiveTLDService
> etld
= nsEffectiveTLDService::GetInstance();
511 return nsILoadInfo::NoStoragePermission
;
513 nsCString trackingSite
;
514 rv
= etld
->GetSite(trackingURI
, trackingSite
);
515 if (NS_WARN_IF(NS_FAILED(rv
))) {
516 return nsILoadInfo::NoStoragePermission
;
519 AntiTrackingUtils::CreateStorageFramePermissionKey(trackingSite
, type
);
521 if (AntiTrackingUtils::CheckStoragePermission(
522 targetPrincipal
, type
, NS_UsePrivateBrowsing(aChannel
),
523 &unusedReason
, unusedReason
)) {
524 return nsILoadInfo::HasStoragePermission
;
528 return nsILoadInfo::NoStoragePermission
;
531 uint64_t AntiTrackingUtils::GetTopLevelAntiTrackingWindowId(
532 BrowsingContext
* aBrowsingContext
) {
533 MOZ_ASSERT(aBrowsingContext
);
535 RefPtr
<WindowContext
> winContext
=
536 aBrowsingContext
->GetCurrentWindowContext();
537 if (!winContext
|| winContext
->GetCookieBehavior().isNothing()) {
541 // Do not check BEHAVIOR_REJECT_TRACKER_AND_PARTITION_FOREIGN her because when
542 // a third-party subresource is inside the main frame, we need to return the
543 // top-level window id to partition its cookies correctly.
544 uint32_t behavior
= *winContext
->GetCookieBehavior();
545 if (behavior
== nsICookieService::BEHAVIOR_REJECT_TRACKER
&&
546 aBrowsingContext
->IsTop()) {
550 return aBrowsingContext
->Top()->GetCurrentInnerWindowId();
553 uint64_t AntiTrackingUtils::GetTopLevelStorageAreaWindowId(
554 BrowsingContext
* aBrowsingContext
) {
555 MOZ_ASSERT(aBrowsingContext
);
557 if (Document::StorageAccessSandboxed(aBrowsingContext
->GetSandboxFlags())) {
561 BrowsingContext
* parentBC
= aBrowsingContext
->GetParent();
563 // No parent browsing context available!
567 if (!parentBC
->IsTop()) {
571 return parentBC
->GetCurrentInnerWindowId();
575 already_AddRefed
<nsIPrincipal
> AntiTrackingUtils::GetPrincipal(
576 BrowsingContext
* aBrowsingContext
) {
577 MOZ_ASSERT(aBrowsingContext
);
579 nsCOMPtr
<nsIPrincipal
> principal
;
581 if (XRE_IsContentProcess()) {
582 // Passing an out-of-process browsing context in child processes to
583 // this API won't get any result, so just assert.
584 MOZ_ASSERT(aBrowsingContext
->IsInProcess());
586 nsPIDOMWindowOuter
* outer
= aBrowsingContext
->GetDOMWindow();
587 if (NS_WARN_IF(!outer
)) {
591 nsPIDOMWindowInner
* inner
= outer
->GetCurrentInnerWindow();
592 if (NS_WARN_IF(!inner
)) {
596 principal
= nsGlobalWindowInner::Cast(inner
)->GetPrincipal();
598 WindowGlobalParent
* wgp
=
599 aBrowsingContext
->Canonical()->GetCurrentWindowGlobal();
600 if (NS_WARN_IF(!wgp
)) {
604 principal
= wgp
->DocumentPrincipal();
606 return principal
.forget();
610 bool AntiTrackingUtils::GetPrincipalAndTrackingOrigin(
611 BrowsingContext
* aBrowsingContext
, nsIPrincipal
** aPrincipal
,
612 nsACString
& aTrackingOrigin
) {
613 MOZ_ASSERT(aBrowsingContext
);
615 // Passing an out-of-process browsing context in child processes to
616 // this API won't get any result, so just assert.
617 MOZ_ASSERT_IF(XRE_IsContentProcess(), aBrowsingContext
->IsInProcess());
619 // Let's take the principal and the origin of the tracker.
620 nsCOMPtr
<nsIPrincipal
> principal
=
621 AntiTrackingUtils::GetPrincipal(aBrowsingContext
);
622 if (NS_WARN_IF(!principal
)) {
626 nsresult rv
= principal
->GetOriginNoSuffix(aTrackingOrigin
);
627 if (NS_WARN_IF(NS_FAILED(rv
))) {
632 principal
.forget(aPrincipal
);
639 uint32_t AntiTrackingUtils::GetCookieBehavior(
640 BrowsingContext
* aBrowsingContext
) {
641 MOZ_ASSERT(aBrowsingContext
);
643 RefPtr
<dom::WindowContext
> win
= aBrowsingContext
->GetCurrentWindowContext();
644 if (!win
|| win
->GetCookieBehavior().isNothing()) {
645 return nsICookieService::BEHAVIOR_REJECT
;
648 return *win
->GetCookieBehavior();
652 already_AddRefed
<WindowGlobalParent
>
653 AntiTrackingUtils::GetTopWindowExcludingExtensionAccessibleContentFrames(
654 CanonicalBrowsingContext
* aBrowsingContext
, nsIURI
* aURIBeingLoaded
) {
655 MOZ_ASSERT(XRE_IsParentProcess());
656 MOZ_ASSERT(aBrowsingContext
);
658 CanonicalBrowsingContext
* bc
= aBrowsingContext
;
659 RefPtr
<WindowGlobalParent
> prev
;
660 while (RefPtr
<WindowGlobalParent
> parent
= bc
->GetParentWindowContext()) {
661 CanonicalBrowsingContext
* parentBC
= parent
->BrowsingContext();
663 nsIPrincipal
* parentPrincipal
= parent
->DocumentPrincipal();
664 nsIURI
* uri
= prev
? prev
->GetDocumentURI() : aURIBeingLoaded
;
666 // If the new parent has permission to load the current page, we're
667 // at a moz-extension:// frame which has a host permission that allows
668 // it to load the document that we've loaded. In that case, stop at
669 // this frame and consider it the top-level frame.
671 BasePrincipal::Cast(parentPrincipal
)->AddonAllowsLoad(uri
, true)) {
679 prev
= bc
->GetCurrentWindowGlobal();
681 return prev
.forget();
685 void AntiTrackingUtils::ComputeIsThirdPartyToTopWindow(nsIChannel
* aChannel
) {
686 MOZ_ASSERT(aChannel
);
687 MOZ_ASSERT(XRE_IsParentProcess());
689 nsCOMPtr
<nsILoadInfo
> loadInfo
= aChannel
->LoadInfo();
691 // When a top-level load is opened by window.open, BrowsingContext from
692 // LoadInfo is its opener, which may make the third-party caculation code
693 // below returns an incorrect result. So we use TYPE_DOCUMENT to
694 // ensure a top-level load is not considered 3rd-party.
695 auto policyType
= loadInfo
->GetExternalContentPolicyType();
696 if (policyType
== ExtContentPolicy::TYPE_DOCUMENT
) {
697 loadInfo
->SetIsThirdPartyContextToTopWindow(false);
701 RefPtr
<BrowsingContext
> bc
;
702 loadInfo
->GetBrowsingContext(getter_AddRefs(bc
));
704 nsCOMPtr
<nsIURI
> uri
;
705 Unused
<< aChannel
->GetURI(getter_AddRefs(uri
));
707 // In some cases we don't have a browsingContext. For example, in xpcshell
708 // tests, channels that are used to download images and channels for loading
711 // If the flag was set before, we don't need to compute again. This could
712 // happen for the channels used to load worker scripts.
714 // Note that we cannot stop computing the flag in general even it has set
715 // before because sometimes we need to get the up-to-date flag, e.g.
717 if (static_cast<net::LoadInfo
*>(loadInfo
.get())
718 ->HasIsThirdPartyContextToTopWindowSet()) {
722 // We turn to check the loading principal if there is no browsing context.
723 auto* loadingPrincipal
=
724 BasePrincipal::Cast(loadInfo
->GetLoadingPrincipal());
726 if (uri
&& loadingPrincipal
) {
727 bool isThirdParty
= true;
728 nsresult rv
= loadingPrincipal
->IsThirdPartyURI(uri
, &isThirdParty
);
730 if (NS_SUCCEEDED(rv
)) {
731 loadInfo
->SetIsThirdPartyContextToTopWindow(isThirdParty
);
737 RefPtr
<WindowGlobalParent
> topWindow
=
738 GetTopWindowExcludingExtensionAccessibleContentFrames(bc
->Canonical(),
741 if (NS_WARN_IF(!topWindow
)) {
745 nsCOMPtr
<nsIPrincipal
> topWindowPrincipal
= topWindow
->DocumentPrincipal();
746 if (topWindowPrincipal
&& !topWindowPrincipal
->GetIsNullPrincipal()) {
747 auto* basePrin
= BasePrincipal::Cast(topWindowPrincipal
);
748 bool isThirdParty
= true;
750 // For about:blank and about:srcdoc, we can't just compare uri to determine
751 // whether the page is third-party, so we use channel result principal
752 // instead. By doing this, an the resource inherits the principal from
753 // its parent is considered not a third-party.
754 if (NS_IsAboutBlank(uri
) || NS_IsAboutSrcdoc(uri
)) {
755 nsIScriptSecurityManager
* ssm
= nsContentUtils::GetSecurityManager();
756 if (NS_WARN_IF(!ssm
)) {
760 nsCOMPtr
<nsIPrincipal
> principal
;
762 ssm
->GetChannelResultPrincipal(aChannel
, getter_AddRefs(principal
));
763 if (NS_WARN_IF(NS_FAILED(rv
))) {
767 basePrin
->IsThirdPartyPrincipal(principal
, &isThirdParty
);
769 basePrin
->IsThirdPartyURI(uri
, &isThirdParty
);
772 loadInfo
->SetIsThirdPartyContextToTopWindow(isThirdParty
);
777 bool AntiTrackingUtils::IsThirdPartyChannel(nsIChannel
* aChannel
) {
778 MOZ_ASSERT(aChannel
);
780 // We only care whether the channel is 3rd-party with respect to
782 nsCOMPtr
<nsILoadInfo
> loadInfo
= aChannel
->LoadInfo();
783 return loadInfo
->GetIsThirdPartyContextToTopWindow();
787 bool AntiTrackingUtils::IsThirdPartyWindow(nsPIDOMWindowInner
* aWindow
,
791 // We assume that the window is foreign to the URI by default.
792 bool thirdParty
= true;
794 // We will skip checking URIs for about:blank and about:srcdoc because they
795 // have no domain. So, comparing them will always fail.
796 if (aURI
&& !NS_IsAboutBlank(aURI
) && !NS_IsAboutSrcdoc(aURI
)) {
797 nsCOMPtr
<nsIScriptObjectPrincipal
> scriptObjPrin
=
798 do_QueryInterface(aWindow
);
799 if (!scriptObjPrin
) {
803 nsCOMPtr
<nsIPrincipal
> prin
= scriptObjPrin
->GetPrincipal();
808 // Determine whether aURI is foreign with respect to the current principal.
809 nsresult rv
= prin
->IsThirdPartyURI(aURI
, &thirdParty
);
819 RefPtr
<Document
> doc
= aWindow
->GetDoc();
821 // If we can't get the document from the window, ex, about:blank, fallback
822 // to use IsThirdPartyWindow check that examine the whole hierarchy.
823 nsCOMPtr
<mozIThirdPartyUtil
> thirdPartyUtil
=
824 components::ThirdPartyUtil::Service();
825 Unused
<< thirdPartyUtil
->IsThirdPartyWindow(aWindow
->GetOuterWindow(),
826 nullptr, &thirdParty
);
830 return IsThirdPartyDocument(doc
);
834 bool AntiTrackingUtils::IsThirdPartyDocument(Document
* aDocument
) {
835 MOZ_ASSERT(aDocument
);
836 if (!aDocument
->GetChannel()) {
837 // If we can't get the channel from the document, i.e. initial about:blank
838 // page, we use the browsingContext of the document to check if it's in the
839 // third-party context. If the browsing context is still not available, we
840 // will treat the window as third-party.
841 RefPtr
<BrowsingContext
> bc
= aDocument
->GetBrowsingContext();
842 return bc
? IsThirdPartyContext(bc
) : true;
845 // We only care whether the channel is 3rd-party with respect to
847 nsCOMPtr
<nsILoadInfo
> loadInfo
= aDocument
->GetChannel()->LoadInfo();
848 return loadInfo
->GetIsThirdPartyContextToTopWindow();
852 bool AntiTrackingUtils::IsThirdPartyContext(BrowsingContext
* aBrowsingContext
) {
853 MOZ_ASSERT(aBrowsingContext
);
854 MOZ_ASSERT(aBrowsingContext
->IsInProcess());
856 if (aBrowsingContext
->IsTopContent()) {
860 // If the top browsing context is not in the same process, it's cross-origin.
861 if (!aBrowsingContext
->Top()->IsInProcess()) {
865 nsIDocShell
* docShell
= aBrowsingContext
->GetDocShell();
869 Document
* doc
= docShell
->GetExtantDocument();
873 nsIPrincipal
* principal
= doc
->NodePrincipal();
875 nsIDocShell
* topDocShell
= aBrowsingContext
->Top()->GetDocShell();
879 Document
* topDoc
= topDocShell
->GetDocument();
883 nsIPrincipal
* topPrincipal
= topDoc
->NodePrincipal();
885 auto* topBasePrin
= BasePrincipal::Cast(topPrincipal
);
886 bool isThirdParty
= true;
888 topBasePrin
->IsThirdPartyPrincipal(principal
, &isThirdParty
);
894 nsCString
AntiTrackingUtils::GrantedReasonToString(
895 ContentBlockingNotifier::StorageAccessPermissionGrantedReason aReason
) {
897 case ContentBlockingNotifier::eOpener
:
899 case ContentBlockingNotifier::eOpenerAfterUserInteraction
:
900 return "user interaction"_ns
;
902 return "stroage access API"_ns
;
907 void AntiTrackingUtils::UpdateAntiTrackingInfoForChannel(nsIChannel
* aChannel
) {
908 MOZ_ASSERT(aChannel
);
910 if (!XRE_IsParentProcess()) {
914 MOZ_DIAGNOSTIC_ASSERT(XRE_IsParentProcess());
916 AntiTrackingUtils::ComputeIsThirdPartyToTopWindow(aChannel
);
918 nsCOMPtr
<nsILoadInfo
> loadInfo
= aChannel
->LoadInfo();
920 Unused
<< loadInfo
->SetStoragePermission(
921 AntiTrackingUtils::GetStoragePermissionStateInParent(aChannel
));
923 // We only update the IsOnContentBlockingAllowList flag and the partition key
924 // for the top-level http channel.
926 // The IsOnContentBlockingAllowList is only for http. For other types of
927 // channels, such as 'file:', there will be no interface to modify this. So,
928 // we only update it in http channels.
930 // The partition key is computed based on the site, so it's no point to set it
931 // for channels other than http channels.
932 nsCOMPtr
<nsIHttpChannel
> httpChannel
= do_QueryInterface(aChannel
);
933 if (!httpChannel
|| loadInfo
->GetExternalContentPolicyType() !=
934 ExtContentPolicy::TYPE_DOCUMENT
) {
938 nsCOMPtr
<nsICookieJarSettings
> cookieJarSettings
;
939 Unused
<< loadInfo
->GetCookieJarSettings(getter_AddRefs(cookieJarSettings
));
941 // Update the IsOnContentBlockingAllowList flag in the CookieJarSettings
942 // if this is a top level loading. For sub-document loading, this flag
943 // would inherit from the parent.
944 net::CookieJarSettings::Cast(cookieJarSettings
)
945 ->UpdateIsOnContentBlockingAllowList(aChannel
);
947 // We only need to set FPD for top-level loads. FPD will automatically be
948 // propagated to non-top level loads via CookieJarSetting.
949 nsCOMPtr
<nsIURI
> uri
;
950 Unused
<< aChannel
->GetURI(getter_AddRefs(uri
));
951 net::CookieJarSettings::Cast(cookieJarSettings
)->SetPartitionKey(uri
);
953 // Generate the fingerprinting randomization key for top-level loads. The key
954 // will automatically be propagated to sub loads.
955 auto RFPRandomKey
= nsRFPService::GenerateKey(aChannel
);
957 net::CookieJarSettings::Cast(cookieJarSettings
)
958 ->SetFingerprintingRandomizationKey(RFPRandomKey
.ref());