tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

CookieServiceChild.cpp (19941B)


      1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
      2 /* This Source Code Form is subject to the terms of the Mozilla Public
      3 * License, v. 2.0. If a copy of the MPL was not distributed with this
      4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
      5 
      6 #include "Cookie.h"
      7 #include "CookieCommons.h"
      8 #include "CookieLogging.h"
      9 #include "CookieNotification.h"
     10 #include "CookieParser.h"
     11 #include "CookieService.h"
     12 #include "mozilla/net/CookieServiceChild.h"
     13 #include "ErrorList.h"
     14 #include "mozilla/net/HttpChannelChild.h"
     15 #include "mozilla/net/NeckoChannelParams.h"
     16 #include "mozilla/LoadInfo.h"
     17 #include "mozilla/BasePrincipal.h"
     18 #include "mozilla/ClearOnShutdown.h"
     19 #include "mozilla/ConsoleReportCollector.h"
     20 #include "mozilla/dom/ContentChild.h"
     21 #include "mozilla/dom/Document.h"
     22 #include "mozilla/glean/NetwerkMetrics.h"
     23 #include "mozilla/ipc/URIUtils.h"
     24 #include "mozilla/net/NeckoChild.h"
     25 #include "mozilla/StaticPrefs_network.h"
     26 #include "mozilla/StoragePrincipalHelper.h"
     27 #include "nsNetCID.h"
     28 #include "nsNetUtil.h"
     29 #include "nsICookieJarSettings.h"
     30 #include "nsIChannel.h"
     31 #include "nsIClassifiedChannel.h"
     32 #include "nsIHttpChannel.h"
     33 #include "nsIEffectiveTLDService.h"
     34 #include "nsIURI.h"
     35 #include "nsIPrefBranch.h"
     36 #include "nsIScriptSecurityManager.h"
     37 #include "nsIWebProgressListener.h"
     38 #include "nsQueryObject.h"
     39 #include "nsServiceManagerUtils.h"
     40 #include "mozilla/TimeStamp.h"
     41 #include "ThirdPartyUtil.h"
     42 #include "nsIConsoleReportCollector.h"
     43 #include "mozilla/dom/WindowGlobalChild.h"
     44 
     45 using namespace mozilla::ipc;
     46 
     47 namespace mozilla {
     48 namespace net {
     49 
     50 static StaticRefPtr<CookieServiceChild> gCookieChildService;
     51 
     52 already_AddRefed<CookieServiceChild> CookieServiceChild::GetSingleton() {
     53  if (!gCookieChildService) {
     54    gCookieChildService = new CookieServiceChild();
     55    gCookieChildService->Init();
     56    ClearOnShutdown(&gCookieChildService);
     57  }
     58 
     59  return do_AddRef(gCookieChildService);
     60 }
     61 
     62 NS_IMPL_ISUPPORTS(CookieServiceChild, nsICookieService,
     63                  nsISupportsWeakReference)
     64 
     65 CookieServiceChild::CookieServiceChild() { NeckoChild::InitNeckoChild(); }
     66 
     67 CookieServiceChild::~CookieServiceChild() { gCookieChildService = nullptr; }
     68 
     69 void CookieServiceChild::Init() {
     70  auto* cc = static_cast<mozilla::dom::ContentChild*>(gNeckoChild->Manager());
     71  if (cc->IsShuttingDown()) {
     72    return;
     73  }
     74 
     75  // This corresponds to Release() in DeallocPCookieService.
     76  NS_ADDREF_THIS();
     77 
     78  // Create a child PCookieService actor. Don't do this in the constructor
     79  // since it could release 'this' on failure
     80  gNeckoChild->SendPCookieServiceConstructor(this);
     81 
     82  mThirdPartyUtil = ThirdPartyUtil::GetInstance();
     83  NS_ASSERTION(mThirdPartyUtil, "couldn't get ThirdPartyUtil service");
     84 
     85  mTLDService = do_GetService(NS_EFFECTIVETLDSERVICE_CONTRACTID);
     86  NS_ASSERTION(mTLDService, "couldn't get TLDService");
     87 }
     88 
     89 RefPtr<GenericPromise> CookieServiceChild::TrackCookieLoad(
     90    nsIChannel* aChannel) {
     91  if (!CanSend()) {
     92    return GenericPromise::CreateAndReject(NS_ERROR_NOT_AVAILABLE, __func__);
     93  }
     94 
     95  uint32_t rejectedReason = 0;
     96  ThirdPartyAnalysisResult result = mThirdPartyUtil->AnalyzeChannel(
     97      aChannel, true, nullptr, RequireThirdPartyCheck, &rejectedReason);
     98 
     99  nsCOMPtr<nsIURI> uri;
    100  aChannel->GetURI(getter_AddRefs(uri));
    101  nsCOMPtr<nsILoadInfo> loadInfo = aChannel->LoadInfo();
    102 
    103  OriginAttributes storageOriginAttributes = loadInfo->GetOriginAttributes();
    104  StoragePrincipalHelper::PrepareEffectiveStoragePrincipalOriginAttributes(
    105      aChannel, storageOriginAttributes);
    106 
    107  bool isSafeTopLevelNav = CookieCommons::IsSafeTopLevelNav(aChannel);
    108  bool hadCrossSiteRedirects = false;
    109  bool isSameSiteForeign =
    110      CookieCommons::IsSameSiteForeign(aChannel, uri, &hadCrossSiteRedirects);
    111 
    112  RefPtr<CookieServiceChild> self(this);
    113 
    114  nsTArray<OriginAttributes> originAttributesList;
    115  originAttributesList.AppendElement(storageOriginAttributes);
    116 
    117  // CHIPS - If CHIPS is enabled the partitioned cookie jar is always available
    118  // (and therefore the partitioned OriginAttributes), the unpartitioned cookie
    119  // jar is only available in first-party or third-party with storageAccess
    120  // contexts.
    121  nsCOMPtr<nsICookieJarSettings> cookieJarSettings =
    122      CookieCommons::GetCookieJarSettings(aChannel);
    123  bool isCHIPS = StaticPrefs::network_cookie_CHIPS_enabled() &&
    124                 !cookieJarSettings->GetBlockingAllContexts();
    125  bool isUnpartitioned =
    126      !result.contains(ThirdPartyAnalysis::IsForeign) ||
    127      result.contains(ThirdPartyAnalysis::IsStorageAccessPermissionGranted);
    128  if (isCHIPS && isUnpartitioned) {
    129    // Assert that the storage originAttributes is empty. In other words,
    130    // it's unpartitioned.
    131    MOZ_ASSERT(storageOriginAttributes.mPartitionKey.IsEmpty());
    132    // Add the partitioned principal to principals.
    133    OriginAttributes partitionedOriginAttributes;
    134    StoragePrincipalHelper::GetOriginAttributes(
    135        aChannel, partitionedOriginAttributes,
    136        StoragePrincipalHelper::ePartitionedPrincipal);
    137    originAttributesList.AppendElement(partitionedOriginAttributes);
    138    // Only append the partitioned originAttributes if the partitionKey is set.
    139    // The partitionKey could be empty for partitionKey in partitioned
    140    // originAttributes if the channel is for privilege request, such as
    141    // extension's requests.
    142    if (!partitionedOriginAttributes.mPartitionKey.IsEmpty()) {
    143      originAttributesList.AppendElement(partitionedOriginAttributes);
    144    }
    145  }
    146 
    147  return SendGetCookieList(
    148             uri, result.contains(ThirdPartyAnalysis::IsForeign),
    149             result.contains(ThirdPartyAnalysis::IsThirdPartyTrackingResource),
    150             result.contains(
    151                 ThirdPartyAnalysis::IsThirdPartySocialTrackingResource),
    152             result.contains(
    153                 ThirdPartyAnalysis::IsStorageAccessPermissionGranted),
    154             rejectedReason, isSafeTopLevelNav, isSameSiteForeign,
    155             hadCrossSiteRedirects, originAttributesList)
    156      ->Then(
    157          GetCurrentSerialEventTarget(), __func__,
    158          [self, uri](const nsTArray<CookieStructTable>& aCookiesListTable) {
    159            for (auto& entry : aCookiesListTable) {
    160              auto& cookies = entry.cookies();
    161              for (auto& cookieEntry : cookies) {
    162                RefPtr<Cookie> cookie =
    163                    Cookie::Create(cookieEntry, entry.attrs());
    164                cookie->SetIsHttpOnly(false);
    165                self->RecordDocumentCookie(cookie, entry.attrs());
    166              }
    167            }
    168            return GenericPromise::CreateAndResolve(true, __func__);
    169          },
    170          [](const mozilla::ipc::ResponseRejectReason) {
    171            return GenericPromise::CreateAndReject(NS_ERROR_FAILURE, __func__);
    172          });
    173 }
    174 
    175 IPCResult CookieServiceChild::RecvRemoveAll() {
    176  mCookiesMap.Clear();
    177 
    178  nsCOMPtr<nsIObserverService> obsService = services::GetObserverService();
    179  if (obsService) {
    180    obsService->NotifyObservers(nullptr, "content-removed-all-cookies",
    181                                nullptr);
    182  }
    183  return IPC_OK();
    184 }
    185 
    186 IPCResult CookieServiceChild::RecvRemoveCookie(
    187    const CookieStruct& aCookie, const OriginAttributes& aAttrs,
    188    const Maybe<nsID>& aOperationID) {
    189  RemoveSingleCookie(aCookie, aAttrs, aOperationID);
    190 
    191  nsCOMPtr<nsIObserverService> obsService = services::GetObserverService();
    192  if (obsService) {
    193    obsService->NotifyObservers(nullptr, "content-removed-cookie", nullptr);
    194  }
    195  return IPC_OK();
    196 }
    197 
    198 void CookieServiceChild::RemoveSingleCookie(const CookieStruct& aCookie,
    199                                            const OriginAttributes& aAttrs,
    200                                            const Maybe<nsID>& aOperationID) {
    201  nsCString baseDomain;
    202  if (NS_FAILED(CookieCommons::GetBaseDomainFromHost(
    203          mTLDService, aCookie.host(), baseDomain))) {
    204    MOZ_ASSERT(false,
    205               "CookieServiceChild::RemoveSingleCookie - GetBaseDomainFromHost "
    206               "shouldn't fail");
    207    return;
    208  }
    209  CookieKey key(baseDomain, aAttrs);
    210  CookiesList* cookiesList = nullptr;
    211  mCookiesMap.Get(key, &cookiesList);
    212 
    213  if (!cookiesList) {
    214    return;
    215  }
    216 
    217  for (uint32_t i = 0; i < cookiesList->Length(); i++) {
    218    RefPtr<Cookie> cookie = cookiesList->ElementAt(i);
    219    // bug 1858366: In the case that we are updating a stale cookie
    220    // from the content process: the parent process will signal
    221    // a batch deletion for the old cookie.
    222    // When received by the content process we should not remove
    223    // the new cookie since we have already updated the content
    224    // process cookies. So we also check the expiry here.
    225    if (cookie->Name().Equals(aCookie.name()) &&
    226        cookie->Host().Equals(aCookie.host()) &&
    227        cookie->Path().Equals(aCookie.path()) &&
    228        cookie->ExpiryInMSec() <= aCookie.expiryInMSec()) {
    229      cookiesList->RemoveElementAt(i);
    230      NotifyObservers(cookie, aAttrs, CookieNotificationAction::CookieDeleted,
    231                      aOperationID);
    232      break;
    233    }
    234  }
    235 }
    236 
    237 IPCResult CookieServiceChild::RecvAddCookie(const CookieStruct& aCookie,
    238                                            const OriginAttributes& aAttrs,
    239                                            const Maybe<nsID>& aOperationID) {
    240  RefPtr<Cookie> cookie = Cookie::Create(aCookie, aAttrs);
    241 
    242  CookieNotificationAction action = RecordDocumentCookie(cookie, aAttrs);
    243  NotifyObservers(cookie, aAttrs, action, aOperationID);
    244 
    245  // signal test code to check their cookie list
    246  nsCOMPtr<nsIObserverService> obsService = services::GetObserverService();
    247  if (obsService) {
    248    obsService->NotifyObservers(nullptr, "content-added-cookie", nullptr);
    249  }
    250 
    251  return IPC_OK();
    252 }
    253 
    254 IPCResult CookieServiceChild::RecvRemoveBatchDeletedCookies(
    255    nsTArray<CookieStruct>&& aCookiesList,
    256    nsTArray<OriginAttributes>&& aAttrsList) {
    257  MOZ_ASSERT(aCookiesList.Length() == aAttrsList.Length());
    258  for (uint32_t i = 0; i < aCookiesList.Length(); i++) {
    259    CookieStruct cookieStruct = aCookiesList.ElementAt(i);
    260    RemoveSingleCookie(cookieStruct, aAttrsList.ElementAt(i), Nothing());
    261  }
    262 
    263  nsCOMPtr<nsIObserverService> obsService = services::GetObserverService();
    264  if (obsService) {
    265    obsService->NotifyObservers(nullptr, "content-batch-deleted-cookies",
    266                                nullptr);
    267  }
    268  return IPC_OK();
    269 }
    270 
    271 IPCResult CookieServiceChild::RecvTrackCookiesLoad(
    272    nsTArray<CookieStructTable>&& aCookiesListTable) {
    273  for (auto& entry : aCookiesListTable) {
    274    for (auto& cookieEntry : entry.cookies()) {
    275      RefPtr<Cookie> cookie = Cookie::Create(cookieEntry, entry.attrs());
    276      cookie->SetIsHttpOnly(false);
    277      RecordDocumentCookie(cookie, entry.attrs());
    278    }
    279  }
    280 
    281  nsCOMPtr<nsIObserverService> obsService = services::GetObserverService();
    282  if (obsService) {
    283    obsService->NotifyObservers(nullptr, "content-track-cookies-loaded",
    284                                nullptr);
    285  }
    286 
    287  return IPC_OK();
    288 }
    289 
    290 /* static */ bool CookieServiceChild::RequireThirdPartyCheck(
    291    nsILoadInfo* aLoadInfo) {
    292  if (!aLoadInfo) {
    293    return false;
    294  }
    295 
    296  nsCOMPtr<nsICookieJarSettings> cookieJarSettings;
    297  nsresult rv =
    298      aLoadInfo->GetCookieJarSettings(getter_AddRefs(cookieJarSettings));
    299  if (NS_WARN_IF(NS_FAILED(rv))) {
    300    return false;
    301  }
    302 
    303  uint32_t cookieBehavior = cookieJarSettings->GetCookieBehavior();
    304  return cookieBehavior == nsICookieService::BEHAVIOR_REJECT_FOREIGN ||
    305         cookieBehavior == nsICookieService::BEHAVIOR_LIMIT_FOREIGN ||
    306         cookieBehavior == nsICookieService::BEHAVIOR_REJECT_TRACKER ||
    307         cookieBehavior ==
    308             nsICookieService::BEHAVIOR_REJECT_TRACKER_AND_PARTITION_FOREIGN;
    309 }
    310 
    311 CookieServiceChild::CookieNotificationAction
    312 CookieServiceChild::RecordDocumentCookie(Cookie* aCookie,
    313                                         const OriginAttributes& aAttrs) {
    314  nsAutoCString baseDomain;
    315  if (NS_FAILED(CookieCommons::GetBaseDomainFromHost(
    316          mTLDService, aCookie->Host(), baseDomain))) {
    317    MOZ_ASSERT(false,
    318               "CookieServiceChild::RecordDocumentCookie - "
    319               "GetBaseDomainFromHost shouldn't fail");
    320    return CookieNotificationAction::NoActionNeeded;
    321  }
    322 
    323  if (CookieCommons::IsFirstPartyPartitionedCookieWithoutCHIPS(
    324          aCookie, baseDomain, aAttrs)) {
    325    COOKIE_LOGSTRING(LogLevel::Error,
    326                     ("Invalid first-party partitioned cookie without "
    327                      "partitioned cookie attribution from the document."));
    328    MOZ_ASSERT(false);
    329    return CookieNotificationAction::NoActionNeeded;
    330  }
    331 
    332  CookieKey key(baseDomain, aAttrs);
    333  CookiesList* cookiesList = nullptr;
    334  mCookiesMap.Get(key, &cookiesList);
    335 
    336  if (!cookiesList) {
    337    cookiesList = mCookiesMap.GetOrInsertNew(key);
    338  }
    339 
    340  bool cookieFound = false;
    341 
    342  for (uint32_t i = 0; i < cookiesList->Length(); i++) {
    343    Cookie* cookie = cookiesList->ElementAt(i);
    344    if (cookie->Name().Equals(aCookie->Name()) &&
    345        cookie->Host().Equals(aCookie->Host()) &&
    346        cookie->Path().Equals(aCookie->Path())) {
    347      if (cookie->Value().Equals(aCookie->Value()) &&
    348          cookie->ExpiryInMSec() == aCookie->ExpiryInMSec() &&
    349          cookie->IsSecure() == aCookie->IsSecure() &&
    350          cookie->SameSite() == aCookie->SameSite() &&
    351          cookie->IsSession() == aCookie->IsSession() &&
    352          cookie->IsHttpOnly() == aCookie->IsHttpOnly()) {
    353        cookie->SetLastAccessedInUSec(aCookie->LastAccessedInUSec());
    354        return CookieNotificationAction::NoActionNeeded;
    355      }
    356      cookiesList->RemoveElementAt(i);
    357      cookieFound = true;
    358      break;
    359    }
    360  }
    361 
    362  int64_t currentTimeInMSec = PR_Now() / PR_USEC_PER_MSEC;
    363  if (aCookie->ExpiryInMSec() <= currentTimeInMSec) {
    364    return cookieFound ? CookieNotificationAction::CookieDeleted
    365                       : CookieNotificationAction::NoActionNeeded;
    366  }
    367 
    368  cookiesList->AppendElement(aCookie);
    369  return cookieFound ? CookieNotificationAction::CookieChanged
    370                     : CookieNotificationAction::CookieAdded;
    371 }
    372 
    373 NS_IMETHODIMP
    374 CookieServiceChild::GetCookieStringFromHttp(nsIURI* /*aHostURI*/,
    375                                            nsIChannel* /*aChannel*/,
    376                                            nsACString& /*aCookieString*/) {
    377  MOZ_CRASH("This method should not be called");
    378  return NS_ERROR_NOT_IMPLEMENTED;
    379 }
    380 
    381 NS_IMETHODIMP
    382 CookieServiceChild::SetCookieStringFromHttp(nsIURI* aHostURI,
    383                                            const nsACString& aCookieString,
    384                                            nsIChannel* aChannel) {
    385  MOZ_CRASH("This method should not be called");
    386  return NS_ERROR_NOT_IMPLEMENTED;
    387 }
    388 
    389 NS_IMETHODIMP
    390 CookieServiceChild::RunInTransaction(
    391    nsICookieTransactionCallback* /*aCallback*/) {
    392  return NS_ERROR_NOT_IMPLEMENTED;
    393 }
    394 
    395 void CookieServiceChild::GetCookiesFromHost(
    396    const nsACString& aBaseDomain, const OriginAttributes& aOriginAttributes,
    397    nsTArray<RefPtr<Cookie>>& aCookies) {
    398  CookieKey key(aBaseDomain, aOriginAttributes);
    399 
    400  CookiesList* cookiesList = nullptr;
    401  mCookiesMap.Get(key, &cookiesList);
    402 
    403  if (cookiesList) {
    404    aCookies.AppendElements(*cookiesList);
    405  }
    406 }
    407 
    408 void CookieServiceChild::StaleCookies(const nsTArray<RefPtr<Cookie>>& aCookies,
    409                                      int64_t aCurrentTimeInUsec) {
    410  // Nothing to do here.
    411 }
    412 
    413 bool CookieServiceChild::HasExistingCookies(
    414    const nsACString& aBaseDomain, const OriginAttributes& aOriginAttributes) {
    415  CookiesList* cookiesList = nullptr;
    416 
    417  CookieKey key(aBaseDomain, aOriginAttributes);
    418  mCookiesMap.Get(key, &cookiesList);
    419 
    420  return cookiesList ? cookiesList->Length() : 0;
    421 }
    422 
    423 void CookieServiceChild::AddCookieFromDocument(
    424    CookieParser& aCookieParser, const nsACString& aBaseDomain,
    425    const OriginAttributes& aOriginAttributes, Cookie& aCookie,
    426    int64_t aCurrentTimeInUsec, nsIURI* aDocumentURI, bool aThirdParty,
    427    dom::Document* aDocument) {
    428  MOZ_ASSERT(aDocumentURI);
    429  MOZ_ASSERT(aDocument);
    430 
    431  CookieKey key(aBaseDomain, aOriginAttributes);
    432  CookiesList* cookies = mCookiesMap.Get(key);
    433 
    434  if (cookies) {
    435    // We need to see if the cookie we're setting would overwrite an httponly
    436    // or a secure one. This would not affect anything we send over the net
    437    // (those come from the parent, which already checks this),
    438    // but script could see an inconsistent view of things.
    439 
    440    // CHIPS - If the cookie has the "Partitioned" attribute set it will be
    441    // stored in the partitioned cookie jar.
    442    bool needPartitioned = StaticPrefs::network_cookie_CHIPS_enabled() &&
    443                           aCookie.RawIsPartitioned();
    444    nsCOMPtr<nsIPrincipal> principal =
    445        needPartitioned ? aDocument->PartitionedPrincipal()
    446                        : aDocument->EffectiveCookiePrincipal();
    447    bool isPotentiallyTrustworthy =
    448        principal->GetIsOriginPotentiallyTrustworthy();
    449 
    450    for (uint32_t i = 0; i < cookies->Length(); ++i) {
    451      RefPtr<Cookie> existingCookie = cookies->ElementAt(i);
    452      if (existingCookie->Name().Equals(aCookie.Name()) &&
    453          existingCookie->Host().Equals(aCookie.Host()) &&
    454          existingCookie->Path().Equals(aCookie.Path())) {
    455        // Can't overwrite an httponly cookie from a script context.
    456        if (existingCookie->IsHttpOnly()) {
    457          return;
    458        }
    459 
    460        // prevent insecure cookie from overwriting a secure one in insecure
    461        // context.
    462        if (existingCookie->IsSecure() && !isPotentiallyTrustworthy) {
    463          return;
    464        }
    465      }
    466    }
    467  }
    468 
    469  CookieNotificationAction action =
    470      RecordDocumentCookie(&aCookie, aOriginAttributes);
    471  NotifyObservers(&aCookie, aOriginAttributes, action);
    472 
    473  if (CanSend()) {
    474    nsTArray<CookieStruct> cookiesToSend;
    475    cookiesToSend.AppendElement(aCookie.ToIPC());
    476 
    477    // Asynchronously call the parent.
    478    dom::WindowGlobalChild* windowGlobalChild =
    479        aDocument->GetWindowGlobalChild();
    480 
    481    // If there is no WindowGlobalChild fall back to PCookieService SetCookies.
    482    if (NS_WARN_IF(!windowGlobalChild)) {
    483      SendSetCookies(aBaseDomain, aOriginAttributes, aDocumentURI, false,
    484                     aThirdParty, cookiesToSend);
    485      return;
    486    }
    487 
    488    windowGlobalChild->SendSetCookies(aBaseDomain, aOriginAttributes,
    489                                      aDocumentURI, false, aThirdParty,
    490                                      cookiesToSend);
    491  }
    492 }
    493 
    494 void CookieServiceChild::NotifyObservers(Cookie* aCookie,
    495                                         const OriginAttributes& aAttrs,
    496                                         CookieNotificationAction aAction,
    497                                         const Maybe<nsID>& aOperationID) {
    498  nsICookieNotification::Action notificationAction;
    499  switch (aAction) {
    500    case CookieNotificationAction::NoActionNeeded:
    501      return;
    502 
    503    case CookieNotificationAction::CookieAdded:
    504      notificationAction = nsICookieNotification::COOKIE_ADDED;
    505      break;
    506 
    507    case CookieNotificationAction::CookieChanged:
    508      notificationAction = nsICookieNotification::COOKIE_CHANGED;
    509      break;
    510 
    511    case CookieNotificationAction::CookieDeleted:
    512      notificationAction = nsICookieNotification::COOKIE_DELETED;
    513      break;
    514  }
    515 
    516  nsCOMPtr<nsIObserverService> os = services::GetObserverService();
    517  if (!os) {
    518    return;
    519  }
    520 
    521  nsAutoCString baseDomain;
    522  if (NS_FAILED(CookieCommons::GetBaseDomainFromHost(
    523          mTLDService, aCookie->Host(), baseDomain))) {
    524    MOZ_ASSERT(false,
    525               "CookieServiceChild::NotifyObservers - GetBaseDomainFromHost "
    526               "shouldn't fail");
    527    return;
    528  }
    529 
    530  nsCOMPtr<nsICookieNotification> notification =
    531      new CookieNotification(notificationAction, aCookie, baseDomain, false,
    532                             nullptr, 0, aOperationID.ptrOr(nullptr));
    533 
    534  os->NotifyObservers(
    535      notification,
    536      aAttrs.IsPrivateBrowsing() ? "private-cookie-changed" : "cookie-changed",
    537      u"");
    538 }
    539 
    540 }  // namespace net
    541 }  // namespace mozilla