tor-browser

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

DocumentLoadListener.cpp (131697B)


      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 #include "DocumentLoadListener.h"
      9 
     10 #include "imgLoader.h"
     11 #include "NeckoCommon.h"
     12 #include "nsLoadGroup.h"
     13 #include "mozilla/AntiTrackingUtils.h"
     14 #include "mozilla/AppShutdown.h"
     15 #include "mozilla/DebugOnly.h"
     16 #include "mozilla/DynamicFpiNavigationHeuristic.h"
     17 #include "mozilla/Components.h"
     18 #include "mozilla/LoadInfo.h"
     19 #include "mozilla/ipc/PBackgroundSharedTypes.h"
     20 #include "mozilla/NullPrincipal.h"
     21 #include "mozilla/RefPtr.h"
     22 #include "mozilla/ResultVariant.h"
     23 #include "mozilla/ScopeExit.h"
     24 #include "mozilla/StaticPrefs_extensions.h"
     25 #include "mozilla/StaticPrefs_fission.h"
     26 #include "mozilla/StaticPrefs_security.h"
     27 #include "mozilla/dom/BrowserParent.h"
     28 #include "mozilla/dom/BrowsingContextGroup.h"
     29 #include "mozilla/dom/CanonicalBrowsingContext.h"
     30 #include "mozilla/dom/ChildProcessChannelListener.h"
     31 #include "mozilla/dom/ClientChannelHelper.h"
     32 #include "mozilla/dom/ContentParent.h"
     33 #include "mozilla/dom/ContentProcessManager.h"
     34 #include "mozilla/dom/ProcessIsolation.h"
     35 #include "mozilla/dom/SessionHistoryEntry.h"
     36 #include "mozilla/dom/WindowGlobalParent.h"
     37 #include "mozilla/dom/ipc/IdType.h"
     38 #include "mozilla/net/CookieJarSettings.h"
     39 #include "mozilla/net/HttpChannelParent.h"
     40 #include "mozilla/net/RedirectChannelRegistrar.h"
     41 #include "nsContentSecurityUtils.h"
     42 #include "nsContentSecurityManager.h"
     43 #include "nsDocShell.h"
     44 #include "nsDocShellLoadState.h"
     45 #include "nsDocShellLoadTypes.h"
     46 #include "nsDOMNavigationTiming.h"
     47 #include "nsDSURIContentListener.h"
     48 #include "nsObjectLoadingContent.h"
     49 #include "nsOpenWindowInfo.h"
     50 #include "nsExternalHelperAppService.h"
     51 #include "nsHttpChannel.h"
     52 #include "nsIBrowser.h"
     53 #include "nsIClassifiedChannel.h"
     54 #include "nsIHttpChannelInternal.h"
     55 #include "nsINetworkInterceptController.h"
     56 #include "nsIStreamConverterService.h"
     57 #include "nsIViewSourceChannel.h"
     58 #include "nsImportModule.h"
     59 #include "nsIXULRuntime.h"
     60 #include "nsMimeTypes.h"
     61 #include "nsQueryObject.h"
     62 #include "nsRedirectHistoryEntry.h"
     63 #include "nsSandboxFlags.h"
     64 #include "nsScriptSecurityManager.h"
     65 #include "nsSHistory.h"
     66 #include "nsStringStream.h"
     67 #include "nsURILoader.h"
     68 #include "nsWebNavigationInfo.h"
     69 #include "mozilla/dom/BrowserParent.h"
     70 #include "mozilla/dom/Element.h"
     71 #include "mozilla/dom/nsHTTPSOnlyUtils.h"
     72 #include "mozilla/dom/ReferrerInfo.h"
     73 #include "mozilla/dom/RemoteWebProgressRequest.h"
     74 #include "mozilla/net/UrlClassifierFeatureFactory.h"
     75 #include "mozilla/ExtensionPolicyService.h"
     76 #include "mozilla/intl/Localization.h"
     77 #include "nsDocLoader.h"  // for FormatStatusMessage
     78 
     79 #ifdef ANDROID
     80 #  include "mozilla/widget/nsWindow.h"
     81 #endif /* ANDROID */
     82 
     83 mozilla::LazyLogModule gDocumentChannelLog("DocumentChannel");
     84 #define LOG(fmt) MOZ_LOG(gDocumentChannelLog, mozilla::LogLevel::Verbose, fmt)
     85 
     86 extern mozilla::LazyLogModule gSHIPBFCacheLog;
     87 
     88 // Bug 136580: Limit to the number of nested content frames that can have the
     89 //             same URL. This is to stop content that is recursively loading
     90 //             itself.  Note that "#foo" on the end of URL doesn't affect
     91 //             whether it's considered identical, but "?foo" or ";foo" are
     92 //             considered and compared.
     93 // Limit this to 2, like chromium does.
     94 static constexpr int kMaxSameURLContentFrames = 2;
     95 
     96 using namespace mozilla::dom;
     97 
     98 namespace mozilla {
     99 namespace net {
    100 
    101 static StaticRefPtr<mozilla::intl::Localization> sL10n;
    102 
    103 static ContentParentId GetContentProcessId(ContentParent* aContentParent) {
    104  return aContentParent ? aContentParent->ChildID() : ContentParentId{0};
    105 }
    106 
    107 static void SetNeedToAddURIVisit(nsIChannel* aChannel,
    108                                 bool aNeedToAddURIVisit) {
    109  nsCOMPtr<nsIWritablePropertyBag2> props(do_QueryInterface(aChannel));
    110  if (!props) {
    111    return;
    112  }
    113 
    114  props->SetPropertyAsBool(u"docshell.needToAddURIVisit"_ns,
    115                           aNeedToAddURIVisit);
    116 }
    117 
    118 static auto SecurityFlagsForLoadInfo(nsDocShellLoadState* aLoadState)
    119    -> nsSecurityFlags {
    120  // TODO: Block copied from nsDocShell::DoURILoad, refactor out somewhere
    121  nsSecurityFlags securityFlags =
    122      nsILoadInfo::SEC_ALLOW_CROSS_ORIGIN_SEC_CONTEXT_IS_NULL;
    123 
    124  if (aLoadState->LoadType() == LOAD_ERROR_PAGE) {
    125    securityFlags |= nsILoadInfo::SEC_LOAD_ERROR_PAGE;
    126  }
    127 
    128  if (aLoadState->PrincipalToInherit()) {
    129    nsIURI* uri = aLoadState->URI();
    130    bool isSrcdoc = aLoadState->HasInternalLoadFlags(
    131        nsDocShell::INTERNAL_LOAD_FLAGS_IS_SRCDOC);
    132    bool inheritAttrs = nsContentUtils::ChannelShouldInheritPrincipal(
    133        aLoadState->PrincipalToInherit(), uri,
    134        true,  // aInheritForAboutBlank
    135        isSrcdoc);
    136 
    137    if (inheritAttrs && !uri->SchemeIs("data")) {
    138      securityFlags |= nsILoadInfo::SEC_FORCE_INHERIT_PRINCIPAL;
    139    }
    140  }
    141 
    142  return securityFlags;
    143 }
    144 
    145 // Construct a LoadInfo object to use when creating the internal channel for a
    146 // Document/SubDocument load.
    147 static auto CreateDocumentLoadInfo(CanonicalBrowsingContext* aBrowsingContext,
    148                                   nsDocShellLoadState* aLoadState)
    149    -> already_AddRefed<LoadInfo> {
    150  uint32_t sandboxFlags = aBrowsingContext->GetSandboxFlags();
    151  RefPtr<LoadInfo> loadInfo;
    152 
    153  auto securityFlags = SecurityFlagsForLoadInfo(aLoadState);
    154 
    155  if (aBrowsingContext->GetParent()) {
    156    loadInfo = LoadInfo::CreateForFrame(
    157        aBrowsingContext, aLoadState->TriggeringPrincipal(),
    158        aLoadState->GetEffectiveTriggeringRemoteType(), securityFlags,
    159        sandboxFlags);
    160  } else {
    161    OriginAttributes attrs;
    162    aBrowsingContext->GetOriginAttributes(attrs);
    163    loadInfo = LoadInfo::CreateForDocument(
    164        aBrowsingContext, aLoadState->URI(), aLoadState->TriggeringPrincipal(),
    165        aLoadState->GetEffectiveTriggeringRemoteType(), attrs, securityFlags,
    166        sandboxFlags);
    167  }
    168 
    169  if (aLoadState->IsExemptFromHTTPSFirstMode() &&
    170      nsHTTPSOnlyUtils::GetUpgradeMode(loadInfo) ==
    171          nsHTTPSOnlyUtils::HTTPS_FIRST_MODE) {
    172    uint32_t httpsOnlyStatus = loadInfo->GetHttpsOnlyStatus();
    173    httpsOnlyStatus |= nsILoadInfo::HTTPS_ONLY_EXEMPT;
    174    loadInfo->SetHttpsOnlyStatus(httpsOnlyStatus);
    175  }
    176 
    177  loadInfo->SetSchemelessInput(aLoadState->GetSchemelessInput());
    178  loadInfo->SetHttpsUpgradeTelemetry(aLoadState->GetHttpsUpgradeTelemetry());
    179 
    180  loadInfo->SetTriggeringSandboxFlags(aLoadState->TriggeringSandboxFlags());
    181  loadInfo->SetTriggeringWindowId(aLoadState->TriggeringWindowId());
    182  loadInfo->SetTriggeringStorageAccess(aLoadState->TriggeringStorageAccess());
    183  ClassificationFlags classificationFlags =
    184      aLoadState->TriggeringClassificationFlags();
    185  loadInfo->SetTriggeringFirstPartyClassificationFlags(
    186      classificationFlags.firstPartyFlags);
    187  loadInfo->SetTriggeringThirdPartyClassificationFlags(
    188      classificationFlags.thirdPartyFlags);
    189  loadInfo->SetHasValidUserGestureActivation(
    190      aLoadState->HasValidUserGestureActivation());
    191  loadInfo->SetTextDirectiveUserActivation(
    192      aLoadState->GetTextDirectiveUserActivation());
    193  loadInfo->SetIsMetaRefresh(aLoadState->IsMetaRefresh());
    194 
    195  return loadInfo.forget();
    196 }
    197 
    198 // Construct a LoadInfo object to use when creating the internal channel for an
    199 // Object/Embed load.
    200 static auto CreateObjectLoadInfo(nsDocShellLoadState* aLoadState,
    201                                 uint64_t aInnerWindowId,
    202                                 nsContentPolicyType aContentPolicyType,
    203                                 uint32_t aSandboxFlags)
    204    -> already_AddRefed<LoadInfo> {
    205  RefPtr<WindowGlobalParent> wgp =
    206      WindowGlobalParent::GetByInnerWindowId(aInnerWindowId);
    207  MOZ_RELEASE_ASSERT(wgp);
    208 
    209  auto securityFlags = SecurityFlagsForLoadInfo(aLoadState);
    210 
    211  RefPtr<LoadInfo> loadInfo = LoadInfo::CreateForNonDocument(
    212      wgp, wgp->DocumentPrincipal(), aContentPolicyType, securityFlags,
    213      aSandboxFlags);
    214 
    215  loadInfo->SetHasValidUserGestureActivation(
    216      aLoadState->HasValidUserGestureActivation());
    217  loadInfo->SetTextDirectiveUserActivation(
    218      aLoadState->GetTextDirectiveUserActivation());
    219  loadInfo->SetTriggeringSandboxFlags(aLoadState->TriggeringSandboxFlags());
    220  loadInfo->SetTriggeringWindowId(aLoadState->TriggeringWindowId());
    221  loadInfo->SetTriggeringStorageAccess(aLoadState->TriggeringStorageAccess());
    222  net::ClassificationFlags classificationFlags =
    223      aLoadState->TriggeringClassificationFlags();
    224  loadInfo->SetTriggeringFirstPartyClassificationFlags(
    225      classificationFlags.firstPartyFlags);
    226  loadInfo->SetTriggeringThirdPartyClassificationFlags(
    227      classificationFlags.thirdPartyFlags);
    228  loadInfo->SetIsMetaRefresh(aLoadState->IsMetaRefresh());
    229 
    230  return loadInfo.forget();
    231 }
    232 
    233 /**
    234 * An extension to nsDocumentOpenInfo that we run in the parent process, so
    235 * that we can make the decision to retarget to content handlers or the external
    236 * helper app, before we make process switching decisions.
    237 *
    238 * This modifies the behaviour of nsDocumentOpenInfo so that it can do
    239 * retargeting, but doesn't do stream conversion (but confirms that we will be
    240 * able to do so later).
    241 *
    242 * We still run nsDocumentOpenInfo in the content process, but disable
    243 * retargeting, so that it can only apply stream conversion, and then send data
    244 * to the docshell.
    245 */
    246 class ParentProcessDocumentOpenInfo final : public nsDocumentOpenInfo,
    247                                            public nsIMultiPartChannelListener {
    248 public:
    249  ParentProcessDocumentOpenInfo(ParentChannelListener* aListener,
    250                                uint32_t aFlags,
    251                                mozilla::dom::BrowsingContext* aBrowsingContext,
    252                                const nsACString& aTypeHint,
    253                                bool aIsDocumentLoad)
    254      : nsDocumentOpenInfo(aFlags, false),
    255        mBrowsingContext(aBrowsingContext),
    256        mListener(aListener),
    257        mTypeHint(aTypeHint),
    258        mIsDocumentLoad(aIsDocumentLoad) {
    259    LOG(("ParentProcessDocumentOpenInfo ctor [this=%p]", this));
    260  }
    261 
    262  NS_DECL_ISUPPORTS_INHERITED
    263 
    264  // The default content listener is always a docshell (potentially with an
    265  // interstitial nsObjectLoadingContent), so this manually implements the same
    266  // checks, and if it succeeds, uses the parent channel listener so that we
    267  // forward onto DocumentLoadListener.
    268  bool TryDefaultContentListener(nsIChannel* aChannel,
    269                                 const nsCString& aContentType) {
    270    uint32_t canHandle = nsWebNavigationInfo::IsTypeSupported(aContentType);
    271    // NOTE: We do not support the default content listener for `FALLBACK` on
    272    // object/embed loads, as there's no need to send content to the content
    273    // process in the fallback case. By rejecting the channel will be cancelled
    274    // with NS_ERROR_WONT_HANDLE_CONTENT, which will lead to a fallback in
    275    // content without sending the response data down.
    276    if (canHandle != nsIWebNavigationInfo::UNSUPPORTED &&
    277        (mIsDocumentLoad || canHandle != nsIWebNavigationInfo::FALLBACK)) {
    278      m_targetStreamListener = mListener;
    279      nsLoadFlags loadFlags = 0;
    280      aChannel->GetLoadFlags(&loadFlags);
    281      aChannel->SetLoadFlags(loadFlags | nsIChannel::LOAD_TARGETED);
    282      return true;
    283    }
    284    return false;
    285  }
    286 
    287  bool TryDefaultContentListener(nsIChannel* aChannel) override {
    288    return TryDefaultContentListener(aChannel, mContentType);
    289  }
    290 
    291  // Generally we only support stream converters that can tell
    292  // use exactly what type they'll output. If we find one, then
    293  // we just target to our default listener directly (without
    294  // conversion), and the content process nsDocumentOpenInfo will
    295  // run and do the actual conversion.
    296  nsresult TryStreamConversion(nsIChannel* aChannel) override {
    297    // The one exception is nsUnknownDecoder, which works in the parent
    298    // (and we need to know what the content type is before we can
    299    // decide if it will be handled in the parent), so we run that here.
    300    if (mContentType.LowerCaseEqualsASCII(UNKNOWN_CONTENT_TYPE) ||
    301        mContentType.IsEmpty()) {
    302      return nsDocumentOpenInfo::TryStreamConversion(aChannel);
    303    }
    304 
    305    nsresult rv;
    306    nsCOMPtr<nsIStreamConverterService> streamConvService;
    307    nsAutoCString str;
    308    streamConvService = mozilla::components::StreamConverter::Service(&rv);
    309    rv = streamConvService->ConvertedType(mContentType, aChannel, str);
    310    NS_ENSURE_SUCCESS(rv, rv);
    311 
    312    // We only support passing data to the default content listener
    313    // (docshell), and we don't supported chaining converters.
    314    if (TryDefaultContentListener(aChannel, str)) {
    315      mContentType = str;
    316      return NS_OK;
    317    }
    318    // This is the same result as nsStreamConverterService uses when it
    319    // can't find a converter
    320    return NS_ERROR_FAILURE;
    321  }
    322 
    323  nsresult TryExternalHelperApp(nsIExternalHelperAppService* aHelperAppService,
    324                                nsIChannel* aChannel) override {
    325    RefPtr<nsIStreamListener> listener;
    326    nsresult rv = aHelperAppService->CreateListener(
    327        mContentType, aChannel, mBrowsingContext, false, nullptr,
    328        getter_AddRefs(listener));
    329    if (NS_SUCCEEDED(rv)) {
    330      m_targetStreamListener = listener;
    331    }
    332    return rv;
    333  }
    334 
    335  nsDocumentOpenInfo* Clone() override {
    336    mCloned = true;
    337    return new ParentProcessDocumentOpenInfo(
    338        mListener, mFlags, mBrowsingContext, mTypeHint, mIsDocumentLoad);
    339  }
    340 
    341  nsresult OnDocumentStartRequest(nsIRequest* request) {
    342    LOG(("ParentProcessDocumentOpenInfo OnDocumentStartRequest [this=%p]",
    343         this));
    344 
    345    return nsDocumentOpenInfo::OnStartRequest(request);
    346  }
    347 
    348  nsresult OnObjectStartRequest(nsIRequest* request) {
    349    LOG(("ParentProcessDocumentOpenInfo OnObjectStartRequest [this=%p]", this));
    350 
    351    // Respect the specified image MIME type if loading binary content type into
    352    // an object/embed element.
    353    if (nsCOMPtr<nsIChannel> channel = do_QueryInterface(request)) {
    354      nsAutoCString channelType;
    355      channel->GetContentType(channelType);
    356      if (!mTypeHint.IsEmpty() &&
    357          imgLoader::SupportImageWithMimeType(mTypeHint) &&
    358          (channelType.EqualsASCII(APPLICATION_GUESS_FROM_EXT) ||
    359           channelType.EqualsASCII(APPLICATION_OCTET_STREAM) ||
    360           channelType.EqualsASCII(BINARY_OCTET_STREAM))) {
    361        channel->SetContentType(mTypeHint);
    362      }
    363    }
    364 
    365    // If the load is considered to have failed, we're going to display fallback
    366    // content in the nsDocShellLoadingContent. Cancel the channel to reflect
    367    // this.
    368    nsresult status = NS_OK;
    369    if (!nsObjectLoadingContent::IsSuccessfulRequest(request, &status)) {
    370      LOG(("OnObjectStartRequest for unsuccessful request [this=%p, status=%s]",
    371           this, GetStaticErrorName(status)));
    372      return NS_ERROR_WONT_HANDLE_CONTENT;
    373    }
    374 
    375    // All successful object loads will be treated as document loads, so run
    376    // through nsDocumentOpenInfo. This will check the MIME type to ensure it is
    377    // supported, and attempt stream conversions where applicable.
    378    //
    379    // If the dom.navigation.object_embed.allow_retargeting pref is enabled,
    380    // this may lead to the resource being downloaded.
    381    return OnDocumentStartRequest(request);
    382  }
    383 
    384  NS_IMETHOD OnStartRequest(nsIRequest* request) override {
    385    LOG(("ParentProcessDocumentOpenInfo OnStartRequest [this=%p]", this));
    386 
    387    nsresult rv = mIsDocumentLoad ? OnDocumentStartRequest(request)
    388                                  : OnObjectStartRequest(request);
    389 
    390    // If we didn't find a content handler, and we don't have a listener, then
    391    // just forward to our default listener. This happens when the channel is in
    392    // an error state, and we want to just forward that on to be handled in the
    393    // content process, or when DONT_RETARGET is set.
    394    if (!mUsedContentHandler && !m_targetStreamListener) {
    395      m_targetStreamListener = mListener;
    396      if (NS_FAILED(rv)) {
    397        LOG(("nsDocumentOpenInfo OnStartRequest Failed [this=%p, rv=%s]", this,
    398             GetStaticErrorName(rv)));
    399        request->CancelWithReason(
    400            rv, "nsDocumentOpenInfo::OnStartRequest failed"_ns);
    401      }
    402      return m_targetStreamListener->OnStartRequest(request);
    403    }
    404    if (m_targetStreamListener != mListener) {
    405      LOG(
    406          ("ParentProcessDocumentOpenInfo targeted to non-default listener "
    407           "[this=%p]",
    408           this));
    409      // If this is the only part, then we can immediately tell our listener
    410      // that it won't be getting any content and disconnect it. For multipart
    411      // channels we have to wait until we've handled all parts before we know.
    412      // This does mean that the content process can still Cancel() a multipart
    413      // response while the response is being handled externally, but this
    414      // matches the single-process behaviour.
    415      // If we got cloned, then we don't need to do this, as only the last link
    416      // needs to do it.
    417      // Multi-part channels are guaranteed to call OnAfterLastPart, which we
    418      // forward to the listeners, so it will handle disconnection at that
    419      // point.
    420      nsCOMPtr<nsIMultiPartChannel> multiPartChannel =
    421          do_QueryInterface(request);
    422      if (!multiPartChannel && !mCloned) {
    423        DisconnectChildListeners(NS_FAILED(rv) ? rv : NS_BINDING_RETARGETED,
    424                                 rv);
    425      }
    426    }
    427 
    428    return rv;
    429  }
    430 
    431  NS_IMETHOD OnAfterLastPart(nsresult aStatus) override {
    432    mListener->OnAfterLastPart(aStatus);
    433    return NS_OK;
    434  }
    435 
    436 private:
    437  virtual ~ParentProcessDocumentOpenInfo() {
    438    LOG(("ParentProcessDocumentOpenInfo dtor [this=%p]", this));
    439  }
    440 
    441  void DisconnectChildListeners(nsresult aStatus, nsresult aLoadGroupStatus) {
    442    // Tell the DocumentLoadListener to notify the content process that it's
    443    // been entirely retargeted, and to stop waiting.
    444    // Clear mListener's pointer to the DocumentLoadListener to break the
    445    // reference cycle.
    446    RefPtr<DocumentLoadListener> doc = do_GetInterface(ToSupports(mListener));
    447    MOZ_ASSERT(doc);
    448    doc->DisconnectListeners(aStatus, aLoadGroupStatus);
    449    mListener->SetListenerAfterRedirect(nullptr);
    450  }
    451 
    452  RefPtr<mozilla::dom::BrowsingContext> mBrowsingContext;
    453  RefPtr<ParentChannelListener> mListener;
    454  nsCString mTypeHint;
    455  const bool mIsDocumentLoad;
    456 
    457  /**
    458   * Set to true if we got cloned to create a chained listener.
    459   */
    460  bool mCloned = false;
    461 };
    462 
    463 NS_IMPL_ADDREF_INHERITED(ParentProcessDocumentOpenInfo, nsDocumentOpenInfo)
    464 NS_IMPL_RELEASE_INHERITED(ParentProcessDocumentOpenInfo, nsDocumentOpenInfo)
    465 
    466 NS_INTERFACE_MAP_BEGIN(ParentProcessDocumentOpenInfo)
    467  NS_INTERFACE_MAP_ENTRY(nsIMultiPartChannelListener)
    468 NS_INTERFACE_MAP_END_INHERITING(nsDocumentOpenInfo)
    469 
    470 NS_IMPL_ADDREF(DocumentLoadListener)
    471 NS_IMPL_RELEASE(DocumentLoadListener)
    472 
    473 NS_INTERFACE_MAP_BEGIN(DocumentLoadListener)
    474  NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIInterfaceRequestor)
    475  NS_INTERFACE_MAP_ENTRY(nsIInterfaceRequestor)
    476  NS_INTERFACE_MAP_ENTRY(nsIRequestObserver)
    477  NS_INTERFACE_MAP_ENTRY(nsIStreamListener)
    478  NS_INTERFACE_MAP_ENTRY(nsIParentChannel)
    479  NS_INTERFACE_MAP_ENTRY(nsIAsyncVerifyRedirectReadyCallback)
    480  NS_INTERFACE_MAP_ENTRY(nsIChannelEventSink)
    481  NS_INTERFACE_MAP_ENTRY(nsIMultiPartChannelListener)
    482  NS_INTERFACE_MAP_ENTRY(nsIProgressEventSink)
    483  NS_INTERFACE_MAP_ENTRY(nsIEarlyHintObserver)
    484  NS_INTERFACE_MAP_ENTRY_CONCRETE(DocumentLoadListener)
    485 NS_INTERFACE_MAP_END
    486 
    487 DocumentLoadListener::DocumentLoadListener(
    488    CanonicalBrowsingContext* aLoadingBrowsingContext, bool aIsDocumentLoad)
    489    : mIsDocumentLoad(aIsDocumentLoad) {
    490  LOG(("DocumentLoadListener ctor [this=%p]", this));
    491  mParentChannelListener =
    492      new ParentChannelListener(this, aLoadingBrowsingContext);
    493 }
    494 
    495 DocumentLoadListener::~DocumentLoadListener() {
    496  LOG(("DocumentLoadListener dtor [this=%p]", this));
    497 }
    498 
    499 void DocumentLoadListener::AddURIVisit(nsIChannel* aChannel,
    500                                       uint32_t aLoadFlags) {
    501  if (mLoadStateLoadType == LOAD_ERROR_PAGE ||
    502      mLoadStateLoadType == LOAD_BYPASS_HISTORY) {
    503    return;
    504  }
    505 
    506  nsCOMPtr<nsIURI> uri;
    507  NS_GetFinalChannelURI(aChannel, getter_AddRefs(uri));
    508 
    509  nsCOMPtr<nsIURI> previousURI;
    510  uint32_t previousFlags = 0;
    511  if (mLoadStateLoadType & nsIDocShell::LOAD_CMD_RELOAD) {
    512    previousURI = uri;
    513  } else {
    514    nsDocShell::ExtractLastVisit(aChannel, getter_AddRefs(previousURI),
    515                                 &previousFlags);
    516  }
    517 
    518  // Get the HTTP response code, if available.
    519  uint32_t responseStatus = 0;
    520  nsCOMPtr<nsIHttpChannel> httpChannel = do_QueryInterface(aChannel);
    521  if (httpChannel) {
    522    (void)httpChannel->GetResponseStatus(&responseStatus);
    523  }
    524 
    525  RefPtr<CanonicalBrowsingContext> browsingContext =
    526      GetDocumentBrowsingContext();
    527  nsCOMPtr<nsIWidget> widget =
    528      browsingContext->GetParentProcessWidgetContaining();
    529 
    530  nsCOMPtr<nsILoadInfo> loadInfo = aChannel->LoadInfo();
    531 
    532  // Check if the URI has a http scheme and if either HSTS is enabled for this
    533  // host, or if we were upgraded by HTTPS-Only/First. If this is the case, we
    534  // want to mark this URI specially, as it will be followed shortly by an
    535  // almost identical https history entry. That way the global history
    536  // implementation can handle the visit appropriately (e.g. by marking it as
    537  // `hidden`, so only the https url appears in default history views).
    538  bool wasUpgraded =
    539      uri->SchemeIs("http") &&
    540      (loadInfo->GetHstsStatus() ||
    541       (loadInfo->GetHttpsOnlyStatus() &
    542        (nsILoadInfo::HTTPS_ONLY_UPGRADED_HTTPS_FIRST |
    543         nsILoadInfo::HTTPS_ONLY_UPGRADED_LISTENER_NOT_REGISTERED |
    544         nsILoadInfo::HTTPS_ONLY_UPGRADED_LISTENER_REGISTERED)));
    545 
    546  nsDocShell::InternalAddURIVisit(uri, previousURI, previousFlags,
    547                                  responseStatus, browsingContext, widget,
    548                                  mLoadStateLoadType, wasUpgraded);
    549 }
    550 
    551 CanonicalBrowsingContext* DocumentLoadListener::GetLoadingBrowsingContext()
    552    const {
    553  return mParentChannelListener ? mParentChannelListener->GetBrowsingContext()
    554                                : nullptr;
    555 }
    556 
    557 CanonicalBrowsingContext* DocumentLoadListener::GetDocumentBrowsingContext()
    558    const {
    559  return mIsDocumentLoad ? GetLoadingBrowsingContext() : nullptr;
    560 }
    561 
    562 CanonicalBrowsingContext* DocumentLoadListener::GetTopBrowsingContext() const {
    563  auto* loadingContext = GetLoadingBrowsingContext();
    564  return loadingContext ? loadingContext->Top() : nullptr;
    565 }
    566 
    567 WindowGlobalParent* DocumentLoadListener::GetParentWindowContext() const {
    568  return mParentWindowContext;
    569 }
    570 
    571 bool CheckRecursiveLoad(CanonicalBrowsingContext* aLoadingContext,
    572                        nsDocShellLoadState* aLoadState, bool aIsDocumentLoad) {
    573  if (!aLoadState->ShouldCheckForRecursion()) {
    574    return true;
    575  }
    576 
    577  // Bug 136580: Check for recursive frame loading excluding about:srcdoc URIs.
    578  // srcdoc URIs require their contents to be specified inline, so it isn't
    579  // possible for undesirable recursion to occur without the aid of a
    580  // non-srcdoc URI,  which this method will block normally.
    581  // Besides, URI is not enough to guarantee uniqueness of srcdoc documents.
    582  nsAutoCString buffer;
    583  if (aLoadState->URI()->SchemeIs("about")) {
    584    nsresult rv = aLoadState->URI()->GetPathQueryRef(buffer);
    585    if (NS_SUCCEEDED(rv) && buffer.EqualsLiteral("srcdoc")) {
    586      // Duplicates allowed up to depth limits
    587      return true;
    588    }
    589  }
    590 
    591  RefPtr<WindowGlobalParent> parent;
    592  if (!aIsDocumentLoad) {  // object load
    593    parent = aLoadingContext->GetCurrentWindowGlobal();
    594  } else {
    595    parent = aLoadingContext->GetParentWindowContext();
    596  }
    597 
    598  int matchCount = 0;
    599  CanonicalBrowsingContext* ancestorBC;
    600  for (WindowGlobalParent* ancestorWGP = parent; ancestorWGP;
    601       ancestorWGP = ancestorBC->GetParentWindowContext()) {
    602    ancestorBC = ancestorWGP->BrowsingContext();
    603    MOZ_ASSERT(ancestorBC);
    604    if (nsCOMPtr<nsIURI> parentURI = ancestorWGP->GetDocumentURI()) {
    605      bool equal;
    606      nsresult rv = aLoadState->URI()->EqualsExceptRef(parentURI, &equal);
    607      NS_ENSURE_SUCCESS(rv, false);
    608 
    609      if (equal) {
    610        matchCount++;
    611        if (matchCount >= kMaxSameURLContentFrames) {
    612          NS_WARNING(
    613              "Too many nested content frames/objects have the same url "
    614              "(recursion?) "
    615              "so giving up");
    616          return false;
    617        }
    618      }
    619    }
    620  }
    621  return true;
    622 }
    623 
    624 // Check that the load state, potentially received from a child process, appears
    625 // to be performing a load of the specified LoadingSessionHistoryInfo.
    626 // Returns a Result<…> containing the SessionHistoryEntry found for the
    627 // LoadingSessionHistoryInfo as success value if the validation succeeded, or a
    628 // static (telemetry-safe) string naming what did not match as a failure value
    629 // if the validation failed.
    630 static Result<SessionHistoryEntry*, const char*> ValidateHistoryLoad(
    631    CanonicalBrowsingContext* aLoadingContext,
    632    nsDocShellLoadState* aLoadState) {
    633  MOZ_ASSERT(SessionHistoryInParent());
    634  MOZ_ASSERT(aLoadState->LoadIsFromSessionHistory());
    635 
    636  if (!aLoadState->GetLoadingSessionHistoryInfo()) {
    637    return Err("Missing LoadingSessionHistoryInfo");
    638  }
    639 
    640  SessionHistoryEntry::LoadingEntry* loading = SessionHistoryEntry::GetByLoadId(
    641      aLoadState->GetLoadingSessionHistoryInfo()->mLoadId);
    642  if (!loading) {
    643    return Err("Missing SessionHistoryEntry");
    644  }
    645 
    646  SessionHistoryInfo* snapshot = loading->mInfoSnapshotForValidation.get();
    647  // History loads do not inherit principal.
    648  if (aLoadState->HasInternalLoadFlags(
    649          nsDocShell::INTERNAL_LOAD_FLAGS_INHERIT_PRINCIPAL)) {
    650    return Err("LOAD_FLAGS_INHERIT_PRINCIPAL");
    651  }
    652 
    653  auto uriEq = [](nsIURI* a, nsIURI* b) -> bool {
    654    bool eq = false;
    655    return a == b || (a && b && NS_SUCCEEDED(a->Equals(b, &eq)) && eq);
    656  };
    657  auto principalEq = [](nsIPrincipal* a, nsIPrincipal* b) -> bool {
    658    return a == b || (a && b && a->Equals(b));
    659  };
    660 
    661  // XXX: Needing to do all of this validation manually is kinda gross.
    662  if (!uriEq(snapshot->GetURI(), aLoadState->URI())) {
    663    return Err("URI");
    664  }
    665  if (!uriEq(snapshot->GetOriginalURI(), aLoadState->OriginalURI())) {
    666    return Err("OriginalURI");
    667  }
    668  if (!aLoadState->ResultPrincipalURIIsSome() ||
    669      !uriEq(snapshot->GetResultPrincipalURI(),
    670             aLoadState->ResultPrincipalURI())) {
    671    return Err("ResultPrincipalURI");
    672  }
    673  if (!uriEq(snapshot->GetUnstrippedURI(), aLoadState->GetUnstrippedURI())) {
    674    return Err("UnstrippedURI");
    675  }
    676  if (!principalEq(snapshot->GetTriggeringPrincipal(),
    677                   aLoadState->TriggeringPrincipal())) {
    678    return Err("TriggeringPrincipal");
    679  }
    680  if (!principalEq(snapshot->GetPrincipalToInherit(),
    681                   aLoadState->PrincipalToInherit())) {
    682    return Err("PrincipalToInherit");
    683  }
    684  if (!principalEq(snapshot->GetPartitionedPrincipalToInherit(),
    685                   aLoadState->PartitionedPrincipalToInherit())) {
    686    return Err("PartitionedPrincipalToInherit");
    687  }
    688 
    689  // Everything matches!
    690  return loading->mEntry;
    691 }
    692 
    693 auto DocumentLoadListener::Open(nsDocShellLoadState* aLoadState,
    694                                LoadInfo* aLoadInfo, nsLoadFlags aLoadFlags,
    695                                uint32_t aCacheKey,
    696                                const Maybe<uint64_t>& aChannelId,
    697                                const TimeStamp& aAsyncOpenTime,
    698                                nsDOMNavigationTiming* aTiming,
    699                                Maybe<ClientInfo>&& aInfo, bool aUrgentStart,
    700                                dom::ContentParent* aContentParent,
    701                                nsresult* aRv) -> RefPtr<OpenPromise> {
    702  auto* loadingContext = GetLoadingBrowsingContext();
    703 
    704  // Snapshot the referrer policy to be used when running the "create internal
    705  // ancestor origins list".
    706  aLoadInfo->SetFrameReferrerPolicySnapshot(
    707      loadingContext->GetEmbedderFrameReferrerPolicy());
    708 
    709  MOZ_DIAGNOSTIC_ASSERT_IF(loadingContext->GetParent(),
    710                           loadingContext->GetParentWindowContext());
    711 
    712  OriginAttributes attrs;
    713  loadingContext->GetOriginAttributes(attrs);
    714 
    715  aLoadInfo->SetContinerFeaturePolicy(
    716      loadingContext->GetContainerFeaturePolicy());
    717 
    718  mLoadIdentifier = aLoadState->GetLoadIdentifier();
    719  // See description of  mFileName in nsDocShellLoadState.h
    720  mIsDownload = !aLoadState->FileName().IsVoid();
    721  mIsLoadingJSURI = aLoadState->URI()->SchemeIs("javascript");
    722  mHTTPSFirstDowngradeData = aLoadState->GetHttpsFirstDowngradeData().forget();
    723 
    724  // Check for infinite recursive object or iframe loads
    725  if (!CheckRecursiveLoad(loadingContext, aLoadState, mIsDocumentLoad)) {
    726    *aRv = NS_ERROR_RECURSIVE_DOCUMENT_LOAD;
    727    mParentChannelListener = nullptr;
    728    return nullptr;
    729  }
    730 
    731  auto* documentContext = GetDocumentBrowsingContext();
    732 
    733  // If we are using SHIP and this load is from session history, validate that
    734  // the load matches our local copy of the loading history entry.
    735  //
    736  // NOTE: Keep this check in-sync with the check in
    737  // `nsDocShellLoadState::GetEffectiveTriggeringRemoteType()`!
    738  RefPtr<SessionHistoryEntry> existingEntry;
    739  if (SessionHistoryInParent() && aLoadState->LoadIsFromSessionHistory() &&
    740      aLoadState->LoadType() != LOAD_ERROR_PAGE) {
    741    Result<SessionHistoryEntry*, const char*> result =
    742        ValidateHistoryLoad(loadingContext, aLoadState);
    743    if (result.isErr()) {
    744      const char* mismatch = result.unwrapErr();
    745      LOG(
    746          ("DocumentLoadListener::Open with invalid loading history entry "
    747           "[this=%p, mismatch=%s]",
    748           this, mismatch));
    749 #ifdef MOZ_DIAGNOSTIC_ASSERT_ENABLED
    750      MOZ_CRASH_UNSAFE_PRINTF(
    751          "DocumentLoadListener::Open for invalid history entry due to "
    752          "mismatch of '%s'",
    753          mismatch);
    754 #endif
    755      *aRv = NS_ERROR_DOM_SECURITY_ERR;
    756      mParentChannelListener = nullptr;
    757      return nullptr;
    758    }
    759 
    760    existingEntry = result.unwrap();
    761    if (!existingEntry->IsInSessionHistory() &&
    762        !documentContext->HasLoadingHistoryEntry(existingEntry)) {
    763      SessionHistoryEntry::RemoveLoadId(
    764          aLoadState->GetLoadingSessionHistoryInfo()->mLoadId);
    765      LOG(
    766          ("DocumentLoadListener::Open with disconnected history entry "
    767           "[this=%p]",
    768           this));
    769 
    770      *aRv = NS_BINDING_ABORTED;
    771      mParentChannelListener = nullptr;
    772      mChannel = nullptr;
    773      return nullptr;
    774    }
    775  }
    776 
    777  if (aLoadState->GetRemoteTypeOverride()) {
    778    if (!mIsDocumentLoad || !NS_IsAboutBlank(aLoadState->URI()) ||
    779        !loadingContext->IsTopContent()) {
    780      LOG(
    781          ("DocumentLoadListener::Open with invalid remoteTypeOverride "
    782           "[this=%p]",
    783           this));
    784      *aRv = NS_ERROR_DOM_SECURITY_ERR;
    785      mParentChannelListener = nullptr;
    786      return nullptr;
    787    }
    788 
    789    mRemoteTypeOverride = aLoadState->GetRemoteTypeOverride();
    790  }
    791 
    792  if (NS_WARN_IF(!loadingContext->IsOwnedByProcess(
    793          GetContentProcessId(aContentParent)))) {
    794    LOG(
    795        ("DocumentLoadListener::Open called from non-current content process "
    796         "[this=%p, current=%" PRIu64 ", caller=%" PRIu64 "]",
    797         this, loadingContext->OwnerProcessId(),
    798         uint64_t(GetContentProcessId(aContentParent))));
    799    *aRv = NS_BINDING_ABORTED;
    800    mParentChannelListener = nullptr;
    801    return nullptr;
    802  }
    803 
    804  if (mIsDocumentLoad && loadingContext->IsContent() &&
    805      NS_WARN_IF(loadingContext->IsReplaced())) {
    806    LOG(
    807        ("DocumentLoadListener::Open called from replaced BrowsingContext "
    808         "[this=%p, browserid=%" PRIx64 ", bcid=%" PRIx64 "]",
    809         this, loadingContext->BrowserId(), loadingContext->Id()));
    810    *aRv = NS_BINDING_ABORTED;
    811    mParentChannelListener = nullptr;
    812    return nullptr;
    813  }
    814 
    815  if (!nsDocShell::CreateAndConfigureRealChannelForLoadState(
    816          loadingContext, aLoadState, aLoadInfo, mParentChannelListener,
    817          nullptr, attrs, aLoadFlags, aCacheKey, *aRv,
    818          getter_AddRefs(mChannel))) {
    819    LOG(("DocumentLoadListener::Open failed to create channel [this=%p]",
    820         this));
    821    mParentChannelListener = nullptr;
    822    return nullptr;
    823  }
    824 
    825  if (nsCOMPtr<nsIHttpChannel> httpChannel = do_QueryInterface(mChannel)) {
    826    // We need to set the partitioned principal to the load state so that we can
    827    // propagate it to the loadingSessionHistoryInfo. To do this, we need to
    828    // get the finalized cookieJarSettings for the channel because it is used by
    829    // StoragePrincipalHelper to mint the partitioned principal.
    830    //
    831    // ClientChannelHelper below also needs us to have finalized the principal
    832    // for the channel because it will request that StoragePrincipalHelper mint
    833    // a principal that needs to match the same principal that a later call to
    834    // StoragePrincipalHelper will mint when determining the right origin to
    835    // look up the ServiceWorker.
    836    //
    837    // Because nsHttpChannel::AsyncOpen calls UpdateAntiTrackingInfoForChannel
    838    // which potentially flips the third party bit/flag on the partition key on
    839    // the cookie jar which impacts the principal that will be minted, it is
    840    // essential that UpdateAntiTrackingInfoForChannel is called before
    841    // AddClientChannelHelperInParent below.
    842    //
    843    // Because the call to UpdateAntiTrackingInfoForChannel is largely
    844    // idempotent, we currently just make the call ourselves right now.  The one
    845    // caveat is that the RFPRandomKey may be spuriously regenerated for
    846    // top-level documents.
    847    AntiTrackingUtils::UpdateAntiTrackingInfoForChannel(httpChannel);
    848 
    849    nsCOMPtr<nsIPrincipal> partitionedPrincipal;
    850 
    851    (void)StoragePrincipalHelper::GetPrincipal(
    852        httpChannel, StoragePrincipalHelper::ePartitionedPrincipal,
    853        getter_AddRefs(partitionedPrincipal));
    854 
    855    aLoadState->SetPartitionedPrincipalToInherit(partitionedPrincipal);
    856  }
    857 
    858  if (documentContext && aLoadState->LoadType() != LOAD_ERROR_PAGE &&
    859      mozilla::SessionHistoryInParent()) {
    860    // It's hard to know at this point whether session history will be enabled
    861    // in the browsing context, so we always create an entry for a load here.
    862    mLoadingSessionHistoryInfo =
    863        documentContext->CreateLoadingSessionHistoryEntryForLoad(
    864            aLoadState, existingEntry, mChannel);
    865    MOZ_ASSERT(mLoadingSessionHistoryInfo);
    866  }
    867 
    868  nsCOMPtr<nsIURI> uriBeingLoaded;
    869  (void)NS_WARN_IF(NS_FAILED(mChannel->GetURI(getter_AddRefs(uriBeingLoaded))));
    870 
    871  RefPtr<HttpBaseChannel> httpBaseChannel = do_QueryObject(mChannel, aRv);
    872  if (uriBeingLoaded && httpBaseChannel) {
    873    nsCOMPtr<nsIURI> topWindowURI;
    874    if (mIsDocumentLoad && loadingContext->IsTop()) {
    875      // If this is for the top level loading, the top window URI should be the
    876      // URI which we are loading.
    877      topWindowURI = uriBeingLoaded;
    878    } else if (RefPtr<WindowGlobalParent> topWindow = AntiTrackingUtils::
    879                   GetTopWindowExcludingExtensionAccessibleContentFrames(
    880                       loadingContext, uriBeingLoaded)) {
    881      nsCOMPtr<nsIPrincipal> topWindowPrincipal =
    882          topWindow->DocumentPrincipal();
    883      if (topWindowPrincipal && !topWindowPrincipal->GetIsNullPrincipal()) {
    884        auto* basePrin = BasePrincipal::Cast(topWindowPrincipal);
    885        basePrin->GetURI(getter_AddRefs(topWindowURI));
    886      }
    887    }
    888    httpBaseChannel->SetTopWindowURI(topWindowURI);
    889  }
    890 
    891  nsCOMPtr<nsIIdentChannel> identChannel = do_QueryInterface(mChannel);
    892  if (identChannel && aChannelId) {
    893    (void)identChannel->SetChannelId(*aChannelId);
    894  }
    895  mDocumentChannelId = aChannelId;
    896 
    897  RefPtr<nsHttpChannel> httpChannelImpl = do_QueryObject(mChannel);
    898  if (httpChannelImpl) {
    899    httpChannelImpl->SetWarningReporter(this);
    900 
    901    if (mIsDocumentLoad && loadingContext->IsTop()) {
    902      httpChannelImpl->SetEarlyHintObserver(this);
    903    }
    904  }
    905 
    906  nsCOMPtr<nsITimedChannel> timedChannel = do_QueryInterface(mChannel);
    907  if (timedChannel) {
    908    timedChannel->SetAsyncOpen(aAsyncOpenTime);
    909  }
    910 
    911  if (nsCOMPtr<nsIHttpChannel> httpChannel = do_QueryInterface(mChannel)) {
    912    (void)httpChannel->SetRequestContextID(
    913        loadingContext->GetRequestContextId());
    914 
    915    nsCOMPtr<nsIClassOfService> cos(do_QueryInterface(httpChannel));
    916    if (cos && aUrgentStart) {
    917      cos->AddClassFlags(nsIClassOfService::UrgentStart);
    918    }
    919  }
    920 
    921  // Setup a ClientChannelHelper to watch for redirects, and copy
    922  // across any serviceworker related data between channels as needed.
    923  AddClientChannelHelperInParent(mChannel, std::move(aInfo));
    924 
    925  if (documentContext && !documentContext->StartDocumentLoad(this)) {
    926    LOG(("DocumentLoadListener::Open failed StartDocumentLoad [this=%p]",
    927         this));
    928    *aRv = NS_BINDING_ABORTED;
    929    mParentChannelListener = nullptr;
    930    mChannel = nullptr;
    931    return nullptr;
    932  }
    933 
    934  // Recalculate the openFlags, matching the logic in use in Content process.
    935  MOZ_ASSERT(!aLoadState->GetPendingRedirectedChannel());
    936  uint32_t openFlags = nsDocShell::ComputeURILoaderFlags(
    937      loadingContext, aLoadState->LoadType(), mIsDocumentLoad);
    938 
    939  RefPtr<ParentProcessDocumentOpenInfo> openInfo =
    940      new ParentProcessDocumentOpenInfo(mParentChannelListener, openFlags,
    941                                        loadingContext, aLoadState->TypeHint(),
    942                                        mIsDocumentLoad);
    943  openInfo->Prepare();
    944 
    945 #ifdef ANDROID
    946  RefPtr<MozPromise<bool, bool, false>> promise;
    947  if (documentContext && aLoadState->LoadType() != LOAD_ERROR_PAGE &&
    948      !(aLoadState->HasInternalLoadFlags(
    949          nsDocShell::INTERNAL_LOAD_FLAGS_BYPASS_LOAD_URI_DELEGATE)) &&
    950      !(aLoadState->LoadType() & LOAD_HISTORY) &&
    951      !nsExternalHelperAppService::ExternalProtocolIsBlockedBySandbox(
    952          documentContext, aLoadState->HasValidUserGestureActivation())) {
    953    nsCOMPtr<nsIWidget> widget =
    954        documentContext->GetParentProcessWidgetContaining();
    955    RefPtr<nsWindow> window = nsWindow::From(widget);
    956 
    957    if (window) {
    958      promise = window->OnLoadRequest(
    959          aLoadState->URI(), nsIBrowserDOMWindow::OPEN_CURRENTWINDOW,
    960          aLoadState->InternalLoadFlags(), aLoadState->TriggeringPrincipal(),
    961          aLoadState->HasValidUserGestureActivation(),
    962          documentContext->IsTopContent());
    963    }
    964  }
    965 
    966  if (promise) {
    967    RefPtr<DocumentLoadListener> self = this;
    968    promise->Then(
    969        GetCurrentSerialEventTarget(), __func__,
    970        [=](const MozPromise<bool, bool, false>::ResolveOrRejectValue& aValue) {
    971          if (aValue.IsResolve()) {
    972            bool handled = aValue.ResolveValue();
    973            if (handled) {
    974              self->DisconnectListeners(NS_ERROR_ABORT, NS_ERROR_ABORT);
    975              mParentChannelListener = nullptr;
    976            } else {
    977              nsresult rv = mChannel->AsyncOpen(openInfo);
    978              if (NS_FAILED(rv)) {
    979                self->DisconnectListeners(rv, rv);
    980                mParentChannelListener = nullptr;
    981              }
    982            }
    983          }
    984        });
    985  } else
    986 #endif /* ANDROID */
    987  {
    988    *aRv = mChannel->AsyncOpen(openInfo);
    989    if (NS_FAILED(*aRv)) {
    990      LOG(("DocumentLoadListener::Open failed AsyncOpen [this=%p rv=%" PRIx32
    991           "]",
    992           this, static_cast<uint32_t>(*aRv)));
    993      if (documentContext) {
    994        documentContext->EndDocumentLoad(false);
    995      }
    996      mParentChannelListener = nullptr;
    997      return nullptr;
    998    }
    999  }
   1000 
   1001  // HTTPS-Only Mode fights potential timeouts caused by upgrades. Instantly
   1002  // after opening the document channel we have to kick off countermeasures.
   1003  nsHTTPSOnlyUtils::PotentiallyFireHttpRequestToShortenTimout(this);
   1004 
   1005  nsCOMPtr<nsILoadInfo> loadInfo = mChannel->LoadInfo();
   1006  loadInfo->SetChannelCreationOriginalURI(aLoadState->URI());
   1007 
   1008  mContentParent = aContentParent;
   1009  mLoadStateExternalLoadFlags = aLoadState->LoadFlags();
   1010  mLoadStateInternalLoadFlags = aLoadState->InternalLoadFlags();
   1011  mLoadStateLoadType = aLoadState->LoadType();
   1012  mTiming = aTiming;
   1013  mSrcdocData = aLoadState->SrcdocData();
   1014  mBaseURI = aLoadState->BaseURI();
   1015  mOriginalUriString = aLoadState->GetOriginalURIString();
   1016  if (documentContext) {
   1017    mParentWindowContext = documentContext->GetParentWindowContext();
   1018  } else {
   1019    mParentWindowContext =
   1020        WindowGlobalParent::GetByInnerWindowId(aLoadInfo->GetInnerWindowID());
   1021    MOZ_RELEASE_ASSERT(mParentWindowContext->GetBrowsingContext() ==
   1022                           GetLoadingBrowsingContext(),
   1023                       "mismatched parent window context?");
   1024  }
   1025 
   1026  // For content-initiated loads, this flag is set in nsDocShell::LoadURI.
   1027  // For parent-initiated loads, we have to set it here.
   1028  // Below comment is copied from nsDocShell::LoadURI -
   1029  // If we have a system triggering principal, we can assume that this load was
   1030  // triggered by some UI in the browser chrome, such as the URL bar or
   1031  // bookmark bar. This should count as a user interaction for the current sh
   1032  // entry, so that the user may navigate back to the current entry, from the
   1033  // entry that is going to be added as part of this load.
   1034  if (!mSupportsRedirectToRealChannel && aLoadState->TriggeringPrincipal() &&
   1035      aLoadState->TriggeringPrincipal()->IsSystemPrincipal()) {
   1036    WindowContext* topWc = loadingContext->GetTopWindowContext();
   1037    if (topWc && !topWc->IsDiscarded()) {
   1038      MOZ_ALWAYS_SUCCEEDS(topWc->SetSHEntryHasUserInteraction(true));
   1039    }
   1040  }
   1041 
   1042  *aRv = NS_OK;
   1043  mOpenPromise = new OpenPromise::Private(__func__);
   1044  // We make the promise use direct task dispatch in order to reduce the number
   1045  // of event loops iterations.
   1046  mOpenPromise->UseDirectTaskDispatch(__func__);
   1047  return mOpenPromise;
   1048 }
   1049 
   1050 auto DocumentLoadListener::OpenDocument(
   1051    nsDocShellLoadState* aLoadState, nsLoadFlags aLoadFlags, uint32_t aCacheKey,
   1052    const Maybe<uint64_t>& aChannelId, const TimeStamp& aAsyncOpenTime,
   1053    nsDOMNavigationTiming* aTiming, Maybe<dom::ClientInfo>&& aInfo,
   1054    bool aUriModified, Maybe<bool> aIsEmbeddingBlockedError,
   1055    dom::ContentParent* aContentParent, nsresult* aRv) -> RefPtr<OpenPromise> {
   1056  LOG(("DocumentLoadListener [%p] OpenDocument [uri=%s]", this,
   1057       aLoadState->URI()->GetSpecOrDefault().get()));
   1058 
   1059  MOZ_ASSERT(mIsDocumentLoad);
   1060 
   1061  RefPtr<CanonicalBrowsingContext> browsingContext =
   1062      GetDocumentBrowsingContext();
   1063 
   1064  // As a security check, check that aLoadFlags matches what we expect. We
   1065  // expect them to be the same we compute on the parent, except for the load
   1066  // group flags.
   1067  {
   1068    const nsLoadFlags parentLoadFlags = aLoadState->CalculateChannelLoadFlags(
   1069        browsingContext, aUriModified, std::move(aIsEmbeddingBlockedError));
   1070    const nsLoadFlags differing = parentLoadFlags ^ aLoadFlags;
   1071    if (differing & ~nsLoadGroup::kInheritedLoadFlags) {
   1072 #ifdef MOZ_DIAGNOSTIC_ASSERT_ENABLED
   1073      MOZ_CRASH_UNSAFE_PRINTF(
   1074          "DocumentLoadListener::OpenDocument: Unexpected load flags: "
   1075          "%x vs. %x (differing %x vs. %x)",
   1076          parentLoadFlags, aLoadFlags, differing & parentLoadFlags,
   1077          differing & aLoadFlags);
   1078 #endif
   1079      *aRv = NS_ERROR_UNEXPECTED;
   1080      return nullptr;
   1081    }
   1082  }
   1083 
   1084  // If this is a top-level load, then rebuild the LoadInfo from scratch,
   1085  // since the goal is to be able to initiate loads in the parent, where the
   1086  // content process won't have provided us with an existing one.
   1087  RefPtr<LoadInfo> loadInfo =
   1088      CreateDocumentLoadInfo(browsingContext, aLoadState);
   1089 
   1090  // Keep track of navigation for the Bounce Tracking Protection.
   1091  if (browsingContext->IsTopContent()) {
   1092    RefPtr<BounceTrackingState> bounceTrackingState =
   1093        browsingContext->GetBounceTrackingState();
   1094 
   1095    // Not every browsing context has a BounceTrackingState. It's also null when
   1096    // the feature is disabled.
   1097    if (bounceTrackingState) {
   1098      nsCOMPtr<nsIPrincipal> triggeringPrincipal;
   1099      nsresult rv =
   1100          loadInfo->GetTriggeringPrincipal(getter_AddRefs(triggeringPrincipal));
   1101 
   1102      if (!NS_WARN_IF(NS_FAILED(rv))) {
   1103        DebugOnly<nsresult> rv = bounceTrackingState->OnStartNavigation(
   1104            triggeringPrincipal, loadInfo->GetHasValidUserGestureActivation());
   1105        NS_WARNING_ASSERTION(NS_SUCCEEDED(rv),
   1106                             "BounceTrackingState::OnStartNavigation failed");
   1107      }
   1108    }
   1109  }
   1110 
   1111  return Open(aLoadState, loadInfo, aLoadFlags, aCacheKey, aChannelId,
   1112              aAsyncOpenTime, aTiming, std::move(aInfo), false, aContentParent,
   1113              aRv);
   1114 }
   1115 
   1116 auto DocumentLoadListener::OpenObject(
   1117    nsDocShellLoadState* aLoadState, uint32_t aCacheKey,
   1118    const Maybe<uint64_t>& aChannelId, const TimeStamp& aAsyncOpenTime,
   1119    nsDOMNavigationTiming* aTiming, Maybe<dom::ClientInfo>&& aInfo,
   1120    uint64_t aInnerWindowId, nsLoadFlags aLoadFlags,
   1121    nsContentPolicyType aContentPolicyType, bool aUrgentStart,
   1122    dom::ContentParent* aContentParent,
   1123    ObjectUpgradeHandler* aObjectUpgradeHandler, nsresult* aRv)
   1124    -> RefPtr<OpenPromise> {
   1125  LOG(("DocumentLoadListener [%p] OpenObject [uri=%s]", this,
   1126       aLoadState->URI()->GetSpecOrDefault().get()));
   1127 
   1128  MOZ_ASSERT(!mIsDocumentLoad);
   1129 
   1130  auto sandboxFlags = aLoadState->TriggeringSandboxFlags();
   1131 
   1132  RefPtr<LoadInfo> loadInfo = CreateObjectLoadInfo(
   1133      aLoadState, aInnerWindowId, aContentPolicyType, sandboxFlags);
   1134 
   1135  mObjectUpgradeHandler = aObjectUpgradeHandler;
   1136 
   1137  return Open(aLoadState, loadInfo, aLoadFlags, aCacheKey, aChannelId,
   1138              aAsyncOpenTime, aTiming, std::move(aInfo), aUrgentStart,
   1139              aContentParent, aRv);
   1140 }
   1141 
   1142 auto DocumentLoadListener::OpenInParent(nsDocShellLoadState* aLoadState,
   1143                                        bool aSupportsRedirectToRealChannel)
   1144    -> RefPtr<OpenPromise> {
   1145  MOZ_ASSERT(mIsDocumentLoad);
   1146 
   1147  // We currently only support passing nullptr for aLoadInfo for
   1148  // top level browsing contexts.
   1149  auto* browsingContext = GetDocumentBrowsingContext();
   1150  if (!browsingContext->IsTopContent() ||
   1151      !browsingContext->GetContentParent()) {
   1152    LOG(("DocumentLoadListener::OpenInParent failed because of subdoc"));
   1153    return nullptr;
   1154  }
   1155 
   1156  // Clone because this mutates the load flags in the load state, which
   1157  // breaks nsDocShells expectations of being able to do it.
   1158  RefPtr<nsDocShellLoadState> loadState = new nsDocShellLoadState(*aLoadState);
   1159  loadState->CalculateLoadURIFlags();
   1160 
   1161  RefPtr<nsDOMNavigationTiming> timing = new nsDOMNavigationTiming(nullptr);
   1162  timing->NotifyNavigationStart(
   1163      browsingContext->IsActive()
   1164          ? nsDOMNavigationTiming::DocShellState::eActive
   1165          : nsDOMNavigationTiming::DocShellState::eInactive);
   1166 
   1167  const mozilla::dom::LoadingSessionHistoryInfo* loadingInfo =
   1168      loadState->GetLoadingSessionHistoryInfo();
   1169 
   1170  uint32_t cacheKey = 0;
   1171  auto loadType = aLoadState->LoadType();
   1172  if (loadType == LOAD_HISTORY || loadType == LOAD_RELOAD_NORMAL ||
   1173      loadType == LOAD_RELOAD_CHARSET_CHANGE ||
   1174      loadType == LOAD_RELOAD_CHARSET_CHANGE_BYPASS_CACHE ||
   1175      loadType == LOAD_RELOAD_CHARSET_CHANGE_BYPASS_PROXY_AND_CACHE) {
   1176    if (loadingInfo) {
   1177      cacheKey = loadingInfo->mInfo.GetCacheKey();
   1178    }
   1179  }
   1180 
   1181  // Loads start in the content process might have exposed a channel id to
   1182  // observers, so we need to preserve the value in the parent. That can't have
   1183  // happened here, so Nothing() is fine.
   1184  Maybe<uint64_t> channelId = Nothing();
   1185 
   1186  // Initial client info is only relevant for subdocument loads, which we're
   1187  // not supporting yet.
   1188  Maybe<dom::ClientInfo> initialClientInfo;
   1189 
   1190  mSupportsRedirectToRealChannel = aSupportsRedirectToRealChannel;
   1191 
   1192  // This is a top-level load, so rebuild the LoadInfo from scratch,
   1193  // since in the parent the
   1194  // content process won't have provided us with an existing one.
   1195  RefPtr<LoadInfo> loadInfo =
   1196      CreateDocumentLoadInfo(browsingContext, aLoadState);
   1197 
   1198  nsLoadFlags loadFlags = loadState->CalculateChannelLoadFlags(
   1199      browsingContext, loadingInfo && loadingInfo->mInfo.GetURIWasModified(),
   1200      Nothing());
   1201 
   1202  nsresult rv;
   1203  return Open(loadState, loadInfo, loadFlags, cacheKey, channelId,
   1204              TimeStamp::Now(), timing, std::move(initialClientInfo), false,
   1205              browsingContext->GetContentParent(), &rv);
   1206 }
   1207 
   1208 base::ProcessId DocumentLoadListener::OtherPid() const {
   1209  return mContentParent ? mContentParent->OtherPid() : base::ProcessId{0};
   1210 }
   1211 
   1212 void DocumentLoadListener::FireStateChange(uint32_t aStateFlags,
   1213                                           nsresult aStatus) {
   1214  nsCOMPtr<nsIChannel> request = GetChannel();
   1215 
   1216  RefPtr<BrowsingContextWebProgress> webProgress =
   1217      GetLoadingBrowsingContext()->GetWebProgress();
   1218 
   1219  if (webProgress) {
   1220    NS_DispatchToMainThread(
   1221        NS_NewRunnableFunction("DocumentLoadListener::FireStateChange", [=]() {
   1222          webProgress->OnStateChange(webProgress, request, aStateFlags,
   1223                                     aStatus);
   1224        }));
   1225  }
   1226 }
   1227 
   1228 static void SetNavigating(CanonicalBrowsingContext* aBrowsingContext,
   1229                          bool aNavigating) {
   1230  nsCOMPtr<nsIBrowser> browser;
   1231  if (RefPtr<Element> currentElement = aBrowsingContext->GetEmbedderElement()) {
   1232    browser = currentElement->AsBrowser();
   1233  }
   1234 
   1235  if (!browser) {
   1236    return;
   1237  }
   1238 
   1239  NS_DispatchToMainThread(NS_NewRunnableFunction(
   1240      "DocumentLoadListener::SetNavigating",
   1241      [browser, aNavigating]() { browser->SetIsNavigating(aNavigating); }));
   1242 }
   1243 
   1244 /* static */ bool DocumentLoadListener::LoadInParent(
   1245    CanonicalBrowsingContext* aBrowsingContext, nsDocShellLoadState* aLoadState,
   1246    bool aSetNavigating) {
   1247  SetNavigating(aBrowsingContext, aSetNavigating);
   1248 
   1249  RefPtr<DocumentLoadListener> load =
   1250      new DocumentLoadListener(aBrowsingContext, true);
   1251  RefPtr<DocumentLoadListener::OpenPromise> promise = load->OpenInParent(
   1252      aLoadState, /* aSupportsRedirectToRealChannel */ false);
   1253  if (!promise) {
   1254    SetNavigating(aBrowsingContext, false);
   1255    return false;
   1256  }
   1257 
   1258  // We passed false for aSupportsRedirectToRealChannel, so we should always
   1259  // take the process switching path, and reject this promise.
   1260  promise->Then(
   1261      GetCurrentSerialEventTarget(), __func__,
   1262      [load](DocumentLoadListener::OpenPromise::ResolveOrRejectValue&& aValue) {
   1263        MOZ_ASSERT(aValue.IsReject());
   1264        DocumentLoadListener::OpenPromiseFailedType& rejectValue =
   1265            aValue.RejectValue();
   1266        if (!rejectValue.mContinueNavigating) {
   1267          // If we're not switching the load to a new process, then it is
   1268          // finished (and failed), and we should fire a state change to notify
   1269          // observers. Normally the docshell would fire this, and it would get
   1270          // filtered out by BrowserParent if needed.
   1271          load->FireStateChange(nsIWebProgressListener::STATE_STOP |
   1272                                    nsIWebProgressListener::STATE_IS_WINDOW |
   1273                                    nsIWebProgressListener::STATE_IS_NETWORK,
   1274                                rejectValue.mStatus);
   1275        }
   1276      });
   1277 
   1278  load->FireStateChange(nsIWebProgressListener::STATE_START |
   1279                            nsIWebProgressListener::STATE_IS_DOCUMENT |
   1280                            nsIWebProgressListener::STATE_IS_REQUEST |
   1281                            nsIWebProgressListener::STATE_IS_WINDOW |
   1282                            nsIWebProgressListener::STATE_IS_NETWORK,
   1283                        NS_OK);
   1284  SetNavigating(aBrowsingContext, false);
   1285  return true;
   1286 }
   1287 
   1288 /* static */
   1289 bool DocumentLoadListener::SpeculativeLoadInParent(
   1290    dom::CanonicalBrowsingContext* aBrowsingContext,
   1291    nsDocShellLoadState* aLoadState) {
   1292  LOG(("DocumentLoadListener::OpenFromParent"));
   1293 
   1294  RefPtr<DocumentLoadListener> listener =
   1295      new DocumentLoadListener(aBrowsingContext, true);
   1296 
   1297  auto promise = listener->OpenInParent(aLoadState, true);
   1298  if (promise) {
   1299    // Create an entry in the redirect channel registrar to
   1300    // allocate an identifier for this load.
   1301    nsCOMPtr<nsIRedirectChannelRegistrar> registrar =
   1302        RedirectChannelRegistrar::GetOrCreate();
   1303    uint64_t loadIdentifier = aLoadState->GetLoadIdentifier();
   1304    DebugOnly<nsresult> rv =
   1305        registrar->RegisterChannel(nullptr, loadIdentifier);
   1306    MOZ_ASSERT(NS_SUCCEEDED(rv));
   1307    // Register listener (as an nsIParentChannel) under our new identifier.
   1308    rv = registrar->LinkChannels(loadIdentifier, listener, nullptr);
   1309    MOZ_ASSERT(NS_SUCCEEDED(rv));
   1310  }
   1311  return !!promise;
   1312 }
   1313 
   1314 void DocumentLoadListener::CleanupParentLoadAttempt(uint64_t aLoadIdent) {
   1315  nsCOMPtr<nsIRedirectChannelRegistrar> registrar =
   1316      RedirectChannelRegistrar::GetOrCreate();
   1317 
   1318  nsCOMPtr<nsIParentChannel> parentChannel;
   1319  registrar->GetParentChannel(aLoadIdent, getter_AddRefs(parentChannel));
   1320  RefPtr<DocumentLoadListener> loadListener = do_QueryObject(parentChannel);
   1321 
   1322  if (loadListener) {
   1323    // If the load listener is still registered, then we must have failed
   1324    // to connect DocumentChannel into it. Better cancel it!
   1325    loadListener->NotifyDocumentChannelFailed();
   1326  }
   1327 
   1328  registrar->DeregisterChannels(aLoadIdent);
   1329 }
   1330 
   1331 auto DocumentLoadListener::ClaimParentLoad(DocumentLoadListener** aListener,
   1332                                           uint64_t aLoadIdent,
   1333                                           Maybe<uint64_t> aChannelId)
   1334    -> RefPtr<OpenPromise> {
   1335  nsCOMPtr<nsIRedirectChannelRegistrar> registrar =
   1336      RedirectChannelRegistrar::GetOrCreate();
   1337 
   1338  nsCOMPtr<nsIParentChannel> parentChannel;
   1339  registrar->GetParentChannel(aLoadIdent, getter_AddRefs(parentChannel));
   1340  RefPtr<DocumentLoadListener> loadListener = do_QueryObject(parentChannel);
   1341  registrar->DeregisterChannels(aLoadIdent);
   1342 
   1343  if (!loadListener) {
   1344    // The parent went away unexpectedly.
   1345    *aListener = nullptr;
   1346    return nullptr;
   1347  }
   1348 
   1349  loadListener->mDocumentChannelId = aChannelId;
   1350 
   1351  MOZ_DIAGNOSTIC_ASSERT(loadListener->mOpenPromise);
   1352  loadListener.forget(aListener);
   1353 
   1354  return (*aListener)->mOpenPromise;
   1355 }
   1356 
   1357 void DocumentLoadListener::NotifyDocumentChannelFailed() {
   1358  LOG(("DocumentLoadListener NotifyDocumentChannelFailed [this=%p]", this));
   1359  // There's been no calls to ClaimParentLoad, and so no listeners have been
   1360  // attached to mOpenPromise yet. As such we can run Then() on it.
   1361  mOpenPromise->Then(
   1362      GetMainThreadSerialEventTarget(), __func__,
   1363      [](DocumentLoadListener::OpenPromiseSucceededType&& aResolveValue) {
   1364        aResolveValue.mPromise->Resolve(NS_BINDING_ABORTED, __func__);
   1365      },
   1366      []() {});
   1367 
   1368  Cancel(NS_BINDING_ABORTED,
   1369         "DocumentLoadListener::NotifyDocumentChannelFailed"_ns);
   1370 }
   1371 
   1372 void DocumentLoadListener::Disconnect(bool aContinueNavigating) {
   1373  LOG(("DocumentLoadListener Disconnect [this=%p, aContinueNavigating=%d]",
   1374       this, aContinueNavigating));
   1375  // The nsHttpChannel may have a reference to this parent, release it
   1376  // to avoid circular references.
   1377  RefPtr<nsHttpChannel> httpChannelImpl = do_QueryObject(mChannel);
   1378  if (httpChannelImpl) {
   1379    httpChannelImpl->SetWarningReporter(nullptr);
   1380    httpChannelImpl->SetEarlyHintObserver(nullptr);
   1381  }
   1382 
   1383  // Don't cancel ongoing early hints when continuing to load the web page.
   1384  // Early hints are loaded earlier in the code and shouldn't get cancelled
   1385  // here. See also: Bug 1765652
   1386  if (!aContinueNavigating) {
   1387    mEarlyHintsService.Cancel("DocumentLoadListener::Disconnect"_ns);
   1388  }
   1389 
   1390  if (auto* ctx = GetDocumentBrowsingContext()) {
   1391    ctx->EndDocumentLoad(aContinueNavigating);
   1392  }
   1393 }
   1394 
   1395 void DocumentLoadListener::Cancel(const nsresult& aStatusCode,
   1396                                  const nsACString& aReason) {
   1397  LOG(
   1398      ("DocumentLoadListener Cancel [this=%p, "
   1399       "aStatusCode=%" PRIx32 " ]",
   1400       this, static_cast<uint32_t>(aStatusCode)));
   1401  if (mOpenPromiseResolved) {
   1402    return;
   1403  }
   1404  if (mChannel) {
   1405    mChannel->CancelWithReason(aStatusCode, aReason);
   1406  }
   1407 
   1408  DisconnectListeners(aStatusCode, aStatusCode);
   1409 }
   1410 
   1411 void DocumentLoadListener::DisconnectListeners(nsresult aStatus,
   1412                                               nsresult aLoadGroupStatus,
   1413                                               bool aContinueNavigating) {
   1414  LOG(
   1415      ("DocumentLoadListener DisconnectListener [this=%p, "
   1416       "aStatus=%" PRIx32 ", aLoadGroupStatus=%" PRIx32
   1417       ", aContinueNavigating=%d]",
   1418       this, static_cast<uint32_t>(aStatus),
   1419       static_cast<uint32_t>(aLoadGroupStatus), aContinueNavigating));
   1420 
   1421  RejectOpenPromise(aStatus, aLoadGroupStatus, aContinueNavigating, __func__);
   1422 
   1423  Disconnect(aContinueNavigating);
   1424 
   1425  // Clear any pending stream filter requests. If we're going to be sending a
   1426  // response to the content process due to a navigation, our caller will have
   1427  // already stashed the array to be passed to `TriggerRedirectToRealChannel`,
   1428  // so it's safe for us to clear here.
   1429  // TODO: If we retargeted the stream to a non-default handler (e.g. to trigger
   1430  // a download), we currently never attach a stream filter. Should we attach a
   1431  // stream filter in those situations as well?
   1432  mStreamFilterRequests.Clear();
   1433 }
   1434 
   1435 void DocumentLoadListener::RedirectToRealChannelFinished(nsresult aRv) {
   1436  LOG(
   1437      ("DocumentLoadListener RedirectToRealChannelFinished [this=%p, "
   1438       "aRv=%" PRIx32 " ]",
   1439       this, static_cast<uint32_t>(aRv)));
   1440  if (NS_FAILED(aRv)) {
   1441    FinishReplacementChannelSetup(aRv);
   1442    return;
   1443  }
   1444 
   1445  // Wait for background channel ready on target channel
   1446  nsCOMPtr<nsIRedirectChannelRegistrar> redirectReg =
   1447      RedirectChannelRegistrar::GetOrCreate();
   1448  MOZ_ASSERT(redirectReg);
   1449 
   1450  nsCOMPtr<nsIParentChannel> redirectParentChannel;
   1451  redirectReg->GetParentChannel(mRedirectChannelId,
   1452                                getter_AddRefs(redirectParentChannel));
   1453  if (!redirectParentChannel) {
   1454    FinishReplacementChannelSetup(NS_ERROR_FAILURE);
   1455    return;
   1456  }
   1457 
   1458  nsCOMPtr<nsIParentRedirectingChannel> redirectingParent =
   1459      do_QueryInterface(redirectParentChannel);
   1460  if (!redirectingParent) {
   1461    // Continue verification procedure if redirecting to non-Http protocol
   1462    FinishReplacementChannelSetup(NS_OK);
   1463    return;
   1464  }
   1465 
   1466  // Ask redirected channel if verification can proceed.
   1467  // ReadyToVerify will be invoked when redirected channel is ready.
   1468  redirectingParent->ContinueVerification(this);
   1469 }
   1470 
   1471 NS_IMETHODIMP
   1472 DocumentLoadListener::ReadyToVerify(nsresult aResultCode) {
   1473  FinishReplacementChannelSetup(aResultCode);
   1474  return NS_OK;
   1475 }
   1476 
   1477 void DocumentLoadListener::FinishReplacementChannelSetup(nsresult aResult) {
   1478  LOG(
   1479      ("DocumentLoadListener FinishReplacementChannelSetup [this=%p, "
   1480       "aResult=%x]",
   1481       this, int(aResult)));
   1482 
   1483  auto endDocumentLoad = MakeScopeExit([&]() {
   1484    if (auto* ctx = GetDocumentBrowsingContext()) {
   1485      ctx->EndDocumentLoad(false);
   1486    }
   1487  });
   1488  mStreamFilterRequests.Clear();
   1489 
   1490  nsCOMPtr<nsIRedirectChannelRegistrar> registrar =
   1491      RedirectChannelRegistrar::GetOrCreate();
   1492  MOZ_ASSERT(registrar);
   1493 
   1494  nsCOMPtr<nsIParentChannel> redirectChannel;
   1495  nsresult rv = registrar->GetParentChannel(mRedirectChannelId,
   1496                                            getter_AddRefs(redirectChannel));
   1497  if (NS_FAILED(rv) || !redirectChannel) {
   1498    aResult = NS_ERROR_FAILURE;
   1499  }
   1500 
   1501  // Release all previously registered channels, they are no longer needed to
   1502  // be kept in the registrar from this moment.
   1503  registrar->DeregisterChannels(mRedirectChannelId);
   1504  mRedirectChannelId = 0;
   1505  if (NS_FAILED(aResult)) {
   1506    if (redirectChannel) {
   1507      redirectChannel->Delete();
   1508    }
   1509    mChannel->Cancel(aResult);
   1510    mChannel->Resume();
   1511    return;
   1512  }
   1513 
   1514  MOZ_ASSERT(
   1515      !SameCOMIdentity(redirectChannel, static_cast<nsIParentChannel*>(this)));
   1516 
   1517  redirectChannel->SetParentListener(mParentChannelListener);
   1518 
   1519  ApplyPendingFunctions(redirectChannel);
   1520 
   1521  if (!ResumeSuspendedChannel(redirectChannel)) {
   1522    nsCOMPtr<nsILoadGroup> loadGroup;
   1523    mChannel->GetLoadGroup(getter_AddRefs(loadGroup));
   1524    if (loadGroup) {
   1525      // We added ourselves to the load group, but attempting
   1526      // to resume has notified us that the channel is already
   1527      // finished. Better remove ourselves from the loadgroup
   1528      // again. The only time the channel will be in a loadgroup
   1529      // is if we're connected to the parent process.
   1530      nsresult status = NS_OK;
   1531      mChannel->GetStatus(&status);
   1532      loadGroup->RemoveRequest(mChannel, nullptr, status);
   1533    }
   1534  }
   1535 }
   1536 
   1537 void DocumentLoadListener::ApplyPendingFunctions(
   1538    nsIParentChannel* aChannel) const {
   1539  // We stored the values from all nsIParentChannel functions called since we
   1540  // couldn't handle them. Copy them across to the real channel since it
   1541  // should know what to do.
   1542 
   1543  nsCOMPtr<nsIParentChannel> parentChannel = aChannel;
   1544  for (const auto& variant : mIParentChannelFunctions) {
   1545    variant.match(
   1546        [parentChannel](const ClassifierMatchedInfoParams& aParams) {
   1547          parentChannel->SetClassifierMatchedInfo(
   1548              aParams.mList, aParams.mProvider, aParams.mFullHash);
   1549        },
   1550        [parentChannel](const ClassifierMatchedTrackingInfoParams& aParams) {
   1551          parentChannel->SetClassifierMatchedTrackingInfo(aParams.mLists,
   1552                                                          aParams.mFullHashes);
   1553        },
   1554        [parentChannel](const ClassificationFlagsParams& aParams) {
   1555          parentChannel->NotifyClassificationFlags(aParams.mClassificationFlags,
   1556                                                   aParams.mIsThirdParty);
   1557        });
   1558  }
   1559 
   1560  RefPtr<HttpChannelSecurityWarningReporter> reporter;
   1561  if (RefPtr<HttpChannelParent> httpParent = do_QueryObject(aChannel)) {
   1562    reporter = httpParent;
   1563  } else if (RefPtr<nsHttpChannel> httpChannel = do_QueryObject(aChannel)) {
   1564    reporter = httpChannel->GetWarningReporter();
   1565  }
   1566  if (reporter) {
   1567    for (const auto& variant : mSecurityWarningFunctions) {
   1568      variant.match(
   1569          [reporter](const ReportSecurityMessageParams& aParams) {
   1570            (void)reporter->ReportSecurityMessage(aParams.mMessageTag,
   1571                                                  aParams.mMessageCategory);
   1572          },
   1573          [reporter](const LogBlockedCORSRequestParams& aParams) {
   1574            (void)reporter->LogBlockedCORSRequest(
   1575                aParams.mMessage, aParams.mCategory, aParams.mIsWarning);
   1576          },
   1577          [reporter](const LogMimeTypeMismatchParams& aParams) {
   1578            (void)reporter->LogMimeTypeMismatch(aParams.mMessageName,
   1579                                                aParams.mWarning, aParams.mURL,
   1580                                                aParams.mContentType);
   1581          });
   1582    }
   1583  }
   1584 }
   1585 
   1586 bool DocumentLoadListener::ResumeSuspendedChannel(
   1587    nsIStreamListener* aListener) {
   1588  LOG(("DocumentLoadListener ResumeSuspendedChannel [this=%p]", this));
   1589  RefPtr<nsHttpChannel> httpChannel = do_QueryObject(mChannel);
   1590  if (httpChannel) {
   1591    httpChannel->SetApplyConversion(mOldApplyConversion);
   1592  }
   1593 
   1594  if (!mIsFinished) {
   1595    mParentChannelListener->SetListenerAfterRedirect(aListener);
   1596  }
   1597 
   1598  // If we failed to suspend the channel, then we might have received
   1599  // some messages while the redirected was being handled.
   1600  // Manually send them on now.
   1601  nsTArray<StreamListenerFunction> streamListenerFunctions =
   1602      std::move(mStreamListenerFunctions);
   1603  if (!aListener) {
   1604    streamListenerFunctions.Clear();
   1605  }
   1606 
   1607  ForwardStreamListenerFunctions(std::move(streamListenerFunctions), aListener);
   1608 
   1609  // We don't expect to get new stream listener functions added
   1610  // via re-entrancy. If this ever happens, we should understand
   1611  // exactly why before allowing it.
   1612  NS_ASSERTION(mStreamListenerFunctions.IsEmpty(),
   1613               "Should not have added new stream listener function!");
   1614 
   1615  mChannel->Resume();
   1616 
   1617  // Our caller will invoke `EndDocumentLoad` for us.
   1618 
   1619  return !mIsFinished;
   1620 }
   1621 
   1622 void DocumentLoadListener::CancelEarlyHintPreloads() {
   1623  mEarlyHintsService.Cancel("DocumentLoadListener::CancelEarlyHintPreloads"_ns);
   1624 }
   1625 
   1626 void DocumentLoadListener::RegisterEarlyHintLinksAndGetConnectArgs(
   1627    dom::ContentParentId aCpId, nsTArray<EarlyHintConnectArgs>& aOutLinks) {
   1628  mEarlyHintsService.RegisterLinksAndGetConnectArgs(aCpId, aOutLinks);
   1629 }
   1630 
   1631 void DocumentLoadListener::SerializeRedirectData(
   1632    RedirectToRealChannelArgs& aArgs, bool aIsCrossProcess,
   1633    uint32_t aRedirectFlags, uint32_t aLoadFlags,
   1634    nsTArray<EarlyHintConnectArgs>&& aEarlyHints,
   1635    uint32_t aEarlyHintLinkType) const {
   1636  aArgs.uri() = GetChannelCreationURI();
   1637  aArgs.loadIdentifier() = mLoadIdentifier;
   1638  aArgs.earlyHints() = std::move(aEarlyHints);
   1639  aArgs.earlyHintLinkType() = aEarlyHintLinkType;
   1640 
   1641  // I previously used HttpBaseChannel::CloneLoadInfoForRedirect, but that
   1642  // clears the principal to inherit, which fails tests (probably because this
   1643  // 'redirect' is usually just an implementation detail). It's also http
   1644  // only, and mChannel can be anything that we redirected to.
   1645  nsCOMPtr<nsILoadInfo> channelLoadInfo = mChannel->LoadInfo();
   1646  nsCOMPtr<nsIPrincipal> principalToInherit;
   1647  channelLoadInfo->GetPrincipalToInherit(getter_AddRefs(principalToInherit));
   1648 
   1649  const RefPtr<nsHttpChannel> baseChannel = do_QueryObject(mChannel);
   1650 
   1651  nsCOMPtr<nsILoadContext> loadContext;
   1652  NS_QueryNotificationCallbacks(mChannel, loadContext);
   1653  nsCOMPtr<nsILoadInfo> redirectLoadInfo;
   1654 
   1655  // Only use CloneLoadInfoForRedirect if we have a load context,
   1656  // since it internally tries to pull OriginAttributes from the
   1657  // the load context and asserts if they don't match the load info.
   1658  // We can end up without a load context if the channel has been aborted
   1659  // and the callbacks have been cleared.
   1660  if (baseChannel && loadContext) {
   1661    redirectLoadInfo = baseChannel->CloneLoadInfoForRedirect(
   1662        aArgs.uri(), nsIChannelEventSink::REDIRECT_INTERNAL);
   1663    redirectLoadInfo->SetResultPrincipalURI(aArgs.uri());
   1664 
   1665    // The clone process clears this, and then we fail tests..
   1666    // docshell/test/mochitest/test_forceinheritprincipal_overrule_owner.html
   1667    if (principalToInherit) {
   1668      redirectLoadInfo->SetPrincipalToInherit(principalToInherit);
   1669    }
   1670  } else {
   1671    redirectLoadInfo =
   1672        static_cast<mozilla::net::LoadInfo*>(channelLoadInfo.get())->Clone();
   1673 
   1674    redirectLoadInfo->AppendRedirectHistoryEntry(mChannel, true);
   1675  }
   1676 
   1677  const Maybe<ClientInfo>& reservedClientInfo =
   1678      channelLoadInfo->GetReservedClientInfo();
   1679  if (reservedClientInfo) {
   1680    redirectLoadInfo->SetReservedClientInfo(*reservedClientInfo);
   1681  }
   1682 
   1683  aArgs.registrarId() = mRedirectChannelId;
   1684 
   1685 #ifdef DEBUG
   1686  // We only set the granularFingerprintingProtection field when opening http
   1687  // channels. So, we mark the field as set here if the channel is not a
   1688  // nsHTTPChannel to pass the assertion check for getting this field in below
   1689  // LoadInfoToLoadInfoArgs() call.
   1690  if (!baseChannel) {
   1691    static_cast<mozilla::net::LoadInfo*>(redirectLoadInfo.get())
   1692        ->MarkOverriddenFingerprintingSettingsAsSet();
   1693  }
   1694 #endif
   1695 
   1696  MOZ_ALWAYS_SUCCEEDS(
   1697      ipc::LoadInfoToLoadInfoArgs(redirectLoadInfo, &aArgs.loadInfo()));
   1698 
   1699  if (StaticPrefs::dom_location_ancestorOrigins_enabled()) {
   1700    MOZ_ASSERT(XRE_IsParentProcess());
   1701    if (RefPtr bc = redirectLoadInfo->GetFrameBrowsingContext()) {
   1702      nsCOMPtr<nsIPrincipal> resultPrincipal;
   1703      // If this fails, we get an empty location.ancestorOrigins list
   1704      if (NS_SUCCEEDED(
   1705              nsContentUtils::GetSecurityManager()->GetChannelResultPrincipal(
   1706                  mChannel, getter_AddRefs(resultPrincipal)))) {
   1707        const auto referrerPolicy =
   1708            static_cast<LoadInfo*>(channelLoadInfo.get())
   1709                ->GetFrameReferrerPolicySnapshot();
   1710        bc->Canonical()->CreateRedactedAncestorOriginsList(resultPrincipal,
   1711                                                           referrerPolicy);
   1712      }
   1713 
   1714      // convert principals to IPC data
   1715      constexpr auto prepareInfo =
   1716          [](nsIPrincipal* aPrincipal) -> Maybe<ipc::PrincipalInfo> {
   1717        if (aPrincipal == nullptr) {
   1718          return Nothing();
   1719        }
   1720        ipc::PrincipalInfo data;
   1721        return NS_SUCCEEDED(PrincipalToPrincipalInfo(aPrincipal, &data))
   1722                   ? Some(std::move(data))
   1723                   : Nothing();
   1724      };
   1725 
   1726      // The ancestorOrigins list the document should ultimately have, that we
   1727      // send down with load args.
   1728      auto& ancestorOrigins = aArgs.loadInfo().ancestorOrigins();
   1729      for (const auto& ancestorPrincipal :
   1730           bc->Canonical()->GetPossiblyRedactedAncestorOriginsList()) {
   1731        ancestorOrigins.AppendElement(prepareInfo(ancestorPrincipal));
   1732      }
   1733    }
   1734  }
   1735 
   1736  mChannel->GetOriginalURI(getter_AddRefs(aArgs.originalURI()));
   1737 
   1738  // mChannel can be a nsHttpChannel as well as InterceptedHttpChannel so we
   1739  // can't use baseChannel here.
   1740  if (nsCOMPtr<nsIHttpChannel> httpChannel = do_QueryInterface(mChannel)) {
   1741    MOZ_ALWAYS_SUCCEEDS(httpChannel->GetChannelId(&aArgs.channelId()));
   1742 
   1743    // propagated the channel's referrerInfo back to child if the redirection
   1744    // is caused by ServiceWorker interception.
   1745    if (nsCOMPtr<nsIInterceptedChannel> interceptedChannel =
   1746            do_QueryInterface(mChannel)) {
   1747      nsCOMPtr<nsIReferrerInfo> referrerInfo;
   1748      MOZ_ALWAYS_SUCCEEDS(
   1749          httpChannel->GetReferrerInfo(getter_AddRefs(referrerInfo)));
   1750      if (referrerInfo) {
   1751        aArgs.referrerInfo() = referrerInfo;
   1752      }
   1753    }
   1754  }
   1755 
   1756  aArgs.redirectMode() = nsIHttpChannelInternal::REDIRECT_MODE_FOLLOW;
   1757  nsCOMPtr<nsIHttpChannelInternal> httpChannelInternal =
   1758      do_QueryInterface(mChannel);
   1759  if (httpChannelInternal) {
   1760    MOZ_ALWAYS_SUCCEEDS(
   1761        httpChannelInternal->GetRedirectMode(&aArgs.redirectMode()));
   1762  }
   1763 
   1764  if (baseChannel) {
   1765    aArgs.init() =
   1766        Some(baseChannel
   1767                 ->CloneReplacementChannelConfig(
   1768                     true, aRedirectFlags,
   1769                     HttpBaseChannel::ReplacementReason::DocumentChannel)
   1770                 .Serialize());
   1771  }
   1772 
   1773  uint32_t contentDispositionTemp;
   1774  nsresult rv = mChannel->GetContentDisposition(&contentDispositionTemp);
   1775  if (NS_SUCCEEDED(rv)) {
   1776    aArgs.contentDisposition() = Some(contentDispositionTemp);
   1777  }
   1778 
   1779  nsString contentDispositionFilenameTemp;
   1780  rv = mChannel->GetContentDispositionFilename(contentDispositionFilenameTemp);
   1781  if (NS_SUCCEEDED(rv)) {
   1782    aArgs.contentDispositionFilename() = Some(contentDispositionFilenameTemp);
   1783  }
   1784 
   1785  SetNeedToAddURIVisit(mChannel, false);
   1786 
   1787  aArgs.newLoadFlags() = aLoadFlags;
   1788  aArgs.redirectFlags() = aRedirectFlags;
   1789  aArgs.properties() = do_QueryObject(mChannel);
   1790  aArgs.srcdocData() = mSrcdocData;
   1791  aArgs.baseUri() = mBaseURI;
   1792  aArgs.loadStateExternalLoadFlags() = mLoadStateExternalLoadFlags;
   1793  aArgs.loadStateInternalLoadFlags() = mLoadStateInternalLoadFlags;
   1794  aArgs.loadStateLoadType() = mLoadStateLoadType;
   1795  aArgs.originalUriString() = mOriginalUriString;
   1796  if (mLoadingSessionHistoryInfo) {
   1797    aArgs.loadingSessionHistoryInfo().emplace(*mLoadingSessionHistoryInfo);
   1798  }
   1799 }
   1800 
   1801 static bool IsFirstLoadInWindow(nsIChannel* aChannel) {
   1802  nsCOMPtr<nsILoadInfo> loadInfo = aChannel->LoadInfo();
   1803  return loadInfo->GetIsNewWindowTarget();
   1804 }
   1805 
   1806 // Get where the document loaded by this nsIChannel should be rendered. This
   1807 // will be `OPEN_CURRENTWINDOW` unless we're loading an attachment which would
   1808 // normally open in an external program, but we're instead choosing to render
   1809 // internally.
   1810 static int32_t GetWhereToOpen(nsIChannel* aChannel, bool aIsDocumentLoad) {
   1811  // Ignore content disposition for loads from an object or embed element.
   1812  if (!aIsDocumentLoad) {
   1813    return nsIBrowserDOMWindow::OPEN_CURRENTWINDOW;
   1814  }
   1815 
   1816  // Always continue in the same window if we're not loading an attachment.
   1817  uint32_t disposition = nsIChannel::DISPOSITION_INLINE;
   1818  if (NS_FAILED(aChannel->GetContentDisposition(&disposition)) ||
   1819      disposition != nsIChannel::DISPOSITION_ATTACHMENT) {
   1820    return nsIBrowserDOMWindow::OPEN_CURRENTWINDOW;
   1821  }
   1822 
   1823  // If the channel is for a new window target, continue in the same window.
   1824  if (IsFirstLoadInWindow(aChannel)) {
   1825    return nsIBrowserDOMWindow::OPEN_CURRENTWINDOW;
   1826  }
   1827 
   1828  // Respect the user's preferences with browser.link.open_newwindow
   1829  // FIXME: There should probably be a helper for this, as the logic is
   1830  // duplicated in a few places.
   1831  int32_t where = Preferences::GetInt("browser.link.open_newwindow",
   1832                                      nsIBrowserDOMWindow::OPEN_NEWTAB);
   1833  if (where == nsIBrowserDOMWindow::OPEN_CURRENTWINDOW ||
   1834      where == nsIBrowserDOMWindow::OPEN_NEWWINDOW ||
   1835      where == nsIBrowserDOMWindow::OPEN_NEWTAB) {
   1836    return where;
   1837  }
   1838  // NOTE: nsIBrowserDOMWindow::OPEN_NEWTAB_BACKGROUND and
   1839  //       nsIBrowserDOMWindow::OPEN_NEWTAB_FOREGROUND are not allowed as pref
   1840  //       values.
   1841  return nsIBrowserDOMWindow::OPEN_NEWTAB;
   1842 }
   1843 
   1844 static bool ContextCanProcessSwitch(CanonicalBrowsingContext* aBrowsingContext,
   1845                                    WindowGlobalParent* aParentWindow,
   1846                                    bool aSwitchToNewTab) {
   1847  if (NS_WARN_IF(!aBrowsingContext)) {
   1848    MOZ_LOG(gProcessIsolationLog, LogLevel::Warning,
   1849            ("Process Switch Abort: no browsing context"));
   1850    return false;
   1851  }
   1852  if (!aBrowsingContext->IsContent()) {
   1853    MOZ_LOG(gProcessIsolationLog, LogLevel::Warning,
   1854            ("Process Switch Abort: non-content browsing context"));
   1855    return false;
   1856  }
   1857 
   1858  // If we're switching into a new tab, we can skip the remaining checks, as
   1859  // we're not actually changing the process of aBrowsingContext, so whether or
   1860  // not it is allowed to process switch isn't relevant.
   1861  if (aSwitchToNewTab) {
   1862    return true;
   1863  }
   1864 
   1865  if (aParentWindow) {
   1866    // If remote subframes are disabled, subframes never process switch.
   1867    if (!aBrowsingContext->UseRemoteSubframes()) {
   1868      MOZ_LOG(gProcessIsolationLog, LogLevel::Warning,
   1869              ("Process Switch Abort: remote subframes disabled"));
   1870      return false;
   1871    }
   1872 
   1873    // Otherwise, subframes can always process-switch unless they are directly
   1874    // embedded within a parent-process document.
   1875    if (aParentWindow->IsInProcess()) {
   1876      MOZ_LOG(gProcessIsolationLog, LogLevel::Warning,
   1877              ("Process Switch Abort: Subframe with in-process parent"));
   1878      return false;
   1879    }
   1880    return true;
   1881  }
   1882 
   1883  // Check if the "maychangeremoteness" attribute is present on the embedding
   1884  // element. Assume the context can process switch if the embedder element is
   1885  // unknown, as it's safer to fail to switch in that scenario.
   1886  Element* browserElement = aBrowsingContext->Top()->GetEmbedderElement();
   1887  if (browserElement &&
   1888      !browserElement->HasAttribute(u"maychangeremoteness"_ns)) {
   1889    MOZ_LOG(gProcessIsolationLog, LogLevel::Warning,
   1890            ("Process Switch Abort: toplevel switch disabled by <browser>"));
   1891    return false;
   1892  }
   1893 
   1894  // In some tests, we use `createWindowlessBrowser(false)` to create a
   1895  // windowless content browser. As process switching relies on the `<browser>`
   1896  // element to perform a remote abstraction, we cannot perform process
   1897  // switching on the root element of this browser.
   1898  if (!browserElement && aBrowsingContext->Windowless()) {
   1899    MOZ_LOG(gProcessIsolationLog, LogLevel::Warning,
   1900            ("Process Switch Abort: switch disabled by windowless browser"));
   1901    return false;
   1902  }
   1903 
   1904  return true;
   1905 }
   1906 
   1907 static RefPtr<dom::BrowsingContextCallbackReceivedPromise> SwitchToNewTab(
   1908    CanonicalBrowsingContext* aLoadingBrowsingContext, int32_t aWhere) {
   1909  MOZ_ASSERT(aWhere == nsIBrowserDOMWindow::OPEN_NEWTAB ||
   1910                 aWhere == nsIBrowserDOMWindow::OPEN_NEWTAB_BACKGROUND ||
   1911                 aWhere == nsIBrowserDOMWindow::OPEN_NEWTAB_FOREGROUND ||
   1912                 aWhere == nsIBrowserDOMWindow::OPEN_NEWWINDOW,
   1913             "Unsupported open location");
   1914 
   1915  auto promise =
   1916      MakeRefPtr<dom::BrowsingContextCallbackReceivedPromise::Private>(
   1917          __func__);
   1918 
   1919  // Get the nsIBrowserDOMWindow for the given BrowsingContext's tab.
   1920  nsCOMPtr<nsIBrowserDOMWindow> browserDOMWindow =
   1921      aLoadingBrowsingContext->GetBrowserDOMWindow();
   1922  if (NS_WARN_IF(!browserDOMWindow)) {
   1923    MOZ_LOG(gProcessIsolationLog, LogLevel::Warning,
   1924            ("Process Switch Abort: Unable to get nsIBrowserDOMWindow"));
   1925    promise->Reject(NS_ERROR_FAILURE, __func__);
   1926    return promise;
   1927  }
   1928 
   1929  // Open a new content tab by calling into frontend. We don't need to worry
   1930  // about the triggering principal or CSP, as createContentWindow doesn't
   1931  // actually start loading anything, but use a null principal anyway in case
   1932  // something changes.
   1933  nsCOMPtr<nsIPrincipal> triggeringPrincipal =
   1934      NullPrincipal::Create(aLoadingBrowsingContext->OriginAttributesRef());
   1935 
   1936  RefPtr<nsOpenWindowInfo> openInfo = new nsOpenWindowInfo();
   1937  openInfo->mBrowsingContextReadyCallback =
   1938      new nsBrowsingContextReadyCallback(promise);
   1939  openInfo->mParent = aLoadingBrowsingContext;
   1940  openInfo->mForceNoOpener = true;
   1941  openInfo->mIsRemote = true;
   1942  openInfo->mPrincipalToInheritForAboutBlank = triggeringPrincipal;
   1943 
   1944  // Do the actual work to open a new tab or window async.
   1945  nsresult rv = NS_DispatchToMainThread(NS_NewRunnableFunction(
   1946      "DocumentLoadListener::SwitchToNewTab",
   1947      [browserDOMWindow, openInfo, aWhere, triggeringPrincipal, promise] {
   1948        RefPtr<BrowsingContext> bc;
   1949        nsresult rv = browserDOMWindow->CreateContentWindow(
   1950            /* uri */ nullptr, openInfo, aWhere,
   1951            nsIBrowserDOMWindow::OPEN_NO_REFERRER, triggeringPrincipal,
   1952            /* policyContainer */ nullptr, getter_AddRefs(bc));
   1953        if (NS_WARN_IF(NS_FAILED(rv))) {
   1954          MOZ_LOG(gProcessIsolationLog, LogLevel::Warning,
   1955                  ("Process Switch Abort: CreateContentWindow threw"));
   1956          promise->Reject(rv, __func__);
   1957        }
   1958        if (bc) {
   1959          promise->Resolve(bc, __func__);
   1960        }
   1961      }));
   1962  if (NS_WARN_IF(NS_FAILED(rv))) {
   1963    promise->Reject(NS_ERROR_UNEXPECTED, __func__);
   1964  }
   1965  return promise;
   1966 }
   1967 
   1968 bool DocumentLoadListener::MaybeTriggerProcessSwitch(
   1969    bool* aWillSwitchToRemote) {
   1970  MOZ_ASSERT(XRE_IsParentProcess());
   1971  MOZ_DIAGNOSTIC_ASSERT(mChannel);
   1972  MOZ_DIAGNOSTIC_ASSERT(mParentChannelListener);
   1973  MOZ_DIAGNOSTIC_ASSERT(aWillSwitchToRemote);
   1974 
   1975  MOZ_LOG(gProcessIsolationLog, LogLevel::Verbose,
   1976          ("DocumentLoadListener MaybeTriggerProcessSwitch [this=%p, uri=%s, "
   1977           "browserid=%" PRIx64 "]",
   1978           this, GetChannelCreationURI()->GetSpecOrDefault().get(),
   1979           GetLoadingBrowsingContext()->Top()->BrowserId()));
   1980 
   1981  // Check if we should handle this load in a different tab or window.
   1982  int32_t where = GetWhereToOpen(mChannel, mIsDocumentLoad);
   1983  bool switchToNewTab = where != nsIBrowserDOMWindow::OPEN_CURRENTWINDOW;
   1984 
   1985  // Get the loading BrowsingContext. This may not be the context which will be
   1986  // switching processes when switching to a new tab, and in the case of an
   1987  // <object> or <embed> element, as we don't create the final context until
   1988  // after process selection.
   1989  //
   1990  // - /!\ WARNING /!\ -
   1991  // Don't use `browsingContext->IsTop()` in this method! It will behave
   1992  // incorrectly for non-document loads such as `<object>` or `<embed>`.
   1993  // Instead, check whether or not `parentWindow` is null.
   1994  RefPtr<CanonicalBrowsingContext> browsingContext =
   1995      GetLoadingBrowsingContext();
   1996  // If switching to a new tab, the final BC isn't a frame.
   1997  RefPtr<WindowGlobalParent> parentWindow =
   1998      switchToNewTab ? nullptr : GetParentWindowContext();
   1999  if (!ContextCanProcessSwitch(browsingContext, parentWindow, switchToNewTab)) {
   2000    return false;
   2001  }
   2002 
   2003  if (!browsingContext->IsOwnedByProcess(GetContentProcessId(mContentParent))) {
   2004    MOZ_LOG(gProcessIsolationLog, LogLevel::Error,
   2005            ("Process Switch Abort: context no longer owned by creator"));
   2006    Cancel(NS_BINDING_ABORTED,
   2007           "Process Switch Abort: context no longer owned by creator"_ns);
   2008    return false;
   2009  }
   2010 
   2011  if (browsingContext->IsReplaced()) {
   2012    MOZ_LOG(gProcessIsolationLog, LogLevel::Warning,
   2013            ("Process Switch Abort: replaced browsing context"));
   2014    Cancel(NS_BINDING_ABORTED,
   2015           "Process Switch Abort: replaced browsing context"_ns);
   2016    return false;
   2017  }
   2018 
   2019  nsAutoCString currentRemoteType(NOT_REMOTE_TYPE);
   2020  if (mContentParent) {
   2021    currentRemoteType = mContentParent->GetRemoteType();
   2022  }
   2023 
   2024  auto optionsResult = IsolationOptionsForNavigation(
   2025      browsingContext->Top(), switchToNewTab ? nullptr : parentWindow.get(),
   2026      GetChannelCreationURI(), mChannel, currentRemoteType,
   2027      HasCrossOriginOpenerPolicyMismatch(), switchToNewTab, mLoadStateLoadType,
   2028      mDocumentChannelId, mRemoteTypeOverride);
   2029  if (optionsResult.isErr()) {
   2030    MOZ_LOG(gProcessIsolationLog, LogLevel::Error,
   2031            ("Process Switch Abort: CheckIsolationForNavigation Failed with %s",
   2032             GetStaticErrorName(optionsResult.inspectErr())));
   2033    Cancel(optionsResult.unwrapErr(),
   2034           "Process Switch Abort: CheckIsolationForNavigation Failed"_ns);
   2035    return false;
   2036  }
   2037 
   2038  NavigationIsolationOptions options = optionsResult.unwrap();
   2039 
   2040  if (options.mTryUseBFCache) {
   2041    MOZ_ASSERT(!parentWindow, "Can only BFCache toplevel windows");
   2042    MOZ_ASSERT(!switchToNewTab, "Can't BFCache for a tab switch");
   2043    bool sameOrigin = false;
   2044    if (auto* wgp = browsingContext->GetCurrentWindowGlobal()) {
   2045      nsCOMPtr<nsIPrincipal> resultPrincipal;
   2046      MOZ_ALWAYS_SUCCEEDS(
   2047          nsContentUtils::GetSecurityManager()->GetChannelResultPrincipal(
   2048              mChannel, getter_AddRefs(resultPrincipal)));
   2049      sameOrigin =
   2050          wgp->DocumentPrincipal()->EqualsConsideringDomain(resultPrincipal);
   2051    }
   2052 
   2053    // We only reset the window name for content.
   2054    mLoadingSessionHistoryInfo->mForceMaybeResetName.emplace(
   2055        StaticPrefs::privacy_window_name_update_enabled() &&
   2056        browsingContext->IsContent() && !sameOrigin);
   2057  }
   2058 
   2059  MOZ_LOG(
   2060      gProcessIsolationLog, LogLevel::Verbose,
   2061      ("CheckIsolationForNavigation -> current:(%s) remoteType:(%s) replace:%d "
   2062       "group:%" PRIx64 " bfcache:%d shentry:%p newTab:%d",
   2063       currentRemoteType.get(), options.mRemoteType.get(),
   2064       options.mReplaceBrowsingContext, options.mSpecificGroupId,
   2065       options.mTryUseBFCache, options.mActiveSessionHistoryEntry.get(),
   2066       switchToNewTab));
   2067 
   2068  // Check if a process switch is needed.
   2069  if (currentRemoteType == options.mRemoteType &&
   2070      !options.mReplaceBrowsingContext && !switchToNewTab) {
   2071    MOZ_LOG(gProcessIsolationLog, LogLevel::Info,
   2072            ("Process Switch Abort: type (%s) is compatible",
   2073             options.mRemoteType.get()));
   2074    return false;
   2075  }
   2076 
   2077  if (NS_WARN_IF(parentWindow && options.mRemoteType.IsEmpty())) {
   2078    MOZ_LOG(gProcessIsolationLog, LogLevel::Error,
   2079            ("Process Switch Abort: non-remote target process for subframe"));
   2080    return false;
   2081  }
   2082 
   2083  *aWillSwitchToRemote = !options.mRemoteType.IsEmpty();
   2084 
   2085  // If we've decided to re-target this load into a new tab or window (see
   2086  // `GetWhereToOpen`), do so before performing a process switch. This will
   2087  // require creating the new <browser> to load in, which may be performed
   2088  // async.
   2089  if (switchToNewTab) {
   2090    SwitchToNewTab(browsingContext, where)
   2091        ->Then(
   2092            GetMainThreadSerialEventTarget(), __func__,
   2093            [self = RefPtr{this},
   2094             options](const RefPtr<BrowsingContext>& aBrowsingContext)
   2095                MOZ_CAN_RUN_SCRIPT_BOUNDARY_LAMBDA mutable {
   2096                  if (aBrowsingContext->IsDiscarded()) {
   2097                    MOZ_LOG(gProcessIsolationLog, LogLevel::Error,
   2098                            ("Process Switch: Got invalid new-tab "
   2099                             "BrowsingContext"));
   2100                    self->RedirectToRealChannelFinished(NS_ERROR_FAILURE);
   2101                    return;
   2102                  }
   2103 
   2104                  MOZ_LOG(gProcessIsolationLog, LogLevel::Verbose,
   2105                          ("Process Switch: Redirected load to new tab"));
   2106                  self->TriggerProcessSwitch(
   2107                      MOZ_KnownLive(aBrowsingContext->Canonical()), options,
   2108                      /* aIsNewTab */ true);
   2109                },
   2110            [self = RefPtr{this}](const CopyableErrorResult&) {
   2111              MOZ_LOG(gProcessIsolationLog, LogLevel::Error,
   2112                      ("Process Switch: SwitchToNewTab failed"));
   2113              self->RedirectToRealChannelFinished(NS_ERROR_FAILURE);
   2114            });
   2115    return true;
   2116  }
   2117 
   2118  // If we're doing a document load, we can immediately perform a process
   2119  // switch.
   2120  if (mIsDocumentLoad) {
   2121    TriggerProcessSwitch(browsingContext, options);
   2122    return true;
   2123  }
   2124 
   2125  // We're not doing a document load, which means we must be performing an
   2126  // object load. We need a BrowsingContext to perform the switch in, so will
   2127  // trigger an upgrade.
   2128  if (!mObjectUpgradeHandler) {
   2129    MOZ_LOG(gProcessIsolationLog, LogLevel::Warning,
   2130            ("Process Switch Abort: no object upgrade handler"));
   2131    return false;
   2132  }
   2133 
   2134  mObjectUpgradeHandler->UpgradeObjectLoad()->Then(
   2135      GetMainThreadSerialEventTarget(), __func__,
   2136      [self = RefPtr{this}, options,
   2137       parentWindow](const RefPtr<CanonicalBrowsingContext>& aBrowsingContext)
   2138          MOZ_CAN_RUN_SCRIPT_BOUNDARY_LAMBDA mutable {
   2139            if (aBrowsingContext->IsDiscarded() ||
   2140                parentWindow != aBrowsingContext->GetParentWindowContext()) {
   2141              MOZ_LOG(
   2142                  gProcessIsolationLog, LogLevel::Error,
   2143                  ("Process Switch: Got invalid BrowsingContext from object "
   2144                   "upgrade!"));
   2145              self->RedirectToRealChannelFinished(NS_ERROR_FAILURE);
   2146              return;
   2147            }
   2148 
   2149            // At this point the element has stored the container feature policy
   2150            // in the new browsing context, but we need to make sure that we
   2151            // copy it over to the load info.
   2152            nsCOMPtr<nsILoadInfo> loadInfo = self->mChannel->LoadInfo();
   2153            if (aBrowsingContext->GetContainerFeaturePolicy()) {
   2154              loadInfo->SetContainerFeaturePolicyInfo(
   2155                  *aBrowsingContext->GetContainerFeaturePolicy());
   2156            }
   2157 
   2158            MOZ_LOG(gProcessIsolationLog, LogLevel::Verbose,
   2159                    ("Process Switch: Upgraded Object to Document Load"));
   2160            self->TriggerProcessSwitch(aBrowsingContext, options);
   2161          },
   2162      [self = RefPtr{this}](nsresult aStatusCode) {
   2163        MOZ_ASSERT(NS_FAILED(aStatusCode), "Status should be error");
   2164        self->RedirectToRealChannelFinished(aStatusCode);
   2165      });
   2166  return true;
   2167 }
   2168 
   2169 void DocumentLoadListener::TriggerProcessSwitch(
   2170    CanonicalBrowsingContext* aContext,
   2171    const NavigationIsolationOptions& aOptions, bool aIsNewTab) {
   2172  MOZ_DIAGNOSTIC_ASSERT(aIsNewTab || aContext->IsOwnedByProcess(
   2173                                         GetContentProcessId(mContentParent)),
   2174                        "not owned by creator process anymore?");
   2175  if (MOZ_LOG_TEST(gProcessIsolationLog, LogLevel::Info)) {
   2176    nsCString currentRemoteType = "INVALID"_ns;
   2177    aContext->GetCurrentRemoteType(currentRemoteType, IgnoreErrors());
   2178 
   2179    MOZ_LOG(gProcessIsolationLog, LogLevel::Info,
   2180            ("Process Switch: Changing Remoteness from '%s' to '%s'",
   2181             currentRemoteType.get(), aOptions.mRemoteType.get()));
   2182  }
   2183 
   2184  // Stash our stream filter requests to pass to TriggerRedirectToRealChannel,
   2185  // as the call to `DisconnectListeners` will clear our list.
   2186  nsTArray<StreamFilterRequest> streamFilterRequests =
   2187      std::move(mStreamFilterRequests);
   2188 
   2189  // We're now committing to a process switch, so we can disconnect from
   2190  // the listeners in the old process.
   2191  // As the navigation is continuing, we don't actually want to cancel the
   2192  // request in the old process unless we're redirecting the load into a new
   2193  // tab.
   2194  DisconnectListeners(NS_BINDING_ABORTED, NS_BINDING_ABORTED, !aIsNewTab);
   2195 
   2196  MOZ_LOG(gProcessIsolationLog, LogLevel::Verbose,
   2197          ("Process Switch: Calling ChangeRemoteness"));
   2198  aContext->ChangeRemoteness(aOptions, mLoadIdentifier)
   2199      ->Then(
   2200          GetMainThreadSerialEventTarget(), __func__,
   2201          [self = RefPtr{this}, requests = std::move(streamFilterRequests)](
   2202              const std::pair<RefPtr<BrowserParent>,
   2203                              RefPtr<CanonicalBrowsingContext>>&
   2204                  aResult) mutable {
   2205            MOZ_ASSERT(self->mChannel,
   2206                       "Something went wrong, channel got cancelled");
   2207            const auto& [browserParent, browsingContext] = aResult;
   2208            self->TriggerRedirectToRealChannel(
   2209                browsingContext,
   2210                Some(browserParent ? browserParent->Manager() : nullptr),
   2211                std::move(requests));
   2212          },
   2213          [self = RefPtr{this}](nsresult aStatusCode) {
   2214            MOZ_ASSERT(NS_FAILED(aStatusCode), "Status should be error");
   2215            self->RedirectToRealChannelFinished(aStatusCode);
   2216          });
   2217 }
   2218 
   2219 RefPtr<PDocumentChannelParent::RedirectToRealChannelPromise>
   2220 DocumentLoadListener::RedirectToParentProcess(uint32_t aRedirectFlags,
   2221                                              uint32_t aLoadFlags) {
   2222  // This is largely the same as ContentChild::RecvCrossProcessRedirect,
   2223  // except without needing to deserialize or create an nsIChildChannel.
   2224 
   2225  RefPtr<nsDocShellLoadState> loadState;
   2226  nsDocShellLoadState::CreateFromPendingChannel(
   2227      mChannel, mLoadIdentifier, mRedirectChannelId, getter_AddRefs(loadState));
   2228 
   2229  loadState->SetLoadFlags(mLoadStateExternalLoadFlags);
   2230  loadState->SetInternalLoadFlags(mLoadStateInternalLoadFlags);
   2231  loadState->SetLoadType(mLoadStateLoadType);
   2232  if (mLoadingSessionHistoryInfo) {
   2233    loadState->SetLoadingSessionHistoryInfo(*mLoadingSessionHistoryInfo);
   2234  }
   2235 
   2236  // This is poorly named now.
   2237  RefPtr<ChildProcessChannelListener> processListener =
   2238      ChildProcessChannelListener::GetSingleton();
   2239 
   2240  auto promise =
   2241      MakeRefPtr<PDocumentChannelParent::RedirectToRealChannelPromise::Private>(
   2242          __func__);
   2243  promise->UseDirectTaskDispatch(__func__);
   2244  auto resolve = [promise](nsresult aResult) {
   2245    promise->Resolve(aResult, __func__);
   2246  };
   2247 
   2248  nsTArray<ipc::Endpoint<extensions::PStreamFilterParent>> endpoints;
   2249  processListener->OnChannelReady(loadState, mLoadIdentifier,
   2250                                  std::move(endpoints), mTiming,
   2251                                  std::move(resolve));
   2252 
   2253  return promise;
   2254 }
   2255 
   2256 RefPtr<PDocumentChannelParent::RedirectToRealChannelPromise>
   2257 DocumentLoadListener::RedirectToRealChannel(
   2258    uint32_t aRedirectFlags, uint32_t aLoadFlags,
   2259    const Maybe<ContentParent*>& aDestinationProcess,
   2260    nsTArray<ParentEndpoint>&& aStreamFilterEndpoints) {
   2261  LOG(
   2262      ("DocumentLoadListener RedirectToRealChannel [this=%p] "
   2263       "aRedirectFlags=%" PRIx32 ", aLoadFlags=%" PRIx32,
   2264       this, aRedirectFlags, aLoadFlags));
   2265 
   2266  if (mIsDocumentLoad) {
   2267    // TODO(djg): Add the last URI visit to history if success. Is there a
   2268    // better place to handle this? Need access to the updated aLoadFlags.
   2269    nsresult status = NS_OK;
   2270    mChannel->GetStatus(&status);
   2271    bool updateGHistory =
   2272        nsDocShell::ShouldUpdateGlobalHistory(mLoadStateLoadType);
   2273    if (NS_SUCCEEDED(status) && updateGHistory &&
   2274        !net::ChannelIsPost(mChannel)) {
   2275      AddURIVisit(mChannel, aLoadFlags);
   2276    }
   2277  }
   2278 
   2279  // Register the new channel and obtain id for it
   2280  nsCOMPtr<nsIRedirectChannelRegistrar> registrar =
   2281      RedirectChannelRegistrar::GetOrCreate();
   2282  MOZ_ASSERT(registrar);
   2283  nsCOMPtr<nsIChannel> chan = mChannel;
   2284  if (nsCOMPtr<nsIViewSourceChannel> vsc = do_QueryInterface(chan)) {
   2285    chan = vsc->GetInnerChannel();
   2286  }
   2287  mRedirectChannelId = nsContentUtils::GenerateLoadIdentifier();
   2288  MOZ_ALWAYS_SUCCEEDS(registrar->RegisterChannel(chan, mRedirectChannelId));
   2289 
   2290  if (aDestinationProcess) {
   2291    RefPtr<ContentParent> cp = *aDestinationProcess;
   2292    if (!cp) {
   2293      MOZ_ASSERT(aStreamFilterEndpoints.IsEmpty());
   2294      return RedirectToParentProcess(aRedirectFlags, aLoadFlags);
   2295    }
   2296 
   2297    if (!cp->CanSend()) {
   2298      return PDocumentChannelParent::RedirectToRealChannelPromise::
   2299          CreateAndReject(ipc::ResponseRejectReason::SendError, __func__);
   2300    }
   2301 
   2302    nsTArray<EarlyHintConnectArgs> ehArgs;
   2303    mEarlyHintsService.RegisterLinksAndGetConnectArgs(cp->ChildID(), ehArgs);
   2304 
   2305    RedirectToRealChannelArgs args;
   2306    SerializeRedirectData(args, /* aIsCrossProcess */ true, aRedirectFlags,
   2307                          aLoadFlags, std::move(ehArgs),
   2308                          mEarlyHintsService.LinkType());
   2309    if (mTiming) {
   2310      mTiming->Anonymize(args.uri());
   2311      args.timing() = std::move(mTiming);
   2312    }
   2313 
   2314    cp->TransmitBlobDataIfBlobURL(args.uri());
   2315 
   2316    if (CanonicalBrowsingContext* bc = GetDocumentBrowsingContext()) {
   2317      if (bc->IsTop() && bc->IsActive()) {
   2318        nsContentUtils::RequestGeckoTaskBurst();
   2319      }
   2320    }
   2321 
   2322    return cp->SendCrossProcessRedirect(args,
   2323                                        std::move(aStreamFilterEndpoints));
   2324  }
   2325 
   2326  if (mOpenPromiseResolved) {
   2327    LOG(
   2328        ("DocumentLoadListener RedirectToRealChannel [this=%p] "
   2329         "promise already resolved. Aborting.",
   2330         this));
   2331    // The promise has already been resolved or aborted, so we have no way to
   2332    // return a promise again to the listener which would cancel the operation.
   2333    // Reject the promise immediately.
   2334    return PDocumentChannelParent::RedirectToRealChannelPromise::
   2335        CreateAndResolve(NS_BINDING_ABORTED, __func__);
   2336  }
   2337 
   2338  // This promise will be passed on the promise listener which will
   2339  // resolve this promise for us.
   2340  auto promise =
   2341      MakeRefPtr<PDocumentChannelParent::RedirectToRealChannelPromise::Private>(
   2342          __func__);
   2343 
   2344  mOpenPromise->Resolve(
   2345      OpenPromiseSucceededType({std::move(aStreamFilterEndpoints),
   2346                                aRedirectFlags, aLoadFlags,
   2347                                mEarlyHintsService.LinkType(), promise}),
   2348      __func__);
   2349 
   2350  // There is no way we could come back here if the promise had been resolved
   2351  // previously. But for clarity and to avoid all doubt, we set this boolean to
   2352  // true.
   2353  mOpenPromiseResolved = true;
   2354 
   2355  return promise;
   2356 }
   2357 
   2358 void DocumentLoadListener::TriggerRedirectToRealChannel(
   2359    CanonicalBrowsingContext* aDestinationBrowsingContext,
   2360    const Maybe<ContentParent*>& aDestinationProcess,
   2361    nsTArray<StreamFilterRequest> aStreamFilterRequests) {
   2362  LOG(
   2363      ("DocumentLoadListener::TriggerRedirectToRealChannel [this=%p] "
   2364       "aDestinationBrowsingContext=%" PRIx64 " aDestinationProcess=%" PRId64,
   2365       this, aDestinationBrowsingContext->Id(),
   2366       aDestinationProcess.valueOr(nullptr)
   2367           ? int64_t((*aDestinationProcess)->ChildID())
   2368           : int64_t(-1)));
   2369  MOZ_ASSERT(aDestinationBrowsingContext);
   2370 
   2371  // This initiates replacing the current DocumentChannel with a
   2372  // protocol specific 'real' channel, maybe in a different process than
   2373  // the current DocumentChannelChild, if aDestinationProces is set.
   2374  // It registers the current mChannel with the registrar to get an ID
   2375  // so that the remote end can setup a new IPDL channel and lookup
   2376  // the same underlying channel.
   2377  // We expect this process to finish with FinishReplacementChannelSetup
   2378  // (for both in-process and process switch cases), where we cleanup
   2379  // the registrar and copy across any needed state to the replacing
   2380  // IPDL parent object.
   2381 
   2382  // If we've already called `DisconnectListeners`, and have no destination
   2383  // process we're switching to, abort early to skip the following checks.
   2384  if (mOpenPromiseResolved && !aDestinationProcess) {
   2385    LOG(
   2386        ("DocumentLoadListener::TriggerRedirectToRealChannel [this=%p] "
   2387         "Listeners already disconnected for non-switching redirect. Aborting.",
   2388         this));
   2389    RedirectToRealChannelFinished(NS_BINDING_ABORTED);
   2390    return;
   2391  }
   2392 
   2393  RefPtr<ContentParent> contentParent =
   2394      aDestinationProcess.valueOr(mContentParent);
   2395 
   2396  nsTArray<ParentEndpoint> parentEndpoints(aStreamFilterRequests.Length());
   2397  if (!aStreamFilterRequests.IsEmpty()) {
   2398    base::ProcessId pid =
   2399        contentParent ? contentParent->OtherPid() : base::ProcessId{0};
   2400 
   2401    for (StreamFilterRequest& request : aStreamFilterRequests) {
   2402      if (!pid) {
   2403        request.mPromise->Reject(false, __func__);
   2404        request.mPromise = nullptr;
   2405        continue;
   2406      }
   2407      ParentEndpoint parent;
   2408      nsresult rv = extensions::PStreamFilter::CreateEndpoints(
   2409          &parent, &request.mChildEndpoint);
   2410 
   2411      if (NS_FAILED(rv)) {
   2412        request.mPromise->Reject(false, __func__);
   2413        request.mPromise = nullptr;
   2414      } else {
   2415        parentEndpoints.AppendElement(std::move(parent));
   2416      }
   2417    }
   2418  }
   2419 
   2420  // Check if the load is for a "silent" error (i.e. no document or error page
   2421  // will load). This is the case for all failed object/embed loads, and some
   2422  // failed document loads.
   2423  // We never process switch for these silent loads (as that will destroy the
   2424  // existing document, which is being navigated away from)
   2425  nsresult status = NS_OK;
   2426  mChannel->GetStatus(&status);
   2427  bool silentErrorLoad = !DocShellWillDisplayContent(status);
   2428 
   2429  // Get the unsandboxed result principal for our channel. This is required both
   2430  // to validate that the response which will be loaded is being sent to the
   2431  // appropriate process, as well as to apply origin keying.
   2432  nsCOMPtr<nsIPrincipal> unsandboxedPrincipal;
   2433  nsresult rv = nsScriptSecurityManager::GetScriptSecurityManager()
   2434                    ->GetChannelResultPrincipalIfNotSandboxed(
   2435                        mChannel, getter_AddRefs(unsandboxedPrincipal));
   2436  if (NS_FAILED(rv)) {
   2437    LOG(
   2438        ("DocumentLoadListener::TriggerRedirectToRealChannel [this=%p] "
   2439         "GetChannelResultPrincipalIfNotSandboxed failed",
   2440         this));
   2441    RedirectToRealChannelFinished(NS_ERROR_FAILURE);
   2442    return;
   2443  }
   2444 
   2445  // Validate that the target process, if specified, would be allowed to load
   2446  // this principal, and fail the navigation if it would not.
   2447  // Don't enforce this requirement for silent error loads, as those never
   2448  // process switch, and should not result in a document being loaded in the
   2449  // content process.
   2450  // System principals are allowed for now, as they are used in some edge-cases.
   2451  if (!silentErrorLoad && contentParent &&
   2452      !contentParent->ValidatePrincipal(
   2453          unsandboxedPrincipal, {ValidatePrincipalOptions::AllowSystem})) {
   2454    ContentParent::LogAndAssertFailedPrincipalValidationInfo(
   2455        unsandboxedPrincipal, "TriggerRedirectToRealChannel");
   2456    RedirectToRealChannelFinished(NS_ERROR_FAILURE);
   2457    return;
   2458  }
   2459 
   2460  // Ensure that the BrowsingContextGroup which will finish this load has the
   2461  // UseOriginAgentCluster flag set to a value. We'll try to base it on
   2462  // `mChannel` if it has the appropriate header.
   2463  //
   2464  // This needs to be set such that we can perform correct DocGroup keying in
   2465  // the content process.
   2466  //
   2467  // In effect, this is performing the side-effect component of "obtain a
   2468  // similar-origin window agent", leading to the historical agent cluster key
   2469  // map being populated in the BrowsingContextGroup.
   2470  //
   2471  // https://html.spec.whatwg.org/#obtain-similar-origin-window-agent
   2472  if (aDestinationBrowsingContext->Group()
   2473          ->UsesOriginAgentCluster(unsandboxedPrincipal)
   2474          .isNothing()) {
   2475    // UseOriginAgentCluster requires a secure context, so never origin key
   2476    // unless we're a potentially-trustworthy origin.
   2477    //
   2478    // We don't handle this within BrowsingContextGroup, as the set of URIs
   2479    // which are considered "potentially trustworthy" can change at runtime, so
   2480    // we want to cache the decision at the time we make it.
   2481    bool isSecureContext =
   2482        unsandboxedPrincipal->GetIsOriginPotentiallyTrustworthy();
   2483    bool hasOriginAgentCluster =
   2484        StaticPrefs::dom_origin_agent_cluster_default() && isSecureContext;
   2485    if (nsCOMPtr<nsIHttpChannelInternal> httpChannel =
   2486            do_QueryInterface(mChannel);
   2487        httpChannel && isSecureContext &&
   2488        StaticPrefs::dom_origin_agent_cluster_enabled()) {
   2489      bool headerValue = false;
   2490      if (NS_SUCCEEDED(
   2491              httpChannel->GetOriginAgentClusterHeader(&headerValue))) {
   2492        hasOriginAgentCluster = headerValue;
   2493      }
   2494    }
   2495    aDestinationBrowsingContext->Group()->SetUseOriginAgentClusterFromNetwork(
   2496        unsandboxedPrincipal, hasOriginAgentCluster);
   2497  }
   2498 
   2499  // If we didn't have any redirects, then we pass the REDIRECT_INTERNAL flag
   2500  // for this channel switch so that it isn't recorded in session history etc.
   2501  // If there were redirect(s), then we want this switch to be recorded as a
   2502  // real one, since we have a new URI.
   2503  uint32_t redirectFlags = 0;
   2504  if (!mHaveVisibleRedirect) {
   2505    redirectFlags = nsIChannelEventSink::REDIRECT_INTERNAL;
   2506  }
   2507 
   2508  uint32_t newLoadFlags = nsIRequest::LOAD_NORMAL;
   2509  MOZ_ALWAYS_SUCCEEDS(mChannel->GetLoadFlags(&newLoadFlags));
   2510  // We're pulling our flags from the inner channel, which may not have this
   2511  // flag set on it. This is the case when loading a 'view-source' channel.
   2512  if (mIsDocumentLoad || aDestinationProcess) {
   2513    newLoadFlags |= nsIChannel::LOAD_DOCUMENT_URI;
   2514  }
   2515  if (!aDestinationProcess) {
   2516    newLoadFlags |= nsIChannel::LOAD_REPLACE;
   2517  }
   2518 
   2519  // INHIBIT_PERSISTENT_CACHING is clearing during http redirects (from
   2520  // both parent and content process channel instances), but only ever
   2521  // re-added to the parent-side nsHttpChannel.
   2522  // To match that behaviour, we want to explicitly avoid copying this flag
   2523  // back to our newly created content side channel, otherwise it can
   2524  // affect sub-resources loads in the same load group.
   2525  nsCOMPtr<nsIURI> uri;
   2526  mChannel->GetURI(getter_AddRefs(uri));
   2527  if (uri && uri->SchemeIs("https")) {
   2528    newLoadFlags &= ~nsIRequest::INHIBIT_PERSISTENT_CACHING;
   2529  }
   2530 
   2531  RefPtr<DocumentLoadListener> self = this;
   2532  RedirectToRealChannel(redirectFlags, newLoadFlags, aDestinationProcess,
   2533                        std::move(parentEndpoints))
   2534      ->Then(
   2535          GetCurrentSerialEventTarget(), __func__,
   2536          [self, requests = std::move(aStreamFilterRequests)](
   2537              const nsresult& aResponse) mutable {
   2538            for (StreamFilterRequest& request : requests) {
   2539              if (request.mPromise) {
   2540                request.mPromise->Resolve(std::move(request.mChildEndpoint),
   2541                                          __func__);
   2542                request.mPromise = nullptr;
   2543              }
   2544            }
   2545            self->RedirectToRealChannelFinished(aResponse);
   2546          },
   2547          [self](const mozilla::ipc::ResponseRejectReason) {
   2548            self->RedirectToRealChannelFinished(NS_ERROR_FAILURE);
   2549          });
   2550 }
   2551 
   2552 void DocumentLoadListener::MaybeReportBlockedByURLClassifier(nsresult aStatus) {
   2553  auto* browsingContext = GetDocumentBrowsingContext();
   2554  if (!browsingContext || browsingContext->IsTop() ||
   2555      !StaticPrefs::privacy_trackingprotection_testing_report_blocked_node()) {
   2556    return;
   2557  }
   2558 
   2559  if (!UrlClassifierFeatureFactory::IsClassifierBlockingErrorCode(aStatus)) {
   2560    return;
   2561  }
   2562 
   2563  RefPtr<WindowGlobalParent> parent = browsingContext->GetParentWindowContext();
   2564  if (parent) {
   2565    (void)parent->SendAddBlockedFrameNodeByClassifier(browsingContext);
   2566  }
   2567 }
   2568 
   2569 bool DocumentLoadListener::DocShellWillDisplayContent(nsresult aStatus) {
   2570  if (NS_SUCCEEDED(aStatus)) {
   2571    return true;
   2572  }
   2573 
   2574  // Always return errored loads to the <object> or <embed> element's process,
   2575  // as load errors will not be rendered as documents.
   2576  if (!mIsDocumentLoad) {
   2577    return false;
   2578  }
   2579 
   2580  // nsDocShell attempts urifixup on some failure types,
   2581  // but also of those also display an error page if we don't
   2582  // succeed with fixup, so we don't need to check for it
   2583  // here.
   2584 
   2585  auto* loadingContext = GetLoadingBrowsingContext();
   2586 
   2587  nsresult rv = nsDocShell::FilterStatusForErrorPage(
   2588      aStatus, mChannel, mLoadStateLoadType, loadingContext->IsTop(),
   2589      loadingContext->GetUseErrorPages(), nullptr);
   2590 
   2591  // If filtering returned a failure code, then an error page will
   2592  // be display for that code, so return true;
   2593  return NS_FAILED(rv);
   2594 }
   2595 
   2596 bool DocumentLoadListener::MaybeHandleLoadErrorWithURIFixup(nsresult aStatus) {
   2597  RefPtr<CanonicalBrowsingContext> bc = GetDocumentBrowsingContext();
   2598  if (!bc) {
   2599    return false;
   2600  }
   2601 
   2602  nsCOMPtr<nsIInputStream> newPostData;
   2603  nsILoadInfo::SchemelessInputType schemelessInput =
   2604      nsILoadInfo::SchemelessInputTypeUnset;
   2605  nsCOMPtr<nsIURI> newURI = nsDocShell::AttemptURIFixup(
   2606      mChannel, aStatus, mOriginalUriString, mLoadStateLoadType, bc->IsTop(),
   2607      mLoadStateInternalLoadFlags &
   2608          nsDocShell::INTERNAL_LOAD_FLAGS_ALLOW_THIRD_PARTY_FIXUP,
   2609      bc->UsePrivateBrowsing(), true, getter_AddRefs(newPostData),
   2610      &schemelessInput);
   2611 
   2612  // Since aStatus will be NS_OK for 4xx and 5xx error codes we
   2613  // have to check each request which was upgraded by https-first.
   2614  // If an error (including 4xx and 5xx) occured, then let's check if
   2615  // we can downgrade the scheme to HTTP again.
   2616  bool isHTTPSFirstFixup = false;
   2617  if (!newURI) {
   2618    newURI =
   2619        nsHTTPSOnlyUtils::PotentiallyDowngradeHttpsFirstRequest(this, aStatus);
   2620    isHTTPSFirstFixup = true;
   2621  }
   2622 
   2623  if (!newURI) {
   2624    return false;
   2625  }
   2626 
   2627  // If we got a new URI, then we should initiate a load with that.
   2628  // Notify the listeners that this load is complete (with a code that
   2629  // won't trigger an error page), and then start the new one.
   2630  DisconnectListeners(NS_BINDING_ABORTED, NS_BINDING_ABORTED);
   2631 
   2632  RefPtr<nsDocShellLoadState> loadState = new nsDocShellLoadState(newURI);
   2633  nsCOMPtr<nsILoadInfo> loadInfo = mChannel->LoadInfo();
   2634 
   2635  nsCOMPtr<nsIPolicyContainer> policyContainerToInherit =
   2636      loadInfo->GetPolicyContainerToInherit();
   2637  loadState->SetPolicyContainer(policyContainerToInherit);
   2638 
   2639  nsCOMPtr<nsIPrincipal> triggeringPrincipal = loadInfo->TriggeringPrincipal();
   2640  loadState->SetTriggeringPrincipal(triggeringPrincipal);
   2641 
   2642  loadState->SetPostDataStream(newPostData);
   2643 
   2644  // Record whether the protocol was added through a fixup.
   2645  loadState->SetSchemelessInput(schemelessInput);
   2646 
   2647  if (isHTTPSFirstFixup) {
   2648    nsHTTPSOnlyUtils::UpdateLoadStateAfterHTTPSFirstDowngrade(this, loadState);
   2649  }
   2650 
   2651  // Ensure to set referrer information in the fallback channel equally to the
   2652  // not-upgraded original referrer info.
   2653  //
   2654  // A simply copy of the referrer info from the upgraded one leads to problems.
   2655  // For example:
   2656  // 1. https://some-site.com redirects to http://other-site.com with referrer
   2657  // policy
   2658  //   "no-referrer-when-downgrade".
   2659  // 2. https-first upgrades the redirection, so redirects to
   2660  // https://other-site.com,
   2661  //    according to referrer policy the referrer will be send (https-> https)
   2662  // 3. Assume other-site.com is not supporting https, https-first performs
   2663  // fall-
   2664  //    back.
   2665  // If the referrer info from the upgraded channel gets copied into the
   2666  // http fallback channel, the referrer info would contain the referrer
   2667  // (https://some-site.com). That would violate the policy
   2668  // "no-referrer-when-downgrade". A recreation of the original referrer info
   2669  // would ensure us that the referrer is set according to the referrer policy.
   2670  nsCOMPtr<nsIHttpChannel> httpChannel = do_QueryInterface(mChannel);
   2671  if (httpChannel) {
   2672    nsCOMPtr<nsIReferrerInfo> referrerInfo = httpChannel->GetReferrerInfo();
   2673    if (referrerInfo) {
   2674      ReferrerPolicy referrerPolicy = referrerInfo->ReferrerPolicy();
   2675      nsCOMPtr<nsIURI> originalReferrer = referrerInfo->GetOriginalReferrer();
   2676      if (originalReferrer) {
   2677        // Create new ReferrerInfo with the original referrer and the referrer
   2678        // policy.
   2679        nsCOMPtr<nsIReferrerInfo> newReferrerInfo =
   2680            new ReferrerInfo(originalReferrer, referrerPolicy);
   2681        loadState->SetReferrerInfo(newReferrerInfo);
   2682      }
   2683    }
   2684  }
   2685 
   2686  bc->LoadURI(loadState, false);
   2687  return true;
   2688 }
   2689 
   2690 NS_IMETHODIMP
   2691 DocumentLoadListener::OnStartRequest(nsIRequest* aRequest) {
   2692  return DoOnStartRequest(aRequest);
   2693 }
   2694 
   2695 nsresult DocumentLoadListener::DoOnStartRequest(nsIRequest* aRequest) {
   2696  LOG(("DocumentLoadListener OnStartRequest [this=%p]", this));
   2697 
   2698  nsCOMPtr<nsIMultiPartChannel> multiPartChannel = do_QueryInterface(aRequest);
   2699  if (multiPartChannel) {
   2700    multiPartChannel->GetBaseChannel(getter_AddRefs(mChannel));
   2701  } else {
   2702    mChannel = do_QueryInterface(aRequest);
   2703  }
   2704  MOZ_DIAGNOSTIC_ASSERT(mChannel);
   2705 
   2706  if (mHaveVisibleRedirect && GetDocumentBrowsingContext() &&
   2707      mLoadingSessionHistoryInfo) {
   2708    mLoadingSessionHistoryInfo =
   2709        GetDocumentBrowsingContext()->ReplaceLoadingSessionHistoryEntryForLoad(
   2710            mLoadingSessionHistoryInfo.get(), mChannel);
   2711  }
   2712 
   2713  RefPtr<nsHttpChannel> httpChannel = do_QueryObject(mChannel);
   2714 
   2715  // Enforce CSP frame-ancestors and x-frame-options checks which
   2716  // might cancel the channel.
   2717  nsContentSecurityUtils::PerformCSPFrameAncestorAndXFOCheck(mChannel);
   2718 
   2719  // HTTPS-Only Mode tries to upgrade connections to https. Once loading
   2720  // is in progress we set that flag so that timeout counter measures
   2721  // do not kick in.
   2722  if (httpChannel) {
   2723    nsCOMPtr<nsILoadInfo> loadInfo = httpChannel->LoadInfo();
   2724    if (nsHTTPSOnlyUtils::GetUpgradeMode(loadInfo) ==
   2725        nsHTTPSOnlyUtils::HTTPS_ONLY_MODE) {
   2726      uint32_t httpsOnlyStatus = loadInfo->GetHttpsOnlyStatus();
   2727      httpsOnlyStatus |= nsILoadInfo::HTTPS_ONLY_TOP_LEVEL_LOAD_IN_PROGRESS;
   2728      loadInfo->SetHttpsOnlyStatus(httpsOnlyStatus);
   2729    }
   2730 
   2731    if (mLoadingSessionHistoryInfo &&
   2732        nsDocShell::ShouldDiscardLayoutState(httpChannel)) {
   2733      mLoadingSessionHistoryInfo->mInfo.SetSaveLayoutStateFlag(false);
   2734    }
   2735  }
   2736 
   2737  auto* loadingContext = GetLoadingBrowsingContext();
   2738  if (!loadingContext || loadingContext->IsDiscarded()) {
   2739    Cancel(NS_ERROR_UNEXPECTED, "No valid LoadingBrowsingContext."_ns);
   2740    return NS_ERROR_UNEXPECTED;
   2741  }
   2742 
   2743  if (AppShutdown::IsInOrBeyond(ShutdownPhase::AppShutdownConfirmed)) {
   2744    Cancel(NS_ERROR_ILLEGAL_DURING_SHUTDOWN,
   2745           "Aborting OnStartRequest after shutdown started."_ns);
   2746    return NS_OK;
   2747  }
   2748 
   2749  // Block top-level data URI navigations if triggered by the web. Logging is
   2750  // performed in AllowTopLevelNavigationToDataURI.
   2751  if (!nsContentSecurityManager::AllowTopLevelNavigationToDataURI(mChannel)) {
   2752    mChannel->Cancel(NS_ERROR_DOM_BAD_URI);
   2753    if (loadingContext) {
   2754      RefPtr<MaybeCloseWindowHelper> maybeCloseWindowHelper =
   2755          new MaybeCloseWindowHelper(loadingContext);
   2756      // If a new window was opened specifically for this request, close it
   2757      // after blocking the navigation.
   2758      maybeCloseWindowHelper->SetShouldCloseWindow(
   2759          IsFirstLoadInWindow(mChannel));
   2760      (void)maybeCloseWindowHelper->MaybeCloseWindow();
   2761    }
   2762    DisconnectListeners(NS_ERROR_DOM_BAD_URI, NS_ERROR_DOM_BAD_URI);
   2763    return NS_OK;
   2764  }
   2765 
   2766  // Generally we want to switch to a real channel even if the request failed,
   2767  // since the listener might want to access protocol-specific data (like http
   2768  // response headers) in its error handling.
   2769  // An exception to this is when nsExtProtocolChannel handled the request and
   2770  // returned NS_ERROR_NO_CONTENT, since creating a real one in the content
   2771  // process will attempt to handle the URI a second time.
   2772  nsresult status = NS_OK;
   2773  aRequest->GetStatus(&status);
   2774  if (status == NS_ERROR_NO_CONTENT) {
   2775    DisconnectListeners(status, status);
   2776    return NS_OK;
   2777  }
   2778 
   2779  // PerformCSPFrameAncestorAndXFOCheck may cancel a moz-extension request that
   2780  // needs to be handled here. Without this, the resource would be loaded and
   2781  // not blocked when the real channel is created in the content process.
   2782  if (status == NS_ERROR_CSP_FRAME_ANCESTOR_VIOLATION && !httpChannel) {
   2783    DisconnectListeners(status, status);
   2784    return NS_OK;
   2785  }
   2786 
   2787  // If this was a failed load and we want to try fixing the uri, then
   2788  // this will initiate a new load (and disconnect this one), and we don't
   2789  // need to do anything else.
   2790  if (MaybeHandleLoadErrorWithURIFixup(status)) {
   2791    return NS_OK;
   2792  }
   2793 
   2794  // If this is a successful load with a successful status code, we can possibly
   2795  // submit HTTPS-First telemetry.
   2796  if (NS_SUCCEEDED(status) && httpChannel) {
   2797    uint32_t responseStatus = 0;
   2798    if (NS_SUCCEEDED(httpChannel->GetResponseStatus(&responseStatus)) &&
   2799        responseStatus < 400) {
   2800      nsHTTPSOnlyUtils::SubmitHTTPSFirstTelemetry(
   2801          mChannel->LoadInfo(), mHTTPSFirstDowngradeData.forget());
   2802    }
   2803  }
   2804 
   2805  mStreamListenerFunctions.AppendElement(StreamListenerFunction{
   2806      VariantIndex<0>{}, OnStartRequestParams{aRequest}});
   2807 
   2808  if (mOpenPromiseResolved || mInitiatedRedirectToRealChannel) {
   2809    // I we have already resolved the promise, there's no point to continue
   2810    // attempting a process switch or redirecting to the real channel.
   2811    // We can also have multiple calls to OnStartRequest when dealing with
   2812    // multi-part content, but only want to redirect once.
   2813    return NS_OK;
   2814  }
   2815 
   2816  // Keep track of server responses resulting in a document for the Bounce
   2817  // Tracking Protection.
   2818  if (mIsDocumentLoad && GetParentWindowContext() == nullptr &&
   2819      loadingContext->IsTopContent()) {
   2820    RefPtr<BounceTrackingState> bounceTrackingState =
   2821        loadingContext->GetBounceTrackingState();
   2822 
   2823    // Not every browsing context has a BounceTrackingState. It's also null when
   2824    // the feature is disabled.
   2825    if (bounceTrackingState) {
   2826      // Don't warn when OnDocumentStartRequest fails until bug 1894936 is
   2827      // fixed, because it fails frequently because of that.
   2828      (void)bounceTrackingState->OnDocumentStartRequest(mChannel);
   2829 
   2830      DynamicFpiNavigationHeuristic::MaybeGrantStorageAccess(loadingContext,
   2831                                                             mChannel);
   2832    }
   2833  }
   2834 
   2835  mChannel->Suspend();
   2836 
   2837  mInitiatedRedirectToRealChannel = true;
   2838 
   2839  MaybeReportBlockedByURLClassifier(status);
   2840 
   2841  // If the channel has failed, and the docshell isn't going to display an
   2842  // error page for that failure, then don't allow process switching, since
   2843  // we just want to keep our existing document.
   2844  bool silentErrorLoad = !DocShellWillDisplayContent(status);
   2845  if (silentErrorLoad) {
   2846    MOZ_LOG(gProcessIsolationLog, LogLevel::Verbose,
   2847            ("Skipping process switch, as DocShell will not display content "
   2848             "(status: %s) %s",
   2849             GetStaticErrorName(status),
   2850             GetChannelCreationURI()->GetSpecOrDefault().get()));
   2851 
   2852    // If this load would not lead to the content docShell displaying any
   2853    // content, cancel it here to ensure that we don't spuriously succeed when
   2854    // finishing the load in the content process. We don't do this for HTTP
   2855    // channels, which may have extra information (e.g. navigation timing) which
   2856    // would be relevant to the content process.
   2857    if (!httpChannel) {
   2858      DisconnectListeners(status, status);
   2859      return NS_OK;
   2860    }
   2861  }
   2862 
   2863  // Determine if a new process needs to be spawned. If it does, this will
   2864  // trigger a cross process switch, and we should hold off on redirecting to
   2865  // the real channel.
   2866  bool willBeRemote = false;
   2867  if (silentErrorLoad || !MaybeTriggerProcessSwitch(&willBeRemote)) {
   2868    // We're not going to be doing a process switch, so redirect to the real
   2869    // channel within our current process.
   2870    nsTArray<StreamFilterRequest> streamFilterRequests =
   2871        std::move(mStreamFilterRequests);
   2872    if (!mSupportsRedirectToRealChannel) {
   2873      RefPtr<BrowserParent> browserParent = loadingContext->GetBrowserParent();
   2874      if (browserParent->Manager() != mContentParent) {
   2875        LOG(
   2876            ("DocumentLoadListener::RedirectToRealChannel failed because "
   2877             "browsingContext no longer owned by creator"));
   2878        Cancel(NS_BINDING_ABORTED,
   2879               "DocumentLoadListener::RedirectToRealChannel failed because "
   2880               "browsingContext no longer owned by creator"_ns);
   2881        return NS_OK;
   2882      }
   2883      MOZ_DIAGNOSTIC_ASSERT(
   2884          browserParent->GetBrowsingContext() == loadingContext,
   2885          "make sure the load is going to the right place");
   2886 
   2887      // If the existing process is right for this load, but the bridge doesn't
   2888      // support redirects, then we need to do it manually, by faking a process
   2889      // switch.
   2890      DisconnectListeners(NS_BINDING_ABORTED, NS_BINDING_ABORTED,
   2891                          /* aContinueNavigating */ true);
   2892 
   2893      // Notify the docshell that it should load using the newly connected
   2894      // channel
   2895      browserParent->ResumeLoad(mLoadIdentifier);
   2896 
   2897      // Use the current process ID to run the 'process switch' path and connect
   2898      // the channel into the current process.
   2899      TriggerRedirectToRealChannel(loadingContext, Some(mContentParent),
   2900                                   std::move(streamFilterRequests));
   2901    } else {
   2902      TriggerRedirectToRealChannel(loadingContext, Nothing(),
   2903                                   std::move(streamFilterRequests));
   2904    }
   2905 
   2906    // If we're not switching, then check if we're currently remote.
   2907    if (mContentParent) {
   2908      willBeRemote = true;
   2909    }
   2910  }
   2911 
   2912  if (httpChannel) {
   2913    mEarlyHintsService.Reset();
   2914  } else {
   2915    mEarlyHintsService.Cancel(
   2916        "DocumentLoadListener::OnStartRequest: no httpChannel"_ns);
   2917  }
   2918 
   2919  // If we're going to be delivering this channel to a remote content
   2920  // process, then we want to install any required content conversions
   2921  // in the content process.
   2922  // The caller of this OnStartRequest will install a conversion
   2923  // helper after we return if we haven't disabled conversion. Normally
   2924  // HttpChannelParent::OnStartRequest would disable conversion, but we're
   2925  // defering calling that until later. Manually disable it now to prevent the
   2926  // converter from being installed (since we want the child to do it), and
   2927  // also save the value so that when we do call
   2928  // HttpChannelParent::OnStartRequest, we can have the value as it originally
   2929  // was.
   2930  if (httpChannel) {
   2931    (void)httpChannel->GetApplyConversion(&mOldApplyConversion);
   2932    if (willBeRemote) {
   2933      httpChannel->SetApplyConversion(false);
   2934    }
   2935  }
   2936 
   2937  return NS_OK;
   2938 }
   2939 
   2940 NS_IMETHODIMP
   2941 DocumentLoadListener::OnStopRequest(nsIRequest* aRequest,
   2942                                    nsresult aStatusCode) {
   2943  LOG(("DocumentLoadListener OnStopRequest [this=%p]", this));
   2944  mStreamListenerFunctions.AppendElement(StreamListenerFunction{
   2945      VariantIndex<2>{}, OnStopRequestParams{aRequest, aStatusCode}});
   2946 
   2947  // If we're not a multi-part channel, then we're finished and we don't
   2948  // expect any further events. If we are, then this might be called again,
   2949  // so wait for OnAfterLastPart instead.
   2950  nsCOMPtr<nsIMultiPartChannel> multiPartChannel = do_QueryInterface(aRequest);
   2951  if (!multiPartChannel) {
   2952    mIsFinished = true;
   2953  }
   2954 
   2955  mStreamFilterRequests.Clear();
   2956 
   2957  return NS_OK;
   2958 }
   2959 
   2960 NS_IMETHODIMP
   2961 DocumentLoadListener::OnDataAvailable(nsIRequest* aRequest,
   2962                                      nsIInputStream* aInputStream,
   2963                                      uint64_t aOffset, uint32_t aCount) {
   2964  LOG(("DocumentLoadListener OnDataAvailable [this=%p]", this));
   2965  // This isn't supposed to happen, since we suspended the channel, but
   2966  // sometimes Suspend just doesn't work. This can happen when we're routing
   2967  // through nsUnknownDecoder to sniff the content type, and it doesn't handle
   2968  // being suspended. Let's just store the data and manually forward it to our
   2969  // redirected channel when it's ready.
   2970  nsCString data;
   2971  nsresult rv = NS_ReadInputStreamToString(aInputStream, data, aCount);
   2972  NS_ENSURE_SUCCESS(rv, rv);
   2973 
   2974  mStreamListenerFunctions.AppendElement(StreamListenerFunction{
   2975      VariantIndex<1>{},
   2976      OnDataAvailableParams{aRequest, std::move(data), aOffset, aCount}});
   2977 
   2978  return NS_OK;
   2979 }
   2980 
   2981 //-----------------------------------------------------------------------------
   2982 // DoucmentLoadListener::nsIMultiPartChannelListener
   2983 //-----------------------------------------------------------------------------
   2984 
   2985 NS_IMETHODIMP
   2986 DocumentLoadListener::OnAfterLastPart(nsresult aStatus) {
   2987  LOG(("DocumentLoadListener OnAfterLastPart [this=%p]", this));
   2988  if (!mInitiatedRedirectToRealChannel) {
   2989    // if we get here, and we haven't initiated a redirect to a real
   2990    // channel, then it means we never got OnStartRequest (maybe a problem?)
   2991    // and we retargeted everything.
   2992    LOG(("DocumentLoadListener Disconnecting child"));
   2993    DisconnectListeners(NS_BINDING_RETARGETED, NS_OK);
   2994    return NS_OK;
   2995  }
   2996  mStreamListenerFunctions.AppendElement(StreamListenerFunction{
   2997      VariantIndex<3>{}, OnAfterLastPartParams{aStatus}});
   2998  mIsFinished = true;
   2999  return NS_OK;
   3000 }
   3001 
   3002 NS_IMETHODIMP
   3003 DocumentLoadListener::GetInterface(const nsIID& aIID, void** result) {
   3004  RefPtr<CanonicalBrowsingContext> browsingContext =
   3005      GetLoadingBrowsingContext();
   3006  if (aIID.Equals(NS_GET_IID(nsILoadContext)) && browsingContext) {
   3007    browsingContext.forget(result);
   3008    return NS_OK;
   3009  }
   3010 
   3011  return QueryInterface(aIID, result);
   3012 }
   3013 
   3014 ////////////////////////////////////////////////////////////////////////////////
   3015 // nsIParentChannel
   3016 ////////////////////////////////////////////////////////////////////////////////
   3017 
   3018 NS_IMETHODIMP
   3019 DocumentLoadListener::SetParentListener(
   3020    mozilla::net::ParentChannelListener* listener) {
   3021  // We don't need this (do we?)
   3022  return NS_OK;
   3023 }
   3024 
   3025 NS_IMETHODIMP
   3026 DocumentLoadListener::SetClassifierMatchedInfo(const nsACString& aList,
   3027                                               const nsACString& aProvider,
   3028                                               const nsACString& aFullHash) {
   3029  ClassifierMatchedInfoParams params;
   3030  params.mList = aList;
   3031  params.mProvider = aProvider;
   3032  params.mFullHash = aFullHash;
   3033 
   3034  mIParentChannelFunctions.AppendElement(
   3035      IParentChannelFunction{VariantIndex<0>{}, std::move(params)});
   3036  return NS_OK;
   3037 }
   3038 
   3039 NS_IMETHODIMP
   3040 DocumentLoadListener::SetClassifierMatchedTrackingInfo(
   3041    const nsACString& aLists, const nsACString& aFullHash) {
   3042  ClassifierMatchedTrackingInfoParams params;
   3043  params.mLists = aLists;
   3044  params.mFullHashes = aFullHash;
   3045 
   3046  mIParentChannelFunctions.AppendElement(
   3047      IParentChannelFunction{VariantIndex<1>{}, std::move(params)});
   3048  return NS_OK;
   3049 }
   3050 
   3051 NS_IMETHODIMP
   3052 DocumentLoadListener::NotifyClassificationFlags(uint32_t aClassificationFlags,
   3053                                                bool aIsThirdParty) {
   3054  mIParentChannelFunctions.AppendElement(IParentChannelFunction{
   3055      VariantIndex<2>{},
   3056      ClassificationFlagsParams{aClassificationFlags, aIsThirdParty}});
   3057  return NS_OK;
   3058 }
   3059 
   3060 NS_IMETHODIMP
   3061 DocumentLoadListener::Delete() {
   3062  MOZ_ASSERT_UNREACHABLE("This method is unused");
   3063  return NS_OK;
   3064 }
   3065 
   3066 NS_IMETHODIMP
   3067 DocumentLoadListener::GetRemoteType(nsACString& aRemoteType) {
   3068  // FIXME: The remote type here should be pulled from the remote process used
   3069  // to create this DLL, not from the current `browsingContext`.
   3070  RefPtr<CanonicalBrowsingContext> browsingContext =
   3071      GetDocumentBrowsingContext();
   3072  if (!browsingContext) {
   3073    return NS_ERROR_UNEXPECTED;
   3074  }
   3075 
   3076  ErrorResult error;
   3077  browsingContext->GetCurrentRemoteType(aRemoteType, error);
   3078  if (error.Failed()) {
   3079    aRemoteType = NOT_REMOTE_TYPE;
   3080  }
   3081  return NS_OK;
   3082 }
   3083 
   3084 ////////////////////////////////////////////////////////////////////////////////
   3085 // nsIChannelEventSink
   3086 ////////////////////////////////////////////////////////////////////////////////
   3087 
   3088 NS_IMETHODIMP
   3089 DocumentLoadListener::AsyncOnChannelRedirect(
   3090    nsIChannel* aOldChannel, nsIChannel* aNewChannel, uint32_t aFlags,
   3091    nsIAsyncVerifyRedirectCallback* aCallback) {
   3092  LOG(("DocumentLoadListener::AsyncOnChannelRedirect [this=%p flags=%" PRIu32
   3093       "]",
   3094       this, aFlags));
   3095  // We generally don't want to notify the content process about redirects,
   3096  // so just update our channel and tell the callback that we're good to go.
   3097  mChannel = aNewChannel;
   3098 
   3099  // We need the original URI of the current channel to use to open the real
   3100  // channel in the content process. Unfortunately we overwrite the original
   3101  // uri of the new channel with the original pre-redirect URI, so grab
   3102  // a copy of it now and save it on the loadInfo corresponding to the
   3103  // new channel.
   3104  nsCOMPtr<nsILoadInfo> loadInfoFromChannel = mChannel->LoadInfo();
   3105  MOZ_ASSERT(loadInfoFromChannel);
   3106  nsCOMPtr<nsIURI> uri;
   3107  mChannel->GetOriginalURI(getter_AddRefs(uri));
   3108  loadInfoFromChannel->SetChannelCreationOriginalURI(uri);
   3109 
   3110  // Since we're redirecting away from aOldChannel, we should check if it
   3111  // had a COOP mismatch, since we want the final result for this to
   3112  // include the state of all channels we redirected through.
   3113  nsCOMPtr<nsIHttpChannelInternal> httpChannel = do_QueryInterface(aOldChannel);
   3114  if (httpChannel) {
   3115    bool isCOOPMismatch = false;
   3116    (void)NS_WARN_IF(NS_FAILED(
   3117        httpChannel->HasCrossOriginOpenerPolicyMismatch(&isCOOPMismatch)));
   3118    mHasCrossOriginOpenerPolicyMismatch |= isCOOPMismatch;
   3119  }
   3120 
   3121  // If HTTPS-Only mode is enabled, we need to check whether the exception-flag
   3122  // needs to be removed or set, by asking the PermissionManager.
   3123  nsHTTPSOnlyUtils::TestSitePermissionAndPotentiallyAddExemption(mChannel);
   3124 
   3125  // We don't need to confirm internal redirects or record any
   3126  // history for them, so just immediately verify and return.
   3127  if (aFlags & nsIChannelEventSink::REDIRECT_INTERNAL) {
   3128    LOG(
   3129        ("DocumentLoadListener AsyncOnChannelRedirect [this=%p] "
   3130         "flags=REDIRECT_INTERNAL",
   3131         this));
   3132    aCallback->OnRedirectVerifyCallback(NS_OK);
   3133    return NS_OK;
   3134  }
   3135 
   3136  // Cancel cross origin redirects as described by whatwg:
   3137  // > Note: [The early hint reponse] is discarded if it is succeeded by a
   3138  // > cross-origin redirect.
   3139  // https://html.spec.whatwg.org/multipage/semantics.html#early-hints
   3140  nsCOMPtr<nsIURI> oldURI;
   3141  aOldChannel->GetURI(getter_AddRefs(oldURI));
   3142  nsIScriptSecurityManager* ssm = nsContentUtils::GetSecurityManager();
   3143  nsresult rv = ssm->CheckSameOriginURI(oldURI, uri, false, false);
   3144  if (NS_FAILED(rv)) {
   3145    mEarlyHintsService.Cancel(
   3146        "DocumentLoadListener::AsyncOnChannelRedirect: cors redirect"_ns);
   3147  }
   3148 
   3149  if (GetDocumentBrowsingContext()) {
   3150    if (!net::ChannelIsPost(aOldChannel)) {
   3151      AddURIVisit(aOldChannel, 0);
   3152      nsDocShell::SaveLastVisit(aNewChannel, oldURI, aFlags);
   3153    }
   3154  }
   3155  mHaveVisibleRedirect |= true;
   3156 
   3157  LOG(
   3158      ("DocumentLoadListener AsyncOnChannelRedirect [this=%p] "
   3159       "mHaveVisibleRedirect=%c",
   3160       this, mHaveVisibleRedirect ? 'T' : 'F'));
   3161 
   3162  // Clear out our nsIParentChannel functions, since a normal parent
   3163  // channel would actually redirect and not have those values on the new one.
   3164  // We expect the URI classifier to run on the redirected channel with
   3165  // the new URI and set these again.
   3166  mIParentChannelFunctions.Clear();
   3167 
   3168  // If we had a remote type override, ensure it's been cleared after a
   3169  // redirect, as it can't apply anymore.
   3170  mRemoteTypeOverride.reset();
   3171 
   3172 #ifdef ANDROID
   3173  nsCOMPtr<nsIURI> uriBeingLoaded =
   3174      AntiTrackingUtils::MaybeGetDocumentURIBeingLoaded(mChannel);
   3175 
   3176  RefPtr<MozPromise<bool, bool, false>> promise;
   3177  RefPtr<CanonicalBrowsingContext> bc =
   3178      mParentChannelListener->GetBrowsingContext();
   3179  nsCOMPtr<nsIWidget> widget =
   3180      bc ? bc->GetParentProcessWidgetContaining() : nullptr;
   3181  RefPtr<nsWindow> window = nsWindow::From(widget);
   3182 
   3183  if (window && !nsExternalHelperAppService::ExternalProtocolIsBlockedBySandbox(
   3184                    bc, false)) {
   3185    promise = window->OnLoadRequest(uriBeingLoaded,
   3186                                    nsIBrowserDOMWindow::OPEN_CURRENTWINDOW,
   3187                                    nsIWebNavigation::LOAD_FLAGS_IS_REDIRECT,
   3188                                    nullptr, false, bc->IsTopContent());
   3189  }
   3190 
   3191  if (promise) {
   3192    RefPtr<nsIAsyncVerifyRedirectCallback> cb = aCallback;
   3193    promise->Then(
   3194        GetCurrentSerialEventTarget(), __func__,
   3195        [=](const MozPromise<bool, bool, false>::ResolveOrRejectValue& aValue) {
   3196          if (aValue.IsResolve()) {
   3197            bool handled = aValue.ResolveValue();
   3198            if (handled) {
   3199              cb->OnRedirectVerifyCallback(NS_ERROR_ABORT);
   3200            } else {
   3201              cb->OnRedirectVerifyCallback(NS_OK);
   3202            }
   3203          }
   3204        });
   3205  } else
   3206 #endif /* ANDROID */
   3207  {
   3208    aCallback->OnRedirectVerifyCallback(NS_OK);
   3209  }
   3210  return NS_OK;
   3211 }
   3212 
   3213 nsIURI* DocumentLoadListener::GetChannelCreationURI() const {
   3214  nsCOMPtr<nsILoadInfo> channelLoadInfo = mChannel->LoadInfo();
   3215 
   3216  nsCOMPtr<nsIURI> uri;
   3217  channelLoadInfo->GetChannelCreationOriginalURI(getter_AddRefs(uri));
   3218  if (uri) {
   3219    // See channelCreationOriginalURI for more info. We use this instead of the
   3220    // originalURI of the channel to help us avoid the situation when we use
   3221    // the URI of a redirect that has failed to happen.
   3222    return uri;
   3223  }
   3224 
   3225  // Otherwise, get the original URI from the channel.
   3226  mChannel->GetOriginalURI(getter_AddRefs(uri));
   3227  return uri;
   3228 }
   3229 
   3230 // This method returns the cached result of running the Cross-Origin-Opener
   3231 // policy compare algorithm by calling ComputeCrossOriginOpenerPolicyMismatch
   3232 bool DocumentLoadListener::HasCrossOriginOpenerPolicyMismatch() const {
   3233  // If we found a COOP mismatch on an earlier channel and then
   3234  // redirected away from that, we should use that result.
   3235  if (mHasCrossOriginOpenerPolicyMismatch) {
   3236    return true;
   3237  }
   3238 
   3239  nsCOMPtr<nsIHttpChannelInternal> httpChannel = do_QueryInterface(mChannel);
   3240  if (!httpChannel) {
   3241    // Not an nsIHttpChannelInternal assume it's okay to switch.
   3242    return false;
   3243  }
   3244 
   3245  bool isCOOPMismatch = false;
   3246  (void)NS_WARN_IF(NS_FAILED(
   3247      httpChannel->HasCrossOriginOpenerPolicyMismatch(&isCOOPMismatch)));
   3248  return isCOOPMismatch;
   3249 }
   3250 
   3251 auto DocumentLoadListener::AttachStreamFilter()
   3252    -> RefPtr<ChildEndpointPromise> {
   3253  LOG(("DocumentLoadListener AttachStreamFilter [this=%p]", this));
   3254 
   3255  StreamFilterRequest* request = mStreamFilterRequests.AppendElement();
   3256  request->mPromise = new ChildEndpointPromise::Private(__func__);
   3257  return request->mPromise;
   3258 }
   3259 
   3260 NS_IMETHODIMP DocumentLoadListener::OnProgress(nsIRequest* aRequest,
   3261                                               int64_t aProgress,
   3262                                               int64_t aProgressMax) {
   3263  return NS_OK;
   3264 }
   3265 
   3266 NS_IMETHODIMP DocumentLoadListener::OnStatus(nsIRequest* aRequest,
   3267                                             nsresult aStatus,
   3268                                             const char16_t* aStatusArg) {
   3269  nsCOMPtr<nsIChannel> channel = mChannel;
   3270 
   3271  RefPtr<BrowsingContextWebProgress> webProgress =
   3272      GetLoadingBrowsingContext()->GetWebProgress();
   3273 
   3274  nsAutoString host;
   3275  host.Append(aStatusArg);
   3276 
   3277  nsAutoString message;
   3278  nsresult rv = nsDocLoader::FormatStatusMessage(aStatus, host, message, sL10n);
   3279  if (NS_WARN_IF(NS_FAILED(rv))) {
   3280    return rv;
   3281  }
   3282 
   3283  if (webProgress) {
   3284    NS_DispatchToMainThread(
   3285        NS_NewRunnableFunction("DocumentLoadListener::OnStatus", [=]() {
   3286          webProgress->OnStatusChange(webProgress, channel, aStatus,
   3287                                      message.get());
   3288        }));
   3289  }
   3290  return NS_OK;
   3291 }
   3292 
   3293 NS_IMETHODIMP DocumentLoadListener::EarlyHint(const nsACString& aLinkHeader,
   3294                                              const nsACString& aReferrerPolicy,
   3295                                              const nsACString& aCSPHeader) {
   3296  LOG(("DocumentLoadListener::EarlyHint.\n"));
   3297  mEarlyHintsService.EarlyHint(aLinkHeader, GetChannelCreationURI(), mChannel,
   3298                               aReferrerPolicy, aCSPHeader,
   3299                               GetLoadingBrowsingContext());
   3300  return NS_OK;
   3301 }
   3302 
   3303 }  // namespace net
   3304 }  // namespace mozilla
   3305 
   3306 #undef LOG