tor-browser

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

ReferrerInfo.cpp (54442B)


      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 "ReferrerInfo.h"
      8 
      9 #include "mozilla/BasePrincipal.h"
     10 #include "mozilla/ContentBlockingAllowList.h"
     11 #include "mozilla/RefPtr.h"
     12 #include "mozilla/StaticPrefs_network.h"
     13 #include "mozilla/StorageAccess.h"
     14 #include "mozilla/StyleSheet.h"
     15 #include "mozilla/dom/Document.h"
     16 #include "mozilla/dom/Element.h"
     17 #include "mozilla/dom/ReferrerPolicyBinding.h"
     18 #include "mozilla/dom/RequestBinding.h"
     19 #include "mozilla/glean/DomSecurityMetrics.h"
     20 #include "mozilla/net/CookieJarSettings.h"
     21 #include "mozilla/net/HttpBaseChannel.h"
     22 #include "nsCharSeparatedTokenizer.h"
     23 #include "nsContentUtils.h"
     24 #include "nsIClassInfoImpl.h"
     25 #include "nsIEffectiveTLDService.h"
     26 #include "nsIHttpChannel.h"
     27 #include "nsIOService.h"
     28 #include "nsIObjectInputStream.h"
     29 #include "nsIObjectOutputStream.h"
     30 #include "nsIPipe.h"
     31 #include "nsIURL.h"
     32 #include "nsIWebProgressListener.h"
     33 #include "nsNetUtil.h"
     34 #include "nsScriptSecurityManager.h"
     35 #include "nsStreamUtils.h"
     36 #include "nsWhitespaceTokenizer.h"
     37 
     38 static mozilla::LazyLogModule gReferrerInfoLog("ReferrerInfo");
     39 #define LOG(msg) MOZ_LOG(gReferrerInfoLog, mozilla::LogLevel::Debug, msg)
     40 #define LOG_ENABLED() MOZ_LOG_TEST(gReferrerInfoLog, mozilla::LogLevel::Debug)
     41 
     42 using namespace mozilla::net;
     43 
     44 namespace mozilla::dom {
     45 
     46 // Implementation of ClassInfo is required to serialize/deserialize
     47 NS_IMPL_CLASSINFO(ReferrerInfo, nullptr, nsIClassInfo::THREADSAFE,
     48                  REFERRERINFO_CID)
     49 
     50 NS_IMPL_ISUPPORTS_CI(ReferrerInfo, nsIReferrerInfo, nsISerializable)
     51 
     52 #define MAX_REFERRER_SENDING_POLICY 2
     53 #define MAX_CROSS_ORIGIN_SENDING_POLICY 2
     54 #define MAX_TRIMMING_POLICY 2
     55 
     56 #define MIN_REFERRER_SENDING_POLICY 0
     57 #define MIN_CROSS_ORIGIN_SENDING_POLICY 0
     58 #define MIN_TRIMMING_POLICY 0
     59 
     60 /*
     61 * Default referrer policy to use
     62 */
     63 enum DefaultReferrerPolicy : uint32_t {
     64  eDefaultPolicyNoReferrer = 0,
     65  eDefaultPolicySameOrgin = 1,
     66  eDefaultPolicyStrictWhenXorigin = 2,
     67  eDefaultPolicyNoReferrerWhenDownGrade = 3,
     68 };
     69 
     70 static uint32_t GetDefaultFirstPartyReferrerPolicyPref(bool aPrivateBrowsing) {
     71  return aPrivateBrowsing
     72             ? StaticPrefs::network_http_referer_defaultPolicy_pbmode()
     73             : StaticPrefs::network_http_referer_defaultPolicy();
     74 }
     75 
     76 static uint32_t GetDefaultThirdPartyReferrerPolicyPref(bool aPrivateBrowsing) {
     77  return aPrivateBrowsing
     78             ? StaticPrefs::network_http_referer_defaultPolicy_trackers_pbmode()
     79             : StaticPrefs::network_http_referer_defaultPolicy_trackers();
     80 }
     81 
     82 static ReferrerPolicy DefaultReferrerPolicyToReferrerPolicy(
     83    uint32_t aDefaultToUse) {
     84  switch (aDefaultToUse) {
     85    case DefaultReferrerPolicy::eDefaultPolicyNoReferrer:
     86      return ReferrerPolicy::No_referrer;
     87    case DefaultReferrerPolicy::eDefaultPolicySameOrgin:
     88      return ReferrerPolicy::Same_origin;
     89    case DefaultReferrerPolicy::eDefaultPolicyStrictWhenXorigin:
     90      return ReferrerPolicy::Strict_origin_when_cross_origin;
     91  }
     92 
     93  return ReferrerPolicy::No_referrer_when_downgrade;
     94 }
     95 
     96 struct LegacyReferrerPolicyTokenMap {
     97  const char* mToken;
     98  ReferrerPolicy mPolicy;
     99 };
    100 
    101 /*
    102 * Parse ReferrerPolicy from token.
    103 * The supported tokens are defined in ReferrerPolicy.webidl.
    104 * The legacy tokens are "never", "default", "always" and
    105 * "origin-when-crossorigin". The legacy tokens are only supported in meta
    106 * referrer content
    107 *
    108 * @param aContent content string to be transformed into
    109 *                 ReferrerPolicyEnum, e.g. "origin".
    110 */
    111 ReferrerPolicy ReferrerPolicyFromToken(const nsAString& aContent,
    112                                       bool allowedLegacyToken) {
    113  nsString lowerContent(aContent);
    114  ToLowerCase(lowerContent);
    115 
    116  if (allowedLegacyToken) {
    117    static const LegacyReferrerPolicyTokenMap sLegacyReferrerPolicyToken[] = {
    118        {"never", ReferrerPolicy::No_referrer},
    119        {"default", ReferrerPolicy::No_referrer_when_downgrade},
    120        {"always", ReferrerPolicy::Unsafe_url},
    121        {"origin-when-crossorigin", ReferrerPolicy::Origin_when_cross_origin},
    122    };
    123 
    124    uint8_t numStr = (sizeof(sLegacyReferrerPolicyToken) /
    125                      sizeof(sLegacyReferrerPolicyToken[0]));
    126    for (uint8_t i = 0; i < numStr; i++) {
    127      if (lowerContent.EqualsASCII(sLegacyReferrerPolicyToken[i].mToken)) {
    128        return sLegacyReferrerPolicyToken[i].mPolicy;
    129      }
    130    }
    131  }
    132 
    133  // Return no referrer policy (empty string) if it's not a valid enum value.
    134  return StringToEnum<ReferrerPolicy>(lowerContent)
    135      .valueOr(ReferrerPolicy::_empty);
    136 }
    137 
    138 // static
    139 ReferrerPolicy ReferrerInfo::ReferrerPolicyFromMetaString(
    140    const nsAString& aContent) {
    141  // This is implemented as described in
    142  // https://html.spec.whatwg.org/multipage/semantics.html#meta-referrer
    143  // Meta referrer accepts both supported tokens in ReferrerPolicy.webidl and
    144  // legacy tokens.
    145  return ReferrerPolicyFromToken(aContent, true);
    146 }
    147 
    148 // static
    149 ReferrerPolicy ReferrerInfo::ReferrerPolicyAttributeFromString(
    150    const nsAString& aContent) {
    151  // This is implemented as described in
    152  // https://html.spec.whatwg.org/multipage/infrastructure.html#referrer-policy-attribute
    153  // referrerpolicy attribute only accepts supported tokens in
    154  // ReferrerPolicy.webidl
    155  return ReferrerPolicyFromToken(aContent, false);
    156 }
    157 
    158 // static
    159 ReferrerPolicy ReferrerInfo::ReferrerPolicyFromHeaderString(
    160    const nsAString& aContent) {
    161  // Multiple headers could be concatenated into one comma-separated
    162  // list of policies. Need to tokenize the multiple headers.
    163  ReferrerPolicyEnum referrerPolicy = ReferrerPolicy::_empty;
    164  for (const auto& token : nsCharSeparatedTokenizer(aContent, ',').ToRange()) {
    165    if (token.IsEmpty()) {
    166      continue;
    167    }
    168 
    169    // Referrer-Policy header only accepts supported tokens in
    170    // ReferrerPolicy.webidl
    171    ReferrerPolicyEnum policy = ReferrerPolicyFromToken(token, false);
    172    // If there are multiple policies available, the last valid policy should be
    173    // used.
    174    // https://w3c.github.io/webappsec-referrer-policy/#unknown-policy-values
    175    if (policy != ReferrerPolicy::_empty) {
    176      referrerPolicy = policy;
    177    }
    178  }
    179  return referrerPolicy;
    180 }
    181 
    182 /* static */
    183 uint32_t ReferrerInfo::GetUserReferrerSendingPolicy() {
    184  return std::clamp<uint32_t>(
    185      StaticPrefs::network_http_sendRefererHeader_DoNotUseDirectly(),
    186      MIN_REFERRER_SENDING_POLICY, MAX_REFERRER_SENDING_POLICY);
    187 }
    188 
    189 /* static */
    190 uint32_t ReferrerInfo::GetUserXOriginSendingPolicy() {
    191  return std::clamp<uint32_t>(
    192      StaticPrefs::network_http_referer_XOriginPolicy_DoNotUseDirectly(),
    193      MIN_CROSS_ORIGIN_SENDING_POLICY, MAX_CROSS_ORIGIN_SENDING_POLICY);
    194 }
    195 
    196 /* static */
    197 uint32_t ReferrerInfo::GetUserTrimmingPolicy() {
    198  return std::clamp<uint32_t>(
    199      StaticPrefs::network_http_referer_trimmingPolicy_DoNotUseDirectly(),
    200      MIN_TRIMMING_POLICY, MAX_TRIMMING_POLICY);
    201 }
    202 
    203 /* static */
    204 uint32_t ReferrerInfo::GetUserXOriginTrimmingPolicy() {
    205  return std::clamp<uint32_t>(
    206      StaticPrefs::
    207          network_http_referer_XOriginTrimmingPolicy_DoNotUseDirectly(),
    208      MIN_TRIMMING_POLICY, MAX_TRIMMING_POLICY);
    209 }
    210 
    211 /* static */
    212 ReferrerPolicy ReferrerInfo::GetDefaultReferrerPolicy(nsIHttpChannel* aChannel,
    213                                                      nsIURI* aURI,
    214                                                      bool aPrivateBrowsing) {
    215  bool thirdPartyTrackerIsolated = false;
    216  if (aChannel && aURI) {
    217    nsCOMPtr<nsILoadInfo> loadInfo = aChannel->LoadInfo();
    218    nsCOMPtr<nsICookieJarSettings> cjs;
    219    (void)loadInfo->GetCookieJarSettings(getter_AddRefs(cjs));
    220    if (!cjs) {
    221      bool shouldResistFingerprinting =
    222          nsContentUtils::ShouldResistFingerprinting(
    223              aChannel, RFPTarget::IsAlwaysEnabledForPrecompute);
    224      cjs = aPrivateBrowsing
    225                ? net::CookieJarSettings::Create(CookieJarSettings::ePrivate,
    226                                                 shouldResistFingerprinting)
    227                : net::CookieJarSettings::Create(CookieJarSettings::eRegular,
    228                                                 shouldResistFingerprinting);
    229    }
    230 
    231    // We only check if the channel is isolated if it's in the parent process
    232    // with the rejection of third party contexts is enabled. We don't need to
    233    // check this in content processes since the tracking state of the channel
    234    // is unknown here and the referrer policy would be updated when the channel
    235    // starts connecting in the parent process.
    236    if (XRE_IsParentProcess() && cjs->GetRejectThirdPartyContexts()) {
    237      uint32_t rejectedReason = 0;
    238      thirdPartyTrackerIsolated =
    239          !ShouldAllowAccessFor(aChannel, aURI, &rejectedReason) &&
    240          rejectedReason !=
    241              static_cast<uint32_t>(
    242                  nsIWebProgressListener::STATE_COOKIES_PARTITIONED_FOREIGN);
    243      // Here we intentionally do not notify about the rejection reason, if any
    244      // in order to avoid this check to have any visible side-effects (e.g. a
    245      // web console report.)
    246    }
    247  }
    248 
    249  // Select the appropriate pref starting with
    250  // "network.http.referer.defaultPolicy" to use based on private-browsing
    251  // ("pbmode") AND third-party trackers ("trackers").
    252  return DefaultReferrerPolicyToReferrerPolicy(
    253      thirdPartyTrackerIsolated
    254          ? GetDefaultThirdPartyReferrerPolicyPref(aPrivateBrowsing)
    255          : GetDefaultFirstPartyReferrerPolicyPref(aPrivateBrowsing));
    256 }
    257 
    258 /* static */
    259 bool ReferrerInfo::IsReferrerSchemeAllowed(nsIURI* aReferrer) {
    260  NS_ENSURE_TRUE(aReferrer, false);
    261 
    262  nsAutoCString scheme;
    263  nsresult rv = aReferrer->GetScheme(scheme);
    264  if (NS_WARN_IF(NS_FAILED(rv))) {
    265    return false;
    266  }
    267 
    268  return scheme.EqualsIgnoreCase("https") || scheme.EqualsIgnoreCase("http");
    269 }
    270 
    271 /* static */
    272 bool ReferrerInfo::ShouldResponseInheritReferrerInfo(nsIChannel* aChannel) {
    273  if (!aChannel) {
    274    return false;
    275  }
    276 
    277  nsCOMPtr<nsIURI> channelURI;
    278  nsresult rv = aChannel->GetURI(getter_AddRefs(channelURI));
    279  NS_ENSURE_SUCCESS(rv, false);
    280 
    281  return NS_IsAboutSrcdoc(channelURI);
    282 }
    283 
    284 /* static */
    285 nsresult ReferrerInfo::HandleSecureToInsecureReferral(
    286    nsIURI* aOriginalURI, nsIURI* aURI, ReferrerPolicyEnum aPolicy,
    287    bool& aAllowed) {
    288  NS_ENSURE_ARG(aOriginalURI);
    289  NS_ENSURE_ARG(aURI);
    290 
    291  aAllowed = false;
    292 
    293  bool referrerIsHttpsScheme = aOriginalURI->SchemeIs("https");
    294  if (!referrerIsHttpsScheme) {
    295    aAllowed = true;
    296    return NS_OK;
    297  }
    298 
    299  // It's ok to send referrer for https-to-http scenarios if the referrer
    300  // policy is "unsafe-url", "origin", or "origin-when-cross-origin".
    301  // in other referrer policies, https->http is not allowed...
    302  bool uriIsHttpsScheme = aURI->SchemeIs("https");
    303  if (aPolicy != ReferrerPolicy::Unsafe_url &&
    304      aPolicy != ReferrerPolicy::Origin_when_cross_origin &&
    305      aPolicy != ReferrerPolicy::Origin && !uriIsHttpsScheme) {
    306    return NS_OK;
    307  }
    308 
    309  aAllowed = true;
    310  return NS_OK;
    311 }
    312 
    313 nsresult ReferrerInfo::HandleUserXOriginSendingPolicy(nsIURI* aURI,
    314                                                      nsIURI* aReferrer,
    315                                                      bool& aAllowed) const {
    316  NS_ENSURE_ARG(aURI);
    317  aAllowed = false;
    318 
    319  nsAutoCString uriHost;
    320  nsAutoCString referrerHost;
    321 
    322  nsresult rv = aURI->GetAsciiHost(uriHost);
    323  if (NS_WARN_IF(NS_FAILED(rv))) {
    324    return rv;
    325  }
    326 
    327  rv = aReferrer->GetAsciiHost(referrerHost);
    328  if (NS_WARN_IF(NS_FAILED(rv))) {
    329    return rv;
    330  }
    331 
    332  // Send an empty referrer if xorigin and leaving a .onion domain.
    333  if (StaticPrefs::network_http_referer_hideOnionSource() &&
    334      !uriHost.Equals(referrerHost) &&
    335      StringEndsWith(referrerHost, ".onion"_ns)) {
    336    return NS_OK;
    337  }
    338 
    339  switch (GetUserXOriginSendingPolicy()) {
    340    // Check policy for sending referrer only when hosts match
    341    case XOriginSendingPolicy::ePolicySendWhenSameHost: {
    342      if (!uriHost.Equals(referrerHost)) {
    343        return NS_OK;
    344      }
    345      break;
    346    }
    347 
    348    case XOriginSendingPolicy::ePolicySendWhenSameDomain: {
    349      nsCOMPtr<nsIEffectiveTLDService> eTLDService =
    350          do_GetService(NS_EFFECTIVETLDSERVICE_CONTRACTID);
    351      if (!eTLDService) {
    352        // check policy for sending only when effective top level domain
    353        // matches. this falls back on using host if eTLDService does not work
    354        if (!uriHost.Equals(referrerHost)) {
    355          return NS_OK;
    356        }
    357        break;
    358      }
    359 
    360      nsAutoCString uriDomain;
    361      nsAutoCString referrerDomain;
    362      uint32_t extraDomains = 0;
    363 
    364      rv = eTLDService->GetBaseDomain(aURI, extraDomains, uriDomain);
    365      if (rv == NS_ERROR_HOST_IS_IP_ADDRESS ||
    366          rv == NS_ERROR_INSUFFICIENT_DOMAIN_LEVELS) {
    367        // uri is either an IP address, an alias such as 'localhost', an eTLD
    368        // such as 'co.uk', or the empty string. Uses the normalized host in
    369        // such cases.
    370        rv = aURI->GetAsciiHost(uriDomain);
    371      }
    372 
    373      if (NS_WARN_IF(NS_FAILED(rv))) {
    374        return rv;
    375      }
    376 
    377      rv = eTLDService->GetBaseDomain(aReferrer, extraDomains, referrerDomain);
    378      if (rv == NS_ERROR_HOST_IS_IP_ADDRESS ||
    379          rv == NS_ERROR_INSUFFICIENT_DOMAIN_LEVELS) {
    380        // referrer is either an IP address, an alias such as 'localhost', an
    381        // eTLD such as 'co.uk', or the empty string. Uses the normalized host
    382        // in such cases.
    383        rv = aReferrer->GetAsciiHost(referrerDomain);
    384      }
    385 
    386      if (NS_WARN_IF(NS_FAILED(rv))) {
    387        return rv;
    388      }
    389 
    390      if (!uriDomain.Equals(referrerDomain)) {
    391        return NS_OK;
    392      }
    393      break;
    394    }
    395 
    396    default:
    397      break;
    398  }
    399 
    400  aAllowed = true;
    401  return NS_OK;
    402 }
    403 
    404 // This roughly implements Step 3.1. of
    405 // https://fetch.spec.whatwg.org/#append-a-request-origin-header
    406 /* static */
    407 bool ReferrerInfo::ShouldSetNullOriginHeader(net::HttpBaseChannel* aChannel,
    408                                             nsIURI* aOriginURI) {
    409  MOZ_ASSERT(aChannel);
    410  MOZ_ASSERT(aOriginURI);
    411 
    412  // If request’s mode is not "cors", then switch on request’s referrer policy:
    413  RequestMode requestMode = RequestMode::No_cors;
    414  MOZ_ALWAYS_SUCCEEDS(aChannel->GetRequestMode(&requestMode));
    415  if (requestMode == RequestMode::Cors) {
    416    return false;
    417  }
    418 
    419  nsCOMPtr<nsIReferrerInfo> referrerInfo;
    420  NS_ENSURE_SUCCESS(aChannel->GetReferrerInfo(getter_AddRefs(referrerInfo)),
    421                    false);
    422  if (!referrerInfo) {
    423    return false;
    424  }
    425 
    426  // "no-referrer":
    427  enum ReferrerPolicy policy = referrerInfo->ReferrerPolicy();
    428  if (policy == ReferrerPolicy::No_referrer) {
    429    // Set serializedOrigin to `null`.
    430    // Note: Returning true is the same as setting the serializedOrigin to null
    431    // in this method.
    432    return true;
    433  }
    434 
    435  // "no-referrer-when-downgrade":
    436  // "strict-origin":
    437  // "strict-origin-when-cross-origin":
    438  //   If request’s origin is a tuple origin, its scheme is "https", and
    439  //   request’s current URL’s scheme is not "https", then set serializedOrigin
    440  //   to `null`.
    441  bool allowed = false;
    442  nsCOMPtr<nsIURI> uri;
    443  NS_ENSURE_SUCCESS(aChannel->GetURI(getter_AddRefs(uri)), false);
    444  if (NS_SUCCEEDED(ReferrerInfo::HandleSecureToInsecureReferral(
    445          aOriginURI, uri, policy, allowed)) &&
    446      !allowed) {
    447    return true;
    448  }
    449 
    450  // "same-origin":
    451  if (policy == ReferrerPolicy::Same_origin) {
    452    // If request’s origin is not same origin with request’s current URL’s
    453    // origin, then set serializedOrigin to `null`.
    454    return ReferrerInfo::IsCrossOriginRequest(aChannel);
    455  }
    456 
    457  // Otherwise:
    458  //  Do Nothing.
    459  return false;
    460 }
    461 
    462 nsresult ReferrerInfo::HandleUserReferrerSendingPolicy(nsIHttpChannel* aChannel,
    463                                                       bool& aAllowed) const {
    464  aAllowed = false;
    465  uint32_t referrerSendingPolicy;
    466  uint32_t loadFlags;
    467  nsresult rv = aChannel->GetLoadFlags(&loadFlags);
    468  if (NS_WARN_IF(NS_FAILED(rv))) {
    469    return rv;
    470  }
    471 
    472  if (loadFlags & nsIHttpChannel::LOAD_INITIAL_DOCUMENT_URI) {
    473    referrerSendingPolicy = ReferrerSendingPolicy::ePolicySendWhenUserTrigger;
    474  } else {
    475    referrerSendingPolicy = ReferrerSendingPolicy::ePolicySendInlineContent;
    476  }
    477  if (GetUserReferrerSendingPolicy() < referrerSendingPolicy) {
    478    return NS_OK;
    479  }
    480 
    481  aAllowed = true;
    482  return NS_OK;
    483 }
    484 
    485 /* static */
    486 bool ReferrerInfo::IsCrossOriginRequest(nsIHttpChannel* aChannel) {
    487  nsCOMPtr<nsILoadInfo> loadInfo = aChannel->LoadInfo();
    488 
    489  if (!loadInfo->TriggeringPrincipal()->GetIsContentPrincipal()) {
    490    LOG(("no triggering URI via loadInfo, assuming load is cross-origin"));
    491    return true;
    492  }
    493 
    494  if (LOG_ENABLED()) {
    495    nsAutoCString triggeringURISpec;
    496    loadInfo->TriggeringPrincipal()->GetAsciiSpec(triggeringURISpec);
    497    LOG(("triggeringURI=%s\n", triggeringURISpec.get()));
    498  }
    499 
    500  nsCOMPtr<nsIURI> uri;
    501  nsresult rv = aChannel->GetURI(getter_AddRefs(uri));
    502  if (NS_WARN_IF(NS_FAILED(rv))) {
    503    return true;
    504  }
    505 
    506  return !loadInfo->TriggeringPrincipal()->IsSameOrigin(uri);
    507 }
    508 
    509 /* static */
    510 bool ReferrerInfo::IsReferrerCrossOrigin(nsIHttpChannel* aChannel,
    511                                         nsIURI* aReferrer) {
    512  nsCOMPtr<nsILoadInfo> loadInfo = aChannel->LoadInfo();
    513 
    514  if (!loadInfo->TriggeringPrincipal()->GetIsContentPrincipal()) {
    515    LOG(("no triggering URI via loadInfo, assuming load is cross-site"));
    516    return true;
    517  }
    518 
    519  nsCOMPtr<nsIURI> uri;
    520  nsresult rv = aChannel->GetURI(getter_AddRefs(uri));
    521  if (NS_WARN_IF(NS_FAILED(rv))) {
    522    return true;
    523  }
    524 
    525  return !nsScriptSecurityManager::SecurityCompareURIs(uri, aReferrer);
    526 }
    527 
    528 /* static */
    529 bool ReferrerInfo::IsCrossSiteRequest(nsIHttpChannel* aChannel) {
    530  nsCOMPtr<nsILoadInfo> loadInfo = aChannel->LoadInfo();
    531 
    532  if (!loadInfo->TriggeringPrincipal()->GetIsContentPrincipal()) {
    533    LOG(("no triggering URI via loadInfo, assuming load is cross-site"));
    534    return true;
    535  }
    536 
    537  if (LOG_ENABLED()) {
    538    nsAutoCString triggeringURISpec;
    539    loadInfo->TriggeringPrincipal()->GetAsciiSpec(triggeringURISpec);
    540    LOG(("triggeringURI=%s\n", triggeringURISpec.get()));
    541  }
    542 
    543  nsCOMPtr<nsIURI> uri;
    544  nsresult rv = aChannel->GetURI(getter_AddRefs(uri));
    545  if (NS_WARN_IF(NS_FAILED(rv))) {
    546    return true;
    547  }
    548 
    549  bool isCrossSite = true;
    550  rv = loadInfo->TriggeringPrincipal()->IsThirdPartyURI(uri, &isCrossSite);
    551  if (NS_FAILED(rv)) {
    552    return true;
    553  }
    554 
    555  return isCrossSite;
    556 }
    557 
    558 ReferrerInfo::TrimmingPolicy ReferrerInfo::ComputeTrimmingPolicy(
    559    nsIHttpChannel* aChannel, nsIURI* aReferrer) const {
    560  uint32_t trimmingPolicy = GetUserTrimmingPolicy();
    561 
    562  switch (mPolicy) {
    563    case ReferrerPolicy::Origin:
    564    case ReferrerPolicy::Strict_origin:
    565      trimmingPolicy = TrimmingPolicy::ePolicySchemeHostPort;
    566      break;
    567 
    568    case ReferrerPolicy::Origin_when_cross_origin:
    569    case ReferrerPolicy::Strict_origin_when_cross_origin:
    570      if (trimmingPolicy != TrimmingPolicy::ePolicySchemeHostPort &&
    571          IsReferrerCrossOrigin(aChannel, aReferrer)) {
    572        // Ignore set trimmingPolicy if it is already the strictest
    573        // policy.
    574        trimmingPolicy = TrimmingPolicy::ePolicySchemeHostPort;
    575      }
    576      break;
    577 
    578    // This function is called when a nonempty referrer value is allowed to
    579    // send. For the next 3 policies: same-origin, no-referrer-when-downgrade,
    580    // unsafe-url, without trimming we should have a full uri. And the trimming
    581    // policy only depends on user prefs.
    582    case ReferrerPolicy::Same_origin:
    583    case ReferrerPolicy::No_referrer_when_downgrade:
    584    case ReferrerPolicy::Unsafe_url:
    585      if (trimmingPolicy != TrimmingPolicy::ePolicySchemeHostPort) {
    586        // Ignore set trimmingPolicy if it is already the strictest
    587        // policy. Apply the user cross-origin trimming policy if it's more
    588        // restrictive than the general one.
    589        if (GetUserXOriginTrimmingPolicy() != TrimmingPolicy::ePolicyFullURI &&
    590            IsCrossOriginRequest(aChannel)) {
    591          trimmingPolicy =
    592              std::max(trimmingPolicy, GetUserXOriginTrimmingPolicy());
    593        }
    594      }
    595      break;
    596 
    597    case ReferrerPolicy::No_referrer:
    598    case ReferrerPolicy::_empty:
    599    default:
    600      MOZ_ASSERT_UNREACHABLE("Unexpected value");
    601      break;
    602  }
    603 
    604  return static_cast<TrimmingPolicy>(trimmingPolicy);
    605 }
    606 
    607 nsresult ReferrerInfo::LimitReferrerLength(
    608    nsIHttpChannel* aChannel, nsIURI* aReferrer, TrimmingPolicy aTrimmingPolicy,
    609    nsACString& aInAndOutTrimmedReferrer) const {
    610  if (!StaticPrefs::network_http_referer_referrerLengthLimit()) {
    611    return NS_OK;
    612  }
    613 
    614  if (aInAndOutTrimmedReferrer.Length() <=
    615      StaticPrefs::network_http_referer_referrerLengthLimit()) {
    616    return NS_OK;
    617  }
    618 
    619  nsAutoString referrerLengthLimit;
    620  referrerLengthLimit.AppendInt(
    621      StaticPrefs::network_http_referer_referrerLengthLimit());
    622  if (aTrimmingPolicy == ePolicyFullURI ||
    623      aTrimmingPolicy == ePolicySchemeHostPortPath) {
    624    // If referrer header is over max Length, down to origin
    625    nsresult rv = GetOriginFromReferrerURI(aReferrer, aInAndOutTrimmedReferrer);
    626    if (NS_WARN_IF(NS_FAILED(rv))) {
    627      return rv;
    628    }
    629 
    630    // Step 6 within https://w3c.github.io/webappsec-referrer-policy/#strip-url
    631    // states that the trailing "/" does not need to get stripped. However,
    632    // GetOriginFromReferrerURI() also removes any trailing "/" hence we have to
    633    // add it back here.
    634    aInAndOutTrimmedReferrer.AppendLiteral("/");
    635    if (aInAndOutTrimmedReferrer.Length() <=
    636        StaticPrefs::network_http_referer_referrerLengthLimit()) {
    637      AutoTArray<nsString, 2> params = {
    638          referrerLengthLimit, NS_ConvertUTF8toUTF16(aInAndOutTrimmedReferrer)};
    639      LogMessageToConsole(aChannel, "ReferrerLengthOverLimitation", params);
    640      return NS_OK;
    641    }
    642  }
    643 
    644  // If we end up here either the trimmingPolicy is equal to
    645  // 'ePolicySchemeHostPort' or the 'origin' of any other policy is still over
    646  // the length limit. If so, truncate the referrer entirely.
    647  AutoTArray<nsString, 2> params = {
    648      referrerLengthLimit, NS_ConvertUTF8toUTF16(aInAndOutTrimmedReferrer)};
    649  LogMessageToConsole(aChannel, "ReferrerOriginLengthOverLimitation", params);
    650  aInAndOutTrimmedReferrer.Truncate();
    651 
    652  return NS_OK;
    653 }
    654 
    655 nsresult ReferrerInfo::GetOriginFromReferrerURI(nsIURI* aReferrer,
    656                                                nsACString& aResult) const {
    657  MOZ_ASSERT(aReferrer);
    658  aResult.Truncate();
    659  // We want the IDN-normalized PrePath. That's not something currently
    660  // available and there doesn't yet seem to be justification for adding it to
    661  // the interfaces, so just build it up from scheme+AsciiHostPort
    662  nsAutoCString scheme, asciiHostPort;
    663  nsresult rv = aReferrer->GetScheme(scheme);
    664  if (NS_WARN_IF(NS_FAILED(rv))) {
    665    return rv;
    666  }
    667 
    668  aResult = scheme;
    669  aResult.AppendLiteral("://");
    670  // Note we explicitly cleared UserPass above, so do not need to build it.
    671  rv = aReferrer->GetAsciiHostPort(asciiHostPort);
    672  if (NS_WARN_IF(NS_FAILED(rv))) {
    673    return rv;
    674  }
    675 
    676  aResult.Append(asciiHostPort);
    677  return NS_OK;
    678 }
    679 
    680 nsresult ReferrerInfo::TrimReferrerWithPolicy(nsIURI* aReferrer,
    681                                              TrimmingPolicy aTrimmingPolicy,
    682                                              nsACString& aResult) const {
    683  MOZ_ASSERT(aReferrer);
    684 
    685  if (aTrimmingPolicy == TrimmingPolicy::ePolicyFullURI) {
    686    return aReferrer->GetAsciiSpec(aResult);
    687  }
    688 
    689  nsresult rv = GetOriginFromReferrerURI(aReferrer, aResult);
    690  if (NS_WARN_IF(NS_FAILED(rv))) {
    691    return rv;
    692  }
    693 
    694  if (aTrimmingPolicy == TrimmingPolicy::ePolicySchemeHostPortPath) {
    695    nsCOMPtr<nsIURL> url(do_QueryInterface(aReferrer));
    696    if (url) {
    697      nsAutoCString path;
    698      rv = url->GetFilePath(path);
    699      if (NS_WARN_IF(NS_FAILED(rv))) {
    700        return rv;
    701      }
    702 
    703      aResult.Append(path);
    704      return NS_OK;
    705    }
    706  }
    707 
    708  // Step 6 within https://w3c.github.io/webappsec-referrer-policy/#strip-url
    709  // states that the trailing "/" does not need to get stripped. However,
    710  // GetOriginFromReferrerURI() also removes any trailing "/" hence we have to
    711  // add it back here.
    712  aResult.AppendLiteral("/");
    713  return NS_OK;
    714 }
    715 
    716 bool ReferrerInfo::ShouldIgnoreLessRestrictedPolicies(
    717    nsIHttpChannel* aChannel, const ReferrerPolicyEnum aPolicy) const {
    718  MOZ_ASSERT(aChannel);
    719 
    720  // We only care about the less restricted policies.
    721  if (aPolicy != ReferrerPolicy::Unsafe_url &&
    722      aPolicy != ReferrerPolicy::No_referrer_when_downgrade &&
    723      aPolicy != ReferrerPolicy::Origin_when_cross_origin) {
    724    return false;
    725  }
    726 
    727  nsCOMPtr<nsILoadInfo> loadInfo = aChannel->LoadInfo();
    728  bool isPrivate = NS_UsePrivateBrowsing(aChannel);
    729 
    730  // Return early if we don't want to ignore less restricted policies for the
    731  // top navigation.
    732  if (loadInfo->GetExternalContentPolicyType() ==
    733      ExtContentPolicy::TYPE_DOCUMENT) {
    734    bool isEnabledForTopNavigation =
    735        isPrivate
    736            ? StaticPrefs::
    737                  network_http_referer_disallowCrossSiteRelaxingDefault_pbmode_top_navigation()
    738            : StaticPrefs::
    739                  network_http_referer_disallowCrossSiteRelaxingDefault_top_navigation();
    740    if (!isEnabledForTopNavigation) {
    741      return false;
    742    }
    743 
    744    // We have to get the value of the contentBlockingAllowList earlier because
    745    // the channel hasn't been opened yet here. Note that we only need to do
    746    // this for first-party navigation. For third-party loads, the value is
    747    // inherited from the parent.
    748    if (XRE_IsParentProcess()) {
    749      nsCOMPtr<nsICookieJarSettings> cookieJarSettings;
    750      (void)loadInfo->GetCookieJarSettings(getter_AddRefs(cookieJarSettings));
    751 
    752      net::CookieJarSettings::Cast(cookieJarSettings)
    753          ->UpdateIsOnContentBlockingAllowList(aChannel);
    754    }
    755  }
    756 
    757  // We don't ignore less restricted referrer policies if ETP is toggled off.
    758  // This would affect iframe loads and top navigation. For iframes, it will
    759  // stop ignoring if the first-party site toggled ETP off. For top navigation,
    760  // it depends on the ETP toggle for the destination site.
    761  if (ContentBlockingAllowList::Check(aChannel)) {
    762    return false;
    763  }
    764 
    765  bool isCrossSite = IsCrossSiteRequest(aChannel);
    766  bool isEnabled =
    767      isPrivate
    768          ? StaticPrefs::
    769                network_http_referer_disallowCrossSiteRelaxingDefault_pbmode()
    770          : StaticPrefs::
    771                network_http_referer_disallowCrossSiteRelaxingDefault();
    772 
    773  if (!isEnabled) {
    774    // Log the warning message to console to inform that we will ignore
    775    // less restricted policies for cross-site requests in the future.
    776    if (isCrossSite) {
    777      nsCOMPtr<nsIURI> uri;
    778      nsresult rv = aChannel->GetURI(getter_AddRefs(uri));
    779      NS_ENSURE_SUCCESS(rv, false);
    780 
    781      AutoTArray<nsString, 1> params = {
    782          NS_ConvertUTF8toUTF16(uri->GetSpecOrDefault())};
    783      LogMessageToConsole(aChannel, "ReferrerPolicyDisallowRelaxingWarning",
    784                          params);
    785    }
    786    return false;
    787  }
    788 
    789  // Check if the channel is triggered by the system or the extension.
    790  auto* triggerBasePrincipal =
    791      BasePrincipal::Cast(loadInfo->TriggeringPrincipal());
    792  if (triggerBasePrincipal->IsSystemPrincipal() ||
    793      triggerBasePrincipal->AddonPolicy()) {
    794    return false;
    795  }
    796 
    797  if (isCrossSite) {
    798    // Log the console message to say that the less restricted policy was
    799    // ignored.
    800    nsCOMPtr<nsIURI> uri;
    801    nsresult rv = aChannel->GetURI(getter_AddRefs(uri));
    802    NS_ENSURE_SUCCESS(rv, true);
    803 
    804    AutoTArray<nsString, 2> params = {
    805        NS_ConvertUTF8toUTF16(GetEnumString(aPolicy)),
    806        NS_ConvertUTF8toUTF16(uri->GetSpecOrDefault())};
    807    LogMessageToConsole(aChannel, "ReferrerPolicyDisallowRelaxingMessage",
    808                        params);
    809  }
    810 
    811  return isCrossSite;
    812 }
    813 
    814 void ReferrerInfo::LogMessageToConsole(
    815    nsIHttpChannel* aChannel, const char* aMsg,
    816    const nsTArray<nsString>& aParams) const {
    817  MOZ_ASSERT(aChannel);
    818 
    819  nsCOMPtr<nsIURI> uri;
    820  nsresult rv = aChannel->GetURI(getter_AddRefs(uri));
    821  if (NS_WARN_IF(NS_FAILED(rv))) {
    822    return;
    823  }
    824 
    825  uint64_t windowID = 0;
    826 
    827  rv = aChannel->GetTopLevelContentWindowId(&windowID);
    828  if (NS_WARN_IF(NS_FAILED(rv))) {
    829    return;
    830  }
    831 
    832  if (!windowID) {
    833    nsCOMPtr<nsILoadGroup> loadGroup;
    834    rv = aChannel->GetLoadGroup(getter_AddRefs(loadGroup));
    835    if (NS_WARN_IF(NS_FAILED(rv))) {
    836      return;
    837    }
    838 
    839    if (loadGroup) {
    840      windowID = nsContentUtils::GetInnerWindowID(loadGroup);
    841    }
    842  }
    843 
    844  nsAutoString localizedMsg;
    845  rv = nsContentUtils::FormatLocalizedString(
    846      nsContentUtils::eSECURITY_PROPERTIES, aMsg, aParams, localizedMsg);
    847  if (NS_WARN_IF(NS_FAILED(rv))) {
    848    return;
    849  }
    850 
    851  rv = nsContentUtils::ReportToConsoleByWindowID(
    852      localizedMsg, nsIScriptError::infoFlag, "Security"_ns, windowID,
    853      SourceLocation(std::move(uri)));
    854  (void)NS_WARN_IF(NS_FAILED(rv));
    855 }
    856 
    857 ReferrerPolicy ReferrerPolicyIDLToReferrerPolicy(
    858    nsIReferrerInfo::ReferrerPolicyIDL aReferrerPolicy) {
    859  switch (aReferrerPolicy) {
    860    case nsIReferrerInfo::EMPTY:
    861      return ReferrerPolicy::_empty;
    862      break;
    863    case nsIReferrerInfo::NO_REFERRER:
    864      return ReferrerPolicy::No_referrer;
    865      break;
    866    case nsIReferrerInfo::NO_REFERRER_WHEN_DOWNGRADE:
    867      return ReferrerPolicy::No_referrer_when_downgrade;
    868      break;
    869    case nsIReferrerInfo::ORIGIN:
    870      return ReferrerPolicy::Origin;
    871      break;
    872    case nsIReferrerInfo::ORIGIN_WHEN_CROSS_ORIGIN:
    873      return ReferrerPolicy::Origin_when_cross_origin;
    874      break;
    875    case nsIReferrerInfo::UNSAFE_URL:
    876      return ReferrerPolicy::Unsafe_url;
    877      break;
    878    case nsIReferrerInfo::SAME_ORIGIN:
    879      return ReferrerPolicy::Same_origin;
    880      break;
    881    case nsIReferrerInfo::STRICT_ORIGIN:
    882      return ReferrerPolicy::Strict_origin;
    883      break;
    884    case nsIReferrerInfo::STRICT_ORIGIN_WHEN_CROSS_ORIGIN:
    885      return ReferrerPolicy::Strict_origin_when_cross_origin;
    886      break;
    887    default:
    888      MOZ_ASSERT_UNREACHABLE("Invalid ReferrerPolicy value");
    889      break;
    890  }
    891 
    892  return ReferrerPolicy::_empty;
    893 }
    894 
    895 nsIReferrerInfo::ReferrerPolicyIDL ReferrerPolicyToReferrerPolicyIDL(
    896    ReferrerPolicy aReferrerPolicy) {
    897  switch (aReferrerPolicy) {
    898    case ReferrerPolicy::_empty:
    899      return nsIReferrerInfo::EMPTY;
    900      break;
    901    case ReferrerPolicy::No_referrer:
    902      return nsIReferrerInfo::NO_REFERRER;
    903      break;
    904    case ReferrerPolicy::No_referrer_when_downgrade:
    905      return nsIReferrerInfo::NO_REFERRER_WHEN_DOWNGRADE;
    906      break;
    907    case ReferrerPolicy::Origin:
    908      return nsIReferrerInfo::ORIGIN;
    909      break;
    910    case ReferrerPolicy::Origin_when_cross_origin:
    911      return nsIReferrerInfo::ORIGIN_WHEN_CROSS_ORIGIN;
    912      break;
    913    case ReferrerPolicy::Unsafe_url:
    914      return nsIReferrerInfo::UNSAFE_URL;
    915      break;
    916    case ReferrerPolicy::Same_origin:
    917      return nsIReferrerInfo::SAME_ORIGIN;
    918      break;
    919    case ReferrerPolicy::Strict_origin:
    920      return nsIReferrerInfo::STRICT_ORIGIN;
    921      break;
    922    case ReferrerPolicy::Strict_origin_when_cross_origin:
    923      return nsIReferrerInfo::STRICT_ORIGIN_WHEN_CROSS_ORIGIN;
    924      break;
    925    default:
    926      MOZ_ASSERT_UNREACHABLE("Invalid ReferrerPolicy value");
    927      break;
    928  }
    929 
    930  return nsIReferrerInfo::EMPTY;
    931 }
    932 
    933 ReferrerInfo::ReferrerInfo()
    934    : mOriginalReferrer(nullptr),
    935      mPolicy(ReferrerPolicy::_empty),
    936      mOriginalPolicy(ReferrerPolicy::_empty),
    937      mSendReferrer(true),
    938      mInitialized(false),
    939      mOverridePolicyByDefault(false) {}
    940 
    941 ReferrerInfo::ReferrerInfo(const Document& aDoc, const bool aSendReferrer)
    942    : ReferrerInfo() {
    943  InitWithDocument(&aDoc);
    944  mSendReferrer = aSendReferrer;
    945 }
    946 
    947 ReferrerInfo::ReferrerInfo(const Element& aElement) : ReferrerInfo() {
    948  InitWithElement(&aElement);
    949 }
    950 
    951 ReferrerInfo::ReferrerInfo(const Element& aElement,
    952                           ReferrerPolicyEnum aOverridePolicy)
    953    : ReferrerInfo(aElement) {
    954  // Override referrer policy if not empty
    955  if (aOverridePolicy != ReferrerPolicyEnum::_empty) {
    956    mPolicy = aOverridePolicy;
    957    mOriginalPolicy = aOverridePolicy;
    958  }
    959 }
    960 
    961 ReferrerInfo::ReferrerInfo(nsIURI* aOriginalReferrer,
    962                           ReferrerPolicyEnum aPolicy, bool aSendReferrer,
    963                           const Maybe<nsCString>& aComputedReferrer)
    964    : mOriginalReferrer(aOriginalReferrer),
    965      mPolicy(aPolicy),
    966      mOriginalPolicy(aPolicy),
    967      mSendReferrer(aSendReferrer),
    968      mInitialized(true),
    969      mOverridePolicyByDefault(false),
    970      mComputedReferrer(aComputedReferrer) {}
    971 
    972 ReferrerInfo::ReferrerInfo(const ReferrerInfo& rhs)
    973    : mOriginalReferrer(rhs.mOriginalReferrer),
    974      mPolicy(rhs.mPolicy),
    975      mOriginalPolicy(rhs.mOriginalPolicy),
    976      mSendReferrer(rhs.mSendReferrer),
    977      mInitialized(rhs.mInitialized),
    978      mOverridePolicyByDefault(rhs.mOverridePolicyByDefault),
    979      mComputedReferrer(rhs.mComputedReferrer) {}
    980 
    981 already_AddRefed<ReferrerInfo> ReferrerInfo::Clone() const {
    982  RefPtr<ReferrerInfo> copy(new ReferrerInfo(*this));
    983  return copy.forget();
    984 }
    985 
    986 already_AddRefed<ReferrerInfo> ReferrerInfo::CloneWithNewPolicy(
    987    ReferrerPolicyEnum aPolicy) const {
    988  RefPtr<ReferrerInfo> copy(new ReferrerInfo(*this));
    989  copy->mPolicy = aPolicy;
    990  copy->mOriginalPolicy = aPolicy;
    991  return copy.forget();
    992 }
    993 
    994 already_AddRefed<ReferrerInfo> ReferrerInfo::CloneWithNewOriginalReferrer(
    995    nsIURI* aOriginalReferrer) const {
    996  RefPtr<ReferrerInfo> copy(new ReferrerInfo(*this));
    997  copy->mOriginalReferrer = aOriginalReferrer;
    998  return copy.forget();
    999 }
   1000 
   1001 NS_IMETHODIMP
   1002 ReferrerInfo::GetOriginalReferrer(nsIURI** aOriginalReferrer) {
   1003  *aOriginalReferrer = mOriginalReferrer;
   1004  NS_IF_ADDREF(*aOriginalReferrer);
   1005  return NS_OK;
   1006 }
   1007 
   1008 NS_IMETHODIMP
   1009 ReferrerInfo::GetReferrerPolicy(
   1010    JSContext* aCx, nsIReferrerInfo::ReferrerPolicyIDL* aReferrerPolicy) {
   1011  *aReferrerPolicy = ReferrerPolicyToReferrerPolicyIDL(mPolicy);
   1012  return NS_OK;
   1013 }
   1014 
   1015 NS_IMETHODIMP
   1016 ReferrerInfo::GetReferrerPolicyString(nsACString& aResult) {
   1017  aResult.AssignASCII(GetEnumString(mPolicy));
   1018  return NS_OK;
   1019 }
   1020 
   1021 ReferrerPolicy ReferrerInfo::ReferrerPolicy() { return mPolicy; }
   1022 
   1023 NS_IMETHODIMP
   1024 ReferrerInfo::GetSendReferrer(bool* aSendReferrer) {
   1025  *aSendReferrer = mSendReferrer;
   1026  return NS_OK;
   1027 }
   1028 
   1029 NS_IMETHODIMP
   1030 ReferrerInfo::Equals(nsIReferrerInfo* aOther, bool* aResult) {
   1031  NS_ENSURE_TRUE(aOther, NS_ERROR_INVALID_ARG);
   1032  MOZ_ASSERT(mInitialized);
   1033  if (aOther == this) {
   1034    *aResult = true;
   1035    return NS_OK;
   1036  }
   1037 
   1038  *aResult = false;
   1039  ReferrerInfo* other = static_cast<ReferrerInfo*>(aOther);
   1040  MOZ_ASSERT(other->mInitialized);
   1041 
   1042  if (mPolicy != other->mPolicy || mSendReferrer != other->mSendReferrer ||
   1043      mOverridePolicyByDefault != other->mOverridePolicyByDefault ||
   1044      mComputedReferrer != other->mComputedReferrer) {
   1045    return NS_OK;
   1046  }
   1047 
   1048  if (!mOriginalReferrer != !other->mOriginalReferrer) {
   1049    // One or the other has mOriginalReferrer, but not both... not equal
   1050    return NS_OK;
   1051  }
   1052 
   1053  bool originalReferrerEquals;
   1054  if (mOriginalReferrer &&
   1055      (NS_FAILED(mOriginalReferrer->Equals(other->mOriginalReferrer,
   1056                                           &originalReferrerEquals)) ||
   1057       !originalReferrerEquals)) {
   1058    return NS_OK;
   1059  }
   1060 
   1061  *aResult = true;
   1062  return NS_OK;
   1063 }
   1064 
   1065 NS_IMETHODIMP
   1066 ReferrerInfo::GetComputedReferrerSpec(nsACString& aComputedReferrerSpec) {
   1067  aComputedReferrerSpec.Assign(
   1068      mComputedReferrer.isSome() ? mComputedReferrer.value() : EmptyCString());
   1069  return NS_OK;
   1070 }
   1071 
   1072 already_AddRefed<nsIURI> ReferrerInfo::GetComputedReferrer() {
   1073  if (!mComputedReferrer.isSome() || mComputedReferrer.value().IsEmpty()) {
   1074    return nullptr;
   1075  }
   1076 
   1077  nsCOMPtr<nsIURI> result;
   1078  nsresult rv = NS_NewURI(getter_AddRefs(result), mComputedReferrer.value());
   1079  if (NS_FAILED(rv)) {
   1080    return nullptr;
   1081  }
   1082 
   1083  return result.forget();
   1084 }
   1085 
   1086 HashNumber ReferrerInfo::Hash() const {
   1087  MOZ_ASSERT(mInitialized);
   1088  nsAutoCString originalReferrerSpec;
   1089  if (mOriginalReferrer) {
   1090    (void)mOriginalReferrer->GetSpec(originalReferrerSpec);
   1091  }
   1092 
   1093  return mozilla::AddToHash(
   1094      static_cast<uint32_t>(mPolicy), mSendReferrer, mOverridePolicyByDefault,
   1095      mozilla::HashString(originalReferrerSpec),
   1096      mozilla::HashString(mComputedReferrer.isSome() ? mComputedReferrer.value()
   1097                                                     : ""_ns));
   1098 }
   1099 
   1100 NS_IMETHODIMP
   1101 ReferrerInfo::Init(nsIReferrerInfo::ReferrerPolicyIDL aReferrerPolicy,
   1102                   bool aSendReferrer, nsIURI* aOriginalReferrer) {
   1103  MOZ_ASSERT(!mInitialized);
   1104  if (mInitialized) {
   1105    return NS_ERROR_ALREADY_INITIALIZED;
   1106  };
   1107 
   1108  mPolicy = ReferrerPolicyIDLToReferrerPolicy(aReferrerPolicy);
   1109  mOriginalPolicy = mPolicy;
   1110  mSendReferrer = aSendReferrer;
   1111  mOriginalReferrer = aOriginalReferrer;
   1112  mInitialized = true;
   1113  return NS_OK;
   1114 }
   1115 
   1116 NS_IMETHODIMP
   1117 ReferrerInfo::InitWithDocument(const Document* aDocument) {
   1118  MOZ_ASSERT(!mInitialized);
   1119  if (mInitialized) {
   1120    return NS_ERROR_ALREADY_INITIALIZED;
   1121  };
   1122 
   1123  mPolicy = aDocument->GetReferrerPolicy();
   1124  mOriginalPolicy = mPolicy;
   1125  mSendReferrer = true;
   1126  mOriginalReferrer = aDocument->GetDocumentURIAsReferrer();
   1127  mInitialized = true;
   1128  return NS_OK;
   1129 }
   1130 
   1131 /**
   1132 * Check whether the given node has referrerpolicy attribute and parse
   1133 * referrer policy from the attribute.
   1134 * Currently, referrerpolicy attribute is supported in a, area, img, iframe,
   1135 * script, or link element.
   1136 */
   1137 static ReferrerPolicy ReferrerPolicyFromAttribute(const Element& aElement) {
   1138  if (!aElement.IsAnyOfHTMLElements(nsGkAtoms::a, nsGkAtoms::area,
   1139                                    nsGkAtoms::script, nsGkAtoms::iframe,
   1140                                    nsGkAtoms::link, nsGkAtoms::img)) {
   1141    return ReferrerPolicy::_empty;
   1142  }
   1143  return aElement.GetReferrerPolicyAsEnum();
   1144 }
   1145 
   1146 static bool HasRelNoReferrer(const Element& aElement) {
   1147  // rel=noreferrer is only supported in <a>, <area>, and <form>
   1148  if (!aElement.IsAnyOfHTMLElements(nsGkAtoms::a, nsGkAtoms::area,
   1149                                    nsGkAtoms::form) &&
   1150      !aElement.IsSVGElement(nsGkAtoms::a)) {
   1151    return false;
   1152  }
   1153 
   1154  nsAutoString rel;
   1155  aElement.GetAttr(nsGkAtoms::rel, rel);
   1156  nsWhitespaceTokenizerTemplate<nsContentUtils::IsHTMLWhitespace> tok(rel);
   1157 
   1158  while (tok.hasMoreTokens()) {
   1159    const nsAString& token = tok.nextToken();
   1160    if (token.LowerCaseEqualsLiteral("noreferrer")) {
   1161      return true;
   1162    }
   1163  }
   1164 
   1165  return false;
   1166 }
   1167 
   1168 NS_IMETHODIMP
   1169 ReferrerInfo::InitWithElement(const Element* aElement) {
   1170  MOZ_ASSERT(!mInitialized);
   1171  if (mInitialized) {
   1172    return NS_ERROR_ALREADY_INITIALIZED;
   1173  };
   1174 
   1175  // Referrer policy from referrerpolicy attribute will have a higher priority
   1176  // than referrer policy from <meta> tag and Referrer-Policy header.
   1177  mPolicy = ReferrerPolicyFromAttribute(*aElement);
   1178  if (mPolicy == ReferrerPolicy::_empty) {
   1179    // Fallback to use document's referrer poicy if we don't have referrer
   1180    // policy from attribute.
   1181    mPolicy = aElement->OwnerDoc()->GetReferrerPolicy();
   1182  }
   1183 
   1184  mOriginalPolicy = mPolicy;
   1185  mSendReferrer = !HasRelNoReferrer(*aElement);
   1186  mOriginalReferrer = aElement->OwnerDoc()->GetDocumentURIAsReferrer();
   1187 
   1188  mInitialized = true;
   1189  return NS_OK;
   1190 }
   1191 
   1192 /* static */
   1193 already_AddRefed<nsIReferrerInfo>
   1194 ReferrerInfo::CreateFromDocumentAndPolicyOverride(
   1195    Document* aDoc, ReferrerPolicyEnum aPolicyOverride) {
   1196  MOZ_ASSERT(aDoc);
   1197  ReferrerPolicyEnum policy = aPolicyOverride != ReferrerPolicy::_empty
   1198                                  ? aPolicyOverride
   1199                                  : aDoc->GetReferrerPolicy();
   1200  nsCOMPtr<nsIReferrerInfo> referrerInfo =
   1201      new ReferrerInfo(aDoc->GetDocumentURIAsReferrer(), policy);
   1202  return referrerInfo.forget();
   1203 }
   1204 
   1205 /* static */
   1206 already_AddRefed<nsIReferrerInfo> ReferrerInfo::CreateForFetch(
   1207    nsIPrincipal* aPrincipal, Document* aDoc) {
   1208  MOZ_ASSERT(aPrincipal);
   1209 
   1210  nsCOMPtr<nsIReferrerInfo> referrerInfo;
   1211  if (!aPrincipal || aPrincipal->IsSystemPrincipal()) {
   1212    referrerInfo = new ReferrerInfo(nullptr);
   1213    return referrerInfo.forget();
   1214  }
   1215 
   1216  if (!aDoc) {
   1217    aPrincipal->CreateReferrerInfo(ReferrerPolicy::_empty,
   1218                                   getter_AddRefs(referrerInfo));
   1219    return referrerInfo.forget();
   1220  }
   1221 
   1222  // If it weren't for history.push/replaceState, we could just use the
   1223  // principal's URI here.  But since we want changes to the URI effected
   1224  // by push/replaceState to be reflected in the XHR referrer, we have to
   1225  // be more clever.
   1226  //
   1227  // If the document's original URI (before any push/replaceStates) matches
   1228  // our principal, then we use the document's current URI (after
   1229  // push/replaceStates).  Otherwise (if the document is, say, a data:
   1230  // URI), we just use the principal's URI.
   1231  nsCOMPtr<nsIURI> docCurURI = aDoc->GetDocumentURI();
   1232  nsCOMPtr<nsIURI> docOrigURI = aDoc->GetOriginalURI();
   1233 
   1234  if (docCurURI && docOrigURI) {
   1235    bool equal = false;
   1236    aPrincipal->EqualsURI(docOrigURI, &equal);
   1237    if (equal) {
   1238      referrerInfo = new ReferrerInfo(docCurURI, aDoc->GetReferrerPolicy());
   1239      return referrerInfo.forget();
   1240    }
   1241  }
   1242  aPrincipal->CreateReferrerInfo(aDoc->GetReferrerPolicy(),
   1243                                 getter_AddRefs(referrerInfo));
   1244  return referrerInfo.forget();
   1245 }
   1246 
   1247 /* static */
   1248 already_AddRefed<nsIReferrerInfo> ReferrerInfo::CreateForExternalCSSResources(
   1249    mozilla::StyleSheet* aExternalSheet, nsIURI* aExternalSheetURI,
   1250    ReferrerPolicyEnum aPolicy) {
   1251  MOZ_ASSERT(aExternalSheet);
   1252  MOZ_ASSERT(aExternalSheetURI);
   1253  // Step 2
   1254  // https://w3c.github.io/webappsec-referrer-policy/#integration-with-css
   1255  // Use empty policy at the beginning and update it later from Referrer-Policy
   1256  // header.
   1257  nsCOMPtr<nsIReferrerInfo> referrerInfo =
   1258      new ReferrerInfo(aExternalSheetURI, aPolicy);
   1259  return referrerInfo.forget();
   1260 }
   1261 
   1262 /* static */
   1263 already_AddRefed<nsIReferrerInfo>
   1264 ReferrerInfo::CreateForInternalCSSAndSVGResources(Document* aDocument) {
   1265  MOZ_ASSERT(aDocument);
   1266  return do_AddRef(new ReferrerInfo(aDocument->GetDocumentURI(),
   1267                                    aDocument->GetReferrerPolicy()));
   1268 }
   1269 
   1270 nsresult ReferrerInfo::ComputeReferrer(nsIHttpChannel* aChannel) {
   1271  NS_ENSURE_ARG(aChannel);
   1272  MOZ_ASSERT(NS_IsMainThread());
   1273 
   1274  // If the referrerInfo is passed around when redirect, just use the last
   1275  // computedReferrer to recompute
   1276  nsCOMPtr<nsIURI> referrer;
   1277  nsresult rv = NS_OK;
   1278  mOverridePolicyByDefault = false;
   1279 
   1280  if (mComputedReferrer.isSome()) {
   1281    if (mComputedReferrer.value().IsEmpty()) {
   1282      return NS_OK;
   1283    }
   1284 
   1285    rv = NS_NewURI(getter_AddRefs(referrer), mComputedReferrer.value());
   1286    if (NS_WARN_IF(NS_FAILED(rv))) {
   1287      return rv;
   1288    }
   1289  }
   1290 
   1291  mComputedReferrer.reset();
   1292  // Emplace mComputedReferrer with an empty string, which means we have
   1293  // computed the referrer and the result referrer value is empty (not send
   1294  // referrer). So any early return later than this line will use that empty
   1295  // referrer.
   1296  mComputedReferrer.emplace(""_ns);
   1297 
   1298  nsCOMPtr<nsILoadInfo> loadInfo = aChannel->LoadInfo();
   1299  nsCOMPtr<nsIPrincipal> triggeringPrincipal = loadInfo->TriggeringPrincipal();
   1300 
   1301  // Special treatment for resources injected by add-ons.
   1302  if (triggeringPrincipal &&
   1303      StaticPrefs::privacy_antitracking_isolateContentScriptResources() &&
   1304      triggeringPrincipal->GetIsAddonOrExpandedAddonPrincipal()) {
   1305    mPolicy = ReferrerPolicy::No_referrer;
   1306    mOverridePolicyByDefault = true;
   1307    return NS_OK;
   1308  }
   1309 
   1310  if (!mSendReferrer || !mOriginalReferrer ||
   1311      mPolicy == ReferrerPolicy::No_referrer) {
   1312    return NS_OK;
   1313  }
   1314 
   1315  if (mPolicy == ReferrerPolicy::_empty ||
   1316      ShouldIgnoreLessRestrictedPolicies(aChannel, mOriginalPolicy)) {
   1317    OriginAttributes attrs = loadInfo->GetOriginAttributes();
   1318    bool isPrivate = attrs.IsPrivateBrowsing();
   1319 
   1320    nsCOMPtr<nsIURI> uri;
   1321    rv = aChannel->GetURI(getter_AddRefs(uri));
   1322    if (NS_WARN_IF(NS_FAILED(rv))) {
   1323      return rv;
   1324    }
   1325 
   1326    mPolicy = GetDefaultReferrerPolicy(aChannel, uri, isPrivate);
   1327    mOverridePolicyByDefault = true;
   1328  }
   1329 
   1330  // This is for the case where the ETP toggle is off. In this case, we need to
   1331  // reset the referrer and the policy if the original policy is different from
   1332  // the current policy in order to recompute the referrer policy with the
   1333  // original policy.
   1334  if (!mOverridePolicyByDefault && mOriginalPolicy != ReferrerPolicy::_empty &&
   1335      mPolicy != mOriginalPolicy) {
   1336    referrer = nullptr;
   1337    mPolicy = mOriginalPolicy;
   1338  }
   1339 
   1340  if (mPolicy == ReferrerPolicy::No_referrer) {
   1341    return NS_OK;
   1342  }
   1343 
   1344  bool isUserReferrerSendingAllowed = false;
   1345  rv = HandleUserReferrerSendingPolicy(aChannel, isUserReferrerSendingAllowed);
   1346  if (NS_WARN_IF(NS_FAILED(rv))) {
   1347    return rv;
   1348  }
   1349 
   1350  if (!isUserReferrerSendingAllowed) {
   1351    return NS_OK;
   1352  }
   1353 
   1354  // Enforce Referrer allowlist, only http, https scheme are allowed
   1355  if (!IsReferrerSchemeAllowed(mOriginalReferrer)) {
   1356    return NS_OK;
   1357  }
   1358 
   1359  nsCOMPtr<nsIURI> uri;
   1360  rv = aChannel->GetURI(getter_AddRefs(uri));
   1361  if (NS_WARN_IF(NS_FAILED(rv))) {
   1362    return rv;
   1363  }
   1364 
   1365  bool isSecureToInsecureAllowed = false;
   1366  rv = HandleSecureToInsecureReferral(mOriginalReferrer, uri, mPolicy,
   1367                                      isSecureToInsecureAllowed);
   1368  if (NS_WARN_IF(NS_FAILED(rv))) {
   1369    return rv;
   1370  }
   1371 
   1372  if (!isSecureToInsecureAllowed) {
   1373    return NS_OK;
   1374  }
   1375 
   1376  // Strip away any fragment per RFC 2616 section 14.36
   1377  // and Referrer Policy section 6.3.5.
   1378  if (!referrer) {
   1379    rv = NS_GetURIWithoutRef(mOriginalReferrer, getter_AddRefs(referrer));
   1380    if (NS_WARN_IF(NS_FAILED(rv))) {
   1381      return rv;
   1382    }
   1383  }
   1384 
   1385  bool isUserXOriginAllowed = false;
   1386  rv = HandleUserXOriginSendingPolicy(uri, referrer, isUserXOriginAllowed);
   1387  if (NS_WARN_IF(NS_FAILED(rv))) {
   1388    return rv;
   1389  }
   1390 
   1391  if (!isUserXOriginAllowed) {
   1392    return NS_OK;
   1393  }
   1394 
   1395  // Handle user pref network.http.referer.spoofSource, send spoofed referrer if
   1396  // desired
   1397  if (StaticPrefs::network_http_referer_spoofSource()) {
   1398    nsCOMPtr<nsIURI> userSpoofReferrer;
   1399    rv = NS_GetURIWithoutRef(uri, getter_AddRefs(userSpoofReferrer));
   1400    if (NS_WARN_IF(NS_FAILED(rv))) {
   1401      return rv;
   1402    }
   1403    referrer = userSpoofReferrer;
   1404  }
   1405 
   1406  // strip away any userpass; we don't want to be giving out passwords ;-)
   1407  // This is required by Referrer Policy stripping algorithm.
   1408  nsCOMPtr<nsIURI> exposableURI = nsIOService::CreateExposableURI(referrer);
   1409  referrer = exposableURI;
   1410 
   1411  // Don't send referrer when the request is cross-origin and policy is
   1412  // "same-origin".
   1413  if (mPolicy == ReferrerPolicy::Same_origin &&
   1414      IsReferrerCrossOrigin(aChannel, referrer)) {
   1415    return NS_OK;
   1416  }
   1417 
   1418  TrimmingPolicy trimmingPolicy = ComputeTrimmingPolicy(aChannel, referrer);
   1419 
   1420  nsAutoCString trimmedReferrer;
   1421  // We first trim the referrer according to the policy by calling
   1422  // 'TrimReferrerWithPolicy' and right after we have to call
   1423  // 'LimitReferrerLength' (using the same arguments) because the trimmed
   1424  // referrer might exceed the allowed max referrer length.
   1425  rv = TrimReferrerWithPolicy(referrer, trimmingPolicy, trimmedReferrer);
   1426  if (NS_WARN_IF(NS_FAILED(rv))) {
   1427    return rv;
   1428  }
   1429 
   1430  rv = LimitReferrerLength(aChannel, referrer, trimmingPolicy, trimmedReferrer);
   1431  if (NS_WARN_IF(NS_FAILED(rv))) {
   1432    return rv;
   1433  }
   1434 
   1435  // finally, remember the referrer spec.
   1436  mComputedReferrer.reset();
   1437  mComputedReferrer.emplace(trimmedReferrer);
   1438 
   1439  return NS_OK;
   1440 }
   1441 
   1442 /* ===== nsISerializable implementation ====== */
   1443 
   1444 nsresult ReferrerInfo::ReadTailDataBeforeGecko100(
   1445    const uint32_t& aData, nsIObjectInputStream* aInputStream) {
   1446  MOZ_ASSERT(aInputStream);
   1447 
   1448  nsCOMPtr<nsIInputStream> reader;
   1449  nsCOMPtr<nsIOutputStream> writer;
   1450 
   1451  // We need to create a new pipe in order to read the aData and the rest of
   1452  // the input stream together in the old format. This would also help us with
   1453  // handling big endian correctly.
   1454  NS_NewPipe(getter_AddRefs(reader), getter_AddRefs(writer));
   1455 
   1456  nsCOMPtr<nsIBinaryOutputStream> binaryPipeWriter =
   1457      NS_NewObjectOutputStream(writer);
   1458 
   1459  // Write back the aData so that we can read bytes from it and handle big
   1460  // endian correctly.
   1461  nsresult rv = binaryPipeWriter->Write32(aData);
   1462  if (NS_WARN_IF(NS_FAILED(rv))) {
   1463    return rv;
   1464  }
   1465 
   1466  nsCOMPtr<nsIBinaryInputStream> binaryPipeReader =
   1467      NS_NewObjectInputStream(reader);
   1468 
   1469  rv = binaryPipeReader->ReadBoolean(&mSendReferrer);
   1470  if (NS_WARN_IF(NS_FAILED(rv))) {
   1471    return rv;
   1472  }
   1473 
   1474  bool isComputed;
   1475  rv = binaryPipeReader->ReadBoolean(&isComputed);
   1476  if (NS_WARN_IF(NS_FAILED(rv))) {
   1477    return rv;
   1478  }
   1479 
   1480  // We need to handle the following string if isComputed is true.
   1481  if (isComputed) {
   1482    // Comsume the following 2 bytes from the input stream. They are the half
   1483    // part of the length prefix of the following string.
   1484    uint16_t data;
   1485    rv = aInputStream->Read16(&data);
   1486    if (NS_WARN_IF(NS_FAILED(rv))) {
   1487      return rv;
   1488    }
   1489 
   1490    // Write the bytes to the pipe so that we can read the length of the string.
   1491    rv = binaryPipeWriter->Write16(data);
   1492    if (NS_WARN_IF(NS_FAILED(rv))) {
   1493      return rv;
   1494    }
   1495 
   1496    uint32_t length;
   1497    rv = binaryPipeReader->Read32(&length);
   1498    if (NS_WARN_IF(NS_FAILED(rv))) {
   1499      return rv;
   1500    }
   1501 
   1502    // Consume the string body from the input stream.
   1503    nsAutoCString computedReferrer;
   1504    rv = NS_ConsumeStream(aInputStream, length, computedReferrer);
   1505    if (NS_WARN_IF(NS_FAILED(rv))) {
   1506      return rv;
   1507    }
   1508    mComputedReferrer.emplace(computedReferrer);
   1509 
   1510    // Read the remaining two bytes and write to the pipe.
   1511    uint16_t remain;
   1512    rv = aInputStream->Read16(&remain);
   1513    if (NS_WARN_IF(NS_FAILED(rv))) {
   1514      return rv;
   1515    }
   1516 
   1517    rv = binaryPipeWriter->Write16(remain);
   1518    if (NS_WARN_IF(NS_FAILED(rv))) {
   1519      return rv;
   1520    }
   1521  }
   1522 
   1523  rv = binaryPipeReader->ReadBoolean(&mInitialized);
   1524  if (NS_WARN_IF(NS_FAILED(rv))) {
   1525    return rv;
   1526  }
   1527 
   1528  rv = binaryPipeReader->ReadBoolean(&mOverridePolicyByDefault);
   1529  if (NS_WARN_IF(NS_FAILED(rv))) {
   1530    return rv;
   1531  }
   1532 
   1533  return NS_OK;
   1534 }
   1535 
   1536 NS_IMETHODIMP
   1537 ReferrerInfo::Read(nsIObjectInputStream* aStream) {
   1538  bool nonNull;
   1539  nsresult rv = aStream->ReadBoolean(&nonNull);
   1540  if (NS_WARN_IF(NS_FAILED(rv))) {
   1541    return rv;
   1542  }
   1543 
   1544  if (nonNull) {
   1545    nsAutoCString spec;
   1546    nsresult rv = aStream->ReadCString(spec);
   1547    if (NS_WARN_IF(NS_FAILED(rv))) {
   1548      return rv;
   1549    }
   1550 
   1551    rv = NS_NewURI(getter_AddRefs(mOriginalReferrer), spec);
   1552    if (NS_WARN_IF(NS_FAILED(rv))) {
   1553      return rv;
   1554    }
   1555  } else {
   1556    mOriginalReferrer = nullptr;
   1557  }
   1558 
   1559  // ReferrerPolicy.webidl has different order with ReferrerPolicyIDL. We store
   1560  // to disk using the order of ReferrerPolicyIDL, so we convert to
   1561  // ReferrerPolicyIDL to make it be compatible to the old format.
   1562  uint32_t policy;
   1563  rv = aStream->Read32(&policy);
   1564  if (NS_WARN_IF(NS_FAILED(rv))) {
   1565    return rv;
   1566  }
   1567  mPolicy = ReferrerPolicyIDLToReferrerPolicy(
   1568      static_cast<nsIReferrerInfo::ReferrerPolicyIDL>(policy));
   1569 
   1570  uint32_t originalPolicy;
   1571  rv = aStream->Read32(&originalPolicy);
   1572  if (NS_WARN_IF(NS_FAILED(rv))) {
   1573    return rv;
   1574  }
   1575 
   1576  // See https://bugzilla.mozilla.org/show_bug.cgi?id=1784045#c6 for more
   1577  // details.
   1578  //
   1579  // We need to differentiate the old format and the new format here in order
   1580  // to be able to read both formats. The check here helps us with verifying
   1581  // which format it is.
   1582  if (MOZ_UNLIKELY(originalPolicy > 0xFF)) {
   1583    mOriginalPolicy = mPolicy;
   1584 
   1585    return ReadTailDataBeforeGecko100(originalPolicy, aStream);
   1586  }
   1587 
   1588  mOriginalPolicy = ReferrerPolicyIDLToReferrerPolicy(
   1589      static_cast<nsIReferrerInfo::ReferrerPolicyIDL>(originalPolicy));
   1590 
   1591  rv = aStream->ReadBoolean(&mSendReferrer);
   1592  if (NS_WARN_IF(NS_FAILED(rv))) {
   1593    return rv;
   1594  }
   1595 
   1596  bool isComputed;
   1597  rv = aStream->ReadBoolean(&isComputed);
   1598  if (NS_WARN_IF(NS_FAILED(rv))) {
   1599    return rv;
   1600  }
   1601 
   1602  if (isComputed) {
   1603    nsAutoCString computedReferrer;
   1604    rv = aStream->ReadCString(computedReferrer);
   1605    if (NS_WARN_IF(NS_FAILED(rv))) {
   1606      return rv;
   1607    }
   1608    mComputedReferrer.emplace(computedReferrer);
   1609  }
   1610 
   1611  rv = aStream->ReadBoolean(&mInitialized);
   1612  if (NS_WARN_IF(NS_FAILED(rv))) {
   1613    return rv;
   1614  }
   1615 
   1616  rv = aStream->ReadBoolean(&mOverridePolicyByDefault);
   1617  if (NS_WARN_IF(NS_FAILED(rv))) {
   1618    return rv;
   1619  }
   1620 
   1621  return NS_OK;
   1622 }
   1623 
   1624 NS_IMETHODIMP
   1625 ReferrerInfo::Write(nsIObjectOutputStream* aStream) {
   1626  bool nonNull = (mOriginalReferrer != nullptr);
   1627  nsresult rv = aStream->WriteBoolean(nonNull);
   1628  if (NS_WARN_IF(NS_FAILED(rv))) {
   1629    return rv;
   1630  }
   1631 
   1632  if (nonNull) {
   1633    nsAutoCString spec;
   1634    nsresult rv = mOriginalReferrer->GetSpec(spec);
   1635    if (NS_WARN_IF(NS_FAILED(rv))) {
   1636      return rv;
   1637    }
   1638 
   1639    rv = aStream->WriteStringZ(spec.get());
   1640    if (NS_WARN_IF(NS_FAILED(rv))) {
   1641      return rv;
   1642    }
   1643  }
   1644 
   1645  rv = aStream->Write32(ReferrerPolicyToReferrerPolicyIDL(mPolicy));
   1646  if (NS_WARN_IF(NS_FAILED(rv))) {
   1647    return rv;
   1648  }
   1649 
   1650  rv = aStream->Write32(ReferrerPolicyToReferrerPolicyIDL(mOriginalPolicy));
   1651  if (NS_WARN_IF(NS_FAILED(rv))) {
   1652    return rv;
   1653  }
   1654 
   1655  rv = aStream->WriteBoolean(mSendReferrer);
   1656  if (NS_WARN_IF(NS_FAILED(rv))) {
   1657    return rv;
   1658  }
   1659 
   1660  bool isComputed = mComputedReferrer.isSome();
   1661  rv = aStream->WriteBoolean(isComputed);
   1662  if (NS_WARN_IF(NS_FAILED(rv))) {
   1663    return rv;
   1664  }
   1665 
   1666  if (isComputed) {
   1667    rv = aStream->WriteStringZ(mComputedReferrer.value().get());
   1668    if (NS_WARN_IF(NS_FAILED(rv))) {
   1669      return rv;
   1670    }
   1671  }
   1672 
   1673  rv = aStream->WriteBoolean(mInitialized);
   1674  if (NS_WARN_IF(NS_FAILED(rv))) {
   1675    return rv;
   1676  }
   1677 
   1678  rv = aStream->WriteBoolean(mOverridePolicyByDefault);
   1679  if (NS_WARN_IF(NS_FAILED(rv))) {
   1680    return rv;
   1681  }
   1682  return NS_OK;
   1683 }
   1684 
   1685 void ReferrerInfo::RecordTelemetry(nsIHttpChannel* aChannel) {
   1686 #ifdef DEBUG
   1687  MOZ_ASSERT(!mTelemetryRecorded);
   1688  mTelemetryRecorded = true;
   1689 #endif  // DEBUG
   1690 
   1691  // The telemetry probe has 18 buckets. The first 9 buckets are for same-site
   1692  // requests and the rest 9 buckets are for cross-site requests.
   1693  uint32_t telemetryOffset =
   1694      IsCrossSiteRequest(aChannel)
   1695          ? UnderlyingValue(
   1696                MaxContiguousEnumValue<dom::ReferrerPolicy>::value) +
   1697                1
   1698          : 0;
   1699 
   1700  glean::security::referrer_policy_count.AccumulateSingleSample(
   1701      static_cast<uint32_t>(mPolicy) + telemetryOffset);
   1702 }
   1703 
   1704 }  // namespace mozilla::dom
   1705 
   1706 #undef LOG
   1707 #undef LOG_ENABLED