Backed out changeset 5669c20b0617 (bug 1903669) for causing crashtest failures on...
[gecko.git] / netwerk / ipc / ParentProcessDocumentChannel.cpp
blobed9decac72d2ca0c8ce5e9fb1c10ad1b4c62be6b
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"
14 #include "nsCRT.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)
25 namespace mozilla {
26 namespace net {
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);
58 if (mLoadGroup) {
59 channel->SetLoadGroup(mLoadGroup);
62 if (XRE_IsE10sParentProcess()) {
63 nsCOMPtr<nsIURI> uri;
64 MOZ_ALWAYS_SUCCEEDS(NS_GetFinalChannelURI(channel, getter_AddRefs(uri)));
65 if (!nsDocShell::CanLoadInParentProcess(uri)) {
66 nsAutoCString msg;
67 uri->GetSpec(msg);
68 msg.Insert(
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__);
89 nsresult rv =
90 gHttpHandler->AsyncOnChannelRedirect(this, channel, aRedirectFlags);
91 if (NS_FAILED(rv)) {
92 LOG(
93 ("ParentProcessDocumentChannel RedirectToRealChannel "
94 "AsyncOnChannelRedirect failed [this=%p "
95 "aRv=%d]",
96 this, int(rv)));
97 OnRedirectVerifyCallback(rv);
100 return p;
103 NS_IMETHODIMP
104 ParentProcessDocumentChannel::OnRedirectVerifyCallback(nsresult aResult) {
105 LOG(
106 ("ParentProcessDocumentChannel OnRedirectVerifyCallback [this=%p "
107 "aResult=%d]",
108 this, int(aResult)));
110 MOZ_ASSERT(mDocumentLoadListener);
112 if (NS_FAILED(aResult)) {
113 Cancel(aResult);
114 } else if (mCanceled) {
115 aResult = NS_ERROR_ABORT;
116 } else {
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.
122 if (mCanceled) {
123 aResult = NS_ERROR_ABORT;
124 } else {
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__);
139 return NS_OK;
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()));
156 // Add observers.
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()));
172 nsresult rv = NS_OK;
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);
181 } else {
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);
190 if (NS_FAILED(rv)) {
191 MOZ_ASSERT(!promise);
192 mDocumentLoadListener = nullptr;
193 RemoveObserver();
194 return rv;
197 mListener = aListener;
198 if (mLoadGroup) {
199 mLoadGroup->AddRequest(this, nullptr);
202 RefPtr<ParentProcessDocumentChannel> self = this;
203 promise->Then(
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,
215 earlyHints)
216 ->Then(
217 GetCurrentSerialEventTarget(), __func__,
218 [self](RedirectToRealChannelPromise::ResolveOrRejectValue&&
219 aValue) -> RefPtr<RedirectToRealChannelPromise> {
220 MOZ_ASSERT(aValue.IsResolve());
221 nsresult rv = aValue.ResolveValue();
222 if (NS_FAILED(rv)) {
223 self->DisconnectChildListeners(rv, rv);
225 self->mLoadGroup = nullptr;
226 self->mListener = nullptr;
227 self->mCallbacks = nullptr;
228 self->RemoveObserver();
229 auto p =
230 MakeRefPtr<RedirectToRealChannelPromise::Private>(
231 __func__);
232 p->UseDirectTaskDispatch(__func__);
233 p->ResolveOrReject(std::move(aValue), __func__);
234 return p;
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();
252 return NS_OK;
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));
262 if (mCanceled) {
263 return NS_OK;
266 mCanceled = true;
267 // This will force the DocumentListener to abort the promise if there's one
268 // pending.
269 mDocumentLoadListener->Cancel(aStatusCode, aReason);
271 return NS_OK;
274 void ParentProcessDocumentChannel::RemoveObserver() {
275 if (nsCOMPtr<nsIObserverService> observerService =
276 mozilla::services::GetObserverService()) {
277 observerService->RemoveObserver(this, NS_HTTP_ON_MODIFY_REQUEST_TOPIC);
281 ////////////////////////////////////////////////////////////////////////////////
282 // nsIObserver
283 ////////////////////////////////////////////////////////////////////////////////
285 NS_IMETHODIMP
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
293 // encountered.
294 return NS_OK;
296 nsCOMPtr<nsIChannel> channel = do_QueryInterface(aSubject);
297 if (!channel || mDocumentLoadListener->GetChannel() != channel) {
298 // Not a channel we are interested with.
299 return NS_OK;
301 LOG(("DocumentChannelParent Observe [this=%p aChannel=%p]", this,
302 channel.get()));
303 if (!nsCRT::strcmp(aTopic, NS_HTTP_ON_MODIFY_REQUEST_TOPIC)) {
304 mRequestObserversCalled = true;
305 gHttpHandler->OnModifyDocumentRequest(this);
308 return NS_OK;
311 } // namespace net
312 } // namespace mozilla
314 #undef LOG