Backed out 2 changesets (bug 1943998) for causing wd failures @ phases.py CLOSED...
[gecko.git] / netwerk / ipc / DocumentChannelChild.cpp
blob6040e9769c23eab0735956e741eebff6c33c6c1a
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 "DocumentChannelChild.h"
10 #include "mozilla/dom/Document.h"
11 #include "mozilla/dom/RemoteType.h"
12 #include "mozilla/extensions/StreamFilterParent.h"
13 #include "mozilla/ipc/Endpoint.h"
14 #include "mozilla/net/HttpBaseChannel.h"
15 #include "mozilla/net/NeckoChild.h"
16 #include "mozilla/ScopeExit.h"
17 #include "mozilla/StaticPrefs_fission.h"
18 #include "nsHashPropertyBag.h"
19 #include "nsIHttpChannelInternal.h"
20 #include "nsIObjectLoadingContent.h"
21 #include "nsIXULRuntime.h"
22 #include "nsIWritablePropertyBag.h"
23 #include "nsFrameLoader.h"
24 #include "nsFrameLoaderOwner.h"
25 #include "nsQueryObject.h"
26 #include "nsDocShellLoadState.h"
28 using namespace mozilla::dom;
29 using namespace mozilla::ipc;
31 extern mozilla::LazyLogModule gDocumentChannelLog;
32 #define LOG(fmt) MOZ_LOG(gDocumentChannelLog, mozilla::LogLevel::Verbose, fmt)
34 namespace mozilla {
35 namespace net {
37 //-----------------------------------------------------------------------------
38 // DocumentChannelChild::nsISupports
40 NS_INTERFACE_MAP_BEGIN(DocumentChannelChild)
41 NS_INTERFACE_MAP_ENTRY(nsIAsyncVerifyRedirectCallback)
42 NS_INTERFACE_MAP_END_INHERITING(DocumentChannel)
44 NS_IMPL_ADDREF_INHERITED(DocumentChannelChild, DocumentChannel)
45 NS_IMPL_RELEASE_INHERITED(DocumentChannelChild, DocumentChannel)
47 DocumentChannelChild::DocumentChannelChild(nsDocShellLoadState* aLoadState,
48 net::LoadInfo* aLoadInfo,
49 nsLoadFlags aLoadFlags,
50 uint32_t aCacheKey,
51 bool aUriModified,
52 bool aIsEmbeddingBlockedError)
53 : DocumentChannel(aLoadState, aLoadInfo, aLoadFlags, aCacheKey,
54 aUriModified, aIsEmbeddingBlockedError) {
55 mLoadingContext = nullptr;
56 LOG(("DocumentChannelChild ctor [this=%p, uri=%s]", this,
57 aLoadState->URI()->GetSpecOrDefault().get()));
60 DocumentChannelChild::~DocumentChannelChild() {
61 LOG(("DocumentChannelChild dtor [this=%p]", this));
64 NS_IMETHODIMP
65 DocumentChannelChild::AsyncOpen(nsIStreamListener* aListener) {
66 nsresult rv = NS_OK;
68 nsCOMPtr<nsIStreamListener> listener = aListener;
70 NS_ENSURE_TRUE(gNeckoChild, NS_ERROR_FAILURE);
71 NS_ENSURE_ARG_POINTER(listener);
72 NS_ENSURE_TRUE(!mIsPending, NS_ERROR_IN_PROGRESS);
73 NS_ENSURE_TRUE(!mWasOpened, NS_ERROR_ALREADY_OPENED);
75 // Port checked in parent, but duplicate here so we can return with error
76 // immediately, as we've done since before e10s.
77 rv = NS_CheckPortSafety(mURI);
78 NS_ENSURE_SUCCESS(rv, rv);
80 bool isNotDownload = mLoadState->FileName().IsVoid();
82 // If not a download, add ourselves to the load group
83 if (isNotDownload && mLoadGroup) {
84 // During this call, we can re-enter back into the DocumentChannelChild to
85 // call SetNavigationTiming.
86 mLoadGroup->AddRequest(this, nullptr);
89 if (mCanceled) {
90 // We may have been canceled already, either by on-modify-request
91 // listeners or by load group observers; in that case, don't create IPDL
92 // connection. See nsHttpChannel::AsyncOpen().
93 return mStatus;
96 gHttpHandler->OnOpeningDocumentRequest(this);
98 RefPtr<nsDocShell> docShell = GetDocShell();
99 if (!docShell) {
100 return NS_ERROR_FAILURE;
103 // `loadingContext` is the BC that is initiating the resource load.
104 // For normal subdocument loads, the BC is the one that the subdoc will load
105 // into. For <object>/<embed> it's the embedder doc's BC.
106 RefPtr<BrowsingContext> loadingContext = docShell->GetBrowsingContext();
107 if (!loadingContext || loadingContext->IsDiscarded()) {
108 return NS_ERROR_FAILURE;
110 mLoadingContext = loadingContext;
112 Maybe<IPCClientInfo> ipcClientInfo;
113 if (mInitialClientInfo.isSome()) {
114 ipcClientInfo.emplace(mInitialClientInfo.ref().ToIPC());
117 DocumentChannelElementCreationArgs ipcElementCreationArgs;
118 switch (mLoadInfo->GetExternalContentPolicyType()) {
119 case ExtContentPolicy::TYPE_DOCUMENT:
120 case ExtContentPolicy::TYPE_SUBDOCUMENT: {
121 DocumentCreationArgs docArgs;
122 docArgs.loadFlags() = mLoadFlags;
123 docArgs.uriModified() = mUriModified;
124 docArgs.isEmbeddingBlockedError() = mIsEmbeddingBlockedError;
126 ipcElementCreationArgs = docArgs;
127 break;
130 case ExtContentPolicy::TYPE_OBJECT: {
131 ObjectCreationArgs objectArgs;
132 objectArgs.embedderInnerWindowId() = InnerWindowIDForExtantDoc(docShell);
133 objectArgs.loadFlags() = mLoadFlags;
134 objectArgs.contentPolicyType() = mLoadInfo->InternalContentPolicyType();
135 objectArgs.isUrgentStart() = UserActivation::IsHandlingUserInput();
137 ipcElementCreationArgs = objectArgs;
138 break;
141 default:
142 MOZ_ASSERT_UNREACHABLE("unsupported content policy type");
143 return NS_ERROR_FAILURE;
146 switch (mLoadInfo->GetExternalContentPolicyType()) {
147 case ExtContentPolicy::TYPE_DOCUMENT:
148 case ExtContentPolicy::TYPE_SUBDOCUMENT:
149 MOZ_ALWAYS_SUCCEEDS(loadingContext->SetCurrentLoadIdentifier(
150 Some(mLoadState->GetLoadIdentifier())));
151 break;
153 default:
154 break;
157 mLoadState->AssertProcessCouldTriggerLoadIfSystem();
159 DocumentChannelCreationArgs args(
160 mozilla::WrapNotNull(mLoadState), TimeStamp::Now(), mChannelId, mCacheKey,
161 mTiming, ipcClientInfo, ipcElementCreationArgs,
162 loadingContext->GetParentInitiatedNavigationEpoch());
164 gNeckoChild->SendPDocumentChannelConstructor(this, loadingContext, args);
166 mIsPending = true;
167 mWasOpened = true;
168 mListener = listener;
170 return NS_OK;
173 IPCResult DocumentChannelChild::RecvFailedAsyncOpen(
174 const nsresult& aStatusCode) {
175 if (aStatusCode == NS_ERROR_RECURSIVE_DOCUMENT_LOAD) {
176 // This exists so that we are able to fire an error event
177 // for when there are too many recursive iframe or object loads.
178 // This is an incomplete solution, because right now we don't have a unified
179 // way of firing error events due to errors in document channel.
180 // This should be fixed in bug 1629201.
181 MOZ_DIAGNOSTIC_ASSERT(mLoadingContext);
182 if (RefPtr<Element> embedder = mLoadingContext->GetEmbedderElement()) {
183 if (RefPtr<nsFrameLoaderOwner> flo = do_QueryObject(embedder)) {
184 if (RefPtr<nsFrameLoader> fl = flo->GetFrameLoader()) {
185 fl->FireErrorEvent();
190 ShutdownListeners(aStatusCode);
191 return IPC_OK();
194 IPCResult DocumentChannelChild::RecvDisconnectChildListeners(
195 const nsresult& aStatus, const nsresult& aLoadGroupStatus,
196 bool aContinueNavigating) {
197 // If this disconnect is not due to a process switch, perform the disconnect
198 // immediately.
199 if (!aContinueNavigating) {
200 DisconnectChildListeners(aStatus, aLoadGroupStatus);
201 return IPC_OK();
204 // Otherwise, the disconnect will occur later using some other mechanism,
205 // depending on what's happening to the loading DocShell. If this is a
206 // toplevel navigation, and this BrowsingContext enters the BFCache, we will
207 // cancel this channel when the PageHide event is firing, whereas if it does
208 // not enter BFCache (e.g. due to being an object, subframe or non-bfcached
209 // toplevel navigation), we will cancel this channel when the DocShell is
210 // destroyed.
211 nsDocShell* shell = GetDocShell();
212 if (mLoadInfo->GetExternalContentPolicyType() ==
213 ExtContentPolicy::TYPE_DOCUMENT &&
214 shell) {
215 MOZ_ASSERT(shell->GetBrowsingContext()->IsTop());
216 if (mozilla::SessionHistoryInParent() &&
217 shell->GetBrowsingContext()->IsInBFCache()) {
218 DisconnectChildListeners(aStatus, aLoadGroupStatus);
219 } else {
220 // Tell the DocShell which channel to cancel if it enters the BFCache.
221 shell->SetChannelToDisconnectOnPageHide(mChannelId);
225 return IPC_OK();
228 IPCResult DocumentChannelChild::RecvRedirectToRealChannel(
229 RedirectToRealChannelArgs&& aArgs,
230 nsTArray<Endpoint<extensions::PStreamFilterParent>>&& aEndpoints,
231 RedirectToRealChannelResolver&& aResolve) {
232 LOG(("DocumentChannelChild RecvRedirectToRealChannel [this=%p, uri=%s]", this,
233 aArgs.uri()->GetSpecOrDefault().get()));
235 // The document that created the cspToInherit.
236 // This is used when deserializing LoadInfo from the parent
237 // process, since we can't serialize Documents directly.
238 // TODO: For a fission OOP iframe this will be unavailable,
239 // as will the loadingContext computed in LoadInfoArgsToLoadInfo.
240 // Figure out if we need these for cross-origin subdocs.
241 RefPtr<dom::Document> cspToInheritLoadingDocument;
242 nsCOMPtr<nsIContentSecurityPolicy> policy = mLoadState->Csp();
243 if (policy) {
244 nsWeakPtr ctx =
245 static_cast<nsCSPContext*>(policy.get())->GetLoadingContext();
246 cspToInheritLoadingDocument = do_QueryReferent(ctx);
248 nsCOMPtr<nsILoadInfo> loadInfo;
249 MOZ_ALWAYS_SUCCEEDS(LoadInfoArgsToLoadInfo(aArgs.loadInfo(), NOT_REMOTE_TYPE,
250 cspToInheritLoadingDocument,
251 getter_AddRefs(loadInfo)));
253 mRedirectResolver = std::move(aResolve);
255 nsCOMPtr<nsIChannel> newChannel;
256 MOZ_ASSERT((aArgs.loadStateInternalLoadFlags() &
257 nsDocShell::InternalLoad::INTERNAL_LOAD_FLAGS_IS_SRCDOC) ||
258 aArgs.srcdocData().IsVoid());
259 nsresult rv = nsDocShell::CreateRealChannelForDocument(
260 getter_AddRefs(newChannel), aArgs.uri(), loadInfo, nullptr,
261 aArgs.newLoadFlags(), aArgs.srcdocData(), aArgs.baseUri());
262 if (newChannel) {
263 newChannel->SetLoadGroup(mLoadGroup);
266 if (RefPtr<HttpBaseChannel> httpChannel = do_QueryObject(newChannel)) {
267 httpChannel->SetEarlyHints(std::move(aArgs.earlyHints()));
268 httpChannel->SetEarlyHintLinkType(aArgs.earlyHintLinkType());
271 // This is used to report any errors back to the parent by calling
272 // CrossProcessRedirectFinished.
273 auto scopeExit = MakeScopeExit([&]() {
274 mRedirectResolver(rv);
275 mRedirectResolver = nullptr;
278 if (NS_FAILED(rv)) {
279 return IPC_OK();
282 if (nsCOMPtr<nsIHttpChannel> httpChannel = do_QueryInterface(newChannel)) {
283 rv = httpChannel->SetChannelId(aArgs.channelId());
285 if (NS_FAILED(rv)) {
286 return IPC_OK();
289 rv = newChannel->SetOriginalURI(aArgs.originalURI());
290 if (NS_FAILED(rv)) {
291 return IPC_OK();
294 if (nsCOMPtr<nsIHttpChannelInternal> httpChannelInternal =
295 do_QueryInterface(newChannel)) {
296 rv = httpChannelInternal->SetRedirectMode(aArgs.redirectMode());
298 if (NS_FAILED(rv)) {
299 return IPC_OK();
302 newChannel->SetNotificationCallbacks(mCallbacks);
304 if (aArgs.init()) {
305 HttpBaseChannel::ReplacementChannelConfig config(*aArgs.init());
306 HttpBaseChannel::ConfigureReplacementChannel(
307 newChannel, config,
308 HttpBaseChannel::ReplacementReason::DocumentChannel);
311 if (aArgs.contentDisposition()) {
312 newChannel->SetContentDisposition(*aArgs.contentDisposition());
315 if (aArgs.contentDispositionFilename()) {
316 newChannel->SetContentDispositionFilename(
317 *aArgs.contentDispositionFilename());
320 nsDocShell* docShell = GetDocShell();
321 if (docShell && aArgs.loadingSessionHistoryInfo().isSome()) {
322 docShell->SetLoadingSessionHistoryInfo(
323 aArgs.loadingSessionHistoryInfo().ref());
326 // transfer any properties. This appears to be entirely a content-side
327 // interface and isn't copied across to the parent. Copying the values
328 // for this from this into the new actor will work, since the parent
329 // won't have the right details anyway.
330 // TODO: What about the process switch equivalent
331 // (ContentChild::RecvCrossProcessRedirect)? In that case there is no local
332 // existing actor in the destination process... We really need all information
333 // to go up to the parent, and then come down to the new child actor.
334 if (nsCOMPtr<nsIWritablePropertyBag> bag = do_QueryInterface(newChannel)) {
335 nsHashPropertyBag::CopyFrom(bag, aArgs.properties());
338 // connect parent.
339 nsCOMPtr<nsIChildChannel> childChannel = do_QueryInterface(newChannel);
340 if (childChannel) {
341 rv = childChannel->ConnectParent(
342 aArgs.registrarId()); // creates parent channel
343 if (NS_FAILED(rv)) {
344 return IPC_OK();
347 mRedirectChannel = newChannel;
348 mStreamFilterEndpoints = std::move(aEndpoints);
350 rv = gHttpHandler->AsyncOnChannelRedirect(this, newChannel,
351 aArgs.redirectFlags(),
352 GetMainThreadSerialEventTarget());
354 if (NS_SUCCEEDED(rv)) {
355 scopeExit.release();
358 // scopeExit will call CrossProcessRedirectFinished(rv) here
359 return IPC_OK();
362 IPCResult DocumentChannelChild::RecvUpgradeObjectLoad(
363 UpgradeObjectLoadResolver&& aResolve) {
364 // We're doing a load for an <object> or <embed> element if we got here.
365 MOZ_ASSERT(mLoadFlags & nsIRequest::LOAD_HTML_OBJECT_DATA,
366 "Should have LOAD_HTML_OBJECT_DATA set");
367 MOZ_ASSERT(!(mLoadFlags & nsIChannel::LOAD_DOCUMENT_URI),
368 "Shouldn't be a LOAD_DOCUMENT_URI load yet");
369 MOZ_ASSERT(mLoadInfo->GetExternalContentPolicyType() ==
370 ExtContentPolicy::TYPE_OBJECT,
371 "Should have the TYPE_OBJECT content policy type");
373 // If our load has already failed, or been cancelled, abort this attempt to
374 // upgade the load.
375 if (NS_FAILED(mStatus)) {
376 aResolve(nullptr);
377 return IPC_OK();
380 nsCOMPtr<nsIObjectLoadingContent> loadingContent;
381 NS_QueryNotificationCallbacks(this, loadingContent);
382 if (!loadingContent) {
383 return IPC_FAIL(this, "Channel is not for ObjectLoadingContent!");
386 // We're upgrading to a document channel now. Add the LOAD_DOCUMENT_URI flag
387 // after-the-fact.
388 mLoadFlags |= nsIChannel::LOAD_DOCUMENT_URI;
390 RefPtr<BrowsingContext> browsingContext;
391 nsresult rv = loadingContent->UpgradeLoadToDocument(
392 this, getter_AddRefs(browsingContext));
393 if (NS_FAILED(rv) || !browsingContext) {
394 // Oops! Looks like something went wrong, so let's bail out.
395 mLoadFlags &= ~nsIChannel::LOAD_DOCUMENT_URI;
396 aResolve(nullptr);
397 return IPC_OK();
400 aResolve(browsingContext);
401 return IPC_OK();
404 NS_IMETHODIMP
405 DocumentChannelChild::OnRedirectVerifyCallback(nsresult aStatusCode) {
406 LOG(
407 ("DocumentChannelChild OnRedirectVerifyCallback [this=%p, "
408 "aRv=0x%08" PRIx32 " ]",
409 this, static_cast<uint32_t>(aStatusCode)));
410 nsCOMPtr<nsIChannel> redirectChannel = std::move(mRedirectChannel);
411 RedirectToRealChannelResolver redirectResolver = std::move(mRedirectResolver);
413 // If we've already shut down, then just notify the parent that
414 // we're done.
415 if (NS_FAILED(mStatus)) {
416 redirectChannel->SetNotificationCallbacks(nullptr);
417 redirectResolver(aStatusCode);
418 return NS_OK;
421 nsresult rv = aStatusCode;
422 if (NS_SUCCEEDED(rv)) {
423 if (nsCOMPtr<nsIChildChannel> childChannel =
424 do_QueryInterface(redirectChannel)) {
425 rv = childChannel->CompleteRedirectSetup(mListener);
426 } else {
427 rv = redirectChannel->AsyncOpen(mListener);
429 } else {
430 redirectChannel->SetNotificationCallbacks(nullptr);
433 for (auto& endpoint : mStreamFilterEndpoints) {
434 extensions::StreamFilterParent::Attach(redirectChannel,
435 std::move(endpoint));
438 redirectResolver(rv);
440 if (NS_FAILED(rv)) {
441 ShutdownListeners(rv);
442 return NS_OK;
445 if (mLoadGroup) {
446 mLoadGroup->RemoveRequest(this, nullptr, NS_BINDING_REDIRECTED);
448 mCallbacks = nullptr;
449 mListener = nullptr;
451 // This calls NeckoChild::DeallocPDocumentChannel(), which deletes |this| if
452 // IPDL holds the last reference. Don't rely on |this| existing after here!
453 if (CanSend()) {
454 Send__delete__(this);
457 return NS_OK;
460 NS_IMETHODIMP
461 DocumentChannelChild::Cancel(nsresult aStatusCode) {
462 return CancelWithReason(aStatusCode, "DocumentChannelChild::Cancel"_ns);
465 NS_IMETHODIMP DocumentChannelChild::CancelWithReason(
466 nsresult aStatusCode, const nsACString& aReason) {
467 if (mCanceled) {
468 return NS_OK;
471 mCanceled = true;
472 if (CanSend()) {
473 SendCancel(aStatusCode, aReason);
476 ShutdownListeners(aStatusCode);
478 return NS_OK;
481 } // namespace net
482 } // namespace mozilla
484 #undef LOG