diff --git a/caps/BasePrincipal.cpp b/caps/BasePrincipal.cpp index af343c2337f9..6a03b541ece3 100644 --- a/caps/BasePrincipal.cpp +++ b/caps/BasePrincipal.cpp @@ -36,7 +36,7 @@ #include "prnetdb.h" #include "nsIURIFixup.h" #include "mozilla/dom/StorageUtils.h" -#include "mozilla/StorageAccess.h" +#include "mozilla/ContentBlocking.h" #include "nsPIDOMWindow.h" #include "nsIURIMutator.h" #include "mozilla/PermissionManager.h" @@ -785,7 +785,8 @@ BasePrincipal::HasFirstpartyStorageAccess(mozIDOMWindow* aCheckWindow, if (NS_FAILED(rv)) { return rv; } - *aOutAllowed = ShouldAllowAccessFor(win, uri, aRejectedReason); + *aOutAllowed = + ContentBlocking::ShouldAllowAccessFor(win, uri, aRejectedReason); return NS_OK; } diff --git a/docshell/base/nsDocShell.cpp b/docshell/base/nsDocShell.cpp index 5a1a642a56b8..3c634133e271 100644 --- a/docshell/base/nsDocShell.cpp +++ b/docshell/base/nsDocShell.cpp @@ -2743,9 +2743,29 @@ nsresult nsDocShell::SetDocLoaderParent(nsDocLoader* aParent) { mContentListener->SetParentContentListener(parentURIListener); } + // Inform windows when they're being removed from their parent. + if (!aParent) { + MaybeClearStorageAccessFlag(); + } + return NS_OK; } +void nsDocShell::MaybeClearStorageAccessFlag() { + if (mScriptGlobal) { + // Tell our window that the parent has now changed. + mScriptGlobal->ParentWindowChanged(); + + // Tell all of our children about the change recursively as well. + for (auto* childDocLoader : mChildList.ForwardRange()) { + nsCOMPtr child = do_QueryObject(childDocLoader); + if (child) { + static_cast(child.get())->MaybeClearStorageAccessFlag(); + } + } + } +} + void nsDocShell::MaybeRestoreWindowName() { if (!StaticPrefs::privacy_window_name_update_enabled()) { return; diff --git a/docshell/base/nsDocShell.h b/docshell/base/nsDocShell.h index 09f84b25ae9b..7326eb6d5c92 100644 --- a/docshell/base/nsDocShell.h +++ b/docshell/base/nsDocShell.h @@ -401,6 +401,9 @@ class nsDocShell final : public nsDocLoader, nsDocShellLoadState* aLoadState, mozilla::Maybe aCacheKey = mozilla::Nothing()); + // Clear the document's storage access flag if needed. + void MaybeClearStorageAccessFlag(); + void MaybeRestoreWindowName(); void StoreWindowNameToSHEntries(); diff --git a/dom/base/Document.cpp b/dom/base/Document.cpp index 5acd37f65836..4ed6caf154c5 100644 --- a/dom/base/Document.cpp +++ b/dom/base/Document.cpp @@ -49,6 +49,7 @@ #include "mozilla/Base64.h" #include "mozilla/BasePrincipal.h" #include "mozilla/CSSEnabledState.h" +#include "mozilla/ContentBlocking.h" #include "mozilla/ContentBlockingAllowList.h" #include "mozilla/ContentBlockingNotifier.h" #include "mozilla/ContentBlockingUserInteraction.h" @@ -95,7 +96,6 @@ #include "mozilla/PresShellInlines.h" #include "mozilla/PseudoStyleType.h" #include "mozilla/RefCountType.h" -#include "mozilla/RejectForeignAllowList.h" #include "mozilla/RelativeTo.h" #include "mozilla/RestyleManager.h" #include "mozilla/ReverseIterator.h" @@ -16425,7 +16425,7 @@ void Document::MaybeAllowStorageForOpenerAfterUserInteraction() { } // We don't care when the asynchronous work finishes here. - Unused << StorageAccessAPIHelper::AllowAccessFor( + Unused << ContentBlocking::AllowAccessFor( NodePrincipal(), openerBC, ContentBlockingNotifier::eOpenerAfterUserInteraction); } @@ -16834,7 +16834,7 @@ nsresult Document::HasStorageAccessSync(bool& aHasStorageAccess) { return NS_OK; } Maybe resultBecauseCookiesApproved = - StorageAccessAPIHelper::CheckCookiesPermittedDecidesStorageAccessAPI( + ContentBlocking::CheckCookiesPermittedDecidesStorageAccessAPI( CookieJarSettings(), NodePrincipal()); if (resultBecauseCookiesApproved.isSome()) { if (resultBecauseCookiesApproved.value()) { @@ -16849,19 +16849,9 @@ nsresult Document::HasStorageAccessSync(bool& aHasStorageAccess) { // Step 2: Check if the browser settings determine whether or not this // document has access to its unpartitioned cookies. bool isThirdPartyDocument = AntiTrackingUtils::IsThirdPartyDocument(this); - bool isOnRejectForeignAllowList = RejectForeignAllowList::Check(this); - bool isOnThirdPartySkipList = false; - if (mChannel) { - nsCOMPtr loadInfo = mChannel->LoadInfo(); - isOnThirdPartySkipList = loadInfo->GetStoragePermission() == - nsILoadInfo::StoragePermissionAllowListed; - } - bool isThirdPartyTracker = - nsContentUtils::IsThirdPartyTrackingResourceWindow(inner); Maybe resultBecauseBrowserSettings = - StorageAccessAPIHelper::CheckBrowserSettingsDecidesStorageAccessAPI( - CookieJarSettings(), isThirdPartyDocument, isOnRejectForeignAllowList, - isOnThirdPartySkipList, isThirdPartyTracker); + ContentBlocking::CheckBrowserSettingsDecidesStorageAccessAPI( + CookieJarSettings(), isThirdPartyDocument); if (resultBecauseBrowserSettings.isSome()) { if (resultBecauseBrowserSettings.value()) { aHasStorageAccess = true; @@ -16875,8 +16865,7 @@ nsresult Document::HasStorageAccessSync(bool& aHasStorageAccess) { // Step 3: Check if the location of this call (embedded, top level, same-site) // determines if cookies are permitted or not. Maybe resultBecauseCallContext = - StorageAccessAPIHelper::CheckCallingContextDecidesStorageAccessAPI(this, - false); + ContentBlocking::CheckCallingContextDecidesStorageAccessAPI(this, false); if (resultBecauseCallContext.isSome()) { if (resultBecauseCallContext.value()) { aHasStorageAccess = true; @@ -16890,8 +16879,7 @@ nsresult Document::HasStorageAccessSync(bool& aHasStorageAccess) { // Step 4: Check if the permissions for this document determine if if has // access or is denied cookies. Maybe resultBecausePreviousPermission = - StorageAccessAPIHelper::CheckExistingPermissionDecidesStorageAccessAPI( - this); + ContentBlocking::CheckExistingPermissionDecidesStorageAccessAPI(this); if (resultBecausePreviousPermission.isSome()) { if (resultBecausePreviousPermission.value()) { aHasStorageAccess = true; @@ -16901,6 +16889,7 @@ nsresult Document::HasStorageAccessSync(bool& aHasStorageAccess) { return NS_OK; } } + // If you get here, we default to not giving you permission. aHasStorageAccess = false; return NS_OK; @@ -16969,9 +16958,8 @@ RefPtr> Document::RequestStorageAccessAsyncHelper( // called later in CompleteAllowAccessFor inside of AllowAccessFor. auto performFinalChecks = [inner, self, principal, aHasUserInteraction]() { // Create the user prompt - RefPtr p = - new StorageAccessAPIHelper::StorageAccessFinalCheckPromise::Private( - __func__); + RefPtr p = + new ContentBlocking::StorageAccessFinalCheckPromise::Private(__func__); RefPtr sapr = StorageAccessPermissionRequest::Create( inner, principal, @@ -16979,7 +16967,7 @@ RefPtr> Document::RequestStorageAccessAsyncHelper( [p] { Telemetry::AccumulateCategorical( Telemetry::LABELS_STORAGE_ACCESS_API_UI::Allow); - p->Resolve(StorageAccessAPIHelper::eAllow, __func__); + p->Resolve(ContentBlocking::eAllow, __func__); }, // Block [p] { @@ -17029,10 +17017,10 @@ RefPtr> Document::RequestStorageAccessAsyncHelper( MOZ_ASSERT_IF(pr2 != PromptResult::Granted, pr2 == PromptResult::Denied); if (pr2 == PromptResult::Granted) { - StorageAccessAPIHelper::StorageAccessPromptChoices choice = - StorageAccessAPIHelper::eAllow; + ContentBlocking::StorageAccessPromptChoices choice = + ContentBlocking::eAllow; if (autoGrant) { - choice = StorageAccessAPIHelper::eAllowAutoGrant; + choice = ContentBlocking::eAllowAutoGrant; } if (!autoGrant) { p->Resolve(choice, __func__); @@ -17059,8 +17047,8 @@ RefPtr> Document::RequestStorageAccessAsyncHelper( }; // Try to allow access for the given principal. - return StorageAccessAPIHelper::AllowAccessFor(principal, aBrowsingContext, - aNotifier, performFinalChecks); + return ContentBlocking::AllowAccessFor(principal, aBrowsingContext, aNotifier, + performFinalChecks); } already_AddRefed Document::RequestStorageAccess( @@ -17076,33 +17064,12 @@ already_AddRefed Document::RequestStorageAccess( return nullptr; } - // Step 0: Check that we have user activation before proceeding to prevent - // rapid calls to the API to leak information. - if (!HasValidTransientUserGestureActivation()) { - // Report an error to the console for this case - nsContentUtils::ReportToConsole(nsIScriptError::errorFlag, - nsLiteralCString("requestStorageAccess"), - this, nsContentUtils::eDOM_PROPERTIES, - "RequestStorageAccessUserGesture"); - this->ConsumeTransientUserGestureActivation(); - promise->MaybeRejectWithUndefined(); - return promise.forget(); - } - - // Get a pointer to the inner window- We need this for convenience sake - RefPtr inner = this->GetInnerWindow(); - if (!inner) { - this->ConsumeTransientUserGestureActivation(); - promise->MaybeRejectWithUndefined(); - return promise.forget(); - } - // Step 1: Check if the principal calling this has a permission that lets // them use cookies or forbids them from using cookies. // This is outside of the spec of the StorageAccess API, but makes the return // values to have proper semantics. Maybe resultBecauseCookiesApproved = - StorageAccessAPIHelper::CheckCookiesPermittedDecidesStorageAccessAPI( + ContentBlocking::CheckCookiesPermittedDecidesStorageAccessAPI( CookieJarSettings(), NodePrincipal()); if (resultBecauseCookiesApproved.isSome()) { if (resultBecauseCookiesApproved.value()) { @@ -17120,19 +17087,9 @@ already_AddRefed Document::RequestStorageAccess( // This is outside of the spec of the StorageAccess API, but makes the return // values to have proper semantics. bool isThirdPartyDocument = AntiTrackingUtils::IsThirdPartyDocument(this); - bool isOnRejectForeignAllowList = RejectForeignAllowList::Check(this); - bool isOnThirdPartySkipList = false; - if (mChannel) { - nsCOMPtr loadInfo = mChannel->LoadInfo(); - isOnThirdPartySkipList = loadInfo->GetStoragePermission() == - nsILoadInfo::StoragePermissionAllowListed; - } - bool isThirdPartyTracker = - nsContentUtils::IsThirdPartyTrackingResourceWindow(inner); Maybe resultBecauseBrowserSettings = - StorageAccessAPIHelper::CheckBrowserSettingsDecidesStorageAccessAPI( - CookieJarSettings(), isThirdPartyDocument, isOnRejectForeignAllowList, - isOnThirdPartySkipList, isThirdPartyTracker); + ContentBlocking::CheckBrowserSettingsDecidesStorageAccessAPI( + CookieJarSettings(), isThirdPartyDocument); if (resultBecauseBrowserSettings.isSome()) { if (resultBecauseBrowserSettings.value()) { promise->MaybeResolveWithUndefined(); @@ -17147,8 +17104,7 @@ already_AddRefed Document::RequestStorageAccess( // Step 3: Check if the Document calling requestStorageAccess has anything to // gain from storage access. It should be embedded, non-null, etc. Maybe resultBecauseCallContext = - StorageAccessAPIHelper::CheckCallingContextDecidesStorageAccessAPI(this, - true); + ContentBlocking::CheckCallingContextDecidesStorageAccessAPI(this, true); if (resultBecauseCallContext.isSome()) { if (resultBecauseCallContext.value()) { promise->MaybeResolveWithUndefined(); @@ -17163,8 +17119,7 @@ already_AddRefed Document::RequestStorageAccess( // Step 4: Check if we already allowed or denied storage access for this // document's storage key. Maybe resultBecausePreviousPermission = - StorageAccessAPIHelper::CheckExistingPermissionDecidesStorageAccessAPI( - this); + ContentBlocking::CheckExistingPermissionDecidesStorageAccessAPI(this); if (resultBecausePreviousPermission.isSome()) { if (resultBecausePreviousPermission.value()) { promise->MaybeResolveWithUndefined(); @@ -17178,6 +17133,12 @@ already_AddRefed Document::RequestStorageAccess( // Get pointers to some objects that will be used in the async portion RefPtr bc = this->GetBrowsingContext(); + nsPIDOMWindowInner* inner = this->GetInnerWindow(); + if (!inner) { + this->ConsumeTransientUserGestureActivation(); + promise->MaybeRejectWithUndefined(); + return promise.forget(); + } RefPtr outer = nsGlobalWindowOuter::Cast(inner->GetOuterWindow()); if (!outer) { @@ -17199,12 +17160,19 @@ already_AddRefed Document::RequestStorageAccess( ContentBlockingNotifier::eStorageAccessAPI) ->Then( GetCurrentSerialEventTarget(), __func__, - [self, inner, promise] { - inner->SaveStorageAccessPermissionGranted(); + [self, outer, promise] { + // Step 10. Grant the document access to cookies and store + // that fact for + // the purposes of future calls to + // hasStorageAccess() and requestStorageAccess(). + outer->SetStorageAccessPermissionGranted(true); self->NotifyUserGestureActivation(); promise->MaybeResolveWithUndefined(); }, - [promise] { promise->MaybeRejectWithUndefined(); }); + [outer, promise] { + outer->SetStorageAccessPermissionGranted(false); + promise->MaybeRejectWithUndefined(); + }); return promise.forget(); } @@ -17222,19 +17190,6 @@ already_AddRefed Document::RequestStorageAccessForOrigin( return nullptr; } - // Step 0: Check that we have user activation before proceeding to prevent - // rapid calls to the API to leak information. - if (!HasValidTransientUserGestureActivation()) { - // Report an error to the console for this case - nsContentUtils::ReportToConsole(nsIScriptError::errorFlag, - nsLiteralCString("requestStorageAccess"), - this, nsContentUtils::eDOM_PROPERTIES, - "RequestStorageAccessUserGesture"); - this->ConsumeTransientUserGestureActivation(); - promise->MaybeRejectWithUndefined(); - return promise.forget(); - } - // Step 1: Check if the provided URI is different-site to this Document nsCOMPtr thirdPartyURI; nsresult rv = NS_NewURI(getter_AddRefs(thirdPartyURI), aThirdPartyOrigin); @@ -17248,12 +17203,9 @@ already_AddRefed Document::RequestStorageAccessForOrigin( aRv.Throw(rv); return nullptr; } - bool isOnRejectForeignAllowList = - RejectForeignAllowList::Check(thirdPartyURI); Maybe resultBecauseBrowserSettings = - StorageAccessAPIHelper::CheckBrowserSettingsDecidesStorageAccessAPI( - CookieJarSettings(), isThirdPartyDocument, isOnRejectForeignAllowList, - false, true); + ContentBlocking::CheckBrowserSettingsDecidesStorageAccessAPI( + CookieJarSettings(), isThirdPartyDocument); if (resultBecauseBrowserSettings.isSome()) { if (resultBecauseBrowserSettings.value()) { promise->MaybeResolveWithUndefined(); @@ -17266,8 +17218,8 @@ already_AddRefed Document::RequestStorageAccessForOrigin( // Step 2: Check that this Document is same-site to the top, and check that // we have user activation if we require it. - Maybe resultBecauseCallContext = StorageAccessAPIHelper:: - CheckSameSiteCallingContextDecidesStorageAccessAPI( + Maybe resultBecauseCallContext = + ContentBlocking::CheckSameSiteCallingContextDecidesStorageAccessAPI( this, aRequireUserActivation); if (resultBecauseCallContext.isSome()) { if (resultBecauseCallContext.value()) { @@ -17314,7 +17266,7 @@ already_AddRefed Document::RequestStorageAccessForOrigin( // permission, but this can't be done in this process. We needs the cookie // permission of the URL as if it were embedded on this page, so we need to // make this check in the ContentParent. - StorageAccessAPIHelper::AsyncCheckCookiesPermittedDecidesStorageAccessAPI( + ContentBlocking::AsyncCheckCookiesPermittedDecidesStorageAccessAPI( GetBrowsingContext(), principal) ->Then( GetCurrentSerialEventTarget(), __func__, @@ -17363,8 +17315,7 @@ already_AddRefed Document::RequestStorageAccessForOrigin( GetCurrentSerialEventTarget(), __func__, // If the previous handlers resolved, we should reinstate user // activation and resolve the promise we returned in Step 5. - [self, inner, promise] { - inner->SaveStorageAccessPermissionGranted(); + [self, promise] { self->NotifyUserGestureActivation(); promise->MaybeResolveWithUndefined(); }, @@ -17705,7 +17656,8 @@ nsIPrincipal* Document::EffectiveStoragePrincipal() const { // We use the lower-level ContentBlocking API here to ensure this // check doesn't send notifications. uint32_t rejectedReason = 0; - if (ShouldAllowAccessFor(inner, GetDocumentURI(), &rejectedReason)) { + if (ContentBlocking::ShouldAllowAccessFor(inner, GetDocumentURI(), + &rejectedReason)) { return mActiveStoragePrincipal = NodePrincipal(); } diff --git a/dom/base/Navigator.cpp b/dom/base/Navigator.cpp index 85f28cce26f3..b7da9e6a03bd 100644 --- a/dom/base/Navigator.cpp +++ b/dom/base/Navigator.cpp @@ -12,6 +12,7 @@ #include "nsPluginArray.h" #include "nsMimeTypeArray.h" #include "mozilla/Components.h" +#include "mozilla/ContentBlocking.h" #include "mozilla/ContentBlockingNotifier.h" #include "mozilla/MemoryReporting.h" #include "mozilla/dom/BodyExtractor.h" diff --git a/dom/base/ThirdPartyUtil.cpp b/dom/base/ThirdPartyUtil.cpp index b8c06368342f..a768a282bc1e 100644 --- a/dom/base/ThirdPartyUtil.cpp +++ b/dom/base/ThirdPartyUtil.cpp @@ -13,12 +13,12 @@ #include "mozilla/Assertions.h" #include "mozilla/BasePrincipal.h" #include "mozilla/ClearOnShutdown.h" +#include "mozilla/ContentBlocking.h" #include "mozilla/ContentBlockingNotifier.h" #include "mozilla/Logging.h" #include "mozilla/MacroForEach.h" #include "mozilla/Components.h" #include "mozilla/StaticPtr.h" -#include "mozilla/StorageAccess.h" #include "mozilla/TextUtils.h" #include "mozilla/Unused.h" #include "mozilla/dom/BrowsingContext.h" @@ -512,8 +512,8 @@ ThirdPartyUtil::AnalyzeChannel(nsIChannel* aChannel, bool aNotify, nsIURI* aURI, aRequireThirdPartyCheck ? result.contains(ThirdPartyAnalysis::IsForeign) : true; if (performStorageChecks && - ShouldAllowAccessFor(aChannel, aURI ? aURI : uri.get(), - aRejectedReason)) { + ContentBlocking::ShouldAllowAccessFor(aChannel, aURI ? aURI : uri.get(), + aRejectedReason)) { result += ThirdPartyAnalysis::IsStorageAccessPermissionGranted; } diff --git a/dom/base/nsFrameLoader.cpp b/dom/base/nsFrameLoader.cpp index 06b2c63e31ff..42974f0c3c06 100644 --- a/dom/base/nsFrameLoader.cpp +++ b/dom/base/nsFrameLoader.cpp @@ -750,6 +750,12 @@ nsresult nsFrameLoader::ReallyStartLoadingInternal() { return NS_OK; } + if (GetDocShell()) { + // If we already have a docshell, ensure that the docshell's storage access + // flag is cleared. + GetDocShell()->MaybeClearStorageAccessFlag(); + } + nsresult rv = MaybeCreateDocShell(); if (NS_FAILED(rv)) { return rv; @@ -1167,6 +1173,8 @@ void nsFrameLoader::Hide() { return; } + GetDocShell()->MaybeClearStorageAccessFlag(); + nsCOMPtr contentViewer; GetDocShell()->GetContentViewer(getter_AddRefs(contentViewer)); if (contentViewer) contentViewer->SetSticky(false); diff --git a/dom/base/nsGlobalWindowOuter.cpp b/dom/base/nsGlobalWindowOuter.cpp index 025f6accb270..683285fe968a 100644 --- a/dom/base/nsGlobalWindowOuter.cpp +++ b/dom/base/nsGlobalWindowOuter.cpp @@ -24,6 +24,7 @@ #include "nsISecureBrowserUI.h" #include "nsIWebProgressListener.h" #include "mozilla/AntiTrackingUtils.h" +#include "mozilla/ContentBlocking.h" #include "mozilla/dom/AutoPrintEventDispatcher.h" #include "mozilla/dom/BindingUtils.h" #include "mozilla/dom/BrowserChild.h" @@ -51,7 +52,6 @@ #include "mozilla/dom/WindowFeatures.h" // WindowFeatures #include "mozilla/dom/WindowProxyHolder.h" #include "mozilla/IntegerPrintfMacros.h" -#include "mozilla/StorageAccessAPIHelper.h" #include "nsBaseCommandController.h" #include "nsError.h" #include "nsICookieService.h" @@ -1310,6 +1310,7 @@ nsGlobalWindowOuter::nsGlobalWindowOuter(uint64_t aWindowID) mIsChrome(false), mAllowScriptsToClose(false), mTopLevelOuterContentWindow(false), + mStorageAccessPermissionGranted(false), mDelayedPrintUntilAfterLoad(false), mDelayedCloseForPrinting(false), mShouldDelayPrintUntilAfterLoad(false), @@ -2506,6 +2507,9 @@ nsresult nsGlobalWindowOuter::SetNewDocument(Document* aDocument, PreloadLocalStorage(); + mStorageAccessPermissionGranted = ContentBlocking::ShouldAllowAccessFor( + newInnerWindow, aDocument->GetDocumentURI(), nullptr); + // Do this here rather than in say the Document constructor, since // we need a WindowContext available. mDoc->InitUseCounters(); @@ -7134,8 +7138,8 @@ void nsGlobalWindowOuter::MaybeAllowStorageForOpenedWindow(nsIURI* aURI) { aURI, doc->NodePrincipal()->OriginAttributesRef()); // We don't care when the asynchronous work finishes here. - Unused << StorageAccessAPIHelper::AllowAccessFor( - principal, GetBrowsingContext(), ContentBlockingNotifier::eOpener); + Unused << ContentBlocking::AllowAccessFor(principal, GetBrowsingContext(), + ContentBlockingNotifier::eOpener); } //***************************************************************************** diff --git a/dom/base/nsGlobalWindowOuter.h b/dom/base/nsGlobalWindowOuter.h index e34b8b93a8d0..1a6a72b9261f 100644 --- a/dom/base/nsGlobalWindowOuter.h +++ b/dom/base/nsGlobalWindowOuter.h @@ -677,6 +677,11 @@ class nsGlobalWindowOuter final : public mozilla::dom::EventTarget, virtual bool IsInSyncOperation() override; + void ParentWindowChanged() { + // Reset our storage access permission flag when we get reparented. + mStorageAccessPermissionGranted = false; + } + public: double GetInnerWidthOuter(mozilla::ErrorResult& aError); @@ -871,6 +876,13 @@ class nsGlobalWindowOuter final : public mozilla::dom::EventTarget, bool IsInModalState(); + bool IsStorageAccessPermissionGranted() const { + return mStorageAccessPermissionGranted; + } + void SetStorageAccessPermissionGranted(bool aStorageAccessPermissionGranted) { + mStorageAccessPermissionGranted = aStorageAccessPermissionGranted; + } + // Convenience functions for the many methods that need to scale // from device to CSS pixels. This computes it with cached scale in // PresContext which may be not recent information of the widget. @@ -1087,6 +1099,9 @@ class nsGlobalWindowOuter final : public mozilla::dom::EventTarget, bool mTopLevelOuterContentWindow : 1; + // whether storage access has been granted to this frame. + bool mStorageAccessPermissionGranted : 1; + // Whether we've delayed a print until after load. bool mDelayedPrintUntilAfterLoad : 1; // Whether we've delayed a close() operation because there was a pending diff --git a/dom/ipc/BrowserChild.cpp b/dom/ipc/BrowserChild.cpp index 3d3365234073..fea4f1a4b411 100644 --- a/dom/ipc/BrowserChild.cpp +++ b/dom/ipc/BrowserChild.cpp @@ -939,6 +939,8 @@ mozilla::ipc::IPCResult BrowserChild::RecvLoadURL( } docShell->LoadURI(aLoadState, true); + nsDocShell::Cast(docShell)->MaybeClearStorageAccessFlag(); + CrashReporter::AnnotateCrashReport(CrashReporter::Annotation::URL, spec); return IPC_OK(); } diff --git a/dom/ipc/ContentChild.cpp b/dom/ipc/ContentChild.cpp index 650df9fd1860..01f1d260e908 100644 --- a/dom/ipc/ContentChild.cpp +++ b/dom/ipc/ContentChild.cpp @@ -18,6 +18,7 @@ #include "mozilla/Attributes.h" #include "mozilla/BackgroundHangMonitor.h" #include "mozilla/BenchmarkStorageChild.h" +#include "mozilla/ContentBlocking.h" #include "mozilla/FOGIPC.h" #include "GMPServiceChild.h" #include "Geolocation.h" @@ -48,7 +49,6 @@ #include "mozilla/StaticPrefs_dom.h" #include "mozilla/StaticPrefs_fission.h" #include "mozilla/StaticPrefs_media.h" -#include "mozilla/StorageAccessAPIHelper.h" #include "mozilla/TelemetryIPC.h" #include "mozilla/Unused.h" #include "mozilla/WebBrowserPersistDocumentChild.h" @@ -3621,8 +3621,8 @@ mozilla::ipc::IPCResult ContentChild::RecvOnAllowAccessFor( aReason) { MOZ_ASSERT(!aContext.IsNull(), "Browsing context cannot be null"); - StorageAccessAPIHelper::OnAllowAccessFor( - aContext.GetMaybeDiscarded(), aTrackingOrigin, aCookieBehavior, aReason); + ContentBlocking::OnAllowAccessFor(aContext.GetMaybeDiscarded(), + aTrackingOrigin, aCookieBehavior, aReason); return IPC_OK(); } diff --git a/dom/ipc/ContentParent.cpp b/dom/ipc/ContentParent.cpp index 1f96d9d1a2a1..3061f3add508 100644 --- a/dom/ipc/ContentParent.cpp +++ b/dom/ipc/ContentParent.cpp @@ -55,6 +55,7 @@ #include "gfxPlatformFontList.h" #include "mozilla/AppShutdown.h" #include "mozilla/AutoRestore.h" +#include "mozilla/ContentBlocking.h" #include "mozilla/BasePrincipal.h" #include "mozilla/BenchmarkStorageParent.h" #include "mozilla/ContentBlockingUserInteraction.h" @@ -82,7 +83,6 @@ #include "mozilla/StaticPrefs_fission.h" #include "mozilla/StaticPrefs_media.h" #include "mozilla/StaticPrefs_widget.h" -#include "mozilla/StorageAccessAPIHelper.h" #include "mozilla/StyleSheet.h" #include "mozilla/StyleSheetInlines.h" #include "mozilla/Telemetry.h" @@ -6406,17 +6406,18 @@ ContentParent::RecvStorageAccessPermissionGrantedForOrigin( aReason.value()); } - StorageAccessAPIHelper::SaveAccessForOriginOnParentProcess( + ContentBlocking::SaveAccessForOriginOnParentProcess( aTopLevelWindowId, aParentContext.get_canonical(), aTrackingPrincipal, aAllowMode) - ->Then(GetCurrentSerialEventTarget(), __func__, - [aResolver = std::move(aResolver)]( - StorageAccessAPIHelper::ParentAccessGrantPromise:: - ResolveOrRejectValue&& aValue) { - bool success = - aValue.IsResolve() && NS_SUCCEEDED(aValue.ResolveValue()); - aResolver(success); - }); + ->Then( + GetCurrentSerialEventTarget(), __func__, + [aResolver = std::move(aResolver)]( + ContentBlocking::ParentAccessGrantPromise::ResolveOrRejectValue&& + aValue) { + bool success = + aValue.IsResolve() && NS_SUCCEEDED(aValue.ResolveValue()); + aResolver(success); + }); return IPC_OK(); } @@ -6431,12 +6432,12 @@ mozilla::ipc::IPCResult ContentParent::RecvCompleteAllowAccessFor( return IPC_OK(); } - StorageAccessAPIHelper::CompleteAllowAccessFor( + ContentBlocking::CompleteAllowAccessFor( aParentContext.get_canonical(), aTopLevelWindowId, aTrackingPrincipal, aTrackingOrigin, aCookieBehavior, aReason, nullptr) ->Then(GetCurrentSerialEventTarget(), __func__, [aResolver = std::move(aResolver)]( - StorageAccessAPIHelper::StorageAccessPermissionGrantPromise:: + ContentBlocking::StorageAccessPermissionGrantPromise:: ResolveOrRejectValue&& aValue) { Maybe choice; if (aValue.IsResolve()) { @@ -6470,8 +6471,8 @@ mozilla::ipc::IPCResult ContentParent::RecvTestCookiePermissionDecided( nsCOMPtr cjs = wgp->CookieJarSettings(); Maybe result = - StorageAccessAPIHelper::CheckCookiesPermittedDecidesStorageAccessAPI( - cjs, aPrincipal); + ContentBlocking::CheckCookiesPermittedDecidesStorageAccessAPI(cjs, + aPrincipal); aResolver(result); return IPC_OK(); } diff --git a/dom/ipc/PContent.ipdl b/dom/ipc/PContent.ipdl index 8a510ae73d79..3e6e5d20b5a6 100644 --- a/dom/ipc/PContent.ipdl +++ b/dom/ipc/PContent.ipdl @@ -140,7 +140,7 @@ using mozilla::dom::PositionState from "mozilla/dom/MediaSession.h"; using mozilla::dom::ServiceWorkerShutdownState::Progress from "mozilla/dom/ServiceWorkerShutdownState.h"; using mozilla::ContentBlockingNotifier::StorageAccessPermissionGrantedReason from "mozilla/ContentBlockingNotifier.h"; using mozilla::ContentBlockingNotifier::BlockingDecision from "mozilla/ContentBlockingNotifier.h"; -using mozilla::StorageAccessAPIHelper::StorageAccessPromptChoices from "mozilla/StorageAccessAPIHelper.h"; +using mozilla::ContentBlocking::StorageAccessPromptChoices from "mozilla/ContentBlocking.h"; using JSActorMessageKind from "mozilla/dom/JSActor.h"; using JSActorMessageMeta from "mozilla/dom/PWindowGlobal.h"; using mozilla::PermissionDelegateHandler::DelegatedPermissionList from "mozilla/PermissionDelegateHandler.h"; diff --git a/dom/ipc/WindowGlobalChild.cpp b/dom/ipc/WindowGlobalChild.cpp index cfd03acd6ec8..0df7509eea14 100644 --- a/dom/ipc/WindowGlobalChild.cpp +++ b/dom/ipc/WindowGlobalChild.cpp @@ -479,6 +479,12 @@ WindowGlobalChild::RecvSaveStorageAccessPermissionGranted() { inner->SaveStorageAccessPermissionGranted(); } + nsCOMPtr outer = + nsPIDOMWindowOuter::GetFromCurrentInner(inner); + if (outer) { + nsGlobalWindowOuter::Cast(outer)->SetStorageAccessPermissionGranted(true); + } + return IPC_OK(); } diff --git a/dom/security/ReferrerInfo.cpp b/dom/security/ReferrerInfo.cpp index 1303522f5d4c..3b25121a66d1 100644 --- a/dom/security/ReferrerInfo.cpp +++ b/dom/security/ReferrerInfo.cpp @@ -20,13 +20,13 @@ #include "ReferrerInfo.h" #include "mozilla/BasePrincipal.h" +#include "mozilla/ContentBlocking.h" #include "mozilla/ContentBlockingAllowList.h" #include "mozilla/net/CookieJarSettings.h" #include "mozilla/net/HttpBaseChannel.h" #include "mozilla/dom/Document.h" #include "mozilla/dom/Element.h" #include "mozilla/StaticPrefs_network.h" -#include "mozilla/StorageAccess.h" #include "mozilla/StyleSheet.h" #include "mozilla/Telemetry.h" #include "nsIWebProgressListener.h" @@ -210,7 +210,8 @@ ReferrerPolicy ReferrerInfo::GetDefaultReferrerPolicy(nsIHttpChannel* aChannel, if (XRE_IsParentProcess() && cjs->GetRejectThirdPartyContexts()) { uint32_t rejectedReason = 0; thirdPartyTrackerIsolated = - !ShouldAllowAccessFor(aChannel, aURI, &rejectedReason) && + !ContentBlocking::ShouldAllowAccessFor(aChannel, aURI, + &rejectedReason) && rejectedReason != static_cast( nsIWebProgressListener::STATE_COOKIES_PARTITIONED_FOREIGN); diff --git a/image/ImageCacheKey.cpp b/image/ImageCacheKey.cpp index 47689cc7aa11..67819071bd36 100644 --- a/image/ImageCacheKey.cpp +++ b/image/ImageCacheKey.cpp @@ -8,6 +8,7 @@ #include #include "mozilla/AntiTrackingUtils.h" +#include "mozilla/ContentBlocking.h" #include "mozilla/HashFunctions.h" #include "mozilla/StorageAccess.h" #include "mozilla/StoragePrincipalHelper.h" @@ -16,7 +17,6 @@ #include "mozilla/dom/File.h" #include "mozilla/dom/ServiceWorkerManager.h" #include "mozilla/StaticPrefs_privacy.h" -#include "mozilla/StorageAccess.h" #include "nsContentUtils.h" #include "nsHashKeys.h" #include "nsLayoutUtils.h" @@ -153,8 +153,8 @@ nsCString ImageCacheKey::GetIsolationKey(Document* aDocument, nsIURI* aURI) { // this point. The best approach here is to be conservative: if we are sure // that the permission is granted, let's return 0. Otherwise, let's make a // unique image cache per the top-level document eTLD+1. - if (!ApproximateAllowAccessForWithoutChannel(aDocument->GetInnerWindow(), - aURI)) { + if (!ContentBlocking::ApproximateAllowAccessForWithoutChannel( + aDocument->GetInnerWindow(), aURI)) { // If we are here, the image is a 3rd-party resource loaded by a first-party // context. We can just use the document's base domain as the key because it // should be the same as the top-level document's base domain. diff --git a/netwerk/cookie/CookieCommons.cpp b/netwerk/cookie/CookieCommons.cpp index f97a9384bf70..5c6f6933b718 100644 --- a/netwerk/cookie/CookieCommons.cpp +++ b/netwerk/cookie/CookieCommons.cpp @@ -7,11 +7,11 @@ #include "CookieCommons.h" #include "CookieLogging.h" #include "CookieService.h" +#include "mozilla/ContentBlocking.h" #include "mozilla/ConsoleReportCollector.h" #include "mozilla/ContentBlockingNotifier.h" #include "mozilla/ScopeExit.h" #include "mozilla/StaticPrefs_network.h" -#include "mozilla/StorageAccess.h" #include "mozilla/dom/Document.h" #include "mozilla/dom/nsMixedContentBlocker.h" #include "mozilla/net/CookieJarSettings.h" @@ -386,7 +386,8 @@ already_AddRefed CookieCommons::CreateCookieFromDocument( if (aDocument->CookieJarSettings()->GetLimitForeignContexts() && !aHasExistingCookiesLambda(baseDomain, storagePrincipal->OriginAttributesRef()) && - !ShouldAllowAccessFor(innerWindow, principalURI, &dummyRejectedReason)) { + !ContentBlocking::ShouldAllowAccessFor(innerWindow, principalURI, + &dummyRejectedReason)) { return nullptr; } diff --git a/netwerk/protocol/http/ClassifierDummyChannel.cpp b/netwerk/protocol/http/ClassifierDummyChannel.cpp index 123111662953..ae90bdd36a4a 100644 --- a/netwerk/protocol/http/ClassifierDummyChannel.cpp +++ b/netwerk/protocol/http/ClassifierDummyChannel.cpp @@ -7,18 +7,19 @@ #include "ClassifierDummyChannel.h" +#include "mozilla/ContentBlocking.h" #include "mozilla/net/ClassifierDummyChannelChild.h" #include "mozilla/net/UrlClassifierCommon.h" #include "mozilla/dom/ContentChild.h" #include "mozilla/Preferences.h" #include "mozilla/StaticPrefs_privacy.h" -#include "mozilla/StorageAccess.h" #include "nsContentSecurityManager.h" #include "nsIChannel.h" #include "nsIURI.h" #include "nsProxyRelease.h" #include "nsQueryObject.h" #include "NeckoChild.h" +#include "mozilla/ContentBlocking.h" #include "nsIHttpChannel.h" #include "nsIStreamListener.h" @@ -63,7 +64,7 @@ ClassifierDummyChannel::StorageAllowed( return eAsyncNeeded; } - if (ShouldAllowAccessFor(httpChannel, uri, nullptr)) { + if (ContentBlocking::ShouldAllowAccessFor(httpChannel, uri, nullptr)) { return eStorageGranted; } diff --git a/netwerk/protocol/http/ClassifierDummyChannelChild.cpp b/netwerk/protocol/http/ClassifierDummyChannelChild.cpp index 8d0a7f0dd3f6..b352473db0e0 100644 --- a/netwerk/protocol/http/ClassifierDummyChannelChild.cpp +++ b/netwerk/protocol/http/ClassifierDummyChannelChild.cpp @@ -5,11 +5,11 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #include "ClassifierDummyChannelChild.h" +#include "mozilla/ContentBlocking.h" #include "mozilla/ipc/BackgroundUtils.h" #include "mozilla/ipc/URIUtils.h" #include "nsIURI.h" #include "mozilla/AntiTrackingUtils.h" -#include "mozilla/StorageAccess.h" #include "nsIHttpChannelInternal.h" #include "nsIHttpChannel.h" #include "mozilla/net/NeckoChannelParams.h" @@ -82,7 +82,8 @@ mozilla::ipc::IPCResult ClassifierDummyChannelChild::Recv__delete__( RefPtr httpChannel = do_QueryObject(channel); httpChannel->AddClassificationFlags(aClassificationFlags, mIsThirdParty); - bool storageGranted = ShouldAllowAccessFor(httpChannel, mURI, nullptr); + bool storageGranted = + ContentBlocking::ShouldAllowAccessFor(httpChannel, mURI, nullptr); mCallback(storageGranted); return IPC_OK(); } diff --git a/netwerk/protocol/http/nsHttpChannel.cpp b/netwerk/protocol/http/nsHttpChannel.cpp index 23e3f0e25f16..a098ddffd25b 100644 --- a/netwerk/protocol/http/nsHttpChannel.cpp +++ b/netwerk/protocol/http/nsHttpChannel.cpp @@ -59,6 +59,7 @@ #include "mozilla/AntiTrackingUtils.h" #include "mozilla/Attributes.h" #include "mozilla/BasePrincipal.h" +#include "mozilla/ContentBlocking.h" #include "mozilla/DebugOnly.h" #include "mozilla/Preferences.h" #include "mozilla/ProfilerLabels.h" diff --git a/toolkit/components/antitracking/AntiTrackingIPCUtils.h b/toolkit/components/antitracking/AntiTrackingIPCUtils.h index cbe9b1eef62b..abd0c41df618 100644 --- a/toolkit/components/antitracking/AntiTrackingIPCUtils.h +++ b/toolkit/components/antitracking/AntiTrackingIPCUtils.h @@ -10,7 +10,7 @@ #include "ipc/EnumSerializer.h" #include "mozilla/ContentBlockingNotifier.h" -#include "mozilla/StorageAccessAPIHelper.h" +#include "mozilla/ContentBlocking.h" #include "nsILoadInfo.h" @@ -38,13 +38,13 @@ struct ParamTraits mozilla::ContentBlockingNotifier::BlockingDecision::eBlock, mozilla::ContentBlockingNotifier::BlockingDecision::eAllow> {}; -// StorageAccessAPIHelper::StorageAccessPromptChoices over IPC. +// ContentBlocking::StorageAccessPromptChoices over IPC. template <> -struct ParamTraits +struct ParamTraits : public ContiguousEnumSerializerInclusive< - mozilla::StorageAccessAPIHelper::StorageAccessPromptChoices, - mozilla::StorageAccessAPIHelper::StorageAccessPromptChoices::eAllow, - mozilla::StorageAccessAPIHelper::StorageAccessPromptChoices:: + mozilla::ContentBlocking::StorageAccessPromptChoices, + mozilla::ContentBlocking::StorageAccessPromptChoices::eAllow, + mozilla::ContentBlocking::StorageAccessPromptChoices:: eAllowAutoGrant> {}; // nsILoadInfo::StoragePermissionState over IPC. diff --git a/toolkit/components/antitracking/AntiTrackingRedirectHeuristic.cpp b/toolkit/components/antitracking/AntiTrackingRedirectHeuristic.cpp index 9200e6156239..1df3c9454125 100644 --- a/toolkit/components/antitracking/AntiTrackingRedirectHeuristic.cpp +++ b/toolkit/components/antitracking/AntiTrackingRedirectHeuristic.cpp @@ -6,9 +6,9 @@ #include "AntiTrackingLog.h" #include "AntiTrackingRedirectHeuristic.h" +#include "ContentBlocking.h" #include "ContentBlockingAllowList.h" #include "ContentBlockingUserInteraction.h" -#include "StorageAccessAPIHelper.h" #include "mozilla/dom/BrowsingContext.h" #include "mozilla/dom/Document.h" @@ -422,10 +422,10 @@ void FinishAntiTrackingRedirectHeuristic(nsIChannel* aNewChannel, Telemetry::LABELS_STORAGE_ACCESS_GRANTED_COUNT::Redirect); // We don't care about this promise because the operation is actually sync. - RefPtr promise = - StorageAccessAPIHelper::SaveAccessForOriginOnParentProcess( + RefPtr promise = + ContentBlocking::SaveAccessForOriginOnParentProcess( newPrincipal, oldPrincipal, - StorageAccessAPIHelper::StorageAccessPromptChoices::eAllow, + ContentBlocking::StorageAccessPromptChoices::eAllow, StaticPrefs::privacy_restrict3rdpartystorage_expiration_redirect()); Unused << promise; } diff --git a/toolkit/components/antitracking/StorageAccessAPIHelper.cpp b/toolkit/components/antitracking/ContentBlocking.cpp similarity index 62% rename from toolkit/components/antitracking/StorageAccessAPIHelper.cpp rename to toolkit/components/antitracking/ContentBlocking.cpp index c50b78e1fc29..db0aa0db408b 100644 --- a/toolkit/components/antitracking/StorageAccessAPIHelper.cpp +++ b/toolkit/components/antitracking/ContentBlocking.cpp @@ -5,7 +5,7 @@ * You can obtain one at http://mozilla.org/MPL/2.0/. */ #include "AntiTrackingLog.h" -#include "StorageAccessAPIHelper.h" +#include "ContentBlocking.h" #include "AntiTrackingUtils.h" #include "TemporaryAccessGrantObserver.h" @@ -64,13 +64,80 @@ bool GetTopLevelWindowId(BrowsingContext* aParentContext, uint32_t aBehavior, return aTopLevelInnerWindowId != 0; } +// This internal method returns ACCESS_DENY if the access is denied, +// ACCESS_DEFAULT if unknown, some other access code if granted. +uint32_t CheckCookiePermissionForPrincipal( + nsICookieJarSettings* aCookieJarSettings, nsIPrincipal* aPrincipal) { + MOZ_ASSERT(aCookieJarSettings); + MOZ_ASSERT(aPrincipal); + + uint32_t cookiePermission = nsICookiePermission::ACCESS_DEFAULT; + if (!aPrincipal->GetIsContentPrincipal()) { + return cookiePermission; + } + + nsresult rv = + aCookieJarSettings->CookiePermission(aPrincipal, &cookiePermission); + if (NS_WARN_IF(NS_FAILED(rv))) { + return nsICookiePermission::ACCESS_DEFAULT; + } + + // If we have a custom cookie permission, let's use it. + return cookiePermission; +} + +int32_t CookiesBehavior(Document* a3rdPartyDocument) { + MOZ_ASSERT(a3rdPartyDocument); + + // WebExtensions principals always get BEHAVIOR_ACCEPT as cookieBehavior + // (See Bug 1406675 and Bug 1525917 for rationale). + if (BasePrincipal::Cast(a3rdPartyDocument->NodePrincipal())->AddonPolicy()) { + return nsICookieService::BEHAVIOR_ACCEPT; + } + + return a3rdPartyDocument->CookieJarSettings()->GetCookieBehavior(); +} + +int32_t CookiesBehavior(nsILoadInfo* aLoadInfo, nsIURI* a3rdPartyURI) { + MOZ_ASSERT(aLoadInfo); + MOZ_ASSERT(a3rdPartyURI); + + // WebExtensions 3rd party URI always get BEHAVIOR_ACCEPT as cookieBehavior, + // this is semantically equivalent to the principal having a AddonPolicy(). + if (a3rdPartyURI->SchemeIs("moz-extension")) { + return nsICookieService::BEHAVIOR_ACCEPT; + } + + nsCOMPtr cookieJarSettings; + nsresult rv = + aLoadInfo->GetCookieJarSettings(getter_AddRefs(cookieJarSettings)); + if (NS_WARN_IF(NS_FAILED(rv))) { + return nsICookieService::BEHAVIOR_REJECT; + } + + return cookieJarSettings->GetCookieBehavior(); +} + +int32_t CookiesBehavior(nsIPrincipal* aPrincipal, + nsICookieJarSettings* aCookieJarSettings) { + MOZ_ASSERT(aPrincipal); + MOZ_ASSERT(aCookieJarSettings); + + // WebExtensions principals always get BEHAVIOR_ACCEPT as cookieBehavior + // (See Bug 1406675 for rationale). + if (BasePrincipal::Cast(aPrincipal)->AddonPolicy()) { + return nsICookieService::BEHAVIOR_ACCEPT; + } + + return aCookieJarSettings->GetCookieBehavior(); +} } // namespace -/* static */ RefPtr -StorageAccessAPIHelper::AllowAccessFor( +/* static */ RefPtr +ContentBlocking::AllowAccessFor( nsIPrincipal* aPrincipal, dom::BrowsingContext* aParentContext, ContentBlockingNotifier::StorageAccessPermissionGrantedReason aReason, - const StorageAccessAPIHelper::PerformFinalChecks& aPerformFinalChecks) { + const ContentBlocking::PerformFinalChecks& aPerformFinalChecks) { MOZ_ASSERT(aParentContext); switch (aReason) { @@ -283,7 +350,7 @@ StorageAccessAPIHelper::AllowAccessFor( } if (runInSameProcess) { - return StorageAccessAPIHelper::CompleteAllowAccessFor( + return ContentBlocking::CompleteAllowAccessFor( aParentContext, topLevelWindowId, trackingPrincipal, trackingOrigin, behavior, aReason, aPerformFinalChecks); } @@ -313,8 +380,8 @@ StorageAccessAPIHelper::AllowAccessFor( if (aReason == ContentBlockingNotifier::eOpener && !bc->IsDiscarded()) { MOZ_ASSERT(bc->IsInProcess()); - StorageAccessAPIHelper::OnAllowAccessFor(bc, trackingOrigin, - behavior, aReason); + ContentBlocking::OnAllowAccessFor(bc, trackingOrigin, + behavior, aReason); } return StorageAccessPermissionGrantPromise::CreateAndResolve( aValue.ResolveValue().value(), __func__); @@ -355,8 +422,8 @@ StorageAccessAPIHelper::AllowAccessFor( // privilege API. So, it is always in-process. And we don't need to check the // user interaction permission for the tracking origin in this case. We can // run in the same process. -/* static */ RefPtr -StorageAccessAPIHelper::CompleteAllowAccessFor( +/* static */ RefPtr +ContentBlocking::CompleteAllowAccessFor( dom::BrowsingContext* aParentContext, uint64_t aTopLevelWindowId, nsIPrincipal* aTrackingPrincipal, const nsCString& aTrackingOrigin, uint32_t aCookieBehavior, @@ -440,8 +507,8 @@ StorageAccessAPIHelper::CompleteAllowAccessFor( // Inform the window we granted permission for. This has to be done in the // window's process. if (aParentContext->IsInProcess()) { - StorageAccessAPIHelper::OnAllowAccessFor(aParentContext, trackingOrigin, - aCookieBehavior, aReason); + ContentBlocking::OnAllowAccessFor(aParentContext, trackingOrigin, + aCookieBehavior, aReason); } else { MOZ_ASSERT(XRE_IsParentProcess()); @@ -493,7 +560,7 @@ StorageAccessAPIHelper::CompleteAllowAccessFor( ContentBlockingUserInteraction::Observe(trackingPrincipal); } return StorageAccessPermissionGrantPromise::CreateAndResolve( - StorageAccessAPIHelper::eAllow, __func__); + ContentBlocking::eAllow, __func__); }); } @@ -547,7 +614,7 @@ StorageAccessAPIHelper::CompleteAllowAccessFor( return storePermission(false); } -/* static */ void StorageAccessAPIHelper::OnAllowAccessFor( +/* static */ void ContentBlocking::OnAllowAccessFor( dom::BrowsingContext* aParentContext, const nsCString& aTrackingOrigin, uint32_t aCookieBehavior, ContentBlockingNotifier::StorageAccessPermissionGrantedReason aReason) { @@ -555,8 +622,8 @@ StorageAccessAPIHelper::CompleteAllowAccessFor( // Let's inform the parent window and the other windows having the // same tracking origin about the storage permission is granted. - StorageAccessAPIHelper::UpdateAllowAccessOnCurrentProcess(aParentContext, - aTrackingOrigin); + ContentBlocking::UpdateAllowAccessOnCurrentProcess(aParentContext, + aTrackingOrigin); // Let's inform the parent window. nsCOMPtr parentInner = @@ -609,8 +676,8 @@ StorageAccessAPIHelper::CompleteAllowAccessFor( } /* static */ -RefPtr -StorageAccessAPIHelper::SaveAccessForOriginOnParentProcess( +RefPtr +ContentBlocking::SaveAccessForOriginOnParentProcess( uint64_t aTopLevelWindowId, BrowsingContext* aParentContext, nsIPrincipal* aTrackingPrincipal, int aAllowMode, uint64_t aExpirationTime) { @@ -640,17 +707,17 @@ StorageAccessAPIHelper::SaveAccessForOriginOnParentProcess( // If the permission is granted on a first-party window, also have to update // the permission to all the other windows with the same tracking origin (in // the same tab), if any. - StorageAccessAPIHelper::UpdateAllowAccessOnParentProcess(aParentContext, - trackingOrigin); + ContentBlocking::UpdateAllowAccessOnParentProcess(aParentContext, + trackingOrigin); - return StorageAccessAPIHelper::SaveAccessForOriginOnParentProcess( + return ContentBlocking::SaveAccessForOriginOnParentProcess( wgp->DocumentPrincipal(), aTrackingPrincipal, aAllowMode, aExpirationTime); } /* static */ -RefPtr -StorageAccessAPIHelper::SaveAccessForOriginOnParentProcess( +RefPtr +ContentBlocking::SaveAccessForOriginOnParentProcess( nsIPrincipal* aParentPrincipal, nsIPrincipal* aTrackingPrincipal, int aAllowMode, uint64_t aExpirationTime) { MOZ_ASSERT(XRE_IsParentProcess()); @@ -736,8 +803,7 @@ StorageAccessAPIHelper::SaveAccessForOriginOnParentProcess( } // static -Maybe -StorageAccessAPIHelper::CheckCookiesPermittedDecidesStorageAccessAPI( +Maybe ContentBlocking::CheckCookiesPermittedDecidesStorageAccessAPI( nsICookieJarSettings* aCookieJarSettings, nsIPrincipal* aRequestingPrincipal) { MOZ_ASSERT(aCookieJarSettings); @@ -759,7 +825,7 @@ StorageAccessAPIHelper::CheckCookiesPermittedDecidesStorageAccessAPI( // static RefPtr, nsresult, true>> -StorageAccessAPIHelper::AsyncCheckCookiesPermittedDecidesStorageAccessAPI( +ContentBlocking::AsyncCheckCookiesPermittedDecidesStorageAccessAPI( dom::BrowsingContext* aBrowsingContext, nsIPrincipal* aRequestingPrincipal) { MOZ_ASSERT(XRE_IsContentProcess()); @@ -784,55 +850,21 @@ StorageAccessAPIHelper::AsyncCheckCookiesPermittedDecidesStorageAccessAPI( } // static -Maybe StorageAccessAPIHelper::CheckBrowserSettingsDecidesStorageAccessAPI( - nsICookieJarSettings* aCookieJarSettings, bool aThirdParty, - bool aOnRejectForeignAllowlist, bool aIsOnThirdPartySkipList, - bool aIsThirdPartyTracker) { +Maybe ContentBlocking::CheckBrowserSettingsDecidesStorageAccessAPI( + nsICookieJarSettings* aCookieJarSettings, bool aThirdParty) { MOZ_ASSERT(aCookieJarSettings); - uint32_t behavior = aCookieJarSettings->GetCookieBehavior(); - switch (behavior) { - case nsICookieService::BEHAVIOR_ACCEPT: - return Some(true); - case nsICookieService::BEHAVIOR_REJECT_FOREIGN: - if (!aThirdParty) { - return Some(true); - } - if (!StaticPrefs::network_cookie_rejectForeignWithExceptions_enabled()) { - return Some(false); - } - return Some(aOnRejectForeignAllowlist); - case nsICookieService::BEHAVIOR_REJECT: - return Some(false); - case nsICookieService::BEHAVIOR_LIMIT_FOREIGN: - if (!aThirdParty) { - return Some(true); - } - return Some(false); - case nsICookieService::BEHAVIOR_REJECT_TRACKER: - if (!aIsThirdPartyTracker) { - return Some(true); - } - if (aIsOnThirdPartySkipList) { - return Some(true); - } - return Nothing(); - case nsICookieService::BEHAVIOR_REJECT_TRACKER_AND_PARTITION_FOREIGN: - if (!aThirdParty) { - return Some(true); - } - if (aIsOnThirdPartySkipList) { - return Some(true); - } - return Nothing(); - default: - MOZ_ASSERT_UNREACHABLE("Must not have undefined cookie behavior"); + if (aCookieJarSettings->GetBlockingAllContexts() || + (aCookieJarSettings->GetBlockingAllThirdPartyContexts() && aThirdParty)) { + return Some(false); + } + if (!aCookieJarSettings->GetRejectThirdPartyContexts()) { + return Some(true); } - MOZ_ASSERT_UNREACHABLE("Must not have undefined cookie behavior"); return Nothing(); } // static -Maybe StorageAccessAPIHelper::CheckCallingContextDecidesStorageAccessAPI( +Maybe ContentBlocking::CheckCallingContextDecidesStorageAccessAPI( Document* aDocument, bool aRequestingStorageAccess) { MOZ_ASSERT(aDocument); // Window doesn't have user activation and we are asking for access -> reject. @@ -904,8 +936,7 @@ Maybe StorageAccessAPIHelper::CheckCallingContextDecidesStorageAccessAPI( } // static -Maybe -StorageAccessAPIHelper::CheckSameSiteCallingContextDecidesStorageAccessAPI( +Maybe ContentBlocking::CheckSameSiteCallingContextDecidesStorageAccessAPI( dom::Document* aDocument, bool aRequireUserActivation) { MOZ_ASSERT(aDocument); if (aRequireUserActivation) { @@ -941,18 +972,20 @@ StorageAccessAPIHelper::CheckSameSiteCallingContextDecidesStorageAccessAPI( } // static -Maybe -StorageAccessAPIHelper::CheckExistingPermissionDecidesStorageAccessAPI( +Maybe ContentBlocking::CheckExistingPermissionDecidesStorageAccessAPI( dom::Document* aDocument) { MOZ_ASSERT(aDocument); - if (aDocument->StorageAccessSandboxed()) { - nsContentUtils::ReportToConsole(nsIScriptError::errorFlag, - nsLiteralCString("requestStorageAccess"), - aDocument, nsContentUtils::eDOM_PROPERTIES, - "RequestStorageAccessSandboxed"); + nsPIDOMWindowInner* inner = aDocument->GetInnerWindow(); + if (!inner) { return Some(false); } - if (aDocument->HasStorageAccessPermissionGranted()) { + nsGlobalWindowOuter* outer = + nsGlobalWindowOuter::Cast(inner->GetOuterWindow()); + if (!outer) { + return Some(false); + } + bool explicitPermissionGranted = outer->IsStorageAccessPermissionGranted(); + if (explicitPermissionGranted) { return Some(true); } return Nothing(); @@ -977,7 +1010,7 @@ StorageAccessAPIHelper::CheckExistingPermissionDecidesStorageAccessAPI( // This function is used to update permission to all in-process windows, so it // can be called either from the parent or the child. /* static */ -void StorageAccessAPIHelper::UpdateAllowAccessOnCurrentProcess( +void ContentBlocking::UpdateAllowAccessOnCurrentProcess( BrowsingContext* aParentContext, const nsACString& aTrackingOrigin) { MOZ_ASSERT(aParentContext && aParentContext->IsInProcess()); @@ -1006,13 +1039,20 @@ void StorageAccessAPIHelper::UpdateAllowAccessOnCurrentProcess( if (inner) { inner->SaveStorageAccessPermissionGranted(); } + + nsCOMPtr outer = + nsPIDOMWindowOuter::GetFromCurrentInner(inner); + if (outer) { + nsGlobalWindowOuter::Cast(outer)->SetStorageAccessPermissionGranted( + true); + } } } }); } /* static */ -void StorageAccessAPIHelper::UpdateAllowAccessOnParentProcess( +void ContentBlocking::UpdateAllowAccessOnParentProcess( BrowsingContext* aParentContext, const nsACString& aTrackingOrigin) { MOZ_ASSERT(XRE_IsParentProcess()); @@ -1069,3 +1109,554 @@ void StorageAccessAPIHelper::UpdateAllowAccessOnParentProcess( }); } } + +bool ContentBlocking::ShouldAllowAccessFor(nsPIDOMWindowInner* aWindow, + nsIURI* aURI, + uint32_t* aRejectedReason) { + MOZ_ASSERT(aWindow); + MOZ_ASSERT(aURI); + + // Let's avoid a null check on aRejectedReason everywhere else. + uint32_t rejectedReason = 0; + if (!aRejectedReason) { + aRejectedReason = &rejectedReason; + } + + LOG_SPEC(("Computing whether window %p has access to URI %s", aWindow, _spec), + aURI); + + nsGlobalWindowInner* innerWindow = nsGlobalWindowInner::Cast(aWindow); + Document* document = innerWindow->GetExtantDoc(); + if (!document) { + LOG(("Our window has no document")); + return false; + } + + uint32_t cookiePermission = CheckCookiePermissionForPrincipal( + document->CookieJarSettings(), document->NodePrincipal()); + if (cookiePermission != nsICookiePermission::ACCESS_DEFAULT) { + LOG( + ("CheckCookiePermissionForPrincipal() returned a non-default access " + "code (%d) for window's principal, returning %s", + int(cookiePermission), + cookiePermission != nsICookiePermission::ACCESS_DENY ? "success" + : "failure")); + if (cookiePermission != nsICookiePermission::ACCESS_DENY) { + return true; + } + + *aRejectedReason = + nsIWebProgressListener::STATE_COOKIES_BLOCKED_BY_PERMISSION; + return false; + } + + int32_t behavior = CookiesBehavior(document); + if (behavior == nsICookieService::BEHAVIOR_ACCEPT) { + LOG(("The cookie behavior pref mandates accepting all cookies!")); + return true; + } + + if (ContentBlockingAllowList::Check(aWindow)) { + return true; + } + + if (behavior == nsICookieService::BEHAVIOR_REJECT) { + LOG(("The cookie behavior pref mandates rejecting all cookies!")); + *aRejectedReason = nsIWebProgressListener::STATE_COOKIES_BLOCKED_ALL; + return false; + } + + // As a performance optimization, we only perform this check for + // BEHAVIOR_REJECT_FOREIGN and BEHAVIOR_LIMIT_FOREIGN. For + // BEHAVIOR_REJECT_TRACKER and BEHAVIOR_REJECT_TRACKER_AND_PARTITION_FOREIGN, + // third-partiness is implicily checked later below. + if (behavior != nsICookieService::BEHAVIOR_REJECT_TRACKER && + behavior != + nsICookieService::BEHAVIOR_REJECT_TRACKER_AND_PARTITION_FOREIGN) { + // Let's check if this is a 3rd party context. + if (!AntiTrackingUtils::IsThirdPartyWindow(aWindow, aURI)) { + LOG(("Our window isn't a third-party window")); + return true; + } + } + + if ((behavior == nsICookieService::BEHAVIOR_REJECT_FOREIGN && + !CookieJarSettings::IsRejectThirdPartyWithExceptions(behavior)) || + behavior == nsICookieService::BEHAVIOR_LIMIT_FOREIGN) { + // XXX For non-cookie forms of storage, we handle BEHAVIOR_LIMIT_FOREIGN by + // simply rejecting the request to use the storage. In the future, if we + // change the meaning of BEHAVIOR_LIMIT_FOREIGN to be one which makes sense + // for non-cookie storage types, this may change. + LOG(("Nothing more to do due to the behavior code %d", int(behavior))); + *aRejectedReason = nsIWebProgressListener::STATE_COOKIES_BLOCKED_FOREIGN; + return false; + } + + // The document has been allowlisted. We can return from here directly. + if (document->HasStorageAccessPermissionGrantedByAllowList()) { + return true; + } + + MOZ_ASSERT( + CookieJarSettings::IsRejectThirdPartyWithExceptions(behavior) || + behavior == nsICookieService::BEHAVIOR_REJECT_TRACKER || + behavior == + nsICookieService::BEHAVIOR_REJECT_TRACKER_AND_PARTITION_FOREIGN); + + uint32_t blockedReason = + nsIWebProgressListener::STATE_COOKIES_BLOCKED_TRACKER; + + if (behavior == nsICookieService::BEHAVIOR_REJECT_TRACKER) { + if (!nsContentUtils::IsThirdPartyTrackingResourceWindow(aWindow)) { + LOG(("Our window isn't a third-party tracking window")); + return true; + } + + nsCOMPtr classifiedChannel = + do_QueryInterface(document->GetChannel()); + if (classifiedChannel) { + uint32_t classificationFlags = + classifiedChannel->GetThirdPartyClassificationFlags(); + if (classificationFlags & nsIClassifiedChannel::ClassificationFlags:: + CLASSIFIED_SOCIALTRACKING) { + blockedReason = + nsIWebProgressListener::STATE_COOKIES_BLOCKED_SOCIALTRACKER; + } + } + } else if (behavior == + nsICookieService::BEHAVIOR_REJECT_TRACKER_AND_PARTITION_FOREIGN) { + if (nsContentUtils::IsThirdPartyTrackingResourceWindow(aWindow)) { + // fall through + } else if (AntiTrackingUtils::IsThirdPartyWindow(aWindow, aURI)) { + LOG(("We're in the third-party context, storage should be partitioned")); + // fall through, but remember that we're partitioning. + blockedReason = nsIWebProgressListener::STATE_COOKIES_PARTITIONED_FOREIGN; + } else { + LOG(("Our window isn't a third-party window, storage is allowed")); + return true; + } + } else { + MOZ_ASSERT(CookieJarSettings::IsRejectThirdPartyWithExceptions(behavior)); + if (RejectForeignAllowList::Check(document)) { + LOG(("This window is exceptionlisted for reject foreign")); + return true; + } + + blockedReason = nsIWebProgressListener::STATE_COOKIES_PARTITIONED_FOREIGN; + } + + Document* doc = aWindow->GetExtantDoc(); + // Make sure storage access isn't disabled + if (doc && (doc->StorageAccessSandboxed())) { + LOG(("Our document is sandboxed")); + *aRejectedReason = blockedReason; + return false; + } + + // Document::HasStoragePermission first checks if storage access granted is + // cached in the inner window, if no, it then checks the storage permission + // flag in the channel's loadinfo + bool allowed = document->HasStorageAccessPermissionGranted(); + + if (!allowed) { + *aRejectedReason = blockedReason; + } else { + if (MOZ_LOG_TEST(gAntiTrackingLog, mozilla::LogLevel::Debug) && + aWindow->HasStorageAccessPermissionGranted()) { + LOG(("Permission stored in the window. All good.")); + } + } + + return allowed; +} + +bool ContentBlocking::ShouldAllowAccessFor(nsIChannel* aChannel, nsIURI* aURI, + uint32_t* aRejectedReason) { + MOZ_ASSERT(aURI); + MOZ_ASSERT(aChannel); + + // Let's avoid a null check on aRejectedReason everywhere else. + uint32_t rejectedReason = 0; + if (!aRejectedReason) { + aRejectedReason = &rejectedReason; + } + + nsIScriptSecurityManager* ssm = + nsScriptSecurityManager::GetScriptSecurityManager(); + MOZ_ASSERT(ssm); + + nsCOMPtr channelURI; + nsresult rv = NS_GetFinalChannelURI(aChannel, getter_AddRefs(channelURI)); + if (NS_FAILED(rv)) { + LOG(("Failed to get the channel final URI, bail out early")); + return true; + } + LOG_SPEC( + ("Computing whether channel %p has access to URI %s", aChannel, _spec), + channelURI); + + nsCOMPtr loadInfo = aChannel->LoadInfo(); + nsCOMPtr cookieJarSettings; + rv = loadInfo->GetCookieJarSettings(getter_AddRefs(cookieJarSettings)); + if (NS_WARN_IF(NS_FAILED(rv))) { + LOG( + ("Failed to get the cookie jar settings from the loadinfo, bail out " + "early")); + return true; + } + + nsCOMPtr channelPrincipal; + rv = ssm->GetChannelURIPrincipal(aChannel, getter_AddRefs(channelPrincipal)); + if (NS_WARN_IF(NS_FAILED(rv))) { + LOG(("No channel principal, bail out early")); + return false; + } + + uint32_t cookiePermission = + CheckCookiePermissionForPrincipal(cookieJarSettings, channelPrincipal); + if (cookiePermission != nsICookiePermission::ACCESS_DEFAULT) { + LOG( + ("CheckCookiePermissionForPrincipal() returned a non-default access " + "code (%d) for channel's principal, returning %s", + int(cookiePermission), + cookiePermission != nsICookiePermission::ACCESS_DENY ? "success" + : "failure")); + if (cookiePermission != nsICookiePermission::ACCESS_DENY) { + return true; + } + + *aRejectedReason = + nsIWebProgressListener::STATE_COOKIES_BLOCKED_BY_PERMISSION; + return false; + } + + if (!channelURI) { + LOG(("No channel uri, bail out early")); + return false; + } + + int32_t behavior = CookiesBehavior(loadInfo, channelURI); + if (behavior == nsICookieService::BEHAVIOR_ACCEPT) { + LOG(("The cookie behavior pref mandates accepting all cookies!")); + return true; + } + + nsCOMPtr httpChannel = do_QueryInterface(aChannel); + + if (httpChannel && ContentBlockingAllowList::Check(httpChannel)) { + return true; + } + + if (behavior == nsICookieService::BEHAVIOR_REJECT) { + LOG(("The cookie behavior pref mandates rejecting all cookies!")); + *aRejectedReason = nsIWebProgressListener::STATE_COOKIES_BLOCKED_ALL; + return false; + } + + nsCOMPtr thirdPartyUtil = + components::ThirdPartyUtil::Service(); + if (!thirdPartyUtil) { + LOG(("No thirdPartyUtil, bail out early")); + return true; + } + + bool thirdParty = false; + rv = thirdPartyUtil->IsThirdPartyChannel(aChannel, aURI, &thirdParty); + // Grant if it's not a 3rd party. + // Be careful to check the return value of IsThirdPartyChannel, since + // IsThirdPartyChannel() will fail if the channel's loading principal is the + // system principal... + if (NS_SUCCEEDED(rv) && !thirdParty) { + LOG(("Our channel isn't a third-party channel")); + return true; + } + + if ((behavior == nsICookieService::BEHAVIOR_REJECT_FOREIGN && + !CookieJarSettings::IsRejectThirdPartyWithExceptions(behavior)) || + behavior == nsICookieService::BEHAVIOR_LIMIT_FOREIGN) { + // XXX For non-cookie forms of storage, we handle BEHAVIOR_LIMIT_FOREIGN by + // simply rejecting the request to use the storage. In the future, if we + // change the meaning of BEHAVIOR_LIMIT_FOREIGN to be one which makes sense + // for non-cookie storage types, this may change. + LOG(("Nothing more to do due to the behavior code %d", int(behavior))); + *aRejectedReason = nsIWebProgressListener::STATE_COOKIES_BLOCKED_FOREIGN; + return false; + } + + // The channel has been allowlisted. We can return from here. + if (loadInfo->GetStoragePermission() == + nsILoadInfo::StoragePermissionAllowListed) { + return true; + } + + MOZ_ASSERT( + CookieJarSettings::IsRejectThirdPartyWithExceptions(behavior) || + behavior == nsICookieService::BEHAVIOR_REJECT_TRACKER || + behavior == + nsICookieService::BEHAVIOR_REJECT_TRACKER_AND_PARTITION_FOREIGN); + + uint32_t blockedReason = + nsIWebProgressListener::STATE_COOKIES_BLOCKED_TRACKER; + + // Not a tracker. + nsCOMPtr classifiedChannel = + do_QueryInterface(aChannel); + if (behavior == nsICookieService::BEHAVIOR_REJECT_TRACKER) { + if (classifiedChannel) { + if (!classifiedChannel->IsThirdPartyTrackingResource()) { + LOG(("Our channel isn't a third-party tracking channel")); + return true; + } + + uint32_t classificationFlags = + classifiedChannel->GetThirdPartyClassificationFlags(); + if (classificationFlags & nsIClassifiedChannel::ClassificationFlags:: + CLASSIFIED_SOCIALTRACKING) { + blockedReason = + nsIWebProgressListener::STATE_COOKIES_BLOCKED_SOCIALTRACKER; + } + } + } else if (behavior == + nsICookieService::BEHAVIOR_REJECT_TRACKER_AND_PARTITION_FOREIGN) { + if (classifiedChannel && + classifiedChannel->IsThirdPartyTrackingResource()) { + // fall through + } else if (AntiTrackingUtils::IsThirdPartyChannel(aChannel)) { + LOG(("We're in the third-party context, storage should be partitioned")); + // fall through but remember that we're partitioning. + blockedReason = nsIWebProgressListener::STATE_COOKIES_PARTITIONED_FOREIGN; + } else { + LOG(("Our channel isn't a third-party channel, storage is allowed")); + return true; + } + } else { + MOZ_ASSERT(CookieJarSettings::IsRejectThirdPartyWithExceptions(behavior)); + if (httpChannel && RejectForeignAllowList::Check(httpChannel)) { + LOG(("This channel is exceptionlisted")); + return true; + } + blockedReason = nsIWebProgressListener::STATE_COOKIES_BLOCKED_FOREIGN; + } + + RefPtr targetBC; + rv = loadInfo->GetTargetBrowsingContext(getter_AddRefs(targetBC)); + if (!targetBC || NS_WARN_IF(NS_FAILED(rv))) { + LOG(("Failed to get the channel's target browsing context")); + return false; + } + + if (Document::StorageAccessSandboxed(targetBC->GetSandboxFlags())) { + LOG(("Our document is sandboxed")); + *aRejectedReason = blockedReason; + return false; + } + + // Let's see if we have to grant the access for this particular channel. + + // HasStorageAccessPermissionGranted only applies to channels that load + // documents, for sub-resources loads, just returns the result from loadInfo. + bool isDocument = false; + aChannel->GetIsDocument(&isDocument); + + if (isDocument) { + nsCOMPtr inner = + AntiTrackingUtils::GetInnerWindow(targetBC); + if (inner && inner->HasStorageAccessPermissionGranted()) { + LOG(("Permission stored in the window. All good.")); + return true; + } + } + + bool allowed = + loadInfo->GetStoragePermission() != nsILoadInfo::NoStoragePermission; + if (!allowed) { + *aRejectedReason = blockedReason; + } + + return allowed; +} + +bool ContentBlocking::ShouldAllowAccessFor( + nsIPrincipal* aPrincipal, nsICookieJarSettings* aCookieJarSettings) { + MOZ_ASSERT(aPrincipal); + MOZ_ASSERT(aCookieJarSettings); + + uint32_t access = nsICookiePermission::ACCESS_DEFAULT; + if (aPrincipal->GetIsContentPrincipal()) { + PermissionManager* permManager = PermissionManager::GetInstance(); + if (permManager) { + Unused << NS_WARN_IF(NS_FAILED(permManager->TestPermissionFromPrincipal( + aPrincipal, "cookie"_ns, &access))); + } + } + + if (access != nsICookiePermission::ACCESS_DEFAULT) { + return access != nsICookiePermission::ACCESS_DENY; + } + + int32_t behavior = CookiesBehavior(aPrincipal, aCookieJarSettings); + return behavior != nsICookieService::BEHAVIOR_REJECT; +} + +/* static */ +bool ContentBlocking::ApproximateAllowAccessForWithoutChannel( + nsPIDOMWindowInner* aFirstPartyWindow, nsIURI* aURI) { + MOZ_ASSERT(aFirstPartyWindow); + MOZ_ASSERT(aURI); + + LOG_SPEC( + ("Computing a best guess as to whether window %p has access to URI %s", + aFirstPartyWindow, _spec), + aURI); + + Document* parentDocument = + nsGlobalWindowInner::Cast(aFirstPartyWindow)->GetExtantDoc(); + if (NS_WARN_IF(!parentDocument)) { + LOG(("Failed to get the first party window's document")); + return false; + } + + if (!parentDocument->CookieJarSettings()->GetRejectThirdPartyContexts()) { + LOG(("Disabled by the pref (%d), bail out early", + parentDocument->CookieJarSettings()->GetCookieBehavior())); + return true; + } + + if (ContentBlockingAllowList::Check(aFirstPartyWindow)) { + return true; + } + + if (!AntiTrackingUtils::IsThirdPartyWindow(aFirstPartyWindow, aURI)) { + LOG(("Our window isn't a third-party window")); + return true; + } + + uint32_t cookiePermission = CheckCookiePermissionForPrincipal( + parentDocument->CookieJarSettings(), parentDocument->NodePrincipal()); + if (cookiePermission != nsICookiePermission::ACCESS_DEFAULT) { + LOG( + ("CheckCookiePermissionForPrincipal() returned a non-default access " + "code (%d), returning %s", + int(cookiePermission), + cookiePermission != nsICookiePermission::ACCESS_DENY ? "success" + : "failure")); + return cookiePermission != nsICookiePermission::ACCESS_DENY; + } + + nsAutoCString origin; + nsresult rv = nsContentUtils::GetASCIIOrigin(aURI, origin); + if (NS_WARN_IF(NS_FAILED(rv))) { + LOG_SPEC(("Failed to compute the origin from %s", _spec), aURI); + return false; + } + + nsIPrincipal* parentPrincipal = parentDocument->NodePrincipal(); + + nsAutoCString type; + AntiTrackingUtils::CreateStoragePermissionKey(origin, type); + + return AntiTrackingUtils::CheckStoragePermission( + parentPrincipal, type, + nsContentUtils::IsInPrivateBrowsing(parentDocument), nullptr, 0); +} + +NS_IMPL_ISUPPORTS(ContentBlocking::TrackerClassifierFeatureCallback, + nsIUrlClassifierFeatureCallback) + +NS_IMETHODIMP +ContentBlocking::TrackerClassifierFeatureCallback::OnClassifyComplete( + const nsTArray>& aResults) { + MOZ_ASSERT(NS_IsMainThread()); + + if (aResults.IsEmpty()) { + // Reject if we can not find url in tracker list. + mHolder.RejectIfExists(NS_OK, __func__); + return NS_OK; + } + + bool isSocialTracker = false; + + // Check if the principal is listed in the social tracking list to report + // different tracker type. + for (const auto& result : aResults) { + nsCOMPtr feature; + result->GetFeature(getter_AddRefs(feature)); + + if (!feature) { + continue; + } + + nsAutoCString name; + feature->GetName(name); + + if (name.EqualsLiteral("socialtracking-annotation")) { + isSocialTracker = true; + break; + } + } + + mHolder.ResolveIfExists( + isSocialTracker + ? nsIWebProgressListener::STATE_COOKIES_BLOCKED_SOCIALTRACKER + : nsIWebProgressListener::STATE_COOKIES_BLOCKED_TRACKER, + __func__); + + return NS_OK; +} + +/* static */ +RefPtr +ContentBlocking::CheckTrackerForPrincipal(nsIPrincipal* aPrincipal) { + MOZ_ASSERT(aPrincipal); + MOZ_ASSERT(XRE_IsParentProcess()); + + nsCOMPtr uri; + auto* basePrincipal = BasePrincipal::Cast(aPrincipal); + basePrincipal->GetURI(getter_AddRefs(uri)); + + if (!uri) { + LOG(("Cannot get uri from the principal.")); + return CheckTrackerForPrincipalPromise::CreateAndReject(NS_ERROR_FAILURE, + __func__); + } + + nsresult rv; + nsCOMPtr uriClassifier = + mozilla::components::UrlClassifierDB::Service(&rv); + if (NS_WARN_IF(NS_FAILED(rv))) { + LOG(("Cannot get the uri classifier.")); + return CheckTrackerForPrincipalPromise::CreateAndReject(rv, __func__); + } + + // Check the uri of the principal with the tracking annotation features + // including the social tracker. + nsTArray featureNames = {"tracking-annotation"_ns, + "socialtracking-annotation"_ns}; + nsTArray> features; + + for (auto& name : featureNames) { + nsCOMPtr feature; + uriClassifier->GetFeatureByName(name, getter_AddRefs(feature)); + MOZ_ASSERT(feature); + + if (!feature) { + LOG(("Cannot get feature for feature name(%s)", name.get())); + return CheckTrackerForPrincipalPromise::CreateAndReject(NS_ERROR_FAILURE, + __func__); + } + features.AppendElement(feature); + } + + auto callback = MakeRefPtr(); + + RefPtr promise = callback->Promise(); + + rv = uriClassifier->AsyncClassifyLocalWithFeatures( + uri, features, nsIUrlClassifierFeature::blocklist, callback); + if (NS_FAILED(rv)) { + LOG(("Fail on classifying the url.")); + callback->Reject(rv); + } + + return promise; +} diff --git a/toolkit/components/antitracking/StorageAccessAPIHelper.h b/toolkit/components/antitracking/ContentBlocking.h similarity index 75% rename from toolkit/components/antitracking/StorageAccessAPIHelper.h rename to toolkit/components/antitracking/ContentBlocking.h index ee86383bcb2b..c238b18b98aa 100644 --- a/toolkit/components/antitracking/StorageAccessAPIHelper.h +++ b/toolkit/components/antitracking/ContentBlocking.h @@ -33,8 +33,44 @@ class ContentParent; class Document; } // namespace dom -class StorageAccessAPIHelper final { +class ContentBlocking final { public: + // This method returns true if the URI has first party storage access when + // loaded inside the passed 3rd party context tracking resource window. + // If the window is first party context, please use + // ApproximateAllowAccessForWithoutChannel(); + // + // aRejectedReason could be set to one of these values if passed and if the + // storage permission is not granted: + // * nsIWebProgressListener::STATE_COOKIES_BLOCKED_BY_PERMISSION + // * nsIWebProgressListener::STATE_COOKIES_BLOCKED_TRACKER + // * nsIWebProgressListener::STATE_COOKIES_BLOCKED_SOCIALTRACKER + // * nsIWebProgressListener::STATE_COOKIES_BLOCKED_ALL + // * nsIWebProgressListener::STATE_COOKIES_BLOCKED_FOREIGN + static bool ShouldAllowAccessFor(nsPIDOMWindowInner* a3rdPartyTrackingWindow, + nsIURI* aURI, uint32_t* aRejectedReason); + + // Note: you should use ShouldAllowAccessFor() passing the nsIChannel! Use + // this method _only_ if the channel is not available. For first party + // window, it's impossible to know if the aURI is a tracking resource + // synchronously, so here we return the best guest: if we are sure that the + // permission is granted for the origin of aURI, this method returns true, + // otherwise false. + static bool ApproximateAllowAccessForWithoutChannel( + nsPIDOMWindowInner* aFirstPartyWindow, nsIURI* aURI); + + // It returns true if the URI has access to the first party storage. + // aChannel can be a 3rd party channel, or not. + // See ShouldAllowAccessFor(window) to see the possible values of + // aRejectedReason. + static bool ShouldAllowAccessFor(nsIChannel* aChannel, nsIURI* aURI, + uint32_t* aRejectedReason); + + // This method checks if the principal has the permission to access to the + // first party storage. + static bool ShouldAllowAccessFor(nsIPrincipal* aPrincipal, + nsICookieJarSettings* aCookieJarSettings); + enum StorageAccessPromptChoices { eAllow, eAllowAutoGrant }; // Grant the permission for aOrigin to have access to the first party storage. @@ -111,9 +147,7 @@ class StorageAccessAPIHelper final { // Some(false) if unpartitioned cookies will be blocked // None if it is not clear from settings alone what to do static Maybe CheckBrowserSettingsDecidesStorageAccessAPI( - nsICookieJarSettings* aCookieJarSettings, bool aThirdParty, - bool aOnRejectForeignAllowlist, bool aIsOnThirdPartySkipList, - bool aIsThirdPartyTracker); + nsICookieJarSettings* aCookieJarSettings, bool aThirdParty); // This function checks if the document's context (like if it is third-party // or an iframe) gives an answer of how a the StorageAccessAPI call, that is @@ -175,6 +209,32 @@ class StorageAccessAPIHelper final { static void UpdateAllowAccessOnParentProcess( dom::BrowsingContext* aParentContext, const nsACString& aTrackingOrigin); + + typedef MozPromise CheckTrackerForPrincipalPromise; + class TrackerClassifierFeatureCallback final + : public nsIUrlClassifierFeatureCallback { + public: + NS_DECL_ISUPPORTS + NS_DECL_NSIURLCLASSIFIERFEATURECALLBACK + + RefPtr Promise() { + return mHolder.Ensure(__func__); + } + + void Reject(nsresult rv) { mHolder.Reject(rv, __func__); } + + TrackerClassifierFeatureCallback() = default; + + private: + ~TrackerClassifierFeatureCallback() = default; + + MozPromiseHolder mHolder; + }; + + // This method checks if the given princpal belongs to a tracker or a social + // tracker. + [[nodiscard]] static RefPtr + CheckTrackerForPrincipal(nsIPrincipal* aPrincipal); }; } // namespace mozilla diff --git a/toolkit/components/antitracking/ContentBlockingAllowList.h b/toolkit/components/antitracking/ContentBlockingAllowList.h index 1488760569b4..36b51d3d6199 100644 --- a/toolkit/components/antitracking/ContentBlockingAllowList.h +++ b/toolkit/components/antitracking/ContentBlockingAllowList.h @@ -30,10 +30,6 @@ class ContentBlockingAllowList final { bool aIsPrivateBrowsing, bool& aIsAllowListed); static bool Check(nsIHttpChannel* aChannel); - // Utility APIs for ContentBlocking. - static bool Check(nsPIDOMWindowInner* aWindow); - static bool Check(nsIPrincipal* aTopWinPrincipal, bool aIsPrivateBrowsing); - static bool Check(nsICookieJarSettings* aCookieJarSettings); // Computes the principal used to check the content blocking allow list for a // top-level document based on the document principal. This function is used @@ -44,6 +40,14 @@ class ContentBlockingAllowList final { static void RecomputePrincipal(nsIURI* aURIBeingLoaded, const OriginAttributes& aAttrs, nsIPrincipal** aPrincipal); + + private: + // Utility APIs for ContentBlocking. + static bool Check(nsIPrincipal* aTopWinPrincipal, bool aIsPrivateBrowsing); + static bool Check(nsPIDOMWindowInner* aWindow); + static bool Check(nsICookieJarSettings* aCookieJarSettings); + + friend class ContentBlocking; }; } // namespace mozilla diff --git a/toolkit/components/antitracking/ContentBlockingNotifier.cpp b/toolkit/components/antitracking/ContentBlockingNotifier.cpp index e5b303c33af1..965eef17de3a 100644 --- a/toolkit/components/antitracking/ContentBlockingNotifier.cpp +++ b/toolkit/components/antitracking/ContentBlockingNotifier.cpp @@ -12,7 +12,6 @@ #include "mozilla/StaticPrefs_privacy.h" #include "mozilla/dom/BrowserChild.h" #include "mozilla/dom/BrowsingContext.h" -#include "mozilla/dom/ContentParent.h" #include "mozilla/dom/WindowGlobalParent.h" #include "nsIClassifiedChannel.h" #include "nsIRunnable.h" diff --git a/toolkit/components/antitracking/DynamicFpiRedirectHeuristic.cpp b/toolkit/components/antitracking/DynamicFpiRedirectHeuristic.cpp index d42bf9dce3e1..042067b6d8d4 100644 --- a/toolkit/components/antitracking/DynamicFpiRedirectHeuristic.cpp +++ b/toolkit/components/antitracking/DynamicFpiRedirectHeuristic.cpp @@ -6,9 +6,9 @@ #include "AntiTrackingLog.h" #include "DynamicFpiRedirectHeuristic.h" +#include "ContentBlocking.h" #include "ContentBlockingAllowList.h" #include "ContentBlockingUserInteraction.h" -#include "StorageAccessAPIHelper.h" #include "mozilla/net/HttpBaseChannel.h" #include "mozilla/Telemetry.h" @@ -329,10 +329,10 @@ void DynamicFpiRedirectHeuristic(nsIChannel* aOldChannel, nsIURI* aOldURI, Telemetry::LABELS_STORAGE_ACCESS_GRANTED_COUNT::Redirect); // We don't care about this promise because the operation is actually sync. - RefPtr promise = - StorageAccessAPIHelper::SaveAccessForOriginOnParentProcess( + RefPtr promise = + ContentBlocking::SaveAccessForOriginOnParentProcess( newPrincipal, oldPrincipal, - StorageAccessAPIHelper::StorageAccessPromptChoices::eAllow, + ContentBlocking::StorageAccessPromptChoices::eAllow, StaticPrefs::privacy_restrict3rdpartystorage_expiration_visited()); Unused << promise; } diff --git a/toolkit/components/antitracking/RejectForeignAllowList.cpp b/toolkit/components/antitracking/RejectForeignAllowList.cpp index b7117d77af87..226366611526 100644 --- a/toolkit/components/antitracking/RejectForeignAllowList.cpp +++ b/toolkit/components/antitracking/RejectForeignAllowList.cpp @@ -78,11 +78,6 @@ RejectForeignAllowList* RejectForeignAllowList::GetOrCreate() { return gRejectForeignAllowList; } -// static -bool RejectForeignAllowList::Check(nsIURI* aURI) { - return GetOrCreate()->CheckInternal(aURI); -} - bool RejectForeignAllowList::CheckInternal(nsIURI* aURI) { MOZ_ASSERT(aURI); return nsContentUtils::IsURIInList(aURI, mList); diff --git a/toolkit/components/antitracking/RejectForeignAllowList.h b/toolkit/components/antitracking/RejectForeignAllowList.h index 98ea9143c28f..fd2271da6518 100644 --- a/toolkit/components/antitracking/RejectForeignAllowList.h +++ b/toolkit/components/antitracking/RejectForeignAllowList.h @@ -27,7 +27,6 @@ class RejectForeignAllowList final static bool Check(dom::Document* aDocument); static bool Check(nsIHttpChannel* aChannel); static bool Check(nsIPrincipal* aPrincipal); - static bool Check(nsIURI* aURI); private: static RejectForeignAllowList* GetOrCreate(); diff --git a/toolkit/components/antitracking/StorageAccess.cpp b/toolkit/components/antitracking/StorageAccess.cpp index 2fb5e486eaa2..d4f4d7a42007 100644 --- a/toolkit/components/antitracking/StorageAccess.cpp +++ b/toolkit/components/antitracking/StorageAccess.cpp @@ -8,6 +8,7 @@ #include "mozilla/dom/Document.h" #include "mozilla/net/CookieJarSettings.h" +#include "mozilla/ContentBlocking.h" #include "mozilla/StaticPrefs_browser.h" #include "mozilla/StaticPrefs_network.h" #include "mozilla/StaticPrefs_privacy.h" @@ -22,7 +23,6 @@ using namespace mozilla; using namespace mozilla::dom; -using mozilla::net::CookieJarSettings; /** * Gets the cookie lifetime policy for a given cookieJarSettings and a given @@ -57,28 +57,6 @@ static void GetCookieLifetimePolicyFromCookieJarSettings( } } -// This internal method returns ACCESS_DENY if the access is denied, -// ACCESS_DEFAULT if unknown, some other access code if granted. -uint32_t CheckCookiePermissionForPrincipal( - nsICookieJarSettings* aCookieJarSettings, nsIPrincipal* aPrincipal) { - MOZ_ASSERT(aCookieJarSettings); - MOZ_ASSERT(aPrincipal); - - uint32_t cookiePermission = nsICookiePermission::ACCESS_DEFAULT; - if (!aPrincipal->GetIsContentPrincipal()) { - return cookiePermission; - } - - nsresult rv = - aCookieJarSettings->CookiePermission(aPrincipal, &cookiePermission); - if (NS_WARN_IF(NS_FAILED(rv))) { - return nsICookiePermission::ACCESS_DEFAULT; - } - - // If we have a custom cookie permission, let's use it. - return cookiePermission; -} - /* * Checks if storage for a given principal is permitted by the user's * preferences. If aWindow is non-null, its principal must be passed as @@ -249,8 +227,8 @@ static bool StorageDisabledByAntiTrackingInternal( if (aWindow) { nsIURI* documentURI = aURI ? aURI : aWindow->GetDocumentURI(); - return !documentURI || - !ShouldAllowAccessFor(aWindow, documentURI, &aRejectedReason); + return !documentURI || !ContentBlocking::ShouldAllowAccessFor( + aWindow, documentURI, &aRejectedReason); } if (aChannel) { @@ -260,11 +238,12 @@ static bool StorageDisabledByAntiTrackingInternal( return false; } - return !ShouldAllowAccessFor(aChannel, uri, &aRejectedReason); + return !ContentBlocking::ShouldAllowAccessFor(aChannel, uri, + &aRejectedReason); } MOZ_ASSERT(aPrincipal); - return !ShouldAllowAccessFor(aPrincipal, aCookieJarSettings); + return !ContentBlocking::ShouldAllowAccessFor(aPrincipal, aCookieJarSettings); } namespace mozilla { @@ -438,498 +417,4 @@ bool StoragePartitioningEnabled(uint32_t aRejectedReason, nsICookieService::BEHAVIOR_REJECT_TRACKER_AND_PARTITION_FOREIGN; } -int32_t CookiesBehavior(Document* a3rdPartyDocument) { - MOZ_ASSERT(a3rdPartyDocument); - - // WebExtensions principals always get BEHAVIOR_ACCEPT as cookieBehavior - // (See Bug 1406675 and Bug 1525917 for rationale). - if (BasePrincipal::Cast(a3rdPartyDocument->NodePrincipal())->AddonPolicy()) { - return nsICookieService::BEHAVIOR_ACCEPT; - } - - return a3rdPartyDocument->CookieJarSettings()->GetCookieBehavior(); -} - -int32_t CookiesBehavior(nsILoadInfo* aLoadInfo, nsIURI* a3rdPartyURI) { - MOZ_ASSERT(aLoadInfo); - MOZ_ASSERT(a3rdPartyURI); - - // WebExtensions 3rd party URI always get BEHAVIOR_ACCEPT as cookieBehavior, - // this is semantically equivalent to the principal having a AddonPolicy(). - if (a3rdPartyURI->SchemeIs("moz-extension")) { - return nsICookieService::BEHAVIOR_ACCEPT; - } - - nsCOMPtr cookieJarSettings; - nsresult rv = - aLoadInfo->GetCookieJarSettings(getter_AddRefs(cookieJarSettings)); - if (NS_WARN_IF(NS_FAILED(rv))) { - return nsICookieService::BEHAVIOR_REJECT; - } - - return cookieJarSettings->GetCookieBehavior(); -} - -int32_t CookiesBehavior(nsIPrincipal* aPrincipal, - nsICookieJarSettings* aCookieJarSettings) { - MOZ_ASSERT(aPrincipal); - MOZ_ASSERT(aCookieJarSettings); - - // WebExtensions principals always get BEHAVIOR_ACCEPT as cookieBehavior - // (See Bug 1406675 for rationale). - if (BasePrincipal::Cast(aPrincipal)->AddonPolicy()) { - return nsICookieService::BEHAVIOR_ACCEPT; - } - - return aCookieJarSettings->GetCookieBehavior(); -} - -bool ShouldAllowAccessFor(nsPIDOMWindowInner* aWindow, nsIURI* aURI, - uint32_t* aRejectedReason) { - MOZ_ASSERT(aWindow); - MOZ_ASSERT(aURI); - - // Let's avoid a null check on aRejectedReason everywhere else. - uint32_t rejectedReason = 0; - if (!aRejectedReason) { - aRejectedReason = &rejectedReason; - } - - LOG_SPEC(("Computing whether window %p has access to URI %s", aWindow, _spec), - aURI); - - nsGlobalWindowInner* innerWindow = nsGlobalWindowInner::Cast(aWindow); - Document* document = innerWindow->GetExtantDoc(); - if (!document) { - LOG(("Our window has no document")); - return false; - } - - uint32_t cookiePermission = CheckCookiePermissionForPrincipal( - document->CookieJarSettings(), document->NodePrincipal()); - if (cookiePermission != nsICookiePermission::ACCESS_DEFAULT) { - LOG( - ("CheckCookiePermissionForPrincipal() returned a non-default access " - "code (%d) for window's principal, returning %s", - int(cookiePermission), - cookiePermission != nsICookiePermission::ACCESS_DENY ? "success" - : "failure")); - if (cookiePermission != nsICookiePermission::ACCESS_DENY) { - return true; - } - - *aRejectedReason = - nsIWebProgressListener::STATE_COOKIES_BLOCKED_BY_PERMISSION; - return false; - } - - int32_t behavior = CookiesBehavior(document); - if (behavior == nsICookieService::BEHAVIOR_ACCEPT) { - LOG(("The cookie behavior pref mandates accepting all cookies!")); - return true; - } - - if (ContentBlockingAllowList::Check(aWindow)) { - return true; - } - - if (behavior == nsICookieService::BEHAVIOR_REJECT) { - LOG(("The cookie behavior pref mandates rejecting all cookies!")); - *aRejectedReason = nsIWebProgressListener::STATE_COOKIES_BLOCKED_ALL; - return false; - } - - // As a performance optimization, we only perform this check for - // BEHAVIOR_REJECT_FOREIGN and BEHAVIOR_LIMIT_FOREIGN. For - // BEHAVIOR_REJECT_TRACKER and BEHAVIOR_REJECT_TRACKER_AND_PARTITION_FOREIGN, - // third-partiness is implicily checked later below. - if (behavior != nsICookieService::BEHAVIOR_REJECT_TRACKER && - behavior != - nsICookieService::BEHAVIOR_REJECT_TRACKER_AND_PARTITION_FOREIGN) { - // Let's check if this is a 3rd party context. - if (!AntiTrackingUtils::IsThirdPartyWindow(aWindow, aURI)) { - LOG(("Our window isn't a third-party window")); - return true; - } - } - - if ((behavior == nsICookieService::BEHAVIOR_REJECT_FOREIGN && - !CookieJarSettings::IsRejectThirdPartyWithExceptions(behavior)) || - behavior == nsICookieService::BEHAVIOR_LIMIT_FOREIGN) { - // XXX For non-cookie forms of storage, we handle BEHAVIOR_LIMIT_FOREIGN by - // simply rejecting the request to use the storage. In the future, if we - // change the meaning of BEHAVIOR_LIMIT_FOREIGN to be one which makes sense - // for non-cookie storage types, this may change. - LOG(("Nothing more to do due to the behavior code %d", int(behavior))); - *aRejectedReason = nsIWebProgressListener::STATE_COOKIES_BLOCKED_FOREIGN; - return false; - } - - // The document has been allowlisted. We can return from here directly. - if (document->HasStorageAccessPermissionGrantedByAllowList()) { - return true; - } - - MOZ_ASSERT( - CookieJarSettings::IsRejectThirdPartyWithExceptions(behavior) || - behavior == nsICookieService::BEHAVIOR_REJECT_TRACKER || - behavior == - nsICookieService::BEHAVIOR_REJECT_TRACKER_AND_PARTITION_FOREIGN); - - uint32_t blockedReason = - nsIWebProgressListener::STATE_COOKIES_BLOCKED_TRACKER; - - if (behavior == nsICookieService::BEHAVIOR_REJECT_TRACKER) { - if (!nsContentUtils::IsThirdPartyTrackingResourceWindow(aWindow)) { - LOG(("Our window isn't a third-party tracking window")); - return true; - } - - nsCOMPtr classifiedChannel = - do_QueryInterface(document->GetChannel()); - if (classifiedChannel) { - uint32_t classificationFlags = - classifiedChannel->GetThirdPartyClassificationFlags(); - if (classificationFlags & nsIClassifiedChannel::ClassificationFlags:: - CLASSIFIED_SOCIALTRACKING) { - blockedReason = - nsIWebProgressListener::STATE_COOKIES_BLOCKED_SOCIALTRACKER; - } - } - } else if (behavior == - nsICookieService::BEHAVIOR_REJECT_TRACKER_AND_PARTITION_FOREIGN) { - if (nsContentUtils::IsThirdPartyTrackingResourceWindow(aWindow)) { - // fall through - } else if (AntiTrackingUtils::IsThirdPartyWindow(aWindow, aURI)) { - LOG(("We're in the third-party context, storage should be partitioned")); - // fall through, but remember that we're partitioning. - blockedReason = nsIWebProgressListener::STATE_COOKIES_PARTITIONED_FOREIGN; - } else { - LOG(("Our window isn't a third-party window, storage is allowed")); - return true; - } - } else { - MOZ_ASSERT(CookieJarSettings::IsRejectThirdPartyWithExceptions(behavior)); - if (RejectForeignAllowList::Check(document)) { - LOG(("This window is exceptionlisted for reject foreign")); - return true; - } - - blockedReason = nsIWebProgressListener::STATE_COOKIES_PARTITIONED_FOREIGN; - } - - Document* doc = aWindow->GetExtantDoc(); - // Make sure storage access isn't disabled - if (doc && (doc->StorageAccessSandboxed())) { - LOG(("Our document is sandboxed")); - *aRejectedReason = blockedReason; - return false; - } - - // Document::HasStoragePermission first checks if storage access granted is - // cached in the inner window, if no, it then checks the storage permission - // flag in the channel's loadinfo - bool allowed = document->HasStorageAccessPermissionGranted(); - - if (!allowed) { - *aRejectedReason = blockedReason; - } else { - if (MOZ_LOG_TEST(gAntiTrackingLog, mozilla::LogLevel::Debug) && - aWindow->HasStorageAccessPermissionGranted()) { - LOG(("Permission stored in the window. All good.")); - } - } - - return allowed; -} - -bool ShouldAllowAccessFor(nsIChannel* aChannel, nsIURI* aURI, - uint32_t* aRejectedReason) { - MOZ_ASSERT(aURI); - MOZ_ASSERT(aChannel); - - // Let's avoid a null check on aRejectedReason everywhere else. - uint32_t rejectedReason = 0; - if (!aRejectedReason) { - aRejectedReason = &rejectedReason; - } - - nsIScriptSecurityManager* ssm = - nsScriptSecurityManager::GetScriptSecurityManager(); - MOZ_ASSERT(ssm); - - nsCOMPtr channelURI; - nsresult rv = NS_GetFinalChannelURI(aChannel, getter_AddRefs(channelURI)); - if (NS_FAILED(rv)) { - LOG(("Failed to get the channel final URI, bail out early")); - return true; - } - LOG_SPEC( - ("Computing whether channel %p has access to URI %s", aChannel, _spec), - channelURI); - - nsCOMPtr loadInfo = aChannel->LoadInfo(); - nsCOMPtr cookieJarSettings; - rv = loadInfo->GetCookieJarSettings(getter_AddRefs(cookieJarSettings)); - if (NS_WARN_IF(NS_FAILED(rv))) { - LOG( - ("Failed to get the cookie jar settings from the loadinfo, bail out " - "early")); - return true; - } - - nsCOMPtr channelPrincipal; - rv = ssm->GetChannelURIPrincipal(aChannel, getter_AddRefs(channelPrincipal)); - if (NS_WARN_IF(NS_FAILED(rv))) { - LOG(("No channel principal, bail out early")); - return false; - } - - uint32_t cookiePermission = - CheckCookiePermissionForPrincipal(cookieJarSettings, channelPrincipal); - if (cookiePermission != nsICookiePermission::ACCESS_DEFAULT) { - LOG( - ("CheckCookiePermissionForPrincipal() returned a non-default access " - "code (%d) for channel's principal, returning %s", - int(cookiePermission), - cookiePermission != nsICookiePermission::ACCESS_DENY ? "success" - : "failure")); - if (cookiePermission != nsICookiePermission::ACCESS_DENY) { - return true; - } - - *aRejectedReason = - nsIWebProgressListener::STATE_COOKIES_BLOCKED_BY_PERMISSION; - return false; - } - - if (!channelURI) { - LOG(("No channel uri, bail out early")); - return false; - } - - int32_t behavior = CookiesBehavior(loadInfo, channelURI); - if (behavior == nsICookieService::BEHAVIOR_ACCEPT) { - LOG(("The cookie behavior pref mandates accepting all cookies!")); - return true; - } - - nsCOMPtr httpChannel = do_QueryInterface(aChannel); - - if (httpChannel && ContentBlockingAllowList::Check(httpChannel)) { - return true; - } - - if (behavior == nsICookieService::BEHAVIOR_REJECT) { - LOG(("The cookie behavior pref mandates rejecting all cookies!")); - *aRejectedReason = nsIWebProgressListener::STATE_COOKIES_BLOCKED_ALL; - return false; - } - - nsCOMPtr thirdPartyUtil = - components::ThirdPartyUtil::Service(); - if (!thirdPartyUtil) { - LOG(("No thirdPartyUtil, bail out early")); - return true; - } - - bool thirdParty = false; - rv = thirdPartyUtil->IsThirdPartyChannel(aChannel, aURI, &thirdParty); - // Grant if it's not a 3rd party. - // Be careful to check the return value of IsThirdPartyChannel, since - // IsThirdPartyChannel() will fail if the channel's loading principal is the - // system principal... - if (NS_SUCCEEDED(rv) && !thirdParty) { - LOG(("Our channel isn't a third-party channel")); - return true; - } - - if ((behavior == nsICookieService::BEHAVIOR_REJECT_FOREIGN && - !CookieJarSettings::IsRejectThirdPartyWithExceptions(behavior)) || - behavior == nsICookieService::BEHAVIOR_LIMIT_FOREIGN) { - // XXX For non-cookie forms of storage, we handle BEHAVIOR_LIMIT_FOREIGN by - // simply rejecting the request to use the storage. In the future, if we - // change the meaning of BEHAVIOR_LIMIT_FOREIGN to be one which makes sense - // for non-cookie storage types, this may change. - LOG(("Nothing more to do due to the behavior code %d", int(behavior))); - *aRejectedReason = nsIWebProgressListener::STATE_COOKIES_BLOCKED_FOREIGN; - return false; - } - - // The channel has been allowlisted. We can return from here. - if (loadInfo->GetStoragePermission() == - nsILoadInfo::StoragePermissionAllowListed) { - return true; - } - - MOZ_ASSERT( - CookieJarSettings::IsRejectThirdPartyWithExceptions(behavior) || - behavior == nsICookieService::BEHAVIOR_REJECT_TRACKER || - behavior == - nsICookieService::BEHAVIOR_REJECT_TRACKER_AND_PARTITION_FOREIGN); - - uint32_t blockedReason = - nsIWebProgressListener::STATE_COOKIES_BLOCKED_TRACKER; - - // Not a tracker. - nsCOMPtr classifiedChannel = - do_QueryInterface(aChannel); - if (behavior == nsICookieService::BEHAVIOR_REJECT_TRACKER) { - if (classifiedChannel) { - if (!classifiedChannel->IsThirdPartyTrackingResource()) { - LOG(("Our channel isn't a third-party tracking channel")); - return true; - } - - uint32_t classificationFlags = - classifiedChannel->GetThirdPartyClassificationFlags(); - if (classificationFlags & nsIClassifiedChannel::ClassificationFlags:: - CLASSIFIED_SOCIALTRACKING) { - blockedReason = - nsIWebProgressListener::STATE_COOKIES_BLOCKED_SOCIALTRACKER; - } - } - } else if (behavior == - nsICookieService::BEHAVIOR_REJECT_TRACKER_AND_PARTITION_FOREIGN) { - if (classifiedChannel && - classifiedChannel->IsThirdPartyTrackingResource()) { - // fall through - } else if (AntiTrackingUtils::IsThirdPartyChannel(aChannel)) { - LOG(("We're in the third-party context, storage should be partitioned")); - // fall through but remember that we're partitioning. - blockedReason = nsIWebProgressListener::STATE_COOKIES_PARTITIONED_FOREIGN; - } else { - LOG(("Our channel isn't a third-party channel, storage is allowed")); - return true; - } - } else { - MOZ_ASSERT(CookieJarSettings::IsRejectThirdPartyWithExceptions(behavior)); - if (httpChannel && RejectForeignAllowList::Check(httpChannel)) { - LOG(("This channel is exceptionlisted")); - return true; - } - blockedReason = nsIWebProgressListener::STATE_COOKIES_BLOCKED_FOREIGN; - } - - RefPtr targetBC; - rv = loadInfo->GetTargetBrowsingContext(getter_AddRefs(targetBC)); - if (!targetBC || NS_WARN_IF(NS_FAILED(rv))) { - LOG(("Failed to get the channel's target browsing context")); - return false; - } - - if (Document::StorageAccessSandboxed(targetBC->GetSandboxFlags())) { - LOG(("Our document is sandboxed")); - *aRejectedReason = blockedReason; - return false; - } - - // Let's see if we have to grant the access for this particular channel. - - // HasStorageAccessPermissionGranted only applies to channels that load - // documents, for sub-resources loads, just returns the result from loadInfo. - bool isDocument = false; - aChannel->GetIsDocument(&isDocument); - - if (isDocument) { - nsCOMPtr inner = - AntiTrackingUtils::GetInnerWindow(targetBC); - if (inner && inner->HasStorageAccessPermissionGranted()) { - LOG(("Permission stored in the window. All good.")); - return true; - } - } - - bool allowed = - loadInfo->GetStoragePermission() != nsILoadInfo::NoStoragePermission; - if (!allowed) { - *aRejectedReason = blockedReason; - } - - return allowed; -} - -bool ShouldAllowAccessFor(nsIPrincipal* aPrincipal, - nsICookieJarSettings* aCookieJarSettings) { - MOZ_ASSERT(aPrincipal); - MOZ_ASSERT(aCookieJarSettings); - - uint32_t access = nsICookiePermission::ACCESS_DEFAULT; - if (aPrincipal->GetIsContentPrincipal()) { - PermissionManager* permManager = PermissionManager::GetInstance(); - if (permManager) { - Unused << NS_WARN_IF(NS_FAILED(permManager->TestPermissionFromPrincipal( - aPrincipal, "cookie"_ns, &access))); - } - } - - if (access != nsICookiePermission::ACCESS_DEFAULT) { - return access != nsICookiePermission::ACCESS_DENY; - } - - int32_t behavior = CookiesBehavior(aPrincipal, aCookieJarSettings); - return behavior != nsICookieService::BEHAVIOR_REJECT; -} - -/* static */ -bool ApproximateAllowAccessForWithoutChannel( - nsPIDOMWindowInner* aFirstPartyWindow, nsIURI* aURI) { - MOZ_ASSERT(aFirstPartyWindow); - MOZ_ASSERT(aURI); - - LOG_SPEC( - ("Computing a best guess as to whether window %p has access to URI %s", - aFirstPartyWindow, _spec), - aURI); - - Document* parentDocument = - nsGlobalWindowInner::Cast(aFirstPartyWindow)->GetExtantDoc(); - if (NS_WARN_IF(!parentDocument)) { - LOG(("Failed to get the first party window's document")); - return false; - } - - if (!parentDocument->CookieJarSettings()->GetRejectThirdPartyContexts()) { - LOG(("Disabled by the pref (%d), bail out early", - parentDocument->CookieJarSettings()->GetCookieBehavior())); - return true; - } - - if (ContentBlockingAllowList::Check(aFirstPartyWindow)) { - return true; - } - - if (!AntiTrackingUtils::IsThirdPartyWindow(aFirstPartyWindow, aURI)) { - LOG(("Our window isn't a third-party window")); - return true; - } - - uint32_t cookiePermission = CheckCookiePermissionForPrincipal( - parentDocument->CookieJarSettings(), parentDocument->NodePrincipal()); - if (cookiePermission != nsICookiePermission::ACCESS_DEFAULT) { - LOG( - ("CheckCookiePermissionForPrincipal() returned a non-default access " - "code (%d), returning %s", - int(cookiePermission), - cookiePermission != nsICookiePermission::ACCESS_DENY ? "success" - : "failure")); - return cookiePermission != nsICookiePermission::ACCESS_DENY; - } - - nsAutoCString origin; - nsresult rv = nsContentUtils::GetASCIIOrigin(aURI, origin); - if (NS_WARN_IF(NS_FAILED(rv))) { - LOG_SPEC(("Failed to compute the origin from %s", _spec), aURI); - return false; - } - - nsIPrincipal* parentPrincipal = parentDocument->NodePrincipal(); - - nsAutoCString type; - AntiTrackingUtils::CreateStoragePermissionKey(origin, type); - - return AntiTrackingUtils::CheckStoragePermission( - parentPrincipal, type, - nsContentUtils::IsInPrivateBrowsing(parentDocument), nullptr, 0); -} } // namespace mozilla diff --git a/toolkit/components/antitracking/StorageAccess.h b/toolkit/components/antitracking/StorageAccess.h index 339ea6fb9929..3caf02b11fce 100644 --- a/toolkit/components/antitracking/StorageAccess.h +++ b/toolkit/components/antitracking/StorageAccess.h @@ -120,42 +120,6 @@ bool StoragePartitioningEnabled(StorageAccess aAccess, bool StoragePartitioningEnabled(uint32_t aRejectedReason, nsICookieJarSettings* aCookieJarSettings); -// This method returns true if the URI has first party storage access when -// loaded inside the passed 3rd party context tracking resource window. -// If the window is first party context, please use -// ApproximateAllowAccessForWithoutChannel(); -// -// aRejectedReason could be set to one of these values if passed and if the -// storage permission is not granted: -// * nsIWebProgressListener::STATE_COOKIES_BLOCKED_BY_PERMISSION -// * nsIWebProgressListener::STATE_COOKIES_BLOCKED_TRACKER -// * nsIWebProgressListener::STATE_COOKIES_BLOCKED_SOCIALTRACKER -// * nsIWebProgressListener::STATE_COOKIES_BLOCKED_ALL -// * nsIWebProgressListener::STATE_COOKIES_BLOCKED_FOREIGN -bool ShouldAllowAccessFor(nsPIDOMWindowInner* a3rdPartyTrackingWindow, - nsIURI* aURI, uint32_t* aRejectedReason); - -// Note: you should use ShouldAllowAccessFor() passing the nsIChannel! Use -// this method _only_ if the channel is not available. For first party -// window, it's impossible to know if the aURI is a tracking resource -// synchronously, so here we return the best guest: if we are sure that the -// permission is granted for the origin of aURI, this method returns true, -// otherwise false. -bool ApproximateAllowAccessForWithoutChannel( - nsPIDOMWindowInner* aFirstPartyWindow, nsIURI* aURI); - -// It returns true if the URI has access to the first party storage. -// aChannel can be a 3rd party channel, or not. -// See ShouldAllowAccessFor(window) to see the possible values of -// aRejectedReason. -bool ShouldAllowAccessFor(nsIChannel* aChannel, nsIURI* aURI, - uint32_t* aRejectedReason); - -// This method checks if the principal has the permission to access to the -// first party storage. -bool ShouldAllowAccessFor(nsIPrincipal* aPrincipal, - nsICookieJarSettings* aCookieJarSettings); - } // namespace mozilla #endif // mozilla_StorageAccess_h diff --git a/toolkit/components/antitracking/StoragePrincipalHelper.cpp b/toolkit/components/antitracking/StoragePrincipalHelper.cpp index eca933ec2f28..71f2ffc34829 100644 --- a/toolkit/components/antitracking/StoragePrincipalHelper.cpp +++ b/toolkit/components/antitracking/StoragePrincipalHelper.cpp @@ -7,6 +7,7 @@ #include "StoragePrincipalHelper.h" #include "mozilla/ipc/PBackgroundSharedTypes.h" +#include "mozilla/ContentBlocking.h" #include "mozilla/dom/WorkerPrivate.h" #include "mozilla/ScopeExit.h" #include "mozilla/StaticPrefs_privacy.h" @@ -30,7 +31,7 @@ bool ShouldPartitionChannel(nsIChannel* aChannel, } uint32_t rejectedReason = 0; - if (ShouldAllowAccessFor(aChannel, uri, &rejectedReason)) { + if (ContentBlocking::ShouldAllowAccessFor(aChannel, uri, &rejectedReason)) { return false; } diff --git a/toolkit/components/antitracking/moz.build b/toolkit/components/antitracking/moz.build index 2f7dbd3c7bfd..887c409e6a69 100644 --- a/toolkit/components/antitracking/moz.build +++ b/toolkit/components/antitracking/moz.build @@ -38,6 +38,7 @@ EXPORTS.mozilla = [ "AntiTrackingIPCUtils.h", "AntiTrackingRedirectHeuristic.h", "AntiTrackingUtils.h", + "ContentBlocking.h", "ContentBlockingAllowList.h", "ContentBlockingLog.h", "ContentBlockingNotifier.h", @@ -45,9 +46,7 @@ EXPORTS.mozilla = [ "ContentBlockingUserInteraction.h", "DynamicFpiRedirectHeuristic.h", "PartitioningExceptionList.h", - "RejectForeignAllowList.h", "StorageAccess.h", - "StorageAccessAPIHelper.h", "StoragePrincipalHelper.h", "URLDecorationStripper.h", "URLQueryStringStripper.h", @@ -56,6 +55,7 @@ EXPORTS.mozilla = [ UNIFIED_SOURCES += [ "AntiTrackingRedirectHeuristic.cpp", "AntiTrackingUtils.cpp", + "ContentBlocking.cpp", "ContentBlockingAllowList.cpp", "ContentBlockingLog.cpp", "ContentBlockingNotifier.cpp", @@ -66,7 +66,6 @@ UNIFIED_SOURCES += [ "RejectForeignAllowList.cpp", "SettingsChangeObserver.cpp", "StorageAccess.cpp", - "StorageAccessAPIHelper.cpp", "StoragePrincipalHelper.cpp", "TemporaryAccessGrantObserver.cpp", "URLDecorationStripper.cpp", diff --git a/tools/lint/rejected-words.yml b/tools/lint/rejected-words.yml index 2ab2d916649b..17f438960d75 100644 --- a/tools/lint/rejected-words.yml +++ b/tools/lint/rejected-words.yml @@ -323,7 +323,7 @@ avoid-blacklist-and-whitelist: - toolkit/actors/RemotePageChild.jsm - toolkit/actors/WebChannelChild.jsm - toolkit/components/aboutperformance/content/aboutPerformance.js - - toolkit/components/antitracking/StorageAccessAPIHelper.cpp + - toolkit/components/antitracking/ContentBlocking.cpp - toolkit/components/antitracking/PurgeTrackerService.jsm - toolkit/components/antitracking/StorageAccess.cpp - toolkit/components/antitracking/test/browser/antitracking_head.js