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"
36 #include "mozilla/Logging.h"
38 #include "nsContentPolicyUtils.h"
39 #include "nsContentUtils.h"
40 #include "nsDocShellLoadState.h"
41 #include "nsGkAtoms.h"
42 #include "nsThreadUtils.h"
43 #include "nsNetUtil.h"
44 #include "nsMimeTypes.h"
45 #include "nsStyleUtil.h"
46 #include "mozilla/Preferences.h"
47 #include "nsQueryObject.h"
50 #include "nsFrameLoader.h"
52 #include "nsObjectLoadingContent.h"
53 #include "js/Object.h" // JS::GetClass
55 #include "nsWidgetsCID.h"
56 #include "mozilla/BasicEvents.h"
57 #include "mozilla/Components.h"
58 #include "mozilla/LoadInfo.h"
59 #include "mozilla/dom/BindingUtils.h"
60 #include "mozilla/dom/Element.h"
61 #include "mozilla/dom/Event.h"
62 #include "mozilla/dom/ScriptSettings.h"
63 #include "mozilla/dom/PluginCrashedEvent.h"
64 #include "mozilla/AsyncEventDispatcher.h"
65 #include "mozilla/EventDispatcher.h"
66 #include "mozilla/IMEStateManager.h"
67 #include "mozilla/widget/IMEData.h"
68 #include "mozilla/dom/ContentChild.h"
69 #include "mozilla/dom/HTMLObjectElementBinding.h"
70 #include "mozilla/dom/HTMLEmbedElement.h"
71 #include "mozilla/dom/HTMLObjectElement.h"
72 #include "mozilla/dom/UserActivation.h"
73 #include "mozilla/dom/nsCSPContext.h"
74 #include "mozilla/net/DocumentChannel.h"
75 #include "mozilla/net/UrlClassifierFeatureFactory.h"
76 #include "mozilla/PresShell.h"
77 #include "mozilla/ProfilerLabels.h"
78 #include "mozilla/StaticPrefs_browser.h"
79 #include "nsChannelClassifier.h"
80 #include "nsFocusManager.h"
81 #include "ReferrerInfo.h"
82 #include "nsIEffectiveTLDService.h"
85 // Thanks so much, Microsoft! :(
91 static const char kPrefYoutubeRewrite
[] = "plugins.rewrite_youtube_embeds";
93 using namespace mozilla
;
94 using namespace mozilla::dom
;
95 using namespace mozilla::net
;
97 static LogModule
* GetObjectLog() {
98 static LazyLogModule
sLog("objlc");
102 #define LOG(args) MOZ_LOG(GetObjectLog(), mozilla::LogLevel::Debug, args)
103 #define LOG_ENABLED() MOZ_LOG_TEST(GetObjectLog(), mozilla::LogLevel::Debug)
105 static bool IsFlashMIME(const nsACString
& aMIMEType
) {
106 return aMIMEType
.LowerCaseEqualsASCII("application/x-shockwave-flash") ||
107 aMIMEType
.LowerCaseEqualsASCII("application/futuresplash") ||
108 aMIMEType
.LowerCaseEqualsASCII("application/x-shockwave-flash-test");
111 static bool IsPluginType(nsObjectLoadingContent::ObjectType type
) {
112 return type
== nsObjectLoadingContent::eType_Fallback
;
115 bool nsObjectLoadingContent::IsFallbackMimeType(const nsACString
& aMIMEType
) {
116 return IsFlashMIME(aMIMEType
) ||
117 aMIMEType
.LowerCaseEqualsASCII("application/x-test");
121 /// Runnables and helper classes
124 // Sets a object's mIsLoading bit to false when destroyed
125 class AutoSetLoadingToFalse
{
127 explicit AutoSetLoadingToFalse(nsObjectLoadingContent
* aContent
)
128 : mContent(aContent
) {}
129 ~AutoSetLoadingToFalse() { mContent
->mIsLoading
= false; }
132 nsObjectLoadingContent
* mContent
;
139 bool nsObjectLoadingContent::IsSuccessfulRequest(nsIRequest
* aRequest
,
141 nsresult rv
= aRequest
->GetStatus(aStatus
);
142 if (NS_FAILED(rv
) || NS_FAILED(*aStatus
)) {
146 // This may still be an error page or somesuch
147 nsCOMPtr
<nsIHttpChannel
> httpChan(do_QueryInterface(aRequest
));
150 rv
= httpChan
->GetRequestSucceeded(&success
);
151 if (NS_FAILED(rv
) || !success
) {
156 // Otherwise, the request is successful
160 static bool CanHandleURI(nsIURI
* aURI
) {
161 nsAutoCString scheme
;
162 if (NS_FAILED(aURI
->GetScheme(scheme
))) {
166 nsCOMPtr
<nsIIOService
> ios
= mozilla::components::IO::Service();
171 nsCOMPtr
<nsIProtocolHandler
> handler
;
172 ios
->GetProtocolHandler(scheme
.get(), getter_AddRefs(handler
));
177 nsCOMPtr
<nsIExternalProtocolHandler
> extHandler
= do_QueryInterface(handler
);
178 // We can handle this URI if its protocol handler is not the external one
179 return extHandler
== nullptr;
182 // Helper for tedious URI equality syntax when one or both arguments may be
183 // null and URIEquals(null, null) should be true
184 static bool inline URIEquals(nsIURI
* a
, nsIURI
* b
) {
186 return (!a
&& !b
) || (a
&& b
&& NS_SUCCEEDED(a
->Equals(b
, &equal
)) && equal
);
193 // Helper to spawn the frameloader.
194 void nsObjectLoadingContent::SetupFrameLoader() {
195 mFrameLoader
= nsFrameLoader::Create(AsElement(), mNetworkCreated
);
196 MOZ_ASSERT(mFrameLoader
, "nsFrameLoader::Create failed");
199 // Helper to spawn the frameloader and return a pointer to its docshell.
200 already_AddRefed
<nsIDocShell
> nsObjectLoadingContent::SetupDocShell(
201 nsIURI
* aRecursionCheckURI
) {
207 nsCOMPtr
<nsIDocShell
> docShell
;
209 if (aRecursionCheckURI
) {
210 nsresult rv
= mFrameLoader
->CheckForRecursiveLoad(aRecursionCheckURI
);
211 if (NS_SUCCEEDED(rv
)) {
212 IgnoredErrorResult result
;
213 docShell
= mFrameLoader
->GetDocShell(result
);
214 if (result
.Failed()) {
215 MOZ_ASSERT_UNREACHABLE("Could not get DocShell from mFrameLoader?");
218 LOG(("OBJLC [%p]: Aborting recursive load", this));
223 mFrameLoader
->Destroy();
224 mFrameLoader
= nullptr;
228 MaybeStoreCrossOriginFeaturePolicy();
230 return docShell
.forget();
233 void nsObjectLoadingContent::UnbindFromTree(bool aNullParent
) {
234 // Reset state and clear pending events
235 /// XXX(johns): The implementation for GenericFrame notes that ideally we
236 /// would keep the docshell around, but trash the frameloader
240 nsObjectLoadingContent::nsObjectLoadingContent()
241 : mType(eType_Loading
),
242 mChannelLoaded(false),
243 mNetworkCreated(true),
244 mContentBlockingEnabled(false),
247 mScriptRequested(false),
248 mRewrittenYoutubeEmbed(false),
249 mLoadingSyntheticDocument(false) {}
251 nsObjectLoadingContent::~nsObjectLoadingContent() {
252 // Should have been unbound from the tree at this point, and
253 // CheckPluginStopEvent keeps us alive
255 MOZ_ASSERT_UNREACHABLE(
256 "Should not be tearing down frame loaders at this point");
257 mFrameLoader
->Destroy();
261 // nsIRequestObserver
263 nsObjectLoadingContent::OnStartRequest(nsIRequest
* aRequest
) {
264 AUTO_PROFILER_LABEL("nsObjectLoadingContent::OnStartRequest", NETWORK
);
266 LOG(("OBJLC [%p]: Channel OnStartRequest", this));
268 if (aRequest
!= mChannel
|| !aRequest
) {
269 // happens when a new load starts before the previous one got here
270 return NS_BINDING_ABORTED
;
273 nsCOMPtr
<nsIChannel
> chan(do_QueryInterface(aRequest
));
274 NS_ASSERTION(chan
, "Why is our request not a channel?");
276 nsresult status
= NS_OK
;
277 bool success
= IsSuccessfulRequest(aRequest
, &status
);
279 // If we have already switched to type document, we're doing a
280 // process-switching DocumentChannel load. We should be able to pass down the
281 // load to our inner listener, but should also make sure to update our local
283 if (mType
== eType_Document
) {
284 if (!mFinalListener
) {
285 MOZ_ASSERT_UNREACHABLE(
286 "Already are eType_Document, but don't have final listener yet?");
287 return NS_BINDING_ABORTED
;
290 // If the load looks successful, fix up some of our local state before
291 // forwarding the request to the final URI loader.
293 // Forward load errors down to the document loader, so we don't tear down
294 // the nsDocShell ourselves.
296 LOG(("OBJLC [%p]: OnStartRequest: DocumentChannel request succeeded\n",
298 nsCString channelType
;
299 MOZ_ALWAYS_SUCCEEDS(mChannel
->GetContentType(channelType
));
301 if (GetTypeOfContent(channelType
) != eType_Document
) {
302 MOZ_CRASH("DocumentChannel request with non-document MIME");
304 mContentType
= channelType
;
307 NS_GetFinalChannelURI(mChannel
, getter_AddRefs(mURI
)));
310 return mFinalListener
->OnStartRequest(aRequest
);
313 // Otherwise we should be state loading, and call LoadObject with the channel
314 if (mType
!= eType_Loading
) {
315 MOZ_ASSERT_UNREACHABLE("Should be type loading at this point");
316 return NS_BINDING_ABORTED
;
318 NS_ASSERTION(!mChannelLoaded
, "mChannelLoaded set already?");
319 NS_ASSERTION(!mFinalListener
, "mFinalListener exists already?");
321 mChannelLoaded
= true;
323 if (status
== NS_ERROR_BLOCKED_URI
) {
324 nsCOMPtr
<nsIConsoleService
> console(
325 do_GetService("@mozilla.org/consoleservice;1"));
327 nsCOMPtr
<nsIURI
> uri
;
328 chan
->GetURI(getter_AddRefs(uri
));
331 NS_ConvertASCIItoUTF16(uri
->GetSpecOrDefault().get()) +
333 u
" since it was found on an internal Firefox blocklist.");
334 console
->LogStringMessage(message
.get());
336 mContentBlockingEnabled
= true;
337 return NS_ERROR_FAILURE
;
340 if (UrlClassifierFeatureFactory::IsClassifierBlockingErrorCode(status
)) {
341 mContentBlockingEnabled
= true;
342 return NS_ERROR_FAILURE
;
346 LOG(("OBJLC [%p]: OnStartRequest: Request failed\n", this));
347 // If the request fails, we still call LoadObject() to handle fallback
348 // content and notifying of failure. (mChannelLoaded && !mChannel) indicates
351 LoadObject(true, false);
352 return NS_ERROR_FAILURE
;
355 return LoadObject(true, false, aRequest
);
359 nsObjectLoadingContent::OnStopRequest(nsIRequest
* aRequest
,
360 nsresult aStatusCode
) {
361 AUTO_PROFILER_LABEL("nsObjectLoadingContent::OnStopRequest", NETWORK
);
363 // Handle object not loading error because source was a tracking URL (or
364 // fingerprinting, cryptomining, etc.).
365 // We make a note of this object node by including it in a dedicated
366 // array of blocked tracking nodes under its parent document.
367 if (UrlClassifierFeatureFactory::IsClassifierBlockingErrorCode(aStatusCode
)) {
368 nsCOMPtr
<nsIContent
> thisNode
=
369 do_QueryInterface(static_cast<nsIObjectLoadingContent
*>(this));
370 if (thisNode
&& thisNode
->IsInComposedDoc()) {
371 thisNode
->GetComposedDoc()->AddBlockedNodeByClassifier(thisNode
);
375 if (aRequest
!= mChannel
) {
376 return NS_BINDING_ABORTED
;
381 if (mFinalListener
) {
382 // This may re-enter in the case of plugin listeners
383 nsCOMPtr
<nsIStreamListener
> listenerGrip(mFinalListener
);
384 mFinalListener
= nullptr;
385 listenerGrip
->OnStopRequest(aRequest
, aStatusCode
);
388 // Return value doesn't matter
394 nsObjectLoadingContent::OnDataAvailable(nsIRequest
* aRequest
,
395 nsIInputStream
* aInputStream
,
396 uint64_t aOffset
, uint32_t aCount
) {
397 if (aRequest
!= mChannel
) {
398 return NS_BINDING_ABORTED
;
401 if (mFinalListener
) {
402 // This may re-enter in the case of plugin listeners
403 nsCOMPtr
<nsIStreamListener
> listenerGrip(mFinalListener
);
404 return listenerGrip
->OnDataAvailable(aRequest
, aInputStream
, aOffset
,
408 // We shouldn't have a connected channel with no final listener
409 MOZ_ASSERT_UNREACHABLE(
410 "Got data for channel with no connected final "
414 return NS_ERROR_UNEXPECTED
;
418 nsObjectLoadingContent::GetActualType(nsACString
& aType
) {
419 aType
= mContentType
;
424 nsObjectLoadingContent::GetDisplayedType(uint32_t* aType
) {
425 *aType
= DisplayedType();
429 // nsIInterfaceRequestor
430 // We use a shim class to implement this so that JS consumers still
431 // see an interface requestor even though WebIDL bindings don't expose
433 class ObjectInterfaceRequestorShim final
: public nsIInterfaceRequestor
,
434 public nsIChannelEventSink
,
435 public nsIStreamListener
{
437 NS_DECL_CYCLE_COLLECTING_ISUPPORTS
438 NS_DECL_CYCLE_COLLECTION_CLASS_AMBIGUOUS(ObjectInterfaceRequestorShim
,
439 nsIInterfaceRequestor
)
440 NS_DECL_NSIINTERFACEREQUESTOR
441 // RefPtr<nsObjectLoadingContent> fails due to ambiguous AddRef/Release,
442 // hence the ugly static cast :(
443 NS_FORWARD_NSICHANNELEVENTSINK(
444 static_cast<nsObjectLoadingContent
*>(mContent
.get())->)
445 NS_FORWARD_NSISTREAMLISTENER(
446 static_cast<nsObjectLoadingContent
*>(mContent
.get())->)
447 NS_FORWARD_NSIREQUESTOBSERVER(
448 static_cast<nsObjectLoadingContent
*>(mContent
.get())->)
450 explicit ObjectInterfaceRequestorShim(nsIObjectLoadingContent
* aContent
)
451 : mContent(aContent
) {}
454 ~ObjectInterfaceRequestorShim() = default;
455 nsCOMPtr
<nsIObjectLoadingContent
> mContent
;
458 NS_IMPL_CYCLE_COLLECTION(ObjectInterfaceRequestorShim
, mContent
)
460 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(ObjectInterfaceRequestorShim
)
461 NS_INTERFACE_MAP_ENTRY(nsIInterfaceRequestor
)
462 NS_INTERFACE_MAP_ENTRY(nsIChannelEventSink
)
463 NS_INTERFACE_MAP_ENTRY(nsIStreamListener
)
464 NS_INTERFACE_MAP_ENTRY(nsIRequestObserver
)
465 NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports
, nsIInterfaceRequestor
)
468 NS_IMPL_CYCLE_COLLECTING_ADDREF(ObjectInterfaceRequestorShim
)
469 NS_IMPL_CYCLE_COLLECTING_RELEASE(ObjectInterfaceRequestorShim
)
472 ObjectInterfaceRequestorShim::GetInterface(const nsIID
& aIID
, void** aResult
) {
473 if (aIID
.Equals(NS_GET_IID(nsIChannelEventSink
))) {
474 nsIChannelEventSink
* sink
= this;
479 if (aIID
.Equals(NS_GET_IID(nsIObjectLoadingContent
))) {
480 nsIObjectLoadingContent
* olc
= mContent
;
485 return NS_NOINTERFACE
;
488 // nsIChannelEventSink
490 nsObjectLoadingContent::AsyncOnChannelRedirect(
491 nsIChannel
* aOldChannel
, nsIChannel
* aNewChannel
, uint32_t aFlags
,
492 nsIAsyncVerifyRedirectCallback
* cb
) {
493 // If we're already busy with a new load, or have no load at all,
494 // cancel the redirect.
495 if (!mChannel
|| aOldChannel
!= mChannel
) {
496 return NS_BINDING_ABORTED
;
499 mChannel
= aNewChannel
;
501 if (mFinalListener
) {
502 nsCOMPtr
<nsIChannelEventSink
> sink(do_QueryInterface(mFinalListener
));
503 MOZ_RELEASE_ASSERT(sink
, "mFinalListener isn't nsIChannelEventSink?");
504 if (mType
!= eType_Document
) {
505 MOZ_ASSERT_UNREACHABLE(
506 "Not a DocumentChannel load, but we're getting a "
507 "AsyncOnChannelRedirect with a mFinalListener?");
508 return NS_BINDING_ABORTED
;
511 return sink
->AsyncOnChannelRedirect(aOldChannel
, aNewChannel
, aFlags
, cb
);
514 cb
->OnRedirectVerifyCallback(NS_OK
);
518 void nsObjectLoadingContent::MaybeRewriteYoutubeEmbed(nsIURI
* aURI
,
520 nsIURI
** aRewrittenURI
) {
521 nsCOMPtr
<nsIEffectiveTLDService
> tldService
=
522 do_GetService(NS_EFFECTIVETLDSERVICE_CONTRACTID
);
523 // If we can't analyze the URL, just pass on through.
525 NS_WARNING("Could not get TLD service!");
529 nsAutoCString currentBaseDomain
;
530 bool ok
= NS_SUCCEEDED(tldService
->GetBaseDomain(aURI
, 0, currentBaseDomain
));
532 // Data URIs (commonly used for things like svg embeds) won't parse
533 // correctly, so just fail silently here.
537 // See if URL is referencing youtube
538 if (!currentBaseDomain
.EqualsLiteral("youtube.com") &&
539 !currentBaseDomain
.EqualsLiteral("youtube-nocookie.com")) {
543 // We should only rewrite URLs with paths starting with "/v/", as we shouldn't
544 // touch object nodes with "/embed/" urls that already do that right thing.
546 aURI
->GetPathQueryRef(path
);
547 if (!StringBeginsWith(path
, "/v/"_ns
)) {
551 // See if requester is planning on using the JS API.
553 nsresult rv
= aURI
->GetSpec(uri
);
558 // Some YouTube urls have parameters in path components, e.g.
559 // http://youtube.com/embed/7LcUOEP7Brc&start=35. These URLs work with flash,
560 // but break iframe/object embedding. If this situation occurs with rewritten
561 // URLs, convert the parameters to query in order to make the video load
562 // correctly as an iframe. In either case, warn about it in the
563 // developer console.
564 int32_t ampIndex
= uri
.FindChar('&', 0);
565 bool replaceQuery
= false;
566 if (ampIndex
!= -1) {
567 int32_t qmIndex
= uri
.FindChar('?', 0);
568 if (qmIndex
== -1 || qmIndex
> ampIndex
) {
573 Document
* doc
= AsElement()->OwnerDoc();
574 // If we've made it this far, we've got a rewritable embed. Log it in
576 doc
->SetUseCounter(eUseCounter_custom_YouTubeFlashEmbed
);
578 // If we're pref'd off, return after telemetry has been logged.
579 if (!Preferences::GetBool(kPrefYoutubeRewrite
)) {
583 nsAutoString utf16OldURI
= NS_ConvertUTF8toUTF16(uri
);
584 // If we need to convert the URL, it means an ampersand comes first.
585 // Use the index we found earlier.
587 // Replace question marks with ampersands.
588 uri
.ReplaceChar('?', '&');
589 // Replace the first ampersand with a question mark.
590 uri
.SetCharAt('?', ampIndex
);
592 // Switch out video access url formats, which should possibly allow HTML5
594 uri
.ReplaceSubstring("/v/"_ns
, "/embed/"_ns
);
595 nsAutoString utf16URI
= NS_ConvertUTF8toUTF16(uri
);
596 rv
= nsContentUtils::NewURIWithDocumentCharset(aRewrittenURI
, utf16URI
, doc
,
601 AutoTArray
<nsString
, 2> params
= {utf16OldURI
, utf16URI
};
603 // If there's no query to rewrite, just notify in the developer console
604 // that we're changing the embed.
606 msgName
= "RewriteYouTubeEmbed";
608 msgName
= "RewriteYouTubeEmbedPathParams";
610 nsContentUtils::ReportToConsole(nsIScriptError::warningFlag
, "Plugins"_ns
,
611 doc
, nsContentUtils::eDOM_PROPERTIES
, msgName
,
615 bool nsObjectLoadingContent::CheckLoadPolicy(int16_t* aContentPolicy
) {
616 if (!aContentPolicy
|| !mURI
) {
617 MOZ_ASSERT_UNREACHABLE("Doing it wrong");
621 Element
* el
= AsElement();
622 Document
* doc
= el
->OwnerDoc();
624 nsContentPolicyType contentPolicyType
= GetContentPolicyType();
626 nsCOMPtr
<nsILoadInfo
> secCheckLoadInfo
=
627 new LoadInfo(doc
->NodePrincipal(), // loading principal
628 doc
->NodePrincipal(), // triggering principal
629 el
, nsILoadInfo::SEC_ONLY_FOR_EXPLICIT_CONTENTSEC_CHECK
,
632 *aContentPolicy
= nsIContentPolicy::ACCEPT
;
634 NS_CheckContentLoadPolicy(mURI
, secCheckLoadInfo
, aContentPolicy
,
635 nsContentUtils::GetContentPolicy());
636 NS_ENSURE_SUCCESS(rv
, false);
637 if (NS_CP_REJECTED(*aContentPolicy
)) {
638 LOG(("OBJLC [%p]: Content policy denied load of %s", this,
639 mURI
->GetSpecOrDefault().get()));
646 bool nsObjectLoadingContent::CheckProcessPolicy(int16_t* aContentPolicy
) {
647 if (!aContentPolicy
) {
648 MOZ_ASSERT_UNREACHABLE("Null out variable");
652 Element
* el
= AsElement();
653 Document
* doc
= el
->OwnerDoc();
655 nsContentPolicyType objectType
;
658 objectType
= nsIContentPolicy::TYPE_DOCUMENT
;
661 objectType
= GetContentPolicyType();
664 MOZ_ASSERT_UNREACHABLE(
665 "Calling checkProcessPolicy with an unloadable "
670 nsCOMPtr
<nsILoadInfo
> secCheckLoadInfo
= new LoadInfo(
671 doc
->NodePrincipal(), // loading principal
672 doc
->NodePrincipal(), // triggering principal
673 el
, nsILoadInfo::SEC_ONLY_FOR_EXPLICIT_CONTENTSEC_CHECK
, objectType
);
675 *aContentPolicy
= nsIContentPolicy::ACCEPT
;
676 nsresult rv
= NS_CheckContentProcessPolicy(
677 mURI
? mURI
: mBaseURI
, secCheckLoadInfo
, aContentPolicy
,
678 nsContentUtils::GetContentPolicy());
679 NS_ENSURE_SUCCESS(rv
, false);
681 if (NS_CP_REJECTED(*aContentPolicy
)) {
682 LOG(("OBJLC [%p]: CheckContentProcessPolicy rejected load", this));
689 nsObjectLoadingContent::ParameterUpdateFlags
690 nsObjectLoadingContent::UpdateObjectParameters() {
691 Element
* el
= AsElement();
693 uint32_t caps
= GetCapabilities();
694 LOG(("OBJLC [%p]: Updating object parameters", this));
697 nsAutoCString newMime
;
698 nsAutoString typeAttr
;
699 nsCOMPtr
<nsIURI
> newURI
;
700 nsCOMPtr
<nsIURI
> newBaseURI
;
702 // Set if this state can't be used to load anything, forces eType_Null
703 bool stateInvalid
= false;
704 // Indicates what parameters changed.
705 // eParamChannelChanged - means parameters that affect channel opening
707 // eParamStateChanged - means anything that affects what content we load
708 // changed, even if the channel we'd open remains the
711 // State changes outside of the channel parameters only matter if we've
712 // already opened a channel or tried to instantiate content, whereas channel
713 // parameter changes require re-opening the channel even if we haven't gotten
715 nsObjectLoadingContent::ParameterUpdateFlags retval
= eParamNoChange
;
718 /// Initial MIME Type
721 if (caps
& eFallbackIfClassIDPresent
&&
722 el
->HasNonEmptyAttr(nsGkAtoms::classid
)) {
723 // We don't support class ID plugin references, so we should always treat
724 // having class Ids as attributes as invalid, and fallback accordingly.
733 nsAutoString codebaseStr
;
734 nsIURI
* docBaseURI
= el
->GetBaseURI();
735 el
->GetAttr(nsGkAtoms::codebase
, codebaseStr
);
737 if (!codebaseStr
.IsEmpty()) {
738 rv
= nsContentUtils::NewURIWithDocumentCharset(
739 getter_AddRefs(newBaseURI
), codebaseStr
, el
->OwnerDoc(), docBaseURI
);
743 ("OBJLC [%p]: Could not parse plugin's codebase as a URI, "
744 "will use document baseURI instead",
749 // If we failed to build a valid URI, use the document's base URI
751 newBaseURI
= docBaseURI
;
754 nsAutoString rawTypeAttr
;
755 el
->GetAttr(nsGkAtoms::type
, rawTypeAttr
);
756 if (!rawTypeAttr
.IsEmpty()) {
757 typeAttr
= rawTypeAttr
;
760 nsContentUtils::SplitMimeType(rawTypeAttr
, mime
, params
);
761 CopyUTF16toUTF8(mime
, newMime
);
769 // Different elements keep this in various locations
770 if (el
->NodeInfo()->Equals(nsGkAtoms::object
)) {
771 el
->GetAttr(nsGkAtoms::data
, uriStr
);
772 } else if (el
->NodeInfo()->Equals(nsGkAtoms::embed
)) {
773 el
->GetAttr(nsGkAtoms::src
, uriStr
);
775 MOZ_ASSERT_UNREACHABLE("Unrecognized plugin-loading tag");
778 mRewrittenYoutubeEmbed
= false;
779 // Note that the baseURI changing could affect the newURI, even if uriStr did
781 if (!uriStr
.IsEmpty()) {
782 rv
= nsContentUtils::NewURIWithDocumentCharset(
783 getter_AddRefs(newURI
), uriStr
, el
->OwnerDoc(), newBaseURI
);
784 nsCOMPtr
<nsIURI
> rewrittenURI
;
785 MaybeRewriteYoutubeEmbed(newURI
, newBaseURI
, getter_AddRefs(rewrittenURI
));
787 newURI
= rewrittenURI
;
788 mRewrittenYoutubeEmbed
= true;
789 newMime
= "text/html"_ns
;
798 /// Check if the original (pre-channel) content-type or URI changed, and
799 /// record mOriginal{ContentType,URI}
802 if ((mOriginalContentType
!= newMime
) || !URIEquals(mOriginalURI
, newURI
)) {
803 // These parameters changing requires re-opening the channel, so don't
804 // consider the currently-open channel below
805 // XXX(johns): Changing the mime type might change our decision on whether
806 // or not we load a channel, so we count changes to it as a
807 // channel parameter change for the sake of simplicity.
808 retval
= (ParameterUpdateFlags
)(retval
| eParamChannelChanged
);
809 LOG(("OBJLC [%p]: Channel parameters changed", this));
811 mOriginalContentType
= newMime
;
812 mOriginalURI
= newURI
;
815 /// If we have a channel, see if its MIME type should take precendence and
816 /// check the final (redirected) URL
819 // If we have a loaded channel and channel parameters did not change, use it
820 // to determine what we would load.
821 bool useChannel
= mChannelLoaded
&& !(retval
& eParamChannelChanged
);
822 // If we have a channel and are type loading, as opposed to having an existing
823 // channel for a previous load.
824 bool newChannel
= useChannel
&& mType
== eType_Loading
;
826 RefPtr
<DocumentChannel
> documentChannel
= do_QueryObject(mChannel
);
827 if (newChannel
&& documentChannel
) {
828 // If we've got a DocumentChannel which is marked as loaded using
829 // `mChannelLoaded`, we are currently in the middle of a
830 // `UpgradeLoadToDocument`.
832 // As we don't have the real mime-type from the channel, handle this by
836 MOZ_DIAGNOSTIC_ASSERT(GetTypeOfContent(newMime
) == eType_Document
,
837 "How is text/html not eType_Document?");
838 } else if (newChannel
&& mChannel
) {
839 nsCString channelType
;
840 rv
= mChannel
->GetContentType(channelType
);
842 MOZ_ASSERT_UNREACHABLE("GetContentType failed");
844 channelType
.Truncate();
847 LOG(("OBJLC [%p]: Channel has a content type of %s", this,
850 bool binaryChannelType
= false;
851 if (channelType
.EqualsASCII(APPLICATION_GUESS_FROM_EXT
)) {
852 channelType
= APPLICATION_OCTET_STREAM
;
853 mChannel
->SetContentType(channelType
);
854 binaryChannelType
= true;
855 } else if (channelType
.EqualsASCII(APPLICATION_OCTET_STREAM
) ||
856 channelType
.EqualsASCII(BINARY_OCTET_STREAM
)) {
857 binaryChannelType
= true;
860 // Channel can change our URI through redirection
861 rv
= NS_GetFinalChannelURI(mChannel
, getter_AddRefs(newURI
));
863 MOZ_ASSERT_UNREACHABLE("NS_GetFinalChannelURI failure");
867 ObjectType typeHint
=
868 newMime
.IsEmpty() ? eType_Null
: GetTypeOfContent(newMime
);
871 // In order of preference:
873 // 1) Use our type hint if it matches a plugin
874 // 2) If we have eAllowPluginSkipChannel, use the uri file extension if
875 // it matches a plugin
876 // 3) If the channel returns a binary stream type:
877 // 3a) If we have a type non-null non-document type hint, use that
878 // 3b) If the uri file extension matches a plugin type, use that
879 // 4) Use the channel type
881 bool overrideChannelType
= false;
882 if (IsPluginType(typeHint
)) {
883 LOG(("OBJLC [%p]: Using plugin type hint in favor of any channel type",
885 overrideChannelType
= true;
886 } else if (binaryChannelType
&& typeHint
!= eType_Null
) {
887 if (typeHint
== eType_Document
) {
888 if (imgLoader::SupportImageWithMimeType(newMime
)) {
890 ("OBJLC [%p]: Using type hint in favor of binary channel type "
893 overrideChannelType
= true;
897 ("OBJLC [%p]: Using type hint in favor of binary channel type "
898 "(Non-Image Document)",
900 overrideChannelType
= true;
904 if (overrideChannelType
) {
905 // Set the type we'll use for dispatch on the channel. Otherwise we could
906 // end up trying to dispatch to a nsFrameLoader, which will complain that
907 // it couldn't find a way to handle application/octet-stream
908 nsAutoCString parsedMime
, dummy
;
909 NS_ParseResponseContentType(newMime
, parsedMime
, dummy
);
910 if (!parsedMime
.IsEmpty()) {
911 mChannel
->SetContentType(parsedMime
);
914 newMime
= channelType
;
916 } else if (newChannel
) {
917 LOG(("OBJLC [%p]: We failed to open a channel, marking invalid", this));
922 /// Determine final type
924 // In order of preference:
925 // 1) If we have attempted channel load, or set stateInvalid above, the type
926 // is always null (fallback)
927 // 2) If we have a loaded channel, we grabbed its mimeType above, use that
929 // 3) If we have a plugin type and no URI, use that type.
930 // 4) If we have a plugin type and eAllowPluginSkipChannel, use that type.
931 // 5) if we have a URI, set type to loading to indicate we'd need a channel
933 // 6) Otherwise, type null to indicate unloadable content (fallback)
936 ObjectType newMime_Type
= GetTypeOfContent(newMime
);
939 newType
= eType_Null
;
940 LOG(("OBJLC [%p]: NewType #0: %s - %u", this, newMime
.get(), newType
));
942 } else if (newChannel
) {
943 // If newChannel is set above, we considered it in setting newMime
944 newType
= newMime_Type
;
945 LOG(("OBJLC [%p]: NewType #1: %s - %u", this, newMime
.get(), newType
));
946 LOG(("OBJLC [%p]: Using channel type", this));
947 } else if (((caps
& eAllowPluginSkipChannel
) || !newURI
) &&
948 IsPluginType(newMime_Type
)) {
949 newType
= newMime_Type
;
950 LOG(("OBJLC [%p]: NewType #2: %s - %u", this, newMime
.get(), newType
));
951 LOG(("OBJLC [%p]: Plugin type with no URI, skipping channel load", this));
953 (mOriginalContentType
.IsEmpty() || newMime_Type
!= eType_Null
)) {
954 // We could potentially load this if we opened a channel on mURI, indicate
955 // this by leaving type as loading.
957 // If a MIME type was requested in the tag, but we have decided to set load
958 // type to null, ignore (otherwise we'll default to document type loading).
959 newType
= eType_Loading
;
960 LOG(("OBJLC [%p]: NewType #3: %u", this, newType
));
962 // Unloadable - no URI, and no plugin/MIME type. Non-plugin types (images,
963 // documents) always load with a channel.
964 newType
= eType_Null
;
965 LOG(("OBJLC [%p]: NewType #4: %u", this, newType
));
968 mLoadingSyntheticDocument
=
969 newType
== eType_Document
&& imgLoader::SupportImageWithMimeType(newMime
);
972 /// Handle existing channels
975 if (useChannel
&& newType
== eType_Loading
) {
976 // We decided to use a channel, and also that the previous channel is still
977 // usable, so re-use the existing values.
979 LOG(("OBJLC [%p]: NewType #5: %u", this, newType
));
980 newMime
= mContentType
;
982 } else if (useChannel
&& !newChannel
) {
983 // We have an existing channel, but did not decide to use one.
984 retval
= (ParameterUpdateFlags
)(retval
| eParamChannelChanged
);
989 /// Update changed values
992 if (newType
!= mType
) {
993 retval
= (ParameterUpdateFlags
)(retval
| eParamStateChanged
);
994 LOG(("OBJLC [%p]: Type changed from %u -> %u", this, mType
, newType
));
998 if (!URIEquals(mBaseURI
, newBaseURI
)) {
999 LOG(("OBJLC [%p]: Object effective baseURI changed", this));
1000 mBaseURI
= newBaseURI
;
1003 if (!URIEquals(newURI
, mURI
)) {
1004 retval
= (ParameterUpdateFlags
)(retval
| eParamStateChanged
);
1005 LOG(("OBJLC [%p]: Object effective URI changed", this));
1009 // We don't update content type when loading, as the type is not final and we
1010 // don't want to superfluously change between mOriginalContentType ->
1011 // mContentType when doing |obj.data = obj.data| with a channel and differing
1013 if (mType
!= eType_Loading
&& mContentType
!= newMime
) {
1014 retval
= (ParameterUpdateFlags
)(retval
| eParamStateChanged
);
1015 retval
= (ParameterUpdateFlags
)(retval
| eParamContentTypeChanged
);
1016 LOG(("OBJLC [%p]: Object effective mime type changed (%s -> %s)", this,
1017 mContentType
.get(), newMime
.get()));
1018 mContentType
= newMime
;
1021 // If we decided to keep using info from an old channel, but also that state
1022 // changed, we need to invalidate it.
1023 if (useChannel
&& !newChannel
&& (retval
& eParamStateChanged
)) {
1024 mType
= eType_Loading
;
1025 retval
= (ParameterUpdateFlags
)(retval
| eParamChannelChanged
);
1031 // Only OnStartRequest should be passing the channel parameter
1032 nsresult
nsObjectLoadingContent::LoadObject(bool aNotify
, bool aForceLoad
) {
1033 return LoadObject(aNotify
, aForceLoad
, nullptr);
1036 nsresult
nsObjectLoadingContent::LoadObject(bool aNotify
, bool aForceLoad
,
1037 nsIRequest
* aLoadingChannel
) {
1038 Element
* el
= AsElement();
1039 Document
* doc
= el
->OwnerDoc();
1040 nsresult rv
= NS_OK
;
1042 // Per bug 1318303, if the parent document is not active, load the alternative
1044 if (!doc
->IsCurrentActiveDocument()) {
1045 // Since this can be triggered on change of attributes, make sure we've
1046 // unloaded whatever is loaded first.
1048 ObjectType oldType
= mType
;
1049 mType
= eType_Fallback
;
1050 ConfigureFallback();
1051 NotifyStateChanged(oldType
, true);
1055 // XXX(johns): In these cases, we refuse to touch our content and just
1056 // remain unloaded, as per legacy behavior. It would make more sense to
1057 // load fallback content initially and refuse to ever change state again.
1058 if (doc
->IsBeingUsedAsImage()) {
1062 if (doc
->IsLoadedAsData() || doc
->IsStaticDocument()) {
1066 LOG(("OBJLC [%p]: LoadObject called, notify %u, forceload %u, channel %p",
1067 this, aNotify
, aForceLoad
, aLoadingChannel
));
1069 // We can't re-use an already open channel, but aForceLoad may make us try
1070 // to load a plugin without any changes in channel state.
1071 if (aForceLoad
&& mChannelLoaded
) {
1073 mChannelLoaded
= false;
1076 // Save these for NotifyStateChanged();
1077 ObjectType oldType
= mType
;
1079 ParameterUpdateFlags stateChange
= UpdateObjectParameters();
1081 if (!stateChange
&& !aForceLoad
) {
1086 /// State has changed, unload existing content and attempt to load new type
1088 LOG(("OBJLC [%p]: LoadObject - plugin state changed (%u)", this,
1091 // We synchronously start/stop plugin instances below, which may spin the
1092 // event loop. Re-entering into the load is fine, but at that point the
1093 // original load call needs to abort when unwinding
1094 // NOTE this is located *after* the state change check, a subsequent load
1095 // with no subsequently changed state will be a no-op.
1097 LOG(("OBJLC [%p]: Re-entering into LoadObject", this));
1100 AutoSetLoadingToFalse
reentryCheck(this);
1102 // Unload existing content, keeping in mind stopping plugins might spin the
1103 // event loop. Note that we check for still-open channels below
1104 UnloadObject(false); // Don't reset state
1106 // The event loop must've spun and re-entered into LoadObject, which
1107 // finished the load
1108 LOG(("OBJLC [%p]: Re-entered into LoadObject, aborting outer load", this));
1112 // Determine what's going on with our channel.
1113 if (stateChange
& eParamChannelChanged
) {
1114 // If the channel params changed, throw away the channel, but unset
1115 // mChannelLoaded so we'll still try to open a new one for this load if
1118 mChannelLoaded
= false;
1119 } else if (mType
== eType_Null
&& mChannel
) {
1120 // If we opened a channel but then failed to find a loadable state, throw it
1121 // away. mChannelLoaded will indicate that we tried to load a channel at one
1122 // point so we wont recurse
1124 } else if (mType
== eType_Loading
&& mChannel
) {
1125 // We're still waiting on a channel load, already opened one, and
1126 // channel parameters didn't change
1128 } else if (mChannelLoaded
&& mChannel
!= aLoadingChannel
) {
1129 // The only time we should have a loaded channel with a changed state is
1130 // when the channel has just opened -- in which case this call should
1131 // have originated from OnStartRequest
1132 MOZ_ASSERT_UNREACHABLE(
1133 "Loading with a channel, but state doesn't make sense");
1141 if (mType
!= eType_Null
&& mType
!= eType_Fallback
) {
1142 bool allowLoad
= true;
1143 int16_t contentPolicy
= nsIContentPolicy::ACCEPT
;
1144 // If mChannelLoaded is set we presumably already passed load policy
1145 // If mType == eType_Loading then we call OpenChannel() which internally
1146 // creates a new channel and calls asyncOpen() on that channel which
1147 // then enforces content policy checks.
1148 if (allowLoad
&& mURI
&& !mChannelLoaded
&& mType
!= eType_Loading
) {
1149 allowLoad
= CheckLoadPolicy(&contentPolicy
);
1151 // If we're loading a type now, check ProcessPolicy. Note that we may check
1152 // both now in the case of plugins whose type is determined before opening a
1154 if (allowLoad
&& mType
!= eType_Loading
) {
1155 allowLoad
= CheckProcessPolicy(&contentPolicy
);
1158 // Content policy implementations can mutate the DOM, check for re-entry
1160 LOG(("OBJLC [%p]: We re-entered in content policy, leaving original load",
1165 // Load denied, switch to null
1167 LOG(("OBJLC [%p]: Load denied by policy", this));
1172 // Don't allow view-source scheme.
1173 // view-source is the only scheme to which this applies at the moment due to
1174 // potential timing attacks to read data from cross-origin documents. If this
1175 // widens we should add a protocol flag for whether the scheme is only allowed
1176 // in top and use something like nsNetUtil::NS_URIChainHasFlags.
1177 if (mType
!= eType_Null
) {
1178 nsCOMPtr
<nsIURI
> tempURI
= mURI
;
1179 nsCOMPtr
<nsINestedURI
> nestedURI
= do_QueryInterface(tempURI
);
1181 // view-source should always be an nsINestedURI, loop and check the
1182 // scheme on this and all inner URIs that are also nested URIs.
1183 if (tempURI
->SchemeIs("view-source")) {
1184 LOG(("OBJLC [%p]: Blocking as effective URI has view-source scheme",
1190 nestedURI
->GetInnerURI(getter_AddRefs(tempURI
));
1191 nestedURI
= do_QueryInterface(tempURI
);
1195 // Items resolved as Image/Document are not candidates for content blocking,
1196 // as well as invalid plugins (they will not have the mContentType set).
1197 if ((mType
== eType_Null
|| IsPluginType(mType
)) && ShouldBlockContent()) {
1198 LOG(("OBJLC [%p]: Enable content blocking", this));
1199 mType
= eType_Loading
;
1202 // Sanity check: We shouldn't have any loaded resources, pending events, or
1203 // a final listener at this point
1204 if (mFrameLoader
|| mFinalListener
) {
1205 MOZ_ASSERT_UNREACHABLE("Trying to load new plugin with existing content");
1209 // More sanity-checking:
1210 // If mChannel is set, mChannelLoaded should be set, and vice-versa
1211 if (mType
!= eType_Null
&& !!mChannel
!= mChannelLoaded
) {
1212 MOZ_ASSERT_UNREACHABLE("Trying to load with bad channel state");
1217 /// Attempt to load new type
1220 // We don't set mFinalListener until OnStartRequest has been called, to
1221 // prevent re-entry ugliness with CloseChannel()
1222 nsCOMPtr
<nsIStreamListener
> finalListener
;
1224 case eType_Document
: {
1226 // We could mFrameLoader->LoadURI(mURI), but UpdateObjectParameters
1227 // requires documents have a channel, so this is not a valid state.
1228 MOZ_ASSERT_UNREACHABLE(
1229 "Attempting to load a document without a "
1231 rv
= NS_ERROR_FAILURE
;
1235 nsCOMPtr
<nsIDocShell
> docShell
= SetupDocShell(mURI
);
1237 rv
= NS_ERROR_FAILURE
;
1241 // We're loading a document, so we have to set LOAD_DOCUMENT_URI
1242 // (especially important for firing onload)
1243 nsLoadFlags flags
= 0;
1244 mChannel
->GetLoadFlags(&flags
);
1245 flags
|= nsIChannel::LOAD_DOCUMENT_URI
;
1246 mChannel
->SetLoadFlags(flags
);
1248 nsCOMPtr
<nsIInterfaceRequestor
> req(do_QueryInterface(docShell
));
1249 NS_ASSERTION(req
, "Docshell must be an ifreq");
1251 nsCOMPtr
<nsIURILoader
> uriLoader(components::URILoader::Service());
1252 if (NS_WARN_IF(!uriLoader
)) {
1253 MOZ_ASSERT_UNREACHABLE("Failed to get uriLoader service");
1254 mFrameLoader
->Destroy();
1255 mFrameLoader
= nullptr;
1259 rv
= uriLoader
->OpenChannel(mChannel
, nsIURILoader::DONT_RETARGET
, req
,
1260 getter_AddRefs(finalListener
));
1261 // finalListener will receive OnStartRequest either below, or if
1262 // `mChannel` is a `DocumentChannel`, it will be received after
1263 // RedirectToRealChannel.
1266 // If our type remains Loading, we need a channel to proceed
1268 if (NS_FAILED(rv
)) {
1269 LOG(("OBJLC [%p]: OpenChannel returned failure (%" PRIu32
")", this,
1270 static_cast<uint32_t>(rv
)));
1274 case eType_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 null", this));
1288 // If we didn't load anything, handle switching to fallback state
1289 if (mType
== eType_Fallback
|| mType
== eType_Null
) {
1290 LOG(("OBJLC [%p]: Switching to fallback state", this));
1291 MOZ_ASSERT(!mFrameLoader
, "switched to fallback but also loaded something");
1293 MaybeFireErrorEvent();
1296 // If we were loading with a channel but then failed over, throw it away
1300 // Don't try to initialize plugins or final listener below
1301 finalListener
= nullptr;
1303 ConfigureFallback();
1306 // Notify of our final state
1307 NotifyStateChanged(oldType
, aNotify
);
1308 NS_ENSURE_TRUE(mIsLoading
, NS_OK
);
1311 // Spawning plugins and dispatching to the final listener may re-enter, so are
1312 // delayed until after we fire a notification, to prevent missing
1313 // notifications or firing them out of order.
1315 // Note that we ensured that we entered into LoadObject() from
1316 // ::OnStartRequest above when loading with a channel.
1320 if (finalListener
) {
1321 NS_ASSERTION(mType
!= eType_Null
&& mType
!= eType_Loading
,
1322 "We should not have a final listener with a non-loaded type");
1323 mFinalListener
= finalListener
;
1325 // If we're a DocumentChannel load, hold off on firing the `OnStartRequest`
1326 // callback, as we haven't received it yet from our caller.
1327 RefPtr
<DocumentChannel
> documentChannel
= do_QueryObject(mChannel
);
1328 if (documentChannel
) {
1330 mType
== eType_Document
,
1331 "We have a DocumentChannel here but aren't loading a document?");
1333 rv
= finalListener
->OnStartRequest(mChannel
);
1337 if ((NS_FAILED(rv
) && rv
!= NS_ERROR_PARSED_DATA_CACHED
) && mIsLoading
) {
1338 // Since we've already notified of our transition, we can just Unload and
1339 // call ConfigureFallback (which will notify again)
1341 mType
= eType_Fallback
;
1342 UnloadObject(false);
1343 NS_ENSURE_TRUE(mIsLoading
, NS_OK
);
1345 ConfigureFallback();
1346 NotifyStateChanged(oldType
, true);
1352 // This call can re-enter when dealing with plugin listeners
1353 nsresult
nsObjectLoadingContent::CloseChannel() {
1355 LOG(("OBJLC [%p]: Closing channel\n", this));
1356 // Null the values before potentially-reentering, and ensure they survive
1358 nsCOMPtr
<nsIChannel
> channelGrip(mChannel
);
1359 nsCOMPtr
<nsIStreamListener
> listenerGrip(mFinalListener
);
1361 mFinalListener
= nullptr;
1362 channelGrip
->CancelWithReason(NS_BINDING_ABORTED
,
1363 "nsObjectLoadingContent::CloseChannel"_ns
);
1365 // mFinalListener is only set by LoadObject after OnStartRequest, or
1366 // by OnStartRequest in the case of late-opened plugin streams
1367 listenerGrip
->OnStopRequest(channelGrip
, NS_BINDING_ABORTED
);
1373 bool nsObjectLoadingContent::IsAboutBlankLoadOntoInitialAboutBlank(
1374 nsIURI
* aURI
, bool aInheritPrincipal
, nsIPrincipal
* aPrincipalToInherit
) {
1375 return NS_IsAboutBlank(aURI
) && aInheritPrincipal
&&
1376 (!mFrameLoader
|| !mFrameLoader
->GetExistingDocShell() ||
1377 mFrameLoader
->GetExistingDocShell()
1378 ->IsAboutBlankLoadOntoInitialAboutBlank(aURI
, aInheritPrincipal
,
1379 aPrincipalToInherit
));
1382 nsresult
nsObjectLoadingContent::OpenChannel() {
1383 Element
* el
= AsElement();
1384 Document
* doc
= el
->OwnerDoc();
1385 NS_ASSERTION(doc
, "No owner document?");
1391 if (!mURI
|| !CanHandleURI(mURI
)) {
1392 return NS_ERROR_NOT_AVAILABLE
;
1395 nsCOMPtr
<nsILoadGroup
> group
= doc
->GetDocumentLoadGroup();
1396 nsCOMPtr
<nsIChannel
> chan
;
1397 RefPtr
<ObjectInterfaceRequestorShim
> shim
=
1398 new ObjectInterfaceRequestorShim(this);
1400 bool inheritAttrs
= nsContentUtils::ChannelShouldInheritPrincipal(
1401 el
->NodePrincipal(), // aLoadState->PrincipalToInherit()
1402 mURI
, // aLoadState->URI()
1403 true, // aInheritForAboutBlank
1404 false); // aForceInherit
1406 bool inheritPrincipal
= inheritAttrs
&& !SchemeIsData(mURI
);
1408 nsSecurityFlags securityFlags
=
1409 nsILoadInfo::SEC_ALLOW_CROSS_ORIGIN_SEC_CONTEXT_IS_NULL
;
1410 if (inheritPrincipal
) {
1411 securityFlags
|= nsILoadInfo::SEC_FORCE_INHERIT_PRINCIPAL
;
1414 nsContentPolicyType contentPolicyType
= GetContentPolicyType();
1415 // The setting of LOAD_BYPASS_SERVICE_WORKER here is now an optimization.
1416 // ServiceWorkerInterceptController::ShouldPrepareForIntercept does a more
1417 // expensive check of BrowsingContext ancestors to look for object/embed.
1418 nsLoadFlags loadFlags
= nsIChannel::LOAD_CALL_CONTENT_SNIFFERS
|
1419 nsIChannel::LOAD_BYPASS_SERVICE_WORKER
|
1420 nsIRequest::LOAD_HTML_OBJECT_DATA
;
1421 uint32_t sandboxFlags
= doc
->GetSandboxFlags();
1423 // For object loads we store the CSP that potentially needs to
1424 // be inherited, e.g. in case we are loading an opaque origin
1425 // like a data: URI. The actual inheritance check happens within
1426 // Document::InitCSP(). Please create an actual copy of the CSP
1427 // (do not share the same reference) otherwise a Meta CSP of an
1428 // opaque origin will incorrectly be propagated to the embedding
1430 RefPtr
<nsCSPContext
> cspToInherit
;
1431 if (nsCOMPtr
<nsIContentSecurityPolicy
> csp
= doc
->GetCsp()) {
1432 cspToInherit
= new nsCSPContext();
1433 cspToInherit
->InitFromOther(static_cast<nsCSPContext
*>(csp
.get()));
1436 // --- Create LoadInfo
1437 RefPtr
<LoadInfo
> loadInfo
= new LoadInfo(
1438 /*aLoadingPrincipal = aLoadingContext->NodePrincipal() */ nullptr,
1439 /*aTriggeringPrincipal = aLoadingPrincipal */ nullptr,
1440 /*aLoadingContext = */ el
,
1441 /*aSecurityFlags = */ securityFlags
,
1442 /*aContentPolicyType = */ contentPolicyType
,
1443 /*aLoadingClientInfo = */ Nothing(),
1444 /*aController = */ Nothing(),
1445 /*aSandboxFlags = */ sandboxFlags
);
1448 loadInfo
->SetPrincipalToInherit(el
->NodePrincipal());
1452 loadInfo
->SetCSPToInherit(cspToInherit
);
1455 if (DocumentChannel::CanUseDocumentChannel(mURI
) &&
1456 !IsAboutBlankLoadOntoInitialAboutBlank(mURI
, inheritPrincipal
,
1457 el
->NodePrincipal())) {
1458 // --- Create LoadState
1459 RefPtr
<nsDocShellLoadState
> loadState
= new nsDocShellLoadState(mURI
);
1460 loadState
->SetPrincipalToInherit(el
->NodePrincipal());
1461 loadState
->SetTriggeringPrincipal(loadInfo
->TriggeringPrincipal());
1463 loadState
->SetCsp(cspToInherit
);
1465 loadState
->SetTriggeringSandboxFlags(sandboxFlags
);
1467 // TODO(djg): This was httpChan->SetReferrerInfoWithoutClone(referrerInfo);
1468 // Is the ...WithoutClone(...) important?
1469 auto referrerInfo
= MakeRefPtr
<ReferrerInfo
>(*doc
);
1470 loadState
->SetReferrerInfo(referrerInfo
);
1473 DocumentChannel::CreateForObject(loadState
, loadInfo
, loadFlags
, shim
);
1475 // NS_NewChannel sets the group on the channel. CreateDocumentChannel does
1477 chan
->SetLoadGroup(group
);
1479 rv
= NS_NewChannelInternal(getter_AddRefs(chan
), // outChannel
1481 loadInfo
, // aLoadInfo
1482 nullptr, // aPerformanceStorage
1483 group
, // aLoadGroup
1485 loadFlags
, // aLoadFlags
1486 nullptr); // aIoService
1487 NS_ENSURE_SUCCESS(rv
, rv
);
1490 nsCOMPtr
<nsILoadInfo
> loadinfo
= chan
->LoadInfo();
1491 loadinfo
->SetPrincipalToInherit(el
->NodePrincipal());
1494 // For object loads we store the CSP that potentially needs to
1495 // be inherited, e.g. in case we are loading an opaque origin
1496 // like a data: URI. The actual inheritance check happens within
1497 // Document::InitCSP(). Please create an actual copy of the CSP
1498 // (do not share the same reference) otherwise a Meta CSP of an
1499 // opaque origin will incorrectly be propagated to the embedding
1502 nsCOMPtr
<nsILoadInfo
> loadinfo
= chan
->LoadInfo();
1503 static_cast<LoadInfo
*>(loadinfo
.get())->SetCSPToInherit(cspToInherit
);
1508 nsCOMPtr
<nsIHttpChannel
> httpChan(do_QueryInterface(chan
));
1510 auto referrerInfo
= MakeRefPtr
<ReferrerInfo
>(*doc
);
1512 rv
= httpChan
->SetReferrerInfoWithoutClone(referrerInfo
);
1513 MOZ_ASSERT(NS_SUCCEEDED(rv
));
1515 // Set the initiator type
1516 nsCOMPtr
<nsITimedChannel
> timedChannel(do_QueryInterface(httpChan
));
1518 timedChannel
->SetInitiatorType(el
->LocalName());
1521 nsCOMPtr
<nsIClassOfService
> cos(do_QueryInterface(httpChan
));
1522 if (cos
&& UserActivation::IsHandlingUserInput()) {
1523 cos
->AddClassFlags(nsIClassOfService::UrgentStart
);
1527 nsCOMPtr
<nsIScriptChannel
> scriptChannel
= do_QueryInterface(chan
);
1528 if (scriptChannel
) {
1529 // Allow execution against our context if the principals match
1530 scriptChannel
->SetExecutionPolicy(nsIScriptChannel::EXECUTE_NORMAL
);
1533 // AsyncOpen can fail if a file does not exist.
1534 rv
= chan
->AsyncOpen(shim
);
1535 NS_ENSURE_SUCCESS(rv
, rv
);
1536 LOG(("OBJLC [%p]: Channel opened", this));
1541 uint32_t nsObjectLoadingContent::GetCapabilities() const {
1542 return eSupportImages
| eSupportDocuments
;
1545 void nsObjectLoadingContent::Destroy() {
1547 mFrameLoader
->Destroy();
1548 mFrameLoader
= nullptr;
1551 // Reset state so that if the element is re-appended to tree again (e.g.
1552 // adopting to another document), it will reload resource again.
1557 void nsObjectLoadingContent::Traverse(nsObjectLoadingContent
* tmp
,
1558 nsCycleCollectionTraversalCallback
& cb
) {
1559 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mFrameLoader
);
1563 void nsObjectLoadingContent::Unlink(nsObjectLoadingContent
* tmp
) {
1564 if (tmp
->mFrameLoader
) {
1565 tmp
->mFrameLoader
->Destroy();
1567 NS_IMPL_CYCLE_COLLECTION_UNLINK(mFrameLoader
);
1570 void nsObjectLoadingContent::UnloadObject(bool aResetState
) {
1572 mFrameLoader
->Destroy();
1573 mFrameLoader
= nullptr;
1578 mChannelLoaded
= false;
1579 mType
= eType_Loading
;
1580 mURI
= mOriginalURI
= mBaseURI
= nullptr;
1581 mContentType
.Truncate();
1582 mOriginalContentType
.Truncate();
1585 mScriptRequested
= false;
1587 mIsStopping
= false;
1589 mSubdocumentIntrinsicSize
.reset();
1590 mSubdocumentIntrinsicRatio
.reset();
1593 void nsObjectLoadingContent::NotifyStateChanged(ObjectType aOldType
,
1595 LOG(("OBJLC [%p]: NotifyStateChanged: (%u) -> (%u) (notify %i)", this,
1596 aOldType
, mType
, aNotify
));
1598 dom::Element
* thisEl
= AsElement();
1599 // Non-images are always not broken.
1600 // XXX: I assume we could just remove this completely?
1601 thisEl
->RemoveStates(ElementState::BROKEN
, aNotify
);
1603 if (mType
== aOldType
) {
1607 Document
* doc
= thisEl
->GetComposedDoc();
1609 return; // Nothing to do
1612 PresShell
* presShell
= doc
->GetPresShell();
1613 // If there is no PresShell or it hasn't been initialized there isn't much to
1615 if (!presShell
|| !presShell
->DidInitialize()) {
1618 presShell
->PostRecreateFramesFor(thisEl
);
1621 nsObjectLoadingContent::ObjectType
nsObjectLoadingContent::GetTypeOfContent(
1622 const nsCString
& aMIMEType
) {
1623 Element
* el
= AsElement();
1624 NS_ASSERTION(el
, "must be a content");
1626 // Images and documents are always supported.
1627 MOZ_ASSERT((GetCapabilities() & (eSupportImages
| eSupportDocuments
)) ==
1628 (eSupportImages
| eSupportDocuments
));
1631 ("OBJLC [%p]: calling HtmlObjectContentTypeForMIMEType: aMIMEType: %s - "
1633 this, aMIMEType
.get(), el
));
1634 auto ret
= static_cast<ObjectType
>(
1635 nsContentUtils::HtmlObjectContentTypeForMIMEType(aMIMEType
));
1636 LOG(("OBJLC [%p]: called HtmlObjectContentTypeForMIMEType\n", this));
1640 void nsObjectLoadingContent::CreateStaticClone(
1641 nsObjectLoadingContent
* aDest
) const {
1642 MOZ_ASSERT(aDest
->AsElement()->OwnerDoc()->IsStaticDocument());
1643 aDest
->mType
= mType
;
1646 aDest
->AsElement()->OwnerDoc()->AddPendingFrameStaticClone(aDest
,
1652 nsObjectLoadingContent::GetSrcURI(nsIURI
** aURI
) {
1653 NS_IF_ADDREF(*aURI
= GetSrcURI());
1657 void nsObjectLoadingContent::ConfigureFallback() {
1658 MOZ_ASSERT(!mFrameLoader
&& !mChannel
,
1659 "ConfigureFallback called with loaded content");
1661 // We only fallback in special cases where we are already of fallback
1662 // type (e.g. removed Flash plugin use) or where something went wrong
1663 // (e.g. unknown MIME type).
1664 MOZ_ASSERT(mType
== eType_Fallback
|| mType
== eType_Null
);
1666 Element
* el
= AsElement();
1668 // There are two types of fallback:
1669 // 1. HTML fallbacks are children of the <object> or <embed> DOM element.
1670 // 2. The special transparent region fallback replacing Flash use.
1671 // If our type is eType_Fallback (e.g. Flash use) then we use #1 if
1672 // available, otherwise we use #2.
1673 // If our type is eType_Null (e.g. unknown MIME type) then we use
1674 // #1, otherwise the element has no size.
1675 bool hasHtmlFallback
= false;
1676 if (el
->IsHTMLElement(nsGkAtoms::object
)) {
1677 // Do a depth-first traverse of node tree with the current element as root,
1678 // looking for non-<param> elements. If we find some then we have an HTML
1679 // fallback for this element.
1680 for (nsIContent
* child
= el
->GetFirstChild(); child
;) {
1682 hasHtmlFallback
|| (!child
->IsHTMLElement(nsGkAtoms::param
) &&
1683 nsStyleUtil::IsSignificantChild(child
, false));
1685 // <object> and <embed> elements in the fallback need to StartObjectLoad.
1686 // Their children should be ignored since they are part of those
1687 // element's fallback.
1688 if (auto* embed
= HTMLEmbedElement::FromNode(child
)) {
1689 embed
->StartObjectLoad(true, true);
1690 // Skip the children
1691 child
= child
->GetNextNonChildNode(el
);
1692 } else if (auto* object
= HTMLObjectElement::FromNode(child
)) {
1693 object
->StartObjectLoad(true, true);
1694 // Skip the children
1695 child
= child
->GetNextNonChildNode(el
);
1697 child
= child
->GetNextNode(el
);
1702 // If we find an HTML fallback then we always switch type to null.
1703 if (hasHtmlFallback
) {
1709 nsObjectLoadingContent::UpgradeLoadToDocument(
1710 nsIChannel
* aRequest
, BrowsingContext
** aBrowsingContext
) {
1711 AUTO_PROFILER_LABEL("nsObjectLoadingContent::UpgradeLoadToDocument", NETWORK
);
1713 LOG(("OBJLC [%p]: UpgradeLoadToDocument", this));
1715 if (aRequest
!= mChannel
|| !aRequest
) {
1716 // happens when a new load starts before the previous one got here.
1717 return NS_BINDING_ABORTED
;
1720 // We should be state loading.
1721 if (mType
!= eType_Loading
) {
1722 MOZ_ASSERT_UNREACHABLE("Should be type loading at this point");
1723 return NS_BINDING_ABORTED
;
1725 MOZ_ASSERT(!mChannelLoaded
, "mChannelLoaded set already?");
1726 MOZ_ASSERT(!mFinalListener
, "mFinalListener exists already?");
1728 mChannelLoaded
= true;
1730 // We don't need to check for errors here, unlike in `OnStartRequest`, as
1731 // `UpgradeLoadToDocument` is only called when the load is going to become a
1732 // process-switching load. As we never process switch for failed object loads,
1733 // we know our channel status is successful.
1735 // Call `LoadObject` to trigger our nsObjectLoadingContext to switch into the
1736 // specified new state.
1737 nsresult rv
= LoadObject(true, false, aRequest
);
1738 if (NS_WARN_IF(NS_FAILED(rv
))) {
1742 RefPtr
<BrowsingContext
> bc
= GetBrowsingContext();
1744 return NS_ERROR_FAILURE
;
1747 bc
.forget(aBrowsingContext
);
1751 bool nsObjectLoadingContent::ShouldBlockContent() {
1752 return mContentBlockingEnabled
&& mURI
&& IsFlashMIME(mContentType
) &&
1753 StaticPrefs::browser_safebrowsing_blockedURIs_enabled();
1756 Document
* nsObjectLoadingContent::GetContentDocument(
1757 nsIPrincipal
& aSubjectPrincipal
) {
1758 Element
* el
= AsElement();
1759 if (!el
->IsInComposedDoc()) {
1763 Document
* sub_doc
= el
->OwnerDoc()->GetSubDocumentFor(el
);
1768 // Return null for cross-origin contentDocument.
1769 if (!aSubjectPrincipal
.SubsumesConsideringDomain(sub_doc
->NodePrincipal())) {
1776 void nsObjectLoadingContent::MaybeFireErrorEvent() {
1777 Element
* el
= AsElement();
1778 // Queue a task to fire an error event if we're an <object> element. The
1779 // queueing is important, since then we don't have to worry about reentry.
1780 if (el
->IsHTMLElement(nsGkAtoms::object
)) {
1781 RefPtr
<AsyncEventDispatcher
> loadBlockingAsyncDispatcher
=
1782 new LoadBlockingAsyncEventDispatcher(el
, u
"error"_ns
, CanBubble::eNo
,
1783 ChromeOnlyDispatch::eNo
);
1784 loadBlockingAsyncDispatcher
->PostDOMEvent();
1788 bool nsObjectLoadingContent::BlockEmbedOrObjectContentLoading() {
1789 Element
* el
= AsElement();
1791 // Traverse up the node tree to see if we have any ancestors that may block us
1793 for (nsIContent
* parent
= el
->GetParent(); parent
;
1794 parent
= parent
->GetParent()) {
1795 if (parent
->IsAnyOfHTMLElements(nsGkAtoms::video
, nsGkAtoms::audio
)) {
1798 // If we have an ancestor that is an object with a source, it'll have an
1799 // associated displayed type. If that type is not null, don't load content
1801 if (HTMLObjectElement
* object
= HTMLObjectElement::FromNode(parent
)) {
1802 uint32_t type
= object
->DisplayedType();
1803 if (type
!= eType_Null
) {
1811 void nsObjectLoadingContent::SubdocumentIntrinsicSizeOrRatioChanged(
1812 const Maybe
<IntrinsicSize
>& aIntrinsicSize
,
1813 const Maybe
<AspectRatio
>& aIntrinsicRatio
) {
1814 if (aIntrinsicSize
== mSubdocumentIntrinsicSize
&&
1815 aIntrinsicRatio
== mSubdocumentIntrinsicRatio
) {
1819 mSubdocumentIntrinsicSize
= aIntrinsicSize
;
1820 mSubdocumentIntrinsicRatio
= aIntrinsicRatio
;
1822 if (nsSubDocumentFrame
* sdf
= do_QueryFrame(AsElement()->GetPrimaryFrame())) {
1823 sdf
->SubdocumentIntrinsicSizeOrRatioChanged();
1827 void nsObjectLoadingContent::SubdocumentImageLoadComplete(nsresult aResult
) {
1828 ObjectType oldType
= mType
;
1829 mLoadingSyntheticDocument
= false;
1831 if (NS_FAILED(aResult
)) {
1833 mType
= eType_Fallback
;
1834 ConfigureFallback();
1835 NotifyStateChanged(oldType
, true);
1839 // (mChannelLoaded && mChannel) indicates this is a good state, not any sort
1841 MOZ_DIAGNOSTIC_ASSERT_IF(mChannelLoaded
&& mChannel
, mType
== eType_Document
);
1842 NotifyStateChanged(oldType
, true);
1845 void nsObjectLoadingContent::MaybeStoreCrossOriginFeaturePolicy() {
1846 MOZ_DIAGNOSTIC_ASSERT(mFrameLoader
);
1848 // If the browsingContext is not ready (because docshell is dead), don't try
1850 if (!mFrameLoader
->IsRemoteFrame() && !mFrameLoader
->GetExistingDocShell()) {
1854 RefPtr
<BrowsingContext
> browsingContext
= mFrameLoader
->GetBrowsingContext();
1856 if (!browsingContext
|| !browsingContext
->IsContentSubframe()) {
1860 Element
* el
= AsElement();
1861 if (!el
->IsInComposedDoc()) {
1865 FeaturePolicy
* featurePolicy
= el
->OwnerDoc()->FeaturePolicy();
1867 if (ContentChild
* cc
= ContentChild::GetSingleton()) {
1868 Unused
<< cc
->SendSetContainerFeaturePolicy(browsingContext
, featurePolicy
);