no bug - Bumping Firefox l10n changesets r=release a=l10n-bump DONTBUILD CLOSED TREE
[gecko.git] / netwerk / ipc / DocumentChannelChild.cpp
blobead243eaa25c18bfdd33deb9a0bdc3bd3d0ea038
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.uriModified() = mUriModified;
123 docArgs.isEmbeddingBlockedError() = mIsEmbeddingBlockedError;
125 ipcElementCreationArgs = docArgs;
126 break;
129 case ExtContentPolicy::TYPE_OBJECT: {
130 ObjectCreationArgs objectArgs;
131 objectArgs.embedderInnerWindowId() = InnerWindowIDForExtantDoc(docShell);
132 objectArgs.loadFlags() = mLoadFlags;
133 objectArgs.contentPolicyType() = mLoadInfo->InternalContentPolicyType();
134 objectArgs.isUrgentStart() = UserActivation::IsHandlingUserInput();
136 ipcElementCreationArgs = objectArgs;
137 break;
140 default:
141 MOZ_ASSERT_UNREACHABLE("unsupported content policy type");
142 return NS_ERROR_FAILURE;
145 switch (mLoadInfo->GetExternalContentPolicyType()) {
146 case ExtContentPolicy::TYPE_DOCUMENT:
147 case ExtContentPolicy::TYPE_SUBDOCUMENT:
148 MOZ_ALWAYS_SUCCEEDS(loadingContext->SetCurrentLoadIdentifier(
149 Some(mLoadState->GetLoadIdentifier())));
150 break;
152 default:
153 break;
156 mLoadState->AssertProcessCouldTriggerLoadIfSystem();
158 DocumentChannelCreationArgs args(
159 mozilla::WrapNotNull(mLoadState), TimeStamp::Now(), mChannelId, mCacheKey,
160 mTiming, ipcClientInfo, ipcElementCreationArgs,
161 loadingContext->GetParentInitiatedNavigationEpoch());
163 gNeckoChild->SendPDocumentChannelConstructor(this, loadingContext, args);
165 mIsPending = true;
166 mWasOpened = true;
167 mListener = listener;
169 return NS_OK;
172 IPCResult DocumentChannelChild::RecvFailedAsyncOpen(
173 const nsresult& aStatusCode) {
174 if (aStatusCode == NS_ERROR_RECURSIVE_DOCUMENT_LOAD) {
175 // This exists so that we are able to fire an error event
176 // for when there are too many recursive iframe or object loads.
177 // This is an incomplete solution, because right now we don't have a unified
178 // way of firing error events due to errors in document channel.
179 // This should be fixed in bug 1629201.
180 MOZ_DIAGNOSTIC_ASSERT(mLoadingContext);
181 if (RefPtr<Element> embedder = mLoadingContext->GetEmbedderElement()) {
182 if (RefPtr<nsFrameLoaderOwner> flo = do_QueryObject(embedder)) {
183 if (RefPtr<nsFrameLoader> fl = flo->GetFrameLoader()) {
184 fl->FireErrorEvent();
189 ShutdownListeners(aStatusCode);
190 return IPC_OK();
193 IPCResult DocumentChannelChild::RecvDisconnectChildListeners(
194 const nsresult& aStatus, const nsresult& aLoadGroupStatus,
195 bool aContinueNavigating) {
196 // If this disconnect is not due to a process switch, perform the disconnect
197 // immediately.
198 if (!aContinueNavigating) {
199 DisconnectChildListeners(aStatus, aLoadGroupStatus);
200 return IPC_OK();
203 // Otherwise, the disconnect will occur later using some other mechanism,
204 // depending on what's happening to the loading DocShell. If this is a
205 // toplevel navigation, and this BrowsingContext enters the BFCache, we will
206 // cancel this channel when the PageHide event is firing, whereas if it does
207 // not enter BFCache (e.g. due to being an object, subframe or non-bfcached
208 // toplevel navigation), we will cancel this channel when the DocShell is
209 // destroyed.
210 nsDocShell* shell = GetDocShell();
211 if (mLoadInfo->GetExternalContentPolicyType() ==
212 ExtContentPolicy::TYPE_DOCUMENT &&
213 shell) {
214 MOZ_ASSERT(shell->GetBrowsingContext()->IsTop());
215 if (mozilla::SessionHistoryInParent() &&
216 shell->GetBrowsingContext()->IsInBFCache()) {
217 DisconnectChildListeners(aStatus, aLoadGroupStatus);
218 } else {
219 // Tell the DocShell which channel to cancel if it enters the BFCache.
220 shell->SetChannelToDisconnectOnPageHide(mChannelId);
224 return IPC_OK();
227 IPCResult DocumentChannelChild::RecvRedirectToRealChannel(
228 RedirectToRealChannelArgs&& aArgs,
229 nsTArray<Endpoint<extensions::PStreamFilterParent>>&& aEndpoints,
230 RedirectToRealChannelResolver&& aResolve) {
231 LOG(("DocumentChannelChild RecvRedirectToRealChannel [this=%p, uri=%s]", this,
232 aArgs.uri()->GetSpecOrDefault().get()));
234 // The document that created the cspToInherit.
235 // This is used when deserializing LoadInfo from the parent
236 // process, since we can't serialize Documents directly.
237 // TODO: For a fission OOP iframe this will be unavailable,
238 // as will the loadingContext computed in LoadInfoArgsToLoadInfo.
239 // Figure out if we need these for cross-origin subdocs.
240 RefPtr<dom::Document> cspToInheritLoadingDocument;
241 nsCOMPtr<nsIContentSecurityPolicy> policy = mLoadState->Csp();
242 if (policy) {
243 nsWeakPtr ctx =
244 static_cast<nsCSPContext*>(policy.get())->GetLoadingContext();
245 cspToInheritLoadingDocument = do_QueryReferent(ctx);
247 nsCOMPtr<nsILoadInfo> loadInfo;
248 MOZ_ALWAYS_SUCCEEDS(LoadInfoArgsToLoadInfo(aArgs.loadInfo(), NOT_REMOTE_TYPE,
249 cspToInheritLoadingDocument,
250 getter_AddRefs(loadInfo)));
252 mRedirectResolver = std::move(aResolve);
254 nsCOMPtr<nsIChannel> newChannel;
255 MOZ_ASSERT((aArgs.loadStateInternalLoadFlags() &
256 nsDocShell::InternalLoad::INTERNAL_LOAD_FLAGS_IS_SRCDOC) ||
257 aArgs.srcdocData().IsVoid());
258 nsresult rv = nsDocShell::CreateRealChannelForDocument(
259 getter_AddRefs(newChannel), aArgs.uri(), loadInfo, nullptr,
260 aArgs.newLoadFlags(), aArgs.srcdocData(), aArgs.baseUri());
261 if (newChannel) {
262 newChannel->SetLoadGroup(mLoadGroup);
265 if (RefPtr<HttpBaseChannel> httpChannel = do_QueryObject(newChannel)) {
266 httpChannel->SetEarlyHints(std::move(aArgs.earlyHints()));
267 httpChannel->SetEarlyHintLinkType(aArgs.earlyHintLinkType());
270 // This is used to report any errors back to the parent by calling
271 // CrossProcessRedirectFinished.
272 auto scopeExit = MakeScopeExit([&]() {
273 mRedirectResolver(rv);
274 mRedirectResolver = nullptr;
277 if (NS_FAILED(rv)) {
278 return IPC_OK();
281 if (nsCOMPtr<nsIHttpChannel> httpChannel = do_QueryInterface(newChannel)) {
282 rv = httpChannel->SetChannelId(aArgs.channelId());
284 if (NS_FAILED(rv)) {
285 return IPC_OK();
288 rv = newChannel->SetOriginalURI(aArgs.originalURI());
289 if (NS_FAILED(rv)) {
290 return IPC_OK();
293 if (nsCOMPtr<nsIHttpChannelInternal> httpChannelInternal =
294 do_QueryInterface(newChannel)) {
295 rv = httpChannelInternal->SetRedirectMode(aArgs.redirectMode());
297 if (NS_FAILED(rv)) {
298 return IPC_OK();
301 newChannel->SetNotificationCallbacks(mCallbacks);
303 if (aArgs.init()) {
304 HttpBaseChannel::ReplacementChannelConfig config(*aArgs.init());
305 HttpBaseChannel::ConfigureReplacementChannel(
306 newChannel, config,
307 HttpBaseChannel::ReplacementReason::DocumentChannel);
310 if (aArgs.contentDisposition()) {
311 newChannel->SetContentDisposition(*aArgs.contentDisposition());
314 if (aArgs.contentDispositionFilename()) {
315 newChannel->SetContentDispositionFilename(
316 *aArgs.contentDispositionFilename());
319 nsDocShell* docShell = GetDocShell();
320 if (docShell && aArgs.loadingSessionHistoryInfo().isSome()) {
321 docShell->SetLoadingSessionHistoryInfo(
322 aArgs.loadingSessionHistoryInfo().ref());
325 // transfer any properties. This appears to be entirely a content-side
326 // interface and isn't copied across to the parent. Copying the values
327 // for this from this into the new actor will work, since the parent
328 // won't have the right details anyway.
329 // TODO: What about the process switch equivalent
330 // (ContentChild::RecvCrossProcessRedirect)? In that case there is no local
331 // existing actor in the destination process... We really need all information
332 // to go up to the parent, and then come down to the new child actor.
333 if (nsCOMPtr<nsIWritablePropertyBag> bag = do_QueryInterface(newChannel)) {
334 nsHashPropertyBag::CopyFrom(bag, aArgs.properties());
337 // connect parent.
338 nsCOMPtr<nsIChildChannel> childChannel = do_QueryInterface(newChannel);
339 if (childChannel) {
340 rv = childChannel->ConnectParent(
341 aArgs.registrarId()); // creates parent channel
342 if (NS_FAILED(rv)) {
343 return IPC_OK();
346 mRedirectChannel = newChannel;
347 mStreamFilterEndpoints = std::move(aEndpoints);
349 rv = gHttpHandler->AsyncOnChannelRedirect(this, newChannel,
350 aArgs.redirectFlags(),
351 GetMainThreadSerialEventTarget());
353 if (NS_SUCCEEDED(rv)) {
354 scopeExit.release();
357 // scopeExit will call CrossProcessRedirectFinished(rv) here
358 return IPC_OK();
361 IPCResult DocumentChannelChild::RecvUpgradeObjectLoad(
362 UpgradeObjectLoadResolver&& aResolve) {
363 // We're doing a load for an <object> or <embed> element if we got here.
364 MOZ_ASSERT(mLoadFlags & nsIRequest::LOAD_HTML_OBJECT_DATA,
365 "Should have LOAD_HTML_OBJECT_DATA set");
366 MOZ_ASSERT(!(mLoadFlags & nsIChannel::LOAD_DOCUMENT_URI),
367 "Shouldn't be a LOAD_DOCUMENT_URI load yet");
368 MOZ_ASSERT(mLoadInfo->GetExternalContentPolicyType() ==
369 ExtContentPolicy::TYPE_OBJECT,
370 "Should have the TYPE_OBJECT content policy type");
372 // If our load has already failed, or been cancelled, abort this attempt to
373 // upgade the load.
374 if (NS_FAILED(mStatus)) {
375 aResolve(nullptr);
376 return IPC_OK();
379 nsCOMPtr<nsIObjectLoadingContent> loadingContent;
380 NS_QueryNotificationCallbacks(this, loadingContent);
381 if (!loadingContent) {
382 return IPC_FAIL(this, "Channel is not for ObjectLoadingContent!");
385 // We're upgrading to a document channel now. Add the LOAD_DOCUMENT_URI flag
386 // after-the-fact.
387 mLoadFlags |= nsIChannel::LOAD_DOCUMENT_URI;
389 RefPtr<BrowsingContext> browsingContext;
390 nsresult rv = loadingContent->UpgradeLoadToDocument(
391 this, getter_AddRefs(browsingContext));
392 if (NS_FAILED(rv) || !browsingContext) {
393 // Oops! Looks like something went wrong, so let's bail out.
394 mLoadFlags &= ~nsIChannel::LOAD_DOCUMENT_URI;
395 aResolve(nullptr);
396 return IPC_OK();
399 aResolve(browsingContext);
400 return IPC_OK();
403 NS_IMETHODIMP
404 DocumentChannelChild::OnRedirectVerifyCallback(nsresult aStatusCode) {
405 LOG(
406 ("DocumentChannelChild OnRedirectVerifyCallback [this=%p, "
407 "aRv=0x%08" PRIx32 " ]",
408 this, static_cast<uint32_t>(aStatusCode)));
409 nsCOMPtr<nsIChannel> redirectChannel = std::move(mRedirectChannel);
410 RedirectToRealChannelResolver redirectResolver = std::move(mRedirectResolver);
412 // If we've already shut down, then just notify the parent that
413 // we're done.
414 if (NS_FAILED(mStatus)) {
415 redirectChannel->SetNotificationCallbacks(nullptr);
416 redirectResolver(aStatusCode);
417 return NS_OK;
420 nsresult rv = aStatusCode;
421 if (NS_SUCCEEDED(rv)) {
422 if (nsCOMPtr<nsIChildChannel> childChannel =
423 do_QueryInterface(redirectChannel)) {
424 rv = childChannel->CompleteRedirectSetup(mListener);
425 } else {
426 rv = redirectChannel->AsyncOpen(mListener);
428 } else {
429 redirectChannel->SetNotificationCallbacks(nullptr);
432 for (auto& endpoint : mStreamFilterEndpoints) {
433 extensions::StreamFilterParent::Attach(redirectChannel,
434 std::move(endpoint));
437 redirectResolver(rv);
439 if (NS_FAILED(rv)) {
440 ShutdownListeners(rv);
441 return NS_OK;
444 if (mLoadGroup) {
445 mLoadGroup->RemoveRequest(this, nullptr, NS_BINDING_REDIRECTED);
447 mCallbacks = nullptr;
448 mListener = nullptr;
450 // This calls NeckoChild::DeallocPDocumentChannel(), which deletes |this| if
451 // IPDL holds the last reference. Don't rely on |this| existing after here!
452 if (CanSend()) {
453 Send__delete__(this);
456 return NS_OK;
459 NS_IMETHODIMP
460 DocumentChannelChild::Cancel(nsresult aStatusCode) {
461 return CancelWithReason(aStatusCode, "DocumentChannelChild::Cancel"_ns);
464 NS_IMETHODIMP DocumentChannelChild::CancelWithReason(
465 nsresult aStatusCode, const nsACString& aReason) {
466 if (mCanceled) {
467 return NS_OK;
470 mCanceled = true;
471 if (CanSend()) {
472 SendCancel(aStatusCode, aReason);
475 ShutdownListeners(aStatusCode);
477 return NS_OK;
480 } // namespace net
481 } // namespace mozilla
483 #undef LOG