summaryrefslogtreecommitdiffstats
path: root/netwerk/cookie/CookieJarSettings.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'netwerk/cookie/CookieJarSettings.cpp')
-rw-r--r--netwerk/cookie/CookieJarSettings.cpp719
1 files changed, 719 insertions, 0 deletions
diff --git a/netwerk/cookie/CookieJarSettings.cpp b/netwerk/cookie/CookieJarSettings.cpp
new file mode 100644
index 0000000000..110f1eef63
--- /dev/null
+++ b/netwerk/cookie/CookieJarSettings.cpp
@@ -0,0 +1,719 @@
+/* -*- 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 "mozilla/AntiTrackingUtils.h"
+#include "mozilla/BasePrincipal.h"
+#include "mozilla/ClearOnShutdown.h"
+#include "mozilla/ContentBlockingAllowList.h"
+#include "mozilla/dom/BrowsingContext.h"
+#include "mozilla/net/CookieJarSettings.h"
+#include "mozilla/net/NeckoChannelParams.h"
+#include "mozilla/Permission.h"
+#include "mozilla/PermissionManager.h"
+#include "mozilla/SchedulerGroup.h"
+#include "mozilla/StaticPrefs_network.h"
+#include "mozilla/Unused.h"
+#include "nsGlobalWindowInner.h"
+#include "nsIPrincipal.h"
+#if defined(MOZ_THUNDERBIRD) || defined(MOZ_SUITE)
+# include "nsIProtocolHandler.h"
+#endif
+#include "nsIClassInfoImpl.h"
+#include "nsIChannel.h"
+#include "nsICookieManager.h"
+#include "nsICookieService.h"
+#include "nsIObjectInputStream.h"
+#include "nsIObjectOutputStream.h"
+#include "nsNetUtil.h"
+
+namespace mozilla {
+namespace net {
+
+NS_IMPL_CLASSINFO(CookieJarSettings, nullptr, nsIClassInfo::THREADSAFE,
+ COOKIEJARSETTINGS_CID)
+
+NS_IMPL_ISUPPORTS_CI(CookieJarSettings, nsICookieJarSettings, nsISerializable)
+
+static StaticRefPtr<CookieJarSettings> sBlockinAll;
+
+namespace {
+
+class PermissionComparator {
+ public:
+ static bool Equals(nsIPermission* aA, nsIPermission* aB) {
+ nsCOMPtr<nsIPrincipal> principalA;
+ nsresult rv = aA->GetPrincipal(getter_AddRefs(principalA));
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return false;
+ }
+
+ nsCOMPtr<nsIPrincipal> principalB;
+ rv = aB->GetPrincipal(getter_AddRefs(principalB));
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return false;
+ }
+
+ bool equals = false;
+ rv = principalA->Equals(principalB, &equals);
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return false;
+ }
+
+ return equals;
+ }
+};
+
+class ReleaseCookiePermissions final : public Runnable {
+ public:
+ explicit ReleaseCookiePermissions(nsTArray<RefPtr<nsIPermission>>&& aArray)
+ : Runnable("ReleaseCookiePermissions"), mArray(std::move(aArray)) {}
+
+ NS_IMETHOD Run() override {
+ MOZ_ASSERT(NS_IsMainThread());
+ mArray.Clear();
+ return NS_OK;
+ }
+
+ private:
+ nsTArray<RefPtr<nsIPermission>> mArray;
+};
+
+} // namespace
+
+// static
+already_AddRefed<nsICookieJarSettings> CookieJarSettings::GetBlockingAll(
+ bool aShouldResistFingerprinting) {
+ MOZ_ASSERT(NS_IsMainThread());
+
+ if (sBlockinAll) {
+ return do_AddRef(sBlockinAll);
+ }
+
+ sBlockinAll = new CookieJarSettings(nsICookieService::BEHAVIOR_REJECT,
+ OriginAttributes::IsFirstPartyEnabled(),
+ aShouldResistFingerprinting, eFixed);
+ ClearOnShutdown(&sBlockinAll);
+
+ return do_AddRef(sBlockinAll);
+}
+
+// static
+already_AddRefed<nsICookieJarSettings> CookieJarSettings::Create(
+ CreateMode aMode, bool aShouldResistFingerprinting) {
+ MOZ_ASSERT(NS_IsMainThread());
+
+ RefPtr<CookieJarSettings> cookieJarSettings;
+
+ switch (aMode) {
+ case eRegular:
+ case ePrivate:
+ cookieJarSettings = new CookieJarSettings(
+ nsICookieManager::GetCookieBehavior(aMode == ePrivate),
+ OriginAttributes::IsFirstPartyEnabled(), aShouldResistFingerprinting,
+ eProgressive);
+ break;
+
+ default:
+ MOZ_CRASH("Unexpected create mode.");
+ }
+
+ return cookieJarSettings.forget();
+}
+
+// static
+already_AddRefed<nsICookieJarSettings> CookieJarSettings::Create(
+ nsIPrincipal* aPrincipal) {
+ MOZ_ASSERT(NS_IsMainThread());
+
+ bool shouldResistFingerprinting =
+ nsContentUtils::ShouldResistFingerprinting_dangerous(
+ aPrincipal, "We are constructing CookieJarSettings here.");
+
+ if (aPrincipal && aPrincipal->OriginAttributesRef().mPrivateBrowsingId > 0) {
+ return Create(ePrivate, shouldResistFingerprinting);
+ }
+
+ return Create(eRegular, shouldResistFingerprinting);
+}
+
+// static
+already_AddRefed<nsICookieJarSettings> CookieJarSettings::Create(
+ uint32_t aCookieBehavior, const nsAString& aPartitionKey,
+ bool aIsFirstPartyIsolated, bool aIsOnContentBlockingAllowList,
+ bool aShouldResistFingerprinting) {
+ MOZ_ASSERT(NS_IsMainThread());
+
+ RefPtr<CookieJarSettings> cookieJarSettings =
+ new CookieJarSettings(aCookieBehavior, aIsFirstPartyIsolated,
+ aShouldResistFingerprinting, eProgressive);
+ cookieJarSettings->mPartitionKey = aPartitionKey;
+ cookieJarSettings->mIsOnContentBlockingAllowList =
+ aIsOnContentBlockingAllowList;
+
+ return cookieJarSettings.forget();
+}
+
+// static
+already_AddRefed<nsICookieJarSettings> CookieJarSettings::CreateForXPCOM() {
+ MOZ_ASSERT(NS_IsMainThread());
+ return Create(eRegular, /* shouldResistFingerprinting */ false);
+}
+
+CookieJarSettings::CookieJarSettings(uint32_t aCookieBehavior,
+ bool aIsFirstPartyIsolated,
+ bool aShouldResistFingerprinting,
+ State aState)
+ : mCookieBehavior(aCookieBehavior),
+ mIsFirstPartyIsolated(aIsFirstPartyIsolated),
+ mIsOnContentBlockingAllowList(false),
+ mIsOnContentBlockingAllowListUpdated(false),
+ mState(aState),
+ mToBeMerged(false),
+ mShouldResistFingerprinting(aShouldResistFingerprinting) {
+ MOZ_ASSERT(NS_IsMainThread());
+ MOZ_ASSERT_IF(
+ mIsFirstPartyIsolated,
+ mCookieBehavior !=
+ nsICookieService::BEHAVIOR_REJECT_TRACKER_AND_PARTITION_FOREIGN);
+}
+
+CookieJarSettings::~CookieJarSettings() {
+ if (!NS_IsMainThread() && !mCookiePermissions.IsEmpty()) {
+ RefPtr<Runnable> r =
+ new ReleaseCookiePermissions(std::move(mCookiePermissions));
+ MOZ_ASSERT(mCookiePermissions.IsEmpty());
+
+ SchedulerGroup::Dispatch(TaskCategory::Other, r.forget());
+ }
+}
+
+NS_IMETHODIMP
+CookieJarSettings::InitWithURI(nsIURI* aURI, bool aIsPrivate) {
+ NS_ENSURE_ARG(aURI);
+
+ mCookieBehavior = nsICookieManager::GetCookieBehavior(aIsPrivate);
+
+ SetPartitionKey(aURI);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+CookieJarSettings::GetCookieBehavior(uint32_t* aCookieBehavior) {
+ *aCookieBehavior = mCookieBehavior;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+CookieJarSettings::GetIsFirstPartyIsolated(bool* aIsFirstPartyIsolated) {
+ *aIsFirstPartyIsolated = mIsFirstPartyIsolated;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+CookieJarSettings::GetShouldResistFingerprinting(
+ bool* aShouldResistFingerprinting) {
+ *aShouldResistFingerprinting = mShouldResistFingerprinting;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+CookieJarSettings::GetRejectThirdPartyContexts(
+ bool* aRejectThirdPartyContexts) {
+ *aRejectThirdPartyContexts =
+ CookieJarSettings::IsRejectThirdPartyContexts(mCookieBehavior);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+CookieJarSettings::GetLimitForeignContexts(bool* aLimitForeignContexts) {
+ *aLimitForeignContexts =
+ mCookieBehavior == nsICookieService::BEHAVIOR_LIMIT_FOREIGN ||
+ (StaticPrefs::privacy_dynamic_firstparty_limitForeign() &&
+ mCookieBehavior ==
+ nsICookieService::BEHAVIOR_REJECT_TRACKER_AND_PARTITION_FOREIGN);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+CookieJarSettings::GetBlockingAllThirdPartyContexts(
+ bool* aBlockingAllThirdPartyContexts) {
+ // 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.
+ *aBlockingAllThirdPartyContexts =
+ mCookieBehavior == nsICookieService::BEHAVIOR_LIMIT_FOREIGN ||
+ (!StaticPrefs::network_cookie_rejectForeignWithExceptions_enabled() &&
+ mCookieBehavior == nsICookieService::BEHAVIOR_REJECT_FOREIGN);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+CookieJarSettings::GetBlockingAllContexts(bool* aBlockingAllContexts) {
+ *aBlockingAllContexts = mCookieBehavior == nsICookieService::BEHAVIOR_REJECT;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+CookieJarSettings::GetPartitionForeign(bool* aPartitionForeign) {
+ *aPartitionForeign =
+ mCookieBehavior ==
+ nsICookieService::BEHAVIOR_REJECT_TRACKER_AND_PARTITION_FOREIGN;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+CookieJarSettings::SetPartitionForeign(bool aPartitionForeign) {
+ if (mIsFirstPartyIsolated) {
+ return NS_OK;
+ }
+
+ if (aPartitionForeign) {
+ mCookieBehavior =
+ nsICookieService::BEHAVIOR_REJECT_TRACKER_AND_PARTITION_FOREIGN;
+ }
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+CookieJarSettings::GetIsOnContentBlockingAllowList(
+ bool* aIsOnContentBlockingAllowList) {
+ *aIsOnContentBlockingAllowList = mIsOnContentBlockingAllowList;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+CookieJarSettings::GetPartitionKey(nsAString& aPartitionKey) {
+ aPartitionKey = mPartitionKey;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+CookieJarSettings::CookiePermission(nsIPrincipal* aPrincipal,
+ uint32_t* aCookiePermission) {
+ MOZ_ASSERT(NS_IsMainThread());
+ NS_ENSURE_ARG_POINTER(aPrincipal);
+ NS_ENSURE_ARG_POINTER(aCookiePermission);
+
+ *aCookiePermission = nsIPermissionManager::UNKNOWN_ACTION;
+
+ nsresult rv;
+
+ // Let's see if we know this permission.
+ if (!mCookiePermissions.IsEmpty()) {
+ for (const RefPtr<nsIPermission>& permission : mCookiePermissions) {
+ bool match = false;
+ rv = permission->Matches(aPrincipal, false, &match);
+ if (NS_WARN_IF(NS_FAILED(rv)) || !match) {
+ continue;
+ }
+
+ rv = permission->GetCapability(aCookiePermission);
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+
+ return NS_OK;
+ }
+ }
+
+ // Let's ask the permission manager.
+ PermissionManager* pm = PermissionManager::GetInstance();
+ if (NS_WARN_IF(!pm)) {
+ return NS_ERROR_FAILURE;
+ }
+
+#if defined(MOZ_THUNDERBIRD) || defined(MOZ_SUITE)
+ // Check if this protocol doesn't allow cookies.
+ bool hasFlags;
+ nsCOMPtr<nsIURI> uri;
+ BasePrincipal::Cast(aPrincipal)->GetURI(getter_AddRefs(uri));
+
+ rv = NS_URIChainHasFlags(uri, nsIProtocolHandler::URI_FORBIDS_COOKIE_ACCESS,
+ &hasFlags);
+ if (NS_FAILED(rv) || hasFlags) {
+ *aCookiePermission = PermissionManager::DENY_ACTION;
+ rv = NS_OK; // Reset, so it's not caught as a bad status after the `else`.
+ } else // Note the tricky `else` which controls the call below.
+#endif
+
+ rv = pm->TestPermissionFromPrincipal(aPrincipal, "cookie"_ns,
+ aCookiePermission);
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+
+ // Let's store the permission, also if the result is UNKNOWN in order to avoid
+ // race conditions.
+
+ nsCOMPtr<nsIPermission> permission =
+ Permission::Create(aPrincipal, "cookie"_ns, *aCookiePermission, 0, 0, 0);
+ if (permission) {
+ mCookiePermissions.AppendElement(permission);
+ }
+
+ mToBeMerged = true;
+ return NS_OK;
+}
+
+void CookieJarSettings::Serialize(CookieJarSettingsArgs& aData) {
+ MOZ_ASSERT(NS_IsMainThread());
+
+ aData.isFixed() = mState == eFixed;
+ aData.cookieBehavior() = mCookieBehavior;
+ aData.isFirstPartyIsolated() = mIsFirstPartyIsolated;
+ aData.shouldResistFingerprinting() = mShouldResistFingerprinting;
+ aData.isOnContentBlockingAllowList() = mIsOnContentBlockingAllowList;
+ aData.partitionKey() = mPartitionKey;
+
+ for (const RefPtr<nsIPermission>& permission : mCookiePermissions) {
+ nsCOMPtr<nsIPrincipal> principal;
+ nsresult rv = permission->GetPrincipal(getter_AddRefs(principal));
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ continue;
+ }
+
+ ipc::PrincipalInfo principalInfo;
+ rv = PrincipalToPrincipalInfo(principal, &principalInfo,
+ true /* aSkipBaseDomain */);
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ continue;
+ }
+
+ uint32_t cookiePermission = 0;
+ rv = permission->GetCapability(&cookiePermission);
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ continue;
+ }
+
+ aData.cookiePermissions().AppendElement(
+ CookiePermissionData(principalInfo, cookiePermission));
+ }
+
+ mToBeMerged = false;
+}
+
+/* static */ void CookieJarSettings::Deserialize(
+ const CookieJarSettingsArgs& aData,
+ nsICookieJarSettings** aCookieJarSettings) {
+ MOZ_ASSERT(NS_IsMainThread());
+
+ CookiePermissionList list;
+ for (const CookiePermissionData& data : aData.cookiePermissions()) {
+ auto principalOrErr = PrincipalInfoToPrincipal(data.principalInfo());
+ if (NS_WARN_IF(principalOrErr.isErr())) {
+ continue;
+ }
+
+ nsCOMPtr<nsIPrincipal> principal = principalOrErr.unwrap();
+
+ nsCOMPtr<nsIPermission> permission = Permission::Create(
+ principal, "cookie"_ns, data.cookiePermission(), 0, 0, 0);
+ if (NS_WARN_IF(!permission)) {
+ continue;
+ }
+
+ list.AppendElement(permission);
+ }
+
+ RefPtr<CookieJarSettings> cookieJarSettings = new CookieJarSettings(
+ aData.cookieBehavior(), aData.isFirstPartyIsolated(),
+ aData.shouldResistFingerprinting(),
+ aData.isFixed() ? eFixed : eProgressive);
+
+ cookieJarSettings->mIsOnContentBlockingAllowList =
+ aData.isOnContentBlockingAllowList();
+ cookieJarSettings->mCookiePermissions = std::move(list);
+ cookieJarSettings->mPartitionKey = aData.partitionKey();
+ cookieJarSettings->mShouldResistFingerprinting =
+ aData.shouldResistFingerprinting();
+
+ cookieJarSettings.forget(aCookieJarSettings);
+}
+
+void CookieJarSettings::Merge(const CookieJarSettingsArgs& aData) {
+ MOZ_ASSERT(NS_IsMainThread());
+ MOZ_ASSERT(
+ mCookieBehavior == aData.cookieBehavior() ||
+ (mCookieBehavior == nsICookieService::BEHAVIOR_REJECT_TRACKER &&
+ aData.cookieBehavior() ==
+ nsICookieService::BEHAVIOR_REJECT_TRACKER_AND_PARTITION_FOREIGN) ||
+ (mCookieBehavior ==
+ nsICookieService::BEHAVIOR_REJECT_TRACKER_AND_PARTITION_FOREIGN &&
+ aData.cookieBehavior() == nsICookieService::BEHAVIOR_REJECT_TRACKER));
+
+ if (mState == eFixed) {
+ return;
+ }
+
+ // Merge cookie behavior pref values
+ if (mCookieBehavior == nsICookieService::BEHAVIOR_REJECT_TRACKER &&
+ aData.cookieBehavior() ==
+ nsICookieService::BEHAVIOR_REJECT_TRACKER_AND_PARTITION_FOREIGN) {
+ // If the other side has decided to partition third-party cookies, update
+ // our side when first-party isolation is disabled.
+ if (!mIsFirstPartyIsolated) {
+ mCookieBehavior =
+ nsICookieService::BEHAVIOR_REJECT_TRACKER_AND_PARTITION_FOREIGN;
+ }
+ }
+ if (mCookieBehavior ==
+ nsICookieService::BEHAVIOR_REJECT_TRACKER_AND_PARTITION_FOREIGN &&
+ aData.cookieBehavior() == nsICookieService::BEHAVIOR_REJECT_TRACKER) {
+ // If we've decided to partition third-party cookies, the other side may not
+ // have caught up yet unless it has first-party isolation enabled.
+ if (aData.isFirstPartyIsolated()) {
+ mCookieBehavior = nsICookieService::BEHAVIOR_REJECT_TRACKER;
+ mIsFirstPartyIsolated = true;
+ }
+ }
+ // Ignore all other cases.
+ MOZ_ASSERT_IF(
+ mIsFirstPartyIsolated,
+ mCookieBehavior !=
+ nsICookieService::BEHAVIOR_REJECT_TRACKER_AND_PARTITION_FOREIGN);
+
+ if (aData.shouldResistFingerprinting()) {
+ mShouldResistFingerprinting = true;
+ }
+
+ PermissionComparator comparator;
+
+ for (const CookiePermissionData& data : aData.cookiePermissions()) {
+ auto principalOrErr = PrincipalInfoToPrincipal(data.principalInfo());
+ if (NS_WARN_IF(principalOrErr.isErr())) {
+ continue;
+ }
+
+ nsCOMPtr<nsIPrincipal> principal = principalOrErr.unwrap();
+ nsCOMPtr<nsIPermission> permission = Permission::Create(
+ principal, "cookie"_ns, data.cookiePermission(), 0, 0, 0);
+ if (NS_WARN_IF(!permission)) {
+ continue;
+ }
+
+ if (!mCookiePermissions.Contains(permission, comparator)) {
+ mCookiePermissions.AppendElement(permission);
+ }
+ }
+}
+
+void CookieJarSettings::SetPartitionKey(nsIURI* aURI) {
+ MOZ_ASSERT(aURI);
+
+ OriginAttributes attrs;
+ attrs.SetPartitionKey(aURI);
+ mPartitionKey = std::move(attrs.mPartitionKey);
+}
+
+void CookieJarSettings::UpdateIsOnContentBlockingAllowList(
+ nsIChannel* aChannel) {
+ MOZ_DIAGNOSTIC_ASSERT(XRE_IsParentProcess());
+ MOZ_ASSERT(aChannel);
+
+ // Early return if the flag was updated before.
+ if (mIsOnContentBlockingAllowListUpdated) {
+ return;
+ }
+ mIsOnContentBlockingAllowListUpdated = true;
+
+ nsCOMPtr<nsILoadInfo> loadInfo = aChannel->LoadInfo();
+
+ nsCOMPtr<nsIURI> uri;
+ nsresult rv = aChannel->GetURI(getter_AddRefs(uri));
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return;
+ }
+
+ // We need to recompute the ContentBlockingAllowListPrincipal here for the
+ // top level channel because we might navigate from the the initial
+ // about:blank page or the existing page which may have a different origin
+ // than the URI we are going to load here. Thus, we need to recompute the
+ // prinicpal in order to get the correct ContentBlockingAllowListPrincipal.
+ nsCOMPtr<nsIPrincipal> contentBlockingAllowListPrincipal;
+ OriginAttributes attrs;
+ loadInfo->GetOriginAttributes(&attrs);
+ ContentBlockingAllowList::RecomputePrincipal(
+ uri, attrs, getter_AddRefs(contentBlockingAllowListPrincipal));
+
+ if (!contentBlockingAllowListPrincipal ||
+ !contentBlockingAllowListPrincipal->GetIsContentPrincipal()) {
+ return;
+ }
+
+ Unused << ContentBlockingAllowList::Check(contentBlockingAllowListPrincipal,
+ NS_UsePrivateBrowsing(aChannel),
+ mIsOnContentBlockingAllowList);
+}
+
+// static
+bool CookieJarSettings::IsRejectThirdPartyContexts(uint32_t aCookieBehavior) {
+ return aCookieBehavior == nsICookieService::BEHAVIOR_REJECT_TRACKER ||
+ aCookieBehavior ==
+ nsICookieService::BEHAVIOR_REJECT_TRACKER_AND_PARTITION_FOREIGN ||
+ IsRejectThirdPartyWithExceptions(aCookieBehavior);
+}
+
+// static
+bool CookieJarSettings::IsRejectThirdPartyWithExceptions(
+ uint32_t aCookieBehavior) {
+ return aCookieBehavior == nsICookieService::BEHAVIOR_REJECT_FOREIGN &&
+ StaticPrefs::network_cookie_rejectForeignWithExceptions_enabled();
+}
+
+NS_IMETHODIMP
+CookieJarSettings::Read(nsIObjectInputStream* aStream) {
+ nsresult rv = aStream->Read32(&mCookieBehavior);
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+
+ rv = aStream->ReadBoolean(&mIsFirstPartyIsolated);
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+
+ rv = aStream->ReadBoolean(&mShouldResistFingerprinting);
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+
+ bool isFixed;
+ aStream->ReadBoolean(&isFixed);
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+ mState = isFixed ? eFixed : eProgressive;
+
+ rv = aStream->ReadBoolean(&mIsOnContentBlockingAllowList);
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+
+ rv = aStream->ReadString(mPartitionKey);
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+
+ // Deserializing the cookie permission list.
+ uint32_t cookiePermissionsLength;
+ rv = aStream->Read32(&cookiePermissionsLength);
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+
+ if (!cookiePermissionsLength) {
+ // Bailing out early because there is no cookie permission.
+ return NS_OK;
+ }
+
+ CookiePermissionList list;
+ mCookiePermissions.SetCapacity(cookiePermissionsLength);
+ for (uint32_t i = 0; i < cookiePermissionsLength; ++i) {
+ nsAutoCString principalJSON;
+ aStream->ReadCString(principalJSON);
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+
+ nsCOMPtr<nsIPrincipal> principal = BasePrincipal::FromJSON(principalJSON);
+
+ if (NS_WARN_IF(!principal)) {
+ continue;
+ }
+
+ uint32_t cookiePermission;
+ aStream->Read32(&cookiePermission);
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+
+ nsCOMPtr<nsIPermission> permission =
+ Permission::Create(principal, "cookie"_ns, cookiePermission, 0, 0, 0);
+ if (NS_WARN_IF(!permission)) {
+ continue;
+ }
+
+ list.AppendElement(permission);
+ }
+
+ mCookiePermissions = std::move(list);
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+CookieJarSettings::Write(nsIObjectOutputStream* aStream) {
+ nsresult rv = aStream->Write32(mCookieBehavior);
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+
+ rv = aStream->WriteBoolean(mIsFirstPartyIsolated);
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+
+ rv = aStream->WriteBoolean(mShouldResistFingerprinting);
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+
+ rv = aStream->WriteBoolean(mState == eFixed);
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+
+ rv = aStream->WriteBoolean(mIsOnContentBlockingAllowList);
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+
+ rv = aStream->WriteWStringZ(mPartitionKey.get());
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+
+ // Serializing the cookie permission list. It will first write the length of
+ // the list, and then, write the cookie permission consecutively.
+ uint32_t cookiePermissionsLength = mCookiePermissions.Length();
+ rv = aStream->Write32(cookiePermissionsLength);
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+
+ for (const RefPtr<nsIPermission>& permission : mCookiePermissions) {
+ nsCOMPtr<nsIPrincipal> principal;
+ nsresult rv = permission->GetPrincipal(getter_AddRefs(principal));
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ continue;
+ }
+
+ nsAutoCString principalJSON;
+ BasePrincipal::Cast(principal)->ToJSON(principalJSON);
+
+ rv = aStream->WriteStringZ(principalJSON.get());
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+
+ uint32_t cookiePermission = 0;
+ rv = permission->GetCapability(&cookiePermission);
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ continue;
+ }
+
+ rv = aStream->Write32(cookiePermission);
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+ }
+
+ return NS_OK;
+}
+
+} // namespace net
+} // namespace mozilla