Bug 1860712 [wpt PR 42705] - Port popover test to WPT and adjust for close requests...
[gecko.git] / dom / base / nsObjectLoadingContent.cpp
blobe1879d701c43c2cebecd56af6d5d1a1faa50bd82
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 "nsIContent.h"
17 #include "nsIContentInlines.h"
18 #include "nsIDocShell.h"
19 #include "mozilla/BasePrincipal.h"
20 #include "mozilla/dom/BindContext.h"
21 #include "mozilla/dom/Document.h"
22 #include "nsIExternalProtocolHandler.h"
23 #include "nsIInterfaceRequestorUtils.h"
24 #include "nsIOService.h"
25 #include "nsIPermissionManager.h"
26 #include "nsPluginHost.h"
27 #include "nsIHttpChannel.h"
28 #include "nsINestedURI.h"
29 #include "nsScriptSecurityManager.h"
30 #include "nsIURILoader.h"
31 #include "nsIURL.h"
32 #include "nsIScriptChannel.h"
33 #include "nsIBlocklistService.h"
34 #include "nsIAsyncVerifyRedirectCallback.h"
35 #include "nsIAppShell.h"
36 #include "nsIXULRuntime.h"
37 #include "nsIScriptError.h"
38 #include "nsSubDocumentFrame.h"
40 #include "nsError.h"
42 // Util headers
43 #include "prenv.h"
44 #include "mozilla/Logging.h"
46 #include "nsCURILoader.h"
47 #include "nsContentPolicyUtils.h"
48 #include "nsContentUtils.h"
49 #include "nsDocShellCID.h"
50 #include "nsDocShellLoadState.h"
51 #include "nsGkAtoms.h"
52 #include "nsThreadUtils.h"
53 #include "nsNetUtil.h"
54 #include "nsMimeTypes.h"
55 #include "nsStyleUtil.h"
56 #include "nsUnicharUtils.h"
57 #include "mozilla/Preferences.h"
58 #include "nsSandboxFlags.h"
59 #include "nsQueryObject.h"
61 // Concrete classes
62 #include "nsFrameLoader.h"
64 #include "nsObjectLoadingContent.h"
65 #include "mozAutoDocUpdate.h"
66 #include "nsWrapperCacheInlines.h"
67 #include "nsDOMJSUtils.h"
68 #include "js/Object.h" // JS::GetClass
70 #include "nsWidgetsCID.h"
71 #include "nsContentCID.h"
72 #include "mozilla/BasicEvents.h"
73 #include "mozilla/Components.h"
74 #include "mozilla/LoadInfo.h"
75 #include "mozilla/dom/BindingUtils.h"
76 #include "mozilla/dom/Element.h"
77 #include "mozilla/dom/Event.h"
78 #include "mozilla/dom/ScriptSettings.h"
79 #include "mozilla/dom/PluginCrashedEvent.h"
80 #include "mozilla/AsyncEventDispatcher.h"
81 #include "mozilla/EventDispatcher.h"
82 #include "mozilla/IMEStateManager.h"
83 #include "mozilla/widget/IMEData.h"
84 #include "mozilla/IntegerPrintfMacros.h"
85 #include "mozilla/dom/ContentChild.h"
86 #include "mozilla/dom/HTMLObjectElementBinding.h"
87 #include "mozilla/dom/HTMLEmbedElement.h"
88 #include "mozilla/dom/HTMLObjectElement.h"
89 #include "mozilla/dom/UserActivation.h"
90 #include "mozilla/dom/nsCSPContext.h"
91 #include "mozilla/net/DocumentChannel.h"
92 #include "mozilla/net/UrlClassifierFeatureFactory.h"
93 #include "mozilla/PresShell.h"
94 #include "mozilla/ProfilerLabels.h"
95 #include "mozilla/StaticPrefs_browser.h"
96 #include "mozilla/StaticPrefs_security.h"
97 #include "nsChannelClassifier.h"
98 #include "nsFocusManager.h"
99 #include "ReferrerInfo.h"
100 #include "nsIEffectiveTLDService.h"
102 #ifdef XP_WIN
103 // Thanks so much, Microsoft! :(
104 # ifdef CreateEvent
105 # undef CreateEvent
106 # endif
107 #endif // XP_WIN
109 static const char kPrefYoutubeRewrite[] = "plugins.rewrite_youtube_embeds";
111 using namespace mozilla;
112 using namespace mozilla::dom;
113 using namespace mozilla::net;
115 static LogModule* GetObjectLog() {
116 static LazyLogModule sLog("objlc");
117 return sLog;
120 #define LOG(args) MOZ_LOG(GetObjectLog(), mozilla::LogLevel::Debug, args)
121 #define LOG_ENABLED() MOZ_LOG_TEST(GetObjectLog(), mozilla::LogLevel::Debug)
123 static bool IsFlashMIME(const nsACString& aMIMEType) {
124 return nsPluginHost::GetSpecialType(aMIMEType) ==
125 nsPluginHost::eSpecialType_Flash;
128 static bool IsPluginType(nsObjectLoadingContent::ObjectType type) {
129 return type == nsObjectLoadingContent::eType_Fallback ||
130 type == nsObjectLoadingContent::eType_FakePlugin;
134 /// Runnables and helper classes
137 // Sets a object's mIsLoading bit to false when destroyed
138 class AutoSetLoadingToFalse {
139 public:
140 explicit AutoSetLoadingToFalse(nsObjectLoadingContent* aContent)
141 : mContent(aContent) {}
142 ~AutoSetLoadingToFalse() { mContent->mIsLoading = false; }
144 private:
145 nsObjectLoadingContent* mContent;
149 /// Helper functions
152 bool nsObjectLoadingContent::IsSuccessfulRequest(nsIRequest* aRequest,
153 nsresult* aStatus) {
154 nsresult rv = aRequest->GetStatus(aStatus);
155 if (NS_FAILED(rv) || NS_FAILED(*aStatus)) {
156 return false;
159 // This may still be an error page or somesuch
160 nsCOMPtr<nsIHttpChannel> httpChan(do_QueryInterface(aRequest));
161 if (httpChan) {
162 bool success;
163 rv = httpChan->GetRequestSucceeded(&success);
164 if (NS_FAILED(rv) || !success) {
165 return false;
169 // Otherwise, the request is successful
170 return true;
173 static bool CanHandleURI(nsIURI* aURI) {
174 nsAutoCString scheme;
175 if (NS_FAILED(aURI->GetScheme(scheme))) {
176 return false;
179 nsCOMPtr<nsIIOService> ios = mozilla::components::IO::Service();
180 if (!ios) {
181 return false;
184 nsCOMPtr<nsIProtocolHandler> handler;
185 ios->GetProtocolHandler(scheme.get(), getter_AddRefs(handler));
186 if (!handler) {
187 return false;
190 nsCOMPtr<nsIExternalProtocolHandler> extHandler = do_QueryInterface(handler);
191 // We can handle this URI if its protocol handler is not the external one
192 return extHandler == nullptr;
195 // Helper for tedious URI equality syntax when one or both arguments may be
196 // null and URIEquals(null, null) should be true
197 static bool inline URIEquals(nsIURI* a, nsIURI* b) {
198 bool equal;
199 return (!a && !b) || (a && b && NS_SUCCEEDED(a->Equals(b, &equal)) && equal);
203 /// Member Functions
206 // Helper to spawn the frameloader.
207 void nsObjectLoadingContent::SetupFrameLoader(int32_t aJSPluginId) {
208 nsCOMPtr<nsIContent> thisContent =
209 do_QueryInterface(static_cast<nsIImageLoadingContent*>(this));
210 NS_ASSERTION(thisContent, "must be a content");
212 mFrameLoader =
213 nsFrameLoader::Create(thisContent->AsElement(), mNetworkCreated);
214 MOZ_ASSERT(mFrameLoader, "nsFrameLoader::Create failed");
217 // Helper to spawn the frameloader and return a pointer to its docshell.
218 already_AddRefed<nsIDocShell> nsObjectLoadingContent::SetupDocShell(
219 nsIURI* aRecursionCheckURI) {
220 SetupFrameLoader(nsFakePluginTag::NOT_JSPLUGIN);
221 if (!mFrameLoader) {
222 return nullptr;
225 nsCOMPtr<nsIDocShell> docShell;
227 if (aRecursionCheckURI) {
228 nsresult rv = mFrameLoader->CheckForRecursiveLoad(aRecursionCheckURI);
229 if (NS_SUCCEEDED(rv)) {
230 IgnoredErrorResult result;
231 docShell = mFrameLoader->GetDocShell(result);
232 if (result.Failed()) {
233 MOZ_ASSERT_UNREACHABLE("Could not get DocShell from mFrameLoader?");
235 } else {
236 LOG(("OBJLC [%p]: Aborting recursive load", this));
240 if (!docShell) {
241 mFrameLoader->Destroy();
242 mFrameLoader = nullptr;
243 return nullptr;
246 MaybeStoreCrossOriginFeaturePolicy();
248 return docShell.forget();
251 void nsObjectLoadingContent::UnbindFromTree(bool aNullParent) {
252 nsImageLoadingContent::UnbindFromTree(aNullParent);
254 if (mType != eType_Image) {
255 // nsImageLoadingContent handles the image case.
256 // Reset state and clear pending events
257 /// XXX(johns): The implementation for GenericFrame notes that ideally we
258 /// would keep the docshell around, but trash the frameloader
259 UnloadObject();
263 nsObjectLoadingContent::nsObjectLoadingContent()
264 : mType(eType_Loading),
265 mRunID(0),
266 mHasRunID(false),
267 mChannelLoaded(false),
268 mNetworkCreated(true),
269 mContentBlockingEnabled(false),
270 mSkipFakePlugins(false),
271 mIsStopping(false),
272 mIsLoading(false),
273 mScriptRequested(false),
274 mRewrittenYoutubeEmbed(false),
275 mLoadingSyntheticDocument(false) {}
277 nsObjectLoadingContent::~nsObjectLoadingContent() {
278 // Should have been unbound from the tree at this point, and
279 // CheckPluginStopEvent keeps us alive
280 if (mFrameLoader) {
281 MOZ_ASSERT_UNREACHABLE(
282 "Should not be tearing down frame loaders at this point");
283 mFrameLoader->Destroy();
286 nsImageLoadingContent::Destroy();
289 void nsObjectLoadingContent::GetPluginAttributes(
290 nsTArray<MozPluginParameter>& aAttributes) {
291 aAttributes = mCachedAttributes.Clone();
294 void nsObjectLoadingContent::GetPluginParameters(
295 nsTArray<MozPluginParameter>& aParameters) {
296 aParameters = mCachedParameters.Clone();
299 void nsObjectLoadingContent::GetNestedParams(
300 nsTArray<MozPluginParameter>& aParameters) {
301 nsCOMPtr<Element> ourElement =
302 do_QueryInterface(static_cast<nsIObjectLoadingContent*>(this));
304 nsCOMPtr<nsIHTMLCollection> allParams;
305 constexpr auto xhtml_ns = u"http://www.w3.org/1999/xhtml"_ns;
306 ErrorResult rv;
307 allParams = ourElement->GetElementsByTagNameNS(xhtml_ns, u"param"_ns, rv);
308 if (rv.Failed()) {
309 return;
311 MOZ_ASSERT(allParams);
313 uint32_t numAllParams = allParams->Length();
314 for (uint32_t i = 0; i < numAllParams; i++) {
315 RefPtr<Element> element = allParams->Item(i);
317 nsAutoString name;
318 element->GetAttr(nsGkAtoms::name, name);
320 if (name.IsEmpty()) continue;
322 nsCOMPtr<nsIContent> parent = element->GetParent();
323 RefPtr<HTMLObjectElement> objectElement;
324 while (!objectElement && parent) {
325 objectElement = HTMLObjectElement::FromNode(parent);
326 parent = parent->GetParent();
329 if (objectElement) {
330 parent = objectElement;
331 } else {
332 continue;
335 if (parent == ourElement) {
336 MozPluginParameter param;
337 element->GetAttr(nsGkAtoms::name, param.mName);
338 element->GetAttr(nsGkAtoms::value, param.mValue);
340 param.mName.Trim(" \n\r\t\b", true, true, false);
341 param.mValue.Trim(" \n\r\t\b", true, true, false);
343 aParameters.AppendElement(param);
348 nsresult nsObjectLoadingContent::BuildParametersArray() {
349 if (mCachedAttributes.Length() || mCachedParameters.Length()) {
350 MOZ_ASSERT(false, "Parameters array should be empty.");
351 return NS_OK;
354 nsCOMPtr<Element> element =
355 do_QueryInterface(static_cast<nsIImageLoadingContent*>(this));
357 for (uint32_t i = 0; i != element->GetAttrCount(); i += 1) {
358 MozPluginParameter param;
359 const nsAttrName* attrName = element->GetAttrNameAt(i);
360 nsAtom* atom = attrName->LocalName();
361 element->GetAttr(attrName->NamespaceID(), atom, param.mValue);
362 atom->ToString(param.mName);
363 mCachedAttributes.AppendElement(param);
366 nsAutoCString wmodeOverride;
367 Preferences::GetCString("plugins.force.wmode", wmodeOverride);
369 for (uint32_t i = 0; i < mCachedAttributes.Length(); i++) {
370 if (!wmodeOverride.IsEmpty() &&
371 mCachedAttributes[i].mName.EqualsIgnoreCase("wmode")) {
372 CopyASCIItoUTF16(wmodeOverride, mCachedAttributes[i].mValue);
373 wmodeOverride.Truncate();
377 if (!wmodeOverride.IsEmpty()) {
378 MozPluginParameter param;
379 param.mName = u"wmode"_ns;
380 CopyASCIItoUTF16(wmodeOverride, param.mValue);
381 mCachedAttributes.AppendElement(param);
384 // Some plugins were never written to understand the "data" attribute of the
385 // OBJECT tag. Real and WMP will not play unless they find a "src" attribute,
386 // see bug 152334. Nav 4.x would simply replace the "data" with "src". Because
387 // some plugins correctly look for "data", lets instead copy the "data"
388 // attribute and add another entry to the bottom of the array if there isn't
389 // already a "src" specified.
390 if (element->IsHTMLElement(nsGkAtoms::object) &&
391 !element->HasAttr(nsGkAtoms::src)) {
392 MozPluginParameter param;
393 element->GetAttr(nsGkAtoms::data, param.mValue);
394 if (!param.mValue.IsEmpty()) {
395 param.mName = u"SRC"_ns;
396 mCachedAttributes.AppendElement(param);
400 GetNestedParams(mCachedParameters);
402 return NS_OK;
405 void nsObjectLoadingContent::NotifyOwnerDocumentActivityChanged() {
406 // XXX(johns): We cannot touch plugins or run arbitrary script from this call,
407 // as Document is in a non-reentrant state.
409 nsImageLoadingContent::NotifyOwnerDocumentActivityChanged();
412 // nsIRequestObserver
413 NS_IMETHODIMP
414 nsObjectLoadingContent::OnStartRequest(nsIRequest* aRequest) {
415 AUTO_PROFILER_LABEL("nsObjectLoadingContent::OnStartRequest", NETWORK);
417 LOG(("OBJLC [%p]: Channel OnStartRequest", this));
419 if (aRequest != mChannel || !aRequest) {
420 // happens when a new load starts before the previous one got here
421 return NS_BINDING_ABORTED;
424 nsCOMPtr<nsIChannel> chan(do_QueryInterface(aRequest));
425 NS_ASSERTION(chan, "Why is our request not a channel?");
427 nsresult status = NS_OK;
428 bool success = IsSuccessfulRequest(aRequest, &status);
430 // If we have already switched to type document, we're doing a
431 // process-switching DocumentChannel load. We should be able to pass down the
432 // load to our inner listener, but should also make sure to update our local
433 // state.
434 if (mType == eType_Document) {
435 if (!mFinalListener) {
436 MOZ_ASSERT_UNREACHABLE(
437 "Already are eType_Document, but don't have final listener yet?");
438 return NS_BINDING_ABORTED;
441 // If the load looks successful, fix up some of our local state before
442 // forwarding the request to the final URI loader.
444 // Forward load errors down to the document loader, so we don't tear down
445 // the nsDocShell ourselves.
446 if (success) {
447 LOG(("OBJLC [%p]: OnStartRequest: DocumentChannel request succeeded\n",
448 this));
449 nsCString channelType;
450 MOZ_ALWAYS_SUCCEEDS(mChannel->GetContentType(channelType));
452 if (GetTypeOfContent(channelType, mSkipFakePlugins) != eType_Document) {
453 MOZ_CRASH("DocumentChannel request with non-document MIME");
455 mContentType = channelType;
457 MOZ_ALWAYS_SUCCEEDS(
458 NS_GetFinalChannelURI(mChannel, getter_AddRefs(mURI)));
461 return mFinalListener->OnStartRequest(aRequest);
464 // Otherwise we should be state loading, and call LoadObject with the channel
465 if (mType != eType_Loading) {
466 MOZ_ASSERT_UNREACHABLE("Should be type loading at this point");
467 return NS_BINDING_ABORTED;
469 NS_ASSERTION(!mChannelLoaded, "mChannelLoaded set already?");
470 NS_ASSERTION(!mFinalListener, "mFinalListener exists already?");
472 mChannelLoaded = true;
474 if (status == NS_ERROR_BLOCKED_URI) {
475 nsCOMPtr<nsIConsoleService> console(
476 do_GetService("@mozilla.org/consoleservice;1"));
477 if (console) {
478 nsCOMPtr<nsIURI> uri;
479 chan->GetURI(getter_AddRefs(uri));
480 nsString message =
481 u"Blocking "_ns +
482 NS_ConvertASCIItoUTF16(uri->GetSpecOrDefault().get()) +
483 nsLiteralString(
484 u" since it was found on an internal Firefox blocklist.");
485 console->LogStringMessage(message.get());
487 mContentBlockingEnabled = true;
488 return NS_ERROR_FAILURE;
491 if (UrlClassifierFeatureFactory::IsClassifierBlockingErrorCode(status)) {
492 mContentBlockingEnabled = true;
493 return NS_ERROR_FAILURE;
496 if (!success) {
497 LOG(("OBJLC [%p]: OnStartRequest: Request failed\n", this));
498 // If the request fails, we still call LoadObject() to handle fallback
499 // content and notifying of failure. (mChannelLoaded && !mChannel) indicates
500 // the bad state.
501 mChannel = nullptr;
502 LoadObject(true, false);
503 return NS_ERROR_FAILURE;
506 return LoadObject(true, false, aRequest);
509 NS_IMETHODIMP
510 nsObjectLoadingContent::OnStopRequest(nsIRequest* aRequest,
511 nsresult aStatusCode) {
512 AUTO_PROFILER_LABEL("nsObjectLoadingContent::OnStopRequest", NETWORK);
514 // Handle object not loading error because source was a tracking URL (or
515 // fingerprinting, cryptomining, etc.).
516 // We make a note of this object node by including it in a dedicated
517 // array of blocked tracking nodes under its parent document.
518 if (UrlClassifierFeatureFactory::IsClassifierBlockingErrorCode(aStatusCode)) {
519 nsCOMPtr<nsIContent> thisNode =
520 do_QueryInterface(static_cast<nsIObjectLoadingContent*>(this));
521 if (thisNode && thisNode->IsInComposedDoc()) {
522 thisNode->GetComposedDoc()->AddBlockedNodeByClassifier(thisNode);
526 if (aRequest != mChannel) {
527 return NS_BINDING_ABORTED;
530 mChannel = nullptr;
532 if (mFinalListener) {
533 // This may re-enter in the case of plugin listeners
534 nsCOMPtr<nsIStreamListener> listenerGrip(mFinalListener);
535 mFinalListener = nullptr;
536 listenerGrip->OnStopRequest(aRequest, aStatusCode);
539 // Return value doesn't matter
540 return NS_OK;
543 // nsIStreamListener
544 NS_IMETHODIMP
545 nsObjectLoadingContent::OnDataAvailable(nsIRequest* aRequest,
546 nsIInputStream* aInputStream,
547 uint64_t aOffset, uint32_t aCount) {
548 if (aRequest != mChannel) {
549 return NS_BINDING_ABORTED;
552 if (mFinalListener) {
553 // This may re-enter in the case of plugin listeners
554 nsCOMPtr<nsIStreamListener> listenerGrip(mFinalListener);
555 return listenerGrip->OnDataAvailable(aRequest, aInputStream, aOffset,
556 aCount);
559 // We shouldn't have a connected channel with no final listener
560 MOZ_ASSERT_UNREACHABLE(
561 "Got data for channel with no connected final "
562 "listener");
563 mChannel = nullptr;
565 return NS_ERROR_UNEXPECTED;
568 void nsObjectLoadingContent::PresetOpenerWindow(
569 const Nullable<WindowProxyHolder>& aOpenerWindow, ErrorResult& aRv) {
570 aRv.Throw(NS_ERROR_FAILURE);
573 NS_IMETHODIMP
574 nsObjectLoadingContent::GetActualType(nsACString& aType) {
575 aType = mContentType;
576 return NS_OK;
579 NS_IMETHODIMP
580 nsObjectLoadingContent::GetDisplayedType(uint32_t* aType) {
581 *aType = DisplayedType();
582 return NS_OK;
585 NS_IMETHODIMP
586 nsObjectLoadingContent::GetContentTypeForMIMEType(const nsACString& aMIMEType,
587 uint32_t* aType) {
588 *aType = GetTypeOfContent(PromiseFlatCString(aMIMEType), false);
589 return NS_OK;
592 // nsIInterfaceRequestor
593 // We use a shim class to implement this so that JS consumers still
594 // see an interface requestor even though WebIDL bindings don't expose
595 // that stuff.
596 class ObjectInterfaceRequestorShim final : public nsIInterfaceRequestor,
597 public nsIChannelEventSink,
598 public nsIStreamListener {
599 public:
600 NS_DECL_CYCLE_COLLECTING_ISUPPORTS
601 NS_DECL_CYCLE_COLLECTION_CLASS_AMBIGUOUS(ObjectInterfaceRequestorShim,
602 nsIInterfaceRequestor)
603 NS_DECL_NSIINTERFACEREQUESTOR
604 // RefPtr<nsObjectLoadingContent> fails due to ambiguous AddRef/Release,
605 // hence the ugly static cast :(
606 NS_FORWARD_NSICHANNELEVENTSINK(
607 static_cast<nsObjectLoadingContent*>(mContent.get())->)
608 NS_FORWARD_NSISTREAMLISTENER(
609 static_cast<nsObjectLoadingContent*>(mContent.get())->)
610 NS_FORWARD_NSIREQUESTOBSERVER(
611 static_cast<nsObjectLoadingContent*>(mContent.get())->)
613 explicit ObjectInterfaceRequestorShim(nsIObjectLoadingContent* aContent)
614 : mContent(aContent) {}
616 protected:
617 ~ObjectInterfaceRequestorShim() = default;
618 nsCOMPtr<nsIObjectLoadingContent> mContent;
621 NS_IMPL_CYCLE_COLLECTION(ObjectInterfaceRequestorShim, mContent)
623 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(ObjectInterfaceRequestorShim)
624 NS_INTERFACE_MAP_ENTRY(nsIInterfaceRequestor)
625 NS_INTERFACE_MAP_ENTRY(nsIChannelEventSink)
626 NS_INTERFACE_MAP_ENTRY(nsIStreamListener)
627 NS_INTERFACE_MAP_ENTRY(nsIRequestObserver)
628 NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIInterfaceRequestor)
629 NS_INTERFACE_MAP_END
631 NS_IMPL_CYCLE_COLLECTING_ADDREF(ObjectInterfaceRequestorShim)
632 NS_IMPL_CYCLE_COLLECTING_RELEASE(ObjectInterfaceRequestorShim)
634 NS_IMETHODIMP
635 ObjectInterfaceRequestorShim::GetInterface(const nsIID& aIID, void** aResult) {
636 if (aIID.Equals(NS_GET_IID(nsIChannelEventSink))) {
637 nsIChannelEventSink* sink = this;
638 *aResult = sink;
639 NS_ADDREF(sink);
640 return NS_OK;
642 if (aIID.Equals(NS_GET_IID(nsIObjectLoadingContent))) {
643 nsIObjectLoadingContent* olc = mContent;
644 *aResult = olc;
645 NS_ADDREF(olc);
646 return NS_OK;
648 return NS_NOINTERFACE;
651 // nsIChannelEventSink
652 NS_IMETHODIMP
653 nsObjectLoadingContent::AsyncOnChannelRedirect(
654 nsIChannel* aOldChannel, nsIChannel* aNewChannel, uint32_t aFlags,
655 nsIAsyncVerifyRedirectCallback* cb) {
656 // If we're already busy with a new load, or have no load at all,
657 // cancel the redirect.
658 if (!mChannel || aOldChannel != mChannel) {
659 return NS_BINDING_ABORTED;
662 mChannel = aNewChannel;
664 if (mFinalListener) {
665 nsCOMPtr<nsIChannelEventSink> sink(do_QueryInterface(mFinalListener));
666 MOZ_RELEASE_ASSERT(sink, "mFinalListener isn't nsIChannelEventSink?");
667 if (mType != eType_Document) {
668 MOZ_ASSERT_UNREACHABLE(
669 "Not a DocumentChannel load, but we're getting a "
670 "AsyncOnChannelRedirect with a mFinalListener?");
671 return NS_BINDING_ABORTED;
674 return sink->AsyncOnChannelRedirect(aOldChannel, aNewChannel, aFlags, cb);
677 cb->OnRedirectVerifyCallback(NS_OK);
678 return NS_OK;
681 void nsObjectLoadingContent::MaybeRewriteYoutubeEmbed(nsIURI* aURI,
682 nsIURI* aBaseURI,
683 nsIURI** aRewrittenURI) {
684 nsCOMPtr<nsIContent> thisContent =
685 do_QueryInterface(static_cast<nsIImageLoadingContent*>(this));
686 NS_ASSERTION(thisContent, "Must be an instance of content");
688 // We're only interested in switching out embed and object tags
689 if (!thisContent->NodeInfo()->Equals(nsGkAtoms::embed) &&
690 !thisContent->NodeInfo()->Equals(nsGkAtoms::object)) {
691 return;
694 nsCOMPtr<nsIEffectiveTLDService> tldService =
695 do_GetService(NS_EFFECTIVETLDSERVICE_CONTRACTID);
696 // If we can't analyze the URL, just pass on through.
697 if (!tldService) {
698 NS_WARNING("Could not get TLD service!");
699 return;
702 nsAutoCString currentBaseDomain;
703 bool ok = NS_SUCCEEDED(tldService->GetBaseDomain(aURI, 0, currentBaseDomain));
704 if (!ok) {
705 // Data URIs (commonly used for things like svg embeds) won't parse
706 // correctly, so just fail silently here.
707 return;
710 // See if URL is referencing youtube
711 if (!currentBaseDomain.EqualsLiteral("youtube.com") &&
712 !currentBaseDomain.EqualsLiteral("youtube-nocookie.com")) {
713 return;
716 // We should only rewrite URLs with paths starting with "/v/", as we shouldn't
717 // touch object nodes with "/embed/" urls that already do that right thing.
718 nsAutoCString path;
719 aURI->GetPathQueryRef(path);
720 if (!StringBeginsWith(path, "/v/"_ns)) {
721 return;
724 // See if requester is planning on using the JS API.
725 nsAutoCString uri;
726 nsresult rv = aURI->GetSpec(uri);
727 if (NS_FAILED(rv)) {
728 return;
731 // Some YouTube urls have parameters in path components, e.g.
732 // http://youtube.com/embed/7LcUOEP7Brc&start=35. These URLs work with flash,
733 // but break iframe/object embedding. If this situation occurs with rewritten
734 // URLs, convert the parameters to query in order to make the video load
735 // correctly as an iframe. In either case, warn about it in the
736 // developer console.
737 int32_t ampIndex = uri.FindChar('&', 0);
738 bool replaceQuery = false;
739 if (ampIndex != -1) {
740 int32_t qmIndex = uri.FindChar('?', 0);
741 if (qmIndex == -1 || qmIndex > ampIndex) {
742 replaceQuery = true;
746 // If we've made it this far, we've got a rewritable embed. Log it in
747 // telemetry.
748 thisContent->OwnerDoc()->SetUseCounter(eUseCounter_custom_YouTubeFlashEmbed);
750 // If we're pref'd off, return after telemetry has been logged.
751 if (!Preferences::GetBool(kPrefYoutubeRewrite)) {
752 return;
755 nsAutoString utf16OldURI = NS_ConvertUTF8toUTF16(uri);
756 // If we need to convert the URL, it means an ampersand comes first.
757 // Use the index we found earlier.
758 if (replaceQuery) {
759 // Replace question marks with ampersands.
760 uri.ReplaceChar('?', '&');
761 // Replace the first ampersand with a question mark.
762 uri.SetCharAt('?', ampIndex);
764 // Switch out video access url formats, which should possibly allow HTML5
765 // video loading.
766 uri.ReplaceSubstring("/v/"_ns, "/embed/"_ns);
767 nsAutoString utf16URI = NS_ConvertUTF8toUTF16(uri);
768 rv = nsContentUtils::NewURIWithDocumentCharset(
769 aRewrittenURI, utf16URI, thisContent->OwnerDoc(), aBaseURI);
770 if (NS_FAILED(rv)) {
771 return;
773 AutoTArray<nsString, 2> params = {utf16OldURI, utf16URI};
774 const char* msgName;
775 // If there's no query to rewrite, just notify in the developer console
776 // that we're changing the embed.
777 if (!replaceQuery) {
778 msgName = "RewriteYouTubeEmbed";
779 } else {
780 msgName = "RewriteYouTubeEmbedPathParams";
782 nsContentUtils::ReportToConsole(
783 nsIScriptError::warningFlag, "Plugins"_ns, thisContent->OwnerDoc(),
784 nsContentUtils::eDOM_PROPERTIES, msgName, params);
787 bool nsObjectLoadingContent::CheckLoadPolicy(int16_t* aContentPolicy) {
788 if (!aContentPolicy || !mURI) {
789 MOZ_ASSERT_UNREACHABLE("Doing it wrong");
790 return false;
793 nsCOMPtr<nsIContent> thisContent =
794 do_QueryInterface(static_cast<nsIImageLoadingContent*>(this));
795 NS_ASSERTION(thisContent, "Must be an instance of content");
797 Document* doc = thisContent->OwnerDoc();
799 nsContentPolicyType contentPolicyType = GetContentPolicyType();
801 nsCOMPtr<nsILoadInfo> secCheckLoadInfo = new LoadInfo(
802 doc->NodePrincipal(), // loading principal
803 doc->NodePrincipal(), // triggering principal
804 thisContent, nsILoadInfo::SEC_ONLY_FOR_EXPLICIT_CONTENTSEC_CHECK,
805 contentPolicyType);
807 *aContentPolicy = nsIContentPolicy::ACCEPT;
808 nsresult rv = NS_CheckContentLoadPolicy(mURI, secCheckLoadInfo, mContentType,
809 aContentPolicy,
810 nsContentUtils::GetContentPolicy());
811 NS_ENSURE_SUCCESS(rv, false);
812 if (NS_CP_REJECTED(*aContentPolicy)) {
813 LOG(("OBJLC [%p]: Content policy denied load of %s", this,
814 mURI->GetSpecOrDefault().get()));
815 return false;
818 return true;
821 bool nsObjectLoadingContent::CheckProcessPolicy(int16_t* aContentPolicy) {
822 if (!aContentPolicy) {
823 MOZ_ASSERT_UNREACHABLE("Null out variable");
824 return false;
827 nsCOMPtr<nsIContent> thisContent =
828 do_QueryInterface(static_cast<nsIImageLoadingContent*>(this));
829 NS_ASSERTION(thisContent, "Must be an instance of content");
831 Document* doc = thisContent->OwnerDoc();
833 nsContentPolicyType objectType;
834 switch (mType) {
835 case eType_Image:
836 objectType = nsIContentPolicy::TYPE_INTERNAL_IMAGE;
837 break;
838 case eType_Document:
839 objectType = nsIContentPolicy::TYPE_DOCUMENT;
840 break;
841 case eType_Fallback:
842 case eType_FakePlugin:
843 objectType = GetContentPolicyType();
844 break;
845 default:
846 MOZ_ASSERT_UNREACHABLE(
847 "Calling checkProcessPolicy with an unloadable "
848 "type");
849 return false;
852 nsCOMPtr<nsILoadInfo> secCheckLoadInfo = new LoadInfo(
853 doc->NodePrincipal(), // loading principal
854 doc->NodePrincipal(), // triggering principal
855 thisContent, nsILoadInfo::SEC_ONLY_FOR_EXPLICIT_CONTENTSEC_CHECK,
856 objectType);
858 *aContentPolicy = nsIContentPolicy::ACCEPT;
859 nsresult rv = NS_CheckContentProcessPolicy(
860 mURI ? mURI : mBaseURI, secCheckLoadInfo, mContentType, aContentPolicy,
861 nsContentUtils::GetContentPolicy());
862 NS_ENSURE_SUCCESS(rv, false);
864 if (NS_CP_REJECTED(*aContentPolicy)) {
865 LOG(("OBJLC [%p]: CheckContentProcessPolicy rejected load", this));
866 return false;
869 return true;
872 nsObjectLoadingContent::ParameterUpdateFlags
873 nsObjectLoadingContent::UpdateObjectParameters() {
874 nsCOMPtr<Element> thisElement =
875 do_QueryInterface(static_cast<nsIImageLoadingContent*>(this));
876 MOZ_ASSERT(thisElement, "Must be an Element");
878 uint32_t caps = GetCapabilities();
879 LOG(("OBJLC [%p]: Updating object parameters", this));
881 nsresult rv;
882 nsAutoCString newMime;
883 nsAutoString typeAttr;
884 nsCOMPtr<nsIURI> newURI;
885 nsCOMPtr<nsIURI> newBaseURI;
886 ObjectType newType;
887 // Set if this state can't be used to load anything, forces eType_Null
888 bool stateInvalid = false;
889 // Indicates what parameters changed.
890 // eParamChannelChanged - means parameters that affect channel opening
891 // decisions changed
892 // eParamStateChanged - means anything that affects what content we load
893 // changed, even if the channel we'd open remains the
894 // same.
896 // State changes outside of the channel parameters only matter if we've
897 // already opened a channel or tried to instantiate content, whereas channel
898 // parameter changes require re-opening the channel even if we haven't gotten
899 // that far.
900 nsObjectLoadingContent::ParameterUpdateFlags retval = eParamNoChange;
903 /// Initial MIME Type
906 if (caps & eFallbackIfClassIDPresent) {
907 nsAutoString classIDAttr;
908 thisElement->GetAttr(nsGkAtoms::classid, classIDAttr);
909 // We don't support class ID plugin references, so we should always treat
910 // having class Ids as attributes as invalid, and fallback accordingly.
911 if (!classIDAttr.IsEmpty()) {
912 newMime.Truncate();
913 stateInvalid = true;
918 /// Codebase
921 nsAutoString codebaseStr;
922 nsIURI* docBaseURI = thisElement->GetBaseURI();
923 thisElement->GetAttr(nsGkAtoms::codebase, codebaseStr);
925 if (!codebaseStr.IsEmpty()) {
926 rv = nsContentUtils::NewURIWithDocumentCharset(
927 getter_AddRefs(newBaseURI), codebaseStr, thisElement->OwnerDoc(),
928 docBaseURI);
929 if (NS_FAILED(rv)) {
930 // Malformed URI
931 LOG(
932 ("OBJLC [%p]: Could not parse plugin's codebase as a URI, "
933 "will use document baseURI instead",
934 this));
938 // If we failed to build a valid URI, use the document's base URI
939 if (!newBaseURI) {
940 newBaseURI = docBaseURI;
943 nsAutoString rawTypeAttr;
944 thisElement->GetAttr(nsGkAtoms::type, rawTypeAttr);
945 if (!rawTypeAttr.IsEmpty()) {
946 typeAttr = rawTypeAttr;
947 nsAutoString params;
948 nsAutoString mime;
949 nsContentUtils::SplitMimeType(rawTypeAttr, mime, params);
950 CopyUTF16toUTF8(mime, newMime);
954 /// URI
957 nsAutoString uriStr;
958 // Different elements keep this in various locations
959 if (thisElement->NodeInfo()->Equals(nsGkAtoms::object)) {
960 thisElement->GetAttr(nsGkAtoms::data, uriStr);
961 } else if (thisElement->NodeInfo()->Equals(nsGkAtoms::embed)) {
962 thisElement->GetAttr(nsGkAtoms::src, uriStr);
963 } else {
964 MOZ_ASSERT_UNREACHABLE("Unrecognized plugin-loading tag");
967 mRewrittenYoutubeEmbed = false;
968 // Note that the baseURI changing could affect the newURI, even if uriStr did
969 // not change.
970 if (!uriStr.IsEmpty()) {
971 rv = nsContentUtils::NewURIWithDocumentCharset(
972 getter_AddRefs(newURI), uriStr, thisElement->OwnerDoc(), newBaseURI);
973 nsCOMPtr<nsIURI> rewrittenURI;
974 MaybeRewriteYoutubeEmbed(newURI, newBaseURI, getter_AddRefs(rewrittenURI));
975 if (rewrittenURI) {
976 newURI = rewrittenURI;
977 mRewrittenYoutubeEmbed = true;
978 newMime = "text/html"_ns;
981 if (NS_FAILED(rv)) {
982 stateInvalid = true;
987 /// Check if the original (pre-channel) content-type or URI changed, and
988 /// record mOriginal{ContentType,URI}
991 if ((mOriginalContentType != newMime) || !URIEquals(mOriginalURI, newURI)) {
992 // These parameters changing requires re-opening the channel, so don't
993 // consider the currently-open channel below
994 // XXX(johns): Changing the mime type might change our decision on whether
995 // or not we load a channel, so we count changes to it as a
996 // channel parameter change for the sake of simplicity.
997 retval = (ParameterUpdateFlags)(retval | eParamChannelChanged);
998 LOG(("OBJLC [%p]: Channel parameters changed", this));
1000 mOriginalContentType = newMime;
1001 mOriginalURI = newURI;
1004 /// If we have a channel, see if its MIME type should take precendence and
1005 /// check the final (redirected) URL
1008 // If we have a loaded channel and channel parameters did not change, use it
1009 // to determine what we would load.
1010 bool useChannel = mChannelLoaded && !(retval & eParamChannelChanged);
1011 // If we have a channel and are type loading, as opposed to having an existing
1012 // channel for a previous load.
1013 bool newChannel = useChannel && mType == eType_Loading;
1015 RefPtr<DocumentChannel> documentChannel = do_QueryObject(mChannel);
1016 if (newChannel && documentChannel) {
1017 // If we've got a DocumentChannel which is marked as loaded using
1018 // `mChannelLoaded`, we are currently in the middle of a
1019 // `UpgradeLoadToDocument`.
1021 // As we don't have the real mime-type from the channel, handle this by
1022 // using `newMime`.
1023 newMime = TEXT_HTML;
1025 MOZ_DIAGNOSTIC_ASSERT(
1026 GetTypeOfContent(newMime, mSkipFakePlugins) == eType_Document,
1027 "How is text/html not eType_Document?");
1028 } else if (newChannel && mChannel) {
1029 nsCString channelType;
1030 rv = mChannel->GetContentType(channelType);
1031 if (NS_FAILED(rv)) {
1032 MOZ_ASSERT_UNREACHABLE("GetContentType failed");
1033 stateInvalid = true;
1034 channelType.Truncate();
1037 LOG(("OBJLC [%p]: Channel has a content type of %s", this,
1038 channelType.get()));
1040 bool binaryChannelType = false;
1041 if (channelType.EqualsASCII(APPLICATION_GUESS_FROM_EXT)) {
1042 channelType = APPLICATION_OCTET_STREAM;
1043 mChannel->SetContentType(channelType);
1044 binaryChannelType = true;
1045 } else if (channelType.EqualsASCII(APPLICATION_OCTET_STREAM) ||
1046 channelType.EqualsASCII(BINARY_OCTET_STREAM)) {
1047 binaryChannelType = true;
1050 // Channel can change our URI through redirection
1051 rv = NS_GetFinalChannelURI(mChannel, getter_AddRefs(newURI));
1052 if (NS_FAILED(rv)) {
1053 MOZ_ASSERT_UNREACHABLE("NS_GetFinalChannelURI failure");
1054 stateInvalid = true;
1057 ObjectType typeHint = newMime.IsEmpty()
1058 ? eType_Null
1059 : GetTypeOfContent(newMime, mSkipFakePlugins);
1062 // In order of preference:
1064 // 1) Use our type hint if it matches a plugin
1065 // 2) If we have eAllowPluginSkipChannel, use the uri file extension if
1066 // it matches a plugin
1067 // 3) If the channel returns a binary stream type:
1068 // 3a) If we have a type non-null non-document type hint, use that
1069 // 3b) If the uri file extension matches a plugin type, use that
1070 // 4) Use the channel type
1072 bool overrideChannelType = false;
1073 if (IsPluginType(typeHint)) {
1074 LOG(("OBJLC [%p]: Using plugin type hint in favor of any channel type",
1075 this));
1076 overrideChannelType = true;
1077 } else if (binaryChannelType && typeHint != eType_Null) {
1078 if (typeHint == eType_Document) {
1079 if (StaticPrefs::
1080 browser_opaqueResponseBlocking_syntheticBrowsingContext_AtStartup() &&
1081 imgLoader::SupportImageWithMimeType(newMime)) {
1082 LOG(
1083 ("OBJLC [%p]: Using type hint in favor of binary channel type "
1084 "(Image Document)",
1085 this));
1086 overrideChannelType = true;
1088 } else {
1089 LOG(
1090 ("OBJLC [%p]: Using type hint in favor of binary channel type "
1091 "(Non-Image Document)",
1092 this));
1093 overrideChannelType = true;
1097 if (overrideChannelType) {
1098 // Set the type we'll use for dispatch on the channel. Otherwise we could
1099 // end up trying to dispatch to a nsFrameLoader, which will complain that
1100 // it couldn't find a way to handle application/octet-stream
1101 nsAutoCString parsedMime, dummy;
1102 NS_ParseResponseContentType(newMime, parsedMime, dummy);
1103 if (!parsedMime.IsEmpty()) {
1104 mChannel->SetContentType(parsedMime);
1106 } else {
1107 newMime = channelType;
1109 } else if (newChannel) {
1110 LOG(("OBJLC [%p]: We failed to open a channel, marking invalid", this));
1111 stateInvalid = true;
1115 /// Determine final type
1117 // In order of preference:
1118 // 1) If we have attempted channel load, or set stateInvalid above, the type
1119 // is always null (fallback)
1120 // 2) If we have a loaded channel, we grabbed its mimeType above, use that
1121 // type.
1122 // 3) If we have a plugin type and no URI, use that type.
1123 // 4) If we have a plugin type and eAllowPluginSkipChannel, use that type.
1124 // 5) if we have a URI, set type to loading to indicate we'd need a channel
1125 // to proceed.
1126 // 6) Otherwise, type null to indicate unloadable content (fallback)
1129 ObjectType newMime_Type = GetTypeOfContent(newMime, mSkipFakePlugins);
1131 if (stateInvalid) {
1132 newType = eType_Null;
1133 LOG(("OBJLC [%p]: NewType #0: %s - %u", this, newMime.get(), newType));
1134 newMime.Truncate();
1135 } else if (newChannel) {
1136 // If newChannel is set above, we considered it in setting newMime
1137 newType = newMime_Type;
1138 LOG(("OBJLC [%p]: NewType #1: %s - %u", this, newMime.get(), newType));
1139 LOG(("OBJLC [%p]: Using channel type", this));
1140 } else if (((caps & eAllowPluginSkipChannel) || !newURI) &&
1141 IsPluginType(newMime_Type)) {
1142 newType = newMime_Type;
1143 LOG(("OBJLC [%p]: NewType #2: %s - %u", this, newMime.get(), newType));
1144 LOG(("OBJLC [%p]: Plugin type with no URI, skipping channel load", this));
1145 } else if (newURI &&
1146 (mOriginalContentType.IsEmpty() || newMime_Type != eType_Null)) {
1147 // We could potentially load this if we opened a channel on mURI, indicate
1148 // this by leaving type as loading.
1150 // If a MIME type was requested in the tag, but we have decided to set load
1151 // type to null, ignore (otherwise we'll default to document type loading).
1152 newType = eType_Loading;
1153 LOG(("OBJLC [%p]: NewType #3: %u", this, newType));
1154 } else {
1155 // Unloadable - no URI, and no plugin/MIME type. Non-plugin types (images,
1156 // documents) always load with a channel.
1157 newType = eType_Null;
1158 LOG(("OBJLC [%p]: NewType #4: %u", this, newType));
1161 mLoadingSyntheticDocument =
1162 newType == eType_Document &&
1163 StaticPrefs::
1164 browser_opaqueResponseBlocking_syntheticBrowsingContext_AtStartup() &&
1165 imgLoader::SupportImageWithMimeType(newMime);
1168 /// Handle existing channels
1171 if (useChannel && newType == eType_Loading) {
1172 // We decided to use a channel, and also that the previous channel is still
1173 // usable, so re-use the existing values.
1174 newType = mType;
1175 LOG(("OBJLC [%p]: NewType #5: %u", this, newType));
1176 newMime = mContentType;
1177 newURI = mURI;
1178 } else if (useChannel && !newChannel) {
1179 // We have an existing channel, but did not decide to use one.
1180 retval = (ParameterUpdateFlags)(retval | eParamChannelChanged);
1181 useChannel = false;
1185 /// Update changed values
1188 if (newType != mType) {
1189 retval = (ParameterUpdateFlags)(retval | eParamStateChanged);
1190 LOG(("OBJLC [%p]: Type changed from %u -> %u", this, mType, newType));
1191 mType = newType;
1194 if (!URIEquals(mBaseURI, newBaseURI)) {
1195 LOG(("OBJLC [%p]: Object effective baseURI changed", this));
1196 mBaseURI = newBaseURI;
1199 if (!URIEquals(newURI, mURI)) {
1200 retval = (ParameterUpdateFlags)(retval | eParamStateChanged);
1201 LOG(("OBJLC [%p]: Object effective URI changed", this));
1202 mURI = newURI;
1205 // We don't update content type when loading, as the type is not final and we
1206 // don't want to superfluously change between mOriginalContentType ->
1207 // mContentType when doing |obj.data = obj.data| with a channel and differing
1208 // type.
1209 if (mType != eType_Loading && mContentType != newMime) {
1210 retval = (ParameterUpdateFlags)(retval | eParamStateChanged);
1211 retval = (ParameterUpdateFlags)(retval | eParamContentTypeChanged);
1212 LOG(("OBJLC [%p]: Object effective mime type changed (%s -> %s)", this,
1213 mContentType.get(), newMime.get()));
1214 mContentType = newMime;
1217 // If we decided to keep using info from an old channel, but also that state
1218 // changed, we need to invalidate it.
1219 if (useChannel && !newChannel && (retval & eParamStateChanged)) {
1220 mType = eType_Loading;
1221 retval = (ParameterUpdateFlags)(retval | eParamChannelChanged);
1224 return retval;
1227 // Used by PluginDocument to kick off our initial load from the already-opened
1228 // channel.
1229 NS_IMETHODIMP
1230 nsObjectLoadingContent::InitializeFromChannel(nsIRequest* aChannel) {
1231 LOG(("OBJLC [%p] InitializeFromChannel: %p", this, aChannel));
1232 if (mType != eType_Loading || mChannel) {
1233 // We could technically call UnloadObject() here, if consumers have a valid
1234 // reason for wanting to call this on an already-loaded tag.
1235 MOZ_ASSERT_UNREACHABLE("Should not have begun loading at this point");
1236 return NS_ERROR_UNEXPECTED;
1239 // Because we didn't open this channel from an initial LoadObject, we'll
1240 // update our parameters now, so the OnStartRequest->LoadObject doesn't
1241 // believe our src/type suddenly changed.
1242 UpdateObjectParameters();
1243 // But we always want to load from a channel, in this case.
1244 mType = eType_Loading;
1245 mChannel = do_QueryInterface(aChannel);
1246 NS_ASSERTION(mChannel, "passed a request that is not a channel");
1248 // OnStartRequest will now see we have a channel in the loading state, and
1249 // call into LoadObject. There's a possibility LoadObject will decide not to
1250 // load anything from a channel - it will call CloseChannel() in that case.
1251 return NS_OK;
1254 // Only OnStartRequest should be passing the channel parameter
1255 nsresult nsObjectLoadingContent::LoadObject(bool aNotify, bool aForceLoad) {
1256 return LoadObject(aNotify, aForceLoad, nullptr);
1259 nsresult nsObjectLoadingContent::LoadObject(bool aNotify, bool aForceLoad,
1260 nsIRequest* aLoadingChannel) {
1261 nsCOMPtr<nsIContent> thisContent =
1262 do_QueryInterface(static_cast<nsIImageLoadingContent*>(this));
1263 NS_ASSERTION(thisContent, "must be a content");
1264 Document* doc = thisContent->OwnerDoc();
1265 nsresult rv = NS_OK;
1267 // Per bug 1318303, if the parent document is not active, load the alternative
1268 // and return.
1269 if (!doc->IsCurrentActiveDocument()) {
1270 // Since this can be triggered on change of attributes, make sure we've
1271 // unloaded whatever is loaded first.
1272 UnloadObject();
1273 ObjectType oldType = mType;
1274 mType = eType_Fallback;
1275 ConfigureFallback();
1276 NotifyStateChanged(oldType, true);
1277 return NS_OK;
1280 // XXX(johns): In these cases, we refuse to touch our content and just
1281 // remain unloaded, as per legacy behavior. It would make more sense to
1282 // load fallback content initially and refuse to ever change state again.
1283 if (doc->IsBeingUsedAsImage()) {
1284 return NS_OK;
1287 if (doc->IsLoadedAsData() && !doc->IsStaticDocument()) {
1288 return NS_OK;
1290 if (doc->IsStaticDocument()) {
1291 // We only allow image loads in static documents, but we need to let the
1292 // eType_Loading state go through too while we do so.
1293 if (mType != eType_Image && mType != eType_Loading) {
1294 return NS_OK;
1298 LOG(("OBJLC [%p]: LoadObject called, notify %u, forceload %u, channel %p",
1299 this, aNotify, aForceLoad, aLoadingChannel));
1301 // We can't re-use an already open channel, but aForceLoad may make us try
1302 // to load a plugin without any changes in channel state.
1303 if (aForceLoad && mChannelLoaded) {
1304 CloseChannel();
1305 mChannelLoaded = false;
1308 // Save these for NotifyStateChanged();
1309 ObjectType oldType = mType;
1311 ParameterUpdateFlags stateChange = UpdateObjectParameters();
1313 if (!stateChange && !aForceLoad) {
1314 return NS_OK;
1318 /// State has changed, unload existing content and attempt to load new type
1320 LOG(("OBJLC [%p]: LoadObject - plugin state changed (%u)", this,
1321 stateChange));
1323 // We synchronously start/stop plugin instances below, which may spin the
1324 // event loop. Re-entering into the load is fine, but at that point the
1325 // original load call needs to abort when unwinding
1326 // NOTE this is located *after* the state change check, a subsequent load
1327 // with no subsequently changed state will be a no-op.
1328 if (mIsLoading) {
1329 LOG(("OBJLC [%p]: Re-entering into LoadObject", this));
1331 mIsLoading = true;
1332 AutoSetLoadingToFalse reentryCheck(this);
1334 // Unload existing content, keeping in mind stopping plugins might spin the
1335 // event loop. Note that we check for still-open channels below
1336 UnloadObject(false); // Don't reset state
1337 if (!mIsLoading) {
1338 // The event loop must've spun and re-entered into LoadObject, which
1339 // finished the load
1340 LOG(("OBJLC [%p]: Re-entered into LoadObject, aborting outer load", this));
1341 return NS_OK;
1344 // Determine what's going on with our channel.
1345 if (stateChange & eParamChannelChanged) {
1346 // If the channel params changed, throw away the channel, but unset
1347 // mChannelLoaded so we'll still try to open a new one for this load if
1348 // necessary
1349 CloseChannel();
1350 mChannelLoaded = false;
1351 } else if (mType == eType_Null && mChannel) {
1352 // If we opened a channel but then failed to find a loadable state, throw it
1353 // away. mChannelLoaded will indicate that we tried to load a channel at one
1354 // point so we wont recurse
1355 CloseChannel();
1356 } else if (mType == eType_Loading && mChannel) {
1357 // We're still waiting on a channel load, already opened one, and
1358 // channel parameters didn't change
1359 return NS_OK;
1360 } else if (mChannelLoaded && mChannel != aLoadingChannel) {
1361 // The only time we should have a loaded channel with a changed state is
1362 // when the channel has just opened -- in which case this call should
1363 // have originated from OnStartRequest
1364 MOZ_ASSERT_UNREACHABLE(
1365 "Loading with a channel, but state doesn't make sense");
1366 return NS_OK;
1370 // Security checks
1373 if (mType != eType_Null && mType != eType_Fallback) {
1374 bool allowLoad = true;
1375 int16_t contentPolicy = nsIContentPolicy::ACCEPT;
1376 // If mChannelLoaded is set we presumably already passed load policy
1377 // If mType == eType_Loading then we call OpenChannel() which internally
1378 // creates a new channel and calls asyncOpen() on that channel which
1379 // then enforces content policy checks.
1380 if (allowLoad && mURI && !mChannelLoaded && mType != eType_Loading) {
1381 allowLoad = CheckLoadPolicy(&contentPolicy);
1383 // If we're loading a type now, check ProcessPolicy. Note that we may check
1384 // both now in the case of plugins whose type is determined before opening a
1385 // channel.
1386 if (allowLoad && mType != eType_Loading) {
1387 allowLoad = CheckProcessPolicy(&contentPolicy);
1390 // Content policy implementations can mutate the DOM, check for re-entry
1391 if (!mIsLoading) {
1392 LOG(("OBJLC [%p]: We re-entered in content policy, leaving original load",
1393 this));
1394 return NS_OK;
1397 // Load denied, switch to null
1398 if (!allowLoad) {
1399 LOG(("OBJLC [%p]: Load denied by policy", this));
1400 mType = eType_Null;
1404 // Don't allow view-source scheme.
1405 // view-source is the only scheme to which this applies at the moment due to
1406 // potential timing attacks to read data from cross-origin documents. If this
1407 // widens we should add a protocol flag for whether the scheme is only allowed
1408 // in top and use something like nsNetUtil::NS_URIChainHasFlags.
1409 if (mType != eType_Null) {
1410 nsCOMPtr<nsIURI> tempURI = mURI;
1411 nsCOMPtr<nsINestedURI> nestedURI = do_QueryInterface(tempURI);
1412 while (nestedURI) {
1413 // view-source should always be an nsINestedURI, loop and check the
1414 // scheme on this and all inner URIs that are also nested URIs.
1415 if (tempURI->SchemeIs("view-source")) {
1416 LOG(("OBJLC [%p]: Blocking as effective URI has view-source scheme",
1417 this));
1418 mType = eType_Null;
1419 break;
1422 nestedURI->GetInnerURI(getter_AddRefs(tempURI));
1423 nestedURI = do_QueryInterface(tempURI);
1427 // Items resolved as Image/Document are not candidates for content blocking,
1428 // as well as invalid plugins (they will not have the mContentType set).
1429 if ((mType == eType_Null || IsPluginType(mType)) && ShouldBlockContent()) {
1430 LOG(("OBJLC [%p]: Enable content blocking", this));
1431 mType = eType_Loading;
1434 // Sanity check: We shouldn't have any loaded resources, pending events, or
1435 // a final listener at this point
1436 if (mFrameLoader || mFinalListener) {
1437 MOZ_ASSERT_UNREACHABLE("Trying to load new plugin with existing content");
1438 return NS_OK;
1441 // More sanity-checking:
1442 // If mChannel is set, mChannelLoaded should be set, and vice-versa
1443 if (mType != eType_Null && !!mChannel != mChannelLoaded) {
1444 MOZ_ASSERT_UNREACHABLE("Trying to load with bad channel state");
1445 return NS_OK;
1449 /// Attempt to load new type
1452 // Cache the current attributes and parameters.
1453 if (mType == eType_Null) {
1454 rv = BuildParametersArray();
1455 NS_ENSURE_SUCCESS(rv, rv);
1458 // We don't set mFinalListener until OnStartRequest has been called, to
1459 // prevent re-entry ugliness with CloseChannel()
1460 nsCOMPtr<nsIStreamListener> finalListener;
1461 switch (mType) {
1462 case eType_Image:
1463 if (!mChannel) {
1464 // We have a LoadImage() call, but UpdateObjectParameters requires a
1465 // channel for images, so this is not a valid state.
1466 MOZ_ASSERT_UNREACHABLE("Attempting to load image without a channel?");
1467 rv = NS_ERROR_UNEXPECTED;
1468 break;
1470 rv = LoadImageWithChannel(mChannel, getter_AddRefs(finalListener));
1471 // finalListener will receive OnStartRequest below
1472 break;
1473 case eType_Document: {
1474 if (!mChannel) {
1475 // We could mFrameLoader->LoadURI(mURI), but UpdateObjectParameters
1476 // requires documents have a channel, so this is not a valid state.
1477 MOZ_ASSERT_UNREACHABLE(
1478 "Attempting to load a document without a "
1479 "channel");
1480 rv = NS_ERROR_FAILURE;
1481 break;
1484 nsCOMPtr<nsIDocShell> docShell = SetupDocShell(mURI);
1485 if (!docShell) {
1486 rv = NS_ERROR_FAILURE;
1487 break;
1490 // We're loading a document, so we have to set LOAD_DOCUMENT_URI
1491 // (especially important for firing onload)
1492 nsLoadFlags flags = 0;
1493 mChannel->GetLoadFlags(&flags);
1494 flags |= nsIChannel::LOAD_DOCUMENT_URI;
1495 mChannel->SetLoadFlags(flags);
1497 nsCOMPtr<nsIInterfaceRequestor> req(do_QueryInterface(docShell));
1498 NS_ASSERTION(req, "Docshell must be an ifreq");
1500 nsCOMPtr<nsIURILoader> uriLoader(components::URILoader::Service());
1501 if (NS_WARN_IF(!uriLoader)) {
1502 MOZ_ASSERT_UNREACHABLE("Failed to get uriLoader service");
1503 mFrameLoader->Destroy();
1504 mFrameLoader = nullptr;
1505 break;
1508 rv = uriLoader->OpenChannel(mChannel, nsIURILoader::DONT_RETARGET, req,
1509 getter_AddRefs(finalListener));
1510 // finalListener will receive OnStartRequest either below, or if
1511 // `mChannel` is a `DocumentChannel`, it will be received after
1512 // RedirectToRealChannel.
1513 } break;
1514 case eType_Loading:
1515 // If our type remains Loading, we need a channel to proceed
1516 rv = OpenChannel();
1517 if (NS_FAILED(rv)) {
1518 LOG(("OBJLC [%p]: OpenChannel returned failure (%" PRIu32 ")", this,
1519 static_cast<uint32_t>(rv)));
1521 break;
1522 case eType_Null:
1523 case eType_Fallback:
1524 // Handled below, silence compiler warnings
1525 break;
1526 case eType_FakePlugin:
1527 // We're now in the process of removing FakePlugin. See bug 1529133.
1528 MOZ_CRASH(
1529 "Shouldn't reach here! This means there's a fakeplugin trying to be "
1530 "loaded.");
1534 // Loaded, handle notifications and fallback
1536 if (NS_FAILED(rv)) {
1537 // If we failed in the loading hunk above, switch to null (empty) region
1538 LOG(("OBJLC [%p]: Loading failed, switching to null", this));
1539 mType = eType_Null;
1542 // If we didn't load anything, handle switching to fallback state
1543 if (mType == eType_Fallback || mType == eType_Null) {
1544 LOG(("OBJLC [%p]: Switching to fallback state", this));
1545 MOZ_ASSERT(!mFrameLoader, "switched to fallback but also loaded something");
1547 MaybeFireErrorEvent();
1549 if (mChannel) {
1550 // If we were loading with a channel but then failed over, throw it away
1551 CloseChannel();
1554 // Don't try to initialize plugins or final listener below
1555 finalListener = nullptr;
1557 ConfigureFallback();
1560 // Notify of our final state
1561 NotifyStateChanged(oldType, aNotify);
1562 NS_ENSURE_TRUE(mIsLoading, NS_OK);
1565 // Spawning plugins and dispatching to the final listener may re-enter, so are
1566 // delayed until after we fire a notification, to prevent missing
1567 // notifications or firing them out of order.
1569 // Note that we ensured that we entered into LoadObject() from
1570 // ::OnStartRequest above when loading with a channel.
1573 rv = NS_OK;
1574 if (finalListener) {
1575 NS_ASSERTION(mType != eType_Null && mType != eType_Loading,
1576 "We should not have a final listener with a non-loaded type");
1577 mFinalListener = finalListener;
1579 // If we're a DocumentChannel load, hold off on firing the `OnStartRequest`
1580 // callback, as we haven't received it yet from our caller.
1581 RefPtr<DocumentChannel> documentChannel = do_QueryObject(mChannel);
1582 if (documentChannel) {
1583 MOZ_ASSERT(
1584 mType == eType_Document,
1585 "We have a DocumentChannel here but aren't loading a document?");
1586 } else {
1587 rv = finalListener->OnStartRequest(mChannel);
1591 if ((NS_FAILED(rv) && rv != NS_ERROR_PARSED_DATA_CACHED) && mIsLoading) {
1592 // Since we've already notified of our transition, we can just Unload and
1593 // call ConfigureFallback (which will notify again)
1594 oldType = mType;
1595 mType = eType_Fallback;
1596 UnloadObject(false);
1597 NS_ENSURE_TRUE(mIsLoading, NS_OK);
1598 CloseChannel();
1599 ConfigureFallback();
1600 NotifyStateChanged(oldType, true);
1603 return NS_OK;
1606 // This call can re-enter when dealing with plugin listeners
1607 nsresult nsObjectLoadingContent::CloseChannel() {
1608 if (mChannel) {
1609 LOG(("OBJLC [%p]: Closing channel\n", this));
1610 // Null the values before potentially-reentering, and ensure they survive
1611 // the call
1612 nsCOMPtr<nsIChannel> channelGrip(mChannel);
1613 nsCOMPtr<nsIStreamListener> listenerGrip(mFinalListener);
1614 mChannel = nullptr;
1615 mFinalListener = nullptr;
1616 channelGrip->CancelWithReason(NS_BINDING_ABORTED,
1617 "nsObjectLoadingContent::CloseChannel"_ns);
1618 if (listenerGrip) {
1619 // mFinalListener is only set by LoadObject after OnStartRequest, or
1620 // by OnStartRequest in the case of late-opened plugin streams
1621 listenerGrip->OnStopRequest(channelGrip, NS_BINDING_ABORTED);
1624 return NS_OK;
1627 bool nsObjectLoadingContent::IsAboutBlankLoadOntoInitialAboutBlank(
1628 nsIURI* aURI, bool aInheritPrincipal, nsIPrincipal* aPrincipalToInherit) {
1629 return NS_IsAboutBlank(aURI) && aInheritPrincipal &&
1630 (!mFrameLoader || !mFrameLoader->GetExistingDocShell() ||
1631 mFrameLoader->GetExistingDocShell()
1632 ->IsAboutBlankLoadOntoInitialAboutBlank(aURI, aInheritPrincipal,
1633 aPrincipalToInherit));
1636 nsresult nsObjectLoadingContent::OpenChannel() {
1637 nsCOMPtr<nsIContent> thisContent =
1638 do_QueryInterface(static_cast<nsIImageLoadingContent*>(this));
1639 NS_ASSERTION(thisContent, "must be a content");
1640 Document* doc = thisContent->OwnerDoc();
1641 NS_ASSERTION(doc, "No owner document?");
1643 nsresult rv;
1644 mChannel = nullptr;
1646 // E.g. mms://
1647 if (!mURI || !CanHandleURI(mURI)) {
1648 return NS_ERROR_NOT_AVAILABLE;
1651 nsCOMPtr<nsILoadGroup> group = doc->GetDocumentLoadGroup();
1652 nsCOMPtr<nsIChannel> chan;
1653 RefPtr<ObjectInterfaceRequestorShim> shim =
1654 new ObjectInterfaceRequestorShim(this);
1656 bool inheritAttrs = nsContentUtils::ChannelShouldInheritPrincipal(
1657 thisContent->NodePrincipal(), // aLoadState->PrincipalToInherit()
1658 mURI, // aLoadState->URI()
1659 true, // aInheritForAboutBlank
1660 false); // aForceInherit
1662 bool inheritPrincipal = inheritAttrs && !SchemeIsData(mURI);
1664 nsSecurityFlags securityFlags =
1665 nsILoadInfo::SEC_ALLOW_CROSS_ORIGIN_SEC_CONTEXT_IS_NULL;
1666 if (inheritPrincipal) {
1667 securityFlags |= nsILoadInfo::SEC_FORCE_INHERIT_PRINCIPAL;
1670 nsContentPolicyType contentPolicyType = GetContentPolicyType();
1671 // The setting of LOAD_BYPASS_SERVICE_WORKER here is now an optimization.
1672 // ServiceWorkerInterceptController::ShouldPrepareForIntercept does a more
1673 // expensive check of BrowsingContext ancestors to look for object/embed.
1674 nsLoadFlags loadFlags = nsIChannel::LOAD_CALL_CONTENT_SNIFFERS |
1675 nsIChannel::LOAD_BYPASS_SERVICE_WORKER |
1676 nsIRequest::LOAD_HTML_OBJECT_DATA;
1677 uint32_t sandboxFlags = doc->GetSandboxFlags();
1679 // For object loads we store the CSP that potentially needs to
1680 // be inherited, e.g. in case we are loading an opaque origin
1681 // like a data: URI. The actual inheritance check happens within
1682 // Document::InitCSP(). Please create an actual copy of the CSP
1683 // (do not share the same reference) otherwise a Meta CSP of an
1684 // opaque origin will incorrectly be propagated to the embedding
1685 // document.
1686 RefPtr<nsCSPContext> cspToInherit;
1687 if (nsCOMPtr<nsIContentSecurityPolicy> csp = doc->GetCsp()) {
1688 cspToInherit = new nsCSPContext();
1689 cspToInherit->InitFromOther(static_cast<nsCSPContext*>(csp.get()));
1692 // --- Create LoadInfo
1693 RefPtr<LoadInfo> loadInfo = new LoadInfo(
1694 /*aLoadingPrincipal = aLoadingContext->NodePrincipal() */ nullptr,
1695 /*aTriggeringPrincipal = aLoadingPrincipal */ nullptr,
1696 /*aLoadingContext = */ thisContent,
1697 /*aSecurityFlags = */ securityFlags,
1698 /*aContentPolicyType = */ contentPolicyType,
1699 /*aLoadingClientInfo = */ Nothing(),
1700 /*aController = */ Nothing(),
1701 /*aSandboxFlags = */ sandboxFlags);
1703 if (inheritAttrs) {
1704 loadInfo->SetPrincipalToInherit(thisContent->NodePrincipal());
1707 if (cspToInherit) {
1708 loadInfo->SetCSPToInherit(cspToInherit);
1711 if (DocumentChannel::CanUseDocumentChannel(mURI) &&
1712 !IsAboutBlankLoadOntoInitialAboutBlank(mURI, inheritPrincipal,
1713 thisContent->NodePrincipal())) {
1714 // --- Create LoadState
1715 RefPtr<nsDocShellLoadState> loadState = new nsDocShellLoadState(mURI);
1716 loadState->SetPrincipalToInherit(thisContent->NodePrincipal());
1717 loadState->SetTriggeringPrincipal(loadInfo->TriggeringPrincipal());
1718 if (cspToInherit) {
1719 loadState->SetCsp(cspToInherit);
1721 loadState->SetTriggeringSandboxFlags(sandboxFlags);
1723 // TODO(djg): This was httpChan->SetReferrerInfoWithoutClone(referrerInfo);
1724 // Is the ...WithoutClone(...) important?
1725 auto referrerInfo = MakeRefPtr<ReferrerInfo>(*doc);
1726 loadState->SetReferrerInfo(referrerInfo);
1728 chan =
1729 DocumentChannel::CreateForObject(loadState, loadInfo, loadFlags, shim);
1730 MOZ_ASSERT(chan);
1731 // NS_NewChannel sets the group on the channel. CreateDocumentChannel does
1732 // not.
1733 chan->SetLoadGroup(group);
1734 } else {
1735 rv = NS_NewChannelInternal(getter_AddRefs(chan), // outChannel
1736 mURI, // aUri
1737 loadInfo, // aLoadInfo
1738 nullptr, // aPerformanceStorage
1739 group, // aLoadGroup
1740 shim, // aCallbacks
1741 loadFlags, // aLoadFlags
1742 nullptr); // aIoService
1743 NS_ENSURE_SUCCESS(rv, rv);
1745 if (inheritAttrs) {
1746 nsCOMPtr<nsILoadInfo> loadinfo = chan->LoadInfo();
1747 loadinfo->SetPrincipalToInherit(thisContent->NodePrincipal());
1750 // For object loads we store the CSP that potentially needs to
1751 // be inherited, e.g. in case we are loading an opaque origin
1752 // like a data: URI. The actual inheritance check happens within
1753 // Document::InitCSP(). Please create an actual copy of the CSP
1754 // (do not share the same reference) otherwise a Meta CSP of an
1755 // opaque origin will incorrectly be propagated to the embedding
1756 // document.
1757 if (cspToInherit) {
1758 nsCOMPtr<nsILoadInfo> loadinfo = chan->LoadInfo();
1759 static_cast<LoadInfo*>(loadinfo.get())->SetCSPToInherit(cspToInherit);
1763 // Referrer
1764 nsCOMPtr<nsIHttpChannel> httpChan(do_QueryInterface(chan));
1765 if (httpChan) {
1766 auto referrerInfo = MakeRefPtr<ReferrerInfo>(*doc);
1768 rv = httpChan->SetReferrerInfoWithoutClone(referrerInfo);
1769 MOZ_ASSERT(NS_SUCCEEDED(rv));
1771 // Set the initiator type
1772 nsCOMPtr<nsITimedChannel> timedChannel(do_QueryInterface(httpChan));
1773 if (timedChannel) {
1774 timedChannel->SetInitiatorType(thisContent->LocalName());
1777 nsCOMPtr<nsIClassOfService> cos(do_QueryInterface(httpChan));
1778 if (cos && UserActivation::IsHandlingUserInput()) {
1779 cos->AddClassFlags(nsIClassOfService::UrgentStart);
1783 nsCOMPtr<nsIScriptChannel> scriptChannel = do_QueryInterface(chan);
1784 if (scriptChannel) {
1785 // Allow execution against our context if the principals match
1786 scriptChannel->SetExecutionPolicy(nsIScriptChannel::EXECUTE_NORMAL);
1789 // AsyncOpen can fail if a file does not exist.
1790 rv = chan->AsyncOpen(shim);
1791 NS_ENSURE_SUCCESS(rv, rv);
1792 LOG(("OBJLC [%p]: Channel opened", this));
1793 mChannel = chan;
1794 return NS_OK;
1797 uint32_t nsObjectLoadingContent::GetCapabilities() const {
1798 return eSupportImages | eSupportPlugins | eSupportDocuments;
1801 void nsObjectLoadingContent::Destroy() {
1802 if (mFrameLoader) {
1803 mFrameLoader->Destroy();
1804 mFrameLoader = nullptr;
1807 // Reset state so that if the element is re-appended to tree again (e.g.
1808 // adopting to another document), it will reload resource again.
1809 UnloadObject();
1811 nsImageLoadingContent::Destroy();
1814 /* static */
1815 void nsObjectLoadingContent::Traverse(nsObjectLoadingContent* tmp,
1816 nsCycleCollectionTraversalCallback& cb) {
1817 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mFrameLoader);
1820 /* static */
1821 void nsObjectLoadingContent::Unlink(nsObjectLoadingContent* tmp) {
1822 if (tmp->mFrameLoader) {
1823 tmp->mFrameLoader->Destroy();
1825 NS_IMPL_CYCLE_COLLECTION_UNLINK(mFrameLoader);
1828 void nsObjectLoadingContent::UnloadObject(bool aResetState) {
1829 // Don't notify in CancelImageRequests until we transition to a new loaded
1830 // state, but not if we've loaded the image in a synthetic browsing context.
1831 CancelImageRequests(false);
1832 if (mFrameLoader) {
1833 mFrameLoader->Destroy();
1834 mFrameLoader = nullptr;
1837 if (aResetState) {
1838 CloseChannel();
1839 mChannelLoaded = false;
1840 mType = eType_Loading;
1841 mURI = mOriginalURI = mBaseURI = nullptr;
1842 mContentType.Truncate();
1843 mOriginalContentType.Truncate();
1846 mScriptRequested = false;
1848 mIsStopping = false;
1850 mCachedAttributes.Clear();
1851 mCachedParameters.Clear();
1853 mSubdocumentIntrinsicSize.reset();
1854 mSubdocumentIntrinsicRatio.reset();
1857 void nsObjectLoadingContent::NotifyStateChanged(ObjectType aOldType,
1858 bool aNotify) {
1859 LOG(("OBJLC [%p]: NotifyStateChanged: (%u) -> (%u) (notify %i)", this,
1860 aOldType, mType, aNotify));
1862 dom::Element* thisEl = AsContent()->AsElement();
1863 if (mType != eType_Image) {
1864 // Non-images are always not broken.
1865 thisEl->RemoveStates(ElementState::BROKEN, aNotify);
1868 if (mType == aOldType) {
1869 return;
1872 Document* doc = thisEl->GetComposedDoc();
1873 if (!doc) {
1874 return; // Nothing to do
1877 PresShell* presShell = doc->GetPresShell();
1878 // If there is no PresShell or it hasn't been initialized there isn't much to
1879 // do.
1880 if (!presShell || !presShell->DidInitialize()) {
1881 return;
1883 presShell->PostRecreateFramesFor(thisEl);
1886 nsObjectLoadingContent::ObjectType nsObjectLoadingContent::GetTypeOfContent(
1887 const nsCString& aMIMEType, bool aNoFakePlugin) {
1888 nsCOMPtr<nsIContent> thisContent =
1889 do_QueryInterface(static_cast<nsIImageLoadingContent*>(this));
1890 NS_ASSERTION(thisContent, "must be a content");
1892 // Images, documents and (fake) plugins are always supported.
1893 MOZ_ASSERT(GetCapabilities() &
1894 (eSupportImages | eSupportDocuments | eSupportPlugins));
1896 LOG(
1897 ("OBJLC [%p]: calling HtmlObjectContentTypeForMIMEType: aMIMEType: %s - "
1898 "thisContent: %p\n",
1899 this, aMIMEType.get(), thisContent.get()));
1900 auto ret =
1901 static_cast<ObjectType>(nsContentUtils::HtmlObjectContentTypeForMIMEType(
1902 aMIMEType, aNoFakePlugin));
1903 LOG(("OBJLC [%p]: called HtmlObjectContentTypeForMIMEType\n", this));
1904 return ret;
1907 void nsObjectLoadingContent::CreateStaticClone(
1908 nsObjectLoadingContent* aDest) const {
1909 aDest->mType = mType;
1911 if (mFrameLoader) {
1912 nsCOMPtr<nsIContent> content =
1913 do_QueryInterface(static_cast<nsIImageLoadingContent*>(aDest));
1914 Document* doc = content->OwnerDoc();
1915 if (doc->IsStaticDocument()) {
1916 doc->AddPendingFrameStaticClone(aDest, mFrameLoader);
1921 NS_IMETHODIMP
1922 nsObjectLoadingContent::GetSrcURI(nsIURI** aURI) {
1923 NS_IF_ADDREF(*aURI = GetSrcURI());
1924 return NS_OK;
1927 void nsObjectLoadingContent::ConfigureFallback() {
1928 MOZ_ASSERT(!mFrameLoader && !mChannel,
1929 "ConfigureFallback called with loaded content");
1931 // We only fallback in special cases where we are already of fallback
1932 // type (e.g. removed Flash plugin use) or where something went wrong
1933 // (e.g. unknown MIME type).
1934 MOZ_ASSERT(mType == eType_Fallback || mType == eType_Null);
1936 nsCOMPtr<nsIContent> thisContent =
1937 do_QueryInterface(static_cast<nsIImageLoadingContent*>(this));
1938 NS_ASSERTION(thisContent, "must be a content");
1940 // There are two types of fallback:
1941 // 1. HTML fallbacks are children of the <object> or <embed> DOM element.
1942 // 2. The special transparent region fallback replacing Flash use.
1943 // If our type is eType_Fallback (e.g. Flash use) then we use #1 if
1944 // available, otherwise we use #2.
1945 // If our type is eType_Null (e.g. unknown MIME type) then we use
1946 // #1, otherwise the element has no size.
1947 bool hasHtmlFallback = false;
1948 if (thisContent->IsHTMLElement(nsGkAtoms::object)) {
1949 // Do a depth-first traverse of node tree with the current element as root,
1950 // looking for non-<param> elements. If we find some then we have an HTML
1951 // fallback for this element.
1952 for (nsIContent* child = thisContent->GetFirstChild(); child;) {
1953 hasHtmlFallback =
1954 hasHtmlFallback || (!child->IsHTMLElement(nsGkAtoms::param) &&
1955 nsStyleUtil::IsSignificantChild(child, false));
1957 // <object> and <embed> elements in the fallback need to StartObjectLoad.
1958 // Their children should be ignored since they are part of those
1959 // element's fallback.
1960 if (auto embed = HTMLEmbedElement::FromNode(child)) {
1961 embed->StartObjectLoad(true, true);
1962 // Skip the children
1963 child = child->GetNextNonChildNode(thisContent);
1964 } else if (auto object = HTMLObjectElement::FromNode(child)) {
1965 object->StartObjectLoad(true, true);
1966 // Skip the children
1967 child = child->GetNextNonChildNode(thisContent);
1968 } else {
1969 child = child->GetNextNode(thisContent);
1974 // If we find an HTML fallback then we always switch type to null.
1975 if (hasHtmlFallback) {
1976 mType = eType_Null;
1980 NS_IMETHODIMP
1981 nsObjectLoadingContent::Reload(bool aClearActivation) {
1982 if (aClearActivation) {
1983 mSkipFakePlugins = false;
1986 return LoadObject(true, true);
1989 NS_IMETHODIMP
1990 nsObjectLoadingContent::SkipFakePlugins() {
1991 if (!nsContentUtils::IsCallerChrome()) return NS_ERROR_NOT_AVAILABLE;
1993 mSkipFakePlugins = true;
1995 // If we're showing a fake plugin now, reload
1996 if (mType == eType_FakePlugin) {
1997 return LoadObject(true, true);
2000 return NS_OK;
2003 NS_IMETHODIMP
2004 nsObjectLoadingContent::UpgradeLoadToDocument(
2005 nsIChannel* aRequest, BrowsingContext** aBrowsingContext) {
2006 AUTO_PROFILER_LABEL("nsObjectLoadingContent::UpgradeLoadToDocument", NETWORK);
2008 LOG(("OBJLC [%p]: UpgradeLoadToDocument", this));
2010 if (aRequest != mChannel || !aRequest) {
2011 // happens when a new load starts before the previous one got here.
2012 return NS_BINDING_ABORTED;
2015 // We should be state loading.
2016 if (mType != eType_Loading) {
2017 MOZ_ASSERT_UNREACHABLE("Should be type loading at this point");
2018 return NS_BINDING_ABORTED;
2020 MOZ_ASSERT(!mChannelLoaded, "mChannelLoaded set already?");
2021 MOZ_ASSERT(!mFinalListener, "mFinalListener exists already?");
2023 mChannelLoaded = true;
2025 // We don't need to check for errors here, unlike in `OnStartRequest`, as
2026 // `UpgradeLoadToDocument` is only called when the load is going to become a
2027 // process-switching load. As we never process switch for failed object loads,
2028 // we know our channel status is successful.
2030 // Call `LoadObject` to trigger our nsObjectLoadingContext to switch into the
2031 // specified new state.
2032 nsresult rv = LoadObject(true, false, aRequest);
2033 if (NS_WARN_IF(NS_FAILED(rv))) {
2034 return rv;
2037 RefPtr<BrowsingContext> bc = GetBrowsingContext();
2038 if (!bc) {
2039 return NS_ERROR_FAILURE;
2042 bc.forget(aBrowsingContext);
2043 return NS_OK;
2046 uint32_t nsObjectLoadingContent::GetRunID(SystemCallerGuarantee,
2047 ErrorResult& aRv) {
2048 if (!mHasRunID) {
2049 // The plugin instance must not have a run ID, so we must
2050 // be running the plugin in-process.
2051 aRv.Throw(NS_ERROR_NOT_IMPLEMENTED);
2052 return 0;
2054 return mRunID;
2057 bool nsObjectLoadingContent::ShouldBlockContent() {
2058 if (mContentBlockingEnabled && mURI && IsFlashMIME(mContentType) &&
2059 StaticPrefs::browser_safebrowsing_blockedURIs_enabled()) {
2060 return true;
2063 return false;
2066 Document* nsObjectLoadingContent::GetContentDocument(
2067 nsIPrincipal& aSubjectPrincipal) {
2068 nsCOMPtr<nsIContent> thisContent =
2069 do_QueryInterface(static_cast<nsIImageLoadingContent*>(this));
2071 if (!thisContent->IsInComposedDoc()) {
2072 return nullptr;
2075 Document* sub_doc = thisContent->OwnerDoc()->GetSubDocumentFor(thisContent);
2076 if (!sub_doc) {
2077 return nullptr;
2080 // Return null for cross-origin contentDocument.
2081 if (!aSubjectPrincipal.SubsumesConsideringDomain(sub_doc->NodePrincipal())) {
2082 return nullptr;
2085 return sub_doc;
2088 bool nsObjectLoadingContent::DoResolve(
2089 JSContext* aCx, JS::Handle<JSObject*> aObject, JS::Handle<jsid> aId,
2090 JS::MutableHandle<mozilla::Maybe<JS::PropertyDescriptor>> aDesc) {
2091 return true;
2094 /* static */
2095 bool nsObjectLoadingContent::MayResolve(jsid aId) {
2096 // We can resolve anything, really.
2097 return true;
2100 void nsObjectLoadingContent::GetOwnPropertyNames(
2101 JSContext* aCx, JS::MutableHandleVector<jsid> /* unused */,
2102 bool /* unused */, ErrorResult& aRv) {}
2104 void nsObjectLoadingContent::MaybeFireErrorEvent() {
2105 nsCOMPtr<nsIContent> thisContent =
2106 do_QueryInterface(static_cast<nsIImageLoadingContent*>(this));
2107 // Queue a task to fire an error event if we're an <object> element. The
2108 // queueing is important, since then we don't have to worry about reentry.
2109 if (thisContent->IsHTMLElement(nsGkAtoms::object)) {
2110 RefPtr<AsyncEventDispatcher> loadBlockingAsyncDispatcher =
2111 new LoadBlockingAsyncEventDispatcher(
2112 thisContent, u"error"_ns, CanBubble::eNo, ChromeOnlyDispatch::eNo);
2113 loadBlockingAsyncDispatcher->PostDOMEvent();
2117 bool nsObjectLoadingContent::BlockEmbedOrObjectContentLoading() {
2118 nsCOMPtr<nsIContent> thisContent =
2119 do_QueryInterface(static_cast<nsIImageLoadingContent*>(this));
2121 // Traverse up the node tree to see if we have any ancestors that may block us
2122 // from loading
2123 for (nsIContent* parent = thisContent->GetParent(); parent;
2124 parent = parent->GetParent()) {
2125 if (parent->IsAnyOfHTMLElements(nsGkAtoms::video, nsGkAtoms::audio)) {
2126 return true;
2128 // If we have an ancestor that is an object with a source, it'll have an
2129 // associated displayed type. If that type is not null, don't load content
2130 // for the embed.
2131 if (HTMLObjectElement* object = HTMLObjectElement::FromNode(parent)) {
2132 uint32_t type = object->DisplayedType();
2133 if (type != eType_Null) {
2134 return true;
2138 return false;
2141 void nsObjectLoadingContent::SubdocumentIntrinsicSizeOrRatioChanged(
2142 const Maybe<IntrinsicSize>& aIntrinsicSize,
2143 const Maybe<AspectRatio>& aIntrinsicRatio) {
2144 if (aIntrinsicSize == mSubdocumentIntrinsicSize &&
2145 aIntrinsicRatio == mSubdocumentIntrinsicRatio) {
2146 return;
2149 mSubdocumentIntrinsicSize = aIntrinsicSize;
2150 mSubdocumentIntrinsicRatio = aIntrinsicRatio;
2152 if (nsSubDocumentFrame* sdf = do_QueryFrame(AsContent()->GetPrimaryFrame())) {
2153 sdf->SubdocumentIntrinsicSizeOrRatioChanged();
2157 void nsObjectLoadingContent::SubdocumentImageLoadComplete(nsresult aResult) {
2158 ObjectType oldType = mType;
2159 mLoadingSyntheticDocument = false;
2161 if (NS_FAILED(aResult)) {
2162 UnloadObject();
2163 mType = eType_Fallback;
2164 ConfigureFallback();
2165 NotifyStateChanged(oldType, true);
2166 return;
2169 // (mChannelLoaded && mChannel) indicates this is a good state, not any sort
2170 // of failures.
2171 MOZ_DIAGNOSTIC_ASSERT_IF(mChannelLoaded && mChannel, mType == eType_Document);
2172 NotifyStateChanged(oldType, true);
2175 void nsObjectLoadingContent::MaybeStoreCrossOriginFeaturePolicy() {
2176 MOZ_DIAGNOSTIC_ASSERT(mFrameLoader);
2178 // If the browsingContext is not ready (because docshell is dead), don't try
2179 // to create one.
2180 if (!mFrameLoader->IsRemoteFrame() && !mFrameLoader->GetExistingDocShell()) {
2181 return;
2184 RefPtr<BrowsingContext> browsingContext = mFrameLoader->GetBrowsingContext();
2186 if (!browsingContext || !browsingContext->IsContentSubframe()) {
2187 return;
2190 nsCOMPtr<nsIContent> thisContent = AsContent();
2192 if (!thisContent->IsInComposedDoc()) {
2193 return;
2196 FeaturePolicy* featurePolicy = thisContent->OwnerDoc()->FeaturePolicy();
2198 if (ContentChild* cc = ContentChild::GetSingleton()) {
2199 Unused << cc->SendSetContainerFeaturePolicy(browsingContext, featurePolicy);