Backed out 6 changesets (bug 1835907) for causing multiple failures. CLOSED TREE
[gecko.git] / toolkit / components / antitracking / AntiTrackingUtils.cpp
blobe4f578a898698aa45ef588e0b1624f1571e9ab18
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"
28 #include "nsIURI.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();
47 if (!outer) {
48 return nullptr;
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();
58 if (!document) {
59 return nullptr;
62 nsIChannel* channel = document->GetChannel();
63 if (!channel) {
64 return nullptr;
67 nsCOMPtr<nsPIDOMWindowOuter> pwin =
68 aWindow->GetBrowsingContext()->Top()->GetDOMWindow();
70 if (!pwin) {
71 return nullptr;
74 return pwin.forget();
77 /* static */
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))) {
84 return nullptr;
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))) {
93 return nullptr;
96 return uriBeingLoaded.forget();
99 // static
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);
112 // static
113 bool AntiTrackingUtils::CreateStoragePermissionKey(nsIPrincipal* aPrincipal,
114 nsACString& aKey) {
115 if (!aPrincipal) {
116 return false;
119 nsAutoCString origin;
120 nsresult rv = aPrincipal->GetOriginNoSuffix(origin);
121 if (NS_WARN_IF(NS_FAILED(rv))) {
122 return false;
125 CreateStoragePermissionKey(origin, aKey);
126 return true;
129 // static
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);
142 // static
143 bool AntiTrackingUtils::CreateStorageFramePermissionKey(
144 nsIPrincipal* aPrincipal, nsACString& aKey) {
145 MOZ_ASSERT(aPrincipal);
147 nsAutoCString site;
148 nsresult rv = aPrincipal->GetSiteOriginNoSuffix(site);
149 if (NS_WARN_IF(NS_FAILED(rv))) {
150 return false;
153 CreateStorageFramePermissionKey(site, aKey);
154 return true;
157 // static
158 bool AntiTrackingUtils::CreateStorageRequestPermissionKey(
159 nsIURI* aURI, nsACString& aPermissionKey) {
160 MOZ_ASSERT(aPermissionKey.IsEmpty());
161 RefPtr<nsEffectiveTLDService> eTLDService =
162 nsEffectiveTLDService::GetInstance();
163 if (!eTLDService) {
164 return false;
166 nsCString site;
167 nsresult rv = eTLDService->GetSite(aURI, site);
168 if (NS_FAILED(rv)) {
169 return false;
171 static const nsLiteralCString prefix =
172 nsLiteralCString("AllowStorageAccessRequest^");
173 aPermissionKey.SetCapacity(prefix.Length() + site.Length());
174 aPermissionKey.Append(prefix);
175 aPermissionKey.Append(site);
176 return true;
179 // static
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)) {
198 return false;
201 nsAutoCString type;
202 nsresult rv = aPermission->GetType(type);
203 if (NS_WARN_IF(NS_FAILED(rv))) {
204 return false;
207 return StringBeginsWith(type, permissionKey);
210 // static
211 Maybe<size_t> AntiTrackingUtils::CountSitesAllowStorageAccess(
212 nsIPrincipal* aPrincipal) {
213 PermissionManager* permManager = PermissionManager::GetInstance();
214 if (NS_WARN_IF(!permManager)) {
215 return Nothing();
218 nsAutoCString prefix;
219 AntiTrackingUtils::CreateStoragePermissionKey(aPrincipal, prefix);
220 nsAutoCString framePrefix;
221 AntiTrackingUtils::CreateStorageFramePermissionKey(aPrincipal, framePrefix);
223 using Permissions = nsTArray<RefPtr<nsIPermission>>;
224 Permissions perms;
225 nsresult rv = permManager->GetAllWithTypePrefix(prefix, perms);
226 if (NS_WARN_IF(NS_FAILED(rv))) {
227 return Nothing();
229 Permissions framePermissions;
230 rv = permManager->GetAllWithTypePrefix(framePrefix, framePermissions);
231 if (NS_WARN_IF(NS_FAILED(rv))) {
232 return Nothing();
234 if (!perms.AppendElements(framePermissions, fallible)) {
235 return Nothing();
238 // Create a set of unique origins
239 using Sites = nsTArray<nsCString>;
240 Sites sites;
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) {
248 nsAutoCString type;
249 rv = perm->GetType(type);
250 if (NS_WARN_IF(NS_FAILED(rv))) {
251 return Nothing();
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) {
258 continue;
261 nsCOMPtr<nsIPrincipal> principal;
262 rv = perm->GetPrincipal(getter_AddRefs(principal));
263 if (NS_WARN_IF(NS_FAILED(rv))) {
264 return Nothing();
267 nsAutoCString site;
268 rv = principal->GetSiteOrigin(site);
269 if (NS_WARN_IF(NS_FAILED(rv))) {
270 return Nothing();
273 ToLowerCase(site);
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());
284 // static
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"));
293 return false;
296 uint32_t result = 0;
297 if (aIsInPrivateBrowsing) {
298 LOG_PRIN(("Querying the permissions for private modei looking for a "
299 "permission of type %s for %s",
300 aType.get(), _spec),
301 aPrincipal);
302 if (!permManager->PermissionAvailable(aPrincipal, aType)) {
303 LOG(
304 ("Permission isn't available for this principal in the current "
305 "process"));
306 return false;
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"));
312 return false;
315 bool found = false;
316 for (const auto& permission : permissions) {
317 if (!permission) {
318 LOG(("Couldn't get the permission for unknown reasons"));
319 continue;
322 nsAutoCString permissionType;
323 if (NS_SUCCEEDED(permission->GetType(permissionType)) &&
324 permissionType != aType) {
325 LOG(("Non-matching permission type: %s", aType.get()));
326 continue;
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));
333 continue;
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));
340 continue;
343 int64_t expirationTime = 0;
344 if (NS_SUCCEEDED(permission->GetExpireTime(&expirationTime)) &&
345 expirationTime != 0) {
346 LOG(("Non-matching permission expiration time: %" PRId64,
347 expirationTime));
348 continue;
351 LOG(("Found a matching permission"));
352 found = true;
353 break;
356 if (!found) {
357 if (aRejectedReason) {
358 *aRejectedReason = aBlockedReason;
360 return false;
362 } else {
363 nsresult rv = permManager->TestPermissionWithoutDefaultsFromPrincipal(
364 aPrincipal, aType, &result);
365 if (NS_WARN_IF(NS_FAILED(rv))) {
366 LOG(("Failed to test the permission"));
367 return false;
370 LOG_PRIN(
371 ("Testing permission type %s for %s resulted in %d (%s)", aType.get(),
372 _spec, int(result),
373 result == nsIPermissionManager::ALLOW_ACTION ? "success" : "failure"),
374 aPrincipal);
376 if (result != nsIPermissionManager::ALLOW_ACTION) {
377 if (aRejectedReason) {
378 *aRejectedReason = aBlockedReason;
380 return false;
384 return true;
387 /* static */
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;
405 nsresult rv =
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();
440 } else {
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);
448 if (httpChannel) {
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;
495 nsAutoCString type;
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
507 // browsing context.
508 if (!bc->IsTop()) {
509 RefPtr<nsEffectiveTLDService> etld = nsEffectiveTLDService::GetInstance();
510 if (!etld) {
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;
518 nsAutoCString type;
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()) {
538 return 0;
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()) {
547 return 0;
550 return aBrowsingContext->Top()->GetCurrentInnerWindowId();
553 uint64_t AntiTrackingUtils::GetTopLevelStorageAreaWindowId(
554 BrowsingContext* aBrowsingContext) {
555 MOZ_ASSERT(aBrowsingContext);
557 if (Document::StorageAccessSandboxed(aBrowsingContext->GetSandboxFlags())) {
558 return 0;
561 BrowsingContext* parentBC = aBrowsingContext->GetParent();
562 if (!parentBC) {
563 // No parent browsing context available!
564 return 0;
567 if (!parentBC->IsTop()) {
568 return 0;
571 return parentBC->GetCurrentInnerWindowId();
574 /* static */
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)) {
588 return nullptr;
591 nsPIDOMWindowInner* inner = outer->GetCurrentInnerWindow();
592 if (NS_WARN_IF(!inner)) {
593 return nullptr;
596 principal = nsGlobalWindowInner::Cast(inner)->GetPrincipal();
597 } else {
598 WindowGlobalParent* wgp =
599 aBrowsingContext->Canonical()->GetCurrentWindowGlobal();
600 if (NS_WARN_IF(!wgp)) {
601 return nullptr;
604 principal = wgp->DocumentPrincipal();
606 return principal.forget();
609 /* static */
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)) {
623 return false;
626 nsresult rv = principal->GetOriginNoSuffix(aTrackingOrigin);
627 if (NS_WARN_IF(NS_FAILED(rv))) {
628 return false;
631 if (aPrincipal) {
632 principal.forget(aPrincipal);
635 return true;
638 /* static */
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();
651 /* static */
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.
670 if (uri &&
671 BasePrincipal::Cast(parentPrincipal)->AddonAllowsLoad(uri, true)) {
672 break;
675 bc = parentBC;
676 prev = parent;
678 if (!prev) {
679 prev = bc->GetCurrentWindowGlobal();
681 return prev.forget();
684 /* static */
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);
698 return;
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
709 // worker script.
710 if (!bc) {
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.
716 // redirects.
717 if (static_cast<net::LoadInfo*>(loadInfo.get())
718 ->HasIsThirdPartyContextToTopWindowSet()) {
719 return;
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);
734 return;
737 RefPtr<WindowGlobalParent> topWindow =
738 GetTopWindowExcludingExtensionAccessibleContentFrames(bc->Canonical(),
739 uri);
741 if (NS_WARN_IF(!topWindow)) {
742 return;
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)) {
757 return;
760 nsCOMPtr<nsIPrincipal> principal;
761 nsresult rv =
762 ssm->GetChannelResultPrincipal(aChannel, getter_AddRefs(principal));
763 if (NS_WARN_IF(NS_FAILED(rv))) {
764 return;
767 basePrin->IsThirdPartyPrincipal(principal, &isThirdParty);
768 } else {
769 basePrin->IsThirdPartyURI(uri, &isThirdParty);
772 loadInfo->SetIsThirdPartyContextToTopWindow(isThirdParty);
776 /* static */
777 bool AntiTrackingUtils::IsThirdPartyChannel(nsIChannel* aChannel) {
778 MOZ_ASSERT(aChannel);
780 // We only care whether the channel is 3rd-party with respect to
781 // the top-level.
782 nsCOMPtr<nsILoadInfo> loadInfo = aChannel->LoadInfo();
783 return loadInfo->GetIsThirdPartyContextToTopWindow();
786 /* static */
787 bool AntiTrackingUtils::IsThirdPartyWindow(nsPIDOMWindowInner* aWindow,
788 nsIURI* aURI) {
789 MOZ_ASSERT(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) {
800 return thirdParty;
803 nsCOMPtr<nsIPrincipal> prin = scriptObjPrin->GetPrincipal();
804 if (!prin) {
805 return thirdParty;
808 // Determine whether aURI is foreign with respect to the current principal.
809 nsresult rv = prin->IsThirdPartyURI(aURI, &thirdParty);
810 if (NS_FAILED(rv)) {
811 return thirdParty;
814 if (thirdParty) {
815 return thirdParty;
819 RefPtr<Document> doc = aWindow->GetDoc();
820 if (!doc) {
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);
827 return thirdParty;
830 return IsThirdPartyDocument(doc);
833 /* static */
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
846 // the top-level.
847 nsCOMPtr<nsILoadInfo> loadInfo = aDocument->GetChannel()->LoadInfo();
848 return loadInfo->GetIsThirdPartyContextToTopWindow();
851 /* static */
852 bool AntiTrackingUtils::IsThirdPartyContext(BrowsingContext* aBrowsingContext) {
853 MOZ_ASSERT(aBrowsingContext);
854 MOZ_ASSERT(aBrowsingContext->IsInProcess());
856 if (aBrowsingContext->IsTopContent()) {
857 return false;
860 // If the top browsing context is not in the same process, it's cross-origin.
861 if (!aBrowsingContext->Top()->IsInProcess()) {
862 return true;
865 nsIDocShell* docShell = aBrowsingContext->GetDocShell();
866 if (!docShell) {
867 return true;
869 Document* doc = docShell->GetExtantDocument();
870 if (!doc) {
871 return true;
873 nsIPrincipal* principal = doc->NodePrincipal();
875 nsIDocShell* topDocShell = aBrowsingContext->Top()->GetDocShell();
876 if (!topDocShell) {
877 return true;
879 Document* topDoc = topDocShell->GetDocument();
880 if (!topDoc) {
881 return true;
883 nsIPrincipal* topPrincipal = topDoc->NodePrincipal();
885 auto* topBasePrin = BasePrincipal::Cast(topPrincipal);
886 bool isThirdParty = true;
888 topBasePrin->IsThirdPartyPrincipal(principal, &isThirdParty);
890 return isThirdParty;
893 /* static */
894 nsCString AntiTrackingUtils::GrantedReasonToString(
895 ContentBlockingNotifier::StorageAccessPermissionGrantedReason aReason) {
896 switch (aReason) {
897 case ContentBlockingNotifier::eOpener:
898 return "opener"_ns;
899 case ContentBlockingNotifier::eOpenerAfterUserInteraction:
900 return "user interaction"_ns;
901 default:
902 return "stroage access API"_ns;
906 /* static */
907 void AntiTrackingUtils::UpdateAntiTrackingInfoForChannel(nsIChannel* aChannel) {
908 MOZ_ASSERT(aChannel);
910 if (!XRE_IsParentProcess()) {
911 return;
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) {
935 return;
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);
956 if (RFPRandomKey) {
957 net::CookieJarSettings::Cast(cookieJarSettings)
958 ->SetFingerprintingRandomizationKey(RFPRandomKey.ref());