content/base/src/nsObjectLoadingContent.cpp

Wed, 31 Dec 2014 06:09:35 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 06:09:35 +0100
changeset 0
6474c204b198
permissions
-rw-r--r--

Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.

     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(&currentLevel);
   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;
  1005 // nsIStreamListener
  1006 NS_IMETHODIMP
  1007 nsObjectLoadingContent::OnDataAvailable(nsIRequest *aRequest,
  1008                                         nsISupports *aContext,
  1009                                         nsIInputStream *aInputStream,
  1010                                         uint64_t aOffset, uint32_t aCount)
  1012   NS_ENSURE_TRUE(nsContentUtils::IsCallerChrome(), NS_ERROR_NOT_AVAILABLE);
  1014   if (aRequest != mChannel) {
  1015     return NS_BINDING_ABORTED;
  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);
  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;
  1032 // nsIFrameLoaderOwner
  1033 NS_IMETHODIMP
  1034 nsObjectLoadingContent::GetFrameLoader(nsIFrameLoader** aFrameLoader)
  1036   NS_IF_ADDREF(*aFrameLoader = mFrameLoader);
  1037   return NS_OK;
  1040 NS_IMETHODIMP_(already_AddRefed<nsFrameLoader>)
  1041 nsObjectLoadingContent::GetFrameLoader()
  1043   nsRefPtr<nsFrameLoader> loader = mFrameLoader;
  1044   return loader.forget();
  1047 NS_IMETHODIMP
  1048 nsObjectLoadingContent::SwapFrameLoaders(nsIFrameLoaderOwner* aOtherLoader)
  1050   return NS_ERROR_NOT_IMPLEMENTED;
  1053 NS_IMETHODIMP
  1054 nsObjectLoadingContent::GetActualType(nsACString& aType)
  1056   aType = mContentType;
  1057   return NS_OK;
  1060 NS_IMETHODIMP
  1061 nsObjectLoadingContent::GetDisplayedType(uint32_t* aType)
  1063   *aType = DisplayedType();
  1064   return NS_OK;
  1067 NS_IMETHODIMP
  1068 nsObjectLoadingContent::HasNewFrame(nsIObjectFrame* aFrame)
  1070   if (mType != eType_Plugin) {
  1071     return NS_OK;
  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);
  1082       QueueCheckPluginStopEvent();
  1084     return NS_OK;
  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;
  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;
  1104 NS_IMETHODIMP
  1105 nsObjectLoadingContent::GetPluginInstance(nsNPAPIPluginInstance** aInstance)
  1107   *aInstance = nullptr;
  1109   if (!mInstanceOwner) {
  1110     return NS_OK;
  1113   return mInstanceOwner->GetInstance(aInstance);
  1116 NS_IMETHODIMP
  1117 nsObjectLoadingContent::GetContentTypeForMIMEType(const nsACString& aMIMEType,
  1118                                                   uint32_t* aType)
  1120   *aType = GetTypeOfContent(PromiseFlatCString(aMIMEType));
  1121   return NS_OK;
  1124 NS_IMETHODIMP
  1125 nsObjectLoadingContent::GetBaseURI(nsIURI **aResult)
  1127   NS_IF_ADDREF(*aResult = mBaseURI);
  1128   return NS_OK;
  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
  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)
  1177   if (aIID.Equals(NS_GET_IID(nsIChannelEventSink))) {
  1178     nsIChannelEventSink* sink = this;
  1179     *aResult = sink;
  1180     NS_ADDREF(sink);
  1181     return NS_OK;
  1183   return NS_NOINTERFACE;
  1186 // nsIChannelEventSink
  1187 NS_IMETHODIMP
  1188 nsObjectLoadingContent::AsyncOnChannelRedirect(nsIChannel *aOldChannel,
  1189                                                nsIChannel *aNewChannel,
  1190                                                uint32_t aFlags,
  1191                                                nsIAsyncVerifyRedirectCallback *cb)
  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;
  1199   mChannel = aNewChannel;
  1200   cb->OnRedirectVerifyCallback(NS_OK);
  1201   return NS_OK;
  1204 // <public>
  1205 EventStates
  1206 nsObjectLoadingContent::ObjectState() const
  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;
  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;
  1252   };
  1253   NS_NOTREACHED("unknown type?");
  1254   return NS_EVENT_STATE_LOADING;
  1257 // Returns false if mBaseURI is not acceptable for java applets.
  1258 bool
  1259 nsObjectLoadingContent::CheckJavaCodebase()
  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;
  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;
  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;
  1294   return true;
  1297 bool
  1298 nsObjectLoadingContent::CheckLoadPolicy(int16_t *aContentPolicy)
  1300   if (!aContentPolicy || !mURI) {
  1301     NS_NOTREACHED("Doing it wrong");
  1302     return false;
  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;
  1332   return true;
  1335 bool
  1336 nsObjectLoadingContent::CheckProcessPolicy(int16_t *aContentPolicy)
  1338   if (!aContentPolicy) {
  1339     NS_NOTREACHED("Null out variable");
  1340     return false;
  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;
  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;
  1383   return true;
  1386 nsObjectLoadingContent::ParameterUpdateFlags
  1387 nsObjectLoadingContent::UpdateObjectParameters(bool aJavaURI)
  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());
  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;
  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;
  1512             if (domapplet || domobject) {
  1513               if (domapplet) {
  1514                 parent = do_QueryInterface(domapplet);
  1516               else {
  1517                 parent = do_QueryInterface(domobject);
  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);
  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(".");
  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));
  1559   // If we failed to build a valid URI, use the document's base URI
  1560   if (!newBaseURI) {
  1561     newBaseURI = docBaseURI;
  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");
  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;
  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);
  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));
  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();
  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;
  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;
  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;
  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;
  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);
  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;
  1731   } else if (newChannel) {
  1732     LOG(("OBJLC [%p]: We failed to open a channel, marking invalid", this));
  1733     stateInvalid = true;
  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;
  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;
  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;
  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);
  1805     LOG(("OBJLC [%p]: Object effective baseURI changed", this));
  1806     mBaseURI = newBaseURI;
  1809   if (!URIEquals(newURI, mURI)) {
  1810     retval = (ParameterUpdateFlags)(retval | eParamStateChanged);
  1811     LOG(("OBJLC [%p]: Object effective URI changed", this));
  1812     mURI = newURI;
  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;
  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);
  1834   return retval;
  1837 // Used by PluginDocument to kick off our initial load from the already-opened
  1838 // channel.
  1839 NS_IMETHODIMP
  1840 nsObjectLoadingContent::InitializeFromChannel(nsIRequest *aChannel)
  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;
  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;
  1865 // Only OnStartRequest should be passing the channel parameter
  1866 nsresult
  1867 nsObjectLoadingContent::LoadObject(bool aNotify,
  1868                                    bool aForceLoad)
  1870   return LoadObject(aNotify, aForceLoad, nullptr);
  1873 nsresult
  1874 nsObjectLoadingContent::LoadObject(bool aNotify,
  1875                                    bool aForceLoad,
  1876                                    nsIRequest *aLoadingChannel)
  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;
  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;
  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;
  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;
  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;
  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;
  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));
  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;
  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;
  1986   //
  1987   // Security checks
  1988   //
  1990   if (mType != eType_Null) {
  1991     bool allowLoad = true;
  1992     if (nsPluginHost::IsJavaMIMEType(mContentType.get())) {
  1993       allowLoad = CheckJavaCodebase();
  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);
  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);
  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;
  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;
  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;
  2049       nestedURI->GetInnerURI(getter_AddRefs(tempURI));
  2050       nestedURI = do_QueryInterface(tempURI);
  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;
  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;
  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)
  2078     NS_NOTREACHED("Trying to load new plugin with existing content");
  2079     rv = NS_ERROR_UNEXPECTED;
  2080     return NS_OK;
  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;
  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;
  2110       rv = LoadImageWithChannel(mChannel, getter_AddRefs(finalListener));
  2111       // finalListener will receive OnStartRequest below
  2112     break;
  2113     case eType_Plugin:
  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;
  2129         // We'll handle this below
  2130         doSpawnPlugin = true;
  2131       } else {
  2132         rv = AsyncStartPluginInstance();
  2135     break;
  2136     case eType_Document:
  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;
  2146       mFrameLoader = nsFrameLoader::Create(thisContent->AsElement(),
  2147                                            mNetworkCreated);
  2148       if (!mFrameLoader) {
  2149         NS_NOTREACHED("nsFrameLoader::Create failed");
  2150         mType = eType_Null;
  2151         break;
  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;
  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;
  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;
  2188       rv = uriLoader->OpenChannel(mChannel, nsIURILoader::DONT_RETARGET, req,
  2189                                   getter_AddRefs(finalListener));
  2190       // finalListener will receive OnStartRequest below
  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));
  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;
  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();
  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);
  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;
  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);
  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);
  2282   return NS_OK;
  2285 // This call can re-enter when dealing with plugin listeners
  2286 nsresult
  2287 nsObjectLoadingContent::CloseChannel()
  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);
  2304   return NS_OK;
  2307 nsresult
  2308 nsObjectLoadingContent::OpenChannel()
  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;
  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);
  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());
  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();
  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);
  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;
  2389 uint32_t
  2390 nsObjectLoadingContent::GetCapabilities() const
  2392   return eSupportImages |
  2393          eSupportPlugins |
  2394          eSupportDocuments |
  2395          eSupportSVG;
  2398 void
  2399 nsObjectLoadingContent::DestroyContent()
  2401   if (mFrameLoader) {
  2402     mFrameLoader->Destroy();
  2403     mFrameLoader = nullptr;
  2406   QueueCheckPluginStopEvent();
  2409 /* static */
  2410 void
  2411 nsObjectLoadingContent::Traverse(nsObjectLoadingContent *tmp,
  2412                                  nsCycleCollectionTraversalCallback &cb)
  2414   NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "mFrameLoader");
  2415   cb.NoteXPCOMChild(static_cast<nsIFrameLoader*>(tmp->mFrameLoader));
  2418 void
  2419 nsObjectLoadingContent::UnloadObject(bool aResetState)
  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;
  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();
  2435     mChannelLoaded = false;
  2436     mType = eType_Loading;
  2437     mURI = mOriginalURI = mBaseURI = nullptr;
  2438     mContentType.Truncate();
  2439     mOriginalContentType.Truncate();
  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;
  2456   // This call should be last as it may re-enter
  2457   StopPluginInstance();
  2460 void
  2461 nsObjectLoadingContent::NotifyStateChanged(ObjectType aOldType,
  2462                                            EventStates aOldState,
  2463                                            bool aSync,
  2464                                            bool aNotify)
  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;
  2488   nsIDocument* doc = thisContent->GetCurrentDoc();
  2489   if (!doc) {
  2490     return; // Nothing to do
  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;
  2501       nsAutoScriptBlocker scriptBlocker;
  2502       doc->ContentStateChanged(thisContent, changedBits);
  2504     if (aSync) {
  2505       // Make sure that frames are actually constructed immediately.
  2506       doc->FlushPendingNotifications(Flush_Frames);
  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);
  2518 nsObjectLoadingContent::ObjectType
  2519 nsObjectLoadingContent::GetTypeOfContent(const nsCString& aMIMEType)
  2521   if (aMIMEType.IsEmpty()) {
  2522     return eType_Null;
  2525   uint32_t caps = GetCapabilities();
  2527   if ((caps & eSupportImages) && IsSupportedImage(aMIMEType)) {
  2528     return eType_Image;
  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;
  2538   if (caps & eSupportPlugins && PluginExistsForType(aMIMEType.get())) {
  2539     // ShouldPlay will handle checking for disabled plugins
  2540     return eType_Plugin;
  2543   return eType_Null;
  2546 nsObjectFrame*
  2547 nsObjectLoadingContent::GetExistingFrame()
  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);
  2555 void
  2556 nsObjectLoadingContent::CreateStaticClone(nsObjectLoadingContent* aDest) const
  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();
  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);
  2579 NS_IMETHODIMP
  2580 nsObjectLoadingContent::GetPrintFrame(nsIFrame** aFrame)
  2582   *aFrame = mPrintFrame.GetFrame();
  2583   return NS_OK;
  2586 NS_IMETHODIMP
  2587 nsObjectLoadingContent::PluginDestroyed()
  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;
  2598 NS_IMETHODIMP
  2599 nsObjectLoadingContent::PluginCrashed(nsIPluginTag* aPluginTag,
  2600                                       const nsAString& pluginDumpID,
  2601                                       const nsAString& browserDumpID,
  2602                                       bool submittedCrashReport)
  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");
  2634   return NS_OK;
  2637 nsresult
  2638 nsObjectLoadingContent::ScriptRequestPluginInstance(JSContext* aCx,
  2639                                                     nsNPAPIPluginInstance **aResult)
  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");
  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();
  2679   if (mInstanceOwner) {
  2680     return mInstanceOwner->GetInstance(aResult);
  2683   // Note that returning a null plugin is expected (and happens often)
  2684   return NS_OK;
  2687 NS_IMETHODIMP
  2688 nsObjectLoadingContent::SyncStartPluginInstance()
  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;
  2701   nsCOMPtr<nsIURI> kungFuURIGrip(mURI);
  2702   nsCString contentType(mContentType);
  2703   return InstantiatePluginInstance();
  2706 NS_IMETHODIMP
  2707 nsObjectLoadingContent::AsyncStartPluginInstance()
  2709   // OK to have an instance already or a pending spawn.
  2710   if (mInstanceOwner || mPendingInstantiateEvent) {
  2711     return NS_OK;
  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;
  2721   nsCOMPtr<nsIRunnable> event = new nsAsyncInstantiateEvent(this);
  2722   if (!event) {
  2723     return NS_ERROR_OUT_OF_MEMORY;
  2725   nsresult rv = NS_DispatchToCurrentThread(event);
  2726   if (NS_SUCCEEDED(rv)) {
  2727     // Track pending events
  2728     mPendingInstantiateEvent = event;
  2731   return rv;
  2734 NS_IMETHODIMP
  2735 nsObjectLoadingContent::GetSrcURI(nsIURI** aURI)
  2737   NS_IF_ADDREF(*aURI = GetSrcURI());
  2738   return NS_OK;
  2741 static bool
  2742 DoDelayedStop(nsPluginInstanceOwner* aInstanceOwner,
  2743               nsObjectLoadingContent* aContent,
  2744               bool aDelayedStop)
  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;
  2761   return false;
  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;
  2785   if (thisContent->Tag() == nsGkAtoms::object &&
  2786       (aType == eFallbackUnsupported ||
  2787        aType == eFallbackDisabled ||
  2788        aType == eFallbackBlocklisted))
  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;
  2801   mType = eType_Null;
  2802   mFallbackType = aType;
  2804   // Notify
  2805   if (!aNotify) {
  2806     return; // done
  2809   NotifyStateChanged(oldType, oldState, false, true);
  2812 void
  2813 nsObjectLoadingContent::DoStopPlugin(nsPluginInstanceOwner* aInstanceOwner,
  2814                                      bool aDelayedStop,
  2815                                      bool aForcedReentry)
  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;
  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;
  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);
  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;
  2853   TeardownProtoChain();
  2854   mIsStopping = false;
  2857 NS_IMETHODIMP
  2858 nsObjectLoadingContent::StopPluginInstance()
  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;
  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();
  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;
  2899 #endif
  2901   nsRefPtr<nsPluginInstanceOwner> ownerGrip(mInstanceOwner);
  2902   mInstanceOwner = nullptr;
  2904   // This can/will re-enter
  2905   DoStopPlugin(ownerGrip, delayedStop);
  2907   return NS_OK;
  2910 void
  2911 nsObjectLoadingContent::NotifyContentObjectWrapper()
  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;
  2939   SetupProtoChain(cx, obj);
  2942 NS_IMETHODIMP
  2943 nsObjectLoadingContent::PlayPlugin()
  2945   if (!nsContentUtils::IsCallerChrome())
  2946     return NS_OK;
  2948   if (!mActivated) {
  2949     mActivated = true;
  2950     LOG(("OBJLC [%p]: Activated by user", this));
  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);
  2960   return NS_OK;
  2963 NS_IMETHODIMP
  2964 nsObjectLoadingContent::Reload(bool aClearActivation)
  2966   if (aClearActivation) {
  2967     mActivated = false;
  2968     mPlayPreviewCanceled = false;
  2971   return LoadObject(true, true);
  2974 NS_IMETHODIMP
  2975 nsObjectLoadingContent::GetActivated(bool *aActivated)
  2977   *aActivated = Activated();
  2978   return NS_OK;
  2981 NS_IMETHODIMP
  2982 nsObjectLoadingContent::GetPluginFallbackType(uint32_t* aPluginFallbackType)
  2984   NS_ENSURE_TRUE(nsContentUtils::IsCallerChrome(), NS_ERROR_NOT_AVAILABLE);
  2985   *aPluginFallbackType = mFallbackType;
  2986   return NS_OK;
  2989 uint32_t
  2990 nsObjectLoadingContent::DefaultFallbackType()
  2992   FallbackType reason;
  2993   bool go = ShouldPlay(reason, true);
  2994   if (go) {
  2995     return PLUGIN_ACTIVE;
  2997   return reason;
  3000 NS_IMETHODIMP
  3001 nsObjectLoadingContent::GetHasRunningPlugin(bool *aHasPlugin)
  3003   NS_ENSURE_TRUE(nsContentUtils::IsCallerChrome(), NS_ERROR_NOT_AVAILABLE);
  3004   *aHasPlugin = HasRunningPlugin();
  3005   return NS_OK;
  3008 NS_IMETHODIMP
  3009 nsObjectLoadingContent::CancelPlayPreview()
  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);
  3021   return NS_OK;
  3024 static bool sPrefsInitialized;
  3025 static uint32_t sSessionTimeoutMinutes;
  3026 static uint32_t sPersistentTimeoutDays;
  3028 bool
  3029 nsObjectLoadingContent::ShouldPlay(FallbackType &aReason, bool aIgnoreCurrentType)
  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;
  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);
  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;
  3057   // at this point if it's not a plugin, we let it play/fallback
  3058   if (!aIgnoreCurrentType && mType != eType_Plugin) {
  3059     return true;
  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;
  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;
  3092   if (blocklistState == nsIBlocklistService::STATE_VULNERABLE_UPDATE_AVAILABLE) {
  3093     aReason = eFallbackVulnerableUpdatable;
  3095   else if (blocklistState == nsIBlocklistService::STATE_VULNERABLE_NO_UPDATE) {
  3096     aReason = eFallbackVulnerableNoUpdate;
  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;
  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;
  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);
  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;
  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;
  3170   switch (enabledState) {
  3171   case nsIPluginTag::STATE_ENABLED:
  3172     return true;
  3173   case nsIPluginTag::STATE_CLICKTOPLAY:
  3174     return false;
  3176   MOZ_CRASH("Unexpected enabledState");
  3179 nsIDocument*
  3180 nsObjectLoadingContent::GetContentDocument()
  3182   nsCOMPtr<nsIContent> thisContent =
  3183     do_QueryInterface(static_cast<nsIImageLoadingContent*>(this));
  3185   if (!thisContent->IsInDoc()) {
  3186     return nullptr;
  3189   // XXXbz should this use GetCurrentDoc()?  sXBL/XBL2 issue!
  3190   nsIDocument *sub_doc = thisContent->OwnerDoc()->GetSubDocumentFor(thisContent);
  3191   if (!sub_doc) {
  3192     return nullptr;
  3195   // Return null for cross-origin contentDocument.
  3196   if (!nsContentUtils::GetSubjectPrincipal()->SubsumesConsideringDomain(sub_doc->NodePrincipal())) {
  3197     return nullptr;
  3200   return sub_doc;
  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)
  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;
  3224   if (nsDOMClassInfo::ObjectIsNativeWrapper(aCx, obj)) {
  3225     aRv.Throw(NS_ERROR_NOT_AVAILABLE);
  3226     return;
  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;
  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;
  3245   JS::Rooted<JS::Value> thisVal(aCx, aThisVal);
  3246   if (!JS_WrapValue(aCx, &thisVal)) {
  3247     aRv.Throw(NS_ERROR_UNEXPECTED);
  3248     return;
  3251   nsRefPtr<nsNPAPIPluginInstance> pi;
  3252   nsresult rv = ScriptRequestPluginInstance(aCx, getter_AddRefs(pi));
  3253   if (NS_FAILED(rv)) {
  3254     aRv.Throw(rv);
  3255     return;
  3258   // if there's no plugin around for this object, throw.
  3259   if (!pi) {
  3260     aRv.Throw(NS_ERROR_NOT_AVAILABLE);
  3261     return;
  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;
  3273   if (!pi_obj) {
  3274     aRv.Throw(NS_ERROR_NOT_AVAILABLE);
  3275     return;
  3278   bool ok = JS::Call(aCx, thisVal, pi_obj, args, aRetval);
  3279   if (!ok) {
  3280     aRv.Throw(NS_ERROR_FAILURE);
  3281     return;
  3284   Telemetry::Accumulate(Telemetry::PLUGIN_CALLED_DIRECTLY, true);
  3287 void
  3288 nsObjectLoadingContent::SetupProtoChain(JSContext* aCx,
  3289                                         JS::Handle<JSObject*> aObject)
  3291   MOZ_ASSERT(nsCOMPtr<nsIContent>(do_QueryInterface(
  3292     static_cast<nsIObjectLoadingContent*>(this)))->IsDOMBinding());
  3294   if (mType != eType_Plugin) {
  3295     return;
  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;
  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;
  3322   if (!pi) {
  3323     // No plugin around for this object.
  3324     return;
  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;
  3335   if (!pi_obj) {
  3336     // Didn't get a plugin instance JSObject, nothing we can do then.
  3337     return;
  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;
  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;
  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;
  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   //
  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)
  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;
  3434   return NS_OK;
  3437 void
  3438 nsObjectLoadingContent::TeardownProtoChain()
  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;
  3459     if (!proto) {
  3460       break;
  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;
  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);
  3477     obj = proto;
  3481 bool
  3482 nsObjectLoadingContent::DoNewResolve(JSContext* aCx, JS::Handle<JSObject*> aObject,
  3483                                      JS::Handle<jsid> aId,
  3484                                      JS::MutableHandle<JSPropertyDescriptor> aDesc)
  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);
  3494   return true;
  3497 void
  3498 nsObjectLoadingContent::GetOwnPropertyNames(JSContext* aCx,
  3499                                             nsTArray<nsString>& /* unused */,
  3500                                             ErrorResult& aRv)
  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));
  3510 // SetupProtoChainRunner implementation
  3511 nsObjectLoadingContent::SetupProtoChainRunner::SetupProtoChainRunner(
  3512     nsIScriptContext* scriptContext,
  3513     nsObjectLoadingContent* aContent)
  3514   : mContext(scriptContext)
  3515   , mContent(aContent)
  3519 NS_IMETHODIMP
  3520 nsObjectLoadingContent::SetupProtoChainRunner::Run()
  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;
  3536   nsObjectLoadingContent* objectLoadingContent =
  3537     static_cast<nsObjectLoadingContent*>(mContent.get());
  3538   objectLoadingContent->SetupProtoChain(cx, obj);
  3539   return NS_OK;
  3542 NS_IMPL_ISUPPORTS(nsObjectLoadingContent::SetupProtoChainRunner, nsIRunnable)

mercurial