Backed out changeset b462e7b742d8 (bug 1908261) for causing multiple reftest failures...
[gecko.git] / dom / base / nsObjectLoadingContent.cpp
blob4b7e309de0d30b0b9ec28c11f6f05cf53489f664
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6 /*
7 * A base class implementing nsIObjectLoadingContent for use by
8 * various content nodes that want to provide plugin/document/image
9 * loading functionality (eg <embed>, <object>, etc).
12 // Interface headers
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"
32 #include "nsError.h"
34 // Util headers
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"
48 // Concrete classes
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"
81 #ifdef XP_WIN
82 // Thanks so much, Microsoft! :(
83 # ifdef CreateEvent
84 # undef CreateEvent
85 # endif
86 #endif // XP_WIN
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");
96 return sLog;
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 {
119 public:
120 explicit AutoSetLoadingToFalse(nsObjectLoadingContent* aContent)
121 : mContent(aContent) {}
122 ~AutoSetLoadingToFalse() { mContent->mIsLoading = false; }
124 private:
125 nsObjectLoadingContent* mContent;
129 /// Helper functions
132 bool nsObjectLoadingContent::IsSuccessfulRequest(nsIRequest* aRequest,
133 nsresult* aStatus) {
134 nsresult rv = aRequest->GetStatus(aStatus);
135 if (NS_FAILED(rv) || NS_FAILED(*aStatus)) {
136 return false;
139 // This may still be an error page or somesuch
140 nsCOMPtr<nsIHttpChannel> httpChan(do_QueryInterface(aRequest));
141 if (httpChan) {
142 bool success;
143 rv = httpChan->GetRequestSucceeded(&success);
144 if (NS_FAILED(rv) || !success) {
145 return false;
149 // Otherwise, the request is successful
150 return true;
153 static bool CanHandleURI(nsIURI* aURI) {
154 nsAutoCString scheme;
155 if (NS_FAILED(aURI->GetScheme(scheme))) {
156 return false;
159 nsCOMPtr<nsIIOService> ios = mozilla::components::IO::Service();
160 if (!ios) {
161 return false;
164 nsCOMPtr<nsIProtocolHandler> handler;
165 ios->GetProtocolHandler(scheme.get(), getter_AddRefs(handler));
166 if (!handler) {
167 return false;
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) {
178 bool equal;
179 return (!a && !b) || (a && b && NS_SUCCEEDED(a->Equals(b, &equal)) && equal);
183 /// Member Functions
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) {
195 SetupFrameLoader();
196 if (!mFrameLoader) {
197 return nullptr;
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?");
210 } else {
211 LOG(("OBJLC [%p]: Aborting recursive load", this));
215 if (!docShell) {
216 mFrameLoader->Destroy();
217 mFrameLoader = nullptr;
218 return 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
228 UnloadObject();
231 nsObjectLoadingContent::nsObjectLoadingContent()
232 : mType(ObjectType::Loading),
233 mChannelLoaded(false),
234 mNetworkCreated(true),
235 mContentBlockingEnabled(false),
236 mIsStopping(false),
237 mIsLoading(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
244 if (mFrameLoader) {
245 MOZ_ASSERT_UNREACHABLE(
246 "Should not be tearing down frame loaders at this point");
247 mFrameLoader->Destroy();
251 // nsIRequestObserver
252 NS_IMETHODIMP
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
272 // state.
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.
285 if (success) {
286 LOG(("OBJLC [%p]: OnStartRequest: DocumentChannel request succeeded\n",
287 this));
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;
296 MOZ_ALWAYS_SUCCEEDS(
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"));
316 if (console) {
317 nsCOMPtr<nsIURI> uri;
318 chan->GetURI(getter_AddRefs(uri));
319 nsString message =
320 u"Blocking "_ns +
321 NS_ConvertASCIItoUTF16(uri->GetSpecOrDefault().get()) +
322 nsLiteralString(
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;
335 if (!success) {
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
339 // the bad state.
340 mChannel = nullptr;
341 LoadObject(true, false);
342 return NS_ERROR_FAILURE;
345 return LoadObject(true, false, aRequest);
348 NS_IMETHODIMP
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;
369 mChannel = nullptr;
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
379 return NS_OK;
382 // nsIStreamListener
383 NS_IMETHODIMP
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,
395 aCount);
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 "
401 "listener");
402 mChannel = nullptr;
404 return NS_ERROR_UNEXPECTED;
407 NS_IMETHODIMP
408 nsObjectLoadingContent::GetActualType(nsACString& aType) {
409 aType = mContentType;
410 return NS_OK;
413 NS_IMETHODIMP
414 nsObjectLoadingContent::GetDisplayedType(uint32_t* aType) {
415 *aType = DisplayedType();
416 return NS_OK;
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
422 // that stuff.
423 class ObjectInterfaceRequestorShim final : public nsIInterfaceRequestor,
424 public nsIChannelEventSink,
425 public nsIStreamListener {
426 public:
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) {}
443 protected:
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)
456 NS_INTERFACE_MAP_END
458 NS_IMPL_CYCLE_COLLECTING_ADDREF(ObjectInterfaceRequestorShim)
459 NS_IMPL_CYCLE_COLLECTING_RELEASE(ObjectInterfaceRequestorShim)
461 NS_IMETHODIMP
462 ObjectInterfaceRequestorShim::GetInterface(const nsIID& aIID, void** aResult) {
463 if (aIID.Equals(NS_GET_IID(nsIChannelEventSink))) {
464 nsIChannelEventSink* sink = this;
465 *aResult = sink;
466 NS_ADDREF(sink);
467 return NS_OK;
469 if (aIID.Equals(NS_GET_IID(nsIObjectLoadingContent))) {
470 nsIObjectLoadingContent* olc = mContent;
471 *aResult = olc;
472 NS_ADDREF(olc);
473 return NS_OK;
475 return NS_NOINTERFACE;
478 // nsIChannelEventSink
479 NS_IMETHODIMP
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);
505 return NS_OK;
508 void nsObjectLoadingContent::MaybeRewriteYoutubeEmbed(nsIURI* aURI,
509 nsIURI* aBaseURI,
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.
514 if (!tldService) {
515 NS_WARNING("Could not get TLD service!");
516 return;
519 nsAutoCString currentBaseDomain;
520 bool ok = NS_SUCCEEDED(tldService->GetBaseDomain(aURI, 0, currentBaseDomain));
521 if (!ok) {
522 // Data URIs (commonly used for things like svg embeds) won't parse
523 // correctly, so just fail silently here.
524 return;
527 // See if URL is referencing youtube
528 if (!currentBaseDomain.EqualsLiteral("youtube.com") &&
529 !currentBaseDomain.EqualsLiteral("youtube-nocookie.com")) {
530 return;
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.
535 nsAutoCString path;
536 aURI->GetPathQueryRef(path);
537 if (!StringBeginsWith(path, "/v/"_ns)) {
538 return;
541 // See if requester is planning on using the JS API.
542 nsAutoCString uri;
543 nsresult rv = aURI->GetSpec(uri);
544 if (NS_FAILED(rv)) {
545 return;
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) {
559 replaceQuery = true;
563 Document* doc = AsElement()->OwnerDoc();
564 // If we've made it this far, we've got a rewritable embed. Log it in
565 // telemetry.
566 doc->SetUseCounter(eUseCounter_custom_YouTubeFlashEmbed);
568 // If we're pref'd off, return after telemetry has been logged.
569 if (!Preferences::GetBool(kPrefYoutubeRewrite)) {
570 return;
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.
576 if (replaceQuery) {
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
583 // video loading.
584 uri.ReplaceSubstring("/v/"_ns, "/embed/"_ns);
585 nsAutoString utf16URI = NS_ConvertUTF8toUTF16(uri);
586 rv = nsContentUtils::NewURIWithDocumentCharset(aRewrittenURI, utf16URI, doc,
587 aBaseURI);
588 if (NS_FAILED(rv)) {
589 return;
591 AutoTArray<nsString, 2> params = {utf16OldURI, utf16URI};
592 const char* msgName;
593 // If there's no query to rewrite, just notify in the developer console
594 // that we're changing the embed.
595 if (!replaceQuery) {
596 msgName = "RewriteYouTubeEmbed";
597 } else {
598 msgName = "RewriteYouTubeEmbedPathParams";
600 nsContentUtils::ReportToConsole(nsIScriptError::warningFlag, "Plugins"_ns,
601 doc, nsContentUtils::eDOM_PROPERTIES, msgName,
602 params);
605 bool nsObjectLoadingContent::CheckLoadPolicy(int16_t* aContentPolicy) {
606 if (!aContentPolicy || !mURI) {
607 MOZ_ASSERT_UNREACHABLE("Doing it wrong");
608 return false;
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,
620 contentPolicyType);
622 *aContentPolicy = nsIContentPolicy::ACCEPT;
623 nsresult rv =
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()));
630 return false;
633 return true;
636 bool nsObjectLoadingContent::CheckProcessPolicy(int16_t* aContentPolicy) {
637 if (!aContentPolicy) {
638 MOZ_ASSERT_UNREACHABLE("Null out variable");
639 return false;
642 Element* el = AsElement();
643 Document* doc = el->OwnerDoc();
645 nsContentPolicyType objectType;
646 switch (mType) {
647 case ObjectType::Document:
648 objectType = nsIContentPolicy::TYPE_DOCUMENT;
649 break;
650 default:
651 MOZ_ASSERT_UNREACHABLE(
652 "Calling checkProcessPolicy with an unexpected type");
653 return false;
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));
669 return false;
672 return true;
675 bool nsObjectLoadingContent::IsSyntheticImageDocument() const {
676 if (mType != ObjectType::Document || !mFrameLoader) {
677 return false;
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));
691 nsresult rv;
692 nsAutoCString newMime;
693 nsAutoString typeAttr;
694 nsCOMPtr<nsIURI> newURI;
695 nsCOMPtr<nsIURI> newBaseURI;
696 ObjectType newType;
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
702 // decisions changed
703 // eParamStateChanged - means anything that affects what content we load
704 // changed, even if the channel we'd open remains the
705 // same.
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
710 // that far.
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.
721 newMime.Truncate();
722 stateInvalid = true;
726 /// Codebase
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);
736 if (NS_FAILED(rv)) {
737 // Malformed URI
738 LOG(
739 ("OBJLC [%p]: Could not parse plugin's codebase as a URI, "
740 "will use document baseURI instead",
741 this));
745 // If we failed to build a valid URI, use the document's base URI
746 if (!newBaseURI) {
747 newBaseURI = docBaseURI;
750 nsAutoString rawTypeAttr;
751 el->GetAttr(nsGkAtoms::type, rawTypeAttr);
752 if (!rawTypeAttr.IsEmpty()) {
753 typeAttr = rawTypeAttr;
754 nsAutoString params;
755 nsAutoString mime;
756 nsContentUtils::SplitMimeType(rawTypeAttr, mime, params);
757 CopyUTF16toUTF8(mime, newMime);
761 /// URI
764 nsAutoString uriStr;
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);
770 } else {
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
777 // not change.
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));
783 if (rewrittenURI) {
784 newURI = rewrittenURI;
785 mRewrittenYoutubeEmbed = true;
786 newMime = "text/html"_ns;
789 if (NS_FAILED(rv)) {
790 stateInvalid = true;
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
830 // using `newMime`.
831 newMime = TEXT_HTML;
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);
838 if (NS_FAILED(rv)) {
839 MOZ_ASSERT_UNREACHABLE("GetContentType failed");
840 stateInvalid = true;
841 channelType.Truncate();
844 LOG(("OBJLC [%p]: Channel has a content type of %s", this,
845 channelType.get()));
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));
859 if (NS_FAILED(rv)) {
860 MOZ_ASSERT_UNREACHABLE("NS_GetFinalChannelURI failure");
861 stateInvalid = true;
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",
880 this));
881 overrideChannelType = true;
882 } else if (binaryChannelType && typeHint != ObjectType::Fallback) {
883 if (typeHint == ObjectType::Document) {
884 if (imgLoader::SupportImageWithMimeType(newMime)) {
885 LOG(
886 ("OBJLC [%p]: Using type hint in favor of binary channel type "
887 "(Image Document)",
888 this));
889 overrideChannelType = true;
891 } else {
892 LOG(
893 ("OBJLC [%p]: Using type hint in favor of binary channel type "
894 "(Non-Image Document)",
895 this));
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);
909 } else {
910 newMime = channelType;
912 } else if (newChannel) {
913 LOG(("OBJLC [%p]: We failed to open a channel, marking invalid", this));
914 stateInvalid = true;
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
924 // type.
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
928 // to proceed.
929 // 6) Otherwise, type null to indicate unloadable content (fallback)
932 ObjectType newMime_Type = GetTypeOfContent(newMime);
934 if (stateInvalid) {
935 newType = ObjectType::Fallback;
936 LOG(("OBJLC [%p]: NewType #0: %s - %u", this, newMime.get(),
937 uint32_t(newType)));
938 newMime.Truncate();
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(),
943 uint32_t(newType)));
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(),
949 uint32_t(newType)));
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)));
960 } else {
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.
974 newType = mType;
975 LOG(("OBJLC [%p]: NewType #5: %u", this, uint32_t(newType)));
976 newMime = mContentType;
977 newURI = mURI;
978 } else if (useChannel && !newChannel) {
979 // We have an existing channel, but did not decide to use one.
980 retval = (ParameterUpdateFlags)(retval | eParamChannelChanged);
981 useChannel = false;
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),
991 uint32_t(newType)));
992 mType = newType;
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));
1003 mURI = newURI;
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
1009 // type.
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);
1025 return retval;
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
1040 // and return.
1041 if (!doc->IsCurrentActiveDocument()) {
1042 // Since this can be triggered on change of attributes, make sure we've
1043 // unloaded whatever is loaded first.
1044 UnloadObject();
1045 ObjectType oldType = mType;
1046 mType = ObjectType::Fallback;
1047 TriggerInnerFallbackLoads();
1048 NotifyStateChanged(oldType, true);
1049 return NS_OK;
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()) {
1056 return NS_OK;
1059 if (doc->IsLoadedAsData() || doc->IsStaticDocument()) {
1060 return NS_OK;
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) {
1069 CloseChannel();
1070 mChannelLoaded = false;
1073 // Save these for NotifyStateChanged();
1074 ObjectType oldType = mType;
1076 ParameterUpdateFlags stateChange = UpdateObjectParameters();
1078 if (!stateChange && !aForceLoad) {
1079 return NS_OK;
1083 /// State has changed, unload existing content and attempt to load new type
1085 LOG(("OBJLC [%p]: LoadObject - plugin state changed (%u)", this,
1086 stateChange));
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.
1093 if (mIsLoading) {
1094 LOG(("OBJLC [%p]: Re-entering into LoadObject", this));
1096 mIsLoading = true;
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
1102 if (!mIsLoading) {
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));
1106 return NS_OK;
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
1113 // necessary
1114 CloseChannel();
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
1120 CloseChannel();
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
1124 return NS_OK;
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");
1131 return NS_OK;
1135 // Security checks
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
1150 // channel.
1151 if (allowLoad && mType != ObjectType::Loading) {
1152 allowLoad = CheckProcessPolicy(&contentPolicy);
1155 // Content policy implementations can mutate the DOM, check for re-entry
1156 if (!mIsLoading) {
1157 LOG(("OBJLC [%p]: We re-entered in content policy, leaving original load",
1158 this));
1159 return NS_OK;
1162 // Load denied, switch to null
1163 if (!allowLoad) {
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);
1177 while (nestedURI) {
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",
1182 this));
1183 mType = ObjectType::Fallback;
1184 break;
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");
1203 return NS_OK;
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");
1210 return NS_OK;
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;
1220 switch (mType) {
1221 case ObjectType::Document: {
1222 if (!mChannel) {
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 "
1227 "channel");
1228 rv = NS_ERROR_FAILURE;
1229 break;
1232 nsCOMPtr<nsIDocShell> docShell = SetupDocShell(mURI);
1233 if (!docShell) {
1234 rv = NS_ERROR_FAILURE;
1235 break;
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;
1253 break;
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.
1265 } break;
1266 case ObjectType::Loading:
1267 // If our type remains Loading, we need a channel to proceed
1268 rv = OpenChannel();
1269 if (NS_FAILED(rv)) {
1270 LOG(("OBJLC [%p]: OpenChannel returned failure (%" PRIu32 ")", this,
1271 static_cast<uint32_t>(rv)));
1273 break;
1274 case ObjectType::Fallback:
1275 // Handled below, silence compiler warnings
1276 break;
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();
1294 if (mChannel) {
1295 // If we were loading with a channel but then failed over, throw it away
1296 CloseChannel();
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.
1318 rv = NS_OK;
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) {
1328 MOZ_ASSERT(
1329 mType == ObjectType::Document,
1330 "We have a DocumentChannel here but aren't loading a document?");
1331 } else {
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)
1339 oldType = mType;
1340 mType = ObjectType::Fallback;
1341 UnloadObject(false);
1342 NS_ENSURE_TRUE(mIsLoading, NS_OK);
1343 CloseChannel();
1344 TriggerInnerFallbackLoads();
1345 NotifyStateChanged(oldType, true);
1348 return NS_OK;
1351 // This call can re-enter when dealing with plugin listeners
1352 nsresult nsObjectLoadingContent::CloseChannel() {
1353 if (mChannel) {
1354 LOG(("OBJLC [%p]: Closing channel\n", this));
1355 // Null the values before potentially-reentering, and ensure they survive
1356 // the call
1357 nsCOMPtr<nsIChannel> channelGrip(mChannel);
1358 nsCOMPtr<nsIStreamListener> listenerGrip(mFinalListener);
1359 mChannel = nullptr;
1360 mFinalListener = nullptr;
1361 channelGrip->CancelWithReason(NS_BINDING_ABORTED,
1362 "nsObjectLoadingContent::CloseChannel"_ns);
1363 if (listenerGrip) {
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);
1369 return NS_OK;
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?");
1386 nsresult rv;
1387 mChannel = nullptr;
1389 // E.g. mms://
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
1428 // document.
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);
1446 if (inheritAttrs) {
1447 loadInfo->SetPrincipalToInherit(el->NodePrincipal());
1450 if (cspToInherit) {
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());
1461 if (cspToInherit) {
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);
1484 chan =
1485 DocumentChannel::CreateForObject(loadState, loadInfo, loadFlags, shim);
1486 MOZ_ASSERT(chan);
1487 // NS_NewChannel sets the group on the channel. CreateDocumentChannel does
1488 // not.
1489 chan->SetLoadGroup(group);
1490 } else {
1491 rv = NS_NewChannelInternal(getter_AddRefs(chan), // outChannel
1492 mURI, // aUri
1493 loadInfo, // aLoadInfo
1494 nullptr, // aPerformanceStorage
1495 group, // aLoadGroup
1496 shim, // aCallbacks
1497 loadFlags, // aLoadFlags
1498 nullptr); // aIoService
1499 NS_ENSURE_SUCCESS(rv, rv);
1501 if (inheritAttrs) {
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
1512 // document.
1513 if (cspToInherit) {
1514 nsCOMPtr<nsILoadInfo> loadinfo = chan->LoadInfo();
1515 static_cast<LoadInfo*>(loadinfo.get())->SetCSPToInherit(cspToInherit);
1519 // Referrer
1520 nsCOMPtr<nsIHttpChannel> httpChan(do_QueryInterface(chan));
1521 if (httpChan) {
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));
1529 if (timedChannel) {
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));
1549 mChannel = chan;
1550 return NS_OK;
1553 uint32_t nsObjectLoadingContent::GetCapabilities() const {
1554 return eSupportImages | eSupportDocuments;
1557 void nsObjectLoadingContent::Destroy() {
1558 if (mFrameLoader) {
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.
1565 UnloadObject();
1568 /* static */
1569 void nsObjectLoadingContent::Traverse(nsObjectLoadingContent* tmp,
1570 nsCycleCollectionTraversalCallback& cb) {
1571 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mFrameLoader);
1572 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mFeaturePolicy);
1575 /* static */
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) {
1585 if (mFrameLoader) {
1586 mFrameLoader->Destroy();
1587 mFrameLoader = nullptr;
1590 if (aResetState) {
1591 CloseChannel();
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,
1608 bool aNotify) {
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) {
1618 return;
1621 Document* doc = thisEl->GetComposedDoc();
1622 if (!doc) {
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
1628 // do.
1629 if (!presShell || !presShell->DidInitialize()) {
1630 return;
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));
1646 LOG(
1647 ("OBJLC [%p]: calling HtmlObjectContentTypeForMIMEType: aMIMEType: %s - "
1648 "el: %p\n",
1649 this, aMIMEType.get(), el));
1650 auto ret =
1651 static_cast<ObjectType>(nsContentUtils::HtmlObjectContentTypeForMIMEType(
1652 aMIMEType, doc->GetSandboxFlags()));
1653 LOG(("OBJLC [%p]: called HtmlObjectContentTypeForMIMEType\n", this));
1654 return ret;
1657 void nsObjectLoadingContent::CreateStaticClone(
1658 nsObjectLoadingContent* aDest) const {
1659 MOZ_ASSERT(aDest->AsElement()->OwnerDoc()->IsStaticDocument());
1660 aDest->mType = mType;
1662 if (mFrameLoader) {
1663 aDest->AsElement()->OwnerDoc()->AddPendingFrameStaticClone(aDest,
1664 mFrameLoader);
1668 NS_IMETHODIMP
1669 nsObjectLoadingContent::GetSrcURI(nsIURI** aURI) {
1670 NS_IF_ADDREF(*aURI = GetSrcURI());
1671 return NS_OK;
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)) {
1681 return;
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
1689 // fallback.
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);
1698 } else {
1699 child = child->GetNextNode(el);
1704 NS_IMETHODIMP
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))) {
1735 return rv;
1738 RefPtr<BrowsingContext> bc = GetBrowsingContext();
1739 if (!bc) {
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);
1751 return NS_OK;
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()) {
1763 return nullptr;
1766 Document* sub_doc = el->OwnerDoc()->GetSubDocumentFor(el);
1767 if (!sub_doc) {
1768 return nullptr;
1771 // Return null for cross-origin contentDocument.
1772 if (!aSubjectPrincipal.SubsumesConsideringDomain(sub_doc->NodePrincipal())) {
1773 return nullptr;
1776 return sub_doc;
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
1795 // from loading
1796 for (nsIContent* parent = el->GetParent(); parent;
1797 parent = parent->GetParent()) {
1798 if (parent->IsAnyOfHTMLElements(nsGkAtoms::video, nsGkAtoms::audio)) {
1799 return true;
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
1803 // for the embed.
1804 if (auto* object = HTMLObjectElement::FromNode(parent)) {
1805 if (object->Type() != ObjectType::Fallback) {
1806 return true;
1810 return false;
1813 void nsObjectLoadingContent::SubdocumentIntrinsicSizeOrRatioChanged(
1814 const Maybe<IntrinsicSize>& aIntrinsicSize,
1815 const Maybe<AspectRatio>& aIntrinsicRatio) {
1816 if (aIntrinsicSize == mSubdocumentIntrinsicSize &&
1817 aIntrinsicRatio == mSubdocumentIntrinsicRatio) {
1818 return;
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)) {
1832 UnloadObject();
1833 mType = ObjectType::Fallback;
1834 TriggerInnerFallbackLoads();
1835 NotifyStateChanged(oldType, true);
1836 return;
1839 // (mChannelLoaded && mChannel) indicates this is a good state, not any sort
1840 // of failures.
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) {
1849 return;
1852 // If the browsingContext is not ready (because docshell is dead), don't try
1853 // to create one.
1854 if (!mFrameLoader->IsRemoteFrame() && !mFrameLoader->GetExistingDocShell()) {
1855 return;
1858 RefPtr<BrowsingContext> browsingContext = mFrameLoader->GetBrowsingContext();
1860 if (!browsingContext || !browsingContext->IsContentSubframe()) {
1861 return;
1864 auto* el = nsGenericHTMLElement::FromNode(AsElement());
1865 if (!el->IsInComposedDoc()) {
1866 return;
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;
1887 if (nodeURI) {
1888 principal = BasePrincipal::CreateContentPrincipal(
1889 nodeURI,
1890 BasePrincipal::Cast(el->NodePrincipal())->OriginAttributesRef());
1891 } else {
1892 principal = el->NodePrincipal();
1895 return principal.forget();
1898 void nsObjectLoadingContent::RefreshFeaturePolicy() {
1899 if (mType != ObjectType::Document) {
1900 return;
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());
1909 MOZ_ASSERT(origin);
1910 mFeaturePolicy->SetDefaultOrigin(origin);
1912 mFeaturePolicy->InheritPolicy(AsElement()->OwnerDoc()->FeaturePolicy());
1913 MaybeStoreCrossOriginFeaturePolicy();