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/. */
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).
13 #include "imgLoader.h"
14 #include "nsIClassOfService.h"
15 #include "nsIConsoleService.h"
16 #include "nsIDocShell.h"
17 #include "mozilla/BasePrincipal.h"
18 #include "mozilla/dom/BindContext.h"
19 #include "mozilla/dom/Document.h"
20 #include "nsIExternalProtocolHandler.h"
21 #include "nsIPermissionManager.h"
22 #include "nsIHttpChannel.h"
23 #include "nsINestedURI.h"
24 #include "nsScriptSecurityManager.h"
25 #include "nsIURILoader.h"
26 #include "nsIScriptChannel.h"
27 #include "nsIAsyncVerifyRedirectCallback.h"
28 #include "nsIAppShell.h"
29 #include "nsIScriptError.h"
30 #include "nsSubDocumentFrame.h"
35 #include "mozilla/Logging.h"
37 #include "nsContentPolicyUtils.h"
38 #include "nsContentUtils.h"
39 #include "nsDocShellLoadState.h"
40 #include "nsGkAtoms.h"
41 #include "nsThreadUtils.h"
42 #include "nsNetUtil.h"
43 #include "nsMimeTypes.h"
44 #include "nsStyleUtil.h"
45 #include "mozilla/Preferences.h"
46 #include "nsQueryObject.h"
49 #include "nsFrameLoader.h"
51 #include "nsObjectLoadingContent.h"
53 #include "nsWidgetsCID.h"
54 #include "mozilla/BasicEvents.h"
55 #include "mozilla/Components.h"
56 #include "mozilla/LoadInfo.h"
57 #include "mozilla/dom/BindingUtils.h"
58 #include "mozilla/dom/Element.h"
59 #include "mozilla/dom/Event.h"
60 #include "mozilla/dom/ScriptSettings.h"
61 #include "mozilla/AsyncEventDispatcher.h"
62 #include "mozilla/EventDispatcher.h"
63 #include "mozilla/IMEStateManager.h"
64 #include "mozilla/widget/IMEData.h"
65 #include "mozilla/dom/ContentChild.h"
66 #include "mozilla/dom/HTMLEmbedElement.h"
67 #include "mozilla/dom/HTMLObjectElementBinding.h"
68 #include "mozilla/dom/HTMLObjectElement.h"
69 #include "mozilla/dom/UserActivation.h"
70 #include "mozilla/dom/nsCSPContext.h"
71 #include "mozilla/net/DocumentChannel.h"
72 #include "mozilla/net/UrlClassifierFeatureFactory.h"
73 #include "mozilla/PresShell.h"
74 #include "mozilla/ProfilerLabels.h"
75 #include "mozilla/StaticPrefs_browser.h"
76 #include "nsChannelClassifier.h"
77 #include "nsFocusManager.h"
78 #include "ReferrerInfo.h"
79 #include "nsIEffectiveTLDService.h"
82 // Thanks so much, Microsoft! :(
88 static const char kPrefYoutubeRewrite
[] = "plugins.rewrite_youtube_embeds";
90 using namespace mozilla
;
91 using namespace mozilla::dom
;
92 using namespace mozilla::net
;
94 static LogModule
* GetObjectLog() {
95 static LazyLogModule
sLog("objlc");
99 #define LOG(args) MOZ_LOG(GetObjectLog(), mozilla::LogLevel::Debug, args)
100 #define LOG_ENABLED() MOZ_LOG_TEST(GetObjectLog(), mozilla::LogLevel::Debug)
102 static bool IsFlashMIME(const nsACString
& aMIMEType
) {
103 return aMIMEType
.LowerCaseEqualsASCII("application/x-shockwave-flash") ||
104 aMIMEType
.LowerCaseEqualsASCII("application/futuresplash") ||
105 aMIMEType
.LowerCaseEqualsASCII("application/x-shockwave-flash-test");
108 static bool IsPluginMIME(const nsACString
& aMIMEType
) {
109 return IsFlashMIME(aMIMEType
) ||
110 aMIMEType
.LowerCaseEqualsASCII("application/x-test");
114 /// Runnables and helper classes
117 // Sets a object's mIsLoading bit to false when destroyed
118 class AutoSetLoadingToFalse
{
120 explicit AutoSetLoadingToFalse(nsObjectLoadingContent
* aContent
)
121 : mContent(aContent
) {}
122 ~AutoSetLoadingToFalse() { mContent
->mIsLoading
= false; }
125 nsObjectLoadingContent
* mContent
;
132 bool nsObjectLoadingContent::IsSuccessfulRequest(nsIRequest
* aRequest
,
134 nsresult rv
= aRequest
->GetStatus(aStatus
);
135 if (NS_FAILED(rv
) || NS_FAILED(*aStatus
)) {
139 // This may still be an error page or somesuch
140 nsCOMPtr
<nsIHttpChannel
> httpChan(do_QueryInterface(aRequest
));
143 rv
= httpChan
->GetRequestSucceeded(&success
);
144 if (NS_FAILED(rv
) || !success
) {
149 // Otherwise, the request is successful
153 static bool CanHandleURI(nsIURI
* aURI
) {
154 nsAutoCString scheme
;
155 if (NS_FAILED(aURI
->GetScheme(scheme
))) {
159 nsCOMPtr
<nsIIOService
> ios
= mozilla::components::IO::Service();
164 nsCOMPtr
<nsIProtocolHandler
> handler
;
165 ios
->GetProtocolHandler(scheme
.get(), getter_AddRefs(handler
));
170 nsCOMPtr
<nsIExternalProtocolHandler
> extHandler
= do_QueryInterface(handler
);
171 // We can handle this URI if its protocol handler is not the external one
172 return extHandler
== nullptr;
175 // Helper for tedious URI equality syntax when one or both arguments may be
176 // null and URIEquals(null, null) should be true
177 static bool inline URIEquals(nsIURI
* a
, nsIURI
* b
) {
179 return (!a
&& !b
) || (a
&& b
&& NS_SUCCEEDED(a
->Equals(b
, &equal
)) && equal
);
186 // Helper to spawn the frameloader.
187 void nsObjectLoadingContent::SetupFrameLoader() {
188 mFrameLoader
= nsFrameLoader::Create(AsElement(), mNetworkCreated
);
189 MOZ_ASSERT(mFrameLoader
, "nsFrameLoader::Create failed");
192 // Helper to spawn the frameloader and return a pointer to its docshell.
193 already_AddRefed
<nsIDocShell
> nsObjectLoadingContent::SetupDocShell(
194 nsIURI
* aRecursionCheckURI
) {
200 nsCOMPtr
<nsIDocShell
> docShell
;
202 if (aRecursionCheckURI
) {
203 nsresult rv
= mFrameLoader
->CheckForRecursiveLoad(aRecursionCheckURI
);
204 if (NS_SUCCEEDED(rv
)) {
205 IgnoredErrorResult result
;
206 docShell
= mFrameLoader
->GetDocShell(result
);
207 if (result
.Failed()) {
208 MOZ_ASSERT_UNREACHABLE("Could not get DocShell from mFrameLoader?");
211 LOG(("OBJLC [%p]: Aborting recursive load", this));
216 mFrameLoader
->Destroy();
217 mFrameLoader
= nullptr;
221 return docShell
.forget();
224 void nsObjectLoadingContent::UnbindFromTree() {
225 // Reset state and clear pending events
226 /// XXX(johns): The implementation for GenericFrame notes that ideally we
227 /// would keep the docshell around, but trash the frameloader
231 nsObjectLoadingContent::nsObjectLoadingContent()
232 : mType(ObjectType::Loading
),
233 mChannelLoaded(false),
234 mNetworkCreated(true),
235 mContentBlockingEnabled(false),
238 mScriptRequested(false),
239 mRewrittenYoutubeEmbed(false) {}
241 nsObjectLoadingContent::~nsObjectLoadingContent() {
242 // Should have been unbound from the tree at this point, and
243 // CheckPluginStopEvent keeps us alive
245 MOZ_ASSERT_UNREACHABLE(
246 "Should not be tearing down frame loaders at this point");
247 mFrameLoader
->Destroy();
251 // nsIRequestObserver
253 nsObjectLoadingContent::OnStartRequest(nsIRequest
* aRequest
) {
254 AUTO_PROFILER_LABEL("nsObjectLoadingContent::OnStartRequest", NETWORK
);
256 LOG(("OBJLC [%p]: Channel OnStartRequest", this));
258 if (aRequest
!= mChannel
|| !aRequest
) {
259 // happens when a new load starts before the previous one got here
260 return NS_BINDING_ABORTED
;
263 nsCOMPtr
<nsIChannel
> chan(do_QueryInterface(aRequest
));
264 NS_ASSERTION(chan
, "Why is our request not a channel?");
266 nsresult status
= NS_OK
;
267 bool success
= IsSuccessfulRequest(aRequest
, &status
);
269 // If we have already switched to type document, we're doing a
270 // process-switching DocumentChannel load. We should be able to pass down the
271 // load to our inner listener, but should also make sure to update our local
273 if (mType
== ObjectType::Document
) {
274 if (!mFinalListener
) {
275 MOZ_ASSERT_UNREACHABLE(
276 "Already is Document, but don't have final listener yet?");
277 return NS_BINDING_ABORTED
;
280 // If the load looks successful, fix up some of our local state before
281 // forwarding the request to the final URI loader.
283 // Forward load errors down to the document loader, so we don't tear down
284 // the nsDocShell ourselves.
286 LOG(("OBJLC [%p]: OnStartRequest: DocumentChannel request succeeded\n",
288 nsCString channelType
;
289 MOZ_ALWAYS_SUCCEEDS(mChannel
->GetContentType(channelType
));
291 if (GetTypeOfContent(channelType
) != ObjectType::Document
) {
292 MOZ_CRASH("DocumentChannel request with non-document MIME");
294 mContentType
= channelType
;
297 NS_GetFinalChannelURI(mChannel
, getter_AddRefs(mURI
)));
300 return mFinalListener
->OnStartRequest(aRequest
);
303 // Otherwise we should be state loading, and call LoadObject with the channel
304 if (mType
!= ObjectType::Loading
) {
305 MOZ_ASSERT_UNREACHABLE("Should be type loading at this point");
306 return NS_BINDING_ABORTED
;
308 NS_ASSERTION(!mChannelLoaded
, "mChannelLoaded set already?");
309 NS_ASSERTION(!mFinalListener
, "mFinalListener exists already?");
311 mChannelLoaded
= true;
313 if (status
== NS_ERROR_BLOCKED_URI
) {
314 nsCOMPtr
<nsIConsoleService
> console(
315 do_GetService("@mozilla.org/consoleservice;1"));
317 nsCOMPtr
<nsIURI
> uri
;
318 chan
->GetURI(getter_AddRefs(uri
));
321 NS_ConvertASCIItoUTF16(uri
->GetSpecOrDefault().get()) +
323 u
" since it was found on an internal Firefox blocklist.");
324 console
->LogStringMessage(message
.get());
326 mContentBlockingEnabled
= true;
327 return NS_ERROR_FAILURE
;
330 if (UrlClassifierFeatureFactory::IsClassifierBlockingErrorCode(status
)) {
331 mContentBlockingEnabled
= true;
332 return NS_ERROR_FAILURE
;
336 LOG(("OBJLC [%p]: OnStartRequest: Request failed\n", this));
337 // If the request fails, we still call LoadObject() to handle fallback
338 // content and notifying of failure. (mChannelLoaded && !mChannel) indicates
341 LoadObject(true, false);
342 return NS_ERROR_FAILURE
;
345 return LoadObject(true, false, aRequest
);
349 nsObjectLoadingContent::OnStopRequest(nsIRequest
* aRequest
,
350 nsresult aStatusCode
) {
351 AUTO_PROFILER_LABEL("nsObjectLoadingContent::OnStopRequest", NETWORK
);
353 // Handle object not loading error because source was a tracking URL (or
354 // fingerprinting, cryptomining, etc.).
355 // We make a note of this object node by including it in a dedicated
356 // array of blocked tracking nodes under its parent document.
357 if (UrlClassifierFeatureFactory::IsClassifierBlockingErrorCode(aStatusCode
)) {
358 nsCOMPtr
<nsIContent
> thisNode
=
359 do_QueryInterface(static_cast<nsIObjectLoadingContent
*>(this));
360 if (thisNode
&& thisNode
->IsInComposedDoc()) {
361 thisNode
->GetComposedDoc()->AddBlockedNodeByClassifier(thisNode
);
365 if (aRequest
!= mChannel
) {
366 return NS_BINDING_ABORTED
;
371 if (mFinalListener
) {
372 // This may re-enter in the case of plugin listeners
373 nsCOMPtr
<nsIStreamListener
> listenerGrip(mFinalListener
);
374 mFinalListener
= nullptr;
375 listenerGrip
->OnStopRequest(aRequest
, aStatusCode
);
378 // Return value doesn't matter
384 nsObjectLoadingContent::OnDataAvailable(nsIRequest
* aRequest
,
385 nsIInputStream
* aInputStream
,
386 uint64_t aOffset
, uint32_t aCount
) {
387 if (aRequest
!= mChannel
) {
388 return NS_BINDING_ABORTED
;
391 if (mFinalListener
) {
392 // This may re-enter in the case of plugin listeners
393 nsCOMPtr
<nsIStreamListener
> listenerGrip(mFinalListener
);
394 return listenerGrip
->OnDataAvailable(aRequest
, aInputStream
, aOffset
,
398 // We shouldn't have a connected channel with no final listener
399 MOZ_ASSERT_UNREACHABLE(
400 "Got data for channel with no connected final "
404 return NS_ERROR_UNEXPECTED
;
408 nsObjectLoadingContent::GetActualType(nsACString
& aType
) {
409 aType
= mContentType
;
414 nsObjectLoadingContent::GetDisplayedType(uint32_t* aType
) {
415 *aType
= DisplayedType();
419 // nsIInterfaceRequestor
420 // We use a shim class to implement this so that JS consumers still
421 // see an interface requestor even though WebIDL bindings don't expose
423 class ObjectInterfaceRequestorShim final
: public nsIInterfaceRequestor
,
424 public nsIChannelEventSink
,
425 public nsIStreamListener
{
427 NS_DECL_CYCLE_COLLECTING_ISUPPORTS
428 NS_DECL_CYCLE_COLLECTION_CLASS_AMBIGUOUS(ObjectInterfaceRequestorShim
,
429 nsIInterfaceRequestor
)
430 NS_DECL_NSIINTERFACEREQUESTOR
431 // RefPtr<nsObjectLoadingContent> fails due to ambiguous AddRef/Release,
432 // hence the ugly static cast :(
433 NS_FORWARD_NSICHANNELEVENTSINK(
434 static_cast<nsObjectLoadingContent
*>(mContent
.get())->)
435 NS_FORWARD_NSISTREAMLISTENER(
436 static_cast<nsObjectLoadingContent
*>(mContent
.get())->)
437 NS_FORWARD_NSIREQUESTOBSERVER(
438 static_cast<nsObjectLoadingContent
*>(mContent
.get())->)
440 explicit ObjectInterfaceRequestorShim(nsIObjectLoadingContent
* aContent
)
441 : mContent(aContent
) {}
444 ~ObjectInterfaceRequestorShim() = default;
445 nsCOMPtr
<nsIObjectLoadingContent
> mContent
;
448 NS_IMPL_CYCLE_COLLECTION(ObjectInterfaceRequestorShim
, mContent
)
450 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(ObjectInterfaceRequestorShim
)
451 NS_INTERFACE_MAP_ENTRY(nsIInterfaceRequestor
)
452 NS_INTERFACE_MAP_ENTRY(nsIChannelEventSink
)
453 NS_INTERFACE_MAP_ENTRY(nsIStreamListener
)
454 NS_INTERFACE_MAP_ENTRY(nsIRequestObserver
)
455 NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports
, nsIInterfaceRequestor
)
458 NS_IMPL_CYCLE_COLLECTING_ADDREF(ObjectInterfaceRequestorShim
)
459 NS_IMPL_CYCLE_COLLECTING_RELEASE(ObjectInterfaceRequestorShim
)
462 ObjectInterfaceRequestorShim::GetInterface(const nsIID
& aIID
, void** aResult
) {
463 if (aIID
.Equals(NS_GET_IID(nsIChannelEventSink
))) {
464 nsIChannelEventSink
* sink
= this;
469 if (aIID
.Equals(NS_GET_IID(nsIObjectLoadingContent
))) {
470 nsIObjectLoadingContent
* olc
= mContent
;
475 return NS_NOINTERFACE
;
478 // nsIChannelEventSink
480 nsObjectLoadingContent::AsyncOnChannelRedirect(
481 nsIChannel
* aOldChannel
, nsIChannel
* aNewChannel
, uint32_t aFlags
,
482 nsIAsyncVerifyRedirectCallback
* cb
) {
483 // If we're already busy with a new load, or have no load at all,
484 // cancel the redirect.
485 if (!mChannel
|| aOldChannel
!= mChannel
) {
486 return NS_BINDING_ABORTED
;
489 mChannel
= aNewChannel
;
491 if (mFinalListener
) {
492 nsCOMPtr
<nsIChannelEventSink
> sink(do_QueryInterface(mFinalListener
));
493 MOZ_RELEASE_ASSERT(sink
, "mFinalListener isn't nsIChannelEventSink?");
494 if (mType
!= ObjectType::Document
) {
495 MOZ_ASSERT_UNREACHABLE(
496 "Not a DocumentChannel load, but we're getting a "
497 "AsyncOnChannelRedirect with a mFinalListener?");
498 return NS_BINDING_ABORTED
;
501 return sink
->AsyncOnChannelRedirect(aOldChannel
, aNewChannel
, aFlags
, cb
);
504 cb
->OnRedirectVerifyCallback(NS_OK
);
508 void nsObjectLoadingContent::MaybeRewriteYoutubeEmbed(nsIURI
* aURI
,
510 nsIURI
** aRewrittenURI
) {
511 nsCOMPtr
<nsIEffectiveTLDService
> tldService
=
512 do_GetService(NS_EFFECTIVETLDSERVICE_CONTRACTID
);
513 // If we can't analyze the URL, just pass on through.
515 NS_WARNING("Could not get TLD service!");
519 nsAutoCString currentBaseDomain
;
520 bool ok
= NS_SUCCEEDED(tldService
->GetBaseDomain(aURI
, 0, currentBaseDomain
));
522 // Data URIs (commonly used for things like svg embeds) won't parse
523 // correctly, so just fail silently here.
527 // See if URL is referencing youtube
528 if (!currentBaseDomain
.EqualsLiteral("youtube.com") &&
529 !currentBaseDomain
.EqualsLiteral("youtube-nocookie.com")) {
533 // We should only rewrite URLs with paths starting with "/v/", as we shouldn't
534 // touch object nodes with "/embed/" urls that already do that right thing.
536 aURI
->GetPathQueryRef(path
);
537 if (!StringBeginsWith(path
, "/v/"_ns
)) {
541 // See if requester is planning on using the JS API.
543 nsresult rv
= aURI
->GetSpec(uri
);
548 // Some YouTube urls have parameters in path components, e.g.
549 // http://youtube.com/embed/7LcUOEP7Brc&start=35. These URLs work with flash,
550 // but break iframe/object embedding. If this situation occurs with rewritten
551 // URLs, convert the parameters to query in order to make the video load
552 // correctly as an iframe. In either case, warn about it in the
553 // developer console.
554 int32_t ampIndex
= uri
.FindChar('&', 0);
555 bool replaceQuery
= false;
556 if (ampIndex
!= -1) {
557 int32_t qmIndex
= uri
.FindChar('?', 0);
558 if (qmIndex
== -1 || qmIndex
> ampIndex
) {
563 Document
* doc
= AsElement()->OwnerDoc();
564 // If we've made it this far, we've got a rewritable embed. Log it in
566 doc
->SetUseCounter(eUseCounter_custom_YouTubeFlashEmbed
);
568 // If we're pref'd off, return after telemetry has been logged.
569 if (!Preferences::GetBool(kPrefYoutubeRewrite
)) {
573 nsAutoString utf16OldURI
= NS_ConvertUTF8toUTF16(uri
);
574 // If we need to convert the URL, it means an ampersand comes first.
575 // Use the index we found earlier.
577 // Replace question marks with ampersands.
578 uri
.ReplaceChar('?', '&');
579 // Replace the first ampersand with a question mark.
580 uri
.SetCharAt('?', ampIndex
);
582 // Switch out video access url formats, which should possibly allow HTML5
584 uri
.ReplaceSubstring("/v/"_ns
, "/embed/"_ns
);
585 nsAutoString utf16URI
= NS_ConvertUTF8toUTF16(uri
);
586 rv
= nsContentUtils::NewURIWithDocumentCharset(aRewrittenURI
, utf16URI
, doc
,
591 AutoTArray
<nsString
, 2> params
= {utf16OldURI
, utf16URI
};
593 // If there's no query to rewrite, just notify in the developer console
594 // that we're changing the embed.
596 msgName
= "RewriteYouTubeEmbed";
598 msgName
= "RewriteYouTubeEmbedPathParams";
600 nsContentUtils::ReportToConsole(nsIScriptError::warningFlag
, "Plugins"_ns
,
601 doc
, nsContentUtils::eDOM_PROPERTIES
, msgName
,
605 bool nsObjectLoadingContent::CheckLoadPolicy(int16_t* aContentPolicy
) {
606 if (!aContentPolicy
|| !mURI
) {
607 MOZ_ASSERT_UNREACHABLE("Doing it wrong");
611 Element
* el
= AsElement();
612 Document
* doc
= el
->OwnerDoc();
614 nsContentPolicyType contentPolicyType
= GetContentPolicyType();
616 nsCOMPtr
<nsILoadInfo
> secCheckLoadInfo
=
617 new LoadInfo(doc
->NodePrincipal(), // loading principal
618 doc
->NodePrincipal(), // triggering principal
619 el
, nsILoadInfo::SEC_ONLY_FOR_EXPLICIT_CONTENTSEC_CHECK
,
622 *aContentPolicy
= nsIContentPolicy::ACCEPT
;
624 NS_CheckContentLoadPolicy(mURI
, secCheckLoadInfo
, aContentPolicy
,
625 nsContentUtils::GetContentPolicy());
626 NS_ENSURE_SUCCESS(rv
, false);
627 if (NS_CP_REJECTED(*aContentPolicy
)) {
628 LOG(("OBJLC [%p]: Content policy denied load of %s", this,
629 mURI
->GetSpecOrDefault().get()));
636 bool nsObjectLoadingContent::CheckProcessPolicy(int16_t* aContentPolicy
) {
637 if (!aContentPolicy
) {
638 MOZ_ASSERT_UNREACHABLE("Null out variable");
642 Element
* el
= AsElement();
643 Document
* doc
= el
->OwnerDoc();
645 nsContentPolicyType objectType
;
647 case ObjectType::Document
:
648 objectType
= nsIContentPolicy::TYPE_DOCUMENT
;
651 MOZ_ASSERT_UNREACHABLE(
652 "Calling checkProcessPolicy with an unexpected type");
656 nsCOMPtr
<nsILoadInfo
> secCheckLoadInfo
= new LoadInfo(
657 doc
->NodePrincipal(), // loading principal
658 doc
->NodePrincipal(), // triggering principal
659 el
, nsILoadInfo::SEC_ONLY_FOR_EXPLICIT_CONTENTSEC_CHECK
, objectType
);
661 *aContentPolicy
= nsIContentPolicy::ACCEPT
;
662 nsresult rv
= NS_CheckContentProcessPolicy(
663 mURI
? mURI
: mBaseURI
, secCheckLoadInfo
, aContentPolicy
,
664 nsContentUtils::GetContentPolicy());
665 NS_ENSURE_SUCCESS(rv
, false);
667 if (NS_CP_REJECTED(*aContentPolicy
)) {
668 LOG(("OBJLC [%p]: CheckContentProcessPolicy rejected load", this));
675 bool nsObjectLoadingContent::IsSyntheticImageDocument() const {
676 if (mType
!= ObjectType::Document
|| !mFrameLoader
) {
680 BrowsingContext
* browsingContext
= mFrameLoader
->GetExtantBrowsingContext();
681 return browsingContext
&& browsingContext
->GetIsSyntheticDocumentContainer();
684 nsObjectLoadingContent::ParameterUpdateFlags
685 nsObjectLoadingContent::UpdateObjectParameters() {
686 Element
* el
= AsElement();
688 uint32_t caps
= GetCapabilities();
689 LOG(("OBJLC [%p]: Updating object parameters", this));
692 nsAutoCString newMime
;
693 nsAutoString typeAttr
;
694 nsCOMPtr
<nsIURI
> newURI
;
695 nsCOMPtr
<nsIURI
> newBaseURI
;
697 // Set if this state can't be used to load anything, forces
698 // ObjectType::Fallback
699 bool stateInvalid
= false;
700 // Indicates what parameters changed.
701 // eParamChannelChanged - means parameters that affect channel opening
703 // eParamStateChanged - means anything that affects what content we load
704 // changed, even if the channel we'd open remains the
707 // State changes outside of the channel parameters only matter if we've
708 // already opened a channel or tried to instantiate content, whereas channel
709 // parameter changes require re-opening the channel even if we haven't gotten
711 ParameterUpdateFlags retval
= eParamNoChange
;
714 /// Initial MIME Type
717 if (caps
& eFallbackIfClassIDPresent
&&
718 el
->HasNonEmptyAttr(nsGkAtoms::classid
)) {
719 // We don't support class ID plugin references, so we should always treat
720 // having class Ids as attributes as invalid, and fallback accordingly.
729 nsAutoString codebaseStr
;
730 nsIURI
* docBaseURI
= el
->GetBaseURI();
731 el
->GetAttr(nsGkAtoms::codebase
, codebaseStr
);
733 if (!codebaseStr
.IsEmpty()) {
734 rv
= nsContentUtils::NewURIWithDocumentCharset(
735 getter_AddRefs(newBaseURI
), codebaseStr
, el
->OwnerDoc(), docBaseURI
);
739 ("OBJLC [%p]: Could not parse plugin's codebase as a URI, "
740 "will use document baseURI instead",
745 // If we failed to build a valid URI, use the document's base URI
747 newBaseURI
= docBaseURI
;
750 nsAutoString rawTypeAttr
;
751 el
->GetAttr(nsGkAtoms::type
, rawTypeAttr
);
752 if (!rawTypeAttr
.IsEmpty()) {
753 typeAttr
= rawTypeAttr
;
756 nsContentUtils::SplitMimeType(rawTypeAttr
, mime
, params
);
757 CopyUTF16toUTF8(mime
, newMime
);
765 // Different elements keep this in various locations
766 if (el
->NodeInfo()->Equals(nsGkAtoms::object
)) {
767 el
->GetAttr(nsGkAtoms::data
, uriStr
);
768 } else if (el
->NodeInfo()->Equals(nsGkAtoms::embed
)) {
769 el
->GetAttr(nsGkAtoms::src
, uriStr
);
771 MOZ_ASSERT_UNREACHABLE("Unrecognized plugin-loading tag");
774 mRewrittenYoutubeEmbed
= false;
776 // Note that the baseURI changing could affect the newURI, even if uriStr did
778 if (!uriStr
.IsEmpty()) {
779 rv
= nsContentUtils::NewURIWithDocumentCharset(
780 getter_AddRefs(newURI
), uriStr
, el
->OwnerDoc(), newBaseURI
);
781 nsCOMPtr
<nsIURI
> rewrittenURI
;
782 MaybeRewriteYoutubeEmbed(newURI
, newBaseURI
, getter_AddRefs(rewrittenURI
));
784 newURI
= rewrittenURI
;
785 mRewrittenYoutubeEmbed
= true;
786 newMime
= "text/html"_ns
;
795 /// Check if the original (pre-channel) content-type or URI changed, and
796 /// record mOriginal{ContentType,URI}
799 if ((mOriginalContentType
!= newMime
) || !URIEquals(mOriginalURI
, newURI
)) {
800 // These parameters changing requires re-opening the channel, so don't
801 // consider the currently-open channel below
802 // XXX(johns): Changing the mime type might change our decision on whether
803 // or not we load a channel, so we count changes to it as a
804 // channel parameter change for the sake of simplicity.
805 retval
= (ParameterUpdateFlags
)(retval
| eParamChannelChanged
);
806 LOG(("OBJLC [%p]: Channel parameters changed", this));
808 mOriginalContentType
= newMime
;
809 mOriginalURI
= newURI
;
812 /// If we have a channel, see if its MIME type should take precendence and
813 /// check the final (redirected) URL
816 // If we have a loaded channel and channel parameters did not change, use it
817 // to determine what we would load.
818 bool useChannel
= mChannelLoaded
&& !(retval
& eParamChannelChanged
);
819 // If we have a channel and are type loading, as opposed to having an existing
820 // channel for a previous load.
821 bool newChannel
= useChannel
&& mType
== ObjectType::Loading
;
823 RefPtr
<DocumentChannel
> documentChannel
= do_QueryObject(mChannel
);
824 if (newChannel
&& documentChannel
) {
825 // If we've got a DocumentChannel which is marked as loaded using
826 // `mChannelLoaded`, we are currently in the middle of a
827 // `UpgradeLoadToDocument`.
829 // As we don't have the real mime-type from the channel, handle this by
833 MOZ_DIAGNOSTIC_ASSERT(GetTypeOfContent(newMime
) == ObjectType::Document
,
834 "How is text/html not ObjectType::Document?");
835 } else if (newChannel
&& mChannel
) {
836 nsCString channelType
;
837 rv
= mChannel
->GetContentType(channelType
);
839 MOZ_ASSERT_UNREACHABLE("GetContentType failed");
841 channelType
.Truncate();
844 LOG(("OBJLC [%p]: Channel has a content type of %s", this,
847 bool binaryChannelType
= false;
848 if (channelType
.EqualsASCII(APPLICATION_GUESS_FROM_EXT
)) {
849 channelType
= APPLICATION_OCTET_STREAM
;
850 mChannel
->SetContentType(channelType
);
851 binaryChannelType
= true;
852 } else if (channelType
.EqualsASCII(APPLICATION_OCTET_STREAM
) ||
853 channelType
.EqualsASCII(BINARY_OCTET_STREAM
)) {
854 binaryChannelType
= true;
857 // Channel can change our URI through redirection
858 rv
= NS_GetFinalChannelURI(mChannel
, getter_AddRefs(newURI
));
860 MOZ_ASSERT_UNREACHABLE("NS_GetFinalChannelURI failure");
864 ObjectType typeHint
=
865 newMime
.IsEmpty() ? ObjectType::Fallback
: GetTypeOfContent(newMime
);
867 // In order of preference:
869 // 1) Use our type hint if it matches a plugin
870 // 2) If we have eAllowPluginSkipChannel, use the uri file extension if
871 // it matches a plugin
872 // 3) If the channel returns a binary stream type:
873 // 3a) If we have a type non-null non-document type hint, use that
874 // 3b) If the uri file extension matches a plugin type, use that
875 // 4) Use the channel type
877 bool overrideChannelType
= false;
878 if (IsPluginMIME(newMime
)) {
879 LOG(("OBJLC [%p]: Using plugin type hint in favor of any channel type",
881 overrideChannelType
= true;
882 } else if (binaryChannelType
&& typeHint
!= ObjectType::Fallback
) {
883 if (typeHint
== ObjectType::Document
) {
884 if (imgLoader::SupportImageWithMimeType(newMime
)) {
886 ("OBJLC [%p]: Using type hint in favor of binary channel type "
889 overrideChannelType
= true;
893 ("OBJLC [%p]: Using type hint in favor of binary channel type "
894 "(Non-Image Document)",
896 overrideChannelType
= true;
900 if (overrideChannelType
) {
901 // Set the type we'll use for dispatch on the channel. Otherwise we could
902 // end up trying to dispatch to a nsFrameLoader, which will complain that
903 // it couldn't find a way to handle application/octet-stream
904 nsAutoCString parsedMime
, dummy
;
905 NS_ParseResponseContentType(newMime
, parsedMime
, dummy
);
906 if (!parsedMime
.IsEmpty()) {
907 mChannel
->SetContentType(parsedMime
);
910 newMime
= channelType
;
912 } else if (newChannel
) {
913 LOG(("OBJLC [%p]: We failed to open a channel, marking invalid", this));
918 /// Determine final type
920 // In order of preference:
921 // 1) If we have attempted channel load, or set stateInvalid above, the type
922 // is always null (fallback)
923 // 2) If we have a loaded channel, we grabbed its mimeType above, use that
925 // 3) If we have a plugin type and no URI, use that type.
926 // 4) If we have a plugin type and eAllowPluginSkipChannel, use that type.
927 // 5) if we have a URI, set type to loading to indicate we'd need a channel
929 // 6) Otherwise, type null to indicate unloadable content (fallback)
932 ObjectType newMime_Type
= GetTypeOfContent(newMime
);
935 newType
= ObjectType::Fallback
;
936 LOG(("OBJLC [%p]: NewType #0: %s - %u", this, newMime
.get(),
939 } else if (newChannel
) {
940 // If newChannel is set above, we considered it in setting newMime
941 newType
= newMime_Type
;
942 LOG(("OBJLC [%p]: NewType #1: %s - %u", this, newMime
.get(),
944 LOG(("OBJLC [%p]: Using channel type", this));
945 } else if (((caps
& eAllowPluginSkipChannel
) || !newURI
) &&
946 IsPluginMIME(newMime
)) {
947 newType
= newMime_Type
;
948 LOG(("OBJLC [%p]: NewType #2: %s - %u", this, newMime
.get(),
950 LOG(("OBJLC [%p]: Plugin type with no URI, skipping channel load", this));
951 } else if (newURI
&& (mOriginalContentType
.IsEmpty() ||
952 newMime_Type
!= ObjectType::Fallback
)) {
953 // We could potentially load this if we opened a channel on mURI, indicate
954 // this by leaving type as loading.
956 // If a MIME type was requested in the tag, but we have decided to set load
957 // type to null, ignore (otherwise we'll default to document type loading).
958 newType
= ObjectType::Loading
;
959 LOG(("OBJLC [%p]: NewType #3: %u", this, uint32_t(newType
)));
961 // Unloadable - no URI, and no plugin/MIME type. Non-plugin types (images,
962 // documents) always load with a channel.
963 newType
= ObjectType::Fallback
;
964 LOG(("OBJLC [%p]: NewType #4: %u", this, uint32_t(newType
)));
968 /// Handle existing channels
971 if (useChannel
&& newType
== ObjectType::Loading
) {
972 // We decided to use a channel, and also that the previous channel is still
973 // usable, so re-use the existing values.
975 LOG(("OBJLC [%p]: NewType #5: %u", this, uint32_t(newType
)));
976 newMime
= mContentType
;
978 } else if (useChannel
&& !newChannel
) {
979 // We have an existing channel, but did not decide to use one.
980 retval
= (ParameterUpdateFlags
)(retval
| eParamChannelChanged
);
985 /// Update changed values
988 if (newType
!= mType
) {
989 retval
= (ParameterUpdateFlags
)(retval
| eParamStateChanged
);
990 LOG(("OBJLC [%p]: Type changed from %u -> %u", this, uint32_t(mType
),
995 if (!URIEquals(mBaseURI
, newBaseURI
)) {
996 LOG(("OBJLC [%p]: Object effective baseURI changed", this));
997 mBaseURI
= newBaseURI
;
1000 if (!URIEquals(newURI
, mURI
)) {
1001 retval
= (ParameterUpdateFlags
)(retval
| eParamStateChanged
);
1002 LOG(("OBJLC [%p]: Object effective URI changed", this));
1006 // We don't update content type when loading, as the type is not final and we
1007 // don't want to superfluously change between mOriginalContentType ->
1008 // mContentType when doing |obj.data = obj.data| with a channel and differing
1010 if (mType
!= ObjectType::Loading
&& mContentType
!= newMime
) {
1011 retval
= (ParameterUpdateFlags
)(retval
| eParamStateChanged
);
1012 retval
= (ParameterUpdateFlags
)(retval
| eParamContentTypeChanged
);
1013 LOG(("OBJLC [%p]: Object effective mime type changed (%s -> %s)", this,
1014 mContentType
.get(), newMime
.get()));
1015 mContentType
= newMime
;
1018 // If we decided to keep using info from an old channel, but also that state
1019 // changed, we need to invalidate it.
1020 if (useChannel
&& !newChannel
&& (retval
& eParamStateChanged
)) {
1021 mType
= ObjectType::Loading
;
1022 retval
= (ParameterUpdateFlags
)(retval
| eParamChannelChanged
);
1028 // Only OnStartRequest should be passing the channel parameter
1029 nsresult
nsObjectLoadingContent::LoadObject(bool aNotify
, bool aForceLoad
) {
1030 return LoadObject(aNotify
, aForceLoad
, nullptr);
1033 nsresult
nsObjectLoadingContent::LoadObject(bool aNotify
, bool aForceLoad
,
1034 nsIRequest
* aLoadingChannel
) {
1035 Element
* el
= AsElement();
1036 Document
* doc
= el
->OwnerDoc();
1037 nsresult rv
= NS_OK
;
1039 // Per bug 1318303, if the parent document is not active, load the alternative
1041 if (!doc
->IsCurrentActiveDocument()) {
1042 // Since this can be triggered on change of attributes, make sure we've
1043 // unloaded whatever is loaded first.
1045 ObjectType oldType
= mType
;
1046 mType
= ObjectType::Fallback
;
1047 TriggerInnerFallbackLoads();
1048 NotifyStateChanged(oldType
, true);
1052 // XXX(johns): In these cases, we refuse to touch our content and just
1053 // remain unloaded, as per legacy behavior. It would make more sense to
1054 // load fallback content initially and refuse to ever change state again.
1055 if (doc
->IsBeingUsedAsImage()) {
1059 if (doc
->IsLoadedAsData() || doc
->IsStaticDocument()) {
1063 LOG(("OBJLC [%p]: LoadObject called, notify %u, forceload %u, channel %p",
1064 this, aNotify
, aForceLoad
, aLoadingChannel
));
1066 // We can't re-use an already open channel, but aForceLoad may make us try
1067 // to load a plugin without any changes in channel state.
1068 if (aForceLoad
&& mChannelLoaded
) {
1070 mChannelLoaded
= false;
1073 // Save these for NotifyStateChanged();
1074 ObjectType oldType
= mType
;
1076 ParameterUpdateFlags stateChange
= UpdateObjectParameters();
1078 if (!stateChange
&& !aForceLoad
) {
1083 /// State has changed, unload existing content and attempt to load new type
1085 LOG(("OBJLC [%p]: LoadObject - plugin state changed (%u)", this,
1088 // We synchronously start/stop plugin instances below, which may spin the
1089 // event loop. Re-entering into the load is fine, but at that point the
1090 // original load call needs to abort when unwinding
1091 // NOTE this is located *after* the state change check, a subsequent load
1092 // with no subsequently changed state will be a no-op.
1094 LOG(("OBJLC [%p]: Re-entering into LoadObject", this));
1097 AutoSetLoadingToFalse
reentryCheck(this);
1099 // Unload existing content, keeping in mind stopping plugins might spin the
1100 // event loop. Note that we check for still-open channels below
1101 UnloadObject(false); // Don't reset state
1103 // The event loop must've spun and re-entered into LoadObject, which
1104 // finished the load
1105 LOG(("OBJLC [%p]: Re-entered into LoadObject, aborting outer load", this));
1109 // Determine what's going on with our channel.
1110 if (stateChange
& eParamChannelChanged
) {
1111 // If the channel params changed, throw away the channel, but unset
1112 // mChannelLoaded so we'll still try to open a new one for this load if
1115 mChannelLoaded
= false;
1116 } else if (mType
== ObjectType::Fallback
&& mChannel
) {
1117 // If we opened a channel but then failed to find a loadable state, throw it
1118 // away. mChannelLoaded will indicate that we tried to load a channel at one
1119 // point so we wont recurse
1121 } else if (mType
== ObjectType::Loading
&& mChannel
) {
1122 // We're still waiting on a channel load, already opened one, and
1123 // channel parameters didn't change
1125 } else if (mChannelLoaded
&& mChannel
!= aLoadingChannel
) {
1126 // The only time we should have a loaded channel with a changed state is
1127 // when the channel has just opened -- in which case this call should
1128 // have originated from OnStartRequest
1129 MOZ_ASSERT_UNREACHABLE(
1130 "Loading with a channel, but state doesn't make sense");
1138 if (mType
!= ObjectType::Fallback
) {
1139 bool allowLoad
= true;
1140 int16_t contentPolicy
= nsIContentPolicy::ACCEPT
;
1141 // If mChannelLoaded is set we presumably already passed load policy
1142 // If mType == ObjectType::Loading then we call OpenChannel() which
1143 // internally creates a new channel and calls asyncOpen() on that channel
1144 // which then enforces content policy checks.
1145 if (allowLoad
&& mURI
&& !mChannelLoaded
&& mType
!= ObjectType::Loading
) {
1146 allowLoad
= CheckLoadPolicy(&contentPolicy
);
1148 // If we're loading a type now, check ProcessPolicy. Note that we may check
1149 // both now in the case of plugins whose type is determined before opening a
1151 if (allowLoad
&& mType
!= ObjectType::Loading
) {
1152 allowLoad
= CheckProcessPolicy(&contentPolicy
);
1155 // Content policy implementations can mutate the DOM, check for re-entry
1157 LOG(("OBJLC [%p]: We re-entered in content policy, leaving original load",
1162 // Load denied, switch to null
1164 LOG(("OBJLC [%p]: Load denied by policy", this));
1165 mType
= ObjectType::Fallback
;
1169 // Don't allow view-source scheme.
1170 // view-source is the only scheme to which this applies at the moment due to
1171 // potential timing attacks to read data from cross-origin documents. If this
1172 // widens we should add a protocol flag for whether the scheme is only allowed
1173 // in top and use something like nsNetUtil::NS_URIChainHasFlags.
1174 if (mType
!= ObjectType::Fallback
) {
1175 nsCOMPtr
<nsIURI
> tempURI
= mURI
;
1176 nsCOMPtr
<nsINestedURI
> nestedURI
= do_QueryInterface(tempURI
);
1178 // view-source should always be an nsINestedURI, loop and check the
1179 // scheme on this and all inner URIs that are also nested URIs.
1180 if (tempURI
->SchemeIs("view-source")) {
1181 LOG(("OBJLC [%p]: Blocking as effective URI has view-source scheme",
1183 mType
= ObjectType::Fallback
;
1187 nestedURI
->GetInnerURI(getter_AddRefs(tempURI
));
1188 nestedURI
= do_QueryInterface(tempURI
);
1192 // Items resolved as Image/Document are not candidates for content blocking,
1193 // as well as invalid plugins (they will not have the mContentType set).
1194 if (mType
== ObjectType::Fallback
&& ShouldBlockContent()) {
1195 LOG(("OBJLC [%p]: Enable content blocking", this));
1196 mType
= ObjectType::Loading
;
1199 // Sanity check: We shouldn't have any loaded resources, pending events, or
1200 // a final listener at this point
1201 if (mFrameLoader
|| mFinalListener
) {
1202 MOZ_ASSERT_UNREACHABLE("Trying to load new plugin with existing content");
1206 // More sanity-checking:
1207 // If mChannel is set, mChannelLoaded should be set, and vice-versa
1208 if (mType
!= ObjectType::Fallback
&& !!mChannel
!= mChannelLoaded
) {
1209 MOZ_ASSERT_UNREACHABLE("Trying to load with bad channel state");
1214 /// Attempt to load new type
1217 // We don't set mFinalListener until OnStartRequest has been called, to
1218 // prevent re-entry ugliness with CloseChannel()
1219 nsCOMPtr
<nsIStreamListener
> finalListener
;
1221 case ObjectType::Document
: {
1223 // We could mFrameLoader->LoadURI(mURI), but UpdateObjectParameters
1224 // requires documents have a channel, so this is not a valid state.
1225 MOZ_ASSERT_UNREACHABLE(
1226 "Attempting to load a document without a "
1228 rv
= NS_ERROR_FAILURE
;
1232 nsCOMPtr
<nsIDocShell
> docShell
= SetupDocShell(mURI
);
1234 rv
= NS_ERROR_FAILURE
;
1238 // We're loading a document, so we have to set LOAD_DOCUMENT_URI
1239 // (especially important for firing onload)
1240 nsLoadFlags flags
= 0;
1241 mChannel
->GetLoadFlags(&flags
);
1242 flags
|= nsIChannel::LOAD_DOCUMENT_URI
;
1243 mChannel
->SetLoadFlags(flags
);
1245 nsCOMPtr
<nsIInterfaceRequestor
> req(do_QueryInterface(docShell
));
1246 NS_ASSERTION(req
, "Docshell must be an ifreq");
1248 nsCOMPtr
<nsIURILoader
> uriLoader(components::URILoader::Service());
1249 if (NS_WARN_IF(!uriLoader
)) {
1250 MOZ_ASSERT_UNREACHABLE("Failed to get uriLoader service");
1251 mFrameLoader
->Destroy();
1252 mFrameLoader
= nullptr;
1256 uint32_t uriLoaderFlags
= nsDocShell::ComputeURILoaderFlags(
1257 docShell
->GetBrowsingContext(), LOAD_NORMAL
,
1258 /* aIsDocumentLoad */ false);
1260 rv
= uriLoader
->OpenChannel(mChannel
, uriLoaderFlags
, req
,
1261 getter_AddRefs(finalListener
));
1262 // finalListener will receive OnStartRequest either below, or if
1263 // `mChannel` is a `DocumentChannel`, it will be received after
1264 // RedirectToRealChannel.
1266 case ObjectType::Loading
:
1267 // If our type remains Loading, we need a channel to proceed
1269 if (NS_FAILED(rv
)) {
1270 LOG(("OBJLC [%p]: OpenChannel returned failure (%" PRIu32
")", this,
1271 static_cast<uint32_t>(rv
)));
1274 case ObjectType::Fallback
:
1275 // Handled below, silence compiler warnings
1280 // Loaded, handle notifications and fallback
1282 if (NS_FAILED(rv
)) {
1283 // If we failed in the loading hunk above, switch to null (empty) region
1284 LOG(("OBJLC [%p]: Loading failed, switching to fallback", this));
1285 mType
= ObjectType::Fallback
;
1288 if (mType
== ObjectType::Fallback
) {
1289 LOG(("OBJLC [%p]: Switching to fallback state", this));
1290 MOZ_ASSERT(!mFrameLoader
, "switched to fallback but also loaded something");
1292 MaybeFireErrorEvent();
1295 // If we were loading with a channel but then failed over, throw it away
1299 // Don't try to initialize plugins or final listener below
1300 finalListener
= nullptr;
1302 TriggerInnerFallbackLoads();
1305 // Notify of our final state
1306 NotifyStateChanged(oldType
, aNotify
);
1307 NS_ENSURE_TRUE(mIsLoading
, NS_OK
);
1310 // Spawning plugins and dispatching to the final listener may re-enter, so are
1311 // delayed until after we fire a notification, to prevent missing
1312 // notifications or firing them out of order.
1314 // Note that we ensured that we entered into LoadObject() from
1315 // ::OnStartRequest above when loading with a channel.
1319 if (finalListener
) {
1320 NS_ASSERTION(mType
!= ObjectType::Fallback
&& mType
!= ObjectType::Loading
,
1321 "We should not have a final listener with a non-loaded type");
1322 mFinalListener
= finalListener
;
1324 // If we're a DocumentChannel load, hold off on firing the `OnStartRequest`
1325 // callback, as we haven't received it yet from our caller.
1326 RefPtr
<DocumentChannel
> documentChannel
= do_QueryObject(mChannel
);
1327 if (documentChannel
) {
1329 mType
== ObjectType::Document
,
1330 "We have a DocumentChannel here but aren't loading a document?");
1332 rv
= finalListener
->OnStartRequest(mChannel
);
1336 if ((NS_FAILED(rv
) && rv
!= NS_ERROR_PARSED_DATA_CACHED
) && mIsLoading
) {
1337 // Since we've already notified of our transition, we can just Unload and
1338 // call ConfigureFallback (which will notify again)
1340 mType
= ObjectType::Fallback
;
1341 UnloadObject(false);
1342 NS_ENSURE_TRUE(mIsLoading
, NS_OK
);
1344 TriggerInnerFallbackLoads();
1345 NotifyStateChanged(oldType
, true);
1351 // This call can re-enter when dealing with plugin listeners
1352 nsresult
nsObjectLoadingContent::CloseChannel() {
1354 LOG(("OBJLC [%p]: Closing channel\n", this));
1355 // Null the values before potentially-reentering, and ensure they survive
1357 nsCOMPtr
<nsIChannel
> channelGrip(mChannel
);
1358 nsCOMPtr
<nsIStreamListener
> listenerGrip(mFinalListener
);
1360 mFinalListener
= nullptr;
1361 channelGrip
->CancelWithReason(NS_BINDING_ABORTED
,
1362 "nsObjectLoadingContent::CloseChannel"_ns
);
1364 // mFinalListener is only set by LoadObject after OnStartRequest, or
1365 // by OnStartRequest in the case of late-opened plugin streams
1366 listenerGrip
->OnStopRequest(channelGrip
, NS_BINDING_ABORTED
);
1372 bool nsObjectLoadingContent::IsAboutBlankLoadOntoInitialAboutBlank(
1373 nsIURI
* aURI
, bool aInheritPrincipal
, nsIPrincipal
* aPrincipalToInherit
) {
1374 return NS_IsAboutBlank(aURI
) && aInheritPrincipal
&&
1375 (!mFrameLoader
|| !mFrameLoader
->GetExistingDocShell() ||
1376 mFrameLoader
->GetExistingDocShell()
1377 ->IsAboutBlankLoadOntoInitialAboutBlank(aURI
, aInheritPrincipal
,
1378 aPrincipalToInherit
));
1381 nsresult
nsObjectLoadingContent::OpenChannel() {
1382 Element
* el
= AsElement();
1383 Document
* doc
= el
->OwnerDoc();
1384 NS_ASSERTION(doc
, "No owner document?");
1390 if (!mURI
|| !CanHandleURI(mURI
)) {
1391 return NS_ERROR_NOT_AVAILABLE
;
1394 nsCOMPtr
<nsILoadGroup
> group
= doc
->GetDocumentLoadGroup();
1395 nsCOMPtr
<nsIChannel
> chan
;
1396 RefPtr
<ObjectInterfaceRequestorShim
> shim
=
1397 new ObjectInterfaceRequestorShim(this);
1399 bool inheritAttrs
= nsContentUtils::ChannelShouldInheritPrincipal(
1400 el
->NodePrincipal(), // aLoadState->PrincipalToInherit()
1401 mURI
, // aLoadState->URI()
1402 true, // aInheritForAboutBlank
1403 false); // aForceInherit
1405 bool inheritPrincipal
= inheritAttrs
&& !SchemeIsData(mURI
);
1407 nsSecurityFlags securityFlags
=
1408 nsILoadInfo::SEC_ALLOW_CROSS_ORIGIN_SEC_CONTEXT_IS_NULL
;
1409 if (inheritPrincipal
) {
1410 securityFlags
|= nsILoadInfo::SEC_FORCE_INHERIT_PRINCIPAL
;
1413 nsContentPolicyType contentPolicyType
= GetContentPolicyType();
1414 // The setting of LOAD_BYPASS_SERVICE_WORKER here is now an optimization.
1415 // ServiceWorkerInterceptController::ShouldPrepareForIntercept does a more
1416 // expensive check of BrowsingContext ancestors to look for object/embed.
1417 nsLoadFlags loadFlags
= nsIChannel::LOAD_CALL_CONTENT_SNIFFERS
|
1418 nsIChannel::LOAD_BYPASS_SERVICE_WORKER
|
1419 nsIRequest::LOAD_HTML_OBJECT_DATA
;
1420 uint32_t sandboxFlags
= doc
->GetSandboxFlags();
1422 // For object loads we store the CSP that potentially needs to
1423 // be inherited, e.g. in case we are loading an opaque origin
1424 // like a data: URI. The actual inheritance check happens within
1425 // Document::InitCSP(). Please create an actual copy of the CSP
1426 // (do not share the same reference) otherwise a Meta CSP of an
1427 // opaque origin will incorrectly be propagated to the embedding
1429 RefPtr
<nsCSPContext
> cspToInherit
;
1430 if (nsCOMPtr
<nsIContentSecurityPolicy
> csp
= doc
->GetCsp()) {
1431 cspToInherit
= new nsCSPContext();
1432 cspToInherit
->InitFromOther(static_cast<nsCSPContext
*>(csp
.get()));
1435 // --- Create LoadInfo
1436 RefPtr
<LoadInfo
> loadInfo
= new LoadInfo(
1437 /*aLoadingPrincipal = aLoadingContext->NodePrincipal() */ nullptr,
1438 /*aTriggeringPrincipal = aLoadingPrincipal */ nullptr,
1439 /*aLoadingContext = */ el
,
1440 /*aSecurityFlags = */ securityFlags
,
1441 /*aContentPolicyType = */ contentPolicyType
,
1442 /*aLoadingClientInfo = */ Nothing(),
1443 /*aController = */ Nothing(),
1444 /*aSandboxFlags = */ sandboxFlags
);
1447 loadInfo
->SetPrincipalToInherit(el
->NodePrincipal());
1451 loadInfo
->SetCSPToInherit(cspToInherit
);
1454 if (DocumentChannel::CanUseDocumentChannel(mURI
) &&
1455 !IsAboutBlankLoadOntoInitialAboutBlank(mURI
, inheritPrincipal
,
1456 el
->NodePrincipal())) {
1457 // --- Create LoadState
1458 RefPtr
<nsDocShellLoadState
> loadState
= new nsDocShellLoadState(mURI
);
1459 loadState
->SetPrincipalToInherit(el
->NodePrincipal());
1460 loadState
->SetTriggeringPrincipal(loadInfo
->TriggeringPrincipal());
1462 loadState
->SetCsp(cspToInherit
);
1464 loadState
->SetTriggeringSandboxFlags(sandboxFlags
);
1466 // TODO(djg): This was httpChan->SetReferrerInfoWithoutClone(referrerInfo);
1467 // Is the ...WithoutClone(...) important?
1468 auto referrerInfo
= MakeRefPtr
<ReferrerInfo
>(*doc
);
1469 loadState
->SetReferrerInfo(referrerInfo
);
1471 loadState
->SetShouldCheckForRecursion(true);
1473 // When loading using DocumentChannel, ensure that the MIME type hint is
1474 // propagated to DocumentLoadListener. Object elements can override MIME
1475 // handling in some scenarios.
1476 if (!mOriginalContentType
.IsEmpty()) {
1477 nsAutoCString parsedMime
, dummy
;
1478 NS_ParseResponseContentType(mOriginalContentType
, parsedMime
, dummy
);
1479 if (!parsedMime
.IsEmpty()) {
1480 loadState
->SetTypeHint(parsedMime
);
1485 DocumentChannel::CreateForObject(loadState
, loadInfo
, loadFlags
, shim
);
1487 // NS_NewChannel sets the group on the channel. CreateDocumentChannel does
1489 chan
->SetLoadGroup(group
);
1491 rv
= NS_NewChannelInternal(getter_AddRefs(chan
), // outChannel
1493 loadInfo
, // aLoadInfo
1494 nullptr, // aPerformanceStorage
1495 group
, // aLoadGroup
1497 loadFlags
, // aLoadFlags
1498 nullptr); // aIoService
1499 NS_ENSURE_SUCCESS(rv
, rv
);
1502 nsCOMPtr
<nsILoadInfo
> loadinfo
= chan
->LoadInfo();
1503 loadinfo
->SetPrincipalToInherit(el
->NodePrincipal());
1506 // For object loads we store the CSP that potentially needs to
1507 // be inherited, e.g. in case we are loading an opaque origin
1508 // like a data: URI. The actual inheritance check happens within
1509 // Document::InitCSP(). Please create an actual copy of the CSP
1510 // (do not share the same reference) otherwise a Meta CSP of an
1511 // opaque origin will incorrectly be propagated to the embedding
1514 nsCOMPtr
<nsILoadInfo
> loadinfo
= chan
->LoadInfo();
1515 static_cast<LoadInfo
*>(loadinfo
.get())->SetCSPToInherit(cspToInherit
);
1520 nsCOMPtr
<nsIHttpChannel
> httpChan(do_QueryInterface(chan
));
1522 auto referrerInfo
= MakeRefPtr
<ReferrerInfo
>(*doc
);
1524 rv
= httpChan
->SetReferrerInfoWithoutClone(referrerInfo
);
1525 MOZ_ASSERT(NS_SUCCEEDED(rv
));
1527 // Set the initiator type
1528 nsCOMPtr
<nsITimedChannel
> timedChannel(do_QueryInterface(httpChan
));
1530 timedChannel
->SetInitiatorType(el
->LocalName());
1533 nsCOMPtr
<nsIClassOfService
> cos(do_QueryInterface(httpChan
));
1534 if (cos
&& UserActivation::IsHandlingUserInput()) {
1535 cos
->AddClassFlags(nsIClassOfService::UrgentStart
);
1539 nsCOMPtr
<nsIScriptChannel
> scriptChannel
= do_QueryInterface(chan
);
1540 if (scriptChannel
) {
1541 // Allow execution against our context if the principals match
1542 scriptChannel
->SetExecutionPolicy(nsIScriptChannel::EXECUTE_NORMAL
);
1545 // AsyncOpen can fail if a file does not exist.
1546 rv
= chan
->AsyncOpen(shim
);
1547 NS_ENSURE_SUCCESS(rv
, rv
);
1548 LOG(("OBJLC [%p]: Channel opened", this));
1553 uint32_t nsObjectLoadingContent::GetCapabilities() const {
1554 return eSupportImages
| eSupportDocuments
;
1557 void nsObjectLoadingContent::Destroy() {
1559 mFrameLoader
->Destroy();
1560 mFrameLoader
= nullptr;
1563 // Reset state so that if the element is re-appended to tree again (e.g.
1564 // adopting to another document), it will reload resource again.
1569 void nsObjectLoadingContent::Traverse(nsObjectLoadingContent
* tmp
,
1570 nsCycleCollectionTraversalCallback
& cb
) {
1571 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mFrameLoader
);
1572 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mFeaturePolicy
);
1576 void nsObjectLoadingContent::Unlink(nsObjectLoadingContent
* tmp
) {
1577 if (tmp
->mFrameLoader
) {
1578 tmp
->mFrameLoader
->Destroy();
1580 NS_IMPL_CYCLE_COLLECTION_UNLINK(mFrameLoader
);
1581 NS_IMPL_CYCLE_COLLECTION_UNLINK(mFeaturePolicy
);
1584 void nsObjectLoadingContent::UnloadObject(bool aResetState
) {
1586 mFrameLoader
->Destroy();
1587 mFrameLoader
= nullptr;
1592 mChannelLoaded
= false;
1593 mType
= ObjectType::Loading
;
1594 mURI
= mOriginalURI
= mBaseURI
= nullptr;
1595 mContentType
.Truncate();
1596 mOriginalContentType
.Truncate();
1599 mScriptRequested
= false;
1601 mIsStopping
= false;
1603 mSubdocumentIntrinsicSize
.reset();
1604 mSubdocumentIntrinsicRatio
.reset();
1607 void nsObjectLoadingContent::NotifyStateChanged(ObjectType aOldType
,
1609 LOG(("OBJLC [%p]: NotifyStateChanged: (%u) -> (%u) (notify %i)", this,
1610 uint32_t(aOldType
), uint32_t(mType
), aNotify
));
1612 dom::Element
* thisEl
= AsElement();
1613 // Non-images are always not broken.
1614 // XXX: I assume we could just remove this completely?
1615 thisEl
->RemoveStates(ElementState::BROKEN
, aNotify
);
1617 if (mType
== aOldType
) {
1621 Document
* doc
= thisEl
->GetComposedDoc();
1623 return; // Nothing to do
1626 PresShell
* presShell
= doc
->GetPresShell();
1627 // If there is no PresShell or it hasn't been initialized there isn't much to
1629 if (!presShell
|| !presShell
->DidInitialize()) {
1632 presShell
->PostRecreateFramesFor(thisEl
);
1635 nsObjectLoadingContent::ObjectType
nsObjectLoadingContent::GetTypeOfContent(
1636 const nsCString
& aMIMEType
) {
1637 Element
* el
= AsElement();
1638 NS_ASSERTION(el
, "must be a content");
1640 Document
* doc
= el
->OwnerDoc();
1642 // Images and documents are always supported.
1643 MOZ_ASSERT((GetCapabilities() & (eSupportImages
| eSupportDocuments
)) ==
1644 (eSupportImages
| eSupportDocuments
));
1647 ("OBJLC [%p]: calling HtmlObjectContentTypeForMIMEType: aMIMEType: %s - "
1649 this, aMIMEType
.get(), el
));
1651 static_cast<ObjectType
>(nsContentUtils::HtmlObjectContentTypeForMIMEType(
1652 aMIMEType
, doc
->GetSandboxFlags()));
1653 LOG(("OBJLC [%p]: called HtmlObjectContentTypeForMIMEType\n", this));
1657 void nsObjectLoadingContent::CreateStaticClone(
1658 nsObjectLoadingContent
* aDest
) const {
1659 MOZ_ASSERT(aDest
->AsElement()->OwnerDoc()->IsStaticDocument());
1660 aDest
->mType
= mType
;
1663 aDest
->AsElement()->OwnerDoc()->AddPendingFrameStaticClone(aDest
,
1669 nsObjectLoadingContent::GetSrcURI(nsIURI
** aURI
) {
1670 NS_IF_ADDREF(*aURI
= GetSrcURI());
1674 void nsObjectLoadingContent::TriggerInnerFallbackLoads() {
1675 MOZ_ASSERT(!mFrameLoader
&& !mChannel
,
1676 "ConfigureFallback called with loaded content");
1677 MOZ_ASSERT(mType
== ObjectType::Fallback
);
1679 Element
* el
= AsElement();
1680 if (!el
->IsHTMLElement(nsGkAtoms::object
)) {
1683 // Do a depth-first traverse of node tree with the current element as root,
1684 // looking for non-<param> elements. If we find some then we have an HTML
1685 // fallback for this element.
1686 for (nsIContent
* child
= el
->GetFirstChild(); child
;) {
1687 // <object> and <embed> elements in the fallback need to StartObjectLoad.
1688 // Their children should be ignored since they are part of those element's
1690 if (auto* embed
= HTMLEmbedElement::FromNode(child
)) {
1691 embed
->StartObjectLoad(true, true);
1692 // Skip the children
1693 child
= child
->GetNextNonChildNode(el
);
1694 } else if (auto* object
= HTMLObjectElement::FromNode(child
)) {
1695 object
->StartObjectLoad(true, true);
1696 // Skip the children
1697 child
= child
->GetNextNonChildNode(el
);
1699 child
= child
->GetNextNode(el
);
1705 nsObjectLoadingContent::UpgradeLoadToDocument(
1706 nsIChannel
* aRequest
, BrowsingContext
** aBrowsingContext
) {
1707 AUTO_PROFILER_LABEL("nsObjectLoadingContent::UpgradeLoadToDocument", NETWORK
);
1709 LOG(("OBJLC [%p]: UpgradeLoadToDocument", this));
1711 if (aRequest
!= mChannel
|| !aRequest
) {
1712 // happens when a new load starts before the previous one got here.
1713 return NS_BINDING_ABORTED
;
1716 // We should be state loading.
1717 if (mType
!= ObjectType::Loading
) {
1718 MOZ_ASSERT_UNREACHABLE("Should be type loading at this point");
1719 return NS_BINDING_ABORTED
;
1721 MOZ_ASSERT(!mChannelLoaded
, "mChannelLoaded set already?");
1722 MOZ_ASSERT(!mFinalListener
, "mFinalListener exists already?");
1724 mChannelLoaded
= true;
1726 // We don't need to check for errors here, unlike in `OnStartRequest`, as
1727 // `UpgradeLoadToDocument` is only called when the load is going to become a
1728 // process-switching load. As we never process switch for failed object loads,
1729 // we know our channel status is successful.
1731 // Call `LoadObject` to trigger our nsObjectLoadingContext to switch into the
1732 // specified new state.
1733 nsresult rv
= LoadObject(true, false, aRequest
);
1734 if (NS_WARN_IF(NS_FAILED(rv
))) {
1738 RefPtr
<BrowsingContext
> bc
= GetBrowsingContext();
1740 return NS_ERROR_FAILURE
;
1743 // At this point we know that we have a browsing context, so it's time to make
1744 // sure that that browsing context gets the correct container feature policy.
1745 // This is needed for `DocumentLoadListener::MaybeTriggerProcessSwitch` to be
1746 // able to start loading the document with the correct container feature
1747 // policy in the load info.
1748 RefreshFeaturePolicy();
1750 bc
.forget(aBrowsingContext
);
1754 bool nsObjectLoadingContent::ShouldBlockContent() {
1755 return mContentBlockingEnabled
&& mURI
&& IsFlashMIME(mContentType
) &&
1756 StaticPrefs::browser_safebrowsing_blockedURIs_enabled();
1759 Document
* nsObjectLoadingContent::GetContentDocument(
1760 nsIPrincipal
& aSubjectPrincipal
) {
1761 Element
* el
= AsElement();
1762 if (!el
->IsInComposedDoc()) {
1766 Document
* sub_doc
= el
->OwnerDoc()->GetSubDocumentFor(el
);
1771 // Return null for cross-origin contentDocument.
1772 if (!aSubjectPrincipal
.SubsumesConsideringDomain(sub_doc
->NodePrincipal())) {
1779 void nsObjectLoadingContent::MaybeFireErrorEvent() {
1780 Element
* el
= AsElement();
1781 // Queue a task to fire an error event if we're an <object> element. The
1782 // queueing is important, since then we don't have to worry about reentry.
1783 if (el
->IsHTMLElement(nsGkAtoms::object
)) {
1784 RefPtr
<AsyncEventDispatcher
> loadBlockingAsyncDispatcher
=
1785 new LoadBlockingAsyncEventDispatcher(el
, u
"error"_ns
, CanBubble::eNo
,
1786 ChromeOnlyDispatch::eNo
);
1787 loadBlockingAsyncDispatcher
->PostDOMEvent();
1791 bool nsObjectLoadingContent::BlockEmbedOrObjectContentLoading() {
1792 Element
* el
= AsElement();
1794 // Traverse up the node tree to see if we have any ancestors that may block us
1796 for (nsIContent
* parent
= el
->GetParent(); parent
;
1797 parent
= parent
->GetParent()) {
1798 if (parent
->IsAnyOfHTMLElements(nsGkAtoms::video
, nsGkAtoms::audio
)) {
1801 // If we have an ancestor that is an object with a source, it'll have an
1802 // associated displayed type. If that type is not null, don't load content
1804 if (auto* object
= HTMLObjectElement::FromNode(parent
)) {
1805 if (object
->Type() != ObjectType::Fallback
) {
1813 void nsObjectLoadingContent::SubdocumentIntrinsicSizeOrRatioChanged(
1814 const Maybe
<IntrinsicSize
>& aIntrinsicSize
,
1815 const Maybe
<AspectRatio
>& aIntrinsicRatio
) {
1816 if (aIntrinsicSize
== mSubdocumentIntrinsicSize
&&
1817 aIntrinsicRatio
== mSubdocumentIntrinsicRatio
) {
1821 mSubdocumentIntrinsicSize
= aIntrinsicSize
;
1822 mSubdocumentIntrinsicRatio
= aIntrinsicRatio
;
1824 if (nsSubDocumentFrame
* sdf
= do_QueryFrame(AsElement()->GetPrimaryFrame())) {
1825 sdf
->SubdocumentIntrinsicSizeOrRatioChanged();
1829 void nsObjectLoadingContent::SubdocumentImageLoadComplete(nsresult aResult
) {
1830 ObjectType oldType
= mType
;
1831 if (NS_FAILED(aResult
)) {
1833 mType
= ObjectType::Fallback
;
1834 TriggerInnerFallbackLoads();
1835 NotifyStateChanged(oldType
, true);
1839 // (mChannelLoaded && mChannel) indicates this is a good state, not any sort
1841 MOZ_DIAGNOSTIC_ASSERT_IF(mChannelLoaded
&& mChannel
,
1842 mType
== ObjectType::Document
);
1843 NotifyStateChanged(oldType
, true);
1846 void nsObjectLoadingContent::MaybeStoreCrossOriginFeaturePolicy() {
1847 MOZ_DIAGNOSTIC_ASSERT(mFrameLoader
);
1848 if (!mFrameLoader
) {
1852 // If the browsingContext is not ready (because docshell is dead), don't try
1854 if (!mFrameLoader
->IsRemoteFrame() && !mFrameLoader
->GetExistingDocShell()) {
1858 RefPtr
<BrowsingContext
> browsingContext
= mFrameLoader
->GetBrowsingContext();
1860 if (!browsingContext
|| !browsingContext
->IsContentSubframe()) {
1864 auto* el
= nsGenericHTMLElement::FromNode(AsElement());
1865 if (!el
->IsInComposedDoc()) {
1869 if (ContentChild
* cc
= ContentChild::GetSingleton()) {
1870 Unused
<< cc
->SendSetContainerFeaturePolicy(
1871 browsingContext
, Some(mFeaturePolicy
->ToFeaturePolicyInfo()));
1875 /* static */ already_AddRefed
<nsIPrincipal
>
1876 nsObjectLoadingContent::GetFeaturePolicyDefaultOrigin(nsINode
* aNode
) {
1877 auto* el
= nsGenericHTMLElement::FromNode(aNode
);
1878 nsCOMPtr
<nsIURI
> nodeURI
;
1879 // Different elements keep this in various locations
1880 if (el
->NodeInfo()->Equals(nsGkAtoms::object
)) {
1881 el
->GetURIAttr(nsGkAtoms::data
, nullptr, getter_AddRefs(nodeURI
));
1882 } else if (el
->NodeInfo()->Equals(nsGkAtoms::embed
)) {
1883 el
->GetURIAttr(nsGkAtoms::src
, nullptr, getter_AddRefs(nodeURI
));
1886 nsCOMPtr
<nsIPrincipal
> principal
;
1888 principal
= BasePrincipal::CreateContentPrincipal(
1890 BasePrincipal::Cast(el
->NodePrincipal())->OriginAttributesRef());
1892 principal
= el
->NodePrincipal();
1895 return principal
.forget();
1898 void nsObjectLoadingContent::RefreshFeaturePolicy() {
1899 if (mType
!= ObjectType::Document
) {
1903 if (!mFeaturePolicy
) {
1904 mFeaturePolicy
= MakeAndAddRef
<FeaturePolicy
>(AsElement());
1907 // The origin can change if 'src' or 'data' attributes change.
1908 nsCOMPtr
<nsIPrincipal
> origin
= GetFeaturePolicyDefaultOrigin(AsElement());
1910 mFeaturePolicy
->SetDefaultOrigin(origin
);
1912 mFeaturePolicy
->InheritPolicy(AsElement()->OwnerDoc()->FeaturePolicy());
1913 MaybeStoreCrossOriginFeaturePolicy();