1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set sw=2 ts=8 et tw=80 : */
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/. */
8 #include "DocumentLoadListener.h"
10 #include "NeckoCommon.h"
11 #include "mozilla/AntiTrackingUtils.h"
12 #include "mozilla/DebugOnly.h"
13 #include "mozilla/LoadInfo.h"
14 #include "mozilla/NullPrincipal.h"
15 #include "mozilla/RefPtr.h"
16 #include "mozilla/ResultVariant.h"
17 #include "mozilla/ScopeExit.h"
18 #include "mozilla/StaticPrefs_extensions.h"
19 #include "mozilla/StaticPrefs_fission.h"
20 #include "mozilla/StaticPrefs_security.h"
21 #include "mozilla/dom/BrowserParent.h"
22 #include "mozilla/dom/BrowsingContextGroup.h"
23 #include "mozilla/dom/CanonicalBrowsingContext.h"
24 #include "mozilla/dom/ChildProcessChannelListener.h"
25 #include "mozilla/dom/ClientChannelHelper.h"
26 #include "mozilla/dom/ContentParent.h"
27 #include "mozilla/dom/ContentProcessManager.h"
28 #include "mozilla/dom/ProcessIsolation.h"
29 #include "mozilla/dom/SessionHistoryEntry.h"
30 #include "mozilla/dom/WindowGlobalParent.h"
31 #include "mozilla/dom/ipc/IdType.h"
32 #include "mozilla/net/CookieJarSettings.h"
33 #include "mozilla/net/HttpChannelParent.h"
34 #include "mozilla/net/RedirectChannelRegistrar.h"
35 #include "nsContentSecurityUtils.h"
36 #include "nsContentSecurityManager.h"
37 #include "nsDocShell.h"
38 #include "nsDocShellLoadState.h"
39 #include "nsDocShellLoadTypes.h"
40 #include "nsDOMNavigationTiming.h"
41 #include "nsDSURIContentListener.h"
42 #include "nsObjectLoadingContent.h"
43 #include "nsOpenWindowInfo.h"
44 #include "nsExternalHelperAppService.h"
45 #include "nsHttpChannel.h"
46 #include "nsIBrowser.h"
47 #include "nsIHttpChannelInternal.h"
48 #include "nsIStreamConverterService.h"
49 #include "nsIViewSourceChannel.h"
50 #include "nsImportModule.h"
51 #include "nsIXULRuntime.h"
52 #include "nsMimeTypes.h"
53 #include "nsQueryObject.h"
54 #include "nsRedirectHistoryEntry.h"
55 #include "nsSandboxFlags.h"
56 #include "nsSHistory.h"
57 #include "nsStringStream.h"
58 #include "nsURILoader.h"
59 #include "nsWebNavigationInfo.h"
60 #include "mozilla/dom/BrowserParent.h"
61 #include "mozilla/dom/Element.h"
62 #include "mozilla/dom/nsHTTPSOnlyUtils.h"
63 #include "mozilla/dom/ReferrerInfo.h"
64 #include "mozilla/dom/RemoteWebProgressRequest.h"
65 #include "mozilla/net/UrlClassifierFeatureFactory.h"
66 #include "mozilla/ExtensionPolicyService.h"
69 # include "mozilla/widget/nsWindow.h"
72 mozilla::LazyLogModule
gDocumentChannelLog("DocumentChannel");
73 #define LOG(fmt) MOZ_LOG(gDocumentChannelLog, mozilla::LogLevel::Verbose, fmt)
75 extern mozilla::LazyLogModule gSHIPBFCacheLog
;
77 // Bug 136580: Limit to the number of nested content frames that can have the
78 // same URL. This is to stop content that is recursively loading
79 // itself. Note that "#foo" on the end of URL doesn't affect
80 // whether it's considered identical, but "?foo" or ";foo" are
81 // considered and compared.
82 // Limit this to 2, like chromium does.
83 static constexpr int kMaxSameURLContentFrames
= 2;
85 using namespace mozilla::dom
;
90 static ContentParentId
GetContentProcessId(ContentParent
* aContentParent
) {
91 return aContentParent
? aContentParent
->ChildID() : ContentParentId
{0};
94 static void SetNeedToAddURIVisit(nsIChannel
* aChannel
,
95 bool aNeedToAddURIVisit
) {
96 nsCOMPtr
<nsIWritablePropertyBag2
> props(do_QueryInterface(aChannel
));
101 props
->SetPropertyAsBool(u
"docshell.needToAddURIVisit"_ns
,
105 static auto SecurityFlagsForLoadInfo(nsDocShellLoadState
* aLoadState
)
107 // TODO: Block copied from nsDocShell::DoURILoad, refactor out somewhere
108 nsSecurityFlags securityFlags
=
109 nsILoadInfo::SEC_ALLOW_CROSS_ORIGIN_SEC_CONTEXT_IS_NULL
;
111 if (aLoadState
->LoadType() == LOAD_ERROR_PAGE
) {
112 securityFlags
|= nsILoadInfo::SEC_LOAD_ERROR_PAGE
;
115 if (aLoadState
->PrincipalToInherit()) {
116 bool isSrcdoc
= aLoadState
->HasInternalLoadFlags(
117 nsDocShell::INTERNAL_LOAD_FLAGS_IS_SRCDOC
);
118 bool inheritAttrs
= nsContentUtils::ChannelShouldInheritPrincipal(
119 aLoadState
->PrincipalToInherit(), aLoadState
->URI(),
120 true, // aInheritForAboutBlank
123 bool isData
= SchemeIsData(aLoadState
->URI());
124 if (inheritAttrs
&& !isData
) {
125 securityFlags
|= nsILoadInfo::SEC_FORCE_INHERIT_PRINCIPAL
;
129 return securityFlags
;
132 // Construct a LoadInfo object to use when creating the internal channel for a
133 // Document/SubDocument load.
134 static auto CreateDocumentLoadInfo(CanonicalBrowsingContext
* aBrowsingContext
,
135 nsDocShellLoadState
* aLoadState
)
136 -> already_AddRefed
<LoadInfo
> {
137 uint32_t sandboxFlags
= aBrowsingContext
->GetSandboxFlags();
138 RefPtr
<LoadInfo
> loadInfo
;
140 auto securityFlags
= SecurityFlagsForLoadInfo(aLoadState
);
142 if (aBrowsingContext
->GetParent()) {
143 loadInfo
= LoadInfo::CreateForFrame(
144 aBrowsingContext
, aLoadState
->TriggeringPrincipal(),
145 aLoadState
->GetEffectiveTriggeringRemoteType(), securityFlags
,
148 OriginAttributes attrs
;
149 aBrowsingContext
->GetOriginAttributes(attrs
);
150 loadInfo
= LoadInfo::CreateForDocument(
151 aBrowsingContext
, aLoadState
->URI(), aLoadState
->TriggeringPrincipal(),
152 aLoadState
->GetEffectiveTriggeringRemoteType(), attrs
, securityFlags
,
156 if (aLoadState
->IsExemptFromHTTPSFirstMode()) {
157 uint32_t httpsOnlyStatus
= loadInfo
->GetHttpsOnlyStatus();
158 httpsOnlyStatus
|= nsILoadInfo::HTTPS_FIRST_EXEMPT_NEXT_LOAD
;
159 loadInfo
->SetHttpsOnlyStatus(httpsOnlyStatus
);
162 loadInfo
->SetWasSchemelessInput(aLoadState
->GetWasSchemelessInput());
164 loadInfo
->SetTriggeringSandboxFlags(aLoadState
->TriggeringSandboxFlags());
165 loadInfo
->SetTriggeringWindowId(aLoadState
->TriggeringWindowId());
166 loadInfo
->SetTriggeringStorageAccess(aLoadState
->TriggeringStorageAccess());
167 loadInfo
->SetHasValidUserGestureActivation(
168 aLoadState
->HasValidUserGestureActivation());
169 loadInfo
->SetIsMetaRefresh(aLoadState
->IsMetaRefresh());
171 return loadInfo
.forget();
174 // Construct a LoadInfo object to use when creating the internal channel for an
175 // Object/Embed load.
176 static auto CreateObjectLoadInfo(nsDocShellLoadState
* aLoadState
,
177 uint64_t aInnerWindowId
,
178 nsContentPolicyType aContentPolicyType
,
179 uint32_t aSandboxFlags
)
180 -> already_AddRefed
<LoadInfo
> {
181 RefPtr
<WindowGlobalParent
> wgp
=
182 WindowGlobalParent::GetByInnerWindowId(aInnerWindowId
);
183 MOZ_RELEASE_ASSERT(wgp
);
185 auto securityFlags
= SecurityFlagsForLoadInfo(aLoadState
);
187 RefPtr
<LoadInfo
> loadInfo
= LoadInfo::CreateForNonDocument(
188 wgp
, wgp
->DocumentPrincipal(), aContentPolicyType
, securityFlags
,
191 loadInfo
->SetHasValidUserGestureActivation(
192 aLoadState
->HasValidUserGestureActivation());
193 loadInfo
->SetTriggeringSandboxFlags(aLoadState
->TriggeringSandboxFlags());
194 loadInfo
->SetTriggeringWindowId(aLoadState
->TriggeringWindowId());
195 loadInfo
->SetTriggeringStorageAccess(aLoadState
->TriggeringStorageAccess());
196 loadInfo
->SetIsMetaRefresh(aLoadState
->IsMetaRefresh());
198 return loadInfo
.forget();
202 * An extension to nsDocumentOpenInfo that we run in the parent process, so
203 * that we can make the decision to retarget to content handlers or the external
204 * helper app, before we make process switching decisions.
206 * This modifies the behaviour of nsDocumentOpenInfo so that it can do
207 * retargeting, but doesn't do stream conversion (but confirms that we will be
208 * able to do so later).
210 * We still run nsDocumentOpenInfo in the content process, but disable
211 * retargeting, so that it can only apply stream conversion, and then send data
214 class ParentProcessDocumentOpenInfo final
: public nsDocumentOpenInfo
,
215 public nsIMultiPartChannelListener
{
217 ParentProcessDocumentOpenInfo(ParentChannelListener
* aListener
,
219 mozilla::dom::BrowsingContext
* aBrowsingContext
,
220 bool aIsDocumentLoad
)
221 : nsDocumentOpenInfo(aFlags
, false),
222 mBrowsingContext(aBrowsingContext
),
223 mListener(aListener
),
224 mIsDocumentLoad(aIsDocumentLoad
) {
225 LOG(("ParentProcessDocumentOpenInfo ctor [this=%p]", this));
228 NS_DECL_ISUPPORTS_INHERITED
230 // The default content listener is always a docshell, so this manually
231 // implements the same checks, and if it succeeds, uses the parent
232 // channel listener so that we forward onto DocumentLoadListener.
233 bool TryDefaultContentListener(nsIChannel
* aChannel
,
234 const nsCString
& aContentType
) {
235 uint32_t canHandle
= nsWebNavigationInfo::IsTypeSupported(aContentType
);
236 if (canHandle
!= nsIWebNavigationInfo::UNSUPPORTED
) {
237 m_targetStreamListener
= mListener
;
238 nsLoadFlags loadFlags
= 0;
239 aChannel
->GetLoadFlags(&loadFlags
);
240 aChannel
->SetLoadFlags(loadFlags
| nsIChannel::LOAD_TARGETED
);
246 bool TryDefaultContentListener(nsIChannel
* aChannel
) override
{
247 return TryDefaultContentListener(aChannel
, mContentType
);
250 // Generally we only support stream converters that can tell
251 // use exactly what type they'll output. If we find one, then
252 // we just target to our default listener directly (without
253 // conversion), and the content process nsDocumentOpenInfo will
254 // run and do the actual conversion.
255 nsresult
TryStreamConversion(nsIChannel
* aChannel
) override
{
256 // The one exception is nsUnknownDecoder, which works in the parent
257 // (and we need to know what the content type is before we can
258 // decide if it will be handled in the parent), so we run that here.
259 if (mContentType
.LowerCaseEqualsASCII(UNKNOWN_CONTENT_TYPE
) ||
260 mContentType
.IsEmpty()) {
261 return nsDocumentOpenInfo::TryStreamConversion(aChannel
);
265 nsCOMPtr
<nsIStreamConverterService
> streamConvService
=
266 do_GetService(NS_STREAMCONVERTERSERVICE_CONTRACTID
, &rv
);
268 rv
= streamConvService
->ConvertedType(mContentType
, aChannel
, str
);
269 NS_ENSURE_SUCCESS(rv
, rv
);
271 // We only support passing data to the default content listener
272 // (docshell), and we don't supported chaining converters.
273 if (TryDefaultContentListener(aChannel
, str
)) {
277 // This is the same result as nsStreamConverterService uses when it
278 // can't find a converter
279 return NS_ERROR_FAILURE
;
282 nsresult
TryExternalHelperApp(nsIExternalHelperAppService
* aHelperAppService
,
283 nsIChannel
* aChannel
) override
{
284 RefPtr
<nsIStreamListener
> listener
;
285 nsresult rv
= aHelperAppService
->CreateListener(
286 mContentType
, aChannel
, mBrowsingContext
, false, nullptr,
287 getter_AddRefs(listener
));
288 if (NS_SUCCEEDED(rv
)) {
289 m_targetStreamListener
= listener
;
294 nsDocumentOpenInfo
* Clone() override
{
296 return new ParentProcessDocumentOpenInfo(mListener
, mFlags
,
297 mBrowsingContext
, mIsDocumentLoad
);
300 nsresult
OnDocumentStartRequest(nsIRequest
* request
) {
301 LOG(("ParentProcessDocumentOpenInfo OnDocumentStartRequest [this=%p]",
304 nsresult rv
= nsDocumentOpenInfo::OnStartRequest(request
);
306 // If we didn't find a content handler, and we don't have a listener, then
307 // just forward to our default listener. This happens when the channel is in
308 // an error state, and we want to just forward that on to be handled in the
309 // content process, or when DONT_RETARGET is set.
310 if ((NS_SUCCEEDED(rv
) || rv
== NS_ERROR_WONT_HANDLE_CONTENT
) &&
311 !mUsedContentHandler
&& !m_targetStreamListener
) {
312 m_targetStreamListener
= mListener
;
313 return m_targetStreamListener
->OnStartRequest(request
);
315 if (m_targetStreamListener
!= mListener
) {
317 ("ParentProcessDocumentOpenInfo targeted to non-default listener "
320 // If this is the only part, then we can immediately tell our listener
321 // that it won't be getting any content and disconnect it. For multipart
322 // channels we have to wait until we've handled all parts before we know.
323 // This does mean that the content process can still Cancel() a multipart
324 // response while the response is being handled externally, but this
325 // matches the single-process behaviour.
326 // If we got cloned, then we don't need to do this, as only the last link
328 // Multi-part channels are guaranteed to call OnAfterLastPart, which we
329 // forward to the listeners, so it will handle disconnection at that
331 nsCOMPtr
<nsIMultiPartChannel
> multiPartChannel
=
332 do_QueryInterface(request
);
333 if (!multiPartChannel
&& !mCloned
) {
334 DisconnectChildListeners(NS_FAILED(rv
) ? rv
: NS_BINDING_RETARGETED
,
341 nsresult
OnObjectStartRequest(nsIRequest
* request
) {
342 LOG(("ParentProcessDocumentOpenInfo OnObjectStartRequest [this=%p]", this));
344 // If this load will be treated as a document load, run through
345 // nsDocumentOpenInfo for consistency with other document loads.
347 // If the dom.navigation.object_embed.allow_retargeting pref is enabled,
348 // this may lead to the resource being downloaded.
349 if (nsCOMPtr
<nsIChannel
> channel
= do_QueryInterface(request
);
350 channel
&& channel
->IsDocument()) {
351 return OnDocumentStartRequest(request
);
354 // Just redirect to the nsObjectLoadingContent in the content process.
355 m_targetStreamListener
= mListener
;
356 return m_targetStreamListener
->OnStartRequest(request
);
359 NS_IMETHOD
OnStartRequest(nsIRequest
* request
) override
{
360 LOG(("ParentProcessDocumentOpenInfo OnStartRequest [this=%p]", this));
362 if (mIsDocumentLoad
) {
363 return OnDocumentStartRequest(request
);
366 return OnObjectStartRequest(request
);
369 NS_IMETHOD
OnAfterLastPart(nsresult aStatus
) override
{
370 mListener
->OnAfterLastPart(aStatus
);
375 virtual ~ParentProcessDocumentOpenInfo() {
376 LOG(("ParentProcessDocumentOpenInfo dtor [this=%p]", this));
379 void DisconnectChildListeners(nsresult aStatus
, nsresult aLoadGroupStatus
) {
380 // Tell the DocumentLoadListener to notify the content process that it's
381 // been entirely retargeted, and to stop waiting.
382 // Clear mListener's pointer to the DocumentLoadListener to break the
384 RefPtr
<DocumentLoadListener
> doc
= do_GetInterface(ToSupports(mListener
));
386 doc
->DisconnectListeners(aStatus
, aLoadGroupStatus
);
387 mListener
->SetListenerAfterRedirect(nullptr);
390 RefPtr
<mozilla::dom::BrowsingContext
> mBrowsingContext
;
391 RefPtr
<ParentChannelListener
> mListener
;
392 const bool mIsDocumentLoad
;
395 * Set to true if we got cloned to create a chained listener.
397 bool mCloned
= false;
400 NS_IMPL_ADDREF_INHERITED(ParentProcessDocumentOpenInfo
, nsDocumentOpenInfo
)
401 NS_IMPL_RELEASE_INHERITED(ParentProcessDocumentOpenInfo
, nsDocumentOpenInfo
)
403 NS_INTERFACE_MAP_BEGIN(ParentProcessDocumentOpenInfo
)
404 NS_INTERFACE_MAP_ENTRY(nsIMultiPartChannelListener
)
405 NS_INTERFACE_MAP_END_INHERITING(nsDocumentOpenInfo
)
407 NS_IMPL_ADDREF(DocumentLoadListener
)
408 NS_IMPL_RELEASE(DocumentLoadListener
)
410 NS_INTERFACE_MAP_BEGIN(DocumentLoadListener
)
411 NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports
, nsIInterfaceRequestor
)
412 NS_INTERFACE_MAP_ENTRY(nsIInterfaceRequestor
)
413 NS_INTERFACE_MAP_ENTRY(nsIRequestObserver
)
414 NS_INTERFACE_MAP_ENTRY(nsIStreamListener
)
415 NS_INTERFACE_MAP_ENTRY(nsIParentChannel
)
416 NS_INTERFACE_MAP_ENTRY(nsIAsyncVerifyRedirectReadyCallback
)
417 NS_INTERFACE_MAP_ENTRY(nsIChannelEventSink
)
418 NS_INTERFACE_MAP_ENTRY(nsIMultiPartChannelListener
)
419 NS_INTERFACE_MAP_ENTRY(nsIProgressEventSink
)
420 NS_INTERFACE_MAP_ENTRY(nsIEarlyHintObserver
)
421 NS_INTERFACE_MAP_ENTRY_CONCRETE(DocumentLoadListener
)
424 DocumentLoadListener::DocumentLoadListener(
425 CanonicalBrowsingContext
* aLoadingBrowsingContext
, bool aIsDocumentLoad
)
426 : mIsDocumentLoad(aIsDocumentLoad
) {
427 LOG(("DocumentLoadListener ctor [this=%p]", this));
428 mParentChannelListener
=
429 new ParentChannelListener(this, aLoadingBrowsingContext
);
432 DocumentLoadListener::~DocumentLoadListener() {
433 LOG(("DocumentLoadListener dtor [this=%p]", this));
436 void DocumentLoadListener::AddURIVisit(nsIChannel
* aChannel
,
437 uint32_t aLoadFlags
) {
438 if (mLoadStateLoadType
== LOAD_ERROR_PAGE
||
439 mLoadStateLoadType
== LOAD_BYPASS_HISTORY
) {
443 nsCOMPtr
<nsIURI
> uri
;
444 NS_GetFinalChannelURI(aChannel
, getter_AddRefs(uri
));
446 nsCOMPtr
<nsIURI
> previousURI
;
447 uint32_t previousFlags
= 0;
448 if (mLoadStateLoadType
& nsIDocShell::LOAD_CMD_RELOAD
) {
451 nsDocShell::ExtractLastVisit(aChannel
, getter_AddRefs(previousURI
),
455 // Get the HTTP response code, if available.
456 uint32_t responseStatus
= 0;
457 nsCOMPtr
<nsIHttpChannel
> httpChannel
= do_QueryInterface(aChannel
);
459 Unused
<< httpChannel
->GetResponseStatus(&responseStatus
);
462 RefPtr
<CanonicalBrowsingContext
> browsingContext
=
463 GetDocumentBrowsingContext();
464 nsCOMPtr
<nsIWidget
> widget
=
465 browsingContext
->GetParentProcessWidgetContaining();
467 nsCOMPtr
<nsILoadInfo
> loadInfo
= aChannel
->LoadInfo();
469 // Check if the URI has a http scheme and if either HSTS is enabled for this
470 // host, or if we were upgraded by HTTPS-Only/First. If this is the case, we
471 // want to mark this URI specially, as it will be followed shortly by an
472 // almost identical https history entry. That way the global history
473 // implementation can handle the visit appropriately (e.g. by marking it as
474 // `hidden`, so only the https url appears in default history views).
476 uri
->SchemeIs("http") &&
477 (loadInfo
->GetHstsStatus() ||
478 (loadInfo
->GetHttpsOnlyStatus() &
479 (nsILoadInfo::HTTPS_ONLY_UPGRADED_HTTPS_FIRST
|
480 nsILoadInfo::HTTPS_ONLY_UPGRADED_LISTENER_NOT_REGISTERED
|
481 nsILoadInfo::HTTPS_ONLY_UPGRADED_LISTENER_REGISTERED
)));
483 nsDocShell::InternalAddURIVisit(uri
, previousURI
, previousFlags
,
484 responseStatus
, browsingContext
, widget
,
485 mLoadStateLoadType
, wasUpgraded
);
488 CanonicalBrowsingContext
* DocumentLoadListener::GetLoadingBrowsingContext()
490 return mParentChannelListener
? mParentChannelListener
->GetBrowsingContext()
494 CanonicalBrowsingContext
* DocumentLoadListener::GetDocumentBrowsingContext()
496 return mIsDocumentLoad
? GetLoadingBrowsingContext() : nullptr;
499 CanonicalBrowsingContext
* DocumentLoadListener::GetTopBrowsingContext() const {
500 auto* loadingContext
= GetLoadingBrowsingContext();
501 return loadingContext
? loadingContext
->Top() : nullptr;
504 WindowGlobalParent
* DocumentLoadListener::GetParentWindowContext() const {
505 return mParentWindowContext
;
508 bool CheckRecursiveLoad(CanonicalBrowsingContext
* aLoadingContext
,
509 nsDocShellLoadState
* aLoadState
, bool aIsDocumentLoad
) {
510 // Bug 136580: Check for recursive frame loading excluding about:srcdoc URIs.
511 // srcdoc URIs require their contents to be specified inline, so it isn't
512 // possible for undesirable recursion to occur without the aid of a
513 // non-srcdoc URI, which this method will block normally.
514 // Besides, URI is not enough to guarantee uniqueness of srcdoc documents.
515 nsAutoCString buffer
;
516 if (aLoadState
->URI()->SchemeIs("about")) {
517 nsresult rv
= aLoadState
->URI()->GetPathQueryRef(buffer
);
518 if (NS_SUCCEEDED(rv
) && buffer
.EqualsLiteral("srcdoc")) {
519 // Duplicates allowed up to depth limits
524 RefPtr
<WindowGlobalParent
> parent
;
525 if (!aIsDocumentLoad
) { // object load
526 parent
= aLoadingContext
->GetCurrentWindowGlobal();
528 parent
= aLoadingContext
->GetParentWindowContext();
532 CanonicalBrowsingContext
* ancestorBC
;
533 for (WindowGlobalParent
* ancestorWGP
= parent
; ancestorWGP
;
534 ancestorWGP
= ancestorBC
->GetParentWindowContext()) {
535 ancestorBC
= ancestorWGP
->BrowsingContext();
536 MOZ_ASSERT(ancestorBC
);
537 if (nsCOMPtr
<nsIURI
> parentURI
= ancestorWGP
->GetDocumentURI()) {
539 nsresult rv
= aLoadState
->URI()->EqualsExceptRef(parentURI
, &equal
);
540 NS_ENSURE_SUCCESS(rv
, false);
544 if (matchCount
>= kMaxSameURLContentFrames
) {
546 "Too many nested content frames/objects have the same url "
557 // Check that the load state, potentially received from a child process, appears
558 // to be performing a load of the specified LoadingSessionHistoryInfo.
559 // Returns a Result<…> containing the SessionHistoryEntry found for the
560 // LoadingSessionHistoryInfo as success value if the validation succeeded, or a
561 // static (telemetry-safe) string naming what did not match as a failure value
562 // if the validation failed.
563 static Result
<SessionHistoryEntry
*, const char*> ValidateHistoryLoad(
564 CanonicalBrowsingContext
* aLoadingContext
,
565 nsDocShellLoadState
* aLoadState
) {
566 MOZ_ASSERT(SessionHistoryInParent());
567 MOZ_ASSERT(aLoadState
->LoadIsFromSessionHistory());
569 if (!aLoadState
->GetLoadingSessionHistoryInfo()) {
570 return Err("Missing LoadingSessionHistoryInfo");
573 SessionHistoryEntry::LoadingEntry
* loading
= SessionHistoryEntry::GetByLoadId(
574 aLoadState
->GetLoadingSessionHistoryInfo()->mLoadId
);
576 return Err("Missing SessionHistoryEntry");
579 SessionHistoryInfo
* snapshot
= loading
->mInfoSnapshotForValidation
.get();
580 // History loads do not inherit principal.
581 if (aLoadState
->HasInternalLoadFlags(
582 nsDocShell::INTERNAL_LOAD_FLAGS_INHERIT_PRINCIPAL
)) {
583 return Err("LOAD_FLAGS_INHERIT_PRINCIPAL");
586 auto uriEq
= [](nsIURI
* a
, nsIURI
* b
) -> bool {
588 return a
== b
|| (a
&& b
&& NS_SUCCEEDED(a
->Equals(b
, &eq
)) && eq
);
590 auto principalEq
= [](nsIPrincipal
* a
, nsIPrincipal
* b
) -> bool {
591 return a
== b
|| (a
&& b
&& a
->Equals(b
));
594 // XXX: Needing to do all of this validation manually is kinda gross.
595 if (!uriEq(snapshot
->GetURI(), aLoadState
->URI())) {
598 if (!uriEq(snapshot
->GetOriginalURI(), aLoadState
->OriginalURI())) {
599 return Err("OriginalURI");
601 if (!aLoadState
->ResultPrincipalURIIsSome() ||
602 !uriEq(snapshot
->GetResultPrincipalURI(),
603 aLoadState
->ResultPrincipalURI())) {
604 return Err("ResultPrincipalURI");
606 if (!uriEq(snapshot
->GetUnstrippedURI(), aLoadState
->GetUnstrippedURI())) {
607 return Err("UnstrippedURI");
609 if (!principalEq(snapshot
->GetTriggeringPrincipal(),
610 aLoadState
->TriggeringPrincipal())) {
611 return Err("TriggeringPrincipal");
613 if (!principalEq(snapshot
->GetPrincipalToInherit(),
614 aLoadState
->PrincipalToInherit())) {
615 return Err("PrincipalToInherit");
617 if (!principalEq(snapshot
->GetPartitionedPrincipalToInherit(),
618 aLoadState
->PartitionedPrincipalToInherit())) {
619 return Err("PartitionedPrincipalToInherit");
622 // Everything matches!
623 return loading
->mEntry
;
626 auto DocumentLoadListener::Open(nsDocShellLoadState
* aLoadState
,
627 LoadInfo
* aLoadInfo
, nsLoadFlags aLoadFlags
,
629 const Maybe
<uint64_t>& aChannelId
,
630 const TimeStamp
& aAsyncOpenTime
,
631 nsDOMNavigationTiming
* aTiming
,
632 Maybe
<ClientInfo
>&& aInfo
, bool aUrgentStart
,
633 dom::ContentParent
* aContentParent
,
634 nsresult
* aRv
) -> RefPtr
<OpenPromise
> {
635 auto* loadingContext
= GetLoadingBrowsingContext();
637 MOZ_DIAGNOSTIC_ASSERT_IF(loadingContext
->GetParent(),
638 loadingContext
->GetParentWindowContext());
640 OriginAttributes attrs
;
641 loadingContext
->GetOriginAttributes(attrs
);
643 mLoadIdentifier
= aLoadState
->GetLoadIdentifier();
644 // See description of mFileName in nsDocShellLoadState.h
645 mIsDownload
= !aLoadState
->FileName().IsVoid();
646 mIsLoadingJSURI
= net::SchemeIsJavascript(aLoadState
->URI());
648 // Check for infinite recursive object or iframe loads
649 if (aLoadState
->OriginalFrameSrc() || !mIsDocumentLoad
) {
650 if (!CheckRecursiveLoad(loadingContext
, aLoadState
, mIsDocumentLoad
)) {
651 *aRv
= NS_ERROR_RECURSIVE_DOCUMENT_LOAD
;
652 mParentChannelListener
= nullptr;
657 auto* documentContext
= GetDocumentBrowsingContext();
659 // If we are using SHIP and this load is from session history, validate that
660 // the load matches our local copy of the loading history entry.
662 // NOTE: Keep this check in-sync with the check in
663 // `nsDocShellLoadState::GetEffectiveTriggeringRemoteType()`!
664 RefPtr
<SessionHistoryEntry
> existingEntry
;
665 if (SessionHistoryInParent() && aLoadState
->LoadIsFromSessionHistory() &&
666 aLoadState
->LoadType() != LOAD_ERROR_PAGE
) {
667 Result
<SessionHistoryEntry
*, const char*> result
=
668 ValidateHistoryLoad(loadingContext
, aLoadState
);
669 if (result
.isErr()) {
670 const char* mismatch
= result
.unwrapErr();
672 ("DocumentLoadListener::Open with invalid loading history entry "
673 "[this=%p, mismatch=%s]",
675 #ifdef MOZ_DIAGNOSTIC_ASSERT_ENABLED
676 MOZ_CRASH_UNSAFE_PRINTF(
677 "DocumentLoadListener::Open for invalid history entry due to "
681 *aRv
= NS_ERROR_DOM_SECURITY_ERR
;
682 mParentChannelListener
= nullptr;
686 existingEntry
= result
.unwrap();
687 if (!existingEntry
->IsInSessionHistory() &&
688 !documentContext
->HasLoadingHistoryEntry(existingEntry
)) {
689 SessionHistoryEntry::RemoveLoadId(
690 aLoadState
->GetLoadingSessionHistoryInfo()->mLoadId
);
692 ("DocumentLoadListener::Open with disconnected history entry "
696 *aRv
= NS_BINDING_ABORTED
;
697 mParentChannelListener
= nullptr;
703 if (aLoadState
->GetRemoteTypeOverride()) {
704 if (!mIsDocumentLoad
|| !NS_IsAboutBlank(aLoadState
->URI()) ||
705 !loadingContext
->IsTopContent()) {
707 ("DocumentLoadListener::Open with invalid remoteTypeOverride "
710 *aRv
= NS_ERROR_DOM_SECURITY_ERR
;
711 mParentChannelListener
= nullptr;
715 mRemoteTypeOverride
= aLoadState
->GetRemoteTypeOverride();
718 if (NS_WARN_IF(!loadingContext
->IsOwnedByProcess(
719 GetContentProcessId(aContentParent
)))) {
721 ("DocumentLoadListener::Open called from non-current content process "
722 "[this=%p, current=%" PRIu64
", caller=%" PRIu64
"]",
723 this, loadingContext
->OwnerProcessId(),
724 uint64_t(GetContentProcessId(aContentParent
))));
725 *aRv
= NS_BINDING_ABORTED
;
726 mParentChannelListener
= nullptr;
730 if (mIsDocumentLoad
&& loadingContext
->IsContent() &&
731 NS_WARN_IF(loadingContext
->IsReplaced())) {
733 ("DocumentLoadListener::Open called from replaced BrowsingContext "
734 "[this=%p, browserid=%" PRIx64
", bcid=%" PRIx64
"]",
735 this, loadingContext
->BrowserId(), loadingContext
->Id()));
736 *aRv
= NS_BINDING_ABORTED
;
737 mParentChannelListener
= nullptr;
741 if (!nsDocShell::CreateAndConfigureRealChannelForLoadState(
742 loadingContext
, aLoadState
, aLoadInfo
, mParentChannelListener
,
743 nullptr, attrs
, aLoadFlags
, aCacheKey
, *aRv
,
744 getter_AddRefs(mChannel
))) {
745 LOG(("DocumentLoadListener::Open failed to create channel [this=%p]",
747 mParentChannelListener
= nullptr;
751 if (documentContext
&& aLoadState
->LoadType() != LOAD_ERROR_PAGE
&&
752 mozilla::SessionHistoryInParent()) {
753 // It's hard to know at this point whether session history will be enabled
754 // in the browsing context, so we always create an entry for a load here.
755 mLoadingSessionHistoryInfo
=
756 documentContext
->CreateLoadingSessionHistoryEntryForLoad(
757 aLoadState
, existingEntry
, mChannel
);
758 MOZ_ASSERT(mLoadingSessionHistoryInfo
);
761 nsCOMPtr
<nsIURI
> uriBeingLoaded
;
762 Unused
<< NS_WARN_IF(
763 NS_FAILED(mChannel
->GetURI(getter_AddRefs(uriBeingLoaded
))));
765 RefPtr
<HttpBaseChannel
> httpBaseChannel
= do_QueryObject(mChannel
, aRv
);
766 if (uriBeingLoaded
&& httpBaseChannel
) {
767 nsCOMPtr
<nsIURI
> topWindowURI
;
768 if (mIsDocumentLoad
&& loadingContext
->IsTop()) {
769 // If this is for the top level loading, the top window URI should be the
770 // URI which we are loading.
771 topWindowURI
= uriBeingLoaded
;
772 } else if (RefPtr
<WindowGlobalParent
> topWindow
= AntiTrackingUtils::
773 GetTopWindowExcludingExtensionAccessibleContentFrames(
774 loadingContext
, uriBeingLoaded
)) {
775 nsCOMPtr
<nsIPrincipal
> topWindowPrincipal
=
776 topWindow
->DocumentPrincipal();
777 if (topWindowPrincipal
&& !topWindowPrincipal
->GetIsNullPrincipal()) {
778 auto* basePrin
= BasePrincipal::Cast(topWindowPrincipal
);
779 basePrin
->GetURI(getter_AddRefs(topWindowURI
));
782 httpBaseChannel
->SetTopWindowURI(topWindowURI
);
785 nsCOMPtr
<nsIIdentChannel
> identChannel
= do_QueryInterface(mChannel
);
786 if (identChannel
&& aChannelId
) {
787 Unused
<< identChannel
->SetChannelId(*aChannelId
);
789 mDocumentChannelId
= aChannelId
;
791 RefPtr
<nsHttpChannel
> httpChannelImpl
= do_QueryObject(mChannel
);
792 if (httpChannelImpl
) {
793 httpChannelImpl
->SetWarningReporter(this);
795 if (mIsDocumentLoad
&& loadingContext
->IsTop()) {
796 httpChannelImpl
->SetEarlyHintObserver(this);
800 nsCOMPtr
<nsITimedChannel
> timedChannel
= do_QueryInterface(mChannel
);
802 timedChannel
->SetAsyncOpen(aAsyncOpenTime
);
805 if (nsCOMPtr
<nsIHttpChannel
> httpChannel
= do_QueryInterface(mChannel
)) {
806 Unused
<< httpChannel
->SetRequestContextID(
807 loadingContext
->GetRequestContextId());
809 nsCOMPtr
<nsIClassOfService
> cos(do_QueryInterface(httpChannel
));
810 if (cos
&& aUrgentStart
) {
811 cos
->AddClassFlags(nsIClassOfService::UrgentStart
);
815 // Setup a ClientChannelHelper to watch for redirects, and copy
816 // across any serviceworker related data between channels as needed.
817 AddClientChannelHelperInParent(mChannel
, std::move(aInfo
));
819 if (documentContext
&& !documentContext
->StartDocumentLoad(this)) {
820 LOG(("DocumentLoadListener::Open failed StartDocumentLoad [this=%p]",
822 *aRv
= NS_BINDING_ABORTED
;
823 mParentChannelListener
= nullptr;
828 // Recalculate the openFlags, matching the logic in use in Content process.
829 MOZ_ASSERT(!aLoadState
->GetPendingRedirectedChannel());
830 uint32_t openFlags
= nsDocShell::ComputeURILoaderFlags(
831 loadingContext
, aLoadState
->LoadType(), mIsDocumentLoad
);
833 RefPtr
<ParentProcessDocumentOpenInfo
> openInfo
=
834 new ParentProcessDocumentOpenInfo(mParentChannelListener
, openFlags
,
835 loadingContext
, mIsDocumentLoad
);
839 RefPtr
<MozPromise
<bool, bool, false>> promise
;
840 if (documentContext
&& aLoadState
->LoadType() != LOAD_ERROR_PAGE
&&
841 !(aLoadState
->HasInternalLoadFlags(
842 nsDocShell::INTERNAL_LOAD_FLAGS_BYPASS_LOAD_URI_DELEGATE
)) &&
843 !(aLoadState
->LoadType() & LOAD_HISTORY
)) {
844 nsCOMPtr
<nsIWidget
> widget
=
845 documentContext
->GetParentProcessWidgetContaining();
846 RefPtr
<nsWindow
> window
= nsWindow::From(widget
);
849 promise
= window
->OnLoadRequest(
850 aLoadState
->URI(), nsIBrowserDOMWindow::OPEN_CURRENTWINDOW
,
851 aLoadState
->InternalLoadFlags(), aLoadState
->TriggeringPrincipal(),
852 aLoadState
->HasValidUserGestureActivation(),
853 documentContext
->IsTopContent());
858 RefPtr
<DocumentLoadListener
> self
= this;
860 GetCurrentSerialEventTarget(), __func__
,
861 [=](const MozPromise
<bool, bool, false>::ResolveOrRejectValue
& aValue
) {
862 if (aValue
.IsResolve()) {
863 bool handled
= aValue
.ResolveValue();
865 self
->DisconnectListeners(NS_ERROR_ABORT
, NS_ERROR_ABORT
);
866 mParentChannelListener
= nullptr;
868 nsresult rv
= mChannel
->AsyncOpen(openInfo
);
870 self
->DisconnectListeners(rv
, rv
);
871 mParentChannelListener
= nullptr;
879 *aRv
= mChannel
->AsyncOpen(openInfo
);
880 if (NS_FAILED(*aRv
)) {
881 LOG(("DocumentLoadListener::Open failed AsyncOpen [this=%p rv=%" PRIx32
883 this, static_cast<uint32_t>(*aRv
)));
884 if (documentContext
) {
885 documentContext
->EndDocumentLoad(false);
887 mParentChannelListener
= nullptr;
892 // HTTPS-Only Mode fights potential timeouts caused by upgrades. Instantly
893 // after opening the document channel we have to kick off countermeasures.
894 nsHTTPSOnlyUtils::PotentiallyFireHttpRequestToShortenTimout(this);
896 nsCOMPtr
<nsILoadInfo
> loadInfo
= mChannel
->LoadInfo();
897 loadInfo
->SetChannelCreationOriginalURI(aLoadState
->URI());
899 mContentParent
= aContentParent
;
900 mLoadStateExternalLoadFlags
= aLoadState
->LoadFlags();
901 mLoadStateInternalLoadFlags
= aLoadState
->InternalLoadFlags();
902 mLoadStateLoadType
= aLoadState
->LoadType();
904 mSrcdocData
= aLoadState
->SrcdocData();
905 mBaseURI
= aLoadState
->BaseURI();
906 mOriginalUriString
= aLoadState
->GetOriginalURIString();
907 if (documentContext
) {
908 mParentWindowContext
= documentContext
->GetParentWindowContext();
910 mParentWindowContext
=
911 WindowGlobalParent::GetByInnerWindowId(aLoadInfo
->GetInnerWindowID());
912 MOZ_RELEASE_ASSERT(mParentWindowContext
->GetBrowsingContext() ==
913 GetLoadingBrowsingContext(),
914 "mismatched parent window context?");
917 // For content-initiated loads, this flag is set in nsDocShell::LoadURI.
918 // For parent-initiated loads, we have to set it here.
919 // Below comment is copied from nsDocShell::LoadURI -
920 // If we have a system triggering principal, we can assume that this load was
921 // triggered by some UI in the browser chrome, such as the URL bar or
922 // bookmark bar. This should count as a user interaction for the current sh
923 // entry, so that the user may navigate back to the current entry, from the
924 // entry that is going to be added as part of this load.
925 if (!mSupportsRedirectToRealChannel
&& aLoadState
->TriggeringPrincipal() &&
926 aLoadState
->TriggeringPrincipal()->IsSystemPrincipal()) {
927 WindowContext
* topWc
= loadingContext
->GetTopWindowContext();
928 if (topWc
&& !topWc
->IsDiscarded()) {
929 MOZ_ALWAYS_SUCCEEDS(topWc
->SetSHEntryHasUserInteraction(true));
934 mOpenPromise
= new OpenPromise::Private(__func__
);
935 // We make the promise use direct task dispatch in order to reduce the number
936 // of event loops iterations.
937 mOpenPromise
->UseDirectTaskDispatch(__func__
);
941 auto DocumentLoadListener::OpenDocument(
942 nsDocShellLoadState
* aLoadState
, uint32_t aCacheKey
,
943 const Maybe
<uint64_t>& aChannelId
, const TimeStamp
& aAsyncOpenTime
,
944 nsDOMNavigationTiming
* aTiming
, Maybe
<dom::ClientInfo
>&& aInfo
,
945 Maybe
<bool> aUriModified
, Maybe
<bool> aIsEmbeddingBlockedError
,
946 dom::ContentParent
* aContentParent
, nsresult
* aRv
) -> RefPtr
<OpenPromise
> {
947 LOG(("DocumentLoadListener [%p] OpenDocument [uri=%s]", this,
948 aLoadState
->URI()->GetSpecOrDefault().get()));
950 MOZ_ASSERT(mIsDocumentLoad
);
952 RefPtr
<CanonicalBrowsingContext
> browsingContext
=
953 GetDocumentBrowsingContext();
955 // If this is a top-level load, then rebuild the LoadInfo from scratch,
956 // since the goal is to be able to initiate loads in the parent, where the
957 // content process won't have provided us with an existing one.
958 RefPtr
<LoadInfo
> loadInfo
=
959 CreateDocumentLoadInfo(browsingContext
, aLoadState
);
961 nsLoadFlags loadFlags
= aLoadState
->CalculateChannelLoadFlags(
962 browsingContext
, std::move(aUriModified
),
963 std::move(aIsEmbeddingBlockedError
));
965 // Keep track of navigation for the Bounce Tracking Protection.
966 if (browsingContext
->IsTopContent()) {
967 RefPtr
<BounceTrackingState
> bounceTrackingState
=
968 browsingContext
->GetBounceTrackingState();
970 // Not every browsing context has a BounceTrackingState. It's also null when
971 // the feature is disabled.
972 if (bounceTrackingState
) {
973 nsCOMPtr
<nsIPrincipal
> triggeringPrincipal
;
975 loadInfo
->GetTriggeringPrincipal(getter_AddRefs(triggeringPrincipal
));
977 if (!NS_WARN_IF(NS_FAILED(rv
))) {
978 DebugOnly
<nsresult
> rv
= bounceTrackingState
->OnStartNavigation(
979 triggeringPrincipal
, loadInfo
->GetHasValidUserGestureActivation());
980 NS_WARNING_ASSERTION(NS_SUCCEEDED(rv
),
981 "BounceTrackingState::OnStartNavigation failed");
986 return Open(aLoadState
, loadInfo
, loadFlags
, aCacheKey
, aChannelId
,
987 aAsyncOpenTime
, aTiming
, std::move(aInfo
), false, aContentParent
,
991 auto DocumentLoadListener::OpenObject(
992 nsDocShellLoadState
* aLoadState
, uint32_t aCacheKey
,
993 const Maybe
<uint64_t>& aChannelId
, const TimeStamp
& aAsyncOpenTime
,
994 nsDOMNavigationTiming
* aTiming
, Maybe
<dom::ClientInfo
>&& aInfo
,
995 uint64_t aInnerWindowId
, nsLoadFlags aLoadFlags
,
996 nsContentPolicyType aContentPolicyType
, bool aUrgentStart
,
997 dom::ContentParent
* aContentParent
,
998 ObjectUpgradeHandler
* aObjectUpgradeHandler
, nsresult
* aRv
)
999 -> RefPtr
<OpenPromise
> {
1000 LOG(("DocumentLoadListener [%p] OpenObject [uri=%s]", this,
1001 aLoadState
->URI()->GetSpecOrDefault().get()));
1003 MOZ_ASSERT(!mIsDocumentLoad
);
1005 auto sandboxFlags
= aLoadState
->TriggeringSandboxFlags();
1007 RefPtr
<LoadInfo
> loadInfo
= CreateObjectLoadInfo(
1008 aLoadState
, aInnerWindowId
, aContentPolicyType
, sandboxFlags
);
1010 mObjectUpgradeHandler
= aObjectUpgradeHandler
;
1012 return Open(aLoadState
, loadInfo
, aLoadFlags
, aCacheKey
, aChannelId
,
1013 aAsyncOpenTime
, aTiming
, std::move(aInfo
), aUrgentStart
,
1014 aContentParent
, aRv
);
1017 auto DocumentLoadListener::OpenInParent(nsDocShellLoadState
* aLoadState
,
1018 bool aSupportsRedirectToRealChannel
)
1019 -> RefPtr
<OpenPromise
> {
1020 MOZ_ASSERT(mIsDocumentLoad
);
1022 // We currently only support passing nullptr for aLoadInfo for
1023 // top level browsing contexts.
1024 auto* browsingContext
= GetDocumentBrowsingContext();
1025 if (!browsingContext
->IsTopContent() ||
1026 !browsingContext
->GetContentParent()) {
1027 LOG(("DocumentLoadListener::OpenInParent failed because of subdoc"));
1031 // Clone because this mutates the load flags in the load state, which
1032 // breaks nsDocShells expectations of being able to do it.
1033 RefPtr
<nsDocShellLoadState
> loadState
= new nsDocShellLoadState(*aLoadState
);
1034 loadState
->CalculateLoadURIFlags();
1036 RefPtr
<nsDOMNavigationTiming
> timing
= new nsDOMNavigationTiming(nullptr);
1037 timing
->NotifyNavigationStart(
1038 browsingContext
->IsActive()
1039 ? nsDOMNavigationTiming::DocShellState::eActive
1040 : nsDOMNavigationTiming::DocShellState::eInactive
);
1042 const mozilla::dom::LoadingSessionHistoryInfo
* loadingInfo
=
1043 loadState
->GetLoadingSessionHistoryInfo();
1045 uint32_t cacheKey
= 0;
1046 auto loadType
= aLoadState
->LoadType();
1047 if (loadType
== LOAD_HISTORY
|| loadType
== LOAD_RELOAD_NORMAL
||
1048 loadType
== LOAD_RELOAD_CHARSET_CHANGE
||
1049 loadType
== LOAD_RELOAD_CHARSET_CHANGE_BYPASS_CACHE
||
1050 loadType
== LOAD_RELOAD_CHARSET_CHANGE_BYPASS_PROXY_AND_CACHE
) {
1052 cacheKey
= loadingInfo
->mInfo
.GetCacheKey();
1056 // Loads start in the content process might have exposed a channel id to
1057 // observers, so we need to preserve the value in the parent. That can't have
1058 // happened here, so Nothing() is fine.
1059 Maybe
<uint64_t> channelId
= Nothing();
1061 // Initial client info is only relevant for subdocument loads, which we're
1062 // not supporting yet.
1063 Maybe
<dom::ClientInfo
> initialClientInfo
;
1065 mSupportsRedirectToRealChannel
= aSupportsRedirectToRealChannel
;
1067 // This is a top-level load, so rebuild the LoadInfo from scratch,
1068 // since in the parent the
1069 // content process won't have provided us with an existing one.
1070 RefPtr
<LoadInfo
> loadInfo
=
1071 CreateDocumentLoadInfo(browsingContext
, aLoadState
);
1073 nsLoadFlags loadFlags
= loadState
->CalculateChannelLoadFlags(
1075 Some(loadingInfo
&& loadingInfo
->mInfo
.GetURIWasModified()), Nothing());
1078 return Open(loadState
, loadInfo
, loadFlags
, cacheKey
, channelId
,
1079 TimeStamp::Now(), timing
, std::move(initialClientInfo
), false,
1080 browsingContext
->GetContentParent(), &rv
);
1083 base::ProcessId
DocumentLoadListener::OtherPid() const {
1084 return mContentParent
? mContentParent
->OtherPid() : base::ProcessId
{0};
1087 void DocumentLoadListener::FireStateChange(uint32_t aStateFlags
,
1089 nsCOMPtr
<nsIChannel
> request
= GetChannel();
1091 RefPtr
<BrowsingContextWebProgress
> webProgress
=
1092 GetLoadingBrowsingContext()->GetWebProgress();
1095 NS_DispatchToMainThread(
1096 NS_NewRunnableFunction("DocumentLoadListener::FireStateChange", [=]() {
1097 webProgress
->OnStateChange(webProgress
, request
, aStateFlags
,
1103 static void SetNavigating(CanonicalBrowsingContext
* aBrowsingContext
,
1105 nsCOMPtr
<nsIBrowser
> browser
;
1106 if (RefPtr
<Element
> currentElement
= aBrowsingContext
->GetEmbedderElement()) {
1107 browser
= currentElement
->AsBrowser();
1114 NS_DispatchToMainThread(NS_NewRunnableFunction(
1115 "DocumentLoadListener::SetNavigating",
1116 [browser
, aNavigating
]() { browser
->SetIsNavigating(aNavigating
); }));
1119 /* static */ bool DocumentLoadListener::LoadInParent(
1120 CanonicalBrowsingContext
* aBrowsingContext
, nsDocShellLoadState
* aLoadState
,
1121 bool aSetNavigating
) {
1122 SetNavigating(aBrowsingContext
, aSetNavigating
);
1124 RefPtr
<DocumentLoadListener
> load
=
1125 new DocumentLoadListener(aBrowsingContext
, true);
1126 RefPtr
<DocumentLoadListener::OpenPromise
> promise
= load
->OpenInParent(
1127 aLoadState
, /* aSupportsRedirectToRealChannel */ false);
1129 SetNavigating(aBrowsingContext
, false);
1133 // We passed false for aSupportsRedirectToRealChannel, so we should always
1134 // take the process switching path, and reject this promise.
1136 GetCurrentSerialEventTarget(), __func__
,
1137 [load
](DocumentLoadListener::OpenPromise::ResolveOrRejectValue
&& aValue
) {
1138 MOZ_ASSERT(aValue
.IsReject());
1139 DocumentLoadListener::OpenPromiseFailedType
& rejectValue
=
1140 aValue
.RejectValue();
1141 if (!rejectValue
.mContinueNavigating
) {
1142 // If we're not switching the load to a new process, then it is
1143 // finished (and failed), and we should fire a state change to notify
1144 // observers. Normally the docshell would fire this, and it would get
1145 // filtered out by BrowserParent if needed.
1146 load
->FireStateChange(nsIWebProgressListener::STATE_STOP
|
1147 nsIWebProgressListener::STATE_IS_WINDOW
|
1148 nsIWebProgressListener::STATE_IS_NETWORK
,
1149 rejectValue
.mStatus
);
1153 load
->FireStateChange(nsIWebProgressListener::STATE_START
|
1154 nsIWebProgressListener::STATE_IS_DOCUMENT
|
1155 nsIWebProgressListener::STATE_IS_REQUEST
|
1156 nsIWebProgressListener::STATE_IS_WINDOW
|
1157 nsIWebProgressListener::STATE_IS_NETWORK
,
1159 SetNavigating(aBrowsingContext
, false);
1164 bool DocumentLoadListener::SpeculativeLoadInParent(
1165 dom::CanonicalBrowsingContext
* aBrowsingContext
,
1166 nsDocShellLoadState
* aLoadState
) {
1167 LOG(("DocumentLoadListener::OpenFromParent"));
1169 RefPtr
<DocumentLoadListener
> listener
=
1170 new DocumentLoadListener(aBrowsingContext
, true);
1172 auto promise
= listener
->OpenInParent(aLoadState
, true);
1174 // Create an entry in the redirect channel registrar to
1175 // allocate an identifier for this load.
1176 nsCOMPtr
<nsIRedirectChannelRegistrar
> registrar
=
1177 RedirectChannelRegistrar::GetOrCreate();
1178 uint64_t loadIdentifier
= aLoadState
->GetLoadIdentifier();
1179 DebugOnly
<nsresult
> rv
=
1180 registrar
->RegisterChannel(nullptr, loadIdentifier
);
1181 MOZ_ASSERT(NS_SUCCEEDED(rv
));
1182 // Register listener (as an nsIParentChannel) under our new identifier.
1183 rv
= registrar
->LinkChannels(loadIdentifier
, listener
, nullptr);
1184 MOZ_ASSERT(NS_SUCCEEDED(rv
));
1189 void DocumentLoadListener::CleanupParentLoadAttempt(uint64_t aLoadIdent
) {
1190 nsCOMPtr
<nsIRedirectChannelRegistrar
> registrar
=
1191 RedirectChannelRegistrar::GetOrCreate();
1193 nsCOMPtr
<nsIParentChannel
> parentChannel
;
1194 registrar
->GetParentChannel(aLoadIdent
, getter_AddRefs(parentChannel
));
1195 RefPtr
<DocumentLoadListener
> loadListener
= do_QueryObject(parentChannel
);
1198 // If the load listener is still registered, then we must have failed
1199 // to connect DocumentChannel into it. Better cancel it!
1200 loadListener
->NotifyDocumentChannelFailed();
1203 registrar
->DeregisterChannels(aLoadIdent
);
1206 auto DocumentLoadListener::ClaimParentLoad(DocumentLoadListener
** aListener
,
1207 uint64_t aLoadIdent
,
1208 Maybe
<uint64_t> aChannelId
)
1209 -> RefPtr
<OpenPromise
> {
1210 nsCOMPtr
<nsIRedirectChannelRegistrar
> registrar
=
1211 RedirectChannelRegistrar::GetOrCreate();
1213 nsCOMPtr
<nsIParentChannel
> parentChannel
;
1214 registrar
->GetParentChannel(aLoadIdent
, getter_AddRefs(parentChannel
));
1215 RefPtr
<DocumentLoadListener
> loadListener
= do_QueryObject(parentChannel
);
1216 registrar
->DeregisterChannels(aLoadIdent
);
1218 if (!loadListener
) {
1219 // The parent went away unexpectedly.
1220 *aListener
= nullptr;
1224 loadListener
->mDocumentChannelId
= aChannelId
;
1226 MOZ_DIAGNOSTIC_ASSERT(loadListener
->mOpenPromise
);
1227 loadListener
.forget(aListener
);
1229 return (*aListener
)->mOpenPromise
;
1232 void DocumentLoadListener::NotifyDocumentChannelFailed() {
1233 LOG(("DocumentLoadListener NotifyDocumentChannelFailed [this=%p]", this));
1234 // There's been no calls to ClaimParentLoad, and so no listeners have been
1235 // attached to mOpenPromise yet. As such we can run Then() on it.
1237 GetMainThreadSerialEventTarget(), __func__
,
1238 [](DocumentLoadListener::OpenPromiseSucceededType
&& aResolveValue
) {
1239 aResolveValue
.mPromise
->Resolve(NS_BINDING_ABORTED
, __func__
);
1243 Cancel(NS_BINDING_ABORTED
,
1244 "DocumentLoadListener::NotifyDocumentChannelFailed"_ns
);
1247 void DocumentLoadListener::Disconnect(bool aContinueNavigating
) {
1248 LOG(("DocumentLoadListener Disconnect [this=%p, aContinueNavigating=%d]",
1249 this, aContinueNavigating
));
1250 // The nsHttpChannel may have a reference to this parent, release it
1251 // to avoid circular references.
1252 RefPtr
<nsHttpChannel
> httpChannelImpl
= do_QueryObject(mChannel
);
1253 if (httpChannelImpl
) {
1254 httpChannelImpl
->SetWarningReporter(nullptr);
1255 httpChannelImpl
->SetEarlyHintObserver(nullptr);
1258 // Don't cancel ongoing early hints when continuing to load the web page.
1259 // Early hints are loaded earlier in the code and shouldn't get cancelled
1260 // here. See also: Bug 1765652
1261 if (!aContinueNavigating
) {
1262 mEarlyHintsService
.Cancel("DocumentLoadListener::Disconnect"_ns
);
1265 if (auto* ctx
= GetDocumentBrowsingContext()) {
1266 ctx
->EndDocumentLoad(aContinueNavigating
);
1270 void DocumentLoadListener::Cancel(const nsresult
& aStatusCode
,
1271 const nsACString
& aReason
) {
1273 ("DocumentLoadListener Cancel [this=%p, "
1274 "aStatusCode=%" PRIx32
" ]",
1275 this, static_cast<uint32_t>(aStatusCode
)));
1276 if (mOpenPromiseResolved
) {
1280 mChannel
->CancelWithReason(aStatusCode
, aReason
);
1283 DisconnectListeners(aStatusCode
, aStatusCode
);
1286 void DocumentLoadListener::DisconnectListeners(nsresult aStatus
,
1287 nsresult aLoadGroupStatus
,
1288 bool aContinueNavigating
) {
1290 ("DocumentLoadListener DisconnectListener [this=%p, "
1291 "aStatus=%" PRIx32
", aLoadGroupStatus=%" PRIx32
1292 ", aContinueNavigating=%d]",
1293 this, static_cast<uint32_t>(aStatus
),
1294 static_cast<uint32_t>(aLoadGroupStatus
), aContinueNavigating
));
1296 RejectOpenPromise(aStatus
, aLoadGroupStatus
, aContinueNavigating
, __func__
);
1298 Disconnect(aContinueNavigating
);
1300 // Clear any pending stream filter requests. If we're going to be sending a
1301 // response to the content process due to a navigation, our caller will have
1302 // already stashed the array to be passed to `TriggerRedirectToRealChannel`,
1303 // so it's safe for us to clear here.
1304 // TODO: If we retargeted the stream to a non-default handler (e.g. to trigger
1305 // a download), we currently never attach a stream filter. Should we attach a
1306 // stream filter in those situations as well?
1307 mStreamFilterRequests
.Clear();
1310 void DocumentLoadListener::RedirectToRealChannelFinished(nsresult aRv
) {
1312 ("DocumentLoadListener RedirectToRealChannelFinished [this=%p, "
1313 "aRv=%" PRIx32
" ]",
1314 this, static_cast<uint32_t>(aRv
)));
1315 if (NS_FAILED(aRv
)) {
1316 FinishReplacementChannelSetup(aRv
);
1320 // Wait for background channel ready on target channel
1321 nsCOMPtr
<nsIRedirectChannelRegistrar
> redirectReg
=
1322 RedirectChannelRegistrar::GetOrCreate();
1323 MOZ_ASSERT(redirectReg
);
1325 nsCOMPtr
<nsIParentChannel
> redirectParentChannel
;
1326 redirectReg
->GetParentChannel(mRedirectChannelId
,
1327 getter_AddRefs(redirectParentChannel
));
1328 if (!redirectParentChannel
) {
1329 FinishReplacementChannelSetup(NS_ERROR_FAILURE
);
1333 nsCOMPtr
<nsIParentRedirectingChannel
> redirectingParent
=
1334 do_QueryInterface(redirectParentChannel
);
1335 if (!redirectingParent
) {
1336 // Continue verification procedure if redirecting to non-Http protocol
1337 FinishReplacementChannelSetup(NS_OK
);
1341 // Ask redirected channel if verification can proceed.
1342 // ReadyToVerify will be invoked when redirected channel is ready.
1343 redirectingParent
->ContinueVerification(this);
1347 DocumentLoadListener::ReadyToVerify(nsresult aResultCode
) {
1348 FinishReplacementChannelSetup(aResultCode
);
1352 void DocumentLoadListener::FinishReplacementChannelSetup(nsresult aResult
) {
1354 ("DocumentLoadListener FinishReplacementChannelSetup [this=%p, "
1356 this, int(aResult
)));
1358 auto endDocumentLoad
= MakeScopeExit([&]() {
1359 if (auto* ctx
= GetDocumentBrowsingContext()) {
1360 ctx
->EndDocumentLoad(false);
1363 mStreamFilterRequests
.Clear();
1365 nsCOMPtr
<nsIRedirectChannelRegistrar
> registrar
=
1366 RedirectChannelRegistrar::GetOrCreate();
1367 MOZ_ASSERT(registrar
);
1369 nsCOMPtr
<nsIParentChannel
> redirectChannel
;
1370 nsresult rv
= registrar
->GetParentChannel(mRedirectChannelId
,
1371 getter_AddRefs(redirectChannel
));
1372 if (NS_FAILED(rv
) || !redirectChannel
) {
1373 aResult
= NS_ERROR_FAILURE
;
1376 // Release all previously registered channels, they are no longer needed to
1377 // be kept in the registrar from this moment.
1378 registrar
->DeregisterChannels(mRedirectChannelId
);
1379 mRedirectChannelId
= 0;
1380 if (NS_FAILED(aResult
)) {
1381 if (redirectChannel
) {
1382 redirectChannel
->Delete();
1384 mChannel
->Cancel(aResult
);
1390 !SameCOMIdentity(redirectChannel
, static_cast<nsIParentChannel
*>(this)));
1392 redirectChannel
->SetParentListener(mParentChannelListener
);
1394 ApplyPendingFunctions(redirectChannel
);
1396 if (!ResumeSuspendedChannel(redirectChannel
)) {
1397 nsCOMPtr
<nsILoadGroup
> loadGroup
;
1398 mChannel
->GetLoadGroup(getter_AddRefs(loadGroup
));
1400 // We added ourselves to the load group, but attempting
1401 // to resume has notified us that the channel is already
1402 // finished. Better remove ourselves from the loadgroup
1403 // again. The only time the channel will be in a loadgroup
1404 // is if we're connected to the parent process.
1405 nsresult status
= NS_OK
;
1406 mChannel
->GetStatus(&status
);
1407 loadGroup
->RemoveRequest(mChannel
, nullptr, status
);
1412 void DocumentLoadListener::ApplyPendingFunctions(
1413 nsIParentChannel
* aChannel
) const {
1414 // We stored the values from all nsIParentChannel functions called since we
1415 // couldn't handle them. Copy them across to the real channel since it
1416 // should know what to do.
1418 nsCOMPtr
<nsIParentChannel
> parentChannel
= aChannel
;
1419 for (const auto& variant
: mIParentChannelFunctions
) {
1421 [parentChannel
](const ClassifierMatchedInfoParams
& aParams
) {
1422 parentChannel
->SetClassifierMatchedInfo(
1423 aParams
.mList
, aParams
.mProvider
, aParams
.mFullHash
);
1425 [parentChannel
](const ClassifierMatchedTrackingInfoParams
& aParams
) {
1426 parentChannel
->SetClassifierMatchedTrackingInfo(aParams
.mLists
,
1427 aParams
.mFullHashes
);
1429 [parentChannel
](const ClassificationFlagsParams
& aParams
) {
1430 parentChannel
->NotifyClassificationFlags(aParams
.mClassificationFlags
,
1431 aParams
.mIsThirdParty
);
1435 RefPtr
<HttpChannelSecurityWarningReporter
> reporter
;
1436 if (RefPtr
<HttpChannelParent
> httpParent
= do_QueryObject(aChannel
)) {
1437 reporter
= httpParent
;
1438 } else if (RefPtr
<nsHttpChannel
> httpChannel
= do_QueryObject(aChannel
)) {
1439 reporter
= httpChannel
->GetWarningReporter();
1442 for (const auto& variant
: mSecurityWarningFunctions
) {
1444 [reporter
](const ReportSecurityMessageParams
& aParams
) {
1445 Unused
<< reporter
->ReportSecurityMessage(aParams
.mMessageTag
,
1446 aParams
.mMessageCategory
);
1448 [reporter
](const LogBlockedCORSRequestParams
& aParams
) {
1449 Unused
<< reporter
->LogBlockedCORSRequest(
1450 aParams
.mMessage
, aParams
.mCategory
, aParams
.mIsWarning
);
1452 [reporter
](const LogMimeTypeMismatchParams
& aParams
) {
1453 Unused
<< reporter
->LogMimeTypeMismatch(
1454 aParams
.mMessageName
, aParams
.mWarning
, aParams
.mURL
,
1455 aParams
.mContentType
);
1461 bool DocumentLoadListener::ResumeSuspendedChannel(
1462 nsIStreamListener
* aListener
) {
1463 LOG(("DocumentLoadListener ResumeSuspendedChannel [this=%p]", this));
1464 RefPtr
<nsHttpChannel
> httpChannel
= do_QueryObject(mChannel
);
1466 httpChannel
->SetApplyConversion(mOldApplyConversion
);
1470 mParentChannelListener
->SetListenerAfterRedirect(aListener
);
1473 // If we failed to suspend the channel, then we might have received
1474 // some messages while the redirected was being handled.
1475 // Manually send them on now.
1476 nsTArray
<StreamListenerFunction
> streamListenerFunctions
=
1477 std::move(mStreamListenerFunctions
);
1479 streamListenerFunctions
.Clear();
1482 ForwardStreamListenerFunctions(std::move(streamListenerFunctions
), aListener
);
1484 // We don't expect to get new stream listener functions added
1485 // via re-entrancy. If this ever happens, we should understand
1486 // exactly why before allowing it.
1487 NS_ASSERTION(mStreamListenerFunctions
.IsEmpty(),
1488 "Should not have added new stream listener function!");
1492 // Our caller will invoke `EndDocumentLoad` for us.
1494 return !mIsFinished
;
1497 void DocumentLoadListener::CancelEarlyHintPreloads() {
1498 mEarlyHintsService
.Cancel("DocumentLoadListener::CancelEarlyHintPreloads"_ns
);
1501 void DocumentLoadListener::RegisterEarlyHintLinksAndGetConnectArgs(
1502 dom::ContentParentId aCpId
, nsTArray
<EarlyHintConnectArgs
>& aOutLinks
) {
1503 mEarlyHintsService
.RegisterLinksAndGetConnectArgs(aCpId
, aOutLinks
);
1506 void DocumentLoadListener::SerializeRedirectData(
1507 RedirectToRealChannelArgs
& aArgs
, bool aIsCrossProcess
,
1508 uint32_t aRedirectFlags
, uint32_t aLoadFlags
,
1509 nsTArray
<EarlyHintConnectArgs
>&& aEarlyHints
,
1510 uint32_t aEarlyHintLinkType
) const {
1511 aArgs
.uri() = GetChannelCreationURI();
1512 aArgs
.loadIdentifier() = mLoadIdentifier
;
1513 aArgs
.earlyHints() = std::move(aEarlyHints
);
1514 aArgs
.earlyHintLinkType() = aEarlyHintLinkType
;
1516 // I previously used HttpBaseChannel::CloneLoadInfoForRedirect, but that
1517 // clears the principal to inherit, which fails tests (probably because this
1518 // 'redirect' is usually just an implementation detail). It's also http
1519 // only, and mChannel can be anything that we redirected to.
1520 nsCOMPtr
<nsILoadInfo
> channelLoadInfo
= mChannel
->LoadInfo();
1521 nsCOMPtr
<nsIPrincipal
> principalToInherit
;
1522 channelLoadInfo
->GetPrincipalToInherit(getter_AddRefs(principalToInherit
));
1524 const RefPtr
<nsHttpChannel
> baseChannel
= do_QueryObject(mChannel
);
1526 nsCOMPtr
<nsILoadContext
> loadContext
;
1527 NS_QueryNotificationCallbacks(mChannel
, loadContext
);
1528 nsCOMPtr
<nsILoadInfo
> redirectLoadInfo
;
1530 // Only use CloneLoadInfoForRedirect if we have a load context,
1531 // since it internally tries to pull OriginAttributes from the
1532 // the load context and asserts if they don't match the load info.
1533 // We can end up without a load context if the channel has been aborted
1534 // and the callbacks have been cleared.
1535 if (baseChannel
&& loadContext
) {
1536 redirectLoadInfo
= baseChannel
->CloneLoadInfoForRedirect(
1537 aArgs
.uri(), nsIChannelEventSink::REDIRECT_INTERNAL
);
1538 redirectLoadInfo
->SetResultPrincipalURI(aArgs
.uri());
1540 // The clone process clears this, and then we fail tests..
1541 // docshell/test/mochitest/test_forceinheritprincipal_overrule_owner.html
1542 if (principalToInherit
) {
1543 redirectLoadInfo
->SetPrincipalToInherit(principalToInherit
);
1547 static_cast<mozilla::net::LoadInfo
*>(channelLoadInfo
.get())->Clone();
1549 redirectLoadInfo
->AppendRedirectHistoryEntry(mChannel
, true);
1552 const Maybe
<ClientInfo
>& reservedClientInfo
=
1553 channelLoadInfo
->GetReservedClientInfo();
1554 if (reservedClientInfo
) {
1555 redirectLoadInfo
->SetReservedClientInfo(*reservedClientInfo
);
1558 aArgs
.registrarId() = mRedirectChannelId
;
1561 // We only set the granularFingerprintingProtection field when opening http
1562 // channels. So, we mark the field as set here if the channel is not a
1563 // nsHTTPChannel to pass the assertion check for getting this field in below
1564 // LoadInfoToLoadInfoArgs() call.
1566 static_cast<mozilla::net::LoadInfo
*>(redirectLoadInfo
.get())
1567 ->MarkOverriddenFingerprintingSettingsAsSet();
1571 MOZ_ALWAYS_SUCCEEDS(
1572 ipc::LoadInfoToLoadInfoArgs(redirectLoadInfo
, &aArgs
.loadInfo()));
1574 mChannel
->GetOriginalURI(getter_AddRefs(aArgs
.originalURI()));
1576 // mChannel can be a nsHttpChannel as well as InterceptedHttpChannel so we
1577 // can't use baseChannel here.
1578 if (nsCOMPtr
<nsIHttpChannel
> httpChannel
= do_QueryInterface(mChannel
)) {
1579 MOZ_ALWAYS_SUCCEEDS(httpChannel
->GetChannelId(&aArgs
.channelId()));
1582 aArgs
.redirectMode() = nsIHttpChannelInternal::REDIRECT_MODE_FOLLOW
;
1583 nsCOMPtr
<nsIHttpChannelInternal
> httpChannelInternal
=
1584 do_QueryInterface(mChannel
);
1585 if (httpChannelInternal
) {
1586 MOZ_ALWAYS_SUCCEEDS(
1587 httpChannelInternal
->GetRedirectMode(&aArgs
.redirectMode()));
1593 ->CloneReplacementChannelConfig(
1594 true, aRedirectFlags
,
1595 HttpBaseChannel::ReplacementReason::DocumentChannel
)
1599 uint32_t contentDispositionTemp
;
1600 nsresult rv
= mChannel
->GetContentDisposition(&contentDispositionTemp
);
1601 if (NS_SUCCEEDED(rv
)) {
1602 aArgs
.contentDisposition() = Some(contentDispositionTemp
);
1605 nsString contentDispositionFilenameTemp
;
1606 rv
= mChannel
->GetContentDispositionFilename(contentDispositionFilenameTemp
);
1607 if (NS_SUCCEEDED(rv
)) {
1608 aArgs
.contentDispositionFilename() = Some(contentDispositionFilenameTemp
);
1611 SetNeedToAddURIVisit(mChannel
, false);
1613 aArgs
.newLoadFlags() = aLoadFlags
;
1614 aArgs
.redirectFlags() = aRedirectFlags
;
1615 aArgs
.properties() = do_QueryObject(mChannel
);
1616 aArgs
.srcdocData() = mSrcdocData
;
1617 aArgs
.baseUri() = mBaseURI
;
1618 aArgs
.loadStateExternalLoadFlags() = mLoadStateExternalLoadFlags
;
1619 aArgs
.loadStateInternalLoadFlags() = mLoadStateInternalLoadFlags
;
1620 aArgs
.loadStateLoadType() = mLoadStateLoadType
;
1621 aArgs
.originalUriString() = mOriginalUriString
;
1622 if (mLoadingSessionHistoryInfo
) {
1623 aArgs
.loadingSessionHistoryInfo().emplace(*mLoadingSessionHistoryInfo
);
1627 static bool IsFirstLoadInWindow(nsIChannel
* aChannel
) {
1628 if (nsCOMPtr
<nsIPropertyBag2
> props
= do_QueryInterface(aChannel
)) {
1631 props
->GetPropertyAsBool(u
"docshell.newWindowTarget"_ns
, &tmp
);
1632 return NS_SUCCEEDED(rv
) && tmp
;
1637 // Get where the document loaded by this nsIChannel should be rendered. This
1638 // will be `OPEN_CURRENTWINDOW` unless we're loading an attachment which would
1639 // normally open in an external program, but we're instead choosing to render
1641 static int32_t GetWhereToOpen(nsIChannel
* aChannel
, bool aIsDocumentLoad
) {
1642 // Ignore content disposition for loads from an object or embed element.
1643 if (!aIsDocumentLoad
) {
1644 return nsIBrowserDOMWindow::OPEN_CURRENTWINDOW
;
1647 // Always continue in the same window if we're not loading an attachment.
1648 uint32_t disposition
= nsIChannel::DISPOSITION_INLINE
;
1649 if (NS_FAILED(aChannel
->GetContentDisposition(&disposition
)) ||
1650 disposition
!= nsIChannel::DISPOSITION_ATTACHMENT
) {
1651 return nsIBrowserDOMWindow::OPEN_CURRENTWINDOW
;
1654 // If the channel is for a new window target, continue in the same window.
1655 if (IsFirstLoadInWindow(aChannel
)) {
1656 return nsIBrowserDOMWindow::OPEN_CURRENTWINDOW
;
1659 // Respect the user's preferences with browser.link.open_newwindow
1660 // FIXME: There should probably be a helper for this, as the logic is
1661 // duplicated in a few places.
1662 int32_t where
= Preferences::GetInt("browser.link.open_newwindow",
1663 nsIBrowserDOMWindow::OPEN_NEWTAB
);
1664 if (where
== nsIBrowserDOMWindow::OPEN_CURRENTWINDOW
||
1665 where
== nsIBrowserDOMWindow::OPEN_NEWWINDOW
||
1666 where
== nsIBrowserDOMWindow::OPEN_NEWTAB
) {
1669 // NOTE: nsIBrowserDOMWindow::OPEN_NEWTAB_BACKGROUND is not allowed as a pref
1671 return nsIBrowserDOMWindow::OPEN_NEWTAB
;
1674 static DocumentLoadListener::ProcessBehavior
GetProcessSwitchBehavior(
1675 Element
* aBrowserElement
) {
1676 if (aBrowserElement
->HasAttribute(u
"maychangeremoteness"_ns
)) {
1677 return DocumentLoadListener::ProcessBehavior::PROCESS_BEHAVIOR_STANDARD
;
1679 nsCOMPtr
<nsIBrowser
> browser
= aBrowserElement
->AsBrowser();
1680 bool isRemoteBrowser
= false;
1681 browser
->GetIsRemoteBrowser(&isRemoteBrowser
);
1682 if (isRemoteBrowser
) {
1683 return DocumentLoadListener::ProcessBehavior::
1684 PROCESS_BEHAVIOR_SUBFRAME_ONLY
;
1686 return DocumentLoadListener::ProcessBehavior::PROCESS_BEHAVIOR_DISABLED
;
1689 static bool ContextCanProcessSwitch(CanonicalBrowsingContext
* aBrowsingContext
,
1690 WindowGlobalParent
* aParentWindow
,
1691 bool aSwitchToNewTab
) {
1692 if (NS_WARN_IF(!aBrowsingContext
)) {
1693 MOZ_LOG(gProcessIsolationLog
, LogLevel::Warning
,
1694 ("Process Switch Abort: no browsing context"));
1697 if (!aBrowsingContext
->IsContent()) {
1698 MOZ_LOG(gProcessIsolationLog
, LogLevel::Warning
,
1699 ("Process Switch Abort: non-content browsing context"));
1703 // If we're switching into a new tab, we can skip the remaining checks, as
1704 // we're not actually changing the process of aBrowsingContext, so whether or
1705 // not it is allowed to process switch isn't relevant.
1706 if (aSwitchToNewTab
) {
1710 if (aParentWindow
&& !aBrowsingContext
->UseRemoteSubframes()) {
1711 MOZ_LOG(gProcessIsolationLog
, LogLevel::Warning
,
1712 ("Process Switch Abort: remote subframes disabled"));
1716 if (aParentWindow
&& aParentWindow
->IsInProcess()) {
1717 MOZ_LOG(gProcessIsolationLog
, LogLevel::Warning
,
1718 ("Process Switch Abort: Subframe with in-process parent"));
1722 // Determine what process switching behaviour is being requested by the root
1723 // <browser> element.
1724 Element
* browserElement
= aBrowsingContext
->Top()->GetEmbedderElement();
1725 if (!browserElement
) {
1726 MOZ_LOG(gProcessIsolationLog
, LogLevel::Warning
,
1727 ("Process Switch Abort: cannot get embedder element"));
1730 nsCOMPtr
<nsIBrowser
> browser
= browserElement
->AsBrowser();
1732 MOZ_LOG(gProcessIsolationLog
, LogLevel::Warning
,
1733 ("Process Switch Abort: not loaded within nsIBrowser"));
1737 DocumentLoadListener::ProcessBehavior processBehavior
=
1738 GetProcessSwitchBehavior(browserElement
);
1740 // Check if the process switch we're considering is disabled by the
1741 // <browser>'s process behavior.
1742 if (processBehavior
==
1743 DocumentLoadListener::ProcessBehavior::PROCESS_BEHAVIOR_DISABLED
) {
1744 MOZ_LOG(gProcessIsolationLog
, LogLevel::Warning
,
1745 ("Process Switch Abort: switch disabled by <browser>"));
1748 if (!aParentWindow
&& processBehavior
==
1749 DocumentLoadListener::ProcessBehavior::
1750 PROCESS_BEHAVIOR_SUBFRAME_ONLY
) {
1751 MOZ_LOG(gProcessIsolationLog
, LogLevel::Warning
,
1752 ("Process Switch Abort: toplevel switch disabled by <browser>"));
1759 static RefPtr
<dom::BrowsingContextCallbackReceivedPromise
> SwitchToNewTab(
1760 CanonicalBrowsingContext
* aLoadingBrowsingContext
, int32_t aWhere
) {
1761 MOZ_ASSERT(aWhere
== nsIBrowserDOMWindow::OPEN_NEWTAB
||
1762 aWhere
== nsIBrowserDOMWindow::OPEN_NEWTAB_BACKGROUND
||
1763 aWhere
== nsIBrowserDOMWindow::OPEN_NEWWINDOW
,
1764 "Unsupported open location");
1767 MakeRefPtr
<dom::BrowsingContextCallbackReceivedPromise::Private
>(
1770 // Get the nsIBrowserDOMWindow for the given BrowsingContext's tab.
1771 nsCOMPtr
<nsIBrowserDOMWindow
> browserDOMWindow
=
1772 aLoadingBrowsingContext
->GetBrowserDOMWindow();
1773 if (NS_WARN_IF(!browserDOMWindow
)) {
1774 MOZ_LOG(gProcessIsolationLog
, LogLevel::Warning
,
1775 ("Process Switch Abort: Unable to get nsIBrowserDOMWindow"));
1776 promise
->Reject(NS_ERROR_FAILURE
, __func__
);
1780 // Open a new content tab by calling into frontend. We don't need to worry
1781 // about the triggering principal or CSP, as createContentWindow doesn't
1782 // actually start loading anything, but use a null principal anyway in case
1783 // something changes.
1784 nsCOMPtr
<nsIPrincipal
> triggeringPrincipal
=
1785 NullPrincipal::Create(aLoadingBrowsingContext
->OriginAttributesRef());
1787 RefPtr
<nsOpenWindowInfo
> openInfo
= new nsOpenWindowInfo();
1788 openInfo
->mBrowsingContextReadyCallback
=
1789 new nsBrowsingContextReadyCallback(promise
);
1790 openInfo
->mOriginAttributes
= aLoadingBrowsingContext
->OriginAttributesRef();
1791 openInfo
->mParent
= aLoadingBrowsingContext
;
1792 openInfo
->mForceNoOpener
= true;
1793 openInfo
->mIsRemote
= true;
1795 // Do the actual work to open a new tab or window async.
1796 nsresult rv
= NS_DispatchToMainThread(NS_NewRunnableFunction(
1797 "DocumentLoadListener::SwitchToNewTab",
1798 [browserDOMWindow
, openInfo
, aWhere
, triggeringPrincipal
, promise
] {
1799 RefPtr
<BrowsingContext
> bc
;
1800 nsresult rv
= browserDOMWindow
->CreateContentWindow(
1801 /* uri */ nullptr, openInfo
, aWhere
,
1802 nsIBrowserDOMWindow::OPEN_NO_REFERRER
, triggeringPrincipal
,
1803 /* csp */ nullptr, getter_AddRefs(bc
));
1804 if (NS_WARN_IF(NS_FAILED(rv
))) {
1805 MOZ_LOG(gProcessIsolationLog
, LogLevel::Warning
,
1806 ("Process Switch Abort: CreateContentWindow threw"));
1807 promise
->Reject(rv
, __func__
);
1810 promise
->Resolve(bc
, __func__
);
1813 if (NS_WARN_IF(NS_FAILED(rv
))) {
1814 promise
->Reject(NS_ERROR_UNEXPECTED
, __func__
);
1819 bool DocumentLoadListener::MaybeTriggerProcessSwitch(
1820 bool* aWillSwitchToRemote
) {
1821 MOZ_ASSERT(XRE_IsParentProcess());
1822 MOZ_DIAGNOSTIC_ASSERT(mChannel
);
1823 MOZ_DIAGNOSTIC_ASSERT(mParentChannelListener
);
1824 MOZ_DIAGNOSTIC_ASSERT(aWillSwitchToRemote
);
1826 MOZ_LOG(gProcessIsolationLog
, LogLevel::Verbose
,
1827 ("DocumentLoadListener MaybeTriggerProcessSwitch [this=%p, uri=%s, "
1828 "browserid=%" PRIx64
"]",
1829 this, GetChannelCreationURI()->GetSpecOrDefault().get(),
1830 GetLoadingBrowsingContext()->Top()->BrowserId()));
1832 // If we're doing an <object>/<embed> load, we may be doing a document load at
1833 // this point. We never need to do a process switch for a non-document
1834 // <object> or <embed> load.
1835 if (!mIsDocumentLoad
) {
1836 if (!mChannel
->IsDocument()) {
1837 MOZ_LOG(gProcessIsolationLog
, LogLevel::Verbose
,
1838 ("Process Switch Abort: non-document load"));
1842 if (!nsObjectLoadingContent::IsSuccessfulRequest(mChannel
, &status
)) {
1843 MOZ_LOG(gProcessIsolationLog
, LogLevel::Verbose
,
1844 ("Process Switch Abort: error page"));
1849 // Check if we should handle this load in a different tab or window.
1850 int32_t where
= GetWhereToOpen(mChannel
, mIsDocumentLoad
);
1851 bool switchToNewTab
= where
!= nsIBrowserDOMWindow::OPEN_CURRENTWINDOW
;
1853 // Get the loading BrowsingContext. This may not be the context which will be
1854 // switching processes when switching to a new tab, and in the case of an
1855 // <object> or <embed> element, as we don't create the final context until
1856 // after process selection.
1858 // - /!\ WARNING /!\ -
1859 // Don't use `browsingContext->IsTop()` in this method! It will behave
1860 // incorrectly for non-document loads such as `<object>` or `<embed>`.
1861 // Instead, check whether or not `parentWindow` is null.
1862 RefPtr
<CanonicalBrowsingContext
> browsingContext
=
1863 GetLoadingBrowsingContext();
1864 // If switching to a new tab, the final BC isn't a frame.
1865 RefPtr
<WindowGlobalParent
> parentWindow
=
1866 switchToNewTab
? nullptr : GetParentWindowContext();
1867 if (!ContextCanProcessSwitch(browsingContext
, parentWindow
, switchToNewTab
)) {
1871 if (!browsingContext
->IsOwnedByProcess(GetContentProcessId(mContentParent
))) {
1872 MOZ_LOG(gProcessIsolationLog
, LogLevel::Error
,
1873 ("Process Switch Abort: context no longer owned by creator"));
1874 Cancel(NS_BINDING_ABORTED
,
1875 "Process Switch Abort: context no longer owned by creator"_ns
);
1879 if (browsingContext
->IsReplaced()) {
1880 MOZ_LOG(gProcessIsolationLog
, LogLevel::Warning
,
1881 ("Process Switch Abort: replaced browsing context"));
1882 Cancel(NS_BINDING_ABORTED
,
1883 "Process Switch Abort: replaced browsing context"_ns
);
1887 nsAutoCString
currentRemoteType(NOT_REMOTE_TYPE
);
1888 if (mContentParent
) {
1889 currentRemoteType
= mContentParent
->GetRemoteType();
1892 auto optionsResult
= IsolationOptionsForNavigation(
1893 browsingContext
->Top(), switchToNewTab
? nullptr : parentWindow
.get(),
1894 GetChannelCreationURI(), mChannel
, currentRemoteType
,
1895 HasCrossOriginOpenerPolicyMismatch(), switchToNewTab
, mLoadStateLoadType
,
1896 mDocumentChannelId
, mRemoteTypeOverride
);
1897 if (optionsResult
.isErr()) {
1898 MOZ_LOG(gProcessIsolationLog
, LogLevel::Error
,
1899 ("Process Switch Abort: CheckIsolationForNavigation Failed with %s",
1900 GetStaticErrorName(optionsResult
.inspectErr())));
1901 Cancel(optionsResult
.unwrapErr(),
1902 "Process Switch Abort: CheckIsolationForNavigation Failed"_ns
);
1906 NavigationIsolationOptions options
= optionsResult
.unwrap();
1908 if (options
.mTryUseBFCache
) {
1909 MOZ_ASSERT(!parentWindow
, "Can only BFCache toplevel windows");
1910 MOZ_ASSERT(!switchToNewTab
, "Can't BFCache for a tab switch");
1911 bool sameOrigin
= false;
1912 if (auto* wgp
= browsingContext
->GetCurrentWindowGlobal()) {
1913 nsCOMPtr
<nsIPrincipal
> resultPrincipal
;
1914 MOZ_ALWAYS_SUCCEEDS(
1915 nsContentUtils::GetSecurityManager()->GetChannelResultPrincipal(
1916 mChannel
, getter_AddRefs(resultPrincipal
)));
1918 wgp
->DocumentPrincipal()->EqualsConsideringDomain(resultPrincipal
);
1921 // We only reset the window name for content.
1922 mLoadingSessionHistoryInfo
->mForceMaybeResetName
.emplace(
1923 StaticPrefs::privacy_window_name_update_enabled() &&
1924 browsingContext
->IsContent() && !sameOrigin
);
1928 gProcessIsolationLog
, LogLevel::Verbose
,
1929 ("CheckIsolationForNavigation -> current:(%s) remoteType:(%s) replace:%d "
1930 "group:%" PRIx64
" bfcache:%d shentry:%p newTab:%d",
1931 currentRemoteType
.get(), options
.mRemoteType
.get(),
1932 options
.mReplaceBrowsingContext
, options
.mSpecificGroupId
,
1933 options
.mTryUseBFCache
, options
.mActiveSessionHistoryEntry
.get(),
1936 // Check if a process switch is needed.
1937 if (currentRemoteType
== options
.mRemoteType
&&
1938 !options
.mReplaceBrowsingContext
&& !switchToNewTab
) {
1939 MOZ_LOG(gProcessIsolationLog
, LogLevel::Info
,
1940 ("Process Switch Abort: type (%s) is compatible",
1941 options
.mRemoteType
.get()));
1945 if (NS_WARN_IF(parentWindow
&& options
.mRemoteType
.IsEmpty())) {
1946 MOZ_LOG(gProcessIsolationLog
, LogLevel::Error
,
1947 ("Process Switch Abort: non-remote target process for subframe"));
1951 *aWillSwitchToRemote
= !options
.mRemoteType
.IsEmpty();
1953 // If we've decided to re-target this load into a new tab or window (see
1954 // `GetWhereToOpen`), do so before performing a process switch. This will
1955 // require creating the new <browser> to load in, which may be performed
1957 if (switchToNewTab
) {
1958 SwitchToNewTab(browsingContext
, where
)
1960 GetMainThreadSerialEventTarget(), __func__
,
1961 [self
= RefPtr
{this},
1962 options
](const RefPtr
<BrowsingContext
>& aBrowsingContext
) mutable {
1963 if (aBrowsingContext
->IsDiscarded()) {
1965 gProcessIsolationLog
, LogLevel::Error
,
1966 ("Process Switch: Got invalid new-tab BrowsingContext"));
1967 self
->RedirectToRealChannelFinished(NS_ERROR_FAILURE
);
1971 MOZ_LOG(gProcessIsolationLog
, LogLevel::Verbose
,
1972 ("Process Switch: Redirected load to new tab"));
1973 self
->TriggerProcessSwitch(aBrowsingContext
->Canonical(), options
,
1974 /* aIsNewTab */ true);
1976 [self
= RefPtr
{this}](const CopyableErrorResult
&) {
1977 MOZ_LOG(gProcessIsolationLog
, LogLevel::Error
,
1978 ("Process Switch: SwitchToNewTab failed"));
1979 self
->RedirectToRealChannelFinished(NS_ERROR_FAILURE
);
1984 // If we're doing a document load, we can immediately perform a process
1986 if (mIsDocumentLoad
) {
1987 TriggerProcessSwitch(browsingContext
, options
);
1991 // We're not doing a document load, which means we must be performing an
1992 // object load. We need a BrowsingContext to perform the switch in, so will
1993 // trigger an upgrade.
1994 if (!mObjectUpgradeHandler
) {
1995 MOZ_LOG(gProcessIsolationLog
, LogLevel::Warning
,
1996 ("Process Switch Abort: no object upgrade handler"));
2000 mObjectUpgradeHandler
->UpgradeObjectLoad()->Then(
2001 GetMainThreadSerialEventTarget(), __func__
,
2002 [self
= RefPtr
{this}, options
, parentWindow
](
2003 const RefPtr
<CanonicalBrowsingContext
>& aBrowsingContext
) mutable {
2004 if (aBrowsingContext
->IsDiscarded() ||
2005 parentWindow
!= aBrowsingContext
->GetParentWindowContext()) {
2006 MOZ_LOG(gProcessIsolationLog
, LogLevel::Error
,
2007 ("Process Switch: Got invalid BrowsingContext from object "
2009 self
->RedirectToRealChannelFinished(NS_ERROR_FAILURE
);
2013 MOZ_LOG(gProcessIsolationLog
, LogLevel::Verbose
,
2014 ("Process Switch: Upgraded Object to Document Load"));
2015 self
->TriggerProcessSwitch(aBrowsingContext
, options
);
2017 [self
= RefPtr
{this}](nsresult aStatusCode
) {
2018 MOZ_ASSERT(NS_FAILED(aStatusCode
), "Status should be error");
2019 self
->RedirectToRealChannelFinished(aStatusCode
);
2024 void DocumentLoadListener::TriggerProcessSwitch(
2025 CanonicalBrowsingContext
* aContext
,
2026 const NavigationIsolationOptions
& aOptions
, bool aIsNewTab
) {
2027 MOZ_DIAGNOSTIC_ASSERT(aIsNewTab
|| aContext
->IsOwnedByProcess(
2028 GetContentProcessId(mContentParent
)),
2029 "not owned by creator process anymore?");
2030 if (MOZ_LOG_TEST(gProcessIsolationLog
, LogLevel::Info
)) {
2031 nsCString currentRemoteType
= "INVALID"_ns
;
2032 aContext
->GetCurrentRemoteType(currentRemoteType
, IgnoreErrors());
2034 MOZ_LOG(gProcessIsolationLog
, LogLevel::Info
,
2035 ("Process Switch: Changing Remoteness from '%s' to '%s'",
2036 currentRemoteType
.get(), aOptions
.mRemoteType
.get()));
2039 // Stash our stream filter requests to pass to TriggerRedirectToRealChannel,
2040 // as the call to `DisconnectListeners` will clear our list.
2041 nsTArray
<StreamFilterRequest
> streamFilterRequests
=
2042 std::move(mStreamFilterRequests
);
2044 // We're now committing to a process switch, so we can disconnect from
2045 // the listeners in the old process.
2046 // As the navigation is continuing, we don't actually want to cancel the
2047 // request in the old process unless we're redirecting the load into a new
2049 DisconnectListeners(NS_BINDING_ABORTED
, NS_BINDING_ABORTED
, !aIsNewTab
);
2051 MOZ_LOG(gProcessIsolationLog
, LogLevel::Verbose
,
2052 ("Process Switch: Calling ChangeRemoteness"));
2053 aContext
->ChangeRemoteness(aOptions
, mLoadIdentifier
)
2055 GetMainThreadSerialEventTarget(), __func__
,
2056 [self
= RefPtr
{this}, requests
= std::move(streamFilterRequests
)](
2057 BrowserParent
* aBrowserParent
) mutable {
2058 MOZ_ASSERT(self
->mChannel
,
2059 "Something went wrong, channel got cancelled");
2060 self
->TriggerRedirectToRealChannel(
2061 Some(aBrowserParent
? aBrowserParent
->Manager() : nullptr),
2062 std::move(requests
));
2064 [self
= RefPtr
{this}](nsresult aStatusCode
) {
2065 MOZ_ASSERT(NS_FAILED(aStatusCode
), "Status should be error");
2066 self
->RedirectToRealChannelFinished(aStatusCode
);
2070 RefPtr
<PDocumentChannelParent::RedirectToRealChannelPromise
>
2071 DocumentLoadListener::RedirectToParentProcess(uint32_t aRedirectFlags
,
2072 uint32_t aLoadFlags
) {
2073 // This is largely the same as ContentChild::RecvCrossProcessRedirect,
2074 // except without needing to deserialize or create an nsIChildChannel.
2076 RefPtr
<nsDocShellLoadState
> loadState
;
2077 nsDocShellLoadState::CreateFromPendingChannel(
2078 mChannel
, mLoadIdentifier
, mRedirectChannelId
, getter_AddRefs(loadState
));
2080 loadState
->SetLoadFlags(mLoadStateExternalLoadFlags
);
2081 loadState
->SetInternalLoadFlags(mLoadStateInternalLoadFlags
);
2082 loadState
->SetLoadType(mLoadStateLoadType
);
2083 if (mLoadingSessionHistoryInfo
) {
2084 loadState
->SetLoadingSessionHistoryInfo(*mLoadingSessionHistoryInfo
);
2087 // This is poorly named now.
2088 RefPtr
<ChildProcessChannelListener
> processListener
=
2089 ChildProcessChannelListener::GetSingleton();
2092 MakeRefPtr
<PDocumentChannelParent::RedirectToRealChannelPromise::Private
>(
2094 promise
->UseDirectTaskDispatch(__func__
);
2095 auto resolve
= [promise
](nsresult aResult
) {
2096 promise
->Resolve(aResult
, __func__
);
2099 nsTArray
<ipc::Endpoint
<extensions::PStreamFilterParent
>> endpoints
;
2100 processListener
->OnChannelReady(loadState
, mLoadIdentifier
,
2101 std::move(endpoints
), mTiming
,
2102 std::move(resolve
));
2107 RefPtr
<PDocumentChannelParent::RedirectToRealChannelPromise
>
2108 DocumentLoadListener::RedirectToRealChannel(
2109 uint32_t aRedirectFlags
, uint32_t aLoadFlags
,
2110 const Maybe
<ContentParent
*>& aDestinationProcess
,
2111 nsTArray
<ParentEndpoint
>&& aStreamFilterEndpoints
) {
2113 ("DocumentLoadListener RedirectToRealChannel [this=%p] "
2114 "aRedirectFlags=%" PRIx32
", aLoadFlags=%" PRIx32
,
2115 this, aRedirectFlags
, aLoadFlags
));
2117 if (mIsDocumentLoad
) {
2118 // TODO(djg): Add the last URI visit to history if success. Is there a
2119 // better place to handle this? Need access to the updated aLoadFlags.
2120 nsresult status
= NS_OK
;
2121 mChannel
->GetStatus(&status
);
2122 bool updateGHistory
=
2123 nsDocShell::ShouldUpdateGlobalHistory(mLoadStateLoadType
);
2124 if (NS_SUCCEEDED(status
) && updateGHistory
&&
2125 !net::ChannelIsPost(mChannel
)) {
2126 AddURIVisit(mChannel
, aLoadFlags
);
2130 // Register the new channel and obtain id for it
2131 nsCOMPtr
<nsIRedirectChannelRegistrar
> registrar
=
2132 RedirectChannelRegistrar::GetOrCreate();
2133 MOZ_ASSERT(registrar
);
2134 nsCOMPtr
<nsIChannel
> chan
= mChannel
;
2135 if (nsCOMPtr
<nsIViewSourceChannel
> vsc
= do_QueryInterface(chan
)) {
2136 chan
= vsc
->GetInnerChannel();
2138 mRedirectChannelId
= nsContentUtils::GenerateLoadIdentifier();
2139 MOZ_ALWAYS_SUCCEEDS(registrar
->RegisterChannel(chan
, mRedirectChannelId
));
2141 if (aDestinationProcess
) {
2142 RefPtr
<ContentParent
> cp
= *aDestinationProcess
;
2144 MOZ_ASSERT(aStreamFilterEndpoints
.IsEmpty());
2145 return RedirectToParentProcess(aRedirectFlags
, aLoadFlags
);
2148 if (!cp
->CanSend()) {
2149 return PDocumentChannelParent::RedirectToRealChannelPromise::
2150 CreateAndReject(ipc::ResponseRejectReason::SendError
, __func__
);
2153 nsTArray
<EarlyHintConnectArgs
> ehArgs
;
2154 mEarlyHintsService
.RegisterLinksAndGetConnectArgs(cp
->ChildID(), ehArgs
);
2156 RedirectToRealChannelArgs args
;
2157 SerializeRedirectData(args
, /* aIsCrossProcess */ true, aRedirectFlags
,
2158 aLoadFlags
, std::move(ehArgs
),
2159 mEarlyHintsService
.LinkType());
2161 mTiming
->Anonymize(args
.uri());
2162 args
.timing() = std::move(mTiming
);
2165 cp
->TransmitBlobDataIfBlobURL(args
.uri());
2167 if (CanonicalBrowsingContext
* bc
= GetDocumentBrowsingContext()) {
2168 if (bc
->IsTop() && bc
->IsActive()) {
2169 nsContentUtils::RequestGeckoTaskBurst();
2173 return cp
->SendCrossProcessRedirect(args
,
2174 std::move(aStreamFilterEndpoints
));
2177 if (mOpenPromiseResolved
) {
2179 ("DocumentLoadListener RedirectToRealChannel [this=%p] "
2180 "promise already resolved. Aborting.",
2182 // The promise has already been resolved or aborted, so we have no way to
2183 // return a promise again to the listener which would cancel the operation.
2184 // Reject the promise immediately.
2185 return PDocumentChannelParent::RedirectToRealChannelPromise::
2186 CreateAndResolve(NS_BINDING_ABORTED
, __func__
);
2189 // This promise will be passed on the promise listener which will
2190 // resolve this promise for us.
2192 MakeRefPtr
<PDocumentChannelParent::RedirectToRealChannelPromise::Private
>(
2195 mOpenPromise
->Resolve(
2196 OpenPromiseSucceededType({std::move(aStreamFilterEndpoints
),
2197 aRedirectFlags
, aLoadFlags
,
2198 mEarlyHintsService
.LinkType(), promise
}),
2201 // There is no way we could come back here if the promise had been resolved
2202 // previously. But for clarity and to avoid all doubt, we set this boolean to
2204 mOpenPromiseResolved
= true;
2209 void DocumentLoadListener::TriggerRedirectToRealChannel(
2210 const Maybe
<ContentParent
*>& aDestinationProcess
,
2211 nsTArray
<StreamFilterRequest
> aStreamFilterRequests
) {
2213 "DocumentLoadListener::TriggerRedirectToRealChannel [this=%p] "
2214 "aDestinationProcess=%" PRId64
,
2215 this, aDestinationProcess
? int64_t(*aDestinationProcess
) : int64_t(-1)));
2216 // This initiates replacing the current DocumentChannel with a
2217 // protocol specific 'real' channel, maybe in a different process than
2218 // the current DocumentChannelChild, if aDestinationProces is set.
2219 // It registers the current mChannel with the registrar to get an ID
2220 // so that the remote end can setup a new IPDL channel and lookup
2221 // the same underlying channel.
2222 // We expect this process to finish with FinishReplacementChannelSetup
2223 // (for both in-process and process switch cases), where we cleanup
2224 // the registrar and copy across any needed state to the replacing
2225 // IPDL parent object.
2227 nsTArray
<ParentEndpoint
> parentEndpoints(aStreamFilterRequests
.Length());
2228 if (!aStreamFilterRequests
.IsEmpty()) {
2229 ContentParent
* cp
= aDestinationProcess
.valueOr(mContentParent
);
2230 base::ProcessId pid
= cp
? cp
->OtherPid() : base::ProcessId
{0};
2232 for (StreamFilterRequest
& request
: aStreamFilterRequests
) {
2234 request
.mPromise
->Reject(false, __func__
);
2235 request
.mPromise
= nullptr;
2238 ParentEndpoint parent
;
2239 nsresult rv
= extensions::PStreamFilter::CreateEndpoints(
2240 &parent
, &request
.mChildEndpoint
);
2242 if (NS_FAILED(rv
)) {
2243 request
.mPromise
->Reject(false, __func__
);
2244 request
.mPromise
= nullptr;
2246 parentEndpoints
.AppendElement(std::move(parent
));
2251 // If we didn't have any redirects, then we pass the REDIRECT_INTERNAL flag
2252 // for this channel switch so that it isn't recorded in session history etc.
2253 // If there were redirect(s), then we want this switch to be recorded as a
2254 // real one, since we have a new URI.
2255 uint32_t redirectFlags
= 0;
2256 if (!mHaveVisibleRedirect
) {
2257 redirectFlags
= nsIChannelEventSink::REDIRECT_INTERNAL
;
2260 uint32_t newLoadFlags
= nsIRequest::LOAD_NORMAL
;
2261 MOZ_ALWAYS_SUCCEEDS(mChannel
->GetLoadFlags(&newLoadFlags
));
2262 // We're pulling our flags from the inner channel, which may not have this
2263 // flag set on it. This is the case when loading a 'view-source' channel.
2264 if (mIsDocumentLoad
|| aDestinationProcess
) {
2265 newLoadFlags
|= nsIChannel::LOAD_DOCUMENT_URI
;
2267 if (!aDestinationProcess
) {
2268 newLoadFlags
|= nsIChannel::LOAD_REPLACE
;
2271 // INHIBIT_PERSISTENT_CACHING is clearing during http redirects (from
2272 // both parent and content process channel instances), but only ever
2273 // re-added to the parent-side nsHttpChannel.
2274 // To match that behaviour, we want to explicitly avoid copying this flag
2275 // back to our newly created content side channel, otherwise it can
2276 // affect sub-resources loads in the same load group.
2277 nsCOMPtr
<nsIURI
> uri
;
2278 mChannel
->GetURI(getter_AddRefs(uri
));
2279 if (uri
&& uri
->SchemeIs("https")) {
2280 newLoadFlags
&= ~nsIRequest::INHIBIT_PERSISTENT_CACHING
;
2283 RefPtr
<DocumentLoadListener
> self
= this;
2284 RedirectToRealChannel(redirectFlags
, newLoadFlags
, aDestinationProcess
,
2285 std::move(parentEndpoints
))
2287 GetCurrentSerialEventTarget(), __func__
,
2288 [self
, requests
= std::move(aStreamFilterRequests
)](
2289 const nsresult
& aResponse
) mutable {
2290 for (StreamFilterRequest
& request
: requests
) {
2291 if (request
.mPromise
) {
2292 request
.mPromise
->Resolve(std::move(request
.mChildEndpoint
),
2294 request
.mPromise
= nullptr;
2297 self
->RedirectToRealChannelFinished(aResponse
);
2299 [self
](const mozilla::ipc::ResponseRejectReason
) {
2300 self
->RedirectToRealChannelFinished(NS_ERROR_FAILURE
);
2304 void DocumentLoadListener::MaybeReportBlockedByURLClassifier(nsresult aStatus
) {
2305 auto* browsingContext
= GetDocumentBrowsingContext();
2306 if (!browsingContext
|| browsingContext
->IsTop() ||
2307 !StaticPrefs::privacy_trackingprotection_testing_report_blocked_node()) {
2311 if (!UrlClassifierFeatureFactory::IsClassifierBlockingErrorCode(aStatus
)) {
2315 RefPtr
<WindowGlobalParent
> parent
= browsingContext
->GetParentWindowContext();
2317 Unused
<< parent
->SendAddBlockedFrameNodeByClassifier(browsingContext
);
2321 bool DocumentLoadListener::DocShellWillDisplayContent(nsresult aStatus
) {
2322 if (NS_SUCCEEDED(aStatus
)) {
2326 // Always return errored loads to the <object> or <embed> element's process,
2327 // as load errors will not be rendered as documents.
2328 if (!mIsDocumentLoad
) {
2332 // nsDocShell attempts urifixup on some failure types,
2333 // but also of those also display an error page if we don't
2334 // succeed with fixup, so we don't need to check for it
2337 auto* loadingContext
= GetLoadingBrowsingContext();
2339 bool isInitialDocument
= true;
2340 if (WindowGlobalParent
* currentWindow
=
2341 loadingContext
->GetCurrentWindowGlobal()) {
2342 isInitialDocument
= currentWindow
->IsInitialDocument();
2345 nsresult rv
= nsDocShell::FilterStatusForErrorPage(
2346 aStatus
, mChannel
, mLoadStateLoadType
, loadingContext
->IsTop(),
2347 loadingContext
->GetUseErrorPages(), isInitialDocument
, nullptr);
2349 if (NS_SUCCEEDED(rv
)) {
2350 MOZ_LOG(gProcessIsolationLog
, LogLevel::Verbose
,
2351 ("Skipping process switch, as DocShell will not display content "
2353 GetStaticErrorName(aStatus
),
2354 GetChannelCreationURI()->GetSpecOrDefault().get()));
2357 // If filtering returned a failure code, then an error page will
2358 // be display for that code, so return true;
2359 return NS_FAILED(rv
);
2362 bool DocumentLoadListener::MaybeHandleLoadErrorWithURIFixup(nsresult aStatus
) {
2363 RefPtr
<CanonicalBrowsingContext
> bc
= GetDocumentBrowsingContext();
2368 nsCOMPtr
<nsIInputStream
> newPostData
;
2369 bool wasSchemelessInput
= false;
2370 nsCOMPtr
<nsIURI
> newURI
= nsDocShell::AttemptURIFixup(
2371 mChannel
, aStatus
, mOriginalUriString
, mLoadStateLoadType
, bc
->IsTop(),
2372 mLoadStateInternalLoadFlags
&
2373 nsDocShell::INTERNAL_LOAD_FLAGS_ALLOW_THIRD_PARTY_FIXUP
,
2374 bc
->UsePrivateBrowsing(), true, getter_AddRefs(newPostData
),
2375 &wasSchemelessInput
);
2377 // Since aStatus will be NS_OK for 4xx and 5xx error codes we
2378 // have to check each request which was upgraded by https-first.
2379 // If an error (including 4xx and 5xx) occured, then let's check if
2380 // we can downgrade the scheme to HTTP again.
2381 bool isHTTPSFirstFixup
= false;
2384 nsHTTPSOnlyUtils::PotentiallyDowngradeHttpsFirstRequest(this, aStatus
);
2385 isHTTPSFirstFixup
= true;
2392 // If we got a new URI, then we should initiate a load with that.
2393 // Notify the listeners that this load is complete (with a code that
2394 // won't trigger an error page), and then start the new one.
2395 DisconnectListeners(NS_BINDING_ABORTED
, NS_BINDING_ABORTED
);
2397 RefPtr
<nsDocShellLoadState
> loadState
= new nsDocShellLoadState(newURI
);
2398 nsCOMPtr
<nsILoadInfo
> loadInfo
= mChannel
->LoadInfo();
2400 nsCOMPtr
<nsIContentSecurityPolicy
> cspToInherit
= loadInfo
->GetCspToInherit();
2401 loadState
->SetCsp(cspToInherit
);
2403 nsCOMPtr
<nsIPrincipal
> triggeringPrincipal
= loadInfo
->TriggeringPrincipal();
2404 loadState
->SetTriggeringPrincipal(triggeringPrincipal
);
2406 loadState
->SetPostDataStream(newPostData
);
2408 // Record whether the protocol was added through a fixup.
2409 loadState
->SetWasSchemelessInput(wasSchemelessInput
);
2411 if (isHTTPSFirstFixup
) {
2412 // We have to exempt the load from HTTPS-First to prevent a
2413 // upgrade-downgrade loop.
2414 loadState
->SetIsExemptFromHTTPSFirstMode(true);
2417 // Ensure to set referrer information in the fallback channel equally to the
2418 // not-upgraded original referrer info.
2420 // A simply copy of the referrer info from the upgraded one leads to problems.
2422 // 1. https://some-site.com redirects to http://other-site.com with referrer
2424 // "no-referrer-when-downgrade".
2425 // 2. https-first upgrades the redirection, so redirects to
2426 // https://other-site.com,
2427 // according to referrer policy the referrer will be send (https-> https)
2428 // 3. Assume other-site.com is not supporting https, https-first performs
2431 // If the referrer info from the upgraded channel gets copied into the
2432 // http fallback channel, the referrer info would contain the referrer
2433 // (https://some-site.com). That would violate the policy
2434 // "no-referrer-when-downgrade". A recreation of the original referrer info
2435 // would ensure us that the referrer is set according to the referrer policy.
2436 nsCOMPtr
<nsIHttpChannel
> httpChannel
= do_QueryInterface(mChannel
);
2438 nsCOMPtr
<nsIReferrerInfo
> referrerInfo
= httpChannel
->GetReferrerInfo();
2440 ReferrerPolicy referrerPolicy
= referrerInfo
->ReferrerPolicy();
2441 nsCOMPtr
<nsIURI
> originalReferrer
= referrerInfo
->GetOriginalReferrer();
2442 if (originalReferrer
) {
2443 // Create new ReferrerInfo with the original referrer and the referrer
2445 nsCOMPtr
<nsIReferrerInfo
> newReferrerInfo
=
2446 new ReferrerInfo(originalReferrer
, referrerPolicy
);
2447 loadState
->SetReferrerInfo(newReferrerInfo
);
2452 bc
->LoadURI(loadState
, false);
2457 DocumentLoadListener::OnStartRequest(nsIRequest
* aRequest
) {
2458 LOG(("DocumentLoadListener OnStartRequest [this=%p]", this));
2460 nsCOMPtr
<nsIMultiPartChannel
> multiPartChannel
= do_QueryInterface(aRequest
);
2461 if (multiPartChannel
) {
2462 multiPartChannel
->GetBaseChannel(getter_AddRefs(mChannel
));
2464 mChannel
= do_QueryInterface(aRequest
);
2466 MOZ_DIAGNOSTIC_ASSERT(mChannel
);
2468 if (mHaveVisibleRedirect
&& GetDocumentBrowsingContext() &&
2469 mLoadingSessionHistoryInfo
) {
2470 mLoadingSessionHistoryInfo
=
2471 GetDocumentBrowsingContext()->ReplaceLoadingSessionHistoryEntryForLoad(
2472 mLoadingSessionHistoryInfo
.get(), mChannel
);
2475 RefPtr
<nsHttpChannel
> httpChannel
= do_QueryObject(mChannel
);
2477 // Enforce CSP frame-ancestors and x-frame-options checks which
2478 // might cancel the channel.
2479 nsContentSecurityUtils::PerformCSPFrameAncestorAndXFOCheck(mChannel
);
2481 // HTTPS-Only Mode tries to upgrade connections to https. Once loading
2482 // is in progress we set that flag so that timeout counter measures
2485 nsCOMPtr
<nsILoadInfo
> loadInfo
= httpChannel
->LoadInfo();
2486 bool isPrivateWin
= loadInfo
->GetOriginAttributes().mPrivateBrowsingId
> 0;
2487 if (nsHTTPSOnlyUtils::IsHttpsOnlyModeEnabled(isPrivateWin
)) {
2488 uint32_t httpsOnlyStatus
= loadInfo
->GetHttpsOnlyStatus();
2489 httpsOnlyStatus
|= nsILoadInfo::HTTPS_ONLY_TOP_LEVEL_LOAD_IN_PROGRESS
;
2490 loadInfo
->SetHttpsOnlyStatus(httpsOnlyStatus
);
2493 if (mLoadingSessionHistoryInfo
&&
2494 nsDocShell::ShouldDiscardLayoutState(httpChannel
)) {
2495 mLoadingSessionHistoryInfo
->mInfo
.SetSaveLayoutStateFlag(false);
2499 auto* loadingContext
= GetLoadingBrowsingContext();
2500 if (!loadingContext
|| loadingContext
->IsDiscarded()) {
2501 Cancel(NS_ERROR_UNEXPECTED
, "No valid LoadingBrowsingContext."_ns
);
2502 return NS_ERROR_UNEXPECTED
;
2505 if (AppShutdown::IsInOrBeyond(ShutdownPhase::AppShutdownConfirmed
)) {
2506 Cancel(NS_ERROR_ILLEGAL_DURING_SHUTDOWN
,
2507 "Aborting OnStartRequest after shutdown started."_ns
);
2511 // Block top-level data URI navigations if triggered by the web. Logging is
2512 // performed in AllowTopLevelNavigationToDataURI.
2513 if (!nsContentSecurityManager::AllowTopLevelNavigationToDataURI(mChannel
)) {
2514 mChannel
->Cancel(NS_ERROR_DOM_BAD_URI
);
2515 if (loadingContext
) {
2516 RefPtr
<MaybeCloseWindowHelper
> maybeCloseWindowHelper
=
2517 new MaybeCloseWindowHelper(loadingContext
);
2518 // If a new window was opened specifically for this request, close it
2519 // after blocking the navigation.
2520 maybeCloseWindowHelper
->SetShouldCloseWindow(
2521 IsFirstLoadInWindow(mChannel
));
2522 Unused
<< maybeCloseWindowHelper
->MaybeCloseWindow();
2524 DisconnectListeners(NS_ERROR_DOM_BAD_URI
, NS_ERROR_DOM_BAD_URI
);
2528 // Generally we want to switch to a real channel even if the request failed,
2529 // since the listener might want to access protocol-specific data (like http
2530 // response headers) in its error handling.
2531 // An exception to this is when nsExtProtocolChannel handled the request and
2532 // returned NS_ERROR_NO_CONTENT, since creating a real one in the content
2533 // process will attempt to handle the URI a second time.
2534 nsresult status
= NS_OK
;
2535 aRequest
->GetStatus(&status
);
2536 if (status
== NS_ERROR_NO_CONTENT
) {
2537 DisconnectListeners(status
, status
);
2541 // PerformCSPFrameAncestorAndXFOCheck may cancel a moz-extension request that
2542 // needs to be handled here. Without this, the resource would be loaded and
2543 // not blocked when the real channel is created in the content process.
2544 if (status
== NS_ERROR_CSP_FRAME_ANCESTOR_VIOLATION
&& !httpChannel
) {
2545 DisconnectListeners(status
, status
);
2549 // If this was a failed load and we want to try fixing the uri, then
2550 // this will initiate a new load (and disconnect this one), and we don't
2551 // need to do anything else.
2552 if (MaybeHandleLoadErrorWithURIFixup(status
)) {
2556 mStreamListenerFunctions
.AppendElement(StreamListenerFunction
{
2557 VariantIndex
<0>{}, OnStartRequestParams
{aRequest
}});
2559 if (mOpenPromiseResolved
|| mInitiatedRedirectToRealChannel
) {
2560 // I we have already resolved the promise, there's no point to continue
2561 // attempting a process switch or redirecting to the real channel.
2562 // We can also have multiple calls to OnStartRequest when dealing with
2563 // multi-part content, but only want to redirect once.
2567 // Keep track of server responses resulting in a document for the Bounce
2568 // Tracking Protection.
2569 if (mIsDocumentLoad
&& GetParentWindowContext() == nullptr &&
2570 loadingContext
->IsTopContent()) {
2571 RefPtr
<BounceTrackingState
> bounceTrackingState
=
2572 loadingContext
->GetBounceTrackingState();
2574 // Not every browsing context has a BounceTrackingState. It's also null when
2575 // the feature is disabled.
2576 if (bounceTrackingState
) {
2577 DebugOnly
<nsresult
> rv
=
2578 bounceTrackingState
->OnDocumentStartRequest(mChannel
);
2579 NS_WARNING_ASSERTION(
2581 "BounceTrackingState::OnDocumentStartRequest failed.");
2585 mChannel
->Suspend();
2587 mInitiatedRedirectToRealChannel
= true;
2589 MaybeReportBlockedByURLClassifier(status
);
2591 // Determine if a new process needs to be spawned. If it does, this will
2592 // trigger a cross process switch, and we should hold off on redirecting to
2593 // the real channel.
2594 // If the channel has failed, and the docshell isn't going to display an
2595 // error page for that failure, then don't allow process switching, since
2596 // we just want to keep our existing document.
2597 bool willBeRemote
= false;
2598 if (!DocShellWillDisplayContent(status
) ||
2599 !MaybeTriggerProcessSwitch(&willBeRemote
)) {
2600 // We're not going to be doing a process switch, so redirect to the real
2601 // channel within our current process.
2602 nsTArray
<StreamFilterRequest
> streamFilterRequests
=
2603 std::move(mStreamFilterRequests
);
2604 if (!mSupportsRedirectToRealChannel
) {
2605 RefPtr
<BrowserParent
> browserParent
= loadingContext
->GetBrowserParent();
2606 if (browserParent
->Manager() != mContentParent
) {
2608 ("DocumentLoadListener::RedirectToRealChannel failed because "
2609 "browsingContext no longer owned by creator"));
2610 Cancel(NS_BINDING_ABORTED
,
2611 "DocumentLoadListener::RedirectToRealChannel failed because "
2612 "browsingContext no longer owned by creator"_ns
);
2615 MOZ_DIAGNOSTIC_ASSERT(
2616 browserParent
->GetBrowsingContext() == loadingContext
,
2617 "make sure the load is going to the right place");
2619 // If the existing process is right for this load, but the bridge doesn't
2620 // support redirects, then we need to do it manually, by faking a process
2622 DisconnectListeners(NS_BINDING_ABORTED
, NS_BINDING_ABORTED
,
2623 /* aContinueNavigating */ true);
2625 // Notify the docshell that it should load using the newly connected
2627 browserParent
->ResumeLoad(mLoadIdentifier
);
2629 // Use the current process ID to run the 'process switch' path and connect
2630 // the channel into the current process.
2631 TriggerRedirectToRealChannel(Some(mContentParent
),
2632 std::move(streamFilterRequests
));
2634 TriggerRedirectToRealChannel(Nothing(), std::move(streamFilterRequests
));
2637 // If we're not switching, then check if we're currently remote.
2638 if (mContentParent
) {
2639 willBeRemote
= true;
2644 uint32_t responseStatus
= 0;
2645 Unused
<< httpChannel
->GetResponseStatus(&responseStatus
);
2646 mEarlyHintsService
.FinalResponse(responseStatus
);
2648 mEarlyHintsService
.Cancel(
2649 "DocumentLoadListener::OnStartRequest: no httpChannel"_ns
);
2652 // If we're going to be delivering this channel to a remote content
2653 // process, then we want to install any required content conversions
2654 // in the content process.
2655 // The caller of this OnStartRequest will install a conversion
2656 // helper after we return if we haven't disabled conversion. Normally
2657 // HttpChannelParent::OnStartRequest would disable conversion, but we're
2658 // defering calling that until later. Manually disable it now to prevent the
2659 // converter from being installed (since we want the child to do it), and
2660 // also save the value so that when we do call
2661 // HttpChannelParent::OnStartRequest, we can have the value as it originally
2664 Unused
<< httpChannel
->GetApplyConversion(&mOldApplyConversion
);
2666 httpChannel
->SetApplyConversion(false);
2674 DocumentLoadListener::OnStopRequest(nsIRequest
* aRequest
,
2675 nsresult aStatusCode
) {
2676 LOG(("DocumentLoadListener OnStopRequest [this=%p]", this));
2677 mStreamListenerFunctions
.AppendElement(StreamListenerFunction
{
2678 VariantIndex
<2>{}, OnStopRequestParams
{aRequest
, aStatusCode
}});
2680 // If we're not a multi-part channel, then we're finished and we don't
2681 // expect any further events. If we are, then this might be called again,
2682 // so wait for OnAfterLastPart instead.
2683 nsCOMPtr
<nsIMultiPartChannel
> multiPartChannel
= do_QueryInterface(aRequest
);
2684 if (!multiPartChannel
) {
2688 mStreamFilterRequests
.Clear();
2694 DocumentLoadListener::OnDataAvailable(nsIRequest
* aRequest
,
2695 nsIInputStream
* aInputStream
,
2696 uint64_t aOffset
, uint32_t aCount
) {
2697 LOG(("DocumentLoadListener OnDataAvailable [this=%p]", this));
2698 // This isn't supposed to happen, since we suspended the channel, but
2699 // sometimes Suspend just doesn't work. This can happen when we're routing
2700 // through nsUnknownDecoder to sniff the content type, and it doesn't handle
2701 // being suspended. Let's just store the data and manually forward it to our
2702 // redirected channel when it's ready.
2704 nsresult rv
= NS_ReadInputStreamToString(aInputStream
, data
, aCount
);
2705 NS_ENSURE_SUCCESS(rv
, rv
);
2707 mStreamListenerFunctions
.AppendElement(StreamListenerFunction
{
2709 OnDataAvailableParams
{aRequest
, std::move(data
), aOffset
, aCount
}});
2714 //-----------------------------------------------------------------------------
2715 // DoucmentLoadListener::nsIMultiPartChannelListener
2716 //-----------------------------------------------------------------------------
2719 DocumentLoadListener::OnAfterLastPart(nsresult aStatus
) {
2720 LOG(("DocumentLoadListener OnAfterLastPart [this=%p]", this));
2721 if (!mInitiatedRedirectToRealChannel
) {
2722 // if we get here, and we haven't initiated a redirect to a real
2723 // channel, then it means we never got OnStartRequest (maybe a problem?)
2724 // and we retargeted everything.
2725 LOG(("DocumentLoadListener Disconnecting child"));
2726 DisconnectListeners(NS_BINDING_RETARGETED
, NS_OK
);
2729 mStreamListenerFunctions
.AppendElement(StreamListenerFunction
{
2730 VariantIndex
<3>{}, OnAfterLastPartParams
{aStatus
}});
2736 DocumentLoadListener::GetInterface(const nsIID
& aIID
, void** result
) {
2737 RefPtr
<CanonicalBrowsingContext
> browsingContext
=
2738 GetLoadingBrowsingContext();
2739 if (aIID
.Equals(NS_GET_IID(nsILoadContext
)) && browsingContext
) {
2740 browsingContext
.forget(result
);
2744 return QueryInterface(aIID
, result
);
2747 ////////////////////////////////////////////////////////////////////////////////
2749 ////////////////////////////////////////////////////////////////////////////////
2752 DocumentLoadListener::SetParentListener(
2753 mozilla::net::ParentChannelListener
* listener
) {
2754 // We don't need this (do we?)
2759 DocumentLoadListener::SetClassifierMatchedInfo(const nsACString
& aList
,
2760 const nsACString
& aProvider
,
2761 const nsACString
& aFullHash
) {
2762 ClassifierMatchedInfoParams params
;
2763 params
.mList
= aList
;
2764 params
.mProvider
= aProvider
;
2765 params
.mFullHash
= aFullHash
;
2767 mIParentChannelFunctions
.AppendElement(
2768 IParentChannelFunction
{VariantIndex
<0>{}, std::move(params
)});
2773 DocumentLoadListener::SetClassifierMatchedTrackingInfo(
2774 const nsACString
& aLists
, const nsACString
& aFullHash
) {
2775 ClassifierMatchedTrackingInfoParams params
;
2776 params
.mLists
= aLists
;
2777 params
.mFullHashes
= aFullHash
;
2779 mIParentChannelFunctions
.AppendElement(
2780 IParentChannelFunction
{VariantIndex
<1>{}, std::move(params
)});
2785 DocumentLoadListener::NotifyClassificationFlags(uint32_t aClassificationFlags
,
2786 bool aIsThirdParty
) {
2787 mIParentChannelFunctions
.AppendElement(IParentChannelFunction
{
2789 ClassificationFlagsParams
{aClassificationFlags
, aIsThirdParty
}});
2794 DocumentLoadListener::Delete() {
2795 MOZ_ASSERT_UNREACHABLE("This method is unused");
2800 DocumentLoadListener::GetRemoteType(nsACString
& aRemoteType
) {
2801 // FIXME: The remote type here should be pulled from the remote process used
2802 // to create this DLL, not from the current `browsingContext`.
2803 RefPtr
<CanonicalBrowsingContext
> browsingContext
=
2804 GetDocumentBrowsingContext();
2805 if (!browsingContext
) {
2806 return NS_ERROR_UNEXPECTED
;
2810 browsingContext
->GetCurrentRemoteType(aRemoteType
, error
);
2811 if (error
.Failed()) {
2812 aRemoteType
= NOT_REMOTE_TYPE
;
2817 ////////////////////////////////////////////////////////////////////////////////
2818 // nsIChannelEventSink
2819 ////////////////////////////////////////////////////////////////////////////////
2822 DocumentLoadListener::AsyncOnChannelRedirect(
2823 nsIChannel
* aOldChannel
, nsIChannel
* aNewChannel
, uint32_t aFlags
,
2824 nsIAsyncVerifyRedirectCallback
* aCallback
) {
2825 LOG(("DocumentLoadListener::AsyncOnChannelRedirect [this=%p flags=%" PRIu32
2828 // We generally don't want to notify the content process about redirects,
2829 // so just update our channel and tell the callback that we're good to go.
2830 mChannel
= aNewChannel
;
2832 // We need the original URI of the current channel to use to open the real
2833 // channel in the content process. Unfortunately we overwrite the original
2834 // uri of the new channel with the original pre-redirect URI, so grab
2835 // a copy of it now and save it on the loadInfo corresponding to the
2837 nsCOMPtr
<nsILoadInfo
> loadInfoFromChannel
= mChannel
->LoadInfo();
2838 MOZ_ASSERT(loadInfoFromChannel
);
2839 nsCOMPtr
<nsIURI
> uri
;
2840 mChannel
->GetOriginalURI(getter_AddRefs(uri
));
2841 loadInfoFromChannel
->SetChannelCreationOriginalURI(uri
);
2843 // Since we're redirecting away from aOldChannel, we should check if it
2844 // had a COOP mismatch, since we want the final result for this to
2845 // include the state of all channels we redirected through.
2846 nsCOMPtr
<nsIHttpChannelInternal
> httpChannel
= do_QueryInterface(aOldChannel
);
2848 bool isCOOPMismatch
= false;
2849 Unused
<< NS_WARN_IF(NS_FAILED(
2850 httpChannel
->HasCrossOriginOpenerPolicyMismatch(&isCOOPMismatch
)));
2851 mHasCrossOriginOpenerPolicyMismatch
|= isCOOPMismatch
;
2854 // If HTTPS-Only mode is enabled, we need to check whether the exception-flag
2855 // needs to be removed or set, by asking the PermissionManager.
2856 nsHTTPSOnlyUtils::TestSitePermissionAndPotentiallyAddExemption(mChannel
);
2858 // We don't need to confirm internal redirects or record any
2859 // history for them, so just immediately verify and return.
2860 if (aFlags
& nsIChannelEventSink::REDIRECT_INTERNAL
) {
2862 ("DocumentLoadListener AsyncOnChannelRedirect [this=%p] "
2863 "flags=REDIRECT_INTERNAL",
2865 aCallback
->OnRedirectVerifyCallback(NS_OK
);
2869 // Cancel cross origin redirects as described by whatwg:
2870 // > Note: [The early hint reponse] is discarded if it is succeeded by a
2871 // > cross-origin redirect.
2872 // https://html.spec.whatwg.org/multipage/semantics.html#early-hints
2873 nsCOMPtr
<nsIURI
> oldURI
;
2874 aOldChannel
->GetURI(getter_AddRefs(oldURI
));
2875 nsIScriptSecurityManager
* ssm
= nsContentUtils::GetSecurityManager();
2876 nsresult rv
= ssm
->CheckSameOriginURI(oldURI
, uri
, false, false);
2877 if (NS_FAILED(rv
)) {
2878 mEarlyHintsService
.Cancel(
2879 "DocumentLoadListener::AsyncOnChannelRedirect: cors redirect"_ns
);
2882 if (GetDocumentBrowsingContext()) {
2883 if (!net::ChannelIsPost(aOldChannel
)) {
2884 AddURIVisit(aOldChannel
, 0);
2885 nsDocShell::SaveLastVisit(aNewChannel
, oldURI
, aFlags
);
2888 mHaveVisibleRedirect
|= true;
2891 ("DocumentLoadListener AsyncOnChannelRedirect [this=%p] "
2892 "mHaveVisibleRedirect=%c",
2893 this, mHaveVisibleRedirect
? 'T' : 'F'));
2895 // Clear out our nsIParentChannel functions, since a normal parent
2896 // channel would actually redirect and not have those values on the new one.
2897 // We expect the URI classifier to run on the redirected channel with
2898 // the new URI and set these again.
2899 mIParentChannelFunctions
.Clear();
2901 // If we had a remote type override, ensure it's been cleared after a
2902 // redirect, as it can't apply anymore.
2903 mRemoteTypeOverride
.reset();
2906 nsCOMPtr
<nsIURI
> uriBeingLoaded
=
2907 AntiTrackingUtils::MaybeGetDocumentURIBeingLoaded(mChannel
);
2909 RefPtr
<MozPromise
<bool, bool, false>> promise
;
2910 RefPtr
<CanonicalBrowsingContext
> bc
=
2911 mParentChannelListener
->GetBrowsingContext();
2912 nsCOMPtr
<nsIWidget
> widget
=
2913 bc
? bc
->GetParentProcessWidgetContaining() : nullptr;
2914 RefPtr
<nsWindow
> window
= nsWindow::From(widget
);
2917 promise
= window
->OnLoadRequest(uriBeingLoaded
,
2918 nsIBrowserDOMWindow::OPEN_CURRENTWINDOW
,
2919 nsIWebNavigation::LOAD_FLAGS_IS_REDIRECT
,
2920 nullptr, false, bc
->IsTopContent());
2924 RefPtr
<nsIAsyncVerifyRedirectCallback
> cb
= aCallback
;
2926 GetCurrentSerialEventTarget(), __func__
,
2927 [=](const MozPromise
<bool, bool, false>::ResolveOrRejectValue
& aValue
) {
2928 if (aValue
.IsResolve()) {
2929 bool handled
= aValue
.ResolveValue();
2931 cb
->OnRedirectVerifyCallback(NS_ERROR_ABORT
);
2933 cb
->OnRedirectVerifyCallback(NS_OK
);
2938 #endif /* ANDROID */
2940 aCallback
->OnRedirectVerifyCallback(NS_OK
);
2945 nsIURI
* DocumentLoadListener::GetChannelCreationURI() const {
2946 nsCOMPtr
<nsILoadInfo
> channelLoadInfo
= mChannel
->LoadInfo();
2948 nsCOMPtr
<nsIURI
> uri
;
2949 channelLoadInfo
->GetChannelCreationOriginalURI(getter_AddRefs(uri
));
2951 // See channelCreationOriginalURI for more info. We use this instead of the
2952 // originalURI of the channel to help us avoid the situation when we use
2953 // the URI of a redirect that has failed to happen.
2957 // Otherwise, get the original URI from the channel.
2958 mChannel
->GetOriginalURI(getter_AddRefs(uri
));
2962 // This method returns the cached result of running the Cross-Origin-Opener
2963 // policy compare algorithm by calling ComputeCrossOriginOpenerPolicyMismatch
2964 bool DocumentLoadListener::HasCrossOriginOpenerPolicyMismatch() const {
2965 // If we found a COOP mismatch on an earlier channel and then
2966 // redirected away from that, we should use that result.
2967 if (mHasCrossOriginOpenerPolicyMismatch
) {
2971 nsCOMPtr
<nsIHttpChannelInternal
> httpChannel
= do_QueryInterface(mChannel
);
2973 // Not an nsIHttpChannelInternal assume it's okay to switch.
2977 bool isCOOPMismatch
= false;
2978 Unused
<< NS_WARN_IF(NS_FAILED(
2979 httpChannel
->HasCrossOriginOpenerPolicyMismatch(&isCOOPMismatch
)));
2980 return isCOOPMismatch
;
2983 auto DocumentLoadListener::AttachStreamFilter()
2984 -> RefPtr
<ChildEndpointPromise
> {
2985 LOG(("DocumentLoadListener AttachStreamFilter [this=%p]", this));
2987 StreamFilterRequest
* request
= mStreamFilterRequests
.AppendElement();
2988 request
->mPromise
= new ChildEndpointPromise::Private(__func__
);
2989 return request
->mPromise
;
2992 NS_IMETHODIMP
DocumentLoadListener::OnProgress(nsIRequest
* aRequest
,
2994 int64_t aProgressMax
) {
2998 NS_IMETHODIMP
DocumentLoadListener::OnStatus(nsIRequest
* aRequest
,
3000 const char16_t
* aStatusArg
) {
3001 nsCOMPtr
<nsIChannel
> channel
= mChannel
;
3003 RefPtr
<BrowsingContextWebProgress
> webProgress
=
3004 GetLoadingBrowsingContext()->GetWebProgress();
3005 const nsString
message(aStatusArg
);
3008 NS_DispatchToMainThread(
3009 NS_NewRunnableFunction("DocumentLoadListener::OnStatus", [=]() {
3010 webProgress
->OnStatusChange(webProgress
, channel
, aStatus
,
3017 NS_IMETHODIMP
DocumentLoadListener::EarlyHint(const nsACString
& aLinkHeader
,
3018 const nsACString
& aReferrerPolicy
,
3019 const nsACString
& aCSPHeader
) {
3020 LOG(("DocumentLoadListener::EarlyHint.\n"));
3021 mEarlyHintsService
.EarlyHint(aLinkHeader
, GetChannelCreationURI(), mChannel
,
3022 aReferrerPolicy
, aCSPHeader
,
3023 GetLoadingBrowsingContext());
3028 } // namespace mozilla