Bug 1917491 - Part 3: Introduce call-like syntax for resource disposal in DisposableS...
[gecko.git] / toolkit / components / antitracking / AntiTrackingUtils.cpp
blob25e891e8b71fded13667d70fff58e8d3faeda2d3
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"
29 #include "nsIURI.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();
49 if (!outer) {
50 return nullptr;
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();
60 if (!document) {
61 return nullptr;
64 nsIChannel* channel = document->GetChannel();
65 if (!channel) {
66 return nullptr;
69 nsCOMPtr<nsPIDOMWindowOuter> pwin =
70 aWindow->GetBrowsingContext()->Top()->GetDOMWindow();
72 if (!pwin) {
73 return nullptr;
76 return pwin.forget();
79 /* static */
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))) {
86 return nullptr;
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))) {
95 return nullptr;
98 return uriBeingLoaded.forget();
101 // static
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);
114 // static
115 bool AntiTrackingUtils::CreateStoragePermissionKey(nsIPrincipal* aPrincipal,
116 nsACString& aKey) {
117 if (!aPrincipal) {
118 return false;
121 nsAutoCString origin;
122 nsresult rv = aPrincipal->GetOriginNoSuffix(origin);
123 if (NS_WARN_IF(NS_FAILED(rv))) {
124 return false;
127 CreateStoragePermissionKey(origin, aKey);
128 return true;
131 // static
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);
144 // static
145 bool AntiTrackingUtils::CreateStorageFramePermissionKey(
146 nsIPrincipal* aPrincipal, nsACString& aKey) {
147 MOZ_ASSERT(aPrincipal);
149 nsAutoCString site;
150 nsresult rv = aPrincipal->GetSiteOriginNoSuffix(site);
151 if (NS_WARN_IF(NS_FAILED(rv))) {
152 return false;
155 CreateStorageFramePermissionKey(site, aKey);
156 return true;
159 // static
160 bool AntiTrackingUtils::CreateStorageRequestPermissionKey(
161 nsIURI* aURI, nsACString& aPermissionKey) {
162 MOZ_ASSERT(aPermissionKey.IsEmpty());
163 RefPtr<nsEffectiveTLDService> eTLDService =
164 nsEffectiveTLDService::GetInstance();
165 if (!eTLDService) {
166 return false;
168 nsCString site;
169 nsresult rv = eTLDService->GetSite(aURI, site);
170 if (NS_FAILED(rv)) {
171 return false;
173 static const nsLiteralCString prefix =
174 nsLiteralCString("AllowStorageAccessRequest^");
175 aPermissionKey.SetCapacity(prefix.Length() + site.Length());
176 aPermissionKey.Append(prefix);
177 aPermissionKey.Append(site);
178 return true;
181 // static
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)) {
200 return false;
203 nsAutoCString type;
204 nsresult rv = aPermission->GetType(type);
205 if (NS_WARN_IF(NS_FAILED(rv))) {
206 return false;
209 return StringBeginsWith(type, permissionKey);
212 // static
213 Maybe<size_t> AntiTrackingUtils::CountSitesAllowStorageAccess(
214 nsIPrincipal* aPrincipal) {
215 PermissionManager* permManager = PermissionManager::GetInstance();
216 if (NS_WARN_IF(!permManager)) {
217 return Nothing();
220 nsAutoCString prefix;
221 AntiTrackingUtils::CreateStoragePermissionKey(aPrincipal, prefix);
222 nsAutoCString framePrefix;
223 AntiTrackingUtils::CreateStorageFramePermissionKey(aPrincipal, framePrefix);
225 using Permissions = nsTArray<RefPtr<nsIPermission>>;
226 Permissions perms;
227 nsresult rv = permManager->GetAllWithTypePrefix(prefix, perms);
228 if (NS_WARN_IF(NS_FAILED(rv))) {
229 return Nothing();
231 Permissions framePermissions;
232 rv = permManager->GetAllWithTypePrefix(framePrefix, framePermissions);
233 if (NS_WARN_IF(NS_FAILED(rv))) {
234 return Nothing();
236 if (!perms.AppendElements(framePermissions, fallible)) {
237 return Nothing();
240 // Create a set of unique origins
241 using Sites = nsTArray<nsCString>;
242 Sites sites;
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) {
250 nsAutoCString type;
251 rv = perm->GetType(type);
252 if (NS_WARN_IF(NS_FAILED(rv))) {
253 return Nothing();
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) {
260 continue;
263 nsCOMPtr<nsIPrincipal> principal;
264 rv = perm->GetPrincipal(getter_AddRefs(principal));
265 if (NS_WARN_IF(NS_FAILED(rv))) {
266 return Nothing();
269 nsAutoCString site;
270 rv = principal->GetSiteOrigin(site);
271 if (NS_WARN_IF(NS_FAILED(rv))) {
272 return Nothing();
275 ToLowerCase(site);
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());
286 // static
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"));
295 return false;
298 uint32_t result = 0;
299 if (aIsInPrivateBrowsing) {
300 LOG_PRIN(("Querying the permissions for private modei looking for a "
301 "permission of type %s for %s",
302 aType.get(), _spec),
303 aPrincipal);
304 if (!permManager->PermissionAvailable(aPrincipal, aType)) {
305 LOG(
306 ("Permission isn't available for this principal in the current "
307 "process"));
308 return false;
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"));
314 return false;
317 bool found = false;
318 for (const auto& permission : permissions) {
319 if (!permission) {
320 LOG(("Couldn't get the permission for unknown reasons"));
321 continue;
324 nsAutoCString permissionType;
325 if (NS_SUCCEEDED(permission->GetType(permissionType)) &&
326 permissionType != aType) {
327 LOG(("Non-matching permission type: %s", aType.get()));
328 continue;
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));
335 continue;
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));
342 continue;
345 int64_t expirationTime = 0;
346 if (NS_SUCCEEDED(permission->GetExpireTime(&expirationTime)) &&
347 expirationTime != 0) {
348 LOG(("Non-matching permission expiration time: %" PRId64,
349 expirationTime));
350 continue;
353 LOG(("Found a matching permission"));
354 found = true;
355 break;
358 if (!found) {
359 if (aRejectedReason) {
360 *aRejectedReason = aBlockedReason;
362 return false;
364 } else {
365 nsresult rv = permManager->TestPermissionWithoutDefaultsFromPrincipal(
366 aPrincipal, aType, &result);
367 if (NS_WARN_IF(NS_FAILED(rv))) {
368 LOG(("Failed to test the permission"));
369 return false;
372 LOG_PRIN(
373 ("Testing permission type %s for %s resulted in %d (%s)", aType.get(),
374 _spec, int(result),
375 result == nsIPermissionManager::ALLOW_ACTION ? "success" : "failure"),
376 aPrincipal);
378 if (result != nsIPermissionManager::ALLOW_ACTION) {
379 if (aRejectedReason) {
380 *aRejectedReason = aBlockedReason;
382 return false;
386 return true;
389 /* static */
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) {
419 *aResult = access;
420 return NS_OK;
423 uint32_t frameAccess = nsIPermissionManager::UNKNOWN_ACTION;
424 rv = permMgr->TestPermissionFromPrincipal(
425 aTopPrincipal, requestFramePermissionKey, &frameAccess);
426 NS_ENSURE_SUCCESS(rv, rv);
428 *aResult = frameAccess;
429 return NS_OK;
432 /* static */
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;
450 nsresult rv =
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();
485 } else {
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);
493 if (httpChannel) {
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;
544 nsAutoCString type;
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();
556 if (!wc) {
557 return nsILoadInfo::NoStoragePermission;
559 WindowGlobalParent* wgp = wc->Canonical();
560 if (!wgp) {
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;
578 rv =
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);
599 if (NS_FAILED(rv)) {
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()) {
630 return 0;
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()) {
639 return 0;
642 return aBrowsingContext->Top()->GetCurrentInnerWindowId();
645 uint64_t AntiTrackingUtils::GetTopLevelStorageAreaWindowId(
646 BrowsingContext* aBrowsingContext) {
647 MOZ_ASSERT(aBrowsingContext);
649 if (Document::StorageAccessSandboxed(aBrowsingContext->GetSandboxFlags())) {
650 return 0;
653 BrowsingContext* parentBC = aBrowsingContext->GetParent();
654 if (!parentBC) {
655 // No parent browsing context available!
656 return 0;
659 if (!parentBC->IsTop()) {
660 return 0;
663 return parentBC->GetCurrentInnerWindowId();
666 /* static */
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)) {
680 return nullptr;
683 nsPIDOMWindowInner* inner = outer->GetCurrentInnerWindow();
684 if (NS_WARN_IF(!inner)) {
685 return nullptr;
688 principal = nsGlobalWindowInner::Cast(inner)->GetPrincipal();
689 } else {
690 WindowGlobalParent* wgp =
691 aBrowsingContext->Canonical()->GetCurrentWindowGlobal();
692 if (NS_WARN_IF(!wgp)) {
693 return nullptr;
696 principal = wgp->DocumentPrincipal();
698 return principal.forget();
701 /* static */
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)) {
715 return false;
718 nsresult rv = principal->GetOriginNoSuffix(aTrackingOrigin);
719 if (NS_WARN_IF(NS_FAILED(rv))) {
720 return false;
723 if (aPrincipal) {
724 principal.forget(aPrincipal);
727 return true;
730 /* static */
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();
743 /* static */
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.
762 if (uri &&
763 BasePrincipal::Cast(parentPrincipal)->AddonAllowsLoad(uri, true)) {
764 break;
767 bc = parentBC;
768 prev = parent;
770 if (!prev) {
771 prev = bc->GetCurrentWindowGlobal();
773 return prev.forget();
776 /* static */
777 nsresult AntiTrackingUtils::IsThirdPartyToPartitionKeySite(
778 nsIChannel* aChannel, const nsCOMPtr<nsIURI>& aURI, bool* aIsThirdParty) {
779 MOZ_ASSERT(XRE_IsParentProcess());
780 NS_ENSURE_ARG(aChannel);
781 NS_ENSURE_ARG(aURI);
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;
793 int32_t _unused1;
794 bool _unused2;
795 if (!OriginAttributes::ParsePartitionKey(partitionKey, scheme, host, _unused1,
796 _unused2)) {
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,
810 aIsThirdParty);
813 /* static */
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);
827 return;
830 RefPtr<BrowsingContext> bc;
831 loadInfo->GetBrowsingContext(getter_AddRefs(bc));
832 if (!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
841 // worker script.
842 if (!bc) {
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.
848 // redirects.
849 if (static_cast<net::LoadInfo*>(loadInfo.get())
850 ->HasIsThirdPartyContextToTopWindowSet()) {
851 return;
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.
862 if (uri) {
863 if (!principal) {
864 bool isThirdParty = true;
865 nsresult rv =
866 IsThirdPartyToPartitionKeySite(aChannel, uri, &isThirdParty);
867 if (NS_SUCCEEDED(rv)) {
868 loadInfo->SetIsThirdPartyContextToTopWindow(isThirdParty);
869 return;
872 principal = BasePrincipal::Cast(loadInfo->GetLoadingPrincipal());
874 if (principal) {
875 bool isThirdParty = true;
876 nsresult rv = principal->IsThirdPartyURI(uri, &isThirdParty);
878 if (NS_SUCCEEDED(rv)) {
879 loadInfo->SetIsThirdPartyContextToTopWindow(isThirdParty);
882 return;
885 RefPtr<WindowGlobalParent> topWindow =
886 GetTopWindowExcludingExtensionAccessibleContentFrames(bc->Canonical(),
887 uri);
889 if (NS_WARN_IF(!topWindow)) {
890 return;
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)) {
906 return;
909 nsCOMPtr<nsIPrincipal> principal;
910 nsresult rv =
911 ssm->GetChannelResultPrincipal(aChannel, getter_AddRefs(principal));
912 if (NS_WARN_IF(NS_FAILED(rv))) {
913 return;
916 basePrin->IsThirdPartyPrincipal(principal, &isThirdParty);
917 } else {
918 basePrin->IsThirdPartyURI(uri, &isThirdParty);
921 loadInfo->SetIsThirdPartyContextToTopWindow(isThirdParty);
925 /* static */
926 bool AntiTrackingUtils::IsThirdPartyChannel(nsIChannel* aChannel) {
927 MOZ_ASSERT(aChannel);
929 nsCOMPtr<mozIThirdPartyUtil> tpuService =
930 mozilla::components::ThirdPartyUtil::Service();
931 if (!tpuService) {
932 return true;
934 bool thirdParty = true;
935 nsresult rv = tpuService->IsThirdPartyChannel(aChannel, nullptr, &thirdParty);
936 if (NS_FAILED(rv)) {
937 return true;
939 return thirdParty;
942 /* static */
943 bool AntiTrackingUtils::IsThirdPartyWindow(nsPIDOMWindowInner* aWindow,
944 nsIURI* aURI) {
945 MOZ_ASSERT(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) {
956 return thirdParty;
959 nsCOMPtr<nsIPrincipal> prin = scriptObjPrin->GetPrincipal();
960 if (!prin) {
961 return thirdParty;
964 // Determine whether aURI is foreign with respect to the current principal.
965 nsresult rv = prin->IsThirdPartyURI(aURI, &thirdParty);
966 if (NS_FAILED(rv)) {
967 return thirdParty;
970 if (thirdParty) {
971 return thirdParty;
975 RefPtr<Document> doc = aWindow->GetDoc();
976 if (!doc) {
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);
983 return thirdParty;
986 return IsThirdPartyDocument(doc);
989 /* static */
990 bool AntiTrackingUtils::IsThirdPartyDocument(Document* aDocument) {
991 MOZ_ASSERT(aDocument);
992 nsCOMPtr<mozIThirdPartyUtil> tpuService =
993 mozilla::components::ThirdPartyUtil::Service();
994 if (!tpuService) {
995 return true;
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)) {
1010 return true;
1012 return thirdParty;
1015 /* static */
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();
1023 if (!docShell) {
1024 return true;
1026 Document* doc = docShell->GetExtantDocument();
1027 if (!doc || doc->GetSandboxFlags() & SANDBOXED_ORIGIN) {
1028 return true;
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
1035 // cross-origin.
1036 if (!traversingParent->IsInProcess()) {
1037 return true;
1040 nsIDocShell* parentDocShell = traversingParent->GetDocShell();
1041 if (!parentDocShell) {
1042 return true;
1044 Document* parentDoc = parentDocShell->GetDocument();
1045 if (!parentDoc || parentDoc->GetSandboxFlags() & SANDBOXED_ORIGIN) {
1046 return true;
1048 nsIPrincipal* parentPrincipal = parentDoc->NodePrincipal();
1050 auto* parentBasePrin = BasePrincipal::Cast(parentPrincipal);
1051 bool isThirdParty = true;
1053 parentBasePrin->IsThirdPartyPrincipal(principal, &isThirdParty);
1054 if (isThirdParty) {
1055 return true;
1058 traversingParent = traversingParent->GetParent();
1060 return false;
1063 /* static */
1064 nsCString AntiTrackingUtils::GrantedReasonToString(
1065 ContentBlockingNotifier::StorageAccessPermissionGrantedReason aReason) {
1066 switch (aReason) {
1067 case ContentBlockingNotifier::eOpener:
1068 return "opener"_ns;
1069 case ContentBlockingNotifier::eOpenerAfterUserInteraction:
1070 return "user interaction"_ns;
1071 default:
1072 return "stroage access API"_ns;
1076 /* static */
1077 void AntiTrackingUtils::UpdateAntiTrackingInfoForChannel(nsIChannel* aChannel) {
1078 MOZ_ASSERT(aChannel);
1080 if (!XRE_IsParentProcess()) {
1081 return;
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
1095 // protections.
1096 Maybe<RFPTarget> overriddenFingerprintingSettings =
1097 nsRFPService::GetOverriddenFingerprintingSettingsForChannel(aChannel);
1099 if (overriddenFingerprintingSettings) {
1100 loadInfo->SetOverriddenFingerprintingSettings(
1101 overriddenFingerprintingSettings.ref());
1103 #ifdef DEBUG
1104 static_cast<mozilla::net::LoadInfo*>(loadInfo.get())
1105 ->MarkOverriddenFingerprintingSettingsAsSet();
1106 #endif
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
1112 // here.
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) {
1128 return;
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);
1146 if (RFPRandomKey) {
1147 net::CookieJarSettings::Cast(cookieJarSettings)
1148 ->SetFingerprintingRandomizationKey(RFPRandomKey.ref());