nsObjectLoadingContent.cpp (66787B)
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */ 3 /* This Source Code Form is subject to the terms of the Mozilla Public 4 * License, v. 2.0. If a copy of the MPL was not distributed with this 5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 6 /* 7 * A base class implementing nsIObjectLoadingContent for use by 8 * various content nodes that want to provide plugin/document/image 9 * loading functionality (eg <embed>, <object>, etc). 10 */ 11 12 // Interface headers 13 #include "imgLoader.h" 14 #include "mozilla/BasePrincipal.h" 15 #include "mozilla/dom/BindContext.h" 16 #include "mozilla/dom/Document.h" 17 #include "nsError.h" 18 #include "nsIAppShell.h" 19 #include "nsIAsyncVerifyRedirectCallback.h" 20 #include "nsIClassOfService.h" 21 #include "nsIConsoleService.h" 22 #include "nsIDocShell.h" 23 #include "nsIExternalProtocolHandler.h" 24 #include "nsIHttpChannel.h" 25 #include "nsINestedURI.h" 26 #include "nsIPermissionManager.h" 27 #include "nsIScriptChannel.h" 28 #include "nsIScriptError.h" 29 #include "nsIURILoader.h" 30 #include "nsScriptSecurityManager.h" 31 #include "nsSubDocumentFrame.h" 32 33 // Util headers 34 #include "mozilla/Logging.h" 35 #include "mozilla/Preferences.h" 36 #include "nsContentPolicyUtils.h" 37 #include "nsContentUtils.h" 38 #include "nsDocShellLoadState.h" 39 #include "nsGkAtoms.h" 40 #include "nsMimeTypes.h" 41 #include "nsNetUtil.h" 42 #include "nsQueryObject.h" 43 #include "nsStyleUtil.h" 44 #include "nsThreadUtils.h" 45 46 // Concrete classes 47 #include "ReferrerInfo.h" 48 #include "mozilla/AsyncEventDispatcher.h" 49 #include "mozilla/BasicEvents.h" 50 #include "mozilla/Components.h" 51 #include "mozilla/EventDispatcher.h" 52 #include "mozilla/IMEStateManager.h" 53 #include "mozilla/LoadInfo.h" 54 #include "mozilla/PresShell.h" 55 #include "mozilla/ProfilerLabels.h" 56 #include "mozilla/StaticPrefs_browser.h" 57 #include "mozilla/StaticPrefs_dom.h" 58 #include "mozilla/dom/BindingUtils.h" 59 #include "mozilla/dom/ContentChild.h" 60 #include "mozilla/dom/Element.h" 61 #include "mozilla/dom/Event.h" 62 #include "mozilla/dom/HTMLEmbedElement.h" 63 #include "mozilla/dom/HTMLObjectElement.h" 64 #include "mozilla/dom/HTMLObjectElementBinding.h" 65 #include "mozilla/dom/PolicyContainer.h" 66 #include "mozilla/dom/ScriptSettings.h" 67 #include "mozilla/dom/UserActivation.h" 68 #include "mozilla/dom/nsCSPContext.h" 69 #include "mozilla/net/DocumentChannel.h" 70 #include "mozilla/net/UrlClassifierFeatureFactory.h" 71 #include "mozilla/widget/IMEData.h" 72 #include "nsChannelClassifier.h" 73 #include "nsFocusManager.h" 74 #include "nsFrameLoader.h" 75 #include "nsIEffectiveTLDService.h" 76 #include "nsObjectLoadingContent.h" 77 #include "nsWidgetsCID.h" 78 79 #ifdef XP_WIN 80 // Thanks so much, Microsoft! :( 81 # ifdef CreateEvent 82 # undef CreateEvent 83 # endif 84 #endif // XP_WIN 85 86 static const char kPrefYoutubeRewrite[] = "plugins.rewrite_youtube_embeds"; 87 88 using namespace mozilla; 89 using namespace mozilla::dom; 90 using namespace mozilla::net; 91 92 static LogModule* GetObjectLog() { 93 static LazyLogModule sLog("objlc"); 94 return sLog; 95 } 96 97 #define LOG(args) MOZ_LOG(GetObjectLog(), mozilla::LogLevel::Debug, args) 98 #define LOG_ENABLED() MOZ_LOG_TEST(GetObjectLog(), mozilla::LogLevel::Debug) 99 100 static bool IsFlashMIME(const nsACString& aMIMEType) { 101 return aMIMEType.LowerCaseEqualsASCII("application/x-shockwave-flash") || 102 aMIMEType.LowerCaseEqualsASCII("application/futuresplash") || 103 aMIMEType.LowerCaseEqualsASCII("application/x-shockwave-flash-test"); 104 } 105 106 static bool IsPluginMIME(const nsACString& aMIMEType) { 107 return IsFlashMIME(aMIMEType) || 108 aMIMEType.LowerCaseEqualsASCII("application/x-test"); 109 } 110 111 /// 112 /// Runnables and helper classes 113 /// 114 115 // Sets a object's mIsLoading bit to false when destroyed 116 class AutoSetLoadingToFalse { 117 public: 118 explicit AutoSetLoadingToFalse(nsObjectLoadingContent* aContent) 119 : mContent(aContent) {} 120 ~AutoSetLoadingToFalse() { mContent->mIsLoading = false; } 121 122 private: 123 nsObjectLoadingContent* mContent; 124 }; 125 126 /// 127 /// Helper functions 128 /// 129 130 bool nsObjectLoadingContent::IsSuccessfulRequest(nsIRequest* aRequest, 131 nsresult* aStatus) { 132 nsresult rv = aRequest->GetStatus(aStatus); 133 if (NS_FAILED(rv) || NS_FAILED(*aStatus)) { 134 return false; 135 } 136 137 // This may still be an error page or somesuch 138 nsCOMPtr<nsIHttpChannel> httpChan(do_QueryInterface(aRequest)); 139 if (httpChan) { 140 bool success; 141 rv = httpChan->GetRequestSucceeded(&success); 142 if (NS_FAILED(rv) || !success) { 143 return false; 144 } 145 } 146 147 // Otherwise, the request is successful 148 return true; 149 } 150 151 static bool CanHandleURI(nsIURI* aURI) { 152 nsAutoCString scheme; 153 if (NS_FAILED(aURI->GetScheme(scheme))) { 154 return false; 155 } 156 157 nsCOMPtr<nsIIOService> ios = mozilla::components::IO::Service(); 158 if (!ios) { 159 return false; 160 } 161 162 nsCOMPtr<nsIProtocolHandler> handler; 163 ios->GetProtocolHandler(scheme.get(), getter_AddRefs(handler)); 164 if (!handler) { 165 return false; 166 } 167 168 nsCOMPtr<nsIExternalProtocolHandler> extHandler = do_QueryInterface(handler); 169 // We can handle this URI if its protocol handler is not the external one 170 return extHandler == nullptr; 171 } 172 173 // Helper for tedious URI equality syntax when one or both arguments may be 174 // null and URIEquals(null, null) should be true 175 static bool inline URIEquals(nsIURI* a, nsIURI* b) { 176 bool equal; 177 return (!a && !b) || (a && b && NS_SUCCEEDED(a->Equals(b, &equal)) && equal); 178 } 179 180 /// 181 /// Member Functions 182 /// 183 184 // Helper to spawn the frameloader. 185 void nsObjectLoadingContent::SetupFrameLoader() { 186 mFrameLoader = nsFrameLoader::Create(AsElement(), mNetworkCreated); 187 MOZ_ASSERT(mFrameLoader, "nsFrameLoader::Create failed"); 188 } 189 190 // Helper to spawn the frameloader and return a pointer to its docshell. 191 already_AddRefed<nsIDocShell> nsObjectLoadingContent::SetupDocShell( 192 nsIURI* aRecursionCheckURI) { 193 SetupFrameLoader(); 194 if (!mFrameLoader) { 195 return nullptr; 196 } 197 198 nsCOMPtr<nsIDocShell> docShell; 199 200 if (aRecursionCheckURI) { 201 nsresult rv = mFrameLoader->CheckForRecursiveLoad(aRecursionCheckURI); 202 if (NS_SUCCEEDED(rv)) { 203 IgnoredErrorResult result; 204 docShell = mFrameLoader->GetDocShell(result); 205 if (result.Failed()) { 206 MOZ_ASSERT_UNREACHABLE("Could not get DocShell from mFrameLoader?"); 207 } 208 } else { 209 LOG(("OBJLC [%p]: Aborting recursive load", this)); 210 } 211 } 212 213 if (!docShell) { 214 mFrameLoader->Destroy(); 215 mFrameLoader = nullptr; 216 return nullptr; 217 } 218 219 return docShell.forget(); 220 } 221 222 void nsObjectLoadingContent::UnbindFromTree() { 223 // Reset state and clear pending events 224 /// XXX(johns): The implementation for GenericFrame notes that ideally we 225 /// would keep the docshell around, but trash the frameloader 226 UnloadObject(); 227 } 228 229 nsObjectLoadingContent::nsObjectLoadingContent() 230 : mType(ObjectType::Loading), 231 mChannelLoaded(false), 232 mNetworkCreated(true), 233 mContentBlockingEnabled(false), 234 mIsStopping(false), 235 mIsLoading(false), 236 mScriptRequested(false), 237 mRewrittenYoutubeEmbed(false) {} 238 239 nsObjectLoadingContent::~nsObjectLoadingContent() { 240 // Should have been unbound from the tree at this point, and 241 // CheckPluginStopEvent keeps us alive 242 if (mFrameLoader) { 243 MOZ_ASSERT_UNREACHABLE( 244 "Should not be tearing down frame loaders at this point"); 245 mFrameLoader->Destroy(); 246 } 247 } 248 249 // nsIRequestObserver 250 NS_IMETHODIMP 251 nsObjectLoadingContent::OnStartRequest(nsIRequest* aRequest) { 252 AUTO_PROFILER_LABEL("nsObjectLoadingContent::OnStartRequest", NETWORK); 253 254 LOG(("OBJLC [%p]: Channel OnStartRequest", this)); 255 256 if (aRequest != mChannel || !aRequest) { 257 // happens when a new load starts before the previous one got here 258 return NS_BINDING_ABORTED; 259 } 260 261 nsCOMPtr<nsIChannel> chan(do_QueryInterface(aRequest)); 262 NS_ASSERTION(chan, "Why is our request not a channel?"); 263 264 nsresult status = NS_OK; 265 bool success = IsSuccessfulRequest(aRequest, &status); 266 267 // If we have already switched to type document, we're doing a 268 // process-switching DocumentChannel load. We should be able to pass down the 269 // load to our inner listener, but should also make sure to update our local 270 // state. 271 if (mType == ObjectType::Document) { 272 if (!mFinalListener) { 273 MOZ_ASSERT_UNREACHABLE( 274 "Already is Document, but don't have final listener yet?"); 275 return NS_BINDING_ABORTED; 276 } 277 278 // If the load looks successful, fix up some of our local state before 279 // forwarding the request to the final URI loader. 280 // 281 // Forward load errors down to the document loader, so we don't tear down 282 // the nsDocShell ourselves. 283 if (success) { 284 LOG(("OBJLC [%p]: OnStartRequest: DocumentChannel request succeeded\n", 285 this)); 286 nsCString channelType; 287 MOZ_ALWAYS_SUCCEEDS(mChannel->GetContentType(channelType)); 288 289 if (GetTypeOfContent(channelType) != ObjectType::Document) { 290 MOZ_CRASH("DocumentChannel request with non-document MIME"); 291 } 292 mContentType = channelType; 293 294 MOZ_ALWAYS_SUCCEEDS( 295 NS_GetFinalChannelURI(mChannel, getter_AddRefs(mURI))); 296 } 297 298 return mFinalListener->OnStartRequest(aRequest); 299 } 300 301 // Otherwise we should be state loading, and call LoadObject with the channel 302 if (mType != ObjectType::Loading) { 303 MOZ_ASSERT_UNREACHABLE("Should be type loading at this point"); 304 return NS_BINDING_ABORTED; 305 } 306 NS_ASSERTION(!mChannelLoaded, "mChannelLoaded set already?"); 307 NS_ASSERTION(!mFinalListener, "mFinalListener exists already?"); 308 309 mChannelLoaded = true; 310 311 if (status == NS_ERROR_BLOCKED_URI) { 312 nsCOMPtr<nsIConsoleService> console( 313 do_GetService("@mozilla.org/consoleservice;1")); 314 if (console) { 315 nsCOMPtr<nsIURI> uri; 316 chan->GetURI(getter_AddRefs(uri)); 317 nsString message = 318 u"Blocking "_ns + 319 NS_ConvertASCIItoUTF16(uri->GetSpecOrDefault().get()) + 320 nsLiteralString( 321 u" since it was found on an internal Firefox blocklist."); 322 console->LogStringMessage(message.get()); 323 } 324 mContentBlockingEnabled = true; 325 return NS_ERROR_FAILURE; 326 } 327 328 if (UrlClassifierFeatureFactory::IsClassifierBlockingErrorCode(status)) { 329 mContentBlockingEnabled = true; 330 return NS_ERROR_FAILURE; 331 } 332 333 if (!success) { 334 LOG(("OBJLC [%p]: OnStartRequest: Request failed\n", this)); 335 // If the request fails, we still call LoadObject() to handle fallback 336 // content and notifying of failure. (mChannelLoaded && !mChannel) indicates 337 // the bad state. 338 mChannel = nullptr; 339 LoadObject(true, false); 340 return NS_ERROR_FAILURE; 341 } 342 343 return LoadObject(true, false, aRequest); 344 } 345 346 NS_IMETHODIMP 347 nsObjectLoadingContent::OnStopRequest(nsIRequest* aRequest, 348 nsresult aStatusCode) { 349 AUTO_PROFILER_LABEL("nsObjectLoadingContent::OnStopRequest", NETWORK); 350 351 // Handle object not loading error because source was a tracking URL (or 352 // fingerprinting, cryptomining, etc.). 353 // We make a note of this object node by including it in a dedicated 354 // array of blocked tracking nodes under its parent document. 355 if (UrlClassifierFeatureFactory::IsClassifierBlockingErrorCode(aStatusCode)) { 356 nsCOMPtr<nsIContent> thisNode = 357 do_QueryInterface(static_cast<nsIObjectLoadingContent*>(this)); 358 if (thisNode && thisNode->IsInComposedDoc()) { 359 thisNode->GetComposedDoc()->AddBlockedNodeByClassifier(thisNode); 360 } 361 } 362 363 if (aRequest != mChannel) { 364 return NS_BINDING_ABORTED; 365 } 366 367 mChannel = nullptr; 368 369 if (mFinalListener) { 370 // This may re-enter in the case of plugin listeners 371 nsCOMPtr<nsIStreamListener> listenerGrip(mFinalListener); 372 mFinalListener = nullptr; 373 listenerGrip->OnStopRequest(aRequest, aStatusCode); 374 } 375 376 // Return value doesn't matter 377 return NS_OK; 378 } 379 380 // nsIStreamListener 381 NS_IMETHODIMP 382 nsObjectLoadingContent::OnDataAvailable(nsIRequest* aRequest, 383 nsIInputStream* aInputStream, 384 uint64_t aOffset, uint32_t aCount) { 385 if (aRequest != mChannel) { 386 return NS_BINDING_ABORTED; 387 } 388 389 if (mFinalListener) { 390 // This may re-enter in the case of plugin listeners 391 nsCOMPtr<nsIStreamListener> listenerGrip(mFinalListener); 392 return listenerGrip->OnDataAvailable(aRequest, aInputStream, aOffset, 393 aCount); 394 } 395 396 // We shouldn't have a connected channel with no final listener 397 MOZ_ASSERT_UNREACHABLE( 398 "Got data for channel with no connected final " 399 "listener"); 400 mChannel = nullptr; 401 402 return NS_ERROR_UNEXPECTED; 403 } 404 405 NS_IMETHODIMP 406 nsObjectLoadingContent::GetActualType(nsACString& aType) { 407 aType = mContentType; 408 return NS_OK; 409 } 410 411 NS_IMETHODIMP 412 nsObjectLoadingContent::GetDisplayedType(uint32_t* aType) { 413 *aType = DisplayedType(); 414 return NS_OK; 415 } 416 417 // nsIInterfaceRequestor 418 // We use a shim class to implement this so that JS consumers still 419 // see an interface requestor even though WebIDL bindings don't expose 420 // that stuff. 421 class ObjectInterfaceRequestorShim final : public nsIInterfaceRequestor, 422 public nsIChannelEventSink, 423 public nsIStreamListener { 424 public: 425 NS_DECL_CYCLE_COLLECTING_ISUPPORTS 426 NS_DECL_CYCLE_COLLECTION_CLASS_AMBIGUOUS(ObjectInterfaceRequestorShim, 427 nsIInterfaceRequestor) 428 NS_DECL_NSIINTERFACEREQUESTOR 429 // RefPtr<nsObjectLoadingContent> fails due to ambiguous AddRef/Release, 430 // hence the ugly static cast :( 431 NS_FORWARD_NSICHANNELEVENTSINK( 432 static_cast<nsObjectLoadingContent*>(mContent.get())->) 433 NS_FORWARD_NSISTREAMLISTENER( 434 static_cast<nsObjectLoadingContent*>(mContent.get())->) 435 NS_FORWARD_NSIREQUESTOBSERVER( 436 static_cast<nsObjectLoadingContent*>(mContent.get())->) 437 438 explicit ObjectInterfaceRequestorShim(nsIObjectLoadingContent* aContent) 439 : mContent(aContent) {} 440 441 protected: 442 ~ObjectInterfaceRequestorShim() = default; 443 nsCOMPtr<nsIObjectLoadingContent> mContent; 444 }; 445 446 NS_IMPL_CYCLE_COLLECTION(ObjectInterfaceRequestorShim, mContent) 447 448 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(ObjectInterfaceRequestorShim) 449 NS_INTERFACE_MAP_ENTRY(nsIInterfaceRequestor) 450 NS_INTERFACE_MAP_ENTRY(nsIChannelEventSink) 451 NS_INTERFACE_MAP_ENTRY(nsIStreamListener) 452 NS_INTERFACE_MAP_ENTRY(nsIRequestObserver) 453 NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIInterfaceRequestor) 454 NS_INTERFACE_MAP_END 455 456 NS_IMPL_CYCLE_COLLECTING_ADDREF(ObjectInterfaceRequestorShim) 457 NS_IMPL_CYCLE_COLLECTING_RELEASE(ObjectInterfaceRequestorShim) 458 459 NS_IMETHODIMP 460 ObjectInterfaceRequestorShim::GetInterface(const nsIID& aIID, void** aResult) { 461 if (aIID.Equals(NS_GET_IID(nsIChannelEventSink))) { 462 nsIChannelEventSink* sink = this; 463 *aResult = sink; 464 NS_ADDREF(sink); 465 return NS_OK; 466 } 467 if (aIID.Equals(NS_GET_IID(nsIObjectLoadingContent))) { 468 nsIObjectLoadingContent* olc = mContent; 469 *aResult = olc; 470 NS_ADDREF(olc); 471 return NS_OK; 472 } 473 return NS_NOINTERFACE; 474 } 475 476 // nsIChannelEventSink 477 NS_IMETHODIMP 478 nsObjectLoadingContent::AsyncOnChannelRedirect( 479 nsIChannel* aOldChannel, nsIChannel* aNewChannel, uint32_t aFlags, 480 nsIAsyncVerifyRedirectCallback* cb) { 481 // If we're already busy with a new load, or have no load at all, 482 // cancel the redirect. 483 if (!mChannel || aOldChannel != mChannel) { 484 return NS_BINDING_ABORTED; 485 } 486 487 mChannel = aNewChannel; 488 489 if (mFinalListener) { 490 nsCOMPtr<nsIChannelEventSink> sink(do_QueryInterface(mFinalListener)); 491 MOZ_RELEASE_ASSERT(sink, "mFinalListener isn't nsIChannelEventSink?"); 492 if (mType != ObjectType::Document) { 493 MOZ_ASSERT_UNREACHABLE( 494 "Not a DocumentChannel load, but we're getting a " 495 "AsyncOnChannelRedirect with a mFinalListener?"); 496 return NS_BINDING_ABORTED; 497 } 498 499 return sink->AsyncOnChannelRedirect(aOldChannel, aNewChannel, aFlags, cb); 500 } 501 502 cb->OnRedirectVerifyCallback(NS_OK); 503 return NS_OK; 504 } 505 506 void nsObjectLoadingContent::MaybeRewriteYoutubeEmbed(nsIURI* aURI, 507 nsIURI* aBaseURI, 508 nsIURI** aRewrittenURI) { 509 nsCOMPtr<nsIEffectiveTLDService> tldService = 510 do_GetService(NS_EFFECTIVETLDSERVICE_CONTRACTID); 511 // If we can't analyze the URL, just pass on through. 512 if (!tldService) { 513 NS_WARNING("Could not get TLD service!"); 514 return; 515 } 516 517 nsAutoCString currentBaseDomain; 518 bool ok = NS_SUCCEEDED(tldService->GetBaseDomain(aURI, 0, currentBaseDomain)); 519 if (!ok) { 520 // Data URIs (commonly used for things like svg embeds) won't parse 521 // correctly, so just fail silently here. 522 return; 523 } 524 525 // See if URL is referencing youtube 526 if (!currentBaseDomain.EqualsLiteral("youtube.com") && 527 !currentBaseDomain.EqualsLiteral("youtube-nocookie.com")) { 528 return; 529 } 530 531 // We should only rewrite URLs with paths starting with "/v/", as we shouldn't 532 // touch object nodes with "/embed/" urls that already do that right thing. 533 nsAutoCString path; 534 aURI->GetPathQueryRef(path); 535 if (!StringBeginsWith(path, "/v/"_ns)) { 536 return; 537 } 538 539 // See if requester is planning on using the JS API. 540 nsAutoCString prePath; 541 nsresult rv = aURI->GetPrePath(prePath); 542 if (NS_FAILED(rv)) { 543 return; 544 } 545 546 // Some YouTube urls have parameters in path components, e.g. 547 // http://youtube.com/embed/7LcUOEP7Brc&start=35. These URLs work with flash, 548 // but break iframe/object embedding. If this situation occurs with rewritten 549 // URLs, convert the parameters to query in order to make the video load 550 // correctly as an iframe. In either case, warn about it in the 551 // developer console. 552 int32_t ampIndex = path.FindChar('&', 0); 553 bool replaceQuery = false; 554 if (ampIndex != -1) { 555 int32_t qmIndex = path.FindChar('?', 0); 556 if (qmIndex == -1 || qmIndex > ampIndex) { 557 replaceQuery = true; 558 } 559 } 560 561 Document* doc = AsElement()->OwnerDoc(); 562 // If we've made it this far, we've got a rewritable embed. Log it in 563 // telemetry. 564 doc->SetUseCounter(eUseCounter_custom_YouTubeFlashEmbed); 565 566 // If we're pref'd off, return after telemetry has been logged. 567 if (!Preferences::GetBool(kPrefYoutubeRewrite)) { 568 return; 569 } 570 571 NS_ConvertUTF8toUTF16 utf16OldURI(prePath); 572 AppendUTF8toUTF16(path, utf16OldURI); 573 // If we need to convert the URL, it means an ampersand comes first. 574 // Use the index we found earlier. 575 if (replaceQuery) { 576 // Replace question marks with ampersands. 577 path.ReplaceChar('?', '&'); 578 // Replace the first ampersand with a question mark. 579 path.SetCharAt('?', ampIndex); 580 } 581 // Switch out video access url formats, which should possibly allow HTML5 582 // video loading. 583 path.ReplaceSubstring("/v/"_ns, "/embed/"_ns); 584 NS_ConvertUTF8toUTF16 utf16URI(prePath); 585 AppendUTF8toUTF16(path, utf16URI); 586 rv = nsContentUtils::NewURIWithDocumentCharset(aRewrittenURI, utf16URI, doc, 587 aBaseURI); 588 if (NS_FAILED(rv)) { 589 return; 590 } 591 AutoTArray<nsString, 2> params = {utf16OldURI, utf16URI}; 592 const char* msgName; 593 // If there's no query to rewrite, just notify in the developer console 594 // that we're changing the embed. 595 if (!replaceQuery) { 596 msgName = "RewriteYouTubeEmbed"; 597 } else { 598 msgName = "RewriteYouTubeEmbedPathParams"; 599 } 600 nsContentUtils::ReportToConsole(nsIScriptError::warningFlag, "Plugins"_ns, 601 doc, nsContentUtils::eDOM_PROPERTIES, msgName, 602 params); 603 } 604 605 bool nsObjectLoadingContent::CheckLoadPolicy(int16_t* aContentPolicy) { 606 if (!aContentPolicy || !mURI) { 607 MOZ_ASSERT_UNREACHABLE("Doing it wrong"); 608 return false; 609 } 610 611 Element* el = AsElement(); 612 Document* doc = el->OwnerDoc(); 613 614 nsContentPolicyType contentPolicyType = GetContentPolicyType(); 615 616 Result<RefPtr<LoadInfo>, nsresult> maybeLoadInfo = 617 LoadInfo::Create(doc->NodePrincipal(), // loading principal 618 doc->NodePrincipal(), // triggering principal 619 el, nsILoadInfo::SEC_ONLY_FOR_EXPLICIT_CONTENTSEC_CHECK, 620 contentPolicyType); 621 if (NS_WARN_IF(maybeLoadInfo.isErr())) { 622 return false; 623 } 624 RefPtr<LoadInfo> secCheckLoadInfo = maybeLoadInfo.unwrap(); 625 626 *aContentPolicy = nsIContentPolicy::ACCEPT; 627 nsresult rv = 628 NS_CheckContentLoadPolicy(mURI, secCheckLoadInfo, aContentPolicy, 629 nsContentUtils::GetContentPolicy()); 630 NS_ENSURE_SUCCESS(rv, false); 631 if (NS_CP_REJECTED(*aContentPolicy)) { 632 LOG(("OBJLC [%p]: Content policy denied load of %s", this, 633 mURI->GetSpecOrDefault().get())); 634 return false; 635 } 636 637 return true; 638 } 639 640 bool nsObjectLoadingContent::CheckProcessPolicy(int16_t* aContentPolicy) { 641 if (!aContentPolicy) { 642 MOZ_ASSERT_UNREACHABLE("Null out variable"); 643 return false; 644 } 645 646 Element* el = AsElement(); 647 Document* doc = el->OwnerDoc(); 648 649 nsContentPolicyType objectType; 650 switch (mType) { 651 case ObjectType::Document: 652 objectType = nsIContentPolicy::TYPE_DOCUMENT; 653 break; 654 default: 655 MOZ_ASSERT_UNREACHABLE( 656 "Calling checkProcessPolicy with an unexpected type"); 657 return false; 658 } 659 660 Result<RefPtr<LoadInfo>, nsresult> maybeLoadInfo = LoadInfo::Create( 661 doc->NodePrincipal(), // loading principal 662 doc->NodePrincipal(), // triggering principal 663 el, nsILoadInfo::SEC_ONLY_FOR_EXPLICIT_CONTENTSEC_CHECK, objectType); 664 if (NS_WARN_IF(maybeLoadInfo.isErr())) { 665 return false; 666 } 667 RefPtr<LoadInfo> secCheckLoadInfo = maybeLoadInfo.unwrap(); 668 669 *aContentPolicy = nsIContentPolicy::ACCEPT; 670 nsresult rv = NS_CheckContentProcessPolicy( 671 mURI ? mURI : mBaseURI, secCheckLoadInfo, aContentPolicy, 672 nsContentUtils::GetContentPolicy()); 673 NS_ENSURE_SUCCESS(rv, false); 674 675 if (NS_CP_REJECTED(*aContentPolicy)) { 676 LOG(("OBJLC [%p]: CheckContentProcessPolicy rejected load", this)); 677 return false; 678 } 679 680 return true; 681 } 682 683 bool nsObjectLoadingContent::IsSyntheticImageDocument() const { 684 if (mType != ObjectType::Document || !mFrameLoader) { 685 return false; 686 } 687 688 BrowsingContext* browsingContext = mFrameLoader->GetExtantBrowsingContext(); 689 return browsingContext && browsingContext->GetIsSyntheticDocumentContainer(); 690 } 691 692 nsObjectLoadingContent::ParameterUpdateFlags 693 nsObjectLoadingContent::UpdateObjectParameters() { 694 Element* el = AsElement(); 695 696 uint32_t caps = GetCapabilities(); 697 LOG(("OBJLC [%p]: Updating object parameters", this)); 698 699 nsresult rv; 700 nsAutoCString newMime; 701 nsCOMPtr<nsIURI> newURI; 702 nsCOMPtr<nsIURI> newBaseURI; 703 ObjectType newType; 704 // Set if this state can't be used to load anything, forces 705 // ObjectType::Fallback 706 bool stateInvalid = false; 707 // Indicates what parameters changed. 708 // eParamChannelChanged - means parameters that affect channel opening 709 // decisions changed 710 // eParamStateChanged - means anything that affects what content we load 711 // changed, even if the channel we'd open remains the 712 // same. 713 // 714 // State changes outside of the channel parameters only matter if we've 715 // already opened a channel or tried to instantiate content, whereas channel 716 // parameter changes require re-opening the channel even if we haven't gotten 717 // that far. 718 ParameterUpdateFlags retval = eParamNoChange; 719 720 /// 721 /// Initial MIME Type 722 /// 723 724 if (caps & eFallbackIfClassIDPresent && 725 el->HasNonEmptyAttr(nsGkAtoms::classid)) { 726 // We don't support class ID plugin references, so we should always treat 727 // having class Ids as attributes as invalid, and fallback accordingly. 728 stateInvalid = true; 729 } 730 731 /// 732 /// Codebase 733 /// 734 735 nsIURI* docBaseURI = el->GetBaseURI(); 736 737 nsAutoString codebaseStr; 738 el->GetAttr(nsGkAtoms::codebase, codebaseStr); 739 if (StaticPrefs::dom_object_embed_codebase_enabled() && 740 !codebaseStr.IsEmpty()) { 741 rv = nsContentUtils::NewURIWithDocumentCharset( 742 getter_AddRefs(newBaseURI), codebaseStr, el->OwnerDoc(), docBaseURI); 743 if (NS_FAILED(rv)) { 744 // Malformed URI 745 LOG( 746 ("OBJLC [%p]: Could not parse plugin's codebase as a URI, " 747 "will use document baseURI instead", 748 this)); 749 } 750 } 751 752 // If we failed to build a valid URI, use the document's base URI 753 if (!newBaseURI) { 754 newBaseURI = docBaseURI; 755 } 756 757 /// 758 /// URI 759 /// 760 761 nsAutoString uriStr; 762 // Different elements keep this in various locations 763 if (el->NodeInfo()->Equals(nsGkAtoms::object)) { 764 el->GetAttr(nsGkAtoms::data, uriStr); 765 } else if (el->NodeInfo()->Equals(nsGkAtoms::embed)) { 766 el->GetAttr(nsGkAtoms::src, uriStr); 767 } else { 768 MOZ_ASSERT_UNREACHABLE("Unrecognized plugin-loading tag"); 769 } 770 771 mRewrittenYoutubeEmbed = false; 772 773 // Note that the baseURI changing could affect the newURI, even if uriStr did 774 // not change. 775 if (!uriStr.IsEmpty()) { 776 rv = nsContentUtils::NewURIWithDocumentCharset( 777 getter_AddRefs(newURI), uriStr, el->OwnerDoc(), newBaseURI); 778 nsCOMPtr<nsIURI> rewrittenURI; 779 MaybeRewriteYoutubeEmbed(newURI, newBaseURI, getter_AddRefs(rewrittenURI)); 780 if (rewrittenURI) { 781 newURI = rewrittenURI; 782 mRewrittenYoutubeEmbed = true; 783 newMime = "text/html"_ns; 784 } 785 786 if (NS_FAILED(rv)) { 787 stateInvalid = true; 788 } 789 } 790 791 /// 792 /// type 793 /// 794 nsAutoString rawTypeAttr; 795 el->GetAttr(nsGkAtoms::type, rawTypeAttr); 796 // YouTube embeds might be using type="application/x-shockwave-flash" 797 // which needs to be allowed, but must not override the text/html MIME set 798 // above. 799 if (!mRewrittenYoutubeEmbed && !rawTypeAttr.IsEmpty()) { 800 nsAutoString params; 801 nsAutoString mime; 802 nsContentUtils::SplitMimeType(rawTypeAttr, mime, params); 803 804 if (!StaticPrefs::dom_object_embed_type_hint_enabled()) { 805 NS_ConvertUTF16toUTF8 mimeUTF8(mime); 806 if (imgLoader::SupportImageWithMimeType(mimeUTF8)) { 807 // Normally the type attribute should not be used as a hint, but for 808 // images it does seem to happen in Chrome and Safari. Images generally 809 // don't lead to code execution and we don't use 810 // AcceptedMimeTypes::IMAGES_AND_DOCUMENTS above. 811 newMime = mimeUTF8; 812 } else if (GetTypeOfContent(mimeUTF8) != ObjectType::Document) { 813 LOG( 814 ("OBJLC [%p]: MIME '%s' from type attribute is not supported, " 815 "forcing fallback.", 816 this, mimeUTF8.get())); 817 stateInvalid = true; 818 } 819 820 // Don't use the type attribute as a Content-Type hint in other cases. 821 } else { 822 CopyUTF16toUTF8(mime, newMime); 823 } 824 } 825 826 /// 827 /// Check if the original (pre-channel) content-type or URI changed, and 828 /// record mOriginal{ContentType,URI} 829 /// 830 831 if ((mOriginalContentType != newMime) || !URIEquals(mOriginalURI, newURI)) { 832 // These parameters changing requires re-opening the channel, so don't 833 // consider the currently-open channel below 834 // XXX(johns): Changing the mime type might change our decision on whether 835 // or not we load a channel, so we count changes to it as a 836 // channel parameter change for the sake of simplicity. 837 retval = (ParameterUpdateFlags)(retval | eParamChannelChanged); 838 LOG(("OBJLC [%p]: Channel parameters changed", this)); 839 } 840 mOriginalContentType = newMime; 841 mOriginalURI = newURI; 842 843 /// 844 /// If we have a channel, see if its MIME type should take precendence and 845 /// check the final (redirected) URL 846 /// 847 848 // If we have a loaded channel and channel parameters did not change, use it 849 // to determine what we would load. 850 bool useChannel = mChannelLoaded && !(retval & eParamChannelChanged); 851 // If we have a channel and are type loading, as opposed to having an existing 852 // channel for a previous load. 853 bool newChannel = useChannel && mType == ObjectType::Loading; 854 855 RefPtr<DocumentChannel> documentChannel = do_QueryObject(mChannel); 856 if (newChannel && documentChannel) { 857 // If we've got a DocumentChannel which is marked as loaded using 858 // `mChannelLoaded`, we are currently in the middle of a 859 // `UpgradeLoadToDocument`. 860 // 861 // As we don't have the real mime-type from the channel, handle this by 862 // using `newMime`. 863 newMime = TEXT_HTML; 864 865 MOZ_DIAGNOSTIC_ASSERT(GetTypeOfContent(newMime) == ObjectType::Document, 866 "How is text/html not ObjectType::Document?"); 867 } else if (newChannel && mChannel) { 868 nsCString channelType; 869 rv = mChannel->GetContentType(channelType); 870 if (NS_FAILED(rv)) { 871 MOZ_ASSERT_UNREACHABLE("GetContentType failed"); 872 stateInvalid = true; 873 channelType.Truncate(); 874 } 875 876 LOG(("OBJLC [%p]: Channel has a content type of %s", this, 877 channelType.get())); 878 879 bool binaryChannelType = false; 880 if (channelType.EqualsASCII(APPLICATION_GUESS_FROM_EXT)) { 881 channelType = APPLICATION_OCTET_STREAM; 882 mChannel->SetContentType(channelType); 883 binaryChannelType = true; 884 } else if (channelType.EqualsASCII(APPLICATION_OCTET_STREAM) || 885 channelType.EqualsASCII(BINARY_OCTET_STREAM)) { 886 binaryChannelType = true; 887 } 888 889 // Channel can change our URI through redirection 890 rv = NS_GetFinalChannelURI(mChannel, getter_AddRefs(newURI)); 891 if (NS_FAILED(rv)) { 892 MOZ_ASSERT_UNREACHABLE("NS_GetFinalChannelURI failure"); 893 stateInvalid = true; 894 } 895 896 ObjectType typeHint = 897 newMime.IsEmpty() ? ObjectType::Fallback : GetTypeOfContent(newMime); 898 899 // In order of preference: 900 // 901 // 1) Use our type hint if it matches a plugin 902 // 2) If we have eAllowPluginSkipChannel, use the uri file extension if 903 // it matches a plugin 904 // 3) If the channel returns a binary stream type: 905 // 3a) If we have a type non-null non-document type hint, use that 906 // 3b) If the uri file extension matches a plugin type, use that 907 // 4) Use the channel type 908 909 bool overrideChannelType = false; 910 if (IsPluginMIME(newMime)) { 911 LOG(("OBJLC [%p]: Using plugin type hint in favor of any channel type", 912 this)); 913 overrideChannelType = true; 914 } else if (binaryChannelType && typeHint != ObjectType::Fallback) { 915 if (typeHint == ObjectType::Document) { 916 if (imgLoader::SupportImageWithMimeType(newMime)) { 917 LOG( 918 ("OBJLC [%p]: Using type hint in favor of binary channel type " 919 "(Image Document)", 920 this)); 921 overrideChannelType = true; 922 } 923 } else { 924 LOG( 925 ("OBJLC [%p]: Using type hint in favor of binary channel type " 926 "(Non-Image Document)", 927 this)); 928 overrideChannelType = true; 929 } 930 } 931 932 if (overrideChannelType) { 933 // Set the type we'll use for dispatch on the channel. Otherwise we could 934 // end up trying to dispatch to a nsFrameLoader, which will complain that 935 // it couldn't find a way to handle application/octet-stream 936 nsAutoCString parsedMime, dummy; 937 NS_ParseResponseContentType(newMime, parsedMime, dummy); 938 if (!parsedMime.IsEmpty()) { 939 mChannel->SetContentType(parsedMime); 940 } 941 } else { 942 newMime = channelType; 943 } 944 } else if (newChannel) { 945 LOG(("OBJLC [%p]: We failed to open a channel, marking invalid", this)); 946 stateInvalid = true; 947 } 948 949 /// 950 /// Determine final type 951 /// 952 // In order of preference: 953 // 1) If we have attempted channel load, or set stateInvalid above, the type 954 // is always null (fallback) 955 // 2) If we have a loaded channel, we grabbed its mimeType above, use that 956 // type. 957 // 3) If we have a plugin type and no URI, use that type. 958 // 4) If we have a plugin type and eAllowPluginSkipChannel, use that type. 959 // 5) if we have a URI, set type to loading to indicate we'd need a channel 960 // to proceed. 961 // 6) Otherwise, type null to indicate unloadable content (fallback) 962 // 963 964 ObjectType newMime_Type = GetTypeOfContent(newMime); 965 966 if (stateInvalid) { 967 newType = ObjectType::Fallback; 968 LOG(("OBJLC [%p]: NewType #0: %s - %u", this, newMime.get(), 969 uint32_t(newType))); 970 newMime.Truncate(); 971 } else if (newChannel) { 972 // If newChannel is set above, we considered it in setting newMime 973 newType = newMime_Type; 974 LOG(("OBJLC [%p]: NewType #1: %s - %u", this, newMime.get(), 975 uint32_t(newType))); 976 LOG(("OBJLC [%p]: Using channel type", this)); 977 } else if (((caps & eAllowPluginSkipChannel) || !newURI) && 978 IsPluginMIME(newMime)) { 979 newType = newMime_Type; 980 LOG(("OBJLC [%p]: NewType #2: %s - %u", this, newMime.get(), 981 uint32_t(newType))); 982 LOG(("OBJLC [%p]: Plugin type with no URI, skipping channel load", this)); 983 } else if (newURI && (mOriginalContentType.IsEmpty() || 984 newMime_Type != ObjectType::Fallback)) { 985 // We could potentially load this if we opened a channel on mURI, indicate 986 // this by leaving type as loading. 987 // 988 // If a MIME type was requested in the tag, but we have decided to set load 989 // type to null, ignore (otherwise we'll default to document type loading). 990 newType = ObjectType::Loading; 991 LOG(("OBJLC [%p]: NewType #3: %u", this, uint32_t(newType))); 992 } else { 993 // Unloadable - no URI, and no plugin/MIME type. Non-plugin types (images, 994 // documents) always load with a channel. 995 newType = ObjectType::Fallback; 996 LOG(("OBJLC [%p]: NewType #4: %u", this, uint32_t(newType))); 997 } 998 999 /// 1000 /// Handle existing channels 1001 /// 1002 1003 if (useChannel && newType == ObjectType::Loading) { 1004 // We decided to use a channel, and also that the previous channel is still 1005 // usable, so re-use the existing values. 1006 newType = mType; 1007 LOG(("OBJLC [%p]: NewType #5: %u", this, uint32_t(newType))); 1008 newMime = mContentType; 1009 newURI = mURI; 1010 } else if (useChannel && !newChannel) { 1011 // We have an existing channel, but did not decide to use one. 1012 retval = (ParameterUpdateFlags)(retval | eParamChannelChanged); 1013 useChannel = false; 1014 } 1015 1016 /// 1017 /// Update changed values 1018 /// 1019 1020 if (newType != mType) { 1021 retval = (ParameterUpdateFlags)(retval | eParamStateChanged); 1022 LOG(("OBJLC [%p]: Type changed from %u -> %u", this, uint32_t(mType), 1023 uint32_t(newType))); 1024 mType = newType; 1025 } 1026 1027 if (!URIEquals(mBaseURI, newBaseURI)) { 1028 LOG(("OBJLC [%p]: Object effective baseURI changed", this)); 1029 mBaseURI = newBaseURI; 1030 } 1031 1032 if (!URIEquals(newURI, mURI)) { 1033 retval = (ParameterUpdateFlags)(retval | eParamStateChanged); 1034 LOG(("OBJLC [%p]: Object effective URI changed", this)); 1035 mURI = newURI; 1036 } 1037 1038 // We don't update content type when loading, as the type is not final and we 1039 // don't want to superfluously change between mOriginalContentType -> 1040 // mContentType when doing |obj.data = obj.data| with a channel and differing 1041 // type. 1042 if (mType != ObjectType::Loading && mContentType != newMime) { 1043 retval = (ParameterUpdateFlags)(retval | eParamStateChanged); 1044 retval = (ParameterUpdateFlags)(retval | eParamContentTypeChanged); 1045 LOG(("OBJLC [%p]: Object effective mime type changed (%s -> %s)", this, 1046 mContentType.get(), newMime.get())); 1047 mContentType = newMime; 1048 } 1049 1050 // If we decided to keep using info from an old channel, but also that state 1051 // changed, we need to invalidate it. 1052 if (useChannel && !newChannel && (retval & eParamStateChanged)) { 1053 mType = ObjectType::Loading; 1054 retval = (ParameterUpdateFlags)(retval | eParamChannelChanged); 1055 } 1056 1057 return retval; 1058 } 1059 1060 // Only OnStartRequest should be passing the channel parameter 1061 nsresult nsObjectLoadingContent::LoadObject(bool aNotify, bool aForceLoad) { 1062 return LoadObject(aNotify, aForceLoad, nullptr); 1063 } 1064 1065 nsresult nsObjectLoadingContent::LoadObject(bool aNotify, bool aForceLoad, 1066 nsIRequest* aLoadingChannel) { 1067 Element* el = AsElement(); 1068 Document* doc = el->OwnerDoc(); 1069 nsresult rv = NS_OK; 1070 1071 // Per bug 1318303, if the parent document is not active, load the alternative 1072 // and return. 1073 if (!doc->IsCurrentActiveDocument()) { 1074 // Since this can be triggered on change of attributes, make sure we've 1075 // unloaded whatever is loaded first. 1076 UnloadObject(); 1077 ObjectType oldType = mType; 1078 mType = ObjectType::Fallback; 1079 TriggerInnerFallbackLoads(); 1080 NotifyStateChanged(oldType, true); 1081 return NS_OK; 1082 } 1083 1084 // XXX(johns): In these cases, we refuse to touch our content and just 1085 // remain unloaded, as per legacy behavior. It would make more sense to 1086 // load fallback content initially and refuse to ever change state again. 1087 if (doc->IsBeingUsedAsImage()) { 1088 return NS_OK; 1089 } 1090 1091 if (doc->IsLoadedAsData() || doc->IsStaticDocument()) { 1092 return NS_OK; 1093 } 1094 1095 LOG(("OBJLC [%p]: LoadObject called, notify %u, forceload %u, channel %p", 1096 this, aNotify, aForceLoad, aLoadingChannel)); 1097 1098 // We can't re-use an already open channel, but aForceLoad may make us try 1099 // to load a plugin without any changes in channel state. 1100 if (aForceLoad && mChannelLoaded) { 1101 CloseChannel(); 1102 mChannelLoaded = false; 1103 } 1104 1105 // Save these for NotifyStateChanged(); 1106 ObjectType oldType = mType; 1107 1108 ParameterUpdateFlags stateChange = UpdateObjectParameters(); 1109 1110 if (!stateChange && !aForceLoad) { 1111 return NS_OK; 1112 } 1113 1114 /// 1115 /// State has changed, unload existing content and attempt to load new type 1116 /// 1117 LOG(("OBJLC [%p]: LoadObject - plugin state changed (%u)", this, 1118 stateChange)); 1119 1120 // We synchronously start/stop plugin instances below, which may spin the 1121 // event loop. Re-entering into the load is fine, but at that point the 1122 // original load call needs to abort when unwinding 1123 // NOTE this is located *after* the state change check, a subsequent load 1124 // with no subsequently changed state will be a no-op. 1125 if (mIsLoading) { 1126 LOG(("OBJLC [%p]: Re-entering into LoadObject", this)); 1127 } 1128 mIsLoading = true; 1129 AutoSetLoadingToFalse reentryCheck(this); 1130 1131 // Unload existing content, keeping in mind stopping plugins might spin the 1132 // event loop. Note that we check for still-open channels below 1133 UnloadObject(false); // Don't reset state 1134 if (!mIsLoading) { 1135 // The event loop must've spun and re-entered into LoadObject, which 1136 // finished the load 1137 LOG(("OBJLC [%p]: Re-entered into LoadObject, aborting outer load", this)); 1138 return NS_OK; 1139 } 1140 1141 // Determine what's going on with our channel. 1142 if (stateChange & eParamChannelChanged) { 1143 // If the channel params changed, throw away the channel, but unset 1144 // mChannelLoaded so we'll still try to open a new one for this load if 1145 // necessary 1146 CloseChannel(); 1147 mChannelLoaded = false; 1148 } else if (mType == ObjectType::Fallback && mChannel) { 1149 // If we opened a channel but then failed to find a loadable state, throw it 1150 // away. mChannelLoaded will indicate that we tried to load a channel at one 1151 // point so we wont recurse 1152 CloseChannel(); 1153 } else if (mType == ObjectType::Loading && mChannel) { 1154 // We're still waiting on a channel load, already opened one, and 1155 // channel parameters didn't change 1156 return NS_OK; 1157 } else if (mChannelLoaded && mChannel != aLoadingChannel) { 1158 // The only time we should have a loaded channel with a changed state is 1159 // when the channel has just opened -- in which case this call should 1160 // have originated from OnStartRequest 1161 MOZ_ASSERT_UNREACHABLE( 1162 "Loading with a channel, but state doesn't make sense"); 1163 return NS_OK; 1164 } 1165 1166 // 1167 // Security checks 1168 // 1169 1170 if (mType != ObjectType::Fallback) { 1171 bool allowLoad = true; 1172 int16_t contentPolicy = nsIContentPolicy::ACCEPT; 1173 // If mChannelLoaded is set we presumably already passed load policy 1174 // If mType == ObjectType::Loading then we call OpenChannel() which 1175 // internally creates a new channel and calls asyncOpen() on that channel 1176 // which then enforces content policy checks. 1177 if (allowLoad && mURI && !mChannelLoaded && mType != ObjectType::Loading) { 1178 allowLoad = CheckLoadPolicy(&contentPolicy); 1179 } 1180 // If we're loading a type now, check ProcessPolicy. Note that we may check 1181 // both now in the case of plugins whose type is determined before opening a 1182 // channel. 1183 if (allowLoad && mType != ObjectType::Loading) { 1184 allowLoad = CheckProcessPolicy(&contentPolicy); 1185 } 1186 1187 // Content policy implementations can mutate the DOM, check for re-entry 1188 if (!mIsLoading) { 1189 LOG(("OBJLC [%p]: We re-entered in content policy, leaving original load", 1190 this)); 1191 return NS_OK; 1192 } 1193 1194 // Load denied, switch to null 1195 if (!allowLoad) { 1196 LOG(("OBJLC [%p]: Load denied by policy", this)); 1197 mType = ObjectType::Fallback; 1198 } 1199 } 1200 1201 // https://html.spec.whatwg.org/multipage/iframe-embed-object.html#the-object-element 1202 // requires that `embed` and `object` go through `Fetch` with mode=navigate, 1203 // see 1.3.5. This will in https://fetch.spec.whatwg.org/#fetching plumb us 1204 // through to https://fetch.spec.whatwg.org/#concept-main-fetch where in step 1205 // 12 a switch is performed. Since `object` and `embed` have mode=navigate the 1206 // result of https://fetch.spec.whatwg.org/#concept-scheme-fetch will decide 1207 // if main fetch proceeds. We short-circuit that scheme-fetch here, inspecting 1208 // if the scheme of `mURI` is one that would return a network error. The 1209 // following schemes are allowed through in scheme fetch: 1210 // "about", "blob", "data", "file", "http", "https". 1211 // 1212 // Some accessibility tests use our internal "chrome" scheme. 1213 if (mType != ObjectType::Fallback && mURI) { 1214 ObjectType type = ObjectType::Fallback; 1215 for (const auto& candidate : 1216 {"about", "blob", "chrome", "data", "file", "http", "https"}) { 1217 if (mURI->SchemeIs(candidate)) { 1218 type = mType; 1219 break; 1220 } 1221 } 1222 mType = type; 1223 } 1224 1225 // Items resolved as Image/Document are not candidates for content blocking, 1226 // as well as invalid plugins (they will not have the mContentType set). 1227 if (mType == ObjectType::Fallback && ShouldBlockContent()) { 1228 LOG(("OBJLC [%p]: Enable content blocking", this)); 1229 mType = ObjectType::Loading; 1230 } 1231 1232 // Sanity check: We shouldn't have any loaded resources, pending events, or 1233 // a final listener at this point 1234 if (mFrameLoader || mFinalListener) { 1235 MOZ_ASSERT_UNREACHABLE("Trying to load new plugin with existing content"); 1236 return NS_OK; 1237 } 1238 1239 // More sanity-checking: 1240 // If mChannel is set, mChannelLoaded should be set, and vice-versa 1241 if (mType != ObjectType::Fallback && !!mChannel != mChannelLoaded) { 1242 MOZ_ASSERT_UNREACHABLE("Trying to load with bad channel state"); 1243 return NS_OK; 1244 } 1245 1246 /// 1247 /// Attempt to load new type 1248 /// 1249 1250 // We don't set mFinalListener until OnStartRequest has been called, to 1251 // prevent re-entry ugliness with CloseChannel() 1252 nsCOMPtr<nsIStreamListener> finalListener; 1253 switch (mType) { 1254 case ObjectType::Document: { 1255 if (!mChannel) { 1256 // We could mFrameLoader->LoadURI(mURI), but UpdateObjectParameters 1257 // requires documents have a channel, so this is not a valid state. 1258 MOZ_ASSERT_UNREACHABLE( 1259 "Attempting to load a document without a " 1260 "channel"); 1261 rv = NS_ERROR_FAILURE; 1262 break; 1263 } 1264 1265 nsCOMPtr<nsIDocShell> docShell = SetupDocShell(mURI); 1266 if (!docShell) { 1267 rv = NS_ERROR_FAILURE; 1268 break; 1269 } 1270 1271 // We're loading a document, so we have to set LOAD_DOCUMENT_URI 1272 // (especially important for firing onload) 1273 nsLoadFlags flags = 0; 1274 mChannel->GetLoadFlags(&flags); 1275 flags |= nsIChannel::LOAD_DOCUMENT_URI; 1276 mChannel->SetLoadFlags(flags); 1277 1278 nsCOMPtr<nsIInterfaceRequestor> req(do_QueryInterface(docShell)); 1279 NS_ASSERTION(req, "Docshell must be an ifreq"); 1280 1281 nsCOMPtr<nsIURILoader> uriLoader(components::URILoader::Service()); 1282 if (NS_WARN_IF(!uriLoader)) { 1283 MOZ_ASSERT_UNREACHABLE("Failed to get uriLoader service"); 1284 mFrameLoader->Destroy(); 1285 mFrameLoader = nullptr; 1286 break; 1287 } 1288 1289 uint32_t uriLoaderFlags = nsDocShell::ComputeURILoaderFlags( 1290 docShell->GetBrowsingContext(), LOAD_NORMAL, 1291 /* aIsDocumentLoad */ false); 1292 1293 rv = uriLoader->OpenChannel(mChannel, uriLoaderFlags, req, 1294 getter_AddRefs(finalListener)); 1295 // finalListener will receive OnStartRequest either below, or if 1296 // `mChannel` is a `DocumentChannel`, it will be received after 1297 // RedirectToRealChannel. 1298 } break; 1299 case ObjectType::Loading: 1300 // If our type remains Loading, we need a channel to proceed 1301 rv = OpenChannel(); 1302 if (NS_FAILED(rv)) { 1303 LOG(("OBJLC [%p]: OpenChannel returned failure (%" PRIu32 ")", this, 1304 static_cast<uint32_t>(rv))); 1305 } 1306 break; 1307 case ObjectType::Fallback: 1308 // Handled below, silence compiler warnings 1309 break; 1310 } 1311 1312 // 1313 // Loaded, handle notifications and fallback 1314 // 1315 if (NS_FAILED(rv)) { 1316 // If we failed in the loading hunk above, switch to null (empty) region 1317 LOG(("OBJLC [%p]: Loading failed, switching to fallback", this)); 1318 mType = ObjectType::Fallback; 1319 } 1320 1321 if (mType == ObjectType::Fallback) { 1322 LOG(("OBJLC [%p]: Switching to fallback state", this)); 1323 MOZ_ASSERT(!mFrameLoader, "switched to fallback but also loaded something"); 1324 1325 MaybeFireErrorEvent(); 1326 1327 if (mChannel) { 1328 // If we were loading with a channel but then failed over, throw it away 1329 CloseChannel(); 1330 } 1331 1332 // Don't try to initialize plugins or final listener below 1333 finalListener = nullptr; 1334 1335 TriggerInnerFallbackLoads(); 1336 } 1337 1338 // Notify of our final state 1339 NotifyStateChanged(oldType, aNotify); 1340 NS_ENSURE_TRUE(mIsLoading, NS_OK); 1341 1342 // 1343 // Spawning plugins and dispatching to the final listener may re-enter, so are 1344 // delayed until after we fire a notification, to prevent missing 1345 // notifications or firing them out of order. 1346 // 1347 // Note that we ensured that we entered into LoadObject() from 1348 // ::OnStartRequest above when loading with a channel. 1349 // 1350 1351 rv = NS_OK; 1352 if (finalListener) { 1353 NS_ASSERTION(mType != ObjectType::Fallback && mType != ObjectType::Loading, 1354 "We should not have a final listener with a non-loaded type"); 1355 mFinalListener = finalListener; 1356 1357 // If we're a DocumentChannel load, hold off on firing the `OnStartRequest` 1358 // callback, as we haven't received it yet from our caller. 1359 RefPtr<DocumentChannel> documentChannel = do_QueryObject(mChannel); 1360 if (documentChannel) { 1361 MOZ_ASSERT( 1362 mType == ObjectType::Document, 1363 "We have a DocumentChannel here but aren't loading a document?"); 1364 } else { 1365 rv = finalListener->OnStartRequest(mChannel); 1366 } 1367 } 1368 1369 if ((NS_FAILED(rv) && rv != NS_ERROR_PARSED_DATA_CACHED) && mIsLoading) { 1370 // Since we've already notified of our transition, we can just Unload and 1371 // call ConfigureFallback (which will notify again) 1372 oldType = mType; 1373 mType = ObjectType::Fallback; 1374 UnloadObject(false); 1375 NS_ENSURE_TRUE(mIsLoading, NS_OK); 1376 CloseChannel(); 1377 TriggerInnerFallbackLoads(); 1378 NotifyStateChanged(oldType, true); 1379 } 1380 1381 return NS_OK; 1382 } 1383 1384 // This call can re-enter when dealing with plugin listeners 1385 nsresult nsObjectLoadingContent::CloseChannel() { 1386 if (mChannel) { 1387 LOG(("OBJLC [%p]: Closing channel\n", this)); 1388 // Null the values before potentially-reentering, and ensure they survive 1389 // the call 1390 nsCOMPtr<nsIChannel> channelGrip(mChannel); 1391 nsCOMPtr<nsIStreamListener> listenerGrip(mFinalListener); 1392 mChannel = nullptr; 1393 mFinalListener = nullptr; 1394 channelGrip->CancelWithReason(NS_BINDING_ABORTED, 1395 "nsObjectLoadingContent::CloseChannel"_ns); 1396 if (listenerGrip) { 1397 // mFinalListener is only set by LoadObject after OnStartRequest, or 1398 // by OnStartRequest in the case of late-opened plugin streams 1399 listenerGrip->OnStopRequest(channelGrip, NS_BINDING_ABORTED); 1400 } 1401 } 1402 return NS_OK; 1403 } 1404 1405 bool nsObjectLoadingContent::IsAboutBlankLoadOntoInitialAboutBlank( 1406 nsIURI* aURI, bool aInheritPrincipal, nsIPrincipal* aPrincipalToInherit) { 1407 if (!NS_IsAboutBlankAllowQueryAndFragment(aURI) || !aInheritPrincipal) { 1408 return false; 1409 } 1410 1411 if (!mFrameLoader || !mFrameLoader->GetExistingDocShell()) { 1412 return false; 1413 } 1414 1415 RefPtr<nsDocShellLoadState> dummyLoadState = new nsDocShellLoadState(mURI); 1416 return mFrameLoader->GetExistingDocShell()->ShouldDoInitialAboutBlankSyncLoad( 1417 aURI, dummyLoadState, aPrincipalToInherit); 1418 } 1419 1420 nsresult nsObjectLoadingContent::OpenChannel() { 1421 Element* el = AsElement(); 1422 Document* doc = el->OwnerDoc(); 1423 NS_ASSERTION(doc, "No owner document?"); 1424 1425 nsresult rv; 1426 mChannel = nullptr; 1427 1428 // E.g. mms:// 1429 if (!mURI || !CanHandleURI(mURI)) { 1430 return NS_ERROR_NOT_AVAILABLE; 1431 } 1432 1433 nsCOMPtr<nsILoadGroup> group = doc->GetDocumentLoadGroup(); 1434 nsCOMPtr<nsIChannel> chan; 1435 RefPtr<ObjectInterfaceRequestorShim> shim = 1436 new ObjectInterfaceRequestorShim(this); 1437 1438 bool inheritAttrs = nsContentUtils::ChannelShouldInheritPrincipal( 1439 el->NodePrincipal(), // aLoadState->PrincipalToInherit() 1440 mURI, // aLoadState->URI() 1441 true, // aInheritForAboutBlank 1442 false); // aForceInherit 1443 1444 bool inheritPrincipal = inheritAttrs && !mURI->SchemeIs("data"); 1445 1446 nsSecurityFlags securityFlags = 1447 nsILoadInfo::SEC_ALLOW_CROSS_ORIGIN_SEC_CONTEXT_IS_NULL; 1448 if (inheritPrincipal) { 1449 securityFlags |= nsILoadInfo::SEC_FORCE_INHERIT_PRINCIPAL; 1450 } 1451 1452 nsContentPolicyType contentPolicyType = GetContentPolicyType(); 1453 // The setting of LOAD_BYPASS_SERVICE_WORKER here is now an optimization. 1454 // ServiceWorkerInterceptController::ShouldPrepareForIntercept does a more 1455 // expensive check of BrowsingContext ancestors to look for object/embed. 1456 nsLoadFlags loadFlags = nsIChannel::LOAD_CALL_CONTENT_SNIFFERS | 1457 nsIChannel::LOAD_BYPASS_SERVICE_WORKER | 1458 nsIRequest::LOAD_HTML_OBJECT_DATA; 1459 uint32_t sandboxFlags = doc->GetSandboxFlags(); 1460 1461 // For object loads we store the policyContainer that potentially needs to 1462 // be inherited, e.g. in case we are loading an opaque origin 1463 // like a data: URI. The actual inheritance check happens within 1464 // Document::InitPolicyContainer(). Please create an actual copy of the 1465 // policyContainer (do not share the same reference) otherwise modifications 1466 // done (such as the meta CSP of the new doc) in an opaque origin will 1467 // incorrectly be propagated to the embedding document. 1468 RefPtr<PolicyContainer> policyContainerToInherit; 1469 if (nsCOMPtr<nsIPolicyContainer> policyContainer = 1470 doc->GetPolicyContainer()) { 1471 policyContainerToInherit = new PolicyContainer(); 1472 policyContainerToInherit->InitFromOther( 1473 PolicyContainer::Cast(policyContainer.get())); 1474 } 1475 1476 // --- Create LoadInfo 1477 RefPtr<LoadInfo> loadInfo = MOZ_TRY(LoadInfo::Create( 1478 /*aLoadingPrincipal = aLoadingContext->NodePrincipal() */ nullptr, 1479 /*aTriggeringPrincipal = aLoadingPrincipal */ nullptr, 1480 /*aLoadingContext = */ el, 1481 /*aSecurityFlags = */ securityFlags, 1482 /*aContentPolicyType = */ contentPolicyType, 1483 /*aLoadingClientInfo = */ Nothing(), 1484 /*aController = */ Nothing(), 1485 /*aSandboxFlags = */ sandboxFlags)); 1486 1487 if (inheritAttrs) { 1488 loadInfo->SetPrincipalToInherit(el->NodePrincipal()); 1489 } 1490 1491 // For object loads we store the policyContainer that potentially needs to 1492 // be inherited, e.g. in case we are loading an opaque origin 1493 // like a data: URI. The actual inheritance check happens within 1494 // Document::InitPolicyContainer(). Please create an actual copy of the 1495 // policyContainer (do not share the same reference) otherwise modifications 1496 // done (such as the meta CSP of the new doc) in an opaque origin will 1497 // incorrectly be propagated to the embedding document. 1498 if (policyContainerToInherit) { 1499 loadInfo->SetPolicyContainerToInherit(policyContainerToInherit); 1500 } 1501 1502 if (DocumentChannel::CanUseDocumentChannel(mURI) && 1503 !IsAboutBlankLoadOntoInitialAboutBlank(mURI, inheritPrincipal, 1504 el->NodePrincipal())) { 1505 // --- Create LoadState 1506 RefPtr<nsDocShellLoadState> loadState = new nsDocShellLoadState(mURI); 1507 loadState->SetPrincipalToInherit(el->NodePrincipal()); 1508 loadState->SetTriggeringPrincipal(loadInfo->TriggeringPrincipal()); 1509 if (policyContainerToInherit) { 1510 loadState->SetPolicyContainer(policyContainerToInherit); 1511 } 1512 loadState->SetTriggeringSandboxFlags(sandboxFlags); 1513 1514 // TODO(djg): This was httpChan->SetReferrerInfoWithoutClone(referrerInfo); 1515 // Is the ...WithoutClone(...) important? 1516 auto referrerInfo = MakeRefPtr<ReferrerInfo>(*doc); 1517 loadState->SetReferrerInfo(referrerInfo); 1518 1519 loadState->SetShouldCheckForRecursion(true); 1520 1521 // When loading using DocumentChannel, ensure that the MIME type hint is 1522 // propagated to DocumentLoadListener. Object elements can override MIME 1523 // handling in some scenarios. 1524 if (!mOriginalContentType.IsEmpty()) { 1525 nsAutoCString parsedMime, dummy; 1526 NS_ParseResponseContentType(mOriginalContentType, parsedMime, dummy); 1527 if (!parsedMime.IsEmpty()) { 1528 loadState->SetTypeHint(parsedMime); 1529 } 1530 } 1531 1532 chan = 1533 DocumentChannel::CreateForObject(loadState, loadInfo, loadFlags, shim); 1534 MOZ_ASSERT(chan); 1535 // NS_NewChannel sets the group on the channel. CreateDocumentChannel does 1536 // not. 1537 chan->SetLoadGroup(group); 1538 } else { 1539 rv = NS_NewChannelInternal(getter_AddRefs(chan), // outChannel 1540 mURI, // aUri 1541 loadInfo, // aLoadInfo 1542 nullptr, // aPerformanceStorage 1543 group, // aLoadGroup 1544 shim, // aCallbacks 1545 loadFlags, // aLoadFlags 1546 nullptr); // aIoService 1547 NS_ENSURE_SUCCESS(rv, rv); 1548 }; 1549 1550 // Referrer 1551 if (nsCOMPtr<nsIHttpChannel> httpChan = do_QueryInterface(chan)) { 1552 auto referrerInfo = MakeRefPtr<ReferrerInfo>(*doc); 1553 1554 rv = httpChan->SetReferrerInfoWithoutClone(referrerInfo); 1555 MOZ_ASSERT(NS_SUCCEEDED(rv)); 1556 1557 // Set the initiator type 1558 if (nsCOMPtr<nsITimedChannel> timedChannel = do_QueryInterface(httpChan)) { 1559 timedChannel->SetInitiatorType(el->LocalName()); 1560 } 1561 1562 nsCOMPtr<nsIClassOfService> cos(do_QueryInterface(httpChan)); 1563 if (cos && UserActivation::IsHandlingUserInput()) { 1564 cos->AddClassFlags(nsIClassOfService::UrgentStart); 1565 } 1566 } 1567 1568 // AsyncOpen can fail if a file does not exist. 1569 rv = chan->AsyncOpen(shim); 1570 NS_ENSURE_SUCCESS(rv, rv); 1571 LOG(("OBJLC [%p]: Channel opened", this)); 1572 mChannel = chan; 1573 return NS_OK; 1574 } 1575 1576 uint32_t nsObjectLoadingContent::GetCapabilities() const { 1577 return eSupportImages | eSupportDocuments; 1578 } 1579 1580 void nsObjectLoadingContent::Destroy() { 1581 if (mFrameLoader) { 1582 mFrameLoader->Destroy(); 1583 mFrameLoader = nullptr; 1584 } 1585 1586 // Reset state so that if the element is re-appended to tree again (e.g. 1587 // adopting to another document), it will reload resource again. 1588 UnloadObject(); 1589 } 1590 1591 /* static */ 1592 void nsObjectLoadingContent::Traverse(nsObjectLoadingContent* tmp, 1593 nsCycleCollectionTraversalCallback& cb) { 1594 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mFrameLoader); 1595 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mFeaturePolicy); 1596 } 1597 1598 /* static */ 1599 void nsObjectLoadingContent::Unlink(nsObjectLoadingContent* tmp) { 1600 if (tmp->mFrameLoader) { 1601 tmp->mFrameLoader->Destroy(); 1602 } 1603 NS_IMPL_CYCLE_COLLECTION_UNLINK(mFrameLoader); 1604 NS_IMPL_CYCLE_COLLECTION_UNLINK(mFeaturePolicy); 1605 } 1606 1607 void nsObjectLoadingContent::UnloadObject(bool aResetState) { 1608 if (mFrameLoader) { 1609 mFrameLoader->Destroy(); 1610 mFrameLoader = nullptr; 1611 } 1612 1613 if (aResetState) { 1614 CloseChannel(); 1615 mChannelLoaded = false; 1616 mType = ObjectType::Loading; 1617 mURI = mOriginalURI = mBaseURI = nullptr; 1618 mContentType.Truncate(); 1619 mOriginalContentType.Truncate(); 1620 } 1621 1622 mScriptRequested = false; 1623 1624 mIsStopping = false; 1625 1626 mSubdocumentIntrinsicSize.reset(); 1627 mSubdocumentIntrinsicRatio.reset(); 1628 } 1629 1630 void nsObjectLoadingContent::NotifyStateChanged(ObjectType aOldType, 1631 bool aNotify) { 1632 LOG(("OBJLC [%p]: NotifyStateChanged: (%u) -> (%u) (notify %i)", this, 1633 uint32_t(aOldType), uint32_t(mType), aNotify)); 1634 1635 dom::Element* thisEl = AsElement(); 1636 // Non-images are always not broken. 1637 // XXX: I assume we could just remove this completely? 1638 thisEl->RemoveStates(ElementState::BROKEN, aNotify); 1639 1640 if (mType == aOldType) { 1641 return; 1642 } 1643 1644 Document* doc = thisEl->GetComposedDoc(); 1645 if (!doc) { 1646 return; // Nothing to do 1647 } 1648 1649 PresShell* presShell = doc->GetPresShell(); 1650 // If there is no PresShell or it hasn't been initialized there isn't much to 1651 // do. 1652 if (!presShell || !presShell->DidInitialize()) { 1653 return; 1654 } 1655 presShell->PostRecreateFramesFor(thisEl); 1656 } 1657 1658 nsObjectLoadingContent::ObjectType nsObjectLoadingContent::GetTypeOfContent( 1659 const nsCString& aMIMEType) { 1660 Element* el = AsElement(); 1661 NS_ASSERTION(el, "must be a content"); 1662 1663 Document* doc = el->OwnerDoc(); 1664 1665 // Images and documents are always supported. 1666 MOZ_ASSERT((GetCapabilities() & (eSupportImages | eSupportDocuments)) == 1667 (eSupportImages | eSupportDocuments)); 1668 1669 LOG( 1670 ("OBJLC [%p]: calling HtmlObjectContentTypeForMIMEType: aMIMEType: %s - " 1671 "el: %p\n", 1672 this, aMIMEType.get(), el)); 1673 auto ret = 1674 static_cast<ObjectType>(nsContentUtils::HtmlObjectContentTypeForMIMEType( 1675 aMIMEType, doc->GetSandboxFlags())); 1676 LOG(("OBJLC [%p]: called HtmlObjectContentTypeForMIMEType\n", this)); 1677 return ret; 1678 } 1679 1680 void nsObjectLoadingContent::CreateStaticClone( 1681 nsObjectLoadingContent* aDest) const { 1682 MOZ_ASSERT(aDest->AsElement()->OwnerDoc()->IsStaticDocument()); 1683 aDest->mType = mType; 1684 1685 if (mFrameLoader) { 1686 aDest->AsElement()->OwnerDoc()->AddPendingFrameStaticClone(aDest, 1687 mFrameLoader); 1688 } 1689 } 1690 1691 NS_IMETHODIMP 1692 nsObjectLoadingContent::GetSrcURI(nsIURI** aURI) { 1693 NS_IF_ADDREF(*aURI = GetSrcURI()); 1694 return NS_OK; 1695 } 1696 1697 void nsObjectLoadingContent::TriggerInnerFallbackLoads() { 1698 MOZ_ASSERT(!mFrameLoader && !mChannel, 1699 "ConfigureFallback called with loaded content"); 1700 MOZ_ASSERT(mType == ObjectType::Fallback); 1701 1702 Element* el = AsElement(); 1703 if (!el->IsHTMLElement(nsGkAtoms::object)) { 1704 return; 1705 } 1706 // Do a depth-first traverse of node tree with the current element as root, 1707 // looking for non-<param> elements. If we find some then we have an HTML 1708 // fallback for this element. 1709 for (nsIContent* child = el->GetFirstChild(); child;) { 1710 // <object> and <embed> elements in the fallback need to StartObjectLoad. 1711 // Their children should be ignored since they are part of those element's 1712 // fallback. 1713 if (auto* embed = HTMLEmbedElement::FromNode(child)) { 1714 embed->StartObjectLoad(true, true); 1715 // Skip the children 1716 child = child->GetNextNonChildNode(el); 1717 } else if (auto* object = HTMLObjectElement::FromNode(child)) { 1718 object->StartObjectLoad(true, true); 1719 // Skip the children 1720 child = child->GetNextNonChildNode(el); 1721 } else { 1722 child = child->GetNextNode(el); 1723 } 1724 } 1725 } 1726 1727 NS_IMETHODIMP 1728 nsObjectLoadingContent::UpgradeLoadToDocument( 1729 nsIChannel* aRequest, BrowsingContext** aBrowsingContext) { 1730 AUTO_PROFILER_LABEL("nsObjectLoadingContent::UpgradeLoadToDocument", NETWORK); 1731 1732 LOG(("OBJLC [%p]: UpgradeLoadToDocument", this)); 1733 1734 if (aRequest != mChannel || !aRequest) { 1735 // happens when a new load starts before the previous one got here. 1736 return NS_BINDING_ABORTED; 1737 } 1738 1739 // We should be state loading. 1740 if (mType != ObjectType::Loading) { 1741 MOZ_ASSERT_UNREACHABLE("Should be type loading at this point"); 1742 return NS_BINDING_ABORTED; 1743 } 1744 MOZ_ASSERT(!mChannelLoaded, "mChannelLoaded set already?"); 1745 MOZ_ASSERT(!mFinalListener, "mFinalListener exists already?"); 1746 1747 mChannelLoaded = true; 1748 1749 // We don't need to check for errors here, unlike in `OnStartRequest`, as 1750 // `UpgradeLoadToDocument` is only called when the load is going to become a 1751 // process-switching load. As we never process switch for failed object loads, 1752 // we know our channel status is successful. 1753 1754 // Call `LoadObject` to trigger our nsObjectLoadingContext to switch into the 1755 // specified new state. 1756 nsresult rv = LoadObject(true, false, aRequest); 1757 if (NS_WARN_IF(NS_FAILED(rv))) { 1758 return rv; 1759 } 1760 1761 RefPtr<BrowsingContext> bc = GetBrowsingContext(); 1762 if (!bc) { 1763 return NS_ERROR_FAILURE; 1764 } 1765 1766 // At this point we know that we have a browsing context, so it's time to make 1767 // sure that that browsing context gets the correct container feature policy. 1768 // This is needed for `DocumentLoadListener::MaybeTriggerProcessSwitch` to be 1769 // able to start loading the document with the correct container feature 1770 // policy in the load info. 1771 RefreshFeaturePolicy(); 1772 1773 bc.forget(aBrowsingContext); 1774 return NS_OK; 1775 } 1776 1777 bool nsObjectLoadingContent::ShouldBlockContent() { 1778 return mContentBlockingEnabled && mURI && IsFlashMIME(mContentType) && 1779 StaticPrefs::browser_safebrowsing_blockedURIs_enabled(); 1780 } 1781 1782 Document* nsObjectLoadingContent::GetContentDocument( 1783 nsIPrincipal& aSubjectPrincipal) { 1784 Element* el = AsElement(); 1785 if (!el->IsInComposedDoc()) { 1786 return nullptr; 1787 } 1788 1789 Document* sub_doc = el->OwnerDoc()->GetSubDocumentFor(el); 1790 if (!sub_doc) { 1791 return nullptr; 1792 } 1793 1794 // Return null for cross-origin contentDocument. 1795 if (!aSubjectPrincipal.SubsumesConsideringDomain(sub_doc->NodePrincipal())) { 1796 return nullptr; 1797 } 1798 1799 return sub_doc; 1800 } 1801 1802 void nsObjectLoadingContent::MaybeFireErrorEvent() { 1803 Element* el = AsElement(); 1804 // Queue a task to fire an error event if we're an <object> element. The 1805 // queueing is important, since then we don't have to worry about reentry. 1806 if (el->IsHTMLElement(nsGkAtoms::object)) { 1807 RefPtr<AsyncEventDispatcher> loadBlockingAsyncDispatcher = 1808 new LoadBlockingAsyncEventDispatcher(el, u"error"_ns, CanBubble::eNo, 1809 ChromeOnlyDispatch::eNo); 1810 loadBlockingAsyncDispatcher->PostDOMEvent(); 1811 } 1812 } 1813 1814 bool nsObjectLoadingContent::BlockEmbedOrObjectContentLoading() { 1815 Element* el = AsElement(); 1816 1817 // Traverse up the node tree to see if we have any ancestors that may block us 1818 // from loading 1819 for (nsIContent* parent = el->GetParent(); parent; 1820 parent = parent->GetParent()) { 1821 if (parent->IsAnyOfHTMLElements(nsGkAtoms::video, nsGkAtoms::audio)) { 1822 return true; 1823 } 1824 // If we have an ancestor that is an object with a source, it'll have an 1825 // associated displayed type. If that type is not null, don't load content 1826 // for the embed. 1827 if (auto* object = HTMLObjectElement::FromNode(parent)) { 1828 if (object->Type() != ObjectType::Fallback) { 1829 return true; 1830 } 1831 } 1832 } 1833 return false; 1834 } 1835 1836 void nsObjectLoadingContent::SubdocumentIntrinsicSizeOrRatioChanged( 1837 const Maybe<IntrinsicSize>& aIntrinsicSize, 1838 const Maybe<AspectRatio>& aIntrinsicRatio) { 1839 if (aIntrinsicSize == mSubdocumentIntrinsicSize && 1840 aIntrinsicRatio == mSubdocumentIntrinsicRatio) { 1841 return; 1842 } 1843 1844 mSubdocumentIntrinsicSize = aIntrinsicSize; 1845 mSubdocumentIntrinsicRatio = aIntrinsicRatio; 1846 1847 if (nsSubDocumentFrame* sdf = do_QueryFrame(AsElement()->GetPrimaryFrame())) { 1848 sdf->SubdocumentIntrinsicSizeOrRatioChanged(); 1849 } 1850 } 1851 1852 void nsObjectLoadingContent::SubdocumentImageLoadComplete(nsresult aResult) { 1853 ObjectType oldType = mType; 1854 if (NS_FAILED(aResult)) { 1855 UnloadObject(); 1856 mType = ObjectType::Fallback; 1857 TriggerInnerFallbackLoads(); 1858 NotifyStateChanged(oldType, true); 1859 return; 1860 } 1861 1862 // (mChannelLoaded && mChannel) indicates this is a good state, not any sort 1863 // of failures. 1864 MOZ_DIAGNOSTIC_ASSERT_IF(mChannelLoaded && mChannel, 1865 mType == ObjectType::Document); 1866 NotifyStateChanged(oldType, true); 1867 } 1868 1869 void nsObjectLoadingContent::MaybeStoreCrossOriginFeaturePolicy() { 1870 MOZ_DIAGNOSTIC_ASSERT(mFrameLoader); 1871 if (!mFrameLoader) { 1872 return; 1873 } 1874 1875 // If the browsingContext is not ready (because docshell is dead), don't try 1876 // to create one. 1877 if (!mFrameLoader->IsRemoteFrame() && !mFrameLoader->GetExistingDocShell()) { 1878 return; 1879 } 1880 1881 RefPtr<BrowsingContext> browsingContext = mFrameLoader->GetBrowsingContext(); 1882 1883 if (!browsingContext || !browsingContext->IsContentSubframe()) { 1884 return; 1885 } 1886 1887 auto* el = nsGenericHTMLElement::FromNode(AsElement()); 1888 if (!el->IsInComposedDoc()) { 1889 return; 1890 } 1891 1892 if (ContentChild* cc = ContentChild::GetSingleton()) { 1893 (void)cc->SendSetContainerFeaturePolicy( 1894 browsingContext, Some(mFeaturePolicy->ToFeaturePolicyInfo())); 1895 } 1896 } 1897 1898 /* static */ already_AddRefed<nsIPrincipal> 1899 nsObjectLoadingContent::GetFeaturePolicyDefaultOrigin(nsINode* aNode) { 1900 auto* el = nsGenericHTMLElement::FromNode(aNode); 1901 nsCOMPtr<nsIURI> nodeURI; 1902 // Different elements keep this in various locations 1903 if (el->NodeInfo()->Equals(nsGkAtoms::object)) { 1904 el->GetURIAttr(nsGkAtoms::data, nullptr, getter_AddRefs(nodeURI)); 1905 } else if (el->NodeInfo()->Equals(nsGkAtoms::embed)) { 1906 el->GetURIAttr(nsGkAtoms::src, nullptr, getter_AddRefs(nodeURI)); 1907 } 1908 1909 nsCOMPtr<nsIPrincipal> principal; 1910 if (nodeURI) { 1911 principal = BasePrincipal::CreateContentPrincipal( 1912 nodeURI, 1913 BasePrincipal::Cast(el->NodePrincipal())->OriginAttributesRef()); 1914 } else { 1915 principal = el->NodePrincipal(); 1916 } 1917 1918 return principal.forget(); 1919 } 1920 1921 void nsObjectLoadingContent::RefreshFeaturePolicy() { 1922 if (mType != ObjectType::Document) { 1923 return; 1924 } 1925 1926 if (!mFeaturePolicy) { 1927 mFeaturePolicy = MakeAndAddRef<FeaturePolicy>(AsElement()); 1928 } 1929 1930 // The origin can change if 'src' or 'data' attributes change. 1931 nsCOMPtr<nsIPrincipal> origin = GetFeaturePolicyDefaultOrigin(AsElement()); 1932 MOZ_ASSERT(origin); 1933 mFeaturePolicy->SetDefaultOrigin(origin); 1934 1935 mFeaturePolicy->InheritPolicy(AsElement()->OwnerDoc()->FeaturePolicy()); 1936 MaybeStoreCrossOriginFeaturePolicy(); 1937 }