ParentProcessDocumentChannel.cpp (11659B)
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 2 /* vim: set sw=2 ts=8 et tw=80 : */ 3 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/. */ 7 8 #include "ParentProcessDocumentChannel.h" 9 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" 21 22 extern mozilla::LazyLogModule gDocumentChannelLog; 23 #define LOG(fmt) MOZ_LOG(gDocumentChannelLog, mozilla::LogLevel::Verbose, fmt) 24 25 namespace mozilla { 26 namespace net { 27 28 using RedirectToRealChannelPromise = 29 typename PDocumentChannelParent::RedirectToRealChannelPromise; 30 31 NS_IMPL_ISUPPORTS_INHERITED(ParentProcessDocumentChannel, DocumentChannel, 32 nsIAsyncVerifyRedirectCallback, nsIObserver) 33 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)); 41 } 42 43 ParentProcessDocumentChannel::~ParentProcessDocumentChannel() { 44 LOG(("ParentProcessDocumentChannel dtor [this=%p]", this)); 45 } 46 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); 57 58 if (mLoadGroup) { 59 channel->SetLoadGroup(mLoadGroup); 60 } 61 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__); 73 } 74 } 75 mStreamFilterEndpoints = std::move(aStreamFilterEndpoints); 76 77 if (mDocumentLoadListener->IsDocumentLoad() && 78 mozilla::SessionHistoryInParent() && GetDocShell() && 79 mDocumentLoadListener->GetLoadingSessionHistoryInfo()) { 80 GetDocShell()->SetLoadingSessionHistoryInfo( 81 *mDocumentLoadListener->GetLoadingSessionHistoryInfo()); 82 } 83 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__); 88 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); 98 } 99 100 return p; 101 } 102 103 NS_IMETHODIMP 104 ParentProcessDocumentChannel::OnRedirectVerifyCallback(nsresult aResult) { 105 LOG( 106 ("ParentProcessDocumentChannel OnRedirectVerifyCallback [this=%p " 107 "aResult=%d]", 108 this, int(aResult))); 109 110 MOZ_ASSERT(mDocumentLoadListener); 111 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)); 128 } 129 130 RefPtr<ParentChannelWrapper> wrapper = 131 new ParentChannelWrapper(channel, mListener); 132 133 wrapper->Register(mDocumentLoadListener->GetRedirectChannelId()); 134 } 135 } 136 137 mPromise.Resolve(aResult, __func__); 138 139 return NS_OK; 140 } 141 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); 147 148 bool isDocumentLoad = mLoadInfo->GetExternalContentPolicyType() != 149 ExtContentPolicy::TYPE_OBJECT; 150 151 mDocumentLoadListener = MakeRefPtr<DocumentLoadListener>( 152 docShell->GetBrowsingContext()->Canonical(), isDocumentLoad); 153 LOG(("Created PPDocumentChannel with listener=%p", 154 mDocumentLoadListener.get())); 155 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)); 162 } 163 164 gHttpHandler->OnOpeningDocumentRequest(this); 165 166 if (isDocumentLoad) { 167 // Return value of setting synced field should be checked. See bug 1656492. 168 (void)GetDocShell()->GetBrowsingContext()->SetCurrentLoadIdentifier( 169 Some(mLoadState->GetLoadIdentifier())); 170 } 171 172 nsresult rv = NS_OK; 173 Maybe<dom::ClientInfo> initialClientInfo = mInitialClientInfo; 174 175 RefPtr<DocumentLoadListener::OpenPromise> promise; 176 if (isDocumentLoad) { 177 promise = mDocumentLoadListener->OpenDocument( 178 mLoadState, mLoadFlags, mCacheKey, Some(mChannelId), TimeStamp::Now(), 179 mTiming, std::move(initialClientInfo), 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); 188 } 189 190 if (NS_FAILED(rv)) { 191 MOZ_ASSERT(!promise); 192 mDocumentLoadListener = nullptr; 193 RemoveObserver(); 194 return rv; 195 } 196 197 mListener = aListener; 198 if (mLoadGroup) { 199 mLoadGroup->AddRequest(this, nullptr); 200 } 201 202 RefPtr<ParentProcessDocumentChannel> self = this; 203 promise->Then( 204 GetCurrentSerialEventTarget(), __func__, 205 [self](DocumentLoadListener::OpenPromiseSucceededType&& aResolveValue) { 206 self->mDocumentLoadListener->CancelEarlyHintPreloads(); 207 nsTArray<EarlyHintConnectArgs> earlyHints; 208 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); 224 } 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; 235 }); 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__); 240 }, 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); 249 } 250 self->RemoveObserver(); 251 }); 252 return NS_OK; 253 } 254 255 NS_IMETHODIMP ParentProcessDocumentChannel::Cancel(nsresult aStatus) { 256 return CancelWithReason(aStatus, "ParentProcessDocumentChannel::Cancel"_ns); 257 } 258 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; 264 } 265 266 mCanceled = true; 267 // This will force the DocumentListener to abort the promise if there's one 268 // pending. 269 mDocumentLoadListener->Cancel(aStatusCode, aReason); 270 271 return NS_OK; 272 } 273 274 void ParentProcessDocumentChannel::RemoveObserver() { 275 if (nsCOMPtr<nsIObserverService> observerService = 276 mozilla::services::GetObserverService()) { 277 observerService->RemoveObserver(this, NS_HTTP_ON_MODIFY_REQUEST_TOPIC); 278 } 279 } 280 281 //////////////////////////////////////////////////////////////////////////////// 282 // nsIObserver 283 //////////////////////////////////////////////////////////////////////////////// 284 285 NS_IMETHODIMP 286 ParentProcessDocumentChannel::Observe(nsISupports* aSubject, const char* aTopic, 287 const char16_t* aData) { 288 MOZ_ASSERT(NS_IsMainThread()); 289 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; 295 } 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; 300 } 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); 306 } 307 308 return NS_OK; 309 } 310 311 } // namespace net 312 } // namespace mozilla 313 314 #undef LOG