summaryrefslogtreecommitdiffstats
path: root/toolkit/components/antitracking/StoragePrincipalHelper.cpp
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-07 17:32:43 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-07 17:32:43 +0000
commit6bf0a5cb5034a7e684dcc3500e841785237ce2dd (patch)
treea68f146d7fa01f0134297619fbe7e33db084e0aa /toolkit/components/antitracking/StoragePrincipalHelper.cpp
parentInitial commit. (diff)
downloadthunderbird-6bf0a5cb5034a7e684dcc3500e841785237ce2dd.tar.xz
thunderbird-6bf0a5cb5034a7e684dcc3500e841785237ce2dd.zip
Adding upstream version 1:115.7.0.upstream/1%115.7.0upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'toolkit/components/antitracking/StoragePrincipalHelper.cpp')
-rw-r--r--toolkit/components/antitracking/StoragePrincipalHelper.cpp677
1 files changed, 677 insertions, 0 deletions
diff --git a/toolkit/components/antitracking/StoragePrincipalHelper.cpp b/toolkit/components/antitracking/StoragePrincipalHelper.cpp
new file mode 100644
index 0000000000..10be1112ca
--- /dev/null
+++ b/toolkit/components/antitracking/StoragePrincipalHelper.cpp
@@ -0,0 +1,677 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "StoragePrincipalHelper.h"
+
+#include "mozilla/ipc/PBackgroundSharedTypes.h"
+#include "mozilla/dom/Document.h"
+#include "mozilla/dom/WorkerPrivate.h"
+#include "mozilla/net/CookieJarSettings.h"
+#include "mozilla/ScopeExit.h"
+#include "mozilla/StaticPrefs_privacy.h"
+#include "mozilla/StorageAccess.h"
+#include "nsContentUtils.h"
+#include "nsICookieJarSettings.h"
+#include "nsICookieService.h"
+#include "nsIDocShell.h"
+#include "nsIEffectiveTLDService.h"
+#include "nsIPrivateBrowsingChannel.h"
+#include "AntiTrackingUtils.h"
+
+namespace mozilla {
+
+namespace {
+
+bool ShouldPartitionChannel(nsIChannel* aChannel,
+ nsICookieJarSettings* aCookieJarSettings) {
+ MOZ_ASSERT(aChannel);
+
+ nsCOMPtr<nsIURI> uri;
+ nsresult rv = aChannel->GetURI(getter_AddRefs(uri));
+ if (NS_FAILED(rv)) {
+ return false;
+ }
+
+ uint32_t rejectedReason = 0;
+ if (ShouldAllowAccessFor(aChannel, uri, &rejectedReason)) {
+ return false;
+ }
+
+ // Let's use the storage principal only if we need to partition the cookie
+ // jar. We use the lower-level ContentBlocking API here to ensure this
+ // check doesn't send notifications.
+ if (!ShouldPartitionStorage(rejectedReason) ||
+ !StoragePartitioningEnabled(rejectedReason, aCookieJarSettings)) {
+ return false;
+ }
+
+ return true;
+}
+
+bool ChooseOriginAttributes(nsIChannel* aChannel, OriginAttributes& aAttrs,
+ bool aForcePartitionedPrincipal) {
+ MOZ_ASSERT(aChannel);
+
+ nsCOMPtr<nsILoadInfo> loadInfo = aChannel->LoadInfo();
+ nsCOMPtr<nsICookieJarSettings> cjs;
+ Unused << loadInfo->GetCookieJarSettings(getter_AddRefs(cjs));
+
+ if (!aForcePartitionedPrincipal && !ShouldPartitionChannel(aChannel, cjs)) {
+ return false;
+ }
+
+ nsAutoString partitionKey;
+ Unused << cjs->GetPartitionKey(partitionKey);
+
+ if (!partitionKey.IsEmpty()) {
+ aAttrs.SetPartitionKey(partitionKey);
+ return true;
+ }
+
+ // Fallback to get first-party domain from top-level principal when we can't
+ // get it from CookieJarSetting. This might happen when a channel is not
+ // opened via http, for example, about page.
+ nsCOMPtr<nsIPrincipal> toplevelPrincipal = loadInfo->GetTopLevelPrincipal();
+ if (!toplevelPrincipal) {
+ return false;
+ }
+ // Cast to BasePrincipal to continue to get acess to GetUri()
+ auto* basePrin = BasePrincipal::Cast(toplevelPrincipal);
+ nsCOMPtr<nsIURI> principalURI;
+
+ nsresult rv = basePrin->GetURI(getter_AddRefs(principalURI));
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return false;
+ }
+
+ aAttrs.SetPartitionKey(principalURI);
+ return true;
+}
+
+bool VerifyValidPartitionedPrincipalInfoForPrincipalInfoInternal(
+ const ipc::PrincipalInfo& aPartitionedPrincipalInfo,
+ const ipc::PrincipalInfo& aPrincipalInfo,
+ bool aIgnoreSpecForContentPrincipal,
+ bool aIgnoreDomainForContentPrincipal) {
+ if (aPartitionedPrincipalInfo.type() != aPrincipalInfo.type()) {
+ return false;
+ }
+
+ if (aPartitionedPrincipalInfo.type() ==
+ mozilla::ipc::PrincipalInfo::TContentPrincipalInfo) {
+ const mozilla::ipc::ContentPrincipalInfo& spInfo =
+ aPartitionedPrincipalInfo.get_ContentPrincipalInfo();
+ const mozilla::ipc::ContentPrincipalInfo& pInfo =
+ aPrincipalInfo.get_ContentPrincipalInfo();
+
+ return spInfo.attrs().EqualsIgnoringPartitionKey(pInfo.attrs()) &&
+ spInfo.originNoSuffix() == pInfo.originNoSuffix() &&
+ (aIgnoreSpecForContentPrincipal || spInfo.spec() == pInfo.spec()) &&
+ (aIgnoreDomainForContentPrincipal ||
+ spInfo.domain() == pInfo.domain()) &&
+ spInfo.baseDomain() == pInfo.baseDomain();
+ }
+
+ if (aPartitionedPrincipalInfo.type() ==
+ mozilla::ipc::PrincipalInfo::TSystemPrincipalInfo) {
+ // Nothing to check here.
+ return true;
+ }
+
+ if (aPartitionedPrincipalInfo.type() ==
+ mozilla::ipc::PrincipalInfo::TNullPrincipalInfo) {
+ const mozilla::ipc::NullPrincipalInfo& spInfo =
+ aPartitionedPrincipalInfo.get_NullPrincipalInfo();
+ const mozilla::ipc::NullPrincipalInfo& pInfo =
+ aPrincipalInfo.get_NullPrincipalInfo();
+
+ return spInfo.spec() == pInfo.spec() &&
+ spInfo.attrs().EqualsIgnoringPartitionKey(pInfo.attrs());
+ }
+
+ if (aPartitionedPrincipalInfo.type() ==
+ mozilla::ipc::PrincipalInfo::TExpandedPrincipalInfo) {
+ const mozilla::ipc::ExpandedPrincipalInfo& spInfo =
+ aPartitionedPrincipalInfo.get_ExpandedPrincipalInfo();
+ const mozilla::ipc::ExpandedPrincipalInfo& pInfo =
+ aPrincipalInfo.get_ExpandedPrincipalInfo();
+
+ if (!spInfo.attrs().EqualsIgnoringPartitionKey(pInfo.attrs())) {
+ return false;
+ }
+
+ if (spInfo.allowlist().Length() != pInfo.allowlist().Length()) {
+ return false;
+ }
+
+ for (uint32_t i = 0; i < spInfo.allowlist().Length(); ++i) {
+ if (!VerifyValidPartitionedPrincipalInfoForPrincipalInfoInternal(
+ spInfo.allowlist()[i], pInfo.allowlist()[i],
+ aIgnoreSpecForContentPrincipal,
+ aIgnoreDomainForContentPrincipal)) {
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ MOZ_CRASH("Invalid principalInfo type");
+ return false;
+}
+
+} // namespace
+
+// static
+nsresult StoragePrincipalHelper::Create(nsIChannel* aChannel,
+ nsIPrincipal* aPrincipal,
+ bool aForceIsolation,
+ nsIPrincipal** aStoragePrincipal) {
+ MOZ_ASSERT(aChannel);
+ MOZ_ASSERT(aPrincipal);
+ MOZ_ASSERT(aStoragePrincipal);
+
+ auto scopeExit = MakeScopeExit([&] {
+ nsCOMPtr<nsIPrincipal> storagePrincipal = aPrincipal;
+ storagePrincipal.forget(aStoragePrincipal);
+ });
+
+ OriginAttributes attrs = aPrincipal->OriginAttributesRef();
+ if (!ChooseOriginAttributes(aChannel, attrs, aForceIsolation)) {
+ return NS_OK;
+ }
+
+ scopeExit.release();
+
+ nsCOMPtr<nsIPrincipal> storagePrincipal =
+ BasePrincipal::Cast(aPrincipal)->CloneForcingOriginAttributes(attrs);
+
+ // If aPrincipal is not a ContentPrincipal, e.g. a NullPrincipal, the clone
+ // call will return a nullptr.
+ NS_ENSURE_TRUE(storagePrincipal, NS_ERROR_FAILURE);
+
+ storagePrincipal.forget(aStoragePrincipal);
+ return NS_OK;
+}
+
+// static
+nsresult StoragePrincipalHelper::CreatePartitionedPrincipalForServiceWorker(
+ nsIPrincipal* aPrincipal, nsICookieJarSettings* aCookieJarSettings,
+ nsIPrincipal** aPartitionedPrincipal) {
+ MOZ_ASSERT(aPrincipal);
+ MOZ_ASSERT(aPartitionedPrincipal);
+
+ OriginAttributes attrs = aPrincipal->OriginAttributesRef();
+
+ nsAutoString partitionKey;
+ Unused << aCookieJarSettings->GetPartitionKey(partitionKey);
+
+ if (!partitionKey.IsEmpty()) {
+ attrs.SetPartitionKey(partitionKey);
+ }
+
+ nsCOMPtr<nsIPrincipal> partitionedPrincipal =
+ BasePrincipal::Cast(aPrincipal)->CloneForcingOriginAttributes(attrs);
+
+ // If aPrincipal is not a ContentPrincipal, e.g. a NullPrincipal, the clone
+ // call will return a nullptr.
+ NS_ENSURE_TRUE(partitionedPrincipal, NS_ERROR_FAILURE);
+
+ partitionedPrincipal.forget(aPartitionedPrincipal);
+ return NS_OK;
+}
+
+// static
+nsresult
+StoragePrincipalHelper::PrepareEffectiveStoragePrincipalOriginAttributes(
+ nsIChannel* aChannel, OriginAttributes& aOriginAttributes) {
+ MOZ_ASSERT(aChannel);
+
+ ChooseOriginAttributes(aChannel, aOriginAttributes, false);
+ return NS_OK;
+}
+
+// static
+bool StoragePrincipalHelper::VerifyValidStoragePrincipalInfoForPrincipalInfo(
+ const mozilla::ipc::PrincipalInfo& aStoragePrincipalInfo,
+ const mozilla::ipc::PrincipalInfo& aPrincipalInfo) {
+ return VerifyValidPartitionedPrincipalInfoForPrincipalInfoInternal(
+ aStoragePrincipalInfo, aPrincipalInfo, false, false);
+}
+
+// static
+bool StoragePrincipalHelper::VerifyValidClientPrincipalInfoForPrincipalInfo(
+ const mozilla::ipc::PrincipalInfo& aClientPrincipalInfo,
+ const mozilla::ipc::PrincipalInfo& aPrincipalInfo) {
+ return VerifyValidPartitionedPrincipalInfoForPrincipalInfoInternal(
+ aClientPrincipalInfo, aPrincipalInfo, true, true);
+}
+
+// static
+nsresult StoragePrincipalHelper::GetPrincipal(nsIChannel* aChannel,
+ PrincipalType aPrincipalType,
+ nsIPrincipal** aPrincipal) {
+ MOZ_ASSERT(aChannel);
+ MOZ_ASSERT(aPrincipal);
+
+ nsCOMPtr<nsILoadInfo> loadInfo = aChannel->LoadInfo();
+ nsCOMPtr<nsICookieJarSettings> cjs;
+ Unused << loadInfo->GetCookieJarSettings(getter_AddRefs(cjs));
+
+ nsIScriptSecurityManager* ssm = nsContentUtils::GetSecurityManager();
+ MOZ_DIAGNOSTIC_ASSERT(ssm);
+
+ nsCOMPtr<nsIPrincipal> principal;
+ nsCOMPtr<nsIPrincipal> partitionedPrincipal;
+
+ nsresult rv =
+ ssm->GetChannelResultPrincipals(aChannel, getter_AddRefs(principal),
+ getter_AddRefs(partitionedPrincipal));
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+
+ // The aChannel might not be opened in some cases, e.g. getting principal
+ // for the new channel during a redirect. So, the value
+ // `IsThirdPartyToTopWindow` is incorrect in this case because this value is
+ // calculated during opening a channel. And we need to know the value in order
+ // to get the correct principal. To fix this, we compute the value here even
+ // the channel hasn't been opened yet.
+ //
+ // Note that we don't need to compute the value if there is no browsing
+ // context ID assigned. This could happen in a GTest or XPCShell.
+ //
+ // ToDo: The AntiTrackingUtils::ComputeIsThirdPartyToTopWindow() is only
+ // available in the parent process. So, this can only work in the parent
+ // process. It's fine for now, but we should change this to also work in
+ // content processes. Bug 1736452 will address this.
+ //
+ if (XRE_IsParentProcess() && loadInfo->GetBrowsingContextID() != 0) {
+ AntiTrackingUtils::ComputeIsThirdPartyToTopWindow(aChannel);
+ }
+
+ nsCOMPtr<nsIPrincipal> outPrincipal = principal;
+
+ switch (aPrincipalType) {
+ case eRegularPrincipal:
+ break;
+
+ case eStorageAccessPrincipal:
+ if (ShouldPartitionChannel(aChannel, cjs)) {
+ outPrincipal = partitionedPrincipal;
+ }
+ break;
+
+ case ePartitionedPrincipal:
+ outPrincipal = partitionedPrincipal;
+ break;
+
+ case eForeignPartitionedPrincipal:
+ // We only support foreign partitioned principal when dFPI is enabled.
+ if (cjs->GetCookieBehavior() ==
+ nsICookieService::BEHAVIOR_REJECT_TRACKER_AND_PARTITION_FOREIGN &&
+ loadInfo->GetIsThirdPartyContextToTopWindow()) {
+ outPrincipal = partitionedPrincipal;
+ }
+ break;
+ }
+
+ outPrincipal.forget(aPrincipal);
+ return NS_OK;
+}
+
+// static
+nsresult StoragePrincipalHelper::GetPrincipal(nsPIDOMWindowInner* aWindow,
+ PrincipalType aPrincipalType,
+ nsIPrincipal** aPrincipal) {
+ MOZ_ASSERT(aWindow);
+ MOZ_ASSERT(aPrincipal);
+
+ nsCOMPtr<dom::Document> doc = aWindow->GetExtantDoc();
+ NS_ENSURE_STATE(doc);
+
+ nsCOMPtr<nsIPrincipal> outPrincipal;
+
+ switch (aPrincipalType) {
+ case eRegularPrincipal:
+ outPrincipal = doc->NodePrincipal();
+ break;
+
+ case eStorageAccessPrincipal:
+ outPrincipal = doc->EffectiveStoragePrincipal();
+ break;
+
+ case ePartitionedPrincipal:
+ outPrincipal = doc->PartitionedPrincipal();
+ break;
+
+ case eForeignPartitionedPrincipal:
+ // We only support foreign partitioned principal when dFPI is enabled.
+ if (doc->CookieJarSettings()->GetCookieBehavior() ==
+ nsICookieService::BEHAVIOR_REJECT_TRACKER_AND_PARTITION_FOREIGN &&
+ AntiTrackingUtils::IsThirdPartyWindow(aWindow, nullptr)) {
+ outPrincipal = doc->PartitionedPrincipal();
+ } else {
+ outPrincipal = doc->NodePrincipal();
+ }
+ break;
+ }
+
+ outPrincipal.forget(aPrincipal);
+ return NS_OK;
+}
+
+// static
+bool StoragePrincipalHelper::ShouldUsePartitionPrincipalForServiceWorker(
+ nsIDocShell* aDocShell) {
+ MOZ_ASSERT(aDocShell);
+
+ // We don't use the partitioned principal for service workers if it's
+ // disabled.
+ if (!StaticPrefs::privacy_partition_serviceWorkers()) {
+ return false;
+ }
+
+ RefPtr<dom::Document> document = aDocShell->GetExtantDocument();
+
+ // If we cannot get the document from the docShell, we turn to get its
+ // parent's document.
+ if (!document) {
+ nsCOMPtr<nsIDocShellTreeItem> parentItem;
+ aDocShell->GetInProcessSameTypeParent(getter_AddRefs(parentItem));
+
+ if (parentItem) {
+ document = parentItem->GetDocument();
+ }
+ }
+
+ nsCOMPtr<nsICookieJarSettings> cookieJarSettings;
+
+ if (document) {
+ cookieJarSettings = document->CookieJarSettings();
+ } else {
+ // If there was no document, we create one cookieJarSettings here in order
+ // to get the cookieBehavior. We don't need a real value for RFP because
+ // we are only using this object to check default cookie behavior.
+ cookieJarSettings =
+ net::CookieJarSettings::Create(net::CookieJarSettings::eRegular,
+ /* shouldResistFingerpreinting */ false);
+ }
+
+ // We only support partitioned service workers when dFPI is enabled.
+ if (cookieJarSettings->GetCookieBehavior() !=
+ nsICookieService::BEHAVIOR_REJECT_TRACKER_AND_PARTITION_FOREIGN) {
+ return false;
+ }
+
+ // Only the third-party context will need to use the partitioned principal. A
+ // first-party context is still using the regular principal for the service
+ // worker.
+ return AntiTrackingUtils::IsThirdPartyContext(
+ document ? document->GetBrowsingContext()
+ : aDocShell->GetBrowsingContext());
+}
+
+// static
+bool StoragePrincipalHelper::ShouldUsePartitionPrincipalForServiceWorker(
+ dom::WorkerPrivate* aWorkerPrivate) {
+ MOZ_ASSERT(aWorkerPrivate);
+
+ // We don't use the partitioned principal for service workers if it's
+ // disabled.
+ if (!StaticPrefs::privacy_partition_serviceWorkers()) {
+ return false;
+ }
+
+ nsCOMPtr<nsICookieJarSettings> cookieJarSettings =
+ aWorkerPrivate->CookieJarSettings();
+
+ // We only support partitioned service workers when dFPI is enabled.
+ if (cookieJarSettings->GetCookieBehavior() !=
+ nsICookieService::BEHAVIOR_REJECT_TRACKER_AND_PARTITION_FOREIGN) {
+ return false;
+ }
+
+ return aWorkerPrivate->IsThirdPartyContextToTopWindow();
+}
+
+// static
+bool StoragePrincipalHelper::GetOriginAttributes(
+ nsIChannel* aChannel, mozilla::OriginAttributes& aAttributes,
+ StoragePrincipalHelper::PrincipalType aPrincipalType) {
+ nsCOMPtr<nsILoadInfo> loadInfo = aChannel->LoadInfo();
+ loadInfo->GetOriginAttributes(&aAttributes);
+
+ bool isPrivate = false;
+ nsCOMPtr<nsIPrivateBrowsingChannel> pbChannel = do_QueryInterface(aChannel);
+ if (pbChannel) {
+ nsresult rv = pbChannel->GetIsChannelPrivate(&isPrivate);
+ NS_ENSURE_SUCCESS(rv, false);
+ } else {
+ // Some channels may not implement nsIPrivateBrowsingChannel
+ nsCOMPtr<nsILoadContext> loadContext;
+ NS_QueryNotificationCallbacks(aChannel, loadContext);
+ isPrivate = loadContext && loadContext->UsePrivateBrowsing();
+ }
+ aAttributes.SyncAttributesWithPrivateBrowsing(isPrivate);
+
+ nsCOMPtr<nsICookieJarSettings> cjs;
+
+ switch (aPrincipalType) {
+ case eRegularPrincipal:
+ break;
+
+ case eStorageAccessPrincipal:
+ PrepareEffectiveStoragePrincipalOriginAttributes(aChannel, aAttributes);
+ break;
+
+ case ePartitionedPrincipal:
+ ChooseOriginAttributes(aChannel, aAttributes, true);
+ break;
+
+ case eForeignPartitionedPrincipal:
+ Unused << loadInfo->GetCookieJarSettings(getter_AddRefs(cjs));
+
+ // We only support foreign partitioned principal when dFPI is enabled.
+ // Otherwise, we will use the regular principal.
+ if (cjs->GetCookieBehavior() ==
+ nsICookieService::BEHAVIOR_REJECT_TRACKER_AND_PARTITION_FOREIGN &&
+ loadInfo->GetIsThirdPartyContextToTopWindow()) {
+ ChooseOriginAttributes(aChannel, aAttributes, true);
+ }
+ break;
+ }
+
+ return true;
+}
+
+// static
+bool StoragePrincipalHelper::GetRegularPrincipalOriginAttributes(
+ dom::Document* aDocument, OriginAttributes& aAttributes) {
+ aAttributes = mozilla::OriginAttributes();
+ if (!aDocument) {
+ return false;
+ }
+
+ nsCOMPtr<nsILoadGroup> loadGroup = aDocument->GetDocumentLoadGroup();
+ if (loadGroup) {
+ return GetRegularPrincipalOriginAttributes(loadGroup, aAttributes);
+ }
+
+ nsCOMPtr<nsIChannel> channel = aDocument->GetChannel();
+ if (!channel) {
+ return false;
+ }
+
+ return GetOriginAttributes(channel, aAttributes, eRegularPrincipal);
+}
+
+// static
+bool StoragePrincipalHelper::GetRegularPrincipalOriginAttributes(
+ nsILoadGroup* aLoadGroup, OriginAttributes& aAttributes) {
+ aAttributes = mozilla::OriginAttributes();
+ if (!aLoadGroup) {
+ return false;
+ }
+
+ nsCOMPtr<nsIInterfaceRequestor> callbacks;
+ aLoadGroup->GetNotificationCallbacks(getter_AddRefs(callbacks));
+ if (!callbacks) {
+ return false;
+ }
+
+ nsCOMPtr<nsILoadContext> loadContext = do_GetInterface(callbacks);
+ if (!loadContext) {
+ return false;
+ }
+
+ loadContext->GetOriginAttributes(aAttributes);
+ return true;
+}
+
+// static
+bool StoragePrincipalHelper::GetOriginAttributesForNetworkState(
+ nsIChannel* aChannel, OriginAttributes& aAttributes) {
+ return StoragePrincipalHelper::GetOriginAttributes(
+ aChannel, aAttributes,
+ StaticPrefs::privacy_partition_network_state() ? ePartitionedPrincipal
+ : eRegularPrincipal);
+}
+
+// static
+void StoragePrincipalHelper::GetOriginAttributesForNetworkState(
+ dom::Document* aDocument, OriginAttributes& aAttributes) {
+ aAttributes = aDocument->NodePrincipal()->OriginAttributesRef();
+
+ if (!StaticPrefs::privacy_partition_network_state()) {
+ return;
+ }
+
+ aAttributes = aDocument->PartitionedPrincipal()->OriginAttributesRef();
+}
+
+// static
+void StoragePrincipalHelper::UpdateOriginAttributesForNetworkState(
+ nsIURI* aFirstPartyURI, OriginAttributes& aAttributes) {
+ if (!StaticPrefs::privacy_partition_network_state()) {
+ return;
+ }
+
+ aAttributes.SetPartitionKey(aFirstPartyURI);
+}
+
+enum SupportedScheme { HTTP, HTTPS };
+
+static bool GetOriginAttributesWithScheme(nsIChannel* aChannel,
+ OriginAttributes& aAttributes,
+ SupportedScheme aScheme) {
+ const nsString targetScheme = aScheme == HTTP ? u"http"_ns : u"https"_ns;
+ if (!StoragePrincipalHelper::GetOriginAttributesForNetworkState(
+ aChannel, aAttributes)) {
+ return false;
+ }
+
+ if (aAttributes.mPartitionKey.IsEmpty() ||
+ aAttributes.mPartitionKey[0] != '(') {
+ return true;
+ }
+
+ nsAString::const_iterator start, end;
+ aAttributes.mPartitionKey.BeginReading(start);
+ aAttributes.mPartitionKey.EndReading(end);
+
+ MOZ_DIAGNOSTIC_ASSERT(*start == '(');
+ start++;
+
+ nsAString::const_iterator iter(start);
+ bool ok = FindCharInReadable(',', iter, end);
+ MOZ_DIAGNOSTIC_ASSERT(ok);
+
+ if (!ok) {
+ return false;
+ }
+
+ nsAutoString scheme;
+ scheme.Assign(Substring(start, iter));
+
+ if (scheme.Equals(targetScheme)) {
+ return true;
+ }
+
+ nsAutoString key;
+ key += u"("_ns;
+ key += targetScheme;
+ key.Append(Substring(iter, end));
+ aAttributes.SetPartitionKey(key);
+
+ return true;
+}
+
+// static
+bool StoragePrincipalHelper::GetOriginAttributesForHSTS(
+ nsIChannel* aChannel, OriginAttributes& aAttributes) {
+ return GetOriginAttributesWithScheme(aChannel, aAttributes, HTTP);
+}
+
+// static
+bool StoragePrincipalHelper::GetOriginAttributesForHTTPSRR(
+ nsIChannel* aChannel, OriginAttributes& aAttributes) {
+ return GetOriginAttributesWithScheme(aChannel, aAttributes, HTTPS);
+}
+
+// static
+bool StoragePrincipalHelper::GetOriginAttributes(
+ const mozilla::ipc::PrincipalInfo& aPrincipalInfo,
+ OriginAttributes& aAttributes) {
+ aAttributes = mozilla::OriginAttributes();
+
+ using Type = ipc::PrincipalInfo;
+ switch (aPrincipalInfo.type()) {
+ case Type::TContentPrincipalInfo:
+ aAttributes = aPrincipalInfo.get_ContentPrincipalInfo().attrs();
+ break;
+ case Type::TNullPrincipalInfo:
+ aAttributes = aPrincipalInfo.get_NullPrincipalInfo().attrs();
+ break;
+ case Type::TExpandedPrincipalInfo:
+ aAttributes = aPrincipalInfo.get_ExpandedPrincipalInfo().attrs();
+ break;
+ case Type::TSystemPrincipalInfo:
+ break;
+ default:
+ return false;
+ }
+
+ return true;
+}
+
+bool StoragePrincipalHelper::PartitionKeyHasBaseDomain(
+ const nsAString& aPartitionKey, const nsACString& aBaseDomain) {
+ return PartitionKeyHasBaseDomain(aPartitionKey,
+ NS_ConvertUTF8toUTF16(aBaseDomain));
+}
+
+// static
+bool StoragePrincipalHelper::PartitionKeyHasBaseDomain(
+ const nsAString& aPartitionKey, const nsAString& aBaseDomain) {
+ if (aPartitionKey.IsEmpty() || aBaseDomain.IsEmpty()) {
+ return false;
+ }
+
+ nsString scheme;
+ nsString pkBaseDomain;
+ int32_t port;
+ bool success = OriginAttributes::ParsePartitionKey(aPartitionKey, scheme,
+ pkBaseDomain, port);
+
+ if (!success) {
+ return false;
+ }
+
+ return aBaseDomain.Equals(pkBaseDomain);
+}
+
+} // namespace mozilla