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