Backed out changeset 2450366cf7ca (bug 1891629) for causing win msix mochitest failures
[gecko.git] / dom / base / nsObjectLoadingContent.cpp
blobab87c58e87131016583fb358e76cb11633e2d9a2
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 MaybeStoreCrossOriginFeaturePolicy();
223 return docShell.forget();
226 void nsObjectLoadingContent::UnbindFromTree() {
227 // Reset state and clear pending events
228 /// XXX(johns): The implementation for GenericFrame notes that ideally we
229 /// would keep the docshell around, but trash the frameloader
230 UnloadObject();
233 nsObjectLoadingContent::nsObjectLoadingContent()
234 : mType(ObjectType::Loading),
235 mChannelLoaded(false),
236 mNetworkCreated(true),
237 mContentBlockingEnabled(false),
238 mIsStopping(false),
239 mIsLoading(false),
240 mScriptRequested(false),
241 mRewrittenYoutubeEmbed(false),
242 mLoadingSyntheticDocument(false) {}
244 nsObjectLoadingContent::~nsObjectLoadingContent() {
245 // Should have been unbound from the tree at this point, and
246 // CheckPluginStopEvent keeps us alive
247 if (mFrameLoader) {
248 MOZ_ASSERT_UNREACHABLE(
249 "Should not be tearing down frame loaders at this point");
250 mFrameLoader->Destroy();
254 // nsIRequestObserver
255 NS_IMETHODIMP
256 nsObjectLoadingContent::OnStartRequest(nsIRequest* aRequest) {
257 AUTO_PROFILER_LABEL("nsObjectLoadingContent::OnStartRequest", NETWORK);
259 LOG(("OBJLC [%p]: Channel OnStartRequest", this));
261 if (aRequest != mChannel || !aRequest) {
262 // happens when a new load starts before the previous one got here
263 return NS_BINDING_ABORTED;
266 nsCOMPtr<nsIChannel> chan(do_QueryInterface(aRequest));
267 NS_ASSERTION(chan, "Why is our request not a channel?");
269 nsresult status = NS_OK;
270 bool success = IsSuccessfulRequest(aRequest, &status);
272 // If we have already switched to type document, we're doing a
273 // process-switching DocumentChannel load. We should be able to pass down the
274 // load to our inner listener, but should also make sure to update our local
275 // state.
276 if (mType == ObjectType::Document) {
277 if (!mFinalListener) {
278 MOZ_ASSERT_UNREACHABLE(
279 "Already is Document, but don't have final listener yet?");
280 return NS_BINDING_ABORTED;
283 // If the load looks successful, fix up some of our local state before
284 // forwarding the request to the final URI loader.
286 // Forward load errors down to the document loader, so we don't tear down
287 // the nsDocShell ourselves.
288 if (success) {
289 LOG(("OBJLC [%p]: OnStartRequest: DocumentChannel request succeeded\n",
290 this));
291 nsCString channelType;
292 MOZ_ALWAYS_SUCCEEDS(mChannel->GetContentType(channelType));
294 if (GetTypeOfContent(channelType) != ObjectType::Document) {
295 MOZ_CRASH("DocumentChannel request with non-document MIME");
297 mContentType = channelType;
299 MOZ_ALWAYS_SUCCEEDS(
300 NS_GetFinalChannelURI(mChannel, getter_AddRefs(mURI)));
303 return mFinalListener->OnStartRequest(aRequest);
306 // Otherwise we should be state loading, and call LoadObject with the channel
307 if (mType != ObjectType::Loading) {
308 MOZ_ASSERT_UNREACHABLE("Should be type loading at this point");
309 return NS_BINDING_ABORTED;
311 NS_ASSERTION(!mChannelLoaded, "mChannelLoaded set already?");
312 NS_ASSERTION(!mFinalListener, "mFinalListener exists already?");
314 mChannelLoaded = true;
316 if (status == NS_ERROR_BLOCKED_URI) {
317 nsCOMPtr<nsIConsoleService> console(
318 do_GetService("@mozilla.org/consoleservice;1"));
319 if (console) {
320 nsCOMPtr<nsIURI> uri;
321 chan->GetURI(getter_AddRefs(uri));
322 nsString message =
323 u"Blocking "_ns +
324 NS_ConvertASCIItoUTF16(uri->GetSpecOrDefault().get()) +
325 nsLiteralString(
326 u" since it was found on an internal Firefox blocklist.");
327 console->LogStringMessage(message.get());
329 mContentBlockingEnabled = true;
330 return NS_ERROR_FAILURE;
333 if (UrlClassifierFeatureFactory::IsClassifierBlockingErrorCode(status)) {
334 mContentBlockingEnabled = true;
335 return NS_ERROR_FAILURE;
338 if (!success) {
339 LOG(("OBJLC [%p]: OnStartRequest: Request failed\n", this));
340 // If the request fails, we still call LoadObject() to handle fallback
341 // content and notifying of failure. (mChannelLoaded && !mChannel) indicates
342 // the bad state.
343 mChannel = nullptr;
344 LoadObject(true, false);
345 return NS_ERROR_FAILURE;
348 return LoadObject(true, false, aRequest);
351 NS_IMETHODIMP
352 nsObjectLoadingContent::OnStopRequest(nsIRequest* aRequest,
353 nsresult aStatusCode) {
354 AUTO_PROFILER_LABEL("nsObjectLoadingContent::OnStopRequest", NETWORK);
356 // Handle object not loading error because source was a tracking URL (or
357 // fingerprinting, cryptomining, etc.).
358 // We make a note of this object node by including it in a dedicated
359 // array of blocked tracking nodes under its parent document.
360 if (UrlClassifierFeatureFactory::IsClassifierBlockingErrorCode(aStatusCode)) {
361 nsCOMPtr<nsIContent> thisNode =
362 do_QueryInterface(static_cast<nsIObjectLoadingContent*>(this));
363 if (thisNode && thisNode->IsInComposedDoc()) {
364 thisNode->GetComposedDoc()->AddBlockedNodeByClassifier(thisNode);
368 if (aRequest != mChannel) {
369 return NS_BINDING_ABORTED;
372 mChannel = nullptr;
374 if (mFinalListener) {
375 // This may re-enter in the case of plugin listeners
376 nsCOMPtr<nsIStreamListener> listenerGrip(mFinalListener);
377 mFinalListener = nullptr;
378 listenerGrip->OnStopRequest(aRequest, aStatusCode);
381 // Return value doesn't matter
382 return NS_OK;
385 // nsIStreamListener
386 NS_IMETHODIMP
387 nsObjectLoadingContent::OnDataAvailable(nsIRequest* aRequest,
388 nsIInputStream* aInputStream,
389 uint64_t aOffset, uint32_t aCount) {
390 if (aRequest != mChannel) {
391 return NS_BINDING_ABORTED;
394 if (mFinalListener) {
395 // This may re-enter in the case of plugin listeners
396 nsCOMPtr<nsIStreamListener> listenerGrip(mFinalListener);
397 return listenerGrip->OnDataAvailable(aRequest, aInputStream, aOffset,
398 aCount);
401 // We shouldn't have a connected channel with no final listener
402 MOZ_ASSERT_UNREACHABLE(
403 "Got data for channel with no connected final "
404 "listener");
405 mChannel = nullptr;
407 return NS_ERROR_UNEXPECTED;
410 NS_IMETHODIMP
411 nsObjectLoadingContent::GetActualType(nsACString& aType) {
412 aType = mContentType;
413 return NS_OK;
416 NS_IMETHODIMP
417 nsObjectLoadingContent::GetDisplayedType(uint32_t* aType) {
418 *aType = DisplayedType();
419 return NS_OK;
422 // nsIInterfaceRequestor
423 // We use a shim class to implement this so that JS consumers still
424 // see an interface requestor even though WebIDL bindings don't expose
425 // that stuff.
426 class ObjectInterfaceRequestorShim final : public nsIInterfaceRequestor,
427 public nsIChannelEventSink,
428 public nsIStreamListener {
429 public:
430 NS_DECL_CYCLE_COLLECTING_ISUPPORTS
431 NS_DECL_CYCLE_COLLECTION_CLASS_AMBIGUOUS(ObjectInterfaceRequestorShim,
432 nsIInterfaceRequestor)
433 NS_DECL_NSIINTERFACEREQUESTOR
434 // RefPtr<nsObjectLoadingContent> fails due to ambiguous AddRef/Release,
435 // hence the ugly static cast :(
436 NS_FORWARD_NSICHANNELEVENTSINK(
437 static_cast<nsObjectLoadingContent*>(mContent.get())->)
438 NS_FORWARD_NSISTREAMLISTENER(
439 static_cast<nsObjectLoadingContent*>(mContent.get())->)
440 NS_FORWARD_NSIREQUESTOBSERVER(
441 static_cast<nsObjectLoadingContent*>(mContent.get())->)
443 explicit ObjectInterfaceRequestorShim(nsIObjectLoadingContent* aContent)
444 : mContent(aContent) {}
446 protected:
447 ~ObjectInterfaceRequestorShim() = default;
448 nsCOMPtr<nsIObjectLoadingContent> mContent;
451 NS_IMPL_CYCLE_COLLECTION(ObjectInterfaceRequestorShim, mContent)
453 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(ObjectInterfaceRequestorShim)
454 NS_INTERFACE_MAP_ENTRY(nsIInterfaceRequestor)
455 NS_INTERFACE_MAP_ENTRY(nsIChannelEventSink)
456 NS_INTERFACE_MAP_ENTRY(nsIStreamListener)
457 NS_INTERFACE_MAP_ENTRY(nsIRequestObserver)
458 NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIInterfaceRequestor)
459 NS_INTERFACE_MAP_END
461 NS_IMPL_CYCLE_COLLECTING_ADDREF(ObjectInterfaceRequestorShim)
462 NS_IMPL_CYCLE_COLLECTING_RELEASE(ObjectInterfaceRequestorShim)
464 NS_IMETHODIMP
465 ObjectInterfaceRequestorShim::GetInterface(const nsIID& aIID, void** aResult) {
466 if (aIID.Equals(NS_GET_IID(nsIChannelEventSink))) {
467 nsIChannelEventSink* sink = this;
468 *aResult = sink;
469 NS_ADDREF(sink);
470 return NS_OK;
472 if (aIID.Equals(NS_GET_IID(nsIObjectLoadingContent))) {
473 nsIObjectLoadingContent* olc = mContent;
474 *aResult = olc;
475 NS_ADDREF(olc);
476 return NS_OK;
478 return NS_NOINTERFACE;
481 // nsIChannelEventSink
482 NS_IMETHODIMP
483 nsObjectLoadingContent::AsyncOnChannelRedirect(
484 nsIChannel* aOldChannel, nsIChannel* aNewChannel, uint32_t aFlags,
485 nsIAsyncVerifyRedirectCallback* cb) {
486 // If we're already busy with a new load, or have no load at all,
487 // cancel the redirect.
488 if (!mChannel || aOldChannel != mChannel) {
489 return NS_BINDING_ABORTED;
492 mChannel = aNewChannel;
494 if (mFinalListener) {
495 nsCOMPtr<nsIChannelEventSink> sink(do_QueryInterface(mFinalListener));
496 MOZ_RELEASE_ASSERT(sink, "mFinalListener isn't nsIChannelEventSink?");
497 if (mType != ObjectType::Document) {
498 MOZ_ASSERT_UNREACHABLE(
499 "Not a DocumentChannel load, but we're getting a "
500 "AsyncOnChannelRedirect with a mFinalListener?");
501 return NS_BINDING_ABORTED;
504 return sink->AsyncOnChannelRedirect(aOldChannel, aNewChannel, aFlags, cb);
507 cb->OnRedirectVerifyCallback(NS_OK);
508 return NS_OK;
511 void nsObjectLoadingContent::MaybeRewriteYoutubeEmbed(nsIURI* aURI,
512 nsIURI* aBaseURI,
513 nsIURI** aRewrittenURI) {
514 nsCOMPtr<nsIEffectiveTLDService> tldService =
515 do_GetService(NS_EFFECTIVETLDSERVICE_CONTRACTID);
516 // If we can't analyze the URL, just pass on through.
517 if (!tldService) {
518 NS_WARNING("Could not get TLD service!");
519 return;
522 nsAutoCString currentBaseDomain;
523 bool ok = NS_SUCCEEDED(tldService->GetBaseDomain(aURI, 0, currentBaseDomain));
524 if (!ok) {
525 // Data URIs (commonly used for things like svg embeds) won't parse
526 // correctly, so just fail silently here.
527 return;
530 // See if URL is referencing youtube
531 if (!currentBaseDomain.EqualsLiteral("youtube.com") &&
532 !currentBaseDomain.EqualsLiteral("youtube-nocookie.com")) {
533 return;
536 // We should only rewrite URLs with paths starting with "/v/", as we shouldn't
537 // touch object nodes with "/embed/" urls that already do that right thing.
538 nsAutoCString path;
539 aURI->GetPathQueryRef(path);
540 if (!StringBeginsWith(path, "/v/"_ns)) {
541 return;
544 // See if requester is planning on using the JS API.
545 nsAutoCString uri;
546 nsresult rv = aURI->GetSpec(uri);
547 if (NS_FAILED(rv)) {
548 return;
551 // Some YouTube urls have parameters in path components, e.g.
552 // http://youtube.com/embed/7LcUOEP7Brc&start=35. These URLs work with flash,
553 // but break iframe/object embedding. If this situation occurs with rewritten
554 // URLs, convert the parameters to query in order to make the video load
555 // correctly as an iframe. In either case, warn about it in the
556 // developer console.
557 int32_t ampIndex = uri.FindChar('&', 0);
558 bool replaceQuery = false;
559 if (ampIndex != -1) {
560 int32_t qmIndex = uri.FindChar('?', 0);
561 if (qmIndex == -1 || qmIndex > ampIndex) {
562 replaceQuery = true;
566 Document* doc = AsElement()->OwnerDoc();
567 // If we've made it this far, we've got a rewritable embed. Log it in
568 // telemetry.
569 doc->SetUseCounter(eUseCounter_custom_YouTubeFlashEmbed);
571 // If we're pref'd off, return after telemetry has been logged.
572 if (!Preferences::GetBool(kPrefYoutubeRewrite)) {
573 return;
576 nsAutoString utf16OldURI = NS_ConvertUTF8toUTF16(uri);
577 // If we need to convert the URL, it means an ampersand comes first.
578 // Use the index we found earlier.
579 if (replaceQuery) {
580 // Replace question marks with ampersands.
581 uri.ReplaceChar('?', '&');
582 // Replace the first ampersand with a question mark.
583 uri.SetCharAt('?', ampIndex);
585 // Switch out video access url formats, which should possibly allow HTML5
586 // video loading.
587 uri.ReplaceSubstring("/v/"_ns, "/embed/"_ns);
588 nsAutoString utf16URI = NS_ConvertUTF8toUTF16(uri);
589 rv = nsContentUtils::NewURIWithDocumentCharset(aRewrittenURI, utf16URI, doc,
590 aBaseURI);
591 if (NS_FAILED(rv)) {
592 return;
594 AutoTArray<nsString, 2> params = {utf16OldURI, utf16URI};
595 const char* msgName;
596 // If there's no query to rewrite, just notify in the developer console
597 // that we're changing the embed.
598 if (!replaceQuery) {
599 msgName = "RewriteYouTubeEmbed";
600 } else {
601 msgName = "RewriteYouTubeEmbedPathParams";
603 nsContentUtils::ReportToConsole(nsIScriptError::warningFlag, "Plugins"_ns,
604 doc, nsContentUtils::eDOM_PROPERTIES, msgName,
605 params);
608 bool nsObjectLoadingContent::CheckLoadPolicy(int16_t* aContentPolicy) {
609 if (!aContentPolicy || !mURI) {
610 MOZ_ASSERT_UNREACHABLE("Doing it wrong");
611 return false;
614 Element* el = AsElement();
615 Document* doc = el->OwnerDoc();
617 nsContentPolicyType contentPolicyType = GetContentPolicyType();
619 nsCOMPtr<nsILoadInfo> secCheckLoadInfo =
620 new LoadInfo(doc->NodePrincipal(), // loading principal
621 doc->NodePrincipal(), // triggering principal
622 el, nsILoadInfo::SEC_ONLY_FOR_EXPLICIT_CONTENTSEC_CHECK,
623 contentPolicyType);
625 *aContentPolicy = nsIContentPolicy::ACCEPT;
626 nsresult rv =
627 NS_CheckContentLoadPolicy(mURI, secCheckLoadInfo, aContentPolicy,
628 nsContentUtils::GetContentPolicy());
629 NS_ENSURE_SUCCESS(rv, false);
630 if (NS_CP_REJECTED(*aContentPolicy)) {
631 LOG(("OBJLC [%p]: Content policy denied load of %s", this,
632 mURI->GetSpecOrDefault().get()));
633 return false;
636 return true;
639 bool nsObjectLoadingContent::CheckProcessPolicy(int16_t* aContentPolicy) {
640 if (!aContentPolicy) {
641 MOZ_ASSERT_UNREACHABLE("Null out variable");
642 return false;
645 Element* el = AsElement();
646 Document* doc = el->OwnerDoc();
648 nsContentPolicyType objectType;
649 switch (mType) {
650 case ObjectType::Document:
651 objectType = nsIContentPolicy::TYPE_DOCUMENT;
652 break;
653 default:
654 MOZ_ASSERT_UNREACHABLE(
655 "Calling checkProcessPolicy with an unexpected type");
656 return false;
659 nsCOMPtr<nsILoadInfo> secCheckLoadInfo = new LoadInfo(
660 doc->NodePrincipal(), // loading principal
661 doc->NodePrincipal(), // triggering principal
662 el, nsILoadInfo::SEC_ONLY_FOR_EXPLICIT_CONTENTSEC_CHECK, objectType);
664 *aContentPolicy = nsIContentPolicy::ACCEPT;
665 nsresult rv = NS_CheckContentProcessPolicy(
666 mURI ? mURI : mBaseURI, secCheckLoadInfo, aContentPolicy,
667 nsContentUtils::GetContentPolicy());
668 NS_ENSURE_SUCCESS(rv, false);
670 if (NS_CP_REJECTED(*aContentPolicy)) {
671 LOG(("OBJLC [%p]: CheckContentProcessPolicy rejected load", this));
672 return false;
675 return true;
678 nsObjectLoadingContent::ParameterUpdateFlags
679 nsObjectLoadingContent::UpdateObjectParameters() {
680 Element* el = AsElement();
682 uint32_t caps = GetCapabilities();
683 LOG(("OBJLC [%p]: Updating object parameters", this));
685 nsresult rv;
686 nsAutoCString newMime;
687 nsAutoString typeAttr;
688 nsCOMPtr<nsIURI> newURI;
689 nsCOMPtr<nsIURI> newBaseURI;
690 ObjectType newType;
691 // Set if this state can't be used to load anything, forces
692 // ObjectType::Fallback
693 bool stateInvalid = false;
694 // Indicates what parameters changed.
695 // eParamChannelChanged - means parameters that affect channel opening
696 // decisions changed
697 // eParamStateChanged - means anything that affects what content we load
698 // changed, even if the channel we'd open remains the
699 // same.
701 // State changes outside of the channel parameters only matter if we've
702 // already opened a channel or tried to instantiate content, whereas channel
703 // parameter changes require re-opening the channel even if we haven't gotten
704 // that far.
705 nsObjectLoadingContent::ParameterUpdateFlags retval = eParamNoChange;
708 /// Initial MIME Type
711 if (caps & eFallbackIfClassIDPresent &&
712 el->HasNonEmptyAttr(nsGkAtoms::classid)) {
713 // We don't support class ID plugin references, so we should always treat
714 // having class Ids as attributes as invalid, and fallback accordingly.
715 newMime.Truncate();
716 stateInvalid = true;
720 /// Codebase
723 nsAutoString codebaseStr;
724 nsIURI* docBaseURI = el->GetBaseURI();
725 el->GetAttr(nsGkAtoms::codebase, codebaseStr);
727 if (!codebaseStr.IsEmpty()) {
728 rv = nsContentUtils::NewURIWithDocumentCharset(
729 getter_AddRefs(newBaseURI), codebaseStr, el->OwnerDoc(), docBaseURI);
730 if (NS_FAILED(rv)) {
731 // Malformed URI
732 LOG(
733 ("OBJLC [%p]: Could not parse plugin's codebase as a URI, "
734 "will use document baseURI instead",
735 this));
739 // If we failed to build a valid URI, use the document's base URI
740 if (!newBaseURI) {
741 newBaseURI = docBaseURI;
744 nsAutoString rawTypeAttr;
745 el->GetAttr(nsGkAtoms::type, rawTypeAttr);
746 if (!rawTypeAttr.IsEmpty()) {
747 typeAttr = rawTypeAttr;
748 nsAutoString params;
749 nsAutoString mime;
750 nsContentUtils::SplitMimeType(rawTypeAttr, mime, params);
751 CopyUTF16toUTF8(mime, newMime);
755 /// URI
758 nsAutoString uriStr;
759 // Different elements keep this in various locations
760 if (el->NodeInfo()->Equals(nsGkAtoms::object)) {
761 el->GetAttr(nsGkAtoms::data, uriStr);
762 } else if (el->NodeInfo()->Equals(nsGkAtoms::embed)) {
763 el->GetAttr(nsGkAtoms::src, uriStr);
764 } else {
765 MOZ_ASSERT_UNREACHABLE("Unrecognized plugin-loading tag");
768 mRewrittenYoutubeEmbed = false;
769 // Note that the baseURI changing could affect the newURI, even if uriStr did
770 // not change.
771 if (!uriStr.IsEmpty()) {
772 rv = nsContentUtils::NewURIWithDocumentCharset(
773 getter_AddRefs(newURI), uriStr, el->OwnerDoc(), newBaseURI);
774 nsCOMPtr<nsIURI> rewrittenURI;
775 MaybeRewriteYoutubeEmbed(newURI, newBaseURI, getter_AddRefs(rewrittenURI));
776 if (rewrittenURI) {
777 newURI = rewrittenURI;
778 mRewrittenYoutubeEmbed = true;
779 newMime = "text/html"_ns;
782 if (NS_FAILED(rv)) {
783 stateInvalid = true;
788 /// Check if the original (pre-channel) content-type or URI changed, and
789 /// record mOriginal{ContentType,URI}
792 if ((mOriginalContentType != newMime) || !URIEquals(mOriginalURI, newURI)) {
793 // These parameters changing requires re-opening the channel, so don't
794 // consider the currently-open channel below
795 // XXX(johns): Changing the mime type might change our decision on whether
796 // or not we load a channel, so we count changes to it as a
797 // channel parameter change for the sake of simplicity.
798 retval = (ParameterUpdateFlags)(retval | eParamChannelChanged);
799 LOG(("OBJLC [%p]: Channel parameters changed", this));
801 mOriginalContentType = newMime;
802 mOriginalURI = newURI;
805 /// If we have a channel, see if its MIME type should take precendence and
806 /// check the final (redirected) URL
809 // If we have a loaded channel and channel parameters did not change, use it
810 // to determine what we would load.
811 bool useChannel = mChannelLoaded && !(retval & eParamChannelChanged);
812 // If we have a channel and are type loading, as opposed to having an existing
813 // channel for a previous load.
814 bool newChannel = useChannel && mType == ObjectType::Loading;
816 RefPtr<DocumentChannel> documentChannel = do_QueryObject(mChannel);
817 if (newChannel && documentChannel) {
818 // If we've got a DocumentChannel which is marked as loaded using
819 // `mChannelLoaded`, we are currently in the middle of a
820 // `UpgradeLoadToDocument`.
822 // As we don't have the real mime-type from the channel, handle this by
823 // using `newMime`.
824 newMime = TEXT_HTML;
826 MOZ_DIAGNOSTIC_ASSERT(GetTypeOfContent(newMime) == ObjectType::Document,
827 "How is text/html not ObjectType::Document?");
828 } else if (newChannel && mChannel) {
829 nsCString channelType;
830 rv = mChannel->GetContentType(channelType);
831 if (NS_FAILED(rv)) {
832 MOZ_ASSERT_UNREACHABLE("GetContentType failed");
833 stateInvalid = true;
834 channelType.Truncate();
837 LOG(("OBJLC [%p]: Channel has a content type of %s", this,
838 channelType.get()));
840 bool binaryChannelType = false;
841 if (channelType.EqualsASCII(APPLICATION_GUESS_FROM_EXT)) {
842 channelType = APPLICATION_OCTET_STREAM;
843 mChannel->SetContentType(channelType);
844 binaryChannelType = true;
845 } else if (channelType.EqualsASCII(APPLICATION_OCTET_STREAM) ||
846 channelType.EqualsASCII(BINARY_OCTET_STREAM)) {
847 binaryChannelType = true;
850 // Channel can change our URI through redirection
851 rv = NS_GetFinalChannelURI(mChannel, getter_AddRefs(newURI));
852 if (NS_FAILED(rv)) {
853 MOZ_ASSERT_UNREACHABLE("NS_GetFinalChannelURI failure");
854 stateInvalid = true;
857 ObjectType typeHint =
858 newMime.IsEmpty() ? ObjectType::Fallback : GetTypeOfContent(newMime);
860 // In order of preference:
862 // 1) Use our type hint if it matches a plugin
863 // 2) If we have eAllowPluginSkipChannel, use the uri file extension if
864 // it matches a plugin
865 // 3) If the channel returns a binary stream type:
866 // 3a) If we have a type non-null non-document type hint, use that
867 // 3b) If the uri file extension matches a plugin type, use that
868 // 4) Use the channel type
870 bool overrideChannelType = false;
871 if (IsPluginMIME(newMime)) {
872 LOG(("OBJLC [%p]: Using plugin type hint in favor of any channel type",
873 this));
874 overrideChannelType = true;
875 } else if (binaryChannelType && typeHint != ObjectType::Fallback) {
876 if (typeHint == ObjectType::Document) {
877 if (imgLoader::SupportImageWithMimeType(newMime)) {
878 LOG(
879 ("OBJLC [%p]: Using type hint in favor of binary channel type "
880 "(Image Document)",
881 this));
882 overrideChannelType = true;
884 } else {
885 LOG(
886 ("OBJLC [%p]: Using type hint in favor of binary channel type "
887 "(Non-Image Document)",
888 this));
889 overrideChannelType = true;
893 if (overrideChannelType) {
894 // Set the type we'll use for dispatch on the channel. Otherwise we could
895 // end up trying to dispatch to a nsFrameLoader, which will complain that
896 // it couldn't find a way to handle application/octet-stream
897 nsAutoCString parsedMime, dummy;
898 NS_ParseResponseContentType(newMime, parsedMime, dummy);
899 if (!parsedMime.IsEmpty()) {
900 mChannel->SetContentType(parsedMime);
902 } else {
903 newMime = channelType;
905 } else if (newChannel) {
906 LOG(("OBJLC [%p]: We failed to open a channel, marking invalid", this));
907 stateInvalid = true;
911 /// Determine final type
913 // In order of preference:
914 // 1) If we have attempted channel load, or set stateInvalid above, the type
915 // is always null (fallback)
916 // 2) If we have a loaded channel, we grabbed its mimeType above, use that
917 // type.
918 // 3) If we have a plugin type and no URI, use that type.
919 // 4) If we have a plugin type and eAllowPluginSkipChannel, use that type.
920 // 5) if we have a URI, set type to loading to indicate we'd need a channel
921 // to proceed.
922 // 6) Otherwise, type null to indicate unloadable content (fallback)
925 ObjectType newMime_Type = GetTypeOfContent(newMime);
927 if (stateInvalid) {
928 newType = ObjectType::Fallback;
929 LOG(("OBJLC [%p]: NewType #0: %s - %u", this, newMime.get(),
930 uint32_t(newType)));
931 newMime.Truncate();
932 } else if (newChannel) {
933 // If newChannel is set above, we considered it in setting newMime
934 newType = newMime_Type;
935 LOG(("OBJLC [%p]: NewType #1: %s - %u", this, newMime.get(),
936 uint32_t(newType)));
937 LOG(("OBJLC [%p]: Using channel type", this));
938 } else if (((caps & eAllowPluginSkipChannel) || !newURI) &&
939 IsPluginMIME(newMime)) {
940 newType = newMime_Type;
941 LOG(("OBJLC [%p]: NewType #2: %s - %u", this, newMime.get(),
942 uint32_t(newType)));
943 LOG(("OBJLC [%p]: Plugin type with no URI, skipping channel load", this));
944 } else if (newURI && (mOriginalContentType.IsEmpty() ||
945 newMime_Type != ObjectType::Fallback)) {
946 // We could potentially load this if we opened a channel on mURI, indicate
947 // this by leaving type as loading.
949 // If a MIME type was requested in the tag, but we have decided to set load
950 // type to null, ignore (otherwise we'll default to document type loading).
951 newType = ObjectType::Loading;
952 LOG(("OBJLC [%p]: NewType #3: %u", this, uint32_t(newType)));
953 } else {
954 // Unloadable - no URI, and no plugin/MIME type. Non-plugin types (images,
955 // documents) always load with a channel.
956 newType = ObjectType::Fallback;
957 LOG(("OBJLC [%p]: NewType #4: %u", this, uint32_t(newType)));
960 mLoadingSyntheticDocument = newType == ObjectType::Document &&
961 imgLoader::SupportImageWithMimeType(newMime);
964 /// Handle existing channels
967 if (useChannel && newType == ObjectType::Loading) {
968 // We decided to use a channel, and also that the previous channel is still
969 // usable, so re-use the existing values.
970 newType = mType;
971 LOG(("OBJLC [%p]: NewType #5: %u", this, uint32_t(newType)));
972 newMime = mContentType;
973 newURI = mURI;
974 } else if (useChannel && !newChannel) {
975 // We have an existing channel, but did not decide to use one.
976 retval = (ParameterUpdateFlags)(retval | eParamChannelChanged);
977 useChannel = false;
981 /// Update changed values
984 if (newType != mType) {
985 retval = (ParameterUpdateFlags)(retval | eParamStateChanged);
986 LOG(("OBJLC [%p]: Type changed from %u -> %u", this, uint32_t(mType),
987 uint32_t(newType)));
988 mType = newType;
991 if (!URIEquals(mBaseURI, newBaseURI)) {
992 LOG(("OBJLC [%p]: Object effective baseURI changed", this));
993 mBaseURI = newBaseURI;
996 if (!URIEquals(newURI, mURI)) {
997 retval = (ParameterUpdateFlags)(retval | eParamStateChanged);
998 LOG(("OBJLC [%p]: Object effective URI changed", this));
999 mURI = newURI;
1002 // We don't update content type when loading, as the type is not final and we
1003 // don't want to superfluously change between mOriginalContentType ->
1004 // mContentType when doing |obj.data = obj.data| with a channel and differing
1005 // type.
1006 if (mType != ObjectType::Loading && mContentType != newMime) {
1007 retval = (ParameterUpdateFlags)(retval | eParamStateChanged);
1008 retval = (ParameterUpdateFlags)(retval | eParamContentTypeChanged);
1009 LOG(("OBJLC [%p]: Object effective mime type changed (%s -> %s)", this,
1010 mContentType.get(), newMime.get()));
1011 mContentType = newMime;
1014 // If we decided to keep using info from an old channel, but also that state
1015 // changed, we need to invalidate it.
1016 if (useChannel && !newChannel && (retval & eParamStateChanged)) {
1017 mType = ObjectType::Loading;
1018 retval = (ParameterUpdateFlags)(retval | eParamChannelChanged);
1021 return retval;
1024 // Only OnStartRequest should be passing the channel parameter
1025 nsresult nsObjectLoadingContent::LoadObject(bool aNotify, bool aForceLoad) {
1026 return LoadObject(aNotify, aForceLoad, nullptr);
1029 nsresult nsObjectLoadingContent::LoadObject(bool aNotify, bool aForceLoad,
1030 nsIRequest* aLoadingChannel) {
1031 Element* el = AsElement();
1032 Document* doc = el->OwnerDoc();
1033 nsresult rv = NS_OK;
1035 // Per bug 1318303, if the parent document is not active, load the alternative
1036 // and return.
1037 if (!doc->IsCurrentActiveDocument()) {
1038 // Since this can be triggered on change of attributes, make sure we've
1039 // unloaded whatever is loaded first.
1040 UnloadObject();
1041 ObjectType oldType = mType;
1042 mType = ObjectType::Fallback;
1043 TriggerInnerFallbackLoads();
1044 NotifyStateChanged(oldType, true);
1045 return NS_OK;
1048 // XXX(johns): In these cases, we refuse to touch our content and just
1049 // remain unloaded, as per legacy behavior. It would make more sense to
1050 // load fallback content initially and refuse to ever change state again.
1051 if (doc->IsBeingUsedAsImage()) {
1052 return NS_OK;
1055 if (doc->IsLoadedAsData() || doc->IsStaticDocument()) {
1056 return NS_OK;
1059 LOG(("OBJLC [%p]: LoadObject called, notify %u, forceload %u, channel %p",
1060 this, aNotify, aForceLoad, aLoadingChannel));
1062 // We can't re-use an already open channel, but aForceLoad may make us try
1063 // to load a plugin without any changes in channel state.
1064 if (aForceLoad && mChannelLoaded) {
1065 CloseChannel();
1066 mChannelLoaded = false;
1069 // Save these for NotifyStateChanged();
1070 ObjectType oldType = mType;
1072 ParameterUpdateFlags stateChange = UpdateObjectParameters();
1074 if (!stateChange && !aForceLoad) {
1075 return NS_OK;
1079 /// State has changed, unload existing content and attempt to load new type
1081 LOG(("OBJLC [%p]: LoadObject - plugin state changed (%u)", this,
1082 stateChange));
1084 // We synchronously start/stop plugin instances below, which may spin the
1085 // event loop. Re-entering into the load is fine, but at that point the
1086 // original load call needs to abort when unwinding
1087 // NOTE this is located *after* the state change check, a subsequent load
1088 // with no subsequently changed state will be a no-op.
1089 if (mIsLoading) {
1090 LOG(("OBJLC [%p]: Re-entering into LoadObject", this));
1092 mIsLoading = true;
1093 AutoSetLoadingToFalse reentryCheck(this);
1095 // Unload existing content, keeping in mind stopping plugins might spin the
1096 // event loop. Note that we check for still-open channels below
1097 UnloadObject(false); // Don't reset state
1098 if (!mIsLoading) {
1099 // The event loop must've spun and re-entered into LoadObject, which
1100 // finished the load
1101 LOG(("OBJLC [%p]: Re-entered into LoadObject, aborting outer load", this));
1102 return NS_OK;
1105 // Determine what's going on with our channel.
1106 if (stateChange & eParamChannelChanged) {
1107 // If the channel params changed, throw away the channel, but unset
1108 // mChannelLoaded so we'll still try to open a new one for this load if
1109 // necessary
1110 CloseChannel();
1111 mChannelLoaded = false;
1112 } else if (mType == ObjectType::Fallback && mChannel) {
1113 // If we opened a channel but then failed to find a loadable state, throw it
1114 // away. mChannelLoaded will indicate that we tried to load a channel at one
1115 // point so we wont recurse
1116 CloseChannel();
1117 } else if (mType == ObjectType::Loading && mChannel) {
1118 // We're still waiting on a channel load, already opened one, and
1119 // channel parameters didn't change
1120 return NS_OK;
1121 } else if (mChannelLoaded && mChannel != aLoadingChannel) {
1122 // The only time we should have a loaded channel with a changed state is
1123 // when the channel has just opened -- in which case this call should
1124 // have originated from OnStartRequest
1125 MOZ_ASSERT_UNREACHABLE(
1126 "Loading with a channel, but state doesn't make sense");
1127 return NS_OK;
1131 // Security checks
1134 if (mType != ObjectType::Fallback) {
1135 bool allowLoad = true;
1136 int16_t contentPolicy = nsIContentPolicy::ACCEPT;
1137 // If mChannelLoaded is set we presumably already passed load policy
1138 // If mType == ObjectType::Loading then we call OpenChannel() which
1139 // internally creates a new channel and calls asyncOpen() on that channel
1140 // which then enforces content policy checks.
1141 if (allowLoad && mURI && !mChannelLoaded && mType != ObjectType::Loading) {
1142 allowLoad = CheckLoadPolicy(&contentPolicy);
1144 // If we're loading a type now, check ProcessPolicy. Note that we may check
1145 // both now in the case of plugins whose type is determined before opening a
1146 // channel.
1147 if (allowLoad && mType != ObjectType::Loading) {
1148 allowLoad = CheckProcessPolicy(&contentPolicy);
1151 // Content policy implementations can mutate the DOM, check for re-entry
1152 if (!mIsLoading) {
1153 LOG(("OBJLC [%p]: We re-entered in content policy, leaving original load",
1154 this));
1155 return NS_OK;
1158 // Load denied, switch to null
1159 if (!allowLoad) {
1160 LOG(("OBJLC [%p]: Load denied by policy", this));
1161 mType = ObjectType::Fallback;
1165 // Don't allow view-source scheme.
1166 // view-source is the only scheme to which this applies at the moment due to
1167 // potential timing attacks to read data from cross-origin documents. If this
1168 // widens we should add a protocol flag for whether the scheme is only allowed
1169 // in top and use something like nsNetUtil::NS_URIChainHasFlags.
1170 if (mType != ObjectType::Fallback) {
1171 nsCOMPtr<nsIURI> tempURI = mURI;
1172 nsCOMPtr<nsINestedURI> nestedURI = do_QueryInterface(tempURI);
1173 while (nestedURI) {
1174 // view-source should always be an nsINestedURI, loop and check the
1175 // scheme on this and all inner URIs that are also nested URIs.
1176 if (tempURI->SchemeIs("view-source")) {
1177 LOG(("OBJLC [%p]: Blocking as effective URI has view-source scheme",
1178 this));
1179 mType = ObjectType::Fallback;
1180 break;
1183 nestedURI->GetInnerURI(getter_AddRefs(tempURI));
1184 nestedURI = do_QueryInterface(tempURI);
1188 // Items resolved as Image/Document are not candidates for content blocking,
1189 // as well as invalid plugins (they will not have the mContentType set).
1190 if (mType == ObjectType::Fallback && ShouldBlockContent()) {
1191 LOG(("OBJLC [%p]: Enable content blocking", this));
1192 mType = ObjectType::Loading;
1195 // Sanity check: We shouldn't have any loaded resources, pending events, or
1196 // a final listener at this point
1197 if (mFrameLoader || mFinalListener) {
1198 MOZ_ASSERT_UNREACHABLE("Trying to load new plugin with existing content");
1199 return NS_OK;
1202 // More sanity-checking:
1203 // If mChannel is set, mChannelLoaded should be set, and vice-versa
1204 if (mType != ObjectType::Fallback && !!mChannel != mChannelLoaded) {
1205 MOZ_ASSERT_UNREACHABLE("Trying to load with bad channel state");
1206 return NS_OK;
1210 /// Attempt to load new type
1213 // We don't set mFinalListener until OnStartRequest has been called, to
1214 // prevent re-entry ugliness with CloseChannel()
1215 nsCOMPtr<nsIStreamListener> finalListener;
1216 switch (mType) {
1217 case ObjectType::Document: {
1218 if (!mChannel) {
1219 // We could mFrameLoader->LoadURI(mURI), but UpdateObjectParameters
1220 // requires documents have a channel, so this is not a valid state.
1221 MOZ_ASSERT_UNREACHABLE(
1222 "Attempting to load a document without a "
1223 "channel");
1224 rv = NS_ERROR_FAILURE;
1225 break;
1228 nsCOMPtr<nsIDocShell> docShell = SetupDocShell(mURI);
1229 if (!docShell) {
1230 rv = NS_ERROR_FAILURE;
1231 break;
1234 // We're loading a document, so we have to set LOAD_DOCUMENT_URI
1235 // (especially important for firing onload)
1236 nsLoadFlags flags = 0;
1237 mChannel->GetLoadFlags(&flags);
1238 flags |= nsIChannel::LOAD_DOCUMENT_URI;
1239 mChannel->SetLoadFlags(flags);
1241 nsCOMPtr<nsIInterfaceRequestor> req(do_QueryInterface(docShell));
1242 NS_ASSERTION(req, "Docshell must be an ifreq");
1244 nsCOMPtr<nsIURILoader> uriLoader(components::URILoader::Service());
1245 if (NS_WARN_IF(!uriLoader)) {
1246 MOZ_ASSERT_UNREACHABLE("Failed to get uriLoader service");
1247 mFrameLoader->Destroy();
1248 mFrameLoader = nullptr;
1249 break;
1252 uint32_t uriLoaderFlags = nsDocShell::ComputeURILoaderFlags(
1253 docShell->GetBrowsingContext(), LOAD_NORMAL,
1254 /* aIsDocumentLoad */ false);
1256 rv = uriLoader->OpenChannel(mChannel, uriLoaderFlags, req,
1257 getter_AddRefs(finalListener));
1258 // finalListener will receive OnStartRequest either below, or if
1259 // `mChannel` is a `DocumentChannel`, it will be received after
1260 // RedirectToRealChannel.
1261 } break;
1262 case ObjectType::Loading:
1263 // If our type remains Loading, we need a channel to proceed
1264 rv = OpenChannel();
1265 if (NS_FAILED(rv)) {
1266 LOG(("OBJLC [%p]: OpenChannel returned failure (%" PRIu32 ")", this,
1267 static_cast<uint32_t>(rv)));
1269 break;
1270 case ObjectType::Fallback:
1271 // Handled below, silence compiler warnings
1272 break;
1276 // Loaded, handle notifications and fallback
1278 if (NS_FAILED(rv)) {
1279 // If we failed in the loading hunk above, switch to null (empty) region
1280 LOG(("OBJLC [%p]: Loading failed, switching to fallback", this));
1281 mType = ObjectType::Fallback;
1284 if (mType == ObjectType::Fallback) {
1285 LOG(("OBJLC [%p]: Switching to fallback state", this));
1286 MOZ_ASSERT(!mFrameLoader, "switched to fallback but also loaded something");
1288 MaybeFireErrorEvent();
1290 if (mChannel) {
1291 // If we were loading with a channel but then failed over, throw it away
1292 CloseChannel();
1295 // Don't try to initialize plugins or final listener below
1296 finalListener = nullptr;
1298 TriggerInnerFallbackLoads();
1301 // Notify of our final state
1302 NotifyStateChanged(oldType, aNotify);
1303 NS_ENSURE_TRUE(mIsLoading, NS_OK);
1306 // Spawning plugins and dispatching to the final listener may re-enter, so are
1307 // delayed until after we fire a notification, to prevent missing
1308 // notifications or firing them out of order.
1310 // Note that we ensured that we entered into LoadObject() from
1311 // ::OnStartRequest above when loading with a channel.
1314 rv = NS_OK;
1315 if (finalListener) {
1316 NS_ASSERTION(mType != ObjectType::Fallback && mType != ObjectType::Loading,
1317 "We should not have a final listener with a non-loaded type");
1318 mFinalListener = finalListener;
1320 // If we're a DocumentChannel load, hold off on firing the `OnStartRequest`
1321 // callback, as we haven't received it yet from our caller.
1322 RefPtr<DocumentChannel> documentChannel = do_QueryObject(mChannel);
1323 if (documentChannel) {
1324 MOZ_ASSERT(
1325 mType == ObjectType::Document,
1326 "We have a DocumentChannel here but aren't loading a document?");
1327 } else {
1328 rv = finalListener->OnStartRequest(mChannel);
1332 if ((NS_FAILED(rv) && rv != NS_ERROR_PARSED_DATA_CACHED) && mIsLoading) {
1333 // Since we've already notified of our transition, we can just Unload and
1334 // call ConfigureFallback (which will notify again)
1335 oldType = mType;
1336 mType = ObjectType::Fallback;
1337 UnloadObject(false);
1338 NS_ENSURE_TRUE(mIsLoading, NS_OK);
1339 CloseChannel();
1340 TriggerInnerFallbackLoads();
1341 NotifyStateChanged(oldType, true);
1344 return NS_OK;
1347 // This call can re-enter when dealing with plugin listeners
1348 nsresult nsObjectLoadingContent::CloseChannel() {
1349 if (mChannel) {
1350 LOG(("OBJLC [%p]: Closing channel\n", this));
1351 // Null the values before potentially-reentering, and ensure they survive
1352 // the call
1353 nsCOMPtr<nsIChannel> channelGrip(mChannel);
1354 nsCOMPtr<nsIStreamListener> listenerGrip(mFinalListener);
1355 mChannel = nullptr;
1356 mFinalListener = nullptr;
1357 channelGrip->CancelWithReason(NS_BINDING_ABORTED,
1358 "nsObjectLoadingContent::CloseChannel"_ns);
1359 if (listenerGrip) {
1360 // mFinalListener is only set by LoadObject after OnStartRequest, or
1361 // by OnStartRequest in the case of late-opened plugin streams
1362 listenerGrip->OnStopRequest(channelGrip, NS_BINDING_ABORTED);
1365 return NS_OK;
1368 bool nsObjectLoadingContent::IsAboutBlankLoadOntoInitialAboutBlank(
1369 nsIURI* aURI, bool aInheritPrincipal, nsIPrincipal* aPrincipalToInherit) {
1370 return NS_IsAboutBlank(aURI) && aInheritPrincipal &&
1371 (!mFrameLoader || !mFrameLoader->GetExistingDocShell() ||
1372 mFrameLoader->GetExistingDocShell()
1373 ->IsAboutBlankLoadOntoInitialAboutBlank(aURI, aInheritPrincipal,
1374 aPrincipalToInherit));
1377 nsresult nsObjectLoadingContent::OpenChannel() {
1378 Element* el = AsElement();
1379 Document* doc = el->OwnerDoc();
1380 NS_ASSERTION(doc, "No owner document?");
1382 nsresult rv;
1383 mChannel = nullptr;
1385 // E.g. mms://
1386 if (!mURI || !CanHandleURI(mURI)) {
1387 return NS_ERROR_NOT_AVAILABLE;
1390 nsCOMPtr<nsILoadGroup> group = doc->GetDocumentLoadGroup();
1391 nsCOMPtr<nsIChannel> chan;
1392 RefPtr<ObjectInterfaceRequestorShim> shim =
1393 new ObjectInterfaceRequestorShim(this);
1395 bool inheritAttrs = nsContentUtils::ChannelShouldInheritPrincipal(
1396 el->NodePrincipal(), // aLoadState->PrincipalToInherit()
1397 mURI, // aLoadState->URI()
1398 true, // aInheritForAboutBlank
1399 false); // aForceInherit
1401 bool inheritPrincipal = inheritAttrs && !SchemeIsData(mURI);
1403 nsSecurityFlags securityFlags =
1404 nsILoadInfo::SEC_ALLOW_CROSS_ORIGIN_SEC_CONTEXT_IS_NULL;
1405 if (inheritPrincipal) {
1406 securityFlags |= nsILoadInfo::SEC_FORCE_INHERIT_PRINCIPAL;
1409 nsContentPolicyType contentPolicyType = GetContentPolicyType();
1410 // The setting of LOAD_BYPASS_SERVICE_WORKER here is now an optimization.
1411 // ServiceWorkerInterceptController::ShouldPrepareForIntercept does a more
1412 // expensive check of BrowsingContext ancestors to look for object/embed.
1413 nsLoadFlags loadFlags = nsIChannel::LOAD_CALL_CONTENT_SNIFFERS |
1414 nsIChannel::LOAD_BYPASS_SERVICE_WORKER |
1415 nsIRequest::LOAD_HTML_OBJECT_DATA;
1416 uint32_t sandboxFlags = doc->GetSandboxFlags();
1418 // For object loads we store the CSP that potentially needs to
1419 // be inherited, e.g. in case we are loading an opaque origin
1420 // like a data: URI. The actual inheritance check happens within
1421 // Document::InitCSP(). Please create an actual copy of the CSP
1422 // (do not share the same reference) otherwise a Meta CSP of an
1423 // opaque origin will incorrectly be propagated to the embedding
1424 // document.
1425 RefPtr<nsCSPContext> cspToInherit;
1426 if (nsCOMPtr<nsIContentSecurityPolicy> csp = doc->GetCsp()) {
1427 cspToInherit = new nsCSPContext();
1428 cspToInherit->InitFromOther(static_cast<nsCSPContext*>(csp.get()));
1431 // --- Create LoadInfo
1432 RefPtr<LoadInfo> loadInfo = new LoadInfo(
1433 /*aLoadingPrincipal = aLoadingContext->NodePrincipal() */ nullptr,
1434 /*aTriggeringPrincipal = aLoadingPrincipal */ nullptr,
1435 /*aLoadingContext = */ el,
1436 /*aSecurityFlags = */ securityFlags,
1437 /*aContentPolicyType = */ contentPolicyType,
1438 /*aLoadingClientInfo = */ Nothing(),
1439 /*aController = */ Nothing(),
1440 /*aSandboxFlags = */ sandboxFlags);
1442 if (inheritAttrs) {
1443 loadInfo->SetPrincipalToInherit(el->NodePrincipal());
1446 if (cspToInherit) {
1447 loadInfo->SetCSPToInherit(cspToInherit);
1450 if (DocumentChannel::CanUseDocumentChannel(mURI) &&
1451 !IsAboutBlankLoadOntoInitialAboutBlank(mURI, inheritPrincipal,
1452 el->NodePrincipal())) {
1453 // --- Create LoadState
1454 RefPtr<nsDocShellLoadState> loadState = new nsDocShellLoadState(mURI);
1455 loadState->SetPrincipalToInherit(el->NodePrincipal());
1456 loadState->SetTriggeringPrincipal(loadInfo->TriggeringPrincipal());
1457 if (cspToInherit) {
1458 loadState->SetCsp(cspToInherit);
1460 loadState->SetTriggeringSandboxFlags(sandboxFlags);
1462 // TODO(djg): This was httpChan->SetReferrerInfoWithoutClone(referrerInfo);
1463 // Is the ...WithoutClone(...) important?
1464 auto referrerInfo = MakeRefPtr<ReferrerInfo>(*doc);
1465 loadState->SetReferrerInfo(referrerInfo);
1467 chan =
1468 DocumentChannel::CreateForObject(loadState, loadInfo, loadFlags, shim);
1469 MOZ_ASSERT(chan);
1470 // NS_NewChannel sets the group on the channel. CreateDocumentChannel does
1471 // not.
1472 chan->SetLoadGroup(group);
1473 } else {
1474 rv = NS_NewChannelInternal(getter_AddRefs(chan), // outChannel
1475 mURI, // aUri
1476 loadInfo, // aLoadInfo
1477 nullptr, // aPerformanceStorage
1478 group, // aLoadGroup
1479 shim, // aCallbacks
1480 loadFlags, // aLoadFlags
1481 nullptr); // aIoService
1482 NS_ENSURE_SUCCESS(rv, rv);
1484 if (inheritAttrs) {
1485 nsCOMPtr<nsILoadInfo> loadinfo = chan->LoadInfo();
1486 loadinfo->SetPrincipalToInherit(el->NodePrincipal());
1489 // For object loads we store the CSP that potentially needs to
1490 // be inherited, e.g. in case we are loading an opaque origin
1491 // like a data: URI. The actual inheritance check happens within
1492 // Document::InitCSP(). Please create an actual copy of the CSP
1493 // (do not share the same reference) otherwise a Meta CSP of an
1494 // opaque origin will incorrectly be propagated to the embedding
1495 // document.
1496 if (cspToInherit) {
1497 nsCOMPtr<nsILoadInfo> loadinfo = chan->LoadInfo();
1498 static_cast<LoadInfo*>(loadinfo.get())->SetCSPToInherit(cspToInherit);
1502 // Referrer
1503 nsCOMPtr<nsIHttpChannel> httpChan(do_QueryInterface(chan));
1504 if (httpChan) {
1505 auto referrerInfo = MakeRefPtr<ReferrerInfo>(*doc);
1507 rv = httpChan->SetReferrerInfoWithoutClone(referrerInfo);
1508 MOZ_ASSERT(NS_SUCCEEDED(rv));
1510 // Set the initiator type
1511 nsCOMPtr<nsITimedChannel> timedChannel(do_QueryInterface(httpChan));
1512 if (timedChannel) {
1513 timedChannel->SetInitiatorType(el->LocalName());
1516 nsCOMPtr<nsIClassOfService> cos(do_QueryInterface(httpChan));
1517 if (cos && UserActivation::IsHandlingUserInput()) {
1518 cos->AddClassFlags(nsIClassOfService::UrgentStart);
1522 nsCOMPtr<nsIScriptChannel> scriptChannel = do_QueryInterface(chan);
1523 if (scriptChannel) {
1524 // Allow execution against our context if the principals match
1525 scriptChannel->SetExecutionPolicy(nsIScriptChannel::EXECUTE_NORMAL);
1528 // AsyncOpen can fail if a file does not exist.
1529 rv = chan->AsyncOpen(shim);
1530 NS_ENSURE_SUCCESS(rv, rv);
1531 LOG(("OBJLC [%p]: Channel opened", this));
1532 mChannel = chan;
1533 return NS_OK;
1536 uint32_t nsObjectLoadingContent::GetCapabilities() const {
1537 return eSupportImages | eSupportDocuments;
1540 void nsObjectLoadingContent::Destroy() {
1541 if (mFrameLoader) {
1542 mFrameLoader->Destroy();
1543 mFrameLoader = nullptr;
1546 // Reset state so that if the element is re-appended to tree again (e.g.
1547 // adopting to another document), it will reload resource again.
1548 UnloadObject();
1551 /* static */
1552 void nsObjectLoadingContent::Traverse(nsObjectLoadingContent* tmp,
1553 nsCycleCollectionTraversalCallback& cb) {
1554 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mFrameLoader);
1557 /* static */
1558 void nsObjectLoadingContent::Unlink(nsObjectLoadingContent* tmp) {
1559 if (tmp->mFrameLoader) {
1560 tmp->mFrameLoader->Destroy();
1562 NS_IMPL_CYCLE_COLLECTION_UNLINK(mFrameLoader);
1565 void nsObjectLoadingContent::UnloadObject(bool aResetState) {
1566 if (mFrameLoader) {
1567 mFrameLoader->Destroy();
1568 mFrameLoader = nullptr;
1571 if (aResetState) {
1572 CloseChannel();
1573 mChannelLoaded = false;
1574 mType = ObjectType::Loading;
1575 mURI = mOriginalURI = mBaseURI = nullptr;
1576 mContentType.Truncate();
1577 mOriginalContentType.Truncate();
1580 mScriptRequested = false;
1582 mIsStopping = false;
1584 mSubdocumentIntrinsicSize.reset();
1585 mSubdocumentIntrinsicRatio.reset();
1588 void nsObjectLoadingContent::NotifyStateChanged(ObjectType aOldType,
1589 bool aNotify) {
1590 LOG(("OBJLC [%p]: NotifyStateChanged: (%u) -> (%u) (notify %i)", this,
1591 uint32_t(aOldType), uint32_t(mType), aNotify));
1593 dom::Element* thisEl = AsElement();
1594 // Non-images are always not broken.
1595 // XXX: I assume we could just remove this completely?
1596 thisEl->RemoveStates(ElementState::BROKEN, aNotify);
1598 if (mType == aOldType) {
1599 return;
1602 Document* doc = thisEl->GetComposedDoc();
1603 if (!doc) {
1604 return; // Nothing to do
1607 PresShell* presShell = doc->GetPresShell();
1608 // If there is no PresShell or it hasn't been initialized there isn't much to
1609 // do.
1610 if (!presShell || !presShell->DidInitialize()) {
1611 return;
1613 presShell->PostRecreateFramesFor(thisEl);
1616 nsObjectLoadingContent::ObjectType nsObjectLoadingContent::GetTypeOfContent(
1617 const nsCString& aMIMEType) {
1618 Element* el = AsElement();
1619 NS_ASSERTION(el, "must be a content");
1621 // Images and documents are always supported.
1622 MOZ_ASSERT((GetCapabilities() & (eSupportImages | eSupportDocuments)) ==
1623 (eSupportImages | eSupportDocuments));
1625 LOG(
1626 ("OBJLC [%p]: calling HtmlObjectContentTypeForMIMEType: aMIMEType: %s - "
1627 "el: %p\n",
1628 this, aMIMEType.get(), el));
1629 auto ret = static_cast<ObjectType>(
1630 nsContentUtils::HtmlObjectContentTypeForMIMEType(aMIMEType));
1631 LOG(("OBJLC [%p]: called HtmlObjectContentTypeForMIMEType\n", this));
1632 return ret;
1635 void nsObjectLoadingContent::CreateStaticClone(
1636 nsObjectLoadingContent* aDest) const {
1637 MOZ_ASSERT(aDest->AsElement()->OwnerDoc()->IsStaticDocument());
1638 aDest->mType = mType;
1640 if (mFrameLoader) {
1641 aDest->AsElement()->OwnerDoc()->AddPendingFrameStaticClone(aDest,
1642 mFrameLoader);
1646 NS_IMETHODIMP
1647 nsObjectLoadingContent::GetSrcURI(nsIURI** aURI) {
1648 NS_IF_ADDREF(*aURI = GetSrcURI());
1649 return NS_OK;
1652 void nsObjectLoadingContent::TriggerInnerFallbackLoads() {
1653 MOZ_ASSERT(!mFrameLoader && !mChannel,
1654 "ConfigureFallback called with loaded content");
1655 MOZ_ASSERT(mType == ObjectType::Fallback);
1657 Element* el = AsElement();
1658 if (!el->IsHTMLElement(nsGkAtoms::object)) {
1659 return;
1661 // Do a depth-first traverse of node tree with the current element as root,
1662 // looking for non-<param> elements. If we find some then we have an HTML
1663 // fallback for this element.
1664 for (nsIContent* child = el->GetFirstChild(); child;) {
1665 // <object> and <embed> elements in the fallback need to StartObjectLoad.
1666 // Their children should be ignored since they are part of those element's
1667 // fallback.
1668 if (auto* embed = HTMLEmbedElement::FromNode(child)) {
1669 embed->StartObjectLoad(true, true);
1670 // Skip the children
1671 child = child->GetNextNonChildNode(el);
1672 } else if (auto* object = HTMLObjectElement::FromNode(child)) {
1673 object->StartObjectLoad(true, true);
1674 // Skip the children
1675 child = child->GetNextNonChildNode(el);
1676 } else {
1677 child = child->GetNextNode(el);
1682 NS_IMETHODIMP
1683 nsObjectLoadingContent::UpgradeLoadToDocument(
1684 nsIChannel* aRequest, BrowsingContext** aBrowsingContext) {
1685 AUTO_PROFILER_LABEL("nsObjectLoadingContent::UpgradeLoadToDocument", NETWORK);
1687 LOG(("OBJLC [%p]: UpgradeLoadToDocument", this));
1689 if (aRequest != mChannel || !aRequest) {
1690 // happens when a new load starts before the previous one got here.
1691 return NS_BINDING_ABORTED;
1694 // We should be state loading.
1695 if (mType != ObjectType::Loading) {
1696 MOZ_ASSERT_UNREACHABLE("Should be type loading at this point");
1697 return NS_BINDING_ABORTED;
1699 MOZ_ASSERT(!mChannelLoaded, "mChannelLoaded set already?");
1700 MOZ_ASSERT(!mFinalListener, "mFinalListener exists already?");
1702 mChannelLoaded = true;
1704 // We don't need to check for errors here, unlike in `OnStartRequest`, as
1705 // `UpgradeLoadToDocument` is only called when the load is going to become a
1706 // process-switching load. As we never process switch for failed object loads,
1707 // we know our channel status is successful.
1709 // Call `LoadObject` to trigger our nsObjectLoadingContext to switch into the
1710 // specified new state.
1711 nsresult rv = LoadObject(true, false, aRequest);
1712 if (NS_WARN_IF(NS_FAILED(rv))) {
1713 return rv;
1716 RefPtr<BrowsingContext> bc = GetBrowsingContext();
1717 if (!bc) {
1718 return NS_ERROR_FAILURE;
1721 bc.forget(aBrowsingContext);
1722 return NS_OK;
1725 bool nsObjectLoadingContent::ShouldBlockContent() {
1726 return mContentBlockingEnabled && mURI && IsFlashMIME(mContentType) &&
1727 StaticPrefs::browser_safebrowsing_blockedURIs_enabled();
1730 Document* nsObjectLoadingContent::GetContentDocument(
1731 nsIPrincipal& aSubjectPrincipal) {
1732 Element* el = AsElement();
1733 if (!el->IsInComposedDoc()) {
1734 return nullptr;
1737 Document* sub_doc = el->OwnerDoc()->GetSubDocumentFor(el);
1738 if (!sub_doc) {
1739 return nullptr;
1742 // Return null for cross-origin contentDocument.
1743 if (!aSubjectPrincipal.SubsumesConsideringDomain(sub_doc->NodePrincipal())) {
1744 return nullptr;
1747 return sub_doc;
1750 void nsObjectLoadingContent::MaybeFireErrorEvent() {
1751 Element* el = AsElement();
1752 // Queue a task to fire an error event if we're an <object> element. The
1753 // queueing is important, since then we don't have to worry about reentry.
1754 if (el->IsHTMLElement(nsGkAtoms::object)) {
1755 RefPtr<AsyncEventDispatcher> loadBlockingAsyncDispatcher =
1756 new LoadBlockingAsyncEventDispatcher(el, u"error"_ns, CanBubble::eNo,
1757 ChromeOnlyDispatch::eNo);
1758 loadBlockingAsyncDispatcher->PostDOMEvent();
1762 bool nsObjectLoadingContent::BlockEmbedOrObjectContentLoading() {
1763 Element* el = AsElement();
1765 // Traverse up the node tree to see if we have any ancestors that may block us
1766 // from loading
1767 for (nsIContent* parent = el->GetParent(); parent;
1768 parent = parent->GetParent()) {
1769 if (parent->IsAnyOfHTMLElements(nsGkAtoms::video, nsGkAtoms::audio)) {
1770 return true;
1772 // If we have an ancestor that is an object with a source, it'll have an
1773 // associated displayed type. If that type is not null, don't load content
1774 // for the embed.
1775 if (auto* object = HTMLObjectElement::FromNode(parent)) {
1776 if (object->Type() != ObjectType::Fallback) {
1777 return true;
1781 return false;
1784 void nsObjectLoadingContent::SubdocumentIntrinsicSizeOrRatioChanged(
1785 const Maybe<IntrinsicSize>& aIntrinsicSize,
1786 const Maybe<AspectRatio>& aIntrinsicRatio) {
1787 if (aIntrinsicSize == mSubdocumentIntrinsicSize &&
1788 aIntrinsicRatio == mSubdocumentIntrinsicRatio) {
1789 return;
1792 mSubdocumentIntrinsicSize = aIntrinsicSize;
1793 mSubdocumentIntrinsicRatio = aIntrinsicRatio;
1795 if (nsSubDocumentFrame* sdf = do_QueryFrame(AsElement()->GetPrimaryFrame())) {
1796 sdf->SubdocumentIntrinsicSizeOrRatioChanged();
1800 void nsObjectLoadingContent::SubdocumentImageLoadComplete(nsresult aResult) {
1801 ObjectType oldType = mType;
1802 mLoadingSyntheticDocument = false;
1804 if (NS_FAILED(aResult)) {
1805 UnloadObject();
1806 mType = ObjectType::Fallback;
1807 TriggerInnerFallbackLoads();
1808 NotifyStateChanged(oldType, true);
1809 return;
1812 // (mChannelLoaded && mChannel) indicates this is a good state, not any sort
1813 // of failures.
1814 MOZ_DIAGNOSTIC_ASSERT_IF(mChannelLoaded && mChannel,
1815 mType == ObjectType::Document);
1816 NotifyStateChanged(oldType, true);
1819 void nsObjectLoadingContent::MaybeStoreCrossOriginFeaturePolicy() {
1820 MOZ_DIAGNOSTIC_ASSERT(mFrameLoader);
1822 // If the browsingContext is not ready (because docshell is dead), don't try
1823 // to create one.
1824 if (!mFrameLoader->IsRemoteFrame() && !mFrameLoader->GetExistingDocShell()) {
1825 return;
1828 RefPtr<BrowsingContext> browsingContext = mFrameLoader->GetBrowsingContext();
1830 if (!browsingContext || !browsingContext->IsContentSubframe()) {
1831 return;
1834 Element* el = AsElement();
1835 if (!el->IsInComposedDoc()) {
1836 return;
1839 FeaturePolicy* featurePolicy = el->OwnerDoc()->FeaturePolicy();
1841 if (ContentChild* cc = ContentChild::GetSingleton()) {
1842 Unused << cc->SendSetContainerFeaturePolicy(browsingContext, featurePolicy);