Bug 1750871 - run mochitest-remote on fission everywhere. r=releng-reviewers,aki
[gecko.git] / dom / base / nsObjectLoadingContent.cpp
bloba9400a1e19967cd6556021fbd214174d3e1caace
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/EventStates.h"
83 #include "mozilla/IMEStateManager.h"
84 #include "mozilla/widget/IMEData.h"
85 #include "mozilla/IntegerPrintfMacros.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/LoadInfo.h"
94 #include "mozilla/PresShell.h"
95 #include "mozilla/ProfilerLabels.h"
96 #include "mozilla/StaticPrefs_browser.h"
97 #include "mozilla/StaticPrefs_security.h"
98 #include "nsChannelClassifier.h"
99 #include "nsFocusManager.h"
100 #include "ReferrerInfo.h"
101 #include "nsIEffectiveTLDService.h"
103 #ifdef XP_WIN
104 // Thanks so much, Microsoft! :(
105 # ifdef CreateEvent
106 # undef CreateEvent
107 # endif
108 #endif // XP_WIN
110 static const char kPrefYoutubeRewrite[] = "plugins.rewrite_youtube_embeds";
112 using namespace mozilla;
113 using namespace mozilla::dom;
114 using namespace mozilla::net;
116 static LogModule* GetObjectLog() {
117 static LazyLogModule sLog("objlc");
118 return sLog;
121 #define LOG(args) MOZ_LOG(GetObjectLog(), mozilla::LogLevel::Debug, args)
122 #define LOG_ENABLED() MOZ_LOG_TEST(GetObjectLog(), mozilla::LogLevel::Debug)
124 static bool IsFlashMIME(const nsACString& aMIMEType) {
125 return nsPluginHost::GetSpecialType(aMIMEType) ==
126 nsPluginHost::eSpecialType_Flash;
129 static bool InActiveDocument(nsIContent* aContent) {
130 if (!aContent->IsInComposedDoc()) {
131 return false;
133 Document* doc = aContent->OwnerDoc();
134 return (doc && doc->IsActive());
137 static bool IsPluginType(nsObjectLoadingContent::ObjectType type) {
138 return type == nsObjectLoadingContent::eType_Fallback ||
139 type == nsObjectLoadingContent::eType_FakePlugin;
143 /// Runnables and helper classes
146 class nsAsyncInstantiateEvent : public Runnable {
147 public:
148 explicit nsAsyncInstantiateEvent(nsObjectLoadingContent* aContent)
149 : Runnable("nsAsyncInstantiateEvent"), mContent(aContent) {}
151 ~nsAsyncInstantiateEvent() override = default;
153 NS_IMETHOD Run() override;
155 private:
156 nsCOMPtr<nsIObjectLoadingContent> mContent;
159 NS_IMETHODIMP
160 nsAsyncInstantiateEvent::Run() {
161 nsObjectLoadingContent* objLC =
162 static_cast<nsObjectLoadingContent*>(mContent.get());
164 // If objLC is no longer tracking this event, we've been canceled or
165 // superseded
166 if (objLC->mPendingInstantiateEvent != this) {
167 return NS_OK;
169 objLC->mPendingInstantiateEvent = nullptr;
171 return objLC->SyncStartPluginInstance();
174 // Checks to see if the content for a plugin instance should be unloaded
175 // (outside an active document) or stopped (in a document but unrendered). This
176 // is used to allow scripts to move a plugin around the document hierarchy
177 // without re-instantiating it.
178 class CheckPluginStopEvent : public Runnable {
179 public:
180 explicit CheckPluginStopEvent(nsObjectLoadingContent* aContent)
181 : Runnable("CheckPluginStopEvent"), mContent(aContent) {}
183 ~CheckPluginStopEvent() override = default;
185 NS_IMETHOD Run() override;
187 private:
188 nsCOMPtr<nsIObjectLoadingContent> mContent;
191 NS_IMETHODIMP
192 CheckPluginStopEvent::Run() {
193 nsObjectLoadingContent* objLC =
194 static_cast<nsObjectLoadingContent*>(mContent.get());
196 // If objLC is no longer tracking this event, we've been canceled or
197 // superseded. We clear this before we finish - either by calling
198 // UnloadObject/StopPluginInstance, or directly if we took no action.
199 if (objLC->mPendingCheckPluginStopEvent != this) {
200 return NS_OK;
203 // CheckPluginStopEvent is queued when we either lose our frame, are removed
204 // from the document, or the document goes inactive. To avoid stopping the
205 // plugin when script is reparenting us or layout is rebuilding, we wait until
206 // this event to decide to stop.
208 nsCOMPtr<nsIContent> content =
209 do_QueryInterface(static_cast<nsIImageLoadingContent*>(objLC));
210 if (!InActiveDocument(content)) {
211 LOG(("OBJLC [%p]: Unloading plugin outside of document", this));
212 objLC->StopPluginInstance();
213 return NS_OK;
216 if (content->GetPrimaryFrame()) {
217 LOG(
218 ("OBJLC [%p]: CheckPluginStopEvent - in active document with frame"
219 ", no action",
220 this));
221 objLC->mPendingCheckPluginStopEvent = nullptr;
222 return NS_OK;
225 // In an active document, but still no frame. Flush layout to see if we can
226 // regain a frame now.
227 LOG(("OBJLC [%p]: CheckPluginStopEvent - No frame, flushing layout", this));
228 Document* composedDoc = content->GetComposedDoc();
229 if (composedDoc) {
230 composedDoc->FlushPendingNotifications(FlushType::Layout);
231 if (objLC->mPendingCheckPluginStopEvent != this) {
232 LOG(("OBJLC [%p]: CheckPluginStopEvent - superseded in layout flush",
233 this));
234 return NS_OK;
236 if (content->GetPrimaryFrame()) {
237 LOG(("OBJLC [%p]: CheckPluginStopEvent - frame gained in layout flush",
238 this));
239 objLC->mPendingCheckPluginStopEvent = nullptr;
240 return NS_OK;
244 // Still no frame, suspend plugin.
245 LOG(("OBJLC [%p]: Stopping plugin that lost frame", this));
246 objLC->StopPluginInstance();
248 return NS_OK;
251 // Sets a object's mInstantiating bit to false when destroyed
252 class AutoSetLoadingToFalse {
253 public:
254 explicit AutoSetLoadingToFalse(nsObjectLoadingContent* aContent)
255 : mContent(aContent) {}
256 ~AutoSetLoadingToFalse() { mContent->mIsLoading = false; }
258 private:
259 nsObjectLoadingContent* mContent;
263 /// Helper functions
266 bool nsObjectLoadingContent::IsSuccessfulRequest(nsIRequest* aRequest,
267 nsresult* aStatus) {
268 nsresult rv = aRequest->GetStatus(aStatus);
269 if (NS_FAILED(rv) || NS_FAILED(*aStatus)) {
270 return false;
273 // This may still be an error page or somesuch
274 nsCOMPtr<nsIHttpChannel> httpChan(do_QueryInterface(aRequest));
275 if (httpChan) {
276 bool success;
277 rv = httpChan->GetRequestSucceeded(&success);
278 if (NS_FAILED(rv) || !success) {
279 return false;
283 // Otherwise, the request is successful
284 return true;
287 static bool CanHandleURI(nsIURI* aURI) {
288 nsAutoCString scheme;
289 if (NS_FAILED(aURI->GetScheme(scheme))) {
290 return false;
293 nsIIOService* ios = nsContentUtils::GetIOService();
294 if (!ios) return false;
296 nsCOMPtr<nsIProtocolHandler> handler;
297 ios->GetProtocolHandler(scheme.get(), getter_AddRefs(handler));
298 if (!handler) {
299 return false;
302 nsCOMPtr<nsIExternalProtocolHandler> extHandler = do_QueryInterface(handler);
303 // We can handle this URI if its protocol handler is not the external one
304 return extHandler == nullptr;
307 // Helper for tedious URI equality syntax when one or both arguments may be
308 // null and URIEquals(null, null) should be true
309 static bool inline URIEquals(nsIURI* a, nsIURI* b) {
310 bool equal;
311 return (!a && !b) || (a && b && NS_SUCCEEDED(a->Equals(b, &equal)) && equal);
315 /// Member Functions
318 // Helper to queue a CheckPluginStopEvent for a OBJLC object
319 void nsObjectLoadingContent::QueueCheckPluginStopEvent() {
320 nsCOMPtr<nsIRunnable> event = new CheckPluginStopEvent(this);
321 mPendingCheckPluginStopEvent = event;
323 NS_DispatchToCurrentThread(event);
326 // Helper to spawn the frameloader.
327 void nsObjectLoadingContent::SetupFrameLoader(int32_t aJSPluginId) {
328 nsCOMPtr<nsIContent> thisContent =
329 do_QueryInterface(static_cast<nsIImageLoadingContent*>(this));
330 NS_ASSERTION(thisContent, "must be a content");
332 mFrameLoader =
333 nsFrameLoader::Create(thisContent->AsElement(), mNetworkCreated);
334 MOZ_ASSERT(mFrameLoader, "nsFrameLoader::Create failed");
337 // Helper to spawn the frameloader and return a pointer to its docshell.
338 already_AddRefed<nsIDocShell> nsObjectLoadingContent::SetupDocShell(
339 nsIURI* aRecursionCheckURI) {
340 SetupFrameLoader(nsFakePluginTag::NOT_JSPLUGIN);
341 if (!mFrameLoader) {
342 return nullptr;
345 nsCOMPtr<nsIDocShell> docShell;
347 if (aRecursionCheckURI) {
348 nsresult rv = mFrameLoader->CheckForRecursiveLoad(aRecursionCheckURI);
349 if (NS_SUCCEEDED(rv)) {
350 IgnoredErrorResult result;
351 docShell = mFrameLoader->GetDocShell(result);
352 if (result.Failed()) {
353 MOZ_ASSERT_UNREACHABLE("Could not get DocShell from mFrameLoader?");
355 } else {
356 LOG(("OBJLC [%p]: Aborting recursive load", this));
360 if (!docShell) {
361 mFrameLoader->Destroy();
362 mFrameLoader = nullptr;
363 return nullptr;
366 return docShell.forget();
369 void nsObjectLoadingContent::UnbindFromTree(bool aNullParent) {
370 nsImageLoadingContent::UnbindFromTree(aNullParent);
372 if (mType != eType_Image) {
373 // nsImageLoadingContent handles the image case.
374 // Reset state and clear pending events
375 /// XXX(johns): The implementation for GenericFrame notes that ideally we
376 /// would keep the docshell around, but trash the frameloader
377 UnloadObject();
381 nsObjectLoadingContent::nsObjectLoadingContent()
382 : mType(eType_Loading),
383 mRunID(0),
384 mHasRunID(false),
385 mChannelLoaded(false),
386 mInstantiating(false),
387 mNetworkCreated(true),
388 mContentBlockingEnabled(false),
389 mSkipFakePlugins(false),
390 mIsStopping(false),
391 mIsLoading(false),
392 mScriptRequested(false),
393 mRewrittenYoutubeEmbed(false) {}
395 nsObjectLoadingContent::~nsObjectLoadingContent() {
396 // Should have been unbound from the tree at this point, and
397 // CheckPluginStopEvent keeps us alive
398 if (mFrameLoader) {
399 MOZ_ASSERT_UNREACHABLE(
400 "Should not be tearing down frame loaders at this point");
401 mFrameLoader->Destroy();
403 if (mInstantiating) {
404 // This is especially bad as delayed stop will try to hold on to this
405 // object...
406 MOZ_ASSERT_UNREACHABLE(
407 "Should not be tearing down a plugin at this point!");
408 StopPluginInstance();
410 nsImageLoadingContent::Destroy();
413 nsresult nsObjectLoadingContent::InstantiatePluginInstance(bool aIsLoading) {
414 return NS_ERROR_FAILURE;
417 void nsObjectLoadingContent::GetPluginAttributes(
418 nsTArray<MozPluginParameter>& aAttributes) {
419 aAttributes = mCachedAttributes.Clone();
422 void nsObjectLoadingContent::GetPluginParameters(
423 nsTArray<MozPluginParameter>& aParameters) {
424 aParameters = mCachedParameters.Clone();
427 void nsObjectLoadingContent::GetNestedParams(
428 nsTArray<MozPluginParameter>& aParams) {
429 nsCOMPtr<Element> ourElement =
430 do_QueryInterface(static_cast<nsIObjectLoadingContent*>(this));
432 nsCOMPtr<nsIHTMLCollection> allParams;
433 constexpr auto xhtml_ns = u"http://www.w3.org/1999/xhtml"_ns;
434 ErrorResult rv;
435 allParams = ourElement->GetElementsByTagNameNS(xhtml_ns, u"param"_ns, rv);
436 if (rv.Failed()) {
437 return;
439 MOZ_ASSERT(allParams);
441 uint32_t numAllParams = allParams->Length();
442 for (uint32_t i = 0; i < numAllParams; i++) {
443 RefPtr<Element> element = allParams->Item(i);
445 nsAutoString name;
446 element->GetAttr(nsGkAtoms::name, name);
448 if (name.IsEmpty()) continue;
450 nsCOMPtr<nsIContent> parent = element->GetParent();
451 RefPtr<HTMLObjectElement> objectElement;
452 while (!objectElement && parent) {
453 objectElement = HTMLObjectElement::FromNode(parent);
454 parent = parent->GetParent();
457 if (objectElement) {
458 parent = objectElement;
459 } else {
460 continue;
463 if (parent == ourElement) {
464 MozPluginParameter param;
465 element->GetAttr(nsGkAtoms::name, param.mName);
466 element->GetAttr(nsGkAtoms::value, param.mValue);
468 param.mName.Trim(" \n\r\t\b", true, true, false);
469 param.mValue.Trim(" \n\r\t\b", true, true, false);
471 aParams.AppendElement(param);
476 nsresult nsObjectLoadingContent::BuildParametersArray() {
477 if (mCachedAttributes.Length() || mCachedParameters.Length()) {
478 MOZ_ASSERT(false, "Parameters array should be empty.");
479 return NS_OK;
482 nsCOMPtr<Element> element =
483 do_QueryInterface(static_cast<nsIImageLoadingContent*>(this));
485 for (uint32_t i = 0; i != element->GetAttrCount(); i += 1) {
486 MozPluginParameter param;
487 const nsAttrName* attrName = element->GetAttrNameAt(i);
488 nsAtom* atom = attrName->LocalName();
489 element->GetAttr(attrName->NamespaceID(), atom, param.mValue);
490 atom->ToString(param.mName);
491 mCachedAttributes.AppendElement(param);
494 nsAutoCString wmodeOverride;
495 Preferences::GetCString("plugins.force.wmode", wmodeOverride);
497 for (uint32_t i = 0; i < mCachedAttributes.Length(); i++) {
498 if (!wmodeOverride.IsEmpty() &&
499 mCachedAttributes[i].mName.EqualsIgnoreCase("wmode")) {
500 CopyASCIItoUTF16(wmodeOverride, mCachedAttributes[i].mValue);
501 wmodeOverride.Truncate();
505 if (!wmodeOverride.IsEmpty()) {
506 MozPluginParameter param;
507 param.mName = u"wmode"_ns;
508 CopyASCIItoUTF16(wmodeOverride, param.mValue);
509 mCachedAttributes.AppendElement(param);
512 // Some plugins were never written to understand the "data" attribute of the
513 // OBJECT tag. Real and WMP will not play unless they find a "src" attribute,
514 // see bug 152334. Nav 4.x would simply replace the "data" with "src". Because
515 // some plugins correctly look for "data", lets instead copy the "data"
516 // attribute and add another entry to the bottom of the array if there isn't
517 // already a "src" specified.
518 if (element->IsHTMLElement(nsGkAtoms::object) &&
519 !element->HasAttr(kNameSpaceID_None, nsGkAtoms::src)) {
520 MozPluginParameter param;
521 element->GetAttr(kNameSpaceID_None, nsGkAtoms::data, param.mValue);
522 if (!param.mValue.IsEmpty()) {
523 param.mName = u"SRC"_ns;
524 mCachedAttributes.AppendElement(param);
528 GetNestedParams(mCachedParameters);
530 return NS_OK;
533 void nsObjectLoadingContent::NotifyOwnerDocumentActivityChanged() {
534 // XXX(johns): We cannot touch plugins or run arbitrary script from this call,
535 // as Document is in a non-reentrant state.
537 // If we have a plugin we want to queue an event to stop it unless we are
538 // moved into an active document before returning to the event loop.
539 if (mInstantiating) {
540 QueueCheckPluginStopEvent();
542 nsImageLoadingContent::NotifyOwnerDocumentActivityChanged();
545 // nsIRequestObserver
546 NS_IMETHODIMP
547 nsObjectLoadingContent::OnStartRequest(nsIRequest* aRequest) {
548 AUTO_PROFILER_LABEL("nsObjectLoadingContent::OnStartRequest", NETWORK);
550 LOG(("OBJLC [%p]: Channel OnStartRequest", this));
552 if (aRequest != mChannel || !aRequest) {
553 // happens when a new load starts before the previous one got here
554 return NS_BINDING_ABORTED;
557 nsCOMPtr<nsIChannel> chan(do_QueryInterface(aRequest));
558 NS_ASSERTION(chan, "Why is our request not a channel?");
560 nsresult status = NS_OK;
561 bool success = IsSuccessfulRequest(aRequest, &status);
563 // If we have already switched to type document, we're doing a
564 // process-switching DocumentChannel load. We should be able to pass down the
565 // load to our inner listener, but should also make sure to update our local
566 // state.
567 if (mType == eType_Document) {
568 if (!mFinalListener) {
569 MOZ_ASSERT_UNREACHABLE(
570 "Already are eType_Document, but don't have final listener yet?");
571 return NS_BINDING_ABORTED;
574 // If the load looks successful, fix up some of our local state before
575 // forwarding the request to the final URI loader.
577 // Forward load errors down to the document loader, so we don't tear down
578 // the nsDocShell ourselves.
579 if (success) {
580 LOG(("OBJLC [%p]: OnStartRequest: DocumentChannel request succeeded\n",
581 this));
582 nsCString channelType;
583 MOZ_ALWAYS_SUCCEEDS(mChannel->GetContentType(channelType));
585 if (GetTypeOfContent(channelType, mSkipFakePlugins) != eType_Document) {
586 MOZ_CRASH("DocumentChannel request with non-document MIME");
588 mContentType = channelType;
590 MOZ_ALWAYS_SUCCEEDS(
591 NS_GetFinalChannelURI(mChannel, getter_AddRefs(mURI)));
594 return mFinalListener->OnStartRequest(aRequest);
597 // Otherwise we should be state loading, and call LoadObject with the channel
598 if (mType != eType_Loading) {
599 MOZ_ASSERT_UNREACHABLE("Should be type loading at this point");
600 return NS_BINDING_ABORTED;
602 NS_ASSERTION(!mChannelLoaded, "mChannelLoaded set already?");
603 NS_ASSERTION(!mFinalListener, "mFinalListener exists already?");
605 mChannelLoaded = true;
607 if (status == NS_ERROR_BLOCKED_URI) {
608 nsCOMPtr<nsIConsoleService> console(
609 do_GetService("@mozilla.org/consoleservice;1"));
610 if (console) {
611 nsCOMPtr<nsIURI> uri;
612 chan->GetURI(getter_AddRefs(uri));
613 nsString message =
614 u"Blocking "_ns +
615 NS_ConvertASCIItoUTF16(uri->GetSpecOrDefault().get()) +
616 nsLiteralString(
617 u" since it was found on an internal Firefox blocklist.");
618 console->LogStringMessage(message.get());
620 mContentBlockingEnabled = true;
621 return NS_ERROR_FAILURE;
624 if (UrlClassifierFeatureFactory::IsClassifierBlockingErrorCode(status)) {
625 mContentBlockingEnabled = true;
626 return NS_ERROR_FAILURE;
629 if (!success) {
630 LOG(("OBJLC [%p]: OnStartRequest: Request failed\n", this));
631 // If the request fails, we still call LoadObject() to handle fallback
632 // content and notifying of failure. (mChannelLoaded && !mChannel) indicates
633 // the bad state.
634 mChannel = nullptr;
635 LoadObject(true, false);
636 return NS_ERROR_FAILURE;
639 return LoadObject(true, false, aRequest);
642 NS_IMETHODIMP
643 nsObjectLoadingContent::OnStopRequest(nsIRequest* aRequest,
644 nsresult aStatusCode) {
645 AUTO_PROFILER_LABEL("nsObjectLoadingContent::OnStopRequest", NETWORK);
647 // Handle object not loading error because source was a tracking URL (or
648 // fingerprinting, cryptomining, etc.).
649 // We make a note of this object node by including it in a dedicated
650 // array of blocked tracking nodes under its parent document.
651 if (UrlClassifierFeatureFactory::IsClassifierBlockingErrorCode(aStatusCode)) {
652 nsCOMPtr<nsIContent> thisNode =
653 do_QueryInterface(static_cast<nsIObjectLoadingContent*>(this));
654 if (thisNode && thisNode->IsInComposedDoc()) {
655 thisNode->GetComposedDoc()->AddBlockedNodeByClassifier(thisNode);
659 if (aRequest != mChannel) {
660 return NS_BINDING_ABORTED;
663 mChannel = nullptr;
665 if (mFinalListener) {
666 // This may re-enter in the case of plugin listeners
667 nsCOMPtr<nsIStreamListener> listenerGrip(mFinalListener);
668 mFinalListener = nullptr;
669 listenerGrip->OnStopRequest(aRequest, aStatusCode);
672 // Return value doesn't matter
673 return NS_OK;
676 // nsIStreamListener
677 NS_IMETHODIMP
678 nsObjectLoadingContent::OnDataAvailable(nsIRequest* aRequest,
679 nsIInputStream* aInputStream,
680 uint64_t aOffset, uint32_t aCount) {
681 if (aRequest != mChannel) {
682 return NS_BINDING_ABORTED;
685 if (mFinalListener) {
686 // This may re-enter in the case of plugin listeners
687 nsCOMPtr<nsIStreamListener> listenerGrip(mFinalListener);
688 return listenerGrip->OnDataAvailable(aRequest, aInputStream, aOffset,
689 aCount);
692 // We shouldn't have a connected channel with no final listener
693 MOZ_ASSERT_UNREACHABLE(
694 "Got data for channel with no connected final "
695 "listener");
696 mChannel = nullptr;
698 return NS_ERROR_UNEXPECTED;
701 void nsObjectLoadingContent::PresetOpenerWindow(
702 const Nullable<WindowProxyHolder>& aOpenerWindow, ErrorResult& aRv) {
703 aRv.Throw(NS_ERROR_FAILURE);
706 NS_IMETHODIMP
707 nsObjectLoadingContent::GetActualType(nsACString& aType) {
708 aType = mContentType;
709 return NS_OK;
712 NS_IMETHODIMP
713 nsObjectLoadingContent::GetDisplayedType(uint32_t* aType) {
714 *aType = DisplayedType();
715 return NS_OK;
718 NS_IMETHODIMP
719 nsObjectLoadingContent::GetContentTypeForMIMEType(const nsACString& aMIMEType,
720 uint32_t* aType) {
721 *aType = GetTypeOfContent(PromiseFlatCString(aMIMEType), false);
722 return NS_OK;
725 // nsIInterfaceRequestor
726 // We use a shim class to implement this so that JS consumers still
727 // see an interface requestor even though WebIDL bindings don't expose
728 // that stuff.
729 class ObjectInterfaceRequestorShim final : public nsIInterfaceRequestor,
730 public nsIChannelEventSink,
731 public nsIStreamListener {
732 public:
733 NS_DECL_CYCLE_COLLECTING_ISUPPORTS
734 NS_DECL_CYCLE_COLLECTION_CLASS_AMBIGUOUS(ObjectInterfaceRequestorShim,
735 nsIInterfaceRequestor)
736 NS_DECL_NSIINTERFACEREQUESTOR
737 // RefPtr<nsObjectLoadingContent> fails due to ambiguous AddRef/Release,
738 // hence the ugly static cast :(
739 NS_FORWARD_NSICHANNELEVENTSINK(
740 static_cast<nsObjectLoadingContent*>(mContent.get())->)
741 NS_FORWARD_NSISTREAMLISTENER(
742 static_cast<nsObjectLoadingContent*>(mContent.get())->)
743 NS_FORWARD_NSIREQUESTOBSERVER(
744 static_cast<nsObjectLoadingContent*>(mContent.get())->)
746 explicit ObjectInterfaceRequestorShim(nsIObjectLoadingContent* aContent)
747 : mContent(aContent) {}
749 protected:
750 ~ObjectInterfaceRequestorShim() = default;
751 nsCOMPtr<nsIObjectLoadingContent> mContent;
754 NS_IMPL_CYCLE_COLLECTION(ObjectInterfaceRequestorShim, mContent)
756 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(ObjectInterfaceRequestorShim)
757 NS_INTERFACE_MAP_ENTRY(nsIInterfaceRequestor)
758 NS_INTERFACE_MAP_ENTRY(nsIChannelEventSink)
759 NS_INTERFACE_MAP_ENTRY(nsIStreamListener)
760 NS_INTERFACE_MAP_ENTRY(nsIRequestObserver)
761 NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIInterfaceRequestor)
762 NS_INTERFACE_MAP_END
764 NS_IMPL_CYCLE_COLLECTING_ADDREF(ObjectInterfaceRequestorShim)
765 NS_IMPL_CYCLE_COLLECTING_RELEASE(ObjectInterfaceRequestorShim)
767 NS_IMETHODIMP
768 ObjectInterfaceRequestorShim::GetInterface(const nsIID& aIID, void** aResult) {
769 if (aIID.Equals(NS_GET_IID(nsIChannelEventSink))) {
770 nsIChannelEventSink* sink = this;
771 *aResult = sink;
772 NS_ADDREF(sink);
773 return NS_OK;
775 if (aIID.Equals(NS_GET_IID(nsIObjectLoadingContent))) {
776 nsIObjectLoadingContent* olc = mContent;
777 *aResult = olc;
778 NS_ADDREF(olc);
779 return NS_OK;
781 return NS_NOINTERFACE;
784 // nsIChannelEventSink
785 NS_IMETHODIMP
786 nsObjectLoadingContent::AsyncOnChannelRedirect(
787 nsIChannel* aOldChannel, nsIChannel* aNewChannel, uint32_t aFlags,
788 nsIAsyncVerifyRedirectCallback* cb) {
789 // If we're already busy with a new load, or have no load at all,
790 // cancel the redirect.
791 if (!mChannel || aOldChannel != mChannel) {
792 return NS_BINDING_ABORTED;
795 mChannel = aNewChannel;
797 if (mFinalListener) {
798 nsCOMPtr<nsIChannelEventSink> sink(do_QueryInterface(mFinalListener));
799 MOZ_RELEASE_ASSERT(sink, "mFinalListener isn't nsIChannelEventSink?");
800 if (mType != eType_Document) {
801 MOZ_ASSERT_UNREACHABLE(
802 "Not a DocumentChannel load, but we're getting a "
803 "AsyncOnChannelRedirect with a mFinalListener?");
804 return NS_BINDING_ABORTED;
807 return sink->AsyncOnChannelRedirect(aOldChannel, aNewChannel, aFlags, cb);
810 cb->OnRedirectVerifyCallback(NS_OK);
811 return NS_OK;
814 // <public>
815 EventStates nsObjectLoadingContent::ObjectState() const {
816 switch (mType) {
817 case eType_Loading:
818 return NS_EVENT_STATE_LOADING;
819 case eType_Image:
820 return ImageState();
821 case eType_FakePlugin:
822 case eType_Document:
823 // These are OK. If documents start to load successfully, they display
824 // something, and are thus not broken in this sense. The same goes for
825 // plugins.
826 return EventStates();
827 case eType_Fallback:
828 // This may end up handled as TYPE_NULL or as a "special" type, as
829 // chosen by the layout.use-plugin-fallback pref.
830 return EventStates();
831 case eType_Null:
832 return NS_EVENT_STATE_BROKEN;
834 MOZ_ASSERT_UNREACHABLE("unknown type?");
835 return NS_EVENT_STATE_LOADING;
838 void nsObjectLoadingContent::MaybeRewriteYoutubeEmbed(nsIURI* aURI,
839 nsIURI* aBaseURI,
840 nsIURI** aOutURI) {
841 nsCOMPtr<nsIContent> thisContent =
842 do_QueryInterface(static_cast<nsIImageLoadingContent*>(this));
843 NS_ASSERTION(thisContent, "Must be an instance of content");
845 // We're only interested in switching out embed and object tags
846 if (!thisContent->NodeInfo()->Equals(nsGkAtoms::embed) &&
847 !thisContent->NodeInfo()->Equals(nsGkAtoms::object)) {
848 return;
851 nsCOMPtr<nsIEffectiveTLDService> tldService =
852 do_GetService(NS_EFFECTIVETLDSERVICE_CONTRACTID);
853 // If we can't analyze the URL, just pass on through.
854 if (!tldService) {
855 NS_WARNING("Could not get TLD service!");
856 return;
859 nsAutoCString currentBaseDomain;
860 bool ok = NS_SUCCEEDED(tldService->GetBaseDomain(aURI, 0, currentBaseDomain));
861 if (!ok) {
862 // Data URIs (commonly used for things like svg embeds) won't parse
863 // correctly, so just fail silently here.
864 return;
867 // See if URL is referencing youtube
868 if (!currentBaseDomain.EqualsLiteral("youtube.com") &&
869 !currentBaseDomain.EqualsLiteral("youtube-nocookie.com")) {
870 return;
873 // We should only rewrite URLs with paths starting with "/v/", as we shouldn't
874 // touch object nodes with "/embed/" urls that already do that right thing.
875 nsAutoCString path;
876 aURI->GetPathQueryRef(path);
877 if (!StringBeginsWith(path, "/v/"_ns)) {
878 return;
881 // See if requester is planning on using the JS API.
882 nsAutoCString uri;
883 nsresult rv = aURI->GetSpec(uri);
884 if (NS_FAILED(rv)) {
885 return;
888 // Some YouTube urls have parameters in path components, e.g.
889 // http://youtube.com/embed/7LcUOEP7Brc&start=35. These URLs work with flash,
890 // but break iframe/object embedding. If this situation occurs with rewritten
891 // URLs, convert the parameters to query in order to make the video load
892 // correctly as an iframe. In either case, warn about it in the
893 // developer console.
894 int32_t ampIndex = uri.FindChar('&', 0);
895 bool replaceQuery = false;
896 if (ampIndex != -1) {
897 int32_t qmIndex = uri.FindChar('?', 0);
898 if (qmIndex == -1 || qmIndex > ampIndex) {
899 replaceQuery = true;
903 // If we're pref'd off, return after telemetry has been logged.
904 if (!Preferences::GetBool(kPrefYoutubeRewrite)) {
905 return;
908 nsAutoString utf16OldURI = NS_ConvertUTF8toUTF16(uri);
909 // If we need to convert the URL, it means an ampersand comes first.
910 // Use the index we found earlier.
911 if (replaceQuery) {
912 // Replace question marks with ampersands.
913 uri.ReplaceChar('?', '&');
914 // Replace the first ampersand with a question mark.
915 uri.SetCharAt('?', ampIndex);
917 // Switch out video access url formats, which should possibly allow HTML5
918 // video loading.
919 uri.ReplaceSubstring("/v/"_ns, "/embed/"_ns);
920 nsAutoString utf16URI = NS_ConvertUTF8toUTF16(uri);
921 rv = nsContentUtils::NewURIWithDocumentCharset(
922 aOutURI, utf16URI, thisContent->OwnerDoc(), aBaseURI);
923 if (NS_FAILED(rv)) {
924 return;
926 AutoTArray<nsString, 2> params = {utf16OldURI, utf16URI};
927 const char* msgName;
928 // If there's no query to rewrite, just notify in the developer console
929 // that we're changing the embed.
930 if (!replaceQuery) {
931 msgName = "RewriteYouTubeEmbed";
932 } else {
933 msgName = "RewriteYouTubeEmbedPathParams";
935 nsContentUtils::ReportToConsole(
936 nsIScriptError::warningFlag, "Plugins"_ns, thisContent->OwnerDoc(),
937 nsContentUtils::eDOM_PROPERTIES, msgName, params);
940 bool nsObjectLoadingContent::CheckLoadPolicy(int16_t* aContentPolicy) {
941 if (!aContentPolicy || !mURI) {
942 MOZ_ASSERT_UNREACHABLE("Doing it wrong");
943 return false;
946 nsCOMPtr<nsIContent> thisContent =
947 do_QueryInterface(static_cast<nsIImageLoadingContent*>(this));
948 NS_ASSERTION(thisContent, "Must be an instance of content");
950 Document* doc = thisContent->OwnerDoc();
952 nsContentPolicyType contentPolicyType = GetContentPolicyType();
954 nsCOMPtr<nsILoadInfo> secCheckLoadInfo = new LoadInfo(
955 doc->NodePrincipal(), // loading principal
956 doc->NodePrincipal(), // triggering principal
957 thisContent, nsILoadInfo::SEC_ONLY_FOR_EXPLICIT_CONTENTSEC_CHECK,
958 contentPolicyType);
960 *aContentPolicy = nsIContentPolicy::ACCEPT;
961 nsresult rv = NS_CheckContentLoadPolicy(mURI, secCheckLoadInfo, mContentType,
962 aContentPolicy,
963 nsContentUtils::GetContentPolicy());
964 NS_ENSURE_SUCCESS(rv, false);
965 if (NS_CP_REJECTED(*aContentPolicy)) {
966 LOG(("OBJLC [%p]: Content policy denied load of %s", this,
967 mURI->GetSpecOrDefault().get()));
968 return false;
971 return true;
974 bool nsObjectLoadingContent::CheckProcessPolicy(int16_t* aContentPolicy) {
975 if (!aContentPolicy) {
976 MOZ_ASSERT_UNREACHABLE("Null out variable");
977 return false;
980 nsCOMPtr<nsIContent> thisContent =
981 do_QueryInterface(static_cast<nsIImageLoadingContent*>(this));
982 NS_ASSERTION(thisContent, "Must be an instance of content");
984 Document* doc = thisContent->OwnerDoc();
986 nsContentPolicyType objectType;
987 switch (mType) {
988 case eType_Image:
989 objectType = nsIContentPolicy::TYPE_INTERNAL_IMAGE;
990 break;
991 case eType_Document:
992 objectType = nsIContentPolicy::TYPE_DOCUMENT;
993 break;
994 case eType_Fallback:
995 case eType_FakePlugin:
996 objectType = GetContentPolicyType();
997 break;
998 default:
999 MOZ_ASSERT_UNREACHABLE(
1000 "Calling checkProcessPolicy with an unloadable "
1001 "type");
1002 return false;
1005 nsCOMPtr<nsILoadInfo> secCheckLoadInfo = new LoadInfo(
1006 doc->NodePrincipal(), // loading principal
1007 doc->NodePrincipal(), // triggering principal
1008 thisContent, nsILoadInfo::SEC_ONLY_FOR_EXPLICIT_CONTENTSEC_CHECK,
1009 objectType);
1011 *aContentPolicy = nsIContentPolicy::ACCEPT;
1012 nsresult rv = NS_CheckContentProcessPolicy(
1013 mURI ? mURI : mBaseURI, secCheckLoadInfo, mContentType, aContentPolicy,
1014 nsContentUtils::GetContentPolicy());
1015 NS_ENSURE_SUCCESS(rv, false);
1017 if (NS_CP_REJECTED(*aContentPolicy)) {
1018 LOG(("OBJLC [%p]: CheckContentProcessPolicy rejected load", this));
1019 return false;
1022 return true;
1025 nsObjectLoadingContent::ParameterUpdateFlags
1026 nsObjectLoadingContent::UpdateObjectParameters() {
1027 nsCOMPtr<Element> thisElement =
1028 do_QueryInterface(static_cast<nsIImageLoadingContent*>(this));
1029 MOZ_ASSERT(thisElement, "Must be an Element");
1031 uint32_t caps = GetCapabilities();
1032 LOG(("OBJLC [%p]: Updating object parameters", this));
1034 nsresult rv;
1035 nsAutoCString newMime;
1036 nsAutoString typeAttr;
1037 nsCOMPtr<nsIURI> newURI;
1038 nsCOMPtr<nsIURI> newBaseURI;
1039 ObjectType newType;
1040 // Set if this state can't be used to load anything, forces eType_Null
1041 bool stateInvalid = false;
1042 // Indicates what parameters changed.
1043 // eParamChannelChanged - means parameters that affect channel opening
1044 // decisions changed
1045 // eParamStateChanged - means anything that affects what content we load
1046 // changed, even if the channel we'd open remains the
1047 // same.
1049 // State changes outside of the channel parameters only matter if we've
1050 // already opened a channel or tried to instantiate content, whereas channel
1051 // parameter changes require re-opening the channel even if we haven't gotten
1052 // that far.
1053 nsObjectLoadingContent::ParameterUpdateFlags retval = eParamNoChange;
1056 /// Initial MIME Type
1059 if (caps & eFallbackIfClassIDPresent) {
1060 nsAutoString classIDAttr;
1061 thisElement->GetAttr(kNameSpaceID_None, nsGkAtoms::classid, classIDAttr);
1062 // We don't support class ID plugin references, so we should always treat
1063 // having class Ids as attributes as invalid, and fallback accordingly.
1064 if (!classIDAttr.IsEmpty()) {
1065 newMime.Truncate();
1066 stateInvalid = true;
1071 /// Codebase
1074 nsAutoString codebaseStr;
1075 nsIURI* docBaseURI = thisElement->GetBaseURI();
1076 thisElement->GetAttr(kNameSpaceID_None, nsGkAtoms::codebase, codebaseStr);
1078 if (!codebaseStr.IsEmpty()) {
1079 rv = nsContentUtils::NewURIWithDocumentCharset(
1080 getter_AddRefs(newBaseURI), codebaseStr, thisElement->OwnerDoc(),
1081 docBaseURI);
1082 if (NS_FAILED(rv)) {
1083 // Malformed URI
1084 LOG(
1085 ("OBJLC [%p]: Could not parse plugin's codebase as a URI, "
1086 "will use document baseURI instead",
1087 this));
1091 // If we failed to build a valid URI, use the document's base URI
1092 if (!newBaseURI) {
1093 newBaseURI = docBaseURI;
1096 nsAutoString rawTypeAttr;
1097 thisElement->GetAttr(kNameSpaceID_None, nsGkAtoms::type, rawTypeAttr);
1098 if (!rawTypeAttr.IsEmpty()) {
1099 typeAttr = rawTypeAttr;
1100 nsAutoString params;
1101 nsAutoString mime;
1102 nsContentUtils::SplitMimeType(rawTypeAttr, mime, params);
1103 CopyUTF16toUTF8(mime, newMime);
1107 /// URI
1110 nsAutoString uriStr;
1111 // Different elements keep this in various locations
1112 if (thisElement->NodeInfo()->Equals(nsGkAtoms::object)) {
1113 thisElement->GetAttr(kNameSpaceID_None, nsGkAtoms::data, uriStr);
1114 } else if (thisElement->NodeInfo()->Equals(nsGkAtoms::embed)) {
1115 thisElement->GetAttr(kNameSpaceID_None, nsGkAtoms::src, uriStr);
1116 } else {
1117 MOZ_ASSERT_UNREACHABLE("Unrecognized plugin-loading tag");
1120 mRewrittenYoutubeEmbed = false;
1121 // Note that the baseURI changing could affect the newURI, even if uriStr did
1122 // not change.
1123 if (!uriStr.IsEmpty()) {
1124 rv = nsContentUtils::NewURIWithDocumentCharset(
1125 getter_AddRefs(newURI), uriStr, thisElement->OwnerDoc(), newBaseURI);
1126 nsCOMPtr<nsIURI> rewrittenURI;
1127 MaybeRewriteYoutubeEmbed(newURI, newBaseURI, getter_AddRefs(rewrittenURI));
1128 if (rewrittenURI) {
1129 newURI = rewrittenURI;
1130 mRewrittenYoutubeEmbed = true;
1131 newMime = "text/html"_ns;
1134 if (NS_FAILED(rv)) {
1135 stateInvalid = true;
1140 /// Check if the original (pre-channel) content-type or URI changed, and
1141 /// record mOriginal{ContentType,URI}
1144 if ((mOriginalContentType != newMime) || !URIEquals(mOriginalURI, newURI)) {
1145 // These parameters changing requires re-opening the channel, so don't
1146 // consider the currently-open channel below
1147 // XXX(johns): Changing the mime type might change our decision on whether
1148 // or not we load a channel, so we count changes to it as a
1149 // channel parameter change for the sake of simplicity.
1150 retval = (ParameterUpdateFlags)(retval | eParamChannelChanged);
1151 LOG(("OBJLC [%p]: Channel parameters changed", this));
1153 mOriginalContentType = newMime;
1154 mOriginalURI = newURI;
1157 /// If we have a channel, see if its MIME type should take precendence and
1158 /// check the final (redirected) URL
1161 // If we have a loaded channel and channel parameters did not change, use it
1162 // to determine what we would load.
1163 bool useChannel = mChannelLoaded && !(retval & eParamChannelChanged);
1164 // If we have a channel and are type loading, as opposed to having an existing
1165 // channel for a previous load.
1166 bool newChannel = useChannel && mType == eType_Loading;
1168 RefPtr<DocumentChannel> documentChannel = do_QueryObject(mChannel);
1169 if (newChannel && documentChannel) {
1170 // If we've got a DocumentChannel which is marked as loaded using
1171 // `mChannelLoaded`, we are currently in the middle of a
1172 // `UpgradeLoadToDocument`.
1174 // As we don't have the real mime-type from the channel, handle this by
1175 // using `newMime`.
1176 newMime = TEXT_HTML;
1178 MOZ_DIAGNOSTIC_ASSERT(
1179 GetTypeOfContent(newMime, mSkipFakePlugins) == eType_Document,
1180 "How is text/html not eType_Document?");
1181 } else if (newChannel && mChannel) {
1182 nsCString channelType;
1183 rv = mChannel->GetContentType(channelType);
1184 if (NS_FAILED(rv)) {
1185 MOZ_ASSERT_UNREACHABLE("GetContentType failed");
1186 stateInvalid = true;
1187 channelType.Truncate();
1190 LOG(("OBJLC [%p]: Channel has a content type of %s", this,
1191 channelType.get()));
1193 bool binaryChannelType = false;
1194 if (channelType.EqualsASCII(APPLICATION_GUESS_FROM_EXT)) {
1195 channelType = APPLICATION_OCTET_STREAM;
1196 mChannel->SetContentType(channelType);
1197 binaryChannelType = true;
1198 } else if (channelType.EqualsASCII(APPLICATION_OCTET_STREAM) ||
1199 channelType.EqualsASCII(BINARY_OCTET_STREAM)) {
1200 binaryChannelType = true;
1203 // Channel can change our URI through redirection
1204 rv = NS_GetFinalChannelURI(mChannel, getter_AddRefs(newURI));
1205 if (NS_FAILED(rv)) {
1206 MOZ_ASSERT_UNREACHABLE("NS_GetFinalChannelURI failure");
1207 stateInvalid = true;
1210 ObjectType typeHint = newMime.IsEmpty()
1211 ? eType_Null
1212 : GetTypeOfContent(newMime, mSkipFakePlugins);
1215 // In order of preference:
1217 // 1) Use our type hint if it matches a plugin
1218 // 2) If we have eAllowPluginSkipChannel, use the uri file extension if
1219 // it matches a plugin
1220 // 3) If the channel returns a binary stream type:
1221 // 3a) If we have a type non-null non-document type hint, use that
1222 // 3b) If the uri file extension matches a plugin type, use that
1223 // 4) Use the channel type
1225 bool overrideChannelType = false;
1226 if (IsPluginType(typeHint)) {
1227 LOG(("OBJLC [%p]: Using plugin type hint in favor of any channel type",
1228 this));
1229 overrideChannelType = true;
1230 } else if (binaryChannelType && typeHint != eType_Null &&
1231 typeHint != eType_Document) {
1232 LOG(("OBJLC [%p]: Using type hint in favor of binary channel type",
1233 this));
1234 overrideChannelType = true;
1237 if (overrideChannelType) {
1238 // Set the type we'll use for dispatch on the channel. Otherwise we could
1239 // end up trying to dispatch to a nsFrameLoader, which will complain that
1240 // it couldn't find a way to handle application/octet-stream
1241 nsAutoCString parsedMime, dummy;
1242 NS_ParseResponseContentType(newMime, parsedMime, dummy);
1243 if (!parsedMime.IsEmpty()) {
1244 mChannel->SetContentType(parsedMime);
1246 } else {
1247 newMime = channelType;
1249 } else if (newChannel) {
1250 LOG(("OBJLC [%p]: We failed to open a channel, marking invalid", this));
1251 stateInvalid = true;
1255 /// Determine final type
1257 // In order of preference:
1258 // 1) If we have attempted channel load, or set stateInvalid above, the type
1259 // is always null (fallback)
1260 // 2) If we have a loaded channel, we grabbed its mimeType above, use that
1261 // type.
1262 // 3) If we have a plugin type and no URI, use that type.
1263 // 4) If we have a plugin type and eAllowPluginSkipChannel, use that type.
1264 // 5) if we have a URI, set type to loading to indicate we'd need a channel
1265 // to proceed.
1266 // 6) Otherwise, type null to indicate unloadable content (fallback)
1269 ObjectType newMime_Type = GetTypeOfContent(newMime, mSkipFakePlugins);
1271 if (stateInvalid) {
1272 newType = eType_Null;
1273 LOG(("OBJLC [%p]: NewType #0: %s - %u", this, newMime.get(), newType));
1274 newMime.Truncate();
1275 } else if (newChannel) {
1276 // If newChannel is set above, we considered it in setting newMime
1277 newType = newMime_Type;
1278 LOG(("OBJLC [%p]: NewType #1: %s - %u", this, newMime.get(), newType));
1279 LOG(("OBJLC [%p]: Using channel type", this));
1280 } else if (((caps & eAllowPluginSkipChannel) || !newURI) &&
1281 IsPluginType(newMime_Type)) {
1282 newType = newMime_Type;
1283 LOG(("OBJLC [%p]: NewType #2: %s - %u", this, newMime.get(), newType));
1284 LOG(("OBJLC [%p]: Plugin type with no URI, skipping channel load", this));
1285 } else if (newURI &&
1286 (mOriginalContentType.IsEmpty() || newMime_Type != eType_Null)) {
1287 // We could potentially load this if we opened a channel on mURI, indicate
1288 // this by leaving type as loading.
1290 // If a MIME type was requested in the tag, but we have decided to set load
1291 // type to null, ignore (otherwise we'll default to document type loading).
1292 newType = eType_Loading;
1293 LOG(("OBJLC [%p]: NewType #3: %u", this, newType));
1294 } else {
1295 // Unloadable - no URI, and no plugin/MIME type. Non-plugin types (images,
1296 // documents) always load with a channel.
1297 newType = eType_Null;
1298 LOG(("OBJLC [%p]: NewType #4: %u", this, newType));
1302 /// Handle existing channels
1305 if (useChannel && newType == eType_Loading) {
1306 // We decided to use a channel, and also that the previous channel is still
1307 // usable, so re-use the existing values.
1308 newType = mType;
1309 LOG(("OBJLC [%p]: NewType #5: %u", this, newType));
1310 newMime = mContentType;
1311 newURI = mURI;
1312 } else if (useChannel && !newChannel) {
1313 // We have an existing channel, but did not decide to use one.
1314 retval = (ParameterUpdateFlags)(retval | eParamChannelChanged);
1315 useChannel = false;
1319 /// Update changed values
1322 if (newType != mType) {
1323 retval = (ParameterUpdateFlags)(retval | eParamStateChanged);
1324 LOG(("OBJLC [%p]: Type changed from %u -> %u", this, mType, newType));
1325 mType = newType;
1328 if (!URIEquals(mBaseURI, newBaseURI)) {
1329 LOG(("OBJLC [%p]: Object effective baseURI changed", this));
1330 mBaseURI = newBaseURI;
1333 if (!URIEquals(newURI, mURI)) {
1334 retval = (ParameterUpdateFlags)(retval | eParamStateChanged);
1335 LOG(("OBJLC [%p]: Object effective URI changed", this));
1336 mURI = newURI;
1339 // We don't update content type when loading, as the type is not final and we
1340 // don't want to superfluously change between mOriginalContentType ->
1341 // mContentType when doing |obj.data = obj.data| with a channel and differing
1342 // type.
1343 if (mType != eType_Loading && mContentType != newMime) {
1344 retval = (ParameterUpdateFlags)(retval | eParamStateChanged);
1345 retval = (ParameterUpdateFlags)(retval | eParamContentTypeChanged);
1346 LOG(("OBJLC [%p]: Object effective mime type changed (%s -> %s)", this,
1347 mContentType.get(), newMime.get()));
1348 mContentType = newMime;
1351 // If we decided to keep using info from an old channel, but also that state
1352 // changed, we need to invalidate it.
1353 if (useChannel && !newChannel && (retval & eParamStateChanged)) {
1354 mType = eType_Loading;
1355 retval = (ParameterUpdateFlags)(retval | eParamChannelChanged);
1358 return retval;
1361 // Used by PluginDocument to kick off our initial load from the already-opened
1362 // channel.
1363 NS_IMETHODIMP
1364 nsObjectLoadingContent::InitializeFromChannel(nsIRequest* aChannel) {
1365 LOG(("OBJLC [%p] InitializeFromChannel: %p", this, aChannel));
1366 if (mType != eType_Loading || mChannel) {
1367 // We could technically call UnloadObject() here, if consumers have a valid
1368 // reason for wanting to call this on an already-loaded tag.
1369 MOZ_ASSERT_UNREACHABLE("Should not have begun loading at this point");
1370 return NS_ERROR_UNEXPECTED;
1373 // Because we didn't open this channel from an initial LoadObject, we'll
1374 // update our parameters now, so the OnStartRequest->LoadObject doesn't
1375 // believe our src/type suddenly changed.
1376 UpdateObjectParameters();
1377 // But we always want to load from a channel, in this case.
1378 mType = eType_Loading;
1379 mChannel = do_QueryInterface(aChannel);
1380 NS_ASSERTION(mChannel, "passed a request that is not a channel");
1382 // OnStartRequest will now see we have a channel in the loading state, and
1383 // call into LoadObject. There's a possibility LoadObject will decide not to
1384 // load anything from a channel - it will call CloseChannel() in that case.
1385 return NS_OK;
1388 // Only OnStartRequest should be passing the channel parameter
1389 nsresult nsObjectLoadingContent::LoadObject(bool aNotify, bool aForceLoad) {
1390 return LoadObject(aNotify, aForceLoad, nullptr);
1393 nsresult nsObjectLoadingContent::LoadObject(bool aNotify, bool aForceLoad,
1394 nsIRequest* aLoadingChannel) {
1395 nsCOMPtr<nsIContent> thisContent =
1396 do_QueryInterface(static_cast<nsIImageLoadingContent*>(this));
1397 NS_ASSERTION(thisContent, "must be a content");
1398 Document* doc = thisContent->OwnerDoc();
1399 nsresult rv = NS_OK;
1401 // Per bug 1318303, if the parent document is not active, load the alternative
1402 // and return.
1403 if (!doc->IsCurrentActiveDocument()) {
1404 // Since this can be triggered on change of attributes, make sure we've
1405 // unloaded whatever is loaded first.
1406 UnloadObject();
1407 ObjectType oldType = mType;
1408 mType = eType_Fallback;
1409 ConfigureFallback();
1410 NotifyStateChanged(oldType, ObjectState(), true);
1411 return NS_OK;
1414 // XXX(johns): In these cases, we refuse to touch our content and just
1415 // remain unloaded, as per legacy behavior. It would make more sense to
1416 // load fallback content initially and refuse to ever change state again.
1417 if (doc->IsBeingUsedAsImage()) {
1418 return NS_OK;
1421 if (doc->IsLoadedAsData() && !doc->IsStaticDocument()) {
1422 return NS_OK;
1424 if (doc->IsStaticDocument()) {
1425 // We only allow image loads in static documents, but we need to let the
1426 // eType_Loading state go through too while we do so.
1427 if (mType != eType_Image && mType != eType_Loading) {
1428 return NS_OK;
1432 LOG(("OBJLC [%p]: LoadObject called, notify %u, forceload %u, channel %p",
1433 this, aNotify, aForceLoad, aLoadingChannel));
1435 // We can't re-use an already open channel, but aForceLoad may make us try
1436 // to load a plugin without any changes in channel state.
1437 if (aForceLoad && mChannelLoaded) {
1438 CloseChannel();
1439 mChannelLoaded = false;
1442 // Save these for NotifyStateChanged();
1443 EventStates oldState = ObjectState();
1444 ObjectType oldType = mType;
1446 ParameterUpdateFlags stateChange = UpdateObjectParameters();
1448 if (!stateChange && !aForceLoad) {
1449 return NS_OK;
1453 /// State has changed, unload existing content and attempt to load new type
1455 LOG(("OBJLC [%p]: LoadObject - plugin state changed (%u)", this,
1456 stateChange));
1458 // We synchronously start/stop plugin instances below, which may spin the
1459 // event loop. Re-entering into the load is fine, but at that point the
1460 // original load call needs to abort when unwinding
1461 // NOTE this is located *after* the state change check, a subsequent load
1462 // with no subsequently changed state will be a no-op.
1463 if (mIsLoading) {
1464 LOG(("OBJLC [%p]: Re-entering into LoadObject", this));
1466 mIsLoading = true;
1467 AutoSetLoadingToFalse reentryCheck(this);
1469 // Unload existing content, keeping in mind stopping plugins might spin the
1470 // event loop. Note that we check for still-open channels below
1471 UnloadObject(false); // Don't reset state
1472 if (!mIsLoading) {
1473 // The event loop must've spun and re-entered into LoadObject, which
1474 // finished the load
1475 LOG(("OBJLC [%p]: Re-entered into LoadObject, aborting outer load", this));
1476 return NS_OK;
1479 // Determine what's going on with our channel.
1480 if (stateChange & eParamChannelChanged) {
1481 // If the channel params changed, throw away the channel, but unset
1482 // mChannelLoaded so we'll still try to open a new one for this load if
1483 // necessary
1484 CloseChannel();
1485 mChannelLoaded = false;
1486 } else if (mType == eType_Null && mChannel) {
1487 // If we opened a channel but then failed to find a loadable state, throw it
1488 // away. mChannelLoaded will indicate that we tried to load a channel at one
1489 // point so we wont recurse
1490 CloseChannel();
1491 } else if (mType == eType_Loading && mChannel) {
1492 // We're still waiting on a channel load, already opened one, and
1493 // channel parameters didn't change
1494 return NS_OK;
1495 } else if (mChannelLoaded && mChannel != aLoadingChannel) {
1496 // The only time we should have a loaded channel with a changed state is
1497 // when the channel has just opened -- in which case this call should
1498 // have originated from OnStartRequest
1499 MOZ_ASSERT_UNREACHABLE(
1500 "Loading with a channel, but state doesn't make sense");
1501 return NS_OK;
1505 // Security checks
1508 if (mType != eType_Null && mType != eType_Fallback) {
1509 bool allowLoad = true;
1510 int16_t contentPolicy = nsIContentPolicy::ACCEPT;
1511 // If mChannelLoaded is set we presumably already passed load policy
1512 // If mType == eType_Loading then we call OpenChannel() which internally
1513 // creates a new channel and calls asyncOpen() on that channel which
1514 // then enforces content policy checks.
1515 if (allowLoad && mURI && !mChannelLoaded && mType != eType_Loading) {
1516 allowLoad = CheckLoadPolicy(&contentPolicy);
1518 // If we're loading a type now, check ProcessPolicy. Note that we may check
1519 // both now in the case of plugins whose type is determined before opening a
1520 // channel.
1521 if (allowLoad && mType != eType_Loading) {
1522 allowLoad = CheckProcessPolicy(&contentPolicy);
1525 // Content policy implementations can mutate the DOM, check for re-entry
1526 if (!mIsLoading) {
1527 LOG(("OBJLC [%p]: We re-entered in content policy, leaving original load",
1528 this));
1529 return NS_OK;
1532 // Load denied, switch to null
1533 if (!allowLoad) {
1534 LOG(("OBJLC [%p]: Load denied by policy", this));
1535 mType = eType_Null;
1539 // Don't allow view-source scheme.
1540 // view-source is the only scheme to which this applies at the moment due to
1541 // potential timing attacks to read data from cross-origin documents. If this
1542 // widens we should add a protocol flag for whether the scheme is only allowed
1543 // in top and use something like nsNetUtil::NS_URIChainHasFlags.
1544 if (mType != eType_Null) {
1545 nsCOMPtr<nsIURI> tempURI = mURI;
1546 nsCOMPtr<nsINestedURI> nestedURI = do_QueryInterface(tempURI);
1547 while (nestedURI) {
1548 // view-source should always be an nsINestedURI, loop and check the
1549 // scheme on this and all inner URIs that are also nested URIs.
1550 if (tempURI->SchemeIs("view-source")) {
1551 LOG(("OBJLC [%p]: Blocking as effective URI has view-source scheme",
1552 this));
1553 mType = eType_Null;
1554 break;
1557 nestedURI->GetInnerURI(getter_AddRefs(tempURI));
1558 nestedURI = do_QueryInterface(tempURI);
1562 // Items resolved as Image/Document are not candidates for content blocking,
1563 // as well as invalid plugins (they will not have the mContentType set).
1564 if ((mType == eType_Null || IsPluginType(mType)) && ShouldBlockContent()) {
1565 LOG(("OBJLC [%p]: Enable content blocking", this));
1566 mType = eType_Loading;
1569 // Sanity check: We shouldn't have any loaded resources, pending events, or
1570 // a final listener at this point
1571 if (mFrameLoader || mPendingInstantiateEvent ||
1572 mPendingCheckPluginStopEvent || mFinalListener) {
1573 MOZ_ASSERT_UNREACHABLE("Trying to load new plugin with existing content");
1574 return NS_OK;
1577 // More sanity-checking:
1578 // If mChannel is set, mChannelLoaded should be set, and vice-versa
1579 if (mType != eType_Null && !!mChannel != mChannelLoaded) {
1580 MOZ_ASSERT_UNREACHABLE("Trying to load with bad channel state");
1581 return NS_OK;
1585 /// Attempt to load new type
1588 // Cache the current attributes and parameters.
1589 if (mType == eType_Null) {
1590 rv = BuildParametersArray();
1591 NS_ENSURE_SUCCESS(rv, rv);
1594 // We don't set mFinalListener until OnStartRequest has been called, to
1595 // prevent re-entry ugliness with CloseChannel()
1596 nsCOMPtr<nsIStreamListener> finalListener;
1597 switch (mType) {
1598 case eType_Image:
1599 if (!mChannel) {
1600 // We have a LoadImage() call, but UpdateObjectParameters requires a
1601 // channel for images, so this is not a valid state.
1602 MOZ_ASSERT_UNREACHABLE("Attempting to load image without a channel?");
1603 rv = NS_ERROR_UNEXPECTED;
1604 break;
1606 rv = LoadImageWithChannel(mChannel, getter_AddRefs(finalListener));
1607 // finalListener will receive OnStartRequest below
1608 break;
1609 case eType_Document: {
1610 if (!mChannel) {
1611 // We could mFrameLoader->LoadURI(mURI), but UpdateObjectParameters
1612 // requires documents have a channel, so this is not a valid state.
1613 MOZ_ASSERT_UNREACHABLE(
1614 "Attempting to load a document without a "
1615 "channel");
1616 rv = NS_ERROR_FAILURE;
1617 break;
1620 nsCOMPtr<nsIDocShell> docShell = SetupDocShell(mURI);
1621 if (!docShell) {
1622 rv = NS_ERROR_FAILURE;
1623 break;
1626 // We're loading a document, so we have to set LOAD_DOCUMENT_URI
1627 // (especially important for firing onload)
1628 nsLoadFlags flags = 0;
1629 mChannel->GetLoadFlags(&flags);
1630 flags |= nsIChannel::LOAD_DOCUMENT_URI;
1631 mChannel->SetLoadFlags(flags);
1633 nsCOMPtr<nsIInterfaceRequestor> req(do_QueryInterface(docShell));
1634 NS_ASSERTION(req, "Docshell must be an ifreq");
1636 nsCOMPtr<nsIURILoader> uriLoader(components::URILoader::Service());
1637 if (NS_WARN_IF(!uriLoader)) {
1638 MOZ_ASSERT_UNREACHABLE("Failed to get uriLoader service");
1639 mFrameLoader->Destroy();
1640 mFrameLoader = nullptr;
1641 break;
1644 rv = uriLoader->OpenChannel(mChannel, nsIURILoader::DONT_RETARGET, req,
1645 getter_AddRefs(finalListener));
1646 // finalListener will receive OnStartRequest either below, or if
1647 // `mChannel` is a `DocumentChannel`, it will be received after
1648 // RedirectToRealChannel.
1649 } break;
1650 case eType_Loading:
1651 // If our type remains Loading, we need a channel to proceed
1652 rv = OpenChannel();
1653 if (NS_FAILED(rv)) {
1654 LOG(("OBJLC [%p]: OpenChannel returned failure (%" PRIu32 ")", this,
1655 static_cast<uint32_t>(rv)));
1657 break;
1658 case eType_Null:
1659 case eType_Fallback:
1660 // Handled below, silence compiler warnings
1661 break;
1662 case eType_FakePlugin:
1663 // We're now in the process of removing FakePlugin. See bug 1529133.
1664 MOZ_CRASH(
1665 "Shouldn't reach here! This means there's a fakeplugin trying to be "
1666 "loaded.");
1670 // Loaded, handle notifications and fallback
1672 if (NS_FAILED(rv)) {
1673 // If we failed in the loading hunk above, switch to null (empty) region
1674 LOG(("OBJLC [%p]: Loading failed, switching to null", this));
1675 mType = eType_Null;
1678 // If we didn't load anything, handle switching to fallback state
1679 if (mType == eType_Fallback || mType == eType_Null) {
1680 LOG(("OBJLC [%p]: Switching to fallback state", this));
1681 MOZ_ASSERT(!mFrameLoader, "switched to fallback but also loaded something");
1683 MaybeFireErrorEvent();
1685 if (mChannel) {
1686 // If we were loading with a channel but then failed over, throw it away
1687 CloseChannel();
1690 // Don't try to initialize plugins or final listener below
1691 finalListener = nullptr;
1693 ConfigureFallback();
1696 // Notify of our final state
1697 NotifyStateChanged(oldType, oldState, aNotify);
1698 NS_ENSURE_TRUE(mIsLoading, NS_OK);
1701 // Spawning plugins and dispatching to the final listener may re-enter, so are
1702 // delayed until after we fire a notification, to prevent missing
1703 // notifications or firing them out of order.
1705 // Note that we ensured that we entered into LoadObject() from
1706 // ::OnStartRequest above when loading with a channel.
1709 rv = NS_OK;
1710 if (finalListener) {
1711 NS_ASSERTION(mType != eType_Null && mType != eType_Loading,
1712 "We should not have a final listener with a non-loaded type");
1713 mFinalListener = finalListener;
1715 // If we're a DocumentChannel load, hold off on firing the `OnStartRequest`
1716 // callback, as we haven't received it yet from our caller.
1717 RefPtr<DocumentChannel> documentChannel = do_QueryObject(mChannel);
1718 if (documentChannel) {
1719 MOZ_ASSERT(
1720 mType == eType_Document,
1721 "We have a DocumentChannel here but aren't loading a document?");
1722 } else {
1723 rv = finalListener->OnStartRequest(mChannel);
1727 if (NS_FAILED(rv) && mIsLoading) {
1728 // Since we've already notified of our transition, we can just Unload and
1729 // call ConfigureFallback (which will notify again)
1730 oldType = mType;
1731 mType = eType_Fallback;
1732 UnloadObject(false);
1733 NS_ENSURE_TRUE(mIsLoading, NS_OK);
1734 CloseChannel();
1735 ConfigureFallback();
1736 NotifyStateChanged(oldType, ObjectState(), true);
1739 return NS_OK;
1742 // This call can re-enter when dealing with plugin listeners
1743 nsresult nsObjectLoadingContent::CloseChannel() {
1744 if (mChannel) {
1745 LOG(("OBJLC [%p]: Closing channel\n", this));
1746 // Null the values before potentially-reentering, and ensure they survive
1747 // the call
1748 nsCOMPtr<nsIChannel> channelGrip(mChannel);
1749 nsCOMPtr<nsIStreamListener> listenerGrip(mFinalListener);
1750 mChannel = nullptr;
1751 mFinalListener = nullptr;
1752 channelGrip->Cancel(NS_BINDING_ABORTED);
1753 if (listenerGrip) {
1754 // mFinalListener is only set by LoadObject after OnStartRequest, or
1755 // by OnStartRequest in the case of late-opened plugin streams
1756 listenerGrip->OnStopRequest(channelGrip, NS_BINDING_ABORTED);
1759 return NS_OK;
1762 nsresult nsObjectLoadingContent::OpenChannel() {
1763 nsCOMPtr<nsIContent> thisContent =
1764 do_QueryInterface(static_cast<nsIImageLoadingContent*>(this));
1765 NS_ASSERTION(thisContent, "must be a content");
1766 Document* doc = thisContent->OwnerDoc();
1767 NS_ASSERTION(doc, "No owner document?");
1769 nsresult rv;
1770 mChannel = nullptr;
1772 // E.g. mms://
1773 if (!mURI || !CanHandleURI(mURI)) {
1774 return NS_ERROR_NOT_AVAILABLE;
1777 nsCOMPtr<nsILoadGroup> group = doc->GetDocumentLoadGroup();
1778 nsCOMPtr<nsIChannel> chan;
1779 RefPtr<ObjectInterfaceRequestorShim> shim =
1780 new ObjectInterfaceRequestorShim(this);
1782 bool inheritAttrs = nsContentUtils::ChannelShouldInheritPrincipal(
1783 thisContent->NodePrincipal(), // aLoadState->PrincipalToInherit()
1784 mURI, // aLoadState->URI()
1785 true, // aInheritForAboutBlank
1786 false); // aForceInherit
1788 bool inheritPrincipal = inheritAttrs && !SchemeIsData(mURI);
1790 nsSecurityFlags securityFlags =
1791 nsILoadInfo::SEC_ALLOW_CROSS_ORIGIN_SEC_CONTEXT_IS_NULL;
1792 if (inheritPrincipal) {
1793 securityFlags |= nsILoadInfo::SEC_FORCE_INHERIT_PRINCIPAL;
1796 nsContentPolicyType contentPolicyType = GetContentPolicyType();
1797 nsLoadFlags loadFlags = nsIChannel::LOAD_CALL_CONTENT_SNIFFERS |
1798 nsIChannel::LOAD_BYPASS_SERVICE_WORKER |
1799 nsIRequest::LOAD_HTML_OBJECT_DATA;
1800 uint32_t sandboxFlags = doc->GetSandboxFlags();
1802 // For object loads we store the CSP that potentially needs to
1803 // be inherited, e.g. in case we are loading an opaque origin
1804 // like a data: URI. The actual inheritance check happens within
1805 // Document::InitCSP(). Please create an actual copy of the CSP
1806 // (do not share the same reference) otherwise a Meta CSP of an
1807 // opaque origin will incorrectly be propagated to the embedding
1808 // document.
1809 RefPtr<nsCSPContext> cspToInherit;
1810 if (nsCOMPtr<nsIContentSecurityPolicy> csp = doc->GetCsp()) {
1811 cspToInherit = new nsCSPContext();
1812 cspToInherit->InitFromOther(static_cast<nsCSPContext*>(csp.get()));
1815 // --- Create LoadInfo
1816 RefPtr<LoadInfo> loadInfo = new LoadInfo(
1817 /*aLoadingPrincipal = aLoadingContext->NodePrincipal() */ nullptr,
1818 /*aTriggeringPrincipal = aLoadingPrincipal */ nullptr,
1819 /*aLoadingContext = */ thisContent,
1820 /*aSecurityFlags = */ securityFlags,
1821 /*aContentPolicyType = */ contentPolicyType,
1822 /*aLoadingClientInfo = */ Nothing(),
1823 /*aController = */ Nothing(),
1824 /*aSandboxFlags = */ sandboxFlags);
1826 if (inheritAttrs) {
1827 loadInfo->SetPrincipalToInherit(thisContent->NodePrincipal());
1830 if (cspToInherit) {
1831 loadInfo->SetCSPToInherit(cspToInherit);
1834 if (DocumentChannel::CanUseDocumentChannel(mURI)) {
1835 // --- Create LoadState
1836 RefPtr<nsDocShellLoadState> loadState = new nsDocShellLoadState(mURI);
1837 loadState->SetPrincipalToInherit(thisContent->NodePrincipal());
1838 loadState->SetTriggeringPrincipal(loadInfo->TriggeringPrincipal());
1839 if (cspToInherit) {
1840 loadState->SetCsp(cspToInherit);
1842 loadState->SetTriggeringSandboxFlags(sandboxFlags);
1844 // TODO(djg): This was httpChan->SetReferrerInfoWithoutClone(referrerInfo);
1845 // Is the ...WithoutClone(...) important?
1846 auto referrerInfo = MakeRefPtr<ReferrerInfo>(*doc);
1847 loadState->SetReferrerInfo(referrerInfo);
1849 chan =
1850 DocumentChannel::CreateForObject(loadState, loadInfo, loadFlags, shim);
1851 MOZ_ASSERT(chan);
1852 // NS_NewChannel sets the group on the channel. CreateDocumentChannel does
1853 // not.
1854 chan->SetLoadGroup(group);
1855 } else {
1856 rv = NS_NewChannelInternal(getter_AddRefs(chan), // outChannel
1857 mURI, // aUri
1858 loadInfo, // aLoadInfo
1859 nullptr, // aPerformanceStorage
1860 group, // aLoadGroup
1861 shim, // aCallbacks
1862 loadFlags, // aLoadFlags
1863 nullptr); // aIoService
1864 NS_ENSURE_SUCCESS(rv, rv);
1866 if (inheritAttrs) {
1867 nsCOMPtr<nsILoadInfo> loadinfo = chan->LoadInfo();
1868 loadinfo->SetPrincipalToInherit(thisContent->NodePrincipal());
1871 // For object loads we store the CSP that potentially needs to
1872 // be inherited, e.g. in case we are loading an opaque origin
1873 // like a data: URI. The actual inheritance check happens within
1874 // Document::InitCSP(). Please create an actual copy of the CSP
1875 // (do not share the same reference) otherwise a Meta CSP of an
1876 // opaque origin will incorrectly be propagated to the embedding
1877 // document.
1878 if (cspToInherit) {
1879 nsCOMPtr<nsILoadInfo> loadinfo = chan->LoadInfo();
1880 static_cast<LoadInfo*>(loadinfo.get())->SetCSPToInherit(cspToInherit);
1884 // Referrer
1885 nsCOMPtr<nsIHttpChannel> httpChan(do_QueryInterface(chan));
1886 if (httpChan) {
1887 auto referrerInfo = MakeRefPtr<ReferrerInfo>(*doc);
1889 rv = httpChan->SetReferrerInfoWithoutClone(referrerInfo);
1890 MOZ_ASSERT(NS_SUCCEEDED(rv));
1892 // Set the initiator type
1893 nsCOMPtr<nsITimedChannel> timedChannel(do_QueryInterface(httpChan));
1894 if (timedChannel) {
1895 timedChannel->SetInitiatorType(thisContent->LocalName());
1898 nsCOMPtr<nsIClassOfService> cos(do_QueryInterface(httpChan));
1899 if (cos && UserActivation::IsHandlingUserInput()) {
1900 cos->AddClassFlags(nsIClassOfService::UrgentStart);
1904 nsCOMPtr<nsIScriptChannel> scriptChannel = do_QueryInterface(chan);
1905 if (scriptChannel) {
1906 // Allow execution against our context if the principals match
1907 scriptChannel->SetExecutionPolicy(nsIScriptChannel::EXECUTE_NORMAL);
1910 // AsyncOpen can fail if a file does not exist.
1911 rv = chan->AsyncOpen(shim);
1912 NS_ENSURE_SUCCESS(rv, rv);
1913 LOG(("OBJLC [%p]: Channel opened", this));
1914 mChannel = chan;
1915 return NS_OK;
1918 uint32_t nsObjectLoadingContent::GetCapabilities() const {
1919 return eSupportImages | eSupportPlugins | eSupportDocuments;
1922 void nsObjectLoadingContent::Destroy() {
1923 if (mFrameLoader) {
1924 mFrameLoader->Destroy();
1925 mFrameLoader = nullptr;
1928 if (mInstantiating) {
1929 QueueCheckPluginStopEvent();
1932 // Reset state so that if the element is re-appended to tree again (e.g.
1933 // adopting to another document), it will reload resource again.
1934 UnloadObject();
1936 nsImageLoadingContent::Destroy();
1939 /* static */
1940 void nsObjectLoadingContent::Traverse(nsObjectLoadingContent* tmp,
1941 nsCycleCollectionTraversalCallback& cb) {
1942 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mFrameLoader);
1945 /* static */
1946 void nsObjectLoadingContent::Unlink(nsObjectLoadingContent* tmp) {
1947 if (tmp->mFrameLoader) {
1948 tmp->mFrameLoader->Destroy();
1950 NS_IMPL_CYCLE_COLLECTION_UNLINK(mFrameLoader);
1953 void nsObjectLoadingContent::UnloadObject(bool aResetState) {
1954 // Don't notify in CancelImageRequests until we transition to a new loaded
1955 // state
1956 CancelImageRequests(false);
1957 if (mFrameLoader) {
1958 mFrameLoader->Destroy();
1959 mFrameLoader = nullptr;
1962 if (aResetState) {
1963 CloseChannel();
1964 mChannelLoaded = false;
1965 mType = eType_Loading;
1966 mURI = mOriginalURI = mBaseURI = nullptr;
1967 mContentType.Truncate();
1968 mOriginalContentType.Truncate();
1971 // InstantiatePluginInstance checks this after re-entrant calls and aborts if
1972 // it was cleared from under it
1973 mInstantiating = false;
1975 mScriptRequested = false;
1977 mIsStopping = false;
1979 mCachedAttributes.Clear();
1980 mCachedParameters.Clear();
1982 // This call should be last as it may re-enter
1983 StopPluginInstance();
1986 void nsObjectLoadingContent::NotifyStateChanged(ObjectType aOldType,
1987 EventStates aOldState,
1988 bool aNotify) {
1989 LOG(("OBJLC [%p]: NotifyStateChanged: (%u, %" PRIx64 ") -> (%u, %" PRIx64 ")"
1990 " (notify %i)",
1991 this, aOldType, aOldState.GetInternalValue(), mType,
1992 ObjectState().GetInternalValue(), aNotify));
1994 nsCOMPtr<dom::Element> thisEl =
1995 do_QueryInterface(static_cast<nsIImageLoadingContent*>(this));
1996 MOZ_ASSERT(thisEl, "must be an element");
1998 // XXX(johns): A good bit of the code below replicates UpdateState(true)
2000 // Unfortunately, we do some state changes without notifying
2001 // (e.g. in Fallback when canceling image requests), so we have to
2002 // manually notify object state changes.
2003 thisEl->UpdateState(false);
2005 if (!aNotify) {
2006 // We're done here
2007 return;
2010 Document* doc = thisEl->GetComposedDoc();
2011 if (!doc) {
2012 return; // Nothing to do
2015 const EventStates newState = ObjectState();
2016 if (newState == aOldState && mType == aOldType) {
2017 return; // Also done.
2020 RefPtr<PresShell> presShell = doc->GetPresShell();
2021 if (presShell && (aOldType != mType)) {
2022 presShell->PostRecreateFramesFor(thisEl);
2026 nsObjectLoadingContent::ObjectType nsObjectLoadingContent::GetTypeOfContent(
2027 const nsCString& aMIMEType, bool aNoFakePlugin) {
2028 nsCOMPtr<nsIContent> thisContent =
2029 do_QueryInterface(static_cast<nsIImageLoadingContent*>(this));
2030 NS_ASSERTION(thisContent, "must be a content");
2032 // Images, documents and (fake) plugins are always supported.
2033 MOZ_ASSERT(GetCapabilities() &
2034 (eSupportImages | eSupportDocuments | eSupportPlugins));
2036 LOG(
2037 ("OBJLC[%p]: calling HtmlObjectContentTypeForMIMEType: aMIMEType: %s - "
2038 "thisContent: %p\n",
2039 this, aMIMEType.get(), thisContent.get()));
2040 auto ret =
2041 static_cast<ObjectType>(nsContentUtils::HtmlObjectContentTypeForMIMEType(
2042 aMIMEType, aNoFakePlugin));
2043 LOG(("OBJLC[%p]: called HtmlObjectContentTypeForMIMEType\n", this));
2044 return ret;
2047 void nsObjectLoadingContent::CreateStaticClone(
2048 nsObjectLoadingContent* aDest) const {
2049 aDest->mType = mType;
2051 if (mFrameLoader) {
2052 nsCOMPtr<nsIContent> content =
2053 do_QueryInterface(static_cast<nsIImageLoadingContent*>(aDest));
2054 Document* doc = content->OwnerDoc();
2055 if (doc->IsStaticDocument()) {
2056 doc->AddPendingFrameStaticClone(aDest, mFrameLoader);
2061 NS_IMETHODIMP
2062 nsObjectLoadingContent::SyncStartPluginInstance() {
2063 NS_ASSERTION(
2064 nsContentUtils::IsSafeToRunScript(),
2065 "Must be able to run script in order to instantiate a plugin instance!");
2067 // Don't even attempt to start an instance unless the content is in
2068 // the document and active
2069 nsCOMPtr<nsIContent> thisContent =
2070 do_QueryInterface(static_cast<nsIImageLoadingContent*>(this));
2071 if (!InActiveDocument(thisContent)) {
2072 return NS_ERROR_FAILURE;
2075 nsCOMPtr<nsIURI> kungFuURIGrip(mURI);
2076 mozilla::Unused
2077 << kungFuURIGrip; // This URI is not referred to within this function
2078 nsCString contentType(mContentType);
2079 return InstantiatePluginInstance();
2082 NS_IMETHODIMP
2083 nsObjectLoadingContent::AsyncStartPluginInstance() {
2084 if (mPendingInstantiateEvent) {
2085 return NS_OK;
2088 nsCOMPtr<nsIContent> thisContent =
2089 do_QueryInterface(static_cast<nsIImageLoadingContent*>(this));
2090 Document* doc = thisContent->OwnerDoc();
2091 if (doc->IsStaticDocument() || doc->IsBeingUsedAsImage()) {
2092 return NS_OK;
2095 nsCOMPtr<nsIRunnable> event = new nsAsyncInstantiateEvent(this);
2096 nsresult rv = NS_DispatchToCurrentThread(event);
2097 if (NS_SUCCEEDED(rv)) {
2098 // Track pending events
2099 mPendingInstantiateEvent = event;
2102 return rv;
2105 NS_IMETHODIMP
2106 nsObjectLoadingContent::GetSrcURI(nsIURI** aURI) {
2107 NS_IF_ADDREF(*aURI = GetSrcURI());
2108 return NS_OK;
2111 void nsObjectLoadingContent::ConfigureFallback() {
2112 MOZ_ASSERT(!mFrameLoader && !mChannel,
2113 "ConfigureFallback called with loaded content");
2115 // We only fallback in special cases where we are already of fallback
2116 // type (e.g. removed Flash plugin use) or where something went wrong
2117 // (e.g. unknown MIME type).
2118 MOZ_ASSERT(mType == eType_Fallback || mType == eType_Null);
2120 nsCOMPtr<nsIContent> thisContent =
2121 do_QueryInterface(static_cast<nsIImageLoadingContent*>(this));
2122 NS_ASSERTION(thisContent, "must be a content");
2124 // There are two types of fallback:
2125 // 1. HTML fallbacks are children of the <object> or <embed> DOM element.
2126 // 2. The special transparent region fallback replacing Flash use.
2127 // If our type is eType_Fallback (e.g. Flash use) then we use #1 if
2128 // available, otherwise we use #2.
2129 // If our type is eType_Null (e.g. unknown MIME type) then we use
2130 // #1, otherwise the element has no size.
2131 bool hasHtmlFallback = false;
2132 if (thisContent->IsHTMLElement(nsGkAtoms::object)) {
2133 // Do a depth-first traverse of node tree with the current element as root,
2134 // looking for non-<param> elements. If we find some then we have an HTML
2135 // fallback for this element.
2136 for (nsIContent* child = thisContent->GetFirstChild(); child;) {
2137 hasHtmlFallback =
2138 hasHtmlFallback || (!child->IsHTMLElement(nsGkAtoms::param) &&
2139 nsStyleUtil::IsSignificantChild(child, false));
2141 // <object> and <embed> elements in the fallback need to StartObjectLoad.
2142 // Their children should be ignored since they are part of those
2143 // element's fallback.
2144 if (auto embed = HTMLEmbedElement::FromNode(child)) {
2145 embed->StartObjectLoad(true, true);
2146 // Skip the children
2147 child = child->GetNextNonChildNode(thisContent);
2148 } else if (auto object = HTMLObjectElement::FromNode(child)) {
2149 object->StartObjectLoad(true, true);
2150 // Skip the children
2151 child = child->GetNextNonChildNode(thisContent);
2152 } else {
2153 child = child->GetNextNode(thisContent);
2158 // If we find an HTML fallback then we always switch type to null.
2159 if (hasHtmlFallback) {
2160 mType = eType_Null;
2164 NS_IMETHODIMP
2165 nsObjectLoadingContent::StopPluginInstance() {
2166 AUTO_PROFILER_LABEL("nsObjectLoadingContent::StopPluginInstance", OTHER);
2167 // Clear any pending events
2168 mPendingInstantiateEvent = nullptr;
2169 mPendingCheckPluginStopEvent = nullptr;
2171 // If we're currently instantiating, clearing this will cause
2172 // InstantiatePluginInstance's re-entrance check to destroy the created plugin
2173 mInstantiating = false;
2175 return NS_OK;
2178 NS_IMETHODIMP
2179 nsObjectLoadingContent::Reload(bool aClearActivation) {
2180 if (aClearActivation) {
2181 mSkipFakePlugins = false;
2184 return LoadObject(true, true);
2187 NS_IMETHODIMP
2188 nsObjectLoadingContent::SkipFakePlugins() {
2189 if (!nsContentUtils::IsCallerChrome()) return NS_ERROR_NOT_AVAILABLE;
2191 mSkipFakePlugins = true;
2193 // If we're showing a fake plugin now, reload
2194 if (mType == eType_FakePlugin) {
2195 return LoadObject(true, true);
2198 return NS_OK;
2201 NS_IMETHODIMP
2202 nsObjectLoadingContent::UpgradeLoadToDocument(
2203 nsIChannel* aRequest, BrowsingContext** aBrowsingContext) {
2204 AUTO_PROFILER_LABEL("nsObjectLoadingContent::UpgradeLoadToDocument", NETWORK);
2206 LOG(("OBJLC [%p]: UpgradeLoadToDocument", this));
2208 if (aRequest != mChannel || !aRequest) {
2209 // happens when a new load starts before the previous one got here.
2210 return NS_BINDING_ABORTED;
2213 // We should be state loading.
2214 if (mType != eType_Loading) {
2215 MOZ_ASSERT_UNREACHABLE("Should be type loading at this point");
2216 return NS_BINDING_ABORTED;
2218 MOZ_ASSERT(!mChannelLoaded, "mChannelLoaded set already?");
2219 MOZ_ASSERT(!mFinalListener, "mFinalListener exists already?");
2221 mChannelLoaded = true;
2223 // We don't need to check for errors here, unlike in `OnStartRequest`, as
2224 // `UpgradeLoadToDocument` is only called when the load is going to become a
2225 // process-switching load. As we never process switch for failed object loads,
2226 // we know our channel status is successful.
2228 // Call `LoadObject` to trigger our nsObjectLoadingContext to switch into the
2229 // specified new state.
2230 nsresult rv = LoadObject(true, false, aRequest);
2231 if (NS_WARN_IF(NS_FAILED(rv))) {
2232 return rv;
2235 RefPtr<BrowsingContext> bc = GetBrowsingContext();
2236 if (!bc) {
2237 return NS_ERROR_FAILURE;
2240 bc.forget(aBrowsingContext);
2241 return NS_OK;
2244 uint32_t nsObjectLoadingContent::GetRunID(SystemCallerGuarantee,
2245 ErrorResult& aRv) {
2246 if (!mHasRunID) {
2247 // The plugin instance must not have a run ID, so we must
2248 // be running the plugin in-process.
2249 aRv.Throw(NS_ERROR_NOT_IMPLEMENTED);
2250 return 0;
2252 return mRunID;
2255 bool nsObjectLoadingContent::ShouldBlockContent() {
2256 if (mContentBlockingEnabled && mURI && IsFlashMIME(mContentType) &&
2257 StaticPrefs::browser_safebrowsing_blockedURIs_enabled()) {
2258 return true;
2261 return false;
2264 Document* nsObjectLoadingContent::GetContentDocument(
2265 nsIPrincipal& aSubjectPrincipal) {
2266 nsCOMPtr<nsIContent> thisContent =
2267 do_QueryInterface(static_cast<nsIImageLoadingContent*>(this));
2269 if (!thisContent->IsInComposedDoc()) {
2270 return nullptr;
2273 Document* sub_doc = thisContent->OwnerDoc()->GetSubDocumentFor(thisContent);
2274 if (!sub_doc) {
2275 return nullptr;
2278 // Return null for cross-origin contentDocument.
2279 if (!aSubjectPrincipal.SubsumesConsideringDomain(sub_doc->NodePrincipal())) {
2280 return nullptr;
2283 return sub_doc;
2286 bool nsObjectLoadingContent::DoResolve(
2287 JSContext* aCx, JS::Handle<JSObject*> aObject, JS::Handle<jsid> aId,
2288 JS::MutableHandle<mozilla::Maybe<JS::PropertyDescriptor>> aDesc) {
2289 return true;
2292 /* static */
2293 bool nsObjectLoadingContent::MayResolve(jsid aId) {
2294 // We can resolve anything, really.
2295 return true;
2298 void nsObjectLoadingContent::GetOwnPropertyNames(
2299 JSContext* aCx, JS::MutableHandleVector<jsid> /* unused */,
2300 bool /* unused */, ErrorResult& aRv) {}
2302 void nsObjectLoadingContent::MaybeFireErrorEvent() {
2303 nsCOMPtr<nsIContent> thisContent =
2304 do_QueryInterface(static_cast<nsIImageLoadingContent*>(this));
2305 // Queue a task to fire an error event if we're an <object> element. The
2306 // queueing is important, since then we don't have to worry about reentry.
2307 if (thisContent->IsHTMLElement(nsGkAtoms::object)) {
2308 RefPtr<AsyncEventDispatcher> loadBlockingAsyncDispatcher =
2309 new LoadBlockingAsyncEventDispatcher(
2310 thisContent, u"error"_ns, CanBubble::eNo, ChromeOnlyDispatch::eNo);
2311 loadBlockingAsyncDispatcher->PostDOMEvent();
2315 bool nsObjectLoadingContent::BlockEmbedOrObjectContentLoading() {
2316 nsCOMPtr<nsIContent> thisContent =
2317 do_QueryInterface(static_cast<nsIImageLoadingContent*>(this));
2319 // Traverse up the node tree to see if we have any ancestors that may block us
2320 // from loading
2321 for (nsIContent* parent = thisContent->GetParent(); parent;
2322 parent = parent->GetParent()) {
2323 if (parent->IsAnyOfHTMLElements(nsGkAtoms::video, nsGkAtoms::audio)) {
2324 return true;
2326 // If we have an ancestor that is an object with a source, it'll have an
2327 // associated displayed type. If that type is not null, don't load content
2328 // for the embed.
2329 if (HTMLObjectElement* object = HTMLObjectElement::FromNode(parent)) {
2330 uint32_t type = object->DisplayedType();
2331 if (type != eType_Null) {
2332 return true;
2336 return false;
2339 void nsObjectLoadingContent::SubdocumentIntrinsicSizeOrRatioChanged(
2340 const Maybe<IntrinsicSize>& aIntrinsicSize,
2341 const Maybe<AspectRatio>& aIntrinsicRatio) {
2342 if (aIntrinsicSize == mSubdocumentIntrinsicSize &&
2343 aIntrinsicRatio == mSubdocumentIntrinsicRatio) {
2344 return;
2347 mSubdocumentIntrinsicSize = aIntrinsicSize;
2348 mSubdocumentIntrinsicRatio = aIntrinsicRatio;
2350 nsCOMPtr<nsIContent> thisContent =
2351 do_QueryInterface(static_cast<nsIImageLoadingContent*>(this));
2353 if (nsSubDocumentFrame* sdf = do_QueryFrame(thisContent->GetPrimaryFrame())) {
2354 sdf->SubdocumentIntrinsicSizeOrRatioChanged();