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