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 "ParentProcessDocumentChannel.h"
10 #include "mozilla/extensions/StreamFilterParent.h"
11 #include "mozilla/net/ParentChannelWrapper.h"
12 #include "mozilla/net/UrlClassifierCommon.h"
13 #include "mozilla/StaticPrefs_extensions.h"
15 #include "nsDocShell.h"
16 #include "nsIObserverService.h"
17 #include "nsIClassifiedChannel.h"
18 #include "nsIXULRuntime.h"
19 #include "nsHttpHandler.h"
20 #include "nsDocShellLoadState.h"
22 extern mozilla::LazyLogModule gDocumentChannelLog
;
23 #define LOG(fmt) MOZ_LOG(gDocumentChannelLog, mozilla::LogLevel::Verbose, fmt)
28 using RedirectToRealChannelPromise
=
29 typename
PDocumentChannelParent::RedirectToRealChannelPromise
;
31 NS_IMPL_ISUPPORTS_INHERITED(ParentProcessDocumentChannel
, DocumentChannel
,
32 nsIAsyncVerifyRedirectCallback
, nsIObserver
)
34 ParentProcessDocumentChannel::ParentProcessDocumentChannel(
35 nsDocShellLoadState
* aLoadState
, class LoadInfo
* aLoadInfo
,
36 nsLoadFlags aLoadFlags
, uint32_t aCacheKey
, bool aUriModified
,
37 bool aIsEmbeddingBlockedError
)
38 : DocumentChannel(aLoadState
, aLoadInfo
, aLoadFlags
, aCacheKey
,
39 aUriModified
, aIsEmbeddingBlockedError
) {
40 LOG(("ParentProcessDocumentChannel ctor [this=%p]", this));
43 ParentProcessDocumentChannel::~ParentProcessDocumentChannel() {
44 LOG(("ParentProcessDocumentChannel dtor [this=%p]", this));
47 RefPtr
<RedirectToRealChannelPromise
>
48 ParentProcessDocumentChannel::RedirectToRealChannel(
49 nsTArray
<ipc::Endpoint
<extensions::PStreamFilterParent
>>&&
50 aStreamFilterEndpoints
,
51 uint32_t aRedirectFlags
, uint32_t aLoadFlags
,
52 const nsTArray
<EarlyHintConnectArgs
>& aEarlyHints
) {
53 LOG(("ParentProcessDocumentChannel RedirectToRealChannel [this=%p]", this));
54 nsCOMPtr
<nsIChannel
> channel
= mDocumentLoadListener
->GetChannel();
55 channel
->SetLoadFlags(aLoadFlags
);
56 channel
->SetNotificationCallbacks(mCallbacks
);
59 channel
->SetLoadGroup(mLoadGroup
);
62 if (XRE_IsE10sParentProcess()) {
64 MOZ_ALWAYS_SUCCEEDS(NS_GetFinalChannelURI(channel
, getter_AddRefs(uri
)));
65 if (!nsDocShell::CanLoadInParentProcess(uri
)) {
69 "Attempt to load a non-authorised load in the parent process: ", 0);
70 NS_ASSERTION(false, msg
.get());
71 return RedirectToRealChannelPromise::CreateAndResolve(
72 NS_ERROR_CONTENT_BLOCKED
, __func__
);
75 mStreamFilterEndpoints
= std::move(aStreamFilterEndpoints
);
77 if (mDocumentLoadListener
->IsDocumentLoad() &&
78 mozilla::SessionHistoryInParent() && GetDocShell() &&
79 mDocumentLoadListener
->GetLoadingSessionHistoryInfo()) {
80 GetDocShell()->SetLoadingSessionHistoryInfo(
81 *mDocumentLoadListener
->GetLoadingSessionHistoryInfo());
84 RefPtr
<RedirectToRealChannelPromise
> p
= mPromise
.Ensure(__func__
);
85 // We make the promise use direct task dispatch in order to reduce the number
86 // of event loops iterations.
87 mPromise
.UseDirectTaskDispatch(__func__
);
90 gHttpHandler
->AsyncOnChannelRedirect(this, channel
, aRedirectFlags
);
93 ("ParentProcessDocumentChannel RedirectToRealChannel "
94 "AsyncOnChannelRedirect failed [this=%p "
97 OnRedirectVerifyCallback(rv
);
104 ParentProcessDocumentChannel::OnRedirectVerifyCallback(nsresult aResult
) {
106 ("ParentProcessDocumentChannel OnRedirectVerifyCallback [this=%p "
108 this, int(aResult
)));
110 MOZ_ASSERT(mDocumentLoadListener
);
112 if (NS_FAILED(aResult
)) {
114 } else if (mCanceled
) {
115 aResult
= NS_ERROR_ABORT
;
117 const nsCOMPtr
<nsIChannel
> channel
= mDocumentLoadListener
->GetChannel();
118 mLoadGroup
->AddRequest(channel
, nullptr);
119 // Adding the channel to the loadgroup could have triggered a status
120 // change with an observer being called destroying the docShell, resulting
121 // in the PPDC to be canceled.
123 aResult
= NS_ERROR_ABORT
;
125 mLoadGroup
->RemoveRequest(this, nullptr, NS_BINDING_REDIRECTED
);
126 for (auto& endpoint
: mStreamFilterEndpoints
) {
127 extensions::StreamFilterParent::Attach(channel
, std::move(endpoint
));
130 RefPtr
<ParentChannelWrapper
> wrapper
=
131 new ParentChannelWrapper(channel
, mListener
);
133 wrapper
->Register(mDocumentLoadListener
->GetRedirectChannelId());
137 mPromise
.Resolve(aResult
, __func__
);
142 NS_IMETHODIMP
ParentProcessDocumentChannel::AsyncOpen(
143 nsIStreamListener
* aListener
) {
144 LOG(("ParentProcessDocumentChannel AsyncOpen [this=%p]", this));
145 auto docShell
= RefPtr
<nsDocShell
>(GetDocShell());
146 MOZ_ASSERT(docShell
);
148 bool isDocumentLoad
= mLoadInfo
->GetExternalContentPolicyType() !=
149 ExtContentPolicy::TYPE_OBJECT
;
151 mDocumentLoadListener
= MakeRefPtr
<DocumentLoadListener
>(
152 docShell
->GetBrowsingContext()->Canonical(), isDocumentLoad
);
153 LOG(("Created PPDocumentChannel with listener=%p",
154 mDocumentLoadListener
.get()));
157 nsCOMPtr
<nsIObserverService
> observerService
=
158 mozilla::services::GetObserverService();
159 if (observerService
) {
160 MOZ_ALWAYS_SUCCEEDS(observerService
->AddObserver(
161 this, NS_HTTP_ON_MODIFY_REQUEST_TOPIC
, false));
164 gHttpHandler
->OnOpeningDocumentRequest(this);
166 if (isDocumentLoad
) {
167 // Return value of setting synced field should be checked. See bug 1656492.
168 Unused
<< GetDocShell()->GetBrowsingContext()->SetCurrentLoadIdentifier(
169 Some(mLoadState
->GetLoadIdentifier()));
173 Maybe
<dom::ClientInfo
> initialClientInfo
= mInitialClientInfo
;
175 RefPtr
<DocumentLoadListener::OpenPromise
> promise
;
176 if (isDocumentLoad
) {
177 promise
= mDocumentLoadListener
->OpenDocument(
178 mLoadState
, mCacheKey
, Some(mChannelId
), TimeStamp::Now(), mTiming
,
179 std::move(initialClientInfo
), Some(mUriModified
),
180 Some(mIsEmbeddingBlockedError
), nullptr /* ContentParent */, &rv
);
182 promise
= mDocumentLoadListener
->OpenObject(
183 mLoadState
, mCacheKey
, Some(mChannelId
), TimeStamp::Now(), mTiming
,
184 std::move(initialClientInfo
), InnerWindowIDForExtantDoc(docShell
),
185 mLoadFlags
, mLoadInfo
->InternalContentPolicyType(),
186 dom::UserActivation::IsHandlingUserInput(), nullptr /* ContentParent */,
187 nullptr /* ObjectUpgradeHandler */, &rv
);
191 MOZ_ASSERT(!promise
);
192 mDocumentLoadListener
= nullptr;
197 mListener
= aListener
;
199 mLoadGroup
->AddRequest(this, nullptr);
202 RefPtr
<ParentProcessDocumentChannel
> self
= this;
204 GetCurrentSerialEventTarget(), __func__
,
205 [self
](DocumentLoadListener::OpenPromiseSucceededType
&& aResolveValue
) {
206 self
->mDocumentLoadListener
->CancelEarlyHintPreloads();
207 nsTArray
<EarlyHintConnectArgs
> earlyHints
;
209 // The DLL is waiting for us to resolve the
210 // RedirectToRealChannelPromise given as parameter.
211 RefPtr
<RedirectToRealChannelPromise
> p
=
212 self
->RedirectToRealChannel(
213 std::move(aResolveValue
.mStreamFilterEndpoints
),
214 aResolveValue
.mRedirectFlags
, aResolveValue
.mLoadFlags
,
217 GetCurrentSerialEventTarget(), __func__
,
218 [self
](RedirectToRealChannelPromise::ResolveOrRejectValue
&&
219 aValue
) -> RefPtr
<RedirectToRealChannelPromise
> {
220 MOZ_ASSERT(aValue
.IsResolve());
221 nsresult rv
= aValue
.ResolveValue();
223 self
->DisconnectChildListeners(rv
, rv
);
225 self
->mLoadGroup
= nullptr;
226 self
->mListener
= nullptr;
227 self
->mCallbacks
= nullptr;
228 self
->RemoveObserver();
230 MakeRefPtr
<RedirectToRealChannelPromise::Private
>(
232 p
->UseDirectTaskDispatch(__func__
);
233 p
->ResolveOrReject(std::move(aValue
), __func__
);
236 // We chain the promise the DLL is waiting on to the one returned by
237 // RedirectToRealChannel. As soon as the promise returned is
238 // resolved or rejected, so will the DLL's promise.
239 p
->ChainTo(aResolveValue
.mPromise
.forget(), __func__
);
241 [self
](DocumentLoadListener::OpenPromiseFailedType
&& aRejectValue
) {
242 // If this is a normal failure, then we want to disconnect our listeners
243 // and notify them of the failure. If this is a process switch, then we
244 // can just ignore it silently, and trust that the switch will shut down
245 // our docshell and cancel us when it's ready.
246 if (!aRejectValue
.mContinueNavigating
) {
247 self
->DisconnectChildListeners(aRejectValue
.mStatus
,
248 aRejectValue
.mLoadGroupStatus
);
250 self
->RemoveObserver();
255 NS_IMETHODIMP
ParentProcessDocumentChannel::Cancel(nsresult aStatus
) {
256 return CancelWithReason(aStatus
, "ParentProcessDocumentChannel::Cancel"_ns
);
259 NS_IMETHODIMP
ParentProcessDocumentChannel::CancelWithReason(
260 nsresult aStatusCode
, const nsACString
& aReason
) {
261 LOG(("ParentProcessDocumentChannel CancelWithReason [this=%p]", this));
267 // This will force the DocumentListener to abort the promise if there's one
269 mDocumentLoadListener
->Cancel(aStatusCode
, aReason
);
274 void ParentProcessDocumentChannel::RemoveObserver() {
275 if (nsCOMPtr
<nsIObserverService
> observerService
=
276 mozilla::services::GetObserverService()) {
277 observerService
->RemoveObserver(this, NS_HTTP_ON_MODIFY_REQUEST_TOPIC
);
281 ////////////////////////////////////////////////////////////////////////////////
283 ////////////////////////////////////////////////////////////////////////////////
286 ParentProcessDocumentChannel::Observe(nsISupports
* aSubject
, const char* aTopic
,
287 const char16_t
* aData
) {
288 MOZ_ASSERT(NS_IsMainThread());
290 if (mRequestObserversCalled
) {
291 // We have already emitted the event, we don't want to emit it again.
292 // We only care about forwarding the first NS_HTTP_ON_MODIFY_REQUEST_TOPIC
296 nsCOMPtr
<nsIChannel
> channel
= do_QueryInterface(aSubject
);
297 if (!channel
|| mDocumentLoadListener
->GetChannel() != channel
) {
298 // Not a channel we are interested with.
301 LOG(("DocumentChannelParent Observe [this=%p aChannel=%p]", this,
303 if (!nsCRT::strcmp(aTopic
, NS_HTTP_ON_MODIFY_REQUEST_TOPIC
)) {
304 mRequestObserversCalled
= true;
305 gHttpHandler
->OnModifyDocumentRequest(this);
312 } // namespace mozilla