Thu, 15 Jan 2015 21:03:48 +0100
Integrate friendly tips from Tor colleagues to make (or not) 4.5 alpha 3;
This includes removal of overloaded (but unused) methods, and addition of
a overlooked call to DataStruct::SetData(nsISupports, uint32_t, bool.)
1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 // vim:set et cin sw=2 sts=2:
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>, <applet>, etc).
10 */
12 // Interface headers
13 #include "imgLoader.h"
14 #include "nsIContent.h"
15 #include "nsIDocShell.h"
16 #include "nsIDocument.h"
17 #include "nsIDOMCustomEvent.h"
18 #include "nsIDOMDocument.h"
19 #include "nsIDOMHTMLObjectElement.h"
20 #include "nsIDOMHTMLAppletElement.h"
21 #include "nsIExternalProtocolHandler.h"
22 #include "nsIObjectFrame.h"
23 #include "nsIPermissionManager.h"
24 #include "nsPluginHost.h"
25 #include "nsPluginInstanceOwner.h"
26 #include "nsJSNPRuntime.h"
27 #include "nsINestedURI.h"
28 #include "nsIPresShell.h"
29 #include "nsIScriptGlobalObject.h"
30 #include "nsScriptSecurityManager.h"
31 #include "nsIScriptSecurityManager.h"
32 #include "nsIStreamConverterService.h"
33 #include "nsIURILoader.h"
34 #include "nsIURL.h"
35 #include "nsIWebNavigation.h"
36 #include "nsIWebNavigationInfo.h"
37 #include "nsIScriptChannel.h"
38 #include "nsIBlocklistService.h"
39 #include "nsIAsyncVerifyRedirectCallback.h"
40 #include "nsIAppShell.h"
42 #include "nsError.h"
44 // Util headers
45 #include "prenv.h"
46 #include "prlog.h"
48 #include "nsAutoPtr.h"
49 #include "nsCURILoader.h"
50 #include "nsContentPolicyUtils.h"
51 #include "nsContentUtils.h"
52 #include "nsCxPusher.h"
53 #include "nsDocShellCID.h"
54 #include "nsGkAtoms.h"
55 #include "nsThreadUtils.h"
56 #include "nsNetUtil.h"
57 #include "nsMimeTypes.h"
58 #include "nsStyleUtil.h"
59 #include "nsUnicharUtils.h"
60 #include "mozilla/Preferences.h"
61 #include "nsSandboxFlags.h"
63 // Concrete classes
64 #include "nsFrameLoader.h"
66 #include "nsObjectLoadingContent.h"
67 #include "mozAutoDocUpdate.h"
68 #include "nsIContentSecurityPolicy.h"
69 #include "nsIChannelPolicy.h"
70 #include "nsChannelPolicy.h"
71 #include "GeckoProfiler.h"
72 #include "nsObjectFrame.h"
73 #include "nsDOMClassInfo.h"
74 #include "nsWrapperCacheInlines.h"
75 #include "nsDOMJSUtils.h"
77 #include "nsWidgetsCID.h"
78 #include "nsContentCID.h"
79 #include "mozilla/BasicEvents.h"
80 #include "mozilla/dom/BindingUtils.h"
81 #include "mozilla/dom/Element.h"
82 #include "mozilla/dom/Event.h"
83 #include "mozilla/EventDispatcher.h"
84 #include "mozilla/EventStates.h"
85 #include "mozilla/Telemetry.h"
87 #ifdef XP_WIN
88 // Thanks so much, Microsoft! :(
89 #ifdef CreateEvent
90 #undef CreateEvent
91 #endif
92 #endif // XP_WIN
94 static NS_DEFINE_CID(kAppShellCID, NS_APPSHELL_CID);
96 static const char *kPrefJavaMIME = "plugin.java.mime";
98 using namespace mozilla;
99 using namespace mozilla::dom;
101 #ifdef PR_LOGGING
102 static PRLogModuleInfo*
103 GetObjectLog()
104 {
105 static PRLogModuleInfo *sLog;
106 if (!sLog)
107 sLog = PR_NewLogModule("objlc");
108 return sLog;
109 }
110 #endif
112 #define LOG(args) PR_LOG(GetObjectLog(), PR_LOG_DEBUG, args)
113 #define LOG_ENABLED() PR_LOG_TEST(GetObjectLog(), PR_LOG_DEBUG)
115 static bool
116 InActiveDocument(nsIContent *aContent)
117 {
118 if (!aContent->IsInDoc()) {
119 return false;
120 }
121 nsIDocument *doc = aContent->OwnerDoc();
122 return (doc && doc->IsActive());
123 }
125 ///
126 /// Runnables and helper classes
127 ///
129 class nsAsyncInstantiateEvent : public nsRunnable {
130 public:
131 nsAsyncInstantiateEvent(nsObjectLoadingContent *aContent)
132 : mContent(aContent) {}
134 ~nsAsyncInstantiateEvent() {}
136 NS_IMETHOD Run();
138 private:
139 nsCOMPtr<nsIObjectLoadingContent> mContent;
140 };
142 NS_IMETHODIMP
143 nsAsyncInstantiateEvent::Run()
144 {
145 nsObjectLoadingContent *objLC =
146 static_cast<nsObjectLoadingContent *>(mContent.get());
148 // If objLC is no longer tracking this event, we've been canceled or
149 // superseded
150 if (objLC->mPendingInstantiateEvent != this) {
151 return NS_OK;
152 }
153 objLC->mPendingInstantiateEvent = nullptr;
155 return objLC->SyncStartPluginInstance();
156 }
158 // Checks to see if the content for a plugin instance should be unloaded
159 // (outside an active document) or stopped (in a document but unrendered). This
160 // is used to allow scripts to move a plugin around the document hierarchy
161 // without re-instantiating it.
162 class CheckPluginStopEvent : public nsRunnable {
163 public:
164 CheckPluginStopEvent(nsObjectLoadingContent *aContent)
165 : mContent(aContent) {}
167 ~CheckPluginStopEvent() {}
169 NS_IMETHOD Run();
171 private:
172 nsCOMPtr<nsIObjectLoadingContent> mContent;
173 };
175 NS_IMETHODIMP
176 CheckPluginStopEvent::Run()
177 {
178 nsObjectLoadingContent *objLC =
179 static_cast<nsObjectLoadingContent *>(mContent.get());
181 // If objLC is no longer tracking this event, we've been canceled or
182 // superseded. We clear this before we finish - either by calling
183 // UnloadObject/StopPluginInstance, or directly if we took no action.
184 if (objLC->mPendingCheckPluginStopEvent != this) {
185 return NS_OK;
186 }
188 nsCOMPtr<nsIContent> content =
189 do_QueryInterface(static_cast<nsIImageLoadingContent *>(objLC));
190 if (!InActiveDocument(content)) {
191 // Unload the object entirely
192 LOG(("OBJLC [%p]: Unloading plugin outside of document", this));
193 objLC->UnloadObject();
194 return NS_OK;
195 }
197 if (!content->GetPrimaryFrame()) {
198 LOG(("OBJLC [%p]: CheckPluginStopEvent - No frame, flushing layout", this));
199 nsIDocument* currentDoc = content->GetCurrentDoc();
200 if (currentDoc) {
201 currentDoc->FlushPendingNotifications(Flush_Layout);
202 if (objLC->mPendingCheckPluginStopEvent != this) {
203 LOG(("OBJLC [%p]: CheckPluginStopEvent - superseded in layout flush",
204 this));
205 return NS_OK;
206 } else if (content->GetPrimaryFrame()) {
207 LOG(("OBJLC [%p]: CheckPluginStopEvent - frame gained in layout flush",
208 this));
209 objLC->mPendingCheckPluginStopEvent = nullptr;
210 return NS_OK;
211 }
212 }
213 // Still no frame, suspend plugin. HasNewFrame will restart us when we
214 // become rendered again
215 LOG(("OBJLC [%p]: Stopping plugin that lost frame", this));
216 // Okay to leave loaded as a plugin, but stop the unrendered instance
217 objLC->StopPluginInstance();
218 }
220 objLC->mPendingCheckPluginStopEvent = nullptr;
221 return NS_OK;
222 }
224 /**
225 * Helper task for firing simple events
226 */
227 class nsSimplePluginEvent : public nsRunnable {
228 public:
229 nsSimplePluginEvent(nsIContent* aTarget, const nsAString &aEvent)
230 : mTarget(aTarget)
231 , mDocument(aTarget->GetCurrentDoc())
232 , mEvent(aEvent)
233 {
234 MOZ_ASSERT(aTarget && mDocument);
235 }
237 nsSimplePluginEvent(nsIDocument* aTarget, const nsAString& aEvent)
238 : mTarget(aTarget)
239 , mDocument(aTarget)
240 , mEvent(aEvent)
241 {
242 MOZ_ASSERT(aTarget);
243 }
245 nsSimplePluginEvent(nsIContent* aTarget,
246 nsIDocument* aDocument,
247 const nsAString& aEvent)
248 : mTarget(aTarget)
249 , mDocument(aDocument)
250 , mEvent(aEvent)
251 {
252 MOZ_ASSERT(aTarget && aDocument);
253 }
255 ~nsSimplePluginEvent() {}
257 NS_IMETHOD Run();
259 private:
260 nsCOMPtr<nsISupports> mTarget;
261 nsCOMPtr<nsIDocument> mDocument;
262 nsString mEvent;
263 };
265 NS_IMETHODIMP
266 nsSimplePluginEvent::Run()
267 {
268 if (mDocument && mDocument->IsActive()) {
269 LOG(("OBJLC [%p]: nsSimplePluginEvent firing event \"%s\"", mTarget.get(),
270 NS_ConvertUTF16toUTF8(mEvent).get()));
271 nsContentUtils::DispatchTrustedEvent(mDocument, mTarget,
272 mEvent, true, true);
273 }
274 return NS_OK;
275 }
277 /**
278 * A task for firing PluginCrashed DOM Events.
279 */
280 class nsPluginCrashedEvent : public nsRunnable {
281 public:
282 nsCOMPtr<nsIContent> mContent;
283 nsString mPluginDumpID;
284 nsString mBrowserDumpID;
285 nsString mPluginName;
286 nsString mPluginFilename;
287 bool mSubmittedCrashReport;
289 nsPluginCrashedEvent(nsIContent* aContent,
290 const nsAString& aPluginDumpID,
291 const nsAString& aBrowserDumpID,
292 const nsAString& aPluginName,
293 const nsAString& aPluginFilename,
294 bool submittedCrashReport)
295 : mContent(aContent),
296 mPluginDumpID(aPluginDumpID),
297 mBrowserDumpID(aBrowserDumpID),
298 mPluginName(aPluginName),
299 mPluginFilename(aPluginFilename),
300 mSubmittedCrashReport(submittedCrashReport)
301 {}
303 ~nsPluginCrashedEvent() {}
305 NS_IMETHOD Run();
306 };
308 NS_IMETHODIMP
309 nsPluginCrashedEvent::Run()
310 {
311 LOG(("OBJLC [%p]: Firing plugin crashed event\n",
312 mContent.get()));
314 nsCOMPtr<nsIDocument> doc = mContent->GetDocument();
315 if (!doc) {
316 NS_WARNING("Couldn't get document for PluginCrashed event!");
317 return NS_OK;
318 }
320 ErrorResult rv;
321 nsRefPtr<Event> event =
322 doc->CreateEvent(NS_LITERAL_STRING("customevent"), rv);
323 nsCOMPtr<nsIDOMCustomEvent> customEvent(do_QueryObject(event));
324 if (!customEvent) {
325 NS_WARNING("Couldn't QI event for PluginCrashed event!");
326 return NS_OK;
327 }
329 nsCOMPtr<nsIWritableVariant> variant;
330 variant = do_CreateInstance("@mozilla.org/variant;1");
331 if (!variant) {
332 NS_WARNING("Couldn't create detail variant for PluginCrashed event!");
333 return NS_OK;
334 }
335 customEvent->InitCustomEvent(NS_LITERAL_STRING("PluginCrashed"),
336 true, true, variant);
337 event->SetTrusted(true);
338 event->GetInternalNSEvent()->mFlags.mOnlyChromeDispatch = true;
340 nsCOMPtr<nsIWritablePropertyBag2> propBag;
341 propBag = do_CreateInstance("@mozilla.org/hash-property-bag;1");
342 if (!propBag) {
343 NS_WARNING("Couldn't create a property bag for PluginCrashed event!");
344 return NS_OK;
345 }
347 // add a "pluginDumpID" property to this event
348 propBag->SetPropertyAsAString(NS_LITERAL_STRING("pluginDumpID"),
349 mPluginDumpID);
351 // add a "browserDumpID" property to this event
352 propBag->SetPropertyAsAString(NS_LITERAL_STRING("browserDumpID"),
353 mBrowserDumpID);
355 // add a "pluginName" property to this event
356 propBag->SetPropertyAsAString(NS_LITERAL_STRING("pluginName"),
357 mPluginName);
359 // add a "pluginFilename" property to this event
360 propBag->SetPropertyAsAString(NS_LITERAL_STRING("pluginFilename"),
361 mPluginFilename);
363 // add a "submittedCrashReport" property to this event
364 propBag->SetPropertyAsBool(NS_LITERAL_STRING("submittedCrashReport"),
365 mSubmittedCrashReport);
367 variant->SetAsISupports(propBag);
369 EventDispatcher::DispatchDOMEvent(mContent, nullptr, event, nullptr, nullptr);
370 return NS_OK;
371 }
373 class nsStopPluginRunnable : public nsRunnable, public nsITimerCallback
374 {
375 public:
376 NS_DECL_ISUPPORTS_INHERITED
378 nsStopPluginRunnable(nsPluginInstanceOwner* aInstanceOwner,
379 nsObjectLoadingContent* aContent)
380 : mInstanceOwner(aInstanceOwner)
381 , mContent(aContent)
382 {
383 NS_ASSERTION(aInstanceOwner, "need an owner");
384 NS_ASSERTION(aContent, "need a nsObjectLoadingContent");
385 }
387 // nsRunnable
388 NS_IMETHOD Run();
390 // nsITimerCallback
391 NS_IMETHOD Notify(nsITimer *timer);
393 private:
394 nsCOMPtr<nsITimer> mTimer;
395 nsRefPtr<nsPluginInstanceOwner> mInstanceOwner;
396 nsCOMPtr<nsIObjectLoadingContent> mContent;
397 };
399 NS_IMPL_ISUPPORTS_INHERITED(nsStopPluginRunnable, nsRunnable, nsITimerCallback)
401 NS_IMETHODIMP
402 nsStopPluginRunnable::Notify(nsITimer *aTimer)
403 {
404 return Run();
405 }
407 NS_IMETHODIMP
408 nsStopPluginRunnable::Run()
409 {
410 // InitWithCallback calls Release before AddRef so we need to hold a
411 // strong ref on 'this' since we fall through to this scope if it fails.
412 nsCOMPtr<nsITimerCallback> kungFuDeathGrip = this;
413 nsCOMPtr<nsIAppShell> appShell = do_GetService(kAppShellCID);
414 if (appShell) {
415 uint32_t currentLevel = 0;
416 appShell->GetEventloopNestingLevel(¤tLevel);
417 if (currentLevel > mInstanceOwner->GetLastEventloopNestingLevel()) {
418 if (!mTimer)
419 mTimer = do_CreateInstance("@mozilla.org/timer;1");
420 if (mTimer) {
421 // Fire 100ms timer to try to tear down this plugin as quickly as
422 // possible once the nesting level comes back down.
423 nsresult rv = mTimer->InitWithCallback(this, 100,
424 nsITimer::TYPE_ONE_SHOT);
425 if (NS_SUCCEEDED(rv)) {
426 return rv;
427 }
428 }
429 NS_ERROR("Failed to setup a timer to stop the plugin later (at a safe "
430 "time). Stopping the plugin now, this might crash.");
431 }
432 }
434 mTimer = nullptr;
436 static_cast<nsObjectLoadingContent*>(mContent.get())->
437 DoStopPlugin(mInstanceOwner, false, true);
439 return NS_OK;
440 }
442 // You can't take the address of bitfield members, so we have two separate
443 // classes for these :-/
445 // Sets a object's mInstantiating bit to false when destroyed
446 class AutoSetInstantiatingToFalse {
447 public:
448 AutoSetInstantiatingToFalse(nsObjectLoadingContent *aContent)
449 : mContent(aContent) {}
450 ~AutoSetInstantiatingToFalse() { mContent->mInstantiating = false; }
451 private:
452 nsObjectLoadingContent* mContent;
453 };
455 // Sets a object's mInstantiating bit to false when destroyed
456 class AutoSetLoadingToFalse {
457 public:
458 AutoSetLoadingToFalse(nsObjectLoadingContent *aContent)
459 : mContent(aContent) {}
460 ~AutoSetLoadingToFalse() { mContent->mIsLoading = false; }
461 private:
462 nsObjectLoadingContent* mContent;
463 };
465 ///
466 /// Helper functions
467 ///
469 static bool
470 IsSuccessfulRequest(nsIRequest* aRequest)
471 {
472 nsresult status;
473 nsresult rv = aRequest->GetStatus(&status);
474 if (NS_FAILED(rv) || NS_FAILED(status)) {
475 return false;
476 }
478 // This may still be an error page or somesuch
479 nsCOMPtr<nsIHttpChannel> httpChan(do_QueryInterface(aRequest));
480 if (httpChan) {
481 bool success;
482 rv = httpChan->GetRequestSucceeded(&success);
483 if (NS_FAILED(rv) || !success) {
484 return false;
485 }
486 }
488 // Otherwise, the request is successful
489 return true;
490 }
492 static bool
493 CanHandleURI(nsIURI* aURI)
494 {
495 nsAutoCString scheme;
496 if (NS_FAILED(aURI->GetScheme(scheme))) {
497 return false;
498 }
500 nsIIOService* ios = nsContentUtils::GetIOService();
501 if (!ios)
502 return false;
504 nsCOMPtr<nsIProtocolHandler> handler;
505 ios->GetProtocolHandler(scheme.get(), getter_AddRefs(handler));
506 if (!handler) {
507 return false;
508 }
510 nsCOMPtr<nsIExternalProtocolHandler> extHandler =
511 do_QueryInterface(handler);
512 // We can handle this URI if its protocol handler is not the external one
513 return extHandler == nullptr;
514 }
516 // Helper for tedious URI equality syntax when one or both arguments may be
517 // null and URIEquals(null, null) should be true
518 static bool inline
519 URIEquals(nsIURI *a, nsIURI *b)
520 {
521 bool equal;
522 return (!a && !b) || (a && b && NS_SUCCEEDED(a->Equals(b, &equal)) && equal);
523 }
525 static bool
526 IsSupportedImage(const nsCString& aMimeType)
527 {
528 return imgLoader::SupportImageWithMimeType(aMimeType.get());
529 }
531 static void
532 GetExtensionFromURI(nsIURI* uri, nsCString& ext)
533 {
534 nsCOMPtr<nsIURL> url(do_QueryInterface(uri));
535 if (url) {
536 url->GetFileExtension(ext);
537 } else {
538 nsCString spec;
539 uri->GetSpec(spec);
541 int32_t offset = spec.RFindChar('.');
542 if (offset != kNotFound) {
543 ext = Substring(spec, offset + 1, spec.Length());
544 }
545 }
546 }
548 /**
549 * Checks whether a plugin exists and is enabled for the extension
550 * in the given URI. The MIME type is returned in the mimeType out parameter.
551 */
552 bool
553 IsPluginEnabledByExtension(nsIURI* uri, nsCString& mimeType)
554 {
555 nsAutoCString ext;
556 GetExtensionFromURI(uri, ext);
558 if (ext.IsEmpty()) {
559 return false;
560 }
562 nsRefPtr<nsPluginHost> pluginHost = nsPluginHost::GetInst();
564 if (!pluginHost) {
565 NS_NOTREACHED("No pluginhost");
566 return false;
567 }
569 const char* typeFromExt;
570 nsresult rv = pluginHost->IsPluginEnabledForExtension(ext.get(), typeFromExt);
571 if (NS_SUCCEEDED(rv)) {
572 mimeType = typeFromExt;
573 return true;
574 }
575 return false;
576 }
578 bool
579 PluginExistsForType(const char* aMIMEType)
580 {
581 nsRefPtr<nsPluginHost> pluginHost = nsPluginHost::GetInst();
583 if (!pluginHost) {
584 NS_NOTREACHED("No pluginhost");
585 return false;
586 }
588 return pluginHost->PluginExistsForType(aMIMEType);
589 }
591 ///
592 /// Member Functions
593 ///
595 // Helper to queue a CheckPluginStopEvent for a OBJLC object
596 void
597 nsObjectLoadingContent::QueueCheckPluginStopEvent()
598 {
599 nsCOMPtr<nsIRunnable> event = new CheckPluginStopEvent(this);
600 mPendingCheckPluginStopEvent = event;
602 nsCOMPtr<nsIAppShell> appShell = do_GetService(kAppShellCID);
603 if (appShell) {
604 appShell->RunInStableState(event);
605 }
606 }
608 // Tedious syntax to create a plugin stream listener with checks and put it in
609 // mFinalListener
610 bool
611 nsObjectLoadingContent::MakePluginListener()
612 {
613 if (!mInstanceOwner) {
614 NS_NOTREACHED("expecting a spawned plugin");
615 return false;
616 }
617 nsRefPtr<nsPluginHost> pluginHost = nsPluginHost::GetInst();
618 if (!pluginHost) {
619 NS_NOTREACHED("No pluginHost");
620 return false;
621 }
622 NS_ASSERTION(!mFinalListener, "overwriting a final listener");
623 nsresult rv;
624 nsRefPtr<nsNPAPIPluginInstance> inst;
625 nsCOMPtr<nsIStreamListener> finalListener;
626 rv = mInstanceOwner->GetInstance(getter_AddRefs(inst));
627 NS_ENSURE_SUCCESS(rv, false);
628 rv = pluginHost->NewPluginStreamListener(mURI, inst,
629 getter_AddRefs(finalListener));
630 NS_ENSURE_SUCCESS(rv, false);
631 mFinalListener = finalListener;
632 return true;
633 }
636 bool
637 nsObjectLoadingContent::IsSupportedDocument(const nsCString& aMimeType)
638 {
639 nsCOMPtr<nsIContent> thisContent =
640 do_QueryInterface(static_cast<nsIImageLoadingContent*>(this));
641 NS_ASSERTION(thisContent, "must be a content");
643 nsCOMPtr<nsIWebNavigationInfo> info(
644 do_GetService(NS_WEBNAVIGATION_INFO_CONTRACTID));
645 if (!info) {
646 return false;
647 }
649 nsCOMPtr<nsIWebNavigation> webNav;
650 nsIDocument* currentDoc = thisContent->GetCurrentDoc();
651 if (currentDoc) {
652 webNav = do_GetInterface(currentDoc->GetWindow());
653 }
655 uint32_t supported;
656 nsresult rv = info->IsTypeSupported(aMimeType, webNav, &supported);
658 if (NS_FAILED(rv)) {
659 return false;
660 }
662 if (supported != nsIWebNavigationInfo::UNSUPPORTED) {
663 // Don't want to support plugins as documents
664 return supported != nsIWebNavigationInfo::PLUGIN;
665 }
667 // Try a stream converter
668 // NOTE: We treat any type we can convert from as a supported type. If a
669 // type is not actually supported, the URI loader will detect that and
670 // return an error, and we'll fallback.
671 nsCOMPtr<nsIStreamConverterService> convServ =
672 do_GetService("@mozilla.org/streamConverters;1");
673 bool canConvert = false;
674 if (convServ) {
675 rv = convServ->CanConvert(aMimeType.get(), "*/*", &canConvert);
676 }
677 return NS_SUCCEEDED(rv) && canConvert;
678 }
680 nsresult
681 nsObjectLoadingContent::BindToTree(nsIDocument* aDocument,
682 nsIContent* aParent,
683 nsIContent* aBindingParent,
684 bool aCompileEventHandlers)
685 {
686 nsImageLoadingContent::BindToTree(aDocument, aParent, aBindingParent,
687 aCompileEventHandlers);
689 if (aDocument) {
690 return aDocument->AddPlugin(this);
691 }
692 return NS_OK;
693 }
695 void
696 nsObjectLoadingContent::UnbindFromTree(bool aDeep, bool aNullParent)
697 {
698 nsImageLoadingContent::UnbindFromTree(aDeep, aNullParent);
700 nsCOMPtr<nsIContent> thisContent =
701 do_QueryInterface(static_cast<nsIObjectLoadingContent*>(this));
702 MOZ_ASSERT(thisContent);
703 nsIDocument* ownerDoc = thisContent->OwnerDoc();
704 ownerDoc->RemovePlugin(this);
706 if (mType == eType_Plugin && (mInstanceOwner || mInstantiating)) {
707 // we'll let the plugin continue to run at least until we get back to
708 // the event loop. If we get back to the event loop and the node
709 // has still not been added back to the document then we tear down the
710 // plugin
711 QueueCheckPluginStopEvent();
712 } else if (mType != eType_Image) {
713 // nsImageLoadingContent handles the image case.
714 // Reset state and clear pending events
715 /// XXX(johns): The implementation for GenericFrame notes that ideally we
716 /// would keep the docshell around, but trash the frameloader
717 UnloadObject();
718 }
719 nsIDocument* doc = thisContent->GetCurrentDoc();
720 if (doc && doc->IsActive()) {
721 nsCOMPtr<nsIRunnable> ev = new nsSimplePluginEvent(doc,
722 NS_LITERAL_STRING("PluginRemoved"));
723 NS_DispatchToCurrentThread(ev);
724 }
725 }
727 nsObjectLoadingContent::nsObjectLoadingContent()
728 : mType(eType_Loading)
729 , mFallbackType(eFallbackAlternate)
730 , mChannelLoaded(false)
731 , mInstantiating(false)
732 , mNetworkCreated(true)
733 , mActivated(false)
734 , mPlayPreviewCanceled(false)
735 , mIsStopping(false)
736 , mIsLoading(false)
737 , mScriptRequested(false) {}
739 nsObjectLoadingContent::~nsObjectLoadingContent()
740 {
741 // Should have been unbound from the tree at this point, and
742 // CheckPluginStopEvent keeps us alive
743 if (mFrameLoader) {
744 NS_NOTREACHED("Should not be tearing down frame loaders at this point");
745 mFrameLoader->Destroy();
746 }
747 if (mInstanceOwner || mInstantiating) {
748 // This is especially bad as delayed stop will try to hold on to this
749 // object...
750 NS_NOTREACHED("Should not be tearing down a plugin at this point!");
751 StopPluginInstance();
752 }
753 DestroyImageLoadingContent();
754 }
756 nsresult
757 nsObjectLoadingContent::InstantiatePluginInstance(bool aIsLoading)
758 {
759 if (mInstanceOwner || mType != eType_Plugin || (mIsLoading != aIsLoading) ||
760 mInstantiating) {
761 // If we hit this assertion it's probably because LoadObject re-entered :(
762 //
763 // XXX(johns): This hackiness will go away in bug 767635
764 NS_ASSERTION(mIsLoading || !aIsLoading,
765 "aIsLoading should only be true inside LoadObject");
766 return NS_OK;
767 }
769 mInstantiating = true;
770 AutoSetInstantiatingToFalse autoInstantiating(this);
772 nsCOMPtr<nsIContent> thisContent =
773 do_QueryInterface(static_cast<nsIImageLoadingContent *>(this));
775 nsCOMPtr<nsIDocument> doc = thisContent->GetCurrentDoc();
776 if (!doc || !InActiveDocument(thisContent)) {
777 NS_ERROR("Shouldn't be calling "
778 "InstantiatePluginInstance without an active document");
779 return NS_ERROR_FAILURE;
780 }
782 // Instantiating an instance can result in script execution, which
783 // can destroy this DOM object. Don't allow that for the scope
784 // of this method.
785 nsCOMPtr<nsIObjectLoadingContent> kungFuDeathGrip = this;
787 // Flush layout so that the frame is created if possible and the plugin is
788 // initialized with the latest information.
789 doc->FlushPendingNotifications(Flush_Layout);
790 // Flushing layout may have re-entered and loaded something underneath us
791 NS_ENSURE_TRUE(mInstantiating, NS_OK);
793 if (!thisContent->GetPrimaryFrame()) {
794 LOG(("OBJLC [%p]: Not instantiating plugin with no frame", this));
795 return NS_OK;
796 }
798 nsresult rv = NS_ERROR_FAILURE;
799 nsRefPtr<nsPluginHost> pluginHost = nsPluginHost::GetInst();
801 if (!pluginHost) {
802 NS_NOTREACHED("No pluginhost");
803 return NS_ERROR_FAILURE;
804 }
806 // If you add early return(s), be sure to balance this call to
807 // appShell->SuspendNative() with additional call(s) to
808 // appShell->ReturnNative().
809 nsCOMPtr<nsIAppShell> appShell = do_GetService(kAppShellCID);
810 if (appShell) {
811 appShell->SuspendNative();
812 }
814 nsRefPtr<nsPluginInstanceOwner> newOwner;
815 rv = pluginHost->InstantiatePluginInstance(mContentType.get(),
816 mURI.get(), this,
817 getter_AddRefs(newOwner));
819 // XXX(johns): We don't suspend native inside stopping plugins...
820 if (appShell) {
821 appShell->ResumeNative();
822 }
824 if (!mInstantiating || NS_FAILED(rv)) {
825 LOG(("OBJLC [%p]: Plugin instantiation failed or re-entered, "
826 "killing old instance", this));
827 // XXX(johns): This needs to be de-duplicated with DoStopPlugin, but we
828 // don't want to touch the protochain or delayed stop.
829 // (Bug 767635)
830 if (newOwner) {
831 nsRefPtr<nsNPAPIPluginInstance> inst;
832 newOwner->GetInstance(getter_AddRefs(inst));
833 newOwner->SetFrame(nullptr);
834 if (inst) {
835 pluginHost->StopPluginInstance(inst);
836 }
837 newOwner->Destroy();
838 }
839 return NS_OK;
840 }
842 mInstanceOwner = newOwner;
844 // Ensure the frame did not change during instantiation re-entry (common).
845 // HasNewFrame would not have mInstanceOwner yet, so the new frame would be
846 // dangling. (Bug 854082)
847 nsIFrame* frame = thisContent->GetPrimaryFrame();
848 if (frame && mInstanceOwner) {
849 mInstanceOwner->SetFrame(static_cast<nsObjectFrame*>(frame));
851 // Bug 870216 - Adobe Reader renders with incorrect dimensions until it gets
852 // a second SetWindow call. This is otherwise redundant.
853 mInstanceOwner->CallSetWindow();
854 }
856 // Set up scripting interfaces.
857 NotifyContentObjectWrapper();
859 nsRefPtr<nsNPAPIPluginInstance> pluginInstance;
860 GetPluginInstance(getter_AddRefs(pluginInstance));
861 if (pluginInstance) {
862 nsCOMPtr<nsIPluginTag> pluginTag;
863 pluginHost->GetPluginTagForInstance(pluginInstance,
864 getter_AddRefs(pluginTag));
866 nsCOMPtr<nsIBlocklistService> blocklist =
867 do_GetService("@mozilla.org/extensions/blocklist;1");
868 if (blocklist) {
869 uint32_t blockState = nsIBlocklistService::STATE_NOT_BLOCKED;
870 blocklist->GetPluginBlocklistState(pluginTag, EmptyString(),
871 EmptyString(), &blockState);
872 if (blockState == nsIBlocklistService::STATE_OUTDATED) {
873 // Fire plugin outdated event if necessary
874 LOG(("OBJLC [%p]: Dispatching plugin outdated event for content %p\n",
875 this));
876 nsCOMPtr<nsIRunnable> ev = new nsSimplePluginEvent(thisContent,
877 NS_LITERAL_STRING("PluginOutdated"));
878 nsresult rv = NS_DispatchToCurrentThread(ev);
879 if (NS_FAILED(rv)) {
880 NS_WARNING("failed to dispatch nsSimplePluginEvent");
881 }
882 }
883 }
885 // If we have a URI but didn't open a channel yet (eAllowPluginSkipChannel)
886 // or we did load with a channel but are re-instantiating, re-open the
887 // channel. OpenChannel() performs security checks, and this plugin has
888 // already passed content policy in LoadObject.
889 if ((mURI && !mChannelLoaded) || (mChannelLoaded && !aIsLoading)) {
890 NS_ASSERTION(!mChannel, "should not have an existing channel here");
891 // We intentionally ignore errors here, leaving it up to the plugin to
892 // deal with not having an initial stream.
893 OpenChannel();
894 }
895 }
897 nsCOMPtr<nsIRunnable> ev = \
898 new nsSimplePluginEvent(thisContent,
899 doc,
900 NS_LITERAL_STRING("PluginInstantiated"));
901 NS_DispatchToCurrentThread(ev);
903 return NS_OK;
904 }
906 void
907 nsObjectLoadingContent::NotifyOwnerDocumentActivityChanged()
908 {
909 // XXX(johns): We cannot touch plugins or run arbitrary script from this call,
910 // as nsDocument is in a non-reentrant state.
912 // If we have a plugin we want to queue an event to stop it unless we are
913 // moved into an active document before returning to the event loop.
914 if (mInstanceOwner || mInstantiating)
915 QueueCheckPluginStopEvent();
916 }
918 // nsIRequestObserver
919 NS_IMETHODIMP
920 nsObjectLoadingContent::OnStartRequest(nsIRequest *aRequest,
921 nsISupports *aContext)
922 {
923 PROFILER_LABEL("nsObjectLoadingContent", "OnStartRequest");
925 LOG(("OBJLC [%p]: Channel OnStartRequest", this));
927 if (aRequest != mChannel || !aRequest) {
928 // happens when a new load starts before the previous one got here
929 return NS_BINDING_ABORTED;
930 }
932 // If we already switched to type plugin, this channel can just be passed to
933 // the final listener.
934 if (mType == eType_Plugin) {
935 if (!mInstanceOwner) {
936 // We drop mChannel when stopping plugins, so something is wrong
937 NS_NOTREACHED("Opened a channel in plugin mode, but don't have a plugin");
938 return NS_BINDING_ABORTED;
939 }
940 if (MakePluginListener()) {
941 return mFinalListener->OnStartRequest(aRequest, nullptr);
942 } else {
943 NS_NOTREACHED("Failed to create PluginStreamListener, aborting channel");
944 return NS_BINDING_ABORTED;
945 }
946 }
948 // Otherwise we should be state loading, and call LoadObject with the channel
949 if (mType != eType_Loading) {
950 NS_NOTREACHED("Should be type loading at this point");
951 return NS_BINDING_ABORTED;
952 }
953 NS_ASSERTION(!mChannelLoaded, "mChannelLoaded set already?");
954 NS_ASSERTION(!mFinalListener, "mFinalListener exists already?");
956 mChannelLoaded = true;
958 nsCOMPtr<nsIChannel> chan(do_QueryInterface(aRequest));
959 NS_ASSERTION(chan, "Why is our request not a channel?");
961 nsCOMPtr<nsIURI> uri;
963 if (IsSuccessfulRequest(aRequest)) {
964 chan->GetURI(getter_AddRefs(uri));
965 }
967 if (!uri) {
968 LOG(("OBJLC [%p]: OnStartRequest: Request failed\n", this));
969 // If the request fails, we still call LoadObject() to handle fallback
970 // content and notifying of failure. (mChannelLoaded && !mChannel) indicates
971 // the bad state.
972 mChannel = nullptr;
973 LoadObject(true, false);
974 return NS_ERROR_FAILURE;
975 }
977 return LoadObject(true, false, aRequest);
978 }
980 NS_IMETHODIMP
981 nsObjectLoadingContent::OnStopRequest(nsIRequest *aRequest,
982 nsISupports *aContext,
983 nsresult aStatusCode)
984 {
985 NS_ENSURE_TRUE(nsContentUtils::IsCallerChrome(), NS_ERROR_NOT_AVAILABLE);
987 if (aRequest != mChannel) {
988 return NS_BINDING_ABORTED;
989 }
991 mChannel = nullptr;
993 if (mFinalListener) {
994 // This may re-enter in the case of plugin listeners
995 nsCOMPtr<nsIStreamListener> listenerGrip(mFinalListener);
996 mFinalListener = nullptr;
997 listenerGrip->OnStopRequest(aRequest, aContext, aStatusCode);
998 }
1000 // Return value doesn't matter
1001 return NS_OK;
1002 }
1005 // nsIStreamListener
1006 NS_IMETHODIMP
1007 nsObjectLoadingContent::OnDataAvailable(nsIRequest *aRequest,
1008 nsISupports *aContext,
1009 nsIInputStream *aInputStream,
1010 uint64_t aOffset, uint32_t aCount)
1011 {
1012 NS_ENSURE_TRUE(nsContentUtils::IsCallerChrome(), NS_ERROR_NOT_AVAILABLE);
1014 if (aRequest != mChannel) {
1015 return NS_BINDING_ABORTED;
1016 }
1018 if (mFinalListener) {
1019 // This may re-enter in the case of plugin listeners
1020 nsCOMPtr<nsIStreamListener> listenerGrip(mFinalListener);
1021 return listenerGrip->OnDataAvailable(aRequest, aContext, aInputStream,
1022 aOffset, aCount);
1023 }
1025 // We shouldn't have a connected channel with no final listener
1026 NS_NOTREACHED("Got data for channel with no connected final listener");
1027 mChannel = nullptr;
1029 return NS_ERROR_UNEXPECTED;
1030 }
1032 // nsIFrameLoaderOwner
1033 NS_IMETHODIMP
1034 nsObjectLoadingContent::GetFrameLoader(nsIFrameLoader** aFrameLoader)
1035 {
1036 NS_IF_ADDREF(*aFrameLoader = mFrameLoader);
1037 return NS_OK;
1038 }
1040 NS_IMETHODIMP_(already_AddRefed<nsFrameLoader>)
1041 nsObjectLoadingContent::GetFrameLoader()
1042 {
1043 nsRefPtr<nsFrameLoader> loader = mFrameLoader;
1044 return loader.forget();
1045 }
1047 NS_IMETHODIMP
1048 nsObjectLoadingContent::SwapFrameLoaders(nsIFrameLoaderOwner* aOtherLoader)
1049 {
1050 return NS_ERROR_NOT_IMPLEMENTED;
1051 }
1053 NS_IMETHODIMP
1054 nsObjectLoadingContent::GetActualType(nsACString& aType)
1055 {
1056 aType = mContentType;
1057 return NS_OK;
1058 }
1060 NS_IMETHODIMP
1061 nsObjectLoadingContent::GetDisplayedType(uint32_t* aType)
1062 {
1063 *aType = DisplayedType();
1064 return NS_OK;
1065 }
1067 NS_IMETHODIMP
1068 nsObjectLoadingContent::HasNewFrame(nsIObjectFrame* aFrame)
1069 {
1070 if (mType != eType_Plugin) {
1071 return NS_OK;
1072 }
1074 if (!aFrame) {
1075 // Lost our frame. If we aren't going to be getting a new frame, e.g. we've
1076 // become display:none, we'll want to stop the plugin. Queue a
1077 // CheckPluginStopEvent
1078 if (mInstanceOwner || mInstantiating) {
1079 if (mInstanceOwner) {
1080 mInstanceOwner->SetFrame(nullptr);
1081 }
1082 QueueCheckPluginStopEvent();
1083 }
1084 return NS_OK;
1085 }
1087 // Have a new frame
1089 if (!mInstanceOwner) {
1090 // We are successfully setup as type plugin, but have not spawned an
1091 // instance due to a lack of a frame.
1092 AsyncStartPluginInstance();
1093 return NS_OK;
1094 }
1096 // Otherwise, we're just changing frames
1097 // Set up relationship between instance owner and frame.
1098 nsObjectFrame *objFrame = static_cast<nsObjectFrame*>(aFrame);
1099 mInstanceOwner->SetFrame(objFrame);
1101 return NS_OK;
1102 }
1104 NS_IMETHODIMP
1105 nsObjectLoadingContent::GetPluginInstance(nsNPAPIPluginInstance** aInstance)
1106 {
1107 *aInstance = nullptr;
1109 if (!mInstanceOwner) {
1110 return NS_OK;
1111 }
1113 return mInstanceOwner->GetInstance(aInstance);
1114 }
1116 NS_IMETHODIMP
1117 nsObjectLoadingContent::GetContentTypeForMIMEType(const nsACString& aMIMEType,
1118 uint32_t* aType)
1119 {
1120 *aType = GetTypeOfContent(PromiseFlatCString(aMIMEType));
1121 return NS_OK;
1122 }
1124 NS_IMETHODIMP
1125 nsObjectLoadingContent::GetBaseURI(nsIURI **aResult)
1126 {
1127 NS_IF_ADDREF(*aResult = mBaseURI);
1128 return NS_OK;
1129 }
1131 // nsIInterfaceRequestor
1132 // We use a shim class to implement this so that JS consumers still
1133 // see an interface requestor even though WebIDL bindings don't expose
1134 // that stuff.
1135 class ObjectInterfaceRequestorShim MOZ_FINAL : public nsIInterfaceRequestor,
1136 public nsIChannelEventSink,
1137 public nsIStreamListener
1138 {
1139 public:
1140 NS_DECL_CYCLE_COLLECTING_ISUPPORTS
1141 NS_DECL_CYCLE_COLLECTION_CLASS_AMBIGUOUS(ObjectInterfaceRequestorShim,
1142 nsIInterfaceRequestor)
1143 NS_DECL_NSIINTERFACEREQUESTOR
1144 // nsRefPtr<nsObjectLoadingContent> fails due to ambiguous AddRef/Release,
1145 // hence the ugly static cast :(
1146 NS_FORWARD_NSICHANNELEVENTSINK(static_cast<nsObjectLoadingContent *>
1147 (mContent.get())->)
1148 NS_FORWARD_NSISTREAMLISTENER (static_cast<nsObjectLoadingContent *>
1149 (mContent.get())->)
1150 NS_FORWARD_NSIREQUESTOBSERVER (static_cast<nsObjectLoadingContent *>
1151 (mContent.get())->)
1153 ObjectInterfaceRequestorShim(nsIObjectLoadingContent* aContent)
1154 : mContent(aContent)
1155 {}
1157 protected:
1158 nsCOMPtr<nsIObjectLoadingContent> mContent;
1159 };
1161 NS_IMPL_CYCLE_COLLECTION(ObjectInterfaceRequestorShim, mContent)
1163 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(ObjectInterfaceRequestorShim)
1164 NS_INTERFACE_MAP_ENTRY(nsIInterfaceRequestor)
1165 NS_INTERFACE_MAP_ENTRY(nsIChannelEventSink)
1166 NS_INTERFACE_MAP_ENTRY(nsIStreamListener)
1167 NS_INTERFACE_MAP_ENTRY(nsIRequestObserver)
1168 NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIInterfaceRequestor)
1169 NS_INTERFACE_MAP_END
1171 NS_IMPL_CYCLE_COLLECTING_ADDREF(ObjectInterfaceRequestorShim)
1172 NS_IMPL_CYCLE_COLLECTING_RELEASE(ObjectInterfaceRequestorShim)
1174 NS_IMETHODIMP
1175 ObjectInterfaceRequestorShim::GetInterface(const nsIID & aIID, void **aResult)
1176 {
1177 if (aIID.Equals(NS_GET_IID(nsIChannelEventSink))) {
1178 nsIChannelEventSink* sink = this;
1179 *aResult = sink;
1180 NS_ADDREF(sink);
1181 return NS_OK;
1182 }
1183 return NS_NOINTERFACE;
1184 }
1186 // nsIChannelEventSink
1187 NS_IMETHODIMP
1188 nsObjectLoadingContent::AsyncOnChannelRedirect(nsIChannel *aOldChannel,
1189 nsIChannel *aNewChannel,
1190 uint32_t aFlags,
1191 nsIAsyncVerifyRedirectCallback *cb)
1192 {
1193 // If we're already busy with a new load, or have no load at all,
1194 // cancel the redirect.
1195 if (!mChannel || aOldChannel != mChannel) {
1196 return NS_BINDING_ABORTED;
1197 }
1199 mChannel = aNewChannel;
1200 cb->OnRedirectVerifyCallback(NS_OK);
1201 return NS_OK;
1202 }
1204 // <public>
1205 EventStates
1206 nsObjectLoadingContent::ObjectState() const
1207 {
1208 switch (mType) {
1209 case eType_Loading:
1210 return NS_EVENT_STATE_LOADING;
1211 case eType_Image:
1212 return ImageState();
1213 case eType_Plugin:
1214 case eType_Document:
1215 // These are OK. If documents start to load successfully, they display
1216 // something, and are thus not broken in this sense. The same goes for
1217 // plugins.
1218 return EventStates();
1219 case eType_Null:
1220 switch (mFallbackType) {
1221 case eFallbackSuppressed:
1222 return NS_EVENT_STATE_SUPPRESSED;
1223 case eFallbackUserDisabled:
1224 return NS_EVENT_STATE_USERDISABLED;
1225 case eFallbackClickToPlay:
1226 return NS_EVENT_STATE_TYPE_CLICK_TO_PLAY;
1227 case eFallbackPlayPreview:
1228 return NS_EVENT_STATE_TYPE_PLAY_PREVIEW;
1229 case eFallbackDisabled:
1230 return NS_EVENT_STATE_BROKEN | NS_EVENT_STATE_HANDLER_DISABLED;
1231 case eFallbackBlocklisted:
1232 return NS_EVENT_STATE_BROKEN | NS_EVENT_STATE_HANDLER_BLOCKED;
1233 case eFallbackCrashed:
1234 return NS_EVENT_STATE_BROKEN | NS_EVENT_STATE_HANDLER_CRASHED;
1235 case eFallbackUnsupported: {
1236 // Check to see if plugins are blocked on this platform.
1237 char* pluginsBlocked = PR_GetEnv("MOZ_PLUGINS_BLOCKED");
1238 if (pluginsBlocked && pluginsBlocked[0] == '1') {
1239 return NS_EVENT_STATE_BROKEN | NS_EVENT_STATE_TYPE_UNSUPPORTED_PLATFORM;
1240 } else {
1241 return NS_EVENT_STATE_BROKEN | NS_EVENT_STATE_TYPE_UNSUPPORTED;
1242 }
1243 }
1244 case eFallbackOutdated:
1245 case eFallbackAlternate:
1246 return NS_EVENT_STATE_BROKEN;
1247 case eFallbackVulnerableUpdatable:
1248 return NS_EVENT_STATE_VULNERABLE_UPDATABLE;
1249 case eFallbackVulnerableNoUpdate:
1250 return NS_EVENT_STATE_VULNERABLE_NO_UPDATE;
1251 }
1252 };
1253 NS_NOTREACHED("unknown type?");
1254 return NS_EVENT_STATE_LOADING;
1255 }
1257 // Returns false if mBaseURI is not acceptable for java applets.
1258 bool
1259 nsObjectLoadingContent::CheckJavaCodebase()
1260 {
1261 nsCOMPtr<nsIContent> thisContent =
1262 do_QueryInterface(static_cast<nsIImageLoadingContent*>(this));
1263 nsCOMPtr<nsIScriptSecurityManager> secMan =
1264 nsContentUtils::GetSecurityManager();
1265 nsCOMPtr<nsINetUtil> netutil = do_GetNetUtil();
1266 NS_ASSERTION(thisContent && secMan && netutil, "expected interfaces");
1269 // Note that mBaseURI is this tag's requested base URI, not the codebase of
1270 // the document for security purposes
1271 nsresult rv = secMan->CheckLoadURIWithPrincipal(thisContent->NodePrincipal(),
1272 mBaseURI, 0);
1273 if (NS_FAILED(rv)) {
1274 LOG(("OBJLC [%p]: Java codebase check failed", this));
1275 return false;
1276 }
1278 nsCOMPtr<nsIURI> principalBaseURI;
1279 rv = thisContent->NodePrincipal()->GetURI(getter_AddRefs(principalBaseURI));
1280 if (NS_FAILED(rv)) {
1281 NS_NOTREACHED("Failed to URI from node principal?");
1282 return false;
1283 }
1284 // We currently allow java's codebase to be non-same-origin, with
1285 // the exception of URIs that represent local files
1286 if (NS_URIIsLocalFile(mBaseURI) &&
1287 nsScriptSecurityManager::GetStrictFileOriginPolicy() &&
1288 !NS_RelaxStrictFileOriginPolicy(mBaseURI, principalBaseURI, true)) {
1289 LOG(("OBJLC [%p]: Java failed RelaxStrictFileOriginPolicy for file URI",
1290 this));
1291 return false;
1292 }
1294 return true;
1295 }
1297 bool
1298 nsObjectLoadingContent::CheckLoadPolicy(int16_t *aContentPolicy)
1299 {
1300 if (!aContentPolicy || !mURI) {
1301 NS_NOTREACHED("Doing it wrong");
1302 return false;
1303 }
1305 nsCOMPtr<nsIContent> thisContent =
1306 do_QueryInterface(static_cast<nsIImageLoadingContent*>(this));
1307 NS_ASSERTION(thisContent, "Must be an instance of content");
1309 nsIDocument* doc = thisContent->OwnerDoc();
1311 *aContentPolicy = nsIContentPolicy::ACCEPT;
1312 nsresult rv = NS_CheckContentLoadPolicy(nsIContentPolicy::TYPE_OBJECT,
1313 mURI,
1314 doc->NodePrincipal(),
1315 thisContent,
1316 mContentType,
1317 nullptr, //extra
1318 aContentPolicy,
1319 nsContentUtils::GetContentPolicy(),
1320 nsContentUtils::GetSecurityManager());
1321 NS_ENSURE_SUCCESS(rv, false);
1322 if (NS_CP_REJECTED(*aContentPolicy)) {
1323 nsAutoCString uri;
1324 nsAutoCString baseUri;
1325 mURI->GetSpec(uri);
1326 mURI->GetSpec(baseUri);
1327 LOG(("OBJLC [%p]: Content policy denied load of %s (base %s)",
1328 this, uri.get(), baseUri.get()));
1329 return false;
1330 }
1332 return true;
1333 }
1335 bool
1336 nsObjectLoadingContent::CheckProcessPolicy(int16_t *aContentPolicy)
1337 {
1338 if (!aContentPolicy) {
1339 NS_NOTREACHED("Null out variable");
1340 return false;
1341 }
1343 nsCOMPtr<nsIContent> thisContent =
1344 do_QueryInterface(static_cast<nsIImageLoadingContent*>(this));
1345 NS_ASSERTION(thisContent, "Must be an instance of content");
1347 nsIDocument* doc = thisContent->OwnerDoc();
1349 int32_t objectType;
1350 switch (mType) {
1351 case eType_Image:
1352 objectType = nsIContentPolicy::TYPE_IMAGE;
1353 break;
1354 case eType_Document:
1355 objectType = nsIContentPolicy::TYPE_DOCUMENT;
1356 break;
1357 case eType_Plugin:
1358 objectType = nsIContentPolicy::TYPE_OBJECT;
1359 break;
1360 default:
1361 NS_NOTREACHED("Calling checkProcessPolicy with a unloadable type");
1362 return false;
1363 }
1365 *aContentPolicy = nsIContentPolicy::ACCEPT;
1366 nsresult rv =
1367 NS_CheckContentProcessPolicy(objectType,
1368 mURI ? mURI : mBaseURI,
1369 doc->NodePrincipal(),
1370 static_cast<nsIImageLoadingContent*>(this),
1371 mContentType,
1372 nullptr, //extra
1373 aContentPolicy,
1374 nsContentUtils::GetContentPolicy(),
1375 nsContentUtils::GetSecurityManager());
1376 NS_ENSURE_SUCCESS(rv, false);
1378 if (NS_CP_REJECTED(*aContentPolicy)) {
1379 LOG(("OBJLC [%p]: CheckContentProcessPolicy rejected load", this));
1380 return false;
1381 }
1383 return true;
1384 }
1386 nsObjectLoadingContent::ParameterUpdateFlags
1387 nsObjectLoadingContent::UpdateObjectParameters(bool aJavaURI)
1388 {
1389 nsCOMPtr<nsIContent> thisContent =
1390 do_QueryInterface(static_cast<nsIImageLoadingContent*>(this));
1391 NS_ASSERTION(thisContent, "Must be an instance of content");
1393 uint32_t caps = GetCapabilities();
1394 LOG(("OBJLC [%p]: Updating object parameters", this));
1396 nsresult rv;
1397 nsAutoCString newMime;
1398 nsAutoString typeAttr;
1399 nsCOMPtr<nsIURI> newURI;
1400 nsCOMPtr<nsIURI> newBaseURI;
1401 ObjectType newType;
1402 bool isJava = false;
1403 // Set if this state can't be used to load anything, forces eType_Null
1404 bool stateInvalid = false;
1405 // Indicates what parameters changed.
1406 // eParamChannelChanged - means parameters that affect channel opening
1407 // decisions changed
1408 // eParamStateChanged - means anything that affects what content we load
1409 // changed, even if the channel we'd open remains the
1410 // same.
1411 //
1412 // State changes outside of the channel parameters only matter if we've
1413 // already opened a channel or tried to instantiate content, whereas channel
1414 // parameter changes require re-opening the channel even if we haven't gotten
1415 // that far.
1416 nsObjectLoadingContent::ParameterUpdateFlags retval = eParamNoChange;
1418 ///
1419 /// Initial MIME Type
1420 ///
1422 if (aJavaURI || thisContent->NodeInfo()->Equals(nsGkAtoms::applet)) {
1423 nsAdoptingCString javaMIME = Preferences::GetCString(kPrefJavaMIME);
1424 newMime = javaMIME;
1425 NS_ASSERTION(nsPluginHost::IsJavaMIMEType(newMime.get()),
1426 "plugin.mime.java should be recognized by IsJavaMIMEType");
1427 isJava = true;
1428 } else {
1429 nsAutoString rawTypeAttr;
1430 thisContent->GetAttr(kNameSpaceID_None, nsGkAtoms::type, rawTypeAttr);
1431 if (!rawTypeAttr.IsEmpty()) {
1432 typeAttr = rawTypeAttr;
1433 CopyUTF16toUTF8(rawTypeAttr, newMime);
1434 isJava = nsPluginHost::IsJavaMIMEType(newMime.get());
1435 }
1436 }
1438 ///
1439 /// classID
1440 ///
1442 if (caps & eSupportClassID) {
1443 nsAutoString classIDAttr;
1444 thisContent->GetAttr(kNameSpaceID_None, nsGkAtoms::classid, classIDAttr);
1445 if (!classIDAttr.IsEmpty()) {
1446 // Our classid support is limited to 'java:' ids
1447 nsAdoptingCString javaMIME = Preferences::GetCString(kPrefJavaMIME);
1448 NS_ASSERTION(nsPluginHost::IsJavaMIMEType(javaMIME.get()),
1449 "plugin.mime.java should be recognized by IsJavaMIMEType");
1450 if (StringBeginsWith(classIDAttr, NS_LITERAL_STRING("java:")) &&
1451 PluginExistsForType(javaMIME)) {
1452 newMime = javaMIME;
1453 isJava = true;
1454 } else {
1455 // XXX(johns): Our de-facto behavior since forever was to refuse to load
1456 // Objects who don't have a classid we support, regardless of other type
1457 // or uri info leads to a valid plugin.
1458 newMime.Truncate();
1459 stateInvalid = true;
1460 }
1461 }
1462 }
1464 ///
1465 /// Codebase
1466 ///
1468 nsAutoString codebaseStr;
1469 nsCOMPtr<nsIURI> docBaseURI = thisContent->GetBaseURI();
1470 bool hasCodebase = thisContent->HasAttr(kNameSpaceID_None, nsGkAtoms::codebase);
1471 if (hasCodebase)
1472 thisContent->GetAttr(kNameSpaceID_None, nsGkAtoms::codebase, codebaseStr);
1475 // Java wants the codebase attribute even if it occurs in <param> tags
1476 // XXX(johns): This duplicates a chunk of code from nsInstanceOwner, see
1477 // bug 853995
1478 if (isJava) {
1479 // Find all <param> tags that are nested beneath us, but not beneath another
1480 // object/applet tag.
1481 nsCOMArray<nsIDOMElement> ourParams;
1482 nsCOMPtr<nsIDOMElement> mydomElement = do_QueryInterface(thisContent);
1484 nsCOMPtr<nsIDOMHTMLCollection> allParams;
1485 NS_NAMED_LITERAL_STRING(xhtml_ns, "http://www.w3.org/1999/xhtml");
1486 mydomElement->GetElementsByTagNameNS(xhtml_ns, NS_LITERAL_STRING("param"),
1487 getter_AddRefs(allParams));
1488 if (allParams) {
1489 uint32_t numAllParams;
1490 allParams->GetLength(&numAllParams);
1491 for (uint32_t i = 0; i < numAllParams; i++) {
1492 nsCOMPtr<nsIDOMNode> pnode;
1493 allParams->Item(i, getter_AddRefs(pnode));
1494 nsCOMPtr<nsIDOMElement> domelement = do_QueryInterface(pnode);
1495 if (domelement) {
1496 nsAutoString name;
1497 domelement->GetAttribute(NS_LITERAL_STRING("name"), name);
1498 name.Trim(" \n\r\t\b", true, true, false);
1499 if (name.EqualsIgnoreCase("codebase")) {
1500 // Find the first plugin element parent
1501 nsCOMPtr<nsIDOMNode> parent;
1502 nsCOMPtr<nsIDOMHTMLObjectElement> domobject;
1503 nsCOMPtr<nsIDOMHTMLAppletElement> domapplet;
1504 pnode->GetParentNode(getter_AddRefs(parent));
1505 while (!(domobject || domapplet) && parent) {
1506 domobject = do_QueryInterface(parent);
1507 domapplet = do_QueryInterface(parent);
1508 nsCOMPtr<nsIDOMNode> temp;
1509 parent->GetParentNode(getter_AddRefs(temp));
1510 parent = temp;
1511 }
1512 if (domapplet || domobject) {
1513 if (domapplet) {
1514 parent = do_QueryInterface(domapplet);
1515 }
1516 else {
1517 parent = do_QueryInterface(domobject);
1518 }
1519 nsCOMPtr<nsIDOMNode> mydomNode = do_QueryInterface(mydomElement);
1520 if (parent == mydomNode) {
1521 hasCodebase = true;
1522 domelement->GetAttribute(NS_LITERAL_STRING("value"),
1523 codebaseStr);
1524 codebaseStr.Trim(" \n\r\t\b", true, true, false);
1525 }
1526 }
1527 }
1528 }
1529 }
1530 }
1531 }
1533 if (isJava && hasCodebase && codebaseStr.IsEmpty()) {
1534 // Java treats codebase="" as "/"
1535 codebaseStr.AssignLiteral("/");
1536 // XXX(johns): This doesn't cover the case of "https:" which java would
1537 // interpret as "https:///" but we interpret as this document's
1538 // URI but with a changed scheme.
1539 } else if (isJava && !hasCodebase) {
1540 // Java expects a directory as the codebase, or else it will construct
1541 // relative URIs incorrectly :(
1542 codebaseStr.AssignLiteral(".");
1543 }
1545 if (!codebaseStr.IsEmpty()) {
1546 rv = nsContentUtils::NewURIWithDocumentCharset(getter_AddRefs(newBaseURI),
1547 codebaseStr,
1548 thisContent->OwnerDoc(),
1549 docBaseURI);
1550 if (NS_SUCCEEDED(rv)) {
1551 NS_TryToSetImmutable(newBaseURI);
1552 } else {
1553 // Malformed URI
1554 LOG(("OBJLC [%p]: Could not parse plugin's codebase as a URI, "
1555 "will use document baseURI instead", this));
1556 }
1557 }
1559 // If we failed to build a valid URI, use the document's base URI
1560 if (!newBaseURI) {
1561 newBaseURI = docBaseURI;
1562 }
1564 ///
1565 /// URI
1566 ///
1568 nsAutoString uriStr;
1569 // Different elements keep this in various locations
1570 if (isJava) {
1571 // Applet tags and embed/object with explicit java MIMEs have src/data
1572 // attributes that are not meant to be parsed as URIs or opened by the
1573 // browser -- act as if they are null. (Setting these attributes triggers a
1574 // force-load, so tracking the old value to determine if they have changed
1575 // is not necessary.)
1576 } else if (thisContent->NodeInfo()->Equals(nsGkAtoms::object)) {
1577 thisContent->GetAttr(kNameSpaceID_None, nsGkAtoms::data, uriStr);
1578 } else if (thisContent->NodeInfo()->Equals(nsGkAtoms::embed)) {
1579 thisContent->GetAttr(kNameSpaceID_None, nsGkAtoms::src, uriStr);
1580 } else {
1581 // Applet tags should always have a java MIME type at this point
1582 NS_NOTREACHED("Unrecognized plugin-loading tag");
1583 }
1585 // Note that the baseURI changing could affect the newURI, even if uriStr did
1586 // not change.
1587 if (!uriStr.IsEmpty()) {
1588 rv = nsContentUtils::NewURIWithDocumentCharset(getter_AddRefs(newURI),
1589 uriStr,
1590 thisContent->OwnerDoc(),
1591 newBaseURI);
1592 if (NS_SUCCEEDED(rv)) {
1593 NS_TryToSetImmutable(newURI);
1594 } else {
1595 stateInvalid = true;
1596 }
1597 }
1599 // For eAllowPluginSkipChannel tags, if we have a non-plugin type, but can get
1600 // a plugin type from the extension, prefer that to falling back to a channel.
1601 if (GetTypeOfContent(newMime) != eType_Plugin && newURI &&
1602 (caps & eAllowPluginSkipChannel) &&
1603 IsPluginEnabledByExtension(newURI, newMime)) {
1604 LOG(("OBJLC [%p]: Using extension as type hint (%s)", this, newMime.get()));
1605 if (!isJava && nsPluginHost::IsJavaMIMEType(newMime.get())) {
1606 return UpdateObjectParameters(true);
1607 }
1608 }
1610 ///
1611 /// Check if the original (pre-channel) content-type or URI changed, and
1612 /// record mOriginal{ContentType,URI}
1613 ///
1615 if ((mOriginalContentType != newMime) || !URIEquals(mOriginalURI, newURI)) {
1616 // These parameters changing requires re-opening the channel, so don't
1617 // consider the currently-open channel below
1618 // XXX(johns): Changing the mime type might change our decision on whether
1619 // or not we load a channel, so we count changes to it as a
1620 // channel parameter change for the sake of simplicity.
1621 retval = (ParameterUpdateFlags)(retval | eParamChannelChanged);
1622 LOG(("OBJLC [%p]: Channel parameters changed", this));
1623 }
1624 mOriginalContentType = newMime;
1625 mOriginalURI = newURI;
1627 ///
1628 /// If we have a channel, see if its MIME type should take precendence and
1629 /// check the final (redirected) URL
1630 ///
1632 // If we have a loaded channel and channel parameters did not change, use it
1633 // to determine what we would load.
1634 bool useChannel = mChannelLoaded && !(retval & eParamChannelChanged);
1635 // If we have a channel and are type loading, as opposed to having an existing
1636 // channel for a previous load.
1637 bool newChannel = useChannel && mType == eType_Loading;
1639 if (newChannel && mChannel) {
1640 nsCString channelType;
1641 rv = mChannel->GetContentType(channelType);
1642 if (NS_FAILED(rv)) {
1643 NS_NOTREACHED("GetContentType failed");
1644 stateInvalid = true;
1645 channelType.Truncate();
1646 }
1648 LOG(("OBJLC [%p]: Channel has a content type of %s", this, channelType.get()));
1650 bool binaryChannelType = false;
1651 if (channelType.EqualsASCII(APPLICATION_GUESS_FROM_EXT)) {
1652 channelType = APPLICATION_OCTET_STREAM;
1653 mChannel->SetContentType(channelType);
1654 binaryChannelType = true;
1655 } else if (channelType.EqualsASCII(APPLICATION_OCTET_STREAM)
1656 || channelType.EqualsASCII(BINARY_OCTET_STREAM)) {
1657 binaryChannelType = true;
1658 }
1660 // Channel can change our URI through redirection
1661 rv = NS_GetFinalChannelURI(mChannel, getter_AddRefs(newURI));
1662 if (NS_FAILED(rv)) {
1663 NS_NOTREACHED("NS_GetFinalChannelURI failure");
1664 stateInvalid = true;
1665 }
1667 ObjectType typeHint = newMime.IsEmpty() ?
1668 eType_Null : GetTypeOfContent(newMime);
1670 //
1671 // In order of preference:
1672 //
1673 // 1) Perform typemustmatch check.
1674 // If check is sucessful use type without further checks.
1675 // If check is unsuccessful set stateInvalid to true
1676 // 2) Use our type hint if it matches a plugin
1677 // 3) If we have eAllowPluginSkipChannel, use the uri file extension if
1678 // it matches a plugin
1679 // 4) If the channel returns a binary stream type:
1680 // 4a) If we have a type non-null non-document type hint, use that
1681 // 4b) If the uri file extension matches a plugin type, use that
1682 // 5) Use the channel type
1684 bool overrideChannelType = false;
1685 if (thisContent->HasAttr(kNameSpaceID_None, nsGkAtoms::typemustmatch)) {
1686 if (!typeAttr.LowerCaseEqualsASCII(channelType.get())) {
1687 stateInvalid = true;
1688 }
1689 } else if (typeHint == eType_Plugin) {
1690 LOG(("OBJLC [%p]: Using plugin type hint in favor of any channel type",
1691 this));
1692 overrideChannelType = true;
1693 } else if ((caps & eAllowPluginSkipChannel) &&
1694 IsPluginEnabledByExtension(newURI, newMime)) {
1695 LOG(("OBJLC [%p]: Using extension as type hint for "
1696 "eAllowPluginSkipChannel tag (%s)", this, newMime.get()));
1697 overrideChannelType = true;
1698 } else if (binaryChannelType &&
1699 typeHint != eType_Null && typeHint != eType_Document) {
1700 LOG(("OBJLC [%p]: Using type hint in favor of binary channel type",
1701 this));
1702 overrideChannelType = true;
1703 } else if (binaryChannelType &&
1704 IsPluginEnabledByExtension(newURI, newMime)) {
1705 LOG(("OBJLC [%p]: Using extension as type hint for binary channel (%s)",
1706 this, newMime.get()));
1707 overrideChannelType = true;
1708 }
1710 if (overrideChannelType) {
1711 // Set the type we'll use for dispatch on the channel. Otherwise we could
1712 // end up trying to dispatch to a nsFrameLoader, which will complain that
1713 // it couldn't find a way to handle application/octet-stream
1714 nsAutoCString parsedMime, dummy;
1715 NS_ParseContentType(newMime, parsedMime, dummy);
1716 if (!parsedMime.IsEmpty()) {
1717 mChannel->SetContentType(parsedMime);
1718 }
1719 } else {
1720 newMime = channelType;
1721 if (nsPluginHost::IsJavaMIMEType(newMime.get())) {
1722 // Java does not load with a channel, and being java retroactively
1723 // changes how we may have interpreted the codebase to construct this
1724 // URI above. Because the behavior here is more or less undefined, play
1725 // it safe and reject the load.
1726 LOG(("OBJLC [%p]: Refusing to load with channel with java MIME",
1727 this));
1728 stateInvalid = true;
1729 }
1730 }
1731 } else if (newChannel) {
1732 LOG(("OBJLC [%p]: We failed to open a channel, marking invalid", this));
1733 stateInvalid = true;
1734 }
1736 ///
1737 /// Determine final type
1738 ///
1739 // In order of preference:
1740 // 1) If we have attempted channel load, or set stateInvalid above, the type
1741 // is always null (fallback)
1742 // 2) If we have a loaded channel, we grabbed its mimeType above, use that
1743 // type.
1744 // 3) If we have a plugin type and no URI, use that type.
1745 // 4) If we have a plugin type and eAllowPluginSkipChannel, use that type.
1746 // 5) if we have a URI, set type to loading to indicate we'd need a channel
1747 // to proceed.
1748 // 6) Otherwise, type null to indicate unloadable content (fallback)
1749 //
1751 if (stateInvalid) {
1752 newType = eType_Null;
1753 newMime.Truncate();
1754 } else if (newChannel) {
1755 // If newChannel is set above, we considered it in setting newMime
1756 newType = GetTypeOfContent(newMime);
1757 LOG(("OBJLC [%p]: Using channel type", this));
1758 } else if (((caps & eAllowPluginSkipChannel) || !newURI) &&
1759 GetTypeOfContent(newMime) == eType_Plugin) {
1760 newType = eType_Plugin;
1761 LOG(("OBJLC [%p]: Plugin type with no URI, skipping channel load", this));
1762 } else if (newURI) {
1763 // We could potentially load this if we opened a channel on mURI, indicate
1764 // This by leaving type as loading
1765 newType = eType_Loading;
1766 } else {
1767 // Unloadable - no URI, and no plugin type. Non-plugin types (images,
1768 // documents) always load with a channel.
1769 newType = eType_Null;
1770 }
1772 ///
1773 /// Handle existing channels
1774 ///
1776 if (useChannel && newType == eType_Loading) {
1777 // We decided to use a channel, and also that the previous channel is still
1778 // usable, so re-use the existing values.
1779 newType = mType;
1780 newMime = mContentType;
1781 newURI = mURI;
1782 } else if (useChannel && !newChannel) {
1783 // We have an existing channel, but did not decide to use one.
1784 retval = (ParameterUpdateFlags)(retval | eParamChannelChanged);
1785 useChannel = false;
1786 }
1788 ///
1789 /// Update changed values
1790 ///
1792 if (newType != mType) {
1793 retval = (ParameterUpdateFlags)(retval | eParamStateChanged);
1794 LOG(("OBJLC [%p]: Type changed from %u -> %u", this, mType, newType));
1795 mType = newType;
1796 }
1798 if (!URIEquals(mBaseURI, newBaseURI)) {
1799 if (isJava) {
1800 // Java bases its class loading on the base URI, so we consider the state
1801 // to have changed if this changes. If the object is using a relative URI,
1802 // mURI will have changed below regardless
1803 retval = (ParameterUpdateFlags)(retval | eParamStateChanged);
1804 }
1805 LOG(("OBJLC [%p]: Object effective baseURI changed", this));
1806 mBaseURI = newBaseURI;
1807 }
1809 if (!URIEquals(newURI, mURI)) {
1810 retval = (ParameterUpdateFlags)(retval | eParamStateChanged);
1811 LOG(("OBJLC [%p]: Object effective URI changed", this));
1812 mURI = newURI;
1813 }
1815 // We don't update content type when loading, as the type is not final and we
1816 // don't want to superfluously change between mOriginalContentType ->
1817 // mContentType when doing |obj.data = obj.data| with a channel and differing
1818 // type.
1819 if (mType != eType_Loading && mContentType != newMime) {
1820 retval = (ParameterUpdateFlags)(retval | eParamStateChanged);
1821 retval = (ParameterUpdateFlags)(retval | eParamContentTypeChanged);
1822 LOG(("OBJLC [%p]: Object effective mime type changed (%s -> %s)",
1823 this, mContentType.get(), newMime.get()));
1824 mContentType = newMime;
1825 }
1827 // If we decided to keep using info from an old channel, but also that state
1828 // changed, we need to invalidate it.
1829 if (useChannel && !newChannel && (retval & eParamStateChanged)) {
1830 mType = eType_Loading;
1831 retval = (ParameterUpdateFlags)(retval | eParamChannelChanged);
1832 }
1834 return retval;
1835 }
1837 // Used by PluginDocument to kick off our initial load from the already-opened
1838 // channel.
1839 NS_IMETHODIMP
1840 nsObjectLoadingContent::InitializeFromChannel(nsIRequest *aChannel)
1841 {
1842 LOG(("OBJLC [%p] InitializeFromChannel: %p", this, aChannel));
1843 if (mType != eType_Loading || mChannel) {
1844 // We could technically call UnloadObject() here, if consumers have a valid
1845 // reason for wanting to call this on an already-loaded tag.
1846 NS_NOTREACHED("Should not have begun loading at this point");
1847 return NS_ERROR_UNEXPECTED;
1848 }
1850 // Because we didn't open this channel from an initial LoadObject, we'll
1851 // update our parameters now, so the OnStartRequest->LoadObject doesn't
1852 // believe our src/type suddenly changed.
1853 UpdateObjectParameters();
1854 // But we always want to load from a channel, in this case.
1855 mType = eType_Loading;
1856 mChannel = do_QueryInterface(aChannel);
1857 NS_ASSERTION(mChannel, "passed a request that is not a channel");
1859 // OnStartRequest will now see we have a channel in the loading state, and
1860 // call into LoadObject. There's a possibility LoadObject will decide not to
1861 // load anything from a channel - it will call CloseChannel() in that case.
1862 return NS_OK;
1863 }
1865 // Only OnStartRequest should be passing the channel parameter
1866 nsresult
1867 nsObjectLoadingContent::LoadObject(bool aNotify,
1868 bool aForceLoad)
1869 {
1870 return LoadObject(aNotify, aForceLoad, nullptr);
1871 }
1873 nsresult
1874 nsObjectLoadingContent::LoadObject(bool aNotify,
1875 bool aForceLoad,
1876 nsIRequest *aLoadingChannel)
1877 {
1878 nsCOMPtr<nsIContent> thisContent =
1879 do_QueryInterface(static_cast<nsIImageLoadingContent*>(this));
1880 NS_ASSERTION(thisContent, "must be a content");
1881 nsIDocument* doc = thisContent->OwnerDoc();
1882 nsresult rv = NS_OK;
1884 // Sanity check
1885 if (!InActiveDocument(thisContent)) {
1886 NS_NOTREACHED("LoadObject called while not bound to an active document");
1887 return NS_ERROR_UNEXPECTED;
1888 }
1890 // XXX(johns): In these cases, we refuse to touch our content and just
1891 // remain unloaded, as per legacy behavior. It would make more sense to
1892 // load fallback content initially and refuse to ever change state again.
1893 if (doc->IsBeingUsedAsImage() || doc->IsLoadedAsData()) {
1894 return NS_OK;
1895 }
1897 LOG(("OBJLC [%p]: LoadObject called, notify %u, forceload %u, channel %p",
1898 this, aNotify, aForceLoad, aLoadingChannel));
1900 // We can't re-use an already open channel, but aForceLoad may make us try
1901 // to load a plugin without any changes in channel state.
1902 if (aForceLoad && mChannelLoaded) {
1903 CloseChannel();
1904 mChannelLoaded = false;
1905 }
1907 // Save these for NotifyStateChanged();
1908 EventStates oldState = ObjectState();
1909 ObjectType oldType = mType;
1911 ParameterUpdateFlags stateChange = UpdateObjectParameters();
1913 if (!stateChange && !aForceLoad) {
1914 return NS_OK;
1915 }
1917 ///
1918 /// State has changed, unload existing content and attempt to load new type
1919 ///
1920 LOG(("OBJLC [%p]: LoadObject - plugin state changed (%u)",
1921 this, stateChange));
1923 // Setup fallback info. We may also change type to fallback below in case of
1924 // sanity/OOM/etc. errors. We default to showing alternate content
1925 // NOTE LoadFallback can override this in some cases
1926 FallbackType fallbackType = eFallbackAlternate;
1928 // mType can differ with GetTypeOfContent(mContentType) if we support this
1929 // type, but the parameters are invalid e.g. a embed tag with type "image/png"
1930 // but no URI -- don't show a plugin error or unknown type error in that case.
1931 if (mType == eType_Null && GetTypeOfContent(mContentType) == eType_Null) {
1932 fallbackType = eFallbackUnsupported;
1933 }
1935 // Explicit user activation should reset if the object changes content types
1936 if (mActivated && (stateChange & eParamContentTypeChanged)) {
1937 LOG(("OBJLC [%p]: Content type changed, clearing activation state", this));
1938 mActivated = false;
1939 }
1941 // We synchronously start/stop plugin instances below, which may spin the
1942 // event loop. Re-entering into the load is fine, but at that point the
1943 // original load call needs to abort when unwinding
1944 // NOTE this is located *after* the state change check, a subseqent load
1945 // with no subsequently changed state will be a no-op.
1946 if (mIsLoading) {
1947 LOG(("OBJLC [%p]: Re-entering into LoadObject", this));
1948 }
1949 mIsLoading = true;
1950 AutoSetLoadingToFalse reentryCheck(this);
1952 // Unload existing content, keeping in mind stopping plugins might spin the
1953 // event loop. Note that we check for still-open channels below
1954 UnloadObject(false); // Don't reset state
1955 if (!mIsLoading) {
1956 // The event loop must've spun and re-entered into LoadObject, which
1957 // finished the load
1958 LOG(("OBJLC [%p]: Re-entered into LoadObject, aborting outer load", this));
1959 return NS_OK;
1960 }
1962 // Determine what's going on with our channel.
1963 if (stateChange & eParamChannelChanged) {
1964 // If the channel params changed, throw away the channel, but unset
1965 // mChannelLoaded so we'll still try to open a new one for this load if
1966 // necessary
1967 CloseChannel();
1968 mChannelLoaded = false;
1969 } else if (mType == eType_Null && mChannel) {
1970 // If we opened a channel but then failed to find a loadable state, throw it
1971 // away. mChannelLoaded will indicate that we tried to load a channel at one
1972 // point so we wont recurse
1973 CloseChannel();
1974 } else if (mType == eType_Loading && mChannel) {
1975 // We're still waiting on a channel load, already opened one, and
1976 // channel parameters didn't change
1977 return NS_OK;
1978 } else if (mChannelLoaded && mChannel != aLoadingChannel) {
1979 // The only time we should have a loaded channel with a changed state is
1980 // when the channel has just opened -- in which case this call should
1981 // have originated from OnStartRequest
1982 NS_NOTREACHED("Loading with a channel, but state doesn't make sense");
1983 return NS_OK;
1984 }
1986 //
1987 // Security checks
1988 //
1990 if (mType != eType_Null) {
1991 bool allowLoad = true;
1992 if (nsPluginHost::IsJavaMIMEType(mContentType.get())) {
1993 allowLoad = CheckJavaCodebase();
1994 }
1995 int16_t contentPolicy = nsIContentPolicy::ACCEPT;
1996 // If mChannelLoaded is set we presumably already passed load policy
1997 if (allowLoad && mURI && !mChannelLoaded) {
1998 allowLoad = CheckLoadPolicy(&contentPolicy);
1999 }
2000 // If we're loading a type now, check ProcessPolicy. Note that we may check
2001 // both now in the case of plugins whose type is determined before opening a
2002 // channel.
2003 if (allowLoad && mType != eType_Loading) {
2004 allowLoad = CheckProcessPolicy(&contentPolicy);
2005 }
2007 // Content policy implementations can mutate the DOM, check for re-entry
2008 if (!mIsLoading) {
2009 LOG(("OBJLC [%p]: We re-entered in content policy, leaving original load",
2010 this));
2011 return NS_OK;
2012 }
2014 // Load denied, switch to fallback and set disabled/suppressed if applicable
2015 if (!allowLoad) {
2016 LOG(("OBJLC [%p]: Load denied by policy", this));
2017 mType = eType_Null;
2018 if (contentPolicy == nsIContentPolicy::REJECT_TYPE) {
2019 // XXX(johns) This is assuming that we were rejected by
2020 // nsContentBlocker, which rejects by type if permissions
2021 // reject plugins
2022 fallbackType = eFallbackUserDisabled;
2023 } else {
2024 fallbackType = eFallbackSuppressed;
2025 }
2026 }
2027 }
2029 // Don't allow view-source scheme.
2030 // view-source is the only scheme to which this applies at the moment due to
2031 // potential timing attacks to read data from cross-origin documents. If this
2032 // widens we should add a protocol flag for whether the scheme is only allowed
2033 // in top and use something like nsNetUtil::NS_URIChainHasFlags.
2034 if (mType != eType_Null) {
2035 nsCOMPtr<nsIURI> tempURI = mURI;
2036 nsCOMPtr<nsINestedURI> nestedURI = do_QueryInterface(tempURI);
2037 while (nestedURI) {
2038 // view-source should always be an nsINestedURI, loop and check the
2039 // scheme on this and all inner URIs that are also nested URIs.
2040 bool isViewSource = false;
2041 rv = tempURI->SchemeIs("view-source", &isViewSource);
2042 if (NS_FAILED(rv) || isViewSource) {
2043 LOG(("OBJLC [%p]: Blocking as effective URI has view-source scheme",
2044 this));
2045 mType = eType_Null;
2046 break;
2047 }
2049 nestedURI->GetInnerURI(getter_AddRefs(tempURI));
2050 nestedURI = do_QueryInterface(tempURI);
2051 }
2052 }
2054 // If we're a plugin but shouldn't start yet, load fallback with
2055 // reason click-to-play instead. Items resolved as Image/Document
2056 // will not be checked for previews, as well as invalid plugins
2057 // (they will not have the mContentType set).
2058 FallbackType clickToPlayReason;
2059 if (!mActivated && (mType == eType_Null || mType == eType_Plugin) &&
2060 !ShouldPlay(clickToPlayReason, false)) {
2061 LOG(("OBJLC [%p]: Marking plugin as click-to-play", this));
2062 mType = eType_Null;
2063 fallbackType = clickToPlayReason;
2064 }
2066 if (!mActivated && mType == eType_Plugin) {
2067 // Object passed ShouldPlay, so it should be considered
2068 // activated until it changes content type
2069 LOG(("OBJLC [%p]: Object implicitly activated", this));
2070 mActivated = true;
2071 }
2073 // Sanity check: We shouldn't have any loaded resources, pending events, or
2074 // a final listener at this point
2075 if (mFrameLoader || mPendingInstantiateEvent || mInstanceOwner ||
2076 mPendingCheckPluginStopEvent || mFinalListener)
2077 {
2078 NS_NOTREACHED("Trying to load new plugin with existing content");
2079 rv = NS_ERROR_UNEXPECTED;
2080 return NS_OK;
2081 }
2083 // More sanity-checking:
2084 // If mChannel is set, mChannelLoaded should be set, and vice-versa
2085 if (mType != eType_Null && !!mChannel != mChannelLoaded) {
2086 NS_NOTREACHED("Trying to load with bad channel state");
2087 rv = NS_ERROR_UNEXPECTED;
2088 return NS_OK;
2089 }
2091 ///
2092 /// Attempt to load new type
2093 ///
2095 // We don't set mFinalListener until OnStartRequest has been called, to
2096 // prevent re-entry ugliness with CloseChannel()
2097 nsCOMPtr<nsIStreamListener> finalListener;
2098 // If we decide to synchronously spawn a plugin, we do it after firing
2099 // notifications to avoid re-entry causing notifications to fire out of order.
2100 bool doSpawnPlugin = false;
2101 switch (mType) {
2102 case eType_Image:
2103 if (!mChannel) {
2104 // We have a LoadImage() call, but UpdateObjectParameters requires a
2105 // channel for images, so this is not a valid state.
2106 NS_NOTREACHED("Attempting to load image without a channel?");
2107 rv = NS_ERROR_UNEXPECTED;
2108 break;
2109 }
2110 rv = LoadImageWithChannel(mChannel, getter_AddRefs(finalListener));
2111 // finalListener will receive OnStartRequest below
2112 break;
2113 case eType_Plugin:
2114 {
2115 if (mChannel) {
2116 // Force a sync state change now, we need the frame created
2117 NotifyStateChanged(oldType, oldState, true, aNotify);
2118 oldType = mType;
2119 oldState = ObjectState();
2121 if (!thisContent->GetPrimaryFrame()) {
2122 // We're un-rendered, and can't instantiate a plugin. HasNewFrame will
2123 // re-start us when we can proceed.
2124 LOG(("OBJLC [%p]: Aborting load - plugin-type, but no frame", this));
2125 CloseChannel();
2126 break;
2127 }
2129 // We'll handle this below
2130 doSpawnPlugin = true;
2131 } else {
2132 rv = AsyncStartPluginInstance();
2133 }
2134 }
2135 break;
2136 case eType_Document:
2137 {
2138 if (!mChannel) {
2139 // We could mFrameLoader->LoadURI(mURI), but UpdateObjectParameters
2140 // requires documents have a channel, so this is not a valid state.
2141 NS_NOTREACHED("Attempting to load a document without a channel");
2142 mType = eType_Null;
2143 break;
2144 }
2146 mFrameLoader = nsFrameLoader::Create(thisContent->AsElement(),
2147 mNetworkCreated);
2148 if (!mFrameLoader) {
2149 NS_NOTREACHED("nsFrameLoader::Create failed");
2150 mType = eType_Null;
2151 break;
2152 }
2154 rv = mFrameLoader->CheckForRecursiveLoad(mURI);
2155 if (NS_FAILED(rv)) {
2156 LOG(("OBJLC [%p]: Aborting recursive load", this));
2157 mFrameLoader->Destroy();
2158 mFrameLoader = nullptr;
2159 mType = eType_Null;
2160 break;
2161 }
2163 // We're loading a document, so we have to set LOAD_DOCUMENT_URI
2164 // (especially important for firing onload)
2165 nsLoadFlags flags = 0;
2166 mChannel->GetLoadFlags(&flags);
2167 flags |= nsIChannel::LOAD_DOCUMENT_URI;
2168 mChannel->SetLoadFlags(flags);
2170 nsCOMPtr<nsIDocShell> docShell;
2171 rv = mFrameLoader->GetDocShell(getter_AddRefs(docShell));
2172 if (NS_FAILED(rv)) {
2173 NS_NOTREACHED("Could not get DocShell from mFrameLoader?");
2174 mType = eType_Null;
2175 break;
2176 }
2178 nsCOMPtr<nsIInterfaceRequestor> req(do_QueryInterface(docShell));
2179 NS_ASSERTION(req, "Docshell must be an ifreq");
2181 nsCOMPtr<nsIURILoader>
2182 uriLoader(do_GetService(NS_URI_LOADER_CONTRACTID, &rv));
2183 if (NS_FAILED(rv)) {
2184 NS_NOTREACHED("Failed to get uriLoader service");
2185 mType = eType_Null;
2186 break;
2187 }
2188 rv = uriLoader->OpenChannel(mChannel, nsIURILoader::DONT_RETARGET, req,
2189 getter_AddRefs(finalListener));
2190 // finalListener will receive OnStartRequest below
2191 }
2192 break;
2193 case eType_Loading:
2194 // If our type remains Loading, we need a channel to proceed
2195 rv = OpenChannel();
2196 if (NS_FAILED(rv)) {
2197 LOG(("OBJLC [%p]: OpenChannel returned failure (%u)", this, rv));
2198 }
2199 break;
2200 case eType_Null:
2201 // Handled below, silence compiler warnings
2202 break;
2203 };
2205 //
2206 // Loaded, handle notifications and fallback
2207 //
2208 if (NS_FAILED(rv)) {
2209 // If we failed in the loading hunk above, switch to fallback
2210 LOG(("OBJLC [%p]: Loading failed, switching to fallback", this));
2211 mType = eType_Null;
2212 }
2214 // If we didn't load anything, handle switching to fallback state
2215 if (mType == eType_Null) {
2216 LOG(("OBJLC [%p]: Loading fallback, type %u", this, fallbackType));
2217 NS_ASSERTION(!mFrameLoader && !mInstanceOwner,
2218 "switched to type null but also loaded something");
2220 if (mChannel) {
2221 // If we were loading with a channel but then failed over, throw it away
2222 CloseChannel();
2223 }
2225 // Don't try to initialize plugins or final listener below
2226 doSpawnPlugin = false;
2227 finalListener = nullptr;
2229 // Don't notify, as LoadFallback doesn't know of our previous state
2230 // (so really this is just setting mFallbackType)
2231 LoadFallback(fallbackType, false);
2232 }
2234 // Notify of our final state
2235 NotifyStateChanged(oldType, oldState, false, aNotify);
2236 NS_ENSURE_TRUE(mIsLoading, NS_OK);
2239 //
2240 // Spawning plugins and dispatching to the final listener may re-enter, so are
2241 // delayed until after we fire a notification, to prevent missing
2242 // notifications or firing them out of order.
2243 //
2244 // Note that we ensured that we entered into LoadObject() from
2245 // ::OnStartRequest above when loading with a channel.
2246 //
2248 rv = NS_OK;
2249 if (doSpawnPlugin) {
2250 rv = InstantiatePluginInstance(true);
2251 NS_ENSURE_TRUE(mIsLoading, NS_OK);
2252 // Create the final listener if we're loading with a channel. We can't do
2253 // this in the loading block above as it requires an instance.
2254 if (aLoadingChannel && NS_SUCCEEDED(rv)) {
2255 if (NS_SUCCEEDED(rv) && MakePluginListener()) {
2256 rv = mFinalListener->OnStartRequest(mChannel, nullptr);
2257 if (NS_FAILED(rv)) {
2258 // Plugins can reject their initial stream, but continue to run.
2259 CloseChannel();
2260 NS_ENSURE_TRUE(mIsLoading, NS_OK);
2261 rv = NS_OK;
2262 }
2263 }
2264 }
2265 } else if (finalListener) {
2266 NS_ASSERTION(mType != eType_Null && mType != eType_Loading,
2267 "We should not have a final listener with a non-loaded type");
2268 mFinalListener = finalListener;
2269 rv = finalListener->OnStartRequest(mChannel, nullptr);
2270 }
2272 if (NS_FAILED(rv) && mIsLoading) {
2273 // Since we've already notified of our transition, we can just Unload and
2274 // call LoadFallback (which will notify again)
2275 mType = eType_Null;
2276 UnloadObject(false);
2277 NS_ENSURE_TRUE(mIsLoading, NS_OK);
2278 CloseChannel();
2279 LoadFallback(fallbackType, true);
2280 }
2282 return NS_OK;
2283 }
2285 // This call can re-enter when dealing with plugin listeners
2286 nsresult
2287 nsObjectLoadingContent::CloseChannel()
2288 {
2289 if (mChannel) {
2290 LOG(("OBJLC [%p]: Closing channel\n", this));
2291 // Null the values before potentially-reentering, and ensure they survive
2292 // the call
2293 nsCOMPtr<nsIChannel> channelGrip(mChannel);
2294 nsCOMPtr<nsIStreamListener> listenerGrip(mFinalListener);
2295 mChannel = nullptr;
2296 mFinalListener = nullptr;
2297 channelGrip->Cancel(NS_BINDING_ABORTED);
2298 if (listenerGrip) {
2299 // mFinalListener is only set by LoadObject after OnStartRequest, or
2300 // by OnStartRequest in the case of late-opened plugin streams
2301 listenerGrip->OnStopRequest(channelGrip, nullptr, NS_BINDING_ABORTED);
2302 }
2303 }
2304 return NS_OK;
2305 }
2307 nsresult
2308 nsObjectLoadingContent::OpenChannel()
2309 {
2310 nsCOMPtr<nsIContent> thisContent =
2311 do_QueryInterface(static_cast<nsIImageLoadingContent*>(this));
2312 nsCOMPtr<nsIScriptSecurityManager> secMan =
2313 nsContentUtils::GetSecurityManager();
2314 NS_ASSERTION(thisContent, "must be a content");
2315 nsIDocument* doc = thisContent->OwnerDoc();
2316 NS_ASSERTION(doc, "No owner document?");
2318 nsresult rv;
2319 mChannel = nullptr;
2321 // E.g. mms://
2322 if (!mURI || !CanHandleURI(mURI)) {
2323 return NS_ERROR_NOT_AVAILABLE;
2324 }
2326 rv = secMan->CheckLoadURIWithPrincipal(thisContent->NodePrincipal(), mURI, 0);
2327 NS_ENSURE_SUCCESS(rv, rv);
2329 nsCOMPtr<nsILoadGroup> group = doc->GetDocumentLoadGroup();
2330 nsCOMPtr<nsIChannel> chan;
2331 nsCOMPtr<nsIChannelPolicy> channelPolicy;
2332 nsCOMPtr<nsIContentSecurityPolicy> csp;
2333 rv = doc->NodePrincipal()->GetCsp(getter_AddRefs(csp));
2334 NS_ENSURE_SUCCESS(rv, rv);
2335 if (csp) {
2336 channelPolicy = do_CreateInstance("@mozilla.org/nschannelpolicy;1");
2337 channelPolicy->SetContentSecurityPolicy(csp);
2338 channelPolicy->SetLoadType(nsIContentPolicy::TYPE_OBJECT);
2339 }
2340 nsRefPtr<ObjectInterfaceRequestorShim> shim =
2341 new ObjectInterfaceRequestorShim(this);
2342 rv = NS_NewChannel(getter_AddRefs(chan), mURI, nullptr, group, shim,
2343 nsIChannel::LOAD_CALL_CONTENT_SNIFFERS |
2344 nsIChannel::LOAD_CLASSIFY_URI,
2345 channelPolicy);
2346 NS_ENSURE_SUCCESS(rv, rv);
2348 // Referrer
2349 nsCOMPtr<nsIHttpChannel> httpChan(do_QueryInterface(chan));
2350 if (httpChan) {
2351 httpChan->SetReferrer(doc->GetDocumentURI());
2353 // Set the initiator type
2354 nsCOMPtr<nsITimedChannel> timedChannel(do_QueryInterface(httpChan));
2355 if (timedChannel) {
2356 timedChannel->SetInitiatorType(thisContent->LocalName());
2357 }
2358 }
2360 // Set up the channel's principal and such, like nsDocShell::DoURILoad does.
2361 // If the content being loaded should be sandboxed with respect to origin we
2362 // create a new null principal here. nsContentUtils::SetUpChannelOwner is
2363 // used with a flag to force it to be set as the channel owner.
2364 nsCOMPtr<nsIPrincipal> ownerPrincipal;
2365 uint32_t sandboxFlags = doc->GetSandboxFlags();
2366 if (sandboxFlags & SANDBOXED_ORIGIN) {
2367 ownerPrincipal = do_CreateInstance("@mozilla.org/nullprincipal;1");
2368 } else {
2369 // Not sandboxed - we allow the content to assume its natural owner.
2370 ownerPrincipal = thisContent->NodePrincipal();
2371 }
2372 nsContentUtils::SetUpChannelOwner(ownerPrincipal, chan, mURI, true,
2373 sandboxFlags & SANDBOXED_ORIGIN);
2375 nsCOMPtr<nsIScriptChannel> scriptChannel = do_QueryInterface(chan);
2376 if (scriptChannel) {
2377 // Allow execution against our context if the principals match
2378 scriptChannel->SetExecutionPolicy(nsIScriptChannel::EXECUTE_NORMAL);
2379 }
2381 // AsyncOpen can fail if a file does not exist.
2382 rv = chan->AsyncOpen(shim, nullptr);
2383 NS_ENSURE_SUCCESS(rv, rv);
2384 LOG(("OBJLC [%p]: Channel opened", this));
2385 mChannel = chan;
2386 return NS_OK;
2387 }
2389 uint32_t
2390 nsObjectLoadingContent::GetCapabilities() const
2391 {
2392 return eSupportImages |
2393 eSupportPlugins |
2394 eSupportDocuments |
2395 eSupportSVG;
2396 }
2398 void
2399 nsObjectLoadingContent::DestroyContent()
2400 {
2401 if (mFrameLoader) {
2402 mFrameLoader->Destroy();
2403 mFrameLoader = nullptr;
2404 }
2406 QueueCheckPluginStopEvent();
2407 }
2409 /* static */
2410 void
2411 nsObjectLoadingContent::Traverse(nsObjectLoadingContent *tmp,
2412 nsCycleCollectionTraversalCallback &cb)
2413 {
2414 NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "mFrameLoader");
2415 cb.NoteXPCOMChild(static_cast<nsIFrameLoader*>(tmp->mFrameLoader));
2416 }
2418 void
2419 nsObjectLoadingContent::UnloadObject(bool aResetState)
2420 {
2421 // Don't notify in CancelImageRequests until we transition to a new loaded
2422 // state
2423 CancelImageRequests(false);
2424 if (mFrameLoader) {
2425 mFrameLoader->Destroy();
2426 mFrameLoader = nullptr;
2427 }
2429 if (aResetState) {
2430 if (mType != eType_Plugin) {
2431 // This can re-enter when dealing with plugins, and StopPluginInstance
2432 // will handle it
2433 CloseChannel();
2434 }
2435 mChannelLoaded = false;
2436 mType = eType_Loading;
2437 mURI = mOriginalURI = mBaseURI = nullptr;
2438 mContentType.Truncate();
2439 mOriginalContentType.Truncate();
2440 }
2442 // InstantiatePluginInstance checks this after re-entrant calls and aborts if
2443 // it was cleared from under it
2444 mInstantiating = false;
2446 mScriptRequested = false;
2448 if (!mInstanceOwner) {
2449 // The protochain is normally thrown out after a plugin stops, but if we
2450 // re-enter while stopping a plugin and try to load something new, we need
2451 // to throw away the old protochain in the nested unload.
2452 TeardownProtoChain();
2453 mIsStopping = false;
2454 }
2456 // This call should be last as it may re-enter
2457 StopPluginInstance();
2458 }
2460 void
2461 nsObjectLoadingContent::NotifyStateChanged(ObjectType aOldType,
2462 EventStates aOldState,
2463 bool aSync,
2464 bool aNotify)
2465 {
2466 LOG(("OBJLC [%p]: Notifying about state change: (%u, %llx) -> (%u, %llx)"
2467 " (sync %i, notify %i)", this, aOldType, aOldState.GetInternalValue(),
2468 mType, ObjectState().GetInternalValue(), aSync, aNotify));
2470 nsCOMPtr<nsIContent> thisContent =
2471 do_QueryInterface(static_cast<nsIImageLoadingContent*>(this));
2472 NS_ASSERTION(thisContent, "must be a content");
2474 NS_ASSERTION(thisContent->IsElement(), "Not an element?");
2476 // XXX(johns): A good bit of the code below replicates UpdateState(true)
2478 // Unfortunately, we do some state changes without notifying
2479 // (e.g. in Fallback when canceling image requests), so we have to
2480 // manually notify object state changes.
2481 thisContent->AsElement()->UpdateState(false);
2483 if (!aNotify) {
2484 // We're done here
2485 return;
2486 }
2488 nsIDocument* doc = thisContent->GetCurrentDoc();
2489 if (!doc) {
2490 return; // Nothing to do
2491 }
2493 EventStates newState = ObjectState();
2495 if (newState != aOldState) {
2496 // This will trigger frame construction
2497 NS_ASSERTION(InActiveDocument(thisContent), "Something is confused");
2498 EventStates changedBits = aOldState ^ newState;
2500 {
2501 nsAutoScriptBlocker scriptBlocker;
2502 doc->ContentStateChanged(thisContent, changedBits);
2503 }
2504 if (aSync) {
2505 // Make sure that frames are actually constructed immediately.
2506 doc->FlushPendingNotifications(Flush_Frames);
2507 }
2508 } else if (aOldType != mType) {
2509 // If our state changed, then we already recreated frames
2510 // Otherwise, need to do that here
2511 nsCOMPtr<nsIPresShell> shell = doc->GetShell();
2512 if (shell) {
2513 shell->RecreateFramesFor(thisContent);
2514 }
2515 }
2516 }
2518 nsObjectLoadingContent::ObjectType
2519 nsObjectLoadingContent::GetTypeOfContent(const nsCString& aMIMEType)
2520 {
2521 if (aMIMEType.IsEmpty()) {
2522 return eType_Null;
2523 }
2525 uint32_t caps = GetCapabilities();
2527 if ((caps & eSupportImages) && IsSupportedImage(aMIMEType)) {
2528 return eType_Image;
2529 }
2531 // SVGs load as documents, but are their own capability
2532 bool isSVG = aMIMEType.LowerCaseEqualsLiteral("image/svg+xml");
2533 Capabilities supportType = isSVG ? eSupportSVG : eSupportDocuments;
2534 if ((caps & supportType) && IsSupportedDocument(aMIMEType)) {
2535 return eType_Document;
2536 }
2538 if (caps & eSupportPlugins && PluginExistsForType(aMIMEType.get())) {
2539 // ShouldPlay will handle checking for disabled plugins
2540 return eType_Plugin;
2541 }
2543 return eType_Null;
2544 }
2546 nsObjectFrame*
2547 nsObjectLoadingContent::GetExistingFrame()
2548 {
2549 nsCOMPtr<nsIContent> thisContent = do_QueryInterface(static_cast<nsIImageLoadingContent*>(this));
2550 nsIFrame* frame = thisContent->GetPrimaryFrame();
2551 nsIObjectFrame* objFrame = do_QueryFrame(frame);
2552 return static_cast<nsObjectFrame*>(objFrame);
2553 }
2555 void
2556 nsObjectLoadingContent::CreateStaticClone(nsObjectLoadingContent* aDest) const
2557 {
2558 nsImageLoadingContent::CreateStaticImageClone(aDest);
2560 aDest->mType = mType;
2561 nsObjectLoadingContent* thisObj = const_cast<nsObjectLoadingContent*>(this);
2562 if (thisObj->mPrintFrame.IsAlive()) {
2563 aDest->mPrintFrame = thisObj->mPrintFrame;
2564 } else {
2565 aDest->mPrintFrame = const_cast<nsObjectLoadingContent*>(this)->GetExistingFrame();
2566 }
2568 if (mFrameLoader) {
2569 nsCOMPtr<nsIContent> content =
2570 do_QueryInterface(static_cast<nsIImageLoadingContent*>(aDest));
2571 nsFrameLoader* fl = nsFrameLoader::Create(content->AsElement(), false);
2572 if (fl) {
2573 aDest->mFrameLoader = fl;
2574 mFrameLoader->CreateStaticClone(fl);
2575 }
2576 }
2577 }
2579 NS_IMETHODIMP
2580 nsObjectLoadingContent::GetPrintFrame(nsIFrame** aFrame)
2581 {
2582 *aFrame = mPrintFrame.GetFrame();
2583 return NS_OK;
2584 }
2586 NS_IMETHODIMP
2587 nsObjectLoadingContent::PluginDestroyed()
2588 {
2589 // Called when our plugin is destroyed from under us, usually when reloading
2590 // plugins in plugin host. Invalidate instance owner / prototype but otherwise
2591 // don't take any action.
2592 TeardownProtoChain();
2593 mInstanceOwner->Destroy();
2594 mInstanceOwner = nullptr;
2595 return NS_OK;
2596 }
2598 NS_IMETHODIMP
2599 nsObjectLoadingContent::PluginCrashed(nsIPluginTag* aPluginTag,
2600 const nsAString& pluginDumpID,
2601 const nsAString& browserDumpID,
2602 bool submittedCrashReport)
2603 {
2604 LOG(("OBJLC [%p]: Plugin Crashed, queuing crash event", this));
2605 NS_ASSERTION(mType == eType_Plugin, "PluginCrashed at non-plugin type");
2607 PluginDestroyed();
2609 // Switch to fallback/crashed state, notify
2610 LoadFallback(eFallbackCrashed, true);
2612 // send nsPluginCrashedEvent
2613 nsCOMPtr<nsIContent> thisContent =
2614 do_QueryInterface(static_cast<nsIImageLoadingContent*>(this));
2616 // Note that aPluginTag in invalidated after we're called, so copy
2617 // out any data we need now.
2618 nsAutoCString pluginName;
2619 aPluginTag->GetName(pluginName);
2620 nsAutoCString pluginFilename;
2621 aPluginTag->GetFilename(pluginFilename);
2623 nsCOMPtr<nsIRunnable> ev =
2624 new nsPluginCrashedEvent(thisContent,
2625 pluginDumpID,
2626 browserDumpID,
2627 NS_ConvertUTF8toUTF16(pluginName),
2628 NS_ConvertUTF8toUTF16(pluginFilename),
2629 submittedCrashReport);
2630 nsresult rv = NS_DispatchToCurrentThread(ev);
2631 if (NS_FAILED(rv)) {
2632 NS_WARNING("failed to dispatch nsPluginCrashedEvent");
2633 }
2634 return NS_OK;
2635 }
2637 nsresult
2638 nsObjectLoadingContent::ScriptRequestPluginInstance(JSContext* aCx,
2639 nsNPAPIPluginInstance **aResult)
2640 {
2641 // The below methods pull the cx off the stack, so make sure they match.
2642 //
2643 // NB: Sometimes there's a null cx on the stack, in which case |cx| is the
2644 // safe JS context. But in that case, IsCallerChrome() will return true,
2645 // so the ensuing expression is short-circuited.
2646 MOZ_ASSERT_IF(nsContentUtils::GetCurrentJSContext(),
2647 aCx == nsContentUtils::GetCurrentJSContext());
2648 bool callerIsContentJS = (!nsContentUtils::IsCallerChrome() &&
2649 !nsContentUtils::IsCallerXBL() &&
2650 js::IsContextRunningJS(aCx));
2652 nsCOMPtr<nsIContent> thisContent =
2653 do_QueryInterface(static_cast<nsIImageLoadingContent*>(this));
2655 *aResult = nullptr;
2657 // The first time content script attempts to access placeholder content, fire
2658 // an event. Fallback types >= eFallbackClickToPlay are plugin-replacement
2659 // types, see header.
2660 if (callerIsContentJS && !mScriptRequested &&
2661 InActiveDocument(thisContent) && mType == eType_Null &&
2662 mFallbackType >= eFallbackClickToPlay) {
2663 nsCOMPtr<nsIRunnable> ev =
2664 new nsSimplePluginEvent(thisContent,
2665 NS_LITERAL_STRING("PluginScripted"));
2666 nsresult rv = NS_DispatchToCurrentThread(ev);
2667 if (NS_FAILED(rv)) {
2668 NS_NOTREACHED("failed to dispatch PluginScripted event");
2669 }
2670 mScriptRequested = true;
2671 } else if (callerIsContentJS && mType == eType_Plugin && !mInstanceOwner &&
2672 nsContentUtils::IsSafeToRunScript() &&
2673 InActiveDocument(thisContent)) {
2674 // If we're configured as a plugin in an active document and it's safe to
2675 // run scripts right now, try spawning synchronously
2676 SyncStartPluginInstance();
2677 }
2679 if (mInstanceOwner) {
2680 return mInstanceOwner->GetInstance(aResult);
2681 }
2683 // Note that returning a null plugin is expected (and happens often)
2684 return NS_OK;
2685 }
2687 NS_IMETHODIMP
2688 nsObjectLoadingContent::SyncStartPluginInstance()
2689 {
2690 NS_ASSERTION(nsContentUtils::IsSafeToRunScript(),
2691 "Must be able to run script in order to instantiate a plugin instance!");
2693 // Don't even attempt to start an instance unless the content is in
2694 // the document and active
2695 nsCOMPtr<nsIContent> thisContent =
2696 do_QueryInterface(static_cast<nsIImageLoadingContent*>(this));
2697 if (!InActiveDocument(thisContent)) {
2698 return NS_ERROR_FAILURE;
2699 }
2701 nsCOMPtr<nsIURI> kungFuURIGrip(mURI);
2702 nsCString contentType(mContentType);
2703 return InstantiatePluginInstance();
2704 }
2706 NS_IMETHODIMP
2707 nsObjectLoadingContent::AsyncStartPluginInstance()
2708 {
2709 // OK to have an instance already or a pending spawn.
2710 if (mInstanceOwner || mPendingInstantiateEvent) {
2711 return NS_OK;
2712 }
2714 nsCOMPtr<nsIContent> thisContent =
2715 do_QueryInterface(static_cast<nsIImageLoadingContent*>(this));
2716 nsIDocument* doc = thisContent->OwnerDoc();
2717 if (doc->IsStaticDocument() || doc->IsBeingUsedAsImage()) {
2718 return NS_OK;
2719 }
2721 nsCOMPtr<nsIRunnable> event = new nsAsyncInstantiateEvent(this);
2722 if (!event) {
2723 return NS_ERROR_OUT_OF_MEMORY;
2724 }
2725 nsresult rv = NS_DispatchToCurrentThread(event);
2726 if (NS_SUCCEEDED(rv)) {
2727 // Track pending events
2728 mPendingInstantiateEvent = event;
2729 }
2731 return rv;
2732 }
2734 NS_IMETHODIMP
2735 nsObjectLoadingContent::GetSrcURI(nsIURI** aURI)
2736 {
2737 NS_IF_ADDREF(*aURI = GetSrcURI());
2738 return NS_OK;
2739 }
2741 static bool
2742 DoDelayedStop(nsPluginInstanceOwner* aInstanceOwner,
2743 nsObjectLoadingContent* aContent,
2744 bool aDelayedStop)
2745 {
2746 // Don't delay stopping QuickTime (bug 425157), Flip4Mac (bug 426524),
2747 // XStandard (bug 430219), CMISS Zinc (bug 429604).
2748 if (aDelayedStop
2749 #if !(defined XP_WIN || defined MOZ_X11)
2750 && !aInstanceOwner->MatchPluginName("QuickTime")
2751 && !aInstanceOwner->MatchPluginName("Flip4Mac")
2752 && !aInstanceOwner->MatchPluginName("XStandard plugin")
2753 && !aInstanceOwner->MatchPluginName("CMISS Zinc Plugin")
2754 #endif
2755 ) {
2756 nsCOMPtr<nsIRunnable> evt =
2757 new nsStopPluginRunnable(aInstanceOwner, aContent);
2758 NS_DispatchToCurrentThread(evt);
2759 return true;
2760 }
2761 return false;
2762 }
2764 void
2765 nsObjectLoadingContent::LoadFallback(FallbackType aType, bool aNotify) {
2766 EventStates oldState = ObjectState();
2767 ObjectType oldType = mType;
2769 NS_ASSERTION(!mInstanceOwner && !mFrameLoader && !mChannel,
2770 "LoadFallback called with loaded content");
2772 //
2773 // Fixup mFallbackType
2774 //
2775 nsCOMPtr<nsIContent> thisContent =
2776 do_QueryInterface(static_cast<nsIImageLoadingContent*>(this));
2777 NS_ASSERTION(thisContent, "must be a content");
2779 if (!thisContent->IsHTML() || mContentType.IsEmpty()) {
2780 // Don't let custom fallback handlers run outside HTML, tags without a
2781 // determined type should always just be alternate content
2782 aType = eFallbackAlternate;
2783 }
2785 if (thisContent->Tag() == nsGkAtoms::object &&
2786 (aType == eFallbackUnsupported ||
2787 aType == eFallbackDisabled ||
2788 aType == eFallbackBlocklisted))
2789 {
2790 // Show alternate content instead, if it exists
2791 for (nsIContent* child = thisContent->GetFirstChild();
2792 child; child = child->GetNextSibling()) {
2793 if (!child->IsHTML(nsGkAtoms::param) &&
2794 nsStyleUtil::IsSignificantChild(child, true, false)) {
2795 aType = eFallbackAlternate;
2796 break;
2797 }
2798 }
2799 }
2801 mType = eType_Null;
2802 mFallbackType = aType;
2804 // Notify
2805 if (!aNotify) {
2806 return; // done
2807 }
2809 NotifyStateChanged(oldType, oldState, false, true);
2810 }
2812 void
2813 nsObjectLoadingContent::DoStopPlugin(nsPluginInstanceOwner* aInstanceOwner,
2814 bool aDelayedStop,
2815 bool aForcedReentry)
2816 {
2817 // DoStopPlugin can process events -- There may be pending
2818 // CheckPluginStopEvent events which can drop in underneath us and destroy the
2819 // instance we are about to destroy. We prevent that with the mPluginStopping
2820 // flag. (aForcedReentry is only true from the callback of an earlier delayed
2821 // stop)
2822 if (mIsStopping && !aForcedReentry) {
2823 return;
2824 }
2825 mIsStopping = true;
2827 nsRefPtr<nsPluginInstanceOwner> kungFuDeathGrip(aInstanceOwner);
2828 nsRefPtr<nsNPAPIPluginInstance> inst;
2829 aInstanceOwner->GetInstance(getter_AddRefs(inst));
2830 if (inst) {
2831 if (DoDelayedStop(aInstanceOwner, this, aDelayedStop)) {
2832 return;
2833 }
2835 #if defined(XP_MACOSX)
2836 aInstanceOwner->HidePluginWindow();
2837 #endif
2839 nsRefPtr<nsPluginHost> pluginHost = nsPluginHost::GetInst();
2840 NS_ASSERTION(pluginHost, "No plugin host?");
2841 pluginHost->StopPluginInstance(inst);
2842 }
2844 aInstanceOwner->Destroy();
2846 // If we re-enter in plugin teardown UnloadObject will tear down the
2847 // protochain -- the current protochain could be from a new, unrelated, load.
2848 if (!mIsStopping) {
2849 LOG(("OBJLC [%p]: Re-entered in plugin teardown", this));
2850 return;
2851 }
2853 TeardownProtoChain();
2854 mIsStopping = false;
2855 }
2857 NS_IMETHODIMP
2858 nsObjectLoadingContent::StopPluginInstance()
2859 {
2860 // Clear any pending events
2861 mPendingInstantiateEvent = nullptr;
2862 mPendingCheckPluginStopEvent = nullptr;
2864 // If we're currently instantiating, clearing this will cause
2865 // InstantiatePluginInstance's re-entrance check to destroy the created plugin
2866 mInstantiating = false;
2868 if (!mInstanceOwner) {
2869 return NS_OK;
2870 }
2872 if (mChannel) {
2873 // The plugin has already used data from this channel, we'll need to
2874 // re-open it to handle instantiating again, even if we don't invalidate
2875 // our loaded state.
2876 /// XXX(johns): Except currently, we don't, just leaving re-opening channels
2877 /// to plugins...
2878 LOG(("OBJLC [%p]: StopPluginInstance - Closing used channel", this));
2879 CloseChannel();
2880 }
2882 // We detach the instance owner's frame before destruction, but don't destroy
2883 // the instance owner until the plugin is stopped.
2884 mInstanceOwner->SetFrame(nullptr);
2886 bool delayedStop = false;
2887 #ifdef XP_WIN
2888 // Force delayed stop for Real plugin only; see bug 420886, 426852.
2889 nsRefPtr<nsNPAPIPluginInstance> inst;
2890 mInstanceOwner->GetInstance(getter_AddRefs(inst));
2891 if (inst) {
2892 const char* mime = nullptr;
2893 if (NS_SUCCEEDED(inst->GetMIMEType(&mime)) && mime) {
2894 if (strcmp(mime, "audio/x-pn-realaudio-plugin") == 0) {
2895 delayedStop = true;
2896 }
2897 }
2898 }
2899 #endif
2901 nsRefPtr<nsPluginInstanceOwner> ownerGrip(mInstanceOwner);
2902 mInstanceOwner = nullptr;
2904 // This can/will re-enter
2905 DoStopPlugin(ownerGrip, delayedStop);
2907 return NS_OK;
2908 }
2910 void
2911 nsObjectLoadingContent::NotifyContentObjectWrapper()
2912 {
2913 nsCOMPtr<nsIContent> thisContent =
2914 do_QueryInterface(static_cast<nsIImageLoadingContent*>(this));
2916 nsCOMPtr<nsIDocument> doc = thisContent->GetDocument();
2917 if (!doc)
2918 return;
2920 nsCOMPtr<nsIScriptGlobalObject> sgo = do_QueryInterface(doc->GetScopeObject());
2921 if (!sgo)
2922 return;
2924 nsIScriptContext *scx = sgo->GetContext();
2925 if (!scx)
2926 return;
2928 JSContext *cx = scx->GetNativeContext();
2929 nsCxPusher pusher;
2930 pusher.Push(cx);
2932 JS::Rooted<JSObject*> obj(cx, thisContent->GetWrapper());
2933 if (!obj) {
2934 // Nothing to do here if there's no wrapper for mContent. The proto
2935 // chain will be fixed appropriately when the wrapper is created.
2936 return;
2937 }
2939 SetupProtoChain(cx, obj);
2940 }
2942 NS_IMETHODIMP
2943 nsObjectLoadingContent::PlayPlugin()
2944 {
2945 if (!nsContentUtils::IsCallerChrome())
2946 return NS_OK;
2948 if (!mActivated) {
2949 mActivated = true;
2950 LOG(("OBJLC [%p]: Activated by user", this));
2951 }
2953 // If we're in a click-to-play or play preview state, we need to reload
2954 // Fallback types >= eFallbackClickToPlay are plugin-replacement types, see
2955 // header
2956 if (mType == eType_Null && mFallbackType >= eFallbackClickToPlay) {
2957 return LoadObject(true, true);
2958 }
2960 return NS_OK;
2961 }
2963 NS_IMETHODIMP
2964 nsObjectLoadingContent::Reload(bool aClearActivation)
2965 {
2966 if (aClearActivation) {
2967 mActivated = false;
2968 mPlayPreviewCanceled = false;
2969 }
2971 return LoadObject(true, true);
2972 }
2974 NS_IMETHODIMP
2975 nsObjectLoadingContent::GetActivated(bool *aActivated)
2976 {
2977 *aActivated = Activated();
2978 return NS_OK;
2979 }
2981 NS_IMETHODIMP
2982 nsObjectLoadingContent::GetPluginFallbackType(uint32_t* aPluginFallbackType)
2983 {
2984 NS_ENSURE_TRUE(nsContentUtils::IsCallerChrome(), NS_ERROR_NOT_AVAILABLE);
2985 *aPluginFallbackType = mFallbackType;
2986 return NS_OK;
2987 }
2989 uint32_t
2990 nsObjectLoadingContent::DefaultFallbackType()
2991 {
2992 FallbackType reason;
2993 bool go = ShouldPlay(reason, true);
2994 if (go) {
2995 return PLUGIN_ACTIVE;
2996 }
2997 return reason;
2998 }
3000 NS_IMETHODIMP
3001 nsObjectLoadingContent::GetHasRunningPlugin(bool *aHasPlugin)
3002 {
3003 NS_ENSURE_TRUE(nsContentUtils::IsCallerChrome(), NS_ERROR_NOT_AVAILABLE);
3004 *aHasPlugin = HasRunningPlugin();
3005 return NS_OK;
3006 }
3008 NS_IMETHODIMP
3009 nsObjectLoadingContent::CancelPlayPreview()
3010 {
3011 if (!nsContentUtils::IsCallerChrome())
3012 return NS_ERROR_NOT_AVAILABLE;
3014 mPlayPreviewCanceled = true;
3016 // If we're in play preview state already, reload
3017 if (mType == eType_Null && mFallbackType == eFallbackPlayPreview) {
3018 return LoadObject(true, true);
3019 }
3021 return NS_OK;
3022 }
3024 static bool sPrefsInitialized;
3025 static uint32_t sSessionTimeoutMinutes;
3026 static uint32_t sPersistentTimeoutDays;
3028 bool
3029 nsObjectLoadingContent::ShouldPlay(FallbackType &aReason, bool aIgnoreCurrentType)
3030 {
3031 nsresult rv;
3033 if (!sPrefsInitialized) {
3034 Preferences::AddUintVarCache(&sSessionTimeoutMinutes,
3035 "plugin.sessionPermissionNow.intervalInMinutes", 60);
3036 Preferences::AddUintVarCache(&sPersistentTimeoutDays,
3037 "plugin.persistentPermissionAlways.intervalInDays", 90);
3038 sPrefsInitialized = true;
3039 }
3041 nsRefPtr<nsPluginHost> pluginHost = nsPluginHost::GetInst();
3043 nsCOMPtr<nsIPluginPlayPreviewInfo> playPreviewInfo;
3044 bool isPlayPreviewSpecified = NS_SUCCEEDED(pluginHost->GetPlayPreviewInfo(
3045 mContentType, getter_AddRefs(playPreviewInfo)));
3046 bool ignoreCTP = false;
3047 if (isPlayPreviewSpecified) {
3048 playPreviewInfo->GetIgnoreCTP(&ignoreCTP);
3049 }
3050 if (isPlayPreviewSpecified && !mPlayPreviewCanceled &&
3051 ignoreCTP) {
3052 // play preview in ignoreCTP mode is shown even if the native plugin
3053 // is not present/installed
3054 aReason = eFallbackPlayPreview;
3055 return false;
3056 }
3057 // at this point if it's not a plugin, we let it play/fallback
3058 if (!aIgnoreCurrentType && mType != eType_Plugin) {
3059 return true;
3060 }
3062 // Order of checks:
3063 // * Assume a default of click-to-play
3064 // * If globally disabled, per-site permissions cannot override.
3065 // * If blocklisted, override the reason with the blocklist reason
3066 // * If not blocklisted but playPreview, override the reason with the
3067 // playPreview reason.
3068 // * Check per-site permissions and follow those if specified.
3069 // * Honor per-plugin disabled permission
3070 // * Blocklisted plugins are forced to CtP
3071 // * Check per-plugin permission and follow that.
3073 aReason = eFallbackClickToPlay;
3075 uint32_t enabledState = nsIPluginTag::STATE_DISABLED;
3076 pluginHost->GetStateForType(mContentType, &enabledState);
3077 if (nsIPluginTag::STATE_DISABLED == enabledState) {
3078 aReason = eFallbackDisabled;
3079 return false;
3080 }
3082 // Before we check permissions, get the blocklist state of this plugin to set
3083 // the fallback reason correctly.
3084 uint32_t blocklistState = nsIBlocklistService::STATE_NOT_BLOCKED;
3085 pluginHost->GetBlocklistStateForType(mContentType.get(), &blocklistState);
3086 if (blocklistState == nsIBlocklistService::STATE_BLOCKED) {
3087 // no override possible
3088 aReason = eFallbackBlocklisted;
3089 return false;
3090 }
3092 if (blocklistState == nsIBlocklistService::STATE_VULNERABLE_UPDATE_AVAILABLE) {
3093 aReason = eFallbackVulnerableUpdatable;
3094 }
3095 else if (blocklistState == nsIBlocklistService::STATE_VULNERABLE_NO_UPDATE) {
3096 aReason = eFallbackVulnerableNoUpdate;
3097 }
3099 if (aReason == eFallbackClickToPlay && isPlayPreviewSpecified &&
3100 !mPlayPreviewCanceled && !ignoreCTP) {
3101 // play preview in click-to-play mode is shown instead of standard CTP UI
3102 aReason = eFallbackPlayPreview;
3103 }
3105 // Check the permission manager for permission based on the principal of
3106 // the toplevel content.
3108 nsCOMPtr<nsIContent> thisContent = do_QueryInterface(static_cast<nsIObjectLoadingContent*>(this));
3109 MOZ_ASSERT(thisContent);
3110 nsIDocument* ownerDoc = thisContent->OwnerDoc();
3112 nsCOMPtr<nsIDOMWindow> window = ownerDoc->GetWindow();
3113 if (!window) {
3114 return false;
3115 }
3116 nsCOMPtr<nsIDOMWindow> topWindow;
3117 rv = window->GetTop(getter_AddRefs(topWindow));
3118 NS_ENSURE_SUCCESS(rv, false);
3119 nsCOMPtr<nsIDOMDocument> topDocument;
3120 rv = topWindow->GetDocument(getter_AddRefs(topDocument));
3121 NS_ENSURE_SUCCESS(rv, false);
3122 nsCOMPtr<nsIDocument> topDoc = do_QueryInterface(topDocument);
3124 nsCOMPtr<nsIPermissionManager> permissionManager = do_GetService(NS_PERMISSIONMANAGER_CONTRACTID, &rv);
3125 NS_ENSURE_SUCCESS(rv, false);
3127 // For now we always say that the system principal uses click-to-play since
3128 // that maintains current behavior and we have tests that expect this.
3129 // What we really should do is disable plugins entirely in pages that use
3130 // the system principal, i.e. in chrome pages. That way the click-to-play
3131 // code here wouldn't matter at all. Bug 775301 is tracking this.
3132 if (!nsContentUtils::IsSystemPrincipal(topDoc->NodePrincipal())) {
3133 nsAutoCString permissionString;
3134 rv = pluginHost->GetPermissionStringForType(mContentType, permissionString);
3135 NS_ENSURE_SUCCESS(rv, false);
3136 uint32_t permission;
3137 rv = permissionManager->TestPermissionFromPrincipal(topDoc->NodePrincipal(),
3138 permissionString.Data(),
3139 &permission);
3140 NS_ENSURE_SUCCESS(rv, false);
3141 if (permission != nsIPermissionManager::UNKNOWN_ACTION) {
3142 uint64_t nowms = PR_Now() / 1000;
3143 permissionManager->UpdateExpireTime(
3144 topDoc->NodePrincipal(), permissionString.Data(), false,
3145 nowms + sSessionTimeoutMinutes * 60 * 1000,
3146 nowms / 1000 + uint64_t(sPersistentTimeoutDays) * 24 * 60 * 60 * 1000);
3147 }
3148 switch (permission) {
3149 case nsIPermissionManager::ALLOW_ACTION:
3150 return true;
3151 case nsIPermissionManager::DENY_ACTION:
3152 aReason = eFallbackDisabled;
3153 return false;
3154 case nsIPermissionManager::PROMPT_ACTION:
3155 return false;
3156 case nsIPermissionManager::UNKNOWN_ACTION:
3157 break;
3158 default:
3159 MOZ_ASSERT(false);
3160 return false;
3161 }
3162 }
3164 // No site-specific permissions. Vulnerable plugins are automatically CtP
3165 if (blocklistState == nsIBlocklistService::STATE_VULNERABLE_UPDATE_AVAILABLE ||
3166 blocklistState == nsIBlocklistService::STATE_VULNERABLE_NO_UPDATE) {
3167 return false;
3168 }
3170 switch (enabledState) {
3171 case nsIPluginTag::STATE_ENABLED:
3172 return true;
3173 case nsIPluginTag::STATE_CLICKTOPLAY:
3174 return false;
3175 }
3176 MOZ_CRASH("Unexpected enabledState");
3177 }
3179 nsIDocument*
3180 nsObjectLoadingContent::GetContentDocument()
3181 {
3182 nsCOMPtr<nsIContent> thisContent =
3183 do_QueryInterface(static_cast<nsIImageLoadingContent*>(this));
3185 if (!thisContent->IsInDoc()) {
3186 return nullptr;
3187 }
3189 // XXXbz should this use GetCurrentDoc()? sXBL/XBL2 issue!
3190 nsIDocument *sub_doc = thisContent->OwnerDoc()->GetSubDocumentFor(thisContent);
3191 if (!sub_doc) {
3192 return nullptr;
3193 }
3195 // Return null for cross-origin contentDocument.
3196 if (!nsContentUtils::GetSubjectPrincipal()->SubsumesConsideringDomain(sub_doc->NodePrincipal())) {
3197 return nullptr;
3198 }
3200 return sub_doc;
3201 }
3203 void
3204 nsObjectLoadingContent::LegacyCall(JSContext* aCx,
3205 JS::Handle<JS::Value> aThisVal,
3206 const Sequence<JS::Value>& aArguments,
3207 JS::MutableHandle<JS::Value> aRetval,
3208 ErrorResult& aRv)
3209 {
3210 nsCOMPtr<nsIContent> thisContent =
3211 do_QueryInterface(static_cast<nsIImageLoadingContent*>(this));
3212 JS::Rooted<JSObject*> obj(aCx, thisContent->GetWrapper());
3213 MOZ_ASSERT(obj, "How did we get called?");
3215 // Make sure we're not dealing with an Xray. Our DoCall code can't handle
3216 // random cross-compartment wrappers, so we're going to have to wrap
3217 // everything up into our compartment, but that means we need to check that
3218 // this is not an Xray situation by hand.
3219 if (!JS_WrapObject(aCx, &obj)) {
3220 aRv.Throw(NS_ERROR_UNEXPECTED);
3221 return;
3222 }
3224 if (nsDOMClassInfo::ObjectIsNativeWrapper(aCx, obj)) {
3225 aRv.Throw(NS_ERROR_NOT_AVAILABLE);
3226 return;
3227 }
3229 obj = thisContent->GetWrapper();
3230 // Now wrap things up into the compartment of "obj"
3231 JSAutoCompartment ac(aCx, obj);
3232 JS::AutoValueVector args(aCx);
3233 if (!args.append(aArguments.Elements(), aArguments.Length())) {
3234 aRv.Throw(NS_ERROR_OUT_OF_MEMORY);
3235 return;
3236 }
3238 for (size_t i = 0; i < args.length(); i++) {
3239 if (!JS_WrapValue(aCx, args.handleAt(i))) {
3240 aRv.Throw(NS_ERROR_UNEXPECTED);
3241 return;
3242 }
3243 }
3245 JS::Rooted<JS::Value> thisVal(aCx, aThisVal);
3246 if (!JS_WrapValue(aCx, &thisVal)) {
3247 aRv.Throw(NS_ERROR_UNEXPECTED);
3248 return;
3249 }
3251 nsRefPtr<nsNPAPIPluginInstance> pi;
3252 nsresult rv = ScriptRequestPluginInstance(aCx, getter_AddRefs(pi));
3253 if (NS_FAILED(rv)) {
3254 aRv.Throw(rv);
3255 return;
3256 }
3258 // if there's no plugin around for this object, throw.
3259 if (!pi) {
3260 aRv.Throw(NS_ERROR_NOT_AVAILABLE);
3261 return;
3262 }
3264 JS::Rooted<JSObject*> pi_obj(aCx);
3265 JS::Rooted<JSObject*> pi_proto(aCx);
3267 rv = GetPluginJSObject(aCx, obj, pi, &pi_obj, &pi_proto);
3268 if (NS_FAILED(rv)) {
3269 aRv.Throw(rv);
3270 return;
3271 }
3273 if (!pi_obj) {
3274 aRv.Throw(NS_ERROR_NOT_AVAILABLE);
3275 return;
3276 }
3278 bool ok = JS::Call(aCx, thisVal, pi_obj, args, aRetval);
3279 if (!ok) {
3280 aRv.Throw(NS_ERROR_FAILURE);
3281 return;
3282 }
3284 Telemetry::Accumulate(Telemetry::PLUGIN_CALLED_DIRECTLY, true);
3285 }
3287 void
3288 nsObjectLoadingContent::SetupProtoChain(JSContext* aCx,
3289 JS::Handle<JSObject*> aObject)
3290 {
3291 MOZ_ASSERT(nsCOMPtr<nsIContent>(do_QueryInterface(
3292 static_cast<nsIObjectLoadingContent*>(this)))->IsDOMBinding());
3294 if (mType != eType_Plugin) {
3295 return;
3296 }
3298 if (!nsContentUtils::IsSafeToRunScript()) {
3299 // This may be null if the JS context is not a DOM context. That's ok, we'll
3300 // use the safe context from XPConnect in the runnable.
3301 nsCOMPtr<nsIScriptContext> scriptContext = GetScriptContextFromJSContext(aCx);
3303 nsRefPtr<SetupProtoChainRunner> runner =
3304 new SetupProtoChainRunner(scriptContext, this);
3305 nsContentUtils::AddScriptRunner(runner);
3306 return;
3307 }
3309 // We get called on random compartments here for some reason
3310 // (perhaps because WrapObject can happen on a random compartment?)
3311 // so make sure to enter the compartment of aObject.
3312 MOZ_ASSERT(aCx == nsContentUtils::GetCurrentJSContext());
3314 JSAutoCompartment ac(aCx, aObject);
3316 nsRefPtr<nsNPAPIPluginInstance> pi;
3317 nsresult rv = ScriptRequestPluginInstance(aCx, getter_AddRefs(pi));
3318 if (NS_FAILED(rv)) {
3319 return;
3320 }
3322 if (!pi) {
3323 // No plugin around for this object.
3324 return;
3325 }
3327 JS::Rooted<JSObject*> pi_obj(aCx); // XPConnect-wrapped peer object, when we get it.
3328 JS::Rooted<JSObject*> pi_proto(aCx); // 'pi.__proto__'
3330 rv = GetPluginJSObject(aCx, aObject, pi, &pi_obj, &pi_proto);
3331 if (NS_FAILED(rv)) {
3332 return;
3333 }
3335 if (!pi_obj) {
3336 // Didn't get a plugin instance JSObject, nothing we can do then.
3337 return;
3338 }
3340 // If we got an xpconnect-wrapped plugin object, set obj's
3341 // prototype's prototype to the scriptable plugin.
3343 JS::Rooted<JSObject*> global(aCx, JS_GetGlobalForObject(aCx, aObject));
3344 JS::Handle<JSObject*> my_proto = GetDOMClass(aObject)->mGetProto(aCx, global);
3345 MOZ_ASSERT(my_proto);
3347 // Set 'this.__proto__' to pi
3348 if (!::JS_SetPrototype(aCx, aObject, pi_obj)) {
3349 return;
3350 }
3352 if (pi_proto && js::GetObjectClass(pi_proto) != js::ObjectClassPtr) {
3353 // The plugin wrapper has a proto that's not Object.prototype, set
3354 // 'pi.__proto__.__proto__' to the original 'this.__proto__'
3355 if (pi_proto != my_proto && !::JS_SetPrototype(aCx, pi_proto, my_proto)) {
3356 return;
3357 }
3358 } else {
3359 // 'pi' didn't have a prototype, or pi's proto was
3360 // 'Object.prototype' (i.e. pi is an NPRuntime wrapped JS object)
3361 // set 'pi.__proto__' to the original 'this.__proto__'
3362 if (!::JS_SetPrototype(aCx, pi_obj, my_proto)) {
3363 return;
3364 }
3365 }
3367 // Before this proto dance the objects involved looked like this:
3368 //
3369 // this.__proto__.__proto__
3370 // ^ ^ ^
3371 // | | |__ Object.prototype
3372 // | |
3373 // | |__ WebIDL prototype (shared)
3374 // |
3375 // |__ WebIDL object
3376 //
3377 // pi.__proto__
3378 // ^ ^
3379 // | |__ Object.prototype or some other object
3380 // |
3381 // |__ Plugin NPRuntime JS object wrapper
3382 //
3383 // Now, after the above prototype setup the prototype chain should
3384 // look like this if pi.__proto__ was Object.prototype:
3385 //
3386 // this.__proto__.__proto__.__proto__
3387 // ^ ^ ^ ^
3388 // | | | |__ Object.prototype
3389 // | | |
3390 // | | |__ WebIDL prototype (shared)
3391 // | |
3392 // | |__ Plugin NPRuntime JS object wrapper
3393 // |
3394 // |__ WebIDL object
3395 //
3396 // or like this if pi.__proto__ was some other object:
3397 //
3398 // this.__proto__.__proto__.__proto__.__proto__
3399 // ^ ^ ^ ^ ^
3400 // | | | | |__ Object.prototype
3401 // | | | |
3402 // | | | |__ WebIDL prototype (shared)
3403 // | | |
3404 // | | |__ old pi.__proto__
3405 // | |
3406 // | |__ Plugin NPRuntime JS object wrapper
3407 // |
3408 // |__ WebIDL object
3409 //
3410 }
3412 // static
3413 nsresult
3414 nsObjectLoadingContent::GetPluginJSObject(JSContext *cx,
3415 JS::Handle<JSObject*> obj,
3416 nsNPAPIPluginInstance *plugin_inst,
3417 JS::MutableHandle<JSObject*> plugin_obj,
3418 JS::MutableHandle<JSObject*> plugin_proto)
3419 {
3420 // NB: We need an AutoEnterCompartment because we can be called from
3421 // nsObjectFrame when the plugin loads after the JS object for our content
3422 // node has been created.
3423 JSAutoCompartment ac(cx, obj);
3425 if (plugin_inst) {
3426 plugin_inst->GetJSObject(cx, plugin_obj.address());
3427 if (plugin_obj) {
3428 if (!::JS_GetPrototype(cx, plugin_obj, plugin_proto)) {
3429 return NS_ERROR_UNEXPECTED;
3430 }
3431 }
3432 }
3434 return NS_OK;
3435 }
3437 void
3438 nsObjectLoadingContent::TeardownProtoChain()
3439 {
3440 nsCOMPtr<nsIContent> thisContent =
3441 do_QueryInterface(static_cast<nsIImageLoadingContent*>(this));
3443 // Use the safe JSContext here as we're not always able to find the
3444 // JSContext associated with the NPP any more.
3445 AutoSafeJSContext cx;
3446 JS::Rooted<JSObject*> obj(cx, thisContent->GetWrapper());
3447 NS_ENSURE_TRUE(obj, /* void */);
3449 JS::Rooted<JSObject*> proto(cx);
3450 JSAutoCompartment ac(cx, obj);
3452 // Loop over the DOM element's JS object prototype chain and remove
3453 // all JS objects of the class sNPObjectJSWrapperClass
3454 DebugOnly<bool> removed = false;
3455 while (obj) {
3456 if (!::JS_GetPrototype(cx, obj, &proto)) {
3457 return;
3458 }
3459 if (!proto) {
3460 break;
3461 }
3462 // Unwrap while checking the jsclass - if the prototype is a wrapper for
3463 // an NP object, that counts too.
3464 if (JS_GetClass(js::UncheckedUnwrap(proto)) == &sNPObjectJSWrapperClass) {
3465 // We found an NPObject on the proto chain, get its prototype...
3466 if (!::JS_GetPrototype(cx, proto, &proto)) {
3467 return;
3468 }
3470 MOZ_ASSERT(!removed, "more than one NPObject in prototype chain");
3471 removed = true;
3473 // ... and pull it out of the chain.
3474 ::JS_SetPrototype(cx, obj, proto);
3475 }
3477 obj = proto;
3478 }
3479 }
3481 bool
3482 nsObjectLoadingContent::DoNewResolve(JSContext* aCx, JS::Handle<JSObject*> aObject,
3483 JS::Handle<jsid> aId,
3484 JS::MutableHandle<JSPropertyDescriptor> aDesc)
3485 {
3486 // We don't resolve anything; we just try to make sure we're instantiated.
3487 // This purposefully does not fire for chrome/xray resolves, see bug 967694
3489 nsRefPtr<nsNPAPIPluginInstance> pi;
3490 nsresult rv = ScriptRequestPluginInstance(aCx, getter_AddRefs(pi));
3491 if (NS_FAILED(rv)) {
3492 return mozilla::dom::Throw(aCx, rv);
3493 }
3494 return true;
3495 }
3497 void
3498 nsObjectLoadingContent::GetOwnPropertyNames(JSContext* aCx,
3499 nsTArray<nsString>& /* unused */,
3500 ErrorResult& aRv)
3501 {
3502 // Just like DoNewResolve, just make sure we're instantiated. That will do
3503 // the work our Enumerate hook needs to do. This purposefully does not fire
3504 // for xray resolves, see bug 967694
3505 nsRefPtr<nsNPAPIPluginInstance> pi;
3506 aRv = ScriptRequestPluginInstance(aCx, getter_AddRefs(pi));
3507 }
3510 // SetupProtoChainRunner implementation
3511 nsObjectLoadingContent::SetupProtoChainRunner::SetupProtoChainRunner(
3512 nsIScriptContext* scriptContext,
3513 nsObjectLoadingContent* aContent)
3514 : mContext(scriptContext)
3515 , mContent(aContent)
3516 {
3517 }
3519 NS_IMETHODIMP
3520 nsObjectLoadingContent::SetupProtoChainRunner::Run()
3521 {
3522 // XXXbz Does it really matter what JSContext we use here? Seems
3523 // like we could just always use the safe context....
3524 nsCxPusher pusher;
3525 JSContext* cx = mContext ? mContext->GetNativeContext()
3526 : nsContentUtils::GetSafeJSContext();
3527 pusher.Push(cx);
3529 nsCOMPtr<nsIContent> content;
3530 CallQueryInterface(mContent.get(), getter_AddRefs(content));
3531 JS::Rooted<JSObject*> obj(cx, content->GetWrapper());
3532 if (!obj) {
3533 // No need to set up our proto chain if we don't even have an object
3534 return NS_OK;
3535 }
3536 nsObjectLoadingContent* objectLoadingContent =
3537 static_cast<nsObjectLoadingContent*>(mContent.get());
3538 objectLoadingContent->SetupProtoChain(cx, obj);
3539 return NS_OK;
3540 }
3542 NS_IMPL_ISUPPORTS(nsObjectLoadingContent::SetupProtoChainRunner, nsIRunnable)