1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4  * License, v. 2.0. If a copy of the MPL was not distributed with this
5  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6 
7 #include "mozilla/AntiTrackingUtils.h"
8 #include "mozilla/BasePrincipal.h"
9 #include "mozilla/ClearOnShutdown.h"
10 #include "mozilla/ContentBlockingAllowList.h"
11 #include "mozilla/dom/BrowsingContext.h"
12 #include "mozilla/net/CookieJarSettings.h"
13 #include "mozilla/net/NeckoChannelParams.h"
14 #include "mozilla/Permission.h"
15 #include "mozilla/PermissionManager.h"
16 #include "mozilla/SchedulerGroup.h"
17 #include "mozilla/StaticPrefs_network.h"
18 #include "mozilla/Unused.h"
19 #include "nsGlobalWindowInner.h"
20 #include "nsIPrincipal.h"
21 #if defined(MOZ_THUNDERBIRD) || defined(MOZ_SUITE)
22 #  include "nsIProtocolHandler.h"
23 #endif
24 #include "nsIClassInfoImpl.h"
25 #include "nsIChannel.h"
26 #include "nsICookieManager.h"
27 #include "nsICookieService.h"
28 #include "nsIObjectInputStream.h"
29 #include "nsIObjectOutputStream.h"
30 #include "nsNetUtil.h"
31 
32 namespace mozilla {
33 namespace net {
34 
35 NS_IMPL_CLASSINFO(CookieJarSettings, nullptr, nsIClassInfo::THREADSAFE,
36                   COOKIEJARSETTINGS_CID)
37 
38 NS_IMPL_ISUPPORTS_CI(CookieJarSettings, nsICookieJarSettings, nsISerializable)
39 
40 static StaticRefPtr<CookieJarSettings> sBlockinAll;
41 
42 namespace {
43 
44 class PermissionComparator {
45  public:
Equals(nsIPermission * aA,nsIPermission * aB)46   static bool Equals(nsIPermission* aA, nsIPermission* aB) {
47     nsCOMPtr<nsIPrincipal> principalA;
48     nsresult rv = aA->GetPrincipal(getter_AddRefs(principalA));
49     if (NS_WARN_IF(NS_FAILED(rv))) {
50       return false;
51     }
52 
53     nsCOMPtr<nsIPrincipal> principalB;
54     rv = aB->GetPrincipal(getter_AddRefs(principalB));
55     if (NS_WARN_IF(NS_FAILED(rv))) {
56       return false;
57     }
58 
59     bool equals = false;
60     rv = principalA->Equals(principalB, &equals);
61     if (NS_WARN_IF(NS_FAILED(rv))) {
62       return false;
63     }
64 
65     return equals;
66   }
67 };
68 
69 class ReleaseCookiePermissions final : public Runnable {
70  public:
ReleaseCookiePermissions(nsTArray<RefPtr<nsIPermission>> && aArray)71   explicit ReleaseCookiePermissions(nsTArray<RefPtr<nsIPermission>>&& aArray)
72       : Runnable("ReleaseCookiePermissions"), mArray(std::move(aArray)) {}
73 
Run()74   NS_IMETHOD Run() override {
75     MOZ_ASSERT(NS_IsMainThread());
76     mArray.Clear();
77     return NS_OK;
78   }
79 
80  private:
81   nsTArray<RefPtr<nsIPermission>> mArray;
82 };
83 
84 }  // namespace
85 
86 // static
GetBlockingAll()87 already_AddRefed<nsICookieJarSettings> CookieJarSettings::GetBlockingAll() {
88   MOZ_ASSERT(NS_IsMainThread());
89 
90   if (sBlockinAll) {
91     return do_AddRef(sBlockinAll);
92   }
93 
94   sBlockinAll =
95       new CookieJarSettings(nsICookieService::BEHAVIOR_REJECT,
96                             OriginAttributes::IsFirstPartyEnabled(), eFixed);
97   ClearOnShutdown(&sBlockinAll);
98 
99   return do_AddRef(sBlockinAll);
100 }
101 
102 // static
Create(CreateMode aMode)103 already_AddRefed<nsICookieJarSettings> CookieJarSettings::Create(
104     CreateMode aMode) {
105   MOZ_ASSERT(NS_IsMainThread());
106 
107   RefPtr<CookieJarSettings> cookieJarSettings;
108 
109   switch (aMode) {
110     case eRegular:
111     case ePrivate:
112       cookieJarSettings = new CookieJarSettings(
113           nsICookieManager::GetCookieBehavior(aMode == ePrivate),
114           OriginAttributes::IsFirstPartyEnabled(), eProgressive);
115       break;
116 
117     default:
118       MOZ_CRASH("Unexpected create mode.");
119   }
120 
121   return cookieJarSettings.forget();
122 }
123 
124 // static
Create(nsIPrincipal * aPrincipal)125 already_AddRefed<nsICookieJarSettings> CookieJarSettings::Create(
126     nsIPrincipal* aPrincipal) {
127   MOZ_ASSERT(NS_IsMainThread());
128 
129   if (aPrincipal && aPrincipal->OriginAttributesRef().mPrivateBrowsingId > 0) {
130     return Create(ePrivate);
131   }
132 
133   return Create(eRegular);
134 }
135 
136 // static
Create(uint32_t aCookieBehavior,const nsAString & aPartitionKey,bool aIsFirstPartyIsolated,bool aIsOnContentBlockingAllowList)137 already_AddRefed<nsICookieJarSettings> CookieJarSettings::Create(
138     uint32_t aCookieBehavior, const nsAString& aPartitionKey,
139     bool aIsFirstPartyIsolated, bool aIsOnContentBlockingAllowList) {
140   MOZ_ASSERT(NS_IsMainThread());
141 
142   RefPtr<CookieJarSettings> cookieJarSettings = new CookieJarSettings(
143       aCookieBehavior, aIsFirstPartyIsolated, eProgressive);
144   cookieJarSettings->mPartitionKey = aPartitionKey;
145   cookieJarSettings->mIsOnContentBlockingAllowList =
146       aIsOnContentBlockingAllowList;
147 
148   return cookieJarSettings.forget();
149 }
150 
151 // static
CreateForXPCOM()152 already_AddRefed<nsICookieJarSettings> CookieJarSettings::CreateForXPCOM() {
153   MOZ_ASSERT(NS_IsMainThread());
154   return Create(eRegular);
155 }
156 
CookieJarSettings(uint32_t aCookieBehavior,bool aIsFirstPartyIsolated,State aState)157 CookieJarSettings::CookieJarSettings(uint32_t aCookieBehavior,
158                                      bool aIsFirstPartyIsolated, State aState)
159     : mCookieBehavior(aCookieBehavior),
160       mIsFirstPartyIsolated(aIsFirstPartyIsolated),
161       mIsOnContentBlockingAllowList(false),
162       mState(aState),
163       mToBeMerged(false) {
164   MOZ_ASSERT(NS_IsMainThread());
165   MOZ_ASSERT_IF(
166       mIsFirstPartyIsolated,
167       mCookieBehavior !=
168           nsICookieService::BEHAVIOR_REJECT_TRACKER_AND_PARTITION_FOREIGN);
169 }
170 
~CookieJarSettings()171 CookieJarSettings::~CookieJarSettings() {
172   if (!NS_IsMainThread() && !mCookiePermissions.IsEmpty()) {
173     RefPtr<Runnable> r =
174         new ReleaseCookiePermissions(std::move(mCookiePermissions));
175     MOZ_ASSERT(mCookiePermissions.IsEmpty());
176 
177     SchedulerGroup::Dispatch(TaskCategory::Other, r.forget());
178   }
179 }
180 
181 NS_IMETHODIMP
InitWithURI(nsIURI * aURI,bool aIsPrivate)182 CookieJarSettings::InitWithURI(nsIURI* aURI, bool aIsPrivate) {
183   NS_ENSURE_ARG(aURI);
184 
185   mCookieBehavior = nsICookieManager::GetCookieBehavior(aIsPrivate);
186 
187   SetPartitionKey(aURI);
188   return NS_OK;
189 }
190 
191 NS_IMETHODIMP
GetCookieBehavior(uint32_t * aCookieBehavior)192 CookieJarSettings::GetCookieBehavior(uint32_t* aCookieBehavior) {
193   *aCookieBehavior = mCookieBehavior;
194   return NS_OK;
195 }
196 
197 NS_IMETHODIMP
GetIsFirstPartyIsolated(bool * aIsFirstPartyIsolated)198 CookieJarSettings::GetIsFirstPartyIsolated(bool* aIsFirstPartyIsolated) {
199   *aIsFirstPartyIsolated = mIsFirstPartyIsolated;
200   return NS_OK;
201 }
202 
203 NS_IMETHODIMP
GetRejectThirdPartyContexts(bool * aRejectThirdPartyContexts)204 CookieJarSettings::GetRejectThirdPartyContexts(
205     bool* aRejectThirdPartyContexts) {
206   *aRejectThirdPartyContexts =
207       CookieJarSettings::IsRejectThirdPartyContexts(mCookieBehavior);
208   return NS_OK;
209 }
210 
211 NS_IMETHODIMP
GetLimitForeignContexts(bool * aLimitForeignContexts)212 CookieJarSettings::GetLimitForeignContexts(bool* aLimitForeignContexts) {
213   *aLimitForeignContexts =
214       mCookieBehavior == nsICookieService::BEHAVIOR_LIMIT_FOREIGN ||
215       (StaticPrefs::privacy_dynamic_firstparty_limitForeign() &&
216        mCookieBehavior ==
217            nsICookieService::BEHAVIOR_REJECT_TRACKER_AND_PARTITION_FOREIGN);
218   return NS_OK;
219 }
220 
221 NS_IMETHODIMP
GetBlockingAllThirdPartyContexts(bool * aBlockingAllThirdPartyContexts)222 CookieJarSettings::GetBlockingAllThirdPartyContexts(
223     bool* aBlockingAllThirdPartyContexts) {
224   // XXX For non-cookie forms of storage, we handle BEHAVIOR_LIMIT_FOREIGN by
225   // simply rejecting the request to use the storage. In the future, if we
226   // change the meaning of BEHAVIOR_LIMIT_FOREIGN to be one which makes sense
227   // for non-cookie storage types, this may change.
228   *aBlockingAllThirdPartyContexts =
229       mCookieBehavior == nsICookieService::BEHAVIOR_LIMIT_FOREIGN ||
230       (!StaticPrefs::network_cookie_rejectForeignWithExceptions_enabled() &&
231        mCookieBehavior == nsICookieService::BEHAVIOR_REJECT_FOREIGN);
232   return NS_OK;
233 }
234 
235 NS_IMETHODIMP
GetBlockingAllContexts(bool * aBlockingAllContexts)236 CookieJarSettings::GetBlockingAllContexts(bool* aBlockingAllContexts) {
237   *aBlockingAllContexts = mCookieBehavior == nsICookieService::BEHAVIOR_REJECT;
238   return NS_OK;
239 }
240 
241 NS_IMETHODIMP
GetPartitionForeign(bool * aPartitionForeign)242 CookieJarSettings::GetPartitionForeign(bool* aPartitionForeign) {
243   *aPartitionForeign =
244       mCookieBehavior ==
245       nsICookieService::BEHAVIOR_REJECT_TRACKER_AND_PARTITION_FOREIGN;
246   return NS_OK;
247 }
248 
249 NS_IMETHODIMP
SetPartitionForeign(bool aPartitionForeign)250 CookieJarSettings::SetPartitionForeign(bool aPartitionForeign) {
251   if (mIsFirstPartyIsolated) {
252     return NS_OK;
253   }
254 
255   if (aPartitionForeign) {
256     mCookieBehavior =
257         nsICookieService::BEHAVIOR_REJECT_TRACKER_AND_PARTITION_FOREIGN;
258   }
259   return NS_OK;
260 }
261 
262 NS_IMETHODIMP
GetIsOnContentBlockingAllowList(bool * aIsOnContentBlockingAllowList)263 CookieJarSettings::GetIsOnContentBlockingAllowList(
264     bool* aIsOnContentBlockingAllowList) {
265   *aIsOnContentBlockingAllowList = mIsOnContentBlockingAllowList;
266   return NS_OK;
267 }
268 
269 NS_IMETHODIMP
GetPartitionKey(nsAString & aPartitionKey)270 CookieJarSettings::GetPartitionKey(nsAString& aPartitionKey) {
271   aPartitionKey = mPartitionKey;
272   return NS_OK;
273 }
274 
275 NS_IMETHODIMP
CookiePermission(nsIPrincipal * aPrincipal,uint32_t * aCookiePermission)276 CookieJarSettings::CookiePermission(nsIPrincipal* aPrincipal,
277                                     uint32_t* aCookiePermission) {
278   MOZ_ASSERT(NS_IsMainThread());
279   NS_ENSURE_ARG_POINTER(aPrincipal);
280   NS_ENSURE_ARG_POINTER(aCookiePermission);
281 
282   *aCookiePermission = nsIPermissionManager::UNKNOWN_ACTION;
283 
284   nsresult rv;
285 
286   // Let's see if we know this permission.
287   if (!mCookiePermissions.IsEmpty()) {
288     for (const RefPtr<nsIPermission>& permission : mCookiePermissions) {
289       bool match = false;
290       rv = permission->Matches(aPrincipal, false, &match);
291       if (NS_WARN_IF(NS_FAILED(rv)) || !match) {
292         continue;
293       }
294 
295       rv = permission->GetCapability(aCookiePermission);
296       if (NS_WARN_IF(NS_FAILED(rv))) {
297         return rv;
298       }
299 
300       return NS_OK;
301     }
302   }
303 
304   // Let's ask the permission manager.
305   PermissionManager* pm = PermissionManager::GetInstance();
306   if (NS_WARN_IF(!pm)) {
307     return NS_ERROR_FAILURE;
308   }
309 
310 #if defined(MOZ_THUNDERBIRD) || defined(MOZ_SUITE)
311   // Check if this protocol doesn't allow cookies.
312   bool hasFlags;
313   nsCOMPtr<nsIURI> uri;
314   BasePrincipal::Cast(aPrincipal)->GetURI(getter_AddRefs(uri));
315 
316   rv = NS_URIChainHasFlags(uri, nsIProtocolHandler::URI_FORBIDS_COOKIE_ACCESS,
317                            &hasFlags);
318   if (NS_FAILED(rv) || hasFlags) {
319     *aCookiePermission = PermissionManager::DENY_ACTION;
320     rv = NS_OK;  // Reset, so it's not caught as a bad status after the `else`.
321   } else         // Note the tricky `else` which controls the call below.
322 #endif
323 
324     rv = pm->TestPermissionFromPrincipal(aPrincipal, "cookie"_ns,
325                                          aCookiePermission);
326   if (NS_WARN_IF(NS_FAILED(rv))) {
327     return rv;
328   }
329 
330   // Let's store the permission, also if the result is UNKNOWN in order to avoid
331   // race conditions.
332 
333   nsCOMPtr<nsIPermission> permission =
334       Permission::Create(aPrincipal, "cookie"_ns, *aCookiePermission, 0, 0, 0);
335   if (permission) {
336     mCookiePermissions.AppendElement(permission);
337   }
338 
339   mToBeMerged = true;
340   return NS_OK;
341 }
342 
Serialize(CookieJarSettingsArgs & aData)343 void CookieJarSettings::Serialize(CookieJarSettingsArgs& aData) {
344   MOZ_ASSERT(NS_IsMainThread());
345 
346   aData.isFixed() = mState == eFixed;
347   aData.cookieBehavior() = mCookieBehavior;
348   aData.isFirstPartyIsolated() = mIsFirstPartyIsolated;
349   aData.isOnContentBlockingAllowList() = mIsOnContentBlockingAllowList;
350   aData.partitionKey() = mPartitionKey;
351 
352   for (const RefPtr<nsIPermission>& permission : mCookiePermissions) {
353     nsCOMPtr<nsIPrincipal> principal;
354     nsresult rv = permission->GetPrincipal(getter_AddRefs(principal));
355     if (NS_WARN_IF(NS_FAILED(rv))) {
356       continue;
357     }
358 
359     ipc::PrincipalInfo principalInfo;
360     rv = PrincipalToPrincipalInfo(principal, &principalInfo,
361                                   true /* aSkipBaseDomain */);
362     if (NS_WARN_IF(NS_FAILED(rv))) {
363       continue;
364     }
365 
366     uint32_t cookiePermission = 0;
367     rv = permission->GetCapability(&cookiePermission);
368     if (NS_WARN_IF(NS_FAILED(rv))) {
369       continue;
370     }
371 
372     aData.cookiePermissions().AppendElement(
373         CookiePermissionData(principalInfo, cookiePermission));
374   }
375 
376   mToBeMerged = false;
377 }
378 
Deserialize(const CookieJarSettingsArgs & aData,nsICookieJarSettings ** aCookieJarSettings)379 /* static */ void CookieJarSettings::Deserialize(
380     const CookieJarSettingsArgs& aData,
381     nsICookieJarSettings** aCookieJarSettings) {
382   MOZ_ASSERT(NS_IsMainThread());
383 
384   CookiePermissionList list;
385   for (const CookiePermissionData& data : aData.cookiePermissions()) {
386     auto principalOrErr = PrincipalInfoToPrincipal(data.principalInfo());
387     if (NS_WARN_IF(principalOrErr.isErr())) {
388       continue;
389     }
390 
391     nsCOMPtr<nsIPrincipal> principal = principalOrErr.unwrap();
392 
393     nsCOMPtr<nsIPermission> permission = Permission::Create(
394         principal, "cookie"_ns, data.cookiePermission(), 0, 0, 0);
395     if (NS_WARN_IF(!permission)) {
396       continue;
397     }
398 
399     list.AppendElement(permission);
400   }
401 
402   RefPtr<CookieJarSettings> cookieJarSettings = new CookieJarSettings(
403       aData.cookieBehavior(), aData.isFirstPartyIsolated(),
404       aData.isFixed() ? eFixed : eProgressive);
405 
406   cookieJarSettings->mIsOnContentBlockingAllowList =
407       aData.isOnContentBlockingAllowList();
408   cookieJarSettings->mCookiePermissions = std::move(list);
409   cookieJarSettings->mPartitionKey = aData.partitionKey();
410 
411   cookieJarSettings.forget(aCookieJarSettings);
412 }
413 
Merge(const CookieJarSettingsArgs & aData)414 void CookieJarSettings::Merge(const CookieJarSettingsArgs& aData) {
415   MOZ_ASSERT(NS_IsMainThread());
416   MOZ_ASSERT(
417       mCookieBehavior == aData.cookieBehavior() ||
418       (mCookieBehavior == nsICookieService::BEHAVIOR_REJECT_TRACKER &&
419        aData.cookieBehavior() ==
420            nsICookieService::BEHAVIOR_REJECT_TRACKER_AND_PARTITION_FOREIGN) ||
421       (mCookieBehavior ==
422            nsICookieService::BEHAVIOR_REJECT_TRACKER_AND_PARTITION_FOREIGN &&
423        aData.cookieBehavior() == nsICookieService::BEHAVIOR_REJECT_TRACKER));
424 
425   if (mState == eFixed) {
426     return;
427   }
428 
429   // Merge cookie behavior pref values
430   if (mCookieBehavior == nsICookieService::BEHAVIOR_REJECT_TRACKER &&
431       aData.cookieBehavior() ==
432           nsICookieService::BEHAVIOR_REJECT_TRACKER_AND_PARTITION_FOREIGN) {
433     // If the other side has decided to partition third-party cookies, update
434     // our side when first-party isolation is disabled.
435     if (!mIsFirstPartyIsolated) {
436       mCookieBehavior =
437           nsICookieService::BEHAVIOR_REJECT_TRACKER_AND_PARTITION_FOREIGN;
438     }
439   }
440   if (mCookieBehavior ==
441           nsICookieService::BEHAVIOR_REJECT_TRACKER_AND_PARTITION_FOREIGN &&
442       aData.cookieBehavior() == nsICookieService::BEHAVIOR_REJECT_TRACKER) {
443     // If we've decided to partition third-party cookies, the other side may not
444     // have caught up yet unless it has first-party isolation enabled.
445     if (aData.isFirstPartyIsolated()) {
446       mCookieBehavior = nsICookieService::BEHAVIOR_REJECT_TRACKER;
447       mIsFirstPartyIsolated = true;
448     }
449   }
450   // Ignore all other cases.
451   MOZ_ASSERT_IF(
452       mIsFirstPartyIsolated,
453       mCookieBehavior !=
454           nsICookieService::BEHAVIOR_REJECT_TRACKER_AND_PARTITION_FOREIGN);
455 
456   PermissionComparator comparator;
457 
458   for (const CookiePermissionData& data : aData.cookiePermissions()) {
459     auto principalOrErr = PrincipalInfoToPrincipal(data.principalInfo());
460     if (NS_WARN_IF(principalOrErr.isErr())) {
461       continue;
462     }
463 
464     nsCOMPtr<nsIPrincipal> principal = principalOrErr.unwrap();
465     nsCOMPtr<nsIPermission> permission = Permission::Create(
466         principal, "cookie"_ns, data.cookiePermission(), 0, 0, 0);
467     if (NS_WARN_IF(!permission)) {
468       continue;
469     }
470 
471     if (!mCookiePermissions.Contains(permission, comparator)) {
472       mCookiePermissions.AppendElement(permission);
473     }
474   }
475 }
476 
SetPartitionKey(nsIURI * aURI)477 void CookieJarSettings::SetPartitionKey(nsIURI* aURI) {
478   MOZ_ASSERT(aURI);
479 
480   OriginAttributes attrs;
481   attrs.SetPartitionKey(aURI);
482   mPartitionKey = std::move(attrs.mPartitionKey);
483 }
484 
UpdateIsOnContentBlockingAllowList(nsIChannel * aChannel)485 void CookieJarSettings::UpdateIsOnContentBlockingAllowList(
486     nsIChannel* aChannel) {
487   MOZ_DIAGNOSTIC_ASSERT(XRE_IsParentProcess());
488   MOZ_ASSERT(aChannel);
489   nsCOMPtr<nsILoadInfo> loadInfo = aChannel->LoadInfo();
490 
491   nsCOMPtr<nsIURI> uri;
492   nsresult rv = aChannel->GetURI(getter_AddRefs(uri));
493   if (NS_WARN_IF(NS_FAILED(rv))) {
494     return;
495   }
496 
497   // We need to recompute the ContentBlockingAllowListPrincipal here for the
498   // top level channel because we might navigate from the the initial
499   // about:blank page or the existing page which may have a different origin
500   // than the URI we are going to load here. Thus, we need to recompute the
501   // prinicpal in order to get the correct ContentBlockingAllowListPrincipal.
502   nsCOMPtr<nsIPrincipal> contentBlockingAllowListPrincipal;
503   OriginAttributes attrs;
504   loadInfo->GetOriginAttributes(&attrs);
505   ContentBlockingAllowList::RecomputePrincipal(
506       uri, attrs, getter_AddRefs(contentBlockingAllowListPrincipal));
507 
508   if (!contentBlockingAllowListPrincipal ||
509       !contentBlockingAllowListPrincipal->GetIsContentPrincipal()) {
510     return;
511   }
512 
513   Unused << ContentBlockingAllowList::Check(contentBlockingAllowListPrincipal,
514                                             NS_UsePrivateBrowsing(aChannel),
515                                             mIsOnContentBlockingAllowList);
516 }
517 
518 // static
IsRejectThirdPartyContexts(uint32_t aCookieBehavior)519 bool CookieJarSettings::IsRejectThirdPartyContexts(uint32_t aCookieBehavior) {
520   return aCookieBehavior == nsICookieService::BEHAVIOR_REJECT_TRACKER ||
521          aCookieBehavior ==
522              nsICookieService::BEHAVIOR_REJECT_TRACKER_AND_PARTITION_FOREIGN ||
523          IsRejectThirdPartyWithExceptions(aCookieBehavior);
524 }
525 
526 // static
IsRejectThirdPartyWithExceptions(uint32_t aCookieBehavior)527 bool CookieJarSettings::IsRejectThirdPartyWithExceptions(
528     uint32_t aCookieBehavior) {
529   return aCookieBehavior == nsICookieService::BEHAVIOR_REJECT_FOREIGN &&
530          StaticPrefs::network_cookie_rejectForeignWithExceptions_enabled();
531 }
532 
533 NS_IMETHODIMP
Read(nsIObjectInputStream * aStream)534 CookieJarSettings::Read(nsIObjectInputStream* aStream) {
535   nsresult rv = aStream->Read32(&mCookieBehavior);
536   if (NS_WARN_IF(NS_FAILED(rv))) {
537     return rv;
538   }
539 
540   rv = aStream->ReadBoolean(&mIsFirstPartyIsolated);
541   if (NS_WARN_IF(NS_FAILED(rv))) {
542     return rv;
543   }
544 
545   bool isFixed;
546   aStream->ReadBoolean(&isFixed);
547   if (NS_WARN_IF(NS_FAILED(rv))) {
548     return rv;
549   }
550   mState = isFixed ? eFixed : eProgressive;
551 
552   rv = aStream->ReadBoolean(&mIsOnContentBlockingAllowList);
553   if (NS_WARN_IF(NS_FAILED(rv))) {
554     return rv;
555   }
556 
557   rv = aStream->ReadString(mPartitionKey);
558   if (NS_WARN_IF(NS_FAILED(rv))) {
559     return rv;
560   }
561 
562   // Deserializing the cookie permission list.
563   uint32_t cookiePermissionsLength;
564   rv = aStream->Read32(&cookiePermissionsLength);
565   if (NS_WARN_IF(NS_FAILED(rv))) {
566     return rv;
567   }
568 
569   if (!cookiePermissionsLength) {
570     // Bailing out early because there is no cookie permission.
571     return NS_OK;
572   }
573 
574   CookiePermissionList list;
575   mCookiePermissions.SetCapacity(cookiePermissionsLength);
576   for (uint32_t i = 0; i < cookiePermissionsLength; ++i) {
577     nsAutoCString principalJSON;
578     aStream->ReadCString(principalJSON);
579     if (NS_WARN_IF(NS_FAILED(rv))) {
580       return rv;
581     }
582 
583     nsCOMPtr<nsIPrincipal> principal = BasePrincipal::FromJSON(principalJSON);
584 
585     if (NS_WARN_IF(!principal)) {
586       continue;
587     }
588 
589     uint32_t cookiePermission;
590     aStream->Read32(&cookiePermission);
591     if (NS_WARN_IF(NS_FAILED(rv))) {
592       return rv;
593     }
594 
595     nsCOMPtr<nsIPermission> permission =
596         Permission::Create(principal, "cookie"_ns, cookiePermission, 0, 0, 0);
597     if (NS_WARN_IF(!permission)) {
598       continue;
599     }
600 
601     list.AppendElement(permission);
602   }
603 
604   mCookiePermissions = std::move(list);
605 
606   return NS_OK;
607 }
608 
609 NS_IMETHODIMP
Write(nsIObjectOutputStream * aStream)610 CookieJarSettings::Write(nsIObjectOutputStream* aStream) {
611   nsresult rv = aStream->Write32(mCookieBehavior);
612   if (NS_WARN_IF(NS_FAILED(rv))) {
613     return rv;
614   }
615 
616   rv = aStream->WriteBoolean(mIsFirstPartyIsolated);
617   if (NS_WARN_IF(NS_FAILED(rv))) {
618     return rv;
619   }
620 
621   rv = aStream->WriteBoolean(mState == eFixed);
622   if (NS_WARN_IF(NS_FAILED(rv))) {
623     return rv;
624   }
625 
626   rv = aStream->WriteBoolean(mIsOnContentBlockingAllowList);
627   if (NS_WARN_IF(NS_FAILED(rv))) {
628     return rv;
629   }
630 
631   rv = aStream->WriteWStringZ(mPartitionKey.get());
632   if (NS_WARN_IF(NS_FAILED(rv))) {
633     return rv;
634   }
635 
636   // Serializing the cookie permission list. It will first write the length of
637   // the list, and then, write the cookie permission consecutively.
638   uint32_t cookiePermissionsLength = mCookiePermissions.Length();
639   rv = aStream->Write32(cookiePermissionsLength);
640   if (NS_WARN_IF(NS_FAILED(rv))) {
641     return rv;
642   }
643 
644   for (const RefPtr<nsIPermission>& permission : mCookiePermissions) {
645     nsCOMPtr<nsIPrincipal> principal;
646     nsresult rv = permission->GetPrincipal(getter_AddRefs(principal));
647     if (NS_WARN_IF(NS_FAILED(rv))) {
648       continue;
649     }
650 
651     nsAutoCString principalJSON;
652     BasePrincipal::Cast(principal)->ToJSON(principalJSON);
653 
654     rv = aStream->WriteStringZ(principalJSON.get());
655     if (NS_WARN_IF(NS_FAILED(rv))) {
656       return rv;
657     }
658 
659     uint32_t cookiePermission = 0;
660     rv = permission->GetCapability(&cookiePermission);
661     if (NS_WARN_IF(NS_FAILED(rv))) {
662       continue;
663     }
664 
665     rv = aStream->Write32(cookiePermission);
666     if (NS_WARN_IF(NS_FAILED(rv))) {
667       return rv;
668     }
669   }
670 
671   return NS_OK;
672 }
673 
674 }  // namespace net
675 }  // namespace mozilla
676