DocumentChannel.cpp (15410B)
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 "mozilla/net/DocumentChannel.h" 9 10 #include <inttypes.h> 11 #include "mozIDOMWindow.h" 12 #include "mozilla/AlreadyAddRefed.h" 13 #include "mozilla/Assertions.h" 14 #include "mozilla/LoadInfo.h" 15 #include "mozilla/Logging.h" 16 #include "mozilla/RefPtr.h" 17 #include "mozilla/TimeStamp.h" 18 #include "mozilla/dom/Document.h" 19 #include "mozilla/net/DocumentChannelChild.h" 20 #include "mozilla/net/ParentProcessDocumentChannel.h" 21 #include "nsCOMPtr.h" 22 #include "nsDebug.h" 23 #include "nsDocShell.h" 24 #include "nsDocShellLoadState.h" 25 #include "nsHttpHandler.h" 26 #include "nsIContentPolicy.h" 27 #include "nsIInterfaceRequestor.h" 28 #include "nsILoadContext.h" 29 #include "nsILoadGroup.h" 30 #include "nsILoadInfo.h" 31 #include "nsIStreamListener.h" 32 #include "nsIURI.h" 33 #include "nsLoadGroup.h" 34 #include "nsMimeTypes.h" 35 #include "nsNetUtil.h" 36 #include "nsPIDOMWindow.h" 37 #include "nsPIDOMWindowInlines.h" 38 #include "nsStringFwd.h" 39 #include "nsThreadUtils.h" 40 #include "nsXULAppAPI.h" 41 #include "nscore.h" 42 43 using namespace mozilla::dom; 44 using namespace mozilla::ipc; 45 46 extern mozilla::LazyLogModule gDocumentChannelLog; 47 #define LOG(fmt) MOZ_LOG(gDocumentChannelLog, mozilla::LogLevel::Verbose, fmt) 48 49 namespace mozilla { 50 namespace net { 51 52 //----------------------------------------------------------------------------- 53 // DocumentChannel::nsISupports 54 55 NS_IMPL_ADDREF(DocumentChannel) 56 NS_IMPL_RELEASE(DocumentChannel) 57 58 NS_INTERFACE_MAP_BEGIN(DocumentChannel) 59 NS_INTERFACE_MAP_ENTRY(nsIRequest) 60 NS_INTERFACE_MAP_ENTRY(nsIChannel) 61 NS_INTERFACE_MAP_ENTRY(nsIIdentChannel) 62 NS_INTERFACE_MAP_ENTRY_CONCRETE(DocumentChannel) 63 NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIRequest) 64 NS_INTERFACE_MAP_END 65 66 DocumentChannel::DocumentChannel(nsDocShellLoadState* aLoadState, 67 net::LoadInfo* aLoadInfo, 68 nsLoadFlags aLoadFlags, uint32_t aCacheKey, 69 bool aUriModified, 70 bool aIsEmbeddingBlockedError) 71 : mLoadState(aLoadState), 72 mCacheKey(aCacheKey), 73 mLoadFlags(aLoadFlags), 74 mURI(aLoadState->URI()), 75 mLoadInfo(aLoadInfo), 76 mUriModified(aUriModified), 77 mIsEmbeddingBlockedError(aIsEmbeddingBlockedError) { 78 LOG(("DocumentChannel ctor [this=%p, uri=%s]", this, 79 aLoadState->URI()->GetSpecOrDefault().get())); 80 RefPtr<nsHttpHandler> handler = nsHttpHandler::GetInstance(); 81 mChannelId = handler->NewChannelId(); 82 } 83 84 NS_IMETHODIMP 85 DocumentChannel::AsyncOpen(nsIStreamListener* aListener) { 86 MOZ_CRASH("If we get here, something is broken"); 87 return NS_ERROR_NOT_IMPLEMENTED; 88 } 89 90 void DocumentChannel::ShutdownListeners(nsresult aStatusCode) { 91 LOG(("DocumentChannel ShutdownListeners [this=%p, status=%" PRIx32 "]", this, 92 static_cast<uint32_t>(aStatusCode))); 93 mStatus = aStatusCode; 94 95 nsCOMPtr<nsIStreamListener> listener = mListener; 96 if (listener) { 97 listener->OnStartRequest(this); 98 } 99 100 mIsPending = false; 101 102 listener = mListener; // it might have changed! 103 nsCOMPtr<nsILoadGroup> loadGroup = mLoadGroup; 104 105 mListener = nullptr; 106 mLoadGroup = nullptr; 107 mCallbacks = nullptr; 108 109 NS_DispatchToMainThread(NS_NewRunnableFunction( 110 "DocumentChannel::ShutdownListeners", [=, self = RefPtr{this}] { 111 if (listener) { 112 listener->OnStopRequest(self, aStatusCode); 113 } 114 115 if (loadGroup) { 116 loadGroup->RemoveRequest(self, nullptr, aStatusCode); 117 } 118 })); 119 120 DeleteIPDL(); 121 } 122 123 void DocumentChannel::DisconnectChildListeners( 124 const nsresult& aStatus, const nsresult& aLoadGroupStatus) { 125 MOZ_ASSERT(NS_FAILED(aStatus)); 126 127 // In the case where the channel was redirected to be downloaded by the 128 // nsExternalHelperAppService in the parent process, we'll be called with a 129 // different aLoadGroupStatus and aStatus. 130 // 131 // Before DocumentChannel was implemented, the channel would have been removed 132 // from the load group by the nsExternalHelperAppService. This simulates that 133 // behaviour by removing the load group when the channel is no longer in use. 134 // 135 // We cannot unconditionally remove the channel from the load group here, as 136 // that may unblock load events too early if new navigations will be started 137 // by channel listeners. See bug 1961008. 138 if (aStatus != aLoadGroupStatus) { 139 MOZ_ASSERT(aStatus == NS_BINDING_RETARGETED); 140 MOZ_ASSERT(NS_SUCCEEDED(aLoadGroupStatus)); 141 142 mStatus = aLoadGroupStatus; 143 if (mLoadGroup) { 144 mLoadGroup->RemoveRequest(this, nullptr, aStatus); 145 mLoadGroup = nullptr; 146 } 147 } 148 149 ShutdownListeners(aStatus); 150 } 151 152 nsDocShell* DocumentChannel::GetDocShell() { 153 nsCOMPtr<nsILoadContext> loadContext; 154 NS_QueryNotificationCallbacks(this, loadContext); 155 if (!loadContext) { 156 return nullptr; 157 } 158 nsCOMPtr<mozIDOMWindowProxy> domWindow; 159 loadContext->GetAssociatedWindow(getter_AddRefs(domWindow)); 160 if (!domWindow) { 161 return nullptr; 162 } 163 auto* pDomWindow = nsPIDOMWindowOuter::From(domWindow); 164 nsIDocShell* docshell = pDomWindow->GetDocShell(); 165 return nsDocShell::Cast(docshell); 166 } 167 168 static bool URIUsesDocChannel(nsIURI* aURI) { 169 if (aURI->SchemeIs("javascript")) { 170 return false; 171 } 172 173 nsCString spec = aURI->GetSpecOrDefault(); 174 return 175 #ifdef MOZ_WIDGET_ANDROID 176 !spec.EqualsLiteral("about:crashcontentjava") && 177 #endif 178 !spec.EqualsLiteral("about:crashcontent"); 179 } 180 181 bool DocumentChannel::CanUseDocumentChannel(nsIURI* aURI) { 182 // We want to use DocumentChannel if we're using a supported scheme. 183 return URIUsesDocChannel(aURI); 184 } 185 186 /* static */ 187 already_AddRefed<DocumentChannel> DocumentChannel::CreateForDocument( 188 nsDocShellLoadState* aLoadState, class LoadInfo* aLoadInfo, 189 nsLoadFlags aLoadFlags, nsIInterfaceRequestor* aNotificationCallbacks, 190 uint32_t aCacheKey, bool aUriModified, bool aIsEmbeddingBlockedError) { 191 RefPtr<DocumentChannel> channel; 192 if (XRE_IsContentProcess()) { 193 channel = 194 new DocumentChannelChild(aLoadState, aLoadInfo, aLoadFlags, aCacheKey, 195 aUriModified, aIsEmbeddingBlockedError); 196 } else { 197 channel = new ParentProcessDocumentChannel( 198 aLoadState, aLoadInfo, aLoadFlags, aCacheKey, aUriModified, 199 aIsEmbeddingBlockedError); 200 } 201 channel->SetNotificationCallbacks(aNotificationCallbacks); 202 return channel.forget(); 203 } 204 205 /* static */ 206 already_AddRefed<DocumentChannel> DocumentChannel::CreateForObject( 207 nsDocShellLoadState* aLoadState, class LoadInfo* aLoadInfo, 208 nsLoadFlags aLoadFlags, nsIInterfaceRequestor* aNotificationCallbacks) { 209 return CreateForDocument(aLoadState, aLoadInfo, aLoadFlags, 210 aNotificationCallbacks, 0, false, false); 211 } 212 213 NS_IMETHODIMP DocumentChannel::SetCanceledReason(const nsACString& aReason) { 214 return SetCanceledReasonImpl(aReason); 215 } 216 217 NS_IMETHODIMP DocumentChannel::GetCanceledReason(nsACString& aReason) { 218 return GetCanceledReasonImpl(aReason); 219 } 220 221 NS_IMETHODIMP DocumentChannel::CancelWithReason(nsresult aStatus, 222 const nsACString& aReason) { 223 return CancelWithReasonImpl(aStatus, aReason); 224 } 225 226 NS_IMETHODIMP 227 DocumentChannel::Cancel(nsresult aStatusCode) { 228 MOZ_CRASH("If we get here, something is broken"); 229 return NS_ERROR_NOT_IMPLEMENTED; 230 } 231 232 NS_IMETHODIMP 233 DocumentChannel::Suspend() { 234 MOZ_CRASH("If we get here, something is broken"); 235 return NS_ERROR_NOT_IMPLEMENTED; 236 } 237 238 NS_IMETHODIMP 239 DocumentChannel::Resume() { 240 MOZ_CRASH("If we get here, something is broken"); 241 return NS_ERROR_NOT_IMPLEMENTED; 242 } 243 244 //----------------------------------------------------------------------------- 245 // Remainder of nsIRequest/nsIChannel. 246 //----------------------------------------------------------------------------- 247 248 NS_IMETHODIMP DocumentChannel::GetNotificationCallbacks( 249 nsIInterfaceRequestor** aCallbacks) { 250 nsCOMPtr<nsIInterfaceRequestor> callbacks(mCallbacks); 251 callbacks.forget(aCallbacks); 252 return NS_OK; 253 } 254 255 NS_IMETHODIMP DocumentChannel::SetNotificationCallbacks( 256 nsIInterfaceRequestor* aNotificationCallbacks) { 257 mCallbacks = aNotificationCallbacks; 258 return NS_OK; 259 } 260 261 NS_IMETHODIMP DocumentChannel::GetLoadGroup(nsILoadGroup** aLoadGroup) { 262 nsCOMPtr<nsILoadGroup> loadGroup(mLoadGroup); 263 loadGroup.forget(aLoadGroup); 264 return NS_OK; 265 } 266 267 NS_IMETHODIMP DocumentChannel::SetLoadGroup(nsILoadGroup* aLoadGroup) { 268 mLoadGroup = aLoadGroup; 269 return NS_OK; 270 } 271 272 NS_IMETHODIMP DocumentChannel::GetStatus(nsresult* aStatus) { 273 *aStatus = mStatus; 274 return NS_OK; 275 } 276 277 NS_IMETHODIMP DocumentChannel::GetName(nsACString& aResult) { 278 if (!mURI) { 279 aResult.Truncate(); 280 return NS_OK; 281 } 282 nsCString spec; 283 nsresult rv = mURI->GetSpec(spec); 284 NS_ENSURE_SUCCESS(rv, rv); 285 286 aResult.AssignLiteral("documentchannel:"); 287 aResult.Append(spec); 288 return NS_OK; 289 } 290 291 NS_IMETHODIMP DocumentChannel::IsPending(bool* aResult) { 292 *aResult = mIsPending; 293 return NS_OK; 294 } 295 296 NS_IMETHODIMP DocumentChannel::GetLoadFlags(nsLoadFlags* aLoadFlags) { 297 *aLoadFlags = mLoadFlags; 298 return NS_OK; 299 } 300 301 NS_IMETHODIMP 302 DocumentChannel::GetTRRMode(nsIRequest::TRRMode* aTRRMode) { 303 return GetTRRModeImpl(aTRRMode); 304 } 305 306 NS_IMETHODIMP 307 DocumentChannel::SetTRRMode(nsIRequest::TRRMode aTRRMode) { 308 return SetTRRModeImpl(aTRRMode); 309 } 310 311 NS_IMETHODIMP DocumentChannel::SetLoadFlags(nsLoadFlags aLoadFlags) { 312 nsLoadFlags mayChange = 0; 313 if (mLoadInfo->GetExternalContentPolicyType() == 314 ExtContentPolicy::TYPE_OBJECT) { 315 // Setting load flags for TYPE_OBJECT is OK, so long as the channel to 316 // parent isn't opened yet, or we're only setting the `LOAD_DOCUMENT_URI` 317 // flag. 318 mayChange = mWasOpened ? LOAD_DOCUMENT_URI : ~0u; 319 } else if (!mWasOpened) { 320 // If we haven't been opened yet, allow the LoadGroup to 321 // set cache control flags inherited from the default channel. 322 mayChange = nsLoadGroup::kInheritedLoadFlags; 323 } 324 325 // Check if we're allowed to adjust these flags. 326 if ((mLoadFlags & ~mayChange) == (aLoadFlags & ~mayChange)) { 327 mLoadFlags = aLoadFlags; 328 return NS_OK; 329 } 330 MOZ_CRASH_UNSAFE_PRINTF( 331 "DocumentChannel::SetLoadFlags: Don't set flags after creation " 332 "(differing flags %x != %x)", 333 (mLoadFlags ^ aLoadFlags) & mLoadFlags, 334 (mLoadFlags ^ aLoadFlags) & aLoadFlags); 335 return NS_OK; 336 } 337 338 NS_IMETHODIMP DocumentChannel::GetOriginalURI(nsIURI** aOriginalURI) { 339 nsCOMPtr<nsIURI> originalURI = 340 mLoadState->OriginalURI() ? mLoadState->OriginalURI() : mLoadState->URI(); 341 originalURI.forget(aOriginalURI); 342 return NS_OK; 343 } 344 345 NS_IMETHODIMP DocumentChannel::SetOriginalURI(nsIURI* aOriginalURI) { 346 MOZ_CRASH("If we get here, something is broken"); 347 return NS_ERROR_NOT_IMPLEMENTED; 348 } 349 350 NS_IMETHODIMP DocumentChannel::GetURI(nsIURI** aURI) { 351 nsCOMPtr<nsIURI> uri(mURI); 352 uri.forget(aURI); 353 return NS_OK; 354 } 355 356 NS_IMETHODIMP DocumentChannel::GetOwner(nsISupports** aOwner) { 357 nsCOMPtr<nsISupports> owner(mOwner); 358 owner.forget(aOwner); 359 return NS_OK; 360 } 361 362 NS_IMETHODIMP DocumentChannel::SetOwner(nsISupports* aOwner) { 363 mOwner = aOwner; 364 return NS_OK; 365 } 366 367 NS_IMETHODIMP DocumentChannel::GetSecurityInfo( 368 nsITransportSecurityInfo** aSecurityInfo) { 369 *aSecurityInfo = nullptr; 370 return NS_OK; 371 } 372 373 NS_IMETHODIMP DocumentChannel::GetContentType(nsACString& aContentType) { 374 // We may be trying to load HTML object data, and have determined that we're 375 // going to be performing a document load. In that case, fake the "text/html" 376 // content type for nsObjectLoadingContent. 377 if ((mLoadFlags & nsIRequest::LOAD_HTML_OBJECT_DATA) && 378 (mLoadFlags & nsIChannel::LOAD_DOCUMENT_URI)) { 379 aContentType = TEXT_HTML; 380 return NS_OK; 381 } 382 383 NS_ERROR("If we get here, something is broken"); 384 return NS_ERROR_NOT_IMPLEMENTED; 385 } 386 387 NS_IMETHODIMP DocumentChannel::SetContentType(const nsACString& aContentType) { 388 MOZ_CRASH("If we get here, something is broken"); 389 return NS_ERROR_NOT_IMPLEMENTED; 390 } 391 392 NS_IMETHODIMP DocumentChannel::GetContentCharset(nsACString& aContentCharset) { 393 MOZ_CRASH("If we get here, something is broken"); 394 return NS_ERROR_NOT_IMPLEMENTED; 395 } 396 397 NS_IMETHODIMP DocumentChannel::SetContentCharset( 398 const nsACString& aContentCharset) { 399 MOZ_CRASH("If we get here, something is broken"); 400 return NS_ERROR_NOT_IMPLEMENTED; 401 } 402 403 NS_IMETHODIMP DocumentChannel::GetContentLength(int64_t* aContentLength) { 404 MOZ_CRASH("If we get here, something is broken"); 405 return NS_ERROR_NOT_IMPLEMENTED; 406 } 407 408 NS_IMETHODIMP DocumentChannel::SetContentLength(int64_t aContentLength) { 409 MOZ_CRASH("If we get here, something is broken"); 410 return NS_ERROR_NOT_IMPLEMENTED; 411 } 412 413 NS_IMETHODIMP DocumentChannel::Open(nsIInputStream** aStream) { 414 MOZ_CRASH("If we get here, something is broken"); 415 return NS_ERROR_NOT_IMPLEMENTED; 416 } 417 418 NS_IMETHODIMP DocumentChannel::GetContentDisposition( 419 uint32_t* aContentDisposition) { 420 MOZ_CRASH("If we get here, something is broken"); 421 return NS_ERROR_NOT_IMPLEMENTED; 422 } 423 424 NS_IMETHODIMP DocumentChannel::SetContentDisposition( 425 uint32_t aContentDisposition) { 426 MOZ_CRASH("If we get here, something is broken"); 427 return NS_ERROR_NOT_IMPLEMENTED; 428 } 429 430 NS_IMETHODIMP DocumentChannel::GetContentDispositionFilename( 431 nsAString& aContentDispositionFilename) { 432 MOZ_CRASH("If we get here, something will be broken"); 433 return NS_ERROR_NOT_IMPLEMENTED; 434 } 435 436 NS_IMETHODIMP DocumentChannel::SetContentDispositionFilename( 437 const nsAString& aContentDispositionFilename) { 438 MOZ_CRASH("If we get here, something will be broken"); 439 return NS_ERROR_NOT_IMPLEMENTED; 440 } 441 442 NS_IMETHODIMP DocumentChannel::GetContentDispositionHeader( 443 nsACString& aContentDispositionHeader) { 444 MOZ_CRASH("If we get here, something is broken"); 445 return NS_ERROR_NOT_IMPLEMENTED; 446 } 447 448 NS_IMETHODIMP DocumentChannel::GetLoadInfo(nsILoadInfo** aLoadInfo) { 449 nsCOMPtr<nsILoadInfo> loadInfo(mLoadInfo); 450 loadInfo.forget(aLoadInfo); 451 return NS_OK; 452 } 453 454 NS_IMETHODIMP DocumentChannel::SetLoadInfo(nsILoadInfo* aLoadInfo) { 455 MOZ_CRASH("If we get here, something is broken"); 456 return NS_ERROR_NOT_IMPLEMENTED; 457 } 458 459 NS_IMETHODIMP DocumentChannel::GetIsDocument(bool* aIsDocument) { 460 return NS_GetIsDocumentChannel(this, aIsDocument); 461 } 462 463 NS_IMETHODIMP DocumentChannel::GetCanceled(bool* aCanceled) { 464 *aCanceled = mCanceled; 465 return NS_OK; 466 } 467 468 //----------------------------------------------------------------------------- 469 // nsIIdentChannel 470 //----------------------------------------------------------------------------- 471 472 NS_IMETHODIMP 473 DocumentChannel::GetChannelId(uint64_t* aChannelId) { 474 *aChannelId = mChannelId; 475 return NS_OK; 476 } 477 478 NS_IMETHODIMP 479 DocumentChannel::SetChannelId(uint64_t aChannelId) { 480 mChannelId = aChannelId; 481 return NS_OK; 482 } 483 484 //----------------------------------------------------------------------------- 485 // Helpers 486 //----------------------------------------------------------------------------- 487 488 uint64_t InnerWindowIDForExtantDoc(nsDocShell* docShell) { 489 if (!docShell) { 490 return 0; 491 } 492 493 Document* doc = docShell->GetExtantDocument(); 494 if (!doc) { 495 return 0; 496 } 497 498 return doc->InnerWindowID(); 499 } 500 501 } // namespace net 502 } // namespace mozilla 503 504 #undef LOG