tor-browser

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

HttpBaseChannel.cpp (228831B)


      1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
      2 /* vim: set sw=2 ts=8 et tw=80 : */
      3 
      4 /* This Source Code Form is subject to the terms of the Mozilla Public
      5 * License, v. 2.0. If a copy of the MPL was not distributed with this
      6 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
      7 
      8 // HttpLog.h should generally be included first
      9 #include "mozilla/net/HttpBaseChannel.h"
     10 
     11 #include <algorithm>
     12 #include <utility>
     13 
     14 #include "HttpBaseChannel.h"
     15 #include "HttpLog.h"
     16 #include "LoadInfo.h"
     17 #include "ReferrerInfo.h"
     18 #include "mozIRemoteLazyInputStream.h"
     19 #include "mozIThirdPartyUtil.h"
     20 #include "mozilla/LoadInfo.h"
     21 #include "mozilla/AntiTrackingUtils.h"
     22 #include "mozilla/BasePrincipal.h"
     23 #include "mozilla/BinarySearch.h"
     24 #include "mozilla/CompactPair.h"
     25 #include "mozilla/ConsoleReportCollector.h"
     26 #include "mozilla/DebugOnly.h"
     27 #include "mozilla/InputStreamLengthHelper.h"
     28 #include "mozilla/Mutex.h"
     29 #include "mozilla/NullPrincipal.h"
     30 #include "mozilla/PermissionManager.h"
     31 #include "mozilla/Components.h"
     32 #include "mozilla/StaticPrefs_browser.h"
     33 #include "mozilla/StaticPrefs_fission.h"
     34 #include "mozilla/StaticPrefs_network.h"
     35 #include "mozilla/StaticPrefs_security.h"
     36 #include "mozilla/glean/NetwerkProtocolHttpMetrics.h"
     37 #include "mozilla/Telemetry.h"
     38 #include "mozilla/Tokenizer.h"
     39 #include "mozilla/browser/NimbusFeatures.h"
     40 #include "mozilla/dom/BrowsingContext.h"
     41 #include "mozilla/dom/CanonicalBrowsingContext.h"
     42 #include "mozilla/dom/Document.h"
     43 #include "mozilla/dom/FetchPriority.h"
     44 #include "mozilla/dom/LoadURIOptionsBinding.h"
     45 #include "mozilla/dom/nsHTTPSOnlyUtils.h"
     46 #include "mozilla/dom/nsMixedContentBlocker.h"
     47 #include "mozilla/dom/Performance.h"
     48 #include "mozilla/dom/PerformanceStorage.h"
     49 #include "mozilla/dom/PolicyContainer.h"
     50 #include "mozilla/dom/ProcessIsolation.h"
     51 #include "mozilla/dom/RequestBinding.h"
     52 #include "mozilla/dom/WindowGlobalParent.h"
     53 #include "mozilla/net/OpaqueResponseUtils.h"
     54 #include "mozilla/net/UrlClassifierCommon.h"
     55 #include "mozilla/net/UrlClassifierFeatureFactory.h"
     56 #include "mozilla/StaticPrefs_javascript.h"
     57 #include "nsBufferedStreams.h"
     58 #include "nsCOMPtr.h"
     59 #include "nsCRT.h"
     60 #include "nsContentSecurityManager.h"
     61 #include "nsContentSecurityUtils.h"
     62 #include "nsContentUtils.h"
     63 #include "nsDebug.h"
     64 #include "nsEscape.h"
     65 #include "nsGlobalWindowInner.h"
     66 #include "nsGlobalWindowOuter.h"
     67 #include "nsHttpChannel.h"
     68 #include "nsHTTPCompressConv.h"
     69 #include "nsHttpHandler.h"
     70 #include "nsICacheInfoChannel.h"
     71 #include "nsICachingChannel.h"
     72 #include "nsIChannelEventSink.h"
     73 #include "nsIConsoleService.h"
     74 #include "nsIContentPolicy.h"
     75 #include "nsICookieService.h"
     76 #include "nsIDOMWindowUtils.h"
     77 #include "nsIDocShell.h"
     78 #include "nsIDNSService.h"
     79 #include "nsIEncodedChannel.h"
     80 #include "nsIHttpHeaderVisitor.h"
     81 #include "nsILoadGroupChild.h"
     82 #include "nsIMIMEInputStream.h"
     83 #include "nsIMultiplexInputStream.h"
     84 #include "nsIMutableArray.h"
     85 #include "nsINetworkInterceptController.h"
     86 #include "nsIObserverService.h"
     87 #include "nsIPrincipal.h"
     88 #include "nsIProtocolProxyService.h"
     89 #include "nsIScriptError.h"
     90 #include "nsIScriptSecurityManager.h"
     91 #include "nsISecurityConsoleMessage.h"
     92 #include "nsISeekableStream.h"
     93 #include "nsIStorageStream.h"
     94 #include "nsIStreamConverterService.h"
     95 #include "nsITimedChannel.h"
     96 #include "nsITransportSecurityInfo.h"
     97 #include "nsIURIMutator.h"
     98 #include "nsMimeTypes.h"
     99 #include "nsNetCID.h"
    100 #include "nsNetUtil.h"
    101 #include "nsPIDOMWindow.h"
    102 #include "nsProxyRelease.h"
    103 #include "nsReadableUtils.h"
    104 #include "nsRedirectHistoryEntry.h"
    105 #include "nsServerTiming.h"
    106 #include "nsStreamListenerWrapper.h"
    107 #include "nsStreamUtils.h"
    108 #include "nsString.h"
    109 #include "nsThreadUtils.h"
    110 #include "nsURLHelper.h"
    111 #include "mozilla/RemoteLazyInputStreamChild.h"
    112 #include "mozilla/net/SFVService.h"
    113 #include "mozilla/dom/ContentChild.h"
    114 #include "nsQueryObject.h"
    115 
    116 using mozilla::dom::ForceMediaDocument;
    117 using mozilla::dom::RequestMode;
    118 
    119 #define LOGORB(msg, ...)                \
    120  MOZ_LOG(GetORBLog(), LogLevel::Debug, \
    121          ("%s: %p " msg, __func__, this, ##__VA_ARGS__))
    122 
    123 namespace mozilla {
    124 namespace net {
    125 
    126 static bool IsHeaderBlacklistedForRedirectCopy(nsHttpAtom const& aHeader) {
    127  // IMPORTANT: keep this list ASCII-code sorted
    128  static nsHttpAtomLiteral const* blackList[] = {
    129      &nsHttp::Accept,
    130      &nsHttp::Accept_Encoding,
    131      &nsHttp::Accept_Language,
    132      &nsHttp::Alternate_Service_Used,
    133      &nsHttp::Authentication,
    134      &nsHttp::Authorization,
    135      &nsHttp::Connection,
    136      &nsHttp::Content_Length,
    137      &nsHttp::Cookie,
    138      &nsHttp::Host,
    139      &nsHttp::If,
    140      &nsHttp::If_Match,
    141      &nsHttp::If_Modified_Since,
    142      &nsHttp::If_None_Match,
    143      &nsHttp::If_None_Match_Any,
    144      &nsHttp::If_Range,
    145      &nsHttp::If_Unmodified_Since,
    146      &nsHttp::Proxy_Authenticate,
    147      &nsHttp::Proxy_Authorization,
    148      &nsHttp::Range,
    149      &nsHttp::TE,
    150      &nsHttp::Transfer_Encoding,
    151      &nsHttp::Upgrade,
    152      &nsHttp::User_Agent,
    153      &nsHttp::WWW_Authenticate};
    154 
    155  class HttpAtomComparator {
    156    nsHttpAtom const& mTarget;
    157 
    158   public:
    159    explicit HttpAtomComparator(nsHttpAtom const& aTarget) : mTarget(aTarget) {}
    160    int operator()(nsHttpAtom const* aVal) const {
    161      if (mTarget == *aVal) {
    162        return 0;
    163      }
    164      return strcmp(mTarget.get(), aVal->get());
    165    }
    166    int operator()(nsHttpAtomLiteral const* aVal) const {
    167      if (mTarget == *aVal) {
    168        return 0;
    169      }
    170      return strcmp(mTarget.get(), aVal->get());
    171    }
    172  };
    173 
    174  size_t unused;
    175  return BinarySearchIf(blackList, 0, std::size(blackList),
    176                        HttpAtomComparator(aHeader), &unused);
    177 }
    178 
    179 class AddHeadersToChannelVisitor final : public nsIHttpHeaderVisitor {
    180 public:
    181  NS_DECL_ISUPPORTS
    182 
    183  explicit AddHeadersToChannelVisitor(nsIHttpChannel* aChannel)
    184      : mChannel(aChannel) {}
    185 
    186  NS_IMETHOD VisitHeader(const nsACString& aHeader,
    187                         const nsACString& aValue) override {
    188    nsHttpAtom atom = nsHttp::ResolveAtom(aHeader);
    189    if (!IsHeaderBlacklistedForRedirectCopy(atom)) {
    190      DebugOnly<nsresult> rv =
    191          mChannel->SetRequestHeader(aHeader, aValue, false);
    192      MOZ_ASSERT(NS_SUCCEEDED(rv));
    193    }
    194    return NS_OK;
    195  }
    196 
    197 private:
    198  ~AddHeadersToChannelVisitor() = default;
    199 
    200  nsCOMPtr<nsIHttpChannel> mChannel;
    201 };
    202 
    203 NS_IMPL_ISUPPORTS(AddHeadersToChannelVisitor, nsIHttpHeaderVisitor)
    204 
    205 static OpaqueResponseFilterFetch ConfiguredFilterFetchResponseBehaviour() {
    206  uint32_t pref = StaticPrefs::
    207      browser_opaqueResponseBlocking_filterFetchResponse_DoNotUseDirectly();
    208  if (NS_WARN_IF(pref >
    209                 static_cast<uint32_t>(OpaqueResponseFilterFetch::All))) {
    210    return OpaqueResponseFilterFetch::All;
    211  }
    212 
    213  return static_cast<OpaqueResponseFilterFetch>(pref);
    214 }
    215 
    216 HttpBaseChannel::HttpBaseChannel()
    217    : mReportCollector(new ConsoleReportCollector()),
    218      mHttpHandler(gHttpHandler),
    219      mClassOfService(0, false),
    220      mRequestMode(RequestMode::No_cors),
    221      mRedirectionLimit(gHttpHandler->RedirectionLimit()),
    222      mCachedOpaqueResponseBlockingPref(
    223          StaticPrefs::browser_opaqueResponseBlocking()) {
    224  StoreApplyConversion(true);
    225  StoreAllowSTS(true);
    226  StoreTracingEnabled(true);
    227  StoreReportTiming(true);
    228  StoreAllowSpdy(true);
    229  StoreAllowHttp3(true);
    230  StoreAllowAltSvc(true);
    231  StoreResponseTimeoutEnabled(true);
    232  StoreAllRedirectsSameOrigin(true);
    233  StoreAllRedirectsPassTimingAllowCheck(true);
    234  StoreUpgradableToSecure(true);
    235  StoreIsUserAgentHeaderModified(false);
    236 
    237  this->mSelfAddr.inet = {};
    238  this->mPeerAddr.inet = {};
    239  LOG(("Creating HttpBaseChannel @%p\n", this));
    240 
    241  // Subfields of unions cannot be targeted in an initializer list.
    242 #ifdef MOZ_VALGRIND
    243  // Zero the entire unions so that Valgrind doesn't complain when we send them
    244  // to another process.
    245  memset(&mSelfAddr, 0, sizeof(NetAddr));
    246  memset(&mPeerAddr, 0, sizeof(NetAddr));
    247 #endif
    248  mSelfAddr.raw.family = PR_AF_UNSPEC;
    249  mPeerAddr.raw.family = PR_AF_UNSPEC;
    250 }
    251 
    252 HttpBaseChannel::~HttpBaseChannel() {
    253  LOG(("Destroying HttpBaseChannel @%p\n", this));
    254 
    255  // Make sure we don't leak
    256  CleanRedirectCacheChainIfNecessary();
    257 
    258  ReleaseMainThreadOnlyReferences();
    259 }
    260 
    261 namespace {  // anon
    262 
    263 class NonTailRemover : public nsISupports {
    264  NS_DECL_THREADSAFE_ISUPPORTS
    265 
    266  explicit NonTailRemover(nsIRequestContext* rc) : mRequestContext(rc) {}
    267 
    268 private:
    269  virtual ~NonTailRemover() {
    270    MOZ_ASSERT(NS_IsMainThread());
    271    mRequestContext->RemoveNonTailRequest();
    272  }
    273 
    274  nsCOMPtr<nsIRequestContext> mRequestContext;
    275 };
    276 
    277 NS_IMPL_ISUPPORTS0(NonTailRemover)
    278 
    279 }  // namespace
    280 
    281 void HttpBaseChannel::ReleaseMainThreadOnlyReferences() {
    282  if (NS_IsMainThread()) {
    283    // Already on main thread, let dtor to
    284    // take care of releasing references
    285    RemoveAsNonTailRequest();
    286    return;
    287  }
    288 
    289  nsTArray<nsCOMPtr<nsISupports>> arrayToRelease;
    290  arrayToRelease.AppendElement(mLoadGroup.forget());
    291  arrayToRelease.AppendElement(mLoadInfo.forget());
    292  arrayToRelease.AppendElement(mCallbacks.forget());
    293  arrayToRelease.AppendElement(mProgressSink.forget());
    294  arrayToRelease.AppendElement(mPrincipal.forget());
    295  arrayToRelease.AppendElement(mListener.forget());
    296  arrayToRelease.AppendElement(mCompressListener.forget());
    297  arrayToRelease.AppendElement(mORB.forget());
    298 
    299  if (LoadAddedAsNonTailRequest()) {
    300    // RemoveNonTailRequest() on our request context must be called on the main
    301    // thread
    302    MOZ_RELEASE_ASSERT(mRequestContext,
    303                       "Someone released rc or set flags w/o having it?");
    304 
    305    nsCOMPtr<nsISupports> nonTailRemover(new NonTailRemover(mRequestContext));
    306    arrayToRelease.AppendElement(nonTailRemover.forget());
    307  }
    308 
    309  NS_DispatchToMainThread(new ProxyReleaseRunnable(std::move(arrayToRelease)));
    310 }
    311 
    312 void HttpBaseChannel::AddClassificationFlags(uint32_t aClassificationFlags,
    313                                             bool aIsThirdParty) {
    314  LOG(
    315      ("HttpBaseChannel::AddClassificationFlags classificationFlags=%d "
    316       "thirdparty=%d %p",
    317       aClassificationFlags, static_cast<int>(aIsThirdParty), this));
    318 
    319  if (aIsThirdParty) {
    320    mThirdPartyClassificationFlags |= aClassificationFlags;
    321  } else {
    322    mFirstPartyClassificationFlags |= aClassificationFlags;
    323  }
    324 }
    325 
    326 static bool isSecureOrTrustworthyURL(nsIURI* aURI) {
    327  return aURI->SchemeIs("https") ||
    328         nsMixedContentBlocker::IsPotentiallyTrustworthyOnion(aURI) ||
    329         (StaticPrefs::network_http_encoding_trustworthy_is_https() &&
    330          nsMixedContentBlocker::IsPotentiallyTrustworthyLoopbackURL(aURI));
    331 }
    332 
    333 nsresult HttpBaseChannel::Init(nsIURI* aURI, uint32_t aCaps,
    334                               nsProxyInfo* aProxyInfo,
    335                               uint32_t aProxyResolveFlags, nsIURI* aProxyURI,
    336                               uint64_t aChannelId, nsILoadInfo* aLoadInfo) {
    337  LOG1(("HttpBaseChannel::Init [this=%p]\n", this));
    338 
    339  MOZ_ASSERT(aURI, "null uri");
    340 
    341  mURI = aURI;
    342  mOriginalURI = aURI;
    343  mDocumentURI = nullptr;
    344  mCaps = aCaps;
    345  mProxyResolveFlags = aProxyResolveFlags;
    346  mProxyURI = aProxyURI;
    347  mChannelId = aChannelId;
    348  mLoadInfo = aLoadInfo;
    349 
    350  // Construct connection info object
    351  nsAutoCString host;
    352  int32_t port = -1;
    353 
    354  nsresult rv = mURI->GetAsciiHost(host);
    355  if (NS_FAILED(rv)) return rv;
    356 
    357  // Reject the URL if it doesn't specify a host
    358  if (host.IsEmpty()) return NS_ERROR_MALFORMED_URI;
    359 
    360  rv = mURI->GetPort(&port);
    361  if (NS_FAILED(rv)) return rv;
    362 
    363  LOG1(("host=%s port=%d\n", host.get(), port));
    364 
    365  rv = mURI->GetAsciiSpec(mSpec);
    366  if (NS_FAILED(rv)) return rv;
    367  LOG1(("uri=%s\n", mSpec.get()));
    368 
    369  // Assert default request method
    370  MOZ_ASSERT(mRequestHead.EqualsMethod(nsHttpRequestHead::kMethod_Get));
    371 
    372  // Set request headers
    373  nsAutoCString hostLine;
    374  rv = nsHttpHandler::GenerateHostPort(host, port, hostLine);
    375  if (NS_FAILED(rv)) return rv;
    376 
    377  rv = mRequestHead.SetHeader(nsHttp::Host, hostLine);
    378  if (NS_FAILED(rv)) return rv;
    379 
    380  // Override the Accept header if a specific MediaDocument kind is forced.
    381  ExtContentPolicy contentPolicyType =
    382      mLoadInfo->GetExternalContentPolicyType();
    383  // TRRLoadInfo doesn't implement GetForceMediaDocument.
    384  ForceMediaDocument forceMediaDocument;
    385  if (NS_SUCCEEDED(mLoadInfo->GetForceMediaDocument(&forceMediaDocument))) {
    386    switch (forceMediaDocument) {
    387      case ForceMediaDocument::Image:
    388        contentPolicyType = ExtContentPolicy::TYPE_IMAGE;
    389        break;
    390      case ForceMediaDocument::Video:
    391        contentPolicyType = ExtContentPolicy::TYPE_MEDIA;
    392        break;
    393      case ForceMediaDocument::None:
    394        break;
    395    }
    396  }
    397 
    398  RefPtr<mozilla::dom::BrowsingContext> browsingContext;
    399  mLoadInfo->GetBrowsingContext(getter_AddRefs(browsingContext));
    400 
    401  const nsCString& languageOverride =
    402      browsingContext ? browsingContext->Top()->GetLanguageOverride()
    403                      : EmptyCString();
    404 
    405  rv = gHttpHandler->AddStandardRequestHeaders(
    406      &mRequestHead, aURI, isSecureOrTrustworthyURL(mURI), contentPolicyType,
    407      nsContentUtils::ShouldResistFingerprinting(this,
    408                                                 RFPTarget::HttpUserAgent),
    409      languageOverride);
    410  if (NS_FAILED(rv)) return rv;
    411 
    412  nsAutoCString type;
    413  if (aProxyInfo && NS_SUCCEEDED(aProxyInfo->GetType(type)) &&
    414      !type.EqualsLiteral("unknown")) {
    415    mProxyInfo = aProxyInfo;
    416  }
    417 
    418  mCurrentThread = GetCurrentSerialEventTarget();
    419  return rv;
    420 }
    421 
    422 //-----------------------------------------------------------------------------
    423 // HttpBaseChannel::nsISupports
    424 //-----------------------------------------------------------------------------
    425 
    426 NS_IMPL_ADDREF(HttpBaseChannel)
    427 NS_IMPL_RELEASE(HttpBaseChannel)
    428 
    429 NS_INTERFACE_MAP_BEGIN(HttpBaseChannel)
    430  NS_INTERFACE_MAP_ENTRY(nsIRequest)
    431  NS_INTERFACE_MAP_ENTRY(nsIChannel)
    432  NS_INTERFACE_MAP_ENTRY(nsIIdentChannel)
    433  NS_INTERFACE_MAP_ENTRY(nsIEncodedChannel)
    434  NS_INTERFACE_MAP_ENTRY(nsIHttpChannel)
    435  NS_INTERFACE_MAP_ENTRY(nsIHttpChannelInternal)
    436  NS_INTERFACE_MAP_ENTRY(nsIForcePendingChannel)
    437  NS_INTERFACE_MAP_ENTRY(nsIUploadChannel)
    438  NS_INTERFACE_MAP_ENTRY(nsIFormPOSTActionChannel)
    439  NS_INTERFACE_MAP_ENTRY(nsIUploadChannel2)
    440  NS_INTERFACE_MAP_ENTRY(nsISupportsPriority)
    441  NS_INTERFACE_MAP_ENTRY(nsITraceableChannel)
    442  NS_INTERFACE_MAP_ENTRY(nsIPrivateBrowsingChannel)
    443  NS_INTERFACE_MAP_ENTRY(nsITimedChannel)
    444  NS_INTERFACE_MAP_ENTRY(nsIConsoleReportCollector)
    445  NS_INTERFACE_MAP_ENTRY(nsIThrottledInputChannel)
    446  NS_INTERFACE_MAP_ENTRY(nsIClassifiedChannel)
    447  NS_INTERFACE_MAP_ENTRY_CONCRETE(HttpBaseChannel)
    448 NS_INTERFACE_MAP_END_INHERITING(nsHashPropertyBag)
    449 
    450 //-----------------------------------------------------------------------------
    451 // HttpBaseChannel::nsIRequest
    452 //-----------------------------------------------------------------------------
    453 
    454 NS_IMETHODIMP
    455 HttpBaseChannel::GetName(nsACString& aName) {
    456  aName = mSpec;
    457  return NS_OK;
    458 }
    459 
    460 NS_IMETHODIMP
    461 HttpBaseChannel::IsPending(bool* aIsPending) {
    462  NS_ENSURE_ARG_POINTER(aIsPending);
    463  *aIsPending = LoadIsPending() || LoadForcePending();
    464  return NS_OK;
    465 }
    466 
    467 NS_IMETHODIMP
    468 HttpBaseChannel::GetStatus(nsresult* aStatus) {
    469  NS_ENSURE_ARG_POINTER(aStatus);
    470  *aStatus = mStatus;
    471  return NS_OK;
    472 }
    473 
    474 NS_IMETHODIMP
    475 HttpBaseChannel::GetLoadGroup(nsILoadGroup** aLoadGroup) {
    476  NS_ENSURE_ARG_POINTER(aLoadGroup);
    477  *aLoadGroup = do_AddRef(mLoadGroup).take();
    478  return NS_OK;
    479 }
    480 
    481 NS_IMETHODIMP
    482 HttpBaseChannel::SetLoadGroup(nsILoadGroup* aLoadGroup) {
    483  MOZ_ASSERT(NS_IsMainThread(), "Should only be called on the main thread.");
    484 
    485  if (!CanSetLoadGroup(aLoadGroup)) {
    486    return NS_ERROR_FAILURE;
    487  }
    488 
    489  mLoadGroup = aLoadGroup;
    490  mProgressSink = nullptr;
    491  UpdatePrivateBrowsing();
    492  return NS_OK;
    493 }
    494 
    495 NS_IMETHODIMP
    496 HttpBaseChannel::GetLoadFlags(nsLoadFlags* aLoadFlags) {
    497  NS_ENSURE_ARG_POINTER(aLoadFlags);
    498  *aLoadFlags = mLoadFlags;
    499  return NS_OK;
    500 }
    501 
    502 NS_IMETHODIMP
    503 HttpBaseChannel::SetLoadFlags(nsLoadFlags aLoadFlags) {
    504  mLoadFlags = aLoadFlags;
    505  return NS_OK;
    506 }
    507 
    508 NS_IMETHODIMP
    509 HttpBaseChannel::GetTRRMode(nsIRequest::TRRMode* aTRRMode) {
    510  if (!LoadIsOCSP()) {
    511    return GetTRRModeImpl(aTRRMode);
    512  }
    513 
    514  nsCOMPtr<nsIDNSService> dns = do_GetService(NS_DNSSERVICE_CONTRACTID);
    515  nsIDNSService::ResolverMode trrMode = nsIDNSService::MODE_NATIVEONLY;
    516  // If this is an OCSP channel, and the global TRR mode is TRR_ONLY (3)
    517  // then we set the mode for this channel as TRR_DISABLED_MODE.
    518  // We do this to prevent a TRR service channel's OCSP validation from
    519  // blocking DNS resolution completely.
    520  if (dns && NS_SUCCEEDED(dns->GetCurrentTrrMode(&trrMode)) &&
    521      trrMode == nsIDNSService::MODE_TRRONLY) {
    522    *aTRRMode = nsIRequest::TRR_DISABLED_MODE;
    523    return NS_OK;
    524  }
    525 
    526  return GetTRRModeImpl(aTRRMode);
    527 }
    528 
    529 NS_IMETHODIMP
    530 HttpBaseChannel::SetTRRMode(nsIRequest::TRRMode aTRRMode) {
    531  return SetTRRModeImpl(aTRRMode);
    532 }
    533 
    534 NS_IMETHODIMP
    535 HttpBaseChannel::SetDocshellUserAgentOverride() {
    536  RefPtr<dom::BrowsingContext> bc;
    537  MOZ_ALWAYS_SUCCEEDS(mLoadInfo->GetBrowsingContext(getter_AddRefs(bc)));
    538  if (!bc) {
    539    return NS_OK;
    540  }
    541 
    542  nsAutoString customUserAgent;
    543  bc->GetCustomUserAgent(customUserAgent);
    544  if (customUserAgent.IsEmpty() || customUserAgent.IsVoid()) {
    545    return NS_OK;
    546  }
    547 
    548  NS_ConvertUTF16toUTF8 utf8CustomUserAgent(customUserAgent);
    549  nsresult rv = SetRequestHeaderInternal(
    550      "User-Agent"_ns, utf8CustomUserAgent, false,
    551      nsHttpHeaderArray::eVarietyRequestEnforceDefault);
    552  if (NS_FAILED(rv)) {
    553    return rv;
    554  }
    555 
    556  return NS_OK;
    557 }
    558 
    559 //-----------------------------------------------------------------------------
    560 // HttpBaseChannel::nsIChannel
    561 //-----------------------------------------------------------------------------
    562 
    563 NS_IMETHODIMP
    564 HttpBaseChannel::GetOriginalURI(nsIURI** aOriginalURI) {
    565  NS_ENSURE_ARG_POINTER(aOriginalURI);
    566  *aOriginalURI = do_AddRef(mOriginalURI).take();
    567  return NS_OK;
    568 }
    569 
    570 NS_IMETHODIMP
    571 HttpBaseChannel::SetOriginalURI(nsIURI* aOriginalURI) {
    572  ENSURE_CALLED_BEFORE_CONNECT();
    573 
    574  NS_ENSURE_ARG_POINTER(aOriginalURI);
    575  mOriginalURI = aOriginalURI;
    576  return NS_OK;
    577 }
    578 
    579 NS_IMETHODIMP
    580 HttpBaseChannel::GetURI(nsIURI** aURI) {
    581  NS_ENSURE_ARG_POINTER(aURI);
    582  *aURI = do_AddRef(mURI).take();
    583  return NS_OK;
    584 }
    585 
    586 NS_IMETHODIMP
    587 HttpBaseChannel::GetOwner(nsISupports** aOwner) {
    588  NS_ENSURE_ARG_POINTER(aOwner);
    589  *aOwner = do_AddRef(mOwner).take();
    590  return NS_OK;
    591 }
    592 
    593 NS_IMETHODIMP
    594 HttpBaseChannel::SetOwner(nsISupports* aOwner) {
    595  mOwner = aOwner;
    596  return NS_OK;
    597 }
    598 
    599 NS_IMETHODIMP
    600 HttpBaseChannel::SetLoadInfo(nsILoadInfo* aLoadInfo) {
    601  MOZ_RELEASE_ASSERT(aLoadInfo, "loadinfo can't be null");
    602  mLoadInfo = aLoadInfo;
    603  return NS_OK;
    604 }
    605 
    606 NS_IMETHODIMP
    607 HttpBaseChannel::GetLoadInfo(nsILoadInfo** aLoadInfo) {
    608  *aLoadInfo = do_AddRef(mLoadInfo).take();
    609  return NS_OK;
    610 }
    611 
    612 NS_IMETHODIMP
    613 HttpBaseChannel::GetIsDocument(bool* aIsDocument) {
    614  return NS_GetIsDocumentChannel(this, aIsDocument);
    615 }
    616 
    617 NS_IMETHODIMP
    618 HttpBaseChannel::GetNotificationCallbacks(nsIInterfaceRequestor** aCallbacks) {
    619  *aCallbacks = do_AddRef(mCallbacks).take();
    620  return NS_OK;
    621 }
    622 
    623 NS_IMETHODIMP
    624 HttpBaseChannel::SetNotificationCallbacks(nsIInterfaceRequestor* aCallbacks) {
    625  MOZ_ASSERT(NS_IsMainThread(), "Should only be called on the main thread.");
    626 
    627  if (!CanSetCallbacks(aCallbacks)) {
    628    return NS_ERROR_FAILURE;
    629  }
    630 
    631  mCallbacks = aCallbacks;
    632  mProgressSink = nullptr;
    633 
    634  UpdatePrivateBrowsing();
    635  return NS_OK;
    636 }
    637 
    638 NS_IMETHODIMP
    639 HttpBaseChannel::GetContentType(nsACString& aContentType) {
    640  if (!mResponseHead) {
    641    aContentType.Truncate();
    642    return NS_ERROR_NOT_AVAILABLE;
    643  }
    644 
    645  mResponseHead->ContentType(aContentType);
    646  if (!aContentType.IsEmpty()) {
    647    return NS_OK;
    648  }
    649 
    650  aContentType.AssignLiteral(UNKNOWN_CONTENT_TYPE);
    651  return NS_OK;
    652 }
    653 
    654 NS_IMETHODIMP
    655 HttpBaseChannel::SetContentType(const nsACString& aContentType) {
    656  if (mListener || LoadWasOpened() || mDummyChannelForCachedResource) {
    657    if (!mResponseHead) return NS_ERROR_NOT_AVAILABLE;
    658 
    659    nsAutoCString contentTypeBuf, charsetBuf;
    660    bool hadCharset;
    661    net_ParseContentType(aContentType, contentTypeBuf, charsetBuf, &hadCharset);
    662 
    663    mResponseHead->SetContentType(contentTypeBuf);
    664 
    665    // take care not to stomp on an existing charset
    666    if (hadCharset) mResponseHead->SetContentCharset(charsetBuf);
    667 
    668  } else {
    669    // We are being given a content-type hint.
    670    bool dummy;
    671    net_ParseContentType(aContentType, mContentTypeHint, mContentCharsetHint,
    672                         &dummy);
    673  }
    674 
    675  return NS_OK;
    676 }
    677 
    678 NS_IMETHODIMP
    679 HttpBaseChannel::GetContentCharset(nsACString& aContentCharset) {
    680  if (!mResponseHead) return NS_ERROR_NOT_AVAILABLE;
    681 
    682  mResponseHead->ContentCharset(aContentCharset);
    683  return NS_OK;
    684 }
    685 
    686 NS_IMETHODIMP
    687 HttpBaseChannel::SetContentCharset(const nsACString& aContentCharset) {
    688  if (mListener) {
    689    if (!mResponseHead) return NS_ERROR_NOT_AVAILABLE;
    690 
    691    mResponseHead->SetContentCharset(aContentCharset);
    692  } else {
    693    // Charset hint
    694    mContentCharsetHint = aContentCharset;
    695  }
    696  return NS_OK;
    697 }
    698 
    699 NS_IMETHODIMP
    700 HttpBaseChannel::GetContentDisposition(uint32_t* aContentDisposition) {
    701  if (mLoadInfo->GetForceMediaDocument() != ForceMediaDocument::None) {
    702    *aContentDisposition = nsIChannel::DISPOSITION_FORCE_INLINE;
    703    return NS_OK;
    704  }
    705 
    706  // See bug 1658877. If mContentDispositionHint is already
    707  // DISPOSITION_ATTACHMENT, it means this channel is created from a
    708  // download attribute. In this case, we should prefer the value from the
    709  // download attribute rather than the value in content disposition header.
    710  // DISPOSITION_FORCE_INLINE is used to explicitly set inline, used by
    711  // the pdf reader when loading a attachment pdf without having to
    712  // download it.
    713  if (mContentDispositionHint == nsIChannel::DISPOSITION_ATTACHMENT ||
    714      mContentDispositionHint == nsIChannel::DISPOSITION_FORCE_INLINE) {
    715    *aContentDisposition = mContentDispositionHint;
    716    return NS_OK;
    717  }
    718 
    719  nsresult rv;
    720  nsCString header;
    721 
    722  rv = GetContentDispositionHeader(header);
    723  if (NS_FAILED(rv)) {
    724    if (mContentDispositionHint == UINT32_MAX) return rv;
    725 
    726    *aContentDisposition = mContentDispositionHint;
    727    return NS_OK;
    728  }
    729 
    730  *aContentDisposition = NS_GetContentDispositionFromHeader(header, this);
    731  return NS_OK;
    732 }
    733 
    734 NS_IMETHODIMP
    735 HttpBaseChannel::SetContentDisposition(uint32_t aContentDisposition) {
    736  mContentDispositionHint = aContentDisposition;
    737  return NS_OK;
    738 }
    739 
    740 NS_IMETHODIMP
    741 HttpBaseChannel::GetContentDispositionFilename(
    742    nsAString& aContentDispositionFilename) {
    743  aContentDispositionFilename.Truncate();
    744  nsresult rv;
    745  nsCString header;
    746 
    747  rv = GetContentDispositionHeader(header);
    748  if (NS_SUCCEEDED(rv)) {
    749    rv = NS_GetFilenameFromDisposition(aContentDispositionFilename, header);
    750  }
    751 
    752  // If we failed to get the filename from header, we should use
    753  // mContentDispositionFilename, since mContentDispositionFilename is set from
    754  // the download attribute.
    755  if (NS_FAILED(rv)) {
    756    if (!mContentDispositionFilename) {
    757      return rv;
    758    }
    759 
    760    aContentDispositionFilename = *mContentDispositionFilename;
    761    return NS_OK;
    762  }
    763 
    764  return rv;
    765 }
    766 
    767 NS_IMETHODIMP
    768 HttpBaseChannel::SetContentDispositionFilename(
    769    const nsAString& aContentDispositionFilename) {
    770  mContentDispositionFilename =
    771      MakeUnique<nsString>(aContentDispositionFilename);
    772 
    773  // For safety reasons ensure the filename doesn't contain null characters and
    774  // replace them with underscores. We may later pass the extension to system
    775  // MIME APIs that expect null terminated strings.
    776  mContentDispositionFilename->ReplaceChar(char16_t(0), '_');
    777 
    778  return NS_OK;
    779 }
    780 
    781 NS_IMETHODIMP
    782 HttpBaseChannel::GetContentDispositionHeader(
    783    nsACString& aContentDispositionHeader) {
    784  if (!mResponseHead) return NS_ERROR_NOT_AVAILABLE;
    785 
    786  nsresult rv = mResponseHead->GetHeader(nsHttp::Content_Disposition,
    787                                         aContentDispositionHeader);
    788  if (NS_FAILED(rv) || aContentDispositionHeader.IsEmpty()) {
    789    return NS_ERROR_NOT_AVAILABLE;
    790  }
    791 
    792  return NS_OK;
    793 }
    794 
    795 NS_IMETHODIMP
    796 HttpBaseChannel::GetContentLength(int64_t* aContentLength) {
    797  NS_ENSURE_ARG_POINTER(aContentLength);
    798 
    799  if (!mResponseHead) return NS_ERROR_NOT_AVAILABLE;
    800 
    801  if (LoadDeliveringAltData()) {
    802    MOZ_ASSERT(!mAvailableCachedAltDataType.IsEmpty());
    803    *aContentLength = mAltDataLength;
    804    return NS_OK;
    805  }
    806 
    807  *aContentLength = mResponseHead->ContentLength();
    808  return NS_OK;
    809 }
    810 
    811 NS_IMETHODIMP
    812 HttpBaseChannel::SetContentLength(int64_t value) {
    813  if (!mDummyChannelForCachedResource) {
    814    MOZ_ASSERT_UNREACHABLE("HttpBaseChannel::SetContentLength");
    815    return NS_ERROR_NOT_IMPLEMENTED;
    816  }
    817  MOZ_ASSERT(mResponseHead);
    818  mResponseHead->SetContentLength(value);
    819  return NS_OK;
    820 }
    821 
    822 NS_IMETHODIMP
    823 HttpBaseChannel::Open(nsIInputStream** aStream) {
    824  if (!gHttpHandler->Active()) {
    825    LOG(("HttpBaseChannel::Open after HTTP shutdown..."));
    826    return NS_ERROR_NOT_AVAILABLE;
    827  }
    828 
    829  nsCOMPtr<nsIStreamListener> listener;
    830  nsresult rv =
    831      nsContentSecurityManager::doContentSecurityCheck(this, listener);
    832  NS_ENSURE_SUCCESS(rv, rv);
    833 
    834  NS_ENSURE_TRUE(!LoadWasOpened(), NS_ERROR_IN_PROGRESS);
    835 
    836  if (!gHttpHandler->Active()) {
    837    LOG(("HttpBaseChannel::Open after HTTP shutdown..."));
    838    return NS_ERROR_NOT_AVAILABLE;
    839  }
    840 
    841  return NS_ImplementChannelOpen(this, aStream);
    842 }
    843 
    844 //-----------------------------------------------------------------------------
    845 // HttpBaseChannel::nsIUploadChannel
    846 //-----------------------------------------------------------------------------
    847 
    848 NS_IMETHODIMP
    849 HttpBaseChannel::GetUploadStream(nsIInputStream** stream) {
    850  NS_ENSURE_ARG_POINTER(stream);
    851  *stream = do_AddRef(mUploadStream).take();
    852  return NS_OK;
    853 }
    854 
    855 NS_IMETHODIMP
    856 HttpBaseChannel::SetUploadStream(nsIInputStream* stream,
    857                                 const nsACString& contentTypeArg,
    858                                 int64_t contentLength) {
    859  // NOTE: for backwards compatibility and for compatibility with old style
    860  // plugins, |stream| may include headers, specifically Content-Type and
    861  // Content-Length headers.  in this case, |contentType| and |contentLength|
    862  // would be unspecified.  this is traditionally the case of a POST request,
    863  // and so we select POST as the request method if contentType and
    864  // contentLength are unspecified.
    865 
    866  if (stream) {
    867    nsAutoCString method;
    868    bool hasHeaders = false;
    869 
    870    // This method and ExplicitSetUploadStream mean different things by "empty
    871    // content type string".  This method means "no header", but
    872    // ExplicitSetUploadStream means "header with empty value".  So we have to
    873    // massage the contentType argument into the form ExplicitSetUploadStream
    874    // expects.
    875    nsCOMPtr<nsIMIMEInputStream> mimeStream;
    876    nsCString contentType(contentTypeArg);
    877    if (contentType.IsEmpty()) {
    878      contentType.SetIsVoid(true);
    879      method = "POST"_ns;
    880 
    881      // MIME streams are a special case, and include headers which need to be
    882      // copied to the channel.
    883      mimeStream = do_QueryInterface(stream);
    884      if (mimeStream) {
    885        // Copy non-origin related headers to the channel.
    886        nsCOMPtr<nsIHttpHeaderVisitor> visitor =
    887            new AddHeadersToChannelVisitor(this);
    888        mimeStream->VisitHeaders(visitor);
    889 
    890        return ExplicitSetUploadStream(stream, contentType, contentLength,
    891                                       method, hasHeaders);
    892      }
    893 
    894      hasHeaders = true;
    895    } else {
    896      method = "PUT"_ns;
    897 
    898      MOZ_ASSERT(
    899          NS_FAILED(CallQueryInterface(stream, getter_AddRefs(mimeStream))),
    900          "nsIMIMEInputStream should not be set with an explicit content type");
    901    }
    902    return ExplicitSetUploadStream(stream, contentType, contentLength, method,
    903                                   hasHeaders);
    904  }
    905 
    906  // if stream is null, ExplicitSetUploadStream returns error.
    907  // So we need special case for GET method.
    908  StoreUploadStreamHasHeaders(false);
    909  SetRequestMethod("GET"_ns);  // revert to GET request
    910  mUploadStream = nullptr;
    911  return NS_OK;
    912 }
    913 
    914 namespace {
    915 
    916 class MIMEHeaderCopyVisitor final : public nsIHttpHeaderVisitor {
    917 public:
    918  explicit MIMEHeaderCopyVisitor(nsIMIMEInputStream* aDest) : mDest(aDest) {}
    919 
    920  NS_DECL_ISUPPORTS
    921  NS_IMETHOD VisitHeader(const nsACString& aName,
    922                         const nsACString& aValue) override {
    923    return mDest->AddHeader(PromiseFlatCString(aName).get(),
    924                            PromiseFlatCString(aValue).get());
    925  }
    926 
    927 private:
    928  ~MIMEHeaderCopyVisitor() = default;
    929 
    930  nsCOMPtr<nsIMIMEInputStream> mDest;
    931 };
    932 
    933 NS_IMPL_ISUPPORTS(MIMEHeaderCopyVisitor, nsIHttpHeaderVisitor)
    934 
    935 static void NormalizeCopyComplete(void* aClosure, nsresult aStatus) {
    936 #ifdef DEBUG
    937  // Called on the STS thread by NS_AsyncCopy
    938  nsCOMPtr<nsIEventTarget> sts =
    939      do_GetService(NS_STREAMTRANSPORTSERVICE_CONTRACTID);
    940  bool result = false;
    941  sts->IsOnCurrentThread(&result);
    942  MOZ_ASSERT(result, "Should only be called on the STS thread.");
    943 #endif
    944 
    945  RefPtr<GenericPromise::Private> ready =
    946      already_AddRefed(static_cast<GenericPromise::Private*>(aClosure));
    947  if (NS_SUCCEEDED(aStatus)) {
    948    ready->Resolve(true, __func__);
    949  } else {
    950    ready->Reject(aStatus, __func__);
    951  }
    952 }
    953 
    954 // Normalize the upload stream for a HTTP channel, so that is one of the
    955 // expected and compatible types. Components like WebExtensions and DevTools
    956 // expect that upload streams in the parent process are cloneable, seekable, and
    957 // synchronous to read, which this function helps guarantee somewhat efficiently
    958 // and without loss of information.
    959 //
    960 // If the replacement stream outparameter is not initialized to `nullptr`, the
    961 // returned stream should be used instead of `aUploadStream` as the upload
    962 // stream for the HTTP channel, and the previous stream should not be touched
    963 // again.
    964 //
    965 // If aReadyPromise is non-nullptr after the function is called, it is a promise
    966 // which should be awaited before continuing to `AsyncOpen` the HTTP channel,
    967 // as the replacement stream will not be ready until it is resolved.
    968 static nsresult NormalizeUploadStream(nsIInputStream* aUploadStream,
    969                                      nsIInputStream** aReplacementStream,
    970                                      GenericPromise** aReadyPromise) {
    971  MOZ_ASSERT(XRE_IsParentProcess());
    972 
    973  *aReplacementStream = nullptr;
    974  *aReadyPromise = nullptr;
    975 
    976  // Unwrap RemoteLazyInputStream and normalize the contents as we're in the
    977  // parent process.
    978  if (nsCOMPtr<mozIRemoteLazyInputStream> lazyStream =
    979          do_QueryInterface(aUploadStream)) {
    980    nsCOMPtr<nsIInputStream> internal;
    981    if (NS_SUCCEEDED(
    982            lazyStream->TakeInternalStream(getter_AddRefs(internal)))) {
    983      nsCOMPtr<nsIInputStream> replacement;
    984      nsresult rv = NormalizeUploadStream(internal, getter_AddRefs(replacement),
    985                                          aReadyPromise);
    986      NS_ENSURE_SUCCESS(rv, rv);
    987 
    988      if (replacement) {
    989        replacement.forget(aReplacementStream);
    990      } else {
    991        internal.forget(aReplacementStream);
    992      }
    993      return NS_OK;
    994    }
    995  }
    996 
    997  // Preserve MIME information on the stream when normalizing.
    998  if (nsCOMPtr<nsIMIMEInputStream> mime = do_QueryInterface(aUploadStream)) {
    999    nsCOMPtr<nsIInputStream> data;
   1000    nsresult rv = mime->GetData(getter_AddRefs(data));
   1001    NS_ENSURE_SUCCESS(rv, rv);
   1002 
   1003    nsCOMPtr<nsIInputStream> replacement;
   1004    rv =
   1005        NormalizeUploadStream(data, getter_AddRefs(replacement), aReadyPromise);
   1006    NS_ENSURE_SUCCESS(rv, rv);
   1007 
   1008    if (replacement) {
   1009      nsCOMPtr<nsIMIMEInputStream> replacementMime(
   1010          do_CreateInstance("@mozilla.org/network/mime-input-stream;1", &rv));
   1011      NS_ENSURE_SUCCESS(rv, rv);
   1012 
   1013      nsCOMPtr<nsIHttpHeaderVisitor> visitor =
   1014          new MIMEHeaderCopyVisitor(replacementMime);
   1015      rv = mime->VisitHeaders(visitor);
   1016      NS_ENSURE_SUCCESS(rv, rv);
   1017 
   1018      rv = replacementMime->SetData(replacement);
   1019      NS_ENSURE_SUCCESS(rv, rv);
   1020 
   1021      replacementMime.forget(aReplacementStream);
   1022    }
   1023    return NS_OK;
   1024  }
   1025 
   1026  // Preserve "real" buffered input streams which wrap data (i.e. are backed by
   1027  // nsBufferedInputStream), but normalize the wrapped stream.
   1028  if (nsCOMPtr<nsIBufferedInputStream> buffered =
   1029          do_QueryInterface(aUploadStream)) {
   1030    nsCOMPtr<nsIInputStream> data;
   1031    if (NS_SUCCEEDED(buffered->GetData(getter_AddRefs(data)))) {
   1032      nsCOMPtr<nsIInputStream> replacement;
   1033      nsresult rv = NormalizeUploadStream(data, getter_AddRefs(replacement),
   1034                                          aReadyPromise);
   1035      NS_ENSURE_SUCCESS(rv, rv);
   1036      if (replacement) {
   1037        // This buffer size should be kept in sync with HTMLFormSubmission.
   1038        rv = NS_NewBufferedInputStream(aReplacementStream, replacement.forget(),
   1039                                       8192);
   1040        NS_ENSURE_SUCCESS(rv, rv);
   1041      }
   1042      return NS_OK;
   1043    }
   1044  }
   1045 
   1046  // Preserve multiplex input streams, normalizing each individual inner stream
   1047  // to avoid unnecessary copying.
   1048  if (nsCOMPtr<nsIMultiplexInputStream> multiplex =
   1049          do_QueryInterface(aUploadStream)) {
   1050    uint32_t count = multiplex->GetCount();
   1051    nsTArray<nsCOMPtr<nsIInputStream>> streams(count);
   1052    nsTArray<RefPtr<GenericPromise>> promises(count);
   1053    bool replace = false;
   1054    for (uint32_t i = 0; i < count; ++i) {
   1055      nsCOMPtr<nsIInputStream> inner;
   1056      nsresult rv = multiplex->GetStream(i, getter_AddRefs(inner));
   1057      NS_ENSURE_SUCCESS(rv, rv);
   1058 
   1059      RefPtr<GenericPromise> promise;
   1060      nsCOMPtr<nsIInputStream> replacement;
   1061      rv = NormalizeUploadStream(inner, getter_AddRefs(replacement),
   1062                                 getter_AddRefs(promise));
   1063      NS_ENSURE_SUCCESS(rv, rv);
   1064      if (promise) {
   1065        promises.AppendElement(promise);
   1066      }
   1067      if (replacement) {
   1068        streams.AppendElement(replacement);
   1069        replace = true;
   1070      } else {
   1071        streams.AppendElement(inner);
   1072      }
   1073    }
   1074 
   1075    // If any of the inner streams needed to be replaced, replace the entire
   1076    // nsIMultiplexInputStream.
   1077    if (replace) {
   1078      nsresult rv;
   1079      nsCOMPtr<nsIMultiplexInputStream> replacement =
   1080          do_CreateInstance("@mozilla.org/io/multiplex-input-stream;1", &rv);
   1081      NS_ENSURE_SUCCESS(rv, rv);
   1082      for (auto& stream : streams) {
   1083        rv = replacement->AppendStream(stream);
   1084        NS_ENSURE_SUCCESS(rv, rv);
   1085      }
   1086 
   1087      MOZ_ALWAYS_SUCCEEDS(CallQueryInterface(replacement, aReplacementStream));
   1088    }
   1089 
   1090    // Wait for all inner promises to settle before resolving the final promise.
   1091    if (!promises.IsEmpty()) {
   1092      RefPtr<GenericPromise> ready =
   1093          GenericPromise::AllSettled(GetCurrentSerialEventTarget(), promises)
   1094              ->Then(GetCurrentSerialEventTarget(), __func__,
   1095                     [](GenericPromise::AllSettledPromiseType::
   1096                            ResolveOrRejectValue&& aResults)
   1097                         -> RefPtr<GenericPromise> {
   1098                       MOZ_ASSERT(aResults.IsResolve(),
   1099                                  "AllSettled never rejects");
   1100                       for (auto& result : aResults.ResolveValue()) {
   1101                         if (result.IsReject()) {
   1102                           return GenericPromise::CreateAndReject(
   1103                               result.RejectValue(), __func__);
   1104                         }
   1105                       }
   1106                       return GenericPromise::CreateAndResolve(true, __func__);
   1107                     });
   1108      ready.forget(aReadyPromise);
   1109    }
   1110    return NS_OK;
   1111  }
   1112 
   1113  // If the stream is cloneable, seekable and non-async, we can allow it.  Async
   1114  // input streams can cause issues, as various consumers of input streams
   1115  // expect the payload to be synchronous and `Available()` to be the length of
   1116  // the stream, which is not true for asynchronous streams.
   1117  nsCOMPtr<nsIAsyncInputStream> async = do_QueryInterface(aUploadStream);
   1118  nsCOMPtr<nsISeekableStream> seekable = do_QueryInterface(aUploadStream);
   1119  if (NS_InputStreamIsCloneable(aUploadStream) && seekable && !async) {
   1120    return NS_OK;
   1121  }
   1122 
   1123  // Asynchronously copy our non-normalized stream into a StorageStream so that
   1124  // it is seekable, cloneable, and synchronous once the copy completes.
   1125 
   1126  NS_WARNING("Upload Stream is being copied into StorageStream");
   1127 
   1128  nsCOMPtr<nsIStorageStream> storageStream;
   1129  nsresult rv =
   1130      NS_NewStorageStream(4096, UINT32_MAX, getter_AddRefs(storageStream));
   1131  NS_ENSURE_SUCCESS(rv, rv);
   1132 
   1133  nsCOMPtr<nsIOutputStream> sink;
   1134  rv = storageStream->GetOutputStream(0, getter_AddRefs(sink));
   1135  NS_ENSURE_SUCCESS(rv, rv);
   1136 
   1137  nsCOMPtr<nsIInputStream> replacementStream;
   1138  rv = storageStream->NewInputStream(0, getter_AddRefs(replacementStream));
   1139  NS_ENSURE_SUCCESS(rv, rv);
   1140 
   1141  // Ensure the source stream is buffered before starting the copy so we can use
   1142  // ReadSegments, as nsStorageStream doesn't implement WriteSegments.
   1143  nsCOMPtr<nsIInputStream> source = aUploadStream;
   1144  if (!NS_InputStreamIsBuffered(aUploadStream)) {
   1145    nsCOMPtr<nsIInputStream> bufferedSource;
   1146    rv = NS_NewBufferedInputStream(getter_AddRefs(bufferedSource),
   1147                                   source.forget(), 4096);
   1148    NS_ENSURE_SUCCESS(rv, rv);
   1149    source = bufferedSource.forget();
   1150  }
   1151 
   1152  // Perform an AsyncCopy into the input stream on the STS.
   1153  nsCOMPtr<nsIEventTarget> target =
   1154      do_GetService(NS_STREAMTRANSPORTSERVICE_CONTRACTID);
   1155  RefPtr<GenericPromise::Private> ready = new GenericPromise::Private(__func__);
   1156  rv = NS_AsyncCopy(source, sink, target, NS_ASYNCCOPY_VIA_READSEGMENTS, 4096,
   1157                    NormalizeCopyComplete, do_AddRef(ready).take());
   1158  if (NS_WARN_IF(NS_FAILED(rv))) {
   1159    ready.get()->Release();
   1160    return rv;
   1161  }
   1162 
   1163  replacementStream.forget(aReplacementStream);
   1164  ready.forget(aReadyPromise);
   1165  return NS_OK;
   1166 }
   1167 
   1168 }  // anonymous namespace
   1169 
   1170 NS_IMETHODIMP
   1171 HttpBaseChannel::CloneUploadStream(int64_t* aContentLength,
   1172                                   nsIInputStream** aClonedStream) {
   1173  NS_ENSURE_ARG_POINTER(aContentLength);
   1174  NS_ENSURE_ARG_POINTER(aClonedStream);
   1175  *aClonedStream = nullptr;
   1176 
   1177  if (!XRE_IsParentProcess()) {
   1178    NS_WARNING("CloneUploadStream is only supported in the parent process");
   1179    return NS_ERROR_NOT_AVAILABLE;
   1180  }
   1181 
   1182  if (!mUploadStream) {
   1183    return NS_OK;
   1184  }
   1185 
   1186  nsCOMPtr<nsIInputStream> clonedStream;
   1187  nsresult rv =
   1188      NS_CloneInputStream(mUploadStream, getter_AddRefs(clonedStream));
   1189  NS_ENSURE_SUCCESS(rv, rv);
   1190 
   1191  clonedStream.forget(aClonedStream);
   1192 
   1193  *aContentLength = mReqContentLength;
   1194  return NS_OK;
   1195 }
   1196 
   1197 //-----------------------------------------------------------------------------
   1198 // HttpBaseChannel::nsIUploadChannel2
   1199 //-----------------------------------------------------------------------------
   1200 
   1201 NS_IMETHODIMP
   1202 HttpBaseChannel::ExplicitSetUploadStream(nsIInputStream* aStream,
   1203                                         const nsACString& aContentType,
   1204                                         int64_t aContentLength,
   1205                                         const nsACString& aMethod,
   1206                                         bool aStreamHasHeaders) {
   1207  // Ensure stream is set and method is valid
   1208  NS_ENSURE_TRUE(aStream, NS_ERROR_FAILURE);
   1209 
   1210  {
   1211    DebugOnly<nsCOMPtr<nsIMIMEInputStream>> mimeStream;
   1212    MOZ_ASSERT(
   1213        !aStreamHasHeaders || NS_FAILED(CallQueryInterface(
   1214                                  aStream, getter_AddRefs(mimeStream.value))),
   1215        "nsIMIMEInputStream should not include headers");
   1216  }
   1217 
   1218  nsresult rv = SetRequestMethod(aMethod);
   1219  NS_ENSURE_SUCCESS(rv, rv);
   1220 
   1221  if (!aStreamHasHeaders && !aContentType.IsVoid()) {
   1222    if (aContentType.IsEmpty()) {
   1223      SetEmptyRequestHeader("Content-Type"_ns);
   1224    } else {
   1225      SetRequestHeader("Content-Type"_ns, aContentType, false);
   1226    }
   1227  }
   1228 
   1229  StoreUploadStreamHasHeaders(aStreamHasHeaders);
   1230 
   1231  return InternalSetUploadStream(aStream, aContentLength, !aStreamHasHeaders);
   1232 }
   1233 
   1234 nsresult HttpBaseChannel::InternalSetUploadStream(
   1235    nsIInputStream* aUploadStream, int64_t aContentLength,
   1236    bool aSetContentLengthHeader) {
   1237  // If we're not on the main thread, such as for TRR, the content length must
   1238  // be provided, as we can't normalize our upload stream.
   1239  if (!NS_IsMainThread()) {
   1240    if (aContentLength < 0) {
   1241      MOZ_ASSERT_UNREACHABLE(
   1242          "Upload content length must be explicit off-main-thread");
   1243      return NS_ERROR_INVALID_ARG;
   1244    }
   1245 
   1246    nsCOMPtr<nsISeekableStream> seekable = do_QueryInterface(aUploadStream);
   1247    if (!NS_InputStreamIsCloneable(aUploadStream) || !seekable) {
   1248      MOZ_ASSERT_UNREACHABLE(
   1249          "Upload stream must be cloneable & seekable off-main-thread");
   1250      return NS_ERROR_INVALID_ARG;
   1251    }
   1252 
   1253    mUploadStream = aUploadStream;
   1254    ExplicitSetUploadStreamLength(aContentLength, aSetContentLengthHeader);
   1255    return NS_OK;
   1256  }
   1257 
   1258  // Normalize the upload stream we're provided to ensure that it is cloneable,
   1259  // seekable, and synchronous when in the parent process.
   1260  //
   1261  // This might be an async operation, in which case ready will be returned and
   1262  // resolved when the operation is complete.
   1263  nsCOMPtr<nsIInputStream> replacement;
   1264  RefPtr<GenericPromise> ready;
   1265  if (XRE_IsParentProcess()) {
   1266    nsresult rv = NormalizeUploadStream(
   1267        aUploadStream, getter_AddRefs(replacement), getter_AddRefs(ready));
   1268    NS_ENSURE_SUCCESS(rv, rv);
   1269  }
   1270 
   1271  mUploadStream = replacement ? replacement.get() : aUploadStream;
   1272 
   1273  // Once the upload stream is ready, fetch its length before proceeding with
   1274  // AsyncOpen.
   1275  auto onReady = [self = RefPtr{this}, aContentLength, aSetContentLengthHeader,
   1276                  stream = mUploadStream]() {
   1277    auto setLengthAndResume = [self, aSetContentLengthHeader](int64_t aLength) {
   1278      self->StorePendingUploadStreamNormalization(false);
   1279      self->ExplicitSetUploadStreamLength(aLength >= 0 ? aLength : 0,
   1280                                          aSetContentLengthHeader);
   1281      self->MaybeResumeAsyncOpen();
   1282    };
   1283 
   1284    if (aContentLength >= 0) {
   1285      setLengthAndResume(aContentLength);
   1286      return;
   1287    }
   1288 
   1289    int64_t length;
   1290    if (InputStreamLengthHelper::GetSyncLength(stream, &length)) {
   1291      setLengthAndResume(length);
   1292      return;
   1293    }
   1294 
   1295    InputStreamLengthHelper::GetAsyncLength(stream, setLengthAndResume);
   1296  };
   1297  StorePendingUploadStreamNormalization(true);
   1298 
   1299  // Resolve onReady synchronously unless a promise is returned.
   1300  if (ready) {
   1301    ready->Then(GetCurrentSerialEventTarget(), __func__,
   1302                [onReady = std::move(onReady)](
   1303                    GenericPromise::ResolveOrRejectValue&&) { onReady(); });
   1304  } else {
   1305    onReady();
   1306  }
   1307  return NS_OK;
   1308 }
   1309 
   1310 void HttpBaseChannel::ExplicitSetUploadStreamLength(
   1311    uint64_t aContentLength, bool aSetContentLengthHeader) {
   1312  // We already have the content length. We don't need to determinate it.
   1313  mReqContentLength = aContentLength;
   1314 
   1315  if (!aSetContentLengthHeader) {
   1316    return;
   1317  }
   1318 
   1319  nsAutoCString header;
   1320  header.AssignLiteral("Content-Length");
   1321 
   1322  // Maybe the content-length header has been already set.
   1323  nsAutoCString value;
   1324  nsresult rv = GetRequestHeader(header, value);
   1325  if (NS_SUCCEEDED(rv) && !value.IsEmpty()) {
   1326    return;
   1327  }
   1328 
   1329  nsAutoCString contentLengthStr;
   1330  contentLengthStr.AppendInt(aContentLength);
   1331  SetRequestHeader(header, contentLengthStr, false);
   1332 }
   1333 
   1334 NS_IMETHODIMP
   1335 HttpBaseChannel::GetUploadStreamHasHeaders(bool* hasHeaders) {
   1336  NS_ENSURE_ARG(hasHeaders);
   1337 
   1338  *hasHeaders = LoadUploadStreamHasHeaders();
   1339  return NS_OK;
   1340 }
   1341 
   1342 bool HttpBaseChannel::MaybeWaitForUploadStreamNormalization(
   1343    nsIStreamListener* aListener, nsISupports* aContext) {
   1344  MOZ_ASSERT(NS_IsMainThread());
   1345  MOZ_ASSERT(!LoadAsyncOpenWaitingForStreamNormalization(),
   1346             "AsyncOpen() called twice?");
   1347 
   1348  if (!LoadPendingUploadStreamNormalization()) {
   1349    return false;
   1350  }
   1351 
   1352  mListener = aListener;
   1353  StoreAsyncOpenWaitingForStreamNormalization(true);
   1354  return true;
   1355 }
   1356 
   1357 void HttpBaseChannel::MaybeResumeAsyncOpen() {
   1358  MOZ_ASSERT(NS_IsMainThread());
   1359  MOZ_ASSERT(!LoadPendingUploadStreamNormalization());
   1360 
   1361  if (!LoadAsyncOpenWaitingForStreamNormalization()) {
   1362    return;
   1363  }
   1364 
   1365  nsCOMPtr<nsIStreamListener> listener;
   1366  listener.swap(mListener);
   1367 
   1368  StoreAsyncOpenWaitingForStreamNormalization(false);
   1369 
   1370  nsresult rv = AsyncOpen(listener);
   1371  if (NS_WARN_IF(NS_FAILED(rv))) {
   1372    DoAsyncAbort(rv);
   1373  }
   1374 }
   1375 
   1376 //-----------------------------------------------------------------------------
   1377 // HttpBaseChannel::nsIEncodedChannel
   1378 //-----------------------------------------------------------------------------
   1379 
   1380 NS_IMETHODIMP
   1381 HttpBaseChannel::GetApplyConversion(bool* value) {
   1382  *value = LoadApplyConversion();
   1383  return NS_OK;
   1384 }
   1385 
   1386 NS_IMETHODIMP
   1387 HttpBaseChannel::SetApplyConversion(bool value) {
   1388  LOG(("HttpBaseChannel::SetApplyConversion [this=%p value=%d]\n", this,
   1389       value));
   1390  StoreApplyConversion(value);
   1391  return NS_OK;
   1392 }
   1393 
   1394 nsresult HttpBaseChannel::DoApplyContentConversions(
   1395    nsIStreamListener* aNextListener, nsIStreamListener** aNewNextListener) {
   1396  return DoApplyContentConversionsInternal(aNextListener, aNewNextListener,
   1397                                           false, nullptr);
   1398 }
   1399 
   1400 // create a listener chain that looks like this
   1401 // http-channel -> decompressor (n times) -> InterceptFailedOnSTop ->
   1402 // channel-creator-listener
   1403 //
   1404 // we need to do this because not every decompressor has fully streamed output
   1405 // so may need a call to OnStopRequest to identify its completion state.. and if
   1406 // it creates an error there the channel status code needs to be updated before
   1407 // calling the terminal listener. Having the decompress do it via cancel() means
   1408 // channels cannot effectively be used in two contexts (specifically this one
   1409 // and a peek context for sniffing)
   1410 //
   1411 class InterceptFailedOnStop : public nsIThreadRetargetableStreamListener {
   1412  virtual ~InterceptFailedOnStop() = default;
   1413  nsCOMPtr<nsIStreamListener> mNext;
   1414  HttpBaseChannel* mChannel;
   1415 
   1416 public:
   1417  InterceptFailedOnStop(nsIStreamListener* arg, HttpBaseChannel* chan)
   1418      : mNext(arg), mChannel(chan) {}
   1419  NS_DECL_THREADSAFE_ISUPPORTS
   1420  NS_DECL_NSITHREADRETARGETABLESTREAMLISTENER
   1421 
   1422  NS_IMETHOD OnStartRequest(nsIRequest* aRequest) override {
   1423    return mNext->OnStartRequest(aRequest);
   1424  }
   1425 
   1426  NS_IMETHOD OnStopRequest(nsIRequest* aRequest,
   1427                           nsresult aStatusCode) override {
   1428    if (NS_FAILED(aStatusCode) && NS_SUCCEEDED(mChannel->mStatus)) {
   1429      LOG(("HttpBaseChannel::InterceptFailedOnStop %p seting status %" PRIx32,
   1430           mChannel, static_cast<uint32_t>(aStatusCode)));
   1431      mChannel->mStatus = aStatusCode;
   1432    }
   1433    return mNext->OnStopRequest(aRequest, aStatusCode);
   1434  }
   1435 
   1436  NS_IMETHOD OnDataAvailable(nsIRequest* aRequest, nsIInputStream* aInputStream,
   1437                             uint64_t aOffset, uint32_t aCount) override {
   1438    return mNext->OnDataAvailable(aRequest, aInputStream, aOffset, aCount);
   1439  }
   1440 };
   1441 
   1442 NS_IMPL_ADDREF(InterceptFailedOnStop)
   1443 NS_IMPL_RELEASE(InterceptFailedOnStop)
   1444 
   1445 NS_INTERFACE_MAP_BEGIN(InterceptFailedOnStop)
   1446  NS_INTERFACE_MAP_ENTRY(nsIStreamListener)
   1447  NS_INTERFACE_MAP_ENTRY(nsIRequestObserver)
   1448  NS_INTERFACE_MAP_ENTRY(nsIThreadRetargetableStreamListener)
   1449  NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIRequestObserver)
   1450 NS_INTERFACE_MAP_END
   1451 
   1452 NS_IMETHODIMP
   1453 InterceptFailedOnStop::CheckListenerChain() {
   1454  nsCOMPtr<nsIThreadRetargetableStreamListener> listener =
   1455      do_QueryInterface(mNext);
   1456  if (!listener) {
   1457    return NS_ERROR_NO_INTERFACE;
   1458  }
   1459 
   1460  return listener->CheckListenerChain();
   1461 }
   1462 
   1463 NS_IMETHODIMP
   1464 InterceptFailedOnStop::OnDataFinished(nsresult aStatus) {
   1465  nsCOMPtr<nsIThreadRetargetableStreamListener> listener =
   1466      do_QueryInterface(mNext);
   1467  if (listener) {
   1468    return listener->OnDataFinished(aStatus);
   1469  }
   1470 
   1471  return NS_OK;
   1472 }
   1473 
   1474 NS_IMETHODIMP
   1475 HttpBaseChannel::DoApplyContentConversions(nsIStreamListener* aNextListener,
   1476                                           nsIStreamListener** aNewNextListener,
   1477                                           nsISupports* aCtxt) {
   1478  return DoApplyContentConversionsInternal(aNextListener, aNewNextListener,
   1479                                           false, aCtxt);
   1480 }
   1481 
   1482 nsresult HttpBaseChannel::DoApplyContentConversionsInternal(
   1483    nsIStreamListener* aNextListener, nsIStreamListener** aNewNextListener,
   1484    bool aRemoveEncodings, nsISupports* aCtxt) {
   1485  *aNewNextListener = nullptr;
   1486  if (!mResponseHead || !aNextListener) {
   1487    return NS_OK;
   1488  }
   1489 
   1490  LOG(
   1491      ("HttpBaseChannel::DoApplyContentConversions [this=%p], "
   1492       "removeEncodings=%d\n",
   1493       this, aRemoveEncodings));
   1494 
   1495 #ifdef DEBUG
   1496  {
   1497    nsAutoCString contentEncoding;
   1498    nsresult rv =
   1499        mResponseHead->GetHeader(nsHttp::Content_Encoding, contentEncoding);
   1500    if (NS_SUCCEEDED(rv) && !contentEncoding.IsEmpty()) {
   1501      nsAutoCString newEncoding;
   1502      char* cePtr = contentEncoding.BeginWriting();
   1503      while (char* val = nsCRT::strtok(cePtr, HTTP_LWS ",", &cePtr)) {
   1504        if (strcmp(val, "dcb") == 0 || strcmp(val, "dcz") == 0) {
   1505          MOZ_ASSERT(LoadApplyConversion() && !LoadHasAppliedConversion());
   1506        }
   1507      }
   1508    }
   1509  }
   1510 #endif
   1511 
   1512  if (!LoadApplyConversion()) {
   1513    LOG(("not applying conversion per ApplyConversion\n"));
   1514    return NS_OK;
   1515  }
   1516 
   1517  if (LoadHasAppliedConversion()) {
   1518    LOG(("not applying conversion because HasAppliedConversion is true\n"));
   1519    return NS_OK;
   1520  }
   1521 
   1522  if (LoadDeliveringAltData()) {
   1523    MOZ_ASSERT(!mAvailableCachedAltDataType.IsEmpty());
   1524    LOG(("not applying conversion because delivering alt-data\n"));
   1525    return NS_OK;
   1526  }
   1527 
   1528  nsAutoCString contentEncoding;
   1529  nsresult rv =
   1530      mResponseHead->GetHeader(nsHttp::Content_Encoding, contentEncoding);
   1531  if (NS_FAILED(rv) || contentEncoding.IsEmpty()) return NS_OK;
   1532 
   1533  nsCOMPtr<nsIStreamListener> nextListener =
   1534      new InterceptFailedOnStop(aNextListener, this);
   1535 
   1536  // The encodings are listed in the order they were applied
   1537  // (see rfc 2616 section 14.11), so they need to removed in reverse
   1538  // order. This is accomplished because the converter chain ends up
   1539  // being a stack with the last converter created being the first one
   1540  // to accept the raw network data.
   1541 
   1542  char* cePtr = contentEncoding.BeginWriting();
   1543  uint32_t count = 0;
   1544  while (char* val = nsCRT::strtok(cePtr, HTTP_LWS ",", &cePtr)) {
   1545    if (++count > 16) {
   1546      // For compatibility with old code, we will just carry on without
   1547      // removing the encodings
   1548      LOG(("Too many Content-Encodings. Ignoring remainder.\n"));
   1549      break;
   1550    }
   1551 
   1552    if (gHttpHandler->IsAcceptableEncoding(val,
   1553                                           isSecureOrTrustworthyURL(mURI))) {
   1554      RefPtr<nsHTTPCompressConv> converter = new nsHTTPCompressConv();
   1555      nsAutoCString from(val);
   1556      ToLowerCase(from);
   1557      rv = converter->AsyncConvertData(from.get(), "uncompressed", nextListener,
   1558                                       aCtxt);
   1559      if (NS_FAILED(rv)) {
   1560        LOG(("Unexpected failure of AsyncConvertData %s\n", val));
   1561        return rv;
   1562      }
   1563 
   1564      LOG(("Adding converter for content-encoding '%s'", val));
   1565      if (Telemetry::CanRecordPrereleaseData()) {
   1566        int mode = 0;
   1567        if (from.EqualsLiteral("gzip") || from.EqualsLiteral("x-gzip")) {
   1568          mode = 1;
   1569        } else if (from.EqualsLiteral("deflate") ||
   1570                   from.EqualsLiteral("x-deflate")) {
   1571          mode = 2;
   1572        } else if (from.EqualsLiteral("br")) {
   1573          mode = 3;
   1574        } else if (from.EqualsLiteral("zstd")) {
   1575          mode = 4;
   1576        } else if (from.EqualsLiteral("dcb")) {
   1577          mode = 5;
   1578        } else if (from.EqualsLiteral("dcz")) {
   1579          mode = 6;
   1580        }
   1581        glean::http::content_encoding.AccumulateSingleSample(mode);
   1582      }
   1583 #ifdef MOZ_DIAGNOSTIC_ASSERT_ENABLED
   1584      if (from.EqualsLiteral("dcb") || from.EqualsLiteral("dcz")) {
   1585        MOZ_DIAGNOSTIC_ASSERT(XRE_IsParentProcess());
   1586      }
   1587 #endif
   1588      nextListener = converter;
   1589    } else {
   1590      if (val) {
   1591        LOG(("Unknown content encoding '%s'\n", val));
   1592      }
   1593      // we *should* return NS_ERROR_UNEXPECTED here, but that will break sites
   1594      // that use things like content-encoding: x-gzip, x-gzip (or any other
   1595      // weird encoding)
   1596    }
   1597  }
   1598 
   1599  // dcb and dcz encodings are removed when it's decompressed (always in
   1600  // the parent process).  However, in theory you could have
   1601  // Content-Encoding: dcb,gzip
   1602  // in which case we could pass it down to the content process as
   1603  // Content-Encoding: gzip.   We won't do that; we'll remove all compressors
   1604  // if we need to remove any.
   1605  // This double compression of course is silly, but supported by the spec.
   1606  if (aRemoveEncodings) {
   1607    // If we have dcb or dcz, all content-encodings in the header should be
   1608    // removed as we're decompressing before the tee in the parent
   1609    // process. Also we should remove any encodings if it's a dictionary
   1610    // so we can load it quickly
   1611    LOG(("Changing Content-Encoding from '%s' to ''", contentEncoding.get()));
   1612    // Can't use SetHeader; we need to overwrite the current value
   1613    rv = mResponseHead->SetHeaderOverride(nsHttp::Content_Encoding, ""_ns);
   1614  }
   1615  *aNewNextListener = do_AddRef(nextListener).take();
   1616  return NS_OK;
   1617 }
   1618 
   1619 NS_IMETHODIMP
   1620 HttpBaseChannel::GetContentEncodings(nsIUTF8StringEnumerator** aEncodings) {
   1621  if (!mResponseHead) {
   1622    *aEncodings = nullptr;
   1623    return NS_OK;
   1624  }
   1625 
   1626  nsAutoCString encoding;
   1627  (void)mResponseHead->GetHeader(nsHttp::Content_Encoding, encoding);
   1628  if (encoding.IsEmpty()) {
   1629    *aEncodings = nullptr;
   1630    return NS_OK;
   1631  }
   1632  RefPtr<nsContentEncodings> enumerator =
   1633      new nsContentEncodings(this, encoding.get());
   1634  enumerator.forget(aEncodings);
   1635  return NS_OK;
   1636 }
   1637 
   1638 //-----------------------------------------------------------------------------
   1639 // HttpBaseChannel::nsContentEncodings <public>
   1640 //-----------------------------------------------------------------------------
   1641 
   1642 HttpBaseChannel::nsContentEncodings::nsContentEncodings(
   1643    nsIHttpChannel* aChannel, const char* aEncodingHeader)
   1644    : mEncodingHeader(aEncodingHeader), mChannel(aChannel), mReady(false) {
   1645  mCurEnd = aEncodingHeader + strlen(aEncodingHeader);
   1646  mCurStart = mCurEnd;
   1647 }
   1648 
   1649 //-----------------------------------------------------------------------------
   1650 // HttpBaseChannel::nsContentEncodings::nsISimpleEnumerator
   1651 //-----------------------------------------------------------------------------
   1652 
   1653 NS_IMETHODIMP
   1654 HttpBaseChannel::nsContentEncodings::HasMore(bool* aMoreEncodings) {
   1655  if (mReady) {
   1656    *aMoreEncodings = true;
   1657    return NS_OK;
   1658  }
   1659 
   1660  nsresult rv = PrepareForNext();
   1661  *aMoreEncodings = NS_SUCCEEDED(rv);
   1662  return NS_OK;
   1663 }
   1664 
   1665 NS_IMETHODIMP
   1666 HttpBaseChannel::nsContentEncodings::GetNext(nsACString& aNextEncoding) {
   1667  aNextEncoding.Truncate();
   1668  if (!mReady) {
   1669    nsresult rv = PrepareForNext();
   1670    if (NS_FAILED(rv)) {
   1671      return NS_ERROR_FAILURE;
   1672    }
   1673  }
   1674 
   1675  const nsACString& encoding = Substring(mCurStart, mCurEnd);
   1676 
   1677  nsACString::const_iterator start, end;
   1678  encoding.BeginReading(start);
   1679  encoding.EndReading(end);
   1680 
   1681  bool haveType = false;
   1682  if (CaseInsensitiveFindInReadable("gzip"_ns, start, end)) {
   1683    aNextEncoding.AssignLiteral(APPLICATION_GZIP);
   1684    haveType = true;
   1685  }
   1686 
   1687  if (!haveType) {
   1688    encoding.BeginReading(start);
   1689    if (CaseInsensitiveFindInReadable("compress"_ns, start, end)) {
   1690      aNextEncoding.AssignLiteral(APPLICATION_COMPRESS);
   1691      haveType = true;
   1692    }
   1693  }
   1694 
   1695  if (!haveType) {
   1696    encoding.BeginReading(start);
   1697    if (CaseInsensitiveFindInReadable("deflate"_ns, start, end)) {
   1698      aNextEncoding.AssignLiteral(APPLICATION_ZIP);
   1699      haveType = true;
   1700    }
   1701  }
   1702 
   1703  if (!haveType) {
   1704    encoding.BeginReading(start);
   1705    if (CaseInsensitiveFindInReadable("br"_ns, start, end)) {
   1706      aNextEncoding.AssignLiteral(APPLICATION_BROTLI);
   1707      haveType = true;
   1708    }
   1709  }
   1710 
   1711  if (!haveType) {
   1712    encoding.BeginReading(start);
   1713    if (CaseInsensitiveFindInReadable("zstd"_ns, start, end)) {
   1714      aNextEncoding.AssignLiteral(APPLICATION_ZSTD);
   1715      haveType = true;
   1716    }
   1717  }
   1718 
   1719  // Prepare to fetch the next encoding
   1720  mCurEnd = mCurStart;
   1721  mReady = false;
   1722 
   1723  if (haveType) return NS_OK;
   1724 
   1725  NS_WARNING("Unknown encoding type");
   1726  return NS_ERROR_FAILURE;
   1727 }
   1728 
   1729 //-----------------------------------------------------------------------------
   1730 // HttpBaseChannel::nsContentEncodings::nsISupports
   1731 //-----------------------------------------------------------------------------
   1732 
   1733 NS_IMPL_ISUPPORTS(HttpBaseChannel::nsContentEncodings, nsIUTF8StringEnumerator,
   1734                  nsIStringEnumerator)
   1735 
   1736 //-----------------------------------------------------------------------------
   1737 // HttpBaseChannel::nsContentEncodings <private>
   1738 //-----------------------------------------------------------------------------
   1739 
   1740 nsresult HttpBaseChannel::nsContentEncodings::PrepareForNext(void) {
   1741  MOZ_ASSERT(mCurStart == mCurEnd, "Indeterminate state");
   1742 
   1743  // At this point both mCurStart and mCurEnd point to somewhere
   1744  // past the end of the next thing we want to return
   1745 
   1746  while (mCurEnd != mEncodingHeader) {
   1747    --mCurEnd;
   1748    if (*mCurEnd != ',' && !nsCRT::IsAsciiSpace(*mCurEnd)) break;
   1749  }
   1750  if (mCurEnd == mEncodingHeader) {
   1751    return NS_ERROR_NOT_AVAILABLE;  // no more encodings
   1752  }
   1753  ++mCurEnd;
   1754 
   1755  // At this point mCurEnd points to the first char _after_ the
   1756  // header we want.  Furthermore, mCurEnd - 1 != mEncodingHeader
   1757 
   1758  mCurStart = mCurEnd - 1;
   1759  while (mCurStart != mEncodingHeader && *mCurStart != ',' &&
   1760         !nsCRT::IsAsciiSpace(*mCurStart)) {
   1761    --mCurStart;
   1762  }
   1763  if (*mCurStart == ',' || nsCRT::IsAsciiSpace(*mCurStart)) {
   1764    ++mCurStart;  // we stopped because of a weird char, so move up one
   1765  }
   1766 
   1767  // At this point mCurStart and mCurEnd bracket the encoding string
   1768  // we want.  Check that it's not "identity"
   1769  if (Substring(mCurStart, mCurEnd)
   1770          .Equals("identity", nsCaseInsensitiveCStringComparator)) {
   1771    mCurEnd = mCurStart;
   1772    return PrepareForNext();
   1773  }
   1774 
   1775  mReady = true;
   1776  return NS_OK;
   1777 }
   1778 
   1779 //-----------------------------------------------------------------------------
   1780 // HttpBaseChannel::nsIHttpChannel
   1781 //-----------------------------------------------------------------------------
   1782 
   1783 NS_IMETHODIMP
   1784 HttpBaseChannel::GetChannelId(uint64_t* aChannelId) {
   1785  NS_ENSURE_ARG_POINTER(aChannelId);
   1786  *aChannelId = mChannelId;
   1787  return NS_OK;
   1788 }
   1789 
   1790 NS_IMETHODIMP
   1791 HttpBaseChannel::SetChannelId(uint64_t aChannelId) {
   1792  mChannelId = aChannelId;
   1793  return NS_OK;
   1794 }
   1795 
   1796 NS_IMETHODIMP HttpBaseChannel::GetTopLevelContentWindowId(uint64_t* aWindowId) {
   1797  if (!mContentWindowId) {
   1798    nsCOMPtr<nsILoadContext> loadContext;
   1799    GetCallback(loadContext);
   1800    if (loadContext) {
   1801      nsCOMPtr<mozIDOMWindowProxy> topWindow;
   1802      loadContext->GetTopWindow(getter_AddRefs(topWindow));
   1803      if (topWindow) {
   1804        if (nsPIDOMWindowInner* inner =
   1805                nsPIDOMWindowOuter::From(topWindow)->GetCurrentInnerWindow()) {
   1806          mContentWindowId = inner->WindowID();
   1807        }
   1808      }
   1809    }
   1810  }
   1811  *aWindowId = mContentWindowId;
   1812  return NS_OK;
   1813 }
   1814 
   1815 NS_IMETHODIMP HttpBaseChannel::SetBrowserId(uint64_t aId) {
   1816  mBrowserId = aId;
   1817  return NS_OK;
   1818 }
   1819 
   1820 NS_IMETHODIMP HttpBaseChannel::GetBrowserId(uint64_t* aId) {
   1821  EnsureBrowserId();
   1822  *aId = mBrowserId;
   1823  return NS_OK;
   1824 }
   1825 
   1826 NS_IMETHODIMP HttpBaseChannel::SetTopLevelContentWindowId(uint64_t aWindowId) {
   1827  mContentWindowId = aWindowId;
   1828  return NS_OK;
   1829 }
   1830 
   1831 NS_IMETHODIMP
   1832 HttpBaseChannel::IsThirdPartyTrackingResource(bool* aIsTrackingResource) {
   1833  MOZ_ASSERT(
   1834      !(mFirstPartyClassificationFlags && mThirdPartyClassificationFlags));
   1835  *aIsTrackingResource = UrlClassifierCommon::IsTrackingClassificationFlag(
   1836      mThirdPartyClassificationFlags,
   1837      mLoadInfo->GetOriginAttributes().IsPrivateBrowsing());
   1838  return NS_OK;
   1839 }
   1840 
   1841 NS_IMETHODIMP
   1842 HttpBaseChannel::IsThirdPartySocialTrackingResource(
   1843    bool* aIsThirdPartySocialTrackingResource) {
   1844  MOZ_ASSERT(!mFirstPartyClassificationFlags ||
   1845             !mThirdPartyClassificationFlags);
   1846  *aIsThirdPartySocialTrackingResource =
   1847      UrlClassifierCommon::IsSocialTrackingClassificationFlag(
   1848          mThirdPartyClassificationFlags);
   1849  return NS_OK;
   1850 }
   1851 
   1852 NS_IMETHODIMP
   1853 HttpBaseChannel::GetClassificationFlags(uint32_t* aFlags) {
   1854  if (mThirdPartyClassificationFlags) {
   1855    *aFlags = mThirdPartyClassificationFlags;
   1856  } else {
   1857    *aFlags = mFirstPartyClassificationFlags;
   1858  }
   1859  return NS_OK;
   1860 }
   1861 
   1862 NS_IMETHODIMP
   1863 HttpBaseChannel::GetFirstPartyClassificationFlags(uint32_t* aFlags) {
   1864  *aFlags = mFirstPartyClassificationFlags;
   1865  return NS_OK;
   1866 }
   1867 
   1868 NS_IMETHODIMP
   1869 HttpBaseChannel::GetThirdPartyClassificationFlags(uint32_t* aFlags) {
   1870  *aFlags = mThirdPartyClassificationFlags;
   1871  return NS_OK;
   1872 }
   1873 
   1874 NS_IMETHODIMP
   1875 HttpBaseChannel::GetTransferSize(uint64_t* aTransferSize) {
   1876  MutexAutoLock lock(mOnDataFinishedMutex);
   1877  *aTransferSize = mTransferSize;
   1878  return NS_OK;
   1879 }
   1880 
   1881 NS_IMETHODIMP
   1882 HttpBaseChannel::GetRequestSize(uint64_t* aRequestSize) {
   1883  *aRequestSize = mRequestSize;
   1884  return NS_OK;
   1885 }
   1886 
   1887 NS_IMETHODIMP
   1888 HttpBaseChannel::GetDecodedBodySize(uint64_t* aDecodedBodySize) {
   1889  MutexAutoLock lock(mOnDataFinishedMutex);
   1890  *aDecodedBodySize = mDecodedBodySize;
   1891  return NS_OK;
   1892 }
   1893 
   1894 NS_IMETHODIMP
   1895 HttpBaseChannel::GetEncodedBodySize(uint64_t* aEncodedBodySize) {
   1896  MutexAutoLock lock(mOnDataFinishedMutex);
   1897  *aEncodedBodySize = mEncodedBodySize;
   1898  return NS_OK;
   1899 }
   1900 
   1901 NS_IMETHODIMP
   1902 HttpBaseChannel::GetSupportsHTTP3(bool* aSupportsHTTP3) {
   1903  *aSupportsHTTP3 = mSupportsHTTP3;
   1904  return NS_OK;
   1905 }
   1906 
   1907 NS_IMETHODIMP
   1908 HttpBaseChannel::GetHasHTTPSRR(bool* aHasHTTPSRR) {
   1909  *aHasHTTPSRR = LoadHasHTTPSRR();
   1910  return NS_OK;
   1911 }
   1912 
   1913 NS_IMETHODIMP
   1914 HttpBaseChannel::GetRequestMethod(nsACString& aMethod) {
   1915  mRequestHead.Method(aMethod);
   1916  return NS_OK;
   1917 }
   1918 
   1919 NS_IMETHODIMP
   1920 HttpBaseChannel::SetRequestMethod(const nsACString& aMethod) {
   1921  ENSURE_CALLED_BEFORE_CONNECT();
   1922 
   1923  mLoadInfo->SetIsGETRequest(aMethod.Equals("GET"));
   1924 
   1925  const nsCString& flatMethod = PromiseFlatCString(aMethod);
   1926 
   1927  // Method names are restricted to valid HTTP tokens.
   1928  if (!nsHttp::IsValidToken(flatMethod)) return NS_ERROR_INVALID_ARG;
   1929 
   1930  mRequestHead.SetMethod(flatMethod);
   1931  return NS_OK;
   1932 }
   1933 
   1934 NS_IMETHODIMP
   1935 HttpBaseChannel::GetReferrerInfo(nsIReferrerInfo** aReferrerInfo) {
   1936  NS_ENSURE_ARG_POINTER(aReferrerInfo);
   1937  *aReferrerInfo = do_AddRef(mReferrerInfo).take();
   1938  return NS_OK;
   1939 }
   1940 
   1941 nsresult HttpBaseChannel::SetReferrerInfoInternal(
   1942    nsIReferrerInfo* aReferrerInfo, bool aClone, bool aCompute,
   1943    bool aRespectBeforeConnect) {
   1944  LOG(
   1945      ("HttpBaseChannel::SetReferrerInfoInternal [this=%p aClone(%d) "
   1946       "aCompute(%d)]\n",
   1947       this, aClone, aCompute));
   1948  if (aRespectBeforeConnect) {
   1949    ENSURE_CALLED_BEFORE_CONNECT();
   1950  }
   1951 
   1952  mReferrerInfo = aReferrerInfo;
   1953 
   1954  // clear existing referrer, if any
   1955  nsresult rv = ClearReferrerHeader();
   1956  if (NS_WARN_IF(NS_FAILED(rv))) {
   1957    return rv;
   1958  }
   1959 
   1960  if (!mReferrerInfo) {
   1961    return NS_OK;
   1962  }
   1963 
   1964  if (aClone) {
   1965    mReferrerInfo = static_cast<dom::ReferrerInfo*>(aReferrerInfo)->Clone();
   1966  }
   1967 
   1968  dom::ReferrerInfo* referrerInfo =
   1969      static_cast<dom::ReferrerInfo*>(mReferrerInfo.get());
   1970 
   1971  // Don't set referrerInfo if it has not been initialized.
   1972  if (!referrerInfo->IsInitialized()) {
   1973    mReferrerInfo = nullptr;
   1974    return NS_ERROR_NOT_INITIALIZED;
   1975  }
   1976 
   1977  if (aClone) {
   1978    // Record the telemetry once we set the referrer info to the channel
   1979    // successfully.
   1980    referrerInfo->RecordTelemetry(this);
   1981  }
   1982 
   1983  if (aCompute) {
   1984    rv = referrerInfo->ComputeReferrer(this);
   1985    if (NS_WARN_IF(NS_FAILED(rv))) {
   1986      return rv;
   1987    }
   1988  }
   1989 
   1990  nsCOMPtr<nsIURI> computedReferrer = mReferrerInfo->GetComputedReferrer();
   1991  if (!computedReferrer) {
   1992    return NS_OK;
   1993  }
   1994 
   1995  nsAutoCString spec;
   1996  rv = computedReferrer->GetSpec(spec);
   1997  if (NS_WARN_IF(NS_FAILED(rv))) {
   1998    return rv;
   1999  }
   2000 
   2001  return SetReferrerHeader(spec, aRespectBeforeConnect);
   2002 }
   2003 
   2004 NS_IMETHODIMP
   2005 HttpBaseChannel::SetReferrerInfo(nsIReferrerInfo* aReferrerInfo) {
   2006  return SetReferrerInfoInternal(aReferrerInfo, true, true, true);
   2007 }
   2008 
   2009 NS_IMETHODIMP
   2010 HttpBaseChannel::SetReferrerInfoWithoutClone(nsIReferrerInfo* aReferrerInfo) {
   2011  return SetReferrerInfoInternal(aReferrerInfo, false, true, true);
   2012 }
   2013 
   2014 // Return the channel's proxy URI, or if it doesn't exist, the
   2015 // channel's main URI.
   2016 NS_IMETHODIMP
   2017 HttpBaseChannel::GetProxyURI(nsIURI** aOut) {
   2018  NS_ENSURE_ARG_POINTER(aOut);
   2019  nsCOMPtr<nsIURI> result(mProxyURI);
   2020  result.forget(aOut);
   2021  return NS_OK;
   2022 }
   2023 
   2024 NS_IMETHODIMP
   2025 HttpBaseChannel::GetRequestHeader(const nsACString& aHeader,
   2026                                  nsACString& aValue) {
   2027  aValue.Truncate();
   2028 
   2029  // XXX might be better to search the header list directly instead of
   2030  // hitting the http atom hash table.
   2031  nsHttpAtom atom = nsHttp::ResolveAtom(aHeader);
   2032  if (!atom) return NS_ERROR_NOT_AVAILABLE;
   2033 
   2034  return mRequestHead.GetHeader(atom, aValue);
   2035 }
   2036 
   2037 NS_IMETHODIMP
   2038 HttpBaseChannel::SetRequestHeader(const nsACString& aHeader,
   2039                                  const nsACString& aValue, bool aMerge) {
   2040  return SetRequestHeaderInternal(aHeader, aValue, aMerge,
   2041                                  nsHttpHeaderArray::eVarietyRequestOverride);
   2042 }
   2043 
   2044 nsresult HttpBaseChannel::SetRequestHeaderInternal(
   2045    const nsACString& aHeader, const nsACString& aValue, bool aMerge,
   2046    nsHttpHeaderArray::HeaderVariety aVariety) {
   2047  const nsCString& flatHeader = PromiseFlatCString(aHeader);
   2048  const nsCString& flatValue = PromiseFlatCString(aValue);
   2049 
   2050  LOG(
   2051      ("HttpBaseChannel::SetRequestHeader [this=%p header=\"%s\" value=\"%s\" "
   2052       "merge=%u]\n",
   2053       this, flatHeader.get(), flatValue.get(), aMerge));
   2054 
   2055  // Verify header names are valid HTTP tokens and header values are reasonably
   2056  // close to whats allowed in RFC 2616.
   2057  if (!nsHttp::IsValidToken(flatHeader) ||
   2058      !nsHttp::IsReasonableHeaderValue(flatValue)) {
   2059    return NS_ERROR_INVALID_ARG;
   2060  }
   2061 
   2062  // Mark that the User-Agent header has been modified.
   2063  if (nsHttp::ResolveAtom(aHeader) == nsHttp::User_Agent) {
   2064    StoreIsUserAgentHeaderModified(true);
   2065  }
   2066 
   2067  return mRequestHead.SetHeader(aHeader, flatValue, aMerge);
   2068 }
   2069 
   2070 NS_IMETHODIMP
   2071 HttpBaseChannel::SetNewReferrerInfo(const nsACString& aUrl,
   2072                                    nsIReferrerInfo::ReferrerPolicyIDL aPolicy,
   2073                                    bool aSendReferrer) {
   2074  nsresult rv;
   2075  // Create URI from string
   2076  nsCOMPtr<nsIURI> aURI;
   2077  rv = NS_NewURI(getter_AddRefs(aURI), aUrl);
   2078  NS_ENSURE_SUCCESS(rv, rv);
   2079  // Create new ReferrerInfo and initialize it.
   2080  nsCOMPtr<nsIReferrerInfo> referrerInfo = new mozilla::dom::ReferrerInfo();
   2081  rv = referrerInfo->Init(aPolicy, aSendReferrer, aURI);
   2082  NS_ENSURE_SUCCESS(rv, rv);
   2083  // Set ReferrerInfo
   2084  return SetReferrerInfo(referrerInfo);
   2085 }
   2086 
   2087 NS_IMETHODIMP
   2088 HttpBaseChannel::SetEmptyRequestHeader(const nsACString& aHeader) {
   2089  const nsCString& flatHeader = PromiseFlatCString(aHeader);
   2090 
   2091  LOG(("HttpBaseChannel::SetEmptyRequestHeader [this=%p header=\"%s\"]\n", this,
   2092       flatHeader.get()));
   2093 
   2094  // Verify header names are valid HTTP tokens and header values are reasonably
   2095  // close to whats allowed in RFC 2616.
   2096  if (!nsHttp::IsValidToken(flatHeader)) {
   2097    return NS_ERROR_INVALID_ARG;
   2098  }
   2099 
   2100  // Mark that the User-Agent header has been modified.
   2101  if (nsHttp::ResolveAtom(aHeader) == nsHttp::User_Agent) {
   2102    StoreIsUserAgentHeaderModified(true);
   2103  }
   2104 
   2105  return mRequestHead.SetEmptyHeader(aHeader);
   2106 }
   2107 
   2108 NS_IMETHODIMP
   2109 HttpBaseChannel::VisitRequestHeaders(nsIHttpHeaderVisitor* visitor) {
   2110  return mRequestHead.VisitHeaders(visitor);
   2111 }
   2112 
   2113 NS_IMETHODIMP
   2114 HttpBaseChannel::VisitNonDefaultRequestHeaders(nsIHttpHeaderVisitor* visitor) {
   2115  return mRequestHead.VisitHeaders(visitor,
   2116                                   nsHttpHeaderArray::eFilterSkipDefault);
   2117 }
   2118 
   2119 NS_IMETHODIMP
   2120 HttpBaseChannel::GetResponseHeader(const nsACString& header,
   2121                                   nsACString& value) {
   2122  value.Truncate();
   2123 
   2124  if (!mResponseHead) return NS_ERROR_NOT_AVAILABLE;
   2125 
   2126  nsHttpAtom atom = nsHttp::ResolveAtom(header);
   2127  if (!atom) return NS_ERROR_NOT_AVAILABLE;
   2128 
   2129  return mResponseHead->GetHeader(atom, value);
   2130 }
   2131 
   2132 NS_IMETHODIMP
   2133 HttpBaseChannel::SetResponseHeader(const nsACString& header,
   2134                                   const nsACString& value, bool merge) {
   2135  LOG(
   2136      ("HttpBaseChannel::SetResponseHeader [this=%p header=\"%s\" value=\"%s\" "
   2137       "merge=%u]\n",
   2138       this, PromiseFlatCString(header).get(), PromiseFlatCString(value).get(),
   2139       merge));
   2140 
   2141  if (!mResponseHead) return NS_ERROR_NOT_AVAILABLE;
   2142 
   2143  nsHttpAtom atom = nsHttp::ResolveAtom(header);
   2144  if (!atom) return NS_ERROR_NOT_AVAILABLE;
   2145 
   2146  // these response headers must not be changed
   2147  if (atom == nsHttp::Content_Type || atom == nsHttp::Content_Length ||
   2148      atom == nsHttp::Content_Encoding || atom == nsHttp::Trailer ||
   2149      atom == nsHttp::Transfer_Encoding) {
   2150    return NS_ERROR_ILLEGAL_VALUE;
   2151  }
   2152 
   2153  StoreResponseHeadersModified(true);
   2154 
   2155  return mResponseHead->SetHeader(header, value, merge);
   2156 }
   2157 
   2158 NS_IMETHODIMP
   2159 HttpBaseChannel::VisitResponseHeaders(nsIHttpHeaderVisitor* visitor) {
   2160  if (!mResponseHead) {
   2161    return NS_ERROR_NOT_AVAILABLE;
   2162  }
   2163  return mResponseHead->VisitHeaders(visitor,
   2164                                     nsHttpHeaderArray::eFilterResponse);
   2165 }
   2166 
   2167 NS_IMETHODIMP
   2168 HttpBaseChannel::GetOriginalResponseHeader(const nsACString& aHeader,
   2169                                           nsIHttpHeaderVisitor* aVisitor) {
   2170  if (!mResponseHead) {
   2171    return NS_ERROR_NOT_AVAILABLE;
   2172  }
   2173 
   2174  nsHttpAtom atom = nsHttp::ResolveAtom(aHeader);
   2175  if (!atom) {
   2176    return NS_ERROR_NOT_AVAILABLE;
   2177  }
   2178 
   2179  return mResponseHead->GetOriginalHeader(atom, aVisitor);
   2180 }
   2181 
   2182 NS_IMETHODIMP
   2183 HttpBaseChannel::VisitOriginalResponseHeaders(nsIHttpHeaderVisitor* aVisitor) {
   2184  if (!mResponseHead) {
   2185    return NS_ERROR_NOT_AVAILABLE;
   2186  }
   2187 
   2188  return mResponseHead->VisitHeaders(
   2189      aVisitor, nsHttpHeaderArray::eFilterResponseOriginal);
   2190 }
   2191 
   2192 NS_IMETHODIMP
   2193 HttpBaseChannel::GetAllowSTS(bool* value) {
   2194  NS_ENSURE_ARG_POINTER(value);
   2195  *value = LoadAllowSTS();
   2196  return NS_OK;
   2197 }
   2198 
   2199 NS_IMETHODIMP
   2200 HttpBaseChannel::SetAllowSTS(bool value) {
   2201  ENSURE_CALLED_BEFORE_CONNECT();
   2202  StoreAllowSTS(value);
   2203  return NS_OK;
   2204 }
   2205 
   2206 NS_IMETHODIMP
   2207 HttpBaseChannel::GetIsOCSP(bool* value) {
   2208  NS_ENSURE_ARG_POINTER(value);
   2209  *value = LoadIsOCSP();
   2210  return NS_OK;
   2211 }
   2212 
   2213 NS_IMETHODIMP
   2214 HttpBaseChannel::SetIsOCSP(bool value) {
   2215  ENSURE_CALLED_BEFORE_CONNECT();
   2216  StoreIsOCSP(value);
   2217  return NS_OK;
   2218 }
   2219 
   2220 NS_IMETHODIMP
   2221 HttpBaseChannel::GetIsUserAgentHeaderModified(bool* value) {
   2222  NS_ENSURE_ARG_POINTER(value);
   2223  *value = LoadIsUserAgentHeaderModified();
   2224  return NS_OK;
   2225 }
   2226 
   2227 NS_IMETHODIMP
   2228 HttpBaseChannel::SetIsUserAgentHeaderModified(bool value) {
   2229  StoreIsUserAgentHeaderModified(value);
   2230  return NS_OK;
   2231 }
   2232 
   2233 NS_IMETHODIMP
   2234 HttpBaseChannel::GetRedirectionLimit(uint32_t* value) {
   2235  NS_ENSURE_ARG_POINTER(value);
   2236  *value = mRedirectionLimit;
   2237  return NS_OK;
   2238 }
   2239 
   2240 NS_IMETHODIMP
   2241 HttpBaseChannel::SetRedirectionLimit(uint32_t value) {
   2242  ENSURE_CALLED_BEFORE_CONNECT();
   2243 
   2244  mRedirectionLimit = std::min<uint32_t>(value, 0xff);
   2245  return NS_OK;
   2246 }
   2247 
   2248 nsresult HttpBaseChannel::OverrideSecurityInfo(
   2249    nsITransportSecurityInfo* aSecurityInfo) {
   2250  MOZ_ASSERT(!mSecurityInfo,
   2251             "This can only be called when we don't have a security info "
   2252             "object already");
   2253  MOZ_RELEASE_ASSERT(
   2254      aSecurityInfo,
   2255      "This can only be called with a valid security info object");
   2256  MOZ_ASSERT(!BypassServiceWorker(),
   2257             "This can only be called on channels that are not bypassing "
   2258             "interception");
   2259  MOZ_ASSERT(LoadResponseCouldBeSynthesized(),
   2260             "This can only be called on channels that can be intercepted");
   2261  if (mSecurityInfo) {
   2262    LOG(
   2263        ("HttpBaseChannel::OverrideSecurityInfo mSecurityInfo is null! "
   2264         "[this=%p]\n",
   2265         this));
   2266    return NS_ERROR_UNEXPECTED;
   2267  }
   2268  if (!LoadResponseCouldBeSynthesized()) {
   2269    LOG(
   2270        ("HttpBaseChannel::OverrideSecurityInfo channel cannot be intercepted! "
   2271         "[this=%p]\n",
   2272         this));
   2273    return NS_ERROR_UNEXPECTED;
   2274  }
   2275 
   2276  mSecurityInfo = aSecurityInfo;
   2277  return NS_OK;
   2278 }
   2279 
   2280 NS_IMETHODIMP
   2281 HttpBaseChannel::IsNoStoreResponse(bool* value) {
   2282  if (!mResponseHead) return NS_ERROR_NOT_AVAILABLE;
   2283  *value = mResponseHead->NoStore();
   2284  return NS_OK;
   2285 }
   2286 
   2287 NS_IMETHODIMP
   2288 HttpBaseChannel::IsNoCacheResponse(bool* value) {
   2289  if (!mResponseHead) return NS_ERROR_NOT_AVAILABLE;
   2290  *value = mResponseHead->NoCache();
   2291  if (!*value) *value = mResponseHead->ExpiresInPast();
   2292  return NS_OK;
   2293 }
   2294 
   2295 NS_IMETHODIMP
   2296 HttpBaseChannel::GetResponseStatus(uint32_t* aValue) {
   2297  if (!mResponseHead) return NS_ERROR_NOT_AVAILABLE;
   2298  *aValue = mResponseHead->Status();
   2299  return NS_OK;
   2300 }
   2301 
   2302 NS_IMETHODIMP
   2303 HttpBaseChannel::GetResponseStatusText(nsACString& aValue) {
   2304  if (!mResponseHead) return NS_ERROR_NOT_AVAILABLE;
   2305  nsAutoCString version;
   2306  // https://fetch.spec.whatwg.org :
   2307  // Responses over an HTTP/2 connection will always have the empty byte
   2308  // sequence as status message as HTTP/2 does not support them.
   2309  if (NS_WARN_IF(NS_FAILED(GetProtocolVersion(version))) ||
   2310      !version.EqualsLiteral("h2")) {
   2311    mResponseHead->StatusText(aValue);
   2312  }
   2313  return NS_OK;
   2314 }
   2315 
   2316 NS_IMETHODIMP
   2317 HttpBaseChannel::GetRequestSucceeded(bool* aValue) {
   2318  if (!mResponseHead) return NS_ERROR_NOT_AVAILABLE;
   2319  uint32_t status = mResponseHead->Status();
   2320  *aValue = (status / 100 == 2);
   2321  return NS_OK;
   2322 }
   2323 
   2324 NS_IMETHODIMP
   2325 HttpBaseChannel::RedirectTo(nsIURI* targetURI) {
   2326  NS_ENSURE_ARG(targetURI);
   2327 
   2328  nsAutoCString spec;
   2329  targetURI->GetAsciiSpec(spec);
   2330  LOG(("HttpBaseChannel::RedirectTo [this=%p, uri=%s]", this, spec.get()));
   2331  LogCallingScriptLocation(this);
   2332 
   2333  // We cannot redirect after OnStartRequest of the listener
   2334  // has been called, since to redirect we have to switch channels
   2335  // and the dance with OnStartRequest et al has to start over.
   2336  // This would break the nsIStreamListener contract.
   2337  NS_ENSURE_FALSE(LoadOnStartRequestCalled(), NS_ERROR_NOT_AVAILABLE);
   2338 
   2339  // The first parameter is the URI we would like to redirect to
   2340  // The second parameter should default to false if normal redirect
   2341  mAPIRedirectTo =
   2342      Some(mozilla::MakeCompactPair(nsCOMPtr<nsIURI>(targetURI), false));
   2343 
   2344  // Only Web Extensions are allowed to redirect a channel to a data:
   2345  // URI. To avoid any bypasses after the channel was flagged by
   2346  // the WebRequst API, we are dropping the flag here.
   2347  mLoadInfo->SetAllowInsecureRedirectToDataURI(false);
   2348 
   2349  // We may want to rewrite origin allowance, hence we need an
   2350  // artificial response head.
   2351  if (!mResponseHead) {
   2352    mResponseHead.reset(new nsHttpResponseHead());
   2353  }
   2354  return NS_OK;
   2355 }
   2356 
   2357 NS_IMETHODIMP
   2358 HttpBaseChannel::TransparentRedirectTo(nsIURI* targetURI) {
   2359  LOG(("HttpBaseChannel::TransparentRedirectTo [this=%p]", this));
   2360  RedirectTo(targetURI);
   2361  MOZ_ASSERT(mAPIRedirectTo, "How did this happen?");
   2362  mAPIRedirectTo->second() = true;
   2363  return NS_OK;
   2364 }
   2365 
   2366 NS_IMETHODIMP
   2367 HttpBaseChannel::UpgradeToSecure() {
   2368  // Upgrades are handled internally between http-on-modify-request and
   2369  // http-on-before-connect, which means upgrades are only possible during
   2370  // on-modify, or WebRequest.onBeforeRequest in Web Extensions.  Once we are
   2371  // past the code path where upgrades are handled, attempting an upgrade
   2372  // will throw an error.
   2373  NS_ENSURE_TRUE(LoadUpgradableToSecure(), NS_ERROR_NOT_AVAILABLE);
   2374 
   2375  StoreUpgradeToSecure(true);
   2376  // todo: Currently UpgradeToSecure() is called only by web extensions, if
   2377  // that ever changes, we need to update the following telemetry collection
   2378  // to reflect any future changes.
   2379  mLoadInfo->SetHttpsUpgradeTelemetry(nsILoadInfo::WEB_EXTENSION_UPGRADE);
   2380 
   2381  return NS_OK;
   2382 }
   2383 
   2384 NS_IMETHODIMP
   2385 HttpBaseChannel::GetRequestObserversCalled(bool* aCalled) {
   2386  NS_ENSURE_ARG_POINTER(aCalled);
   2387  *aCalled = LoadRequestObserversCalled();
   2388  return NS_OK;
   2389 }
   2390 
   2391 NS_IMETHODIMP
   2392 HttpBaseChannel::SetRequestObserversCalled(bool aCalled) {
   2393  StoreRequestObserversCalled(aCalled);
   2394  return NS_OK;
   2395 }
   2396 
   2397 NS_IMETHODIMP
   2398 HttpBaseChannel::GetRequestContextID(uint64_t* aRCID) {
   2399  NS_ENSURE_ARG_POINTER(aRCID);
   2400  *aRCID = mRequestContextID;
   2401  return NS_OK;
   2402 }
   2403 
   2404 NS_IMETHODIMP
   2405 HttpBaseChannel::SetRequestContextID(uint64_t aRCID) {
   2406  mRequestContextID = aRCID;
   2407  return NS_OK;
   2408 }
   2409 
   2410 NS_IMETHODIMP
   2411 HttpBaseChannel::GetIsMainDocumentChannel(bool* aValue) {
   2412  NS_ENSURE_ARG_POINTER(aValue);
   2413  *aValue = IsNavigation();
   2414  return NS_OK;
   2415 }
   2416 
   2417 NS_IMETHODIMP
   2418 HttpBaseChannel::SetIsMainDocumentChannel(bool aValue) {
   2419  StoreForceMainDocumentChannel(aValue);
   2420  return NS_OK;
   2421 }
   2422 
   2423 NS_IMETHODIMP
   2424 HttpBaseChannel::GetIsUserAgentHeaderOutdated(bool* aValue) {
   2425  NS_ENSURE_ARG_POINTER(aValue);
   2426  *aValue = LoadIsUserAgentHeaderOutdated();
   2427  return NS_OK;
   2428 }
   2429 
   2430 NS_IMETHODIMP
   2431 HttpBaseChannel::SetIsUserAgentHeaderOutdated(bool aValue) {
   2432  StoreIsUserAgentHeaderOutdated(aValue);
   2433  return NS_OK;
   2434 }
   2435 
   2436 NS_IMETHODIMP
   2437 HttpBaseChannel::GetProtocolVersion(nsACString& aProtocolVersion) {
   2438  // Try to use ALPN if available and if it is not for a proxy, i.e if an
   2439  // https proxy was not used or if https proxy was used but the connection to
   2440  // the origin server is also https. In the case, an https proxy was used and
   2441  // the connection to the origin server was http, mSecurityInfo will be from
   2442  // the proxy.
   2443  if (!mConnectionInfo || !mConnectionInfo->UsingHttpsProxy() ||
   2444      mConnectionInfo->EndToEndSSL()) {
   2445    nsAutoCString protocol;
   2446    if (mSecurityInfo &&
   2447        NS_SUCCEEDED(mSecurityInfo->GetNegotiatedNPN(protocol)) &&
   2448        !protocol.IsEmpty()) {
   2449      // The negotiated protocol was not empty so we can use it.
   2450      aProtocolVersion = protocol;
   2451      return NS_OK;
   2452    }
   2453  }
   2454 
   2455  if (mResponseHead) {
   2456    HttpVersion version = mResponseHead->Version();
   2457    aProtocolVersion.Assign(nsHttp::GetProtocolVersion(version));
   2458    return NS_OK;
   2459  }
   2460 
   2461  return NS_ERROR_NOT_AVAILABLE;
   2462 }
   2463 
   2464 //-----------------------------------------------------------------------------
   2465 // HttpBaseChannel::nsIHttpChannelInternal
   2466 //-----------------------------------------------------------------------------
   2467 
   2468 NS_IMETHODIMP
   2469 HttpBaseChannel::SetTopWindowURIIfUnknown(nsIURI* aTopWindowURI) {
   2470  if (!aTopWindowURI) {
   2471    return NS_ERROR_INVALID_ARG;
   2472  }
   2473 
   2474  if (mTopWindowURI) {
   2475    LOG(
   2476        ("HttpChannelBase::SetTopWindowURIIfUnknown [this=%p] "
   2477         "mTopWindowURI is already set.\n",
   2478         this));
   2479    return NS_ERROR_FAILURE;
   2480  }
   2481 
   2482  nsCOMPtr<nsIURI> topWindowURI;
   2483  (void)GetTopWindowURI(getter_AddRefs(topWindowURI));
   2484 
   2485  // Don't modify |mTopWindowURI| if we can get one from GetTopWindowURI().
   2486  if (topWindowURI) {
   2487    LOG(
   2488        ("HttpChannelBase::SetTopWindowURIIfUnknown [this=%p] "
   2489         "Return an error since we got a top window uri.\n",
   2490         this));
   2491    return NS_ERROR_FAILURE;
   2492  }
   2493 
   2494  mTopWindowURI = aTopWindowURI;
   2495  return NS_OK;
   2496 }
   2497 
   2498 NS_IMETHODIMP
   2499 HttpBaseChannel::GetTopWindowURI(nsIURI** aTopWindowURI) {
   2500  nsCOMPtr<nsIURI> uriBeingLoaded =
   2501      AntiTrackingUtils::MaybeGetDocumentURIBeingLoaded(this);
   2502  return GetTopWindowURI(uriBeingLoaded, aTopWindowURI);
   2503 }
   2504 
   2505 nsresult HttpBaseChannel::GetTopWindowURI(nsIURI* aURIBeingLoaded,
   2506                                          nsIURI** aTopWindowURI) {
   2507  nsresult rv = NS_OK;
   2508  nsCOMPtr<mozIThirdPartyUtil> util;
   2509  // Only compute the top window URI once. In e10s, this must be computed in the
   2510  // child. The parent gets the top window URI through HttpChannelOpenArgs.
   2511  if (!mTopWindowURI) {
   2512    util = components::ThirdPartyUtil::Service();
   2513    if (!util) {
   2514      return NS_ERROR_NOT_AVAILABLE;
   2515    }
   2516    nsCOMPtr<mozIDOMWindowProxy> win;
   2517    rv = util->GetTopWindowForChannel(this, aURIBeingLoaded,
   2518                                      getter_AddRefs(win));
   2519    if (NS_SUCCEEDED(rv)) {
   2520      rv = util->GetURIFromWindow(win, getter_AddRefs(mTopWindowURI));
   2521 #if DEBUG
   2522      if (mTopWindowURI) {
   2523        nsCString spec;
   2524        if (NS_SUCCEEDED(mTopWindowURI->GetSpec(spec))) {
   2525          LOG(("HttpChannelBase::Setting topwindow URI spec %s [this=%p]\n",
   2526               spec.get(), this));
   2527        }
   2528      }
   2529 #endif
   2530    }
   2531  }
   2532  *aTopWindowURI = do_AddRef(mTopWindowURI).take();
   2533  return rv;
   2534 }
   2535 
   2536 NS_IMETHODIMP
   2537 HttpBaseChannel::GetDocumentURI(nsIURI** aDocumentURI) {
   2538  NS_ENSURE_ARG_POINTER(aDocumentURI);
   2539  *aDocumentURI = do_AddRef(mDocumentURI).take();
   2540  return NS_OK;
   2541 }
   2542 
   2543 NS_IMETHODIMP
   2544 HttpBaseChannel::SetDocumentURI(nsIURI* aDocumentURI) {
   2545  ENSURE_CALLED_BEFORE_CONNECT();
   2546  mDocumentURI = aDocumentURI;
   2547  return NS_OK;
   2548 }
   2549 
   2550 NS_IMETHODIMP
   2551 HttpBaseChannel::GetRequestVersion(uint32_t* major, uint32_t* minor) {
   2552  HttpVersion version = mRequestHead.Version();
   2553 
   2554  if (major) {
   2555    *major = static_cast<uint32_t>(version) / 10;
   2556  }
   2557  if (minor) {
   2558    *minor = static_cast<uint32_t>(version) % 10;
   2559  }
   2560 
   2561  return NS_OK;
   2562 }
   2563 
   2564 NS_IMETHODIMP
   2565 HttpBaseChannel::GetResponseVersion(uint32_t* major, uint32_t* minor) {
   2566  if (!mResponseHead) {
   2567    *major = *minor = 0;  // we should at least be kind about it
   2568    return NS_ERROR_NOT_AVAILABLE;
   2569  }
   2570 
   2571  HttpVersion version = mResponseHead->Version();
   2572 
   2573  if (major) {
   2574    *major = static_cast<uint32_t>(version) / 10;
   2575  }
   2576  if (minor) {
   2577    *minor = static_cast<uint32_t>(version) % 10;
   2578  }
   2579 
   2580  return NS_OK;
   2581 }
   2582 
   2583 bool HttpBaseChannel::IsBrowsingContextDiscarded() const {
   2584  // If there is no loadGroup attached to the current channel, we check the
   2585  // global private browsing state for the private channel instead. For
   2586  // non-private channel, we will always return false here.
   2587  //
   2588  // Note that we can only access the global private browsing state in the
   2589  // parent process. So, we will fallback to just return false in the content
   2590  // process.
   2591  if (!mLoadGroup) {
   2592    if (!XRE_IsParentProcess()) {
   2593      return false;
   2594    }
   2595 
   2596    return mLoadInfo->GetOriginAttributes().IsPrivateBrowsing() &&
   2597           !dom::CanonicalBrowsingContext::IsPrivateBrowsingActive();
   2598  }
   2599 
   2600  return mLoadGroup->GetIsBrowsingContextDiscarded();
   2601 }
   2602 
   2603 // https://mikewest.github.io/corpp/#process-navigation-response
   2604 nsresult HttpBaseChannel::ProcessCrossOriginEmbedderPolicyHeader() {
   2605  nsresult rv;
   2606  if (!StaticPrefs::browser_tabs_remote_useCrossOriginEmbedderPolicy()) {
   2607    return NS_OK;
   2608  }
   2609 
   2610  // Only consider Cross-Origin-Embedder-Policy for document loads.
   2611  if (mLoadInfo->GetExternalContentPolicyType() !=
   2612          ExtContentPolicy::TYPE_DOCUMENT &&
   2613      mLoadInfo->GetExternalContentPolicyType() !=
   2614          ExtContentPolicy::TYPE_SUBDOCUMENT) {
   2615    return NS_OK;
   2616  }
   2617 
   2618  nsILoadInfo::CrossOriginEmbedderPolicy resultPolicy =
   2619      nsILoadInfo::EMBEDDER_POLICY_NULL;
   2620  bool isCoepCredentiallessEnabled;
   2621  rv = mLoadInfo->GetIsOriginTrialCoepCredentiallessEnabledForTopLevel(
   2622      &isCoepCredentiallessEnabled);
   2623  NS_ENSURE_SUCCESS(rv, rv);
   2624  rv = GetResponseEmbedderPolicy(isCoepCredentiallessEnabled, &resultPolicy);
   2625  if (NS_FAILED(rv)) {
   2626    return NS_OK;
   2627  }
   2628 
   2629  // https://html.spec.whatwg.org/multipage/origin.html#coep
   2630  if (mLoadInfo->GetExternalContentPolicyType() ==
   2631          ExtContentPolicy::TYPE_SUBDOCUMENT &&
   2632      !nsHttpChannel::IsRedirectStatus(mResponseHead->Status()) &&
   2633      mLoadInfo->GetLoadingEmbedderPolicy() !=
   2634          nsILoadInfo::EMBEDDER_POLICY_NULL &&
   2635      resultPolicy != nsILoadInfo::EMBEDDER_POLICY_REQUIRE_CORP &&
   2636      resultPolicy != nsILoadInfo::EMBEDDER_POLICY_CREDENTIALLESS) {
   2637    return NS_ERROR_DOM_COEP_FAILED;
   2638  }
   2639 
   2640  return NS_OK;
   2641 }
   2642 
   2643 // https://mikewest.github.io/corpp/#corp-check
   2644 nsresult HttpBaseChannel::ProcessCrossOriginResourcePolicyHeader() {
   2645  // Fetch 4.5.9
   2646  dom::RequestMode requestMode;
   2647  MOZ_ALWAYS_SUCCEEDS(GetRequestMode(&requestMode));
   2648  // XXX this seems wrong per spec? What about navigate
   2649  if (requestMode != RequestMode::No_cors) {
   2650    return NS_OK;
   2651  }
   2652 
   2653  // We only apply this for resources.
   2654  auto extContentPolicyType = mLoadInfo->GetExternalContentPolicyType();
   2655  if (extContentPolicyType == ExtContentPolicy::TYPE_DOCUMENT ||
   2656      extContentPolicyType == ExtContentPolicy::TYPE_WEBSOCKET ||
   2657      extContentPolicyType == ExtContentPolicy::TYPE_SAVEAS_DOWNLOAD) {
   2658    return NS_OK;
   2659  }
   2660 
   2661  if (extContentPolicyType == ExtContentPolicy::TYPE_SUBDOCUMENT) {
   2662    // COEP pref off, skip CORP checking for subdocument.
   2663    if (!StaticPrefs::browser_tabs_remote_useCrossOriginEmbedderPolicy()) {
   2664      return NS_OK;
   2665    }
   2666    // COEP 3.2.1.2 when request targets a nested browsing context then embedder
   2667    // policy value is "unsafe-none", then return allowed.
   2668    if (mLoadInfo->GetLoadingEmbedderPolicy() ==
   2669        nsILoadInfo::EMBEDDER_POLICY_NULL) {
   2670      return NS_OK;
   2671    }
   2672  }
   2673 
   2674  MOZ_ASSERT(mLoadInfo->GetLoadingPrincipal(),
   2675             "Resources should always have a LoadingPrincipal");
   2676  if (!mResponseHead) {
   2677    return NS_OK;
   2678  }
   2679 
   2680  if (mLoadInfo->GetLoadingPrincipal()->IsSystemPrincipal()) {
   2681    return NS_OK;
   2682  }
   2683 
   2684  nsAutoCString content;
   2685  (void)mResponseHead->GetHeader(nsHttp::Cross_Origin_Resource_Policy, content);
   2686 
   2687  if (StaticPrefs::browser_tabs_remote_useCrossOriginEmbedderPolicy()) {
   2688    if (content.IsEmpty()) {
   2689      if (mLoadInfo->GetLoadingEmbedderPolicy() ==
   2690          nsILoadInfo::EMBEDDER_POLICY_CREDENTIALLESS) {
   2691        bool requestIncludesCredentials = false;
   2692        nsresult rv = GetCorsIncludeCredentials(&requestIncludesCredentials);
   2693        if (NS_FAILED(rv)) {
   2694          return NS_OK;
   2695        }
   2696        // COEP: Set policy to `same-origin` if: response’s
   2697        // request-includes-credentials is true, or forNavigation is true.
   2698        if (requestIncludesCredentials ||
   2699            extContentPolicyType == ExtContentPolicyType::TYPE_SUBDOCUMENT) {
   2700          content = "same-origin"_ns;
   2701        }
   2702      } else if (mLoadInfo->GetLoadingEmbedderPolicy() ==
   2703                 nsILoadInfo::EMBEDDER_POLICY_REQUIRE_CORP) {
   2704        // COEP 3.2.1.6 If policy is null, and embedder policy is
   2705        // "require-corp", set policy to "same-origin". Note that we treat
   2706        // invalid value as "cross-origin", which spec indicates. We might want
   2707        // to make that stricter.
   2708        content = "same-origin"_ns;
   2709      }
   2710    }
   2711  }
   2712 
   2713  if (content.IsEmpty()) {
   2714    return NS_OK;
   2715  }
   2716 
   2717  nsCOMPtr<nsIPrincipal> channelOrigin;
   2718  nsContentUtils::GetSecurityManager()->GetChannelResultPrincipal(
   2719      this, getter_AddRefs(channelOrigin));
   2720 
   2721  // Cross-Origin-Resource-Policy = %s"same-origin" / %s"same-site" /
   2722  // %s"cross-origin"
   2723  if (content.EqualsLiteral("same-origin")) {
   2724    if (!channelOrigin->Equals(mLoadInfo->GetLoadingPrincipal())) {
   2725      return NS_ERROR_DOM_CORP_FAILED;
   2726    }
   2727    return NS_OK;
   2728  }
   2729  if (content.EqualsLiteral("same-site")) {
   2730    nsAutoCString documentBaseDomain;
   2731    nsAutoCString resourceBaseDomain;
   2732    mLoadInfo->GetLoadingPrincipal()->GetBaseDomain(documentBaseDomain);
   2733    channelOrigin->GetBaseDomain(resourceBaseDomain);
   2734    if (documentBaseDomain != resourceBaseDomain) {
   2735      return NS_ERROR_DOM_CORP_FAILED;
   2736    }
   2737 
   2738    nsCOMPtr<nsIURI> resourceURI = channelOrigin->GetURI();
   2739    if (!mLoadInfo->GetLoadingPrincipal()->SchemeIs("https") &&
   2740        resourceURI->SchemeIs("https")) {
   2741      return NS_ERROR_DOM_CORP_FAILED;
   2742    }
   2743 
   2744    return NS_OK;
   2745  }
   2746 
   2747  return NS_OK;
   2748 }
   2749 
   2750 // See https://gist.github.com/annevk/6f2dd8c79c77123f39797f6bdac43f3e
   2751 // This method runs steps 1-4 of the algorithm to compare
   2752 // cross-origin-opener policies
   2753 static bool CompareCrossOriginOpenerPolicies(
   2754    nsILoadInfo::CrossOriginOpenerPolicy documentPolicy,
   2755    nsIPrincipal* documentOrigin,
   2756    nsILoadInfo::CrossOriginOpenerPolicy resultPolicy,
   2757    nsIPrincipal* resultOrigin) {
   2758  if (documentPolicy == nsILoadInfo::OPENER_POLICY_UNSAFE_NONE &&
   2759      resultPolicy == nsILoadInfo::OPENER_POLICY_UNSAFE_NONE) {
   2760    return true;
   2761  }
   2762 
   2763  if (documentPolicy == nsILoadInfo::OPENER_POLICY_UNSAFE_NONE ||
   2764      resultPolicy == nsILoadInfo::OPENER_POLICY_UNSAFE_NONE) {
   2765    return false;
   2766  }
   2767 
   2768  if (documentPolicy == resultPolicy && documentOrigin->Equals(resultOrigin)) {
   2769    return true;
   2770  }
   2771 
   2772  return false;
   2773 }
   2774 
   2775 // This runs steps 1-5 of the algorithm when navigating a top level document.
   2776 // See https://gist.github.com/annevk/6f2dd8c79c77123f39797f6bdac43f3e
   2777 nsresult HttpBaseChannel::ComputeCrossOriginOpenerPolicyMismatch() {
   2778  MOZ_ASSERT(XRE_IsParentProcess());
   2779 
   2780  StoreHasCrossOriginOpenerPolicyMismatch(false);
   2781  if (!StaticPrefs::browser_tabs_remote_useCrossOriginOpenerPolicy()) {
   2782    return NS_OK;
   2783  }
   2784 
   2785  // Only consider Cross-Origin-Opener-Policy for toplevel document loads.
   2786  if (mLoadInfo->GetExternalContentPolicyType() !=
   2787      ExtContentPolicy::TYPE_DOCUMENT) {
   2788    return NS_OK;
   2789  }
   2790 
   2791  // Maybe the channel failed and we have no response head?
   2792  if (!mResponseHead) {
   2793    // Not having a response head is not a hard failure at the point where
   2794    // this method is called.
   2795    return NS_OK;
   2796  }
   2797 
   2798  RefPtr<mozilla::dom::BrowsingContext> ctx;
   2799  mLoadInfo->GetBrowsingContext(getter_AddRefs(ctx));
   2800 
   2801  // In xpcshell-tests we don't always have a browsingContext
   2802  if (!ctx) {
   2803    return NS_OK;
   2804  }
   2805 
   2806  nsCOMPtr<nsIPrincipal> resultOrigin;
   2807  nsContentUtils::GetSecurityManager()->GetChannelResultPrincipal(
   2808      this, getter_AddRefs(resultOrigin));
   2809 
   2810  // Get the policy of the active document, and the policy for the result.
   2811  nsILoadInfo::CrossOriginOpenerPolicy documentPolicy = ctx->GetOpenerPolicy();
   2812  nsILoadInfo::CrossOriginOpenerPolicy resultPolicy =
   2813      nsILoadInfo::OPENER_POLICY_UNSAFE_NONE;
   2814  (void)ComputeCrossOriginOpenerPolicy(documentPolicy, &resultPolicy);
   2815  mComputedCrossOriginOpenerPolicy = resultPolicy;
   2816 
   2817  // Add a permission to mark this site as high-value into the permission DB.
   2818  if (resultPolicy != nsILoadInfo::OPENER_POLICY_UNSAFE_NONE) {
   2819    mozilla::dom::AddHighValuePermission(
   2820        resultOrigin, mozilla::dom::kHighValueCOOPPermission);
   2821  }
   2822 
   2823  // If bc's popup sandboxing flag set is not empty and potentialCOOP is
   2824  // non-null, then navigate bc to a network error and abort these steps.
   2825  if (resultPolicy != nsILoadInfo::OPENER_POLICY_UNSAFE_NONE &&
   2826      mLoadInfo->GetSandboxFlags()) {
   2827    LOG((
   2828        "HttpBaseChannel::ComputeCrossOriginOpenerPolicyMismatch network error "
   2829        "for non empty sandboxing and non null COOP"));
   2830    return NS_ERROR_DOM_COOP_FAILED;
   2831  }
   2832 
   2833  // In xpcshell-tests we don't always have a current window global
   2834  RefPtr<mozilla::dom::WindowGlobalParent> currentWindowGlobal =
   2835      ctx->Canonical()->GetCurrentWindowGlobal();
   2836  if (!currentWindowGlobal) {
   2837    return NS_OK;
   2838  }
   2839 
   2840  // We use the top window principal as the documentOrigin
   2841  nsCOMPtr<nsIPrincipal> documentOrigin =
   2842      currentWindowGlobal->DocumentPrincipal();
   2843 
   2844  bool compareResult = CompareCrossOriginOpenerPolicies(
   2845      documentPolicy, documentOrigin, resultPolicy, resultOrigin);
   2846 
   2847  if (LOG_ENABLED()) {
   2848    LOG(
   2849        ("HttpBaseChannel::HasCrossOriginOpenerPolicyMismatch - "
   2850         "doc:%d result:%d - compare:%d\n",
   2851         documentPolicy, resultPolicy, compareResult));
   2852    nsAutoCString docOrigin("(null)");
   2853    nsCOMPtr<nsIURI> uri = documentOrigin->GetURI();
   2854    if (uri) {
   2855      uri->GetSpec(docOrigin);
   2856    }
   2857    nsAutoCString resOrigin("(null)");
   2858    uri = resultOrigin->GetURI();
   2859    if (uri) {
   2860      uri->GetSpec(resOrigin);
   2861    }
   2862    LOG(("doc origin:%s - res origin: %s\n", docOrigin.get(), resOrigin.get()));
   2863  }
   2864 
   2865  if (compareResult) {
   2866    return NS_OK;
   2867  }
   2868 
   2869  // If one of the following is false:
   2870  //   - document's policy is same-origin-allow-popups
   2871  //   - resultPolicy is null
   2872  //   - doc is the initial about:blank document
   2873  // then we have a mismatch.
   2874 
   2875  if (documentPolicy != nsILoadInfo::OPENER_POLICY_SAME_ORIGIN_ALLOW_POPUPS) {
   2876    StoreHasCrossOriginOpenerPolicyMismatch(true);
   2877    return NS_OK;
   2878  }
   2879 
   2880  if (resultPolicy != nsILoadInfo::OPENER_POLICY_UNSAFE_NONE) {
   2881    StoreHasCrossOriginOpenerPolicyMismatch(true);
   2882    return NS_OK;
   2883  }
   2884 
   2885  if (!currentWindowGlobal->IsInitialDocument()) {
   2886    StoreHasCrossOriginOpenerPolicyMismatch(true);
   2887    return NS_OK;
   2888  }
   2889 
   2890  return NS_OK;
   2891 }
   2892 
   2893 nsresult HttpBaseChannel::ProcessCrossOriginSecurityHeaders() {
   2894  StoreProcessCrossOriginSecurityHeadersCalled(true);
   2895  nsresult rv = ProcessCrossOriginEmbedderPolicyHeader();
   2896  if (NS_FAILED(rv)) {
   2897    return rv;
   2898  }
   2899  rv = ProcessCrossOriginResourcePolicyHeader();
   2900  if (NS_FAILED(rv)) {
   2901    return rv;
   2902  }
   2903  return ComputeCrossOriginOpenerPolicyMismatch();
   2904 }
   2905 
   2906 enum class Report { Error, Warning };
   2907 
   2908 // Helper Function to report messages to the console when the loaded
   2909 // script had a wrong MIME type.
   2910 void ReportMimeTypeMismatch(HttpBaseChannel* aChannel, const char* aMessageName,
   2911                            nsIURI* aURI, const nsACString& aContentType,
   2912                            Report report) {
   2913  NS_ConvertUTF8toUTF16 spec(aURI->GetSpecOrDefault());
   2914  NS_ConvertUTF8toUTF16 contentType(aContentType);
   2915 
   2916  aChannel->LogMimeTypeMismatch(nsCString(aMessageName),
   2917                                report == Report::Warning, spec, contentType);
   2918 }
   2919 
   2920 // Check and potentially enforce X-Content-Type-Options: nosniff
   2921 nsresult ProcessXCTO(HttpBaseChannel* aChannel, nsIURI* aURI,
   2922                     nsHttpResponseHead* aResponseHead,
   2923                     nsILoadInfo* aLoadInfo) {
   2924  if (!aURI || !aResponseHead || !aLoadInfo) {
   2925    // if there is no uri, no response head or no loadInfo, then there is
   2926    // nothing to do
   2927    return NS_OK;
   2928  }
   2929 
   2930  // 1) Query the XCTO header and check if 'nosniff' is the first value.
   2931  nsAutoCString contentTypeOptionsHeader;
   2932  if (!aResponseHead->GetContentTypeOptionsHeader(contentTypeOptionsHeader)) {
   2933    // if failed to get XCTO header, then there is nothing to do.
   2934    return NS_OK;
   2935  }
   2936 
   2937  // let's compare the header (ignoring case)
   2938  // e.g. "NoSniFF" -> "nosniff"
   2939  // if it's not 'nosniff' then there is nothing to do here
   2940  if (!contentTypeOptionsHeader.EqualsIgnoreCase("nosniff")) {
   2941    // since we are getting here, the XCTO header was sent;
   2942    // a non matching value most likely means a mistake happenend;
   2943    // e.g. sending 'nosnif' instead of 'nosniff', let's log a warning.
   2944    AutoTArray<nsString, 1> params;
   2945    CopyUTF8toUTF16(contentTypeOptionsHeader, *params.AppendElement());
   2946    RefPtr<dom::Document> doc;
   2947    aLoadInfo->GetLoadingDocument(getter_AddRefs(doc));
   2948    nsContentUtils::ReportToConsole(nsIScriptError::warningFlag, "XCTO"_ns, doc,
   2949                                    nsContentUtils::eSECURITY_PROPERTIES,
   2950                                    "XCTOHeaderValueMissing", params);
   2951    return NS_OK;
   2952  }
   2953 
   2954  // 2) Query the content type from the channel
   2955  nsAutoCString contentType;
   2956  aResponseHead->ContentType(contentType);
   2957 
   2958  // 3) Compare the expected MIME type with the actual type
   2959  if (aLoadInfo->GetExternalContentPolicyType() ==
   2960      ExtContentPolicy::TYPE_STYLESHEET) {
   2961    if (contentType.EqualsLiteral(TEXT_CSS)) {
   2962      return NS_OK;
   2963    }
   2964    ReportMimeTypeMismatch(aChannel, "MimeTypeMismatch2", aURI, contentType,
   2965                           Report::Error);
   2966    return NS_ERROR_CORRUPTED_CONTENT;
   2967  }
   2968 
   2969  if (aLoadInfo->GetExternalContentPolicyType() ==
   2970      ExtContentPolicy::TYPE_SCRIPT) {
   2971    if (nsContentUtils::IsJavascriptMIMEType(
   2972            NS_ConvertUTF8toUTF16(contentType))) {
   2973      return NS_OK;
   2974    }
   2975    ReportMimeTypeMismatch(aChannel, "MimeTypeMismatch2", aURI, contentType,
   2976                           Report::Error);
   2977    return NS_ERROR_CORRUPTED_CONTENT;
   2978  }
   2979 
   2980  auto policyType = aLoadInfo->GetExternalContentPolicyType();
   2981  if (policyType == ExtContentPolicy::TYPE_DOCUMENT ||
   2982      policyType == ExtContentPolicy::TYPE_SUBDOCUMENT ||
   2983      policyType == ExtContentPolicy::TYPE_OBJECT) {
   2984    // If the header XCTO nosniff is set for any browsing context, then
   2985    // we set the skipContentSniffing flag on the Loadinfo. Within
   2986    // GetMIMETypeFromContent we then bail early and do not do any sniffing.
   2987    aLoadInfo->SetSkipContentSniffing(true);
   2988    return NS_OK;
   2989  }
   2990 
   2991  return NS_OK;
   2992 }
   2993 
   2994 nsresult EnsureMIMEOfJSONModule(HttpBaseChannel* aChannel, nsIURI* aURI,
   2995                                nsHttpResponseHead* aResponseHead,
   2996                                nsILoadInfo* aLoadInfo) {
   2997  if (!aURI || !aResponseHead || !aLoadInfo) {
   2998    // if there is no uri, no response head or no loadInfo, then there is
   2999    // nothing to do
   3000    return NS_OK;
   3001  }
   3002 
   3003  if (aLoadInfo->GetExternalContentPolicyType() !=
   3004      ExtContentPolicy::TYPE_JSON) {
   3005    // if this is not a JSON load, then there is nothing to do
   3006    return NS_OK;
   3007  }
   3008 
   3009  nsAutoCString contentType;
   3010  aResponseHead->ContentType(contentType);
   3011  NS_ConvertUTF8toUTF16 typeString(contentType);
   3012 
   3013  if (nsContentUtils::IsJsonMimeType(typeString)) {
   3014    return NS_OK;
   3015  }
   3016 
   3017  ReportMimeTypeMismatch(aChannel, "BlockJsonModuleWithWrongMimeType", aURI,
   3018                         contentType, Report::Error);
   3019  return NS_ERROR_CORRUPTED_CONTENT;
   3020 }
   3021 
   3022 // Ensure that a load of type script has correct MIME type
   3023 nsresult EnsureMIMEOfScript(HttpBaseChannel* aChannel, nsIURI* aURI,
   3024                            nsHttpResponseHead* aResponseHead,
   3025                            nsILoadInfo* aLoadInfo) {
   3026  if (!aURI || !aResponseHead || !aLoadInfo) {
   3027    // if there is no uri, no response head or no loadInfo, then there is
   3028    // nothing to do
   3029    return NS_OK;
   3030  }
   3031 
   3032  if (aLoadInfo->GetExternalContentPolicyType() !=
   3033      ExtContentPolicy::TYPE_SCRIPT) {
   3034    // if this is not a script load, then there is nothing to do
   3035    return NS_OK;
   3036  }
   3037 
   3038  nsAutoCString contentType;
   3039  aResponseHead->ContentType(contentType);
   3040  NS_ConvertUTF8toUTF16 typeString(contentType);
   3041 
   3042  if (nsContentUtils::IsJavascriptMIMEType(typeString)) {
   3043    // script load has type script
   3044    glean::http::script_block_incorrect_mime
   3045        .EnumGet(glean::http::ScriptBlockIncorrectMimeLabel::eJavascript)
   3046        .Add();
   3047    return NS_OK;
   3048  }
   3049 
   3050  const auto internalPolicyType = aLoadInfo->InternalContentPolicyType();
   3051  if (internalPolicyType ==
   3052          nsIContentPolicy::TYPE_INTERNAL_WORKER_STATIC_MODULE &&
   3053      nsContentUtils::IsJsonMimeType(typeString)) {
   3054    // script and json are both allowed
   3055    glean::http::script_block_incorrect_mime
   3056        .EnumGet(glean::http::ScriptBlockIncorrectMimeLabel::eTextJson)
   3057        .Add();
   3058    return NS_OK;
   3059  }
   3060 
   3061  switch (aLoadInfo->InternalContentPolicyType()) {
   3062    case nsIContentPolicy::TYPE_SCRIPT:
   3063    case nsIContentPolicy::TYPE_INTERNAL_SCRIPT:
   3064    case nsIContentPolicy::TYPE_INTERNAL_SCRIPT_PRELOAD:
   3065    case nsIContentPolicy::TYPE_INTERNAL_MODULE:
   3066    case nsIContentPolicy::TYPE_INTERNAL_MODULE_PRELOAD:
   3067    case nsIContentPolicy::TYPE_INTERNAL_CHROMEUTILS_COMPILED_SCRIPT:
   3068    case nsIContentPolicy::TYPE_INTERNAL_FRAME_MESSAGEMANAGER_SCRIPT:
   3069      glean::http::script_block_incorrect_mime
   3070          .EnumGet(glean::http::ScriptBlockIncorrectMimeLabel::eScriptLoad)
   3071          .Add();
   3072      break;
   3073    case nsIContentPolicy::TYPE_INTERNAL_WORKER:
   3074    case nsIContentPolicy::TYPE_INTERNAL_WORKER_STATIC_MODULE:
   3075    case nsIContentPolicy::TYPE_INTERNAL_SHARED_WORKER:
   3076      glean::http::script_block_incorrect_mime
   3077          .EnumGet(glean::http::ScriptBlockIncorrectMimeLabel::eWorkerLoad)
   3078          .Add();
   3079      break;
   3080    case nsIContentPolicy::TYPE_INTERNAL_SERVICE_WORKER:
   3081      glean::http::script_block_incorrect_mime
   3082          .EnumGet(
   3083              glean::http::ScriptBlockIncorrectMimeLabel::eServiceworkerLoad)
   3084          .Add();
   3085      break;
   3086    case nsIContentPolicy::TYPE_INTERNAL_WORKER_IMPORT_SCRIPTS:
   3087      glean::http::script_block_incorrect_mime
   3088          .EnumGet(
   3089              glean::http::ScriptBlockIncorrectMimeLabel::eImportscriptLoad)
   3090          .Add();
   3091      break;
   3092    case nsIContentPolicy::TYPE_INTERNAL_AUDIOWORKLET:
   3093    case nsIContentPolicy::TYPE_INTERNAL_PAINTWORKLET:
   3094      glean::http::script_block_incorrect_mime
   3095          .EnumGet(glean::http::ScriptBlockIncorrectMimeLabel::eWorkletLoad)
   3096          .Add();
   3097      break;
   3098    default:
   3099      MOZ_ASSERT_UNREACHABLE("unexpected script type");
   3100      break;
   3101  }
   3102 
   3103  if (aLoadInfo->GetLoadingPrincipal()->IsSameOrigin(aURI)) {
   3104    // same origin
   3105    glean::http::script_block_incorrect_mime
   3106        .EnumGet(glean::http::ScriptBlockIncorrectMimeLabel::eSameOrigin)
   3107        .Add();
   3108  } else {
   3109    bool cors = false;
   3110    nsAutoCString corsOrigin;
   3111    nsresult rv = aResponseHead->GetHeader(
   3112        nsHttp::ResolveAtom("Access-Control-Allow-Origin"_ns), corsOrigin);
   3113    if (NS_SUCCEEDED(rv)) {
   3114      if (corsOrigin.Equals("*")) {
   3115        cors = true;
   3116      } else {
   3117        nsCOMPtr<nsIURI> corsOriginURI;
   3118        rv = NS_NewURI(getter_AddRefs(corsOriginURI), corsOrigin);
   3119        if (NS_SUCCEEDED(rv)) {
   3120          if (aLoadInfo->GetLoadingPrincipal()->IsSameOrigin(corsOriginURI)) {
   3121            cors = true;
   3122          }
   3123        }
   3124      }
   3125    }
   3126    if (cors) {
   3127      // cors origin
   3128      glean::http::script_block_incorrect_mime
   3129          .EnumGet(glean::http::ScriptBlockIncorrectMimeLabel::eCorsOrigin)
   3130          .Add();
   3131    } else {
   3132      // cross origin
   3133      glean::http::script_block_incorrect_mime
   3134          .EnumGet(glean::http::ScriptBlockIncorrectMimeLabel::eCrossOrigin)
   3135          .Add();
   3136    }
   3137  }
   3138 
   3139  bool block = false;
   3140  if (StringBeginsWith(contentType, "image/"_ns)) {
   3141    // script load has type image
   3142    glean::http::script_block_incorrect_mime
   3143        .EnumGet(glean::http::ScriptBlockIncorrectMimeLabel::eImage)
   3144        .Add();
   3145    block = true;
   3146  } else if (StringBeginsWith(contentType, "audio/"_ns)) {
   3147    // script load has type audio
   3148    glean::http::script_block_incorrect_mime
   3149        .EnumGet(glean::http::ScriptBlockIncorrectMimeLabel::eAudio)
   3150        .Add();
   3151    block = true;
   3152  } else if (StringBeginsWith(contentType, "video/"_ns)) {
   3153    // script load has type video
   3154    glean::http::script_block_incorrect_mime
   3155        .EnumGet(glean::http::ScriptBlockIncorrectMimeLabel::eVideo)
   3156        .Add();
   3157    block = true;
   3158  } else if (StringBeginsWith(contentType, "text/csv"_ns)) {
   3159    // script load has type text/csv
   3160    glean::http::script_block_incorrect_mime
   3161        .EnumGet(glean::http::ScriptBlockIncorrectMimeLabel::eTextCsv)
   3162        .Add();
   3163    block = true;
   3164  }
   3165 
   3166  if (block) {
   3167    ReportMimeTypeMismatch(aChannel, "BlockScriptWithWrongMimeType2", aURI,
   3168                           contentType, Report::Error);
   3169    return NS_ERROR_CORRUPTED_CONTENT;
   3170  }
   3171 
   3172  if (StringBeginsWith(contentType, "text/plain"_ns)) {
   3173    // script load has type text/plain
   3174    glean::http::script_block_incorrect_mime
   3175        .EnumGet(glean::http::ScriptBlockIncorrectMimeLabel::eTextPlain)
   3176        .Add();
   3177  } else if (StringBeginsWith(contentType, "text/xml"_ns)) {
   3178    // script load has type text/xml
   3179    glean::http::script_block_incorrect_mime
   3180        .EnumGet(glean::http::ScriptBlockIncorrectMimeLabel::eTextXml)
   3181        .Add();
   3182  } else if (StringBeginsWith(contentType, "application/octet-stream"_ns)) {
   3183    // script load has type application/octet-stream
   3184    glean::http::script_block_incorrect_mime
   3185        .EnumGet(glean::http::ScriptBlockIncorrectMimeLabel::eAppOctetStream)
   3186        .Add();
   3187  } else if (StringBeginsWith(contentType, "application/xml"_ns)) {
   3188    // script load has type application/xml
   3189    glean::http::script_block_incorrect_mime
   3190        .EnumGet(glean::http::ScriptBlockIncorrectMimeLabel::eAppXml)
   3191        .Add();
   3192  } else if (StringBeginsWith(contentType, "application/json"_ns)) {
   3193    // script load has type application/json
   3194    glean::http::script_block_incorrect_mime
   3195        .EnumGet(glean::http::ScriptBlockIncorrectMimeLabel::eAppJson)
   3196        .Add();
   3197  } else if (StringBeginsWith(contentType, "text/json"_ns)) {
   3198    // script load has type text/json
   3199    glean::http::script_block_incorrect_mime
   3200        .EnumGet(glean::http::ScriptBlockIncorrectMimeLabel::eTextJson)
   3201        .Add();
   3202  } else if (StringBeginsWith(contentType, "text/html"_ns)) {
   3203    // script load has type text/html
   3204    glean::http::script_block_incorrect_mime
   3205        .EnumGet(glean::http::ScriptBlockIncorrectMimeLabel::eTextHtml)
   3206        .Add();
   3207  } else if (contentType.IsEmpty()) {
   3208    // script load has no type
   3209    glean::http::script_block_incorrect_mime
   3210        .EnumGet(glean::http::ScriptBlockIncorrectMimeLabel::eEmpty)
   3211        .Add();
   3212  } else {
   3213    // script load has unknown type
   3214    glean::http::script_block_incorrect_mime
   3215        .EnumGet(glean::http::ScriptBlockIncorrectMimeLabel::eUnknown)
   3216        .Add();
   3217  }
   3218 
   3219  nsContentPolicyType internalType = aLoadInfo->InternalContentPolicyType();
   3220 
   3221  // We restrict importScripts() in worker code to JavaScript MIME types.
   3222  if (internalType == nsIContentPolicy::TYPE_INTERNAL_WORKER_IMPORT_SCRIPTS ||
   3223      internalType == nsIContentPolicy::TYPE_INTERNAL_WORKER_STATIC_MODULE) {
   3224    ReportMimeTypeMismatch(aChannel, "BlockImportScriptsWithWrongMimeType",
   3225                           aURI, contentType, Report::Error);
   3226    return NS_ERROR_CORRUPTED_CONTENT;
   3227  }
   3228 
   3229  if (internalType == nsIContentPolicy::TYPE_INTERNAL_WORKER ||
   3230      internalType == nsIContentPolicy::TYPE_INTERNAL_SHARED_WORKER) {
   3231    // Do not block the load if the feature is not enabled.
   3232    if (!StaticPrefs::security_block_Worker_with_wrong_mime()) {
   3233      return NS_OK;
   3234    }
   3235 
   3236 #ifdef NIGHTLY_BUILD
   3237    if (StaticPrefs::javascript_options_experimental_wasm_esm_integration()) {
   3238      if (nsContentUtils::HasWasmMimeTypeEssence(typeString)) {
   3239        return NS_OK;
   3240      }
   3241    }
   3242 #endif
   3243 
   3244    ReportMimeTypeMismatch(aChannel, "BlockWorkerWithWrongMimeType", aURI,
   3245                           contentType, Report::Error);
   3246    return NS_ERROR_CORRUPTED_CONTENT;
   3247  }
   3248 
   3249  // ES6 modules require a strict MIME type check.
   3250  if (internalType == nsIContentPolicy::TYPE_INTERNAL_MODULE ||
   3251      internalType == nsIContentPolicy::TYPE_INTERNAL_MODULE_PRELOAD) {
   3252 #ifdef NIGHTLY_BUILD
   3253    if (StaticPrefs::javascript_options_experimental_wasm_esm_integration()) {
   3254      if (nsContentUtils::HasWasmMimeTypeEssence(typeString)) {
   3255        return NS_OK;
   3256      }
   3257    }
   3258 #endif
   3259 
   3260    ReportMimeTypeMismatch(aChannel, "BlockModuleWithWrongMimeType", aURI,
   3261                           contentType, Report::Error);
   3262    return NS_ERROR_CORRUPTED_CONTENT;
   3263  }
   3264 
   3265  return NS_OK;
   3266 }
   3267 
   3268 // Warn when a load of type script uses a wrong MIME type and
   3269 // wasn't blocked by EnsureMIMEOfScript or ProcessXCTO.
   3270 void WarnWrongMIMEOfScript(HttpBaseChannel* aChannel, nsIURI* aURI,
   3271                           nsHttpResponseHead* aResponseHead,
   3272                           nsILoadInfo* aLoadInfo) {
   3273  if (!aURI || !aResponseHead || !aLoadInfo) {
   3274    // If there is no uri, no response head or no loadInfo, then there is
   3275    // nothing to do.
   3276    return;
   3277  }
   3278 
   3279  if (aLoadInfo->GetExternalContentPolicyType() !=
   3280      ExtContentPolicy::TYPE_SCRIPT) {
   3281    // If this is not a script load, then there is nothing to do.
   3282    return;
   3283  }
   3284 
   3285  bool succeeded;
   3286  MOZ_ALWAYS_SUCCEEDS(aChannel->GetRequestSucceeded(&succeeded));
   3287  if (!succeeded) {
   3288    // Do not warn for failed loads: HTTP error pages are usually in HTML.
   3289    return;
   3290  }
   3291 
   3292  nsAutoCString contentType;
   3293  aResponseHead->ContentType(contentType);
   3294  NS_ConvertUTF8toUTF16 typeString(contentType);
   3295 
   3296  if (nsContentUtils::IsJavascriptMIMEType(typeString)) {
   3297    return;
   3298  }
   3299 
   3300 #ifdef NIGHTLY_BUILD
   3301  if (StaticPrefs::javascript_options_experimental_wasm_esm_integration()) {
   3302    if (nsContentUtils::HasWasmMimeTypeEssence(typeString)) {
   3303      return;
   3304    }
   3305  }
   3306 #endif
   3307 
   3308  ReportMimeTypeMismatch(aChannel, "WarnScriptWithWrongMimeType", aURI,
   3309                         contentType, Report::Warning);
   3310 }
   3311 
   3312 nsresult HttpBaseChannel::ValidateMIMEType() {
   3313  nsresult rv = EnsureMIMEOfScript(this, mURI, mResponseHead.get(), mLoadInfo);
   3314  if (NS_FAILED(rv)) {
   3315    return rv;
   3316  }
   3317 
   3318  rv = EnsureMIMEOfJSONModule(this, mURI, mResponseHead.get(), mLoadInfo);
   3319  if (NS_FAILED(rv)) {
   3320    return rv;
   3321  }
   3322 
   3323  rv = ProcessXCTO(this, mURI, mResponseHead.get(), mLoadInfo);
   3324  if (NS_FAILED(rv)) {
   3325    return rv;
   3326  }
   3327 
   3328  WarnWrongMIMEOfScript(this, mURI, mResponseHead.get(), mLoadInfo);
   3329  return NS_OK;
   3330 }
   3331 
   3332 bool HttpBaseChannel::ShouldFilterOpaqueResponse(
   3333    OpaqueResponseFilterFetch aFilterType) const {
   3334  MOZ_ASSERT(ShouldBlockOpaqueResponse());
   3335 
   3336  if (!mLoadInfo || ConfiguredFilterFetchResponseBehaviour() != aFilterType) {
   3337    return false;
   3338  }
   3339 
   3340  // We should filter a response in the parent if it is opaque and is the result
   3341  // of a fetch() function from the Fetch specification.
   3342  return mLoadInfo->InternalContentPolicyType() == nsIContentPolicy::TYPE_FETCH;
   3343 }
   3344 
   3345 bool HttpBaseChannel::ShouldBlockOpaqueResponse() const {
   3346  if (!mURI || !mResponseHead || !mLoadInfo) {
   3347    // if there is no uri, no response head or no loadInfo, then there is
   3348    // nothing to do
   3349    LOGORB("No block: no mURI, mResponseHead, or mLoadInfo");
   3350    return false;
   3351  }
   3352 
   3353  nsCOMPtr<nsIPrincipal> principal = mLoadInfo->GetLoadingPrincipal();
   3354  if (!principal || principal->IsSystemPrincipal()) {
   3355    // If it's a top-level load or a system principal, then there is nothing to
   3356    // do.
   3357    LOGORB("No block: top-level load or system principal");
   3358    return false;
   3359  }
   3360 
   3361  // Check if the response is a opaque response, which means requestMode should
   3362  // be RequestMode::No_cors and responseType should be ResponseType::Opaque.
   3363  nsContentPolicyType contentPolicy = mLoadInfo->InternalContentPolicyType();
   3364 
   3365  // Skip the RequestMode would be RequestMode::Navigate
   3366  if (contentPolicy == nsIContentPolicy::TYPE_DOCUMENT ||
   3367      contentPolicy == nsIContentPolicy::TYPE_SUBDOCUMENT ||
   3368      contentPolicy == nsIContentPolicy::TYPE_INTERNAL_FRAME ||
   3369      contentPolicy == nsIContentPolicy::TYPE_INTERNAL_IFRAME ||
   3370      // Skip the RequestMode would be RequestMode::Same_origin
   3371      contentPolicy == nsIContentPolicy::TYPE_INTERNAL_WORKER ||
   3372      contentPolicy == nsIContentPolicy::TYPE_INTERNAL_SHARED_WORKER) {
   3373    return false;
   3374  }
   3375 
   3376  uint32_t securityMode = mLoadInfo->GetSecurityMode();
   3377  // Skip when RequestMode would not be RequestMode::no_cors
   3378  if (securityMode !=
   3379          nsILoadInfo::SEC_ALLOW_CROSS_ORIGIN_INHERITS_SEC_CONTEXT &&
   3380      securityMode != nsILoadInfo::SEC_ALLOW_CROSS_ORIGIN_SEC_CONTEXT_IS_NULL) {
   3381    LOGORB("No block: not no_cors requests");
   3382    return false;
   3383  }
   3384 
   3385  // Only continue when ResponseType would be ResponseType::Opaque
   3386  if (mLoadInfo->GetTainting() != mozilla::LoadTainting::Opaque) {
   3387    LOGORB("No block: not opaque response");
   3388    return false;
   3389  }
   3390 
   3391  auto extContentPolicyType = mLoadInfo->GetExternalContentPolicyType();
   3392  if (extContentPolicyType == ExtContentPolicy::TYPE_OBJECT ||
   3393      extContentPolicyType == ExtContentPolicy::TYPE_WEBSOCKET ||
   3394      extContentPolicyType == ExtContentPolicy::TYPE_SAVEAS_DOWNLOAD) {
   3395    LOGORB("No block: object || websocket request || save as download");
   3396    return false;
   3397  }
   3398 
   3399  // Ignore the request from object or embed elements
   3400  if (mLoadInfo->GetIsFromObjectOrEmbed()) {
   3401    LOGORB("No block: Request From <object> or <embed>");
   3402    return false;
   3403  }
   3404 
   3405  // Exclude no_cors System XHR
   3406  if (extContentPolicyType == ExtContentPolicy::TYPE_XMLHTTPREQUEST) {
   3407    if (securityMode ==
   3408        nsILoadInfo::SEC_ALLOW_CROSS_ORIGIN_INHERITS_SEC_CONTEXT) {
   3409      LOGORB("No block: System XHR");
   3410      return false;
   3411    }
   3412  }
   3413 
   3414  // Exclude no_cors web-identity
   3415  if (extContentPolicyType == ExtContentPolicy::TYPE_WEB_IDENTITY) {
   3416    if (securityMode ==
   3417        nsILoadInfo::SEC_ALLOW_CROSS_ORIGIN_INHERITS_SEC_CONTEXT) {
   3418      printf("Allowing ORB for web-identity\n");
   3419      LOGORB("No block: System web-identity");
   3420      return false;
   3421    }
   3422  }
   3423 
   3424  uint32_t httpsOnlyStatus = mLoadInfo->GetHttpsOnlyStatus();
   3425  if (httpsOnlyStatus & nsILoadInfo::HTTPS_ONLY_BYPASS_ORB) {
   3426    LOGORB("No block: HTTPS_ONLY_BYPASS_ORB");
   3427    return false;
   3428  }
   3429 
   3430  bool isInDevToolsContext;
   3431  mLoadInfo->GetIsInDevToolsContext(&isInDevToolsContext);
   3432  if (isInDevToolsContext) {
   3433    LOGORB("No block: Request created by devtools");
   3434    return false;
   3435  }
   3436 
   3437  return true;
   3438 }
   3439 
   3440 OpaqueResponse HttpBaseChannel::BlockOrFilterOpaqueResponse(
   3441    OpaqueResponseBlocker* aORB, const nsAString& aReason,
   3442    const OpaqueResponseBlockedTelemetryReason aTelemetryReason,
   3443    const char* aFormat, ...) {
   3444  NimbusFeatures::RecordExposureEvent("opaqueResponseBlocking"_ns, true);
   3445 
   3446  const bool shouldFilter =
   3447      ShouldFilterOpaqueResponse(OpaqueResponseFilterFetch::BlockedByORB);
   3448 
   3449  if (MOZ_UNLIKELY(MOZ_LOG_TEST(GetORBLog(), LogLevel::Debug))) {
   3450    va_list ap;
   3451    va_start(ap, aFormat);
   3452    nsVprintfCString logString(aFormat, ap);
   3453    va_end(ap);
   3454 
   3455    LOGORB("%s: %s", shouldFilter ? "Filtered" : "Blocked", logString.get());
   3456  }
   3457 
   3458  if (shouldFilter) {
   3459    glean::orb::block_initiator
   3460        .EnumGet(glean::orb::BlockInitiatorLabel::eFilteredFetch)
   3461        .Add();
   3462    // The existence of `mORB` depends on `BlockOrFilterOpaqueResponse` being
   3463    // called before or after sniffing has completed.
   3464    // Another requirement is that `OpaqueResponseFilter` must come after
   3465    // `OpaqueResponseBlocker`, which is why in the case of having an
   3466    // `OpaqueResponseBlocker` we let it handle creating an
   3467    // `OpaqueResponseFilter`.
   3468    if (aORB) {
   3469      MOZ_DIAGNOSTIC_ASSERT(!mORB || aORB == mORB);
   3470      aORB->FilterResponse();
   3471    } else {
   3472      mListener = new OpaqueResponseFilter(mListener);
   3473    }
   3474    return OpaqueResponse::Allow;
   3475  }
   3476 
   3477  LogORBError(aReason, aTelemetryReason);
   3478  return OpaqueResponse::Block;
   3479 }
   3480 
   3481 // The specification for ORB is currently being written:
   3482 // https://whatpr.org/fetch/1442.html#orb-algorithm
   3483 // The `opaque-response-safelist check` is implemented in:
   3484 // * `HttpBaseChannel::PerformOpaqueResponseSafelistCheckBeforeSniff`
   3485 // * `nsHttpChannel::DisableIsOpaqueResponseAllowedAfterSniffCheck`
   3486 // * `HttpBaseChannel::PerformOpaqueResponseSafelistCheckAfterSniff`
   3487 // * `OpaqueResponseBlocker::ValidateJavaScript`
   3488 OpaqueResponse
   3489 HttpBaseChannel::PerformOpaqueResponseSafelistCheckBeforeSniff() {
   3490  MOZ_ASSERT(XRE_IsParentProcess());
   3491 
   3492  // https://whatpr.org/fetch/1442.html#http-fetch, step 6.4
   3493  if (!ShouldBlockOpaqueResponse()) {
   3494    return OpaqueResponse::Allow;
   3495  }
   3496 
   3497  // Regardless of if ORB is enabled or not, we check if we should filter the
   3498  // response in the parent. This way data won't reach a content process that
   3499  // will create a filtered `Response` object. This is enabled when
   3500  // 'browser.opaqueResponseBlocking.filterFetchResponse' is
   3501  // `OpaqueResponseFilterFetch::All`.
   3502  // See https://fetch.spec.whatwg.org/#concept-filtered-response-opaque
   3503  if (ShouldFilterOpaqueResponse(OpaqueResponseFilterFetch::All)) {
   3504    mListener = new OpaqueResponseFilter(mListener);
   3505 
   3506    // If we're filtering a response in the parent, there will be no data to
   3507    // determine if it should be blocked or not so the only option we have is to
   3508    // allow it.
   3509    return OpaqueResponse::Allow;
   3510  }
   3511 
   3512  if (!mCachedOpaqueResponseBlockingPref) {
   3513    return OpaqueResponse::Allow;
   3514  }
   3515 
   3516  // If ORB is enabled, we check if we should filter the response in the parent.
   3517  // This way data won't reach a content process that will create a filtered
   3518  // `Response` object. We allow ORB to determine if the response should be
   3519  // blocked or filtered, but regardless no data should reach the content
   3520  // process. This is enabled when
   3521  // 'browser.opaqueResponseBlocking.filterFetchResponse' is
   3522  // `OpaqueResponseFilterFetch::AllowedByORB`.
   3523  // See https://fetch.spec.whatwg.org/#concept-filtered-response-opaque
   3524  if (ShouldFilterOpaqueResponse(OpaqueResponseFilterFetch::AllowedByORB)) {
   3525    mListener = new OpaqueResponseFilter(mListener);
   3526  }
   3527 
   3528  glean::opaque_response_blocking::cross_origin_opaque_response_count.Add(1);
   3529 
   3530  PROFILER_MARKER_TEXT("ORB safelist check", NETWORK, {}, "Before sniff"_ns);
   3531 
   3532  // https://whatpr.org/fetch/1442.html#orb-algorithm
   3533  // Step 1
   3534  nsAutoCString contentType;
   3535  mResponseHead->ContentType(contentType);
   3536 
   3537  // Step 2
   3538  nsAutoCString contentTypeOptionsHeader;
   3539  bool nosniff =
   3540      mResponseHead->GetContentTypeOptionsHeader(contentTypeOptionsHeader) &&
   3541      contentTypeOptionsHeader.EqualsIgnoreCase("nosniff");
   3542 
   3543  // Step 3
   3544  switch (GetOpaqueResponseBlockedReason(contentType, mResponseHead->Status(),
   3545                                         nosniff)) {
   3546    case OpaqueResponseBlockedReason::ALLOWED_SAFE_LISTED:
   3547      // Step 3.1
   3548      return OpaqueResponse::Allow;
   3549    case OpaqueResponseBlockedReason::ALLOWED_SAFE_LISTED_SPEC_BREAKING:
   3550      LOGORB("Allowed %s in a spec breaking way", contentType.get());
   3551      return OpaqueResponse::Allow;
   3552    case OpaqueResponseBlockedReason::BLOCKED_BLOCKLISTED_NEVER_SNIFFED:
   3553      return BlockOrFilterOpaqueResponse(
   3554          mORB, u"mimeType is an opaque-blocklisted-never-sniffed MIME type"_ns,
   3555          OpaqueResponseBlockedTelemetryReason::eMimeNeverSniffed,
   3556          "BLOCKED_BLOCKLISTED_NEVER_SNIFFED");
   3557    case OpaqueResponseBlockedReason::BLOCKED_206_AND_BLOCKLISTED:
   3558      // Step 3.3
   3559      return BlockOrFilterOpaqueResponse(
   3560          mORB,
   3561          u"response's status is 206 and mimeType is an opaque-blocklisted MIME type"_ns,
   3562          OpaqueResponseBlockedTelemetryReason::eResp206Blclisted,
   3563          "BLOCKED_206_AND_BLOCKEDLISTED");
   3564    case OpaqueResponseBlockedReason::
   3565        BLOCKED_NOSNIFF_AND_EITHER_BLOCKLISTED_OR_TEXTPLAIN:
   3566      // Step 3.4
   3567      return BlockOrFilterOpaqueResponse(
   3568          mORB,
   3569          u"nosniff is true and mimeType is an opaque-blocklisted MIME type or its essence is 'text/plain'"_ns,
   3570          OpaqueResponseBlockedTelemetryReason::eNosniffBlcOrTextp,
   3571          "BLOCKED_NOSNIFF_AND_EITHER_BLOCKLISTED_OR_TEXTPLAIN");
   3572    default:
   3573      break;
   3574  }
   3575 
   3576  // Step 4
   3577  // If it's a media subsequent request, we assume that it will only be made
   3578  // after a successful initial request.
   3579  bool isMediaRequest;
   3580  mLoadInfo->GetIsMediaRequest(&isMediaRequest);
   3581  if (isMediaRequest) {
   3582    bool isMediaInitialRequest;
   3583    mLoadInfo->GetIsMediaInitialRequest(&isMediaInitialRequest);
   3584    if (!isMediaInitialRequest) {
   3585      return OpaqueResponse::Allow;
   3586    }
   3587  }
   3588 
   3589  // Step 5
   3590  if (mResponseHead->Status() == 206 &&
   3591      !IsFirstPartialResponse(*mResponseHead)) {
   3592    return BlockOrFilterOpaqueResponse(
   3593        mORB, u"response status is 206 and not first partial response"_ns,
   3594        OpaqueResponseBlockedTelemetryReason::eResp206Blclisted,
   3595        "Is not a valid partial response given 0");
   3596  }
   3597 
   3598  // Setup for steps 6, 7, 8 and 10.
   3599  // Steps 6 and 7 are handled by the sniffer framework.
   3600  // Steps 8 and 10 by are handled by
   3601  // `nsHttpChannel::DisableIsOpaqueResponseAllowedAfterSniffCheck`
   3602  if (mLoadFlags & nsIChannel::LOAD_CALL_CONTENT_SNIFFERS) {
   3603    mSnifferCategoryType = SnifferCategoryType::All;
   3604  } else {
   3605    mSnifferCategoryType = SnifferCategoryType::OpaqueResponseBlocking;
   3606  }
   3607 
   3608  mLoadFlags |= (nsIChannel::LOAD_CALL_CONTENT_SNIFFERS |
   3609                 nsIChannel::LOAD_MEDIA_SNIFFER_OVERRIDES_CONTENT_TYPE);
   3610 
   3611  // Install an input stream listener that performs ORB checks that depend on
   3612  // inspecting the incoming data. It is crucial that `OnStartRequest` is called
   3613  // on this listener either after sniffing is completed or that we skip
   3614  // sniffing, otherwise `OpaqueResponseBlocker` will allow responses that it
   3615  // shouldn't.
   3616  mORB = new OpaqueResponseBlocker(mListener, this, contentType, nosniff);
   3617  mListener = mORB;
   3618 
   3619  nsAutoCString contentEncoding;
   3620  nsresult rv =
   3621      mResponseHead->GetHeader(nsHttp::Content_Encoding, contentEncoding);
   3622 
   3623  if (NS_SUCCEEDED(rv) && !contentEncoding.IsEmpty()) {
   3624    return OpaqueResponse::SniffCompressed;
   3625  }
   3626  mLoadFlags |= (nsIChannel::LOAD_CALL_CONTENT_SNIFFERS |
   3627                 nsIChannel::LOAD_MEDIA_SNIFFER_OVERRIDES_CONTENT_TYPE);
   3628  return OpaqueResponse::Sniff;
   3629 }
   3630 
   3631 // The specification for ORB is currently being written:
   3632 // https://whatpr.org/fetch/1442.html#orb-algorithm
   3633 // The `opaque-response-safelist check` is implemented in:
   3634 // * `HttpBaseChannel::PerformOpaqueResponseSafelistCheckBeforeSniff`
   3635 // * `nsHttpChannel::DisableIsOpaqueResponseAllowedAfterSniffCheck`
   3636 // * `HttpBaseChannel::PerformOpaqueResponseSafelistCheckAfterSniff`
   3637 // * `OpaqueResponseBlocker::ValidateJavaScript`
   3638 OpaqueResponse HttpBaseChannel::PerformOpaqueResponseSafelistCheckAfterSniff(
   3639    const nsACString& aContentType, bool aNoSniff) {
   3640  PROFILER_MARKER_TEXT("ORB safelist check", NETWORK, {}, "After sniff"_ns);
   3641 
   3642  // https://whatpr.org/fetch/1442.html#orb-algorithm
   3643  MOZ_ASSERT(XRE_IsParentProcess());
   3644  MOZ_ASSERT(mCachedOpaqueResponseBlockingPref);
   3645 
   3646  // Step 9
   3647  bool isMediaRequest;
   3648  mLoadInfo->GetIsMediaRequest(&isMediaRequest);
   3649  if (isMediaRequest) {
   3650    return BlockOrFilterOpaqueResponse(
   3651        mORB, u"after sniff: media request"_ns,
   3652        OpaqueResponseBlockedTelemetryReason::eAfterSniffMedia,
   3653        "media request");
   3654  }
   3655 
   3656  // Step 11
   3657  if (aNoSniff) {
   3658    return BlockOrFilterOpaqueResponse(
   3659        mORB, u"after sniff: nosniff is true"_ns,
   3660        OpaqueResponseBlockedTelemetryReason::eAfterSniffNosniff, "nosniff");
   3661  }
   3662 
   3663  // Step 12
   3664  if (mResponseHead &&
   3665      (mResponseHead->Status() < 200 || mResponseHead->Status() > 299)) {
   3666    return BlockOrFilterOpaqueResponse(
   3667        mORB, u"after sniff: status code is not in allowed range"_ns,
   3668        OpaqueResponseBlockedTelemetryReason::eAfterSniffStaCode,
   3669        "status code (%d) is not allowed", mResponseHead->Status());
   3670  }
   3671 
   3672  // Step 13
   3673  if (!mResponseHead || aContentType.IsEmpty()) {
   3674    LOGORB("Allowed: mimeType is failure");
   3675    return OpaqueResponse::Allow;
   3676  }
   3677 
   3678  // Step 14
   3679  if (StringBeginsWith(aContentType, "image/"_ns) ||
   3680      StringBeginsWith(aContentType, "video/"_ns) ||
   3681      StringBeginsWith(aContentType, "audio/"_ns)) {
   3682    return BlockOrFilterOpaqueResponse(
   3683        mORB,
   3684        u"after sniff: content-type declares image/video/audio, but sniffing fails"_ns,
   3685        OpaqueResponseBlockedTelemetryReason::eAfterSniffCtFail,
   3686        "ContentType is image/video/audio");
   3687  }
   3688 
   3689  return OpaqueResponse::Sniff;
   3690 }
   3691 
   3692 bool HttpBaseChannel::NeedOpaqueResponseAllowedCheckAfterSniff() const {
   3693  return mORB ? mORB->IsSniffing() : false;
   3694 }
   3695 
   3696 void HttpBaseChannel::BlockOpaqueResponseAfterSniff(
   3697    const nsAString& aReason,
   3698    const OpaqueResponseBlockedTelemetryReason aTelemetryReason) {
   3699  MOZ_DIAGNOSTIC_ASSERT(mORB);
   3700  LogORBError(aReason, aTelemetryReason);
   3701  mORB->BlockResponse(this, NS_BINDING_ABORTED);
   3702 }
   3703 
   3704 void HttpBaseChannel::AllowOpaqueResponseAfterSniff() {
   3705  MOZ_DIAGNOSTIC_ASSERT(mORB);
   3706  mORB->AllowResponse();
   3707 }
   3708 
   3709 void HttpBaseChannel::SetChannelBlockedByOpaqueResponse() {
   3710  mChannelBlockedByOpaqueResponse = true;
   3711 
   3712  RefPtr<dom::BrowsingContext> browsingContext =
   3713      dom::BrowsingContext::GetCurrentTopByBrowserId(mBrowserId);
   3714  if (!browsingContext) {
   3715    return;
   3716  }
   3717 
   3718  dom::WindowContext* windowContext = browsingContext->GetTopWindowContext();
   3719  if (windowContext) {
   3720    windowContext->Canonical()->SetShouldReportHasBlockedOpaqueResponse(
   3721        mLoadInfo->InternalContentPolicyType());
   3722  }
   3723 }
   3724 
   3725 NS_IMETHODIMP
   3726 HttpBaseChannel::SetCookieHeaders(const nsTArray<nsCString>& aCookieHeaders) {
   3727  if (mLoadFlags & LOAD_ANONYMOUS) return NS_OK;
   3728 
   3729  // The loadGroup of the channel in the parent process could be null in the
   3730  // XPCShell content process test, see test_cookiejars_wrap.js. In this case,
   3731  // we cannot explicitly set the loadGroup for the parent channel because it's
   3732  // created from the content process. To workaround this, we add a testing pref
   3733  // to skip this check.
   3734  if (!StaticPrefs::
   3735          network_cookie_skip_browsing_context_check_in_parent_for_testing() &&
   3736      IsBrowsingContextDiscarded()) {
   3737    return NS_OK;
   3738  }
   3739 
   3740  // empty header isn't an error
   3741  if (aCookieHeaders.IsEmpty()) {
   3742    return NS_OK;
   3743  }
   3744 
   3745  nsICookieService* cs = gHttpHandler->GetCookieService();
   3746  NS_ENSURE_TRUE(cs, NS_ERROR_FAILURE);
   3747 
   3748  for (const nsCString& cookieHeader : aCookieHeaders) {
   3749    nsresult rv = cs->SetCookieStringFromHttp(mURI, cookieHeader, this);
   3750    NS_ENSURE_SUCCESS(rv, rv);
   3751  }
   3752 
   3753  return NS_OK;
   3754 }
   3755 
   3756 NS_IMETHODIMP
   3757 HttpBaseChannel::GetThirdPartyFlags(uint32_t* aFlags) {
   3758  *aFlags = LoadThirdPartyFlags();
   3759  return NS_OK;
   3760 }
   3761 
   3762 NS_IMETHODIMP
   3763 HttpBaseChannel::SetThirdPartyFlags(uint32_t aFlags) {
   3764  ENSURE_CALLED_BEFORE_ASYNC_OPEN();
   3765 
   3766  StoreThirdPartyFlags(aFlags);
   3767  return NS_OK;
   3768 }
   3769 
   3770 NS_IMETHODIMP
   3771 HttpBaseChannel::GetForceAllowThirdPartyCookie(bool* aForce) {
   3772  *aForce = !!(LoadThirdPartyFlags() &
   3773               nsIHttpChannelInternal::THIRD_PARTY_FORCE_ALLOW);
   3774  return NS_OK;
   3775 }
   3776 
   3777 NS_IMETHODIMP
   3778 HttpBaseChannel::SetForceAllowThirdPartyCookie(bool aForce) {
   3779  ENSURE_CALLED_BEFORE_ASYNC_OPEN();
   3780 
   3781  if (aForce) {
   3782    StoreThirdPartyFlags(LoadThirdPartyFlags() |
   3783                         nsIHttpChannelInternal::THIRD_PARTY_FORCE_ALLOW);
   3784  } else {
   3785    StoreThirdPartyFlags(LoadThirdPartyFlags() &
   3786                         ~nsIHttpChannelInternal::THIRD_PARTY_FORCE_ALLOW);
   3787  }
   3788 
   3789  return NS_OK;
   3790 }
   3791 
   3792 NS_IMETHODIMP
   3793 HttpBaseChannel::GetCanceled(bool* aCanceled) {
   3794  *aCanceled = mCanceled;
   3795  return NS_OK;
   3796 }
   3797 
   3798 NS_IMETHODIMP
   3799 HttpBaseChannel::GetChannelIsForDownload(bool* aChannelIsForDownload) {
   3800  *aChannelIsForDownload = LoadChannelIsForDownload();
   3801  return NS_OK;
   3802 }
   3803 
   3804 NS_IMETHODIMP
   3805 HttpBaseChannel::SetChannelIsForDownload(bool aChannelIsForDownload) {
   3806  StoreChannelIsForDownload(aChannelIsForDownload);
   3807  return NS_OK;
   3808 }
   3809 
   3810 NS_IMETHODIMP
   3811 HttpBaseChannel::SetCacheKeysRedirectChain(nsTArray<nsCString>* cacheKeys) {
   3812  auto RedirectedCachekeys = mRedirectedCachekeys.Lock();
   3813  auto& ref = RedirectedCachekeys.ref();
   3814  ref = WrapUnique(cacheKeys);
   3815  return NS_OK;
   3816 }
   3817 
   3818 NS_IMETHODIMP
   3819 HttpBaseChannel::GetLocalAddress(nsACString& addr) {
   3820  if (mSelfAddr.raw.family == PR_AF_UNSPEC) return NS_ERROR_NOT_AVAILABLE;
   3821 
   3822  addr.SetLength(kIPv6CStrBufSize);
   3823  mSelfAddr.ToStringBuffer(addr.BeginWriting(), kIPv6CStrBufSize);
   3824  addr.SetLength(strlen(addr.BeginReading()));
   3825 
   3826  return NS_OK;
   3827 }
   3828 
   3829 NS_IMETHODIMP
   3830 HttpBaseChannel::TakeAllSecurityMessages(
   3831    nsCOMArray<nsISecurityConsoleMessage>& aMessages) {
   3832  MOZ_ASSERT(NS_IsMainThread());
   3833 
   3834  aMessages.Clear();
   3835  for (const auto& pair : mSecurityConsoleMessages) {
   3836    nsresult rv;
   3837    nsCOMPtr<nsISecurityConsoleMessage> message =
   3838        do_CreateInstance(NS_SECURITY_CONSOLE_MESSAGE_CONTRACTID, &rv);
   3839    NS_ENSURE_SUCCESS(rv, rv);
   3840 
   3841    message->SetTag(pair.first);
   3842    message->SetCategory(pair.second);
   3843    aMessages.AppendElement(message);
   3844  }
   3845 
   3846  MOZ_ASSERT(mSecurityConsoleMessages.Length() == aMessages.Length());
   3847  mSecurityConsoleMessages.Clear();
   3848 
   3849  return NS_OK;
   3850 }
   3851 
   3852 /* Please use this method with care. This can cause the message
   3853 * queue to grow large and cause the channel to take up a lot
   3854 * of memory. Use only static string messages and do not add
   3855 * server side data to the queue, as that can be large.
   3856 * Add only a limited number of messages to the queue to keep
   3857 * the channel size down and do so only in rare erroneous situations.
   3858 * More information can be found here:
   3859 * https://bugzilla.mozilla.org/show_bug.cgi?id=846918
   3860 */
   3861 nsresult HttpBaseChannel::AddSecurityMessage(
   3862    const nsAString& aMessageTag, const nsAString& aMessageCategory) {
   3863  MOZ_ASSERT(NS_IsMainThread());
   3864 
   3865  nsresult rv;
   3866 
   3867  // nsSecurityConsoleMessage is not thread-safe refcounted.
   3868  // Delay the object construction until requested.
   3869  // See TakeAllSecurityMessages()
   3870  std::pair<nsString, nsString> pair(aMessageTag, aMessageCategory);
   3871  mSecurityConsoleMessages.AppendElement(std::move(pair));
   3872 
   3873  nsCOMPtr<nsIConsoleService> console(
   3874      do_GetService(NS_CONSOLESERVICE_CONTRACTID));
   3875  if (!console) {
   3876    return NS_ERROR_FAILURE;
   3877  }
   3878 
   3879  nsCOMPtr<nsILoadInfo> loadInfo = LoadInfo();
   3880 
   3881  auto innerWindowID = loadInfo->GetInnerWindowID();
   3882 
   3883  nsAutoString errorText;
   3884  rv = nsContentUtils::GetLocalizedString(
   3885      nsContentUtils::eSECURITY_PROPERTIES,
   3886      NS_ConvertUTF16toUTF8(aMessageTag).get(), errorText);
   3887  NS_ENSURE_SUCCESS(rv, rv);
   3888 
   3889  nsCOMPtr<nsIScriptError> error(do_CreateInstance(NS_SCRIPTERROR_CONTRACTID));
   3890  error->InitWithSourceURI(errorText, mURI, 0, 0, nsIScriptError::warningFlag,
   3891                           NS_ConvertUTF16toUTF8(aMessageCategory),
   3892                           innerWindowID);
   3893 
   3894  console->LogMessage(error);
   3895 
   3896  return NS_OK;
   3897 }
   3898 
   3899 NS_IMETHODIMP
   3900 HttpBaseChannel::GetLocalPort(int32_t* port) {
   3901  NS_ENSURE_ARG_POINTER(port);
   3902 
   3903  if (mSelfAddr.raw.family == PR_AF_INET) {
   3904    *port = (int32_t)ntohs(mSelfAddr.inet.port);
   3905  } else if (mSelfAddr.raw.family == PR_AF_INET6) {
   3906    *port = (int32_t)ntohs(mSelfAddr.inet6.port);
   3907  } else {
   3908    return NS_ERROR_NOT_AVAILABLE;
   3909  }
   3910 
   3911  return NS_OK;
   3912 }
   3913 
   3914 NS_IMETHODIMP
   3915 HttpBaseChannel::GetRemoteAddress(nsACString& addr) {
   3916  if (mPeerAddr.raw.family == PR_AF_UNSPEC) return NS_ERROR_NOT_AVAILABLE;
   3917 
   3918  addr.SetLength(kIPv6CStrBufSize);
   3919  mPeerAddr.ToStringBuffer(addr.BeginWriting(), kIPv6CStrBufSize);
   3920  addr.SetLength(strlen(addr.BeginReading()));
   3921 
   3922  return NS_OK;
   3923 }
   3924 
   3925 NS_IMETHODIMP
   3926 HttpBaseChannel::GetRemotePort(int32_t* port) {
   3927  NS_ENSURE_ARG_POINTER(port);
   3928 
   3929  if (mPeerAddr.raw.family == PR_AF_INET) {
   3930    *port = (int32_t)ntohs(mPeerAddr.inet.port);
   3931  } else if (mPeerAddr.raw.family == PR_AF_INET6) {
   3932    *port = (int32_t)ntohs(mPeerAddr.inet6.port);
   3933  } else {
   3934    return NS_ERROR_NOT_AVAILABLE;
   3935  }
   3936 
   3937  return NS_OK;
   3938 }
   3939 
   3940 NS_IMETHODIMP
   3941 HttpBaseChannel::HTTPUpgrade(const nsACString& aProtocolName,
   3942                             nsIHttpUpgradeListener* aListener) {
   3943  NS_ENSURE_ARG(!aProtocolName.IsEmpty());
   3944  NS_ENSURE_ARG_POINTER(aListener);
   3945 
   3946  mUpgradeProtocol = aProtocolName;
   3947  mUpgradeProtocolCallback = aListener;
   3948  return NS_OK;
   3949 }
   3950 
   3951 NS_IMETHODIMP
   3952 HttpBaseChannel::GetOnlyConnect(bool* aOnlyConnect) {
   3953  NS_ENSURE_ARG_POINTER(aOnlyConnect);
   3954 
   3955  *aOnlyConnect = mCaps & NS_HTTP_CONNECT_ONLY;
   3956  return NS_OK;
   3957 }
   3958 
   3959 NS_IMETHODIMP
   3960 HttpBaseChannel::SetConnectOnly(bool aTlsTunnel) {
   3961  ENSURE_CALLED_BEFORE_CONNECT();
   3962 
   3963  if (!mUpgradeProtocolCallback) {
   3964    return NS_ERROR_FAILURE;
   3965  }
   3966 
   3967  mCaps |= NS_HTTP_CONNECT_ONLY;
   3968  if (aTlsTunnel) {
   3969    mCaps |= NS_HTTP_TLS_TUNNEL;
   3970  }
   3971  mProxyResolveFlags = nsIProtocolProxyService::RESOLVE_PREFER_HTTPS_PROXY |
   3972                       nsIProtocolProxyService::RESOLVE_ALWAYS_TUNNEL;
   3973  return SetLoadFlags(nsIRequest::INHIBIT_CACHING | nsIChannel::LOAD_ANONYMOUS |
   3974                      nsIRequest::LOAD_BYPASS_CACHE |
   3975                      nsIChannel::LOAD_BYPASS_SERVICE_WORKER);
   3976 }
   3977 
   3978 NS_IMETHODIMP
   3979 HttpBaseChannel::GetAllowSpdy(bool* aAllowSpdy) {
   3980  NS_ENSURE_ARG_POINTER(aAllowSpdy);
   3981 
   3982  *aAllowSpdy = LoadAllowSpdy();
   3983  return NS_OK;
   3984 }
   3985 
   3986 NS_IMETHODIMP
   3987 HttpBaseChannel::SetAllowSpdy(bool aAllowSpdy) {
   3988  StoreAllowSpdy(aAllowSpdy);
   3989  return NS_OK;
   3990 }
   3991 
   3992 NS_IMETHODIMP
   3993 HttpBaseChannel::GetAllowHttp3(bool* aAllowHttp3) {
   3994  NS_ENSURE_ARG_POINTER(aAllowHttp3);
   3995 
   3996  *aAllowHttp3 = LoadAllowHttp3();
   3997  return NS_OK;
   3998 }
   3999 
   4000 NS_IMETHODIMP
   4001 HttpBaseChannel::SetAllowHttp3(bool aAllowHttp3) {
   4002  StoreAllowHttp3(aAllowHttp3);
   4003  return NS_OK;
   4004 }
   4005 
   4006 NS_IMETHODIMP
   4007 HttpBaseChannel::GetAllowAltSvc(bool* aAllowAltSvc) {
   4008  NS_ENSURE_ARG_POINTER(aAllowAltSvc);
   4009 
   4010  *aAllowAltSvc = LoadAllowAltSvc();
   4011  return NS_OK;
   4012 }
   4013 
   4014 NS_IMETHODIMP
   4015 HttpBaseChannel::SetAllowAltSvc(bool aAllowAltSvc) {
   4016  StoreAllowAltSvc(aAllowAltSvc);
   4017  return NS_OK;
   4018 }
   4019 
   4020 NS_IMETHODIMP
   4021 HttpBaseChannel::GetBeConservative(bool* aBeConservative) {
   4022  NS_ENSURE_ARG_POINTER(aBeConservative);
   4023 
   4024  *aBeConservative = LoadBeConservative();
   4025  return NS_OK;
   4026 }
   4027 
   4028 NS_IMETHODIMP
   4029 HttpBaseChannel::SetBeConservative(bool aBeConservative) {
   4030  StoreBeConservative(aBeConservative);
   4031  return NS_OK;
   4032 }
   4033 
   4034 bool HttpBaseChannel::BypassProxy() {
   4035  return StaticPrefs::network_proxy_allow_bypass() && LoadBypassProxy();
   4036 }
   4037 
   4038 NS_IMETHODIMP
   4039 HttpBaseChannel::GetBypassProxy(bool* aBypassProxy) {
   4040  NS_ENSURE_ARG_POINTER(aBypassProxy);
   4041 
   4042  *aBypassProxy = BypassProxy();
   4043  return NS_OK;
   4044 }
   4045 
   4046 NS_IMETHODIMP
   4047 HttpBaseChannel::SetBypassProxy(bool aBypassProxy) {
   4048  if (StaticPrefs::network_proxy_allow_bypass()) {
   4049    StoreBypassProxy(aBypassProxy);
   4050  } else {
   4051    NS_WARNING("bypassProxy set but network.proxy.allow_bypass is disabled");
   4052    return NS_ERROR_FAILURE;
   4053  }
   4054  return NS_OK;
   4055 }
   4056 
   4057 NS_IMETHODIMP
   4058 HttpBaseChannel::GetIsTRRServiceChannel(bool* aIsTRRServiceChannel) {
   4059  NS_ENSURE_ARG_POINTER(aIsTRRServiceChannel);
   4060 
   4061  *aIsTRRServiceChannel = LoadIsTRRServiceChannel();
   4062  return NS_OK;
   4063 }
   4064 
   4065 NS_IMETHODIMP
   4066 HttpBaseChannel::SetIsTRRServiceChannel(bool aIsTRRServiceChannel) {
   4067  StoreIsTRRServiceChannel(aIsTRRServiceChannel);
   4068  return NS_OK;
   4069 }
   4070 
   4071 NS_IMETHODIMP
   4072 HttpBaseChannel::GetIsResolvedByTRR(bool* aResolvedByTRR) {
   4073  NS_ENSURE_ARG_POINTER(aResolvedByTRR);
   4074  *aResolvedByTRR = LoadResolvedByTRR();
   4075  return NS_OK;
   4076 }
   4077 
   4078 NS_IMETHODIMP
   4079 HttpBaseChannel::GetEffectiveTRRMode(nsIRequest::TRRMode* aEffectiveTRRMode) {
   4080  *aEffectiveTRRMode = mEffectiveTRRMode;
   4081  return NS_OK;
   4082 }
   4083 
   4084 NS_IMETHODIMP
   4085 HttpBaseChannel::GetTrrSkipReason(nsITRRSkipReason::value* aTrrSkipReason) {
   4086  *aTrrSkipReason = mTRRSkipReason;
   4087  return NS_OK;
   4088 }
   4089 
   4090 NS_IMETHODIMP
   4091 HttpBaseChannel::GetIsLoadedBySocketProcess(bool* aResult) {
   4092  NS_ENSURE_ARG_POINTER(aResult);
   4093  *aResult = LoadLoadedBySocketProcess();
   4094  return NS_OK;
   4095 }
   4096 
   4097 NS_IMETHODIMP
   4098 HttpBaseChannel::GetTlsFlags(uint32_t* aTlsFlags) {
   4099  NS_ENSURE_ARG_POINTER(aTlsFlags);
   4100 
   4101  *aTlsFlags = mTlsFlags;
   4102  return NS_OK;
   4103 }
   4104 
   4105 NS_IMETHODIMP
   4106 HttpBaseChannel::SetTlsFlags(uint32_t aTlsFlags) {
   4107  mTlsFlags = aTlsFlags;
   4108  return NS_OK;
   4109 }
   4110 
   4111 NS_IMETHODIMP
   4112 HttpBaseChannel::GetApiRedirectToURI(nsIURI** aResult) {
   4113  if (!mAPIRedirectTo) {
   4114    return NS_ERROR_NOT_AVAILABLE;
   4115  }
   4116  NS_ENSURE_ARG_POINTER(aResult);
   4117  *aResult = do_AddRef(mAPIRedirectTo->first()).take();
   4118  return NS_OK;
   4119 }
   4120 
   4121 NS_IMETHODIMP
   4122 HttpBaseChannel::GetResponseTimeoutEnabled(bool* aEnable) {
   4123  if (NS_WARN_IF(!aEnable)) {
   4124    return NS_ERROR_NULL_POINTER;
   4125  }
   4126  *aEnable = LoadResponseTimeoutEnabled();
   4127  return NS_OK;
   4128 }
   4129 
   4130 NS_IMETHODIMP
   4131 HttpBaseChannel::SetResponseTimeoutEnabled(bool aEnable) {
   4132  StoreResponseTimeoutEnabled(aEnable);
   4133  return NS_OK;
   4134 }
   4135 
   4136 NS_IMETHODIMP
   4137 HttpBaseChannel::GetInitialRwin(uint32_t* aRwin) {
   4138  if (NS_WARN_IF(!aRwin)) {
   4139    return NS_ERROR_NULL_POINTER;
   4140  }
   4141  *aRwin = mInitialRwin;
   4142  return NS_OK;
   4143 }
   4144 
   4145 NS_IMETHODIMP
   4146 HttpBaseChannel::SetInitialRwin(uint32_t aRwin) {
   4147  ENSURE_CALLED_BEFORE_CONNECT();
   4148  mInitialRwin = aRwin;
   4149  return NS_OK;
   4150 }
   4151 
   4152 NS_IMETHODIMP
   4153 HttpBaseChannel::ForcePending(bool aForcePending) {
   4154  StoreForcePending(aForcePending);
   4155  return NS_OK;
   4156 }
   4157 
   4158 NS_IMETHODIMP
   4159 HttpBaseChannel::GetLastModifiedTime(PRTime* lastModifiedTime) {
   4160  if (!mResponseHead) return NS_ERROR_NOT_AVAILABLE;
   4161  uint32_t lastMod;
   4162  nsresult rv = mResponseHead->GetLastModifiedValue(&lastMod);
   4163  NS_ENSURE_SUCCESS(rv, rv);
   4164  *lastModifiedTime = lastMod;
   4165  return NS_OK;
   4166 }
   4167 
   4168 NS_IMETHODIMP
   4169 HttpBaseChannel::GetCorsIncludeCredentials(bool* aInclude) {
   4170  *aInclude = LoadCorsIncludeCredentials();
   4171  return NS_OK;
   4172 }
   4173 
   4174 NS_IMETHODIMP
   4175 HttpBaseChannel::SetCorsIncludeCredentials(bool aInclude) {
   4176  StoreCorsIncludeCredentials(aInclude);
   4177  return NS_OK;
   4178 }
   4179 
   4180 NS_IMETHODIMP
   4181 HttpBaseChannel::GetRequestMode(RequestMode* aMode) {
   4182  *aMode = mRequestMode;
   4183  return NS_OK;
   4184 }
   4185 
   4186 NS_IMETHODIMP
   4187 HttpBaseChannel::SetRequestMode(RequestMode aMode) {
   4188  mRequestMode = aMode;
   4189  return NS_OK;
   4190 }
   4191 
   4192 NS_IMETHODIMP
   4193 HttpBaseChannel::GetRedirectMode(uint32_t* aMode) {
   4194  *aMode = mRedirectMode;
   4195  return NS_OK;
   4196 }
   4197 
   4198 NS_IMETHODIMP
   4199 HttpBaseChannel::SetRedirectMode(uint32_t aMode) {
   4200  mRedirectMode = aMode;
   4201  return NS_OK;
   4202 }
   4203 
   4204 namespace {
   4205 
   4206 bool ContainsAllFlags(uint32_t aLoadFlags, uint32_t aMask) {
   4207  return (aLoadFlags & aMask) == aMask;
   4208 }
   4209 
   4210 }  // anonymous namespace
   4211 
   4212 NS_IMETHODIMP
   4213 HttpBaseChannel::GetFetchCacheMode(uint32_t* aFetchCacheMode) {
   4214  NS_ENSURE_ARG_POINTER(aFetchCacheMode);
   4215 
   4216  // Otherwise try to guess an appropriate cache mode from the load flags.
   4217  if (ContainsAllFlags(mLoadFlags, INHIBIT_CACHING | LOAD_BYPASS_CACHE)) {
   4218    *aFetchCacheMode = nsIHttpChannelInternal::FETCH_CACHE_MODE_NO_STORE;
   4219  } else if (ContainsAllFlags(mLoadFlags, LOAD_BYPASS_CACHE)) {
   4220    *aFetchCacheMode = nsIHttpChannelInternal::FETCH_CACHE_MODE_RELOAD;
   4221  } else if (ContainsAllFlags(mLoadFlags, VALIDATE_ALWAYS) ||
   4222             LoadForceValidateCacheContent()) {
   4223    *aFetchCacheMode = nsIHttpChannelInternal::FETCH_CACHE_MODE_NO_CACHE;
   4224  } else if (ContainsAllFlags(
   4225                 mLoadFlags,
   4226                 VALIDATE_NEVER | nsICachingChannel::LOAD_ONLY_FROM_CACHE)) {
   4227    *aFetchCacheMode = nsIHttpChannelInternal::FETCH_CACHE_MODE_ONLY_IF_CACHED;
   4228  } else if (ContainsAllFlags(mLoadFlags, VALIDATE_NEVER)) {
   4229    *aFetchCacheMode = nsIHttpChannelInternal::FETCH_CACHE_MODE_FORCE_CACHE;
   4230  } else {
   4231    *aFetchCacheMode = nsIHttpChannelInternal::FETCH_CACHE_MODE_DEFAULT;
   4232  }
   4233 
   4234  return NS_OK;
   4235 }
   4236 
   4237 namespace {
   4238 
   4239 void SetCacheFlags(uint32_t& aLoadFlags, uint32_t aFlags) {
   4240  // First, clear any possible cache related flags.
   4241  uint32_t allPossibleFlags =
   4242      nsIRequest::INHIBIT_CACHING | nsIRequest::LOAD_BYPASS_CACHE |
   4243      nsIRequest::VALIDATE_ALWAYS | nsIRequest::LOAD_FROM_CACHE |
   4244      nsICachingChannel::LOAD_ONLY_FROM_CACHE;
   4245  aLoadFlags &= ~allPossibleFlags;
   4246 
   4247  // Then set the new flags.
   4248  aLoadFlags |= aFlags;
   4249 }
   4250 
   4251 }  // anonymous namespace
   4252 
   4253 NS_IMETHODIMP
   4254 HttpBaseChannel::SetFetchCacheMode(uint32_t aFetchCacheMode) {
   4255  ENSURE_CALLED_BEFORE_CONNECT();
   4256 
   4257  // Now, set the load flags that implement each cache mode.
   4258  switch (aFetchCacheMode) {
   4259    case nsIHttpChannelInternal::FETCH_CACHE_MODE_DEFAULT:
   4260      // The "default" mode means to use the http cache normally and
   4261      // respect any http cache-control headers.  We effectively want
   4262      // to clear our cache related load flags.
   4263      SetCacheFlags(mLoadFlags, 0);
   4264      break;
   4265    case nsIHttpChannelInternal::FETCH_CACHE_MODE_NO_STORE:
   4266      // no-store means don't consult the cache on the way to the network, and
   4267      // don't store the response in the cache even if it's cacheable.
   4268      SetCacheFlags(mLoadFlags, INHIBIT_CACHING | LOAD_BYPASS_CACHE);
   4269      break;
   4270    case nsIHttpChannelInternal::FETCH_CACHE_MODE_RELOAD:
   4271      // reload means don't consult the cache on the way to the network, but
   4272      // do store the response in the cache if possible.
   4273      SetCacheFlags(mLoadFlags, LOAD_BYPASS_CACHE);
   4274      break;
   4275    case nsIHttpChannelInternal::FETCH_CACHE_MODE_NO_CACHE:
   4276      // no-cache means always validate what's in the cache.
   4277      SetCacheFlags(mLoadFlags, VALIDATE_ALWAYS);
   4278      break;
   4279    case nsIHttpChannelInternal::FETCH_CACHE_MODE_FORCE_CACHE:
   4280      // force-cache means don't validate unless if the response would vary.
   4281      SetCacheFlags(mLoadFlags, VALIDATE_NEVER);
   4282      break;
   4283    case nsIHttpChannelInternal::FETCH_CACHE_MODE_ONLY_IF_CACHED:
   4284      // only-if-cached means only from cache, no network, no validation,
   4285      // generate a network error if the document was't in the cache. The
   4286      // privacy implications of these flags (making it fast/easy to check if
   4287      // the user has things in their cache without any network traffic side
   4288      // effects) are addressed in the Request constructor which
   4289      // enforces/requires same-origin request mode.
   4290      SetCacheFlags(mLoadFlags,
   4291                    VALIDATE_NEVER | nsICachingChannel::LOAD_ONLY_FROM_CACHE);
   4292      break;
   4293  }
   4294 
   4295 #ifdef MOZ_DIAGNOSTIC_ASSERT_ENABLED
   4296  uint32_t finalMode = 0;
   4297  MOZ_ALWAYS_SUCCEEDS(GetFetchCacheMode(&finalMode));
   4298  MOZ_DIAGNOSTIC_ASSERT(finalMode == aFetchCacheMode);
   4299 #endif  // MOZ_DIAGNOSTIC_ASSERT_ENABLED
   4300 
   4301  return NS_OK;
   4302 }
   4303 
   4304 //-----------------------------------------------------------------------------
   4305 // HttpBaseChannel::nsISupportsPriority
   4306 //-----------------------------------------------------------------------------
   4307 
   4308 NS_IMETHODIMP
   4309 HttpBaseChannel::GetPriority(int32_t* value) {
   4310  *value = mPriority;
   4311  return NS_OK;
   4312 }
   4313 
   4314 NS_IMETHODIMP
   4315 HttpBaseChannel::AdjustPriority(int32_t delta) {
   4316  return SetPriority(mPriority + delta);
   4317 }
   4318 
   4319 //-----------------------------------------------------------------------------
   4320 // HttpBaseChannel::nsIResumableChannel
   4321 //-----------------------------------------------------------------------------
   4322 
   4323 NS_IMETHODIMP
   4324 HttpBaseChannel::GetEntityID(nsACString& aEntityID) {
   4325  // Don't return an entity ID for Non-GET requests which require
   4326  // additional data
   4327  if (!mRequestHead.IsGet()) {
   4328    return NS_ERROR_NOT_RESUMABLE;
   4329  }
   4330 
   4331  uint64_t size = UINT64_MAX;
   4332  nsAutoCString etag, lastmod;
   4333  if (mResponseHead) {
   4334    // Don't return an entity if the server sent the following header:
   4335    // Accept-Ranges: none
   4336    // Not sending the Accept-Ranges header means we can still try
   4337    // sending range requests.
   4338    nsAutoCString acceptRanges;
   4339    (void)mResponseHead->GetHeader(nsHttp::Accept_Ranges, acceptRanges);
   4340    if (!acceptRanges.IsEmpty() &&
   4341        !nsHttp::FindToken(acceptRanges.get(), "bytes",
   4342                           HTTP_HEADER_VALUE_SEPS)) {
   4343      return NS_ERROR_NOT_RESUMABLE;
   4344    }
   4345 
   4346    size = mResponseHead->TotalEntitySize();
   4347    (void)mResponseHead->GetHeader(nsHttp::Last_Modified, lastmod);
   4348    (void)mResponseHead->GetHeader(nsHttp::ETag, etag);
   4349  }
   4350  nsCString entityID;
   4351  NS_EscapeURL(etag.BeginReading(), etag.Length(),
   4352               esc_AlwaysCopy | esc_FileBaseName | esc_Forced, entityID);
   4353  entityID.Append('/');
   4354  entityID.AppendInt(int64_t(size));
   4355  entityID.Append('/');
   4356  entityID.Append(lastmod);
   4357  // NOTE: Appending lastmod as the last part avoids having to escape it
   4358 
   4359  aEntityID = entityID;
   4360 
   4361  return NS_OK;
   4362 }
   4363 
   4364 //-----------------------------------------------------------------------------
   4365 // HttpBaseChannel::nsIConsoleReportCollector
   4366 //-----------------------------------------------------------------------------
   4367 
   4368 void HttpBaseChannel::AddConsoleReport(
   4369    uint32_t aErrorFlags, const nsACString& aCategory,
   4370    nsContentUtils::PropertiesFile aPropertiesFile,
   4371    const nsACString& aSourceFileURI, uint32_t aLineNumber,
   4372    uint32_t aColumnNumber, const nsACString& aMessageName,
   4373    const nsTArray<nsString>& aStringParams) {
   4374  mReportCollector->AddConsoleReport(aErrorFlags, aCategory, aPropertiesFile,
   4375                                     aSourceFileURI, aLineNumber, aColumnNumber,
   4376                                     aMessageName, aStringParams);
   4377 
   4378  // If this channel is already part of a loadGroup, we can flush this console
   4379  // report immediately.
   4380  HttpBaseChannel::MaybeFlushConsoleReports();
   4381 }
   4382 
   4383 void HttpBaseChannel::FlushReportsToConsole(uint64_t aInnerWindowID,
   4384                                            ReportAction aAction) {
   4385  mReportCollector->FlushReportsToConsole(aInnerWindowID, aAction);
   4386 }
   4387 
   4388 void HttpBaseChannel::FlushReportsToConsoleForServiceWorkerScope(
   4389    const nsACString& aScope, ReportAction aAction) {
   4390  mReportCollector->FlushReportsToConsoleForServiceWorkerScope(aScope, aAction);
   4391 }
   4392 
   4393 void HttpBaseChannel::FlushConsoleReports(dom::Document* aDocument,
   4394                                          ReportAction aAction) {
   4395  mReportCollector->FlushConsoleReports(aDocument, aAction);
   4396 }
   4397 
   4398 void HttpBaseChannel::FlushConsoleReports(nsILoadGroup* aLoadGroup,
   4399                                          ReportAction aAction) {
   4400  mReportCollector->FlushConsoleReports(aLoadGroup, aAction);
   4401 }
   4402 
   4403 void HttpBaseChannel::FlushConsoleReports(
   4404    nsIConsoleReportCollector* aCollector) {
   4405  mReportCollector->FlushConsoleReports(aCollector);
   4406 }
   4407 
   4408 void HttpBaseChannel::StealConsoleReports(
   4409    nsTArray<net::ConsoleReportCollected>& aReports) {
   4410  mReportCollector->StealConsoleReports(aReports);
   4411 }
   4412 
   4413 void HttpBaseChannel::ClearConsoleReports() {
   4414  mReportCollector->ClearConsoleReports();
   4415 }
   4416 
   4417 bool HttpBaseChannel::IsNavigation() {
   4418  return LoadForceMainDocumentChannel() || (mLoadFlags & LOAD_DOCUMENT_URI);
   4419 }
   4420 
   4421 bool HttpBaseChannel::BypassServiceWorker() const {
   4422  return mLoadFlags & LOAD_BYPASS_SERVICE_WORKER;
   4423 }
   4424 
   4425 bool HttpBaseChannel::ShouldIntercept(nsIURI* aURI) {
   4426  nsCOMPtr<nsINetworkInterceptController> controller;
   4427  GetCallback(controller);
   4428  bool shouldIntercept = false;
   4429 
   4430  if (!StaticPrefs::dom_serviceWorkers_enabled()) {
   4431    return false;
   4432  }
   4433 
   4434  // We should never intercept internal redirects.  The ServiceWorker code
   4435  // can trigger interntal redirects as the result of a FetchEvent.  If
   4436  // we re-intercept then an infinite loop can occur.
   4437  //
   4438  // Its also important that we do not set the LOAD_BYPASS_SERVICE_WORKER
   4439  // flag because an internal redirect occurs.  Its possible that another
   4440  // interception should occur after the internal redirect.  For example,
   4441  // if the ServiceWorker chooses not to call respondWith() the channel
   4442  // will be reset with an internal redirect.  If the request is a navigation
   4443  // and the network then triggers a redirect its possible the new URL
   4444  // should be intercepted again.
   4445  //
   4446  // Note, HSTS upgrade redirects are often treated the same as internal
   4447  // redirects.  In this case, however, we intentionally allow interception
   4448  // of HSTS upgrade redirects.  This matches the expected spec behavior and
   4449  // does not run the risk of infinite loops as described above.
   4450  bool internalRedirect =
   4451      mLastRedirectFlags & nsIChannelEventSink::REDIRECT_INTERNAL;
   4452 
   4453  if (controller && mLoadInfo && !BypassServiceWorker() && !internalRedirect) {
   4454    nsresult rv = controller->ShouldPrepareForIntercept(
   4455        aURI ? aURI : mURI.get(), this, &shouldIntercept);
   4456    if (NS_FAILED(rv)) {
   4457      return false;
   4458    }
   4459  }
   4460  return shouldIntercept;
   4461 }
   4462 
   4463 void HttpBaseChannel::AddAsNonTailRequest() {
   4464  MOZ_ASSERT(NS_IsMainThread());
   4465 
   4466  if (EnsureRequestContext()) {
   4467    LOG((
   4468        "HttpBaseChannel::AddAsNonTailRequest this=%p, rc=%p, already added=%d",
   4469        this, mRequestContext.get(), (bool)LoadAddedAsNonTailRequest()));
   4470 
   4471    if (!LoadAddedAsNonTailRequest()) {
   4472      mRequestContext->AddNonTailRequest();
   4473      StoreAddedAsNonTailRequest(true);
   4474    }
   4475  }
   4476 }
   4477 
   4478 void HttpBaseChannel::RemoveAsNonTailRequest() {
   4479  MOZ_ASSERT(NS_IsMainThread());
   4480 
   4481  if (mRequestContext) {
   4482    LOG(
   4483        ("HttpBaseChannel::RemoveAsNonTailRequest this=%p, rc=%p, already "
   4484         "added=%d",
   4485         this, mRequestContext.get(), (bool)LoadAddedAsNonTailRequest()));
   4486 
   4487    if (LoadAddedAsNonTailRequest()) {
   4488      mRequestContext->RemoveNonTailRequest();
   4489      StoreAddedAsNonTailRequest(false);
   4490    }
   4491  }
   4492 }
   4493 
   4494 #ifdef DEBUG
   4495 void HttpBaseChannel::AssertPrivateBrowsingId() {
   4496  nsCOMPtr<nsILoadContext> loadContext;
   4497  NS_QueryNotificationCallbacks(this, loadContext);
   4498 
   4499  if (!loadContext) {
   4500    return;
   4501  }
   4502 
   4503  // We skip testing of favicon loading here since it could be triggered by XUL
   4504  // image which uses SystemPrincipal. The SystemPrincpal doesn't have
   4505  // mPrivateBrowsingId.
   4506  if (mLoadInfo->GetLoadingPrincipal() &&
   4507      mLoadInfo->GetLoadingPrincipal()->IsSystemPrincipal() &&
   4508      mLoadInfo->InternalContentPolicyType() ==
   4509          nsIContentPolicy::TYPE_INTERNAL_IMAGE_FAVICON) {
   4510    return;
   4511  }
   4512 
   4513  OriginAttributes docShellAttrs;
   4514  loadContext->GetOriginAttributes(docShellAttrs);
   4515  MOZ_ASSERT(mLoadInfo->GetOriginAttributes().mPrivateBrowsingId ==
   4516                 docShellAttrs.mPrivateBrowsingId,
   4517             "PrivateBrowsingId values are not the same between LoadInfo and "
   4518             "LoadContext.");
   4519 }
   4520 #endif
   4521 
   4522 already_AddRefed<nsILoadInfo> HttpBaseChannel::CloneLoadInfoForRedirect(
   4523    nsIURI* aNewURI, uint32_t aRedirectFlags) {
   4524  // make a copy of the loadinfo, append to the redirectchain
   4525  // this will be set on the newly created channel for the redirect target.
   4526  nsCOMPtr<nsILoadInfo> newLoadInfo =
   4527      static_cast<mozilla::net::LoadInfo*>(mLoadInfo.get())->Clone();
   4528 
   4529  ExtContentPolicyType contentPolicyType =
   4530      mLoadInfo->GetExternalContentPolicyType();
   4531  if (contentPolicyType == ExtContentPolicy::TYPE_DOCUMENT ||
   4532      contentPolicyType == ExtContentPolicy::TYPE_SUBDOCUMENT) {
   4533    // Reset PrincipalToInherit to a null principal. We'll credit the the
   4534    // redirecting resource's result principal as the new principal's precursor.
   4535    // This means that a data: URI will end up loading in a process based on the
   4536    // redirected-from URI.
   4537    nsCOMPtr<nsIPrincipal> redirectPrincipal;
   4538    nsContentUtils::GetSecurityManager()->GetChannelResultPrincipal(
   4539        this, getter_AddRefs(redirectPrincipal));
   4540    nsCOMPtr<nsIPrincipal> nullPrincipalToInherit =
   4541        NullPrincipal::CreateWithInheritedAttributes(redirectPrincipal);
   4542    newLoadInfo->SetPrincipalToInherit(nullPrincipalToInherit);
   4543  }
   4544 
   4545  bool isTopLevelDoc = newLoadInfo->GetExternalContentPolicyType() ==
   4546                       ExtContentPolicy::TYPE_DOCUMENT;
   4547 
   4548  if (isTopLevelDoc) {
   4549    // re-compute the origin attributes of the loadInfo if it's top-level load.
   4550    nsCOMPtr<nsILoadContext> loadContext;
   4551    NS_QueryNotificationCallbacks(this, loadContext);
   4552    OriginAttributes docShellAttrs;
   4553    if (loadContext) {
   4554      loadContext->GetOriginAttributes(docShellAttrs);
   4555    }
   4556 
   4557    OriginAttributes attrs = newLoadInfo->GetOriginAttributes();
   4558 
   4559    MOZ_ASSERT(
   4560        docShellAttrs.mUserContextId == attrs.mUserContextId,
   4561        "docshell and necko should have the same userContextId attribute.");
   4562    MOZ_ASSERT(
   4563        docShellAttrs.mPrivateBrowsingId == attrs.mPrivateBrowsingId,
   4564        "docshell and necko should have the same privateBrowsingId attribute.");
   4565    MOZ_ASSERT(docShellAttrs.mGeckoViewSessionContextId ==
   4566                   attrs.mGeckoViewSessionContextId,
   4567               "docshell and necko should have the same "
   4568               "geckoViewSessionContextId attribute");
   4569 
   4570    attrs = docShellAttrs;
   4571    attrs.SetFirstPartyDomain(true, aNewURI);
   4572    newLoadInfo->SetOriginAttributes(attrs);
   4573 
   4574    // re-compute the upgrade insecure requests bit for document navigations
   4575    // since it should only apply to same-origin navigations (redirects).
   4576    // we only do this if the CSP of the triggering element (the cspToInherit)
   4577    // uses 'upgrade-insecure-requests', otherwise UIR does not apply.
   4578    nsCOMPtr<nsIPolicyContainer> policyContainer =
   4579        newLoadInfo->GetPolicyContainerToInherit();
   4580    nsCOMPtr<nsIContentSecurityPolicy> csp =
   4581        PolicyContainer::GetCSP(policyContainer);
   4582    if (csp) {
   4583      bool upgradeInsecureRequests = false;
   4584      csp->GetUpgradeInsecureRequests(&upgradeInsecureRequests);
   4585      if (upgradeInsecureRequests) {
   4586        nsCOMPtr<nsIPrincipal> resultPrincipal =
   4587            BasePrincipal::CreateContentPrincipal(
   4588                aNewURI, newLoadInfo->GetOriginAttributes());
   4589        bool isConsideredSameOriginforUIR =
   4590            nsContentSecurityUtils::IsConsideredSameOriginForUIR(
   4591                newLoadInfo->TriggeringPrincipal(), resultPrincipal);
   4592        static_cast<mozilla::net::LoadInfo*>(newLoadInfo.get())
   4593            ->SetUpgradeInsecureRequests(isConsideredSameOriginforUIR);
   4594      }
   4595    }
   4596  }
   4597 
   4598  // Clone a new cookieJarSettings from the old one for the new channel.
   4599  // Otherwise, updating the new cookieJarSettings will affect the old one.
   4600  nsCOMPtr<nsICookieJarSettings> oldCookieJarSettings;
   4601  mLoadInfo->GetCookieJarSettings(getter_AddRefs(oldCookieJarSettings));
   4602 
   4603  RefPtr<CookieJarSettings> newCookieJarSettings;
   4604  newCookieJarSettings = CookieJarSettings::Cast(oldCookieJarSettings)->Clone();
   4605 
   4606  newLoadInfo->SetCookieJarSettings(newCookieJarSettings);
   4607 
   4608  // Clear the isThirdPartyContextToTopWindow flag for the new channel so that
   4609  // it will be computed again when the new channel is opened.
   4610  static_cast<net::LoadInfo*>(newLoadInfo.get())
   4611      ->ClearIsThirdPartyContextToTopWindow();
   4612 
   4613  // Leave empty, we want a 'clean ground' when creating the new channel.
   4614  // This will be ensured to be either set by the protocol handler or set
   4615  // to the redirect target URI properly after the channel creation.
   4616  newLoadInfo->SetResultPrincipalURI(nullptr);
   4617 
   4618  bool isInternalRedirect =
   4619      (aRedirectFlags & (nsIChannelEventSink::REDIRECT_INTERNAL |
   4620                         nsIChannelEventSink::REDIRECT_STS_UPGRADE));
   4621 
   4622  // Reset our sandboxed null principal ID when cloning loadInfo for an
   4623  // externally visible redirect.
   4624  if (!isInternalRedirect) {
   4625    // If we've redirected from http to something that isn't, clear
   4626    // the "external" flag, as loads that now go to other apps should be
   4627    // allowed to go ahead and not trip infinite-loop protection
   4628    // (see bug 1717314 for context).
   4629    if (!net::SchemeIsHttpOrHttps(aNewURI)) {
   4630      newLoadInfo->SetLoadTriggeredFromExternal(false);
   4631    }
   4632    newLoadInfo->ResetSandboxedNullPrincipalID();
   4633 
   4634    if (isTopLevelDoc) {
   4635      // Reset HTTPS-first and -only status on http redirect. To not
   4636      // unexpectedly downgrade requests that weren't upgraded via HTTPS-First
   4637      // (Bug 1904238).
   4638      (void)newLoadInfo->SetHttpsOnlyStatus(
   4639          nsILoadInfo::HTTPS_ONLY_UNINITIALIZED);
   4640 
   4641      // Reset schemeless status flag to prevent schemeless HTTPS-First from
   4642      // repeatedly trying to upgrade loads that get downgraded again from the
   4643      // server by a redirect (Bug 1937386).
   4644      (void)newLoadInfo->SetSchemelessInput(
   4645          nsILoadInfo::SchemelessInputTypeUnset);
   4646    }
   4647  }
   4648 
   4649  newLoadInfo->AppendRedirectHistoryEntry(this, isInternalRedirect);
   4650 
   4651  return newLoadInfo.forget();
   4652 }
   4653 
   4654 //-----------------------------------------------------------------------------
   4655 // nsHttpChannel::nsITraceableChannel
   4656 //-----------------------------------------------------------------------------
   4657 
   4658 NS_IMETHODIMP
   4659 HttpBaseChannel::SetNewListener(nsIStreamListener* aListener,
   4660                                bool aMustApplyContentConversion,
   4661                                nsIStreamListener** _retval) {
   4662  LOG((
   4663      "HttpBaseChannel::SetNewListener [this=%p, mListener=%p, newListener=%p]",
   4664      this, mListener.get(), aListener));
   4665 
   4666  if (!LoadTracingEnabled()) return NS_ERROR_FAILURE;
   4667 
   4668  NS_ENSURE_STATE(mListener);
   4669  NS_ENSURE_ARG_POINTER(aListener);
   4670 
   4671  nsCOMPtr<nsIStreamListener> wrapper = new nsStreamListenerWrapper(mListener);
   4672 
   4673  wrapper.forget(_retval);
   4674  mListener = aListener;
   4675  if (aMustApplyContentConversion) {
   4676    StoreListenerRequiresContentConversion(true);
   4677  }
   4678  return NS_OK;
   4679 }
   4680 
   4681 //-----------------------------------------------------------------------------
   4682 // HttpBaseChannel helpers
   4683 //-----------------------------------------------------------------------------
   4684 
   4685 void HttpBaseChannel::ReleaseListeners() {
   4686  MOZ_ASSERT(mCurrentThread->IsOnCurrentThread(),
   4687             "Should only be called on the current thread");
   4688 
   4689  mListener = nullptr;
   4690  mCallbacks = nullptr;
   4691  mProgressSink = nullptr;
   4692  mCompressListener = nullptr;
   4693  mORB = nullptr;
   4694 }
   4695 
   4696 void HttpBaseChannel::DoNotifyListener() {
   4697  LOG(("HttpBaseChannel::DoNotifyListener this=%p", this));
   4698 
   4699  // In case nsHttpChannel::OnStartRequest wasn't called (e.g. due to flag
   4700  // LOAD_ONLY_IF_MODIFIED) we want to set AfterOnStartRequestBegun to true
   4701  // before notifying listener.
   4702  if (!LoadAfterOnStartRequestBegun()) {
   4703    StoreAfterOnStartRequestBegun(true);
   4704  }
   4705 
   4706  if (mListener && !LoadOnStartRequestCalled()) {
   4707    nsCOMPtr<nsIStreamListener> listener = mListener;
   4708    StoreOnStartRequestCalled(true);
   4709    listener->OnStartRequest(this);
   4710  }
   4711  StoreOnStartRequestCalled(true);
   4712 
   4713  // Make sure IsPending is set to false. At this moment we are done from
   4714  // the point of view of our consumer and we have to report our self
   4715  // as not-pending.
   4716  StoreIsPending(false);
   4717 
   4718  // notify "http-on-before-stop-request" observers
   4719  gHttpHandler->OnBeforeStopRequest(this);
   4720 
   4721  if (mListener && !LoadOnStopRequestCalled()) {
   4722    nsCOMPtr<nsIStreamListener> listener = mListener;
   4723    StoreOnStopRequestCalled(true);
   4724    listener->OnStopRequest(this, mStatus);
   4725  }
   4726  StoreOnStopRequestCalled(true);
   4727 
   4728  // notify "http-on-stop-request" observers
   4729  gHttpHandler->OnStopRequest(this);
   4730 
   4731  // This channel has finished its job, potentially release any tail-blocked
   4732  // requests with this.
   4733  RemoveAsNonTailRequest();
   4734 
   4735  // We have to make sure to drop the references to listeners and callbacks
   4736  // no longer needed.
   4737  ReleaseListeners();
   4738 
   4739  DoNotifyListenerCleanup();
   4740 
   4741  // If this is a navigation, then we must let the docshell flush the reports
   4742  // to the console later.  The LoadDocument() is pointing at the detached
   4743  // document that started the navigation.  We want to show the reports on the
   4744  // new document.  Otherwise the console is wiped and the user never sees
   4745  // the information.
   4746  if (!IsNavigation()) {
   4747    if (mLoadGroup) {
   4748      FlushConsoleReports(mLoadGroup);
   4749    } else {
   4750      RefPtr<dom::Document> doc;
   4751      mLoadInfo->GetLoadingDocument(getter_AddRefs(doc));
   4752      FlushConsoleReports(doc);
   4753    }
   4754  }
   4755 }
   4756 
   4757 void HttpBaseChannel::AddCookiesToRequest() {
   4758  if (mLoadFlags & LOAD_ANONYMOUS) {
   4759    return;
   4760  }
   4761 
   4762  bool useCookieService = (XRE_IsParentProcess());
   4763  nsAutoCString cookie;
   4764  if (useCookieService) {
   4765    nsICookieService* cs = gHttpHandler->GetCookieService();
   4766    if (cs) {
   4767      cs->GetCookieStringFromHttp(mURI, this, cookie);
   4768    }
   4769 
   4770    if (cookie.IsEmpty()) {
   4771      cookie = mUserSetCookieHeader;
   4772    } else if (!mUserSetCookieHeader.IsEmpty()) {
   4773      cookie.AppendLiteral("; ");
   4774      cookie.Append(mUserSetCookieHeader);
   4775    }
   4776  } else {
   4777    cookie = mUserSetCookieHeader;
   4778  }
   4779 
   4780  // If we are in the child process, we want the parent seeing any
   4781  // cookie headers that might have been set by SetRequestHeader()
   4782  SetRequestHeader(nsHttp::Cookie.val(), cookie, false);
   4783 }
   4784 
   4785 /* static */
   4786 void HttpBaseChannel::PropagateReferenceIfNeeded(
   4787    nsIURI* aURI, nsCOMPtr<nsIURI>& aRedirectURI) {
   4788  bool hasRef = false;
   4789  nsresult rv = aRedirectURI->GetHasRef(&hasRef);
   4790  if (NS_SUCCEEDED(rv) && !hasRef) {
   4791    nsAutoCString ref;
   4792    aURI->GetRef(ref);
   4793    if (!ref.IsEmpty()) {
   4794      // NOTE: SetRef will fail if mRedirectURI is immutable
   4795      // (e.g. an about: URI)... Oh well.
   4796      (void)NS_MutateURI(aRedirectURI).SetRef(ref).Finalize(aRedirectURI);
   4797    }
   4798  }
   4799 }
   4800 
   4801 bool HttpBaseChannel::ShouldRewriteRedirectToGET(
   4802    uint32_t httpStatus, nsHttpRequestHead::ParsedMethodType method) {
   4803  // for 301 and 302, only rewrite POST
   4804  if (httpStatus == 301 || httpStatus == 302) {
   4805    return method == nsHttpRequestHead::kMethod_Post;
   4806  }
   4807 
   4808  // rewrite for 303 unless it was HEAD
   4809  if (httpStatus == 303) return method != nsHttpRequestHead::kMethod_Head;
   4810 
   4811  // otherwise, such as for 307, do not rewrite
   4812  return false;
   4813 }
   4814 
   4815 NS_IMETHODIMP
   4816 HttpBaseChannel::ShouldStripRequestBodyHeader(const nsACString& aMethod,
   4817                                              bool* aResult) {
   4818  *aResult = false;
   4819  uint32_t httpStatus = 0;
   4820  if (NS_FAILED(GetResponseStatus(&httpStatus))) {
   4821    return NS_OK;
   4822  }
   4823 
   4824  nsAutoCString method(aMethod);
   4825  nsHttpRequestHead::ParsedMethodType parsedMethod;
   4826  nsHttpRequestHead::ParseMethod(method, parsedMethod);
   4827  // Fetch 4.4.11, which is slightly different than the perserved method
   4828  // algrorithm: strip request-body-header for GET->GET redirection for 303.
   4829  *aResult =
   4830      ShouldRewriteRedirectToGET(httpStatus, parsedMethod) &&
   4831      !(httpStatus == 303 && parsedMethod == nsHttpRequestHead::kMethod_Get);
   4832 
   4833  return NS_OK;
   4834 }
   4835 
   4836 HttpBaseChannel::ReplacementChannelConfig
   4837 HttpBaseChannel::CloneReplacementChannelConfig(bool aPreserveMethod,
   4838                                               uint32_t aRedirectFlags,
   4839                                               ReplacementReason aReason) {
   4840  ReplacementChannelConfig config;
   4841  config.redirectFlags = aRedirectFlags;
   4842  config.classOfService = mClassOfService;
   4843 
   4844  if (mPrivateBrowsingOverriden) {
   4845    config.privateBrowsing = Some(mPrivateBrowsing);
   4846  }
   4847 
   4848  if (mReferrerInfo) {
   4849    // When cloning for a document channel replacement (parent process
   4850    // copying values for a new content process channel), this happens after
   4851    // OnStartRequest so we have the headers for the response available.
   4852    // We don't want to apply them to the referrer for the channel though,
   4853    // since that is the referrer for the current document, and the header
   4854    // should only apply to navigations from the current document.
   4855    if (aReason == ReplacementReason::DocumentChannel) {
   4856      config.referrerInfo = mReferrerInfo;
   4857    } else {
   4858      dom::ReferrerPolicy referrerPolicy = dom::ReferrerPolicy::_empty;
   4859      nsAutoCString tRPHeaderCValue;
   4860      (void)GetResponseHeader("referrer-policy"_ns, tRPHeaderCValue);
   4861      NS_ConvertUTF8toUTF16 tRPHeaderValue(tRPHeaderCValue);
   4862 
   4863      if (!tRPHeaderValue.IsEmpty()) {
   4864        referrerPolicy =
   4865            dom::ReferrerInfo::ReferrerPolicyFromHeaderString(tRPHeaderValue);
   4866      }
   4867 
   4868      // In case we are here because an upgrade happened through mixed content
   4869      // upgrading, CSP upgrade-insecure-requests, HTTPS-Only or HTTPS-First, we
   4870      // have to recalculate the referrer based on the original referrer to
   4871      // account for the different scheme. This does NOT apply to HSTS.
   4872      // See Bug 1857894 and order of https://fetch.spec.whatwg.org/#main-fetch.
   4873      // Otherwise, if we have a new referrer policy, we want to recalculate the
   4874      // referrer based on the old computed referrer (Bug 1678545).
   4875      bool wasNonHSTSUpgrade =
   4876          (aRedirectFlags & nsIChannelEventSink::REDIRECT_STS_UPGRADE) &&
   4877          (!mLoadInfo->GetHstsStatus());
   4878      if (wasNonHSTSUpgrade) {
   4879        nsCOMPtr<nsIURI> referrer = mReferrerInfo->GetOriginalReferrer();
   4880        config.referrerInfo =
   4881            new dom::ReferrerInfo(referrer, mReferrerInfo->ReferrerPolicy(),
   4882                                  mReferrerInfo->GetSendReferrer());
   4883      } else if (referrerPolicy != dom::ReferrerPolicy::_empty) {
   4884        nsCOMPtr<nsIURI> referrer = mReferrerInfo->GetComputedReferrer();
   4885        config.referrerInfo = new dom::ReferrerInfo(
   4886            referrer, referrerPolicy, mReferrerInfo->GetSendReferrer());
   4887      } else {
   4888        config.referrerInfo = mReferrerInfo;
   4889      }
   4890    }
   4891  }
   4892 
   4893  nsCOMPtr<nsITimedChannel> oldTimedChannel(
   4894      do_QueryInterface(static_cast<nsIHttpChannel*>(this)));
   4895  if (oldTimedChannel) {
   4896    config.timedChannelInfo = Some(dom::TimedChannelInfo());
   4897    config.timedChannelInfo->redirectCount() = mRedirectCount;
   4898    config.timedChannelInfo->internalRedirectCount() = mInternalRedirectCount;
   4899    config.timedChannelInfo->asyncOpen() = mAsyncOpenTime;
   4900    config.timedChannelInfo->channelCreation() = mChannelCreationTimestamp;
   4901    config.timedChannelInfo->redirectStart() = mRedirectStartTimeStamp;
   4902    config.timedChannelInfo->redirectEnd() = mRedirectEndTimeStamp;
   4903    config.timedChannelInfo->initiatorType() = mInitiatorType;
   4904    config.timedChannelInfo->allRedirectsSameOrigin() =
   4905        LoadAllRedirectsSameOrigin();
   4906    config.timedChannelInfo->allRedirectsPassTimingAllowCheck() =
   4907        LoadAllRedirectsPassTimingAllowCheck();
   4908    // Execute the timing allow check to determine whether
   4909    // to report the redirect timing info
   4910    nsCOMPtr<nsILoadInfo> loadInfo = LoadInfo();
   4911    // TYPE_DOCUMENT loads don't have a loadingPrincipal, so we can't set
   4912    // AllRedirectsPassTimingAllowCheck on them.
   4913    if (loadInfo->GetExternalContentPolicyType() !=
   4914        ExtContentPolicy::TYPE_DOCUMENT) {
   4915      nsCOMPtr<nsIPrincipal> principal = loadInfo->GetLoadingPrincipal();
   4916      config.timedChannelInfo->timingAllowCheckForPrincipal() =
   4917          Some(oldTimedChannel->TimingAllowCheck(principal));
   4918    }
   4919 
   4920    config.timedChannelInfo->allRedirectsPassTimingAllowCheck() =
   4921        LoadAllRedirectsPassTimingAllowCheck();
   4922    config.timedChannelInfo->launchServiceWorkerStart() =
   4923        mLaunchServiceWorkerStart;
   4924    config.timedChannelInfo->launchServiceWorkerEnd() = mLaunchServiceWorkerEnd;
   4925    config.timedChannelInfo->dispatchFetchEventStart() =
   4926        mDispatchFetchEventStart;
   4927    config.timedChannelInfo->dispatchFetchEventEnd() = mDispatchFetchEventEnd;
   4928    config.timedChannelInfo->handleFetchEventStart() = mHandleFetchEventStart;
   4929    config.timedChannelInfo->handleFetchEventEnd() = mHandleFetchEventEnd;
   4930    config.timedChannelInfo->responseStart() =
   4931        mTransactionTimings.responseStart;
   4932    config.timedChannelInfo->responseEnd() = mTransactionTimings.responseEnd;
   4933  }
   4934 
   4935  if (aPreserveMethod) {
   4936    // since preserveMethod is true, we need to ensure that the appropriate
   4937    // request method gets set on the channel, regardless of whether or not
   4938    // we set the upload stream above. This means SetRequestMethod() will
   4939    // be called twice if ExplicitSetUploadStream() gets called above.
   4940 
   4941    nsAutoCString method;
   4942    mRequestHead.Method(method);
   4943    config.method = Some(method);
   4944 
   4945    if (mUploadStream) {
   4946      // rewind upload stream
   4947      nsCOMPtr<nsISeekableStream> seekable = do_QueryInterface(mUploadStream);
   4948      if (seekable) {
   4949        seekable->Seek(nsISeekableStream::NS_SEEK_SET, 0);
   4950      }
   4951      config.uploadStream = mUploadStream;
   4952    }
   4953    config.uploadStreamLength = mReqContentLength;
   4954    config.uploadStreamHasHeaders = LoadUploadStreamHasHeaders();
   4955 
   4956    nsAutoCString contentType;
   4957    nsresult rv = mRequestHead.GetHeader(nsHttp::Content_Type, contentType);
   4958    if (NS_SUCCEEDED(rv)) {
   4959      config.contentType = Some(contentType);
   4960    }
   4961 
   4962    nsAutoCString contentLength;
   4963    rv = mRequestHead.GetHeader(nsHttp::Content_Length, contentLength);
   4964    if (NS_SUCCEEDED(rv)) {
   4965      config.contentLength = Some(contentLength);
   4966    }
   4967  }
   4968 
   4969  return config;
   4970 }
   4971 
   4972 /* static */ void HttpBaseChannel::ConfigureReplacementChannel(
   4973    nsIChannel* newChannel, const ReplacementChannelConfig& config,
   4974    ReplacementReason aReason) {
   4975  nsCOMPtr<nsIClassOfService> cos(do_QueryInterface(newChannel));
   4976  if (cos) {
   4977    cos->SetClassOfService(config.classOfService);
   4978  }
   4979 
   4980  // Try to preserve the privacy bit if it has been overridden
   4981  if (config.privateBrowsing) {
   4982    nsCOMPtr<nsIPrivateBrowsingChannel> newPBChannel =
   4983        do_QueryInterface(newChannel);
   4984    if (newPBChannel) {
   4985      newPBChannel->SetPrivate(*config.privateBrowsing);
   4986    }
   4987  }
   4988 
   4989  // Transfer the timing data (if we are dealing with an nsITimedChannel).
   4990  nsCOMPtr<nsITimedChannel> newTimedChannel(do_QueryInterface(newChannel));
   4991  if (config.timedChannelInfo && newTimedChannel) {
   4992    // If we're an internal redirect, or a document channel replacement,
   4993    // then we shouldn't record any new timing for this and just copy
   4994    // over the existing values.
   4995    bool shouldHideTiming = aReason != ReplacementReason::Redirect;
   4996    if (shouldHideTiming) {
   4997      newTimedChannel->SetRedirectCount(
   4998          config.timedChannelInfo->redirectCount());
   4999      int32_t newCount = config.timedChannelInfo->internalRedirectCount() + 1;
   5000      newTimedChannel->SetInternalRedirectCount(std::max(
   5001          newCount, static_cast<int32_t>(
   5002                        config.timedChannelInfo->internalRedirectCount())));
   5003    } else {
   5004      int32_t newCount = config.timedChannelInfo->redirectCount() + 1;
   5005      newTimedChannel->SetRedirectCount(std::max(
   5006          newCount,
   5007          static_cast<int32_t>(config.timedChannelInfo->redirectCount())));
   5008      newTimedChannel->SetInternalRedirectCount(
   5009          config.timedChannelInfo->internalRedirectCount());
   5010    }
   5011 
   5012    if (shouldHideTiming) {
   5013      if (!config.timedChannelInfo->channelCreation().IsNull()) {
   5014        newTimedChannel->SetChannelCreation(
   5015            config.timedChannelInfo->channelCreation());
   5016      }
   5017 
   5018      if (!config.timedChannelInfo->asyncOpen().IsNull()) {
   5019        newTimedChannel->SetAsyncOpen(config.timedChannelInfo->asyncOpen());
   5020      }
   5021    }
   5022 
   5023    // If the RedirectStart is null, we will use the AsyncOpen value of the
   5024    // previous channel (this is the first redirect in the redirects chain).
   5025    if (config.timedChannelInfo->redirectStart().IsNull()) {
   5026      // Only do this for real redirects.  Internal redirects should be hidden.
   5027      if (!shouldHideTiming) {
   5028        newTimedChannel->SetRedirectStart(config.timedChannelInfo->asyncOpen());
   5029      }
   5030    } else {
   5031      newTimedChannel->SetRedirectStart(
   5032          config.timedChannelInfo->redirectStart());
   5033    }
   5034 
   5035    // For internal redirects just propagate the last redirect end time
   5036    // forward.  Otherwise the new redirect end time is the last response
   5037    // end time.
   5038    TimeStamp newRedirectEnd;
   5039    if (shouldHideTiming) {
   5040      newRedirectEnd = config.timedChannelInfo->redirectEnd();
   5041    } else if (!config.timedChannelInfo->responseEnd().IsNull()) {
   5042      newRedirectEnd = config.timedChannelInfo->responseEnd();
   5043    } else {
   5044      newRedirectEnd = TimeStamp::Now();
   5045    }
   5046    newTimedChannel->SetRedirectEnd(newRedirectEnd);
   5047 
   5048    newTimedChannel->SetInitiatorType(config.timedChannelInfo->initiatorType());
   5049 
   5050    nsCOMPtr<nsILoadInfo> loadInfo = newChannel->LoadInfo();
   5051    MOZ_ASSERT(loadInfo);
   5052 
   5053    newTimedChannel->SetAllRedirectsSameOrigin(
   5054        config.timedChannelInfo->allRedirectsSameOrigin());
   5055 
   5056    if (config.timedChannelInfo->timingAllowCheckForPrincipal()) {
   5057      newTimedChannel->SetAllRedirectsPassTimingAllowCheck(
   5058          config.timedChannelInfo->allRedirectsPassTimingAllowCheck() &&
   5059          *config.timedChannelInfo->timingAllowCheckForPrincipal());
   5060    }
   5061 
   5062    // Propagate service worker measurements across redirects.  The
   5063    // PeformanceResourceTiming.workerStart API expects to see the
   5064    // worker start time after a redirect.
   5065    newTimedChannel->SetLaunchServiceWorkerStart(
   5066        config.timedChannelInfo->launchServiceWorkerStart());
   5067    newTimedChannel->SetLaunchServiceWorkerEnd(
   5068        config.timedChannelInfo->launchServiceWorkerEnd());
   5069    newTimedChannel->SetDispatchFetchEventStart(
   5070        config.timedChannelInfo->dispatchFetchEventStart());
   5071    newTimedChannel->SetDispatchFetchEventEnd(
   5072        config.timedChannelInfo->dispatchFetchEventEnd());
   5073    newTimedChannel->SetHandleFetchEventStart(
   5074        config.timedChannelInfo->handleFetchEventStart());
   5075    newTimedChannel->SetHandleFetchEventEnd(
   5076        config.timedChannelInfo->handleFetchEventEnd());
   5077  }
   5078 
   5079  nsCOMPtr<nsIHttpChannel> httpChannel = do_QueryInterface(newChannel);
   5080  if (!httpChannel) {
   5081    return;  // no other options to set
   5082  }
   5083 
   5084  if (config.uploadStream) {
   5085    nsCOMPtr<nsIUploadChannel2> uploadChannel2 = do_QueryInterface(httpChannel);
   5086    // replicate original call to SetUploadStream...
   5087    if (uploadChannel2) {
   5088      const nsACString& ctype =
   5089          config.contentType ? *config.contentType : VoidCString();
   5090      // If header is not present mRequestHead.HasHeaderValue will truncated
   5091      // it.  But we want to end up with a void string, not an empty string,
   5092      // because ExplicitSetUploadStream treats the former as "no header" and
   5093      // the latter as "header with empty string value".
   5094      const nsACString& method = config.method ? *config.method : VoidCString();
   5095      uploadChannel2->ExplicitSetUploadStream(config.uploadStream, ctype,
   5096                                              config.uploadStreamLength, method,
   5097                                              config.uploadStreamHasHeaders);
   5098    } else if (nsCOMPtr<nsIUploadChannel> uploadChannel =
   5099                   do_QueryInterface(httpChannel)) {
   5100      MOZ_ASSERT(false,
   5101                 "Should not QI to nsIUploadChannel but not nsIUploadChannel2");
   5102    }
   5103  }
   5104 
   5105  if (config.referrerInfo) {
   5106    DebugOnly<nsresult> success{};
   5107    success = httpChannel->SetReferrerInfo(config.referrerInfo);
   5108    MOZ_ASSERT(NS_SUCCEEDED(success));
   5109  }
   5110 
   5111  if (config.method) {
   5112    DebugOnly<nsresult> rv = httpChannel->SetRequestMethod(*config.method);
   5113    MOZ_ASSERT(NS_SUCCEEDED(rv));
   5114  }
   5115 }
   5116 
   5117 HttpBaseChannel::ReplacementChannelConfig::ReplacementChannelConfig(
   5118    const dom::ReplacementChannelConfigInit& aInit) {
   5119  redirectFlags = aInit.redirectFlags();
   5120  classOfService = aInit.classOfService();
   5121  privateBrowsing = aInit.privateBrowsing();
   5122  method = aInit.method();
   5123  referrerInfo = aInit.referrerInfo();
   5124  timedChannelInfo = aInit.timedChannelInfo();
   5125  uploadStream = aInit.uploadStream();
   5126  uploadStreamLength = aInit.uploadStreamLength();
   5127  uploadStreamHasHeaders = aInit.uploadStreamHasHeaders();
   5128  contentType = aInit.contentType();
   5129  contentLength = aInit.contentLength();
   5130 }
   5131 
   5132 dom::ReplacementChannelConfigInit
   5133 HttpBaseChannel::ReplacementChannelConfig::Serialize() {
   5134  dom::ReplacementChannelConfigInit config;
   5135  config.redirectFlags() = redirectFlags;
   5136  config.classOfService() = classOfService;
   5137  config.privateBrowsing() = privateBrowsing;
   5138  config.method() = method;
   5139  config.referrerInfo() = referrerInfo;
   5140  config.timedChannelInfo() = timedChannelInfo;
   5141  config.uploadStream() =
   5142      uploadStream ? RemoteLazyInputStream::WrapStream(uploadStream) : nullptr;
   5143  config.uploadStreamLength() = uploadStreamLength;
   5144  config.uploadStreamHasHeaders() = uploadStreamHasHeaders;
   5145  config.contentType() = contentType;
   5146  config.contentLength() = contentLength;
   5147 
   5148  return config;
   5149 }
   5150 
   5151 nsresult HttpBaseChannel::SetupReplacementChannel(nsIURI* newURI,
   5152                                                  nsIChannel* newChannel,
   5153                                                  bool preserveMethod,
   5154                                                  uint32_t redirectFlags) {
   5155  nsresult rv;
   5156 
   5157  LOG(
   5158      ("HttpBaseChannel::SetupReplacementChannel "
   5159       "[this=%p newChannel=%p preserveMethod=%d]",
   5160       this, newChannel, preserveMethod));
   5161 
   5162  // Ensure the channel's loadInfo's result principal URI so that it's
   5163  // either non-null or updated to the redirect target URI.
   5164  // We must do this because in case the loadInfo's result principal URI
   5165  // is null, it would be taken from OriginalURI of the channel.  But we
   5166  // overwrite it with the whole redirect chain first URI before opening
   5167  // the target channel, hence the information would be lost.
   5168  // If the protocol handler that created the channel wants to use
   5169  // the originalURI of the channel as the principal URI, this fulfills
   5170  // that request - newURI is the original URI of the channel.
   5171  nsCOMPtr<nsILoadInfo> newLoadInfo = newChannel->LoadInfo();
   5172  nsCOMPtr<nsIURI> resultPrincipalURI;
   5173  rv = newLoadInfo->GetResultPrincipalURI(getter_AddRefs(resultPrincipalURI));
   5174  NS_ENSURE_SUCCESS(rv, rv);
   5175  if (!resultPrincipalURI) {
   5176    rv = newLoadInfo->SetResultPrincipalURI(newURI);
   5177    NS_ENSURE_SUCCESS(rv, rv);
   5178  }
   5179 
   5180  nsLoadFlags loadFlags = mLoadFlags;
   5181  loadFlags |= LOAD_REPLACE;
   5182 
   5183  // if the original channel was using SSL and this channel is not using
   5184  // SSL, then no need to inhibit persistent caching.  however, if the
   5185  // original channel was not using SSL and has INHIBIT_PERSISTENT_CACHING
   5186  // set, then allow the flag to apply to the redirected channel as well.
   5187  // since we force set INHIBIT_PERSISTENT_CACHING on all HTTPS channels,
   5188  // we only need to check if the original channel was using SSL.
   5189  if (mURI->SchemeIs("https")) {
   5190    loadFlags &= ~INHIBIT_PERSISTENT_CACHING;
   5191  }
   5192 
   5193  newChannel->SetLoadFlags(loadFlags);
   5194 
   5195  nsCOMPtr<nsIHttpChannel> httpChannel = do_QueryInterface(newChannel);
   5196 
   5197  ReplacementReason redirectType =
   5198      redirectFlags & (nsIChannelEventSink::REDIRECT_INTERNAL |
   5199                       nsIChannelEventSink::REDIRECT_TRANSPARENT)
   5200          ? ReplacementReason::InternalRedirect
   5201          : ReplacementReason::Redirect;
   5202  ReplacementChannelConfig config = CloneReplacementChannelConfig(
   5203      preserveMethod, redirectFlags, redirectType);
   5204  ConfigureReplacementChannel(newChannel, config, redirectType);
   5205 
   5206  // Check whether or not this was a cross-domain redirect.
   5207  nsCOMPtr<nsITimedChannel> newTimedChannel(do_QueryInterface(newChannel));
   5208  bool sameOriginWithOriginalUri = SameOriginWithOriginalUri(newURI);
   5209  if (config.timedChannelInfo && newTimedChannel) {
   5210    newTimedChannel->SetAllRedirectsSameOrigin(
   5211        config.timedChannelInfo->allRedirectsSameOrigin() &&
   5212        sameOriginWithOriginalUri);
   5213  }
   5214 
   5215  newChannel->SetLoadGroup(mLoadGroup);
   5216  newChannel->SetNotificationCallbacks(mCallbacks);
   5217  // TODO: create tests for cross-origin redirect in bug 1662896.
   5218  if (sameOriginWithOriginalUri) {
   5219    newChannel->SetContentDisposition(mContentDispositionHint);
   5220    if (mContentDispositionFilename) {
   5221      newChannel->SetContentDispositionFilename(*mContentDispositionFilename);
   5222    }
   5223  }
   5224 
   5225  if (!httpChannel) return NS_OK;  // no other options to set
   5226 
   5227  // Preserve the CORS preflight information.
   5228  nsCOMPtr<nsIHttpChannelInternal> httpInternal = do_QueryInterface(newChannel);
   5229  if (httpInternal) {
   5230    httpInternal->SetLastRedirectFlags(redirectFlags);
   5231 
   5232    if (LoadRequireCORSPreflight()) {
   5233      httpInternal->SetCorsPreflightParameters(mUnsafeHeaders, false, false);
   5234    }
   5235  }
   5236 
   5237  // convey the LoadAllowSTS() flags
   5238  rv = httpChannel->SetAllowSTS(LoadAllowSTS());
   5239  MOZ_ASSERT(NS_SUCCEEDED(rv));
   5240 
   5241  // convey the Accept header value
   5242  {
   5243    nsAutoCString oldAcceptValue;
   5244    nsresult hasHeader = mRequestHead.GetHeader(nsHttp::Accept, oldAcceptValue);
   5245    if (NS_SUCCEEDED(hasHeader)) {
   5246      rv = httpChannel->SetRequestHeader("Accept"_ns, oldAcceptValue, false);
   5247      MOZ_ASSERT(NS_SUCCEEDED(rv));
   5248    }
   5249  }
   5250 
   5251  // convey the User-Agent header value
   5252  // since we might be setting custom user agent from DevTools.
   5253  if (httpInternal && mRequestMode == RequestMode::No_cors &&
   5254      redirectType == ReplacementReason::Redirect) {
   5255    nsAutoCString oldUserAgent;
   5256    nsresult hasHeader =
   5257        mRequestHead.GetHeader(nsHttp::User_Agent, oldUserAgent);
   5258    if (NS_SUCCEEDED(hasHeader)) {
   5259      rv = httpChannel->SetRequestHeader("User-Agent"_ns, oldUserAgent, false);
   5260      MOZ_ASSERT(NS_SUCCEEDED(rv));
   5261    }
   5262  }
   5263 
   5264  // convery the IsUserAgentHeaderModified value.
   5265  if (httpInternal) {
   5266    rv = httpInternal->SetIsUserAgentHeaderModified(
   5267        LoadIsUserAgentHeaderModified());
   5268    MOZ_ASSERT(NS_SUCCEEDED(rv));
   5269  }
   5270 
   5271  // share the request context - see bug 1236650
   5272  rv = httpChannel->SetRequestContextID(mRequestContextID);
   5273  MOZ_ASSERT(NS_SUCCEEDED(rv));
   5274 
   5275  // When on the parent process, the channel can't attempt to get it itself.
   5276  // When on the child process, it would be waste to query it again.
   5277  rv = httpChannel->SetBrowserId(mBrowserId);
   5278  MOZ_ASSERT(NS_SUCCEEDED(rv));
   5279 
   5280  // Not setting this flag would break carrying permissions down to the child
   5281  // process when the channel is artificially forced to be a main document load.
   5282  rv = httpChannel->SetIsMainDocumentChannel(LoadForceMainDocumentChannel());
   5283  MOZ_ASSERT(NS_SUCCEEDED(rv));
   5284 
   5285  // Preserve the loading order
   5286  nsCOMPtr<nsISupportsPriority> p = do_QueryInterface(newChannel);
   5287  if (p) {
   5288    p->SetPriority(mPriority);
   5289  }
   5290 
   5291  if (httpInternal) {
   5292    // Convey third party cookie, conservative, and spdy flags.
   5293    rv = httpInternal->SetThirdPartyFlags(LoadThirdPartyFlags());
   5294    MOZ_ASSERT(NS_SUCCEEDED(rv));
   5295    rv = httpInternal->SetAllowSpdy(LoadAllowSpdy());
   5296    MOZ_ASSERT(NS_SUCCEEDED(rv));
   5297    rv = httpInternal->SetAllowHttp3(LoadAllowHttp3());
   5298    MOZ_ASSERT(NS_SUCCEEDED(rv));
   5299    rv = httpInternal->SetAllowAltSvc(LoadAllowAltSvc());
   5300    MOZ_ASSERT(NS_SUCCEEDED(rv));
   5301    rv = httpInternal->SetBeConservative(LoadBeConservative());
   5302    MOZ_ASSERT(NS_SUCCEEDED(rv));
   5303    rv = httpInternal->SetIsTRRServiceChannel(LoadIsTRRServiceChannel());
   5304    MOZ_ASSERT(NS_SUCCEEDED(rv));
   5305    rv = httpInternal->SetTlsFlags(mTlsFlags);
   5306    MOZ_ASSERT(NS_SUCCEEDED(rv));
   5307 
   5308    // Ensure the type of realChannel involves all types it may redirect to.
   5309    // Such as nsHttpChannel and InterceptedChannel.
   5310    // Even thought InterceptedChannel itself doesn't require these information,
   5311    // it may still be necessary for the following redirections.
   5312    // E.g. nsHttpChannel -> InterceptedChannel -> nsHttpChannel
   5313    RefPtr<HttpBaseChannel> realChannel;
   5314    CallQueryInterface(newChannel, realChannel.StartAssignment());
   5315    if (realChannel) {
   5316      realChannel->SetTopWindowURI(mTopWindowURI);
   5317 
   5318      realChannel->StoreTaintedOriginFlag(
   5319          ShouldTaintReplacementChannelOrigin(newChannel, redirectFlags));
   5320    }
   5321 
   5322    // update the DocumentURI indicator since we are being redirected.
   5323    // if this was a top-level document channel, then the new channel
   5324    // should have its mDocumentURI point to newURI; otherwise, we
   5325    // just need to pass along our mDocumentURI to the new channel.
   5326    if (newURI && (mURI == mDocumentURI)) {
   5327      rv = httpInternal->SetDocumentURI(newURI);
   5328    } else {
   5329      rv = httpInternal->SetDocumentURI(mDocumentURI);
   5330    }
   5331    MOZ_ASSERT(NS_SUCCEEDED(rv));
   5332 
   5333    // if there is a chain of keys for redirect-responses we transfer it to
   5334    // the new channel (see bug #561276)
   5335    {
   5336      auto redirectedCachekeys = mRedirectedCachekeys.Lock();
   5337      auto& ref = redirectedCachekeys.ref();
   5338      if (ref) {
   5339        LOG(
   5340            ("HttpBaseChannel::SetupReplacementChannel "
   5341             "[this=%p] transferring chain of redirect cache-keys",
   5342             this));
   5343        rv = httpInternal->SetCacheKeysRedirectChain(ref.release());
   5344        MOZ_ASSERT(NS_SUCCEEDED(rv));
   5345      }
   5346    }
   5347 
   5348    // Preserve Request mode.
   5349    rv = httpInternal->SetRequestMode(mRequestMode);
   5350    MOZ_ASSERT(NS_SUCCEEDED(rv));
   5351 
   5352    // Preserve Redirect mode flag.
   5353    rv = httpInternal->SetRedirectMode(mRedirectMode);
   5354    MOZ_ASSERT(NS_SUCCEEDED(rv));
   5355 
   5356    httpInternal->SetAltDataForChild(LoadAltDataForChild());
   5357    if (LoadDisableAltDataCache()) {
   5358      httpInternal->DisableAltDataCache();
   5359    }
   5360  }
   5361 
   5362  // transfer any properties
   5363  nsCOMPtr<nsIWritablePropertyBag> bag(do_QueryInterface(newChannel));
   5364  if (bag) {
   5365    for (const auto& entry : mPropertyHash) {
   5366      bag->SetProperty(entry.GetKey(), entry.GetWeak());
   5367    }
   5368  }
   5369 
   5370  // Pass the preferred alt-data type on to the new channel.
   5371  nsCOMPtr<nsICacheInfoChannel> cacheInfoChan(do_QueryInterface(newChannel));
   5372  if (cacheInfoChan) {
   5373    for (auto& data : mPreferredCachedAltDataTypes) {
   5374      cacheInfoChan->PreferAlternativeDataType(data.type(), data.contentType(),
   5375                                               data.deliverAltData());
   5376    }
   5377 
   5378    if (LoadForceValidateCacheContent()) {
   5379      (void)cacheInfoChan->SetForceValidateCacheContent(true);
   5380    }
   5381  }
   5382 
   5383  if (redirectFlags & (nsIChannelEventSink::REDIRECT_INTERNAL |
   5384                       nsIChannelEventSink::REDIRECT_STS_UPGRADE)) {
   5385    // Copy non-origin related headers to the new channel.
   5386    nsCOMPtr<nsIHttpHeaderVisitor> visitor =
   5387        new AddHeadersToChannelVisitor(httpChannel);
   5388    rv = mRequestHead.VisitHeaders(visitor);
   5389    MOZ_ASSERT(NS_SUCCEEDED(rv));
   5390  }
   5391 
   5392  // we need to strip Authentication headers for cross-origin requests
   5393  // Ref: https://fetch.spec.whatwg.org/#http-redirect-fetch
   5394  nsAutoCString authHeader;
   5395  if (NS_SUCCEEDED(
   5396          httpChannel->GetRequestHeader("Authorization"_ns, authHeader)) &&
   5397      NS_ShouldRemoveAuthHeaderOnRedirect(static_cast<nsIChannel*>(this),
   5398                                          newChannel, redirectFlags)) {
   5399    rv = httpChannel->SetRequestHeader("Authorization"_ns, ""_ns, false);
   5400    MOZ_ASSERT(NS_SUCCEEDED(rv));
   5401  }
   5402 
   5403  return NS_OK;
   5404 }
   5405 
   5406 // check whether the new channel is of same origin as the current channel
   5407 bool HttpBaseChannel::IsNewChannelSameOrigin(nsIChannel* aNewChannel) {
   5408  bool isSameOrigin = false;
   5409  nsIScriptSecurityManager* ssm = nsContentUtils::GetSecurityManager();
   5410 
   5411  if (!ssm) {
   5412    return false;
   5413  }
   5414 
   5415  nsCOMPtr<nsIURI> newURI;
   5416  NS_GetFinalChannelURI(aNewChannel, getter_AddRefs(newURI));
   5417 
   5418  nsresult rv = ssm->CheckSameOriginURI(newURI, mURI, false, false);
   5419  if (NS_SUCCEEDED(rv)) {
   5420    isSameOrigin = true;
   5421  }
   5422 
   5423  return isSameOrigin;
   5424 }
   5425 
   5426 bool HttpBaseChannel::ShouldTaintReplacementChannelOrigin(
   5427    nsIChannel* aNewChannel, uint32_t aRedirectFlags) {
   5428  if (LoadTaintedOriginFlag()) {
   5429    return true;
   5430  }
   5431 
   5432  if (NS_IsInternalSameURIRedirect(this, aNewChannel, aRedirectFlags) ||
   5433      NS_IsHSTSUpgradeRedirect(this, aNewChannel, aRedirectFlags)) {
   5434    return false;
   5435  }
   5436 
   5437  // If new channel is not of same origin we need to taint unless
   5438  // mURI <-> mOriginalURI/LoadingPrincipal are same origin.
   5439  if (IsNewChannelSameOrigin(aNewChannel)) {
   5440    return false;
   5441  }
   5442 
   5443  nsresult rv;
   5444 
   5445  if (mLoadInfo->GetLoadingPrincipal()) {
   5446    bool sameOrigin = false;
   5447    rv = mLoadInfo->GetLoadingPrincipal()->IsSameOrigin(mURI, &sameOrigin);
   5448    if (NS_FAILED(rv)) {
   5449      return true;
   5450    }
   5451    return !sameOrigin;
   5452  }
   5453  if (!mOriginalURI) {
   5454    return true;
   5455  }
   5456 
   5457  nsIScriptSecurityManager* ssm = nsContentUtils::GetSecurityManager();
   5458  if (!ssm) {
   5459    return true;
   5460  }
   5461 
   5462  rv = ssm->CheckSameOriginURI(mOriginalURI, mURI, false, false);
   5463  return NS_FAILED(rv);
   5464 }
   5465 
   5466 // Redirect Tracking
   5467 bool HttpBaseChannel::SameOriginWithOriginalUri(nsIURI* aURI) {
   5468  nsIScriptSecurityManager* ssm = nsContentUtils::GetSecurityManager();
   5469  bool isPrivateWin = mLoadInfo->GetOriginAttributes().IsPrivateBrowsing();
   5470  nsresult rv =
   5471      ssm->CheckSameOriginURI(aURI, mOriginalURI, false, isPrivateWin);
   5472  return (NS_SUCCEEDED(rv));
   5473 }
   5474 
   5475 //-----------------------------------------------------------------------------
   5476 // HttpBaseChannel::nsIClassifiedChannel
   5477 
   5478 NS_IMETHODIMP
   5479 HttpBaseChannel::GetMatchedList(nsACString& aList) {
   5480  aList = mMatchedList;
   5481  return NS_OK;
   5482 }
   5483 
   5484 NS_IMETHODIMP
   5485 HttpBaseChannel::GetMatchedProvider(nsACString& aProvider) {
   5486  aProvider = mMatchedProvider;
   5487  return NS_OK;
   5488 }
   5489 
   5490 NS_IMETHODIMP
   5491 HttpBaseChannel::GetMatchedFullHash(nsACString& aFullHash) {
   5492  aFullHash = mMatchedFullHash;
   5493  return NS_OK;
   5494 }
   5495 
   5496 NS_IMETHODIMP
   5497 HttpBaseChannel::SetMatchedInfo(const nsACString& aList,
   5498                                const nsACString& aProvider,
   5499                                const nsACString& aFullHash) {
   5500  NS_ENSURE_ARG(!aList.IsEmpty());
   5501 
   5502  mMatchedList = aList;
   5503  mMatchedProvider = aProvider;
   5504  mMatchedFullHash = aFullHash;
   5505  return NS_OK;
   5506 }
   5507 
   5508 NS_IMETHODIMP
   5509 HttpBaseChannel::GetMatchedTrackingLists(nsTArray<nsCString>& aLists) {
   5510  aLists = mMatchedTrackingLists.Clone();
   5511  return NS_OK;
   5512 }
   5513 
   5514 NS_IMETHODIMP
   5515 HttpBaseChannel::GetMatchedTrackingFullHashes(
   5516    nsTArray<nsCString>& aFullHashes) {
   5517  aFullHashes = mMatchedTrackingFullHashes.Clone();
   5518  return NS_OK;
   5519 }
   5520 
   5521 NS_IMETHODIMP
   5522 HttpBaseChannel::SetMatchedTrackingInfo(
   5523    const nsTArray<nsCString>& aLists, const nsTArray<nsCString>& aFullHashes) {
   5524  NS_ENSURE_ARG(!aLists.IsEmpty());
   5525  // aFullHashes can be empty for non hash-matching algorithm, for example,
   5526  // host based test entries in preference.
   5527 
   5528  mMatchedTrackingLists = aLists.Clone();
   5529  mMatchedTrackingFullHashes = aFullHashes.Clone();
   5530  return NS_OK;
   5531 }
   5532 //-----------------------------------------------------------------------------
   5533 // HttpBaseChannel::nsITimedChannel
   5534 //-----------------------------------------------------------------------------
   5535 
   5536 NS_IMETHODIMP
   5537 HttpBaseChannel::GetChannelCreation(TimeStamp* _retval) {
   5538  *_retval = mChannelCreationTimestamp;
   5539  return NS_OK;
   5540 }
   5541 
   5542 NS_IMETHODIMP
   5543 HttpBaseChannel::SetChannelCreation(TimeStamp aValue) {
   5544  MOZ_DIAGNOSTIC_ASSERT(!aValue.IsNull());
   5545  TimeDuration adjust = aValue - mChannelCreationTimestamp;
   5546  mChannelCreationTimestamp = aValue;
   5547  mChannelCreationTime += (PRTime)adjust.ToMicroseconds();
   5548  return NS_OK;
   5549 }
   5550 
   5551 NS_IMETHODIMP
   5552 HttpBaseChannel::GetAsyncOpen(TimeStamp* _retval) {
   5553  *_retval = mAsyncOpenTime;
   5554  return NS_OK;
   5555 }
   5556 
   5557 NS_IMETHODIMP
   5558 HttpBaseChannel::SetAsyncOpen(TimeStamp aValue) {
   5559  MOZ_DIAGNOSTIC_ASSERT(!aValue.IsNull());
   5560  mAsyncOpenTime = aValue;
   5561  StoreAsyncOpenTimeOverriden(true);
   5562  return NS_OK;
   5563 }
   5564 
   5565 /**
   5566 * @return the number of redirects. There is no check for cross-domain
   5567 * redirects. This check must be done by the consumers.
   5568 */
   5569 NS_IMETHODIMP
   5570 HttpBaseChannel::GetRedirectCount(uint8_t* aRedirectCount) {
   5571  *aRedirectCount = mRedirectCount;
   5572  return NS_OK;
   5573 }
   5574 
   5575 NS_IMETHODIMP
   5576 HttpBaseChannel::SetRedirectCount(uint8_t aRedirectCount) {
   5577  mRedirectCount = aRedirectCount;
   5578  return NS_OK;
   5579 }
   5580 
   5581 NS_IMETHODIMP
   5582 HttpBaseChannel::GetInternalRedirectCount(uint8_t* aRedirectCount) {
   5583  *aRedirectCount = mInternalRedirectCount;
   5584  return NS_OK;
   5585 }
   5586 
   5587 NS_IMETHODIMP
   5588 HttpBaseChannel::SetInternalRedirectCount(uint8_t aRedirectCount) {
   5589  mInternalRedirectCount = aRedirectCount;
   5590  return NS_OK;
   5591 }
   5592 
   5593 NS_IMETHODIMP
   5594 HttpBaseChannel::GetRedirectStart(TimeStamp* _retval) {
   5595  *_retval = mRedirectStartTimeStamp;
   5596  return NS_OK;
   5597 }
   5598 
   5599 NS_IMETHODIMP
   5600 HttpBaseChannel::SetRedirectStart(TimeStamp aRedirectStart) {
   5601  mRedirectStartTimeStamp = aRedirectStart;
   5602  return NS_OK;
   5603 }
   5604 
   5605 NS_IMETHODIMP
   5606 HttpBaseChannel::GetRedirectEnd(TimeStamp* _retval) {
   5607  *_retval = mRedirectEndTimeStamp;
   5608  return NS_OK;
   5609 }
   5610 
   5611 NS_IMETHODIMP
   5612 HttpBaseChannel::SetRedirectEnd(TimeStamp aRedirectEnd) {
   5613  mRedirectEndTimeStamp = aRedirectEnd;
   5614  return NS_OK;
   5615 }
   5616 
   5617 NS_IMETHODIMP
   5618 HttpBaseChannel::GetAllRedirectsSameOrigin(bool* aAllRedirectsSameOrigin) {
   5619  *aAllRedirectsSameOrigin = LoadAllRedirectsSameOrigin();
   5620  return NS_OK;
   5621 }
   5622 
   5623 NS_IMETHODIMP
   5624 HttpBaseChannel::SetAllRedirectsSameOrigin(bool aAllRedirectsSameOrigin) {
   5625  StoreAllRedirectsSameOrigin(aAllRedirectsSameOrigin);
   5626  return NS_OK;
   5627 }
   5628 
   5629 NS_IMETHODIMP
   5630 HttpBaseChannel::GetAllRedirectsPassTimingAllowCheck(bool* aPassesCheck) {
   5631  *aPassesCheck = LoadAllRedirectsPassTimingAllowCheck();
   5632  return NS_OK;
   5633 }
   5634 
   5635 NS_IMETHODIMP
   5636 HttpBaseChannel::SetAllRedirectsPassTimingAllowCheck(bool aPassesCheck) {
   5637  StoreAllRedirectsPassTimingAllowCheck(aPassesCheck);
   5638  return NS_OK;
   5639 }
   5640 
   5641 // https://fetch.spec.whatwg.org/#cors-check
   5642 bool HttpBaseChannel::PerformCORSCheck() {
   5643  // Step 1
   5644  // Let origin be the result of getting `Access-Control-Allow-Origin`
   5645  // from response’s header list.
   5646  nsAutoCString origin;
   5647  nsresult rv = GetResponseHeader("Access-Control-Allow-Origin"_ns, origin);
   5648 
   5649  // Step 2
   5650  // If origin is null, then return failure. (Note: null, not 'null').
   5651  if (NS_FAILED(rv) || origin.IsVoid()) {
   5652    return false;
   5653  }
   5654 
   5655  // Step 3
   5656  // If request’s credentials mode is not "include"
   5657  // and origin is `*`, then return success.
   5658  uint32_t cookiePolicy = mLoadInfo->GetCookiePolicy();
   5659  if (cookiePolicy != nsILoadInfo::SEC_COOKIES_INCLUDE &&
   5660      origin.EqualsLiteral("*")) {
   5661    return true;
   5662  }
   5663 
   5664  // Step 4
   5665  // If the result of byte-serializing a request origin
   5666  // with request is not origin, then return failure.
   5667  nsIScriptSecurityManager* ssm = nsContentUtils::GetSecurityManager();
   5668  nsCOMPtr<nsIPrincipal> resourcePrincipal;
   5669  rv = ssm->GetChannelURIPrincipal(this, getter_AddRefs(resourcePrincipal));
   5670  if (NS_FAILED(rv) || !resourcePrincipal) {
   5671    return false;
   5672  }
   5673  nsAutoCString serializedOrigin;
   5674  nsContentSecurityManager::GetSerializedOrigin(
   5675      mLoadInfo->TriggeringPrincipal(), resourcePrincipal, serializedOrigin,
   5676      mLoadInfo);
   5677  if (!serializedOrigin.Equals(origin)) {
   5678    return false;
   5679  }
   5680 
   5681  // Step 5
   5682  // If request’s credentials mode is not "include", then return success.
   5683  if (cookiePolicy != nsILoadInfo::SEC_COOKIES_INCLUDE) {
   5684    return true;
   5685  }
   5686 
   5687  // Step 6
   5688  // Let credentials be the result of getting
   5689  // `Access-Control-Allow-Credentials` from response’s header list.
   5690  nsAutoCString credentials;
   5691  rv = GetResponseHeader("Access-Control-Allow-Credentials"_ns, credentials);
   5692 
   5693  // Step 7 and 8
   5694  // If credentials is `true`, then return success.
   5695  // (else) return failure.
   5696  return NS_SUCCEEDED(rv) && credentials.EqualsLiteral("true");
   5697 }
   5698 
   5699 NS_IMETHODIMP
   5700 HttpBaseChannel::BodyInfoAccessAllowedCheck(nsIPrincipal* aOrigin,
   5701                                            BodyInfoAccess* _retval) {
   5702  // Per the Fetch spec, https://fetch.spec.whatwg.org/#response-body-info,
   5703  // the bodyInfo for Resource Timing and Navigation Timing info consists of
   5704  // encoded size, decoded size, and content type. It is however made opaque
   5705  // whenever the response is turned into a network error, which sets its
   5706  // bodyInfo to its default values (sizes=0, content-type="").
   5707 
   5708  // Case 1:
   5709  // "no-cors" -> Upon success, fetch will return an opaque filtered response.
   5710  // An opaque(-redirect) filtered response is a filtered response
   5711  //   whose ... body info is a new response body info.
   5712  auto tainting = mLoadInfo->GetTainting();
   5713  if (tainting == mozilla::LoadTainting::Opaque) {
   5714    *_retval = BodyInfoAccess::DISALLOWED;
   5715    return NS_OK;
   5716  }
   5717 
   5718  // Case 2:
   5719  // If request’s response tainting is "cors" and a CORS check for request
   5720  // and response returns failure, then return a network error.
   5721  if (tainting == mozilla::LoadTainting::CORS && !PerformCORSCheck()) {
   5722    *_retval = BodyInfoAccess::DISALLOWED;
   5723    return NS_OK;
   5724  }
   5725 
   5726  // Otherwise:
   5727  // The fetch response handover, given a fetch params fetchParams
   5728  //    and a response response, run these steps:
   5729  // processResponseEndOfBody:
   5730  // - If fetchParams’s request’s mode is not "navigate" or response’s
   5731  //   has-cross-origin-redirects is false:
   5732  //   - Let mimeType be the result of extracting a MIME type from
   5733  //     response’s header list.
   5734  //   - If mimeType is not failure, then set bodyInfo’s content type to the
   5735  //     result of minimizing a supported MIME type given mimeType.
   5736  dom::RequestMode requestMode;
   5737  MOZ_ALWAYS_SUCCEEDS(GetRequestMode(&requestMode));
   5738  if (requestMode != RequestMode::Navigate || LoadAllRedirectsSameOrigin()) {
   5739    *_retval = BodyInfoAccess::ALLOW_ALL;
   5740    return NS_OK;
   5741  }
   5742 
   5743  *_retval = BodyInfoAccess::ALLOW_SIZES;
   5744  return NS_OK;
   5745 }
   5746 
   5747 // https://fetch.spec.whatwg.org/#tao-check
   5748 NS_IMETHODIMP
   5749 HttpBaseChannel::TimingAllowCheck(nsIPrincipal* aOrigin, bool* _retval) {
   5750  nsIScriptSecurityManager* ssm = nsContentUtils::GetSecurityManager();
   5751  nsCOMPtr<nsIPrincipal> resourcePrincipal;
   5752  nsresult rv =
   5753      ssm->GetChannelURIPrincipal(this, getter_AddRefs(resourcePrincipal));
   5754  if (NS_FAILED(rv) || !resourcePrincipal || !aOrigin) {
   5755    *_retval = false;
   5756    return NS_OK;
   5757  }
   5758 
   5759  bool sameOrigin = false;
   5760  rv = resourcePrincipal->Equals(aOrigin, &sameOrigin);
   5761 
   5762  nsAutoCString serializedOrigin;
   5763  nsContentSecurityManager::GetSerializedOrigin(aOrigin, resourcePrincipal,
   5764                                                serializedOrigin, mLoadInfo);
   5765 
   5766  // All redirects are same origin
   5767  if (sameOrigin && (!serializedOrigin.IsEmpty() &&
   5768                     !serializedOrigin.EqualsLiteral("null"))) {
   5769    *_retval = true;
   5770    return NS_OK;
   5771  }
   5772 
   5773  nsAutoCString headerValue;
   5774  rv = GetResponseHeader("Timing-Allow-Origin"_ns, headerValue);
   5775  if (NS_FAILED(rv)) {
   5776    *_retval = false;
   5777    return NS_OK;
   5778  }
   5779 
   5780  Tokenizer p(headerValue);
   5781  Tokenizer::Token t;
   5782 
   5783  p.Record();
   5784  nsAutoCString headerItem;
   5785  while (p.Next(t)) {
   5786    if (t.Type() == Tokenizer::TOKEN_EOF ||
   5787        t.Equals(Tokenizer::Token::Char(','))) {
   5788      p.Claim(headerItem);
   5789      nsHttp::TrimHTTPWhitespace(headerItem, headerItem);
   5790      // If the list item contains a case-sensitive match for the value of the
   5791      // origin, or a wildcard, return pass
   5792      if (headerItem == serializedOrigin || headerItem == "*") {
   5793        *_retval = true;
   5794        return NS_OK;
   5795      }
   5796      // We start recording again for the following items in the list
   5797      p.Record();
   5798    }
   5799  }
   5800 
   5801  *_retval = false;
   5802  return NS_OK;
   5803 }
   5804 
   5805 NS_IMETHODIMP
   5806 HttpBaseChannel::GetLaunchServiceWorkerStart(TimeStamp* _retval) {
   5807  MOZ_ASSERT(_retval);
   5808  *_retval = mLaunchServiceWorkerStart;
   5809  return NS_OK;
   5810 }
   5811 
   5812 NS_IMETHODIMP
   5813 HttpBaseChannel::SetLaunchServiceWorkerStart(TimeStamp aTimeStamp) {
   5814  mLaunchServiceWorkerStart = aTimeStamp;
   5815  return NS_OK;
   5816 }
   5817 
   5818 NS_IMETHODIMP
   5819 HttpBaseChannel::GetLaunchServiceWorkerEnd(TimeStamp* _retval) {
   5820  MOZ_ASSERT(_retval);
   5821  *_retval = mLaunchServiceWorkerEnd;
   5822  return NS_OK;
   5823 }
   5824 
   5825 NS_IMETHODIMP
   5826 HttpBaseChannel::SetLaunchServiceWorkerEnd(TimeStamp aTimeStamp) {
   5827  mLaunchServiceWorkerEnd = aTimeStamp;
   5828  return NS_OK;
   5829 }
   5830 
   5831 NS_IMETHODIMP
   5832 HttpBaseChannel::GetDispatchFetchEventStart(TimeStamp* _retval) {
   5833  MOZ_ASSERT(_retval);
   5834  *_retval = mDispatchFetchEventStart;
   5835  return NS_OK;
   5836 }
   5837 
   5838 NS_IMETHODIMP
   5839 HttpBaseChannel::SetDispatchFetchEventStart(TimeStamp aTimeStamp) {
   5840  mDispatchFetchEventStart = aTimeStamp;
   5841  return NS_OK;
   5842 }
   5843 
   5844 NS_IMETHODIMP
   5845 HttpBaseChannel::GetDispatchFetchEventEnd(TimeStamp* _retval) {
   5846  MOZ_ASSERT(_retval);
   5847  *_retval = mDispatchFetchEventEnd;
   5848  return NS_OK;
   5849 }
   5850 
   5851 NS_IMETHODIMP
   5852 HttpBaseChannel::SetDispatchFetchEventEnd(TimeStamp aTimeStamp) {
   5853  mDispatchFetchEventEnd = aTimeStamp;
   5854  return NS_OK;
   5855 }
   5856 
   5857 NS_IMETHODIMP
   5858 HttpBaseChannel::GetHandleFetchEventStart(TimeStamp* _retval) {
   5859  MOZ_ASSERT(_retval);
   5860  *_retval = mHandleFetchEventStart;
   5861  return NS_OK;
   5862 }
   5863 
   5864 NS_IMETHODIMP
   5865 HttpBaseChannel::SetHandleFetchEventStart(TimeStamp aTimeStamp) {
   5866  mHandleFetchEventStart = aTimeStamp;
   5867  return NS_OK;
   5868 }
   5869 
   5870 NS_IMETHODIMP
   5871 HttpBaseChannel::GetHandleFetchEventEnd(TimeStamp* _retval) {
   5872  MOZ_ASSERT(_retval);
   5873  *_retval = mHandleFetchEventEnd;
   5874  return NS_OK;
   5875 }
   5876 
   5877 NS_IMETHODIMP
   5878 HttpBaseChannel::SetHandleFetchEventEnd(TimeStamp aTimeStamp) {
   5879  mHandleFetchEventEnd = aTimeStamp;
   5880  return NS_OK;
   5881 }
   5882 
   5883 NS_IMETHODIMP
   5884 HttpBaseChannel::GetDomainLookupStart(TimeStamp* _retval) {
   5885  *_retval = mTransactionTimings.domainLookupStart;
   5886  return NS_OK;
   5887 }
   5888 
   5889 NS_IMETHODIMP
   5890 HttpBaseChannel::GetDomainLookupEnd(TimeStamp* _retval) {
   5891  *_retval = mTransactionTimings.domainLookupEnd;
   5892  return NS_OK;
   5893 }
   5894 
   5895 NS_IMETHODIMP
   5896 HttpBaseChannel::GetConnectStart(TimeStamp* _retval) {
   5897  *_retval = mTransactionTimings.connectStart;
   5898  return NS_OK;
   5899 }
   5900 
   5901 NS_IMETHODIMP
   5902 HttpBaseChannel::GetTcpConnectEnd(TimeStamp* _retval) {
   5903  *_retval = mTransactionTimings.tcpConnectEnd;
   5904  return NS_OK;
   5905 }
   5906 
   5907 NS_IMETHODIMP
   5908 HttpBaseChannel::GetSecureConnectionStart(TimeStamp* _retval) {
   5909  *_retval = mTransactionTimings.secureConnectionStart;
   5910  return NS_OK;
   5911 }
   5912 
   5913 NS_IMETHODIMP
   5914 HttpBaseChannel::GetConnectEnd(TimeStamp* _retval) {
   5915  *_retval = mTransactionTimings.connectEnd;
   5916  return NS_OK;
   5917 }
   5918 
   5919 NS_IMETHODIMP
   5920 HttpBaseChannel::GetRequestStart(TimeStamp* _retval) {
   5921  *_retval = mTransactionTimings.requestStart;
   5922  return NS_OK;
   5923 }
   5924 
   5925 NS_IMETHODIMP
   5926 HttpBaseChannel::GetResponseStart(TimeStamp* _retval) {
   5927  *_retval = mTransactionTimings.responseStart;
   5928  return NS_OK;
   5929 }
   5930 
   5931 NS_IMETHODIMP
   5932 HttpBaseChannel::GetResponseEnd(TimeStamp* _retval) {
   5933  *_retval = mTransactionTimings.responseEnd;
   5934  return NS_OK;
   5935 }
   5936 
   5937 NS_IMETHODIMP
   5938 HttpBaseChannel::GetCacheReadStart(TimeStamp* _retval) {
   5939  *_retval = mCacheReadStart;
   5940  return NS_OK;
   5941 }
   5942 
   5943 NS_IMETHODIMP
   5944 HttpBaseChannel::GetCacheReadEnd(TimeStamp* _retval) {
   5945  *_retval = mCacheReadEnd;
   5946  return NS_OK;
   5947 }
   5948 
   5949 NS_IMETHODIMP
   5950 HttpBaseChannel::GetTransactionPending(TimeStamp* _retval) {
   5951  *_retval = mTransactionTimings.transactionPending;
   5952  return NS_OK;
   5953 }
   5954 
   5955 NS_IMETHODIMP
   5956 HttpBaseChannel::GetInitiatorType(nsAString& aInitiatorType) {
   5957  aInitiatorType = mInitiatorType;
   5958  return NS_OK;
   5959 }
   5960 
   5961 NS_IMETHODIMP
   5962 HttpBaseChannel::SetInitiatorType(const nsAString& aInitiatorType) {
   5963  mInitiatorType = aInitiatorType;
   5964  return NS_OK;
   5965 }
   5966 
   5967 #define IMPL_TIMING_ATTR(name)                                           \
   5968  NS_IMETHODIMP                                                          \
   5969  HttpBaseChannel::Get##name##Time(PRTime* _retval) {                    \
   5970    TimeStamp stamp;                                                     \
   5971    Get##name(&stamp);                                                   \
   5972    if (stamp.IsNull()) {                                                \
   5973      *_retval = 0;                                                      \
   5974      return NS_OK;                                                      \
   5975    }                                                                    \
   5976    *_retval =                                                           \
   5977        mChannelCreationTime +                                           \
   5978        (PRTime)((stamp - mChannelCreationTimestamp).ToSeconds() * 1e6); \
   5979    return NS_OK;                                                        \
   5980  }
   5981 
   5982 IMPL_TIMING_ATTR(ChannelCreation)
   5983 IMPL_TIMING_ATTR(AsyncOpen)
   5984 IMPL_TIMING_ATTR(LaunchServiceWorkerStart)
   5985 IMPL_TIMING_ATTR(LaunchServiceWorkerEnd)
   5986 IMPL_TIMING_ATTR(DispatchFetchEventStart)
   5987 IMPL_TIMING_ATTR(DispatchFetchEventEnd)
   5988 IMPL_TIMING_ATTR(HandleFetchEventStart)
   5989 IMPL_TIMING_ATTR(HandleFetchEventEnd)
   5990 IMPL_TIMING_ATTR(DomainLookupStart)
   5991 IMPL_TIMING_ATTR(DomainLookupEnd)
   5992 IMPL_TIMING_ATTR(ConnectStart)
   5993 IMPL_TIMING_ATTR(TcpConnectEnd)
   5994 IMPL_TIMING_ATTR(SecureConnectionStart)
   5995 IMPL_TIMING_ATTR(ConnectEnd)
   5996 IMPL_TIMING_ATTR(RequestStart)
   5997 IMPL_TIMING_ATTR(ResponseStart)
   5998 IMPL_TIMING_ATTR(ResponseEnd)
   5999 IMPL_TIMING_ATTR(CacheReadStart)
   6000 IMPL_TIMING_ATTR(CacheReadEnd)
   6001 IMPL_TIMING_ATTR(RedirectStart)
   6002 IMPL_TIMING_ATTR(RedirectEnd)
   6003 IMPL_TIMING_ATTR(TransactionPending)
   6004 
   6005 #undef IMPL_TIMING_ATTR
   6006 
   6007 void HttpBaseChannel::MaybeReportTimingData() {
   6008  // There is no point in continuing, since the performance object in the parent
   6009  // isn't the same as the one in the child which will be reporting resource
   6010  // performance.
   6011  if (XRE_IsE10sParentProcess()) {
   6012    return;
   6013  }
   6014 
   6015  // Devtools can create fetch requests on behalf the content document.
   6016  // If we don't exclude these requests, they'd also be reported
   6017  // to the content document.
   6018  bool isInDevToolsContext;
   6019  mLoadInfo->GetIsInDevToolsContext(&isInDevToolsContext);
   6020  if (isInDevToolsContext) {
   6021    return;
   6022  }
   6023 
   6024  mozilla::dom::PerformanceStorage* documentPerformance =
   6025      mLoadInfo->GetPerformanceStorage();
   6026  if (documentPerformance) {
   6027    documentPerformance->AddEntry(this, this);
   6028    return;
   6029  }
   6030 
   6031  if (!nsGlobalWindowInner::GetInnerWindowWithId(
   6032          mLoadInfo->GetInnerWindowID())) {
   6033    // The inner window is in a different process.
   6034    dom::ContentChild* child = dom::ContentChild::GetSingleton();
   6035 
   6036    if (!child) {
   6037      return;
   6038    }
   6039    nsAutoString initiatorType;
   6040    nsAutoString entryName;
   6041 
   6042    UniquePtr<dom::PerformanceTimingData> performanceTimingData(
   6043        dom::PerformanceTimingData::Create(this, this, 0, initiatorType,
   6044                                           entryName));
   6045    if (!performanceTimingData) {
   6046      return;
   6047    }
   6048 
   6049    LoadInfoArgs loadInfoArgs;
   6050    mozilla::ipc::LoadInfoToLoadInfoArgs(mLoadInfo, &loadInfoArgs);
   6051    child->SendReportFrameTimingData(loadInfoArgs, entryName, initiatorType,
   6052                                     std::move(performanceTimingData));
   6053  }
   6054 }
   6055 
   6056 NS_IMETHODIMP
   6057 HttpBaseChannel::SetReportResourceTiming(bool enabled) {
   6058  StoreReportTiming(enabled);
   6059  return NS_OK;
   6060 }
   6061 
   6062 NS_IMETHODIMP
   6063 HttpBaseChannel::GetReportResourceTiming(bool* _retval) {
   6064  *_retval = LoadReportTiming();
   6065  return NS_OK;
   6066 }
   6067 
   6068 nsIURI* HttpBaseChannel::GetReferringPage() {
   6069  nsCOMPtr<nsPIDOMWindowInner> pDomWindow = GetInnerDOMWindow();
   6070  if (!pDomWindow) {
   6071    return nullptr;
   6072  }
   6073  return pDomWindow->GetDocumentURI();
   6074 }
   6075 
   6076 nsPIDOMWindowInner* HttpBaseChannel::GetInnerDOMWindow() {
   6077  nsCOMPtr<nsILoadContext> loadContext;
   6078  NS_QueryNotificationCallbacks(this, loadContext);
   6079  if (!loadContext) {
   6080    return nullptr;
   6081  }
   6082  nsCOMPtr<mozIDOMWindowProxy> domWindow;
   6083  loadContext->GetAssociatedWindow(getter_AddRefs(domWindow));
   6084  if (!domWindow) {
   6085    return nullptr;
   6086  }
   6087  auto* pDomWindow = nsPIDOMWindowOuter::From(domWindow);
   6088  if (!pDomWindow) {
   6089    return nullptr;
   6090  }
   6091  nsCOMPtr<nsPIDOMWindowInner> innerWindow =
   6092      pDomWindow->GetCurrentInnerWindow();
   6093  if (!innerWindow) {
   6094    return nullptr;
   6095  }
   6096 
   6097  return innerWindow;
   6098 }
   6099 
   6100 //-----------------------------------------------------------------------------
   6101 // HttpBaseChannel::nsIThrottledInputChannel
   6102 //-----------------------------------------------------------------------------
   6103 
   6104 NS_IMETHODIMP
   6105 HttpBaseChannel::SetThrottleQueue(nsIInputChannelThrottleQueue* aQueue) {
   6106  if (!XRE_IsParentProcess()) {
   6107    return NS_ERROR_FAILURE;
   6108  }
   6109 
   6110  mThrottleQueue = aQueue;
   6111  return NS_OK;
   6112 }
   6113 
   6114 NS_IMETHODIMP
   6115 HttpBaseChannel::GetThrottleQueue(nsIInputChannelThrottleQueue** aQueue) {
   6116  NS_ENSURE_ARG_POINTER(aQueue);
   6117  nsCOMPtr<nsIInputChannelThrottleQueue> queue = mThrottleQueue;
   6118  queue.forget(aQueue);
   6119  return NS_OK;
   6120 }
   6121 
   6122 //------------------------------------------------------------------------------
   6123 
   6124 bool HttpBaseChannel::EnsureRequestContextID() {
   6125  if (mRequestContextID) {
   6126    // Already have a request context ID, no need to do the rest of this work
   6127    LOG(("HttpBaseChannel::EnsureRequestContextID this=%p id=%" PRIx64, this,
   6128         mRequestContextID));
   6129    return true;
   6130  }
   6131 
   6132  // Find the loadgroup at the end of the chain in order
   6133  // to make sure all channels derived from the load group
   6134  // use the same connection scope.
   6135  nsCOMPtr<nsILoadGroupChild> childLoadGroup = do_QueryInterface(mLoadGroup);
   6136  if (!childLoadGroup) {
   6137    return false;
   6138  }
   6139 
   6140  nsCOMPtr<nsILoadGroup> rootLoadGroup;
   6141  childLoadGroup->GetRootLoadGroup(getter_AddRefs(rootLoadGroup));
   6142  if (!rootLoadGroup) {
   6143    return false;
   6144  }
   6145 
   6146  // Set the load group connection scope on this channel and its transaction
   6147  rootLoadGroup->GetRequestContextID(&mRequestContextID);
   6148 
   6149  LOG(("HttpBaseChannel::EnsureRequestContextID this=%p id=%" PRIx64, this,
   6150       mRequestContextID));
   6151 
   6152  return true;
   6153 }
   6154 
   6155 bool HttpBaseChannel::EnsureRequestContext() {
   6156  if (mRequestContext) {
   6157    // Already have a request context, no need to do the rest of this work
   6158    return true;
   6159  }
   6160 
   6161  if (!EnsureRequestContextID()) {
   6162    return false;
   6163  }
   6164 
   6165  nsIRequestContextService* rcsvc = gHttpHandler->GetRequestContextService();
   6166  if (!rcsvc) {
   6167    return false;
   6168  }
   6169 
   6170  rcsvc->GetRequestContext(mRequestContextID, getter_AddRefs(mRequestContext));
   6171  return static_cast<bool>(mRequestContext);
   6172 }
   6173 
   6174 void HttpBaseChannel::EnsureBrowserId() {
   6175  if (mBrowserId) {
   6176    return;
   6177  }
   6178 
   6179  RefPtr<dom::BrowsingContext> bc;
   6180  MOZ_ALWAYS_SUCCEEDS(mLoadInfo->GetBrowsingContext(getter_AddRefs(bc)));
   6181 
   6182  if (bc) {
   6183    mBrowserId = bc->GetBrowserId();
   6184  }
   6185 }
   6186 
   6187 void HttpBaseChannel::SetCorsPreflightParameters(
   6188    const nsTArray<nsCString>& aUnsafeHeaders,
   6189    bool aShouldStripRequestBodyHeader, bool aShouldStripAuthHeader) {
   6190  MOZ_RELEASE_ASSERT(!LoadRequestObserversCalled());
   6191 
   6192  StoreRequireCORSPreflight(true);
   6193  mUnsafeHeaders = aUnsafeHeaders.Clone();
   6194  if (aShouldStripRequestBodyHeader || aShouldStripAuthHeader) {
   6195    mUnsafeHeaders.RemoveElementsBy([&](const nsCString& aHeader) {
   6196      return (aShouldStripRequestBodyHeader &&
   6197              (aHeader.LowerCaseEqualsASCII("content-type") ||
   6198               aHeader.LowerCaseEqualsASCII("content-encoding") ||
   6199               aHeader.LowerCaseEqualsASCII("content-language") ||
   6200               aHeader.LowerCaseEqualsASCII("content-location"))) ||
   6201             (aShouldStripAuthHeader &&
   6202              aHeader.LowerCaseEqualsASCII("authorization"));
   6203    });
   6204  }
   6205 }
   6206 
   6207 void HttpBaseChannel::SetAltDataForChild(bool aIsForChild) {
   6208  StoreAltDataForChild(aIsForChild);
   6209 }
   6210 
   6211 NS_IMETHODIMP
   6212 HttpBaseChannel::GetBlockAuthPrompt(bool* aValue) {
   6213  if (!aValue) {
   6214    return NS_ERROR_FAILURE;
   6215  }
   6216 
   6217  *aValue = LoadBlockAuthPrompt();
   6218  return NS_OK;
   6219 }
   6220 
   6221 NS_IMETHODIMP
   6222 HttpBaseChannel::SetBlockAuthPrompt(bool aValue) {
   6223  ENSURE_CALLED_BEFORE_CONNECT();
   6224 
   6225  StoreBlockAuthPrompt(aValue);
   6226  return NS_OK;
   6227 }
   6228 
   6229 NS_IMETHODIMP
   6230 HttpBaseChannel::GetConnectionInfoHashKey(nsACString& aConnectionInfoHashKey) {
   6231  if (!mConnectionInfo) {
   6232    return NS_ERROR_FAILURE;
   6233  }
   6234  aConnectionInfoHashKey.Assign(mConnectionInfo->HashKey());
   6235  return NS_OK;
   6236 }
   6237 
   6238 NS_IMETHODIMP
   6239 HttpBaseChannel::GetLastRedirectFlags(uint32_t* aValue) {
   6240  NS_ENSURE_ARG(aValue);
   6241  *aValue = mLastRedirectFlags;
   6242  return NS_OK;
   6243 }
   6244 
   6245 NS_IMETHODIMP
   6246 HttpBaseChannel::SetLastRedirectFlags(uint32_t aValue) {
   6247  mLastRedirectFlags = aValue;
   6248  return NS_OK;
   6249 }
   6250 
   6251 NS_IMETHODIMP
   6252 HttpBaseChannel::GetNavigationStartTimeStamp(TimeStamp* aTimeStamp) {
   6253  return NS_ERROR_NOT_IMPLEMENTED;
   6254 }
   6255 
   6256 NS_IMETHODIMP
   6257 HttpBaseChannel::SetNavigationStartTimeStamp(TimeStamp aTimeStamp) {
   6258  return NS_ERROR_NOT_IMPLEMENTED;
   6259 }
   6260 
   6261 nsresult HttpBaseChannel::CheckRedirectLimit(nsIURI* aNewURI,
   6262                                             uint32_t aRedirectFlags) const {
   6263  if (aRedirectFlags & nsIChannelEventSink::REDIRECT_INTERNAL) {
   6264    // for internal redirect due to auth retry we do not have any limit
   6265    // as we might restrict the number of times a user might retry
   6266    // authentication
   6267    if (aRedirectFlags & nsIChannelEventSink::REDIRECT_AUTH_RETRY) {
   6268      return NS_OK;
   6269    }
   6270    // Some platform features, like Service Workers, depend on internal
   6271    // redirects.  We should allow some number of internal redirects above
   6272    // and beyond the normal redirect limit so these features continue
   6273    // to work.
   6274    static const int8_t kMinInternalRedirects = 5;
   6275 
   6276    if (mInternalRedirectCount >= (mRedirectionLimit + kMinInternalRedirects)) {
   6277      LOG(("internal redirection limit reached!\n"));
   6278      return NS_ERROR_REDIRECT_LOOP;
   6279    }
   6280    return NS_OK;
   6281  }
   6282 
   6283  MOZ_ASSERT(aRedirectFlags & (nsIChannelEventSink::REDIRECT_TEMPORARY |
   6284                               nsIChannelEventSink::REDIRECT_PERMANENT |
   6285                               nsIChannelEventSink::REDIRECT_STS_UPGRADE));
   6286 
   6287  if (mRedirectCount >= mRedirectionLimit) {
   6288    LOG(("redirection limit reached!\n"));
   6289    return NS_ERROR_REDIRECT_LOOP;
   6290  }
   6291 
   6292  // in case https-only mode is enabled which upgrades top-level requests to
   6293  // https and the page answers with a redirect (meta, 302, win.location, ...)
   6294  // then this method can break the cycle which causes the https-only exception
   6295  // page to appear. Note that https-first mode breaks upgrade downgrade endless
   6296  // loops within ShouldUpgradeHttpsFirstRequest because https-first does not
   6297  // display an exception page but needs a soft fallback/downgrade.
   6298  if (nsHTTPSOnlyUtils::IsUpgradeDowngradeEndlessLoop(
   6299          mURI, aNewURI, mLoadInfo,
   6300          {nsHTTPSOnlyUtils::UpgradeDowngradeEndlessLoopOptions::
   6301               EnforceForHTTPSOnlyMode})) {
   6302    // Mark that we didn't upgrade to https due to loop detection in https-only
   6303    // mode to show https-only error page. We know that we are in https-only
   6304    // mode, because we passed `EnforceForHTTPSOnlyMode` to
   6305    // `IsUpgradeDowngradeEndlessLoop`. In other words we upgrade the request
   6306    // with https-only mode, but then immediately cancel the request.
   6307    uint32_t httpsOnlyStatus = mLoadInfo->GetHttpsOnlyStatus();
   6308    if (httpsOnlyStatus & nsILoadInfo::HTTPS_ONLY_UNINITIALIZED) {
   6309      httpsOnlyStatus ^= nsILoadInfo::HTTPS_ONLY_UNINITIALIZED;
   6310      httpsOnlyStatus |=
   6311          nsILoadInfo::HTTPS_ONLY_UPGRADED_LISTENER_NOT_REGISTERED;
   6312      mLoadInfo->SetHttpsOnlyStatus(httpsOnlyStatus);
   6313    }
   6314 
   6315    LOG(("upgrade downgrade redirect loop!\n"));
   6316    return NS_ERROR_REDIRECT_LOOP;
   6317  }
   6318  // in case of http-first mode we want to add an exception to disable the
   6319  // upgrade behavior if we have upgrade-downgrade loop to break the loop and
   6320  // load the http request next
   6321  if (mozilla::StaticPrefs::
   6322          dom_security_https_first_add_exception_on_failure() &&
   6323      nsHTTPSOnlyUtils::IsUpgradeDowngradeEndlessLoop(
   6324          mURI, aNewURI, mLoadInfo,
   6325          {nsHTTPSOnlyUtils::UpgradeDowngradeEndlessLoopOptions::
   6326               EnforceForHTTPSFirstMode})) {
   6327    nsHTTPSOnlyUtils::AddHTTPSFirstException(mURI, mLoadInfo);
   6328  }
   6329 
   6330  return NS_OK;
   6331 }
   6332 
   6333 // NOTE: This function duplicates code from nsBaseChannel. This will go away
   6334 // once HTTP uses nsBaseChannel (part of bug 312760)
   6335 /* static */
   6336 void HttpBaseChannel::CallTypeSniffers(void* aClosure, const uint8_t* aData,
   6337                                       uint32_t aCount) {
   6338  nsIChannel* chan = static_cast<nsIChannel*>(aClosure);
   6339  const char* snifferType = [chan]() {
   6340    if (RefPtr<nsHttpChannel> httpChannel = do_QueryObject(chan)) {
   6341      switch (httpChannel->GetSnifferCategoryType()) {
   6342        case SnifferCategoryType::NetContent:
   6343          return NS_CONTENT_SNIFFER_CATEGORY;
   6344        case SnifferCategoryType::OpaqueResponseBlocking:
   6345          return NS_ORB_SNIFFER_CATEGORY;
   6346        case SnifferCategoryType::All:
   6347          return NS_CONTENT_AND_ORB_SNIFFER_CATEGORY;
   6348        default:
   6349          MOZ_ASSERT_UNREACHABLE("Unexpected SnifferCategoryType!");
   6350      }
   6351    }
   6352 
   6353    return NS_CONTENT_SNIFFER_CATEGORY;
   6354  }();
   6355 
   6356  nsAutoCString newType;
   6357  NS_SniffContent(snifferType, chan, aData, aCount, newType);
   6358  if (!newType.IsEmpty()) {
   6359    chan->SetContentType(newType);
   6360  }
   6361 }
   6362 
   6363 template <class T>
   6364 static void ParseServerTimingHeader(
   6365    const UniquePtr<T>& aHeader, nsTArray<nsCOMPtr<nsIServerTiming>>& aOutput) {
   6366  if (!aHeader) {
   6367    return;
   6368  }
   6369 
   6370  nsAutoCString serverTimingHeader;
   6371  (void)aHeader->GetHeader(nsHttp::Server_Timing, serverTimingHeader);
   6372  if (serverTimingHeader.IsEmpty()) {
   6373    return;
   6374  }
   6375 
   6376  ServerTimingParser parser(serverTimingHeader);
   6377  parser.Parse();
   6378 
   6379  nsTArray<nsCOMPtr<nsIServerTiming>> array = parser.TakeServerTimingHeaders();
   6380  aOutput.AppendElements(array);
   6381 }
   6382 
   6383 NS_IMETHODIMP
   6384 HttpBaseChannel::GetServerTiming(nsIArray** aServerTiming) {
   6385  nsresult rv;
   6386  NS_ENSURE_ARG_POINTER(aServerTiming);
   6387 
   6388  nsCOMPtr<nsIMutableArray> array = do_CreateInstance(NS_ARRAY_CONTRACTID, &rv);
   6389  NS_ENSURE_SUCCESS(rv, rv);
   6390 
   6391  nsTArray<nsCOMPtr<nsIServerTiming>> data;
   6392  rv = GetNativeServerTiming(data);
   6393  NS_ENSURE_SUCCESS(rv, rv);
   6394 
   6395  for (const auto& entry : data) {
   6396    array->AppendElement(entry);
   6397  }
   6398 
   6399  array.forget(aServerTiming);
   6400  return NS_OK;
   6401 }
   6402 
   6403 NS_IMETHODIMP
   6404 HttpBaseChannel::GetNativeServerTiming(
   6405    nsTArray<nsCOMPtr<nsIServerTiming>>& aServerTiming) {
   6406  aServerTiming.Clear();
   6407 
   6408  if (nsContentUtils::ComputeIsSecureContext(this)) {
   6409    ParseServerTimingHeader(mResponseHead, aServerTiming);
   6410    ParseServerTimingHeader(mResponseTrailers, aServerTiming);
   6411  }
   6412 
   6413  return NS_OK;
   6414 }
   6415 
   6416 NS_IMETHODIMP
   6417 HttpBaseChannel::CancelByURLClassifier(nsresult aErrorCode) {
   6418  MOZ_ASSERT(
   6419      UrlClassifierFeatureFactory::IsClassifierBlockingErrorCode(aErrorCode));
   6420  return Cancel(aErrorCode);
   6421 }
   6422 
   6423 NS_IMETHODIMP HttpBaseChannel::SetIPv4Disabled() {
   6424  mCaps |= NS_HTTP_DISABLE_IPV4;
   6425  return NS_OK;
   6426 }
   6427 
   6428 NS_IMETHODIMP HttpBaseChannel::SetIPv6Disabled() {
   6429  mCaps |= NS_HTTP_DISABLE_IPV6;
   6430  return NS_OK;
   6431 }
   6432 
   6433 NS_IMETHODIMP HttpBaseChannel::GetResponseEmbedderPolicy(
   6434    bool aIsOriginTrialCoepCredentiallessEnabled,
   6435    nsILoadInfo::CrossOriginEmbedderPolicy* aOutPolicy) {
   6436  *aOutPolicy = nsILoadInfo::EMBEDDER_POLICY_NULL;
   6437  if (!mResponseHead) {
   6438    return NS_ERROR_NOT_AVAILABLE;
   6439  }
   6440 
   6441  if (!nsContentUtils::ComputeIsSecureContext(this)) {
   6442    // Feature is only available for secure contexts.
   6443    return NS_OK;
   6444  }
   6445 
   6446  nsAutoCString content;
   6447  (void)mResponseHead->GetHeader(nsHttp::Cross_Origin_Embedder_Policy, content);
   6448  *aOutPolicy = NS_GetCrossOriginEmbedderPolicyFromHeader(
   6449      content, aIsOriginTrialCoepCredentiallessEnabled);
   6450  return NS_OK;
   6451 }
   6452 
   6453 // Obtain a cross-origin opener-policy from a response response and a
   6454 // cross-origin opener policy initiator.
   6455 // https://gist.github.com/annevk/6f2dd8c79c77123f39797f6bdac43f3e
   6456 NS_IMETHODIMP HttpBaseChannel::ComputeCrossOriginOpenerPolicy(
   6457    nsILoadInfo::CrossOriginOpenerPolicy aInitiatorPolicy,
   6458    nsILoadInfo::CrossOriginOpenerPolicy* aOutPolicy) {
   6459  MOZ_ASSERT(aOutPolicy);
   6460  *aOutPolicy = nsILoadInfo::OPENER_POLICY_UNSAFE_NONE;
   6461 
   6462  if (!mResponseHead) {
   6463    return NS_ERROR_NOT_AVAILABLE;
   6464  }
   6465 
   6466  // COOP headers are ignored for insecure-context loads.
   6467  if (!nsContentUtils::ComputeIsSecureContext(this)) {
   6468    return NS_OK;
   6469  }
   6470 
   6471  nsAutoCString openerPolicy;
   6472  (void)mResponseHead->GetHeader(nsHttp::Cross_Origin_Opener_Policy,
   6473                                 openerPolicy);
   6474 
   6475  // Cross-Origin-Opener-Policy = %s"same-origin" /
   6476  //                              %s"same-origin-allow-popups" /
   6477  //                              %s"unsafe-none"; case-sensitive
   6478 
   6479  nsCOMPtr<nsISFVService> sfv = GetSFVService();
   6480 
   6481  nsCOMPtr<nsISFVItem> item;
   6482  nsresult rv = sfv->ParseItem(openerPolicy, getter_AddRefs(item));
   6483  if (NS_FAILED(rv)) {
   6484    return rv;
   6485  }
   6486 
   6487  nsCOMPtr<nsISFVBareItem> value;
   6488  rv = item->GetValue(getter_AddRefs(value));
   6489  if (NS_FAILED(rv)) {
   6490    return rv;
   6491  }
   6492 
   6493  nsCOMPtr<nsISFVToken> token = do_QueryInterface(value);
   6494  if (!token) {
   6495    return NS_ERROR_UNEXPECTED;
   6496  }
   6497 
   6498  rv = token->GetValue(openerPolicy);
   6499  if (NS_FAILED(rv)) {
   6500    return rv;
   6501  }
   6502 
   6503  nsILoadInfo::CrossOriginOpenerPolicy policy =
   6504      nsILoadInfo::OPENER_POLICY_UNSAFE_NONE;
   6505 
   6506  if (openerPolicy.EqualsLiteral("same-origin")) {
   6507    policy = nsILoadInfo::OPENER_POLICY_SAME_ORIGIN;
   6508  } else if (openerPolicy.EqualsLiteral("same-origin-allow-popups")) {
   6509    policy = nsILoadInfo::OPENER_POLICY_SAME_ORIGIN_ALLOW_POPUPS;
   6510  }
   6511  if (policy == nsILoadInfo::OPENER_POLICY_SAME_ORIGIN) {
   6512    nsILoadInfo::CrossOriginEmbedderPolicy coep =
   6513        nsILoadInfo::EMBEDDER_POLICY_NULL;
   6514    bool isCoepCredentiallessEnabled;
   6515    rv = mLoadInfo->GetIsOriginTrialCoepCredentiallessEnabledForTopLevel(
   6516        &isCoepCredentiallessEnabled);
   6517    if (!isCoepCredentiallessEnabled) {
   6518      nsAutoCString originTrialToken;
   6519      (void)mResponseHead->GetHeader(nsHttp::OriginTrial, originTrialToken);
   6520      if (!originTrialToken.IsEmpty()) {
   6521        nsCOMPtr<nsIPrincipal> resultPrincipal;
   6522        rv = nsContentUtils::GetSecurityManager()->GetChannelResultPrincipal(
   6523            this, getter_AddRefs(resultPrincipal));
   6524        if (!NS_WARN_IF(NS_FAILED(rv))) {
   6525          OriginTrials trials;
   6526          trials.UpdateFromToken(NS_ConvertASCIItoUTF16(originTrialToken),
   6527                                 resultPrincipal);
   6528          if (trials.IsEnabled(OriginTrial::CoepCredentialless)) {
   6529            isCoepCredentiallessEnabled = true;
   6530          }
   6531        }
   6532      }
   6533    }
   6534 
   6535    NS_ENSURE_SUCCESS(rv, rv);
   6536    if (NS_SUCCEEDED(
   6537            GetResponseEmbedderPolicy(isCoepCredentiallessEnabled, &coep)) &&
   6538        (coep == nsILoadInfo::EMBEDDER_POLICY_REQUIRE_CORP ||
   6539         coep == nsILoadInfo::EMBEDDER_POLICY_CREDENTIALLESS)) {
   6540      policy =
   6541          nsILoadInfo::OPENER_POLICY_SAME_ORIGIN_EMBEDDER_POLICY_REQUIRE_CORP;
   6542    }
   6543  }
   6544 
   6545  *aOutPolicy = policy;
   6546  return NS_OK;
   6547 }
   6548 
   6549 NS_IMETHODIMP
   6550 HttpBaseChannel::GetCrossOriginOpenerPolicy(
   6551    nsILoadInfo::CrossOriginOpenerPolicy* aPolicy) {
   6552  MOZ_ASSERT(aPolicy);
   6553  if (!aPolicy) {
   6554    return NS_ERROR_INVALID_ARG;
   6555  }
   6556  // If this method is called before OnStartRequest (ie. before we call
   6557  // ComputeCrossOriginOpenerPolicy) or if we were unable to compute the
   6558  // policy we'll throw an error.
   6559  if (!LoadOnStartRequestCalled()) {
   6560    return NS_ERROR_NOT_AVAILABLE;
   6561  }
   6562  *aPolicy = mComputedCrossOriginOpenerPolicy;
   6563  return NS_OK;
   6564 }
   6565 
   6566 NS_IMETHODIMP
   6567 HttpBaseChannel::HasCrossOriginOpenerPolicyMismatch(bool* aIsMismatch) {
   6568  // This should only be called in parent process.
   6569  MOZ_ASSERT(XRE_IsParentProcess());
   6570  *aIsMismatch = LoadHasCrossOriginOpenerPolicyMismatch();
   6571  return NS_OK;
   6572 }
   6573 
   6574 NS_IMETHODIMP
   6575 HttpBaseChannel::GetOriginAgentClusterHeader(bool* aValue) {
   6576  MOZ_ASSERT(XRE_IsParentProcess());
   6577  if (!mResponseHead) {
   6578    return NS_ERROR_NOT_AVAILABLE;
   6579  }
   6580 
   6581  nsAutoCString content;
   6582  nsresult rv = mResponseHead->GetHeader(nsHttp::OriginAgentCluster, content);
   6583  if (NS_FAILED(rv)) {
   6584    return rv;
   6585  }
   6586 
   6587  // Origin-Agent-Cluster = <boolean>
   6588  nsCOMPtr<nsISFVService> sfv = GetSFVService();
   6589  nsCOMPtr<nsISFVItem> item;
   6590  rv = sfv->ParseItem(content, getter_AddRefs(item));
   6591  if (NS_FAILED(rv)) {
   6592    return rv;
   6593  }
   6594  nsCOMPtr<nsISFVBareItem> value;
   6595  rv = item->GetValue(getter_AddRefs(value));
   6596  if (NS_FAILED(rv)) {
   6597    return rv;
   6598  }
   6599  nsCOMPtr<nsISFVBool> flag = do_QueryInterface(value);
   6600  if (!flag) {
   6601    return NS_ERROR_NOT_AVAILABLE;
   6602  }
   6603  return flag->GetValue(aValue);
   6604 }
   6605 
   6606 void HttpBaseChannel::MaybeFlushConsoleReports() {
   6607  // Flush if we have a known window ID.
   6608  if (mLoadInfo->GetInnerWindowID() > 0) {
   6609    FlushReportsToConsole(mLoadInfo->GetInnerWindowID());
   6610    return;
   6611  }
   6612 
   6613  // If this channel is part of a loadGroup, we can flush the console reports
   6614  // immediately.
   6615  nsCOMPtr<nsILoadGroup> loadGroup;
   6616  nsresult rv = GetLoadGroup(getter_AddRefs(loadGroup));
   6617  if (NS_SUCCEEDED(rv) && loadGroup) {
   6618    FlushConsoleReports(loadGroup);
   6619  }
   6620 }
   6621 
   6622 void HttpBaseChannel::DoDiagnosticAssertWhenOnStopNotCalledOnDestroy() {}
   6623 
   6624 bool HttpBaseChannel::Http3Allowed() const {
   6625  bool allowedProxyInfo =
   6626      mProxyInfo ? (static_cast<nsProxyInfo*>(mProxyInfo.get())->IsDirect() ||
   6627                    static_cast<nsProxyInfo*>(mProxyInfo.get())->IsHttp3Proxy())
   6628                 : true;
   6629  // TODO: When mUpgradeProtocolCallback is not null, we should allow HTTP/3 for
   6630  // connect-udp.
   6631  return !mUpgradeProtocolCallback && allowedProxyInfo &&
   6632         !(mCaps & NS_HTTP_BE_CONSERVATIVE) && !LoadBeConservative() &&
   6633         LoadAllowHttp3();
   6634 }
   6635 
   6636 UniquePtr<nsHttpResponseHead>
   6637 HttpBaseChannel::MaybeCloneResponseHeadForCachedResource() {
   6638  if (!mResponseHead) {
   6639    return nullptr;
   6640  }
   6641 
   6642  return MakeUnique<nsHttpResponseHead>(*mResponseHead);
   6643 }
   6644 
   6645 void HttpBaseChannel::SetDummyChannelForCachedResource(
   6646    const nsHttpResponseHead* aMaybeResponseHead /* = nullptr */) {
   6647  mDummyChannelForCachedResource = true;
   6648  MOZ_ASSERT(!mResponseHead,
   6649             "SetDummyChannelForCachedResource should only be called once");
   6650  if (aMaybeResponseHead) {
   6651    mResponseHead = MakeUnique<nsHttpResponseHead>(*aMaybeResponseHead);
   6652  } else {
   6653    mResponseHead = MakeUnique<nsHttpResponseHead>();
   6654  }
   6655 }
   6656 
   6657 void HttpBaseChannel::SetEarlyHints(
   6658    nsTArray<EarlyHintConnectArgs>&& aEarlyHints) {
   6659  mEarlyHints = std::move(aEarlyHints);
   6660 }
   6661 
   6662 nsTArray<EarlyHintConnectArgs>&& HttpBaseChannel::TakeEarlyHints() {
   6663  return std::move(mEarlyHints);
   6664 }
   6665 
   6666 NS_IMETHODIMP
   6667 HttpBaseChannel::SetEarlyHintPreloaderId(uint64_t aEarlyHintPreloaderId) {
   6668  mEarlyHintPreloaderId = aEarlyHintPreloaderId;
   6669  return NS_OK;
   6670 }
   6671 
   6672 NS_IMETHODIMP
   6673 HttpBaseChannel::GetEarlyHintPreloaderId(uint64_t* aEarlyHintPreloaderId) {
   6674  NS_ENSURE_ARG_POINTER(aEarlyHintPreloaderId);
   6675  *aEarlyHintPreloaderId = mEarlyHintPreloaderId;
   6676  return NS_OK;
   6677 }
   6678 
   6679 NS_IMETHODIMP
   6680 HttpBaseChannel::SetClassicScriptHintCharset(
   6681    const nsAString& aClassicScriptHintCharset) {
   6682  mClassicScriptHintCharset = aClassicScriptHintCharset;
   6683  return NS_OK;
   6684 }
   6685 
   6686 NS_IMETHODIMP HttpBaseChannel::GetClassicScriptHintCharset(
   6687    nsAString& aClassicScriptHintCharset) {
   6688  aClassicScriptHintCharset = mClassicScriptHintCharset;
   6689  return NS_OK;
   6690 }
   6691 
   6692 NS_IMETHODIMP HttpBaseChannel::SetDocumentCharacterSet(
   6693    const nsAString& aDocumentCharacterSet) {
   6694  mDocumentCharacterSet = aDocumentCharacterSet;
   6695  return NS_OK;
   6696 }
   6697 
   6698 NS_IMETHODIMP HttpBaseChannel::GetDocumentCharacterSet(
   6699    nsAString& aDocumentCharacterSet) {
   6700  aDocumentCharacterSet = mDocumentCharacterSet;
   6701  return NS_OK;
   6702 }
   6703 
   6704 void HttpBaseChannel::SetConnectionInfo(nsHttpConnectionInfo* aCI) {
   6705  mConnectionInfo = aCI ? aCI->Clone() : nullptr;
   6706 }
   6707 
   6708 NS_IMETHODIMP
   6709 HttpBaseChannel::GetIsProxyUsed(bool* aIsProxyUsed) {
   6710  if (mProxyInfo) {
   6711    if (!static_cast<nsProxyInfo*>(mProxyInfo.get())->IsDirect()) {
   6712      StoreIsProxyUsed(true);
   6713    }
   6714  }
   6715  *aIsProxyUsed = LoadIsProxyUsed();
   6716  return NS_OK;
   6717 }
   6718 
   6719 static void CollectORBBlockTelemetry(
   6720    const OpaqueResponseBlockedTelemetryReason aTelemetryReason,
   6721    ExtContentPolicy aPolicy) {
   6722  glean::orb::block_reason.EnumGet(aTelemetryReason).Add();
   6723 
   6724  switch (aPolicy) {
   6725    case ExtContentPolicy::TYPE_INVALID:
   6726      glean::orb::block_initiator
   6727          .EnumGet(glean::orb::BlockInitiatorLabel::eInvalid)
   6728          .Add();
   6729      break;
   6730    case ExtContentPolicy::TYPE_OTHER:
   6731      glean::orb::block_initiator
   6732          .EnumGet(glean::orb::BlockInitiatorLabel::eOther)
   6733          .Add();
   6734      break;
   6735    case ExtContentPolicy::TYPE_FETCH:
   6736      glean::orb::block_initiator
   6737          .EnumGet(glean::orb::BlockInitiatorLabel::eBlockedFetch)
   6738          .Add();
   6739      break;
   6740    case ExtContentPolicy::TYPE_SCRIPT:
   6741      glean::orb::block_initiator
   6742          .EnumGet(glean::orb::BlockInitiatorLabel::eScript)
   6743          .Add();
   6744      break;
   6745    case ExtContentPolicy::TYPE_JSON:
   6746      glean::orb::block_initiator
   6747          .EnumGet(glean::orb::BlockInitiatorLabel::eJson)
   6748          .Add();
   6749      break;
   6750    case ExtContentPolicy::TYPE_IMAGE:
   6751      glean::orb::block_initiator
   6752          .EnumGet(glean::orb::BlockInitiatorLabel::eImage)
   6753          .Add();
   6754      break;
   6755    case ExtContentPolicy::TYPE_STYLESHEET:
   6756      glean::orb::block_initiator
   6757          .EnumGet(glean::orb::BlockInitiatorLabel::eStylesheet)
   6758          .Add();
   6759      break;
   6760    case ExtContentPolicy::TYPE_XMLHTTPREQUEST:
   6761      glean::orb::block_initiator
   6762          .EnumGet(glean::orb::BlockInitiatorLabel::eXmlhttprequest)
   6763          .Add();
   6764      break;
   6765    case ExtContentPolicy::TYPE_DTD:
   6766      glean::orb::block_initiator.EnumGet(glean::orb::BlockInitiatorLabel::eDtd)
   6767          .Add();
   6768      break;
   6769    case ExtContentPolicy::TYPE_FONT:
   6770      glean::orb::block_initiator
   6771          .EnumGet(glean::orb::BlockInitiatorLabel::eFont)
   6772          .Add();
   6773      break;
   6774    case ExtContentPolicy::TYPE_MEDIA:
   6775      glean::orb::block_initiator
   6776          .EnumGet(glean::orb::BlockInitiatorLabel::eMedia)
   6777          .Add();
   6778      break;
   6779    case ExtContentPolicy::TYPE_CSP_REPORT:
   6780      glean::orb::block_initiator
   6781          .EnumGet(glean::orb::BlockInitiatorLabel::eCspReport)
   6782          .Add();
   6783      break;
   6784    case ExtContentPolicy::TYPE_XSLT:
   6785      glean::orb::block_initiator
   6786          .EnumGet(glean::orb::BlockInitiatorLabel::eXslt)
   6787          .Add();
   6788      break;
   6789    case ExtContentPolicy::TYPE_IMAGESET:
   6790      glean::orb::block_initiator
   6791          .EnumGet(glean::orb::BlockInitiatorLabel::eImageset)
   6792          .Add();
   6793      break;
   6794    case ExtContentPolicy::TYPE_WEB_MANIFEST:
   6795      glean::orb::block_initiator
   6796          .EnumGet(glean::orb::BlockInitiatorLabel::eWebManifest)
   6797          .Add();
   6798      break;
   6799    case ExtContentPolicy::TYPE_SPECULATIVE:
   6800      glean::orb::block_initiator
   6801          .EnumGet(glean::orb::BlockInitiatorLabel::eSpeculative)
   6802          .Add();
   6803      break;
   6804    case ExtContentPolicy::TYPE_UA_FONT:
   6805      glean::orb::block_initiator
   6806          .EnumGet(glean::orb::BlockInitiatorLabel::eUaFont)
   6807          .Add();
   6808      break;
   6809    case ExtContentPolicy::TYPE_PROXIED_WEBRTC_MEDIA:
   6810      glean::orb::block_initiator
   6811          .EnumGet(glean::orb::BlockInitiatorLabel::eProxiedWebrtcMedia)
   6812          .Add();
   6813      break;
   6814    case ExtContentPolicy::TYPE_PING:
   6815      glean::orb::block_initiator
   6816          .EnumGet(glean::orb::BlockInitiatorLabel::ePing)
   6817          .Add();
   6818      break;
   6819    case ExtContentPolicy::TYPE_BEACON:
   6820      glean::orb::block_initiator
   6821          .EnumGet(glean::orb::BlockInitiatorLabel::eBeacon)
   6822          .Add();
   6823      break;
   6824    case ExtContentPolicy::TYPE_WEB_TRANSPORT:
   6825      glean::orb::block_initiator
   6826          .EnumGet(glean::orb::BlockInitiatorLabel::eWebTransport)
   6827          .Add();
   6828      break;
   6829    case ExtContentPolicy::TYPE_WEB_IDENTITY:
   6830      // Don't bother extending the telemetry for this.
   6831      glean::orb::block_initiator
   6832          .EnumGet(glean::orb::BlockInitiatorLabel::eOther)
   6833          .Add();
   6834      break;
   6835    case ExtContentPolicy::TYPE_DOCUMENT:
   6836    case ExtContentPolicy::TYPE_SUBDOCUMENT:
   6837    case ExtContentPolicy::TYPE_OBJECT:
   6838    case ExtContentPolicy::TYPE_WEBSOCKET:
   6839    case ExtContentPolicy::TYPE_SAVEAS_DOWNLOAD:
   6840      MOZ_ASSERT_UNREACHABLE("Shouldn't block this type");
   6841      // DOCUMENT, SUBDOCUMENT, OBJECT,
   6842      // WEBSOCKET and SAVEAS_DOWNLOAD are excluded from ORB
   6843      glean::orb::block_initiator
   6844          .EnumGet(glean::orb::BlockInitiatorLabel::eExcluded)
   6845          .Add();
   6846      break;
   6847      // Do not add default: so that compilers can catch the missing case.
   6848  }
   6849 }
   6850 
   6851 void HttpBaseChannel::LogORBError(
   6852    const nsAString& aReason,
   6853    const OpaqueResponseBlockedTelemetryReason aTelemetryReason) {
   6854  auto policy = mLoadInfo->GetExternalContentPolicyType();
   6855  CollectORBBlockTelemetry(aTelemetryReason, policy);
   6856 
   6857  // Blocking `ExtContentPolicy::TYPE_BEACON` isn't web observable, so keep
   6858  // quiet in the console about blocking it.
   6859  if (policy == ExtContentPolicy::TYPE_BEACON) {
   6860    return;
   6861  }
   6862 
   6863  RefPtr<dom::Document> doc;
   6864  mLoadInfo->GetLoadingDocument(getter_AddRefs(doc));
   6865 
   6866  nsAutoCString uri;
   6867  nsresult rv = nsContentUtils::AnonymizeURI(mURI, uri);
   6868  if (NS_WARN_IF(NS_FAILED(rv))) {
   6869    return;
   6870  }
   6871 
   6872  uint64_t contentWindowId;
   6873  GetTopLevelContentWindowId(&contentWindowId);
   6874  if (contentWindowId) {
   6875    nsContentUtils::ReportToConsoleByWindowID(
   6876        u"A resource is blocked by OpaqueResponseBlocking, please check browser console for details."_ns,
   6877        nsIScriptError::warningFlag, "ORB"_ns, contentWindowId,
   6878        SourceLocation(mURI.get()));
   6879  }
   6880 
   6881  AutoTArray<nsString, 2> params;
   6882  params.AppendElement(NS_ConvertUTF8toUTF16(uri));
   6883  params.AppendElement(aReason);
   6884  nsContentUtils::ReportToConsole(nsIScriptError::warningFlag, "ORB"_ns, doc,
   6885                                  nsContentUtils::eNECKO_PROPERTIES,
   6886                                  "ResourceBlockedORB", params);
   6887 }
   6888 
   6889 NS_IMETHODIMP HttpBaseChannel::SetEarlyHintLinkType(
   6890    uint32_t aEarlyHintLinkType) {
   6891  mEarlyHintLinkType = aEarlyHintLinkType;
   6892  return NS_OK;
   6893 }
   6894 
   6895 NS_IMETHODIMP HttpBaseChannel::GetEarlyHintLinkType(
   6896    uint32_t* aEarlyHintLinkType) {
   6897  *aEarlyHintLinkType = mEarlyHintLinkType;
   6898  return NS_OK;
   6899 }
   6900 
   6901 NS_IMETHODIMP
   6902 HttpBaseChannel::SetHasContentDecompressed(bool aValue) {
   6903  LOG(("HttpBaseChannel::SetHasContentDecompressed [this=%p value=%d]\n", this,
   6904       aValue));
   6905  mHasContentDecompressed = aValue;
   6906  return NS_OK;
   6907 }
   6908 NS_IMETHODIMP
   6909 HttpBaseChannel::GetHasContentDecompressed(bool* value) {
   6910  *value = mHasContentDecompressed;
   6911  return NS_OK;
   6912 }
   6913 
   6914 NS_IMETHODIMP
   6915 HttpBaseChannel::SetRenderBlocking(bool aRenderBlocking) {
   6916  mRenderBlocking = aRenderBlocking;
   6917  return NS_OK;
   6918 }
   6919 
   6920 NS_IMETHODIMP
   6921 HttpBaseChannel::GetRenderBlocking(bool* aRenderBlocking) {
   6922  *aRenderBlocking = mRenderBlocking;
   6923  return NS_OK;
   6924 }
   6925 
   6926 NS_IMETHODIMP HttpBaseChannel::GetLastTransportStatus(
   6927    nsresult* aLastTransportStatus) {
   6928  return NS_ERROR_NOT_IMPLEMENTED;
   6929 }
   6930 
   6931 void HttpBaseChannel::SetFetchPriorityDOM(
   6932    mozilla::dom::FetchPriority aPriority) {
   6933  switch (aPriority) {
   6934    case mozilla::dom::FetchPriority::Auto:
   6935      SetFetchPriority(nsIClassOfService::FETCHPRIORITY_AUTO);
   6936      return;
   6937    case mozilla::dom::FetchPriority::High:
   6938      SetFetchPriority(nsIClassOfService::FETCHPRIORITY_HIGH);
   6939      return;
   6940    case mozilla::dom::FetchPriority::Low:
   6941      SetFetchPriority(nsIClassOfService::FETCHPRIORITY_LOW);
   6942      return;
   6943    default:
   6944      MOZ_ASSERT_UNREACHABLE();
   6945  }
   6946 }
   6947 
   6948 }  // namespace net
   6949 }  // namespace mozilla